diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 14aef5db92..6828b236ff 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,7 +2,7 @@ | ---------------------------------------- | ------------------ | **JIRA issue** | [IBX-XXXX](https://issues.ibexa.co/browse/IBX-XXXX) | **Type** | feature/bug/improvement -| **Target Ibexa version** | `v3.3` +| **Target Ibexa version** | `v4.0` | **BC breaks** | yes/no @@ -11,6 +11,6 @@ - [ ] Provided PR description. - [ ] Tested the solution manually. - [ ] Provided automated test coverage. -- [ ] Checked that target branch is set correctly (master for features, the oldest supported for bugs). +- [ ] Checked that target branch is set correctly (main for features, the oldest supported for bugs). - [ ] Ran PHP CS Fixer for new PHP code (use `$ composer fix-cs`). -- [ ] Asked for a review (ping `@ezsystems/engineering-team`). +- [ ] Asked for a review (ping `@ibexa/engineering`). diff --git a/.github/workflows/browser-tests.yaml b/.github/workflows/browser-tests.yaml index 797e0c7bd4..345c838fdc 100644 --- a/.github/workflows/browser-tests.yaml +++ b/.github/workflows/browser-tests.yaml @@ -13,7 +13,6 @@ jobs: uses: ibexa/gh-workflows/.github/workflows/browser-tests.yml@main with: project-edition: 'oss' - project-version: '^3.3.x-dev' test-setup-phase-1: '--mode=standard --profile=core --suite=setup' test-suite: "--mode=standard --profile=core --tags='~@broken&&~@setup'" secrets: diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f2960b8c0f..3d110a576e 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -3,7 +3,7 @@ name: CI on: push: branches: - - master + - main - '[0-9]+.[0-9]+' pull_request: ~ @@ -42,7 +42,6 @@ jobs: fail-fast: false matrix: php: - - '7.3' - '7.4' - '8.0' - '8.1' @@ -98,7 +97,6 @@ jobs: fail-fast: false matrix: php: - - '7.3' - '7.4' - '8.0' - '8.1' @@ -224,7 +222,9 @@ jobs: coverage: none - name: Add solr dependency - run: composer require --no-update "ezsystems/ezplatform-solr-search-engine:^3.3@dev" + run: | + VERSION=$(jq -r '.extra | ."branch-alias" | ."dev-main"' < composer.json) + composer require --no-update "ibexa/solr:$VERSION" - uses: "ramsey/composer-install@v1" with: @@ -236,4 +236,3 @@ jobs: CUSTOM_CACHE_POOL: singleredis CACHE_HOST: 127.0.0.1 CORES_SETUP: single - diff --git a/.github/workflows/gha-docker-solr.yaml b/.github/workflows/gha-docker-solr.yaml new file mode 100644 index 0000000000..f32cb9525f --- /dev/null +++ b/.github/workflows/gha-docker-solr.yaml @@ -0,0 +1,74 @@ +name: Build and publish Solr Docker image +on: + workflow_dispatch: + inputs: + force: + default: false + required: false + type: boolean + description: "Push new image even when tests fail" + schedule: + - cron: "45 21 3 * *" + +env: + IMAGE_NAME: ghcr.io/ibexa/core/solr + +jobs: + build-and-publish: + runs-on: "ubuntu-20.04" + permissions: + packages: write + services: + redis: + image: redis + ports: + - 6379:6379 + options: + --memory=60m + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Build Solr image + run: docker build -t "$IMAGE_NAME:latest" docker/solr + + - name: Start Solr image + run: | + docker run --health-cmd "solr status" \ + --health-interval 10s --health-timeout 5s --health-retries 10 \ + -d -p 8983:8983 "$IMAGE_NAME:latest" + + - name: Setup PHP Action + uses: shivammathur/setup-php@v2 + with: + php-version: 7.4 + coverage: none + + - name: Add solr dependency + run: | + VERSION=$(jq -r '.extra | ."branch-alias" | ."dev-main"' < composer.json) + composer require --no-update "ibexa/solr:$VERSION" + + - uses: "ramsey/composer-install@v2" + with: + dependency-versions: "highest" + + - name: Run integration test suite + run: composer test-integration-solr + continue-on-error: ${{ inputs.force != '' }} + env: + CUSTOM_CACHE_POOL: singleredis + CACHE_HOST: 127.0.0.1 + CORES_SETUP: single + + - name: Log in to the Container registry + uses: docker/login-action@65b78e6e13532edd9afa3aa52ac7964289d1a9c1 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Push image + run: docker push "$IMAGE_NAME" + diff --git a/.gitignore b/.gitignore index d9f2c5599d..0e0fe9fb47 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -config.php doc/apidoc/ docblox.xml composer.phar diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php index 99655a3b64..32a66524a7 100644 --- a/.php-cs-fixer.php +++ b/.php-cs-fixer.php @@ -11,31 +11,8 @@ ->setFinder( PhpCsFixer\Finder::create() ->in([ - __DIR__ . '/eZ', __DIR__ . '/src', __DIR__ . '/tests', ]) - ->exclude( - [ - 'Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures', - 'Publish/API/Repository/Tests/FieldType/_fixtures', - 'Publish/API/Repository/Tests/_fixtures', - 'Publish/Core/FieldType/Tests/Url/Gateway/_fixtures', - 'Publish/Core/IO/Tests/_fixtures', - 'Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures', - 'Publish/Core/Persistence/Legacy/Tests/Content/Location/Gateway/_fixtures', - 'Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/_fixtures', - 'Publish/Core/Persistence/Legacy/Tests/Content/Type/_fixtures', - 'Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures', - 'Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures', - 'Publish/Core/Persistence/Legacy/Tests/Content/UrlWildcard/Gateway/_fixtures', - 'Publish/Core/Persistence/Legacy/Tests/Content/_fixtures', - 'Publish/Core/Persistence/Legacy/Tests/_fixtures', - 'Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures', - 'Publish/Core/Repository/Tests/Service/Integration/Legacy/_fixtures', - 'Publish/Core/Search/Legacy/Tests/_fixtures', - 'Publish/SPI/Tests/FieldType/_fixtures', - ] - ) ->files()->name('*.php') ); diff --git a/.sonarcloud.properties b/.sonarcloud.properties index a6f00c520f..43ab56dedb 100644 --- a/.sonarcloud.properties +++ b/.sonarcloud.properties @@ -1 +1 @@ -sonar.exclusions=**/Tests/**/_fixtures/* +sonar.exclusions=tests/**/_fixtures/**/* diff --git a/COPYRIGHT b/COPYRIGHT index a684485c39..4e034771b1 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,4 +1,4 @@ -Copyright (C) 1999-2021 Ibexa AS (formerly eZ Systems AS). All rights reserved. +Copyright (C) 1999-2024 Ibexa AS (formerly eZ Systems AS). All rights reserved. This source code is available separately under the following licenses: diff --git a/LICENSE b/LICENSE index 88475992a1..eb76224307 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (C) 1999-2021 Ibexa AS (formerly eZ Systems AS). All rights reserved. +Copyright (C) 1999-2024 Ibexa AS (formerly eZ Systems AS). All rights reserved. This source code is available separately under the following licenses: diff --git a/Readme.md b/Readme.md index 54f0ca76b2..ba9cdc4179 100644 --- a/Readme.md +++ b/Readme.md @@ -10,15 +10,14 @@ Ibexa Kernel also aims to provide additional features for the MVC layer (Symfony ## Current Organization MVC layer: -- [eZ/Bundle](eZ/Bundle) - the bundles that are important to expose the functionality of the Backend and MVC layer to Symfony. -- [eZ/Publish/Core/MVC](eZ/Publish/Core/MVC) - the parts that make up the different components extending Symfony. -- [eZ/Publish/Core/Pagination](eZ/Publish/Core/Pagination) - a component extending PagerFanta for pagination of eZ Platform search queries. +- [src/bundle](src/bundle) - the bundles that are important to expose the functionality of the Backend and MVC layer to Symfony. +- [src/lib/MVC](src/lib/MVC) - the parts that make up the different components extending Symfony. +- [src/lib/Pagination](src/lib/Pagination) - a component extending PagerFanta for pagination of Ibexa search queries. Backend: -- [eZ/Publish/API](eZ/Publish/API) - the definition of stable interfaces for the PHP *Public* API, mainly Content *Repository API*. -- [eZ/Publish/SPI/Persistence](eZ/Publish/SPI/Persistence) - a layer which is not frozen yet, meaning it might change in between releases. Those are persistence interfaces for Storage Engine. -- [eZ/Publish/SPI](eZ/Publish/SPI) - (anything other than Persistence) is frozen and has a Backward Compatibility promise of Service Provider Interface, meaning no breaking changes both from consumption and implementation POV. -- [eZ/Publish/Core](eZ/Publish/Core) - implementations of both APIs and SPIs; the naming aims to map to name of the interface they implement. For example, `Core\Persistence\Legacy` being implementation of `SPI\Persistence`. +- [src/contracts](src/contracts) - the definition of stable interfaces for the PHP *Public* API, mainly Content *Repository API*. +- [src/contracts/Persistence](src/contracts/Persistence) - a layer which is not frozen yet, meaning it might change in between releases. Those are persistence interfaces for Storage Engine. +- [src/lib](src/lib) - implementations of API Contracts; the naming aims to map to name of the interface they implement. For example, `Ibexa\Core\Persistence\Legacy` being implementation of `Ibexa\Contracts\Core\Persistence`. ## Testing Locally @@ -34,15 +33,15 @@ For Contributing to this Bundle, you should make sure to run both unit and integ ```bash # Note: Change the line below to the ssh format of your fork to create topic branches to propose as pull requests - git clone https://github.com/ezsystems/ezplatform-kernel.git - cd ezplatform-kernel + git clone https://github.com/ibexa/core.git + cd core composer install ``` 2. Run unit tests: At this point you should be able to run unit tests: ```bash - php -d memory_limit=-1 vendor/bin/phpunit + composer unit ``` 3. Run integration tests: @@ -51,10 +50,10 @@ For Contributing to this Bundle, you should make sure to run both unit and integ # If you want to test against mysql or postgres instead of sqlite, define one of these with reference to an empty test db: # export DATABASE="mysql://root@localhost/$DB_NAME" # export DATABASE="pgsql://postgres@localhost/$DB_NAME" - php -d memory_limit=-1 vendor/bin/phpunit -c phpunit-integration-legacy.xml + composer integration ``` - To run integration tests against Solr, see [Solr Search Engine Bundle for Ibexa DXP](https://github.com/ezsystems/ezplatform-solr-search-engine). + To run integration tests against Solr, see [Solr Search Engine Bundle for Ibexa DXP](https://github.com/ibexa/solr-search-engine). ## COPYRIGHT diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 8894e037d2..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,47 +0,0 @@ -build: off -clone_depth: 30 -clone_folder: c:\projects\ezplatform-kernel - -skip_commits: - message: /\[skip ci\]/i - -# test only master, stable branches and pull requests -branches: - only: - - master - - /^\d.\d+$/ - -init: - - SET COMPOSER_NO_INTERACTION=1 - - SET ANSICON=121x90 (121x90) - - REG ADD "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v DelayedExpansion /t REG_DWORD /d 1 /f - -install: - - cinst php -y -i --version 7.3.5 - - cinst composer -y -i - - refreshenv - - cd c:\Tools\php73 - - copy /Y php.ini-development php.ini - - echo extension_dir="ext" >> php.ini - - echo max_execution_time=0 >> php.ini - - echo date.timezone="UTC" >> php.ini - - echo extension=php_intl.dll >> php.ini - - echo extension=php_xsl.dll >> php.ini - - echo extension=php_gd2.dll >> php.ini - - echo extension=php_pdo_sqlite.dll >> php.ini - - echo extension=php_mbstring.dll >> php.ini - - echo extension=php_openssl.dll >> php.ini - - echo extension=php_fileinfo.dll >> php.ini - - echo extension=php_curl.dll >> php.ini - - echo memory_limit=3G >> php.ini - - echo default_charset="utf-8" >> php.ini - - cd c:\projects\ezplatform-kernel - - composer install --no-suggest --no-progress --ansi - -test_script: - - cd c:\projects\ezplatform-kernel - - copy /Y config.php-DEVELOPMENT config.php - - SET X=0 - - vendor/bin/phpunit --colors=never -c phpunit.xml || SET X=!errorlevel! - - vendor/bin/phpunit --colors=never -c phpunit-integration-legacy.xml || SET X=!errorlevel! - - exit %X% diff --git a/bin/extract-translations.sh b/bin/extract-translations.sh deleted file mode 100755 index e76330bc6f..0000000000 --- a/bin/extract-translations.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env sh -echo 'Translation extraction'; -cd ../../..; -# Extract string for default locale -echo '# Extract Kernel : EzPublishCoreBundle'; -./bin/console translation:extract en -v \ - --dir=./vendor/ezsystems/ezplatform-kernel/eZ \ - --exclude-dir=Tests \ - --exclude-dir=Features \ - --exclude-dir=tests \ - --output-dir=./vendor/ezsystems/ezplatform-kernel/eZ/Bundle/EzPublishCoreBundle/Resources/translations \ - --enable-extractor=ez_fieldtypes \ - --keep - "$@" - -echo '# Clean file references'; -sed -i "s|>.*/vendor/ezsystems/ezplatform-kernel/|>|g" ./vendor/ezsystems/ezplatform-kernel/eZ/Bundle/EzPublishCoreBundle/Resources/translations/*.xlf - -cd vendor/ezsystems/ezplatform-kernel; -echo 'Translation extraction done'; diff --git a/bootstrap.php b/bootstrap.php index 4168370f78..4ead6e13d3 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -1,33 +1,12 @@ __DIR__, - // Path to the containerBuilder.php file used by service container - 'container_builder_path' => __DIR__ . '/eZ/Publish/Core/settings/containerBuilder.php', - // The cache directory - 'cache_dir' => __DIR__ . '/var/cache', -); diff --git a/container.php b/container.php deleted file mode 100644 index 4082a8e15b..0000000000 --- a/container.php +++ /dev/null @@ -1,27 +0,0 @@ -',1024392098,14,1,'folder',2,1,2,1448831672,14,'a3d4 (1,'',1031484992,14,5,'image',2,0,3,1048494784,14,'f6df12aa74e36230eb675f364fccd25a',NULL,'a:2:{s:6:\"eng-GB\";s:5:\"Image\";s:16:\"always-available\";s:6:\"eng-GB\";}',1,1,NULL,0), (1,'',1052385472,14,12,'file',2,0,3,1052385669,14,'637d58bfddf164627bdfd265733280a0',NULL,'a:2:{s:6:\"eng-GB\";s:4:\"File\";s:16:\"always-available\";s:6:\"eng-GB\";}',1,1,NULL,0); -INSERT INTO `ezcontentclass_attribute` (`can_translate`, `category`, `contentclass_id`, `data_float1`, `data_float2`, `data_float3`, `data_float4`, `data_int1`, `data_int2`, `data_int3`, `data_int4`, `data_text1`, `data_text2`, `data_text3`, `data_text4`, `data_text5`, `data_type_string`, `id`, `identifier`, `is_information_collector`, `is_required`, `is_searchable`, `placement`, `serialized_data_text`, `serialized_description_list`, `serialized_name_list`, `version`) -VALUES (1,'',2,0,0,0,0,255,0,0,0,'New article','','','','','ezstring',1,'title',0,1,1,1,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:5:\"Title\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',1,NULL,NULL,NULL,NULL,255,0,NULL,NULL,'Folder',NULL,NULL,NULL,NULL,'ezstring',4,'name',0,1,1,1,'N;','a:0:{}','a:1:{s:6:\"eng-GB\";s:4:\"Name\";}',0), - (1,'',3,0,0,0,0,255,0,0,0,'','','','',NULL,'ezstring',6,'name',0,1,1,1,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:4:\"Name\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',3,0,0,0,0,255,0,0,0,'','','','',NULL,'ezstring',7,'description',0,0,1,2,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:11:\"Description\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',4,0,0,0,0,255,0,0,0,'','','','','','ezstring',8,'first_name',0,1,1,1,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:10:\"First name\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',4,0,0,0,0,255,0,0,0,'','','','','','ezstring',9,'last_name',0,1,1,2,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:9:\"Last name\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (0,'',4,0,0,0,0,7,10,0,0,'','^[^@]+$','','','','ezuser',12,'user_account',0,1,0,3,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:12:\"User account\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',5,0,0,0,0,150,0,0,0,'','','','',NULL,'ezstring',116,'name',0,1,1,1,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:4:\"Name\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',5,0,0,0,0,10,0,0,0,'','','','',NULL,'ezrichtext',117,'caption',0,0,1,2,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:7:\"Caption\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',5,0,0,0,0,10,0,0,0,'','','','',NULL,'ezimage',118,'image',0,0,0,3,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:5:\"Image\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',1,NULL,NULL,NULL,NULL,5,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'ezrichtext',119,'short_description',0,0,1,3,'N;','a:0:{}','a:1:{s:6:\"eng-GB\";s:17:\"Short description\";}',0), - (1,'',2,0,0,0,0,10,0,0,0,'','','','','','ezrichtext',120,'intro',0,1,1,4,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:5:\"Intro\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',2,0,0,0,0,20,0,0,0,'','','','','','ezrichtext',121,'body',0,0,1,5,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:4:\"Body\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (0,'',2,0,0,0,0,0,0,0,0,'','','','','','ezboolean',123,'enable_comments',0,0,0,6,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:15:\"Enable comments\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',12,0,0,0,0,0,0,0,0,'New file','','','',NULL,'ezstring',146,'name',0,1,1,1,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:4:\"Name\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',12,0,0,0,0,10,0,0,0,'','','','',NULL,'ezrichtext',147,'description',0,0,1,2,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:11:\"Description\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',12,0,0,0,0,0,0,0,0,'','','','',NULL,'ezbinaryfile',148,'file',0,1,0,3,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:4:\"File\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',2,0,0,0,0,255,0,0,0,'','','','','','ezstring',152,'short_title',0,0,1,2,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:11:\"Short title\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',2,0,0,0,0,1,0,0,0,'','','','','','ezauthor',153,'author',0,0,0,3,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:6:\"Author\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',2,0,0,0,0,0,0,0,0,'','','','','','ezobjectrelation',154,'image',0,0,1,7,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:5:\"Image\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',1,NULL,NULL,NULL,NULL,100,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'ezstring',155,'short_name',0,0,1,2,'N;','a:0:{}','a:1:{s:6:\"eng-GB\";s:10:\"Short name\";}',0), - (1,'',1,NULL,NULL,NULL,NULL,20,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'ezrichtext',156,'description',0,0,1,4,'N;','a:0:{}','a:1:{s:6:\"eng-GB\";s:11:\"Description\";}',0), - (1,'',4,0,0,0,0,10,0,0,0,'','','','','','eztext',179,'signature',0,0,1,4,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:9:\"Signature\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), - (1,'',4,0,0,0,0,10,0,0,0,'','','','','','ezimage',180,'image',0,0,0,5,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:5:\"Image\";s:16:\"always-available\";s:6:\"eng-GB\";}',0); +INSERT INTO `ezcontentclass_attribute` (`can_translate`, `category`, `contentclass_id`, `data_float1`, `data_float2`, `data_float3`, `data_float4`, `data_int1`, `data_int2`, `data_int3`, `data_int4`, `data_text1`, `data_text2`, `data_text3`, `data_text4`, `data_text5`, `data_type_string`, `id`, `identifier`, `is_information_collector`, `is_required`, `is_searchable`, `is_thumbnail`, `placement`, `serialized_data_text`, `serialized_description_list`, `serialized_name_list`, `version`) +VALUES (1,'',2,0,0,0,0,255,0,0,0,'New article','','','','','ezstring',1,'title',0,1,1,1,0,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:5:\"Title\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',1,NULL,NULL,NULL,NULL,255,0,NULL,NULL,'Folder',NULL,NULL,NULL,NULL,'ezstring',4,'name',0,1,1,0,1,'N;','a:0:{}','a:1:{s:6:\"eng-GB\";s:4:\"Name\";}',0), + (1,'',3,0,0,0,0,255,0,0,0,'','','','',NULL,'ezstring',6,'name',0,1,1,0,1,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:4:\"Name\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',3,0,0,0,0,255,0,0,0,'','','','',NULL,'ezstring',7,'description',0,0,1,0,2,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:11:\"Description\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',4,0,0,0,0,255,0,0,0,'','','','','','ezstring',8,'first_name',0,1,1,0,1,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:10:\"First name\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',4,0,0,0,0,255,0,0,0,'','','','','','ezstring',9,'last_name',0,1,1,0,2,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:9:\"Last name\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (0,'',4,0,0,0,0,7,10,0,0,'','^[^@]+$','','','','ezuser',12,'user_account',0,1,0,0,3,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:12:\"User account\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',5,0,0,0,0,150,0,0,0,'','','','',NULL,'ezstring',116,'name',0,1,1,0,1,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:4:\"Name\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',5,0,0,0,0,10,0,0,0,'','','','',NULL,'ezrichtext',117,'caption',0,0,1,0,2,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:7:\"Caption\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',5,10.0,0,0,0,0,0,0,0,'MB','','','',NULL,'ezimage',118,'image',0,0,1,1,3,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:5:\"Image\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',1,NULL,NULL,NULL,NULL,5,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'ezrichtext',119,'short_description',0,0,1,0,3,'N;','a:0:{}','a:1:{s:6:\"eng-GB\";s:17:\"Short description\";}',0), + (1,'',2,0,0,0,0,10,0,0,0,'','','','','','ezrichtext',120,'intro',0,1,1,0,4,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:5:\"Intro\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',2,0,0,0,0,20,0,0,0,'','','','','','ezrichtext',121,'body',0,0,1,0,5,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:4:\"Body\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (0,'',2,0,0,0,0,0,0,0,0,'','','','','','ezboolean',123,'enable_comments',0,0,0,0,6,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:15:\"Enable comments\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',12,0,0,0,0,0,0,0,0,'New file','','','',NULL,'ezstring',146,'name',0,1,1,0,1,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:4:\"Name\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',12,0,0,0,0,10,0,0,0,'','','','',NULL,'ezrichtext',147,'description',0,0,1,0,2,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:11:\"Description\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',12,0,0,0,0,0,0,0,0,'','','','',NULL,'ezbinaryfile',148,'file',0,1,0,0,3,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:4:\"File\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',2,0,0,0,0,255,0,0,0,'','','','','','ezstring',152,'short_title',0,0,1,0,2,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:11:\"Short title\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',2,0,0,0,0,1,0,0,0,'','','','','','ezauthor',153,'author',0,0,0,0,3,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:6:\"Author\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',2,0,0,0,0,0,0,0,0,'','','','','','ezobjectrelation',154,'image',0,0,1,0,7,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:5:\"Image\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',1,NULL,NULL,NULL,NULL,100,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'ezstring',155,'short_name',0,0,1,0,2,'N;','a:0:{}','a:1:{s:6:\"eng-GB\";s:10:\"Short name\";}',0), + (1,'',1,NULL,NULL,NULL,NULL,20,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'ezrichtext',156,'description',0,0,1,0,4,'N;','a:0:{}','a:1:{s:6:\"eng-GB\";s:11:\"Description\";}',0), + (1,'',4,0,0,0,0,10,0,0,0,'','','','','','eztext',179,'signature',0,0,1,0,4,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:9:\"Signature\";s:16:\"always-available\";s:6:\"eng-GB\";}',0), + (1,'',4,10.0,0,0,0,0,0,0,0,'MB','','','','','ezimage',180,'image',0,0,0,1,5,NULL,NULL,'a:2:{s:6:\"eng-GB\";s:5:\"Image\";s:16:\"always-available\";s:6:\"eng-GB\";}',0); INSERT INTO `ezcontentclass_classgroup` (`contentclass_id`, `contentclass_version`, `group_id`, `group_name`) VALUES (1, 0, 1, 'Content'), @@ -93,14 +93,14 @@ VALUES (1,9,1,2,3,1448889046,'Ibexa Platform',14,1448889046,'9459d3c29e15006e451 (3,1,13,2,3,1033920794,'Editors',14,1033920794,'3c160cca19fb135f83bd02d911f04db2',2,1), (4,3,14,2,3,1301062024,'Administrator User',14,1033920830,'1bb4fe25487f05527efa8bfd394cecc7',2,1), (1,1,41,2,3,1060695457,'Media',14,1060695457,'a6e35cbcb7cd6ae4b691f3eee30cd262',3,1), - (3,1,42,2,3,1072180330,'Anonymous Users',14,1072180330,'15b256dbea2ae72418ff5facc999e8f9',2,1), + (3,1,42,2,3,1072180330,'Anonymous users',14,1072180330,'15b256dbea2ae72418ff5facc999e8f9',2,1), (1,1,49,2,3,1080220197,'Images',14,1080220197,'e7ff633c6b8e0fd3531e74c6e712bead',3,1), (1,1,50,2,3,1080220220,'Files',14,1080220220,'732a5acd01b51a6fe6eab448ad4138a9',3,1), (1,1,51,2,3,1080220233,'Multimedia',14,1080220233,'09082deb98662a104f325aaa8c4933d3',3,1); INSERT INTO `ezcontentobject_attribute` (`attribute_original_id`, `contentclassattribute_id`, `contentobject_id`, `data_float`, `data_int`, `data_text`, `data_type_string`, `id`, `language_code`, `language_id`, `sort_key_int`, `sort_key_string`, `version`) VALUES (0,4,1,NULL,NULL,'Ibexa Platform','ezstring',1,'eng-GB',3,0,'ibexa platform',9), - (0,119,1,NULL,NULL,'
You are now ready to start your project.
','ezrichtext',2,'eng-GB',3,0,'',9), + (0,119,1,NULL,NULL,'
You are now ready to start your project.
','ezrichtext',2,'eng-GB',3,0,'',9), (0,7,4,NULL,NULL,'Main group','ezstring',7,'eng-GB',3,0,'',1), (0,6,4,NULL,NULL,'Users','ezstring',8,'eng-GB',3,0,'',1), (0,8,10,0,0,'Anonymous','ezstring',19,'eng-GB',3,0,'anonymous',2), @@ -116,25 +116,25 @@ VALUES (0,4,1,NULL,NULL,'Ibexa Platform','ezstring',1,'eng-GB',3,0,'ibexa platfo (0,9,14,0,0,'User','ezstring',29,'eng-GB',3,0,'user',3), (30,12,14,0,0,'','ezuser',30,'eng-GB',3,0,'',3), (0,4,41,0,0,'Media','ezstring',98,'eng-GB',3,0,'',1), - (0,119,41,0,1045487555,'\n
\n','ezrichtext',99,'eng-GB',3,0,'',1), - (0,6,42,0,0,'Anonymous Users','ezstring',100,'eng-GB',3,0,'anonymous users',1), + (0,119,41,0,1045487555,'\n
\n','ezrichtext',99,'eng-GB',3,0,'',1), + (0,6,42,0,0,'Anonymous users','ezstring',100,'eng-GB',3,0,'anonymous users',1), (0,7,42,0,0,'User group for the anonymous user','ezstring',101,'eng-GB',3,0,'user group for the anonymous user',1), (0,155,1,NULL,NULL,'Ibexa Platform','ezstring',102,'eng-GB',3,0,'ibexa platform',9), (0,155,41,0,0,'','ezstring',103,'eng-GB',3,0,'',1), - (0,156,1,NULL,NULL,'
This is the clean installation coming with Ibexa Platform.It''s a bare-bones setup of the Platform, an excellent foundation to build upon if you want to start your project from scratch.
','ezrichtext',104,'eng-GB',3,0,'',9), - (0,156,41,0,1045487555,'\n
\n','ezrichtext',105,'eng-GB',3,0,'',1), + (0,156,1,NULL,NULL,'
This is the clean installation coming with Ibexa Platform.It''s a bare-bones setup of the Platform, an excellent foundation to build upon if you want to start your project from scratch.
','ezrichtext',104,'eng-GB',3,0,'',9), + (0,156,41,0,1045487555,'\n
\n','ezrichtext',105,'eng-GB',3,0,'',1), (0,4,49,0,0,'Images','ezstring',142,'eng-GB',3,0,'images',1), (0,155,49,0,0,'','ezstring',143,'eng-GB',3,0,'',1), - (0,119,49,0,1045487555,'\n
\n','ezrichtext',144,'eng-GB',3,0,'',1), - (0,156,49,0,1045487555,'\n
\n','ezrichtext',145,'eng-GB',3,0,'',1), + (0,119,49,0,1045487555,'\n
\n','ezrichtext',144,'eng-GB',3,0,'',1), + (0,156,49,0,1045487555,'\n
\n','ezrichtext',145,'eng-GB',3,0,'',1), (0,4,50,0,0,'Files','ezstring',147,'eng-GB',3,0,'files',1), (0,155,50,0,0,'','ezstring',148,'eng-GB',3,0,'',1), - (0,119,50,0,1045487555,'\n
\n','ezrichtext',149,'eng-GB',3,0,'',1), - (0,156,50,0,1045487555,'\n
\n','ezrichtext',150,'eng-GB',3,0,'',1), + (0,119,50,0,1045487555,'\n
\n','ezrichtext',149,'eng-GB',3,0,'',1), + (0,156,50,0,1045487555,'\n
\n','ezrichtext',150,'eng-GB',3,0,'',1), (0,4,51,0,0,'Multimedia','ezstring',152,'eng-GB',3,0,'multimedia',1), (0,155,51,0,0,'','ezstring',153,'eng-GB',3,0,'',1), - (0,119,51,0,1045487555,'\n
\n','ezrichtext',154,'eng-GB',3,0,'',1), - (0,156,51,0,1045487555,'\n
\n','ezrichtext',155,'eng-GB',3,0,'',1), + (0,119,51,0,1045487555,'\n
\n','ezrichtext',154,'eng-GB',3,0,'',1), + (0,156,51,0,1045487555,'\n
\n','ezrichtext',155,'eng-GB',3,0,'',1), (0,179,10,0,0,'','eztext',177,'eng-GB',3,0,'',2), (0,179,14,0,0,'','eztext',178,'eng-GB',3,0,'',3), (0,180,10,0,0,'','ezimage',179,'eng-GB',3,0,'',2), @@ -149,7 +149,7 @@ VALUES ('eng-GB',9,1,2,'Ibexa Platform','eng-GB'), ('eng-GB',1,13,3,'Editors','eng-GB'), ('eng-GB',3,14,3,'Administrator User','eng-GB'), ('eng-GB',1,41,3,'Media','eng-GB'), - ('eng-GB',1,42,3,'Anonymous Users','eng-GB'), + ('eng-GB',1,42,3,'Anonymous users','eng-GB'), ('eng-GB',1,49,3,'Images','eng-GB'), ('eng-GB',1,50,3,'Files','eng-GB'), ('eng-GB',1,51,3,'Multimedia','eng-GB'); @@ -236,7 +236,7 @@ VALUES (1,'standard','','Standard','ezcontentnavigationpart'), (3,'media','','Media','ezmedianavigationpart'); INSERT INTO `ezsite_data` (`name`, `value`) -VALUES ('ibexa-release','3.3'); +VALUES ('ibexa-release','4.6'); INSERT INTO `ezurlalias` (`destination_url`, `forward_to_id`, `id`, `is_imported`, `is_internal`, `is_wildcard`, `source_md5`, `source_url`) VALUES ('content/view/full/2',0,12,1,1,0,'d41d8cd98f00b204e9800998ecf8427e',''), @@ -299,3 +299,6 @@ VALUES (11,28,'','',1), INSERT INTO `ezuser_setting` (`is_enabled`, `max_login`, `user_id`) VALUES (1,1000,10), (1,10,14); + +INSERT INTO `ezpreferences` (`name`, `user_id`, `value`) +SELECT 'focus_mode', u.contentobject_id, '0' FROM `ezuser` u WHERE u.login = 'admin'; diff --git a/data/postgresql/cleandata.sql b/data/postgresql/cleandata.sql index 2d4ef235bd..7c63933b18 100644 --- a/data/postgresql/cleandata.sql +++ b/data/postgresql/cleandata.sql @@ -37,31 +37,31 @@ VALUES (1,'',1024392098,14,1,'folder',2,1,2,1448831672,14,'a3d4 (1,'',1031484992,14,5,'image',2,0,3,1048494784,14,'f6df12aa74e36230eb675f364fccd25a',NULL,'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}',1,1,NULL,0), (1,'',1052385472,14,12,'file',2,0,3,1052385669,14,'637d58bfddf164627bdfd265733280a0',NULL,'a:2:{s:6:"eng-GB";s:4:"File";s:16:"always-available";s:6:"eng-GB";}',1,1,NULL,0); -INSERT INTO "ezcontentclass_attribute" ("can_translate", "category", "contentclass_id", "data_float1", "data_float2", "data_float3", "data_float4", "data_int1", "data_int2", "data_int3", "data_int4", "data_text1", "data_text2", "data_text3", "data_text4", "data_text5", "data_type_string", "id", "identifier", "is_information_collector", "is_required", "is_searchable", "placement", "serialized_data_text", "serialized_description_list", "serialized_name_list", "version") -VALUES (1,'',2,0,0,0,0,255,0,0,0,'New article','','','','','ezstring',1,'title',0,1,1,1,NULL,NULL,'a:2:{s:6:"eng-GB";s:5:"Title";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',1,NULL,NULL,NULL,NULL,255,0,NULL,NULL,'Folder',NULL,NULL,NULL,NULL,'ezstring',4,'name',0,1,1,1,'N;','a:0:{}','a:1:{s:6:"eng-GB";s:4:"Name";}',0), - (1,'',3,0,0,0,0,255,0,0,0,'','','','',NULL,'ezstring',6,'name',0,1,1,1,NULL,NULL,'a:2:{s:6:"eng-GB";s:4:"Name";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',3,0,0,0,0,255,0,0,0,'','','','',NULL,'ezstring',7,'description',0,0,1,2,NULL,NULL,'a:2:{s:6:"eng-GB";s:11:"Description";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',4,0,0,0,0,255,0,0,0,'','','','','','ezstring',8,'first_name',0,1,1,1,NULL,NULL,'a:2:{s:6:"eng-GB";s:10:"First name";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',4,0,0,0,0,255,0,0,0,'','','','','','ezstring',9,'last_name',0,1,1,2,NULL,NULL,'a:2:{s:6:"eng-GB";s:9:"Last name";s:16:"always-available";s:6:"eng-GB";}',0), - (0,'',4,0,0,0,0,7,10,0,0,'','^[^@]+$','','','','ezuser',12,'user_account',0,1,0,3,NULL,NULL,'a:2:{s:6:"eng-GB";s:12:"User account";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',5,0,0,0,0,150,0,0,0,'','','','',NULL,'ezstring',116,'name',0,1,1,1,NULL,NULL,'a:2:{s:6:"eng-GB";s:4:"Name";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',5,0,0,0,0,10,0,0,0,'','','','',NULL,'ezrichtext',117,'caption',0,0,1,2,NULL,NULL,'a:2:{s:6:"eng-GB";s:7:"Caption";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',5,0,0,0,0,10,0,0,0,'','','','',NULL,'ezimage',118,'image',0,0,0,3,NULL,NULL,'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',1,NULL,NULL,NULL,NULL,5,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'ezrichtext',119,'short_description',0,0,1,3,'N;','a:0:{}','a:1:{s:6:"eng-GB";s:17:"Short description";}',0), - (1,'',2,0,0,0,0,10,0,0,0,'','','','','','ezrichtext',120,'intro',0,1,1,4,NULL,NULL,'a:2:{s:6:"eng-GB";s:5:"Intro";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',2,0,0,0,0,20,0,0,0,'','','','','','ezrichtext',121,'body',0,0,1,5,NULL,NULL,'a:2:{s:6:"eng-GB";s:4:"Body";s:16:"always-available";s:6:"eng-GB";}',0), - (0,'',2,0,0,0,0,0,0,0,0,'','','','','','ezboolean',123,'enable_comments',0,0,0,6,NULL,NULL,'a:2:{s:6:"eng-GB";s:15:"Enable comments";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',12,0,0,0,0,0,0,0,0,'New file','','','',NULL,'ezstring',146,'name',0,1,1,1,NULL,NULL,'a:2:{s:6:"eng-GB";s:4:"Name";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',12,0,0,0,0,10,0,0,0,'','','','',NULL,'ezrichtext',147,'description',0,0,1,2,NULL,NULL,'a:2:{s:6:"eng-GB";s:11:"Description";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',12,0,0,0,0,0,0,0,0,'','','','',NULL,'ezbinaryfile',148,'file',0,1,0,3,NULL,NULL,'a:2:{s:6:"eng-GB";s:4:"File";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',2,0,0,0,0,255,0,0,0,'','','','','','ezstring',152,'short_title',0,0,1,2,NULL,NULL,'a:2:{s:6:"eng-GB";s:11:"Short title";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',2,0,0,0,0,1,0,0,0,'','','','','','ezauthor',153,'author',0,0,0,3,NULL,NULL,'a:2:{s:6:"eng-GB";s:6:"Author";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',2,0,0,0,0,0,0,0,0,'','','','','','ezobjectrelation',154,'image',0,0,1,7,NULL,NULL,'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',1,NULL,NULL,NULL,NULL,100,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'ezstring',155,'short_name',0,0,1,2,'N;','a:0:{}','a:1:{s:6:"eng-GB";s:10:"Short name";}',0), - (1,'',1,NULL,NULL,NULL,NULL,20,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'ezrichtext',156,'description',0,0,1,4,'N;','a:0:{}','a:1:{s:6:"eng-GB";s:11:"Description";}',0), - (1,'',4,0,0,0,0,10,0,0,0,'','','','','','eztext',179,'signature',0,0,1,4,NULL,NULL,'a:2:{s:6:"eng-GB";s:9:"Signature";s:16:"always-available";s:6:"eng-GB";}',0), - (1,'',4,0,0,0,0,10,0,0,0,'','','','','','ezimage',180,'image',0,0,0,5,NULL,NULL,'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}',0); +INSERT INTO "ezcontentclass_attribute" ("can_translate", "category", "contentclass_id", "data_float1", "data_float2", "data_float3", "data_float4", "data_int1", "data_int2", "data_int3", "data_int4", "data_text1", "data_text2", "data_text3", "data_text4", "data_text5", "data_type_string", "id", "identifier", "is_information_collector", "is_required", "is_searchable", "is_thumbnail", "placement", "serialized_data_text", "serialized_description_list", "serialized_name_list", "version") +VALUES (1,'',2,0,0,0,0,255,0,0,0,'New article','','','','','ezstring',1,'title',0,1,1,FALSE,1,NULL,NULL,'a:2:{s:6:"eng-GB";s:5:"Title";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',1,NULL,NULL,NULL,NULL,255,0,NULL,NULL,'Folder',NULL,NULL,NULL,NULL,'ezstring',4,'name',0,1,1,FALSE,1,'N;','a:0:{}','a:1:{s:6:"eng-GB";s:4:"Name";}',0), + (1,'',3,0,0,0,0,255,0,0,0,'','','','',NULL,'ezstring',6,'name',0,1,1,FALSE,1,NULL,NULL,'a:2:{s:6:"eng-GB";s:4:"Name";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',3,0,0,0,0,255,0,0,0,'','','','',NULL,'ezstring',7,'description',0,0,1,FALSE,2,NULL,NULL,'a:2:{s:6:"eng-GB";s:11:"Description";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',4,0,0,0,0,255,0,0,0,'','','','','','ezstring',8,'first_name',0,1,1,FALSE,1,NULL,NULL,'a:2:{s:6:"eng-GB";s:10:"First name";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',4,0,0,0,0,255,0,0,0,'','','','','','ezstring',9,'last_name',0,1,1,FALSE,2,NULL,NULL,'a:2:{s:6:"eng-GB";s:9:"Last name";s:16:"always-available";s:6:"eng-GB";}',0), + (0,'',4,0,0,0,0,7,10,0,0,'','^[^@]+$','','','','ezuser',12,'user_account',0,1,0,FALSE,3,NULL,NULL,'a:2:{s:6:"eng-GB";s:12:"User account";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',5,0,0,0,0,150,0,0,0,'','','','',NULL,'ezstring',116,'name',0,1,1,FALSE,1,NULL,NULL,'a:2:{s:6:"eng-GB";s:4:"Name";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',5,0,0,0,0,10,0,0,0,'','','','',NULL,'ezrichtext',117,'caption',0,0,1,FALSE,2,NULL,NULL,'a:2:{s:6:"eng-GB";s:7:"Caption";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',5,10.0,0,0,0,0,0,0,0,'MB','','','',NULL,'ezimage',118,'image',0,0,1,TRUE,3,NULL,NULL,'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',1,NULL,NULL,NULL,NULL,5,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'ezrichtext',119,'short_description',0,0,1,FALSE,3,'N;','a:0:{}','a:1:{s:6:"eng-GB";s:17:"Short description";}',0), + (1,'',2,0,0,0,0,10,0,0,0,'','','','','','ezrichtext',120,'intro',0,1,1,FALSE,4,NULL,NULL,'a:2:{s:6:"eng-GB";s:5:"Intro";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',2,0,0,0,0,20,0,0,0,'','','','','','ezrichtext',121,'body',0,0,1,FALSE,5,NULL,NULL,'a:2:{s:6:"eng-GB";s:4:"Body";s:16:"always-available";s:6:"eng-GB";}',0), + (0,'',2,0,0,0,0,0,0,0,0,'','','','','','ezboolean',123,'enable_comments',0,0,0,FALSE,6,NULL,NULL,'a:2:{s:6:"eng-GB";s:15:"Enable comments";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',12,0,0,0,0,0,0,0,0,'New file','','','',NULL,'ezstring',146,'name',0,1,1,FALSE,1,NULL,NULL,'a:2:{s:6:"eng-GB";s:4:"Name";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',12,0,0,0,0,10,0,0,0,'','','','',NULL,'ezrichtext',147,'description',0,0,1,FALSE,2,NULL,NULL,'a:2:{s:6:"eng-GB";s:11:"Description";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',12,0,0,0,0,0,0,0,0,'','','','',NULL,'ezbinaryfile',148,'file',0,1,0,FALSE,3,NULL,NULL,'a:2:{s:6:"eng-GB";s:4:"File";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',2,0,0,0,0,255,0,0,0,'','','','','','ezstring',152,'short_title',0,0,1,FALSE,2,NULL,NULL,'a:2:{s:6:"eng-GB";s:11:"Short title";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',2,0,0,0,0,1,0,0,0,'','','','','','ezauthor',153,'author',0,0,0,FALSE,3,NULL,NULL,'a:2:{s:6:"eng-GB";s:6:"Author";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',2,0,0,0,0,0,0,0,0,'','','','','','ezobjectrelation',154,'image',0,0,1,FALSE,7,NULL,NULL,'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',1,NULL,NULL,NULL,NULL,100,0,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'ezstring',155,'short_name',0,0,1,FALSE,2,'N;','a:0:{}','a:1:{s:6:"eng-GB";s:10:"Short name";}',0), + (1,'',1,NULL,NULL,NULL,NULL,20,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,'ezrichtext',156,'description',0,0,1,FALSE,4,'N;','a:0:{}','a:1:{s:6:"eng-GB";s:11:"Description";}',0), + (1,'',4,0,0,0,0,10,0,0,0,'','','','','','eztext',179,'signature',0,0,1,FALSE,4,NULL,NULL,'a:2:{s:6:"eng-GB";s:9:"Signature";s:16:"always-available";s:6:"eng-GB";}',0), + (1,'',4,10.0,0,0,0,0,0,0,0,'MB','','','','','ezimage',180,'image',0,0,0,TRUE,5,NULL,NULL,'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}',0); INSERT INTO "ezcontentclass_classgroup" ("contentclass_id", "contentclass_version", "group_id", "group_name") VALUES (1,0,1,'Content'), @@ -93,14 +93,14 @@ VALUES (1,9,1,2,3,1448889046,'Ibexa Platform',14,1448889046,'9459d3c29e15006e451 (3,1,13,2,3,1033920794,'Editors',14,1033920794,'3c160cca19fb135f83bd02d911f04db2',2,1), (4,3,14,2,3,1301062024,'Administrator User',14,1033920830,'1bb4fe25487f05527efa8bfd394cecc7',2,1), (1,1,41,2,3,1060695457,'Media',14,1060695457,'a6e35cbcb7cd6ae4b691f3eee30cd262',3,1), - (3,1,42,2,3,1072180330,'Anonymous Users',14,1072180330,'15b256dbea2ae72418ff5facc999e8f9',2,1), + (3,1,42,2,3,1072180330,'Anonymous users',14,1072180330,'15b256dbea2ae72418ff5facc999e8f9',2,1), (1,1,49,2,3,1080220197,'Images',14,1080220197,'e7ff633c6b8e0fd3531e74c6e712bead',3,1), (1,1,50,2,3,1080220220,'Files',14,1080220220,'732a5acd01b51a6fe6eab448ad4138a9',3,1), (1,1,51,2,3,1080220233,'Multimedia',14,1080220233,'09082deb98662a104f325aaa8c4933d3',3,1); INSERT INTO "ezcontentobject_attribute" ("attribute_original_id", "contentclassattribute_id", "contentobject_id", "data_float", "data_int", "data_text", "data_type_string", "id", "language_code", "language_id", "sort_key_int", "sort_key_string", "version") VALUES (0,4,1,NULL,NULL,'Ibexa Platform','ezstring',1,'eng-GB',3,0,'ibexa platform',9), - (0,119,1,NULL,NULL,E'
You are now ready to start your project.
','ezrichtext',2,'eng-GB',3,0,'',9), + (0,119,1,NULL,NULL,E'
You are now ready to start your project.
','ezrichtext',2,'eng-GB',3,0,'',9), (0,7,4,NULL,NULL,'Main group','ezstring',7,'eng-GB',3,0,'',1), (0,6,4,NULL,NULL,'Users','ezstring',8,'eng-GB',3,0,'',1), (0,8,10,0,0,'Anonymous','ezstring',19,'eng-GB',3,0,'anonymous',2), @@ -116,25 +116,25 @@ VALUES (0,4,1,NULL,NULL,'Ibexa Platform','ezstring',1,'eng-GB',3,0,'ibexa platfo (0,9,14,0,0,'User','ezstring',29,'eng-GB',3,0,'user',3), (30,12,14,0,0,'','ezuser',30,'eng-GB',3,0,'',3), (0,4,41,0,0,'Media','ezstring',98,'eng-GB',3,0,'',1), - (0,119,41,0,1045487555,E'\n
\n','ezrichtext',99,'eng-GB',3,0,'',1), - (0,6,42,0,0,'Anonymous Users','ezstring',100,'eng-GB',3,0,'anonymous users',1), + (0,119,41,0,1045487555,E'\n
\n','ezrichtext',99,'eng-GB',3,0,'',1), + (0,6,42,0,0,'Anonymous users','ezstring',100,'eng-GB',3,0,'anonymous users',1), (0,7,42,0,0,'User group for the anonymous user','ezstring',101,'eng-GB',3,0,'user group for the anonymous user',1), (0,155,1,NULL,NULL,'Ibexa Platform','ezstring',102,'eng-GB',3,0,'ibexa platform',9), (0,155,41,0,0,'','ezstring',103,'eng-GB',3,0,'',1), - (0,156,1,NULL,NULL,E'
This is the clean installation coming with Ibexa Platform.It''s a bare-bones setup of the Platform, an excellent foundation to build upon if you want to start your project from scratch.
','ezrichtext',104,'eng-GB',3,0,'',9), - (0,156,41,0,1045487555,E'\n
\n','ezrichtext',105,'eng-GB',3,0,'',1), + (0,156,1,NULL,NULL,E'
This is the clean installation coming with Ibexa Platform.It''s a bare-bones setup of the Platform, an excellent foundation to build upon if you want to start your project from scratch.
','ezrichtext',104,'eng-GB',3,0,'',9), + (0,156,41,0,1045487555,E'\n
\n','ezrichtext',105,'eng-GB',3,0,'',1), (0,4,49,0,0,'Images','ezstring',142,'eng-GB',3,0,'images',1), (0,155,49,0,0,'','ezstring',143,'eng-GB',3,0,'',1), - (0,119,49,0,1045487555,E'\n
\n','ezrichtext',144,'eng-GB',3,0,'',1), - (0,156,49,0,1045487555,E'\n
\n','ezrichtext',145,'eng-GB',3,0,'',1), + (0,119,49,0,1045487555,E'\n
\n','ezrichtext',144,'eng-GB',3,0,'',1), + (0,156,49,0,1045487555,E'\n
\n','ezrichtext',145,'eng-GB',3,0,'',1), (0,4,50,0,0,'Files','ezstring',147,'eng-GB',3,0,'files',1), (0,155,50,0,0,'','ezstring',148,'eng-GB',3,0,'',1), - (0,119,50,0,1045487555,E'\n
\n','ezrichtext',149,'eng-GB',3,0,'',1), - (0,156,50,0,1045487555,E'\n
\n','ezrichtext',150,'eng-GB',3,0,'',1), + (0,119,50,0,1045487555,E'\n
\n','ezrichtext',149,'eng-GB',3,0,'',1), + (0,156,50,0,1045487555,E'\n
\n','ezrichtext',150,'eng-GB',3,0,'',1), (0,4,51,0,0,'Multimedia','ezstring',152,'eng-GB',3,0,'multimedia',1), (0,155,51,0,0,'','ezstring',153,'eng-GB',3,0,'',1), - (0,119,51,0,1045487555,E'\n
\n','ezrichtext',154,'eng-GB',3,0,'',1), - (0,156,51,0,1045487555,E'\n
\n','ezrichtext',155,'eng-GB',3,0,'',1), + (0,119,51,0,1045487555,E'\n
\n','ezrichtext',154,'eng-GB',3,0,'',1), + (0,156,51,0,1045487555,E'\n
\n','ezrichtext',155,'eng-GB',3,0,'',1), (0,179,10,0,0,'','eztext',177,'eng-GB',3,0,'',2), (0,179,14,0,0,'','eztext',178,'eng-GB',3,0,'',3), (0,180,10,0,0,'','ezimage',179,'eng-GB',3,0,'',2), @@ -149,7 +149,7 @@ VALUES ('eng-GB',9,1,2,'Ibexa Platform','eng-GB'), ('eng-GB',1,13,3,'Editors','eng-GB'), ('eng-GB',3,14,3,'Administrator User','eng-GB'), ('eng-GB',1,41,3,'Media','eng-GB'), - ('eng-GB',1,42,3,'Anonymous Users','eng-GB'), + ('eng-GB',1,42,3,'Anonymous users','eng-GB'), ('eng-GB',1,49,3,'Images','eng-GB'), ('eng-GB',1,50,3,'Files','eng-GB'), ('eng-GB',1,51,3,'Multimedia','eng-GB'); @@ -236,7 +236,7 @@ VALUES (1,'standard','','Standard','ezcontentnavigationpart'), (3,'media','','Media','ezmedianavigationpart'); INSERT INTO "ezsite_data" ("name", "value") -VALUES ('ibexa-release','3.3'); +VALUES ('ibexa-release','4.6'); INSERT INTO "ezurlalias" ("destination_url", "forward_to_id", "id", "is_imported", "is_internal", "is_wildcard", "source_md5", "source_url") VALUES ('content/view/full/2',0,12,1,1,0,'d41d8cd98f00b204e9800998ecf8427e',''), @@ -300,6 +300,9 @@ INSERT INTO "ezuser_setting" ("is_enabled", "max_login", "user_id") VALUES (1, 1000, 10), (1, 10, 14); +INSERT INTO "ezpreferences" ("name", "user_id", "value") +SELECT 'focus_mode', u.contentobject_id, '0' FROM "ezuser" u WHERE u.login = 'admin'; + -- Set proper sequence values after inserting data SELECT SETVAL('ezcobj_state_group_id_seq', COALESCE(MAX(id), 1) ) FROM ezcobj_state_group; SELECT SETVAL('ezcobj_state_id_seq', COALESCE(MAX(id), 1) ) FROM ezcobj_state; diff --git a/doc/bc/1.0/dropped-container-parameters.md b/doc/bc/1.0/dropped-container-parameters.md deleted file mode 100644 index ae064b5811..0000000000 --- a/doc/bc/1.0/dropped-container-parameters.md +++ /dev/null @@ -1,479 +0,0 @@ -For the full list of changes see [changes-1.0](../changes-1.0.md). - -The following deprecated Symfony Dependency Injection Container parameters ending with `.class` have -been removed: - -* `ezpubish.image_alias.variation_path_generator.alias_directory.class` -* `ezpubish.image_alias.variation_path_generator.original_directory.class` -* `ezpublish.api.content.class` -* `ezpublish.api.location.class` -* `ezpublish.api.repository_configuration_provider.class` -* `ezpublish.api.repository.factory.class` -* `ezpublish.api.repository.factory.class` -* `ezpublish.api.role.limitation_type.blocking.class` -* `ezpublish.api.role.limitation_type.content_type.class` -* `ezpublish.api.role.limitation_type.language.class` -* `ezpublish.api.role.limitation_type.location.class` -* `ezpublish.api.role.limitation_type.new_section.class` -* `ezpublish.api.role.limitation_type.new_state.class` -* `ezpublish.api.role.limitation_type.owner.class` -* `ezpublish.api.role.limitation_type.parent_content_type.class` -* `ezpublish.api.role.limitation_type.parent_depth.class` -* `ezpublish.api.role.limitation_type.parent_group.class` -* `ezpublish.api.role.limitation_type.parent_owner.class` -* `ezpublish.api.role.limitation_type.section.class` -* `ezpublish.api.role.limitation_type.siteaccess.class` -* `ezpublish.api.role.limitation_type.state.class` -* `ezpublish.api.role.limitation_type.status.class` -* `ezpublish.api.role.limitation_type.subtree.class` -* `ezpublish.api.role.limitation_type.user_group.class` -* `ezpublish.api.search_engine.class` -* `ezpublish.api.search_engine.factory.class` -* `ezpublish.api.search_engine.indexer.factory.class` -* `ezpublish.api.service.bookmark.class` -* `ezpublish.api.service.content.class` -* `ezpublish.api.service.content_type.class` -* `ezpublish.api.service.field_type.class` -* `ezpublish.api.service.language.class` -* `ezpublish.api.service.location.class` -* `ezpublish.api.service.notification.class` -* `ezpublish.api.service.object_state.class` -* `ezpublish.api.service.role.class` -* `ezpublish.api.service.search.class` -* `ezpublish.api.service.section.class` -* `ezpublish.api.service.trash.class` -* `ezpublish.api.service.url_alias.class` -* `ezpublish.api.service.url.class` -* `ezpublish.api.service.url_wildcard.class` -* `ezpublish.api.service.user.class` -* `ezpublish.api.service.user_preference.class` -* `ezpublish.api.storage_engine.class` -* `ezpublish.api.storage_engine.factory.class` -* `ezpublish.api.storage_engine.legacy.dbhandler.class` -* `ezpublish.api.storage_engine.pcre_compiler.class` -* `ezpublish.api.storage_engine.transformation_converter.class` -* `ezpublish.api.storage_engine.transformation_parser.class` -* `ezpublish.api.storage_engine.transformation_processor.class` -* `ezpublish.api.version.class` -* `ezpublish.cache_pool.factory.class` -* `ezpublish.chain_router.class` -* `ezpublish.config.complex_setting_value.resolver.class` -* `ezpublish.config.dynamic_setting.parser.class` -* `ezpublish.config.resolver.chain.class` -* `ezpublish.config.resolver.dynamic.class` -* `ezpublish.config_scope_listener.class` -* `ezpublish.console_event_listener.class` -* `ezpublish.content_preview_helper.class` -* `ezpublish.content_preview.location_provider.class` -* `ezpublish.controller.base.class` -* `ezpublish.controller.content.download.class` -* `ezpublish.controller.content.download_redirection.class` -* `ezpublish.controller.content.preview.class` -* `ezpublish.controller.content.view.class` -* `ezpublish.core.io.binarydata_handler.flysystem.class` -* `ezpublish.core.io.command.migrate_files.class` -* `ezpublish.core.io.flysystem.default_adapter.class` -* `ezpublish.core.io.image_fieldtype.legacy_url_redecorator.class` -* `ezpublish.core.io.metadata_handler.flysystem.class` -* `ezpublish.core.io.metadataHandler.imageSize.class` -* `ezpublish.core.io.migration.file_lister.binary_file_lister.class` -* `ezpublish.core.io.migration.file_lister.file_iterator.binary_file_iterator.class` -* `ezpublish.core.io.migration.file_lister.file_iterator.media_file_iterator.class` -* `ezpublish.core.io.migration.file_lister.file_row_reader.binary_file_row_reader.class` -* `ezpublish.core.io.migration.file_lister.file_row_reader.media_file_row_reader.class` -* `ezpublish.core.io.migration.file_lister.image_file_lister.class` -* `ezpublish.core.io.migration.file_lister.media_file_lister.class` -* `ezpublish.core.io.migration.file_lister_registry.class` -* `ezpublish.core.io.migration.file_migrator.class` -* `ezpublish.core.io.migration.migration_handler.class` -* `ezpublish.core.io.mimeTypeDetector.fileinfo.class` -* `ezpublish.core.io.service.class` -* `ezpublish.core.io.stream_file_listener.class` -* `ezpublish.core.io.tolerant_service.class` -* `ezpublish.core.io.url_decorator.absolute_prefix.class` -* `ezpublish.core.io.url_decorator.prefix.class` -* `ezpublish.debug.data_collector.class` -* `ezpublish_debug.persistence_collector.class` -* `ezpublish_debug.siteaccess_collector.class` -* `ezpublish.decorated_fragment_renderer.class` -* `ezpublish.decorated_fragment_renderer.inline.class` -* `ezpublish.dynamic_settings_listener.class` -* `ezpublish.exception_listener.class` -* `ezpublish.field_helper.class` -* `ezpublish.fields_groups.list.class` -* `ezpublish.fieldType.class` -* `ezpublish.fieldType.ezauthor.class` -* `ezpublish.fieldType.ezauthor.converter.class` -* `ezpublish.fieldType.ezbinarybase.downloadUrlGenerator.class` -* `ezpublish.fieldType.ezbinaryfile.class` -* `ezpublish.fieldType.ezbinaryfile.converter.class` -* `ezpublish.fieldType.ezbinaryfile.externalStorage.class` -* `ezpublish.fieldType.ezbinaryfile.pathGenerator.class` -* `ezpublish.fieldType.ezbinaryfile.pathGenerator.class` -* `ezpublish.fieldType.ezbinaryfile.storage_gateway.class` -* `ezpublish.fieldType.ezboolean.class` -* `ezpublish.fieldType.ezboolean.converter.class` -* `ezpublish.fieldType.ezcountry.class` -* `ezpublish.fieldType.ezcountry.converter.class` -* `ezpublish.fieldType.ezdate.class` -* `ezpublish.fieldType.ezdate.converter.class` -* `ezpublish.fieldType.ezdatetime.class` -* `ezpublish.fieldType.ezdatetime.converter.class` -* `ezpublish.fieldType.ezemail.class` -* `ezpublish.fieldType.ezemail.converter.class` -* `ezpublish.fieldType.ezfloat.class` -* `ezpublish.fieldType.ezfloat.converter.class` -* `ezpublish.fieldType.ezgmaplocation.class` -* `ezpublish.fieldType.ezgmaplocation.converter.class` -* `ezpublish.fieldType.ezgmaplocation.externalStorage.class` -* `ezpublish.fieldType.ezgmaplocation.storage_gateway.class` -* `ezpublish.fieldType.ezimage.class` -* `ezpublish.fieldType.ezimage.converter.class` -* `ezpublish.fieldType.ezimage.externalStorage.class` -* `ezpublish.fieldType.ezimage.io_legacy.class` -* `ezpublish.fieldType.ezimage.io_legacy.class` -* `ezpublish.fieldType.ezimage.io_service.options_provider.class` -* `ezpublish.fieldType.ezimage.io_service.options_provider.class` -* `ezpublish.fieldType.ezimage.pathGenerator.class` -* `ezpublish.fieldType.ezimage.pathGenerator.class` -* `ezpublish.fieldType.ezimage.storage_gateway.class` -* `ezpublish.fieldType.ezinteger.class` -* `ezpublish.fieldType.ezinteger.converter.class` -* `ezpublish.fieldType.ezisbn.class` -* `ezpublish.fieldType.ezisbn.converter.class` -* `ezpublish.fieldType.ezkeyword.class` -* `ezpublish.fieldType.ezkeyword.converter.class` -* `ezpublish.fieldType.ezkeyword.externalStorage.class` -* `ezpublish.fieldType.ezkeyword.storage_gateway.class` -* `ezpublish.fieldType.ezmedia.class` -* `ezpublish.fieldType.ezmedia.converter.class` -* `ezpublish.fieldType.ezmedia.externalStorage.class` -* `ezpublish.fieldType.ezmedia.storage_gateway.class` -* `ezpublish.fieldType.eznull.class` -* `ezpublish.fieldType.eznull.converter.class` -* `ezpublish.fieldType.ezobjectrelation.class` -* `ezpublish.fieldType.ezobjectrelation.converter.class` -* `ezpublish.fieldType.ezobjectrelationlist.class` -* `ezpublish.fieldType.ezobjectrelationlist.converter.class` -* `ezpublish.fieldType.ezselection.class` -* `ezpublish.fieldType.ezselection.converter.class` -* `ezpublish.fieldType.ezstring.class` -* `ezpublish.fieldType.ezstring.converter.class` -* `ezpublish.fieldType.eztext.class` -* `ezpublish.fieldType.eztext.converter.class` -* `ezpublish.fieldType.eztime.class` -* `ezpublish.fieldType.eztime.converter.class` -* `ezpublish.fieldType.ezurl.class` -* `ezpublish.fieldType.ezurl.converter.class` -* `ezpublish.fieldType.ezurl.externalStorage.class` -* `ezpublish.fieldType.ezurl.storage_gateway.class` -* `ezpublish.fieldType.ezuser.class` -* `ezpublish.fieldType.ezuser.converter.class` -* `ezpublish.fieldType.ezuser.externalStorage.class` -* `ezpublish.fieldType.ezuser.storage_gateway.class` -* `ezpublish.fieldType.indexable.ezauthor.class` -* `ezpublish.fieldType.indexable.ezbinaryfile.class` -* `ezpublish.fieldType.indexable.ezboolean.class` -* `ezpublish.fieldType.indexable.ezcountry.class` -* `ezpublish.fieldType.indexable.ezdate.class` -* `ezpublish.fieldType.indexable.ezdatetime.class` -* `ezpublish.fieldType.indexable.ezemail.class` -* `ezpublish.fieldType.indexable.ezfloat.class` -* `ezpublish.fieldType.indexable.ezgmaplocation.class` -* `ezpublish.fieldType.indexable.ezimage.class` -* `ezpublish.fieldType.indexable.ezinteger.class` -* `ezpublish.fieldType.indexable.ezisbn.class` -* `ezpublish.fieldType.indexable.ezkeyword.class` -* `ezpublish.fieldType.indexable.ezmedia.class` -* `ezpublish.fieldType.indexable.ezobjectrelation.class` -* `ezpublish.fieldType.indexable.ezobjectrelationlist.class` -* `ezpublish.fieldType.indexable.ezselection.class` -* `ezpublish.fieldType.indexable.ezstring.class` -* `ezpublish.fieldType.indexable.eztext.class` -* `ezpublish.fieldType.indexable.eztime.class` -* `ezpublish.fieldType.indexable.ezurl.class` -* `ezpublish.fieldType.locale.parameterProvider.class` -* `ezpublish.fieldType.parameterProviderRegistry.class` -* `ezpublish.fragment_listener.factory.class` -* `ezpublish.image_alias.imagine.alias_cleaner.class` -* `ezpublish.image_alias.imagine.alias_generator.class` -* `ezpublish.image_alias.imagine.binary_loader.class` -* `ezpublish.image_alias.imagine.cache.alias_generator_decorator.class` -* `ezpublish.image_alias.imagine.cache_resolver.class` -* `ezpublish.image_alias.imagine.cache_resolver_decorator.class` -* `ezpublish.image_alias.imagine.cache_resolver_decorator_factory.class` -* `ezpublish.image_alias.imagine.cache_resolver_decorator_proxy.class` -* `ezpublish.image_alias.imagine.cache_resolver_decorator_relative.class` -* `ezpublish.image_alias.imagine.filter.loader.border.class` -* `ezpublish.image_alias.imagine.filter.loader.crop.class` -* `ezpublish.image_alias.imagine.filter.loader.grayscale.class` -* `ezpublish.image_alias.imagine.filter.loader.reduce_noise.class` -* `ezpublish.image_alias.imagine.filter.loader.scale.class` -* `ezpublish.image_alias.imagine.filter.loader.scaledownonly.height.class` -* `ezpublish.image_alias.imagine.filter.loader.scaledownonly.width.class` -* `ezpublish.image_alias.imagine.filter.loader.scaledownonly.width_height.class` -* `ezpublish.image_alias.imagine.filter.loader.scale_exact.class` -* `ezpublish.image_alias.imagine.filter.loader.scaleheight.class` -* `ezpublish.image_alias.imagine.filter.loader.scale_percent.class` -* `ezpublish.image_alias.imagine.filter.loader.scalewidth.class` -* `ezpublish.image_alias.imagine.filter.loader.swirl.class` -* `ezpublish.image_alias.imagine.filter.reduce_noise.gmagick.class` -* `ezpublish.image_alias.imagine.filter.reduce_noise.imagick.class` -* `ezpublish.image_alias.imagine.filter.swirl.gmagick.class` -* `ezpublish.image_alias.imagine.filter.swirl.imagick.class` -* `ezpublish.image_alias.imagine.filter.unsupported.class` -* `ezpublish.image_alias.imagine.variation.imagine_alias_generator.class` -* `ezpublish.image_alias.variation_purger.io.class` -* `ezpublish.image_alias.variation_purger.legacy_storage_image_file.class` -* `ezpublish.image_alias.variation_purger.legacy_storage_image_file.image_file_list.class` -* `ezpublish.image_alias.variation_purger.legacy_storage_image_file.image_file_row_reader.class` -* `ezpublish.locale.converter.class` -* `ezpublish.original_request_listener.class` -* `ezpublish.param_converter.content.class` -* `ezpublish.param_converter.location.class` -* `ezpublish.persistence.connection.class` -* `ezpublish.persistence.connection.class` -* `ezpublish.persistence.connection.factory.class` -* `ezpublish.persistence.external_storage_registry.class` -* `ezpublish.persistence.field_type_registry.class` -* `ezpublish.persistence.legacy.content.gateway.class` -* `ezpublish.persistence.legacy.content.gateway.exception_conversion.class` -* `ezpublish.persistence.legacy.content.mapper.class` -* `ezpublish.persistence.legacy.content.query_builder.class` -* `ezpublish.persistence.legacy.content_type.content_updater.class` -* `ezpublish.persistence.legacy.content_type.gateway.class` -* `ezpublish.persistence.legacy.content_type.gateway.exception_conversion.class` -* `ezpublish.persistence.legacy.content_type.mapper.class` -* `ezpublish.persistence.legacy.content_type.update_handler.base.class` -* `ezpublish.persistence.legacy.content_type.update_handler.class` -* `ezpublish.persistence.legacy.content_type.update_handler.deferred.class` -* `ezpublish.persistence.legacy.external_storage_handler.class` -* `ezpublish.persistence.legacy.field_handler.class` -* `ezpublish.persistence.legacy.field_value_converter.registry.class` -* `ezpublish.persistence.legacy.language.gateway.class` -* `ezpublish.persistence.legacy.language.gateway.exception_conversion.class` -* `ezpublish.persistence.legacy.language.mapper.class` -* `ezpublish.persistence.legacy.language.mask_generator.class` -* `ezpublish.persistence.legacy.location.gateway.class` -* `ezpublish.persistence.legacy.location.gateway.exception_conversion.class` -* `ezpublish.persistence.legacy.location.mapper.class` -* `ezpublish.persistence.legacy.object_state.gateway.class` -* `ezpublish.persistence.legacy.object_state.gateway.exception_conversion.class` -* `ezpublish.persistence.legacy.object_state.mapper.class` -* `ezpublish.persistence.legacy.role.gateway.class` -* `ezpublish.persistence.legacy.role.gateway.exception_conversion.class` -* `ezpublish.persistence.legacy.role.limitation.converter.class` -* `ezpublish.persistence.legacy.role.limitation.handler.class` -* `ezpublish.persistence.legacy.role.limitation.handler.object_state.class` -* `ezpublish.persistence.legacy.section.gateway.class` -* `ezpublish.persistence.legacy.section.gateway.exception_conversion.class` -* `ezpublish.persistence.legacy.tree_handler.class` -* `ezpublish.persistence.legacy.url_alias.gateway.class` -* `ezpublish.persistence.legacy.url_alias.gateway.exception_conversion.class` -* `ezpublish.persistence.legacy.url_alias.mapper.class` -* `ezpublish.persistence.legacy.url.criterion_handler.logical_and.class` -* `ezpublish.persistence.legacy.url.criterion_handler.logical_not.class` -* `ezpublish.persistence.legacy.url.criterion_handler.logical_or.class` -* `ezpublish.persistence.legacy.url.criterion_handler.match_all.class` -* `ezpublish.persistence.legacy.url.criterion_handler.match_none.class` -* `ezpublish.persistence.legacy.url.criterion_handler.pattern.class` -* `ezpublish.persistence.legacy.url.criterion_handler.section_id.class` -* `ezpublish.persistence.legacy.url.criterion_handler.section_identifier.class` -* `ezpublish.persistence.legacy.url.criterion_handler.validity.class` -* `ezpublish.persistence.legacy.url.criterion_handler.visible_only.class` -* `ezpublish.persistence.legacy.url.gateway.exception_conversion.class` -* `ezpublish.persistence.legacy.url.gateway.inner.class` -* `ezpublish.persistence.legacy.url.mapper.class` -* `ezpublish.persistence.legacy.url_wildcard.gateway.class` -* `ezpublish.persistence.legacy.url_wildcard.gateway.exception_conversion.class` -* `ezpublish.persistence.legacy.url_wildcard.mapper.class` -* `ezpublish.persistence.legacy.user.gateway.class` -* `ezpublish.persistence.legacy.user.gateway.exception_conversion.class` -* `ezpublish.persistence.legacy.user.mapper.class` -* `ezpublish.persistence.slug_converter.class` -* `ezpublish.preview_request_listener.class` -* `ezpublish.request_index_listener.class` -* `ezpublish.request_redirect_listener.class` -* `ezpublish.route_reference.generator.class` -* `ezpublish.route_reference.listener.content_download.class` -* `ezpublish.route_reference.listener.language_switch.class` -* `ezpublish.search.common.field_name_generator.class` -* `ezpublish.search.common.field_name_resolver.class` -* `ezpublish.search.common.field_registry.class` -* `ezpublish.search.common.field_value_mapper.aggregate.class` -* `ezpublish.search.common.field_value_mapper.boolean.class` -* `ezpublish.search.common.field_value_mapper.date.class` -* `ezpublish.search.common.field_value_mapper.document.class` -* `ezpublish.search.common.field_value_mapper.float.class` -* `ezpublish.search.common.field_value_mapper.geo_location.class` -* `ezpublish.search.common.field_value_mapper.identifier.class` -* `ezpublish.search.common.field_value_mapper.integer.class` -* `ezpublish.search.common.field_value_mapper.multiple_boolean.class` -* `ezpublish.search.common.field_value_mapper.multiple_identifier.class` -* `ezpublish.search.common.field_value_mapper.multiple_integer.class` -* `ezpublish.search.common.field_value_mapper.multiple_string.class` -* `ezpublish.search.common.field_value_mapper.price.class` -* `ezpublish.search.common.field_value_mapper.string.class` -* `ezpublish.search.common.indexer.class` -* `ezpublish.search.connection.class` -* `ezpublish.search.legacy.connection.class` -* `ezpublish.search.legacy.connection.factory.class` -* `ezpublish.search.legacy.dbhandler.class` -* `ezpublish.search.legacy.gateway.content.class` -* `ezpublish.search.legacy.gateway.content.exception_conversion.class` -* `ezpublish.search.legacy.gateway.criteria_converter.class` -* `ezpublish.search.legacy.gateway.criterion_field_value_converter.class` -* `ezpublish.search.legacy.gateway.criterion_field_value_handler.collection.class` -* `ezpublish.search.legacy.gateway.criterion_field_value_handler.composite.class` -* `ezpublish.search.legacy.gateway.criterion_field_value_handler.keyword.class` -* `ezpublish.search.legacy.gateway.criterion_field_value_handler.registry.class` -* `ezpublish.search.legacy.gateway.criterion_field_value_handler.simple.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.content_id.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.content_type_group_id.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.content_type_id.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.content_type_identifier.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.date_metadata.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.field.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.field_empty.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.field_relation.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.full_text.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.language_code.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.logical_and.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.logical_not.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.logical_or.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.map_location_distance.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.match_all.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.match_none.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.object_state_id.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.remote_id.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.section_id.class` -* `ezpublish.search.legacy.gateway.criterion_handler.common.user_metadata.class` -* `ezpublish.search.legacy.gateway.criterion_handler.content.ancestor.class` -* `ezpublish.search.legacy.gateway.criterion_handler.content.location_id.class` -* `ezpublish.search.legacy.gateway.criterion_handler.content.location_remote_id.class` -* `ezpublish.search.legacy.gateway.criterion_handler.content.parent_location_id.class` -* `ezpublish.search.legacy.gateway.criterion_handler.content.permission_subtree.class` -* `ezpublish.search.legacy.gateway.criterion_handler.content.subtree.class` -* `ezpublish.search.legacy.gateway.criterion_handler.content.visibility.class` -* `ezpublish.search.legacy.gateway.criterion_handler.location.ancestor.class` -* `ezpublish.search.legacy.gateway.criterion_handler.location.depth.class` -* `ezpublish.search.legacy.gateway.criterion_handler.location.is_main_location.class` -* `ezpublish.search.legacy.gateway.criterion_handler.location.location_id.class` -* `ezpublish.search.legacy.gateway.criterion_handler.location.location_remote_id.class` -* `ezpublish.search.legacy.gateway.criterion_handler.location.parent_location_id.class` -* `ezpublish.search.legacy.gateway.criterion_handler.location.priority.class` -* `ezpublish.search.legacy.gateway.criterion_handler.location.subtree.class` -* `ezpublish.search.legacy.gateway.criterion_handler.location.visibility.class` -* `ezpublish.search.legacy.gateway.location.class` -* `ezpublish.search.legacy.gateway.location.exception_conversion.class` -* `ezpublish.search.legacy.gateway.sort_clause_converter.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.common.content_id.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.common.content_name.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.common.date_modified.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.common.date_published.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.common.field.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.common.map_location_distance.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.common.random.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.common.section_identifier.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.common.section_name.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.location.depth.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.location.id.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.location.is_main_location.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.location.path.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.location.priority.class` -* `ezpublish.search.legacy.gateway.sort_clause_handler.location.visibility.class` -* `ezpublish.search.legacy.gateway.wordIndexer.class` -* `ezpublish.search.legacy.mapper.fulltext.class` -* `ezpublish.search.legacy.repository.searchIndex.class` -* `ezpublish.security.controller.class` -* `ezpublish.security.login_listener.class` -* `ezpublish.security.user_provider.class` -* `ezpublish.security.voter.core.class` -* `ezpublish.security.voter.value_object.class` -* `ezpublish.session_init_by_post_listener.class` -* `ezpublish.session_set_dynamic_name_listener.class` -* `ezpublish.siteaccess.class` -* `ezpublish.siteaccess_listener.class` -* `ezpublish.siteaccess_listener.routing.class` -* `ezpublish.siteaccess.matcher_builder.class` -* `ezpublish.siteaccess_match_listener.class` -* `ezpublish.siteaccess_router.class` -* `ezpublish.spi.persistence.bookmark_handler.class` -* `ezpublish.spi.persistence.cache.abstractHandler.class` -* `ezpublish.spi.persistence.cache.bookmarkHandler.class` -* `ezpublish.spi.persistence.cache.class` -* `ezpublish.spi.persistence.cache.contentHandler.class` -* `ezpublish.spi.persistence.cache.contentLanguageHandler.class` -* `ezpublish.spi.persistence.cache.contentTypeHandler.class` -* `ezpublish.spi.persistence.cache.locationHandler.class` -* `ezpublish.spi.persistence.cache.notificationHandler.class` -* `ezpublish.spi.persistence.cache.objectStateHandler.class` -* `ezpublish.spi.persistence.cache.persistenceLogger.class` -* `ezpublish.spi.persistence.cache.sectionHandler.class` -* `ezpublish.spi.persistence.cache.transactionhandler.class` -* `ezpublish.spi.persistence.cache.trashHandler.class` -* `ezpublish.spi.persistence.cache.urlAliasHandler.class` -* `ezpublish.spi.persistence.cache.urlHandler.class` -* `ezpublish.spi.persistence.cache.urlWildcardHandler.class` -* `ezpublish.spi.persistence.cache.userHandler.class` -* `ezpublish.spi.persistence.cache.userPreferenceHandler.class` -* `ezpublish.spi.persistence.content_handler.class` -* `ezpublish.spi.persistence.content_type_handler.class` -* `ezpublish.spi.persistence.language_handler.class` -* `ezpublish.spi.persistence.legacy.class` -* `ezpublish.spi.persistence.legacy.content.handler.class` -* `ezpublish.spi.persistence.legacy.content_type.handler.caching.class` -* `ezpublish.spi.persistence.legacy.content_type.handler.class` -* `ezpublish.spi.persistence.legacy.language.handler.caching.class` -* `ezpublish.spi.persistence.legacy.language.handler.class` -* `ezpublish.spi.persistence.legacy.location.handler.class` -* `ezpublish.spi.persistence.legacy.object_state.handler.class` -* `ezpublish.spi.persistence.legacy.section.handler.class` -* `ezpublish.spi.persistence.legacy.transactionhandler.class` -* `ezpublish.spi.persistence.legacy.trash.handler.class` -* `ezpublish.spi.persistence.legacy.url_alias.handler.class` -* `ezpublish.spi.persistence.legacy.url.criterion_converter.class` -* `ezpublish.spi.persistence.legacy.url.handler.class` -* `ezpublish.spi.persistence.legacy.url_wildcard.handler.class` -* `ezpublish.spi.persistence.legacy.user.handler.class` -* `ezpublish.spi.persistence.location_handler.class` -* `ezpublish.spi.persistence.object_state_handler.class` -* `ezpublish.spi.persistence.section_handler.class` -* `ezpublish.spi.persistence.trash_handler.class` -* `ezpublish.spi.persistence.url_alias_handler.class` -* `ezpublish.spi.persistence.url_wildcard_handler.class` -* `ezpublish.spi.persistence.user_handler.class` -* `ezpublish.spi.persistence.user_preference_handler.class` -* `ezpublish.spi.search.legacy.class` -* `ezpublish.spi.search.legacy.class` -* `ezpublish.spi.search.legacy.indexer.class` -* `ezpublish.templating.extension.routing.class` -* `ezpublish.templating.field_block_renderer.twig.class` -* `ezpublish.templating.global_helper.core.class` -* `ezpublish.translation_helper.class` -* `ezpublish.twig.extension.content.class` -* `ezpublish.twig.extension.core.class` -* `ezpublish.twig.extension.field_rendering.class` -* `ezpublish.twig.extension.filesize.class` -* `ezpublish.twig.extension.image.class` -* `ezpublish.urlalias_generator.class` -* `ezpublish.urlalias_router.class` -* `ezpublish.url_generator.base.class` -* `ezpublish.utils.deprecation_warner.class` -* `ezpublish.view_builder.content.class` -* `ezpublish.view.builder_parameter_collector.request_attributes.class` -* `ezpublish.view_builder.registry.class` -* `ezpublish.view.cache_response_listener.class` -* `ezpublish.view.configurator.class` -* `ezpublish.view_controller_listener.class` -* `ezpublish.view.custom_location_controller_checker.class` -* `ezpublish.view_manager.class` -* `ezpublish.view_provider.registry.class` -* `ezpublish.view.renderer_listener.class` -* `ezpublish.view.template_renderer.class` -* `ezpublish.view.view_parameters.injector.custom_parameters.class` -* `ezpublish.view.view_parameters.injector.dispatcher.class` -* `ezpublish.view.view_parameters.injector.embed_object_parameters.class` -* `ezpublish.view.view_parameters.injector.no_layout.class` -* `ezpublish.view.view_parameters.injector.value_objects_ids.class` -* `ezpublish.view.view_parameters.injector.viewbase_layout.class` diff --git a/doc/bc/changes-1.0.md b/doc/bc/changes-1.0.md deleted file mode 100644 index 3cfd8ed113..0000000000 --- a/doc/bc/changes-1.0.md +++ /dev/null @@ -1,424 +0,0 @@ -# Backwards compatibility changes - -Changes affecting version compatibility with deprecated ezpublish-kernel version 7.5 -(used by eZ Platform 2.5 LTS). - -## Removed features - -* Elasticsearch support has been dropped. It supported Elasticsearch 1.x, - while the latest Elasticsearch release is 7.0. - - The support for this search engine will be provided once again as a separate bundle. - -* The following Field Types are not supported any more and have been removed: - * `ezprice`, - * `ezpage` together with block rendering subsystem, - * `ezsrrating`. - -* The following configuration nodes are not available anymore: - * `ezpublish..ezpage.*` - * `ezpublish..block_view.*` - * `ezpublish.siteaccess.relation_map` has been replaced by `getSiteAccessesRelation` method from `eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessService` - -* REST Client has been dropped. - -* REST Server implementation and Common namespace have been removed in favor of - eZ Platform REST Bundle available via - [ezsystems/ezplatform-rest](https://github.com/ezsystems/ezplatform-rest) package. - -* Assetic support has been dropped. - -* Minimal PHP version has been raised to 7.3. - -* Deprecated method `getName` from the interface `eZ\Publish\SPI\FieldType\FieldType` has been changed. - Now it accepts two additional parameters: `FieldDefinition $fieldDefinition` and `string $languageCode` - -* Interface `eZ\Publish\SPI\FieldType\FieldType` has been transformed to abstract class. - -* Interface `eZ\Publish\SPI\FieldType\Nameable` has been removed. - -* `ez_trans_prop` twig function was removed - -* `ezrichtext` Field Type has been completely removed from this package. - Use [eZ Platform RichText Bundle](https://github.com/ezsystems/ezplatform-richtext) instead. - - It also implies that: - * the semantic configuration available as: - ```yaml - ezpublish: - ezrichtext: - ``` - is no longer supported. To upgrade please make `ezrichtext` the top node: - ```yaml - ezrichtext: - ``` - * the namespace `eZ\Publish\Core\FieldType\RichText` has been dropped (all classes are available - in the mentioned package). - -* Deprecated hash types constants have been dropped from `\eZ\Publish\API\Repository\Values\User\User`. - -* Deprecated `eZ\Publish\SPI\FieldType\EventListener` interface, `eZ\Publish\SPI\FieldType\Event` class and - `eZ\Publish\SPI\FieldType\Events` namespace have been dropped. - -* Deprecated `eZ\Publish\Core\MVC\Symfony\Matcher\MatcherInterface` interface has been dropped. Deprecated classes relying on that interface have been removed as well: - * `eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory` - * `eZ\Publish\Core\MVC\Symfony\Matcher\ContentBasedMatcherFactory` - * `eZ\Publish\Core\MVC\Symfony\Matcher\ContentMatcherFactory` - * `eZ\Publish\Core\MVC\Symfony\Matcher\LocationMatcherFactory` - -* Deprecated Symfony framework templating component integration has been dropped. - -* Following API methods have been removed: - - * `\eZ\Publish\API\Repository\ContentService::removeTranslation` - * `\eZ\Publish\API\Repository\UserService::loadAnonymousUser` - * `\eZ\Publish\API\Repository\Repository::getCurrentUser` - * `\eZ\Publish\API\Repository\Repository::getCurrentUserReference` - * `\eZ\Publish\API\Repository\Repository::setCurrentUser` - * `\eZ\Publish\API\Repository\Repository::hasAccess` - * `\eZ\Publish\API\Repository\Repository::canUser` - * `\eZ\Publish\API\Repository\RoleService::updateRole` - * `\eZ\Publish\API\Repository\RoleService::addPolicy` - * `\eZ\Publish\API\Repository\RoleService::deletePolicy` - * `\eZ\Publish\API\Repository\RoleService::updatePolicy` - * `\eZ\Publish\API\Repository\RoleService::loadPoliciesByUserId` - * `\eZ\Publish\API\Repository\RoleService::unassignRoleFromUser` - * `\eZ\Publish\API\Repository\RoleService::unassignRoleFromUserGroup` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\Ancestor::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentId::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeGroupId::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeId::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\FieldRelation::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\FullText::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\LanguageCode::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\LocationId::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\LocationRemoteId::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchAll::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchNone::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\MoreLikeThis::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\ObjectStateId::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\RemoteId::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\SectionId::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree::createFromQueryBuilder` - * `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\Visibility::createFromQueryBuilder` - -* Following SPI methods have been removed: - - * `\eZ\Publish\SPI\Persistence\Content\Handler::removeTranslationFromContent` - -* The "Setup" folder and Section have been removed from the initial (clean installation) data. - -* The "Design" Section has been removed from the initial (clean installation) data. - -* The following obsolete tables have been removed from the database schema: - - ezapprove_items, - - ezbasket, - - ezcollab_group, - - ezcollab_item, - - ezcollab_item_group_link, - - ezcollab_item_message_link, - - ezcollab_item_participant_link, - - ezcollab_item_status, - - ezcollab_notification_rule, - - ezcollab_profile, - - ezcollab_simple_message, - - ezcomment, - - ezcomment_notification, - - ezcomment_subscriber, - - ezcomment_subscription, - - ezcontentbrowserecent, - - ezcurrencydata, - - ezdiscountrule, - - ezdiscountsubrule, - - ezdiscountsubrule_value, - - ezenumobjectvalue, - - ezenumvalue, - - ezforgot_password, - - ezgeneral_digest_user_settings, - - ezinfocollection, - - ezinfocollection_attribute, - - ezisbn_group, - - ezisbn_group_range, - - ezisbn_registrant_range, - - ezm_block, - - ezm_pool, - - ezmessage, - - ezmodule_run, - - ezmultipricedata, - - eznotificationcollection, - - eznotificationcollection_item, - - eznotificationevent, - - ezoperation_memento, - - ezorder, - - ezorder_item, - - ezorder_nr_incr, - - ezorder_status, - - ezorder_status_history, - - ezpaymentobject, - - ezpdf_export, - - ezpending_actions, - - ezprest_authcode, - - ezprest_authorized_clients, - - ezprest_clients, - - ezprest_token, - - ezproductcategory, - - ezproductcollection, - - ezproductcollection_item, - - ezproductcollection_item_opt, - - ezpublishingqueueprocesses, - - ezrss_export, - - ezrss_export_item, - - ezrss_import, - - ezscheduled_script, - - ezsearch_search_phrase, - - ezsession, - - ezstarrating, - - ezstarrating_data, - - ezsubtree_notification_rule, - - eztipafriend_counter, - - eztipafriend_request, - - eztrigger, - - ezuservisit, - - ezuser_discountrule, - - ezvatrule, - - ezvatrule_product_category, - - ezvattype, - - ezview_counter, - - ezwaituntildatevalue, - - ezwishlist, - - ezworkflow, - - ezworkflow_assign, - - ezworkflow_event, - - ezworkflow_group, - - ezworkflow_group_link, - - ezworkflow_process. - - If your project doesn't use them, you can drop them from your database by executing the SQL: - ```sql - DROP TABLE ; - ``` - -* Clean Installer (`EzSystems\PlatformInstallerBundle\Installer\CleanInstaller`), its service - definition (`ezplatform.installer.clean_installer`), and the - `ezplatform.installer.clean_installer.class` parameter have been dropped. Instead use - the `EzSystems\DoctrineSchema\API\Event\SchemaBuilderEvents::BUILD_SCHEMA` event - with an event subscriber, and if needed, additionally with the Core Installer - (`EzSystems\PlatformInstallerBundle\Installer\CoreInstaller`). - See eZ Platform Documentation for more details. - -* The `ezplatform.installer.db_based_installer.class` and `ezplatform.installer.install_command.class` - parameters have been dropped. The `ezplatform.installer.db_based_installer` service definition has - been dropped as well. Instead, the FQCN-named service - `EzSystems\PlatformInstallerBundle\Installer\DbBasedInstaller` is available. - -* The `EzSystems\PlatformInstallerBundle\Command\InstallPlatformCommand` has been marked as final. - Overriding it was never supported. Use the SchemaBuilder event-oriented extension point to inject - custom behavior into the installation process (see eZ Platform Documentation for more details). - -* The deprecated Legacy SQL schema files (`data/mysql/schema.sql`, `data/mysql/dfs_schema.sql`) - have been removed. Use Schema Builder instead. - -* The obsolete `data/demo_data.php` file has been removed. - -* The following deprecated (since v6.11) LegacyStorage Gateways were removed: - - `eZ\Publish\Core\FieldType\BinaryFile\BinaryBaseStorage\Gateway\LegacyStorage`, - - `eZ\Publish\Core\FieldType\BinaryFile\BinaryFileStorage\Gateway\LegacyStorage`, - - `eZ\Publish\Core\FieldType\MapLocation\MapLocationStorage\Gateway\LegacyStorage`, - - `eZ\Publish\Core\FieldType\Image\ImageStorage\Gateway\LegacyStorage`, - - `eZ\Publish\Core\FieldType\Keyword\KeywordStorage\Gateway\LegacyStorage`, - - `eZ\Publish\Core\FieldType\Media\MediaStorage\Gateway\LegacyStorage`, - - `eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway\LegacyStorage`, - - `eZ\Publish\Core\FieldType\User\UserStorage\Gateway\LegacyStorage`. - - Use `DoctrineStorage` Gateways from the same namespace instead. - -* Query Types: Traversing bundles to automatically register a Query Type by the naming - convention `\QueryType\*QueryType` has been dropped. - Register your Query Type as a service and explicitly tag that service with `ezpublish.query` - or enable its automatic configuration (`autoconfigure: true`). - -* The deprecated Symfony Dependency Injection Container parameters ending with `.class` have been - removed, services relying on them have now their classes defined explicitly. - To properly decorate a Symfony service, use the `decorates` attribute instead. - For the full list of the dropped parameters please see the - [1.0/dropped-container-parameters.md](1.0/dropped-container-parameters.md) document. - -* Deprecated `viewLocation` and `embedLocation` actions of `ViewController` have been dropped, along with - related route `_ezpublishLocation`. As stated in controller, use `viewAction` in place of `viewLocation` and - `embedAction` in place of `embedLocation`. - -* Obsolete DeferredLegacy Content Type Update handler - (`eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler\DeferredLegacy`) and its optional - Symfony Container Service (`ezpublish.persistence.legacy.content_type.update_handler.deferred`) - have been removed. Subscribe to eZ Platform Symfony Events to handle deferring of updating - of Content items after their Content Type update instead. - -* Deprecated `UserService::loadUserByCredentials` method has been dropped. From now on, we rely on - Security package of Symfony framework to provide authenticated user, as it may happen there are - different ways of providing users configured in the application (ref.: https://symfony.com/doc/current/security/user_provider.html). - -* Legacy (SQL) Search Engine will no longer treat deprecated `%` as a search wildcard. Use `*` instead. - -* Dynamic Settings feature has been dropped. It was conceptually not compatible with Symfony's Container. Consider injecting `\eZ\Publish\Core\MVC\ConfigResolverInterface` instead and using `getParameter` method to fetch SiteAccess dependent settings. - -* Field Type External Storage Handlers `$context` array no longer has the "connection" key. Rely on - injected Connection instead. - -* The deprecated Zeta Components (eZc) Database handler has been dropped. All classes and interfaces - from the namespaces `eZ\Publish\Core\Persistence\Database` and `eZ\Publish\Core\Persistence\Doctrine` - were removed. - -## Deprecated features - -* Using SiteAccess-aware `pagelayout` setting is derecated, use `page_layout` instead. -* View parameter `pagelayout` set by `pagelayout` setting is deprecated, use `page_layout` instead in your Twig templates. -* The `$context` array of `\eZ\Publish\SPI\FieldType\FieldStorage` methods (`storeFieldData`, - `getFieldData`, `deleteFieldData`, `getIndexData`) is deprecated and will be dropped in the next - major version. Rely on injected Connection instead. - -## Renamed features - -* `ezpublish` global twig variable was renamed to `ezplatform` -* `ez_is_field_empty` twig function was renamed to `ez_field_is_empty` -* `ez_first_filled_image_field_identifier` twig function was renamed to `ez_content_field_identifier_first_filled_image` -* `ez_render_fielddefinition_settings` twig function was renamed to `ez_render_field_definition_settings` -* `ez_image_asset_content_field_identifier` twig function was renamed to `ez_content_field_identifier_image_asset` -* `richtext_to_html5` twig filter was renamed to `ez_richtext_to_html5` -* `richtext_to_html5_edit` twig filter was renamed to `ez_richtext_to_html5_edit` -* `\eZ\Bundle\EzPublishIOBundle\ApiLoader\HandlerFactory` class was renamed to `HandlerRegistry` -* `ezpublish.core.io.metadata_handler.factory` service was renamed to `ezpublish.core.io.metadata_handler.registry` -* `ezpublish.core.io.binarydata_handler.factory` service was renamed to `ezpublish.core.io.binarydata_handler.registry` - -## Changed features - -* The signature of the `\eZ\Publish\API\Repository\SearchService::supports` method was changed to: - ```php - public function supports(int $capabilityFlag): bool; - ``` -* The signature of the `\eZ\Publish\SPI\Search\Capable::supports` method was changed to: - ```php - public function supports(int $capabilityFlag): bool; - ``` -* The signature of the `\eZ\Publish\API\Repository\Values\ValueObject\SiteAccess::__construct` method was changed to make `name` property required: - ```php - public function __construct( - string $name, - string $matchingType = self::DEFAULT_MATCHING_TYPE, - $matcher = null, - ?string $provider = null - ); - ``` - -* The signature of the `\eZ\Publish\API\Repository\Values\ContentType\ContentType::getFieldDefinitions` method was changed to: - ```php - abstract public function getFieldDefinitions(): FieldDefinitionCollection; - ``` - -* The signature of the `\eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler::handle` contract - accepts now `\Doctrine\DBAL\Query\QueryBuilder` instead of `\eZ\Publish\Core\Persistence\Database\SelectQuery` - and has the following form: - ```php - use \Doctrine\DBAL\Query\QueryBuilder; - use \eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; - use \eZ\Publish\API\Repository\Values\URL\Query\Criterion; - - public function handle(CriteriaConverter $converter, QueryBuilder $query, Criterion $criterion); - ``` - -* The signature of the `\eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler::handle` - contract accepts now `\Doctrine\DBAL\Query\QueryBuilder` instead of `\eZ\Publish\Core\Persistence\Database\SelectQuery` - and has the following form: - ```php - use \Doctrine\DBAL\Query\QueryBuilder; - use \eZ\Publish\API\Repository\Values\Content\Query\Criterion; - use \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; - - abstract public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ); - ``` -* The signature of the `\eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler::handle` - contract accepts now two new arguments of `\Doctrine\DBAL\Query\QueryBuilder` type instead of - `\eZ\Publish\Core\Persistence\Database\SelectQuery`. The first one is an outer query, to be used - only for parameter binding. The second one is a sub-query that can be modified according to a strategy - needed by a particular Field Type. This change is necessary due to the nature of Doctrine Query Builder. - While sub-queries are used for convenience, in the end, the only query having parameters bound - and being executed is the outer query. The sub-query is used just to generate nested SQL for the outer query. - The signature has the following form: - ```php - use \Doctrine\DBAL\Query\QueryBuilder; - use \eZ\Publish\API\Repository\Values\Content\Query\Criterion; - use \Doctrine\DBAL\Query\Expression\CompositeExpression; - - public function handle( - QueryBuilder $outerQuery, - QueryBuilder $subQuery, - Criterion $criterion, - string $column - ): CompositeExpression|string; - ``` - -* The signature of the `\eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler::applySelect` - contract accepts now `\Doctrine\DBAL\Query\QueryBuilder` instead of `\eZ\Publish\Core\Persistence\Database\SelectQuery` - and has the following form: - ```php - use \Doctrine\DBAL\Query\QueryBuilder; - use \eZ\Publish\API\Repository\Values\Content\Query\SortClause; - - abstract public function applySelect(QueryBuilder $query, SortClause $sortClause, int $number): array; - ``` - -* The signature of the `\eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler::applyJoin` - contract accepts now `\Doctrine\DBAL\Query\QueryBuilder` instead of `\eZ\Publish\Core\Persistence\Database\SelectQuery` - and has the following form: - ```php - use \Doctrine\DBAL\Query\QueryBuilder; - use \eZ\Publish\API\Repository\Values\Content\Query\SortClause; - - public function applyJoin( - QueryBuilder $query, - SortClause $sortClause, - int $number, - array $languageSettings - ): void; - ``` - -## Removed services - -* `ezpublish.field_type_collection.factory` has been removed in favor of `eZ\Publish\Core\FieldType\FieldTypeRegistry` - -* `ezpublish.persistence.external_storage_registry.factory` - -* `ezpublish.config.resolver.core` has been removed. `eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver` should be used instead - -* `ezpublish.api.search_engine.legacy.dbhandler` and `ezpublish.api.storage_engine.legacy.dbhandler` - have been removed. Inject `\Doctrine\DBAL\Connection` via `ezpublish.persistence.connection` instead. - -* `ezpublish.connection` has been removed. Use `ezpublish.persistence.connection` instead. - -## Changed behavior - -* Service based View Matchers now require to be tagged with `ezplatform.view.matcher`. Moreover now to use it you have to prefix service name with `@` sign: -```yaml -site: - content_view: - full: - home: - template: "content.html.twig" - match: - '@App\Matcher\MyMatcher': ~ -``` - -* Service based SiteAccess Matchers now require to be tagged with `ezplatform.siteaccess.matcher`. - -* `eZ\Bundle\EzPublishCoreBundle\Controller` extends `Symfony\Bundle\FrameworkBundle\Controller\AbstractController` instead of `Symfony\Bundle\FrameworkBundle\Controller\Controller` which has limited access to the dependency injection container. See https://symfony.com/doc/current/service_container/service_subscribers_locators.html - -* SiteAccessAware Repository layer is now used by default. If you need to load repository object in all translations, explicitly pass `\eZ\Publish\API\Repository\Values\Content\Language::ALL` as as prioritized languages list. - -* Changed `$valueFormat` type from `string` to `int` in `\eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications::__construct` diff --git a/doc/upgrade/1.0.md b/doc/upgrade/1.0.md deleted file mode 100644 index 8f525ce5be..0000000000 --- a/doc/upgrade/1.0.md +++ /dev/null @@ -1,3 +0,0 @@ -# Upgrade steps from ezpublish-kernel 7.5 to ezplatform-kernel 1.0 - -See [`doc/bc/changes-1.0.md`](../bc/changes-1.0.md) for requirements changes and deprecations. diff --git a/docblox.dist.xml b/docblox.dist.xml index 9423d40e30..344cddb335 100644 --- a/docblox.dist.xml +++ b/docblox.dist.xml @@ -9,8 +9,5 @@ . doc/* - ezp/*/Tests/* - eZ/Publish/SPI/Persistence/Storage/*/Tests/* - eZ/Publish/Core/*/Tests/* diff --git a/docker/solr/Dockerfile b/docker/solr/Dockerfile new file mode 100644 index 0000000000..7f00ba7387 --- /dev/null +++ b/docker/solr/Dockerfile @@ -0,0 +1,14 @@ +FROM alpine:3.18.5 as builder +RUN apk add --no-cache --upgrade bash git curl +RUN adduser --disabled-password user +USER user +WORKDIR /home/user +RUN git clone --depth=1 https://github.com/ibexa/solr.git solr +RUN ./solr/bin/generate-solr-config.sh --destination-dir=config --solr-version=8.6.3 + +FROM solr:8.6.3 +USER root +RUN rm -rf server/solr/configsets/_default/conf/* +USER solr +COPY --from=builder /home/user/config server/solr/configsets/_default/conf +CMD ["solr-precreate", "collection1"] diff --git a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/CacheFactory.php b/eZ/Bundle/EzPublishCoreBundle/ApiLoader/CacheFactory.php deleted file mode 100644 index 49eacd6380..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/CacheFactory.php +++ /dev/null @@ -1,43 +0,0 @@ -container->get($configResolver->getParameter('cache_service_name')); - - // If cache service is already implementing TagAwareAdapterInterface, return as-is - if ($cacheService instanceof TagAwareAdapterInterface) { - return $cacheService; - } - - return new TagAwareAdapter( - $cacheService - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/Exception/InvalidRepositoryException.php b/eZ/Bundle/EzPublishCoreBundle/ApiLoader/Exception/InvalidRepositoryException.php deleted file mode 100644 index 5488d5921c..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/Exception/InvalidRepositoryException.php +++ /dev/null @@ -1,13 +0,0 @@ -configResolver = $configResolver; - $this->repositoryClass = $repositoryClass; - $this->policyMap = $policyMap; - $this->languageResolver = $languageResolver; - $this->logger = null !== $logger ? $logger : new NullLogger(); - } - - /** - * Builds the main repository, heart of eZ Publish API. - * - * This always returns the true inner Repository, please depend on ezpublish.api.repository and not this method - * directly to make sure you get an instance wrapped inside Event / Cache / * functionality. - */ - public function buildRepository( - PersistenceHandler $persistenceHandler, - SearchHandler $searchHandler, - BackgroundIndexer $backgroundIndexer, - RelationProcessor $relationProcessor, - FieldTypeRegistry $fieldTypeRegistry, - PasswordHashService $passwordHashService, - ThumbnailStrategy $thumbnailStrategy, - ProxyDomainMapperFactoryInterface $proxyDomainMapperFactory, - Mapper\ContentDomainMapper $contentDomainMapper, - Mapper\ContentTypeDomainMapper $contentTypeDomainMapper, - Mapper\RoleDomainMapper $roleDomainMapper, - Mapper\ContentMapper $contentMapper, - ContentValidator $contentValidator, - LimitationService $limitationService, - PermissionService $permissionService, - ContentFilteringHandler $contentFilteringHandler, - LocationFilteringHandler $locationFilteringHandler, - PasswordValidatorInterface $passwordValidator - ): Repository { - $config = $this->container->get('ezpublish.api.repository_configuration_provider')->getRepositoryConfig(); - - return new $this->repositoryClass( - $persistenceHandler, - $searchHandler, - $backgroundIndexer, - $relationProcessor, - $fieldTypeRegistry, - $passwordHashService, - $thumbnailStrategy, - $proxyDomainMapperFactory, - $contentDomainMapper, - $contentTypeDomainMapper, - $roleDomainMapper, - $contentMapper, - $contentValidator, - $limitationService, - $this->languageResolver, - $permissionService, - $contentFilteringHandler, - $locationFilteringHandler, - $passwordValidator, - [ - 'role' => [ - 'policyMap' => $this->policyMap, - ], - 'languages' => $this->configResolver->getParameter('languages'), - 'content' => [ - 'default_version_archive_limit' => $config['options']['default_version_archive_limit'], - 'remove_archived_versions_on_publish' => $config['options']['remove_archived_versions_on_publish'], - ], - ], - $this->logger - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/SearchEngineFactory.php b/eZ/Bundle/EzPublishCoreBundle/ApiLoader/SearchEngineFactory.php deleted file mode 100644 index 33d67335a8..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/SearchEngineFactory.php +++ /dev/null @@ -1,83 +0,0 @@ -repositoryConfigurationProvider = $repositoryConfigurationProvider; - } - - /** - * Registers $searchHandler as a valid search engine with identifier $searchEngineIdentifier. - * - * Note It is strongly recommended to register a lazy persistent handler. - * - * @param \eZ\Publish\SPI\Search\Handler $searchHandler - * @param string $searchEngineIdentifier - */ - public function registerSearchEngine(SearchHandler $searchHandler, $searchEngineIdentifier) - { - $this->searchEngines[$searchEngineIdentifier] = $searchHandler; - } - - /** - * Returns registered search engines. - * - * @return \eZ\Publish\SPI\Search\Handler[] - */ - public function getSearchEngines() - { - return $this->searchEngines; - } - - /** - * Builds search engine identified by its identifier (the "alias" attribute in the service tag), - * resolved for current siteaccess. - * - * @throws \eZ\Bundle\EzPublishCoreBundle\ApiLoader\Exception\InvalidSearchEngine - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - public function buildSearchEngine() - { - $repositoryConfig = $this->repositoryConfigurationProvider->getRepositoryConfig(); - - if ( - !( - isset($repositoryConfig['search']['engine']) - && isset($this->searchEngines[$repositoryConfig['search']['engine']]) - ) - ) { - throw new InvalidSearchEngine( - "Invalid search engine '{$repositoryConfig['search']['engine']}'. " . - "Could not find any service tagged with 'ezplatform.search_engine' " . - "with alias '{$repositoryConfig['search']['engine']}'." - ); - } - - return $this->searchEngines[$repositoryConfig['search']['engine']]; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/SearchEngineIndexerFactory.php b/eZ/Bundle/EzPublishCoreBundle/ApiLoader/SearchEngineIndexerFactory.php deleted file mode 100644 index 01a980b470..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/SearchEngineIndexerFactory.php +++ /dev/null @@ -1,83 +0,0 @@ -repositoryConfigurationProvider = $repositoryConfigurationProvider; - } - - /** - * Registers $searchEngineIndexer as a valid search engine indexer with identifier $searchEngineIdentifier. - * - * note: It is strongly recommended to register indexer as a lazy service. - * - * @param \eZ\Publish\Core\Search\Common\Indexer $searchEngineIndexer - * @param string $searchEngineIdentifier - */ - public function registerSearchEngineIndexer(SearchEngineIndexer $searchEngineIndexer, $searchEngineIdentifier) - { - $this->searchEngineIndexers[$searchEngineIdentifier] = $searchEngineIndexer; - } - - /** - * Returns registered search engine indexers. - * - * @return \eZ\Publish\Core\Search\Common\Indexer[] - */ - public function getSearchEngineIndexers() - { - return $this->searchEngineIndexers; - } - - /** - * Build search engine indexer identified by its identifier (the "alias" attribute in the service tag), - * resolved for current siteaccess. - * - * @throws \eZ\Bundle\EzPublishCoreBundle\ApiLoader\Exception\InvalidSearchEngineIndexer - * - * @return \eZ\Publish\Core\Search\Common\Indexer - */ - public function buildSearchEngineIndexer() - { - $repositoryConfig = $this->repositoryConfigurationProvider->getRepositoryConfig(); - - if ( - !( - isset($repositoryConfig['search']['engine']) - && isset($this->searchEngineIndexers[$repositoryConfig['search']['engine']]) - ) - ) { - throw new InvalidSearchEngineIndexer( - "Invalid search engine '{$repositoryConfig['search']['engine']}'. " . - "Could not find any service tagged with 'ezplatform.search_engine.indexer' " . - "with alias '{$repositoryConfig['search']['engine']}'." - ); - } - - return $this->searchEngineIndexers[$repositoryConfig['search']['engine']]; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/StorageEngineFactory.php b/eZ/Bundle/EzPublishCoreBundle/ApiLoader/StorageEngineFactory.php deleted file mode 100644 index 3d03ea2aa1..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/StorageEngineFactory.php +++ /dev/null @@ -1,80 +0,0 @@ -repositoryConfigurationProvider = $repositoryConfigurationProvider; - } - - /** - * Registers $persistenceHandler as a valid storage engine, with identifier $storageEngineIdentifier. - * - * Note: It is strongly recommenced to register a lazy persistent handler. - * - * @param \eZ\Publish\SPI\Persistence\Handler $persistenceHandler - * @param string $storageEngineIdentifier - */ - public function registerStorageEngine(PersistenceHandler $persistenceHandler, $storageEngineIdentifier) - { - $this->storageEngines[$storageEngineIdentifier] = $persistenceHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Handler[] - */ - public function getStorageEngines() - { - return $this->storageEngines; - } - - /** - * Builds storage engine identified by $storageEngineIdentifier (the "alias" attribute in the service tag). - * - * @throws \eZ\Bundle\EzPublishCoreBundle\ApiLoader\Exception\InvalidStorageEngine - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - public function buildStorageEngine() - { - $repositoryConfig = $this->repositoryConfigurationProvider->getRepositoryConfig(); - - if ( - !( - isset($repositoryConfig['storage']['engine']) - && isset($this->storageEngines[$repositoryConfig['storage']['engine']]) - ) - ) { - throw new InvalidStorageEngine( - "Invalid storage engine '{$repositoryConfig['storage']['engine']}'. " . - 'Could not find any service tagged with ezpublish.storageEngine ' . - "with alias {$repositoryConfig['storage']['engine']}." - ); - } - - return $this->storageEngines[$repositoryConfig['storage']['engine']]; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Cache/Warmer/ProxyCacheWarmer.php b/eZ/Bundle/EzPublishCoreBundle/Cache/Warmer/ProxyCacheWarmer.php deleted file mode 100644 index 95dbacd9c9..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Cache/Warmer/ProxyCacheWarmer.php +++ /dev/null @@ -1,54 +0,0 @@ -proxyGenerator = $proxyGenerator; - } - - public function isOptional(): bool - { - return false; - } - - public function warmUp($cacheDir): void - { - $this->proxyGenerator->warmUp(self::PROXY_CLASSES); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Command/BackwardCompatibleCommand.php b/eZ/Bundle/EzPublishCoreBundle/Command/BackwardCompatibleCommand.php deleted file mode 100644 index 889bac1058..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Command/BackwardCompatibleCommand.php +++ /dev/null @@ -1,19 +0,0 @@ -locationService = $locationService; - $this->permissionResolver = $permissionResolver; - $this->userService = $userService; - $this->contentTypeService = $contentTypeService; - $this->searchService = $searchService; - } - - protected function configure() - { - $this - ->setName('ibexa:copy-subtree') - ->setAliases($this->getDeprecatedAliases()) - ->addArgument( - 'source-location-id', - InputArgument::REQUIRED, - 'ID of source Location' - ) - ->addArgument( - 'target-location-id', - InputArgument::REQUIRED, - 'ID of target Location' - ) - ->addOption( - 'user', - 'u', - InputOption::VALUE_OPTIONAL, - 'eZ Platform username (with Role containing at least content policies: create, read)', - 'admin' - ) - ->setDescription('Copies a subtree from one Location to another'); - } - - /** - * @param \Symfony\Component\Console\Input\InputInterface $input - * @param \Symfony\Component\Console\Output\OutputInterface $output - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - protected function initialize(InputInterface $input, OutputInterface $output) - { - parent::initialize($input, $output); - $this->permissionResolver->setCurrentUserReference( - $this->userService->loadUserByLogin($input->getOption('user')) - ); - } - - /** - * @param \Symfony\Component\Console\Input\InputInterface $input - * @param \Symfony\Component\Console\Output\OutputInterface $output - * - * @return int|null - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - protected function execute(InputInterface $input, OutputInterface $output): int - { - $sourceLocationId = $input->getArgument('source-location-id'); - $targetLocationId = $input->getArgument('target-location-id'); - - $sourceLocation = $this->locationService->loadLocation($sourceLocationId); - $targetLocation = $this->locationService->loadLocation($targetLocationId); - - if (stripos($targetLocation->pathString, $sourceLocation->pathString) !== false) { - throw new InvalidArgumentException( - 'target-location-id', - 'Cannot copy subtree to its own descendant Location' - ); - } - - $targetContentType = $this->contentTypeService->loadContentType( - $targetLocation->getContentInfo()->contentTypeId - ); - - if (!$targetContentType->isContainer) { - throw new InvalidArgumentException( - 'target-location-id', - 'The selected Location cannot contain children' - ); - } - $questionHelper = $this->getHelper('question'); - $question = new ConfirmationQuestion( - sprintf( - 'Are you sure you want to copy `%s` subtree (no. of children: %d) into `%s`? This may take a while for a big number of nested children [Y/n]? ', - $sourceLocation->contentInfo->name, - $this->getAllChildrenCount($sourceLocation), - $targetLocation->contentInfo->name - ) - ); - - if (!$input->getOption('no-interaction') && !$questionHelper->ask($input, $output, $question)) { - return 0; - } - - $this->locationService->copySubtree( - $sourceLocation, - $targetLocation - ); - - $output->writeln( - 'Finished' - ); - - return 0; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return int - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - protected function getAllChildrenCount(Location $location): int - { - $query = new LocationQuery([ - 'filter' => new Criterion\Subtree($location->pathString), - ]); - - $searchResults = $this->searchService->findLocations($query); - - return $searchResults->totalCount; - } - - public function getDeprecatedAliases(): array - { - return ['ezplatform:copy-subtree']; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Controller.php b/eZ/Bundle/EzPublishCoreBundle/Controller.php deleted file mode 100644 index c2bbac5d9f..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Controller.php +++ /dev/null @@ -1,55 +0,0 @@ -container->get('ezpublish.api.repository'); - } - - protected function getConfigResolver(): ConfigResolverInterface - { - return $this->container->get('ezpublish.config.resolver'); - } - - public function getGlobalHelper(): GlobalHelper - { - return $this->container->get('ezpublish.templating.global_helper'); - } - - /** - * Returns the root location object for current siteaccess configuration. - * - * @return \eZ\Publish\API\Repository\Values\Content\Location - */ - public function getRootLocation(): Location - { - return $this->getGlobalHelper()->getRootLocation(); - } - - public static function getSubscribedServices(): array - { - return array_merge( - parent::getSubscribedServices(), - [ - 'ezpublish.api.repository' => Repository::class, - 'ezpublish.config.resolver' => ConfigResolverInterface::class, - 'ezpublish.templating.global_helper' => GlobalHelper::class, - ] - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Converter/ContentParamConverter.php b/eZ/Bundle/EzPublishCoreBundle/Converter/ContentParamConverter.php deleted file mode 100644 index c74e6621c1..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Converter/ContentParamConverter.php +++ /dev/null @@ -1,35 +0,0 @@ -contentService = $contentService; - } - - protected function getSupportedClass() - { - return 'eZ\Publish\API\Repository\Values\Content\Content'; - } - - protected function getPropertyName() - { - return 'contentId'; - } - - protected function loadValueObject($id) - { - return $this->contentService->loadContent($id); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Converter/LocationParamConverter.php b/eZ/Bundle/EzPublishCoreBundle/Converter/LocationParamConverter.php deleted file mode 100644 index 26524099fd..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Converter/LocationParamConverter.php +++ /dev/null @@ -1,48 +0,0 @@ -locationService = $locationService; - $this->contentPreviewHelper = $contentPreviewHelper; - } - - protected function getSupportedClass() - { - return 'eZ\Publish\API\Repository\Values\Content\Location'; - } - - protected function getPropertyName() - { - return 'locationId'; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - protected function loadValueObject($id): Location - { - $prioritizedLanguages = $this->contentPreviewHelper->isPreviewActive() ? Language::ALL : null; - - return $this->locationService->loadLocation($id, $prioritizedLanguages); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/BinaryContentDownloadPass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/BinaryContentDownloadPass.php deleted file mode 100644 index b330230ea6..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/BinaryContentDownloadPass.php +++ /dev/null @@ -1,39 +0,0 @@ -has('ezpublish.fieldType.ezbinarybase.download_url_generator')) { - return; - } - - $downloadUrlReference = new Reference('ezpublish.fieldType.ezbinarybase.download_url_generator'); - - $this->addCall($container, $downloadUrlReference, 'ezpublish.fieldType.ezmedia.externalStorage'); - $this->addCall($container, $downloadUrlReference, 'ezpublish.fieldType.ezbinaryfile.externalStorage'); - } - - private function addCall(ContainerBuilder $container, Reference $reference, $targetServiceName) - { - if (!$container->has($targetServiceName)) { - return; - } - - $definition = $container->findDefinition($targetServiceName); - $definition->addMethodCall('setDownloadUrlGenerator', [$reference]); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ContentViewPass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ContentViewPass.php deleted file mode 100644 index cdfe0adca6..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ContentViewPass.php +++ /dev/null @@ -1,22 +0,0 @@ -hasDefinition('ezpublish.fieldType.parameterProviderRegistry')) { - return; - } - - $parameterProviderRegistryDef = $container->getDefinition('ezpublish.fieldType.parameterProviderRegistry'); - - $iterator = new BackwardCompatibleIterator( - $container, - self::FIELD_TYPE_PARAMETER_PROVIDER_SERVICE_TAG, - self::DEPRECATED_FIELD_TYPE_PARAMETER_PROVIDER_SERVICE_TAG - ); - - foreach ($iterator as $id => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['alias'])) { - throw new \LogicException( - sprintf( - '%s or %s service tag needs an "alias" attribute to identify the Field Type.', - self::DEPRECATED_FIELD_TYPE_PARAMETER_PROVIDER_SERVICE_TAG, - self::FIELD_TYPE_PARAMETER_PROVIDER_SERVICE_TAG - ) - ); - } - - $parameterProviderRegistryDef->addMethodCall( - 'setParameterProvider', - [ - // Only pass the service Id since field types will be lazy loaded via the service container - new Reference($id), - $attribute['alias'], - ] - ); - } - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ImaginePass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ImaginePass.php deleted file mode 100644 index bc58b84430..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ImaginePass.php +++ /dev/null @@ -1,59 +0,0 @@ -hasDefinition('liip_imagine.filter.configuration')) { - return; - } - - $filterConfigDef = $container->findDefinition('liip_imagine.filter.configuration'); - $filterConfigDef->setClass(FilterConfiguration::class); - $filterConfigDef->addMethodCall('setConfigResolver', [new Reference('ezpublish.config.resolver')]); - - if ($container->hasAlias('liip_imagine')) { - $imagineAlias = (string)$container->getAlias('liip_imagine'); - $driver = substr($imagineAlias, strrpos($imagineAlias, '.') + 1); - - $this->processReduceNoiseFilter($container, $driver); - $this->processSwirlFilter($container, $driver); - } - } - - private function processReduceNoiseFilter(ContainerBuilder $container, $driver) - { - if ($driver !== 'imagick' && $driver !== 'gmagick') { - return; - } - - $container->setAlias( - 'ezpublish.image_alias.imagine.filter.reduce_noise', - new Alias("ezpublish.image_alias.imagine.filter.reduce_noise.$driver") - ); - } - - private function processSwirlFilter(ContainerBuilder $container, $driver) - { - if ($driver !== 'imagick' && $driver !== 'gmagick') { - return; - } - - $container->setAlias( - 'ezpublish.image_alias.imagine.filter.swirl', - new Alias("ezpublish.image_alias.imagine.filter.swirl.$driver") - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/LocationViewPass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/LocationViewPass.php deleted file mode 100644 index 3d790ffb6d..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/LocationViewPass.php +++ /dev/null @@ -1,22 +0,0 @@ -hasDefinition('ezpublish.query_type.registry')) { - return; - } - - $queryTypes = []; - - $iterator = new BackwardCompatibleIterator( - $container, - self::QUERY_TYPE_SERVICE_TAG, - self::DEPRECATED_QUERY_TYPE_SERVICE_TAG - ); - - foreach ($iterator as $taggedServiceId => $tags) { - $queryTypeDefinition = $container->getDefinition($taggedServiceId); - $queryTypeClass = $container->getParameterBag()->resolveValue($queryTypeDefinition->getClass()); - - for ($i = 0, $count = count($tags); $i < $count; ++$i) { - $name = isset($tags[$i]['alias']) ? $tags[$i]['alias'] : $queryTypeClass::getName(); - $queryTypes[$name] = new Reference($taggedServiceId); - } - } - - $aggregatorDefinition = $container->getDefinition('ezpublish.query_type.registry'); - $aggregatorDefinition->addMethodCall('addQueryTypes', [$queryTypes]); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/RegisterSearchEngineIndexerPass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/RegisterSearchEngineIndexerPass.php deleted file mode 100644 index d932c2dc73..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/RegisterSearchEngineIndexerPass.php +++ /dev/null @@ -1,77 +0,0 @@ -hasDefinition($this->factoryId)) { - return; - } - - $searchEngineIndexerFactoryDefinition = $container->getDefinition($this->factoryId); - - $iterator = new BackwardCompatibleIterator( - $container, - self::SEARCH_ENGINE_INDEXER_SERVICE_TAG, - self::DEPRECATED_SEARCH_ENGINE_INDEXER_SERVICE_TAG - ); - - foreach ($iterator as $serviceId => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['alias'])) { - throw new LogicException( - sprintf( - 'Service "%s" tagged with "%s" or "%s" needs an "alias" attribute to identify the search engine', - $serviceId, - self::SEARCH_ENGINE_INDEXER_SERVICE_TAG, - self::DEPRECATED_SEARCH_ENGINE_INDEXER_SERVICE_TAG - ) - ); - } - - // Register the search engine with the search engine factory - $searchEngineIndexerFactoryDefinition->addMethodCall( - 'registerSearchEngineIndexer', - [ - new Reference($serviceId), - $attribute['alias'], - ] - ); - } - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/RegisterSearchEnginePass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/RegisterSearchEnginePass.php deleted file mode 100644 index cc5ed6c0f3..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/RegisterSearchEnginePass.php +++ /dev/null @@ -1,77 +0,0 @@ -hasDefinition($this->factoryId)) { - return; - } - - $searchEngineFactoryDefinition = $container->getDefinition($this->factoryId); - - $iterator = new BackwardCompatibleIterator( - $container, - self::SEARCH_ENGINE_SERVICE_TAG, - self::DEPRECATED_SEATCH_ENGINE_SERVICE_TAG - ); - - foreach ($iterator as $serviceId => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['alias'])) { - throw new LogicException( - sprintf( - 'Service "%s" tagged with "%s" or "%s" needs an "alias" attribute to identify the search engine', - $serviceId, - self::SEARCH_ENGINE_SERVICE_TAG, - self::DEPRECATED_SEATCH_ENGINE_SERVICE_TAG - ) - ); - } - - // Register the search engine with the search engine factory - $searchEngineFactoryDefinition->addMethodCall( - 'registerSearchEngine', - [ - new Reference($serviceId), - $attribute['alias'], - ] - ); - } - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/RegisterStorageEnginePass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/RegisterStorageEnginePass.php deleted file mode 100644 index e04dff8bdf..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/RegisterStorageEnginePass.php +++ /dev/null @@ -1,53 +0,0 @@ -hasDefinition('ezpublish.api.storage_engine.factory')) { - return; - } - - $storageEngineFactoryDef = $container->getDefinition('ezpublish.api.storage_engine.factory'); - foreach ($container->findTaggedServiceIds('ezpublish.storageEngine') as $id => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['alias'])) { - throw new LogicException('ezpublish.storageEngine service tag needs an "alias" attribute to identify the storage engine.'); - } - - // Register the storage engine on the main storage engine factory - $storageEngineFactoryDef->addMethodCall( - 'registerStorageEngine', - [ - new Reference($id), - $attribute['alias'], - ] - ); - } - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/SecurityPass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/SecurityPass.php deleted file mode 100644 index cf8396083c..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/SecurityPass.php +++ /dev/null @@ -1,109 +0,0 @@ -hasDefinition('security.authentication.provider.dao') && - $container->hasDefinition('security.authentication.provider.rememberme') && - $container->hasDefinition('security.authentication.provider.anonymous'))) { - return; - } - - $configResolverRef = new Reference('ezpublish.config.resolver'); - $permissionResolverRef = new Reference(PermissionResolver::class); - $userServiceRef = new Reference(UserService::class); - $loggerRef = new Reference('logger'); - - // Override and inject the Repository in the authentication provider. - // We need it for checking user credentials - $daoAuthenticationProviderDef = $container->findDefinition('security.authentication.provider.dao'); - $daoAuthenticationProviderDef->setClass(RepositoryAuthenticationProvider::class); - $daoAuthenticationProviderDef->addMethodCall( - 'setPermissionResolver', - [$permissionResolverRef] - ); - $daoAuthenticationProviderDef->addMethodCall( - 'setUserService', - [$userServiceRef] - ); - $daoAuthenticationProviderDef->addMethodCall( - 'setConstantAuthTime', - [ - $container->hasParameter(self::CONSTANT_AUTH_TIME_SETTING) ? - (float)$container->getParameter(self::CONSTANT_AUTH_TIME_SETTING) : - self::CONSTANT_AUTH_TIME_DEFAULT, - ] - ); - $daoAuthenticationProviderDef->addMethodCall( - 'setLogger', - [$loggerRef] - ); - - $rememberMeAuthenticationProviderDef = $container->findDefinition('security.authentication.provider.rememberme'); - $rememberMeAuthenticationProviderDef->setClass(RememberMeRepositoryAuthenticationProvider::class); - $rememberMeAuthenticationProviderDef->addMethodCall( - 'setPermissionResolver', - [$permissionResolverRef] - ); - - $anonymousAuthenticationProviderDef = $container->findDefinition('security.authentication.provider.anonymous'); - $anonymousAuthenticationProviderDef->setClass(AnonymousAuthenticationProvider::class); - $anonymousAuthenticationProviderDef->addMethodCall( - 'setPermissionResolver', - [$permissionResolverRef] - ); - - $anonymousAuthenticationProviderDef->addMethodCall( - 'setConfigResolver', - [$configResolverRef] - ); - - if (!$container->hasDefinition('security.http_utils')) { - return; - } - - $httpUtilsDef = $container->findDefinition('security.http_utils'); - $httpUtilsDef->setClass(HttpUtils::class); - $httpUtilsDef->addMethodCall( - 'setSiteAccess', - [new Reference('ezpublish.siteaccess')] - ); - - if (!$container->hasDefinition('security.authentication.success_handler')) { - return; - } - - $successHandlerDef = $container->getDefinition('security.authentication.success_handler'); - $successHandlerDef->setClass(DefaultAuthenticationSuccessHandler::class); - $successHandlerDef->addMethodCall( - 'setConfigResolver', - [$configResolverRef] - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/StorageConnectionPass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/StorageConnectionPass.php deleted file mode 100644 index a98726a775..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/StorageConnectionPass.php +++ /dev/null @@ -1,39 +0,0 @@ -findTaggedServiceIds('ezpublish.storageEngine') as $id => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['alias'])) { - throw new LogicException( - 'ezpublish.storageEngine service tag needs an "alias" attribute to ' . - 'identify the storage engine.' - ); - } - - $alias = $attribute['alias']; - - $container->setAlias( - "ezpublish.api.storage_engine.{$alias}.connection", - 'ezpublish.persistence.connection' - ); - } - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/TranslationCollectorPass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/TranslationCollectorPass.php deleted file mode 100644 index abbd0bc7bb..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/TranslationCollectorPass.php +++ /dev/null @@ -1,57 +0,0 @@ - 'de', - 'el_GR' => 'el', - 'es_ES' => 'es', - 'fi_FI' => 'fi', - 'fr_FR' => 'fr', - 'hi_IN' => 'hi', - 'hr_HR' => 'hr', - 'hu_HU' => 'hu', - 'it_IT' => 'it', - 'ja_JP' => 'ja', - 'nb_NO' => 'nb', - 'pl_PL' => 'pl', - 'pt_PT' => 'pt', - 'ru_RU' => 'ru', - ]; - - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('translator.default')) { - return; - } - - $collector = new GlobCollector($container->getParameterBag()->get('kernel.project_dir')); - - $availableTranslations = [self::ORIGINAL_TRANSLATION]; - foreach ($collector->collect() as $file) { - /* TODO - to remove when translation files will have proper names. */ - if (isset(self::LOCALES_MAP[$file['locale']])) { - $file['locale'] = self::LOCALES_MAP[$file['locale']]; - } - $availableTranslations[] = $file['locale']; - } - - $container->setParameter('available_translations', array_values(array_unique($availableTranslations))); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/URLHandlerPass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/URLHandlerPass.php deleted file mode 100644 index 9c4470e764..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/URLHandlerPass.php +++ /dev/null @@ -1,45 +0,0 @@ -hasDefinition('ezpublish.url_checker.handler_registry')) { - return; - } - - $definition = $container->findDefinition('ezpublish.url_checker.handler_registry'); - foreach ($container->findTaggedServiceIds('ezpublish.url_handler') as $id => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['scheme'])) { - throw new LogicException(sprintf( - '%s service tag needs a "scheme" attribute to identify which scheme is supported by the handler.', - 'ezpublish.url_handler' - )); - } - - $definition->addMethodCall('addHandler', [ - $attribute['scheme'], - new Reference($id), - ]); - } - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ViewManagerPass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ViewManagerPass.php deleted file mode 100644 index 3af6499530..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ViewManagerPass.php +++ /dev/null @@ -1,43 +0,0 @@ -findTaggedServiceIds(static::VIEW_PROVIDER_IDENTIFIER) as $id => $attributes) { - $taggedServiceDefinition = $container->getDefinition($id); - foreach ($attributes as $attribute) { - // @todo log deprecated message - $priority = isset($attribute['priority']) ? (int)$attribute['priority'] : 0; - $taggedServiceDefinition->clearTag(static::VIEW_PROVIDER_IDENTIFIER); - $taggedServiceDefinition->addTag( - 'ezpublish.view_provider', - ['type' => static::VIEW_TYPE, 'priority' => $priority] - ); - } - $container->setDefinition($id, $taggedServiceDefinition); - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ViewMatcherRegistryPass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ViewMatcherRegistryPass.php deleted file mode 100644 index bc7a68d452..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ViewMatcherRegistryPass.php +++ /dev/null @@ -1,41 +0,0 @@ -hasDefinition(ViewMatcherRegistry::class)) { - return; - } - - $matcherServiceRegistry = $container->getDefinition(ViewMatcherRegistry::class); - - foreach ($container->findTaggedServiceIds(self::MATCHER_TAG) as $id => $attributes) { - $matcherServiceRegistry->addMethodCall( - 'setMatcher', - [ - $id, - new Reference($id), - ] - ); - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ViewProvidersPass.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ViewProvidersPass.php deleted file mode 100644 index 53bd53ace6..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ViewProvidersPass.php +++ /dev/null @@ -1,79 +0,0 @@ -findTaggedServiceIds('ezpublish.view_provider') as $serviceId => $tags) { - foreach ($tags as $attributes) { - // Priority range is between -255 (the lowest) and 255 (the highest) - $priority = isset($attributes['priority']) ? max(min((int)$attributes['priority'], 255), -255) : 0; - - if (!isset($attributes['type'])) { - throw new LogicException("Missing mandatory attribute 'type' for ezpublish.view_provider tag"); - } - $type = $attributes['type']; - - $rawViewProviders[$type][$priority][] = new Reference($serviceId); - } - } - - $viewProviders = []; - foreach ($rawViewProviders as $type => $viewProvidersPerPriority) { - krsort($viewProvidersPerPriority); - foreach ($viewProvidersPerPriority as $priorityViewProviders) { - if (!isset($viewProviders[$type])) { - $viewProviders[$type] = []; - } - $viewProviders[$type] = array_merge($viewProviders[$type], $priorityViewProviders); - } - } - - if ($container->hasDefinition('ezpublish.view_provider.registry')) { - $container->getDefinition('ezpublish.view_provider.registry')->addMethodCall( - 'setViewProviders', - [$viewProviders] - ); - } - - $flattenedViewProviders = []; - foreach ($viewProviders as $type => $typeViewProviders) { - foreach ($typeViewProviders as $typeViewProvider) { - $flattenedViewProviders[] = $typeViewProvider; - } - } - - if ($container->hasDefinition('ezpublish.config_scope_listener')) { - $container->getDefinition('ezpublish.config_scope_listener')->addMethodCall( - 'setViewProviders', - [$flattenedViewProviders] - ); - } - - // 5.4.5 BC service after location view deprecation - if ($container->hasDefinition('ezpublish.view.custom_location_controller_checker')) { - $container->getDefinition('ezpublish.view.custom_location_controller_checker')->addMethodCall( - 'addViewProviders', - [$viewProviders['eZ\Publish\Core\MVC\Symfony\View\ContentView']] - ); - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration.php deleted file mode 100644 index 153e0fccd7..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration.php +++ /dev/null @@ -1,578 +0,0 @@ -suggestionCollector = $suggestionCollector; - $this->mainConfigParser = $mainConfigParser; - } - - public function setSiteAccessConfigurationFilters(array $filters) - { - $this->siteAccessConfigurationFilters = $filters; - } - - /** - * Generates the configuration tree builder. - * - * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder - */ - public function getConfigTreeBuilder() - { - $treeBuilder = new TreeBuilder('ezpublish'); - - $rootNode = $treeBuilder->getRootNode(); - - $this->addRepositoriesSection($rootNode); - $this->addSiteaccessSection($rootNode); - $this->addImageMagickSection($rootNode); - $this->addHttpCacheSection($rootNode); - $this->addRouterSection($rootNode); - $this->addUrlAliasSection($rootNode); - $this->addImagePlaceholderSection($rootNode); - $this->addUrlWildcardsSection($rootNode); - $this->addOrmSection($rootNode); - - // Delegate SiteAccess config to configuration parsers - $this->mainConfigParser->addSemanticConfig($this->generateScopeBaseNode($rootNode)); - - return $treeBuilder; - } - - public function addRepositoriesSection(ArrayNodeDefinition $rootNode) - { - $rootNode - ->children() - ->arrayNode('repositories') - ->info('Content repositories configuration') - ->example( - [ - 'main' => [ - 'storage' => [ - 'engine' => 'legacy', - 'connection' => 'my_doctrine_connection_name', - ], - ], - ] - ) - ->useAttributeAsKey('alias') - ->prototype('array') - ->beforeNormalization() - ->always( - // Handling deprecated structure by mapping it to new one - static function ($v) { - if (isset($v['storage'])) { - return $v; - } - - if (isset($v['engine'])) { - $v['storage']['engine'] = $v['engine']; - unset($v['engine']); - } - - if (isset($v['connection'])) { - $v['storage']['connection'] = $v['connection']; - unset($v['connection']); - } - - if (isset($v['config'])) { - $v['storage']['config'] = $v['config']; - unset($v['config']); - } - - return $v; - } - ) - ->end() - ->beforeNormalization() - ->always( - // Setting default values - static function ($v) { - if ($v === null) { - $v = []; - } - - if (!isset($v['storage'])) { - $v['storage'] = []; - } - - if (!isset($v['search'])) { - $v['search'] = []; - } - - if (!isset($v['fields_groups']['list'])) { - $v['fields_groups']['list'] = []; - } - - if (!isset($v['options'])) { - $v['options'] = []; - } - - return $v; - } - ) - ->end() - ->children() - ->arrayNode('storage') - ->children() - ->scalarNode('engine') - ->defaultValue('%ezpublish.api.storage_engine.default%') - ->info('The storage engine to use') - ->end() - ->scalarNode('connection') - ->defaultNull() - ->info('The connection name, if applicable (e.g. Doctrine connection name). If not set, the default connection will be used.') - ->end() - ->arrayNode('config') - ->info('Arbitrary configuration options, supported by your storage engine') - ->useAttributeAsKey('key') - ->prototype('variable')->end() - ->end() - ->end() - ->end() - ->arrayNode('search') - ->children() - ->scalarNode('engine') - ->defaultValue('%ezpublish.api.search_engine.default%') - ->info('The search engine to use') - ->end() - ->scalarNode('connection') - ->defaultNull() - ->info('The connection name, if applicable (e.g. Doctrine connection name). If not set, the default connection will be used.') - ->end() - ->arrayNode('config') - ->info('Arbitrary configuration options, supported by your search engine') - ->useAttributeAsKey('key') - ->prototype('variable')->end() - ->end() - ->end() - ->end() - ->arrayNode('fields_groups') - ->info('Definitions of fields groups.') - ->children() - ->arrayNode('list')->prototype('scalar')->end()->end() - ->scalarNode('default')->defaultValue('%ezsettings.default.content.field_groups.default%')->end() - ->end() - ->end() - ->arrayNode('options') - ->info('Options for repository.') - ->children() - ->scalarNode('default_version_archive_limit') - ->defaultValue(5) - ->info('Default version archive limit (0-50), only enforced on publish, not on un-publish.') - ->end() - ->booleanNode('remove_archived_versions_on_publish') - ->defaultTrue() - ->info('Enables automatic removal of archived versions when publishing, at the cost of performance. "ezplatform:content:cleanup-versions" command should be used to perform this task instead if this option is set to false.') - ->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->end(); - } - - public function addSiteaccessSection(ArrayNodeDefinition $rootNode) - { - $rootNode - ->children() - ->arrayNode('siteaccess') - ->info('SiteAccess configuration') - ->children() - ->arrayNode('list') - ->info('Available SiteAccess list') - ->example(['ezdemo_site', 'ezdemo_site_admin']) - ->isRequired() - ->requiresAtLeastOneElement() - ->prototype('scalar')->end() - ->end() - ->arrayNode('groups') - ->useAttributeAsKey('key') - ->info('SiteAccess groups. Useful to share settings between Siteaccess') - ->example(['ezdemo_group' => ['ezdemo_site', 'ezdemo_site_admin']]) - ->prototype('array') - ->prototype('scalar')->end() - ->end() - ->end() - ->scalarNode('default_siteaccess')->isRequired()->info('Name of the default siteaccess')->end() - ->arrayNode('match') - ->info('Siteaccess match configuration. First key is the matcher class, value is passed to the matcher. Key can be a service identifier (prepended by "@"), or a FQ class name (prepended by "\\")') - ->example( - [ - 'Map\\URI' => [ - 'foo' => 'ezdemo_site', - 'ezdemo_site' => 'ezdemo_site', - 'ezdemo_site_admin' => 'ezdemo_site_admin', - ], - 'Map\\Host' => [ - 'ezpublish.dev' => 'ezdemo_site', - 'admin.ezpublish.dev' => 'ezdemo_site_admin', - ], - '\\My\\Custom\\Matcher' => [ - 'some' => 'configuration', - ], - '@my.custom.matcher' => [ - 'some' => 'other_configuration', - ], - ] - ) - ->isRequired() - ->useAttributeAsKey('key') - ->normalizeKeys(false) - ->prototype('array') - ->useAttributeAsKey('key') - ->beforeNormalization() - ->always( - static function ($v) { - // Value passed to the matcher should always be an array. - // If value is not an array, we transform it to a hash, with 'value' as key. - if (!is_array($v)) { - return ['value' => $v]; - } - - // If passed value is a numerically indexed array, we must convert it into a hash. - // See https://jira.ez.no/browse/EZP-21876 - if (array_keys($v) === range(0, count($v) - 1)) { - $final = []; - foreach ($v as $i => $val) { - $final["i$i"] = $val; - } - - return $final; - } - - return $v; - } - ) - ->end() - ->normalizeKeys(false) - ->prototype('variable')->end() - ->end() - ->end() - ->end() - ->beforeNormalization() - ->always()->then(function ($v) { - if (isset($this->siteAccessConfigurationFilters)) { - foreach ($this->siteAccessConfigurationFilters as $filter) { - $v = $filter->filter($v); - } - } - - return $v; - }) - ->end() - ->end() - ->arrayNode('locale_conversion') - ->info('Locale conversion map between eZ Publish format (i.e. fre-FR) to POSIX (i.e. fr_FR). The key is the eZ Publish locale. Check locale.yml in EzPublishCoreBundle to see natively supported locales.') - ->example(['fre-FR' => 'fr_FR']) - ->useAttributeAsKey('key') - ->normalizeKeys(false) - ->prototype('scalar')->end() - ->end() - ->end(); - } - - private function addImageMagickSection(ArrayNodeDefinition $rootNode) - { - $filtersInfo = -<<children() - ->arrayNode('imagemagick') - ->info('ImageMagick configuration') - ->children() - ->booleanNode('enabled')->defaultTrue()->end() - ->scalarNode('path') - ->info('Absolute path of ImageMagick / GraphicsMagick "convert" binary.') - ->beforeNormalization() - ->ifTrue( - static function ($v) { - $basename = basename($v); - // If there is a space in the basename, just drop it and everything after it. - if (($wsPos = strpos($basename, ' ')) !== false) { - $basename = substr($basename, 0, $wsPos); - } - - return !is_executable(dirname($v) . \DIRECTORY_SEPARATOR . $basename); - } - ) - ->thenInvalid('Please provide full path to ImageMagick / GraphicsMagick "convert" binary. Please also check that it is executable.') - ->end() - ->end() - ->arrayNode('filters') - ->info($filtersInfo) - ->example(['geometry/scaledownonly' => '"-geometry {1}x{2}>"']) - ->prototype('scalar')->end() - ->end() - ->end() - ->end() - ->end(); - } - - private function addHttpCacheSection(ArrayNodeDefinition $rootNode) - { - $purgeTypeInfo = << Purge locations #123, #456, #789. - - .* => Purge all locations. -EOT; - - $rootNode - ->children() - ->arrayNode('http_cache') - ->children() - ->scalarNode('purge_type') - ->info($purgeTypeInfo) - ->defaultValue('local') - ->beforeNormalization() - ->ifTrue( - static function ($v) { - $http = ['multiple_http' => true, 'single_http' => true]; - - return isset($http[$v]); - } - ) - ->then( - static function () { - return 'http'; - } - ) - ->end() - ->end() - ->scalarNode('timeout')->info('DEPRECATED')->end() - ->end() - ->end() - ->end(); - } - - private function addRouterSection(ArrayNodeDefinition $rootNode) - { - $nonSAAwareInfo = <<children() - ->arrayNode('router') - ->children() - ->arrayNode('default_router') - ->children() - ->arrayNode('non_siteaccess_aware_routes') - ->prototype('scalar')->end() - ->info($nonSAAwareInfo) - ->example(['my_route_name', 'some_prefix_']) - ->end() - ->end() - ->end() - ->end() - ->info('Router related settings') - ->end() - ->end(); - } - - /** - * Defines configuration the images placeholder generation. - * - * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $rootNode - */ - private function addImagePlaceholderSection(ArrayNodeDefinition $rootNode) - { - $rootNode - ->children() - ->arrayNode('image_placeholder') - ->info('Configuration for strategy of replacing missing images') - ->useAttributeAsKey('name') - ->arrayPrototype() - ->children() - ->scalarNode('provider') - ->end() - ->variableNode('options') - ->defaultValue([]) - ->end() - ->booleanNode('verify_binary_data_availability') - ->info('Enable additional binary data availability check for source image. Will cause additional IO operation.') - ->defaultFalse() - ->end() - ->end() - ->end() - ->end() - ->end(); - } - - /** - * Define Url Alias Slug converter Semantic Configuration. - * - * The configuration is available at: - * - * ezpublish: - * url_alias: - * slug_converter: - * transformation: name_of_transformation_group_to_use - * separator: name_of_separator_to_use - * transformation_groups: - * transformation_group_name: name of existing or new transformation group - * commands : [] array of commands which will be added to group - * cleanup_method: name_of_cleanup_method - * - * - * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $rootNode - * - * @return \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition - */ - private function addUrlAliasSection(ArrayNodeDefinition $rootNode) - { - return $rootNode - ->children() - ->arrayNode('url_alias') - ->children() - ->arrayNode('slug_converter') - ->children() - ->scalarNode('transformation')->end() - ->scalarNode('separator')->end() - ->arrayNode('transformation_groups') - ->arrayPrototype() - ->children() - ->arrayNode('commands') - ->scalarPrototype()->end() - ->end() - ->scalarNode('cleanup_method')->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->end(); - } - - /** - * Defines configuration for Url Wildcards. - * - * The configuration is available at: - * - * ezpublish: - * url_wildcards: - * enabled: true - * - * - * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $rootNode - * - * @return \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition - */ - private function addUrlWildcardsSection($rootNode): ArrayNodeDefinition - { - return $rootNode - ->children() - ->arrayNode('url_wildcards') - ->children() - ->booleanNode('enabled') - ->info('Enable UrlWildcards support') - ->defaultFalse() - ->end() - ->end() - ->end() - ->end(); - } - - /** - * Defines configuration for Doctrine ORM. - * - * The configuration is available at: - * - * ezpublish: - * orm: - * entity_mappings: - * EzPublishCoreBundle: - * is_bundle: true - * type: annotation - * dir: Entity - * prefix: eZ\Bundle\EzPublishCoreBundle\Entity - * - * - * - * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $rootNode - * - * @return \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition - */ - private function addOrmSection($rootNode): ArrayNodeDefinition - { - return $rootNode - ->children() - ->arrayNode('orm') - ->children() - ->arrayNode('entity_mappings') - ->info('Entity Mapping configuration compatible with Doctrine ORM bundle') - ->useAttributeAsKey('name') - ->defaultValue([]) - ->arrayPrototype() - ->children() - ->booleanNode('is_bundle')->defaultTrue()->end() - ->booleanNode('mapping')->defaultTrue()->end() - ->scalarNode('type') - ->isRequired() - ->end() - ->scalarNode('dir') - ->isRequired() - ->end() - ->scalarNode('prefix') - ->isRequired() - ->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->end(); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/AbstractParser.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/AbstractParser.php deleted file mode 100644 index 383e6d4450..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/AbstractParser.php +++ /dev/null @@ -1,44 +0,0 @@ -mapConfigArray(). - * - * @see ConfigurationProcessor::mapConfig() - * @see ContextualizerInterface::mapConfigArray() - * - * @param array $config Complete parsed semantic configuration - * @param \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface $contextualizer - * - * @return mixed - */ - public function preMap(array $config, ContextualizerInterface $contextualizer) - { - } - - /** - * This method is called by the ConfigurationProcessor after looping over available scopes. - * You may here use $contextualizer->mapConfigArray(). - * - * @see ConfigurationProcessor::mapConfig() - * @see ContextualizerInterface::mapConfigArray() - * - * @param array $config Complete parsed semantic configuration - * @param \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface $contextualizer - * - * @return mixed - */ - public function postMap(array $config, ContextualizerInterface $contextualizer) - { - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserInterface.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserInterface.php deleted file mode 100644 index dfbcb942f9..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserInterface.php +++ /dev/null @@ -1,35 +0,0 @@ -containerBuilder = $containerBuilder; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/AbstractFieldTypeParser.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/AbstractFieldTypeParser.php deleted file mode 100644 index c6f2574668..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/AbstractFieldTypeParser.php +++ /dev/null @@ -1,30 +0,0 @@ -..fieldtypes.. - */ -abstract class AbstractFieldTypeParser extends AbstractParser implements FieldTypeParserInterface -{ - /** - * Adds semantic configuration definition. - * - * @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $nodeBuilder Node just under ezpublish.. - */ - public function addSemanticConfig(NodeBuilder $nodeBuilder) - { - $fieldTypeNodeBuilder = $nodeBuilder->arrayNode($this->getFieldTypeIdentifier())->children(); - - $this->addFieldTypeSemanticConfig($fieldTypeNodeBuilder); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Content.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Content.php deleted file mode 100644 index daad763eb4..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Content.php +++ /dev/null @@ -1,81 +0,0 @@ - - */ - public function addSemanticConfig(NodeBuilder $nodeBuilder) - { - $nodeBuilder - ->arrayNode('content') - ->info('Content related configuration') - ->children() - ->booleanNode('view_cache')->end() - ->booleanNode('ttl_cache')->end() - ->scalarNode('default_ttl')->info('Default value for TTL cache, in seconds')->end() - ->arrayNode('tree_root') - ->canBeUnset() - ->children() - ->integerNode('location_id') - ->info("Root locationId for routing and link generation.\nUseful for multisite apps with one repository.") - ->isRequired() - ->end() - ->arrayNode('excluded_uri_prefixes') - ->info("URI prefixes that are allowed to be outside the content tree\n(useful for content sharing between multiple sites).\nPrefixes are not case sensitive") - ->example(['/media/images', '/products']) - ->prototype('scalar')->end() - ->end() - ->end() - ->end() - ->end() - ->end(); - } - - public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerInterface $contextualizer) - { - if (!empty($scopeSettings['content'])) { - if (isset($scopeSettings['content']['view_cache'])) { - $contextualizer->setContextualParameter('content.view_cache', $currentScope, $scopeSettings['content']['view_cache']); - } - - if (isset($scopeSettings['content']['ttl_cache'])) { - $contextualizer->setContextualParameter('content.ttl_cache', $currentScope, $scopeSettings['content']['ttl_cache']); - } - - if (isset($scopeSettings['content']['default_ttl'])) { - $contextualizer->setContextualParameter('content.default_ttl', $currentScope, $scopeSettings['content']['default_ttl']); - } - - if (isset($scopeSettings['content']['tree_root'])) { - $contextualizer->setContextualParameter( - 'content.tree_root.location_id', - $currentScope, - $scopeSettings['content']['tree_root']['location_id'] - ); - if (isset($scopeSettings['content']['tree_root']['excluded_uri_prefixes'])) { - $contextualizer->setContextualParameter( - 'content.tree_root.excluded_uri_prefixes', - $currentScope, - $scopeSettings['content']['tree_root']['excluded_uri_prefixes'] - ); - } - } - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/ContentView.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/ContentView.php deleted file mode 100644 index 0bf2aa99ec..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/ContentView.php +++ /dev/null @@ -1,13 +0,0 @@ -suggestionCollector = $suggestionCollector; - } - - /** - * Adds semantic configuration definition. - * - * @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $nodeBuilder Node just under ezpublish.system. - */ - public function addSemanticConfig(NodeBuilder $nodeBuilder) - { - $nodeBuilder - ->arrayNode('imagemagick') - ->info('DEPRECATED.') - ->children() - ->scalarNode('pre_parameters')->info('Parameters that must be run BEFORE the filenames and filters')->end() - ->scalarNode('post_parameters')->info('Parameters that must be run AFTER the filenames and filters')->end() - ->end() - ->end() - ->arrayNode('image_variations') - ->info('Configuration for your image variations (aka "image aliases")') - ->example( - [ - 'my_image_variation' => [ - 'reference' => '~', - 'filters' => [ - [ - 'name' => 'geometry/scaledownonly', - 'params' => [400, 350], - ], - ], - ], - 'my_cropped_variation' => [ - 'reference' => 'my_image_variation', - 'filters' => [ - [ - 'name' => 'geometry/scalewidthdownonly', - 'params' => [300], - ], - [ - 'name' => 'geometry/crop', - 'params' => [300, 300, 0, 0], - ], - ], - ], - ] - ) - ->useAttributeAsKey('variation_name') - ->normalizeKeys(false) - ->prototype('array') - ->children() - ->scalarNode('reference') - ->info('Tells the system which original variation to use as reference image. Defaults to original') - ->example('large') - ->end() - ->arrayNode('filters') - ->info('A list of filters to run, each filter must be supported by the active image converters') - ->useAttributeAsKey('name') - ->normalizeKeys(false) - ->prototype('array') - ->info('Array/Hash of parameters to pass to the filter') - ->useAttributeAsKey('options') - ->beforeNormalization() - ->ifTrue( - static function ($v) { - // Check if passed array only contains a "params" key (BC with <=5.3). - return is_array($v) && count($v) === 1 && isset($v['params']); - } - ) - ->then( - static function ($v) { - // If we have the "params" key, just use the value. - return $v['params']; - } - ) - ->end() - ->prototype('variable')->end() - ->end() - ->end() - ->arrayNode('post_processors') - ->info('Post processors as defined in LiipImagineBundle. See https://github.com/liip/LiipImagineBundle/blob/master/Resources/doc/filters.md#post-processors') - ->useAttributeAsKey('name') - ->prototype('array') - ->useAttributeAsKey('name') - ->prototype('variable')->end() - ->end() - ->end() - ->end() - ->end() - ->end() - ->scalarNode('image_host') - ->info('Images host. All system images URLs are prefixed with given host if configured.') - ->example('https://ezplatform.com') - ->end(); - } - - public function preMap(array $config, ContextualizerInterface $contextualizer) - { - $contextualizer->mapConfigArray('image_variations', $config); - $contextualizer->mapSetting('image_host', $config); - } - - public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerInterface $contextualizer) - { - if (isset($scopeSettings['imagemagick'])) { - $suggestion = new ConfigSuggestion( - '"imagemagick" settings are deprecated. Just remove them from your configuration file.' - ); - $this->suggestionCollector->addSuggestion($suggestion); - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/View.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/View.php deleted file mode 100644 index b5efe56145..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/View.php +++ /dev/null @@ -1,80 +0,0 @@ - - */ - public function addSemanticConfig(NodeBuilder $nodeBuilder) - { - $nodeBuilder - ->arrayNode(static::NODE_KEY) - ->info(static::INFO) - ->useAttributeAsKey('key') - ->normalizeKeys(false) - ->prototype('array') - ->useAttributeAsKey('key') - ->normalizeKeys(false) - ->info("View selection rulesets, grouped by view type. Key is the view type (e.g. 'full', 'line', ...)") - ->prototype('array') - ->children() - ->scalarNode('template')->info('Your template path, as MyBundle:subdir:my_template.html.twig')->end() - ->scalarNode('controller') - ->info( - <<example('MyBundle:MyControllerClass:view') - ->end() - ->arrayNode('match') - ->info('Condition matchers configuration') - ->isRequired() - ->useAttributeAsKey('key') - ->prototype('variable')->end() - ->end() - ->arrayNode('params') - ->info( - <<example( - [ - 'foo' => '%some.parameter.reference%', - 'osTypes' => ['osx', 'linux', 'windows'], - ] - ) - ->useAttributeAsKey('key') - ->prototype('variable')->end() - ->end() - ->end() - ->end() - ->end() - ->end(); - } - - public function preMap(array $config, ContextualizerInterface $contextualizer) - { - $contextualizer->mapConfigArray(static::NODE_KEY, $config, ContextualizerInterface::MERGE_FROM_SECOND_LEVEL); - } - - public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerInterface $contextualizer) - { - // Nothing to do here. - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ParserInterface.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ParserInterface.php deleted file mode 100644 index ece5fcbb6a..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ParserInterface.php +++ /dev/null @@ -1,20 +0,0 @@ - - */ - public function addSemanticConfig(NodeBuilder $nodeBuilder); -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/Configuration.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/Configuration.php deleted file mode 100644 index df50d772a3..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/Configuration.php +++ /dev/null @@ -1,67 +0,0 @@ - - * ezpublish: - * system: - * eng: - * languages: - * - eng-GB - * - * fre: - * languages: - * - fre-FR - * - eng-GB - * - */ -abstract class Configuration implements ConfigurationInterface -{ - /** - * Generates the context node under which context based configuration will be defined. - * - * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $rootNode Node under which the generated node will be placed. - * @param string $scopeNodeName - * - * @return \Symfony\Component\Config\Definition\Builder\NodeBuilder - */ - public function generateScopeBaseNode(ArrayNodeDefinition $rootNode, $scopeNodeName = 'system') - { - $contextNode = $rootNode - ->children() - ->arrayNode($scopeNodeName) - ->info('System configuration. First key is always a siteaccess or siteaccess group name') - ->example( - [ - 'my_siteaccess' => [ - 'preferred_quote' => 'Let there be Light!', - 'j_aime' => ['le_saucisson'], - ], - 'my_siteaccess_group' => [ - 'j_aime' => ['la_truite_a_la_vapeur'], - ], - ] - ) - ->useAttributeAsKey('siteaccess_name') - ->requiresAtLeastOneElement() - ->normalizeKeys(false) - ->prototype('array') - ->children(); - - return $contextNode; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollector.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollector.php deleted file mode 100644 index 4589e2922d..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollector.php +++ /dev/null @@ -1,43 +0,0 @@ -suggestions[] = $suggestion; - } - - /** - * Returns all config suggestions. - * - * @return \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\ConfigSuggestion[] - */ - public function getSuggestions() - { - return $this->suggestions; - } - - /** - * @return bool - */ - public function hasSuggestions() - { - return !empty($this->suggestions); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorAwareInterface.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorAwareInterface.php deleted file mode 100644 index c412632865..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorAwareInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -getMessage(); - $suggestion = $configSuggestion->getSuggestion(); - if ($suggestion) { - $yamlConfig = Yaml::dump($suggestion, 8); - if (\PHP_SAPI !== 'cli') { - $yamlConfig = "
$yamlConfig
"; - } - - return <<addSuggestion($suggestion); - } - - $this->assertTrue($collector->hasSuggestions()); - $this->assertSame($suggestions, $collector->getSuggestions()); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/EzPublishCoreExtension.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/EzPublishCoreExtension.php deleted file mode 100644 index 97fc9fc095..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/EzPublishCoreExtension.php +++ /dev/null @@ -1,685 +0,0 @@ - null, - 'mappings' => [], - ]; - - private const DEBUG_PARAM = 'kernel.debug'; - - /** @var \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollector */ - private $suggestionCollector; - - /** @var \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ParserInterface */ - private $mainConfigParser; - - /** @var \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ParserInterface[] */ - private $configParsers; - - /** @var \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\PolicyProviderInterface[] */ - private $policyProviders = []; - - /** - * Holds a collection of YAML files, as an array with directory path as a - * key to the array of contained file names. - * - * @var array - */ - private $defaultSettingsCollection = []; - - /** @var \eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessConfigurationFilter[] */ - private $siteaccessConfigurationFilters = []; - - public function __construct(array $configParsers = []) - { - $this->configParsers = $configParsers; - $this->suggestionCollector = new SuggestionCollector(); - } - - public function getAlias() - { - return 'ezpublish'; - } - - /** - * Loads a specific configuration. - * - * @param mixed[] $configs An array of configuration values - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container A ContainerBuilder instance - * - * @throws \InvalidArgumentException When provided tag is not defined in this extension - * - * @api - */ - public function load(array $configs, ContainerBuilder $container) - { - $loader = new Loader\YamlFileLoader( - $container, - new FileLocator(__DIR__ . '/../Resources/config') - ); - - $configuration = $this->getConfiguration($configs, $container); - - if ($this->shouldLoadTestServices($container)) { - $loader->load('feature_contexts.yml'); - } - - // Note: this is where the transformation occurs - $config = $this->processConfiguration($configuration, $configs); - - // Base services and services overrides - $loader->load('services.yml'); - // Security services - $loader->load('security.yml'); - // HTTP Kernel - $loader->load('http_kernel.yml'); - - if (interface_exists('FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractorInterface')) { - $loader->load('routing/js_routing.yml'); - } - - // Default settings - $this->handleDefaultSettingsLoading($container, $loader); - - $this->registerRepositoriesConfiguration($config, $container); - $this->registerSiteAccessConfiguration($config, $container); - $this->registerImageMagickConfiguration($config, $container); - $this->registerUrlAliasConfiguration($config, $container); - $this->registerUrlWildcardsConfiguration($config, $container); - $this->registerOrmConfiguration($config, $container); - - // Routing - $this->handleRouting($config, $container, $loader); - // Public API loading - $this->handleApiLoading($container, $loader); - $this->handleTemplating($container, $loader); - $this->handleSessionLoading($container, $loader); - $this->handleCache($config, $container, $loader); - $this->handleLocale($config, $container, $loader); - $this->handleHelpers($config, $container, $loader); - $this->handleImage($config, $container, $loader); - $this->handleUrlChecker($config, $container, $loader); - $this->handleUrlWildcards($config, $container, $loader); - - // Map settings - $processor = new ConfigurationProcessor($container, 'ezsettings'); - $processor->mapConfig($config, $this->getMainConfigParser()); - - if ($this->suggestionCollector->hasSuggestions()) { - $message = ''; - $suggestionFormatter = new YamlSuggestionFormatter(); - foreach ($this->suggestionCollector->getSuggestions() as $suggestion) { - $message .= $suggestionFormatter->format($suggestion) . "\n\n"; - } - - throw new InvalidArgumentException($message); - } - - $this->buildPolicyMap($container); - - $this->registerForAutoConfiguration($container); - } - - /** - * @param array $config - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * - * @return \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration - */ - public function getConfiguration(array $config, ContainerBuilder $container) - { - $configuration = new Configuration($this->getMainConfigParser(), $this->suggestionCollector); - $configuration->setSiteAccessConfigurationFilters($this->siteaccessConfigurationFilters); - - return $configuration; - } - - /** - * {@inheritdoc} - */ - public function prepend(ContainerBuilder $container) - { - $this->prependTranslatorConfiguration($container); - $this->prependDoctrineConfiguration($container); - } - - /** - * @return \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ParserInterface - */ - private function getMainConfigParser() - { - if ($this->mainConfigParser === null) { - foreach ($this->configParsers as $parser) { - if ($parser instanceof SuggestionCollectorAwareInterface) { - $parser->setSuggestionCollector($this->suggestionCollector); - } - } - - $this->mainConfigParser = new ConfigParser($this->configParsers); - } - - return $this->mainConfigParser; - } - - /** - * Handle default settings. - * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader - * - * @throws \Exception - */ - private function handleDefaultSettingsLoading(ContainerBuilder $container, FileLoader $loader) - { - $loader->load('default_settings.yml'); - - foreach ($this->defaultSettingsCollection as $fileLocation => $files) { - $externalLoader = new Loader\YamlFileLoader($container, new FileLocator($fileLocation)); - foreach ($files as $file) { - $externalLoader->load($file); - } - } - } - - private function registerRepositoriesConfiguration(array $config, ContainerBuilder $container) - { - if (!isset($config['repositories'])) { - $config['repositories'] = []; - } - - foreach ($config['repositories'] as $name => &$repository) { - if (empty($repository['fields_groups']['list'])) { - $repository['fields_groups']['list'] = $container->getParameter('ezsettings.default.content.field_groups.list'); - } - } - - $container->setParameter('ezpublish.repositories', $config['repositories']); - } - - private function registerSiteAccessConfiguration(array $config, ContainerBuilder $container) - { - if (!isset($config['siteaccess'])) { - $config['siteaccess'] = []; - $config['siteaccess']['list'] = ['setup']; - $config['siteaccess']['default_siteaccess'] = 'setup'; - $config['siteaccess']['groups'] = []; - $config['siteaccess']['match'] = null; - } - - $container->setParameter('ezpublish.siteaccess.list', $config['siteaccess']['list']); - ConfigurationProcessor::setAvailableSiteAccesses($config['siteaccess']['list']); - $container->setParameter('ezpublish.siteaccess.default', $config['siteaccess']['default_siteaccess']); - $container->setParameter('ezpublish.siteaccess.match_config', $config['siteaccess']['match']); - - // Register siteaccess groups + reverse - $container->setParameter('ezpublish.siteaccess.groups', $config['siteaccess']['groups']); - ConfigurationProcessor::setAvailableSiteAccessGroups($config['siteaccess']['groups']); - $groupsBySiteaccess = []; - foreach ($config['siteaccess']['groups'] as $groupName => $groupMembers) { - foreach ($groupMembers as $member) { - if (!isset($groupsBySiteaccess[$member])) { - $groupsBySiteaccess[$member] = []; - } - - $groupsBySiteaccess[$member][] = $groupName; - } - } - $container->setParameter('ezpublish.siteaccess.groups_by_siteaccess', $groupsBySiteaccess); - ConfigurationProcessor::setGroupsBySiteAccess($groupsBySiteaccess); - } - - private function registerImageMagickConfiguration(array $config, ContainerBuilder $container) - { - if (isset($config['imagemagick'])) { - $container->setParameter('ezpublish.image.imagemagick.enabled', $config['imagemagick']['enabled']); - if ($config['imagemagick']['enabled']) { - $container->setParameter('ezpublish.image.imagemagick.executable_path', dirname($config['imagemagick']['path'])); - $container->setParameter('ezpublish.image.imagemagick.executable', basename($config['imagemagick']['path'])); - } - } - - $filters = isset($config['imagemagick']['filters']) ? $config['imagemagick']['filters'] : []; - $filters = $filters + $container->getParameter('ezpublish.image.imagemagick.filters'); - $container->setParameter('ezpublish.image.imagemagick.filters', $filters); - } - - private function registerOrmConfiguration(array $config, ContainerBuilder $container): void - { - if (!isset($config['orm']['entity_mappings'])) { - return; - } - - $entityMappings = $config['orm']['entity_mappings']; - $container->setParameter('ibexa.orm.entity_mappings', $entityMappings); - } - - /** - * Handle routing parameters. - * - * @param array $config - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader - */ - private function handleRouting(array $config, ContainerBuilder $container, FileLoader $loader) - { - $loader->load('routing.yml'); - $container->setAlias('router', 'ezpublish.chain_router'); - $container->getAlias('router')->setPublic(true); - - if (isset($config['router']['default_router']['non_siteaccess_aware_routes'])) { - $container->setParameter( - 'ezpublish.default_router.non_siteaccess_aware_routes', - array_merge( - $container->getParameter('ezpublish.default_router.non_siteaccess_aware_routes'), - $config['router']['default_router']['non_siteaccess_aware_routes'] - ) - ); - } - } - - /** - * Handle public API loading. - * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader - */ - private function handleApiLoading(ContainerBuilder $container, FileLoader $loader) - { - // Loading configuration from Core/settings - $coreLoader = new Loader\YamlFileLoader( - $container, - new FileLocator(__DIR__ . '/../../../Publish/Core/settings') - ); - $coreLoader->load('repository.yml'); - $coreLoader->load('repository/inner.yml'); - $coreLoader->load('repository/event.yml'); - $coreLoader->load('repository/siteaccessaware.yml'); - $coreLoader->load('repository/autowire.yml'); - $coreLoader->load('fieldtype_external_storages.yml'); - $coreLoader->load('fieldtypes.yml'); - $coreLoader->load('indexable_fieldtypes.yml'); - $coreLoader->load('fieldtype_services.yml'); - $coreLoader->load('roles.yml'); - $coreLoader->load('storage_engines/common.yml'); - $coreLoader->load('storage_engines/cache.yml'); - $coreLoader->load('storage_engines/legacy.yml'); - $coreLoader->load('storage_engines/shortcuts.yml'); - $coreLoader->load('search_engines/common.yml'); - $coreLoader->load('utils.yml'); - $coreLoader->load('io.yml'); - $coreLoader->load('policies.yml'); - $coreLoader->load('notification.yml'); - $coreLoader->load('user_preference.yml'); - $coreLoader->load('events.yml'); - $coreLoader->load('thumbnails.yml'); - $coreLoader->load('content_location_mapper.yml'); - - // Public API services - $loader->load('papi.yml'); - - // Built-in field types - $loader->load('fieldtype_services.yml'); - - // Storage engine - $loader->load('storage_engines.yml'); - - $loader->load('query_types.yml'); - $loader->load('sort_spec.yml'); - } - - /** - * Handle templating parameters. - * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader - */ - private function handleTemplating(ContainerBuilder $container, FileLoader $loader) - { - $loader->load('templating.yml'); - } - - /** - * Handle session parameters. - * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader - */ - private function handleSessionLoading(ContainerBuilder $container, FileLoader $loader) - { - $loader->load('session.yml'); - } - - /** - * Handle cache parameters. - * - * @param array $config - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader - * - * @throws \InvalidArgumentException - */ - private function handleCache(array $config, ContainerBuilder $container, FileLoader $loader) - { - $loader->load('cache.yml'); - - if (isset($config['http_cache']['purge_type'])) { - // resolves ENV variable at compile time, needed by ezplatform-http-cache to setup purge driver - $purgeType = $container->resolveEnvPlaceholders($config['http_cache']['purge_type'], true); - - $container->setParameter('ezpublish.http_cache.purge_type', $purgeType); - } - - if ( - $container->hasParameter(self::DEBUG_PARAM) - && $container->getParameter(self::DEBUG_PARAM) === true - ) { - $loader->load('debug/cache_validator.yaml'); - } - } - - /** - * Handle locale parameters. - * - * @param array $config - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader - */ - private function handleLocale(array $config, ContainerBuilder $container, FileLoader $loader) - { - $loader->load('locale.yml'); - $container->setParameter( - 'ezpublish.locale.conversion_map', - $config['locale_conversion'] + $container->getParameter('ezpublish.locale.conversion_map') - ); - } - - /** - * Handle helpers. - * - * @param array $config - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader - */ - private function handleHelpers(array $config, ContainerBuilder $container, FileLoader $loader) - { - $loader->load('helpers.yml'); - } - - /** - * @param array $config - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader - */ - private function handleImage(array $config, ContainerBuilder $container, FileLoader $loader) - { - $loader->load('image.yml'); - - $providers = []; - if (isset($config['image_placeholder'])) { - foreach ($config['image_placeholder'] as $name => $value) { - if (isset($providers[$name])) { - throw new InvalidConfigurationException("An image_placeholder called $name already exists"); - } - - $providers[$name] = $value; - } - } - - $container->setParameter('image_alias.placeholder_providers', $providers); - } - - private function handleUrlChecker($config, ContainerBuilder $container, FileLoader $loader) - { - $loader->load('url_checker.yml'); - } - - private function buildPolicyMap(ContainerBuilder $container) - { - $policiesBuilder = new PoliciesConfigBuilder($container); - foreach ($this->policyProviders as $provider) { - $provider->addPolicies($policiesBuilder); - } - } - - /** - * Adds a new policy provider to the internal collection. - * One can call this method from a bundle `build()` method. - * - * ```php - * public function build(ContainerBuilder $container) - * { - * $ezExtension = $container->getExtension('ezpublish'); - * $ezExtension->addPolicyProvider($myPolicyProvider); - * } - * ``` - * - * @since 6.0 - * - * @param \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\PolicyProviderInterface $policyProvider - */ - public function addPolicyProvider(PolicyProviderInterface $policyProvider) - { - $this->policyProviders[] = $policyProvider; - } - - /** - * Adds a new config parser to the internal collection. - * One can call this method from a bundle `build()` method. - * - * ```php - * public function build(ContainerBuilder $container) - * { - * $ezExtension = $container->getExtension('ezpublish'); - * $ezExtension->addConfigParser($myConfigParser); - * } - * ``` - * - * @since 6.0 - * - * @param \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ParserInterface $configParser - */ - public function addConfigParser(ParserInterface $configParser) - { - $this->configParsers[] = $configParser; - } - - /** - * Adds new default settings to the internal collection. - * One can call this method from a bundle `build()` method. - * - * ```php - * public function build(ContainerBuilder $container) - * { - * $ezExtension = $container->getExtension('ezpublish'); - * $ezExtension->addDefaultSettings( - * __DIR__ . '/Resources/config', - * ['default_settings.yml'] - * ); - * } - * ``` - * - * @since 6.0 - * - * @param string $fileLocation - * @param array $files - */ - public function addDefaultSettings($fileLocation, array $files) - { - $this->defaultSettingsCollection[$fileLocation] = $files; - } - - public function addSiteAccessConfigurationFilter(SiteAccessConfigurationFilter $filter) - { - $this->siteaccessConfigurationFilters[] = $filter; - } - - /** - * @param array $config - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - */ - private function registerUrlAliasConfiguration(array $config, ContainerBuilder $container) - { - if (!isset($config['url_alias'])) { - $config['url_alias'] = ['slug_converter' => []]; - } - - $container->setParameter('ezpublish.url_alias.slug_converter', $config['url_alias']['slug_converter']); - } - - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - */ - private function prependTranslatorConfiguration(ContainerBuilder $container) - { - if (!$container->hasExtension('framework')) { - return; - } - - $fileSystem = new Filesystem(); - $translationsPath = $container->getParameterBag()->get('kernel.project_dir') . '/vendor/ezplatform-i18n'; - - if ($fileSystem->exists($translationsPath)) { - $container->prependExtensionConfig('framework', ['translator' => ['paths' => [$translationsPath]]]); - } - } - - private function prependDoctrineConfiguration(ContainerBuilder $container): void - { - if (!$container->hasExtension('doctrine')) { - return; - } - - $kernelConfigs = array_merge( - $container->getExtensionConfig('ezpublish'), - $container->getExtensionConfig('ezplatform') - ); - $entityMappings = []; - - $repositoryConnections = []; - foreach ($kernelConfigs as $config) { - if (isset($config['orm']['entity_mappings'])) { - $entityMappings[] = $config['orm']['entity_mappings']; - } - - if (isset($config['repositories'])) { - $repositoryConnections[] = array_map( - static function (array $repository): ?string { - return $repository['storage']['connection'] - ?? 'default'; - }, - $config['repositories'] - ); - } - } - - // compose clean array with all connection identifiers - $connections = array_values( - array_filter( - array_unique( - array_merge(...$repositoryConnections) ?? [] - ) - ) - ); - - $doctrineConfig = [ - 'orm' => [ - 'entity_managers' => [], - ], - ]; - - $entityMappingConfig = !empty($entityMappings) ? array_merge_recursive(...$entityMappings) : []; - - foreach ($connections as $connection) { - $doctrineConfig['orm']['entity_managers'][sprintf('ibexa_%s', $connection)] = array_merge( - self::ENTITY_MANAGER_TEMPLATE, - ['connection' => $connection, 'mappings' => $entityMappingConfig] - ); - } - - $container->prependExtensionConfig('doctrine', $doctrineConfig); - } - - /** - * @param array $config - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - */ - private function registerUrlWildcardsConfiguration(array $config, ContainerBuilder $container): void - { - $container->setParameter('ezpublish.url_wildcards.enabled', $config['url_wildcards']['enabled'] ?? false); - } - - /** - * Loads configuration for UrlWildcardsRouter service if enabled. - * - * @param array $config - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader - */ - private function handleUrlWildcards(array $config, ContainerBuilder $container, Loader\YamlFileLoader $loader) - { - if ($container->getParameter('ezpublish.url_wildcards.enabled')) { - $loader->load('url_wildcard.yml'); - } - } - - private function registerForAutoConfiguration(ContainerBuilder $container): void - { - $container->registerForAutoconfiguration(QueryType::class) - ->addTag(QueryTypePass::QUERY_TYPE_SERVICE_TAG); - - $container->registerForAutoconfiguration(ConfigScopeChangeSubscriber::class) - ->addTag( - 'kernel.event_listener', - ['method' => 'onConfigScopeChange', 'event' => MVCEvents::CONFIG_SCOPE_CHANGE] - ) - ->addTag( - 'kernel.event_listener', - ['method' => 'onConfigScopeChange', 'event' => MVCEvents::CONFIG_SCOPE_RESTORE] - ); - - $container->registerForAutoconfiguration(FilteringCriterionQueryBuilder::class) - ->addTag(ServiceTags::FILTERING_CRITERION_QUERY_BUILDER); - - $container->registerForAutoconfiguration(FilteringSortClauseQueryBuilder::class) - ->addTag(ServiceTags::FILTERING_SORT_CLAUSE_QUERY_BUILDER); - } - - private function shouldLoadTestServices(ContainerBuilder $container): bool - { - return $container->hasParameter('ibexa.testing.browser.enabled') - && true === $container->getParameter('ibexa.testing.browser.enabled'); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Security/HttpBasicFactory.php b/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Security/HttpBasicFactory.php deleted file mode 100644 index 62883ef38b..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Security/HttpBasicFactory.php +++ /dev/null @@ -1,22 +0,0 @@ -configResolvers = $configResolvers; - $this->viewManager = $viewManager; - } - - public static function getSubscribedEvents() - { - return [ - MVCEvents::CONFIG_SCOPE_CHANGE => ['onConfigScopeChange', 100], - MVCEvents::CONFIG_SCOPE_RESTORE => ['onConfigScopeChange', 100], - ]; - } - - public function onConfigScopeChange(ScopeChangeEvent $event): void - { - $siteAccess = $event->getSiteAccess(); - - foreach ($this->configResolvers as $configResolver) { - if ($configResolver instanceof VersatileScopeInterface) { - $configResolver->setDefaultScope($siteAccess->name); - } - } - - if ($this->viewManager instanceof SiteAccessAware) { - $this->viewManager->setSiteAccess($siteAccess); - } - - foreach ($this->viewProviders as $viewProvider) { - if ($viewProvider instanceof SiteAccessAware) { - $viewProvider->setSiteAccess($siteAccess); - } - } - } - - /** - * Sets the complete list of view providers. - */ - public function setViewProviders(array $viewProviders) - { - $this->viewProviders = $viewProviders; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/EzPublishCoreBundle.php b/eZ/Bundle/EzPublishCoreBundle/EzPublishCoreBundle.php deleted file mode 100644 index 3568988f1e..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/EzPublishCoreBundle.php +++ /dev/null @@ -1,132 +0,0 @@ -addCompilerPass(new GenericFieldTypeConverterPass(), PassConfig::TYPE_OPTIMIZE); - $container->addCompilerPass(new FieldTypeRegistryPass(), PassConfig::TYPE_OPTIMIZE); - $container->addCompilerPass(new PersistenceFieldTypeRegistryPass(), PassConfig::TYPE_OPTIMIZE); - $container->addCompilerPass(new FieldTypeParameterProviderRegistryPass()); - $container->addCompilerPass(new ChainRoutingPass()); - $container->addCompilerPass(new ChainConfigResolverPass()); - $container->addCompilerPass(new RegisterStorageEnginePass()); - $container->addCompilerPass(new RegisterSearchEnginePass()); - $container->addCompilerPass(new RegisterSearchEngineIndexerPass()); - $container->addCompilerPass(new AggregateFieldValueMapperPass()); - $container->addCompilerPass(new FieldRegistryPass()); - $container->addCompilerPass(new ContentViewPass()); - $container->addCompilerPass(new LocationViewPass()); - $container->addCompilerPass(new RouterPass()); - $container->addCompilerPass(new SecurityPass()); - $container->addCompilerPass(new FragmentPass()); - $container->addCompilerPass(new StorageConnectionPass()); - $container->addCompilerPass(new ImaginePass()); - $container->addCompilerPass(new URLHandlerPass()); - $container->addCompilerPass(new BinaryContentDownloadPass()); - $container->addCompilerPass(new ViewProvidersPass()); - $container->addCompilerPass(new PlaceholderProviderPass()); - $container->addCompilerPass(new NotificationRendererPass()); - $container->addCompilerPass(new ConsoleCacheWarmupPass()); - $container->addCompilerPass(new ViewMatcherRegistryPass()); - $container->addCompilerPass(new SiteAccessMatcherRegistryPass()); - $container->addCompilerPass(new ConsoleCommandPass()); - $container->addCompilerPass(new LazyDoctrineRepositoriesPass(), PassConfig::TYPE_BEFORE_REMOVING); - $container->addCompilerPass(new EntityManagerFactoryServiceLocatorPass()); - $container->addCompilerPass(new InjectEntityManagerMappingsPass()); - - // Storage passes - $container->addCompilerPass(new ExternalStorageRegistryPass()); - // Legacy Storage passes - $container->addCompilerPass(new FieldValueConverterRegistryPass()); - $container->addCompilerPass(new RoleLimitationConverterPass()); - $container->addCompilerPass(new QueryTypePass()); - - $securityExtension = $container->getExtension('security'); - $securityExtension->addSecurityListenerFactory(new HttpBasicFactory()); - $container->addCompilerPass(new TranslationCollectorPass()); - $container->addCompilerPass(new SlugConverterConfigurationPass()); - - $container->registerForAutoconfiguration(VariableProvider::class)->addTag('ezplatform.view.variable_provider'); - } - - public function getContainerExtension() - { - if (!isset($this->extension)) { - $this->extension = new EzPublishCoreExtension([ - // LocationView config parser needs to be specified AFTER ContentView config - // parser since it is used to convert location view override rules to content - // view override rules. If it were specified before, ContentView provider would - // just undo the conversion LocationView did. - new ConfigParser\ContentView(), - new ConfigParser\LocationView(), - new ConfigParser\Common(), - new ConfigParser\Content(), - new ConfigParser\FieldType\ImageAsset(), - new ConfigParser\FieldTemplates(), - new ConfigParser\FieldEditTemplates(), - new ConfigParser\FieldDefinitionSettingsTemplates(), - new ConfigParser\FieldDefinitionEditTemplates(), - new ConfigParser\Image(), - new ConfigParser\Languages(), - new ConfigParser\IO(new ComplexSettingParser()), - new ConfigParser\UrlChecker(), - new ConfigParser\TwigVariablesParser(), - ]); - } - - return $this->extension; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/AliasGenerator.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/AliasGenerator.php deleted file mode 100644 index 4f8c1e65a1..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/AliasGenerator.php +++ /dev/null @@ -1,167 +0,0 @@ -dataLoader = $dataLoader; - $this->filterManager = $filterManager; - $this->ioResolver = $ioResolver; - $this->filterConfiguration = $filterConfiguration; - $this->logger = null !== $logger ? $logger : new NullLogger(); - } - - /** - * {@inheritdoc} - * - * @throws \InvalidArgumentException If field value is not an instance of \eZ\Publish\Core\FieldType\Image\Value. - * @throws \eZ\Publish\Core\MVC\Exception\SourceImageNotFoundException If source image cannot be found. - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidVariationException If a problem occurs with generated variation. - */ - public function getVariation(Field $field, VersionInfo $versionInfo, $variationName, array $parameters = []) - { - /** @var \eZ\Publish\Core\FieldType\Image\Value $imageValue */ - $imageValue = $field->value; - $fieldId = $field->id; - $fieldDefIdentifier = $field->fieldDefIdentifier; - if (!$this->supportsValue($imageValue)) { - throw new InvalidArgumentException("Value of Field with ID $fieldId ($fieldDefIdentifier) cannot be used for generating an image variation."); - } - - $originalPath = $imageValue->id; - - $variationWidth = $variationHeight = null; - // Create the image alias only if it does not already exist. - if ($variationName !== IORepositoryResolver::VARIATION_ORIGINAL && !$this->ioResolver->isStored($originalPath, $variationName)) { - try { - $originalBinary = $this->dataLoader->find($originalPath); - } catch (NotLoadableException $e) { - throw new SourceImageNotFoundException((string)$originalPath, 0, $e); - } - - $this->logger->debug("Generating '$variationName' variation on $originalPath, field #$fieldId ($fieldDefIdentifier)"); - - $this->ioResolver->store( - $this->applyFilter($originalBinary, $variationName), - $originalPath, - $variationName - ); - } else { - if ($variationName === IORepositoryResolver::VARIATION_ORIGINAL) { - $variationWidth = $imageValue->width; - $variationHeight = $imageValue->height; - } - $this->logger->debug("'$variationName' variation on $originalPath is already generated. Loading from cache."); - } - - try { - $aliasInfo = new SplFileInfo( - $this->ioResolver->resolve($originalPath, $variationName) - ); - } catch (NotResolvableException $e) { - // If for some reason image alias cannot be resolved, throw the appropriate exception. - throw new InvalidVariationException($variationName, 'image', 0, $e); - } catch (RuntimeException $e) { - throw new InvalidVariationException($variationName, 'image', 0, $e); - } - - return new ImageVariation( - [ - 'name' => $variationName, - 'fileName' => $aliasInfo->getFilename(), - 'dirPath' => $aliasInfo->getPath(), - 'uri' => $aliasInfo->getPathname(), - 'imageId' => $imageValue->imageId, - 'width' => $variationWidth, - 'height' => $variationHeight, - ] - ); - } - - /** - * Applies $variationName filters on $image. - * - * Both variations configured in eZ (SiteAccess context) and LiipImagineBundle are used. - * An eZ variation may have a "reference". - * In that case, reference's filters are applied first, recursively (a reference may also have another reference). - * Reference must be a valid variation name, configured in eZ or in LiipImagineBundle. - * - * @param \Liip\ImagineBundle\Binary\BinaryInterface $image - * @param string $variationName - * - * @return \Liip\ImagineBundle\Binary\BinaryInterface - */ - private function applyFilter(BinaryInterface $image, $variationName) - { - $filterConfig = $this->filterConfiguration->get($variationName); - // If the variation has a reference, we recursively call this method to apply reference's filters. - if (isset($filterConfig['reference']) && $filterConfig['reference'] !== IORepositoryResolver::VARIATION_ORIGINAL) { - $image = $this->applyFilter($image, $filterConfig['reference']); - } - - return $this->filterManager->applyFilter($image, $variationName); - } - - public function supportsValue(Value $value) - { - return $value instanceof ImageValue; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/FilterConfiguration.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/FilterConfiguration.php deleted file mode 100644 index 7ad49b5279..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/FilterConfiguration.php +++ /dev/null @@ -1,100 +0,0 @@ -configResolver = $configResolver; - } - - public function get($filter) - { - $configuredVariations = $this->configResolver->getParameter('image_variations'); - if (!isset($configuredVariations[$filter])) { - return parent::get($filter); - } - - $filterConfig = isset($this->filters[$filter]) ? parent::get($filter) : []; - - return [ - 'cache' => 'ezpublish', - 'data_loader' => 'ezpublish', - 'reference' => isset($configuredVariations[$filter]['reference']) ? $configuredVariations[$filter]['reference'] : null, - 'filters' => $this->getVariationFilters($filter, $configuredVariations), - 'post_processors' => $this->getVariationPostProcessors($filter, $configuredVariations), - ] + $filterConfig; - } - - public function all() - { - return $this->configResolver->getParameter('image_variations') + parent::all(); - } - - /** - * Returns filters to be used for $variationName. - * - * Both variations configured in eZ (SiteAccess context) and LiipImagineBundle are used. - * eZ variations always have precedence. - * - * @param string $variationName - * @param array $configuredVariations Variations set in eZ. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidVariationException - * - * @return array - */ - private function getVariationFilters($variationName, array $configuredVariations) - { - if (!isset($configuredVariations[$variationName]['filters']) && !isset($this->filters[$variationName]['filters'])) { - throw new InvalidVariationException($variationName, 'image'); - } - - // Check variations configured in eZ config first. - if (isset($configuredVariations[$variationName]['filters'])) { - $filters = $configuredVariations[$variationName]['filters']; - } else { - // Falback to variations configured in LiipImagineBundle. - $filters = $this->filters[$variationName]['filters']; - } - - return $filters; - } - - /** - * Returns post processors to be used for $variationName. - * - * Both variations configured in eZ and LiipImagineBundle are used. - * eZ variations always have precedence. - * - * @param string $variationName - * @param array $configuredVariations Variations set in eZ. - * - * @return array - */ - private function getVariationPostProcessors($variationName, array $configuredVariations) - { - if (isset($configuredVariations[$variationName]['post_processors'])) { - return $configuredVariations[$variationName]['post_processors']; - } elseif (isset($this->filters[$variationName]['post_processors'])) { - return $this->filters[$variationName]['post_processors']; - } - - return []; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Gmagick/ReduceNoiseFilter.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Gmagick/ReduceNoiseFilter.php deleted file mode 100644 index a427b93f59..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Gmagick/ReduceNoiseFilter.php +++ /dev/null @@ -1,27 +0,0 @@ -getGmagick(); - $gmagick->reduceNoiseImage((float)$this->getOption('radius', 0)); - - return $image; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Gmagick/SwirlFilter.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Gmagick/SwirlFilter.php deleted file mode 100644 index 610351e653..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Gmagick/SwirlFilter.php +++ /dev/null @@ -1,27 +0,0 @@ -getGmagick(); - $gmagick->swirlimage((float)$this->getOption('degrees', 60)); - - return $image; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Imagick/ReduceNoiseFilter.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Imagick/ReduceNoiseFilter.php deleted file mode 100644 index 7929140afd..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Imagick/ReduceNoiseFilter.php +++ /dev/null @@ -1,27 +0,0 @@ -getImagick(); - $imagick->reduceNoiseImage((float)$this->getOption('radius', 0)); - - return $image; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Imagick/SwirlFilter.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Imagick/SwirlFilter.php deleted file mode 100644 index 6d9d18d97c..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Imagick/SwirlFilter.php +++ /dev/null @@ -1,27 +0,0 @@ -getImagick(); - $imagick->swirlImage((float)$this->getOption('degrees', 60)); - - return $image; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/GrayscaleFilterLoader.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/GrayscaleFilterLoader.php deleted file mode 100644 index 5ff5a78b07..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/GrayscaleFilterLoader.php +++ /dev/null @@ -1,24 +0,0 @@ -effects()->grayscale(); - - return $image; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/SwirlFilterLoader.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/SwirlFilterLoader.php deleted file mode 100644 index 6937f693c4..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/SwirlFilterLoader.php +++ /dev/null @@ -1,31 +0,0 @@ -filter = $filter; - } - - public function load(ImageInterface $image, array $options = []) - { - if (!empty($options)) { - $this->filter->setOption('degrees', $options[0]); - } - - return $this->filter->apply($image); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/IORepositoryResolver.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/IORepositoryResolver.php deleted file mode 100644 index e8d07fd0c7..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/IORepositoryResolver.php +++ /dev/null @@ -1,178 +0,0 @@ -ioService = $ioService; - $this->requestContext = $requestContext; - $this->filterConfiguration = $filterConfiguration; - $this->variationPurger = $variationPurger; - $this->variationPathGenerator = $variationPathGenerator; - } - - public function isStored($path, $filter) - { - return $this->ioService->exists($this->getFilePath($path, $filter)); - } - - public function resolve($path, $filter) - { - try { - $binaryFile = $this->ioService->loadBinaryFile($path); - - // Treat a MissingBinaryFile as a not loadable file. - if ($binaryFile instanceof MissingBinaryFile) { - throw new NotResolvableException("Variation image not found in $path"); - } - - if ($filter !== static::VARIATION_ORIGINAL) { - $variationPath = $this->getFilePath($path, $filter); - $variationBinaryFile = $this->ioService->loadBinaryFile($variationPath); - $path = $variationBinaryFile->uri; - } else { - $path = $binaryFile->uri; - } - - return sprintf( - '%s%s', - $path[0] === '/' ? $this->getBaseUrl() : '', - $path - ); - } catch (NotFoundException $e) { - throw new NotResolvableException("Variation image not found in $path", 0, $e); - } - } - - /** - * Stores image alias in the IO Repository. - * A temporary file is created to dump the filtered image and is used as basis for creation in the IO Repository. - * - * {@inheritdoc} - */ - public function store(BinaryInterface $binary, $path, $filter) - { - $tmpFile = tmpfile(); - fwrite($tmpFile, $binary->getContent()); - $tmpMetadata = stream_get_meta_data($tmpFile); - - $binaryCreateStruct = $this->ioService->newBinaryCreateStructFromLocalFile($tmpMetadata['uri']); - $binaryCreateStruct->id = $this->getFilePath($path, $filter); - $this->ioService->createBinaryFile($binaryCreateStruct); - - fclose($tmpFile); - } - - /** - * @param string[] $paths The paths where the original files are expected to be. - * @param string[] $filters The imagine filters in effect. - */ - public function remove(array $paths, array $filters) - { - if (empty($filters)) { - $filters = array_keys($this->filterConfiguration->all()); - } - - if (empty($paths)) { - $this->variationPurger->purge($filters); - } - - foreach ($paths as $path) { - foreach ($filters as $filter) { - $filteredImagePath = $this->getFilePath($path, $filter); - if (!$this->ioService->exists($filteredImagePath)) { - continue; - } - - $binaryFile = $this->ioService->loadBinaryFile($filteredImagePath); - $this->ioService->deleteBinaryFile($binaryFile); - } - } - } - - /** - * Returns path for filtered image from original path, using the VariationPathGenerator. - * - * @param string $path - * @param string $filter - * - * @return string - */ - public function getFilePath($path, $filter) - { - return $this->variationPathGenerator->getVariationPath($path, $filter); - } - - /** - * Returns base URL, with scheme, host and port, for current request context. - * If no delivery URL is configured for current SiteAccess, will return base URL from current RequestContext. - * - * @return string - */ - protected function getBaseUrl() - { - $port = ''; - if ($this->requestContext->getScheme() === 'https' && $this->requestContext->getHttpsPort() != 443) { - $port = ":{$this->requestContext->getHttpsPort()}"; - } - - if ($this->requestContext->getScheme() === 'http' && $this->requestContext->getHttpPort() != 80) { - $port = ":{$this->requestContext->getHttpPort()}"; - } - - $baseUrl = $this->requestContext->getBaseUrl(); - if (substr($this->requestContext->getBaseUrl(), -4) === '.php') { - $baseUrl = pathinfo($this->requestContext->getBaseurl(), PATHINFO_DIRNAME); - } - $baseUrl = rtrim($baseUrl, '/\\'); - - return sprintf( - '%s://%s%s%s', - $this->requestContext->getScheme(), - $this->requestContext->getHost(), - $port, - $baseUrl - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/ImageAsset/AliasGenerator.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/ImageAsset/AliasGenerator.php deleted file mode 100644 index bc66ea68d4..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/ImageAsset/AliasGenerator.php +++ /dev/null @@ -1,81 +0,0 @@ -innerAliasGenerator = $innerAliasGenerator; - $this->contentService = $contentService; - $this->assetMapper = $assetMapper; - } - - /** - * {@inheritdoc} - */ - public function getVariation(Field $field, VersionInfo $versionInfo, $variationName, array $parameters = []): Variation - { - if ($this->supportsValue($field->value)) { - $destinationContent = $this->contentService->loadContent( - (int)$field->value->destinationContentId - ); - - return $this->innerAliasGenerator->getVariation( - $this->assetMapper->getAssetField($destinationContent), - $destinationContent->versionInfo, - $variationName, - $parameters - ); - } - - return $this->innerAliasGenerator->getVariation($field, $versionInfo, $variationName, $parameters); - } - - /** - * Returns TRUE if the value is supported by alias generator. - * - * @param \eZ\Publish\SPI\FieldType\Value $value - * - * @return bool - */ - public function supportsValue(Value $value): bool - { - return $value instanceof ImageAssetValue; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProvider.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProvider.php deleted file mode 100644 index a6d14862f7..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProvider.php +++ /dev/null @@ -1,22 +0,0 @@ -aliasGenerator = $aliasGenerator; - $this->variationPathGenerator = $variationPathGenerator; - $this->ioService = $ioService; - $this->imagine = $imagine; - } - - /** - * Returns a Variation object, ensuring proper image dimensions. - * - * {@inheritdoc} - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function getVariation( - Field $field, - VersionInfo $versionInfo, - $variationName, - array $parameters = [] - ) { - /** @var \eZ\Publish\SPI\Variation\Values\ImageVariation $variation */ - $variation = $this->aliasGenerator->getVariation( - $field, - $versionInfo, - $variationName, - $parameters - ); - - if (null === $variation->width || null === $variation->height) { - $variationBinaryFile = $this->getVariationBinaryFile($field->value->id, $variationName); - $image = $this->imagine->load($this->ioService->getFileContents($variationBinaryFile)); - $dimensions = $image->getSize(); - - return new ImageVariation( - [ - 'name' => $variation->name, - 'fileName' => $variation->fileName, - 'dirPath' => $variation->dirPath, - 'uri' => $variation->uri, - 'imageId' => $variation->imageId, - 'width' => $dimensions->getWidth(), - 'height' => $dimensions->getHeight(), - 'fileSize' => $variationBinaryFile->size, - 'mimeType' => $this->ioService->getMimeType($variationBinaryFile->id), - 'lastModified' => $variationBinaryFile->mtime, - ] - ); - } - - return $variation; - } - - /** - * Get image variation filesystem path. - * - * @param string $originalPath - * @param string $variationName - * - * @return \eZ\Publish\Core\IO\Values\BinaryFile - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - private function getVariationBinaryFile($originalPath, $variationName) - { - if ($variationName !== IORepositoryResolver::VARIATION_ORIGINAL) { - $variationPath = $this->variationPathGenerator->getVariationPath( - $originalPath, - $variationName - ); - } else { - $variationPath = $originalPath; - } - - return $this->ioService->loadBinaryFile($variationPath); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPathGenerator.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPathGenerator.php deleted file mode 100644 index 64c858c790..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPathGenerator.php +++ /dev/null @@ -1,23 +0,0 @@ - subfolder. - * - * Example: - * my/image/file.jpg -> _aliases/large/my/image/file.jpg - */ -class AliasDirectoryVariationPathGenerator implements VariationPathGenerator -{ - public function getVariationPath($originalPath, $filter) - { - $info = pathinfo($originalPath); - - return sprintf( - '_aliases/%s/%s/%s%s', - $filter, - $info['dirname'], - $info['filename'], - empty($info['extension']) ? '' : '.' . $info['extension'] - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGenerator.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGenerator.php deleted file mode 100644 index 92ee6592bf..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGenerator.php +++ /dev/null @@ -1,31 +0,0 @@ - my/image/file_large.jpg - */ -class OriginalDirectoryVariationPathGenerator implements VariationPathGenerator -{ - public function getVariationPath($originalPath, $filter) - { - $info = pathinfo($originalPath); - - return sprintf( - '%s/%s_%s%s', - $info['dirname'], - $info['filename'], - $filter, - empty($info['extension']) ? '' : '.' . $info['extension'] - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/ImageFileList.php b/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/ImageFileList.php deleted file mode 100644 index 138cf29c73..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/ImageFileList.php +++ /dev/null @@ -1,17 +0,0 @@ -viewMatcherRegistry = $viewMatcherRegistry; - - parent::__construct($repository, $relativeNamespace, $matchConfig); - } - - /** - * @param string $matcherIdentifier - * - * @return \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MatcherInterface - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - protected function getMatcher($matcherIdentifier) - { - if (strpos($matcherIdentifier, '@') === 0) { - return $this->viewMatcherRegistry->getMatcher(substr($matcherIdentifier, 1)); - } - - return parent::getMatcher($matcherIdentifier); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Matcher/ViewMatcherRegistry.php b/eZ/Bundle/EzPublishCoreBundle/Matcher/ViewMatcherRegistry.php deleted file mode 100644 index eceb6a37d0..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Matcher/ViewMatcherRegistry.php +++ /dev/null @@ -1,47 +0,0 @@ -matchers = $matchers; - } - - public function setMatcher(string $matcherIdentifier, ViewMatcherInterface $matcher): void - { - $this->matchers[$matcherIdentifier] = $matcher; - } - - /** - * @param string $matcherIdentifier - * - * @return \eZ\Publish\Core\MVC\Symfony\Matcher\ViewMatcherInterface - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function getMatcher(string $matcherIdentifier): ViewMatcherInterface - { - if (!isset($this->matchers[$matcherIdentifier])) { - throw new NotFoundException('Matcher', $matcherIdentifier); - } - - return $this->matchers[$matcherIdentifier]; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/cache.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/cache.yml deleted file mode 100644 index e82700497f..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/cache.yml +++ /dev/null @@ -1,17 +0,0 @@ -services: - ezpublish.cache_pool: - # As we support custom TagAware services, we set class as interface here so lazy class is "correct" - class: Symfony\Component\Cache\Adapter\TagAwareAdapterInterface - factory: ["@ezpublish.cache_pool.factory", getCachePool] - arguments: ["@ezpublish.config.resolver"] - - ezpublish.cache_pool.factory: - class: eZ\Bundle\EzPublishCoreBundle\ApiLoader\CacheFactory - calls: - - [setContainer, ["@service_container"]] - - eZ\Bundle\EzPublishCoreBundle\Cache\Warmer\ProxyCacheWarmer: - arguments: - - '@eZ\Publish\Core\Repository\ProxyFactory\ProxyGeneratorInterface' - tags: - - { name: kernel.cache_warmer } diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/commands.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/commands.yml deleted file mode 100644 index 165639f868..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/commands.yml +++ /dev/null @@ -1,64 +0,0 @@ -services: - ezpublish.console.command.regenerate_url_aliases: - class: eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand - arguments: - # intentionally passing inner repository to avoid sending Events due to performance issues - - '@ezpublish.api.inner_repository' - - '@?logger' - tags: - - { name: console.command } - - ezplatform.core.command.debug_config_resolver: - class: eZ\Bundle\EzPublishCoreBundle\Command\DebugConfigResolverCommand - arguments: - - "@ezpublish.config.resolver" - - "@ezpublish.siteaccess" - tags: - - { name: console.command } - - ezplatform.core.command.check_urls: - class: eZ\Bundle\EzPublishCoreBundle\Command\CheckURLsCommand - arguments: - - '@eZ\Publish\API\Repository\UserService' - - '@eZ\Publish\API\Repository\PermissionResolver' - - '@eZ\Publish\API\Repository\URLService' - - '@ezpublish.url_checker' - tags: - - { name: console.command } - - eZ\Bundle\EzPublishCoreBundle\Command\UpdateTimestampsToUTCCommand: - arguments: - - '@ezpublish.api.search_engine.legacy.connection' - tags: - - { name: console.command } - - eZ\Bundle\EzPublishCoreBundle\Command\ReindexCommand: - autowire: true - autoconfigure: true - arguments: - $searchIndexer: '@ezpublish.spi.search.indexer' - $locationHandler: '@ezpublish.spi.persistence.location_handler' - $siteaccess: '@ezpublish.siteaccess' - $env: '%kernel.environment%' - $projectDir: '%kernel.project_dir%' - $isDebug: '%kernel.debug%' - tags: - - { name: console.command } - - eZ\Bundle\EzPublishCoreBundle\Command\NormalizeImagesPathsCommand: - autowire: true - autoconfigure: true - arguments: - $connection: '@ezpublish.persistence.connection' - $imageGateway: '@ezpublish.fieldType.ezimage.storage_gateway' - $ioService: '@ezpublish.fieldType.ezimage.io_service' - tags: - - { name: console.command } - - Ibexa\Bundle\Core\Command\ExpireUserPasswordsCommand: - autowire: true - autoconfigure: true - arguments: - $userHandler: '@ezpublish.spi.persistence.user_handler' - tags: - - { name: console.command } diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/default_settings.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/default_settings.yml deleted file mode 100644 index b02c013804..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/default_settings.yml +++ /dev/null @@ -1,257 +0,0 @@ -parameters: - # Kernel related params - webroot_dir: "%kernel.project_dir%/public" - - ### - # ezsettings namespace, default scope - ### - - # Content/Location view - ezsettings.default.location_view: {} - ezsettings.default.content_view: {} - ezsettings.default.block_view: {} - - # Default Twig variables - ezsettings.default.twig_variables: {} - - # Default view templates - ezplatform.default_view_templates.content.full: '@@EzPublishCore/default/content/full.html.twig' - ezplatform.default_view_templates.content.line: '@@EzPublishCore/default/content/line.html.twig' - ezplatform.default_view_templates.content.text_linked: '@@EzPublishCore/default/content/text_linked.html.twig' - ezplatform.default_view_templates.content.embed: '@@EzPublishCore/default/content/embed.html.twig' - ezplatform.default_view_templates.content.embed_inline: '@@EzPublishCore/default/content/embed_inline.html.twig' - ezplatform.default_view_templates.content.embed_image: '@@EzPublishCore/default/content/embed_image.html.twig' - ezplatform.default_view_templates.content.asset_image: '@@EzPublishCore/default/content/asset_image.html.twig' - ezplatform.default_view_templates.block: '@@EzPublishCore/default/block/block.html.twig' - - # Default templates - ezplatform.default_templates.pagelayout: '@@EzPublishCore/pagelayout.html.twig' - ezplatform.default_templates.field_templates: '@@EzPublishCore/content_fields.html.twig' - ezplatform.default_templates.fielddefinition_settings_templates: '@@EzPublishCore/fielddefinition_settings.html.twig' - - # Image Asset mappings - ezsettings.default.fieldtypes.ezimageasset.mappings: - content_type_identifier: image - content_field_identifier: image - name_field_identifier: name - parent_location_id: 51 - - ezsettings.default.pagelayout: '%ezplatform.default_templates.pagelayout%' - ezsettings.default.page_layout: '%ezplatform.default_templates.pagelayout%' - - # List of content type identifiers to display as image when embedded - ezplatform.content_view.image_embed_content_types_identifiers: ['image'] - - ezsettings.default.content_view_defaults: - full: - default: - template: "%ezplatform.default_view_templates.content.full%" - match: [] - line: - default: - template: "%ezplatform.default_view_templates.content.line%" - match: [] - text_linked: - default: - template: "%ezplatform.default_view_templates.content.text_linked%" - match: [] - embed: - image: - template: '%ezplatform.default_view_templates.content.embed_image%' - match: - Identifier\ContentType: '%ezplatform.content_view.image_embed_content_types_identifiers%' - default: - template: "%ezplatform.default_view_templates.content.embed%" - match: [] - embed-inline: - default: - template: "%ezplatform.default_view_templates.content.embed_inline%" - match: [] - asset_image: - default: - template: '%ezplatform.default_view_templates.content.asset_image%' - match: [] - - ezsettings.default.block_view_defaults: - block: - default: - template: "%ezplatform.default_view_templates.block%" - match: [] - - # Common settings - ezpublish.repositories: {} - ezsettings.default.repository: ~ - ezpublish.session_name.default: "eZSESSID{siteaccess_hash}" - ezsettings.default.session_name: "%ezpublish.session_name.default%" # Using "{siteaccess_hash}" in session name makes it unique per siteaccess - ezsettings.default.session: { name: "%ezpublish.session_name.default%" } # Session options that will override options from framework - ezsettings.default.url_alias_router: true # Use UrlAliasRouter by default - ezsettings.default.index_page: ~ # The page to show when accessing IndexPage (/) - ezsettings.default.default_page: ~ # The default page to show, e.g. after user login this will be used for default redirection - ezsettings.default.languages: [] - ezsettings.default.translation_siteaccesses: [] - ezsettings.default.related_siteaccesses: [] - ezsettings.default.cache_service_name: "cache.app" # The cache pool serive name to use for a siteaccess / siteaccess-group - ezsettings.default.var_dir: "var" # The root directory where all log files, cache files and other stored files are created - ezsettings.default.storage_dir: "storage" # Where to place new files for storage, it's relative to var directory - ezsettings.default.binary_dir: "original" - ezsettings.default.anonymous_user_id: 10 # The ID of the user to be used for everyone who is not logged in - ezsettings.default.api_keys: { google_maps: ~ } # Google Maps APIs v3 key (https://developers.google.com/maps/documentation/javascript/get-api-key) - - # IO - ezsettings.default.io.metadata_handler: "default" - ezsettings.default.io.binarydata_handler: "default" - ezsettings.default.io.url_prefix: "$var_dir$/$storage_dir$" - ezsettings.default.io.legacy_url_prefix: "$var_dir$/$storage_dir$" - ezsettings.default.io.root_dir: "%webroot_dir%/$var_dir$/$storage_dir$" - ezsettings.default.io.permissions.files: 0644 - ezsettings.default.io.permissions.directories: 0755 - # Blacklist against storing certain file types, validation will be refused for these - ezsettings.default.io.file_storage.file_type_blacklist: - - php - - php3 - - php4 - - php5 - - phps - - phar - - phpt - - pht - - phtml - - pgif - - hta - - htm - - html - - xhtm - - xhtml - - jar - - js - - jse - - svg - - swf - - # Content settings - ezsettings.default.content.view_cache: true # Whether to use content view cache or not (Etag/Last-Modified based) - ezsettings.default.content.ttl_cache: true # Whether to use TTL Cache for content (i.e. Max-Age response header) - ezsettings.default.content.default_ttl: 60 # Default TTL cache value for content - ezsettings.default.content.tree_root.location_id: 2 # Root locationId for routing and link generation. Useful for multisite apps with one repository. - ezsettings.default.content.tree_root.excluded_uri_prefixes: [] # URI prefixes that are allowed to be outside the content tree - ezsettings.default.content.field_groups.list: ['content', 'metadata'] - ezsettings.default.content.field_groups.default: 'content' - - # URL Wilcards - ezsettings.default.url_wildcards.enabled: '%ezpublish.url_wildcards.enabled%' - - # FieldType settings - - # Cache settings - # Server(s) URL(s) that will be used for purging HTTP cache with BAN requests. - ezsettings.default.http_cache.purge_servers: [] - - # Treemenu settings (admin interface) - ezsettings.default.treemenu.http_cache: true # Whether to use HttpCache or not for admin tree menu - ezsettings.default.treemenu.ttl_cache: 86400 # If HttpCache is used, cache time to live in seconds - - # Templates to use while rendering fields - ezsettings.default.field_templates: - - {template: '%ezplatform.default_templates.field_templates%', priority: 0} - - # Templates for Field edition. Follows the same structure than for field_templates - ezsettings.default.field_edit_templates: [] - - # Templates to use while rendering field definition settings - ezsettings.default.fielddefinition_settings_templates: - - {template: '%ezplatform.default_templates.fielddefinition_settings_templates%', priority: 0} - - # Templates for FieldDefinition edition. Follows the same structure than for field_templates - ezsettings.default.fielddefinition_edit_templates: [] - - # Security settings - ezsettings.default.security.login_template: "@@EzPublishCore/Security/login.html.twig" - ezsettings.default.security.base_layout: "%ezsettings.default.page_layout%" - - # Image settings - ezsettings.default.image.temporary_dir: imagetmp - ezsettings.default.image.published_images_dir: images - ezsettings.default.image.versioned_images_dir: images-versioned - ezsettings.default.image_variations: - reference: - reference: ~ - filters: - geometry/scaledownonly: [600, 600] - small: - reference: reference - filters: - geometry/scaledownonly: [100, 100] - tiny: - reference: reference - filters: - geometry/scaledownonly: [30, 30] - medium: - reference: reference - filters: - geometry/scaledownonly: [200, 200] - large: - reference: reference - filters: - geometry/scaledownonly: [300, 300] - - # ImageMagick - # TODO: Deprecated. Move this to ezpublish_legacy. - ezpublish.image.imagemagick.enabled: false - ezpublish.image.imagemagick.executable_path: - ezpublish.image.imagemagick.executable: convert - ezsettings.default.imagemagick.pre_parameters: - ezsettings.default.imagemagick.post_parameters: - ezpublish.image.imagemagick.filters: - geometry/scale: "-geometry {1}x{2}" - geometry/scalewidth: "-geometry {1}" - geometry/scaleheight: "-geometry x{1}" - geometry/scaledownonly: "-geometry {1}x{2}>" - geometry/scalewidthdownonly: "-geometry {1}>" - geometry/scaleheightdownonly: "-geometry x{1}>" - geometry/scaleexact: "-geometry {1}x{2}!" - geometry/scalepercent: "-geometry {1}x{2}%" - geometry/crop: "-crop {1}x{2}+{3}+{4}" - filter/noise: "-noise {1}" - filter/swirl: "-swirl {1}" - colorspace/gray: "-colorspace GRAY" - colorspace/transparent: "-colorspace Transparent" - colorspace: "-colorspace {1}" - border: "-border {1}x{2}" - border/color: "-bordercolor rgb({1},{2},{3})" - border/width: "-borderwidth {1}" - flatten: "-flatten" - resize: "-resize {1}" - optimize: "-strip" - - ezsettings.default.image_host: '' - - ezsettings.default.url_handler.http.options: - timeout: 10 - connection_timeout: 5 - batch_size: 25 - ignore_certificate: false - ezsettings.default.url_handler.https.options: - timeout: 10 - connection_timeout: 5 - batch_size: 25 - ignore_certificate: false - ezsettings.default.url_handler.mailto.options: {} - - ### - # Internal settings - ### - ezpublish.siteaccess.list: [] - ezpublish.siteaccess.groups: {} - ezpublish.siteaccess.groups_by_siteaccess: {} - ezpublish.siteaccess.default: ~ - # SiteAccesses relation map. 2 dimensions array. - # First dimension is indexed by repository identifier. - # Second dimension is indexed by root location Id. - ezpublish.siteaccess.relation_map: {} - # SiteAccesses, indexed by language. - ezpublish.siteaccesses_by_language: {} - - ## - # Siteaccess aware Entity Manager - ## - ibexa.orm.entity_mappings: [] diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/events.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/events.yml deleted file mode 100644 index f00f34a449..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/events.yml +++ /dev/null @@ -1,8 +0,0 @@ -services: - _defaults: - autowire: true - autoconfigure: true - public: false - - eZ\Publish\Core\Repository\EventSubscriber\: - resource: '../../../eZ/Publish/Core/Repository/EventSubscriber/*' diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/feature_contexts.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/feature_contexts.yml deleted file mode 100644 index 47331ea777..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/feature_contexts.yml +++ /dev/null @@ -1,49 +0,0 @@ -services: - eZ\Bundle\EzPublishCoreBundle\Features\Context\ContentContext: - public: true - arguments: - $repository: '@ezpublish.api.repository' - - eZ\Bundle\EzPublishCoreBundle\Features\Context\ConsoleContext: - public: true - arguments: - $configResolver: '@ezpublish.config.resolver' - $siteaccessList: '%ezpublish.siteaccess.list%' - $defaultSiteaccess: '%ezpublish.siteaccess.default%' - - eZ\Bundle\EzPublishCoreBundle\Features\Context\YamlConfigurationContext: - public: true - arguments: - $kernel: '@kernel' - - eZ\Bundle\EzPublishCoreBundle\Features\Context\QueryControllerContext: ~ - - eZ\Bundle\EzPublishCoreBundle\Features\Context\ContentTypeContext: - public: true - arguments: - $contentTypeService: '@ezpublish.api.service.content_type' - - eZ\Bundle\EzPublishCoreBundle\Features\Context\BasicContentContext: - public: true - arguments: - $repository: '@ezpublish.api.repository' - $contentTypeService: '@ezpublish.api.service.content_type' - $contentService: '@ezpublish.api.service.content' - - eZ\Bundle\EzPublishCoreBundle\Features\Context\FieldTypeContext: - public: true - arguments: - $contentTypeService: '@ezpublish.api.service.content_type' - $contentService: '@ezpublish.api.service.content' - $locationService: '@ezpublish.api.service.location' - - eZ\Bundle\EzPublishCoreBundle\Features\Context\RoleContext: - public: true - arguments: - $roleService: '@ezpublish.api.service.role' - - eZ\Bundle\EzPublishCoreBundle\Features\Context\UserContext: - public: true - arguments: - $userService: '@ezpublish.api.service.user' - $searchService: '@ezpublish.api.service.search' diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/fieldtype_services.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/fieldtype_services.yml deleted file mode 100644 index f3a9b44aef..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/fieldtype_services.yml +++ /dev/null @@ -1,129 +0,0 @@ -services: - # Parameter providers - ezpublish.fieldType.parameterProviderRegistry: - class: eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry - - ezpublish.fieldType.ezdatetime.parameterProvider: - class: eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProvider\LocaleParameterProvider - arguments: ["@ezpublish.locale.converter"] - calls: - - [setRequestStack, ["@request_stack"]] - tags: - - {name: ezplatform.field_type.parameter_provider, alias: ezdatetime} - - {name: ezplatform.field_type.parameter_provider, alias: ezdate} - - {name: ezplatform.field_type.parameter_provider, alias: eztime} - - ezpublish.fieldType.ezobjectrelation.parameterProvider: - class: eZ\Publish\Core\MVC\Symfony\FieldType\Relation\ParameterProvider - arguments: - - "@ezpublish.api.service.content" - tags: - - {name: ezplatform.field_type.parameter_provider, alias: ezobjectrelation} - - ezpublish.fieldType.ezobjectrelationlist.parameterProvider: - class: eZ\Publish\Core\MVC\Symfony\FieldType\RelationList\ParameterProvider - arguments: - - "@ezpublish.api.service.content" - tags: - - {name: ezplatform.field_type.parameter_provider, alias: ezobjectrelationlist} - - ezpublish.fieldType.ezimageasset.parameterProvider: - class: eZ\Publish\Core\MVC\Symfony\FieldType\ImageAsset\ParameterProvider - lazy: true - arguments: - - "@ezpublish.siteaccessaware.repository" - tags: - - {name: ezplatform.field_type.parameter_provider, alias: ezimageasset} - - eZ\Publish\Core\MVC\Symfony\FieldType\User\ParameterProvider: - lazy: true - arguments: - - "@ezpublish.api.service.user" - tags: - - {name: ezplatform.field_type.parameter_provider, alias: ezuser} - - # Image - ezpublish.fieldType.ezimage.io_service: - class: eZ\Publish\Core\FieldType\Image\IO\Legacy - arguments: - - "@ezpublish.fieldType.ezimage.io_service.published" - - "@ezpublish.fieldType.ezimage.io_service.draft" - - "@ezpublish.fieldType.ezimage.io_service.options_provider" - # Required by ezpublish.core.io.stream_file_listener. Request listeners are initialized very early. - lazy: true - - ezpublish.fieldType.ezimage.io_service.options_provider: - class: eZ\Publish\Core\FieldType\Image\IO\OptionsProvider - arguments: - $configResolver: '@ezpublish.config.resolver' - - # Image alias generator - ezpublish.fieldType.ezimage.variation_service: - alias: ezpublish.image_alias.imagine.alias_generator.image_asset - - ezpublish.fieldType.ezimage.io_service.published: - parent: ezpublish.core.io.service - - ezpublish.fieldType.ezimage.io_service.draft: - parent: ezpublish.core.io.service - - ezpublish.fieldType.ezimage.io_service.published.config_scope_change_aware: - class: eZ\Publish\Core\IO\ConfigScopeChangeAwareIOService - decorates: ezpublish.fieldType.ezimage.io_service.published - lazy: true - arguments: - $configResolver: '@ezpublish.config.resolver' - $innerIOService: '@ezpublish.fieldType.ezimage.io_service.published.config_scope_change_aware.inner' - $prefixParameterName: 'image.published_images_dir' - - ezpublish.fieldType.ezimage.io_service.draft.config_scope_change_aware: - class: eZ\Publish\Core\IO\ConfigScopeChangeAwareIOService - decorates: ezpublish.fieldType.ezimage.io_service.draft - autoconfigure: true - lazy: true - arguments: - $configResolver: '@ezpublish.config.resolver' - $innerIOService: '@ezpublish.fieldType.ezimage.io_service.draft.config_scope_change_aware.inner' - $prefixParameterName: 'image.versioned_images_dir' - - ezpublish.fieldType.ezimage.pathGenerator: - class: eZ\Publish\Core\FieldType\Image\PathGenerator\LegacyPathGenerator - - ezpublish.fieldType.validator.black_list: - class: eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator - arguments: - - '@ezpublish.config.resolver' - - ezpublish.fieldType.validator.image: - class: eZ\Publish\Core\FieldType\Validator\ImageValidator - - # BinaryFile - ezpublish.fieldType.ezbinaryfile.io_service: - parent: ezpublish.core.io.service - - ezpublish.fieldType.ezbinaryfile.io_service.config_scope_change_aware: - class: eZ\Publish\Core\IO\ConfigScopeChangeAwareIOService - decorates: ezpublish.fieldType.ezbinaryfile.io_service - lazy: true - arguments: - $configResolver: '@ezpublish.config.resolver' - $innerIOService: '@ezpublish.fieldType.ezbinaryfile.io_service.config_scope_change_aware.inner' - $prefixParameterName: 'binary_dir' - - ezpublish.fieldType.ezbinaryfile.pathGenerator: - class: eZ\Publish\Core\FieldType\BinaryBase\PathGenerator\LegacyPathGenerator - - # Will be added to binaryfile & mediafile external storage handlers by a compiler pass - ezpublish.fieldType.ezbinarybase.download_url_generator: - public: true # @todo should be private - class: eZ\Publish\Core\MVC\Symfony\FieldType\BinaryBase\ContentDownloadUrlGenerator - arguments: ["@router"] - - # Symfony 3.4+ service definitions: - eZ\Publish\Core\FieldType\ImageAsset\AssetMapper: - lazy: true - arguments: - $contentService: '@ezpublish.api.service.content' - $locationService: '@ezpublish.api.service.location' - $contentTypeService: '@ezpublish.api.service.content_type' - $configResolver: '@ezpublish.config.resolver' diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/helpers.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/helpers.yml deleted file mode 100644 index ee6909ca4f..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/helpers.yml +++ /dev/null @@ -1,67 +0,0 @@ -parameters: - # Helpers - ezpublish.config_resolver.resettable_services: [] - ezpublish.config_resolver.updateable_services: [] - -services: - # Helpers - ezpublish.translation_helper: - class: eZ\Publish\Core\Helper\TranslationHelper - arguments: - - "@ezpublish.config.resolver" - - "@ezpublish.api.service.content" - - "%ezpublish.siteaccesses_by_language%" - - "@?logger" - - ezpublish.field_helper: - class: eZ\Publish\Core\Helper\FieldHelper - arguments: ["@ezpublish.translation_helper", "@ezpublish.api.service.content_type", "@ezpublish.api.service.field_type"] - - ezpublish.content_preview_helper: - class: eZ\Publish\Core\Helper\ContentPreviewHelper - arguments: ["@event_dispatcher", "@ezpublish.siteaccess_router"] - calls: - - [setSiteAccess, ["@ezpublish.siteaccess"]] - - ezpublish.config_scope_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\ConfigScopeListener - arguments: - $configResolvers: !tagged ezpublish.config.resolver - $viewManager: '@ezpublish.view_manager' - tags: - - { name: kernel.event_subscriber } - - ezpublish.content_preview.location_provider: - class: eZ\Publish\Core\Helper\PreviewLocationProvider - arguments: - - "@ezpublish.api.service.location" - - "@ezpublish.api.service.content" - - "@ezpublish.spi.persistence.cache.locationHandler" - - ezpublish.content_info_location_loader.main: - class: 'eZ\Publish\Core\Helper\ContentInfoLocationLoader\SudoMainLocationLoader' - arguments: - - "@ezpublish.api.repository" - - eZ\Bundle\EzPublishCoreBundle\SiteAccess\LanguageResolver: - parent: eZ\Publish\Core\Repository\SiteAccessAware\Language\AbstractLanguageResolver - arguments: - $configResolver: '@ezpublish.config.resolver' - - eZ\Publish\API\Repository\LanguageResolver: - alias: eZ\Bundle\EzPublishCoreBundle\SiteAccess\LanguageResolver - - eZ\Publish\Core\IO\IOConfigProvider: - alias: eZ\Bundle\EzPublishCoreBundle\SiteAccess\Config\IOConfigResolver - - eZ\Bundle\EzPublishCoreBundle\SiteAccess\Config\ComplexConfigProcessor: - arguments: - $configResolver: '@ezpublish.config.resolver' - $siteAccessService: '@ezpublish.siteaccess_service' - - eZ\Publish\SPI\SiteAccess\ConfigProcessor: - alias: eZ\Bundle\EzPublishCoreBundle\SiteAccess\Config\ComplexConfigProcessor - - eZ\Bundle\EzPublishCoreBundle\SiteAccess\Config\IOConfigResolver: - arguments: - $complexConfigProcessor: '@eZ\Bundle\EzPublishCoreBundle\SiteAccess\Config\ComplexConfigProcessor' diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/http_kernel.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/http_kernel.yml deleted file mode 100644 index f3e6c091f5..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/http_kernel.yml +++ /dev/null @@ -1,5 +0,0 @@ -services: - ezpublish.http_kernel.reject_explicit_front_controller_requests_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\RejectExplicitFrontControllerRequestsListener - tags: - - { name: kernel.event_subscriber } diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/image.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/image.yml deleted file mode 100644 index 753a868287..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/image.yml +++ /dev/null @@ -1,295 +0,0 @@ -services: - # Filters - ezpublish.image_alias.imagine.filter.unsupported: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\UnsupportedFilter - public: false - - ezpublish.image_alias.imagine.filter.reduce_noise.imagick: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Imagick\ReduceNoiseFilter - public: false - - ezpublish.image_alias.imagine.filter.reduce_noise.gmagick: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Gmagick\ReduceNoiseFilter - public: false - - # Aliasing by default to unsupported filter as it's not supported by GD. - # Alias is changed by Imagine compiler pass to use current driver. - ezpublish.image_alias.imagine.filter.reduce_noise: - alias: ezpublish.image_alias.imagine.filter.unsupported - - ezpublish.image_alias.imagine.filter.swirl.imagick: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Imagick\SwirlFilter - public: false - - ezpublish.image_alias.imagine.filter.swirl.gmagick: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Gmagick\SwirlFilter - public: false - - ezpublish.image_alias.imagine.filter.swirl: - alias: ezpublish.image_alias.imagine.filter.unsupported - - # Filter loaders - ezpublish.image_alias.imagine.binary_loader: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\BinaryLoader - arguments: ["@ezpublish.fieldType.ezimage.io_service", "@mime_types"] - tags: - - { name: liip_imagine.binary.loader, loader: ezpublish } - - ezpublish.image_alias.imagine.cache_resolver: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\IORepositoryResolver - arguments: - - "@ezpublish.fieldType.ezimage.io_service" - - "@router.request_context" - - "@liip_imagine.filter.configuration" - - "@ezpublish.image_alias.variation_purger" - - "@ezpublish.image_alias.variation_path_generator" - tags: - - { name: liip_imagine.cache.resolver, resolver: ezpublish } - - ezpublish.image_alias.imagine.cache_resolver_decorator_factory: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\ResolverFactory - arguments: - - '@ezpublish.config.resolver' - - '@ezpublish.image_alias.imagine.cache_resolver_decorator.inner' - - 'eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\Resolver\ProxyResolver' - - 'eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\Resolver\RelativeResolver' - - ezpublish.image_alias.imagine.cache_resolver_decorator: - class: Liip\ImagineBundle\Imagine\Cache\Resolver\ProxyResolver - factory: ['@ezpublish.image_alias.imagine.cache_resolver_decorator_factory', 'createCacheResolver'] - decorates: ezpublish.image_alias.imagine.cache_resolver - lazy: true - - ezpublish.image_alias.imagine.cache.alias_generator_decorator: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\AliasGeneratorDecorator - lazy: true - arguments: - - '@ezpublish.image_alias.imagine.variation.imagine_alias_generator' - - '@ezpublish.cache_pool' - - '@router.request_context' - - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface' - calls: - - [setSiteAccess, ['@ezpublish.siteaccess']] - - ezpublish.image_alias.imagine.variation.imagine_alias_generator: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Variation\ImagineAwareAliasGenerator - arguments: - - '@ezpublish.image_alias.imagine.alias_generator' - - '@ezpublish.image_alias.variation_path_generator' - - '@ezpublish.fieldType.ezimage.io_service' - - '@liip_imagine' - - ezpublish.image_alias.imagine.alias_generator: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\AliasGenerator - arguments: - - "@ezpublish.image_alias.imagine.binary_loader" - - "@liip_imagine.filter.manager" - - "@ezpublish.image_alias.imagine.cache_resolver" - - "@liip_imagine.filter.configuration" - - "@?logger" - - ezpublish.image_alias.imagine.alias_cleaner: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\AliasCleaner - arguments: ["@ezpublish.image_alias.imagine.cache_resolver"] - - eZ\Publish\Core\FieldType\Image\AliasCleanerInterface: - alias: ezpublish.image_alias.imagine.alias_cleaner - - ezpublish.image_alias.imagine.placeholder_provider.configurator: - class: 'eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderAliasGeneratorConfigurator' - arguments: - - '@ezpublish.config.resolver' - - '@ezpublish.image_alias.imagine.placeholder_provider.registry' - - '%image_alias.placeholder_providers%' - - ezpublish.image_alias.imagine.alias_generator.placeholder: - class: 'eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderAliasGenerator' - decorates: 'ezpublish.image_alias.imagine.alias_generator' - configurator: ['@ezpublish.image_alias.imagine.placeholder_provider.configurator', 'configure'] - arguments: - - '@ezpublish.image_alias.imagine.alias_generator.placeholder.inner' - - '@ezpublish.image_alias.imagine.cache_resolver' - - '@ezpublish.fieldType.ezimage.io_service' - public: false - - ezpublish.image_alias.imagine.alias_generator.image_asset: - class: 'eZ\Bundle\EzPublishCoreBundle\Imagine\ImageAsset\AliasGenerator' - arguments: - - '@ezpublish.image_alias.imagine.cache.alias_generator_decorator' - - '@ezpublish.api.service.content' - - '@eZ\Publish\Core\FieldType\ImageAsset\AssetMapper' - public: false - - ezpublish.image_alias.imagine.placeholder_provider.registry: - class: 'eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProviderRegistry' - - ezpublish.image_alias.placeholder_provider.generic: - class: 'eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider\GenericProvider' - arguments: - - '@liip_imagine' - tags: - - { name: 'ezpublish.placeholder_provider', type: 'generic' } - - ezpublish.image_alias.imagine.placeholder_provider.remote: - class: 'eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider\RemoteProvider' - tags: - - { name: 'ezpublish.placeholder_provider', type: 'remote' } - - ezpublish.image_alias.imagine.filter.loader.scaledown.base: - abstract: true - public: false - calls: - - [setInnerLoader, ["@liip_imagine.filter.loader.thumbnail"]] - - ezpublish.image_alias.imagine.filter.loader.scaledownonly.width_height: - parent: ezpublish.image_alias.imagine.filter.loader.scaledown.base - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleDownOnlyFilterLoader - tags: - - { name: liip_imagine.filter.loader, loader: "geometry/scaledownonly" } - - ezpublish.image_alias.imagine.filter.loader.scaledownonly.width: - parent: ezpublish.image_alias.imagine.filter.loader.scaledown.base - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleWidthDownOnlyFilterLoader - tags: - - { name: liip_imagine.filter.loader, loader: "geometry/scalewidthdownonly" } - - ezpublish.image_alias.imagine.filter.loader.scaledownonly.height: - parent: ezpublish.image_alias.imagine.filter.loader.scaledown.base - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleHeightDownOnlyFilterLoader - tags: - - { name: liip_imagine.filter.loader, loader: "geometry/scaleheightdownonly" } - - ezpublish.image_alias.imagine.filter.loader.relative_scale: - abstract: true - public: false - calls: - - [setInnerLoader, ["@liip_imagine.filter.loader.relative_resize"]] - - ezpublish.image_alias.imagine.filter.loader.scalewidth: - parent: ezpublish.image_alias.imagine.filter.loader.relative_scale - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleWidthFilterLoader - tags: - - { name: liip_imagine.filter.loader, loader: "geometry/scalewidth" } - - ezpublish.image_alias.imagine.filter.loader.scaleheight: - parent: ezpublish.image_alias.imagine.filter.loader.relative_scale - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleHeightFilterLoader - tags: - - { name: liip_imagine.filter.loader, loader: "geometry/scaleheight" } - - ezpublish.image_alias.imagine.filter.loader.scale: - parent: ezpublish.image_alias.imagine.filter.loader.relative_scale - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleFilterLoader - tags: - - { name: liip_imagine.filter.loader, loader: "geometry/scale" } - - ezpublish.image_alias.imagine.filter.loader.scale_exact.base: - abstract: true - public: false - calls: - - [setInnerLoader, ["@liip_imagine.filter.loader.resize"]] - - ezpublish.image_alias.imagine.filter.loader.scale_exact: - parent: ezpublish.image_alias.imagine.filter.loader.scale_exact.base - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleExactFilterLoader - tags: - - { name: liip_imagine.filter.loader, loader: "geometry/scaleexact" } - - ezpublish.image_alias.imagine.filter.loader.scale_percent: - parent: ezpublish.image_alias.imagine.filter.loader.scale_exact.base - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScalePercentFilterLoader - tags: - - { name: liip_imagine.filter.loader, loader: "geometry/scalepercent" } - - ezpublish.image_alias.imagine.filter.loader.crop: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\CropFilterLoader - public: false - calls: - - [setInnerLoader, ["@liip_imagine.filter.loader.crop"]] - tags: - - { name: liip_imagine.filter.loader, loader: "geometry/crop" } - - ezpublish.image_alias.imagine.filter.loader.border: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\BorderFilterLoader - public: false - tags: - - { name: liip_imagine.filter.loader, loader: "border" } - - ezpublish.image_alias.imagine.filter.loader.reduce_noise: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ReduceNoiseFilterLoader - arguments: ["@ezpublish.image_alias.imagine.filter.reduce_noise"] - public: false - tags: - - { name: liip_imagine.filter.loader, loader: "filter/noise" } - - ezpublish.image_alias.imagine.filter.loader.swirl: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\SwirlFilterLoader - arguments: ["@ezpublish.image_alias.imagine.filter.swirl"] - public: false - tags: - - { name: liip_imagine.filter.loader, loader: "filter/swirl" } - - ezpublish.image_alias.imagine.filter.loader.grayscale: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\GrayscaleFilterLoader - public: false - tags: - - { name: liip_imagine.filter.loader, loader: "colorspace/gray" } - - ezpublish.image_alias.variation_purger: - # < platform 2015.05 - # alias: ezpublish.image_alias.variation_purger.legacy_storage_image_file - # >= platform 2015.05 - alias: ezpublish.image_alias.variation_purger.io - - ezpublish.image_alias.variation_purger.io: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\IOVariationPurger - arguments: - - '@ezpublish.fieldType.ezimage.io_service' - - '@ezpublish.cache_pool' - - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface' - - '@ezpublish.image_alias.imagine.cache.alias_generator_decorator' - - '@ezpublish.image_alias.variation_path_generator.alias_directory' - calls: - - [setLogger, ["@?logger"]] - - ezpublish.image_alias.variation_purger.legacy_storage_image_file: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\ImageFileVariationPurger - arguments: - - "@ezpublish.image_alias.variation_purger.legacy_storage_image_file.image_file_list" - - "@ezpublish.fieldType.ezimage.io_service" - - "@ezpublish.image_alias.variation_path_generator.original_directory" - calls: - - [setLogger, ["@?logger"]] - - ezpublish.image_alias.variation_purger.legacy_storage_image_file.image_file_list: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\LegacyStorageImageFileList - arguments: - - "@ezpublish.image_alias.variation_purger.legacy_storage_image_file.image_file_row_reader" - - '@eZ\Publish\Core\IO\IOConfigProvider' - - '@ezpublish.config.resolver' - - ezpublish.image_alias.variation_purger.legacy_storage_image_file.image_file_row_reader: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\LegacyStorageImageFileRowReader - arguments: - $connection: '@ezpublish.persistence.connection' - - ezpublish.image_alias.variation_path_generator: - # < platform 2015.05 - # alias: ezpublish.image_alias.variation_path_generator.original_directory - # >= platform 2015.05 - alias: ezpublish.image_alias.variation_path_generator.alias_directory - - ezpublish.image_alias.variation_path_generator.original_directory: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator\OriginalDirectoryVariationPathGenerator - - ezpublish.image_alias.variation_path_generator.alias_directory: - class: eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator\AliasDirectoryVariationPathGenerator - - Ibexa\Bundle\Core\Imagine\VariationPathGenerator\WebpFormatVariationPathGenerator: - decorates: ezpublish.image_alias.variation_path_generator - arguments: - $innerVariationPathGenerator: '@.inner' - $filterConfiguration: '@liip_imagine.filter.configuration' - - # SPI Aliases - eZ\Publish\SPI\Variation\VariationHandler: '@ezpublish.image_alias.imagine.variation.imagine_alias_generator' diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/locale.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/locale.yml deleted file mode 100644 index 1b04070a27..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/locale.yml +++ /dev/null @@ -1,124 +0,0 @@ -parameters: - ezpublish.locale.conversion_map: - alb-AL: sq_AL - ara-SA: ar_SA - bos-BA: bs_BA - cat-ES: ca_ES - chi-CN: zh_CN - chi-HK: zh_HK - chi-TW: zh_TW - cro-HR: hr_HR - cze-CZ: cs_CZ - dan-DK: da_DK - dut-NL: nl_NL - ell-GR: el_GR - eng-AU: en_AU - eng-CA: en_CA - eng-GB: en_GB - eng-NZ: en_NZ - eng-US: en_US - epo-EO: eo - esl-ES: es_ES - esl-MX: es_MX - fas-IR: fa_IR - fin-FI: fi_FI - fre-BE: fr_BE - fre-CA: fr_CA - fre-CH: fr_CH - fre-FR: fr_FR - ger-CH: de_CH - ger-DE: de_DE - heb-IL: he_IL - hin-IN: hi_IN - hun-HU: hu_HU - ind-ID: id_ID - ita-CH: it_CH - ita-IT: it_IT - jpn-JP: ja_JP - kor-KR: ko_KR - mkd-MK: mk_MK - nno-NO: nn_NO - nor-NO: no_NO - pol-PL: pl_PL - por-BR: pt_BR - por-MZ: pt_MZ - por-PT: pt_PT - rus-RU: ru_RU - ser-SR: sr_RS - slk-SK: sk_SK - slo-SI: sl_SI - sqi-AL: sq_AL - srp-RS: sr_RS - swe-SE: sv_SE - tur-TR: tr_TR - ukr-UA: uk_UA - vie-VN: vi_VN - - ezpublish.locale.browser_map: - au: ['eng-AU'] - be: ['fre-BE'] - br: ['por-BR'] - ca: ['eng-CA'] - cn: ['chi-CN'] - cz: ['cze-CZ'] - de: ['ger-DE'] - dk: ['dan-DK'] - en: ['eng-GB', 'eng-US'] - en_us: ['eng-US'] - es: ['esl-ES'] - fa: ['fas-IR'] - fi: ['fin-FI'] - fr: ['fre-FR'] - gb: ['eng-GB'] - gr: ['ell-GR'] - hk: ['chi-HK'] - hr: ['cro-HR'] - hu: ['hun-HU'] - id: ['ind-ID'] - il: ['heb-IL'] - in: ['hin-IN'] - it: ['ita-IT'] - jp: ['jpn-JP'] - kr: ['kor-KR'] - mx: ['esl-MX'] - mz: ['por-MZ'] - nl: ['dut-NL'] - no: ['nor-NO'] - nz: ['eng-NZ'] - pl: ['pol-PL'] - pt: ['por-PT'] - rs: ['srp-RS'] - ru: ['rus-RU'] - sa: ['ara-SA'] - se: ['swe-SE'] - sk: ['slk-SK'] - tr: ['tur-TR'] - tw: ['chi-TW'] - ua: ['ukr-UA'] - -services: - ezpublish.locale.converter: - class: eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverter - arguments: ["%ezpublish.locale.conversion_map%", "@logger"] - public: true - - eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface: - alias: ezpublish.locale.converter - - # Overriding the original locale listener to trigger the internal locale conversion correctly. - eZ\Bundle\EzPublishCoreBundle\EventListener\LocaleListener: - decorates: locale_listener - arguments: - - '@eZ\Bundle\EzPublishCoreBundle\EventListener\LocaleListener.inner' - - '@ezpublish.config.resolver' - - '@ezpublish.locale.converter' - tags: - - { name: kernel.event_subscriber } - - eZ\Publish\Core\MVC\Symfony\Locale\UserLanguagePreferenceProvider: - autowire: true - arguments: - $languageCodesMap: '%ezpublish.locale.browser_map%' - $localeFallback: '%locale_fallback%' - - eZ\Publish\Core\MVC\Symfony\Locale\UserLanguagePreferenceProviderInterface: '@eZ\Publish\Core\MVC\Symfony\Locale\UserLanguagePreferenceProvider' diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/papi.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/papi.yml deleted file mode 100644 index 8f1e248e3d..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/papi.yml +++ /dev/null @@ -1,75 +0,0 @@ -parameters: - ezpublish.kernel.root_dir: "%kernel.project_dir%/vendor/ezsystems/ezplatform-kernel" - - # API - ezplatform.kernel.proxy_cache_dir: '%kernel.cache_dir%/repository/proxy' - - # Using legacy storage engine for data compatibility with 4.x - ezpublish.api.storage_engine.default: legacy - ezpublish.api.search_engine.default: legacy - -services: - # API - ezpublish.api.repository.factory: - class: eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryFactory - arguments: - - "@ezpublish.config.resolver" - - "%ezpublish.api.inner_repository.class%" - - "%ezpublish.api.role.policy_map%" - - '@eZ\Publish\API\Repository\LanguageResolver' - - "@?logger" - calls: - - [setContainer, ["@service_container"]] - - ezpublish.api.storage_engine.factory: - class: eZ\Bundle\EzPublishCoreBundle\ApiLoader\StorageEngineFactory - arguments: - - "@ezpublish.api.repository_configuration_provider" - - ezpublish.api.persistence_handler: - #To disable cache, switch alias to ezpublish.api.storage_engine - alias: ezpublish.spi.persistence.cache - - ezpublish.api.storage_engine: - class: eZ\Publish\SPI\Persistence\Handler - factory: ["@ezpublish.api.storage_engine.factory", buildStorageEngine] - public: false - - ezpublish.api.search_engine.factory: - class: eZ\Bundle\EzPublishCoreBundle\ApiLoader\SearchEngineFactory - arguments: - - "@ezpublish.api.repository_configuration_provider" - - ezpublish.api.search_engine.indexer.factory: - class: eZ\Bundle\EzPublishCoreBundle\ApiLoader\SearchEngineIndexerFactory - arguments: - - "@ezpublish.api.repository_configuration_provider" - - ezpublish.spi.search: - alias: ezpublish.spi.search_engine - - ezpublish.spi.search.indexer: - alias: ezpublish.spi.search_engine.indexer - - ezpublish.spi.search_engine: - class: eZ\Publish\SPI\Search\VersatileHandler - factory: ["@ezpublish.api.search_engine.factory", buildSearchEngine] - public: false - lazy: true - - ezpublish.spi.search_engine.indexer: - class: eZ\Publish\Core\Search\Common\Indexer - factory: ["@ezpublish.api.search_engine.indexer.factory", buildSearchEngineIndexer] - public: false - - # Redefine background indexer to the one provided here which works on kernel/console.terminate. - ezpublish.search.background_indexer: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\BackgroundIndexingTerminateListener - arguments: - - '@ezpublish.api.persistence_handler' - - '@ezpublish.spi.search' - tags: - - { name: kernel.event_subscriber } - - eZ\Publish\SPI\Search\Content\IndexerGateway: - alias: eZ\Publish\Core\Search\Legacy\Content\IndexerGateway diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/query_types.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/query_types.yml deleted file mode 100644 index 2067b609ba..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/query_types.yml +++ /dev/null @@ -1,36 +0,0 @@ -services: - _defaults: - autowire: true - autoconfigure: true - public: false - - eZ\Publish\Core\QueryType\BuiltIn\AncestorsQueryType: - tags: - - { name: ezplatform.query_type, alias: 'Ancestors' } - - eZ\Publish\Core\QueryType\BuiltIn\ChildrenQueryType: - tags: - - { name: ezplatform.query_type, alias: 'Children' } - - eZ\Publish\Core\QueryType\BuiltIn\SiblingsQueryType: - tags: - - { name: ezplatform.query_type, alias: 'Siblings' } - - eZ\Publish\Core\QueryType\BuiltIn\RelatedToContentQueryType: - tags: - - { name: ezplatform.query_type, alias: 'RelatedTo' } - - eZ\Publish\Core\QueryType\BuiltIn\GeoLocationQueryType: - tags: - - { name: ezplatform.query_type, alias: 'GeoLocation' } - - eZ\Publish\Core\QueryType\BuiltIn\SubtreeQueryType: - tags: - - { name: ezplatform.query_type, alias: 'Subtree' } - - eZ\Publish\Core\QueryType\BuiltIn\SortClausesFactory: - arguments: - $sortClauseArgsParser: '@eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserDispatcher' - - eZ\Publish\Core\QueryType\BuiltIn\SortClausesFactoryInterface: - alias: 'eZ\Publish\Core\QueryType\BuiltIn\SortClausesFactory' diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing.yml deleted file mode 100644 index baadcbe5bc..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing.yml +++ /dev/null @@ -1,163 +0,0 @@ -parameters: - ezpublish.default_router.non_siteaccess_aware_routes: ['_assetic_', '_wdt', '_profiler', '_configurator_', '_ez_user_hash'] - # characters that may require encoding in the urlalias generator - ezpublish.urlalias_generator.charmap: - "\"" : "%22" - "'" : "%27" - "<" : "%3C" - ">" : "%3E" - -services: - ezpublish.chain_router: - class: eZ\Publish\Core\MVC\Symfony\Routing\ChainRouter - arguments: ["@?logger"] - calls: - - [setContext, ["@router.request_context"]] - - ezpublish.siteaccess_match_listener: - class: eZ\Publish\Core\MVC\Symfony\EventListener\SiteAccessMatchListener - arguments: - $siteAccessRouter: '@ezpublish.siteaccess_router' - $eventDispatcher: '@event_dispatcher' - $siteAccessMatcherRegistry: '@eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessMatcherRegistryInterface' - tags: - - { name: kernel.event_subscriber } - - eZ\Publish\Core\MVC\Symfony\Component\Serializer\CompoundMatcherNormalizer: - tags: - - { name: serializer.normalizer } - - ezpublish.url_generator.base: - class: eZ\Publish\Core\MVC\Symfony\Routing\Generator - abstract: true - calls: - - [setRequestContext, ["@router.request_context"]] - - [setSiteAccess, ["@?ezpublish.siteaccess"]] - - [setSiteAccessRouter, ["@ezpublish.siteaccess_router"]] - - [setLogger, ["@?logger"]] - - ezpublish.urlalias_router: - class: eZ\Bundle\EzPublishCoreBundle\Routing\UrlAliasRouter - arguments: - - "@ezpublish.api.service.location" - - "@ezpublish.api.service.url_alias" - - "@ezpublish.api.service.content" - - "@ezpublish.urlalias_generator" - - "@?router.request_context" - - "@?logger" - calls: - - [setConfigResolver, ["@ezpublish.config.resolver"]] - tags: - - {name: router, priority: 200} - - ezpublish.urlalias_generator: - class: eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator - arguments: - - "@ezpublish.api.repository" - - "@router.default" - - "@ezpublish.config.resolver" - - "%ezpublish.urlalias_generator.charmap%" - parent: ezpublish.url_generator.base - - eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessMatcherRegistry: ~ - eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessMatcherRegistryInterface: - alias: 'eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessMatcherRegistry' - - ezpublish.siteaccess.matcher_builder: - class: eZ\Bundle\EzPublishCoreBundle\SiteAccess\MatcherBuilder - arguments: - - '@eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessMatcherRegistry' - - ezpublish.siteaccess.provider.static: - class: eZ\Publish\Core\MVC\Symfony\SiteAccess\Provider\StaticSiteAccessProvider - arguments: - - "%ezpublish.siteaccess.list%" - - "%ezpublish.siteaccess.groups_by_siteaccess%" - tags: - - { name: ezplatform.siteaccess.provider, priority: 10 } - - ezpublish.siteaccess.provider.chain: - class: eZ\Publish\Core\MVC\Symfony\SiteAccess\Provider\ChainSiteAccessProvider - arguments: - $providers: !tagged ezplatform.siteaccess.provider - - ezpublish.siteaccess.provider: - alias: ezpublish.siteaccess.provider.chain - - ezpublish.siteaccess_service: - class: eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessService - arguments: - - "@ezpublish.siteaccess.provider" - - "@ezpublish.config.resolver" - calls: - - [setSiteAccess, ['@ezpublish.siteaccess']] - - ezpublish.siteaccess_router: - class: eZ\Publish\Core\MVC\Symfony\SiteAccess\Router - arguments: - - "@ezpublish.siteaccess.matcher_builder" - - "@logger" - - "%ezpublish.siteaccess.default%" - - "%ezpublish.siteaccess.match_config%" - - "@ezpublish.siteaccess.provider" - - 'eZ\Publish\Core\MVC\Symfony\SiteAccess' - - "%kernel.debug%" - - ezpublish.siteaccess_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\SiteAccessListener - arguments: - - "@ezpublish.siteaccess" - tags: - - { name: kernel.event_subscriber } - - ezpublish.siteaccess_listener.routing: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\RoutingListener - arguments: ["@ezpublish.config.resolver", "@ezpublish.urlalias_router", "@ezpublish.urlalias_generator"] - tags: - - { name: kernel.event_subscriber } - - ezpublish.request_redirect_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\RequestEventListener - arguments: - - "@ezpublish.config.resolver" - - "@router" - - "%ezpublish.siteaccess.default%" - - "@?logger" - tags: - - { name: kernel.event_subscriber } - - ezpublish.request_index_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\IndexRequestListener - arguments: - - "@ezpublish.config.resolver" - tags: - - { name: kernel.event_subscriber } - - ezpublish.route_reference.generator: - class: eZ\Publish\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator - arguments: ["@event_dispatcher"] - calls: - - [setRequestStack, ["@request_stack"]] - - ezpublish.route_reference.listener.language_switch: - class: eZ\Publish\Core\MVC\Symfony\EventListener\LanguageSwitchListener - arguments: ["@ezpublish.translation_helper"] - tags: - - { name: kernel.event_subscriber } - - ezpublish.original_request_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\OriginalRequestListener - tags: - - { name: kernel.event_subscriber } - - ezpublish.preview_request_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\PreviewRequestListener - arguments: ["@request_stack"] - tags: - - { name: kernel.event_subscriber } - - ezpublish.route_reference.listener.content_download: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\ContentDownloadRouteReferenceListener - tags: - - { name: kernel.event_subscriber } - arguments: ["@ezpublish.translation_helper"] diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing/internal.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing/internal.yml deleted file mode 100644 index 80cf11e8db..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing/internal.yml +++ /dev/null @@ -1,43 +0,0 @@ -_ez_content_translation_view: - path: /view/content/{contentId}/{viewType}/{layout}/translation/{languageCode}/{locationId} - defaults: - _controller: ez_content:viewAction - viewType: full - locationId: null - layout: true - options: - expose: true - -_ez_content_view: - path: /view/content/{contentId}/{viewType}/{layout}/{locationId} - defaults: - _controller: ez_content:viewAction - viewType: full - locationId: null - layout: true - options: - expose: true - -_ezpublishPreviewContent: - path: /content/versionview/{contentId}/{versionNo}/{language}/site_access/{siteAccessName} - defaults: { _controller: ezpublish.controller.content.preview:previewContentAction } - methods: [GET] - -_ezpublishPreviewContentDefaultSa: - path: /content/versionview/{contentId}/{versionNo}/{language} - defaults: { _controller: ezpublish.controller.content.preview:previewContentAction } - methods: [GET] - -_ez_user_hash: - path: /_fos_user_context_hash - -ez_content_download: - path: /content/download/{contentId}/{fieldIdentifier}/{filename} - defaults: { _controller: ezpublish.controller.content.download:downloadBinaryFileAction } - -ez_content_download_field_id: - path: /content/download/{contentId}/{fieldId} - defaults: { _controller: ezpublish.controller.content.download:downloadBinaryFileByIdAction } - requirements: - contentId: '\d+' - fieldId: '\d+' diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing/js_routing.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing/js_routing.yml deleted file mode 100644 index 4056252ba9..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/routing/js_routing.yml +++ /dev/null @@ -1,7 +0,0 @@ -services: - ezpublish.js_routing.extractor: - class: eZ\Bundle\EzPublishCoreBundle\Routing\JsRouting\ExposedRoutesExtractor - decorates: 'fos_js_routing.extractor' - arguments: - - '@ezpublish.js_routing.extractor.inner' - - '@request_stack' diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/security.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/security.yml deleted file mode 100644 index 0f706baffe..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/security.yml +++ /dev/null @@ -1,56 +0,0 @@ -parameters: - # Constant authentication execution time in seconds (float). Blocks timing attacks. - # Must be larger than expected real execution time, with a good margin. - # If set to zero, constant time authentication is disabled. Do not do this on production environments. - ibexa.security.authentication.constant_auth_time: !php/const eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\SecurityPass::CONSTANT_AUTH_TIME_DEFAULT - -services: - ezpublish.security.user_provider.username: - class: eZ\Publish\Core\MVC\Symfony\Security\User\UsernameProvider - arguments: - - '@eZ\Publish\API\Repository\UserService' - - '@eZ\Publish\API\Repository\PermissionResolver' - - ezpublish.security.user_provider.email: - class: eZ\Publish\Core\MVC\Symfony\Security\User\EmailProvider - arguments: - - '@eZ\Publish\API\Repository\UserService' - - '@eZ\Publish\API\Repository\PermissionResolver' - - eZ\Publish\Core\MVC\Symfony\Security\UserChecker: - arguments: - - '@eZ\Publish\API\Repository\UserService' - - ezpublish.security.voter.core: - class: eZ\Publish\Core\MVC\Symfony\Security\Authorization\Voter\CoreVoter - arguments: ['@eZ\Publish\API\Repository\PermissionResolver'] - public: false - tags: - - { name: security.voter } - - ezpublish.security.voter.value_object: - class: eZ\Publish\Core\MVC\Symfony\Security\Authorization\Voter\ValueObjectVoter - arguments: ['@eZ\Publish\API\Repository\PermissionResolver'] - public: false - tags: - - { name: security.voter } - - ezpublish.security.controller: - public: true - class: eZ\Publish\Core\MVC\Symfony\Controller\SecurityController - arguments: ["@twig", "@ezpublish.config.resolver", "@security.authentication_utils"] - - ezpublish.security.login_listener: - class: eZ\Publish\Core\MVC\Symfony\Security\EventListener\SecurityListener - arguments: - - '@eZ\Publish\API\Repository\PermissionResolver' - - '@eZ\Publish\API\Repository\UserService' - - "@ezpublish.config.resolver" - - "@event_dispatcher" - - "@security.token_storage" - - "@security.authorization_checker" - - "%fragment.path%" - tags: - - { name: kernel.event_subscriber } - - ezpublish.security.user_provider: '@ezpublish.security.user_provider.username' diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/services.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/services.yml deleted file mode 100644 index 967c64fbb7..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/services.yml +++ /dev/null @@ -1,360 +0,0 @@ -imports: - - { resource: commands.yml } - -parameters: - ezpublish.siteaccess.default.name: default - ezpublish.config.default_scope: ezsettings - - # Param converters - ezpublish.param_converter.content.priority: -2 - ezpublish.param_converter.location.priority: -2 - -services: - # Siteaccess is injected in the container at runtime - ezpublish.siteaccess: - class: eZ\Publish\Core\MVC\Symfony\SiteAccess - arguments: ["%ezpublish.siteaccess.default.name%", 'uninitialized'] - - eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver\DefaultScopeConfigResolver: - arguments: - - '%ezpublish.config.default_scope%' - calls: - - [setContainer, ['@service_container']] - lazy: true - tags: - - { name: ezpublish.config.resolver, priority: 0 } - - eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver\SiteAccessGroupConfigResolver: - arguments: - - '@ezpublish.siteaccess.provider' - - '%ezpublish.config.default_scope%' - - '%ezpublish.siteaccess.groups%' - calls: - - [setSiteAccess, ['@ezpublish.siteaccess']] - - [setContainer, ['@service_container']] - lazy: true - tags: - - { name: ezpublish.config.resolver, priority: 50 } - - eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver\StaticSiteAccessConfigResolver: - arguments: - - '@ezpublish.siteaccess.provider' - - '%ezpublish.config.default_scope%' - calls: - - [setSiteAccess, ['@ezpublish.siteaccess']] - - [setContainer, ['@service_container']] - lazy: true - tags: - - { name: ezpublish.config.resolver, priority: 100 } - - eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver\GlobalScopeConfigResolver: - arguments: - - '%ezpublish.config.default_scope%' - calls: - - [setContainer, ['@service_container']] - lazy: true - tags: - - { name: ezpublish.config.resolver, priority: 255 } - - eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver: - public: true # @todo should be private - - ezpublish.config.resolver: - public: true # @todo should be private - alias: eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver - - ezpublish.config.dynamic_setting.parser: - class: eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\DynamicSettingParser - - ezpublish.config.complex_setting_value.resolver: - class: eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ComplexSettings\ComplexSettingValueResolver - - ezpublish.console_event_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\ConsoleCommandListener - arguments: - - "%ezpublish.siteaccess.default%" - - '@ezpublish.siteaccess.provider' - - "@event_dispatcher" - - "%kernel.debug%" - calls: - - [setSiteAccess, ["@ezpublish.siteaccess"]] - tags: - - { name: kernel.event_subscriber } - - eZ\Bundle\EzPublishCoreBundle\EventListener\BackwardCompatibleCommandListener: - tags: - - { name: kernel.event_subscriber } - - ezpublish.controller.base: - class: eZ\Publish\Core\MVC\Symfony\Controller\Controller - abstract: true - calls: - - [ setContainer, ["@service_container"] ] - - ezpublish.controller.content.view: - class: eZ\Publish\Core\MVC\Symfony\Controller\Content\ViewController - arguments: - - "@ezpublish.view_manager" - - "@security.authorization_checker" - parent: ezpublish.controller.base - tags: - - { name: controller.service_arguments } - - ezpublish.controller.content.preview.core: - class: eZ\Publish\Core\MVC\Symfony\Controller\Content\PreviewController - arguments: - $contentService: '@ezpublish.api.service.content' - $locationService: '@ezpublish.api.service.location' - $kernel: '@http_kernel' - $previewHelper: '@ezpublish.content_preview_helper' - $authorizationChecker: '@security.authorization_checker' - $locationProvider: '@ezpublish.content_preview.location_provider' - $controllerChecker: '@ezpublish.view.custom_location_controller_checker' - tags: - - { name: controller.service_arguments } - - ezpublish.controller.content.preview: - alias: ezpublish.controller.content.preview.core - public: true - - ezpublish.controller.content.download: - class: eZ\Publish\Core\MVC\Symfony\Controller\Content\DownloadController - arguments: - - "@ezpublish.api.service.content" - - "@ezpublish.fieldType.ezbinaryfile.io_service" - - "@ezpublish.translation_helper" - - "@router" - - "@ezpublish.route_reference.generator" - parent: ezpublish.controller.base - tags: - - { name: controller.service_arguments } - - ezpublish.controller.content.download_redirection: - class: eZ\Publish\Core\MVC\Symfony\Controller\Content\DownloadRedirectionController - arguments: - - "@ezpublish.api.service.content" - - "@router" - - "@ezpublish.route_reference.generator" - parent: ezpublish.controller.base - tags: - - { name: controller.service_arguments } - - # This alias allows easier management for subrequests - # {{ render( controller( "ez_content:viewAction", {"contentId": 12, "locationId": 123, "viewType": "line"} ) ) } - ez_content: - public: true - alias: ezpublish.controller.content.view - - ezpublish.view_controller_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\ViewControllerListener - arguments: - - "@controller_resolver" - - "@ezpublish.view_builder.registry" - - "@event_dispatcher" - - "@logger" - tags: - - { name: kernel.event_subscriber } - - ezpublish.fragment_listener.factory: - class: eZ\Bundle\EzPublishCoreBundle\Fragment\FragmentListenerFactory - arguments: ["%fragment.path%"] - calls: - - [setRequestStack, ["@request_stack"]] - - ezpublish.decorated_fragment_renderer: - class: eZ\Bundle\EzPublishCoreBundle\Fragment\DecoratedFragmentRenderer - # Arguments replaced at compile time - arguments: [] - calls: - - [setFragmentPath, ["%fragment.path%"]] - - [setSiteAccess, ["@ezpublish.siteaccess"]] - abstract: true - - eZ\Bundle\EzPublishCoreBundle\Fragment\DirectFragmentRenderer: - arguments: - $kernel: '@kernel' - $controllerListener: '@ezpublish.view_controller_listener' - $controllerResolver: '@controller_resolver' - $argumentMetadataFactory: '@argument_metadata_factory' - $argumentValueResolver: '@argument_resolver.request_attribute' - $viewTemplateRenderer: '@ezpublish.view.template_renderer' - tags: - - { name: kernel.fragment_renderer, alias: !php/const eZ\Bundle\EzPublishCoreBundle\Fragment\DirectFragmentRenderer::NAME } - - ezpublish.param_converter.content: - class: eZ\Bundle\EzPublishCoreBundle\Converter\ContentParamConverter - arguments: - - "@ezpublish.siteaccessaware.service.content" - tags: - - { name: request.param_converter, priority: "%ezpublish.param_converter.content.priority%", converter: ez_content_converter } - - ezpublish.param_converter.location: - class: eZ\Bundle\EzPublishCoreBundle\Converter\LocationParamConverter - arguments: - $locationService: '@ezpublish.siteaccessaware.service.location' - $contentPreviewHelper: '@ezpublish.content_preview_helper' - tags: - - { name: request.param_converter, priority: "%ezpublish.param_converter.location.priority%", converter: ez_location_converter } - - ezpublish.exception_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\ExceptionListener - arguments: ["@translator"] - tags: - - { name: kernel.event_subscriber } - - ezpublish.query_type.registry: - class: eZ\Publish\Core\QueryType\ArrayQueryTypeRegistry - - eZ\Publish\Core\Query\QueryFactory: - arguments: - $queryTypeRegistry: '@ezpublish.query_type.registry' - - eZ\Publish\Core\Query\QueryFactoryInterface: - alias: eZ\Publish\Core\Query\QueryFactory - - eZ\Publish\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactory: - arguments: - $searchService: '@ezpublish.api.service.search' - - eZ\Publish\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface: - alias: eZ\Publish\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactory - - ezpublish.fields_groups.list: '@eZ\Publish\Core\Helper\FieldsGroups\ArrayTranslatorFieldsGroupsList' - - eZ\Publish\Core\Helper\FieldsGroups\ArrayTranslatorFieldsGroupsList: - lazy: eZ\Publish\Core\Helper\FieldsGroups\FieldsGroupsList - factory: [ "@ezpublish.fields_groups.list.repository_settings_factory", "build" ] - arguments: - - "@translator" - - ezpublish.fields_groups.list.repository_settings_factory: - class: eZ\Publish\Core\Helper\FieldsGroups\RepositoryConfigFieldsGroupsListFactory - arguments: - - "@ezpublish.api.repository_configuration_provider" - - ezpublish.query_type_content_view_mapper: - class: eZ\Publish\Core\QueryType\QueryParameterContentViewQueryTypeMapper - arguments: - - "@ezpublish.query_type.registry" - - ezpublish.controller.query: - class: eZ\Publish\Core\MVC\Symfony\Controller\Content\QueryController - arguments: - - "@ezpublish.query_type_content_view_mapper" - - "@ezpublish.api.service.search" - tags: - - { name: controller.service_arguments } - - eZ\Publish\Core\MVC\Symfony\Controller\QueryRenderController: - arguments: - $queryFactory: '@eZ\Publish\Core\Query\QueryFactoryInterface' - $searchHitAdapterFactory: '@eZ\Publish\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface' - tags: - - { name: controller.service_arguments } - - ez_query: - alias: ezpublish.controller.query - public: true - - ez_query_render: - alias: 'eZ\Publish\Core\MVC\Symfony\Controller\QueryRenderController' - public: true - - ezplatform.core.jms_translation.catalog_mapper_file_writer: - class: eZ\Publish\Core\MVC\Symfony\Translation\CatalogueMapperFileWriter - decorates: jms_translation.file_writer - arguments: - - "@ezplatform.core.jms_translation.catalog_mapper_file_writer.inner" - - "@jms_translation.loader_manager" - public: false - - ezplatform.core.translation.extractor.fieldtypes: - class: eZ\Publish\Core\MVC\Symfony\Translation\FieldTypesTranslationExtractor - arguments: - - '@eZ\Publish\Core\FieldType\FieldTypeRegistry' - tags: - - { name: jms_translation.extractor, alias: ez_fieldtypes } - - ezplatform.core.translation.file_visitor.exception_message_template: - parent: jms_translation.extractor.file.default_php_extractor - class: eZ\Publish\Core\MVC\Symfony\Translation\ExceptionMessageTemplateFileVisitor - tags: - - { name: jms_translation.file_visitor, alias: ez_exception_message_template } - - ezplatform.core.translation.file_visitor.translatable_exceptions: - parent: jms_translation.extractor.file.default_php_extractor - class: eZ\Publish\Core\MVC\Symfony\Translation\TranslatableExceptionsFileVisitor - tags: - - { name: jms_translation.file_visitor, alias: ez_translatable_exception } - - ezplatform.core.translation.file_visitor.validation_errors: - parent: jms_translation.extractor.file.default_php_extractor - class: eZ\Publish\Core\MVC\Symfony\Translation\ValidationErrorFileVisitor - tags: - - { name: jms_translation.file_visitor, alias: ez_validation_error } - - ezplatform.core.translation.event_subscriber.crowdin_request_locale: - class: eZ\Bundle\EzPublishCoreBundle\EventSubscriber\CrowdinRequestLocaleSubscriber - tags: - - {name: kernel.event_subscriber} - - ezplatform.core.command.delete_content_translation: - class: eZ\Bundle\EzPublishCoreBundle\Command\DeleteContentTranslationCommand - arguments: - - '@ezpublish.api.repository' - tags: - - { name: console.command } - - ezplatform.core.command.cleanup_versions: - class: eZ\Bundle\EzPublishCoreBundle\Command\CleanupVersionsCommand - arguments: - - '@eZ\Publish\Core\Event\Repository' - - "@ezpublish.api.repository_configuration_provider" - - "@ezpublish.persistence.connection" - tags: - - { name: console.command } - - ezplatform.core.session.handler.native_redis: - class: eZ\Bundle\EzPublishCoreBundle\Session\Handler\NativeSessionHandler - arguments: - - '%session.save_path%' - - 'redis' - - ezplatform.core.command.copy_subtree: - class: eZ\Bundle\EzPublishCoreBundle\Command\CopySubtreeCommand - autowire: true - autoconfigure: true - arguments: - $locationService: '@ezpublish.api.service.location' - $permissionResolver: '@eZ\Publish\API\Repository\PermissionResolver' - $userService: '@ezpublish.api.service.user' - $contentTypeService: '@ezpublish.api.service.content_type' - $searchService: '@ezpublish.api.service.search' - tags: - - { name: console.command } - - ezplatform.core.command.resize_original_images: - class: eZ\Bundle\EzPublishCoreBundle\Command\ResizeOriginalImagesCommand - autowire: true - autoconfigure: true - arguments: - $ioService: '@ezpublish.fieldType.ezimage.io_service.published' - $imagine: '@liip_imagine' - $filterManager: '@liip_imagine.filter.manager' - $permissionResolver: '@eZ\Publish\API\Repository\PermissionResolver' - $userService: '@ezpublish.api.service.user' - $mimeTypes: '@mime_types' - tags: - - { name: console.command } - - ibexa.doctrine.orm.entity_manager: - class: Doctrine\ORM\EntityManager - lazy: true - factory: ['@ibexa.doctrine.orm.entity_manager_factory', 'getEntityManager'] - - ibexa.doctrine.orm.entity_manager_factory: - class: eZ\Bundle\EzPublishCoreBundle\Entity\EntityManagerFactory - arguments: - $repositoryConfigurationProvider: '@ezpublish.api.repository_configuration_provider' - $defaultConnection: '%doctrine.default_connection%' - $entityManagers: '%doctrine.entity_managers%' diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/session.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/session.yml deleted file mode 100644 index 305388939b..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/session.yml +++ /dev/null @@ -1,20 +0,0 @@ -parameters: - ezpublish.session.attribute_bag.storage_key: "_ezpublish" - -services: - ezpublish.session_set_dynamic_name_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\SessionSetDynamicNameListener - arguments: ["@ezpublish.config.resolver", "@session.storage.factory"] - tags: - - { name: kernel.event_subscriber } - - ezpublish.session_init_by_post_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\SessionInitByPostListener - tags: - - { name: kernel.event_subscriber } - - # Override the session attribute bag to set custom storage key so same is used as legacy - # @deprecated To be removed in 7.0 kernel, see 594b083d94f1cff7008fb0d7d54f40d0ce0a2ace - session.attribute_bag: - class: Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag - arguments: ["%ezpublish.session.attribute_bag.storage_key%"] diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/sort_spec.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/sort_spec.yml deleted file mode 100644 index 72447014b7..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/sort_spec.yml +++ /dev/null @@ -1,45 +0,0 @@ -services: - _defaults: - autowire: true - autoconfigure: true - public: false - - eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserDispatcher: - arguments: - $parsers: !tagged_iterator ezplatform.query_type.sort_clause_parser - - eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\CustomFieldSortClauseParser: - tags: - - { name: ezplatform.query_type.sort_clause_parser } - - eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\FieldSortClauseParser: - tags: - - { name: ezplatform.query_type.sort_clause_parser } - - eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\MapDistanceSortClauseParser: - tags: - - { name: ezplatform.query_type.sort_clause_parser } - - eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\RandomSortClauseParser: - tags: - - { name: ezplatform.query_type.sort_clause_parser } - - eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\DefaultSortClauseParser: - arguments: - $valueObjectClassMap: - content_id: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\ContentId - content_name: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\ContentName - content_translated_name: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\ContentTranslatedName - date_modified: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\DateModified - date_published: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\DatePublished - section_identifier: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\SectionIdentifier - section_name: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\SectionName - score: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Score - location_depth: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location\Depth - location_id: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location\Id - location_is_main: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location\IsMainLocation - location_path: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location\Path - location_priority: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location\Priority - location_visibility: \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location\Visibility - tags: - - { name: ezplatform.query_type.sort_clause_parser } diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/storage_engines.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/storage_engines.yml deleted file mode 100644 index 691acbe437..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/storage_engines.yml +++ /dev/null @@ -1,24 +0,0 @@ -services: - ezpublish.api.repository_configuration_provider: - public: true # @todo should be private - class: eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider - arguments: - - "@ezpublish.config.resolver" - - "%ezpublish.repositories%" - - ezpublish.persistence.connection.factory: - class: eZ\Bundle\EzPublishCoreBundle\ApiLoader\StorageConnectionFactory - arguments: - - "@ezpublish.api.repository_configuration_provider" - calls: - - [setContainer, ["@service_container"]] - - ezpublish.persistence.connection: - public: true # @todo should be private - class: Doctrine\DBAL\Connection - factory: ["@ezpublish.persistence.connection.factory", getConnection] - lazy: true - - # Legacy storage engine - ezpublish.api.storage_engine.legacy: - alias: ezpublish.spi.persistence.legacy diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/templating.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/templating.yml deleted file mode 100644 index eb9bc89c50..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/templating.yml +++ /dev/null @@ -1,309 +0,0 @@ -parameters: - # @todo drop once core dependencies stop relying on those parameters - ezpublish.view_provider.configured.class: eZ\Bundle\EzPublishCoreBundle\View\Provider\Configured - - ezpublish.view.matcher_factory.class: eZ\Bundle\EzPublishCoreBundle\Matcher\ServiceAwareMatcherFactory - - ezpublish.content_view.viewbase_layout: "@@EzPublishCore/viewbase_layout.html.twig" - ezpublish.content_view.content_block_name: "content" - - ezpublish.twig.extension.filesize.suffixes: ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB'] - -services: - ezpublish.twig.extension.content: - class: eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\ContentExtension - arguments: - - "@ezpublish.api.repository" - - "@ezpublish.translation_helper" - - "@ezpublish.field_helper" - - "@?logger" - tags: - - {name: twig.extension} - - ezpublish.view_manager: - class: eZ\Bundle\EzPublishCoreBundle\View\Manager - arguments: - - "@twig" - - "@event_dispatcher" - - "@ezpublish.siteaccessaware.repository" - - "@ezpublish.config.resolver" - - "%ezpublish.content_view.viewbase_layout%" - - "@ezpublish.view.configurator" - - "@?logger" - - eZ\Bundle\EzPublishCoreBundle\Matcher\ViewMatcherRegistry: ~ - - ezpublish.content_view_provider.configured: - class: eZ\Bundle\EzPublishCoreBundle\View\Provider\Configured - arguments: ["@ezpublish.content_view.matcher_factory"] - tags: - - {name: ezpublish.view_provider, type: 'eZ\Publish\Core\MVC\Symfony\View\ContentView', priority: 10} - - ezpublish.content_view.matcher_factory: - class: eZ\Bundle\EzPublishCoreBundle\Matcher\ServiceAwareMatcherFactory - arguments: - - '@eZ\Bundle\EzPublishCoreBundle\Matcher\ViewMatcherRegistry' - - '@ezpublish.api.repository' - - 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased' - - ezpublish.content_view.matcher_factory.dynamically_configured: - class: eZ\Publish\Core\MVC\Symfony\Matcher\DynamicallyConfiguredMatcherFactoryDecorator - decorates: ezpublish.content_view.matcher_factory - arguments: - $innerConfigurableMatcherFactory: '@ezpublish.content_view.matcher_factory.dynamically_configured.inner' - $configResolver: '@ezpublish.config.resolver' - $parameterName: content_view - - ezpublish.content_view_provider.default_configured: - class: eZ\Bundle\EzPublishCoreBundle\View\Provider\Configured - arguments: ["@ezpublish.content_view.default_matcher_factory"] - tags: - - {name: ezpublish.view_provider, type: 'eZ\Publish\Core\MVC\Symfony\View\ContentView', priority: -1} - - ezpublish.content_view.default_matcher_factory: - class: eZ\Bundle\EzPublishCoreBundle\Matcher\ServiceAwareMatcherFactory - arguments: - - '@eZ\Bundle\EzPublishCoreBundle\Matcher\ViewMatcherRegistry' - - '@ezpublish.api.repository' - - 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased' - - ezpublish.content_view.default_matcher_factory.dynamically_configured: - class: eZ\Publish\Core\MVC\Symfony\Matcher\DynamicallyConfiguredMatcherFactoryDecorator - decorates: ezpublish.content_view.default_matcher_factory - arguments: - $innerConfigurableMatcherFactory: '@ezpublish.content_view.default_matcher_factory.dynamically_configured.inner' - $configResolver: '@ezpublish.config.resolver' - $parameterName: content_view_defaults - - ezpublish.location_view_provider.configured: - class: eZ\Bundle\EzPublishCoreBundle\View\Provider\Configured - arguments: ["@ezpublish.location_view.matcher_factory"] - tags: - - {name: ezpublish.view_provider, type: 'eZ\Publish\Core\MVC\Symfony\View\ContentView', priority: 10} - - ezpublish.location_view.matcher_factory: - class: eZ\Bundle\EzPublishCoreBundle\Matcher\ServiceAwareMatcherFactory - arguments: - - '@eZ\Bundle\EzPublishCoreBundle\Matcher\ViewMatcherRegistry' - - '@ezpublish.api.repository' - - 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased' - - ezpublish.location_view.matcher_factory.dynamically_configured: - class: eZ\Publish\Core\MVC\Symfony\Matcher\DynamicallyConfiguredMatcherFactoryDecorator - decorates: ezpublish.location_view.matcher_factory - arguments: - $innerConfigurableMatcherFactory: '@ezpublish.location_view.matcher_factory.dynamically_configured.inner' - $configResolver: '@ezpublish.config.resolver' - $parameterName: location_view - - ezpublish.templating.global_helper.core: - class: eZ\Publish\Core\MVC\Symfony\Templating\GlobalHelper - arguments: ["@ezpublish.config.resolver", "@ezpublish.siteaccessaware.service.location", "@router", "@ezpublish.translation_helper"] - calls: - - [setRequestStack, ["@request_stack"]] - - eZ\Publish\Core\MVC\Symfony\Templating\GlobalHelper: - alias: ezpublish.templating.global_helper.core - - ezpublish.templating.global_helper: - alias: ezpublish.templating.global_helper.core - - ezpublish.twig.extension.core: - class: eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\CoreExtension - arguments: ["@ezpublish.templating.global_helper"] - tags: - - {name: twig.extension} - - ezpublish.twig.extension.filesize: - class: eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\FileSizeExtension - arguments: ["@translator", "%ezpublish.twig.extension.filesize.suffixes%", "@ezpublish.config.resolver", "@ezpublish.locale.converter" ] - tags: - - {name: twig.extension} - - ezpublish.templating.extension.routing: - class: eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\RoutingExtension - arguments: ["@ezpublish.route_reference.generator", "@router"] - tags: - - {name: twig.extension} - - eZ\Publish\Core\MVC\Symfony\Templating\Twig\ResourceProvider: - arguments: - $configResolver: '@ezpublish.config.resolver' - - ezpublish.templating.field_block_renderer.twig: - class: eZ\Publish\Core\MVC\Symfony\Templating\Twig\FieldBlockRenderer - arguments: - $twig: '@twig' - $resourceProvider: '@eZ\Publish\Core\MVC\Symfony\Templating\Twig\ResourceProvider' - $baseTemplate: '%ezpublish.content_view.viewbase_layout%' - - ezpublish.templating.field_block_renderer: - alias: ezpublish.templating.field_block_renderer.twig - - ezpublish.twig.extension.field_rendering: - class: eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\FieldRenderingExtension - arguments: - - "@ezpublish.templating.field_block_renderer" - - "@ezpublish.fieldType.parameterProviderRegistry" - - "@ezpublish.translation_helper" - tags: - - { name: twig.extension } - - eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\QueryRenderingExtension: - arguments: - - '@fragment.handler' - tags: - - { name: twig.extension } - - eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\RenderExtension: - arguments: - $renderStrategy: '@eZ\Publish\SPI\MVC\Templating\RenderStrategy' - $eventDispatcher: '@event_dispatcher' - tags: - - { name: twig.extension } - - eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\RenderContentExtension: - arguments: - $renderContentStrategy: '@eZ\Publish\Core\MVC\Symfony\Templating\RenderContentStrategy' - $eventDispatcher: '@event_dispatcher' - tags: - - { name: twig.extension } - - eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\RenderLocationExtension: - arguments: - $renderLocationStrategy: '@eZ\Publish\Core\MVC\Symfony\Templating\RenderLocationStrategy' - $eventDispatcher: '@event_dispatcher' - tags: - - { name: twig.extension } - - eZ\Publish\Core\MVC\Symfony\Templating\RenderStrategy: - arguments: - $strategies: !tagged_iterator ibexa.platform.render.strategy - - eZ\Publish\SPI\MVC\Templating\RenderStrategy: '@eZ\Publish\Core\MVC\Symfony\Templating\RenderStrategy' - - eZ\Publish\Core\MVC\Symfony\Templating\RenderContentStrategy: - arguments: - $fragmentRenderers: !tagged_iterator kernel.fragment_renderer - $defaultRenderer: !php/const eZ\Bundle\EzPublishCoreBundle\Fragment\DirectFragmentRenderer::NAME - $siteAccess: '@ezpublish.siteaccess' - $requestStack: '@request_stack' - tags: - - { name: ibexa.platform.render.strategy } - - eZ\Publish\Core\MVC\Symfony\Templating\RenderLocationStrategy: - arguments: - $fragmentRenderers: !tagged_iterator kernel.fragment_renderer - $defaultRenderer: !php/const eZ\Bundle\EzPublishCoreBundle\Fragment\DirectFragmentRenderer::NAME - $siteAccess: '@ezpublish.siteaccess' - $requestStack: '@request_stack' - tags: - - { name: ibexa.platform.render.strategy } - - ezpublish.twig.extension.image: - class: eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\ImageExtension - arguments: - - '@ezpublish.fieldType.ezimage.variation_service' - - '@eZ\Publish\Core\FieldType\ImageAsset\AssetMapper' - tags: - - { name: twig.extension } - - ezpublish.view.custom_location_controller_checker: - class: eZ\Publish\Core\MVC\Symfony\View\CustomLocationControllerChecker - - ezpublish.view_provider.registry: - class: eZ\Publish\Core\MVC\Symfony\View\Provider\Registry - - ezpublish.view.configurator: - class: eZ\Publish\Core\MVC\Symfony\View\Configurator\ViewProvider - arguments: ["@ezpublish.view_provider.registry"] - - ezpublish.view_builder.registry: - class: eZ\Publish\Core\MVC\Symfony\View\Builder\Registry\ControllerMatch - arguments: - $viewBuilders: !tagged_iterator { tag: ibexa.view_builder } - - ezpublish.view_builder.content: - class: eZ\Publish\Core\MVC\Symfony\View\Builder\ContentViewBuilder - arguments: - - "@ezpublish.siteaccessaware.repository" - - "@ezpublish.view.configurator" - - "@ezpublish.view.view_parameters.injector.dispatcher" - - "@request_stack" - - "@ezpublish.content_info_location_loader.main" - tags: - - { name: ibexa.view_builder } - - ezpublish.view.builder_parameter_collector.request_attributes: - class: eZ\Publish\Core\MVC\Symfony\View\Builder\ParametersFilter\RequestAttributes - tags: - - {name: kernel.event_subscriber} - - ezpublish.view.template_renderer: - class: eZ\Publish\Core\MVC\Symfony\View\Renderer\TemplateRenderer - arguments: ["@twig", "@event_dispatcher"] - - ezpublish.view.renderer_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\ViewRendererListener - arguments: ["@ezpublish.view.template_renderer"] - tags: - - { name: kernel.event_subscriber } - - ezpublish.view.view_parameters.injector.dispatcher: - class: eZ\Publish\Core\MVC\Symfony\View\ParametersInjector\EventDispatcherInjector - arguments: ["@event_dispatcher"] - - ezpublish.view.view_parameters.injector.custom_parameters: - class: eZ\Publish\Core\MVC\Symfony\View\ParametersInjector\CustomParameters - tags: - - { name: kernel.event_subscriber } - - ezpublish.view.view_parameters.injector.embed_object_parameters: - class: eZ\Publish\Core\MVC\Symfony\View\ParametersInjector\EmbedObjectParameters - tags: - - { name: kernel.event_subscriber } - - ezpublish.view.view_parameters.injector.no_layout: - class: eZ\Publish\Core\MVC\Symfony\View\ParametersInjector\NoLayout - tags: - - { name: kernel.event_subscriber } - - ezpublish.view.view_parameters.injector.value_objects_ids: - class: eZ\Publish\Core\MVC\Symfony\View\ParametersInjector\ValueObjectsIds - tags: - - { name: kernel.event_subscriber } - - ezpublish.view.view_parameters.injector.viewbase_layout: - class: eZ\Publish\Core\MVC\Symfony\View\ParametersInjector\ViewbaseLayout - arguments: - - "%ezpublish.content_view.viewbase_layout%" - - "@ezpublish.config.resolver" - tags: - - { name: kernel.event_subscriber } - - ezpublish.view.cache_response_listener: - class: eZ\Bundle\EzPublishCoreBundle\EventListener\CacheViewResponseListener - arguments: - $configResolver: '@ezpublish.config.resolver' - tags: - - { name: kernel.event_subscriber } - - eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\DataAttributesExtension: - autoconfigure: true - public: false - - eZ\Bundle\EzPublishCoreBundle\Templating\Twig\ContextAwareTwigVariablesExtension: - arguments: - $configResolver: "@ezpublish.config.resolver" - tags: - - { name: twig.extension } - - eZ\Publish\Core\MVC\Symfony\EventListener\ContentViewTwigVariablesSubscriber: - autoconfigure: true - autowire: true - - eZ\Publish\Core\MVC\Symfony\View\GenericVariableProviderRegistry: - arguments: - $twigVariableProviders: !tagged ezplatform.view.variable_provider - - eZ\Publish\Core\MVC\Symfony\View\VariableProviderRegistry: '@eZ\Publish\Core\MVC\Symfony\View\GenericVariableProviderRegistry' diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/thumbnails.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/thumbnails.yml deleted file mode 100644 index 481540de75..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/thumbnails.yml +++ /dev/null @@ -1,39 +0,0 @@ -services: - _defaults: - public: false - autoconfigure: true - autowire: true - - eZ\Publish\Core\FieldType\Image\ImageThumbnailStrategy: - arguments: - $variationHandler: '@eZ\Publish\SPI\Variation\VariationHandler' - $variationName: 'medium' - - eZ\Publish\Core\FieldType\Image\ImageThumbnailProxyStrategy: - decorates: eZ\Publish\Core\FieldType\Image\ImageThumbnailStrategy - arguments: - $imageThumbnailStrategy: '@.inner' - $proxyGenerator: '@eZ\Publish\Core\Repository\ProxyFactory\ProxyGeneratorInterface' - - eZ\Publish\Core\FieldType\ImageAsset\ImageAssetThumbnailStrategy: - lazy: true - arguments: - $thumbnailStrategy: '@eZ\Publish\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy' - $contentService: '@ezpublish.api.service.content' - - eZ\Publish\Core\Repository\Strategy\ContentThumbnail\Field\ContentFieldStrategy: - arguments: - $strategies: - ezimage: '@eZ\Publish\Core\FieldType\Image\ImageThumbnailStrategy' - ezimageasset: '@eZ\Publish\Core\FieldType\ImageAsset\ImageAssetThumbnailStrategy' - - eZ\Publish\Core\Repository\Strategy\ContentThumbnail\FirstMatchingFieldStrategy: - arguments: - $fieldTypeService: '@ezpublish.api.service.field_type' - $contentFieldStrategy: '@eZ\Publish\Core\Repository\Strategy\ContentThumbnail\Field\ContentFieldStrategy' - tags: - - { name: ezplatform.spi.content.thumbnail_strategy, priority: 0 } - - eZ\Publish\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy: - arguments: - $strategies: !tagged_iterator ezplatform.spi.content.thumbnail_strategy diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/url_checker.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/url_checker.yml deleted file mode 100644 index e1494b20b4..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/url_checker.yml +++ /dev/null @@ -1,45 +0,0 @@ -services: - ezpublish.url_checker: - class: 'eZ\Bundle\EzPublishCoreBundle\URLChecker\URLChecker' - arguments: - - '@ezpublish.api.service.inner_url' - - '@ezpublish.url_checker.handler_registry' - calls: - - ['setLogger', ['@?logger']] - lazy: true - - ezpublish.url_checker.handler_registry: - class: 'eZ\Bundle\EzPublishCoreBundle\URLChecker\URLHandlerRegistry' - - ezpublish.url_checker.handler.base: - abstract: true - arguments: - - '@ezpublish.api.service.inner_url' - calls: - - ['setLogger', ['@?logger']] - - ezpublish.url_checker.handler.http: - class: 'eZ\Bundle\EzPublishCoreBundle\URLChecker\Handler\HTTPHandler' - parent: ezpublish.url_checker.handler.base - arguments: - $configResolver: '@ezpublish.config.resolver' - $parameterName: url_handler.http.options - tags: - - { name: ezpublish.url_handler, scheme: http } - - ezpublish.url_checker.handler.https: - class: 'eZ\Bundle\EzPublishCoreBundle\URLChecker\Handler\HTTPHandler' - parent: ezpublish.url_checker.handler.base - arguments: - $configResolver: '@ezpublish.config.resolver' - $parameterName: url_handler.https.options - tags: - - { name: ezpublish.url_handler, scheme: https } - - ezpublish.url_checker.handler.mailto: - class: 'eZ\Bundle\EzPublishCoreBundle\URLChecker\Handler\MailToHandler' - parent: ezpublish.url_checker.handler.base - arguments: - $configResolver: '@ezpublish.config.resolver' - tags: - - { name: ezpublish.url_handler, scheme: mailto } diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/url_wildcard.yml b/eZ/Bundle/EzPublishCoreBundle/Resources/config/url_wildcard.yml deleted file mode 100644 index 29ecddeb17..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/url_wildcard.yml +++ /dev/null @@ -1,12 +0,0 @@ -services: - ezpublish.urlwildcard_router: - class: eZ\Publish\Core\MVC\Symfony\Routing\UrlWildcardRouter - public: true - arguments: - - '@ezpublish.api.service.url_wildcard' - - '@ezpublish.urlalias_generator' - - '@router.request_context' - calls: - - [setLogger, ['@?logger']] - tags: - - { name: router, priority: 210 } diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/translations/content_fields.en.xlf b/eZ/Bundle/EzPublishCoreBundle/Resources/translations/content_fields.en.xlf deleted file mode 100644 index 696fb92681..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/translations/content_fields.en.xlf +++ /dev/null @@ -1,21 +0,0 @@ - - - -
- - The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. -
- - - Not set - Not set - key: content-field.latitude.not_set - - - Not set - Not set - key: content-field.longitude.not_set - - -
-
diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/translations/fielddefinition.en.xlf b/eZ/Bundle/EzPublishCoreBundle/Resources/translations/fielddefinition.en.xlf deleted file mode 100644 index e865d83100..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/translations/fielddefinition.en.xlf +++ /dev/null @@ -1,398 +0,0 @@ - - - -
- - The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. -
- - - Any - Any - key: fielddefinition.allowed-content-types.any - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Allowed content types: - Allowed content types: - key: fielddefinition.allowed-content-types.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Default layout: - Default layout: - key: fielddefinition.default-layout.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - None - None - key: fielddefinition.default-layout.none - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Checked - Checked - key: fielddefinition.default-value.checked - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Current date - Current date - key: fielddefinition.default-value.current_date - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Current datetime - Current datetime - key: fielddefinition.default-value.current_datetime - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Current datetime adjusted by - Current datetime adjusted by - key: fielddefinition.default-value.current_datetime_adjust_by - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Current time - Current time - key: fielddefinition.default-value.current_time - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Empty - Empty - key: fielddefinition.default-value.empty - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Default value: - Default value: - key: fielddefinition.default-value.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Unchecked - Unchecked - key: fielddefinition.default-value.unchecked - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - No default value - No default value - key: fielddefinition.default-value.undefined - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - %default% %day% day(s) - %default% %day% day(s) - key: fielddefinition.interval.day - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - %default% %hour% hour(s) - %default% %hour% hour(s) - key: fielddefinition.interval.hour - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - %default% %minute% minute(s) - %default% %minute% minute(s) - key: fielddefinition.interval.minute - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - %default% %month% month(s) - %default% %month% month(s) - key: fielddefinition.interval.month - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - %default% %second% second(s) - %default% %second% second(s) - key: fielddefinition.interval.second - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - %default% %year% year(s) - %default% %year% year(s) - key: fielddefinition.interval.year - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Selected ISBN format: - Selected ISBN format: - key: fielddefinition.isbn.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Max string length: - Max string length: - key: fielddefinition.max-length.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - No defined maximum string length - No defined maximum string length - key: fielddefinition.max-length.undefined - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - %max% characters - %max% characters - key: fielddefinition.max-length.value - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Maximum value: - Maximum value: - key: fielddefinition.max-value.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - No defined maximum value - No defined maximum value - key: fielddefinition.max-value.undefined - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Maximum file size: - Maximum file size: - key: fielddefinition.maximum-file-size.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - No defined maximum size - No defined maximum size - key: fielddefinition.maximum-file-size.undefined - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - %max% MB - %max% MB - key: fielddefinition.maximum-file-size.value - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Flash - Flash - key: fielddefinition.media-player-type.flash - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - HTML5 Audio - HTML5 Audio - key: fielddefinition.media-player-type.html5_audio - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - HTML5 Video - HTML5 Video - key: fielddefinition.media-player-type.html5_video - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Media player type: - Media player type: - key: fielddefinition.media-player-type.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Quicktime - Quicktime - key: fielddefinition.media-player-type.quick_time - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Real Player - Real Player - key: fielddefinition.media-player-type.real_player - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Silverlight - Silverlight - key: fielddefinition.media-player-type.silverlight - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - No defined value - No defined value - key: fielddefinition.media-player-type.undefined - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Window Media Player - Window Media Player - key: fielddefinition.media-player-type.windows_media_player - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Min string length: - Min string length: - key: fielddefinition.min-length.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - No defined minimum string length - No defined minimum string length - key: fielddefinition.min-length.undefined - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - %min% characters - %min% characters - key: fielddefinition.min-length.value - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Minimum value: - Minimum value: - key: fielddefinition.min-value.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - No defined minimum value - No defined minimum value - key: fielddefinition.min-value.undefined - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Allow multiple choices: - Allow multiple choices: - key: fielddefinition.multiple.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Yes - Yes - key: fielddefinition.multiple.yes - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - No - No - key: fielddefinition.multiple.no - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Defined options - Defined options - key: fielddefinition.options.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Preferred number of rows: - Preferred number of rows: - key: fielddefinition.preferred-rows-number.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - No preferred number of rows - No preferred number of rows - key: fielddefinition.preferred-rows-number.undefined - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - %rows% rows - %rows% rows - key: fielddefinition.preferred-rows-number.value - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Browse - Browse - key: fielddefinition.selection-method.browse - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - List with checkboxes - List with checkboxes - key: fielddefinition.selection-method.checkbox - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Selection method: - Selection method: - key: fielddefinition.selection-method.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Drop-down list - Drop-down list - key: fielddefinition.selection-method.list - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Template based, multi - Template based, multi - key: fielddefinition.selection-method.multi_template - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Multiple selection list - Multiple selection list - key: fielddefinition.selection-method.multiple_list - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - List with radio buttons - List with radio buttons - key: fielddefinition.selection-method.radio - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Template based, single - Template based, single - key: fielddefinition.selection-method.single_template - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Drop-down tree - Drop-down tree - key: fielddefinition.selection-method.tree - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Selection root: - Selection root: - key: fielddefinition.selection-root.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - No defined root - No defined root - key: fielddefinition.selection-root.undefined - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Use seconds: - Use seconds: - key: fielddefinition.use-seconds.label - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - Yes - Yes - key: fielddefinition.use-seconds.yes - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - - No - No - key: fielddefinition.use-seconds.no - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig - - -
-
diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/translations/fieldtypes.en.xlf b/eZ/Bundle/EzPublishCoreBundle/Resources/translations/fieldtypes.en.xlf deleted file mode 100644 index 88c3cf9e74..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/translations/fieldtypes.en.xlf +++ /dev/null @@ -1,131 +0,0 @@ - - - -
- - The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. -
- - - Authors - Authors - key: ezauthor.name - - - File - File - key: ezbinaryfile.name - - - Checkbox - Checkbox - key: ezboolean.name - - - Country - Country - key: ezcountry.name - - - Date - Date - key: ezdate.name - - - Date and time - Date and time - key: ezdatetime.name - - - Email address - Email address - key: ezemail.name - - - Float - Float - key: ezfloat.name - - - Map location - Map location - key: ezgmaplocation.name - - - Image - Image - key: ezimage.name - - - Image Asset - Image Asset - key: ezimageasset.name - - - Integer - Integer - key: ezinteger.name - - - ISBN - ISBN - key: ezisbn.name - - - Keywords - Keywords - key: ezkeyword.name - - - Media - Media - key: ezmedia.name - - - Content relation (single) - Content relation (single) - key: ezobjectrelation.name - - - Content relations (multiple) - Content relations (multiple) - key: ezobjectrelationlist.name - - - Selection - Selection - key: ezselection.name - - - Text line - Text line - key: ezstring.name - - - Text block - Text block - key: eztext.name - - - Time - Time - key: eztime.name - - - URL - URL - key: ezurl.name - - - User account - User account - key: ezuser.name - - - XML block - XML block - key: ezxmltext.name - - -
-
diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/translations/messages.en.xlf b/eZ/Bundle/EzPublishCoreBundle/Resources/translations/messages.en.xlf deleted file mode 100644 index cc8211f9c0..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/translations/messages.en.xlf +++ /dev/null @@ -1,42 +0,0 @@ - - - -
- - The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. -
- - - Enter login or email - Enter login or email - key: Enter login or email - eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig - - - Enter password - Enter password - key: Enter password - eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig - - - Login - Login - key: Login - eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig - eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig - - - Password: - Password: - key: Password: - eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig - - - Username: - Username: - key: Username: - eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig - - -
-
diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/translations/repository_exceptions.en.xlf b/eZ/Bundle/EzPublishCoreBundle/Resources/translations/repository_exceptions.en.xlf deleted file mode 100644 index 037d4470c7..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/translations/repository_exceptions.en.xlf +++ /dev/null @@ -1,243 +0,0 @@ - - - -
- - The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message. -
- - - '%actualValue%' is wrong value - '%actualValue%' is wrong value - key: '%actualValue%' is wrong value - eZ/Publish/Core/Base/Exceptions/InvalidArgumentValue.php - - - '%actualValue%' is wrong value in class '%className%' - '%actualValue%' is wrong value in class '%className%' - key: '%actualValue%' is wrong value in class '%className%' - eZ/Publish/Core/Base/Exceptions/InvalidArgumentValue.php - - - A value is set for non translatable field definition '%identifier%' with language '%languageCode%' - A value is set for non translatable field definition '%identifier%' with language '%languageCode%' - key: A value is set for non translatable field definition '%identifier%' with language '%languageCode%' - eZ/Publish/Core/Repository/ContentService.php - eZ/Publish/Core/Repository/ContentService.php - - - Argument '%argumentName%' has a bad state: %whatIsWrong% - Argument '%argumentName%' has a bad state: %whatIsWrong% - key: Argument '%argumentName%' has a bad state: %whatIsWrong% - eZ/Publish/Core/Base/Exceptions/BadStateException.php - - - Argument '%argumentName%' is invalid: %whatIsWrong% - Argument '%argumentName%' is invalid: %whatIsWrong% - key: Argument '%argumentName%' is invalid: %whatIsWrong% - eZ/Publish/Core/Base/Exceptions/InvalidArgumentException.php - - - Argument '%argumentName%' is invalid: expected value to be of type '%expectedType%' - Argument '%argumentName%' is invalid: expected value to be of type '%expectedType%' - key: Argument '%argumentName%' is invalid: expected value to be of type '%expectedType%' - eZ/Publish/Core/Base/Exceptions/InvalidArgumentType.php - - - Argument '%argumentName%' is invalid: expected value to be of type '%expectedType%', got '%actualType%' - Argument '%argumentName%' is invalid: expected value to be of type '%expectedType%', got '%actualType%' - key: Argument '%argumentName%' is invalid: expected value to be of type '%expectedType%', got '%actualType%' - eZ/Publish/Core/Base/Exceptions/InvalidArgumentType.php - - - Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, binary file ids can not begin with a '/' - Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, binary file ids can not begin with a '/' - key: Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, binary file ids can not begin with a '/' - eZ/Publish/Core/IO/Exception/InvalidBinaryAbsolutePathException.php - - - Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, it does not contain prefix '%prefix%'. Is 'var_dir' config correct? - Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, it does not contain prefix '%prefix%'. Is 'var_dir' config correct? - key: Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, it does not contain prefix '%prefix%'. Is 'var_dir' config correct? - eZ/Publish/Core/IO/Exception/InvalidBinaryPrefixException.php - - - Content fields did not validate - Content fields did not validate - key: Content fields did not validate - eZ/Publish/Core/Base/Exceptions/ContentFieldValidationException.php - - - Content type cannot be unlinked from the only remaining group - Content type cannot be unlinked from the only remaining group - key: Content type cannot be unlinked from the only remaining group - eZ/Publish/Core/REST/Server/Controller/ContentType.php - - - Content type is already linked to provided group - Content type is already linked to provided group - key: Content type is already linked to provided group - eZ/Publish/Core/REST/Server/Controller/ContentType.php - - - ContentType FieldDefinitions did not validate - ContentType FieldDefinitions did not validate - key: ContentType FieldDefinitions did not validate - eZ/Publish/Core/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php - - - Could not find %classType% class '%className%' - Could not find %classType% class '%className%' - key: Could not find %classType% class '%className%' - eZ/Publish/Core/Base/Exceptions/MissingClass.php - - - Could not find '%what%' with identifier '%identifier%' - Could not find '%what%' with identifier '%identifier%' - key: Could not find '%what%' with identifier '%identifier%' - eZ/Publish/Core/Base/Exceptions/NotFoundException.php - - - Could not find class '%className%' - Could not find class '%className%' - key: Could not find class '%className%' - eZ/Publish/Core/Base/Exceptions/MissingClass.php - - - Current version is already in status DRAFT - Current version is already in status DRAFT - key: Current version is already in status DRAFT - eZ/Publish/Core/REST/Server/Controller/Content.php - - - Empty content type draft cannot be published - Empty content type draft cannot be published - key: Empty content type draft cannot be published - eZ/Publish/Core/REST/Server/Controller/ContentType.php - - - Field definition '%identifier%' does not exist in given ContentType - Field definition '%identifier%' does not exist in given ContentType - key: Field definition '%identifier%' does not exist in given ContentType - eZ/Publish/Core/Repository/ContentService.php - eZ/Publish/Core/Repository/ContentService.php - - - FieldType '%fieldType%' not found, needs to be implemented or configured to use FieldType\Null\Type - FieldType '%fieldType%' not found, needs to be implemented or configured to use FieldType\Null\Type - key: FieldType '%fieldType%' not found, needs to be implemented or configured to use FieldType\Null\Type - eZ/Publish/Core/Base/Exceptions/NotFound/FieldTypeNotFoundException.php - - - FieldType '%identifier%' is singular and can't be repeated in a ContentType - FieldType '%identifier%' is singular and can't be repeated in a ContentType - key: FieldType '%identifier%' is singular and can't be repeated in a ContentType - - - Limitation '%limitation%' not found, needs to be implemented or configured to use Limitation\BlockingLimitationType - Limitation '%limitation%' not found, needs to be implemented or configured to use Limitation\BlockingLimitationType - key: Limitation '%limitation%' not found, needs to be implemented or configured to use Limitation\BlockingLimitationType - eZ/Publish/Core/Base/Exceptions/NotFound/LimitationNotFoundException.php - - - Limitations did not validate - Limitations did not validate - key: Limitations did not validate - eZ/Publish/Core/Base/Exceptions/LimitationValidationException.php - - - Only empty content type groups can be deleted - Only empty content type groups can be deleted - key: Only empty content type groups can be deleted - eZ/Publish/Core/REST/Server/Controller/ContentType.php - - - Only version in status DRAFT can be published - Only version in status DRAFT can be published - key: Only version in status DRAFT can be published - eZ/Publish/Core/REST/Server/Controller/Content.php - - - Only version in status DRAFT can be updated - Only version in status DRAFT can be updated - key: Only version in status DRAFT can be updated - eZ/Publish/Core/REST/Server/Controller/Content.php - - - Path '%path%' already exists for the given context - Path '%path%' already exists for the given context - key: Path '%path%' already exists for the given context - eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Handler.php - - - Placeholders are not matching with wildcards. - Placeholders are not matching with wildcards. - key: Placeholders are not matching with wildcards. - eZ/Publish/Core/Repository/URLWildcardService.php - - - Provided content type does not contain ezuser field type - Provided content type does not contain ezuser field type - key: Provided content type does not contain ezuser field type - eZ/Publish/Core/Repository/UserService.php - - - Relation is not of type COMMON - Relation is not of type COMMON - key: Relation is not of type COMMON - eZ/Publish/Core/REST/Server/Controller/Content.php - - - Relation of type COMMON can only be added to drafts - Relation of type COMMON can only be added to drafts - key: Relation of type COMMON can only be added to drafts - eZ/Publish/Core/REST/Server/Controller/Content.php - - - Relation of type COMMON can only be removed from drafts - Relation of type COMMON can only be removed from drafts - key: Relation of type COMMON can only be removed from drafts - eZ/Publish/Core/REST/Server/Controller/Content.php - - - Relation of type COMMON to selected destination content ID already exists - Relation of type COMMON to selected destination content ID already exists - key: Relation of type COMMON to selected destination content ID already exists - eZ/Publish/Core/REST/Server/Controller/Content.php - - - User does not have access to '%function%' '%module%' - User does not have access to '%function%' '%module%' - key: User does not have access to '%function%' '%module%' - eZ/Publish/Core/Base/Exceptions/UnauthorizedException.php - - - User does not have access to '%function%' '%module%' with: %with%' - User does not have access to '%function%' '%module%' with: %with%' - key: User does not have access to '%function%' '%module%' with: %with%' - eZ/Publish/Core/Base/Exceptions/UnauthorizedException.php - - - Version in status PUBLISHED cannot be deleted - Version in status PUBLISHED cannot be deleted - key: Version in status PUBLISHED cannot be deleted - eZ/Publish/Core/REST/Server/Controller/Content.php - - - expected value to be of type '%expectedType%' - expected value to be of type '%expectedType%' - key: expected value to be of type '%expectedType%' - - - expected value to be of type '%expectedType%', got '%actualType%' - expected value to be of type '%expectedType%', got '%actualType%' - key: expected value to be of type '%expectedType%', got '%actualType%' - - - An error has occurred. Please try again later or contact your Administrator. - An error has occurred. Please try again later or contact your Administrator. - key: non_verbose_error - - -
-
diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/asset_image.html.twig b/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/asset_image.html.twig deleted file mode 100644 index e55539994b..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/asset_image.html.twig +++ /dev/null @@ -1,8 +0,0 @@ -{% set image_field_identifier = ez_content_field_identifier_image_asset() %} - -{% if image_field_identifier is not null %} - {{ ez_render_field(content, image_field_identifier, { - template: '@EzPublishCore/content_fields.html.twig', - parameters: parameters - }) }} -{% endif %} diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/embed.html.twig b/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/embed.html.twig deleted file mode 100644 index 1b8f01a97c..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/embed.html.twig +++ /dev/null @@ -1,11 +0,0 @@ -{% set content_name=ez_content_name(content) %} - -{% if objectParameters.doNotGenerateEmbedUrl is defined and objectParameters.doNotGenerateEmbedUrl %} -

{{ content.name }}

-{% else %} - {% if location is defined %} -

{{ content_name }}

- {% else %} -

{{ content_name }}

- {% endif %} -{% endif %} diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/embed_image.html.twig b/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/embed_image.html.twig deleted file mode 100644 index 2c0af8d8a1..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/embed_image.html.twig +++ /dev/null @@ -1,16 +0,0 @@ -{% set image_field_identifier = ez_content_field_identifier_first_filled_image(content) %} - -{% if image_field_identifier is not null %} - {{ ez_render_field( - content, - image_field_identifier, - { - template: '@EzPublishCore/content_fields.html.twig', - parameters: - { - alias: objectParameters.size|default('original'), - ezlink: linkParameters|default({}) - } - } - ) }} -{% endif %} diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/embed_inline.html.twig b/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/embed_inline.html.twig deleted file mode 100644 index 1fc0c8bf74..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/embed_inline.html.twig +++ /dev/null @@ -1,15 +0,0 @@ -{% - set data_attributes_str = (data_attributes is defined) - ? ' ' ~ data_attributes|ez_data_attributes_serialize - : '' -%} - -{% if objectParameters.doNotGenerateEmbedUrl is defined and objectParameters.doNotGenerateEmbedUrl %} - {{ content.name }} -{% else %} - {% if location is defined %} - {{ content.name }} - {% else %} - {{ content.name }} - {% endif %} -{% endif %} diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/full.html.twig b/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/full.html.twig deleted file mode 100644 index 5cba95f004..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/full.html.twig +++ /dev/null @@ -1,8 +0,0 @@ -{% extends no_layout == true ? view_base_layout : page_layout %} -{% block content %} -

{{ ez_content_name(content) }}

- {% for field in content.fieldsByLanguage(language|default(null)) %} -

{{ field.fieldDefIdentifier }}

- {{ ez_render_field(content, field.fieldDefIdentifier, {location: location|default(null)}) }} - {% endfor %} -{% endblock %} diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/line.html.twig b/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/line.html.twig deleted file mode 100644 index b91ff3e810..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/line.html.twig +++ /dev/null @@ -1,7 +0,0 @@ -{% set content_name=ez_content_name(content) %} - -{% if location is defined %} -

{{ content_name }}

-{% else %} -

{{ content_name }}

-{% endif %} diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/text_linked.html.twig b/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/text_linked.html.twig deleted file mode 100644 index 6205cc0432..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/content/text_linked.html.twig +++ /dev/null @@ -1,7 +0,0 @@ -{% set content_name=ez_content_name(content) %} - -{% if location is defined %} - {{ content_name }} -{% else %} -

{{ content_name }}

-{% endif %} diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig b/eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig deleted file mode 100644 index 5f9d305b18..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/views/fielddefinition_settings.html.twig +++ /dev/null @@ -1,402 +0,0 @@ -{# Template blocks used to render the settings of each field definition #} -{# Block naming convention is _settings> #} -{# The following variables are available in each block: - # - \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition fielddefinition the field definition - # - array settings settings of the field definition - #} - -{% trans_default_domain "fielddefinition" %} - -{% block ezstring_settings %} -
    - {% set defaultValue = null %} - {% if fielddefinition.defaultValue.text is not same as('') %} - {% set defaultValue = fielddefinition.defaultValue.text %} - {% endif %} - {{ block( 'settings_defaultvalue' ) }} -
  • -
    {{ 'fielddefinition.min-length.label'|trans|desc("Min string length:")}}
    -
    - {% if fielddefinition.validatorConfiguration.StringLengthValidator.minStringLength %} - {{ 'fielddefinition.min-length.value'|trans({'%min%': fielddefinition.validatorConfiguration.StringLengthValidator.minStringLength})|desc("%min% characters")}} - {% else %} - {{ 'fielddefinition.min-length.undefined'|trans|desc("No defined minimum string length")}} - {% endif %} -
    -
  • -
  • -
    {{ 'fielddefinition.max-length.label'|trans|desc("Max string length:")}}
    -
    - {% if fielddefinition.validatorConfiguration.StringLengthValidator.maxStringLength %} - {{ 'fielddefinition.max-length.value'|trans({'%max%': fielddefinition.validatorConfiguration.StringLengthValidator.maxStringLength})|desc("%max% characters")}} - {% else %} - {{ 'fielddefinition.max-length.undefined'|trans|desc("No defined maximum string length")}} - {% endif %} -
    -
  • -
-{% endblock %} - -{% block eztext_settings %} -
    - {% set rows = settings.textRows %} - {{ block( 'settings_preferredrows' ) }} -
-{% endblock %} - -{% block ezcountry_settings %} -
    - {% set defaultValue = null %} - {% if fielddefinition.defaultValue.countries %} - {% for country in fielddefinition.defaultValue.countries %} - {% set defaultValue = defaultValue ~ country.Name ~ ( not loop.last ? ', ' : '' ) %} - {% endfor %} - {% endif %} - {{ block( 'settings_defaultvalue' ) }} - {% set isMultiple = settings.isMultiple %} - {{ block( 'settings_allowmultiple' ) }} -
-{% endblock %} - -{% block ezboolean_settings %} -
    -
  • -
    {{ 'fielddefinition.default-value.label'|trans|desc("Default value:")}}
    -
    - {% if fielddefinition.defaultValue.bool %} - {{ 'fielddefinition.default-value.checked'|trans|desc("Checked")}} - {% else %} - {{ 'fielddefinition.default-value.unchecked'|trans|desc("Unchecked")}} - {% endif %} -
    -
  • -
-{% endblock %} - -{% block ezdatetime_settings %} -
    - {% if settings.defaultType == constant( 'eZ\\Publish\\Core\\FieldType\\DateAndTime\\Type::DEFAULT_EMPTY' ) %} - {% set defaultValue = 'fielddefinition.default-value.empty'|trans|desc("Empty") %} - {% elseif settings.defaultType == constant( 'eZ\\Publish\\Core\\FieldType\\DateAndTime\\Type::DEFAULT_CURRENT_DATE' ) %} - {% set defaultValue = 'fielddefinition.default-value.current_datetime'|trans|desc("Current datetime") %} - {% else %} - {% set interval = settings.dateInterval %} - {% set defaultValue = 'fielddefinition.default-value.current_datetime_adjust_by'|trans|desc("Current datetime adjusted by") %} - {% set defaultValue = interval.y ? 'fielddefinition.interval.year'|trans({'%default%': defaultValue, '%year%': interval.y})|desc("%default% %year% year(s)") : defaultValue %} - {% set defaultValue = interval.m ? 'fielddefinition.interval.month'|trans({'%default%': defaultValue, '%month%': interval.m})|desc("%default% %month% month(s)") : defaultValue %} - {% set defaultValue = interval.d ? 'fielddefinition.interval.day'|trans({'%default%': defaultValue, '%day%': interval.d})|desc("%default% %day% day(s)") : defaultValue %} - {% set defaultValue = interval.h ? 'fielddefinition.interval.hour'|trans({'%default%': defaultValue, '%hour%': interval.h})|desc("%default% %hour% hour(s)") : defaultValue %} - {% set defaultValue = interval.i ? 'fielddefinition.interval.minute'|trans({'%default%': defaultValue, '%minute%': interval.i})|desc("%default% %minute% minute(s)") : defaultValue %} - {% set defaultValue = interval.s and settings.useSeconds ? 'fielddefinition.interval.second'|trans({'%default%': defaultValue, '%second%': interval.s})|desc("%default% %second% second(s)") : defaultValue %} - {% endif %} - {{ block( 'settings_defaultvalue' ) }} -
  • -
    {{ 'fielddefinition.use-seconds.label'|trans|desc("Use seconds:")}}
    -
    {{ settings.useSeconds ? 'fielddefinition.use-seconds.yes'|trans|desc("Yes") : 'fielddefinition.use-seconds.no'|trans|desc("No") }}
    -
  • -
-{% endblock %} - -{% block ezdate_settings %} -
    - {% if settings.defaultType == constant( 'eZ\\Publish\\Core\\FieldType\\Date\\Type::DEFAULT_EMPTY' ) %} - {% set defaultValue = 'fielddefinition.default-value.empty'|trans|desc("Empty") %} - {% else %} - {% set defaultValue = 'fielddefinition.default-value.current_date'|trans|desc("Current date") %} - {% endif %} - {{ block( 'settings_defaultvalue' ) }} -
-{% endblock %} - -{% block eztime_settings %} -
    - {% if settings.defaultType == constant( 'eZ\\Publish\\Core\\FieldType\\Time\\Type::DEFAULT_EMPTY' ) %} - {% set defaultValue = 'fielddefinition.default-value.empty'|trans|desc("Empty") %} - {% else %} - {% set defaultValue = 'fielddefinition.default-value.current_time'|trans|desc("Current time") %} - {% endif %} - {{ block( 'settings_defaultvalue' ) }} -
  • -
    {{ 'fielddefinition.use-seconds.label'|trans|desc("Use seconds:")}}
    -
    {{ settings.useSeconds ? 'fielddefinition.use-seconds.yes'|trans|desc("Yes") : 'fielddefinition.use-seconds.no'|trans|desc("No") }}
    -
  • -
-{% endblock %} - -{% block ezinteger_settings %} -
    - {% set defaultValue = fielddefinition.defaultValue.value %} - {{ block( 'settings_defaultvalue' ) }} - {% set minValue = fielddefinition.validatorConfiguration.IntegerValueValidator.minIntegerValue %} - {{ block( 'settings_minimumvalue' ) }} - {% set maxValue = fielddefinition.validatorConfiguration.IntegerValueValidator.maxIntegerValue %} - {{ block( 'settings_maximumvalue' ) }} -
-{% endblock %} - -{% block ezfloat_settings %} -
    - {% set defaultValue = fielddefinition.defaultValue.value %} - {{ block( 'settings_defaultvalue' ) }} - {% set minValue = fielddefinition.validatorConfiguration.FloatValueValidator.minFloatValue %} - {{ block( 'settings_minimumvalue' ) }} - {% set maxValue = fielddefinition.validatorConfiguration.FloatValueValidator.maxFloatValue %} - {{ block( 'settings_maximumvalue' ) }} -
-{% endblock %} - -{% block ezselection_settings %} -
    -
  • -
    {{ 'fielddefinition.options.label'|trans|desc("Defined options")}}
    -
    -
      - {% for option in settings.options %} -
    • {{ option }}
    • - {% endfor %} -
    -
    -
  • - {% set isMultiple = settings.isMultiple %} - {{ block( 'settings_allowmultiple' ) }} -
-{% endblock %} - - -{% block ezbinaryfile_settings %} -
    - {{ block( 'settings_maxfilesize' ) }} -
-{% endblock %} - -{% block ezmedia_settings %} -{% set type = settings.mediaType %} -
    - {{ block( 'settings_maxfilesize' ) }} -
  • -
    {{ 'fielddefinition.media-player-type.label'|trans|desc("Media player type:")}}
    -
    - {% if type == 'flash' %} - {{ 'fielddefinition.media-player-type.flash'|trans|desc("Flash")}} - {% elseif type == 'quick_time' %} - {{ 'fielddefinition.media-player-type.quick_time'|trans|desc("Quicktime")}} - {% elseif type == 'real_player' %} - {{ 'fielddefinition.media-player-type.real_player'|trans|desc("Real Player")}} - {% elseif type == 'silverlight' %} - {{ 'fielddefinition.media-player-type.silverlight'|trans|desc("Silverlight")}} - {% elseif type == 'windows_media_player' %} - {{ 'fielddefinition.media-player-type.windows_media_player'|trans|desc("Window Media Player")}} - {% elseif type == 'html5_video' %} - {{ 'fielddefinition.media-player-type.html5_video'|trans|desc("HTML5 Video")}} - {% elseif type == 'html5_audio' %} - {{ 'fielddefinition.media-player-type.html5_audio'|trans|desc("HTML5 Audio")}} - {% else %} - {{ 'fielddefinition.media-player-type.undefined'|trans|desc("No defined value")}} - {% endif %} -
    -
  • -
-{% endblock %} - -{% block ezimage_settings %} -
    - {{ block( 'settings_maxfilesize' ) }} -
-{% endblock %} - -{% block ezobjectrelation_settings %} -
    -
  • -
    {{ 'fielddefinition.selection-method.label'|trans|desc("Selection method:")}}
    -
    - {% if settings.selectionMethod == 0 %} - {{ 'fielddefinition.selection-method.browse'|trans|desc("Browse")}} - {% elseif settings.selectionMethod == 1 %} - {{ 'fielddefinition.selection-method.list'|trans|desc("Drop-down list")}} - {% else %} - {{ 'fielddefinition.selection-method.tree'|trans|desc("Drop-down tree")}} - {% endif %} -
    -
  • - {{ block( 'settings_selection_content_types' ) }} - - {% set rootLocationId = settings.selectionRoot %} - {{ block( 'settings_selectionroot' ) }} -
-{% endblock %} - -{% block ezobjectrelationlist_settings %} -
    -
  • -
    {{ 'fielddefinition.selection-method.label'|trans|desc("Selection method:")}}
    -
    - {% if settings.selectionMethod == 0 %} - {{ 'fielddefinition.selection-method.browse'|trans|desc("Browse")}} - {% elseif settings.selectionMethod == 1 %} - {{ 'fielddefinition.selection-method.list'|trans|desc("Drop-down list")}} - {% elseif settings.selectionMethod == 2 %} - {{ 'fielddefinition.selection-method.radio'|trans|desc("List with radio buttons")}} - {% elseif settings.selectionMethod == 3 %} - {{ 'fielddefinition.selection-method.checkbox'|trans|desc("List with checkboxes")}} - {% elseif settings.selectionMethod == 4 %} - {{ 'fielddefinition.selection-method.multiple_list'|trans|desc("Multiple selection list")}} - {% elseif settings.selectionMethod == 5 %} - {{ 'fielddefinition.selection-method.multi_template'|trans|desc("Template based, multi")}} - {% else %} - {{ 'fielddefinition.selection-method.single_template'|trans|desc("Template based, single")}} - {% endif %} -
    -
  • - - {{ block( 'settings_selection_content_types' ) }} - - {% set rootLocationId = settings.selectionDefaultLocation %} - {{ block( 'settings_selectionroot' ) }} -
-{% endblock %} - -{% block ezauthor_settings %} -
    - {% if settings.defaultType == constant( 'eZ\\Publish\\Core\\FieldType\\Author\\Type::DEFAULT_EMPTY' ) %} - {% set defaultValue = 'fielddefinition.default-value.empty'|trans|desc("Empty") %} - {% else %} - {% set defaultValue = 'fielddefinition.default-value.current_author'|trans|desc("Current User") %} - {% endif %} - {{ block( 'settings_defaultvalue' ) }} -
-{% endblock %} - -{% block ezurl_settings %}{% endblock %} - -{% block ezisbn_settings %} -
    - {% set defaultValue = null %} - {% if fielddefinition.defaultValue.isbn %} - {% set defaultValue = fielddefinition.defaultValue.isbn %} - {% endif %} - {{ block( 'settings_defaultvalue' ) }} - {% set isISBN13 = settings.isISBN13 %} - {{ block( 'settings_allowisbn13' ) }} -
-{% endblock %} - -{% block ezkeyword_settings %}{% endblock %} - -{% block ezuser_settings %}{% endblock %} - -{% block ezemail_settings %}{% endblock %} - -{% block ezgmaplocation_settings %}{% endblock %} - -{% block settings_maxfilesize %} -
  • -
    {{ 'fielddefinition.maximum-file-size.label'|trans|desc("Maximum file size:")}}
    -
    - {% if fielddefinition.validatorConfiguration.FileSizeValidator.maxFileSize %} - {{ 'fielddefinition.maximum-file-size.value'|trans({'%max%': fielddefinition.validatorConfiguration.FileSizeValidator.maxFileSize})|desc("%max% MB")}} - {% else %} - {{ 'fielddefinition.maximum-file-size.undefined'|trans|desc("No defined maximum size")}} - {% endif %} -
    -
  • -{% endblock %} - -{% block settings_preferredrows %} -
  • -
    {{ 'fielddefinition.preferred-rows-number.label'|trans|desc("Preferred number of rows:")}}
    -
    - {% if rows %} - {{ 'fielddefinition.preferred-rows-number.value'|trans({'%rows%': rows})|desc("%rows% rows")}} - {% else %} - {{ 'fielddefinition.preferred-rows-number.undefined'|trans|desc("No preferred number of rows")}} - {% endif %} -
    -
  • -{% endblock %} - -{% block settings_selectionroot %} -
  • -
    {{ 'fielddefinition.selection-root.label'|trans|desc("Selection root:")}}
    -
    - {% if rootLocationId %} - {{ render( controller( "ez_content:viewAction", {'locationId': rootLocationId, 'viewType': 'line', 'layout': false} ), {'strategy': 'esi'}) }} - {% else %} - {{ 'fielddefinition.selection-root.undefined'|trans|desc("No defined root")}} - {% endif %} -
    -
  • -{% endblock %} - -{% block settings_selection_content_types %} -
  • -
    {{ 'fielddefinition.allowed-content-types.label'|trans|desc("Allowed content types:")}}
    -
    - {% if settings.selectionContentTypes %} - {# TODO display content type name #} -
      - {% for typeIdentifier in settings.selectionContentTypes %} -
    • {{ typeIdentifier }}
    • - {% endfor %} -
    - {% else %} - {{ 'fielddefinition.allowed-content-types.any'|trans|desc("Any")}} - {% endif %} -
    -
  • -{% endblock %} - -{% block settings_defaultvalue %} -
  • -
    {{ 'fielddefinition.default-value.label'|trans|desc("Default value:")}}
    -
    - {% if defaultValue is not null %} - {{ defaultValue }} - {% else %} - {{ 'fielddefinition.default-value.undefined'|trans|desc("No default value")}} - {% endif %} -
    -
  • -{% endblock %} - -{% block settings_minimumvalue %} -
  • -
    {{ 'fielddefinition.min-value.label'|trans|desc("Minimum value:")}}
    -
    - {% if minValue %} - {{ minValue }} - {% else %} - {{ 'fielddefinition.min-value.undefined'|trans|desc("No defined minimum value")}} - {% endif %} -
    -
  • -{% endblock %} - -{% block settings_maximumvalue %} -
  • -
    {{ 'fielddefinition.max-value.label'|trans|desc("Maximum value:")}}
    -
    - {% if maxValue %} - {{ maxValue }} - {% else %} - {{ 'fielddefinition.max-value.undefined'|trans|desc("No defined maximum value")}} - {% endif %} -
    -
  • -{% endblock %} - -{% block settings_allowmultiple %} -
  • -
    {{ 'fielddefinition.multiple.label'|trans|desc("Allow multiple choices:")}}
    -
    {{ isMultiple ? 'fielddefinition.multiple.yes'|trans|desc("Yes") : 'fielddefinition.multiple.no'|trans|desc("No") }}
    -
  • -{% endblock %} - -{% block settings_allowisbn13 %} -
  • -
    {{ 'fielddefinition.isbn.label'|trans|desc("Selected ISBN format:")}}
    -
    {{ isISBN13 ? 'ISBN-13' : 'ISBN-10' }}
    -
  • -{% endblock %} - -{% block ezimageasset_settings %} -{% endblock %} diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/pagelayout.html.twig b/eZ/Bundle/EzPublishCoreBundle/Resources/views/pagelayout.html.twig deleted file mode 100644 index ce52236e85..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/views/pagelayout.html.twig +++ /dev/null @@ -1,19 +0,0 @@ - - - - - {% if content is defined and title is not defined %} - {% set title = ez_content_name( content ) %} - {% endif %} - {{ title|default( 'Home' ) }} - - {% if content is defined and location is defined and location is not null and location.id is not null %} - - {% elseif content is defined and content.contentInfo.mainLocationId %} - - {% endif %} - - -{% block content %}{% endblock %} - - diff --git a/eZ/Bundle/EzPublishCoreBundle/Routing/UrlAliasRouter.php b/eZ/Bundle/EzPublishCoreBundle/Routing/UrlAliasRouter.php deleted file mode 100644 index 519a72dfb8..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Routing/UrlAliasRouter.php +++ /dev/null @@ -1,61 +0,0 @@ -configResolver = $configResolver; - } - - public function matchRequest(Request $request) - { - // UrlAliasRouter might be disabled from configuration. - // An example is for running the admin interface: it needs to be entirely run through the legacy kernel. - if ($this->configResolver->getParameter('url_alias_router') === false) { - throw new ResourceNotFoundException('Config requires bypassing UrlAliasRouter'); - } - - return parent::matchRequest($request); - } - - /** - * Will return the right UrlAlias in regards to configured root location. - * - * @param string $pathinfo - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the path does not exist or is not valid for the given language - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - protected function getUrlAlias($pathinfo) - { - $pathPrefix = $this->generator->getPathPrefixByRootLocationId($this->rootLocationId); - - if ( - $this->rootLocationId === null || - $this->generator->isUriPrefixExcluded($pathinfo) || - $pathPrefix === '/' - ) { - return parent::getUrlAlias($pathinfo); - } - - return $this->urlAliasService->lookup($pathPrefix . $pathinfo); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/Config/IOConfigResolver.php b/eZ/Bundle/EzPublishCoreBundle/SiteAccess/Config/IOConfigResolver.php deleted file mode 100644 index 9b2440bdf1..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/Config/IOConfigResolver.php +++ /dev/null @@ -1,41 +0,0 @@ -complexConfigProcessor = $complexConfigProcessor; - } - - public function getRootDir(): string - { - return $this->complexConfigProcessor->processComplexSetting('io.root_dir'); - } - - public function getLegacyUrlPrefix(): string - { - return $this->complexConfigProcessor->processComplexSetting('io.legacy_url_prefix'); - } - - public function getUrlPrefix(): string - { - return $this->complexConfigProcessor->processComplexSetting('io.url_prefix'); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/LanguageResolver.php b/eZ/Bundle/EzPublishCoreBundle/SiteAccess/LanguageResolver.php deleted file mode 100644 index a65149b370..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/LanguageResolver.php +++ /dev/null @@ -1,38 +0,0 @@ -configResolver = $configResolver; - parent::__construct($defaultUseAlwaysAvailable, $defaultShowAllTranslations); - } - - /** - * Get list of languages configured via scope/SiteAccess context. - * - * @return string[] - */ - protected function getConfiguredLanguages(): array - { - return $this->configResolver->getParameter('languages'); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/Matcher.php b/eZ/Bundle/EzPublishCoreBundle/SiteAccess/Matcher.php deleted file mode 100644 index 566f7292d4..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/Matcher.php +++ /dev/null @@ -1,22 +0,0 @@ -siteAccessMatcherRegistry = $siteAccessMatcherRegistry; - } - - /** - * Builds siteaccess matcher. - * If $matchingClass begins with "@", it will be considered as a service identifier. - * - * @param $matchingClass - * @param $matchingConfiguration - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request - * - * @return \eZ\Bundle\EzPublishCoreBundle\SiteAccess\Matcher - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function buildMatcher($matchingClass, $matchingConfiguration, SimplifiedRequest $request) - { - if (strpos($matchingClass, '@') === 0) { - $matcher = $this->siteAccessMatcherRegistry->getMatcher(substr($matchingClass, 1)); - - $matcher->setMatchingConfiguration($matchingConfiguration); - $matcher->setRequest($request); - - return $matcher; - } - - return parent::buildMatcher($matchingClass, $matchingConfiguration, $request); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/SiteAccessMatcherRegistry.php b/eZ/Bundle/EzPublishCoreBundle/SiteAccess/SiteAccessMatcherRegistry.php deleted file mode 100644 index 649cb7521b..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/SiteAccessMatcherRegistry.php +++ /dev/null @@ -1,47 +0,0 @@ -matchers = $matchers; - } - - public function setMatcher(string $identifier, Matcher $matcher): void - { - $this->matchers[$identifier] = $matcher; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function getMatcher(string $identifier): Matcher - { - if (!$this->hasMatcher($identifier)) { - throw new NotFoundException('SiteAccess Matcher', $identifier); - } - - return $this->matchers[$identifier]; - } - - public function hasMatcher(string $identifier): bool - { - return isset($this->matchers[$identifier]); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/SiteAccessMatcherRegistryInterface.php b/eZ/Bundle/EzPublishCoreBundle/SiteAccess/SiteAccessMatcherRegistryInterface.php deleted file mode 100644 index 6eaa3b4bf9..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/SiteAccessMatcherRegistryInterface.php +++ /dev/null @@ -1,26 +0,0 @@ -configResolver = $configResolver; - } - - public function getGlobals(): array - { - return $this->configResolver->getParameter('twig_variables'); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Cache/Warmer/ProxyCacheWarmerTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Cache/Warmer/ProxyCacheWarmerTest.php deleted file mode 100644 index 4e2c3aa322..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Cache/Warmer/ProxyCacheWarmerTest.php +++ /dev/null @@ -1,43 +0,0 @@ -proxyGenerator = $this->createMock(ProxyGeneratorInterface::class); - $this->proxyCacheWarmer = new ProxyCacheWarmer($this->proxyGenerator); - } - - public function testIsOptional(): void - { - $this->assertFalse($this->proxyCacheWarmer->isOptional()); - } - - public function testWarmUp(): void - { - $this->proxyGenerator - ->expects($this->once()) - ->method('warmUp') - ->with(ProxyCacheWarmer::PROXY_CLASSES); - - $this->proxyCacheWarmer->warmUp('/cache/dir'); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/ChainConfigResolverTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/ChainConfigResolverTest.php deleted file mode 100644 index b8fbd361e1..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/ChainConfigResolverTest.php +++ /dev/null @@ -1,286 +0,0 @@ -chainResolver = new ChainConfigResolver(); - } - - /** - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::addResolver - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::sortResolvers - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::getAllResolvers - */ - public function testPriority() - { - $this->assertEquals([], $this->chainResolver->getAllResolvers()); - - list($low, $high) = $this->createResolverMocks(); - - $this->chainResolver->addResolver($low, 10); - $this->chainResolver->addResolver($high, 100); - - $this->assertEquals( - [ - $high, - $low, - ], - $this->chainResolver->getAllResolvers() - ); - } - - /** - * Resolvers are supposed to be sorted only once. - * This test will check that by trying to get all resolvers several times. - * - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::addResolver - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::sortResolvers - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::getAllResolvers - */ - public function testSortResolvers() - { - list($low, $medium, $high) = $this->createResolverMocks(); - // We're using a mock here and not $this->chainResolver because we need to ensure that the sorting operation is done only once. - $resolver = $this->buildMock( - ChainConfigResolver::class, - ['sortResolvers'] - ); - $resolver - ->expects($this->once()) - ->method('sortResolvers') - ->will( - $this->returnValue( - [$high, $medium, $low] - ) - ); - - $resolver->addResolver($low, 10); - $resolver->addResolver($medium, 50); - $resolver->addResolver($high, 100); - $expectedSortedRouters = [$high, $medium, $low]; - // Let's get all routers 5 times, we should only sort once. - for ($i = 0; $i < 5; ++$i) { - $this->assertSame($expectedSortedRouters, $resolver->getAllResolvers()); - } - } - - /** - * This test ensures that if a resolver is being added on the fly, the sorting is reset. - * - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::sortResolvers - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::getAllResolvers - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::addResolver - */ - public function testReSortResolvers() - { - list($low, $medium, $high) = $this->createResolverMocks(); - $highest = clone $high; - // We're using a mock here and not $this->chainResolver because we need to ensure that the sorting operation is done only once. - $resolver = $this->buildMock( - ChainConfigResolver::class, - ['sortResolvers'] - ); - $resolver - ->expects($this->at(0)) - ->method('sortResolvers') - ->will( - $this->returnValue( - [$high, $medium, $low] - ) - ); - // The second time sortResolvers() is called, we're supposed to get the newly added router ($highest) - $resolver - ->expects($this->at(1)) - ->method('sortResolvers') - ->will( - $this->returnValue( - [$highest, $high, $medium, $low] - ) - ); - - $resolver->addResolver($low, 10); - $resolver->addResolver($medium, 50); - $resolver->addResolver($high, 100); - $this->assertSame( - [$high, $medium, $low], - $resolver->getAllResolvers() - ); - - // Now adding another resolver on the fly, sorting must have been reset - $resolver->addResolver($highest, 101); - $this->assertSame( - [$highest, $high, $medium, $low], - $resolver->getAllResolvers() - ); - } - - /** - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::getDefaultNamespace - */ - public function testGetDefaultNamespace() - { - $this->expectException(\LogicException::class); - - $this->chainResolver->getDefaultNamespace(); - } - - /** - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::setDefaultNamespace - */ - public function testSetDefaultNamespace() - { - $namespace = 'foo'; - foreach ($this->createResolverMocks() as $i => $resolver) { - $resolver - ->expects($this->once()) - ->method('setDefaultNamespace') - ->with($namespace); - $this->chainResolver->addResolver($resolver, $i); - } - - $this->chainResolver->setDefaultNamespace($namespace); - } - - /** - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::getParameter - */ - public function testGetParameterInvalid() - { - $this->expectException(\eZ\Publish\Core\MVC\Exception\ParameterNotFoundException::class); - - $paramName = 'foo'; - $namespace = 'namespace'; - $scope = 'scope'; - foreach ($this->createResolverMocks() as $resolver) { - $resolver - ->expects($this->once()) - ->method('getParameter') - ->with($paramName, $namespace, $scope) - ->will($this->throwException(new ParameterNotFoundException($paramName, $namespace))); - $this->chainResolver->addResolver($resolver); - } - - $this->chainResolver->getParameter($paramName, $namespace, $scope); - } - - /** - * @dataProvider getParameterProvider - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::addResolver - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::getParameter - * - * @param string $paramName - * @param string $namespace - * @param string $scope - * @param mixed $expectedValue - */ - public function testGetParameter($paramName, $namespace, $scope, $expectedValue) - { - $resolver = $this->createMock(ConfigResolverInterface::class); - $resolver - ->expects($this->once()) - ->method('getParameter') - ->with($paramName, $namespace, $scope) - ->will($this->returnValue($expectedValue)); - - $this->chainResolver->addResolver($resolver); - $this->assertSame($expectedValue, $this->chainResolver->getParameter($paramName, $namespace, $scope)); - } - - public function getParameterProvider() - { - return [ - ['foo', 'namespace', 'scope', 'someValue'], - ['some.parameter', 'wowNamespace', 'mySiteaccess', ['foo', 'bar']], - ['another.parameter.but.longer.name', 'yetAnotherNamespace', 'anotherSiteaccess', ['foo', ['fruit' => 'apple']]], - ['boolean.parameter', 'yetAnotherNamespace', 'admin', false], - ]; - } - - /** - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::addResolver - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver::hasParameter - */ - public function testHasParameterTrue() - { - $paramName = 'foo'; - $namespace = 'yetAnotherNamespace'; - $scope = 'mySiteaccess'; - - $resolver1 = $this->createMock(ConfigResolverInterface::class); - $resolver1 - ->expects($this->once()) - ->method('hasParameter') - ->with($paramName, $namespace, $scope) - ->will($this->returnValue(false)); - $this->chainResolver->addResolver($resolver1); - - $resolver2 = $this->createMock(ConfigResolverInterface::class); - $resolver2 - ->expects($this->once()) - ->method('hasParameter') - ->with($paramName, $namespace, $scope) - ->will($this->returnValue(true)); - $this->chainResolver->addResolver($resolver2); - - $resolver3 = $this->createMock(ConfigResolverInterface::class); - $resolver3 - ->expects($this->never()) - ->method('hasParameter'); - $this->chainResolver->addResolver($resolver3); - - $this->assertTrue($this->chainResolver->hasParameter($paramName, $namespace, $scope)); - } - - public function testHasParameterFalse() - { - $paramName = 'foo'; - $namespace = 'yetAnotherNamespace'; - $scope = 'mySiteaccess'; - - $resolver = $this->createMock(ConfigResolverInterface::class); - $resolver - ->expects($this->once()) - ->method('hasParameter') - ->with($paramName, $namespace, $scope) - ->will($this->returnValue(false)); - $this->chainResolver->addResolver($resolver); - - $this->assertFalse($this->chainResolver->hasParameter($paramName, $namespace, $scope)); - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject[] - */ - private function createResolverMocks() - { - return [ - $this->createMock(ConfigResolverInterface::class), - $this->createMock(ConfigResolverInterface::class), - $this->createMock(ConfigResolverInterface::class), - ]; - } - - private function buildMock($class, array $methods = []) - { - return $this - ->getMockBuilder($class) - ->disableOriginalConstructor() - ->setMethods($methods) - ->getMock(); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/ConfigResolverTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/ConfigResolverTest.php deleted file mode 100644 index 698495fe7b..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/ConfigResolverTest.php +++ /dev/null @@ -1,295 +0,0 @@ -siteAccess = new SiteAccess('test'); - $this->containerMock = $this->createMock(ContainerInterface::class); - } - - /** - * @param string $defaultNS - * @param int $undefinedStrategy - * @param array $groupsBySiteAccess - * - * @return \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver - */ - private function getResolver($defaultNS = 'ezsettings', $undefinedStrategy = ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION, array $groupsBySiteAccess = []) - { - $configResolver = new ConfigResolver( - null, - $groupsBySiteAccess, - $defaultNS, - $undefinedStrategy - ); - $configResolver->setSiteAccess($this->siteAccess); - $configResolver->setContainer($this->containerMock); - - return $configResolver; - } - - public function testGetSetUndefinedStrategy() - { - $strategy = ConfigResolver::UNDEFINED_STRATEGY_NULL; - $defaultNS = 'ezsettings'; - $resolver = $this->getResolver($defaultNS, $strategy); - - $this->assertSame($strategy, $resolver->getUndefinedStrategy()); - $resolver->setUndefinedStrategy(ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION); - $this->assertSame(ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION, $resolver->getUndefinedStrategy()); - - $this->assertSame($defaultNS, $resolver->getDefaultNamespace()); - $resolver->setDefaultNamespace('anotherNamespace'); - $this->assertSame('anotherNamespace', $resolver->getDefaultNamespace()); - } - - public function testGetParameterFailedWithException() - { - $this->expectException(\eZ\Publish\Core\MVC\Exception\ParameterNotFoundException::class); - - $resolver = $this->getResolver('ezsettings', ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION); - $resolver->getParameter('foo'); - } - - public function testGetParameterFailedNull() - { - $resolver = $this->getResolver('ezsettings', ConfigResolver::UNDEFINED_STRATEGY_NULL); - $this->assertNull($resolver->getParameter('foo')); - } - - public function parameterProvider() - { - return [ - ['foo', 'bar'], - ['some.parameter', true], - ['some.other.parameter', ['foo', 'bar', 'baz']], - ['a.hash.parameter', ['foo' => 'bar', 'tata' => 'toto']], - [ - 'a.deep.hash', [ - 'foo' => 'bar', - 'tata' => 'toto', - 'deeper_hash' => [ - 'likeStarWars' => true, - 'jedi' => ['Obi-Wan Kenobi', 'Mace Windu', 'Luke Skywalker', 'Leïa Skywalker (yes! Read episodes 7-8-9!)'], - 'sith' => ['Darth Vader', 'Darth Maul', 'Palpatine'], - 'roles' => [ - 'Amidala' => ['Queen'], - 'Palpatine' => ['Senator', 'Emperor', 'Villain'], - 'C3PO' => ['Droid', 'Annoying guy'], - 'Jar-Jar' => ['Still wondering his role', 'Annoying guy'], - ], - ], - ], - ], - ]; - } - - /** - * @dataProvider parameterProvider - */ - public function testGetParameterGlobalScope($paramName, $expectedValue) - { - $globalScopeParameter = "ezsettings.global.$paramName"; - $this->containerMock - ->expects($this->once()) - ->method('hasParameter') - ->with($globalScopeParameter) - ->will($this->returnValue(true)); - $this->containerMock - ->expects($this->once()) - ->method('getParameter') - ->with($globalScopeParameter) - ->will($this->returnValue($expectedValue)); - - $this->assertSame($expectedValue, $this->getResolver()->getParameter($paramName)); - } - - /** - * @dataProvider parameterProvider - */ - public function testGetParameterRelativeScope($paramName, $expectedValue) - { - $relativeScopeParameter = "ezsettings.{$this->siteAccess->name}.$paramName"; - $this->containerMock - ->expects($this->exactly(2)) - ->method('hasParameter') - ->with( - $this->logicalOr( - "ezsettings.global.$paramName", - $relativeScopeParameter - ) - ) - // First call is for "global" scope, second is the right one - ->will($this->onConsecutiveCalls(false, true)); - $this->containerMock - ->expects($this->once()) - ->method('getParameter') - ->with($relativeScopeParameter) - ->will($this->returnValue($expectedValue)); - - $this->assertSame($expectedValue, $this->getResolver()->getParameter($paramName)); - } - - /** - * @dataProvider parameterProvider - */ - public function testGetParameterSpecificScope($paramName, $expectedValue) - { - $scope = 'some_siteaccess'; - $relativeScopeParameter = "ezsettings.$scope.$paramName"; - $this->containerMock - ->expects($this->exactly(2)) - ->method('hasParameter') - ->with( - $this->logicalOr( - "ezsettings.global.$paramName", - $relativeScopeParameter - ) - ) - // First call is for "global" scope, second is the right one - ->will($this->onConsecutiveCalls(false, true)); - $this->containerMock - ->expects($this->once()) - ->method('getParameter') - ->with($relativeScopeParameter) - ->will($this->returnValue($expectedValue)); - - $this->assertSame( - $expectedValue, - $this->getResolver()->getParameter($paramName, 'ezsettings', $scope) - ); - } - - /** - * @dataProvider parameterProvider - */ - public function testGetParameterDefaultScope($paramName, $expectedValue) - { - $defaultScopeParameter = "ezsettings.default.$paramName"; - $relativeScopeParameter = "ezsettings.{$this->siteAccess->name}.$paramName"; - $this->containerMock - ->expects($this->exactly(3)) - ->method('hasParameter') - ->with( - $this->logicalOr( - "ezsettings.global.$paramName", - $relativeScopeParameter, - $defaultScopeParameter - ) - ) - // First call is for "global" scope, second is the right one - ->will($this->onConsecutiveCalls(false, false, true)); - $this->containerMock - ->expects($this->once()) - ->method('getParameter') - ->with($defaultScopeParameter) - ->will($this->returnValue($expectedValue)); - - $this->assertSame($expectedValue, $this->getResolver()->getParameter($paramName)); - } - - public function hasParameterProvider() - { - return [ - [true, true, true, true, true], - [true, true, true, false, true], - [true, true, false, false, true], - [false, false, false, false, false], - [false, false, true, false, true], - [false, false, false, true, true], - [false, false, true, true, true], - [false, true, false, false, true], - ]; - } - - /** - * @dataProvider hasParameterProvider - */ - public function testHasParameterNoNamespace($defaultMatch, $groupMatch, $scopeMatch, $globalMatch, $expectedResult) - { - $paramName = 'foo.bar'; - $groupName = 'my_group'; - $configResolver = $this->getResolver( - 'ezsettings', - ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION, - [$this->siteAccess->name => [$groupName]] - ); - - $this->containerMock->expects($this->atLeastOnce()) - ->method('hasParameter') - ->will( - $this->returnValueMap( - [ - ["ezsettings.default.$paramName", $defaultMatch], - ["ezsettings.$groupName.$paramName", $groupMatch], - ["ezsettings.{$this->siteAccess->name}.$paramName", $scopeMatch], - ["ezsettings.global.$paramName", $globalMatch], - ] - ) - ); - - $this->assertSame($expectedResult, $configResolver->hasParameter($paramName)); - } - - /** - * @dataProvider hasParameterProvider - */ - public function testHasParameterWithNamespaceAndScope($defaultMatch, $groupMatch, $scopeMatch, $globalMatch, $expectedResult) - { - $paramName = 'foo.bar'; - $namespace = 'my.namespace'; - $scope = 'another_siteaccess'; - $groupName = 'my_group'; - $configResolver = $this->getResolver( - 'ezsettings', - ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION, - [ - $this->siteAccess->name => ['some_group'], - $scope => [$groupName], - ] - ); - - $this->containerMock->expects($this->atLeastOnce()) - ->method('hasParameter') - ->will( - $this->returnValueMap( - [ - ["$namespace.default.$paramName", $defaultMatch], - ["$namespace.$groupName.$paramName", $groupMatch], - ["$namespace.$scope.$paramName", $scopeMatch], - ["$namespace.global.$paramName", $globalMatch], - ] - ) - ); - - $this->assertSame($expectedResult, $configResolver->hasParameter($paramName, $namespace, $scope)); - } - - public function testGetSetDefaultScope() - { - $newDefaultScope = 'bar'; - $configResolver = $this->getResolver(); - $this->assertSame($this->siteAccess->name, $configResolver->getDefaultScope()); - $configResolver->setDefaultScope($newDefaultScope); - $this->assertSame($newDefaultScope, $configResolver->getDefaultScope()); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ChainConfigResolverPassTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ChainConfigResolverPassTest.php deleted file mode 100644 index c8141083b0..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ChainConfigResolverPassTest.php +++ /dev/null @@ -1,77 +0,0 @@ -setDefinition(ChainConfigResolver::class, new Definition()); - } - - /** - * Register the compiler pass under test, just like you would do inside a bundle's load() - * method:. - * - * $container->addCompilerPass(new MyCompilerPass()); - */ - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new ChainConfigResolverPass()); - } - - /** - * @param int|null $declaredPriority - * @param int $expectedPriority - * - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ChainConfigResolverPass::process - * @dataProvider addResolverProvider - */ - public function testAddResolver($declaredPriority, $expectedPriority) - { - $resolverDef = new Definition(); - $serviceId = 'some_service_id'; - if ($declaredPriority !== null) { - $resolverDef->addTag('ezpublish.config.resolver', ['priority' => $declaredPriority]); - } else { - $resolverDef->addTag('ezpublish.config.resolver'); - } - - $this->setDefinition($serviceId, $resolverDef); - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - ChainConfigResolver::class, - 'addResolver', - [new Reference($serviceId), $expectedPriority] - ); - } - - public function addResolverProvider() - { - return [ - [null, 0], - [0, 0], - [57, 57], - [-23, -23], - [-255, -255], - [-256, -255], - [-1000, -255], - [255, 255], - [256, 255], - [1000, 255], - ]; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ContentViewPassTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ContentViewPassTest.php deleted file mode 100644 index 847dd67e37..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ContentViewPassTest.php +++ /dev/null @@ -1,41 +0,0 @@ -addCompilerPass(new MyCompilerPass()); - */ - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new ContentViewPass()); - } - - public function testAddViewProvider() - { - $def = new Definition(); - $def->addTag(ContentViewPass::VIEW_PROVIDER_IDENTIFIER, ['priority' => 12]); - $serviceId = 'service_id'; - $this->setDefinition($serviceId, $def); - - $this->compile(); - $this->assertContainerBuilderHasServiceDefinitionWithTag( - $serviceId, - 'ezpublish.view_provider', - ['priority' => 12, 'type' => ContentViewPass::VIEW_TYPE] - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/LocationViewPassTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/LocationViewPassTest.php deleted file mode 100644 index e7c693e5b6..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/LocationViewPassTest.php +++ /dev/null @@ -1,41 +0,0 @@ -addCompilerPass(new MyCompilerPass()); - */ - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new LocationViewPass()); - } - - public function testAddViewProvider() - { - $def = new Definition(); - $def->addTag(LocationViewPass::VIEW_PROVIDER_IDENTIFIER, ['priority' => 12]); - $serviceId = 'service_id'; - $this->setDefinition($serviceId, $def); - - $this->compile(); - $this->assertContainerBuilderHasServiceDefinitionWithTag( - $serviceId, - 'ezpublish.view_provider', - ['priority' => 12, 'type' => LocationViewPass::VIEW_TYPE] - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/RegisterStorageEnginePassTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/RegisterStorageEnginePassTest.php deleted file mode 100644 index 967b113433..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/RegisterStorageEnginePassTest.php +++ /dev/null @@ -1,89 +0,0 @@ -setDefinition('ezpublish.api.storage_engine.factory', new Definition()); - $this->container->setParameter('ezpublish.api.storage_engine.default', 'default_storage_engine'); - } - - /** - * Register the compiler pass under test, just like you would do inside a bundle's load() - * method:. - * - * $container->addCompilerPass(new MyCompilerPass()); - */ - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new RegisterStorageEnginePass()); - } - - public function testRegisterStorageEngine() - { - $storageEngineDef = new Definition(); - $storageEngineIdentifier = 'i_am_a_storage_engine'; - $storageEngineDef->addTag('ezpublish.storageEngine', ['alias' => $storageEngineIdentifier]); - $serviceId = 'storage_engine_service'; - $this->setDefinition($serviceId, $storageEngineDef); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.api.storage_engine.factory', - 'registerStorageEngine', - [$serviceId, $storageEngineIdentifier] - ); - } - - public function testRegisterDefaultStorageEngine() - { - $storageEngineDef = new Definition(); - $storageEngineIdentifier = 'i_am_a_storage_engine'; - - $this->container->setParameter('ezpublish.api.storage_engine.default', $storageEngineIdentifier); - $storageEngineDef->addTag('ezpublish.storageEngine', ['alias' => $storageEngineIdentifier]); - $serviceId = 'storage_engine_service'; - $this->setDefinition($serviceId, $storageEngineDef); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.api.storage_engine.factory', - 'registerStorageEngine', - [new Reference($serviceId), $storageEngineIdentifier] - ); - } - - public function testRegisterStorageEngineNoAlias() - { - $this->expectException(\LogicException::class); - - $storageEngineDef = new Definition(); - $storageEngineIdentifier = 'i_am_a_storage_engine'; - $storageEngineDef->addTag('ezpublish.storageEngine'); - $serviceId = 'storage_engine_service'; - $this->setDefinition($serviceId, $storageEngineDef); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.api.storage_engine.factory', - 'registerStorageEngine', - [$serviceId, $storageEngineIdentifier] - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/SecurityPassTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/SecurityPassTest.php deleted file mode 100644 index 426cad2e74..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/SecurityPassTest.php +++ /dev/null @@ -1,75 +0,0 @@ -setDefinition('security.authentication.provider.dao', new Definition()); - $this->setDefinition('security.authentication.provider.rememberme', new Definition()); - $this->setDefinition('security.authentication.provider.anonymous', new Definition()); - $this->setDefinition('security.http_utils', new Definition()); - $this->setDefinition('security.authentication.success_handler', new Definition()); - $this->setDefinition('ezpublish.config.resolver', new Definition()); - $this->setDefinition('ezpublish.siteaccess', new Definition()); - $this->setDefinition('eZ\Publish\API\Repository\PermissionResolver', new Definition()); - $this->setDefinition('eZ\Publish\API\Repository\UserService', new Definition()); - } - - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new SecurityPass()); - } - - public function testAlteredDaoAuthenticationProvider() - { - $this->compile(); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'security.authentication.provider.dao', - 'setPermissionResolver', - [new Reference('eZ\Publish\API\Repository\PermissionResolver')] - ); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'security.authentication.provider.dao', - 'setUserService', - [new Reference('eZ\Publish\API\Repository\UserService')] - ); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'security.authentication.provider.rememberme', - 'setPermissionResolver', - [new Reference('eZ\Publish\API\Repository\PermissionResolver')] - ); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'security.authentication.provider.anonymous', - 'setPermissionResolver', - [new Reference('eZ\Publish\API\Repository\PermissionResolver')] - ); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'security.authentication.provider.anonymous', - 'setConfigResolver', - [new Reference('ezpublish.config.resolver')] - ); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'security.http_utils', - 'setSiteAccess', - [new Reference('ezpublish.siteaccess')] - ); - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'security.authentication.success_handler', - 'setConfigResolver', - [new Reference('ezpublish.config.resolver')] - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/TranslationCollectorPassTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/TranslationCollectorPassTest.php deleted file mode 100644 index c4c337b4e5..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/TranslationCollectorPassTest.php +++ /dev/null @@ -1,40 +0,0 @@ -addCompilerPass(new TranslationCollectorPass()); - } - - public function testTranslationCollector(): void - { - $this->setDefinition('translator.default', new Definition()); - $this->setParameter('kernel.project_dir', __DIR__ . $this->normalizePath('/../Fixtures')); - - $this->compile(); - - $this->assertContainerBuilderHasParameter('available_translations', ['en', 'hi', 'nb']); - } - - /** - * @param $path - * - * @return mixed - */ - private function normalizePath($path) - { - return str_replace('/', \DIRECTORY_SEPARATOR, $path); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/URLHandlerPassTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/URLHandlerPassTest.php deleted file mode 100644 index afbde596f6..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/URLHandlerPassTest.php +++ /dev/null @@ -1,63 +0,0 @@ -setDefinition('ezpublish.url_checker.handler_registry', new Definition()); - } - - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new URLHandlerPass()); - } - - public function testRegisterURLHandler() - { - $serviceId = 'service_id'; - $scheme = 'http'; - $definition = new Definition(); - $definition->addTag('ezpublish.url_handler', ['scheme' => $scheme]); - $this->setDefinition($serviceId, $definition); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.url_checker.handler_registry', - 'addHandler', - [$scheme, new Reference($serviceId)] - ); - } - - public function testRegisterURLHandlerNoScheme() - { - $this->expectException(\LogicException::class); - - $serviceId = 'service_id'; - $scheme = 'http'; - $definition = new Definition(); - $definition->addTag('ezpublish.url_handler'); - $this->setDefinition($serviceId, $definition); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.url_checker.handler_registry', - 'addHandler', - [$scheme, new Reference($serviceId)] - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ViewMatcherRegistryPassTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ViewMatcherRegistryPassTest.php deleted file mode 100644 index b27d5a19f0..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ViewMatcherRegistryPassTest.php +++ /dev/null @@ -1,50 +0,0 @@ -setDefinition(ViewMatcherRegistry::class, new Definition()); - } - - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new ViewMatcherRegistryPass()); - } - - public function testSetMatcher(): void - { - $def = new Definition(); - $def->addTag(ViewMatcherRegistryPass::MATCHER_TAG); - $serviceId = 'service_id'; - $this->setDefinition($serviceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - ViewMatcherRegistry::class, - 'setMatcher', - [ - $serviceId, - new Reference($serviceId), - ] - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolverTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolverTest.php deleted file mode 100644 index 460b672aa0..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolverTest.php +++ /dev/null @@ -1,28 +0,0 @@ -resolveSetting( - '/mnt/nfs/$var_dir$/$storage_dir$', - 'var_dir', - 'var/ezdemo_site', - 'storage_dir', - 'storage' - ) - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php deleted file mode 100644 index e81844cfe2..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php +++ /dev/null @@ -1,340 +0,0 @@ -siteAccess = new SiteAccess(self::FIRST_SA_NAME); - $this->siteAccess->groups = [new SiteAccessGroup(self::SA_GROUP)]; - $this->containerMock = $this->createMock(ContainerInterface::class); - } - - /** - * @dataProvider parameterProvider - */ - public function testGetParameterDefaultScope(string $paramName, $expectedValue): void - { - $globalScopeParameter = $this->getParameter($paramName, self::SCOPE_GLOBAL); - $relativeScopeParameter = $this->getParameter($paramName, $this->siteAccess->name); - $saGroupScopeParameter = $this->getParameter($paramName, self::SA_GROUP); - $defaultScopeParameter = $this->getParameter($paramName, self::SCOPE_DEFAULT); - $this->containerMock - ->expects($this->exactly(4)) - ->method('hasParameter') - ->with( - $this->logicalOr( - $globalScopeParameter, - $relativeScopeParameter, - $saGroupScopeParameter, - $defaultScopeParameter - ) - ) - // First call is for "global" scope, second for SA scope, third fo SA group scope, last is the right one - ->will($this->onConsecutiveCalls(false, false, false, true)); - $this->containerMock - ->expects($this->once()) - ->method('getParameter') - ->with($defaultScopeParameter) - ->willReturn($expectedValue); - - $this->assertSame($expectedValue, $this->getChainConfigResolver()->getParameter($paramName)); - } - - /** - * @dataProvider parameterProvider - */ - public function testGetParameterRelativeScope(string $paramName, $expectedValue): void - { - $globalScopeParameter = $this->getParameter($paramName, self::SCOPE_GLOBAL); - $relativeScopeParameter = $this->getParameter($paramName, $this->siteAccess->name); - $this->containerMock - ->expects($this->exactly(2)) - ->method('hasParameter') - ->with( - $this->logicalOr( - $globalScopeParameter, - $relativeScopeParameter - ) - ) - // First call is for "global" scope, second is the right one - ->will($this->onConsecutiveCalls(false, true)); - $this->containerMock - ->expects($this->once()) - ->method('getParameter') - ->with($relativeScopeParameter) - ->willReturn($expectedValue); - - $this->assertSame($expectedValue, $this->getChainConfigResolver()->getParameter($paramName)); - } - - /** - * @dataProvider parameterProvider - */ - public function testGetParameterSpecificScope(string $paramName, $expectedValue): void - { - $specificScopeParameter = $this->getParameter($paramName, self::FIRST_SA_NAME); - $this->containerMock - ->expects($this->exactly(2)) - ->method('hasParameter') - ->with( - $this->logicalOr( - "ezsettings.global.$paramName", - $specificScopeParameter - ) - ) - // First call is for "global" scope, second is the right one - ->will($this->onConsecutiveCalls(false, true)); - $this->containerMock - ->expects($this->once()) - ->method('getParameter') - ->with($specificScopeParameter) - ->willReturn($expectedValue); - - $this->assertSame( - $expectedValue, - $this->getChainConfigResolver()->getParameter($paramName, self::DEFAULT_NAMESPACE, self::FIRST_SA_NAME) - ); - } - - /** - * @dataProvider parameterProvider - */ - public function testGetParameterGlobalScope(string $paramName, $expectedValue): void - { - $globalScopeParameter = $this->getParameter($paramName, self::SCOPE_GLOBAL); - $this->containerMock - ->expects($this->once()) - ->method('hasParameter') - ->with($globalScopeParameter) - ->willReturn(true); - $this->containerMock - ->expects($this->once()) - ->method('getParameter') - ->with($globalScopeParameter) - ->willReturn($expectedValue); - - $this->assertSame($expectedValue, $this->getChainConfigResolver()->getParameter($paramName)); - } - - /** - * @dataProvider hasParameterProvider - */ - public function testHasParameterNoNamespace( - bool $defaultMatch, - bool $groupMatch, - bool $scopeMatch, - bool $globalMatch, - bool $expectedResult - ): void { - $paramName = 'foo.bar'; - $groupName = self::SA_GROUP; - - $chainConfigResolver = $this->getChainConfigResolver(); - - $this->containerMock->expects($this->atLeastOnce()) - ->method('hasParameter') - ->willReturnMap( - [ - ["ezsettings.default.$paramName", $defaultMatch], - ["ezsettings.$groupName.$paramName", $groupMatch], - ["ezsettings.{$this->siteAccess->name}.$paramName", $scopeMatch], - ["ezsettings.global.$paramName", $globalMatch], - ] - ); - - $this->assertSame($expectedResult, $chainConfigResolver->hasParameter($paramName)); - } - - /** - * @dataProvider hasParameterProvider - */ - public function testHasParameterWithNamespaceAndScope( - bool $defaultMatch, - bool $groupMatch, - bool $scopeMatch, - bool $globalMatch, - bool $expectedResult - ): void { - $paramName = 'foo.bar'; - $namespace = 'my.namespace'; - $scope = self::SECOND_SA_NAME; - $groupName = self::SA_GROUP; - - $chainConfigResolver = $this->getChainConfigResolver(); - - $this->containerMock->expects($this->atLeastOnce()) - ->method('hasParameter') - ->willReturnMap( - [ - ["$namespace.default.$paramName", $defaultMatch], - ["$namespace.$groupName.$paramName", $groupMatch], - ["$namespace.$scope.$paramName", $scopeMatch], - ["$namespace.global.$paramName", $globalMatch], - ] - ); - - $this->assertSame($expectedResult, $chainConfigResolver->hasParameter($paramName, $namespace, $scope)); - } - - private function getGlobalConfigResolver(string $defaultNamespace = self::DEFAULT_NAMESPACE): ConfigResolverInterface - { - $configResolver = new GlobalScopeConfigResolver( - $defaultNamespace - ); - $configResolver->setContainer($this->containerMock); - - return $configResolver; - } - - private function getDefaultConfigResolver(string $defaultNamespace = self::DEFAULT_NAMESPACE): ConfigResolverInterface - { - $configResolver = new DefaultScopeConfigResolver( - $defaultNamespace - ); - $configResolver->setContainer($this->containerMock); - - return $configResolver; - } - - protected function getSiteAccessGroupConfigResolver(string $defaultNamespace = self::DEFAULT_NAMESPACE): ConfigResolverInterface - { - $siteAccess = new SiteAccess( - self::FIRST_SA_NAME, - ); - $configResolver = new SiteAccessGroupConfigResolver( - $this->getStaticSiteAccessProvider(), - $defaultNamespace, - [] - ); - $configResolver->setContainer($this->containerMock); - $configResolver->setSiteAccess($siteAccess); - - return $configResolver; - } - - protected function getSiteAccessConfigResolver(string $defaultNamespace = self::DEFAULT_NAMESPACE): ConfigResolverInterface - { - $siteAccess = new SiteAccess( - self::FIRST_SA_NAME, - ); - $configResolver = new StaticSiteAccessConfigResolver( - $this->getStaticSiteAccessProvider(), - $defaultNamespace - ); - $configResolver->setContainer($this->containerMock); - $configResolver->setSiteAccess($siteAccess); - - return $configResolver; - } - - private function getStaticSiteAccessProvider(): StaticSiteAccessProvider - { - return new StaticSiteAccessProvider( - [ - self::FIRST_SA_NAME, - self::SECOND_SA_NAME, - ], - [ - self::FIRST_SA_NAME => [self::SA_GROUP], - self::SECOND_SA_NAME => [self::SA_GROUP], - ], - ); - } - - public function parameterProvider(): array - { - return [ - ['foo', 'bar'], - ['some.parameter', true], - ['some.other.parameter', ['foo', 'bar', 'baz']], - ['a.hash.parameter', ['foo' => 'bar', 'tata' => 'toto']], - [ - 'a.deep.hash', [ - 'foo' => 'bar', - 'tata' => 'toto', - 'deeper_hash' => [ - 'likeStarWars' => true, - 'jedi' => ['Obi-Wan Kenobi', 'Mace Windu', 'Luke Skywalker', 'Leïa Skywalker (yes! Read episodes 7-8-9!)'], - 'sith' => ['Darth Vader', 'Darth Maul', 'Palpatine'], - 'roles' => [ - 'Amidala' => ['Queen'], - 'Palpatine' => ['Senator', 'Emperor', 'Villain'], - 'C3PO' => ['Droid', 'Annoying guy'], - 'Jar-Jar' => ['Still wondering his role', 'Annoying guy'], - ], - ], - ], - ], - ]; - } - - public function hasParameterProvider(): array - { - return [ - [true, true, true, true, true], - [true, true, true, false, true], - [true, true, false, false, true], - [false, false, false, false, false], - [false, false, true, false, true], - [false, false, false, true, true], - [false, false, true, true, true], - [false, true, false, false, true], - ]; - } - - private function getChainConfigResolver(): ChainConfigResolver - { - $chainConfigResolver = new ChainConfigResolver(); - $chainConfigResolver->addResolver($this->getDefaultConfigResolver(), 0); - $chainConfigResolver->addResolver($this->getSiteAccessGroupConfigResolver(), 50); - $chainConfigResolver->addResolver($this->getSiteAccessConfigResolver(), 100); - $chainConfigResolver->addResolver($this->getGlobalConfigResolver(), 255); - - return $chainConfigResolver; - } - - private function getParameter( - string $paramName, - string $scope, - string $namespace = self::DEFAULT_NAMESPACE - ): string { - return sprintf('%s.%s.%s', $namespace, $scope, $paramName); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/ConfigResolverTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/ConfigResolverTest.php deleted file mode 100644 index 153e580dc7..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/ConfigResolverTest.php +++ /dev/null @@ -1,116 +0,0 @@ -siteAccess = new SiteAccess('test'); - $this->containerMock = $this->createMock(ContainerInterface::class); - } - - abstract protected function getResolver(string $defaultNamespace = 'ezsettings'): ConfigResolverInterface; - - abstract protected function getScope(): string; - - protected function getNamespace(): string - { - return self::DEFAULT_NAMESPACE; - } - - public function testGetParameterFailedWithException(): void - { - $resolver = $this->getResolver(self::DEFAULT_NAMESPACE); - $this->containerMock - ->expects($this->once()) - ->method('hasParameter') - ->with(sprintf('%s.%s.undefined', $this->getNamespace(), $this->getScope())) - ->willReturn(false); - - $this->expectException(ParameterNotFoundException::class); - - $resolver->getParameter('undefined'); - } - - /** - * @dataProvider parameterProvider - */ - public function testGetParameterGlobalScope(string $paramName, $expectedValue): void - { - $globalScopeParameter = sprintf('%s.%s.%s', $this->getNamespace(), $this->getScope(), $paramName); - $this->containerMock - ->expects($this->once()) - ->method('hasParameter') - ->with($globalScopeParameter) - ->willReturn(true); - $this->containerMock - ->expects($this->once()) - ->method('getParameter') - ->with($globalScopeParameter) - ->willReturn($expectedValue); - - $this->assertSame($expectedValue, $this->getResolver()->getParameter($paramName)); - } - - public function parameterProvider(): array - { - return [ - ['foo', 'bar'], - ['some.parameter', true], - ['some.other.parameter', ['foo', 'bar', 'baz']], - ['a.hash.parameter', ['foo' => 'bar', 'tata' => 'toto']], - [ - 'a.deep.hash', [ - 'foo' => 'bar', - 'tata' => 'toto', - 'deeper_hash' => [ - 'likeStarWars' => true, - 'jedi' => ['Obi-Wan Kenobi', 'Mace Windu', 'Luke Skywalker', 'Leïa Skywalker (yes! Read episodes 7-8-9!)'], - 'sith' => ['Darth Vader', 'Darth Maul', 'Palpatine'], - 'roles' => [ - 'Amidala' => ['Queen'], - 'Palpatine' => ['Senator', 'Emperor', 'Villain'], - 'C3PO' => ['Droid', 'Annoying guy'], - 'Jar-Jar' => ['Still wondering his role', 'Annoying guy'], - ], - ], - ], - ], - ]; - } - - public function testGetSetDefaultNamespace(): void - { - $newDefaultNamespace = 'new'; - $configResolver = $this->getResolver(); - $this->assertSame(self::DEFAULT_NAMESPACE, $configResolver->getDefaultNamespace()); - $configResolver->setDefaultNamespace($newDefaultNamespace); - $this->assertSame($newDefaultNamespace, $configResolver->getDefaultNamespace()); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/DefaultScopeConfigResolverTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/DefaultScopeConfigResolverTest.php deleted file mode 100644 index a41e469292..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/DefaultScopeConfigResolverTest.php +++ /dev/null @@ -1,30 +0,0 @@ -setContainer($this->containerMock); - - return $configResolver; - } - - protected function getScope(): string - { - return 'default'; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/GlobalScopeConfigResolverTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/GlobalScopeConfigResolverTest.php deleted file mode 100644 index 1d42c1f244..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/GlobalScopeConfigResolverTest.php +++ /dev/null @@ -1,30 +0,0 @@ -setContainer($this->containerMock); - - return $configResolver; - } - - protected function getScope(): string - { - return 'global'; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolverTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolverTest.php deleted file mode 100644 index b9bffdad89..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolverTest.php +++ /dev/null @@ -1,45 +0,0 @@ - [self::SA_GROUP]], - ); - $siteAccess = new SiteAccess( - self::EXISTING_SA_NAME, - 'default', - $this->createMock(Matcher::class) - ); - $configResolver = new SiteAccessGroupConfigResolver( - $staticSiteAccessProvider, - $defaultNamespace, - [] - ); - $configResolver->setContainer($this->containerMock); - $configResolver->setSiteAccess($siteAccess); - - return $configResolver; - } - - protected function getScope(): string - { - return self::SA_GROUP; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolverTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolverTest.php deleted file mode 100644 index 06dd3f0a2e..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolverTest.php +++ /dev/null @@ -1,44 +0,0 @@ - [self::SA_GROUP]], - ); - $siteAccess = new SiteAccess( - self::EXISTING_SA_NAME, - 'default', - $this->createMock(Matcher::class) - ); - $configResolver = new StaticSiteAccessConfigResolver( - $staticSiteAccessProvider, - $defaultNamespace - ); - $configResolver->setContainer($this->containerMock); - $configResolver->setSiteAccess($siteAccess); - - return $configResolver; - } - - protected function getScope(): string - { - return self::EXISTING_SA_NAME; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/AbstractParserTestCase.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/AbstractParserTestCase.php deleted file mode 100644 index 8073c8aa43..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/AbstractParserTestCase.php +++ /dev/null @@ -1,112 +0,0 @@ -container, - new FileLocator(__DIR__ . '/../../Fixtures') - ); - - $loader->load('parameters.yml'); - } - - /** - * Asserts a parameter from ConfigResolver has expected value for given scope. - * - * @param string $parameterName - * @param mixed $expectedValue - * @param string $scope SiteAccess name, group, default or global - * @param bool $assertSame Set to false if you want to use assertEquals() instead of assertSame() - */ - protected function assertConfigResolverParameterValue($parameterName, $expectedValue, $scope, $assertSame = true) - { - $chainConfigResolver = $this->getConfigResolver(); - $assertMethod = $assertSame ? 'assertSame' : 'assertEquals'; - $this->$assertMethod($expectedValue, $chainConfigResolver->getParameter($parameterName, 'ezsettings', $scope)); - } - - protected function getConfigResolver(): ConfigResolverInterface - { - $chainConfigResolver = new ChainConfigResolver(); - $siteAccessProvider = $this->getSiteAccessProviderMock(); - - $configResolvers = [ - new DefaultScopeConfigResolver('default'), - new SiteAccessGroupConfigResolver($siteAccessProvider, 'default', [self::EMPTY_SA_GROUP => []]), - new StaticSiteAccessConfigResolver($siteAccessProvider, 'default'), - new GlobalScopeConfigResolver('default'), - ]; - - foreach ($configResolvers as $priority => $configResolver) { - $configResolver->setContainer($this->container); - $chainConfigResolver->addResolver($configResolver, $priority); - } - - return $chainConfigResolver; - } - - protected function getSiteAccessProviderMock(): SiteAccessProviderInterface - { - $siteAccessProvider = $this->createMock(SiteAccessProviderInterface::class); - $siteAccessProvider - ->method('isDefined') - ->willReturnMap([ - ['ezdemo_site', true], - ['fre', true], - ['fre2', true], - ['ezdemo_site_admin', true], - ['empty_group', false], - ]); - $siteAccessProvider - ->method('getSiteAccess') - ->willReturnMap([ - ['ezdemo_site', $this->getSiteAccess('ezdemo_site', StaticSiteAccessProvider::class, ['ezdemo_group', 'ezdemo_frontend_group'])], - ['fre', $this->getSiteAccess('fre', StaticSiteAccessProvider::class, ['ezdemo_group', 'ezdemo_frontend_group'])], - ['fre2', $this->getSiteAccess('fre', StaticSiteAccessProvider::class, ['ezdemo_group', 'ezdemo_frontend_group'])], - ['ezdemo_site_admin', $this->getSiteAccess('ezdemo_site_admin', StaticSiteAccessProvider::class, ['ezdemo_group'])], - ]); - - return $siteAccessProvider; - } - - /** - * @param string[] $groupNames - */ - protected function getSiteAccess(string $name, string $provider, array $groupNames): SiteAccess - { - $siteAccess = new SiteAccess($name, SiteAccess::DEFAULT_MATCHING_TYPE, null, $provider); - $siteAccessGroups = []; - foreach ($groupNames as $groupName) { - $siteAccessGroups[] = new SiteAccessGroup($groupName); - } - $siteAccess->groups = $siteAccessGroups; - - return $siteAccess; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/ContentTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/ContentTest.php deleted file mode 100644 index e7f96259d1..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/ContentTest.php +++ /dev/null @@ -1,129 +0,0 @@ -load(); - - $this->assertConfigResolverParameterValue('content.view_cache', true, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('content.ttl_cache', true, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('content.default_ttl', 60, 'ezdemo_site'); - } - - /** - * @dataProvider contentSettingsProvider - */ - public function testContentSettings(array $config, array $expected) - { - $this->load( - [ - 'system' => [ - 'ezdemo_site' => $config, - ], - ] - ); - - foreach ($expected as $key => $val) { - $this->assertConfigResolverParameterValue($key, $val, 'ezdemo_site'); - } - } - - public function contentSettingsProvider() - { - return [ - [ - [ - 'content' => [ - 'view_cache' => true, - 'ttl_cache' => true, - 'default_ttl' => 100, - ], - ], - [ - 'content.view_cache' => true, - 'content.ttl_cache' => true, - 'content.default_ttl' => 100, - ], - ], - [ - [ - 'content' => [ - 'view_cache' => false, - 'ttl_cache' => false, - 'default_ttl' => 123, - ], - ], - [ - 'content.view_cache' => false, - 'content.ttl_cache' => false, - 'content.default_ttl' => 123, - ], - ], - [ - [ - 'content' => [ - 'view_cache' => false, - ], - ], - [ - 'content.view_cache' => false, - 'content.ttl_cache' => true, - 'content.default_ttl' => 60, - ], - ], - [ - [ - 'content' => [ - 'tree_root' => ['location_id' => 123], - ], - ], - [ - 'content.view_cache' => true, - 'content.ttl_cache' => true, - 'content.default_ttl' => 60, - 'content.tree_root.location_id' => 123, - ], - ], - [ - [ - 'content' => [ - 'tree_root' => [ - 'location_id' => 456, - 'excluded_uri_prefixes' => ['/media/images', '/products'], - ], - ], - ], - [ - 'content.view_cache' => true, - 'content.ttl_cache' => true, - 'content.default_ttl' => 60, - 'content.tree_root.location_id' => 456, - 'content.tree_root.excluded_uri_prefixes' => ['/media/images', '/products'], - ], - ], - ]; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/FieldType/ImageAssetTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/FieldType/ImageAssetTest.php deleted file mode 100644 index 3b6e32ecf3..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/FieldType/ImageAssetTest.php +++ /dev/null @@ -1,86 +0,0 @@ -load(); - - $this->assertConfigResolverParameterValue( - 'fieldtypes.ezimageasset.mappings', - [ - 'content_type_identifier' => 'image', - 'content_field_identifier' => 'image', - 'name_field_identifier' => 'name', - 'parent_location_id' => 51, - ], - 'ezdemo_site' - ); - } - - /** - * @dataProvider imageAssetSettingsProvider - */ - public function testImageAssetSettings(array $config, array $expected) - { - $this->load( - [ - 'system' => [ - 'ezdemo_site' => $config, - ], - ] - ); - - foreach ($expected as $key => $val) { - $this->assertConfigResolverParameterValue($key, $val, 'ezdemo_site'); - } - } - - public function imageAssetSettingsProvider(): array - { - return [ - [ - [ - 'fieldtypes' => [ - 'ezimageasset' => [ - 'content_type_identifier' => 'photo', - 'content_field_identifier' => 'file', - 'name_field_identifier' => 'title', - 'parent_location_id' => 68, - ], - ], - ], - [ - 'fieldtypes.ezimageasset.mappings' => [ - 'content_type_identifier' => 'photo', - 'content_field_identifier' => 'file', - 'name_field_identifier' => 'title', - 'parent_location_id' => 68, - ], - ], - ], - ]; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/IOTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/IOTest.php deleted file mode 100644 index 4cff9bcf65..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/IOTest.php +++ /dev/null @@ -1,64 +0,0 @@ -container->setParameter('ezsettings.default.var_dir', 'var'); // PS: Does not seem to take effect - $this->container->setParameter('ezsettings.default.storage_dir', 'storage'); - $this->container->setParameter('ezsettings.ezdemo_site.var_dir', 'var/ezdemo_site'); - } - - protected function getContainerExtensions(): array - { - return [ - new EzPublishCoreExtension([new IO(new ComplexSettingParser())]), - ]; - } - - protected function getMinimalConfiguration(): array - { - return $this->minimalConfig = Yaml::parse(file_get_contents(__DIR__ . '/../../Fixtures/ezpublish_minimal.yml')); - } - - public function testHandlersConfig() - { - $config = [ - 'system' => [ - 'ezdemo_site' => [ - 'io' => [ - 'binarydata_handler' => 'cluster', - 'metadata_handler' => 'cluster', - ], - ], - self::EMPTY_SA_GROUP => [ - 'io' => [ - 'binarydata_handler' => 'group_cluster', - 'metadata_handler' => 'group_cluster', - ], - ], - ], - ]; - - $this->load($config); - - $this->assertConfigResolverParameterValue('io.metadata_handler', 'cluster', 'ezdemo_site'); - $this->assertConfigResolverParameterValue('io.binarydata_handler', 'cluster', 'ezdemo_site'); - $this->assertConfigResolverParameterValue('io.metadata_handler', 'group_cluster', self::EMPTY_SA_GROUP); - $this->assertConfigResolverParameterValue('io.binarydata_handler', 'group_cluster', self::EMPTY_SA_GROUP); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/ImageTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/ImageTest.php deleted file mode 100644 index 7447406345..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/ImageTest.php +++ /dev/null @@ -1,91 +0,0 @@ -markTestSkipped('Missing or mis-configured Imagemagick convert path.'); - } - } - - protected function getMinimalConfiguration(): array - { - $this->config = Yaml::parse(file_get_contents(__DIR__ . '/../../Fixtures/ezpublish_image.yml')); - $this->config += [ - 'imagemagick' => [ - 'enabled' => true, - 'path' => $_ENV['imagemagickConvertPath'], - ], - ]; - - return $this->config; - } - - protected function getContainerExtensions(): array - { - return [ - new EzPublishCoreExtension([new Image()]), - ]; - } - - public function testVariations() - { - $this->load(); - - $expectedParsedVariations = []; - foreach ($this->config['system'] as $sa => $saConfig) { - $expectedParsedVariations[$sa] = []; - foreach ($saConfig['image_variations'] as $variationName => $imageVariationConfig) { - $imageVariationConfig['post_processors'] = []; - foreach ($imageVariationConfig['filters'] as $i => $filter) { - $imageVariationConfig['filters'][$filter['name']] = $filter['params']; - unset($imageVariationConfig['filters'][$i]); - } - $expectedParsedVariations[$sa][$variationName] = $imageVariationConfig; - } - } - - $expected = $expectedParsedVariations['ezdemo_group'] + $this->container->getParameter('ezsettings.default.image_variations'); - $this->assertConfigResolverParameterValue('image_variations', $expected, 'ezdemo_site', false); - $this->assertConfigResolverParameterValue('image_variations', $expected, 'ezdemo_site_admin', false); - $this->assertConfigResolverParameterValue( - 'image_variations', - $expected + $expectedParsedVariations['fre'], - 'fre', - false - ); - } - - public function testPrePostParameters() - { - $this->expectException(\InvalidArgumentException::class); - - $this->load( - [ - 'system' => [ - 'ezdemo_site' => [ - 'imagemagick' => [ - 'pre_parameters' => '-foo -bar', - 'post_parameters' => '-baz', - ], - ], - ], - ] - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/LanguagesTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/LanguagesTest.php deleted file mode 100644 index 1f06988f10..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/LanguagesTest.php +++ /dev/null @@ -1,120 +0,0 @@ -minimalConfig = Yaml::parse(file_get_contents(__DIR__ . '/../../Fixtures/ezpublish_minimal.yml')); - } - - public function testLanguagesSingleSiteaccess() - { - $langDemoSite = ['eng-GB']; - $langFre = ['fre-FR', 'eng-GB']; - $langEmptyGroup = ['pol-PL']; - $config = [ - 'siteaccess' => [ - 'list' => ['fre2'], - 'groups' => [self::EMPTY_SA_GROUP => []], - ], - 'system' => [ - 'ezdemo_site' => ['languages' => $langDemoSite], - 'fre' => ['languages' => $langFre], - 'fre2' => ['languages' => $langFre], - self::EMPTY_SA_GROUP => ['languages' => $langEmptyGroup], - ], - ]; - $this->load($config); - - $this->assertConfigResolverParameterValue('languages', $langDemoSite, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('languages', $langFre, 'fre'); - $this->assertConfigResolverParameterValue('languages', $langFre, 'fre2'); - $this->assertConfigResolverParameterValue('languages', $langEmptyGroup, self::EMPTY_SA_GROUP); - $this->assertSame( - [ - 'eng-GB' => ['ezdemo_site'], - 'fre-FR' => ['fre', 'fre2'], - 'pol-PL' => [self::EMPTY_SA_GROUP], - ], - $this->container->getParameter('ezpublish.siteaccesses_by_language') - ); - // languages for ezdemo_site_admin will take default value (empty array) - $this->assertConfigResolverParameterValue('languages', [], 'ezdemo_site_admin'); - } - - public function testLanguagesSiteaccessGroup() - { - $langDemoSite = ['eng-US', 'eng-GB']; - $config = [ - 'system' => [ - 'ezdemo_frontend_group' => ['languages' => $langDemoSite], - 'ezdemo_site' => [], - 'fre' => [], - ], - ]; - $this->load($config); - - $this->assertConfigResolverParameterValue('languages', $langDemoSite, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('languages', $langDemoSite, 'fre'); - $this->assertConfigResolverParameterValue('languages', [], self::EMPTY_SA_GROUP); - $this->assertSame( - [ - 'eng-US' => ['ezdemo_frontend_group', 'ezdemo_site', 'fre'], - ], - $this->container->getParameter('ezpublish.siteaccesses_by_language') - ); - // languages for ezdemo_site_admin will take default value (empty array) - $this->assertConfigResolverParameterValue('languages', [], 'ezdemo_site_admin'); - } - - public function testTranslationSiteAccesses() - { - $translationSAsDemoSite = ['foo', 'bar']; - $translationSAsFre = ['foo2', 'bar2']; - $config = [ - 'system' => [ - 'ezdemo_site' => ['translation_siteaccesses' => $translationSAsDemoSite], - 'fre' => ['translation_siteaccesses' => $translationSAsFre], - ], - ]; - $this->load($config); - - $this->assertConfigResolverParameterValue('translation_siteaccesses', $translationSAsDemoSite, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('translation_siteaccesses', $translationSAsFre, 'fre'); - $this->assertConfigResolverParameterValue('translation_siteaccesses', [], 'ezdemo_site_admin'); - $this->assertConfigResolverParameterValue('translation_siteaccesses', [], self::EMPTY_SA_GROUP); - } - - public function testTranslationSiteAccessesWithGroup() - { - $translationSAsDemoSite = ['ezdemo_site', 'fre']; - $config = [ - 'system' => [ - 'ezdemo_frontend_group' => ['translation_siteaccesses' => $translationSAsDemoSite], - 'ezdemo_site' => [], - 'fre' => [], - ], - ]; - $this->load($config); - - $this->assertConfigResolverParameterValue('translation_siteaccesses', $translationSAsDemoSite, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('translation_siteaccesses', $translationSAsDemoSite, 'fre'); - $this->assertConfigResolverParameterValue('translation_siteaccesses', [], 'ezdemo_site_admin'); - $this->assertConfigResolverParameterValue('translation_siteaccesses', [], self::EMPTY_SA_GROUP); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/TemplatesTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/TemplatesTest.php deleted file mode 100644 index 88aceaa0f6..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/TemplatesTest.php +++ /dev/null @@ -1,161 +0,0 @@ -config = Yaml::parse(file_get_contents(__DIR__ . '/../../Fixtures/ezpublish_templates.yml')); - } - - public function testFieldTemplates() - { - $this->load(); - $fixedUpConfig = $this->getExpectedConfigFieldTemplates($this->config); - $groupFieldTemplates = $fixedUpConfig['system']['ezdemo_frontend_group']['field_templates']; - $demoSiteFieldTemplates = $fixedUpConfig['system']['ezdemo_site']['field_templates']; - $this->assertConfigResolverParameterValue( - 'field_templates', - array_merge( - // Adding default kernel value. - [['template' => '%ezplatform.default_templates.field_templates%', 'priority' => 0]], - $groupFieldTemplates, - $demoSiteFieldTemplates - ), - 'ezdemo_site', - false - ); - $this->assertConfigResolverParameterValue( - 'field_templates', - array_merge( - // Adding default kernel value. - [['template' => '%ezplatform.default_templates.field_templates%', 'priority' => 0]], - $groupFieldTemplates - ), - 'fre', - false - ); - $this->assertConfigResolverParameterValue( - 'field_templates', - [['template' => '%ezplatform.default_templates.field_templates%', 'priority' => 0]], - 'ezdemo_site_admin', - false - ); - } - - protected function getSiteAccessProviderMock(): SiteAccessProviderInterface - { - $siteAccessProvider = $this->createMock(SiteAccessProviderInterface::class); - $siteAccessProvider - ->method('isDefined') - ->willReturnMap([ - ['ezdemo_site', true], - ['fre', true], - ['ezdemo_site_admin', true], - ]); - $siteAccessProvider - ->method('getSiteAccess') - ->willReturnMap([ - ['ezdemo_site', $this->getSiteAccess('ezdemo_site', StaticSiteAccessProvider::class, ['ezdemo_group', 'ezdemo_frontend_group'])], - ['fre', $this->getSiteAccess('fre', StaticSiteAccessProvider::class, ['ezdemo_group', 'ezdemo_frontend_group'])], - ['ezdemo_site_admin', $this->getSiteAccess('ezdemo_site_admin', StaticSiteAccessProvider::class, ['ezdemo_group'])], - ]); - - return $siteAccessProvider; - } - - /** - * Fixes up input configuration for field_templates as semantic configuration parser does, adding a default priority of 0 when not set. - * - * @param array $config - * - * @return array - */ - private function getExpectedConfigFieldTemplates(array $config) - { - foreach ($config['system']['ezdemo_frontend_group']['field_templates'] as &$block) { - if (!isset($block['priority'])) { - $block['priority'] = 0; - } - } - - return $config; - } - - public function testFieldDefinitionSettingsTemplates() - { - $this->load(); - $fixedUpConfig = $this->getExpectedConfigFieldDefinitionSettingsTemplates($this->config); - $groupFieldTemplates = $fixedUpConfig['system']['ezdemo_frontend_group']['fielddefinition_settings_templates']; - $demoSiteFieldTemplates = $fixedUpConfig['system']['ezdemo_site']['fielddefinition_settings_templates']; - - $this->assertConfigResolverParameterValue( - 'fielddefinition_settings_templates', - array_merge( - // Adding default kernel value. - [['template' => '%ezplatform.default_templates.fielddefinition_settings_templates%', 'priority' => 0]], - $groupFieldTemplates, - $demoSiteFieldTemplates - ), - 'ezdemo_site', - false - ); - $this->assertConfigResolverParameterValue( - 'fielddefinition_settings_templates', - array_merge( - // Adding default kernel value. - [['template' => '%ezplatform.default_templates.fielddefinition_settings_templates%', 'priority' => 0]], - $groupFieldTemplates - ), - 'fre', - false - ); - $this->assertConfigResolverParameterValue( - 'fielddefinition_settings_templates', - [['template' => '%ezplatform.default_templates.fielddefinition_settings_templates%', 'priority' => 0]], - 'ezdemo_site_admin', - false - ); - } - - /** - * Fixes up input configuration for fielddefinition_settings_templates as semantic configuration parser does, adding a default priority of 0 when not set. - * - * @param array $config - * - * @return array - */ - private function getExpectedConfigFieldDefinitionSettingsTemplates(array $config) - { - foreach ($config['system']['ezdemo_frontend_group']['fielddefinition_settings_templates'] as &$block) { - if (!isset($block['priority'])) { - $block['priority'] = 0; - } - } - - return $config; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/ViewTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/ViewTest.php deleted file mode 100644 index a87b2f7fc9..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/ViewTest.php +++ /dev/null @@ -1,68 +0,0 @@ -config = Yaml::parse(file_get_contents(__DIR__ . '/../../Fixtures/ezpublish_view.yml')); - } - - public function testLocationView() - { - $this->load(); - $expectedLocationView = $this->config['system']['ezdemo_frontend_group']['location_view']; - - // Items that don't use a custom controller got converted to content view (location view depreciation) - unset($expectedLocationView['full']['frontpage']); - unset($expectedLocationView['line']['article']); - - foreach ($expectedLocationView as &$rulesets) { - foreach ($rulesets as &$config) { - if (!isset($config['params'])) { - $config['params'] = []; - } - } - } - - $this->assertConfigResolverParameterValue('location_view', $expectedLocationView, 'ezdemo_site', false); - $this->assertConfigResolverParameterValue('location_view', $expectedLocationView, 'fre', false); - $this->assertConfigResolverParameterValue('location_view', [], 'ezdemo_site_admin', false); - } - - public function testContentView() - { - $this->load(); - $expectedContentView = $this->config['system']['ezdemo_frontend_group']['content_view']; - foreach ($expectedContentView as &$rulesets) { - foreach ($rulesets as &$config) { - if (!isset($config['params'])) { - $config['params'] = []; - } - } - } - - $this->assertConfigResolverParameterValue('content_view', $expectedContentView, 'ezdemo_site', false); - $this->assertConfigResolverParameterValue('content_view', $expectedContentView, 'fre', false); - $this->assertConfigResolverParameterValue('content_view', [], 'ezdemo_site_admin', false); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/EzPublishCoreExtensionTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/EzPublishCoreExtensionTest.php deleted file mode 100644 index 7fd7d10a00..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/EzPublishCoreExtensionTest.php +++ /dev/null @@ -1,906 +0,0 @@ -container, - new FileLocator(__DIR__ . '/Fixtures') - ); - - $loader->load('parameters.yml'); - - $this->siteaccessConfig = [ - 'siteaccess' => [ - 'default_siteaccess' => 'ezdemo_site', - 'list' => ['ezdemo_site', 'eng', 'fre', 'ezdemo_site_admin'], - 'groups' => [ - 'ezdemo_group' => ['ezdemo_site', 'eng', 'fre', 'ezdemo_site_admin'], - 'ezdemo_frontend_group' => ['ezdemo_site', 'eng', 'fre'], - 'empty_group' => [], - ], - 'match' => [ - 'URILElement' => 1, - 'Map\URI' => ['the_front' => 'ezdemo_site', 'the_back' => 'ezdemo_site_admin'], - ], - ], - 'system' => [ - 'ezdemo_site' => [], - 'eng' => [], - 'fre' => [], - 'ezdemo_site_admin' => [], - 'empty_group' => ['var_dir' => 'foo'], - ], - ]; - - $_ENV['HTTPCACHE_PURGE_TYPE'] = $_SERVER['HTTPCACHE_PURGE_TYPE'] = 'http'; - } - - protected function getContainerExtensions(): array - { - return [EzPublishCoreExtension::class => $this->getCoreExtension()]; - } - - protected function getMinimalConfiguration(): array - { - return $this->minimalConfig = Yaml::parse(file_get_contents(__DIR__ . '/Fixtures/ezpublish_minimal_no_siteaccess.yml')); - } - - public function testSiteAccessConfiguration() - { - $this->load($this->siteaccessConfig); - $this->assertContainerBuilderHasParameter( - 'ezpublish.siteaccess.list', - $this->siteaccessConfig['siteaccess']['list'] - ); - $this->assertContainerBuilderHasParameter( - 'ezpublish.siteaccess.default', - $this->siteaccessConfig['siteaccess']['default_siteaccess'] - ); - $this->assertContainerBuilderHasParameter('ezpublish.siteaccess.groups', $this->siteaccessConfig['siteaccess']['groups']); - - $expectedMatchingConfig = []; - foreach ($this->siteaccessConfig['siteaccess']['match'] as $key => $val) { - // Value is expected to always be an array (transformed by semantic configuration parser). - $expectedMatchingConfig[$key] = is_array($val) ? $val : ['value' => $val]; - } - $this->assertContainerBuilderHasParameter('ezpublish.siteaccess.match_config', $expectedMatchingConfig); - $this->assertContainerBuilderHasParameter('ezsettings.empty_group.var_dir', 'foo'); - - $groupsBySiteaccess = []; - foreach ($this->siteaccessConfig['siteaccess']['groups'] as $groupName => $groupMembers) { - foreach ($groupMembers as $member) { - if (!isset($groupsBySiteaccess[$member])) { - $groupsBySiteaccess[$member] = []; - } - - $groupsBySiteaccess[$member][] = $groupName; - } - } - } - - public function testSiteAccessNoConfiguration() - { - $this->load(); - $this->assertContainerBuilderHasParameter('ezpublish.siteaccess.list', ['setup']); - $this->assertContainerBuilderHasParameter('ezpublish.siteaccess.default', 'setup'); - $this->assertContainerBuilderHasParameter('ezpublish.siteaccess.groups', []); - $this->assertContainerBuilderHasParameter('ezpublish.siteaccess.groups_by_siteaccess', []); - $this->assertContainerBuilderHasParameter('ezpublish.siteaccess.match_config', null); - } - - public function testImageMagickConfigurationBasic() - { - if (!isset($_ENV['imagemagickConvertPath']) || !is_executable($_ENV['imagemagickConvertPath'])) { - $this->markTestSkipped('Missing or mis-configured Imagemagick convert path.'); - } - - $this->load( - [ - 'imagemagick' => [ - 'enabled' => true, - 'path' => $_ENV['imagemagickConvertPath'], - ], - ] - ); - $this->assertContainerBuilderHasParameter('ezpublish.image.imagemagick.enabled', true); - $this->assertContainerBuilderHasParameter('ezpublish.image.imagemagick.executable_path', dirname($_ENV['imagemagickConvertPath'])); - $this->assertContainerBuilderHasParameter('ezpublish.image.imagemagick.executable', basename($_ENV['imagemagickConvertPath'])); - } - - public function testImageMagickConfigurationFilters() - { - if (!isset($_ENV['imagemagickConvertPath']) || !is_executable($_ENV['imagemagickConvertPath'])) { - $this->markTestSkipped('Missing or mis-configured Imagemagick convert path.'); - } - - $customFilters = [ - 'foobar' => '-foobar', - 'wow' => '-amazing', - ]; - $this->load( - [ - 'imagemagick' => [ - 'enabled' => true, - 'path' => $_ENV['imagemagickConvertPath'], - 'filters' => $customFilters, - ], - ] - ); - $this->assertTrue($this->container->hasParameter('ezpublish.image.imagemagick.filters')); - $filters = $this->container->getParameter('ezpublish.image.imagemagick.filters'); - $this->assertArrayHasKey('foobar', $filters); - $this->assertSame($customFilters['foobar'], $filters['foobar']); - $this->assertArrayHasKey('wow', $filters); - $this->assertSame($customFilters['wow'], $filters['wow']); - } - - public function testImagePlaceholderConfiguration() - { - $this->load([ - 'image_placeholder' => [ - 'default' => [ - 'provider' => 'generic', - 'options' => [ - 'foo' => 'Foo', - 'bar' => 'Bar', - ], - 'verify_binary_data_availability' => true, - ], - 'fancy' => [ - 'provider' => 'remote', - ], - ], - ]); - - $this->assertEquals([ - 'default' => [ - 'provider' => 'generic', - 'options' => [ - 'foo' => 'Foo', - 'bar' => 'Bar', - ], - 'verify_binary_data_availability' => true, - ], - 'fancy' => [ - 'provider' => 'remote', - 'options' => [], - 'verify_binary_data_availability' => false, - ], - ], $this->container->getParameter('image_alias.placeholder_providers')); - } - - public function testRoutingConfiguration() - { - $this->load(); - $this->assertContainerBuilderHasAlias('router', 'ezpublish.chain_router'); - - $this->assertTrue($this->container->hasParameter('ezpublish.default_router.non_siteaccess_aware_routes')); - $nonSiteaccessAwareRoutes = $this->container->getParameter('ezpublish.default_router.non_siteaccess_aware_routes'); - // See ezpublish_minimal_no_siteaccess.yml fixture - $this->assertContains('foo_route', $nonSiteaccessAwareRoutes); - $this->assertContains('my_prefix_', $nonSiteaccessAwareRoutes); - } - - /** - * @dataProvider cacheConfigurationProvider - * - * @param array $customCacheConfig - * @param string $expectedPurgeType - */ - public function testCacheConfiguration(array $customCacheConfig, $expectedPurgeType) - { - $this->load($customCacheConfig); - - $this->assertContainerBuilderHasParameter('ezpublish.http_cache.purge_type', $expectedPurgeType); - } - - public function cacheConfigurationProvider() - { - return [ - [[], 'local'], - [ - [ - 'http_cache' => ['purge_type' => 'local'], - ], - 'local', - ], - [ - [ - 'http_cache' => ['purge_type' => 'multiple_http'], - ], - 'http', - ], - [ - [ - 'http_cache' => ['purge_type' => 'single_http'], - ], - 'http', - ], - [ - [ - 'http_cache' => ['purge_type' => 'http'], - ], - 'http', - ], - [ - [ - 'http_cache' => ['purge_type' => '%env(HTTPCACHE_PURGE_TYPE)%'], - ], - 'http', - ], - ]; - } - - public function testCacheConfigurationCustomPurgeService() - { - $serviceId = 'foobar'; - $this->setDefinition($serviceId, new Definition()); - $this->load( - [ - 'http_cache' => ['purge_type' => 'foobar', 'timeout' => 12], - ] - ); - - $this->assertContainerBuilderHasParameter('ezpublish.http_cache.purge_type', 'foobar'); - } - - public function testLocaleConfiguration() - { - $this->load(['locale_conversion' => ['foo' => 'bar']]); - $conversionMap = $this->container->getParameter('ezpublish.locale.conversion_map'); - $this->assertArrayHasKey('foo', $conversionMap); - $this->assertSame('bar', $conversionMap['foo']); - } - - public function testRepositoriesConfiguration() - { - $repositories = [ - 'main' => [ - 'storage' => [ - 'engine' => 'legacy', - 'connection' => 'default', - ], - 'search' => [ - 'engine' => 'legacy', - 'connection' => 'blabla', - ], - 'fields_groups' => [ - 'list' => ['content', 'metadata'], - 'default' => '%ezsettings.default.content.field_groups.default%', - ], - 'options' => [ - 'default_version_archive_limit' => 5, - 'remove_archived_versions_on_publish' => true, - ], - ], - 'foo' => [ - 'storage' => [ - 'engine' => 'sqlng', - 'connection' => 'default', - ], - 'search' => [ - 'engine' => 'solr', - 'connection' => 'lalala', - ], - 'fields_groups' => [ - 'list' => ['content', 'metadata'], - 'default' => '%ezsettings.default.content.field_groups.default%', - ], - 'options' => [ - 'default_version_archive_limit' => 5, - 'remove_archived_versions_on_publish' => true, - ], - ], - ]; - $this->load(['repositories' => $repositories]); - $this->assertTrue($this->container->hasParameter('ezpublish.repositories')); - - foreach ($repositories as &$repositoryConfig) { - $repositoryConfig['storage']['config'] = []; - $repositoryConfig['search']['config'] = []; - } - $this->assertSame($repositories, $this->container->getParameter('ezpublish.repositories')); - } - - /** - * @dataProvider repositoriesConfigurationFieldGroupsProvider - */ - public function testRepositoriesConfigurationFieldGroups($repositories, $expectedRepositories) - { - $this->load(['repositories' => $repositories]); - $this->assertTrue($this->container->hasParameter('ezpublish.repositories')); - - $repositoriesPar = $this->container->getParameter('ezpublish.repositories'); - $this->assertEquals(count($repositories), count($repositoriesPar)); - - foreach ($repositoriesPar as $key => $repo) { - $this->assertArrayHasKey($key, $expectedRepositories); - $this->assertArrayHasKey('fields_groups', $repo); - $this->assertEqualsCanonicalizing($expectedRepositories[$key]['fields_groups'], $repo['fields_groups'], 'Invalid fields groups element'); - } - } - - public function repositoriesConfigurationFieldGroupsProvider() - { - return [ - //empty config - [ - ['main' => null], - ['main' => [ - 'fields_groups' => [ - 'list' => ['content', 'metadata'], - 'default' => '%ezsettings.default.content.field_groups.default%', - ], - ], - ], - ], - //single item with custom fields - [ - ['foo' => [ - 'fields_groups' => [ - 'list' => ['bar', 'baz', 'john'], - 'default' => 'bar', - ], - ], - ], - ['foo' => [ - 'fields_groups' => [ - 'list' => ['bar', 'baz', 'john'], - 'default' => 'bar', - ], - ], - ], - ], - //mixed item with custom config and empty item - [ - [ - 'foo' => [ - 'fields_groups' => [ - 'list' => ['bar', 'baz', 'john', 'doe'], - 'default' => 'bar', - ], - ], - 'anotherone' => null, - ], - [ - 'foo' => [ - 'fields_groups' => [ - 'list' => ['bar', 'baz', 'john', 'doe'], - 'default' => 'bar', - ], - ], - 'anotherone' => [ - 'fields_groups' => [ - 'list' => ['content', 'metadata'], - 'default' => '%ezsettings.default.content.field_groups.default%', - ], - ], - ], - ], - //items with only one field configured - [ - [ - 'foo' => [ - 'fields_groups' => [ - 'list' => ['bar', 'baz', 'john'], - ], - ], - 'bar' => [ - 'fields_groups' => [ - 'default' => 'metadata', - ], - ], - ], - [ - 'foo' => [ - 'fields_groups' => [ - 'list' => ['bar', 'baz', 'john'], - 'default' => '%ezsettings.default.content.field_groups.default%', - ], - ], - 'bar' => [ - 'fields_groups' => [ - 'list' => ['content', 'metadata'], - 'default' => 'metadata', - ], - ], - ], - ], - //two different repositories - [ - [ - 'foo' => [ - 'fields_groups' => [ - 'list' => ['bar', 'baz', 'john', 'doe'], - 'default' => 'bar', - ], - ], - 'bar' => [ - 'fields_groups' => [ - 'list' => ['lorem', 'ipsum'], - 'default' => 'lorem', - ], - ], - ], - [ - 'foo' => [ - 'fields_groups' => [ - 'list' => ['bar', 'baz', 'john', 'doe'], - 'default' => 'bar', - ], - ], - 'bar' => [ - 'fields_groups' => [ - 'list' => ['lorem', 'ipsum'], - 'default' => 'lorem', - ], - ], - ], - ], - ]; - } - - public function testRepositoriesConfigurationEmpty() - { - $repositories = [ - 'main' => null, - ]; - $expectedRepositories = [ - 'main' => [ - 'storage' => [ - 'engine' => '%ezpublish.api.storage_engine.default%', - 'connection' => null, - 'config' => [], - ], - 'search' => [ - 'engine' => '%ezpublish.api.search_engine.default%', - 'connection' => null, - 'config' => [], - ], - 'fields_groups' => [ - 'list' => ['content', 'metadata'], - 'default' => '%ezsettings.default.content.field_groups.default%', - ], - 'options' => [ - 'default_version_archive_limit' => 5, - 'remove_archived_versions_on_publish' => true, - ], - ], - ]; - $this->load(['repositories' => $repositories]); - $this->assertTrue($this->container->hasParameter('ezpublish.repositories')); - - $this->assertSame( - $expectedRepositories, - $this->container->getParameter('ezpublish.repositories') - ); - } - - public function testRepositoriesConfigurationStorageEmpty() - { - $repositories = [ - 'main' => [ - 'search' => [ - 'engine' => 'fantasticfind', - 'connection' => 'french', - ], - ], - ]; - $expectedRepositories = [ - 'main' => [ - 'search' => [ - 'engine' => 'fantasticfind', - 'connection' => 'french', - 'config' => [], - ], - 'storage' => [ - 'engine' => '%ezpublish.api.storage_engine.default%', - 'connection' => null, - 'config' => [], - ], - 'fields_groups' => [ - 'list' => ['content', 'metadata'], - 'default' => '%ezsettings.default.content.field_groups.default%', - ], - 'options' => [ - 'default_version_archive_limit' => 5, - 'remove_archived_versions_on_publish' => true, - ], - ], - ]; - $this->load(['repositories' => $repositories]); - $this->assertTrue($this->container->hasParameter('ezpublish.repositories')); - - $this->assertSame( - $expectedRepositories, - $this->container->getParameter('ezpublish.repositories') - ); - } - - public function testRepositoriesConfigurationSearchEmpty() - { - $repositories = [ - 'main' => [ - 'storage' => [ - 'engine' => 'persistentprudence', - 'connection' => 'yes', - ], - ], - ]; - $expectedRepositories = [ - 'main' => [ - 'storage' => [ - 'engine' => 'persistentprudence', - 'connection' => 'yes', - 'config' => [], - ], - 'search' => [ - 'engine' => '%ezpublish.api.search_engine.default%', - 'connection' => null, - 'config' => [], - ], - 'fields_groups' => [ - 'list' => ['content', 'metadata'], - 'default' => '%ezsettings.default.content.field_groups.default%', - ], - 'options' => [ - 'default_version_archive_limit' => 5, - 'remove_archived_versions_on_publish' => true, - ], - ], - ]; - $this->load(['repositories' => $repositories]); - $this->assertTrue($this->container->hasParameter('ezpublish.repositories')); - - $this->assertSame( - $expectedRepositories, - $this->container->getParameter('ezpublish.repositories') - ); - } - - public function testRepositoriesConfigurationCompatibility() - { - $repositories = [ - 'main' => [ - 'engine' => 'legacy', - 'connection' => 'default', - 'search' => [ - 'engine' => 'legacy', - 'connection' => 'blabla', - ], - ], - 'foo' => [ - 'engine' => 'sqlng', - 'connection' => 'default', - 'search' => [ - 'engine' => 'solr', - 'connection' => 'lalala', - ], - ], - ]; - $expectedRepositories = [ - 'main' => [ - 'search' => [ - 'engine' => 'legacy', - 'connection' => 'blabla', - 'config' => [], - ], - 'storage' => [ - 'engine' => 'legacy', - 'connection' => 'default', - 'config' => [], - ], - 'fields_groups' => [ - 'list' => ['content', 'metadata'], - 'default' => '%ezsettings.default.content.field_groups.default%', - ], - 'options' => [ - 'default_version_archive_limit' => 5, - 'remove_archived_versions_on_publish' => true, - ], - ], - 'foo' => [ - 'search' => [ - 'engine' => 'solr', - 'connection' => 'lalala', - 'config' => [], - ], - 'storage' => [ - 'engine' => 'sqlng', - 'connection' => 'default', - 'config' => [], - ], - 'fields_groups' => [ - 'list' => ['content', 'metadata'], - 'default' => '%ezsettings.default.content.field_groups.default%', - ], - 'options' => [ - 'default_version_archive_limit' => 5, - 'remove_archived_versions_on_publish' => true, - ], - ], - ]; - $this->load(['repositories' => $repositories]); - $this->assertTrue($this->container->hasParameter('ezpublish.repositories')); - - $this->assertSame( - $expectedRepositories, - $this->container->getParameter('ezpublish.repositories') - ); - } - - public function testRepositoriesConfigurationCompatibility2() - { - $repositories = [ - 'main' => [ - 'engine' => 'legacy', - 'connection' => 'default', - ], - ]; - $expectedRepositories = [ - 'main' => [ - 'storage' => [ - 'engine' => 'legacy', - 'connection' => 'default', - 'config' => [], - ], - 'search' => [ - 'engine' => '%ezpublish.api.search_engine.default%', - 'connection' => null, - 'config' => [], - ], - 'fields_groups' => [ - 'list' => ['content', 'metadata'], - 'default' => '%ezsettings.default.content.field_groups.default%', - ], - 'options' => [ - 'default_version_archive_limit' => 5, - 'remove_archived_versions_on_publish' => true, - ], - ], - ]; - $this->load(['repositories' => $repositories]); - $this->assertTrue($this->container->hasParameter('ezpublish.repositories')); - - $this->assertSame( - $expectedRepositories, - $this->container->getParameter('ezpublish.repositories') - ); - } - - public function testRegisteredPolicies() - { - $this->load(); - self::assertContainerBuilderHasParameter('ezpublish.api.role.policy_map'); - $previousPolicyMap = $this->container->getParameter('ezpublish.api.role.policy_map'); - - $policies1 = [ - 'custom_module' => [ - 'custom_function_1' => null, - 'custom_function_2' => ['CustomLimitation'], - ], - 'helloworld' => [ - 'foo' => ['bar'], - 'baz' => null, - ], - ]; - $this->extension->addPolicyProvider(new StubPolicyProvider($policies1)); - - $policies2 = [ - 'custom_module2' => [ - 'custom_function_3' => null, - 'custom_function_4' => ['CustomLimitation2', 'CustomLimitation3'], - ], - 'helloworld' => [ - 'foo' => ['additional_limitation'], - 'some' => ['thingy', 'thing', 'but', 'wait'], - ], - ]; - $this->extension->addPolicyProvider(new StubPolicyProvider($policies2)); - - $expectedPolicies = [ - 'custom_module' => [ - 'custom_function_1' => [], - 'custom_function_2' => ['CustomLimitation' => true], - ], - 'helloworld' => [ - 'foo' => ['bar' => true, 'additional_limitation' => true], - 'baz' => [], - 'some' => ['thingy' => true, 'thing' => true, 'but' => true, 'wait' => true], - ], - 'custom_module2' => [ - 'custom_function_3' => [], - 'custom_function_4' => ['CustomLimitation2' => true, 'CustomLimitation3' => true], - ], - ]; - - $this->load(); - self::assertContainerBuilderHasParameter('ezpublish.api.role.policy_map'); - $expectedPolicies = array_merge_recursive($expectedPolicies, $previousPolicyMap); - self::assertEquals($expectedPolicies, $this->container->getParameter('ezpublish.api.role.policy_map')); - } - - public function testUrlAliasConfiguration() - { - $configuration = [ - 'transformation' => 'urlalias_lowercase', - 'separator' => 'dash', - 'transformation_groups' => [ - 'urlalias' => [ - 'commands' => [ - 'ascii_lowercase', - 'cyrillic_lowercase', - ], - 'cleanup_method' => 'url_cleanup', - ], - 'urlalias_compact' => [ - 'commands' => [ - 'greek_normalize', - 'exta_lowercase', - ], - 'cleanup_method' => 'compact_cleanup', - ], - ], - ]; - $this->load([ - 'url_alias' => [ - 'slug_converter' => $configuration, - ], - ]); - $parsedConfig = $this->container->getParameter('ezpublish.url_alias.slug_converter'); - $this->assertSame( - $configuration, - $parsedConfig - ); - } - - /** - * Test automatic configuration of services implementing QueryType interface. - * - * @see \eZ\Publish\Core\QueryType\QueryType - */ - public function testQueryTypeAutomaticConfiguration(): void - { - $definition = new Definition(TestQueryType::class); - $definition->setAutoconfigured(true); - $this->setDefinition(TestQueryType::class, $definition); - - $this->load(); - - $this->compileCoreContainer(); - - $this->assertContainerBuilderHasServiceDefinitionWithTag( - TestQueryType::class, - QueryTypePass::QUERY_TYPE_SERVICE_TAG - ); - } - - /** - * Test automatic configuration of services implementing Criterion & SortClause Filtering Query - * Builders. - * - * @dataProvider getFilteringQueryBuilderData - * - * @see \eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder - * @see \eZ\Publish\SPI\Repository\Values\Filter\SortClauseQueryBuilder - */ - public function testFilteringQueryBuildersAutomaticConfiguration( - string $classFQCN, - string $tagName - ): void { - $definition = new Definition($classFQCN); - $definition->setAutoconfigured(true); - $this->setDefinition($classFQCN, $definition); - - $this->load(); - - $this->compileCoreContainer(); - - $this->assertContainerBuilderHasServiceDefinitionWithTag( - $classFQCN, - $tagName - ); - } - - /** - * Data provider for {@see testFilteringQueryBuildersAutomaticConfiguration}. - */ - public function getFilteringQueryBuilderData(): iterable - { - yield Filter\CriterionQueryBuilder::class => [ - CustomCriterionQueryBuilder::class, - ServiceTags::FILTERING_CRITERION_QUERY_BUILDER, - ]; - - yield Filter\SortClauseQueryBuilder::class => [ - CustomSortClauseQueryBuilder::class, - ServiceTags::FILTERING_SORT_CLAUSE_QUERY_BUILDER, - ]; - } - - public function testDoesNotLoadTestServicesByDefault(): void - { - $this->load(); - $this->assertContainerBuilderNotHasService(QueryControllerContext::class); - } - - public function testLoadsTestServicesWhenParameterIsSpecified(): void - { - $this->container->setParameter('ibexa.testing.browser.enabled', true); - $this->load(); - $this->assertContainerBuilderHasService(QueryControllerContext::class); - } - - /** - * Prepare Core Container for compilation by mocking required parameters and compile it. - */ - private function compileCoreContainer(): void - { - $this->disableCheckExceptionOnInvalidReferenceBehaviorPass(); - $this->container->setParameter('webroot_dir', __DIR__); - $this->container->setParameter('kernel.project_dir', __DIR__); - $this->container->setParameter('kernel.cache_dir', __DIR__ . '/cache'); - $this->container->setParameter('kernel.debug', false); - $this->compile(); - } - - final public function disableCheckExceptionOnInvalidReferenceBehaviorPass(): void - { - $compilerPassConfig = $this->container->getCompilerPassConfig(); - $compilerPassConfig->setAfterRemovingPasses( - array_filter( - $compilerPassConfig->getAfterRemovingPasses(), - static function (CompilerPassInterface $pass) { - return !($pass instanceof CheckExceptionOnInvalidReferenceBehaviorPass); - } - ) - ); - } - - protected function getCoreExtension(): EzPublishCoreExtension - { - if (null !== $this->extension) { - return $this->extension; - } - - $this->extension = new EzPublishCoreExtension([new Common(), new Content()]); - - return $this->extension; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_image.yml b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_image.yml deleted file mode 100644 index db6a37623f..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_image.yml +++ /dev/null @@ -1,52 +0,0 @@ -siteaccess: - default_siteaccess: ezdemo_site - list: - - ezdemo_site - - fre - - ezdemo_site_admin - groups: - ezdemo_group: - - ezdemo_site - - fre - - ezdemo_site_admin - ezdemo_frontend_group: - - ezdemo_site - - fre - match: - URIElement: 1 - Map\URI: - the_front: ezdemo_site - the_back: ezdemo_site_admin - -system: - ezdemo_group: - image_variations: - small: - reference: null - filters: - - { name: geometry/scaledownonly, params: [100, 160] } - medium: - reference: null - filters: - - { name: geometry/scaledownonly, params: [200, 290] } - listitem: - reference: null - filters: - - { name: geometry/scaledownonly, params: [130, 190] } - - fre: - image_variations: - test_browse: - reference: reference - filters: - - { name: geometry/scaledownonly, params: [200, 200] } - -imagemagick: - enabled: false - -http_cache: - purge_type: local - -router: - default_router: - non_siteaccess_aware_routes: ['foo_route', 'my_prefix_'] diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_minimal.yml b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_minimal.yml deleted file mode 100644 index c480e573af..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_minimal.yml +++ /dev/null @@ -1,30 +0,0 @@ -siteaccess: - default_siteaccess: ezdemo_site - list: - - ezdemo_site - - fre - - ezdemo_site_admin - groups: - ezdemo_group: - - ezdemo_site - - fre - - ezdemo_site_admin - ezdemo_frontend_group: - - ezdemo_site - - fre - empty_group: [] - match: - URIElement: 1 - Map\URI: - the_front: ezdemo_site - the_back: ezdemo_site_admin - -imagemagick: - enabled: false - -http_cache: - purge_type: local - -router: - default_router: - non_siteaccess_aware_routes: ['foo_route', 'my_prefix_'] diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_templates.yml b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_templates.yml deleted file mode 100644 index ded53c8f75..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_templates.yml +++ /dev/null @@ -1,45 +0,0 @@ -siteaccess: - default_siteaccess: ezdemo_site - list: - - ezdemo_site - - fre - - ezdemo_site_admin - groups: - ezdemo_group: - - ezdemo_site - - fre - - ezdemo_site_admin - ezdemo_frontend_group: - - ezdemo_site - - fre - match: - URIElement: 1 - Map\URI: - the_front: ezdemo_site - the_back: ezdemo_site_admin - -system: - ezdemo_frontend_group: - field_templates: - - { template: "my_field_template.html.twig", priority: 123 } - - { template: "another_template.html.twig" } - - fielddefinition_settings_templates: - - { template: "my_field_template.html.twig", priority: 123 } - - { template: "another_template.html.twig" } - - ezdemo_site: - field_templates: - - { template: "ezdemo_site_template.html.twig", priority: 123 } - fielddefinition_settings_templates: - - { template: "ezdemo_site_template.html.twig", priority: 123 } - -imagemagick: - enabled: false - -http_cache: - purge_type: local - -router: - default_router: - non_siteaccess_aware_routes: ['foo_route', 'my_prefix_'] diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_view.yml b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_view.yml deleted file mode 100644 index fabe9e563f..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_view.yml +++ /dev/null @@ -1,109 +0,0 @@ -siteaccess: - default_siteaccess: ezdemo_site - list: - - ezdemo_site - - fre - - ezdemo_site_admin - groups: - ezdemo_group: - - ezdemo_site - - fre - - ezdemo_site_admin - ezdemo_frontend_group: - - ezdemo_site - - fre - match: - URIElement: 1 - Map\URI: - the_front: ezdemo_site - the_back: ezdemo_site_admin - -system: - ezdemo_frontend_group: - location_view: - full: - article: - controller: "eZDemoBundle:Demo:showArticle" - template: "eZDemoBundle:full:article.html.twig" - match: - Identifier\ContentType: [article] - # There are two ways to add extra information to your response using a custom controller - blog: - # Fully customized, handling everything yourself - controller: "eZDemoBundle:Demo:listBlogPosts" - match: - Identifier\ContentType: [blog] - blog_post: - # Enriched controller, only adding extra parameters - controller: "eZDemoBundle:Demo:showBlogPost" - # Overriding the template used by the default viewLocation - template: "eZDemoBundle:full:blog_post.html.twig" - match: - Identifier\ContentType: [blog_post] - frontpage: - template: "eZDemoBundle:full:landing_page.html.twig" - match: - Identifier\ContentType: "landing_page" - params: - foo: bar - zorglub: 123 - - line: - article: - template: "eZDemoBundle:line:article.html.twig" - match: - Identifier\ContentType: [article] - blog_post: - controller: "eZDemoBundle:Demo:showBlogPost" - template: "eZDemoBundle:line:blog_post.html.twig" - match: - Identifier\ContentType: [blog_post] - - content_view: - full: - article: - controller: "eZDemoBundle:Demo:showArticle" - template: "eZDemoBundle:full:article.html.twig" - match: - Identifier\ContentType: [article] - # There are two ways to add extra information to your response using a custom controller - blog: - # Fully customized, handling everything yourself - controller: "eZDemoBundle:Demo:listBlogPosts" - match: - Identifier\ContentType: [blog] - blog_post: - # Enriched controller, only adding extra parameters - controller: "eZDemoBundle:Demo:showBlogPost" - # Overriding the template used by the default viewLocation - template: "eZDemoBundle:full:blog_post.html.twig" - match: - Identifier\ContentType: [blog_post] - params: - foo: bar - zorglub: 123 - frontpage: - template: "eZDemoBundle:full:landing_page.html.twig" - match: - Identifier\ContentType: "landing_page" - - line: - article: - template: "eZDemoBundle:line:article.html.twig" - match: - Identifier\ContentType: [article] - blog_post: - controller: "eZDemoBundle:Demo:showBlogPost" - template: "eZDemoBundle:line:blog_post.html.twig" - match: - Identifier\ContentType: [blog_post] - -imagemagick: - enabled: false - -http_cache: - purge_type: local - -router: - default_router: - non_siteaccess_aware_routes: ['foo_route', 'my_prefix_'] diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/parameters.yml b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/parameters.yml deleted file mode 100644 index ae04afbaa9..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/parameters.yml +++ /dev/null @@ -1,2 +0,0 @@ -parameters: - kernel.environment: unit diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Stub/AnnotationEntityBundle/AnnotationEntityBundle.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Stub/AnnotationEntityBundle/AnnotationEntityBundle.php deleted file mode 100644 index 9434f2fb41..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Stub/AnnotationEntityBundle/AnnotationEntityBundle.php +++ /dev/null @@ -1,15 +0,0 @@ -policies = $policies; - } - - public function addPolicies(ConfigBuilderInterface $configBuilder) - { - $configBuilder->addConfig($this->policies); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Stub/StubYamlPolicyProvider.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Stub/StubYamlPolicyProvider.php deleted file mode 100644 index 0e5a7f8690..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Stub/StubYamlPolicyProvider.php +++ /dev/null @@ -1,25 +0,0 @@ -files = $files; - } - - protected function getFiles() - { - return $this->files; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Stub/XmlEntityBundle/XmlEntityBundle.php b/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Stub/XmlEntityBundle/XmlEntityBundle.php deleted file mode 100644 index 04e1168fcc..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Stub/XmlEntityBundle/XmlEntityBundle.php +++ /dev/null @@ -1,15 +0,0 @@ -dataLoader = $this->createMock(LoaderInterface::class); - $this->filterManager = $this - ->getMockBuilder(FilterManager::class) - ->disableOriginalConstructor() - ->getMock(); - $this->ioResolver = $this->createMock(ResolverInterface::class); - $this->filterConfiguration = new FilterConfiguration(); - $this->logger = $this->createMock(LoggerInterface::class); - $this->imagine = $this->createMock(ImagineInterface::class); - $this->box = $this->createMock(BoxInterface::class); - $this->image = $this->createMock(ImageInterface::class); - $this->ioService = $this->createMock(IOServiceInterface::class); - $this->variationPathGenerator = $this->createMock(VariationPathGenerator::class); - $this->aliasGenerator = new AliasGenerator( - $this->dataLoader, - $this->filterManager, - $this->ioResolver, - $this->filterConfiguration, - $this->logger - ); - $this->decoratedAliasGenerator = new ImagineAwareAliasGenerator( - $this->aliasGenerator, - $this->variationPathGenerator, - $this->ioService, - $this->imagine - ); - } - - /** - * @dataProvider supportsValueProvider - * - * @param \eZ\Publish\SPI\FieldType\Value $value - * @param bool $isSupported - */ - public function testSupportsValue($value, $isSupported) - { - $this->assertSame($isSupported, $this->aliasGenerator->supportsValue($value)); - } - - /** - * Data provider for testSupportsValue. - * - * @see testSupportsValue - * - * @return array - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function supportsValueProvider() - { - return [ - [$this->createMock(FieldTypeValue::class), false], - [new TextLineValue(), false], - [new ImageValue(), true], - [$this->createMock(ImageValue::class), true], - ]; - } - - public function testGetVariationWrongValue() - { - $this->expectException(\InvalidArgumentException::class); - - $field = new Field(['value' => $this->createMock(FieldTypeValue::class)]); - $this->aliasGenerator->getVariation($field, new VersionInfo(), 'foo'); - } - - /** - * Test obtaining Image Variation that hasn't been stored yet. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType - */ - public function testGetVariationNotStored() - { - $originalPath = 'foo/bar/image.jpg'; - $variationName = 'my_variation'; - $this->filterConfiguration->set($variationName, []); - $imageId = '123-45'; - $imageWidth = 300; - $imageHeight = 300; - $expectedUrl = "http://localhost/foo/bar/image_$variationName.jpg"; - - $this->ioResolver - ->expects($this->once()) - ->method('isStored') - ->with($originalPath, $variationName) - ->will($this->returnValue(false)); - - $this->logger - ->expects($this->once()) - ->method('debug'); - - $binary = $this->createMock(BinaryInterface::class); - $this->dataLoader - ->expects($this->once()) - ->method('find') - ->with($originalPath) - ->will($this->returnValue($binary)); - $this->filterManager - ->expects($this->once()) - ->method('applyFilter') - ->with($binary, $variationName) - ->will($this->returnValue($binary)); - $this->ioResolver - ->expects($this->once()) - ->method('store') - ->with($binary, $originalPath, $variationName); - - $this->assertImageVariationIsCorrect( - $expectedUrl, - $variationName, - $imageId, - $originalPath, - $imageWidth, - $imageHeight - ); - } - - public function testGetVariationOriginal() - { - $originalPath = 'foo/bar/image.jpg'; - $variationName = 'original'; - $imageId = '123-45'; - $imageWidth = 300; - $imageHeight = 300; - // original images already contain proper width and height - $imageValue = new ImageValue( - [ - 'id' => $originalPath, - 'imageId' => $imageId, - 'width' => $imageWidth, - 'height' => $imageHeight, - ] - ); - $field = new Field(['value' => $imageValue]); - $expectedUrl = 'http://localhost/foo/bar/image.jpg'; - - $this->ioResolver - ->expects($this->never()) - ->method('isStored') - ->with($originalPath, $variationName) - ->will($this->returnValue(false)); - - $this->logger - ->expects($this->once()) - ->method('debug'); - - $this->ioResolver - ->expects($this->once()) - ->method('resolve') - ->with($originalPath, $variationName) - ->will($this->returnValue($expectedUrl)); - - $expected = new ImageVariation( - [ - 'name' => $variationName, - 'fileName' => 'image.jpg', - 'dirPath' => 'http://localhost/foo/bar', - 'uri' => $expectedUrl, - 'imageId' => $imageId, - 'height' => $imageHeight, - 'width' => $imageWidth, - ] - ); - $this->assertEquals($expected, $this->decoratedAliasGenerator->getVariation($field, new VersionInfo(), $variationName)); - } - - /** - * Test obtaining Image Variation that hasn't been stored yet and has multiple references. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType - */ - public function testGetVariationNotStoredHavingReferences() - { - $originalPath = 'foo/bar/image.jpg'; - $variationName = 'my_variation'; - $reference1 = 'reference1'; - $reference2 = 'reference2'; - $configVariation = ['reference' => $reference1]; - $configReference1 = ['reference' => $reference2]; - $configReference2 = []; - $this->filterConfiguration->set($variationName, $configVariation); - $this->filterConfiguration->set($reference1, $configReference1); - $this->filterConfiguration->set($reference2, $configReference2); - $imageId = '123-45'; - $imageWidth = 300; - $imageHeight = 300; - $expectedUrl = "http://localhost/foo/bar/image_$variationName.jpg"; - - $this->ioResolver - ->expects($this->once()) - ->method('isStored') - ->with($originalPath, $variationName) - ->will($this->returnValue(false)); - - $this->logger - ->expects($this->once()) - ->method('debug'); - - $binary = $this->createMock(BinaryInterface::class); - $this->dataLoader - ->expects($this->once()) - ->method('find') - ->with($originalPath) - ->will($this->returnValue($binary)); - - // Filter manager is supposed to be called 3 times to generate references, and then passed variation. - $this->filterManager - ->expects($this->at(0)) - ->method('applyFilter') - ->with($binary, $reference2) - ->will($this->returnValue($binary)); - $this->filterManager - ->expects($this->at(1)) - ->method('applyFilter') - ->with($binary, $reference1) - ->will($this->returnValue($binary)); - $this->filterManager - ->expects($this->at(2)) - ->method('applyFilter') - ->with($binary, $variationName) - ->will($this->returnValue($binary)); - - $this->ioResolver - ->expects($this->once()) - ->method('store') - ->with($binary, $originalPath, $variationName); - - $this->assertImageVariationIsCorrect( - $expectedUrl, - $variationName, - $imageId, - $originalPath, - $imageWidth, - $imageHeight - ); - } - - /** - * Test obtaining Image Variation that has been stored already. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType - */ - public function testGetVariationAlreadyStored() - { - $originalPath = 'foo/bar/image.jpg'; - $variationName = 'my_variation'; - $imageId = '123-45'; - $imageWidth = 300; - $imageHeight = 300; - $expectedUrl = "http://localhost/foo/bar/image_$variationName.jpg"; - - $this->ioResolver - ->expects($this->once()) - ->method('isStored') - ->with($originalPath, $variationName) - ->will($this->returnValue(true)); - - $this->logger - ->expects($this->once()) - ->method('debug'); - - $this->dataLoader - ->expects($this->never()) - ->method('find'); - $this->filterManager - ->expects($this->never()) - ->method('applyFilter'); - $this->ioResolver - ->expects($this->never()) - ->method('store'); - - $this->assertImageVariationIsCorrect( - $expectedUrl, - $variationName, - $imageId, - $originalPath, - $imageWidth, - $imageHeight - ); - } - - public function testGetVariationOriginalNotFound() - { - $this->expectException(\eZ\Publish\Core\MVC\Exception\SourceImageNotFoundException::class); - - $this->dataLoader - ->expects($this->once()) - ->method('find') - ->will($this->throwException(new NotLoadableException())); - - $field = new Field(['value' => new ImageValue()]); - $this->aliasGenerator->getVariation($field, new VersionInfo(), 'foo'); - } - - public function testGetVariationInvalidVariation() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidVariationException::class); - - $originalPath = 'foo/bar/image.jpg'; - $variationName = 'my_variation'; - $imageId = '123-45'; - $imageValue = new ImageValue(['id' => $originalPath, 'imageId' => $imageId]); - $field = new Field(['value' => $imageValue]); - - $this->ioResolver - ->expects($this->once()) - ->method('isStored') - ->with($originalPath, $variationName) - ->will($this->returnValue(true)); - - $this->logger - ->expects($this->once()) - ->method('debug'); - - $this->dataLoader - ->expects($this->never()) - ->method('find'); - $this->filterManager - ->expects($this->never()) - ->method('applyFilter'); - $this->ioResolver - ->expects($this->never()) - ->method('store'); - - $this->ioResolver - ->expects($this->once()) - ->method('resolve') - ->with($originalPath, $variationName) - ->will($this->throwException(new NotResolvableException())); - - $this->aliasGenerator->getVariation($field, new VersionInfo(), $variationName); - } - - /** - * Prepare required Imagine-related mocks and assert that the Image Variation is as expected. - * - * @param string $expectedUrl - * @param string $variationName - * @param string $imageId - * @param string $originalPath - * @param int $imageWidth - * @param int $imageHeight - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType - */ - protected function assertImageVariationIsCorrect( - $expectedUrl, - $variationName, - $imageId, - $originalPath, - $imageWidth, - $imageHeight - ) { - $imageValue = new ImageValue(['id' => $originalPath, 'imageId' => $imageId]); - $field = new Field(['value' => $imageValue]); - - $binaryFile = new \eZ\Publish\Core\IO\Values\BinaryFile( - [ - 'uri' => "_aliases/{$variationName}/foo/bar/image.jpg", - ] - ); - - $this->ioResolver - ->expects($this->once()) - ->method('resolve') - ->with($originalPath, $variationName) - ->will($this->returnValue($expectedUrl)); - - $this->variationPathGenerator - ->expects($this->once()) - ->method('getVariationPath') - ->with($originalPath, $variationName) - ->willReturn($binaryFile->uri); - - $this->ioService - ->expects($this->once()) - ->method('loadBinaryFile') - ->withAnyParameters() - ->willReturn($binaryFile); - - $this->ioService - ->expects($this->once()) - ->method('getFileContents') - ->with($binaryFile) - ->willReturn('file contents mock'); - - $this->imagine - ->expects($this->once()) - ->method('load') - ->with('file contents mock') - ->will($this->returnValue($this->image)); - $this->image - ->expects($this->once()) - ->method('getSize') - ->will($this->returnValue($this->box)); - - $this->box - ->expects($this->once()) - ->method('getWidth') - ->will($this->returnValue($imageWidth)); - $this->box - ->expects($this->once()) - ->method('getHeight') - ->will($this->returnValue($imageHeight)); - - $expected = new ImageVariation( - [ - 'name' => $variationName, - 'fileName' => "image_$variationName.jpg", - 'dirPath' => 'http://localhost/foo/bar', - 'uri' => $expectedUrl, - 'imageId' => $imageId, - 'height' => $imageHeight, - 'width' => $imageWidth, - ] - ); - $this->assertEquals( - $expected, - $this->decoratedAliasGenerator->getVariation($field, new VersionInfo(), $variationName) - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ReduceNoiseFilterLoaderTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ReduceNoiseFilterLoaderTest.php deleted file mode 100644 index 29740933ef..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ReduceNoiseFilterLoaderTest.php +++ /dev/null @@ -1,35 +0,0 @@ -filter = $this->createMock(FilterInterface::class); - $this->loader = new ReduceNoiseFilterLoader($this->filter); - } - - public function testLoadInvalidDriver() - { - $this->expectException(\Imagine\Exception\NotSupportedException::class); - - $this->loader->load($this->createMock(ImageInterface::class)); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoaderTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoaderTest.php deleted file mode 100644 index addb68a9e5..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoaderTest.php +++ /dev/null @@ -1,49 +0,0 @@ -innerLoader = $this->createMock(LoaderInterface::class); - $this->loader = new ScaleHeightDownOnlyFilterLoader(); - $this->loader->setInnerLoader($this->innerLoader); - } - - public function testLoadInvalid() - { - $this->expectException(\Imagine\Exception\InvalidArgumentException::class); - - $this->loader->load($this->createMock(ImageInterface::class), []); - } - - public function testLoad() - { - $height = 123; - $image = $this->createMock(ImageInterface::class); - $this->innerLoader - ->expects($this->once()) - ->method('load') - ->with($image, $this->equalTo(['size' => [null, $height], 'mode' => 'inset'])) - ->will($this->returnValue($image)); - - $this->assertSame($image, $this->loader->load($image, [$height])); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleHeightFilterLoaderTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleHeightFilterLoaderTest.php deleted file mode 100644 index a0317e91cc..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleHeightFilterLoaderTest.php +++ /dev/null @@ -1,49 +0,0 @@ -innerLoader = $this->createMock(LoaderInterface::class); - $this->loader = new ScaleHeightFilterLoader(); - $this->loader->setInnerLoader($this->innerLoader); - } - - public function testLoadFail() - { - $this->expectException(\Imagine\Exception\InvalidArgumentException::class); - - $this->loader->load($this->createMock(ImageInterface::class, [])); - } - - public function testLoad() - { - $height = 123; - $image = $this->createMock(ImageInterface::class); - $this->innerLoader - ->expects($this->once()) - ->method('load') - ->with($image, $this->equalTo(['heighten' => $height])) - ->will($this->returnValue($image)); - - $this->assertSame($image, $this->loader->load($image, [$height])); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoaderTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoaderTest.php deleted file mode 100644 index eeab5d9641..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoaderTest.php +++ /dev/null @@ -1,49 +0,0 @@ -innerLoader = $this->createMock(LoaderInterface::class); - $this->loader = new ScaleWidthDownOnlyFilterLoader(); - $this->loader->setInnerLoader($this->innerLoader); - } - - public function testLoadInvalid() - { - $this->expectException(\Imagine\Exception\InvalidArgumentException::class); - - $this->loader->load($this->createMock(ImageInterface::class), []); - } - - public function testLoad() - { - $width = 123; - $image = $this->createMock(ImageInterface::class); - $this->innerLoader - ->expects($this->once()) - ->method('load') - ->with($image, $this->equalTo(['size' => [$width, null], 'mode' => 'inset'])) - ->will($this->returnValue($image)); - - $this->assertSame($image, $this->loader->load($image, [$width])); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleWidthFilterLoaderTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleWidthFilterLoaderTest.php deleted file mode 100644 index 0eb5144cb1..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleWidthFilterLoaderTest.php +++ /dev/null @@ -1,49 +0,0 @@ -innerLoader = $this->createMock(LoaderInterface::class); - $this->loader = new ScaleWidthFilterLoader(); - $this->loader->setInnerLoader($this->innerLoader); - } - - public function testLoadFail() - { - $this->expectException(\Imagine\Exception\InvalidArgumentException::class); - - $this->loader->load($this->createMock(ImageInterface::class, [])); - } - - public function testLoad() - { - $width = 123; - $image = $this->createMock(ImageInterface::class); - $this->innerLoader - ->expects($this->once()) - ->method('load') - ->with($image, $this->equalTo(['widen' => $width])) - ->will($this->returnValue($image)); - - $this->assertSame($image, $this->loader->load($image, [$width])); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/UnsupportedFilterTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/UnsupportedFilterTest.php deleted file mode 100644 index 182e2b4d42..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/UnsupportedFilterTest.php +++ /dev/null @@ -1,21 +0,0 @@ -expectException(\Imagine\Exception\NotSupportedException::class); - - $filter = new UnsupportedFilter(); - $filter->apply($this->createMock(ImageInterface::class)); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/ImageAsset/AliasGeneratorTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/ImageAsset/AliasGeneratorTest.php deleted file mode 100644 index 0e1bd0c264..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/ImageAsset/AliasGeneratorTest.php +++ /dev/null @@ -1,142 +0,0 @@ -innerAliasGenerator = $this->createMock(VariationHandler::class); - $this->contentService = $this->createMock(ContentService::class); - $this->assetMapper = $this->createMock(ImageAsset\AssetMapper::class); - - $this->aliasGenerator = new AliasGenerator( - $this->innerAliasGenerator, - $this->contentService, - $this->assetMapper - ); - } - - public function testGetVariationOfImageAsset() - { - $assetField = new Field([ - 'value' => new ImageAsset\Value(486), - ]); - $imageField = new Field([ - 'value' => new Image\Value([ - 'id' => 'images/6/8/4/0/486-10-eng-GB/photo.jpg', - ]), - ]); - - $assetVersionInfo = new VersionInfo(); - $imageVersionInfo = new VersionInfo(); - $imageContent = new Content([ - 'versionInfo' => $imageVersionInfo, - ]); - - $variationName = 'thumbnail'; - $parameters = []; - - $expectedVariation = new Variation(); - - $this->contentService - ->expects($this->once()) - ->method('loadContent') - ->with($assetField->value->destinationContentId) - ->willReturn($imageContent); - - $this->assetMapper - ->expects($this->once()) - ->method('getAssetField') - ->with($imageContent) - ->willReturn($imageField); - - $this->innerAliasGenerator - ->expects($this->once()) - ->method('getVariation') - ->with($imageField, $imageVersionInfo, $variationName, $parameters) - ->willReturn($expectedVariation); - - $actualVariation = $this->aliasGenerator->getVariation( - $assetField, - $assetVersionInfo, - $variationName, - $parameters - ); - - $this->assertEquals($expectedVariation, $actualVariation); - } - - public function testGetVariationOfNonImageAsset() - { - $imageField = new Field([ - 'value' => new Image\Value([ - 'id' => 'images/6/8/4/0/486-10-eng-GB/photo.jpg', - ]), - ]); - - $imageVersionInfo = new VersionInfo(); - $variationName = 'thumbnail'; - $parameters = []; - - $expectedVariation = new Variation(); - - $this->contentService - ->expects($this->never()) - ->method('loadContent'); - - $this->assetMapper - ->expects($this->never()) - ->method('getAssetField'); - - $this->innerAliasGenerator - ->expects($this->once()) - ->method('getVariation') - ->with($imageField, $imageVersionInfo, $variationName, $parameters) - ->willReturn($expectedVariation); - - $actualVariation = $this->aliasGenerator->getVariation( - $imageField, - $imageVersionInfo, - $variationName, - $parameters - ); - - $this->assertEquals($expectedVariation, $actualVariation); - } - - public function testSupport() - { - $this->assertTrue($this->aliasGenerator->supportsValue(new ImageAsset\Value())); - $this->assertFalse($this->aliasGenerator->supportsValue(new Image\Value())); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPathGenerator/AliasDirectoryVariationPathGeneratorTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPathGenerator/AliasDirectoryVariationPathGeneratorTest.php deleted file mode 100644 index e35f1f6cf6..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPathGenerator/AliasDirectoryVariationPathGeneratorTest.php +++ /dev/null @@ -1,23 +0,0 @@ -getVariationPath('path/to/original.png', 'large') - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGeneratorTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGeneratorTest.php deleted file mode 100644 index 6ae8c0e86a..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGeneratorTest.php +++ /dev/null @@ -1,22 +0,0 @@ -getVariationPath('path/to/original.png', 'large') - ); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPurger/LegacyStorageImageFileListTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPurger/LegacyStorageImageFileListTest.php deleted file mode 100644 index fb383ffdde..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPurger/LegacyStorageImageFileListTest.php +++ /dev/null @@ -1,82 +0,0 @@ -rowReaderMock = $this->createMock(ImageFileRowReader::class); - $this->ioConfigResolverMock = $this->createMock(IOConfigProvider::class); - $this->ioConfigResolverMock - ->method('getLegacyUrlPrefix') - ->willReturn('var/ezdemo_site/storage'); - $this->configResolverMock = $this->createMock(ConfigResolverInterface::class); - $this->configResolverMock - ->method('getParameter') - ->with('image.published_images_dir') - ->willReturn('images'); - - $this->fileList = new LegacyStorageImageFileList( - $this->rowReaderMock, - $this->ioConfigResolverMock, - $this->configResolverMock - ); - } - - public function testIterator() - { - $expected = [ - 'path/to/1st/image.jpg', - 'path/to/2nd/image.jpg', - ]; - $this->configureRowReaderMock($expected); - - foreach ($this->fileList as $index => $file) { - self::assertEquals($expected[$index], $file); - } - } - - /** - * Tests that the iterator transforms the ezimagefile value into a binaryfile id. - */ - public function testImageIdTransformation() - { - $this->configureRowReaderMock(['var/ezdemo_site/storage/images/path/to/1st/image.jpg']); - foreach ($this->fileList as $file) { - self::assertEquals('path/to/1st/image.jpg', $file); - } - } - - private function configureRowReaderMock(array $fileList) - { - $mockInvocator = $this->rowReaderMock->expects($this->any())->method('getRow'); - call_user_func_array([$mockInvocator, 'willReturnOnConsecutiveCalls'], $fileList); - - $this->rowReaderMock->expects($this->any())->method('getCount')->willReturn(count($fileList)); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Matcher/ViewMatcherRegistryTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Matcher/ViewMatcherRegistryTest.php deleted file mode 100644 index 501c527dd7..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Matcher/ViewMatcherRegistryTest.php +++ /dev/null @@ -1,59 +0,0 @@ -getMatcherMock(); - $registry = new ViewMatcherRegistry([self::MATCHER_NAME => $matcher]); - - $this->assertSame($matcher, $registry->getMatcher(self::MATCHER_NAME)); - } - - public function testSetMatcher(): void - { - $matcher = $this->getMatcherMock(); - $registry = new ViewMatcherRegistry(); - - $registry->setMatcher(self::MATCHER_NAME, $matcher); - - $this->assertSame($matcher, $registry->getMatcher(self::MATCHER_NAME)); - } - - public function testSetMatcherOverride(): void - { - $matcher = $this->getMatcherMock(); - $newMatcher = $this->getMatcherMock(); - $registry = new ViewMatcherRegistry([self::MATCHER_NAME, $matcher]); - - $registry->setMatcher(self::MATCHER_NAME, $newMatcher); - - $this->assertSame($newMatcher, $registry->getMatcher(self::MATCHER_NAME)); - } - - public function testGetMatcherNotFound(): void - { - $this->expectException(NotFoundException::class); - $registry = new ViewMatcherRegistry(); - - $registry->getMatcher(self::MATCHER_NAME); - } - - protected function getMatcherMock(): MatcherInterface - { - return $this->createMock(MatcherInterface::class); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/security.yaml b/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/security.yaml deleted file mode 100644 index 3d7159e9e3..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/security.yaml +++ /dev/null @@ -1,8 +0,0 @@ -security: - providers: - default: - id: ezpublish.security.user_provider - - firewalls: - main: - anonymous: ~ diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/services/fixture-services.yaml b/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/services/fixture-services.yaml deleted file mode 100644 index 01a14fcfb8..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/services/fixture-services.yaml +++ /dev/null @@ -1,26 +0,0 @@ -services: - eZ\Publish\API\Repository\Tests\LegacySchemaImporter: - alias: 'test.ibexa.schema_importer' - - test.ibexa.schema_importer: - class: eZ\Publish\API\Repository\Tests\LegacySchemaImporter - public: true - arguments: - - '@doctrine.dbal.default_connection' - - eZ\Publish\SPI\Tests\Persistence\FixtureImporter: - alias: 'test.ibexa.fixture_importer' - - test.ibexa.fixture_importer: - class: eZ\Publish\SPI\Tests\Persistence\FixtureImporter - public: true - arguments: - - '@doctrine.dbal.default_connection' - - EzSystems\DoctrineSchema\Database\DbPlatform\SqliteDbPlatform: - calls: - - [setEventManager, ['@doctrine.dbal.default_connection.event_manager']] - - EzSystems\DoctrineSchema\Database\DbPlatform\PostgreSqlDbPlatform: - calls: - - [ setEventManager, [ '@doctrine.dbal.default_connection.event_manager' ] ] diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Routing/UrlAliasRouterTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Routing/UrlAliasRouterTest.php deleted file mode 100644 index a49dfbff83..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Routing/UrlAliasRouterTest.php +++ /dev/null @@ -1,402 +0,0 @@ -configResolver = $this->createMock(ConfigResolverInterface::class); - $this->configResolver - ->expects($this->any()) - ->method('getParameter') - ->will( - $this->returnValueMap( - [ - ['url_alias_router', null, null, true], - ['content.tree_root.location_id', null, null, null], - ['content.tree_root.excluded_uri_prefixes', null, null, []], - ] - ) - ); - parent::setUp(); - } - - protected function getRouter(LocationService $locationService, URLAliasService $urlAliasService, ContentService $contentService, UrlAliasGenerator $urlAliasGenerator, RequestContext $requestContext) - { - $router = new UrlAliasRouter($locationService, $urlAliasService, $contentService, $urlAliasGenerator, $requestContext); - $router->setConfigResolver($this->configResolver); - - return $router; - } - - /** - * Resets container and configResolver mocks. - */ - protected function resetConfigResolver() - { - $this->configResolver = $this->createMock(ConfigResolverInterface::class); - $this->container = $this->createMock(ContainerInterface::class); - $this->router->setConfigResolver($this->configResolver); - } - - public function testMatchRequestDeactivatedUrlAlias() - { - $this->expectException(\Symfony\Component\Routing\Exception\ResourceNotFoundException::class); - - $this->resetConfigResolver(); - $this->configResolver - ->expects($this->any()) - ->method('getParameter') - ->will( - $this->returnValueMap( - [ - ['url_alias_router', null, null, false], - ] - ) - ); - $this->router->matchRequest($this->getRequestByPathInfo('/foo')); - } - - public function testMatchRequestWithRootLocation() - { - $rootLocationId = 123; - $this->resetConfigResolver(); - $this->configResolver - ->expects($this->any()) - ->method('getParameter') - ->will( - $this->returnValueMap( - [ - ['url_alias_router', null, null, true], - ] - ) - ); - $this->router->setRootLocationId($rootLocationId); - - $prefix = '/root/prefix'; - $this->urlALiasGenerator - ->expects($this->exactly(2)) - ->method('getPathPrefixByRootLocationId') - ->with($rootLocationId) - ->will($this->returnValue($prefix)); - - $locationId = 789; - $path = '/foo/bar'; - $urlAlias = new URLAlias( - [ - 'destination' => $locationId, - 'path' => $prefix . $path, - 'type' => URLAlias::LOCATION, - 'isHistory' => false, - ] - ); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($prefix . $path) - ->will($this->returnValue($urlAlias)); - - $this->urlALiasGenerator - ->expects($this->once()) - ->method('loadLocation') - ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - '_controller' => UrlAliasRouter::VIEW_ACTION, - 'locationId' => $locationId, - 'contentId' => 456, - 'viewType' => ViewManager::VIEW_TYPE_FULL, - 'layout' => true, - ]; - $request = $this->getRequestByPathInfo($path); - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestLocationCaseRedirectWithRootLocation() - { - $rootLocationId = 123; - $this->resetConfigResolver(); - $this->configResolver - ->expects($this->any()) - ->method('getParameter') - ->will( - $this->returnValueMap( - [ - ['url_alias_router', null, null, true], - ] - ) - ); - $this->router->setRootLocationId($rootLocationId); - - $prefix = '/root/prefix'; - $this->urlALiasGenerator - ->expects($this->exactly(2)) - ->method('getPathPrefixByRootLocationId') - ->with($rootLocationId) - ->will($this->returnValue($prefix)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('loadLocation') - ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); - - $locationId = 789; - $path = '/foo/bar'; - $requestedPath = '/Foo/Bar'; - $urlAlias = new URLAlias( - [ - 'destination' => $locationId, - 'path' => $prefix . $path, - 'type' => URLAlias::LOCATION, - 'isHistory' => false, - ] - ); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($prefix . $requestedPath) - ->will($this->returnValue($urlAlias)); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - '_controller' => UrlAliasRouter::VIEW_ACTION, - 'locationId' => $locationId, - 'contentId' => 456, - 'viewType' => ViewManager::VIEW_TYPE_FULL, - 'layout' => true, - 'semanticPathinfo' => $path, - 'needsRedirect' => true, - ]; - $request = $this->getRequestByPathInfo($requestedPath); - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestLocationCaseRedirectWithRootRootLocation() - { - $rootLocationId = 123; - $this->resetConfigResolver(); - $this->configResolver - ->expects($this->any()) - ->method('getParameter') - ->will( - $this->returnValueMap( - [ - ['url_alias_router', null, null, true], - ] - ) - ); - $this->router->setRootLocationId($rootLocationId); - - $prefix = '/'; - $this->urlALiasGenerator - ->expects($this->exactly(2)) - ->method('getPathPrefixByRootLocationId') - ->with($rootLocationId) - ->will($this->returnValue($prefix)); - - $locationId = 789; - $path = '/foo/bar'; - $requestedPath = '/Foo/Bar'; - $urlAlias = new URLAlias( - [ - 'destination' => $locationId, - 'path' => $path, - 'type' => URLAlias::LOCATION, - 'isHistory' => false, - ] - ); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($requestedPath) - ->will($this->returnValue($urlAlias)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('loadLocation') - ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - '_controller' => UrlAliasRouter::VIEW_ACTION, - 'locationId' => $locationId, - 'contentId' => 456, - 'viewType' => ViewManager::VIEW_TYPE_FULL, - 'layout' => true, - 'semanticPathinfo' => $path, - 'needsRedirect' => true, - ]; - $request = $this->getRequestByPathInfo($requestedPath); - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestResourceCaseRedirectWithRootLocation() - { - $rootLocationId = 123; - $this->resetConfigResolver(); - $this->configResolver - ->expects($this->any()) - ->method('getParameter') - ->will( - $this->returnValueMap( - [ - ['url_alias_router', null, null, true], - ] - ) - ); - $this->router->setRootLocationId($rootLocationId); - - $prefix = '/root/prefix'; - $this->urlALiasGenerator - ->expects($this->exactly(2)) - ->method('getPathPrefixByRootLocationId') - ->with($rootLocationId) - ->will($this->returnValue($prefix)); - - $path = '/foo/bar'; - $requestedPath = '/Foo/Bar'; - $urlAlias = new URLAlias( - [ - 'destination' => '/content/search', - 'path' => $prefix . $path, - 'type' => URLAlias::RESOURCE, - 'isHistory' => false, - ] - ); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($prefix . $requestedPath) - ->will($this->returnValue($urlAlias)); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - 'semanticPathinfo' => $path, - 'needsRedirect' => true, - ]; - $request = $this->getRequestByPathInfo($requestedPath); - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestVirtualCaseRedirectWithRootLocation() - { - $rootLocationId = 123; - $this->resetConfigResolver(); - $this->configResolver - ->expects($this->any()) - ->method('getParameter') - ->will( - $this->returnValueMap( - [ - ['url_alias_router', null, null, true], - ] - ) - ); - $this->router->setRootLocationId($rootLocationId); - - $prefix = '/root/prefix'; - $this->urlALiasGenerator - ->expects($this->exactly(2)) - ->method('getPathPrefixByRootLocationId') - ->with($rootLocationId) - ->will($this->returnValue($prefix)); - - $path = '/foo/bar'; - $requestedPath = '/Foo/Bar'; - $urlAlias = new URLAlias( - [ - 'path' => $prefix . $path, - 'type' => URLAlias::VIRTUAL, - ] - ); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($prefix . $requestedPath) - ->will($this->returnValue($urlAlias)); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - 'semanticPathinfo' => $path, - 'needsRedirect' => true, - ]; - $request = $this->getRequestByPathInfo($requestedPath); - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestWithRootLocationAndExclusion() - { - $this->resetConfigResolver(); - $this->configResolver - ->expects($this->any()) - ->method('getParameter') - ->will( - $this->returnValueMap( - [ - ['url_alias_router', null, null, true], - ['content.tree_root.location_id', null, null, 123], - ['content.tree_root.excluded_uri_prefixes', null, null, ['/shared/content']], - ] - ) - ); - $this->router->setRootLocationId(123); - - $pathInfo = '/shared/content/foo-bar'; - $destinationId = 789; - $this->urlALiasGenerator - ->expects($this->any()) - ->method('isUriPrefixExcluded') - ->with($pathInfo) - ->will($this->returnValue(true)); - - $urlAlias = new URLAlias( - [ - 'path' => $pathInfo, - 'type' => UrlAlias::LOCATION, - 'destination' => $destinationId, - 'isHistory' => false, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('loadLocation') - ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - '_controller' => UrlAliasRouter::VIEW_ACTION, - 'locationId' => $destinationId, - 'contentId' => 456, - 'viewType' => ViewManager::VIEW_TYPE_FULL, - 'layout' => true, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/SiteAccess/Config/IOConfigResolverTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/SiteAccess/Config/IOConfigResolverTest.php deleted file mode 100644 index 5e73c0b715..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/SiteAccess/Config/IOConfigResolverTest.php +++ /dev/null @@ -1,133 +0,0 @@ -configResolver = $this->createMock(ConfigResolverInterface::class); - $this->siteAccessService = $this->createMock(SiteAccessService::class); - } - - /** - * @covers \eZ\Bundle\EzPublishCoreBundle\SiteAccess\Config\IOConfigResolver::getUrlPrefix - */ - public function testGetUrlPrefix(): void - { - $this->siteAccessService - ->method('getCurrent') - ->willReturn(new SiteAccess('ezdemo_site')); - - $this->configResolver - ->method('hasParameter') - ->with('io.url_prefix', null, 'ezdemo_site') - ->willReturn(true); - $this->configResolver - ->method('getParameter') - ->willReturnMap([ - ['io.url_prefix', null, 'ezdemo_site', '$var_dir$/ezdemo_site/$storage_dir$'], - ['var_dir', self::DEFAULT_NAMESPACE, 'ezdemo_site', 'var'], - ['storage_dir', self::DEFAULT_NAMESPACE, 'ezdemo_site', 'storage'], - ]); - - $complexConfigProcessor = new ComplexConfigProcessor( - $this->configResolver, - $this->siteAccessService - ); - - $ioConfigResolver = new IOConfigResolver( - $complexConfigProcessor - ); - - $this->assertEquals('var/ezdemo_site/storage', $ioConfigResolver->getUrlPrefix()); - } - - /** - * @covers \eZ\Bundle\EzPublishCoreBundle\SiteAccess\Config\IOConfigResolver::getUrlPrefix - */ - public function testGetLegacyUrlPrefix(): void - { - $this->siteAccessService - ->method('getCurrent') - ->willReturn(new SiteAccess('ezdemo_site')); - - $this->configResolver - ->method('hasParameter') - ->with('io.legacy_url_prefix', null, 'ezdemo_site') - ->willReturn(true); - $this->configResolver - ->method('getParameter') - ->willReturnMap([ - ['io.legacy_url_prefix', null, 'ezdemo_site', '$var_dir$/ezdemo_site/$storage_dir$'], - ['var_dir', self::DEFAULT_NAMESPACE, 'ezdemo_site', 'var'], - ['storage_dir', self::DEFAULT_NAMESPACE, 'ezdemo_site', 'legacy_storage'], - ]); - - $complexConfigProcessor = new ComplexConfigProcessor( - $this->configResolver, - $this->siteAccessService - ); - - $ioConfigResolver = new IOConfigResolver( - $complexConfigProcessor - ); - - $this->assertEquals('var/ezdemo_site/legacy_storage', $ioConfigResolver->getLegacyUrlPrefix()); - } - - /** - * @covers \eZ\Bundle\EzPublishCoreBundle\SiteAccess\Config\IOConfigResolver::getUrlPrefix - */ - public function testGetRootDir(): void - { - $this->siteAccessService - ->method('getCurrent') - ->willReturn(new SiteAccess('ezdemo_site')); - - $this->configResolver - ->method('hasParameter') - ->with('io.root_dir', null, 'ezdemo_site') - ->willReturn(true); - $this->configResolver - ->method('getParameter') - ->willReturnMap([ - ['io.root_dir', null, 'ezdemo_site', '/path/to/ezpublish/web/$var_dir$/ezdemo_site/$storage_dir$'], - ['var_dir', self::DEFAULT_NAMESPACE, 'ezdemo_site', 'var'], - ['storage_dir', self::DEFAULT_NAMESPACE, 'ezdemo_site', 'legacy_storage'], - ]); - - $complexConfigProcessor = new ComplexConfigProcessor( - $this->configResolver, - $this->siteAccessService - ); - - $ioConfigResolver = new IOConfigResolver( - $complexConfigProcessor - ); - - $this->assertEquals('/path/to/ezpublish/web/var/ezdemo_site/legacy_storage', $ioConfigResolver->getRootDir()); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/SiteAccess/MatcherBuilderTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/SiteAccess/MatcherBuilderTest.php deleted file mode 100644 index ccfba84f43..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/SiteAccess/MatcherBuilderTest.php +++ /dev/null @@ -1,89 +0,0 @@ -siteAccessMatcherRegistry = $this->createMock(SiteAccessMatcherRegistryInterface::class); - } - - /** - * @covers \eZ\Bundle\EzPublishCoreBundle\SiteAccess\MatcherBuilder::__construct - * @covers \eZ\Bundle\EzPublishCoreBundle\SiteAccess\MatcherBuilder::buildMatcher - * @covers \eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilder::buildMatcher - */ - public function testBuildMatcherNoService() - { - $this->siteAccessMatcherRegistry - ->expects($this->never()) - ->method('getMatcher'); - $matcherBuilder = new MatcherBuilder($this->siteAccessMatcherRegistry); - $matcher = $this->createMock(Matcher::class); - $builtMatcher = $matcherBuilder->buildMatcher('\\' . get_class($matcher), [], new SimplifiedRequest()); - $this->assertInstanceOf(get_class($matcher), $builtMatcher); - } - - /** - * @covers \eZ\Bundle\EzPublishCoreBundle\SiteAccess\MatcherBuilder::__construct - * @covers \eZ\Bundle\EzPublishCoreBundle\SiteAccess\MatcherBuilder::buildMatcher - */ - public function testBuildMatcherServiceWrongInterface() - { - $this->expectException(\TypeError::class); - - $serviceId = 'foo'; - $this->siteAccessMatcherRegistry - ->expects($this->once()) - ->method('getMatcher') - ->with($serviceId) - ->will($this->returnValue($this->createMock(Matcher::class))); - $matcherBuilder = new MatcherBuilder($this->siteAccessMatcherRegistry); - $matcherBuilder->buildMatcher("@$serviceId", [], new SimplifiedRequest()); - } - - /** - * @covers \eZ\Bundle\EzPublishCoreBundle\SiteAccess\MatcherBuilder::__construct - * @covers \eZ\Bundle\EzPublishCoreBundle\SiteAccess\MatcherBuilder::buildMatcher - */ - public function testBuildMatcherService() - { - $serviceId = 'foo'; - $matcher = $this->createMock(CoreMatcher::class); - $this->siteAccessMatcherRegistry - ->expects($this->once()) - ->method('getMatcher') - ->with($serviceId) - ->will($this->returnValue($matcher)); - - $matchingConfig = ['foo' => 'bar']; - $request = new SimplifiedRequest(); - $matcher - ->expects($this->once()) - ->method('setMatchingConfiguration') - ->with($matchingConfig); - $matcher - ->expects($this->once()) - ->method('setRequest') - ->with($request); - - $matcherBuilder = new MatcherBuilder($this->siteAccessMatcherRegistry); - $matcherBuilder->buildMatcher("@$serviceId", $matchingConfig, $request); - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Translation/GlobCollectorTest.php b/eZ/Bundle/EzPublishCoreBundle/Tests/Translation/GlobCollectorTest.php deleted file mode 100644 index 4eaa74e194..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Translation/GlobCollectorTest.php +++ /dev/null @@ -1,31 +0,0 @@ -collect(); - $this->assertCount(3, $files); - foreach ($files as $file) { - $this->assertTrue(in_array($file['domain'], ['messages', 'dashboard'])); - $this->assertTrue(in_array($file['locale'], ['fr', 'ach_UG'])); - $this->assertEquals($file['format'], 'xlf'); - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/Translation/Collector.php b/eZ/Bundle/EzPublishCoreBundle/Translation/Collector.php deleted file mode 100644 index 44f3894f9e..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/Translation/Collector.php +++ /dev/null @@ -1,18 +0,0 @@ -tranlationPattern = $kernelRootDir . sprintf('%1$svendor%1$sezplatform-i18n%1$sezplatform-i18n-*%1$s*%1$s*.xlf', \DIRECTORY_SEPARATOR); - } - - /** - * @return array - */ - public function collect() - { - $meta = []; - foreach (glob($this->tranlationPattern) as $file) { - list($domain, $locale, $format) = explode('.', basename($file), 3); - $meta[] = [ - 'file' => $file, - 'domain' => $domain, - 'locale' => $locale, - 'format' => $format, - ]; - } - - return $meta; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/URLChecker/URLChecker.php b/eZ/Bundle/EzPublishCoreBundle/URLChecker/URLChecker.php deleted file mode 100644 index 65f398b663..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/URLChecker/URLChecker.php +++ /dev/null @@ -1,97 +0,0 @@ -urlService = $urlService; - $this->handlerRegistry = $handlerRegistry; - $this->logger = new NullLogger(); - } - - /** - * {@inheritdoc} - */ - public function check(URLQuery $query) - { - $grouped = $this->fetchUrls($query); - foreach ($grouped as $scheme => $urls) { - if (!$this->handlerRegistry->supported($scheme)) { - $this->logger->error('Unsupported URL schema: ' . $scheme); - continue; - } - - $handler = $this->handlerRegistry->getHandler($scheme); - $handler->validate($urls); - } - } - - /** - * Fetch URLs to check. - * - * @param \eZ\Publish\API\Repository\Values\URL\URLQuery $query - * - * @return array - */ - protected function fetchUrls(URLQuery $query) - { - return $this->groupByScheme( - $this->urlService->findUrls($query) - ); - } - - /** - * Group URLs by schema. - * - * @param \eZ\Publish\API\Repository\Values\URL\SearchResult $urls - * - * @return array - */ - private function groupByScheme(SearchResult $urls) - { - $grouped = []; - - foreach ($urls as $url) { - $scheme = parse_url($url->url, PHP_URL_SCHEME); - if (!$scheme) { - continue; - } - - if (!isset($grouped[$scheme])) { - $grouped[$scheme] = []; - } - - $grouped[$scheme][] = $url; - } - - return $grouped; - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/URLChecker/URLCheckerInterface.php b/eZ/Bundle/EzPublishCoreBundle/URLChecker/URLCheckerInterface.php deleted file mode 100644 index bd0474d20e..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/URLChecker/URLCheckerInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -logger) { - $this->logger->debug('Changing SiteAccess in view providers'); - } - - $providers = array_merge( - $this->getAllLocationViewProviders(), - $this->getAllContentViewProviders() - ); - foreach ($providers as $provider) { - if ($provider instanceof SiteAccessAware) { - $provider->setSiteAccess($siteAccess); - } - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/View/Provider/Configured.php b/eZ/Bundle/EzPublishCoreBundle/View/Provider/Configured.php deleted file mode 100644 index 0e340ce1e2..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/View/Provider/Configured.php +++ /dev/null @@ -1,26 +0,0 @@ -matcherFactory instanceof SiteAccessAware) { - $this->matcherFactory->setSiteAccess($siteAccess); - } - } -} diff --git a/eZ/Bundle/EzPublishCoreBundle/behat_suites.yml b/eZ/Bundle/EzPublishCoreBundle/behat_suites.yml deleted file mode 100644 index dc867a253d..0000000000 --- a/eZ/Bundle/EzPublishCoreBundle/behat_suites.yml +++ /dev/null @@ -1,33 +0,0 @@ -# This file is meant to be imported from ezplatform's behat.yml.dist. -# All path are relative to the root ezplatform directory. -core: - suites: - console: - paths: - - vendor/ezsystems/ezplatform-kernel/eZ/Bundle/EzPublishCoreBundle/Features/Console - contexts: - - eZ\Bundle\EzPublishCoreBundle\Features\Context\ConsoleContext - web: - paths: - - vendor/ezsystems/ezplatform-kernel/eZ/Bundle/EzPublishCoreBundle/Features/Content - - vendor/ezsystems/ezplatform-kernel/eZ/Bundle/EzPublishCoreBundle/Features/Exception - contexts: - - eZ\Bundle\EzPublishCoreBundle\Features\Context\ContentPreviewContext - - eZ\Bundle\EzPublishCoreBundle\Features\Context\ContentContext - - eZ\Bundle\EzPublishCoreBundle\Features\Context\ExceptionContext - query_controller: - paths: - - vendor/ezsystems/ezplatform-kernel/eZ/Bundle/EzPublishCoreBundle/Features/QueryController/query_controller.feature - contexts: - - Behat\MinkExtension\Context\MinkContext - - eZ\Bundle\EzPublishCoreBundle\Features\Context\QueryControllerContext - - EzSystems\Behat\API\Context\ContentContext - - EzSystems\Behat\API\Context\TestContext - setup: - paths: - - vendor/ezsystems/ezplatform-kernel/eZ/Bundle/EzPublishCoreBundle/Features/QueryController/setup.feature - contexts: - - EzSystems\Behat\API\Context\ContentContext - - EzSystems\Behat\API\Context\TestContext - - EzSystems\Behat\Core\Context\ConfigurationContext - - EzSystems\Behat\Core\Context\FileContext diff --git a/eZ/Bundle/EzPublishDebugBundle/Collector/EzPublishCoreCollector.php b/eZ/Bundle/EzPublishDebugBundle/Collector/EzPublishCoreCollector.php deleted file mode 100644 index c1b7878ab3..0000000000 --- a/eZ/Bundle/EzPublishDebugBundle/Collector/EzPublishCoreCollector.php +++ /dev/null @@ -1,113 +0,0 @@ -reset(); - } - - public function collect(Request $request, Response $response, \Throwable $exception = null) - { - /** @var \Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface $innerCollector */ - foreach ($this->data['collectors'] as $innerCollector) { - $innerCollector->collect($request, $response, $exception); - } - } - - public function getName() - { - return 'ezpublish.debug.toolbar'; - } - - /** - * @param \Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface $collector - */ - public function addCollector(DataCollectorInterface $collector, $panelTemplate = null, $toolbarTemplate = null) - { - $name = $collector->getName(); - $this->data['collectors'][$name] = $collector; - $this->data['panelTemplates'][$name] = $panelTemplate; - $this->data['toolbarTemplates'][$name] = $toolbarTemplate; - } - - /** - * @param string $name Name of the collector - * - * @return \Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface - * - * @throws \InvalidArgumentException - */ - public function getCollector($name) - { - if (!isset($this->data['collectors'][$name])) { - throw new InvalidArgumentException("Invalid debug collector '$name'"); - } - - return $this->data['collectors'][$name]; - } - - /** - * @return \Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface[] - */ - public function getAllCollectors() - { - return $this->data['collectors']; - } - - /** - * Returns toolbar template for given collector name. - * - * @param string $collectorName Name of corresponding collector. - * - * @return string - */ - public function getToolbarTemplate($collectorName) - { - if (!isset($this->data['toolbarTemplates'][$collectorName])) { - return null; - } - - return $this->data['toolbarTemplates'][$collectorName]; - } - - /** - * Returns panel template to use for given collector name. - * - * @param string $collectorName Name of corresponding collector. - * - * @return string - */ - public function getPanelTemplate($collectorName) - { - if (!isset($this->data['panelTemplates'][$collectorName])) { - return null; - } - - return $this->data['panelTemplates'][$collectorName]; - } - - /** - * {@inheritdoc} - */ - public function reset() - { - $this->data = [ - 'collectors' => [], - 'panelTemplates' => [], - 'toolbarTemplates' => [], - ]; - } -} diff --git a/eZ/Bundle/EzPublishDebugBundle/DependencyInjection/Compiler/DataCollectorPass.php b/eZ/Bundle/EzPublishDebugBundle/DependencyInjection/Compiler/DataCollectorPass.php deleted file mode 100644 index 4ca1a86bba..0000000000 --- a/eZ/Bundle/EzPublishDebugBundle/DependencyInjection/Compiler/DataCollectorPass.php +++ /dev/null @@ -1,35 +0,0 @@ -hasDefinition('ezpublish_debug.data_collector')) { - return; - } - - $dataCollectorDef = $container->getDefinition('ezpublish_debug.data_collector'); - foreach ($container->findTaggedServiceIds('ezpublish_data_collector') as $id => $attributes) { - foreach ($attributes as $attribute) { - $dataCollectorDef->addMethodCall( - 'addCollector', - [ - new Reference($id), - isset($attribute['panelTemplate']) ? $attribute['panelTemplate'] : null, - isset($attribute['toolbarTemplate']) ? $attribute['toolbarTemplate'] : null, - ] - ); - } - } - } -} diff --git a/eZ/Bundle/EzPublishDebugBundle/DependencyInjection/EzPublishDebugExtension.php b/eZ/Bundle/EzPublishDebugBundle/DependencyInjection/EzPublishDebugExtension.php deleted file mode 100644 index 98ba2d4754..0000000000 --- a/eZ/Bundle/EzPublishDebugBundle/DependencyInjection/EzPublishDebugExtension.php +++ /dev/null @@ -1,40 +0,0 @@ -load('services.yml'); - } - - /** - * Sets the twig base template class to this bundle's in order to collect template infos. - */ - public function prepend(ContainerBuilder $container) - { - if ($container->getParameter('kernel.debug')) { - $container->prependExtensionConfig( - 'twig', - ['base_template_class' => 'eZ\Bundle\EzPublishDebugBundle\Twig\DebugTemplate'] - ); - } - } -} diff --git a/eZ/Bundle/EzPublishDebugBundle/EzPublishDebugBundle.php b/eZ/Bundle/EzPublishDebugBundle/EzPublishDebugBundle.php deleted file mode 100644 index 03563b061d..0000000000 --- a/eZ/Bundle/EzPublishDebugBundle/EzPublishDebugBundle.php +++ /dev/null @@ -1,20 +0,0 @@ -addCompilerPass(new DataCollectorPass()); - } -} diff --git a/eZ/Bundle/EzPublishDebugBundle/Resources/config/services.yml b/eZ/Bundle/EzPublishDebugBundle/Resources/config/services.yml deleted file mode 100644 index c26c217f18..0000000000 --- a/eZ/Bundle/EzPublishDebugBundle/Resources/config/services.yml +++ /dev/null @@ -1,27 +0,0 @@ -services: - ezpublish_debug.data_collector: - class: eZ\Bundle\EzPublishDebugBundle\Collector\EzPublishCoreCollector - tags: - - - name: data_collector - template: '@EzPublishDebug/Profiler/layout.html.twig' - id: "ezpublish.debug.toolbar" - - ezpublish_debug.siteaccess_collector: - class: eZ\Bundle\EzPublishDebugBundle\Collector\SiteAccessCollector - tags: - - - name: ezpublish_data_collector - id: "ezpublish.debug.siteaccess" - panelTemplate: '@EzPublishDebug/Profiler/siteaccess/panel.html.twig' - toolbarTemplate: '@EzPublishDebug/Profiler/siteaccess/toolbar.html.twig' - - ezpublish_debug.persistence_collector: - class: eZ\Bundle\EzPublishDebugBundle\Collector\PersistenceCacheCollector - arguments: ["@ezpublish.spi.persistence.cache.persistenceLogger"] - tags: - - - name: ezpublish_data_collector - id: "ezpublish.debug.persistence" - panelTemplate: '@EzPublishDebug/Profiler/persistence/panel.html.twig' - toolbarTemplate: '@EzPublishDebug/Profiler/persistence/toolbar.html.twig' diff --git a/eZ/Bundle/EzPublishDebugBundle/Resources/views/Profiler/layout.html.twig b/eZ/Bundle/EzPublishDebugBundle/Resources/views/Profiler/layout.html.twig deleted file mode 100644 index 08d55955a1..0000000000 --- a/eZ/Bundle/EzPublishDebugBundle/Resources/views/Profiler/layout.html.twig +++ /dev/null @@ -1,59 +0,0 @@ -{% extends '@WebProfiler/Profiler/layout.html.twig' %} - -{% block toolbar %} - {% set icon %} - - - - - - - {% endset %} - - {% set text %} - {% for name, inner_collector in collector.allCollectors %} - {% set inner_template = collector.getToolbarTemplate( name ) %} - {% if inner_template %} - {% include inner_template with { "collector": inner_collector } %} - - {% if not loop.last %}
    {% endif %} - {% endif %} - - {% endfor %} - - {% endset %} - - {# Set to red if over 100 uncached, and to yellow if either over 15 uncached or over 100 cache hits lookups #} - {% set stats = collector.getCollector('ezpublish.debug.persistence').stats %} - {% set total_uncached = stats.uncached + stats.miss %} - {% set status_logo = total_uncached > 100 ? 'red' : (total_uncached > 15 ? 'yellow' : (stats.hit > 100 ? 'yellow' : '')) %} - - {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_logo|default('') }) }} -{% endblock %} - -{% block menu %} - - - - - - - - - - eZ Platform - -{% endblock %} - -{% block panel %} -

    Usage Information

    - - {% for name, inner_collector in collector.allCollectors %} - {% set inner_template = collector.getPanelTemplate( name ) %} - {% if inner_template %}{% include inner_template with { "collector": inner_collector } %}{% endif %} - - {% if not loop.last %}
    {% endif %} - - {% endfor %} - -{% endblock %} diff --git a/eZ/Bundle/EzPublishDebugBundle/Tests/Collector/EzPublishCoreCollectorTest.php b/eZ/Bundle/EzPublishDebugBundle/Tests/Collector/EzPublishCoreCollectorTest.php deleted file mode 100644 index 312f4a3e8a..0000000000 --- a/eZ/Bundle/EzPublishDebugBundle/Tests/Collector/EzPublishCoreCollectorTest.php +++ /dev/null @@ -1,168 +0,0 @@ -mainCollector = new EzPublishCoreCollector(); - } - - public function testAddGetCollector() - { - $collector = $this->getDataCollectorMock(); - $name = 'foobar'; - $collector - ->expects($this->once()) - ->method('getName') - ->will($this->returnValue($name)); - - $this->mainCollector->addCollector($collector); - $this->assertSame($collector, $this->mainCollector->getCollector($name)); - } - - public function testGetInvalidCollector() - { - $this->expectException(\InvalidArgumentException::class); - - $collector = $this->getDataCollectorMock(); - $this->mainCollector->addCollector($collector); - $this->assertSame($collector, $this->mainCollector->getCollector('foo')); - } - - public function testGetAllCollectors() - { - $collector1 = $this->getDataCollectorMock(); - $nameCollector1 = 'collector1'; - $collector1 - ->expects($this->once()) - ->method('getName') - ->will($this->returnValue($nameCollector1)); - $collector2 = $this->getDataCollectorMock(); - $nameCollector2 = 'collector2'; - $collector2 - ->expects($this->once()) - ->method('getName') - ->will($this->returnValue($nameCollector2)); - - $allCollectors = [ - $nameCollector1 => $collector1, - $nameCollector2 => $collector2, - ]; - - foreach ($allCollectors as $name => $collector) { - $this->mainCollector->addCollector($collector); - } - - $this->assertSame($allCollectors, $this->mainCollector->getAllCollectors()); - } - - public function testGetToolbarTemplateNothing() - { - $collector = $this->getDataCollectorMock(); - $name = 'foobar'; - $collector - ->expects($this->once()) - ->method('getName') - ->will($this->returnValue($name)); - $this->mainCollector->addCollector($collector); - $this->assertNull($this->mainCollector->getToolbarTemplate($name)); - } - - public function testGetToolbarTemplate() - { - $collector = $this->getDataCollectorMock(); - $name = 'foobar'; - $collector - ->expects($this->once()) - ->method('getName') - ->will($this->returnValue($name)); - $toolbarTemplate = 'toolbar.html.twig'; - - $this->mainCollector->addCollector($collector, 'foo', $toolbarTemplate); - $this->assertSame($toolbarTemplate, $this->mainCollector->getToolbarTemplate($name)); - } - - public function testGetPanelTemplateNothing() - { - $collector = $this->getDataCollectorMock(); - $name = 'foobar'; - $collector - ->expects($this->once()) - ->method('getName') - ->will($this->returnValue($name)); - $this->mainCollector->addCollector($collector); - $this->assertNull($this->mainCollector->getPanelTemplate($name)); - } - - public function testGetPanelTemplate() - { - $collector = $this->getDataCollectorMock(); - $name = 'foobar'; - $collector - ->expects($this->once()) - ->method('getName') - ->will($this->returnValue($name)); - $panelTemplate = 'toolbar.html.twig'; - - $this->mainCollector->addCollector($collector, $panelTemplate, 'foo'); - $this->assertSame($panelTemplate, $this->mainCollector->getPanelTemplate($name)); - } - - public function testCollect() - { - $collector1 = $this->getDataCollectorMock(); - $nameCollector1 = 'collector1'; - $collector1 - ->expects($this->once()) - ->method('getName') - ->will($this->returnValue($nameCollector1)); - $collector2 = $this->getDataCollectorMock(); - $nameCollector2 = 'collector2'; - $collector2 - ->expects($this->once()) - ->method('getName') - ->will($this->returnValue($nameCollector2)); - - $allCollectors = [ - $nameCollector1 => $collector1, - $nameCollector2 => $collector2, - ]; - - $request = new Request(); - $response = new Response(); - $exception = new Exception(); - - /** @var \PHPUnit\Framework\MockObject\MockObject */ - foreach ($allCollectors as $name => $collector) { - $this->mainCollector->addCollector($collector); - $collector - ->expects($this->once()) - ->method('collect') - ->with($request, $response, $exception); - } - - $this->mainCollector->collect($request, $response, $exception); - } - - protected function getDataCollectorMock() - { - return $this->createMock(DataCollectorInterface::class); - } -} diff --git a/eZ/Bundle/EzPublishDebugBundle/Tests/DependencyInjection/Compiler/DataCollectorPassTest.php b/eZ/Bundle/EzPublishDebugBundle/Tests/DependencyInjection/Compiler/DataCollectorPassTest.php deleted file mode 100644 index 46aa35f9e8..0000000000 --- a/eZ/Bundle/EzPublishDebugBundle/Tests/DependencyInjection/Compiler/DataCollectorPassTest.php +++ /dev/null @@ -1,48 +0,0 @@ -setDefinition('ezpublish_debug.data_collector', new Definition()); - } - - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new DataCollectorPass()); - } - - public function testAddCollector() - { - $panelTemplate = 'panel.html.twig'; - $toolbarTemplate = 'toolbar.html.twig'; - $definition = new Definition(); - $definition->addTag( - 'ezpublish_data_collector', - ['panelTemplate' => $panelTemplate, 'toolbarTemplate' => $toolbarTemplate] - ); - - $serviceId = 'service_id'; - $this->setDefinition($serviceId, $definition); - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish_debug.data_collector', - 'addCollector', - [new Reference($serviceId), $panelTemplate, $toolbarTemplate] - ); - } -} diff --git a/eZ/Bundle/EzPublishDebugBundle/Twig/DebugTemplate.php b/eZ/Bundle/EzPublishDebugBundle/Twig/DebugTemplate.php deleted file mode 100644 index 0d10379826..0000000000 --- a/eZ/Bundle/EzPublishDebugBundle/Twig/DebugTemplate.php +++ /dev/null @@ -1,91 +0,0 @@ -fileSystem = $this->fileSystem ?: new Filesystem(); - - // Bufferize to be able to insert template name as HTML comments if applicable. - // Layout template name will only appear at the end, to avoid potential quirks with old browsers - // when comments appear before doctype declaration. - ob_start(); - parent::display($context, $blocks); - $templateResult = ob_get_clean(); - - $templateName = trim($this->fileSystem->makePathRelative($this->getSourceContext()->getPath(), dirname(getcwd())), '/'); - // Check if template name ends with "html.twig", indicating this is an HTML template. - $isHtmlTemplate = substr($templateName, -strlen('html.twig')) === 'html.twig'; - $templateName = $isHtmlTemplate ? $templateName . ' (' . $this->getSourceContext()->getName() . ')' : $templateName; - - // Display start template comment, if applicable. - if ($isHtmlTemplate) { - if (stripos(trim($templateResult), ']+>)#im', - "$1\n', - $templateResult - ); - } else { - echo "\n\n"; - } - } - - // Display stop template comment after result, if applicable. - if ($isHtmlTemplate) { - $bodyPos = stripos($templateResult, ''); - if ($bodyPos !== false) { - // Add layout template name before , to avoid display quirks in some browsers. - echo substr($templateResult, 0, $bodyPos) - . "\n\n" - . substr($templateResult, $bodyPos); - } else { - echo $templateResult; - echo "\n\n"; - } - } else { - echo $templateResult; - } - } - - public function getTemplateName(): string - { - return ''; - } - - public function getSourceContext(): Source - { - return new Source('', ''); - } - - protected function doDisplay(array $context, array $blocks = []): string - { - return ''; - } - - /** - * @return array - */ - public function getDebugInfo(): array - { - return []; - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/ApiLoader/HandlerRegistry.php b/eZ/Bundle/EzPublishIOBundle/ApiLoader/HandlerRegistry.php deleted file mode 100644 index b1ccfee3c1..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/ApiLoader/HandlerRegistry.php +++ /dev/null @@ -1,43 +0,0 @@ -handlersMap = $handlersMap; - } - - /** - * @param string $handlerName - * - * @return object an instance of the requested handler - * - * @throws \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException If the requested handler doesn't exist - */ - public function getConfiguredHandler($handlerName) - { - if (!isset($this->handlersMap[$handlerName])) { - throw new InvalidConfigurationException("Unknown handler $handlerName"); - } - - return $this->handlersMap[$handlerName]; - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/Compiler/IOConfigurationPass.php b/eZ/Bundle/EzPublishIOBundle/DependencyInjection/Compiler/IOConfigurationPass.php deleted file mode 100644 index efceebeaa2..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/Compiler/IOConfigurationPass.php +++ /dev/null @@ -1,125 +0,0 @@ -metadataHandlerFactories = $metadataHandlerFactories; - $this->binarydataHandlerFactories = $binarydataHandlerFactories; - } - - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * - * @throws \LogicException - */ - public function process(ContainerBuilder $container) - { - $ioMetadataHandlers = $container->hasParameter('ez_io.metadata_handlers') ? - $container->getParameter('ez_io.metadata_handlers') : - []; - $this->processHandlers( - $container, - $container->getDefinition('ezpublish.core.io.metadata_handler.registry'), - $ioMetadataHandlers, - $this->metadataHandlerFactories, - 'ezpublish.core.io.metadata_handler.flysystem.default' - ); - - $ioBinarydataHandlers = $container->hasParameter('ez_io.binarydata_handlers') ? - $container->getParameter('ez_io.binarydata_handlers') : - []; - $this->processHandlers( - $container, - $container->getDefinition('ezpublish.core.io.binarydata_handler.registry'), - $ioBinarydataHandlers, - $this->binarydataHandlerFactories, - 'ezpublish.core.io.binarydata_handler.flysystem.default' - ); - - // Unset parameters that are no longer required ? - } - - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param \Symfony\Component\DependencyInjection\Definition $factory The factory service that should receive the list of handlers - * @param array $configuredHandlers Handlers configuration declared via semantic config - * @param \eZ\Bundle\EzPublishIOBundle\DependencyInjection\ConfigurationFactory[]|\ArrayObject $factories Map of alias => handler service id - * @param string $defaultHandler default handler id - * - * @internal param $HandlerTypesMap - */ - protected function processHandlers( - ContainerBuilder $container, - Definition $factory, - array $configuredHandlers, - ArrayObject $factories, - $defaultHandler - ) { - $handlers = ['default' => new Reference($defaultHandler)]; - - foreach ($configuredHandlers as $name => $config) { - $configurationFactory = $this->getFactory($factories, $config['type'], $container); - - $parentHandlerId = $configurationFactory->getParentServiceId(); - $handlerId = sprintf('%s.%s', $parentHandlerId, $name); - $handlerServiceDefinition = new ChildDefinition($parentHandlerId); - $definition = $container->setDefinition($handlerId, $handlerServiceDefinition); - - $configurationFactory->configureHandler($definition, $config); - - $handlers[$name] = new Reference($handlerId); - } - - $factory->addMethodCall('setHandlersMap', [$handlers]); - } - - /** - * Returns from $factories the factory for handler $type. - * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param \eZ\Bundle\EzPublishIOBundle\DependencyInjection\ConfigurationFactory[]|\ArrayObject|ContainerAware[] $factories - * @param string $type - * - * @return \eZ\Bundle\EzPublishIOBundle\DependencyInjection\ConfigurationFactory - */ - protected function getFactory(ArrayObject $factories, $type, ContainerBuilder $container) - { - if (!isset($factories[$type])) { - throw new InvalidConfigurationException("Unknown handler type $type"); - } - if ($factories[$type] instanceof ContainerAwareInterface) { - $factories[$type]->setContainer($container); - } - - return $factories[$type]; - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/Compiler/MigrationFileListerPass.php b/eZ/Bundle/EzPublishIOBundle/DependencyInjection/Compiler/MigrationFileListerPass.php deleted file mode 100644 index af92c1c272..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/Compiler/MigrationFileListerPass.php +++ /dev/null @@ -1,38 +0,0 @@ -has('ezpublish.core.io.migration.file_lister_registry')) { - return; - } - - $fileListersTagged = $container->findTaggedServiceIds('ezpublish.core.io.migration.file_lister'); - - $fileListers = []; - foreach ($fileListersTagged as $id => $tags) { - foreach ($tags as $attributes) { - $fileListers[$attributes['identifier']] = new Reference($id); - } - } - - $fileListerRegistryDef = $container->findDefinition('ezpublish.core.io.migration.file_lister_registry'); - $fileListerRegistryDef->setArguments([$fileListers]); - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/Configuration.php b/eZ/Bundle/EzPublishIOBundle/DependencyInjection/Configuration.php deleted file mode 100644 index 4b421ddd08..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/Configuration.php +++ /dev/null @@ -1,78 +0,0 @@ -metadataHandlerFactories = $factories; - } - - public function setBinarydataHandlerFactories(ArrayObject $factories) - { - $this->binarydataHandlerFactories = $factories; - } - - public function getConfigTreeBuilder() - { - $treeBuilder = new TreeBuilder('ez_io'); - - $rootNode = $treeBuilder->getRootNode(); - - $this->addHandlersSection( - $rootNode, - 'metadata_handlers', - 'Handlers for files metadata, that read & write files metadata (size, modification time...)', - $this->metadataHandlerFactories - ); - $this->addHandlersSection( - $rootNode, - 'binarydata_handlers', - 'Handlers for files binary data. Reads & write files binary content', - $this->binarydataHandlerFactories - ); - - $rootNode->children()->end(); - - return $treeBuilder; - } - - /** - * @param \Symfony\Component\Config\Definition\Builder\NodeDefinition $node - * @param string $name - * @param string $info block info line - * @param ConfigurationFactory[]|\ArrayObject $factories - */ - private function addHandlersSection(NodeDefinition $node, $name, $info, ArrayObject $factories) - { - $handlersNodeBuilder = $node - ->children() - ->arrayNode($name) - ->info($info) - ->useAttributeAsKey('name') - ->prototype('array') - ->performNoDeepMerging() - ->children(); - - foreach ($factories as $name => $factory) { - $factoryNode = $handlersNodeBuilder->arrayNode($name)->canBeUnset(); - $factory->addConfiguration($factoryNode); - } - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/ConfigurationFactory/BinarydataHandler/Flysystem.php b/eZ/Bundle/EzPublishIOBundle/DependencyInjection/ConfigurationFactory/BinarydataHandler/Flysystem.php deleted file mode 100644 index 36636827b7..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/ConfigurationFactory/BinarydataHandler/Flysystem.php +++ /dev/null @@ -1,17 +0,0 @@ -info( - 'Handler based on league/flysystem, an abstract filesystem library. ' . - 'Yes, the metadata handler and binarydata handler look the same; it is NOT a mistake :)' - ) - ->children() - ->scalarNode('adapter') - ->info( - 'Flysystem adapter identifier. Should be configured using oneup flysystem bundle. ' . - 'Yes, the same adapter can be used for a binarydata and metadata handler' - ) - ->isRequired() - ->example('nfs') - ->end() - ->end(); - } - - public function configureHandler(ServiceDefinition $definition, array $config) - { - $filesystemId = $this->createFilesystem($this->container, $config['name'], $config['adapter']); - $definition->replaceArgument(0, new Reference($filesystemId)); - } - - /** - * Creates a flysystem filesystem $name service. - * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param string $name filesystem name (nfs, local...) - * @param string $adapter adapter name - * - * @return string - */ - private function createFilesystem(ContainerBuilder $container, $name, $adapter) - { - $adapterId = sprintf('oneup_flysystem.%s_adapter', $adapter); - // has either definition or alias - if (!$container->has($adapterId)) { - throw new InvalidConfigurationException("Unknown flysystem adapter $adapter"); - } - - $filesystemId = sprintf('ezpublish.core.io.flysystem.%s_filesystem', $name); - $filesystemServiceDefinition = new ChildDefinition('ezpublish.core.io.flysystem.base_filesystem'); - $definition = $container->setDefinition( - $filesystemId, - $filesystemServiceDefinition - ); - $definition->setArguments([new Reference($adapterId)]); - - return $filesystemId; - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/ConfigurationFactory/MetadataHandler/Flysystem.php b/eZ/Bundle/EzPublishIOBundle/DependencyInjection/ConfigurationFactory/MetadataHandler/Flysystem.php deleted file mode 100644 index 8298b2438d..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/ConfigurationFactory/MetadataHandler/Flysystem.php +++ /dev/null @@ -1,17 +0,0 @@ -replaceArgument(0, new Reference($config['connection'])); - } - - public function addConfiguration(ArrayNodeDefinition $node) - { - $node - ->info( - 'A MySQL based handler, compatible with the legacy DFS one, that stores metadata in the ezdfsfile table' - ) - ->children() - ->scalarNode('connection') - ->info('Doctrine connection service') - ->example('doctrine.dbal.cluster_connection') - ->end() - ->end(); - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/EzPublishIOExtension.php b/eZ/Bundle/EzPublishIOBundle/DependencyInjection/EzPublishIOExtension.php deleted file mode 100644 index 68d831019d..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/EzPublishIOExtension.php +++ /dev/null @@ -1,127 +0,0 @@ -metadataHandlerFactories = new ArrayObject(); - $this->binarydataHandlerFactories = new ArrayObject(); - } - - /** - * Registers a metadata handler configuration $factory for handler with $alias. - * - * @param string $alias - * @param \eZ\Bundle\EzPublishIOBundle\DependencyInjection\ConfigurationFactory $factory - */ - public function addMetadataHandlerFactory($alias, ConfigurationFactory $factory) - { - $this->metadataHandlerFactories[$alias] = $factory; - } - - /** - * Registers a binarydata handler configuration $factory for handler with $alias. - * - * @param string $alias - * @param \eZ\Bundle\EzPublishIOBundle\DependencyInjection\ConfigurationFactory $factory - */ - public function addBinarydataHandlerFactory($alias, ConfigurationFactory $factory) - { - $this->binarydataHandlerFactories[$alias] = $factory; - } - - /** - * @return \eZ\Bundle\EzPublishIOBundle\DependencyInjection\ConfigurationFactory[]|\ArrayObject - */ - public function getMetadataHandlerFactories() - { - return $this->metadataHandlerFactories; - } - - /** - * @return \eZ\Bundle\EzPublishIOBundle\DependencyInjection\ConfigurationFactory[]|\ArrayObject - */ - public function getBinarydataHandlerFactories() - { - return $this->binarydataHandlerFactories; - } - - public function getAlias() - { - return 'ez_io'; - } - - /** - * {@inheritdoc} - */ - public function load(array $configs, ContainerBuilder $container) - { - $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); - - $configuration = $this->getConfiguration($configs, $container); - - $config = $this->processConfiguration($configuration, $configs); - - $loader->load('io.yml'); - $loader->load('default_settings.yml'); - - $this->processHandlers($container, $config, 'metadata_handlers'); - $this->processHandlers($container, $config, 'binarydata_handlers'); - } - - /** - * Processes the config key $key, and registers the result in ez_io.$key. - * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * @param string $key Configuration key, either binarydata or metadata - */ - private function processHandlers(ContainerBuilder $container, $config, $key) - { - $handlers = []; - if (isset($config[$key])) { - foreach ($config[$key] as $name => $value) { - if (isset($handlers[$name])) { - throw new InvalidConfigurationException("A $key called $name already exists"); - } - $handlerConfig = current($value); - $handlerConfig['type'] = key($value); - $handlerConfig['name'] = $name; - $handlers[$name] = $handlerConfig; - } - } - $container->setParameter("ez_io.{$key}", $handlers); - } - - public function getConfiguration(array $config, ContainerBuilder $container) - { - $configuration = new Configuration(); - $configuration->setMetadataHandlerFactories($this->getMetadataHandlerFactories()); - $configuration->setBinarydataHandlerFactories($this->getBinarydataHandlerFactories()); - - return $configuration; - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/EzPublishIOBundle.php b/eZ/Bundle/EzPublishIOBundle/EzPublishIOBundle.php deleted file mode 100644 index 0f77a5969f..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/EzPublishIOBundle.php +++ /dev/null @@ -1,44 +0,0 @@ -getContainerExtension(); - $container->addCompilerPass( - new Compiler\IOConfigurationPass( - $extension->getMetadataHandlerFactories(), - $extension->getBinarydataHandlerFactories() - ) - ); - $container->addCompilerPass(new Compiler\MigrationFileListerPass()); - parent::build($container); - } - - public function getContainerExtension() - { - if (!isset($this->extension)) { - $this->extension = new EzPublishIOExtension(); - $this->extension->addMetadataHandlerFactory('flysystem', new ConfigurationFactory\MetadataHandler\Flysystem()); - $this->extension->addMetadataHandlerFactory('legacy_dfs_cluster', new ConfigurationFactory\MetadataHandler\LegacyDFSCluster()); - $this->extension->addBinarydataHandlerFactory('flysystem', new ConfigurationFactory\BinarydataHandler\Flysystem()); - } - - return $this->extension; - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/Flysystem/Adapter/SiteAccessAwareLocalAdapter.php b/eZ/Bundle/EzPublishIOBundle/Flysystem/Adapter/SiteAccessAwareLocalAdapter.php deleted file mode 100644 index c6529dc2e3..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/Flysystem/Adapter/SiteAccessAwareLocalAdapter.php +++ /dev/null @@ -1,49 +0,0 @@ -configProcessor = $configProcessor; - - parent::__construct( - $config['root'], - $config['writeFlags'], - $config['linkHandling'], - $config['permissions'] - ); - - $this->path = $config['path']; - } - - public function getPathPrefix(): string - { - $contextPath = $this->configProcessor->processSettingValue($this->path); - - // path prefix is guaranteed to have path separator suffix, see parent::setPathPrefix - return sprintf('%s%s', $this->pathPrefix, $contextPath); - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/Migration/FileLister/BinaryFileLister.php b/eZ/Bundle/EzPublishIOBundle/Migration/FileLister/BinaryFileLister.php deleted file mode 100644 index d2b6796c42..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/Migration/FileLister/BinaryFileLister.php +++ /dev/null @@ -1,69 +0,0 @@ -fileList = $fileList; - $this->filesDir = $filesDir; - - $this->fileList->rewind(); - - parent::__construct($metadataHandlerRegistry, $binarydataHandlerRegistry, $logger); - } - - public function countFiles() - { - return count($this->fileList); - } - - public function loadMetadataList($limit = null, $offset = null) - { - $metadataList = []; - $fileLimitList = new LimitIterator($this->fileList, $offset, $limit); - - foreach ($fileLimitList as $fileId) { - try { - $metadataList[] = $this->fromMetadataHandler->load($this->filesDir . '/' . $fileId); - } catch (BinaryFileNotFoundException $e) { - $this->logMissingFile($fileId); - - continue; - } - } - - return $metadataList; - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/Migration/FileLister/FileIterator/LegacyStorageFileIterator.php b/eZ/Bundle/EzPublishIOBundle/Migration/FileLister/FileIterator/LegacyStorageFileIterator.php deleted file mode 100644 index be943445de..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/Migration/FileLister/FileIterator/LegacyStorageFileIterator.php +++ /dev/null @@ -1,80 +0,0 @@ -rowReader = $rowReader; - } - - #[\ReturnTypeWillChange] - public function current() - { - return $this->item; - } - - public function next(): void - { - $this->fetchRow(); - } - - #[\ReturnTypeWillChange] - public function key() - { - return $this->cursor; - } - - public function valid(): bool - { - return $this->cursor < $this->count(); - } - - public function rewind(): void - { - $this->cursor = -1; - $this->rowReader->init(); - $this->fetchRow(); - } - - public function count(): int - { - return $this->rowReader->getCount(); - } - - /** - * Fetches the next item from the resultset and moves the cursor forward. - */ - private function fetchRow() - { - ++$this->cursor; - $fileId = $this->rowReader->getRow(); - - $this->item = $fileId; - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/Migration/FileLister/FileIteratorInterface.php b/eZ/Bundle/EzPublishIOBundle/Migration/FileLister/FileIteratorInterface.php deleted file mode 100644 index 33d6faede1..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/Migration/FileLister/FileIteratorInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -registry = $items; - } - - /** - * Returns the FileListerInterface matching the argument. - * - * @param string $identifier An identifier string. - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException If no FileListerInterface exists with this identifier - * - * @return \eZ\Bundle\EzPublishIOBundle\Migration\FileListerInterface The FileListerInterface given by the identifier. - */ - public function getItem($identifier) - { - if (isset($this->registry[$identifier])) { - return $this->registry[$identifier]; - } - - throw new NotFoundException('Migration file lister', $identifier); - } - - /** - * Returns the identifiers of all registered FileListerInterfaces. - * - * @return string[] Array of identifier strings. - */ - public function getIdentifiers() - { - return array_keys($this->registry); - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/Migration/FileMigratorInterface.php b/eZ/Bundle/EzPublishIOBundle/Migration/FileMigratorInterface.php deleted file mode 100644 index 4f9f81b6a9..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/Migration/FileMigratorInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -container->setParameter('ez_io.metadata_handlers', []); - $this->container->setParameter('ez_io.binarydata_handlers', []); - - $this->container->setDefinition('ezpublish.core.io.binarydata_handler.registry', new Definition()); - $this->container->setDefinition('ezpublish.core.io.metadata_handler.registry', new Definition()); - $this->container->setDefinition('ezpublish.core.io.binarydata_handler.flysystem.default', new Definition()); - $this->container->setDefinition('ezpublish.core.io.metadata_handler.flysystem.default', new Definition()); - } - - protected function registerCompilerPass(ContainerBuilder $container): void - { - $this->metadataConfigurationFactoryMock = $this->createMock(ConfigurationFactory::class); - $this->binarydataConfigurationFactoryMock = $this->createMock(ConfigurationFactory::class); - - $container->addCompilerPass( - new IOConfigurationPass( - new ArrayObject( - ['test_handler' => $this->metadataConfigurationFactoryMock] - ), - new ArrayObject( - ['test_handler' => $this->binarydataConfigurationFactoryMock] - ) - ) - ); - } - - /** - * Tests that the default handlers are available when nothing is configured. - */ - public function testDefaultHandlers() - { - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.core.io.binarydata_handler.registry', - 'setHandlersMap', - [['default' => 'ezpublish.core.io.binarydata_handler.flysystem.default']] - ); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.core.io.metadata_handler.registry', - 'setHandlersMap', - [['default' => 'ezpublish.core.io.metadata_handler.flysystem.default']] - ); - } - - public function testBinarydataHandler() - { - $this->container->setParameter( - 'ez_io.binarydata_handlers', - ['my_handler' => ['name' => 'my_handler', 'type' => 'test_handler']] - ); - - $this->binarydataConfigurationFactoryMock - ->expects($this->once()) - ->method('getParentServiceId') - ->will($this->returnValue('test.io.binarydata_handler.test_handler')); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithParent( - 'test.io.binarydata_handler.test_handler.my_handler', - 'test.io.binarydata_handler.test_handler' - ); - } - - public function testMetadataHandler() - { - $this->container->setParameter( - 'ez_io.metadata_handlers', - ['my_handler' => ['name' => 'my_handler', 'type' => 'test_handler']] - ); - - $this->metadataConfigurationFactoryMock - ->expects($this->once()) - ->method('getParentServiceId') - ->will($this->returnValue('test.io.metadata_handler.test_handler')); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithParent( - 'test.io.metadata_handler.test_handler.my_handler', - 'test.io.metadata_handler.test_handler' - ); - } - - public function testUnknownMetadataHandler() - { - $this->expectException(\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException::class); - $this->expectExceptionMessage('Unknown handler'); - - $this->container->setParameter( - 'ez_io.metadata_handlers', - ['test' => ['type' => 'unknown']] - ); - - $this->compile(); - } - - public function testUnknownBinarydataHandler() - { - $this->expectException(\Symfony\Component\Config\Definition\Exception\InvalidConfigurationException::class); - $this->expectExceptionMessage('Unknown handler'); - - $this->container->setParameter( - 'ez_io.binarydata_handlers', - ['test' => ['type' => 'unknown']] - ); - - $this->compile(); - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/Tests/DependencyInjection/ConfigurationFactory/BinarydataHandler/FlysystemTest.php b/eZ/Bundle/EzPublishIOBundle/Tests/DependencyInjection/ConfigurationFactory/BinarydataHandler/FlysystemTest.php deleted file mode 100644 index 46c4591634..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/Tests/DependencyInjection/ConfigurationFactory/BinarydataHandler/FlysystemTest.php +++ /dev/null @@ -1,31 +0,0 @@ - 'doctrine.dbal.test_connection']; - } - - /** - * Lets you test the handler definition after it was configured. - * - * Use the assertContainer* methods from matthiasnoback/SymfonyDependencyInjectionTest. - * - * @param string $handlerServiceId id of the service that was registered by the compiler pass - */ - public function validateConfiguredHandler($handlerServiceId) - { - self::assertContainerBuilderHasServiceDefinitionWithArgument( - $handlerServiceId, - 0, - 'doctrine.dbal.test_connection' - ); - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/Tests/DependencyInjection/EzPublishIOExtensionTest.php b/eZ/Bundle/EzPublishIOBundle/Tests/DependencyInjection/EzPublishIOExtensionTest.php deleted file mode 100644 index af5b3ee1ce..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/Tests/DependencyInjection/EzPublishIOExtensionTest.php +++ /dev/null @@ -1,108 +0,0 @@ -addMetadataHandlerFactory('flysystem', new ConfigurationFactory\MetadataHandler\Flysystem()); - $extension->addBinarydataHandlerFactory('flysystem', new ConfigurationFactory\BinarydataHandler\Flysystem()); - - return [$extension]; - } - - public function testParametersWithoutConfiguration() - { - $this->load(); - - $this->assertContainerBuilderHasParameter('ez_io.metadata_handlers', []); - $this->assertContainerBuilderHasParameter('ez_io.binarydata_handlers', []); - } - - public function testParametersWithMetadataHandler() - { - $config = [ - 'metadata_handlers' => [ - 'my_metadata_handler' => ['flysystem' => ['adapter' => 'my_adapter']], - ], - ]; - $this->load($config); - - $this->assertContainerBuilderHasParameter('ez_io.binarydata_handlers', []); - $this->assertContainerBuilderHasParameter( - 'ez_io.metadata_handlers', - ['my_metadata_handler' => ['name' => 'my_metadata_handler', 'type' => 'flysystem', 'adapter' => 'my_adapter']] - ); - } - - public function testParametersWithBinarydataHandler() - { - $config = [ - 'binarydata_handlers' => [ - 'my_binarydata_handler' => ['flysystem' => ['adapter' => 'my_adapter']], - ], - ]; - $this->load($config); - - $this->assertContainerBuilderHasParameter('ez_io.metadata_handlers', []); - $this->assertContainerBuilderHasParameter( - 'ez_io.binarydata_handlers', - ['my_binarydata_handler' => ['name' => 'my_binarydata_handler', 'type' => 'flysystem', 'adapter' => 'my_adapter']] - ); - } - - public function testUrlPrefixConfigurationIsUsedToDecorateUrl(): void - { - $this->container->registerExtension( - new EzPublishCoreExtension( - [ - new Parser\IO(new ComplexSettingParser()), - ] - ) - ); - $this->container->prependExtensionConfig( - 'ezpublish', - Yaml::parseFile(self::FIXTURES_DIR . '/url_prefix_test_config.yaml')['ezplatform'] - ); - $this->buildMinimalContainerForUrlPrefixTest(); - - $decorator = $this->container->get('ezpublish.core.io.prefix_url_decorator'); - - self::assertEquals( - 'http://static.example.com/my/image.png', - $decorator->decorate('my/image.png') - ); - } - - private function buildMinimalContainerForUrlPrefixTest(): void - { - // unrelated, but needed Container configuration - $this->container->setParameter('kernel.environment', 'dev'); - $this->container->setParameter('kernel.debug', true); - $this->container->setParameter('kernel.project_dir', self::FIXTURES_DIR); - $this->container->setParameter('kernel.cache_dir', self::FIXTURES_DIR . '/cache'); - - $this->container->addCompilerPass(new ChainConfigResolverPass()); - $this->container->addCompilerPass(new SetAllServicesPublicPass()); - - $this->container->compile(); - } -} diff --git a/eZ/Bundle/EzPublishIOBundle/Tests/Flysystem/Adapter/SiteAccessAwareLocalAdapterTest.php b/eZ/Bundle/EzPublishIOBundle/Tests/Flysystem/Adapter/SiteAccessAwareLocalAdapterTest.php deleted file mode 100644 index 741ec1e2e3..0000000000 --- a/eZ/Bundle/EzPublishIOBundle/Tests/Flysystem/Adapter/SiteAccessAwareLocalAdapterTest.php +++ /dev/null @@ -1,65 +0,0 @@ -complexConfigProcessor = $this->createMock(ConfigProcessor::class); - } - - private static function getConfig(): array - { - return [ - 'root' => self::getTemporaryRootDir(), - 'writeFlags' => LOCK_EX, - 'linkHandling' => SiteAccessAwareLocalAdapter::DISALLOW_LINKS, - 'permissions' => [], - 'path' => self::DYNAMIC_PATH, - ]; - } - - public function testGetPathPrefix(): void - { - $this->complexConfigProcessor - ->method('processSettingValue') - ->with(self::equalTo(self::DYNAMIC_PATH)) - ->willReturn(self::STATIC_PATH); - - $adapter = new SiteAccessAwareLocalAdapter( - $this->complexConfigProcessor, - self::getConfig() - ); - - $expectedPath = self::getTemporaryRootDir() . '/' . self::STATIC_PATH; - self::assertEquals($expectedPath, $adapter->getPathPrefix()); - } - - public static function tearDownAfterClass(): void - { - $dir = self::getTemporaryRootDir(); - if (is_dir($dir)) { - rmdir($dir); - } - } - - private static function getTemporaryRootDir(): string - { - return sys_get_temp_dir() . '/ezplatform-kernel-tests'; - } -} diff --git a/eZ/Bundle/EzPublishLegacySearchEngineBundle/DependencyInjection/EzPublishLegacySearchEngineExtension.php b/eZ/Bundle/EzPublishLegacySearchEngineBundle/DependencyInjection/EzPublishLegacySearchEngineExtension.php deleted file mode 100644 index 0231e1e1e9..0000000000 --- a/eZ/Bundle/EzPublishLegacySearchEngineBundle/DependencyInjection/EzPublishLegacySearchEngineExtension.php +++ /dev/null @@ -1,36 +0,0 @@ -load('search_engines/legacy.yml'); - - $loader = new YamlFileLoader( - $container, - new FileLocator(__DIR__ . '/../Resources/config') - ); - $loader->load('services.yml'); - } -} diff --git a/eZ/Bundle/EzPublishLegacySearchEngineBundle/EzPublishLegacySearchEngineBundle.php b/eZ/Bundle/EzPublishLegacySearchEngineBundle/EzPublishLegacySearchEngineBundle.php deleted file mode 100644 index 572581a3a6..0000000000 --- a/eZ/Bundle/EzPublishLegacySearchEngineBundle/EzPublishLegacySearchEngineBundle.php +++ /dev/null @@ -1,36 +0,0 @@ -addCompilerPass(new CriteriaConverterPass()); - $container->addCompilerPass(new CriterionFieldValueHandlerRegistryPass()); - $container->addCompilerPass(new SortClauseConverterPass()); - $container->addCompilerPass(new FieldRegistryPass()); - } - - public function getContainerExtension() - { - if (!isset($this->extension)) { - $this->extension = new DependencyInjection\EzPublishLegacySearchEngineExtension(); - } - - return $this->extension; - } -} diff --git a/eZ/Bundle/EzPublishLegacySearchEngineBundle/Resources/config/services.yml b/eZ/Bundle/EzPublishLegacySearchEngineBundle/Resources/config/services.yml deleted file mode 100644 index f60169957e..0000000000 --- a/eZ/Bundle/EzPublishLegacySearchEngineBundle/Resources/config/services.yml +++ /dev/null @@ -1,12 +0,0 @@ -services: - ezpublish.search.legacy.connection.factory: - class: eZ\Bundle\EzPublishLegacySearchEngineBundle\ApiLoader\ConnectionFactory - arguments: - - "@ezpublish.api.repository_configuration_provider" - calls: - - [setContainer, ["@service_container"]] - - ezpublish.api.search_engine.legacy.connection: - class: Doctrine\DBAL\Connection - factory: ["@ezpublish.search.legacy.connection.factory", getConnection] - lazy: true diff --git a/eZ/Bundle/PlatformInstallerBundle/composer.json b/eZ/Bundle/PlatformInstallerBundle/composer.json deleted file mode 100644 index 2585d557a7..0000000000 --- a/eZ/Bundle/PlatformInstallerBundle/composer.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "ezsystems/platform-installer", - "description": "eZ Platform installer", - "license": "GPL-2.0", - "authors": [ - { - "name": "eZ Systems dev team", - "email": "dev-team@ez.no" - } - ], - "minimum-stability": "stable", - "require": {}, - "autoload": { - "psr-4": { "EzSystems\\PlatformInstaller\\": "src/" } - } -} diff --git a/eZ/Bundle/PlatformInstallerBundle/src/DependencyInjection/EzSystemsPlatformInstallerExtension.php b/eZ/Bundle/PlatformInstallerBundle/src/DependencyInjection/EzSystemsPlatformInstallerExtension.php deleted file mode 100644 index fd7395f54b..0000000000 --- a/eZ/Bundle/PlatformInstallerBundle/src/DependencyInjection/EzSystemsPlatformInstallerExtension.php +++ /dev/null @@ -1,21 +0,0 @@ -load('services.yml'); - } -} diff --git a/eZ/Bundle/PlatformInstallerBundle/src/Event/Subscriber/BuildSchemaSubscriber.php b/eZ/Bundle/PlatformInstallerBundle/src/Event/Subscriber/BuildSchemaSubscriber.php deleted file mode 100644 index a4f896f91d..0000000000 --- a/eZ/Bundle/PlatformInstallerBundle/src/Event/Subscriber/BuildSchemaSubscriber.php +++ /dev/null @@ -1,49 +0,0 @@ -schemaFilePath = $schemaFilePath; - } - - /** - * Returns an array of events this subscriber wants to listen to. - * - * @return array - */ - public static function getSubscribedEvents(): array - { - return [ - SchemaBuilderEvents::BUILD_SCHEMA => ['onBuildSchema', 200], - ]; - } - - /** - * @param \EzSystems\DoctrineSchema\API\Event\SchemaBuilderEvent $event - */ - public function onBuildSchema(SchemaBuilderEvent $event): void - { - $event - ->getSchemaBuilder() - ->importSchemaFromFile($this->schemaFilePath); - } -} diff --git a/eZ/Bundle/PlatformInstallerBundle/src/EzSystemsPlatformInstallerBundle.php b/eZ/Bundle/PlatformInstallerBundle/src/EzSystemsPlatformInstallerBundle.php deleted file mode 100644 index 714797cae7..0000000000 --- a/eZ/Bundle/PlatformInstallerBundle/src/EzSystemsPlatformInstallerBundle.php +++ /dev/null @@ -1,34 +0,0 @@ -hasExtension('ez_doctrine_schema')) { - throw new RuntimeException( - sprintf( - 'eZ Platform Installer requires Doctrine Schema Bundle (enable %s)', - DoctrineSchemaBundle::class - ) - ); - } - - parent::build($container); - $container->addCompilerPass(new InstallerTagPass()); - } -} diff --git a/eZ/Bundle/PlatformInstallerBundle/src/Resources/config/services.yml b/eZ/Bundle/PlatformInstallerBundle/src/Resources/config/services.yml deleted file mode 100644 index 83f5ca577b..0000000000 --- a/eZ/Bundle/PlatformInstallerBundle/src/Resources/config/services.yml +++ /dev/null @@ -1,35 +0,0 @@ -services: - EzSystems\PlatformInstallerBundle\Event\Subscriber\BuildSchemaSubscriber: - autoconfigure: true - public: false - arguments: - - '@=service("kernel").locateResource("@EzPublishCoreBundle/Resources/config/storage/legacy/schema.yaml")' - - EzSystems\PlatformInstallerBundle\Installer\DbBasedInstaller: - abstract: true - arguments: ["@ezpublish.persistence.connection"] - lazy: true - - EzSystems\PlatformInstallerBundle\Installer\CoreInstaller: - autowire: true - parent: EzSystems\PlatformInstallerBundle\Installer\DbBasedInstaller - tags: - - { name: ezplatform.installer, type: clean } # left for BC, should be removed in Ibexa 4.0 - - { name: ezplatform.installer, type: ibexa-oss } - - EzSystems\PlatformInstallerBundle\Command\InstallPlatformCommand: - arguments: - $connection: "@ezpublish.persistence.connection" - $installers: [] - $cachePool: '@ezpublish.cache_pool' - $environment: "%kernel.environment%" - $repositoryConfigurationProvider: '@ezpublish.api.repository_configuration_provider' - tags: - - { name: console.command } - - EzSystems\PlatformInstallerBundle\Command\ValidatePasswordHashesCommand: - arguments: - $userStorage: '@ezpublish.fieldType.ezuser.externalStorage' - $passwordHashService: '@eZ\Publish\API\Repository\PasswordHashService' - tags: - - { name: console.command } diff --git a/eZ/Bundle/PlatformInstallerBundle/tests/DependencyInjection/Compiler/InstallerTagPassTest.php b/eZ/Bundle/PlatformInstallerBundle/tests/DependencyInjection/Compiler/InstallerTagPassTest.php deleted file mode 100644 index f98ac41a68..0000000000 --- a/eZ/Bundle/PlatformInstallerBundle/tests/DependencyInjection/Compiler/InstallerTagPassTest.php +++ /dev/null @@ -1,56 +0,0 @@ -setDefinition( - InstallPlatformCommand::class, - new Definition(InstallPlatformCommand::class, ['$installers' => []]) - ); - $definition = new Definition(); - $definition->addTag( - InstallerTagPass::INSTALLER_TAG, - [ - 'type' => 'installer_type', - ] - ); - - $this->setDefinition('service_id', $definition); - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithArgument( - InstallPlatformCommand::class, - '$installers', - [ - 'installer_type' => new Reference('service_id'), - ] - ); - } - - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new InstallerTagPass()); - } -} diff --git a/eZ/Bundle/PlatformInstallerBundle/tests/DependencyInjection/EzSystemsPlatformInstallerExtensionTest.php b/eZ/Bundle/PlatformInstallerBundle/tests/DependencyInjection/EzSystemsPlatformInstallerExtensionTest.php deleted file mode 100644 index c272965def..0000000000 --- a/eZ/Bundle/PlatformInstallerBundle/tests/DependencyInjection/EzSystemsPlatformInstallerExtensionTest.php +++ /dev/null @@ -1,58 +0,0 @@ -load(); - $this->assertContainerBuilderHasServiceDefinitionWithParent( - CoreInstaller::class, - DbBasedInstaller::class - ); - $this->assertContainerBuilderHasServiceDefinitionWithTag( - CoreInstaller::class, - InstallerTagPass::INSTALLER_TAG, - ['type' => 'clean'] - ); - } - - /** - * @covers \EzSystems\PlatformInstallerBundle\DependencyInjection\EzSystemsPlatformInstallerExtension::load - */ - public function testLoadLoadsTaggedInstallerCommand(): void - { - $this->load(); - $this->assertContainerBuilderHasServiceDefinitionWithTag( - InstallPlatformCommand::class, - 'console.command' - ); - } - - protected function getContainerExtensions(): array - { - return [ - new EzSystemsPlatformInstallerExtension(), - ]; - } -} diff --git a/eZ/Bundle/PlatformInstallerBundle/tests/EzSystemsPlatformInstallerBundleTest.php b/eZ/Bundle/PlatformInstallerBundle/tests/EzSystemsPlatformInstallerBundleTest.php deleted file mode 100644 index 9754284108..0000000000 --- a/eZ/Bundle/PlatformInstallerBundle/tests/EzSystemsPlatformInstallerBundleTest.php +++ /dev/null @@ -1,60 +0,0 @@ -bundle = new EzSystemsPlatformInstallerBundle(); - } - - /** - * @covers \EzSystems\PlatformInstallerBundle\EzSystemsPlatformInstallerBundle::build - */ - public function testBuild(): void - { - $container = new ContainerBuilder(); - $container->registerExtension(new DoctrineSchemaExtension()); - $this->bundle->build($container); - - // check if InstallerTagPass was added - self::assertNotEmpty( - array_filter( - $container->getCompilerPassConfig()->getPasses(), - static function (CompilerPassInterface $compilerPass) { - return $compilerPass instanceof InstallerTagPass; - } - ) - ); - } - - /** - * @covers \EzSystems\PlatformInstallerBundle\EzSystemsPlatformInstallerBundle::build - */ - public function testBuildFailsWithoutDoctrineSchemaBundle(): void - { - $container = new ContainerBuilder(); - - $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('eZ Platform Installer requires Doctrine Schema Bundle'); - $this->bundle->build($container); - } -} diff --git a/eZ/Publish/API/Container.php b/eZ/Publish/API/Container.php deleted file mode 100644 index c267148ab3..0000000000 --- a/eZ/Publish/API/Container.php +++ /dev/null @@ -1,24 +0,0 @@ - $contentInfoList - * - * @return array List of VersionInfo items with Content Ids as keys - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function loadVersionInfoListByContentInfo(array $contentInfoList): array; - - /** - * Loads content in a version for the given content info object. - * - * If no version number is given, the method returns the current version - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if version with the given number does not exist - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param array $languages A language priority, filters returned fields and is used as prioritized language code on - * returned value object. If not given all languages are returned. - * @param int|null $versionNo the version number. If not given the current version is returned from $contentInfo - * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function loadContentByContentInfo(ContentInfo $contentInfo, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content; - - /** - * Loads content in the version given by version info. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on - * returned value object. If not given all languages are returned. - * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function loadContentByVersionInfo(VersionInfo $versionInfo, array $languages = null, bool $useAlwaysAvailable = true): Content; - - /** - * Loads content in a version of the given content object. - * - * If no version number is given, the method returns the current version - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the content or version with the given id and languages does not exist - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions - * - * @param mixed $contentId - * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on - * returned value object. If not given all languages are returned. - * @param int|null $versionNo the version number. If not given the current version is returned - * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function loadContent(int $contentId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content; - - /** - * Loads content in a version for the content object reference by the given remote id. - * - * If no version is given, the method returns the current version - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content or version with the given remote id does not exist - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions - * - * @param string $remoteId - * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on - * returned value object. If not given all languages are returned. - * @param int|null $versionNo the version number. If not given the current version is returned - * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function loadContentByRemoteId(string $remoteId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content; - - /** - * Bulk-load Content items by the list of ContentInfo Value Objects. - * - * Note: it does not throw exceptions on load, just ignores erroneous Content item. - * Moreover, since the method works on pre-loaded ContentInfo list, it is assumed that user is - * allowed to access every Content on the list. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo[] $contentInfoList - * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on - * returned value object. If not given all languages are returned. - * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true, - * unless all languages have been asked for. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content[] list of Content items with Content Ids as keys - */ - public function loadContentListByContentInfo(array $contentInfoList, array $languages = [], bool $useAlwaysAvailable = true): iterable; - - /** - * Creates a new content draft assigned to the authenticated user. - * - * If a different userId is given in $contentCreateStruct it is assigned to the given user - * but this required special rights for the authenticated user - * (this is useful for content staging where the transfer process does not - * have to authenticate with the user which created the content object in the source server). - * The user has to publish the draft if it should be visible. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create the content in the given location - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is a provided remote ID which exists in the system or multiple Locations - * are under the same parent or if the a field value is not accepted by the field type - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is missing or is set to an empty value - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct - * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs an array of {@link \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct} for each location parent under which a location should be created for the content - * While optional, it's highly recommended to use Locations for content as a lot of features in the system is usually tied to the tree structure (including default Role policies). - * @param string[]|null $fieldIdentifiersToValidate List of field identifiers for partial validation or null - * for case of full validation. Empty identifiers array is equal to no validation. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - the newly created content draft - */ - public function createContent(ContentCreateStruct $contentCreateStruct, array $locationCreateStructs = [], ?array $fieldIdentifiersToValidate = null): Content; - - /** - * Updates the metadata. - * - * (see {@link ContentMetadataUpdateStruct}) of a content object - to update fields use updateContent - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update the content meta data - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the remoteId in $contentMetadataUpdateStruct is set but already exists - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param \eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct $contentMetadataUpdateStruct - * - * @return \eZ\Publish\API\Repository\Values\Content\Content the content with the updated attributes - */ - public function updateContentMetadata(ContentInfo $contentInfo, ContentMetadataUpdateStruct $contentMetadataUpdateStruct): Content; - - /** - * Deletes a content object including all its versions and locations including their subtrees. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete the content (in one of the locations of the given content object) - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return int[] Affected Location Id's (List of Locations of the Content that was deleted) - */ - public function deleteContent(ContentInfo $contentInfo): iterable; - - /** - * Creates a draft from a published or archived version. - * - * If no version is given, the current published version is used. - * 4.x: The draft is created with the initialLanguage code of the source version or if not present with the main language. - * It can be changed on updating the version. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to create the draft - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo|null $versionInfo - * @param \eZ\Publish\API\Repository\Values\User\User|null $creator Used as creator of the draft if given - otherwise uses current-user - * @param \eZ\Publish\API\Repository\Values\Content\Language|null if not set the draft is created with the initialLanguage code of the source version or if not present with the main language. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - the newly created content draft - */ - public function createContentDraft( - ContentInfo $contentInfo, - ?VersionInfo $versionInfo = null, - ?User $creator = null, - ?Language $language = null - ): Content; - - /** - * Counts drafts for a user. - * - * If no user is given the number of drafts for the authenticated user are returned - * - * @param \eZ\Publish\API\Repository\Values\User\User $user The user to load drafts for, if defined, otherwise drafts for current-user - * - * @return int The number of drafts ({@link VersionInfo}) owned by the given user - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function countContentDrafts(?User $user = null): int; - - /** - * Loads drafts for a user. - * - * If no user is given the drafts for the authenticated user are returned - * - * @deprecated Please use {@see loadContentDraftList()} instead to avoid risking loading too much data. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to load the draft list - * - * @param \eZ\Publish\API\Repository\Values\User\User $user The user to load drafts for, if defined, otherwise drafts for current-user - * - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo[] the drafts ({@link VersionInfo}) owned by the given user - */ - public function loadContentDrafts(?User $user = null): iterable; - - /** - * Loads drafts for a user when content is not in the trash. The list is sorted by modification date. - * - * If no user is given the drafts for the authenticated user are returned - * - * @since 7.5.5 - * - * @param \eZ\Publish\API\Repository\Values\User\User|null $user The user to load drafts for, if defined, otherwise drafts for current-user - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentDraftList - */ - public function loadContentDraftList(?User $user = null, int $offset = 0, int $limit = -1): ContentDraftList; - - /** - * Updates the fields of a draft. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update this version - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentUpdateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is set to an empty value - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if a field value is not accepted by the field type - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct - * @param string[]|null $fieldIdentifiersToValidate List of field identifiers for partial validation or null - * for case of full validation. Empty identifiers array is equal to no validation. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content the content draft with the updated fields - */ - public function updateContent(VersionInfo $versionInfo, ContentUpdateStruct $contentUpdateStruct, ?array $fieldIdentifiersToValidate = null): Content; - - /** - * Publishes a content version. - * - * Publishes a content version and deletes archive versions if they overflow max archive versions. - * Max archive versions are currently a configuration for default max limit, by default set to 5. - * - * @todo Introduce null|int ContentType->versionArchiveLimit to be able to let admins override this per type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to publish this version - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - * @param string[] $translations List of language codes of translations which will be included - * in a published version. - * By default all translations from the current version will be published. - * If the list is provided but does not cover all currently published translations, - * the missing ones will be copied from the currently published version, - * overriding those in the current version. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function publishVersion(VersionInfo $versionInfo, array $translations = Language::ALL): Content; - - /** - * Removes the given version. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is in - * published state or is a last version of Content in non draft state - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove this version - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - */ - public function deleteVersion(VersionInfo $versionInfo): void; - - /** - * Loads all versions for the given content. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to list versions - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the given status is invalid - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param int|null $status - * - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo[] an array of {@link \eZ\Publish\API\Repository\Values\Content\VersionInfo} sorted by creation date - */ - public function loadVersions(ContentInfo $contentInfo, ?int $status = null): iterable; - - /** - * Copies the content to a new location. If no version is given, - * all versions are copied, otherwise only the given version. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to copy the content to the given location - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct $destinationLocationCreateStruct the target location where the content is copied to - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function copyContent(ContentInfo $contentInfo, LocationCreateStruct $destinationLocationCreateStruct, ?VersionInfo $versionInfo = null): Content; - - /** - * Loads all outgoing relations for the given version. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - * - * @return \eZ\Publish\API\Repository\Values\Content\Relation[] - */ - public function loadRelations(VersionInfo $versionInfo): iterable; - - /** - * Counts all incoming relations for the given content object. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return int The number of reverse relations ({@link Relation}) - */ - public function countReverseRelations(ContentInfo $contentInfo): int; - - /** - * Loads all incoming relations for a content object. - * - * The relations come only from published versions of the source content objects - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return \eZ\Publish\API\Repository\Values\Content\Relation[] - */ - public function loadReverseRelations(ContentInfo $contentInfo): iterable; - - /** - * Loads all incoming relations for a content object. - * - * The relations come only from published versions of the source content objects. - * If the user is not allowed to read specific version then UnauthorizedRelationListItem is returned - * {@link \eZ\Publish\API\Repository\Values\Content\RelationList\Item\UnauthorizedRelationListItem} - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\API\Repository\Values\Content\RelationList - */ - public function loadReverseRelationList(ContentInfo $contentInfo, int $offset = 0, int $limit = -1): RelationList; - - /** - * Adds a relation of type common. - * - * The source of the relation is the content and version - * referenced by $versionInfo. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit this version - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $sourceVersion - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContent the destination of the relation - * - * @return \eZ\Publish\API\Repository\Values\Content\Relation the newly created relation - */ - public function addRelation(VersionInfo $sourceVersion, ContentInfo $destinationContent): Relation; - - /** - * Removes a relation of type COMMON from a draft. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed edit this version - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is no relation of type COMMON for the given destination - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $sourceVersion - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContent - */ - public function deleteRelation(VersionInfo $sourceVersion, ContentInfo $destinationContent): void; - - /** - * Delete Content item Translation from all Versions (including archived ones) of a Content Object. - * - * NOTE: this operation is risky and permanent, so user interface should provide a warning before performing it. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the specified Translation - * is the Main Translation of a Content Item. - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed - * to delete the content (in one of the locations of the given Content Item). - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if languageCode argument - * is invalid for the given content. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param string $languageCode - * - * @since 6.13 - */ - public function deleteTranslation(ContentInfo $contentInfo, string $languageCode): void; - - /** - * Delete specified Translation from a Content Draft. - * - * When using together with ContentService::publishVersion() method, make sure to not provide deleted translation - * in translations array, as it is going to be copied again from published version. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the specified Translation - * is the only one the Content Draft has or it is the main Translation of a Content Object. - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed - * to edit the Content (in one of the locations of the given Content Object). - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if languageCode argument - * is invalid for the given Draft. - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if specified Version was not found - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo Content Version Draft - * @param string $languageCode Language code of the Translation to be removed - * - * @return \eZ\Publish\API\Repository\Values\Content\Content Content Draft w/o the specified Translation - * - * @since 6.12 - */ - public function deleteTranslationFromDraft(VersionInfo $versionInfo, string $languageCode): Content; - - /** - * Hides Content by making all the Locations appear hidden. - * It does not persist hidden state on Location object itself. - * - * Content hidden by this API can be revealed by revealContent API. - * - * @see revealContent - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - */ - public function hideContent(ContentInfo $contentInfo): void; - - /** - * Reveals Content hidden by hideContent API. - * Locations which were hidden before hiding Content will remain hidden. - * - * @see hideContent - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - */ - public function revealContent(ContentInfo $contentInfo): void; - - /** - * Instantiates a new content create struct object. - * - * alwaysAvailable is set to the ContentType's defaultAlwaysAvailable - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * @param string $mainLanguageCode - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct - */ - public function newContentCreateStruct(ContentType $contentType, string $mainLanguageCode): ContentCreateStruct; - - /** - * Instantiates a new content meta data update struct. - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct - */ - public function newContentMetadataUpdateStruct(): ContentMetadataUpdateStruct; - - /** - * Instantiates a new content update struct. - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct - */ - public function newContentUpdateStruct(): ContentUpdateStruct; - - /** - * Validates given content related ValueObject returning field errors structure as a result. - * - * @param array $context Additional context parameters to be used by validators. - * @param string[]|null $fieldIdentifiersToValidate List of field identifiers for partial validation or null - * for case of full validation. Empty identifiers array is equal to no validation. - * - * @return array Validation errors grouped by field definition and language code, in format: - * $returnValue[string|int $fieldDefinitionId][string $languageCode] = $fieldErrors; - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function validate(ValueObject $object, array $context, ?array $fieldIdentifiersToValidate = null): array; - - /** - * Fetch Content items from the Repository filtered by the given conditions. - * - * @param string[] $languages a list of language codes to be added as additional constraints. - * If skipped, by default, unless SiteAccessAware layer has been disabled, languages set - * for a SiteAccess in a current context will be used. - */ - public function find(Filter $filter, ?array $languages = null): ContentList; - - /** - * Count total number of items returned by {@see find} method. - * - * @param string[] $languages a list of language codes to be added as additional constraints. - * If skipped, by default, unless SiteAccessAware layer has been disabled, languages set - * for a SiteAccess in a current context will be used. - */ - public function count(Filter $filter, ?array $languages = null): int; -} diff --git a/eZ/Publish/API/Repository/ContentTypeService.php b/eZ/Publish/API/Repository/ContentTypeService.php deleted file mode 100644 index 6fc55867f9..0000000000 --- a/eZ/Publish/API/Repository/ContentTypeService.php +++ /dev/null @@ -1,396 +0,0 @@ -location = $location; - } - - public function getLocation(): Location - { - return $this->location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Bookmark/BeforeDeleteBookmarkEvent.php b/eZ/Publish/API/Repository/Events/Bookmark/BeforeDeleteBookmarkEvent.php deleted file mode 100644 index aa15645489..0000000000 --- a/eZ/Publish/API/Repository/Events/Bookmark/BeforeDeleteBookmarkEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -location = $location; - } - - public function getLocation(): Location - { - return $this->location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Bookmark/CreateBookmarkEvent.php b/eZ/Publish/API/Repository/Events/Bookmark/CreateBookmarkEvent.php deleted file mode 100644 index ab67ff9655..0000000000 --- a/eZ/Publish/API/Repository/Events/Bookmark/CreateBookmarkEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -location = $location; - } - - public function getLocation(): Location - { - return $this->location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Bookmark/DeleteBookmarkEvent.php b/eZ/Publish/API/Repository/Events/Bookmark/DeleteBookmarkEvent.php deleted file mode 100644 index c2df3f3a22..0000000000 --- a/eZ/Publish/API/Repository/Events/Bookmark/DeleteBookmarkEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -location = $location; - } - - public function getLocation(): Location - { - return $this->location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/AddRelationEvent.php b/eZ/Publish/API/Repository/Events/Content/AddRelationEvent.php deleted file mode 100644 index fb2c5a584b..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/AddRelationEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -relation = $relation; - $this->sourceVersion = $sourceVersion; - $this->destinationContent = $destinationContent; - } - - public function getRelation(): Relation - { - return $this->relation; - } - - public function getSourceVersion(): VersionInfo - { - return $this->sourceVersion; - } - - public function getDestinationContent(): ContentInfo - { - return $this->destinationContent; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/BeforeAddRelationEvent.php b/eZ/Publish/API/Repository/Events/Content/BeforeAddRelationEvent.php deleted file mode 100644 index deb0ad85c1..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/BeforeAddRelationEvent.php +++ /dev/null @@ -1,62 +0,0 @@ -sourceVersion = $sourceVersion; - $this->destinationContent = $destinationContent; - } - - public function getSourceVersion(): VersionInfo - { - return $this->sourceVersion; - } - - public function getDestinationContent(): ContentInfo - { - return $this->destinationContent; - } - - public function getRelation(): Relation - { - if (!$this->hasRelation()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasRelation() or set it using setRelation() before you call the getter.', Relation::class)); - } - - return $this->relation; - } - - public function setRelation(?Relation $relation): void - { - $this->relation = $relation; - } - - public function hasRelation(): bool - { - return $this->relation instanceof Relation; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/BeforeCopyContentEvent.php b/eZ/Publish/API/Repository/Events/Content/BeforeCopyContentEvent.php deleted file mode 100644 index cad6f2e16b..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/BeforeCopyContentEvent.php +++ /dev/null @@ -1,75 +0,0 @@ -contentInfo = $contentInfo; - $this->destinationLocationCreateStruct = $destinationLocationCreateStruct; - $this->versionInfo = $versionInfo; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getDestinationLocationCreateStruct(): LocationCreateStruct - { - return $this->destinationLocationCreateStruct; - } - - public function getVersionInfo(): ?VersionInfo - { - return $this->versionInfo; - } - - public function getContent(): Content - { - if (!$this->hasContent()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not a type of %s. Check hasContent() or set it using setContent() before you call the getter.', Content::class)); - } - - return $this->content; - } - - public function setContent(?Content $content): void - { - $this->content = $content; - } - - public function hasContent(): bool - { - return $this->content instanceof Content; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/BeforeCreateContentDraftEvent.php b/eZ/Publish/API/Repository/Events/Content/BeforeCreateContentDraftEvent.php deleted file mode 100644 index 2f63fcc20f..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/BeforeCreateContentDraftEvent.php +++ /dev/null @@ -1,86 +0,0 @@ -contentInfo = $contentInfo; - $this->versionInfo = $versionInfo; - $this->creator = $creator; - $this->language = $language; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getVersionInfo(): ?VersionInfo - { - return $this->versionInfo; - } - - public function getCreator(): ?User - { - return $this->creator; - } - - public function getLanguage(): ?Language - { - return $this->language; - } - - public function getContentDraft(): Content - { - if (!$this->hasContentDraft()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContentDraft() or set it using setContentDraft() before you call the getter.', Content::class)); - } - - return $this->contentDraft; - } - - public function setContentDraft(?Content $contentDraft): void - { - $this->contentDraft = $contentDraft; - } - - public function hasContentDraft(): bool - { - return $this->contentDraft instanceof Content; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/BeforeDeleteContentEvent.php b/eZ/Publish/API/Repository/Events/Content/BeforeDeleteContentEvent.php deleted file mode 100644 index 6b58781118..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/BeforeDeleteContentEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -contentInfo = $contentInfo; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getLocations(): array - { - if (!$this->hasLocations()) { - throw new UnexpectedValueException('If you use stopPropagation(), you must set the event return value to be an array using setLocations()'); - } - - return $this->locations; - } - - public function setLocations(?array $locations): void - { - $this->locations = $locations; - } - - public function hasLocations(): bool - { - return is_array($this->locations); - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/BeforeDeleteRelationEvent.php b/eZ/Publish/API/Repository/Events/Content/BeforeDeleteRelationEvent.php deleted file mode 100644 index 6f4e39396d..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/BeforeDeleteRelationEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -sourceVersion = $sourceVersion; - $this->destinationContent = $destinationContent; - } - - public function getSourceVersion(): VersionInfo - { - return $this->sourceVersion; - } - - public function getDestinationContent(): ContentInfo - { - return $this->destinationContent; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/BeforeDeleteTranslationEvent.php b/eZ/Publish/API/Repository/Events/Content/BeforeDeleteTranslationEvent.php deleted file mode 100644 index 6701e486cd..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/BeforeDeleteTranslationEvent.php +++ /dev/null @@ -1,36 +0,0 @@ -contentInfo = $contentInfo; - $this->languageCode = $languageCode; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getLanguageCode() - { - return $this->languageCode; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/BeforeDeleteVersionEvent.php b/eZ/Publish/API/Repository/Events/Content/BeforeDeleteVersionEvent.php deleted file mode 100644 index 6db88a340c..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/BeforeDeleteVersionEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -versionInfo = $versionInfo; - } - - public function getVersionInfo(): VersionInfo - { - return $this->versionInfo; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/BeforeHideContentEvent.php b/eZ/Publish/API/Repository/Events/Content/BeforeHideContentEvent.php deleted file mode 100644 index 2e0822d252..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/BeforeHideContentEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -contentInfo = $contentInfo; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/BeforePublishVersionEvent.php b/eZ/Publish/API/Repository/Events/Content/BeforePublishVersionEvent.php deleted file mode 100644 index f86c7f5e3a..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/BeforePublishVersionEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -versionInfo = $versionInfo; - $this->translations = $translations; - } - - public function getVersionInfo(): VersionInfo - { - return $this->versionInfo; - } - - public function getTranslations(): array - { - return $this->translations; - } - - public function getContent(): Content - { - if (!$this->hasContent()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContent() or set it using setContent() before you call the getter.', Content::class)); - } - - return $this->content; - } - - public function setContent(?Content $content): void - { - $this->content = $content; - } - - public function hasContent(): bool - { - return $this->content instanceof Content; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/BeforeRevealContentEvent.php b/eZ/Publish/API/Repository/Events/Content/BeforeRevealContentEvent.php deleted file mode 100644 index 6bc46e8254..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/BeforeRevealContentEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -contentInfo = $contentInfo; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/BeforeUpdateContentEvent.php b/eZ/Publish/API/Repository/Events/Content/BeforeUpdateContentEvent.php deleted file mode 100644 index 0e313596d3..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/BeforeUpdateContentEvent.php +++ /dev/null @@ -1,77 +0,0 @@ -versionInfo = $versionInfo; - $this->contentUpdateStruct = $contentUpdateStruct; - $this->fieldIdentifiersToValidate = $fieldIdentifiersToValidate; - } - - public function getVersionInfo(): VersionInfo - { - return $this->versionInfo; - } - - public function getContentUpdateStruct(): ContentUpdateStruct - { - return $this->contentUpdateStruct; - } - - /** - * @return string[]|null - */ - public function getFieldIdentifiersToValidate(): ?array - { - return $this->fieldIdentifiersToValidate; - } - - public function getContent(): Content - { - if (!$this->hasContent()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContent() or set it using setContent() before you call the getter.', Content::class)); - } - - return $this->content; - } - - public function setContent(?Content $content): void - { - $this->content = $content; - } - - public function hasContent(): bool - { - return $this->content instanceof Content; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/BeforeUpdateContentMetadataEvent.php b/eZ/Publish/API/Repository/Events/Content/BeforeUpdateContentMetadataEvent.php deleted file mode 100644 index 5c4764956d..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/BeforeUpdateContentMetadataEvent.php +++ /dev/null @@ -1,62 +0,0 @@ -contentInfo = $contentInfo; - $this->contentMetadataUpdateStruct = $contentMetadataUpdateStruct; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getContentMetadataUpdateStruct(): ContentMetadataUpdateStruct - { - return $this->contentMetadataUpdateStruct; - } - - public function getContent(): Content - { - if (!$this->hasContent()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContent() or set it using setContent() before you call the getter.', Content::class)); - } - - return $this->content; - } - - public function setContent(?Content $content): void - { - $this->content = $content; - } - - public function hasContent(): bool - { - return $this->content instanceof Content; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/CopyContentEvent.php b/eZ/Publish/API/Repository/Events/Content/CopyContentEvent.php deleted file mode 100644 index 872a17a36b..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/CopyContentEvent.php +++ /dev/null @@ -1,62 +0,0 @@ -content = $content; - $this->contentInfo = $contentInfo; - $this->destinationLocationCreateStruct = $destinationLocationCreateStruct; - $this->versionInfo = $versionInfo; - } - - public function getContent(): Content - { - return $this->content; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getDestinationLocationCreateStruct(): LocationCreateStruct - { - return $this->destinationLocationCreateStruct; - } - - public function getVersionInfo(): ?VersionInfo - { - return $this->versionInfo; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/CreateContentDraftEvent.php b/eZ/Publish/API/Repository/Events/Content/CreateContentDraftEvent.php deleted file mode 100644 index 124a179cc9..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/CreateContentDraftEvent.php +++ /dev/null @@ -1,73 +0,0 @@ -contentDraft = $contentDraft; - $this->contentInfo = $contentInfo; - $this->versionInfo = $versionInfo; - $this->creator = $creator; - $this->language = $language; - } - - public function getContentDraft(): Content - { - return $this->contentDraft; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getVersionInfo(): ?VersionInfo - { - return $this->versionInfo; - } - - public function getCreator(): ?User - { - return $this->creator; - } - - public function getLanguage(): ?Language - { - return $this->language; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/CreateContentEvent.php b/eZ/Publish/API/Repository/Events/Content/CreateContentEvent.php deleted file mode 100644 index d7385e2e7a..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/CreateContentEvent.php +++ /dev/null @@ -1,63 +0,0 @@ -content = $content; - $this->contentCreateStruct = $contentCreateStruct; - $this->locationCreateStructs = $locationCreateStructs; - $this->fieldIdentifiersToValidate = $fieldIdentifiersToValidate; - } - - public function getContentCreateStruct(): ContentCreateStruct - { - return $this->contentCreateStruct; - } - - public function getLocationCreateStructs(): array - { - return $this->locationCreateStructs; - } - - public function getContent(): Content - { - return $this->content; - } - - /** - * @return string[]|null - */ - public function getFieldIdentifiersToValidate(): ?array - { - return $this->fieldIdentifiersToValidate; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/DeleteContentEvent.php b/eZ/Publish/API/Repository/Events/Content/DeleteContentEvent.php deleted file mode 100644 index e34e1a66cf..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/DeleteContentEvent.php +++ /dev/null @@ -1,39 +0,0 @@ -locations = $locations; - $this->contentInfo = $contentInfo; - } - - public function getLocations(): array - { - return $this->locations; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/DeleteRelationEvent.php b/eZ/Publish/API/Repository/Events/Content/DeleteRelationEvent.php deleted file mode 100644 index 4b07ac6e0b..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/DeleteRelationEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -sourceVersion = $sourceVersion; - $this->destinationContent = $destinationContent; - } - - public function getSourceVersion(): VersionInfo - { - return $this->sourceVersion; - } - - public function getDestinationContent(): ContentInfo - { - return $this->destinationContent; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/DeleteTranslationEvent.php b/eZ/Publish/API/Repository/Events/Content/DeleteTranslationEvent.php deleted file mode 100644 index cebf59fe95..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/DeleteTranslationEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -contentInfo = $contentInfo; - $this->languageCode = $languageCode; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getLanguageCode() - { - return $this->languageCode; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/DeleteVersionEvent.php b/eZ/Publish/API/Repository/Events/Content/DeleteVersionEvent.php deleted file mode 100644 index eadf0a154b..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/DeleteVersionEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -versionInfo = $versionInfo; - } - - public function getVersionInfo(): VersionInfo - { - return $this->versionInfo; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/HideContentEvent.php b/eZ/Publish/API/Repository/Events/Content/HideContentEvent.php deleted file mode 100644 index 8fbd328f0b..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/HideContentEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -contentInfo = $contentInfo; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/PublishVersionEvent.php b/eZ/Publish/API/Repository/Events/Content/PublishVersionEvent.php deleted file mode 100644 index 930a05e253..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/PublishVersionEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -content = $content; - $this->versionInfo = $versionInfo; - $this->translations = $translations; - } - - public function getContent(): Content - { - return $this->content; - } - - public function getVersionInfo(): VersionInfo - { - return $this->versionInfo; - } - - public function getTranslations(): array - { - return $this->translations; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/RevealContentEvent.php b/eZ/Publish/API/Repository/Events/Content/RevealContentEvent.php deleted file mode 100644 index 732e1251f4..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/RevealContentEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -contentInfo = $contentInfo; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/UpdateContentEvent.php b/eZ/Publish/API/Repository/Events/Content/UpdateContentEvent.php deleted file mode 100644 index d4a62db4ff..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/UpdateContentEvent.php +++ /dev/null @@ -1,64 +0,0 @@ -content = $content; - $this->versionInfo = $versionInfo; - $this->contentUpdateStruct = $contentUpdateStruct; - $this->fieldIdentifiersToValidate = $fieldIdentifiersToValidate; - } - - public function getContent(): Content - { - return $this->content; - } - - public function getVersionInfo(): VersionInfo - { - return $this->versionInfo; - } - - public function getContentUpdateStruct(): ContentUpdateStruct - { - return $this->contentUpdateStruct; - } - - /** - * @return string[]|null - */ - public function getFieldIdentifiersToValidate(): ?array - { - return $this->fieldIdentifiersToValidate; - } -} diff --git a/eZ/Publish/API/Repository/Events/Content/UpdateContentMetadataEvent.php b/eZ/Publish/API/Repository/Events/Content/UpdateContentMetadataEvent.php deleted file mode 100644 index 0a30428f8f..0000000000 --- a/eZ/Publish/API/Repository/Events/Content/UpdateContentMetadataEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -content = $content; - $this->contentInfo = $contentInfo; - $this->contentMetadataUpdateStruct = $contentMetadataUpdateStruct; - } - - public function getContent(): Content - { - return $this->content; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getContentMetadataUpdateStruct(): ContentMetadataUpdateStruct - { - return $this->contentMetadataUpdateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/AddFieldDefinitionEvent.php b/eZ/Publish/API/Repository/Events/ContentType/AddFieldDefinitionEvent.php deleted file mode 100644 index a249be2e29..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/AddFieldDefinitionEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -contentTypeDraft = $contentTypeDraft; - $this->fieldDefinitionCreateStruct = $fieldDefinitionCreateStruct; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } - - public function getFieldDefinitionCreateStruct(): FieldDefinitionCreateStruct - { - return $this->fieldDefinitionCreateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/AssignContentTypeGroupEvent.php b/eZ/Publish/API/Repository/Events/ContentType/AssignContentTypeGroupEvent.php deleted file mode 100644 index 35280eb9b6..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/AssignContentTypeGroupEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -contentType = $contentType; - $this->contentTypeGroup = $contentTypeGroup; - } - - public function getContentType(): ContentType - { - return $this->contentType; - } - - public function getContentTypeGroup(): ContentTypeGroup - { - return $this->contentTypeGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeAddFieldDefinitionEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeAddFieldDefinitionEvent.php deleted file mode 100644 index f65b208bd5..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeAddFieldDefinitionEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -contentTypeDraft = $contentTypeDraft; - $this->fieldDefinitionCreateStruct = $fieldDefinitionCreateStruct; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } - - public function getFieldDefinitionCreateStruct(): FieldDefinitionCreateStruct - { - return $this->fieldDefinitionCreateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeAssignContentTypeGroupEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeAssignContentTypeGroupEvent.php deleted file mode 100644 index dd911ce587..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeAssignContentTypeGroupEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -contentType = $contentType; - $this->contentTypeGroup = $contentTypeGroup; - } - - public function getContentType(): ContentType - { - return $this->contentType; - } - - public function getContentTypeGroup(): ContentTypeGroup - { - return $this->contentTypeGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeCopyContentTypeEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeCopyContentTypeEvent.php deleted file mode 100644 index 200c55bbf1..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeCopyContentTypeEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -contentType = $contentType; - $this->creator = $creator; - } - - public function getContentType(): ContentType - { - return $this->contentType; - } - - public function getCreator(): ?User - { - return $this->creator; - } - - public function getContentTypeCopy(): ContentType - { - if (!$this->hasContentTypeCopy()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContentTypeCopy() or set it using setContentTypeCopy() before you call the getter.', ContentType::class)); - } - - return $this->contentTypeCopy; - } - - public function setContentTypeCopy(?ContentType $contentTypeCopy): void - { - $this->contentTypeCopy = $contentTypeCopy; - } - - public function hasContentTypeCopy(): bool - { - return $this->contentTypeCopy instanceof ContentType; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeCreateContentTypeDraftEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeCreateContentTypeDraftEvent.php deleted file mode 100644 index 10a3f0c501..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeCreateContentTypeDraftEvent.php +++ /dev/null @@ -1,52 +0,0 @@ -contentType = $contentType; - } - - public function getContentType(): ContentType - { - return $this->contentType; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - if (!$this->hasContentTypeDraft()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContentTypeDraft() or set it using setContentTypeDraft() before you call the getter.', ContentTypeDraft::class)); - } - - return $this->contentTypeDraft; - } - - public function setContentTypeDraft(?ContentTypeDraft $contentTypeDraft): void - { - $this->contentTypeDraft = $contentTypeDraft; - } - - public function hasContentTypeDraft(): bool - { - return $this->contentTypeDraft instanceof ContentTypeDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeCreateContentTypeEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeCreateContentTypeEvent.php deleted file mode 100644 index 65f1a69dc0..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeCreateContentTypeEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -contentTypeCreateStruct = $contentTypeCreateStruct; - $this->contentTypeGroups = $contentTypeGroups; - } - - public function getContentTypeCreateStruct(): ContentTypeCreateStruct - { - return $this->contentTypeCreateStruct; - } - - public function getContentTypeGroups(): array - { - return $this->contentTypeGroups; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - if (!$this->hasContentTypeDraft()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContentTypeDraft() or set it using setContentTypeDraft() before you call the getter.', ContentTypeDraft::class)); - } - - return $this->contentTypeDraft; - } - - public function setContentTypeDraft(?ContentTypeDraft $contentTypeDraft): void - { - $this->contentTypeDraft = $contentTypeDraft; - } - - public function hasContentTypeDraft(): bool - { - return $this->contentTypeDraft instanceof ContentTypeDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeCreateContentTypeGroupEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeCreateContentTypeGroupEvent.php deleted file mode 100644 index 6b3361a4b9..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeCreateContentTypeGroupEvent.php +++ /dev/null @@ -1,52 +0,0 @@ -contentTypeGroupCreateStruct = $contentTypeGroupCreateStruct; - } - - public function getContentTypeGroupCreateStruct(): ContentTypeGroupCreateStruct - { - return $this->contentTypeGroupCreateStruct; - } - - public function getContentTypeGroup(): ContentTypeGroup - { - if (!$this->hasContentTypeGroup()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContentTypeGroup() or set it using setContentTypeGroup() before you call the getter.', ContentTypeGroup::class)); - } - - return $this->contentTypeGroup; - } - - public function setContentTypeGroup(?ContentTypeGroup $contentTypeGroup): void - { - $this->contentTypeGroup = $contentTypeGroup; - } - - public function hasContentTypeGroup(): bool - { - return $this->contentTypeGroup instanceof ContentTypeGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeDeleteContentTypeEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeDeleteContentTypeEvent.php deleted file mode 100644 index 2df965ce0d..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeDeleteContentTypeEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -contentType = $contentType; - } - - public function getContentType(): ContentType - { - return $this->contentType; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeDeleteContentTypeGroupEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeDeleteContentTypeGroupEvent.php deleted file mode 100644 index 69fbfa95fe..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeDeleteContentTypeGroupEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -contentTypeGroup = $contentTypeGroup; - } - - public function getContentTypeGroup(): ContentTypeGroup - { - return $this->contentTypeGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforePublishContentTypeDraftEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforePublishContentTypeDraftEvent.php deleted file mode 100644 index e1d34e5233..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforePublishContentTypeDraftEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -contentTypeDraft = $contentTypeDraft; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeRemoveContentTypeTranslationEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeRemoveContentTypeTranslationEvent.php deleted file mode 100644 index 13319dd96e..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeRemoveContentTypeTranslationEvent.php +++ /dev/null @@ -1,60 +0,0 @@ -contentTypeDraft = $contentTypeDraft; - $this->languageCode = $languageCode; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } - - public function getLanguageCode(): string - { - return $this->languageCode; - } - - public function getNewContentTypeDraft(): ContentTypeDraft - { - if (!$this->hasNewContentTypeDraft()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasNewContentTypeDraft() or set it using setNewContentTypeDraft() before you call the getter.', ContentTypeDraft::class)); - } - - return $this->newContentTypeDraft; - } - - public function setNewContentTypeDraft(?ContentTypeDraft $newContentTypeDraft): void - { - $this->newContentTypeDraft = $newContentTypeDraft; - } - - public function hasNewContentTypeDraft(): bool - { - return $this->newContentTypeDraft instanceof ContentTypeDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeRemoveFieldDefinitionEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeRemoveFieldDefinitionEvent.php deleted file mode 100644 index 1f2055e4c9..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeRemoveFieldDefinitionEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -contentTypeDraft = $contentTypeDraft; - $this->fieldDefinition = $fieldDefinition; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } - - public function getFieldDefinition(): FieldDefinition - { - return $this->fieldDefinition; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeUnassignContentTypeGroupEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeUnassignContentTypeGroupEvent.php deleted file mode 100644 index 52e5b3b14a..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeUnassignContentTypeGroupEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -contentType = $contentType; - $this->contentTypeGroup = $contentTypeGroup; - } - - public function getContentType(): ContentType - { - return $this->contentType; - } - - public function getContentTypeGroup(): ContentTypeGroup - { - return $this->contentTypeGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeUpdateContentTypeDraftEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeUpdateContentTypeDraftEvent.php deleted file mode 100644 index 921742c55e..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeUpdateContentTypeDraftEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -contentTypeDraft = $contentTypeDraft; - $this->contentTypeUpdateStruct = $contentTypeUpdateStruct; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } - - public function getContentTypeUpdateStruct(): ContentTypeUpdateStruct - { - return $this->contentTypeUpdateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeUpdateContentTypeGroupEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeUpdateContentTypeGroupEvent.php deleted file mode 100644 index 79820af6d3..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeUpdateContentTypeGroupEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -contentTypeGroup = $contentTypeGroup; - $this->contentTypeGroupUpdateStruct = $contentTypeGroupUpdateStruct; - } - - public function getContentTypeGroup(): ContentTypeGroup - { - return $this->contentTypeGroup; - } - - public function getContentTypeGroupUpdateStruct(): ContentTypeGroupUpdateStruct - { - return $this->contentTypeGroupUpdateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/BeforeUpdateFieldDefinitionEvent.php b/eZ/Publish/API/Repository/Events/ContentType/BeforeUpdateFieldDefinitionEvent.php deleted file mode 100644 index 53d4ebb304..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/BeforeUpdateFieldDefinitionEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -contentTypeDraft = $contentTypeDraft; - $this->fieldDefinition = $fieldDefinition; - $this->fieldDefinitionUpdateStruct = $fieldDefinitionUpdateStruct; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } - - public function getFieldDefinition(): FieldDefinition - { - return $this->fieldDefinition; - } - - public function getFieldDefinitionUpdateStruct(): FieldDefinitionUpdateStruct - { - return $this->fieldDefinitionUpdateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/CopyContentTypeEvent.php b/eZ/Publish/API/Repository/Events/ContentType/CopyContentTypeEvent.php deleted file mode 100644 index b8488235fa..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/CopyContentTypeEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -contentTypeCopy = $contentTypeCopy; - $this->contentType = $contentType; - $this->creator = $creator; - } - - public function getContentTypeCopy(): ContentType - { - return $this->contentTypeCopy; - } - - public function getContentType(): ContentType - { - return $this->contentType; - } - - public function getCreator(): ?User - { - return $this->creator; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/CreateContentTypeDraftEvent.php b/eZ/Publish/API/Repository/Events/ContentType/CreateContentTypeDraftEvent.php deleted file mode 100644 index 9bdfd4b31a..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/CreateContentTypeDraftEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -contentTypeDraft = $contentTypeDraft; - $this->contentType = $contentType; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } - - public function getContentType(): ContentType - { - return $this->contentType; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/CreateContentTypeEvent.php b/eZ/Publish/API/Repository/Events/ContentType/CreateContentTypeEvent.php deleted file mode 100644 index 786a0f04e1..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/CreateContentTypeEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -contentTypeDraft = $contentTypeDraft; - $this->contentTypeCreateStruct = $contentTypeCreateStruct; - $this->contentTypeGroups = $contentTypeGroups; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } - - public function getContentTypeCreateStruct(): ContentTypeCreateStruct - { - return $this->contentTypeCreateStruct; - } - - public function getContentTypeGroups(): array - { - return $this->contentTypeGroups; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/CreateContentTypeGroupEvent.php b/eZ/Publish/API/Repository/Events/ContentType/CreateContentTypeGroupEvent.php deleted file mode 100644 index 74823c3a05..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/CreateContentTypeGroupEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -contentTypeGroup = $contentTypeGroup; - $this->contentTypeGroupCreateStruct = $contentTypeGroupCreateStruct; - } - - public function getReturnValue(): ContentTypeGroup - { - return $this->contentTypeGroup; - } - - public function getContentTypeGroupCreateStruct(): ContentTypeGroupCreateStruct - { - return $this->contentTypeGroupCreateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/DeleteContentTypeEvent.php b/eZ/Publish/API/Repository/Events/ContentType/DeleteContentTypeEvent.php deleted file mode 100644 index f8233bce2c..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/DeleteContentTypeEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -contentType = $contentType; - } - - public function getContentType(): ContentType - { - return $this->contentType; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/DeleteContentTypeGroupEvent.php b/eZ/Publish/API/Repository/Events/ContentType/DeleteContentTypeGroupEvent.php deleted file mode 100644 index 79f4e61ae0..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/DeleteContentTypeGroupEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -contentTypeGroup = $contentTypeGroup; - } - - public function getContentTypeGroup(): ContentTypeGroup - { - return $this->contentTypeGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/PublishContentTypeDraftEvent.php b/eZ/Publish/API/Repository/Events/ContentType/PublishContentTypeDraftEvent.php deleted file mode 100644 index 685375333d..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/PublishContentTypeDraftEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -contentTypeDraft = $contentTypeDraft; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/RemoveContentTypeTranslationEvent.php b/eZ/Publish/API/Repository/Events/ContentType/RemoveContentTypeTranslationEvent.php deleted file mode 100644 index 1f9b283c75..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/RemoveContentTypeTranslationEvent.php +++ /dev/null @@ -1,49 +0,0 @@ -newContentTypeDraft = $newContentTypeDraft; - $this->contentTypeDraft = $contentTypeDraft; - $this->languageCode = $languageCode; - } - - public function getNewContentTypeDraft(): ContentTypeDraft - { - return $this->newContentTypeDraft; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } - - public function getLanguageCode(): string - { - return $this->languageCode; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/RemoveFieldDefinitionEvent.php b/eZ/Publish/API/Repository/Events/ContentType/RemoveFieldDefinitionEvent.php deleted file mode 100644 index c4495340ca..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/RemoveFieldDefinitionEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -contentTypeDraft = $contentTypeDraft; - $this->fieldDefinition = $fieldDefinition; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } - - public function getFieldDefinition(): FieldDefinition - { - return $this->fieldDefinition; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/UnassignContentTypeGroupEvent.php b/eZ/Publish/API/Repository/Events/ContentType/UnassignContentTypeGroupEvent.php deleted file mode 100644 index 60ab730c9f..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/UnassignContentTypeGroupEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -contentType = $contentType; - $this->contentTypeGroup = $contentTypeGroup; - } - - public function getContentType(): ContentType - { - return $this->contentType; - } - - public function getContentTypeGroup(): ContentTypeGroup - { - return $this->contentTypeGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/UpdateContentTypeDraftEvent.php b/eZ/Publish/API/Repository/Events/ContentType/UpdateContentTypeDraftEvent.php deleted file mode 100644 index 46667dc853..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/UpdateContentTypeDraftEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -contentTypeDraft = $contentTypeDraft; - $this->contentTypeUpdateStruct = $contentTypeUpdateStruct; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } - - public function getContentTypeUpdateStruct(): ContentTypeUpdateStruct - { - return $this->contentTypeUpdateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/UpdateContentTypeGroupEvent.php b/eZ/Publish/API/Repository/Events/ContentType/UpdateContentTypeGroupEvent.php deleted file mode 100644 index 49627bab45..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/UpdateContentTypeGroupEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -contentTypeGroup = $contentTypeGroup; - $this->contentTypeGroupUpdateStruct = $contentTypeGroupUpdateStruct; - } - - public function getContentTypeGroup(): ContentTypeGroup - { - return $this->contentTypeGroup; - } - - public function getContentTypeGroupUpdateStruct(): ContentTypeGroupUpdateStruct - { - return $this->contentTypeGroupUpdateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/ContentType/UpdateFieldDefinitionEvent.php b/eZ/Publish/API/Repository/Events/ContentType/UpdateFieldDefinitionEvent.php deleted file mode 100644 index edc60590c5..0000000000 --- a/eZ/Publish/API/Repository/Events/ContentType/UpdateFieldDefinitionEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -contentTypeDraft = $contentTypeDraft; - $this->fieldDefinition = $fieldDefinition; - $this->fieldDefinitionUpdateStruct = $fieldDefinitionUpdateStruct; - } - - public function getContentTypeDraft(): ContentTypeDraft - { - return $this->contentTypeDraft; - } - - public function getFieldDefinition(): FieldDefinition - { - return $this->fieldDefinition; - } - - public function getFieldDefinitionUpdateStruct(): FieldDefinitionUpdateStruct - { - return $this->fieldDefinitionUpdateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/Language/BeforeCreateLanguageEvent.php b/eZ/Publish/API/Repository/Events/Language/BeforeCreateLanguageEvent.php deleted file mode 100644 index ec307431d4..0000000000 --- a/eZ/Publish/API/Repository/Events/Language/BeforeCreateLanguageEvent.php +++ /dev/null @@ -1,52 +0,0 @@ -languageCreateStruct = $languageCreateStruct; - } - - public function getLanguageCreateStruct(): LanguageCreateStruct - { - return $this->languageCreateStruct; - } - - public function getLanguage(): Language - { - if (!$this->hasLanguage()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasLanguage() or set it using setLanguage() before you call the getter.', Language::class)); - } - - return $this->language; - } - - public function setLanguage(?Language $language): void - { - $this->language = $language; - } - - public function hasLanguage(): bool - { - return $this->language instanceof Language; - } -} diff --git a/eZ/Publish/API/Repository/Events/Language/BeforeDeleteLanguageEvent.php b/eZ/Publish/API/Repository/Events/Language/BeforeDeleteLanguageEvent.php deleted file mode 100644 index 59c10d28a5..0000000000 --- a/eZ/Publish/API/Repository/Events/Language/BeforeDeleteLanguageEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -language = $language; - } - - public function getLanguage(): Language - { - return $this->language; - } -} diff --git a/eZ/Publish/API/Repository/Events/Language/BeforeDisableLanguageEvent.php b/eZ/Publish/API/Repository/Events/Language/BeforeDisableLanguageEvent.php deleted file mode 100644 index b9b401bcea..0000000000 --- a/eZ/Publish/API/Repository/Events/Language/BeforeDisableLanguageEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -language = $language; - } - - public function getLanguage(): Language - { - return $this->language; - } - - public function getDisabledLanguage(): Language - { - if (!$this->hasDisabledLanguage()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasDisabledLanguage() or set it using setDisabledLanguage() before you call the getter.', Language::class)); - } - - return $this->disabledLanguage; - } - - public function setDisabledLanguage(?Language $disabledLanguage): void - { - $this->disabledLanguage = $disabledLanguage; - } - - public function hasDisabledLanguage(): bool - { - return $this->disabledLanguage instanceof Language; - } -} diff --git a/eZ/Publish/API/Repository/Events/Language/BeforeEnableLanguageEvent.php b/eZ/Publish/API/Repository/Events/Language/BeforeEnableLanguageEvent.php deleted file mode 100644 index 8968622b49..0000000000 --- a/eZ/Publish/API/Repository/Events/Language/BeforeEnableLanguageEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -language = $language; - } - - public function getLanguage(): Language - { - return $this->language; - } - - public function getEnabledLanguage(): Language - { - if (!$this->hasEnabledLanguage()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasEnabledLanguage() or set it using setEnabledLanguage() before you call the getter.', Language::class)); - } - - return $this->enabledLanguage; - } - - public function setEnabledLanguage(?Language $enabledLanguage): void - { - $this->enabledLanguage = $enabledLanguage; - } - - public function hasEnabledLanguage(): bool - { - return $this->enabledLanguage instanceof Language; - } -} diff --git a/eZ/Publish/API/Repository/Events/Language/CreateLanguageEvent.php b/eZ/Publish/API/Repository/Events/Language/CreateLanguageEvent.php deleted file mode 100644 index c42c059fbd..0000000000 --- a/eZ/Publish/API/Repository/Events/Language/CreateLanguageEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -language = $language; - $this->languageCreateStruct = $languageCreateStruct; - } - - public function getLanguage(): Language - { - return $this->language; - } - - public function getLanguageCreateStruct(): LanguageCreateStruct - { - return $this->languageCreateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/Language/DeleteLanguageEvent.php b/eZ/Publish/API/Repository/Events/Language/DeleteLanguageEvent.php deleted file mode 100644 index 73288377b1..0000000000 --- a/eZ/Publish/API/Repository/Events/Language/DeleteLanguageEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -language = $language; - } - - public function getLanguage(): Language - { - return $this->language; - } -} diff --git a/eZ/Publish/API/Repository/Events/Language/DisableLanguageEvent.php b/eZ/Publish/API/Repository/Events/Language/DisableLanguageEvent.php deleted file mode 100644 index d39af6e2eb..0000000000 --- a/eZ/Publish/API/Repository/Events/Language/DisableLanguageEvent.php +++ /dev/null @@ -1,39 +0,0 @@ -disabledLanguage = $disabledLanguage; - $this->language = $language; - } - - public function getDisabledLanguage(): Language - { - return $this->disabledLanguage; - } - - public function getLanguage(): Language - { - return $this->language; - } -} diff --git a/eZ/Publish/API/Repository/Events/Language/EnableLanguageEvent.php b/eZ/Publish/API/Repository/Events/Language/EnableLanguageEvent.php deleted file mode 100644 index 32368908ba..0000000000 --- a/eZ/Publish/API/Repository/Events/Language/EnableLanguageEvent.php +++ /dev/null @@ -1,39 +0,0 @@ -enabledLanguage = $enabledLanguage; - $this->language = $language; - } - - public function getEnabledLanguage(): Language - { - return $this->enabledLanguage; - } - - public function getLanguage(): Language - { - return $this->language; - } -} diff --git a/eZ/Publish/API/Repository/Events/Language/UpdateLanguageNameEvent.php b/eZ/Publish/API/Repository/Events/Language/UpdateLanguageNameEvent.php deleted file mode 100644 index cea860bf38..0000000000 --- a/eZ/Publish/API/Repository/Events/Language/UpdateLanguageNameEvent.php +++ /dev/null @@ -1,49 +0,0 @@ -updatedLanguage = $updatedLanguage; - $this->language = $language; - $this->newName = $newName; - } - - public function getUpdatedLanguage(): Language - { - return $this->updatedLanguage; - } - - public function getLanguage(): Language - { - return $this->language; - } - - public function getNewName(): string - { - return $this->newName; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/BeforeCopySubtreeEvent.php b/eZ/Publish/API/Repository/Events/Location/BeforeCopySubtreeEvent.php deleted file mode 100644 index d10fba3166..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/BeforeCopySubtreeEvent.php +++ /dev/null @@ -1,60 +0,0 @@ -subtree = $subtree; - $this->targetParentLocation = $targetParentLocation; - } - - public function getSubtree(): Location - { - return $this->subtree; - } - - public function getTargetParentLocation(): Location - { - return $this->targetParentLocation; - } - - public function getLocation(): Location - { - if (!$this->hasLocation()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasLocation() or set it using setLocation() before you call the getter.', Location::class)); - } - - return $this->location; - } - - public function setLocation(?Location $location): void - { - $this->location = $location; - } - - public function hasLocation(): bool - { - return $this->location instanceof Location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/BeforeCreateLocationEvent.php b/eZ/Publish/API/Repository/Events/Location/BeforeCreateLocationEvent.php deleted file mode 100644 index ca06351f7e..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/BeforeCreateLocationEvent.php +++ /dev/null @@ -1,62 +0,0 @@ -contentInfo = $contentInfo; - $this->locationCreateStruct = $locationCreateStruct; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getLocationCreateStruct(): LocationCreateStruct - { - return $this->locationCreateStruct; - } - - public function getLocation(): Location - { - if (!$this->hasLocation()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasLocation() or set it using setLocation() before you call the getter.', Location::class)); - } - - return $this->location; - } - - public function setLocation(?Location $location): void - { - $this->location = $location; - } - - public function hasLocation(): bool - { - return $this->location instanceof Location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/BeforeDeleteLocationEvent.php b/eZ/Publish/API/Repository/Events/Location/BeforeDeleteLocationEvent.php deleted file mode 100644 index 58ff6adc57..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/BeforeDeleteLocationEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -location = $location; - } - - public function getLocation(): Location - { - return $this->location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/BeforeHideLocationEvent.php b/eZ/Publish/API/Repository/Events/Location/BeforeHideLocationEvent.php deleted file mode 100644 index 013ab4b565..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/BeforeHideLocationEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -location = $location; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getHiddenLocation(): Location - { - if (!$this->hasHiddenLocation()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasHiddenLocation() or set it using setHiddenLocation() before you call the getter.', Location::class)); - } - - return $this->hiddenLocation; - } - - public function setHiddenLocation(?Location $hiddenLocation): void - { - $this->hiddenLocation = $hiddenLocation; - } - - public function hasHiddenLocation(): bool - { - return $this->hiddenLocation instanceof Location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/BeforeMoveSubtreeEvent.php b/eZ/Publish/API/Repository/Events/Location/BeforeMoveSubtreeEvent.php deleted file mode 100644 index c34e7dea1a..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/BeforeMoveSubtreeEvent.php +++ /dev/null @@ -1,37 +0,0 @@ -location = $location; - $this->newParentLocation = $newParentLocation; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getNewParentLocation(): Location - { - return $this->newParentLocation; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/BeforeSwapLocationEvent.php b/eZ/Publish/API/Repository/Events/Location/BeforeSwapLocationEvent.php deleted file mode 100644 index 84ad904fa8..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/BeforeSwapLocationEvent.php +++ /dev/null @@ -1,37 +0,0 @@ -location1 = $location1; - $this->location2 = $location2; - } - - public function getLocation1(): Location - { - return $this->location1; - } - - public function getLocation2(): Location - { - return $this->location2; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/BeforeUnhideLocationEvent.php b/eZ/Publish/API/Repository/Events/Location/BeforeUnhideLocationEvent.php deleted file mode 100644 index 44c34a7b47..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/BeforeUnhideLocationEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -location = $location; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getRevealedLocation(): Location - { - if (!$this->hasRevealedLocation()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasRevealedLocation() or set it using setRevealedLocation() before you call the getter.', Location::class)); - } - - return $this->revealedLocation; - } - - public function setRevealedLocation(?Location $revealedLocation): void - { - $this->revealedLocation = $revealedLocation; - } - - public function hasRevealedLocation(): bool - { - return $this->revealedLocation instanceof Location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/BeforeUpdateLocationEvent.php b/eZ/Publish/API/Repository/Events/Location/BeforeUpdateLocationEvent.php deleted file mode 100644 index 5f2fec20d5..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/BeforeUpdateLocationEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -location = $location; - $this->locationUpdateStruct = $locationUpdateStruct; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getLocationUpdateStruct(): LocationUpdateStruct - { - return $this->locationUpdateStruct; - } - - public function getUpdatedLocation(): Location - { - if (!$this->hasUpdatedLocation()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedLocation() or set it using setUpdatedLocation() before you call the getter.', Location::class)); - } - - return $this->updatedLocation; - } - - public function setUpdatedLocation(?Location $updatedLocation): void - { - $this->updatedLocation = $updatedLocation; - } - - public function hasUpdatedLocation(): bool - { - return $this->updatedLocation instanceof Location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/CopySubtreeEvent.php b/eZ/Publish/API/Repository/Events/Location/CopySubtreeEvent.php deleted file mode 100644 index 5eee86c10a..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/CopySubtreeEvent.php +++ /dev/null @@ -1,49 +0,0 @@ -location = $location; - $this->subtree = $subtree; - $this->targetParentLocation = $targetParentLocation; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getSubtree(): Location - { - return $this->subtree; - } - - public function getTargetParentLocation(): Location - { - return $this->targetParentLocation; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/CreateLocationEvent.php b/eZ/Publish/API/Repository/Events/Location/CreateLocationEvent.php deleted file mode 100644 index 1270d802f5..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/CreateLocationEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -location = $location; - $this->contentInfo = $contentInfo; - $this->locationCreateStruct = $locationCreateStruct; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getLocationCreateStruct(): LocationCreateStruct - { - return $this->locationCreateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/DeleteLocationEvent.php b/eZ/Publish/API/Repository/Events/Location/DeleteLocationEvent.php deleted file mode 100644 index 6e5489808d..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/DeleteLocationEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -location = $location; - } - - public function getLocation(): Location - { - return $this->location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/HideLocationEvent.php b/eZ/Publish/API/Repository/Events/Location/HideLocationEvent.php deleted file mode 100644 index 151cc44987..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/HideLocationEvent.php +++ /dev/null @@ -1,39 +0,0 @@ -location = $location; - $this->hiddenLocation = $hiddenLocation; - } - - public function getHiddenLocation(): Location - { - return $this->hiddenLocation; - } - - public function getLocation(): Location - { - return $this->location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/MoveSubtreeEvent.php b/eZ/Publish/API/Repository/Events/Location/MoveSubtreeEvent.php deleted file mode 100644 index b2c604d9d0..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/MoveSubtreeEvent.php +++ /dev/null @@ -1,39 +0,0 @@ -location = $location; - $this->newParentLocation = $newParentLocation; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getNewParentLocation(): Location - { - return $this->newParentLocation; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/SwapLocationEvent.php b/eZ/Publish/API/Repository/Events/Location/SwapLocationEvent.php deleted file mode 100644 index e9c8ced51a..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/SwapLocationEvent.php +++ /dev/null @@ -1,39 +0,0 @@ -location1 = $location1; - $this->location2 = $location2; - } - - public function getLocation1(): Location - { - return $this->location1; - } - - public function getLocation2(): Location - { - return $this->location2; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/UnhideLocationEvent.php b/eZ/Publish/API/Repository/Events/Location/UnhideLocationEvent.php deleted file mode 100644 index a09b483913..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/UnhideLocationEvent.php +++ /dev/null @@ -1,39 +0,0 @@ -revealedLocation = $revealedLocation; - $this->location = $location; - } - - public function getRevealedLocation(): Location - { - return $this->revealedLocation; - } - - public function getLocation(): Location - { - return $this->location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Location/UpdateLocationEvent.php b/eZ/Publish/API/Repository/Events/Location/UpdateLocationEvent.php deleted file mode 100644 index d529b0941f..0000000000 --- a/eZ/Publish/API/Repository/Events/Location/UpdateLocationEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -updatedLocation = $updatedLocation; - $this->location = $location; - $this->locationUpdateStruct = $locationUpdateStruct; - } - - public function getUpdatedLocation(): Location - { - return $this->updatedLocation; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getLocationUpdateStruct(): LocationUpdateStruct - { - return $this->locationUpdateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/Notification/BeforeCreateNotificationEvent.php b/eZ/Publish/API/Repository/Events/Notification/BeforeCreateNotificationEvent.php deleted file mode 100644 index 1ab9e64d1c..0000000000 --- a/eZ/Publish/API/Repository/Events/Notification/BeforeCreateNotificationEvent.php +++ /dev/null @@ -1,52 +0,0 @@ -createStruct = $createStruct; - } - - public function getCreateStruct(): CreateStruct - { - return $this->createStruct; - } - - public function getNotification(): Notification - { - if (!$this->hasNotification()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasNotification() or set it using setNotification() before you call the getter.', Notification::class)); - } - - return $this->notification; - } - - public function setNotification(?Notification $notification): void - { - $this->notification = $notification; - } - - public function hasNotification(): bool - { - return $this->notification instanceof Notification; - } -} diff --git a/eZ/Publish/API/Repository/Events/Notification/BeforeDeleteNotificationEvent.php b/eZ/Publish/API/Repository/Events/Notification/BeforeDeleteNotificationEvent.php deleted file mode 100644 index 5f1861894a..0000000000 --- a/eZ/Publish/API/Repository/Events/Notification/BeforeDeleteNotificationEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -notification = $notification; - } - - public function getNotification(): Notification - { - return $this->notification; - } -} diff --git a/eZ/Publish/API/Repository/Events/Notification/BeforeMarkNotificationAsReadEvent.php b/eZ/Publish/API/Repository/Events/Notification/BeforeMarkNotificationAsReadEvent.php deleted file mode 100644 index e95136fc76..0000000000 --- a/eZ/Publish/API/Repository/Events/Notification/BeforeMarkNotificationAsReadEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -notification = $notification; - } - - public function getNotification(): Notification - { - return $this->notification; - } -} diff --git a/eZ/Publish/API/Repository/Events/Notification/CreateNotificationEvent.php b/eZ/Publish/API/Repository/Events/Notification/CreateNotificationEvent.php deleted file mode 100644 index c79b1aca64..0000000000 --- a/eZ/Publish/API/Repository/Events/Notification/CreateNotificationEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -notification = $notification; - $this->createStruct = $createStruct; - } - - public function getNotification(): Notification - { - return $this->notification; - } - - public function getCreateStruct(): CreateStruct - { - return $this->createStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/Notification/DeleteNotificationEvent.php b/eZ/Publish/API/Repository/Events/Notification/DeleteNotificationEvent.php deleted file mode 100644 index 66dbb13a04..0000000000 --- a/eZ/Publish/API/Repository/Events/Notification/DeleteNotificationEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -notification = $notification; - } - - public function getNotification(): Notification - { - return $this->notification; - } -} diff --git a/eZ/Publish/API/Repository/Events/Notification/MarkNotificationAsReadEvent.php b/eZ/Publish/API/Repository/Events/Notification/MarkNotificationAsReadEvent.php deleted file mode 100644 index 99b500c941..0000000000 --- a/eZ/Publish/API/Repository/Events/Notification/MarkNotificationAsReadEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -notification = $notification; - } - - public function getNotification(): Notification - { - return $this->notification; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/BeforeCreateObjectStateEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/BeforeCreateObjectStateEvent.php deleted file mode 100644 index 9ca169c514..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/BeforeCreateObjectStateEvent.php +++ /dev/null @@ -1,62 +0,0 @@ -objectStateGroup = $objectStateGroup; - $this->objectStateCreateStruct = $objectStateCreateStruct; - } - - public function getObjectStateGroup(): ObjectStateGroup - { - return $this->objectStateGroup; - } - - public function getObjectStateCreateStruct(): ObjectStateCreateStruct - { - return $this->objectStateCreateStruct; - } - - public function getObjectState(): ObjectState - { - if (!$this->hasObjectState()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasObjectState() or set it using setObjectState() before you call the getter.', ObjectState::class)); - } - - return $this->objectState; - } - - public function setObjectState(?ObjectState $objectState): void - { - $this->objectState = $objectState; - } - - public function hasObjectState(): bool - { - return $this->objectState instanceof ObjectState; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/BeforeCreateObjectStateGroupEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/BeforeCreateObjectStateGroupEvent.php deleted file mode 100644 index 9b54c397e5..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/BeforeCreateObjectStateGroupEvent.php +++ /dev/null @@ -1,52 +0,0 @@ -objectStateGroupCreateStruct = $objectStateGroupCreateStruct; - } - - public function getObjectStateGroupCreateStruct(): ObjectStateGroupCreateStruct - { - return $this->objectStateGroupCreateStruct; - } - - public function getObjectStateGroup(): ObjectStateGroup - { - if (!$this->hasObjectStateGroup()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasObjectStateGroup() or set it using setObjectStateGroup() before you call the getter.', ObjectStateGroup::class)); - } - - return $this->objectStateGroup; - } - - public function setObjectStateGroup(?ObjectStateGroup $objectStateGroup): void - { - $this->objectStateGroup = $objectStateGroup; - } - - public function hasObjectStateGroup(): bool - { - return $this->objectStateGroup instanceof ObjectStateGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/BeforeDeleteObjectStateEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/BeforeDeleteObjectStateEvent.php deleted file mode 100644 index 55692b860f..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/BeforeDeleteObjectStateEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -objectState = $objectState; - } - - public function getObjectState(): ObjectState - { - return $this->objectState; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/BeforeDeleteObjectStateGroupEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/BeforeDeleteObjectStateGroupEvent.php deleted file mode 100644 index 3cda5fc9c0..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/BeforeDeleteObjectStateGroupEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -objectStateGroup = $objectStateGroup; - } - - public function getObjectStateGroup(): ObjectStateGroup - { - return $this->objectStateGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/BeforeSetContentStateEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/BeforeSetContentStateEvent.php deleted file mode 100644 index 7f55d17bd5..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/BeforeSetContentStateEvent.php +++ /dev/null @@ -1,48 +0,0 @@ -contentInfo = $contentInfo; - $this->objectStateGroup = $objectStateGroup; - $this->objectState = $objectState; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getObjectStateGroup(): ObjectStateGroup - { - return $this->objectStateGroup; - } - - public function getObjectState(): ObjectState - { - return $this->objectState; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/BeforeSetPriorityOfObjectStateEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/BeforeSetPriorityOfObjectStateEvent.php deleted file mode 100644 index c6e20d32f7..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/BeforeSetPriorityOfObjectStateEvent.php +++ /dev/null @@ -1,36 +0,0 @@ -objectState = $objectState; - $this->priority = $priority; - } - - public function getObjectState(): ObjectState - { - return $this->objectState; - } - - public function getPriority() - { - return $this->priority; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/BeforeUpdateObjectStateEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/BeforeUpdateObjectStateEvent.php deleted file mode 100644 index d19e70fde5..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/BeforeUpdateObjectStateEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -objectState = $objectState; - $this->objectStateUpdateStruct = $objectStateUpdateStruct; - } - - public function getObjectState(): ObjectState - { - return $this->objectState; - } - - public function getObjectStateUpdateStruct(): ObjectStateUpdateStruct - { - return $this->objectStateUpdateStruct; - } - - public function getUpdatedObjectState(): ObjectState - { - if (!$this->hasUpdatedObjectState()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedObjectState() or set it using setUpdatedObjectState() before you call the getter.', ObjectState::class)); - } - - return $this->updatedObjectState; - } - - public function setUpdatedObjectState(?ObjectState $updatedObjectState): void - { - $this->updatedObjectState = $updatedObjectState; - } - - public function hasUpdatedObjectState(): bool - { - return $this->updatedObjectState instanceof ObjectState; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/BeforeUpdateObjectStateGroupEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/BeforeUpdateObjectStateGroupEvent.php deleted file mode 100644 index 93e241deed..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/BeforeUpdateObjectStateGroupEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -objectStateGroup = $objectStateGroup; - $this->objectStateGroupUpdateStruct = $objectStateGroupUpdateStruct; - } - - public function getObjectStateGroup(): ObjectStateGroup - { - return $this->objectStateGroup; - } - - public function getObjectStateGroupUpdateStruct(): ObjectStateGroupUpdateStruct - { - return $this->objectStateGroupUpdateStruct; - } - - public function getUpdatedObjectStateGroup(): ObjectStateGroup - { - if (!$this->hasUpdatedObjectStateGroup()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedObjectStateGroup() or set it using setUpdatedObjectStateGroup() before you call the getter.', ObjectStateGroup::class)); - } - - return $this->updatedObjectStateGroup; - } - - public function setUpdatedObjectStateGroup(?ObjectStateGroup $updatedObjectStateGroup): void - { - $this->updatedObjectStateGroup = $updatedObjectStateGroup; - } - - public function hasUpdatedObjectStateGroup(): bool - { - return $this->updatedObjectStateGroup instanceof ObjectStateGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/CreateObjectStateEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/CreateObjectStateEvent.php deleted file mode 100644 index 639d37c561..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/CreateObjectStateEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -objectState = $objectState; - $this->objectStateGroup = $objectStateGroup; - $this->objectStateCreateStruct = $objectStateCreateStruct; - } - - public function getObjectState(): ObjectState - { - return $this->objectState; - } - - public function getObjectStateGroup(): ObjectStateGroup - { - return $this->objectStateGroup; - } - - public function getObjectStateCreateStruct(): ObjectStateCreateStruct - { - return $this->objectStateCreateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/CreateObjectStateGroupEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/CreateObjectStateGroupEvent.php deleted file mode 100644 index a9c7aa8df1..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/CreateObjectStateGroupEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -objectStateGroup = $objectStateGroup; - $this->objectStateGroupCreateStruct = $objectStateGroupCreateStruct; - } - - public function getObjectStateGroup(): ObjectStateGroup - { - return $this->objectStateGroup; - } - - public function getObjectStateGroupCreateStruct(): ObjectStateGroupCreateStruct - { - return $this->objectStateGroupCreateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/DeleteObjectStateEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/DeleteObjectStateEvent.php deleted file mode 100644 index 016be96a1d..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/DeleteObjectStateEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -objectState = $objectState; - } - - public function getObjectState(): ObjectState - { - return $this->objectState; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/DeleteObjectStateGroupEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/DeleteObjectStateGroupEvent.php deleted file mode 100644 index 1dea0f06f8..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/DeleteObjectStateGroupEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -objectStateGroup = $objectStateGroup; - } - - public function getObjectStateGroup(): ObjectStateGroup - { - return $this->objectStateGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/SetContentStateEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/SetContentStateEvent.php deleted file mode 100644 index fd34eb4a8f..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/SetContentStateEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -contentInfo = $contentInfo; - $this->objectStateGroup = $objectStateGroup; - $this->objectState = $objectState; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getObjectStateGroup(): ObjectStateGroup - { - return $this->objectStateGroup; - } - - public function getObjectState(): ObjectState - { - return $this->objectState; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/SetPriorityOfObjectStateEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/SetPriorityOfObjectStateEvent.php deleted file mode 100644 index 018c59df7c..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/SetPriorityOfObjectStateEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -objectState = $objectState; - $this->priority = $priority; - } - - public function getObjectState(): ObjectState - { - return $this->objectState; - } - - public function getPriority() - { - return $this->priority; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/UpdateObjectStateEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/UpdateObjectStateEvent.php deleted file mode 100644 index 7ccc192be0..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/UpdateObjectStateEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -updatedObjectState = $updatedObjectState; - $this->objectState = $objectState; - $this->objectStateUpdateStruct = $objectStateUpdateStruct; - } - - public function getUpdatedObjectState(): ObjectState - { - return $this->updatedObjectState; - } - - public function getObjectState(): ObjectState - { - return $this->objectState; - } - - public function getObjectStateUpdateStruct(): ObjectStateUpdateStruct - { - return $this->objectStateUpdateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/ObjectState/UpdateObjectStateGroupEvent.php b/eZ/Publish/API/Repository/Events/ObjectState/UpdateObjectStateGroupEvent.php deleted file mode 100644 index a613ab34c9..0000000000 --- a/eZ/Publish/API/Repository/Events/ObjectState/UpdateObjectStateGroupEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -updatedObjectStateGroup = $updatedObjectStateGroup; - $this->objectStateGroup = $objectStateGroup; - $this->objectStateGroupUpdateStruct = $objectStateGroupUpdateStruct; - } - - public function getUpdatedObjectStateGroup(): ObjectStateGroup - { - return $this->updatedObjectStateGroup; - } - - public function getObjectStateGroup(): ObjectStateGroup - { - return $this->objectStateGroup; - } - - public function getObjectStateGroupUpdateStruct(): ObjectStateGroupUpdateStruct - { - return $this->objectStateGroupUpdateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/AddPolicyByRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/AddPolicyByRoleDraftEvent.php deleted file mode 100644 index e46f3c8222..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/AddPolicyByRoleDraftEvent.php +++ /dev/null @@ -1,49 +0,0 @@ -roleDraft = $roleDraft; - $this->policyCreateStruct = $policyCreateStruct; - $this->updatedRoleDraft = $updatedRoleDraft; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } - - public function getPolicyCreateStruct(): PolicyCreateStruct - { - return $this->policyCreateStruct; - } - - public function getUpdatedRoleDraft(): RoleDraft - { - return $this->updatedRoleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/AssignRoleToUserEvent.php b/eZ/Publish/API/Repository/Events/Role/AssignRoleToUserEvent.php deleted file mode 100644 index 9020ebff45..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/AssignRoleToUserEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -role = $role; - $this->user = $user; - $this->roleLimitation = $roleLimitation; - } - - public function getRole(): Role - { - return $this->role; - } - - public function getUser(): User - { - return $this->user; - } - - public function getRoleLimitation(): ?RoleLimitation - { - return $this->roleLimitation; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/AssignRoleToUserGroupEvent.php b/eZ/Publish/API/Repository/Events/Role/AssignRoleToUserGroupEvent.php deleted file mode 100644 index 0427953297..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/AssignRoleToUserGroupEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -role = $role; - $this->userGroup = $userGroup; - $this->roleLimitation = $roleLimitation; - } - - public function getRole(): Role - { - return $this->role; - } - - public function getUserGroup(): UserGroup - { - return $this->userGroup; - } - - public function getRoleLimitation(): ?RoleLimitation - { - return $this->roleLimitation; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforeAddPolicyByRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforeAddPolicyByRoleDraftEvent.php deleted file mode 100644 index edb9700068..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforeAddPolicyByRoleDraftEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -roleDraft = $roleDraft; - $this->policyCreateStruct = $policyCreateStruct; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } - - public function getPolicyCreateStruct(): PolicyCreateStruct - { - return $this->policyCreateStruct; - } - - public function getUpdatedRoleDraft(): RoleDraft - { - if (!$this->hasUpdatedRoleDraft()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedRoleDraft() or set it using setUpdatedRoleDraft() before you call the getter.', RoleDraft::class)); - } - - return $this->updatedRoleDraft; - } - - public function setUpdatedRoleDraft(?RoleDraft $updatedRoleDraft): void - { - $this->updatedRoleDraft = $updatedRoleDraft; - } - - public function hasUpdatedRoleDraft(): bool - { - return $this->updatedRoleDraft instanceof RoleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforeAssignRoleToUserEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforeAssignRoleToUserEvent.php deleted file mode 100644 index 1e89b2797d..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforeAssignRoleToUserEvent.php +++ /dev/null @@ -1,48 +0,0 @@ -role = $role; - $this->user = $user; - $this->roleLimitation = $roleLimitation; - } - - public function getRole(): Role - { - return $this->role; - } - - public function getUser(): User - { - return $this->user; - } - - public function getRoleLimitation(): ?RoleLimitation - { - return $this->roleLimitation; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforeAssignRoleToUserGroupEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforeAssignRoleToUserGroupEvent.php deleted file mode 100644 index 5c9ec77d98..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforeAssignRoleToUserGroupEvent.php +++ /dev/null @@ -1,48 +0,0 @@ -role = $role; - $this->userGroup = $userGroup; - $this->roleLimitation = $roleLimitation; - } - - public function getRole(): Role - { - return $this->role; - } - - public function getUserGroup(): UserGroup - { - return $this->userGroup; - } - - public function getRoleLimitation(): ?RoleLimitation - { - return $this->roleLimitation; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforeCopyRoleEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforeCopyRoleEvent.php deleted file mode 100644 index 8a51c1336f..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforeCopyRoleEvent.php +++ /dev/null @@ -1,64 +0,0 @@ -role = $role; - $this->roleCopyStruct = $roleCopyStruct; - } - - public function getRole(): Role - { - return $this->role; - } - - public function getRoleCopyStruct(): RoleCopyStruct - { - return $this->roleCopyStruct; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - */ - public function getCopiedRole(): Role - { - if (!$this->hasCopiedRole()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasCopiedRole() or set it using setCopiedRole() before you call the getter.', Role::class)); - } - - return $this->copiedRole; - } - - public function setCopiedRole(?Role $copiedRole): void - { - $this->copiedRole = $copiedRole; - } - - public function hasCopiedRole(): bool - { - return $this->copiedRole instanceof Role; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforeCreateRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforeCreateRoleDraftEvent.php deleted file mode 100644 index 0736fd2790..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforeCreateRoleDraftEvent.php +++ /dev/null @@ -1,52 +0,0 @@ -role = $role; - } - - public function getRole(): Role - { - return $this->role; - } - - public function getRoleDraft(): RoleDraft - { - if (!$this->hasRoleDraft()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasRoleDraft() or set it using setRoleDraft() before you call the getter.', RoleDraft::class)); - } - - return $this->roleDraft; - } - - public function setRoleDraft(?RoleDraft $roleDraft): void - { - $this->roleDraft = $roleDraft; - } - - public function hasRoleDraft(): bool - { - return $this->roleDraft instanceof RoleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforeCreateRoleEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforeCreateRoleEvent.php deleted file mode 100644 index 85ab798a40..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforeCreateRoleEvent.php +++ /dev/null @@ -1,52 +0,0 @@ -roleCreateStruct = $roleCreateStruct; - } - - public function getRoleCreateStruct(): RoleCreateStruct - { - return $this->roleCreateStruct; - } - - public function getRoleDraft(): RoleDraft - { - if (!$this->hasRoleDraft()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasRoleDraft() or set it using setRoleDraft() before you call the getter.', RoleDraft::class)); - } - - return $this->roleDraft; - } - - public function setRoleDraft(?RoleDraft $roleDraft): void - { - $this->roleDraft = $roleDraft; - } - - public function hasRoleDraft(): bool - { - return $this->roleDraft instanceof RoleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforeDeletePolicyEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforeDeletePolicyEvent.php deleted file mode 100644 index 549cce291a..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforeDeletePolicyEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -policy = $policy; - } - - public function getPolicy(): Policy - { - return $this->policy; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforeDeleteRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforeDeleteRoleDraftEvent.php deleted file mode 100644 index 768e14c061..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforeDeleteRoleDraftEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -roleDraft = $roleDraft; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforeDeleteRoleEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforeDeleteRoleEvent.php deleted file mode 100644 index 1b194e495b..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforeDeleteRoleEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -role = $role; - } - - public function getRole(): Role - { - return $this->role; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforePublishRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforePublishRoleDraftEvent.php deleted file mode 100644 index 979aed846f..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforePublishRoleDraftEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -roleDraft = $roleDraft; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforeRemovePolicyByRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforeRemovePolicyByRoleDraftEvent.php deleted file mode 100644 index 111ffff275..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforeRemovePolicyByRoleDraftEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -roleDraft = $roleDraft; - $this->policyDraft = $policyDraft; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } - - public function getPolicyDraft(): PolicyDraft - { - return $this->policyDraft; - } - - public function getUpdatedRoleDraft(): RoleDraft - { - if (!$this->hasUpdatedRoleDraft()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedRoleDraft() or set it using setUpdatedRoleDraft() before you call the getter.', RoleDraft::class)); - } - - return $this->updatedRoleDraft; - } - - public function setUpdatedRoleDraft(?RoleDraft $updatedRoleDraft): void - { - $this->updatedRoleDraft = $updatedRoleDraft; - } - - public function hasUpdatedRoleDraft(): bool - { - return $this->updatedRoleDraft instanceof RoleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforeRemoveRoleAssignmentEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforeRemoveRoleAssignmentEvent.php deleted file mode 100644 index e225f155e3..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforeRemoveRoleAssignmentEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -roleAssignment = $roleAssignment; - } - - public function getRoleAssignment(): RoleAssignment - { - return $this->roleAssignment; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforeUpdatePolicyByRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforeUpdatePolicyByRoleDraftEvent.php deleted file mode 100644 index 2385ff142b..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforeUpdatePolicyByRoleDraftEvent.php +++ /dev/null @@ -1,71 +0,0 @@ -roleDraft = $roleDraft; - $this->policy = $policy; - $this->policyUpdateStruct = $policyUpdateStruct; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } - - public function getPolicy(): PolicyDraft - { - return $this->policy; - } - - public function getPolicyUpdateStruct(): PolicyUpdateStruct - { - return $this->policyUpdateStruct; - } - - public function getUpdatedPolicyDraft(): PolicyDraft - { - if (!$this->hasUpdatedPolicyDraft()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedPolicyDraft() or set it using setUpdatedPolicyDraft() before you call the getter.', PolicyDraft::class)); - } - - return $this->updatedPolicyDraft; - } - - public function setUpdatedPolicyDraft(?PolicyDraft $updatedPolicyDraft): void - { - $this->updatedPolicyDraft = $updatedPolicyDraft; - } - - public function hasUpdatedPolicyDraft(): bool - { - return $this->updatedPolicyDraft instanceof PolicyDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/BeforeUpdateRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/BeforeUpdateRoleDraftEvent.php deleted file mode 100644 index 6c63e166f3..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/BeforeUpdateRoleDraftEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -roleDraft = $roleDraft; - $this->roleUpdateStruct = $roleUpdateStruct; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } - - public function getRoleUpdateStruct(): RoleUpdateStruct - { - return $this->roleUpdateStruct; - } - - public function getUpdatedRoleDraft(): RoleDraft - { - if (!$this->hasUpdatedRoleDraft()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedRoleDraft() or set it using setUpdatedRoleDraft() before you call the getter.', RoleDraft::class)); - } - - return $this->updatedRoleDraft; - } - - public function setUpdatedRoleDraft(?RoleDraft $updatedRoleDraft): void - { - $this->updatedRoleDraft = $updatedRoleDraft; - } - - public function hasUpdatedRoleDraft(): bool - { - return $this->updatedRoleDraft instanceof RoleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/CopyRoleEvent.php b/eZ/Publish/API/Repository/Events/Role/CopyRoleEvent.php deleted file mode 100644 index a29f58d921..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/CopyRoleEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -copiedRole = $copiedRole; - $this->role = $role; - $this->roleCopyStruct = $roleCopyStruct; - } - - public function getRole(): Role - { - return $this->role; - } - - public function getCopiedRole(): Role - { - return $this->copiedRole; - } - - public function getRoleCopyStruct(): RoleCopyStruct - { - return $this->roleCopyStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/CreateRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/CreateRoleDraftEvent.php deleted file mode 100644 index 7913961e3e..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/CreateRoleDraftEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -role = $role; - $this->roleDraft = $roleDraft; - } - - public function getRole(): Role - { - return $this->role; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/CreateRoleEvent.php b/eZ/Publish/API/Repository/Events/Role/CreateRoleEvent.php deleted file mode 100644 index 57916d1572..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/CreateRoleEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -roleCreateStruct = $roleCreateStruct; - $this->roleDraft = $roleDraft; - } - - public function getRoleCreateStruct(): RoleCreateStruct - { - return $this->roleCreateStruct; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/DeletePolicyEvent.php b/eZ/Publish/API/Repository/Events/Role/DeletePolicyEvent.php deleted file mode 100644 index 77e92831f6..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/DeletePolicyEvent.php +++ /dev/null @@ -1,29 +0,0 @@ -policy = $policy; - } - - public function getPolicy(): Policy - { - return $this->policy; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/DeleteRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/DeleteRoleDraftEvent.php deleted file mode 100644 index edc0495254..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/DeleteRoleDraftEvent.php +++ /dev/null @@ -1,29 +0,0 @@ -roleDraft = $roleDraft; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/DeleteRoleEvent.php b/eZ/Publish/API/Repository/Events/Role/DeleteRoleEvent.php deleted file mode 100644 index 198ad06bed..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/DeleteRoleEvent.php +++ /dev/null @@ -1,29 +0,0 @@ -role = $role; - } - - public function getRole(): Role - { - return $this->role; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/PublishRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/PublishRoleDraftEvent.php deleted file mode 100644 index 1eb6664db0..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/PublishRoleDraftEvent.php +++ /dev/null @@ -1,29 +0,0 @@ -roleDraft = $roleDraft; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/RemovePolicyByRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/RemovePolicyByRoleDraftEvent.php deleted file mode 100644 index 5ff133f7a8..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/RemovePolicyByRoleDraftEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -roleDraft = $roleDraft; - $this->policyDraft = $policyDraft; - $this->updatedRoleDraft = $updatedRoleDraft; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } - - public function getPolicyDraft(): PolicyDraft - { - return $this->policyDraft; - } - - public function getUpdatedRoleDraft(): RoleDraft - { - return $this->updatedRoleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/RemoveRoleAssignmentEvent.php b/eZ/Publish/API/Repository/Events/Role/RemoveRoleAssignmentEvent.php deleted file mode 100644 index 9cbccb14b1..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/RemoveRoleAssignmentEvent.php +++ /dev/null @@ -1,29 +0,0 @@ -roleAssignment = $roleAssignment; - } - - public function getRoleAssignment(): RoleAssignment - { - return $this->roleAssignment; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/UpdatePolicyByRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/UpdatePolicyByRoleDraftEvent.php deleted file mode 100644 index fbef6a0fcd..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/UpdatePolicyByRoleDraftEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -roleDraft = $roleDraft; - $this->policy = $policy; - $this->policyUpdateStruct = $policyUpdateStruct; - $this->updatedPolicyDraft = $updatedPolicyDraft; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } - - public function getPolicy(): PolicyDraft - { - return $this->policy; - } - - public function getPolicyUpdateStruct(): PolicyUpdateStruct - { - return $this->policyUpdateStruct; - } - - public function getUpdatedPolicyDraft(): PolicyDraft - { - return $this->updatedPolicyDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Role/UpdateRoleDraftEvent.php b/eZ/Publish/API/Repository/Events/Role/UpdateRoleDraftEvent.php deleted file mode 100644 index 9924608455..0000000000 --- a/eZ/Publish/API/Repository/Events/Role/UpdateRoleDraftEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -roleDraft = $roleDraft; - $this->roleUpdateStruct = $roleUpdateStruct; - $this->updatedRoleDraft = $updatedRoleDraft; - } - - public function getRoleDraft(): RoleDraft - { - return $this->roleDraft; - } - - public function getRoleUpdateStruct(): RoleUpdateStruct - { - return $this->roleUpdateStruct; - } - - public function getUpdatedRoleDraft(): RoleDraft - { - return $this->updatedRoleDraft; - } -} diff --git a/eZ/Publish/API/Repository/Events/Section/AssignSectionEvent.php b/eZ/Publish/API/Repository/Events/Section/AssignSectionEvent.php deleted file mode 100644 index a1fafe79f1..0000000000 --- a/eZ/Publish/API/Repository/Events/Section/AssignSectionEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -contentInfo = $contentInfo; - $this->section = $section; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getSection(): Section - { - return $this->section; - } -} diff --git a/eZ/Publish/API/Repository/Events/Section/AssignSectionToSubtreeEvent.php b/eZ/Publish/API/Repository/Events/Section/AssignSectionToSubtreeEvent.php deleted file mode 100644 index 108c6ebb45..0000000000 --- a/eZ/Publish/API/Repository/Events/Section/AssignSectionToSubtreeEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -location = $location; - $this->section = $section; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getSection(): Section - { - return $this->section; - } -} diff --git a/eZ/Publish/API/Repository/Events/Section/BeforeAssignSectionEvent.php b/eZ/Publish/API/Repository/Events/Section/BeforeAssignSectionEvent.php deleted file mode 100644 index 2c431cb17a..0000000000 --- a/eZ/Publish/API/Repository/Events/Section/BeforeAssignSectionEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -contentInfo = $contentInfo; - $this->section = $section; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getSection(): Section - { - return $this->section; - } -} diff --git a/eZ/Publish/API/Repository/Events/Section/BeforeAssignSectionToSubtreeEvent.php b/eZ/Publish/API/Repository/Events/Section/BeforeAssignSectionToSubtreeEvent.php deleted file mode 100644 index ef4b7f634d..0000000000 --- a/eZ/Publish/API/Repository/Events/Section/BeforeAssignSectionToSubtreeEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -location = $location; - $this->section = $section; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getSection(): Section - { - return $this->section; - } -} diff --git a/eZ/Publish/API/Repository/Events/Section/BeforeCreateSectionEvent.php b/eZ/Publish/API/Repository/Events/Section/BeforeCreateSectionEvent.php deleted file mode 100644 index fa2a56e04c..0000000000 --- a/eZ/Publish/API/Repository/Events/Section/BeforeCreateSectionEvent.php +++ /dev/null @@ -1,52 +0,0 @@ -sectionCreateStruct = $sectionCreateStruct; - } - - public function getSectionCreateStruct(): SectionCreateStruct - { - return $this->sectionCreateStruct; - } - - public function getSection(): Section - { - if (!$this->hasSection()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasSection() or set it using setSection() before you call the getter.', Section::class)); - } - - return $this->section; - } - - public function setSection(?Section $section): void - { - $this->section = $section; - } - - public function hasSection(): bool - { - return $this->section instanceof Section; - } -} diff --git a/eZ/Publish/API/Repository/Events/Section/BeforeDeleteSectionEvent.php b/eZ/Publish/API/Repository/Events/Section/BeforeDeleteSectionEvent.php deleted file mode 100644 index 3e9319e876..0000000000 --- a/eZ/Publish/API/Repository/Events/Section/BeforeDeleteSectionEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -section = $section; - } - - public function getSection(): Section - { - return $this->section; - } -} diff --git a/eZ/Publish/API/Repository/Events/Section/BeforeUpdateSectionEvent.php b/eZ/Publish/API/Repository/Events/Section/BeforeUpdateSectionEvent.php deleted file mode 100644 index 27d0ee245e..0000000000 --- a/eZ/Publish/API/Repository/Events/Section/BeforeUpdateSectionEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -section = $section; - $this->sectionUpdateStruct = $sectionUpdateStruct; - } - - public function getSection(): Section - { - return $this->section; - } - - public function getSectionUpdateStruct(): SectionUpdateStruct - { - return $this->sectionUpdateStruct; - } - - public function getUpdatedSection(): Section - { - if (!$this->hasUpdatedSection()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedSection() or set it using setUpdatedSection() before you call the getter.', Section::class)); - } - - return $this->updatedSection; - } - - public function setUpdatedSection(?Section $updatedSection): void - { - $this->updatedSection = $updatedSection; - } - - public function hasUpdatedSection(): bool - { - return $this->updatedSection instanceof Section; - } -} diff --git a/eZ/Publish/API/Repository/Events/Section/CreateSectionEvent.php b/eZ/Publish/API/Repository/Events/Section/CreateSectionEvent.php deleted file mode 100644 index d8724788b7..0000000000 --- a/eZ/Publish/API/Repository/Events/Section/CreateSectionEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -sectionCreateStruct = $sectionCreateStruct; - $this->section = $section; - } - - public function getSectionCreateStruct(): SectionCreateStruct - { - return $this->sectionCreateStruct; - } - - public function getSection(): Section - { - return $this->section; - } -} diff --git a/eZ/Publish/API/Repository/Events/Section/DeleteSectionEvent.php b/eZ/Publish/API/Repository/Events/Section/DeleteSectionEvent.php deleted file mode 100644 index 60b3ca82a4..0000000000 --- a/eZ/Publish/API/Repository/Events/Section/DeleteSectionEvent.php +++ /dev/null @@ -1,29 +0,0 @@ -section = $section; - } - - public function getSection(): Section - { - return $this->section; - } -} diff --git a/eZ/Publish/API/Repository/Events/Section/UpdateSectionEvent.php b/eZ/Publish/API/Repository/Events/Section/UpdateSectionEvent.php deleted file mode 100644 index ef52dbf8a8..0000000000 --- a/eZ/Publish/API/Repository/Events/Section/UpdateSectionEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -section = $section; - $this->sectionUpdateStruct = $sectionUpdateStruct; - $this->updatedSection = $updatedSection; - } - - public function getSection(): Section - { - return $this->section; - } - - public function getSectionUpdateStruct(): SectionUpdateStruct - { - return $this->sectionUpdateStruct; - } - - public function getUpdatedSection(): Section - { - return $this->updatedSection; - } -} diff --git a/eZ/Publish/API/Repository/Events/Setting/BeforeCreateSettingEvent.php b/eZ/Publish/API/Repository/Events/Setting/BeforeCreateSettingEvent.php deleted file mode 100644 index edd58dc5a0..0000000000 --- a/eZ/Publish/API/Repository/Events/Setting/BeforeCreateSettingEvent.php +++ /dev/null @@ -1,52 +0,0 @@ -settingCreateStruct = $settingCreateStruct; - } - - public function getSettingCreateStruct(): SettingCreateStruct - { - return $this->settingCreateStruct; - } - - public function getSetting(): Setting - { - if (!$this->hasSetting()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasSetting() or set it using setSetting() before you call the getter.', Setting::class)); - } - - return $this->setting; - } - - public function setSetting(?Setting $setting): void - { - $this->setting = $setting; - } - - public function hasSetting(): bool - { - return $this->setting instanceof Setting; - } -} diff --git a/eZ/Publish/API/Repository/Events/Setting/BeforeDeleteSettingEvent.php b/eZ/Publish/API/Repository/Events/Setting/BeforeDeleteSettingEvent.php deleted file mode 100644 index 50a2881cd2..0000000000 --- a/eZ/Publish/API/Repository/Events/Setting/BeforeDeleteSettingEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -setting = $setting; - } - - public function getSetting(): Setting - { - return $this->setting; - } -} diff --git a/eZ/Publish/API/Repository/Events/Setting/BeforeUpdateSettingEvent.php b/eZ/Publish/API/Repository/Events/Setting/BeforeUpdateSettingEvent.php deleted file mode 100644 index 7a239c0c48..0000000000 --- a/eZ/Publish/API/Repository/Events/Setting/BeforeUpdateSettingEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -setting = $setting; - $this->settingUpdateStruct = $settingUpdateStruct; - } - - public function getSetting(): Setting - { - return $this->setting; - } - - public function getSettingUpdateStruct(): SettingUpdateStruct - { - return $this->settingUpdateStruct; - } - - public function getUpdatedSetting(): Setting - { - if (!$this->hasUpdatedSetting()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedSetting() or set it using setUpdatedSetting() before you call the getter.', Setting::class)); - } - - return $this->updatedSetting; - } - - public function setUpdatedSetting(?Setting $updatedSetting): void - { - $this->updatedSetting = $updatedSetting; - } - - public function hasUpdatedSetting(): bool - { - return $this->updatedSetting instanceof Setting; - } -} diff --git a/eZ/Publish/API/Repository/Events/Setting/CreateSettingEvent.php b/eZ/Publish/API/Repository/Events/Setting/CreateSettingEvent.php deleted file mode 100644 index 627d2a1d97..0000000000 --- a/eZ/Publish/API/Repository/Events/Setting/CreateSettingEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -setting = $setting; - $this->settingCreateStruct = $settingCreateStruct; - } - - public function getSetting(): Setting - { - return $this->setting; - } - - public function getSettingCreateStruct(): SettingCreateStruct - { - return $this->settingCreateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/Setting/DeleteSettingEvent.php b/eZ/Publish/API/Repository/Events/Setting/DeleteSettingEvent.php deleted file mode 100644 index 6f7bde76be..0000000000 --- a/eZ/Publish/API/Repository/Events/Setting/DeleteSettingEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -setting = $setting; - } - - public function getSetting(): Setting - { - return $this->setting; - } -} diff --git a/eZ/Publish/API/Repository/Events/Setting/UpdateSettingEvent.php b/eZ/Publish/API/Repository/Events/Setting/UpdateSettingEvent.php deleted file mode 100644 index c44b897101..0000000000 --- a/eZ/Publish/API/Repository/Events/Setting/UpdateSettingEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -updatedSetting = $updatedSetting; - $this->setting = $setting; - $this->settingUpdateStruct = $settingUpdateStruct; - } - - public function getUpdatedSetting(): Setting - { - return $this->updatedSetting; - } - - public function getSetting(): Setting - { - return $this->setting; - } - - public function getSettingUpdateStruct(): SettingUpdateStruct - { - return $this->settingUpdateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/Trash/BeforeDeleteTrashItemEvent.php b/eZ/Publish/API/Repository/Events/Trash/BeforeDeleteTrashItemEvent.php deleted file mode 100644 index 0ca2e8d680..0000000000 --- a/eZ/Publish/API/Repository/Events/Trash/BeforeDeleteTrashItemEvent.php +++ /dev/null @@ -1,52 +0,0 @@ -trashItem = $trashItem; - } - - public function getTrashItem(): TrashItem - { - return $this->trashItem; - } - - public function getResult(): TrashItemDeleteResult - { - if (!$this->hasResult()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasResult() or set it using setResult() before you call the getter.', TrashItemDeleteResult::class)); - } - - return $this->result; - } - - public function setResult(?TrashItemDeleteResult $result): void - { - $this->result = $result; - } - - public function hasResult(): bool - { - return $this->result instanceof TrashItemDeleteResult; - } -} diff --git a/eZ/Publish/API/Repository/Events/Trash/BeforeEmptyTrashEvent.php b/eZ/Publish/API/Repository/Events/Trash/BeforeEmptyTrashEvent.php deleted file mode 100644 index a1dcf058e8..0000000000 --- a/eZ/Publish/API/Repository/Events/Trash/BeforeEmptyTrashEvent.php +++ /dev/null @@ -1,42 +0,0 @@ -hasResultList()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasResultList() or set it using setResultList() before you call the getter.', TrashItemDeleteResultList::class)); - } - - return $this->resultList; - } - - public function setResultList(?TrashItemDeleteResultList $resultList): void - { - $this->resultList = $resultList; - } - - public function hasResultList(): bool - { - return $this->resultList instanceof TrashItemDeleteResultList; - } -} diff --git a/eZ/Publish/API/Repository/Events/Trash/BeforeRecoverEvent.php b/eZ/Publish/API/Repository/Events/Trash/BeforeRecoverEvent.php deleted file mode 100644 index 9a5dac30a7..0000000000 --- a/eZ/Publish/API/Repository/Events/Trash/BeforeRecoverEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -trashItem = $trashItem; - $this->newParentLocation = $newParentLocation; - } - - public function getTrashItem(): TrashItem - { - return $this->trashItem; - } - - public function getNewParentLocation(): ?Location - { - return $this->newParentLocation; - } - - public function getLocation(): Location - { - if (!$this->hasLocation()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasLocation() or set it using setLocation() before you call the getter.', Location::class)); - } - - return $this->location; - } - - public function setLocation(?Location $location): void - { - $this->location = $location; - } - - public function hasLocation(): bool - { - return $this->location instanceof Location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Trash/BeforeTrashEvent.php b/eZ/Publish/API/Repository/Events/Trash/BeforeTrashEvent.php deleted file mode 100644 index a0bc8075e2..0000000000 --- a/eZ/Publish/API/Repository/Events/Trash/BeforeTrashEvent.php +++ /dev/null @@ -1,67 +0,0 @@ -location = $location; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getResult(): TrashItem - { - if (!$this->isResultSet()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s (or null). Check isResultSet() or set it using setResult() before you call the getter.', TrashItem::class)); - } - - return $this->result; - } - - public function setResult(?TrashItem $result): void - { - $this->result = $result; - $this->resultSet = true; - } - - public function hasTrashItem(): bool - { - return $this->result instanceof TrashItem; - } - - public function resetResult(): void - { - $this->result = null; - $this->resultSet = false; - } - - public function isResultSet(): bool - { - return $this->resultSet; - } -} diff --git a/eZ/Publish/API/Repository/Events/Trash/DeleteTrashItemEvent.php b/eZ/Publish/API/Repository/Events/Trash/DeleteTrashItemEvent.php deleted file mode 100644 index b40b6aef9a..0000000000 --- a/eZ/Publish/API/Repository/Events/Trash/DeleteTrashItemEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -trashItem = $trashItem; - $this->result = $result; - } - - public function getTrashItem(): TrashItem - { - return $this->trashItem; - } - - public function getResult(): TrashItemDeleteResult - { - return $this->result; - } -} diff --git a/eZ/Publish/API/Repository/Events/Trash/EmptyTrashEvent.php b/eZ/Publish/API/Repository/Events/Trash/EmptyTrashEvent.php deleted file mode 100644 index 02f09a5b11..0000000000 --- a/eZ/Publish/API/Repository/Events/Trash/EmptyTrashEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -resultList = $resultList; - } - - public function getResultList(): TrashItemDeleteResultList - { - return $this->resultList; - } -} diff --git a/eZ/Publish/API/Repository/Events/Trash/RecoverEvent.php b/eZ/Publish/API/Repository/Events/Trash/RecoverEvent.php deleted file mode 100644 index 23f9c208ac..0000000000 --- a/eZ/Publish/API/Repository/Events/Trash/RecoverEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -trashItem = $trashItem; - $this->newParentLocation = $newParentLocation; - $this->location = $location; - } - - public function getTrashItem(): TrashItem - { - return $this->trashItem; - } - - public function getNewParentLocation(): ?Location - { - return $this->newParentLocation; - } - - public function getLocation(): Location - { - return $this->location; - } -} diff --git a/eZ/Publish/API/Repository/Events/Trash/TrashEvent.php b/eZ/Publish/API/Repository/Events/Trash/TrashEvent.php deleted file mode 100644 index 97cd9601a7..0000000000 --- a/eZ/Publish/API/Repository/Events/Trash/TrashEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -location = $location; - $this->trashItem = $trashItem; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getTrashItem(): ?TrashItem - { - return $this->trashItem; - } -} diff --git a/eZ/Publish/API/Repository/Events/URL/BeforeUpdateUrlEvent.php b/eZ/Publish/API/Repository/Events/URL/BeforeUpdateUrlEvent.php deleted file mode 100644 index 43a85b43a5..0000000000 --- a/eZ/Publish/API/Repository/Events/URL/BeforeUpdateUrlEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -url = $url; - $this->struct = $struct; - } - - public function getUrl(): URL - { - return $this->url; - } - - public function getStruct(): URLUpdateStruct - { - return $this->struct; - } - - public function getUpdatedUrl(): URL - { - if (!$this->hasUpdatedUrl()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedUrl() or set it using setUpdatedUrl() before you call the getter.', URL::class)); - } - - return $this->updatedUrl; - } - - public function setUpdatedUrl(?URL $updatedUrl): void - { - $this->updatedUrl = $updatedUrl; - } - - public function hasUpdatedUrl(): bool - { - return $this->updatedUrl instanceof URL; - } -} diff --git a/eZ/Publish/API/Repository/Events/URL/UpdateUrlEvent.php b/eZ/Publish/API/Repository/Events/URL/UpdateUrlEvent.php deleted file mode 100644 index d46770cccb..0000000000 --- a/eZ/Publish/API/Repository/Events/URL/UpdateUrlEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -url = $url; - $this->struct = $struct; - $this->updatedUrl = $updatedUrl; - } - - public function getUrl(): URL - { - return $this->url; - } - - public function getStruct(): URLUpdateStruct - { - return $this->struct; - } - - public function getUpdatedUrl(): URL - { - return $this->updatedUrl; - } -} diff --git a/eZ/Publish/API/Repository/Events/URLAlias/BeforeRefreshSystemUrlAliasesForLocationEvent.php b/eZ/Publish/API/Repository/Events/URLAlias/BeforeRefreshSystemUrlAliasesForLocationEvent.php deleted file mode 100644 index cc6cfad468..0000000000 --- a/eZ/Publish/API/Repository/Events/URLAlias/BeforeRefreshSystemUrlAliasesForLocationEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -location = $location; - } - - public function getLocation(): Location - { - return $this->location; - } -} diff --git a/eZ/Publish/API/Repository/Events/URLAlias/BeforeRemoveAliasesEvent.php b/eZ/Publish/API/Repository/Events/URLAlias/BeforeRemoveAliasesEvent.php deleted file mode 100644 index e975c2bcba..0000000000 --- a/eZ/Publish/API/Repository/Events/URLAlias/BeforeRemoveAliasesEvent.php +++ /dev/null @@ -1,27 +0,0 @@ -aliasList = $aliasList; - } - - public function getAliasList(): array - { - return $this->aliasList; - } -} diff --git a/eZ/Publish/API/Repository/Events/URLAlias/CreateUrlAliasEvent.php b/eZ/Publish/API/Repository/Events/URLAlias/CreateUrlAliasEvent.php deleted file mode 100644 index 77febb0205..0000000000 --- a/eZ/Publish/API/Repository/Events/URLAlias/CreateUrlAliasEvent.php +++ /dev/null @@ -1,76 +0,0 @@ -location = $location; - $this->path = $path; - $this->languageCode = $languageCode; - $this->forwarding = $forwarding; - $this->alwaysAvailable = $alwaysAvailable; - $this->urlAlias = $urlAlias; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getPath() - { - return $this->path; - } - - public function getLanguageCode() - { - return $this->languageCode; - } - - public function getForwarding() - { - return $this->forwarding; - } - - public function getAlwaysAvailable() - { - return $this->alwaysAvailable; - } - - public function getUrlAlias(): URLAlias - { - return $this->urlAlias; - } -} diff --git a/eZ/Publish/API/Repository/Events/URLAlias/RefreshSystemUrlAliasesForLocationEvent.php b/eZ/Publish/API/Repository/Events/URLAlias/RefreshSystemUrlAliasesForLocationEvent.php deleted file mode 100644 index 4b5da9ee09..0000000000 --- a/eZ/Publish/API/Repository/Events/URLAlias/RefreshSystemUrlAliasesForLocationEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -location = $location; - } - - public function getLocation(): Location - { - return $this->location; - } -} diff --git a/eZ/Publish/API/Repository/Events/URLAlias/RemoveAliasesEvent.php b/eZ/Publish/API/Repository/Events/URLAlias/RemoveAliasesEvent.php deleted file mode 100644 index a8a8331f26..0000000000 --- a/eZ/Publish/API/Repository/Events/URLAlias/RemoveAliasesEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -aliasList = $aliasList; - } - - public function getAliasList(): array - { - return $this->aliasList; - } -} diff --git a/eZ/Publish/API/Repository/Events/URLWildcard/BeforeRemoveEvent.php b/eZ/Publish/API/Repository/Events/URLWildcard/BeforeRemoveEvent.php deleted file mode 100644 index 070c412043..0000000000 --- a/eZ/Publish/API/Repository/Events/URLWildcard/BeforeRemoveEvent.php +++ /dev/null @@ -1,28 +0,0 @@ -urlWildcard = $urlWildcard; - } - - public function getUrlWildcard(): URLWildcard - { - return $this->urlWildcard; - } -} diff --git a/eZ/Publish/API/Repository/Events/URLWildcard/BeforeTranslateEvent.php b/eZ/Publish/API/Repository/Events/URLWildcard/BeforeTranslateEvent.php deleted file mode 100644 index 965af28d97..0000000000 --- a/eZ/Publish/API/Repository/Events/URLWildcard/BeforeTranslateEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -url = $url; - } - - public function getUrl() - { - return $this->url; - } - - public function getResult(): URLWildcardTranslationResult - { - if (!$this->hasResult()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasResult() or set it using setResult() before you call the getter.', URLWildcardTranslationResult::class)); - } - - return $this->result; - } - - public function setResult(?URLWildcardTranslationResult $result): void - { - $this->result = $result; - } - - public function hasResult(): bool - { - return $this->result instanceof URLWildcardTranslationResult; - } -} diff --git a/eZ/Publish/API/Repository/Events/URLWildcard/BeforeUpdateEvent.php b/eZ/Publish/API/Repository/Events/URLWildcard/BeforeUpdateEvent.php deleted file mode 100644 index 0668160c05..0000000000 --- a/eZ/Publish/API/Repository/Events/URLWildcard/BeforeUpdateEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -urlWildcard = $urlWildcard; - $this->updateStruct = $updateStruct; - } - - public function getUrlWildcard(): URLWildcard - { - return $this->urlWildcard; - } - - public function getUpdateStruct(): URLWildcardUpdateStruct - { - return $this->updateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/URLWildcard/CreateEvent.php b/eZ/Publish/API/Repository/Events/URLWildcard/CreateEvent.php deleted file mode 100644 index 85b1223ee3..0000000000 --- a/eZ/Publish/API/Repository/Events/URLWildcard/CreateEvent.php +++ /dev/null @@ -1,56 +0,0 @@ -sourceUrl = $sourceUrl; - $this->destinationUrl = $destinationUrl; - $this->forward = $forward; - $this->urlWildcard = $urlWildcard; - } - - public function getSourceUrl() - { - return $this->sourceUrl; - } - - public function getDestinationUrl() - { - return $this->destinationUrl; - } - - public function getForward() - { - return $this->forward; - } - - public function getUrlWildcard(): URLWildcard - { - return $this->urlWildcard; - } -} diff --git a/eZ/Publish/API/Repository/Events/URLWildcard/RemoveEvent.php b/eZ/Publish/API/Repository/Events/URLWildcard/RemoveEvent.php deleted file mode 100644 index e5bb5f6e48..0000000000 --- a/eZ/Publish/API/Repository/Events/URLWildcard/RemoveEvent.php +++ /dev/null @@ -1,29 +0,0 @@ -urlWildcard = $urlWildcard; - } - - public function getUrlWildcard(): URLWildcard - { - return $this->urlWildcard; - } -} diff --git a/eZ/Publish/API/Repository/Events/URLWildcard/TranslateEvent.php b/eZ/Publish/API/Repository/Events/URLWildcard/TranslateEvent.php deleted file mode 100644 index 1b4e4e3cf3..0000000000 --- a/eZ/Publish/API/Repository/Events/URLWildcard/TranslateEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -url = $url; - $this->result = $result; - } - - public function getUrl() - { - return $this->url; - } - - public function getResult(): URLWildcardTranslationResult - { - return $this->result; - } -} diff --git a/eZ/Publish/API/Repository/Events/URLWildcard/UpdateEvent.php b/eZ/Publish/API/Repository/Events/URLWildcard/UpdateEvent.php deleted file mode 100644 index a1ebd495df..0000000000 --- a/eZ/Publish/API/Repository/Events/URLWildcard/UpdateEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -urlWildcard = $urlWildcard; - $this->updateStruct = $updateStruct; - } - - public function getUrlWildcard(): URLWildcard - { - return $this->urlWildcard; - } - - public function getUpdateStruct(): URLWildcardUpdateStruct - { - return $this->updateStruct; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/AssignUserToUserGroupEvent.php b/eZ/Publish/API/Repository/Events/User/AssignUserToUserGroupEvent.php deleted file mode 100644 index 4d91ad6476..0000000000 --- a/eZ/Publish/API/Repository/Events/User/AssignUserToUserGroupEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -user = $user; - $this->userGroup = $userGroup; - } - - public function getUser(): User - { - return $this->user; - } - - public function getUserGroup(): UserGroup - { - return $this->userGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/BeforeAssignUserToUserGroupEvent.php b/eZ/Publish/API/Repository/Events/User/BeforeAssignUserToUserGroupEvent.php deleted file mode 100644 index 23848d8cf3..0000000000 --- a/eZ/Publish/API/Repository/Events/User/BeforeAssignUserToUserGroupEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -user = $user; - $this->userGroup = $userGroup; - } - - public function getUser(): User - { - return $this->user; - } - - public function getUserGroup(): UserGroup - { - return $this->userGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/BeforeCreateUserEvent.php b/eZ/Publish/API/Repository/Events/User/BeforeCreateUserEvent.php deleted file mode 100644 index 32f2c1a886..0000000000 --- a/eZ/Publish/API/Repository/Events/User/BeforeCreateUserEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -userCreateStruct = $userCreateStruct; - $this->parentGroups = $parentGroups; - } - - public function getUserCreateStruct(): UserCreateStruct - { - return $this->userCreateStruct; - } - - public function getParentGroups(): array - { - return $this->parentGroups; - } - - public function getUser(): User - { - if (!$this->hasUser()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUser() or set it using setUser() before you call the getter.', User::class)); - } - - return $this->user; - } - - public function setUser(?User $user): void - { - $this->user = $user; - } - - public function hasUser(): bool - { - return $this->user instanceof User; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/BeforeCreateUserGroupEvent.php b/eZ/Publish/API/Repository/Events/User/BeforeCreateUserGroupEvent.php deleted file mode 100644 index c11dfc8837..0000000000 --- a/eZ/Publish/API/Repository/Events/User/BeforeCreateUserGroupEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -userGroupCreateStruct = $userGroupCreateStruct; - $this->parentGroup = $parentGroup; - } - - public function getUserGroupCreateStruct(): UserGroupCreateStruct - { - return $this->userGroupCreateStruct; - } - - public function getParentGroup(): UserGroup - { - return $this->parentGroup; - } - - public function getUserGroup(): UserGroup - { - if (!$this->hasUserGroup()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUserGroup() or set it using setUserGroup() before you call the getter.', UserGroup::class)); - } - - return $this->userGroup; - } - - public function setUserGroup(?UserGroup $userGroup): void - { - $this->userGroup = $userGroup; - } - - public function hasUserGroup(): bool - { - return $this->userGroup instanceof UserGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/BeforeMoveUserGroupEvent.php b/eZ/Publish/API/Repository/Events/User/BeforeMoveUserGroupEvent.php deleted file mode 100644 index 2eb2442dc9..0000000000 --- a/eZ/Publish/API/Repository/Events/User/BeforeMoveUserGroupEvent.php +++ /dev/null @@ -1,37 +0,0 @@ -userGroup = $userGroup; - $this->newParent = $newParent; - } - - public function getUserGroup(): UserGroup - { - return $this->userGroup; - } - - public function getNewParent(): UserGroup - { - return $this->newParent; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/BeforeUnAssignUserFromUserGroupEvent.php b/eZ/Publish/API/Repository/Events/User/BeforeUnAssignUserFromUserGroupEvent.php deleted file mode 100644 index 42fe049f33..0000000000 --- a/eZ/Publish/API/Repository/Events/User/BeforeUnAssignUserFromUserGroupEvent.php +++ /dev/null @@ -1,38 +0,0 @@ -user = $user; - $this->userGroup = $userGroup; - } - - public function getUser(): User - { - return $this->user; - } - - public function getUserGroup(): UserGroup - { - return $this->userGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/BeforeUpdateUserEvent.php b/eZ/Publish/API/Repository/Events/User/BeforeUpdateUserEvent.php deleted file mode 100644 index d9016d21e8..0000000000 --- a/eZ/Publish/API/Repository/Events/User/BeforeUpdateUserEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -user = $user; - $this->userUpdateStruct = $userUpdateStruct; - } - - public function getUser(): User - { - return $this->user; - } - - public function getUserUpdateStruct(): UserUpdateStruct - { - return $this->userUpdateStruct; - } - - public function getUpdatedUser(): User - { - if (!$this->hasUpdatedUser()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedUser() or set it using setUpdatedUser() before you call the getter.', User::class)); - } - - return $this->updatedUser; - } - - public function setUpdatedUser(?User $updatedUser): void - { - $this->updatedUser = $updatedUser; - } - - public function hasUpdatedUser(): bool - { - return $this->updatedUser instanceof User; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/BeforeUpdateUserGroupEvent.php b/eZ/Publish/API/Repository/Events/User/BeforeUpdateUserGroupEvent.php deleted file mode 100644 index c8fd4477e2..0000000000 --- a/eZ/Publish/API/Repository/Events/User/BeforeUpdateUserGroupEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -userGroup = $userGroup; - $this->userGroupUpdateStruct = $userGroupUpdateStruct; - } - - public function getUserGroup(): UserGroup - { - return $this->userGroup; - } - - public function getUserGroupUpdateStruct(): UserGroupUpdateStruct - { - return $this->userGroupUpdateStruct; - } - - public function getUpdatedUserGroup(): UserGroup - { - if (!$this->hasUpdatedUserGroup()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedUserGroup() or set it using setUpdatedUserGroup() before you call the getter.', UserGroup::class)); - } - - return $this->updatedUserGroup; - } - - public function setUpdatedUserGroup(?UserGroup $updatedUserGroup): void - { - $this->updatedUserGroup = $updatedUserGroup; - } - - public function hasUpdatedUserGroup(): bool - { - return $this->updatedUserGroup instanceof UserGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/BeforeUpdateUserTokenEvent.php b/eZ/Publish/API/Repository/Events/User/BeforeUpdateUserTokenEvent.php deleted file mode 100644 index a7233a8810..0000000000 --- a/eZ/Publish/API/Repository/Events/User/BeforeUpdateUserTokenEvent.php +++ /dev/null @@ -1,61 +0,0 @@ -user = $user; - $this->userTokenUpdateStruct = $userTokenUpdateStruct; - } - - public function getUser(): User - { - return $this->user; - } - - public function getUserTokenUpdateStruct(): UserTokenUpdateStruct - { - return $this->userTokenUpdateStruct; - } - - public function getUpdatedUser(): User - { - if (!$this->hasUpdatedUser()) { - throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedUser() or set it using setUpdatedUser() before you call the getter.', User::class)); - } - - return $this->updatedUser; - } - - public function setUpdatedUser(?User $updatedUser): void - { - $this->updatedUser = $updatedUser; - } - - public function hasUpdatedUser(): bool - { - return $this->updatedUser instanceof User; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/CreateUserEvent.php b/eZ/Publish/API/Repository/Events/User/CreateUserEvent.php deleted file mode 100644 index 700e38b666..0000000000 --- a/eZ/Publish/API/Repository/Events/User/CreateUserEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -userCreateStruct = $userCreateStruct; - $this->parentGroups = $parentGroups; - $this->user = $user; - } - - public function getUserCreateStruct(): UserCreateStruct - { - return $this->userCreateStruct; - } - - public function getParentGroups(): array - { - return $this->parentGroups; - } - - public function getUser(): User - { - return $this->user; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/CreateUserGroupEvent.php b/eZ/Publish/API/Repository/Events/User/CreateUserGroupEvent.php deleted file mode 100644 index 4c49d52397..0000000000 --- a/eZ/Publish/API/Repository/Events/User/CreateUserGroupEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -userGroupCreateStruct = $userGroupCreateStruct; - $this->parentGroup = $parentGroup; - $this->userGroup = $userGroup; - } - - public function getUserGroupCreateStruct(): UserGroupCreateStruct - { - return $this->userGroupCreateStruct; - } - - public function getParentGroup(): UserGroup - { - return $this->parentGroup; - } - - public function getUserGroup(): UserGroup - { - return $this->userGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/DeleteUserEvent.php b/eZ/Publish/API/Repository/Events/User/DeleteUserEvent.php deleted file mode 100644 index f622d745f1..0000000000 --- a/eZ/Publish/API/Repository/Events/User/DeleteUserEvent.php +++ /dev/null @@ -1,39 +0,0 @@ -user = $user; - $this->locations = $locations; - } - - public function getUser(): User - { - return $this->user; - } - - public function getLocations(): array - { - return $this->locations; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/DeleteUserGroupEvent.php b/eZ/Publish/API/Repository/Events/User/DeleteUserGroupEvent.php deleted file mode 100644 index 60b19c983c..0000000000 --- a/eZ/Publish/API/Repository/Events/User/DeleteUserGroupEvent.php +++ /dev/null @@ -1,39 +0,0 @@ -userGroup = $userGroup; - $this->locations = $locations; - } - - public function getUserGroup(): UserGroup - { - return $this->userGroup; - } - - public function getLocations(): array - { - return $this->locations; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/MoveUserGroupEvent.php b/eZ/Publish/API/Repository/Events/User/MoveUserGroupEvent.php deleted file mode 100644 index 2e769c37ea..0000000000 --- a/eZ/Publish/API/Repository/Events/User/MoveUserGroupEvent.php +++ /dev/null @@ -1,39 +0,0 @@ -userGroup = $userGroup; - $this->newParent = $newParent; - } - - public function getUserGroup(): UserGroup - { - return $this->userGroup; - } - - public function getNewParent(): UserGroup - { - return $this->newParent; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/UnAssignUserFromUserGroupEvent.php b/eZ/Publish/API/Repository/Events/User/UnAssignUserFromUserGroupEvent.php deleted file mode 100644 index 60c1ad57ea..0000000000 --- a/eZ/Publish/API/Repository/Events/User/UnAssignUserFromUserGroupEvent.php +++ /dev/null @@ -1,40 +0,0 @@ -user = $user; - $this->userGroup = $userGroup; - } - - public function getUser(): User - { - return $this->user; - } - - public function getUserGroup(): UserGroup - { - return $this->userGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/UpdateUserEvent.php b/eZ/Publish/API/Repository/Events/User/UpdateUserEvent.php deleted file mode 100644 index 3a113b0429..0000000000 --- a/eZ/Publish/API/Repository/Events/User/UpdateUserEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -user = $user; - $this->userUpdateStruct = $userUpdateStruct; - $this->updatedUser = $updatedUser; - } - - public function getUser(): User - { - return $this->user; - } - - public function getUserUpdateStruct(): UserUpdateStruct - { - return $this->userUpdateStruct; - } - - public function getUpdatedUser(): User - { - return $this->updatedUser; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/UpdateUserGroupEvent.php b/eZ/Publish/API/Repository/Events/User/UpdateUserGroupEvent.php deleted file mode 100644 index cd33f8d273..0000000000 --- a/eZ/Publish/API/Repository/Events/User/UpdateUserGroupEvent.php +++ /dev/null @@ -1,49 +0,0 @@ -userGroup = $userGroup; - $this->userGroupUpdateStruct = $userGroupUpdateStruct; - $this->updatedUserGroup = $updatedUserGroup; - } - - public function getUserGroup(): UserGroup - { - return $this->userGroup; - } - - public function getUserGroupUpdateStruct(): UserGroupUpdateStruct - { - return $this->userGroupUpdateStruct; - } - - public function getUpdatedUserGroup(): UserGroup - { - return $this->updatedUserGroup; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/UpdateUserPasswordEvent.php b/eZ/Publish/API/Repository/Events/User/UpdateUserPasswordEvent.php deleted file mode 100644 index 641f1329b0..0000000000 --- a/eZ/Publish/API/Repository/Events/User/UpdateUserPasswordEvent.php +++ /dev/null @@ -1,49 +0,0 @@ -user = $user; - $this->newPassword = $newPassword; - $this->updatedUser = $updatedUser; - } - - public function getUser(): User - { - return $this->user; - } - - public function getNewPassword(): string - { - return $this->newPassword; - } - - public function getUpdatedUser(): User - { - return $this->updatedUser; - } -} diff --git a/eZ/Publish/API/Repository/Events/User/UpdateUserTokenEvent.php b/eZ/Publish/API/Repository/Events/User/UpdateUserTokenEvent.php deleted file mode 100644 index 87ab06c7d2..0000000000 --- a/eZ/Publish/API/Repository/Events/User/UpdateUserTokenEvent.php +++ /dev/null @@ -1,50 +0,0 @@ -user = $user; - $this->userTokenUpdateStruct = $userTokenUpdateStruct; - $this->updatedUser = $updatedUser; - } - - public function getUser(): User - { - return $this->user; - } - - public function getUserTokenUpdateStruct(): UserTokenUpdateStruct - { - return $this->userTokenUpdateStruct; - } - - public function getUpdatedUser(): User - { - return $this->updatedUser; - } -} diff --git a/eZ/Publish/API/Repository/Events/UserPreference/BeforeSetUserPreferenceEvent.php b/eZ/Publish/API/Repository/Events/UserPreference/BeforeSetUserPreferenceEvent.php deleted file mode 100644 index c532823f6e..0000000000 --- a/eZ/Publish/API/Repository/Events/UserPreference/BeforeSetUserPreferenceEvent.php +++ /dev/null @@ -1,27 +0,0 @@ -userPreferenceSetStructs = $userPreferenceSetStructs; - } - - public function getUserPreferenceSetStructs(): array - { - return $this->userPreferenceSetStructs; - } -} diff --git a/eZ/Publish/API/Repository/Events/UserPreference/SetUserPreferenceEvent.php b/eZ/Publish/API/Repository/Events/UserPreference/SetUserPreferenceEvent.php deleted file mode 100644 index ad53a4bd12..0000000000 --- a/eZ/Publish/API/Repository/Events/UserPreference/SetUserPreferenceEvent.php +++ /dev/null @@ -1,27 +0,0 @@ -userPreferenceSetStructs = $userPreferenceSetStructs; - } - - public function getUserPreferenceSetStructs(): array - { - return $this->userPreferenceSetStructs; - } -} diff --git a/eZ/Publish/API/Repository/Exceptions/BadStateException.php b/eZ/Publish/API/Repository/Exceptions/BadStateException.php deleted file mode 100644 index 153ace1ecc..0000000000 --- a/eZ/Publish/API/Repository/Exceptions/BadStateException.php +++ /dev/null @@ -1,16 +0,0 @@ - - * array( - * 'stringLength' => array( - * 'minStringLength' => array( - * 'type' => 'int', - * 'default' => 0, - * ), - * 'maxStringLength' => array( - * 'type' => 'int' - * 'default' => null, - * ) - * ), - * ); - * - * - * @return mixed - */ - public function getValidatorConfigurationSchema(); - - /** - * Indicates if the field type supports indexing and sort keys for searching. - * - * @return bool - */ - public function isSearchable(): bool; - - /** - * Indicates if the field definition of this type can appear only once in the same ContentType. - * - * @return bool - */ - public function isSingular(): bool; - - /** - * Indicates if the field definition of this type can be added to a ContentType with Content instances. - * - * @return bool - */ - public function onlyEmptyInstance(): bool; - - /** - * Returns the empty value for this field type. - * - * @return mixed - */ - public function getEmptyValue(); - - /** - * Returns if the given $value is considered empty by the field type. - * - * Usually, only the value returned by {@link getEmptyValue()} is - * considered empty but that is not always the case. - * - * Note: This function assumes that $value is valid so this function can only - * be used reliably on $values that came from the API, not from the user. - * - * @param mixed $value - * - * @return bool - */ - public function isEmptyValue($value): bool; - - /** - * Converts an $hash to the Value defined by the field type. - * - * This is the reverse operation to {@link toHash()}. - * - * @param mixed $hash - * - * @return mixed - */ - public function fromHash($hash); - - /** - * Converts the given $value into a plain hash format. - * - * @param mixed $value - * - * @return mixed - */ - public function toHash($value); - - /** - * Converts the given $fieldSettings to a simple hash format. - * - * @param mixed $fieldSettings - * - * @return array|hash|scalar|null - */ - public function fieldSettingsToHash($fieldSettings); - - /** - * Converts the given $fieldSettingsHash to field settings of the type. - * - * This is the reverse operation of {@link fieldSettingsToHash()}. - * - * @param array|hash|scalar|null $fieldSettingsHash - * - * @return mixed - */ - public function fieldSettingsFromHash($fieldSettingsHash); - - /** - * Converts the given $validatorConfiguration to a simple hash format. - * - * @param mixed $validatorConfiguration - * - * @return array|hash|scalar|null - */ - public function validatorConfigurationToHash($validatorConfiguration); - - /** - * Converts the given $validatorConfigurationHash to a validator - * configuration of the type. - * - * @param array|hash|scalar|null $validatorConfigurationHash - * - * @return mixed - */ - public function validatorConfigurationFromHash($validatorConfigurationHash); - - /** - * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * This methods determines if the given $validatorConfiguration is - * structurally correct and complies to the validator configuration schema as defined in FieldType. - * - * @param mixed $validatorConfiguration - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateValidatorConfiguration($validatorConfiguration): iterable; - - /** - * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * This methods determines if the given $fieldSettings are structurally - * correct and comply to the settings schema as defined in FieldType. - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings): iterable; - - /** - * Validates a field value based on the validator configuration in the field definition. - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDef The field definition of the field - * @param \eZ\Publish\SPI\FieldType\Value $value The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateValue(FieldDefinition $fieldDef, Value $value): iterable; -} diff --git a/eZ/Publish/API/Repository/FieldTypeService.php b/eZ/Publish/API/Repository/FieldTypeService.php deleted file mode 100644 index 45c30d54ee..0000000000 --- a/eZ/Publish/API/Repository/FieldTypeService.php +++ /dev/null @@ -1,44 +0,0 @@ -searchService = $searchService; - $this->query = $query; - $this->languageFilter = $languageFilter; - $this->filterOnUserPermissions = $filterOnUserPermissions; - } - - final public function fetch(int $offset, int $limit): Iterator - { - $query = clone $this->query; - $query->offset = $offset; - $query->limit = $limit; - - return $this->executeSearch($query)->getIterator(); - } - - abstract protected function executeSearch(Query $query): SearchResult; -} diff --git a/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/ContentFilteringAdapter.php b/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/ContentFilteringAdapter.php deleted file mode 100644 index 6b075f59b8..0000000000 --- a/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/ContentFilteringAdapter.php +++ /dev/null @@ -1,41 +0,0 @@ -contentService = $contentService; - $this->filter = $filter; - $this->languages = $languages; - } - - public function fetch(int $offset, int $limit): Iterator - { - $filter = clone $this->filter; - $filter->sliceBy($limit, $offset); - - return $this->contentService->find($filter, $this->languages)->getIterator(); - } -} diff --git a/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php b/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php deleted file mode 100644 index 443ecd248c..0000000000 --- a/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php +++ /dev/null @@ -1,24 +0,0 @@ -searchService->findContentInfo( - $query, - $this->languageFilter, - $this->filterOnUserPermissions - ); - } -} diff --git a/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php b/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php deleted file mode 100644 index 518adaf673..0000000000 --- a/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php +++ /dev/null @@ -1,24 +0,0 @@ -searchService->findContent( - $query, - $this->languageFilter, - $this->filterOnUserPermissions - ); - } -} diff --git a/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/LocationFilteringAdapter.php b/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/LocationFilteringAdapter.php deleted file mode 100644 index eea70efdad..0000000000 --- a/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/LocationFilteringAdapter.php +++ /dev/null @@ -1,41 +0,0 @@ -locationService = $locationService; - $this->filter = $filter; - $this->languages = $languages; - } - - public function fetch(int $offset, int $limit): Iterator - { - $filter = clone $this->filter; - $filter->sliceBy($limit, $offset); - - return $this->locationService->find($filter, $this->languages)->getIterator(); - } -} diff --git a/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php b/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php deleted file mode 100644 index f37946063c..0000000000 --- a/eZ/Publish/API/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php +++ /dev/null @@ -1,35 +0,0 @@ -searchService->findLocations( - $query, - $this->languageFilter, - $this->filterOnUserPermissions - ); - } -} diff --git a/eZ/Publish/API/Repository/LanguageResolver.php b/eZ/Publish/API/Repository/LanguageResolver.php deleted file mode 100644 index 81afcecf8d..0000000000 --- a/eZ/Publish/API/Repository/LanguageResolver.php +++ /dev/null @@ -1,70 +0,0 @@ -sudo(function (Repository $repo) use ($locationId) { - * return $repo->getLocationService()->loadLocation($locationId) - * } - * ); - * - * @template T - * - * @param callable(\eZ\Publish\API\Repository\Repository): T $callback - * @param \eZ\Publish\API\Repository\Repository|null $outerRepository Optional, mostly - * for internal use but allows to specify Repository to pass to closure. - * - * @return T - */ - public function sudo(callable $callback, ?Repository $outerRepository = null); - - /** - * Get Content Service. - * - * Get service object to perform operations on Content objects and it's aggregate members. - * - * @return \eZ\Publish\API\Repository\ContentService - */ - public function getContentService(): ContentService; - - /** - * Get Content Language Service. - * - * Get service object to perform operations on Content language objects - * - * @return \eZ\Publish\API\Repository\LanguageService - */ - public function getContentLanguageService(): LanguageService; - - /** - * Get Content Type Service. - * - * Get service object to perform operations on Content Type objects and it's aggregate members. - * ( Group, Field & FieldCategory ) - * - * @return \eZ\Publish\API\Repository\ContentTypeService - */ - public function getContentTypeService(): ContentTypeService; - - /** - * Get Content Location Service. - * - * Get service object to perform operations on Location objects and subtrees - * - * @return \eZ\Publish\API\Repository\LocationService - */ - public function getLocationService(): LocationService; - - /** - * Get Content Trash service. - * - * Trash service allows to perform operations related to location trash - * (trash/untrash, load/list from trash...) - * - * @return \eZ\Publish\API\Repository\TrashService - */ - public function getTrashService(): TrashService; - - /** - * Get Content Section Service. - * - * Get Section service that lets you manipulate section objects - * - * @return \eZ\Publish\API\Repository\SectionService - */ - public function getSectionService(): SectionService; - - /** - * Get Search Service. - * - * Get search service that lets you find content objects - * - * @return \eZ\Publish\API\Repository\SearchService - */ - public function getSearchService(): SearchService; - - /** - * Get User Service. - * - * Get service object to perform operations on Users and UserGroup - * - * @return \eZ\Publish\API\Repository\UserService - */ - public function getUserService(): UserService; - - /** - * Get URLAliasService. - * - * @return \eZ\Publish\API\Repository\URLAliasService - */ - public function getURLAliasService(): URLAliasService; - - /** - * Get URLWildcardService. - * - * @return \eZ\Publish\API\Repository\URLWildcardService - */ - public function getURLWildcardService(): URLWildcardService; - - /** - * Get ObjectStateService. - * - * @return \eZ\Publish\API\Repository\ObjectStateService - */ - public function getObjectStateService(): ObjectStateService; - - /** - * Get RoleService. - * - * @return \eZ\Publish\API\Repository\RoleService - */ - public function getRoleService(): RoleService; - - /** - * Get FieldTypeService. - * - * @return \eZ\Publish\API\Repository\FieldTypeService - */ - public function getFieldTypeService(): FieldTypeService; - - /** - * Get PermissionResolver. - * - * @return \eZ\Publish\API\Repository\PermissionResolver - */ - public function getPermissionResolver(): PermissionResolver; - - /** - * Get URLService. - * - * @return \eZ\Publish\API\Repository\URLService - */ - public function getURLService(): URLService; - - /** - * Get BookmarkService. - * - * @return \eZ\Publish\API\Repository\BookmarkService - */ - public function getBookmarkService(): BookmarkService; - - /** - * Get NotificationService. - * - * @return \eZ\Publish\API\Repository\NotificationService - */ - public function getNotificationService(): NotificationService; - - /** - * Get UserPreferenceService. - * - * @return \eZ\Publish\API\Repository\UserPreferenceService - */ - public function getUserPreferenceService(): UserPreferenceService; - - /** - * Begin transaction. - * - * Begins an transaction, make sure you'll call commit or rollback when done, - * otherwise work will be lost. - */ - public function beginTransaction(): void; - - /** - * Commit transaction. - * - * Commit transaction, or throw exceptions if no transactions has been started. - * - * @throws \RuntimeException If no transaction has been started - */ - public function commit(): void; - - /** - * Rollback transaction. - * - * Rollback transaction, or throw exceptions if no transactions has been started. - * - * @throws \RuntimeException If no transaction has been started - */ - public function rollback(): void; -} diff --git a/eZ/Publish/API/Repository/RoleService.php b/eZ/Publish/API/Repository/RoleService.php deleted file mode 100644 index 08a77cf110..0000000000 --- a/eZ/Publish/API/Repository/RoleService.php +++ /dev/null @@ -1,404 +0,0 @@ -score and SearchResult->maxScore - * properties as well as sort by this if no sort clauses are specified. - * - * @since 6.12 (constant added in 6.7.6 and up) - */ - public const CAPABILITY_SCORING = 1; - - /** - * Capability flag for facets feature for use with {@see ::supports()}. - * - * Faceted search: https://en.wikipedia.org/wiki/Faceted_search - * - * Note: Even if search engine tells you this is supported, beware: - * - It might not support all facets, by design it will only return facets for facet builders the search engine supports. - * - Some of the faceting features are still work in progress in API and won't be further matured before in 7 .x - * releases - * - * @since 6.12 (constant added in 6.7.6 and up) - */ - public const CAPABILITY_FACETS = 2; - - /** - * Capability flag for custom fields feature for use with {@see ::supports()}. - * - * Custom fields is the capability for search engines to 1. allow you to extend the search index via plugins to - * generate custom fields, like a different representation (format, ...) of an existing field or similar. And 2. - * allow you on some search criteria to specify this custom field to rather query on that instead of the default - * field generated by the system. - * - * @since 6.12 (constant added in 6.7.6 and up) - */ - public const CAPABILITY_CUSTOM_FIELDS = 4; - - /** - * Capability flag for spellcheck feature for use with {@see ::supports()}. - * - * Spell check within search capabilities refers to ability to suggest better wordings in fulltext search string. - * - * WARNING: This feature is considered experimental given it is not completely designed yet in terms of how it should - * interact with FullText criterion (singular) which is the most relevant here. Also given how FullText can be part of a more complex criteria it - * might imply a need to more strictly define where users are supposed to place FullText vs other criteria. - * - * @since 6.12 (constant added in 6.7.6 and up) - */ - public const CAPABILITY_SPELLCHECK = 8; - - /** - * Capability flag for highlight feature for use with {@see ::supports()}. - * - * Highlight in search refers to extracting relevant text from the search index that matches the search result, - * typically returning a chunk of text of a predefined size with matching text highlighted. - * - * WARNING: This feature is considered experimental given it is not completely designed yet in terms of how it should - * interact with hits within rich content of either eZ or custom field types. it is also unclear how it should - * hint what part of the highlight is matched. - * - * @internal Maybe it should rather give just matched text and hint of which field (several: one with best score) - * was matched and leave it to field type to render result with that info taking into account. But for now it is - * designed as simple string field, so should be string with for instance `` around matched text. - * - * @since 6.12 (constant added in 6.7.6 and up) - */ - public const CAPABILITY_HIGHLIGHT = 16; - - /** - * Capability flag for suggest feature for use with {@see ::supports()}. - * - * WARNING: This feature is considered experimental given it is not completely clear what it is supposed to do. Feature - * might be deprecated in the future. - * - * @since 6.12 (constant added in 6.7.6 and up) - */ - public const CAPABILITY_SUGGEST = 32; - - /** - * Capability flag for advanced fulltext feature for use with {@see ::supports()}. - * - * Advance full text is a feature making to possible by current engine to parse advance full text expressions. - * - * @since 6.12 (constant added in 6.7.6 and up) - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\FullText - */ - public const CAPABILITY_ADVANCED_FULLTEXT = 64; - - /** - * Capability flag for aggregation feature for use with {@see ::supports()}. - * - * @since eZ Platform 3.2 - */ - public const CAPABILITY_AGGREGATIONS = 128; - - /** - * Finds content objects for the given query. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid - * - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * @param bool $filterOnUserPermissions if true only the objects which the user is allowed to read are returned. - * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult - */ - public function findContent(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult; - - /** - * Finds contentInfo objects for the given query. - * - * This method works just like findContent, however does not load the full Content Objects. This means - * it can be more efficient for use cases where you don't need the full Content. Also including use cases - * where content will be loaded by separate code, like an ESI based sub requests that takes content ID as input. - * - * @since 5.4.5 - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid - * - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * @param bool $filterOnUserPermissions if true (default) only the objects which is the user allowed to read are returned. - * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult - */ - public function findContentInfo(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult; - - /** - * Performs a query for a single content object. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if criterion is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function findSingle(Criterion $filter, array $languageFilter = [], bool $filterOnUserPermissions = true): Content; - - /** - * Suggests a list of values for the given prefix. - * - * @param string $prefix - * @param string[] $fieldPaths - * @param int $limit - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter - */ - public function suggest(string $prefix, array $fieldPaths = [], int $limit = 10, Criterion $filter = null); - - /** - * Finds Locations for the given query. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid - * - * @param \eZ\Publish\API\Repository\Values\Content\LocationQuery $query - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. - * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult - */ - public function findLocations(LocationQuery $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult; - - /** - * Query for supported capability of currently configured search engine. - * - * Will return false if search engine does not implement {@see \eZ\Publish\SPI\Search\Capable}. - * - * @since 6.12 - * - * @param int $capabilityFlag One of CAPABILITY_* constants. - * - * @return bool - */ - public function supports(int $capabilityFlag): bool; -} diff --git a/eZ/Publish/API/Repository/SectionService.php b/eZ/Publish/API/Repository/SectionService.php deleted file mode 100644 index c9fd5314a2..0000000000 --- a/eZ/Publish/API/Repository/SectionService.php +++ /dev/null @@ -1,145 +0,0 @@ -getSetupFactory()->getRepository(false); - } catch (PDOException $e) { - $this->fail( - 'The communication with the database cannot be established. ' . - "This is required in order to perform the tests.\n\n" . - 'Exception: ' . $e - ); - } catch (Exception $e) { - $this->fail( - 'Cannot create a repository with predefined user. ' . - 'Check the UserService or RoleService implementation. ' . - PHP_EOL . PHP_EOL . - 'Exception: ' . $e - ); - } - } - - /** - * Resets the temporary used repository between each test run. - */ - protected function tearDown(): void - { - $this->repository = null; - parent::tearDown(); - } - - /** - * Returns the ID generator, fitting to the repository implementation. - * - * @return \eZ\Publish\API\Repository\Tests\IdManager - */ - protected function getIdManager() - { - return $this->getSetupFactory()->getIdManager(); - } - - /** - * Generates a repository specific ID value. - * - * @param string $type - * @param mixed $rawId - * - * @return mixed - */ - protected function generateId($type, $rawId) - { - return $this->getIdManager()->generateId($type, $rawId); - } - - /** - * Parses a repository specific ID value. - * - * @param string $type - * @param mixed $id - * - * @return mixed - */ - protected function parseId($type, $id) - { - return $this->getIdManager()->parseId($type, $id); - } - - /** - * Returns a config setting provided by the setup factory. - * - * @param string $configKey - * - * @return mixed - */ - protected function getConfigValue($configKey) - { - return $this->getSetupFactory()->getConfigValue($configKey); - } - - /** - * Tests if the currently tested api is based on a V4 implementation. - * - * @deprecated Not in use, will be removed. - * - * @return bool - */ - protected function isVersion4() - { - return isset($_ENV['backendVersion']) && '4' === $_ENV['backendVersion']; - } - - /** - * @param bool $initialInitializeFromScratch Only has an effect if set in first call within a test - */ - protected function getRepository(bool $initialInitializeFromScratch = true): Repository - { - if (null === $this->repository) { - try { - $this->repository = $this->getSetupFactory()->getRepository( - $initialInitializeFromScratch - ); - } catch (ErrorException $e) { - self::fail( - sprintf( - '%s: %s in %s:%d', - __FUNCTION__, - $e->getMessage(), - $e->getFile(), - $e->getLine() - ) - ); - } - } - - return $this->repository; - } - - /** - * @throws \ErrorException - */ - protected function getSetupFactory(): SetupFactory - { - if (null === $this->setupFactory) { - if (false === ($setupClass = getenv('setupFactory'))) { - $setupClass = LegacySetupFactory::class; - putenv("setupFactory={$setupClass}"); - } - - if (false === getenv('fixtureDir')) { - putenv('fixtureDir=Legacy'); - } - - if (false === class_exists($setupClass)) { - throw new ErrorException( - sprintf( - 'Environment variable "setupFactory" does not reference an existing class: %s. Did you forget to install a package dependency?', - $setupClass - ) - ); - } - - $this->setupFactory = new $setupClass(); - } - - return $this->setupFactory; - } - - /** - * Asserts that properties given in $expectedValues are correctly set in - * $actualObject. - * - * @param mixed[] $expectedValues - * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject - */ - protected function assertPropertiesCorrect(array $expectedValues, ValueObject $actualObject) - { - foreach ($expectedValues as $propertyName => $propertyValue) { - if ($propertyValue instanceof ValueObject) { - $this->assertStructPropertiesCorrect($propertyValue, $actualObject->$propertyName); - } elseif (is_array($propertyValue)) { - foreach ($propertyValue as $key => $value) { - if ($value instanceof ValueObject) { - $this->assertStructPropertiesCorrect($value, $actualObject->$propertyName[$key]); - } else { - $this->assertPropertiesEqual("$propertyName\[$key\]", $value, $actualObject->$propertyName[$key]); - } - } - } else { - $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName); - } - } - } - - /** - * Asserts that properties given in $expectedValues are correctly set in - * $actualObject. - * - * If the property type is array, it will be sorted before comparison. - * - * @TODO: introduced because of randomly failing tests, ref: https://jira.ez.no/browse/EZP-21734 - * - * @param mixed[] $expectedValues - * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject - */ - protected function assertPropertiesCorrectUnsorted(array $expectedValues, ValueObject $actualObject) - { - foreach ($expectedValues as $propertyName => $propertyValue) { - if ($propertyValue instanceof ValueObject) { - $this->assertStructPropertiesCorrect($propertyValue, $actualObject->$propertyName); - } else { - $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName, true); - } - } - } - - /** - * Asserts all properties from $expectedValues are correctly set in - * $actualObject. Additional (virtual) properties can be asserted using - * $additionalProperties. - * - * @param \eZ\Publish\API\Repository\Values\ValueObject $expectedValues - * @param \eZ\Publish\API\Repository\Values\ValueObject $actualObject - * @param array $propertyNames - */ - protected function assertStructPropertiesCorrect(ValueObject $expectedValues, ValueObject $actualObject, array $additionalProperties = []) - { - foreach ($expectedValues as $propertyName => $propertyValue) { - if ($propertyValue instanceof ValueObject) { - $this->assertStructPropertiesCorrect($propertyValue, $actualObject->$propertyName); - } else { - $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName); - } - } - - foreach ($additionalProperties as $propertyName) { - $this->assertPropertiesEqual($propertyName, $expectedValues->$propertyName, $actualObject->$propertyName); - } - } - - /** - * @see \eZ\Publish\API\Repository\Tests\BaseTest::assertPropertiesCorrectUnsorted() - * - * @param array $items An array of scalar values - */ - private function sortItems(array &$items) - { - $sorter = function ($a, $b) { - if (!is_scalar($a) || !is_scalar($b)) { - $this->fail('Wrong usage: method ' . __METHOD__ . ' accepts only an array of scalar values'); - } - - return strcmp($a, $b); - }; - usort($items, $sorter); - } - - private function assertPropertiesEqual($propertyName, $expectedValue, $actualValue, $sortArray = false) - { - if ($expectedValue instanceof ArrayObject) { - $expectedValue = $expectedValue->getArrayCopy(); - } elseif ($expectedValue instanceof DateTime) { - $expectedValue = $expectedValue->format(DateTime::RFC850); - } - if ($actualValue instanceof ArrayObject) { - $actualValue = $actualValue->getArrayCopy(); - } elseif ($actualValue instanceof DateTime) { - $actualValue = $actualValue->format(DateTime::RFC850); - } - - if ($sortArray && is_array($actualValue) && is_array($expectedValue)) { - $this->sortItems($actualValue); - $this->sortItems($expectedValue); - } - - $this->assertEquals( - $expectedValue, - $actualValue, - sprintf('Object property "%s" incorrect.', $propertyName) - ); - } - - /** - * Create a user in editor user group. - */ - protected function createUserVersion1(string $login = 'user', ?string $email = null, ContentType $contentType = null): User - { - $repository = $this->getRepository(); - - /* BEGIN: Inline */ - // ID of the "Editors" user group in an eZ Publish demo installation - $editorsGroupId = 13; - - $userService = $repository->getUserService(); - - // Instantiate a create struct with mandatory properties - $email = $email ?? "{$login}@example.com"; - $userCreate = $userService->newUserCreateStruct( - $login, - $email, - 'VerySecret@Password.1234', - 'eng-US' - ); - $userCreate->enabled = true; - - // Set some fields required by the user ContentType - $userCreate->setField('first_name', 'Example'); - $userCreate->setField('last_name', 'User'); - - if (!empty($contentType)) { - $userCreate->contentType = $contentType; - } - - // Load parent group for the user - $group = $userService->loadUserGroup($editorsGroupId); - - // Create a new user instance. - $user = $userService->createUser($userCreate, [$group]); - /* END: Inline */ - - return $user; - } - - /** - * Create a user in new user group with editor rights limited to Media Library (/1/48/). - * - * @uses ::createCustomUserVersion1() - * - * @return \eZ\Publish\API\Repository\Values\User\User - */ - protected function createMediaUserVersion1() - { - return $this->createCustomUserVersion1( - 'Media Editor', - 'Editor', - new SubtreeLimitation(['limitationValues' => ['/1/43/']]) - ); - } - - /** - * Create a user with new user group and assign a existing role (optionally with RoleLimitation). - * - * @param string $userGroupName Name of the new user group to create - * @param string $roleIdentifier Role identifier to assign to the new group - * @param \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation|null $roleLimitation - * - * @return \eZ\Publish\API\Repository\Values\User\User - */ - protected function createCustomUserVersion1($userGroupName, $roleIdentifier, RoleLimitation $roleLimitation = null) - { - return $this->createCustomUserWithLogin( - 'user', - 'user@example.com', - $userGroupName, - $roleIdentifier, - $roleLimitation - ); - } - - /** - * Create a user with new user group and assign a existing role (optionally with RoleLimitation). - * - * @param string $login User login - * @param string $email User e-mail - * @param string $userGroupName Name of the new user group to create - * @param string $roleIdentifier Role identifier to assign to the new group - * @param \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation|null $roleLimitation - * - * @return \eZ\Publish\API\Repository\Values\User\User - */ - protected function createCustomUserWithLogin( - $login, - $email, - $userGroupName, - $roleIdentifier, - RoleLimitation $roleLimitation = null - ) { - $repository = $this->getRepository(); - - /* BEGIN: Inline */ - // ID of the "Users" user group in an eZ Publish demo installation - $rootUsersGroupId = $this->generateId('location', 4); - - $roleService = $repository->getRoleService(); - $userService = $repository->getUserService(); - - // Get a group create struct - $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); - $userGroupCreate->setField('name', $userGroupName); - - // Create new group with media editor rights - $userGroup = $userService->createUserGroup( - $userGroupCreate, - $userService->loadUserGroup($rootUsersGroupId) - ); - $roleService->assignRoleToUserGroup( - $roleService->loadRoleByIdentifier($roleIdentifier), - $userGroup, - $roleLimitation - ); - - // Instantiate a create struct with mandatory properties - $userCreate = $userService->newUserCreateStruct( - $login, - $email, - 'secret', - 'eng-US' - ); - $userCreate->enabled = true; - - // Set some fields required by the user ContentType - $userCreate->setField('first_name', 'Example'); - $userCreate->setField('last_name', ucfirst($login)); - - // Create a new user instance. - $user = $userService->createUser($userCreate, [$userGroup]); - /* END: Inline */ - - return $user; - } - - /** - * Create a user using given data. - * - * @param string $login - * @param string $firstName - * @param string $lastName - * @param \eZ\Publish\API\Repository\Values\User\UserGroup|null $userGroup optional user group, Editor by default - * - * @return \eZ\Publish\API\Repository\Values\User\User - */ - protected function createUser($login, $firstName, $lastName, UserGroup $userGroup = null) - { - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - if (null === $userGroup) { - $userGroup = $userService->loadUserGroup(13); - } - - // Instantiate a create struct with mandatory properties - $userCreate = $userService->newUserCreateStruct( - $login, - "{$login}@example.com", - 'secret', - 'eng-US' - ); - $userCreate->enabled = true; - - // Set some fields required by the user ContentType - $userCreate->setField('first_name', $firstName); - $userCreate->setField('last_name', $lastName); - - // Create a new user instance. - $user = $userService->createUser($userCreate, [$userGroup]); - - return $user; - } - - /** - * Only for internal use. - * - * Creates a \DateTime object for $timestamp in the current time zone - * - * @param int $timestamp - * - * @return \DateTime - */ - public function createDateTime($timestamp = null) - { - $dateTime = new \DateTime(); - if ($timestamp !== null) { - $dateTime->setTimestamp($timestamp); - } - - return $dateTime; - } - - /** - * Calls given Repository's aggregated SearchHandler::refresh(). - * - * @param \eZ\Publish\API\Repository\Repository $repository - */ - protected function refreshSearch(Repository $repository) - { - if ($this->isLegacySearchEngineSetup()) { - return; - } - - while (true) { - $repositoryReflection = new \ReflectionObject($repository); - // If the repository is decorated, we need to recurse in the "repository" property - if (!$repositoryReflection->hasProperty('repository')) { - break; - } - - $repositoryProperty = $repositoryReflection->getProperty('repository'); - $repositoryProperty->setAccessible(true); - $repository = $repositoryProperty->getValue($repository); - } - - $searchHandlerProperty = new \ReflectionProperty($repository, 'searchHandler'); - $searchHandlerProperty->setAccessible(true); - - /** @var \EzSystems\EzPlatformSolrSearchEngine\Handler $searchHandler */ - $searchHandler = $searchHandlerProperty->getValue($repository); - - $searchHandler->commit(); - } - - protected function isLegacySearchEngineSetup(): bool - { - return get_class($this->getSetupFactory()) === LegacySetupFactory::class; - } - - /** - * Create role of a given name with the given policies described by an array. - * - * @param $roleName - * @param array $policiesData [['module' => 'content', 'function' => 'read', 'limitations' => []] - * - * @return \eZ\Publish\API\Repository\Values\User\Role - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function createRoleWithPolicies($roleName, array $policiesData) - { - $repository = $this->getRepository(false); - $roleService = $repository->getRoleService(); - - $roleCreateStruct = $roleService->newRoleCreateStruct($roleName); - foreach ($policiesData as $policyData) { - $policyCreateStruct = $roleService->newPolicyCreateStruct( - $policyData['module'], - $policyData['function'] - ); - - if (isset($policyData['limitations'])) { - foreach ($policyData['limitations'] as $limitation) { - $policyCreateStruct->addLimitation($limitation); - } - } - - $roleCreateStruct->addPolicy($policyCreateStruct); - } - - $roleDraft = $roleService->createRole($roleCreateStruct); - - $roleService->publishRoleDraft($roleDraft); - - return $roleService->loadRole($roleDraft->id); - } - - /** - * Create user and assign new role with the given policies. - * - * @param string $login - * @param array $policiesData list of policies in the form of [ [ 'module' => 'name', 'function' => 'name'] ] - * @param \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation|null $roleLimitation - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function createUserWithPolicies($login, array $policiesData, RoleLimitation $roleLimitation = null) - { - $repository = $this->getRepository(false); - $roleService = $repository->getRoleService(); - $userService = $repository->getUserService(); - - $repository->beginTransaction(); - try { - $userCreateStruct = $userService->newUserCreateStruct( - $login, - "{$login}@test.local", - $login, - 'eng-GB' - ); - $userCreateStruct->setField('first_name', $login); - $userCreateStruct->setField('last_name', $login); - $user = $userService->createUser($userCreateStruct, [$userService->loadUserGroup(4)]); - - $role = $this->createRoleWithPolicies(uniqid('role_for_' . $login . '_', true), $policiesData); - $roleService->assignRoleToUser($role, $user, $roleLimitation); - - $repository->commit(); - - return $user; - } catch (ForbiddenException | NotFoundException | UnauthorizedException $ex) { - $repository->rollback(); - throw $ex; - } - } - - /** - * @throws \ErrorException - */ - protected function getRawDatabaseConnection(): Connection - { - $connection = $this - ->getSetupFactory() - ->getServiceContainer()->get('ezpublish.persistence.connection'); - - if (!$connection instanceof Connection) { - throw new \RuntimeException( - sprintf('Found %s instead of %s', get_class($connection), Connection::class) - ); - } - - return $connection; - } - - /** - * Executes the given callback passing raw Database Connection (\Doctrine\DBAL\Connection). - * Returns the result returned by the given callback. - * - * **Note**: The method clears the entire persistence cache pool. - * - * @throws \Exception - * - * @param callable $callback - * - * @return mixed the return result of the given callback - */ - public function performRawDatabaseOperation(callable $callback) - { - $repository = $this->getRepository(false); - $repository->beginTransaction(); - try { - $callback( - $this->getRawDatabaseConnection() - ); - $repository->commit(); - } catch (Exception $e) { - $repository->rollback(); - throw $e; - } - - /** @var \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface $cachePool */ - $cachePool = $this - ->getSetupFactory() - ->getServiceContainer()->get('ezpublish.cache_pool'); - - $cachePool->clear(); - } - - /** - * Traverse all errors for all fields in all Translations to find expected one. - * - * @param \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException $exception - * @param string $expectedValidationErrorMessage - */ - protected function assertValidationErrorOccurs( - ContentFieldValidationException $exception, - string $expectedValidationErrorMessage - ): void { - $constraint = new PHPUnitConstraint\ValidationErrorOccurs($expectedValidationErrorMessage); - - self::assertThat($exception, $constraint); - } - - /** - * Traverse all errors for all fields in all Translations to find if all expected ones occurred. - * - * @param \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException $exception - * @param string[] $expectedValidationErrorMessages - */ - protected function assertAllValidationErrorsOccur( - ContentFieldValidationException $exception, - array $expectedValidationErrorMessages - ): void { - $constraint = new PHPUnitConstraint\AllValidationErrorsOccur( - $expectedValidationErrorMessages - ); - - self::assertThat($exception, $constraint); - } - - protected function assertContentItemEquals( - Content $expected, - Content $actual, - string $message - ): void { - $constraint = new PHPUnitConstraint\ContentItemEquals($expected); - - self::assertThat($actual, $constraint, $message); - } - - /** - * Create 'folder' Content. - * - * @param array $names Folder names in the form of ['<language_code>' => '<name>'] - * @param int|null $parentLocationId - * - * @return \eZ\Publish\API\Repository\Values\Content\Content published Content - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function createFolder( - array $names, - ?int $parentLocationId = null, - ?string $remoteId = null, - bool $alwaysAvailable = true - ): Content { - $repository = $this->getRepository(false); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - $locationService = $repository->getLocationService(); - - if (empty($names)) { - throw new \RuntimeException(sprintf('%s expects a non-empty names list', __METHOD__)); - } - $mainLanguageCode = array_keys($names)[0]; - - $struct = $contentService->newContentCreateStruct( - $contentTypeService->loadContentTypeByIdentifier('folder'), - $mainLanguageCode - ); - $struct->remoteId = $remoteId; - $struct->alwaysAvailable = $alwaysAvailable; - foreach ($names as $languageCode => $translatedName) { - $struct->setField('name', $translatedName, $languageCode); - } - - $locationCreateStructList = []; - if (null !== $parentLocationId) { - $locationCreateStructList[] = $locationService->newLocationCreateStruct( - $parentLocationId - ); - } - - $contentDraft = $contentService->createContent( - $struct, - $locationCreateStructList - ); - - return $contentService->publishVersion($contentDraft->versionInfo); - } - - /** - * Update 'folder' Content. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param array $names Folder names in the form of ['<language_code>' => '<name>'] - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - protected function updateFolder(Content $content, array $names) - { - $repository = $this->getRepository(false); - $contentService = $repository->getContentService(); - - $draft = $contentService->createContentDraft($content->contentInfo); - $struct = $contentService->newContentUpdateStruct(); - $struct->initialLanguageCode = array_keys($names)[0]; - - foreach ($names as $languageCode => $translatedName) { - $struct->setField('name', $translatedName, $languageCode); - } - - return $contentService->updateContent($draft->versionInfo, $struct); - } - - /** - * Add new Language to the Repository. - * - * @param string $languageCode - * @param string $name - * @param bool $enabled - * - * @return \eZ\Publish\API\Repository\Values\Content\Language - */ - protected function createLanguage(string $languageCode, string $name, bool $enabled = true): Language - { - $repository = $this->getRepository(false); - - $languageService = $repository->getContentLanguageService(); - $languageStruct = $languageService->newLanguageCreateStruct(); - $languageStruct->name = $name; - $languageStruct->languageCode = $languageCode; - $languageStruct->enabled = $enabled; - - return $languageService->createLanguage($languageStruct); - } - - protected function createLanguageIfNotExists( - string $languageCode, - string $name, - bool $enabled = true - ): Language { - $repository = $this->getRepository(false); - - try { - return $repository->getContentLanguageService()->loadLanguage($languageCode); - } catch (NotFoundException $e) { - return $this->createLanguage($languageCode, $name, $enabled); - } - } - - /** - * @param string $identifier Content Type identifier - * @param string $mainTranslation main translation language code - * @param array $fieldsToDefine a map of field definition identifiers to their field type identifiers - * @param bool $alwaysAvailable default always available - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - protected function createSimpleContentType( - string $identifier, - string $mainTranslation, - array $fieldsToDefine, - bool $alwaysAvailable = true - ): ContentType { - $contentTypeService = $this->getRepository(false)->getContentTypeService(); - $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct($identifier); - $contentTypeCreateStruct->mainLanguageCode = $mainTranslation; - $contentTypeCreateStruct->names = [$mainTranslation => $identifier]; - $contentTypeCreateStruct->defaultAlwaysAvailable = $alwaysAvailable; - foreach ($fieldsToDefine as $fieldDefinitionIdentifier => $fieldTypeIdentifier) { - $fieldDefinitionCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( - $fieldDefinitionIdentifier, - $fieldTypeIdentifier - ); - $contentTypeCreateStruct->addFieldDefinition($fieldDefinitionCreateStruct); - } - $contentTypeService->publishContentTypeDraft( - $contentTypeService->createContentType( - $contentTypeCreateStruct, - [$contentTypeService->loadContentTypeGroupByIdentifier('Content')] - ) - ); - - return $contentTypeService->loadContentTypeByIdentifier($identifier); - } - - protected function loginAsUser(UserReference $user): void - { - $this->getRepository(false)->getPermissionResolver()->setCurrentUserReference($user); - } -} diff --git a/eZ/Publish/API/Repository/Tests/BookmarkServiceTest.php b/eZ/Publish/API/Repository/Tests/BookmarkServiceTest.php deleted file mode 100644 index 63fc258adc..0000000000 --- a/eZ/Publish/API/Repository/Tests/BookmarkServiceTest.php +++ /dev/null @@ -1,152 +0,0 @@ -getRepository(); - - /* BEGIN: Use Case */ - $location = $repository->getLocationService()->loadLocation($this->generateId('location', self::LOCATION_ID_BOOKMARKED)); - $isBookmarked = $repository->getBookmarkService()->isBookmarked($location); - /* END: Use Case */ - - $this->assertTrue($isBookmarked); - } - - /** - * @covers \eZ\Publish\API\Repository\BookmarkService::isBookmarked - */ - public function testIsNotBookmarked() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $location = $repository->getLocationService()->loadLocation($this->generateId('location', self::LOCATION_ID_NOT_BOOKMARKED)); - $isBookmarked = $repository->getBookmarkService()->isBookmarked($location); - /* END: Use Case */ - - $this->assertFalse($isBookmarked); - } - - /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::createBookmark - */ - public function testCreateBookmark() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $bookmarkService = $repository->getBookmarkService(); - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation($this->generateId('location', self::LOCATION_ID_NOT_BOOKMARKED)); - $beforeCreateBookmark = $bookmarkService->isBookmarked($location); - $bookmarkService->createBookmark($location); - $afterCreateBookmark = $bookmarkService->isBookmarked($location); - /* END: Use Case */ - - $this->assertFalse($beforeCreateBookmark); - $this->assertTrue($afterCreateBookmark); - } - - /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::createBookmark - * @depends testCreateBookmark - */ - public function testCreateBookmarkThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $bookmarkService = $repository->getBookmarkService(); - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation($this->generateId('location', self::LOCATION_ID_BOOKMARKED)); - $bookmarkService->createBookmark($location); - /* END: Use Case */ - } - - /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::deleteBookmark - */ - public function testDeleteBookmark() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $bookmarkService = $repository->getBookmarkService(); - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation($this->generateId('location', self::LOCATION_ID_BOOKMARKED)); - - $beforeDeleteBookmark = $bookmarkService->isBookmarked($location); - $bookmarkService->deleteBookmark($location); - $afterDeleteBookmark = $bookmarkService->isBookmarked($location); - /* END: Use Case */ - - $this->assertTrue($beforeDeleteBookmark); - $this->assertFalse($afterDeleteBookmark); - } - - /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::deleteBookmark - * @depends testDeleteBookmark - */ - public function testDeleteBookmarkThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $bookmarkService = $repository->getBookmarkService(); - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation($this->generateId('location', self::LOCATION_ID_NOT_BOOKMARKED)); - $bookmarkService->deleteBookmark($location); - /* END: Use Case */ - } - - /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::loadBookmarks - */ - public function testLoadBookmarks() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $bookmarks = $repository->getBookmarkService()->loadBookmarks(1, 3); - /* END: Use Case */ - - $this->assertInstanceOf(BookmarkList::class, $bookmarks); - $this->assertEquals($bookmarks->totalCount, 5); - // Assert bookmarks order: recently added should be first - $this->assertEquals([15, 13, 12], array_map(static function ($location) { - return $location->id; - }, $bookmarks->items)); - } -} diff --git a/eZ/Publish/API/Repository/Tests/Common/SlugConverter.php b/eZ/Publish/API/Repository/Tests/Common/SlugConverter.php deleted file mode 100644 index ae23536d23..0000000000 --- a/eZ/Publish/API/Repository/Tests/Common/SlugConverter.php +++ /dev/null @@ -1,28 +0,0 @@ -configuration[$key] = $value; - } -} diff --git a/eZ/Publish/API/Repository/Tests/ContentServiceTest.php b/eZ/Publish/API/Repository/Tests/ContentServiceTest.php deleted file mode 100644 index f20e2e7a12..0000000000 --- a/eZ/Publish/API/Repository/Tests/ContentServiceTest.php +++ /dev/null @@ -1,6688 +0,0 @@ -getRepository(); - $this->permissionResolver = $repository->getPermissionResolver(); - $this->contentService = $repository->getContentService(); - $this->locationService = $repository->getLocationService(); - } - - /** - * Test for the newContentCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\ContentService::newContentCreateStruct() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifier - * @group user - * @group field-type - */ - public function testNewContentCreateStruct() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); - - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - - $this->assertInstanceOf(ContentCreateStruct::class, $contentCreate); - } - - /** - * Test for the createContent() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentCreateStruct - * @group user - * @group field-type - */ - public function testCreateContent() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); - - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - $contentCreate->setField('name', 'My awesome forum'); - - $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789'; - $contentCreate->alwaysAvailable = true; - - $content = $this->contentService->createContent($contentCreate); - - $this->assertInstanceOf(Content::class, $content); - - return $content; - } - - /** - * Test for the createContent() method. - * - * Tests made for issue #EZP-20955 where Anonymous user is granted access to create content - * and should have access to do that. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentCreateStruct - * @group user - * @group field-type - */ - public function testCreateContentAndPublishWithPrivilegedAnonymousUser() - { - $anonymousUserId = $this->generateId('user', 10); - - $repository = $this->getRepository(); - $contentTypeService = $this->getRepository()->getContentTypeService(); - $roleService = $repository->getRoleService(); - - // Give Anonymous user role additional rights - $role = $roleService->loadRoleByIdentifier('Anonymous'); - $roleDraft = $roleService->createRoleDraft($role); - $policyCreateStruct = $roleService->newPolicyCreateStruct('content', 'create'); - $policyCreateStruct->addLimitation(new SectionLimitation(['limitationValues' => [1]])); - $policyCreateStruct->addLimitation(new LocationLimitation(['limitationValues' => [2]])); - $policyCreateStruct->addLimitation(new ContentTypeLimitation(['limitationValues' => [1]])); - $roleDraft = $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct); - - $policyCreateStruct = $roleService->newPolicyCreateStruct('content', 'publish'); - $policyCreateStruct->addLimitation(new SectionLimitation(['limitationValues' => [1]])); - $policyCreateStruct->addLimitation(new LocationLimitation(['limitationValues' => [2]])); - $policyCreateStruct->addLimitation(new ContentTypeLimitation(['limitationValues' => [1]])); - $roleDraft = $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct); - $roleService->publishRoleDraft($roleDraft); - - // Set Anonymous user as current - $repository->getPermissionResolver()->setCurrentUserReference($repository->getUserService()->loadUser($anonymousUserId)); - - // Create a new content object: - $contentCreate = $this->contentService->newContentCreateStruct( - $contentTypeService->loadContentTypeByIdentifier('folder'), - self::ENG_GB - ); - - $contentCreate->setField('name', 'Folder 1'); - - $content = $this->contentService->createContent( - $contentCreate, - [$this->locationService->newLocationCreateStruct(2)] - ); - - $this->contentService->publishVersion( - $content->getVersionInfo() - ); - } - - /** - * Test for the createContent() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - */ - public function testCreateContentSetsContentInfo($content) - { - $this->assertInstanceOf(ContentInfo::class, $content->contentInfo); - - return $content; - } - - /** - * Test for the createContent() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentSetsContentInfo - */ - public function testCreateContentSetsExpectedContentInfo($content) - { - $permissionResolver = $this->getRepository()->getPermissionResolver(); - - $this->assertEquals( - [ - $content->id, - 28, // id of content type "forum" - true, - 1, - 'abcdef0123456789abcdef0123456789', - self::ENG_US, - $permissionResolver->getCurrentUserReference()->getUserId(), - false, - null, - // Main Location id for unpublished Content should be null - null, - ], - [ - $content->contentInfo->id, - $content->contentInfo->contentTypeId, - $content->contentInfo->alwaysAvailable, - $content->contentInfo->currentVersionNo, - $content->contentInfo->remoteId, - $content->contentInfo->mainLanguageCode, - $content->contentInfo->ownerId, - $content->contentInfo->published, - $content->contentInfo->publishedDate, - $content->contentInfo->mainLocationId, - ] - ); - } - - /** - * Test for the createContent() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - */ - public function testCreateContentSetsVersionInfo($content) - { - $this->assertInstanceOf(VersionInfo::class, $content->getVersionInfo()); - - return $content; - } - - /** - * Test for the createContent() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentSetsVersionInfo - */ - public function testCreateContentSetsExpectedVersionInfo($content) - { - $currentUserReference = $this->getRepository()->getPermissionResolver()->getCurrentUserReference(); - - $this->assertEquals( - [ - 'status' => VersionInfo::STATUS_DRAFT, - 'versionNo' => 1, - 'creatorId' => $currentUserReference->getUserId(), - 'initialLanguageCode' => self::ENG_US, - ], - [ - 'status' => $content->getVersionInfo()->status, - 'versionNo' => $content->getVersionInfo()->versionNo, - 'creatorId' => $content->getVersionInfo()->creatorId, - 'initialLanguageCode' => $content->getVersionInfo()->initialLanguageCode, - ] - ); - $this->assertTrue($content->getVersionInfo()->isDraft()); - $this->assertFalse($content->getVersionInfo()->isPublished()); - $this->assertFalse($content->getVersionInfo()->isArchived()); - } - - /** - * Test for the createContent() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends testCreateContent - */ - public function testCreateContentSetsExpectedContentType($content) - { - $contentType = $content->getContentType(); - - $this->assertEquals( - [ - $contentType->id, - // Won't match as it's set to true in createContentDraftVersion1() - //$contentType->defaultAlwaysAvailable, - //$contentType->defaultSortField, - //$contentType->defaultSortOrder, - ], - [ - $content->contentInfo->contentTypeId, - //$content->contentInfo->alwaysAvailable, - //$location->sortField, - //$location->sortOrder, - ] - ); - } - - /** - * Test for the createContent() method with utilizing ContentType default options. - * - * @covers \eZ\Publish\API\Repository\ContentService::createContent - */ - public function testCreateContentWithContentTypeDefaultOptions(): void - { - $contentType = $this->getRepository()->getContentTypeService() - ->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); - - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_GB); - $contentCreate->setField('name', 'Sorting Test'); - - $content = $this->contentService->createContent( - $contentCreate, - [$this->locationService->newLocationCreateStruct(2)] - ); - $publishedContent = $this->contentService->publishVersion($content->getVersionInfo()); - - $location = $publishedContent->contentInfo->getMainLocation(); - - $this->assertEquals($contentType->defaultSortField, $location->sortField); - $this->assertEquals($contentType->defaultSortOrder, $location->sortOrder); - $this->assertEquals($contentType->defaultAlwaysAvailable, $publishedContent->contentInfo->alwaysAvailable); - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - */ - public function testCreateContentThrowsInvalidArgumentException() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); - - $contentCreate1 = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - $contentCreate1->setField('name', 'An awesome Sidelfingen forum'); - - $contentCreate1->remoteId = 'abcdef0123456789abcdef0123456789'; - $contentCreate1->alwaysAvailable = true; - - $draft = $this->contentService->createContent($contentCreate1); - $this->contentService->publishVersion($draft->versionInfo); - - $contentCreate2 = $this->contentService->newContentCreateStruct($contentType, self::ENG_GB); - $contentCreate2->setField('name', 'An awesome Bielefeld forum'); - - $contentCreate2->remoteId = 'abcdef0123456789abcdef0123456789'; - $contentCreate2->alwaysAvailable = false; - - $this->expectException(APIInvalidArgumentException::class); - $this->contentService->createContent($contentCreate2); - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - */ - public function testCreateContentThrowsInvalidArgumentExceptionOnFieldTypeNotAccept() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); - - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - // The name field does only accept strings and null as its values - $contentCreate->setField('name', new \stdClass()); - - $this->expectException(APIInvalidArgumentException::class); - $this->contentService->createContent($contentCreate); - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - */ - public function testCreateContentThrowsContentFieldValidationException() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); - - $contentCreate1 = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - $contentCreate1->setField('name', 'An awesome Sidelfingen folder'); - // Violates string length constraint - $contentCreate1->setField('short_name', str_repeat('a', 200)); - - $this->expectException(ContentFieldValidationException::class); - - // Throws ContentFieldValidationException, since short_name does not pass validation of the string length validator - $this->contentService->createContent($contentCreate1); - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - */ - public function testCreateContentRequiredFieldMissing() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); - - $contentCreate1 = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - // Required field "name" is not set - - $this->expectException(ContentFieldValidationException::class); - - // Throws a ContentFieldValidationException, since a required field is missing - $this->contentService->createContent($contentCreate1); - } - - /** - * Test for the createContent() method. - * - * NOTE: We have bidirectional dependencies between the ContentService and - * the LocationService, so that we cannot use PHPUnit's test dependencies - * here. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs) - * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testCreateLocation - * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationByRemoteId - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @group user - */ - public function testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately() - { - $this->createContentDraftVersion1(); - - $this->expectException(NotFoundException::class); - - // The location will not have been created, yet, so this throws an exception - $this->locationService->loadLocationByRemoteId('0123456789abcdef0123456789abcdef'); - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately - */ - public function testCreateContentThrowsInvalidArgumentExceptionWithLocationCreateParameter() - { - $parentLocationId = $this->generateId('location', 56); - // $parentLocationId is a valid location ID - - $contentTypeService = $this->getRepository()->getContentTypeService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); - - // Configure new locations - $locationCreate1 = $this->locationService->newLocationCreateStruct($parentLocationId); - - $locationCreate1->priority = 23; - $locationCreate1->hidden = true; - $locationCreate1->remoteId = '0123456789abcdef0123456789aaaaaa'; - $locationCreate1->sortField = Location::SORT_FIELD_NODE_ID; - $locationCreate1->sortOrder = Location::SORT_ORDER_DESC; - - $locationCreate2 = $this->locationService->newLocationCreateStruct($parentLocationId); - - $locationCreate2->priority = 42; - $locationCreate2->hidden = true; - $locationCreate2->remoteId = '0123456789abcdef0123456789bbbbbb'; - $locationCreate2->sortField = Location::SORT_FIELD_NODE_ID; - $locationCreate2->sortOrder = Location::SORT_ORDER_DESC; - - // Configure new content object - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - - $contentCreate->setField('name', 'A awesome Sindelfingen forum'); - $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789'; - $contentCreate->alwaysAvailable = true; - - // Create new content object under the specified location - $draft = $this->contentService->createContent( - $contentCreate, - [$locationCreate1] - ); - $this->contentService->publishVersion($draft->versionInfo); - - $this->expectException(APIInvalidArgumentException::class); - // Content remoteId already exists, - $this->contentService->createContent( - $contentCreate, - [$locationCreate2] - ); - } - - /** - * Test for the loadContentInfo() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentInfo() - * @group user - */ - public function testLoadContentInfo() - { - $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); - - // Load the ContentInfo for "Media" folder - $contentInfo = $this->contentService->loadContentInfo($mediaFolderId); - - $this->assertInstanceOf(ContentInfo::class, $contentInfo); - - return $contentInfo; - } - - /** - * Test for the returned value of the loadContentInfo() method. - * - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - * @covers \eZ\Publish\API\Repository\ContentService::loadContentInfo - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - */ - public function testLoadContentInfoSetsExpectedContentInfo(ContentInfo $contentInfo) - { - $this->assertPropertiesCorrectUnsorted( - $this->getExpectedMediaContentInfoProperties(), - $contentInfo - ); - } - - /** - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - * @covers \eZ\Publish\API\Repository\ContentService::loadContentInfo - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - */ - public function testLoadContentInfoGetContentType(ContentInfo $contentInfo): void - { - $contentType = $contentInfo->getContentType(); - - $this->assertInstanceOf(ContentType::class, $contentType); - $this->assertEquals('folder', $contentType->identifier); - } - - /** - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - * @covers \eZ\Publish\API\Repository\ContentService::loadContentInfo - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - */ - public function testLoadContentInfoGetSection(ContentInfo $contentInfo): void - { - $section = $contentInfo->getSection(); - - $this->assertInstanceOf(Section::class, $section); - $this->assertEquals('media', $section->identifier); - } - - /** - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - * @covers \eZ\Publish\API\Repository\ContentService::loadContentInfo - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - */ - public function testLoadContentInfoGetMainLanguage(ContentInfo $contentInfo): void - { - $language = $contentInfo->getMainLanguage(); - - $this->assertInstanceOf(Language::class, $language); - $this->assertEquals('eng-US', $language->languageCode); - } - - /** - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - * @covers \eZ\Publish\API\Repository\ContentService::loadContentInfo - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - */ - public function testLoadContentInfoGetMainLocation(ContentInfo $contentInfo): void - { - $mainLocation = $contentInfo->getMainLocation(); - - $this->assertInstanceOf(Location::class, $mainLocation); - $this->assertEquals('75c715a51699d2d309a924eca6a95145', $mainLocation->remoteId); - } - - /** - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - * @covers \eZ\Publish\API\Repository\ContentService::loadContentInfo - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - */ - public function testLoadContentInfoSetsExpectedOwnerProxy(ContentInfo $contentInfo): void - { - $owner = $contentInfo->getOwner(); - - $this->assertInstanceOf(User::class, $owner); - $this->assertEquals('Administrator User', $owner->getName()); - } - - /** - * Test for the loadContentInfo() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentInfo() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - */ - public function testLoadContentInfoThrowsNotFoundException() - { - $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX); - - $this->expectException(NotFoundException::class); - - $this->contentService->loadContentInfo($nonExistentContentId); - } - - /** - * Test for the loadContentInfoList() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoList() - */ - public function testLoadContentInfoList() - { - $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); - $list = $this->contentService->loadContentInfoList([$mediaFolderId]); - - $this->assertCount(1, $list); - $this->assertEquals([$mediaFolderId], array_keys($list), 'Array key was not content id'); - $this->assertInstanceOf( - ContentInfo::class, - $list[$mediaFolderId] - ); - } - - /** - * Test for the loadContentInfoList() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoList() - * @depends testLoadContentInfoList - */ - public function testLoadContentInfoListSkipsNotFoundItems() - { - $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX); - $list = $this->contentService->loadContentInfoList([$nonExistentContentId]); - - $this->assertCount(0, $list); - } - - /** - * Test for the loadContentInfoByRemoteId() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoByRemoteId() - */ - public function testLoadContentInfoByRemoteId() - { - // Load the ContentInfo for "Media" folder - $contentInfo = $this->contentService->loadContentInfoByRemoteId('faaeb9be3bd98ed09f606fc16d144eca'); - - $this->assertInstanceOf(ContentInfo::class, $contentInfo); - - return $contentInfo; - } - - /** - * Test for the returned value of the loadContentInfoByRemoteId() method. - * - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfoByRemoteId - * @covers \eZ\Publish\API\Repository\ContentService::loadContentInfoByRemoteId - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - */ - public function testLoadContentInfoByRemoteIdSetsExpectedContentInfo(ContentInfo $contentInfo) - { - $this->assertPropertiesCorrectUnsorted( - [ - 'id' => 10, - 'contentTypeId' => 4, - 'name' => 'Anonymous User', - 'sectionId' => 2, - 'currentVersionNo' => 2, - 'published' => true, - 'ownerId' => 14, - 'modificationDate' => $this->createDateTime(1072180405), - 'publishedDate' => $this->createDateTime(1033920665), - 'alwaysAvailable' => 1, - 'remoteId' => 'faaeb9be3bd98ed09f606fc16d144eca', - 'mainLanguageCode' => self::ENG_US, - 'mainLocationId' => 45, - ], - $contentInfo - ); - } - - /** - * Test for the loadContentInfoByRemoteId() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoByRemoteId() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfoByRemoteId - */ - public function testLoadContentInfoByRemoteIdThrowsNotFoundException() - { - $this->expectException(NotFoundException::class); - - $this->contentService->loadContentInfoByRemoteId('abcdefghijklmnopqrstuvwxyz0123456789'); - } - - /** - * Test for the loadVersionInfo() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfo() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - * @group user - */ - public function testLoadVersionInfo() - { - $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); - // $mediaFolderId contains the ID of the "Media" folder - - // Load the ContentInfo for "Media" folder - $contentInfo = $this->contentService->loadContentInfo($mediaFolderId); - - // Now load the current version info of the "Media" folder - $versionInfo = $this->contentService->loadVersionInfo($contentInfo); - - $this->assertInstanceOf( - VersionInfo::class, - $versionInfo - ); - } - - /** - * Test for the loadVersionInfoById() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById() - */ - public function testLoadVersionInfoById() - { - $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); - // $mediaFolderId contains the ID of the "Media" folder - - // Load the VersionInfo for "Media" folder - $versionInfo = $this->contentService->loadVersionInfoById($mediaFolderId); - - $this->assertInstanceOf( - VersionInfo::class, - $versionInfo - ); - - return $versionInfo; - } - - /** - * Test for the returned value of the loadVersionInfoById() method. - * - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoById - * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - */ - public function testLoadVersionInfoByIdSetsExpectedVersionInfo(VersionInfo $versionInfo) - { - $this->assertPropertiesCorrect( - [ - 'names' => [ - self::ENG_US => 'Media', - ], - 'contentInfo' => new ContentInfo($this->getExpectedMediaContentInfoProperties()), - 'id' => 472, - 'versionNo' => 1, - 'modificationDate' => $this->createDateTime(1060695457), - 'creatorId' => 14, - 'creationDate' => $this->createDateTime(1060695450), - 'status' => VersionInfo::STATUS_PUBLISHED, - 'initialLanguageCode' => self::ENG_US, - 'languageCodes' => [ - self::ENG_US, - ], - ], - $versionInfo - ); - $this->assertTrue($versionInfo->isPublished()); - $this->assertFalse($versionInfo->isDraft()); - $this->assertFalse($versionInfo->isArchived()); - } - - /** - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoById - * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - */ - public function testLoadVersionInfoByIdGetCreator(VersionInfo $versionInfo): void - { - $creator = $versionInfo->getCreator(); - - $this->assertInstanceOf(User::class, $creator); - $this->assertEquals('Administrator User', $creator->getName()); - } - - /** - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoById - * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - */ - public function testLoadVersionInfoByIdGetInitialLanguage(VersionInfo $versionInfo): void - { - $initialLanguage = $versionInfo->getInitialLanguage(); - - $this->assertInstanceOf(Language::class, $initialLanguage); - $this->assertEquals('eng-US', $initialLanguage->languageCode); - } - - /** - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoById - * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - */ - public function testLoadVersionInfoByIdGetLanguages(VersionInfo $versionInfo): void - { - $actualLanguages = $versionInfo->getLanguages(); - - $expectedLanguages = ['eng-US']; - foreach ($expectedLanguages as $i => $expectedLanguage) { - $this->assertEquals($expectedLanguage, $actualLanguages[$i]->languageCode); - } - } - - /** - * Test for the loadVersionInfoById() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoById - */ - public function testLoadVersionInfoByIdThrowsNotFoundException() - { - $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX); - - $this->expectException(NotFoundException::class); - - $this->contentService->loadVersionInfoById($nonExistentContentId); - } - - /** - * Test for the loadContentByContentInfo() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - */ - public function testLoadContentByContentInfo() - { - $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); - // $mediaFolderId contains the ID of the "Media" folder - - // Load the ContentInfo for "Media" folder - $contentInfo = $this->contentService->loadContentInfo($mediaFolderId); - - // Now load the current content version for the info instance - $content = $this->contentService->loadContentByContentInfo($contentInfo); - - $this->assertInstanceOf( - Content::class, - $content - ); - } - - /** - * Test for the loadContentByVersionInfo() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByVersionInfo() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfo - */ - public function testLoadContentByVersionInfo() - { - $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); - // $mediaFolderId contains the ID of the "Media" folder - - // Load the ContentInfo for "Media" folder - $contentInfo = $this->contentService->loadContentInfo($mediaFolderId); - - // Load the current VersionInfo - $versionInfo = $this->contentService->loadVersionInfo($contentInfo); - - // Now load the current content version for the info instance - $content = $this->contentService->loadContentByVersionInfo($versionInfo); - - $this->assertInstanceOf( - Content::class, - $content - ); - } - - /** - * Test for the loadContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContent() - * @group user - * @group field-type - */ - public function testLoadContent() - { - $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); - // $mediaFolderId contains the ID of the "Media" folder - - // Load the Content for "Media" folder, any language and current version - $content = $this->contentService->loadContent($mediaFolderId); - - $this->assertInstanceOf( - Content::class, - $content - ); - } - - /** - * Test for the loadContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent - */ - public function testLoadContentThrowsNotFoundException() - { - $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX); - - $this->expectException(NotFoundException::class); - - $this->contentService->loadContent($nonExistentContentId); - } - - /** - * Data provider for testLoadContentByRemoteId(). - * - * @return array - */ - public function contentRemoteIdVersionLanguageProvider() - { - return [ - ['f5c88a2209584891056f987fd965b0ba', null, null], - ['f5c88a2209584891056f987fd965b0ba', [self::ENG_US], null], - ['f5c88a2209584891056f987fd965b0ba', null, 1], - ['f5c88a2209584891056f987fd965b0ba', [self::ENG_US], 1], - [self::MEDIA_REMOTE_ID, null, null], - [self::MEDIA_REMOTE_ID, [self::ENG_US], null], - [self::MEDIA_REMOTE_ID, null, 1], - [self::MEDIA_REMOTE_ID, [self::ENG_US], 1], - ]; - } - - /** - * Test for the loadContentByRemoteId() method. - * - * @covers \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId - * @dataProvider contentRemoteIdVersionLanguageProvider - * - * @param string $remoteId - * @param array|null $languages - * @param int $versionNo - */ - public function testLoadContentByRemoteId($remoteId, $languages, $versionNo) - { - $content = $this->contentService->loadContentByRemoteId($remoteId, $languages, $versionNo); - - $this->assertInstanceOf( - Content::class, - $content - ); - - $this->assertEquals($remoteId, $content->contentInfo->remoteId); - if ($languages !== null) { - $this->assertEquals($languages, $content->getVersionInfo()->languageCodes); - } - $this->assertEquals($versionNo ?: 1, $content->getVersionInfo()->versionNo); - } - - /** - * Test for the loadContentByRemoteId() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByRemoteId - */ - public function testLoadContentByRemoteIdThrowsNotFoundException() - { - $this->expectException(NotFoundException::class); - - // This call will fail with a "NotFoundException", because no content object exists for the given remoteId - $this->contentService->loadContentByRemoteId('a1b1c1d1e1f1a2b2c2d2e2f2a3b3c3d3'); - } - - /** - * Test for the publishVersion() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfo - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately - * @group user - * @group field-type - */ - public function testPublishVersion() - { - $time = time(); - $content = $this->createContentVersion1(); - - $this->assertInstanceOf(Content::class, $content); - $this->assertTrue($content->contentInfo->published); - $this->assertEquals(VersionInfo::STATUS_PUBLISHED, $content->versionInfo->status); - $this->assertGreaterThanOrEqual($time, $content->contentInfo->publishedDate->getTimestamp()); - $this->assertGreaterThanOrEqual($time, $content->contentInfo->modificationDate->getTimestamp()); - $this->assertTrue($content->versionInfo->isPublished()); - $this->assertFalse($content->versionInfo->isDraft()); - $this->assertFalse($content->versionInfo->isArchived()); - - return $content; - } - - /** - * Test for the publishVersion() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - */ - public function testPublishVersionSetsExpectedContentInfo($content) - { - $userReference = $this->getRepository()->getPermissionResolver()->getCurrentUserReference(); - - $this->assertEquals( - [ - $content->id, - true, - 1, - 'abcdef0123456789abcdef0123456789', - self::ENG_US, - $userReference->getUserId(), - true, - ], - [ - $content->contentInfo->id, - $content->contentInfo->alwaysAvailable, - $content->contentInfo->currentVersionNo, - $content->contentInfo->remoteId, - $content->contentInfo->mainLanguageCode, - $content->contentInfo->ownerId, - $content->contentInfo->published, - ] - ); - - $this->assertNotNull($content->contentInfo->mainLocationId); - $date = new \DateTime('1984/01/01'); - $this->assertGreaterThan( - $date->getTimestamp(), - $content->contentInfo->publishedDate->getTimestamp() - ); - } - - /** - * Test for the publishVersion() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - */ - public function testPublishVersionSetsExpectedVersionInfo($content) - { - $currentUserReference = $this->getRepository()->getPermissionResolver()->getCurrentUserReference(); - - $this->assertEquals( - [ - $currentUserReference->getUserId(), - self::ENG_US, - VersionInfo::STATUS_PUBLISHED, - 1, - ], - [ - $content->getVersionInfo()->creatorId, - $content->getVersionInfo()->initialLanguageCode, - $content->getVersionInfo()->status, - $content->getVersionInfo()->versionNo, - ] - ); - - $date = new \DateTime('1984/01/01'); - $this->assertGreaterThan( - $date->getTimestamp(), - $content->getVersionInfo()->modificationDate->getTimestamp() - ); - - $this->assertNotNull($content->getVersionInfo()->modificationDate); - $this->assertTrue($content->getVersionInfo()->isPublished()); - $this->assertFalse($content->getVersionInfo()->isDraft()); - $this->assertFalse($content->getVersionInfo()->isArchived()); - } - - /** - * Test for the publishVersion() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends testPublishVersion - */ - public function testPublishVersionSetsExpectedContentType($content) - { - $contentType = $content->getContentType(); - - $this->assertEquals( - [ - $contentType->id, - // won't be a match as it's set to true in createContentDraftVersion1() - //$contentType->defaultAlwaysAvailable, - //$contentType->defaultSortField, - //$contentType->defaultSortOrder, - ], - [ - $content->contentInfo->contentTypeId, - //$content->contentInfo->alwaysAvailable, - //$location->sortField, - //$location->sortOrder, - ] - ); - } - - /** - * Test for the publishVersion() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - */ - public function testPublishVersionCreatesLocationsDefinedOnCreate() - { - $content = $this->createContentVersion1(); - - $location = $this->locationService->loadLocationByRemoteId( - '0123456789abcdef0123456789abcdef' - ); - - $this->assertEquals( - $location->getContentInfo(), - $content->getVersionInfo()->getContentInfo() - ); - - return [$content, $location]; - } - - /** - * Test for the publishVersion() method. - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionCreatesLocationsDefinedOnCreate - */ - public function testCreateContentWithLocationCreateParameterCreatesExpectedLocation(array $testData) - { - /** @var \eZ\Publish\API\Repository\Values\Content\Content $content */ - /** @var \eZ\Publish\API\Repository\Values\Content\Location $location */ - list($content, $location) = $testData; - - $parentLocationId = $this->generateId('location', 56); - $parentLocation = $this->getRepository()->getLocationService()->loadLocation($parentLocationId); - $mainLocationId = $content->getVersionInfo()->getContentInfo()->mainLocationId; - - $this->assertPropertiesCorrect( - [ - 'id' => $mainLocationId, - 'priority' => 23, - 'hidden' => true, - 'invisible' => true, - 'remoteId' => '0123456789abcdef0123456789abcdef', - 'parentLocationId' => $parentLocationId, - 'pathString' => $parentLocation->pathString . $mainLocationId . '/', - 'depth' => $parentLocation->depth + 1, - 'sortField' => Location::SORT_FIELD_NODE_ID, - 'sortOrder' => Location::SORT_ORDER_DESC, - ], - $location - ); - } - - /** - * Test for the publishVersion() method. - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - */ - public function testPublishVersionThrowsBadStateException() - { - $draft = $this->createContentDraftVersion1(); - - // Publish the content draft - $this->contentService->publishVersion($draft->getVersionInfo()); - - $this->expectException(BadStateException::class); - - // This call will fail with a "BadStateException", because the version is already published. - $this->contentService->publishVersion($draft->getVersionInfo()); - } - - /** - * Test that publishVersion() does not affect publishedDate (assuming previous version exists). - * - * @covers \eZ\Publish\API\Repository\ContentService::publishVersion - */ - public function testPublishVersionDoesNotChangePublishedDate() - { - $publishedContent = $this->createContentVersion1(); - - // force timestamps to differ - sleep(1); - - $contentDraft = $this->contentService->createContentDraft($publishedContent->contentInfo); - $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); - $contentUpdateStruct->setField('name', 'New name'); - $contentDraft = $this->contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct); - $republishedContent = $this->contentService->publishVersion($contentDraft->versionInfo); - - $this->assertEquals( - $publishedContent->contentInfo->publishedDate->getTimestamp(), - $republishedContent->contentInfo->publishedDate->getTimestamp() - ); - $this->assertGreaterThan( - $publishedContent->contentInfo->modificationDate->getTimestamp(), - $republishedContent->contentInfo->modificationDate->getTimestamp() - ); - } - - /** - * Test for the createContentDraft() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - * @group user - */ - public function testCreateContentDraft() - { - $content = $this->createContentVersion1(); - - // Now we create a new draft from the published content - $draftedContent = $this->contentService->createContentDraft($content->contentInfo); - - $this->assertInstanceOf( - Content::class, - $draftedContent - ); - - return $draftedContent; - } - - /** - * Test for the createContentDraft() method with given language for new draft. - * - * @covers \eZ\Publish\API\Repository\ContentService::createContentDraft - */ - public function testCreateContentDraftInOtherLanguage() - { - $content = $this->createContentVersion1(); - - $language = $this->getRepository()->getContentLanguageService()->loadLanguage('eng-GB'); - - // Now we create a new draft from the published content - $draftedContent = $this->contentService->createContentDraft( - $content->contentInfo, - null, - null, - $language - ); - - $this->assertEquals('eng-US', $content->versionInfo->initialLanguageCode); - $this->assertEquals('eng-GB', $draftedContent->versionInfo->initialLanguageCode); - } - - /** - * Test for the createContentDraft() method. - * - * Test that editor has access to edit own draft. - * Note: Editors have access to version_read, which is needed to load content drafts. - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - * @group user - */ - public function testCreateContentDraftAndLoadAccess() - { - $user = $this->createUserVersion1(); - - // Set new editor as user - $this->permissionResolver->setCurrentUserReference($user); - - // Create draft - $draft = $this->createContentDraftVersion1(2, 'folder'); - - // Try to load the draft - $loadedDraft = $this->contentService->loadContent($draft->id); - - $this->assertEquals($draft->id, $loadedDraft->id); - } - - /** - * Test for the createContentDraft() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $draft - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft - */ - public function testCreateContentDraftSetsExpectedProperties($draft) - { - $this->assertEquals( - [ - 'fieldCount' => 1, - 'relationCount' => 0, - ], - [ - 'fieldCount' => count($draft->getFields()), - 'relationCount' => count($this->getRepository()->getContentService()->loadRelations($draft->getVersionInfo())), - ] - ); - } - - /** - * Test for the createContentDraft() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $draft - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft - */ - public function testCreateContentDraftSetsContentInfo($draft) - { - $contentInfo = $draft->contentInfo; - $currentUserReference = $this->getRepository()->getPermissionResolver()->getCurrentUserReference(); - - $this->assertEquals( - [ - $draft->id, - true, - 1, - self::ENG_US, - $currentUserReference->getUserId(), - 'abcdef0123456789abcdef0123456789', - 1, - ], - [ - $contentInfo->id, - $contentInfo->alwaysAvailable, - $contentInfo->currentVersionNo, - $contentInfo->mainLanguageCode, - $contentInfo->ownerId, - $contentInfo->remoteId, - $contentInfo->sectionId, - ] - ); - } - - /** - * Test for the createContentDraft() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $draft - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft - */ - public function testCreateContentDraftSetsVersionInfo($draft) - { - $versionInfo = $draft->getVersionInfo(); - $currentUserReference = $this->getRepository()->getPermissionResolver()->getCurrentUserReference(); - - $this->assertEquals( - [ - 'creatorId' => $currentUserReference->getUserId(), - 'initialLanguageCode' => self::ENG_US, - 'languageCodes' => [0 => self::ENG_US], - 'status' => VersionInfo::STATUS_DRAFT, - 'versionNo' => 2, - ], - [ - 'creatorId' => $versionInfo->creatorId, - 'initialLanguageCode' => $versionInfo->initialLanguageCode, - 'languageCodes' => $versionInfo->languageCodes, - 'status' => $versionInfo->status, - 'versionNo' => $versionInfo->versionNo, - ] - ); - $this->assertTrue($versionInfo->isDraft()); - $this->assertFalse($versionInfo->isPublished()); - $this->assertFalse($versionInfo->isArchived()); - } - - /** - * Test for the createContentDraft() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $draft - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfo - */ - public function testCreateContentDraftLoadVersionInfoStillLoadsPublishedVersion($draft) - { - $content = $this->createContentVersion1(); - - // Now we create a new draft from the published content - $this->contentService->createContentDraft($content->contentInfo); - - // This call will still load the published version - $versionInfoPublished = $this->contentService->loadVersionInfo($content->contentInfo); - - $this->assertEquals(1, $versionInfoPublished->versionNo); - } - - /** - * Test for the createContentDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft - */ - public function testCreateContentDraftLoadContentStillLoadsPublishedVersion() - { - $content = $this->createContentVersion1(); - - // Now we create a new draft from the published content - $this->contentService->createContentDraft($content->contentInfo); - - // This call will still load the published content version - $contentPublished = $this->contentService->loadContent($content->id); - - $this->assertEquals(1, $contentPublished->getVersionInfo()->versionNo); - } - - /** - * Test for the createContentDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByRemoteId - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft - */ - public function testCreateContentDraftLoadContentByRemoteIdStillLoadsPublishedVersion() - { - $content = $this->createContentVersion1(); - - // Now we create a new draft from the published content - $this->contentService->createContentDraft($content->contentInfo); - - // This call will still load the published content version - $contentPublished = $this->contentService->loadContentByRemoteId('abcdef0123456789abcdef0123456789'); - - $this->assertEquals(1, $contentPublished->getVersionInfo()->versionNo); - } - - /** - * Test for the createContentDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfo - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft - */ - public function testCreateContentDraftLoadContentByContentInfoStillLoadsPublishedVersion() - { - $content = $this->createContentVersion1(); - - // Now we create a new draft from the published content - $this->contentService->createContentDraft($content->contentInfo); - - // This call will still load the published content version - $contentPublished = $this->contentService->loadContentByContentInfo($content->contentInfo); - - $this->assertEquals(1, $contentPublished->getVersionInfo()->versionNo); - } - - /** - * Test for the newContentUpdateStruct() method. - * - * @covers \eZ\Publish\API\Repository\ContentService::newContentUpdateStruct - * @group user - */ - public function testNewContentUpdateStruct() - { - $updateStruct = $this->contentService->newContentUpdateStruct(); - - $this->assertInstanceOf( - ContentUpdateStruct::class, - $updateStruct - ); - - $this->assertPropertiesCorrect( - [ - 'initialLanguageCode' => null, - 'fields' => [], - ], - $updateStruct - ); - } - - /** - * Test for the updateContent() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentUpdateStruct - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft - * @group user - * @group field-type - */ - public function testUpdateContent() - { - $draftVersion2 = $this->createUpdatedDraftVersion2(); - - $this->assertInstanceOf( - Content::class, - $draftVersion2 - ); - - $this->assertEquals( - $this->generateId('user', 10), - $draftVersion2->versionInfo->creatorId, - 'creatorId is not properly set on new Version' - ); - - return $draftVersion2; - } - - /** - * Test for the updateContent_WithDifferentUser() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentUpdateStruct - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft - * @group user - * @group field-type - */ - public function testUpdateContentWithDifferentUser() - { - $arrayWithDraftVersion2 = $this->createUpdatedDraftVersion2NotAdmin(); - - $this->assertInstanceOf( - Content::class, - $arrayWithDraftVersion2[0] - ); - - $this->assertEquals( - $this->generateId('user', $arrayWithDraftVersion2[1]), - $arrayWithDraftVersion2[0]->versionInfo->creatorId, - 'creatorId is not properly set on new Version' - ); - - return $arrayWithDraftVersion2[0]; - } - - /** - * Test for the updateContent() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent - */ - public function testUpdateContentSetsExpectedFields($content) - { - $actual = $this->normalizeFields($content->getFields()); - - $expected = [ - new Field( - [ - 'id' => 0, - 'value' => true, - 'languageCode' => self::ENG_GB, - 'fieldDefIdentifier' => 'name', - 'fieldTypeIdentifier' => 'ezstring', - ] - ), - new Field( - [ - 'id' => 0, - 'value' => true, - 'languageCode' => self::ENG_US, - 'fieldDefIdentifier' => 'name', - 'fieldTypeIdentifier' => 'ezstring', - ] - ), - ]; - - $this->assertEquals($expected, $actual); - } - - /** - * Test for the updateContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent - */ - public function testUpdateContentThrowsBadStateException() - { - $content = $this->createContentVersion1(); - - // Now create an update struct and modify some fields - $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); - $contentUpdateStruct->setField('name', 'An awesome² story about ezp.'); - $contentUpdateStruct->setField('name', 'An awesome²³ story about ezp.', self::ENG_GB); - - $contentUpdateStruct->initialLanguageCode = self::ENG_US; - - $this->expectException(BadStateException::class); - - // This call will fail with a "BadStateException", because $publishedContent is not a draft. - $this->contentService->updateContent( - $content->getVersionInfo(), - $contentUpdateStruct - ); - } - - /** - * Test for the updateContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent - */ - public function testUpdateContentThrowsInvalidArgumentExceptionWhenFieldTypeDoesNotAccept() - { - $draft = $this->createContentDraftVersion1(); - - // Now create an update struct and modify some fields - $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); - // The name field does not accept a stdClass object as its input - $contentUpdateStruct->setField('name', new \stdClass(), self::ENG_US); - - $this->expectException(APIInvalidArgumentException::class); - // is not accepted - $this->contentService->updateContent( - $draft->getVersionInfo(), - $contentUpdateStruct - ); - } - - /** - * Test for the updateContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent - */ - public function testUpdateContentWhenMandatoryFieldIsEmpty() - { - $draft = $this->createContentDraftVersion1(); - - // Now create an update struct and set a mandatory field to null - $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); - $contentUpdateStruct->setField('name', null); - - // Don't set this, then the above call without languageCode will fail - $contentUpdateStruct->initialLanguageCode = self::ENG_US; - - $this->expectException(ContentFieldValidationException::class); - - // This call will fail with a "ContentFieldValidationException", because the mandatory "name" field is empty. - $this->contentService->updateContent( - $draft->getVersionInfo(), - $contentUpdateStruct - ); - } - - /** - * Test for the updateContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent - */ - public function testUpdateContentThrowsContentFieldValidationException() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); - - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - $contentCreate->setField('name', 'An awesome Sidelfingen folder'); - - $draft = $this->contentService->createContent($contentCreate); - - $contentUpdate = $this->contentService->newContentUpdateStruct(); - // Violates string length constraint - $contentUpdate->setField('short_name', str_repeat('a', 200), self::ENG_US); - - $this->expectException(ContentFieldValidationException::class); - - // Throws ContentFieldValidationException because the string length validation of the field "short_name" fails - $this->contentService->updateContent($draft->getVersionInfo(), $contentUpdate); - } - - /** - * Test for the updateContent() method. - * - * @covers \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent - */ - public function testUpdateContentValidatorIgnoresRequiredFieldsOfNotUpdatedLanguages() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); - - // Create multilangual content - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - $contentCreate->setField('name', 'An awesome Sidelfingen folder', self::ENG_US); - $contentCreate->setField('name', 'An awesome Sidelfingen folder', self::ENG_GB); - - $contentDraft = $this->contentService->createContent($contentCreate); - - // 2. Update content type definition - $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); - - $fieldDefinition = $contentType->getFieldDefinition('short_name'); - $fieldDefinitionUpdate = $contentTypeService->newFieldDefinitionUpdateStruct(); - $fieldDefinitionUpdate->identifier = 'short_name'; - $fieldDefinitionUpdate->isRequired = true; - - $contentTypeService->updateFieldDefinition( - $contentTypeDraft, - $fieldDefinition, - $fieldDefinitionUpdate - ); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - // 3. Update only eng-US translation - $contentUpdate = $this->contentService->newContentUpdateStruct(); - $contentUpdate->setField('name', 'An awesome Sidelfingen folder (updated)', self::ENG_US); - $contentUpdate->setField('short_name', 'Lorem ipsum dolor'); - - $this->contentService->updateContent($contentDraft->getVersionInfo(), $contentUpdate); - } - - /** - * Test for the updateContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent - */ - public function testUpdateContentWithNotUpdatingMandatoryField() - { - $draft = $this->createContentDraftVersion1(); - - // Now create an update struct which does not overwrite mandatory - // fields - $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); - - // Don't set this, then the above call without languageCode will fail - $contentUpdateStruct->initialLanguageCode = self::ENG_US; - - // This will only update the "description" field in the "eng-US" language - $updatedDraft = $this->contentService->updateContent( - $draft->getVersionInfo(), - $contentUpdateStruct - ); - - foreach ($updatedDraft->getFields() as $field) { - if ($field->languageCode === self::ENG_US && $field->fieldDefIdentifier === 'name' && $field->value !== null) { - // Found field - return; - } - } - $this->fail( - 'Field with identifier "name" in language "eng-US" could not be found or has empty value.' - ); - } - - /** - * Test for the createContentDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft($contentInfo, $versionInfo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent - */ - public function testCreateContentDraftWithSecondParameter() - { - $contentVersion2 = $this->createContentVersion2(); - - // Now we create a new draft from the initial version - $draftedContentReloaded = $this->contentService->createContentDraft( - $contentVersion2->contentInfo, - $contentVersion2->getVersionInfo() - ); - - $this->assertEquals(3, $draftedContentReloaded->getVersionInfo()->versionNo); - } - - /** - * Test for the createContentDraft() method with third parameter. - * - * @covers \eZ\Publish\Core\Repository\ContentService::createContentDraft - */ - public function testCreateContentDraftWithThirdParameter() - { - $content = $this->contentService->loadContent(4); - $user = $this->createUserVersion1(); - - $draftContent = $this->contentService->createContentDraft( - $content->contentInfo, - $content->getVersionInfo(), - $user - ); - - $this->assertInstanceOf( - Content::class, - $draftContent - ); - } - - /** - * Test for the publishVersion() method. - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent - */ - public function testPublishVersionFromContentDraft() - { - $contentVersion2 = $this->createContentVersion2(); - - $versionInfo = $this->contentService->loadVersionInfo($contentVersion2->contentInfo); - - $this->assertEquals( - [ - 'status' => VersionInfo::STATUS_PUBLISHED, - 'versionNo' => 2, - ], - [ - 'status' => $versionInfo->status, - 'versionNo' => $versionInfo->versionNo, - ] - ); - $this->assertTrue($versionInfo->isPublished()); - $this->assertFalse($versionInfo->isDraft()); - $this->assertFalse($versionInfo->isArchived()); - } - - /** - * Test for the publishVersion() method. - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - */ - public function testPublishVersionFromContentDraftArchivesOldVersion() - { - $contentVersion2 = $this->createContentVersion2(); - - $versionInfo = $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 1); - - $this->assertEquals( - [ - 'status' => VersionInfo::STATUS_ARCHIVED, - 'versionNo' => 1, - ], - [ - 'status' => $versionInfo->status, - 'versionNo' => $versionInfo->versionNo, - ] - ); - $this->assertTrue($versionInfo->isArchived()); - $this->assertFalse($versionInfo->isDraft()); - $this->assertFalse($versionInfo->isPublished()); - } - - /** - * Test for the publishVersion() method. - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - */ - public function testPublishVersionFromContentDraftUpdatesContentInfoCurrentVersion() - { - $contentVersion2 = $this->createContentVersion2(); - - $this->assertEquals(2, $contentVersion2->contentInfo->currentVersionNo); - } - - /** - * Test for the publishVersion() method. - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - */ - public function testPublishVersionFromOldContentDraftArchivesNewerVersionNo() - { - $content = $this->createContentVersion1(); - - // Create a new draft with versionNo = 2 - $draftedContentVersion2 = $this->contentService->createContentDraft($content->contentInfo); - - // Create another new draft with versionNo = 3 - $draftedContentVersion3 = $this->contentService->createContentDraft($content->contentInfo); - - // Publish draft with versionNo = 3 - $this->contentService->publishVersion($draftedContentVersion3->getVersionInfo()); - - // Publish the first draft with versionNo = 2 - // currentVersionNo is now 2, versionNo 3 will be archived - $publishedDraft = $this->contentService->publishVersion($draftedContentVersion2->getVersionInfo()); - - $this->assertEquals(2, $publishedDraft->contentInfo->currentVersionNo); - } - - /** - * Test for the publishVersion() method, and that it creates limited archives. - * - * @todo Adapt this when per content type archive limited is added on repository Content Type model. - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - */ - public function testPublishVersionNotCreatingUnlimitedArchives() - { - $content = $this->createContentVersion1(); - - // load first to make sure list gets updated also (cache) - $versionInfoList = $this->contentService->loadVersions($content->contentInfo); - $this->assertCount(1, $versionInfoList); - $this->assertEquals(1, $versionInfoList[0]->versionNo); - - // Create a new draft with versionNo = 2 - $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo); - $this->contentService->publishVersion($draftedContentVersion->getVersionInfo()); - - // Create a new draft with versionNo = 3 - $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo); - $this->contentService->publishVersion($draftedContentVersion->getVersionInfo()); - - // Create a new draft with versionNo = 4 - $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo); - $this->contentService->publishVersion($draftedContentVersion->getVersionInfo()); - - // Create a new draft with versionNo = 5 - $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo); - $this->contentService->publishVersion($draftedContentVersion->getVersionInfo()); - - // Create a new draft with versionNo = 6 - $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo); - $this->contentService->publishVersion($draftedContentVersion->getVersionInfo()); - - // Create a new draft with versionNo = 7 - $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo); - $this->contentService->publishVersion($draftedContentVersion->getVersionInfo()); - - $versionInfoList = $this->contentService->loadVersions($content->contentInfo); - - $this->assertCount(6, $versionInfoList); - $this->assertEquals(2, $versionInfoList[0]->versionNo); - $this->assertEquals(7, $versionInfoList[5]->versionNo); - - $this->assertEquals( - [ - VersionInfo::STATUS_ARCHIVED, - VersionInfo::STATUS_ARCHIVED, - VersionInfo::STATUS_ARCHIVED, - VersionInfo::STATUS_ARCHIVED, - VersionInfo::STATUS_ARCHIVED, - VersionInfo::STATUS_PUBLISHED, - ], - [ - $versionInfoList[0]->status, - $versionInfoList[1]->status, - $versionInfoList[2]->status, - $versionInfoList[3]->status, - $versionInfoList[4]->status, - $versionInfoList[5]->status, - ] - ); - } - - /** - * Test for the newContentMetadataUpdateStruct() method. - * - * @covers \eZ\Publish\API\Repository\ContentService::newContentMetadataUpdateStruct - * @group user - */ - public function testNewContentMetadataUpdateStruct() - { - // Creates a new metadata update struct - $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct(); - - foreach ($metadataUpdate as $propertyName => $propertyValue) { - $this->assertNull($propertyValue, "Property '{$propertyName}' initial value should be null'"); - } - - $metadataUpdate->remoteId = 'aaaabbbbccccddddeeeeffff11112222'; - $metadataUpdate->mainLanguageCode = self::ENG_GB; - $metadataUpdate->alwaysAvailable = false; - - $this->assertInstanceOf( - ContentMetadataUpdateStruct::class, - $metadataUpdate - ); - } - - /** - * Test for the updateContentMetadata() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testNewContentMetadataUpdateStruct - * @group user - */ - public function testUpdateContentMetadata() - { - $content = $this->createContentVersion1(); - - // Creates a metadata update struct - $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct(); - - $metadataUpdate->remoteId = 'aaaabbbbccccddddeeeeffff11112222'; - $metadataUpdate->mainLanguageCode = self::ENG_GB; - $metadataUpdate->alwaysAvailable = false; - $metadataUpdate->publishedDate = $this->createDateTime(441759600); // 1984/01/01 - $metadataUpdate->modificationDate = $this->createDateTime(441759600); // 1984/01/01 - - // Update the metadata of the published content object - $content = $this->contentService->updateContentMetadata( - $content->contentInfo, - $metadataUpdate - ); - - $this->assertInstanceOf( - Content::class, - $content - ); - - return $content; - } - - /** - * Test for the updateContentMetadata() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata - */ - public function testUpdateContentMetadataSetsExpectedProperties($content) - { - $contentInfo = $content->contentInfo; - $currentUserReference = $this->getRepository()->getPermissionResolver()->getCurrentUserReference(); - - $this->assertEquals( - [ - 'remoteId' => 'aaaabbbbccccddddeeeeffff11112222', - 'sectionId' => $this->generateId('section', 1), - 'alwaysAvailable' => false, - 'currentVersionNo' => 1, - 'mainLanguageCode' => self::ENG_GB, - 'modificationDate' => $this->createDateTime(441759600), - 'ownerId' => $currentUserReference->getUserId(), - 'published' => true, - 'publishedDate' => $this->createDateTime(441759600), - ], - [ - 'remoteId' => $contentInfo->remoteId, - 'sectionId' => $contentInfo->sectionId, - 'alwaysAvailable' => $contentInfo->alwaysAvailable, - 'currentVersionNo' => $contentInfo->currentVersionNo, - 'mainLanguageCode' => $contentInfo->mainLanguageCode, - 'modificationDate' => $contentInfo->modificationDate, - 'ownerId' => $contentInfo->ownerId, - 'published' => $contentInfo->published, - 'publishedDate' => $contentInfo->publishedDate, - ] - ); - } - - /** - * Test for the updateContentMetadata() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata - */ - public function testUpdateContentMetadataNotUpdatesContentVersion($content) - { - $this->assertEquals(1, $content->getVersionInfo()->versionNo); - } - - /** - * Test for the updateContentMetadata() method. - * - * @covers \eZ\Publish\API\Repository\ContentService::updateContentMetadata() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata - */ - public function testUpdateContentMetadataThrowsInvalidArgumentExceptionOnDuplicateRemoteId() - { - $content = $this->createContentVersion1(); - - // Creates a metadata update struct - $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct(); - $metadataUpdate->remoteId = self::MEDIA_REMOTE_ID; - - $this->expectException(APIInvalidArgumentException::class); - // specified remoteId is already used by the "Media" page. - $this->contentService->updateContentMetadata( - $content->contentInfo, - $metadataUpdate - ); - } - - /** - * Test for the updateContentMetadata() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::updateContentMetadata - */ - public function testUpdateContentMetadataThrowsInvalidArgumentExceptionOnNoMetadataPropertiesSet() - { - $contentInfo = $this->contentService->loadContentInfo(4); - $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct(); - - $this->expectException(APIInvalidArgumentException::class); - $this->contentService->updateContentMetadata($contentInfo, $contentMetadataUpdateStruct); - } - - /** - * @covers \eZ\Publish\API\Repository\ContentService::updateContentMetadata - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testUpdateContentAlwaysAvailable(): void - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $folder = $this->createFolder(['eng-GB' => 'Folder'], 2); - - $contentMetadataUpdate = $contentService->newContentMetadataUpdateStruct(); - $contentMetadataUpdate->alwaysAvailable = !$folder->contentInfo->alwaysAvailable; - $contentService->updateContentMetadata($folder->contentInfo, $contentMetadataUpdate); - - $reloadedFolder = $contentService->loadContent($folder->id); - self::assertEquals( - $contentMetadataUpdate->alwaysAvailable, - $reloadedFolder->contentInfo->alwaysAvailable - ); - } - - /** - * @covers \eZ\Publish\API\Repository\ContentService::updateContentMetadata - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function testUpdateContentMainTranslation(): void - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - - // create a Content Type which is not always available by default - $contentType = $this->createSimpleContentType( - 'test_t', - self::ENG_GB, - [ - 'name' => 'ezstring', - ], - false - ); - - $contentCreate = $contentService->newContentCreateStruct( - $contentType, - self::ENG_US - ); - $contentCreate->setField('name', 'My Content'); - $content = $contentService->publishVersion( - $contentService->createContent( - $contentCreate, - [$locationService->newLocationCreateStruct(2)] - )->getVersionInfo() - ); - // perform sanity check - self::assertFalse($content->contentInfo->alwaysAvailable); - - $updateStruct = $contentService->newContentMetadataUpdateStruct(); - $updateStruct->mainLanguageCode = self::ENG_GB; - - $contentService->updateContentMetadata($content->contentInfo, $updateStruct); - - $reloadedContent = $contentService->loadContent($content->id); - self::assertEquals(self::ENG_GB, $reloadedContent->contentInfo->mainLanguageCode); - - // check that other properties remained unchanged - self::assertStructPropertiesCorrect( - $content->contentInfo, - $reloadedContent->contentInfo, - [ - 'id', - 'contentTypeId', - 'name', - 'sectionId', - 'currentVersionNo', - 'published', - 'ownerId', - 'alwaysAvailable', - 'remoteId', - 'mainLocationId', - 'status', - ] - ); - } - - /** - * Test for the deleteContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::deleteContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - */ - public function testDeleteContent() - { - $contentVersion2 = $this->createContentVersion2(); - - // Load the locations for this content object - $locations = $this->locationService->loadLocations($contentVersion2->contentInfo); - - // This will delete the content, all versions and the associated locations - $this->contentService->deleteContent($contentVersion2->contentInfo); - - $this->expectException(NotFoundException::class); - - foreach ($locations as $location) { - $this->locationService->loadLocation($location->id); - } - } - - /** - * Test for the deleteContent() method. - * - * Test for issue EZP-21057: - * "contentService: Unable to delete a content with an empty file attribute" - * - * @see \eZ\Publish\API\Repository\ContentService::deleteContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - */ - public function testDeleteContentWithEmptyBinaryField() - { - $contentVersion = $this->createContentVersion1EmptyBinaryField(); - - // Load the locations for this content object - $locations = $this->locationService->loadLocations($contentVersion->contentInfo); - - // This will delete the content, all versions and the associated locations - $this->contentService->deleteContent($contentVersion->contentInfo); - - $this->expectException(NotFoundException::class); - - foreach ($locations as $location) { - $this->locationService->loadLocation($location->id); - } - } - - public function testCountContentDraftsReturnsZeroByDefault(): void - { - $this->assertSame(0, $this->contentService->countContentDrafts()); - } - - public function testCountContentDrafts(): void - { - // Create 5 drafts - $this->createContentDrafts(5); - - $this->assertSame(5, $this->contentService->countContentDrafts()); - } - - public function testCountContentDraftsForUsers(): void - { - $newUser = $this->createUserWithPolicies( - 'new_user', - [ - ['module' => 'content', 'function' => 'create'], - ['module' => 'content', 'function' => 'read'], - ['module' => 'content', 'function' => 'publish'], - ['module' => 'content', 'function' => 'edit'], - ] - ); - - $previousUser = $this->permissionResolver->getCurrentUserReference(); - - // Set new editor as user - $this->permissionResolver->setCurrentUserReference($newUser); - - // Create a content draft as newUser - $publishedContent = $this->createContentVersion1(); - $this->contentService->createContentDraft($publishedContent->contentInfo); - - // Reset to previous current user - $this->permissionResolver->setCurrentUserReference($previousUser); - - // Now $contentDrafts for the previous current user and the new user - $newUserDrafts = $this->contentService->countContentDrafts($newUser); - $previousUserDrafts = $this->contentService->countContentDrafts(); - - $this->assertSame(1, $newUserDrafts); - $this->assertSame(0, $previousUserDrafts); - } - - /** - * Test for the loadContentDrafts() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts() - */ - public function testLoadContentDraftsReturnsEmptyArrayByDefault() - { - $contentDrafts = $this->contentService->loadContentDrafts(); - - $this->assertSame([], $contentDrafts); - } - - /** - * Test for the loadContentDrafts() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft - */ - public function testLoadContentDrafts() - { - // "Media" content object - $mediaContentInfo = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); - - // "eZ Publish Demo Design ..." content object - $demoDesignContentInfo = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID); - - // Create some drafts - $this->contentService->createContentDraft($mediaContentInfo); - $this->contentService->createContentDraft($demoDesignContentInfo); - - // Now $contentDrafts should contain two drafted versions - $draftedVersions = $this->contentService->loadContentDrafts(); - - $actual = [ - $draftedVersions[0]->status, - $draftedVersions[0]->getContentInfo()->remoteId, - $draftedVersions[1]->status, - $draftedVersions[1]->getContentInfo()->remoteId, - ]; - sort($actual, SORT_STRING); - - $this->assertEquals( - [ - VersionInfo::STATUS_DRAFT, - VersionInfo::STATUS_DRAFT, - self::DEMO_DESIGN_REMOTE_ID, - self::MEDIA_REMOTE_ID, - ], - $actual - ); - } - - /** - * Test for the loadContentDrafts() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts($user) - */ - public function testLoadContentDraftsWithFirstParameter() - { - $user = $this->createUserVersion1(); - - // Get current user - $oldCurrentUser = $this->permissionResolver->getCurrentUserReference(); - - // Set new editor as user - $this->permissionResolver->setCurrentUserReference($user); - - // "Media" content object - $mediaContentInfo = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); - - // Create a content draft - $this->contentService->createContentDraft($mediaContentInfo); - - // Reset to previous current user - $this->permissionResolver->setCurrentUserReference($oldCurrentUser); - - // Now $contentDrafts for the previous current user and the new user - $newCurrentUserDrafts = $this->contentService->loadContentDrafts($user); - $oldCurrentUserDrafts = $this->contentService->loadContentDrafts(); - - $this->assertSame([], $oldCurrentUserDrafts); - - $this->assertEquals( - [ - VersionInfo::STATUS_DRAFT, - self::MEDIA_REMOTE_ID, - ], - [ - $newCurrentUserDrafts[0]->status, - $newCurrentUserDrafts[0]->getContentInfo()->remoteId, - ] - ); - $this->assertTrue($newCurrentUserDrafts[0]->isDraft()); - $this->assertFalse($newCurrentUserDrafts[0]->isArchived()); - $this->assertFalse($newCurrentUserDrafts[0]->isPublished()); - } - - /** - * Test for the loadContentDraftList() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts() - */ - public function testLoadContentDraftListWithPaginationParameters() - { - // Create some drafts - $publishedContent = $this->createContentVersion1(); - $draftContentA = $this->contentService->createContentDraft($publishedContent->contentInfo); - $draftContentB = $this->contentService->createContentDraft($draftContentA->contentInfo); - $draftContentC = $this->contentService->createContentDraft($draftContentB->contentInfo); - $draftContentD = $this->contentService->createContentDraft($draftContentC->contentInfo); - $draftContentE = $this->contentService->createContentDraft($draftContentD->contentInfo); - - $draftsOnPage1 = $this->contentService->loadContentDraftList(null, 0, 2); - $draftsOnPage2 = $this->contentService->loadContentDraftList(null, 2, 2); - - $this->assertSame(5, $draftsOnPage1->totalCount); - $this->assertSame(5, $draftsOnPage2->totalCount); - $this->assertEquals($draftContentE->getVersionInfo(), $draftsOnPage1->items[0]->getVersionInfo()); - $this->assertEquals($draftContentD->getVersionInfo(), $draftsOnPage1->items[1]->getVersionInfo()); - $this->assertEquals($draftContentC->getVersionInfo(), $draftsOnPage2->items[0]->getVersionInfo()); - $this->assertEquals($draftContentB->getVersionInfo(), $draftsOnPage2->items[1]->getVersionInfo()); - } - - /** - * Test for the loadContentDraftList() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts($user) - */ - public function testLoadContentDraftListWithForUserWithLimitation() - { - $oldUser = $this->permissionResolver->getCurrentUserReference(); - - $parentContent = $this->createFolder(['eng-US' => 'parentFolder'], 2); - $content = $this->createFolder(['eng-US' => 'parentFolder'], $parentContent->contentInfo->mainLocationId); - - // User has limitation to read versions only for `$content`, not for `$parentContent` - $newUser = $this->createUserWithVersionReadLimitations([$content->contentInfo->mainLocationId]); - - $this->permissionResolver->setCurrentUserReference($newUser); - - $contentDraftUnauthorized = $this->contentService->createContentDraft($parentContent->contentInfo); - $contentDraftA = $this->contentService->createContentDraft($content->contentInfo); - $contentDraftB = $this->contentService->createContentDraft($content->contentInfo); - - $newUserDraftList = $this->contentService->loadContentDraftList($newUser, 0); - $this->assertSame(3, $newUserDraftList->totalCount); - $this->assertEquals($contentDraftB->getVersionInfo(), $newUserDraftList->items[0]->getVersionInfo()); - $this->assertEquals($contentDraftA->getVersionInfo(), $newUserDraftList->items[1]->getVersionInfo()); - $this->assertEquals( - new UnauthorizedContentDraftListItem('content', 'versionread', ['contentId' => $contentDraftUnauthorized->id]), - $newUserDraftList->items[2] - ); - - // Reset to previous user - $this->permissionResolver->setCurrentUserReference($oldUser); - - $oldUserDraftList = $this->contentService->loadContentDraftList(); - - $this->assertSame(0, $oldUserDraftList->totalCount); - $this->assertSame([], $oldUserDraftList->items); - } - - /** - * Test for the loadContentDraftList() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts() - */ - public function testLoadAllContentDrafts() - { - // Create more drafts then default pagination limit - $this->createContentDrafts(12); - - $this->assertCount(12, $this->contentService->loadContentDraftList()); - } - - /** - * Test for the loadVersionInfo() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfo($contentInfo, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - */ - public function testLoadVersionInfoWithSecondParameter() - { - $publishedContent = $this->createContentVersion1(); - - $this->contentService->createContentDraft($publishedContent->contentInfo); - - // Will return the VersionInfo of the $draftContent - $versionInfo = $this->contentService->loadVersionInfoById($publishedContent->id, 2); - - $this->assertEquals(2, $versionInfo->versionNo); - - // Check that ContentInfo contained in VersionInfo has correct main Location id set - $this->assertEquals( - $publishedContent->getVersionInfo()->getContentInfo()->mainLocationId, - $versionInfo->getContentInfo()->mainLocationId - ); - } - - /** - * Test for the loadVersionInfo() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfo($contentInfo, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoWithSecondParameter - */ - public function testLoadVersionInfoThrowsNotFoundExceptionWithSecondParameter() - { - $draft = $this->createContentDraftVersion1(); - - $this->expectException(NotFoundException::class); - - // This call will fail with a "NotFoundException", because not versionNo 2 exists for this content object. - $this->contentService->loadVersionInfo($draft->contentInfo, 2); - } - - /** - * Test for the loadVersionInfoById() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById($contentId, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoWithSecondParameter - */ - public function testLoadVersionInfoByIdWithSecondParameter() - { - $publishedContent = $this->createContentVersion1(); - - $draftContent = $this->contentService->createContentDraft($publishedContent->contentInfo); - - // Will return the VersionInfo of the $draftContent - $versionInfo = $this->contentService->loadVersionInfoById($publishedContent->id, 2); - - $this->assertEquals(2, $versionInfo->versionNo); - - // Check that ContentInfo contained in VersionInfo has correct main Location id set - $this->assertEquals( - $publishedContent->getVersionInfo()->getContentInfo()->mainLocationId, - $versionInfo->getContentInfo()->mainLocationId - ); - - return [ - 'versionInfo' => $versionInfo, - 'draftContent' => $draftContent, - ]; - } - - /** - * Test for the returned value of the loadVersionInfoById() method. - * - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoByIdWithSecondParameter - * @covers \eZ\Publish\API\Repository\ContentService::loadVersionInfoById - * - * @param array $data - */ - public function testLoadVersionInfoByIdWithSecondParameterSetsExpectedVersionInfo(array $data) - { - /** @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo */ - $versionInfo = $data['versionInfo']; - /** @var \eZ\Publish\API\Repository\Values\Content\Content $draftContent */ - $draftContent = $data['draftContent']; - - $this->assertPropertiesCorrect( - [ - 'names' => [ - self::ENG_US => 'An awesome forum', - ], - 'contentInfo' => new ContentInfo([ - 'id' => $draftContent->contentInfo->id, - 'contentTypeId' => 28, - 'name' => 'An awesome forum', - 'sectionId' => 1, - 'currentVersionNo' => 1, - 'published' => true, - 'ownerId' => 14, - // this Content Object is created at the test runtime - 'modificationDate' => $versionInfo->contentInfo->modificationDate, - 'publishedDate' => $versionInfo->contentInfo->publishedDate, - 'alwaysAvailable' => 1, - 'remoteId' => 'abcdef0123456789abcdef0123456789', - 'mainLanguageCode' => self::ENG_US, - 'mainLocationId' => $draftContent->contentInfo->mainLocationId, - 'status' => ContentInfo::STATUS_PUBLISHED, - ]), - 'id' => $draftContent->versionInfo->id, - 'versionNo' => 2, - 'creatorId' => 14, - 'status' => 0, - 'initialLanguageCode' => self::ENG_US, - 'languageCodes' => [ - self::ENG_US, - ], - ], - $versionInfo - ); - } - - /** - * Test for the loadVersionInfoById() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById($contentId, $versionNo) - */ - public function testLoadVersionInfoByIdThrowsNotFoundExceptionWithSecondParameter() - { - $content = $this->createContentVersion1(); - - $this->expectException(NotFoundException::class); - - // This call will fail with a "NotFoundException", because not versionNo 2 exists for this content object. - $this->contentService->loadVersionInfoById($content->id, 2); - } - - /** - * Test for the loadContentByVersionInfo() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByVersionInfo($versionInfo, $languages) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByVersionInfo - */ - public function testLoadContentByVersionInfoWithSecondParameter() - { - $sectionId = $this->generateId('section', 1); - $contentTypeService = $this->getRepository()->getContentTypeService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); - - $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - - $contentCreateStruct->setField('name', 'Sindelfingen forum²'); - - $contentCreateStruct->setField('name', 'Sindelfingen forum²³', self::ENG_GB); - - $contentCreateStruct->remoteId = 'abcdef0123456789abcdef0123456789'; - // $sectionId contains the ID of section 1 - $contentCreateStruct->sectionId = $sectionId; - $contentCreateStruct->alwaysAvailable = true; - - // Create a new content draft - $content = $this->contentService->createContent($contentCreateStruct); - - // Now publish this draft - $publishedContent = $this->contentService->publishVersion($content->getVersionInfo()); - - // Will return a content instance with fields in "eng-US" - $reloadedContent = $this->contentService->loadContentByVersionInfo( - $publishedContent->getVersionInfo(), - [ - self::ENG_GB, - ], - false - ); - - $actual = []; - foreach ($reloadedContent->getFields() as $field) { - $actual[] = new Field( - [ - 'id' => 0, - 'value' => $field->value !== null, // Actual value tested by FieldType integration tests - 'languageCode' => $field->languageCode, - 'fieldDefIdentifier' => $field->fieldDefIdentifier, - ] - ); - } - usort( - $actual, - static function ($field1, $field2) { - if (0 === ($return = strcasecmp($field1->fieldDefIdentifier, $field2->fieldDefIdentifier))) { - return strcasecmp($field1->languageCode, $field2->languageCode); - } - - return $return; - } - ); - - $expected = [ - new Field( - [ - 'id' => 0, - 'value' => true, - 'languageCode' => self::ENG_GB, - 'fieldDefIdentifier' => 'name', - ] - ), - ]; - - $this->assertEquals($expected, $actual); - } - - /** - * Test for the loadContentByContentInfo() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfo - */ - public function testLoadContentByContentInfoWithLanguageParameters() - { - $sectionId = $this->generateId('section', 1); - $contentTypeService = $this->getRepository()->getContentTypeService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); - - $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - - $contentCreateStruct->setField('name', 'Sindelfingen forum²'); - - $contentCreateStruct->setField('name', 'Sindelfingen forum²³', self::ENG_GB); - - $contentCreateStruct->remoteId = 'abcdef0123456789abcdef0123456789'; - // $sectionId contains the ID of section 1 - $contentCreateStruct->sectionId = $sectionId; - $contentCreateStruct->alwaysAvailable = true; - - // Create a new content draft - $content = $this->contentService->createContent($contentCreateStruct); - - // Now publish this draft - $publishedContent = $this->contentService->publishVersion($content->getVersionInfo()); - - // Will return a content instance with fields in "eng-US" - $reloadedContent = $this->contentService->loadContentByContentInfo( - $publishedContent->contentInfo, - [ - self::ENG_US, - ], - null, - false - ); - - $actual = $this->normalizeFields($reloadedContent->getFields()); - - $expected = [ - new Field( - [ - 'id' => 0, - 'value' => true, - 'languageCode' => self::ENG_US, - 'fieldDefIdentifier' => 'name', - 'fieldTypeIdentifier' => 'ezstring', - ] - ), - ]; - - $this->assertEquals($expected, $actual); - - // Will return a content instance with fields in "eng-GB" (versions prior to 6.0.0-beta9 returned "eng-US" also) - $reloadedContent = $this->contentService->loadContentByContentInfo( - $publishedContent->contentInfo, - [ - self::ENG_GB, - ], - null, - true - ); - - $actual = $this->normalizeFields($reloadedContent->getFields()); - - $expected = [ - new Field( - [ - 'id' => 0, - 'value' => true, - 'languageCode' => self::ENG_GB, - 'fieldDefIdentifier' => 'name', - 'fieldTypeIdentifier' => 'ezstring', - ] - ), - ]; - - $this->assertEquals($expected, $actual); - - // Will return a content instance with fields in main language "eng-US", as "fre-FR" does not exists - $reloadedContent = $this->contentService->loadContentByContentInfo( - $publishedContent->contentInfo, - [ - 'fre-FR', - ], - null, - true - ); - - $actual = $this->normalizeFields($reloadedContent->getFields()); - - $expected = [ - new Field( - [ - 'id' => 0, - 'value' => true, - 'languageCode' => self::ENG_US, - 'fieldDefIdentifier' => 'name', - 'fieldTypeIdentifier' => 'ezstring', - ] - ), - ]; - - $this->assertEquals($expected, $actual); - } - - /** - * Test for the loadContentByContentInfo() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfo - */ - public function testLoadContentByContentInfoWithVersionNumberParameter() - { - $publishedContent = $this->createContentVersion1(); - - $this->contentService->createContentDraft($publishedContent->contentInfo); - - // This content instance is identical to $draftContent - $draftContentReloaded = $this->contentService->loadContentByContentInfo( - $publishedContent->contentInfo, - null, - 2 - ); - - $this->assertEquals( - 2, - $draftContentReloaded->getVersionInfo()->versionNo - ); - - // Check that ContentInfo contained in reloaded draft Content has correct main Location id set - $this->assertEquals( - $publishedContent->versionInfo->contentInfo->mainLocationId, - $draftContentReloaded->versionInfo->contentInfo->mainLocationId - ); - } - - /** - * Test for the loadContentByContentInfo() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfoWithVersionNumberParameter - */ - public function testLoadContentByContentInfoThrowsNotFoundExceptionWithVersionNumberParameter() - { - $content = $this->createContentVersion1(); - - $this->expectException(NotFoundException::class); - - // This call will fail with a "NotFoundException", because no content with versionNo = 2 exists. - $this->contentService->loadContentByContentInfo($content->contentInfo, null, 2); - } - - /** - * Test for the loadContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContent($contentId, $languages) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - */ - public function testLoadContentWithPrioritizedLanguages() - { - $draft = $this->createMultipleLanguageDraftVersion1(); - - // This draft contains those fields localized with "eng-GB" - $draftLocalized = $this->contentService->loadContent($draft->id, [self::ENG_GB], null, false); - - $this->assertLocaleFieldsEquals($draftLocalized->getFields(), self::ENG_GB); - - return $draftLocalized; - } - - /** - * Test for the loadContent() method using undefined translation. - * - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentWithPrioritizedLanguages - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $contentDraft - */ - public function testLoadContentWithPrioritizedLanguagesThrowsNotFoundException(Content $contentDraft) - { - $this->expectException(NotFoundException::class); - - $this->contentService->loadContent($contentDraft->id, [self::GER_DE], null, false); - } - - /** - * Test for the loadContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentWithPrioritizedLanguages - */ - public function testLoadContentPassTroughPrioritizedLanguagesToContentType(Content $content): void - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - - $contentType = $contentTypeService->loadContentType( - $content->contentInfo->contentTypeId, - [self::ENG_GB] - ); - - $this->assertEquals($contentType, $content->getContentType()); - } - - /** - * Test for the loadContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContent($contentId, $languages, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - */ - public function testLoadContentWithThirdParameter() - { - $publishedContent = $this->createContentVersion1(); - - $this->contentService->createContentDraft($publishedContent->contentInfo); - - // This content instance is identical to $draftContent - $draftContentReloaded = $this->contentService->loadContent($publishedContent->id, null, 2); - - $this->assertEquals(2, $draftContentReloaded->getVersionInfo()->versionNo); - - // Check that ContentInfo contained in reloaded draft Content has correct main Location id set - $this->assertEquals( - $publishedContent->versionInfo->contentInfo->mainLocationId, - $draftContentReloaded->versionInfo->contentInfo->mainLocationId - ); - } - - /** - * Test for the loadContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContent($contentId, $languages, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentWithThirdParameter - */ - public function testLoadContentThrowsNotFoundExceptionWithThirdParameter() - { - $content = $this->createContentVersion1(); - - $this->expectException(NotFoundException::class); - - // This call will fail with a "NotFoundException", because for this content object no versionNo=2 exists. - $this->contentService->loadContent($content->id, null, 2); - } - - /** - * Test for the loadContentByRemoteId() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId($remoteId, $languages) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - */ - public function testLoadContentByRemoteIdWithSecondParameter() - { - $draft = $this->createMultipleLanguageDraftVersion1(); - - $this->contentService->publishVersion($draft->versionInfo); - - // This draft contains those fields localized with "eng-GB" - $draftLocalized = $this->contentService->loadContentByRemoteId( - $draft->contentInfo->remoteId, - [self::ENG_GB], - null, - false - ); - - $this->assertLocaleFieldsEquals($draftLocalized->getFields(), self::ENG_GB); - } - - /** - * Test for the loadContentByRemoteId() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId($remoteId, $languages, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - */ - public function testLoadContentByRemoteIdWithThirdParameter() - { - $publishedContent = $this->createContentVersion1(); - - $this->contentService->createContentDraft($publishedContent->contentInfo); - - // This content instance is identical to $draftContent - $draftContentReloaded = $this->contentService->loadContentByRemoteId( - $publishedContent->contentInfo->remoteId, - null, - 2 - ); - - $this->assertEquals(2, $draftContentReloaded->getVersionInfo()->versionNo); - - // Check that ContentInfo contained in reloaded draft Content has correct main Location id set - $this->assertEquals( - $publishedContent->versionInfo->contentInfo->mainLocationId, - $draftContentReloaded->versionInfo->contentInfo->mainLocationId - ); - } - - /** - * Test for the loadContentByRemoteId() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId($remoteId, $languages, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByRemoteIdWithThirdParameter - */ - public function testLoadContentByRemoteIdThrowsNotFoundExceptionWithThirdParameter() - { - $content = $this->createContentVersion1(); - - $this->expectException(NotFoundException::class); - - // This call will fail with a "NotFoundException", because for this content object no versionNo=2 exists. - $this->contentService->loadContentByRemoteId( - $content->contentInfo->remoteId, - null, - 2 - ); - } - - /** - * Test that retrieval of translated name field respects prioritized language list. - * - * @dataProvider getPrioritizedLanguageList - * - * @param string[]|null $languageCodes - */ - public function testLoadContentWithPrioritizedLanguagesList($languageCodes) - { - $content = $this->createContentVersion2(); - - $content = $this->contentService->loadContent($content->id, $languageCodes); - - $expectedName = $content->getVersionInfo()->getName( - isset($languageCodes[0]) ? $languageCodes[0] : null - ); - $nameValue = $content->getFieldValue('name'); - /** @var \eZ\Publish\Core\FieldType\TextLine\Value $nameValue */ - self::assertEquals($expectedName, $nameValue->text); - self::assertEquals($expectedName, $content->getVersionInfo()->getName()); - // Also check value on shortcut method on content - self::assertEquals($expectedName, $content->getName()); - } - - /** - * @return array - */ - public function getPrioritizedLanguageList() - { - return [ - [[self::ENG_US]], - [[self::ENG_GB]], - [[self::ENG_GB, self::ENG_US]], - [[self::ENG_US, self::ENG_GB]], - ]; - } - - /** - * Test for the deleteVersion() method. - * - * @see \eZ\Publish\API\Repository\ContentService::deleteVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft - */ - public function testDeleteVersion() - { - $content = $this->createContentVersion1(); - - // Create new draft, because published or last version of the Content can't be deleted - $draft = $this->contentService->createContentDraft( - $content->getVersionInfo()->getContentInfo() - ); - - // Delete the previously created draft - $this->contentService->deleteVersion($draft->getVersionInfo()); - - $versions = $this->contentService->loadVersions($content->getVersionInfo()->getContentInfo()); - - $this->assertCount(1, $versions); - $this->assertEquals( - $content->getVersionInfo()->id, - $versions[0]->id - ); - } - - /** - * Test for the deleteVersion() method. - * - * @see \eZ\Publish\API\Repository\ContentService::deleteVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - */ - public function testDeleteVersionThrowsBadStateExceptionOnPublishedVersion() - { - $content = $this->createContentVersion1(); - - $this->expectException(BadStateException::class); - - // This call will fail with a "BadStateException", because the content version is currently published. - $this->contentService->deleteVersion($content->getVersionInfo()); - } - - /** - * Test for the deleteVersion() method. - * - * @see \eZ\Publish\API\Repository\ContentService::deleteVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - */ - public function testDeleteVersionWorksIfOnlyVersionIsDraft() - { - $draft = $this->createContentDraftVersion1(); - - $this->contentService->deleteVersion($draft->getVersionInfo()); - - $this->expectException(NotFoundException::class); - - // This call will fail with a "NotFound", because we allow to delete content if remaining version is draft. - // Can normally only happen if there where always only a draft to begin with, simplifies UI edit API usage. - $this->contentService->loadContentInfo($draft->id); - } - - /** - * Test for the loadVersions() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadVersions() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - * - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo[] - */ - public function testLoadVersions() - { - $contentVersion2 = $this->createContentVersion2(); - - // Load versions of this ContentInfo instance - $versions = $this->contentService->loadVersions($contentVersion2->contentInfo); - - $expectedVersionsOrder = [ - $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 1), - $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 2), - ]; - - $this->assertEquals($expectedVersionsOrder, $versions); - - return $versions; - } - - /** - * Test for the loadVersions() method. - * - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersions - * @covers \eZ\Publish\Core\Repository\ContentService::loadVersions - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo[] $versions - */ - public function testLoadVersionsSetsExpectedVersionInfo(array $versions) - { - $this->assertCount(2, $versions); - - $expectedVersions = [ - [ - 'versionNo' => 1, - 'creatorId' => 14, - 'status' => VersionInfo::STATUS_ARCHIVED, - 'initialLanguageCode' => self::ENG_US, - 'languageCodes' => [self::ENG_US], - ], - [ - 'versionNo' => 2, - 'creatorId' => 10, - 'status' => VersionInfo::STATUS_PUBLISHED, - 'initialLanguageCode' => self::ENG_US, - 'languageCodes' => [self::ENG_US, self::ENG_GB], - ], - ]; - - $this->assertPropertiesCorrect($expectedVersions[0], $versions[0]); - $this->assertPropertiesCorrect($expectedVersions[1], $versions[1]); - $this->assertEqualsWithDelta( - $versions[0]->creationDate->getTimestamp(), - $versions[1]->creationDate->getTimestamp(), - 2, - 'Creation time did not match within delta of 2 seconds', - ); - $this->assertEqualsWithDelta( - $versions[0]->modificationDate->getTimestamp(), - $versions[1]->modificationDate->getTimestamp(), - 2, - 'Creation time did not match within delta of 2 seconds', - ); - $this->assertTrue($versions[0]->isArchived()); - $this->assertFalse($versions[0]->isDraft()); - $this->assertFalse($versions[0]->isPublished()); - - $this->assertTrue($versions[1]->isPublished()); - $this->assertFalse($versions[1]->isDraft()); - $this->assertFalse($versions[1]->isArchived()); - } - - /** - * Test for the copyContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::copyContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - * @group field-type - */ - public function testCopyContent() - { - $parentLocationId = $this->generateId('location', 56); - - $contentVersion2 = $this->createMultipleLanguageContentVersion2(); - - // Configure new target location - $targetLocationCreate = $this->locationService->newLocationCreateStruct($parentLocationId); - - $targetLocationCreate->priority = 42; - $targetLocationCreate->hidden = true; - $targetLocationCreate->remoteId = '01234abcdef5678901234abcdef56789'; - $targetLocationCreate->sortField = Location::SORT_FIELD_NODE_ID; - $targetLocationCreate->sortOrder = Location::SORT_ORDER_DESC; - - // Copy content with all versions and drafts - $contentCopied = $this->contentService->copyContent( - $contentVersion2->contentInfo, - $targetLocationCreate - ); - - $this->assertInstanceOf( - Content::class, - $contentCopied - ); - - $this->assertNotEquals( - $contentVersion2->contentInfo->remoteId, - $contentCopied->contentInfo->remoteId - ); - - $this->assertNotEquals( - $contentVersion2->id, - $contentCopied->id - ); - - $this->assertCount( - 2, - $this->contentService->loadVersions($contentCopied->contentInfo) - ); - - $this->assertEquals(2, $contentCopied->getVersionInfo()->versionNo); - - $this->assertAllFieldsEquals($contentCopied->getFields()); - - $this->assertDefaultContentStates($contentCopied->contentInfo); - - $this->assertNotNull( - $contentCopied->contentInfo->mainLocationId, - 'Expected main location to be set given we provided a LocationCreateStruct' - ); - } - - /** - * Test for the copyContent() method with ezsettings.default.content.retain_owner_on_copy set to false - * See settings/test/integration_legacy.yml for service override. - * - * @see \eZ\Publish\API\Repository\ContentService::copyContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - * @group field-type - */ - public function testCopyContentWithNewOwner() - { - $parentLocationId = $this->generateId('location', 56); - - $userService = $this->getRepository()->getUserService(); - - $owner = $this->createUser('new_owner', 'foo', 'bar'); - /** @var \eZ\Publish\API\Repository\Values\Content\Content $contentVersion2 */ - $contentVersion2 = $this->createContentDraftVersion1( - $parentLocationId, - self::FORUM_IDENTIFIER, - 'name', - $owner - ); - - // Configure new target location - $targetLocationCreate = $this->locationService->newLocationCreateStruct($parentLocationId); - - $targetLocationCreate->priority = 42; - $targetLocationCreate->hidden = true; - $targetLocationCreate->remoteId = '01234abcdef5678901234abcdef56789'; - $targetLocationCreate->sortField = Location::SORT_FIELD_NODE_ID; - $targetLocationCreate->sortOrder = Location::SORT_ORDER_DESC; - - $this->contentService->publishVersion($contentVersion2->versionInfo); - $this->contentService->createContentDraft($contentVersion2->contentInfo); - - // Copy content with all versions and drafts - $contentCopied = $this->contentService->copyContent( - $contentVersion2->contentInfo, - $targetLocationCreate - ); - - $this->assertEquals( - $owner->id, - $contentVersion2->contentInfo->ownerId - ); - $newOwnerId = $userService->loadUserByLogin('admin')->getUserId(); - $this->assertEquals( - $newOwnerId, - $contentCopied->contentInfo->ownerId - ); - $versions = $this->contentService->loadVersions($contentCopied->contentInfo); - $this->assertCount(2, $versions); - - foreach ($versions as $version) { - $this->assertEquals( - $newOwnerId, - $version->creatorId - ); - } - } - - /** - * Test for the copyContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::copyContent($contentInfo, $destinationLocationCreateStruct, $versionInfo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCopyContent - */ - public function testCopyContentWithGivenVersion() - { - $parentLocationId = $this->generateId('location', 56); - - $contentVersion2 = $this->createContentVersion2(); - - // Configure new target location - $targetLocationCreate = $this->locationService->newLocationCreateStruct($parentLocationId); - - $targetLocationCreate->priority = 42; - $targetLocationCreate->hidden = true; - $targetLocationCreate->remoteId = '01234abcdef5678901234abcdef56789'; - $targetLocationCreate->sortField = Location::SORT_FIELD_NODE_ID; - $targetLocationCreate->sortOrder = Location::SORT_ORDER_DESC; - - // Copy only the initial version - $contentCopied = $this->contentService->copyContent( - $contentVersion2->contentInfo, - $targetLocationCreate, - $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 1) - ); - - $this->assertInstanceOf( - Content::class, - $contentCopied - ); - - $this->assertNotEquals( - $contentVersion2->contentInfo->remoteId, - $contentCopied->contentInfo->remoteId - ); - - $this->assertNotEquals( - $contentVersion2->id, - $contentCopied->id - ); - - $this->assertCount( - 1, - $this->contentService->loadVersions($contentCopied->contentInfo) - ); - - $this->assertEquals(1, $contentCopied->getVersionInfo()->versionNo); - - $this->assertNotNull( - $contentCopied->contentInfo->mainLocationId, - 'Expected main location to be set given we provided a LocationCreateStruct' - ); - } - - /** - * Test for the addRelation() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @see \eZ\Publish\API\Repository\ContentService::addRelation() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersionFromContentDraft - */ - public function testAddRelation() - { - $draft = $this->createContentDraftVersion1(); - - $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); - - // Create relation between new content object and "Media" page - $relation = $this->contentService->addRelation( - $draft->getVersionInfo(), - $media - ); - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\Relation', - $relation - ); - - return $this->contentService->loadRelations($draft->getVersionInfo()); - } - - /** - * Test for the addRelation() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations - * - * @see \eZ\Publish\API\Repository\ContentService::addRelation() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation - */ - public function testAddRelationAddsRelationToContent($relations) - { - $this->assertCount( - 1, - $relations - ); - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations - */ - protected function assertExpectedRelations($relations) - { - $this->assertEquals( - [ - 'type' => Relation::COMMON, - 'sourceFieldDefinitionIdentifier' => null, - 'sourceContentInfo' => 'abcdef0123456789abcdef0123456789', - 'destinationContentInfo' => self::MEDIA_REMOTE_ID, - ], - [ - 'type' => $relations[0]->type, - 'sourceFieldDefinitionIdentifier' => $relations[0]->sourceFieldDefinitionIdentifier, - 'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId, - 'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId, - ] - ); - } - - /** - * Test for the addRelation() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations - * - * @see \eZ\Publish\API\Repository\ContentService::addRelation() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation - */ - public function testAddRelationSetsExpectedRelations($relations) - { - $this->assertExpectedRelations($relations); - } - - /** - * Test for the createContentDraft() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\Relation[] - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelationSetsExpectedRelations - */ - public function testCreateContentDraftWithRelations() - { - $draft = $this->createContentDraftVersion1(); - $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); - - // Create relation between new content object and "Media" page - $this->contentService->addRelation( - $draft->getVersionInfo(), - $media - ); - - $content = $this->contentService->publishVersion($draft->versionInfo); - $newDraft = $this->contentService->createContentDraft($content->contentInfo); - - return $this->contentService->loadRelations($newDraft->getVersionInfo()); - } - - /** - * Test for the createContentDraft() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations - * - * @return \eZ\Publish\API\Repository\Values\Content\Relation[] - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraftWithRelations - */ - public function testCreateContentDraftWithRelationsCreatesRelations($relations) - { - $this->assertCount( - 1, - $relations - ); - - return $relations; - } - - /** - * Test for the createContentDraft() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $relations - * - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraftWithRelationsCreatesRelations - */ - public function testCreateContentDraftWithRelationsCreatesExpectedRelations($relations) - { - $this->assertExpectedRelations($relations); - } - - /** - * Test for the addRelation() method. - * - * @see \eZ\Publish\API\Repository\ContentService::addRelation() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation - */ - public function testAddRelationThrowsBadStateException() - { - $content = $this->createContentVersion1(); - - $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); - - $this->expectException(BadStateException::class); - - // This call will fail with a "BadStateException", because content is published and not a draft. - $this->contentService->addRelation( - $content->getVersionInfo(), - $media - ); - } - - /** - * Test for the loadRelations() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadRelations() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation - */ - public function testLoadRelations() - { - $draft = $this->createContentDraftVersion1(); - - $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); - $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID); - - // Create relation between new content object and "Media" page - $this->contentService->addRelation( - $draft->getVersionInfo(), - $media - ); - - // Create another relation with the "Demo Design" page - $this->contentService->addRelation( - $draft->getVersionInfo(), - $demoDesign - ); - - $relations = $this->contentService->loadRelations($draft->getVersionInfo()); - - usort( - $relations, - static function ($rel1, $rel2) { - return strcasecmp( - $rel2->getDestinationContentInfo()->remoteId, - $rel1->getDestinationContentInfo()->remoteId - ); - } - ); - - $this->assertEquals( - [ - [ - 'sourceContentInfo' => 'abcdef0123456789abcdef0123456789', - 'destinationContentInfo' => self::MEDIA_REMOTE_ID, - ], - [ - 'sourceContentInfo' => 'abcdef0123456789abcdef0123456789', - 'destinationContentInfo' => self::DEMO_DESIGN_REMOTE_ID, - ], - ], - [ - [ - 'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId, - 'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId, - ], - [ - 'sourceContentInfo' => $relations[1]->sourceContentInfo->remoteId, - 'destinationContentInfo' => $relations[1]->destinationContentInfo->remoteId, - ], - ] - ); - } - - /** - * Test for the loadRelations() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadRelations() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadRelations - */ - public function testLoadRelationsSkipsArchivedContent() - { - $trashService = $this->getRepository()->getTrashService(); - - $draft = $this->createContentDraftVersion1(); - - // Load other content objects - $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); - $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID); - - // Create relation between new content object and "Media" page - $this->contentService->addRelation( - $draft->getVersionInfo(), - $media - ); - - // Create another relation with the "Demo Design" page - $this->contentService->addRelation( - $draft->getVersionInfo(), - $demoDesign - ); - - $demoDesignLocation = $this->locationService->loadLocation($demoDesign->mainLocationId); - - // Trashing Content's last Location will change its status to archived, - // in this case relation towards it will not be loaded. - $trashService->trash($demoDesignLocation); - - // Load all relations - $relations = $this->contentService->loadRelations($draft->getVersionInfo()); - - $this->assertCount(1, $relations); - $this->assertEquals( - [ - [ - 'sourceContentInfo' => 'abcdef0123456789abcdef0123456789', - 'destinationContentInfo' => self::MEDIA_REMOTE_ID, - ], - ], - [ - [ - 'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId, - 'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId, - ], - ] - ); - } - - /** - * Test for the loadRelations() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadRelations() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadRelations - */ - public function testLoadRelationsSkipsDraftContent() - { - $draft = $this->createContentDraftVersion1(); - - // Load other content objects - $media = $this->contentService->loadContentByRemoteId(self::MEDIA_REMOTE_ID); - $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID); - - // Create draft of "Media" page - $mediaDraft = $this->contentService->createContentDraft($media->contentInfo); - - // Create relation between "Media" page and new content object draft. - // This relation will not be loaded before the draft is published. - $this->contentService->addRelation( - $mediaDraft->getVersionInfo(), - $draft->getVersionInfo()->getContentInfo() - ); - - // Create another relation with the "Demo Design" page - $this->contentService->addRelation( - $mediaDraft->getVersionInfo(), - $demoDesign - ); - - $relations = $this->contentService->loadRelations($mediaDraft->getVersionInfo()); - - $this->assertCount(1, $relations); - $this->assertEquals( - [ - [ - 'sourceContentInfo' => self::MEDIA_REMOTE_ID, - 'destinationContentInfo' => self::DEMO_DESIGN_REMOTE_ID, - ], - ], - [ - [ - 'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId, - 'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId, - ], - ] - ); - } - - /** - * Test for the countReverseRelations() method. - * - * @covers \eZ\Publish\API\Repository\ContentService::countReverseRelations - */ - public function testCountReverseRelations(): void - { - $contentWithReverseRelations = $this->createContentWithReverseRelations([ - $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo - ), - $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo - ), - ]); - - $contentInfo = $contentWithReverseRelations->content->getVersionInfo()->getContentInfo(); - - $this->assertEquals(2, $this->contentService->countReverseRelations($contentInfo)); - } - - /** - * Test for the countReverseRelations() method. - * - * @covers \eZ\Publish\API\Repository\ContentService::countReverseRelations - */ - public function testCountReverseRelationsReturnsZeroByDefault(): void - { - $draft = $this->createContentDraftVersion1(); - - $this->assertSame(0, $this->contentService->countReverseRelations($draft->getVersionInfo()->getContentInfo())); - } - - /** - * Test for the countReverseRelations() method. - * - * @covers \eZ\Publish\API\Repository\ContentService::countReverseRelations - */ - public function testCountReverseRelationsForUnauthorizedUser(): void - { - $contentWithReverseRelations = $this->createContentWithReverseRelations([ - $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo - ), - ]); - $mediaUser = $this->createMediaUserVersion1(); - $this->permissionResolver->setCurrentUserReference($mediaUser); - - $contentInfo = $contentWithReverseRelations->content->contentInfo; - - $this->assertSame(0, $this->contentService->countReverseRelations($contentInfo)); - } - - /** - * Test for the loadReverseRelations() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadReverseRelations() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation - */ - public function testLoadReverseRelations() - { - $versionInfo = $this->createContentVersion1()->getVersionInfo(); - $contentInfo = $versionInfo->getContentInfo(); - - // Create some drafts - $mediaDraft = $this->contentService->createContentDraft( - $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID) - ); - $demoDesignDraft = $this->contentService->createContentDraft( - $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID) - ); - - // Create relation between new content object and "Media" page - $relation1 = $this->contentService->addRelation( - $mediaDraft->getVersionInfo(), - $contentInfo - ); - - // Create another relation with the "Demo Design" page - $relation2 = $this->contentService->addRelation( - $demoDesignDraft->getVersionInfo(), - $contentInfo - ); - - // Publish drafts, so relations become active - $this->contentService->publishVersion($mediaDraft->getVersionInfo()); - $this->contentService->publishVersion($demoDesignDraft->getVersionInfo()); - - $relations = $this->contentService->loadRelations($versionInfo); - $reverseRelations = $this->contentService->loadReverseRelations($contentInfo); - - $this->assertEquals($contentInfo->id, $relation1->getDestinationContentInfo()->id); - $this->assertEquals($mediaDraft->id, $relation1->getSourceContentInfo()->id); - - $this->assertEquals($contentInfo->id, $relation2->getDestinationContentInfo()->id); - $this->assertEquals($demoDesignDraft->id, $relation2->getSourceContentInfo()->id); - - $this->assertCount(0, $relations); - $this->assertCount(2, $reverseRelations); - - usort( - $reverseRelations, - static function ($rel1, $rel2) { - return strcasecmp( - $rel2->getSourceContentInfo()->remoteId, - $rel1->getSourceContentInfo()->remoteId - ); - } - ); - - $this->assertEquals( - [ - [ - 'sourceContentInfo' => self::MEDIA_REMOTE_ID, - 'destinationContentInfo' => 'abcdef0123456789abcdef0123456789', - ], - [ - 'sourceContentInfo' => self::DEMO_DESIGN_REMOTE_ID, - 'destinationContentInfo' => 'abcdef0123456789abcdef0123456789', - ], - ], - [ - [ - 'sourceContentInfo' => $reverseRelations[0]->sourceContentInfo->remoteId, - 'destinationContentInfo' => $reverseRelations[0]->destinationContentInfo->remoteId, - ], - [ - 'sourceContentInfo' => $reverseRelations[1]->sourceContentInfo->remoteId, - 'destinationContentInfo' => $reverseRelations[1]->destinationContentInfo->remoteId, - ], - ] - ); - } - - /** - * Test for the loadReverseRelations() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadReverseRelations() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadReverseRelations - */ - public function testLoadReverseRelationsSkipsArchivedContent() - { - $trashService = $this->getRepository()->getTrashService(); - - $versionInfo = $this->createContentVersion1()->getVersionInfo(); - $contentInfo = $versionInfo->getContentInfo(); - - // Create some drafts - $mediaDraft = $this->contentService->createContentDraft( - $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID) - ); - $demoDesignDraft = $this->contentService->createContentDraft( - $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID) - ); - - // Create relation between new content object and "Media" page - $relation1 = $this->contentService->addRelation( - $mediaDraft->getVersionInfo(), - $contentInfo - ); - - // Create another relation with the "Demo Design" page - $relation2 = $this->contentService->addRelation( - $demoDesignDraft->getVersionInfo(), - $contentInfo - ); - - // Publish drafts, so relations become active - $this->contentService->publishVersion($mediaDraft->getVersionInfo()); - $this->contentService->publishVersion($demoDesignDraft->getVersionInfo()); - - $demoDesignLocation = $this->locationService->loadLocation($demoDesignDraft->contentInfo->mainLocationId); - - // Trashing Content's last Location will change its status to archived, - // in this case relation from it will not be loaded. - $trashService->trash($demoDesignLocation); - - // Load all relations - $relations = $this->contentService->loadRelations($versionInfo); - $reverseRelations = $this->contentService->loadReverseRelations($contentInfo); - - $this->assertEquals($contentInfo->id, $relation1->getDestinationContentInfo()->id); - $this->assertEquals($mediaDraft->id, $relation1->getSourceContentInfo()->id); - - $this->assertEquals($contentInfo->id, $relation2->getDestinationContentInfo()->id); - $this->assertEquals($demoDesignDraft->id, $relation2->getSourceContentInfo()->id); - - $this->assertCount(0, $relations); - $this->assertCount(1, $reverseRelations); - - $this->assertEquals( - [ - [ - 'sourceContentInfo' => self::MEDIA_REMOTE_ID, - 'destinationContentInfo' => 'abcdef0123456789abcdef0123456789', - ], - ], - [ - [ - 'sourceContentInfo' => $reverseRelations[0]->sourceContentInfo->remoteId, - 'destinationContentInfo' => $reverseRelations[0]->destinationContentInfo->remoteId, - ], - ] - ); - } - - /** - * Test for the loadReverseRelations() method. - * - * @see \eZ\Publish\API\Repository\ContentService::loadReverseRelations() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadReverseRelations - */ - public function testLoadReverseRelationsSkipsDraftContent() - { - // Load "Media" page Content - $media = $this->contentService->loadContentByRemoteId(self::MEDIA_REMOTE_ID); - - // Create some drafts - $newDraftVersionInfo = $this->createContentDraftVersion1()->getVersionInfo(); - $demoDesignDraft = $this->contentService->createContentDraft( - $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID) - ); - - // Create relation between "Media" page and new content object - $relation1 = $this->contentService->addRelation( - $newDraftVersionInfo, - $media->contentInfo - ); - - // Create another relation with the "Demo Design" page - $relation2 = $this->contentService->addRelation( - $demoDesignDraft->getVersionInfo(), - $media->contentInfo - ); - - // Publish drafts, so relations become active - $this->contentService->publishVersion($demoDesignDraft->getVersionInfo()); - // We will not publish new Content draft, therefore relation from it - // will not be loaded as reverse relation for "Media" page - - $relations = $this->contentService->loadRelations($media->versionInfo); - $reverseRelations = $this->contentService->loadReverseRelations($media->contentInfo); - - $this->assertEquals($media->contentInfo->id, $relation1->getDestinationContentInfo()->id); - $this->assertEquals($newDraftVersionInfo->contentInfo->id, $relation1->getSourceContentInfo()->id); - - $this->assertEquals($media->contentInfo->id, $relation2->getDestinationContentInfo()->id); - $this->assertEquals($demoDesignDraft->id, $relation2->getSourceContentInfo()->id); - - $this->assertCount(0, $relations); - $this->assertCount(1, $reverseRelations); - - $this->assertEquals( - [ - [ - 'sourceContentInfo' => self::DEMO_DESIGN_REMOTE_ID, - 'destinationContentInfo' => self::MEDIA_REMOTE_ID, - ], - ], - [ - [ - 'sourceContentInfo' => $reverseRelations[0]->sourceContentInfo->remoteId, - 'destinationContentInfo' => $reverseRelations[0]->destinationContentInfo->remoteId, - ], - ] - ); - } - - /** - * @covers \eZ\Publish\API\Repository\ContentService::loadReverseRelationList - */ - public function testLoadReverseRelationList(): void - { - $draft1 = $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo - ); - $draft2 = $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo - ); - $draft3 = $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Baz'], 2)->contentInfo - ); - - $contentWithReverseRelations = $this->createContentWithReverseRelations([ - $draft1, - $draft2, - $draft3, - ]); - - $contentInfo = $contentWithReverseRelations->content->contentInfo; - - $reverseRelationList = $this->contentService->loadReverseRelationList($contentInfo); - - $this->assertSame(3, $reverseRelationList->totalCount); - $this->assertEquals( - $contentWithReverseRelations->reverseRelations[2]->contentInfo, - $reverseRelationList->items[0]->getRelation()->sourceContentInfo - ); - $this->assertEquals( - $contentWithReverseRelations->reverseRelations[1]->contentInfo, - $reverseRelationList->items[1]->getRelation()->sourceContentInfo - ); - $this->assertEquals( - $contentWithReverseRelations->reverseRelations[0]->contentInfo, - $reverseRelationList->items[2]->getRelation()->sourceContentInfo - ); - } - - /** - * @covers \eZ\Publish\API\Repository\ContentService::loadReverseRelationList - */ - public function testLoadReverseRelationListWithPagination(): void - { - $draft1 = $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo - ); - $draft2 = $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo - ); - $draft3 = $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Baz'], 2)->contentInfo - ); - - $contentWithReverseRelations = $this->createContentWithReverseRelations([ - $draft1, - $draft2, - $draft3, - ]); - - $contentInfo = $contentWithReverseRelations->content->contentInfo; - - $reverseRelationPage1 = $this->contentService->loadReverseRelationList($contentInfo, 0, 2); - $reverseRelationPage2 = $this->contentService->loadReverseRelationList($contentInfo, 2, 2); - $this->assertSame(3, $reverseRelationPage1->totalCount); - $this->assertSame(3, $reverseRelationPage2->totalCount); - $this->assertEquals( - $contentWithReverseRelations->reverseRelations[2]->contentInfo, - $reverseRelationPage1->items[0]->getRelation()->sourceContentInfo - ); - $this->assertEquals( - $contentWithReverseRelations->reverseRelations[1]->contentInfo, - $reverseRelationPage1->items[1]->getRelation()->sourceContentInfo - ); - $this->assertEquals( - $contentWithReverseRelations->reverseRelations[0]->contentInfo, - $reverseRelationPage2->items[0]->getRelation()->sourceContentInfo - ); - } - - /** - * @covers \eZ\Publish\API\Repository\ContentService::loadReverseRelationList - */ - public function testLoadReverseRelationListSkipsArchivedContent(): void - { - $trashService = $this->getRepository()->getTrashService(); - - $draft1 = $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo - ); - $draft2 = $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo - ); - $draft3 = $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Baz'], 2)->contentInfo - ); - - $contentWithReverseRelations = $this->createContentWithReverseRelations([ - $draft1, - $draft2, - $draft3, - ]); - - $locationToTrash = $this->locationService->loadLocation($draft3->contentInfo->mainLocationId); - - // Trashing Content's last Location will change its status to archived, in this case relation from it will not be loaded. - $trashService->trash($locationToTrash); - - $contentInfo = $contentWithReverseRelations->content->contentInfo; - $reverseRelationList = $this->contentService->loadReverseRelationList($contentInfo); - - $this->assertSame(2, $reverseRelationList->totalCount); - $this->assertEquals( - $contentWithReverseRelations->reverseRelations[1]->contentInfo, - $reverseRelationList->items[0]->getRelation()->sourceContentInfo - ); - $this->assertEquals( - $contentWithReverseRelations->reverseRelations[0]->contentInfo, - $reverseRelationList->items[1]->getRelation()->sourceContentInfo - ); - } - - /** - * @covers \eZ\Publish\API\Repository\ContentService::loadReverseRelationList - */ - public function testLoadReverseRelationListSkipsDraftContent() - { - $draft1 = $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo - ); - - $contentWithReverseRelations = $this->createContentWithReverseRelations([$draft1]); - - $contentInfo = $contentWithReverseRelations->content->contentInfo; - - // create a relation, but without publishing it - $draft2 = $this->contentService->createContentDraft( - $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo - ); - $this->contentService->addRelation( - $draft2->getVersionInfo(), - $contentInfo - ); - - $reverseRelationList = $this->contentService->loadReverseRelationList($contentInfo); - - $this->assertSame(1, $reverseRelationList->totalCount); - $this->assertEquals( - $contentWithReverseRelations->reverseRelations[0]->contentInfo, - $reverseRelationList->items[0]->getRelation()->sourceContentInfo - ); - } - - /** - * Test for the deleteRelation() method. - * - * @see \eZ\Publish\API\Repository\ContentService::deleteRelation() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadRelations - */ - public function testDeleteRelation() - { - $draft = $this->createContentDraftVersion1(); - - $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); - $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID); - - // Establish some relations - $this->contentService->addRelation($draft->getVersionInfo(), $media); - $this->contentService->addRelation($draft->getVersionInfo(), $demoDesign); - - // Delete one of the currently created relations - $this->contentService->deleteRelation($draft->getVersionInfo(), $media); - - // The relations array now contains only one element - $relations = $this->contentService->loadRelations($draft->getVersionInfo()); - - $this->assertCount(1, $relations); - } - - /** - * Test for the deleteRelation() method. - * - * @see \eZ\Publish\API\Repository\ContentService::deleteRelation() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteRelation - */ - public function testDeleteRelationThrowsBadStateException() - { - $content = $this->createContentVersion1(); - - // Load the destination object - $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); - - // Create a new draft - $draftVersion2 = $this->contentService->createContentDraft($content->contentInfo); - - // Add a relation - $this->contentService->addRelation($draftVersion2->getVersionInfo(), $media); - - // Publish new version - $contentVersion2 = $this->contentService->publishVersion( - $draftVersion2->getVersionInfo() - ); - - $this->expectException(BadStateException::class); - - // This call will fail with a "BadStateException", because content is published and not a draft. - $this->contentService->deleteRelation( - $contentVersion2->getVersionInfo(), - $media - ); - } - - /** - * Test for the deleteRelation() method. - * - * @see \eZ\Publish\API\Repository\ContentService::deleteRelation() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteRelation - */ - public function testDeleteRelationThrowsInvalidArgumentException() - { - $draft = $this->createContentDraftVersion1(); - - // Load the destination object - $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); - - // This call will fail with a "InvalidArgumentException", because no relation exists between $draft and $media. - $this->expectException(APIInvalidArgumentException::class); - $this->contentService->deleteRelation( - $draft->getVersionInfo(), - $media - ); - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent - */ - public function testCreateContentInTransactionWithRollback() - { - $repository = $this->getRepository(); - - $contentTypeService = $this->getRepository()->getContentTypeService(); - - // Start a transaction - $repository->beginTransaction(); - - try { - $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); - - // Get a content create struct and set mandatory properties - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - $contentCreate->setField('name', 'Sindelfingen forum'); - - $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789'; - $contentCreate->alwaysAvailable = true; - - // Create a new content object - $contentId = $this->contentService->createContent($contentCreate)->id; - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes - $repository->rollback(); - - try { - // This call will fail with a "NotFoundException" - $this->contentService->loadContent($contentId); - } catch (NotFoundException $e) { - // This is expected - return; - } - - $this->fail('Content object still exists after rollback.'); - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent - */ - public function testCreateContentInTransactionWithCommit() - { - $repository = $this->getRepository(); - - $contentTypeService = $repository->getContentTypeService(); - - // Start a transaction - $repository->beginTransaction(); - - try { - $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); - - // Get a content create struct and set mandatory properties - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - $contentCreate->setField('name', 'Sindelfingen forum'); - - $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789'; - $contentCreate->alwaysAvailable = true; - - // Create a new content object - $contentId = $this->contentService->createContent($contentCreate)->id; - - // Commit changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Load the new content object - $content = $this->contentService->loadContent($contentId); - - $this->assertEquals($contentId, $content->id); - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentThrowsNotFoundException - */ - public function testCreateContentWithLocationCreateParameterInTransactionWithRollback() - { - $repository = $this->getRepository(); - - // Start a transaction - $repository->beginTransaction(); - - try { - $draft = $this->createContentDraftVersion1(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - $contentId = $draft->id; - - // Roleback the transaction - $repository->rollback(); - - try { - // This call will fail with a "NotFoundException" - $this->contentService->loadContent($contentId); - } catch (NotFoundException $e) { - return; - } - - $this->fail('Can still load content object after rollback.'); - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentThrowsNotFoundException - */ - public function testCreateContentWithLocationCreateParameterInTransactionWithCommit() - { - $repository = $this->getRepository(); - - // Start a transaction - $repository->beginTransaction(); - - try { - $draft = $this->createContentDraftVersion1(); - - $contentId = $draft->id; - - // Roleback the transaction - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Load the new content object - $content = $this->contentService->loadContent($contentId); - - $this->assertEquals($contentId, $content->id); - } - - /** - * Test for the createContentDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent - */ - public function testCreateContentDraftInTransactionWithRollback() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); - - // Load the user group content object - $content = $this->contentService->loadContent($contentId); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Create a new draft - $drafted = $this->contentService->createContentDraft($content->contentInfo); - - // Store version number for later reuse - $versionNo = $drafted->versionInfo->versionNo; - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback - $repository->rollback(); - - try { - // This call will fail with a "NotFoundException" - $this->contentService->loadContent($contentId, null, $versionNo); - } catch (NotFoundException $e) { - return; - } - - $this->fail('Can still load content draft after rollback'); - } - - /** - * Test for the createContentDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent - */ - public function testCreateContentDraftInTransactionWithCommit() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); - - // Load the user group content object - $content = $this->contentService->loadContent($contentId); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Create a new draft - $drafted = $this->contentService->createContentDraft($content->contentInfo); - - // Store version number for later reuse - $versionNo = $drafted->versionInfo->versionNo; - - // Commit all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - $content = $this->contentService->loadContent($contentId, null, $versionNo); - - $this->assertEquals( - $versionNo, - $content->getVersionInfo()->versionNo - ); - } - - /** - * Test for the publishVersion() method. - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent - */ - public function testPublishVersionInTransactionWithRollback() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); - - // Load the user group content object - $content = $this->contentService->loadContent($contentId); - - // Start a new transaction - $repository->beginTransaction(); - - try { - $draftVersion = $this->contentService->createContentDraft($content->contentInfo)->getVersionInfo(); - - // Publish a new version - $content = $this->contentService->publishVersion($draftVersion); - - // Store version number for later reuse - $versionNo = $content->versionInfo->versionNo; - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback - $repository->rollback(); - - try { - // This call will fail with a "NotFoundException" - $this->contentService->loadContent($contentId, null, $versionNo); - } catch (NotFoundException $e) { - return; - } - - $this->fail('Can still load content draft after rollback'); - } - - /** - * Test for the publishVersion() method. - * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfo - */ - public function testPublishVersionInTransactionWithCommit() - { - $repository = $this->getRepository(); - - // Load the user group content object - $template = $this->contentService->loadContent(self::ADMINISTRATORS_USER_GROUP_ID); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Publish a new version - $content = $this->contentService->publishVersion( - $this->contentService->createContentDraft($template->contentInfo)->getVersionInfo() - ); - - // Store version number for later reuse - $versionNo = $content->versionInfo->versionNo; - - // Commit all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Load current version info - $versionInfo = $this->contentService->loadVersionInfo($content->contentInfo); - - $this->assertEquals($versionNo, $versionInfo->versionNo); - } - - /** - * Test for the updateContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - */ - public function testUpdateContentInTransactionWithRollback() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); - - // Create a new user group draft - $draft = $this->contentService->createContentDraft( - $this->contentService->loadContentInfo($contentId) - ); - - // Get an update struct and change the group name - $contentUpdate = $this->contentService->newContentUpdateStruct(); - $contentUpdate->setField('name', self::ADMINISTRATORS_USER_GROUP_NAME, self::ENG_US); - - // Start a transaction - $repository->beginTransaction(); - - try { - // Update the group name - $draft = $this->contentService->updateContent( - $draft->getVersionInfo(), - $contentUpdate - ); - - // Publish updated version - $this->contentService->publishVersion($draft->getVersionInfo()); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes. - $repository->rollback(); - - // Name will still be "Administrator users" - $name = $this->contentService->loadContent($contentId)->getFieldValue('name'); - - $this->assertEquals('Administrator users', $name); - } - - /** - * Test for the updateContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - */ - public function testUpdateContentInTransactionWithCommit() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); - - // Create a new user group draft - $draft = $this->contentService->createContentDraft( - $this->contentService->loadContentInfo($contentId) - ); - - // Get an update struct and change the group name - $contentUpdate = $this->contentService->newContentUpdateStruct(); - $contentUpdate->setField('name', self::ADMINISTRATORS_USER_GROUP_NAME, self::ENG_US); - - // Start a transaction - $repository->beginTransaction(); - - try { - // Update the group name - $draft = $this->contentService->updateContent( - $draft->getVersionInfo(), - $contentUpdate - ); - - // Publish updated version - $this->contentService->publishVersion($draft->getVersionInfo()); - - // Commit all changes. - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Name is now "Administrators" - $name = $this->contentService->loadContent($contentId)->getFieldValue('name', self::ENG_US); - - $this->assertEquals(self::ADMINISTRATORS_USER_GROUP_NAME, $name); - } - - /** - * Test for the updateContentMetadata() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - */ - public function testUpdateContentMetadataInTransactionWithRollback() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); - - // Load a ContentInfo object - $contentInfo = $this->contentService->loadContentInfo($contentId); - - // Store remoteId for later testing - $remoteId = $contentInfo->remoteId; - - // Start a transaction - $repository->beginTransaction(); - - try { - // Get metadata update struct and change remoteId - $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct(); - $metadataUpdate->remoteId = md5(microtime(true)); - - // Update the metadata of the published content object - $this->contentService->updateContentMetadata( - $contentInfo, - $metadataUpdate - ); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes. - $repository->rollback(); - - // Load current remoteId - $remoteIdReloaded = $this->contentService->loadContentInfo($contentId)->remoteId; - - $this->assertEquals($remoteId, $remoteIdReloaded); - } - - /** - * Test for the updateContentMetadata() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - */ - public function testUpdateContentMetadataInTransactionWithCommit() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); - - // Load a ContentInfo object - $contentInfo = $this->contentService->loadContentInfo($contentId); - - // Store remoteId for later testing - $remoteId = $contentInfo->remoteId; - - // Start a transaction - $repository->beginTransaction(); - - try { - // Get metadata update struct and change remoteId - $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct(); - $metadataUpdate->remoteId = md5(microtime(true)); - - // Update the metadata of the published content object - $this->contentService->updateContentMetadata( - $contentInfo, - $metadataUpdate - ); - - // Commit all changes. - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Load current remoteId - $remoteIdReloaded = $this->contentService->loadContentInfo($contentId)->remoteId; - - $this->assertNotEquals($remoteId, $remoteIdReloaded); - } - - /** - * Test for the updateContentMetadata() method, and how cache + transactions play together. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata() - * @depends testUpdateContentMetadata - * @depends testLoadContentInfo - */ - public function testUpdateContentMetadataCheckWithinTransaction() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $contentId = $this->generateId('object', 12); - - // Load a ContentInfo object, and warmup cache - $contentInfo = $contentService->loadContentInfo($contentId); - - // Store remoteId for later testing - $remoteId = $contentInfo->remoteId; - - // Start a transaction - $repository->beginTransaction(); - - try { - // Get metadata update struct and change remoteId - $metadataUpdate = $contentService->newContentMetadataUpdateStruct(); - $metadataUpdate->remoteId = md5(microtime(true)); - - // Update the metadata of the published content object - $contentService->updateContentMetadata( - $contentInfo, - $metadataUpdate - ); - - // Check that it's been updated - $remoteIdReloaded = $contentService->loadContentInfo($contentId)->remoteId; - $this->assertNotEquals($remoteId, $remoteIdReloaded); - - // Commit all changes. - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - } - - /** - * Test for the deleteVersion() method. - * - * @see \eZ\Publish\API\Repository\ContentService::deleteVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentDrafts - */ - public function testDeleteVersionInTransactionWithRollback() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Create a new draft - $draft = $this->contentService->createContentDraft( - $this->contentService->loadContentInfo($contentId) - ); - - $this->contentService->deleteVersion($draft->getVersionInfo()); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes. - $repository->rollback(); - - // This array will be empty - $drafts = $this->contentService->loadContentDrafts(); - - $this->assertSame([], $drafts); - } - - /** - * Test for the deleteVersion() method. - * - * @see \eZ\Publish\API\Repository\ContentService::deleteVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentDrafts - */ - public function testDeleteVersionInTransactionWithCommit() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Create a new draft - $draft = $this->contentService->createContentDraft( - $this->contentService->loadContentInfo($contentId) - ); - - $this->contentService->deleteVersion($draft->getVersionInfo()); - - // Commit all changes. - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // This array will contain no element - $drafts = $this->contentService->loadContentDrafts(); - - $this->assertSame([], $drafts); - } - - /** - * Test for the deleteContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::deleteContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - */ - public function testDeleteContentInTransactionWithRollback() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID); - - // Load a ContentInfo instance - $contentInfo = $this->contentService->loadContentInfo($contentId); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Delete content object - $this->contentService->deleteContent($contentInfo); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes - $repository->rollback(); - - // This call will return the original content object - $contentInfo = $this->contentService->loadContentInfo($contentId); - - $this->assertEquals($contentId, $contentInfo->id); - } - - /** - * Test for the deleteContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::deleteContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteContent - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - */ - public function testDeleteContentInTransactionWithCommit() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID); - - // Load a ContentInfo instance - $contentInfo = $this->contentService->loadContentInfo($contentId); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Delete content object - $this->contentService->deleteContent($contentInfo); - - // Commit all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Deleted content info is not found anymore - try { - $this->contentService->loadContentInfo($contentId); - } catch (NotFoundException $e) { - return; - } - - $this->fail('Can still load ContentInfo after commit.'); - } - - /** - * Test for the copyContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::copyContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCopyContent - * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testNewLocationCreateStruct - * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationChildren - * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testCopyContentInTransactionWithRollback() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID); - $locationId = $this->generateId('location', self::ADMINISTRATORS_USER_GROUP_LOCATION_ID); - - // Load content object to copy - $content = $this->contentService->loadContent($contentId); - - // Create new target location - $locationCreate = $this->locationService->newLocationCreateStruct($locationId); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Copy content with all versions and drafts - $this->contentService->copyContent( - $content->contentInfo, - $locationCreate - ); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes - $repository->rollback(); - - $this->refreshSearch($repository); - - // This array will only contain a single admin user object - $locations = $this->locationService->loadLocationChildren( - $this->locationService->loadLocation($locationId) - )->locations; - - $this->assertCount(1, $locations); - } - - /** - * Test for the copyContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::copyContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCopyContent - * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testNewLocationCreateStruct - * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationChildren - * @depend(s) eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testCopyContentInTransactionWithCommit() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID); - $locationId = $this->generateId('location', self::ADMINISTRATORS_USER_GROUP_LOCATION_ID); - - // Load content object to copy - $content = $this->contentService->loadContent($contentId); - - // Create new target location - $locationCreate = $this->locationService->newLocationCreateStruct($locationId); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Copy content with all versions and drafts - $this->contentService->copyContent( - $content->contentInfo, - $locationCreate - ); - - // Commit all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - $this->refreshSearch($repository); - - // This will contain the admin user and the new child location - $locations = $this->locationService->loadLocationChildren( - $this->locationService->loadLocation($locationId) - )->locations; - - $this->assertCount(2, $locations); - } - - public function testURLAliasesCreatedForNewContent() - { - $urlAliasService = $this->getRepository()->getURLAliasService(); - - $draft = $this->createContentDraftVersion1(); - - // Automatically creates a new URLAlias for the content - $liveContent = $this->contentService->publishVersion($draft->getVersionInfo()); - - $location = $this->locationService->loadLocation( - $liveContent->getVersionInfo()->getContentInfo()->mainLocationId - ); - - $aliases = $urlAliasService->listLocationAliases($location, false); - - $this->assertAliasesCorrect( - [ - '/Design/Plain-site/An-awesome-forum' => [ - 'type' => URLAlias::LOCATION, - 'destination' => $location->id, - 'path' => '/Design/Plain-site/An-awesome-forum', - 'languageCodes' => [self::ENG_US], - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ], - ], - $aliases - ); - } - - public function testURLAliasesCreatedForUpdatedContent() - { - $urlAliasService = $this->getRepository()->getURLAliasService(); - - $draft = $this->createUpdatedDraftVersion2(); - - $location = $this->locationService->loadLocation( - $draft->getVersionInfo()->getContentInfo()->mainLocationId - ); - - // Load and assert URL aliases before publishing updated Content, so that - // SPI cache is warmed up and cache invalidation is also tested. - $aliases = $urlAliasService->listLocationAliases($location, false); - - $this->assertAliasesCorrect( - [ - '/Design/Plain-site/An-awesome-forum' => [ - 'type' => URLAlias::LOCATION, - 'destination' => $location->id, - 'path' => '/Design/Plain-site/An-awesome-forum', - 'languageCodes' => [self::ENG_US], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ], - ], - $aliases - ); - - // Automatically marks old aliases for the content as history - // and creates new aliases, based on the changes - $liveContent = $this->contentService->publishVersion($draft->getVersionInfo()); - - $location = $this->locationService->loadLocation( - $liveContent->getVersionInfo()->getContentInfo()->mainLocationId - ); - - $aliases = $urlAliasService->listLocationAliases($location, false); - - $this->assertAliasesCorrect( - [ - '/Design/Plain-site/An-awesome-forum2' => [ - 'type' => URLAlias::LOCATION, - 'destination' => $location->id, - 'path' => '/Design/Plain-site/An-awesome-forum2', - 'languageCodes' => [self::ENG_US], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ], - '/Design/Plain-site/An-awesome-forum23' => [ - 'type' => URLAlias::LOCATION, - 'destination' => $location->id, - 'path' => '/Design/Plain-site/An-awesome-forum23', - 'languageCodes' => [self::ENG_GB], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ], - ], - $aliases - ); - } - - public function testCustomURLAliasesNotHistorizedOnUpdatedContent() - { - $urlAliasService = $this->getRepository()->getURLAliasService(); - - $content = $this->createContentVersion1(); - - // Create a custom URL alias - $urlAliasService->createUrlAlias( - $this->locationService->loadLocation( - $content->getVersionInfo()->getContentInfo()->mainLocationId - ), - '/my/fancy/story-about-ez-publish', - self::ENG_US - ); - - $draftVersion2 = $this->contentService->createContentDraft($content->contentInfo); - - $contentUpdate = $this->contentService->newContentUpdateStruct(); - $contentUpdate->initialLanguageCode = self::ENG_US; - $contentUpdate->setField('name', 'Amazing Bielefeld forum'); - - $draftVersion2 = $this->contentService->updateContent( - $draftVersion2->getVersionInfo(), - $contentUpdate - ); - - // Only marks auto-generated aliases as history - // the custom one is left untouched - $liveContent = $this->contentService->publishVersion($draftVersion2->getVersionInfo()); - - $location = $this->locationService->loadLocation( - $liveContent->getVersionInfo()->getContentInfo()->mainLocationId - ); - - $aliases = $urlAliasService->listLocationAliases($location); - - $this->assertAliasesCorrect( - [ - '/my/fancy/story-about-ez-publish' => [ - 'type' => URLAlias::LOCATION, - 'destination' => $location->id, - 'path' => '/my/fancy/story-about-ez-publish', - 'languageCodes' => [self::ENG_US], - 'isHistory' => false, - 'isCustom' => true, - 'forward' => false, - 'alwaysAvailable' => false, - ], - ], - $aliases - ); - } - - /** - * Test to ensure that old versions are not affected by updates to newer - * drafts. - */ - public function testUpdatingDraftDoesNotUpdateOldVersions() - { - $contentVersion2 = $this->createContentVersion2(); - - $loadedContent1 = $this->contentService->loadContent($contentVersion2->id, null, 1); - $loadedContent2 = $this->contentService->loadContent($contentVersion2->id, null, 2); - - $this->assertNotEquals( - $loadedContent1->getFieldValue('name', self::ENG_US), - $loadedContent2->getFieldValue('name', self::ENG_US) - ); - } - - /** - * Test scenario with writer and publisher users. - * Writer can only create content. Publisher can publish this content. - */ - public function testPublishWorkflow() - { - $this->createRoleWithPolicies('Publisher', [ - ['module' => 'content', 'function' => 'read'], - ['module' => 'content', 'function' => 'create'], - ['module' => 'content', 'function' => 'publish'], - ]); - - $this->createRoleWithPolicies('Writer', [ - ['module' => 'content', 'function' => 'read'], - ['module' => 'content', 'function' => 'create'], - ]); - - $writerUser = $this->createCustomUserWithLogin( - 'writer', - 'writer@example.com', - self::WRITERS_USER_GROUP_NAME, - 'Writer' - ); - - $publisherUser = $this->createCustomUserWithLogin( - 'publisher', - 'publisher@example.com', - 'Publishers', - 'Publisher' - ); - - $this->permissionResolver->setCurrentUserReference($writerUser); - $draft = $this->createContentDraftVersion1(); - - $this->permissionResolver->setCurrentUserReference($publisherUser); - $content = $this->contentService->publishVersion($draft->versionInfo); - - $this->contentService->loadContent($content->id); - } - - /** - * Test publish / content policy is required to be able to publish content. - */ - public function testPublishContentWithoutPublishPolicyThrowsException() - { - $this->createRoleWithPolicies('Writer', [ - ['module' => 'content', 'function' => 'read'], - ['module' => 'content', 'function' => 'create'], - ['module' => 'content', 'function' => 'edit'], - ]); - $writerUser = $this->createCustomUserWithLogin( - 'writer', - 'writer@example.com', - self::WRITERS_USER_GROUP_NAME, - 'Writer' - ); - $this->permissionResolver->setCurrentUserReference($writerUser); - - $this->expectException(CoreUnauthorizedException::class); - $this->expectExceptionMessageMatches('/The User does not have the \'publish\' \'content\' permission/'); - - $this->createContentVersion1(); - } - - /** - * Test removal of the specific translation from all the Versions of a Content Object. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation - */ - public function testDeleteTranslation() - { - $content = $this->createContentVersion2(); - - // create multiple versions to exceed archive limit - for ($i = 0; $i < 5; ++$i) { - $contentDraft = $this->contentService->createContentDraft($content->contentInfo); - $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); - $contentDraft = $this->contentService->updateContent( - $contentDraft->versionInfo, - $contentUpdateStruct - ); - $this->contentService->publishVersion($contentDraft->versionInfo); - } - - $this->contentService->deleteTranslation($content->contentInfo, self::ENG_GB); - - $this->assertTranslationDoesNotExist(self::ENG_GB, $content->id); - } - - /** - * Test deleting a Translation which is initial for some Version, updates initialLanguageCode - * with mainLanguageCode (assuming they are different). - */ - public function testDeleteTranslationUpdatesInitialLanguageCodeVersion() - { - $content = $this->createContentVersion2(); - // create another, copied, version - $contentDraft = $this->contentService->updateContent( - $this->contentService->createContentDraft($content->contentInfo)->versionInfo, - $this->contentService->newContentUpdateStruct() - ); - $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo); - - // remove first version with only one translation as it is not the subject of this test - $this->contentService->deleteVersion( - $this->contentService->loadVersionInfo($publishedContent->contentInfo, 1) - ); - - // sanity check - self::assertEquals(self::ENG_US, $content->contentInfo->mainLanguageCode); - self::assertEquals(self::ENG_US, $content->versionInfo->initialLanguageCode); - - // update mainLanguageCode so it is different than initialLanguageCode for Version - $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct(); - $contentMetadataUpdateStruct->mainLanguageCode = self::ENG_GB; - $content = $this->contentService->updateContentMetadata($publishedContent->contentInfo, $contentMetadataUpdateStruct); - - $this->contentService->deleteTranslation($content->contentInfo, self::ENG_US); - - $this->assertTranslationDoesNotExist(self::ENG_US, $content->id); - } - - /** - * Test removal of the specific translation properly updates languages of the URL alias. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation - */ - public function testDeleteTranslationUpdatesUrlAlias() - { - $urlAliasService = $this->getRepository()->getURLAliasService(); - - $content = $this->createContentVersion2(); - $mainLocation = $this->locationService->loadLocation($content->contentInfo->mainLocationId); - - // create custom URL alias for Content main Location - $urlAliasService->createUrlAlias($mainLocation, '/my-custom-url', self::ENG_GB); - - // create secondary Location for Content - $secondaryLocation = $this->locationService->createLocation( - $content->contentInfo, - $this->locationService->newLocationCreateStruct(2) - ); - - // create custom URL alias for Content secondary Location - $urlAliasService->createUrlAlias($secondaryLocation, '/my-secondary-url', self::ENG_GB); - - // delete Translation - $this->contentService->deleteTranslation($content->contentInfo, self::ENG_GB); - - foreach ([$mainLocation, $secondaryLocation] as $location) { - // check auto-generated URL aliases - foreach ($urlAliasService->listLocationAliases($location, false) as $alias) { - self::assertNotContains(self::ENG_GB, $alias->languageCodes); - } - - // check custom URL aliases - foreach ($urlAliasService->listLocationAliases($location) as $alias) { - self::assertNotContains(self::ENG_GB, $alias->languageCodes); - } - } - } - - /** - * Test removal of a main translation throws BadStateException. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation - */ - public function testDeleteTranslationMainLanguageThrowsBadStateException() - { - $content = $this->createContentVersion2(); - - // delete first version which has only one translation - $this->contentService->deleteVersion($this->contentService->loadVersionInfo($content->contentInfo, 1)); - - // try to delete main translation - $this->expectException(BadStateException::class); - $this->expectExceptionMessage('The provided translation is the main translation of the Content item'); - - $this->contentService->deleteTranslation($content->contentInfo, $content->contentInfo->mainLanguageCode); - } - - /** - * Test removal of a Translation is possible when some archived Versions have only this Translation. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation - */ - public function testDeleteTranslationDeletesSingleTranslationVersions() - { - // content created by the createContentVersion1 method has eng-US translation only. - $content = $this->createContentVersion1(); - - // create new version and add eng-GB translation - $contentDraft = $this->contentService->createContentDraft($content->contentInfo); - $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); - $contentUpdateStruct->setField('name', 'Awesome Board', self::ENG_GB); - $contentDraft = $this->contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct); - $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo); - - // update mainLanguageCode to avoid exception related to that - $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct(); - $contentMetadataUpdateStruct->mainLanguageCode = self::ENG_GB; - - $content = $this->contentService->updateContentMetadata($publishedContent->contentInfo, $contentMetadataUpdateStruct); - - $this->contentService->deleteTranslation($content->contentInfo, self::ENG_US); - - $this->assertTranslationDoesNotExist(self::ENG_US, $content->id); - } - - /** - * Test removal of the translation by the user who is not allowed to delete a content - * throws UnauthorizedException. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation - */ - public function testDeleteTranslationThrowsUnauthorizedException() - { - $content = $this->createContentVersion2(); - - // create user that can read/create/edit but cannot delete content - $this->createRoleWithPolicies('Writer', [ - ['module' => 'content', 'function' => 'read'], - ['module' => 'content', 'function' => 'versionread'], - ['module' => 'content', 'function' => 'create'], - ['module' => 'content', 'function' => 'edit'], - ]); - $writerUser = $this->createCustomUserWithLogin( - 'writer', - 'writer@example.com', - self::WRITERS_USER_GROUP_NAME, - 'Writer' - ); - $this->permissionResolver->setCurrentUserReference($writerUser); - - $this->expectException(UnauthorizedException::class); - $this->expectExceptionMessage('The User does not have the \'remove\' \'content\' permission'); - - $this->contentService->deleteTranslation($content->contentInfo, self::ENG_GB); - } - - /** - * Test removal of a non-existent translation throws InvalidArgumentException. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslation - */ - public function testDeleteTranslationThrowsInvalidArgumentException() - { - // content created by the createContentVersion1 method has eng-US translation only. - $content = $this->createContentVersion1(); - - $this->expectException(APIInvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$languageCode\' is invalid: ger-DE does not exist in the Content item'); - - $this->contentService->deleteTranslation($content->contentInfo, self::GER_DE); - } - - /** - * Test deleting a Translation from Draft. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft - */ - public function testDeleteTranslationFromDraft() - { - $languageCode = self::ENG_GB; - $content = $this->createMultipleLanguageContentVersion2(); - $draft = $this->contentService->createContentDraft($content->contentInfo); - $draft = $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode); - $content = $this->contentService->publishVersion($draft->versionInfo); - - $loadedContent = $this->contentService->loadContent($content->id); - self::assertNotContains($languageCode, $loadedContent->versionInfo->languageCodes); - self::assertEmpty($loadedContent->getFieldsByLanguage($languageCode)); - } - - /** - * Get values for multilingual field. - * - * @return array - */ - public function providerForDeleteTranslationFromDraftRemovesUrlAliasOnPublishing() - { - return [ - [ - [self::ENG_US => 'US Name', self::ENG_GB => 'GB Name'], - ], - [ - [self::ENG_US => 'Same Name', self::ENG_GB => 'Same Name'], - ], - ]; - } - - /** - * Test deleting a Translation from Draft removes previously stored URL aliases for published Content. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft - * - * @dataProvider providerForDeleteTranslationFromDraftRemovesUrlAliasOnPublishing - * - * @param string[] $fieldValues translated field values - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testDeleteTranslationFromDraftRemovesUrlAliasOnPublishing(array $fieldValues) - { - $urlAliasService = $this->getRepository()->getURLAliasService(); - - // set language code to be removed - $languageCode = self::ENG_GB; - $draft = $this->createMultilingualContentDraft( - 'folder', - 2, - self::ENG_US, - [ - 'name' => [ - self::ENG_GB => $fieldValues[self::ENG_GB], - self::ENG_US => $fieldValues[self::ENG_US], - ], - ] - ); - $content = $this->contentService->publishVersion($draft->versionInfo); - - // create secondary location - $this->locationService->createLocation( - $content->contentInfo, - $this->locationService->newLocationCreateStruct(5) - ); - - // sanity check - $locations = $this->locationService->loadLocations($content->contentInfo); - self::assertCount(2, $locations, 'Sanity check: Expected to find 2 Locations'); - foreach ($locations as $location) { - $urlAliasService->createUrlAlias($location, '/us-custom_' . $location->id, self::ENG_US); - $urlAliasService->createUrlAlias($location, '/gb-custom_' . $location->id, self::ENG_GB); - - // check default URL aliases - $aliases = $urlAliasService->listLocationAliases($location, false, $languageCode); - self::assertNotEmpty($aliases, 'Sanity check: URL alias for the translation does not exist'); - - // check custom URL aliases - $aliases = $urlAliasService->listLocationAliases($location, true, $languageCode); - self::assertNotEmpty($aliases, 'Sanity check: Custom URL alias for the translation does not exist'); - } - - // delete translation and publish new version - $draft = $this->contentService->createContentDraft($content->contentInfo); - $draft = $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode); - $this->contentService->publishVersion($draft->versionInfo); - - // check that aliases does not exist - foreach ($locations as $location) { - // check default URL aliases - $aliases = $urlAliasService->listLocationAliases($location, false, $languageCode); - self::assertEmpty($aliases, 'URL alias for the deleted translation still exists'); - - // check custom URL aliases - $aliases = $urlAliasService->listLocationAliases($location, true, $languageCode); - self::assertEmpty($aliases, 'Custom URL alias for the deleted translation still exists'); - } - } - - /** - * Test that URL aliases for deleted Translations are properly archived. - */ - public function testDeleteTranslationFromDraftArchivesUrlAliasOnPublishing() - { - $urlAliasService = $this->getRepository()->getURLAliasService(); - - $content = $this->contentService->publishVersion( - $this->createMultilingualContentDraft( - 'folder', - 2, - self::ENG_US, - [ - 'name' => [ - self::ENG_GB => 'BritishEnglishContent', - self::ENG_US => 'AmericanEnglishContent', - ], - ] - )->versionInfo - ); - - $unrelatedContent = $this->contentService->publishVersion( - $this->createMultilingualContentDraft( - 'folder', - 2, - self::ENG_US, - [ - 'name' => [ - self::ENG_GB => 'AnotherBritishContent', - self::ENG_US => 'AnotherAmericanContent', - ], - ] - )->versionInfo - ); - - $urlAlias = $urlAliasService->lookup('/BritishEnglishContent'); - self::assertFalse($urlAlias->isHistory); - self::assertEquals($urlAlias->path, '/BritishEnglishContent'); - self::assertEquals($urlAlias->destination, $content->contentInfo->mainLocationId); - - $draft = $this->contentService->deleteTranslationFromDraft( - $this->contentService->createContentDraft($content->contentInfo)->versionInfo, - self::ENG_GB - ); - $content = $this->contentService->publishVersion($draft->versionInfo); - - $urlAlias = $urlAliasService->lookup('/BritishEnglishContent'); - self::assertTrue($urlAlias->isHistory); - self::assertEquals($urlAlias->path, '/BritishEnglishContent'); - self::assertEquals($urlAlias->destination, $content->contentInfo->mainLocationId); - - $unrelatedUrlAlias = $urlAliasService->lookup('/AnotherBritishContent'); - self::assertFalse($unrelatedUrlAlias->isHistory); - self::assertEquals($unrelatedUrlAlias->path, '/AnotherBritishContent'); - self::assertEquals($unrelatedUrlAlias->destination, $unrelatedContent->contentInfo->mainLocationId); - } - - /** - * Test deleting a Translation from Draft which has single Translation throws BadStateException. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft - */ - public function testDeleteTranslationFromDraftThrowsBadStateExceptionOnSingleTranslation() - { - // create Content with single Translation - $publishedContent = $this->contentService->publishVersion( - $this->createContentDraft( - self::FORUM_IDENTIFIER, - 2, - ['name' => 'Eng-US Version name'] - )->versionInfo - ); - - // update mainLanguageCode to avoid exception related to trying to delete main Translation - $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct(); - $contentMetadataUpdateStruct->mainLanguageCode = self::ENG_GB; - $publishedContent = $this->contentService->updateContentMetadata( - $publishedContent->contentInfo, - $contentMetadataUpdateStruct - ); - - // create single Translation Version from the first one - $draft = $this->contentService->createContentDraft( - $publishedContent->contentInfo, - $publishedContent->versionInfo - ); - - $this->expectException(BadStateException::class); - $this->expectExceptionMessage('The provided translation is the only translation in this version'); - - // attempt to delete Translation - $this->contentService->deleteTranslationFromDraft($draft->versionInfo, self::ENG_US); - } - - /** - * Test deleting the Main Translation from Draft throws BadStateException. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft - */ - public function testDeleteTranslationFromDraftThrowsBadStateExceptionOnMainTranslation() - { - $mainLanguageCode = self::ENG_US; - $draft = $this->createMultilingualContentDraft( - self::FORUM_IDENTIFIER, - 2, - $mainLanguageCode, - [ - 'name' => [ - self::ENG_US => 'An awesome eng-US forum', - self::ENG_GB => 'An awesome eng-GB forum', - ], - ] - ); - - $this->expectException(BadStateException::class); - $this->expectExceptionMessage('the specified translation is the main translation of the Content item'); - - $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $mainLanguageCode); - } - - /** - * Test deleting the Translation from Published Version throws BadStateException. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft - */ - public function testDeleteTranslationFromDraftThrowsBadStateExceptionOnPublishedVersion() - { - $languageCode = self::ENG_US; - $content = $this->createMultipleLanguageContentVersion2(); - $draft = $this->contentService->createContentDraft($content->contentInfo); - $publishedContent = $this->contentService->publishVersion($draft->versionInfo); - - $this->expectException(BadStateException::class); - $this->expectExceptionMessage('The version is not a draft'); - - $this->contentService->deleteTranslationFromDraft($publishedContent->versionInfo, $languageCode); - } - - /** - * Test deleting a Translation from Draft throws UnauthorizedException if user cannot edit Content. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft - */ - public function testDeleteTranslationFromDraftThrowsUnauthorizedException() - { - $languageCode = self::ENG_GB; - $content = $this->createMultipleLanguageContentVersion2(); - $draft = $this->contentService->createContentDraft($content->contentInfo); - - // create user that can read/create/delete but cannot edit or content - $this->createRoleWithPolicies('Writer', [ - ['module' => 'content', 'function' => 'read'], - ['module' => 'content', 'function' => 'versionread'], - ['module' => 'content', 'function' => 'create'], - ['module' => 'content', 'function' => 'delete'], - ]); - $writerUser = $this->createCustomUserWithLogin( - 'user', - 'user@example.com', - self::WRITERS_USER_GROUP_NAME, - 'Writer' - ); - $this->permissionResolver->setCurrentUserReference($writerUser); - - $this->expectException(UnauthorizedException::class); - $this->expectExceptionMessage('The User does not have the \'edit\' \'content\' permission'); - - $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode); - } - - /** - * Test deleting a non-existent Translation from Draft throws InvalidArgumentException. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteTranslationFromDraft - */ - public function testDeleteTranslationFromDraftThrowsInvalidArgumentException() - { - $languageCode = self::GER_DE; - $content = $this->createMultipleLanguageContentVersion2(); - $draft = $this->contentService->createContentDraft($content->contentInfo); - $this->expectException(APIInvalidArgumentException::class); - $this->expectExceptionMessageMatches('/The version \(ContentId=\d+, VersionNo=\d+\) is not translated into ger-DE/'); - $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode); - } - - /** - * Test loading list of Content items. - */ - public function testLoadContentListByContentInfo() - { - $allLocationsCount = $this->locationService->getAllLocationsCount(); - $contentInfoList = array_map( - static function (Location $location) { - return $location->contentInfo; - }, - $this->locationService->loadAllLocations(0, $allLocationsCount) - ); - - $contentList = $this->contentService->loadContentListByContentInfo($contentInfoList); - self::assertCount(count($contentInfoList), $contentList); - foreach ($contentList as $content) { - try { - $loadedContent = $this->contentService->loadContent($content->id); - self::assertEquals($loadedContent, $content, "Failed to properly bulk-load Content {$content->id}"); - } catch (NotFoundException $e) { - self::fail("Failed to load Content {$content->id}: {$e->getMessage()}"); - } catch (UnauthorizedException $e) { - self::fail("Failed to load Content {$content->id}: {$e->getMessage()}"); - } - } - } - - /** - * Test loading content versions after removing exactly two drafts. - * - * @see https://jira.ez.no/browse/EZP-30271 - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteVersion - */ - public function testLoadVersionsAfterDeletingTwoDrafts() - { - $content = $this->createFolder([self::ENG_GB => 'Foo'], 2); - - // First update and publish - $modifiedContent = $this->updateFolder($content, [self::ENG_GB => 'Foo1']); - $content = $this->contentService->publishVersion($modifiedContent->versionInfo); - - // Second update and publish - $modifiedContent = $this->updateFolder($content, [self::ENG_GB => 'Foo2']); - $content = $this->contentService->publishVersion($modifiedContent->versionInfo); - - // Create drafts - $this->updateFolder($content, [self::ENG_GB => 'Foo3']); - $this->updateFolder($content, [self::ENG_GB => 'Foo4']); - - $versions = $this->contentService->loadVersions($content->contentInfo); - - foreach ($versions as $key => $version) { - if ($version->isDraft()) { - $this->contentService->deleteVersion($version); - unset($versions[$key]); - } - } - - $this->assertEquals($versions, $this->contentService->loadVersions($content->contentInfo)); - } - - /** - * Tests loading list of content versions of status draft. - */ - public function testLoadVersionsOfStatusDraft() - { - $content = $this->createContentVersion1(); - - $this->contentService->createContentDraft($content->contentInfo); - $this->contentService->createContentDraft($content->contentInfo); - $this->contentService->createContentDraft($content->contentInfo); - - $versions = $this->contentService->loadVersions($content->contentInfo, VersionInfo::STATUS_DRAFT); - - $this->assertSame(\count($versions), 3); - } - - /** - * Tests loading list of content versions of status archived. - */ - public function testLoadVersionsOfStatusArchived() - { - $content = $this->createContentVersion1(); - - $draft1 = $this->contentService->createContentDraft($content->contentInfo); - $this->contentService->publishVersion($draft1->versionInfo); - - $draft2 = $this->contentService->createContentDraft($content->contentInfo); - $this->contentService->publishVersion($draft2->versionInfo); - - $versions = $this->contentService->loadVersions($content->contentInfo, VersionInfo::STATUS_ARCHIVED); - - $this->assertSame(\count($versions), 2); - } - - /** - * Asserts that all aliases defined in $expectedAliasProperties with the - * given properties are available in $actualAliases and not more. - * - * @param array $expectedAliasProperties - * @param array $actualAliases - */ - private function assertAliasesCorrect(array $expectedAliasProperties, array $actualAliases) - { - foreach ($actualAliases as $actualAlias) { - if (!isset($expectedAliasProperties[$actualAlias->path])) { - $this->fail( - sprintf( - 'Alias with path "%s" in languages "%s" not expected.', - $actualAlias->path, - implode(', ', $actualAlias->languageCodes) - ) - ); - } - - foreach ($expectedAliasProperties[$actualAlias->path] as $propertyName => $propertyValue) { - $this->assertEquals( - $propertyValue, - $actualAlias->$propertyName, - sprintf( - 'Property $%s incorrect for alias with path "%s" in languages "%s".', - $propertyName, - $actualAlias->path, - implode(', ', $actualAlias->languageCodes) - ) - ); - } - - unset($expectedAliasProperties[$actualAlias->path]); - } - - if (!empty($expectedAliasProperties)) { - $this->fail( - sprintf( - 'Missing expected aliases with paths "%s".', - implode('", "', array_keys($expectedAliasProperties)) - ) - ); - } - } - - /** - * Asserts that the given fields are equal to the default fields fixture. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $fields - */ - private function assertAllFieldsEquals(array $fields) - { - $actual = $this->normalizeFields($fields); - $expected = $this->normalizeFields($this->createFieldsFixture()); - - $this->assertEquals($expected, $actual); - } - - /** - * Asserts that the given fields are equal to a language filtered set of the - * default fields fixture. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $fields - * @param string $languageCode - */ - private function assertLocaleFieldsEquals(array $fields, $languageCode) - { - $actual = $this->normalizeFields($fields); - - $expected = []; - foreach ($this->normalizeFields($this->createFieldsFixture()) as $field) { - if ($field->languageCode !== $languageCode) { - continue; - } - $expected[] = $field; - } - - $this->assertEquals($expected, $actual); - } - - /** - * This method normalizes a set of fields and returns a normalized set. - * - * Normalization means it resets the storage specific field id to zero and - * it sorts the field by their identifier and their language code. In - * addition, the field value is removed, since this one depends on the - * specific FieldType, which is tested in a dedicated integration test. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $fields - * - * @return \eZ\Publish\API\Repository\Values\Content\Field[] - */ - private function normalizeFields(array $fields) - { - $normalized = []; - foreach ($fields as $field) { - $normalized[] = new Field( - [ - 'id' => 0, - 'value' => $field->value !== null, - 'languageCode' => $field->languageCode, - 'fieldDefIdentifier' => $field->fieldDefIdentifier, - 'fieldTypeIdentifier' => $field->fieldTypeIdentifier, - ] - ); - } - usort( - $normalized, - static function ($field1, $field2) { - if (0 === ($return = strcasecmp($field1->fieldDefIdentifier, $field2->fieldDefIdentifier))) { - return strcasecmp($field1->languageCode, $field2->languageCode); - } - - return $return; - } - ); - - return $normalized; - } - - /** - * Asserts that given Content has default ContentStates. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - */ - private function assertDefaultContentStates(ContentInfo $contentInfo) - { - $objectStateService = $this->getRepository()->getObjectStateService(); - - $objectStateGroups = $objectStateService->loadObjectStateGroups(); - - foreach ($objectStateGroups as $objectStateGroup) { - $contentState = $objectStateService->getContentState($contentInfo, $objectStateGroup); - foreach ($objectStateService->loadObjectStates($objectStateGroup, Language::ALL) as $objectState) { - // Only check the first object state which is the default one. - $this->assertEquals( - $objectState, - $contentState - ); - break; - } - } - } - - /** - * Assert that given Content has no references to a translation specified by the $languageCode. - * - * @param string $languageCode - * @param int $contentId - */ - private function assertTranslationDoesNotExist($languageCode, $contentId) - { - $content = $this->contentService->loadContent($contentId); - - foreach ($content->fields as $field) { - /** @var array $field */ - self::assertArrayNotHasKey($languageCode, $field); - self::assertNotEquals($languageCode, $content->contentInfo->mainLanguageCode); - self::assertArrayNotHasKey($languageCode, $content->versionInfo->getNames()); - self::assertNotEquals($languageCode, $content->versionInfo->initialLanguageCode); - self::assertNotContains($languageCode, $content->versionInfo->languageCodes); - } - foreach ($this->contentService->loadVersions($content->contentInfo) as $versionInfo) { - self::assertArrayNotHasKey($languageCode, $versionInfo->getNames()); - self::assertNotEquals($languageCode, $versionInfo->contentInfo->mainLanguageCode); - self::assertNotEquals($languageCode, $versionInfo->initialLanguageCode); - self::assertNotContains($languageCode, $versionInfo->languageCodes); - } - } - - /** - * Returns the default fixture of fields used in most tests. - * - * @return \eZ\Publish\API\Repository\Values\Content\Field[] - */ - private function createFieldsFixture() - { - return [ - new Field( - [ - 'id' => 0, - 'value' => 'An awesome multi-lang forum²', - 'languageCode' => self::ENG_US, - 'fieldDefIdentifier' => 'name', - 'fieldTypeIdentifier' => 'ezstring', - ] - ), - new Field( - [ - 'id' => 0, - 'value' => 'An awesome multi-lang forum²³', - 'languageCode' => self::ENG_GB, - 'fieldDefIdentifier' => 'name', - 'fieldTypeIdentifier' => 'ezstring', - ] - ), - ]; - } - - /** - * Gets expected property values for the "Media" ContentInfo ValueObject. - * - * @return array - */ - private function getExpectedMediaContentInfoProperties() - { - return [ - 'id' => self::MEDIA_CONTENT_ID, - 'contentTypeId' => 1, - 'name' => 'Media', - 'sectionId' => 3, - 'currentVersionNo' => 1, - 'published' => true, - 'ownerId' => 14, - 'modificationDate' => $this->createDateTime(1060695457), - 'publishedDate' => $this->createDateTime(1060695457), - 'alwaysAvailable' => 1, - 'remoteId' => self::MEDIA_REMOTE_ID, - 'mainLanguageCode' => self::ENG_US, - 'mainLocationId' => 43, - 'status' => ContentInfo::STATUS_PUBLISHED, - ]; - } - - /** - * @covers \eZ\Publish\API\Repository\ContentService::hideContent - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testHideContent(): void - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - - $locationCreateStructs = array_map( - function (Location $parentLocation) { - return $this->locationService->newLocationCreateStruct($parentLocation->id); - }, - $this->createParentLocationsForHideReveal(2) - ); - - $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); - - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - $contentCreate->setField('name', 'Folder to hide'); - - $content = $this->contentService->createContent( - $contentCreate, - $locationCreateStructs - ); - - $publishedContent = $this->contentService->publishVersion($content->versionInfo); - $locations = $this->locationService->loadLocations($publishedContent->contentInfo); - - // Sanity check - $this->assertCount(3, $locations); - $this->assertCount(0, $this->filterHiddenLocations($locations)); - - $this->contentService->hideContent($publishedContent->contentInfo); - - $locations = $this->locationService->loadLocations($publishedContent->contentInfo); - $this->assertCount(3, $locations); - $this->assertCount(3, $this->filterHiddenLocations($locations)); - } - - /** - * @covers \eZ\Publish\API\Repository\ContentService::revealContent - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testRevealContent() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - - $locationCreateStructs = array_map( - function (Location $parentLocation) { - return $this->locationService->newLocationCreateStruct($parentLocation->id); - }, - $this->createParentLocationsForHideReveal(2) - ); - - $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); - - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - $contentCreate->setField('name', 'Folder to hide'); - - $locationCreateStructs[0]->hidden = true; - - $content = $this->contentService->createContent( - $contentCreate, - $locationCreateStructs - ); - - $publishedContent = $this->contentService->publishVersion($content->versionInfo); - $locations = $this->locationService->loadLocations($publishedContent->contentInfo); - - // Sanity check - $hiddenLocations = $this->filterHiddenLocations($locations); - $this->assertCount(3, $locations); - $this->assertCount(1, $hiddenLocations); - - $this->contentService->hideContent($publishedContent->contentInfo); - $this->assertCount( - 3, - $this->filterHiddenLocations( - $this->locationService->loadLocations($publishedContent->contentInfo) - ) - ); - - $this->contentService->revealContent($publishedContent->contentInfo); - - $locations = $this->locationService->loadLocations($publishedContent->contentInfo); - $hiddenLocationsAfterReveal = $this->filterHiddenLocations($locations); - $this->assertCount(3, $locations); - $this->assertCount(1, $hiddenLocationsAfterReveal); - $this->assertEquals($hiddenLocations, $hiddenLocationsAfterReveal); - } - - /** - * @depends testRevealContent - */ - public function testRevealContentWithHiddenParent() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); - - $contentNames = [ - 'Parent Content', - 'Child (Nesting 1)', - 'Child (Nesting 2)', - 'Child (Nesting 3)', - 'Child (Nesting 4)', - ]; - - $parentLocation = $this->locationService->newLocationCreateStruct( - $this->generateId('location', 2) - ); - - /** @var \eZ\Publish\API\Repository\Values\Content\Content[] $contents */ - $contents = []; - - foreach ($contentNames as $contentName) { - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - $contentCreate->setField('name', $contentName); - - $content = $this->contentService->createContent($contentCreate, [$parentLocation]); - $contents[] = $publishedContent = $this->contentService->publishVersion($content->versionInfo); - - $parentLocation = $this->locationService->newLocationCreateStruct( - $this->generateId('location', $publishedContent->contentInfo->mainLocationId) - ); - } - - $this->contentService->hideContent($contents[0]->contentInfo); - $this->contentService->hideContent($contents[2]->contentInfo); - $this->contentService->revealContent($contents[2]->contentInfo); - - $parentContent = $this->contentService->loadContent($contents[0]->id); - $parentLocation = $this->locationService->loadLocation($parentContent->contentInfo->mainLocationId); - $parentSublocations = $this->locationService->loadLocationList([ - $contents[1]->contentInfo->mainLocationId, - $contents[2]->contentInfo->mainLocationId, - $contents[3]->contentInfo->mainLocationId, - $contents[4]->contentInfo->mainLocationId, - ]); - - // Parent remains invisible - self::assertTrue($parentLocation->invisible); - - // All parent sublocations remain invisible as well - foreach ($parentSublocations as $parentSublocation) { - self::assertTrue($parentSublocation->invisible); - } - } - - /** - * @depends testRevealContent - */ - public function testRevealContentWithHiddenChildren() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); - - $contentNames = [ - 'Parent Content', - 'Child (Nesting 1)', - 'Child (Nesting 2)', - 'Child (Nesting 3)', - 'Child (Nesting 4)', - ]; - - $parentLocation = $this->locationService->newLocationCreateStruct( - $this->generateId('location', 2) - ); - - /** @var \eZ\Publish\API\Repository\Values\Content\Content[] $contents */ - $contents = []; - - foreach ($contentNames as $contentName) { - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - $contentCreate->setField('name', $contentName); - - $content = $this->contentService->createContent($contentCreate, [$parentLocation]); - $contents[] = $publishedContent = $this->contentService->publishVersion($content->versionInfo); - - $parentLocation = $this->locationService->newLocationCreateStruct( - $this->generateId('location', $publishedContent->contentInfo->mainLocationId) - ); - } - - $this->contentService->hideContent($contents[0]->contentInfo); - $this->contentService->hideContent($contents[2]->contentInfo); - $this->contentService->revealContent($contents[0]->contentInfo); - - $directChildContent = $this->contentService->loadContent($contents[1]->id); - $directChildLocation = $this->locationService->loadLocation($directChildContent->contentInfo->mainLocationId); - - $childContent = $this->contentService->loadContent($contents[2]->id); - $childLocation = $this->locationService->loadLocation($childContent->contentInfo->mainLocationId); - $childSublocations = $this->locationService->loadLocationList([ - $contents[3]->contentInfo->mainLocationId, - $contents[4]->contentInfo->mainLocationId, - ]); - - // Direct child content is not hidden - self::assertFalse($directChildContent->contentInfo->isHidden); - - // Direct child content location is still invisible - self::assertFalse($directChildLocation->invisible); - - // Child content is still hidden - self::assertTrue($childContent->contentInfo->isHidden); - - // Child content location is still invisible - self::assertTrue($childLocation->invisible); - - // All childs sublocations remain invisible as well - foreach ($childSublocations as $childSublocation) { - self::assertTrue($childSublocation->invisible); - } - } - - public function testHideContentWithParentLocation() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); - - $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - $contentCreate->setField('name', 'Parent'); - - $content = $this->contentService->createContent( - $contentCreate, - [ - $this->locationService->newLocationCreateStruct( - $this->generateId('location', 2) - ), - ] - ); - - $publishedContent = $this->contentService->publishVersion($content->versionInfo); - - $this->contentService->hideContent($publishedContent->contentInfo); - - $locations = $this->locationService->loadLocations($publishedContent->contentInfo); - - $childContentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); - $childContentCreate->setField('name', 'Child'); - - $childContent = $this->contentService->createContent( - $childContentCreate, - [ - $this->locationService->newLocationCreateStruct( - $locations[0]->id - ), - ] - ); - - $publishedChildContent = $this->contentService->publishVersion($childContent->versionInfo); - - $childLocations = $this->locationService->loadLocations($publishedChildContent->contentInfo); - - $this->assertTrue($locations[0]->hidden); - $this->assertTrue($locations[0]->invisible); - - $this->assertFalse($childLocations[0]->hidden); - $this->assertTrue($childLocations[0]->invisible); - } - - public function testChangeContentName() - { - $contentDraft = $this->createContentDraft( - 'folder', - $this->generateId('location', 2), - [ - 'name' => 'Marco', - ] - ); - - $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo); - $contentMetadataUpdateStruct = new ContentMetadataUpdateStruct([ - 'name' => 'Polo', - ]); - $this->contentService->updateContentMetadata($publishedContent->contentInfo, $contentMetadataUpdateStruct); - - $updatedContent = $this->contentService->loadContent($publishedContent->id); - - $this->assertEquals('Marco', $publishedContent->contentInfo->name); - $this->assertEquals('Polo', $updatedContent->contentInfo->name); - } - - public function testCopyTranslationsFromPublishedToDraft() - { - $contentDraft = $this->createContentDraft( - 'folder', - $this->generateId('location', 2), - [ - 'name' => 'Folder US', - ] - ); - - $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo); - - $deDraft = $this->contentService->createContentDraft($publishedContent->contentInfo); - - $contentUpdateStruct = new ContentUpdateStruct([ - 'initialLanguageCode' => self::GER_DE, - 'fields' => $contentDraft->getFields(), - ]); - - $contentUpdateStruct->setField('name', 'Folder GER', self::GER_DE); - - $deContent = $this->contentService->updateContent($deDraft->versionInfo, $contentUpdateStruct); - - $updatedContent = $this->contentService->loadContent($deContent->id, null, $deContent->versionInfo->versionNo); - $this->assertEquals( - [ - self::ENG_US => 'Folder US', - self::GER_DE => 'Folder GER', - ], - $updatedContent->fields['name'] - ); - - $gbDraft = $this->contentService->createContentDraft($publishedContent->contentInfo); - - $contentUpdateStruct = new ContentUpdateStruct([ - 'initialLanguageCode' => self::ENG_GB, - 'fields' => $contentDraft->getFields(), - ]); - - $contentUpdateStruct->setField('name', 'Folder GB', self::ENG_GB); - - $gbContent = $this->contentService->updateContent($gbDraft->versionInfo, $contentUpdateStruct); - $this->contentService->publishVersion($gbDraft->versionInfo); - $updatedContent = $this->contentService->loadContent($gbContent->id, null, $gbContent->versionInfo->versionNo); - $this->assertEquals( - [ - self::ENG_US => 'Folder US', - self::ENG_GB => 'Folder GB', - ], - $updatedContent->fields['name'] - ); - - $dePublished = $this->contentService->publishVersion($deDraft->versionInfo); - $this->assertEquals( - [ - self::ENG_US => 'Folder US', - self::GER_DE => 'Folder GER', - self::ENG_GB => 'Folder GB', - ], - $dePublished->fields['name'] - ); - } - - public function testCopyTranslationsFromInvalidPublishedContentToDraft() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - - // Create content type for testing - $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct('test_copy_translation'); - $contentTypeCreateStruct->mainLanguageCode = 'eng-US'; - $contentTypeCreateStruct->names = ['eng-US' => 'Test Content Type for Copy Translations']; - $fieldDefinition = $contentTypeService->newFieldDefinitionCreateStruct('name', 'ezstring'); - $fieldDefinition->position = 1; - $contentTypeCreateStruct->addFieldDefinition($fieldDefinition); - $contentTypeService->publishContentTypeDraft( - $contentTypeService->createContentType( - $contentTypeCreateStruct, - [$contentTypeService->loadContentTypeGroupByIdentifier('Content')] - ) - ); - $contentType = $contentTypeService->loadContentTypeByIdentifier('test_copy_translation'); - - // Create entry content - $contentDraft = $this->createContentDraft( - 'test_copy_translation', - $this->generateId('location', 2), - [ - 'name' => 'Folder US', - ] - ); - $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo); - - // Create translation draft that would act as an OLD version - $deDraft = $this->contentService->createContentDraft($publishedContent->contentInfo); - $contentUpdateStruct = new ContentUpdateStruct([ - 'initialLanguageCode' => self::GER_DE, - 'fields' => $contentDraft->getFields(), - ]); - - $contentUpdateStruct->setField('name', 'Folder GER', self::GER_DE); - $deContent = $this->contentService->updateContent($deDraft->versionInfo, $contentUpdateStruct); - - // Update published version, as copying is only done when there is a diff between published and draft - $gbDraft = $this->contentService->createContentDraft($publishedContent->contentInfo); - $contentUpdateStruct = new ContentUpdateStruct([ - 'initialLanguageCode' => self::ENG_US, - ]); - $contentUpdateStruct->setField('name', 'Folder US 2', self::ENG_US); - - $gbContent = $this->contentService->updateContent($gbDraft->versionInfo, $contentUpdateStruct); - $this->contentService->publishVersion($gbContent->versionInfo); - - // Update content type with new required field - $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); - $fieldDefinition = $contentTypeService->newFieldDefinitionCreateStruct('req_field', 'ezstring'); - $fieldDefinition->position = 2; - $fieldDefinition->isRequired = true; - $contentTypeService->addFieldDefinition($contentTypeDraft, $fieldDefinition); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - // Reload previous german draft, it is now in invalid state for both ENG_US and GER_DE - $invalidContentDraft = $this->contentService->loadContent($deContent->id, null, $deContent->versionInfo->versionNo); - $contentUpdateStruct = new ContentUpdateStruct([ - 'initialLanguageCode' => self::GER_DE, - ]); - $contentUpdateStruct->setField('req_field', 'Required field DE', self::GER_DE); - - $this->contentService->updateContent($invalidContentDraft->versionInfo, $contentUpdateStruct); - $this->contentService->publishVersion($invalidContentDraft->versionInfo, [self::GER_DE]); - - $publishedContent = $this->contentService->loadContent($deContent->id, null, $deContent->versionInfo->versionNo); - - $this->assertEquals( - [ - self::GER_DE => 'Folder GER', - self::ENG_US => 'Folder US 2', - ], - $publishedContent->fields['name'] - ); - // Missing values were copied from last updated draft - $this->assertEquals( - [ - self::GER_DE => 'Required field DE', - self::ENG_US => 'Required field DE', - ], - $publishedContent->fields['req_field'] - ); - } - - /** - * Create structure of parent folders with Locations to be used for Content hide/reveal tests. - * - * @param int $parentLocationId - * - * @return \eZ\Publish\API\Repository\Values\Content\Location[] A list of Locations aimed to be parents - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - private function createParentLocationsForHideReveal(int $parentLocationId): array - { - $parentFoldersLocationsIds = [ - $this->createFolder([self::ENG_US => 'P1'], $parentLocationId)->contentInfo->mainLocationId, - $this->createFolder([self::ENG_US => 'P2'], $parentLocationId)->contentInfo->mainLocationId, - $this->createFolder([self::ENG_US => 'P3'], $parentLocationId)->contentInfo->mainLocationId, - ]; - - return array_values($this->locationService->loadLocationList($parentFoldersLocationsIds)); - } - - /** - * Filter Locations list by hidden only. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location[] $locations - * - * @return array - */ - private function filterHiddenLocations(array $locations): array - { - return array_values( - array_filter( - $locations, - static function (Location $location) { - return $location->hidden; - } - ) - ); - } - - public function testPublishVersionWithSelectedLanguages() - { - $publishedContent = $this->createFolder( - [ - self::ENG_US => 'Published US', - self::GER_DE => 'Published DE', - ], - $this->generateId('location', 2) - ); - - $draft = $this->contentService->createContentDraft($publishedContent->contentInfo); - $contentUpdateStruct = new ContentUpdateStruct([ - 'initialLanguageCode' => self::ENG_US, - ]); - $contentUpdateStruct->setField('name', 'Draft 1 US', self::ENG_US); - $contentUpdateStruct->setField('name', 'Draft 1 DE', self::GER_DE); - - $this->contentService->updateContent($draft->versionInfo, $contentUpdateStruct); - $this->contentService->publishVersion($draft->versionInfo, ['ger-DE']); - $content = $this->contentService->loadContent($draft->contentInfo->id); - - $this->assertEquals( - [ - self::ENG_US => 'Published US', - self::GER_DE => 'Draft 1 DE', - ], - $content->fields['name'] - ); - } - - public function testCreateContentWithRomanianSpecialCharsInTitle() - { - $baseName = 'ȘșțȚdfdf'; - $expectedPath = '/SstTdfdf'; - - $this->createFolder([self::ENG_US => $baseName], 2); - - $urlAliasService = $this->getRepository()->getURLAliasService(); - $urlAlias = $urlAliasService->lookup($expectedPath); - $this->assertSame($expectedPath, $urlAlias->path); - } - - /** - * @param int $amountOfDrafts - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - private function createContentDrafts(int $amountOfDrafts): void - { - if (0 >= $amountOfDrafts) { - throw new InvalidArgumentException('$amountOfDrafts', 'Must be greater then 0'); - } - - $publishedContent = $this->createContentVersion1(); - - for ($i = 1; $i <= $amountOfDrafts; ++$i) { - $this->contentService->createContentDraft($publishedContent->contentInfo); - } - } - - /** - * @param array $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - private function createUserWithVersionReadLimitations(array $limitationValues = []): User - { - $limitations = [ - new LocationLimitation(['limitationValues' => $limitationValues]), - ]; - - return $this->createUserWithPolicies( - 'user', - [ - ['module' => 'content', 'function' => 'versionread', 'limitations' => $limitations], - ['module' => 'content', 'function' => 'create'], - ['module' => 'content', 'function' => 'read'], - ['module' => 'content', 'function' => 'edit'], - ] - ); - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Content[] $drafts - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * - * @return object - */ - private function createContentWithReverseRelations(array $drafts) - { - $contentWithReverseRelations = new class() { - /** @var \eZ\Publish\API\Repository\Values\Content\Content */ - public $content; - - /** @var \eZ\Publish\API\Repository\Values\Content\Content[] */ - public $reverseRelations; - }; - $content = $this->createContentVersion1(); - $versionInfo = $content->getVersionInfo(); - $contentInfo = $versionInfo->getContentInfo(); - $contentWithReverseRelations->content = $content; - - /** @var \eZ\Publish\API\Repository\Values\Content\Content $draft */ - foreach ($drafts as $draft) { - $this->contentService->addRelation( - $draft->getVersionInfo(), - $contentInfo - ); - - $contentWithReverseRelations->reverseRelations[] = $this->contentService->publishVersion($draft->getVersionInfo()); - } - - return $contentWithReverseRelations; - } -} diff --git a/eZ/Publish/API/Repository/Tests/ContentTypeServiceTest.php b/eZ/Publish/API/Repository/Tests/ContentTypeServiceTest.php deleted file mode 100644 index ad095d5632..0000000000 --- a/eZ/Publish/API/Repository/Tests/ContentTypeServiceTest.php +++ /dev/null @@ -1,4485 +0,0 @@ -getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct( - 'new-group' - ); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeGroupCreateStruct', - $groupCreate - ); - - return $groupCreate; - } - - /** - * Test for the newContentTypeGroupCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::newContentTypeGroupCreateStruct() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testNewContentTypeGroupCreateStruct - */ - public function testNewContentTypeGroupCreateStructValues($createStruct) - { - $this->assertPropertiesCorrect( - [ - 'identifier' => 'new-group', - 'creatorId' => null, - 'creationDate' => null, - /* @todo uncomment when support for multilingual names and descriptions is added - 'mainLanguageCode' => null, - */ - ], - $createStruct - ); - } - - /** - * Test for the createContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testNewContentTypeGroupCreateStruct - * @group user - */ - public function testCreateContentTypeGroup() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - $permissionResolver = $repository->getPermissionResolver(); - - $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct( - 'new-group' - ); - $groupCreate->creatorId = $this->generateId('user', $permissionResolver->getCurrentUserReference()->getUserId()); - $groupCreate->creationDate = $this->createDateTime(); - /* @todo uncomment when support for multilingual names and descriptions is added - $groupCreate->mainLanguageCode = 'ger-DE'; - $groupCreate->names = array( 'eng-GB' => 'A name.' ); - $groupCreate->descriptions = array( 'eng-GB' => 'A description.' ); - */ - - $group = $contentTypeService->createContentTypeGroup($groupCreate); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeGroup', - $group - ); - - return [ - 'createStruct' => $groupCreate, - 'group' => $group, - ]; - } - - /** - * Test for the createContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentTypeGroup - */ - public function testCreateContentTypeGroupStructValues(array $data) - { - $createStruct = $data['createStruct']; - $group = $data['group']; - - $this->assertEquals( - [ - 'identifier' => $group->identifier, - 'creatorId' => $group->creatorId, - 'creationDate' => $group->creationDate->getTimestamp(), - ], - [ - 'identifier' => $createStruct->identifier, - 'creatorId' => $createStruct->creatorId, - 'creationDate' => $createStruct->creationDate->getTimestamp(), - ] - ); - $this->assertNotNull( - $group->id - ); - - return $data; - } - - /** - * Test for the createContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentTypeGroupStructValues - */ - public function testCreateContentTypeGroupStructLanguageDependentValues(array $data) - { - $createStruct = $data['createStruct']; - $group = $data['group']; - - $this->assertStructPropertiesCorrect( - $createStruct, - $group - /* @todo uncomment when support for multilingual names and descriptions is added - array( 'names', 'descriptions', 'mainLanguageCode' ) - */ - ); - } - - /** - * Test for the createContentTypeGroup() method. - * - * @covers \eZ\Publish\API\Repository\ContentTypeService::createContentTypeGroup - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentTypeGroup - */ - public function testCreateContentTypeGroupThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$contentTypeGroupCreateStruct\' is invalid: A group with the identifier \'Content\' already exists'); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct( - 'Content' - ); - - // Throws an Exception, since group "Content" already exists - $contentTypeService->createContentTypeGroup($groupCreate); - /* END: Use Case */ - } - - /** - * Test for the loadContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentTypeGroup - * @group user - */ - public function testLoadContentTypeGroup() - { - $repository = $this->getRepository(); - - $contentTypeGroupId = $this->generateId('typegroup', 2); - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Loads the "Users" group - // $contentTypeGroupId is the ID of an existing content type group - $loadedGroup = $contentTypeService->loadContentTypeGroup($contentTypeGroupId); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeGroup', - $loadedGroup - ); - - return $loadedGroup; - } - - /** - * Test for the loadContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeGroup - */ - public function testLoadContentTypeGroupStructValues(ContentTypeGroup $group) - { - $this->assertPropertiesCorrect( - [ - 'id' => $this->generateId('typegroup', 2), - 'identifier' => 'Users', - 'creationDate' => $this->createDateTime(1031216941), - 'modificationDate' => $this->createDateTime(1033922113), - 'creatorId' => $this->generateId('user', 14), - 'modifierId' => $this->generateId('user', 14), - ], - $group - ); - } - - /** - * Test for the loadContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeGroup() - */ - public function testLoadContentTypeGroupThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - $contentTypeService = $repository->getContentTypeService(); - $loadedGroup = $contentTypeService->loadContentTypeGroup($this->generateId('typegroup', 2342)); - } - - /** - * Test for the loadContentTypeGroupByIdentifier() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeGroupByIdentifier() - * @group user - * @group field-type - */ - public function testLoadContentTypeGroupByIdentifier() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $loadedGroup = $contentTypeService->loadContentTypeGroupByIdentifier( - 'Media' - ); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeGroup', - $loadedGroup - ); - - return $loadedGroup; - } - - /** - * Test for the loadContentTypeGroupByIdentifier() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeGroupByIdentifier() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeGroupByIdentifier - */ - public function testLoadContentTypeGroupByIdentifierStructValues(ContentTypeGroup $group) - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $this->assertEquals( - $contentTypeService->loadContentTypeGroup($this->generateId('typegroup', 3)), - $group - ); - } - - /** - * Test for the loadContentTypeGroupByIdentifier() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeGroupByIdentifier() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeGroupByIdentifier - */ - public function testLoadContentTypeGroupByIdentifierThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Throws exception - $loadedGroup = $contentTypeService->loadContentTypeGroupByIdentifier( - 'not-exists' - ); - /* END: Use Case */ - } - - /** - * Test for the loadContentTypeGroups() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeGroups() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentTypeGroup - */ - public function testLoadContentTypeGroups() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Loads an array with all content type groups - $loadedGroups = $contentTypeService->loadContentTypeGroups(); - /* END: Use Case */ - - $this->assertIsArray( - $loadedGroups - ); - - foreach ($loadedGroups as $loadedGroup) { - $this->assertStructPropertiesCorrect( - $contentTypeService->loadContentTypeGroup($loadedGroup->id), - $loadedGroup, - [ - 'id', - 'identifier', - 'creationDate', - 'modificationDate', - 'creatorId', - 'modifierId', - ] - ); - } - - return $loadedGroups; - } - - /** - * Test for the loadContentTypeGroups() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeGroups() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeGroups - */ - public function testLoadContentTypeGroupsIdentifiers($groups) - { - $this->assertCount(4, $groups); - - $expectedIdentifiers = [ - 'Content' => true, - 'Users' => true, - 'Media' => true, - 'Setup' => true, - ]; - - $actualIdentifiers = []; - foreach ($groups as $group) { - $actualIdentifiers[$group->identifier] = true; - } - - ksort($expectedIdentifiers); - ksort($actualIdentifiers); - - $this->assertEquals( - $expectedIdentifiers, - $actualIdentifiers, - 'Identifier missmatch in loaded groups.' - ); - } - - /** - * Test for the newContentTypeGroupUpdateStruct() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::newContentTypeGroupUpdateStruct() - */ - public function testNewContentTypeGroupUpdateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $groupUpdate = $contentTypeService->newContentTypeGroupUpdateStruct(); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeGroupUpdateStruct', - $groupUpdate - ); - } - - /** - * Test for the updateContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentTypeGroup - */ - public function testUpdateContentTypeGroup() - { - $repository = $this->getRepository(); - - $modifierId = $this->generateId('user', 42); - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $group = $contentTypeService->loadContentTypeGroupByIdentifier('Setup'); - - $groupUpdate = $contentTypeService->newContentTypeGroupUpdateStruct(); - - $groupUpdate->identifier = 'Teardown'; - $groupUpdate->modifierId = $modifierId; - $groupUpdate->modificationDate = $this->createDateTime(); - /* @todo uncomment when support for multilingual names and descriptions is added - $groupUpdate->mainLanguageCode = 'eng-GB'; - - $groupUpdate->names = array( - 'eng-GB' => 'A name', - 'eng-US' => 'A name', - ); - $groupUpdate->descriptions = array( - 'eng-GB' => 'A description', - 'eng-US' => 'A description', - ); - */ - - $contentTypeService->updateContentTypeGroup($group, $groupUpdate); - /* END: Use Case */ - - $updatedGroup = $contentTypeService->loadContentTypeGroup($group->id); - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeGroupUpdateStruct', - $groupUpdate - ); - - return [ - 'originalGroup' => $group, - 'updateStruct' => $groupUpdate, - 'updatedGroup' => $updatedGroup, - ]; - } - - /** - * Test for the updateContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUpdateContentTypeGroup - */ - public function testUpdateContentTypeGroupStructValues(array $data) - { - $expectedValues = [ - 'identifier' => $data['updateStruct']->identifier, - 'creationDate' => $data['originalGroup']->creationDate, - 'modificationDate' => $data['updateStruct']->modificationDate, - 'creatorId' => $data['originalGroup']->creatorId, - 'modifierId' => $data['updateStruct']->modifierId, - ]; - - $this->assertPropertiesCorrect($expectedValues, $data['updatedGroup']); - - return $data; - } - - /** - * Test for the updateContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUpdateContentTypeGroupStructValues - */ - public function testUpdateContentTypeGroupStructLanguageDependentValues(array $data) - { - $expectedValues = [ - 'identifier' => $data['updateStruct']->identifier, - 'creationDate' => $data['originalGroup']->creationDate, - 'modificationDate' => $data['updateStruct']->modificationDate, - 'creatorId' => $data['originalGroup']->creatorId, - 'modifierId' => $data['updateStruct']->modifierId, - /* @todo uncomment when support for multilingual names and descriptions is added - 'mainLanguageCode' => $data['updateStruct']->mainLanguageCode, - 'names' => $data['updateStruct']->names, - 'descriptions' => $data['updateStruct']->descriptions, - */ - ]; - - $this->assertPropertiesCorrect($expectedValues, $data['updatedGroup']); - } - - /** - * Test for the updateContentTypeGroup() method. - * - * @covers \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeGroup - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUpdateContentTypeGroup - */ - public function testUpdateContentTypeGroupThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$contentTypeGroupUpdateStruct->identifier\' is invalid: given identifier already exists'); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $group = $contentTypeService->loadContentTypeGroupByIdentifier( - 'Media' - ); - - $groupUpdate = $contentTypeService->newContentTypeGroupUpdateStruct(); - $groupUpdate->identifier = 'Users'; - - // Exception, because group with identifier "Users" exists - $contentTypeService->updateContentTypeGroup($group, $groupUpdate); - /* END: Use Case */ - } - - /** - * Test for the deleteContentTypeGroup() method. - * - * @covers \eZ\Publish\API\Repository\ContentTypeService::deleteContentTypeGroup - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeGroup - */ - public function testDeleteContentTypeGroup() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct( - 'new-group' - ); - $contentTypeService->createContentTypeGroup($groupCreate); - - $group = $contentTypeService->loadContentTypeGroupByIdentifier('new-group'); - - $contentTypeService->deleteContentTypeGroup($group); - /* END: Use Case */ - - // loadContentTypeGroup should throw NotFoundException - $contentTypeService->loadContentTypeGroup($group->id); - - $this->fail('Content type group not deleted.'); - } - - /** - * Test for the newContentTypeCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::newContentTypeCreateStruct() - * @group user - * @group field-type - */ - public function testNewContentTypeCreateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $typeCreate = $contentTypeService->newContentTypeCreateStruct( - 'new-type' - ); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeCreateStruct', - $typeCreate - ); - - return $typeCreate; - } - - /** - * Test for the newContentTypeCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::newContentTypeCreateStruct() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testNewContentTypeCreateStruct - */ - public function testNewContentTypeCreateStructValues($createStruct) - { - $this->assertPropertiesCorrect( - [ - 'identifier' => 'new-type', - 'mainLanguageCode' => null, - 'remoteId' => null, - 'urlAliasSchema' => null, - 'nameSchema' => null, - 'isContainer' => false, - 'defaultSortField' => Location::SORT_FIELD_PUBLISHED, - 'defaultSortOrder' => Location::SORT_ORDER_DESC, - 'defaultAlwaysAvailable' => true, - 'names' => null, - 'descriptions' => null, - 'creatorId' => null, - 'creationDate' => null, - ], - $createStruct - ); - } - - /** - * Test for the newFieldDefinitionCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::newFieldDefinitionCreateStruct() - * @group user - * @group field-type - */ - public function testNewFieldDefinitionCreateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $fieldDefinitionCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ContentType\\FieldDefinitionCreateStruct', - $fieldDefinitionCreate - ); - - return $fieldDefinitionCreate; - } - - /** - * Test for the newFieldDefinitionCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::newFieldDefinitionCreateStruct() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testNewFieldDefinitionCreateStruct - */ - public function testNewFieldDefinitionCreateStructValues($createStruct) - { - $this->assertPropertiesCorrect( - [ - 'fieldTypeIdentifier' => 'ezstring', - 'identifier' => 'title', - 'names' => null, - 'descriptions' => null, - 'fieldGroup' => null, - 'position' => null, - 'isTranslatable' => null, - 'isRequired' => null, - 'isInfoCollector' => null, - 'validatorConfiguration' => null, - 'fieldSettings' => null, - 'defaultValue' => null, - 'isSearchable' => null, - ], - $createStruct - ); - } - - /** - * Test for the deleteContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::deleteContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testDeleteContentTypeGroup - */ - public function testDeleteContentTypeGroupThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); - - // Throws exception, since group contains types - $contentTypeService->deleteContentTypeGroup($contentGroup); - /* END: Use Case */ - } - - /** - * Test for the createContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testNewContentTypeCreateStruct - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testNewFieldDefinitionCreateStruct - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeGroupByIdentifier - * @group user - * @group field-type - */ - public function testCreateContentType() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - $permissionResolver = $repository->getPermissionResolver(); - - $typeCreate = $contentTypeService->newContentTypeCreateStruct('blog-post'); - $typeCreate->mainLanguageCode = 'eng-GB'; - $typeCreate->remoteId = '384b94a1bd6bc06826410e284dd9684887bf56fc'; - $typeCreate->urlAliasSchema = 'url|scheme'; - $typeCreate->nameSchema = 'name|scheme'; - $typeCreate->names = [ - 'eng-GB' => 'Blog post', - 'ger-DE' => 'Blog-Eintrag', - ]; - $typeCreate->descriptions = [ - 'eng-GB' => 'A blog post', - 'ger-DE' => 'Ein Blog-Eintrag', - ]; - $typeCreate->creatorId = $this->generateId('user', $permissionResolver->getCurrentUserReference()->getUserId()); - $typeCreate->creationDate = $this->createDateTime(); - - $titleFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); - $titleFieldCreate->names = [ - 'eng-GB' => 'Title', - 'ger-DE' => 'Titel', - ]; - $titleFieldCreate->descriptions = [ - 'eng-GB' => 'Title of the blog post', - 'ger-DE' => 'Titel des Blog-Eintrages', - ]; - $titleFieldCreate->fieldGroup = 'blog-content'; - $titleFieldCreate->position = 1; - $titleFieldCreate->isTranslatable = true; - $titleFieldCreate->isRequired = true; - $titleFieldCreate->isInfoCollector = false; - $titleFieldCreate->validatorConfiguration = [ - 'StringLengthValidator' => [ - 'minStringLength' => 0, - 'maxStringLength' => 0, - ], - ]; - $titleFieldCreate->fieldSettings = []; - $titleFieldCreate->isSearchable = true; - $titleFieldCreate->defaultValue = 'default title'; - - $typeCreate->addFieldDefinition($titleFieldCreate); - - $bodyFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('body', 'ezstring'); - $bodyFieldCreate->names = [ - 'eng-GB' => 'Body', - 'ger-DE' => 'Textkörper', - ]; - $bodyFieldCreate->descriptions = [ - 'eng-GB' => 'Body of the blog post', - 'ger-DE' => 'Textkörper des Blog-Eintrages', - ]; - $bodyFieldCreate->fieldGroup = 'blog-content'; - $bodyFieldCreate->position = 2; - $bodyFieldCreate->isTranslatable = true; - $bodyFieldCreate->isRequired = true; - $bodyFieldCreate->isInfoCollector = false; - $bodyFieldCreate->validatorConfiguration = [ - 'StringLengthValidator' => [ - 'minStringLength' => 0, - 'maxStringLength' => 0, - ], - ]; - $bodyFieldCreate->fieldSettings = []; - $bodyFieldCreate->isSearchable = true; - $bodyFieldCreate->defaultValue = 'default content'; - - $typeCreate->addFieldDefinition($bodyFieldCreate); - - $groups = [ - $contentTypeService->loadContentTypeGroupByIdentifier('Media'), - $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), - ]; - - $contentTypeDraft = $contentTypeService->createContentType( - $typeCreate, - $groups - ); - /* END: Use Case */ - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentType', - $contentTypeDraft - ); - - return [ - 'typeCreate' => $typeCreate, - 'contentType' => $contentTypeDraft, - 'groups' => $groups, - ]; - } - - /** - * Test for the createContentType() method struct values. - * - * @covers \eZ\Publish\API\Repository\ContentTypeService::createContentType - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * - * @param array $data - */ - public function testCreateContentTypeStructValues(array $data) - { - $typeCreate = $data['typeCreate']; - $contentType = $data['contentType']; - $groups = $data['groups']; - - foreach ($typeCreate as $propertyName => $propertyValue) { - switch ($propertyName) { - case 'fieldDefinitions': - $this->assertFieldDefinitionsCorrect( - $typeCreate->fieldDefinitions, - $contentType->fieldDefinitions->toArray() - ); - break; - - case 'creationDate': - case 'modificationDate': - $this->assertEquals( - $typeCreate->$propertyName->getTimestamp(), - $contentType->$propertyName->getTimestamp() - ); - break; - - default: - $this->assertEquals( - $typeCreate->$propertyName, - $contentType->$propertyName, - "Did not assert that property '{$propertyName}' is equal on struct and resulting value object" - ); - break; - } - } - - $this->assertContentTypeGroupsCorrect( - $groups, - $contentType->contentTypeGroups - ); - - $this->assertNotNull( - $contentType->id - ); - } - - /** - * Asserts field definition creation. - * - * Asserts that all field definitions defined through created structs in - * $expectedDefinitionCreates have been correctly created in - * $actualDefinitions. - * - * @param \eZ\Publish\API\Repository\Values\FieldDefinitionCreateStruct[] $expectedDefinitionCreates - * @param \eZ\Publish\API\Repository\Values\FieldDefinition[] $actualDefinitions - */ - protected function assertFieldDefinitionsCorrect(array $expectedDefinitionCreates, array $actualDefinitions) - { - $this->assertEquals( - count($expectedDefinitionCreates), - count($actualDefinitions), - 'Count of field definition creates did not match count of field definitions.' - ); - - $sorter = static function ($a, $b) { - return strcmp($a->identifier, $b->identifier); - }; - - usort($expectedDefinitionCreates, $sorter); - usort($actualDefinitions, $sorter); - - foreach ($expectedDefinitionCreates as $key => $expectedCreate) { - $this->assertFieldDefinitionsEqual( - $expectedCreate, - $actualDefinitions[$key] - ); - } - } - - /** - * Asserts that a field definition has been correctly created. - * - * Asserts that the given $actualDefinition is correctly created from the - * create struct in $expectedCreate. - * - * @param \eZ\Publish\API\Repository\Values\FieldDefinitionCreateStruct $expectedDefinitionCreate - * @param \eZ\Publish\API\Repository\Values\FieldDefinition $actualDefinition - */ - protected function assertFieldDefinitionsEqual($expectedCreate, $actualDefinition) - { - foreach ($expectedCreate as $propertyName => $propertyValue) { - $this->assertEquals( - $expectedCreate->$propertyName, - $actualDefinition->$propertyName - ); - } - } - - /** - * Asserts that two sets of ContentTypeGroups are equal. - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup[] $expectedGroups - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup[] $actualGroups - */ - protected function assertContentTypeGroupsCorrect($expectedGroups, $actualGroups) - { - $sorter = static function ($a, $b) { - return strcmp($a->id, $b->id); - }; - - usort($expectedGroups, $sorter); - usort($actualGroups, $sorter); - - foreach ($expectedGroups as $key => $expectedGroup) { - $this->assertStructPropertiesCorrect( - $expectedGroup, - $actualGroups[$key], - [ - 'id', - 'identifier', - 'creationDate', - 'modificationDate', - 'creatorId', - 'modifierId', - ] - ); - } - } - - /** - * Test for the createContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - */ - public function testCreateContentTypeThrowsInvalidArgumentExceptionDuplicateIdentifier() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$contentTypeCreateStruct\' is invalid: Another Content Type with identifier \'folder\' exists'); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $typeCreate = $contentTypeService->newContentTypeCreateStruct('folder'); - $typeCreate->mainLanguageCode = 'eng-GB'; - $typeCreate->names = ['eng-GB' => 'Article']; - - $firstFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); - $typeCreate->addFieldDefinition($firstFieldCreate); - - $groups = [ - $contentTypeService->loadContentTypeGroupByIdentifier('Media'), - $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), - ]; - - // Throws exception, since type "folder" exists - $contentTypeService->createContentType($typeCreate, $groups); - /* END: Use Case */ - } - - /** - * Test for the createContentType() method trying to create Content Type with already existing - * remoteId. - * - * @covers \eZ\Publish\API\Repository\ContentTypeService::createContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - */ - public function testCreateContentTypeThrowsInvalidArgumentExceptionDuplicateRemoteId() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Another Content Type with remoteId \'a3d405b81be900468eb153d774f4f0d2\' exists'); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $typeCreate = $contentTypeService->newContentTypeCreateStruct('news-article'); - $typeCreate->remoteId = 'a3d405b81be900468eb153d774f4f0d2'; - $typeCreate->mainLanguageCode = 'eng-GB'; - $typeCreate->names = ['eng-GB' => 'Article']; - - $firstFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); - $typeCreate->addFieldDefinition($firstFieldCreate); - - $groups = [ - $contentTypeService->loadContentTypeGroupByIdentifier('Media'), - $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), - ]; - - // Throws exception, since "folder" type has this remote ID - $contentTypeService->createContentType($typeCreate, $groups); - /* END: Use Case */ - } - - /** - * Test for the createContentType() method creating content with duplicate field identifiers. - * - * @covers \eZ\Publish\API\Repository\ContentTypeService::createContentType - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - */ - public function testCreateContentTypeThrowsInvalidArgumentExceptionDuplicateFieldIdentifier() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$contentTypeCreateStruct\' is invalid: The argument contains duplicate Field definition identifier \'title\''); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $typeCreate = $contentTypeService->newContentTypeCreateStruct('blog-post'); - $typeCreate->mainLanguageCode = 'eng-GB'; - $typeCreate->names = ['eng-GB' => 'Blog post']; - - $firstFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); - $typeCreate->addFieldDefinition($firstFieldCreate); - - $secondFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); - $typeCreate->addFieldDefinition($secondFieldCreate); - - $groups = [ - $contentTypeService->loadContentTypeGroupByIdentifier('Media'), - $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), - ]; - - // Throws exception, due to duplicate "title" field - $contentTypeService->createContentType($typeCreate, $groups); - /* END: Use Case */ - } - - /** - * Test for the createContentTypeGroup() method trying to create a content type with already - * existing identifier. - * - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * @covers \eZ\Publish\Core\Repository\ContentTypeService::createContentType - */ - public function testCreateContentTypeThrowsInvalidArgumentExceptionDuplicateContentTypeIdentifier() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Another Content Type with identifier \'blog-post\' exists'); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - // create published content type with identifier "blog-post" - $contentTypeDraft = $this->createContentTypeDraft(); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - $typeCreateStruct = $contentTypeService->newContentTypeCreateStruct('blog-post'); - $typeCreateStruct->remoteId = 'other-remote-id'; - $typeCreateStruct->creatorId = $repository->getPermissionResolver()->getCurrentUserReference()->getUserId(); - $typeCreateStruct->creationDate = new \DateTime(); - $typeCreateStruct->mainLanguageCode = 'eng-US'; - $typeCreateStruct->names = ['eng-US' => 'A name.']; - $typeCreateStruct->descriptions = ['eng-US' => 'A description.']; - - $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('test', 'eztext'); - $typeCreateStruct->addFieldDefinition($fieldCreate); - - // Throws an exception because content type with identifier "blog-post" already exists - $contentTypeService->createContentType( - $typeCreateStruct, - [ - $contentTypeService->loadContentTypeGroupByIdentifier('Content'), - ] - ); - } - - /** - * Test for the createContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - */ - public function testCreateContentTypeThrowsContentTypeFieldDefinitionValidationException() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $typeCreate = $contentTypeService->newContentTypeCreateStruct('blog-post'); - $typeCreate->mainLanguageCode = 'eng-GB'; - $typeCreate->names = ['eng-GB' => 'Blog post']; - - $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('temperature', 'ezinteger'); - $fieldCreate->isSearchable = true; - $fieldCreate->validatorConfiguration = [ - 'IntegerValueValidator' => [ - 'minIntegerValue' => 'forty two point one', - 'maxIntegerValue' => 75, - ], - ]; - $typeCreate->addFieldDefinition($fieldCreate); - - $groups = [ - $contentTypeService->loadContentTypeGroupByIdentifier('Media'), - $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), - ]; - - try { - // Throws validation exception, because field's validator configuration is invalid - $contentType = $contentTypeService->createContentType($typeCreate, $groups); - } catch (ContentTypeFieldDefinitionValidationException $e) { - $validationErrors = $e->getFieldErrors(); - } - /* END: Use Case */ - - /* @var $validationErrors */ - $this->assertTrue(isset($validationErrors)); - $this->assertIsArray($validationErrors); - $this->assertCount(1, $validationErrors); - $this->assertArrayHasKey('temperature', $validationErrors); - $this->assertIsArray($validationErrors['temperature']); - $this->assertCount(1, $validationErrors['temperature']); - $this->assertInstanceOf('eZ\\Publish\\Core\\FieldType\\ValidationError', $validationErrors['temperature'][0]); - - $this->assertEquals( - new Message( - "Validator parameter '%parameter%' value must be of integer type", - ['%parameter%' => 'minIntegerValue'] - ), - $validationErrors['temperature'][0]->getTranslatableMessage() - ); - } - - /** - * Test for the createContentTypeGroup() method called with no groups. - * - * @depends testCreateContentType - * @covers \eZ\Publish\Core\Repository\ContentTypeService::createContentTypeGroup - */ - public function testCreateContentTypeThrowsInvalidArgumentExceptionGroupsEmpty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$contentTypeGroups\' is invalid: The argument must contain at least one Content Type group'); - - $repository = $this->getRepository(); - - $contentTypeService = $repository->getContentTypeService(); - - $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct( - 'new-type' - ); - $contentTypeCreateStruct->mainLanguageCode = 'eng-GB'; - $contentTypeCreateStruct->names = ['eng-GB' => 'Test type']; - - // Thrown an exception because array of content type groups is empty - $contentTypeService->createContentType($contentTypeCreateStruct, []); - } - - /** - * Test for the newContentTypeUpdateStruct() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::newContentTypeUpdateStruct() - */ - public function testNewContentTypeUpdateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $typeUpdate = $contentTypeService->newContentTypeUpdateStruct(); - /* END: Use Case */ - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeUpdateStruct', - $typeUpdate - ); - - return $typeUpdate; - } - - /** - * Test for the newContentTypeUpdateStruct() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::newContentTypeUpdateStruct() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testNewContentTypeUpdateStruct - */ - public function testNewContentTypeUpdateStructValues($typeUpdate) - { - foreach ($typeUpdate as $propertyName => $propertyValue) { - $this->assertNull( - $propertyValue, - "Property '$propertyName' is not null." - ); - } - } - - /** - * Test for the loadContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - */ - public function testLoadContentTypeDraft() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $contentTypeDraftReloaded = $contentTypeService->loadContentTypeDraft( - $contentTypeDraft->id - ); - /* END: Use Case */ - - $this->assertEquals( - $contentTypeDraft, - $contentTypeDraftReloaded - ); - } - - /** - * Test for the loadContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeDraft - */ - public function testLoadContentTypeDraftThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - $nonExistingContentTypeId = $this->generateId('type', 2342); - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Throws exception, since 2342 does not exist - $contentTypeDraft = $contentTypeService->loadContentTypeDraft($nonExistingContentTypeId); - /* END: Use Case */ - } - - /** - * Test for the loadContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeDraft() - */ - public function testLoadContentTypeDraftThrowsNotFoundExceptionIfDiffrentOwner() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - $permissionResolver = $repository->getPermissionResolver(); - $contentTypeService = $repository->getContentTypeService(); - - $draft = $this->createContentTypeDraft(); - - $anotherUser = $this->createUserVersion1('anotherUser'); - $permissionResolver->setCurrentUserReference($anotherUser); - - $contentTypeDraft = $contentTypeService->loadContentTypeDraft($draft->id); - } - - /** - * Test for the loadContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeDraft() - */ - public function testCanLoadContentTypeDraftEvenIfDiffrentOwner() - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - $permissionResolver = $repository->getPermissionResolver(); - $contentTypeService = $repository->getContentTypeService(); - - $draft = $this->createContentTypeDraft(); - - $anotherUser = $this->createUserVersion1('anotherUser'); - $permissionResolver->setCurrentUserReference($anotherUser); - - $loadedDraft = $contentTypeService->loadContentTypeDraft($draft->id, true); - - $this->assertSame((int)$loadedDraft->id, (int)$draft->id); - } - - /** - * Test for the updateContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeDraft() - */ - public function testUpdateContentTypeDraft() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $modifierId = $this->generateId('user', 14); - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $typeUpdate = $contentTypeService->newContentTypeUpdateStruct(); - $typeUpdate->identifier = 'news-article'; - $typeUpdate->remoteId = '4cf35f5166fd31bf0cda859dc837e095daee9833'; - $typeUpdate->urlAliasSchema = 'url@alias|scheme'; - $typeUpdate->nameSchema = '@name@scheme@'; - $typeUpdate->isContainer = true; - $typeUpdate->mainLanguageCode = 'eng-US'; - $typeUpdate->defaultAlwaysAvailable = false; - $typeUpdate->modifierId = $modifierId; - $typeUpdate->modificationDate = $this->createDateTime(); - $typeUpdate->names = [ - 'eng-GB' => 'News article', - 'ger-DE' => 'Nachrichten-Artikel', - ]; - $typeUpdate->descriptions = [ - 'eng-GB' => 'A news article', - 'ger-DE' => 'Ein Nachrichten-Artikel', - ]; - - $contentTypeService->updateContentTypeDraft($contentTypeDraft, $typeUpdate); - /* END: Use Case */ - - $updatedType = $contentTypeService->loadContentTypeDraft( - $contentTypeDraft->id - ); - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeDraft', - $updatedType - ); - - return [ - 'originalType' => $contentTypeDraft, - 'updateStruct' => $typeUpdate, - 'updatedType' => $updatedType, - ]; - } - - /** - * Test for the updateContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUpdateContentTypeDraft - */ - public function testUpdateContentTypeDraftStructValues($data) - { - $originalType = $data['originalType']; - $updateStruct = $data['updateStruct']; - $updatedType = $data['updatedType']; - - $expectedValues = [ - 'id' => $originalType->id, - 'names' => $updateStruct->names, - 'descriptions' => $updateStruct->descriptions, - 'identifier' => $updateStruct->identifier, - 'creationDate' => $originalType->creationDate, - 'modificationDate' => $updateStruct->modificationDate, - 'creatorId' => $originalType->creatorId, - 'modifierId' => $updateStruct->modifierId, - 'urlAliasSchema' => $updateStruct->urlAliasSchema, - 'nameSchema' => $updateStruct->nameSchema, - 'isContainer' => $updateStruct->isContainer, - 'mainLanguageCode' => $updateStruct->mainLanguageCode, - 'contentTypeGroups' => $originalType->contentTypeGroups, - 'fieldDefinitions' => $originalType->fieldDefinitions, - ]; - - $this->assertPropertiesCorrect( - $expectedValues, - $updatedType - ); - - foreach ($originalType->fieldDefinitions as $index => $expectedFieldDefinition) { - $actualFieldDefinition = $updatedType->fieldDefinitions[$index]; - $this->assertInstanceOf( - FieldDefinition::class, - $actualFieldDefinition - ); - $this->assertEquals($expectedFieldDefinition, $actualFieldDefinition); - } - } - - /** - * @covers \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeDraft - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testUpdateContentTypeDraftWithNewTranslation() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $contentTypeDraft = $this->createContentTypeDraft(); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); - // sanity check - self::assertEquals( - ['eng-US', 'ger-DE'], - array_keys($contentType->getNames()) - ); - - $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); - $updateStruct = $contentTypeService->newContentTypeUpdateStruct(); - $updateStruct->names = [ - 'eng-GB' => 'BrE blog post', - ]; - $contentTypeService->updateContentTypeDraft($contentTypeDraft, $updateStruct); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - self::assertEquals( - [ - 'eng-US' => 'Blog post', - 'ger-DE' => 'Blog-Eintrag', - 'eng-GB' => 'BrE blog post', - ], - $contentTypeService->loadContentType($contentType->id)->getNames() - ); - } - - /** - * Test for the updateContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUpdateContentTypeDraft - */ - public function testUpdateContentTypeDraftThrowsInvalidArgumentExceptionDuplicateIdentifier() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $typeUpdate = $contentTypeService->newContentTypeUpdateStruct(); - $typeUpdate->identifier = 'folder'; - - // Throws exception, since type "folder" already exists - $contentTypeService->updateContentTypeDraft($contentTypeDraft, $typeUpdate); - /* END: Use Case */ - } - - /** - * Test for the updateContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUpdateContentTypeDraft - */ - public function testUpdateContentTypeDraftThrowsInvalidArgumentExceptionDuplicateRemoteId() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $typeUpdate = $contentTypeService->newContentTypeUpdateStruct(); - $typeUpdate->remoteId = 'a3d405b81be900468eb153d774f4f0d2'; - - // Throws exception, since remote ID of type "folder" is used - $contentTypeService->updateContentTypeDraft($contentTypeDraft, $typeUpdate); - /* END: Use Case */ - } - - /** - * Test for the updateContentTypeDraft() method. - * - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUpdateContentTypeDraft - * @covers \eZ\Publish\Core\Repository\ContentTypeService::updateContentTypeDraft - */ - public function testUpdateContentTypeDraftThrowsInvalidArgumentExceptionNoDraftForAuthenticatedUser() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$contentTypeDraft\' is invalid: There is no Content Type draft assigned to the authenticated user'); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $roleService = $repository->getRoleService(); - - $contentTypeDraft = $this->createContentTypeDraft(); - $typeUpdate = $contentTypeService->newContentTypeUpdateStruct(); - - // create Role allowing Content Type updates - $roleCreateStruct = $roleService->newRoleCreateStruct('ContentTypeUpdaters'); - $policyCreateStruct = $roleService->newPolicyCreateStruct('class', 'update'); - $roleDraft = $roleService->createRole($roleCreateStruct); - $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct); - $roleService->publishRoleDraft($roleDraft); - - $user = $this->createUserVersion1(); - $roleService->assignRoleToUser( - $roleService->loadRoleByIdentifier('ContentTypeUpdaters'), - $user - ); - $repository->getPermissionResolver()->setCurrentUserReference($user); - - // Throws exception, since draft belongs to another user - $contentTypeService->updateContentTypeDraft($contentTypeDraft, $typeUpdate); - } - - /** - * Test for the addFieldDefinition() method. - * - * @return array - * - * @see \eZ\Publish\API\Repository\ContentTypeService::addFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - */ - public function testAddFieldDefinition() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $fieldDefCreate = $contentTypeService->newFieldDefinitionCreateStruct('tags', 'ezstring'); - $fieldDefCreate->names = [ - 'eng-GB' => 'Tags', - 'ger-DE' => 'Schlagworte', - ]; - $fieldDefCreate->descriptions = [ - 'eng-GB' => 'Tags of the blog post', - 'ger-DE' => 'Schlagworte des Blog-Eintrages', - ]; - $fieldDefCreate->fieldGroup = 'blog-meta'; - $fieldDefCreate->position = 1; - $fieldDefCreate->isTranslatable = true; - $fieldDefCreate->isRequired = true; - $fieldDefCreate->isInfoCollector = false; - $fieldDefCreate->validatorConfiguration = [ - 'StringLengthValidator' => [ - 'minStringLength' => 0, - 'maxStringLength' => 0, - ], - ]; - $fieldDefCreate->fieldSettings = []; - $fieldDefCreate->isSearchable = true; - $fieldDefCreate->defaultValue = 'default tags'; - - $contentTypeService->addFieldDefinition($contentTypeDraft, $fieldDefCreate); - /* END: Use Case */ - - $loadedType = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeDraft', - $loadedType - ); - - return [ - 'loadedType' => $loadedType, - 'fieldDefCreate' => $fieldDefCreate, - ]; - } - - /** - * Test for the addFieldDefinition() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::addFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testAddFieldDefinition - */ - public function testAddFieldDefinitionStructValues(array $data) - { - $loadedType = $data['loadedType']; - $fieldDefCreate = $data['fieldDefCreate']; - - foreach ($loadedType->fieldDefinitions as $fieldDefinition) { - if ($fieldDefinition->identifier == $fieldDefCreate->identifier) { - $this->assertFieldDefinitionsEqual($fieldDefCreate, $fieldDefinition); - - return; - } - } - - $this->fail( - sprintf( - 'Could not create Field definition with identifier "%s".', - $fieldDefCreate->identifier - ) - ); - } - - /** - * Test for the addFieldDefinition() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::addFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testAddFieldDefinition - */ - public function testAddFieldDefinitionThrowsInvalidArgumentExceptionDuplicateFieldIdentifier() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $fieldDefCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); - - // Throws an exception - $contentTypeService->addFieldDefinition($contentTypeDraft, $fieldDefCreate); - /* END: Use Case */ - } - - /** - * Test for the addFieldDefinition() method. - * - * Testing that field definition of non-repeatable field type can not be added multiple - * times to the same ContentType. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::addFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testAddFieldDefinition - */ - public function testAddFieldDefinitionThrowsContentTypeFieldDefinitionValidationException() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $userContentType = $contentTypeService->loadContentTypeByIdentifier('user'); - $userContentTypeDraft = $contentTypeService->createContentTypeDraft($userContentType); - - $fieldDefCreate = $contentTypeService->newFieldDefinitionCreateStruct('temperature', 'ezinteger'); - $fieldDefCreate->isSearchable = true; - $fieldDefCreate->validatorConfiguration = [ - 'IntegerValueValidator' => [ - 'minIntegerValue' => 42, - 'maxIntegerValue' => 75.3, - ], - ]; - $fieldDefCreate->fieldGroup = 'blog-meta'; - $fieldDefCreate->position = 1; - $fieldDefCreate->isTranslatable = false; - $fieldDefCreate->isRequired = true; - $fieldDefCreate->isInfoCollector = false; - $fieldDefCreate->fieldSettings = []; - - try { - // Throws an exception because field's validator configuration is invalid - $contentTypeService->addFieldDefinition($userContentTypeDraft, $fieldDefCreate); - } catch (ContentTypeFieldDefinitionValidationException $e) { - $validationErrors = $e->getFieldErrors(); - } - /* END: Use Case */ - - /* @var $validationErrors */ - $this->assertTrue(isset($validationErrors)); - $this->assertIsArray($validationErrors); - $this->assertCount(1, $validationErrors); - $this->assertArrayHasKey('temperature', $validationErrors); - $this->assertIsArray($validationErrors['temperature']); - $this->assertCount(1, $validationErrors['temperature']); - $this->assertInstanceOf('eZ\\Publish\\Core\\FieldType\\ValidationError', $validationErrors['temperature'][0]); - - $this->assertEquals( - new Message( - "Validator parameter '%parameter%' value must be of integer type", - ['%parameter%' => 'maxIntegerValue'] - ), - $validationErrors['temperature'][0]->getTranslatableMessage() - ); - } - - /** - * Test for the addFieldDefinition() method. - * - * Testing that field definition of non-repeatable field type can not be added multiple - * times to the same ContentType. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::addFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testAddFieldDefinition - */ - public function testAddFieldDefinitionThrowsBadStateExceptionNonRepeatableField() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class); - $this->expectExceptionMessage('The Content Type already contains a Field definition of the singular Field Type \'ezuser\''); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $userContentType = $contentTypeService->loadContentTypeByIdentifier('user'); - $userContentTypeDraft = $contentTypeService->createContentTypeDraft($userContentType); - - $fieldDefCreate = $contentTypeService->newFieldDefinitionCreateStruct('second_user_account', 'ezuser'); - $fieldDefCreate->names = [ - 'eng-GB' => 'Second user account', - ]; - $fieldDefCreate->descriptions = [ - 'eng-GB' => 'Second user account for the ContentType', - ]; - $fieldDefCreate->fieldGroup = 'users'; - $fieldDefCreate->position = 1; - $fieldDefCreate->isTranslatable = false; - $fieldDefCreate->isRequired = true; - $fieldDefCreate->isInfoCollector = false; - $fieldDefCreate->validatorConfiguration = []; - $fieldDefCreate->fieldSettings = []; - $fieldDefCreate->isSearchable = false; - - // Throws an exception because $userContentTypeDraft already contains non-repeatable field type definition 'ezuser' - $contentTypeService->addFieldDefinition($userContentTypeDraft, $fieldDefCreate); - /* END: Use Case */ - } - - /** - * Test for the ContentTypeService::createContentType() method. - * - * Testing that field definition of non-repeatable field type can not be added multiple - * times to the same ContentTypeCreateStruct. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentType() - */ - public function testCreateContentThrowsContentTypeValidationException() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\ContentTypeValidationException::class); - $this->expectExceptionMessage('Field Type \'ezuser\' is singular and cannot be used more than once in a Content Type'); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct('this_is_new'); - $contentTypeCreateStruct->names = ['eng-GB' => 'This is new']; - $contentTypeCreateStruct->mainLanguageCode = 'eng-GB'; - - // create first field definition - $firstFieldDefinition = $contentTypeService->newFieldDefinitionCreateStruct( - 'first_user', - 'ezuser' - ); - $firstFieldDefinition->names = [ - 'eng-GB' => 'First user account', - ]; - $firstFieldDefinition->position = 1; - - $contentTypeCreateStruct->addFieldDefinition($firstFieldDefinition); - - // create second field definition - $secondFieldDefinition = $contentTypeService->newFieldDefinitionCreateStruct( - 'second_user', - 'ezuser' - ); - $secondFieldDefinition->names = [ - 'eng-GB' => 'Second user account', - ]; - $secondFieldDefinition->position = 2; - - $contentTypeCreateStruct->addFieldDefinition($secondFieldDefinition); - - // Throws an exception because the ContentTypeCreateStruct has a singular field repeated - $contentTypeService->createContentType( - $contentTypeCreateStruct, - [$contentTypeService->loadContentTypeGroupByIdentifier('Content')] - ); - /* END: Use Case */ - } - - /** - * Test for the addFieldDefinition() method. - * - * Testing adding field definition of the field type that can not be added to the ContentType that - * already has Content instances. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::addFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testAddFieldDefinition - */ - public function testAddFieldDefinitionThrowsBadStateExceptionContentInstances() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class); - $this->expectExceptionMessage('A Field definition of the \'ezuser\' Field Type cannot be added because the Content Type already has Content items'); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $folderContentType = $contentTypeService->loadContentTypeByIdentifier('folder'); - $folderContentTypeDraft = $contentTypeService->createContentTypeDraft($folderContentType); - - $fieldDefCreate = $contentTypeService->newFieldDefinitionCreateStruct('user_account', 'ezuser'); - $fieldDefCreate->names = [ - 'eng-GB' => 'User account', - ]; - $fieldDefCreate->descriptions = [ - 'eng-GB' => 'User account field definition for ContentType that has Content instances', - ]; - $fieldDefCreate->fieldGroup = 'users'; - $fieldDefCreate->position = 1; - $fieldDefCreate->isTranslatable = false; - $fieldDefCreate->isRequired = true; - $fieldDefCreate->isInfoCollector = false; - $fieldDefCreate->validatorConfiguration = []; - $fieldDefCreate->fieldSettings = []; - $fieldDefCreate->isSearchable = false; - - // Throws an exception because 'ezuser' type field definition can't be added to ContentType that already has Content instances - $contentTypeService->addFieldDefinition($folderContentTypeDraft, $fieldDefCreate); - /* END: Use Case */ - } - - /** - * Test for the removeFieldDefinition() method. - * - * @return array - * - * @see \eZ\Publish\API\Repository\ContentTypeService::removeFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - */ - public function testRemoveFieldDefinition() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $bodyField = $contentTypeDraft->getFieldDefinition('body'); - - $contentTypeService->removeFieldDefinition($contentTypeDraft, $bodyField); - /* END: Use Case */ - - $loadedType = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeDraft', - $loadedType - ); - - return [ - 'removedFieldDefinition' => $bodyField, - 'loadedType' => $loadedType, - ]; - } - - /** - * Test for the removeFieldDefinition() method. - * - * @param array $data - * - * @see \eZ\Publish\API\Repository\ContentTypeService::removeFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testRemoveFieldDefinition - */ - public function testRemoveFieldDefinitionRemoved(array $data) - { - $removedFieldDefinition = $data['removedFieldDefinition']; - $loadedType = $data['loadedType']; - - foreach ($loadedType->fieldDefinitions as $fieldDefinition) { - if ($fieldDefinition->identifier == $removedFieldDefinition->identifier) { - $this->fail( - sprintf( - 'Field definition with identifier "%s" not removed.', - $removedFieldDefinition->identifier - ) - ); - } - } - } - - /** - * Test for the removeFieldDefinition() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::removeFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testRemoveFieldDefinition - */ - public function testRemoveFieldDefinitionThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $bodyField = $contentTypeDraft->getFieldDefinition('body'); - $contentTypeService->removeFieldDefinition($contentTypeDraft, $bodyField); - - $loadedDraft = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); - - // Throws exception, sine "body" has already been removed - $contentTypeService->removeFieldDefinition($loadedDraft, $bodyField); - /* END: Use Case */ - } - - /** - * Test removeFieldDefinition() method for field in a different draft throws an exception. - * - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testRemoveFieldDefinition - * @covers \eZ\Publish\Core\Repository\ContentTypeService::removeFieldDefinition - */ - public function testRemoveFieldDefinitionThrowsInvalidArgumentExceptionOnWrongDraft() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $contentTypeDraft01 = $this->createContentTypeDraft(); - $contentTypeDraft02 = $this->createContentTypeDraft(); - - $bodyField = $contentTypeDraft02->getFieldDefinition('body'); - - // Throws an exception because $bodyField field belongs to another draft - $contentTypeService->removeFieldDefinition($contentTypeDraft01, $bodyField); - } - - /** - * Test for the removeFieldDefinition() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::removeFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testRemoveFieldDefinition - */ - public function testRemoveFieldDefinitionRemovesFieldFromContent() - { - $repository = $this->getRepository(); - - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - - // Create ContentType - $contentTypeDraft = $this->createContentTypeDraft(); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - $publishedType = $contentTypeService->loadContentType($contentTypeDraft->id); - - // Create multi-language Content in all 3 possible versions - $contentDraft = $this->createContentDraft(); - $archivedContent = $contentService->publishVersion($contentDraft->versionInfo); - $contentDraft = $contentService->createContentDraft($archivedContent->contentInfo); - $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); - $draftContent = $contentService->createContentDraft($publishedContent->contentInfo); - - // Remove field definition from ContentType - $contentTypeDraft = $contentTypeService->createContentTypeDraft($publishedType); - $bodyField = $contentTypeDraft->getFieldDefinition('body'); - $contentTypeService->removeFieldDefinition($contentTypeDraft, $bodyField); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - // Reload all versions - $contentVersion1Archived = $contentService->loadContent( - $archivedContent->contentInfo->id, - null, - $archivedContent->versionInfo->versionNo - ); - $contentVersion2Published = $contentService->loadContent( - $publishedContent->contentInfo->id, - null, - $publishedContent->versionInfo->versionNo - ); - $contentVersion3Draft = $contentService->loadContent( - $draftContent->contentInfo->id, - null, - $draftContent->versionInfo->versionNo - ); - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\Content\\Content', - $contentVersion1Archived - ); - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\Content\\Content', - $contentVersion2Published - ); - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\Content\\Content', - $contentVersion3Draft - ); - - return [ - $contentVersion1Archived, - $contentVersion2Published, - $contentVersion3Draft, - ]; - } - - /** - * Test for the removeFieldDefinition() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content[] $data - * - * @see \eZ\Publish\API\Repository\ContentTypeService::removeFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testRemoveFieldDefinitionRemovesFieldFromContent - */ - public function testRemoveFieldDefinitionRemovesFieldFromContentRemoved($data) - { - list( - $contentVersion1Archived, - $contentVersion1Published, - $contentVersion2Draft - ) = $data; - - $this->assertFalse( - isset($contentVersion1Archived->fields['body']), - 'The field was not removed from archived version.' - ); - $this->assertFalse( - isset($contentVersion1Published->fields['body']), - 'The field was not removed from published version.' - ); - $this->assertFalse( - isset($contentVersion2Draft->fields['body']), - 'The field was not removed from draft version.' - ); - } - - /** - * Test for the addFieldDefinition() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::addFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testAddFieldDefinition - */ - public function testAddFieldDefinitionAddsFieldToContent() - { - $repository = $this->getRepository(); - - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - - // Create ContentType - $contentTypeDraft = $this->createContentTypeDraft(); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - $publishedType = $contentTypeService->loadContentType($contentTypeDraft->id); - - // Create multi-language Content in all 3 possible versions - $contentDraft = $this->createContentDraft(); - $archivedContent = $contentService->publishVersion($contentDraft->versionInfo); - $contentDraft = $contentService->createContentDraft($archivedContent->contentInfo); - $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); - $draftContent = $contentService->createContentDraft($publishedContent->contentInfo); - - // Add field definition to ContentType - $contentTypeDraft = $contentTypeService->createContentTypeDraft($publishedType); - - $fieldDefinitionCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct('byline', 'ezstring'); - $fieldDefinitionCreateStruct->names = [ - 'eng-US' => 'Byline', - ]; - $fieldDefinitionCreateStruct->descriptions = [ - 'eng-US' => 'Byline of the blog post', - ]; - $fieldDefinitionCreateStruct->fieldGroup = 'blog-meta'; - $fieldDefinitionCreateStruct->position = 1; - $fieldDefinitionCreateStruct->isTranslatable = true; - $fieldDefinitionCreateStruct->isRequired = true; - $fieldDefinitionCreateStruct->isInfoCollector = false; - $fieldDefinitionCreateStruct->validatorConfiguration = [ - 'StringLengthValidator' => [ - 'minStringLength' => 0, - 'maxStringLength' => 0, - ], - ]; - $fieldDefinitionCreateStruct->fieldSettings = []; - $fieldDefinitionCreateStruct->isSearchable = true; - - $contentTypeService->addFieldDefinition($contentTypeDraft, $fieldDefinitionCreateStruct); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - // Reload all versions - $contentVersion1Archived = $contentService->loadContent( - $archivedContent->contentInfo->id, - null, - $archivedContent->versionInfo->versionNo - ); - $contentVersion2Published = $contentService->loadContent( - $publishedContent->contentInfo->id, - null, - $publishedContent->versionInfo->versionNo - ); - $contentVersion3Draft = $contentService->loadContent( - $draftContent->contentInfo->id, - null, - $draftContent->versionInfo->versionNo - ); - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\Content\\Content', - $contentVersion1Archived - ); - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\Content\\Content', - $contentVersion2Published - ); - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\Content\\Content', - $contentVersion3Draft - ); - - return [ - $contentVersion1Archived, - $contentVersion2Published, - $contentVersion3Draft, - ]; - } - - /** - * Test for the addFieldDefinition() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content[] $data - * - * @see \eZ\Publish\API\Repository\ContentTypeService::addFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testAddFieldDefinitionAddsFieldToContent - */ - public function testAddFieldDefinitionAddsFieldToContentAdded(array $data) - { - list( - $contentVersion1Archived, - $contentVersion1Published, - $contentVersion2Draft - ) = $data; - - $this->assertTrue( - isset($contentVersion1Archived->fields['byline']), - 'New field was not added to archived version.' - ); - $this->assertTrue( - isset($contentVersion1Published->fields['byline']), - 'New field was not added to published version.' - ); - $this->assertTrue( - isset($contentVersion2Draft->fields['byline']), - 'New field was not added to draft version.' - ); - - $this->assertEquals( - $contentVersion1Archived->getField('byline')->id, - $contentVersion1Published->getField('byline')->id - ); - $this->assertEquals( - $contentVersion1Published->getField('byline')->id, - $contentVersion2Draft->getField('byline')->id - ); - } - - /** - * Test for the newFieldDefinitionUpdateStruct() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::newFieldDefinitionUpdateStruct() - */ - public function testNewFieldDefinitionUpdateStruct() - { - $repository = $this->getRepository(); - /* BEGIN: Use Case */ - // $draftId contains the ID of a content type draft - $contentTypeService = $repository->getContentTypeService(); - - $updateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ContentType\\FieldDefinitionUpdateStruct', - $updateStruct - ); - - return $updateStruct; - } - - /** - * Test for the newFieldDefinitionUpdateStruct() method. - * - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testNewFieldDefinitionUpdateStruct - * @covers \eZ\Publish\Core\Repository\ContentTypeService::newContentTypeUpdateStruct - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct - */ - public function testNewFieldDefinitionUpdateStructValues($fieldDefinitionUpdateStruct) - { - foreach ($fieldDefinitionUpdateStruct as $propertyName => $propertyValue) { - $this->assertNull( - $propertyValue, - "Property '$propertyName' is not null." - ); - } - } - - /** - * Test for the updateFieldDefinition() method. - * - * @return array - * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeDraft - */ - public function testUpdateFieldDefinition() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $bodyField = $contentTypeDraft->getFieldDefinition('body'); - - $bodyUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); - $bodyUpdateStruct->identifier = 'blog-body'; - $bodyUpdateStruct->names = [ - 'eng-GB' => 'Blog post body', - 'ger-DE' => 'Blog-Eintrags-Textkörper', - ]; - $bodyUpdateStruct->descriptions = [ - 'eng-GB' => 'Blog post body of the blog post', - 'ger-DE' => 'Blog-Eintrags-Textkörper des Blog-Eintrages', - ]; - $bodyUpdateStruct->fieldGroup = 'updated-blog-content'; - $bodyUpdateStruct->position = 3; - $bodyUpdateStruct->isTranslatable = false; - $bodyUpdateStruct->isRequired = false; - $bodyUpdateStruct->isInfoCollector = true; - $bodyUpdateStruct->validatorConfiguration = []; - $bodyUpdateStruct->fieldSettings = [ - 'textRows' => 60, - ]; - $bodyUpdateStruct->isSearchable = false; - - $contentTypeService->updateFieldDefinition( - $contentTypeDraft, - $bodyField, - $bodyUpdateStruct - ); - /* END: Use Case */ - - $loadedDraft = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\FieldDefinition', - ($loadedField = $loadedDraft->getFieldDefinition('blog-body')) - ); - - return [ - 'originalField' => $bodyField, - 'updatedField' => $loadedField, - 'updateStruct' => $bodyUpdateStruct, - ]; - } - - /** - * @covers \eZ\Publish\API\Repository\ContentTypeService::updateFieldDefinition - */ - public function testUpdateFieldDefinitionWithNewTranslation() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $bodyField = $contentTypeDraft->getFieldDefinition('body'); - - self::assertEquals( - ['eng-US', 'ger-DE'], - array_keys($bodyField->getNames()) - ); - - $bodyUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); - $bodyUpdateStruct->identifier = 'blog-body'; - $bodyUpdateStruct->names = [ - 'eng-GB' => 'New blog post body', - ]; - $bodyUpdateStruct->descriptions = [ - 'eng-GB' => null, - ]; - $bodyUpdateStruct->fieldGroup = 'updated-blog-content'; - $bodyUpdateStruct->position = 3; - $bodyUpdateStruct->isTranslatable = false; - $bodyUpdateStruct->isRequired = false; - $bodyUpdateStruct->isInfoCollector = true; - $bodyUpdateStruct->validatorConfiguration = []; - $bodyUpdateStruct->fieldSettings = [ - 'textRows' => 60, - ]; - $bodyUpdateStruct->isSearchable = false; - - $contentTypeService->updateFieldDefinition( - $contentTypeDraft, - $bodyField, - $bodyUpdateStruct - ); - /* END: Use Case */ - - $contentType = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); - - self::assertEquals( - [ - 'eng-GB' => 'New blog post body', - 'eng-US' => 'Body', - 'ger-DE' => 'Textkörper', - ], - $contentType->getFieldDefinition('blog-body')->getNames() - ); - self::assertEquals( - [ - 'eng-GB' => null, - 'eng-US' => 'Body of the blog post', - 'ger-DE' => 'Textkörper des Blog-Eintrages', - ], - $contentType->getFieldDefinition('blog-body')->getDescriptions() - ); - } - - /** - * Test for the updateFieldDefinition() method. - * - * @param array $data - * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUpdateFieldDefinition - */ - public function testUpdateFieldDefinitionStructValues(array $data) - { - $originalField = $data['originalField']; - $updatedField = $data['updatedField']; - $updateStruct = $data['updateStruct']; - - $this->assertPropertiesCorrect( - [ - 'id' => $originalField->id, - 'identifier' => $updateStruct->identifier, - 'names' => $updateStruct->names, - 'descriptions' => $updateStruct->descriptions, - 'fieldGroup' => $updateStruct->fieldGroup, - 'position' => $updateStruct->position, - 'fieldTypeIdentifier' => $originalField->fieldTypeIdentifier, - 'isTranslatable' => $updateStruct->isTranslatable, - 'isRequired' => $updateStruct->isRequired, - 'isInfoCollector' => $updateStruct->isInfoCollector, - 'validatorConfiguration' => $updateStruct->validatorConfiguration, - 'defaultValue' => $originalField->defaultValue, - 'isSearchable' => $updateStruct->isSearchable, - ], - $updatedField - ); - } - - /** - * Test for the updateFieldDefinition() method using an empty FieldDefinitionUpdateStruct. - * - * @see \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct - * - * @covers \eZ\Publish\Core\Repository\ContentTypeService::updateFieldDefinition - */ - public function testUpdateFieldDefinitionWithEmptyStruct() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $contentTypeDraft = $this->createContentTypeDraft(); - $fieldDefinition = $contentTypeDraft->getFieldDefinition('body'); - $fieldDefinitionUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); - - $contentTypeService->updateFieldDefinition( - $contentTypeDraft, - $fieldDefinition, - $fieldDefinitionUpdateStruct - ); - $contentTypeDraft = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); - $updatedFieldDefinition = $contentTypeDraft->getFieldDefinition('body'); - - self::assertEquals( - $fieldDefinition, - $updatedFieldDefinition - ); - } - - /** - * Test for the updateFieldDefinition() method with already defined field identifier. - * - * @covers \eZ\Publish\API\Repository\ContentTypeService::updateFieldDefinition - * depends \eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeDraft - */ - public function testUpdateFieldDefinitionThrowsInvalidArgumentExceptionFieldIdentifierExists() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$fieldDefinitionUpdateStruct\' is invalid: Another Field definition with identifier \'title\' exists in the Content Type'); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $bodyField = $contentTypeDraft->getFieldDefinition('body'); - $titleField = $contentTypeDraft->getFieldDefinition('title'); - - $bodyUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); - $bodyUpdateStruct->identifier = 'title'; - - // Throws exception, since "title" field already exists - $contentTypeService->updateFieldDefinition( - $contentTypeDraft, - $bodyField, - $bodyUpdateStruct - ); - /* END: Use Case */ - } - - /** - * Test for the updateFieldDefinition() method trying to update non-existent field. - * - * @covers \eZ\Publish\API\Repository\ContentTypeService::updateFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeDraft - */ - public function testUpdateFieldDefinitionThrowsInvalidArgumentExceptionForUndefinedField() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$fieldDefinition\' is invalid: The given Field definition does not belong to the Content Type'); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $bodyField = $contentTypeDraft->getFieldDefinition('body'); - $contentTypeService->removeFieldDefinition($contentTypeDraft, $bodyField); - - $loadedDraft = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); - - $bodyUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); - - // Throws exception, since field "body" is already deleted - $contentTypeService->updateFieldDefinition( - $loadedDraft, - $bodyField, - $bodyUpdateStruct - ); - /* END: Use Case */ - } - - /** - * Test for the publishContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::publishContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeDraft - */ - public function testPublishContentTypeDraft() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - /* END: Use Case */ - - $publishedType = $contentTypeService->loadContentType($contentTypeDraft->id); - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentType', - $publishedType - ); - $this->assertNotInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeDraft', - $publishedType - ); - } - - /** - * Test for the publishContentTypeDraft() method setting proper ContentType nameSchema. - * - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testPublishContentTypeDraft - * @covers \eZ\Publish\Core\Repository\ContentTypeService::publishContentTypeDraft - */ - public function testPublishContentTypeDraftSetsNameSchema() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $typeCreateStruct = $contentTypeService->newContentTypeCreateStruct( - 'new-type' - ); - $typeCreateStruct->names = [ - 'eng-GB' => 'Type title', - ]; - $typeCreateStruct->mainLanguageCode = 'eng-GB'; - - $titleFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); - $titleFieldCreate->position = 1; - $typeCreateStruct->addFieldDefinition($titleFieldCreate); - - $type = $contentTypeService->createContentType( - $typeCreateStruct, - [ - $contentTypeService->loadContentTypeGroupByIdentifier('Content'), - ] - ); - - $contentTypeService->publishContentTypeDraft($type); - - $loadedContentType = $contentTypeService->loadContentType($type->id); - - $this->assertEquals('', $loadedContentType->nameSchema); - } - - /** - * Test that publishing Content Type Draft refreshes list of Content Types in Content Type Groups. - * - * @covers \eZ\Publish\API\Repository\ContentTypeService::publishContentTypeDraft - */ - public function testPublishContentTypeDraftRefreshesContentTypesList() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $contentTypeDraft = $this->createContentTypeDraft(); - - // Make sure to 1. check draft is not part of lists, and 2. warm cache to make sure it invalidates - $contentTypes = $contentTypeService->loadContentTypeList([1, $contentTypeDraft->id]); - self::assertArrayNotHasKey($contentTypeDraft->id, $contentTypes); - self::assertCount(1, $contentTypes); - - $contentTypeGroups = $contentTypeDraft->getContentTypeGroups(); - foreach ($contentTypeGroups as $contentTypeGroup) { - $contentTypes = $contentTypeService->loadContentTypes($contentTypeGroup); - // check if not published Content Type does not exist on published Content Types list - self::assertNotContains( - $contentTypeDraft->id, - array_map( - static function (ContentType $contentType) { - return $contentType->id; - }, - $contentTypes - ) - ); - } - - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - // After publishing it should be part of lists - $contentTypes = $contentTypeService->loadContentTypeList([1, $contentTypeDraft->id]); - self::assertArrayHasKey($contentTypeDraft->id, $contentTypes); - self::assertCount(2, $contentTypes); - - foreach ($contentTypeGroups as $contentTypeGroup) { - $contentTypes = $contentTypeService->loadContentTypes($contentTypeGroup); - // check if published Content is available in published Content Types list - self::assertContains( - $contentTypeDraft->id, - array_map( - static function (ContentType $contentType) { - return $contentType->id; - }, - $contentTypes - ) - ); - } - } - - /** - * Test for the publishContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::publishContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testPublishContentTypeDraft - */ - public function testPublishContentTypeDraftThrowsBadStateException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - /* BEGIN: Use Case */ - $contentTypeDraft = $this->createContentTypeDraft(); - - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - // Throws exception, since no draft exists anymore - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - /* END: Use Case */ - } - - /** - * Test for the createContentTypeGroup() method trying to create Content Type without any fields. - * - * @covers \eZ\Publish\API\Repository\ContentTypeService::publishContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testPublishContentTypeDraft - */ - public function testPublishContentTypeDraftThrowsInvalidArgumentExceptionWithoutFields() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$contentTypeDraft\' is invalid: The Content Type draft should have at least one Field definition'); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $typeCreateStruct = $contentTypeService->newContentTypeCreateStruct( - 'no-fields-type' - ); - $typeCreateStruct->remoteId = 'new-unique-remoteid'; - $typeCreateStruct->creatorId = $repository->getPermissionResolver()->getCurrentUserReference()->getUserId(); - $typeCreateStruct->creationDate = new \DateTime(); - $typeCreateStruct->mainLanguageCode = 'eng-US'; - $typeCreateStruct->names = ['eng-US' => 'A name.']; - $typeCreateStruct->descriptions = ['eng-US' => 'A description.']; - - $contentTypeDraft = $contentTypeService->createContentType( - $typeCreateStruct, - [ - $contentTypeService->loadContentTypeGroupByIdentifier('Content'), - ] - ); - // Throws an exception because Content Type draft should have at least one field definition. - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - } - - /** - * Test for the loadContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * @group user - * @group field-type - */ - public function testLoadContentType() - { - $repository = $this->getRepository(); - - $userGroupId = $this->generateId('type', 3); - /* BEGIN: Use Case */ - // $userGroupId is the ID of the "user_group" type - $contentTypeService = $repository->getContentTypeService(); - // Loads the standard "user_group" type - $userGroupType = $contentTypeService->loadContentType($userGroupId); - /* END: Use Case */ - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentType', - $userGroupType - ); - - return $userGroupType; - } - - /** - * Test that multi-language logic respects prioritized language list. - * - * @dataProvider getPrioritizedLanguageList - * - * @param string[] $languageCodes - */ - public function testLoadContentTypeWithPrioritizedLanguagesList(array $languageCodes) - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $contentType = $this->createContentTypeDraft(); - $contentTypeService->publishContentTypeDraft($contentType); - $contentType = $contentTypeService->loadContentType($contentType->id, $languageCodes); - - $language = isset($languageCodes[0]) ? $languageCodes[0] : 'eng-US'; - /** @var \eZ\Publish\Core\FieldType\TextLine\Value $nameValue */ - self::assertEquals( - $contentType->getName($language), - $contentType->getName() - ); - self::assertEquals( - $contentType->getDescription($language), - $contentType->getDescription() - ); - - foreach ($contentType->getFieldDefinitions() as $fieldDefinition) { - self::assertEquals( - $fieldDefinition->getName($language), - $fieldDefinition->getName() - ); - self::assertEquals( - $fieldDefinition->getDescription($language), - $fieldDefinition->getDescription() - ); - } - } - - /** - * @return array - */ - public function getPrioritizedLanguageList() - { - return [ - [[]], - [['eng-US']], - [['ger-DE']], - [['eng-US', 'ger-DE']], - [['ger-DE', 'eng-US']], - ]; - } - - /** - * Test for the loadContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentType - */ - public function testLoadContentTypeStructValues($userGroupType) - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $this->assertPropertiesCorrect( - [ - 'id' => $this->generateId('type', 3), - 'status' => 0, - 'identifier' => 'user_group', - 'creationDate' => $this->createDateTime(1024392098), - 'modificationDate' => $this->createDateTime(1048494743), - 'creatorId' => $this->generateId('user', 14), - 'modifierId' => $this->generateId('user', 14), - 'remoteId' => '25b4268cdcd01921b808a0d854b877ef', - 'names' => [ - 'eng-US' => 'User group', - ], - 'descriptions' => [], - 'nameSchema' => '<name>', - 'isContainer' => true, - 'mainLanguageCode' => 'eng-US', - 'defaultAlwaysAvailable' => true, - 'defaultSortField' => 1, - 'defaultSortOrder' => 1, - 'contentTypeGroups' => [ - 0 => $contentTypeService->loadContentTypeGroup($this->generateId('typegroup', 2)), - ], - ], - $userGroupType - ); - - return $userGroupType->fieldDefinitions; - } - - /** - * Test for the loadContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeStructValues - */ - public function testLoadContentTypeFieldDefinitions(APIFieldDefinitionCollection $fieldDefinitions) - { - $expectedFieldDefinitions = [ - 'name' => [ - 'identifier' => 'name', - 'fieldGroup' => '', - 'position' => 1, - 'fieldTypeIdentifier' => 'ezstring', - 'isTranslatable' => true, - 'isRequired' => true, - 'isInfoCollector' => false, - 'isSearchable' => true, - 'defaultValue' => new TextLineValue(), - 'names' => [ - 'eng-US' => 'Name', - ], - 'descriptions' => [], - ], - 'description' => [ - 'identifier' => 'description', - 'fieldGroup' => '', - 'position' => 2, - 'fieldTypeIdentifier' => 'ezstring', - 'isTranslatable' => true, - 'isRequired' => false, - 'isInfoCollector' => false, - 'isSearchable' => true, - 'defaultValue' => new TextLineValue(), - 'names' => [ - 'eng-US' => 'Description', - ], - 'descriptions' => [], - ], - ]; - - $fieldDefinitions = $fieldDefinitions->toArray(); - foreach ($fieldDefinitions as $index => $fieldDefinition) { - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\FieldDefinition', - $fieldDefinition - ); - - $this->assertNotNull($fieldDefinition->id); - - if (!isset($expectedFieldDefinitions[$fieldDefinition->identifier])) { - $this->fail( - sprintf( - 'Unexpected Field definition loaded: "%s" (%s)', - $fieldDefinition->identifier, - $fieldDefinition->id - ) - ); - } - - $this->assertPropertiesCorrect( - $expectedFieldDefinitions[$fieldDefinition->identifier], - $fieldDefinition - ); - unset($expectedFieldDefinitions[$fieldDefinition->identifier]); - unset($fieldDefinitions[$index]); - } - - if (0 !== count($expectedFieldDefinitions)) { - $this->fail( - sprintf( - 'Missing expected Field definitions: %s', - implode(',', array_column($expectedFieldDefinitions, 'identifier')) - ) - ); - } - - if (0 !== count($fieldDefinitions)) { - $this->fail( - sprintf( - 'Loaded unexpected Field definitions: %s', - implode( - ',', - array_map( - static function ($fieldDefinition) { - return $fieldDefinition->identifier; - }, - $fieldDefinitions - ) - ) - ) - ); - } - } - - /** - * Test for the loadContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentType - */ - public function testLoadContentTypeThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - $nonExistentTypeId = $this->generateId('type', 2342); - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Throws exception, since type with ID 2342 does not exist - $contentTypeService->loadContentType($nonExistentTypeId); - /* END: Use Case */ - } - - /** - * Test for the loadContentTypeByIdentifier() method. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeByIdentifier() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentType - * @group user - */ - public function testLoadContentTypeByIdentifier() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $articleType = $contentTypeService->loadContentTypeByIdentifier('article'); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentType', - $articleType - ); - - return $articleType; - } - - /** - * Test for the loadContentTypeByIdentifier() method. - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeByIdentifier() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifier - */ - public function testLoadContentTypeByIdentifierReturnsCorrectInstance($contentType) - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $this->assertEquals( - $contentTypeService->loadContentType($contentType->id), - $contentType - ); - } - - /** - * Test for the loadContentTypeByIdentifier() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeByIdentifier() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifier - */ - public function testLoadContentTypeByIdentifierThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Throws an exception, since no type with this identifier exists - $contentTypeService->loadContentTypeByIdentifier('sindelfingen'); - /* END: Use Case */ - } - - /** - * Test for the loadContentTypeByRemoteId() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeByRemoteId() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentType - */ - public function testLoadContentTypeByRemoteId() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Loads the standard "user_group" type - $userGroupType = $contentTypeService->loadContentTypeByRemoteId( - '25b4268cdcd01921b808a0d854b877ef' - ); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentType', - $userGroupType - ); - - return $userGroupType; - } - - /** - * Test for the loadContentTypeByRemoteId() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeByRemoteId() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByRemoteId - */ - public function testLoadContentTypeByRemoteIdReturnsCorrectInstance($contentType) - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $this->assertEquals( - $contentTypeService->loadContentType($contentType->id), - $contentType - ); - } - - /** - * Test for the loadContentTypeByRemoteId() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeByRemoteId() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentType - */ - public function testLoadContentTypeByRemoteIdThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Throws an exception, since no type with this remote ID exists - $contentTypeService->loadContentTypeByRemoteId('not-exists'); - /* END: Use Case */ - } - - /** - * Test for the loadContentTypeList() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypeList() - * @depends testLoadContentType - */ - public function testLoadContentTypeList() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $types = $contentTypeService->loadContentTypeList([3, 4]); - - $this->assertIsIterable($types); - - $this->assertEquals( - [ - 3 => $contentTypeService->loadContentType(3), - 4 => $contentTypeService->loadContentType(4), - ], - $types - ); - } - - /** - * Test for the loadContentTypes() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypes() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentType - */ - public function testLoadContentTypes() - { - $repository = $this->getRepository(); - - $typeGroupId = $this->generateId('typegroup', 2); - /* BEGIN: Use Case */ - // $typeGroupId is a valid ID of a content type group - $contentTypeService = $repository->getContentTypeService(); - - $contentTypeGroup = $contentTypeService->loadContentTypeGroup($typeGroupId); - - // Loads all types from content type group "Users" - $types = $contentTypeService->loadContentTypes($contentTypeGroup); - /* END: Use Case */ - - $this->assertIsArray($types); - - return $types; - } - - /** - * Test for the loadContentTypes() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::loadContentTypes() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypes - */ - public function testLoadContentTypesContent(array $types) - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - usort( - $types, - static function ($a, $b) { - if ($a->id == $b->id) { - return 0; - } - - return ($a->id < $b->id) ? -1 : 1; - } - ); - $this->assertEquals( - [ - $contentTypeService->loadContentType($this->generateId('type', 3)), - $contentTypeService->loadContentType($this->generateId('type', 4)), - ], - $types - ); - } - - /** - * Test for the createContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentType - */ - public function testCreateContentTypeDraft() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $commentType = $contentTypeService->loadContentTypeByIdentifier('comment', Language::ALL); - - $commentTypeDraft = $contentTypeService->createContentTypeDraft($commentType); - /* END: Use Case */ - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeDraft', - $commentTypeDraft - ); - - return [ - 'originalType' => $commentType, - 'typeDraft' => $commentTypeDraft, - ]; - } - - /** - * Test for the createContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentTypeDraft - */ - public function testCreateContentTypeDraftStructValues(array $data) - { - $originalType = $data['originalType']; - $typeDraft = $data['typeDraft']; - - // Names and descriptions tested in corresponding language test - $this->assertPropertiesCorrect( - [ - 'id' => $originalType->id, - 'names' => $originalType->names, - 'descriptions' => $originalType->descriptions, - 'identifier' => $originalType->identifier, - 'creatorId' => $originalType->creatorId, - 'modifierId' => $originalType->modifierId, - 'remoteId' => $originalType->remoteId, - 'urlAliasSchema' => $originalType->urlAliasSchema, - 'nameSchema' => $originalType->nameSchema, - 'isContainer' => $originalType->isContainer, - 'mainLanguageCode' => $originalType->mainLanguageCode, - 'defaultAlwaysAvailable' => $originalType->defaultAlwaysAvailable, - 'defaultSortField' => $originalType->defaultSortField, - 'defaultSortOrder' => $originalType->defaultSortOrder, - 'contentTypeGroups' => $originalType->contentTypeGroups, - 'fieldDefinitions' => $originalType->fieldDefinitions, - ], - $typeDraft - ); - - $this->assertInstanceOf( - 'DateTime', - $typeDraft->modificationDate - ); - $modificationDifference = $originalType->modificationDate->diff( - $typeDraft->modificationDate - ); - // No modification date is newer, interval is not inverted - $this->assertEquals(0, $modificationDifference->invert); - - $this->assertEquals( - ContentType::STATUS_DRAFT, - $typeDraft->status - ); - - return $data; - } - - /** - * Test for the createContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentTypeDraftStructValues - */ - public function testCreateContentTypeDraftStructLanguageDependentValues(array $data) - { - $originalType = $data['originalType']; - $typeDraft = $data['typeDraft']; - - $this->assertEquals( - [ - 'names' => $originalType->names, - 'descriptions' => $originalType->descriptions, - ], - [ - 'names' => $typeDraft->names, - 'descriptions' => $typeDraft->descriptions, - ] - ); - } - - /** - * Test for the createContentTypeDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentTypeDraft - */ - public function testCreateContentTypeDraftThrowsBadStateException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $commentType = $contentTypeService->loadContentTypeByIdentifier('comment'); - - $contentTypeService->createContentTypeDraft($commentType); - - // Throws exception, since type draft already exists - $contentTypeService->createContentTypeDraft($commentType); - /* END: Use Case */ - } - - /** - * Test for the deleteContentType() method. - * - * @covers \eZ\Publish\API\Repository\ContentTypeService::deleteContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifier - */ - public function testDeleteContentType() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $commentType = $contentTypeService->loadContentTypeByIdentifier('comment'); - - $contentTypeService->deleteContentType($commentType); - /* END: Use Case */ - - $contentTypeService->loadContentType($commentType->id); - $this->fail('Content type could be loaded after delete.'); - } - - /** - * Test for the deleteContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::deleteContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testDeleteContentType - */ - public function testDeleteContentTypeThrowsBadStateException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier('user'); - - // This call will fail with a "BadStateException" because there is at - // least on content object of type "user" in an eZ Publish demo - $contentTypeService->deleteContentType($contentType); - /* END: Use Case */ - } - - /** - * Test for the copyContentType() method. - * - * @return array - * - * @see \eZ\Publish\API\Repository\ContentTypeService::copyContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifier - */ - public function testCopyContentType() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $commentType = $contentTypeService->loadContentTypeByIdentifier('comment'); - $contentTypeGroup = $commentType->contentTypeGroups[0]; - $contentTypes = $contentTypeService->loadContentTypes($contentTypeGroup); - $contentTypesCount = count($contentTypes); - - // Complete copy of the "comment" type - $copiedType = $contentTypeService->copyContentType($commentType); - - $contentTypes = $contentTypeService->loadContentTypes($contentTypeGroup); - $contentTypeIdentifiers = array_map(static function (ContentType $contentType) { - return $contentType->identifier; - }, $contentTypes); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentType', - $copiedType - ); - - $this->assertContains($commentType->identifier, $contentTypeIdentifiers); - $this->assertContains($copiedType->identifier, $contentTypeIdentifiers); - $this->assertCount($contentTypesCount + 1, $contentTypes); - - $originalType = $contentTypeService->loadContentTypeByIdentifier('comment'); - - return [ - 'originalType' => $originalType, - 'copiedType' => $copiedType, - ]; - } - - /** - * Test for the copyContentType() method. - * - * @param array $data - * - * @see \eZ\Publish\API\Repository\ContentTypeService::copyContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCopyContentType - */ - public function testCopyContentTypeStructValues(array $data) - { - $originalType = $data['originalType']; - $copiedType = $data['copiedType']; - - $this->assertCopyContentTypeValues($originalType, $copiedType); - } - - /** - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $originalType - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $copiedType - * @param array $excludedProperties - */ - private function assertCopyContentTypeValues($originalType, $copiedType, $excludedProperties = []) - { - $allProperties = [ - 'names', - 'descriptions', - 'creatorId', - 'modifierId', - 'urlAliasSchema', - 'nameSchema', - 'isContainer', - 'mainLanguageCode', - 'contentTypeGroups', - ]; - $properties = array_diff($allProperties, $excludedProperties); - $this->assertStructPropertiesCorrect( - $originalType, - $copiedType, - $properties - ); - - $this->assertNotEquals( - $originalType->id, - $copiedType->id - ); - $this->assertNotEquals( - $originalType->remoteId, - $copiedType->remoteId - ); - $this->assertNotEquals( - $originalType->identifier, - $copiedType->identifier - ); - $this->assertNotEquals( - $originalType->creationDate, - $copiedType->creationDate - ); - $this->assertNotEquals( - $originalType->modificationDate, - $copiedType->modificationDate - ); - - foreach ($originalType->fieldDefinitions as $originalFieldDefinition) { - $copiedFieldDefinition = $copiedType->getFieldDefinition( - $originalFieldDefinition->identifier - ); - - $this->assertStructPropertiesCorrect( - $originalFieldDefinition, - $copiedFieldDefinition, - [ - 'identifier', - 'names', - 'descriptions', - 'fieldGroup', - 'position', - 'fieldTypeIdentifier', - 'isTranslatable', - 'isRequired', - 'isInfoCollector', - 'validatorConfiguration', - 'defaultValue', - 'isSearchable', - ] - ); - $this->assertNotEquals( - $originalFieldDefinition->id, - $copiedFieldDefinition->id - ); - } - } - - /** - * Test for the copyContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::copyContentType($contentType, $user) - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCopyContentType - */ - public function testCopyContentTypeWithSecondParameter() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $user = $this->createUserVersion1(); - - $commentType = $contentTypeService->loadContentTypeByIdentifier('comment'); - - // Complete copy of the "comment" type - $copiedType = $contentTypeService->copyContentType($commentType, $user); - /* END: Use Case */ - - $this->assertPropertiesCorrect( - [ - 'creatorId' => $user->id, - 'modifierId' => $user->id, - ], - $copiedType - ); - $this->assertCopyContentTypeValues($commentType, $copiedType, ['creatorId', 'modifierId']); - } - - /** - * Test for the assignContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::assignContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeGroupByIdentifier - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifier - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentType - */ - public function testAssignContentTypeGroup() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $mediaGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Media'); - $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); - - $contentTypeService->assignContentTypeGroup($folderType, $mediaGroup); - /* END: Use Case */ - - $loadedType = $contentTypeService->loadContentType($folderType->id); - - foreach ($loadedType->contentTypeGroups as $loadedGroup) { - if ($mediaGroup->id == $loadedGroup->id) { - return; - } - } - $this->fail( - sprintf( - 'Group with ID "%s" not assigned to Content Type.', - $mediaGroup->id - ) - ); - } - - /** - * Test for the assignContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::assignContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testAssignContentTypeGroup - */ - public function testAssignContentTypeGroupThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); - $assignedGroups = $folderType->contentTypeGroups; - - foreach ($assignedGroups as $assignedGroup) { - // Throws an exception, since group is already assigned - $contentTypeService->assignContentTypeGroup($folderType, $assignedGroup); - } - /* END: Use Case */ - } - - /** - * Test for the unassignContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::unassignContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testAssignContentTypeGroup - */ - public function testUnassignContentTypeGroup() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); - - $mediaGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Media'); - $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); - - // May not unassign last group - $contentTypeService->assignContentTypeGroup($folderType, $mediaGroup); - - $contentTypeService->unassignContentTypeGroup($folderType, $contentGroup); - /* END: Use Case */ - - $loadedType = $contentTypeService->loadContentType($folderType->id); - - foreach ($loadedType->contentTypeGroups as $assignedGroup) { - if ($assignedGroup->id == $contentGroup->id) { - $this->fail( - sprintf( - 'Could not unassign group with ID "%s".', - $assignedGroup->id - ) - ); - } - } - } - - /** - * Test for the unassignContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::unassignContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUnassignContentTypeGroup - */ - public function testUnassignContentTypeGroupThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); - $notAssignedGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Media'); - - // Throws an exception, since "Media" group is not assigned to "folder" - $contentTypeService->unassignContentTypeGroup($folderType, $notAssignedGroup); - /* END: Use Case */ - } - - /** - * Test for the unassignContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::unassignContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUnassignContentTypeGroup - */ - public function testUnassignContentTypeGroupThrowsBadStateException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); - $assignedGroups = $folderType->contentTypeGroups; - - foreach ($assignedGroups as $assignedGroup) { - // Throws an exception, when last group is to be removed - $contentTypeService->unassignContentTypeGroup($folderType, $assignedGroup); - } - /* END: Use Case */ - } - - /** - * Test for the createContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeGroup - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentTypeGroup - */ - public function testCreateContentTypeGroupInTransactionWithRollback() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Get create struct and set language property - $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct('new-group'); - /* @todo uncomment when support for multilingual names and descriptions is added - $groupCreate->mainLanguageCode = 'eng-GB'; - */ - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Create the new content type group - $groupId = $contentTypeService->createContentTypeGroup($groupCreate)->id; - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes - $repository->rollback(); - - try { - // This call will fail with a "NotFoundException" - $contentTypeService->loadContentTypeGroup($groupId); - } catch (NotFoundException $e) { - return; - } - /* END: Use Case */ - - $this->fail('Can still load content type group after rollback'); - } - - /** - * Test for the createContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeGroup - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentTypeGroup - */ - public function testCreateContentTypeGroupInTransactionWithCommit() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Get create struct and set language property - $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct('new-group'); - /* @todo uncomment when support for multilingual names and descriptions is added - $groupCreate->mainLanguageCode = 'eng-GB'; - */ - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Create the new content type group - $groupId = $contentTypeService->createContentTypeGroup($groupCreate)->id; - - // Rollback all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Load created content type group - $group = $contentTypeService->loadContentTypeGroup($groupId); - /* END: Use Case */ - - $this->assertEquals($groupId, $group->id); - } - - /** - * Test for the updateContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUpdateContentTypeGroup - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeGroupByIdentifier - */ - public function testUpdateContentTypeGroupInTransactionWithRollback() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Load an existing group - $group = $contentTypeService->loadContentTypeGroupByIdentifier('Setup'); - - // Get an update struct and change the identifier - $groupUpdate = $contentTypeService->newContentTypeGroupUpdateStruct(); - $groupUpdate->identifier = 'Teardown'; - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Apply update to group - $contentTypeService->updateContentTypeGroup($group, $groupUpdate); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes - $repository->rollback(); - - // Load updated group, it will be unchanged - $updatedGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Setup'); - /* END: Use Case */ - - $this->assertEquals('Setup', $updatedGroup->identifier); - } - - /** - * Test for the updateContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUpdateContentTypeGroup - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeGroupByIdentifier - */ - public function testUpdateContentTypeGroupInTransactionWithCommit() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Load an existing group - $group = $contentTypeService->loadContentTypeGroupByIdentifier('Setup'); - - // Get an update struct and change the identifier - $groupUpdate = $contentTypeService->newContentTypeGroupUpdateStruct(); - $groupUpdate->identifier = 'Teardown'; - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Apply update to group - $contentTypeService->updateContentTypeGroup($group, $groupUpdate); - - // Commit all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Load updated group by it's new identifier "Teardown" - $updatedGroup = $contentTypeService->loadContentTypeGroupByIdentifier( - 'Teardown' - ); - /* END: Use Case */ - - $this->assertEquals('Teardown', $updatedGroup->identifier); - } - - /** - * Test for the deleteContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::deleteContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testDeleteContentTypeGroup - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeGroupByIdentifierThrowsNotFoundException - */ - public function testDeleteContentTypeGroupWithRollback() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Get a group create struct - $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct( - 'new-group' - ); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Create the new group - $group = $contentTypeService->createContentTypeGroup($groupCreate); - - // Delete the currently created group - $contentTypeService->deleteContentTypeGroup($group); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes - $repository->rollback(); - - try { - // This call will fail with an "NotFoundException" - $contentTypeService->loadContentTypeGroupByIdentifier('new-group'); - } catch (NotFoundException $e) { - // Expected error path - } - /* END: Use Case */ - - $this->assertTrue(isset($e), 'Group not deleted after rollback'); - } - - /** - * Test for the deleteContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::deleteContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testDeleteContentTypeGroup - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeGroupByIdentifierThrowsNotFoundException - */ - public function testDeleteContentTypeGroupWithCommit() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Get a group create struct - $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct( - 'new-group' - ); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Create the new group - $group = $contentTypeService->createContentTypeGroup($groupCreate); - - // Delete the currently created group - $contentTypeService->deleteContentTypeGroup($group); - - // Commit all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - try { - // This call will fail with an "NotFoundException" - $contentTypeService->loadContentTypeGroupByIdentifier('new-group'); - } catch (NotFoundException $e) { - // Expected error path - } - /* END: Use Case */ - - $this->assertTrue(isset($e), 'Group not deleted after commit.'); - } - - /** - * Test for the createContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifierThrowsNotFoundException - */ - public function testCreateContentTypeInTransactionWithRollback() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Get create struct and set some properties - $typeCreate = $contentTypeService->newContentTypeCreateStruct('blog-post'); - $typeCreate->mainLanguageCode = 'eng-GB'; - $typeCreate->names = ['eng-GB' => 'Blog post']; - - $titleFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); - $titleFieldCreate->names = ['eng-GB' => 'Title']; - $titleFieldCreate->position = 1; - $typeCreate->addFieldDefinition($titleFieldCreate); - - $groups = [ - $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), - ]; - - // Create content type - $contentTypeDraft = $contentTypeService->createContentType( - $typeCreate, - $groups - ); - - // Publish the content type draft - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes. - $repository->rollback(); - - try { - // This call will fail with a "NotFoundException" - $contentTypeService->loadContentTypeByIdentifier('blog-post'); - } catch (NotFoundException $e) { - // Expected execution path - } - /* END: Use Case */ - - $this->assertTrue(isset($e), 'Can still load content type after rollback.'); - } - - /** - * Test for the createContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifierThrowsNotFoundException - */ - public function testCreateContentTypeInTransactionWithCommit() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Get create struct and set some properties - $typeCreate = $contentTypeService->newContentTypeCreateStruct('blog-post'); - $typeCreate->mainLanguageCode = 'eng-GB'; - $typeCreate->names = ['eng-GB' => 'Blog post']; - - $titleFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); - $titleFieldCreate->names = ['eng-GB' => 'Title']; - $titleFieldCreate->position = 1; - $typeCreate->addFieldDefinition($titleFieldCreate); - - $groups = [ - $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), - ]; - - // Create content type - $contentTypeDraft = $contentTypeService->createContentType( - $typeCreate, - $groups - ); - - // Publish the content type draft - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - // Commit all changes. - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Load the newly created content type - $contentType = $contentTypeService->loadContentTypeByIdentifier('blog-post'); - /* END: Use Case */ - - $this->assertEquals($contentTypeDraft->id, $contentType->id); - } - - /** - * Test for the copyContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::copyContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCopyContentType - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifier - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeThrowsNotFoundException - */ - public function testCopyContentTypeInTransactionWithRollback() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Load content type to copy - $contentType = $contentTypeService->loadContentTypeByIdentifier('comment'); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Complete copy of the content type - $copiedType = $contentTypeService->copyContentType($contentType); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes - $repository->rollback(); - - try { - // This call will fail with a "NotFoundException" - $contentTypeService->loadContentType($copiedType->id); - } catch (NotFoundException $e) { - // Expected execution path - } - /* END: Use Case */ - - $this->assertTrue(isset($e), 'Can still load copied content type after rollback.'); - } - - /** - * Test for the copyContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::copyContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCopyContentType - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifier - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeThrowsNotFoundException - */ - public function testCopyContentTypeInTransactionWithCommit() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Load content type to copy - $contentType = $contentTypeService->loadContentTypeByIdentifier('comment'); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Complete copy of the content type - $contentTypeId = $contentTypeService->copyContentType($contentType)->id; - - // Commit all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Load the new content type copy. - $copiedContentType = $contentTypeService->loadContentType($contentTypeId); - /* END: Use Case */ - - $this->assertEquals($contentTypeId, $copiedContentType->id); - } - - /** - * Test for the deleteContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::deleteContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCopyContentType - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifierThrowsNotFoundException - */ - public function testDeleteContentTypeInTransactionWithRollback() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Load content type to copy - $contentType = $contentTypeService->loadContentTypeByIdentifier('comment'); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Delete the "comment" content type. - $contentTypeService->deleteContentType($contentType); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes - $repository->rollback(); - - // Load currently deleted and rollbacked content type - $commentType = $contentTypeService->loadContentTypeByIdentifier('comment'); - /* END: Use Case */ - - $this->assertEquals('comment', $commentType->identifier); - } - - /** - * Test for the deleteContentType() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::deleteContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCopyContentType - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testLoadContentTypeByIdentifierThrowsNotFoundException - */ - public function testDeleteContentTypeInTransactionWithCommit() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - // Load content type to copy - $contentType = $contentTypeService->loadContentTypeByIdentifier('comment'); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Delete the "comment" content type. - $contentTypeService->deleteContentType($contentType); - - // Commit all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - try { - // This call will fail with a "NotFoundException" - $contentTypeService->loadContentTypeByIdentifier('comment'); - } catch (NotFoundException $e) { - // Expected execution path - } - /* END: Use Case */ - - $this->assertTrue(isset($e), 'Can still load content type after rollback.'); - } - - /** - * Test for the assignContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::assignContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testAssignContentTypeGroup - */ - public function testAssignContentTypeGroupInTransactionWithRollback() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $mediaGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Media'); - $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Assign group to content type - $contentTypeService->assignContentTypeGroup($folderType, $mediaGroup); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes - $repository->rollback(); - - // Load all content types assigned to media group - $contentTypes = $contentTypeService->loadContentTypes($mediaGroup); - - $contentTypeIds = []; - foreach ($contentTypes as $contentType) { - $contentTypeIds[] = $contentType->id; - } - /* END: Use Case */ - - $this->assertFalse( - in_array($folderType->id, $contentTypeIds), - 'Folder content type is still in media group after rollback.' - ); - } - - /** - * Test for the assignContentTypeGroup() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::assignContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testAssignContentTypeGroup - */ - public function testAssignContentTypeGroupInTransactionWithCommit() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $mediaGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Media'); - $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Assign group to content type - $contentTypeService->assignContentTypeGroup($folderType, $mediaGroup); - - // Commit all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Load all content types assigned to media group - $contentTypes = $contentTypeService->loadContentTypes($mediaGroup); - - $contentTypeIds = []; - foreach ($contentTypes as $contentType) { - $contentTypeIds[] = $contentType->id; - } - /* END: Use Case */ - - $this->assertTrue( - in_array($folderType->id, $contentTypeIds), - 'Folder content type not in media group after commit.' - ); - } - - /** - * Test for the isContentTypeUsed() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::isContentTypeUsed() - */ - public function testIsContentTypeUsed() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - - $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); - $eventType = $contentTypeService->loadContentTypeByIdentifier('event'); - - $isFolderUsed = $contentTypeService->isContentTypeUsed($folderType); - $isEventUsed = $contentTypeService->isContentTypeUsed($eventType); - /* END: Use Case */ - - $this->assertTrue($isFolderUsed); - $this->assertFalse($isEventUsed); - } - - /** - * @covers \eZ\Publish\API\Repository\ContentTypeService::removeContentTypeTranslation - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testRemoveContentTypeTranslation() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $contentTypeDraft = $this->createContentTypeDraft(); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); - - $this->assertEquals( - [ - 'eng-US' => 'Blog post', - 'ger-DE' => 'Blog-Eintrag', - ], - $contentType->getNames() - ); - - $contentTypeService->removeContentTypeTranslation( - $contentTypeService->createContentTypeDraft($contentType), - 'ger-DE' - ); - - $loadedContentTypeDraft = $contentTypeService->loadContentTypeDraft($contentType->id); - - $this->assertArrayNotHasKey('ger-DE', $loadedContentTypeDraft->getNames()); - $this->assertArrayNotHasKey('ger-DE', $loadedContentTypeDraft->getDescriptions()); - - foreach ($loadedContentTypeDraft->fieldDefinitions as $fieldDefinition) { - $this->assertArrayNotHasKey('ger-DE', $fieldDefinition->getNames()); - $this->assertArrayNotHasKey('ger-DE', $fieldDefinition->getDescriptions()); - } - } - - /** - * @covers \eZ\Publish\API\Repository\ContentTypeService::removeContentTypeTranslation - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testRemoveContentTypeTranslationWithMultilingualData() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $selectionFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('selection', 'ezselection'); - - $selectionFieldCreate->names = [ - 'eng-US' => 'Selection', - 'ger-DE' => 'GER Selection', - ]; - - $selectionFieldCreate->fieldGroup = 'blog-content'; - $selectionFieldCreate->position = 3; - $selectionFieldCreate->isTranslatable = true; - $selectionFieldCreate->isRequired = true; - $selectionFieldCreate->isInfoCollector = false; - $selectionFieldCreate->validatorConfiguration = []; - $selectionFieldCreate->fieldSettings = [ - 'multilingualOptions' => [ - 'eng-US' => [ - 0 => 'A first', - 1 => 'Bielefeld', - 2 => 'Sindelfingen', - 3 => 'Turtles', - 4 => 'Zombies', - ], - 'ger-DE' => [ - 0 => 'Berlin', - 1 => 'Cologne', - 2 => 'Bonn', - 3 => 'Frankfurt', - 4 => 'Hamburg', - ], - ], - ]; - $selectionFieldCreate->isSearchable = false; - - $contentTypeDraft = $this->createContentTypeDraft([$selectionFieldCreate]); - - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); - - $contentTypeService->removeContentTypeTranslation( - $contentTypeService->createContentTypeDraft($contentType), - 'ger-DE' - ); - - $loadedContentTypeDraft = $contentTypeService->loadContentTypeDraft($contentType->id); - - $fieldDefinition = $loadedContentTypeDraft->getFieldDefinition('selection'); - $this->assertArrayNotHasKey('ger-DE', $fieldDefinition->fieldSettings['multilingualOptions']); - } - - /** - * @covers \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeDraft - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testUpdateContentTypeDraftWithNewTranslationWithMultilingualData() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $selectionFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('selection', 'ezselection'); - - $selectionFieldCreate->names = [ - 'eng-US' => 'Selection', - 'ger-DE' => 'GER Selection', - ]; - - $selectionFieldCreate->fieldGroup = 'blog-content'; - $selectionFieldCreate->position = 3; - $selectionFieldCreate->isTranslatable = true; - $selectionFieldCreate->isRequired = true; - $selectionFieldCreate->isInfoCollector = false; - $selectionFieldCreate->validatorConfiguration = []; - $selectionFieldCreate->fieldSettings = [ - 'multilingualOptions' => [ - 'eng-US' => [ - 0 => 'A first', - 1 => 'Bielefeld', - 2 => 'Sindelfingen', - 3 => 'Turtles', - 4 => 'Zombies', - ], - 'ger-DE' => [ - 0 => 'Berlin', - 1 => 'Cologne', - 2 => 'Bonn', - 3 => 'Frankfurt', - 4 => 'Hamburg', - ], - ], - ]; - $selectionFieldCreate->isSearchable = false; - - $contentTypeDraft = $this->createContentTypeDraft([$selectionFieldCreate]); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); - // sanity check - self::assertEquals( - ['eng-US', 'ger-DE'], - array_keys($contentType->getNames()) - ); - - $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); - $updateStruct = $contentTypeService->newContentTypeUpdateStruct(); - $updateStruct->names = [ - 'eng-GB' => 'BrE blog post', - ]; - - $selectionFieldUpdate = $contentTypeService->newFieldDefinitionUpdateStruct(); - - $selectionFieldUpdate->names = [ - 'eng-GB' => 'GB Selection', - ]; - - $selectionFieldUpdate->fieldGroup = 'blog-content'; - $selectionFieldUpdate->position = 3; - $selectionFieldUpdate->isTranslatable = true; - $selectionFieldUpdate->isRequired = true; - $selectionFieldUpdate->isInfoCollector = false; - $selectionFieldUpdate->validatorConfiguration = []; - $selectionFieldUpdate->fieldSettings = [ - 'multilingualOptions' => [ - 'eng-US' => [ - 0 => 'A first', - 1 => 'Bielefeld', - 2 => 'Sindelfingen', - 3 => 'Turtles', - 4 => 'Zombies', - ], - 'ger-DE' => [ - 0 => 'Berlin', - 1 => 'Cologne', - 2 => 'Bonn', - 3 => 'Frankfurt', - 4 => 'Hamburg', - ], - 'eng-GB' => [ - 0 => 'London', - 1 => 'Liverpool', - ], - ], - ]; - $selectionFieldUpdate->isSearchable = false; - - $contentTypeService->updateFieldDefinition( - $contentTypeDraft, - $contentType->getFieldDefinition('selection'), - $selectionFieldUpdate - ); - $contentTypeService->updateContentTypeDraft($contentTypeDraft, $updateStruct); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - $loadedFieldDefinition = $contentTypeService->loadContentType($contentType->id)->getFieldDefinition('selection'); - self::assertEquals( - [ - 'eng-US' => [ - 0 => 'A first', - 1 => 'Bielefeld', - 2 => 'Sindelfingen', - 3 => 'Turtles', - 4 => 'Zombies', - ], - 'ger-DE' => [ - 0 => 'Berlin', - 1 => 'Cologne', - 2 => 'Bonn', - 3 => 'Frankfurt', - 4 => 'Hamburg', - ], - 'eng-GB' => [ - 0 => 'London', - 1 => 'Liverpool', - ], - ], - $loadedFieldDefinition->fieldSettings['multilingualOptions'] - ); - } - - /** - * Test for the deleteUserDrafts() method. - * - * @see \eZ\Publish\API\Repository\ContentTypeService::deleteUserDrafts() - */ - public function testDeleteUserDrafts() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - $permissionResolver = $repository->getPermissionResolver(); - $contentTypeService = $repository->getContentTypeService(); - - $draft = $this->createContentTypeDraft(); - $user = $permissionResolver->getCurrentUserReference(); - - $contentTypeService->deleteUserDrafts($user->getUserId()); - $contentTypeDraft = $contentTypeService->loadContentTypeDraft($draft->id); - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/AuthorIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/AuthorIntegrationTest.php deleted file mode 100644 index 0e50a0bc4c..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/AuthorIntegrationTest.php +++ /dev/null @@ -1,554 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\Author\Author; -use eZ\Publish\Core\FieldType\Author\AuthorCollection; -use eZ\Publish\Core\FieldType\Author\Type; -use eZ\Publish\Core\FieldType\Author\Value as AuthorValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class AuthorIntegrationTest extends SearchMultivaluedBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezauthor'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return [ - 'defaultAuthor' => [ - 'type' => 'choice', - 'default' => Type::DEFAULT_VALUE_EMPTY, - ], - ]; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return [ - 'defaultAuthor' => Type::DEFAULT_VALUE_EMPTY, - ]; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return []; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return []; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'unknown' => ['value' => 42], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return \eZ\Publish\Core\FieldType\Author\Value - */ - public function getValidCreationFieldData() - { - return new AuthorValue( - [ - new Author( - [ - 'id' => 23, - 'name' => 'Hans Mueller', - 'email' => 'hans@example.com', - ] - ), - ] - ); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'Hans Mueller'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - AuthorValue::class, - $field->value - ); - - $expectedData = [ - 'authors' => new AuthorCollection( - [ - new Author( - [ - 'id' => 23, - 'name' => 'Hans Mueller', - 'email' => 'hans@example.com', - ] - ), - ] - ), - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - ['Sindelfingen', 'eZ\\Publish\\API\\Repository\\Exceptions\\InvalidArgumentException'], - ]; - } - - /** - * Get update field externals data. - * - * @return \eZ\Publish\Core\FieldType\Author\Value - */ - public function getValidUpdateFieldData() - { - return new AuthorValue( - [ - new Author( - [ - 'id' => 42, - 'name' => 'Lieschen Mueller', - 'email' => 'lieschen@example.com', - ] - ), - ] - ); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - AuthorValue::class, - $field->value - ); - - $expectedData = [ - 'authors' => new AuthorCollection( - [ - new Author( - [ - 'id' => 42, - 'name' => 'Lieschen Mueller', - 'email' => 'lieschen@example.com', - ] - ), - ] - ), - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - AuthorValue::class, - $field->value - ); - - $expectedData = [ - 'authors' => new AuthorCollection( - [ - new Author( - [ - 'id' => 23, - 'name' => 'Hans Mueller', - 'email' => 'hans@example.com', - ] - ), - ] - ), - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new AuthorValue( - [ - new Author( - [ - 'id' => 23, - 'name' => 'Hans Mueller', - 'email' => 'hans@example.com', - ] - ), - ] - ), - [ - [ - 'id' => 23, - 'name' => 'Hans Mueller', - 'email' => 'hans@example.com', - ], - ], - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - [ - [ - 'id' => 23, - 'name' => 'Hans Mueller', - 'email' => 'hans@example.com', - ], - ], - new AuthorValue( - [ - new Author( - [ - 'id' => 23, - 'name' => 'Hans Mueller', - 'email' => 'hans@example.com', - ] - ), - ] - ), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new AuthorValue()], - [new AuthorValue([])], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - [ - new AuthorValue( - [ - new Author( - [ - 'id' => 23, - 'name' => 'Hans Mueller', - 'email' => 'hans@example.com', - ] - ), - ] - ), - ], - ]; - } - - protected function getValidSearchValueOne() - { - return [ - new Author( - [ - 'id' => 2, - 'name' => 'Ferdinand', - 'email' => 'ferdinand@example.com', - ] - ), - ]; - } - - protected function getValidSearchValueTwo() - { - return [ - new Author( - [ - 'id' => 3, - 'name' => 'Greta', - 'email' => 'greta@example.com', - ] - ), - ]; - } - - protected function getSearchTargetValueOne() - { - return 'Ferdinand'; - } - - protected function getSearchTargetValueTwo() - { - return 'Greta'; - } - - protected function getAdditionallyIndexedFieldData() - { - return [ - [ - 'id', - 2, - 3, - ], - [ - 'email', - 'ferdinand@example.com', - 'greta@example.com', - ], - [ - 'sort_value', - 'Ferdinand', - 'Greta', - ], - ]; - } - - protected function getValidMultivaluedSearchValuesOne() - { - return [ - new Author( - [ - 'id' => 1, - 'name' => 'Antoinette', - 'email' => 'antoinette@example.com', - ] - ), - new Author( - [ - 'id' => 2, - 'name' => 'Ferdinand', - 'email' => 'ferdinand@example.com', - ] - ), - ]; - } - - protected function getValidMultivaluedSearchValuesTwo() - { - return [ - new Author( - [ - 'id' => 3, - 'name' => 'Greta', - 'email' => 'greta@example.com', - ] - ), - new Author( - [ - 'id' => 4, - 'name' => 'Leopold', - 'email' => 'leopold@example.com', - ] - ), - new Author( - [ - 'id' => 5, - 'name' => 'Maximilian', - 'email' => 'maximilian@example.com', - ] - ), - ]; - } - - protected function getMultivaluedSearchTargetValuesOne() - { - return ['Antoinette', 'Ferdinand']; - } - - protected function getMultivaluedSearchTargetValuesTwo() - { - return ['Greta', 'Leopold', 'Maximilian']; - } - - protected function getAdditionallyIndexedMultivaluedFieldData() - { - return [ - [ - 'id', - [1, 2], - [3, 4, 5], - ], - [ - 'email', - ['antoinette@example.com', 'ferdinand@example.com'], - ['greta@example.com', 'leopold@example.com', 'maximilian@example.com'], - ], - ]; - } - - protected function getFullTextIndexedFieldData() - { - return [ - ['Ferdinand', 'Greta'], - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/BaseIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/BaseIntegrationTest.php deleted file mode 100644 index 500fe3c2ac..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/BaseIntegrationTest.php +++ /dev/null @@ -1,1218 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository; -use eZ\Publish\API\Repository\Exceptions\ContentTypeFieldDefinitionValidationException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Tests; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load created content type - * - Create content object of new content type - * - Load created content - * - Publish created content - * - Update content - * - Copy created content - * - Remove copied content - * - Test toHash - * - Test fromHash - * - * @group integration - * @group field-type - * - * @todo Finalize dependencies to other tests (including groups!) - */ -abstract class BaseIntegrationTest extends Tests\BaseTest -{ - /** - * Content version archive limit (default). - * Note: currently there is no way to retrieve this setting from the ContentService. - */ - public const VERSION_ARCHIVE_LIMIT = 5; - - /** - * Identifier of the custom field. - * - * @var string - */ - protected $customFieldIdentifier = 'data'; - - /** - * Get name of tested field type. - * - * @return string - */ - abstract public function getTypeName(); - - /** - * Get expected settings schema. - * - * @return array - */ - abstract public function getSettingsSchema(); - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - abstract public function getValidFieldSettings(); - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - abstract public function getInvalidFieldSettings(); - - /** - * Get expected validator schema. - * - * @return array - */ - abstract public function getValidatorSchema(); - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - abstract public function getValidValidatorConfiguration(); - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - abstract public function getInvalidValidatorConfiguration(); - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - abstract public function getValidCreationFieldData(); - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - abstract public function getFieldName(); - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - abstract public function assertFieldDataLoadedCorrect(Field $field); - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - abstract public function provideInvalidCreationFieldData(); - - /** - * Get valid field data for updating content. - * - * @return mixed - */ - abstract public function getValidUpdateFieldData(); - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidUpdateFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - abstract public function assertUpdatedFieldDataLoadedCorrect(Field $field); - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - abstract public function provideInvalidUpdateFieldData(); - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - abstract public function assertCopiedFieldDataLoadedCorrectly(Field $field); - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - abstract public function provideToHashData(); - - /** - * Get hashes and their respective converted values. - * - * This is a PHPUnit data provider - * - * The returned records must have the the input hash assigned to the - * first index and the expected value result to the second. For example: - * - * <code> - * array( - * array( - * array( 'myValue' => true ), - * new MyValue( true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - abstract public function provideFromHashData(); - - /** - * Method called after content creation. - * - * Useful, if additional stuff should be executed (like creating the actual - * user). - * - * We cannot just overwrite the testCreateContent method, since this messes - * up PHPUnits @depends sorting of tests, so everything will be skipped. - * - * @param \eZ\Publish\API\Repository $repository - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function postCreationHook(Repository\Repository $repository, Repository\Values\Content\Content $content) - { - // Do nothing by default - } - - public function getValidContentTypeConfiguration(): array - { - return []; - } - - public function getValidFieldConfiguration(): array - { - return []; - } - - public function testCreateContentType() - { - $contentType = $this->createContentType( - $this->getValidFieldSettings(), - $this->getValidValidatorConfiguration(), - $this->getValidContentTypeConfiguration(), - $this->getValidFieldConfiguration() - ); - - $this->assertNotNull($contentType->id); - - return $contentType; - } - - /** - * For checking if field type can be used in name/url schema (pattern). - * - * @return bool - */ - protected function checkSupportGetName() - { - return true; - } - - /** - * Creates a content type under test with $fieldSettings and - * $validatorConfiguration. - * - * $typeCreateOverride and $fieldCreateOverride can be used to selectively - * override settings on the type create struct and field create struct. - * - * @param mixed $fieldSettings - * @param mixed $validatorConfiguration - * @param array $typeCreateOverride - * @param array $fieldCreateOverride - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType - */ - protected function createContentType($fieldSettings, $validatorConfiguration, array $typeCreateOverride = [], array $fieldCreateOverride = []) - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $contentTypeIdentifier = 'test-' . $this->getTypeName(); - - try { - return $contentTypeService->loadContentTypeByIdentifier($contentTypeIdentifier); - } catch (NotFoundException $e) { - // Move on to creating Content Type - } - - $createStruct = $contentTypeService->newContentTypeCreateStruct( - $contentTypeIdentifier - ); - $createStruct->mainLanguageCode = $this->getOverride('mainLanguageCode', $typeCreateOverride, 'eng-GB'); - $createStruct->remoteId = $this->getTypeName(); - $createStruct->names = $this->getOverride('names', $typeCreateOverride, ['eng-GB' => 'Test']); - $createStruct->creatorId = 14; - $createStruct->creationDate = $this->createDateTime(); - - if ($this->checkSupportGetName()) { - $createStruct->nameSchema = '<name> <data>'; - $createStruct->urlAliasSchema = '<data>'; - } - - $nameFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('name', 'ezstring'); - $nameFieldCreate->names = ['eng-GB' => 'Title']; - $nameFieldCreate->fieldGroup = 'main'; - $nameFieldCreate->position = 1; - $nameFieldCreate->isTranslatable = true; - $createStruct->addFieldDefinition($nameFieldCreate); - - $dataFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('data', $this->getTypeName()); - $dataFieldCreate->names = $this->getOverride('names', $fieldCreateOverride, ['eng-GB' => 'Title']); - $dataFieldCreate->fieldGroup = 'main'; - $dataFieldCreate->position = 2; - $dataFieldCreate->isTranslatable = $this->getOverride('isTranslatable', $fieldCreateOverride, false); - - // Custom settings - $dataFieldCreate->fieldSettings = $fieldSettings; - $dataFieldCreate->validatorConfiguration = $validatorConfiguration; - - $createStruct->addFieldDefinition($dataFieldCreate); - - $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); - $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); - - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); - - return $contentType; - } - - /** - * Retrieves a value for $key from $overrideValues, falling back to - * $default. - * - * @param string $key - * @param array $overrideValues - * @param mixed $default - * - * @return mixed - */ - protected function getOverride($key, array $overrideValues, $default) - { - return isset($overrideValues[$key]) ? $overrideValues[$key] : $default; - } - - /** - * @covers \eZ\Publish\Core\FieldType\FieldType::isEmptyValue - * @dataProvider providerForTestIsEmptyValue - */ - public function testIsEmptyValue($value) - { - $this->assertTrue($this->getRepository()->getFieldTypeService()->getFieldType($this->getTypeName())->isEmptyValue($value)); - } - - abstract public function providerForTestIsEmptyValue(); - - /** - * @covers \eZ\Publish\Core\FieldType\FieldType::isEmptyValue - * @dataProvider providerForTestIsNotEmptyValue - */ - public function testIsNotEmptyValue($value) - { - $this->assertFalse($this->getRepository()->getFieldTypeService()->getFieldType($this->getTypeName())->isEmptyValue($value)); - } - - abstract public function providerForTestIsNotEmptyValue(); - - /** - * @depends testCreateContentType - */ - public function testContentTypeField($contentType) - { - $this->assertSame( - $this->getTypeName(), - $contentType->fieldDefinitions[1]->fieldTypeIdentifier - ); - } - - /** - * @depends testCreateContentType - */ - public function testLoadContentTypeField() - { - $contentType = $this->testCreateContentType(); - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - return $contentTypeService->loadContentType($contentType->id); - } - - /** - * @depends testLoadContentTypeField - */ - public function testLoadContentTypeFieldType($contentType) - { - $this->assertSame( - $this->getTypeName(), - $contentType->fieldDefinitions[1]->fieldTypeIdentifier - ); - - return $contentType->fieldDefinitions[1]; - } - - public function testSettingsSchema() - { - $repository = $this->getRepository(); - $fieldTypeService = $repository->getFieldTypeService(); - $fieldType = $fieldTypeService->getFieldType($this->getTypeName()); - - $this->assertEquals( - $this->getSettingsSchema(), - $fieldType->getSettingsSchema() - ); - } - - /** - * @depends testLoadContentTypeFieldType - */ - public function testLoadContentTypeFieldData(FieldDefinition $fieldDefinition) - { - $this->assertEquals( - $this->getTypeName(), - $fieldDefinition->fieldTypeIdentifier, - 'Loaded fieldTypeIdentifier does not match.' - ); - $this->assertEquals( - $this->getValidFieldSettings(), - $fieldDefinition->fieldSettings, - 'Loaded fieldSettings do not match.' - ); - $this->assertEquals( - $this->getValidValidatorConfiguration(), - $fieldDefinition->validatorConfiguration, - 'Loaded validatorConfiguration does not match.' - ); - } - - /** - * @depends testCreateContentType - */ - public function testCreateContentTypeFailsWithInvalidFieldSettings() - { - $this->expectException(ContentTypeFieldDefinitionValidationException::class); - - $this->createContentType( - $this->getInvalidFieldSettings(), - $this->getValidValidatorConfiguration() - ); - } - - public function testValidatorSchema() - { - $repository = $this->getRepository(); - $fieldTypeService = $repository->getFieldTypeService(); - $fieldType = $fieldTypeService->getFieldType($this->getTypeName()); - - $this->assertEquals( - $this->getValidatorSchema(), - $fieldType->getValidatorConfigurationSchema() - ); - } - - /** - * @depends testCreateContentType - */ - public function testCreateContentTypeFailsWithInvalidValidatorConfiguration() - { - $this->expectException(ContentTypeFieldDefinitionValidationException::class); - - $this->createContentType( - $this->getValidFieldSettings(), - $this->getInvalidValidatorConfiguration() - ); - } - - /** - * @depends testLoadContentTypeField - */ - public function testCreateContent() - { - return $this->createContent($this->getValidCreationFieldData()); - } - - /** - * Creates content with $fieldData. - * - * @param mixed $fieldData - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - protected function createContent($fieldData, $contentType = null) - { - if ($contentType === null) { - $contentType = $this->testCreateContentType(); - } - - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-US'); - $createStruct->setField('name', 'Test object'); - $createStruct->setField( - 'data', - $fieldData - ); - - $createStruct->remoteId = 'abcdef0123456789abcdef0123456789'; - $createStruct->alwaysAvailable = true; - - return $contentService->createContent($createStruct); - } - - /** - * Create multilingual content of given name and FT-specific data. - * - * @param array $names Content names in the form of <code>[languageCode => name]</code> - * @param array $fieldData FT-specific data in the form of <code>[languageCode => data]</code> - * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - protected function createMultilingualContent(array $names, array $fieldData, array $locationCreateStructs = []) - { - self::assertEquals(array_keys($names), array_keys($fieldData), 'Languages passed to names and data differ'); - - $contentType = $this->createContentType( - $this->getValidFieldSettings(), - $this->getValidValidatorConfiguration(), - $this->getValidContentTypeConfiguration(), - array_merge( - $this->getValidFieldConfiguration(), - ['isTranslatable' => true] - ) - ); - - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-US'); - foreach ($names as $languageCode => $name) { - $createStruct->setField('name', $name, $languageCode); - } - foreach ($fieldData as $languageCode => $value) { - $createStruct->setField('data', $value, $languageCode); - } - - $createStruct->remoteId = md5(uniqid('', true) . microtime()); - $createStruct->alwaysAvailable = true; - - return $contentService->createContent($createStruct, $locationCreateStructs); - } - - /** - * @depends testCreateContent - */ - public function testCreatedFieldType($content) - { - foreach ($content->getFields() as $field) { - if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { - return $field; - } - } - - $this->fail('Custom field not found.'); - } - - /** - * @depends testCreateContent - */ - public function testPublishContent() - { - $draft = $this->testCreateContent(); - - if (!$draft->getVersionInfo()->isDraft()) { - $this->markTestSkipped('Provided content object is not a draft.'); - } - - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - return $contentService->publishVersion($draft->getVersionInfo()); - } - - /** - * @depends testPublishContent - */ - public function testPublishedFieldType($content) - { - foreach ($content->getFields() as $field) { - if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { - return $field; - } - } - - $this->fail('Custom field not found.'); - } - - /** - * @depends testPublishContent - */ - public function testPublishedName(Content $content) - { - $this->assertEquals( - $content->getFieldValue('name') . ' ' . $this->getFieldName(), - $content->contentInfo->name - ); - } - - /** - * @depends testCreateContent - */ - public function testLoadField() - { - $content = $this->testCreateContent(); - - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - return $contentService->loadContent($content->contentInfo->id); - } - - /** - * @depends testLoadField - */ - public function testLoadFieldType() - { - $content = $this->testCreateContent(); - - foreach ($content->getFields() as $field) { - if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { - return $field; - } - } - - $this->fail('Custom field not found.'); - } - - /** - * @depends testLoadFieldType - */ - public function testLoadExternalData() - { - $this->assertFieldDataLoadedCorrect($this->testLoadFieldType()); - } - - public function testCreateContentWithEmptyFieldValue() - { - /** @var \eZ\Publish\Core\FieldType\FieldType $fieldType */ - $fieldType = $this->getRepository()->getFieldTypeService()->getFieldType($this->getTypeName()); - - return $this->createContent($fieldType->getEmptyValue()); - } - - /** - * Test that publishing (and thus indexing) content with an empty field value does not fail. - * - * @depends testCreateContentWithEmptyFieldValue - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $contentDraft - */ - public function testPublishContentWithEmptyFieldValue(Content $contentDraft) - { - $this->getRepository(false)->getContentService()->publishVersion( - $contentDraft->versionInfo - ); - } - - /** - * @depends testCreateContentWithEmptyFieldValue - */ - public function testCreatedEmptyFieldValue($content) - { - foreach ($content->getFields() as $field) { - if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { - return $field; - } - } - - $this->fail('Custom field not found.'); - } - - /** - * @depends testCreateContentWithEmptyFieldValue - * @group xx - */ - public function testLoadEmptyFieldValue() - { - $content = $this->testCreateContentWithEmptyFieldValue(); - - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - return $contentService->loadContent($content->contentInfo->id); - } - - /** - * @depends testLoadEmptyFieldValue - */ - public function testLoadEmptyFieldValueType($content) - { - foreach ($content->getFields() as $field) { - if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { - return $field; - } - } - - $this->fail('Custom field not found.'); - } - - /** - * @depends testLoadEmptyFieldValueType - */ - public function testLoadEmptyFieldValueData($field) - { - /** @var \eZ\Publish\Core\FieldType\FieldType $fieldType */ - $fieldType = $this->getRepository()->getFieldTypeService()->getFieldType($this->getTypeName()); - - // @todo either test this not using acceptValue, or add to API (but is not meant for high level API, so..) - $refObject = new \ReflectionObject($fieldType); - $refProperty = $refObject->getProperty('internalFieldType'); - $refProperty->setAccessible(true); - $spiFieldType = $refProperty->getValue($fieldType); - - $this->assertEquals( - $fieldType->getEmptyValue(), - $spiFieldType->acceptValue($field->value) - ); - } - - /** - * @depends testLoadFieldType - */ - public function testUpdateField() - { - return $this->updateContent($this->getValidUpdateFieldData()); - } - - /** - * Updates the standard published content object with $fieldData. - * - * @param mixed $fieldData - * @param bool $setField If false the update struct will be empty (field value will not be set) - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function updateContent($fieldData, $setField = true) - { - $content = $this->testPublishContent(); - - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $draft = $contentService->createContentDraft($content->contentInfo); - - $updateStruct = $contentService->newContentUpdateStruct(); - if ($setField) { - $updateStruct->setField( - $this->customFieldIdentifier, - $fieldData - ); - } - - return $contentService->updateContent($draft->versionInfo, $updateStruct); - } - - /** - * @depends testUpdateField - */ - public function testUpdateTypeFieldStillAvailable($content) - { - foreach ($content->getFields() as $field) { - if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { - return $field; - } - } - - $this->fail('Custom field not found.'); - } - - /** - * @depends testUpdateTypeFieldStillAvailable - */ - public function testUpdatedDataCorrect(Field $field) - { - $this->assertUpdatedFieldDataLoadedCorrect($field); - } - - /** - * Tests creating a new Version keeps the existing value. - */ - public function testUpdateFieldNoNewContent() - { - return $this->updateContent(null, false); - } - - /** - * @depends testUpdateFieldNoNewContent - */ - public function testUpdateNoNewContentTypeFieldStillAvailable($content) - { - foreach ($content->getFields() as $field) { - if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { - return $field; - } - } - - $this->fail('Custom field not found.'); - } - - /** - * @depends testUpdateNoNewContentTypeFieldStillAvailable - */ - public function testUpdatedNoNewContentDataCorrect(Field $field) - { - $this->assertFieldDataLoadedCorrect($field); - } - - /** - * @depends testCreateContent - */ - public function testCopyField($content) - { - $content = $this->testCreateContent(); - - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $locationService = $repository->getLocationService(); - $parentLocationId = $this->generateId('location', 2); - $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); - - $copied = $contentService->copyContent($content->contentInfo, $locationCreate); - - $this->assertNotSame( - $content->contentInfo->id, - $copied->contentInfo->id - ); - - return $contentService->loadContent($copied->id); - } - - /** - * @depends testCopyField - */ - public function testCopiedFieldType($content) - { - foreach ($content->getFields() as $field) { - if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { - return $field; - } - } - - $this->fail('Custom field not found.'); - } - - /** - * @depends testCopiedFieldType - */ - public function testCopiedExternalData(Field $field) - { - $this->assertCopiedFieldDataLoadedCorrectly($field); - } - - /** - * @depends testCopyField - */ - public function testDeleteContent($content) - { - $this->expectException(NotFoundException::class); - - $content = $this->testPublishContent(); - - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $contentService->deleteContent($content->contentInfo); - - $contentService->loadContent($content->contentInfo->id); - } - - /** - * Tests failing content creation. - * - * @param mixed $failingValue - * - * @dataProvider provideInvalidCreationFieldData - */ - public function testCreateContentFails($failingValue, ?string $expectedException): void - { - $this->expectException($expectedException); - $this->createContent($failingValue); - } - - /** - * Tests failing content update. - * - * @param mixed $failingValue - * @param string $expectedException - * - * @dataProvider provideInvalidUpdateFieldData - */ - public function testUpdateContentFails($failingValue, $expectedException) - { - $this->expectException($expectedException); - $this->updateContent($failingValue); - } - - protected function removeFieldDefinition() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - $content = $this->testPublishContent(); - - $contentType = $contentTypeService->loadContentType($content->contentInfo->contentTypeId); - $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); - $fieldDefinition = $contentTypeDraft->getFieldDefinition('data'); - - $contentTypeService->removeFieldDefinition($contentTypeDraft, $fieldDefinition); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - return $contentService->loadContent($content->id); - } - - /** - * Tests removal of field definition from the ContentType of the Content. - */ - public function testRemoveFieldDefinition() - { - $content = $this->removeFieldDefinition(); - - $this->assertCount(1, $content->getFields()); - $this->assertNull($content->getFieldValue('data')); - } - - protected function addFieldDefinition() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - $content = $this->removeFieldDefinition(); - - $contentType = $contentTypeService->loadContentType($content->contentInfo->contentTypeId); - $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); - - $fieldDefinitionCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( - 'data', - $this->getTypeName() - ); - - $fieldDefinitionCreateStruct->names = $this->getOverride('names', $this->getValidFieldConfiguration(), [$contentType->mainLanguageCode => $this->getTypeName()]); - $fieldDefinitionCreateStruct->validatorConfiguration = $this->getValidValidatorConfiguration(); - $fieldDefinitionCreateStruct->fieldSettings = $this->getValidFieldSettings(); - $fieldDefinitionCreateStruct->defaultValue = null; - - $contentTypeService->addFieldDefinition($contentTypeDraft, $fieldDefinitionCreateStruct); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - return $contentService->loadContent($content->id); - } - - /** - * Tests addition of field definition from the ContentType of the Content. - */ - public function testAddFieldDefinition() - { - $content = $this->addFieldDefinition(); - - $this->assertCount(2, $content->getFields()); - - $this->assertTrue( - $this->getRepository()->getFieldTypeService()->getFieldType( - $this->getTypeName() - )->isEmptyValue( - $content->getFieldValue('data') - ) - ); - } - - /** - * @dataProvider provideToHashData - */ - public function testToHash($value, $expectedHash) - { - $repository = $this->getRepository(); - $fieldTypeService = $repository->getFieldTypeService(); - $fieldType = $fieldTypeService->getFieldType($this->getTypeName()); - - $this->assertEquals( - $expectedHash, - $fieldType->toHash($value) - ); - } - - /** - * @depends testCreateContent - * @dataProvider provideFromHashData - * @todo: Requires correct registered FieldTypeService, needs to be - * maintained! - */ - public function testFromHash($hash, $expectedValue) - { - $repository = $this->getRepository(); - $fieldTypeService = $repository->getFieldTypeService(); - $fieldType = $fieldTypeService->getFieldType($this->getTypeName()); - - $this->assertEquals( - $expectedValue, - $fieldType->fromHash($hash) - ); - } - - /** - * Test that exceeding default version archive limit has no effect on a published content. - */ - public function testExceededVersionArchiveLimitHasNoEffectOnContent() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $contentDraft = $this->createContent($this->getValidCreationFieldData()); - $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); - // update and publish content to exceed version archive limit - $contentUpdateStruct = $contentService->newContentUpdateStruct(); - $contentUpdateStruct->setField('data', $this->getValidUpdateFieldData()); - for ($i = 0; $i < static::VERSION_ARCHIVE_LIMIT + 1; ++$i) { - $contentDraft = $contentService->createContentDraft($publishedContent->contentInfo); - $contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct); - $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); - } - - $loadedContent = $contentService->loadContent( - $publishedContent->contentInfo->id, - ['eng-US'] - ); - $this->assertUpdatedFieldDataLoadedCorrect($loadedContent->getField('data')); - } - - /** - * Test that deleting new draft does not affect data of published version. - */ - public function testDeleteDraftOfPublishedContentDoesNotDeleteData() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $fieldType = $repository->getFieldTypeService()->getFieldType($this->getTypeName()); - - $contentDraft = $this->testCreateContent(); - $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); - - $contentDraft = $contentService->createContentDraft($publishedContent->contentInfo); - - $contentService->deleteVersion($contentDraft->versionInfo); - $loadedContent = $contentService->loadContent($publishedContent->contentInfo->id, ['eng-US']); - - self::assertFalse( - $fieldType->isEmptyValue($loadedContent->getField('data')->value) - ); - } - - /** - * Test creating new translation from existing content with empty field. - */ - public function testUpdateContentWithNewTranslationOnEmptyField() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $content = $this->testCreateContentWithEmptyFieldValue(); - $publishedContent = $contentService->publishVersion($content->versionInfo); - - $contentDraft = $contentService->createContentDraft($publishedContent->contentInfo); - $updateStruct = $contentService->newContentUpdateStruct(); - $updateStruct->setField( - 'data', - $publishedContent->getFieldValue('data', 'eng-US'), - 'eng-US' - ); - $updateStruct->initialLanguageCode = 'eng-GB'; - $updatedContentDraft = $contentService->updateContent($contentDraft->versionInfo, $updateStruct); - $contentService->publishVersion($updatedContentDraft->versionInfo); - } - - /** - * Get proper multilingual FT-specific Values. It Can be overridden by a Field Type test case. - * - * @param string[] $languageCodes List of languages to create data for - * - * @return array an array in the form of <code>[languageCode => data]</code> - */ - public function getValidMultilingualFieldData(array $languageCodes) - { - $data = []; - foreach ($languageCodes as $languageCode) { - $data[$languageCode] = $this->getValidCreationFieldData(); - } - - return $data; - } - - /** - * Test that removing Translation from all Versions works for data from a Field Type. - * - * @covers \eZ\Publish\API\Repository\ContentService::deleteTranslation - */ - public function testDeleteTranslation() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $languageCodes = ['eng-US', 'ger-DE']; - - $fieldName = $this->getFieldName(); - $names = []; - foreach ($languageCodes as $languageCode) { - $names[$languageCode] = "{$languageCode} {$fieldName}"; - } - - $fieldData = $this->getValidMultilingualFieldData($languageCodes); - - $content = $contentService->publishVersion( - $this->createMultilingualContent($names, $fieldData)->versionInfo - ); - - // create one more Version - $publishedContent = $contentService->publishVersion( - $contentService->createContentDraft($content->contentInfo)->versionInfo - ); - - // create Draft - $contentService->createContentDraft($content->contentInfo); - - // create copy of content in all Versions to use it for comparision later on - $contentByVersion = []; - foreach ($contentService->loadVersions($content->contentInfo) as $versionInfo) { - $contentByVersion[$versionInfo->versionNo] = $contentService->loadContent( - $content->id, - null, - $versionInfo->versionNo - ); - } - - // delete Translation from all available Versions - $contentService->deleteTranslation($publishedContent->contentInfo, 'ger-DE'); - - // check if are Versions have valid Translation - foreach ($contentService->loadVersions($publishedContent->contentInfo) as $versionInfo) { - // check if deleted Translation does not exist - self::assertEquals(['eng-US'], array_keys($versionInfo->getNames())); - self::assertEquals(['eng-US'], $versionInfo->languageCodes); - - // load Content of a Version to access other fields data - $versionContent = $contentService->loadContent( - $content->id, - null, - $versionInfo->versionNo - ); - // check if deleted Translation for Field Type data does not exist - self::assertEmpty($versionContent->getFieldsByLanguage('ger-DE')); - self::assertEmpty($versionContent->getField('data', 'ger-DE')); - - // check if the remaining Translation is still valid - $expectedContent = $contentByVersion[$versionContent->versionInfo->versionNo]; - self::assertNotEmpty($versionContent->getFieldsByLanguage('eng-US')); - self::assertEquals( - $expectedContent->getField('name', 'eng-US'), - $versionContent->getField('name', 'eng-US') - ); - self::assertEquals( - $expectedContent->getField('data', 'eng-US'), - $versionContent->getField('data', 'eng-US') - ); - } - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/BinaryFileIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/BinaryFileIntegrationTest.php deleted file mode 100644 index c3eead5c43..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/BinaryFileIntegrationTest.php +++ /dev/null @@ -1,485 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\FieldType\BinaryFile\Value as BinaryFileValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class BinaryFileIntegrationTest extends FileSearchBaseIntegrationTest -{ - /** - * Stores the loaded image path for copy test. - */ - protected static $loadedBinaryFilePath; - - /** - * IOService storage prefix for the tested Type's files. - * - * @var string - */ - protected static $storagePrefixConfigKey = 'binaryfile_storage_prefix'; - - protected function getStoragePrefix() - { - return $this->getConfigValue(self::$storagePrefixConfigKey); - } - - /** - * Sets up fixture data. - * - * @return array - */ - protected function getFixtureData() - { - return [ - 'create' => [ - 'id' => null, - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), - 'fileName' => 'Icy-Night-Flower-Binary.jpg', - 'fileSize' => filesize($path), - 'mimeType' => 'image/jpeg', - // Left out'downloadCount' by intention (will be set to 0) - ], - 'update' => [ - 'id' => null, - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.png'), - 'fileName' => 'Blue-Blue-Blue-Sindelfingen.png', - 'fileSize' => filesize($path), - 'downloadCount' => 23, - // Left out 'mimeType' by intention (will be auto-detected) - ], - ]; - } - - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezbinaryfile'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return []; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return []; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return [ - 'FileSizeValidator' => [ - 'maxFileSize' => [ - 'type' => 'int', - 'default' => false, - ], - ], - ]; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return [ - 'FileSizeValidator' => [ - 'maxFileSize' => 2 * 1024 * 1024, // 2 MB - ], - ]; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'StringLengthValidator' => [ - 'minStringLength' => new \stdClass(), - ], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - $fixtureData = $this->getFixtureData(); - - return new BinaryFileValue($fixtureData['create']); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'Icy-Night-Flower-Binary.jpg'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\BinaryFile\\Value', - $field->value - ); - - $fixtureData = $this->getFixtureData(); - $expectedData = $fixtureData['create']; - - // Will change during storage - unset($expectedData['id']); - $expectedData['inputUri'] = null; - - $this->assertNotEmpty($field->value->id); - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - - $this->assertTrue( - $this->uriExistsOnIO($field->value->uri), - "File {$field->value->uri} doesn't exist" - ); - - self::$loadedBinaryFilePath = $field->value->id; - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - [ - 'id' => '/foo/bar/sindelfingen.pdf', - ], - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentValue', - ], - [ - new BinaryFileValue( - [ - 'id' => '/foo/bar/sindelfingen.pdf', - ] - ), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentValue', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - $fixtureData = $this->getFixtureData(); - - return new BinaryFileValue($fixtureData['update']); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\BinaryFile\\Value', - $field->value - ); - - $fixtureData = $this->getFixtureData(); - $expectedData = $fixtureData['update']; - - // Will change during storage - unset($expectedData['id']); - $expectedData['inputUri'] = null; - - $this->assertNotEmpty($field->value->id); - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - - $this->assertTrue( - $this->uriExistsOnIO($field->value->uri), - "File {$field->value->uri} doesn't exist." - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertFieldDataLoadedCorrect($field); - - $this->assertEquals( - self::$loadedBinaryFilePath, - $field->value->id - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - $fixture = $this->getFixtureData(); - $expected = $fixture['create']; - $expected['downloadCount'] = 0; - $expected['uri'] = $expected['inputUri']; - $expected['path'] = $expected['inputUri']; - - $fieldValue = $this->getValidCreationFieldData(); - $fieldValue->uri = $expected['uri']; - - return [ - [ - $fieldValue, - $expected, - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - $fixture = $this->getFixtureData(); - $fixture['create']['downloadCount'] = 0; - $fixture['create']['uri'] = $fixture['create']['inputUri']; - - $fieldValue = $this->getValidCreationFieldData(); - $fieldValue->uri = $fixture['create']['uri']; - - return [ - [ - $fixture['create'], - $fieldValue, - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new BinaryFileValue()], - [new BinaryFileValue([])], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - ]; - } - - protected function getValidSearchValueOne() - { - return new BinaryFileValue( - [ - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), - 'fileName' => 'blue-blue-blue-sindelfingen.jpg', - 'fileSize' => filesize($path), - ] - ); - } - - /** - * BinaryFile field type is not searchable with Field criterion - * and sort clause in Legacy search engine. - */ - protected function checkSearchEngineSupport() - { - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\\Publish\\API\\Repository\\Tests\\SetupFactory\\Legacy') { - $this->markTestSkipped( - "'ezbinaryfile' field type is not searchable with Legacy Search Engine" - ); - } - } - - protected function getValidSearchValueTwo() - { - return new BinaryFileValue( - [ - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.png'), - 'fileName' => 'icy-night-flower-binary.png', - 'fileSize' => filesize($path), - ] - ); - } - - protected function getSearchTargetValueOne() - { - $value = $this->getValidSearchValueOne(); - // ensure case-insensitivity - return strtoupper($value->fileName); - } - - protected function getSearchTargetValueTwo() - { - $value = $this->getValidSearchValueTwo(); - // ensure case-insensitivity - return strtoupper($value->fileName); - } - - protected function getAdditionallyIndexedFieldData() - { - return [ - [ - 'file_size', - $this->getValidSearchValueOne()->fileSize, - $this->getValidSearchValueTwo()->fileSize, - ], - [ - 'mime_type', - // ensure case-insensitivity - 'IMAGE/JPEG', - 'IMAGE/PNG', - ], - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/CheckboxIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/CheckboxIntegrationTest.php deleted file mode 100644 index e0f7c320ed..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/CheckboxIntegrationTest.php +++ /dev/null @@ -1,452 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\FieldType\Checkbox\Value as CheckboxValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class CheckboxIntegrationTest extends SearchBaseIntegrationTest -{ - private const IS_ACTIVE_FIELD_DEF_IDENTIFIER = 'is_active'; - - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezboolean'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return []; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return []; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return []; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return []; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'unknown' => ['value' => 42], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new CheckboxValue(true); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return '1'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - self::assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Checkbox\\Value', - $field->value - ); - - $expectedData = [ - 'bool' => true, - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - new CheckboxValue(new \stdClass()), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return new CheckboxValue(false); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - self::assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Checkbox\\Value', - $field->value - ); - - $expectedData = [ - 'bool' => false, - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - self::assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Checkbox\\Value', - $field->value - ); - - $expectedData = [ - 'bool' => true, - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new CheckboxValue(true), - '1', - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - '1', - new CheckboxValue(true), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return []; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [$this->getValidCreationFieldData()], - [new CheckboxValue(true)], - [new CheckboxValue()], - [new CheckboxValue(null)], - [new CheckboxValue(false)], - ]; - } - - protected function getValidSearchValueOne() - { - return false; - } - - protected function getValidSearchValueTwo() - { - return true; - } - - protected function getSearchTargetValueOne() - { - // Handling Legacy Search Engine, which stores Checkbox value as integer - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - return (int)$this->getValidSearchValueOne(); - } - - return parent::getSearchTargetValueOne(); - } - - protected function getSearchTargetValueTwo() - { - // Handling Legacy Search Engine, which stores Checkbox value as integer - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - return (int)$this->getValidSearchValueTwo(); - } - - return parent::getSearchTargetValueTwo(); - } - - /** - * Data corresponds to Content items created by {@see createCheckboxContentItems}. - */ - public function getDataForTestFindContentFieldCriterion(): iterable - { - // there are 2 Content items created, one with is_active = true, the other one with is_active = false - yield 'active' => [true]; - yield 'not active' => [false]; - } - - /** - * @dataProvider getDataForTestFindContentFieldCriterion - * @covers \eZ\Publish\API\Repository\SearchService::findContent - */ - public function testFindContentFieldCriterion(bool $isActive): void - { - $repository = $this->getRepository(); - $this->createCheckboxContentItems($repository); - - $criterion = new Criterion\Field( - self::IS_ACTIVE_FIELD_DEF_IDENTIFIER, - Criterion\Operator::EQ, - $isActive - ); - $query = new Query(['query' => $criterion]); - - $searchService = $repository->getSearchService(); - $searchResult = $searchService->findContent($query); - - self::assertEquals(1, $searchResult->totalCount); - $contentItem = $searchResult->searchHits[0]->valueObject; - /** @var \eZ\Publish\API\Repository\Values\Content\Content $contentItem */ - $value = $contentItem->getField('is_active')->value; - /** @var \eZ\Publish\Core\FieldType\Checkbox\Value $value */ - self::assertSame($isActive, $value->bool); - } - - public function testAddFieldDefinition(): void - { - $fieldTypeService = $this->getRepository()->getFieldTypeService(); - $content = $this->addFieldDefinition(); - - self::assertCount(2, $content->getFields()); - self::assertFalse( - $fieldTypeService - ->getFieldType($this->getTypeName()) - ->isEmptyValue($content->getFieldValue('data')) - ); - } - - protected function createCheckboxContentItems(Repository $repository): void - { - $contentType = $this->createContentTypeWithCheckboxField($repository); - - $contentService = $repository->getContentService(); - - $toCreate = [ - 'content-checkbox-active' => true, - 'content-checkbox-not-active' => false, - ]; - foreach ($toCreate as $remoteId => $isActive) { - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->remoteId = $remoteId; - $createStruct->alwaysAvailable = false; - $createStruct->setField(self::IS_ACTIVE_FIELD_DEF_IDENTIFIER, $isActive); - - $contentService->publishVersion( - $contentService->createContent($createStruct)->getVersionInfo() - ); - } - - $this->refreshSearch($repository); - } - - private function createContentTypeWithCheckboxField(Repository $repository): ContentType - { - $contentTypeService = $repository->getContentTypeService(); - - $createStruct = $contentTypeService->newContentTypeCreateStruct('content-checkbox'); - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->names = ['eng-GB' => 'Checkboxes']; - - $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct( - self::IS_ACTIVE_FIELD_DEF_IDENTIFIER, - 'ezboolean' - ); - $fieldCreate->names = ['eng-GB' => 'Active']; - $fieldCreate->position = 1; - $fieldCreate->isTranslatable = false; - $fieldCreate->isSearchable = true; - - $createStruct->addFieldDefinition($fieldCreate); - - $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); - $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - return $contentTypeService->loadContentType($contentTypeDraft->id); - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/CountryIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/CountryIntegrationTest.php deleted file mode 100644 index 8df1a689cf..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/CountryIntegrationTest.php +++ /dev/null @@ -1,519 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\Country\Value as CountryValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class CountryIntegrationTest extends SearchMultivaluedBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezcountry'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return [ - 'isMultiple' => [ - 'type' => 'boolean', - 'default' => false, - ], - ]; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return [ - 'isMultiple' => false, - ]; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return []; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return []; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'unknown' => ['value' => 42], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - ] - ); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'Belgium'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Country\\Value', - $field->value - ); - - $expectedData = [ - 'countries' => [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - ], - ]; - - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - 'Sindelfingen', - 'eZ\\Publish\\API\\Repository\\Exceptions\\InvalidArgumentException', - ], - [ - ['NON_VALID_ALPHA2_CODE'], - 'eZ\\Publish\\API\\Repository\\Exceptions\\InvalidArgumentException', - ], - [ - ['BE', 'FR'], - 'eZ\\Publish\\Core\\Base\\Exceptions\\ContentFieldValidationException', - ], - [ - new CountryValue( - [ - 'NON_VALID_ALPHA2_CODE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - ] - ), - 'eZ\\Publish\\Core\\Base\\Exceptions\\ContentFieldValidationException', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return new CountryValue( - [ - 'FR' => [ - 'Name' => 'France', - 'Alpha2' => 'FR', - 'Alpha3' => 'FRA', - 'IDC' => 33, - ], - ] - ); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Country\\Value', - $field->value - ); - - $expectedData = [ - 'countries' => [ - 'FR' => [ - 'Name' => 'France', - 'Alpha2' => 'FR', - 'Alpha3' => 'FRA', - 'IDC' => 33, - ], - ], - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Country\\Value', - $field->value - ); - - $expectedData = [ - 'countries' => [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - ], - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - 'FR' => [ - 'Name' => 'France', - 'Alpha2' => 'FR', - 'Alpha3' => 'FRA', - 'IDC' => 33, - ], - ] - ), - ['BE', 'FR'], - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - ['BE', 'FR'], - new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - 'FR' => [ - 'Name' => 'France', - 'Alpha2' => 'FR', - 'Alpha3' => 'FRA', - 'IDC' => 33, - ], - ] - ), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new CountryValue()], - [new CountryValue([])], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - ]; - } - - protected function getValidSearchValueOne() - { - return ['Andorra']; - } - - protected function getValidSearchValueTwo() - { - return ['Trinidad and Tobago']; - } - - protected function getSearchTargetValueOne() - { - return 'Andorra'; - } - - protected function getSearchTargetValueTwo() - { - return 'Trinidad and Tobago'; - } - - protected function getAdditionallyIndexedFieldData() - { - return [ - [ - 'idc', - '376', - '1868', - ], - [ - 'alpha2', - 'AD', - 'TT', - ], - [ - 'alpha3', - 'AND', - 'TTO', - ], - [ - 'name', - 'Andorra', - 'Trinidad and Tobago', - ], - [ - 'sort_value', - 'andorra', - 'trinidad and tobago', - ], - ]; - } - - protected function getValidMultivaluedSearchValuesOne() - { - return ['Andorra', 'Bolivia']; - } - - protected function getValidMultivaluedSearchValuesTwo() - { - return ['Syrian Arab Republic', 'Trinidad and Tobago']; - } - - protected function getAdditionallyIndexedMultivaluedFieldData() - { - return [ - [ - 'idc', - [376, 591], - [963, 1868], - ], - [ - 'alpha2', - ['AD', 'BO'], - ['SY', 'TT'], - ], - [ - 'alpha3', - ['AND', 'BOL'], - ['SYR', 'TTO'], - ], - [ - 'name', - ['Andorra', 'Bolivia'], - ['Syrian Arab Republic', 'Trinidad and Tobago'], - ], - ]; - } - - protected function getFullTextIndexedFieldData() - { - return [ - ['Andorra', 'Tobago'], - ]; - } - - protected function createTestContentType() - { - $contentType = $this->createContentType( - [ - 'isMultiple' => true, - ], - $this->getValidValidatorConfiguration() - ); - - return $contentType; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/DateAndTimeIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/DateAndTimeIntegrationTest.php deleted file mode 100644 index b7ecada41d..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/DateAndTimeIntegrationTest.php +++ /dev/null @@ -1,400 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use DateTime; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\DateAndTime\Value as DateAndTimeValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class DateAndTimeIntegrationTest extends SearchBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezdatetime'; - } - - /** - * {@inheritdoc} - */ - protected function supportsLikeWildcard($value) - { - parent::supportsLikeWildcard($value); - - return false; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return [ - 'useSeconds' => [ - 'type' => 'bool', - 'default' => false, - ], - 'defaultType' => [ - 'type' => 'choice', - 'default' => 0, - ], - 'dateInterval' => [ - 'type' => 'dateInterval', - 'default' => null, - ], - ]; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return [ - 'useSeconds' => false, - 'defaultType' => 0, - 'dateInterval' => null, - ]; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return []; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return []; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'unknown' => ['value' => 42], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - // We may only create times from timestamps here, since storing will - // loose information about the timezone. - return DateAndTimeValue::fromTimestamp(123456); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'Fri 1970-02-01 10:17:36'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\DateAndTime\\Value', - $field->value - ); - - $expectedData = [ - 'value' => new \DateTime('@123456'), - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - 'Some unknown date format', 'eZ\\Publish\\API\\Repository\\Exceptions\\InvalidArgumentException', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return DateAndTimeValue::fromTimestamp(12345678); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\DateAndTime\\Value', - $field->value - ); - - $expectedData = [ - 'value' => new \DateTime('@12345678'), - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Tests failing content update. - * - * @param mixed $failingValue - * @param string $expectedException - * - * @dataProvider provideInvalidUpdateFieldData - */ - public function testUpdateContentFails($failingValue, $expectedException) - { - return [ - [ - 'Some unknown date format', 'eZ\\Publish\\API\\Repository\\Exceptions\\InvalidArgumentException', - ], - ]; - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\DateAndTime\\Value', - $field->value - ); - - $expectedData = [ - 'value' => new \DateTime('@123456'), - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - DateAndTimeValue::fromTimestamp(123456), - [ - 'timestamp' => 123456, - 'rfc850' => 'Friday, 02-Jan-70 10:17:36 GMT+0000', - ], - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - [ - 'timestamp' => 123456, - 'rfc850' => 'Friday, 02-Jan-70 10:17:36 GMT+0000', - ], - DateAndTimeValue::fromTimestamp(123456), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new DateAndTimeValue()], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - ]; - } - - protected function getValidSearchValueOne() - { - return '2012-04-15T15:43:56Z'; - } - - protected function getValidSearchValueTwo() - { - return '2015-04-15T15:43:56Z'; - } - - protected function getSearchTargetValueOne() - { - // Handling Legacy Search Engine, which stores DateAndTime value as integer timestamp - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - $dateTime = new DateTime($this->getValidSearchValueOne()); - - return $dateTime->getTimestamp(); - } - - return parent::getSearchTargetValueOne(); - } - - protected function getSearchTargetValueTwo() - { - // Handling Legacy Search Engine, which stores DateAndTime value as integer timestamp - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - $dateTime = new DateTime($this->getValidSearchValueTwo()); - - return $dateTime->getTimestamp(); - } - - return parent::getSearchTargetValueTwo(); - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/DateIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/DateIntegrationTest.php deleted file mode 100644 index 60f79458b8..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/DateIntegrationTest.php +++ /dev/null @@ -1,388 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use DateTime; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\Date\Type; -use eZ\Publish\Core\FieldType\Date\Value as DateValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class DateIntegrationTest extends SearchBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezdate'; - } - - /** - * {@inheritdoc} - */ - protected function supportsLikeWildcard($value) - { - parent::supportsLikeWildcard($value); - - return false; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return [ - 'defaultType' => [ - 'type' => 'choice', - 'default' => Type::DEFAULT_EMPTY, - ], - ]; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return [ - 'defaultType' => Type::DEFAULT_EMPTY, - ]; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return []; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return []; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'unknown' => ['value' => 42], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return DateValue::fromTimestamp(86400); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'Friday 02 January 1970'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Date\\Value', - $field->value - ); - - $expectedData = [ - 'date' => new DateTime('@86400'), - ]; - - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - 'Some unknown date format', - 'eZ\\Publish\\API\\Repository\\Exceptions\\InvalidArgumentException', - ], - ]; - } - - /** - * Get valid field data for updating content. - * - * @return mixed - */ - public function getValidUpdateFieldData() - { - return DateValue::fromTimestamp(86400); - } - - /** - * Asserts the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidUpdateFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Date\\Value', - $field->value - ); - - $expectedData = [ - 'date' => new DateTime('@86400'), - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertFieldDataLoadedCorrect($field); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - $timestamp = 186401; - $dateTime = new DateTime("@{$timestamp}"); - - return [ - [ - DateValue::fromTimestamp($timestamp), - [ - 'timestamp' => $dateTime->setTime(0, 0, 0)->getTimestamp(), - 'rfc850' => $dateTime->format(DateTime::RFC850), - ], - ], - ]; - } - - /** - * Get hashes and their respective converted values. - * - * This is a PHPUnit data provider - * - * The returned records must have the the input hash assigned to the - * first index and the expected value result to the second. For example: - * - * <code> - * array( - * array( - * array( 'myValue' => true ), - * new MyValue( true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideFromHashData() - { - $timestamp = 123456; - - $dateTime = new DateTime("@{$timestamp}"); - $dateTime->setTime(0, 0, 0); - - return [ - [ - [ - 'timestamp' => $timestamp, - 'rfc850' => ($rfc850 = $dateTime->format(DateTime::RFC850)), - ], - DateValue::fromString($rfc850), - ], - [ - [ - 'timestamp' => $dateTime->getTimestamp(), - 'rfc850' => null, - ], - DateValue::fromTimestamp($timestamp), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new DateValue()], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - ]; - } - - protected function getValidSearchValueOne() - { - return 86400; - } - - protected function getValidSearchValueTwo() - { - return 172800; - } - - protected function getSearchTargetValueOne() - { - // Handling Legacy Search Engine, which stores Date value as timestamp - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - return $this->getValidSearchValueOne(); - } - - return '1970-01-02T00:00:00Z'; - } - - protected function getSearchTargetValueTwo() - { - // Handling Legacy Search Engine, which stores Date value as timestamp - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - return $this->getValidSearchValueTwo(); - } - - return '1970-01-03T00:00:00Z'; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/EmailAddressIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/EmailAddressIntegrationTest.php deleted file mode 100644 index d1b6c690a8..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/EmailAddressIntegrationTest.php +++ /dev/null @@ -1,371 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\EmailAddress\Value as EmailAddressValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class EmailAddressIntegrationTest extends SearchBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezemail'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return []; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return []; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return [ - 'EmailAddressValidator' => [], - ]; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return [ - 'EmailAddressValidator' => [], - ]; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'StringLengthValidator' => [ - 'minStringLength' => new \stdClass(), - ], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new EmailAddressValue('spam@ez.no'); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'spam@ez.no'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\EmailAddress\\Value', - $field->value - ); - - $expectedData = [ - 'email' => 'spam@ez.no', - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - new \stdClass(), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - [ - 42, - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - [ - new EmailAddressValue(str_repeat('.', 64)), - 'eZ\\Publish\\Core\\Base\\Exceptions\\ContentFieldValidationException', - ], - [ - new EmailAddressValue('spam@'), - 'eZ\\Publish\\Core\\Base\\Exceptions\\ContentFieldValidationException', - ], - [ - new EmailAddressValue('@ez.no'), - 'eZ\\Publish\\Core\\Base\\Exceptions\\ContentFieldValidationException', - ], - [ - new EmailAddressValue('spam@ez-no'), - 'eZ\\Publish\\Core\\Base\\Exceptions\\ContentFieldValidationException', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return new EmailAddressValue('spam_name@ez-some-thing.no'); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\EmailAddress\\Value', - $field->value - ); - - $expectedData = [ - 'email' => 'spam_name@ez-some-thing.no', - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\EmailAddress\\Value', - $field->value - ); - - $expectedData = [ - 'email' => 'spam@ez.no', - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new EmailAddressValue('spam@example.no'), - 'spam@example.no', - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - 'spam@example.no', - new EmailAddressValue('spam@example.no'), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new EmailAddressValue()], - [new EmailAddressValue(null)], - [new EmailAddressValue('')], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - ]; - } - - protected function getValidSearchValueOne() - { - return 'holmes4@ez.no'; - } - - protected function getSearchTargetValueOne() - { - // ensure case-insensitivity - return strtoupper($this->getValidSearchValueOne()); - } - - protected function getValidSearchValueTwo() - { - return 'wyoming.knott@o2.ru'; - } - - protected function getSearchTargetValueTwo() - { - // ensure case-insensitivity - return strtoupper($this->getValidSearchValueTwo()); - } - - protected function getFullTextIndexedFieldData() - { - return [ - ['holmes4@ez.no', 'wyoming.knott@o2.ru'], - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/FloatIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/FloatIntegrationTest.php deleted file mode 100644 index 90f1e9126b..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/FloatIntegrationTest.php +++ /dev/null @@ -1,358 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\Float\Type; -use eZ\Publish\Core\FieldType\Float\Value as FloatValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class FloatIntegrationTest extends SearchBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezfloat'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return []; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return []; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return [ - 'FloatValueValidator' => [ - 'minFloatValue' => [ - 'type' => 'float', - 'default' => false, - ], - 'maxFloatValue' => [ - 'type' => 'float', - 'default' => false, - ], - ], - ]; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return [ - 'FloatValueValidator' => [ - 'minFloatValue' => 23., - 'maxFloatValue' => 43., - ], - ]; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'FloatValueValidator' => [ - 'minStringLength' => new \stdClass(), - ], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new FloatValue(23.5); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return '23.5'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Float\\Value', - $field->value - ); - - $expectedData = [ - 'value' => 23.5, - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - new \stdClass(), - 'eZ\\Publish\\API\\Repository\\Exceptions\\InvalidArgumentException', - ], - [ - new FloatValue(5.5), - 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentFieldValidationException', - ], - [ - new FloatValue(127.5), - 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentFieldValidationException', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return new FloatValue(42.5); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Float\\Value', - $field->value - ); - - $expectedData = [ - 'value' => 42.5, - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Float\\Value', - $field->value - ); - - $expectedData = [ - 'value' => 23.5, - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new FloatValue(23.5), - 23.5, - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - 42.5, - new FloatValue(42.5), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new FloatValue()], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - [new FloatValue(0)], - [new FloatValue(0.0)], - ]; - } - - protected function getValidSearchValueOne() - { - return 25.519; - } - - protected function getValidSearchValueTwo() - { - return 25.59; - } - - public function checkFullTextSupport(): bool - { - return false; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/ISBNIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/ISBNIntegrationTest.php deleted file mode 100644 index af0e51db86..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/ISBNIntegrationTest.php +++ /dev/null @@ -1,368 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\ISBN\Value as ISBNValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class ISBNIntegrationTest extends SearchBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezisbn'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return [ - 'isISBN13' => [ - 'type' => 'boolean', - 'default' => true, - ], - ]; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return [ - 'isISBN13' => true, - ]; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return []; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return []; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'unknown' => ['value' => 42], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new ISBNValue('9789722514095'); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return '9789722514095'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\ISBN\\Value', - $field->value - ); - - $expectedData = '9789722514095'; - - $this->assertEquals( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - '9789722', - 'eZ\\Publish\\Core\\Base\\Exceptions\\ContentFieldValidationException', - ], - [ - 'NON_VALID_ISBN_CODE', - 'eZ\\Publish\\Core\\Base\\Exceptions\\ContentFieldValidationException', - ], - [ - new \stdClass(), - 'eZ\\Publish\\API\\Repository\\Exceptions\\InvalidArgumentException', - ], - [ - new ISBNValue('97897225'), - 'eZ\\Publish\\Core\\Base\\Exceptions\\ContentFieldValidationException', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return new ISBNValue('978-972-25-1409-5'); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\ISBN\\Value', - $field->value - ); - - $expectedData = '978-972-25-1409-5'; - $this->assertEquals( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\ISBN\\Value', - $field->value - ); - - $expectedData = '9789722514095'; - - $this->assertEquals( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new ISBNValue('9789722514095'), - '9789722514095', - ], - [ - new ISBNValue('978-972-25-1409-5'), - '978-972-25-1409-5', - ], - [ - new ISBNValue('0-9752298-0-X'), - '0-9752298-0-X', - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - '9789722514095', - new ISBNValue('9789722514095'), - ], - [ - '978-972-25-1409-5', - new ISBNValue('978-972-25-1409-5'), - ], - [ - '0-9752298-0-X', - new ISBNValue('0-9752298-0-X'), - ], - [ - '097522980X', - new ISBNValue('097522980X'), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new ISBNValue()], - [new ISBNValue(null)], - [new ISBNValue('')], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - ]; - } - - protected function getValidSearchValueOne() - { - return '9780099067504'; - } - - protected function getValidSearchValueTwo() - { - return '9780380448340'; - } - - protected function getFullTextIndexedFieldData() - { - return [ - ['9780099067504', '9780380448340'], - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/ImageIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/ImageIntegrationTest.php deleted file mode 100644 index eb5dadb47e..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/ImageIntegrationTest.php +++ /dev/null @@ -1,929 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use Doctrine\DBAL\ParameterType; -use DOMDocument; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\Image\Value as ImageValue; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class ImageIntegrationTest extends FileSearchBaseIntegrationTest -{ - /** - * Stores the loaded image path for copy test. - */ - protected static $loadedImagePath; - - /** - * IOService storage prefix for the tested Type's files. - * - * @var string - */ - protected static $storagePrefixConfigKey = 'image_storage_prefix'; - - protected function getStoragePrefix() - { - return $this->getConfigValue(self::$storagePrefixConfigKey); - } - - /** - * Sets up fixture data. - * - * @return array - */ - protected function getFixtureData() - { - return [ - 'create' => [ - 'fileName' => 'Icy-Night-Flower.jpg', - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), - 'alternativeText' => 'My icy flower at night', - 'fileSize' => filesize($path), - ], - 'update' => [ - 'fileName' => 'Blue-Blue-Blue.png', - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.png'), - 'alternativeText' => 'Such a blue …', - 'fileSize' => filesize($path), - ], - ]; - } - - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezimage'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return []; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return []; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return [ - 'FileSizeValidator' => [ - 'maxFileSize' => [ - 'type' => 'int', - 'default' => false, - ], - ], - 'AlternativeTextValidator' => [ - 'required' => [ - 'type' => 'bool', - 'default' => false, - ], - ], - ]; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return [ - 'FileSizeValidator' => [ - 'maxFileSize' => 2 * 1024 * 1024, // 2 MB - ], - 'AlternativeTextValidator' => [ - 'required' => true, - ], - ]; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'StringLengthValidator' => [ - 'minStringLength' => new \stdClass(), - ], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - $fixtureData = $this->getFixtureData(); - - return new ImageValue($fixtureData['create']); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'My icy flower at night'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - */ - public function assertFieldDataLoadedCorrect(Field $field): void - { - self::assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Image\\Value', - $field->value - ); - - $fixtureData = $this->getFixtureData(); - $expectedData = $fixtureData['create']; - - // Will be nullified by external storage - $expectedData['inputUri'] = null; - - // Will be changed by external storage as fileName will be decorated with a hash - $expectedData['fileName'] = $field->value->fileName; - - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - - self::assertTrue( - $this->uriExistsOnIO($field->value->uri), - "Asserting that {$field->value->uri} exists." - ); - - self::$loadedImagePath = $field->value->id; - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - // will fail because the provided file doesn't exist, and fileSize/fileName won't be set - [ - new ImageValue( - [ - 'inputUri' => __DIR__ . '/_fixtures/nofile.png', - ] - ), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - $fixtureData = $this->getFixtureData(); - - return new ImageValue($fixtureData['update']); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - self::assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Image\\Value', - $field->value - ); - - $fixtureData = $this->getFixtureData(); - $expectedData = $fixtureData['update']; - - // Will change during storage - $expectedData['inputUri'] = null; - - // Will change during storage as fileName will be decorated with a hash - $expectedData['fileName'] = $field->value->fileName; - - $expectedData['uri'] = $field->value->uri; - - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - - self::assertTrue( - $this->uriExistsOnIO($field->value->uri), - "Asserting that file {$field->value->uri} exists" - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertFieldDataLoadedCorrect($field); - - $this->assertEquals( - self::$loadedImagePath, - $field->value->id - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new ImageValue( - [ - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), - 'fileName' => 'Icy-Night-Flower.jpg', - 'alternativeText' => 'My icy flower at night', - ] - ), - [ - 'inputUri' => $path, - 'path' => $path, - 'fileName' => 'Icy-Night-Flower.jpg', - 'alternativeText' => 'My icy flower at night', - 'fileSize' => null, - 'id' => null, - 'imageId' => null, - 'uri' => null, - 'width' => null, - 'height' => null, - 'additionalData' => [], - ], - ], - [ - new ImageValue( - [ - 'id' => $path = 'var/test/storage/images/file.png', - 'fileName' => 'Icy-Night-Flower.jpg', - 'alternativeText' => 'My icy flower at night', - 'fileSize' => 23, - 'imageId' => '1-2', - 'uri' => "/$path", - 'width' => 123, - 'height' => 456, - ] - ), - [ - 'id' => $path, - 'path' => $path, - 'fileName' => 'Icy-Night-Flower.jpg', - 'alternativeText' => 'My icy flower at night', - 'fileSize' => 23, - 'inputUri' => null, - 'imageId' => '1-2', - 'uri' => "/$path", - 'width' => 123, - 'height' => 456, - 'additionalData' => [], - ], - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - $fixture = $this->getFixtureData(); - - return [ - [ - $fixture['create'], - $this->getValidCreationFieldData(), - ], - ]; - } - - public function testInherentCopyForNewLanguage() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $type = $this->createContentType( - $this->getValidFieldSettings(), - $this->getValidValidatorConfiguration(), - [], - // Causes a copy of the image value for each language in legacy - // storage - ['isTranslatable' => false] - ); - - $draft = $this->createContent($this->getValidCreationFieldData(), $type); - - $updateStruct = $contentService->newContentUpdateStruct(); - $updateStruct->initialLanguageCode = 'ger-DE'; - $updateStruct->setField('name', 'Sindelfingen'); - - // Automatically creates a copy of the image field in the back ground - $updatedDraft = $contentService->updateContent($draft->versionInfo, $updateStruct); - - $paths = []; - foreach ($updatedDraft->getFields() as $field) { - if ($field->fieldDefIdentifier === 'data') { - $paths[$field->languageCode] = $field->value->uri; - } - } - - $this->assertTrue( - isset($paths['eng-US']) && isset($paths['ger-DE']), - 'Failed asserting that file path for all languages were found in draft' - ); - - $this->assertEquals( - $paths['eng-US'], - $paths['ger-DE'] - ); - - $contentService->deleteContent($updatedDraft->contentInfo); - - foreach ($paths as $uri) { - self::assertFalse( - $this->uriExistsOnIO($uri), - "$uri has not been removed" - ); - } - } - - public function providerForTestIsEmptyValue() - { - return [ - [new ImageValue()], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - ]; - } - - /** - * Covers EZP-23080. - */ - public function testUpdatingImageMetadataOnlyWorks() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $type = $this->createContentType( - $this->getValidFieldSettings(), - $this->getValidValidatorConfiguration(), - [] - ); - - $draft = $this->createContent($this->getValidCreationFieldData(), $type); - - /** @var \eZ\Publish\Core\FieldType\Image\Value $imageFieldValue */ - $imageFieldValue = $draft->getFieldValue('data'); - $initialValueImageUri = $imageFieldValue->uri; - - // update alternative text - $imageFieldValue->alternativeText = __METHOD__; - $updateStruct = $contentService->newContentUpdateStruct(); - $updateStruct->setField('data', $imageFieldValue); - $updatedDraft = $contentService->updateContent($draft->versionInfo, $updateStruct); - - /** @var \eZ\Publish\Core\FieldType\Image\Value $updatedImageValue */ - $updatedImageValue = $updatedDraft->getFieldValue('data'); - - self::assertEquals($initialValueImageUri, $updatedImageValue->uri); - self::assertEquals(__METHOD__, $updatedImageValue->alternativeText); - } - - /** - * @see https://jira.ez.no/browse/EZP-23152 - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testThatRemovingDraftDoesntRemovePublishedImages(): void - { - $repository = $this->getRepository(); - - // Load services - $contentService = $repository->getContentService(); - - // create content and publish image - $content = $this->publishNewImage( - 'EZP23152_1', - $this->getValidCreationFieldData(), - [2] - ); - $originalFileUri = $this->getImageURI($content); - - self::assertTrue( - $this->uriExistsOnIO($originalFileUri), - "Asserting image file $originalFileUri exists." - ); - - // Create a new draft and update it - $updatedDraft = $contentService->createContentDraft($content->contentInfo); - $contentUpdateStruct = $contentService->newContentUpdateStruct(); - $contentUpdateStruct->initialLanguageCode = 'eng-GB'; - $contentUpdateStruct->setField('name', 'EZP23152_2'); - $updatedDraft = $contentService->updateContent($updatedDraft->versionInfo, $contentUpdateStruct); - - // remove the newly published content version, verify that the original file exists - $contentService->deleteVersion($updatedDraft->versionInfo); - self::assertTrue( - $this->uriExistsOnIO($originalFileUri), - "Asserting original image file $originalFileUri exists." - ); - - // delete content - $contentService->deleteContent($content->contentInfo); - self::assertFalse( - $this->uriExistsOnIO($originalFileUri), - "Asserting image file $originalFileUri has been removed." - ); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testUpdateImageAltTextOnly(): void - { - $content = $this->publishNewImage( - __METHOD__, - new ImageValue( - [ - 'inputUri' => __DIR__ . '/_fixtures/image.jpg', - 'fileName' => 'image.jpg', - 'fileSize' => filesize(__DIR__ . '/_fixtures/image.jpg'), - 'alternativeText' => 'Initial alternative text', - ] - ), - [2] - ); - - /** @var \eZ\Publish\Core\FieldType\Image\Value $imageField */ - $imageField = $content->getFieldValue('image'); - $updatedAlternativeText = 'Updated alternative text'; - $imageField->alternativeText = $updatedAlternativeText; - - $content = $this->updateImage($content, $imageField); - - self::assertSame( - $updatedAlternativeText, - $content->getFieldValue('image')->alternativeText - ); - } - - protected function getValidSearchValueOne() - { - return new ImageValue( - [ - 'fileName' => '1234eeee1234-cafe-terrace-at-night.jpg', - 'inputUri' => ($path = __DIR__ . '/_fixtures/1234eeee1234-image.jpg'), - 'alternativeText' => 'café terrace at night, also known as the cafe terrace on the place du forum', - 'fileSize' => filesize($path), - ] - ); - } - - protected function getValidSearchValueTwo() - { - return new ImageValue( - [ - 'fileName' => '2222eeee1111-thatched-cottages-at-cordeville.png', - 'inputUri' => ($path = __DIR__ . '/_fixtures/2222eeee1111-image.png'), - 'alternativeText' => 'chaumes de cordeville à auvers-sur-oise', - 'fileSize' => filesize($path), - ] - ); - } - - protected function getSearchTargetValueOne() - { - $value = $this->getValidSearchValueOne(); - - // ensure case-insensitivity - return strtoupper($value->fileName); - } - - protected function getSearchTargetValueTwo() - { - $value = $this->getValidSearchValueTwo(); - // ensure case-insensitivity - return strtoupper($value->fileName); - } - - protected function getAdditionallyIndexedFieldData() - { - return [ - [ - 'alternative_text', - $this->getValidSearchValueOne()->alternativeText, - $this->getValidSearchValueTwo()->alternativeText, - ], - [ - 'file_size', - $this->getValidSearchValueOne()->fileSize, - $this->getValidSearchValueTwo()->fileSize, - ], - [ - 'mime_type', - // ensure case-insensitivity - 'IMAGE/JPEG', - 'IMAGE/PNG', - ], - ]; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testRemovingContentRemovesImages(): void - { - $repository = $this->getRepository(); - - // Load services - $contentService = $repository->getContentService(); - - $content = $this->publishNewImage('My Image', $this->getValidCreationFieldData()); - $originalFileUri = $this->getImageURI($content); - - // sanity check - self::assertTrue( - $this->uriExistsOnIO($originalFileUri), - "Asserting image file $originalFileUri exists" - ); - - $content = $this->updateImage($content, $this->getValidUpdateFieldData()); - $updatedFileUri = $this->getImageURI($content); - - // sanity check - self::assertNotEquals($originalFileUri, $updatedFileUri); - - $contentService->deleteContent($content->contentInfo); - - self::assertFalse( - $this->uriExistsOnIO($updatedFileUri), - "Asserting updated image file $updatedFileUri has been removed" - ); - - self::assertFalse( - $this->uriExistsOnIO($originalFileUri), - "Asserting original image file $originalFileUri has been removed" - ); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testRemovingDraftRemovesOldImage(): void - { - $repository = $this->getRepository(); - - // Load services - $contentService = $repository->getContentService(); - - $contentVersion1 = $this->publishNewImage('My Image', $this->getValidCreationFieldData()); - $originalFileUri = $this->getImageURI($contentVersion1); - - // sanity check - self::assertTrue( - $this->uriExistsOnIO($originalFileUri), - "Asserting image file $originalFileUri exists" - ); - - $contentVersion2 = $this->updateImage($contentVersion1, $this->getValidUpdateFieldData()); - $updatedFileUri = $this->getImageURI($contentVersion2); - - // delete 1st version with original image - $contentService->deleteVersion( - // reload 1st version (its state changed) to delete - $contentService->loadVersionInfo( - $contentVersion1->contentInfo, - $contentVersion1->getVersionInfo()->versionNo - ) - ); - - // updated image should be available, but original image should be gone now - self::assertTrue( - $this->uriExistsOnIO($updatedFileUri), - "Asserting image file {$updatedFileUri} exists" - ); - - self::assertFalse( - $this->uriExistsOnIO($originalFileUri), - "Asserting image file {$originalFileUri} has been removed" - ); - } - - public function testDeleteImageWithCorruptedName(): void - { - $content = $this->publishNewImage( - __METHOD__, - new ImageValue( - [ - 'inputUri' => __DIR__ . '/_fixtures/image.jpg', - 'fileName' => 'image.jpg', - 'fileSize' => filesize(__DIR__ . '/_fixtures/image.jpg'), - 'alternativeText' => 'Alternative', - ] - ), - [2] - ); - - $imageFieldDefinition = $content->getContentType()->getFieldDefinition('image'); - - $record = $this->fetchXML( - $content->id, - $content->getVersionInfo()->versionNo, - $imageFieldDefinition->id - ); - - $document = $this->corruptImageFieldXML($record); - - $this->updateXML( - $content->id, - $content->getVersionInfo()->versionNo, - $imageFieldDefinition->id, - $document - ); - - $repository = $this->getRepository(false); - $contentService = $repository->getContentService(); - - $contentService->deleteContent($content->getVersionInfo()->getContentInfo()); - - // Expect no League\Flysystem\CorruptedPathDetected thrown - } - - /** - * @return array<string,mixed> - */ - private function fetchXML(int $contentId, int $versionNo, int $fieldDefinitionId): array - { - $connection = $this->getRawDatabaseConnection(); - - $query = $connection->createQueryBuilder(); - $query - ->select('data_text') - ->from(Gateway::CONTENT_FIELD_TABLE) - ->andWhere('contentclassattribute_id = :contentclassattribute_id') - ->andWhere('version = :version') - ->andWhere('contentobject_id = :contentobject_id') - ->setParameter('contentclassattribute_id', $fieldDefinitionId, ParameterType::INTEGER) - ->setParameter('version', $versionNo, ParameterType::INTEGER) - ->setParameter('contentobject_id', $contentId, ParameterType::INTEGER); - $result = $query->execute(); - - return $result->fetchAssociative(); - } - - /** - * @param array<string,mixed> $row - */ - private function corruptImageFieldXML(array $row): DOMDocument - { - $corruptedChar = '­'; - - $document = new DOMDocument('1.0', 'utf-8'); - $document->loadXML($row['data_text']); - $elements = $document->getElementsByTagName('ezimage'); - $element = $elements->item(0); - $element->setAttribute('filename', $element->getAttribute('filename') . $corruptedChar); - $element->setAttribute('url', $element->getAttribute('url') . $corruptedChar); - - return $document; - } - - private function updateXML( - int $contentId, - int $versionNo, - int $fieldDefinitionId, - DOMDocument $document - ): void { - $connection = $this->getRawDatabaseConnection(); - - $query = $connection->createQueryBuilder(); - $query - ->update(Gateway::CONTENT_FIELD_TABLE) - ->set('data_text', ':data_text') - ->setParameter('data_text', $document->saveXML(), ParameterType::STRING) - ->andWhere('contentclassattribute_id = :contentclassattribute_id') - ->andWhere('version = :version') - ->andWhere('contentobject_id = :contentobject_id') - ->setParameter('contentclassattribute_id', $fieldDefinitionId, ParameterType::INTEGER) - ->setParameter('version', $versionNo, ParameterType::INTEGER) - ->setParameter('contentobject_id', $contentId, ParameterType::INTEGER); - - $query->execute(); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - private function publishNewImage( - string $name, - ImageValue $imageValue, - array $parentLocationIDs = [] - ): Content { - $repository = $this->getRepository(false); - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - $contentTypeService = $repository->getContentTypeService(); - - $contentCreateStruct = $contentService->newContentCreateStruct( - $contentTypeService->loadContentTypeByIdentifier('image'), - 'eng-GB' - ); - $contentCreateStruct->setField('name', $name); - $contentCreateStruct->setField('image', $imageValue); - - $locationCreateStructList = []; - foreach ($parentLocationIDs as $parentLocationID) { - $locationCreateStructList[] = $locationService->newLocationCreateStruct( - $parentLocationID - ); - } - - return $contentService->publishVersion( - $contentService - ->createContent($contentCreateStruct, $locationCreateStructList) - ->getVersionInfo() - ); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - private function updateImage(Content $publishedImageContent, ImageValue $newImageValue): Content - { - $repository = $this->getRepository(false); - $contentService = $repository->getContentService(); - - $contentDraft = $contentService->createContentDraft($publishedImageContent->contentInfo); - $contentUpdateStruct = $contentService->newContentUpdateStruct(); - $contentUpdateStruct->setField('image', $newImageValue); - $contentService->updateContent($contentDraft->getVersionInfo(), $contentUpdateStruct); - - $content = $contentService->publishVersion($contentDraft->getVersionInfo()); - - // reload Content to make sure proper data has been persisted - return $contentService->loadContentByContentInfo($content->contentInfo); - } - - private function getImageURI(Content $content): string - { - return $content->getFieldValue('image')->uri; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/IntegerIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/IntegerIntegrationTest.php deleted file mode 100644 index ed88bab664..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/IntegerIntegrationTest.php +++ /dev/null @@ -1,359 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\Integer\Value as IntegerValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class IntegerIntegrationTest extends SearchBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezinteger'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return []; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return []; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return [ - 'IntegerValueValidator' => [ - 'minIntegerValue' => [ - 'type' => 'int', - 'default' => null, - ], - 'maxIntegerValue' => [ - 'type' => 'int', - 'default' => null, - ], - ], - ]; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return [ - 'IntegerValueValidator' => [ - 'minIntegerValue' => 23, - 'maxIntegerValue' => 42, - ], - ]; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'IntegerValueValidator' => [ - 'minStringLength' => new \stdClass(), - ], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new IntegerValue(23); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return '23'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Integer\\Value', - $field->value - ); - - $expectedData = [ - 'value' => 23, - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - new \stdClass(), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - [ - new IntegerValue(5), - 'eZ\\Publish\\Core\\Base\\Exceptions\\ContentFieldValidationException', - ], - [ - new IntegerValue(127), - 'eZ\\Publish\\Core\\Base\\Exceptions\\ContentFieldValidationException', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return new IntegerValue(42); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Integer\\Value', - $field->value - ); - - $expectedData = [ - 'value' => 42, - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Integer\\Value', - $field->value - ); - - $expectedData = [ - 'value' => 23, - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new IntegerValue(23), - 23, - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - 42, - new IntegerValue(42), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new IntegerValue()], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - [new IntegerValue(0)], - [new IntegerValue(0.0)], - ]; - } - - protected function getValidSearchValueOne() - { - return 25; - } - - protected function getValidSearchValueTwo() - { - return 26; - } - - protected function getFullTextIndexedFieldData() - { - return [ - ['25', '26'], - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/KeywordIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/KeywordIntegrationTest.php deleted file mode 100644 index fbd24608f4..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/KeywordIntegrationTest.php +++ /dev/null @@ -1,634 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\FieldType\Keyword\Value as KeywordValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class KeywordIntegrationTest extends SearchMultivaluedBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezkeyword'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return []; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return []; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return []; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return []; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'unknown' => ['value' => 23], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new KeywordValue(['foo', 'bar', 'sindelfingen']); - } - - /** - * {@inheritdoc} - */ - public function getValidMultilingualFieldData(array $languageCodes) - { - $data = []; - foreach ($languageCodes as $languageCode) { - $data[$languageCode] = new KeywordValue( - [ - "{$languageCode} bar", - "{$languageCode} foo", - "$languageCode sindelfingen", - ] - ); - } - - return $data; - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'foo, bar, sindelfingen'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Keyword\\Value', - $field->value - ); - - $this->assertEquals( - ['foo' => true, 'bar' => true, 'sindelfingen' => true], - array_fill_keys($field->value->values, true) - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - new \stdClass(), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - [ - 23, - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return new KeywordValue(['bielefeld']); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Keyword\\Value', - $field->value - ); - - $this->assertEquals( - ['bielefeld' => true], - array_fill_keys($field->value->values, true) - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Keyword\\Value', - $field->value - ); - - $this->assertEquals( - ['foo' => true, 'bar' => true, 'sindelfingen' => true], - array_fill_keys($field->value->values, true) - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new KeywordValue(['bielefeld', 'sindelfingen']), - ['bielefeld', 'sindelfingen'], - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - ['sindelfeld', 'bielefingen'], - new KeywordValue(['sindelfeld', 'bielefingen']), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new KeywordValue()], - [new KeywordValue(null)], - [new KeywordValue([])], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - [ - new KeywordValue(['0']), - ], - ]; - } - - /** - * Test updating multiple contents with ezkeyword field preserves proper fields values. - */ - public function testUpdateContentKeywords() - { - $contentType = $this->testCreateContentType(); - $contentService = $this->getRepository()->getContentService(); - - $value01 = new KeywordValue(['foo', 'FOO', 'bar', 'baz']); - $contentDraft = $this->createContent($value01, $contentType); - $publishedContent01 = $contentService->publishVersion($contentDraft->versionInfo); - $this->assertContentFieldHasCorrectData($publishedContent01->contentInfo->id, $value01); - - // create another content with the same value - $value02 = $value01; - $contentDraft = $this->createContent($value02, $contentType); - $publishedContent02 = $contentService->publishVersion($contentDraft->versionInfo); - $this->assertContentFieldHasCorrectData($publishedContent02->contentInfo->id, $value02); - - // for the first content, create draft, remove one keyword and publish new version - $contentDraft = $contentService->createContentDraft($publishedContent01->contentInfo); - $updateStruct = $contentService->newContentUpdateStruct(); - $value01 = new KeywordValue(['foo', 'FOO', 'bar']); - $updateStruct->setField('data', $value01); - $contentDraft = $contentService->updateContent($contentDraft->versionInfo, $updateStruct); - $publishedContent01 = $contentService->publishVersion($contentDraft->versionInfo); - $this->assertContentFieldHasCorrectData($publishedContent01->contentInfo->id, $value01); - // reload and check the second content value01 - $this->assertContentFieldHasCorrectData($publishedContent02->contentInfo->id, $value02); - - // delete the second content - $contentService->deleteContent($publishedContent02->contentInfo); - // check if the first content was not affected - $this->assertContentFieldHasCorrectData($publishedContent01->contentInfo->id, $value01); - } - - /** - * {@inheritdoc} - */ - protected function createContent($fieldData, $contentType = null) - { - if ($contentType === null) { - $contentType = $this->testCreateContentType(); - } - - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-US'); - $createStruct->setField('name', 'Test object'); - $createStruct->setField( - 'data', - $fieldData - ); - - $createStruct->remoteId = md5(uniqid('', true) . microtime()); - $createStruct->alwaysAvailable = true; - - return $contentService->createContent($createStruct); - } - - /** - * Check that the given Content Object contains proper Keywords. - * - * @param int $contentId - * @param \eZ\Publish\Core\FieldType\Keyword\Value $value - */ - private function assertContentFieldHasCorrectData($contentId, KeywordValue $value) - { - $contentService = $this->getRepository()->getContentService(); - $loadedContent = $contentService->loadContent($contentId, ['eng-US']); - $dataField = $loadedContent->getField('data'); - sort($dataField->value->values); - sort($value->values); - $this->assertEquals($value, $dataField->value); - } - - /** - * Test going back to different version which contains different keywords than the other version. - */ - public function testGoBackToDifferentVersionWithDifferentKeywords(): void - { - $contentType = $this->testCreateContentType(); - $contentService = $this->getRepository()->getContentService(); - - $value01 = new KeywordValue(['foo', 'FOO', 'bar', 'baz']); - $contentDraft01 = $this->createContent($value01, $contentType); - $publishedContent01 = $contentService->publishVersion($contentDraft01->versionInfo); - - // for the first content, create draft, remove one keyword, add new keyword, and publish new version - $contentDraft = $contentService->createContentDraft($publishedContent01->contentInfo); - $updateStruct = $contentService->newContentUpdateStruct(); - $value02 = new KeywordValue(['foo', 'FOO', 'bar', 'far']); - $updateStruct->setField('data', $value02); - $contentDraft02 = $contentService->updateContent($contentDraft->versionInfo, $updateStruct); - $publishedContent01 = $contentService->publishVersion($contentDraft02->versionInfo); - - // go back to the first version and check whether keywords are correct - $contentDraft03 = $contentService->createContentDraft($publishedContent01->contentInfo, $contentDraft01->versionInfo); - $contentService->deleteContent($publishedContent01->contentInfo); - $this->assertEqualsCanonicalizing($contentDraft03->getFieldValue('data'), $value01); - } - - public function testKeywordsAreCaseSensitive() - { - $contentType = $this->testCreateContentType(); - $publishedContent01 = $this->createAndPublishContent('Foo', $contentType, md5(uniqid(__METHOD__, true))); - $publishedContent02 = $this->createAndPublishContent('foo', $contentType, md5(uniqid(__METHOD__, true))); - - $data = $publishedContent01->getField('data')->value; - $this->assertCount(1, $data->values); - $this->assertEquals('Foo', $data->values[0]); - - $data = $publishedContent02->getField('data')->value; - $this->assertCount(1, $data->values); - $this->assertEquals('foo', $data->values[0]); - } - - /** - * Create and publish content of $contentType with $fieldData. - * - * @param mixed $fieldData - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * @param string $remoteId - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - protected function createAndPublishContent($fieldData, ContentType $contentType, $remoteId) - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-US'); - $createStruct->setField('name', 'Test object'); - $createStruct->setField( - 'data', - $fieldData - ); - - $createStruct->remoteId = $remoteId; - $createStruct->alwaysAvailable = true; - - $contentDraft = $contentService->createContent($createStruct); - - return $contentService->publishVersion($contentDraft->versionInfo); - } - - protected function getValidSearchValueOne() - { - return 'add'; - } - - protected function getValidSearchValueTwo() - { - return 'branch'; - } - - protected function getValidMultivaluedSearchValuesOne() - { - return ['add', 'branch']; - } - - protected function getValidMultivaluedSearchValuesTwo() - { - return ['commit', 'delete']; - } - - public function checkFullTextSupport() - { - // Does nothing - } - - protected function getFullTextIndexedFieldData() - { - return [ - ['add', 'branch'], - ]; - } - - public function providerForTestTruncateField() - { - return [ - [new KeywordValue()], - [new KeywordValue(null)], - [new KeywordValue([])], - // an empty array is what actually REST API sets when field should be truncated - [[]], - ]; - } - - /** - * Test that setting an empty value truncates field data. - * - * @dataProvider providerForTestTruncateField - * - * @param mixed $emptyValue data representing an empty value - * - * @todo Move this method to BaseIntegrationTest when fixed for all field types. - */ - public function testTruncateField($emptyValue) - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $fieldType = $repository->getFieldTypeService()->getFieldType($this->getTypeName()); - - $contentDraft = $this->testCreateContent(); - $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); - - $contentDraft = $contentService->createContentDraft($publishedContent->contentInfo); - $updateStruct = $contentService->newContentUpdateStruct(); - $updateStruct->setField('data', $emptyValue); - $contentDraft = $contentService->updateContent($contentDraft->versionInfo, $updateStruct); - $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); - - $content = $contentService->loadContent($publishedContent->contentInfo->id, ['eng-US']); - - $fieldValue = $content->getField('data')->value; - self::assertTrue( - $fieldType->isEmptyValue($fieldValue), - 'Field value is not empty: ' . var_export($fieldValue, true) - ); - } - - /** - * Create test Content with ezkeyword type. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content[] - */ - protected function createKeywordContent() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - - $createStruct = $contentTypeService->newContentTypeCreateStruct('content-keyword'); - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->remoteId = 'content-keyword-123'; - $createStruct->names = ['eng-GB' => 'Keywords']; - $createStruct->creatorId = 14; - $createStruct->creationDate = new \DateTime(); - - $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('tags', 'ezkeyword'); - $fieldCreate->names = ['eng-GB' => 'Tags']; - $fieldCreate->fieldGroup = 'main'; - $fieldCreate->position = 1; - $fieldCreate->isTranslatable = false; - $fieldCreate->isSearchable = true; - - $createStruct->addFieldDefinition($fieldCreate); - - $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); - $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - - $toCreate = [ - 'content-keyword-456' => ['foo', 'bar'], - 'content-keyword-789' => ['bar', 'foobar'], - ]; - $createdContent = []; - foreach ($toCreate as $remoteId => $tagsString) { - $createStruct->remoteId = $remoteId; - $createStruct->alwaysAvailable = false; - $createStruct->setField( - 'tags', - $tagsString - ); - - $draft = $contentService->createContent($createStruct); - $createdContent[] = $contentService->publishVersion($draft->getVersionInfo()); - } - - $this->refreshSearch($repository); - - return $createdContent; - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testFindContentFieldCriterion() - { - $this->createKeywordContent(); - $repository = $this->getRepository(); - - $criterion = new Criterion\Field('tags', Criterion\Operator::IN, ['foo']); - $query = new Query(['query' => $criterion]); - - $searchService = $repository->getSearchService(); - $searchResult = $searchService->findContent($query); - - $this->assertEquals(1, $searchResult->totalCount); - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/MapLocationIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/MapLocationIntegrationTest.php deleted file mode 100644 index 997cf3d2d5..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/MapLocationIntegrationTest.php +++ /dev/null @@ -1,370 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\MapLocation\Value as MapLocationValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class MapLocationIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezgmaplocation'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return []; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return []; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return []; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return []; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'unknown' => ['value' => 23], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new MapLocationValue( - [ - 'latitude' => 51.559997, - 'longitude' => 6.767921, - 'address' => 'Bielefeld', - ] - ); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'Bielefeld'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertEquals( - $this->getValidCreationFieldData(), - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - new \stdClass(), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - [ - 23, - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - [ - new MapLocationValue( - [ - 'latitude' => 'string', - ] - ), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - [ - new MapLocationValue( - [ - 'latitude' => 23.42, - 'longitude' => 'invalid', - ] - ), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - [ - new MapLocationValue( - [ - 'latitude' => 23.42, - 'longitude' => 42.23, - 'address' => true, - ] - ), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - // https://maps.google.de/maps?qll=,&spn=0.139491,0.209942&sll=51.983611,8.574829&sspn=0.36242,0.839767&oq=Punta+Cana&t=h&hnear=Punta+Cana,+La+Altagracia,+Dominikanische+Republik&z=13 - return new MapLocationValue( - [ - 'latitude' => 18.524701, - 'longitude' => -68.363113, - 'address' => 'Punta Cana', - ] - ); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertEquals( - $this->getValidUpdateFieldData(), - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertEquals( - $this->getValidCreationFieldData(), - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new MapLocationValue( - [ - 'latitude' => 51.559997, - 'longitude' => 6.767921, - 'address' => 'Bielefeld', - ] - ), - [ - 'latitude' => 51.559997, - 'longitude' => 6.767921, - 'address' => 'Bielefeld', - ], - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - [ - 'latitude' => 51.559997, - 'longitude' => 6.767921, - 'address' => 'Bielefeld', - ], - new MapLocationValue( - [ - 'latitude' => 51.559997, - 'longitude' => 6.767921, - 'address' => 'Bielefeld', - ] - ), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new MapLocationValue()], - [ - new MapLocationValue( - [ - 'latitude' => null, - 'longitude' => null, - ] - ), - ], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - [ - new MapLocationValue( - [ - 'latitude' => 0, - 'longitude' => 0, - ] - ), - ], - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/MediaIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/MediaIntegrationTest.php deleted file mode 100644 index 451f646bd2..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/MediaIntegrationTest.php +++ /dev/null @@ -1,489 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\Media\Type as MediaType; -use eZ\Publish\Core\FieldType\Media\Value as MediaValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class MediaIntegrationTest extends FileSearchBaseIntegrationTest -{ - /** - * Stores the loaded image path for copy test. - */ - protected static $loadedMediaPath; - - /** - * IOService storage prefix for the tested Type's files. - * - * @var string - */ - protected static $storagePrefixConfigKey = 'binaryfile_storage_prefix'; - - protected function getStoragePrefix() - { - return $this->getConfigValue(self::$storagePrefixConfigKey); - } - - /** - * Sets up fixture data. - * - * @return array - */ - protected function getFixtureData() - { - return [ - 'create' => [ - 'id' => null, - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), - 'fileName' => 'Icy-Night-Flower-Binary.jpg', - 'fileSize' => filesize($path), - 'mimeType' => 'image/jpeg', - // Left out 'hasControlls', 'autoplay', 'loop', 'height' and - // 'width' by intention (will be set to defaults) - ], - 'update' => [ - 'id' => null, - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.png'), - 'fileName' => 'Blue-Blue-Blue-Sindelfingen.png', - 'fileSize' => filesize($path), - // Left out 'mimeType' by intention (will be auto-detected) - 'hasController' => true, - 'autoplay' => true, - 'loop' => true, - 'width' => 23, - 'height' => 42, - ], - ]; - } - - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezmedia'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return [ - 'mediaType' => [ - 'type' => 'choice', - 'default' => MediaType::TYPE_HTML5_VIDEO, - ], - ]; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return [ - 'mediaType' => MediaType::TYPE_FLASH, - ]; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return [ - 'FileSizeValidator' => [ - 'maxFileSize' => [ - 'type' => 'int', - 'default' => false, - ], - ], - ]; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return [ - 'FileSizeValidator' => [ - 'maxFileSize' => 2 * 1024 * 1024, // 2 MB - ], - ]; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'StringLengthValidator' => [ - 'minStringLength' => new \stdClass(), - ], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - $fixtureData = $this->getFixtureData(); - - return new MediaValue($fixtureData['create']); - } - - /** - * Get name generated by the given field type (via or fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'Icy-Night-Flower-Binary.jpg'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Media\\Value', - $field->value - ); - - $fixtureData = $this->getFixtureData(); - $expectedData = $fixtureData['create']; - - // Will change during storage - unset($expectedData['id']); - $expectedData['inputUri'] = null; - - $this->assertNotEmpty($field->value->id); - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - - $this->assertTrue( - $this->uriExistsOnIO($field->value->uri), - "File {$field->value->uri} doesn't exist." - ); - - self::$loadedMediaPath = $field->value->id; - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - [ - 'id' => '/foo/bar/sindelfingen.pdf', - ], - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentValue', - ], - [ - new MediaValue( - [ - 'id' => '/foo/bar/sindelfingen.pdf', - ] - ), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentValue', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - $fixtureData = $this->getFixtureData(); - - return new MediaValue($fixtureData['update']); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Media\\Value', - $field->value - ); - - $fixtureData = $this->getFixtureData(); - $expectedData = $fixtureData['update']; - - // Will change during storage - unset($expectedData['id']); - $expectedData['inputUri'] = null; - - $this->assertNotEmpty($field->value->id); - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - - $this->assertTrue( - $this->uriExistsOnIO($field->value->uri), - "File {$field->value->uri} doesn't exist." - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertFieldDataLoadedCorrect($field); - - $this->assertEquals( - self::$loadedMediaPath, - $field->value->id - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - $fixture = $this->getFixtureData(); - $expected = $fixture['create']; - - $expected['uri'] = $expected['inputUri']; - $expected['path'] = $expected['inputUri']; - - // Defaults set by type - $expected['hasController'] = false; - $expected['autoplay'] = false; - $expected['loop'] = false; - $expected['width'] = 0; - $expected['height'] = 0; - - $fieldValue = $this->getValidCreationFieldData(); - $fieldValue->uri = $expected['uri']; - - return [ - [ - $fieldValue, - $expected, - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - $fixture = $this->getFixtureData(); - $fixture['create']['uri'] = $fixture['create']['id']; - - $fieldValue = $this->getValidCreationFieldData(); - $fieldValue->uri = $fixture['create']['uri']; - - return [ - [ - $fixture['create'], - $fieldValue, - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new MediaValue()], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - ]; - } - - protected function getValidSearchValueOne() - { - return new MediaValue( - [ - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), - 'fileName' => 'blue-blue-blue-sindelfingen.jpg', - 'fileSize' => filesize($path), - ] - ); - } - - protected function getValidSearchValueTwo() - { - return new MediaValue( - [ - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.png'), - 'fileName' => 'icy-night-flower-binary.png', - 'fileSize' => filesize($path), - ] - ); - } - - protected function getSearchTargetValueOne() - { - $value = $this->getValidSearchValueOne(); - // ensure case-insensitivity - return strtoupper($value->fileName); - } - - protected function getSearchTargetValueTwo() - { - $value = $this->getValidSearchValueTwo(); - // ensure case-insensitivity - return strtoupper($value->fileName); - } - - protected function getAdditionallyIndexedFieldData() - { - return [ - [ - 'file_size', - $this->getValidSearchValueOne()->fileSize, - $this->getValidSearchValueTwo()->fileSize, - ], - [ - 'mime_type', - // ensure case-insensitivity - 'IMAGE/JPEG', - 'IMAGE/PNG', - ], - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/RelationIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/RelationIntegrationTest.php deleted file mode 100644 index 99e695ce59..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/RelationIntegrationTest.php +++ /dev/null @@ -1,419 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\Relation\Value as RelationValue; -use eZ\Publish\Core\Repository\Values\Content\Relation; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class RelationIntegrationTest extends SearchBaseIntegrationTest -{ - use RelationSearchBaseIntegrationTestTrait; - - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezobjectrelation'; - } - - /** - * {@inheritdoc} - */ - protected function supportsLikeWildcard($value) - { - parent::supportsLikeWildcard($value); - - return false; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @return array|\eZ\Publish\API\Repository\Values\Content\Relation[] - */ - public function getCreateExpectedRelations(Content $content) - { - $contentService = $this->getRepository()->getContentService(); - - return [ - new Relation( - [ - 'sourceFieldDefinitionIdentifier' => 'data', - 'type' => Relation::FIELD, - 'sourceContentInfo' => $content->contentInfo, - 'destinationContentInfo' => $contentService->loadContentInfo(4), - ] - ), - ]; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @return array|\eZ\Publish\API\Repository\Values\Content\Relation[] - */ - public function getUpdateExpectedRelations(Content $content) - { - $contentService = $this->getRepository()->getContentService(); - - return [ - new Relation( - [ - 'sourceFieldDefinitionIdentifier' => 'data', - 'type' => Relation::FIELD, - 'sourceContentInfo' => $content->contentInfo, - 'destinationContentInfo' => $contentService->loadContentInfo(49), - ] - ), - ]; - } - - /** - * @see eZ\Publish\API\Repository\Tests\FieldType\BaseIntegrationTest::getSettingsSchema() - */ - public function getSettingsSchema() - { - return [ - 'selectionMethod' => [ - 'type' => 'int', - 'default' => 0, - ], - 'selectionRoot' => [ - 'type' => 'string', - 'default' => null, - ], - 'selectionContentTypes' => [ - 'type' => 'array', - 'default' => [], - ], - ]; - } - - /** - * @see eZ\Publish\API\Repository\Tests\FieldType\BaseIntegrationTest::getValidatorSchema() - */ - public function getValidatorSchema() - { - return []; - } - - /** - * Get a valid $fieldSettings value. - * - * @todo Implement correctly - * - * @return mixed - */ - public function getValidFieldSettings() - { - return [ - 'selectionMethod' => 0, - 'selectionRoot' => '1', - 'selectionContentTypes' => [], - ]; - } - - /** - * Get a valid $validatorConfiguration. - * - * @todo Implement correctly - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return []; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @todo Implement correctly - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'selectionMethod' => 'a', - 'selectionRoot' => true, - 'unknownSetting' => false, - 'selectionContentTypes' => true, - ]; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @todo Implement correctly - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return ['noValidator' => true]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new RelationValue(4); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'Users'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Relation\\Value', - $field->value - ); - - $expectedData = [ - 'destinationContentId' => 4, - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - new RelationValue([]), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return new RelationValue(49); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Relation\\Value', - $field->value - ); - - $expectedData = [ - 'destinationContentId' => 49, - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Relation\\Value', - $field->value - ); - - $expectedData = [ - 'destinationContentId' => 4, - ]; - - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new RelationValue(4), - [ - 'destinationContentId' => 4, - ], - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - ['destinationContentId' => 4], - new RelationValue(4), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new RelationValue()], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - ]; - } - - protected function getValidSearchValueOne() - { - // Using different values for Legacy Search Engine, in order to demonstrate that sort will - // depend on how search engine stores field type's value. Legacy stores it as integer, while - // other engines store it as string. - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - return 4; - } - - return 10; - } - - protected function getValidSearchValueTwo() - { - // Using different values for Legacy Search Engine, in order to demonstrate that sort will - // depend on how search engine stores field type's value. Legacy stores it as integer, while - // other engines store it as string. - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - return 49; - } - - return 4; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/RelationListIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/RelationListIntegrationTest.php deleted file mode 100644 index 57435ecd2d..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/RelationListIntegrationTest.php +++ /dev/null @@ -1,458 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\RelationList\Type as RelationListType; -use eZ\Publish\Core\FieldType\RelationList\Value as RelationListValue; -use eZ\Publish\Core\Repository\Values\Content\Relation; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class RelationListIntegrationTest extends SearchMultivaluedBaseIntegrationTest -{ - use RelationSearchBaseIntegrationTestTrait; - - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezobjectrelationlist'; - } - - /** - * {@inheritdoc} - */ - protected function supportsLikeWildcard($value) - { - parent::supportsLikeWildcard($value); - - return false; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @return array|\eZ\Publish\API\Repository\Values\Content\Relation[] - */ - public function getCreateExpectedRelations(Content $content) - { - $contentService = $this->getRepository()->getContentService(); - - return [ - new Relation( - [ - 'sourceFieldDefinitionIdentifier' => 'data', - 'type' => Relation::FIELD, - 'sourceContentInfo' => $content->contentInfo, - 'destinationContentInfo' => $contentService->loadContentInfo(4), - ] - ), - new Relation( - [ - 'sourceFieldDefinitionIdentifier' => 'data', - 'type' => Relation::FIELD, - 'sourceContentInfo' => $content->contentInfo, - 'destinationContentInfo' => $contentService->loadContentInfo(49), - ] - ), - ]; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @return array|\eZ\Publish\API\Repository\Values\Content\Relation[] - */ - public function getUpdateExpectedRelations(Content $content) - { - $contentService = $this->getRepository()->getContentService(); - - return [ - new Relation( - [ - 'id' => null, - 'sourceFieldDefinitionIdentifier' => 'data', - 'type' => Relation::FIELD, - 'sourceContentInfo' => $content->contentInfo, - 'destinationContentInfo' => $contentService->loadContentInfo(4), - ] - ), - new Relation( - [ - 'sourceFieldDefinitionIdentifier' => 'data', - 'type' => Relation::FIELD, - 'sourceContentInfo' => $content->contentInfo, - 'destinationContentInfo' => $contentService->loadContentInfo(49), - ] - ), - new Relation( - [ - 'id' => null, - 'sourceFieldDefinitionIdentifier' => 'data', - 'type' => Relation::FIELD, - 'sourceContentInfo' => $content->contentInfo, - 'destinationContentInfo' => $contentService->loadContentInfo(54), - ] - ), - ]; - } - - /** - * @see eZ\Publish\API\Repository\Tests\FieldType\BaseIntegrationTest::getSettingsSchema() - */ - public function getSettingsSchema() - { - return [ - 'selectionMethod' => [ - 'type' => 'int', - 'default' => RelationListType::SELECTION_BROWSE, - ], - 'selectionDefaultLocation' => [ - 'type' => 'string', - 'default' => null, - ], - 'selectionContentTypes' => [ - 'type' => 'array', - 'default' => [], - ], - ]; - } - - /** - * @see eZ\Publish\API\Repository\Tests\FieldType\BaseIntegrationTest::getValidatorSchema() - */ - public function getValidatorSchema() - { - return [ - 'RelationListValueValidator' => [ - 'selectionLimit' => [ - 'type' => 'int', - 'default' => 0, - ], - ], - ]; - } - - /** - * Get a valid $fieldSettings value. - * - * @todo Implement correctly - * - * @return mixed - */ - public function getValidFieldSettings() - { - return [ - 'selectionMethod' => 1, - 'selectionDefaultLocation' => '2', - 'selectionContentTypes' => [], - ]; - } - - /** - * Get a valid $validatorConfiguration. - * - * @todo Implement correctly - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return [ - 'RelationListValueValidator' => [ - 'selectionLimit' => 0, - ], - ]; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @todo Implement correctly - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return ['selectionMethod' => 'a', 'selectionDefaultLocation' => true, 'unknownSetting' => false]; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @todo Implement correctly - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return ['noValidator' => true]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new RelationListValue([4, 49]); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'Users' . ' ' . 'Images'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\RelationList\\Value', - $field->value - ); - - $expectedData = [ - 'destinationContentIds' => [4, 49], - ]; - $this->assertPropertiesCorrectUnsorted( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - new RelationListValue([null]), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return new RelationListValue([49, 54, 4]); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\RelationList\\Value', - $field->value - ); - - $expectedData = [ - 'destinationContentIds' => [49, 54, 4], - ]; - $this->assertPropertiesCorrectUnsorted( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\RelationList\\Value', - $field->value - ); - - $expectedData = [ - 'destinationContentIds' => [4, 49], - ]; - $this->assertPropertiesCorrectUnsorted( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new RelationListValue([4, 49]), - [ - 'destinationContentIds' => [4, 49], - ], - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - ['destinationContentIds' => [4, 49]], - new RelationListValue([4, 49]), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new RelationListValue()], - [new RelationListValue([])], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - ]; - } - - protected function getValidSearchValueOne() - { - return [11]; - } - - protected function getValidSearchValueTwo() - { - return [12]; - } - - protected function getSearchTargetValueOne() - { - return 11; - } - - protected function getSearchTargetValueTwo() - { - return 12; - } - - protected function getValidMultivaluedSearchValuesOne() - { - return [11, 12]; - } - - protected function getValidMultivaluedSearchValuesTwo() - { - return [13, 14]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/SelectionIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/SelectionIntegrationTest.php deleted file mode 100644 index 653fe78228..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/SelectionIntegrationTest.php +++ /dev/null @@ -1,431 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\Selection\Value as SelectionValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class SelectionIntegrationTest extends SearchMultivaluedBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezselection'; - } - - /** - * {@inheritdoc} - * - * If Selection is improved to be able to index + search for string also with LegacySearch, then adapt this too. - */ - protected function supportsLikeWildcard($value) - { - parent::supportsLikeWildcard($value); - - return false; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return [ - 'isMultiple' => [ - 'type' => 'bool', - 'default' => false, - ], - 'options' => [ - 'type' => 'hash', - 'default' => [], - ], - 'multilingualOptions' => [ - 'type' => 'hash', - 'default' => [], - ], - ]; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return [ - 'isMultiple' => true, - 'options' => [ - 0 => 'A first', - 1 => 'Bielefeld', - 2 => 'Sindelfingen', - 3 => 'Turtles', - 4 => 'Zombies', - ], - 'multilingualOptions' => [ - 'eng-GB' => [ - 0 => 'A first', - 1 => 'Bielefeld', - 2 => 'Sindelfingen', - 3 => 'Turtles', - 4 => 'Zombies', - ], - ], - ]; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - 'isMultiple' => [], - 'options' => new \stdClass(), - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return []; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return []; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'unknown' => ['value' => 23], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new SelectionValue([0, 2]); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'A first' . ' ' . 'Sindelfingen'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Selection\\Value', - $field->value - ); - - $expectedData = [ - 'selection' => [0, 2], - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - new \stdClass(), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - [ - new SelectionValue([7]), - 'eZ\\Publish\\Core\\Base\\Exceptions\\ContentFieldValidationException', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return new SelectionValue([1]); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Selection\\Value', - $field->value - ); - - $expectedData = [ - 'selection' => [1], - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Selection\\Value', - $field->value - ); - - $expectedData = [ - 'selection' => [0, 2], - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new SelectionValue([0, 2]), - [0, 2], - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - [0, 2], - new SelectionValue([0, 2]), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new SelectionValue()], - [new SelectionValue([])], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - [ - new SelectionValue([0]), - ], - ]; - } - - protected function getValidSearchValueOne() - { - return [1]; - } - - protected function getValidSearchValueTwo() - { - return [2]; - } - - protected function getSearchTargetValueOne() - { - return 1; - } - - protected function getSearchTargetValueTwo() - { - return 2; - } - - protected function getAdditionallyIndexedFieldData() - { - return [ - [ - 'selected_option_value', - 'Bielefeld', - 'Sindelfingen', - ], - [ - 'sort_value', - '1', - '2', - ], - ]; - } - - protected function getValidMultivaluedSearchValuesOne() - { - return [0, 1]; - } - - protected function getValidMultivaluedSearchValuesTwo() - { - return [2, 3, 4]; - } - - protected function getAdditionallyIndexedMultivaluedFieldData() - { - return [ - [ - 'selected_option_value', - ['A first', 'Bielefeld'], - ['Sindelfingen', 'Turtles', 'Zombies'], - ], - ]; - } - - protected function getFullTextIndexedFieldData() - { - return [ - ['Bielefeld', 'Sindelfingen'], - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/TextBlockIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/TextBlockIntegrationTest.php deleted file mode 100644 index 84a96c4549..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/TextBlockIntegrationTest.php +++ /dev/null @@ -1,357 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\TextBlock\Value as TextBlockValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class TextBlockIntegrationTest extends SearchBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'eztext'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return [ - 'textRows' => [ - 'type' => 'int', - 'default' => 10, - ], - ]; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return [ - 'textRows' => 0, - ]; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return []; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return []; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'unknown' => ['value' => 23], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new TextBlockValue('Example'); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'Example'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\TextBlock\\Value', - $field->value - ); - - $expectedData = [ - 'text' => 'Example', - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - new \stdClass(), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return new TextBlockValue('Example 2'); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\TextBlock\\Value', - $field->value - ); - - $expectedData = [ - 'text' => 'Example 2', - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\TextBlock\\Value', - $field->value - ); - - $expectedData = [ - 'text' => 'Example', - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new TextBlockValue('Simple value'), - 'Simple value', - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - 'Foobar', - new TextBlockValue('Foobar'), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new TextBlockValue()], - [new TextBlockValue(null)], - [new TextBlockValue('')], - [new TextBlockValue("\n\n\n")], - [new TextBlockValue("\r\r\r")], - [new TextBlockValue(' ')], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - [new TextBlockValue(0)], - [new TextBlockValue('0')], - ]; - } - - protected function getValidSearchValueOne() - { - return 'caution is the " path to mediocrity' . PHP_EOL . 'something completely different'; - } - - protected function getSearchTargetValueOne() - { - // ensure case-insensitivity - return strtoupper('caution is the " path to mediocrity'); - } - - protected function getValidSearchValueTwo() - { - return "truth suffers from ' too much analysis\n hello and goodbye"; - } - - protected function getSearchTargetValueTwo() - { - // ensure case-insensitivity - return strtoupper("truth suffers from ' too much analysis"); - } - - protected function getFullTextIndexedFieldData() - { - return [ - ['path', 'analysis'], - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/TextLineIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/TextLineIntegrationTest.php deleted file mode 100644 index a1e93fc830..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/TextLineIntegrationTest.php +++ /dev/null @@ -1,374 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\TextLine\Value as TextLineValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class TextLineIntegrationTest extends SearchBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezstring'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return []; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return []; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return [ - 'StringLengthValidator' => [ - 'minStringLength' => [ - 'type' => 'int', - 'default' => null, - ], - 'maxStringLength' => [ - 'type' => 'int', - 'default' => null, - ], - ], - ]; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return [ - 'StringLengthValidator' => [ - 'minStringLength' => 1, - 'maxStringLength' => 42, - ], - ]; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'StringLengthValidator' => [ - 'minStringLength' => new \stdClass(), - ], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new TextLineValue('Example'); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'Example'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\TextLine\\Value', - $field->value - ); - - $expectedData = [ - 'text' => 'Example', - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - new \stdClass(), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - [ - 42, - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - [ - new TextLineValue(str_repeat('.', 64)), - 'eZ\\Publish\\Core\\Base\\Exceptions\\ContentFieldValidationException', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return new TextLineValue('Example 2'); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\TextLine\\Value', - $field->value - ); - - $expectedData = [ - 'text' => 'Example 2', - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\TextLine\\Value', - $field->value - ); - - $expectedData = [ - 'text' => 'Example', - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new TextLineValue('Simple value'), - 'Simple value', - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - 'Foobar', - new TextLineValue('Foobar'), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new TextLineValue()], - [new TextLineValue(null)], - [new TextLineValue('')], - [new TextLineValue(' ')], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - [new TextLineValue(0)], - [new TextLineValue('0')], - ]; - } - - protected function getValidSearchValueOne() - { - return 'aaa'; - } - - protected function getSearchTargetValueOne() - { - // ensure case-insensitivity - return strtoupper($this->getValidSearchValueOne()); - } - - protected function getValidSearchValueTwo() - { - return 'bbb'; - } - - protected function getSearchTargetValueTwo() - { - // ensure case-insensitivity - return strtoupper($this->getValidSearchValueTwo()); - } - - protected function getFullTextIndexedFieldData() - { - return [ - ['aaa', 'bbb'], - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/TimeIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/TimeIntegrationTest.php deleted file mode 100644 index 70bd7afa79..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/TimeIntegrationTest.php +++ /dev/null @@ -1,379 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use DateTime; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\Time\Value as TimeValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class TimeIntegrationTest extends SearchBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'eztime'; - } - - /** - * {@inheritdoc} - */ - protected function supportsLikeWildcard($value) - { - parent::supportsLikeWildcard($value); - - return false; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return [ - 'useSeconds' => [ - 'type' => 'bool', - 'default' => false, - ], - 'defaultType' => [ - 'type' => 'choice', - 'default' => 0, - ], - ]; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return [ - 'useSeconds' => false, - 'defaultType' => 0, - ]; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return []; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return []; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'unknown' => ['value' => 42], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - // We may only create times from timestamps here, since storing will - // loose information about the timezone. - return new TimeValue(3661); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return '1:01:01 am'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Time\\Value', - $field->value - ); - - $expectedData = [ - 'time' => 3661, - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - 'Some unknown date format', 'eZ\\Publish\\API\\Repository\\Exceptions\\InvalidArgumentException', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return \eZ\Publish\Core\FieldType\Time\Value - */ - public function getValidUpdateFieldData() - { - return TimeValue::fromTimestamp(12345678); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Time\\Value', - $field->value - ); - - $dateTime = new DateTime('@12345678'); - $expectedData = [ - 'time' => $dateTime->getTimestamp() - $dateTime->setTime(0, 0, 0)->getTimestamp(), - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Tests failing content update. - * - * @param mixed $failingValue - * @param string $expectedException - * - * @dataProvider provideInvalidUpdateFieldData - */ - public function testUpdateContentFails($failingValue, $expectedException) - { - return [ - [ - 'Some unknown date format', 'eZ\\Publish\\API\\Repository\\Exceptions\\InvalidArgumentException', - ], - ]; - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Time\\Value', - $field->value - ); - - $expectedData = [ - 'time' => 3661, - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - $timestamp = 123456; - $dateTime = new DateTime("@{$timestamp}"); - - return [ - [ - TimeValue::fromTimestamp($timestamp), - $dateTime->getTimestamp() - $dateTime->setTime(0, 0, 0)->getTimestamp(), - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - 3661, - new TimeValue(3661), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new TimeValue()], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - ]; - } - - protected function getValidSearchValueOne() - { - return new TimeValue($this->getSearchTargetValueOne()); - } - - protected function getValidSearchValueTwo() - { - return new TimeValue($this->getSearchTargetValueTwo()); - } - - protected function getSearchTargetValueOne() - { - return 9600; - } - - protected function getSearchTargetValueTwo() - { - return 14400; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/UrlIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/UrlIntegrationTest.php deleted file mode 100644 index c7ef18329d..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/UrlIntegrationTest.php +++ /dev/null @@ -1,380 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\Url\Value as UrlValue; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class UrlIntegrationTest extends SearchBaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezurl'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return []; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return []; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return []; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return []; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'unknown' => ['value' => 23], - ]; - } - - /** - * Get initial field data for valid object creation. - * - * @return mixed - */ - public function getValidCreationFieldData() - { - return new UrlValue('http://example.com', 'Example'); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return 'Example'; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Url\\Value', - $field->value - ); - - $expectedData = [ - 'link' => 'http://example.com', - 'text' => 'Example', - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return [ - [ - new UrlValue(23), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - [ - new UrlValue('http://example.com', 23), - 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentType', - ], - ]; - } - - /** - * Get update field externals data. - * - * @return array - */ - public function getValidUpdateFieldData() - { - return new UrlValue('http://example.com/2', 'Example 2'); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Url\\Value', - $field->value - ); - - $expectedData = [ - 'link' => 'http://example.com/2', - 'text' => 'Example 2', - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return $this->provideInvalidCreationFieldData(); - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - 'eZ\\Publish\\Core\\FieldType\\Url\\Value', - $field->value - ); - - $expectedData = [ - 'link' => 'http://example.com', - 'text' => 'Example', - ]; - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new UrlValue('http://example.com'), - [ - 'link' => 'http://example.com', - 'text' => null, - ], - ], - [ - new UrlValue('http://example.com', 'Link text'), - [ - 'link' => 'http://example.com', - 'text' => 'Link text', - ], - ], - ]; - } - - /** - * Get expectations for the fromHash call on our field value. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - ['link' => 'http://example.com/sindelfingen'], - new UrlValue('http://example.com/sindelfingen'), - ], - [ - ['link' => 'http://example.com/sindelfingen', 'text' => 'Foo'], - new UrlValue('http://example.com/sindelfingen', 'Foo'), - ], - ]; - } - - public function providerForTestIsEmptyValue() - { - return [ - [new UrlValue()], - [new UrlValue(null)], - [new UrlValue('')], - [new UrlValue('', '')], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - [ - new UrlValue('http://example.com'), - ], - ]; - } - - protected function getValidSearchValueOne() - { - return new UrlValue('http://ample.com', 'Ample'); - } - - protected function getValidSearchValueTwo() - { - return new UrlValue('http://example.com', 'Example'); - } - - protected function getSearchTargetValueOne() - { - return 'http://ample.com'; - } - - protected function getSearchTargetValueTwo() - { - return 'http://example.com'; - } - - protected function getAdditionallyIndexedFieldData() - { - return [ - [ - 'value_text', - // ensure case-insensitivity - 'AMPLE', - 'EXAMPLE', - ], - ]; - } - - protected function getFullTextIndexedFieldData() - { - return [ - ['ample', 'example'], - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/UserIntegrationTest.php b/eZ/Publish/API/Repository/Tests/FieldType/UserIntegrationTest.php deleted file mode 100644 index 6ac66904d5..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/UserIntegrationTest.php +++ /dev/null @@ -1,596 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use Doctrine\DBAL\Exception\NotNullConstraintViolationException; -use eZ\Publish\API\Repository\Exceptions\BadStateException; -use eZ\Publish\API\Repository\Exceptions\ForbiddenException; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\FieldType\User\Type; -use eZ\Publish\Core\FieldType\User\Value as UserValue; -use eZ\Publish\Core\Repository\Values\User\User; - -/** - * Integration test for use field type. - * - * @group integration - * @group field-type - */ -class UserIntegrationTest extends BaseIntegrationTest -{ - private const TEST_LOGIN = 'hans'; - - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezuser'; - } - - /** - * Get expected settings schema. - * - * @return array - */ - public function getSettingsSchema() - { - return [ - 'PasswordTTL' => [ - 'type' => 'int', - 'default' => null, - ], - 'PasswordTTLWarning' => [ - 'type' => 'int', - 'default' => null, - ], - 'RequireUniqueEmail' => [ - 'type' => 'bool', - 'default' => true, - ], - 'UsernamePattern' => [ - 'type' => 'string', - 'default' => '^[^@]+$', - ], - ]; - } - - /** - * Get a valid $fieldSettings value. - * - * @return mixed - */ - public function getValidFieldSettings() - { - return [ - 'PasswordTTL' => null, - 'PasswordTTLWarning' => null, - 'RequireUniqueEmail' => false, - 'UsernamePattern' => '.*', - ]; - } - - /** - * Get $fieldSettings value not accepted by the field type. - * - * @return mixed - */ - public function getInvalidFieldSettings() - { - return [ - 'somethingUnknown' => 0, - ]; - } - - /** - * Get expected validator schema. - * - * @return array - */ - public function getValidatorSchema() - { - return [ - 'PasswordValueValidator' => [ - 'requireAtLeastOneUpperCaseCharacter' => [ - 'type' => 'int', - 'default' => 1, - ], - 'requireAtLeastOneLowerCaseCharacter' => [ - 'type' => 'int', - 'default' => 1, - ], - 'requireAtLeastOneNumericCharacter' => [ - 'type' => 'int', - 'default' => 1, - ], - 'requireAtLeastOneNonAlphanumericCharacter' => [ - 'type' => 'int', - 'default' => null, - ], - 'requireNewPassword' => [ - 'type' => 'int', - 'default' => null, - ], - 'minLength' => [ - 'type' => 'int', - 'default' => 10, - ], - ], - ]; - } - - /** - * Get a valid $validatorConfiguration. - * - * @return mixed - */ - public function getValidValidatorConfiguration() - { - return [ - 'PasswordValueValidator' => [ - 'requireAtLeastOneUpperCaseCharacter' => true, - 'requireAtLeastOneLowerCaseCharacter' => true, - 'requireAtLeastOneNumericCharacter' => true, - 'requireAtLeastOneNonAlphanumericCharacter' => false, - 'requireNewPassword' => false, - 'minLength' => 10, - ], - ]; - } - - /** - * Get $validatorConfiguration not accepted by the field type. - * - * @return mixed - */ - public function getInvalidValidatorConfiguration() - { - return [ - 'unknown' => ['value' => 23], - ]; - } - - /** - * Get initial field externals data. - * - * @return \eZ\Publish\Core\FieldType\User\Value - */ - public function getValidCreationFieldData(): UserValue - { - return new UserValue([ - 'login' => self::TEST_LOGIN, - 'email' => sprintf('%s@example.com', self::TEST_LOGIN), - 'enabled' => true, - 'plainPassword' => 'PassWord42', - ]); - } - - /** - * Get name generated by the given field type (via fieldType->getName()). - * - * @return string - */ - public function getFieldName() - { - return self::TEST_LOGIN; - } - - /** - * Asserts that the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()} - * was stored and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - UserValue::class, - $field->value - ); - - $expectedData = [ - 'hasStoredLogin' => true, - 'login' => self::TEST_LOGIN, - 'email' => 'hans@example.com', - 'passwordHashType' => User::PASSWORD_HASH_PHP_DEFAULT, - 'enabled' => true, - ]; - - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - - $this->assertNotNull($field->value->contentId); - } - - /** - * Get field data which will result in errors during creation. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidCreationFieldData() - { - return []; - } - - public function testCreateContentFails( - $failingValue = null, - ?string $expectedException = null - ): void { - $this->markTestSkipped('Values are ignored on creation.'); - } - - /** - * Get update field externals data. - * - * @return \eZ\Publish\Core\FieldType\User\Value - */ - public function getValidUpdateFieldData() - { - return new UserValue( - [ - 'hasStoredLogin' => true, - 'login' => 'changeLogin', - 'email' => 'changeEmail@ez.no', - 'passwordHash' => '*2', - 'passwordHashType' => User::DEFAULT_PASSWORD_HASH, - 'enabled' => false, - ] - ); - } - - /** - * Get externals updated field data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function assertUpdatedFieldDataLoadedCorrect(Field $field) - { - $this->assertInstanceOf( - UserValue::class, - $field->value - ); - - $expectedData = [ - 'hasStoredLogin' => true, - 'login' => 'changeLogin', - 'email' => 'changeEmail@ez.no', - 'passwordHashType' => User::DEFAULT_PASSWORD_HASH, - 'enabled' => false, - ]; - - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - - $this->assertNotNull($field->value->contentId); - } - - /** - * Get field data which will result in errors during update. - * - * This is a PHPUnit data provider. - * - * The returned records must contain of an error producing data value and - * the expected exception class (from the API or SPI, not implementation - * specific!) as the second element. For example: - * - * <code> - * array( - * array( - * new DoomedValue( true ), - * 'eZ\\Publish\\API\\Repository\\Exceptions\\ContentValidationException' - * ), - * // ... - * ); - * </code> - * - * @return array[] - */ - public function provideInvalidUpdateFieldData() - { - return [ - [ - null, - NotNullConstraintViolationException::class, - ], - // @todo: Define more failure cases ... - ]; - } - - /** - * Asserts the the field data was loaded correctly. - * - * Asserts that the data provided by {@link getValidCreationFieldData()}; - * was copied and loaded correctly. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - */ - public function assertCopiedFieldDataLoadedCorrectly(Field $field) - { - $this->assertInstanceOf( - UserValue::class, - $field->value - ); - - $expectedData = [ - 'hasStoredLogin' => false, - 'contentId' => null, - 'login' => null, - 'email' => null, - 'passwordHash' => null, - 'passwordHashType' => null, - 'enabled' => false, - 'maxLogin' => null, - ]; - - $this->assertPropertiesCorrect( - $expectedData, - $field->value - ); - } - - /** - * Get data to test to hash method. - * - * This is a PHPUnit data provider - * - * The returned records must have the the original value assigned to the - * first index and the expected hash result to the second. For example: - * - * <code> - * array( - * array( - * new MyValue( true ), - * array( 'myValue' => true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideToHashData() - { - return [ - [ - new UserValue(['login' => self::TEST_LOGIN]), - [ - 'login' => self::TEST_LOGIN, - 'hasStoredLogin' => null, - 'contentId' => null, - 'email' => null, - 'passwordHash' => null, - 'passwordHashType' => null, - 'enabled' => null, - 'maxLogin' => null, - 'plainPassword' => null, - 'passwordUpdatedAt' => null, - ], - ], - ]; - } - - /** - * Get hashes and their respective converted values. - * - * This is a PHPUnit data provider - * - * The returned records must have the the input hash assigned to the - * first index and the expected value result to the second. For example: - * - * <code> - * array( - * array( - * array( 'myValue' => true ), - * new MyValue( true ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideFromHashData() - { - return [ - [ - ['login' => self::TEST_LOGIN], - new UserValue(['login' => self::TEST_LOGIN]), - ], - ]; - } - - /** - * Overwrite normal content creation. - * - * @param mixed $fieldData - */ - protected function createContent($fieldData, $contentType = null) - { - if ($contentType === null) { - $contentType = $this->testCreateContentType(); - } - - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - // Instantiate a create struct with mandatory properties - $userCreate = $userService->newUserCreateStruct( - self::TEST_LOGIN, - 'hans@example.com', - 'PassWord42', - 'eng-US', - $contentType - ); - $userCreate->enabled = true; - - // Set some fields required by the user ContentType - $userCreate->setField('name', 'Example User'); - - // ID of the "Editors" user group in an eZ Publish demo installation - $group = $userService->loadUserGroup(13); - - // Create a new user instance. - $user = $userService->createUser($userCreate, [$group]); - - // Create draft from user content object - $contentService = $repository->getContentService(); - - return $contentService->createContentDraft($user->content->contentInfo, $user->content->versionInfo); - } - - public function testCreateContentWithEmptyFieldValue() - { - $this->markTestSkipped('User field will never be created empty'); - } - - public function providerForTestIsEmptyValue() - { - return [ - [new UserValue()], - [new UserValue([])], - ]; - } - - public function providerForTestIsNotEmptyValue() - { - return [ - [ - $this->getValidCreationFieldData(), - ], - ]; - } - - public function testRemoveFieldDefinition() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - $content = $this->testPublishContent(); - $countBeforeRemoval = count($content->getFields()); - - $contentType = $contentTypeService->loadContentType($content->contentInfo->contentTypeId); - $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); - - $userFieldDefinition = $this->getUserFieldDefinition($contentType); - - $contentTypeService->removeFieldDefinition($contentTypeDraft, $userFieldDefinition); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - $content = $contentService->loadContent($content->id); - - $this->assertCount($countBeforeRemoval - 1, $content->getFields()); - $this->assertNull($content->getFieldValue($userFieldDefinition->identifier)); - } - - public function testAddFieldDefinition() - { - // Field cannot be added to ContentType with existing content. - $this->expectException(BadStateException::class); - - return parent::testAddFieldDefinition(); - } - - /** - * @depends testCreateContent - */ - public function testCopyField($content) - { - // Users cannot be copied. - $this->expectException(ForbiddenException::class); - $this->expectExceptionMessage(sprintf('User "%s" already exists', self::TEST_LOGIN)); - - return parent::testCopyField($content); - } - - /** - * @depends testCopyField - */ - public function testCopiedFieldType($content) - { - $this->markTestSkipped('Users cannot be copied, content is not passed to test.'); - } - - /** - * @depends testCopiedFieldType - */ - public function testCopiedExternalData(Field $field) - { - $this->markTestSkipped('Users cannot be copied, field is not passed to test.'); - } - - /** - * @see https://jira.ez.no/browse/EZP-30966 - */ - public function testUpdateFieldDefinitionWithIncompleteSettingsSchema() - { - $contentTypeService = $this->getRepository()->getContentTypeService(); - $contentType = $this->testCreateContentType(); - $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); - - $userFieldDefinition = $this->getUserFieldDefinition($contentType); - $userFieldDefinitionUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); - $userFieldDefinitionUpdateStruct->fieldSettings = [ - Type::PASSWORD_TTL_WARNING_SETTING => null, - Type::REQUIRE_UNIQUE_EMAIL => false, - Type::USERNAME_PATTERN => '.*', - ]; - - $contentTypeService->updateFieldDefinition($contentTypeDraft, $userFieldDefinition, $userFieldDefinitionUpdateStruct); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - $contentType = $contentTypeService->loadContentType($contentType->id); - $userFieldDefinition = $this->getUserFieldDefinition($contentType); - - $this->assertNull($userFieldDefinition->fieldSettings[Type::PASSWORD_TTL_WARNING_SETTING]); - } - - /** - * Finds ezuser field definition in given $contentType or mark test as failed if it doens't exists. - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition - */ - private function getUserFieldDefinition(ContentType $contentType): FieldDefinition - { - $fieldDefinition = $contentType->getFirstFieldDefinitionOfType('ezuser'); - - if ($fieldDefinition === null) { - $this->fail("'ezuser' field definition was not found"); - } - - return $fieldDefinition; - } -} diff --git a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/invalid/equation.xml b/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/invalid/equation.xml deleted file mode 100644 index aa8949c92d..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/invalid/equation.xml +++ /dev/null @@ -1,14 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" - xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" - version="5.0-variant ezpublish-1.0"> - <eztemplate name="equation"> - <ezcontent> - E = mc^2 - </ezcontent> - <ezconfig> - <ezvalue key="name">Equation</ezvalue> - </ezconfig> - </eztemplate> -</section> diff --git a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/invalid/unknown_tag.xml b/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/invalid/unknown_tag.xml deleted file mode 100644 index dc328b38b9..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/invalid/unknown_tag.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" - xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" - version="5.0-variant ezpublish-1.0"> - <eztemplate name="unknown_tag"> - <ezcontent>Undefined</ezcontent> - <ezconfig> - <ezvalue key="title">Test</ezvalue> - </ezconfig> - </eztemplate> -</section> diff --git a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/invalid/video.xml b/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/invalid/video.xml deleted file mode 100644 index a76f1335aa..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/invalid/video.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" - xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" - version="5.0-variant ezpublish-1.0"> - <eztemplate name="video"> - <ezcontent> - <para>Title: Test</para> - <para>Width: 640</para> - <para>Autoplay: false</para> - </ezcontent> - <ezconfig> - <ezvalue key="title">Test</ezvalue> - <ezvalue key="width">640</ezvalue> - <ezvalue key="autoplay">false</ezvalue> - <ezvalue key="unknown_attribute">false</ezvalue> - </ezconfig> - </eztemplate> -</section> diff --git a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/valid/equation.xml b/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/valid/equation.xml deleted file mode 100644 index 827ae59d35..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/valid/equation.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" - xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" - version="5.0-variant ezpublish-1.0"> - <eztemplate name="equation"> - <ezcontent> - E = mc^2 - </ezcontent> - <ezconfig> - <ezvalue key="name">Equation</ezvalue> - <ezvalue key="processor">Latex</ezvalue> - </ezconfig> - </eztemplate> -</section> diff --git a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/valid/video.xml b/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/valid/video.xml deleted file mode 100644 index 635ceb4f94..0000000000 --- a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/ezrichtext/custom_tags/valid/video.xml +++ /dev/null @@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" - xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" - version="5.0-variant ezpublish-1.0"> - <eztemplate name="video"> - <ezcontent> - <para>Title: Test</para> - <para>Width: 640</para> - <para>Autoplay: false</para> - </ezcontent> - <ezconfig> - <ezvalue key="title">Test</ezvalue> - <ezvalue key="width">640</ezvalue> - <ezvalue key="autoplay">false</ezvalue> - </ezconfig> - </eztemplate> -</section> diff --git a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/ContentFilteringAdapterTest.php b/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/ContentFilteringAdapterTest.php deleted file mode 100644 index cb2580acdc..0000000000 --- a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/ContentFilteringAdapterTest.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorAdapter; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\ContentFilteringAdapter; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentList; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchAll; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use PHPUnit\Framework\TestCase; - -final class ContentFilteringAdapterTest extends TestCase -{ - private const EXAMPLE_LANGUAGE_FILTER = ['eng-GB', 'pol-PL']; - private const EXAMPLE_OFFSET = 10; - private const EXAMPLE_LIMIT = 25; - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function testFetch(): void - { - $content1 = $this->createMock(Content::class); - $content2 = $this->createMock(Content::class); - $content3 = $this->createMock(Content::class); - - $contentList = new ContentList(3, [ - $content1, - $content2, - $content3, - ]); - - $expectedResults = [ - $content1, - $content2, - $content3, - ]; - - $originalFilter = new Filter(); - $originalFilter->withCriterion(new MatchAll()); - - $expectedFilter = new Filter(); - $expectedFilter->withCriterion(new MatchAll()); - $expectedFilter->sliceBy(self::EXAMPLE_LIMIT, self::EXAMPLE_OFFSET); - - $contentService = $this->createMock(ContentService::class); - $contentService - ->expects($this->once()) - ->method('find') - ->with($expectedFilter, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($contentList); - - $adapter = new ContentFilteringAdapter($contentService, $originalFilter, self::EXAMPLE_LANGUAGE_FILTER); - - self::assertSame( - $expectedResults, - iterator_to_array($adapter->fetch(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT)) - ); - - // Input $filter remains untouched - self::assertSame(0, $originalFilter->getOffset()); - self::assertSame(0, $originalFilter->getLimit()); - } -} diff --git a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php b/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php deleted file mode 100644 index d2c599a1c8..0000000000 --- a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorAdapter; - -use eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter; -use eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\ContentInfoSearchAdapter; -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\Query; - -final class ContentInfoSearchAdapterTest extends AbstractSearchAdapterTest -{ - protected function createAdapterUnderTest( - SearchService $searchService, - Query $query, - array $languageFilter, - bool $filterOnPermissions - ): AbstractSearchAdapter { - return new ContentInfoSearchAdapter( - $searchService, - $query, - self::EXAMPLE_LANGUAGE_FILTER, - true - ); - } - - protected function getExpectedFindMethod(): string - { - return 'findContentInfo'; - } -} diff --git a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php b/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php deleted file mode 100644 index 326e41a782..0000000000 --- a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorAdapter; - -use eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter; -use eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\ContentSearchAdapter; -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\Query; - -final class ContentSearchAdapterTest extends AbstractSearchAdapterTest -{ - protected function createAdapterUnderTest( - SearchService $searchService, - Query $query, - array $languageFilter, - bool $filterOnPermissions - ): AbstractSearchAdapter { - return new ContentSearchAdapter( - $searchService, - $query, - self::EXAMPLE_LANGUAGE_FILTER, - true - ); - } - - protected function getExpectedFindMethod(): string - { - return 'findContent'; - } -} diff --git a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/LocationFilteringAdapterTest.php b/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/LocationFilteringAdapterTest.php deleted file mode 100644 index 58e24be81f..0000000000 --- a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/LocationFilteringAdapterTest.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorAdapter; - -use eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\LocationFilteringAdapter; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationList; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchAll; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use PHPUnit\Framework\TestCase; - -final class LocationFilteringAdapterTest extends TestCase -{ - private const EXAMPLE_LANGUAGE_FILTER = ['eng-GB', 'pol-PL']; - private const EXAMPLE_OFFSET = 10; - private const EXAMPLE_LIMIT = 25; - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function testFetch(): void - { - $location1 = $this->createMock(Location::class); - $location2 = $this->createMock(Location::class); - $location3 = $this->createMock(Location::class); - - $locationList = new LocationList([ - 'locations' => [ - $location1, - $location2, - $location3, - ], - 'totalCount' => 3, - ]); - - $expectedResults = [ - $location1, - $location2, - $location3, - ]; - - $originalFilter = new Filter(); - $originalFilter->withCriterion(new MatchAll()); - - $expectedFilter = new Filter(); - $expectedFilter->withCriterion(new MatchAll()); - $expectedFilter->sliceBy(self::EXAMPLE_LIMIT, self::EXAMPLE_OFFSET); - - $locationService = $this->createMock(LocationService::class); - $locationService - ->expects($this->once()) - ->method('find') - ->with($expectedFilter, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($locationList); - - $adapter = new LocationFilteringAdapter($locationService, $originalFilter, self::EXAMPLE_LANGUAGE_FILTER); - - self::assertSame( - $expectedResults, - iterator_to_array($adapter->fetch(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT)) - ); - - // Input $filter remains untouched - self::assertSame(0, $originalFilter->getOffset()); - self::assertSame(0, $originalFilter->getLimit()); - } -} diff --git a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php b/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php deleted file mode 100644 index d4b39dc2b7..0000000000 --- a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorAdapter; - -use eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter; -use eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\LocationSearchAdapter; -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; - -final class LocationSearchAdapterTest extends AbstractSearchAdapterTest -{ - protected function createAdapterUnderTest( - SearchService $searchService, - Query $query, - array $languageFilter, - bool $filterOnPermissions - ): AbstractSearchAdapter { - return new LocationSearchAdapter( - $searchService, - $query, - self::EXAMPLE_LANGUAGE_FILTER, - true - ); - } - - protected function getExpectedFindMethod(): string - { - return 'findLocations'; - } - - protected function newQuery(): Query - { - return new LocationQuery(); - } -} diff --git a/eZ/Publish/API/Repository/Tests/LanguageServiceTest.php b/eZ/Publish/API/Repository/Tests/LanguageServiceTest.php deleted file mode 100644 index f0940375fb..0000000000 --- a/eZ/Publish/API/Repository/Tests/LanguageServiceTest.php +++ /dev/null @@ -1,747 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\LanguageCreateStruct; - -/** - * Test case for operations in the LanguageService using in memory storage. - * - * @see eZ\Publish\API\Repository\LanguageService - * @group integration - * @group language - */ -class LanguageServiceTest extends BaseTest -{ - /** - * Test for the newLanguageCreateStruct() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::newLanguageCreateStruct - */ - public function testNewLanguageCreateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - $languageCreate = $languageService->newLanguageCreateStruct(); - /* END: Use Case */ - - $this->assertInstanceOf( - LanguageCreateStruct::class, - $languageCreate - ); - - $this->assertPropertiesCorrect( - [ - 'languageCode' => null, - 'name' => null, - 'enabled' => true, - ], - $languageCreate - ); - } - - /** - * Test for the createLanguage() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\Language - * - * @covers \eZ\Publish\API\Repository\LanguageService::createLanguage - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testNewLanguageCreateStruct - */ - public function testCreateLanguage() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - $languageCreate = $languageService->newLanguageCreateStruct(); - $languageCreate->enabled = true; - $languageCreate->name = 'English (New Zealand)'; - $languageCreate->languageCode = 'eng-NZ'; - - $language = $languageService->createLanguage($languageCreate); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\Language', - $language - ); - - return $language; - } - - /** - * Test for the createLanguage() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Language $language - * - * @covers \eZ\Publish\API\Repository\LanguageService::createLanguage - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testCreateLanguage - */ - public function testCreateLanguageSetsIdPropertyOnReturnedLanguage($language) - { - $this->assertNotNull($language->id); - } - - /** - * Test for the createLanguage() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Language $language - * - * @covers \eZ\Publish\API\Repository\LanguageService::createLanguage - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testCreateLanguage - */ - public function testCreateLanguageSetsExpectedProperties($language) - { - $this->assertEquals( - [ - true, - 'English (New Zealand)', - 'eng-NZ', - ], - [ - $language->enabled, - $language->name, - $language->languageCode, - ] - ); - } - - /** - * Test for the createLanguage() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::createLanguage - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testCreateLanguage - */ - public function testCreateLanguageThrowsInvalidArgumentException() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'languageCreateStruct\' is invalid: language with the specified language code already exists'); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - $languageCreate = $languageService->newLanguageCreateStruct(); - $languageCreate->enabled = true; - $languageCreate->name = 'Norwegian'; - $languageCreate->languageCode = 'nor-NO'; - - $languageService->createLanguage($languageCreate); - - // This call should fail with an InvalidArgumentException, because - // the language code "nor-NO" already exists. - $languageService->createLanguage($languageCreate); - /* END: Use Case */ - } - - /** - * Test for the loadLanguageById() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::loadLanguageById - * @covers \eZ\Publish\API\Repository\LanguageService::loadLanguageListById - * @depends testCreateLanguage - */ - public function testLoadLanguageById() - { - $repository = $this->getRepository(); - - $languageService = $repository->getContentLanguageService(); - - $languageCreate = $languageService->newLanguageCreateStruct(); - $languageCreate->enabled = false; - $languageCreate->name = 'English'; - $languageCreate->languageCode = 'eng-NZ'; - - $languageId = $languageService->createLanguage($languageCreate)->id; - - $language = $languageService->loadLanguageById($languageId); - - $this->assertInstanceOf( - Language::class, - $language - ); - - $languages = $languageService->loadLanguageListById([$languageId]); - - $this->assertIsIterable($languages); - $this->assertCount(1, $languages); - $this->assertInstanceOf(Language::class, $languages[$languageId]); - } - - /** - * Test for the loadLanguageById() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::loadLanguageById - * @covers \eZ\Publish\API\Repository\LanguageService::loadLanguageListById - * @depends testLoadLanguageById - */ - public function testLoadLanguageByIdThrowsNotFoundException() - { - $repository = $this->getRepository(); - - $nonExistentLanguageId = $this->generateId('language', 2342); - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - $languages = $languageService->loadLanguageListById([$nonExistentLanguageId]); - - $this->assertIsIterable($languages); - $this->assertCount(0, $languages); - - $this->expectException(NotFoundException::class); - - $languageService->loadLanguageById($nonExistentLanguageId); - /* END: Use Case */ - } - - /** - * Test for the updateLanguageName() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::updateLanguageName - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testLoadLanguageById - */ - public function testUpdateLanguageName() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - $languageCreate = $languageService->newLanguageCreateStruct(); - $languageCreate->enabled = false; - $languageCreate->name = 'English'; - $languageCreate->languageCode = 'eng-NZ'; - - $languageId = $languageService->createLanguage($languageCreate)->id; - - $language = $languageService->loadLanguageById($languageId); - - $updatedLanguage = $languageService->updateLanguageName( - $language, - 'New language name.' - ); - /* END: Use Case */ - - // Verify that the service returns an updated language instance. - $this->assertInstanceOf( - Language::class, - $updatedLanguage - ); - - // Verify that the service also persists the changes - $updatedLanguage = $languageService->loadLanguageById($languageId); - $this->assertPropertiesCorrect( - [ - 'id' => $language->id, - 'name' => 'New language name.', - 'languageCode' => $language->languageCode, - 'enabled' => $language->enabled, - ], - $updatedLanguage - ); - } - - /** - * Test service method for updating language name throwing InvalidArgumentException. - * - * @covers \eZ\Publish\API\Repository\LanguageService::updateLanguageName - */ - public function testUpdateLanguageNameThrowsInvalidArgumentException() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'newName\' is invalid: \'\' is incorrect value'); - - $repository = $this->getRepository(); - $languageService = $repository->getContentLanguageService(); - - $language = $languageService->loadLanguage('eng-GB'); - $languageService->updateLanguageName($language, ''); - } - - /** - * Test for the enableLanguage() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::enableLanguage - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testLoadLanguageById - */ - public function testEnableLanguage() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - $languageCreate = $languageService->newLanguageCreateStruct(); - $languageCreate->enabled = false; - $languageCreate->name = 'English'; - $languageCreate->languageCode = 'eng-NZ'; - - $language = $languageService->createLanguage($languageCreate); - - // Now lets enable the newly created language - $languageService->enableLanguage($language); - - $enabledLanguage = $languageService->loadLanguageById($language->id); - /* END: Use Case */ - - $this->assertTrue($enabledLanguage->enabled); - } - - /** - * Test for the disableLanguage() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::disableLanguage - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testLoadLanguageById - */ - public function testDisableLanguage() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - $languageCreate = $languageService->newLanguageCreateStruct(); - $languageCreate->enabled = true; - $languageCreate->name = 'English'; - $languageCreate->languageCode = 'eng-NZ'; - - $language = $languageService->createLanguage($languageCreate); - - // Now lets disable the newly created language - $languageService->disableLanguage($language); - - $enabledLanguage = $languageService->loadLanguageById($language->id); - /* END: Use Case */ - - $this->assertFalse($enabledLanguage->enabled); - } - - /** - * Test for the loadLanguage() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::loadLanguage - * @covers \eZ\Publish\API\Repository\LanguageService::loadLanguageListByCode - * @depends testCreateLanguage - */ - public function testLoadLanguage() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - $languageCreate = $languageService->newLanguageCreateStruct(); - $languageCreate->enabled = true; - $languageCreate->name = 'English'; - $languageCreate->languageCode = 'eng-NZ'; - - $languageId = $languageService->createLanguage($languageCreate)->id; - - // Now load the newly created language by it's language code - $language = $languageService->loadLanguage('eng-NZ'); - /* END: Use Case */ - - $this->assertPropertiesCorrect( - [ - 'id' => $languageId, - 'languageCode' => 'eng-NZ', - 'name' => 'English', - 'enabled' => true, - ], - $language - ); - - $languages = $languageService->loadLanguageListByCode(['eng-NZ']); - - $this->assertIsIterable($languages); - $this->assertCount(1, $languages); - - $this->assertPropertiesCorrect( - [ - 'id' => $languageId, - 'languageCode' => 'eng-NZ', - 'name' => 'English', - 'enabled' => true, - ], - $languages['eng-NZ'] - ); - } - - /** - * Test for the loadLanguage() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::loadLanguage - * @covers \eZ\Publish\API\Repository\LanguageService::loadLanguageListByCode - * @depends testLoadLanguage - */ - public function testLoadLanguageThrowsNotFoundException() - { - $repository = $this->getRepository(); - - $languageService = $repository->getContentLanguageService(); - - $languages = $languageService->loadLanguageListByCode(['fre-FR']); - - $this->assertIsIterable($languages); - $this->assertCount(0, $languages); - - $this->expectException(NotFoundException::class); - - $languageService->loadLanguage('fre-FR'); - } - - /** - * Test service method for loading language throwing InvalidArgumentException. - * - * @covers \eZ\Publish\API\Repository\LanguageService::loadLanguage - */ - public function testLoadLanguageThrowsInvalidArgumentException() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'languageCode\' is invalid: language code has an invalid value'); - - $repository = $this->getRepository(); - - $repository->getContentLanguageService()->loadLanguage(''); - } - - /** - * Test for the loadLanguages() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::loadLanguages - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testCreateLanguage - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testLoadLanguage - */ - public function testLoadLanguages() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - // Create some languages - $languageCreateEnglish = $languageService->newLanguageCreateStruct(); - $languageCreateEnglish->enabled = false; - $languageCreateEnglish->name = 'English'; - $languageCreateEnglish->languageCode = 'eng-NZ'; - - $languageCreateFrench = $languageService->newLanguageCreateStruct(); - $languageCreateFrench->enabled = false; - $languageCreateFrench->name = 'French'; - $languageCreateFrench->languageCode = 'fre-FR'; - - $languageService->createLanguage($languageCreateEnglish); - $languageService->createLanguage($languageCreateFrench); - - $languages = $languageService->loadLanguages(); - self::assertIsArray($languages); - foreach ($languages as $language) { - self::assertInstanceOf(Language::class, $language); - $singleLanguage = $languageService->loadLanguage($language->languageCode); - $this->assertStructPropertiesCorrect( - $singleLanguage, - $language, - ['id', 'languageCode', 'name', 'enabled'] - ); - } - /* END: Use Case */ - - // eng-US, eng-GB, ger-DE + 2 newly created - $this->assertCount(5, $languages); - } - - /** - * Test for the loadLanguages() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::loadLanguages - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testCreateLanguage - */ - public function loadLanguagesReturnsAnEmptyArrayByDefault() - { - $repository = $this->getRepository(); - - $languageService = $repository->getContentLanguageService(); - - $this->assertSame([], $languageService->loadLanguages()); - } - - /** - * Test for the deleteLanguage() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::deleteLanguage - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testLoadLanguages - */ - public function testDeleteLanguage() - { - $repository = $this->getRepository(); - $languageService = $repository->getContentLanguageService(); - - $beforeCount = count($languageService->loadLanguages()); - - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - $languageCreateEnglish = $languageService->newLanguageCreateStruct(); - $languageCreateEnglish->enabled = false; - $languageCreateEnglish->name = 'English'; - $languageCreateEnglish->languageCode = 'eng-NZ'; - - $language = $languageService->createLanguage($languageCreateEnglish); - - // Delete the newly created language - $languageService->deleteLanguage($language); - /* END: Use Case */ - - // +1 -1 - $this->assertEquals($beforeCount, count($languageService->loadLanguages())); - - $this->expectException(NotFoundException::class); - $this->expectExceptionMessage('Could not find \'Language\' with identifier \'eng-NZ\''); - - // ensure just created & deleted language doesn't exist - $languageService->loadLanguage($languageCreateEnglish->languageCode); - self::fail('Language is still returned after being deleted'); - } - - /** - * Test for the deleteLanguage() method. - * - * NOTE: This test has a dependency against several methods in the content - * service, but because there is no topological sort for test dependencies - * we cannot declare them here. - * - * @covers \eZ\Publish\API\Repository\LanguageService::deleteLanguage - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testDeleteLanguage - * @depend(s) eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion - */ - public function testDeleteLanguageThrowsInvalidArgumentException() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'language\' is invalid: Cannot delete language: some content still references the language'); - - $repository = $this->getRepository(); - - $editorsGroupId = $this->generateId('group', 13); - /* BEGIN: Use Case */ - // $editorsGroupId is the ID of the "Editors" user group in an eZ - // Publish demo installation - - $languageService = $repository->getContentLanguageService(); - - $languageCreateEnglish = $languageService->newLanguageCreateStruct(); - $languageCreateEnglish->enabled = true; - $languageCreateEnglish->name = 'English'; - $languageCreateEnglish->languageCode = 'eng-NZ'; - - $language = $languageService->createLanguage($languageCreateEnglish); - - $contentService = $repository->getContentService(); - - // Get metadata update struct and set new language as main language. - $metadataUpdate = $contentService->newContentMetadataUpdateStruct(); - $metadataUpdate->mainLanguageCode = 'eng-NZ'; - - // Update content object - $contentService->updateContentMetadata( - $contentService->loadContentInfo($editorsGroupId), - $metadataUpdate - ); - - // This call will fail with an "InvalidArgumentException", because the - // new language is used by a content object. - $languageService->deleteLanguage($language); - /* END: Use Case */ - } - - /** - * Test for the getDefaultLanguageCode() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::getDefaultLanguageCode - */ - public function testGetDefaultLanguageCode() - { - $repository = $this->getRepository(); - $languageService = $repository->getContentLanguageService(); - - $this->assertRegExp( - '(^[a-z]{3}\-[A-Z]{2}$)', - $languageService->getDefaultLanguageCode() - ); - } - - /** - * Test for the createLanguage() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::createLanguage - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testCreateLanguage - */ - public function testCreateLanguageInTransactionWithRollback() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Get create struct and set properties - $languageCreate = $languageService->newLanguageCreateStruct(); - $languageCreate->enabled = true; - $languageCreate->name = 'English (New Zealand)'; - $languageCreate->languageCode = 'eng-NZ'; - - // Create new language - $languageService->createLanguage($languageCreate); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes - $repository->rollback(); - - try { - // This call will fail with a "NotFoundException" - $languageService->loadLanguage('eng-NZ'); - } catch (NotFoundException $e) { - // Expected execution path - } - /* END: Use Case */ - - $this->assertTrue(isset($e), 'Can still load language after rollback'); - } - - /** - * Test for the createLanguage() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::createLanguage - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testCreateLanguage - */ - public function testCreateLanguageInTransactionWithCommit() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Get create struct and set properties - $languageCreate = $languageService->newLanguageCreateStruct(); - $languageCreate->enabled = true; - $languageCreate->name = 'English (New Zealand)'; - $languageCreate->languageCode = 'eng-NZ'; - - // Create new language - $languageService->createLanguage($languageCreate); - - // Commit all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Load new language - $language = $languageService->loadLanguage('eng-NZ'); - /* END: Use Case */ - - $this->assertEquals('eng-NZ', $language->languageCode); - } - - /** - * Test for the updateLanguageName() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::updateLanguageName - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testUpdateLanguageName - */ - public function testUpdateLanguageNameInTransactionWithRollback() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Load an existing language - $language = $languageService->loadLanguage('eng-US'); - - // Update the language name - $languageService->updateLanguageName($language, 'My English'); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes - $repository->rollback(); - - // Load updated version, name will still be "English (American)" - $updatedLanguage = $languageService->loadLanguage('eng-US'); - /* END: Use Case */ - - $this->assertEquals('English (American)', $updatedLanguage->name); - } - - /** - * Test for the updateLanguageName() method. - * - * @covers \eZ\Publish\API\Repository\LanguageService::updateLanguageName - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testUpdateLanguageName - */ - public function testUpdateLanguageNameInTransactionWithCommit() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $languageService = $repository->getContentLanguageService(); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Load an existing language - $language = $languageService->loadLanguage('eng-US'); - - // Update the language name - $languageService->updateLanguageName($language, 'My English'); - - // Commit all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Load updated version, name will be "My English" - $updatedLanguage = $languageService->loadLanguage('eng-US'); - /* END: Use Case */ - - $this->assertEquals('My English', $updatedLanguage->name); - } -} diff --git a/eZ/Publish/API/Repository/Tests/Limitation/PermissionResolver/BaseLimitationIntegrationTest.php b/eZ/Publish/API/Repository/Tests/Limitation/PermissionResolver/BaseLimitationIntegrationTest.php deleted file mode 100644 index 9219c18c91..0000000000 --- a/eZ/Publish/API/Repository/Tests/Limitation/PermissionResolver/BaseLimitationIntegrationTest.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\Limitation\PermissionResolver; - -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * Base class for all Limitation integration tests. - */ -abstract class BaseLimitationIntegrationTest extends BaseTest -{ - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - protected $permissionResolver; - - protected function setUp(): void - { - $repository = $this->getRepository(false); - $this->permissionResolver = $repository->getPermissionResolver(); - } - - /** - * Map Limitations list to readable string for debugging purposes. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation[] $limitations - * - * @return string - */ - protected function getLimitationsListAsString(array $limitations): string - { - $str = ''; - foreach ($limitations as $limitation) { - $str .= sprintf( - '%s[%s]', - get_class($limitation), - implode(', ', $limitation->limitationValues) - ); - } - - return $str; - } - - /** - * Create Editor user with the given Policy and Limitations and set it as current user. - * - * @param string $module - * @param string $function - * @param array $limitations - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - protected function loginAsEditorUserWithLimitations(string $module, string $function, array $limitations = []): void - { - $user = $this->createUserWithPolicies( - uniqid('editor'), - [ - ['module' => $module, 'function' => $function, 'limitations' => $limitations], - ] - ); - - $this->permissionResolver->setCurrentUserReference($user); - } - - /** - * @param bool $expectedResult - * @param string $module - * @param string $function - * @param array $limitations - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param array $targets - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - protected function assertCanUser( - bool $expectedResult, - string $module, - string $function, - array $limitations, - ValueObject $object, - array $targets = [] - ): void { - self::assertEquals( - $expectedResult, - $this->permissionResolver->canUser($module, $function, $object, $targets), - sprintf( - 'Failure for %s/%s with Limitations: %s', - $module, - $function, - $this->getLimitationsListAsString($limitations) - ) - ); - } -} diff --git a/eZ/Publish/API/Repository/Tests/Limitation/PermissionResolver/LocationLimitationIntegrationTest.php b/eZ/Publish/API/Repository/Tests/Limitation/PermissionResolver/LocationLimitationIntegrationTest.php deleted file mode 100644 index d570e9a1b7..0000000000 --- a/eZ/Publish/API/Repository/Tests/Limitation/PermissionResolver/LocationLimitationIntegrationTest.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\Limitation\PermissionResolver; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation; -use eZ\Publish\SPI\Limitation\Target\Version; - -class LocationLimitationIntegrationTest extends BaseLimitationIntegrationTest -{ - private const LOCATION_ID = 2; - - public function providerForCanUserEditOrPublishContent(): array - { - $limitationRoot = new LocationLimitation(); - $limitationRoot->limitationValues = [self::LOCATION_ID]; - - return [ - [[$limitationRoot], true], - ]; - } - - /** - * @dataProvider providerForCanUserEditOrPublishContent - * - * @param array $limitations - * @param bool $expectedResult - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testCanUserEditContent(array $limitations, bool $expectedResult): void - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation(2); - - $this->loginAsEditorUserWithLimitations('content', 'edit', $limitations); - - $this->assertCanUser( - $expectedResult, - 'content', - 'edit', - $limitations, - $location->contentInfo, - [$location] - ); - - $this->assertCanUser( - $expectedResult, - 'content', - 'edit', - $limitations, - $location->contentInfo, - [$location, new Version(['allLanguageCodesList' => 'eng-GB'])] - ); - } - - /** - * @dataProvider providerForCanUserEditOrPublishContent - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testCanUserReadTrashedContent(array $limitations, bool $expectedResult): void - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation(2); - - $this->loginAsEditorUserWithLimitations('content', 'read', $limitations); - - $trashItem = $repository->sudo( - static function (Repository $repository) use ($location) { - return $repository->getTrashService()->trash($location); - } - ); - - $this->assertCanUser( - $expectedResult, - 'content', - 'read', - $limitations, - $trashItem->contentInfo - ); - } -} diff --git a/eZ/Publish/API/Repository/Tests/LocationServiceTest.php b/eZ/Publish/API/Repository/Tests/LocationServiceTest.php deleted file mode 100644 index 153f768ba7..0000000000 --- a/eZ/Publish/API/Repository/Tests/LocationServiceTest.php +++ /dev/null @@ -1,3699 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\BadStateException; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\URLAliasService as URLAliasServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\LocationList; -use eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation; -use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct; - -/** - * Test case for operations in the LocationService using in memory storage. - * - * @see eZ\Publish\API\Repository\LocationService - * @group location - */ -class LocationServiceTest extends BaseTest -{ - /** - * Test for the newLocationCreateStruct() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct - * - * @see \eZ\Publish\API\Repository\LocationService::newLocationCreateStruct() - */ - public function testNewLocationCreateStruct() - { - $repository = $this->getRepository(); - - $parentLocationId = $this->generateId('location', 1); - /* BEGIN: Use Case */ - // $parentLocationId is the ID of an existing location - $locationService = $repository->getLocationService(); - - $locationCreate = $locationService->newLocationCreateStruct( - $parentLocationId - ); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\LocationCreateStruct', - $locationCreate - ); - - return $locationCreate; - } - - /** - * Test for the newLocationCreateStruct() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct $locationCreate - * - * @see \eZ\Publish\API\Repository\LocationService::newLocationCreateStruct() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testNewLocationCreateStruct - */ - public function testNewLocationCreateStructValues(LocationCreateStruct $locationCreate) - { - $this->assertPropertiesCorrect( - [ - 'priority' => 0, - 'hidden' => false, - // remoteId should be initialized with a default value - //'remoteId' => null, - 'sortField' => null, - 'sortOrder' => null, - 'parentLocationId' => $this->generateId('location', 1), - ], - $locationCreate - ); - } - - /** - * Test for the createLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::createLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testNewLocationCreateStruct - */ - public function testCreateLocation() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', 41); - $parentLocationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $contentId is the ID of an existing content object - // $parentLocationId is the ID of an existing location - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - - // ContentInfo for "How to use eZ Publish" - $contentInfo = $contentService->loadContentInfo($contentId); - - $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); - $locationCreate->priority = 23; - $locationCreate->hidden = true; - $locationCreate->remoteId = 'sindelfingen'; - $locationCreate->sortField = Location::SORT_FIELD_NODE_ID; - $locationCreate->sortOrder = Location::SORT_ORDER_DESC; - - $location = $locationService->createLocation( - $contentInfo, - $locationCreate - ); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\Location', - $location - ); - - return [ - 'locationCreate' => $locationCreate, - 'createdLocation' => $location, - 'contentInfo' => $contentInfo, - 'parentLocation' => $locationService->loadLocation($this->generateId('location', 5)), - ]; - } - - /** - * Test for the createLocation() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::createLocation - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testCreateLocation - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testHideContent - */ - public function testCreateLocationChecksContentVisibility(): void - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', 41); - $parentLocationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $contentId is the ID of an existing content object - // $parentLocationId is the ID of an existing location - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - - // ContentInfo for "How to use eZ Publish" - $contentInfo = $contentService->loadContentInfo($contentId); - $contentService->hideContent($contentInfo); - - $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); - $locationCreate->priority = 23; - $locationCreate->hidden = false; - $locationCreate->remoteId = 'sindelfingen'; - $locationCreate->sortField = Location::SORT_FIELD_NODE_ID; - $locationCreate->sortOrder = Location::SORT_ORDER_DESC; - - $location = $locationService->createLocation( - $contentInfo, - $locationCreate - ); - /* END: Use Case */ - - self::assertInstanceOf(Location::class, $location); - - self::assertTrue($location->invisible); - } - - /** - * Test for the createLocation() method with utilizing default ContentType sorting options. - * - * @covers \eZ\Publish\API\Repository\LocationService::createLocation - */ - public function testCreateLocationWithContentTypeSortingOptions(): void - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', 41); - $parentLocationId = $this->generateId('location', 5); - // $contentId is the ID of an existing content object - // $parentLocationId is the ID of an existing location - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - $locationService = $repository->getLocationService(); - - // ContentInfo for "How to use eZ Publish" - $contentInfo = $contentService->loadContentInfo($contentId); - - // ContentType loading - $contentType = $contentTypeService->loadContentType($contentInfo->contentTypeId); - - $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); - $locationCreate->priority = 23; - $locationCreate->hidden = true; - $locationCreate->remoteId = 'sindelfingen'; - - $location = $locationService->createLocation( - $contentInfo, - $locationCreate - ); - - $this->assertEquals($contentType->defaultSortField, $location->sortField); - $this->assertEquals($contentType->defaultSortOrder, $location->sortOrder); - } - - /** - * Test for the createLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::createLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testCreateLocation - */ - public function testCreateLocationStructValues(array $data) - { - $locationCreate = $data['locationCreate']; - $createdLocation = $data['createdLocation']; - $contentInfo = $data['contentInfo']; - - $this->assertPropertiesCorrect( - [ - 'priority' => $locationCreate->priority, - 'hidden' => $locationCreate->hidden, - 'invisible' => $locationCreate->hidden, - 'remoteId' => $locationCreate->remoteId, - 'contentInfo' => $contentInfo, - 'parentLocationId' => $locationCreate->parentLocationId, - 'pathString' => '/1/5/' . $this->parseId('location', $createdLocation->id) . '/', - 'depth' => 2, - 'sortField' => $locationCreate->sortField, - 'sortOrder' => $locationCreate->sortOrder, - ], - $createdLocation - ); - - $this->assertNotNull($createdLocation->id); - } - - /** - * Test for the createLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::createLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testNewLocationCreateStruct - */ - public function testCreateLocationThrowsInvalidArgumentExceptionContentAlreadyBelowParent() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', 11); - $parentLocationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $contentId is the ID of an existing content object - // $parentLocationId is the ID of an existing location which already - // has the content assigned to one of its descendant locations - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - - // ContentInfo for "How to use eZ Publish" - $contentInfo = $contentService->loadContentInfo($contentId); - - $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); - - // Throws exception, since content is already located at "/1/2/107/110/" - $locationService->createLocation( - $contentInfo, - $locationCreate - ); - /* END: Use Case */ - } - - /** - * Test for the createLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::createLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testNewLocationCreateStruct - */ - public function testCreateLocationThrowsInvalidArgumentExceptionParentIsSubLocationOfContent() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', 4); - $parentLocationId = $this->generateId('location', 12); - /* BEGIN: Use Case */ - // $contentId is the ID of an existing content object - // $parentLocationId is the ID of an existing location which is below a - // location that is assigned to the content - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - - // ContentInfo for "How to use eZ Publish" - $contentInfo = $contentService->loadContentInfo($contentId); - - $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); - - // Throws exception, since content is already located at "/1/2/" - $locationService->createLocation( - $contentInfo, - $locationCreate - ); - /* END: Use Case */ - } - - /** - * Test for the createLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::createLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testNewLocationCreateStruct - */ - public function testCreateLocationThrowsInvalidArgumentExceptionRemoteIdExists() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', 41); - $parentLocationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $contentId is the ID of an existing content object - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - - // ContentInfo for "How to use eZ Publish" - $contentInfo = $contentService->loadContentInfo($contentId); - - $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); - // This remote ID already exists - $locationCreate->remoteId = 'f3e90596361e31d496d4026eb624c983'; - - // Throws exception, since remote ID is already in use - $locationService->createLocation( - $contentInfo, - $locationCreate - ); - /* END: Use Case */ - } - - /** - * Test for the createLocation() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::createLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testNewLocationCreateStruct - * @dataProvider dataProviderForOutOfRangeLocationPriority - */ - public function testCreateLocationThrowsInvalidArgumentExceptionPriorityIsOutOfRange($priority) - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', 41); - $parentLocationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $contentId is the ID of an existing content object - // $parentLocationId is the ID of an existing location - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - - // ContentInfo for "How to use eZ Publish" - $contentInfo = $contentService->loadContentInfo($contentId); - - $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); - $locationCreate->priority = $priority; - $locationCreate->hidden = true; - $locationCreate->remoteId = 'sindelfingen'; - $locationCreate->sortField = Location::SORT_FIELD_NODE_ID; - $locationCreate->sortOrder = Location::SORT_ORDER_DESC; - - // Throws exception, since priority is out of range - $locationService->createLocation( - $contentInfo, - $locationCreate - ); - /* END: Use Case */ - } - - public function dataProviderForOutOfRangeLocationPriority() - { - return [[-2147483649], [2147483648]]; - } - - /** - * Test for the createLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::createLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testCreateLocation - */ - public function testCreateLocationInTransactionWithRollback() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', 41); - $parentLocationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $contentId is the ID of an existing content object - // $parentLocationId is the ID of an existing location - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - - $repository->beginTransaction(); - - try { - // ContentInfo for "How to use eZ Publish" - $contentInfo = $contentService->loadContentInfo($contentId); - - $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); - $locationCreate->remoteId = 'sindelfingen'; - - $createdLocationId = $locationService->createLocation( - $contentInfo, - $locationCreate - )->id; - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - $repository->rollback(); - - try { - // Throws exception since creation of location was rolled back - $location = $locationService->loadLocation($createdLocationId); - } catch (NotFoundException $e) { - return; - } - /* END: Use Case */ - - $this->fail('Objects still exists after rollback.'); - } - - /** - * Test for the loadLocation() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\Location - * - * @covers \eZ\Publish\API\Repository\LocationService::loadLocation - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testCreateLocation - */ - public function testLoadLocation() - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $locationId is the ID of an existing location - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation($locationId); - /* END: Use Case */ - - $this->assertInstanceOf( - Location::class, - $location - ); - self::assertEquals(5, $location->id); - - return $location; - } - - /** - * Test for the loadLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::loadLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testLoadLocationRootStructValues() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - $location = $locationService->loadLocation($this->generateId('location', 1)); - - $this->assertRootLocationStructValues($location); - } - - public function testLoadLocationRootStructValuesWithPrioritizedLanguages(): void - { - $repository = $this->getRepository(); - - $rootLocation = $repository - ->getLocationService() - ->loadLocation( - $this->generateId('location', 1), - [ - 'eng-GB', - 'ger-DE', - ] - ); - - $this->assertRootLocationStructValues($rootLocation); - } - - private function assertRootLocationStructValues(Location $location): void - { - $legacyDateTime = new \DateTime(); - $legacyDateTime->setTimestamp(1030968000); - - $this->assertInstanceOf(Location::class, $location); - $this->assertPropertiesCorrect( - [ - 'id' => $this->generateId('location', 1), - 'status' => 1, - 'priority' => 0, - 'hidden' => false, - 'invisible' => false, - 'remoteId' => '629709ba256fe317c3ddcee35453a96a', - 'parentLocationId' => $this->generateId('location', 1), - 'pathString' => '/1/', - 'depth' => 0, - 'sortField' => 1, - 'sortOrder' => 1, - ], - $location - ); - - $this->assertInstanceOf(ContentInfo::class, $location->contentInfo); - $this->assertPropertiesCorrect( - [ - 'id' => $this->generateId('content', 0), - 'name' => 'Top Level Nodes', - 'sectionId' => 1, - 'mainLocationId' => 1, - 'contentTypeId' => 1, - 'currentVersionNo' => 1, - 'published' => 1, - 'ownerId' => 14, - 'modificationDate' => $legacyDateTime, - 'publishedDate' => $legacyDateTime, - 'alwaysAvailable' => 1, - 'remoteId' => null, - 'mainLanguageCode' => 'eng-GB', - ], - $location->contentInfo - ); - } - - /** - * Test for the loadLocation() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @see \eZ\Publish\API\Repository\LocationService::loadLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testLoadLocationStructValues(Location $location) - { - $this->assertPropertiesCorrect( - [ - 'id' => $this->generateId('location', 5), - 'priority' => 0, - 'hidden' => false, - 'invisible' => false, - 'remoteId' => '3f6d92f8044aed134f32153517850f5a', - 'parentLocationId' => $this->generateId('location', 1), - 'pathString' => '/1/5/', - 'depth' => 1, - 'sortField' => 1, - 'sortOrder' => 1, - ], - $location - ); - - $this->assertInstanceOf(ContentInfo::class, $location->contentInfo); - $this->assertEquals($this->generateId('object', 4), $location->contentInfo->id); - - $this->assertInstanceOf(Location::class, $location->getParentLocation()); - $this->assertEquals($this->generateId('location', 1), $location->getParentLocation()->id); - - // Check lazy loaded proxy on ->content - $this->assertInstanceOf( - Content::class, - $content = $location->getContent() - ); - $this->assertEquals(4, $content->contentInfo->id); - } - - public function testLoadLocationPrioritizedLanguagesFallback() - { - $repository = $this->getRepository(); - - // Add a language - $this->createLanguage('nor-NO', 'Norsk'); - - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - $location = $locationService->loadLocation(5); - - // Translate "Users" - $draft = $contentService->createContentDraft($location->contentInfo); - $struct = $contentService->newContentUpdateStruct(); - $struct->setField('name', 'Brukere', 'nor-NO'); - $draft = $contentService->updateContent($draft->getVersionInfo(), $struct); - $contentService->publishVersion($draft->getVersionInfo()); - - // Load with priority language (fallback will be the old one) - $location = $locationService->loadLocation(5, ['nor-NO']); - - $this->assertInstanceOf( - Location::class, - $location - ); - self::assertEquals(5, $location->id); - $this->assertInstanceOf( - Content::class, - $content = $location->getContent() - ); - $this->assertEquals(4, $content->contentInfo->id); - - $this->assertEquals($content->getVersionInfo()->getName(), 'Brukere'); - $this->assertEquals($content->getVersionInfo()->getName('eng-US'), 'Users'); - } - - /** - * Test that accessing lazy-loaded Content without a translation in the specific - * not available language throws NotFoundException. - */ - public function testLoadLocationThrowsNotFoundExceptionForNotAvailableContent(): void - { - $repository = $this->getRepository(); - - $locationService = $repository->getLocationService(); - - $this->createLanguage('pol-PL', 'Polski'); - - $this->expectException(NotFoundException::class); - - // Note: relying on existing database fixtures to make test case more readable - $locationService->loadLocation(60, ['pol-PL']); - } - - /** - * Test for the loadLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::loadLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testCreateLocation - */ - public function testLoadLocationThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - $nonExistentLocationId = $this->generateId('location', 2342); - /* BEGIN: Use Case */ - $locationService = $repository->getLocationService(); - - // Throws exception, if Location with $nonExistentLocationId does not - // exist - $location = $locationService->loadLocation($nonExistentLocationId); - /* END: Use Case */ - } - - /** - * Test for the loadLocationList() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::loadLocationList - */ - public function testLoadLocationList(): void - { - $repository = $this->getRepository(); - - // 5 is the ID of an existing location, 442 is a non-existing id - $locationService = $repository->getLocationService(); - $locations = $locationService->loadLocationList([5, 442]); - - self::assertIsIterable($locations); - self::assertCount(1, $locations); - self::assertEquals([5], array_keys($locations)); - self::assertInstanceOf(Location::class, $locations[5]); - self::assertEquals(5, $locations[5]->id); - } - - /** - * Test for the loadLocationList() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::loadLocationList - * @depends testLoadLocationList - */ - public function testLoadLocationListPrioritizedLanguagesFallback(): void - { - $repository = $this->getRepository(); - - $this->createLanguage('pol-PL', 'Polski'); - - // 5 is the ID of an existing location, 442 is a non-existing id - $locationService = $repository->getLocationService(); - $locations = $locationService->loadLocationList([5, 442], ['pol-PL'], false); - - self::assertIsIterable($locations); - self::assertCount(0, $locations); - } - - /** - * Test for the loadLocationList() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::loadLocationList - * @depends testLoadLocationListPrioritizedLanguagesFallback - */ - public function testLoadLocationListPrioritizedLanguagesFallbackAndAlwaysAvailable(): void - { - $repository = $this->getRepository(); - - $this->createLanguage('pol-PL', 'Polski'); - - // 5 is the ID of an existing location, 442 is a non-existing id - $locationService = $repository->getLocationService(); - $locations = $locationService->loadLocationList([5, 442], ['pol-PL'], true); - - self::assertIsIterable($locations); - self::assertCount(1, $locations); - self::assertEquals([5], array_keys($locations)); - self::assertInstanceOf(Location::class, $locations[5]); - self::assertEquals(5, $locations[5]->id); - } - - /** - * Test for the loadLocationList() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::loadLocationList - */ - public function testLoadLocationListWithRootLocationId() - { - $repository = $this->getRepository(); - - // 1 is the ID of an root location - $locationService = $repository->getLocationService(); - $locations = $locationService->loadLocationList([1]); - - self::assertIsIterable($locations); - self::assertCount(1, $locations); - self::assertEquals([1], array_keys($locations)); - self::assertInstanceOf(Location::class, $locations[1]); - self::assertEquals(1, $locations[1]->id); - } - - /** - * Test for the loadLocationList() method. - * - * Ensures the list is returned in the same order as passed IDs array. - * - * @covers \eZ\Publish\API\Repository\LocationService::loadLocationList - */ - public function testLoadLocationListInCorrectOrder() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $cachedLocationId = 2; - $locationIdsToLoad = [43, $cachedLocationId, 5]; - - // Call loadLocation to cache it in memory as it might possibly affect list order - $locationService->loadLocation($cachedLocationId); - - $locations = $locationService->loadLocationList($locationIdsToLoad); - $locationIds = array_column($locations, 'id'); - - self::assertEquals($locationIdsToLoad, $locationIds); - } - - /** - * Test for the loadLocationByRemoteId() method. - * - * @see \eZ\Publish\API\Repository\LocationService::loadLocationByRemoteId() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testLoadLocationByRemoteId() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocationByRemoteId( - '3f6d92f8044aed134f32153517850f5a' - ); - /* END: Use Case */ - - $this->assertEquals( - $locationService->loadLocation($this->generateId('location', 5)), - $location - ); - } - - /** - * Test for the loadLocationByRemoteId() method. - * - * @see \eZ\Publish\API\Repository\LocationService::loadLocationByRemoteId() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testLoadLocationByRemoteIdThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $locationService = $repository->getLocationService(); - - // Throws exception, since Location with remote ID does not exist - $location = $locationService->loadLocationByRemoteId( - 'not-exists' - ); - /* END: Use Case */ - } - - /** - * Test for the loadLocations() method. - * - * @see \eZ\Publish\API\Repository\LocationService::loadLocations() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testCreateLocation - */ - public function testLoadLocations() - { - $repository = $this->getRepository(); - - $contentId = $this->generateId('object', 4); - /* BEGIN: Use Case */ - // $contentId contains the ID of an existing content object - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - - $contentInfo = $contentService->loadContentInfo($contentId); - - $locations = $locationService->loadLocations($contentInfo); - /* END: Use Case */ - - $this->assertIsArray($locations); - self::assertNotEmpty($locations); - - foreach ($locations as $location) { - self::assertInstanceOf(Location::class, $location); - self::assertEquals($contentInfo->id, $location->getContentInfo()->id); - } - - return $locations; - } - - /** - * Test for the loadLocations() method. - * - * @see \eZ\Publish\API\Repository\LocationService::loadLocations() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocations - */ - public function testLoadLocationsContent(array $locations) - { - $this->assertCount(1, $locations); - foreach ($locations as $loadedLocation) { - self::assertInstanceOf(Location::class, $loadedLocation); - } - - usort( - $locations, - static function ($a, $b) { - return strcmp($a->id, $b->id); - } - ); - - $this->assertEquals( - [$this->generateId('location', 5)], - array_map( - static function (Location $location) { - return $location->id; - }, - $locations - ) - ); - } - - /** - * Test for the loadLocations() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\Location[] - * - * @see \eZ\Publish\API\Repository\LocationService::loadLocations($contentInfo, $rootLocation) - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocations - */ - public function testLoadLocationsLimitedSubtree() - { - $repository = $this->getRepository(); - - $originalLocationId = $this->generateId('location', 54); - $originalParentLocationId = $this->generateId('location', 48); - $newParentLocationId = $this->generateId('location', 43); - /* BEGIN: Use Case */ - // $originalLocationId is the ID of an existing location - // $originalParentLocationId is the ID of the parent location of - // $originalLocationId - // $newParentLocationId is the ID of an existing location outside the tree - // of $originalLocationId and $originalParentLocationId - $locationService = $repository->getLocationService(); - - // Location at "/1/48/54" - $originalLocation = $locationService->loadLocation($originalLocationId); - - // Create location under "/1/43/" - $locationCreate = $locationService->newLocationCreateStruct($newParentLocationId); - $locationService->createLocation( - $originalLocation->contentInfo, - $locationCreate - ); - - $findRootLocation = $locationService->loadLocation($originalParentLocationId); - - // Returns an array with only $originalLocation - $locations = $locationService->loadLocations( - $originalLocation->contentInfo, - $findRootLocation - ); - /* END: Use Case */ - - $this->assertIsArray($locations); - - return $locations; - } - - /** - * Test for the loadLocations() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location[] $locations - * - * @see \eZ\Publish\API\Repository\LocationService::loadLocations() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationsLimitedSubtree - */ - public function testLoadLocationsLimitedSubtreeContent(array $locations) - { - $this->assertCount(1, $locations); - - $this->assertEquals( - $this->generateId('location', 54), - reset($locations)->id - ); - } - - /** - * Test for the loadLocations() method. - * - * @see \eZ\Publish\API\Repository\LocationService::loadLocations() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocations - */ - public function testLoadLocationsThrowsBadStateException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - - // Create new content, which is not published - $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); - $contentCreate = $contentService->newContentCreateStruct($folderType, 'eng-US'); - $contentCreate->setField('name', 'New Folder'); - $content = $contentService->createContent($contentCreate); - - // Throws Exception, since $content has no published version, yet - $locationService->loadLocations( - $content->contentInfo - ); - /* END: Use Case */ - } - - /** - * Test for the loadLocations() method. - * - * @see \eZ\Publish\API\Repository\LocationService::loadLocations($contentInfo, $rootLocation) - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocations - */ - public function testLoadLocationsThrowsBadStateExceptionLimitedSubtree() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class); - - $repository = $this->getRepository(); - - $someLocationId = $this->generateId('location', 2); - /* BEGIN: Use Case */ - // $someLocationId is the ID of an existing location - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - - // Create new content, which is not published - $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); - $contentCreate = $contentService->newContentCreateStruct($folderType, 'eng-US'); - $contentCreate->setField('name', 'New Folder'); - $content = $contentService->createContent($contentCreate); - - $findRootLocation = $locationService->loadLocation($someLocationId); - - // Throws Exception, since $content has no published version, yet - $locationService->loadLocations( - $content->contentInfo, - $findRootLocation - ); - /* END: Use Case */ - } - - /** - * Test for the loadLocationChildren() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::loadLocationChildren - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testLoadLocationChildren() - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $locationId is the ID of an existing location - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation($locationId); - - $childLocations = $locationService->loadLocationChildren($location); - /* END: Use Case */ - - $this->assertInstanceOf(LocationList::class, $childLocations); - $this->assertIsArray($childLocations->locations); - $this->assertNotEmpty($childLocations->locations); - $this->assertIsInt($childLocations->totalCount); - - foreach ($childLocations->locations as $childLocation) { - $this->assertInstanceOf(Location::class, $childLocation); - $this->assertEquals($location->id, $childLocation->parentLocationId); - } - - return $childLocations; - } - - /** - * Test loading parent Locations for draft Content. - * - * @covers \eZ\Publish\API\Repository\LocationService::loadParentLocationsForDraftContent - */ - public function testLoadParentLocationsForDraftContent() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - - // prepare locations - $locationCreateStructs = [ - $locationService->newLocationCreateStruct(2), - $locationService->newLocationCreateStruct(5), - ]; - - // Create new content - $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); - $contentCreate = $contentService->newContentCreateStruct($folderType, 'eng-US'); - $contentCreate->setField('name', 'New Folder'); - $contentDraft = $contentService->createContent($contentCreate, $locationCreateStructs); - - // Test loading parent Locations - $locations = $locationService->loadParentLocationsForDraftContent($contentDraft->versionInfo); - - self::assertCount(2, $locations); - foreach ($locations as $location) { - // test it is one of the given parent locations - self::assertTrue($location->id === 2 || $location->id === 5); - } - - return $contentDraft; - } - - /** - * Test that trying to load parent Locations throws Exception if Content is not a draft. - * - * @depends testLoadParentLocationsForDraftContent - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $contentDraft - */ - public function testLoadParentLocationsForDraftContentThrowsBadStateException(Content $contentDraft) - { - $this->expectException(BadStateException::class); - $this->expectExceptionMessageMatches('/is already published/'); - - $repository = $this->getRepository(false); - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - - $content = $contentService->publishVersion($contentDraft->versionInfo); - - $locationService->loadParentLocationsForDraftContent($content->versionInfo); - } - - /** - * Test for the getLocationChildCount() method. - * - * @see \eZ\Publish\API\Repository\LocationService::getLocationChildCount() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testGetLocationChildCount() - { - // $locationId is the ID of an existing location - $locationService = $this->getRepository()->getLocationService(); - - $this->assertSame( - 5, - $locationService->getLocationChildCount( - $locationService->loadLocation($this->generateId('location', 5)) - ) - ); - } - - /** - * Test for the loadLocationChildren() method. - * - * @see \eZ\Publish\API\Repository\LocationService::loadLocationChildren() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationChildren - */ - public function testLoadLocationChildrenData(LocationList $locations) - { - $this->assertCount(5, $locations->locations); - $this->assertEquals(5, $locations->getTotalCount()); - - foreach ($locations->locations as $location) { - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\Location', - $location - ); - } - - $this->assertEquals( - [ - $this->generateId('location', 12), - $this->generateId('location', 13), - $this->generateId('location', 14), - $this->generateId('location', 44), - $this->generateId('location', 61), - ], - array_map( - static function (Location $location) { - return $location->id; - }, - $locations->locations - ) - ); - } - - /** - * @covers \eZ\Publish\API\Repository\LocationService::loadLocationChildren - */ - public function testLoadLocationChildrenWithOffset(): LocationList - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - // $locationId is the ID of an existing location - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation($locationId); - - $childLocations = $locationService->loadLocationChildren($location, 2); - - self::assertIsIterable($childLocations); - self::assertIsInt($childLocations->totalCount); - - return $childLocations; - } - - /** - * Test for the loadLocationChildren() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\LocationList $locations - * - * @covers \eZ\Publish\API\Repository\LocationService::loadLocationChildren - * - * @depends testLoadLocationChildrenWithOffset - */ - public function testLoadLocationChildrenDataWithOffset(LocationList $locations): void - { - self::assertCount(3, $locations->locations); - self::assertEquals(5, $locations->getTotalCount()); - - $actualLocationIds = []; - foreach ($locations->locations as $location) { - self::assertInstanceOf(Location::class, $location); - $actualLocationIds[] = $location->id; - } - - self::assertEquals( - [ - $this->generateId('location', 14), - $this->generateId('location', 44), - $this->generateId('location', 61), - ], - $actualLocationIds - ); - } - - /** - * Test for the loadLocationChildren() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::loadLocationChildren - * - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationChildren - */ - public function testLoadLocationChildrenWithOffsetAndLimit(): LocationList - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - // $locationId is the ID of an existing location - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation($locationId); - - $childLocations = $locationService->loadLocationChildren($location, 2, 2); - - $this->assertIsArray($childLocations->locations); - $this->assertIsInt($childLocations->totalCount); - - return $childLocations; - } - - /** - * @covers \eZ\Publish\API\Repository\LocationService::loadLocationChildren - * - * @depends testLoadLocationChildrenWithOffsetAndLimit - */ - public function testLoadLocationChildrenDataWithOffsetAndLimit(LocationList $locations): void - { - $this->assertCount(2, $locations->locations); - $this->assertEquals(5, $locations->getTotalCount()); - - $actualLocationIds = []; - foreach ($locations->locations as $location) { - self::assertInstanceOf(Location::class, $location); - $actualLocationIds[] = $location->id; - } - - $this->assertEquals( - [ - $this->generateId('location', 14), - $this->generateId('location', 44), - ], - $actualLocationIds - ); - } - - public function providerForLoadLocationChildrenRespectsParentSortingClauses(): iterable - { - yield 'Name_ASC' => [ - Location::SORT_FIELD_NAME, - Location::SORT_ORDER_ASC, - ['A', 'B', 'C', 'Test'], - ]; - - yield 'Name_DESC' => [ - Location::SORT_FIELD_NAME, - Location::SORT_ORDER_DESC, - ['Test', 'C', 'B', 'A'], - ]; - - yield 'Priority_ASC' => [ - Location::SORT_FIELD_PRIORITY, - Location::SORT_ORDER_ASC, - ['A', 'C', 'B', 'Test'], - ]; - - yield 'Priority_DESC' => [ - Location::SORT_FIELD_PRIORITY, - Location::SORT_ORDER_DESC, - ['Test', 'B', 'C', 'A'], - ]; - - yield 'Path_ASC' => [ - Location::SORT_FIELD_PATH, - Location::SORT_ORDER_ASC, - ['A', 'C', 'B', 'Test'], - ]; - - yield 'Path_DESC' => [ - Location::SORT_FIELD_PATH, - Location::SORT_ORDER_DESC, - ['Test', 'B', 'C', 'A'], - ]; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\Exception - */ - private function createStructureForTestLoadLocationChildrenRespectsParentSortingClauses(): Location - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - - // Firstly, create a container folder - $rootLocation = $locationService->loadLocation(1); - $createStruct = $contentService->newContentCreateStruct( - $contentTypeService->loadContentTypeByIdentifier('folder'), - 'eng-GB' - ); - $createStruct->setField('name', 'Parent folder'); - $content = $contentService->publishVersion( - $contentService->createContent( - $createStruct, - [$locationService->newLocationCreateStruct($rootLocation->id)] - )->versionInfo - ); - - // Secondly, create child folders that would be sorted later on - $contentNames = ['A', 'C', 'B', 'Test']; - $priority = 1; - foreach ($contentNames as $contentName) { - $rootLocation = $locationService->loadLocation($content->contentInfo->mainLocationId); - $createStruct = $contentService->newContentCreateStruct( - $contentTypeService->loadContentTypeByIdentifier('folder'), - 'eng-GB' - ); - $createStruct->setField('name', $contentName); - - $locationCreateStruct = $locationService->newLocationCreateStruct($rootLocation->id); - $locationCreateStruct->priority = $priority; - $contentService->publishVersion( - $contentService->createContent( - $createStruct, - [$locationCreateStruct] - )->versionInfo - ); - - ++$priority; - } - - $location = $locationService->loadLocation($content->contentInfo->mainLocationId); - $childrenLocations = $locationService->loadLocationChildren($location); - - self::assertCount(count($contentNames), $childrenLocations); - - return $location; - } - - /** - * @covers \eZ\Publish\API\Repository\LocationService::loadLocationChildren - * - * @dataProvider providerForLoadLocationChildrenRespectsParentSortingClauses - * - * @throws \eZ\Publish\API\Repository\Exceptions\Exception - */ - public function testLoadLocationChildrenRespectsParentSortingClauses( - int $sortField, - int $sortOrder, - array $expectedChildrenNames - ): void { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $location = $this->createStructureForTestLoadLocationChildrenRespectsParentSortingClauses(); - - // Update Location in order to change sort clause - $locationUpdateStruct = $locationService->newLocationUpdateStruct(); - $locationUpdateStruct->sortField = $sortField; - $locationUpdateStruct->sortOrder = $sortOrder; - $location = $locationService->updateLocation( - $location, - $locationUpdateStruct - ); - - $childrenNames = array_map( - static function (Location $location) { - return $location->getContentInfo()->name; - }, - iterator_to_array($locationService->loadLocationChildren($location)) - ); - - self::assertSame($expectedChildrenNames, $childrenNames); - } - - /** - * Test for the newLocationUpdateStruct() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::newLocationUpdateStruct - */ - public function testNewLocationUpdateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $locationService = $repository->getLocationService(); - - $updateStruct = $locationService->newLocationUpdateStruct(); - /* END: Use Case */ - - $this->assertInstanceOf( - LocationUpdateStruct::class, - $updateStruct - ); - - $this->assertPropertiesCorrect( - [ - 'priority' => null, - 'remoteId' => null, - 'sortField' => null, - 'sortOrder' => null, - ], - $updateStruct - ); - } - - /** - * Test for the updateLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::updateLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testUpdateLocation() - { - $repository = $this->getRepository(); - - $originalLocationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $originalLocationId is the ID of an existing location - $locationService = $repository->getLocationService(); - - $originalLocation = $locationService->loadLocation($originalLocationId); - - $updateStruct = $locationService->newLocationUpdateStruct(); - $updateStruct->priority = 3; - $updateStruct->remoteId = 'c7adcbf1e96bc29bca28c2d809d0c7ef69272651'; - $updateStruct->sortField = Location::SORT_FIELD_PRIORITY; - $updateStruct->sortOrder = Location::SORT_ORDER_DESC; - - $updatedLocation = $locationService->updateLocation($originalLocation, $updateStruct); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\Location', - $updatedLocation - ); - - return [ - 'originalLocation' => $originalLocation, - 'updateStruct' => $updateStruct, - 'updatedLocation' => $updatedLocation, - ]; - } - - /** - * Test for the updateLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::updateLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testUpdateLocation - */ - public function testUpdateLocationStructValues(array $data) - { - $originalLocation = $data['originalLocation']; - $updateStruct = $data['updateStruct']; - $updatedLocation = $data['updatedLocation']; - - $this->assertPropertiesCorrect( - [ - 'id' => $originalLocation->id, - 'priority' => $updateStruct->priority, - 'hidden' => $originalLocation->hidden, - 'invisible' => $originalLocation->invisible, - 'remoteId' => $updateStruct->remoteId, - 'contentInfo' => $originalLocation->contentInfo, - 'parentLocationId' => $originalLocation->parentLocationId, - 'pathString' => $originalLocation->pathString, - 'depth' => $originalLocation->depth, - 'sortField' => $updateStruct->sortField, - 'sortOrder' => $updateStruct->sortOrder, - ], - $updatedLocation - ); - } - - /** - * Test for the updateLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::updateLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testUpdateLocationWithSameRemoteId() - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $locationId and remote ID is the IDs of the same, existing location - $locationService = $repository->getLocationService(); - - $originalLocation = $locationService->loadLocation($locationId); - - $updateStruct = $locationService->newLocationUpdateStruct(); - - // Remote ID of an existing location with the same locationId - $updateStruct->remoteId = $originalLocation->remoteId; - - // Sets one of the properties to be able to confirm location gets updated, here: priority - $updateStruct->priority = 2; - - $location = $locationService->updateLocation($originalLocation, $updateStruct); - - // Checks that the location was updated - $this->assertEquals(2, $location->priority); - - // Checks that remoteId remains the same - $this->assertEquals($originalLocation->remoteId, $location->remoteId); - /* END: Use Case */ - } - - /** - * Test for the updateLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::updateLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testUpdateLocationThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $locationId and remoteId is the IDs of an existing, but not the same, location - $locationService = $repository->getLocationService(); - - $originalLocation = $locationService->loadLocation($locationId); - - $updateStruct = $locationService->newLocationUpdateStruct(); - - // Remote ID of an existing location with a different locationId - $updateStruct->remoteId = 'f3e90596361e31d496d4026eb624c983'; - - // Throws exception, since remote ID is already taken - $locationService->updateLocation($originalLocation, $updateStruct); - /* END: Use Case */ - } - - /** - * Test for the updateLocation() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::updateLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - * @dataProvider dataProviderForOutOfRangeLocationPriority - */ - public function testUpdateLocationThrowsInvalidArgumentExceptionPriorityIsOutOfRange($priority) - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $locationId and remoteId is the IDs of an existing, but not the same, location - $locationService = $repository->getLocationService(); - - $originalLocation = $locationService->loadLocation($locationId); - - $updateStruct = $locationService->newLocationUpdateStruct(); - - // Priority value is out of range - $updateStruct->priority = $priority; - - // Throws exception, since remote ID is already taken - $locationService->updateLocation($originalLocation, $updateStruct); - /* END: Use Case */ - } - - /** - * Test for the updateLocation() method. - * Ref EZP-23302: Update Location fails if no change is performed with the update. - * - * @see \eZ\Publish\API\Repository\LocationService::updateLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testUpdateLocationTwice() - { - $repository = $this->getRepository(); - $permissionResolver = $repository->getPermissionResolver(); - - $locationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - $locationService = $repository->getLocationService(); - $permissionResolver->setCurrentUserReference($repository->getUserService()->loadUser(14)); - - $originalLocation = $locationService->loadLocation($locationId); - - $updateStruct = $locationService->newLocationUpdateStruct(); - $updateStruct->priority = 42; - - $updatedLocation = $locationService->updateLocation($originalLocation, $updateStruct); - - // Repeated update with the same, unchanged struct - $secondUpdatedLocation = $locationService->updateLocation($updatedLocation, $updateStruct); - /* END: Use Case */ - - $this->assertEquals($updatedLocation->priority, 42); - $this->assertEquals($secondUpdatedLocation->priority, 42); - } - - /** - * Test for the swapLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::swapLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testSwapLocation() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $mediaLocationId = $this->generateId('location', 43); - $demoDesignLocationId = $this->generateId('location', 56); - - $mediaContentInfo = $locationService->loadLocation($mediaLocationId)->getContentInfo(); - $demoDesignContentInfo = $locationService->loadLocation($demoDesignLocationId)->getContentInfo(); - - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation - - // $demoDesignLocationId is the ID of the "Demo Design" page location in an eZ - // Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - $mediaLocation = $locationService->loadLocation($mediaLocationId); - $demoDesignLocation = $locationService->loadLocation($demoDesignLocationId); - - // Swaps the content referred to by the locations - $locationService->swapLocation($mediaLocation, $demoDesignLocation); - /* END: Use Case */ - - // Reload Locations, IDs swapped - $demoDesignLocation = $locationService->loadLocation($mediaLocationId); - $mediaLocation = $locationService->loadLocation($demoDesignLocationId); - - // Assert Location's Content is updated - $this->assertEquals( - $mediaContentInfo->id, - $mediaLocation->getContentInfo()->id - ); - $this->assertEquals( - $demoDesignContentInfo->id, - $demoDesignLocation->getContentInfo()->id - ); - - // Assert URL aliases are updated - $this->assertEquals( - $mediaLocation->id, - $repository->getURLAliasService()->lookup('/Design/Media')->destination - ); - $this->assertEquals( - $demoDesignLocation->id, - $repository->getURLAliasService()->lookup('/eZ-Publish-Demo-Design-without-demo-content')->destination - ); - } - - /** - * Test for the swapLocation() method with custom aliases. - * - * @covers \eZ\Publish\API\Repository\LocationService::swapLocation - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testSwapLocationForContentWithCustomUrlAliases(): void - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - $urlAliasService = $repository->getURLAliasService(); - $this->createLanguage('pol-PL', 'Polski'); - - $folder1 = $this->createFolder(['eng-GB' => 'Folder1', 'pol-PL' => 'Folder1'], 2); - $folder2 = $this->createFolder(['eng-GB' => 'Folder2'], 2); - $location1 = $locationService->loadLocation($folder1->contentInfo->mainLocationId); - $location2 = $locationService->loadLocation($folder2->contentInfo->mainLocationId); - - $urlAlias = $urlAliasService->createUrlAlias($location1, '/custom-location1', 'eng-GB', false, true); - $urlAliasService->createUrlAlias($location1, '/custom-location1', 'pol-PL', false, true); - $urlAliasService->createUrlAlias($location2, '/custom-location2', 'eng-GB', false, true); - $location1UrlAliases = $urlAliasService->listLocationAliases($location1); - $location2UrlAliases = $urlAliasService->listLocationAliases($location2); - - $locationService->swapLocation($location1, $location2); - $location1 = $locationService->loadLocation($location1->contentInfo->mainLocationId); - $location2 = $locationService->loadLocation($location2->contentInfo->mainLocationId); - - $location1UrlAliasesAfterSwap = $urlAliasService->listLocationAliases($location1); - $location2UrlAliasesAfterSwap = $urlAliasService->listLocationAliases($location2); - - $keyUrlAlias = array_search($urlAlias->id, array_column($location1UrlAliasesAfterSwap, 'id')); - - self::assertEquals($folder1->id, $location2->contentInfo->id); - self::assertEquals($folder2->id, $location1->contentInfo->id); - self::assertNotEquals($location1UrlAliases, $location1UrlAliasesAfterSwap); - self::assertEquals($location2UrlAliases, $location2UrlAliasesAfterSwap); - self::assertEquals(['eng-GB'], $location1UrlAliasesAfterSwap[$keyUrlAlias]->languageCodes); - } - - /** - * Test swapping secondary Location with main Location. - * - * @covers \eZ\Publish\API\Repository\LocationService::swapLocation - * - * @see https://jira.ez.no/browse/EZP-28663 - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * - * @return int[] - */ - public function testSwapLocationForMainAndSecondaryLocation(): array - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - - $folder1 = $this->createFolder(['eng-GB' => 'Folder1'], 2); - $folder2 = $this->createFolder(['eng-GB' => 'Folder2'], 2); - $folder3 = $this->createFolder(['eng-GB' => 'Folder3'], 2); - - $primaryLocation = $locationService->loadLocation($folder1->contentInfo->mainLocationId); - $parentLocation = $locationService->loadLocation($folder2->contentInfo->mainLocationId); - $secondaryLocation = $locationService->createLocation( - $folder1->contentInfo, - $locationService->newLocationCreateStruct($parentLocation->id) - ); - - $targetLocation = $locationService->loadLocation($folder3->contentInfo->mainLocationId); - - // perform sanity checks - $this->assertContentHasExpectedLocations([$primaryLocation, $secondaryLocation], $folder1); - - // begin use case - $locationService->swapLocation($secondaryLocation, $targetLocation); - - // test results - $primaryLocation = $locationService->loadLocation($primaryLocation->id); - $secondaryLocation = $locationService->loadLocation($secondaryLocation->id); - $targetLocation = $locationService->loadLocation($targetLocation->id); - - self::assertEquals($folder1->id, $primaryLocation->contentInfo->id); - self::assertEquals($folder1->id, $targetLocation->contentInfo->id); - self::assertEquals($folder3->id, $secondaryLocation->contentInfo->id); - - $this->assertContentHasExpectedLocations([$primaryLocation, $targetLocation], $folder1); - - self::assertEquals( - $folder1, - $contentService->loadContent($folder1->id, Language::ALL) - ); - - self::assertEquals( - $folder2, - $contentService->loadContent($folder2->id, Language::ALL) - ); - - // only in case of Folder 3, main location id changed due to swap - self::assertEquals( - $secondaryLocation->id, - $contentService->loadContent($folder3->id)->contentInfo->mainLocationId - ); - - return [$folder1, $folder2, $folder3]; - } - - /** - * Compare Ids of expected and loaded Locations for the given Content. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location[] $expectedLocations - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - */ - private function assertContentHasExpectedLocations(array $expectedLocations, Content $content) - { - $repository = $this->getRepository(false); - $locationService = $repository->getLocationService(); - - $expectedLocationIds = array_map( - static function (Location $location) { - return (int)$location->id; - }, - $expectedLocations - ); - - $actualLocationsIds = array_map( - static function (Location $location) { - return $location->id; - }, - $locationService->loadLocations($content->contentInfo) - ); - self::assertCount(count($expectedLocations), $actualLocationsIds); - - // perform unordered equality assertion - self::assertEqualsCanonicalizing( - $expectedLocationIds, - $actualLocationsIds, - sprintf( - 'Content %d contains Locations %s, not expected Locations: %s', - $content->id, - implode(', ', $actualLocationsIds), - implode(', ', $expectedLocationIds) - ) - ); - } - - /** - * @depends testSwapLocationForMainAndSecondaryLocation - * - * @param \eZ\Publish\API\Repository\Values\Content\Content[] $contentItems Content items created by testSwapLocationForSecondaryLocation - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function testSwapLocationDoesNotCorruptSearchResults(array $contentItems) - { - $repository = $this->getRepository(false); - $searchService = $repository->getSearchService(); - - $this->refreshSearch($repository); - - $contentIds = array_map( - static function (Content $content) { - return $content->id; - }, - $contentItems - ); - - $query = new Query(); - $query->filter = new Query\Criterion\ContentId($contentIds); - - $searchResult = $searchService->findContent($query); - - self::assertEquals(count($contentItems), $searchResult->totalCount); - self::assertEquals( - $searchResult->totalCount, - count($searchResult->searchHits), - 'Total count of search result hits does not match the actual number of found results' - ); - $foundContentIds = array_map( - static function (SearchHit $searchHit) { - return $searchHit->valueObject->id; - }, - $searchResult->searchHits - ); - sort($contentIds); - sort($foundContentIds); - self::assertSame( - $contentIds, - $foundContentIds, - 'Got different than expected Content item Ids' - ); - } - - /** - * Test swapping two secondary (non-main) Locations. - * - * @covers \eZ\Publish\API\Repository\LocationService::swapLocation - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testSwapLocationForSecondaryLocations() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - - $folder1 = $this->createFolder(['eng-GB' => 'Folder1'], 2); - $folder2 = $this->createFolder(['eng-GB' => 'Folder2'], 2); - $parentFolder1 = $this->createFolder(['eng-GB' => 'Parent1'], 2); - $parentFolder2 = $this->createFolder(['eng-GB' => 'Parent2'], 2); - - $parentLocation1 = $locationService->loadLocation($parentFolder1->contentInfo->mainLocationId); - $parentLocation2 = $locationService->loadLocation($parentFolder2->contentInfo->mainLocationId); - $secondaryLocation1 = $locationService->createLocation( - $folder1->contentInfo, - $locationService->newLocationCreateStruct($parentLocation1->id) - ); - $secondaryLocation2 = $locationService->createLocation( - $folder2->contentInfo, - $locationService->newLocationCreateStruct($parentLocation2->id) - ); - - // begin use case - $locationService->swapLocation($secondaryLocation1, $secondaryLocation2); - - // test results - $secondaryLocation1 = $locationService->loadLocation($secondaryLocation1->id); - $secondaryLocation2 = $locationService->loadLocation($secondaryLocation2->id); - - self::assertEquals($folder2->id, $secondaryLocation1->contentInfo->id); - self::assertEquals($folder1->id, $secondaryLocation2->contentInfo->id); - - self::assertEquals( - $folder1, - $contentService->loadContent($folder1->id, Language::ALL) - ); - - self::assertEquals( - $folder2, - $contentService->loadContent($folder2->id, Language::ALL) - ); - } - - /** - * Test swapping Main Location of a Content with another one updates Content item Main Location. - * - * @covers \eZ\Publish\API\Repository\LocationService::swapLocation - */ - public function testSwapLocationUpdatesMainLocation() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - - $mainLocationParentId = 60; - $secondaryLocationId = 43; - - $publishedContent = $this->publishContentWithParentLocation( - 'Content for Swap Location Test', - $mainLocationParentId - ); - - // sanity check - $mainLocation = $locationService->loadLocation($publishedContent->contentInfo->mainLocationId); - self::assertEquals($mainLocationParentId, $mainLocation->parentLocationId); - - // load another pre-existing location - $secondaryLocation = $locationService->loadLocation($secondaryLocationId); - - // swap the Main Location with a secondary one - $locationService->swapLocation($mainLocation, $secondaryLocation); - - // check if Main Location has been updated - $mainLocation = $locationService->loadLocation($secondaryLocation->id); - self::assertEquals($publishedContent->contentInfo->id, $mainLocation->contentInfo->id); - self::assertEquals($mainLocation->id, $mainLocation->contentInfo->mainLocationId); - - $reloadedContent = $contentService->loadContentByContentInfo($publishedContent->contentInfo); - self::assertEquals($mainLocation->id, $reloadedContent->contentInfo->mainLocationId); - } - - /** - * Test if location swap affects related bookmarks. - * - * @covers \eZ\Publish\API\Repository\LocationService::swapLocation - */ - public function testBookmarksAreSwappedAfterSwapLocation() - { - $repository = $this->getRepository(); - - $mediaLocationId = $this->generateId('location', 43); - $demoDesignLocationId = $this->generateId('location', 56); - - /* BEGIN: Use Case */ - $locationService = $repository->getLocationService(); - $bookmarkService = $repository->getBookmarkService(); - - $mediaLocation = $locationService->loadLocation($mediaLocationId); - $demoDesignLocation = $locationService->loadLocation($demoDesignLocationId); - - // Bookmark locations - $bookmarkService->createBookmark($mediaLocation); - $bookmarkService->createBookmark($demoDesignLocation); - - $beforeSwap = $bookmarkService->loadBookmarks(); - - // Swaps the content referred to by the locations - $locationService->swapLocation($mediaLocation, $demoDesignLocation); - - $afterSwap = $bookmarkService->loadBookmarks(); - /* END: Use Case */ - - $this->assertEquals($beforeSwap->items[0]->id, $afterSwap->items[1]->id); - $this->assertEquals($beforeSwap->items[1]->id, $afterSwap->items[0]->id); - } - - /** - * Test for the hideLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::hideLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testHideLocation() - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $locationId is the ID of an existing location - $locationService = $repository->getLocationService(); - - $visibleLocation = $locationService->loadLocation($locationId); - - $hiddenLocation = $locationService->hideLocation($visibleLocation); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\Location', - $hiddenLocation - ); - - $this->assertTrue( - $hiddenLocation->hidden, - sprintf( - 'Location with ID "%s" is not hidden.', - $hiddenLocation->id - ) - ); - - $this->refreshSearch($repository); - - foreach ($locationService->loadLocationChildren($hiddenLocation)->locations as $child) { - $this->assertSubtreeProperties( - ['invisible' => true], - $child - ); - } - } - - /** - * Assert that $expectedValues are set in the subtree starting at $location. - * - * @param array $expectedValues - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - */ - protected function assertSubtreeProperties(array $expectedValues, Location $location, $stopId = null) - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - if ($location->id === $stopId) { - return; - } - - foreach ($expectedValues as $propertyName => $propertyValue) { - $this->assertEquals( - $propertyValue, - $location->$propertyName - ); - - foreach ($locationService->loadLocationChildren($location)->locations as $child) { - $this->assertSubtreeProperties($expectedValues, $child); - } - } - } - - /** - * Test for the unhideLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::unhideLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testHideLocation - */ - public function testUnhideLocation() - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $locationId is the ID of an existing location - $locationService = $repository->getLocationService(); - - $visibleLocation = $locationService->loadLocation($locationId); - $hiddenLocation = $locationService->hideLocation($visibleLocation); - - $unHiddenLocation = $locationService->unhideLocation($hiddenLocation); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\Location', - $unHiddenLocation - ); - - $this->assertFalse( - $unHiddenLocation->hidden, - sprintf( - 'Location with ID "%s" is hidden.', - $unHiddenLocation->id - ) - ); - - $this->refreshSearch($repository); - - foreach ($locationService->loadLocationChildren($unHiddenLocation)->locations as $child) { - $this->assertSubtreeProperties( - ['invisible' => false], - $child - ); - } - } - - /** - * Test for the unhideLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::unhideLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testUnhideLocation - */ - public function testUnhideLocationNotUnhidesHiddenSubtree() - { - $repository = $this->getRepository(); - - $higherLocationId = $this->generateId('location', 5); - $lowerLocationId = $this->generateId('location', 13); - /* BEGIN: Use Case */ - // $higherLocationId is the ID of a location - // $lowerLocationId is the ID of a location below $higherLocationId - $locationService = $repository->getLocationService(); - - $higherLocation = $locationService->loadLocation($higherLocationId); - $hiddenHigherLocation = $locationService->hideLocation($higherLocation); - - $lowerLocation = $locationService->loadLocation($lowerLocationId); - $hiddenLowerLocation = $locationService->hideLocation($lowerLocation); - - $unHiddenHigherLocation = $locationService->unhideLocation($hiddenHigherLocation); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\Location', - $unHiddenHigherLocation - ); - - $this->assertFalse( - $unHiddenHigherLocation->hidden, - sprintf( - 'Location with ID "%s" is hidden.', - $unHiddenHigherLocation->id - ) - ); - - $this->refreshSearch($repository); - - foreach ($locationService->loadLocationChildren($unHiddenHigherLocation)->locations as $child) { - $this->assertSubtreeProperties( - ['invisible' => false], - $child, - $this->generateId('location', 13) - ); - } - - $stillHiddenLocation = $locationService->loadLocation($this->generateId('location', 13)); - $this->assertTrue( - $stillHiddenLocation->hidden, - sprintf( - 'Hidden sub-location with ID %s unhidden unexpectedly.', - $stillHiddenLocation->id - ) - ); - foreach ($locationService->loadLocationChildren($stillHiddenLocation)->locations as $child) { - $this->assertSubtreeProperties( - ['invisible' => true], - $child - ); - } - } - - /** - * Test for the deleteLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::deleteLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testDeleteLocation() - { - $repository = $this->getRepository(); - - $mediaLocationId = $this->generateId('location', 43); - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the location of the - // "Media" location in an eZ Publish demo installation - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation($mediaLocationId); - - $locationService->deleteLocation($location); - /* END: Use Case */ - - try { - $locationService->loadLocation($mediaLocationId); - $this->fail("Location $mediaLocationId not deleted."); - } catch (NotFoundException $e) { - } - - // The following IDs are IDs of child locations of $mediaLocationId location - // ( Media/Images, Media/Files, Media/Multimedia respectively ) - foreach ([51, 52, 53] as $childLocationId) { - try { - $locationService->loadLocation($this->generateId('location', $childLocationId)); - $this->fail("Location $childLocationId not deleted."); - } catch (NotFoundException $e) { - } - } - - // The following IDs are IDs of content below $mediaLocationId location - // ( Media/Images, Media/Files, Media/Multimedia respectively ) - $contentService = $this->getRepository()->getContentService(); - foreach ([49, 50, 51] as $childContentId) { - try { - $contentService->loadContentInfo($this->generateId('object', $childContentId)); - $this->fail("Content $childContentId not deleted."); - } catch (NotFoundException $e) { - } - } - } - - /** - * Test for the deleteLocation() method. - * - * @see \eZ\Publish\API\Repository\LocationService::deleteLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testDeleteLocation - */ - public function testDeleteLocationDecrementsChildCountOnParent() - { - $repository = $this->getRepository(); - - $mediaLocationId = $this->generateId('location', 43); - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the location of the - // "Media" location in an eZ Publish demo installation - - $locationService = $repository->getLocationService(); - - // Load the current the user group location - $location = $locationService->loadLocation($mediaLocationId); - - // Load the parent location - $parentLocation = $locationService->loadLocation( - $location->parentLocationId - ); - - // Get child count - $childCountBefore = $locationService->getLocationChildCount($parentLocation); - - // Delete the user group location - $locationService->deleteLocation($location); - - $this->refreshSearch($repository); - - // Reload parent location - $parentLocation = $locationService->loadLocation( - $location->parentLocationId - ); - - // This will be $childCountBefore - 1 - $childCountAfter = $locationService->getLocationChildCount($parentLocation); - /* END: Use Case */ - - $this->assertEquals($childCountBefore - 1, $childCountAfter); - } - - /** - * Test for the deleteLocation() method. - * - * Related issue: EZP-21904 - * - * @see \eZ\Publish\API\Repository\LocationService::deleteLocation() - */ - public function testDeleteContentObjectLastLocation() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use case */ - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - $contentTypeService = $repository->getContentTypeService(); - $urlAliasService = $repository->getURLAliasService(); - - // prepare Content object - $createStruct = $contentService->newContentCreateStruct( - $contentTypeService->loadContentTypeByIdentifier('folder'), - 'eng-GB' - ); - $createStruct->setField('name', 'Test folder'); - - // creata Content object - $content = $contentService->publishVersion( - $contentService->createContent( - $createStruct, - [$locationService->newLocationCreateStruct(2)] - )->versionInfo - ); - - // delete location - $locationService->deleteLocation( - $locationService->loadLocation( - $urlAliasService->lookup('/Test-folder')->destination - ) - ); - - // this should throw a not found exception - $contentService->loadContent($content->versionInfo->contentInfo->id); - /* END: Use case*/ - } - - /** - * Test for the deleteLocation() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::deleteLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testDeleteLocation - */ - public function testDeleteLocationDeletesRelatedBookmarks() - { - $repository = $this->getRepository(); - - $parentLocationId = $this->generateId('location', 43); - $childLocationId = $this->generateId('location', 53); - - /* BEGIN: Use Case */ - $locationService = $repository->getLocationService(); - $bookmarkService = $repository->getBookmarkService(); - - // Load location - $childLocation = $locationService->loadLocation($childLocationId); - // Add location to bookmarks - $bookmarkService->createBookmark($childLocation); - // Load parent location - $parentLocation = $locationService->loadLocation($parentLocationId); - // Delete parent location - $locationService->deleteLocation($parentLocation); - /* END: Use Case */ - - // Location isn't bookmarked anymore - foreach ($bookmarkService->loadBookmarks(0, 9999) as $bookmarkedLocation) { - $this->assertNotEquals($childLocation->id, $bookmarkedLocation->id); - } - } - - /** - * @covers \eZ\Publish\API\Repository\LocationService::deleteLocation - */ - public function testDeleteUnusedLocationWhichPreviousHadContentWithRelativeAlias(): void - { - $repository = $this->getRepository(false); - - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - $urlAliasService = $repository->getURLAliasService(); - - $originalFolder = $this->createFolder(['eng-GB' => 'Original folder'], 2); - $newFolder = $this->createFolder(['eng-GB' => 'New folder'], 2); - $originalFolderLocationId = $originalFolder->contentInfo->mainLocationId; - - $forum = $contentService->publishVersion( - $contentService->createContent( - $this->createForumStruct('Some forum'), - [ - $locationService->newLocationCreateStruct($originalFolderLocationId), - ] - )->versionInfo - ); - - $forumMainLocation = $locationService->loadLocation( - $forum->contentInfo->mainLocationId - ); - - $customRelativeAliasPath = '/Original-folder/some-forum-alias'; - - $urlAliasService->createUrlAlias( - $forumMainLocation, - $customRelativeAliasPath, - 'eng-GB', - true, - true - ); - - $locationService->moveSubtree( - $forumMainLocation, - $locationService->loadLocation( - $newFolder->contentInfo->mainLocationId - ) - ); - - $this->assertAliasExists( - $customRelativeAliasPath, - $forumMainLocation, - $urlAliasService - ); - - $urlAliasService->lookup($customRelativeAliasPath); - - $locationService->deleteLocation( - $locationService->loadLocation( - $originalFolder->contentInfo->mainLocationId - ) - ); - - $this->assertAliasExists( - $customRelativeAliasPath, - $forumMainLocation, - $urlAliasService - ); - - $urlAliasService->lookup($customRelativeAliasPath); - } - - /** - * Test for the copySubtree() method. - * - * @see \eZ\Publish\API\Repository\LocationService::copySubtree() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testCopySubtree() - { - $repository = $this->getRepository(); - - $mediaLocationId = $this->generateId('location', 43); - $demoDesignLocationId = $this->generateId('location', 56); - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation - - // $demoDesignLocationId is the ID of the "Demo Design" page location in an eZ - // Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - // Load location to copy - $locationToCopy = $locationService->loadLocation($mediaLocationId); - - // Load new parent location - $newParentLocation = $locationService->loadLocation($demoDesignLocationId); - - // Copy location "Media" to "Demo Design" - $copiedLocation = $locationService->copySubtree( - $locationToCopy, - $newParentLocation - ); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\Location', - $copiedLocation - ); - - $this->assertPropertiesCorrect( - [ - 'depth' => $newParentLocation->depth + 1, - 'parentLocationId' => $newParentLocation->id, - 'pathString' => $newParentLocation->pathString . $this->parseId('location', $copiedLocation->id) . '/', - ], - $copiedLocation - ); - - $this->assertDefaultContentStates($copiedLocation->contentInfo); - } - - /** - * Test for the copySubtree() method. - * - * @see \eZ\Publish\API\Repository\LocationService::copySubtree() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testCopySubtreeWithAliases() - { - $repository = $this->getRepository(); - $urlAliasService = $repository->getURLAliasService(); - - // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation - - // $demoDesignLocationId is the ID of the "Demo Design" page location in an eZ - // Publish demo installation - $mediaLocationId = $this->generateId('location', 43); - $demoDesignLocationId = $this->generateId('location', 56); - - $locationService = $repository->getLocationService(); - $locationToCopy = $locationService->loadLocation($mediaLocationId); - $newParentLocation = $locationService->loadLocation($demoDesignLocationId); - - $expectedSubItemAliases = [ - '/Design/Plain-site/Media/Multimedia', - '/Design/Plain-site/Media/Images', - '/Design/Plain-site/Media/Files', - ]; - - $this->assertAliasesBeforeCopy($urlAliasService, $expectedSubItemAliases); - - // Copy location "Media" to "Design" - $locationService->copySubtree( - $locationToCopy, - $newParentLocation - ); - - $this->assertGeneratedAliases($urlAliasService, $expectedSubItemAliases); - } - - /** - * @covers \eZ\Publish\API\Repository\LocationService::copySubtree - */ - public function testCopySubtreeWithTranslatedContent(): void - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - $locationService = $repository->getLocationService(); - $urlAliasService = $repository->getURLAliasService(); - - $mediaLocationId = $this->generateId('location', 43); - $filesLocationId = $this->generateId('location', 52); - $demoDesignLocationId = $this->generateId('location', 56); - - $locationToCopy = $locationService->loadLocation($mediaLocationId); - $filesLocation = $locationService->loadLocation($filesLocationId); - $newParentLocation = $locationService->loadLocation($demoDesignLocationId); - - // translating the 'middle' folder - $translatedDraft = $contentService->createContentDraft($filesLocation->contentInfo); - $contentUpdateStruct = new ContentUpdateStruct([ - 'initialLanguageCode' => 'ger-DE', - 'fields' => $translatedDraft->getFields(), - ]); - $contentUpdateStruct->setField('short_name', 'FilesGER', 'ger-DE'); - $translatedContent = $contentService->updateContent($translatedDraft->versionInfo, $contentUpdateStruct); - $contentService->publishVersion($translatedContent->versionInfo); - - // creating additional content under translated folder - $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); - $contentCreate = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $contentCreate->setField('name', 'My folder'); - $content = $contentService->createContent( - $contentCreate, - [new LocationCreateStruct(['parentLocationId' => $filesLocationId])] - ); - $contentService->publishVersion($content->versionInfo); - - $expectedSubItemAliases = [ - '/Design/Plain-site/Media/Multimedia', - '/Design/Plain-site/Media/Images', - '/Design/Plain-site/Media/Files', - '/Design/Plain-site/Media/Files/my-folder', - ]; - - $this->assertAliasesBeforeCopy($urlAliasService, $expectedSubItemAliases); - - $locationService->copySubtree( - $locationToCopy, - $newParentLocation - ); - - $this->assertGeneratedAliases($urlAliasService, $expectedSubItemAliases); - } - - /** - * Asserts that given Content has default ContentStates. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - */ - private function assertDefaultContentStates(ContentInfo $contentInfo) - { - $repository = $this->getRepository(); - $objectStateService = $repository->getObjectStateService(); - - $objectStateGroups = $objectStateService->loadObjectStateGroups(); - - foreach ($objectStateGroups as $objectStateGroup) { - $contentState = $objectStateService->getContentState($contentInfo, $objectStateGroup); - foreach ($objectStateService->loadObjectStates($objectStateGroup, Language::ALL) as $objectState) { - // Only check the first object state which is the default one. - $this->assertEquals( - $objectState, - $contentState - ); - break; - } - } - } - - /** - * Test for the copySubtree() method. - * - * @see \eZ\Publish\API\Repository\LocationService::copySubtree() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testCopySubtree - */ - public function testCopySubtreeUpdatesSubtreeProperties() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $locationToCopy = $locationService->loadLocation($this->generateId('location', 43)); - - // Load Subtree properties before copy - $expected = $this->loadSubtreeProperties($locationToCopy); - - $mediaLocationId = $this->generateId('location', 43); - $demoDesignLocationId = $this->generateId('location', 56); - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation - - // $demoDesignLocationId is the ID of the "Demo Design" page location in an eZ - // Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - // Load location to copy - $locationToCopy = $locationService->loadLocation($mediaLocationId); - - // Load new parent location - $newParentLocation = $locationService->loadLocation($demoDesignLocationId); - - // Copy location "Media" to "Demo Design" - $copiedLocation = $locationService->copySubtree( - $locationToCopy, - $newParentLocation - ); - /* END: Use Case */ - - $beforeIds = []; - foreach ($expected as $properties) { - $beforeIds[] = $properties['id']; - } - - $this->refreshSearch($repository); - - // Load Subtree properties after copy - $actual = $this->loadSubtreeProperties($copiedLocation); - - $this->assertEquals(count($expected), count($actual)); - - foreach ($actual as $properties) { - $this->assertNotContains($properties['id'], $beforeIds); - $this->assertStringStartsWith( - $newParentLocation->pathString . $this->parseId('location', $copiedLocation->id) . '/', - $properties['pathString'] - ); - $this->assertStringEndsWith( - '/' . $this->parseId('location', $properties['id']) . '/', - $properties['pathString'] - ); - } - } - - /** - * Test for the copySubtree() method. - * - * @see \eZ\Publish\API\Repository\LocationService::copySubtree() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testCopySubtree - */ - public function testCopySubtreeIncrementsChildCountOfNewParent() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $childCountBefore = $locationService->getLocationChildCount($locationService->loadLocation(56)); - - $mediaLocationId = $this->generateId('location', 43); - $demoDesignLocationId = $this->generateId('location', 56); - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation - - // $demoDesignLocationId is the ID of the "Demo Design" page location in an eZ - // Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - // Load location to copy - $locationToCopy = $locationService->loadLocation($mediaLocationId); - - // Load new parent location - $newParentLocation = $locationService->loadLocation($demoDesignLocationId); - - // Copy location "Media" to "Demo Design" - $copiedLocation = $locationService->copySubtree( - $locationToCopy, - $newParentLocation - ); - /* END: Use Case */ - - $this->refreshSearch($repository); - - $childCountAfter = $locationService->getLocationChildCount($locationService->loadLocation($demoDesignLocationId)); - - $this->assertEquals($childCountBefore + 1, $childCountAfter); - } - - /** - * @covers \eZ\Publish\API\Repository\LocationService::copySubtree() - */ - public function testCopySubtreeWithInvisibleChild(): void - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - // Hide child Location - $locationService->hideLocation($locationService->loadLocation($this->generateId('location', 53))); - - $this->refreshSearch($repository); - - $locationToCopy = $locationService->loadLocation($this->generateId('location', 43)); - - $expected = $this->loadSubtreeProperties($locationToCopy); - - $mediaLocationId = $this->generateId('location', 43); - $demoDesignLocationId = $this->generateId('location', 56); - $locationService = $repository->getLocationService(); - - $locationToCopy = $locationService->loadLocation($mediaLocationId); - - $newParentLocation = $locationService->loadLocation($demoDesignLocationId); - - $copiedLocation = $locationService->copySubtree( - $locationToCopy, - $newParentLocation - ); - - $this->refreshSearch($repository); - - // Load Subtree properties after copy - $actual = $this->loadSubtreeProperties($copiedLocation); - - self::assertEquals(count($expected), count($actual)); - - foreach ($actual as $key => $properties) { - self::assertEquals($expected[$key]['hidden'], $properties['hidden']); - self::assertEquals($expected[$key]['invisible'], $properties['invisible']); - } - } - - /** - * Test for the copySubtree() method. - * - * @see \eZ\Publish\API\Repository\LocationService::copySubtree() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testCopySubtree - */ - public function testCopySubtreeThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $communityLocationId = $this->generateId('location', 5); - /* BEGIN: Use Case */ - // $communityLocationId is the ID of the "Community" page location in - // an eZ Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - // Load location to copy - $locationToCopy = $locationService->loadLocation($communityLocationId); - - // Use a child as new parent - $childLocations = $locationService->loadLocationChildren($locationToCopy)->locations; - $newParentLocation = end($childLocations); - - // This call will fail with an "InvalidArgumentException", because the - // new parent is a child location of the subtree to copy. - $locationService->copySubtree( - $locationToCopy, - $newParentLocation - ); - /* END: Use Case */ - } - - /** - * Test for the moveSubtree() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::moveSubtree - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testMoveSubtree(): void - { - $repository = $this->getRepository(); - - $mediaLocationId = $this->generateId('location', 43); - $demoDesignLocationId = $this->generateId('location', 56); - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation - - // $demoDesignLocationId is the ID of the "Demo Design" page location in an eZ - // Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - // Load location to move - $locationToMove = $locationService->loadLocation($demoDesignLocationId); - - // Load new parent location - $newParentLocation = $locationService->loadLocation($mediaLocationId); - - // Move location from "Home" to "Media" - $locationService->moveSubtree( - $locationToMove, - $newParentLocation - ); - - // Load moved location - $movedLocation = $locationService->loadLocation($demoDesignLocationId); - /* END: Use Case */ - - $this->assertPropertiesCorrect( - [ - 'hidden' => false, - 'invisible' => false, - 'depth' => $newParentLocation->depth + 1, - 'parentLocationId' => $newParentLocation->id, - 'pathString' => $newParentLocation->pathString . $this->parseId('location', $movedLocation->id) . '/', - ], - $movedLocation - ); - } - - /** - * Test for the moveSubtree() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::moveSubtree - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testMoveSubtreeToLocationWithoutContent(): void - { - $repository = $this->getRepository(); - - $rootLocationId = $this->generateId('location', 1); - $demoDesignLocationId = $this->generateId('location', 56); - $locationService = $repository->getLocationService(); - $locationToMove = $locationService->loadLocation($demoDesignLocationId); - $newParentLocation = $locationService->loadLocation($rootLocationId); - - $locationService->moveSubtree( - $locationToMove, - $newParentLocation - ); - - $movedLocation = $locationService->loadLocation($demoDesignLocationId); - - $this->assertPropertiesCorrect( - [ - 'hidden' => false, - 'invisible' => false, - 'depth' => $newParentLocation->depth + 1, - 'parentLocationId' => $newParentLocation->id, - 'pathString' => $newParentLocation->pathString . $this->parseId('location', $movedLocation->id) . '/', - ], - $movedLocation - ); - } - - /** - * Test for the moveSubtree() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::moveSubtree - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testMoveSubtreeThrowsExceptionOnMoveNotIntoContainer(): void - { - $repository = $this->getRepository(); - - $mediaLocationId = $this->generateId('location', 43); - $demoDesignLocationId = $this->generateId('location', 56); - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation - - // $demoDesignLocationId is the ID of the "Demo Design" page location in an eZ - // Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - // Load location to move - $locationToMove = $locationService->loadLocation($mediaLocationId); - - // Load new parent location - $newParentLocation = $locationService->loadLocation($demoDesignLocationId); - - // Move location from "Home" to "Demo Design" (not container) - $this->expectException(InvalidArgumentException::class); - $locationService->moveSubtree($locationToMove, $newParentLocation); - } - - /** - * Test for the moveSubtree() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::moveSubtree - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation - */ - public function testMoveSubtreeThrowsExceptionOnMoveToSame(): void - { - $repository = $this->getRepository(); - - $mediaLocationId = $this->generateId('location', 43); - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - // Load location to move - $locationToMove = $locationService->loadLocation($mediaLocationId); - - // Load parent location - $newParentLocation = $locationService->loadLocation($locationToMove->parentLocationId); - - // Move location from "Home" to "Home" - $this->expectException(InvalidArgumentException::class); - $locationService->moveSubtree($locationToMove, $newParentLocation); - } - - /** - * Test for the moveSubtree() method. - * - * @covers \eZ\Publish\API\Repository\LocationService::moveSubtree - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testMoveSubtree - */ - public function testMoveSubtreeHidden(): void - { - $repository = $this->getRepository(); - - $mediaLocationId = $this->generateId('location', 43); - $demoDesignLocationId = $this->generateId('location', 56); - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation - - // $demoDesignLocationId is the ID of the "Demo Design" page location in an eZ - // Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - // Load location to move - $locationToMove = $locationService->loadLocation($demoDesignLocationId); - - // Load new parent location - $newParentLocation = $locationService->loadLocation($mediaLocationId); - - // Hide the target location before we move - $newParentLocation = $locationService->hideLocation($newParentLocation); - - // Move location from "Demo Design" to "Home" - $locationService->moveSubtree( - $locationToMove, - $newParentLocation - ); - - // Load moved location - $movedLocation = $locationService->loadLocation($demoDesignLocationId); - /* END: Use Case */ - - $this->assertPropertiesCorrect( - [ - 'hidden' => false, - 'invisible' => true, - 'depth' => $newParentLocation->depth + 1, - 'parentLocationId' => $newParentLocation->id, - 'pathString' => $newParentLocation->pathString . $this->parseId('location', $movedLocation->id) . '/', - ], - $movedLocation - ); - } - - /** - * Test for the moveSubtree() method. - * - * @see \eZ\Publish\API\Repository\LocationService::moveSubtree() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testMoveSubtree - */ - public function testMoveSubtreeUpdatesSubtreeProperties() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $locationToMove = $locationService->loadLocation($this->generateId('location', 56)); - $newParentLocation = $locationService->loadLocation($this->generateId('location', 43)); - - // Load Subtree properties before move - $expected = $this->loadSubtreeProperties($locationToMove); - foreach ($expected as $id => $properties) { - $expected[$id]['depth'] = $properties['depth'] + 2; - $expected[$id]['pathString'] = str_replace( - $locationToMove->pathString, - $newParentLocation->pathString . $this->parseId('location', $locationToMove->id) . '/', - $properties['pathString'] - ); - } - - $mediaLocationId = $this->generateId('location', 43); - $demoDesignLocationId = $this->generateId('location', 56); - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation - - // $demoDesignLocationId is the ID of the "Demo Design" page location in an eZ - // Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - // Load location to move - $locationToMove = $locationService->loadLocation($demoDesignLocationId); - - // Load new parent location - $newParentLocation = $locationService->loadLocation($mediaLocationId); - - // Move location from "Demo Design" to "Home" - $locationService->moveSubtree( - $locationToMove, - $newParentLocation - ); - - // Load moved location - $movedLocation = $locationService->loadLocation($demoDesignLocationId); - /* END: Use Case */ - - $this->refreshSearch($repository); - - // Load Subtree properties after move - $actual = $this->loadSubtreeProperties($movedLocation); - - $this->assertEquals($expected, $actual); - } - - /** - * Test for the moveSubtree() method. - * - * @see \eZ\Publish\API\Repository\LocationService::moveSubtree() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testMoveSubtreeUpdatesSubtreeProperties - */ - public function testMoveSubtreeUpdatesSubtreePropertiesHidden() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $locationToMove = $locationService->loadLocation($this->generateId('location', 2)); - $newParentLocation = $locationService->loadLocation($this->generateId('location', 43)); - - // Hide the target location before we move - $newParentLocation = $locationService->hideLocation($newParentLocation); - - // Load Subtree properties before move - $expected = $this->loadSubtreeProperties($locationToMove); - foreach ($expected as $id => $properties) { - $expected[$id]['invisible'] = true; - $expected[$id]['depth'] = $properties['depth'] + 1; - $expected[$id]['pathString'] = str_replace( - $locationToMove->pathString, - $newParentLocation->pathString . $this->parseId('location', $locationToMove->id) . '/', - $properties['pathString'] - ); - } - - $homeLocationId = $this->generateId('location', 2); - $mediaLocationId = $this->generateId('location', 43); - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation - - // $homeLocationId is the ID of the "Home" page location in an eZ - // Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - // Load location to move - $locationToMove = $locationService->loadLocation($homeLocationId); - - // Load new parent location - $newParentLocation = $locationService->loadLocation($mediaLocationId); - - // Move location from "Home" to "Demo Design" - $locationService->moveSubtree( - $locationToMove, - $newParentLocation - ); - - // Load moved location - $movedLocation = $locationService->loadLocation($homeLocationId); - /* END: Use Case */ - - $this->refreshSearch($repository); - - // Load Subtree properties after move - $actual = $this->loadSubtreeProperties($movedLocation); - - $this->assertEquals($expected, $actual); - } - - /** - * Test for the moveSubtree() method. - * - * @see \eZ\Publish\API\Repository\LocationService::moveSubtree() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testMoveSubtree - */ - public function testMoveSubtreeIncrementsChildCountOfNewParent() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $newParentLocation = $locationService->loadLocation($this->generateId('location', 43)); - - // Load expected properties before move - $expected = $this->loadLocationProperties($newParentLocation); - $childCountBefore = $locationService->getLocationChildCount($newParentLocation); - - $mediaLocationId = $this->generateId('location', 43); - $demoDesignLocationId = $this->generateId('location', 56); - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation - - // $demoDesignLocationId is the ID of the "Demo Design" page location in an eZ - // Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - // Load location to move - $locationToMove = $locationService->loadLocation($demoDesignLocationId); - - // Load new parent location - $newParentLocation = $locationService->loadLocation($mediaLocationId); - - // Move location from "Demo Design" to "Home" - $locationService->moveSubtree( - $locationToMove, - $newParentLocation - ); - - // Load moved location - $movedLocation = $locationService->loadLocation($demoDesignLocationId); - - // Reload new parent location - $newParentLocation = $locationService->loadLocation($mediaLocationId); - /* END: Use Case */ - - $this->refreshSearch($repository); - - // Load Subtree properties after move - $actual = $this->loadLocationProperties($newParentLocation); - $childCountAfter = $locationService->getLocationChildCount($newParentLocation); - - $this->assertEquals($expected, $actual); - $this->assertEquals($childCountBefore + 1, $childCountAfter); - } - - /** - * Test for the moveSubtree() method. - * - * @see \eZ\Publish\API\Repository\LocationService::moveSubtree() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testMoveSubtree - */ - public function testMoveSubtreeDecrementsChildCountOfOldParent() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $oldParentLocation = $locationService->loadLocation($this->generateId('location', 1)); - - // Load expected properties before move - $expected = $this->loadLocationProperties($oldParentLocation); - $childCountBefore = $locationService->getLocationChildCount($oldParentLocation); - - $homeLocationId = $this->generateId('location', 2); - $mediaLocationId = $this->generateId('location', 43); - /* BEGIN: Use Case */ - // $homeLocationId is the ID of the "Home" page location in - // an eZ Publish demo installation - - // $mediaLocationId is the ID of the "Media" page location in an eZ - // Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - // Load location to move - $locationToMove = $locationService->loadLocation($mediaLocationId); - - // Get the location id of the old parent - $oldParentLocationId = $locationToMove->parentLocationId; - - // Load new parent location - $newParentLocation = $locationService->loadLocation($homeLocationId); - - // Move location from "Demo Design" to "Home" - $locationService->moveSubtree( - $locationToMove, - $newParentLocation - ); - - // Reload old parent location - $oldParentLocation = $locationService->loadLocation($oldParentLocationId); - /* END: Use Case */ - - $this->refreshSearch($repository); - - // Load Subtree properties after move - $actual = $this->loadLocationProperties($oldParentLocation); - $childCountAfter = $locationService->getLocationChildCount($oldParentLocation); - - $this->assertEquals($expected, $actual); - $this->assertEquals($childCountBefore - 1, $childCountAfter); - } - - /** - * Test moving invisible (hidden by parent) subtree. - * - * @covers \eZ\Publish\API\Repository\LocationService::moveSubtree - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testMoveInvisibleSubtree() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $rootLocationId = 2; - - $folder = $this->createFolder(['eng-GB' => 'Folder'], $rootLocationId); - $child = $this->createFolder(['eng-GB' => 'Child'], $folder->contentInfo->mainLocationId); - $locationService->hideLocation( - $locationService->loadLocation($folder->contentInfo->mainLocationId) - ); - // sanity check - $childLocation = $locationService->loadLocation($child->contentInfo->mainLocationId); - self::assertFalse($childLocation->hidden); - self::assertTrue($childLocation->invisible); - self::assertEquals($folder->contentInfo->mainLocationId, $childLocation->parentLocationId); - - $destination = $this->createFolder(['eng-GB' => 'Destination'], $rootLocationId); - $destinationLocation = $locationService->loadLocation( - $destination->contentInfo->mainLocationId - ); - - $locationService->moveSubtree($childLocation, $destinationLocation); - - $childLocation = $locationService->loadLocation($child->contentInfo->mainLocationId); - // Business logic - Location moved to visible parent becomes visible - self::assertFalse($childLocation->hidden); - self::assertFalse($childLocation->invisible); - self::assertEquals($destinationLocation->id, $childLocation->parentLocationId); - } - - /** - * Test for the moveSubtree() method. - * - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testMoveSubtree - */ - public function testMoveSubtreeThrowsInvalidArgumentException(): void - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - $mediaLocationId = $this->generateId('location', 43); - $multimediaLocationId = $this->generateId('location', 53); - - /* BEGIN: Use Case */ - // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation - - // $multimediaLocationId is the ID of the "Multimedia" page location in an eZ - // Publish demo installation - - // Load the location service - $locationService = $repository->getLocationService(); - - // Load location to move - $locationToMove = $locationService->loadLocation($mediaLocationId); - - // Load new parent location - $newParentLocation = $locationService->loadLocation($multimediaLocationId); - - // Throws an exception because new parent location is placed below location to move - $this->expectException(InvalidArgumentException::class); - $locationService->moveSubtree( - $locationToMove, - $newParentLocation - ); - /* END: Use Case */ - } - - /** - * Test that Legacy ezcontentobject_tree.path_identification_string field is correctly updated - * after moving subtree. - * - * @covers \eZ\Publish\API\Repository\LocationService::moveSubtree - * - * @throws \ErrorException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testMoveSubtreeUpdatesPathIdentificationString(): void - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $topNode = $this->createFolder(['eng-US' => 'top_node'], 2); - - $newParentLocation = $locationService->loadLocation( - $this - ->createFolder(['eng-US' => 'Parent'], $topNode->contentInfo->mainLocationId) - ->contentInfo - ->mainLocationId - ); - $location = $locationService->loadLocation( - $this - ->createFolder(['eng-US' => 'Move Me'], $topNode->contentInfo->mainLocationId) - ->contentInfo - ->mainLocationId - ); - - $locationService->moveSubtree($location, $newParentLocation); - - // path location string is not present on API level, so we need to query database - $serviceContainer = $this->getSetupFactory()->getServiceContainer(); - /** @var \Doctrine\DBAL\Connection $connection */ - $connection = $serviceContainer->get('ezpublish.persistence.connection'); - $query = $connection->createQueryBuilder(); - $query - ->select('path_identification_string') - ->from('ezcontentobject_tree') - ->where('node_id = :nodeId') - ->setParameter('nodeId', $location->id); - - self::assertEquals( - 'top_node/parent/move_me', - $query->execute()->fetchColumn() - ); - } - - /** - * Test that is_visible is set correct for children when moving a location where a child is hidden by content (not by location). - * - * @covers \eZ\Publish\API\Repository\LocationService::moveSubtree - */ - public function testMoveSubtreeKeepsContentHiddenOnChildrenAndParent(): void - { - $repository = $this->getRepository(); - - $mediaLocationId = $this->generateId('location', 43); - - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - - $sourceFolderContent = $this->publishContentWithParentLocation('SourceFolder', $mediaLocationId); // media/SourceFolder - $subFolderContent1 = $this->publishContentWithParentLocation('subFolderContent1', $sourceFolderContent->contentInfo->mainLocationId); - $subFolderContent2 = $this->publishContentWithParentLocation('subFolderContent2', $sourceFolderContent->contentInfo->mainLocationId); - $targetFolderContent = $this->publishContentWithParentLocation('targetFolder', $mediaLocationId); // media/TargetFolder - - $contentService->hideContent($subFolderContent1->contentInfo); - - // Move media/SourceFolder to media/TargetFolder/ - $locationService->moveSubtree( - $sourceFolderContent->contentInfo->getMainLocation(), - $targetFolderContent->contentInfo->getMainLocation() - ); - - $movedLocation = $locationService->loadLocation($sourceFolderContent->contentInfo->mainLocationId); - $newParentLocation = $locationService->loadLocation($targetFolderContent->contentInfo->mainLocationId); - - // Assert Moved Location remains visible ( only child is hidden ) - $this->assertPropertiesCorrect( - [ - 'hidden' => false, - 'invisible' => false, - 'depth' => $newParentLocation->depth + 1, - 'parentLocationId' => $newParentLocation->id, - 'pathString' => $newParentLocation->pathString . $this->parseId('location', $movedLocation->id) . '/', - ], - $movedLocation - ); - self::assertFalse($movedLocation->getContentInfo()->isHidden); - - // Assert children of Moved location - $childrenLocations = [ - $subFolderContent1->contentInfo->getMainLocation(), - $subFolderContent2->contentInfo->getMainLocation(), - ]; - foreach ($childrenLocations as $childLocation) { - $this->assertPropertiesCorrect( - [ - 'hidden' => $childLocation === $subFolderContent1->contentInfo->getMainLocation(), // Only SubFolderContent1 should be hidden, - 'invisible' => $childLocation === $subFolderContent1->contentInfo->getMainLocation(), // Only SubFolderContent1 should be hidden - 'depth' => $movedLocation->depth + 1, - 'parentLocationId' => $movedLocation->id, - 'pathString' => $movedLocation->pathString . $this->parseId('location', $childLocation->id) . '/', - ], - $childLocation - ); - self::assertEquals($childLocation === $subFolderContent1->contentInfo->getMainLocation(), $childLocation->getContentInfo()->isHidden); - } - } - - /** - * Test that is_visible is set correct for children when moving a content which is hidden (location is not hidden). - * - * @covers \eZ\Publish\API\Repository\LocationService::moveSubtree - */ - public function testMoveSubtreeKeepsContentHiddenOnChildren(): void - { - $repository = $this->getRepository(); - - $sourceLocationId = $this->createFolder( - [ - 'eng-GB' => 'SourceParentFolder', - ], - 2 - )->getVersionInfo()->getContentInfo()->mainLocationId; - - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - - $sourceFolderContent = $this->publishContentWithParentLocation('SourceFolder', $sourceLocationId); // media/SourceFolder - $subFolderContent1 = $this->publishContentWithParentLocation('subFolderContent1', $sourceFolderContent->contentInfo->mainLocationId); - $subFolderContent2 = $this->publishContentWithParentLocation('subFolderContent2', $sourceFolderContent->contentInfo->mainLocationId); - $targetFolderContent = $this->publishContentWithParentLocation('targetFolder', $sourceLocationId); // media/TargetFolder - - $contentService->hideContent($sourceFolderContent->contentInfo); - - // Move media/SourceFolder to media/TargetFolder/ - $locationService->moveSubtree( - $sourceFolderContent->contentInfo->getMainLocation(), - $targetFolderContent->contentInfo->getMainLocation() - ); - - $movedLocation = $locationService->loadLocation($sourceFolderContent->contentInfo->mainLocationId); - $newParentLocation = $locationService->loadLocation($targetFolderContent->contentInfo->mainLocationId); - - // Assert Moved Location - $this->assertPropertiesCorrect( - [ - 'hidden' => true, - 'invisible' => true, - 'depth' => $newParentLocation->depth + 1, - 'parentLocationId' => $newParentLocation->id, - 'pathString' => $newParentLocation->pathString . $this->parseId('location', $movedLocation->id) . '/', - ], - $movedLocation - ); - - self::assertTrue($movedLocation->getContentInfo()->isHidden); - - // Assert children of Moved location - $childLocations = [$subFolderContent1->contentInfo->getMainLocation(), $subFolderContent2->contentInfo->getMainLocation()]; - foreach ($childLocations as $childLocation) { - $this->assertPropertiesCorrect( - [ - 'hidden' => false, - 'invisible' => true, - 'depth' => $movedLocation->depth + 1, - 'parentLocationId' => $movedLocation->id, - 'pathString' => $movedLocation->pathString . $this->parseId('location', $childLocation->id) . '/', - ], - $childLocation - ); - self::assertFalse($childLocation->getContentInfo()->isHidden); - } - } - - /** - * Test validating whether content that is being moved is still allowed to be moved when one of its locations - * is inaccessible by a current user, however, when moved location is accessible. - * - * @covers \eZ\Publish\API\Repository\LocationService::moveSubtree - * - * @throws \eZ\Publish\API\Repository\Exceptions\Exception - */ - public function testMoveSubtreeContentWithMultipleLocationsAndOneOfThemInaccessible(): void - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - $permissionResolver = $repository->getPermissionResolver(); - - $folder = $this->publishContentWithParentLocation('Parent folder', 2); - $accessibleFolder = $this->publishContentWithParentLocation('Accessible folder', 2); - $subFolder = $this->publishContentWithParentLocation( - 'Sub folder', - $folder->contentInfo->mainLocationId - ); - $contentToBeMoved = $this->publishContentWithParentLocation( - 'Target folder', - $subFolder->contentInfo->mainLocationId - ); - $forbiddenContent = $this->publishContentWithParentLocation('Forbidden folder', 2); - - // Add second location (parent 'Forbidden folder') to 'Target content' in folder that user won't have access to - $locationService->createLocation( - $contentToBeMoved->contentInfo, - $locationService->newLocationCreateStruct($forbiddenContent->contentInfo->mainLocationId) - ); - - $folderLocation = $locationService->loadLocation($folder->contentInfo->mainLocationId); - $accessibleFolderLocation = $locationService->loadLocation($accessibleFolder->contentInfo->mainLocationId); - - // Set user that cannot access 'Forbidden folder' - $user = $this->createUserWithPolicies( - 'user', - [ - ['module' => 'content', 'function' => 'read'], - ['module' => 'content', 'function' => 'create'], - ], - new SubtreeLimitation( - ['limitationValues' => [ - $folderLocation->getPathString(), - $accessibleFolderLocation->getPathString(), - ], - ] - ) - ); - $permissionResolver->setCurrentUserReference($user); - - // Move Parent folder/Sub folder/Target folder to location of ID = 2 - $locationService->moveSubtree( - $contentToBeMoved->contentInfo->getMainLocation(), - $accessibleFolderLocation - ); - - $targetContentMainLocation = $locationService->loadLocation($contentToBeMoved->contentInfo->mainLocationId); - - self::assertSame($targetContentMainLocation->parentLocationId, $accessibleFolderLocation->id); - } - - public function testGetSubtreeSize(): Location - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $folder = $this->createFolder(['eng-GB' => 'Parent Folder'], 2); - $location = $folder->getVersionInfo()->getContentInfo()->getMainLocation(); - self::assertSame(1, $locationService->getSubtreeSize($location)); - - $this->createFolder(['eng-GB' => 'Child 1'], $location->id); - $this->createFolder(['eng-GB' => 'Child 2'], $location->id); - - self::assertSame(3, $locationService->getSubtreeSize($location)); - - return $location; - } - - /** - * Loads properties from all locations in the $location's subtree. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param array $properties - * - * @return array - */ - private function loadSubtreeProperties(Location $location, array $properties = []) - { - $locationService = $this->getRepository()->getLocationService(); - - foreach ($locationService->loadLocationChildren($location)->locations as $childLocation) { - $properties[] = $this->loadLocationProperties($childLocation); - - $properties = $this->loadSubtreeProperties($childLocation, $properties); - } - - return $properties; - } - - /** - * Loads assertable properties from the given location. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param mixed[] $overwrite - * - * @return array - */ - private function loadLocationProperties(Location $location, array $overwrite = []) - { - return array_merge( - [ - 'id' => $location->id, - 'depth' => $location->depth, - 'parentLocationId' => $location->parentLocationId, - 'pathString' => $location->pathString, - 'remoteId' => $location->remoteId, - 'hidden' => $location->hidden, - 'invisible' => $location->invisible, - 'priority' => $location->priority, - 'sortField' => $location->sortField, - 'sortOrder' => $location->sortOrder, - ], - $overwrite - ); - } - - /** - * Assert generated aliases to expected alias return. - * - * @param \eZ\Publish\API\Repository\URLAliasService $urlAliasService - * @param array $expectedAliases - */ - protected function assertGeneratedAliases($urlAliasService, array $expectedAliases) - { - foreach ($expectedAliases as $expectedAlias) { - $urlAlias = $urlAliasService->lookup($expectedAlias); - $this->assertPropertiesCorrect(['type' => 0], $urlAlias); - } - } - - /** - * @param \eZ\Publish\API\Repository\URLAliasService $urlAliasService - * @param array $expectedSubItemAliases - */ - private function assertAliasesBeforeCopy($urlAliasService, array $expectedSubItemAliases) - { - foreach ($expectedSubItemAliases as $aliasUrl) { - try { - $urlAliasService->lookup($aliasUrl); - $this->fail('We didn\'t expect to find alias, but it was found'); - } catch (\Exception $e) { - $this->assertTrue(true); // OK - alias was not found - } - } - } - - /** - * Create and publish Content with the given parent Location. - * - * @param string $contentName - * @param int $parentLocationId - * - * @return \eZ\Publish\API\Repository\Values\Content\Content published Content - */ - private function publishContentWithParentLocation($contentName, $parentLocationId) - { - $repository = $this->getRepository(false); - $locationService = $repository->getLocationService(); - - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - - $contentCreateStruct = $contentService->newContentCreateStruct( - $contentTypeService->loadContentTypeByIdentifier('folder'), - 'eng-US' - ); - $contentCreateStruct->setField('name', $contentName); - $contentDraft = $contentService->createContent( - $contentCreateStruct, - [ - $locationService->newLocationCreateStruct($parentLocationId), - ] - ); - - return $contentService->publishVersion($contentDraft->versionInfo); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - private function createForumStruct(string $name): ContentCreateStruct - { - $repository = $this->getRepository(false); - - $contentTypeForum = $repository->getContentTypeService() - ->loadContentTypeByIdentifier('forum'); - - $forum = $repository->getContentService() - ->newContentCreateStruct($contentTypeForum, 'eng-GB'); - - $forum->setField('name', $name); - - return $forum; - } - - private function assertAliasExists( - string $expectedAliasPath, - Location $location, - URLAliasServiceInterface $urlAliasService - ): void { - $articleAliasesBeforeDelete = $urlAliasService - ->listLocationAliases($location); - - $this->assertNotEmpty( - array_filter( - $articleAliasesBeforeDelete, - static function (URLAlias $alias) use ($expectedAliasPath) { - return $alias->path === $expectedAliasPath; - } - ) - ); - } -} diff --git a/eZ/Publish/API/Repository/Tests/NonRedundantFieldSetTest.php b/eZ/Publish/API/Repository/Tests/NonRedundantFieldSetTest.php deleted file mode 100644 index e0232a703c..0000000000 --- a/eZ/Publish/API/Repository/Tests/NonRedundantFieldSetTest.php +++ /dev/null @@ -1,789 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; - -/** - * Test case for create and update Content operations in the ContentService with regard to - * non-redundant set of fields being passed to the storage. - * - * These tests depends on TextLine field type being functional. - * - * @see eZ\Publish\API\Repository\ContentService - * @group content - */ -class NonRedundantFieldSetTest extends BaseNonRedundantFieldSetTest -{ - /** - * Test for the createContent() method. - * - * Default values are stored. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function testCreateContentDefaultValues() - { - $mainLanguageCode = 'eng-US'; - $fieldValues = [ - 'field1' => ['eng-US' => 'new value 1'], - 'field3' => ['eng-US' => 'new value 3'], - ]; - - $content = $this->createTestContent($mainLanguageCode, $fieldValues); - - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Content', $content); - - return $content; - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testCreateContentDefaultValues - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function testCreateContentDefaultValuesFields(Content $content) - { - $this->assertCount(1, $content->versionInfo->languageCodes); - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertCount(4, $content->getFields()); - - // eng-US - $this->assertEquals('new value 1', $content->getFieldValue('field1', 'eng-US')); - $this->assertEquals('default value 2', $content->getFieldValue('field2', 'eng-US')); - $this->assertEquals('new value 3', $content->getFieldValue('field3', 'eng-US')); - $this->assertEquals('default value 4', $content->getFieldValue('field4', 'eng-US')); - } - - /** - * Test for the createContent() method. - * - * Creating fields with empty values, no values being passed to storage. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function testCreateContentEmptyValues() - { - $mainLanguageCode = 'eng-US'; - $fieldValues = [ - 'field2' => ['eng-US' => null], - 'field4' => ['eng-US' => null], - ]; - - $content = $this->createTestContent($mainLanguageCode, $fieldValues); - - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Content', $content); - - return $content; - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testCreateContentEmptyValues - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function testCreateContentEmptyValuesFields(Content $content) - { - $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); - - $this->assertCount(1, $content->versionInfo->languageCodes); - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertCount(4, $content->getFields()); - - // eng-US - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field4', 'eng-US')); - } - - /** - * Test for the createContent() method. - * - * Creating fields with empty values, no values being passed to storage. - * Case where additional language is not stored. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function testCreateContentEmptyValuesTranslationNotStored() - { - $mainLanguageCode = 'eng-US'; - $fieldValues = [ - 'field2' => ['eng-US' => null], - 'field4' => ['eng-US' => null, 'ger-DE' => null], - ]; - - $content = $this->createTestContent($mainLanguageCode, $fieldValues); - - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Content', $content); - - return $content; - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testCreateContentEmptyValuesTranslationNotStored - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function testCreateContentEmptyValuesTranslationNotStoredFields(Content $content) - { - $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); - - $this->assertCount(1, $content->versionInfo->languageCodes); - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertCount(4, $content->getFields()); - - // eng-US - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field4', 'eng-US')); - - // ger-DE is not stored! - $this->assertNotContains('ger-DE', $content->versionInfo->languageCodes); - } - - /** - * Test for the createContent() method. - * - * Creating with two languages, main language is always stored (even with all values being empty). - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function testCreateContentTwoLanguagesMainTranslationStored() - { - $mainLanguageCode = 'eng-US'; - $fieldValues = [ - 'field2' => ['eng-US' => null], - 'field4' => ['eng-US' => null, 'ger-DE' => 'new ger-DE value 4'], - ]; - - $content = $this->createTestContent($mainLanguageCode, $fieldValues); - - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Content', $content); - - return $content; - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testCreateContentTwoLanguagesMainTranslationStored - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function testCreateContentTwoLanguagesMainTranslationStoredFields(Content $content) - { - $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); - - $this->assertCount(2, $content->versionInfo->languageCodes); - $this->assertContains('ger-DE', $content->versionInfo->languageCodes); - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertCount(8, $content->getFields()); - - // eng-US - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field4', 'eng-US')); - - // ger-DE - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'ger-DE')); - $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'ger-DE')); - $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'ger-DE')); - $this->assertEquals('new ger-DE value 4', $content->getFieldValue('field4', 'ger-DE')); - } - - /** - * Test for the createContent() method. - * - * Creating with two languages, second (not main one) language with empty values, causing no fields - * for it being passed to the storage. Second language will not be stored. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function testCreateContentTwoLanguagesSecondTranslationNotStored() - { - $mainLanguageCode = 'eng-US'; - $fieldValues = [ - 'field4' => ['ger-DE' => null], - ]; - - $content = $this->createTestContent($mainLanguageCode, $fieldValues); - - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Content', $content); - - return $content; - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testCreateContentTwoLanguagesSecondTranslationNotStored - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function testCreateContentTwoLanguagesSecondTranslationNotStoredFields(Content $content) - { - $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); - - $this->assertCount(1, $content->versionInfo->languageCodes); - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertCount(4, $content->getFields()); - - // eng-US - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); - $this->assertEquals('default value 2', $content->getFieldValue('field2', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'eng-US')); - $this->assertEquals('default value 4', $content->getFieldValue('field4', 'eng-US')); - - // ger-DE is not stored! - $this->assertNotContains('ger-DE', $content->versionInfo->languageCodes); - } - - /** - * Test for the createContent() method. - * - * Creating with no fields in struct, using only default values. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function testCreateContentDefaultValuesNoStructFields() - { - $mainLanguageCode = 'eng-US'; - $fieldValues = []; - - $content = $this->createTestContent($mainLanguageCode, $fieldValues); - - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Content', $content); - - return $content; - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testCreateContentDefaultValuesNoStructFields - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function testCreateContentDefaultValuesNoStructFieldsFields(Content $content) - { - $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); - - $this->assertCount(1, $content->versionInfo->languageCodes); - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertCount(4, $content->getFields()); - - // eng-US - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); - $this->assertEquals('default value 2', $content->getFieldValue('field2', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'eng-US')); - $this->assertEquals('default value 4', $content->getFieldValue('field4', 'eng-US')); - } - - /** - * Test for the createContent() method. - * - * Creating in two languages with no given field values for main language. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function testCreateContentTwoLanguagesNoValuesForMainLanguage() - { - $mainLanguageCode = 'eng-US'; - $fieldValues = [ - 'field4' => ['ger-DE' => 'new value 4'], - ]; - - $content = $this->createTestContent($mainLanguageCode, $fieldValues); - - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Content', $content); - - return $content; - } - - /** - * Test for the createContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testCreateContentTwoLanguagesNoValuesForMainLanguage - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function testCreateContentTwoLanguagesNoValuesForMainLanguageFields(Content $content) - { - $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); - - $this->assertCount(2, $content->versionInfo->languageCodes); - $this->assertContains('ger-DE', $content->versionInfo->languageCodes); - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertCount(8, $content->getFields()); - - // eng-US - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); - $this->assertEquals('default value 2', $content->getFieldValue('field2', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'eng-US')); - $this->assertEquals('default value 4', $content->getFieldValue('field4', 'eng-US')); - - // ger-DE - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'ger-DE')); - $this->assertEquals('default value 2', $content->getFieldValue('field2', 'ger-DE')); - $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'ger-DE')); - $this->assertEquals('new value 4', $content->getFieldValue('field4', 'ger-DE')); - } - - /** - * Test for the createContentDraft() method. - * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testCreateContentTwoLanguagesMainTranslationStoredFields - * - * @return \eZ\Publish\API\Repository\Values\Content\Content[] - */ - public function testCreateContentDraft() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $draft = $this->createMultilingualTestContent(); - $published = $contentService->publishVersion($draft->versionInfo); - $newDraft = $contentService->createContentDraft($published->contentInfo); - - $newDraft = $contentService->loadContent($newDraft->id, null, $newDraft->versionInfo->versionNo); - - return [$published, $newDraft]; - } - - /** - * Test for the createContentDraft() method. - * - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testCreateContentDraft - * - * @param \eZ\Publish\API\Repository\Values\Content\Content[] $data - */ - public function testCreateContentDraftFields(array $data) - { - $content = $data[1]; - - $this->assertEquals(VersionInfo::STATUS_DRAFT, $content->versionInfo->status); - $this->assertEquals(2, $content->versionInfo->versionNo); - $this->assertCount(2, $content->versionInfo->languageCodes); - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertContains('eng-GB', $content->versionInfo->languageCodes); - $this->assertCount(8, $content->getFields()); - - // eng-US - $this->assertEquals('value 1', $content->getFieldValue('field1', 'eng-US')); - $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-US')); - $this->assertEquals('value 3', $content->getFieldValue('field3', 'eng-US')); - $this->assertEquals('value 4', $content->getFieldValue('field4', 'eng-US')); - - // eng-GB - $this->assertEquals('value 1', $content->getFieldValue('field1', 'eng-GB')); - $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-GB')); - $this->assertEquals('value 3 eng-GB', $content->getFieldValue('field3', 'eng-GB')); - $this->assertEquals('value 4 eng-GB', $content->getFieldValue('field4', 'eng-GB')); - } - - /** - * Test for the createContentDraft() method. - * - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testCreateContentDraft - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testCreateContentDraftFields - * - * @param \eZ\Publish\API\Repository\Values\Content\Content[] $data - */ - public function testCreateContentDraftFieldsRetainsIds(array $data) - { - $this->assertFieldIds($data[0], $data[1]); - } - - /** - * Test for the updateContent() method. - * - * Testing update with new language: - * - value for new language is copied from value in main language - * - value for new language is empty - * - value for new language is not empty - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function testUpdateContentWithNewLanguage() - { - $initialLanguageCode = 'ger-DE'; - $fieldValues = [ - 'field4' => ['ger-DE' => 'new value 4'], - ]; - - $content = $this->updateTestContent($initialLanguageCode, $fieldValues); - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Content', $content); - - return $content; - } - - /** - * Test for the updateContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testUpdateContentWithNewLanguage - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function testUpdateContentWithNewLanguageFields(Content $content) - { - $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); - - $this->assertCount(3, $content->versionInfo->languageCodes); - $this->assertContains('ger-DE', $content->versionInfo->languageCodes); - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertContains('eng-GB', $content->versionInfo->languageCodes); - $this->assertCount(12, $content->getFields()); - - // eng-US - $this->assertEquals('value 1', $content->getFieldValue('field1', 'eng-US')); - $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-US')); - $this->assertEquals('value 3', $content->getFieldValue('field3', 'eng-US')); - $this->assertEquals('value 4', $content->getFieldValue('field4', 'eng-US')); - - // eng-GB - $this->assertEquals('value 1', $content->getFieldValue('field1', 'eng-GB')); - $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-GB')); - $this->assertEquals('value 3 eng-GB', $content->getFieldValue('field3', 'eng-GB')); - $this->assertEquals('value 4 eng-GB', $content->getFieldValue('field4', 'eng-GB')); - - // ger-DE - $this->assertEquals('value 1', $content->getFieldValue('field1', 'ger-DE')); - $this->assertEquals('value 2', $content->getFieldValue('field2', 'ger-DE')); - $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'ger-DE')); - $this->assertEquals('new value 4', $content->getFieldValue('field4', 'ger-DE')); - } - - /** - * Test for the updateContent() method. - * - * Testing update of existing language and adding a new language: - * - value for new language is copied from value in main language - * - value for new language is empty - * - value for new language is not empty - * - existing language value updated with empty value - * - existing language value not changed - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function testUpdateContentWithNewLanguageVariant() - { - $initialLanguageCode = 'ger-DE'; - $fieldValues = [ - 'field1' => ['eng-US' => null], - 'field4' => ['ger-DE' => 'new value 4'], - ]; - - $content = $this->updateTestContent($initialLanguageCode, $fieldValues); - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Content', $content); - - return $content; - } - - /** - * Test for the updateContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testUpdateContentWithNewLanguageVariant - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function testUpdateContentWithNewLanguageVariantFields(Content $content) - { - $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); - - $this->assertCount(3, $content->versionInfo->languageCodes); - $this->assertContains('ger-DE', $content->versionInfo->languageCodes); - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertContains('eng-GB', $content->versionInfo->languageCodes); - $this->assertCount(12, $content->getFields()); - - // eng-US - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); - $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-US')); - $this->assertEquals('value 3', $content->getFieldValue('field3', 'eng-US')); - $this->assertEquals('value 4', $content->getFieldValue('field4', 'eng-US')); - - // eng-GB - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-GB')); - $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-GB')); - $this->assertEquals('value 3 eng-GB', $content->getFieldValue('field3', 'eng-GB')); - $this->assertEquals('value 4 eng-GB', $content->getFieldValue('field4', 'eng-GB')); - - // ger-DE - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'ger-DE')); - $this->assertEquals('value 2', $content->getFieldValue('field2', 'ger-DE')); - $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'ger-DE')); - $this->assertEquals('new value 4', $content->getFieldValue('field4', 'ger-DE')); - } - - /** - * Test for the updateContent() method. - * - * Updating with with new language and no field values given in the update struct. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function testUpdateContentWithNewLanguageNoValues() - { - $initialLanguageCode = 'ger-DE'; - $fieldValues = []; - - $content = $this->updateTestContent($initialLanguageCode, $fieldValues); - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Content', $content); - - return $content; - } - - /** - * Test for the updateContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testUpdateContentWithNewLanguageNoValues - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function testUpdateContentWithNewLanguageNoValuesFields(Content $content) - { - $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); - - $this->assertCount(3, $content->versionInfo->languageCodes); - $this->assertContains('ger-DE', $content->versionInfo->languageCodes); - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertContains('eng-GB', $content->versionInfo->languageCodes); - $this->assertCount(12, $content->getFields()); - - // eng-US - $this->assertEquals('value 1', $content->getFieldValue('field1', 'eng-US')); - $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-US')); - $this->assertEquals('value 3', $content->getFieldValue('field3', 'eng-US')); - $this->assertEquals('value 4', $content->getFieldValue('field4', 'eng-US')); - - // eng-GB - $this->assertEquals('value 1', $content->getFieldValue('field1', 'eng-GB')); - $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-GB')); - $this->assertEquals('value 3 eng-GB', $content->getFieldValue('field3', 'eng-GB')); - $this->assertEquals('value 4 eng-GB', $content->getFieldValue('field4', 'eng-GB')); - - // ger-DE - $this->assertEquals('value 1', $content->getFieldValue('field1', 'ger-DE')); - $this->assertEquals('value 2', $content->getFieldValue('field2', 'ger-DE')); - $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'ger-DE')); - $this->assertEquals('default value 4', $content->getFieldValue('field4', 'ger-DE')); - } - - /** - * Test for the updateContent() method. - * - * When updating Content with two languages, updating non-translatable field will also update it's value - * for non-main language. - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function testUpdateContentUpdatingNonTranslatableFieldUpdatesFieldCopy() - { - $initialLanguageCode = 'eng-US'; - $fieldValues = [ - 'field1' => ['eng-US' => 'new value 1'], - 'field2' => ['eng-US' => null], - ]; - - $content = $this->updateTestContent($initialLanguageCode, $fieldValues); - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Content', $content); - - return $content; - } - - /** - * Test for the updateContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testUpdateContentUpdatingNonTranslatableFieldUpdatesFieldCopy - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function testUpdateContentUpdatingNonTranslatableFieldUpdatesFieldCopyFields(Content $content) - { - $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); - - $this->assertCount(2, $content->versionInfo->languageCodes); - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertContains('eng-GB', $content->versionInfo->languageCodes); - $this->assertCount(8, $content->getFields()); - - // eng-US - $this->assertEquals('new value 1', $content->getFieldValue('field1', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-US')); - $this->assertEquals('value 3', $content->getFieldValue('field3', 'eng-US')); - $this->assertEquals('value 4', $content->getFieldValue('field4', 'eng-US')); - - // eng-GB - $this->assertEquals('new value 1', $content->getFieldValue('field1', 'eng-GB')); - $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-GB')); - $this->assertEquals('value 3 eng-GB', $content->getFieldValue('field3', 'eng-GB')); - $this->assertEquals('value 4 eng-GB', $content->getFieldValue('field4', 'eng-GB')); - } - - /** - * Test for the updateContent() method. - * - * Updating with two languages, initial language is always stored (even with all values being empty). - * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function testUpdateContentWithTwoLanguagesInitialLanguageTranslationNotCreated() - { - $initialLanguageCode = 'ger-DE'; - $fieldValues = [ - 'field1' => ['eng-US' => null], - 'field2' => ['eng-US' => null], - 'field4' => ['ger-DE' => null], - ]; - - $content = $this->updateTestContent($initialLanguageCode, $fieldValues); - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Content', $content); - - return $content; - } - - /** - * Test for the updateContent() method. - * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest::testUpdateContentWithTwoLanguagesInitialLanguageTranslationNotCreated - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function testUpdateContentWithTwoLanguagesInitialLanguageTranslationNotCreatedFields(Content $content) - { - $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); - - $this->assertCount(3, $content->versionInfo->languageCodes); - $this->assertContains('ger-DE', $content->versionInfo->languageCodes); - $this->assertContains('eng-US', $content->versionInfo->languageCodes); - $this->assertContains('eng-GB', $content->versionInfo->languageCodes); - $this->assertCount(12, $content->getFields()); - - // eng-US - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); - $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-US')); - $this->assertEquals('value 3', $content->getFieldValue('field3', 'eng-US')); - $this->assertEquals('value 4', $content->getFieldValue('field4', 'eng-US')); - - // eng-GB - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-GB')); - $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-GB')); - $this->assertEquals('value 3 eng-GB', $content->getFieldValue('field3', 'eng-GB')); - $this->assertEquals('value 4 eng-GB', $content->getFieldValue('field4', 'eng-GB')); - - // ger-DE - $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'ger-DE')); - $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'ger-DE')); - $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'ger-DE')); - $this->assertEquals($emptyValue, $content->getFieldValue('field4', 'ger-DE')); - } - - protected function assertFieldIds(Content $content1, Content $content2) - { - $fields1 = $this->mapFields($content1->getFields()); - $fields2 = $this->mapFields($content2->getFields()); - - foreach ($fields1 as $fieldDefinitionIdentifier => $languageFieldIds) { - foreach ($languageFieldIds as $languageCode => $fieldId) { - $this->assertEquals( - $fields2[$fieldDefinitionIdentifier][$languageCode], - $fieldId - ); - } - } - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $fields - * - * @return array - */ - protected function mapFields(array $fields) - { - $mappedFields = []; - - foreach ($fields as $field) { - $mappedFields[$field->fieldDefIdentifier][$field->languageCode] = $field->id; - } - - return $mappedFields; - } -} diff --git a/eZ/Publish/API/Repository/Tests/NotificationServiceTest.php b/eZ/Publish/API/Repository/Tests/NotificationServiceTest.php deleted file mode 100644 index f7d2bab7de..0000000000 --- a/eZ/Publish/API/Repository/Tests/NotificationServiceTest.php +++ /dev/null @@ -1,201 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Notification\CreateStruct; -use eZ\Publish\API\Repository\Values\Notification\Notification; -use eZ\Publish\API\Repository\Values\Notification\NotificationList; - -/** - * Test case for the NotificationService. - * - * @see \eZ\Publish\API\Repository\NotificationService - */ -class NotificationServiceTest extends BaseTest -{ - /** - * @covers \eZ\Publish\API\Repository\NotificationService::loadNotifications() - */ - public function testLoadNotifications() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $notificationService = $repository->getNotificationService(); - $notificationList = $notificationService->loadNotifications(0, 25); - /* END: Use Case */ - - $this->assertInstanceOf(NotificationList::class, $notificationList); - $this->assertIsArray($notificationList->items); - $this->assertIsInt($notificationList->totalCount); - $this->assertEquals(5, $notificationList->totalCount); - } - - /** - * @covers \eZ\Publish\API\Repository\NotificationService::getNotification() - */ - public function testGetNotification() - { - $repository = $this->getRepository(); - - $notificationId = $this->generateId('notification', 5); - - /* BEGIN: Use Case */ - $notificationService = $repository->getNotificationService(); - // $notificationId is the ID of an existing notification - $notification = $notificationService->getNotification($notificationId); - /* END: Use Case */ - - $this->assertInstanceOf(Notification::class, $notification); - $this->assertEquals($notificationId, $notification->id); - } - - /** - * @covers \eZ\Publish\API\Repository\NotificationService::markNotificationAsRead() - */ - public function testMarkNotificationAsRead() - { - $repository = $this->getRepository(); - - $notificationId = $this->generateId('notification', 5); - /* BEGIN: Use Case */ - $notificationService = $repository->getNotificationService(); - - $notification = $notificationService->getNotification($notificationId); - $notificationService->markNotificationAsRead($notification); - $notification = $notificationService->getNotification($notificationId); - /* END: Use Case */ - - $this->assertFalse($notification->isPending); - } - - /** - * @covers \eZ\Publish\API\Repository\NotificationService::getPendingNotificationCount() - */ - public function testGetPendingNotificationCount() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $notificationService = $repository->getNotificationService(); - $notificationPendingCount = $notificationService->getPendingNotificationCount(); - /* END: Use Case */ - - $this->assertEquals(3, $notificationPendingCount); - } - - /** - * @covers \eZ\Publish\API\Repository\NotificationService::getNotificationCount() - */ - public function testGetNotificationCount() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $notificationService = $repository->getNotificationService(); - $notificationCount = $notificationService->getNotificationCount(); - /* END: Use Case */ - - $this->assertEquals(5, $notificationCount); - } - - /** - * @covers \eZ\Publish\API\Repository\NotificationService::deleteNotification() - */ - public function testDeleteNotification() - { - $repository = $this->getRepository(); - - $notificationId = $this->generateId('notification', 5); - /* BEGIN: Use Case */ - $notificationService = $repository->getNotificationService(); - $notification = $notificationService->getNotification($notificationId); - $notificationService->deleteNotification($notification); - /* END: Use Case */ - - try { - $notificationService->getNotification($notificationId); - $this->fail('Notification ' . $notificationId . ' not deleted.'); - } catch (NotFoundException $e) { - } - } - - /** - * @covers \eZ\Publish\API\Repository\NotificationService::createNotification() - */ - public function testCreateNotification() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $notificationService = $repository->getNotificationService(); - $user = $repository->getUserService()->loadUser(14); - - $createStruct = new CreateStruct([ - 'ownerId' => $user->id, - 'type' => 'TEST', - 'data' => [ - 'foo' => 'Foo', - 'bar' => 'Bar', - 'baz' => 'Baz', - ], - ]); - - $notification = $notificationService->createNotification($createStruct); - /* END: Use Case */ - - $this->assertInstanceOf(Notification::class, $notification); - $this->assertGreaterThan(0, $notification->id); - } - - /** - * @covers \eZ\Publish\API\Repository\NotificationService::createNotification() - * @depends testCreateNotification - */ - public function testCreateNotificationThrowsInvalidArgumentExceptionOnMissingOwner() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $notificationService = $repository->getNotificationService(); - - $createStruct = new CreateStruct([ - 'type' => 'TEST', - ]); - - // This call will fail because notification owner is not specified - $notificationService->createNotification($createStruct); - /* END: Use Case */ - } - - /** - * @covers \eZ\Publish\API\Repository\NotificationService::createNotification() - * @depends testCreateNotification - */ - public function testCreateNotificationThrowsInvalidArgumentExceptionOnMissingType() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $notificationService = $repository->getNotificationService(); - - $createStruct = new CreateStruct([ - 'ownerId' => 14, - ]); - - // This call will fail because notification type is not specified - $notificationService->createNotification($createStruct); - /* END: Use Case */ - } -} diff --git a/eZ/Publish/API/Repository/Tests/ObjectStateServiceTest.php b/eZ/Publish/API/Repository/Tests/ObjectStateServiceTest.php deleted file mode 100644 index 5b3048a7fe..0000000000 --- a/eZ/Publish/API/Repository/Tests/ObjectStateServiceTest.php +++ /dev/null @@ -1,1904 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectState; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateUpdateStruct; - -/** - * Test case for operations in the ObjectStateService using in memory storage. - * - * @see \eZ\Publish\API\Repository\ObjectStateService - * @group object-state - */ -class ObjectStateServiceTest extends BaseTest -{ - private const EXISTING_OBJECT_STATE_GROUP_IDENTIFIER = 'ez_lock'; - private const EXISTING_OBJECT_STATE_IDENTIFIER = 'locked'; - - private const NON_EXISTING_OBJECT_STATE_GROUP_IDENTIFIER = 'non-existing'; - private const NON_EXISTING_OBJECT_STATE_IDENTIFIER = 'non-existing'; - - /** - * Test for the newObjectStateGroupCreateStruct() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::newObjectStateGroupCreateStruct() - */ - public function testNewObjectStateGroupCreateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $objectStateService = $repository->getObjectStateService(); - - $objectStateGroupCreate = $objectStateService->newObjectStateGroupCreateStruct( - 'publishing' - ); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ObjectState\\ObjectStateGroupCreateStruct', - $objectStateGroupCreate - ); - - return $objectStateGroupCreate; - } - - /** - * testNewObjectStateGroupCreateStructValues. - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupCreateStruct $objectStateGroupCreate - * - * - * @depends testNewObjectStateGroupCreateStruct - */ - public function testNewObjectStateGroupCreateStructValues(ObjectStateGroupCreateStruct $objectStateGroupCreate) - { - $this->assertPropertiesCorrect( - [ - 'identifier' => 'publishing', - 'defaultLanguageCode' => null, - 'names' => null, - 'descriptions' => null, - ], - $objectStateGroupCreate - ); - } - - /** - * Test for the newObjectStateGroupUpdateStruct() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::newObjectStateGroupUpdateStruct() - */ - public function testNewObjectStateGroupUpdateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $objectStateService = $repository->getObjectStateService(); - - $objectStateGroupUpdate = $objectStateService->newObjectStateGroupUpdateStruct(); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ObjectState\\ObjectStateGroupUpdateStruct', - $objectStateGroupUpdate - ); - - return $objectStateGroupUpdate; - } - - /** - * testNewObjectStateGroupUpdateStructValues. - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct $objectStateGroupUpdate - * - * - * @depends testNewObjectStateGroupUpdateStruct - */ - public function testNewObjectStateGroupUpdateStructValues(ObjectStateGroupUpdateStruct $objectStateGroupUpdate) - { - $this->assertPropertiesCorrect( - [ - 'identifier' => null, - 'defaultLanguageCode' => null, - 'names' => null, - 'descriptions' => null, - ], - $objectStateGroupUpdate - ); - } - - /** - * Test for the newObjectStateCreateStruct() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::newObjectStateCreateStruct() - */ - public function testNewObjectStateCreateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $objectStateService = $repository->getObjectStateService(); - - $objectStateCreate = $objectStateService->newObjectStateCreateStruct( - 'pending' - ); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ObjectState\\ObjectStateCreateStruct', - $objectStateCreate - ); - - return $objectStateCreate; - } - - /** - * testNewObjectStateCreateStructValues. - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateCreateStruct $objectStateCreate - * - * - * @depends testNewObjectStateCreateStruct - */ - public function testNewObjectStateCreateStructValues(ObjectStateCreateStruct $objectStateCreate) - { - $this->assertPropertiesCorrect( - [ - 'identifier' => 'pending', - 'priority' => false, - 'defaultLanguageCode' => null, - 'names' => null, - 'descriptions' => null, - ], - $objectStateCreate - ); - } - - /** - * Test for the newObjectStateUpdateStruct() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::newObjectStateUpdateStruct() - */ - public function testNewObjectStateUpdateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $objectStateService = $repository->getObjectStateService(); - - $objectStateUpdate = $objectStateService->newObjectStateUpdateStruct(); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ObjectState\\ObjectStateUpdateStruct', - $objectStateUpdate - ); - - return $objectStateUpdate; - } - - /** - * testNewObjectStateUpdateStructValues. - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateUpdateStruct $objectStateUpdate - * - * - * @depends testNewObjectStateUpdateStruct - */ - public function testNewObjectStateUpdateStructValues(ObjectStateUpdateStruct $objectStateUpdate) - { - $this->assertPropertiesCorrect( - [ - 'identifier' => null, - 'defaultLanguageCode' => null, - 'names' => null, - 'descriptions' => null, - ], - $objectStateUpdate - ); - } - - /** - * Test for the createObjectStateGroup() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::createObjectStateGroup() - * @depends testNewObjectStateGroupCreateStructValues - */ - public function testCreateObjectStateGroup() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $objectStateService = $repository->getObjectStateService(); - - $objectStateGroupCreate = $objectStateService->newObjectStateGroupCreateStruct( - 'publishing' - ); - $objectStateGroupCreate->defaultLanguageCode = 'eng-US'; - $objectStateGroupCreate->names = [ - 'eng-US' => 'Publishing', - 'ger-DE' => 'Sindelfingen', - ]; - $objectStateGroupCreate->descriptions = [ - 'eng-US' => 'Put something online', - 'ger-DE' => 'Put something ton Sindelfingen.', - ]; - - $createdObjectStateGroup = $objectStateService->createObjectStateGroup( - $objectStateGroupCreate - ); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ObjectState\\ObjectStateGroup', - $createdObjectStateGroup - ); - - return $createdObjectStateGroup; - } - - /** - * testCreateObjectStateGroupStructValues. - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup $createdObjectStateGroup - * - * - * @depends testCreateObjectStateGroup - */ - public function testCreateObjectStateGroupStructValues(ObjectStateGroup $createdObjectStateGroup) - { - $this->assertPropertiesCorrect( - [ - 'identifier' => 'publishing', - 'mainLanguageCode' => 'eng-US', - 'languageCodes' => ['eng-US', 'ger-DE'], - 'names' => [ - 'eng-US' => 'Publishing', - 'ger-DE' => 'Sindelfingen', - ], - 'descriptions' => [ - 'eng-US' => 'Put something online', - 'ger-DE' => 'Put something ton Sindelfingen.', - ], - ], - $createdObjectStateGroup - ); - $this->assertNotNull($createdObjectStateGroup->id); - } - - /** - * Test for the createObjectStateGroup() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::createObjectStateGroup() - * @depends testCreateObjectStateGroup - */ - public function testCreateObjectStateGroupThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $objectStateService = $repository->getObjectStateService(); - - $objectStateGroupCreate = $objectStateService->newObjectStateGroupCreateStruct( - // 'ez_lock' is already existing identifier - 'ez_lock' - ); - $objectStateGroupCreate->defaultLanguageCode = 'eng-US'; - $objectStateGroupCreate->names = [ - 'eng-US' => 'Publishing', - 'eng-GB' => 'Sindelfingen', - ]; - $objectStateGroupCreate->descriptions = [ - 'eng-US' => 'Put something online', - 'eng-GB' => 'Put something ton Sindelfingen.', - ]; - - // This call will fail because group with 'ez_lock' identifier already exists - $objectStateService->createObjectStateGroup( - $objectStateGroupCreate - ); - } - - /** - * Test for the loadObjectStateGroup() method. - * - * @covers \eZ\Publish\API\Repository\ObjectStateService::loadObjectStateGroup - */ - public function testLoadObjectStateGroup() - { - $repository = $this->getRepository(); - - $objectStateGroupId = $this->generateId('objectstategroup', 2); - /* BEGIN: Use Case */ - // $objectStateGroupId contains the ID of the standard object state - // group ez_lock. - $objectStateService = $repository->getObjectStateService(); - - $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( - $objectStateGroupId - ); - /* END: Use Case */ - - $this->assertInstanceOf( - ObjectStateGroup::class, - $loadedObjectStateGroup - ); - - $this->assertPropertiesCorrect( - [ - 'id' => 2, - 'identifier' => 'ez_lock', - 'mainLanguageCode' => 'eng-US', - 'languageCodes' => ['eng-US'], - 'names' => ['eng-US' => 'Lock'], - 'descriptions' => ['eng-US' => ''], - ], - $loadedObjectStateGroup - ); - } - - /** - * Test for the loadObjectStateGroup() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::loadObjectStateGroup() - * @depends testLoadObjectStateGroup - */ - public function testLoadObjectStateGroupThrowsNotFoundException() - { - $this->expectException(NotFoundException::class); - - $repository = $this->getRepository(); - - $nonExistentObjectStateGroupId = $this->generateId('objectstategroup', self::DB_INT_MAX); - /* BEGIN: Use Case */ - // $nonExistentObjectStateGroupId contains an ID for an object state - // that does not exist - $objectStateService = $repository->getObjectStateService(); - - // Throws a not found exception - $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( - $nonExistentObjectStateGroupId - ); - /* END: Use Case */ - } - - /** - * @covers \eZ\Publish\API\Repository\ObjectStateService::loadGroupByIdentifier - */ - public function testLoadObjectStateGroupByIdentifier(): void - { - $repository = $this->getRepository(); - - $objectStateService = $repository->getObjectStateService(); - - $loadedObjectStateGroup = $objectStateService->loadObjectStateGroupByIdentifier( - self::EXISTING_OBJECT_STATE_GROUP_IDENTIFIER - ); - - $this->assertInstanceOf( - ObjectStateGroup::class, - $loadedObjectStateGroup - ); - - $this->assertPropertiesCorrect( - [ - 'id' => 2, - 'identifier' => 'ez_lock', - 'mainLanguageCode' => 'eng-US', - 'languageCodes' => ['eng-US'], - 'names' => ['eng-US' => 'Lock'], - 'descriptions' => ['eng-US' => ''], - ], - $loadedObjectStateGroup - ); - } - - /** - * @covers \eZ\Publish\API\Repository\ObjectStateService::loadGroupByIdentifier - */ - public function testLoadObjectStateGroupByIdentifierThrowsNotFoundException(): void - { - $repository = $this->getRepository(); - - $objectStateService = $repository->getObjectStateService(); - - $this->expectException(NotFoundException::class); - $this->expectExceptionMessage(sprintf( - "Could not find 'ObjectStateGroup' with identifier '%s'", - self::NON_EXISTING_OBJECT_STATE_IDENTIFIER - )); - - $objectStateService->loadObjectStateGroupByIdentifier( - self::NON_EXISTING_OBJECT_STATE_GROUP_IDENTIFIER - ); - } - - /** - * Test for the loadObjectStateGroups() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::loadObjectStateGroups() - * @depends testLoadObjectStateGroup - */ - public function testLoadObjectStateGroups() - { - $repository = $this->getRepository(); - - $expectedGroupIdentifiers = $this->getGroupIdentifierMap($this->createObjectStateGroups()); - $expectedGroupIdentifiers['ez_lock'] = true; - - /* BEGIN: Use Case */ - $objectStateService = $repository->getObjectStateService(); - - $loadedObjectStateGroups = $objectStateService->loadObjectStateGroups(); - /* END: Use Case */ - - $this->assertIsArray($loadedObjectStateGroups); - - $this->assertObjectsLoadedByIdentifiers( - $expectedGroupIdentifiers, - $loadedObjectStateGroups, - 'ObjectStateGroup' - ); - } - - /** - * Creates a set of object state groups and returns an array of all - * existing group identifiers after creation. - * - * @return bool[] - */ - protected function createObjectStateGroups() - { - $repository = $this->getRepository(); - $objectStateService = $repository->getObjectStateService(); - - $identifiersToCreate = [ - 'first', - 'second', - 'third', - ]; - - $createdStateGroups = []; - - $groupCreateStruct = $objectStateService->newObjectStateGroupCreateStruct('dummy'); - - $groupCreateStruct->defaultLanguageCode = 'eng-US'; - $groupCreateStruct->names = [ - 'eng-US' => 'Foo', - 'ger-DE' => 'GerFoo', - ]; - $groupCreateStruct->descriptions = [ - 'eng-US' => 'Foo Bar', - 'ger-DE' => 'GerBar', - ]; - - foreach ($identifiersToCreate as $identifier) { - $groupCreateStruct->identifier = $identifier; - $createdStateGroups[] = $objectStateService->createObjectStateGroup($groupCreateStruct); - } - - return $createdStateGroups; - } - - /** - * Assert object identifiers. - * - * @param array $expectedIdentifiers - * @param array $loadedObjects - * @param string $class - */ - protected function assertObjectsLoadedByIdentifiers(array $expectedIdentifiers, array $loadedObjects, $class) - { - foreach ($loadedObjects as $loadedObject) { - if (!isset($expectedIdentifiers[$loadedObject->identifier])) { - $this->fail( - sprintf( - 'Loaded unexpected %s with identifier "%s"', - $class, - $loadedObject->identifier - ) - ); - } - unset($expectedIdentifiers[$loadedObject->identifier]); - } - - if (!empty($expectedIdentifiers)) { - $this->fail( - sprintf( - 'Expected %ss with identifiers "%s" not loaded.', - $class, - implode('", "', $expectedIdentifiers) - ) - ); - } - } - - /** - * Test for the loadObjectStateGroups() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::loadObjectStateGroups($offset) - * @depends testLoadObjectStateGroups - */ - public function testLoadObjectStateGroupsWithOffset() - { - $repository = $this->getRepository(); - $objectStateService = $repository->getObjectStateService(); - - $this->createObjectStateGroups(); - - $allObjectStateGroups = $objectStateService->loadObjectStateGroups(); - - $existingGroupIdentifiers = $this->getGroupIdentifierMap($allObjectStateGroups); - - /* BEGIN: Use Case */ - $objectStateService = $repository->getObjectStateService(); - - $loadedObjectStateGroups = $objectStateService->loadObjectStateGroups(2); - /* END: Use Case */ - - $this->assertIsArray($loadedObjectStateGroups); - - $this->assertObjectsLoadedByIdentifiers( - array_slice($existingGroupIdentifiers, 2), - $loadedObjectStateGroups, - 'ObjectStateGroup' - ); - } - - /** - * Returns a map of the given object state groups. - * - * @param array $groups - * - * @return array - */ - protected function getGroupIdentifierMap(array $groups) - { - $existingGroupIdentifiers = array_map( - static function ($group) { - return $group->identifier; - }, - $groups - ); - - return array_fill_keys($existingGroupIdentifiers, true); - } - - /** - * Test for the loadObjectStateGroups() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::loadObjectStateGroups($offset, $limit) - * @depends testLoadObjectStateGroupsWithOffset - */ - public function testLoadObjectStateGroupsWithOffsetAndLimit() - { - $repository = $this->getRepository(); - $objectStateService = $repository->getObjectStateService(); - - $allObjectStateGroups = $objectStateService->loadObjectStateGroups(); - - $existingGroupIdentifiers = $this->getGroupIdentifierMap($allObjectStateGroups); - - /* BEGIN: Use Case */ - $objectStateService = $repository->getObjectStateService(); - - $loadedObjectStateGroups = $objectStateService->loadObjectStateGroups(1, 2); - /* END: Use Case */ - - $this->assertIsArray($loadedObjectStateGroups); - - $this->assertObjectsLoadedByIdentifiers( - array_slice($existingGroupIdentifiers, 1, 2), - $loadedObjectStateGroups, - 'ObjectStateGroup' - ); - } - - /** - * Test for the loadObjectStates() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::loadObjectStates() - * @depends testLoadObjectStateGroup - */ - public function testLoadObjectStates() - { - $repository = $this->getRepository(); - - $objectStateGroupId = $this->generateId('objectstategroup', 2); - /* BEGIN: Use Case */ - // $objectStateGroupId contains the ID of the standard object state - // group ez_lock. - $objectStateService = $repository->getObjectStateService(); - - $objectStateGroup = $objectStateService->loadObjectStateGroup( - $objectStateGroupId - ); - - // Loads all object states in $objectStateGroup - $loadedObjectStates = $objectStateService->loadObjectStates($objectStateGroup); - /* END: Use Case */ - - $this->assertIsArray( - $loadedObjectStates - ); - $this->assertObjectsLoadedByIdentifiers( - ['not_locked' => true, 'locked' => true], - $loadedObjectStates, - 'ObjectState' - ); - } - - /** - * Test for the updateObjectStateGroup() method. - * - * @covers \eZ\Publish\API\Repository\ObjectStateService::updateObjectStateGroup - * @depends eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testLoadObjectStateGroup - */ - public function testUpdateObjectStateGroup() - { - $repository = $this->getRepository(); - - $objectStateGroupId = $this->generateId('objectstategroup', 2); - /* BEGIN: Use Case */ - // $objectStateGroupId contains the ID of the standard object state - // group ez_lock. - $objectStateService = $repository->getObjectStateService(); - - $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( - $objectStateGroupId - ); - - // pre populate any kind of cache for all - $objectStateService->loadObjectStateGroups(); - - $groupUpdateStruct = $objectStateService->newObjectStateGroupUpdateStruct(); - $groupUpdateStruct->identifier = 'sindelfingen'; - $groupUpdateStruct->defaultLanguageCode = 'ger-DE'; - $groupUpdateStruct->names = [ - 'ger-DE' => 'Sindelfingen', - ]; - $groupUpdateStruct->descriptions = [ - 'ger-DE' => 'Sindelfingen ist nicht nur eine Stadt', - ]; - - // Updates the $loadObjectStateGroup with the data from - // $groupUpdateStruct and returns the updated group - $updatedObjectStateGroup = $objectStateService->updateObjectStateGroup( - $loadedObjectStateGroup, - $groupUpdateStruct - ); - - $allObjectGroups = $objectStateService->loadObjectStateGroups(); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ObjectState\\ObjectStateGroup', - $updatedObjectStateGroup - ); - - return [ - $loadedObjectStateGroup, - $groupUpdateStruct, - $updatedObjectStateGroup, - $allObjectGroups, - ]; - } - - /** - * Test service method for partially updating object state group. - * - * @covers \eZ\Publish\API\Repository\ObjectStateService::updateObjectStateGroup - * @depends eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testLoadObjectStateGroup - */ - public function testUpdateObjectStateGroupChosenFieldsOnly() - { - $repository = $this->getRepository(); - $objectStateService = $repository->getObjectStateService(); - - $groupUpdateStruct = $objectStateService->newObjectStateGroupUpdateStruct(); - $groupUpdateStruct->defaultLanguageCode = 'eng-GB'; - $groupUpdateStruct->names = ['eng-GB' => 'Test']; - - $group = $objectStateService->loadObjectStateGroup(2); - - $updatedGroup = $objectStateService->updateObjectStateGroup($group, $groupUpdateStruct); - - $this->assertInstanceOf( - ObjectStateGroup::class, - $updatedGroup - ); - - $this->assertPropertiesCorrect( - [ - 'id' => 2, - 'identifier' => 'ez_lock', - 'mainLanguageCode' => 'eng-GB', - 'languageCodes' => ['eng-GB'], - 'names' => ['eng-GB' => 'Test'], - // descriptions array should have an empty value for eng-GB - // without the original descriptions - // since the descriptions were not in the update struct and we're changing default language - 'descriptions' => ['eng-GB' => ''], - ], - $updatedGroup - ); - } - - /** - * Test for the updateObjectStateGroup() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::updateObjectStateGroup() - * @depends testUpdateObjectStateGroup - */ - public function testUpdateObjectStateGroupThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $objectStateService = $repository->getObjectStateService(); - - // Create object state group which we will later update - $objectStateGroupCreate = $objectStateService->newObjectStateGroupCreateStruct( - 'publishing' - ); - $objectStateGroupCreate->defaultLanguageCode = 'eng-US'; - $objectStateGroupCreate->names = [ - 'eng-US' => 'Publishing', - 'eng-GB' => 'Sindelfingen', - ]; - $objectStateGroupCreate->descriptions = [ - 'eng-US' => 'Put something online', - 'eng-GB' => 'Put something ton Sindelfingen.', - ]; - - $createdObjectStateGroup = $objectStateService->createObjectStateGroup( - $objectStateGroupCreate - ); - - $groupUpdateStruct = $objectStateService->newObjectStateGroupUpdateStruct(); - // 'ez_lock' is the identifier of already existing group - $groupUpdateStruct->identifier = 'ez_lock'; - $groupUpdateStruct->defaultLanguageCode = 'ger-DE'; - $groupUpdateStruct->names = [ - 'ger-DE' => 'Sindelfingen', - ]; - $groupUpdateStruct->descriptions = [ - 'ger-DE' => 'Sindelfingen ist nicht nur eine Stadt', - ]; - - // This call will fail since state group with 'ez_lock' identifier already exists - $objectStateService->updateObjectStateGroup( - $createdObjectStateGroup, - $groupUpdateStruct - ); - } - - /** - * testUpdateObjectStateGroupStructValues. - * - * @param array $testData - * - * - * @depends testUpdateObjectStateGroup - */ - public function testUpdateObjectStateGroupStructValues(array $testData) - { - list( - $loadedObjectStateGroup, - $groupUpdateStruct, - $updatedObjectStateGroup, - $allObjectGroups - ) = $testData; - - $this->assertStructPropertiesCorrect( - $groupUpdateStruct, - $updatedObjectStateGroup - ); - - $this->assertContainsEquals($updatedObjectStateGroup, $allObjectGroups, ''); - $this->assertNotContainsEquals($loadedObjectStateGroup, $allObjectGroups, ''); - } - - /** - * Test for the createObjectState() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::createObjectState() - * @depends testLoadObjectStateGroup - * @depends testNewObjectStateCreateStruct - */ - public function testCreateObjectState() - { - $repository = $this->getRepository(); - - $objectStateGroupId = $this->generateId('objectstategroup', 2); - /* BEGIN: Use Case */ - // $objectStateGroupId contains the ID of the standard object state - // group ez_lock. - $objectStateService = $repository->getObjectStateService(); - - $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( - $objectStateGroupId, - Language::ALL - ); - - $objectStateCreateStruct = $objectStateService->newObjectStateCreateStruct( - 'locked_and_unlocked' - ); - $objectStateCreateStruct->priority = 23; - $objectStateCreateStruct->defaultLanguageCode = 'eng-US'; - $objectStateCreateStruct->names = [ - 'eng-US' => 'Locked and Unlocked', - 'ger-DE' => 'geschlossen und ungeschlossen', - ]; - $objectStateCreateStruct->descriptions = [ - 'eng-US' => 'A state between locked and unlocked.', - 'ger-DE' => 'ein Zustand zwischen geschlossen und ungeschlossen.', - ]; - - // Creates a new object state in the $loadObjectStateGroup with the - // data from $objectStateCreateStruct - $createdObjectState = $objectStateService->createObjectState( - $loadedObjectStateGroup, - $objectStateCreateStruct - ); - /* END: Use Case */ - - $this->assertInstanceOf(ObjectState::class, $createdObjectState); - // Object sequences are renumbered - $objectStateCreateStruct->priority = 2; - - return [ - $loadedObjectStateGroup, - $objectStateCreateStruct, - $createdObjectState, - ]; - } - - /** - * Test service method for creating object state in empty group. - * - * @covers \eZ\Publish\API\Repository\ObjectStateService::createObjectState - */ - public function testCreateObjectStateInEmptyGroup() - { - $repository = $this->getRepository(); - $objectStateService = $repository->getObjectStateService(); - - $groupCreateStruct = $objectStateService->newObjectStateGroupCreateStruct('test'); - $groupCreateStruct->defaultLanguageCode = 'eng-GB'; - $groupCreateStruct->names = ['eng-GB' => 'Test']; - $groupCreateStruct->descriptions = ['eng-GB' => 'Test description']; - - $createdGroup = $objectStateService->createObjectStateGroup($groupCreateStruct); - - $stateCreateStruct = $objectStateService->newObjectStateCreateStruct('test'); - $stateCreateStruct->priority = 2; - $stateCreateStruct->defaultLanguageCode = 'eng-GB'; - $stateCreateStruct->names = ['eng-GB' => 'Test']; - $stateCreateStruct->descriptions = ['eng-GB' => 'Test description']; - - $createdState = $objectStateService->createObjectState( - $createdGroup, - $stateCreateStruct - ); - - $this->assertInstanceOf( - ObjectState::class, - $createdState - ); - - $this->assertNotNull($createdState->id); - $this->assertPropertiesCorrect( - [ - 'identifier' => 'test', - 'priority' => 0, - 'mainLanguageCode' => 'eng-GB', - 'languageCodes' => ['eng-GB'], - 'names' => ['eng-GB' => 'Test'], - 'descriptions' => ['eng-GB' => 'Test description'], - ], - $createdState - ); - - $objectStateGroup = $createdState->getObjectStateGroup(); - $this->assertInstanceOf( - ObjectStateGroup::class, - $objectStateGroup - ); - - $this->assertEquals($createdGroup->id, $objectStateGroup->id); - $this->assertGreaterThan(0, $objectStateService->getContentCount($createdState)); - } - - /** - * Test for the createObjectState() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::createObjectState() - * @depends testLoadObjectStateGroup - * @depends testCreateObjectState - */ - public function testCreateObjectStateThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $objectStateGroupId = $this->generateId('objectstategroup', 2); - // $objectStateGroupId contains the ID of the standard object state - // group ez_lock. - $objectStateService = $repository->getObjectStateService(); - - $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( - $objectStateGroupId - ); - - $objectStateCreateStruct = $objectStateService->newObjectStateCreateStruct( - // 'not_locked' is the identifier of already existing state - 'not_locked' - ); - $objectStateCreateStruct->priority = 23; - $objectStateCreateStruct->defaultLanguageCode = 'eng-US'; - $objectStateCreateStruct->names = [ - 'eng-US' => 'Locked and Unlocked', - ]; - $objectStateCreateStruct->descriptions = [ - 'eng-US' => 'A state between locked and unlocked.', - ]; - - // This call will fail because object state with - // 'not_locked' identifier already exists - $objectStateService->createObjectState( - $loadedObjectStateGroup, - $objectStateCreateStruct - ); - } - - /** - * testCreateObjectStateStructValues. - * - * @param array $testData - * - * - * @depends testCreateObjectState - */ - public function testCreateObjectStateStructValues(array $testData) - { - list( - $loadedObjectStateGroup, - $objectStateCreateStruct, - $createdObjectState - ) = $testData; - - $this->assertStructPropertiesCorrect( - $objectStateCreateStruct, - $createdObjectState - ); - - $this->assertNotNull($createdObjectState->id); - - $this->assertEquals( - $loadedObjectStateGroup, - $createdObjectState->getObjectStateGroup() - ); - } - - /** - * Test for the loadObjectState() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::loadObjectState() - * @depends testLoadObjectStateGroup - */ - public function testLoadObjectState() - { - $repository = $this->getRepository(); - - $objectStateId = $this->generateId('objectstate', 2); - /* BEGIN: Use Case */ - // $objectStateId contains the ID of the "locked" state - $objectStateService = $repository->getObjectStateService(); - - $loadedObjectState = $objectStateService->loadObjectState( - $objectStateId - ); - /* END: Use Case */ - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ObjectState\\ObjectState', - $loadedObjectState - ); - - return $loadedObjectState; - } - - /** - * @see \eZ\Publish\API\Repository\ObjectStateService::loadObjectStateByIdentifier - */ - public function testLoadObjectStateByIdentifier(): void - { - $repository = $this->getRepository(); - - $objectStateService = $repository->getObjectStateService(); - - $objectStateGroup = $objectStateService->loadObjectStateGroupByIdentifier( - self::EXISTING_OBJECT_STATE_GROUP_IDENTIFIER - ); - - $loadedObjectState = $objectStateService->loadObjectStateByIdentifier( - $objectStateGroup, - self::EXISTING_OBJECT_STATE_IDENTIFIER - ); - - $this->assertInstanceOf( - ObjectState::class, - $loadedObjectState - ); - - $this->assertPropertiesCorrect( - [ - 'id' => 2, - 'identifier' => self::EXISTING_OBJECT_STATE_IDENTIFIER, - 'priority' => 1, - 'languageCodes' => ['eng-US'], - ], - $loadedObjectState - ); - } - - /** - * @see \eZ\Publish\API\Repository\ObjectStateService::loadObjectStateByIdentifier - */ - public function testLoadObjectStateByIdentifierThrowsNotFoundException(): void - { - $repository = $this->getRepository(); - - $objectStateService = $repository->getObjectStateService(); - - $loadedObjectStateGroup = $objectStateService->loadObjectStateGroupByIdentifier( - self::EXISTING_OBJECT_STATE_GROUP_IDENTIFIER - ); - - $this->expectException(NotFoundException::class); - $this->expectExceptionMessage(sprintf( - "Could not find 'ObjectState' with identifier '%s'", - var_export([ - 'identifier' => self::NON_EXISTING_OBJECT_STATE_IDENTIFIER, - 'groupId' => $loadedObjectStateGroup->id, - ], true) - )); - - $objectStateService->loadObjectStateByIdentifier( - $loadedObjectStateGroup, - self::NON_EXISTING_OBJECT_STATE_IDENTIFIER - ); - } - - /** - * testLoadObjectStateStructValues. - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectState $loadedObjectState - * - * - * @depends testLoadObjectState - */ - public function testLoadObjectStateStructValues(ObjectState $loadedObjectState) - { - $this->assertPropertiesCorrect( - [ - 'id' => 2, - 'identifier' => 'locked', - 'priority' => 1, - 'mainLanguageCode' => 'eng-US', - 'languageCodes' => [0 => 'eng-US'], - 'prioritizedLanguages' => [ - 0 => 'eng-US', - 1 => 'eng-GB', - 2 => 'ger-DE', - ], - 'names' => ['eng-US' => 'Locked'], - 'descriptions' => ['eng-US' => ''], - ], - $loadedObjectState - ); - - $this->assertEquals( - $this->getRepository()->getObjectStateService()->loadObjectStateGroup(2), - $loadedObjectState->getObjectStateGroup() - ); - } - - /** - * Test for the loadObjectState() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::loadObjectState() - * @depends testLoadObjectState - */ - public function testLoadObjectStateThrowsNotFoundException() - { - $this->expectException(NotFoundException::class); - - $repository = $this->getRepository(); - - $nonExistingObjectStateId = $this->generateId('objectstate', self::DB_INT_MAX); - /* BEGIN: Use Case */ - // $nonExistingObjectStateId contains the ID of a non existing state - $objectStateService = $repository->getObjectStateService(); - - // Throws not found exception - $loadedObjectState = $objectStateService->loadObjectState( - $nonExistingObjectStateId - ); - /* END: Use Case */ - } - - /** - * Data provider for PrioritizedLanguageList tests. - * - * @return array - */ - public function getPrioritizedLanguagesList() - { - return [ - [[], null], - [['eng-GB'], null], - [['eng-US'], 'eng-US'], - [['ger-DE'], 'ger-DE'], - [['eng-US', 'ger-DE'], 'eng-US'], - [['ger-DE', 'eng-US'], 'ger-DE'], - [['eng-GB', 'ger-DE', 'eng-US'], 'ger-DE'], - ]; - } - - /** - * Test that multi-language logic for loadObjectStateGroups respects prioritized language list. - * - * @dataProvider getPrioritizedLanguagesList - * - * @param string[] $prioritizedLanguages - * @param string|null $expectedLanguageCode - */ - public function testLoadObjectStateGroupsWithPrioritizedLanguagesList( - array $prioritizedLanguages, - $expectedLanguageCode - ) { - // cleanup before the actual test - $this->deleteExistingObjectStateGroups(); - - $repository = $this->getRepository(false); - $objectStateService = $repository->getObjectStateService(); - - $this->createObjectStateGroups(); - - $objectStateGroups = $objectStateService->loadObjectStateGroups( - 0, - -1, - $prioritizedLanguages - ); - - foreach ($objectStateGroups as $objectStateGroup) { - $languageCode = $expectedLanguageCode === null ? $objectStateGroup->defaultLanguageCode : $expectedLanguageCode; - - self::assertEquals( - $objectStateGroup->getName($languageCode), - $objectStateGroup->getName() - ); - - self::assertEquals( - $objectStateGroup->getDescription($languageCode), - $objectStateGroup->getDescription() - ); - } - } - - /** - * Test that multi-language logic for loadObjectStateGroup respects prioritized language list. - * - * @dataProvider getPrioritizedLanguagesList - * - * @param string[] $prioritizedLanguages - * @param string|null $expectedLanguageCode - */ - public function testLoadObjectStateGroupWithPrioritizedLanguagesList( - array $prioritizedLanguages, - $expectedLanguageCode - ) { - $repository = $this->getRepository(); - $objectStateService = $repository->getObjectStateService(); - - $objectStateGroup = $this->testCreateObjectStateGroup(); - $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( - $objectStateGroup->id, - $prioritizedLanguages - ); - - if ($expectedLanguageCode === null) { - $expectedLanguageCode = $loadedObjectStateGroup->defaultLanguageCode; - } - - self::assertEquals( - $loadedObjectStateGroup->getName($expectedLanguageCode), - $loadedObjectStateGroup->getName() - ); - - self::assertEquals( - $loadedObjectStateGroup->getDescription($expectedLanguageCode), - $loadedObjectStateGroup->getDescription() - ); - } - - /** - * Test that multi-language logic for loadObjectState respects prioritized language list. - * - * @dataProvider getPrioritizedLanguagesList - * - * @param string[] $prioritizedLanguages - * @param string|null $expectedLanguageCode - */ - public function testLoadObjectStateWithPrioritizedLanguagesList( - array $prioritizedLanguages, - $expectedLanguageCode - ) { - $repository = $this->getRepository(); - $objectStateService = $repository->getObjectStateService(); - - $objectStateData = $this->testCreateObjectState(); - /** @see \eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testCreateObjectState */ - $objectState = $objectStateData[2]; - /** @var \eZ\Publish\API\Repository\Values\ObjectState\ObjectState $objectState */ - $loadedObjectState = $objectStateService->loadObjectState($objectState->id, $prioritizedLanguages); - - if ($expectedLanguageCode === null) { - $expectedLanguageCode = $objectState->defaultLanguageCode; - } - - self::assertEquals( - $loadedObjectState->getName($expectedLanguageCode), - $loadedObjectState->getName() - ); - - self::assertEquals( - $loadedObjectState->getDescription($expectedLanguageCode), - $loadedObjectState->getDescription() - ); - } - - /** - * Test that multi-language logic for loadObjectStates respects prioritized language list. - * - * @dataProvider getPrioritizedLanguagesList - * - * @param string[] $languageCodes - * @param string|null $expectedLanguageCode - */ - public function testLoadObjectStatesWithPrioritizedLanguagesList($languageCodes, $expectedLanguageCode) - { - $repository = $this->getRepository(); - $objectStateService = $repository->getObjectStateService(); - - $objectStateGroup = $this->testCreateObjectStateGroup(); - $this->createObjectState( - $objectStateGroup, - 'state_1', - [ - 'eng-US' => 'One', - 'ger-DE' => 'ein', - ], - [ - 'eng-US' => 'State one', - 'ger-DE' => 'ein Zustand', - ] - ); - $this->createObjectState( - $objectStateGroup, - 'state_2', - [ - 'eng-US' => 'Two', - 'ger-DE' => 'zwei', - ], - [ - 'eng-US' => 'State two', - 'ger-DE' => 'zwei Zustand', - ] - ); - - // Loads all object states in $objectStateGroup - $loadedObjectStates = $objectStateService->loadObjectStates($objectStateGroup, $languageCodes); - - foreach ($loadedObjectStates as $objectState) { - self::assertEquals( - $objectState->getName($expectedLanguageCode), - $objectState->getName() - ); - - self::assertEquals( - $objectState->getDescription($expectedLanguageCode), - $objectState->getDescription() - ); - } - } - - /** - * Test for the updateObjectState() method. - * - * @covers \eZ\Publish\API\Repository\ObjectStateService::updateObjectState - * @depends eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testLoadObjectState - */ - public function testUpdateObjectState() - { - $repository = $this->getRepository(); - - $objectStateId = $this->generateId('objectstate', 2); - /* BEGIN: Use Case */ - // $objectStateId contains the ID of the "locked" state - $objectStateService = $repository->getObjectStateService(); - - $loadedObjectState = $objectStateService->loadObjectState( - $objectStateId - ); - - // pre load any possile cache loading all - $objectStateService->loadObjectStates($loadedObjectState->getObjectStateGroup()); - - $updateStateStruct = $objectStateService->newObjectStateUpdateStruct(); - $updateStateStruct->identifier = 'somehow_locked'; - $updateStateStruct->defaultLanguageCode = 'ger-DE'; - $updateStateStruct->names = [ - 'eng-US' => 'Somehow locked', - 'ger-DE' => 'Irgendwie gelockt', - ]; - $updateStateStruct->descriptions = [ - 'eng-US' => 'The object is somehow locked', - 'ger-DE' => 'Sindelfingen', - ]; - - $updatedObjectState = $objectStateService->updateObjectState( - $loadedObjectState, - $updateStateStruct - ); - - $allObjectStates = $objectStateService->loadObjectStates($loadedObjectState->getObjectStateGroup()); - /* END: Use Case */ - - $this->assertInstanceOf( - ObjectState::class, - $updatedObjectState - ); - - return [ - $loadedObjectState, - $updateStateStruct, - $updatedObjectState, - $allObjectStates, - ]; - } - - /** - * Test service method for partially updating object state. - * - * @covers \eZ\Publish\API\Repository\ObjectStateService::updateObjectState - * @depends eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testLoadObjectState - */ - public function testUpdateObjectStateChosenFieldsOnly() - { - $repository = $this->getRepository(); - $objectStateService = $repository->getObjectStateService(); - - $stateUpdateStruct = $objectStateService->newObjectStateUpdateStruct(); - $stateUpdateStruct->identifier = 'test'; - $stateUpdateStruct->names = ['eng-US' => 'Test']; - - $state = $objectStateService->loadObjectState(1); - - $updatedState = $objectStateService->updateObjectState($state, $stateUpdateStruct); - - $this->assertInstanceOf( - ObjectState::class, - $updatedState - ); - - $this->assertPropertiesCorrect( - [ - 'id' => 1, - 'identifier' => 'test', - 'priority' => 0, - 'mainLanguageCode' => 'eng-US', - 'languageCodes' => ['eng-US'], - 'names' => ['eng-US' => 'Test'], - // Original value of empty description for eng-US should be kept - 'descriptions' => ['eng-US' => ''], - ], - $updatedState - ); - - $this->assertInstanceOf( - ObjectStateGroup::class, - $updatedState->getObjectStateGroup() - ); - - $this->assertEquals($state->getObjectStateGroup()->id, $updatedState->getObjectStateGroup()->id); - } - - /** - * Test for the updateObjectState() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::updateObjectState() - * @depends testUpdateObjectState - */ - public function testUpdateObjectStateThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $objectStateId = $this->generateId('objectstate', 2); - // $objectStateId contains the ID of the "locked" state - $objectStateService = $repository->getObjectStateService(); - - $loadedObjectState = $objectStateService->loadObjectState( - $objectStateId - ); - - $updateStateStruct = $objectStateService->newObjectStateUpdateStruct(); - // 'not_locked' is the identifier of already existing state - $updateStateStruct->identifier = 'not_locked'; - $updateStateStruct->defaultLanguageCode = 'ger-DE'; - $updateStateStruct->names = [ - 'eng-US' => 'Somehow locked', - 'ger-DE' => 'Irgendwie gelockt', - ]; - $updateStateStruct->descriptions = [ - 'eng-US' => 'The object is somehow locked', - 'ger-DE' => 'Sindelfingen', - ]; - - // This call will fail because state with - // 'not_locked' identifier already exists - $objectStateService->updateObjectState( - $loadedObjectState, - $updateStateStruct - ); - } - - /** - * testUpdateObjectStateStructValues. - * - * @param array $testData - * - * - * @depends testUpdateObjectState - */ - public function testUpdateObjectStateStructValues(array $testData) - { - list( - $loadedObjectState, - $updateStateStruct, - $updatedObjectState, - $allObjectStates - ) = $testData; - - $this->assertPropertiesCorrect( - [ - 'id' => $loadedObjectState->id, - 'identifier' => $updateStateStruct->identifier, - 'priority' => $loadedObjectState->priority, - 'mainLanguageCode' => $updateStateStruct->defaultLanguageCode, - 'languageCodes' => ['eng-US', 'ger-DE'], - 'names' => $updateStateStruct->names, - 'descriptions' => $updateStateStruct->descriptions, - ], - $updatedObjectState - ); - - $this->assertEquals( - $loadedObjectState->getObjectStateGroup(), - $updatedObjectState->getObjectStateGroup() - ); - - $this->assertContainsEquals($updatedObjectState, $allObjectStates, ''); - $this->assertNotContainsEquals($loadedObjectState, $allObjectStates, ''); - } - - /** - * Test for the setPriorityOfObjectState() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::setPriorityOfObjectState() - * @depends testLoadObjectState - */ - public function testSetPriorityOfObjectState() - { - $repository = $this->getRepository(); - - $objectStateId = $this->generateId('objectstate', 1); - /* BEGIN: Use Case */ - // $objectStateId contains the ID of the "not_locked" state - $objectStateService = $repository->getObjectStateService(); - - $initiallyLoadedObjectState = $objectStateService->loadObjectState( - $objectStateId - ); - - // Sets the given priority on $initiallyLoadedObjectState - $objectStateService->setPriorityOfObjectState( - $initiallyLoadedObjectState, - 23 - ); - // $loadObjectState now has the priority 1, since object state - // priorities are always made sequential - $loadedObjectState = $objectStateService->loadObjectState( - $objectStateId - ); - /* END: Use Case */ - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ObjectState\\ObjectState', - $loadedObjectState - ); - $this->assertEquals(1, $loadedObjectState->priority); - } - - /** - * Test for the getContentState() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::getContentState() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - * @depends testLoadObjectState - */ - public function testGetContentState() - { - $repository = $this->getRepository(); - - $anonymousUserId = $this->generateId('user', 10); - $ezLockObjectStateGroupId = $this->generateId('objectstategroup', 2); - /* BEGIN: Use Case */ - // $anonymousUserId is the content ID of "Anonymous User" - $contentService = $repository->getContentService(); - $objectStateService = $repository->getObjectStateService(); - - $contentInfo = $contentService->loadContentInfo($anonymousUserId); - - $ezLockObjectStateGroup = $objectStateService->loadObjectStateGroup( - $ezLockObjectStateGroupId - ); - - // Loads the state of $contentInfo in the "ez_lock" object state group - $ezLockObjectState = $objectStateService->getContentState( - $contentInfo, - $ezLockObjectStateGroup - ); - /* END: Use Case */ - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ObjectState\\ObjectState', - $ezLockObjectState - ); - $this->assertEquals('not_locked', $ezLockObjectState->identifier); - } - - /** - * testGetInitialObjectState. - * - * - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - * @depends testLoadObjectState - */ - public function testGetInitialObjectState() - { - $repository = $this->getRepository(); - $objectStateService = $repository->getObjectStateService(); - - // Create object state group with custom state - $createdStateGroups = $this->createObjectStateGroups(); - - $customObjectStateGroupId = $createdStateGroups[1]->id; - $anonymousUserId = $this->generateId('user', 10); - - $customGroup = $objectStateService->loadObjectStateGroup( - $customObjectStateGroupId - ); - - $objectStateCreateStruct = $objectStateService->newObjectStateCreateStruct( - 'sindelfingen' - ); - $objectStateCreateStruct->priority = 1; - $objectStateCreateStruct->defaultLanguageCode = 'eng-US'; - $objectStateCreateStruct->names = ['eng-US' => 'Sindelfingen']; - - $createdState = $objectStateService->createObjectState( - $customGroup, - $objectStateCreateStruct - ); - - // Store state ID to be used - $customObjectStateId = $createdState->id; - - /* BEGIN: Use Case */ - // $anonymousUserId is the content ID of "Anonymous User" - // $customObjectStateGroupId is the ID of a state group, from which no - // state has been assigned to $anonymousUserId, yet - $contentService = $repository->getContentService(); - $objectStateService = $repository->getObjectStateService(); - - $contentInfo = $contentService->loadContentInfo($anonymousUserId); - - $customObjectStateGroup = $objectStateService->loadObjectStateGroup( - $customObjectStateGroupId - ); - - // Loads the initial state of the custom state group - $initialObjectState = $objectStateService->getContentState( - $contentInfo, - $customObjectStateGroup - ); - /* END: Use Case */ - - $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\ObjectState\\ObjectState', - $initialObjectState - ); - $this->assertEquals('sindelfingen', $initialObjectState->identifier); - $this->assertEquals(['eng-US' => 'Sindelfingen'], $initialObjectState->names); - $this->assertEquals('eng-US', $initialObjectState->defaultLanguageCode); - } - - /** - * Test for the setContentState() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::setContentState() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo - * @depends testLoadObjectState - */ - public function testSetContentState() - { - $repository = $this->getRepository(); - - $anonymousUserId = $this->generateId('user', 10); - $ezLockObjectStateGroupId = $this->generateId('objectstategroup', 2); - $lockedObjectStateId = $this->generateId('objectstate', 2); - /* BEGIN: Use Case */ - // $anonymousUserId is the content ID of "Anonymous User" - // $ezLockObjectStateGroupId contains the ID of the "ez_lock" object - // state group - // $lockedObjectStateId is the ID of the state "locked" - $contentService = $repository->getContentService(); - $objectStateService = $repository->getObjectStateService(); - - $contentInfo = $contentService->loadContentInfo($anonymousUserId); - - $ezLockObjectStateGroup = $objectStateService->loadObjectStateGroup( - $ezLockObjectStateGroupId - ); - $lockedObjectState = $objectStateService->loadObjectState($lockedObjectStateId); - - // Sets the state of $contentInfo from "not_locked" to "locked" - $objectStateService->setContentState( - $contentInfo, - $ezLockObjectStateGroup, - $lockedObjectState - ); - /* END: Use Case */ - - $ezLockObjectState = $objectStateService->getContentState( - $contentInfo, - $ezLockObjectStateGroup - ); - - $this->assertEquals('locked', $ezLockObjectState->identifier); - } - - /** - * Test for the setContentState() method. - * - * @covers \eZ\Publish\API\Repository\ObjectStateService::setContentState - * @depends eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testSetContentState - */ - public function testSetContentStateThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $createdStateGroups = $this->createObjectStateGroups(); - - $anonymousUserId = $this->generateId('user', 10); - $differentObjectStateGroupId = $createdStateGroups[1]->id; - $lockedObjectStateId = $this->generateId('objectstate', 2); - - /* BEGIN: Use Case */ - // $anonymousUserId is the content ID of "Anonymous User" - // $differentObjectStateGroupId contains the ID of an object state - // group which does not contain $lockedObjectStateId - // $lockedObjectStateId is the ID of the state "locked" - $contentService = $repository->getContentService(); - $objectStateService = $repository->getObjectStateService(); - - $contentInfo = $contentService->loadContentInfo($anonymousUserId); - - $differentObjectStateGroup = $objectStateService->loadObjectStateGroup( - $differentObjectStateGroupId - ); - $lockedObjectState = $objectStateService->loadObjectState($lockedObjectStateId); - - // Throws an invalid argument exception since $lockedObjectState does - // not belong to $differentObjectStateGroup - $objectStateService->setContentState( - $contentInfo, - $differentObjectStateGroup, - $lockedObjectState - ); - /* END: Use Case */ - } - - /** - * Test for the getContentCount() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::getContentCount() - * @depends testLoadObjectState - */ - public function testGetContentCount() - { - $repository = $this->getRepository(); - - $notLockedObjectStateId = $this->generateId('objectstate', 1); - /* BEGIN: Use Case */ - // $notLockedObjectStateId is the ID of the state "not_locked" - $objectStateService = $repository->getObjectStateService(); - - $notLockedObjectState = $objectStateService->loadObjectState($notLockedObjectStateId); - - $objectCount = $objectStateService->getContentCount($notLockedObjectState); - /* END: Use Case */ - - $this->assertEquals(18, $objectCount); - } - - /** - * Test for the deleteObjectState() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::deleteObjectState() - * @depends testLoadObjectState - */ - public function testDeleteObjectState() - { - $repository = $this->getRepository(); - - $notLockedObjectStateId = $this->generateId('objectstate', 1); - $lockedObjectStateId = $this->generateId('objectstate', 2); - /* BEGIN: Use Case */ - // $notLockedObjectStateId is the ID of the state "not_locked" - $objectStateService = $repository->getObjectStateService(); - - $notLockedObjectState = $objectStateService->loadObjectState($notLockedObjectStateId); - - // Deletes the object state and sets all objects, which where in that - // state, to the first state of the same object state group - $objectStateService->deleteObjectState($notLockedObjectState); - /* END: Use Case */ - - $lockedObjectState = $objectStateService->loadObjectState($lockedObjectStateId); - - // All objects transferred - $this->assertEquals( - 18, - $objectStateService->getContentCount($lockedObjectState) - ); - } - - /** - * Test for the deleteObjectStateGroup() method. - * - * - * @see \eZ\Publish\API\Repository\ObjectStateService::deleteObjectStateGroup() - * @depends testLoadObjectStateGroup - */ - public function testDeleteObjectStateGroup() - { - $repository = $this->getRepository(); - - $objectStateGroupId = $this->generateId('objectstategroup', 2); - /* BEGIN: Use Case */ - // $objectStateGroupId contains the ID of the standard object state - // group ez_lock. - $objectStateService = $repository->getObjectStateService(); - - $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( - $objectStateGroupId - ); - - $objectStateService->deleteObjectStateGroup($loadedObjectStateGroup); - /* END: Use Case */ - - try { - $objectStateService->loadObjectStateGroup($objectStateGroupId); - $this->fail( - sprintf( - 'Object state group with ID "%s" not deleted.', - $objectStateGroupId - ) - ); - } catch (NotFoundException $e) { - } - } - - /** - * Delete existing (e.g. initial) object state groups. - */ - private function deleteExistingObjectStateGroups() - { - $repository = $this->getRepository(); - $objectStateService = $repository->getObjectStateService(); - - $objectStateGroups = $objectStateService->loadObjectStateGroups(); - - foreach ($objectStateGroups as $objectStateGroup) { - $objectStateService->deleteObjectStateGroup($objectStateGroup); - } - } - - /** - * Create Object State within the given Object State Group. - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup - * @param string $identifier - * @param array $names multi-language names - * @param array $descriptions multi-language descriptions - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectState - */ - private function createObjectState( - ObjectStateGroup $objectStateGroup, - $identifier, - array $names, - array $descriptions - ) { - $objectStateService = $this->getRepository(false)->getObjectStateService(); - $objectStateCreateStruct = $objectStateService->newObjectStateCreateStruct( - $identifier - ); - $objectStateCreateStruct->priority = 23; - $objectStateCreateStruct->defaultLanguageCode = array_keys($names)[0]; - $objectStateCreateStruct->names = $names; - $objectStateCreateStruct->descriptions = $descriptions; - - // Create a new object state in the $objectStateGroup with the - // data from $objectStateCreateStruct - return $objectStateService->createObjectState( - $objectStateGroup, - $objectStateCreateStruct - ); - } -} diff --git a/eZ/Publish/API/Repository/Tests/Parallel/ContentServiceTest.php b/eZ/Publish/API/Repository/Tests/Parallel/ContentServiceTest.php deleted file mode 100644 index 4262f4ce2b..0000000000 --- a/eZ/Publish/API/Repository/Tests/Parallel/ContentServiceTest.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\Parallel; - -use eZ\Publish\Core\Base\Exceptions\BadStateException; - -final class ContentServiceTest extends BaseParallelTestCase -{ - public function testPublishMultipleVersions(): void - { - $repository = $this->getRepository(); - - $contentService = $repository->getContentService(); - $content = $this->createFolder( - [ - 'eng-US' => 'Content', - ], - $this->generateId('location', 2) - ); - - $version1 = $contentService->createContentDraft($content->contentInfo, $content->versionInfo); - $version2 = $contentService->createContentDraft($content->contentInfo, $content->versionInfo); - - $processList = new ParallelProcessList(); - $this->addParallelProcess($processList, static function () use ($version1, $contentService) { - try { - $contentService->publishVersion($version1->versionInfo); - } catch (BadStateException $e) { - } - }); - - $this->addParallelProcess($processList, static function () use ($version2, $contentService) { - try { - $contentService->publishVersion($version2->versionInfo); - } catch (BadStateException $e) { - } - }); - - $this->runParallelProcesses($processList); - - $version1 = $contentService->loadVersionInfo($version1->contentInfo, 2); - $version2 = $contentService->loadVersionInfo($version2->contentInfo, 3); - - $this->assertTrue( - $version1->isPublished() && $version2->isDraft() || $version1->isDraft() && $version2->isPublished(), - 'One of the versions should be published and the other should be draft' - ); - } -} diff --git a/eZ/Publish/API/Repository/Tests/README.rst b/eZ/Publish/API/Repository/Tests/README.rst deleted file mode 100644 index a1239a2a9f..0000000000 --- a/eZ/Publish/API/Repository/Tests/README.rst +++ /dev/null @@ -1,38 +0,0 @@ -================================ -Using the Integration Test Suite -================================ - -The Integration Test Suite can be used to verify the functionality of the Public -API with different back ends. In general, you just need a working *PHPUnit* in -order to run the tests. - -The Integration Test Suite ships with a memory back end, which can deal as a -verification step that the test suite itself runs. - -In order to run any of the tests, you need to copy the -``config.php-DEVELOPMENT`` configuration file in the ``ezpublish-kernel`` -directory into a ``config.php`` file. - ---------------- -Memory Back End ---------------- - -To run the Integration Test Suite against the Memory Back End, you just need to -run ``phpunit`` inside this directory. This should result in a fully successful -run of the suite. - ------------------ -Database Back End ------------------ - -To run the test suite against the real world implementation, use the alternative -``phpunit-legacy.xml`` as the configuration for PHPUnit. -Beware that you need to have set the correct path to your eZ Publish Legacy instance -in ``config.php``. - -After that, use the following command to run the tests:: - - phpunit -c phpunit-legacy.xml - -Any problems occurring during the run should be issues in the Public API -implementation, as long as `Memory Back End`_ runs correctly. diff --git a/eZ/Publish/API/Repository/Tests/RepositoryTest.php b/eZ/Publish/API/Repository/Tests/RepositoryTest.php deleted file mode 100644 index 1d1cca4e7d..0000000000 --- a/eZ/Publish/API/Repository/Tests/RepositoryTest.php +++ /dev/null @@ -1,330 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use Exception; -use eZ\Publish\API\Repository\NotificationService; -use eZ\Publish\API\Repository\Repository; - -/** - * Test case for operations in the Repository using in memory storage. - * - * @see eZ\Publish\API\Repository\Repository - * @group integration - */ -class RepositoryTest extends BaseTest -{ - /** - * Test for the getRepository() method. - */ - public function testGetRepository() - { - $this->assertInstanceOf(Repository::class, $this->getSetupFactory()->getRepository(true)); - } - - /** - * Test for the getContentService() method. - * - * @group content - * @group user - * - * @see \eZ\Publish\API\Repository\Repository::getContentService() - */ - public function testGetContentService() - { - $repository = $this->getRepository(); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\ContentService', - $repository->getContentService() - ); - } - - /** - * Test for the getContentLanguageService() method. - * - * @group language - * - * @see \eZ\Publish\API\Repository\Repository::getContentLanguageService() - */ - public function testGetContentLanguageService() - { - $repository = $this->getRepository(); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\LanguageService', - $repository->getContentLanguageService() - ); - } - - /** - * Test for the getContentTypeService() method. - * - * @group content-type - * @group field-type - * @group user - * - * @see \eZ\Publish\API\Repository\Repository::getContentTypeService() - */ - public function testGetContentTypeService() - { - $repository = $this->getRepository(); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\ContentTypeService', - $repository->getContentTypeService() - ); - } - - /** - * Test for the getLocationService() method. - * - * @group location - * - * @see \eZ\Publish\API\Repository\Repository::getLocationService() - */ - public function testGetLocationService() - { - $repository = $this->getRepository(); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\LocationService', - $repository->getLocationService() - ); - } - - /** - * Test for the getSectionService() method. - * - * @group section - * - * @see \eZ\Publish\API\Repository\Repository::getSectionService() - */ - public function testGetSectionService() - { - $repository = $this->getRepository(); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\SectionService', - $repository->getSectionService() - ); - } - - /** - * Test for the getUserService() method. - * - * @group user - * - * @see \eZ\Publish\API\Repository\Repository::getUserService() - */ - public function testGetUserService() - { - $repository = $this->getRepository(); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\UserService', - $repository->getUserService() - ); - } - - /** - * Test for the getNotificationService() method. - * - * @group user - * - * @see \eZ\Publish\API\Repository\Repository::getNotificationService() - */ - public function testGetNotificationService() - { - $repository = $this->getRepository(); - $this->assertInstanceOf( - NotificationService::class, - $repository->getNotificationService() - ); - } - - /** - * Test for the getTrashService() method. - * - * @group trash - * - * @see \eZ\Publish\API\Repository\Repository::getTrashService() - */ - public function testGetTrashService() - { - $repository = $this->getRepository(); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\TrashService', - $repository->getTrashService() - ); - } - - /** - * Test for the getRoleService() method. - * - * @group role - * - * @see \eZ\Publish\API\Repository\Repository::getRoleService() - */ - public function testGetRoleService() - { - $repository = $this->getRepository(); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\RoleService', - $repository->getRoleService() - ); - } - - /** - * Test for the getURLAliasService() method. - * - * @group url-alias - * - * @see \eZ\Publish\API\Repository\Repository::getURLAliasService() - */ - public function testGetURLAliasService() - { - $repository = $this->getRepository(); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\URLAliasService', - $repository->getURLAliasService() - ); - } - - /** - * Test for the getUrlWildcardService() method. - * - * @group url-wildcard - * - * @see \eZ\Publish\API\Repository\Repository::getUrlWildcardService() - */ - public function testGetURLWildcardService() - { - $repository = $this->getRepository(); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\URLWildcardService', - $repository->getURLWildcardService() - ); - } - - /** - * Test for the getObjectStateService(). - * - * @group object-state - * - * @see \eZ\Publish\API\Repository\Repository::getObjectStateService() - */ - public function testGetObjectStateService() - { - $repository = $this->getRepository(); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\ObjectStateService', - $repository->getObjectStateService() - ); - } - - /** - * Test for the getFieldTypeService(). - * - * @group object-state - * - * @see \eZ\Publish\API\Repository\Repository::getFieldTypeService() - */ - public function testGetFieldTypeService() - { - $repository = $this->getRepository(); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\FieldTypeService', - $repository->getFieldTypeService() - ); - } - - /** - * Test for the getSearchService() method. - * - * @group search - * - * @see \eZ\Publish\API\Repository\Repository::getSearchService() - */ - public function testGetSearchService() - { - $repository = $this->getRepository(); - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\SearchService', - $repository->getSearchService() - ); - } - - /** - * Test for the getSearchService() method. - * - * @group permission - * - * @see \eZ\Publish\API\Repository\Repository::getPermissionResolver() - */ - public function testGetPermissionResolver() - { - $repository = $this->getRepository(); - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\PermissionResolver', - $repository->getPermissionResolver() - ); - } - - /** - * Test for the commit() method. - * - * @see \eZ\Publish\API\Repository\Repository::commit() - */ - public function testCommit() - { - $repository = $this->getRepository(); - - try { - $repository->beginTransaction(); - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - } - - /** - * Test for the commit() method. - * - * @see \eZ\Publish\API\Repository\Repository::commit() - */ - public function testCommitThrowsRuntimeException() - { - $this->expectException(\RuntimeException::class); - - $repository = $this->getRepository(); - $repository->commit(); - } - - /** - * Test for the rollback() method. - * - * @see \eZ\Publish\API\Repository\Repository::rollback() - */ - public function testRollback() - { - $repository = $this->getRepository(); - $repository->beginTransaction(); - $repository->rollback(); - } - - /** - * Test for the rollback() method. - * - * @see \eZ\Publish\API\Repository\Repository::rollback() - */ - public function testRollbackThrowsRuntimeException() - { - $this->expectException(\RuntimeException::class); - - $repository = $this->getRepository(); - $repository->rollback(); - } -} diff --git a/eZ/Publish/API/Repository/Tests/RoleServiceTest.php b/eZ/Publish/API/Repository/Tests/RoleServiceTest.php deleted file mode 100644 index 2d62b5249f..0000000000 --- a/eZ/Publish/API/Repository/Tests/RoleServiceTest.php +++ /dev/null @@ -1,3075 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\RoleService; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\LanguageLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation; -use eZ\Publish\API\Repository\Values\User\Policy; -use eZ\Publish\API\Repository\Values\User\Role; -use eZ\Publish\API\Repository\Values\User\RoleAssignment; -use eZ\Publish\API\Repository\Values\User\RoleCopyStruct; -use eZ\Publish\API\Repository\Values\User\RoleCreateStruct; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserRoleAssignment; - -/** - * Test case for operations in the RoleService using in memory storage. - * - * The following IDs from the default eZ community edition database are used in - * this test: - * - * <ul> - * <li> - * ContentType - * <ul> - * <li><strong>28</strong>: File</li> - * <li><strong>29</strong>: Flash</li> - * <li><strong>30</strong>: Image</li> - * </ul> - * </li> - * <ul> - * - * @see eZ\Publish\API\Repository\RoleService - * @group role - */ -class RoleServiceTest extends BaseTest -{ - /** - * Test for the newRoleCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\RoleService::newRoleCreateStruct() - */ - public function testNewRoleCreateStruct() - { - $repository = $this->getRepository(); - - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('roleName'); - - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\User\\RoleCreateStruct', $roleCreate); - } - - /** - * @covers \eZ\Publish\API\Repository\RoleService::newRoleCopyStruct - */ - public function testNewRoleCopyStruct(): void - { - $repository = $this->getRepository(); - - $roleService = $repository->getRoleService(); - $roleCopy = $roleService->newRoleCopyStruct('copiedRole'); - - $this->assertSame('copiedRole', $roleCopy->newIdentifier); - $this->assertSame([], $roleCopy->getPolicies()); - } - - /** - * Test for the newRoleCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\RoleService::newRoleCreateStruct() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewRoleCreateStruct - */ - public function testNewRoleCreateStructSetsNamePropertyOnStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('roleName'); - - /* END: Use Case */ - - $this->assertEquals('roleName', $roleCreate->identifier); - } - - /** - * Test for the createRole() method. - * - * @see \eZ\Publish\API\Repository\RoleService::createRole() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewRoleCreateStruct - */ - public function testCreateRole() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('roleName'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $role = $roleService->createRole($roleCreate); - - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\RoleDraft', - $role - ); - - return [ - 'createStruct' => $roleCreate, - 'role' => $role, - ]; - } - - /** - * Test for the createRole() method. - * - * @see \eZ\Publish\API\Repository\RoleService::createRole() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRole - */ - public function testRoleCreateStructValues(array $data) - { - $createStruct = $data['createStruct']; - $role = $data['role']; - - $this->assertEquals( - [ - 'identifier' => $createStruct->identifier, - 'policies' => $createStruct->policies, - ], - [ - 'identifier' => $role->identifier, - 'policies' => $role->policies, - ] - ); - $this->assertNotNull($role->id); - - return $data; - } - - /** - * Test for the createRole() method. - * - * @see \eZ\Publish\API\Repository\RoleService::createRole() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewRoleCreateStruct - */ - public function testCreateRoleWithPolicy() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('roleName'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - // Create new subtree limitation - $limitation = new SubtreeLimitation( - [ - 'limitationValues' => ['/1/2/'], - ] - ); - - // Create policy create struct and add limitation to it - $policyCreate = $roleService->newPolicyCreateStruct('content', 'read'); - $policyCreate->addLimitation($limitation); - - // Add policy create struct to role create struct - $roleCreate->addPolicy($policyCreate); - - $role = $roleService->createRole($roleCreate); - - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\RoleDraft', - $role - ); - - return [ - 'createStruct' => $roleCreate, - 'role' => $role, - ]; - } - - /** - * Test for the createRole() method. - * - * @see \eZ\Publish\API\Repository\RoleService::createRole() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRoleWithPolicy - */ - public function testRoleCreateStructValuesWithPolicy(array $data) - { - $createStruct = $data['createStruct']; - $role = $data['role']; - - $this->assertEquals( - [ - 'identifier' => $createStruct->identifier, - 'policy_module' => $createStruct->policies[0]->module, - 'policy_function' => $createStruct->policies[0]->function, - 'policy_limitation' => array_values($createStruct->policies[0]->limitations), - ], - [ - 'identifier' => $role->identifier, - 'policy_module' => $role->policies[0]->module, - 'policy_function' => $role->policies[0]->function, - 'policy_limitation' => array_values($role->policies[0]->limitations), - ] - ); - $this->assertNotNull($role->id); - - return $data; - } - - /** - * Test creating a role with multiple policies. - * - * @covers \eZ\Publish\API\Repository\RoleService::createRole - */ - public function testCreateRoleWithMultiplePolicies() - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - $limitation1 = new Limitation\ContentTypeLimitation(); - $limitation1->limitationValues = ['1', '3', '13']; - - $limitation2 = new Limitation\SectionLimitation(); - $limitation2->limitationValues = ['2', '3']; - - $limitation3 = new Limitation\OwnerLimitation(); - $limitation3->limitationValues = ['1', '2']; - - $limitation4 = new Limitation\UserGroupLimitation(); - $limitation4->limitationValues = ['1']; - - $policyCreateStruct1 = $roleService->newPolicyCreateStruct('content', 'read'); - $policyCreateStruct1->addLimitation($limitation1); - $policyCreateStruct1->addLimitation($limitation2); - - $policyCreateStruct2 = $roleService->newPolicyCreateStruct('content', 'edit'); - $policyCreateStruct2->addLimitation($limitation3); - $policyCreateStruct2->addLimitation($limitation4); - - $roleCreateStruct = $roleService->newRoleCreateStruct('ultimate_permissions'); - $roleCreateStruct->addPolicy($policyCreateStruct1); - $roleCreateStruct->addPolicy($policyCreateStruct2); - - $createdRole = $roleService->createRole($roleCreateStruct); - - self::assertInstanceOf(Role::class, $createdRole); - self::assertGreaterThan(0, $createdRole->id); - - $this->assertPropertiesCorrect( - [ - 'identifier' => $roleCreateStruct->identifier, - ], - $createdRole - ); - - self::assertCount(2, $createdRole->getPolicies()); - - foreach ($createdRole->getPolicies() as $policy) { - self::assertInstanceOf(Policy::class, $policy); - self::assertGreaterThan(0, $policy->id); - self::assertEquals($createdRole->id, $policy->roleId); - - self::assertCount(2, $policy->getLimitations()); - - foreach ($policy->getLimitations() as $limitation) { - self::assertInstanceOf(Limitation::class, $limitation); - - if ($policy->module == 'content' && $policy->function == 'read') { - switch ($limitation->getIdentifier()) { - case Limitation::CONTENTTYPE: - self::assertEquals($limitation1->limitationValues, $limitation->limitationValues); - break; - - case Limitation::SECTION: - self::assertEquals($limitation2->limitationValues, $limitation->limitationValues); - break; - - default: - self::fail('Created role contains limitations not defined with create struct'); - } - } elseif ($policy->module == 'content' && $policy->function == 'edit') { - switch ($limitation->getIdentifier()) { - case Limitation::OWNER: - self::assertEquals($limitation3->limitationValues, $limitation->limitationValues); - break; - - case Limitation::USERGROUP: - self::assertEquals($limitation4->limitationValues, $limitation->limitationValues); - break; - - default: - self::fail('Created role contains limitations not defined with create struct'); - } - } else { - self::fail('Created role contains policy not defined with create struct'); - } - } - } - } - - /** - * Test for the createRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::createRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewRoleCreateStruct - */ - public function testCreateRoleDraft() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('roleName'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRole($roleDraft->id); - $newRoleDraft = $roleService->createRoleDraft($role); - - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\RoleDraft', - $newRoleDraft - ); - } - - /** - * Test for the createRole() method. - * - * @see \eZ\Publish\API\Repository\RoleService::createRole() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRole - */ - public function testCreateRoleThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('Editor'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - // This call will fail with an InvalidArgumentException, because Editor exists - $roleService->createRole($roleCreate); - - /* END: Use Case */ - } - - /** - * Test for the createRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::createRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRoleDraft - */ - public function testCreateRoleDraftThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('Editor'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRole($roleDraft->id); - $roleService->createRoleDraft($role); // First role draft - - // This call will fail with an InvalidArgumentException, because there is already a draft - $roleService->createRoleDraft($role); - - /* END: Use Case */ - } - - /** - * Test for the createRole() method. - * - * @see \eZ\Publish\API\Repository\RoleService::createRole() - */ - public function testCreateRoleThrowsLimitationValidationException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\LimitationValidationException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - // Create new role create struct - $roleCreate = $roleService->newRoleCreateStruct('Lumberjack'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - // Create new subtree limitation - $limitation = new SubtreeLimitation( - [ - 'limitationValues' => ['/mountain/forest/tree/42/'], - ] - ); - - // Create policy create struct and add limitation to it - $policyCreate = $roleService->newPolicyCreateStruct('content', 'remove'); - $policyCreate->addLimitation($limitation); - - // Add policy create struct to role create struct - $roleCreate->addPolicy($policyCreate); - - // This call will fail with an LimitationValidationException, because subtree - // "/mountain/forest/tree/42/" does not exist - $roleService->createRole($roleCreate); - /* END: Use Case */ - } - - /** - * Test for the createRole() method. - * - * @see \eZ\Publish\API\Repository\RoleService::createRole() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewRoleCreateStruct - */ - public function testCreateRoleInTransactionWithRollback() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - - $repository->beginTransaction(); - - $roleCreate = $roleService->newRoleCreateStruct('roleName'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $createdRoleId = $roleService->createRole($roleCreate)->id; - - $repository->rollback(); - - try { - // This call will fail with a "NotFoundException" - $role = $roleService->loadRole($createdRoleId); - } catch (NotFoundException $e) { - return; - } - /* END: Use Case */ - - $this->fail('Role object still exists after rollback.'); - } - - /** - * Test for the createRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::createRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewRoleCreateStruct - */ - public function testCreateRoleDraftInTransactionWithRollback() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - - $repository->beginTransaction(); - - $roleCreate = $roleService->newRoleCreateStruct('roleName'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $createdRoleId = $roleService->createRole($roleCreate)->id; - - $repository->rollback(); - - try { - // This call will fail with a "NotFoundException" - $role = $roleService->loadRoleDraft($createdRoleId); - } catch (NotFoundException $e) { - return; - } - /* END: Use Case */ - - $this->fail('Role draft object still exists after rollback.'); - } - - public function providerForCopyRoleTests(): array - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - $roleCreateStruct = $roleService->newRoleCreateStruct('newRole'); - $roleCopyStruct = $roleService->newRoleCopyStruct('copiedRole'); - - $policyCreateStruct1 = $roleService->newPolicyCreateStruct('content', 'read'); - $policyCreateStruct2 = $roleService->newPolicyCreateStruct('content', 'edit'); - - $roleLimitations = [ - new SectionLimitation(['limitationValues' => [2]]), - new SubtreeLimitation(['limitationValues' => ['/1/2/']]), - ]; - - $policyCreateStruct1WithLimitations = $roleService->newPolicyCreateStruct('content', 'read'); - foreach ($roleLimitations as $roleLimitation) { - $policyCreateStruct1WithLimitations->addLimitation($roleLimitation); - } - - return [ - 'without-policies' => [ - $roleCreateStruct, - $roleCopyStruct, - [], - ], - 'with-policies' => [ - $roleCreateStruct, - $roleCopyStruct, - [$policyCreateStruct1, $policyCreateStruct2], - ], - 'with-limitations' => [ - $roleCreateStruct, - $roleCopyStruct, - [$policyCreateStruct1WithLimitations, $policyCreateStruct2], - ], - ]; - } - - /** - * @dataProvider providerForCopyRoleTests - * - * @covers \eZ\Publish\API\Repository\RoleService::copyRole - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewRoleCopyStruct - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleByIdentifier - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException - */ - public function testCopyRole(RoleCreateStruct $roleCreateStruct, RoleCopyStruct $roleCopyStruct): void - { - $repository = $this->getRepository(); - - $roleService = $repository->getRoleService(); - - $roleDraft = $roleService->createRole($roleCreateStruct); - - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRole($roleDraft->id); - - $copiedRole = $roleService->copyRole($role, $roleCopyStruct); - - // Now verify that our change was saved - $role = $roleService->loadRoleByIdentifier('copiedRole'); - - $this->assertEquals($role->id, $copiedRole->id); - $this->assertEquals('copiedRole', $role->identifier); - $this->assertEmpty($role->getPolicies()); - } - - /** - * Test for the copyRole() method with added policies. - * - * @dataProvider providerForCopyRoleTests - * - * @covers \eZ\Publish\API\Repository\RoleService::copyRole - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewRoleCopyStruct - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleByIdentifier - * - * @param \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct[] $policies - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException - */ - public function testCopyRoleWithPolicies( - RoleCreateStruct $roleCreateStruct, - RoleCopyStruct $roleCopyStruct, - array $policies - ): void { - $repository = $this->getRepository(); - - $roleService = $repository->getRoleService(); - - foreach ($policies as $policy) { - $roleCreateStruct->addPolicy($policy); - } - - $roleDraft = $roleService->createRole($roleCreateStruct); - - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRole($roleDraft->id); - - $copiedRole = $roleService->copyRole($role, $roleCopyStruct); - - // Now verify that our change was saved - $role = $roleService->loadRoleByIdentifier('copiedRole'); - - $this->assertEquals($role->getPolicies(), $copiedRole->getPolicies()); - } - - /** - * Test for the copyRole() method with added policies and limitations. - * - * @dataProvider providerForCopyRoleTests - * - * @covers \eZ\Publish\API\Repository\RoleService::copyRole - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewRoleCopyStruct - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleByIdentifier - * - * @param \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct[] $policies - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException - */ - public function testCopyRoleWithPoliciesAndLimitations( - RoleCreateStruct $roleCreateStruct, - RoleCopyStruct $roleCopyStruct, - array $policies - ): void { - $repository = $this->getRepository(); - - $roleService = $repository->getRoleService(); - - foreach ($policies as $policy) { - $roleCreateStruct->addPolicy($policy); - } - - $roleDraft = $roleService->createRole($roleCreateStruct); - - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRole($roleDraft->id); - - $copiedRole = $roleService->copyRole($role, $roleCopyStruct); - - // Now verify that our change was saved - $role = $roleService->loadRoleByIdentifier('copiedRole'); - - $limitations = []; - foreach ($role->getPolicies() as $policy) { - $limitations[$policy->function] = $policy->getLimitations(); - } - - $limitationsCopied = []; - foreach ($copiedRole->getPolicies() as $policy) { - $limitationsCopied[$policy->function] = $policy->getLimitations(); - } - - $this->assertEquals($role->getPolicies(), $copiedRole->getPolicies()); - foreach ($limitations as $policy => $limitation) { - $this->assertEquals($limitation, $limitationsCopied[$policy]); - } - } - - /** - * Test for the loadRole() method. - * - * @see \eZ\Publish\API\Repository\RoleService::loadRole() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRole - */ - public function testLoadRole() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('roleName'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - - // Load the newly created role by its ID - $role = $roleService->loadRole($roleDraft->id); - - /* END: Use Case */ - - $this->assertEquals('roleName', $role->identifier); - } - - /** - * Test for the loadRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::loadRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRoleDraft - */ - public function testLoadRoleDraft() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('roleName'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - - // Load the newly created role by its ID - $role = $roleService->loadRoleDraft($roleDraft->id); - - /* END: Use Case */ - - $this->assertEquals('roleName', $role->identifier); - } - - public function testLoadRoleDraftByRoleId() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('roleName'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $role = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($role); - - // Now create a new draft based on the role - $newDraft = $roleService->createRoleDraft($role); - $loadedRoleDraft = $roleService->loadRoleDraftByRoleId($role->id); - - /* END: Use Case */ - - self::assertEquals('roleName', $role->identifier); - self::assertInstanceOf('eZ\Publish\API\Repository\Values\User\RoleDraft', $loadedRoleDraft); - self::assertEquals($newDraft, $loadedRoleDraft); - } - - /** - * Test for the loadRole() method. - * - * @see \eZ\Publish\API\Repository\RoleService::loadRole() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRole - */ - public function testLoadRoleThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - $nonExistingRoleId = $this->generateId('role', self::DB_INT_MAX); - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - - // This call will fail with a NotFoundException, because no such role exists. - $roleService->loadRole($nonExistingRoleId); - - /* END: Use Case */ - } - - /** - * Test for the loadRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::loadRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleDraft - */ - public function testLoadRoleDraftThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - $nonExistingRoleId = $this->generateId('role', self::DB_INT_MAX); - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - - // This call will fail with a NotFoundException, because no such role exists. - $roleService->loadRoleDraft($nonExistingRoleId); - - /* END: Use Case */ - } - - public function testLoadRoleDraftByRoleIdThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - $nonExistingRoleId = $this->generateId('role', self::DB_INT_MAX); - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - - // This call will fail with a NotFoundException, because no such role exists. - $roleService->loadRoleDraftByRoleId($nonExistingRoleId); - - /* END: Use Case */ - } - - /** - * Test for the loadRoleByIdentifier() method. - * - * @see \eZ\Publish\API\Repository\RoleService::loadRoleByIdentifier() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRole - */ - public function testLoadRoleByIdentifier() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('roleName'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - - // Load the newly created role by its identifier - $role = $roleService->loadRoleByIdentifier('roleName'); - - /* END: Use Case */ - - $this->assertEquals('roleName', $role->identifier); - } - - /** - * Test for the loadRoleByIdentifier() method. - * - * @see \eZ\Publish\API\Repository\RoleService::loadRoleByIdentifier() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleByIdentifier - */ - public function testLoadRoleByIdentifierThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - $roleService = $repository->getRoleService(); - - // This call will fail with a NotFoundException, because no such role exists. - $roleService->loadRoleByIdentifier('MissingRole'); - - /* END: Use Case */ - } - - /** - * Test for the loadRoles() method. - * - * @see \eZ\Publish\API\Repository\RoleService::loadRoles() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRole - */ - public function testLoadRoles() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - - // First create a custom role - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('roleName'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - - // Now load all available roles - $roles = $roleService->loadRoles(); - - foreach ($roles as $role) { - if ($role->identifier === 'roleName') { - break; - } - } - - /* END: Use Case */ - - $this->assertEquals('roleName', $role->identifier); - } - - /** - * Test for the loadRoles() method. - * - * @see \eZ\Publish\API\Repository\RoleService::loadRoles() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoles - */ - public function testLoadRolesReturnsExpectedSetOfDefaultRoles() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - $roles = $roleService->loadRoles(); - - $roleNames = []; - foreach ($roles as $role) { - $roleNames[] = $role->identifier; - } - /* END: Use Case */ - - $this->assertEqualsCanonicalizing( - [ - 'Administrator', - 'Anonymous', - 'Editor', - 'Member', - 'Partner', - ], - $roleNames - ); - } - - /** - * Test for the newRoleUpdateStruct() method. - * - * @see \eZ\Publish\API\Repository\RoleService::newRoleUpdateStruct() - */ - public function testNewRoleUpdateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - $roleUpdate = $roleService->newRoleUpdateStruct('newRole'); - /* END: Use Case */ - - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\User\\RoleUpdateStruct', $roleUpdate); - } - - /** - * Test for the updateRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::updateRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewRoleUpdateStruct - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleDraft - */ - public function testUpdateRoleDraft() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('newRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - - $roleUpdate = $roleService->newRoleUpdateStruct(); - $roleUpdate->identifier = 'updatedRole'; - - $updatedRole = $roleService->updateRoleDraft($roleDraft, $roleUpdate); - /* END: Use Case */ - - // Now verify that our change was saved - $role = $roleService->loadRoleDraft($updatedRole->id); - - $this->assertEquals($role->identifier, 'updatedRole'); - } - - /** - * Test for the updateRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::updateRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testUpdateRoleDraft - */ - public function testUpdateRoleDraftThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('newRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - - $roleUpdate = $roleService->newRoleUpdateStruct(); - $roleUpdate->identifier = 'Editor'; - - // This call will fail with an InvalidArgumentException, because Editor is a predefined role - $roleService->updateRoleDraft($roleDraft, $roleUpdate); - /* END: Use Case */ - } - - /** - * Test for the deleteRole() method. - * - * @see \eZ\Publish\API\Repository\RoleService::deleteRole() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRole - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoles - */ - public function testDeleteRole() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('newRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRole($roleDraft->id); - - $roleService->deleteRole($role); - /* END: Use Case */ - - $this->assertCount(5, $roleService->loadRoles()); - } - - /** - * Test for the deleteRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::deleteRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleDraft - */ - public function testDeleteRoleDraft() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('newRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - $roleID = $roleDraft->id; - $roleService->deleteRoleDraft($roleDraft); - - // This call will fail with a NotFoundException, because the draft no longer exists - $roleService->loadRoleDraft($roleID); - /* END: Use Case */ - } - - /** - * Test for the newPolicyCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\RoleService::newPolicyCreateStruct() - */ - public function testNewPolicyCreateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - $policyCreate = $roleService->newPolicyCreateStruct('content', 'create'); - /* END: Use Case */ - - $this->assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\User\\PolicyCreateStruct', $policyCreate); - } - - /** - * Test for the newPolicyCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\RoleService::newPolicyCreateStruct() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewPolicyCreateStruct - */ - public function testNewPolicyCreateStructSetsStructProperties() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - $policyCreate = $roleService->newPolicyCreateStruct('content', 'create'); - /* END: Use Case */ - - $this->assertEquals( - ['content', 'create'], - [$policyCreate->module, $policyCreate->function] - ); - } - - /** - * Test for the addPolicyByRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::addPolicyByRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRoleDraft - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewPolicyCreateStruct - */ - public function testAddPolicyByRoleDraft() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - $roleCreate = $roleService->newRoleCreateStruct('newRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - - $roleDraft = $roleService->addPolicyByRoleDraft( - $roleDraft, - $roleService->newPolicyCreateStruct('content', 'delete') - ); - $roleDraft = $roleService->addPolicyByRoleDraft( - $roleDraft, - $roleService->newPolicyCreateStruct('content', 'create') - ); - /* END: Use Case */ - - $actual = []; - foreach ($roleDraft->getPolicies() as $policy) { - $actual[] = [ - 'module' => $policy->module, - 'function' => $policy->function, - ]; - } - usort( - $actual, - static function ($p1, $p2) { - return strcasecmp($p1['function'], $p2['function']); - } - ); - - $this->assertEquals( - [ - [ - 'module' => 'content', - 'function' => 'create', - ], - [ - 'module' => 'content', - 'function' => 'delete', - ], - ], - $actual - ); - } - - /** - * Test for the addPolicyByRoleDraft() method. - * - * @return array [\eZ\Publish\API\Repository\Values\User\RoleDraft, \eZ\Publish\API\Repository\Values\User\Policy] - * - * @see \eZ\Publish\API\Repository\RoleService::addPolicyByRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAddPolicyByRoleDraft - */ - public function testAddPolicyByRoleDraftUpdatesRole() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - $roleCreate = $roleService->newRoleCreateStruct('newRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - - $policyCreate = $roleService->newPolicyCreateStruct('content', 'create'); - $roleDraft = $roleService->addPolicyByRoleDraft($roleDraft, $policyCreate); - - $policy = null; - foreach ($roleDraft->getPolicies() as $policy) { - if ($policy->module === 'content' && $policy->function === 'create') { - break; - } - } - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\Policy', - $policy - ); - - return [$roleDraft, $policy]; - } - - /** - * Test for the addPolicyByRoleDraft() method. - * - * @param array $roleAndPolicy - * - * @see \eZ\Publish\API\Repository\RoleService::addPolicyByRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAddPolicyByRoleDraftUpdatesRole - */ - public function testAddPolicyByRoleDraftSetsPolicyProperties($roleAndPolicy) - { - list($role, $policy) = $roleAndPolicy; - - $this->assertEquals( - [$role->id, 'content', 'create'], - [$policy->roleId, $policy->module, $policy->function] - ); - } - - /** - * Test for the addPolicyByRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::addPolicyByRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewPolicyCreateStruct - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRoleDraft - */ - public function testAddPolicyByRoleDraftThrowsLimitationValidationException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\LimitationValidationException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - $roleCreate = $roleService->newRoleCreateStruct('Lumberjack'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - - // Create new subtree limitation - $limitation = new SubtreeLimitation( - [ - 'limitationValues' => ['/mountain/forest/tree/42/'], - ] - ); - - // Create policy create struct and add limitation to it - $policyCreateStruct = $roleService->newPolicyCreateStruct('content', 'remove'); - $policyCreateStruct->addLimitation($limitation); - - // This call will fail with an LimitationValidationException, because subtree - // "/mountain/forest/tree/42/" does not exist - $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct); - /* END: Use Case */ - } - - /** - * Test for the createRole() method. - * - * @see \eZ\Publish\API\Repository\RoleService::createRole() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAddPolicyByRoleDraftUpdatesRole - */ - public function testCreateRoleWithAddPolicy() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - // Instantiate a new create struct - $roleCreate = $roleService->newRoleCreateStruct('newRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - // Add some role policies - $roleCreate->addPolicy( - $roleService->newPolicyCreateStruct( - 'content', - 'read' - ) - ); - $roleCreate->addPolicy( - $roleService->newPolicyCreateStruct( - 'content', - 'translate' - ) - ); - - // Create new role instance - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRole($roleDraft->id); - - $policies = []; - foreach ($role->getPolicies() as $policy) { - $policies[] = ['module' => $policy->module, 'function' => $policy->function]; - } - /* END: Use Case */ - array_multisort($policies); - - $this->assertEquals( - [ - [ - 'module' => 'content', - 'function' => 'read', - ], - [ - 'module' => 'content', - 'function' => 'translate', - ], - ], - $policies - ); - } - - /** - * Test for the createRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::createRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAddPolicyByRoleDraftUpdatesRole - */ - public function testCreateRoleDraftWithAddPolicy() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - // Instantiate a new create struct - $roleCreate = $roleService->newRoleCreateStruct('newRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - // Add some role policies - $roleCreate->addPolicy( - $roleService->newPolicyCreateStruct( - 'content', - 'read' - ) - ); - $roleCreate->addPolicy( - $roleService->newPolicyCreateStruct( - 'content', - 'translate' - ) - ); - - // Create new role instance - $roleDraft = $roleService->createRole($roleCreate); - - $policies = []; - foreach ($roleDraft->getPolicies() as $policy) { - $policies[] = ['module' => $policy->module, 'function' => $policy->function]; - } - /* END: Use Case */ - - $this->assertEquals( - [ - [ - 'module' => 'content', - 'function' => 'read', - ], - [ - 'module' => 'content', - 'function' => 'translate', - ], - ], - $policies - ); - } - - /** - * Test for the newPolicyUpdateStruct() method. - * - * @see \eZ\Publish\API\Repository\RoleService::newPolicyUpdateStruct() - */ - public function testNewPolicyUpdateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - $policyUpdate = $roleService->newPolicyUpdateStruct(); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\PolicyUpdateStruct', - $policyUpdate - ); - } - - public function testUpdatePolicyByRoleDraftNoLimitation() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - // Instantiate new policy create - $policyCreate = $roleService->newPolicyCreateStruct('foo', 'bar'); - - // Instantiate a role create and add the policy create - $roleCreate = $roleService->newRoleCreateStruct('myRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleCreate->addPolicy($policyCreate); - - // Create a new role instance. - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRole($roleDraft->id); - - $roleDraft = $roleService->createRoleDraft($role); - // Search for the new policy instance - $policy = null; - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy */ - foreach ($roleDraft->getPolicies() as $policy) { - if ($policy->module === 'foo' && $policy->function === 'bar') { - break; - } - } - - // Create an update struct - $policyUpdate = $roleService->newPolicyUpdateStruct(); - - // Update the the policy - $policy = $roleService->updatePolicyByRoleDraft( - $roleDraft, - $policy, - $policyUpdate - ); - $roleService->publishRoleDraft($roleDraft); - - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\Policy', - $policy - ); - - self::assertEquals([], $policy->getLimitations()); - } - - /** - * @return array - * - * @see \eZ\Publish\API\Repository\RoleService::updatePolicyByRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAddPolicyByRoleDraft - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewPolicyUpdateStruct - */ - public function testUpdatePolicyByRoleDraft() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - // Instantiate new policy create - $policyCreate = $roleService->newPolicyCreateStruct('content', 'translate'); - - // Add some limitations for the new policy - $policyCreate->addLimitation( - new LanguageLimitation( - [ - 'limitationValues' => ['eng-US', 'eng-GB'], - ] - ) - ); - - // Instantiate a role create and add the policy create - $roleCreate = $roleService->newRoleCreateStruct('myRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleCreate->addPolicy($policyCreate); - - // Create a new role instance. - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRole($roleDraft->id); - - $roleDraft = $roleService->createRoleDraft($role); - // Search for the new policy instance - $policy = null; - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy */ - foreach ($roleDraft->getPolicies() as $policy) { - if ($policy->module === 'content' && $policy->function === 'translate') { - break; - } - } - - // Create an update struct and set a modified limitation - $policyUpdate = $roleService->newPolicyUpdateStruct(); - $policyUpdate->addLimitation( - new ContentTypeLimitation( - [ - 'limitationValues' => [29, 30], - ] - ) - ); - - // Update the the policy - $policy = $roleService->updatePolicyByRoleDraft( - $roleDraft, - $policy, - $policyUpdate - ); - $roleService->publishRoleDraft($roleDraft); - - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\Policy', - $policy - ); - - return [$roleService->loadRole($role->id), $policy]; - } - - /** - * @param array $roleAndPolicy - * - * @see \eZ\Publish\API\Repository\RoleService::testUpdatePolicyByRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testUpdatePolicyByRoleDraft - */ - public function testUpdatePolicyUpdatesLimitations($roleAndPolicy) - { - list($role, $policy) = $roleAndPolicy; - - $this->assertEquals( - [ - new ContentTypeLimitation( - [ - 'limitationValues' => [29, 30], - ] - ), - ], - $policy->getLimitations() - ); - - return $role; - } - - /** - * Test for the updatePolicy() method. - * - * @param \eZ\Publish\API\Repository\Values\User\Role $role - * - * @see \eZ\Publish\API\Repository\RoleService::updatePolicyByRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testUpdatePolicyUpdatesLimitations - */ - public function testUpdatePolicyUpdatesRole($role) - { - $limitations = []; - foreach ($role->getPolicies() as $policy) { - foreach ($policy->getLimitations() as $limitation) { - $limitations[] = $limitation; - } - } - - $this->assertCount(1, $limitations); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\Limitation', - $limitations[0] - ); - - $expectedData = [ - 'limitationValues' => [29, 30], - ]; - $this->assertPropertiesCorrectUnsorted( - $expectedData, - $limitations[0] - ); - } - - /** - * Test for the updatePolicy() method. - * - * @see \eZ\Publish\API\Repository\RoleService::updatePolicyByRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAddPolicyByRoleDraft - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewPolicyCreateStruct - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewPolicyUpdateStruct - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testNewRoleCreateStruct - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRole - */ - public function testUpdatePolicyByRoleDraftThrowsLimitationValidationException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\LimitationValidationException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - // Instantiate new policy create - $policyCreate = $roleService->newPolicyCreateStruct('content', 'remove'); - - // Add some limitations for the new policy - $policyCreate->addLimitation( - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/2/'], - ] - ) - ); - - // Instantiate a role create and add the policy create - $roleCreate = $roleService->newRoleCreateStruct('myRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleCreate->addPolicy($policyCreate); - - // Create a new role instance. - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRole($roleDraft->id); - $roleDraft = $roleService->createRoleDraft($role); - // Search for the new policy instance - $policy = null; - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy */ - foreach ($roleDraft->getPolicies() as $policy) { - if ($policy->module === 'content' && $policy->function === 'remove') { - break; - } - } - - // Create an update struct and set a modified limitation - $policyUpdate = $roleService->newPolicyUpdateStruct(); - $policyUpdate->addLimitation( - new SubtreeLimitation( - [ - 'limitationValues' => ['/mountain/forest/tree/42/'], - ] - ) - ); - - // This call will fail with an LimitationValidationException, because subtree - // "/mountain/forest/tree/42/" does not exist - $policy = $roleService->updatePolicyByRoleDraft( - $roleDraft, - $policy, - $policyUpdate - ); - /* END: Use Case */ - } - - /** - * Test for the removePolicyByRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::removePolicyByRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAddPolicyByRoleDraft - */ - public function testRemovePolicyByRoleDraft() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - // Instantiate a new role create - $roleCreate = $roleService->newRoleCreateStruct('newRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - // Create a new role with two policies - $roleDraft = $roleService->createRole($roleCreate); - $roleService->addPolicyByRoleDraft( - $roleDraft, - $roleService->newPolicyCreateStruct('content', 'create') - ); - $roleService->addPolicyByRoleDraft( - $roleDraft, - $roleService->newPolicyCreateStruct('content', 'delete') - ); - - // Delete all policies from the new role - foreach ($roleDraft->getPolicies() as $policy) { - $roleDraft = $roleService->removePolicyByRoleDraft($roleDraft, $policy); - } - /* END: Use Case */ - - $this->assertSame([], $roleDraft->getPolicies()); - } - - /** - * Test for the addPolicyByRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::addPolicyByRoleDraft() - */ - public function testAddPolicyWithRoleAssignment() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - $userService = $repository->getUserService(); - - /* Create new user group */ - $mainGroupId = $this->generateId('group', 4); - $parentUserGroup = $userService->loadUserGroup($mainGroupId); - $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); - $userGroupCreate->setField('name', 'newUserGroup'); - $userGroup = $userService->createUserGroup($userGroupCreate, $parentUserGroup); - - /* Create Role */ - $roleCreate = $roleService->newRoleCreateStruct('newRole'); - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - - $role = $roleService->loadRole($roleDraft->id); - $roleService->assignRoleToUserGroup($role, $userGroup); - - $roleAssignmentsBeforeNewPolicy = $roleService->getRoleAssignments($role)[0]; - - /* Add new policy to existing role */ - $roleUpdateDraft = $roleService->createRoleDraft($role); - $roleUpdateDraft = $roleService->addPolicyByRoleDraft( - $roleUpdateDraft, - $roleService->newPolicyCreateStruct('content', 'create') - ); - $roleService->publishRoleDraft($roleUpdateDraft); - - $roleAfterUpdate = $roleService->loadRole($role->id); - $roleAssignmentsAfterNewPolicy = $roleService->getRoleAssignments($roleAfterUpdate)[0]; - /* END: Use Case */ - - $this->assertNotEquals($roleAssignmentsBeforeNewPolicy->id, $roleAssignmentsAfterNewPolicy->id); - } - - /** - * Test loading user/group role assignments. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroupRoleAssignment - * - * @covers \eZ\Publish\API\Repository\RoleService::loadRoleAssignment - */ - public function testLoadRoleAssignment() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - $user = $repository->getUserService()->loadUser(14); - - // Check initial empty assignments (also warms up potential cache to validate it is correct below) - $this->assertCount(0, $roleService->getRoleAssignmentsForUser($user)); - - // Assignment to user group - $groupRoleAssignment = $roleService->loadRoleAssignment(25); - - // Assignment to user - $role = $roleService->loadRole(2); - $roleService->assignRoleToUser($role, $user); - $userRoleAssignments = $roleService->getRoleAssignmentsForUser($user); - - $userRoleAssignment = $roleService->loadRoleAssignment($userRoleAssignments[0]->id); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\UserGroupRoleAssignment', - $groupRoleAssignment - ); - - $this->assertEquals( - [ - 12, - 2, - 25, - ], - [ - $groupRoleAssignment->userGroup->id, - $groupRoleAssignment->role->id, - $groupRoleAssignment->id, - ] - ); - - self::assertInstanceOf('\\eZ\\Publish\\API\\Repository\\Values\\User\\UserRoleAssignment', $userRoleAssignment); - self::assertEquals(14, $userRoleAssignment->user->id); - - return $groupRoleAssignment; - } - - /** - * Test for the getRoleAssignments() method. - * - * @return \eZ\Publish\API\Repository\Values\User\RoleAssignment[] - * - * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignments() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleByIdentifier - */ - public function testGetRoleAssignments() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - // Load the editor role - $role = $roleService->loadRoleByIdentifier('Editor'); - - // Load all assigned users and user groups - $roleAssignments = $roleService->getRoleAssignments($role); - - /* END: Use Case */ - - $this->assertCount(2, $roleAssignments); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\UserGroupRoleAssignment', - $roleAssignments[0] - ); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\UserGroupRoleAssignment', - $roleAssignments[1] - ); - - return $roleAssignments; - } - - /** - * Test for the getRoleAssignments() method. - * - * @param \eZ\Publish\API\Repository\Values\User\RoleAssignment[] $roleAssignments - * - * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignments() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testGetRoleAssignments - */ - public function testGetRoleAssignmentsContainExpectedLimitation(array $roleAssignments) - { - $this->assertEquals( - 'Subtree', - reset($roleAssignments)->limitation->getIdentifier() - ); - } - - /** - * @covers \eZ\Publish\API\Repository\RoleService::loadRoleAssignments() - */ - public function testLoadRoleAssignments(): void - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - $role = $this->createRoleWithPolicies('testLoadRoleAssignments', []); - $user = $this->createUser('test', 'Test', 'Test'); - $user2 = $this->createUser('test2', 'Test2', 'Test2'); - - $roleService->assignRoleToUser($role, $user); - $roleService->assignRoleToUser($role, $user2); - - $loadedRole = $roleService->loadRole($role->id); - - $roleAssignments = $roleService->loadRoleAssignments($loadedRole, 0, 1); - - self::assertCount(1, $roleAssignments); - self::assertInstanceOf(UserRoleAssignment::class, $roleAssignments[0]); - } - - /** - * @covers \eZ\Publish\API\Repository\RoleService::countRoleAssignments() - */ - public function testLoadRoleAssignmentsWithDeletedUser(): void - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - $userService = $repository->getUserService(); - - $role = $roleService->loadRoleByIdentifier('Editor'); - $roleAssignments = $roleService->loadRoleAssignments($role); - $expectedCount = count($roleAssignments); - - // Adding user should add '1' to the assignments count - $newUser = $this->createUser('login', 'Test', 'Test'); - $roleService->assignRoleToUser($role, $newUser); - ++$expectedCount; - - $roleAssignments = $roleService->loadRoleAssignments($role); - - self::assertCount($expectedCount, $roleAssignments); - - // Removing user should subtract '1' from the assignments count - $userService->deleteUser($newUser); - --$expectedCount; - - $roleAssignments = $roleService->loadRoleAssignments($role); - - self::assertCount($expectedCount, $roleAssignments); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\Exception - */ - public function testCountRoleAssignmentsAfterRemovingRoleAssignment(): void - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - $role = $roleService->loadRoleByIdentifier('Editor'); - $newUser = $this->createUser('login', 'Test', 'Test'); - $roleService->assignRoleToUser($role, $newUser); - $roleAssignmentsCount = $roleService->countRoleAssignments($role); - - $userRoleAssignment = $this->loadRoleAssignmentForUser($roleService, $role, $newUser); - $roleService->removeRoleAssignment($userRoleAssignment); - - self::assertEquals($roleAssignmentsCount - 1, $roleService->countRoleAssignments($role)); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\Exception - */ - public function testCountRoleAssignmentsAfterDeletingUser(): void - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - $userService = $repository->getUserService(); - - $role = $roleService->loadRoleByIdentifier('Editor'); - $newUser = $this->createUser('login', 'Test', 'Test'); - $roleService->assignRoleToUser($role, $newUser); - - $roleAssignmentsCount = $roleService->countRoleAssignments($role); - - $userService->deleteUser($newUser); - $afterUserDeleteCount = $roleService->countRoleAssignments($role); - - self::assertEquals($roleAssignmentsCount - 1, $afterUserDeleteCount); - } - - /** - * Test for the assignRoleToUser() method. - * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUser() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testGetRoleAssignments - */ - public function testAssignRoleToUser() - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Load the existing "Administrator" role - $role = $roleService->loadRoleByIdentifier('Administrator'); - - // Assign the "Administrator" role to the newly created user - $roleService->assignRoleToUser($role, $user); - - // The assignments array will contain the new role<->user assignment - $roleAssignments = $roleService->getRoleAssignments($role); - /* END: Use Case */ - - // Administrator + Example User - $this->assertCount(2, $roleAssignments); - } - - /** - * Test for the assignRoleToUser() method. - * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUser($role, $user, $roleLimitation) - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUser - */ - public function testAssignRoleToUserWithRoleLimitation() - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Load the existing "Anonymous" role - $role = $roleService->loadRoleByIdentifier('Anonymous'); - - // Assign the "Anonymous" role to the newly created user - $roleService->assignRoleToUser( - $role, - $user, - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/43/'], - ] - ) - ); - - // The assignments array will contain the new role<->user assignment - $roleAssignments = $roleService->getRoleAssignments($role); - /* END: Use Case */ - - // Members + Partners + Anonymous + Example User - $this->assertCount(4, $roleAssignments); - - // Get the role limitation - $roleLimitation = null; - foreach ($roleAssignments as $roleAssignment) { - $roleLimitation = $roleAssignment->getRoleLimitation(); - if ($roleLimitation) { - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\UserRoleAssignment', - $roleAssignment - ); - break; - } - } - - $this->assertEquals( - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/43/'], - ] - ), - $roleLimitation - ); - - // Test again to see values being merged - $roleService->assignRoleToUser( - $role, - $user, - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/43/', '/1/2/'], - ] - ) - ); - - // The assignments array will contain the new role<->user assignment - $roleAssignments = $roleService->getRoleAssignments($role); - - // Members + Partners + Anonymous + Example User - $this->assertCount(5, $roleAssignments); - - // Get the role limitation - $roleLimitations = []; - foreach ($roleAssignments as $roleAssignment) { - $roleLimitation = $roleAssignment->getRoleLimitation(); - if ($roleLimitation) { - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\UserRoleAssignment', - $roleAssignment - ); - $roleLimitations[] = $roleLimitation; - } - } - array_multisort($roleLimitations); - - $this->assertEquals( - [ - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/2/'], - ] - ), - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/43/'], - ] - ), - ], - $roleLimitations - ); - } - - /** - * Test for the assignRoleToUser() method. - * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUser($role, $user, $roleLimitation) - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUser - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleByIdentifier - */ - public function testAssignRoleToUserWithRoleLimitationThrowsLimitationValidationException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\LimitationValidationException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - // Load the existing "Anonymous" role - $role = $roleService->loadRoleByIdentifier('Anonymous'); - - // Get current user - $permissionResolver = $this->getRepository()->getPermissionResolver(); - $userService = $repository->getUserService(); - $currentUser = $userService->loadUser($permissionResolver->getCurrentUserReference()->getUserId()); - - // Assign the "Anonymous" role to the current user - // This call will fail with an LimitationValidationException, because subtree "/lorem/ipsum/42/" - // does not exists - $roleService->assignRoleToUser( - $role, - $currentUser, - new SubtreeLimitation( - [ - 'limitationValues' => ['/lorem/ipsum/42/'], - ] - ) - ); - /* END: Use Case */ - } - - /** - * Test for the assignRoleToUser() method. - * - * Makes sure assigning role several times throws. - * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUser($role, $user, $roleLimitation) - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUser - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleByIdentifier - */ - public function testAssignRoleToUserThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - // Load the existing "Anonymous" role - $role = $roleService->loadRoleByIdentifier('Anonymous'); - - // Get current user - $permissionResolver = $this->getRepository()->getPermissionResolver(); - $userService = $repository->getUserService(); - $currentUser = $userService->loadUser($permissionResolver->getCurrentUserReference()->getUserId()); - - // Assign the "Anonymous" role to the current user - try { - $roleService->assignRoleToUser( - $role, - $currentUser - ); - } catch (Exception $e) { - $this->fail('Got exception at first valid attempt to assign role'); - } - - // Re-Assign the "Anonymous" role to the current user - // This call will fail with an InvalidArgumentException, because limitation is already assigned - $roleService->assignRoleToUser( - $role, - $currentUser - ); - /* END: Use Case */ - } - - /** - * Test for the assignRoleToUser() method. - * - * Makes sure assigning role several times with same limitations throws. - * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUser($role, $user, $roleLimitation) - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUser - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleByIdentifier - */ - public function testAssignRoleToUserWithRoleLimitationThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - - // Load the existing "Anonymous" role - $role = $roleService->loadRoleByIdentifier('Anonymous'); - - // Get current user - $permissionResolver = $this->getRepository()->getPermissionResolver(); - $userService = $repository->getUserService(); - $currentUser = $userService->loadUser($permissionResolver->getCurrentUserReference()->getUserId()); - - // Assign the "Anonymous" role to the current user - try { - $roleService->assignRoleToUser( - $role, - $currentUser, - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/43/', '/1/2/'], - ] - ) - ); - } catch (Exception $e) { - $this->fail('Got exception at first valid attempt to assign role'); - } - - // Re-Assign the "Anonymous" role to the current user - // This call will fail with an InvalidArgumentException, because limitation is already assigned - $roleService->assignRoleToUser( - $role, - $currentUser, - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/43/'], - ] - ) - ); - /* END: Use Case */ - } - - /** - * Test for the removeRoleAssignment() method. - * - * @see \eZ\Publish\API\Repository\RoleService::removeRoleAssignment() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUser - */ - public function testRemoveRoleAssignment() - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Load the existing "Member" role - $role = $roleService->loadRoleByIdentifier('Member'); - - // Assign the "Member" role to the newly created user - $roleService->assignRoleToUser($role, $user); - - // Unassign user from role - $roleAssignments = $roleService->getRoleAssignmentsForUser($user); - foreach ($roleAssignments as $roleAssignment) { - if ($roleAssignment->role->id === $role->id) { - $roleService->removeRoleAssignment($roleAssignment); - } - } - // The assignments array will not contain the new role<->user assignment - $roleAssignments = $roleService->getRoleAssignments($role); - /* END: Use Case */ - - // Members + Editors + Partners - $this->assertCount(3, $roleAssignments); - } - - /** - * Test for the getRoleAssignmentsForUser() method. - * - * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignmentsForUser() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUser - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRoleWithAddPolicy - */ - public function testGetRoleAssignmentsForUserDirect() - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Instantiate a role create and add some policies - $roleCreate = $roleService->newRoleCreateStruct('Example Role'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleCreate->addPolicy( - $roleService->newPolicyCreateStruct('user', 'login') - ); - $roleCreate->addPolicy( - $roleService->newPolicyCreateStruct('content', 'read') - ); - $roleCreate->addPolicy( - $roleService->newPolicyCreateStruct('content', 'edit') - ); - - // Create the new role instance - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRole($roleDraft->id); - - // Check inital empty assigments (also warms up potential cache to validate it is correct below) - $this->assertCount(0, $roleService->getRoleAssignmentsForUser($user)); - $this->assertCount(0, $roleService->getRoleAssignments($role)); - - // Assign role to new user - $roleService->assignRoleToUser($role, $user); - - // Load the currently assigned role - $roleAssignments = $roleService->getRoleAssignmentsForUser($user); - /* END: Use Case */ - - $this->assertCount(1, $roleAssignments); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\UserRoleAssignment', - reset($roleAssignments) - ); - $this->assertCount(1, $roleService->getRoleAssignments($role)); - } - - /** - * Test for the getRoleAssignmentsForUser() method. - * - * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignmentsForUser() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUser - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRoleWithAddPolicy - */ - public function testGetRoleAssignmentsForUserEmpty() - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - /* BEGIN: Use Case */ - $permissionResolver = $this->getRepository()->getPermissionResolver(); - $userService = $repository->getUserService(); - $adminUser = $userService->loadUser($permissionResolver->getCurrentUserReference()->getUserId()); - - // Load the currently assigned role - $roleAssignments = $roleService->getRoleAssignmentsForUser($adminUser); - /* END: Use Case */ - - $this->assertCount(0, $roleAssignments); - } - - /** - * Test for the getRoleAssignmentsForUser() method. - * - * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignmentsForUser() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUser - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRoleWithAddPolicy - */ - public function testGetRoleAssignmentsForUserInherited() - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - /* BEGIN: Use Case */ - $permissionResolver = $this->getRepository()->getPermissionResolver(); - $userService = $repository->getUserService(); - $adminUser = $userService->loadUser($permissionResolver->getCurrentUserReference()->getUserId()); - - // Load the currently assigned role + inherited role assignments - $roleAssignments = $roleService->getRoleAssignmentsForUser($adminUser, true); - /* END: Use Case */ - - $this->assertCount(1, $roleAssignments); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\UserGroupRoleAssignment', - reset($roleAssignments) - ); - } - - /** - * Test for the assignRoleToUserGroup() method. - * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUserGroup() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testGetRoleAssignments - */ - public function testAssignRoleToUserGroup() - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - /* BEGIN: Use Case */ - $userGroup = $this->createUserGroupVersion1(); - - // Load the existing "Administrator" role - $role = $roleService->loadRoleByIdentifier('Administrator'); - - // Assign the "Administrator" role to the newly created user group - $roleService->assignRoleToUserGroup($role, $userGroup); - - // The assignments array will contain the new role<->group assignment - $roleAssignments = $roleService->getRoleAssignments($role); - /* END: Use Case */ - - // Administrator + Example Group - $this->assertCount(2, $roleAssignments); - } - - /** - * Test for the assignRoleToUserGroup() method. - * - * Related issue: EZP-29113 - * - * @covers \eZ\Publish\API\Repository\RoleService::assignRoleToUserGroup() - */ - public function testAssignRoleToUserGroupAffectsRoleAssignmentsForUser() - { - $roleService = $this->getRepository()->getRoleService(); - - /* BEGIN: Use Case */ - $userGroup = $this->createUserGroupVersion1(); - $user = $this->createUser('user', 'John', 'Doe', $userGroup); - - $initRoleAssignments = $roleService->getRoleAssignmentsForUser($user, true); - - // Load the existing "Administrator" role - $role = $roleService->loadRoleByIdentifier('Administrator'); - - // Assign the "Administrator" role to the newly created user group - $roleService->assignRoleToUserGroup($role, $userGroup); - - $updatedRoleAssignments = $roleService->getRoleAssignmentsForUser($user, true); - /* END: Use Case */ - - $this->assertEmpty($initRoleAssignments); - $this->assertCount(1, $updatedRoleAssignments); - } - - /** - * Test for the assignRoleToUserGroup() method. - * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUserGroup($role, $userGroup, $roleLimitation) - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUserGroup - */ - public function testAssignRoleToUserGroupWithRoleLimitation() - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - /* BEGIN: Use Case */ - $userGroup = $this->createUserGroupVersion1(); - - // Load the existing "Anonymous" role - $role = $roleService->loadRoleByIdentifier('Anonymous'); - - // Assign the "Anonymous" role to the newly created user group - $roleService->assignRoleToUserGroup( - $role, - $userGroup, - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/43/'], - ] - ) - ); - - // The assignments array will contain the new role<->group assignment - $roleAssignments = $roleService->getRoleAssignments($role); - /* END: Use Case */ - - // Members + Partners + Anonymous + Example Group - $this->assertCount(4, $roleAssignments); - - // Get the role limitation - $roleLimitation = null; - foreach ($roleAssignments as $roleAssignment) { - $roleLimitation = $roleAssignment->getRoleLimitation(); - if ($roleLimitation) { - break; - } - } - - $this->assertEquals( - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/43/'], - ] - ), - $roleLimitation - ); - - // Test again to see values being merged - $roleService->assignRoleToUserGroup( - $role, - $userGroup, - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/43/', '/1/2/'], - ] - ) - ); - - // The assignments array will contain the new role<->user assignment - $roleAssignments = $roleService->getRoleAssignments($role); - - // Members + Partners + Anonymous + Example User - $this->assertCount(5, $roleAssignments); - - // Get the role limitation - $roleLimitations = []; - foreach ($roleAssignments as $roleAssignment) { - $roleLimitation = $roleAssignment->getRoleLimitation(); - if ($roleLimitation) { - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\UserGroupRoleAssignment', - $roleAssignment - ); - $roleLimitations[] = $roleLimitation; - } - } - array_multisort($roleLimitations); - - $this->assertEquals( - [ - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/2/'], - ] - ), - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/43/'], - ] - ), - ], - $roleLimitations - ); - } - - /** - * Test for the assignRoleToUserGroup() method. - * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUserGroup($role, $userGroup, $roleLimitation) - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleByIdentifier - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUserGroup - */ - public function testAssignRoleToUserGroupWithRoleLimitationThrowsLimitationValidationException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\LimitationValidationException::class); - - $repository = $this->getRepository(); - - $mainGroupId = $this->generateId('group', 4); - /* BEGIN: Use Case */ - // $mainGroupId is the ID of the main "Users" group - - $userService = $repository->getUserService(); - $roleService = $repository->getRoleService(); - - $userGroup = $userService->loadUserGroup($mainGroupId); - - // Load the existing "Anonymous" role - $role = $roleService->loadRoleByIdentifier('Anonymous'); - - // Assign the "Anonymous" role to the newly created user group - // This call will fail with an LimitationValidationException, because subtree "/lorem/ipsum/42/" - // does not exists - $roleService->assignRoleToUserGroup( - $role, - $userGroup, - new SubtreeLimitation( - [ - 'limitationValues' => ['/lorem/ipsum/42/'], - ] - ) - ); - /* END: Use Case */ - } - - /** - * Test for the assignRoleToUserGroup() method. - * - * Makes sure assigning role several times throws. - * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUserGroup($role, $userGroup, $roleLimitation) - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleByIdentifier - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUserGroup - */ - public function testAssignRoleToUserGroupThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $mainGroupId = $this->generateId('group', 4); - /* BEGIN: Use Case */ - // $mainGroupId is the ID of the main "Users" group - - $userService = $repository->getUserService(); - $roleService = $repository->getRoleService(); - - $userGroup = $userService->loadUserGroup($mainGroupId); - - // Load the existing "Anonymous" role - $role = $roleService->loadRoleByIdentifier('Anonymous'); - - // Assign the "Anonymous" role to the newly created user group - try { - $roleService->assignRoleToUserGroup( - $role, - $userGroup - ); - } catch (Exception $e) { - $this->fail('Got exception at first valid attempt to assign role'); - } - - // Re-Assign the "Anonymous" role to the newly created user group - // This call will fail with an InvalidArgumentException, because role is already assigned - $roleService->assignRoleToUserGroup( - $role, - $userGroup - ); - /* END: Use Case */ - } - - /** - * Test for the assignRoleToUserGroup() method. - * - * Makes sure assigning role several times with same limitations throws. - * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUserGroup($role, $userGroup, $roleLimitation) - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleByIdentifier - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUserGroup - */ - public function testAssignRoleToUserGroupWithRoleLimitationThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $mainGroupId = $this->generateId('group', 4); - /* BEGIN: Use Case */ - // $mainGroupId is the ID of the main "Users" group - - $userService = $repository->getUserService(); - $roleService = $repository->getRoleService(); - - $userGroup = $userService->loadUserGroup($mainGroupId); - - // Load the existing "Anonymous" role - $role = $roleService->loadRoleByIdentifier('Anonymous'); - - // Assign the "Anonymous" role to the newly created user group - try { - $roleService->assignRoleToUserGroup( - $role, - $userGroup, - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/43/', '/1/2/'], - ] - ) - ); - } catch (Exception $e) { - $this->fail('Got exception at first valid attempt to assign role'); - } - - // Re-Assign the "Anonymous" role to the newly created user group - // This call will fail with an InvalidArgumentException, because limitation is already assigned - $roleService->assignRoleToUserGroup( - $role, - $userGroup, - new SubtreeLimitation( - [ - 'limitationValues' => ['/1/43/'], - ] - ) - ); - /* END: Use Case */ - } - - /** - * Test for the removeRoleAssignment() method. - * - * @see \eZ\Publish\API\Repository\RoleService::removeRoleAssignment() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUserGroup - */ - public function testRemoveRoleAssignmentFromUserGroup() - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - /* BEGIN: Use Case */ - $userGroup = $this->createUserGroupVersion1(); - - // Load the existing "Member" role - $role = $roleService->loadRoleByIdentifier('Member'); - - // Assign the "Member" role to the newly created user group - $roleService->assignRoleToUserGroup($role, $userGroup); - - // Unassign group from role - $roleAssignments = $roleService->getRoleAssignmentsForUserGroup($userGroup); - - // This call will fail with an "UnauthorizedException" - foreach ($roleAssignments as $roleAssignment) { - if ($roleAssignment->role->id === $role->id) { - $roleService->removeRoleAssignment($roleAssignment); - } - } - // The assignments array will not contain the new role<->group assignment - $roleAssignments = $roleService->getRoleAssignments($role); - /* END: Use Case */ - - // Members + Editors + Partners - $this->assertCount(3, $roleAssignments); - } - - /** - * Test unassigning role by assignment. - * - * @covers \eZ\Publish\API\Repository\RoleService::removeRoleAssignment - */ - public function testUnassignRoleByAssignment() - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - $role = $roleService->loadRole(2); - $user = $repository->getUserService()->loadUser(14); - - $originalAssignmentCount = count($roleService->getRoleAssignmentsForUser($user)); - - $roleService->assignRoleToUser($role, $user); - $newAssignmentCount = count($roleService->getRoleAssignmentsForUser($user)); - self::assertEquals($originalAssignmentCount + 1, $newAssignmentCount); - - $assignments = $roleService->getRoleAssignmentsForUser($user); - $roleService->removeRoleAssignment($assignments[0]); - $finalAssignmentCount = count($roleService->getRoleAssignmentsForUser($user)); - self::assertEquals($newAssignmentCount - 1, $finalAssignmentCount); - } - - /** - * Test unassigning role by assignment. - * - * But on current admin user so he lacks access to read roles. - * - * @covers \eZ\Publish\API\Repository\RoleService::removeRoleAssignment - */ - public function testUnassignRoleByAssignmentThrowsUnauthorizedException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); - - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - try { - $adminUserGroup = $repository->getUserService()->loadUserGroup(12); - $assignments = $roleService->getRoleAssignmentsForUserGroup($adminUserGroup); - $roleService->removeRoleAssignment($assignments[0]); - } catch (Exception $e) { - self::fail( - 'Unexpected exception: ' . $e->getMessage() . " \n[" . $e->getFile() . ' (' . $e->getLine() . ')]' - ); - } - - $roleService->removeRoleAssignment($assignments[0]); - } - - /** - * Test unassigning role by non-existing assignment. - * - * @covers \eZ\Publish\API\Repository\RoleService::removeRoleAssignment - */ - public function testUnassignRoleByAssignmentThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - try { - $editorsUserGroup = $repository->getUserService()->loadUserGroup(13); - $assignments = $roleService->getRoleAssignmentsForUserGroup($editorsUserGroup); - $roleService->removeRoleAssignment($assignments[0]); - } catch (Exception $e) { - self::fail( - 'Unexpected exception: ' . $e->getMessage() . " \n[" . $e->getFile() . ' (' . $e->getLine() . ')]' - ); - } - - $roleService->removeRoleAssignment($assignments[0]); - } - - /** - * Test for the getRoleAssignmentsForUserGroup() method. - * - * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignmentsForUserGroup() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUserGroup - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRoleWithAddPolicy - */ - public function testGetRoleAssignmentsForUserGroup() - { - $repository = $this->getRepository(); - $roleService = $repository->getRoleService(); - - /* BEGIN: Use Case */ - $userGroup = $this->createUserGroupVersion1(); - - // Instantiate a role create and add some policies - $roleCreate = $roleService->newRoleCreateStruct('Example Role'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleCreate->addPolicy( - $roleService->newPolicyCreateStruct('user', 'login') - ); - $roleCreate->addPolicy( - $roleService->newPolicyCreateStruct('content', 'read') - ); - $roleCreate->addPolicy( - $roleService->newPolicyCreateStruct('content', 'edit') - ); - - // Create the new role instance - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRole($roleDraft->id); - - // Assign role to new user group - $roleService->assignRoleToUserGroup($role, $userGroup); - - // Load the currently assigned role - $roleAssignments = $roleService->getRoleAssignmentsForUserGroup($userGroup); - /* END: Use Case */ - - $this->assertCount(1, $roleAssignments); - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\UserGroupRoleAssignment', - reset($roleAssignments) - ); - } - - /** - * Test for the getRoleAssignmentsForUser() method. - * - * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignmentsForUser() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUser - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUserGroup - */ - public function testLoadPoliciesByUserId() - { - $repository = $this->getRepository(); - - $anonUserId = $this->generateId('user', 10); - /* BEGIN: Use Case */ - // $anonUserId is the ID of the "Anonymous" user. - - $userService = $repository->getUserService(); - $roleService = $repository->getRoleService(); - - // Load "Anonymous" user - $user = $userService->loadUser($anonUserId); - - // Instantiate a role create and add some policies - $roleCreate = $roleService->newRoleCreateStruct('User Role'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleCreate->addPolicy( - $roleService->newPolicyCreateStruct('notification', 'use') - ); - $roleCreate->addPolicy( - $roleService->newPolicyCreateStruct('user', 'password') - ); - $roleCreate->addPolicy( - $roleService->newPolicyCreateStruct('user', 'selfedit') - ); - - // Create the new role instance - $roleDraft = $roleService->createRole($roleCreate); - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRole($roleDraft->id); - - // Assign role to anon user - $roleService->assignRoleToUser($role, $user); - $roleAssignments = $roleService->getRoleAssignmentsForUser($user, true); - - $policies = []; - foreach ($roleAssignments as $roleAssignment) { - $policies[] = $roleAssignment->getRole()->getPolicies(); - } - $policies = array_merge(...$policies); - - $simplePolicyList = []; - foreach ($policies as $simplePolicy) { - $simplePolicyList[] = [$simplePolicy->roleId, $simplePolicy->module, $simplePolicy->function]; - } - /* END: Use Case */ - array_multisort($simplePolicyList); - - $this->assertEquals( - [ - [1, 'content', 'pdf'], - [1, 'content', 'read'], - [1, 'content', 'read'], - [1, 'rss', 'feed'], - [1, 'user', 'login'], - [1, 'user', 'login'], - [1, 'user', 'login'], - [1, 'user', 'login'], - [$role->id, 'notification', 'use'], - [$role->id, 'user', 'password'], - [$role->id, 'user', 'selfedit'], - ], - $simplePolicyList - ); - } - - /** - * Test for the publishRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::publishRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRoleDraft - */ - public function testPublishRoleDraft() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('newRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - - $roleDraft = $roleService->addPolicyByRoleDraft( - $roleDraft, - $roleService->newPolicyCreateStruct('content', 'delete') - ); - $roleDraft = $roleService->addPolicyByRoleDraft( - $roleDraft, - $roleService->newPolicyCreateStruct('content', 'create') - ); - - $roleService->publishRoleDraft($roleDraft); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\Role', - $roleService->loadRoleByIdentifier($roleCreate->identifier) - ); - } - - /** - * Test for the publishRoleDraft() method. - * - * @see \eZ\Publish\API\Repository\RoleService::publishRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRoleDraft - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAddPolicyByRoleDraft - */ - public function testPublishRoleDraftAddPolicies() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $roleService = $repository->getRoleService(); - $roleCreate = $roleService->newRoleCreateStruct('newRole'); - - // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 - // $roleCreate->mainLanguageCode = 'eng-US'; - - $roleDraft = $roleService->createRole($roleCreate); - - $roleDraft = $roleService->addPolicyByRoleDraft( - $roleDraft, - $roleService->newPolicyCreateStruct('content', 'delete') - ); - $roleDraft = $roleService->addPolicyByRoleDraft( - $roleDraft, - $roleService->newPolicyCreateStruct('content', 'create') - ); - - $roleService->publishRoleDraft($roleDraft); - $role = $roleService->loadRoleByIdentifier($roleCreate->identifier); - /* END: Use Case */ - - $actual = []; - foreach ($role->getPolicies() as $policy) { - $actual[] = [ - 'module' => $policy->module, - 'function' => $policy->function, - ]; - } - usort( - $actual, - static function ($p1, $p2) { - return strcasecmp($p1['function'], $p2['function']); - } - ); - - $this->assertEquals( - [ - [ - 'module' => 'content', - 'function' => 'create', - ], - [ - 'module' => 'content', - 'function' => 'delete', - ], - ], - $actual - ); - } - - /** - * Create a user group fixture in a variable named <b>$userGroup</b>,. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - */ - private function createUserGroupVersion1() - { - $repository = $this->getRepository(); - - $mainGroupId = $this->generateId('group', 4); - /* BEGIN: Inline */ - // $mainGroupId is the ID of the main "Users" group - - $roleService = $repository->getRoleService(); - $userService = $repository->getUserService(); - - // Load main group - $parentUserGroup = $userService->loadUserGroup($mainGroupId); - - // Instantiate a new create struct - $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); - $userGroupCreate->setField('name', 'Example Group'); - - // Create the new user group - $userGroup = $userService->createUserGroup( - $userGroupCreate, - $parentUserGroup - ); - /* END: Inline */ - - return $userGroup; - } - - private function loadRoleAssignmentForUser(RoleService $roleService, Role $role, User $newUser): UserRoleAssignment - { - [$userRoleAssignment] = array_values( - array_filter( - (array)$roleService->getRoleAssignments($role), - static function (RoleAssignment $roleAssignment) use ($newUser): bool { - return $roleAssignment instanceof UserRoleAssignment - && $roleAssignment->getUser()->login === $newUser->login; - } - ) - ); - - return $userRoleAssignment; - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/ContentTypeGroupTermAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/ContentTypeGroupTermAggregationTest.php deleted file mode 100644 index c2a5dece74..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/ContentTypeGroupTermAggregationTest.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\ContentTypeGroupTermAggregation; - -final class ContentTypeGroupTermAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - $aggregation = new ContentTypeGroupTermAggregation('content_type_group'); - - $builder = new TermAggregationDataSetBuilder($aggregation); - $builder->setExpectedEntries([ - 'Content' => 8, - 'Users' => 8, - 'Setup' => 2, - ]); - - $builder->setEntryMapper([ - $this->getRepository()->getContentTypeService(), - 'loadContentTypeGroupByIdentifier', - ]); - - yield $builder->build(); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/ContentTypeTermAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/ContentTypeTermAggregationTest.php deleted file mode 100644 index d77d90f644..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/ContentTypeTermAggregationTest.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\ContentTypeTermAggregation; - -final class ContentTypeTermAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - $aggregation = new ContentTypeTermAggregation('content_type'); - - $builder = new TermAggregationDataSetBuilder($aggregation); - $builder->setExpectedEntries([ - 'folder' => 6, - 'user_group' => 6, - 'user' => 2, - 'common_ini_settings' => 1, - 'template_look' => 1, - 'feedback_form' => 1, - 'landing_page' => 1, - ]); - - $builder->setEntryMapper([ - $this->getRepository()->getContentTypeService(), - 'loadContentTypeByIdentifier', - ]); - - yield $builder->build(); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/DataSetBuilder/TermAggregationDataSetBuilder.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/DataSetBuilder/TermAggregationDataSetBuilder.php deleted file mode 100644 index 9ac02d7df1..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/DataSetBuilder/TermAggregationDataSetBuilder.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\DataSetBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; - -/** - * @internal - */ -final class TermAggregationDataSetBuilder -{ - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Aggregation */ - private $aggregation; - - /** @var array */ - private $entries; - - /** @var callable|null */ - private $mapper; - - public function __construct(Aggregation $aggregation) - { - $this->aggregation = $aggregation; - $this->entries = []; - $this->mapper = null; - } - - public function setExpectedEntries(array $entries): self - { - $this->entries = $entries; - - return $this; - } - - public function setEntryMapper(callable $mapper): self - { - $this->mapper = $mapper; - - return $this; - } - - public function build(): array - { - return [ - $this->aggregation, - $this->buildExpectedTermAggregationResult(), - ]; - } - - private function buildExpectedTermAggregationResult(): TermAggregationResult - { - $entries = []; - foreach ($this->entries as $key => $count) { - if ($this->mapper !== null) { - $key = ($this->mapper)($key); - } - - $entries[] = new TermAggregationResultEntry($key, $count); - } - - return TermAggregationResult::createForAggregation($this->aggregation, $entries); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/CheckboxTermAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/CheckboxTermAggregationTest.php deleted file mode 100644 index dd3170bfa6..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/CheckboxTermAggregationTest.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\AbstractAggregationTest; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\CheckboxTermAggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; -use eZ\Publish\Core\FieldType\Checkbox\Value as CheckboxValue; - -final class CheckboxTermAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - yield [ - new CheckboxTermAggregation('checkbox_term', 'content_type', 'boolean'), - new TermAggregationResult( - 'checkbox_term', - [ - new TermAggregationResultEntry(true, 3), - new TermAggregationResultEntry(false, 2), - ] - ), - ]; - } - - protected function createFixturesForAggregation(Aggregation $aggregation): void - { - $generator = new FieldAggregationFixtureGenerator($this->getRepository()); - $generator->setContentTypeIdentifier('content_type'); - $generator->setFieldDefinitionIdentifier('boolean'); - $generator->setFieldTypeIdentifier('ezboolean'); - $generator->setValues([ - new CheckboxValue(true), - new CheckboxValue(true), - new CheckboxValue(true), - new CheckboxValue(false), - new CheckboxValue(false), - ]); - - $generator->execute(); - - $this->refreshSearch($this->getRepository()); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/FloatRangeAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/FloatRangeAggregationTest.php deleted file mode 100644 index 71a0167965..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/FloatRangeAggregationTest.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\AbstractAggregationTest; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\FloatRangeAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Range; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; - -final class FloatRangeAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - yield [ - new FloatRangeAggregation('float_range', 'content_type', 'float_field', [ - new Range(null, 10.0), - new Range(10.0, 25.0), - new Range(25.0, 50.0), - new Range(50.0, null), - ]), - new RangeAggregationResult( - 'float_range', - [ - new RangeAggregationResultEntry(new Range(null, 10.0), 4), - new RangeAggregationResultEntry(new Range(10.0, 25.0), 6), - new RangeAggregationResultEntry(new Range(25, 50), 10), - new RangeAggregationResultEntry(new Range(50, null), 20), - ] - ), - ]; - } - - protected function createFixturesForAggregation(Aggregation $aggregation): void - { - $generator = new FieldAggregationFixtureGenerator($this->getRepository()); - $generator->setContentTypeIdentifier('content_type'); - $generator->setFieldDefinitionIdentifier('float_field'); - $generator->setFieldTypeIdentifier('ezfloat'); - $generator->setValues(range(1.0, 100.0, 2.5)); - - $generator->execute(); - - $this->refreshSearch($this->getRepository()); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/FloatStatsAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/FloatStatsAggregationTest.php deleted file mode 100644 index 28ad02edde..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/FloatStatsAggregationTest.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\AbstractAggregationTest; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\FloatStatsAggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\StatsAggregationResult; - -final class FloatStatsAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - yield [ - new FloatStatsAggregation('float_stats', 'content_type', 'float_field_2'), - new StatsAggregationResult( - 'float_stats', - 5, - 1.0, - 7.75, - 3.8, - 19.0 - ), - ]; - } - - protected function createFixturesForAggregation(Aggregation $aggregation): void - { - $generator = new FieldAggregationFixtureGenerator($this->getRepository()); - $generator->setContentTypeIdentifier('content_type'); - $generator->setFieldDefinitionIdentifier('float_field_2'); - $generator->setFieldTypeIdentifier('ezfloat'); - $generator->setValues([1.0, 2.5, 2.5, 5.25, 7.75]); - - $generator->execute(); - - $this->refreshSearch($this->getRepository()); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/IntegerRangeAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/IntegerRangeAggregationTest.php deleted file mode 100644 index 8a7513adae..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/IntegerRangeAggregationTest.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\AbstractAggregationTest; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\IntegerRangeAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Range; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; - -final class IntegerRangeAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - yield [ - new IntegerRangeAggregation('integer_range', 'content_type', 'integer_field', [ - new Range(null, 10), - new Range(10, 25), - new Range(25, 50), - new Range(50, null), - ]), - new RangeAggregationResult( - 'integer_range', - [ - new RangeAggregationResultEntry(new Range(null, 10), 9), - new RangeAggregationResultEntry(new Range(10, 25), 15), - new RangeAggregationResultEntry(new Range(25, 50), 25), - new RangeAggregationResultEntry(new Range(50, null), 51), - ] - ), - ]; - } - - protected function createFixturesForAggregation(Aggregation $aggregation): void - { - $generator = new FieldAggregationFixtureGenerator($this->getRepository()); - $generator->setContentTypeIdentifier('content_type'); - $generator->setFieldDefinitionIdentifier('integer_field'); - $generator->setFieldTypeIdentifier('ezinteger'); - $generator->setValues(range(1, 100)); - - $generator->execute(); - - $this->refreshSearch($this->getRepository()); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/IntegerStatsAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/IntegerStatsAggregationTest.php deleted file mode 100644 index 20077e7587..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/IntegerStatsAggregationTest.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\AbstractAggregationTest; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\IntegerStatsAggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\StatsAggregationResult; - -final class IntegerStatsAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - yield [ - new IntegerStatsAggregation('integer_stats', 'content_type', 'integer_field'), - new StatsAggregationResult( - 'integer_stats', - 7, - 1, - 21, - 7.571428571428571, - 53 - ), - ]; - } - - protected function createFixturesForAggregation(Aggregation $aggregation): void - { - $generator = new FieldAggregationFixtureGenerator($this->getRepository()); - $generator->setContentTypeIdentifier('content_type'); - $generator->setFieldDefinitionIdentifier('integer_field'); - $generator->setFieldTypeIdentifier('ezinteger'); - $generator->setValues([1, 2, 3, 5, 8, 13, 21]); - $generator->execute(); - - $this->refreshSearch($this->getRepository()); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/KeywordTermAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/KeywordTermAggregationTest.php deleted file mode 100644 index c1f0a1d44b..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/KeywordTermAggregationTest.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\AbstractAggregationTest; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\KeywordTermAggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; - -final class KeywordTermAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - yield [ - new KeywordTermAggregation( - 'keyword_term', - 'content_type', - 'keyword_field' - ), - new TermAggregationResult( - 'keyword_term', - [ - new TermAggregationResultEntry('foo', 3), - new TermAggregationResultEntry('bar', 2), - new TermAggregationResultEntry('baz', 1), - ] - ), - ]; - } - - protected function createFixturesForAggregation(Aggregation $aggregation): void - { - $generator = new FieldAggregationFixtureGenerator($this->getRepository()); - $generator->setContentTypeIdentifier('content_type'); - $generator->setFieldDefinitionIdentifier('keyword_field'); - $generator->setFieldTypeIdentifier('ezkeyword'); - $generator->setValues([ - ['foo'], - ['foo', 'bar'], - ['foo', 'bar', 'baz'], - ]); - - $generator->execute(); - - $this->refreshSearch($this->getRepository()); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/SelectionTermAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/SelectionTermAggregationTest.php deleted file mode 100644 index ff6e9a64ae..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/SelectionTermAggregationTest.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\AbstractAggregationTest; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\SelectionTermAggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; - -final class SelectionTermAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - yield [ - new SelectionTermAggregation( - 'selection_term', - 'content_type', - 'selection_field' - ), - new TermAggregationResult( - 'selection_term', - [ - new TermAggregationResultEntry('foo', 3), - new TermAggregationResultEntry('bar', 2), - new TermAggregationResultEntry('baz', 1), - ] - ), - ]; - } - - protected function createFixturesForAggregation(Aggregation $aggregation): void - { - $generator = new FieldAggregationFixtureGenerator($this->getRepository()); - $generator->setContentTypeIdentifier('content_type'); - $generator->setFieldDefinitionIdentifier('selection_field'); - $generator->setFieldTypeIdentifier('ezselection'); - $generator->setValues([ - [0], - [0, 1], - [0, 1, 2], - ]); - - $generator->setFieldDefinitionCreateStructConfigurator( - static function (FieldDefinitionCreateStruct $createStruct): void { - $createStruct->fieldSettings = [ - 'isMultiple' => true, - 'options' => [ - 0 => 'foo', - 1 => 'bar', - 2 => 'baz', - ], - ]; - }, - ); - - $generator->execute(); - - $this->refreshSearch($this->getRepository()); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/TimeRangeAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/TimeRangeAggregationTest.php deleted file mode 100644 index 1f82a7e1d4..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/TimeRangeAggregationTest.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\AbstractAggregationTest; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\TimeRangeAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Range; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; -use eZ\Publish\Core\FieldType\Time\Value as TimeValue; - -final class TimeRangeAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - yield [ - new TimeRangeAggregation( - 'time_term', - 'content_type', - 'time_field', - [ - new Range(null, mktime(7, 0, 0, 0, 0, 0)), - new Range( - mktime(7, 0, 0, 0, 0, 0), - mktime(12, 0, 0, 0, 0, 0) - ), - new Range(mktime(12, 0, 0, 0, 0, 0), null), - ] - ), - new RangeAggregationResult( - 'time_term', - [ - new RangeAggregationResultEntry( - new Range(null, mktime(7, 0, 0, 0, 0, 0)), - 2 - ), - new RangeAggregationResultEntry( - new Range( - mktime(7, 0, 0, 0, 0, 0), - mktime(12, 0, 0, 0, 0, 0) - ), - 2 - ), - new RangeAggregationResultEntry( - new Range(mktime(12, 0, 0, 0, 0, 0), null), - 3 - ), - ] - ), - ]; - } - - protected function createFixturesForAggregation(Aggregation $aggregation): void - { - $generator = new FieldAggregationFixtureGenerator($this->getRepository()); - $generator->setContentTypeIdentifier('content_type'); - $generator->setFieldDefinitionIdentifier('time_field'); - $generator->setFieldTypeIdentifier('eztime'); - $generator->setValues([ - new TimeValue(mktime(6, 45, 0, 0, 0, 0)), - new TimeValue(mktime(7, 0, 0, 0, 0, 0)), - new TimeValue(mktime(6, 30, 0, 0, 0, 0)), - new TimeValue(mktime(11, 45, 0, 0, 0, 0)), - new TimeValue(mktime(16, 00, 0, 0, 0, 0)), - new TimeValue(mktime(17, 00, 0, 0, 0, 0)), - new TimeValue(mktime(17, 30, 0, 0, 0, 0)), - ]); - - $generator->execute(); - - $this->refreshSearch($this->getRepository()); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/LanguageTermAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/LanguageTermAggregationTest.php deleted file mode 100644 index 6660ec7eb9..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/LanguageTermAggregationTest.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\LanguageTermAggregation; - -final class LanguageTermAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - $aggregation = new LanguageTermAggregation('language'); - - $builder = new TermAggregationDataSetBuilder($aggregation); - $builder->setExpectedEntries([ - 'eng-US' => 16, - 'eng-GB' => 2, - ]); - - $builder->setEntryMapper([ - $this->getRepository()->getContentLanguageService(), - 'loadLanguage', - ]); - - yield $builder->build(); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/LocationChildrenTermAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/LocationChildrenTermAggregationTest.php deleted file mode 100644 index a9951b08b7..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/LocationChildrenTermAggregationTest.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Location\LocationChildrenTermAggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult; - -final class LocationChildrenTermAggregationTest extends AbstractAggregationTest -{ - /** - * @dataProvider dataProviderForTestFindContentWithAggregation - */ - public function testFindContentWithAggregation( - Aggregation $aggregation, - AggregationResult $expectedResult - ): void { - self::markTestSkipped('LocationChildrenTermAggregation is only available for Location search'); - } - - public function dataProviderForTestFindContentWithAggregation(): iterable - { - $aggregation = new LocationChildrenTermAggregation('children'); - - $builder = new TermAggregationDataSetBuilder($aggregation); - $builder->setExpectedEntries([ - 1 => 5, - 5 => 5, - 43 => 3, - 13 => 1, - 2 => 1, - 44 => 1, - 48 => 1, - 58 => 1, - ]); - - $builder->setEntryMapper([ - $this->getRepository()->getLocationService(), - 'loadLocation', - ]); - - yield $builder->build(); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/ObjectStateTermAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/ObjectStateTermAggregationTest.php deleted file mode 100644 index bf38962a42..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/ObjectStateTermAggregationTest.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\ObjectStateTermAggregation; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectState; - -final class ObjectStateTermAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - $aggregation = new ObjectStateTermAggregation('object_state', 'ez_lock'); - - $builder = new TermAggregationDataSetBuilder($aggregation); - $builder->setExpectedEntries([ - // TODO: Change the state of some content objects to have better test data - 'not_locked' => 18, - ]); - - $builder->setEntryMapper( - function (string $identifier): ObjectState { - $objectStateService = $this->getRepository()->getObjectStateService(); - - static $objectStateGroup = null; - if ($objectStateGroup === null) { - $objectStateGroup = $objectStateService->loadObjectStateGroupByIdentifier('ez_lock'); - } - - return $objectStateService->loadObjectStateByIdentifier($objectStateGroup, $identifier); - } - ); - - yield $builder->build(); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/RawRangeAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/RawRangeAggregationTest.php deleted file mode 100644 index 36db64a5b1..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/RawRangeAggregationTest.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; - -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Range; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\RawRangeAggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; - -final class RawRangeAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - yield [ - new RawRangeAggregation( - 'raw_range', - 'content_version_no_i', - [ - new Range(null, 2), - new Range(2, 3), - new Range(3, null), - ] - ), - new RangeAggregationResult( - 'raw_range', - [ - new RangeAggregationResultEntry(new Range(null, 2), 14), - new RangeAggregationResultEntry(new Range(2, 3), 3), - new RangeAggregationResultEntry(new Range(3, null), 1), - ] - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/RawStatsAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/RawStatsAggregationTest.php deleted file mode 100644 index 7f7d86ceca..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/RawStatsAggregationTest.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; - -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\RawStatsAggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\StatsAggregationResult; - -final class RawStatsAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - yield [ - new RawStatsAggregation( - 'raw_stats', - 'content_version_no_i' - ), - new StatsAggregationResult( - 'raw_stats', - 18, - 1.0, - 4.0, - 1.3333333333333333, - 24.0 - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/RawTermAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/RawTermAggregationTest.php deleted file mode 100644 index b3a0b580b8..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/RawTermAggregationTest.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; - -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\RawTermAggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; - -final class RawTermAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - yield [ - new RawTermAggregation( - 'raw_term', - 'content_section_identifier_id' - ), - new TermAggregationResult('raw_term', [ - new TermAggregationResultEntry('users', 8), - new TermAggregationResultEntry('media', 4), - new TermAggregationResultEntry('design', 2), - new TermAggregationResultEntry('setup', 2), - new TermAggregationResultEntry('standard', 2), - ]), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/SectionTermAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/SectionTermAggregationTest.php deleted file mode 100644 index 12ecdb7469..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/SectionTermAggregationTest.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\SectionTermAggregation; - -final class SectionTermAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - $aggregation = new SectionTermAggregation('section'); - - $builder = new TermAggregationDataSetBuilder($aggregation); - $builder->setExpectedEntries([ - 'users' => 8, - 'media' => 4, - 'standard' => 2, - 'setup' => 2, - 'design' => 2, - ]); - - $builder->setEntryMapper([ - $this->getRepository()->getSectionService(), - 'loadSectionByIdentifier', - ]); - - yield $builder->build(); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/SubtreeTermAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/SubtreeTermAggregationTest.php deleted file mode 100644 index db5d235a8e..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/SubtreeTermAggregationTest.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Location\SubtreeTermAggregation; - -final class SubtreeTermAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - $aggregation = new SubtreeTermAggregation('subtree', '/1/5/'); - - $builder = new TermAggregationDataSetBuilder($aggregation); - $builder->setExpectedEntries([ - 5 => 7, - 13 => 1, - 44 => 1, - ]); - - $builder->setEntryMapper([ - $this->getRepository()->getLocationService(), - 'loadLocation', - ]); - - yield $builder->build(); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/VisibilityTermAggregationTest.php b/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/VisibilityTermAggregationTest.php deleted file mode 100644 index 2f625472ca..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/VisibilityTermAggregationTest.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; - -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\VisibilityTermAggregation; - -final class VisibilityTermAggregationTest extends AbstractAggregationTest -{ - public function dataProviderForTestFindContentWithAggregation(): iterable - { - $aggregation = new VisibilityTermAggregation('visibility'); - - $builder = new TermAggregationDataSetBuilder($aggregation); - $builder->setExpectedEntries([ - true => 18, - ]); - - yield $builder->build(); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/SearchServiceFullTextEmbedTest.php b/eZ/Publish/API/Repository/Tests/SearchService/SearchServiceFullTextEmbedTest.php deleted file mode 100644 index 87e606fead..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/SearchServiceFullTextEmbedTest.php +++ /dev/null @@ -1,200 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService; - -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\FieldType\RichText\Value as RichTextValue; - -/** - * Test case for full text search in the SearchService (for embed). - * - * @see \eZ\Publish\API\Repository\SearchService - * @group integration - * @group search - * @group fulltext - */ -class SearchServiceFullTextEmbedTest extends BaseTest -{ - private const EMBEDDED_ARTICLE_NAME = 'test1'; - - private static $createdIds = []; - - protected function setUp(): void - { - self::markTestIncomplete( - 'Requires EZP-31337 due to RichText being no longer available in Kernel' - ); - - parent::setUp(); - - $repository = $this->getRepository(false); - - if ( - false === $repository->getSearchService()->supports( - SearchService::CAPABILITY_ADVANCED_FULLTEXT - ) - ) { - $this->markTestSkipped( - 'Advanced FullText search is not supported by the current search engine' - ); - } - } - - public function testFullTextContentSearch(): void - { - $this->prepareTestContent(); - - $searchService = $this->getRepository()->getSearchService(); - - $query = new Query([ - 'query' => new Criterion\FullText(self::EMBEDDED_ARTICLE_NAME), - ]); - - $searchResult = $searchService->findContent($query); - - $this->assertGreaterThanOrEqual(2, $searchResult->totalCount); - $this->assertResults($searchResult->searchHits); - } - - public function testFullTextLocationSearch(): void - { - $this->prepareTestContent(); - - $searchService = $this->getRepository()->getSearchService(); - - $query = new LocationQuery([ - 'query' => new Criterion\FullText(self::EMBEDDED_ARTICLE_NAME), - ]); - - $searchResult = $searchService->findLocations($query); - - $this->assertGreaterThanOrEqual(2, $searchResult->totalCount); - $this->assertResults($searchResult->searchHits); - } - - private function hasTestPreparedContent(): bool - { - return !empty(self::$createdIds); - } - - private function prepareTestContent(): void - { - if ($this->hasTestPreparedContent()) { - return; - } - - $contentService = $this->getRepository()->getContentService(); - $baseArticleStruct = $this->prepareBaseArticleStruct(); - - $embeddedArticleStruct = $this->fillEmbeddedArticleStruct(clone $baseArticleStruct); - $embeddedArticleContent = $contentService->publishVersion( - $this->createContent($embeddedArticleStruct)->versionInfo - ); - - $mainArticleStruct = $this->fillMainArticleStruct(clone $baseArticleStruct, $embeddedArticleContent->id); - $mainArticleContent = $contentService->publishVersion( - $this->createContent($mainArticleStruct)->versionInfo - ); - - $this->refreshSearch($this->getRepository()); - - self::$createdIds = [ - $embeddedArticleContent->id, - $mainArticleContent->id, - ]; - } - - private function prepareBaseArticleStruct(): ContentCreateStruct - { - $introDocument = new \DOMDocument(); - $introDocument->loadXML( - <<<EOT -<?xml version="1.0" encoding="UTF-8"?> -<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> -<para>some paragraph</para> -</section> -EOT - ); - - $repository = $this->getRepository(); - $contentType = $repository->getContentTypeService()->loadContentTypeByIdentifier('article'); - - $articleStruct = $repository->getContentService()->newContentCreateStruct($contentType, 'eng-GB'); - $articleStruct->setField('intro', new RichTextValue($introDocument), 'eng-GB'); - - return $articleStruct; - } - - private function fillEmbeddedArticleStruct( - ContentCreateStruct $articleStruct - ): ContentCreateStruct { - $articleBodyDoc = new \DOMDocument(); - $articleBodyDoc->loadXML( - <<<EOT -<?xml version="1.0" encoding="UTF-8"?> -<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> -<para>body-content</para> -</section> -EOT - ); - - $articleStruct->setField('title', self::EMBEDDED_ARTICLE_NAME); - $articleStruct->setField('body', new RichTextValue($articleBodyDoc), 'eng-GB'); - - return $articleStruct; - } - - private function fillMainArticleStruct( - ContentCreateStruct $articleStruct, - int $embedContentId - ): ContentCreateStruct { - $mainArticleBodyDoc = new \DOMDocument(); - $mainArticleBodyDoc->loadXML( - <<<EOT -<?xml version="1.0" encoding="UTF-8"?> -<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"> -<para><ezembedinline xlink:href="ezcontent://{$embedContentId}" view="embed-inline"/></para> -</section> -EOT - ); - - $articleStruct->setField('title', 'test'); - $articleStruct->setField('body', new RichTextValue($mainArticleBodyDoc), 'eng-GB'); - - return $articleStruct; - } - - private function createContent(ContentCreateStruct $contentCreateStruct): Content - { - $repository = $this->getRepository(); - - return $repository->getContentService()->createContent( - $contentCreateStruct, - [$repository->getLocationService()->newLocationCreateStruct(2)] - ); - } - - private function assertResults(array $searchHits): void - { - $resultIds = []; - - /** @var \eZ\Publish\API\Repository\Values\Content\Search\SearchHit $contentItem */ - foreach ($searchHits as $contentItem) { - $resultIds[] = $contentItem->valueObject->contentInfo->id; - } - - self::assertCount(2, array_intersect($resultIds, self::$createdIds)); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchService/SortClause/AbstractSortClauseTest.php b/eZ/Publish/API/Repository/Tests/SearchService/SortClause/AbstractSortClauseTest.php deleted file mode 100644 index 6fb894e95d..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchService/SortClause/AbstractSortClauseTest.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\SearchService\SortClause; - -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; - -abstract class AbstractSortClauseTest extends BaseTest -{ - protected function assertSearchResultOrderByRemoteId( - array $expectedOrderedIds, - SearchResult $actualSearchResults - ): void { - self::assertEquals( - count($expectedOrderedIds), - $actualSearchResults->totalCount - ); - - $actualIds = array_map( - static function (SearchHit $searchHit): string { - return $searchHit->valueObject->remoteId; - }, - $actualSearchResults->searchHits - ); - - self::assertEquals($expectedOrderedIds, $actualIds); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SearchServiceTest.php b/eZ/Publish/API/Repository/Tests/SearchServiceTest.php deleted file mode 100644 index 780eaac3fd..0000000000 --- a/eZ/Publish/API/Repository/Tests/SearchServiceTest.php +++ /dev/null @@ -1,5250 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use function count; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use EzSystems\EzPlatformSolrSearchEngine\Tests\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory; -use InvalidArgumentException; - -/** - * Test case for operations in the SearchService. - * - * @see eZ\Publish\API\Repository\SearchService - * @group integration - * @group search - */ -class SearchServiceTest extends BaseTest -{ - public const QUERY_CLASS = Query::class; - - public const FIND_CONTENT_METHOD = 'findContent'; - public const FIND_LOCATION_METHOD = 'findLocations'; - - public const AVAILABLE_FIND_METHODS = [ - self::FIND_CONTENT_METHOD, - self::FIND_LOCATION_METHOD, - ]; - - use Common\FacetedSearchProvider; - - public function getFilterContentSearches() - { - $fixtureDir = $this->getFixtureDir(); - - return [ - 0 => [ - [ - 'filter' => new Criterion\ContentId( - [1, 4, 10] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'ContentId.php', - ], - 1 => [ - [ - 'filter' => new Criterion\LogicalAnd( - [ - new Criterion\ContentId( - [1, 4, 10] - ), - new Criterion\ContentId( - [4, 12] - ), - ] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'LogicalAnd.php', - ], - 2 => [ - [ - 'filter' => new Criterion\LogicalOr( - [ - new Criterion\ContentId( - [1, 4, 10] - ), - new Criterion\ContentId( - [4, 12] - ), - ] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'LogicalOr.php', - ], - 3 => [ - [ - 'filter' => new Criterion\LogicalAnd( - [ - new Criterion\ContentId( - [1, 4, 10] - ), - new Criterion\LogicalNot( - new Criterion\ContentId( - [10, 12] - ) - ), - ] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'LogicalNot.php', - ], - 4 => [ - [ - 'filter' => new Criterion\LogicalAnd( - [ - new Criterion\ContentId( - [1, 4, 10] - ), - new Criterion\LogicalAnd( - [ - new Criterion\LogicalNot( - new Criterion\ContentId( - [10, 12] - ) - ), - ] - ), - ] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'LogicalNot.php', - ], - 5 => [ - [ - 'filter' => new Criterion\ContentTypeId( - 4 - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'ContentTypeId.php', - ], - 6 => [ - [ - 'filter' => new Criterion\ContentTypeIdentifier( - 'user' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'ContentTypeId.php', - ], - 7 => [ - [ - 'filter' => new Criterion\ContentTypeIdentifier( - 'user', - 'invalid' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'ContentTypeId.php', - ], - 8 => [ - [ - 'filter' => new Criterion\ContentTypeIdentifier( - 'invalid1', - 'invalid2' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'MatchNone.php', - ], - 9 => [ - [ - 'filter' => new Criterion\MatchNone(), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'MatchNone.php', - ], - 10 => [ - [ - 'filter' => new Criterion\ContentTypeGroupId( - 2 - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'ContentTypeGroupId.php', - ], - 11 => [ - [ - 'filter' => new Criterion\DateMetadata( - Criterion\DateMetadata::MODIFIED, - Criterion\Operator::GT, - 1343140540 - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'DateMetadataGt.php', - ], - 12 => [ - [ - 'filter' => new Criterion\DateMetadata( - Criterion\DateMetadata::MODIFIED, - Criterion\Operator::GTE, - 1311154215 - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'DateMetadataGte.php', - ], - 13 => [ - [ - 'filter' => new Criterion\DateMetadata( - Criterion\DateMetadata::MODIFIED, - Criterion\Operator::LTE, - 1311154215 - ), - 'limit' => 10, - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'DateMetadataLte.php', - ], - 14 => [ - [ - 'filter' => new Criterion\DateMetadata( - Criterion\DateMetadata::MODIFIED, - Criterion\Operator::IN, - [1033920794, 1060695457, 1343140540] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'DateMetadataIn.php', - ], - 15 => [ - [ - 'filter' => new Criterion\DateMetadata( - Criterion\DateMetadata::MODIFIED, - Criterion\Operator::BETWEEN, - [1033920776, 1072180276] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'DateMetadataBetween.php', - ], - 16 => [ - [ - 'filter' => new Criterion\DateMetadata( - Criterion\DateMetadata::CREATED, - Criterion\Operator::BETWEEN, - [1033920776, 1072180278] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'DateMetadataCreated.php', - ], - 17 => [ - [ - 'filter' => new Criterion\CustomField( - 'user_group_name_value_s', - Criterion\Operator::EQ, - 'Members' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'Field.php', - ], - 18 => [ - [ - 'filter' => new Criterion\CustomField( - 'user_group_name_value_s', - Criterion\Operator::CONTAINS, - 'Members' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'Field.php', - ], - 19 => [ - [ - 'filter' => new Criterion\CustomField( - 'user_group_name_value_s', - Criterion\Operator::LT, - 'Members' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'CustomFieldLt.php', - ], - 20 => [ - [ - 'filter' => new Criterion\CustomField( - 'user_group_name_value_s', - Criterion\Operator::LTE, - 'Members' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'CustomFieldLte.php', - ], - 21 => [ - [ - 'filter' => new Criterion\CustomField( - 'user_group_name_value_s', - Criterion\Operator::GT, - 'Members' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'CustomFieldGt.php', - ], - 22 => [ - [ - 'filter' => new Criterion\CustomField( - 'user_group_name_value_s', - Criterion\Operator::GTE, - 'Members' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'CustomFieldGte.php', - ], - 23 => [ - [ - 'filter' => new Criterion\CustomField( - 'user_group_name_value_s', - Criterion\Operator::BETWEEN, - ['Administrator users', 'Members'] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'CustomFieldBetween.php', - ], - 24 => [ - [ - 'filter' => new Criterion\RemoteId( - ['f5c88a2209584891056f987fd965b0ba', 'faaeb9be3bd98ed09f606fc16d144eca'] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'RemoteId.php', - ], - 25 => [ - [ - 'filter' => new Criterion\SectionId( - [2] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'SectionId.php', - ], - 26 => [ - [ - 'filter' => new Criterion\Field( - 'name', - Criterion\Operator::EQ, - 'Members' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'Field.php', - ], - 27 => [ - [ - 'filter' => new Criterion\Field( - 'name', - Criterion\Operator::IN, - ['Members', 'Anonymous Users'] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'FieldIn.php', - ], - 28 => [ - [ - 'filter' => new Criterion\DateMetadata( - Criterion\DateMetadata::MODIFIED, - Criterion\Operator::BETWEEN, - [1033920275, 1033920794] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'FieldBetween.php', - ], - 29 => [ - [ - 'filter' => new Criterion\LogicalOr( - [ - new Criterion\Field( - 'name', - Criterion\Operator::EQ, - 'Members' - ), - new Criterion\DateMetadata( - Criterion\DateMetadata::MODIFIED, - Criterion\Operator::BETWEEN, - [1033920275, 1033920794] - ), - ] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'FieldOr.php', - ], - 30 => [ - [ - 'filter' => new Criterion\Subtree( - '/1/5/' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'Subtree.php', - ], - 31 => [ - [ - 'filter' => new Criterion\LocationId( - [1, 2, 5] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'LocationId.php', - ], - 32 => [ - [ - 'filter' => new Criterion\ParentLocationId( - [1] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'ParentLocationId.php', - ], - 33 => [ - [ - 'filter' => new Criterion\LocationRemoteId( - ['3f6d92f8044aed134f32153517850f5a', 'f3e90596361e31d496d4026eb624c983'] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'LocationRemoteId.php', - ], - 34 => [ - [ - // There is no Status Criterion anymore, this should match all published as well - 'filter' => new Criterion\Subtree( - '/1/' - ), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'Status.php', - // Result having the same sort level should be sorted between them to be system independent - static function (&$data) { - usort( - $data->searchHits, - static function ($a, $b) { - if ($a->score == $b->score) { - if ($a->valueObject['id'] == $b->valueObject['id']) { - return 0; - } - - // Order by ascending ID - return ($a->valueObject['id'] < $b->valueObject['id']) ? -1 : 1; - } - - // Order by descending score - return ($a->score > $b->score) ? -1 : 1; - } - ); - }, - ], - 35 => [ - [ - 'filter' => new Criterion\UserMetadata( - Criterion\UserMetadata::MODIFIER, - Criterion\Operator::EQ, - 14 - ), - 'sortClauses' => [ - new SortClause\ContentId(), - ], - 'limit' => 50, - ], - $fixtureDir . 'UserMetadata.php', - ], - 36 => [ - [ - 'filter' => new Criterion\UserMetadata( - Criterion\UserMetadata::MODIFIER, - Criterion\Operator::IN, - [14] - ), - 'sortClauses' => [ - new SortClause\ContentId(), - ], - 'limit' => 50, - ], - $fixtureDir . 'UserMetadata.php', - ], - 37 => [ - [ - 'filter' => new Criterion\UserMetadata( - Criterion\UserMetadata::OWNER, - Criterion\Operator::EQ, - 14 - ), - 'sortClauses' => [ - new SortClause\ContentId(), - ], - 'limit' => 50, - ], - $fixtureDir . 'UserMetadata.php', - ], - 38 => [ - [ - 'filter' => new Criterion\UserMetadata( - Criterion\UserMetadata::OWNER, - Criterion\Operator::IN, - [14] - ), - 'sortClauses' => [ - new SortClause\ContentId(), - ], - 'limit' => 50, - ], - $fixtureDir . 'UserMetadata.php', - ], - 39 => [ - [ - 'filter' => new Criterion\UserMetadata( - Criterion\UserMetadata::GROUP, - Criterion\Operator::EQ, - 12 - ), - 'sortClauses' => [ - new SortClause\ContentId(), - ], - 'limit' => 50, - ], - $fixtureDir . 'UserMetadata.php', - ], - 40 => [ - [ - 'filter' => new Criterion\UserMetadata( - Criterion\UserMetadata::GROUP, - Criterion\Operator::IN, - [12] - ), - 'sortClauses' => [ - new SortClause\ContentId(), - ], - 'limit' => 50, - ], - $fixtureDir . 'UserMetadata.php', - ], - 41 => [ - [ - 'filter' => new Criterion\UserMetadata( - Criterion\UserMetadata::GROUP, - Criterion\Operator::EQ, - 4 - ), - 'sortClauses' => [ - new SortClause\ContentId(), - ], - 'limit' => 50, - ], - $fixtureDir . 'UserMetadata.php', - ], - 42 => [ - [ - 'filter' => new Criterion\UserMetadata( - Criterion\UserMetadata::GROUP, - Criterion\Operator::IN, - [4] - ), - 'sortClauses' => [ - new SortClause\ContentId(), - ], - 'limit' => 50, - ], - $fixtureDir . 'UserMetadata.php', - ], - 43 => [ - [ - 'filter' => new Criterion\UserMetadata( - Criterion\UserMetadata::GROUP, - null, - [4] - ), - 'sortClauses' => [ - new SortClause\ContentId(), - ], - 'limit' => 50, - ], - $fixtureDir . 'UserMetadata.php', - ], - 44 => [ - [ - 'filter' => new Criterion\Ancestor( - [ - '/1/5/44/', - '/1/5/44/45/', - ] - ), - 'sortClauses' => [ - new SortClause\ContentId(), - ], - 'limit' => 50, - ], - $fixtureDir . 'AncestorContent.php', - ], - ]; - } - - public function getContentQuerySearches() - { - $fixtureDir = $this->getFixtureDir(); - - return [ - [ - [ - 'filter' => new Criterion\ContentId( - [58, 10] - ), - 'query' => new Criterion\FullText('contact'), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'FullTextFiltered.php', - ], - [ - [ - 'query' => new Criterion\FullText( - 'contact', - [ - 'boost' => [ - 'title' => 2, - ], - 'fuzziness' => .5, - ] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'FullText.php', - ], - [ - [ - 'query' => new Criterion\FullText( - 'Contact*' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'FullTextWildcard.php', - ], - [ - [ - 'query' => new Criterion\LanguageCode('eng-GB', false), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'LanguageCode.php', - ], - [ - [ - 'query' => new Criterion\LanguageCode(['eng-US', 'eng-GB']), - 'offset' => 10, - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'LanguageCodeIn.php', - ], - [ - [ - 'query' => new Criterion\LanguageCode('eng-GB'), - 'offset' => 10, - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'LanguageCodeAlwaysAvailable.php', - ], - [ - [ - 'query' => new Criterion\Visibility( - Criterion\Visibility::VISIBLE - ), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'Visibility.php', - ], - [ - [ - 'query' => new Criterion\IsUserEnabled(true), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 5, - ], - $fixtureDir . 'IsUserEnabledTrue.php', - ], - [ - [ - 'query' => new Criterion\IsUserEnabled(false), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 5, - ], - $fixtureDir . 'IsUserEnabledFalse.php', - ], - [ - [ - 'query' => new Criterion\IsUserBased(true), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'IsUserBasedTrue.php', - ], - [ - [ - 'query' => new Criterion\IsUserBased(false), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 1, - ], - $fixtureDir . 'IsUserBasedFalse.php', - ], - [ - [ - 'query' => new Criterion\UserId(14), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'UserIdEq.php', - ], - [ - [ - 'query' => new Criterion\UserId([14, 10]), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'UserIdIn.php', - ], - [ - [ - 'query' => new Criterion\UserLogin('*adm*', Operator::LIKE), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'UserLogin.php', - ], - [ - [ - 'query' => new Criterion\UserLogin('admin', Operator::EQ), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'UserLogin.php', - ], - [ - [ - 'query' => new Criterion\UserLogin(['admin'], Operator::IN), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'UserLogin.php', - ], - [ - [ - 'query' => new Criterion\UserEmail('*anonymous*', Operator::LIKE), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'UserEmail.php', - ], - [ - [ - 'query' => new Criterion\UserEmail('anonymous@link.invalid', Operator::EQ), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'UserEmail.php', - ], - [ - [ - 'query' => new Criterion\UserEmail(['anonymous@link.invalid'], Operator::IN), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'UserEmail.php', - ], - [ - [ - 'query' => new Criterion\SectionIdentifier('users'), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'SectionIdentifier.php', - ], - [ - [ - 'query' => new Criterion\SectionIdentifier(['users']), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'SectionIdentifier.php', - ], - [ - [ - 'query' => new Criterion\ObjectStateIdentifier('not_locked'), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'ObjectStateIdentifier.php', - ], - [ - [ - 'query' => new Criterion\ObjectStateIdentifier(['not_locked']), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'ObjectStateIdentifier.php', - ], - [ - [ - 'query' => new Criterion\ObjectStateIdentifier(['not_locked'], 'ez_lock'), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'ObjectStateIdentifier.php', - ], - [ - [ - 'query' => new Criterion\Sibling(58, 1), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'Sibling.php', - ], - ]; - } - - public function getLocationQuerySearches() - { - $fixtureDir = $this->getFixtureDir(); - - return [ - [ - [ - 'query' => new Criterion\Location\Depth(Criterion\Operator::EQ, 1), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'Depth.php', - ], - [ - [ - 'query' => new Criterion\Location\Depth(Criterion\Operator::IN, [1, 3]), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'DepthIn.php', - ], - [ - [ - 'query' => new Criterion\Location\Depth(Criterion\Operator::GT, 2), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'DepthGt.php', - ], - [ - [ - 'query' => new Criterion\Location\Depth(Criterion\Operator::GTE, 2), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'DepthGte.php', - ], - [ - [ - 'query' => new Criterion\Location\Depth(Criterion\Operator::LT, 2), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'Depth.php', - ], - [ - [ - 'query' => new Criterion\Location\Depth(Criterion\Operator::LTE, 2), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'DepthLte.php', - ], - [ - [ - 'query' => new Criterion\Location\Depth(Criterion\Operator::BETWEEN, [1, 2]), - 'sortClauses' => [new SortClause\ContentId()], - 'limit' => 50, - ], - $fixtureDir . 'DepthLte.php', - ], - [ - [ - 'filter' => new Criterion\Ancestor('/1/5/44/45/'), - 'sortClauses' => [ - new SortClause\Location\Depth(), - ], - 'limit' => 50, - ], - $fixtureDir . 'AncestorLocation.php', - ], - ]; - } - - /** - * Test for the findContent() method. - * - * @dataProvider getFilterContentSearches - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testFindContentFiltered($queryData, $fixture, $closure = null) - { - $query = new Query($queryData); - $this->assertQueryFixture($query, $fixture, $closure); - } - - /** - * Test for the findContentInfo() method. - * - * @dataProvider getFilterContentSearches - * - * @see \eZ\Publish\API\Repository\SearchService::findContentInfo() - */ - public function testFindContentInfoFiltered($queryData, $fixture, $closure = null) - { - $query = new Query($queryData); - $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true); - } - - /** - * Test for the findLocations() method. - * - * @dataProvider getFilterContentSearches - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - */ - public function testFindLocationsContentFiltered($queryData, $fixture, $closure = null) - { - $query = new LocationQuery($queryData); - $this->assertQueryFixture($query, $fixture, $closure); - } - - /** - * Test for deprecated $criterion property on query object. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * @deprecated - */ - public function testDeprecatedCriteriaProperty() - { - $this->assertQueryFixture( - new Query( - [ - 'query' => new Criterion\ContentId( - [1, 4, 10] - ), - 'sortClauses' => [new SortClause\ContentId()], - ] - ), - $this->getFixtureDir() . 'DeprecatedContentIdQuery.php' - ); - } - - /** - * Test for the findContent() method. - * - * @dataProvider getContentQuerySearches - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testQueryContent($queryData, $fixture, $closure = null) - { - $query = new Query($queryData); - $this->assertQueryFixture($query, $fixture, $closure); - } - - /** - * Test for the findContentInfo() method. - * - * @dataProvider getContentQuerySearches - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testQueryContentInfo($queryData, $fixture, $closure = null) - { - $query = new Query($queryData); - $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true); - } - - /** - * Test for the findLocations() method. - * - * @dataProvider getContentQuerySearches - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - */ - public function testQueryContentLocations($queryData, $fixture, $closure = null) - { - $query = new LocationQuery($queryData); - $this->assertQueryFixture($query, $fixture, $closure); - } - - /** - * Test for the findLocations() method. - * - * @dataProvider getLocationQuerySearches - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - */ - public function testQueryLocations($queryData, $fixture, $closure = null) - { - $query = new LocationQuery($queryData); - $this->assertQueryFixture($query, $fixture, $closure); - } - - public function getCaseInsensitiveSearches() - { - return [ - [ - [ - 'filter' => new Criterion\Field( - 'name', - Criterion\Operator::EQ, - 'Members' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - ], - [ - [ - 'filter' => new Criterion\Field( - 'name', - Criterion\Operator::EQ, - 'members' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - ], - [ - [ - 'filter' => new Criterion\Field( - 'name', - Criterion\Operator::EQ, - 'MEMBERS' - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - ], - ]; - } - - /** - * Test for the findContent() method. - * - * @dataProvider getCaseInsensitiveSearches - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testFindContentFieldFiltersCaseSensitivity($queryData) - { - $query = new Query($queryData); - $this->assertQueryFixture( - $query, - $this->getFixtureDir() . 'Field.php' - ); - } - - /** - * Test for the findLocations() method. - * - * @dataProvider getCaseInsensitiveSearches - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - */ - public function testFindLocationsFieldFiltersCaseSensitivity($queryData) - { - $query = new LocationQuery($queryData); - $this->assertQueryFixture( - $query, - $this->getFixtureDir() . 'Field.php' - ); - } - - public function getRelationFieldFilterSearches() - { - $fixtureDir = $this->getFixtureDir(); - - return [ - 0 => [ - [ - 'filter' => new Criterion\FieldRelation( - 'image', - Criterion\Operator::IN, - [1, 4, 10] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'FieldRelation.php', - ], - 1 => [ - [ - 'filter' => new Criterion\FieldRelation( - 'image', - Criterion\Operator::IN, - [4, 49] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'FieldRelationAll.php', - ], - 2 => [ - [ - 'filter' => new Criterion\FieldRelation( - 'image', - Criterion\Operator::IN, - [4] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'FieldRelation.php', - ], - 3 => [ - [ - 'filter' => new Criterion\FieldRelation( - 'image', - Criterion\Operator::CONTAINS, - [1, 4, 10] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'MatchNone.php', - ], - 4 => [ - [ - 'filter' => new Criterion\FieldRelation( - 'image', - Criterion\Operator::CONTAINS, - [4, 49] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'MatchNone.php', - ], - 5 => [ - [ - 'filter' => new Criterion\FieldRelation( - 'image', - Criterion\Operator::CONTAINS, - [4] - ), - 'sortClauses' => [new SortClause\ContentId()], - ], - $fixtureDir . 'FieldRelation.php', - ], - ]; - } - - /** - * Purely for creating relation data needed for testFindRelationFieldContentInfoFiltered() - * and testFindRelationFieldLocationsFiltered(). - */ - public function testRelationContentCreation() - { - $repository = $this->getRepository(); - $galleryType = $repository->getContentTypeService()->loadContentTypeByIdentifier('gallery'); - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - - $locationCreateStruct = $locationService->newLocationCreateStruct(2); // Home - - $createStruct = $contentService->newContentCreateStruct($galleryType, 'eng-GB'); - $createStruct->setField('name', 'Image gallery'); - $createStruct->setField('image', 49); // Images folder - $draft = $contentService->createContent($createStruct, [$locationCreateStruct]); - $contentService->publishVersion($draft->getVersionInfo()); - - $createStruct = $contentService->newContentCreateStruct($galleryType, 'eng-GB'); - $createStruct->setField('name', 'User gallery'); - $createStruct->setField('image', 4); // User folder - $draft = $contentService->createContent($createStruct, [$locationCreateStruct]); - $contentService->publishVersion($draft->getVersionInfo()); - - $this->refreshSearch($repository); - } - - /** - * Test for FieldRelation using findContentInfo() method. - * - * @dataProvider getRelationFieldFilterSearches - * - * @see \eZ\Publish\API\Repository\SearchService::findContentInfo() - * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testRelationContentCreation - */ - public function testFindRelationFieldContentInfoFiltered($queryData, $fixture) - { - $this->getRepository(false); // To make sure repo is setup w/o removing data from getRelationFieldFilterContentSearches - $query = new Query($queryData); - $this->assertQueryFixture($query, $fixture, null, true, true, false); - } - - /** - * Test for FieldRelation using findLocations() method. - * - * @dataProvider getRelationFieldFilterSearches - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testRelationContentCreation - */ - public function testFindRelationFieldLocationsFiltered($queryData, $fixture) - { - $this->getRepository(false); // To make sure repo is setup w/o removing data from getRelationFieldFilterContentSearches - $query = new LocationQuery($queryData); - $this->assertQueryFixture($query, $fixture, null, true, false, false); - } - - public function testFindSingle() - { - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $content = $searchService->findSingle( - new Criterion\ContentId( - [4] - ) - ); - - $this->assertEquals( - 4, - $content->id - ); - } - - public function testFindNoPerformCount() - { - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $query = new Query(); - $query->performCount = false; - $query->query = new Criterion\ContentTypeId( - [4] - ); - - $searchHit = $searchService->findContent($query); - - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - $this->assertNull( - $searchHit->totalCount - ); - } else { - $this->assertEquals( - 2, - $searchHit->totalCount - ); - } - } - - public function testFindNoPerformCountException() - { - $this->expectException(\RuntimeException::class); - - if (ltrim(get_class($this->getSetupFactory()), '\\') !== 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - $this->markTestSkipped('Only applicable to Legacy/DB based search'); - } - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $query = new Query(); - $query->performCount = false; - $query->limit = 0; - $query->query = new Criterion\ContentTypeId( - [4] - ); - - $searchService->findContent($query); - } - - public function testFindLocationsNoPerformCount() - { - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $query = new LocationQuery(); - $query->performCount = false; - $query->query = new Criterion\ContentTypeId( - [4] - ); - - $searchHit = $searchService->findLocations($query); - - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - $this->assertNull( - $searchHit->totalCount - ); - } else { - $this->assertEquals( - 2, - $searchHit->totalCount - ); - } - } - - public function testFindLocationsNoPerformCountException() - { - $this->expectException(\RuntimeException::class); - - if (ltrim(get_class($this->getSetupFactory()), '\\') !== 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - $this->markTestSkipped('Only applicable to Legacy/DB based search'); - } - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $query = new LocationQuery(); - $query->performCount = false; - $query->limit = 0; - $query->query = new Criterion\ContentTypeId( - [4] - ); - - $searchService->findLocations($query); - } - - /** - * Create movie Content with subtitle field set to null. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content[] - */ - protected function createMovieContent() - { - $movies = []; - - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - - $createStruct = $contentTypeService->newContentTypeCreateStruct('movie'); - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->remoteId = 'movie-123'; - $createStruct->names = ['eng-GB' => 'Movie']; - $createStruct->creatorId = 14; - $createStruct->creationDate = new \DateTime(); - - $fieldTitle = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); - $fieldTitle->names = ['eng-GB' => 'Title']; - $fieldTitle->fieldGroup = 'main'; - $fieldTitle->position = 1; - $fieldTitle->isTranslatable = false; - $fieldTitle->isSearchable = true; - $fieldTitle->isRequired = true; - $createStruct->addFieldDefinition($fieldTitle); - - $fieldSubtitle = $contentTypeService->newFieldDefinitionCreateStruct('subtitle', 'ezstring'); - $fieldSubtitle->names = ['eng-GB' => 'Subtitle']; - $fieldSubtitle->fieldGroup = 'main'; - $fieldSubtitle->position = 2; - $fieldSubtitle->isTranslatable = false; - $fieldSubtitle->isSearchable = true; - $fieldSubtitle->isRequired = false; - $createStruct->addFieldDefinition($fieldSubtitle); - - $contentTypeGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); - $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentTypeGroup]); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); - - $createStructRambo = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStructRambo->remoteId = 'movie-456'; - $createStructRambo->alwaysAvailable = false; - $createStructRambo->setField('title', 'Rambo'); - - $ramboDraft = $contentService->createContent($createStructRambo); - $movies[] = $contentService->publishVersion($ramboDraft->getVersionInfo()); - $this->refreshSearch($repository); - $createStructRobocop = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStructRobocop->remoteId = 'movie-789'; - $createStructRobocop->alwaysAvailable = false; - $createStructRobocop->setField('title', 'Robocop'); - $createStructRobocop->setField('subtitle', ''); - - $robocopDraft = $contentService->createContent($createStructRobocop); - $movies[] = $contentService->publishVersion($robocopDraft->getVersionInfo()); - $this->refreshSearch($repository); - $createStructLastHope = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStructLastHope->remoteId = 'movie-101112'; - $createStructLastHope->alwaysAvailable = false; - $createStructLastHope->setField('title', 'Star Wars'); - $createStructLastHope->setField('subtitle', 'Last Hope'); - - $lastHopeDraft = $contentService->createContent($createStructLastHope); - $movies[] = $contentService->publishVersion($lastHopeDraft->getVersionInfo()); - - $this->refreshSearch($repository); - - return $movies; - } - - /** - * Create test Content with ezcountry field having multiple countries selected. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content[] - */ - protected function createMultipleCountriesContent() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - - $createStruct = $contentTypeService->newContentTypeCreateStruct('countries-multiple'); - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->remoteId = 'countries-multiple-123'; - $createStruct->names = ['eng-GB' => 'Multiple countries']; - $createStruct->creatorId = 14; - $createStruct->creationDate = new \DateTime(); - - $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('countries', 'ezcountry'); - $fieldCreate->names = ['eng-GB' => 'Countries']; - $fieldCreate->fieldGroup = 'main'; - $fieldCreate->position = 1; - $fieldCreate->isTranslatable = false; - $fieldCreate->isSearchable = true; - $fieldCreate->fieldSettings = ['isMultiple' => true]; - - $createStruct->addFieldDefinition($fieldCreate); - - $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); - $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->remoteId = 'countries-multiple-456'; - $createStruct->alwaysAvailable = false; - $createStruct->setField( - 'countries', - ['BE', 'DE', 'FR', 'HR', 'NO', 'PT', 'RU'] - ); - - $draft = $contentService->createContent($createStruct); - $content = $contentService->publishVersion($draft->getVersionInfo()); - - $this->refreshSearch($repository); - - return $content; - } - - /** - * Test for the findContent() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content[] - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testFieldIsEmpty() - { - $testContents = $this->createMovieContent(); - - $query = new Query( - [ - 'query' => new Criterion\IsFieldEmpty('subtitle'), - ] - ); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - $result = $searchService->findContent($query, ['eng-GB']); - - $this->assertEquals(2, $result->totalCount); - - $this->assertEquals( - $testContents[0]->id, - $result->searchHits[0]->valueObject->id - ); - $this->assertEquals( - $testContents[1]->id, - $result->searchHits[1]->valueObject->id - ); - - return $testContents; - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testFieldIsNotEmpty() - { - $testContents = $this->createMovieContent(); - - $query = new Query( - [ - 'query' => new Criterion\IsFieldEmpty( - 'subtitle', - false - ), - ] - ); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - $result = $searchService->findContent($query, ['eng-GB']); - - $this->assertEquals(1, $result->totalCount); - $this->assertEquals( - $testContents[2]->id, - $result->searchHits[0]->valueObject->id - ); - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testFieldCollectionContains() - { - $testContent = $this->createMultipleCountriesContent(); - - $query = new Query( - [ - 'query' => new Criterion\Field( - 'countries', - Criterion\Operator::CONTAINS, - 'Belgium' - ), - ] - ); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - $result = $searchService->findContent($query); - - $this->assertEquals(1, $result->totalCount); - $this->assertEquals( - $testContent->id, - $result->searchHits[0]->valueObject->id - ); - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testFieldCollectionContains - */ - public function testFieldCollectionContainsNoMatch() - { - $this->createMultipleCountriesContent(); - $query = new Query( - [ - 'query' => new Criterion\Field( - 'countries', - Criterion\Operator::CONTAINS, - 'Netherlands Antilles' - ), - ] - ); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - $result = $searchService->findContent($query); - - $this->assertEquals(0, $result->totalCount); - } - - public function testInvalidFieldIdentifierRange() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$criterion->target\' is invalid: No searchable Fields found for the provided Criterion target \'some_hopefully_unknown_field\''); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $searchService->findContent( - new Query( - [ - 'filter' => new Criterion\Field( - 'some_hopefully_unknown_field', - Criterion\Operator::BETWEEN, - [10, 1000] - ), - 'sortClauses' => [new SortClause\ContentId()], - ] - ) - ); - } - - public function testInvalidFieldIdentifierIn() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$criterion->target\' is invalid: No searchable Fields found for the provided Criterion target \'some_hopefully_unknown_field\''); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $searchService->findContent( - new Query( - [ - 'filter' => new Criterion\Field( - 'some_hopefully_unknown_field', - Criterion\Operator::EQ, - 1000 - ), - 'sortClauses' => [new SortClause\ContentId()], - ] - ) - ); - } - - public function testFindContentWithNonSearchableField() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$criterion->target\' is invalid: No searchable Fields found for the provided Criterion target \'tag_cloud_url\''); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $searchService->findContent( - new Query( - [ - 'filter' => new Criterion\Field( - 'tag_cloud_url', - Criterion\Operator::EQ, - 'http://nimbus.com' - ), - 'sortClauses' => [new SortClause\ContentId()], - ] - ) - ); - } - - public function testSortFieldWithNonSearchableField() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$sortClause->targetData\' is invalid: No searchable Fields found for the provided Sort Clause target \'title\' on \'template_look\''); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $searchService->findContent( - new Query( - [ - 'sortClauses' => [new SortClause\Field('template_look', 'title')], - ] - ) - ); - } - - public function testSortMapLocationDistanceWithNonSearchableField() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$sortClause->targetData\' is invalid: No searchable Fields found for the provided Sort Clause target \'title\' on \'template_look\''); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $searchService->findContent( - new Query( - [ - 'sortClauses' => [ - new SortClause\MapLocationDistance( - 'template_look', - 'title', - 1, - 2 - ), - ], - ] - ) - ); - } - - public function testFindSingleFailMultiple() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $searchService->findSingle( - new Criterion\ContentId( - [4, 10] - ) - ); - } - - public function testFindSingleWithNonSearchableField() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $searchService->findSingle( - new Criterion\Field( - 'tag_cloud_url', - Criterion\Operator::EQ, - 'http://nimbus.com' - ) - ); - } - - public function getSortedContentSearches() - { - $fixtureDir = $this->getFixtureDir(); - - yield [ - [ - 'filter' => new Criterion\SectionId([2]), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [], - ], - $fixtureDir . 'SortNone.php', - // Result having the same sort level should be sorted between them to be system independent - static function (&$data): void { - usort( - $data->searchHits, - static function (SearchHit $a, SearchHit $b): int { - return $a->valueObject['id'] <=> $b->valueObject['id']; - } - ); - }, - ]; - - yield [ - [ - 'filter' => new Criterion\SectionId([2]), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [ - new SortClause\DatePublished(), - new SortClause\ContentId(), - ], - ], - $fixtureDir . 'SortDatePublished.php', - ]; - - yield [ - [ - 'filter' => new Criterion\SectionId([2]), - 'offset' => 0, - 'limit' => 50, - 'sortClauses' => [ - new SortClause\DateModified(), - new SortClause\ContentId(), - ], - ], - $fixtureDir . 'SortDateModified.php', - ]; - - yield [ - [ - 'filter' => new Criterion\SectionId([4, 2, 6, 3]), - 'offset' => 0, - 'limit' => 50, - 'sortClauses' => [ - new SortClause\SectionIdentifier(), - new SortClause\ContentId(), - ], - ], - $fixtureDir . 'SortSectionIdentifier.php', - ]; - - yield [ - [ - 'filter' => new Criterion\SectionId([4, 2, 6, 3]), - 'offset' => 0, - 'limit' => 50, - 'sortClauses' => [ - new SortClause\SectionName(), - new SortClause\ContentId(), - ], - ], - $fixtureDir . 'SortSectionName.php', - ]; - - yield [ - [ - 'filter' => new Criterion\SectionId([2, 3]), - 'offset' => 0, - 'limit' => 50, - 'sortClauses' => [ - new SortClause\ContentName(), - new SortClause\ContentId(), - ], - ], - $fixtureDir . 'SortContentName.php', - ]; - - yield [ - [ - 'filter' => new Criterion\ContentTypeId(1), - 'offset' => 0, - 'limit' => 50, - 'sortClauses' => [ - new SortClause\Field('folder', 'name', Query::SORT_ASC), - new SortClause\ContentId(), - ], - ], - $fixtureDir . 'SortFolderName.php', - ]; - - yield [ - [ - 'filter' => new Criterion\ContentTypeId([1, 3]), - 'offset' => 0, - 'limit' => 50, - 'sortClauses' => [ - new SortClause\Field('folder', 'name', Query::SORT_ASC), - new SortClause\ContentId(), - ], - ], - $fixtureDir . 'SortFieldMultipleTypes.php', - ]; - - yield [ - [ - 'filter' => new Criterion\ContentTypeId([1, 3]), - 'offset' => 0, - 'limit' => 50, - 'sortClauses' => [ - new SortClause\Field('folder', 'name', Query::SORT_DESC), - new SortClause\ContentId(), - ], - ], - $fixtureDir . 'SortFieldMultipleTypesReverse.php', - ]; - - yield [ - [ - 'filter' => new Criterion\ContentTypeId([1, 3]), - 'offset' => 3, - 'limit' => 5, - 'sortClauses' => [ - new SortClause\Field('folder', 'name', Query::SORT_ASC), - new SortClause\Field('user', 'first_name', Query::SORT_ASC), - new SortClause\ContentId(), - ], - ], - $fixtureDir . 'SortFieldMultipleTypesSlice.php', - ]; - - yield [ - [ - 'filter' => new Criterion\ContentTypeId([1, 3]), - 'offset' => 3, - 'limit' => 5, - 'sortClauses' => [ - new SortClause\Field('folder', 'name', Query::SORT_DESC), - new SortClause\Field('user', 'first_name', Query::SORT_ASC), - new SortClause\ContentId(), - ], - ], - $fixtureDir . 'SortFieldMultipleTypesSliceReverse.php', - ]; - - if (!$this->isLegacySearchEngineSetup()) { - yield [ - [ - 'filter' => new Criterion\ContentTypeId(1), - 'offset' => 0, - 'limit' => 50, - 'sortClauses' => [ - new SortClause\CustomField('folder_name_value_s', Query::SORT_ASC), - new SortClause\ContentId(), - ], - ], - $fixtureDir . 'SortFolderName.php', - ]; - } - } - - public function getSortedLocationSearches() - { - $fixtureDir = $this->getFixtureDir(); - - return [ - [ - [ - 'filter' => new Criterion\SectionId([2]), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [new SortClause\Location\Path(Query::SORT_DESC)], - ], - $fixtureDir . 'SortPathString.php', - ], - [ - [ - 'filter' => new Criterion\SectionId([2]), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [new SortClause\Location\Depth(Query::SORT_ASC)], - ], - $fixtureDir . 'SortLocationDepth.php', - // Result having the same sort level should be sorted between them to be system independent - static function (&$data) { - // Result with ids: - // 4 has depth = 1 - // 11, 12, 13, 42, 59 have depth = 2 - // 10, 14 have depth = 3 - $map = [ - 4 => 0, - 11 => 1, - 12 => 2, - 13 => 3, - 42 => 4, - 59 => 5, - 10 => 6, - 14 => 7, - ]; - usort( - $data->searchHits, - static function ($a, $b) use ($map) { - return ($map[$a->valueObject['id']] < $map[$b->valueObject['id']]) ? -1 : 1; - } - ); - }, - ], - [ - [ - 'filter' => new Criterion\SectionId([3]), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [ - new SortClause\Location\Path(Query::SORT_DESC), - new SortClause\ContentName(Query::SORT_ASC), - ], - ], - $fixtureDir . 'SortMultiple.php', - ], - [ - [ - 'filter' => new Criterion\SectionId([2]), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [ - new SortClause\Location\Priority(Query::SORT_DESC), - new SortClause\ContentId(), - ], - ], - $fixtureDir . 'SortDesc.php', - ], - ]; - } - - /** - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType - */ - protected function createTestContentType() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $createStruct = $contentTypeService->newContentTypeCreateStruct('test-type'); - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->names = ['eng-GB' => 'Test type']; - $createStruct->creatorId = 14; - $createStruct->creationDate = new \DateTime(); - - $translatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('integer', 'ezinteger'); - $translatableFieldCreate->names = ['eng-GB' => 'Simple translatable integer field']; - $translatableFieldCreate->fieldGroup = 'main'; - $translatableFieldCreate->position = 1; - $translatableFieldCreate->isTranslatable = true; - $translatableFieldCreate->isSearchable = true; - - $createStruct->addFieldDefinition($translatableFieldCreate); - - $nonTranslatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('integer2', 'ezinteger'); - $nonTranslatableFieldCreate->names = ['eng-GB' => 'Simple non-translatable integer field']; - $nonTranslatableFieldCreate->fieldGroup = 'main'; - $nonTranslatableFieldCreate->position = 2; - $nonTranslatableFieldCreate->isTranslatable = false; - $nonTranslatableFieldCreate->isSearchable = true; - - $createStruct->addFieldDefinition($nonTranslatableFieldCreate); - - $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); - $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); - - return $contentType; - } - - /** - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * @param int $fieldValue11 Value for translatable field in first language - * @param int $fieldValue12 Value for translatable field in second language - * @param int $fieldValue2 Value for non translatable field - * @param string $mainLanguageCode - * @param bool $alwaysAvailable - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - protected function createMultilingualContent( - $contentType, - $fieldValue11 = null, - $fieldValue12 = null, - $fieldValue2 = null, - $mainLanguageCode = 'eng-GB', - $alwaysAvailable = false - ) { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = $alwaysAvailable; - $createStruct->mainLanguageCode = $mainLanguageCode; - if ($fieldValue11) { - $createStruct->setField('integer', $fieldValue11, 'eng-GB'); - } - if ($fieldValue12) { - $createStruct->setField('integer', $fieldValue12, 'ger-DE'); - } - $createStruct->setField('integer2', $fieldValue2, $mainLanguageCode); - - $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(2); - $draft = $contentService->createContent($createStruct, [$locationCreateStruct]); - $content = $contentService->publishVersion($draft->getVersionInfo()); - - $this->refreshSearch($repository); - - return $content; - } - - public function providerForTestMultilingualFieldSort() - { - return [ - 0 => [ - [ - 1 => [1, 2, 1], - 2 => [2, 1, 2], - 3 => [2, 1, 3], - 4 => [1, 2, 4], - ], - [ - 'languages' => [ - 'eng-GB', - 'ger-DE', - ], - ], - [ - new SortClause\Field('test-type', 'integer', Query::SORT_ASC), - new SortClause\Field('test-type', 'integer2', Query::SORT_DESC), - ], - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 4, 1, 2, 4 - * Content 1, 1, 2, 1 - * Content 3, 2, 1, 3 - * Content 2, 2, 1, 2 - */ - [4, 1, 3, 2], - ], - 1 => [ - [ - 1 => [1, 2, 1], - 2 => [2, 1, 2], - 3 => [2, 1, 3], - 4 => [1, 2, 4], - ], - [ - 'languages' => [ - 'ger-DE', - 'eng-GB', - ], - ], - [ - new SortClause\Field('test-type', 'integer', Query::SORT_ASC), - new SortClause\Field('test-type', 'integer2', Query::SORT_DESC), - ], - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 3, 2, 1, 3 - * Content 2, 2, 1, 2 - * Content 4, 1, 2, 4 - * Content 1, 1, 2, 1 - */ - [3, 2, 4, 1], - ], - 2 => [ - [ - 1 => [null, 2, null, 'ger-DE'], - 2 => [3, null, null, 'eng-GB'], - 3 => [4, null, null, 'eng-GB'], - 4 => [null, 1, null, 'ger-DE'], - ], - [ - 'languages' => [ - 'eng-GB', - ], - ], - [ - new SortClause\Field('test-type', 'integer', Query::SORT_DESC), - ], - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 3, 4, - - * Content 2, 3, - - */ - [3, 2], - ], - 3 => [ - [ - 1 => [null, 2, null, 'ger-DE'], - 2 => [3, null, null, 'eng-GB'], - 3 => [4, null, null, 'eng-GB'], - 4 => [null, 1, null, 'ger-DE'], - ], - [ - 'languages' => [ - 'ger-DE', - ], - ], - [ - new SortClause\Field('test-type', 'integer', Query::SORT_DESC), - ], - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 1, -, 2 - * Content 4, -, 1 - */ - [1, 4], - ], - 4 => [ - [ - 1 => [null, 2, null, 'ger-DE'], - 2 => [3, null, null, 'eng-GB'], - 3 => [4, null, null, 'eng-GB'], - 4 => [null, 1, null, 'ger-DE'], - ], - [ - 'languages' => [ - 'eng-GB', - 'ger-DE', - ], - ], - [ - new SortClause\Field('test-type', 'integer', Query::SORT_DESC), - ], - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 3, 4, - - * Content 2, 3, - - * Content 1, -, 2 - * Content 4, -, 1 - */ - [3, 2, 1, 4], - ], - 5 => [ - [ - 1 => [null, 2, null, 'ger-DE'], - 2 => [3, null, null, 'eng-GB'], - 3 => [4, null, null, 'eng-GB'], - 4 => [null, 1, null, 'ger-DE'], - ], - [ - 'languages' => [ - 'ger-DE', - 'eng-GB', - ], - ], - [ - new SortClause\Field('test-type', 'integer', Query::SORT_DESC), - ], - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 3, 4, - - * Content 2, 3, - - * Content 1, -, 2 - * Content 4, -, 1 - */ - [3, 2, 1, 4], - ], - 6 => [ - [ - 1 => [null, 2, null, 'ger-DE'], - 2 => [3, 4, null, 'eng-GB'], - 3 => [4, 3, null, 'eng-GB'], - 4 => [null, 1, null, 'ger-DE'], - ], - [ - 'languages' => [ - 'eng-GB', - 'ger-DE', - ], - ], - [ - new SortClause\Field('test-type', 'integer', Query::SORT_DESC), - ], - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 3, 4, 3 - * Content 2, 3, 4 - * Content 1, -, 2 - * Content 4, -, 1 - */ - [3, 2, 1, 4], - ], - 7 => [ - [ - 1 => [null, 2, null, 'ger-DE'], - 2 => [3, 4, null, 'eng-GB'], - 3 => [4, 3, null, 'eng-GB'], - 4 => [null, 1, null, 'ger-DE'], - ], - [ - 'languages' => [ - 'ger-DE', - 'eng-GB', - ], - ], - [ - new SortClause\Field('test-type', 'integer', Query::SORT_DESC), - ], - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 2, 3, 4 - * Content 3, 4, 3 - * Content 1, -, 2 - * Content 4, -, 1 - */ - [2, 3, 1, 4], - ], - 8 => [ - [ - 1 => [null, 1, null, 'ger-DE', true], - 2 => [4, null, null, 'eng-GB', true], - 3 => [3, null, null, 'eng-GB', false], - 4 => [null, 2, null, 'ger-DE', false], - ], - [ - 'languages' => [ - 'eng-GB', - ], - ], - [ - new SortClause\Field('test-type', 'integer', Query::SORT_ASC), - ], - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 1, -, 1 - * Content 3, 3, - - * Content 2, 4, - - */ - [1, 3, 2], - ], - 9 => [ - [ - 1 => [null, 1, null, 'ger-DE', true], - 2 => [4, null, null, 'eng-GB', true], - 3 => [3, null, null, 'eng-GB', false], - 4 => [null, 2, null, 'ger-DE', false], - ], - [ - 'languages' => [ - 'ger-DE', - ], - ], - [ - new SortClause\Field('test-type', 'integer', Query::SORT_DESC), - ], - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 2, 4, - - * Content 4, -, 2 - * Content 1, -, 1 - */ - [2, 4, 1], - ], - 10 => [ - [ - 1 => [null, 1, null, 'ger-DE', true], - 2 => [4, null, null, 'eng-GB', true], - 3 => [3, null, null, 'eng-GB', false], - 4 => [null, 2, null, 'ger-DE', false], - ], - [ - 'languages' => [ - 'eng-GB', - ], - 'useAlwaysAvailable' => false, - ], - [ - new SortClause\Field('test-type', 'integer', Query::SORT_ASC), - ], - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 3, 3, - - * Content 2, 4, - - */ - [3, 2], - ], - 11 => [ - [ - 1 => [null, 1, null, 'ger-DE', true], - 2 => [4, null, null, 'eng-GB', true], - 3 => [3, null, null, 'eng-GB', false], - 4 => [null, 2, null, 'ger-DE', false], - ], - [ - 'languages' => [ - 'ger-DE', - ], - 'useAlwaysAvailable' => false, - ], - [ - new SortClause\Field('test-type', 'integer', Query::SORT_DESC), - ], - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 4, -, 2 - * Content 1, -, 1 - */ - [4, 1], - ], - ]; - } - - /** - * Test for the findContent() method. - * - * @group rrr - * @dataProvider providerForTestMultilingualFieldSort - * - * @param array $contentDataList - * @param array $languageSettings - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses - * @param array $expected - */ - public function testMultilingualFieldSortContent( - array $contentDataList, - $languageSettings, - array $sortClauses, - $expected - ) { - $this->assertMultilingualFieldSort( - $contentDataList, - $languageSettings, - $sortClauses, - $expected - ); - } - - /** - * Test for the findLocations() method. - * - * @group rrr - * @dataProvider providerForTestMultilingualFieldSort - * - * @param array $contentDataList - * @param array $languageSettings - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses - * @param array $expected - */ - public function testMultilingualFieldSortLocation( - array $contentDataList, - $languageSettings, - array $sortClauses, - $expected - ) { - $this->assertMultilingualFieldSort( - $contentDataList, - $languageSettings, - $sortClauses, - $expected, - false - ); - } - - /** - * @param array $contentDataList - * @param array $languageSettings - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses - * @param array $expected - * @param bool $contentSearch - */ - protected function assertMultilingualFieldSort( - array $contentDataList, - $languageSettings, - array $sortClauses, - $expected, - $contentSearch = true - ) { - $contentType = $this->createTestContentType(); - - // Create a draft to account for behaviour with ContentType in different states - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentTypeService->createContentTypeDraft($contentType); - - $defaults = [null, null, null, 'eng-GB', false]; - $contentIdList = []; - foreach ($contentDataList as $key => $contentData) { - $contentData = $contentData + $defaults; - list( - $fieldValue11, - $fieldValue12, - $fieldValue2, - $mainLanguageCode, - $alwaysAvailable - ) = $contentData; - - $contentIdList[$key] = $this->createMultilingualContent( - $contentType, - $fieldValue11, - $fieldValue12, - $fieldValue2, - $mainLanguageCode, - $alwaysAvailable - )->id; - } - - // "article" type Content is not matched, this ensures that non-matched - // field does not affect sort - $dummySortClause = new SortClause\Field('article', 'title', Query::SORT_ASC); - array_unshift($sortClauses, $dummySortClause); - $sortClauses[] = $dummySortClause; - - $searchService = $repository->getSearchService(); - if ($contentSearch) { - $query = new Query( - [ - 'query' => new Criterion\ContentTypeId($contentType->id), - 'sortClauses' => $sortClauses, - ] - ); - $result = $searchService->findContent($query, $languageSettings); - } else { - $query = new LocationQuery( - [ - 'query' => new Criterion\ContentTypeId($contentType->id), - 'sortClauses' => $sortClauses, - ] - ); - $result = $searchService->findLocations($query, $languageSettings); - } - - $this->assertEquals(count($expected), $result->totalCount); - - $expectedIdList = []; - foreach ($expected as $contentNumber) { - $expectedIdList[] = $contentIdList[$contentNumber]; - } - - $this->assertEquals($expectedIdList, $this->mapResultContentIds($result)); - } - - public function providerForTestMultilingualFieldFilter() - { - return [ - 0 => [ - $fixture = [ - 1 => [null, 1, null, 'ger-DE', true], - 2 => [4, null, null, 'eng-GB', true], - 3 => [3, null, null, 'eng-GB', false], - 4 => [null, 2, null, 'ger-DE', false], - 5 => [5, null, null, 'eng-GB', true], - ], - $languageSettings = [ - 'languages' => [ - 'ger-DE', - ], - ], - new Criterion\Field('integer', Criterion\Operator::LT, 5), - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 2, 4, - - * Content 4, -, 2 - * Content 1, -, 1 - */ - [2, 4, 1], - ], - 1 => [ - $fixture, - [ - 'languages' => [ - 'ger-DE', - ], - 'useAlwaysAvailable' => false, - ], - new Criterion\Field('integer', Criterion\Operator::LT, 2), - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 1, -, 1 - */ - [1], - ], - 2 => [ - $fixture, - [ - 'languages' => [ - 'eng-GB', - ], - ], - new Criterion\Field('integer', Criterion\Operator::LTE, 4), - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 5, 5, - - * Content 2, 4, - - * Content 3, 3, - - * Content 1, -, 1 - */ - [2, 3, 1], - ], - 3 => [ - $fixture, - [ - 'languages' => [ - 'eng-GB', - ], - 'useAlwaysAvailable' => false, - ], - new Criterion\Field('integer', Criterion\Operator::LTE, 4), - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 2, 4, - - * Content 3, 3, - - */ - [2, 3], - ], - 4 => [ - $fixture, - $languageSettings, - new Criterion\Field('integer', Criterion\Operator::LTE, 4), - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 2, 4, - - * Content 4, -, 2 - * Content 1, -, 1 - */ - [2, 4, 1], - ], - 5 => [ - $fixture, - $languageSettings, - new Criterion\Field('integer', Criterion\Operator::GT, 1), - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 5, 5, - - * Content 2, 4, - - * Content 4, -, 2 - */ - [5, 2, 4], - ], - 6 => [ - $fixture, - $languageSettings, - new Criterion\Field('integer', Criterion\Operator::GTE, 2), - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 5, 5, - - * Content 2, 4, - - * Content 4, -, 2 - */ - [5, 2, 4], - ], - 7 => [ - $fixture, - $languageSettings, - new Criterion\Field('integer', Criterion\Operator::BETWEEN, [2, 4]), - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 2, 4, - - * Content 4, -, 2 - */ - [2, 4], - ], - 8 => [ - $fixture, - $languageSettings, - new Criterion\Field('integer', Criterion\Operator::BETWEEN, [4, 2]), - [], - ], - 9 => [ - $fixture, - $languageSettings, - new Criterion\Field('integer', Criterion\Operator::EQ, 4), - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 4, -, 2 - */ - [2], - ], - 10 => [ - $fixture, - $languageSettings, - new Criterion\Field('integer', Criterion\Operator::EQ, 2), - /** - * Expected order, Value eng-GB, Value ger-DE. - * - * Content 2, 4, - - */ - [4], - ], - ]; - } - - /** - * Test for the findContent() method. - * - * @group ttt - * @dataProvider providerForTestMultilingualFieldFilter - * - * @param array $contentDataList - * @param array $languageSettings - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * @param array $expected - */ - public function testMultilingualFieldFilterContent( - array $contentDataList, - $languageSettings, - Criterion $criterion, - $expected - ) { - $this->assertMultilingualFieldFilter( - $contentDataList, - $languageSettings, - $criterion, - $expected - ); - } - - /** - * Test for the findLocations() method. - * - * @group ttt - * @dataProvider providerForTestMultilingualFieldFilter - * - * @param array $contentDataList - * @param array $languageSettings - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * @param array $expected - */ - public function testMultilingualFieldFilterLocation( - array $contentDataList, - $languageSettings, - Criterion $criterion, - $expected - ) { - $this->assertMultilingualFieldFilter( - $contentDataList, - $languageSettings, - $criterion, - $expected, - false - ); - } - - /** - * @param array $contentDataList - * @param array $languageSettings - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * @param array $expected - * @param bool $contentSearch - */ - protected function assertMultilingualFieldFilter( - array $contentDataList, - $languageSettings, - Criterion $criterion, - $expected, - $contentSearch = true - ) { - $contentType = $this->createTestContentType(); - - // Create a draft to account for behaviour with ContentType in different states - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentTypeService->createContentTypeDraft($contentType); - - $defaults = [null, null, null, 'eng-GB', false]; - $contentIdList = []; - foreach ($contentDataList as $key => $contentData) { - $contentData = $contentData + $defaults; - list( - $fieldValue11, - $fieldValue12, - $fieldValue2, - $mainLanguageCode, - $alwaysAvailable - ) = $contentData; - - $contentIdList[$key] = $this->createMultilingualContent( - $contentType, - $fieldValue11, - $fieldValue12, - $fieldValue2, - $mainLanguageCode, - $alwaysAvailable - )->id; - } - - $sortClause = new SortClause\Field('test-type', 'integer', Query::SORT_DESC); - $searchService = $repository->getSearchService(); - if ($contentSearch) { - $query = new Query( - [ - 'query' => new Criterion\LogicalAnd( - [ - new Criterion\ContentTypeId($contentType->id), - $criterion, - ] - ), - 'sortClauses' => [$sortClause], - ] - ); - $result = $searchService->findContent($query, $languageSettings); - } else { - $query = new LocationQuery( - [ - 'query' => new Criterion\LogicalAnd( - [ - new Criterion\ContentTypeId($contentType->id), - $criterion, - ] - ), - 'sortClauses' => [$sortClause], - ] - ); - $result = $searchService->findLocations($query, $languageSettings); - } - - $this->assertEquals(count($expected), $result->totalCount); - - $expectedIdList = []; - foreach ($expected as $contentNumber) { - $expectedIdList[] = $contentIdList[$contentNumber]; - } - - $this->assertEquals($expectedIdList, $this->mapResultContentIds($result)); - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $result - * - * @return array - */ - protected function mapResultContentIds(SearchResult $result) - { - return array_map( - static function (SearchHit $searchHit) { - if ($searchHit->valueObject instanceof Location) { - return $searchHit->valueObject->contentInfo->id; - } - - return $searchHit->valueObject->id; - }, - $result->searchHits - ); - } - - /** - * Test for the findContent() method. - * - * @dataProvider getSortedContentSearches - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testFindAndSortContent($queryData, $fixture, $closure = null) - { - $query = new Query($queryData); - $this->assertQueryFixture($query, $fixture, $closure); - } - - /** - * Test for the findContentInfo() method. - * - * @dataProvider getSortedContentSearches - * - * @see \eZ\Publish\API\Repository\SearchService::findContentInfo() - */ - public function testFindAndSortContentInfo($queryData, $fixture, $closure = null) - { - $query = new Query($queryData); - $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true); - } - - /** - * Test for the findLocations() method. - * - * @dataProvider getSortedContentSearches - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - */ - public function testFindAndSortContentLocations($queryData, $fixture, $closure = null) - { - $query = new LocationQuery($queryData); - $this->assertQueryFixture($query, $fixture, $closure); - } - - /** - * Test for the findLocations() method. - * - * @dataProvider getSortedLocationSearches - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - */ - public function testFindAndSortLocations($queryData, $fixture, $closure = null) - { - $query = new LocationQuery($queryData); - $this->assertQueryFixture($query, $fixture, $closure); - } - - /** - * Test for the findContent() method. - * - * @dataProvider getFacetedSearches - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testFindFacetedContent(Query $query, $fixture) - { - $this->assertQueryFixture($query, $fixture); - } - - /** - * Test for the findContentInfo() method. - * - * @dataProvider getFacetedSearches - * - * @see \eZ\Publish\API\Repository\SearchService::findContentInfo() - */ - public function testFindFacetedContentInfo(Query $query, $fixture) - { - $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure(), true); - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testQueryCustomField() - { - $query = new Query( - [ - 'query' => new Criterion\CustomField( - 'custom_field', - Criterion\Operator::EQ, - 'AdMiNiStRaToR' - ), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [new SortClause\ContentId()], - ] - ); - $this->assertQueryFixture( - $query, - $this->getFixtureDir() . '/QueryCustomField.php' - ); - } - - /** - * Test for the findContent() method. - * - * This tests explicitely queries the first_name while user is contained in - * the last_name of admin and anonymous. This is done to show the custom - * copy field working. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testQueryModifiedField() - { - // Check using get_class since the others extend SetupFactory\Legacy - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - $this->markTestIncomplete( - 'Custom fields not supported by LegacySE ' . - '(@todo: Legacy should fallback to just querying normal field so this should be tested here)' - ); - } - - $query = new Query( - [ - 'query' => new Criterion\Field( - 'first_name', - Criterion\Operator::EQ, - 'User' - ), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [new SortClause\ContentId()], - ] - ); - $query->query->setCustomField('user', 'first_name', 'custom_field'); - - $this->assertQueryFixture( - $query, - $this->getFixtureDir() . '/QueryModifiedField.php' - ); - } - - /** - * Test for the findContent() method. - * - * This tests first explicitly creates sort clause on the 'short_name' which is empty - * for all Content instances of 'folder' ContentType. Custom sort field is then set - * to the index storage name of folder's 'name' field, in order to show the custom - * sort field working. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testSortModifiedField() - { - // Check using get_class since the others extend SetupFactory\Legacy - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - $this->markTestIncomplete( - 'Custom field sort not supported by LegacySE ' . - '(@todo: Legacy should fallback to just querying normal field so this should be tested here)' - ); - } - - $sortClause = new SortClause\Field('folder', 'short_name', Query::SORT_ASC); - $sortClause->setCustomField('folder', 'short_name', 'folder_name_value_s'); - - $query = new Query( - [ - 'filter' => new Criterion\ContentTypeId(1), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [ - $sortClause, - new SortClause\ContentId(), - ], - ] - ); - - $this->assertQueryFixture( - $query, - $this->getFixtureDir() . '/SortFolderName.php' - ); - } - - /** - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType - */ - protected function createTestPlaceContentType() - { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - - $createStruct = $contentTypeService->newContentTypeCreateStruct('testtype'); - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->names = ['eng-GB' => 'Test type']; - $createStruct->creatorId = 14; - $createStruct->creationDate = new \DateTime(); - - $translatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('maplocation', 'ezgmaplocation'); - $translatableFieldCreate->names = ['eng-GB' => 'Map location field']; - $translatableFieldCreate->fieldGroup = 'main'; - $translatableFieldCreate->position = 1; - $translatableFieldCreate->isTranslatable = false; - $translatableFieldCreate->isSearchable = true; - - $createStruct->addFieldDefinition($translatableFieldCreate); - - $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); - $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); - - return $contentType; - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * @group maplocation - */ - public function testMapLocationDistanceLessThanOrEqual() - { - $contentType = $this->createTestPlaceContentType(); - - // Create a draft to account for behaviour with ContentType in different states - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - $contentTypeService->createContentTypeDraft($contentType); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.894877, - 'longitude' => 15.972699, - 'address' => 'Here be wild boars', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.927334, - 'longitude' => 15.934847, - 'address' => 'A lone tree', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $tree = $contentService->publishVersion($draft->getVersionInfo()); - - $this->refreshSearch($repository); - - $query = new Query( - [ - 'filter' => new Criterion\LogicalAnd( - [ - new Criterion\ContentTypeId($contentType->id), - new Criterion\MapLocationDistance( - 'maplocation', - Criterion\Operator::LTE, - 240, - 43.756825, - 15.775074 - ), - ] - ), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [], - ] - ); - - $searchService = $repository->getSearchService(); - $result = $searchService->findContent($query); - - $this->assertEquals(1, $result->totalCount); - $this->assertEquals( - $wildBoars->id, - $result->searchHits[0]->valueObject->id - ); - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * @group maplocation - */ - public function testMapLocationDistanceGreaterThanOrEqual() - { - $contentType = $this->createTestPlaceContentType(); - - // Create a draft to account for behaviour with ContentType in different states - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - $contentTypeService->createContentTypeDraft($contentType); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.894877, - 'longitude' => 15.972699, - 'address' => 'Here be wild boars', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.927334, - 'longitude' => 15.934847, - 'address' => 'A lone tree', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $tree = $contentService->publishVersion($draft->getVersionInfo()); - - $this->refreshSearch($repository); - - $query = new Query( - [ - 'filter' => new Criterion\LogicalAnd( - [ - new Criterion\ContentTypeId($contentType->id), - new Criterion\MapLocationDistance( - 'maplocation', - Criterion\Operator::GTE, - 240, - 43.756825, - 15.775074 - ), - ] - ), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [], - ] - ); - - $searchService = $repository->getSearchService(); - $result = $searchService->findContent($query); - - $this->assertEquals(1, $result->totalCount); - $this->assertEquals( - $tree->id, - $result->searchHits[0]->valueObject->id - ); - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * @group maplocation - */ - public function testMapLocationDistanceBetween() - { - $contentType = $this->createTestPlaceContentType(); - - // Create a draft to account for behaviour with ContentType in different states - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - $contentTypeService->createContentTypeDraft($contentType); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.894877, - 'longitude' => 15.972699, - 'address' => 'Here be wild boars', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.927334, - 'longitude' => 15.934847, - 'address' => 'A lone tree', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $tree = $contentService->publishVersion($draft->getVersionInfo()); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.903777, - 'longitude' => 15.958788, - 'address' => 'Meadow with mushrooms', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $mushrooms = $contentService->publishVersion($draft->getVersionInfo()); - - $this->refreshSearch($repository); - - $query = new Query( - [ - 'filter' => new Criterion\LogicalAnd( - [ - new Criterion\ContentTypeId($contentType->id), - new Criterion\MapLocationDistance( - 'maplocation', - Criterion\Operator::BETWEEN, - [239, 241], - 43.756825, - 15.775074 - ), - ] - ), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [], - ] - ); - - $searchService = $repository->getSearchService(); - $result = $searchService->findContent($query); - - $this->assertEquals(1, $result->totalCount); - $this->assertEquals( - $mushrooms->id, - $result->searchHits[0]->valueObject->id - ); - } - - /** - * Test for the findContent() method. - * - * This tests the distance over the pole. The tests intentionally uses large range, - * as the flat Earth model used in Legacy Storage Search is not precise for the use case. - * What is tested here is that outer bounding box is correctly calculated, so that - * location is not excluded. - * - * Range between 222km and 350km shows the magnitude of error between great-circle - * (always very precise) and flat Earth (very imprecise for this use case) models. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * @group maplocation - */ - public function testMapLocationDistanceBetweenPolar() - { - $contentType = $this->createTestPlaceContentType(); - - // Create a draft to account for behaviour with ContentType in different states - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - $contentTypeService->createContentTypeDraft($contentType); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 89, - 'longitude' => -164, - 'address' => 'Polar bear media tower', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $polarBear = $contentService->publishVersion($draft->getVersionInfo()); - - $this->refreshSearch($repository); - - $query = new Query( - [ - 'filter' => new Criterion\LogicalAnd( - [ - new Criterion\ContentTypeId($contentType->id), - new Criterion\MapLocationDistance( - 'maplocation', - Criterion\Operator::BETWEEN, - [221, 350], - 89, - 16 - ), - ] - ), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [], - ] - ); - - $searchService = $repository->getSearchService(); - $result = $searchService->findContent($query); - - $this->assertEquals(1, $result->totalCount); - $this->assertEquals( - $polarBear->id, - $result->searchHits[0]->valueObject->id - ); - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * @group maplocation - */ - public function testMapLocationDistanceSortAscending() - { - $contentType = $this->createTestPlaceContentType(); - - // Create a draft to account for behaviour with ContentType in different states - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - $contentTypeService->createContentTypeDraft($contentType); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.894877, - 'longitude' => 15.972699, - 'address' => 'Here be wild boars', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.927334, - 'longitude' => 15.934847, - 'address' => 'A lone tree', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $tree = $contentService->publishVersion($draft->getVersionInfo()); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.903777, - 'longitude' => 15.958788, - 'address' => 'Meadow with mushrooms', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $mushrooms = $contentService->publishVersion($draft->getVersionInfo()); - - $this->refreshSearch($repository); - - $wellInVodice = [ - 'latitude' => 43.756825, - 'longitude' => 15.775074, - ]; - - $query = new Query( - [ - 'filter' => new Criterion\LogicalAnd( - [ - new Criterion\ContentTypeId($contentType->id), - new Criterion\MapLocationDistance( - 'maplocation', - Criterion\Operator::GTE, - 235, - $wellInVodice['latitude'], - $wellInVodice['longitude'] - ), - ] - ), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [ - new SortClause\MapLocationDistance( - 'testtype', - 'maplocation', - $wellInVodice['latitude'], - $wellInVodice['longitude'], - Query::SORT_ASC - ), - ], - ] - ); - - $searchService = $repository->getSearchService(); - $result = $searchService->findContent($query); - - $this->assertEquals(3, $result->totalCount); - $this->assertEquals( - $wildBoars->id, - $result->searchHits[0]->valueObject->id - ); - $this->assertEquals( - $mushrooms->id, - $result->searchHits[1]->valueObject->id - ); - $this->assertEquals( - $tree->id, - $result->searchHits[2]->valueObject->id - ); - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * @group maplocation - */ - public function testMapLocationDistanceSortDescending() - { - $contentType = $this->createTestPlaceContentType(); - - // Create a draft to account for behaviour with ContentType in different states - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - $contentTypeService->createContentTypeDraft($contentType); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.894877, - 'longitude' => 15.972699, - 'address' => 'Here be wild boars', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.927334, - 'longitude' => 15.934847, - 'address' => 'A lone tree', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $tree = $contentService->publishVersion($draft->getVersionInfo()); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.903777, - 'longitude' => 15.958788, - 'address' => 'Meadow with mushrooms', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $mushrooms = $contentService->publishVersion($draft->getVersionInfo()); - - $this->refreshSearch($repository); - - $well = [ - 'latitude' => 43.756825, - 'longitude' => 15.775074, - ]; - - $query = new Query( - [ - 'filter' => new Criterion\LogicalAnd( - [ - new Criterion\ContentTypeId($contentType->id), - new Criterion\MapLocationDistance( - 'maplocation', - Criterion\Operator::GTE, - 235, - $well['latitude'], - $well['longitude'] - ), - ] - ), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [ - new SortClause\MapLocationDistance( - 'testtype', - 'maplocation', - $well['latitude'], - $well['longitude'], - Query::SORT_DESC - ), - ], - ] - ); - - $searchService = $repository->getSearchService(); - $result = $searchService->findContent($query); - - $this->assertEquals(3, $result->totalCount); - $this->assertEquals( - $wildBoars->id, - $result->searchHits[2]->valueObject->id - ); - $this->assertEquals( - $mushrooms->id, - $result->searchHits[1]->valueObject->id - ); - $this->assertEquals( - $tree->id, - $result->searchHits[0]->valueObject->id - ); - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * @group maplocation - */ - public function testMapLocationDistanceWithCustomField() - { - $contentType = $this->createTestPlaceContentType(); - - // Create a draft to account for behaviour with ContentType in different states - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - $contentTypeService->createContentTypeDraft($contentType); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.894877, - 'longitude' => 15.972699, - 'address' => 'Here be wild boars', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.927334, - 'longitude' => 15.934847, - 'address' => 'A lone tree', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $tree = $contentService->publishVersion($draft->getVersionInfo()); - - $this->refreshSearch($repository); - - $distanceCriterion = new Criterion\MapLocationDistance( - 'maplocation', - Criterion\Operator::LTE, - 240, - 43.756825, - 15.775074 - ); - $distanceCriterion->setCustomField('testtype', 'maplocation', 'custom_geolocation_field'); - - $query = new Query( - [ - 'filter' => new Criterion\LogicalAnd( - [ - new Criterion\ContentTypeId($contentType->id), - $distanceCriterion, - ] - ), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [], - ] - ); - - $searchService = $repository->getSearchService(); - $result = $searchService->findContent($query); - - $this->assertEquals(1, $result->totalCount); - $this->assertEquals( - $wildBoars->id, - $result->searchHits[0]->valueObject->id - ); - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * @group maplocation - */ - public function testMapLocationDistanceWithCustomFieldSort() - { - $contentType = $this->createTestPlaceContentType(); - - // Create a draft to account for behaviour with ContentType in different states - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - $contentTypeService->createContentTypeDraft($contentType); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.894877, - 'longitude' => 15.972699, - 'address' => 'Here be wild boars', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.927334, - 'longitude' => 15.934847, - 'address' => 'A lone tree', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $tree = $contentService->publishVersion($draft->getVersionInfo()); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->setField( - 'maplocation', - [ - 'latitude' => 45.903777, - 'longitude' => 15.958788, - 'address' => 'Meadow with mushrooms', - ], - 'eng-GB' - ); - - $draft = $contentService->createContent($createStruct); - $mushrooms = $contentService->publishVersion($draft->getVersionInfo()); - - $this->refreshSearch($repository); - - $well = [ - 'latitude' => 43.756825, - 'longitude' => 15.775074, - ]; - - $sortClause = new SortClause\MapLocationDistance( - 'testtype', - 'maplocation', - $well['latitude'], - $well['longitude'], - Query::SORT_DESC - ); - $sortClause->setCustomField('testtype', 'maplocation', 'custom_geolocation_field'); - - $query = new Query( - [ - 'filter' => new Criterion\LogicalAnd( - [ - new Criterion\ContentTypeId($contentType->id), - new Criterion\MapLocationDistance( - 'maplocation', - Criterion\Operator::GTE, - 235, - $well['latitude'], - $well['longitude'] - ), - ] - ), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [ - $sortClause, - ], - ] - ); - - $searchService = $repository->getSearchService(); - $result = $searchService->findContent($query); - - $this->assertEquals(3, $result->totalCount); - $this->assertEquals( - $wildBoars->id, - $result->searchHits[2]->valueObject->id - ); - $this->assertEquals( - $mushrooms->id, - $result->searchHits[1]->valueObject->id - ); - $this->assertEquals( - $tree->id, - $result->searchHits[0]->valueObject->id - ); - } - - /** - * Test for the findLocations() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - */ - public function testFindMainLocation() - { - $plainSiteLocationId = 56; - $designLocationId = 58; - $partnersContentId = 59; - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - - // Add secondary Location for "Partners" user group, under "Design" page - $locationService->createLocation( - $contentService->loadContentInfo($partnersContentId), - $locationService->newLocationCreateStruct($designLocationId) - ); - - $this->refreshSearch($repository); - - $query = new LocationQuery( - [ - 'filter' => new Criterion\LogicalAnd( - [ - new Criterion\ParentLocationId($designLocationId), - new Criterion\Location\IsMainLocation( - Criterion\Location\IsMainLocation::MAIN - ), - ] - ), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [], - ] - ); - - $searchService = $repository->getSearchService(); - $result = $searchService->findLocations($query); - - $this->assertEquals(1, $result->totalCount); - $this->assertEquals($plainSiteLocationId, $result->searchHits[0]->valueObject->id); - } - - /** - * Test for the findLocations() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - */ - public function testFindNonMainLocation() - { - $designLocationId = 58; - $partnersContentId = 59; - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - - // Add secondary Location for "Partners" user group, under "Design" page - $newLocation = $locationService->createLocation( - $contentService->loadContentInfo($partnersContentId), - $locationService->newLocationCreateStruct($designLocationId) - ); - - $this->refreshSearch($repository); - - $query = new LocationQuery( - [ - 'filter' => new Criterion\LogicalAnd( - [ - new Criterion\ParentLocationId($designLocationId), - new Criterion\Location\IsMainLocation( - Criterion\Location\IsMainLocation::NOT_MAIN - ), - ] - ), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [], - ] - ); - - $searchService = $repository->getSearchService(); - $result = $searchService->findLocations($query); - - $this->assertEquals(1, $result->totalCount); - $this->assertEquals($newLocation->id, $result->searchHits[0]->valueObject->id); - } - - /** - * Test for the findLocations() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - */ - public function testSortMainLocationAscending() - { - $plainSiteLocationId = 56; - $designLocationId = 58; - $partnersContentId = 59; - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - - // Add secondary Location for "Partners" user group, under "Design" page - $newLocation = $locationService->createLocation( - $contentService->loadContentInfo($partnersContentId), - $locationService->newLocationCreateStruct($designLocationId) - ); - - $this->refreshSearch($repository); - - $query = new LocationQuery( - [ - 'filter' => new Criterion\ParentLocationId($designLocationId), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [ - new SortClause\Location\IsMainLocation( - LocationQuery::SORT_ASC - ), - ], - ] - ); - - $searchService = $repository->getSearchService(); - $result = $searchService->findLocations($query); - - $this->assertEquals(2, $result->totalCount); - $this->assertEquals($newLocation->id, $result->searchHits[0]->valueObject->id); - $this->assertEquals($plainSiteLocationId, $result->searchHits[1]->valueObject->id); - } - - /** - * Test for the findLocations() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - */ - public function testSortMainLocationDescending() - { - $plainSiteLocationId = 56; - $designLocationId = 58; - $partnersContentId = 59; - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - - // Add secondary Location for "Partners" user group, under "Design" page - $newLocation = $locationService->createLocation( - $contentService->loadContentInfo($partnersContentId), - $locationService->newLocationCreateStruct($designLocationId) - ); - - $this->refreshSearch($repository); - - $query = new LocationQuery( - [ - 'filter' => new Criterion\ParentLocationId($designLocationId), - 'offset' => 0, - 'limit' => 10, - 'sortClauses' => [ - new SortClause\Location\IsMainLocation( - LocationQuery::SORT_DESC - ), - ], - ] - ); - - $searchService = $repository->getSearchService(); - $result = $searchService->findLocations($query); - - $this->assertEquals(2, $result->totalCount); - $this->assertEquals($plainSiteLocationId, $result->searchHits[0]->valueObject->id); - $this->assertEquals($newLocation->id, $result->searchHits[1]->valueObject->id); - } - - /** - * Test for the findLocations() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - */ - public function testContentWithMultipleLocations() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - $locationService = $repository->getLocationService(); - - $forumType = $contentTypeService->loadContentTypeByIdentifier('forum'); - - $createStruct = $contentService->newContentCreateStruct($forumType, 'eng-GB'); - $createStruct->alwaysAvailable = false; - $createStruct->setField('name', 'An awesome duplicate forum'); - - $draft = $contentService->createContent($createStruct); - $content = $contentService->publishVersion($draft->getVersionInfo()); - - $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(2); - $location1 = $locationService->createLocation($content->contentInfo, $locationCreateStruct); - $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(5); - $location2 = $locationService->createLocation($content->contentInfo, $locationCreateStruct); - - $this->refreshSearch($repository); - - $query = new LocationQuery( - [ - 'filter' => new Criterion\ContentId($content->id), - ] - ); - - $searchService = $repository->getSearchService(); - $result = $searchService->findLocations($query); - - $this->assertEquals(2, $result->totalCount); - $locationIds = array_map( - static function (SearchHit $searchHit): int { - return $searchHit->valueObject->id; - }, - $result->searchHits - ); - $this->assertContains($location1->id, $locationIds); - $this->assertContains($location2->id, $locationIds); - } - - protected function createContentForTestUserMetadataGroupHorizontal() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - $locationService = $repository->getLocationService(); - $userService = $repository->getUserService(); - $permissionResolver = $repository->getPermissionResolver(); - - $administratorUser = $userService->loadUser($permissionResolver->getCurrentUserReference()->getUserId()); - // ID of the "Administrators" user group in an eZ Publish demo installation - $administratorsUserGroupId = 12; - // ID of the "Editors" user group in an eZ Publish demo installation - $editorsUserGroupId = 13; - - $administratorsUserGroup = $userService->loadUserGroup($administratorsUserGroupId); - $editorsUserGroup = $userService->loadUserGroup($editorsUserGroupId); - - // Add additional Location for Administrators UserGroup under Editors UserGroup Location - $locationCreateStruct = $locationService->newLocationCreateStruct( - $editorsUserGroup->contentInfo->mainLocationId - ); - $newAdministratorsUserGroupLocation = $locationService->createLocation( - $administratorsUserGroup->contentInfo, - $locationCreateStruct - ); - - // Add additional Location for administrator user under newly created UserGroup Location - $locationCreateStruct = $locationService->newLocationCreateStruct( - $newAdministratorsUserGroupLocation->id - ); - $locationService->createLocation( - $administratorUser->contentInfo, - $locationCreateStruct - ); - - // Create a Content to be found through Editors UserGroup id. - // This ensures data is indexed, it could also be done by updating metadata of - // an existing Content, but listener would need to reindex Content and that should - // be tested elsewhere (dedicated indexing integration tests, missing ATM). - $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->setField('name', 'test'); - - $locationCreateStruct = $locationService->newLocationCreateStruct(2); - $draft = $contentService->createContent($createStruct, [$locationCreateStruct]); - $content = $contentService->publishVersion($draft->getVersionInfo()); - $contentTypeService->createContentTypeDraft($contentType); - - $this->refreshSearch($repository); - - return $content; - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testUserMetadataGroupHorizontalFilterContent($queryType = null) - { - if ($queryType === null) { - $queryType = 'filter'; - } - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - $editorsUserGroupId = 13; - - $content = $this->createContentForTestUserMetadataGroupHorizontal(); - - $criteria = []; - $setupFactory = $this->getSetupFactory(); - - // Do not limit for LSE, as it does not not require reindexing. - // See explanation below. - if ($setupFactory instanceof LegacySolrSetupFactory) { - $criteria[] = new Criterion\ContentTypeIdentifier('folder'); - } - - $criteria[] = new Criterion\UserMetadata( - Criterion\UserMetadata::GROUP, - Criterion\Operator::EQ, - $editorsUserGroupId - ); - - $query = new Query( - [ - $queryType => new Criterion\LogicalAnd($criteria), - 'sortClauses' => [ - new SortClause\ContentId(), - ], - 'limit' => 50, - ] - ); - - if ($setupFactory instanceof LegacySolrSetupFactory) { - $result = $searchService->findContent($query); - - // Administrator User is owned by itself, when additional Locations are added - // it should be reindexed and its UserGroups will updated, which means it should - // also be found as a Content of Editors UserGroup. However we do not handle this - // in listeners yet, and also miss SPI methods to do it without using Search (also - // needed to decouple services), because as indexing is asynchronous Search - // should not eat its own dog food for reindexing. - $this->assertEquals(1, $result->totalCount); - - $this->assertEquals( - $content->id, - $result->searchHits[0]->valueObject->id - ); - } else { - // This is how it should eventually work for all search engines, - // with required reindexing listeners properly implemented. - - $result = $searchService->findContent($query); - - // Assert last hit manually, as id will change because it is created in test - // and not present it base fixture. - $foundContent1 = array_pop($result->searchHits); - $result->totalCount = $result->totalCount - 1; - $this->assertEquals($content->id, $foundContent1->valueObject->id); - - $this->simplifySearchResult($result); - $this->assertEqualsWithDelta( - include $this->getFixtureDir() . '/UserMetadata.php', - $result, - .1, // Be quite generous regarding delay -- most important for scores - 'Search results do not match.', - ); - } - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testUserMetadataGroupHorizontalQueryContent() - { - $this->testUserMetadataGroupHorizontalFilterContent('query'); - } - - /** - * Test for the findLocations() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - */ - public function testUserMetadataGroupHorizontalFilterLocation($queryType = null) - { - if ($queryType === null) { - $queryType = 'filter'; - } - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - $permissionResolver = $repository->getPermissionResolver(); - $editorsUserGroupId = 13; - - $content = $this->createContentForTestUserMetadataGroupHorizontal(); - - $criteria = []; - $setupFactory = $this->getSetupFactory(); - - // Do not limit for LSE, as it does not not require reindexing. - // See explanation below. - if ($setupFactory instanceof LegacySolrSetupFactory) { - $criteria[] = new Criterion\ContentTypeIdentifier('folder'); - } - - $criteria[] = new Criterion\UserMetadata( - Criterion\UserMetadata::GROUP, - Criterion\Operator::EQ, - $editorsUserGroupId - ); - - $query = new LocationQuery( - [ - $queryType => new Criterion\LogicalAnd($criteria), - 'sortClauses' => [ - new SortClause\Location\Id(), - ], - 'limit' => 50, - ] - ); - - if ($setupFactory instanceof LegacySolrSetupFactory) { - $result = $searchService->findLocations($query); - - // Administrator User is owned by itself, when additional Locations are added - // it should be reindexed and its UserGroups will updated, which means it should - // also be found as a Content of Editors UserGroup. However we do not handle this - // in listeners yet, and also miss SPI methods to do it without using Search (also - // needed to decouple services), because as indexing is asynchronous Search - // should not eat its own dog food for reindexing. - $this->assertEquals(1, $result->totalCount); - - $this->assertEquals( - $content->contentInfo->mainLocationId, - $result->searchHits[0]->valueObject->id - ); - } else { - // This is how it should eventually work for all search engines, - // with required reindexing listeners properly implemented. - - $result = $searchService->findLocations($query); - - // Assert last two hits manually, as ids will change because they are created - // in test and not present in base fixture. - $foundLocation1 = array_pop($result->searchHits); - $foundLocation2 = array_pop($result->searchHits); - // Remove additional Administrators UserGroup Location - array_pop($result->searchHits); - $result->totalCount = $result->totalCount - 2; - $this->assertEquals( - $content->versionInfo->contentInfo->mainLocationId, - $foundLocation1->valueObject->id - ); - $this->assertEquals( - $permissionResolver->getCurrentUserReference()->getUserId(), - $foundLocation2->valueObject->contentId - ); - - $this->simplifySearchResult($result); - $this->assertEqualsWithDelta( - include $this->getFixtureDir() . '/UserMetadataLocation.php', - $result, - .1, // Be quite generous regarding delay -- most important for scores - 'Search results do not match.', - ); - } - } - - /** - * Test for the findLocations() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - */ - public function testUserMetadataGroupHorizontalQueryLocation() - { - $this->testUserMetadataGroupHorizontalFilterLocation('query'); - } - - /** - * Test for FullText on the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testFullTextOnNewContent() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - $locationService = $repository->getLocationService(); - $searchService = $repository->getSearchService(); - - $contentCreateStruct = $contentService->newContentCreateStruct( - $contentTypeService->loadContentTypeByIdentifier('folder'), - 'eng-GB' - ); - - $contentCreateStruct->setField('name', 'foxes'); - - $englishContent = $contentService->publishVersion( - $contentService->createContent( - $contentCreateStruct, - [$locationService->newLocationCreateStruct(2)] - )->versionInfo - ); - - $this->refreshSearch($repository); - - $query = new Query( - [ - 'query' => new Criterion\FullText('foxes'), - ] - ); - - $searchResult = $searchService->findContentInfo($query); - - $this->assertEquals(1, $searchResult->totalCount); - $this->assertEquals($englishContent->id, $searchResult->searchHits[0]->valueObject->id); - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testLanguageAnalysisSeparateContent() - { - $this->markTestSkipped('Language analysis is currently not supported'); - - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - $locationService = $repository->getLocationService(); - $searchService = $repository->getSearchService(); - $languageService = $repository->getContentLanguageService(); - - $languageCreateStruct = $languageService->newLanguageCreateStruct(); - $languageCreateStruct->languageCode = 'rus-RU'; - $languageCreateStruct->name = 'Russian'; - - $languageService->createLanguage($languageCreateStruct); - - $contentCreateStruct = $contentService->newContentCreateStruct( - $contentTypeService->loadContentTypeByIdentifier('folder'), - 'eng-GB' - ); - - $contentCreateStruct->setField('name', 'foxes'); - - $englishContent = $contentService->publishVersion( - $contentService->createContent( - $contentCreateStruct, - [$locationService->newLocationCreateStruct(2)] - )->versionInfo - ); - - $contentCreateStruct = $contentService->newContentCreateStruct( - $contentTypeService->loadContentTypeByIdentifier('folder'), - 'rus-RU' - ); - - $contentCreateStruct->setField('name', 'foxes'); - - $russianContent = $contentService->publishVersion( - $contentService->createContent( - $contentCreateStruct, - [$locationService->newLocationCreateStruct(2)] - )->versionInfo - ); - - // Only Content in English should be found, because Content in Russian - // will not be correctly stemmed - $query = new Query( - [ - 'query' => new Criterion\FullText('foxing'), - ] - ); - - $searchResult = $searchService->findContent($query); - - $this->assertEquals(1, $searchResult->totalCount); - $this->assertEquals($englishContent->id, $searchResult->searchHits[0]->valueObject->id); - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testLanguageAnalysisSameContent() - { - $this->markTestSkipped('Language analysis is currently not supported'); - - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - $locationService = $repository->getLocationService(); - $searchService = $repository->getSearchService(); - $languageService = $repository->getContentLanguageService(); - - $languageCreateStruct = $languageService->newLanguageCreateStruct(); - $languageCreateStruct->languageCode = 'rus-RU'; - $languageCreateStruct->name = 'Russian'; - - $languageService->createLanguage($languageCreateStruct); - - $contentCreateStruct = $contentService->newContentCreateStruct( - $contentTypeService->loadContentTypeByIdentifier('folder'), - 'eng-GB' - ); - - $contentCreateStruct->setField('name', 'foxes важнейшими', 'eng-GB'); - $contentCreateStruct->setField('name', 'foxes важнейшими', 'rus-RU'); - - $mixedContent = $contentService->publishVersion( - $contentService->createContent( - $contentCreateStruct, - [$locationService->newLocationCreateStruct(2)] - )->versionInfo - ); - - // Content will be found because translation in Russian will be correctly stemmed - $query = new Query( - [ - 'query' => new Criterion\FullText('важнее'), - ] - ); - - $searchResult = $searchService->findContent($query); - - $this->assertEquals(1, $searchResult->totalCount); - $this->assertEquals($mixedContent->id, $searchResult->searchHits[0]->valueObject->id); - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testLanguageAnalysisSameContentNotFound() - { - $this->markTestSkipped('Language analysis is currently not supported'); - - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - $locationService = $repository->getLocationService(); - $searchService = $repository->getSearchService(); - $languageService = $repository->getContentLanguageService(); - - $languageCreateStruct = $languageService->newLanguageCreateStruct(); - $languageCreateStruct->languageCode = 'rus-RU'; - $languageCreateStruct->name = 'Russian'; - - $languageService->createLanguage($languageCreateStruct); - - $contentCreateStruct = $contentService->newContentCreateStruct( - $contentTypeService->loadContentTypeByIdentifier('folder'), - 'eng-GB' - ); - - $contentCreateStruct->setField('name', 'foxes важнейшими', 'eng-GB'); - $contentCreateStruct->setField('name', 'foxes важнейшими', 'rus-RU'); - - $mixedContent = $contentService->publishVersion( - $contentService->createContent( - $contentCreateStruct, - [$locationService->newLocationCreateStruct(2)] - )->versionInfo - ); - - // Content should be found because translation in Russian will be correctly stemmed - $query = new Query( - [ - 'query' => new Criterion\FullText('важнее'), - ] - ); - - // Filtering fields for only English will cause no match because the term will - // not be correctly stemmed - $searchResult = $searchService->findContent($query, ['languages' => ['eng-GB']]); - - $this->assertEquals(0, $searchResult->totalCount); - } - - /** - * Test for the findContent() method searching for content filtered by languages. - * - * @covers \eZ\Publish\Core\Repository\SearchService::findContent - */ - public function testFindContentWithLanguageFilter() - { - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $query = new Query( - [ - 'filter' => new Criterion\ContentId([4]), - 'offset' => 0, - ] - ); - $searchResult = $searchService->findContent( - $query, - ['languages' => ['eng-US']], - false - ); - /* END: Use Case */ - - $this->assertInstanceOf( - SearchResult::class, - $searchResult - ); - - $this->assertEquals(1, $searchResult->totalCount); - $this->assertCount($searchResult->totalCount, $searchResult->searchHits); - foreach ($searchResult->searchHits as $searchHit) { - $this->assertInstanceOf( - SearchHit::class, - $searchHit - ); - } - } - - /** - * This test prepares data for other tests. - * - * @see testFulltextContentSearchComplex - * @see testFulltextLocationSearchComplex - * - * @return array - */ - public function testFulltextComplex() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $contentTypeService = $repository->getContentTypeService(); - $locationService = $repository->getLocationService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); - $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - - $contentCreateStruct->setField('name', 'red'); - $contentCreateStruct->setField('short_name', 'red apple'); - $content1 = $contentService->publishVersion( - $contentService->createContent( - $contentCreateStruct, - [$locationService->newLocationCreateStruct(2)] - )->versionInfo - ); - - $contentCreateStruct->setField('name', 'apple'); - $contentCreateStruct->setField('short_name', 'two'); - $content2 = $contentService->publishVersion( - $contentService->createContent( - $contentCreateStruct, - [$locationService->newLocationCreateStruct(2)] - )->versionInfo - ); - - $contentCreateStruct->setField('name', 'red apple'); - $contentCreateStruct->setField('short_name', 'three'); - $content3 = $contentService->publishVersion( - $contentService->createContent( - $contentCreateStruct, - [$locationService->newLocationCreateStruct(2)] - )->versionInfo - ); - - $contentCreateStruct->setField('name', 'four'); - $contentCreateStruct->setField('name', 'german red apple', 'ger-DE'); - $contentCreateStruct->setField('short_name', 'four'); - $contentCreateStruct->setField('short_name', 'german red apple', 'ger-DE'); - $contentService->publishVersion( - $contentService->createContent( - $contentCreateStruct, - [$locationService->newLocationCreateStruct(2)] - )->versionInfo - ); - - $this->refreshSearch($repository); - - $criterion = new Criterion\FullText( - 'red apple', - [ - 'boost' => [ - 'short_name' => 2, - ], - 'fuzziness' => .1, - ] - ); - - return [$criterion, $content1, $content2, $content3]; - } - - /** - * Test for the findContent() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * @depends testFulltextComplex - * - * @param array $data - */ - public function testFulltextContentSearchComplex(array $data) - { - // Do not initialize from scratch - $repository = $this->getRepository(false); - $searchService = $repository->getSearchService(); - list($criterion, $content1, $content2, $content3) = $data; - - $searchResult = $searchService->findContent( - new Query(['query' => $criterion]), - ['languages' => ['eng-GB']] - ); - $searchHits = $searchResult->searchHits; - - $this->assertEquals(3, $searchResult->totalCount); - - // Legacy search engine does have scoring, sorting the results by ID in that case - $setupFactory = $this->getSetupFactory(); - if (get_class($setupFactory) === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - $this->sortSearchHitsById($searchHits); - - $this->assertEquals($content1->id, $searchHits[0]->valueObject->id); - $this->assertEquals($content2->id, $searchHits[1]->valueObject->id); - $this->assertEquals($content3->id, $searchHits[2]->valueObject->id); - - return; - } - - // Assert scores are descending - $this->assertGreaterThan($searchHits[1]->score, $searchHits[0]->score); - $this->assertGreaterThan($searchHits[2]->score, $searchHits[1]->score); - - // Assert order - $this->assertEquals($content1->id, $searchHits[0]->valueObject->id); - $this->assertEquals($content3->id, $searchHits[1]->valueObject->id); - $this->assertEquals($content2->id, $searchHits[2]->valueObject->id); - } - - /** - * Test for the findContent() method. - * - * @covers \eZ\Publish\API\Repository\SearchService::findContent() - * @depends testFulltextComplex - * - * @param array $data - */ - public function testFulltextContentTranslationSearch(array $data) - { - $criterion = $data[0]; - $query = new Query(['query' => $criterion]); - - $this->assertFulltextSearchForTranslations(self::FIND_CONTENT_METHOD, $query); - } - - /** - * Test for the findLocations() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - * @depends testFulltextComplex - * - * @param array $data - */ - public function testFulltextLocationSearchComplex(array $data) - { - $setupFactory = $this->getSetupFactory(); - if ($setupFactory instanceof LegacySolrSetupFactory && getenv('SOLR_VERSION') === '4.10.4') { - $this->markTestSkipped('Skipping location search score test on Solr 4.10, you need Solr 6 for this!'); - } - - // Do not initialize from scratch - $repository = $this->getRepository(false); - list($criterion, $content1, $content2, $content3) = $data; - $searchService = $repository->getSearchService(); - - $searchResult = $searchService->findLocations( - new LocationQuery(['query' => $criterion]), - ['languages' => ['eng-GB']] - ); - $searchHits = $searchResult->searchHits; - - $this->assertEquals(3, $searchResult->totalCount); - - // Legacy search engine does have scoring, sorting the results by ID in that case - $setupFactory = $this->getSetupFactory(); - if (get_class($setupFactory) === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { - $this->sortSearchHitsById($searchHits); - - $this->assertEquals($content1->id, $searchHits[0]->valueObject->contentId); - $this->assertEquals($content2->id, $searchHits[1]->valueObject->contentId); - $this->assertEquals($content3->id, $searchHits[2]->valueObject->contentId); - - return; - } - - // Assert scores are descending - $this->assertGreaterThan($searchHits[1]->score, $searchHits[0]->score); - $this->assertGreaterThan($searchHits[2]->score, $searchHits[1]->score); - - // Assert order - $this->assertEquals($content1->id, $searchHits[0]->valueObject->contentId); - $this->assertEquals($content3->id, $searchHits[1]->valueObject->contentId); - $this->assertEquals($content2->id, $searchHits[2]->valueObject->contentId); - } - - /** - * Test for the findLocations() method. - * - * @covers \eZ\Publish\API\Repository\SearchService::findLocations() - * @depends testFulltextComplex - * - * @param array $data - */ - public function testFulltextLocationTranslationSearch(array $data): void - { - $criterion = $data[0]; - $query = new LocationQuery(['query' => $criterion]); - - $this->assertFulltextSearchForTranslations(self::FIND_LOCATION_METHOD, $query); - } - - /** - * Assert that query result matches the given fixture. - * - * @throws \ReflectionException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - protected function assertQueryFixture( - Query $query, - string $fixtureFilePath, - ?callable $closure = null, - bool $ignoreScore = true, - bool $info = false, - bool $id = true - ): void { - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - try { - if ($query instanceof LocationQuery) { - $result = $searchService->findLocations($query); - } elseif ($query instanceof Query) { - if ($info) { - $result = $searchService->findContentInfo($query); - } else { - $result = $searchService->findContent($query); - } - } else { - self::fail('Expected instance of LocationQuery or Query, got: ' . gettype($query)); - } - $this->simplifySearchResult($result); - } catch (NotImplementedException $e) { - self::markTestSkipped( - 'This feature is not supported by the current search backend: ' . $e->getMessage() - ); - } - - if (!is_file($fixtureFilePath)) { - if (isset($_ENV['ez_tests_record'])) { - file_put_contents( - $record = $fixtureFilePath . '.recording', - "<?php\n\nreturn " . var_export($result, true) . ";\n\n" - ); - self::markTestIncomplete("No fixture available. Result recorded at $record. Result: \n" . $this->printResult($result)); - } else { - self::markTestIncomplete("No fixture available. Set \$_ENV['ez_tests_record'] to generate:\n " . $fixtureFilePath); - } - } - - $fixture = require $fixtureFilePath; - - if ($closure !== null) { - $closure($fixture); - $closure($result); - } - - if ($ignoreScore) { - foreach ([$fixture, $result] as $set) { - $property = new \ReflectionProperty(get_class($set), 'maxScore'); - $property->setAccessible(true); - $property->setValue($set, 0.0); - - foreach ($set->searchHits as $hit) { - $property = new \ReflectionProperty(get_class($hit), 'score'); - $property->setAccessible(true); - $property->setValue($hit, 0.0); - } - } - } - - foreach ([$fixture, $result] as $set) { - foreach ($set->searchHits as $hit) { - $property = new \ReflectionProperty(get_class($hit), 'index'); - $property->setAccessible(true); - $property->setValue($hit, null); - - $property = new \ReflectionProperty(get_class($hit), 'matchedTranslation'); - $property->setAccessible(true); - $property->setValue($hit, null); - - if (!$id) { - $hit->valueObject['id'] = null; - } - } - } - - self::assertEqualsWithDelta( - $fixture, - $result, - .99, // Be quite generous regarding delay -- most important for scores - 'Search results do not match the fixture: ' . $fixtureFilePath - ); - } - - /** - * Show a simplified view of the search result for manual introspection. - * - * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $result - * - * @return string - */ - protected function printResult(SearchResult $result) - { - $printed = ''; - foreach ($result->searchHits as $hit) { - $printed .= sprintf(" - %s (%s)\n", $hit->valueObject['title'], $hit->valueObject['id']); - } - - return $printed; - } - - /** - * Simplify search result. - * - * This leads to saner comparisons of results, since we do not get the full - * content objects every time. - * - * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $result - */ - protected function simplifySearchResult(SearchResult $result) - { - $result->time = 1; - - foreach ($result->searchHits as $hit) { - switch (true) { - case $hit->valueObject instanceof Content: - case $hit->valueObject instanceof Location: - $hit->valueObject = [ - 'id' => $hit->valueObject->contentInfo->id, - 'title' => $hit->valueObject->contentInfo->name, - ]; - break; - - case $hit->valueObject instanceof ContentInfo: - $hit->valueObject = [ - 'id' => $hit->valueObject->id, - 'title' => $hit->valueObject->name, - ]; - break; - - default: - throw new \RuntimeException('Unknown search result hit type: ' . get_class($hit->valueObject)); - } - } - } - - /** - * Get fixture directory. - * - * @return string - */ - protected function getFixtureDir() - { - return __DIR__ . '/_fixtures/' . getenv('fixtureDir') . '/'; - } - - /** - * For findContentInfo tests, to reuse fixtures for findContent tests. - * - * @param callable|null $closure - * - * @return callable - */ - private function getContentInfoFixtureClosure($closure = null) - { - /** @var $data \eZ\Publish\API\Repository\Values\Content\Search\SearchResult */ - return static function (&$data) use ($closure) { - foreach ($data->searchHits as $searchHit) { - if ($searchHit->valueObject instanceof Content) { - $searchHit->valueObject = $searchHit->valueObject->getVersionInfo()->getContentInfo(); - } - } - - if (isset($closure)) { - $closure($data); - } - }; - } - - /** - * Test searching using Field Criterion where the given Field Identifier exists in - * both searchable and non-searchable Fields. - * Number of returned results depends on used storage. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - */ - public function testFieldCriterionForContentsWithIdenticalFieldIdentifiers() - { - $this->createContentWithFieldType( - 'url', - 'title', - 'foo' - ); - $this->createContentWithFieldType( - 'string', - 'title', - 'foo' - ); - $query = new Query( - [ - 'query' => new Criterion\Field( - 'title', - Criterion\Operator::EQ, - 'foo' - ), - ] - ); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - $result = $searchService->findContent($query); - - $this->assertTrue(($result->totalCount === 1 || $result->totalCount === 2)); - } - - private function createContentWithFieldType( - string $fieldType, - string $fieldName, - string $fieldValue - ) { - $repository = $this->getRepository(); - $contentTypeService = $repository->getContentTypeService(); - $contentService = $repository->getContentService(); - - $createStruct = $contentTypeService->newContentTypeCreateStruct($fieldType . uniqid()); - $createStruct->mainLanguageCode = 'eng-GB'; - $createStruct->remoteId = $fieldType . '-123'; - $createStruct->names = ['eng-GB' => $fieldType]; - $createStruct->creatorId = 14; - $createStruct->creationDate = new \DateTime(); - - $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct($fieldName, 'ez' . $fieldType); - $fieldCreate->names = ['eng-GB' => $fieldName]; - $fieldCreate->fieldGroup = 'main'; - $fieldCreate->position = 1; - - $createStruct->addFieldDefinition($fieldCreate); - - $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); - $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); - - $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $createStruct->remoteId = $fieldType . '-456'; - $createStruct->alwaysAvailable = false; - $createStruct->setField( - $fieldName, - $fieldValue - ); - - $draft = $contentService->createContent($createStruct); - $content = $contentService->publishVersion($draft->getVersionInfo()); - - $this->refreshSearch($repository); - - return $content; - } - - /** - * Test for the findContent() method with random sort clause. - * - * There is a slight chance when this test could fail, if by some reason, - * we got to same _random_ results, or mt_rand() provides same seed for seed-supported DB implementation. - * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * - * @dataProvider getSeedsForRandomSortClause - */ - public function testRandomSortContent(?int $firstSeed, ?int $secondSeed) - { - if ($firstSeed || $secondSeed) { - $this->skipIfSeedNotImplemented(); - } - - $firstQuery = new Query([ - 'sortClauses' => [ - new SortClause\Random($firstSeed), - ], - ]); - - $secondQuery = new Query([ - 'sortClauses' => [ - new SortClause\Random($secondSeed), - ], - ]); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $method = 'assertNotEquals'; - - if ($firstSeed !== null && $secondSeed !== null && ($firstSeed === $secondSeed)) { - $method = 'assertEquals'; - } - - try { - $this->$method( - $searchService->findContent($firstQuery)->searchHits, - $searchService->findContent($secondQuery)->searchHits - ); - } catch (NotImplementedException $e) { - $this->markTestSkipped( - 'This feature is not supported by the current search backend: ' . $e->getMessage() - ); - } - } - - /** - * Test for the findLocations() method. - * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - * - * @dataProvider getSeedsForRandomSortClause - */ - public function testRandomSortLocation(?int $firstSeed, ?int $secondSeed) - { - if ($firstSeed || $secondSeed) { - $this->skipIfSeedNotImplemented(); - } - - $firstQuery = new LocationQuery([ - 'sortClauses' => [ - new SortClause\Random($firstSeed), - ], - ]); - - $secondQuery = new LocationQuery([ - 'sortClauses' => [ - new SortClause\Random($secondSeed), - ], - ]); - - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $method = 'assertNotEquals'; - - if ($firstSeed !== null && $secondSeed !== null && ($firstSeed === $secondSeed)) { - $method = 'assertEquals'; - } - - try { - $this->$method( - $searchService->findLocations($firstQuery)->searchHits, - $searchService->findLocations($secondQuery)->searchHits - ); - } catch (NotImplementedException $e) { - $this->markTestSkipped( - 'This feature is not supported by the current search backend: ' . $e->getMessage() - ); - } - } - - public function getSeedsForRandomSortClause() - { - $randomSeed = mt_rand(); - - return [ - [ - null, - null, - ], - [ - 123456, - 123456, - ], - [ - $randomSeed, - 2 * $randomSeed, - ], - ]; - } - - private function skipIfSeedNotImplemented() - { - /** @var \eZ\Publish\API\Repository\Tests\SetupFactory\Legacy $setupFactory */ - $setupFactory = $this->getSetupFactory(); - - $db = $setupFactory->getDB(); - - if (in_array($db, ['sqlite', 'pgsql'])) { - $this->markTestSkipped( - 'Seed function is not implemented in ' . $db . '.' - ); - } - } - - /** - * @param string $findMethod - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - * @param array $languages - * @param bool $useAlwaysAvailable - * - * @throws \InvalidArgumentException - * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult - */ - private function find( - string $findMethod, - Query $query, - array $languages, - bool $useAlwaysAvailable - ): SearchResult { - if (false === in_array($findMethod, self::AVAILABLE_FIND_METHODS, true)) { - throw new InvalidArgumentException( - 'Allowed find methods are: ' - . implode(',', self::AVAILABLE_FIND_METHODS) - ); - } - - $repository = $this->getRepository(false); - $searchService = $repository->getSearchService(); - - return $searchService->{$findMethod}( - $query, - [ - 'languages' => $languages, - 'useAlwaysAvailable' => $useAlwaysAvailable, - ] - ); - } - - /** - * @param string $findMethod - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - */ - private function assertFulltextSearchForTranslations(string $findMethod, Query $query): void - { - /* - * Search in German translations without always available - */ - $searchResult = $this->find($findMethod, $query, ['ger-DE'], false); - $this->assertEquals(1, $searchResult->totalCount); - $this->assertSearchResultMatchTranslations($searchResult, ['ger-DE']); - - /* - * Search in German translations with always available - */ - $searchResult = $this->find($findMethod, $query, ['ger-DE'], true); - $this->assertEquals(4, $searchResult->totalCount); - $this->assertSearchResultMatchTranslations($searchResult, ['eng-GB', 'eng-GB', 'eng-GB', 'ger-DE']); - - /* - * Search in multiple (ger-DE, eng-GB) translations without always available - */ - $searchResult = $this->find($findMethod, $query, ['ger-DE', 'eng-GB'], false); - $this->assertEquals(4, $searchResult->totalCount); - $this->assertSearchResultMatchTranslations($searchResult, ['eng-GB', 'eng-GB', 'eng-GB', 'ger-DE']); - - /* - * Search in multiple (eng-US, ger-DE) translations without always available - */ - $searchResult = $this->find($findMethod, $query, ['eng-US', 'ger-DE'], false); - $this->assertEquals(1, $searchResult->totalCount); - $this->assertSearchResultMatchTranslations($searchResult, ['ger-DE']); - - /* - * Search in eng-US translations without always available - */ - $searchResult = $this->find($findMethod, $query, ['eng-US'], false); - $this->assertEquals(0, $searchResult->totalCount); - - /* - * Search in eng-US translations with always available - */ - $searchResult = $this->find($findMethod, $query, ['eng-US'], true); - $this->assertEquals(3, $searchResult->totalCount); - $this->assertSearchResultMatchTranslations($searchResult, ['eng-GB', 'eng-GB', 'eng-GB']); - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $searchResult - * @param string[] $translationsToMatch - * - * @throws \InvalidArgumentException - */ - private function assertSearchResultMatchTranslations( - SearchResult $searchResult, - array $translationsToMatch - ): void { - $translationsToMatchCount = count($translationsToMatch); - - if ($searchResult->totalCount < $translationsToMatchCount - || $searchResult->totalCount > $translationsToMatchCount - ) { - throw new InvalidArgumentException( - 'Argument `translationsToMatch` must be equal to the search result total count!' - ); - } - - $searchHits = $searchResult->searchHits; - $this->sortSearchHitsById($searchHits); - - for ($i = 0; $i < $searchResult->totalCount; ++$i) { - $this->assertEquals( - $translationsToMatch[$i], - $searchHits[$i]->matchedTranslation - ); - } - } - - private function sortSearchHitsById(array &$searchHits): void - { - usort( - $searchHits, - static function (SearchHit $a, SearchHit $b): int { - return $a->valueObject->id <=> $b->valueObject->id; - } - ); - } - - /** - * @dataProvider providerForTestSortingByNumericFieldsWithValuesOfDifferentLength - * - * @param int[] $expectedOrderedIds - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function testSortingByNumericFieldsWithValuesOfDifferentLength( - LocationQuery $query, - array $expectedOrderedIds - ): void { - $repository = $this->getRepository(); - $searchService = $repository->getSearchService(); - - $result = $searchService->findLocations($query); - - self::assertEquals(count($expectedOrderedIds), $result->totalCount); - $actualIds = array_map( - static function (SearchHit $searchHit) { - /** @var \eZ\Publish\API\Repository\Values\Content\Location $location */ - $location = $searchHit->valueObject; - - return $location->id; - }, - $result->searchHits - ); - self::assertEquals($expectedOrderedIds, $actualIds); - } - - public function providerForTestSortingByNumericFieldsWithValuesOfDifferentLength(): iterable - { - yield 'Location ID ASC' => [ - new LocationQuery( - [ - 'filter' => new Criterion\LocationId([43, 5]), - 'sortClauses' => [ - new SortClause\Location\Id(LocationQuery::SORT_ASC), - ], - ] - ), - [5, 43], - ]; - - yield 'Location ID DESC' => [ - new LocationQuery( - [ - 'filter' => new Criterion\LocationId([5, 43]), - 'sortClauses' => [ - new SortClause\Location\Id(LocationQuery::SORT_DESC), - ], - ] - ), - [43, 5], - ]; - - yield 'Content ID ASC' => [ - new LocationQuery( - [ - 'filter' => new Criterion\ContentId([14, 4]), - 'sortClauses' => [ - new SortClause\ContentId(LocationQuery::SORT_ASC), - ], - ] - ), - // those are still Location IDs as it's LocationQuery - [5, 15], - ]; - - yield 'Content ID DESC' => [ - new LocationQuery( - [ - 'filter' => new Criterion\ContentId([4, 14]), - 'sortClauses' => [ - new SortClause\ContentId(LocationQuery::SORT_DESC), - ], - ] - ), - // those are still Location IDs as it's LocationQuery - [15, 5], - ]; - } -} diff --git a/eZ/Publish/API/Repository/Tests/SectionServiceTest.php b/eZ/Publish/API/Repository/Tests/SectionServiceTest.php deleted file mode 100644 index 7c50256230..0000000000 --- a/eZ/Publish/API/Repository/Tests/SectionServiceTest.php +++ /dev/null @@ -1,1066 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Section; -use eZ\Publish\API\Repository\Values\Content\SectionCreateStruct; -use eZ\Publish\API\Repository\Values\Content\SectionUpdateStruct; -use eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation; - -/** - * Test case for operations in the SectionService using in memory storage. - * - * @see eZ\Publish\API\Repository\SectionService - * @group integration - * @group section - */ -class SectionServiceTest extends BaseTest -{ - private const SECTION_UNIQUE_KEY = 'uniqueKey'; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - protected $permissionResolver; - - /** - * Tests that the required <b>ContentService::loadContentInfoByRemoteId()</b> - * at least returns an object, because this method is utilized in several - * tests,. - */ - protected function setUp(): void - { - parent::setUp(); - - try { - // RemoteId of the "Media" page of an eZ Publish demo installation - $mediaRemoteId = 'a6e35cbcb7cd6ae4b691f3eee30cd262'; - - // Load the ContentService - $contentService = $this->getRepository()->getContentService(); - - // Load a content info instance - $contentInfo = $contentService->loadContentInfoByRemoteId( - $mediaRemoteId - ); - - if (false === is_object($contentInfo)) { - $this->markTestSkipped( - 'This test cannot be executed, because the utilized ' . - 'ContentService::loadContentInfoByRemoteId() does not ' . - 'return an object.' - ); - } - } catch (Exception $e) { - $this->markTestSkipped( - 'This test cannot be executed, because the utilized ' . - 'ContentService::loadContentInfoByRemoteId() failed with ' . - PHP_EOL . PHP_EOL . - $e - ); - } - - $repository = $this->getRepository(false); - $this->permissionResolver = $repository->getPermissionResolver(); - } - - /** - * Test for the newSectionCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\SectionService::newSectionCreateStruct() - */ - public function testNewSectionCreateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - $sectionCreate = $sectionService->newSectionCreateStruct(); - /* END: Use Case */ - - $this->assertInstanceOf(SectionCreateStruct::class, $sectionCreate); - } - - /** - * Test for the createSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::createSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testNewSectionCreateStruct - */ - public function testCreateSection() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - $sectionCreate = $sectionService->newSectionCreateStruct(); - $sectionCreate->name = 'Test Section'; - $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; - - $section = $sectionService->createSection($sectionCreate); - /* END: Use Case */ - - $this->assertInstanceOf(Section::class, $section); - } - - /** - * Test for the createSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::createSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testNewSectionCreateStruct - */ - public function testCreateSectionForUserWithSectionLimitation() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - $sectionCreate = $sectionService->newSectionCreateStruct(); - $sectionCreate->name = 'Test Section'; - $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; - - $this->createRoleWithPolicies('sectionCreator', [ - ['module' => 'section', 'function' => 'edit'], - ]); - - $user = $this->createCustomUserWithLogin( - 'user', - 'user@example.com', - 'sectionCreators', - 'sectionCreator', - new SectionLimitation(['limitationValues' => [1]]) - ); - - $repository->getPermissionResolver()->setCurrentUserReference($user); - - $section = $sectionService->createSection($sectionCreate); - /* END: Use Case */ - - $this->assertInstanceOf(Section::class, $section); - $this->assertSame(self::SECTION_UNIQUE_KEY, $section->identifier); - } - - /** - * Test for the createSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::createSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testCreateSection - */ - public function testCreateSectionThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - $sectionCreateOne = $sectionService->newSectionCreateStruct(); - $sectionCreateOne->name = 'Test section one'; - $sectionCreateOne->identifier = self::SECTION_UNIQUE_KEY; - - $sectionService->createSection($sectionCreateOne); - - $sectionCreateTwo = $sectionService->newSectionCreateStruct(); - $sectionCreateTwo->name = 'Test section two'; - $sectionCreateTwo->identifier = self::SECTION_UNIQUE_KEY; - - // This will fail, because identifier uniqueKey already exists. - $sectionService->createSection($sectionCreateTwo); - /* END: Use Case */ - } - - /** - * Test for the loadSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::loadSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testCreateSection - */ - public function testLoadSection() - { - $repository = $this->getRepository(); - - $sectionId = $this->generateId('section', 2); - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - // Loads user section - // $sectionId contains the corresponding ID - $section = $sectionService->loadSection($sectionId); - /* END: Use Case */ - - $this->assertEquals('users', $section->identifier); - } - - /** - * Test for the loadSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::loadSection() - */ - public function testLoadSectionThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - $nonExistentSectionId = $this->generateId('section', self::DB_INT_MAX); - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - // This call should fail with a NotFoundException - // $nonExistentSectionId contains a section ID that is not known - $sectionService->loadSection($nonExistentSectionId); - /* END: Use Case */ - } - - /** - * Test for the newSectionUpdateStruct() method. - * - * @see \eZ\Publish\API\Repository\SectionService::newSectionUpdateStruct() - */ - public function testNewSectionUpdateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - $sectionUpdate = $sectionService->newSectionUpdateStruct(); - /* END: Use Case */ - - $this->assertInstanceOf(SectionUpdateStruct::class, $sectionUpdate); - } - - /** - * Test for the updateSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::updateSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testCreateSection - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testLoadSection - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testNewSectionUpdateStruct - */ - public function testUpdateSection() - { - $repository = $this->getRepository(); - - $standardSectionId = $this->generateId('section', 1); - /* BEGIN: Use Case */ - // $standardSectionId contains the ID of the "Standard" section in a eZ - // Publish demo installation. - - $sectionService = $repository->getSectionService(); - - $section = $sectionService->loadSection($standardSectionId); - - $sectionUpdate = $sectionService->newSectionUpdateStruct(); - $sectionUpdate->name = 'New section name'; - $sectionUpdate->identifier = 'newUniqueKey'; - - $updatedSection = $sectionService->updateSection($section, $sectionUpdate); - /* END: Use Case */ - - // Verify that service returns an instance of Section - $this->assertInstanceOf(Section::class, $updatedSection); - - // Verify that the service also persists the changes - $updatedSection = $sectionService->loadSection($standardSectionId); - - $this->assertEquals('New section name', $updatedSection->name); - } - - /** - * Test for the updateSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::updateSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testCreateSection - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testLoadSection - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testNewSectionUpdateStruct - */ - public function testUpdateSectionForUserWithSectionLimitation() - { - $repository = $this->getRepository(); - $administratorUserId = $this->generateId('user', 14); - /* BEGIN: Use Case */ - // $standardSectionId contains the ID of the "Standard" section in a eZ - // Publish demo installation. - - $sectionService = $repository->getSectionService(); - $userService = $repository->getUserService(); - - $sectionCreate = $sectionService->newSectionCreateStruct(); - $sectionCreate->name = 'Test Section'; - $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; - $section = $sectionService->createSection($sectionCreate); - - $sectionUpdate = $sectionService->newSectionUpdateStruct(); - $sectionUpdate->name = 'New section name'; - $sectionUpdate->identifier = 'newUniqueKey'; - - $this->createRoleWithPolicies('sectionCreator', [ - ['module' => 'section', 'function' => 'edit'], - ]); - $user = $this->createCustomUserWithLogin( - 'user', - 'user@example.com', - 'sectionCreators', - 'sectionCreator', - new SectionLimitation(['limitationValues' => [$section->id]]) - ); - $this->permissionResolver->setCurrentUserReference($user); - - $updatedSection = $sectionService->updateSection($section, $sectionUpdate); - /* END: Use Case */ - - // Verify that service returns an instance of Section - $this->assertInstanceOf(Section::class, $updatedSection); - - // Load section as an administrator - $administratorUser = $userService->loadUser($administratorUserId); - $this->permissionResolver->setCurrentUserReference($administratorUser); - - // Verify that the service also persists the changes - $updatedSection = $sectionService->loadSection($section->id); - - $this->assertEquals('New section name', $updatedSection->name); - $this->assertEquals('newUniqueKey', $updatedSection->identifier); - } - - /** - * Test for the updateSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::updateSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testUpdateSection - */ - public function testUpdateSectionKeepsSectionIdentifierOnNameUpdate() - { - $repository = $this->getRepository(); - - $standardSectionId = $this->generateId('section', 1); - /* BEGIN: Use Case */ - // $standardSectionId contains the ID of the "Standard" section in a eZ - // Publish demo installation. - - $sectionService = $repository->getSectionService(); - - $section = $sectionService->loadSection($standardSectionId); - $sectionUpdate = $sectionService->newSectionUpdateStruct(); - $sectionUpdate->name = 'New section name'; - - $updatedSection = $sectionService->updateSection($section, $sectionUpdate); - /* END: Use Case */ - - $this->assertEquals('standard', $updatedSection->identifier); - } - - /** - * Test for the updateSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::updateSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testUpdateSection - */ - public function testUpdateSectionWithSectionIdentifierOnNameUpdate() - { - $repository = $this->getRepository(); - - $standardSectionId = $this->generateId('section', 1); - /* BEGIN: Use Case */ - // $standardSectionId contains the ID of the "Standard" section in a eZ - // Publish demo installation. - - $sectionService = $repository->getSectionService(); - - $section = $sectionService->loadSection($standardSectionId); - $sectionUpdate = $sectionService->newSectionUpdateStruct(); - $sectionUpdate->name = 'New section name'; - - // section identifier remains the same - $sectionUpdate->identifier = $section->identifier; - - $updatedSection = $sectionService->updateSection($section, $sectionUpdate); - /* END: Use Case */ - - $this->assertEquals('standard', $updatedSection->identifier); - } - - /** - * Test for the updateSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::updateSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testUpdateSection - */ - public function testUpdateSectionKeepsSectionNameOnIdentifierUpdate() - { - $repository = $this->getRepository(); - - $standardSectionId = $this->generateId('section', 1); - /* BEGIN: Use Case */ - // $standardSectionId contains the ID of the "Standard" section in a eZ - // Publish demo installation. - - $sectionService = $repository->getSectionService(); - - $section = $sectionService->loadSection($standardSectionId); - - $sectionUpdate = $sectionService->newSectionUpdateStruct(); - $sectionUpdate->identifier = 'newUniqueKey'; - - $updatedSection = $sectionService->updateSection($section, $sectionUpdate); - /* END: Use Case */ - - $this->assertEquals('Standard', $updatedSection->name); - } - - /** - * Test for the updateSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::updateSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testUpdateSection - */ - public function testUpdateSectionThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $standardSectionId = $this->generateId('section', 1); - /* BEGIN: Use Case */ - // $standardSectionId contains the ID of the "Standard" section in a eZ - // Publish demo installation. - - $sectionService = $repository->getSectionService(); - - // Create section with conflict identifier - $sectionCreate = $sectionService->newSectionCreateStruct(); - $sectionCreate->name = 'Conflict section'; - $sectionCreate->identifier = 'conflictKey'; - - $sectionService->createSection($sectionCreate); - - // Load an existing section and update to an existing identifier - $section = $sectionService->loadSection($standardSectionId); - - $sectionUpdate = $sectionService->newSectionUpdateStruct(); - $sectionUpdate->identifier = 'conflictKey'; - - // This call should fail with an InvalidArgumentException - $sectionService->updateSection($section, $sectionUpdate); - /* END: Use Case */ - } - - /** - * Test for the loadSections() method. - * - * @see \eZ\Publish\API\Repository\SectionService::loadSections() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testCreateSection - */ - public function testLoadSections() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - $sections = $sectionService->loadSections(); - foreach ($sections as $section) { - // Operate on all sections. - } - /* END: Use Case */ - - $this->assertCount(6, $sections); - } - - /** - * Test for the loadSections() method. - * - * @see \eZ\Publish\API\Repository\SectionService::loadSections() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testCreateSection - */ - public function testLoadSectionsReturnsDefaultSectionsByDefault() - { - $repository = $this->getRepository(); - - $sectionService = $repository->getSectionService(); - - $this->assertEquals( - [ - new Section( - [ - 'id' => $this->generateId('section', 1), - 'name' => 'Standard', - 'identifier' => 'standard', - ] - ), - new Section( - [ - 'id' => $this->generateId('section', 2), - 'name' => 'Users', - 'identifier' => 'users', - ] - ), - new Section( - [ - 'id' => $this->generateId('section', 3), - 'name' => 'Media', - 'identifier' => 'media', - ] - ), - new Section( - [ - 'id' => $this->generateId('section', 4), - 'name' => 'Setup', - 'identifier' => 'setup', - ] - ), - new Section( - [ - 'id' => $this->generateId('section', 5), - 'name' => 'Design', - 'identifier' => 'design', - ] - ), - new Section( - [ - 'id' => $this->generateId('section', 6), - 'name' => 'Restricted', - 'identifier' => '', - ] - ), - ], - $sectionService->loadSections() - ); - } - - /** - * Test for the loadSectionByIdentifier() method. - * - * @see \eZ\Publish\API\Repository\SectionService::loadSectionByIdentifier() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testCreateSection - */ - public function testLoadSectionByIdentifier() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - $sectionCreate = $sectionService->newSectionCreateStruct(); - $sectionCreate->name = 'Test Section'; - $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; - - $sectionId = $sectionService->createSection($sectionCreate)->id; - - $section = $sectionService->loadSectionByIdentifier(self::SECTION_UNIQUE_KEY); - /* END: Use Case */ - - $this->assertEquals($sectionId, $section->id); - } - - /** - * Test for the loadSectionByIdentifier() method. - * - * @see \eZ\Publish\API\Repository\SectionService::loadSectionByIdentifier() - */ - public function testLoadSectionByIdentifierThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - // This call should fail with a NotFoundException - $sectionService->loadSectionByIdentifier('someUnknownSectionIdentifier'); - /* END: Use Case */ - } - - /** - * Test for the countAssignedContents() method. - * - * @see \eZ\Publish\API\Repository\SectionService::countAssignedContents() - */ - public function testCountAssignedContents() - { - $repository = $this->getRepository(); - - $sectionService = $repository->getSectionService(); - - $standardSectionId = $this->generateId('section', 1); - /* BEGIN: Use Case */ - // $standardSectionId contains the ID of the "Standard" section in a eZ - // Publish demo installation. - - $standardSection = $sectionService->loadSection($standardSectionId); - - $numberOfAssignedContent = $sectionService->countAssignedContents( - $standardSection - ); - /* END: Use Case */ - - $this->assertEquals( - 2, // Taken from the fixture - $numberOfAssignedContent - ); - } - - /** - * Test for the isSectionUsed() method. - * - * @see \eZ\Publish\API\Repository\SectionService::isSectionUsed() - */ - public function testIsSectionUsed() - { - $repository = $this->getRepository(); - - $sectionService = $repository->getSectionService(); - - $standardSectionId = $this->generateId('section', 1); - /* BEGIN: Use Case */ - // $standardSectionId contains the ID of the "Standard" section in a eZ - // Publish demo installation. - - $standardSection = $sectionService->loadSection($standardSectionId); - - $isSectionUsed = $sectionService->isSectionUsed( - $standardSection - ); - /* END: Use Case */ - - $this->assertTrue( - // Taken from the fixture - $isSectionUsed - ); - } - - /** - * Test for the assignSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::assignSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testCountAssignedContents - */ - public function testAssignSection() - { - $repository = $this->getRepository(); - $sectionService = $repository->getSectionService(); - - $standardSectionId = $this->generateId('section', 1); - $mediaSectionId = $this->generateId('section', 3); - - $beforeStandardCount = $sectionService->countAssignedContents( - $sectionService->loadSection($standardSectionId) - ); - $beforeMediaCount = $sectionService->countAssignedContents( - $sectionService->loadSection($mediaSectionId) - ); - - /* BEGIN: Use Case */ - // $mediaSectionId contains the ID of the "Media" section in a eZ - // Publish demo installation. - - // RemoteId of the "Media" page of an eZ Publish demo installation - $mediaRemoteId = 'a6e35cbcb7cd6ae4b691f3eee30cd262'; - - $contentService = $repository->getContentService(); - $sectionService = $repository->getSectionService(); - - // Load a content info instance - $contentInfo = $contentService->loadContentInfoByRemoteId( - $mediaRemoteId - ); - - // Load the "Standard" section - $section = $sectionService->loadSection($standardSectionId); - - // Assign Section to ContentInfo - $sectionService->assignSection($contentInfo, $section); - /* END: Use Case */ - - $this->assertEquals( - $beforeStandardCount + 1, - $sectionService->countAssignedContents( - $sectionService->loadSection($standardSectionId) - ) - ); - $this->assertEquals( - $beforeMediaCount - 1, - $sectionService->countAssignedContents( - $sectionService->loadSection($mediaSectionId) - ) - ); - } - - /** - * Test for the assignSectionToSubtree() method. - * - * @see \eZ\Publish\API\Repository\SectionService::assignSectionToSubtree() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testCreateSection - */ - public function testAssignSectionToSubtree() - { - $repository = $this->getRepository(); - $sectionService = $repository->getSectionService(); - - $standardSectionId = $this->generateId('section', 1); - $mediaSectionId = $this->generateId('section', 3); - - $beforeStandardCount = $sectionService->countAssignedContents( - $sectionService->loadSection($standardSectionId) - ); - - $beforeMediaCount = $sectionService->countAssignedContents( - $sectionService->loadSection($mediaSectionId) - ); - - // RemoteId of the "Media" page of an eZ Publish demo installation - $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; - - /* BEGIN: Use Case */ - $locationService = $repository->getLocationService(); - - // Load a location instance - $location = $locationService->loadLocationByRemoteId($mediaRemoteId); - - // Load the "Standard" section - $section = $sectionService->loadSection($standardSectionId); - - // Assign Section to ContentInfo - $sectionService->assignSectionToSubtree($location, $section); - - /* END: Use Case */ - $this->assertEquals( - $beforeStandardCount + 4, - $sectionService->countAssignedContents( - $sectionService->loadSection($standardSectionId) - ) - ); - $this->assertEquals( - $beforeMediaCount - 4, - $sectionService->countAssignedContents( - $sectionService->loadSection($mediaSectionId) - ) - ); - } - - /** - * Test for the countAssignedContents() method. - * - * @see \eZ\Publish\API\Repository\SectionService::countAssignedContents() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testCreateSection - */ - public function testCountAssignedContentsReturnsZeroByDefault() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - $sectionCreate = $sectionService->newSectionCreateStruct(); - $sectionCreate->name = 'Test Section'; - $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; - - $section = $sectionService->createSection($sectionCreate); - - // The number of assigned contents should be zero - $assignedContents = $sectionService->countAssignedContents($section); - /* END: Use Case */ - - $this->assertSame(0, $assignedContents); - } - - /** - * Test for the isSectionUsed() method. - * - * @see \eZ\Publish\API\Repository\SectionService::isSectionUsed() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testCreateSection - */ - public function testIsSectionUsedReturnsZeroByDefault() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - $sectionCreate = $sectionService->newSectionCreateStruct(); - $sectionCreate->name = 'Test Section'; - $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; - - $section = $sectionService->createSection($sectionCreate); - - // The number of assigned contents should be zero - $isSectionUsed = $sectionService->isSectionUsed($section); - /* END: Use Case */ - - $this->assertFalse($isSectionUsed); - } - - /** - * Test for the deleteSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::deleteSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testLoadSections - */ - public function testDeleteSection() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - $sectionCreate = $sectionService->newSectionCreateStruct(); - $sectionCreate->name = 'Test Section'; - $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; - - $section = $sectionService->createSection($sectionCreate); - - // Delete the newly created section - $sectionService->deleteSection($section); - /* END: Use Case */ - - $this->assertCount(6, $sectionService->loadSections()); - } - - /** - * Test for the deleteSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::deleteSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testDeleteSection - */ - public function testDeleteSectionThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - $sectionCreate = $sectionService->newSectionCreateStruct(); - $sectionCreate->name = 'Test Section'; - $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; - - $section = $sectionService->createSection($sectionCreate); - - // Delete the newly created section - $sectionService->deleteSection($section); - - // This call should fail with a NotFoundException - $sectionService->deleteSection($section); - /* END: Use Case */ - } - - /** - * Test for the deleteSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::deleteSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testAssignSection - */ - public function testDeleteSectionThrowsBadStateException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class); - - $repository = $this->getRepository(); - - $standardSectionId = $this->generateId('section', 1); - /* BEGIN: Use Case */ - // $standardSectionId contains the ID of the "Standard" section in a eZ - // Publish demo installation. - - // RemoteId of the "Media" page of an eZ Publish demo installation - $mediaRemoteId = 'a6e35cbcb7cd6ae4b691f3eee30cd262'; - - $contentService = $repository->getContentService(); - $sectionService = $repository->getSectionService(); - - // Load the "Media" ContentInfo - $contentInfo = $contentService->loadContentInfoByRemoteId($mediaRemoteId); - - // Load the "Standard" section - $section = $sectionService->loadSection($standardSectionId); - - // Assign "Media" to "Standard" section - $sectionService->assignSection($contentInfo, $section); - - // This call should fail with a BadStateException, because there are assigned contents - $sectionService->deleteSection($section); - /* END: Use Case */ - } - - /** - * Test for the createSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::createSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testCreateSection - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testLoadSectionByIdentifier - */ - public function testCreateSectionInTransactionWithRollback() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Get a create struct and set some properties - $sectionCreate = $sectionService->newSectionCreateStruct(); - $sectionCreate->name = 'Test Section'; - $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; - - // Create a new section - $sectionService->createSection($sectionCreate); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes - $repository->rollback(); - - try { - // This call will fail with a not found exception - $sectionService->loadSectionByIdentifier(self::SECTION_UNIQUE_KEY); - } catch (NotFoundException $e) { - // Expected execution path - } - /* END: Use Case */ - - $this->assertTrue(isset($e), 'Can still load section after rollback.'); - } - - /** - * Test for the createSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::createSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testCreateSection - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testLoadSectionByIdentifier - */ - public function testCreateSectionInTransactionWithCommit() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Get a create struct and set some properties - $sectionCreate = $sectionService->newSectionCreateStruct(); - $sectionCreate->name = 'Test Section'; - $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; - - // Create a new section - $sectionService->createSection($sectionCreate); - - // Commit all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Load new section - $section = $sectionService->loadSectionByIdentifier(self::SECTION_UNIQUE_KEY); - /* END: Use Case */ - - $this->assertEquals(self::SECTION_UNIQUE_KEY, $section->identifier); - } - - /** - * Test for the createSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::createSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testUpdateSection - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testLoadSectionByIdentifier - */ - public function testUpdateSectionInTransactionWithRollback() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Load standard section - $section = $sectionService->loadSectionByIdentifier('standard'); - - // Get an update struct and change section name - $sectionUpdate = $sectionService->newSectionUpdateStruct(); - $sectionUpdate->name = 'My Standard'; - - // Update section - $sectionService->updateSection($section, $sectionUpdate); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Rollback all changes - $repository->rollback(); - - // Load updated section, name will still be "Standard" - $updatedStandard = $sectionService->loadSectionByIdentifier('standard'); - /* END: Use Case */ - - $this->assertEquals('Standard', $updatedStandard->name); - } - - /** - * Test for the createSection() method. - * - * @see \eZ\Publish\API\Repository\SectionService::createSection() - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testUpdateSection - * @depends eZ\Publish\API\Repository\Tests\SectionServiceTest::testLoadSectionByIdentifier - */ - public function testUpdateSectionInTransactionWithCommit() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $sectionService = $repository->getSectionService(); - - // Start a new transaction - $repository->beginTransaction(); - - try { - // Load standard section - $section = $sectionService->loadSectionByIdentifier('standard'); - - // Get an update struct and change section name - $sectionUpdate = $sectionService->newSectionUpdateStruct(); - $sectionUpdate->name = 'My Standard'; - - // Update section - $sectionService->updateSection($section, $sectionUpdate); - - // Commit all changes - $repository->commit(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - // Load updated section, name will now be "My Standard" - $updatedStandard = $sectionService->loadSectionByIdentifier('standard'); - /* END: Use Case */ - - $this->assertEquals('My Standard', $updatedStandard->name); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SettingServiceTest.php b/eZ/Publish/API/Repository/Tests/SettingServiceTest.php deleted file mode 100644 index 441e77efd4..0000000000 --- a/eZ/Publish/API/Repository/Tests/SettingServiceTest.php +++ /dev/null @@ -1,228 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests; - -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\SettingService; -use eZ\Publish\API\Repository\Values\Setting\Setting; - -/** - * Test case for operations in the SettingService using in memory storage. - * - * @covers \eZ\Publish\API\Repository\SettingService - * @group integration - * @group setting - */ -final class SettingServiceTest extends BaseTest -{ - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - protected $permissionResolver; - - /** @var \eZ\Publish\API\Repository\SettingService */ - protected $settingService; - - protected function getSettingService(): SettingService - { - $container = $this->getSetupFactory()->getServiceContainer(); - /** @var \eZ\Publish\API\Repository\SettingService $settingService */ - $settingService = $container->get(SettingService::class); - - return $settingService; - } - - protected function setUp(): void - { - parent::setUp(); - - $repository = $this->getRepository(false); - $this->permissionResolver = $repository->getPermissionResolver(); - } - - /** - * @covers \eZ\Publish\API\Repository\SettingService::createSetting() - * - * @dataProvider dataProviderForCreateSetting - */ - public function testCreateSetting(string $identifier, $value): void - { - $settingService = $this->getSettingService(); - - $settingCreate = $settingService->newSettingCreateStruct(); - $settingCreate->setGroup('test_group'); - $settingCreate->setIdentifier($identifier); - $settingCreate->setValue($value); - - $setting = $settingService->createSetting($settingCreate); - - self::assertEquals(new Setting([ - 'group' => 'test_group', - 'identifier' => $identifier, - 'value' => $value, - ]), $setting); - } - - public function dataProviderForCreateSetting(): iterable - { - yield 'null' => [ - 'example_null', - null, - ]; - - yield 'boolean' => [ - 'example_boolean', - true, - ]; - - yield 'string' => [ - 'example_string', - 'string', - ]; - - yield 'int' => [ - 'example_int', - 2, - ]; - - yield 'float' => [ - 'example_number', - 3.14, - ]; - - yield 'array' => [ - 'example_hash', - [ - 'foo' => 'foo', - 'bar' => 2, - 'baz' => 3.14, - 'foobar' => range(1, 10), - ], - ]; - } - - /** - * @covers \eZ\Publish\API\Repository\SettingService::createSetting() - */ - public function testCreateSettingThrowsInvalidArgumentException(): void - { - $settingService = $this->getSettingService(); - - $settingCreateFirst = $settingService->newSettingCreateStruct(); - $settingCreateFirst->setGroup('test_group2'); - $settingCreateFirst->setIdentifier('test_identifier2'); - $settingCreateFirst->setValue('test_value'); - - $settingService->createSetting($settingCreateFirst); - - $settingCreateSecond = $settingService->newSettingCreateStruct(); - $settingCreateSecond->setGroup('test_group2'); - $settingCreateSecond->setIdentifier('test_identifier2'); - $settingCreateSecond->setValue('another_value'); - - $this->expectException(InvalidArgumentException::class); - - $settingService->createSetting($settingCreateSecond); - } - - /** - * @covers \eZ\Publish\API\Repository\SettingService::loadSetting() - */ - public function testLoadSetting(): void - { - $settingService = $this->getSettingService(); - - $settingCreate = $settingService->newSettingCreateStruct(); - $settingCreate->setGroup('another_group'); - $settingCreate->setIdentifier('another_identifier'); - $settingCreate->setValue('test_value'); - - $settingService->createSetting($settingCreate); - $setting = $settingService->loadSetting('another_group', 'another_identifier'); - - self::assertEquals('test_value', $setting->value); - } - - /** - * @covers \eZ\Publish\API\Repository\SettingService::loadSetting() - */ - public function testLoadSettingThrowsNotFoundException(): void - { - $settingService = $this->getSettingService(); - - $this->expectException(NotFoundException::class); - - $settingService->loadSetting('unknown_group', 'unknown_identifier'); - } - - /** - * @covers \eZ\Publish\API\Repository\SettingService::updateSetting() - */ - public function testUpdateSetting(): void - { - $settingService = $this->getSettingService(); - - $settingCreate = $settingService->newSettingCreateStruct(); - $settingCreate->setGroup('update_group'); - $settingCreate->setIdentifier('update_identifier'); - $settingCreate->setValue('some_value'); - - $setting = $settingService->createSetting($settingCreate); - - $settingUpdate = $settingService->newSettingUpdateStruct(); - $settingUpdate->setValue('updated_value'); - - $settingService->updateSetting($setting, $settingUpdate); - $updatedSetting = $settingService->loadSetting('update_group', 'update_identifier'); - - self::assertEquals('updated_value', $updatedSetting->value); - } - - /** - * @covers \eZ\Publish\API\Repository\SettingService::deleteSetting() - */ - public function testDeleteSetting(): void - { - $settingService = $this->getSettingService(); - - $settingCreate = $settingService->newSettingCreateStruct(); - $settingCreate->setGroup('delete_group'); - $settingCreate->setIdentifier('delete_identifier'); - $settingCreate->setValue('some_value'); - - $setting = $settingService->createSetting($settingCreate); - $settingService->deleteSetting($setting); - - $this->expectException(NotFoundException::class); - - $settingService->loadSetting('delete_group', 'delete_identifier'); - } - - /** - * @covers \eZ\Publish\API\Repository\SettingService::deleteSetting() - */ - public function testDeleteSettingThrowsNotFoundException(): void - { - $settingService = $this->getSettingService(); - - $settingCreate = $settingService->newSettingCreateStruct(); - $settingCreate->setGroup('delete_twice_group'); - $settingCreate->setIdentifier('delete_twice_identifier'); - $settingCreate->setValue('some_value'); - - $setting = $settingService->createSetting($settingCreate); - - // Delete the newly created setting - $settingService->deleteSetting($setting); - - $this->expectException(NotFoundException::class); - - // This call should fail with a NotFoundException - $settingService->deleteSetting($setting); - } -} diff --git a/eZ/Publish/API/Repository/Tests/SetupFactory/Legacy.php b/eZ/Publish/API/Repository/Tests/SetupFactory/Legacy.php deleted file mode 100644 index 89b4380998..0000000000 --- a/eZ/Publish/API/Repository/Tests/SetupFactory/Legacy.php +++ /dev/null @@ -1,379 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\SetupFactory; - -use Doctrine\DBAL\Connection; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\ServiceTags; -use eZ\Publish\API\Repository\Tests\IdManager; -use eZ\Publish\API\Repository\Tests\LegacySchemaImporter; -use eZ\Publish\API\Repository\Tests\SetupFactory; -use eZ\Publish\Core\Base\Container\Compiler; -use eZ\Publish\Core\Base\ServiceContainer; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler as CachingLanguageHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler as CachingContentTypeHandler; -use eZ\Publish\Core\Repository\Values\User\UserReference; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder as FilteringCriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\SortClauseQueryBuilder as FilteringSortClauseQueryBuilder; -use eZ\Publish\SPI\Tests\Persistence\Fixture; -use eZ\Publish\SPI\Tests\Persistence\FixtureImporter; -use eZ\Publish\SPI\Tests\Persistence\YamlFixture; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\Filesystem\Filesystem; - -/** - * A Test Factory is used to setup the infrastructure for a tests, based on a - * specific repository implementation to test. - */ -class Legacy extends SetupFactory -{ - /** - * Data source name. - * - * @var string - */ - protected static $dsn; - - /** - * Root dir for IO operations. - * - * @var string - */ - protected static $ioRootDir; - - /** - * Database type (sqlite, mysql, ...). - * - * @var string - */ - protected static $db; - - /** - * Service container. - * - * @var \eZ\Publish\Core\Base\ServiceContainer - */ - protected static $serviceContainer; - - /** - * If the DB schema has already been initialized. - * - * @var bool - */ - protected static $schemaInitialized = false; - - /** - * Cached in-memory initial database data fixture. - * - * @var \eZ\Publish\SPI\Tests\Persistence\Fixture - */ - private static $initialDataFixture; - - /** - * Cached in-memory post insert SQL statements. - * - * @var string[] - */ - private static $postInsertStatements; - - protected $repositoryReference = 'ezpublish.api.repository'; - - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** - * Creates a new setup factory. - */ - public function __construct() - { - self::$dsn = getenv('DATABASE'); - if (!self::$dsn) { - // use sqlite in-memory by default (does not need special handling for paratest as it's per process) - self::$dsn = 'sqlite://:memory:'; - } elseif (getenv('TEST_TOKEN') !== false) { - // Using paratest, assuming dsn ends with db name here... - self::$dsn .= '_' . getenv('TEST_TOKEN'); - } - - if ($repositoryReference = getenv('REPOSITORY_SERVICE_ID')) { - $this->repositoryReference = $repositoryReference; - } - - self::$db = preg_replace('(^([a-z]+).*)', '\\1', self::$dsn); - - if (!isset(self::$ioRootDir)) { - self::$ioRootDir = $this->createTemporaryDirectory(); - } - } - - /** - * Creates a temporary directory and returns it. - * - * @return string - * @throw \RuntimeException If the root directory can't be created - */ - private function createTemporaryDirectory() - { - $tmpFile = tempnam( - sys_get_temp_dir(), - 'ez_legacy_tests_' . time() - ); - unlink($tmpFile); - - $fs = new Filesystem(); - $fs->mkdir($tmpFile); - - $varDir = $tmpFile . '/var'; - if ($fs->exists($varDir)) { - $fs->remove($varDir); - } - $fs->mkdir($varDir); - - return $tmpFile; - } - - /** - * Returns a configured repository for testing. - * - * @param bool $initializeFromScratch if the back end should be initialized - * from scratch or re-used - * - * @return \eZ\Publish\API\Repository\Repository - */ - public function getRepository($initializeFromScratch = true) - { - if ($initializeFromScratch || !self::$schemaInitialized) { - $this->initializeSchema(); - $this->insertData(); - } - - $this->clearInternalCaches(); - /** @var \eZ\Publish\API\Repository\Repository $repository */ - $repository = $this->getServiceContainer()->get($this->repositoryReference); - - // Set admin user as current user by default - $repository->getPermissionResolver()->setCurrentUserReference( - new UserReference(14) - ); - - return $repository; - } - - /** - * Returns a config value for $configKey. - * - * @param string $configKey - * - * @throws \Exception if $configKey could not be found. - * - * @return mixed - */ - public function getConfigValue($configKey) - { - return $this->getServiceContainer()->getParameter($configKey); - } - - /** - * Returns a repository specific ID manager. - * - * @return \eZ\Publish\API\Repository\Tests\IdManager - */ - public function getIdManager() - { - return new IdManager\Php(); - } - - /** - * Insert the database data. - * - * @throws \Doctrine\DBAL\DBALException - */ - public function insertData(): void - { - $connection = $this->getDatabaseConnection(); - $this->cleanupVarDir($this->getInitialVarDir()); - - $fixtureImporter = new FixtureImporter($connection); - $fixtureImporter->import($this->getInitialDataFixture()); - } - - protected function getInitialVarDir() - { - return __DIR__ . '/../../../../../../var'; - } - - protected function cleanupVarDir($sourceDir) - { - $fs = new Filesystem(); - $varDir = self::$ioRootDir . '/var'; - if ($fs->exists($varDir)) { - $fs->remove($varDir); - } - $fs->mkdir($varDir); - $fs->mirror($sourceDir, $varDir); - } - - /** - * CLears internal in memory caches after inserting data circumventing the - * API. - */ - protected function clearInternalCaches() - { - /** @var $handler \eZ\Publish\Core\Persistence\Legacy\Handler */ - $handler = $this->getServiceContainer()->get('ezpublish.spi.persistence.legacy'); - - $contentLanguageHandler = $handler->contentLanguageHandler(); - if ($contentLanguageHandler instanceof CachingLanguageHandler) { - $contentLanguageHandler->clearCache(); - } - - $contentTypeHandler = $handler->contentTypeHandler(); - if ($contentTypeHandler instanceof CachingContentTypeHandler) { - $contentTypeHandler->clearCache(); - } - - /** @var $cachePool \Psr\Cache\CacheItemPoolInterface */ - $cachePool = $this->getServiceContainer()->get('ezpublish.cache_pool'); - - $cachePool->clear(); - } - - /** - * Returns the initial database data fixture. - * - * @return \eZ\Publish\SPI\Tests\Persistence\Fixture - */ - protected function getInitialDataFixture(): Fixture - { - if (!isset(self::$initialDataFixture)) { - self::$initialDataFixture = new YamlFixture( - __DIR__ . '/../_fixtures/Legacy/data/test_data.yaml' - ); - } - - return self::$initialDataFixture; - } - - /** - * Initializes the database schema. - * - * @throws \Doctrine\DBAL\ConnectionException - */ - protected function initializeSchema(): void - { - if (!self::$schemaInitialized) { - $schemaImporter = new LegacySchemaImporter($this->getDatabaseConnection()); - $schemaImporter->importSchema( - dirname(__DIR__, 5) . - '/Bundle/EzPublishCoreBundle/Resources/config/storage/legacy/schema.yaml' - ); - - self::$schemaInitialized = true; - } - } - - /** - * Returns the raw database connection from the service container. - * - * @return \Doctrine\DBAL\Connection - */ - private function getDatabaseConnection(): Connection - { - if (null === $this->connection) { - $this->connection = $this->getServiceContainer()->get('ezpublish.persistence.connection'); - } - - return $this->connection; - } - - /** - * Returns the service container used for initialization of the repository. - * - * @return \eZ\Publish\Core\Base\ServiceContainer - */ - public function getServiceContainer() - { - if (!isset(self::$serviceContainer)) { - $config = include __DIR__ . '/../../../../../../config.php'; - $installDir = $config['install_dir']; - - /** @var \Symfony\Component\DependencyInjection\ContainerBuilder $containerBuilder */ - $containerBuilder = include $config['container_builder_path']; - - /* @var \Symfony\Component\DependencyInjection\Loader\YamlFileLoader $loader */ - $loader->load('search_engines/legacy.yml'); - $loader->load('tests/integration_legacy.yml'); - - $this->externalBuildContainer($containerBuilder); - - $containerBuilder->setParameter( - 'legacy_dsn', - self::$dsn - ); - - $containerBuilder->setParameter( - 'io_root_dir', - self::$ioRootDir . '/' . $containerBuilder->getParameter('storage_dir') - ); - - $containerBuilder->addCompilerPass(new Compiler\Search\FieldRegistryPass()); - - $this->registerForAutoConfiguration($containerBuilder); - - // load overrides just before creating test Container - $loader->load('tests/override.yml'); - - self::$serviceContainer = new ServiceContainer( - $containerBuilder, - $installDir, - $config['cache_dir'], - true, - true - ); - } - - return self::$serviceContainer; - } - - /** - * This is intended to be used from external repository in order to - * enable container customization. - * - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $containerBuilder - */ - protected function externalBuildContainer(ContainerBuilder $containerBuilder) - { - // Does nothing by default - } - - /** - * Get the Database name. - * - * @return string - */ - public function getDB() - { - return self::$db; - } - - /** - * Apply automatic configuration to needed Symfony Services. - * - * Note: Based on - * {@see \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\EzPublishCoreExtension::registerForAutoConfiguration}, - * but only for services needed by integration test setup. - * - * @see - */ - private function registerForAutoConfiguration(ContainerBuilder $containerBuilder): void - { - $containerBuilder->registerForAutoconfiguration(FilteringCriterionQueryBuilder::class) - ->addTag(ServiceTags::FILTERING_CRITERION_QUERY_BUILDER); - - $containerBuilder->registerForAutoconfiguration(FilteringSortClauseQueryBuilder::class) - ->addTag(ServiceTags::FILTERING_SORT_CLAUSE_QUERY_BUILDER); - } -} diff --git a/eZ/Publish/API/Repository/Tests/TrashServiceTest.php b/eZ/Publish/API/Repository/Tests/TrashServiceTest.php deleted file mode 100644 index c4bbe2950a..0000000000 --- a/eZ/Publish/API/Repository/Tests/TrashServiceTest.php +++ /dev/null @@ -1,1382 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use DateTime; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\URLAliasService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Trash\SearchResult; -use eZ\Publish\API\Repository\Values\Content\TrashItem as APITrashItem; -use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\TrashItem; - -/** - * Test case for operations in the TrashService using in memory storage. - * - * @see eZ\Publish\API\Repository\TrashService - * @group integration - * @group trash - */ -class TrashServiceTest extends BaseTrashServiceTest -{ - /** - * Test for the trash() method. - * - * @see \eZ\Publish\API\Repository\TrashService::trash() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationByRemoteId - */ - public function testTrash() - { - /* BEGIN: Use Case */ - $trashItem = $this->createTrashItem(); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\TrashItem', - $trashItem - ); - } - - /** - * Test for the trash() method. - * - * @see \eZ\Publish\API\Repository\TrashService::trash() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash - */ - public function testTrashSetsExpectedTrashItemProperties() - { - $repository = $this->getRepository(); - - $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; - - // Load the location that will be trashed - $location = $repository->getLocationService() - ->loadLocationByRemoteId($mediaRemoteId); - - $expected = [ - 'id' => $location->id, - 'depth' => $location->depth, - 'hidden' => $location->hidden, - 'invisible' => $location->invisible, - 'parentLocationId' => $location->parentLocationId, - 'pathString' => $location->pathString, - 'priority' => $location->priority, - 'remoteId' => $location->remoteId, - 'sortField' => $location->sortField, - 'sortOrder' => $location->sortOrder, - ]; - - $trashItem = $this->createTrashItem(); - - $this->assertPropertiesCorrect($expected, $trashItem); - } - - /** - * Test for the trash() method. - * - * @see \eZ\Publish\API\Repository\TrashService::trash() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash - */ - public function testTrashRemovesLocationFromMainStorage() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; - - /* BEGIN: Use Case */ - $this->createTrashItem(); - - // Load the location service - $locationService = $repository->getLocationService(); - - // This call will fail with a "NotFoundException", because the media - // location was marked as trashed in the main storage - $locationService->loadLocationByRemoteId($mediaRemoteId); - /* END: Use Case */ - } - - /** - * Test for the trash() method. - * - * @see \eZ\Publish\API\Repository\TrashService::trash() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash - */ - public function testTrashRemovesChildLocationsFromMainStorage() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $remoteIds = $this->createRemoteIdList(); - - $this->createTrashItem(); - - // All invocations to loadLocationByRemoteId() to one of the above - // collected remoteIds will return in an "NotFoundException" - /* END: Use Case */ - - $locationService = $repository->getLocationService(); - foreach ($remoteIds as $remoteId) { - try { - $locationService->loadLocationByRemoteId($remoteId); - $this->fail("Location '{$remoteId}' should exist.'"); - } catch (NotFoundException $e) { - // echo $e->getFile(), ' +', $e->getLine(), PHP_EOL; - } - } - - $this->assertGreaterThan( - 0, - count($remoteIds), - "There should be at least one 'Community' child location." - ); - } - - /** - * Test for the trash() method. - * - * @see \eZ\Publish\API\Repository\TrashService::trash() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash - */ - public function testTrashDecrementsChildCountOnParentLocation() - { - $repository = $this->getRepository(); - $locationService = $repository->getLocationService(); - - $baseLocationId = $this->generateId('location', 1); - - $location = $locationService->loadLocation($baseLocationId); - - $childCount = $locationService->getLocationChildCount($location); - - $this->createTrashItem(); - - $this->refreshSearch($repository); - - $this->assertEquals( - $childCount - 1, - $locationService->getLocationChildCount($location) - ); - } - - /** - * Test sending a location to trash updates Content mainLocation. - * - * @covers \eZ\Publish\API\Repository\TrashService::trash - */ - public function testTrashUpdatesMainLocation() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - $trashService = $repository->getTrashService(); - - $contentInfo = $contentService->loadContentInfo(42); - - // Create additional location that will become new main location - $location = $locationService->createLocation( - $contentInfo, - new LocationCreateStruct(['parentLocationId' => 2]) - ); - - $trashService->trash( - $locationService->loadLocation($contentInfo->mainLocationId) - ); - - self::assertEquals( - $location->id, - $contentService->loadContentInfo(42)->mainLocationId - ); - } - - /** - * Test sending a location to trash. - * - * @covers \eZ\Publish\API\Repository\TrashService::trash - */ - public function testTrashReturnsNull() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - $trashService = $repository->getTrashService(); - - // Create additional location to trash - $location = $locationService->createLocation( - $contentService->loadContentInfo(42), - new LocationCreateStruct(['parentLocationId' => 2]) - ); - - $trashItem = $trashService->trash($location); - - self::assertNull($trashItem); - } - - /** - * Test for the loadTrashItem() method. - * - * @covers \eZ\Publish\API\Repository\TrashService::loadTrashItem - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash - */ - public function testLoadTrashItem() - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - - /* BEGIN: Use Case */ - $trashItem = $this->createTrashItem(); - - // Reload the trash item - $trashItemReloaded = $trashService->loadTrashItem($trashItem->id); - /* END: Use Case */ - - $this->assertInstanceOf( - APITrashItem::class, - $trashItemReloaded - ); - - $this->assertEquals( - $trashItem->pathString, - $trashItemReloaded->pathString - ); - - $this->assertInstanceOf( - DateTime::class, - $trashItemReloaded->trashed - ); - - $this->assertEquals( - $trashItem->trashed->getTimestamp(), - $trashItemReloaded->trashed->getTimestamp() - ); - - $this->assertGreaterThan( - 0, - $trashItemReloaded->trashed->getTimestamp() - ); - - $this->assertInstanceOf( - Content::class, - $content = $trashItemReloaded->getContent() - ); - $this->assertEquals($trashItem->contentId, $content->contentInfo->id); - } - - /** - * Test for the loadTrashItem() method. - * - * @see \eZ\Publish\API\Repository\TrashService::loadTrashItem() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testLoadTrashItem - */ - public function testLoadTrashItemThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - $nonExistingTrashId = $this->generateId('trash', 2342); - /* BEGIN: Use Case */ - $trashService = $repository->getTrashService(); - - // This call will fail with a "NotFoundException", because no trash item - // with the ID 1342 should exist in an eZ Publish demo installation - $trashService->loadTrashItem($nonExistingTrashId); - /* END: Use Case */ - } - - /** - * Test for the recover() method. - * - * @covers \eZ\Publish\API\Repository\TrashService::recover - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash - */ - public function testRecover() - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - $locationService = $repository->getLocationService(); - - $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; - - /* BEGIN: Use Case */ - $trashItem = $this->createTrashItem(); - - // Recover the trashed item - $location = $trashService->recover($trashItem); - - // Load the recovered location - $locationReloaded = $locationService->loadLocationByRemoteId( - $mediaRemoteId - ); - /* END: Use Case */ - - $this->assertInstanceOf( - APILocation::class, - $location - ); - - $this->assertEquals( - $location, - $locationReloaded - ); - - try { - $trashService->loadTrashItem($trashItem->id); - $this->fail('Trash item was not removed after being recovered.'); - } catch (NotFoundException $e) { - // All well - } - } - - /** - * Test recovering a non existing trash item results in a NotFoundException. - * - * @covers \eZ\Publish\API\Repository\TrashService::recover - */ - public function testRecoverThrowsNotFoundExceptionForNonExistingTrashItem() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - - $trashService->recover( - $this->getTrashItemDouble( - 12364, - 12345, - 12363 - ) - ); - } - - /** - * Test for the trash() method. - * - * @see \eZ\Publish\API\Repository\TrashService::recover() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash - */ - public function testNotFoundAliasAfterRemoveIt() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; - - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - $urlAliasService = $repository->getURLAliasService(); - $locationService = $repository->getLocationService(); - - // Double ->lookup() call because there where issue that one call was not enough to spot bug - $urlAliasService->lookup('/Media'); - $urlAliasService->lookup('/Media'); - - $mediaLocation = $locationService->loadLocationByRemoteId($mediaRemoteId); - $trashService->trash($mediaLocation); - - $urlAliasService->lookup('/Media'); - } - - /** - * Test for the recover() method. - * - * @see \eZ\Publish\API\Repository\TrashService::recover() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash - */ - public function testAliasesForRemovedItems() - { - $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; - - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - $urlAliasService = $repository->getURLAliasService(); - $locationService = $repository->getLocationService(); - - // Double ->lookup() call because there where issue that one call was not enough to spot bug - $urlAliasService->lookup('/Media'); - $trashedLocationAlias = $urlAliasService->lookup('/Media'); - - $mediaLocation = $locationService->loadLocationByRemoteId($mediaRemoteId); - $trashItem = $trashService->trash($mediaLocation); - $this->assertAliasNotExists($urlAliasService, '/Media'); - - $this->createNewContentInPlaceTrashedOne($repository, $mediaLocation->parentLocationId); - - $createdLocationAlias = $urlAliasService->lookup('/Media'); - - $this->assertNotEquals( - $trashedLocationAlias->destination, - $createdLocationAlias->destination, - 'Destination for /media url should changed' - ); - - $recoveredLocation = $trashService->recover($trashItem); - $recoveredLocationAlias = $urlAliasService->lookup('/Media2'); - $recoveredLocationAliasReverse = $urlAliasService->reverseLookup($recoveredLocation); - - $this->assertEquals($recoveredLocationAlias->destination, $recoveredLocationAliasReverse->destination); - - $this->assertNotEquals($recoveredLocationAliasReverse->destination, $trashedLocationAlias->destination); - $this->assertNotEquals($recoveredLocationAliasReverse->destination, $createdLocationAlias->destination); - } - - /** - * Test for the recover() method. - * - * @see \eZ\Publish\API\Repository\TrashService::recover() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testRecover - */ - public function testRecoverDoesNotRestoreChildLocations() - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - $locationService = $repository->getLocationService(); - - $remoteIds = $this->createRemoteIdList(); - - // Unset remote ID of actually restored location - unset($remoteIds[array_search('3f6d92f8044aed134f32153517850f5a', $remoteIds)]); - - $trashItem = $this->createTrashItem(); - - $trashService->recover($trashItem); - - $this->assertGreaterThan( - 0, - count($remoteIds), - "There should be at least one 'Community' child location." - ); - - // None of the child locations will be available again - foreach ($remoteIds as $remoteId) { - try { - $locationService->loadLocationByRemoteId($remoteId); - $this->fail( - sprintf( - 'Location with remote ID "%s" unexpectedly restored.', - $remoteId - ) - ); - } catch (NotFoundException $e) { - // All well - } - } - - try { - $trashService->loadTrashItem($trashItem->id); - $this->fail('Trash item was not removed after being recovered.'); - } catch (NotFoundException $e) { - // All well - } - } - - /** - * Test for the recover() method. - * - * @see \eZ\Publish\API\Repository\TrashService::recover($trashItem, $newParentLocation) - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testRecover - * - * @todo Fix naming - */ - public function testRecoverWithLocationCreateStructParameter() - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - $locationService = $repository->getLocationService(); - - $homeLocationId = $this->generateId('location', 2); - /* BEGIN: Use Case */ - // $homeLocationId is the ID of the "Home" location in an eZ Publish - // demo installation - - $trashItem = $this->createTrashItem(); - - // Get the new parent location - $newParentLocation = $locationService->loadLocation($homeLocationId); - - // Recover location with new location - $location = $trashService->recover($trashItem, $newParentLocation); - /* END: Use Case */ - - $this->assertPropertiesCorrect( - [ - 'remoteId' => $trashItem->remoteId, - 'parentLocationId' => $homeLocationId, - // Not the full sub tree is restored - 'depth' => $newParentLocation->depth + 1, - 'hidden' => false, - 'invisible' => $trashItem->invisible, - 'pathString' => $newParentLocation->pathString . $this->parseId('location', $location->id) . '/', - 'priority' => 0, - 'sortField' => APILocation::SORT_FIELD_NAME, - 'sortOrder' => APILocation::SORT_ORDER_ASC, - ], - $location - ); - - try { - $trashService->loadTrashItem($trashItem->id); - $this->fail('Trash item was not removed after being recovered.'); - } catch (NotFoundException $e) { - // All well - } - } - - /** - * Test for the recover() method. - * - * @see \eZ\Publish\API\Repository\TrashService::recover($trashItem) - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testRecover - */ - public function testRecoverIncrementsChildCountOnOriginalParent() - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation($this->generateId('location', 1)); - - $trashItem = $this->createTrashItem(); - - $this->refreshSearch($repository); - - /* BEGIN: Use Case */ - $childCount = $locationService->getLocationChildCount($location); - - // Recover location with new location - $trashService->recover($trashItem); - /* END: Use Case */ - - $this->refreshSearch($repository); - - $this->assertEquals( - $childCount + 1, - $locationService->getLocationChildCount($location) - ); - - try { - $trashService->loadTrashItem($trashItem->id); - $this->fail('Trash item was not removed after being recovered.'); - } catch (NotFoundException $e) { - // All well - } - } - - /** - * Test for the recover() method. - * - * @see \eZ\Publish\API\Repository\TrashService::recover($trashItem, $newParentLocation) - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testRecoverWithLocationCreateStructParameter - */ - public function testRecoverWithLocationCreateStructParameterIncrementsChildCountOnNewParent() - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - $locationService = $repository->getLocationService(); - - $homeLocationId = $this->generateId('location', 2); - - $location = $locationService->loadLocation($homeLocationId); - - $childCount = $locationService->getLocationChildCount($location); - - /* BEGIN: Use Case */ - // $homeLocationId is the ID of the "Home" location in an eZ Publish - // demo installation - - $trashItem = $this->createTrashItem(); - - // Get the new parent location - $newParentLocation = $locationService->loadLocation($homeLocationId); - - // Recover location with new location - $trashService->recover($trashItem, $newParentLocation); - /* END: Use Case */ - - $this->refreshSearch($repository); - - $this->assertEquals( - $childCount + 1, - $locationService->getLocationChildCount($location) - ); - - try { - $trashService->loadTrashItem($trashItem->id); - $this->fail('Trash item was not removed after being recovered.'); - } catch (NotFoundException $e) { - // All well - } - } - - /** - * Test recovering a location from trash to non existing location. - * - * @covers \eZ\Publish\API\Repository\TrashService::recover - */ - public function testRecoverToNonExistingLocation() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation(44); - $trashItem = $trashService->trash($location); - - $newParentLocation = new Location( - [ - 'id' => 123456, - 'parentLocationId' => 123455, - ] - ); - $trashService->recover($trashItem, $newParentLocation); - } - - /** - * @covers \eZ\Publish\API\Repository\TrashService::findTrashItems - * @dataProvider trashFiltersProvider - */ - public function testFindTrashItems( - array $filters, - int $expectedCount - ): void { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - - $this->trashDifferentContentItems(); - - $query = new Query(); - $filtersCount = count($filters); - - if ($filtersCount === 1) { - $query->filter = $filters[0]; - } elseif ($filtersCount > 1) { - $query->filter = new Criterion\LogicalAnd( - $filters - ); - } - - $searchResult = $trashService->findTrashItems($query); - - $this->assertEquals($expectedCount, $searchResult->totalCount); - } - - /** - * @covers \eZ\Publish\API\Repository\TrashService::findTrashItems - * - * @throws \ErrorException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testFindTrashItemsSortedByDateTrashed(): void - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - $locationService = $repository->getLocationService(); - - $folder1 = $this->createFolder(['eng-GB' => 'Folder1'], 2); - $folder2 = $this->createFolder(['eng-GB' => 'Folder2'], 2); - - $firstTrashedItem = $trashService->trash( - $locationService->loadLocation($folder1->contentInfo->mainLocationId) - ); - $this->updateTrashedDate($firstTrashedItem->id, \time() - 100); - $latestTrashItem = $trashService->trash( - $locationService->loadLocation($folder2->contentInfo->mainLocationId) - ); - - $query = new Query(); - - // Load all trashed locations, sorted by trashed date ASC - $query->sortClauses = [new SortClause\Trash\DateTrashed(Query::SORT_ASC)]; - $searchResult = $trashService->findTrashItems($query); - self::assertEquals(2, $searchResult->totalCount); - self::assertEquals($firstTrashedItem->remoteId, $searchResult->items[0]->remoteId); - self::assertEquals($latestTrashItem->remoteId, $searchResult->items[1]->remoteId); - - // Load all trashed locations, sorted by trashed date DESC - $query->sortClauses = [new SortClause\Trash\DateTrashed(Query::SORT_DESC)]; - $searchResult = $trashService->findTrashItems($query); - self::assertEquals(2, $searchResult->totalCount); - self::assertEquals($latestTrashItem->remoteId, $searchResult->items[0]->remoteId); - self::assertEquals($firstTrashedItem->remoteId, $searchResult->items[1]->remoteId); - } - - /** - * @covers \eZ\Publish\API\Repository\TrashService::findTrashItems - * @dataProvider trashSortClausesProvider - */ - public function testFindTrashItemsSort(array $sortClausesClasses): void - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - - $expectedCount = 2; - $ascQuery = new Query(); - $ascQuery->limit = $expectedCount; - $descQuery = clone $ascQuery; - - $this->trashDifferentContentItems(); - - foreach ($sortClausesClasses as $sortClauseClass) { - $ascQuery->sortClauses[] = new $sortClauseClass(Query::SORT_ASC); - } - - $ascResultsIds = []; - foreach ($trashService->findTrashItems($ascQuery) as $result) { - $ascResultsIds[] = $result->contentInfo->id; - } - - $this->assertGreaterThanOrEqual($expectedCount, count($ascResultsIds)); - - foreach ($sortClausesClasses as $sortClauseClass) { - $descQuery->sortClauses[] = new $sortClauseClass(Query::SORT_DESC); - } - - $descResultsIds = []; - foreach ($trashService->findTrashItems($descQuery) as $result) { - $descResultsIds[] = $result->contentInfo->id; - } - - $this->assertNotSame($descResultsIds, $ascResultsIds); - - krsort($descResultsIds); - $descResultsIds = array_values($descResultsIds); - - $this->assertSame($descResultsIds, $ascResultsIds); - } - - /** - * Test for the findTrashItems() method for it's result structure. - * - * @see \eZ\Publish\API\Repository\TrashService::findTrashItems() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testTrash - */ - public function testFindTrashItemsLimits() - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - - $this->createTrashItem(); - - // Create a search query for all trashed items - $query = new Query(); - $query->limit = 2; - - // Load all trashed locations - $searchResult = $trashService->findTrashItems($query); - - $this->assertInstanceOf( - SearchResult::class, - $searchResult - ); - - // 4 trashed locations from the sub tree, but only 2 in results - $this->assertCount(2, $searchResult->items); - $this->assertEquals(4, $searchResult->count); - $this->assertEquals(4, $searchResult->totalCount); - } - - /** - * Test for the findTrashItems() method. - * - * @see \eZ\Publish\API\Repository\TrashService::findTrashItems() - * @depends \eZ\Publish\API\Repository\Tests\TrashServiceTest::testFindTrashItems - */ - public function testFindTrashItemsLimitedAccess() - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - - /* BEGIN: Use Case */ - $this->createTrashItem(); - - // Create a search query for all trashed items - $query = new Query(); - $query->filter = new Criterion\LogicalAnd( - [ - new Criterion\Field('title', Criterion\Operator::LIKE, '*'), - ] - ); - - // Create a user in the Editor user group. - $user = $this->createUserVersion1(); - - // Set the Editor user as current user, these users have no access to Trash by default. - $repository->getPermissionResolver()->setCurrentUserReference($user); - - // Load all trashed locations - $searchResult = $trashService->findTrashItems($query); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\SearchResult', - $searchResult - ); - - // 0 trashed locations found, though 4 exist - $this->assertEquals(0, $searchResult->count); - } - - /** - * Test Section Role Assignment Limitation against user/login. - */ - public function testFindTrashItemsSubtreeLimitation() - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - $contentTypeService = $repository->getContentTypeService(); - $trashService = $repository->getTrashService(); - - $folder1 = $this->createFolder(['eng-GB' => 'Folder1'], 2); - $folderLocationId = $folder1->contentInfo->mainLocationId; - $contentType = $contentTypeService->loadContentTypeByIdentifier('forum'); - $newContent = $contentService->newContentCreateStruct($contentType, 'eng-US'); - $newContent->setField('name', 'Media'); - $draftContent = $contentService->createContent($newContent, [new LocationCreateStruct(['parentLocationId' => $folderLocationId])]); - $published = $contentService->publishVersion($draftContent->versionInfo); - $location = $locationService->loadLocation($published->contentInfo->mainLocationId); - $trashService->trash($location); - - $this->createRoleWithPolicies('roleTrashCleaner', [ - [ - 'module' => 'content', - 'function' => 'cleantrash', - ], - [ - 'module' => 'content', - 'function' => 'read', - 'limitations' => [ - new SubtreeLimitation(['limitationValues' => [sprintf('/1/2/%d/', $folderLocationId)]]), - ], - ], - ]); - $user = $this->createCustomUserWithLogin( - 'user', - 'user@example.com', - 'roleTrashCleaners', - 'roleTrashCleaner' - ); - $repository->getPermissionResolver()->setCurrentUserReference($user); - - $query = new Query(); - - // Load all trashed locations - $searchResult = $trashService->findTrashItems($query); - /* END: Use Case */ - $this->assertInstanceOf( - SearchResult::class, - $searchResult - ); - - $this->assertEquals(1, count($searchResult->items)); - } - - /** - * Test for the emptyTrash() method. - * - * @see \eZ\Publish\API\Repository\TrashService::emptyTrash() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testFindTrashItems - */ - public function testEmptyTrash() - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - $contentService = $repository->getContentService(); - - /* BEGIN: Use Case */ - $trashItem = $this->createTrashItem(); - - // Empty the trash - $trashService->emptyTrash(); - - // Create a search query for all trashed items - $query = new Query(); - - // Load all trashed locations, search result should be empty - $searchResult = $trashService->findTrashItems($query); - /* END: Use Case */ - - $this->assertEquals(0, $searchResult->count); - - // Try to load content - $this->expectException(NotFoundException::class); - $contentService->loadContent($trashItem->contentId); - } - - /** - * Test for the emptyTrash() method with user which has subtree limitations. - * - * @see \eZ\Publish\API\Repository\TrashService::emptyTrash() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testFindTrashItems - */ - public function testEmptyTrashForUserWithSubtreeLimitation() - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - $contentService = $repository->getContentService(); - - /* BEGIN: Use Case */ - $trashItem = $this->createTrashItem(); - - $this->createRoleWithPolicies('roleTrashCleaner', [ - ['module' => 'content', 'function' => 'cleantrash'], - ['module' => 'content', 'function' => 'read'], - ]); - $user = $this->createCustomUserWithLogin( - 'user', - 'user@example.com', - 'roleTrashCleaners', - 'roleTrashCleaner', - new SubtreeLimitation(['limitationValues' => ['/1/2/']]) - ); - $repository->getPermissionResolver()->setCurrentUserReference($user); - - // Empty the trash - $trashService->emptyTrash(); - - // Create a search query for all trashed items - $query = new Query(); - - // Load all trashed locations, search result should be empty - $searchResult = $trashService->findTrashItems($query); - /* END: Use Case */ - - $this->assertEquals(0, $searchResult->totalCount); - - // Try to load content - $this->expectException(NotFoundException::class); - $contentService->loadContent($trashItem->contentId); - } - - /** - * Test for the deleteTrashItem() method. - * - * @see \eZ\Publish\API\Repository\TrashService::deleteTrashItem() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testFindTrashItems - */ - public function testDeleteTrashItem() - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - - $demoDesignLocationId = $this->generateId('location', 56); - /* BEGIN: Use Case */ - // $demoDesignLocationId is the ID of the "Demo Design" location in an eZ - // Publish demo installation - - $trashItem = $this->createTrashItem(); - - // Trash one more location - $trashService->trash( - $locationService->loadLocation($demoDesignLocationId) - ); - - // Empty the trash - $trashService->deleteTrashItem($trashItem); - - // Create a search query for all trashed items - $query = new Query(); - - // Load all trashed locations, should only contain the Demo Design location - $searchResult = $trashService->findTrashItems($query); - /* END: Use Case */ - - $foundIds = array_map( - static function ($trashItem) { - return $trashItem->id; - }, - $searchResult->items - ); - - $this->assertEquals(4, $searchResult->count); - $this->assertTrue( - in_array($demoDesignLocationId, $foundIds) - ); - - // Try to load Content - $this->expectException(NotFoundException::class); - $contentService->loadContent($trashItem->contentId); - } - - /** - * Test deleting a non existing trash item. - * - * @covers \eZ\Publish\API\Repository\TrashService::deleteTrashItem - */ - public function testDeleteThrowsNotFoundExceptionForNonExistingTrashItem() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - - $trashService->deleteTrashItem($this->getTrashItemDouble( - 12364, - 12345, - 12363 - )); - } - - public function testTrashProperlyAssignsRemovedLocationContentMapToTrashItem(): void - { - $repository = $this->getRepository(); - $trashService = $repository->getTrashService(); - $locationService = $repository->getLocationService(); - - $folder1 = $this->createFolder(['eng-GB' => 'Folder1'], 2); - $folder2 = $this->createFolder(['eng-GB' => 'Folder2'], $folder1->contentInfo->getMainLocationId()); - $folder3 = $this->createFolder(['eng-GB' => 'Folder2'], $folder2->contentInfo->getMainLocationId()); - - $folderLocation = $locationService->loadLocation($folder1->contentInfo->getMainLocationId()); - - $trashItem = $trashService->trash($folderLocation); - $removedLocationContentMap = $trashItem->getRemovedLocationContentIdMap(); - - self::assertSame( - [ - $folderLocation->id => $folder1->id, - $folder2->contentInfo->getMainLocationId() => $folder2->id, - $folder3->contentInfo->getMainLocationId() => $folder3->id, - ], - $removedLocationContentMap, - ); - } - - /** - * @return array - */ - public function trashFiltersProvider(): array - { - return [ - [ - [], - 2, - ], - [ - [ - new Criterion\ContentTypeId(4), - ], - 1, - ], - [ - [ - new Criterion\ContentTypeId(999), - ], - 0, - ], - [ - [ - new Criterion\SectionId(2), - ], - 1, - ], - [ - [ - new Criterion\SectionId(999), - ], - 0, - ], - [ - [ - new Criterion\UserMetadata( - Criterion\UserMetadata::OWNER, - Criterion\Operator::EQ, - 14 - ), - ], - 1, - ], - [ - [ - new Criterion\UserMetadata( - Criterion\UserMetadata::OWNER, - Criterion\Operator::EQ, - 999 - ), - ], - 0, - ], - [ - [ - new Criterion\DateMetadata( - Criterion\DateMetadata::TRASHED, - Criterion\Operator::BETWEEN, - [time(), time() + 86400] - ), - ], - 2, - ], - [ - [ - new Criterion\DateMetadata( - Criterion\DateMetadata::TRASHED, - Criterion\Operator::BETWEEN, - [time() - 90, time()] - ), - ], - 0, - ], - [ - [ - new Criterion\ContentTypeId(1), - new Criterion\SectionId(1), - new Criterion\UserMetadata( - Criterion\UserMetadata::OWNER, - Criterion\Operator::EQ, - 14 - ), - new Criterion\DateMetadata( - Criterion\DateMetadata::TRASHED, - Criterion\Operator::BETWEEN, - [time(), time() + 86400] - ), - ], - 1, - ], - [ - [ - new Criterion\ContentTypeId(999), - new Criterion\SectionId(1), - new Criterion\UserMetadata( - Criterion\UserMetadata::OWNER, - Criterion\Operator::EQ, - 10 - ), - new Criterion\DateMetadata( - Criterion\DateMetadata::TRASHED, - Criterion\Operator::BETWEEN, - [time() - 90, time() + 90] - ), - ], - 0, - ], - [ - [ - new Criterion\MatchNone(), - ], - 0, - ], - [ - [ - new Criterion\MatchAll(), - ], - 2, - ], - [ - [ - new Criterion\LogicalNot(new Criterion\SectionId(2)), - ], - 1, - ], - [ - [ - new Criterion\LogicalOr([ - new Criterion\SectionId(1), - new Criterion\ContentTypeId(4), - ]), - ], - 2, - ], - [ - [ - new Criterion\LogicalAnd([ - new Criterion\SectionId(2), - new Criterion\ContentTypeId(4), - ]), - ], - 1, - ], - ]; - } - - public function trashSortClausesProvider(): array - { - return [ - [ - [ - SortClause\SectionName::class, - ], - ], - [ - [ - SortClause\ContentName::class, - ], - ], - [ - [ - SortClause\Trash\ContentTypeName::class, - ], - ], - [ - [ - SortClause\Trash\UserLogin::class, - ], - ], - [ - [ - SortClause\SectionName::class, - SortClause\ContentName::class, - SortClause\Trash\ContentTypeName::class, - SortClause\Trash\UserLogin::class, - ], - ], - ]; - } - - /** - * Returns an array with the remoteIds of all child locations of the - * <b>Community</b> location. It is stored in a local variable named - * <b>$remoteIds</b>. - * - * @return string[] - */ - private function createRemoteIdList() - { - $repository = $this->getRepository(); - - /* BEGIN: Inline */ - // remoteId of the "Community" location in an eZ Publish demo installation - $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; - - // Load the location service - $locationService = $repository->getLocationService(); - - $remoteIds = []; - $children = $locationService->loadLocationChildren($locationService->loadLocationByRemoteId($mediaRemoteId)); - foreach ($children->locations as $child) { - $remoteIds[] = $child->remoteId; - foreach ($locationService->loadLocationChildren($child)->locations as $grandChild) { - $remoteIds[] = $grandChild->remoteId; - } - } - /* END: Inline */ - - return $remoteIds; - } - - /** - * @param \eZ\Publish\API\Repository\Repository $repository - * @param int $parentLocationId - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - protected function createNewContentInPlaceTrashedOne(Repository $repository, $parentLocationId) - { - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - $contentTypeService = $repository->getContentTypeService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier('forum'); - $newContent = $contentService->newContentCreateStruct($contentType, 'eng-US'); - $newContent->setField('name', 'Media'); - - $location = $locationService->newLocationCreateStruct($parentLocationId); - - $draftContent = $contentService->createContent($newContent, [$location]); - - return $contentService->publishVersion($draftContent->versionInfo); - } - - /** - * @param \eZ\Publish\API\Repository\URLAliasService $urlAliasService - * @param string $urlPath Url alias path - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - private function assertAliasExists(URLAliasService $urlAliasService, $urlPath) - { - $urlAlias = $urlAliasService->lookup($urlPath); - - $this->assertInstanceOf('\eZ\Publish\API\Repository\Values\Content\URLAlias', $urlAlias); - - return $urlAlias; - } - - /** - * @param \eZ\Publish\API\Repository\URLAliasService $urlAliasService - * @param string $urlPath Url alias path - */ - private function assertAliasNotExists(URLAliasService $urlAliasService, $urlPath) - { - try { - $this->getRepository()->getURLAliasService()->lookup($urlPath); - $this->fail(sprintf('Alias [%s] should not exist', $urlPath)); - } catch (\eZ\Publish\API\Repository\Exceptions\NotFoundException $e) { - $this->assertTrue(true); - } - } - - /** - * Get Test Double for TrashItem for exception testing and similar. - * - * @param int $trashId - * @param int $contentId - * @param int $parentLocationId - * - * @return \eZ\Publish\API\Repository\Values\Content\TrashItem - */ - private function getTrashItemDouble(int $trashId, int $contentId = 44, int $parentLocationId = 2): APITrashItem - { - return new TrashItem([ - 'id' => $trashId, - 'parentLocationId' => $parentLocationId, - 'contentInfo' => new ContentInfo(['id' => $contentId]), - ]); - } - - private function trashDifferentContentItems(): void - { - $repository = $this->getRepository(false); - $permissionResolver = $repository->getPermissionResolver(); - $trashService = $repository->getTrashService(); - $locationService = $repository->getLocationService(); - $currentUser = $permissionResolver->getCurrentUserReference(); - - $folderContent = $this->createFolder(['eng-GB' => 'Folder'], 2); - - $newCreator = $this->createUserWithPolicies( - 'test_user', - [ - ['module' => 'content', 'function' => 'create'], - ['module' => 'content', 'function' => 'read'], - ['module' => 'content', 'function' => 'publish'], - ] - ); - - $permissionResolver->setCurrentUserReference($newCreator); - - $userContent = $this->createUser('test_user2', 'Some2', 'User2'); - - $permissionResolver->setCurrentUserReference($currentUser); - - $locationIds = [ - $userContent->contentInfo->mainLocationId, - $folderContent->contentInfo->mainLocationId, - ]; - - foreach ($locationIds as $locationId) { - $trashService->trash( - $locationService->loadLocation($locationId) - ); - } - } -} diff --git a/eZ/Publish/API/Repository/Tests/URLAliasService/CustomUrlAliasForMultilingualContentTest.php b/eZ/Publish/API/Repository/Tests/URLAliasService/CustomUrlAliasForMultilingualContentTest.php deleted file mode 100644 index 91a8bd6427..0000000000 --- a/eZ/Publish/API/Repository/Tests/URLAliasService/CustomUrlAliasForMultilingualContentTest.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\URLAliasService; - -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Tests\BaseTest; - -final class CustomUrlAliasForMultilingualContentTest extends BaseTest -{ - /** - * @covers \eZ\Publish\API\Repository\ContentService::publishVersion - * @covers \eZ\Publish\API\Repository\URLAliasService::createUrlAlias - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testCreateCustomUrlAliasWithTheSamePathThrowsException(): void - { - $repository = $this->getRepository(); - $urlAliasService = $repository->getURLAliasService(); - $locationService = $repository->getLocationService(); - $language = 'ger-DE'; - - $names = [ - 'eng-GB' => 'Contact', - 'ger-DE' => 'Kontakt', - 'eng-US' => 'Contact', - ]; - $contactFolder = $this->createFolder( - $names, - 2, - null, - false // not always available, so the created content behaves the same as "article" - ); - - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage( - 'Argument \'$path\' is invalid: Path \'Contact\' already exists for the given context' - ); - // attempt to create custom alias for German translation while a system one - // for a different translation already exists - $urlAliasService->createUrlAlias( - $locationService->loadLocation( - $contactFolder->contentInfo->mainLocationId - ), - 'Contact', - $language, - true, // forwarding - true // always available - ); - } -} diff --git a/eZ/Publish/API/Repository/Tests/URLAliasServiceTest.php b/eZ/Publish/API/Repository/Tests/URLAliasServiceTest.php deleted file mode 100644 index 0bb87cc2a8..0000000000 --- a/eZ/Publish/API/Repository/Tests/URLAliasServiceTest.php +++ /dev/null @@ -1,1765 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use Doctrine\DBAL\Connection; -use Exception; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Tests\Common\SlugConverter as TestSlugConverter; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use PDO; -use RuntimeException; - -/** - * Test case for operations in the URLAliasService using in memory storage. - * - * @see \eZ\Publish\API\Repository\URLAliasService - * @group url-alias - */ -class URLAliasServiceTest extends BaseTest -{ - /** - * Tests that the required <b>LocationService::loadLocation()</b> - * at least returns an object, because this method is utilized in several - * tests. - */ - protected function setUp(): void - { - parent::setUp(); - - try { - // Load the LocationService - $locationService = $this->getRepository()->getLocationService(); - - $membersUserGroupLocationId = 12; - - // Load a location instance - $location = $locationService->loadLocation( - $membersUserGroupLocationId - ); - - if (false === is_object($location)) { - $this->markTestSkipped( - 'This test cannot be executed, because the utilized ' . - 'LocationService::loadLocation() does not ' . - 'return an object.' - ); - } - } catch (Exception $e) { - $this->markTestSkipped( - 'This test cannot be executed, because the utilized ' . - 'LocationService::loadLocation() failed with ' . - PHP_EOL . PHP_EOL . - $e->getTraceAsString() - ); - } - } - - /** - * Test for the createUrlAlias() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::createUrlAlias() - */ - public function testCreateUrlAlias() - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - - /* BEGIN: Use Case */ - // $locationId is the ID of an existing location - - $locationService = $repository->getLocationService(); - $urlAliasService = $repository->getURLAliasService(); - - $location = $locationService->loadLocation($locationId); - - $createdUrlAlias = $urlAliasService->createUrlAlias($location, '/Home/My-New-Site', 'eng-US'); - /* END: Use Case */ - - $this->assertInstanceOf( - URLAlias::class, - $createdUrlAlias - ); - - return [$createdUrlAlias, $location->id]; - } - - /** - * Test for the createUrlAlias() method. - * - * @covers \eZ\Publish\API\Repository\URLAliasService::createUrlAlias - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testCreateSameAliasForDifferentLanguage() - { - $repository = $this->getRepository(); - $locationId = $this->generateId('location', 5); - $locationService = $repository->getLocationService(); - $urlAliasService = $repository->getURLAliasService(); - $location = $locationService->loadLocation($locationId); - - $urlAliasService->createUrlAlias($location, '/alias', 'eng-US'); - $updatedAlias = $urlAliasService->createUrlAlias($location, '/alias', 'eng-GB'); - - $this->assertPropertiesCorrect( - [ - 'languageCodes' => ['eng-US', 'eng-GB'], - ], - $updatedAlias - ); - } - - public function testLoad(): void - { - $repository = $this->getRepository(); - - $urlAliasService = $repository->getURLAliasService(); - - // Load URL alias for root location - $loadedUrlAlias = $urlAliasService->load('0-d41d8cd98f00b204e9800998ecf8427e'); - - $this->assertUrlAliasPropertiesSame( - [ - 'type' => URLAlias::LOCATION, - 'destination' => 2, - 'path' => '/', - 'languageCodes' => ['eng-US', 'eng-GB'], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ], - $loadedUrlAlias - ); - } - - /** - * @param array $testData - * - * @depends testCreateUrlAlias - */ - public function testCreateUrlAliasPropertyValues(array $testData) - { - list($createdUrlAlias, $locationId) = $testData; - - $this->assertNotNull($createdUrlAlias->id); - - $this->assertPropertiesCorrect( - [ - 'type' => URLAlias::LOCATION, - 'destination' => $locationId, - 'path' => '/Home/My-New-Site', - 'languageCodes' => ['eng-US'], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => false, - ], - $createdUrlAlias - ); - } - - /** - * Test for the createUrlAlias() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::createUrlAlias($location, $path, $languageCode, $forwarding) - * @depends testCreateUrlAliasPropertyValues - */ - public function testCreateUrlAliasWithForwarding() - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - - /* BEGIN: Use Case */ - // $locationId is the ID of an existing location - - $locationService = $repository->getLocationService(); - $urlAliasService = $repository->getURLAliasService(); - - $location = $locationService->loadLocation($locationId); - - $createdUrlAlias = $urlAliasService->createUrlAlias($location, '/Home/My-New-Site', 'eng-US', true); - /* END: Use Case */ - - $this->assertInstanceOf( - URLAlias::class, - $createdUrlAlias - ); - - return [$createdUrlAlias, $location->id]; - } - - /** - * @param array $testData - * - * @depends testCreateUrlAliasWithForwarding - */ - public function testCreateUrlAliasPropertyValuesWithForwarding(array $testData) - { - list($createdUrlAlias, $locationId) = $testData; - - $this->assertNotNull($createdUrlAlias->id); - - $this->assertPropertiesCorrect( - [ - 'type' => URLAlias::LOCATION, - 'destination' => $locationId, - 'path' => '/Home/My-New-Site', - 'languageCodes' => ['eng-US'], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => true, - ], - $createdUrlAlias - ); - } - - /** - * Test for the createUrlAlias() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::createUrlAlias($location, $path, $languageCode, $forwarding, $alwaysAvailable) - */ - public function testCreateUrlAliasWithAlwaysAvailable() - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - - /* BEGIN: Use Case */ - // $locationId is the ID of an existing location - - $locationService = $repository->getLocationService(); - $urlAliasService = $repository->getURLAliasService(); - - $location = $locationService->loadLocation($locationId); - - $createdUrlAlias = $urlAliasService->createUrlAlias($location, '/Home/My-New-Site', 'eng-US', false, true); - /* END: Use Case */ - - $this->assertInstanceOf( - URLAlias::class, - $createdUrlAlias - ); - - return [$createdUrlAlias, $location->id]; - } - - /** - * @param array $testData - * - * @depends testCreateUrlAliasWithAlwaysAvailable - */ - public function testCreateUrlAliasPropertyValuesWithAlwaysAvailable(array $testData) - { - list($createdUrlAlias, $locationId) = $testData; - - $this->assertNotNull($createdUrlAlias->id); - - $this->assertPropertiesCorrect( - [ - 'type' => URLAlias::LOCATION, - 'destination' => $locationId, - 'path' => '/Home/My-New-Site', - 'languageCodes' => ['eng-US'], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => false, - ], - $createdUrlAlias - ); - } - - /** - * Test for the createUrlAlias() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::createUrlAlias() - */ - public function testCreateUrlAliasThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - - /* BEGIN: Use Case */ - // $locationId is the ID of an existing location - - $locationService = $repository->getLocationService(); - $urlAliasService = $repository->getURLAliasService(); - - $location = $locationService->loadLocation($locationId); - - // Throws InvalidArgumentException, since this path already exists for the - // language - $createdUrlAlias = $urlAliasService->createUrlAlias($location, '/Design/Plain-site', 'eng-US'); - /* END: Use Case */ - } - - /** - * Test for the createGlobalUrlAlias() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::createGlobalUrlAlias() - */ - public function testCreateGlobalUrlAlias() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlAliasService = $repository->getURLAliasService(); - - $createdUrlAlias = $urlAliasService->createGlobalUrlAlias( - 'module:content/search?SearchText=eZ', - '/Home/My-New-Site', - 'eng-US' - ); - /* END: Use Case */ - - $this->assertInstanceOf( - URLAlias::class, - $createdUrlAlias - ); - - return $createdUrlAlias; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\URLAlias - * - * @depends testCreateGlobalUrlAlias - */ - public function testCreateGlobalUrlAliasPropertyValues(URLAlias $createdUrlAlias) - { - $this->assertNotNull($createdUrlAlias->id); - - $this->assertPropertiesCorrect( - [ - 'type' => URLAlias::RESOURCE, - 'destination' => 'content/search?SearchText=eZ', - 'path' => '/Home/My-New-Site', - 'languageCodes' => ['eng-US'], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => false, - ], - $createdUrlAlias - ); - } - - /** - * Test for the createGlobalUrlAlias() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::createGlobalUrlAlias($resource, $path, $languageCode, $forward) - */ - public function testCreateGlobalUrlAliasWithForward() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlAliasService = $repository->getURLAliasService(); - - $createdUrlAlias = $urlAliasService->createGlobalUrlAlias( - 'module:content/search?SearchText=eZ', - '/Home/My-New-Site', - 'eng-US', - true - ); - /* END: Use Case */ - - $this->assertInstanceOf( - URLAlias::class, - $createdUrlAlias - ); - - return $createdUrlAlias; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\URLAlias - * - * @depends testCreateGlobalUrlAliasWithForward - */ - public function testCreateGlobalUrlAliasWithForwardPropertyValues(URLAlias $createdUrlAlias) - { - $this->assertNotNull($createdUrlAlias->id); - - $this->assertPropertiesCorrect( - [ - 'type' => URLAlias::RESOURCE, - 'destination' => 'content/search?SearchText=eZ', - 'path' => '/Home/My-New-Site', - 'languageCodes' => ['eng-US'], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => true, - ], - $createdUrlAlias - ); - } - - /** - * Test for the createGlobalUrlAlias() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::createGlobalUrlAlias($resource, $path, $languageCode, $forwarding, $alwaysAvailable) - */ - public function testCreateGlobalUrlAliasWithAlwaysAvailable() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlAliasService = $repository->getURLAliasService(); - - $createdUrlAlias = $urlAliasService->createGlobalUrlAlias( - 'module:content/search?SearchText=eZ', - '/Home/My-New-Site', - 'eng-US', - false, - true - ); - /* END: Use Case */ - - $this->assertInstanceOf( - URLAlias::class, - $createdUrlAlias - ); - - return $createdUrlAlias; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\URLAlias - * - * @depends testCreateGlobalUrlAliasWithAlwaysAvailable - */ - public function testCreateGlobalUrlAliasWithAlwaysAvailablePropertyValues(URLAlias $createdUrlAlias) - { - $this->assertNotNull($createdUrlAlias->id); - - $this->assertPropertiesCorrect( - [ - 'type' => URLAlias::RESOURCE, - 'destination' => 'content/search?SearchText=eZ', - 'path' => '/Home/My-New-Site', - 'languageCodes' => ['eng-US'], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => false, - ], - $createdUrlAlias - ); - } - - /** - * Test for the createUrlAlias() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::createGlobalUrlAlias($resource, $path, $languageCode, $forwarding, $alwaysAvailable) - */ - public function testCreateGlobalUrlAliasForLocation() - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - $locationService = $repository->getLocationService(); - $location = $locationService->loadLocation($locationId); - - /* BEGIN: Use Case */ - // $locationId is the ID of an existing location - - $urlAliasService = $repository->getURLAliasService(); - - $createdUrlAlias = $urlAliasService->createGlobalUrlAlias( - 'module:content/view/full/' . $locationId, - '/Home/My-New-Site-global', - 'eng-US', - false, - true - ); - /* END: Use Case */ - - $this->assertInstanceOf( - URLAlias::class, - $createdUrlAlias - ); - - return [$createdUrlAlias, $location->id]; - } - - /** - * Test for the createUrlAlias() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::createGlobalUrlAlias($resource, $path, $languageCode, $forwarding, $alwaysAvailable) - */ - public function testCreateGlobalUrlAliasForLocationVariation() - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 5); - $locationService = $repository->getLocationService(); - $location = $locationService->loadLocation($locationId); - - /* BEGIN: Use Case */ - // $locationId is the ID of an existing location - - $urlAliasService = $repository->getURLAliasService(); - - $createdUrlAlias = $urlAliasService->createGlobalUrlAlias( - 'eznode:' . $locationId, - '/Home/My-New-Site-global', - 'eng-US', - false, - true - ); - /* END: Use Case */ - - $this->assertInstanceOf( - URLAlias::class, - $createdUrlAlias - ); - - return [$createdUrlAlias, $location->id]; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\URLAlias - * - * @depends testCreateGlobalUrlAliasForLocation - */ - public function testCreateGlobalUrlAliasForLocationPropertyValues($testData) - { - list($createdUrlAlias, $locationId) = $testData; - - $this->assertNotNull($createdUrlAlias->id); - - $this->assertPropertiesCorrect( - [ - 'type' => URLAlias::LOCATION, - 'destination' => $locationId, - 'path' => '/Home/My-New-Site-global', - 'languageCodes' => ['eng-US'], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => false, - ], - $createdUrlAlias - ); - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\URLAlias - * - * @depends testCreateGlobalUrlAliasForLocationVariation - */ - public function testCreateGlobalUrlAliasForLocationVariationPropertyValues($testData) - { - $this->testCreateGlobalUrlAliasForLocationPropertyValues($testData); - } - - /** - * Test for the createGlobalUrlAlias() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::createGlobalUrlAlias() - */ - public function testCreateGlobalUrlAliasThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlAliasService = $repository->getURLAliasService(); - - // Throws InvalidArgumentException, since this path already exists for the - // language - $createdUrlAlias = $urlAliasService->createGlobalUrlAlias( - 'module:content/search?SearchText=eZ', - '/Design/Plain-site', - 'eng-US' - ); - /* END: Use Case */ - } - - /** - * Test for the listLocationAliases() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::listLocationAliases() - */ - public function testListLocationAliases() - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 12); - - /* BEGIN: Use Case */ - // $locationId contains the ID of an existing Location - $urlAliasService = $repository->getURLAliasService(); - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation($locationId); - - // Create a custom URL alias for $location - $urlAliasService->createUrlAlias($location, '/My/Great-new-Site', 'eng-US'); - - // $loadedAliases will contain an array of custom URLAlias objects - $loadedAliases = $urlAliasService->listLocationAliases($location); - /* END: Use Case */ - - $this->assertIsArray($loadedAliases); - - // Only 1 non-history alias - $this->assertCount(1, $loadedAliases); - - return [$loadedAliases, $location]; - } - - /** - * @param array $testData - * - * @depends testListLocationAliases - */ - public function testListLocationAliasesLoadsCorrectly(array $testData) - { - list($loadedAliases, $location) = $testData; - - foreach ($loadedAliases as $loadedAlias) { - $this->assertInstanceOf( - URLAlias::class, - $loadedAlias - ); - $this->assertEquals( - $location->id, - $loadedAlias->destination - ); - } - } - - /** - * Test for the listLocationAliases() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::listLocationAliases($location, $custom, $languageCode) - */ - public function testListLocationAliasesWithCustomFilter() - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 12); - - /* BEGIN: Use Case */ - // $locationId contains the ID of an existing Location - $urlAliasService = $repository->getURLAliasService(); - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation($locationId); - - // Create a second URL alias for $location, this is a "custom" one - $urlAliasService->createUrlAlias($location, '/My/Great-new-Site', 'ger-DE'); - - // $loadedAliases will contain 1 aliases in eng-US only - $loadedAliases = $urlAliasService->listLocationAliases($location, false, 'eng-US'); - /* END: Use Case */ - - $this->assertIsArray($loadedAliases); - $this->assertCount(1, $loadedAliases); - } - - /** - * Test for the listLocationAliases() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::listLocationAliases($location, $custom) - */ - public function testListLocationAliasesWithLanguageCodeFilter() - { - $repository = $this->getRepository(); - - $locationId = $this->generateId('location', 12); - - /* BEGIN: Use Case */ - // $locationId contains the ID of an existing Location - $urlAliasService = $repository->getURLAliasService(); - $locationService = $repository->getLocationService(); - - $location = $locationService->loadLocation($locationId); - // Create a custom URL alias for $location - $urlAliasService->createUrlAlias($location, '/My/Great-new-Site', 'eng-US'); - - // $loadedAliases will contain only 1 of 3 aliases (custom in eng-US) - $loadedAliases = $urlAliasService->listLocationAliases($location, true, 'eng-US'); - /* END: Use Case */ - - $this->assertIsArray($loadedAliases); - $this->assertCount(1, $loadedAliases); - } - - /** - * Test for the listGlobalAliases() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::listGlobalAliases() - */ - public function testListGlobalAliases() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlAliasService = $repository->getURLAliasService(); - - // Create some global aliases - $this->createGlobalAliases(); - - // $loadedAliases will contain all 3 global aliases - $loadedAliases = $urlAliasService->listGlobalAliases(); - /* END: Use Case */ - - $this->assertIsArray($loadedAliases); - $this->assertCount(3, $loadedAliases); - } - - /** - * Creates 3 global aliases. - */ - private function createGlobalAliases() - { - $repository = $this->getRepository(); - $urlAliasService = $repository->getURLAliasService(); - - /* BEGIN: Inline */ - $urlAliasService->createGlobalUrlAlias( - 'module:content/search?SearchText=eZ', - '/My/Special-Support', - 'eng-US' - ); - $urlAliasService->createGlobalUrlAlias( - 'module:content/search?SearchText=eZ', - '/My/London-Office', - 'eng-GB' - ); - $urlAliasService->createGlobalUrlAlias( - 'module:content/search?SearchText=Sindelfingen', - '/My/Fancy-Site', - 'eng-US' - ); - /* END: Inline */ - } - - /** - * Test for the listGlobalAliases() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::listGlobalAliases($languageCode) - */ - public function testListGlobalAliasesWithLanguageFilter() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlAliasService = $repository->getURLAliasService(); - - // Create some global aliases - $this->createGlobalAliases(); - - // $loadedAliases will contain only 2 of 3 global aliases - $loadedAliases = $urlAliasService->listGlobalAliases('eng-US'); - /* END: Use Case */ - - $this->assertIsArray($loadedAliases); - $this->assertCount(2, $loadedAliases); - } - - /** - * Test for the listGlobalAliases() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::listGlobalAliases($languageCode, $offset) - */ - public function testListGlobalAliasesWithOffset() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlAliasService = $repository->getURLAliasService(); - - // Create some global aliases - $this->createGlobalAliases(); - - // $loadedAliases will contain only 2 of 3 global aliases - $loadedAliases = $urlAliasService->listGlobalAliases(null, 1); - /* END: Use Case */ - - $this->assertIsArray($loadedAliases); - $this->assertCount(2, $loadedAliases); - } - - /** - * Test for the listGlobalAliases() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::listGlobalAliases($languageCode, $offset, $limit) - */ - public function testListGlobalAliasesWithLimit() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlAliasService = $repository->getURLAliasService(); - - // Create some global aliases - $this->createGlobalAliases(); - - // $loadedAliases will contain only 1 of 3 global aliases - $loadedAliases = $urlAliasService->listGlobalAliases(null, 0, 1); - /* END: Use Case */ - - $this->assertIsArray($loadedAliases); - $this->assertCount(1, $loadedAliases); - } - - /** - * Test for the removeAliases() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::removeAliases() - */ - public function testRemoveAliases() - { - $repository = $this->getRepository(); - - $locationService = $repository->getLocationService(); - $someLocation = $locationService->loadLocation( - $this->generateId('location', 12) - ); - - /* BEGIN: Use Case */ - // $someLocation contains a location with automatically generated - // aliases assigned - $urlAliasService = $repository->getURLAliasService(); - - $initialAliases = $urlAliasService->listLocationAliases($someLocation); - - // Creates a custom alias for $someLocation - $urlAliasService->createUrlAlias( - $someLocation, - '/my/fancy/url/alias/sindelfingen', - 'eng-US' - ); - - $customAliases = $urlAliasService->listLocationAliases($someLocation); - - // The custom alias just created will be removed - // the automatic aliases stay in tact - $urlAliasService->removeAliases($customAliases); - /* END: Use Case */ - - $this->assertEquals( - $initialAliases, - $urlAliasService->listLocationAliases($someLocation) - ); - } - - /** - * Test for the removeAliases() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::removeAliases() - */ - public function testRemoveAliasesThrowsInvalidArgumentExceptionIfAutogeneratedAliasesAreToBeRemoved() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $locationService = $repository->getLocationService(); - $someLocation = $locationService->loadLocation( - $this->generateId('location', 12) - ); - - /* BEGIN: Use Case */ - // $someLocation contains a location with automatically generated - // aliases assigned - $urlAliasService = $repository->getURLAliasService(); - - $autogeneratedAliases = $urlAliasService->listLocationAliases($someLocation, false); - - // Throws an InvalidArgumentException, since autogeneratedAliases - // cannot be removed with this method - $urlAliasService->removeAliases($autogeneratedAliases); - /* END: Use Case */ - } - - /** - * Test for the lookUp() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::lookUp() - */ - public function testLookUp() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlAliasService = $repository->getURLAliasService(); - - $loadedAlias = $urlAliasService->lookup('/Setup2'); - /* END: Use Case */ - - $this->assertInstanceOf( - URLAlias::class, - $loadedAlias - ); - - return $loadedAlias; - } - - /** - * Test for the lookUp() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::lookUp($url, $languageCode) - */ - public function testLookUpWithLanguageFilter() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlAliasService = $repository->getURLAliasService(); - - // Create aliases in multiple languages - $this->createGlobalAliases(); - - $loadedAlias = $urlAliasService->lookup('/My/Special-Support', 'eng-US'); - /* END: Use Case */ - - $this->assertInstanceOf( - URLAlias::class, - $loadedAlias - ); - $this->assertEquals( - 'content/search?SearchText=eZ', - $loadedAlias->destination - ); - } - - /** - * Test for the lookUp() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::lookUp() - */ - public function testLookUpThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlAliasService = $repository->getURLAliasService(); - - // Throws NotFoundException - $urlAliasService->lookup('/non-existent-url'); - /* END: Use Case */ - } - - /** - * Test for the lookUp() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::lookUp($url, $languageCode) - */ - public function testLookUpThrowsNotFoundExceptionWithLanguageFilter() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlAliasService = $repository->getURLAliasService(); - - // Throws NotFoundException - $urlAliasService->lookup('/Contact-Us', 'ger-DE'); - /* END: Use Case */ - } - - /** - * Test for the lookUp() method. - * - * @see \eZ\Publish\API\Repository\URLAliasService::lookUp($url, $languageCode) - */ - public function testLookUpThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlAliasService = $repository->getURLAliasService(); - - // Throws InvalidArgumentException - $loadedAlias = $urlAliasService->lookup(str_repeat('/1', 99), 'ger-DE'); - /* END: Use Case */ - } - - /** - * Test for the lookUp() method after renaming parent which is a part of the lookup path. - * - * @see https://jira.ez.no/browse/EZP-28046 - * @covers \eZ\Publish\API\Repository\URLAliasService::lookUp - * @covers \eZ\Publish\API\Repository\URLAliasService::listLocationAliases - */ - public function testLookupOnRenamedParent() - { - $urlAliasService = $this->getRepository()->getURLAliasService(); - $locationService = $this->getRepository()->getLocationService(); - $contentTypeService = $this->getRepository()->getContentTypeService(); - $contentService = $this->getRepository()->getContentService(); - - // 1. Create new container object (e.g. Folder "My Folder"). - $folderContentType = $contentTypeService->loadContentTypeByIdentifier('folder'); - $folderCreateStruct = $contentService->newContentCreateStruct($folderContentType, 'eng-GB'); - $folderCreateStruct->setField('name', 'My-Folder'); - - $folderDraft = $contentService->createContent($folderCreateStruct, [ - $locationService->newLocationCreateStruct(2), - ]); - - $folder = $contentService->publishVersion($folderDraft->versionInfo); - - // 2. Create new object inside this container (e.g. article "My Article"). - $folderLocation = $locationService->loadLocation($folder->contentInfo->mainLocationId); - - $articleContentType = $contentTypeService->loadContentTypeByIdentifier('article'); - $articleCreateStruct = $contentService->newContentCreateStruct($articleContentType, 'eng-GB'); - $articleCreateStruct->setField('title', 'My Article'); - $article = $contentService->publishVersion( - $contentService->createContent($articleCreateStruct, [ - $locationService->newLocationCreateStruct($folderLocation->id), - ])->versionInfo - ); - $articleLocation = $locationService->loadLocation($article->contentInfo->mainLocationId); - - // 3. Navigate to both of them - $urlAliasService->lookup('/My-Folder'); - $urlAliasService->listLocationAliases($folderLocation, false); - $urlAliasService->lookup('/My-Folder/My-Article'); - $urlAliasService->listLocationAliases($articleLocation, false); - - // 4. Rename "My Folder" to "My Folder Modified". - $folderDraft = $contentService->createContentDraft($folder->contentInfo); - $folderUpdateStruct = $contentService->newContentUpdateStruct(); - $folderUpdateStruct->setField('name', 'My Folder Modified'); - - $contentService->publishVersion( - $contentService->updateContent($folderDraft->versionInfo, $folderUpdateStruct)->versionInfo - ); - - // 5. Navigate to "Article" - $urlAliasService->lookup('/My-Folder/My-Article'); - $aliases = $urlAliasService->listLocationAliases($articleLocation, false); - - $this->assertEquals('/My-Folder-Modified/My-Article', $aliases[0]->path); - } - - /** - * Test lookup on multilingual nested Locations returns proper UrlAlias Value. - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testLookupOnMultilingualNestedLocations() - { - $urlAliasService = $this->getRepository()->getURLAliasService(); - $locationService = $this->getRepository()->getLocationService(); - - $topFolderNames = [ - 'eng-GB' => 'My folder Name', - 'ger-DE' => 'Ger folder Name', - 'eng-US' => 'My folder Name', - ]; - $nestedFolderNames = [ - 'eng-GB' => 'nested Folder name', - 'ger-DE' => 'Ger Nested folder Name', - 'eng-US' => 'nested Folder name', - ]; - $topFolderLocation = $locationService->loadLocation( - $this->createFolder($topFolderNames, 2)->contentInfo->mainLocationId - ); - $nestedFolderLocation = $locationService->loadLocation( - $this->createFolder( - $nestedFolderNames, - $topFolderLocation->id - )->contentInfo->mainLocationId - ); - $urlAlias = $urlAliasService->lookup('/My-Folder-Name/Nested-Folder-Name'); - self::assertPropertiesCorrect( - [ - 'destination' => $nestedFolderLocation->id, - 'path' => '/My-folder-Name/nested-Folder-name', - 'languageCodes' => ['eng-US', 'eng-GB'], - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ], - $urlAlias - ); - $urlAlias = $urlAliasService->lookup('/Ger-Folder-Name/Ger-Nested-Folder-Name'); - self::assertPropertiesCorrect( - [ - 'destination' => $nestedFolderLocation->id, - 'path' => '/Ger-folder-Name/Ger-Nested-folder-Name', - 'languageCodes' => ['ger-DE'], - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ], - $urlAlias - ); - - return [$topFolderLocation, $nestedFolderLocation]; - } - - /** - * Test refreshSystemUrlAliasesForLocation historizes and changes current URL alias after - * changing SlugConverter configuration. - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \ErrorException - */ - public function testRefreshSystemUrlAliasesForLocationWithChangedSlugConverterConfiguration() - { - list($topFolderLocation, $nestedFolderLocation) = $this->testLookupOnMultilingualNestedLocations(); - - $urlAliasService = $this->getRepository(false)->getURLAliasService(); - - $this->changeSlugConverterConfiguration('transformation', 'urlalias_compat'); - $this->changeSlugConverterConfiguration('wordSeparatorName', 'underscore'); - - try { - $urlAliasService->refreshSystemUrlAliasesForLocation($topFolderLocation); - $urlAliasService->refreshSystemUrlAliasesForLocation($nestedFolderLocation); - - $urlAlias = $urlAliasService->lookup('/My-Folder-Name/Nested-Folder-Name'); - $this->assertUrlAliasPropertiesCorrect( - $nestedFolderLocation, - '/My-folder-Name/nested-Folder-name', - ['eng-US', 'eng-GB'], - true, - $urlAlias - ); - - $urlAlias = $urlAliasService->lookup('/my_folder_name/nested_folder_name'); - $this->assertUrlAliasPropertiesCorrect( - $nestedFolderLocation, - '/my_folder_name/nested_folder_name', - ['eng-US', 'eng-GB'], - false, - $urlAlias - ); - - $urlAlias = $urlAliasService->lookup('/Ger-Folder-Name/Ger-Nested-Folder-Name'); - $this->assertUrlAliasPropertiesCorrect( - $nestedFolderLocation, - '/Ger-folder-Name/Ger-Nested-folder-Name', - ['ger-DE'], - true, - $urlAlias - ); - - $urlAlias = $urlAliasService->lookup('/ger_folder_name/ger_nested_folder_name'); - $this->assertUrlAliasPropertiesCorrect( - $nestedFolderLocation, - '/ger_folder_name/ger_nested_folder_name', - ['ger-DE'], - false, - $urlAlias - ); - } finally { - // restore configuration - $this->changeSlugConverterConfiguration('transformation', 'urlalias'); - $this->changeSlugConverterConfiguration('wordSeparatorName', 'dash'); - } - } - - /** - * Test that URL aliases are refreshed after changing URL alias schema Field name of a Content Type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testRefreshSystemUrlAliasesForContentsWithUpdatedContentTypes() - { - list($topFolderLocation, $nestedFolderLocation) = $this->testLookupOnMultilingualNestedLocations(); - /** @var \eZ\Publish\API\Repository\Values\Content\Location $topFolderLocation */ - /** @var \eZ\Publish\API\Repository\Values\Content\Location $nestedFolderLocation */ - - // Default URL Alias schema is <short_name|name> which messes up this test, so: - $this->changeContentTypeUrlAliasSchema('folder', '<name>'); - - $urlAliasService = $this->getRepository(false)->getURLAliasService(); - - $this->updateContentField( - $topFolderLocation->getContentInfo(), - 'short_name', - ['eng-GB' => 'EN Short Name', 'ger-DE' => 'DE Short Name'] - ); - $this->updateContentField( - $nestedFolderLocation->getContentInfo(), - 'short_name', - ['eng-GB' => 'EN Nested Short Name', 'ger-DE' => 'DE Nested Short Name'] - ); - - $this->changeContentTypeUrlAliasSchema('folder', '<short_name>'); - - // sanity test, done after updating CT, because it does not update existing entries by design - $this->assertUrlIsCurrent('/My-folder-Name', $topFolderLocation->id); - $this->assertUrlIsCurrent('/Ger-folder-Name', $topFolderLocation->id); - $this->assertUrlIsCurrent('/My-folder-Name/nested-Folder-name', $nestedFolderLocation->id); - $this->assertUrlIsCurrent('/Ger-folder-Name/Ger-Nested-folder-Name', $nestedFolderLocation->id); - - // Call API being tested - $urlAliasService->refreshSystemUrlAliasesForLocation($topFolderLocation); - $urlAliasService->refreshSystemUrlAliasesForLocation($nestedFolderLocation); - - // check archived aliases - $this->assertUrlIsHistory('/My-folder-Name', $topFolderLocation->id); - $this->assertUrlIsHistory('/Ger-folder-Name', $topFolderLocation->id); - $this->assertUrlIsHistory('/My-folder-Name/nested-Folder-name', $nestedFolderLocation->id); - $this->assertUrlIsHistory('/Ger-folder-Name/Ger-Nested-folder-Name', $nestedFolderLocation->id); - - // check new current aliases - $this->assertUrlIsCurrent('/EN-Short-Name', $topFolderLocation->id); - $this->assertUrlIsCurrent('/DE-Short-Name', $topFolderLocation->id); - $this->assertUrlIsCurrent('/EN-Short-Name/EN-Nested-Short-Name', $nestedFolderLocation->id); - $this->assertUrlIsCurrent('/DE-Short-Name/DE-Nested-Short-Name', $nestedFolderLocation->id); - } - - /** - * Test that created non-latin aliases are non-empty and unique. - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testCreateNonLatinNonEmptyUniqueAliases() - { - $repository = $this->getRepository(); - $urlAliasService = $repository->getURLAliasService(); - $locationService = $repository->getLocationService(); - - $folderNames = [ - 'eng-GB' => 'ひらがな', - ]; - - $folderLocation1 = $locationService->loadLocation( - $this->createFolder($folderNames, 2)->contentInfo->mainLocationId - ); - $urlAlias1 = $urlAliasService->lookup('/1'); - self::assertPropertiesCorrect( - [ - 'destination' => $folderLocation1->id, - 'path' => '/1', - 'languageCodes' => ['eng-GB'], - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ], - $urlAlias1 - ); - - $folderLocation2 = $locationService->loadLocation( - $this->createFolder($folderNames, 2)->contentInfo->mainLocationId - ); - $urlAlias2 = $urlAliasService->lookup('/2'); - self::assertPropertiesCorrect( - [ - 'destination' => $folderLocation2->id, - 'path' => '/2', - 'languageCodes' => ['eng-GB'], - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ], - $urlAlias2 - ); - } - - /** - * Test restoring missing current URL which has existing history. - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \Exception - */ - public function testRefreshSystemUrlAliasesForMissingUrlWithHistory() - { - $repository = $this->getRepository(); - $urlAliasService = $repository->getURLAliasService(); - $locationService = $repository->getLocationService(); - - $folderNames = ['eng-GB' => 'My folder Name']; - $folder = $this->createFolder($folderNames, 2); - $folderLocation = $locationService->loadLocation($folder->contentInfo->mainLocationId); - $nestedFolder = $this->createFolder(['eng-GB' => 'Nested folder'], $folderLocation->id); - $nestedFolderLocation = $locationService->loadLocation($nestedFolder->contentInfo->mainLocationId); - - $folder = $this->updateContentField( - $folder->contentInfo, - 'name', - ['eng-GB' => 'Updated Name'] - ); - // create more historical entries - $this->updateContentField( - $folder->contentInfo, - 'name', - ['eng-GB' => 'Updated Again Name'] - ); - // create historical entry for nested folder - $this->updateContentField( - $nestedFolder->contentInfo, - 'name', - ['eng-GB' => 'Updated Nested folder'] - ); - - // perform sanity check - $this->assertUrlIsHistory('/My-folder-Name', $folderLocation->id); - $this->assertUrlIsHistory('/Updated-Name', $folderLocation->id); - $this->assertUrlIsHistory('/My-folder-Name/Nested-folder', $nestedFolderLocation->id); - $this->assertUrlIsHistory('/Updated-Name/Nested-folder', $nestedFolderLocation->id); - $this->assertUrlIsHistory('/Updated-Again-Name/Nested-folder', $nestedFolderLocation->id); - - $this->assertUrlIsCurrent('/Updated-Again-Name', $folderLocation->id); - $this->assertUrlIsCurrent('/Updated-Again-Name/Updated-Nested-folder', $nestedFolderLocation->id); - - self::assertNotEmpty($urlAliasService->listLocationAliases($folderLocation, false)); - - // corrupt database by removing original entry, keeping its history - $this->performRawDatabaseOperation( - static function (Connection $connection) use ($folderLocation) { - $queryBuilder = $connection->createQueryBuilder(); - $expr = $queryBuilder->expr(); - $queryBuilder - ->delete('ezurlalias_ml') - ->where( - $expr->andX( - $expr->eq( - 'action', - $queryBuilder->createPositionalParameter( - "eznode:{$folderLocation->id}" - ) - ), - $expr->eq( - 'is_original', - $queryBuilder->createPositionalParameter(1) - ) - ) - ); - - return $queryBuilder->execute(); - } - ); - - // perform sanity check - self::assertEmpty($urlAliasService->listLocationAliases($folderLocation, false)); - - // Begin the actual test - $urlAliasService->refreshSystemUrlAliasesForLocation($folderLocation); - $urlAliasService->refreshSystemUrlAliasesForLocation($nestedFolderLocation); - - // make sure there is no corrupted data that could affect the test - $urlAliasService->deleteCorruptedUrlAliases(); - - // test if history was restored - $this->assertUrlIsHistory('/My-folder-Name', $folderLocation->id); - $this->assertUrlIsHistory('/Updated-Name', $folderLocation->id); - $this->assertUrlIsHistory('/My-folder-Name/Nested-folder', $nestedFolderLocation->id); - $this->assertUrlIsHistory('/Updated-Name/Nested-folder', $nestedFolderLocation->id); - $this->assertUrlIsHistory('/Updated-Again-Name/Nested-folder', $nestedFolderLocation->id); - - $this->assertUrlIsCurrent('/Updated-Again-Name', $folderLocation->id); - $this->assertUrlIsCurrent('/Updated-Again-Name/Updated-Nested-folder', $nestedFolderLocation->id); - } - - /** - * Test edge case when updated and archived entry gets moved to another subtree. - * - * @see https://jira.ez.no/browse/EZP-30004 - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \Exception - */ - public function testRefreshSystemUrlAliasesForMovedLocation() - { - $repository = $this->getRepository(); - $urlAliasService = $repository->getURLAliasService(); - $locationService = $repository->getLocationService(); - - $folderNames = ['eng-GB' => 'folder']; - $folder = $this->createFolder($folderNames, 2); - $nestedFolder = $this->createFolder($folderNames, $folder->contentInfo->mainLocationId); - - $nestedFolder = $this->updateContentField( - $nestedFolder->contentInfo, - 'name', - ['eng-GB' => 'folder2'] - ); - - $nestedFolderLocation = $locationService->loadLocation( - $nestedFolder->contentInfo->mainLocationId - ); - $rootLocation = $locationService->loadLocation(2); - - $locationService->moveSubtree($nestedFolderLocation, $rootLocation); - // reload nested Location to get proper parent information - $nestedFolderLocation = $locationService->loadLocation($nestedFolderLocation->id); - - // corrupt database by breaking link to the original URL alias - $this->performRawDatabaseOperation( - static function (Connection $connection) use ($nestedFolderLocation) { - $queryBuilder = $connection->createQueryBuilder(); - $expr = $queryBuilder->expr(); - $queryBuilder - ->update('ezurlalias_ml') - ->set('link', $queryBuilder->createPositionalParameter(666, \PDO::PARAM_INT)) - ->where( - $expr->eq( - 'action', - $queryBuilder->createPositionalParameter( - "eznode:{$nestedFolderLocation->id}" - ) - ) - ) - ->andWhere( - $expr->eq( - 'is_original', - $queryBuilder->createPositionalParameter(0, \PDO::PARAM_INT) - ) - ) - ->andWhere( - $expr->eq('text', $queryBuilder->createPositionalParameter('folder')) - ) - ; - - return $queryBuilder->execute(); - } - ); - - $urlAliasService->refreshSystemUrlAliasesForLocation($nestedFolderLocation); - } - - public function testOverrideHistoryUrlAliasAtTheSameLocation(): void - { - $repository = $this->getRepository(); - $urlAliasService = $repository->getURLAliasService(); - $locationService = $repository->getLocationService(); - - $folderNames = ['eng-GB' => 'foo']; - $folder = $this->createFolder($folderNames, 2); - $destinationFolder = $this->createFolder($folderNames, 2); - - $location = $locationService->loadLocation($folder->contentInfo->mainLocationId); - $destinationFolderLocation = $locationService->loadLocation($destinationFolder->contentInfo->mainLocationId); - - $locationService->moveSubtree($location, $destinationFolderLocation); - - $urlAliasService->lookup('foo'); - $urlAliasService->lookup('foo2/foo'); - - $newFolder = ['eng-GB' => 'foo']; - $this->createFolder($newFolder, 2); - - $newAlias = $urlAliasService->lookup('foo'); - - self::assertFalse($newAlias->isHistory); - } - - /** - * Lookup given URL and check if it is archived and points to the given Location Id. - * - * @param string $lookupUrl - * @param int $expectedDestination Expected Location ID - */ - protected function assertUrlIsHistory($lookupUrl, $expectedDestination) - { - $this->assertLookupHistory(true, $expectedDestination, $lookupUrl); - } - - /** - * Lookup given URL and check if it is current (not archived) and points to the given Location Id. - * - * @param string $lookupUrl - * @param int $expectedDestination Expected Location ID - */ - protected function assertUrlIsCurrent($lookupUrl, $expectedDestination) - { - $this->assertLookupHistory(false, $expectedDestination, $lookupUrl); - } - - /** - * Lookup and URLAlias VO history and destination properties. - * - * @see assertUrlIsHistory - * @see assertUrlIsCurrent - * - * @param bool $expectedIsHistory - * @param int $expectedDestination Expected Location ID - * @param string $lookupUrl - */ - protected function assertLookupHistory($expectedIsHistory, $expectedDestination, $lookupUrl) - { - $urlAliasService = $this->getRepository(false)->getURLAliasService(); - - try { - $urlAlias = $urlAliasService->lookup($lookupUrl); - self::assertPropertiesCorrect( - [ - 'destination' => $expectedDestination, - 'path' => $lookupUrl, - 'isHistory' => $expectedIsHistory, - ], - $urlAlias - ); - } catch (InvalidArgumentException $e) { - self::fail("Failed to lookup {$lookupUrl}: $e"); - } catch (NotFoundException $e) { - self::fail("Failed to lookup {$lookupUrl}: $e"); - } - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param $fieldDefinitionIdentifier - * @param array $fieldValues - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - protected function updateContentField(ContentInfo $contentInfo, $fieldDefinitionIdentifier, array $fieldValues) - { - $contentService = $this->getRepository(false)->getContentService(); - - $contentUpdateStruct = $contentService->newContentUpdateStruct(); - foreach ($fieldValues as $languageCode => $fieldValue) { - $contentUpdateStruct->setField($fieldDefinitionIdentifier, $fieldValue, $languageCode); - } - $contentDraft = $contentService->updateContent( - $contentService->createContentDraft($contentInfo)->versionInfo, - $contentUpdateStruct - ); - - return $contentService->publishVersion($contentDraft->versionInfo); - } - - /** - * Test deleting corrupted URL aliases. - * - * Note: this test will not be needed once we introduce Improved Storage with Foreign keys support. - * - * Note: test depends on already broken URL aliases: eznode:59, eznode:59, eznode:60. - * - * @throws \ErrorException - */ - public function testDeleteCorruptedUrlAliases() - { - $repository = $this->getRepository(); - $urlAliasService = $repository->getURLAliasService(); - $connection = $this->getRawDatabaseConnection(); - - $query = $connection->createQueryBuilder()->select('*')->from('ezurlalias_ml'); - $originalRows = $query->execute()->fetchAll(PDO::FETCH_ASSOC); - - $expectedCount = count($originalRows); - $expectedCount += $this->insertBrokenUrlAliasTableFixtures($connection); - - // sanity check - $updatedRows = $query->execute()->fetchAll(PDO::FETCH_ASSOC); - self::assertCount($expectedCount, $updatedRows, 'Found unexpected number of new rows'); - - // BEGIN API use case - $urlAliasService->deleteCorruptedUrlAliases(); - // END API use case - - $updatedRows = $query->execute()->fetchAll(PDO::FETCH_ASSOC); - self::assertCount( - // API should also remove already broken pre-existing URL aliases - count($originalRows) - 4, - $updatedRows, - 'Number of rows after cleanup is not the same as the original number of rows' - ); - } - - /** - * Mutate 'ezpublish.persistence.slug_converter' Service configuration. - * - * @param string $key - * @param string $value - * - * @throws \ErrorException - * @throws \Exception - */ - protected function changeSlugConverterConfiguration($key, $value) - { - $testSlugConverter = $this - ->getSetupFactory() - ->getServiceContainer() - ->getInnerContainer() - ->get('ezpublish.persistence.slug_converter'); - - if (!$testSlugConverter instanceof TestSlugConverter) { - throw new RuntimeException( - sprintf( - '%s: expected instance of %s, got %s', - __METHOD__, - TestSlugConverter::class, - get_class($testSlugConverter) - ) - ); - } - - $testSlugConverter->setConfigurationValue($key, $value); - } - - /** - * Update Content Type URL alias schema pattern. - * - * @param string $contentTypeIdentifier - * @param string $newUrlAliasSchema - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - protected function changeContentTypeUrlAliasSchema($contentTypeIdentifier, $newUrlAliasSchema) - { - $contentTypeService = $this->getRepository(false)->getContentTypeService(); - - $contentType = $contentTypeService->loadContentTypeByIdentifier($contentTypeIdentifier); - - $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); - $contentTypeUpdateStruct = $contentTypeService->newContentTypeUpdateStruct(); - $contentTypeUpdateStruct->urlAliasSchema = $newUrlAliasSchema; - - $contentTypeService->updateContentTypeDraft($contentTypeDraft, $contentTypeUpdateStruct); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - } - - private function assertUrlAliasPropertiesSame(array $expectedValues, URLAlias $urlAlias): void - { - $this->assertSame( - $expectedValues, - [ - 'type' => $urlAlias->type, - 'destination' => $urlAlias->destination, - 'path' => $urlAlias->path, - 'languageCodes' => $urlAlias->languageCodes, - 'alwaysAvailable' => $urlAlias->alwaysAvailable, - 'isHistory' => $urlAlias->isHistory, - 'isCustom' => $urlAlias->isCustom, - 'forward' => $urlAlias->forward, - ] - ); - } - - private function assertUrlAliasPropertiesCorrect( - Location $expectedDestinationLocation, - $expectedPath, - array $expectedLanguageCodes, - $expectedIsHistory, - URLAlias $actualUrlAliasValue - ) { - self::assertPropertiesCorrect( - [ - 'destination' => $expectedDestinationLocation->id, - 'path' => $expectedPath, - // @todo uncomment after fixing EZP-27124 - //'languageCodes' => $expectedLanguageCodes, - 'isHistory' => $expectedIsHistory, - 'isCustom' => false, - 'forward' => false, - ], - $actualUrlAliasValue - ); - } - - /** - * Insert intentionally broken rows into ezurlalias_ml table to test cleanup API. - * - * @see \eZ\Publish\API\Repository\URLAliasService::deleteCorruptedUrlAliases - * @see testDeleteCorruptedUrlAliases - * - * @param \Doctrine\DBAL\Connection $connection - * - * @return int Number of new rows - */ - private function insertBrokenUrlAliasTableFixtures(Connection $connection) - { - $rows = [ - // link to non-existent location - [ - 'action' => 'eznode:9999', - 'action_type' => 'eznode', - 'alias_redirects' => 0, - 'id' => 9997, - 'is_alias' => 0, - 'is_original' => 1, - 'lang_mask' => 3, - 'link' => 9997, - 'parent' => 0, - 'text' => 'my-location', - 'text_md5' => '19d12b1b9994619cd8e90f00a6f5834e', - ], - // link to non-existent target URL alias (`link` column) - [ - 'action' => 'nop:', - 'action_type' => 'nop', - 'alias_redirects' => 0, - 'id' => 9998, - 'is_alias' => 1, - 'is_original' => 1, - 'lang_mask' => 2, - 'link' => 9995, - 'parent' => 0, - 'text' => 'my-alias1', - 'text_md5' => 'a29dd95ccf4c1bc7ebbd61086863b632', - ], - // link to non-existent parent URL alias - [ - 'action' => 'nop:', - 'action_type' => 'nop', - 'alias_redirects' => 0, - 'id' => 9999, - 'is_alias' => 0, - 'is_original' => 1, - 'lang_mask' => 3, - 'link' => 9999, - 'parent' => 9995, - 'text' => 'my-alias2', - 'text_md5' => 'e5dea18481e4f86857865d9fc94e4ce9', - ], - ]; - - $query = $connection->createQueryBuilder()->insert('ezurlalias_ml'); - - foreach ($rows as $row) { - foreach ($row as $columnName => $value) { - $row[$columnName] = $query->createNamedParameter($value); - } - $query->values($row); - $query->execute(); - } - - return count($rows); - } -} diff --git a/eZ/Publish/API/Repository/Tests/URLServiceTest.php b/eZ/Publish/API/Repository/Tests/URLServiceTest.php deleted file mode 100644 index 34aa0a4f9d..0000000000 --- a/eZ/Publish/API/Repository/Tests/URLServiceTest.php +++ /dev/null @@ -1,942 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use DateTime; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\API\Repository\Values\URL\Query\SortClause; -use eZ\Publish\API\Repository\Values\URL\URL; -use eZ\Publish\API\Repository\Values\URL\URLQuery; -use eZ\Publish\API\Repository\Values\URL\URLUpdateStruct; -use eZ\Publish\API\Repository\Values\URL\UsageSearchResult; - -/** - * Test case for operations in the UserService using in memory storage. - * - * @see \eZ\Publish\API\Repository\URLService - * @group integration - * @group url - */ -class URLServiceTest extends BaseURLServiceTest -{ - private const TOTAL_URLS_COUNT = 20; - - protected function setUp(): void - { - parent::setUp(); - - $urls = [ - [ - 'name' => 'Twitter', - 'url' => 'https://twitter.com/', - 'published' => true, - 'sectionId' => 1, - ], - [ - 'name' => 'Facebook', - 'url' => 'https://www.facebook.com/', - 'published' => true, - 'sectionId' => 1, - ], - [ - 'name' => 'Google', - 'url' => 'https://www.google.com/', - 'published' => true, - 'sectionId' => 1, - ], - [ - 'name' => 'Vimeo', - 'url' => 'https://vimeo.com/', - 'published' => true, - 'sectionId' => 1, - ], - [ - 'name' => 'Facebook Sharer', - 'url' => 'https://www.facebook.com/sharer.php', - 'published' => true, - 'sectionId' => 1, - ], - [ - 'name' => 'Youtube', - 'url' => 'https://www.youtube.com/', - 'published' => true, - 'sectionId' => 1, - ], - [ - 'name' => 'Googel support', - 'url' => 'https://support.google.com/chrome/answer/95647?hl=es', - 'published' => true, - 'sectionId' => 1, - ], - [ - 'name' => 'Instagram', - 'url' => 'https://instagram.com/', - 'published' => true, - 'sectionId' => 1, - ], - [ - 'name' => 'Discuz', - 'url' => 'https://www.discuz.net/forum.php', - 'published' => true, - 'sectionId' => 1, - ], - [ - 'name' => 'Google calendar', - 'url' => 'https://calendar.google.com/calendar/render', - 'published' => true, - 'sectionId' => 1, - ], - [ - 'name' => 'Wikipedia', - 'url' => 'https://www.wikipedia.org/', - 'published' => true, - 'sectionId' => 1, - ], - [ - 'name' => 'Google Analytics', - 'url' => 'https://www.google.com/analytics/', - 'published' => true, - 'sectionId' => 1, - ], - [ - 'name' => 'nazwa.pl', - 'url' => 'https://www.nazwa.pl/', - 'published' => true, - 'sectionId' => 1, - ], - [ - 'name' => 'Apache', - 'url' => 'https://www.apache.org/', - 'published' => true, - 'sectionId' => 2, - ], - [ - 'name' => 'Nginx', - 'url' => 'https://www.nginx.com/', - 'published' => true, - 'sectionId' => 2, - ], - [ - 'name' => 'Microsoft.com', - 'url' => 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', - 'published' => true, - 'sectionId' => 3, - ], - [ - 'name' => 'Dropbox', - 'url' => 'https://www.dropbox.com/', - 'published' => false, - 'sectionId' => 3, - ], - [ - 'name' => 'Google [DE]', - 'url' => 'https://www.google.de/', - 'published' => true, - 'sectionId' => 3, - ], - ]; - - $repository = $this->getRepository(); - - $parentLocationId = $this->generateId('location', 2); - - $contentService = $repository->getContentService(); - $locationService = $repository->getLocationService(); - - $contentType = $repository->getContentTypeService()->loadContentTypeByIdentifier('url'); - foreach ($urls as $data) { - $struct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); - $struct->setField('name', $data['name']); - $struct->setField('url', $data['url']); - $struct->sectionId = $data['sectionId']; - - $location = $locationService->newLocationCreateStruct($parentLocationId); - - $draft = $contentService->createContent($struct, [$location]); - if ($data['published']) { - $contentService->publishVersion($draft->versionInfo); - } - } - } - - /** - * Test for URLService::findUrls() method. - * - * @see \eZ\Publish\Core\Repository\URLService::findUrls() - */ - public function testFindUrls() - { - $expectedUrls = [ - 'https://www.apache.org/', - 'https://calendar.google.com/calendar/render', - 'https://www.dropbox.com/', - '/content/view/sitemap/2', - 'https://support.google.com/chrome/answer/95647?hl=es', - 'https://www.nazwa.pl/', - 'https://www.facebook.com/sharer.php', - 'https://www.wikipedia.org/', - 'https://www.google.de/', - 'https://www.google.com/', - 'https://www.nginx.com/', - '/content/view/tagcloud/2', - 'https://www.youtube.com/', - 'https://vimeo.com/', - 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', - 'https://twitter.com/', - 'https://www.google.com/analytics/', - 'https://www.facebook.com/', - 'https://www.discuz.net/forum.php', - 'https://instagram.com/', - ]; - - $query = new URLQuery(); - $query->filter = new Criterion\MatchAll(); - - $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); - } - - /** - * Test for URLService::findUrls() method. - * - * @see \eZ\Publish\Core\Repository\URLService::findUrls() - */ - public function testFindUrlsWithoutCounting() - { - $expectedUrls = [ - 'https://www.apache.org/', - 'https://calendar.google.com/calendar/render', - 'https://www.dropbox.com/', - '/content/view/sitemap/2', - 'https://support.google.com/chrome/answer/95647?hl=es', - 'https://www.nazwa.pl/', - 'https://www.facebook.com/sharer.php', - 'https://www.wikipedia.org/', - 'https://www.google.de/', - 'https://www.google.com/', - 'https://www.nginx.com/', - '/content/view/tagcloud/2', - 'https://www.youtube.com/', - 'https://vimeo.com/', - 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', - 'https://twitter.com/', - 'https://www.google.com/analytics/', - 'https://www.facebook.com/', - 'https://www.discuz.net/forum.php', - 'https://instagram.com/', - ]; - - $query = new URLQuery(); - $query->filter = new Criterion\MatchAll(); - $query->performCount = false; - - $this->doTestFindUrls($query, $expectedUrls, null); - } - - /** - * Test for URLService::findUrls() method. - * - * @see \eZ\Publish\Core\Repository\URLService::findUrls() - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - */ - public function testFindUrlsUsingMatchNone() - { - $query = new URLQuery(); - $query->filter = new Criterion\MatchNone(); - - $this->doTestFindUrls($query, [], 0); - } - - /** - * Test for URLService::findUrls() method. - * - * @see \eZ\Publish\Core\Repository\URLService::findUrls() - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - */ - public function testFindUrlsUsingPatternCriterion() - { - $expectedUrls = [ - 'https://www.google.de/', - 'https://www.google.com/', - 'https://support.google.com/chrome/answer/95647?hl=es', - 'https://calendar.google.com/calendar/render', - 'https://www.google.com/analytics/', - ]; - - $query = new URLQuery(); - $query->filter = new Criterion\Pattern('google'); - - $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); - } - - /** - * Test for URLService::findUrls() method. - * - * @see \eZ\Publish\Core\Repository\URLService::findUrls() - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - */ - public function testFindUrlsUsingValidityCriterionValid() - { - $expectedUrls = [ - 'https://www.google.com/', - '/content/view/sitemap/2', - 'https://support.google.com/chrome/answer/95647?hl=es', - 'https://www.google.de/', - 'https://www.nginx.com/', - 'https://www.google.com/analytics/', - 'https://www.discuz.net/forum.php', - 'https://www.wikipedia.org/', - 'https://www.facebook.com/sharer.php', - 'https://twitter.com/', - 'https://www.nazwa.pl/', - 'https://instagram.com/', - 'https://www.apache.org/', - 'https://www.dropbox.com/', - 'https://www.facebook.com/', - 'https://www.youtube.com/', - 'https://calendar.google.com/calendar/render', - 'https://vimeo.com/', - 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', - ]; - - $query = new URLQuery(); - $query->filter = new Criterion\Validity(true); - - $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); - } - - /** - * Test for URLService::findUrls() method. - * - * @covers \eZ\Publish\Core\Repository\URLService::findUrls - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - */ - public function testFindUrlsUsingSectionIdCriterion(): void - { - $expectedUrls = [ - 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', - 'https://www.dropbox.com/', - 'https://www.google.de/', - ]; - - $query = new URLQuery(); - $query->filter = new Criterion\SectionId([3]); - - $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); - } - - /** - * Test for URLService::findUrls() method. - * - * @covers \eZ\Publish\Core\Repository\URLService::findUrls() - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - */ - public function testFindUrlsUsingSectionIdAndValidityCriterionValid(): void - { - $expectedUrls = [ - 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', - 'https://www.dropbox.com/', - 'https://www.google.de/', - ]; - - $query = new URLQuery(); - $query->filter = new Criterion\LogicalAnd([ - new Criterion\SectionId([3]), - new Criterion\Validity(true), - ]); - - $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); - } - - /** - * Test for URLService::findUrls() method. - * - * @covers \eZ\Publish\Core\Repository\URLService::findUrls - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - */ - public function testFindUrlsUsingSectionIdentifierCriterion(): void - { - $expectedUrls = [ - 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', - 'https://www.dropbox.com/', - 'https://www.google.de/', - ]; - - $query = new URLQuery(); - $query->filter = new Criterion\SectionIdentifier(['media']); - - $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); - } - - /** - * Test for URLService::findUrls() method. - * - * @covers \eZ\Publish\Core\Repository\URLService::findUrls() - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - */ - public function testFindUrlsUsingSectionIdentifierAndValidityCriterionValid(): void - { - $expectedUrls = [ - 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', - 'https://www.dropbox.com/', - 'https://www.google.de/', - 'https://www.apache.org/', - 'https://www.nginx.com/', - ]; - - $query = new URLQuery(); - $query->filter = new Criterion\LogicalAnd([ - new Criterion\SectionIdentifier(['media', 'users']), - new Criterion\Validity(true), - ]); - - $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); - } - - /** - * Test for URLService::findUrls() method. - * - * @covers \eZ\Publish\Core\Repository\URLService::findUrls() - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - */ - public function testFindUrlsUsingSectionIdentifierOrSectionIdCriterion(): void - { - $expectedUrls = [ - 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', - 'https://www.dropbox.com/', - 'https://www.google.de/', - 'https://www.apache.org/', - 'https://www.nginx.com/', - ]; - - $query = new URLQuery(); - $query->filter = new Criterion\LogicalOr([ - new Criterion\SectionIdentifier(['media']), - new Criterion\SectionId([2]), - ]); - - $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); - } - - /** - * Test for URLService::findUrls() method. - * - * @see \eZ\Publish\Core\Repository\URLService::findUrls() - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - */ - public function testFindUrlsUsingValidityCriterionInvalid() - { - $expectedUrls = [ - '/content/view/tagcloud/2', - ]; - - $query = new URLQuery(); - $query->filter = new Criterion\Validity(false); - - $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); - } - - /** - * Test for URLService::findUrls() method. - * - * @see \eZ\Publish\Core\Repository\URLService::findUrls() - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - */ - public function testFindUrlsUsingVisibleOnlyCriterion() - { - $expectedUrls = [ - 'https://vimeo.com/', - 'https://calendar.google.com/calendar/render', - 'https://www.facebook.com/', - 'https://www.google.com/', - 'https://www.google.com/analytics/', - 'https://www.facebook.com/sharer.php', - 'https://www.apache.org/', - 'https://www.nginx.com/', - 'https://www.wikipedia.org/', - 'https://www.youtube.com/', - 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', - 'https://www.google.de/', - 'https://instagram.com/', - 'https://www.nazwa.pl/', - '/content/view/tagcloud/2', - 'https://www.discuz.net/forum.php', - 'https://support.google.com/chrome/answer/95647?hl=es', - 'https://twitter.com/', - '/content/view/sitemap/2', - ]; - - $query = new URLQuery(); - $query->filter = new Criterion\VisibleOnly(); - - $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); - } - - /** - * @see https://jira.ez.no/browse/EZP-31059 - */ - public function testFindUrlsUsingVisibleOnlyCriterionReturnsUniqueItems(): void - { - $exampleUrl = 'https://ezplatform.com'; - - $this->createContentWithLink('A', $exampleUrl); - $this->createContentWithLink('B', $exampleUrl); - - $urlService = $this->getRepository()->getURLService(); - - $query = new URLQuery(); - $query->filter = new Criterion\VisibleOnly(); - $query->limit = -1; - - $results = $urlService->findUrls($query); - - $this->assertSearchResultItemsAreUnique($results); - } - - /** - * Test for URLService::findUrls() method. - * - * @see \eZ\Publish\Core\Repository\URLService::findUrls() - */ - public function testFindUrlsWithInvalidOffsetThrowsInvalidArgumentException() - { - $query = new URLQuery(); - $query->filter = new Criterion\MatchAll(); - $query->offset = 'invalid!'; - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlService = $repository->getURLService(); - - $this->expectException(\eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue::class); - $urlService->findUrls($query); - /* END: Use Case */ - } - - /** - * Test for URLService::findUrls() method. - * - * @see \eZ\Publish\Core\Repository\URLService::findUrls() - */ - public function testFindUrlsWithInvalidLimitThrowsInvalidArgumentException() - { - $query = new URLQuery(); - $query->filter = new Criterion\MatchAll(); - $query->limit = 'invalid!'; - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlService = $repository->getURLService(); - - $this->expectException(\eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue::class); - $urlService->findUrls($query); - /* END: Use Case */ - } - - /** - * Test for URLService::findUrls() method. - * - * @see \eZ\Publish\Core\Repository\URLService::findUrls() - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - */ - public function testFindUrlsWithOffset() - { - $expectedUrls = [ - 'https://www.discuz.net/forum.php', - 'https://calendar.google.com/calendar/render', - 'https://www.wikipedia.org/', - 'https://www.google.com/analytics/', - 'https://www.nazwa.pl/', - 'https://www.apache.org/', - 'https://www.nginx.com/', - 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', - 'https://www.dropbox.com/', - 'https://www.google.de/', - ]; - - $query = new URLQuery(); - $query->filter = new Criterion\MatchAll(); - $query->offset = 10; - $query->sortClauses = [new SortClause\Id()]; - - $this->doTestFindUrls($query, $expectedUrls, self::TOTAL_URLS_COUNT); - } - - /** - * Test for URLService::findUrls() method. - * - * @see \eZ\Publish\Core\Repository\URLService::findUrls() - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - */ - public function testFindUrlsWithOffsetAndLimit() - { - $expectedUrls = [ - 'https://www.discuz.net/forum.php', - 'https://calendar.google.com/calendar/render', - 'https://www.wikipedia.org/', - ]; - - $query = new URLQuery(); - $query->filter = new Criterion\MatchAll(); - $query->offset = 10; - $query->limit = 3; - $query->sortClauses = [new SortClause\Id()]; - - $this->doTestFindUrls($query, $expectedUrls, self::TOTAL_URLS_COUNT); - } - - /** - * Test for URLService::findUrls() method. - * - * @see \eZ\Publish\Core\Repository\URLService::findUrls() - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - */ - public function testFindUrlsWithLimitZero() - { - $query = new URLQuery(); - $query->filter = new Criterion\MatchAll(); - $query->limit = 0; - - $this->doTestFindUrls($query, [], self::TOTAL_URLS_COUNT); - } - - /** - * Test for URLService::findUrls() method. - * - * @see \eZ\Publish\Core\Repository\URLService::findUrls() - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUrls - * @dataProvider dataProviderForFindUrlsWithSorting - */ - public function testFindUrlsWithSorting(SortClause $sortClause, array $expectedUrls) - { - $query = new URLQuery(); - $query->filter = new Criterion\MatchAll(); - $query->sortClauses = [$sortClause]; - - $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls), false); - } - - public function dataProviderForFindUrlsWithSorting() - { - $urlsSortedById = [ - '/content/view/sitemap/2', - '/content/view/tagcloud/2', - 'https://twitter.com/', - 'https://www.facebook.com/', - 'https://www.google.com/', - 'https://vimeo.com/', - 'https://www.facebook.com/sharer.php', - 'https://www.youtube.com/', - 'https://support.google.com/chrome/answer/95647?hl=es', - 'https://instagram.com/', - 'https://www.discuz.net/forum.php', - 'https://calendar.google.com/calendar/render', - 'https://www.wikipedia.org/', - 'https://www.google.com/analytics/', - 'https://www.nazwa.pl/', - 'https://www.apache.org/', - 'https://www.nginx.com/', - 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', - 'https://www.dropbox.com/', - 'https://www.google.de/', - ]; - - $urlsSortedByURL = $urlsSortedById; - sort($urlsSortedByURL); - - return [ - [new SortClause\Id(SortClause::SORT_ASC), $urlsSortedById], - [new SortClause\Id(SortClause::SORT_DESC), array_reverse($urlsSortedById)], - [new SortClause\URL(SortClause::SORT_ASC), $urlsSortedByURL], - [new SortClause\URL(SortClause::SORT_DESC), array_reverse($urlsSortedByURL)], - ]; - } - - /** - * Test for URLService::updateUrl() method. - * - * @see \eZ\Publish\Core\Repository\URLService::updateUrl() - */ - public function testUpdateUrl() - { - $repository = $this->getRepository(); - - $id = $this->generateId('url', 23); - - /* BEGIN: Use Case */ - $urlService = $repository->getURLService(); - - $urlBeforeUpdate = $urlService->loadById($id); - $updateStruct = $urlService->createUpdateStruct(); - $updateStruct->url = 'https://someurl.com/'; - - $urlAfterUpdate = $urlService->updateUrl($urlBeforeUpdate, $updateStruct); - /* END: Use Case */ - - $this->assertInstanceOf(URL::class, $urlAfterUpdate); - $this->assertPropertiesCorrect([ - 'id' => 23, - 'url' => 'https://someurl.com/', - // (!) URL status should be reset to valid nad never checked - 'isValid' => true, - 'lastChecked' => null, - 'created' => new DateTime('@1343140541'), - ], $urlAfterUpdate); - $this->assertGreaterThanOrEqual($urlBeforeUpdate->modified, $urlAfterUpdate->modified); - } - - /** - * Test for URLService::updateUrl() method. - * - * @see \eZ\Publish\Core\Repository\URLService::updateUrl() - */ - public function testUpdateUrlStatus() - { - $repository = $this->getRepository(); - - $id = $this->generateId('url', 23); - $checked = new DateTime('@' . time()); - - /* BEGIN: Use Case */ - $urlService = $repository->getURLService(); - - $urlBeforeUpdate = $urlService->loadById($id); - - $updateStruct = $urlService->createUpdateStruct(); - $updateStruct->isValid = false; - $updateStruct->lastChecked = $checked; - - $urlAfterUpdate = $urlService->updateUrl($urlBeforeUpdate, $updateStruct); - /* END: Use Case */ - - $this->assertInstanceOf(URL::class, $urlAfterUpdate); - $this->assertPropertiesCorrect([ - 'id' => $id, - 'url' => '/content/view/sitemap/2', - // (!) URL status should be reset to valid nad never checked - 'isValid' => false, - 'lastChecked' => $checked, - 'created' => new DateTime('@1343140541'), - ], $urlAfterUpdate); - $this->assertGreaterThanOrEqual($urlBeforeUpdate->modified, $urlAfterUpdate->modified); - } - - /** - * Test for URLService::updateUrl() method. - * - * @see \eZ\Publish\Core\Repository\URLService::updateUrl() - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testUpdateUrl - */ - public function testUpdateUrlWithNonUniqueUrl() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $id = $this->generateId('url', 23); - - /* BEGIN: Use Case */ - $urlService = $repository->getURLService(); - - $urlBeforeUpdate = $urlService->loadById($id); - $updateStruct = $urlService->createUpdateStruct(); - $updateStruct->url = 'https://www.youtube.com/'; - - // This call will fail with a InvalidArgumentException - $urlService->updateUrl($urlBeforeUpdate, $updateStruct); - /* END: Use Case */ - } - - /** - * Test for URLService::loadById() method. - * - * @see \eZ\Publish\Core\Repository\URLService::loadById - */ - public function testLoadById() - { - $repository = $this->getRepository(); - - $id = $this->generateId('url', 23); - - /* BEGIN: Use Case */ - $urlService = $repository->getURLService(); - - $url = $urlService->loadById($id); - /* END: Use Case */ - - $this->assertInstanceOf(URL::class, $url); - $this->assertPropertiesCorrect([ - 'id' => 23, - 'url' => '/content/view/sitemap/2', - 'isValid' => true, - 'lastChecked' => null, - 'created' => new DateTime('@1343140541'), - 'modified' => new DateTime('@1343140541'), - ], $url); - } - - /** - * Test for URLService::loadById() method. - * - * @see \eZ\Publish\Core\Repository\URLService::loadById - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testLoadById - */ - public function testLoadByIdThrowsNotFoundException() - { - $repository = $this->getRepository(); - - $nonExistentUrlId = $this->generateId('url', self::DB_INT_MAX); - /* BEGIN: Use Case */ - $urlService = $repository->getURLService(); - - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - $urlService->loadById($nonExistentUrlId); - /* END: Use Case */ - } - - /** - * Test for URLService::loadByUrl() method. - * - * @see \eZ\Publish\Core\Repository\URLService::loadByUrl - */ - public function testLoadByUrl() - { - $repository = $this->getRepository(); - - $urlAddr = '/content/view/sitemap/2'; - /* BEGIN: Use Case */ - $urlService = $repository->getURLService(); - - $url = $urlService->loadByUrl($urlAddr); - - /* END: Use Case */ - - $this->assertInstanceOf(URL::class, $url); - $this->assertPropertiesCorrect([ - 'id' => 23, - 'url' => '/content/view/sitemap/2', - 'isValid' => true, - 'lastChecked' => null, - 'created' => new DateTime('@1343140541'), - 'modified' => new DateTime('@1343140541'), - ], $url); - } - - /** - * Test for URLService::loadByUrl() method. - * - * @see \eZ\Publish\Core\Repository\URLService::loadByUrl - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testLoadByUrl - */ - public function testLoadByUrlThrowsNotFoundException() - { - $repository = $this->getRepository(); - - $nonExistentUrl = 'https://laravel.com/'; - /* BEGIN: Use Case */ - $urlService = $repository->getURLService(); - - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - $urlService->loadByUrl($nonExistentUrl); - /* END: Use Case */ - } - - /** - * Test for URLService::createUpdateStruct() method. - * - * @see \eZ\Publish\API\Repository\URLService::createUpdateStruct - * - * @return \eZ\Publish\API\Repository\Values\URL\URLUpdateStruct - */ - public function testCreateUpdateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlService = $repository->getURLService(); - $updateStruct = $urlService->createUpdateStruct(); - /* END: Use Case */ - - $this->assertInstanceOf(URLUpdateStruct::class, $updateStruct); - - return $updateStruct; - } - - /** - * Test for URLService::createUpdateStruct() method. - * - * @param \eZ\Publish\API\Repository\Values\URL\URLUpdateStruct $updateStruct - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testCreateUpdateStruct - */ - public function testCreateUpdateStructValues(URLUpdateStruct $updateStruct) - { - $this->assertPropertiesCorrect([ - 'url' => null, - 'isValid' => null, - 'lastChecked' => null, - ], $updateStruct); - } - - /** - * Test for URLService::testFindUsages() method. - * - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testLoadById - * @dataProvider dataProviderForFindUsages - */ - public function testFindUsages($urlId, $offset, $limit, array $expectedContentInfos, $expectedTotalCount = null) - { - $repository = $this->getRepository(); - - $id = $this->generateId('url', $urlId); - /* BEGIN: Use Case */ - $urlService = $repository->getURLService(); - - $loadedUrl = $urlService->loadById($id); - - $usagesSearchResults = $urlService->findUsages($loadedUrl, $offset, $limit); - /* END: Use Case */ - - $this->assertInstanceOf(UsageSearchResult::class, $usagesSearchResults); - $this->assertEquals($expectedTotalCount, $usagesSearchResults->totalCount); - $this->assertUsagesSearchResultItems($usagesSearchResults, $expectedContentInfos); - } - - public function dataProviderForFindUsages() - { - return [ - // findUsages($url, 0, -1) - [23, 0, -1, [54], 1], - // findUsages($url, 0, $limit) - [23, 0, 1, [54], 1], - ]; - } - - /** - * Test for URLService::testFindUsages() method. - * - * @depends eZ\Publish\API\Repository\Tests\URLServiceTest::testFindUsages - */ - public function testFindUsagesReturnsEmptySearchResults() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlService = $repository->getURLService(); - - $loadedUrl = $urlService->loadByUrl('https://www.dropbox.com/'); - - $usagesSearchResults = $urlService->findUsages($loadedUrl); - /* END: Use Case */ - - $this->assertInstanceOf(UsageSearchResult::class, $usagesSearchResults); - $this->assertPropertiesCorrect([ - 'totalCount' => 0, - 'items' => [], - ], $usagesSearchResults); - } -} diff --git a/eZ/Publish/API/Repository/Tests/URLWildcardServiceAuthorizationTest.php b/eZ/Publish/API/Repository/Tests/URLWildcardServiceAuthorizationTest.php deleted file mode 100644 index 243774c01b..0000000000 --- a/eZ/Publish/API/Repository/Tests/URLWildcardServiceAuthorizationTest.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; - -/** - * Test case for operations in the URLWildcardService. - * - * @see eZ\Publish\API\Repository\URLWildcardService - * @group integration - * @group authorization - */ -class URLWildcardServiceAuthorizationTest extends BaseTest -{ - /** - * Test for the create() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\URLWildcard - * - * @see \eZ\Publish\API\Repository\URLWildcardService::create() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testCreate - */ - public function testCreateThrowsUnauthorizedException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); - - $repository = $this->getRepository(); - - $anonymousUserId = $this->generateId('user', 10); - /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ - // Publish demo installation. - - $userService = $repository->getUserService(); - $urlWildcardService = $repository->getURLWildcardService(); - - $repository->getPermissionResolver()->setCurrentUserReference($userService->loadUser($anonymousUserId)); - - $this->expectException(UnauthorizedException::class); - $urlWildcardService->create('/articles/*', '/content/{1}'); - /* END: Use Case */ - } - - /** - * Test for the remove() method. - * - * @see \eZ\Publish\API\Repository\URLWildcardService::remove() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testRemove - */ - public function testRemoveThrowsUnauthorizedException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); - - $repository = $this->getRepository(); - - $anonymousUserId = $this->generateId('user', 10); - /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ - // Publish demo installation. - $userService = $repository->getUserService(); - $urlWildcardService = $repository->getURLWildcardService(); - - // Create a new url wildcard - $urlWildcardId = $urlWildcardService->create('/articles/*', '/content/{1}')->id; - - $repository->getPermissionResolver()->setCurrentUserReference($userService->loadUser($anonymousUserId)); - - // Load newly created url wildcard - $urlWildcard = $urlWildcardService->load($urlWildcardId); - - $this->expectException(UnauthorizedException::class); - $urlWildcardService->remove($urlWildcard); - /* END: Use Case */ - } -} diff --git a/eZ/Publish/API/Repository/Tests/URLWildcardServiceTest.php b/eZ/Publish/API/Repository/Tests/URLWildcardServiceTest.php deleted file mode 100644 index 8e9f47ffa4..0000000000 --- a/eZ/Publish/API/Repository/Tests/URLWildcardServiceTest.php +++ /dev/null @@ -1,558 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use eZ\Publish\API\Repository\Values\Content\URLWildcard; -use eZ\Publish\API\Repository\Values\Content\URLWildcardTranslationResult; -use eZ\Publish\API\Repository\Values\Content\URLWildcardUpdateStruct; - -/** - * Test case for operations in the URLWildcardService. - * - * @see eZ\Publish\API\Repository\URLWildcardService - * @group url-wildcard - */ -class URLWildcardServiceTest extends BaseTest -{ - /** - * Test for the create() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\URLWildcard - * - * @see \eZ\Publish\API\Repository\URLWildcardService::create() - */ - public function testCreate() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // Create a new url wildcard - $urlWildcard = $urlWildcardService->create('/articles/*', '/content/{1}'); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\URLWildcard', - $urlWildcard - ); - - return $urlWildcard; - } - - /** - * Test for the create() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\URLWildcard $urlWildcard - * - * @see \eZ\Publish\API\Repository\URLWildcardService::create() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testCreate - */ - public function testCreateSetsIdPropertyOnURLWildcard(URLWildcard $urlWildcard) - { - $this->assertNotNull($urlWildcard->id); - } - - /** - * Test for the create() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\URLWildcard $urlWildcard - * - * @see \eZ\Publish\API\Repository\URLWildcardService::create() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testCreate - */ - public function testCreateSetsPropertiesOnURLWildcard(URLWildcard $urlWildcard) - { - $this->assertPropertiesCorrect( - [ - 'sourceUrl' => '/articles/*', - 'destinationUrl' => '/content/{1}', - 'forward' => false, - ], - $urlWildcard - ); - } - - /** - * Test for the create() method. - * - * @see \eZ\Publish\API\Repository\URLWildcardService::create() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testCreate - */ - public function testCreateWithOptionalForwardParameter() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // Create a new url wildcard - $urlWildcard = $urlWildcardService->create('/articles/*', '/content/{1}', true); - /* END: Use Case */ - - $this->assertPropertiesCorrect( - [ - 'sourceUrl' => '/articles/*', - 'destinationUrl' => '/content/{1}', - 'forward' => true, - ], - $urlWildcard - ); - } - - /** - * Test for the create() method. - * - * @see \eZ\Publish\API\Repository\URLWildcardService::create() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testCreate - */ - public function testCreateThrowsInvalidArgumentExceptionOnDuplicateSourceUrl() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // Create a new url wildcard - $urlWildcardService->create('/articles/*', '/content/{1}', true); - - // This call will fail with an InvalidArgumentException because the - // sourceUrl '/articles/*' already exists. - $urlWildcardService->create('/articles/*', '/content/data/{1}'); - /* END: Use Case */ - } - - /** - * Test for the create() method. - * - * @see \eZ\Publish\API\Repository\URLWildcardService::create() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testCreate - */ - public function testCreateThrowsContentValidationExceptionWhenPatternsAndPlaceholdersNotMatch() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // This call will fail with a ContentValidationException because the - // number of patterns '*' does not match the number of {\d} placeholders - $urlWildcardService->create('/articles/*', '/content/{1}/year{2}'); - /* END: Use Case */ - } - - /** - * Test for the create() method. - * - * @see \eZ\Publish\API\Repository\URLWildcardService::create() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testCreate - */ - public function testCreateThrowsContentValidationExceptionWhenPlaceholdersNotValidNumberSequence() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // This call will fail with a ContentValidationException because the - // number of patterns '*' does not match the number of {\d} placeholders - $urlWildcardService->create('/articles/*/*/*', '/content/{1}/year/{2}/{4}'); - /* END: Use Case */ - } - - /** - * Test for the load() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\URLWildcard - * - * @see \eZ\Publish\API\Repository\URLWildcardService::load() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testCreate - */ - public function testLoad() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // Create a new url wildcard - $urlWildcardId = $urlWildcardService->create('/articles/*', '/content/{1}', true)->id; - - // Load newly created url wildcard - $urlWildcard = $urlWildcardService->load($urlWildcardId); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\URLWildcard', - $urlWildcard - ); - - return $urlWildcard; - } - - /** - * Test for the load() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\URLWildcard $urlWildcard - * - * @see \eZ\Publish\API\Repository\URLWildcardService::load() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testLoad - */ - public function testLoadSetsPropertiesOnURLWildcard(URLWildcard $urlWildcard) - { - $this->assertPropertiesCorrect( - [ - 'sourceUrl' => '/articles/*', - 'destinationUrl' => '/content/{1}', - 'forward' => true, - ], - $urlWildcard - ); - } - - /** - * Test for the load() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\URLWildcard $urlWildcard - * - * @see \eZ\Publish\API\Repository\URLWildcardService::load() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testLoad - */ - public function testLoadThrowsNotFoundException(URLWildcard $urlWildcard) - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // This call will fail with a NotFoundException - $urlWildcardService->load(42); - /* END: Use Case */ - } - - /** - * @see \eZ\Publish\API\Repository\URLWildcardService::update - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testUpdate(): void - { - $repository = $this->getRepository(); - - $urlWildcardService = $repository->getURLWildcardService(); - - $urlWildcard = $urlWildcardService->create( - '/articles/*', - '/content/{1}', - true - ); - - $updateStruct = new URLWildcardUpdateStruct(); - $updateStruct->sourceUrl = '/articles/new/*'; - $updateStruct->destinationUrl = '/content/new/*'; - $updateStruct->forward = false; - - $urlWildcardService->update($urlWildcard, $updateStruct); - - $urlWildcardUpdated = $urlWildcardService->load($urlWildcard->id); - - $this->assertEquals( - [ - $urlWildcard->id, - $updateStruct->sourceUrl, - $updateStruct->destinationUrl, - $updateStruct->forward, - ], - [ - $urlWildcardUpdated->id, - $urlWildcardUpdated->sourceUrl, - $urlWildcardUpdated->destinationUrl, - $urlWildcardUpdated->forward, - ] - ); - } - - /** - * Test for the remove() method. - * - * @see \eZ\Publish\API\Repository\URLWildcardService::remove() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testLoad - */ - public function testRemove() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // Create a new url wildcard - $urlWildcard = $urlWildcardService->create('/articles/*', '/content/{1}', true); - - // Store wildcard url for later reuse - $urlWildcardId = $urlWildcard->id; - - // Remove the newly created url wildcard - $urlWildcardService->remove($urlWildcard); - - // This call will fail with a NotFoundException - $urlWildcardService->load($urlWildcardId); - /* END: Use Case */ - } - - /** - * Test for the loadAll() method. - * - * @see \eZ\Publish\API\Repository\URLWildcardService::loadAll() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testCreate - */ - public function testLoadAll() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // Create new url wildcards - $urlWildcardOne = $urlWildcardService->create('/articles/*', '/content/{1}', true); - $urlWildcardTwo = $urlWildcardService->create('/news/*', '/content/{1}', true); - - // Load all available url wildcards - $allUrlWildcards = $urlWildcardService->loadAll(); - /* END: Use Case */ - - $this->assertEquals( - [ - $urlWildcardOne, - $urlWildcardTwo, - ], - $allUrlWildcards - ); - } - - /** - * Test for the loadAll() method. - * - * @see \eZ\Publish\API\Repository\URLWildcardService::loadAll() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testLoadAll - */ - public function testLoadAllWithOffsetParameter() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // Create new url wildcards - $urlWildcardOne = $urlWildcardService->create('/articles/*', '/content/{1}', true); - $urlWildcardTwo = $urlWildcardService->create('/news/*', '/content/{1}', true); - - // Load all available url wildcards - $allUrlWildcards = $urlWildcardService->loadAll(1); - /* END: Use Case */ - - $this->assertEquals([$urlWildcardTwo], $allUrlWildcards); - } - - /** - * Test for the loadAll() method. - * - * @see \eZ\Publish\API\Repository\URLWildcardService::loadAll() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testLoadAll - */ - public function testLoadAllWithOffsetAndLimitParameter() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // Create new url wildcards - $urlWildcardOne = $urlWildcardService->create('/articles/*', '/content/{1}'); - $urlWildcardTwo = $urlWildcardService->create('/news/*', '/content/{1}'); - - // Load all available url wildcards - $allUrlWildcards = $urlWildcardService->loadAll(0, 1); - /* END: Use Case */ - - $this->assertEquals([$urlWildcardOne], $allUrlWildcards); - } - - /** - * Test for the loadAll() method. - * - * @see \eZ\Publish\API\Repository\URLWildcardService::loadAll() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testLoadAll - */ - public function testLoadAllReturnsEmptyArrayByDefault() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // Load all available url wildcards - $allUrlWildcards = $urlWildcardService->loadAll(); - /* END: Use Case */ - - $this->assertSame([], $allUrlWildcards); - } - - /** - * Test for the translate() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\URLWildcardTranslationResult - * - * @see \eZ\Publish\API\Repository\URLWildcardService::translate() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testCreate - */ - public function testTranslate() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // Create a new url wildcard - $urlWildcardService->create('/articles/*', '/content/{1}'); - - // Translate a given url - $result = $urlWildcardService->translate('/articles/2012/05/sindelfingen'); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\URLWildcardTranslationResult', - $result - ); - - return $result; - } - - /** - * Test for the translate() method. - * - * @param \eZ\Publish\API\Repository\Values\Content\URLWildcardTranslationResult $result - * - * @see \eZ\Publish\API\Repository\URLWildcardService::translate() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testTranslate - */ - public function testTranslateSetsPropertiesOnTranslationResult(URLWildcardTranslationResult $result) - { - $this->assertPropertiesCorrect( - [ - 'uri' => '/content/2012/05/sindelfingen', - 'forward' => false, - ], - $result - ); - } - - /** - * Test for the translate() method. - * - * @see \eZ\Publish\API\Repository\URLWildcardService::translate() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testTranslate - */ - public function testTranslateWithForwardSetToTrue() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // Create a new url wildcard - $urlWildcardService->create('/articles/*/05/*', '/content/{2}/year/{1}', true); - - // Translate a given url - $result = $urlWildcardService->translate('/articles/2012/05/sindelfingen'); - /* END: Use Case */ - - $this->assertPropertiesCorrect( - [ - 'uri' => '/content/sindelfingen/year/2012', - 'forward' => true, - ], - $result - ); - } - - /** - * Test for the translate() method. - * - * @see \eZ\Publish\API\Repository\URLWildcardService::translate() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testTranslate - */ - public function testTranslateReturnsLongestMatchingWildcard() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // Create new url wildcards - $urlWildcardService->create('/articles/*/05/*', '/content/{2}/year/{1}'); - $urlWildcardService->create('/articles/*/05/sindelfingen/*', '/content/{2}/bar/{1}'); - - // Translate a given url - $result = $urlWildcardService->translate('/articles/2012/05/sindelfingen/42'); - /* END: Use Case */ - - $this->assertEquals('/content/42/bar/2012', $result->uri); - } - - /** - * Test for the translate() method. - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - * - * @see \eZ\Publish\API\Repository\URLWildcardService::translate() - * @depends eZ\Publish\API\Repository\Tests\URLWildcardServiceTest::testTranslate - */ - public function testTranslateThrowsNotFoundExceptionWhenNotAliasOrWildcardMatches() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $urlWildcardService = $repository->getURLWildcardService(); - - // This call will fail with a NotFoundException because no wildcard or - // url alias matches against the given url. - $urlWildcardService->translate('/sindelfingen'); - /* END: Use Case */ - } - - public function testCountAllReturnsZeroByDefault(): void - { - $repository = $this->getRepository(); - $urlWildcardService = $repository->getURLWildcardService(); - - $this->assertSame(0, $urlWildcardService->countAll()); - } - - public function testCountAll(): void - { - $repository = $this->getRepository(); - $urlWildcardService = $repository->getURLWildcardService(); - - $urlWildcardService->create('/articles/*', '/content/{1}'); - - $this->assertSame(1, $urlWildcardService->countAll()); - } -} diff --git a/eZ/Publish/API/Repository/Tests/UserPreferenceServiceTest.php b/eZ/Publish/API/Repository/Tests/UserPreferenceServiceTest.php deleted file mode 100644 index 581a5f873f..0000000000 --- a/eZ/Publish/API/Repository/Tests/UserPreferenceServiceTest.php +++ /dev/null @@ -1,144 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests; - -use eZ\Publish\API\Repository\Values\UserPreference\UserPreference; -use eZ\Publish\API\Repository\Values\UserPreference\UserPreferenceList; -use eZ\Publish\API\Repository\Values\UserPreference\UserPreferenceSetStruct; - -/** - * Test case for the UserPreferenceService. - * - * @see \eZ\Publish\API\Repository\UserPreferenceService - */ -class UserPreferenceServiceTest extends BaseTest -{ - /** - * @covers \eZ\Publish\API\Repository\UserPreferenceService::loadUserPreferences() - */ - public function testLoadUserPreferences() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $userPreferenceService = $repository->getUserPreferenceService(); - $userPreferenceList = $userPreferenceService->loadUserPreferences(0, 25); - /* END: Use Case */ - - $this->assertInstanceOf(UserPreferenceList::class, $userPreferenceList); - $this->assertIsArray($userPreferenceList->items); - $this->assertIsInt($userPreferenceList->totalCount); - $this->assertEquals(5, $userPreferenceList->totalCount); - } - - /** - * @covers \eZ\Publish\API\Repository\UserPreferenceService::getUserPreference() - */ - public function testGetUserPreference() - { - $repository = $this->getRepository(); - - $userPreferenceName = 'setting_1'; - - /* BEGIN: Use Case */ - $userPreferenceService = $repository->getUserPreferenceService(); - // $userPreferenceName is the name of an existing preference - $userPreference = $userPreferenceService->getUserPreference($userPreferenceName); - /* END: Use Case */ - - $this->assertInstanceOf(UserPreference::class, $userPreference); - $this->assertEquals($userPreferenceName, $userPreference->name); - } - - /** - * @covers \eZ\Publish\API\Repository\UserPreferenceService::setUserPreference() - * @depends testGetUserPreference - */ - public function testSetUserPreference() - { - $repository = $this->getRepository(); - - $userPreferenceName = 'timezone'; - - /* BEGIN: Use Case */ - $userPreferenceService = $repository->getUserPreferenceService(); - - $setStruct = new UserPreferenceSetStruct([ - 'name' => $userPreferenceName, - 'value' => 'America/New_York', - ]); - - $userPreferenceService->setUserPreference([$setStruct]); - $userPreference = $userPreferenceService->getUserPreference($userPreferenceName); - /* END: Use Case */ - - $this->assertInstanceOf(UserPreference::class, $userPreference); - $this->assertEquals($userPreferenceName, $userPreference->name); - } - - /** - * @covers \eZ\Publish\API\Repository\UserPreferenceService::setUserPreference() - * @depends testSetUserPreference - */ - public function testSetUserPreferenceThrowsInvalidArgumentExceptionOnInvalidValue() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $userPreferenceService = $repository->getUserPreferenceService(); - - $setStruct = new UserPreferenceSetStruct([ - 'name' => 'setting', - 'value' => new \stdClass(), - ]); - - // This call will fail because value is not specified - $userPreferenceService->setUserPreference([$setStruct]); - /* END: Use Case */ - } - - /** - * @covers \eZ\Publish\API\Repository\UserPreferenceService::setUserPreference() - * @depends testSetUserPreference - */ - public function testSetUserPreferenceThrowsInvalidArgumentExceptionOnEmptyName() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $userPreferenceService = $repository->getUserPreferenceService(); - - $setStruct = new UserPreferenceSetStruct([ - 'value' => 'value', - ]); - - // This call will fail because value is not specified - $userPreferenceService->setUserPreference([$setStruct]); - /* END: Use Case */ - } - - /** - * @covers \eZ\Publish\API\Repository\UserPreferenceService::getUserPreferenceCount() - */ - public function testGetUserPreferenceCount() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $userPreferenceService = $repository->getUserPreferenceService(); - $userPreferenceCount = $userPreferenceService->getUserPreferenceCount(); - /* END: Use Case */ - - $this->assertEquals(5, $userPreferenceCount); - } -} diff --git a/eZ/Publish/API/Repository/Tests/UserServiceTest.php b/eZ/Publish/API/Repository/Tests/UserServiceTest.php deleted file mode 100644 index c4553c79cf..0000000000 --- a/eZ/Publish/API/Repository/Tests/UserServiceTest.php +++ /dev/null @@ -1,3443 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests; - -use DateInterval; -use DateTime; -use DateTimeImmutable; -use Doctrine\DBAL\ParameterType; -use Exception; -use eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation; -use eZ\Publish\API\Repository\Values\User\PasswordInfo; -use eZ\Publish\API\Repository\Values\User\PasswordValidationContext; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserTokenUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserUpdateStruct; -use eZ\Publish\Core\FieldType\User\Type; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\Persistence\Legacy\User\Gateway; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\User\UserGroup; - -/** - * Test case for operations in the UserService using in memory storage. - * - * @see eZ\Publish\API\Repository\UserService - * @group integration - * @group user - */ -class UserServiceTest extends BaseTest -{ - // Example password matching default rules - private const EXAMPLE_PASSWORD = 'P@ssword123!'; - - private const EXAMPLE_PASSWORD_TTL = 30; - private const EXAMPLE_PASSWORD_TTL_WARNING = 14; - - /** - * Test for the loadUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::loadUserGroup() - */ - public function testLoadUserGroup() - { - $repository = $this->getRepository(); - - $mainGroupId = $this->generateId('group', 4); - /* BEGIN: Use Case */ - // $mainGroupId is the ID of the main "Users" group - - $userService = $repository->getUserService(); - - $userGroup = $userService->loadUserGroup($mainGroupId); - /* END: Use Case */ - - $this->assertInstanceOf(UserGroup::class, $userGroup); - - // User group happens to also be a Content; isUserGroup() should be true and isUser() should be false - $this->assertTrue($userService->isUserGroup($userGroup), 'isUserGroup() => false on a user group'); - $this->assertFalse($userService->isUser($userGroup), 'isUser() => true on a user group'); - $this->assertSame(0, $userGroup->parentId, 'parentId should be equal `0` because it is top level node'); - } - - /** - * Test for the loadUserGroupByRemoteId() method. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUserGroupByRemoteId() - */ - public function testLoadUserGroupByRemoteId(): void - { - $existingRemoteId = 'f5c88a2209584891056f987fd965b0ba'; - - $userService = $this->getRepository()->getUserService(); - $userGroup = $userService->loadUserGroupByRemoteId($existingRemoteId); - - $this->assertInstanceOf(UserGroup::class, $userGroup); - $this->assertEquals($existingRemoteId, $userGroup->contentInfo->remoteId); - // User group happens to also be a Content; isUserGroup() should be true and isUser() should be false - $this->assertTrue($userService->isUserGroup($userGroup), 'isUserGroup() => false on a user group'); - $this->assertFalse($userService->isUser($userGroup), 'isUser() => true on a user group'); - } - - /** - * Test for the loadUserGroup() method to ensure that DomainUserGroupObject is created properly even if a user - * has no access to parent of UserGroup. - * - * @see \eZ\Publish\API\Repository\UserService::loadUserGroup() - */ - public function testLoadUserGroupWithNoAccessToParent() - { - $repository = $this->getRepository(); - - $mainGroupId = $this->generateId('group', 4); - /* BEGIN: Use Case */ - // $mainGroupId is the ID of the main "Users" group - - $userService = $repository->getUserService(); - - $user = $this->createUserWithPolicies( - 'user', - [ - ['module' => 'content', 'function' => 'read'], - ], - new SubtreeLimitation(['limitationValues' => ['/1/5']]) - ); - $repository->getPermissionResolver()->setCurrentUserReference($user); - - $userGroup = $userService->loadUserGroup($mainGroupId); - /* END: Use Case */ - - $this->assertInstanceOf(UserGroup::class, $userGroup); - - // User group happens to also be a Content; isUserGroup() should be true and isUser() should be false - $this->assertTrue($userService->isUserGroup($userGroup), 'isUserGroup() => false on a user group'); - $this->assertFalse($userService->isUser($userGroup), 'isUser() => true on a user group'); - $this->assertSame(0, $userGroup->parentId, 'parentId should be equal `0` because it is top level node'); - } - - /** - * Test for the loadUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::loadUserGroup() - * @depends testLoadUserGroup - */ - public function testLoadUserGroupThrowsNotFoundException() - { - $this->expectException(NotFoundException::class); - - $repository = $this->getRepository(); - - $nonExistingGroupId = $this->generateId('group', self::DB_INT_MAX); - /* BEGIN: Use Case */ - $userService = $repository->getUserService(); - - // This call will fail with a NotFoundException - $userService->loadUserGroup($nonExistingGroupId); - /* END: Use Case */ - } - - /** - * Test for the loadUserGroupByRemoteId() method. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUserGroupByRemoteId() - * @depends \eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserGroupByRemoteId - */ - public function testLoadUserGroupByRemoteIdThrowsNotFoundException(): void - { - $this->expectException(NotFoundException::class); - - $nonExistingGroupRemoteId = 'non-existing'; - - $userService = $this->getRepository()->getUserService(); - $userService->loadUserGroupByRemoteId($nonExistingGroupRemoteId); - } - - /** - * Test for the loadSubUserGroups() method. - * - * @see \eZ\Publish\API\Repository\UserService::loadSubUserGroups() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserGroup - */ - public function testLoadSubUserGroups() - { - $repository = $this->getRepository(); - - $mainGroupId = $this->generateId('group', 4); - /* BEGIN: Use Case */ - // $mainGroupId is the ID of the main "Users" group - - $userService = $repository->getUserService(); - - $userGroup = $userService->loadUserGroup($mainGroupId); - - $subUserGroups = $userService->loadSubUserGroups($userGroup); - foreach ($subUserGroups as $subUserGroup) { - // Do something with the $subUserGroup - $this->assertInstanceOf(UserGroup::class, $subUserGroup); - } - /* END: Use Case */ - } - - /** - * Test loading sub groups throwing NotFoundException. - * - * @covers \eZ\Publish\API\Repository\UserService::loadSubUserGroups - */ - public function testLoadSubUserGroupsThrowsNotFoundException() - { - $this->expectException(NotFoundException::class); - - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $parentGroup = new UserGroup( - [ - 'content' => new Content( - [ - 'versionInfo' => new VersionInfo( - [ - 'contentInfo' => new ContentInfo( - ['id' => 123456] - ), - ] - ), - 'internalFields' => [], - ] - ), - ] - ); - $userService->loadSubUserGroups($parentGroup); - } - - /** - * Test for the newUserGroupCreateStruct() method. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct - * - * @see \eZ\Publish\API\Repository\UserService::newUserGroupCreateStruct() - */ - public function testNewUserGroupCreateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $userService = $repository->getUserService(); - - $groupCreate = $userService->newUserGroupCreateStruct('eng-US'); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\UserGroupCreateStruct', - $groupCreate - ); - - return $groupCreate; - } - - /** - * Test for the newUserGroupCreateStruct() method. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct $groupCreate - * - * @see \eZ\Publish\API\Repository\UserService::newUserGroupCreateStruct() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testNewUserGroupCreateStruct - */ - public function testNewUserGroupCreateStructSetsMainLanguageCode($groupCreate) - { - $this->assertEquals('eng-US', $groupCreate->mainLanguageCode); - } - - /** - * Test for the newUserGroupCreateStruct() method. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct $groupCreate - * - * @see \eZ\Publish\API\Repository\UserService::newUserGroupCreateStruct() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testNewUserGroupCreateStruct - */ - public function testNewUserGroupCreateStructSetsContentType($groupCreate) - { - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentType', - $groupCreate->contentType - ); - } - - /** - * Test for the newUserGroupCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\UserService::newUserGroupCreateStruct($mainLanguageCode, $contentType) - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testNewUserGroupCreateStruct - */ - public function testNewUserGroupCreateStructWithSecondParameter() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - $userService = $repository->getUserService(); - - // Load the default ContentType for user groups - $groupType = $contentTypeService->loadContentTypeByIdentifier('user_group'); - - // Instantiate a new group create struct - $groupCreate = $userService->newUserGroupCreateStruct( - 'eng-US', - $groupType - ); - /* END: Use Case */ - - $this->assertSame($groupType, $groupCreate->contentType); - } - - /** - * Test for the createUserGroup() method. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - * - * @see \eZ\Publish\API\Repository\UserService::createUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testNewUserGroupCreateStruct - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserGroup - */ - public function testCreateUserGroup() - { - /* BEGIN: Use Case */ - $userGroup = $this->createUserGroupVersion1(); - /* END: Use Case */ - - $this->assertInstanceOf( - UserGroup::class, - $userGroup - ); - - $versionInfo = $userGroup->getVersionInfo(); - - $this->assertEquals(APIVersionInfo::STATUS_PUBLISHED, $versionInfo->status); - $this->assertEquals(1, $versionInfo->versionNo); - - return $userGroup; - } - - /** - * Test for the createUserGroup() method. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * - * @see \eZ\Publish\API\Repository\UserService::createUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUserGroup - */ - public function testCreateUserGroupSetsExpectedProperties($userGroup) - { - $this->assertEquals( - [ - 'parentId' => $this->generateId('group', 4), - ], - [ - 'parentId' => $userGroup->parentId, - ] - ); - } - - /** - * Test for the createUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::createUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUserGroup - */ - public function testCreateUserGroupThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $mainGroupId = $this->generateId('group', 4); - /* BEGIN: Use Case */ - // $mainGroupId is the ID of the main "Users" group - - $userService = $repository->getUserService(); - - // Load main group - $parentUserGroup = $userService->loadUserGroup($mainGroupId); - - // Instantiate a new create struct - $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); - $userGroupCreate->setField('name', 'Example Group'); - $userGroupCreate->remoteId = '5f7f0bdb3381d6a461d8c29ff53d908f'; - - // This call will fail with an "InvalidArgumentException", because the - // specified remoteId is already used for the "Members" user group. - $userService->createUserGroup( - $userGroupCreate, - $parentUserGroup - ); - /* END: Use Case */ - } - - /** - * Test for the createUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::createUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUserGroup - */ - public function testCreateUserGroupThrowsInvalidArgumentExceptionFieldTypeNotAccept() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $mainGroupId = $this->generateId('group', 4); - /* BEGIN: Use Case */ - // $mainGroupId is the ID of the main "Users" group - - $userService = $repository->getUserService(); - - // Load main group - $parentUserGroup = $userService->loadUserGroup($mainGroupId); - - // Instantiate a new create struct - $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); - $userGroupCreate->setField('name', new \stdClass()); - - // This call will fail with an "InvalidArgumentException", because the - // specified remoteId is already used for the "Members" user group. - $userService->createUserGroup( - $userGroupCreate, - $parentUserGroup - ); - /* END: Use Case */ - } - - /** - * Test for the createUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::createUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUserGroup - */ - public function testCreateUserGroupWhenMissingField() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class); - - $repository = $this->getRepository(); - - $mainGroupId = $this->generateId('group', 4); - /* BEGIN: Use Case */ - // $mainGroupId is the ID of the main "Users" group - - $userService = $repository->getUserService(); - - // Load main group - $parentUserGroup = $userService->loadUserGroup($mainGroupId); - - // Instantiate a new create struct - $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); - - // This call will fail with a "ContentFieldValidationException", because the - // only mandatory field "name" is not set. - $userService->createUserGroup($userGroupCreate, $parentUserGroup); - /* END: Use Case */ - } - - /** - * Test for the createUserGroup() method. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - * - * @see \eZ\Publish\API\Repository\UserService::createUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testNewUserGroupCreateStruct - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserGroup - */ - public function testCreateUserGroupInTransactionWithRollback() - { - $repository = $this->getRepository(); - - $mainGroupId = $this->generateId('group', 4); - /* BEGIN: Use Case */ - // $mainGroupId is the ID of the main "Users" group - - $userService = $repository->getUserService(); - - $repository->beginTransaction(); - - try { - // Load main group - $parentUserGroup = $userService->loadUserGroup($mainGroupId); - - // Instantiate a new create struct - $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); - $userGroupCreate->setField('name', 'Example Group'); - - // Create the new user group - $createdUserGroupId = $userService->createUserGroup( - $userGroupCreate, - $parentUserGroup - )->id; - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - $repository->rollback(); - - try { - // Throws exception since creation of user group was rolled back - $loadedGroup = $userService->loadUserGroup($createdUserGroupId); - } catch (NotFoundException $e) { - return; - } - /* END: Use Case */ - - $this->fail('User group object still exists after rollback.'); - } - - /** - * Test for the deleteUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::deleteUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUserGroup - */ - public function testDeleteUserGroup() - { - $this->expectException(NotFoundException::class); - - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $userGroup = $this->createUserGroupVersion1(); - - // Delete the currently created user group again - $userService->deleteUserGroup($userGroup); - /* END: Use Case */ - - // We use the NotFoundException here for verification - $userService->loadUserGroup($userGroup->id); - } - - /** - * Test deleting user group throwing NotFoundException. - * - * @covers \eZ\Publish\API\Repository\UserService::deleteUserGroup - */ - public function testDeleteUserGroupThrowsNotFoundException() - { - $this->expectException(NotFoundException::class); - - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $userGroup = new UserGroup( - [ - 'content' => new Content( - [ - 'versionInfo' => new VersionInfo( - ['contentInfo' => new ContentInfo(['id' => 123456])] - ), - 'internalFields' => [], - ] - ), - ] - ); - $userService->deleteUserGroup($userGroup); - } - - /** - * Test for the moveUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::moveUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUserGroup - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadSubUserGroups - */ - public function testMoveUserGroup() - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $membersGroupId = $this->generateId('group', 13); - /* BEGIN: Use Case */ - // $membersGroupId is the ID of the "Members" user group in an eZ - // Publish demo installation - - $userGroup = $this->createUserGroupVersion1(); - - // Load the new parent group - $membersUserGroup = $userService->loadUserGroup($membersGroupId); - - // Move user group from "Users" to "Members" - $userService->moveUserGroup($userGroup, $membersUserGroup); - - // Reload the user group to get an updated $parentId - $userGroup = $userService->loadUserGroup($userGroup->id); - - $this->refreshSearch($repository); - - // The returned array will no contain $userGroup - $subUserGroups = $userService->loadSubUserGroups( - $membersUserGroup - ); - /* END: Use Case */ - - $subUserGroupIds = array_map( - static function ($content) { - return $content->id; - }, - $subUserGroups - ); - - $this->assertEquals($membersGroupId, $userGroup->parentId); - $this->assertEquals([$userGroup->id], $subUserGroupIds); - } - - /** - * Test moving a user group below another group throws NotFoundException. - * - * @covers \eZ\Publish\API\Repository\UserService::moveUserGroup - */ - public function testMoveUserGroupThrowsNotFoundException() - { - $this->expectException(NotFoundException::class); - - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $userGroupToMove = new UserGroup( - [ - 'content' => new Content( - [ - 'versionInfo' => new VersionInfo( - ['contentInfo' => new ContentInfo(['id' => 123456])] - ), - 'internalFields' => [], - ] - ), - ] - ); - $parentUserGroup = new UserGroup( - [ - 'content' => new Content( - [ - 'versionInfo' => new VersionInfo( - ['contentInfo' => new ContentInfo(['id' => 123455])] - ), - 'internalFields' => [], - ] - ), - ] - ); - $userService->moveUserGroup($userGroupToMove, $parentUserGroup); - } - - /** - * Test for the newUserGroupUpdateStruct() method. - * - * @covers \eZ\Publish\API\Repository\UserService::newUserGroupUpdateStruct - */ - public function testNewUserGroupUpdateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $userService = $repository->getUserService(); - - $groupUpdate = $userService->newUserGroupUpdateStruct(); - /* END: Use Case */ - - $this->assertInstanceOf( - UserGroupUpdateStruct::class, - $groupUpdate - ); - - $this->assertNull($groupUpdate->contentUpdateStruct); - $this->assertNull($groupUpdate->contentMetadataUpdateStruct); - } - - /** - * Test for the updateUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::updateUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUserGroup - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testNewUserGroupUpdateStruct - */ - public function testUpdateUserGroup() - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $userGroup = $this->createUserGroupVersion1(); - - // Create a group update struct and change nothing - $groupUpdate = $userService->newUserGroupUpdateStruct(); - - // This update will do nothing - $userGroup = $userService->updateUserGroup( - $userGroup, - $groupUpdate - ); - /* END: Use Case */ - - $this->assertInstanceOf( - UserGroup::class, - $userGroup - ); - - $this->assertEquals(1, $userGroup->getVersionInfo()->versionNo); - } - - /** - * Test for the updateUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::updateUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testUpdateUserGroup - */ - public function testUpdateUserGroupWithSubContentUpdateStruct() - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $userGroup = $this->createUserGroupVersion1(); - - // Load the content service - $contentService = $repository->getContentService(); - - // Create a content update struct and update the group name - $contentUpdate = $contentService->newContentUpdateStruct(); - $contentUpdate->setField('name', 'Sindelfingen', 'eng-US'); - - // Create a group update struct and set content update struct - $groupUpdate = $userService->newUserGroupUpdateStruct(); - $groupUpdate->contentUpdateStruct = $contentUpdate; - - // This will update the name and the increment the group version number - $userGroup = $userService->updateUserGroup( - $userGroup, - $groupUpdate - ); - /* END: Use Case */ - - $this->assertEquals('Sindelfingen', $userGroup->getFieldValue('name', 'eng-US')); - - $versionInfo = $userGroup->getVersionInfo(); - - $this->assertEquals(APIVersionInfo::STATUS_PUBLISHED, $versionInfo->status); - $this->assertEquals(2, $versionInfo->versionNo); - } - - /** - * Test for the updateUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::updateUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testUpdateUserGroup - */ - public function testUpdateUserGroupWithSubContentMetadataUpdateStruct() - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $userGroup = $this->createUserGroupVersion1(); - - // Load the content service - $contentService = $repository->getContentService(); - - // Create a metadata update struct and change the remoteId - $metadataUpdate = $contentService->newContentMetadataUpdateStruct(); - $metadataUpdate->remoteId = '3c61299780663bafa3af2101e52125da'; - - // Create a group update struct and set content update struct - $groupUpdate = $userService->newUserGroupUpdateStruct(); - $groupUpdate->contentMetadataUpdateStruct = $metadataUpdate; - - // This will update the name and the increment the group version number - $userGroup = $userService->updateUserGroup( - $userGroup, - $groupUpdate - ); - /* END: Use Case */ - - $this->assertEquals( - '3c61299780663bafa3af2101e52125da', - $userGroup->contentInfo->remoteId - ); - - $versionInfo = $userGroup->getVersionInfo(); - - $this->assertEquals(APIVersionInfo::STATUS_PUBLISHED, $versionInfo->status); - $this->assertEquals(1, $versionInfo->versionNo); - } - - /** - * Test for the updateUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::updateUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testUpdateUserGroup - */ - public function testUpdateUserGroupThrowsInvalidArgumentExceptionOnFieldTypeNotAccept() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $userGroup = $this->createUserGroupVersion1(); - - // Load the content service - $contentService = $repository->getContentService(); - - // Create a content update struct and update the group name - $contentUpdate = $contentService->newContentUpdateStruct(); - // An object of stdClass is not accepted as a value by the field "name" - $contentUpdate->setField('name', new \stdClass(), 'eng-US'); - - // Create a group update struct and set content update struct - $groupUpdate = $userService->newUserGroupUpdateStruct(); - $groupUpdate->contentUpdateStruct = $contentUpdate; - - // This call will fail with an InvalidArgumentException, because the - // field "name" does not accept the given value - $userService->updateUserGroup($userGroup, $groupUpdate); - /* END: Use Case */ - } - - /** - * Test for the newUserCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\UserService::newUserCreateStruct() - */ - public function testNewUserCreateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $userService = $repository->getUserService(); - - $userCreate = $userService->newUserCreateStruct( - 'user', - 'user@example.com', - 'secret', - 'eng-US' - ); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\UserCreateStruct', - $userCreate - ); - - return $userCreate; - } - - /** - * Test updating a user group throws ContentFieldValidationException. - * - * @covers \eZ\Publish\API\Repository\UserService::updateUserGroup - */ - public function testUpdateUserGroupThrowsContentFieldValidationExceptionOnRequiredFieldEmpty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class); - - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - $contentService = $repository->getContentService(); - - $userGroup = $userService->loadUserGroup(42); - $userGroupUpdateStruct = $userService->newUserGroupUpdateStruct(); - $userGroupUpdateStruct->contentUpdateStruct = $contentService->newContentUpdateStruct(); - $userGroupUpdateStruct->contentUpdateStruct->setField('name', '', 'eng-US'); - - $userService->updateUserGroup($userGroup, $userGroupUpdateStruct); - } - - /** - * Test for the newUserCreateStruct() method. - * - * @param \eZ\Publish\API\Repository\Values\User\UserCreateStruct $userCreate - * - * @see \eZ\Publish\API\Repository\UserService::newUserCreateStruct() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testNewUserCreateStruct - */ - public function testNewUserCreateStructSetsExpectedProperties($userCreate) - { - $this->assertEquals( - [ - 'login' => 'user', - 'email' => 'user@example.com', - 'password' => 'secret', - 'mainLanguageCode' => 'eng-US', - ], - [ - 'login' => $userCreate->login, - 'email' => $userCreate->email, - 'password' => $userCreate->password, - 'mainLanguageCode' => $userCreate->mainLanguageCode, - ] - ); - } - - /** - * Test for the newUserCreateStruct() method. - * - * @see \eZ\Publish\API\Repository\UserService::newUserCreateStruct($login, $email, $password, $mainLanguageCode, $contentType) - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testNewUserCreateStruct - */ - public function testNewUserCreateStructWithFifthParameter() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $contentTypeService = $repository->getContentTypeService(); - $userService = $repository->getUserService(); - - $userType = $contentTypeService->loadContentTypeByIdentifier('user'); - - $userCreate = $userService->newUserCreateStruct( - 'user', - 'user@example.com', - 'secret', - 'eng-US', - $userType - ); - /* END: Use Case */ - - $this->assertSame($userType, $userCreate->contentType); - } - - /** - * Test for creating user with Active Directory login name. - */ - public function testNewUserWithDomainName() - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - $createdUser = $this->createUserVersion1( - 'ez-user-Domain\username-by-login', - 'username-by-login@ez-user-Domain.com' - ); - $loadedUser = $userService->loadUserByLogin('ez-user-Domain\username-by-login', Language::ALL); - - $this->assertEquals($createdUser, $loadedUser); - } - - /** - * Test for the createUser() method. - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @see \eZ\Publish\API\Repository\UserService::createUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserGroup - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testNewUserCreateStruct - */ - public function testCreateUser() - { - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - /* END: Use Case */ - - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\User\\User', - $user - ); - - return $user; - } - - /** - * Test for the createUser() method. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * - * @see \eZ\Publish\API\Repository\UserService::createUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - */ - public function testCreateUserSetsExpectedProperties(User $user) - { - $this->assertEquals( - [ - 'login' => 'user', - 'email' => 'user@example.com', - 'mainLanguageCode' => 'eng-US', - ], - [ - 'login' => $user->login, - 'email' => $user->email, - 'mainLanguageCode' => $user->contentInfo->mainLanguageCode, - ] - ); - } - - /** - * Test for the createUser() method. - * - * @see \eZ\Publish\API\Repository\UserService::createUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - */ - public function testCreateUserWhenMissingField() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class); - - $repository = $this->getRepository(); - - $editorsGroupId = $this->generateId('group', 13); - /* BEGIN: Use Case */ - // $editorsGroupId is the ID of the "Editors" user group in an eZ - // Publish demo installation - - $userService = $repository->getUserService(); - - // Instantiate a create struct with mandatory properties - $userCreate = $userService->newUserCreateStruct( - 'user', - 'user@example.com', - 'secret', - 'eng-US' - ); - - // Do not set the mandatory fields "first_name" and "last_name" - //$userCreate->setField( 'first_name', 'Example' ); - //$userCreate->setField( 'last_name', 'User' ); - - // Load parent group for the user - $group = $userService->loadUserGroup($editorsGroupId); - - // This call will fail with a "ContentFieldValidationException", because the - // mandatory fields "first_name" and "last_name" are not set. - $userService->createUser($userCreate, [$group]); - /* END: Use Case */ - } - - /** - * Test for the createUser() method. - * - * @see \eZ\Publish\API\Repository\UserService::createUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - */ - public function testCreateUserThrowsInvalidArgumentExceptionOnFieldTypeNotAccept() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $editorsGroupId = $this->generateId('group', 13); - /* BEGIN: Use Case */ - // $editorsGroupId is the ID of the "Editors" user group in an eZ - // Publish demo installation - - $userService = $repository->getUserService(); - - // Instantiate a create struct with mandatory properties - $userCreate = $userService->newUserCreateStruct( - 'user', - 'user@example.com', - 'secret', - 'eng-US' - ); - - // An object of stdClass is not a valid value for the field first_name - $userCreate->setField('first_name', new \stdClass()); - $userCreate->setField('last_name', 'User'); - - // Load parent group for the user - $group = $userService->loadUserGroup($editorsGroupId); - - // This call will fail with an "InvalidArgumentException", because the - // value for the firled "first_name" is not accepted by the field type. - $userService->createUser($userCreate, [$group]); - /* END: Use Case */ - } - - /** - * Test for the createUser() method. - * - * @covers \eZ\Publish\API\Repository\UserService::createUser - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - */ - public function testCreateUserThrowsInvalidArgumentException() - { - $repository = $this->getRepository(); - - $editorsGroupId = $this->generateId('group', 13); - /* BEGIN: Use Case */ - // $editorsGroupId is the ID of the "Editors" user group in an eZ - // Publish demo installation - - $userService = $repository->getUserService(); - - // Instantiate a create struct with mandatory properties - $userCreate = $userService->newUserCreateStruct( - // admin is an existing login - 'admin', - 'user@example.com', - 'secret', - 'eng-US' - ); - - $userCreate->setField('first_name', 'Example'); - $userCreate->setField('last_name', 'User'); - - // Load parent group for the user - $group = $userService->loadUserGroup($editorsGroupId); - - try { - // This call will fail with a "InvalidArgumentException", because the - // user with "admin" login already exists. - $userService->createUser($userCreate, [$group]); - /* END: Use Case */ - } catch (ContentFieldValidationException $e) { - // Exception is caught, as there is no other way to check exception properties. - $this->assertValidationErrorOccurs($e, 'The user login \'admin\' is used by another user. You must enter a unique login.'); - - /* END: Use Case */ - return; - } - - $this->fail('Expected ValidationError messages did not occur.'); - } - - /** - * Test for the createUser() method. - * - * @covers \eZ\Publish\API\Repository\UserService::createUser - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - */ - public function testCreateUserWithEmailAlreadyTaken(): void - { - $repository = $this->getRepository(); - - $userContentType = $this->createUserContentTypeWithAccountSettings('user_email_unique', [ - Type::REQUIRE_UNIQUE_EMAIL => true, - ]); - - $existingUser = $this->createUserVersion1( - 'existing_user', - 'unique@email.com', - $userContentType, - ); - - $editorsGroupId = $this->generateId('group', 13); - /* BEGIN: Use Case */ - // $editorsGroupId is the ID of the "Editors" user group in an eZ - // Publish demo installation - - $userService = $repository->getUserService(); - - // Instantiate a create struct with mandatory properties - $userCreate = $userService->newUserCreateStruct( - 'another_user', - // email is already taken - 'unique@email.com', - 'VerySecure@Password.1234', - 'eng-US', - $userContentType - ); - - $userCreate->setField('first_name', 'Example'); - $userCreate->setField('last_name', 'User'); - - // Load parent group for the user - $group = $userService->loadUserGroup($editorsGroupId); - - try { - // This call will fail with a "ContentFieldValidationException", because the - // user with "unique@email.com" email already exists in database. - $userService->createUser($userCreate, [$group]); - } catch (ContentFieldValidationException $e) { - // Exception is caught, as there is no other way to check exception properties. - $this->assertValidationErrorOccurs($e, 'Email \'unique@email.com\' is used by another user. You must enter a unique email.'); - - return; - } - - $this->fail('Expected ValidationError messages did not occur.'); - } - - /** - * Test for the createUser() method. - * - * @covers \eZ\Publish\API\Repository\UserService::createUser - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - */ - public function testCreateInvalidFormatUsername(): void - { - $repository = $this->getRepository(); - - $userContentType = $this->createUserContentTypeWithAccountSettings('username_format', [ - Type::USERNAME_PATTERN => '^[^@]$', - ]); - - $editorsGroupId = $this->generateId('group', 13); - /* BEGIN: Use Case */ - // $editorsGroupId is the ID of the "Editors" user group in an eZ - // Publish demo installation - - $userService = $repository->getUserService(); - - // Instantiate a create struct with mandatory properties - $userCreate = $userService->newUserCreateStruct( - // login contains @ - 'invalid@user', - 'unique@email.com', - 'VerySecure@Password.1234', - 'eng-US', - $userContentType - ); - - $userCreate->setField('first_name', 'Example'); - $userCreate->setField('last_name', 'User'); - - // Load parent group for the user - $group = $userService->loadUserGroup($editorsGroupId); - - try { - // This call will fail with a "ContentFieldValidationException", because the - // user with "invalid@user" login does not match "^[^@]$" pattern. - $userService->createUser($userCreate, [$group]); - } catch (ContentFieldValidationException $e) { - // Exception is caught, as there is no other way to check exception properties. - $this->assertValidationErrorOccurs($e, 'Invalid login format'); - - return; - } - - $this->fail('Expected ValidationError messages did not occur.'); - } - - /** - * Test for the createUser() method. - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @see \eZ\Publish\API\Repository\UserService::createUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserGroup - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testNewUserCreateStruct - */ - public function testCreateUserInTransactionWithRollback() - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $repository->beginTransaction(); - - try { - $user = $this->createUserVersion1(); - } catch (Exception $e) { - // Cleanup hanging transaction on error - $repository->rollback(); - throw $e; - } - - $repository->rollback(); - - try { - // Throws exception since creation of user was rolled back - $loadedUser = $userService->loadUser($user->id); - } catch (NotFoundException $e) { - return; - } - /* END: Use Case */ - - $this->fail('User object still exists after rollback.'); - } - - /** - * Test creating a user throwing NotFoundException. - * - * @covers \eZ\Publish\API\Repository\UserService::createUser - */ - public function testCreateUserThrowsNotFoundException() - { - $this->expectException(NotFoundException::class); - - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $userCreateStruct = $userService->newUserCreateStruct('new_user', 'new_user@ez.no', 'password', 'eng-GB'); - $userCreateStruct->setField('first_name', 'New'); - $userCreateStruct->setField('last_name', 'User'); - - $parentGroup = new UserGroup( - [ - 'content' => new Content( - [ - 'versionInfo' => new VersionInfo( - [ - 'contentInfo' => new ContentInfo(['id' => 123456]), - ] - ), - 'internalFields' => [], - ] - ), - ] - ); - $userService->createUser($userCreateStruct, [$parentGroup]); - } - - /** - * Test creating a user throwing UserPasswordValidationException when password doesn't follow specific rules. - * - * @covers \eZ\Publish\API\Repository\UserService::createUser - */ - public function testCreateUserWithWeakPasswordThrowsUserPasswordValidationException() - { - $userContentType = $this->createUserContentTypeWithStrongPassword(); - - try { - // This call will fail with a "UserPasswordValidationException" because the - // the password does not follow specified rules. - $this->createTestUserWithPassword('pass', $userContentType); - } catch (ContentFieldValidationException $e) { - // Exception is caught, as there is no other way to check exception properties. - $this->assertAllValidationErrorsOccur( - $e, - [ - 'User password must include at least one special character', - 'User password must be at least 8 characters long', - 'User password must include at least one upper case letter', - 'User password must include at least one number', - ] - ); - - return; - } - - $this->fail('Expected ValidationError messages did not occur.'); - } - - /** - * Opposite test case for testCreateUserWithWeakPasswordThrowsUserPasswordValidationException. - * - * @covers \eZ\Publish\API\Repository\UserService::createUser - */ - public function testCreateUserWithStrongPassword() - { - $userContentType = $this->createUserContentTypeWithStrongPassword(); - - /* BEGIN: Use Case */ - $user = $this->createTestUserWithPassword('H@xxi0r!', $userContentType); - /* END: Use Case */ - - $this->assertInstanceOf(User::class, $user); - } - - /** - * Test for the loadUser() method. - * - * @see \eZ\Publish\API\Repository\UserService::loadUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - */ - public function testLoadUser() - { - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Load the newly created user - $userReloaded = $userService->loadUser($user->id, Language::ALL); - /* END: Use Case */ - - $this->assertEquals($user, $userReloaded); - - // User happens to also be a Content; isUser() should be true and isUserGroup() should be false - $this->assertTrue($userService->isUser($user), 'isUser() => false on a user'); - $this->assertFalse($userService->isUserGroup($user), 'isUserGroup() => true on a user group'); - } - - /** - * Test for the loadUser() method. - * - * @see \eZ\Publish\API\Repository\UserService::loadUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUser - */ - public function testLoadUserThrowsNotFoundException() - { - $this->expectException(NotFoundException::class); - - $repository = $this->getRepository(); - - $nonExistingUserId = $this->generateId('user', self::DB_INT_MAX); - /* BEGIN: Use Case */ - $userService = $repository->getUserService(); - - // This call will fail with a "NotFoundException", because no user with - // an id equal to self::DB_INT_MAX should exist. - $userService->loadUser($nonExistingUserId); - /* END: Use Case */ - } - - /** - * @see \eZ\Publish\API\Repository\UserService::checkUserCredentials() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - */ - public function testCheckUserCredentialsValid(): void - { - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Load the newly created user credentials - $credentialsValid = $userService->checkUserCredentials($user, 'VerySecret@Password.1234'); - /* END: Use Case */ - - $this->assertTrue($credentialsValid); - } - - /** - * @see \eZ\Publish\API\Repository\UserService::checkUserCredentials() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - */ - public function testCheckUserCredentialsInvalid(): void - { - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Load the newly created user credentials - $credentialsValid = $userService->checkUserCredentials($user, 'NotSoSecretPassword'); - /* END: Use Case */ - - $this->assertFalse($credentialsValid); - } - - /** - * Test for the loadUserByLogin() method. - * - * @see \eZ\Publish\API\Repository\UserService::loadUserByLogin() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - */ - public function testLoadUserByLogin() - { - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1('User'); - - // Load the newly created user - $userReloaded = $userService->loadUserByLogin('User'); - /* END: Use Case */ - - $this->assertPropertiesCorrect( - [ - 'login' => $user->login, - 'email' => $user->email, - 'passwordHash' => $user->passwordHash, - 'hashAlgorithm' => $user->hashAlgorithm, - 'enabled' => $user->enabled, - 'maxLogin' => $user->maxLogin, - 'id' => $user->id, - 'contentInfo' => $user->contentInfo, - 'versionInfo' => $user->versionInfo, - 'fields' => $user->fields, - ], - $userReloaded - ); - } - - /** - * Test for the loadUserByLogin() method. - * - * @see \eZ\Publish\API\Repository\UserService::loadUserByLogin() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserByLogin - */ - public function testLoadUserByLoginThrowsNotFoundExceptionForUnknownLogin() - { - $this->expectException(NotFoundException::class); - - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $this->createUserVersion1(); - - // This call will fail with a "NotFoundException", because the given - // login/password combination does not exist. - $userService->loadUserByLogin('user42'); - /* END: Use Case */ - } - - /** - * Test for the loadUserByLogin() method. - * - * @see \eZ\Publish\API\Repository\UserService::loadUserByLogin() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserByLogin - */ - public function testLoadUserByLoginWorksForLoginWithWrongCase() - { - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Lookup by user login should ignore casing - $userReloaded = $userService->loadUserByLogin('USER'); - /* END: Use Case */ - - $this->assertPropertiesCorrect( - [ - 'login' => $user->login, - 'email' => $user->email, - 'passwordHash' => $user->passwordHash, - 'hashAlgorithm' => $user->hashAlgorithm, - 'enabled' => $user->enabled, - 'maxLogin' => $user->maxLogin, - 'id' => $user->id, - 'contentInfo' => $user->contentInfo, - 'versionInfo' => $user->versionInfo, - 'fields' => $user->fields, - ], - $userReloaded - ); - } - - /** - * Test for the loadUserByLogin() method. - * - * In some cases people use email as login name, make sure system works as exepcted when asking for user by email. - * - * @see \eZ\Publish\API\Repository\UserService::loadUserByLogin() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserByLogin - */ - public function testLoadUserByLoginThrowsNotFoundExceptionForUnknownLoginByEmail() - { - $this->expectException(NotFoundException::class); - - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Lookup by user login by email should behave as normal - $userService->loadUserByLogin('user@example.com'); - /* END: Use Case */ - } - - /** - * Test for the loadUsersByEmail() method. - * - * @see \eZ\Publish\API\Repository\UserService::loadUsersByEmail() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - */ - public function testLoadUserByEmail() - { - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Load the newly created user - $usersReloaded = $userService->loadUsersByEmail('user@example.com', Language::ALL); - /* END: Use Case */ - - $this->assertEquals([$user], $usersReloaded); - } - - /** - * Test for the loadUsersByEmail() method. - * - * @see \eZ\Publish\API\Repository\UserService::loadUsersByEmail() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserByEmail - */ - public function testLoadUserByEmailReturnsEmptyInUnknownEmail() - { - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $this->createUserVersion1(); - - // This call will return empty array, because the given - // login/password combination does not exist. - $emptyUserList = $userService->loadUsersByEmail('user42@example.com'); - /* END: Use Case */ - - $this->assertEquals([], $emptyUserList); - } - - /** - * Test for the deleteUser() method. - * - * @see \eZ\Publish\API\Repository\UserService::deleteUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUser - */ - public function testDeleteUser() - { - $this->expectException(NotFoundException::class); - - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Delete the currently created user - $userService->deleteUser($user); - /* END: Use Case */ - - // We use the NotFoundException here to verify that the user not exists - $userService->loadUser($user->id); - } - - /** - * Test for the deleteUser() method. - * - * @covers \eZ\Publish\API\Repository\UserService::deleteUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUser - */ - public function testDeleteUserDeletesRelatedBookmarks() - { - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - $locationService = $repository->getLocationService(); - $bookmarkService = $repository->getBookmarkService(); - /* BEGIN: Use Case */ - $admin = $repository->getPermissionResolver()->getCurrentUserReference(); - - $user = $this->createUserVersion1(); - - $repository->getPermissionResolver()->setCurrentUserReference($user); - - $bookmarkService->createBookmark( - $locationService->loadLocation($this->generateId('location', 43)) - ); - - $repository->getPermissionResolver()->setCurrentUserReference($admin); - // Delete the currently created user - $userService->deleteUser($user); - - $repository->getPermissionResolver()->setCurrentUserReference($user); - /* END: Use Case */ - - $this->assertEquals(0, $bookmarkService->loadBookmarks(0, 9999)->totalCount); - } - - /** - * Test for the newUserUpdateStruct() method. - * - * @see \eZ\Publish\API\Repository\UserService::newUserUpdateStruct() - */ - public function testNewUserUpdateStruct() - { - $repository = $this->getRepository(); - - /* BEGIN: Use Case */ - $userService = $repository->getUserService(); - - // Create a new update struct instance - $userUpdate = $userService->newUserUpdateStruct(); - /* END: Use Case */ - - $this->assertInstanceOf( - UserUpdateStruct::class, - $userUpdate - ); - - $this->assertNull($userUpdate->contentUpdateStruct); - $this->assertNull($userUpdate->contentMetadataUpdateStruct); - - $this->assertPropertiesCorrect( - [ - 'email' => null, - 'password' => null, - 'enabled' => null, - 'maxLogin' => null, - ], - $userUpdate - ); - } - - /** - * Test for the updateUser() method. - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @see \eZ\Publish\API\Repository\UserService::updateUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testNewUserUpdateStruct - */ - public function testUpdateUser() - { - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Create a new update struct instance - $userUpdate = $userService->newUserUpdateStruct(); - - // Set new values for password and maxLogin - $userUpdate->password = 'my-new-password'; - $userUpdate->maxLogin = 42; - $userUpdate->enabled = false; - - // Updated the user record. - $userVersion2 = $userService->updateUser($user, $userUpdate); - /* END: Use Case */ - - $this->assertInstanceOf(User::class, $userVersion2); - - return $userVersion2; - } - - /** - * Test for the updateUser() and loadUsersByEmail() method on change to email. - */ - public function testUpdateUserEmail(): void - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - // Create a user - $user = $this->createUserVersion1(); - - // Check we get what we expect (and implicit warmup any kind of cache) - $users = $userService->loadUsersByEmail('user2@example.com'); - $this->assertCount(0, $users); - - // Update user with the given email address - $userUpdate = $userService->newUserUpdateStruct(); - $userUpdate->email = 'user2@example.com'; - $updatedUser = $userService->updateUser($user, $userUpdate); - $this->assertInstanceOf(User::class, $updatedUser); - - // Check that we can load user by email - $users = $userService->loadUsersByEmail('user2@example.com'); - $this->assertCount(1, $users); - $this->assertInstanceOf(User::class, $users[0]); - } - - /** - * Test for the updateUser() method. - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @see \eZ\Publish\API\Repository\UserService::updateUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testNewUserUpdateStruct - */ - public function testUpdateUserNoPassword() - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Create a new update struct instance - $userUpdate = $userService->newUserUpdateStruct(); - - // Set new values for maxLogin, don't change password - $userUpdate->maxLogin = 43; - $userUpdate->enabled = false; - - // Updated the user record. - $userVersion2 = $userService->updateUser($user, $userUpdate); - /* END: Use Case */ - - $this->assertInstanceOf(User::class, $user); - - $this->assertEquals( - [ - 'login' => $user->login, - 'email' => $user->email, - 'passwordHash' => $user->passwordHash, - 'hashAlgorithm' => $user->hashAlgorithm, - 'maxLogin' => 43, - 'enabled' => false, - ], - [ - 'login' => $userVersion2->login, - 'email' => $userVersion2->email, - 'passwordHash' => $userVersion2->passwordHash, - 'hashAlgorithm' => $userVersion2->hashAlgorithm, - 'maxLogin' => $userVersion2->maxLogin, - 'enabled' => $userVersion2->enabled, - ] - ); - } - - /** - * Test for the updateUser() method. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * - * @see \eZ\Publish\API\Repository\UserService::updateUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testUpdateUser - */ - public function testUpdateUserUpdatesExpectedProperties(User $user) - { - $this->assertEquals( - [ - 'login' => 'user', - 'email' => 'user@example.com', - 'maxLogin' => 42, - 'enabled' => false, - ], - [ - 'login' => $user->login, - 'email' => $user->email, - 'maxLogin' => $user->maxLogin, - 'enabled' => $user->enabled, - ] - ); - - // Make sure passwordUpdatedAt field has been updated together with password - $this->assertNotNull($user->passwordUpdatedAt); - $this->assertEqualsWithDelta( - $user->getVersionInfo()->modificationDate->getTimestamp(), - $user->passwordUpdatedAt->getTimestamp(), - 2.0 - ); - } - - /** - * Test for the updateUser() method. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * - * @see \eZ\Publish\API\Repository\UserService::updateUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testUpdateUser - */ - public function testUpdateUserReturnsPublishedVersion(User $user) - { - $this->assertEquals( - APIVersionInfo::STATUS_PUBLISHED, - $user->getVersionInfo()->status - ); - } - - /** - * Test for the updateUser() method. - * - * @see \eZ\Publish\API\Repository\UserService::updateUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testUpdateUser - */ - public function testUpdateUserWithContentMetadataUpdateStruct() - { - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Get the ContentService implementation - $contentService = $repository->getContentService(); - - // Create a metadata update struct and change the remote id. - $metadataUpdate = $contentService->newContentMetadataUpdateStruct(); - $metadataUpdate->remoteId = '85e10037d1ac0a00aa75443ced483e08'; - - // Create a new update struct instance - $userUpdate = $userService->newUserUpdateStruct(); - - // Set the metadata update struct. - $userUpdate->contentMetadataUpdateStruct = $metadataUpdate; - - // Updated the user record. - $userVersion2 = $userService->updateUser($user, $userUpdate); - - // The contentInfo->remoteId will be changed now. - $remoteId = $userVersion2->contentInfo->remoteId; - /* END: Use Case */ - - $this->assertEquals('85e10037d1ac0a00aa75443ced483e08', $remoteId); - } - - /** - * Test for the updateUser() method. - * - * @see \eZ\Publish\API\Repository\UserService::updateUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testUpdateUser - */ - public function testUpdateUserWithContentUpdateStruct() - { - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Get the ContentService implementation - $contentService = $repository->getContentService(); - - // Create a content update struct and change the remote id. - $contentUpdate = $contentService->newContentUpdateStruct(); - $contentUpdate->setField('first_name', 'Hello', 'eng-US'); - $contentUpdate->setField('last_name', 'World', 'eng-US'); - - // Create a new update struct instance - $userUpdate = $userService->newUserUpdateStruct(); - - // Set the content update struct. - $userUpdate->contentUpdateStruct = $contentUpdate; - - // Updated the user record. - $userVersion2 = $userService->updateUser($user, $userUpdate); - - $name = sprintf( - '%s %s', - $userVersion2->getFieldValue('first_name'), - $userVersion2->getFieldValue('last_name') - ); - /* END: Use Case */ - - $this->assertEquals('Hello World', $name); - } - - /** - * Test for the updateUser() method. - * - * @see \eZ\Publish\API\Repository\UserService::updateUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testUpdateUser - */ - public function testUpdateUserWhenMissingField() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class); - - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Get the ContentService implementation - $contentService = $repository->getContentService(); - - // Create a content update struct and change the remote id. - $contentUpdate = $contentService->newContentUpdateStruct(); - $contentUpdate->setField('first_name', null, 'eng-US'); - - // Create a new update struct instance - $userUpdate = $userService->newUserUpdateStruct(); - - // Set the content update struct. - $userUpdate->contentUpdateStruct = $contentUpdate; - - // This call will fail with a "ContentFieldValidationException" because the - // mandatory field "first_name" is set to an empty value. - $userService->updateUser($user, $userUpdate); - - /* END: Use Case */ - } - - /** - * Test for the updateUser() method. - * - * @see \eZ\Publish\API\Repository\UserService::updateUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testUpdateUser - */ - public function testUpdateUserThrowsInvalidArgumentExceptionOnFieldTypeNotAccept() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // Get the ContentService implementation - $contentService = $repository->getContentService(); - - $contentUpdate = $contentService->newContentUpdateStruct(); - // An object of stdClass is not valid for the field first_name - $contentUpdate->setField('first_name', new \stdClass(), 'eng-US'); - - // Create a new update struct instance - $userUpdate = $userService->newUserUpdateStruct(); - - // Set the content update struct. - $userUpdate->contentUpdateStruct = $contentUpdate; - - // This call will fail with a "InvalidArgumentException" because the - // the field "first_name" does not accept the given value. - $userService->updateUser($user, $userUpdate); - - /* END: Use Case */ - } - - /** - * Test updating a user throwing UserPasswordValidationException when password doesn't follow specified rules. - * - * @covers \eZ\Publish\API\Repository\UserService::updateUser - */ - public function testUpdateUserWithWeakPasswordThrowsUserPasswordValidationException() - { - $userService = $this->getRepository()->getUserService(); - - $user = $this->createTestUserWithPassword('H@xxxiR!_1', $this->createUserContentTypeWithStrongPassword()); - - /* BEGIN: Use Case */ - // Create a new update struct instance - $userUpdate = $userService->newUserUpdateStruct(); - $userUpdate->password = 'pass'; - - try { - // This call will fail with a "UserPasswordValidationException" because the - // the password does not follow specified rules - $userService->updateUser($user, $userUpdate); - /* END: Use Case */ - } catch (ContentFieldValidationException $e) { - // Exception is caught, as there is no other way to check exception properties. - $this->assertValidationErrorOccurs($e, 'User password must include at least one special character'); - $this->assertValidationErrorOccurs($e, 'User password must be at least 8 characters long'); - $this->assertValidationErrorOccurs($e, 'User password must include at least one upper case letter'); - $this->assertValidationErrorOccurs($e, 'User password must include at least one number'); - - /* END: Use Case */ - return; - } - - $this->fail('Expected ValidationError messages did not occur.'); - } - - /** - * Opposite test case for testUpdateUserWithWeakPasswordThrowsUserPasswordValidationException. - * - * @covers \eZ\Publish\API\Repository\UserService::updateUser - */ - public function testUpdateUserWithStrongPassword() - { - $userService = $this->getRepository()->getUserService(); - - $user = $this->createTestUserWithPassword('H@xxxiR!_1', $this->createUserContentTypeWithStrongPassword()); - - /* BEGIN: Use Case */ - // Create a new update struct instance - $userUpdate = $userService->newUserUpdateStruct(); - $userUpdate->password = 'H@xxxiR!_2'; - - $user = $userService->updateUser($user, $userUpdate); - /* END: Use Case */ - - $this->assertInstanceOf(User::class, $user); - } - - /** - * @covers \eZ\Publish\API\Repository\UserService::updateUser - */ - public function testUpdateUserByUserWithLimitations(): void - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $user = $this->createTestUserWithPassword('H@xxxiR!_1', $this->createUserContentTypeWithStrongPassword()); - - $currentUser = $this->createUserWithPolicies( - 'user', - [ - ['module' => 'content', 'function' => 'edit'], - ['module' => 'content', 'function' => 'read'], - ['module' => 'content', 'function' => 'versionread'], - ['module' => 'content', 'function' => 'publish'], - ['module' => 'user', 'function' => 'password'], - ], - new SubtreeLimitation(['limitationValues' => ['/1/5']]) - ); - $repository->getPermissionResolver()->setCurrentUserReference($currentUser); - - // Create a new update struct instance - $userUpdate = $userService->newUserUpdateStruct(); - $userUpdate->password = 'H@xxxiR!_2'; - - $user = $userService->updateUser($user, $userUpdate); - - self::assertInstanceOf(User::class, $user); - } - - /** - * @covers \eZ\Publish\API\Repository\UserService::updateUserPassword - */ - public function testUpdateUserPasswordWorksWithUserPasswordRole(): void - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - $permissionResolver = $repository->getPermissionResolver(); - - $this->createRoleWithPolicies('CanChangePassword', [ - ['module' => 'user', 'function' => 'password'], - ]); - - $user = $this->createCustomUserWithLogin( - 'with_role_password', - 'with_role_password@example.com', - 'Anons', - 'CanChangePassword' - ); - $previousHash = $user->passwordHash; - - $permissionResolver->setCurrentUserReference($user); - - $userService->updateUserPassword($user, 'new password'); - - $user = $userService->loadUserByLogin('with_role_password'); - $this->assertNotEquals($previousHash, $user->passwordHash); - } - - /** - * @throws \Doctrine\DBAL\Exception - * @throws \ErrorException - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testUpdateUserPasswordWithUnsupportedHashType(): void - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $user = $this->createUser('john.doe', 'John', 'Doe'); - $oldPasswordHash = $user->passwordHash; - - $wrongHashType = 1; - $this->updateRawPasswordHash($user->getUserId(), $wrongHashType); - $newPassword = 'new_secret123'; - // no need to invalidate cache since there was no load between create & raw database update - $user = $userService->updateUserPassword($user, $newPassword); - - self::assertTrue($userService->checkUserCredentials($user, $newPassword)); - self::assertNotEquals($oldPasswordHash, $user->passwordHash); - } - - /** - * Test for the loadUserGroupsOfUser() method. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUserGroupsOfUser - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - */ - public function testLoadUserGroupsOfUser() - { - $repository = $this->getRepository(); - - $userService = $repository->getUserService(); - - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // This array will contain the "Editors" user group name - $userGroupNames = []; - foreach ($userService->loadUserGroupsOfUser($user) as $userGroup) { - $this->assertInstanceOf(UserGroup::class, $userGroup); - $userGroupNames[] = $userGroup->getFieldValue('name'); - } - /* END: Use Case */ - - $this->assertEquals(['Editors'], $userGroupNames); - } - - /** - * Test for the loadUsersOfUserGroup() method. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUsersOfUserGroup - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser - */ - public function testLoadUsersOfUserGroup() - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $group = $userService->loadUserGroup($this->generateId('group', 13)); - - /* BEGIN: Use Case */ - $this->createUserVersion1(); - - $this->refreshSearch($repository); - - // This array will contain the email of the newly created "Editor" user - $email = []; - foreach ($userService->loadUsersOfUserGroup($group) as $user) { - $this->assertInstanceOf(User::class, $user); - $email[] = $user->email; - } - /* END: Use Case */ - $this->assertEquals(['user@example.com'], $email); - } - - /** - * Test for the assignUserToUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::assignUserToUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserGroupsOfUser - */ - public function testAssignUserToUserGroup() - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $administratorGroupId = $this->generateId('group', 12); - /* BEGIN: Use Case */ - // $administratorGroupId is the ID of the "Administrator" group in an - // eZ Publish demo installation - - $user = $this->createUserVersion1(); - - // Assign group to newly created user - $userService->assignUserToUserGroup( - $user, - $userService->loadUserGroup($administratorGroupId) - ); - - // This array will contain "Editors" and "Administrator users" - $userGroupNames = []; - foreach ($userService->loadUserGroupsOfUser($user) as $userGroup) { - $userGroupNames[] = $userGroup->getFieldValue('name'); - } - /* END: Use Case */ - - sort($userGroupNames, SORT_STRING); - - $this->assertEquals( - [ - 'Administrator users', - 'Editors', - ], - $userGroupNames - ); - } - - /** - * Test for the assignUserToUserGroup() method. - * - * @covers \eZ\Publish\API\Repository\UserService::assignUserToUserGroup - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testAssignUserToUserGroup - */ - public function testAssignUserToUserGroupThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'user\' is invalid: User is already in the given User Group'); - - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $editorsGroupId = $this->generateId('group', 13); - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - // $editorsGroupId is the ID of the "Editors" group in an - // eZ Publish demo installation - - // This call will fail with an "InvalidArgumentException", because the - // user is already assigned to the "Editors" group - $userService->assignUserToUserGroup( - $user, - $userService->loadUserGroup($editorsGroupId) - ); - /* END: Use Case */ - } - - /** - * @covers \eZ\Publish\API\Repository\UserService::assignUserToUserGroup - */ - public function testAssignUserToGroupWithLocationsValidation(): void - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - $locationService = $repository->getLocationService(); - - $administratorGroupId = $this->generateId('group', 12); - - $user = $this->createUserVersion1(); - - $group = $userService->loadUserGroup($administratorGroupId); - $groupLocation = $locationService->loadLocation($group->contentInfo->mainLocationId); - - // Count number of child locations before assigning user to group - $count = $locationService->getLocationChildCount($groupLocation); - $expectedCount = $count + 1; - - $userService->assignUserToUserGroup( - $user, - $group - ); - - $this->refreshSearch($repository); - - // Count number of child locations after assigning the user to a group - $actualCount = $locationService->getLocationChildCount($groupLocation); - - self::assertEquals($expectedCount, $actualCount); - } - - /** - * Test for the unAssignUssrFromUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::unAssignUssrFromUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserGroupsOfUser - */ - public function testUnAssignUserFromUserGroup() - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $editorsGroupId = $this->generateId('group', 13); - $anonymousGroupId = $this->generateId('group', 42); - - /* BEGIN: Use Case */ - // $anonymousGroupId is the ID of the "Anonymous Users" group in an eZ - // Publish demo installation - - $user = $this->createUserVersion1(); - - // Assign group to newly created user - $userService->assignUserToUserGroup( - $user, - $userService->loadUserGroup($anonymousGroupId) - ); - - // Unassign user from "Editors" group - $userService->unAssignUserFromUserGroup( - $user, - $userService->loadUserGroup($editorsGroupId) - ); - - // This array will contain "Anonymous Users" - $userGroupNames = []; - foreach ($userService->loadUserGroupsOfUser($user) as $userGroup) { - $userGroupNames[] = $userGroup->getFieldValue('name'); - } - /* END: Use Case */ - - $this->assertEquals(['Anonymous Users'], $userGroupNames); - } - - /** - * Test for the unAssignUserFromUserGroup() method. - * - * @see \eZ\Publish\API\Repository\UserService::unAssignUserFromUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testUnAssignUserFromUserGroup - */ - public function testUnAssignUserFromUserGroupThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $administratorGroupId = $this->generateId('group', 12); - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - // $administratorGroupId is the ID of the "Administrator" group in an - // eZ Publish demo installation - - // This call will fail with an "InvalidArgumentException", because the - // user is not assigned to the "Administrator" group - $userService->unAssignUserFromUserGroup( - $user, - $userService->loadUserGroup($administratorGroupId) - ); - /* END: Use Case */ - } - - /** - * Test for the unAssignUserFromUserGroup() method removing user from the last group. - * - * @covers \eZ\Publish\API\Repository\UserService::unAssignUserFromUserGroup - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testUnAssignUserFromUserGroup - */ - public function testUnAssignUserFromUserGroupThrowsBadStateArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class); - $this->expectExceptionMessage('Argument \'user\' has a bad state: User only has one User Group, cannot unassign from last group'); - - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $editorsGroupId = $this->generateId('group', 13); - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - // This call will fail with an "BadStateException", because the - // user has to be assigned to at least one group - $userService->unAssignUserFromUserGroup( - $user, - $userService->loadUserGroup($editorsGroupId) - ); - /* END: Use Case */ - } - - /** - * @covers \eZ\Publish\API\Repository\UserService::unAssignUserFromUserGroup - */ - public function testUnAssignUserToGroupWithLocationValidation(): void - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - $locationService = $repository->getLocationService(); - - $editorsGroupId = $this->generateId('group', 13); - $anonymousGroupId = $this->generateId('group', 42); - - $user = $this->createUserVersion1(); - - $this->refreshSearch($repository); - - $group = $userService->loadUserGroup($editorsGroupId); - $groupLocation = $locationService->loadLocation($group->contentInfo->mainLocationId); - - // Count number of child locations before unassigning the user from a group - $count = $locationService->getLocationChildCount($groupLocation); - $expectedCount = $count - 1; - - // Assigning user to a different group to avoid removing all groups from the user - $userService->assignUserToUserGroup( - $user, - $userService->loadUserGroup($anonymousGroupId) - ); - - $userService->unAssignUserFromUserGroup( - $user, - $userService->loadUserGroup($editorsGroupId) - ); - - $this->refreshSearch($repository); - - // Count number of child locations after unassigning the user from a group - $actualCount = $locationService->getLocationChildCount($groupLocation); - - self::assertEquals($expectedCount, $actualCount); - } - - /** - * Test that multi-language logic for the loadUserGroup method respects prioritized language list. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUserGroup - * @dataProvider getPrioritizedLanguageList - * - * @param string[] $prioritizedLanguages - * @param string|null $expectedLanguageCode language code of expected translation - */ - public function testLoadUserGroupWithPrioritizedLanguagesList( - array $prioritizedLanguages, - $expectedLanguageCode - ) { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $userGroup = $this->createMultiLanguageUserGroup(); - if ($expectedLanguageCode === null) { - $expectedLanguageCode = $userGroup->contentInfo->mainLanguageCode; - } - - $loadedUserGroup = $userService->loadUserGroup($userGroup->id, $prioritizedLanguages); - - self::assertEquals( - $loadedUserGroup->getName($expectedLanguageCode), - $loadedUserGroup->getName() - ); - self::assertEquals( - $loadedUserGroup->getFieldValue('description', $expectedLanguageCode), - $loadedUserGroup->getFieldValue('description') - ); - } - - /** - * Test that multi-language logic works correctly after updating user group main language. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUserGroup - * @dataProvider getPrioritizedLanguageList - * - * @param string[] $prioritizedLanguages - * @param string|null $expectedLanguageCode language code of expected translation - */ - public function testLoadUserGroupWithPrioritizedLanguagesListAfterMainLanguageUpdate( - array $prioritizedLanguages, - $expectedLanguageCode - ) { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - $contentService = $repository->getContentService(); - - $userGroup = $this->createMultiLanguageUserGroup(); - - $userGroupUpdateStruct = $userService->newUserGroupUpdateStruct(); - $userGroupUpdateStruct->contentMetadataUpdateStruct = $contentService->newContentMetadataUpdateStruct(); - $userGroupUpdateStruct->contentMetadataUpdateStruct->mainLanguageCode = 'eng-GB'; - $userService->updateUserGroup($userGroup, $userGroupUpdateStruct); - - if ($expectedLanguageCode === null) { - $expectedLanguageCode = 'eng-GB'; - } - - $loadedUserGroup = $userService->loadUserGroup($userGroup->id, $prioritizedLanguages); - - self::assertEquals( - $loadedUserGroup->getName($expectedLanguageCode), - $loadedUserGroup->getName() - ); - self::assertEquals( - $loadedUserGroup->getFieldValue('description', $expectedLanguageCode), - $loadedUserGroup->getFieldValue('description') - ); - } - - /** - * Test that multi-language logic for the loadSubUserGroups method respects prioritized language list. - * - * @covers \eZ\Publish\API\Repository\UserService::loadSubUserGroups - * @dataProvider getPrioritizedLanguageList - * - * @param string[] $prioritizedLanguages - * @param string|null $expectedLanguageCode language code of expected translation - */ - public function testLoadSubUserGroupsWithPrioritizedLanguagesList( - array $prioritizedLanguages, - $expectedLanguageCode - ) { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - // create main group for subgroups - $userGroup = $this->createMultiLanguageUserGroup(4); - if ($expectedLanguageCode === null) { - $expectedLanguageCode = $userGroup->contentInfo->mainLanguageCode; - } - - // create subgroups - $this->createMultiLanguageUserGroup($userGroup->id); - $this->createMultiLanguageUserGroup($userGroup->id); - - $userGroup = $userService->loadUserGroup($userGroup->id, $prioritizedLanguages); - - $subUserGroups = $userService->loadSubUserGroups($userGroup, 0, 2, $prioritizedLanguages); - foreach ($subUserGroups as $subUserGroup) { - self::assertEquals( - $subUserGroup->getName($expectedLanguageCode), - $subUserGroup->getName() - ); - self::assertEquals( - $subUserGroup->getFieldValue('description', $expectedLanguageCode), - $subUserGroup->getFieldValue('description') - ); - } - } - - /** - * Test that multi-language logic for the loadUser method respects prioritized language list. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUser - * @dataProvider getPrioritizedLanguageList - * - * @param string[] $prioritizedLanguages - * @param string|null $expectedLanguageCode language code of expected translation - */ - public function testLoadUserWithPrioritizedLanguagesList( - array $prioritizedLanguages, - $expectedLanguageCode - ) { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $user = $this->createMultiLanguageUser(); - if ($expectedLanguageCode === null) { - $expectedLanguageCode = $user->contentInfo->mainLanguageCode; - } - - $loadedUser = $userService->loadUser($user->id, $prioritizedLanguages); - - self::assertEquals( - $loadedUser->getName($expectedLanguageCode), - $loadedUser->getName() - ); - - foreach (['fist_name', 'last_name', 'signature'] as $fieldIdentifier) { - self::assertEquals( - $loadedUser->getFieldValue($fieldIdentifier, $expectedLanguageCode), - $loadedUser->getFieldValue($fieldIdentifier) - ); - } - } - - /** - * Test that multi-language logic for the loadUser method works correctly after updating - * user content main language. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUserGroup - * @dataProvider getPrioritizedLanguageList - * - * @param string[] $prioritizedLanguages - * @param string|null $expectedLanguageCode language code of expected translation - */ - public function testLoadUserWithPrioritizedLanguagesListAfterMainLanguageUpdate( - array $prioritizedLanguages, - $expectedLanguageCode - ) { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - $contentService = $repository->getContentService(); - - $user = $this->createMultiLanguageUser(); - // sanity check - self::assertEquals($user->contentInfo->mainLanguageCode, 'eng-US'); - - $userUpdateStruct = $userService->newUserUpdateStruct(); - $userUpdateStruct->contentMetadataUpdateStruct = $contentService->newContentMetadataUpdateStruct(); - $userUpdateStruct->contentMetadataUpdateStruct->mainLanguageCode = 'eng-GB'; - $userService->updateUser($user, $userUpdateStruct); - if ($expectedLanguageCode === null) { - $expectedLanguageCode = 'eng-GB'; - } - - $loadedUser = $userService->loadUser($user->id, $prioritizedLanguages); - - self::assertEquals( - $loadedUser->getName($expectedLanguageCode), - $loadedUser->getName() - ); - - foreach (['fist_name', 'last_name', 'signature'] as $fieldIdentifier) { - self::assertEquals( - $loadedUser->getFieldValue($fieldIdentifier, $expectedLanguageCode), - $loadedUser->getFieldValue($fieldIdentifier) - ); - } - } - - /** - * Test that multi-language logic for the loadUserByLogin method respects prioritized language list. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUserByLogin - * @dataProvider getPrioritizedLanguageList - * - * @param string[] $prioritizedLanguages - * @param string|null $expectedLanguageCode language code of expected translation - */ - public function testLoadUserByLoginWithPrioritizedLanguagesList( - array $prioritizedLanguages, - $expectedLanguageCode - ) { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - $user = $this->createMultiLanguageUser(); - - // load, with prioritized languages, the newly created user - $loadedUser = $userService->loadUserByLogin($user->login, $prioritizedLanguages); - if ($expectedLanguageCode === null) { - $expectedLanguageCode = $loadedUser->contentInfo->mainLanguageCode; - } - - self::assertEquals( - $loadedUser->getName($expectedLanguageCode), - $loadedUser->getName() - ); - - foreach (['first_name', 'last_name', 'signature'] as $fieldIdentifier) { - self::assertEquals( - $loadedUser->getFieldValue($fieldIdentifier, $expectedLanguageCode), - $loadedUser->getFieldValue($fieldIdentifier) - ); - } - } - - /** - * Test that multi-language logic for the loadUsersByEmail method respects - * prioritized language list. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUsersByEmail - * @dataProvider getPrioritizedLanguageList - * - * @param string[] $prioritizedLanguages - * @param string|null $expectedLanguageCode language code of expected translation - */ - public function testLoadUsersByEmailWithPrioritizedLanguagesList( - array $prioritizedLanguages, - $expectedLanguageCode - ) { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - $user = $this->createMultiLanguageUser(); - - // load, with prioritized languages, users by email - $loadedUsers = $userService->loadUsersByEmail($user->email, $prioritizedLanguages); - - foreach ($loadedUsers as $loadedUser) { - if ($expectedLanguageCode === null) { - $expectedLanguageCode = $loadedUser->contentInfo->mainLanguageCode; - } - self::assertEquals( - $loadedUser->getName($expectedLanguageCode), - $loadedUser->getName() - ); - - foreach (['first_name', 'last_name', 'signature'] as $fieldIdentifier) { - self::assertEquals( - $loadedUser->getFieldValue($fieldIdentifier, $expectedLanguageCode), - $loadedUser->getFieldValue($fieldIdentifier) - ); - } - } - } - - /** - * Test that multi-language logic for the loadUserGroupsOfUser method respects - * prioritized language list. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUserGroupsOfUser - * @dataProvider getPrioritizedLanguageList - * - * @param string[] $prioritizedLanguages - * @param string|null $expectedLanguageCode language code of expected translation - */ - public function testLoadUserGroupsOfUserWithPrioritizedLanguagesList( - array $prioritizedLanguages, - $expectedLanguageCode - ) { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - $userGroup = $this->createMultiLanguageUserGroup(); - $user = $this->createMultiLanguageUser($userGroup->id); - - $userGroups = $userService->loadUserGroupsOfUser($user, 0, 25, $prioritizedLanguages); - foreach ($userGroups as $userGroup) { - self::assertEquals( - $userGroup->getName($expectedLanguageCode), - $userGroup->getName() - ); - self::assertEquals( - $userGroup->getFieldValue('description', $expectedLanguageCode), - $userGroup->getFieldValue('description') - ); - } - } - - /** - * Test that multi-language logic for the loadUsersOfUserGroup method respects - * prioritized language list. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUsersOfUserGroup - * @dataProvider getPrioritizedLanguageList - * - * @param string[] $prioritizedLanguages - * @param string|null $expectedLanguageCode language code of expected translation - */ - public function testLoadUsersOfUserGroupWithPrioritizedLanguagesList( - array $prioritizedLanguages, - $expectedLanguageCode - ) { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - // create parent user group - $userGroup = $this->createMultiLanguageUserGroup(); - // add two users to the created parent user group - $this->createMultiLanguageUser($userGroup->id); - $this->createMultiLanguageUser($userGroup->id); - - // test loading of users via user group with prioritized languages list - $users = $userService->loadUsersOfUserGroup($userGroup, 0, 25, $prioritizedLanguages); - foreach ($users as $user) { - if ($expectedLanguageCode === null) { - $expectedLanguageCode = $user->contentInfo->mainLanguageCode; - } - self::assertEquals( - $user->getName($expectedLanguageCode), - $user->getName() - ); - - foreach (['first_name', 'last_name', 'signature'] as $fieldIdentifier) { - self::assertEquals( - $user->getFieldValue($fieldIdentifier, $expectedLanguageCode), - $user->getFieldValue($fieldIdentifier) - ); - } - } - } - - /** - * Get prioritized languages list data. - * - * Test cases using this data provider should expect the following arguments: - * <code> - * array $prioritizedLanguagesList - * string $expectedLanguage (if null - use main language) - * </code> - * - * @return array - */ - public function getPrioritizedLanguageList() - { - return [ - [[], null], - [['eng-US'], 'eng-US'], - [['eng-GB'], 'eng-GB'], - [['eng-US', 'eng-GB'], 'eng-US'], - [['eng-GB', 'eng-US'], 'eng-GB'], - // use non-existent group as the first one - [['ger-DE'], null], - [['ger-DE', 'eng-GB'], 'eng-GB'], - ]; - } - - /** - * @param int $parentGroupId - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - */ - private function createMultiLanguageUserGroup($parentGroupId = 4) - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - // create user group with multiple translations - $parentGroupId = $this->generateId('group', $parentGroupId); - $parentGroup = $userService->loadUserGroup($parentGroupId); - - $userGroupCreateStruct = $userService->newUserGroupCreateStruct('eng-US'); - $userGroupCreateStruct->setField('name', 'US user group', 'eng-US'); - $userGroupCreateStruct->setField('name', 'GB user group', 'eng-GB'); - $userGroupCreateStruct->setField('description', 'US user group description', 'eng-US'); - $userGroupCreateStruct->setField('description', 'GB user group description', 'eng-GB'); - $userGroupCreateStruct->alwaysAvailable = true; - - return $userService->createUserGroup($userGroupCreateStruct, $parentGroup); - } - - /** - * Create a user group fixture in a variable named <b>$userGroup</b>,. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - */ - private function createUserGroupVersion1() - { - $repository = $this->getRepository(); - - $mainGroupId = $this->generateId('group', 4); - /* BEGIN: Inline */ - // $mainGroupId is the ID of the main "Users" group - - $userService = $repository->getUserService(); - - // Load main group - $parentUserGroup = $userService->loadUserGroup($mainGroupId); - - // Instantiate a new create struct - $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); - $userGroupCreate->setField('name', 'Example Group'); - - // Create the new user group - $userGroup = $userService->createUserGroup( - $userGroupCreate, - $parentUserGroup - ); - /* END: Inline */ - - return $userGroup; - } - - /** - * Create user with multiple translations of User Content fields. - * - * @param int $userGroupId User group ID (default 13 - Editors) - * - * @return \eZ\Publish\API\Repository\Values\User\User - */ - private function createMultiLanguageUser($userGroupId = 13) - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - // Instantiate a create struct with mandatory properties - $randomLogin = md5(mt_rand() . time()); - $userCreateStruct = $userService->newUserCreateStruct( - $randomLogin, - "{$randomLogin}@example.com", - 'secret', - 'eng-US' - ); - $userCreateStruct->enabled = true; - $userCreateStruct->alwaysAvailable = true; - - // set field for each language - foreach (['eng-US', 'eng-GB'] as $languageCode) { - $userCreateStruct->setField('first_name', "{$languageCode} Example", $languageCode); - $userCreateStruct->setField('last_name', "{$languageCode} User", $languageCode); - $userCreateStruct->setField('signature', "{$languageCode} signature", $languageCode); - } - - // Load parent group for the user - $group = $userService->loadUserGroup($userGroupId); - - // Create a new user - return $userService->createUser($userCreateStruct, [$group]); - } - - /** - * Test for the createUser() method. - * - * @see \eZ\Publish\API\Repository\UserService::createUser() - */ - public function testCreateUserWithDefaultPasswordHashTypeWhenHashTypeIsUnsupported(): void - { - $repository = $this->getRepository(); - $eventUserService = $repository->getUserService(); - - // Instantiate a create struct with mandatory properties. - $createStruct = $eventUserService->newUserCreateStruct( - 'user', - 'user@example.com', - 'secret', - 'eng-US' - ); - - // Set some fields required by the user ContentType. - $createStruct->setField('first_name', 'Example'); - $createStruct->setField('last_name', 'User'); - - // Get User fieldType. - $userFieldDef = null; - foreach ($createStruct->fields as $field) { - if ($field->fieldTypeIdentifier === 'ezuser') { - $userFieldDef = $field; - break; - } - } - - if (!$userFieldDef) { - $this->fail('User FieldType not found in userCreateStruct!'); - } - - /** @var \eZ\Publish\Core\FieldType\User\Value $userValue */ - $userValue = $userFieldDef->value; - - // Set not supported hash type. - $userValue->passwordHashType = 42424242; - - // Create a new user instance. - // 13 is ID of the "Editors" user group in an eZ Publish demo installation. - $createdUser = $eventUserService->createUser($createStruct, [$eventUserService->loadUserGroup(13)]); - - self::assertEquals(User::DEFAULT_PASSWORD_HASH, $createdUser->hashAlgorithm); - } - - /** - * Test loading User by Token. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUserByToken - */ - public function testLoadUserByToken() - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $user = $this->createUserVersion1(); - - $userTokenUpdateStruct = new UserTokenUpdateStruct(); - $userTokenUpdateStruct->hashKey = md5('hash'); - $userTokenUpdateStruct->time = (new DateTime())->add(new DateInterval('PT1H')); - - $userService->updateUserToken($user, $userTokenUpdateStruct); - - $loadedUser = $userService->loadUserByToken($userTokenUpdateStruct->hashKey, Language::ALL); - self::assertEquals($user, $loadedUser); - - return $userTokenUpdateStruct->hashKey; - } - - /** - * Test updating User Token. - * - * @covers \eZ\Publish\API\Repository\UserService::updateUserToken() - * - * @depends testLoadUserByToken - * - * @param string $originalUserToken - */ - public function testUpdateUserToken($originalUserToken) - { - $repository = $this->getRepository(false); - $userService = $repository->getUserService(); - - $user = $userService->loadUserByToken($originalUserToken); - - $userTokenUpdateStruct = new UserTokenUpdateStruct(); - $userTokenUpdateStruct->hashKey = md5('my_updated_hash'); - $userTokenUpdateStruct->time = (new DateTime())->add(new DateInterval('PT1H')); - - $userService->updateUserToken($user, $userTokenUpdateStruct); - - $loadedUser = $userService->loadUserByToken($userTokenUpdateStruct->hashKey); - self::assertEquals($user, $loadedUser); - } - - /** - * Test invalidating (expiring) User Token. - * - * @covers \eZ\Publish\API\Repository\UserService::expireUserToken() - * - * @depends testLoadUserByToken - * - * @param string $userToken - */ - public function testExpireUserToken($userToken) - { - $this->expectException(NotFoundException::class); - - $repository = $this->getRepository(false); - $userService = $repository->getUserService(); - - // sanity check - $userService->loadUserByToken($userToken); - - $userService->expireUserToken($userToken); - - // should throw NotFoundException now - $userService->loadUserByToken($userToken); - } - - /** - * Test trying to load User by invalid Token. - * - * @covers \eZ\Publish\API\Repository\UserService::loadUserByToken - */ - public function testLoadUserByTokenThrowsNotFoundException(): void - { - $repository = $this->getRepository(); - $userService = $repository->getUserService(); - - $this->expectException(NotFoundException::class); - $userService->loadUserByToken('not_existing_token'); - } - - /** - * @covers \eZ\Publish\API\Repository\UserService::validatePassword() - */ - public function testValidatePasswordWithDefaultContext() - { - $userService = $this->getRepository()->getUserService(); - - /* BEGIN: Use Case */ - $errors = $userService->validatePassword('pass'); - /* END: Use Case */ - - $this->assertEmpty($errors); - } - - /** - * @covers \eZ\Publish\API\Repository\UserService::validatePassword() - * @dataProvider dataProviderForValidatePassword - */ - public function testValidatePassword(string $password, array $expectedErrors) - { - $userService = $this->getRepository()->getUserService(); - $contentType = $this->createUserContentTypeWithStrongPassword(); - - /* BEGIN: Use Case */ - $context = new PasswordValidationContext([ - 'contentType' => $contentType, - ]); - - $actualErrors = $userService->validatePassword($password, $context); - /* END: Use Case */ - - $this->assertEquals($expectedErrors, $actualErrors); - } - - public function testValidatePasswordReturnsErrorWhenOldPasswordIsReused(): void - { - $password = 'P@blish123!'; - - $userService = $this->getRepository()->getUserService(); - // Password expiration needs to be enabled - $contentType = $this->createUserContentTypeWithPasswordExpirationDate(); - - $user = $this->createTestUserWithPassword($password, $contentType); - - $context = new PasswordValidationContext([ - 'contentType' => $contentType, - 'user' => $user, - ]); - - $actualErrors = $userService->validatePassword($password, $context); - - $this->assertEquals( - [new ValidationError('New password cannot be the same as old password', null, [], 'password')], - $actualErrors - ); - } - - public function getDataForTestPasswordUpdateRespectsAllValidationSettings(): iterable - { - $oldPassword = 'P@blish123!'; - - yield 'require at least one upper case character' => [ - $oldPassword, - 'p@blish123!', - 'User password must include at least one upper case letter', - ]; - - yield 'require at least one lower case character' => [ - $oldPassword, - 'P@BLISH123!', - 'User password must include at least one lower case letter', - ]; - - yield 'require at least one numeric character' => [ - $oldPassword, - 'P@blishONETWOTHREE!', - 'User password must include at least one number', - ]; - - yield 'require at least one non-alphanumeric character' => [ - $oldPassword, - 'Publish123', - 'User password must include at least one special character', - ]; - - yield 'require min. length >= 8 chars' => [ - $oldPassword, - 'P@b123!', - 'User password must be at least 8 characters long', - ]; - - yield 'require new password' => [ - $oldPassword, - $oldPassword, - 'New password cannot be the same as old password', - ]; - } - - /** - * @dataProvider getDataForTestPasswordUpdateRespectsAllValidationSettings - * - * @throws \eZ\Publish\API\Repository\Exceptions\Exception - * @throws \Exception - */ - public function testUpdateUserPasswordPerformsValidation( - string $oldPassword, - string $newPassword, - string $expectedExceptionMessage - ): void { - $userService = $this->getRepository()->getUserService(); - - $contentType = $this->createUserContentTypeWithStrongPassword(); - $user = $this->createTestUserWithPassword($oldPassword, $contentType); - - try { - $userService->updateUserPassword($user, $newPassword); - - self::fail( - sprintf( - 'Failed to get validation exception with message "%s"', - $expectedExceptionMessage - ) - ); - } catch (ContentFieldValidationException $e) { - $this->assertValidationErrorOccurs($e, $expectedExceptionMessage); - } - } - - /** - * Data provider for testValidatePassword. - * - * @return array - */ - public function dataProviderForValidatePassword(): array - { - return [ - [ - 'pass', - [ - new ValidationError('User password must be at least %length% characters long', null, [ - '%length%' => 8, - ], 'password'), - new ValidationError('User password must include at least one upper case letter', null, [], 'password'), - new ValidationError('User password must include at least one number', null, [], 'password'), - new ValidationError('User password must include at least one special character', null, [], 'password'), - ], - ], - [ - 'H@xxxi0R!!!', - [], - ], - ]; - } - - public function testGetPasswordInfo(): void - { - $userService = $this->getRepository()->getUserService(); - $contentType = $this->createUserContentTypeWithPasswordExpirationDate( - self::EXAMPLE_PASSWORD_TTL, - self::EXAMPLE_PASSWORD_TTL_WARNING - ); - - $user = $this->createTestUser($contentType); - - /* BEGIN: Use Case */ - $passwordInfo = $userService->getPasswordInfo($user); - /* END: Use Case */ - - $passwordUpdatedAt = $user->passwordUpdatedAt; - if ($passwordUpdatedAt instanceof DateTime) { - $passwordUpdatedAt = DateTimeImmutable::createFromFormat(DateTime::ATOM, $passwordUpdatedAt->format(DateTime::ATOM)); - } - - $expectedPasswordExpirationDate = $passwordUpdatedAt->add( - new DateInterval(sprintf('P%dD', self::EXAMPLE_PASSWORD_TTL)) - ); - - $expectedPasswordExpirationWarningDate = $passwordUpdatedAt->add( - new DateInterval(sprintf('P%dD', self::EXAMPLE_PASSWORD_TTL - self::EXAMPLE_PASSWORD_TTL_WARNING)) - ); - - $this->assertEquals(new PasswordInfo( - $expectedPasswordExpirationDate, - $expectedPasswordExpirationWarningDate - ), $passwordInfo); - } - - public function testGetPasswordInfoIfExpirationIsDisabled(): void - { - $userService = $this->getRepository()->getUserService(); - $contentType = $this->createUserContentTypeWithPasswordExpirationDate(null, null); - - $user = $this->createTestUser($contentType); - - /* BEGIN: Use Case */ - $passwordInfo = $userService->getPasswordInfo($user); - /* END: Use Case */ - - $this->assertEquals(new PasswordInfo(), $passwordInfo); - } - - public function testGetPasswordInfoIfExpirationWarningIsDisabled(): void - { - $userService = $this->getRepository()->getUserService(); - $contentType = $this->createUserContentTypeWithPasswordExpirationDate(self::EXAMPLE_PASSWORD_TTL, null); - - $user = $this->createTestUser($contentType); - - /* BEGIN: Use Case */ - $passwordInfo = $userService->getPasswordInfo($user); - /* END: Use Case */ - - $passwordUpdatedAt = $user->passwordUpdatedAt; - if ($passwordUpdatedAt instanceof DateTime) { - $passwordUpdatedAt = DateTimeImmutable::createFromFormat(DateTime::ATOM, $passwordUpdatedAt->format(DateTime::ATOM)); - } - - $expectedPasswordExpirationDate = $passwordUpdatedAt->add( - new DateInterval(sprintf('P%dD', self::EXAMPLE_PASSWORD_TTL)) - ); - - $this->assertEquals(new PasswordInfo($expectedPasswordExpirationDate, null), $passwordInfo); - } - - public function createTestUser(ContentType $contentType): User - { - return $this->createTestUserWithPassword(self::EXAMPLE_PASSWORD, $contentType); - } - - /** - * Creates a user with given password. - * - * @param string $password - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * - * @return \eZ\Publish\API\Repository\Values\User\User - */ - private function createTestUserWithPassword(string $password, ContentType $contentType): User - { - $userService = $this->getRepository()->getUserService(); - // ID of the "Editors" user group in an eZ Publish demo installation - $editorsGroupId = 13; - - // Instantiate a create struct with mandatory properties - $userCreate = $userService->newUserCreateStruct( - 'johndoe', - 'johndoe@example.com', - $password, - 'eng-US', - $contentType - ); - $userCreate->enabled = true; - $userCreate->setField('first_name', 'John'); - $userCreate->setField('last_name', 'Doe'); - - return $userService->createUser($userCreate, [ - $userService->loadUserGroup($editorsGroupId), - ]); - } - - /** - * Creates the User Content Type with password constraints. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType - */ - private function createUserContentTypeWithStrongPassword(): ContentType - { - return $this->createUserContentTypeWithAccountSettings('user-with-strong-password', null, [ - 'PasswordValueValidator' => [ - 'requireAtLeastOneUpperCaseCharacter' => 1, - 'requireAtLeastOneLowerCaseCharacter' => 1, - 'requireAtLeastOneNumericCharacter' => 1, - 'requireAtLeastOneNonAlphanumericCharacter' => 1, - 'requireNewPassword' => 1, - 'minLength' => 8, - ], - ]); - } - - private function createUserContentTypeWithPasswordExpirationDate( - ?int $passwordTTL = self::EXAMPLE_PASSWORD_TTL, - ?int $passwordTTLWarning = self::EXAMPLE_PASSWORD_TTL_WARNING - ): ContentType { - return $this->createUserContentTypeWithAccountSettings('password-expiration', [ - 'PasswordTTL' => $passwordTTL, - 'PasswordTTLWarning' => $passwordTTLWarning, - ]); - } - - private function createUserContentTypeWithAccountSettings( - string $identifier, - ?array $fieldSetting = null, - ?array $validatorConfiguration = null - ): ContentType { - $repository = $this->getRepository(); - - $contentTypeService = $repository->getContentTypeService(); - $permissionResolver = $repository->getPermissionResolver(); - - $typeCreate = $contentTypeService->newContentTypeCreateStruct($identifier); - $typeCreate->mainLanguageCode = 'eng-GB'; - $typeCreate->urlAliasSchema = 'url|scheme'; - $typeCreate->nameSchema = 'name|scheme'; - $typeCreate->names = [ - 'eng-GB' => 'User: ' . $identifier, - ]; - $typeCreate->descriptions = [ - 'eng-GB' => '', - ]; - $typeCreate->creatorId = $this->generateId('user', $permissionResolver->getCurrentUserReference()->getUserId()); - $typeCreate->creationDate = $this->createDateTime(); - - $firstNameFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('first_name', 'ezstring'); - $firstNameFieldCreate->names = [ - 'eng-GB' => 'First name', - ]; - $firstNameFieldCreate->descriptions = [ - 'eng-GB' => '', - ]; - $firstNameFieldCreate->fieldGroup = 'default'; - $firstNameFieldCreate->position = 1; - $firstNameFieldCreate->isTranslatable = false; - $firstNameFieldCreate->isRequired = true; - $firstNameFieldCreate->isInfoCollector = false; - $firstNameFieldCreate->validatorConfiguration = [ - 'StringLengthValidator' => [ - 'minStringLength' => 0, - 'maxStringLength' => 0, - ], - ]; - $firstNameFieldCreate->fieldSettings = []; - $firstNameFieldCreate->isSearchable = true; - $firstNameFieldCreate->defaultValue = ''; - - $typeCreate->addFieldDefinition($firstNameFieldCreate); - - $lastNameFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('last_name', 'ezstring'); - $lastNameFieldCreate->names = [ - 'eng-GB' => 'Last name', - ]; - $lastNameFieldCreate->descriptions = [ - 'eng-GB' => '', - ]; - $lastNameFieldCreate->fieldGroup = 'default'; - $lastNameFieldCreate->position = 2; - $lastNameFieldCreate->isTranslatable = false; - $lastNameFieldCreate->isRequired = true; - $lastNameFieldCreate->isInfoCollector = false; - $lastNameFieldCreate->validatorConfiguration = [ - 'StringLengthValidator' => [ - 'minStringLength' => 0, - 'maxStringLength' => 0, - ], - ]; - $lastNameFieldCreate->fieldSettings = []; - $lastNameFieldCreate->isSearchable = true; - $lastNameFieldCreate->defaultValue = ''; - - $typeCreate->addFieldDefinition($lastNameFieldCreate); - - $accountFieldCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct('user_account', 'ezuser'); - $accountFieldCreateStruct->names = [ - 'eng-GB' => 'User account', - ]; - $accountFieldCreateStruct->descriptions = [ - 'eng-GB' => '', - ]; - $accountFieldCreateStruct->fieldGroup = 'default'; - $accountFieldCreateStruct->position = 3; - $accountFieldCreateStruct->isTranslatable = false; - $accountFieldCreateStruct->isRequired = true; - $accountFieldCreateStruct->isInfoCollector = false; - $accountFieldCreateStruct->validatorConfiguration = $validatorConfiguration; - $accountFieldCreateStruct->fieldSettings = $fieldSetting; - $accountFieldCreateStruct->isSearchable = false; - $accountFieldCreateStruct->defaultValue = null; - - $typeCreate->addFieldDefinition($accountFieldCreateStruct); - - $contentTypeDraft = $contentTypeService->createContentType($typeCreate, [ - $contentTypeService->loadContentTypeGroupByIdentifier('Users'), - ]); - $contentTypeService->publishContentTypeDraft($contentTypeDraft); - - return $contentTypeService->loadContentTypeByIdentifier($identifier); - } - - /** - * @throws \Doctrine\DBAL\Exception - * @throws \ErrorException - */ - protected function updateRawPasswordHash(int $userId, int $newHashType): void - { - $connection = $this->getRawDatabaseConnection(); - $queryBuilder = $connection->createQueryBuilder(); - $queryBuilder - ->update(Gateway::USER_TABLE) - ->set('password_hash_type', ':wrong_hash_type') - ->where('contentobject_id = :user_id') - ->setParameter('wrong_hash_type', $newHashType, ParameterType::INTEGER) - ->setParameter('user_id', $userId); - - $queryBuilder->execute(); - } -} diff --git a/eZ/Publish/API/Repository/Tests/Values/Content/LanguageTest.php b/eZ/Publish/API/Repository/Tests/Values/Content/LanguageTest.php deleted file mode 100644 index ee96462c9c..0000000000 --- a/eZ/Publish/API/Repository/Tests/Values/Content/LanguageTest.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\Values\Content; - -use eZ\Publish\API\Repository\Tests\Values\ValueObjectTestTrait; -use eZ\Publish\API\Repository\Values\Content\Language; -use PHPUnit\Framework\TestCase; - -class LanguageTest extends TestCase -{ - use ValueObjectTestTrait; - - /** - * Test default properties of just created class. - */ - public function testNewClass() - { - $language = new Language(); - - $this->assertPropertiesCorrect( - [ - 'id' => null, - 'languageCode' => null, - 'name' => null, - 'enabled' => null, - ], - $language - ); - } - - /** - * Test retrieving missing property. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Language::__get - */ - public function testMissingProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); - $this->expectExceptionMessage('Property \'notDefined\' not found on class'); - - $language = new Language(); - $value = $language->notDefined; - self::fail('Succeeded getting non existing property'); - } - - /** - * Test setting read only property. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Language::__set - */ - public function testReadOnlyProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - $this->expectExceptionMessage('Property \'id\' is readonly on class'); - - $language = new Language(); - $language->id = 42; - self::fail('Succeeded setting read only property'); - } - - /** - * Test if property exists. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Language::__isset - */ - public function testIsPropertySet() - { - $language = new Language(); - $value = isset($language->notDefined); - self::assertFalse($value); - - $value = isset($language->id); - self::assertTrue($value); - } - - /** - * Test unsetting a property. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Language::__unset - */ - public function testUnsetProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - $this->expectExceptionMessage('Property \'id\' is readonly on class'); - - $language = new Language(['id' => 2]); - unset($language->id); - self::fail('Unsetting read-only property succeeded'); - } -} diff --git a/eZ/Publish/API/Repository/Tests/Values/Content/Query/Aggregation/Location/SubtreeTermAggregationTest.php b/eZ/Publish/API/Repository/Tests/Values/Content/Query/Aggregation/Location/SubtreeTermAggregationTest.php deleted file mode 100644 index 638458f9b0..0000000000 --- a/eZ/Publish/API/Repository/Tests/Values/Content/Query/Aggregation/Location/SubtreeTermAggregationTest.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\Values\Content\Query\Aggregation\Location; - -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Location\SubtreeTermAggregation; -use PHPUnit\Framework\TestCase; - -final class SubtreeTermAggregationTest extends TestCase -{ - private const EXAMPLE_PATH_STRING = '/1/2/'; - private const EXAMPLE_AGGREGATION_NAME = 'foo'; - - public function testConstruct(): void - { - $aggregation = new SubtreeTermAggregation( - self::EXAMPLE_AGGREGATION_NAME, - self::EXAMPLE_PATH_STRING - ); - - $this->assertEquals(self::EXAMPLE_AGGREGATION_NAME, $aggregation->getName()); - $this->assertEquals(self::EXAMPLE_PATH_STRING, $aggregation->getPathString()); - } - - public function testConstructThrowsInvalidArgumentExceptionOnInvalidPathString(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectErrorMessage("'/INVALID/PATH' value must follow the path string format, e.g. /1/2/"); - - $aggregation = new SubtreeTermAggregation('foo', '/INVALID/PATH'); - } - - public function testFromLocation(): void - { - $location = $this->createMock(Location::class); - $location->method('__get')->with('pathString')->willReturn(self::EXAMPLE_PATH_STRING); - - $aggregation = SubtreeTermAggregation::fromLocation(self::EXAMPLE_AGGREGATION_NAME, $location); - - $this->assertEquals(self::EXAMPLE_AGGREGATION_NAME, $aggregation->getName()); - $this->assertEquals(self::EXAMPLE_PATH_STRING, $aggregation->getPathString()); - } -} diff --git a/eZ/Publish/API/Repository/Tests/Values/Content/Query/Aggregation/RangeTest.php b/eZ/Publish/API/Repository/Tests/Values/Content/Query/Aggregation/RangeTest.php deleted file mode 100644 index d0d42cc45c..0000000000 --- a/eZ/Publish/API/Repository/Tests/Values/Content/Query/Aggregation/RangeTest.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Tests\Values\Content\Query\Aggregation; - -use DateTimeImmutable; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Range; -use PHPUnit\Framework\TestCase; - -final class RangeTest extends TestCase -{ - /** - * @dataProvider dataProviderForTestToString - */ - public function testToString(Range $range, string $expected): void - { - $this->assertEquals($expected, (string)$range); - } - - public function dataProviderForTestToString(): iterable - { - yield 'empty' => [ - new Range(null, null), - '[*;*)', - ]; - - yield 'int' => [ - new Range(1, 10), - '[1;10)', - ]; - - yield 'float' => [ - new Range(0.25, 3.25), - '[0.25;3.25)', - ]; - - yield 'datetime' => [ - new Range( - new DateTimeImmutable('2020-01-01T00:00:00+0000'), - new DateTimeImmutable('2020-12-31T23:59:59+0000'), - ), - '[2020-01-01T00:00:00+0000;2020-12-31T23:59:59+0000)', - ]; - } - - public function testOfInt(): void - { - $this->assertEquals(new Range(null, 10), Range::ofInt(null, 10)); - $this->assertEquals(new Range(1, 10), Range::ofInt(1, 10)); - $this->assertEquals(new Range(1, null), Range::ofInt(1, null)); - } - - public function testOfFloat(): void - { - $this->assertEquals(new Range(null, 10.0), Range::ofFloat(null, 10.0)); - $this->assertEquals(new Range(1.0, 10.0), Range::ofFloat(1.0, 10.0)); - $this->assertEquals(new Range(1.0, null), Range::ofFloat(1.0, null)); - } - - public function testOfDateTime(): void - { - $a = new DateTimeImmutable('2020-01-01T00:00:00+0000'); - $b = new DateTimeImmutable('2020-12-31T23:59:59+0000'); - - $this->assertEquals(new Range(null, $b), Range::ofDateTime(null, $b)); - $this->assertEquals(new Range($a, $b), Range::ofDateTime($a, $b)); - $this->assertEquals(new Range($a, null), Range::ofDateTime($a, null)); - } -} diff --git a/eZ/Publish/API/Repository/Tests/Values/Content/SectionTest.php b/eZ/Publish/API/Repository/Tests/Values/Content/SectionTest.php deleted file mode 100644 index 3439784a29..0000000000 --- a/eZ/Publish/API/Repository/Tests/Values/Content/SectionTest.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\Values\Content; - -use eZ\Publish\API\Repository\Tests\Values\ValueObjectTestTrait; -use eZ\Publish\API\Repository\Values\Content\Section; -use PHPUnit\Framework\TestCase; - -class SectionTest extends TestCase -{ - use ValueObjectTestTrait; - - /** - * Test a new class and default values on properties. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Section::__construct - */ - public function testNewClass() - { - $section = new Section(); - - $this->assertPropertiesCorrect( - [ - 'id' => null, - 'identifier' => null, - 'name' => null, - ], - $section - ); - } - - /** - * Test retrieving missing property. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Section::__get - */ - public function testMissingProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); - - $section = new Section(); - $value = $section->notDefined; - self::fail('Succeeded getting non existing property'); - } - - /** - * Test setting read only property. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Section::__set - */ - public function testReadOnlyProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $section = new Section(); - $section->id = 22; - self::fail('Succeeded setting read only property'); - } - - /** - * Test if property exists. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Section::__isset - */ - public function testIsPropertySet() - { - $section = new Section(); - $value = isset($section->notDefined); - self::assertFalse($value); - - $value = isset($section->id); - self::assertTrue($value); - } - - /** - * Test unsetting a property. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Section::__unset - */ - public function testUnsetProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $section = new Section(['id' => 1]); - unset($section->id); - self::fail('Unsetting read-only property succeeded'); - } -} diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/BaseLimitationTest.php b/eZ/Publish/API/Repository/Tests/Values/User/Limitation/BaseLimitationTest.php deleted file mode 100644 index 71a72799cd..0000000000 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/BaseLimitationTest.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; - -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\User\PolicyCreateStruct; -use eZ\Publish\API\Repository\Values\User\Role; - -/** - * Abstract base class for limitation tests. - * - * @group integration - * @group limitation - */ -abstract class BaseLimitationTest extends BaseTest -{ - /** - * Creates a published wiki page. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - protected function createWikiPage() - { - $repository = $this->getRepository(); - - $contentService = $repository->getContentService(); - /* BEGIN: Inline */ - $draft = $this->createWikiPageDraft(); - - $content = $contentService->publishVersion($draft->versionInfo); - /* END: Inline */ - - return $content; - } - - /** - * Creates a fresh clean content draft. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - protected function createWikiPageDraft() - { - $repository = $this->getRepository(); - - $parentLocationId = $this->generateId('location', 60); - $sectionId = $this->generateId('section', 1); - /* BEGIN: Inline */ - $contentTypeService = $repository->getContentTypeService(); - $locationService = $repository->getLocationService(); - $contentService = $repository->getContentService(); - - // Configure new location - // $parentLocationId is the id of the /Home/Contact-Us node - $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); - - $locationCreate->priority = 23; - $locationCreate->hidden = true; - $locationCreate->remoteId = '0123456789abcdef0123456789abcdef'; - $locationCreate->sortField = Location::SORT_FIELD_NODE_ID; - $locationCreate->sortOrder = Location::SORT_ORDER_DESC; - - // Load content type - $wikiPageType = $contentTypeService->loadContentTypeByIdentifier('wiki_page'); - - // Configure new content object - $wikiPageCreate = $contentService->newContentCreateStruct($wikiPageType, 'eng-US'); - - $wikiPageCreate->setField('title', 'An awesome wiki page'); - $wikiPageCreate->remoteId = 'abcdef0123456789abcdef0123456789'; - // $sectionId is the ID of section 1 - $wikiPageCreate->sectionId = $sectionId; - $wikiPageCreate->alwaysAvailable = true; - - // Create a draft - $draft = $contentService->createContent( - $wikiPageCreate, - [$locationCreate] - ); - /* END: Inline */ - - return $draft; - } - - protected function addPolicyToRole(string $roleIdentifier, PolicyCreateStruct $policyCreateStruct): Role - { - $roleService = $this->getRepository()->getRoleService(); - - $role = $roleService->loadRoleByIdentifier($roleIdentifier); - $roleDraft = $roleService->createRoleDraft($role); - $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct); - $roleService->publishRoleDraft($roleDraft); - - return $roleService->loadRoleByIdentifier($roleIdentifier); - } -} diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ObjectStateLimitationTest.php b/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ObjectStateLimitationTest.php deleted file mode 100644 index fa8df339d1..0000000000 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ObjectStateLimitationTest.php +++ /dev/null @@ -1,309 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\ObjectStateService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectState; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation; -use eZ\Publish\API\Repository\Values\User\User; - -/** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation} - * class. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation - * @group integration - * @group limitation - */ -class ObjectStateLimitationTest extends BaseLimitationTest -{ - public const OBJECT_STATE_LOCK_GROUP_ID = 2; - public const OBJECT_STATE_NOT_LOCKED_STATE_ID = 1; - public const OBJECT_STATE_LOCKED_STATE_ID = 2; - public const EDITOR_ROLE_IDENTIFIER = 'Editor'; - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testObjectStateLimitationAllow(): void - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $this->loginAsUser( - $this->createUserWithObjectStateLimitation([self::OBJECT_STATE_NOT_LOCKED_STATE_ID]) - ); - - $draft = $this->createWikiPageDraft(); - - $contentService->deleteContent($draft->contentInfo); - - $this->expectException(NotFoundException::class); - $contentService->loadContent($draft->id); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testObjectStateLimitationForbid(): void - { - $this->expectException(UnauthorizedException::class); - - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - - $this->loginAsUser( - $this->createUserWithObjectStateLimitation([self::OBJECT_STATE_LOCKED_STATE_ID]) - ); - - $draft = $this->createWikiPageDraft(); - - $this->expectException(UnauthorizedException::class); - $contentService->deleteContent($draft->contentInfo); - } - - /** - * Checks if the action is correctly forbidden when using ObjectStateLimitation - * with limitation values from two different StateGroups. - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testObjectStateLimitationForbidVariant(): void - { - $this->expectException(UnauthorizedException::class); - $this->expectExceptionMessage('\'remove\' \'content\''); - - $repository = $this->getRepository(); - $objectStateGroup = $this->createObjectStateGroup(); - $objectState = $this->createObjectState($objectStateGroup); - - $contentService = $repository->getContentService(); - - $this->loginAsUser( - $this->createUserWithObjectStateLimitation( - [ - self::OBJECT_STATE_LOCKED_STATE_ID, - $objectState->id, - ] - ) - ); - - $draft = $this->createWikiPageDraft(); - - $this->expectException(UnauthorizedException::class); - $this->expectExceptionMessage("'remove' 'content'"); - - $contentService->deleteContent($draft->contentInfo); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - private function createObjectStateGroup(): ObjectStateGroup - { - $objectStateService = $this->getRepository()->getObjectStateService(); - - $objectStateGroupCreateStruct = $objectStateService->newObjectStateGroupCreateStruct('second_group'); - $objectStateGroupCreateStruct->defaultLanguageCode = 'eng-US'; - $objectStateGroupCreateStruct->names = ['eng-US' => 'Second Group']; - - return $objectStateService->createObjectStateGroup($objectStateGroupCreateStruct); - } - - /** - * Create new State and assign it to the $objectStateGroup. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - private function createObjectState(ObjectStateGroup $objectStateGroup): ObjectState - { - $objectStateService = $this->getRepository()->getObjectStateService(); - - $objectStateCreateStruct = $objectStateService->newObjectStateCreateStruct('default_state'); - $objectStateCreateStruct->defaultLanguageCode = 'eng-US'; - $objectStateCreateStruct->names = ['eng-US' => 'Default state']; - - return $objectStateService->createObjectState($objectStateGroup, $objectStateCreateStruct); - } - - /** - * Checks if the search results are correctly filtered when using ObjectStateLimitation - * with limitation values from two different StateGroups. - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testObjectStateLimitationSearch(): void - { - $repository = $this->getRepository(); - $permissionResolver = $repository->getPermissionResolver(); - - $objectStateGroup = $this->createObjectStateGroup(); - $objectState = $this->createObjectState($objectStateGroup); - - $user = $this->createUserWithObjectStateLimitationOnContentRead( - [ - self::OBJECT_STATE_NOT_LOCKED_STATE_ID, - $objectState->id, - ] - ); - $adminUser = $permissionResolver->getCurrentUserReference(); - - $wikiPage = $this->createWikiPage(); - - $this->loginAsUser($user); - - $query = new Query(); - $query->filter = new Criterion\MatchAll(); - $query->limit = 50; - - $this->refreshSearch($repository); - $searchResultsBefore = $repository->getSearchService()->findContent($query); - - $this->loginAsUser($adminUser); - - //change the Object State to the one that doesn't match the Limitation - $stateService = $repository->getObjectStateService(); - $stateService->setContentState( - $wikiPage->contentInfo, - $stateService->loadObjectStateGroup(2), - $stateService->loadObjectState(2) - ); - - $this->loginAsUser($user); - - $this->refreshSearch($repository); - $searchResultsAfter = $repository->getSearchService()->findContent($query); - - self::assertEquals($searchResultsBefore->totalCount - 1, $searchResultsAfter->totalCount); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testUserWithNotLockedLimitationCanEditNotLockedContent(): void - { - $repository = $this->getRepository(); - $contentService = $repository->getContentService(); - $objectStateService = $repository->getObjectStateService(); - $lockGroup = $objectStateService->loadObjectStateGroup(self::OBJECT_STATE_LOCK_GROUP_ID); - $notLockedState = $objectStateService->loadObjectState(self::OBJECT_STATE_NOT_LOCKED_STATE_ID); - - // sanity check - self::assertSame('not_locked', $notLockedState->identifier); - - $this->loginAsUser( - $this->createUserWithObjectStateLimitation([self::OBJECT_STATE_NOT_LOCKED_STATE_ID]) - ); - $draft = $this->createWikiPageDraft(); - - $this->assertContentHasState( - $objectStateService, - $draft->contentInfo, - $lockGroup, - $notLockedState - ); - - $contentUpdate = $contentService->newContentUpdateStruct(); - $contentUpdate->setField('title', 'Updated test folder'); - $updatedDraft = $contentService->updateContent($draft->versionInfo, $contentUpdate); - - $this->assertContentHasState( - $objectStateService, - $updatedDraft->contentInfo, - $lockGroup, - $notLockedState - ); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - protected function createUserWithObjectStateLimitation(array $objectStateIDs): User - { - return $this->createUserWithPolicies( - uniqid('test', true), - [ - ['module' => 'content', 'function' => 'read'], - ['module' => 'content', 'function' => 'versionread'], - ['module' => 'content', 'function' => 'create'], - ['module' => 'content', 'function' => 'publish'], - [ - 'module' => 'content', - 'function' => 'edit', - 'limitations' => [ - new ObjectStateLimitation(['limitationValues' => $objectStateIDs]), - ], - ], - [ - 'module' => 'content', - 'function' => 'remove', - 'limitations' => [ - new ObjectStateLimitation(['limitationValues' => $objectStateIDs]), - ], - ], - ] - ); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - private function createUserWithObjectStateLimitationOnContentRead(array $arr): User - { - return $this->createUserWithPolicies( - uniqid('test', true), - [ - [ - 'module' => 'content', - 'function' => 'read', - 'limitations' => [ - new ObjectStateLimitation( - [ - 'limitationValues' => $arr, - ] - ), - ], - ], - ] - ); - } - - private function assertContentHasState( - ObjectStateService $objectStateService, - ContentInfo $contentInfo, - ObjectStateGroup $lockGroup, - ObjectState $objectState - ): void { - self::assertSame( - $objectState->identifier, - $objectStateService->getContentState($contentInfo, $lockGroup)->identifier - ); - } -} diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ParentContentTypeLimitationTest.php b/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ParentContentTypeLimitationTest.php deleted file mode 100644 index f4f32a369e..0000000000 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ParentContentTypeLimitationTest.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ParentContentTypeLimitation; - -/** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\ParentContentTypeLimitation} - * class. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\ParentContentTypeLimitation - * @group integration - * @group limitation - */ -class ParentContentTypeLimitationTest extends BaseLimitationTest -{ - /** - * Test for ParentContentTypeLimitation and ContentTypeLimitation. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ParentContentTypeLimitation - */ - public function testParentContentTypeLimitationAllow() - { - $repository = $this->getRepository(); - $permissionResolver = $repository->getPermissionResolver(); - - $parentContentTypeId = $this->generateId('contentType', 20); - $contentTypeId = $this->generateId('contentType', 22); - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - $roleService = $repository->getRoleService(); - $contentService = $repository->getContentService(); - - $role = $roleService->loadRoleByIdentifier('Editor'); - - $policyCreate = $roleService->newPolicyCreateStruct('content', 'create'); - $policyCreate->addLimitation( - new ParentContentTypeLimitation( - ['limitationValues' => [$parentContentTypeId]] - ) - ); - $policyCreate->addLimitation( - new ContentTypeLimitation( - ['limitationValues' => [$contentTypeId]] - ) - ); - - $roleDraft = $roleService->createRoleDraft($role); - $roleService->addPolicyByRoleDraft( - $roleDraft, - $policyCreate - ); - $roleService->publishRoleDraft($roleDraft); - - $roleService->assignRoleToUser($role, $user); - - $permissionResolver->setCurrentUserReference($user); - - $draft = $this->createWikiPageDraft(); - $content = $contentService->publishVersion($draft->versionInfo); - /* END: Use Case */ - - $this->assertEquals( - 'An awesome wiki page', - $content->getFieldValue('title')->text - ); - } - - /** - * Test for ParentContentTypeLimitation and ContentTypeLimitation. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ParentContentTypeLimitation - */ - public function testParentContentTypeLimitationForbid() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); - - $repository = $this->getRepository(); - $permissionResolver = $repository->getPermissionResolver(); - - $parentContentTypeId = $this->generateId('contentType', 20); - $contentTypeId = $this->generateId('contentType', 33); - /* BEGIN: Use Case */ - $user = $this->createUserVersion1(); - - $roleService = $repository->getRoleService(); - - $role = $roleService->loadRoleByIdentifier('Editor'); - - $policyCreate = $roleService->newPolicyCreateStruct('content', 'create'); - $policyCreate->addLimitation( - new ParentContentTypeLimitation( - ['limitationValues' => [$parentContentTypeId]] - ) - ); - $policyCreate->addLimitation( - new ContentTypeLimitation( - ['limitationValues' => [$contentTypeId]] - ) - ); - - $roleDraft = $roleService->createRoleDraft($role); - $roleService->addPolicyByRoleDraft( - $roleDraft, - $policyCreate - ); - - $roleService->publishRoleDraft($roleDraft); - - $permissionResolver->setCurrentUserReference($user); - - $this->createWikiPageDraft(); - /* END: Use Case */ - } -} diff --git a/eZ/Publish/API/Repository/Tests/Values/ValueObjectTestTrait.php b/eZ/Publish/API/Repository/Tests/Values/ValueObjectTestTrait.php deleted file mode 100644 index 629fa541ac..0000000000 --- a/eZ/Publish/API/Repository/Tests/Values/ValueObjectTestTrait.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Tests\Values; - -use eZ\Publish\API\Repository\Values\ValueObject; - -trait ValueObjectTestTrait -{ - /** - * Asserts that properties given in $expectedValues are correctly set in - * $mockedValueObject. - * - * @param mixed[] $expectedValues - * @param \eZ\Publish\API\Repository\Values\ValueObject $actualValueObject - */ - public function assertPropertiesCorrect(array $expectedValues, ValueObject $actualValueObject) - { - foreach ($expectedValues as $propertyName => $propertyValue) { - self::assertSame( - $propertyValue, - $actualValueObject->$propertyName, - sprintf('Property %s value is incorrect', $propertyName) - ); - } - } -} diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/LanguageFixture.php b/eZ/Publish/API/Repository/Tests/_fixtures/LanguageFixture.php deleted file mode 100644 index 25f2022ed3..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/LanguageFixture.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php -return array( - array( - 2 => new \eZ\Publish\API\Repository\Values\Content\Language( - array( - "id" => 2, - "name" => "English (American)", - "enabled" => true, - "languageCode" => "eng-US", - ) - ), - 4 => new \eZ\Publish\API\Repository\Values\Content\Language( - array( - "id" => 4, - "name" => "German", - "enabled" => true, - "languageCode" => "ger-DE", - ) - ), - 8 => new \eZ\Publish\API\Repository\Values\Content\Language( - array( - "id" => 8, - "name" => "English (United Kingdom)", - "enabled" => true, - "languageCode" => "eng-GB", - ) - ), - ), - array( - "eng-US" => 2, - "ger-DE" => 4, - "eng-GB" => 8, - ), - 8 -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/AncestorContent.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/AncestorContent.php deleted file mode 100644 index fbaeaed0e2..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/AncestorContent.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => NULL, - 'totalCount' => 3, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/AncestorLocation.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/AncestorLocation.php deleted file mode 100644 index 357257bfda..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/AncestorLocation.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => NULL, - 'totalCount' => 3, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ContentId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ContentId.php deleted file mode 100644 index bd3ec8b9dc..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ContentId.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ContentTypeGroupId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ContentTypeGroupId.php deleted file mode 100644 index d6f75f3a9c..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ContentTypeGroupId.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ContentTypeId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ContentTypeId.php deleted file mode 100644 index 3cf2a66266..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ContentTypeId.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataBetween.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataBetween.php deleted file mode 100644 index c570294c08..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataBetween.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataCreated.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataCreated.php deleted file mode 100644 index 49a1eb9401..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataCreated.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 3, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataGt.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataGt.php deleted file mode 100644 index 49c976e077..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataGt.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataGte.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataGte.php deleted file mode 100644 index b1b4bba496..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataGte.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 4, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataIn.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataIn.php deleted file mode 100644 index 49a1eb9401..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataIn.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 3, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataLte.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataLte.php deleted file mode 100644 index ba32768b16..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DateMetadataLte.php +++ /dev/null @@ -1,125 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 14, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DeprecatedContentIdQuery.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DeprecatedContentIdQuery.php deleted file mode 100644 index bd3ec8b9dc..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DeprecatedContentIdQuery.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Depth.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Depth.php deleted file mode 100644 index 35dc922e9f..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Depth.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 5, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DepthGt.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DepthGt.php deleted file mode 100644 index f15a26515b..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DepthGt.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DepthGte.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DepthGte.php deleted file mode 100644 index 1afe2a1927..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DepthGte.php +++ /dev/null @@ -1,157 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 6 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 7 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 8 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 9 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 10 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 11 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 12 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 13, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DepthIn.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DepthIn.php deleted file mode 100644 index 57e5611e4d..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DepthIn.php +++ /dev/null @@ -1,91 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 6 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 7, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DepthLte.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DepthLte.php deleted file mode 100644 index 8acdd419fc..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/DepthLte.php +++ /dev/null @@ -1,190 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 6 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 7 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 8 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 9 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 10 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 11 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 12 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 13 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 14 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 15 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 16, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetContentType.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetContentType.php deleted file mode 100644 index 47512457f2..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetContentType.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => NULL, - 'totalCount' => 2, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetContentTypeMinCount.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetContentTypeMinCount.php deleted file mode 100644 index 47512457f2..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetContentTypeMinCount.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => NULL, - 'totalCount' => 2, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetContentTypeMinLimit.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetContentTypeMinLimit.php deleted file mode 100644 index 47512457f2..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetContentTypeMinLimit.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => NULL, - 'totalCount' => 2, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetFieldRegexp.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetFieldRegexp.php deleted file mode 100644 index 47512457f2..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetFieldRegexp.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => NULL, - 'totalCount' => 2, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetFieldRegexpSortCount.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetFieldRegexpSortCount.php deleted file mode 100644 index 47512457f2..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetFieldRegexpSortCount.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => NULL, - 'totalCount' => 2, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetFieldRegexpSortTerm.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetFieldRegexpSortTerm.php deleted file mode 100644 index 47512457f2..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetFieldRegexpSortTerm.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => NULL, - 'totalCount' => 2, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetFieldSimple.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetFieldSimple.php deleted file mode 100644 index 47512457f2..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetFieldSimple.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => NULL, - 'totalCount' => 2, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetSection.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetSection.php deleted file mode 100644 index 47512457f2..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetSection.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => NULL, - 'totalCount' => 2, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetTerm.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetTerm.php deleted file mode 100644 index 47512457f2..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetTerm.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => NULL, - 'totalCount' => 2, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetUser.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetUser.php deleted file mode 100644 index 47512457f2..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FacetUser.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => NULL, - 'index' => NULL, - 'matchedTranslation' => 'eng-GB', - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => NULL, - 'totalCount' => 2, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Field.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Field.php deleted file mode 100644 index 3fba6f7426..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Field.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldBetween.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldBetween.php deleted file mode 100644 index 8e9d5a7001..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldBetween.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldIn.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldIn.php deleted file mode 100644 index 5dad5da09d..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldIn.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldOr.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldOr.php deleted file mode 100644 index 40cf854080..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldOr.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 3, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldRelation.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldRelation.php deleted file mode 100644 index 708674cc24..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldRelation.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 61, - 'title' => 'User gallery', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldRelationAll.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldRelationAll.php deleted file mode 100644 index b8911ed552..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FieldRelationAll.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 60, - 'title' => 'Image gallery', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 61, - 'title' => 'User gallery', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FullText.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FullText.php deleted file mode 100644 index c2675317ce..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FullText.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FullTextFiltered.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FullTextFiltered.php deleted file mode 100644 index 6ee321a0d4..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FullTextFiltered.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FullTextWildcard.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FullTextWildcard.php deleted file mode 100644 index 6ee321a0d4..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/FullTextWildcard.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/IsUserBasedFalse.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/IsUserBasedFalse.php deleted file mode 100644 index 1a36c6f9f0..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/IsUserBasedFalse.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state([ - 'facets' => [], - 'searchHits' => - [ - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 4, - 'title' => 'Users', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - ]), - ], - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 16, -]); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/IsUserBasedTrue.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/IsUserBasedTrue.php deleted file mode 100644 index 8d37556225..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/IsUserBasedTrue.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state([ - 'facets' => [], - 'searchHits' => - [ - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 10, - 'title' => 'Anonymous User', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - ]), - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 14, - 'title' => 'Administrator User', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - ]), - ], - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -]); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/IsUserEnabledFalse.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/IsUserEnabledFalse.php deleted file mode 100644 index 2cc5dafdb3..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/IsUserEnabledFalse.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state([ - 'facets' => [], - 'searchHits' => [], - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 0, -]); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/IsUserEnabledTrue.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/IsUserEnabledTrue.php deleted file mode 100644 index 8d37556225..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/IsUserEnabledTrue.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state([ - 'facets' => [], - 'searchHits' => - [ - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 10, - 'title' => 'Anonymous User', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - ]), - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 14, - 'title' => 'Administrator User', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - ]), - ], - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -]); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LanguageCode.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LanguageCode.php deleted file mode 100644 index f026bcfded..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LanguageCode.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LanguageCodeAlwaysAvailable.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LanguageCodeAlwaysAvailable.php deleted file mode 100644 index 589fd5e96e..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LanguageCodeAlwaysAvailable.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 16, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LanguageCodeIn.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LanguageCodeIn.php deleted file mode 100644 index c0f4f89852..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LanguageCodeIn.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 6 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 7 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 18, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LocationId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LocationId.php deleted file mode 100644 index 0ab6084a37..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LocationId.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LocationRemoteId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LocationRemoteId.php deleted file mode 100644 index 0ab6084a37..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LocationRemoteId.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LogicalAnd.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LogicalAnd.php deleted file mode 100644 index 8b81e9641b..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LogicalAnd.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LogicalNot.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LogicalNot.php deleted file mode 100644 index 8b81e9641b..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LogicalNot.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LogicalOr.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LogicalOr.php deleted file mode 100644 index d73fe6fc4d..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/LogicalOr.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 3, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/MatchNone.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/MatchNone.php deleted file mode 100644 index 7eeb53a063..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/MatchNone.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 0, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ObjectStateIdentifier.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ObjectStateIdentifier.php deleted file mode 100644 index 1181454cd3..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ObjectStateIdentifier.php +++ /dev/null @@ -1,229 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state([ - 'facets' => [], - 'searchHits' => - [ - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - [ - 'id' => 4, - 'title' => 'Users', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 10, - 'title' => 'Anonymous User', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 11, - 'title' => 'Members', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 12, - 'title' => 'Administrator users', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 13, - 'title' => 'Editors', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 14, - 'title' => 'Administrator User', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 41, - 'title' => 'Media', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 42, - 'title' => 'Anonymous Users', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 45, - 'title' => 'Setup', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 49, - 'title' => 'Images', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 50, - 'title' => 'Files', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 51, - 'title' => 'Multimedia', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 12 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 52, - 'title' => 'Common INI settings', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 13 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 14 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 56, - 'title' => 'Design', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 15 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 57, - 'title' => 'Home', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 16 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 58, - 'title' => 'Contact Us', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - 17 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 59, - 'title' => 'Partners', - ], - 'score' => 0.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - ]), - ], - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 18, -]); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ParentLocationId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ParentLocationId.php deleted file mode 100644 index 99e1923111..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/ParentLocationId.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 5, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/QueryModifiedField.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/QueryModifiedField.php deleted file mode 100644 index 8c3e4d5cbf..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/QueryModifiedField.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/RemoteId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/RemoteId.php deleted file mode 100644 index bd3ec8b9dc..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/RemoteId.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SectionId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SectionId.php deleted file mode 100644 index d6f75f3a9c..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SectionId.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SectionIdentifier.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SectionIdentifier.php deleted file mode 100644 index dbd337cdea..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SectionIdentifier.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state([ - 'facets' => [], - 'searchHits' => - [ - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 4, - 'title' => 'Users', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - ]), - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 10, - 'title' => 'Anonymous User', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - ]), - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 11, - 'title' => 'Members', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - ]), - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 12, - 'title' => 'Administrator users', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - ]), - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 13, - 'title' => 'Editors', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - ]), - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 14, - 'title' => 'Administrator User', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - ]), - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 42, - 'title' => 'Anonymous Users', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - ]), - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 59, - 'title' => 'Partners', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - ]), - ], - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 8, -]); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Sibling.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Sibling.php deleted file mode 100644 index 376682f97e..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Sibling.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 4, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortContentName.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortContentName.php deleted file mode 100644 index 104c96b34e..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortContentName.php +++ /dev/null @@ -1,147 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 12, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortDateModified.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortDateModified.php deleted file mode 100644 index 36d074c802..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortDateModified.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 6 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 7 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 8, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortDatePublished.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortDatePublished.php deleted file mode 100644 index d6f75f3a9c..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortDatePublished.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortDesc.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortDesc.php deleted file mode 100644 index 51d1f985cc..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortDesc.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFieldMultipleTypes.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFieldMultipleTypes.php deleted file mode 100644 index c3aa3b05c9..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFieldMultipleTypes.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 12, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFieldMultipleTypesReverse.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFieldMultipleTypesReverse.php deleted file mode 100644 index 149c939ba7..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFieldMultipleTypesReverse.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 12, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFieldMultipleTypesSlice.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFieldMultipleTypesSlice.php deleted file mode 100644 index e789aa3de3..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFieldMultipleTypesSlice.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 12, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFieldMultipleTypesSliceReverse.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFieldMultipleTypesSliceReverse.php deleted file mode 100644 index a930cb8fbd..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFieldMultipleTypesSliceReverse.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 12, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFolderName.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFolderName.php deleted file mode 100644 index 4cfb696de8..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortFolderName.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 6, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortLocationDepth.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortLocationDepth.php deleted file mode 100644 index c3bf0c6f2b..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortLocationDepth.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortMultiple.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortMultiple.php deleted file mode 100644 index 2ee70aa39d..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortMultiple.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 4, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortNone.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortNone.php deleted file mode 100644 index d6f75f3a9c..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortNone.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortPathString.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortPathString.php deleted file mode 100644 index a7525a9f89..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortPathString.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortSectionIdentifier.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortSectionIdentifier.php deleted file mode 100644 index 7b0c28dfeb..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortSectionIdentifier.php +++ /dev/null @@ -1,169 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 12 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 13 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 14, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortSectionName.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortSectionName.php deleted file mode 100644 index b44b253458..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortSectionName.php +++ /dev/null @@ -1,169 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 12 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 13 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 14, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortTemplateTitle.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortTemplateTitle.php deleted file mode 100644 index cece724571..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/SortTemplateTitle.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Status.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Status.php deleted file mode 100644 index c4d463bf2e..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Status.php +++ /dev/null @@ -1,213 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 12 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 13 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 14 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 15 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 16 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 17 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 18, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Subtree.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Subtree.php deleted file mode 100644 index d6f75f3a9c..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Subtree.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserEmail.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserEmail.php deleted file mode 100644 index 92174012ff..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserEmail.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state([ - 'facets' => [], - 'searchHits' => - [ - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 10, - 'title' => 'Anonymous User', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - ]), - ], - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 1, -]); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserIdEq.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserIdEq.php deleted file mode 100644 index 7769b7fdaf..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserIdEq.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state([ - 'facets' => [], - 'searchHits' => - [ - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 14, - 'title' => 'Administrator User', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - ]), - ], - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 1, -]); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserIdIn.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserIdIn.php deleted file mode 100644 index 8d37556225..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserIdIn.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state([ - 'facets' => [], - 'searchHits' => - [ - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 10, - 'title' => 'Anonymous User', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - ]), - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 14, - 'title' => 'Administrator User', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - ]), - ], - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -]); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserLogin.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserLogin.php deleted file mode 100644 index 7769b7fdaf..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserLogin.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state([ - 'facets' => [], - 'searchHits' => - [ - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state([ - 'valueObject' => - [ - 'id' => 14, - 'title' => 'Administrator User', - ], - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - ]), - ], - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 1, -]); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserMetadata.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserMetadata.php deleted file mode 100644 index 76788fc23e..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserMetadata.php +++ /dev/null @@ -1,231 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 12 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 13 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 14 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 15 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-GB', - )), - 16 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-GB', - )), - 17 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 18, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserMetadataLocation.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserMetadataLocation.php deleted file mode 100644 index 99ce5b7227..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/UserMetadataLocation.php +++ /dev/null @@ -1,231 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-GB', - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 12 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 13 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 14 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 15 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - 16 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-GB', - )), - 17 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - 'matchedTranslation' => 'eng-US', - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 19, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Visibility.php b/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Visibility.php deleted file mode 100644 index dfa7132bd5..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/Visibility.php +++ /dev/null @@ -1,212 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 6 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 7 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 8 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 9 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 10 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 11 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 12 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 13 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 14 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 15 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 16 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - 17 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 18, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/SectionFixture.php b/eZ/Publish/API/Repository/Tests/_fixtures/SectionFixture.php deleted file mode 100644 index 92e69a8801..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/SectionFixture.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php -return array( - array( - 1 => new \eZ\Publish\API\Repository\Values\Content\Section( - array( - "id" => 1, - "name" => "Standard", - "identifier" => "standard", - ) - ), - 2 => new \eZ\Publish\API\Repository\Values\Content\Section( - array( - "id" => 2, - "name" => "Users", - "identifier" => "users", - ) - ), - 3 => new \eZ\Publish\API\Repository\Values\Content\Section( - array( - "id" => 3, - "name" => "Media", - "identifier" => "media", - ) - ), - 4 => new \eZ\Publish\API\Repository\Values\Content\Section( - array( - "id" => 4, - "name" => "Setup", - "identifier" => "setup", - ) - ), - 5 => new \eZ\Publish\API\Repository\Values\Content\Section( - array( - "id" => 5, - "name" => "Design", - "identifier" => "design", - ) - ), - 6 => new \eZ\Publish\API\Repository\Values\Content\Section( - array( - "id" => 6, - "name" => "Restricted", - "identifier" => "", - ) - ), - ), - array( - "standard" => 1, - "users" => 2, - "media" => 3, - "setup" => 4, - "design" => 5, - ), - array( - "2" => array( - 4 => true, - 10 => true, - 11 => true, - 12 => true, - 13 => true, - 14 => true, - 42 => true, - 59 => true, - ), - "3" => array( - 41 => true, - 49 => true, - 50 => true, - 51 => true, - ), - "4" => array( - 45 => true, - 52 => true, - ), - "5" => array( - 54 => true, - 56 => true, - ), - "1" => array( - 57 => true, - 58 => true, - ), - ), - 6 -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/AncestorContent.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/AncestorContent.php deleted file mode 100644 index c07e1aa301..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/AncestorContent.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => 1.0, - 'totalCount' => 3, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/AncestorLocation.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/AncestorLocation.php deleted file mode 100644 index a958d714c6..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/AncestorLocation.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array ( - ), - 'searchHits' => - array ( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array ( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => NULL, - 'matchedTranslation' => NULL, - 'highlight' => NULL, - )), - ), - 'spellSuggestion' => NULL, - 'time' => 1, - 'timedOut' => NULL, - 'maxScore' => 1.0, - 'totalCount' => 3, -)); - diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/ContentId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/ContentId.php deleted file mode 100644 index b799eb5cd5..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/ContentId.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/ContentTypeGroupId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/ContentTypeGroupId.php deleted file mode 100644 index 1c5778e43d..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/ContentTypeGroupId.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/ContentTypeId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/ContentTypeId.php deleted file mode 100644 index 3eccf8ca56..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/ContentTypeId.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldBetween.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldBetween.php deleted file mode 100644 index 882c5908ac..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldBetween.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1, - 'index' => null, - 'matchedTranslation' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1, - 'index' => null, - 'matchedTranslation' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1, - 'index' => null, - 'matchedTranslation' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1, - 'index' => null, - 'matchedTranslation' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1, - 'totalCount' => 4, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldGt.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldGt.php deleted file mode 100644 index 7e3e8a8a93..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldGt.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldGte.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldGte.php deleted file mode 100644 index 6fc72ae225..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldGte.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1, - 'totalCount' => 3, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldLt.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldLt.php deleted file mode 100644 index 53c516b685..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldLt.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1, - 'totalCount' => 3, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldLte.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldLte.php deleted file mode 100644 index cf1a80b2d8..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/CustomFieldLte.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1, - 'totalCount' => 4, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataBetween.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataBetween.php deleted file mode 100644 index 2b58f81ba9..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataBetween.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataCreated.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataCreated.php deleted file mode 100644 index 090342ecbf..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataCreated.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 3, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataGt.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataGt.php deleted file mode 100644 index dfe22a01ca..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataGt.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataGte.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataGte.php deleted file mode 100644 index 9350c69691..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataGte.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 4, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataIn.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataIn.php deleted file mode 100644 index d983bbfac4..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataIn.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 3, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataLte.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataLte.php deleted file mode 100644 index 473b0e5628..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DateMetadataLte.php +++ /dev/null @@ -1,125 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 14, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DeprecatedContentIdQuery.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DeprecatedContentIdQuery.php deleted file mode 100644 index 3808da418b..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DeprecatedContentIdQuery.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 0.57124877, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 0.57124877, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 0.57124877, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Depth.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Depth.php deleted file mode 100644 index 60f1440666..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Depth.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.9200476, - 'totalCount' => 5, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DepthGt.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DepthGt.php deleted file mode 100644 index cb5f58db54..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DepthGt.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - )), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.9200476, - 'totalCount' => 2, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DepthGte.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DepthGte.php deleted file mode 100644 index d8818cd199..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DepthGte.php +++ /dev/null @@ -1,156 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 6 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - )), - 7 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 8 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 9 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 10 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 11 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 12 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.9200476, - 'totalCount' => 13, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DepthIn.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DepthIn.php deleted file mode 100644 index c8de74a194..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DepthIn.php +++ /dev/null @@ -1,91 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.8414208999999999, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.8414208999999999, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.384091, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1.384091, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 1.384091, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => 1.384091, - 'index' => null, - 'highlight' => null, - ) - ), - 6 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1.384091, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.8414208999999999, - 'totalCount' => 7, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DepthLte.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DepthLte.php deleted file mode 100644 index e09ef9d408..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/DepthLte.php +++ /dev/null @@ -1,190 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 6 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 7 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 8 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 9 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 10 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 11 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 12 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 13 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 14 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 15 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.9200476, - 'totalCount' => 16, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetContentType.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetContentType.php deleted file mode 100644 index 6c6aa1e522..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetContentType.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\Facet\ContentTypeFacet::__set_state(array( - 'entries' => - array( - 20 => 1, - 21 => 1, - ), - 'name' => 'type', - )), - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetContentTypeMinCount.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetContentTypeMinCount.php deleted file mode 100644 index e353085dc4..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetContentTypeMinCount.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\Facet\ContentTypeFacet::__set_state(array( - 'entries' => array(), - 'name' => 'type', - )), - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetContentTypeMinLimit.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetContentTypeMinLimit.php deleted file mode 100644 index 6c6aa1e522..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetContentTypeMinLimit.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\Facet\ContentTypeFacet::__set_state(array( - 'entries' => - array( - 20 => 1, - 21 => 1, - ), - 'name' => 'type', - )), - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetSection.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetSection.php deleted file mode 100644 index 2bbe1b19d3..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetSection.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\Facet\SectionFacet::__set_state(array( - 'entries' => - array( - 1 => 2, - ), - 'name' => 'section', - )), - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetUser.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetUser.php deleted file mode 100644 index 38601ba6f0..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FacetUser.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\Facet\UserFacet::__set_state(array( - 'entries' => - array( - 14 => 2, - ), - 'name' => 'creator', - )), - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )) - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Field.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Field.php deleted file mode 100644 index e73d505f10..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Field.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldBetween.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldBetween.php deleted file mode 100644 index 6f783a8281..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldBetween.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldIn.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldIn.php deleted file mode 100644 index 9091060068..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldIn.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldOr.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldOr.php deleted file mode 100644 index 2d2c592945..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldOr.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 3, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldRelation.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldRelation.php deleted file mode 100644 index 708674cc24..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldRelation.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 61, - 'title' => 'User gallery', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldRelationAll.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldRelationAll.php deleted file mode 100644 index b8911ed552..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldRelationAll.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 60, - 'title' => 'Image gallery', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 61, - 'title' => 'User gallery', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldUnknown.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldUnknown.php deleted file mode 100644 index a33431c589..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FieldUnknown.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 0, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FullText.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FullText.php deleted file mode 100644 index 331382585a..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FullText.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => 0.05622538, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 0.20990808, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 0.20990808, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FullTextFiltered.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FullTextFiltered.php deleted file mode 100644 index 83ccd8692e..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FullTextFiltered.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 1.3987858, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.3987858, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FullTextWildcard.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FullTextWildcard.php deleted file mode 100644 index 3bfa088bdb..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/FullTextWildcard.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LanguageCode.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LanguageCode.php deleted file mode 100644 index 42096f656a..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LanguageCode.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 2.7917593, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 2.7917593, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 2.7917593, - 'totalCount' => 2, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LanguageCodeAlwaysAvailable.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LanguageCodeAlwaysAvailable.php deleted file mode 100644 index 1b62fd799c..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LanguageCodeAlwaysAvailable.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 0.20774001, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 0.20774001, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => 0.20774001, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 3.007218, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 1.295869, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 0.20774001, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 3.007218, - 'totalCount' => 16, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LanguageCodeIn.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LanguageCodeIn.php deleted file mode 100644 index 0b2d4f7f11..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LanguageCodeIn.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 0.49504447000000001, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 0.49504447000000001, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => 0.18718657, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => 0.18718657, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => 0.49504447000000001, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1.8913484, - 'index' => null, - 'highlight' => null, - ) - ), - 6 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 0.81501900000000005, - 'index' => null, - 'highlight' => null, - ) - ), - 7 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 0.49504447000000001, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.8913484, - 'totalCount' => 18, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LocationId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LocationId.php deleted file mode 100644 index 359c7fa8ca..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LocationId.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LocationRemoteId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LocationRemoteId.php deleted file mode 100644 index 359c7fa8ca..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LocationRemoteId.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LogicalAnd.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LogicalAnd.php deleted file mode 100644 index 6c38d4d40d..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LogicalAnd.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LogicalNot.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LogicalNot.php deleted file mode 100644 index 6c38d4d40d..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LogicalNot.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LogicalOr.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LogicalOr.php deleted file mode 100644 index 3daca210d5..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/LogicalOr.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 3, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/MatchNone.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/MatchNone.php deleted file mode 100644 index 7eeb53a063..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/MatchNone.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 0, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/ParentLocationId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/ParentLocationId.php deleted file mode 100644 index 9cd6ad56e1..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/ParentLocationId.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 5, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/QueryCustomField.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/QueryCustomField.php deleted file mode 100644 index 90b2cb26df..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/QueryCustomField.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.9982654, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.9982654, - 'totalCount' => 1, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/QueryModifiedField.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/QueryModifiedField.php deleted file mode 100644 index 2e886eac5d..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/QueryModifiedField.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.7448496, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.7448496, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.7448496, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/RemoteId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/RemoteId.php deleted file mode 100644 index b799eb5cd5..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/RemoteId.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 2, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SectionId.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SectionId.php deleted file mode 100644 index 1c5778e43d..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SectionId.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Sibling.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Sibling.php deleted file mode 100644 index 376682f97e..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Sibling.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => null, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => null, - 'totalCount' => 4, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortContentName.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortContentName.php deleted file mode 100644 index 8c5b203130..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortContentName.php +++ /dev/null @@ -1,147 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 12, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortDateModified.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortDateModified.php deleted file mode 100644 index 59e87c8703..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortDateModified.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 6 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - 7 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.9200476, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.9200476, - 'totalCount' => 8, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortDatePublished.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortDatePublished.php deleted file mode 100644 index 1c5778e43d..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortDatePublished.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortDesc.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortDesc.php deleted file mode 100644 index bd41d5b53e..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortDesc.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFieldMultipleTypes.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFieldMultipleTypes.php deleted file mode 100644 index 8ebdcbead5..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFieldMultipleTypes.php +++ /dev/null @@ -1,147 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1, - 'totalCount' => 12, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFieldMultipleTypesReverse.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFieldMultipleTypesReverse.php deleted file mode 100644 index cead6b4f7e..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFieldMultipleTypesReverse.php +++ /dev/null @@ -1,147 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1, - 'totalCount' => 12, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFieldMultipleTypesSlice.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFieldMultipleTypesSlice.php deleted file mode 100644 index 29a9559ea7..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFieldMultipleTypesSlice.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1, - 'totalCount' => 12, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFieldMultipleTypesSliceReverse.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFieldMultipleTypesSliceReverse.php deleted file mode 100644 index 3b5739d86b..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFieldMultipleTypesSliceReverse.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1, - 'totalCount' => 12, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFolderName.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFolderName.php deleted file mode 100644 index 2509543764..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortFolderName.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1, - 'totalCount' => 6, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortLocationDepth.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortLocationDepth.php deleted file mode 100644 index 7e90cec30e..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortLocationDepth.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortMultiple.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortMultiple.php deleted file mode 100644 index 2ef1b3128e..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortMultiple.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 4, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortNone.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortNone.php deleted file mode 100644 index 1c5778e43d..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortNone.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortPathString.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortPathString.php deleted file mode 100644 index 78cf2d50ae..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortPathString.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortSectionIdentifier.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortSectionIdentifier.php deleted file mode 100644 index dd834abd8d..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortSectionIdentifier.php +++ /dev/null @@ -1,169 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 12 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 13 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 14, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortSectionName.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortSectionName.php deleted file mode 100644 index f4a23aeb05..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/SortSectionName.php +++ /dev/null @@ -1,169 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 12 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 13 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 14, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Status.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Status.php deleted file mode 100644 index db69bd77f7..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Status.php +++ /dev/null @@ -1,213 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 12 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 13 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 14 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 15 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 16 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 17 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 18, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Subtree.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Subtree.php deleted file mode 100644 index 1c5778e43d..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Subtree.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1.0, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1.0, - 'totalCount' => 8, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/UserMetadata.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/UserMetadata.php deleted file mode 100644 index f746804bcf..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/UserMetadata.php +++ /dev/null @@ -1,213 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state(array( - 'facets' => - array( - ), - 'searchHits' => - array( - 0 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 1 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 2 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 3 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 4 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 5 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 6 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 7 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 8 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 9 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 10 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 11 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 12 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 13 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 14 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 15 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 16 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - 17 => - eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state(array( - 'valueObject' => - array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 1, - 'index' => null, - 'highlight' => null, - )), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 1, - 'totalCount' => 18, -)); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Visibility.php b/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Visibility.php deleted file mode 100644 index 3e14606295..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Solr/Visibility.php +++ /dev/null @@ -1,212 +0,0 @@ -<?php - -return eZ\Publish\API\Repository\Values\Content\Search\SearchResult::__set_state( - array( - 'facets' => array(), - 'searchHits' => array( - 0 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 4, - 'title' => 'Users', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 1 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 10, - 'title' => 'Anonymous User', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 2 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 11, - 'title' => 'Members', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 3 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 12, - 'title' => 'Administrator users', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 4 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 13, - 'title' => 'Editors', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 5 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 14, - 'title' => 'Administrator User', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 6 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 41, - 'title' => 'Media', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 7 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 42, - 'title' => 'Anonymous Users', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 8 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 45, - 'title' => 'Setup', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 9 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 49, - 'title' => 'Images', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 10 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 50, - 'title' => 'Files', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 11 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 51, - 'title' => 'Multimedia', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 12 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 52, - 'title' => 'Common INI settings', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 13 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 54, - 'title' => 'eZ Publish Demo Design (without demo content)', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 14 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 56, - 'title' => 'Design', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 15 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 57, - 'title' => 'Home', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 16 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 58, - 'title' => 'Contact Us', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - 17 => eZ\Publish\API\Repository\Values\Content\Search\SearchHit::__set_state( - array( - 'valueObject' => array( - 'id' => 59, - 'title' => 'Partners', - ), - 'score' => 0.9459328, - 'index' => null, - 'highlight' => null, - ) - ), - ), - 'spellSuggestion' => null, - 'time' => 1, - 'timedOut' => null, - 'maxScore' => 0.9459328, - 'totalCount' => 18, - ) -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/URLAliasFixture.php b/eZ/Publish/API/Repository/Tests/_fixtures/URLAliasFixture.php deleted file mode 100644 index 77a277f12a..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/URLAliasFixture.php +++ /dev/null @@ -1,563 +0,0 @@ -<?php -return array( - array( - 14 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 14, - "type" => 2, - "destination" => null, - "path" => "/foo_bar_folder", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 39 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 39, - "type" => 0, - "destination" => 60, - "path" => "/Contact-Us", - "languageCodes" => array( - 0 => "eng-GB", - ), - "alwaysAvailable" => 0, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - /* - * @todo FIXME: Location for this alias not found in dump - 38 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 38, - "type" => 0, - "destination" => 59, - "path" => "/eZ-Publish", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - */ - 25 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 25, - "type" => 0, - "destination" => 58, - "path" => "/Design", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 13 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 13, - "type" => 0, - "destination" => 48, - "path" => "/Setup2", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 17 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 17, - "type" => 2, - "destination" => null, - "path" => "/media2", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 9 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 9, - "type" => 0, - "destination" => 43, - "path" => "/Media", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 21 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 21, - "type" => 2, - "destination" => null, - "path" => "/setup3", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 3 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 3, - "type" => 2, - "destination" => null, - "path" => "/users2", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 2 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 2, - "type" => 0, - "destination" => 5, - "path" => "/Users", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 1 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 1, - "type" => 0, - "destination" => 2, - "path" => "/", - "languageCodes" => array( - 0 => "eng-US", - 1 => "eng-GB", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 40 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 40, - "type" => 0, - "destination" => 61, - "path" => "/Users/Partners", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 6 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 6, - "type" => 0, - "destination" => 14, - "path" => "/Users/Editors", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 10 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 10, - "type" => 0, - "destination" => 44, - "path" => "/Users/Anonymous-Users", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 4 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 4, - "type" => 0, - "destination" => 12, - "path" => "/Users/Members", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 41 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 41, - "type" => 0, - "destination" => 12, - "path" => "/Users/Guest-accounts", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 5 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 5, - "type" => 0, - "destination" => 13, - "path" => "/Users/Administrator-users", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 11 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 11, - "type" => 2, - "destination" => null, - "path" => "/users2/anonymous_users2", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 26 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 26, - "type" => 0, - "destination" => 12, - "path" => "/users2/guest_accounts", - "languageCodes" => array( - ), - "alwaysAvailable" => 0, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 29 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 29, - "type" => 0, - "destination" => 14, - "path" => "/users2/editors", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 7 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 7, - "type" => 2, - "destination" => null, - "path" => "/users2/administrator_users2", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 27 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 27, - "type" => 0, - "destination" => 13, - "path" => "/users2/administrator_users", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 30 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 30, - "type" => 0, - "destination" => 44, - "path" => "/users2/anonymous_users", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 8 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 8, - "type" => 0, - "destination" => 15, - "path" => "/Users/Administrator-users/Administrator-User", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 28 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 28, - "type" => 0, - "destination" => 15, - "path" => "/users2/administrator_users2/administrator_user", - "languageCodes" => array( - ), - "alwaysAvailable" => 0, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 20 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 20, - "type" => 0, - "destination" => 53, - "path" => "/Media/Multimedia", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 19 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 19, - "type" => 0, - "destination" => 52, - "path" => "/Media/Files", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 18 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 18, - "type" => 0, - "destination" => 51, - "path" => "/Media/Images", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 12 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 12, - "type" => 0, - "destination" => 45, - "path" => "/Users/Anonymous-Users/Anonymous-User", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 1, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 31 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 31, - "type" => 0, - "destination" => 45, - "path" => "/users2/anonymous_users2/anonymous_user", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 22 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 22, - "type" => 0, - "destination" => 54, - "path" => "/Setup2/Common-INI-settings", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 0, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - 15 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 15, - "type" => 2, - "destination" => null, - "path" => "/foo_bar_folder/images", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 34 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 34, - "type" => 0, - "destination" => 53, - "path" => "/media2/multimedia", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 33 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 33, - "type" => 0, - "destination" => 52, - "path" => "/media2/files", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 32 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 32, - "type" => 0, - "destination" => 51, - "path" => "/media2/images", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 35 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 35, - "type" => 0, - "destination" => 54, - "path" => "/setup3/common_ini_settings", - "languageCodes" => array( - ), - "alwaysAvailable" => 1, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 37 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 37, - "type" => 0, - "destination" => 56, - "path" => "/Design/eZ-publish", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 0, - "isHistory" => true, - "isCustom" => false, - "forward" => true, - ) - ), - 24 => new \eZ\Publish\API\Repository\Values\Content\URLAlias( - array( - "id" => 24, - "type" => 0, - "destination" => 56, - "path" => "/Design/Plain-site", - "languageCodes" => array( - 0 => "eng-US", - ), - "alwaysAvailable" => 0, - "isHistory" => false, - "isCustom" => false, - "forward" => true, - ) - ), - ), - 41 -); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/generate-fixtures.php b/eZ/Publish/API/Repository/Tests/_fixtures/generate-fixtures.php deleted file mode 100755 index e9cfeefa86..0000000000 --- a/eZ/Publish/API/Repository/Tests/_fixtures/generate-fixtures.php +++ /dev/null @@ -1,896 +0,0 @@ -#!/usr/bin/env php -<?php -/** - * File containing a simple fixture generator - * - * @copyright Copyright (C) eZ Systems AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ - -namespace eZ\Publish\API\Repository\Tests; - -if (false === isset($argv[1]) || false === isset($argv[2])) { - echo 'Usage: ', PHP_EOL, - basename(__FILE__), ' <dump-file> <output-dir>', PHP_EOL; - exit(1); -} - -$fixture = include $argv[1]; - -writeFixtureFile(generateContentTypeGroupFixture($fixture), 'ContentTypeGroup', $argv[2]); -// echo generateContentTypeFixture( $fixture ); -writeFixtureFile(generateContentTypeFixture($fixture), 'ContentType', $argv[2]); -writeFixtureFile(generateSectionFixture($fixture), 'Section', $argv[2]); -writeFixtureFile(generateLanguageFixture($fixture), 'Language', $argv[2]); -writeFixtureFile(generateUserFixture($fixture), 'User', $argv[2]); -writeFixtureFile(generateUserGroupFixture($fixture), 'UserGroup', $argv[2]); -writeFixtureFile(generateRoleFixture($fixture), 'Role', $argv[2]); -writeFixtureFile(generateContentInfoFixture($fixture), 'Content', $argv[2]); -writeFixtureFile(generateLocationFixture($fixture), 'Location', $argv[2]); -writeFixtureFile(generateURLAliasFixture($fixture), 'URLAlias', $argv[2]); -writeFixtureFile(generateObjectStateGroupFixture($fixture), 'ObjectStateGroup', $argv[2]); -writeFixtureFile(generateObjectStateFixture($fixture), 'ObjectState', $argv[2]); - -function generateContentTypeGroupFixture(array $fixture) -{ - $nextId = 0; - $groups = array(); - foreach (getFixtureTable('ezcontentclassgroup', $fixture) as $data) { - $groups[$data['id']] = array( - 'id' => $data['id'], - 'identifier' => $data['name'], - 'creationDate' => dateCreateCall($data['created']), - 'modificationDate' => dateCreateCall($data['modified']), - 'creatorId' => $data['creator_id'], - 'modifierId' => $data['modifier_id'] - ); - $nextId = max($nextId, $data['id']); - } - - return generateReturnArray( - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Tests\\Stubs\\Values\\ContentType\\ContentTypeGroupStub', $groups), - $nextId - ); -} - -function generateContentTypeFixture(array $fixture) -{ - $languageCodes = array(); - foreach (getFixtureTable('ezcontent_language', $fixture) as $data) { - $languageCodes[$data['id']] = $data['locale']; - } - - $typeNames = array(); - foreach (getFixtureTable('ezcontentclass_name', $fixture) as $data) { - if (false === isset($typeNames[$data['contentclass_id']])) { - $typeNames[$data['contentclass_id']] = array(); - } - $typeNames[$data['contentclass_id']][$data['language_locale']] = $data['name']; - } - - $typeGroups = array(); - foreach (getFixtureTable('ezcontentclass_classgroup', $fixture) as $data) { - if (false === isset($typeGroups[$data['contentclass_id']])) { - $typeGroups[$data['contentclass_id']] = array(); - } - $typeGroups[$data['contentclass_id']][] = '$scopeValues["groups"][' . valueToString($data['group_id']) . ']'; - } - - list($fieldDef, $nextFieldId) = getContentTypeFieldDefinition($fixture); - - $nextTypeId = 0; - $types = array(); - foreach (getFixtureTable('ezcontentclass', $fixture) as $data) { - $types[$data['id']] = array( - 'id' => $data['id'], - 'status' => 0, // Type::STATUS_DEFINED - 'identifier' => $data['identifier'], - 'creationDate' => dateCreateCall($data['created']), - 'modificationDate' => dateCreateCall($data['modified']), - 'creatorId' => $data['creator_id'], - 'modifierId' => $data['modifier_id'], - 'remoteId' => $data['remote_id'], - // @todo: How do we build the userAliasSchema? - //'urlAliasSchema' => $data[] - 'names' => $typeNames[$data['id']], - 'descriptions' => array(), - 'nameSchema' => $data['contentobject_name'], - 'isContainer' => (boolean)$data['is_container'], - 'mainLanguageCode' => $languageCodes[$data['initial_language_id']], - 'defaultAlwaysAvailable' => (boolean)$data['always_available'], - 'defaultSortField' => $data['sort_field'], - 'defaultSortOrder' => $data['sort_order'], - - 'fieldDefinitions' => trim(generateValueObjects('\\eZ\\Publish\\API\\Repository\\Tests\\Stubs\\Values\\ContentType\\FieldDefinitionStub', isset($fieldDef[$data['id']]) ? $fieldDef[$data['id']] : array())), - 'contentTypeGroups' => isset($typeGroups[$data['id']]) ? $typeGroups[$data['id']] : array(), - ); - - $nextTypeId = max($nextTypeId, $data['id']); - } - - return generateReturnArray( - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Tests\\Stubs\\Values\\ContentType\\ContentTypeStub', $types), - $nextTypeId, - $nextFieldId - ); -} - -function getContentTypeFieldDefinition(array $fixture) -{ - $nextFieldId = 0; - $fieldDef = array(); - foreach (getFixtureTable('ezcontentclass_attribute', $fixture) as $data) { - if (false === isset($fieldDef[$data['contentclass_id']])) { - $fieldDef[$data['contentclass_id']] = array(); - } - - $names = filterTranslatedArray(unserialize($data['serialized_name_list'])); - - $description = filterTranslatedArray(unserialize($data['serialized_description_list'])); - - $fieldDef[$data['contentclass_id']][$data['id']] = array( - 'id' => (int)$data['id'], - 'identifier' => $data['identifier'], - 'fieldGroup' => $data['category'], - 'position' => (int)$data['placement'], - 'fieldTypeIdentifier' => $data['data_type_string'], - 'isTranslatable' => (boolean)$data['can_translate'], - 'isRequired' => (boolean)$data['is_required'], - 'isInfoCollector' => (boolean)$data['is_information_collector'], - 'isSearchable' => (boolean)$data['is_searchable'], - 'defaultValue' => null, - - 'names' => $names, - 'descriptions' => $description, - 'fieldSettings' => array(), - 'validatorConfiguration' => array(), - ); - - $nextFieldId = max($nextFieldId, $data['id']); - } - - return array( $fieldDef, $nextFieldId ); -} - -function filterTranslatedArray(array $translated) -{ - $filtered = array(); - foreach ($translated as $languageCode => $translation) { - if (is_string($languageCode)) { - $filtered[$languageCode] = $translation; - } - } - unset($filtered['always-available']); - return $filtered; -} - -function generateSectionFixture(array $fixture) -{ - $nextId = 0; - $sections = array(); - $identifiers = array(); - - foreach (getFixtureTable('ezsection', $fixture) as $data) { - $sections[$data['id']] = array( - 'id' => $data['id'], - 'name' => $data['name'], - 'identifier' => $data['identifier'], - ); - - if ($data['identifier']) { - $identifiers[$data['identifier']] = $data['id']; - } - - $nextId = max($nextId, $data['id']); - } - - $assignedContents = array(); - - foreach (getFixtureTable('ezcontentobject', $fixture) as $data) { - $sectionId = (int)$data['section_id']; - - if (!isset($assignedContents[$sectionId])) { - $assignedContents[$sectionId] = array(); - } - $assignedContents[$sectionId][(int)$data['id']] = true; - } - - return generateReturnArray( - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Section', $sections), - generateMapping($identifiers), - generateMapping($assignedContents), - $nextId - ); -} - -function getLanguageCodes(array $fixture) -{ - $languageCodes = array(); - foreach (getFixtureTable('ezcontent_language', $fixture) as $data) { - $languageCodes[$data['id']] = $data['locale']; - } - return $languageCodes; -} - -function generateContentInfoFixture(array $fixture) -{ - $nextId = 0; - $contentInfos = array(); - $indexMap = array(); - $languageCodes = getLanguageCodes($fixture); - - $mainLocationIds = array(); - foreach (getFixtureTable('ezcontentobject_tree', $fixture) as $data) { - $mainLocationIds[$data['contentobject_id']] = $data['main_node_id']; - } - - foreach (getFixtureTable('ezcontentobject', $fixture) as $data) { - $indexMap[$data['remote_id']] = array( - 'versionId' => array(), - 'contentId' => array(), - ); - - $indexMap[$data['id']] = array( - 'versionId' => array(), - 'contentId' => array(), - ); - - $contentInfos[$data['id']] = array( - 'id' => $data['id'], - 'name' => $data['name'], - 'contentTypeId' => $data['contentclass_id'], - 'sectionId' => $data['section_id'], - 'currentVersionNo' => $data['current_version'], - 'published' => ($data['published'] != 0), - 'ownerId' => $data['owner_id'], - 'modificationDate' => dateCreateCall($data['modified']), - 'publishedDate' => dateCreateCall($data['published']), - 'alwaysAvailable' => (boolean)($data['language_mask'] & 1), - 'remoteId' => $data['remote_id'], - 'mainLanguageCode' => $languageCodes[$data['initial_language_id']], - 'repository' => '$this', - 'mainLocationId' => $mainLocationIds[$data['id']], - ); - $nextId = max($nextId, $data['id']); - } - - list($fieldDef) = getContentTypeFieldDefinition($fixture); - - $fields = array(); - $fieldNextId = 0; - - foreach (getFixtureTable('ezcontentobject_attribute', $fixture) as $data) { - if (trim($data['data_text'])) { - $value = $data['data_text']; - } elseif (is_numeric($data['data_float']) && $data['data_float'] > 0) { - $value = $data['data_float']; - } else { - $value = $data['data_int']; - } - - $identifier = null; - foreach ($fieldDef as $def) { - if (isset($def[$data['contentclassattribute_id']])) { - $identifier = $def[$data['contentclassattribute_id']]['identifier']; - break; - } - } - - $fields[$data['id']] = array( - 'id' => $data['id'], - 'value' => $value, - 'languageCode' => $data['language_code'], - 'fieldDefIdentifier' => $identifier, - - 'contentId' => $data['contentobject_id'], - 'version' => $data['version'] - ); - - $fieldNextId = max($fieldNextId, $data['id']); - } - - $names = array(); - foreach (getFixtureTable('ezcontentobject_name', $fixture) as $data) { - $objectId = $data['contentobject_id']; - $versionNo = $data['content_version']; - - if (!isset($names[$objectId])) { - $names[$objectId] = array(); - } - if (!isset($names[$objectId][$versionNo])) { - $names[$objectId][$versionNo] = array(); - } - - foreach ($languageCodes as $bit => $code) { - if ($data['language_id'] & $bit) { - $names[$objectId][$versionNo][$code] = $data['name']; - } - } - } - - $content = array(); - $versionInfo = array(); - $versionNextId = 0; - foreach (getFixtureTable('ezcontentobject_version', $fixture) as $data) { - $versionInfo[$data['id']] = array( - 'id' => $data['id'], - 'contentId' => $data['contentobject_id'], - 'status' => $data['status'] <= 2 ? $data['status'] : 1, - 'versionNo' => $data['version'], - 'modificationDate' => dateCreateCall($data['modified']), - 'creatorId' => $data['creator_id'], - 'creationDate' => dateCreateCall($data['created']), - 'initialLanguageCode' => $languageCodes[$data['initial_language_id']], - 'languageCodes' => array(), // @todo: Extract language codes from fields - 'repository' => '$this', - 'names' => $names[$data['contentobject_id']][$data['version']], - ); - - $versionNextId = max($versionNextId, $data['id']); - - $contentFields = array(); - foreach ($fields as $field) { - if ($field['contentId'] != $data['contentobject_id']) { - continue; - } - if ($field['version'] != $data['version']) { - continue; - } - - unset($field['contentId'], $field['version']); - - $contentFields[] = $field; - } - - $contentFields = trim(generateValueObjects('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Field', $contentFields)); - - $content[] = array( - 'id' => $data['contentobject_id'], - 'contentTypeId' => $contentInfos[$data['contentobject_id']]['contentTypeId'], - 'fields' => $contentFields, - 'versionNo' => $data['version'], - 'repository' => '$this' - ); - - $remoteId = $contentInfos[$data['contentobject_id']]['remoteId']; - - $versionId = $data['id']; - $contentId = $data['contentobject_id']; - - $indexMap[$remoteId]['versionId'][$versionId] = $versionId; - $indexMap[$remoteId]['contentId'][$contentId] = $contentId; - $indexMap[$contentId]['versionId'][$versionId] = $versionId; - $indexMap[$contentId]['contentId'][$contentId] = $contentId; - } - - uasort( - $versionInfo, - function ($versionInfo1, $versionInfo2) { - if ($versionInfo1['contentId'] === $versionInfo2['contentId']) { - return $versionInfo2['versionNo'] - $versionInfo1['versionNo']; - } - return $versionInfo1['contentId'] - $versionInfo2['contentId']; - } - ); - - uasort( - $content, - function ($content1, $content2) { - if ($content1['id'] === $content2['id']) { - return $content2['versionNo'] - $content1['versionNo']; - } - return $content1['id'] - $content2['id']; - } - ); - - return generateReturnArray( - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Tests\\Stubs\\Values\\Content\\ContentInfoStub', $contentInfos), - $nextId, - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Tests\\Stubs\\Values\\Content\\VersionInfoStub', $versionInfo), - $versionNextId, - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Tests\\Stubs\\Values\\Content\\ContentStub', $content), - generateMapping($indexMap) - ); -} - -function generateLocationFixture(array $fixture) -{ - $nextId = 0; - $locations = array(); - - foreach (getFixtureTable('ezcontentobject_tree', $fixture) as $data) { - $locations[$data['node_id']] = array( - 'id' => $data['node_id'], - 'priority' => $data['priority'], - 'hidden' => (bool)$data['is_hidden'], - 'invisible' => (bool)$data['is_invisible'], - 'remoteId' => $data['remote_id'], - 'contentInfo' => ($data['node_id'] == 1 ? null :createRepoCall( - 'ContentService', - 'loadContentInfo', - array( $data['contentobject_id'] ) - )), - 'parentLocationId' => $data['parent_node_id'], - 'pathString' => $data['path_string'], - 'depth' => $data['depth'], - 'sortField' => $data['sort_field'], - 'sortOrder' => $data['sort_order'], - ); - $nextId = max($nextId, $data['node_id']); - } - - return generateReturnArray( - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Tests\\Stubs\\Values\\Content\\LocationStub', $locations), - $nextId - ); -} - -function createRepoCall($serviceName, $methodName, array $params) -{ - foreach ($params as $id => $param) { - $params[$id] = valueToString($param); - } - return sprintf( - '$this->get%s()->%s( %s )', - $serviceName, - $methodName, - implode(', ', $params) - ); -} - -function dateCreateCall($timestamp) -{ - return sprintf( - '$this->createDateTime( %s )', - $timestamp - ); -} - -function generateLanguageFixture(array $fixture) -{ - $nextId = 0; - $languages = array(); - $languageCodes = array(); - - foreach (getFixtureTable('ezcontent_language', $fixture) as $data) { - $languages[$data['id']] = array( - 'id' => $data['id'], - 'name' => $data['name'], - 'enabled' => !$data['disabled'], - 'languageCode' => $data['locale'] - ); - - $languageCodes[$data['locale']] = $data['id']; - - $nextId = max($nextId, $data['id']); - } - - return generateReturnArray( - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Values\\Content\\Language', $languages), - generateMapping($languageCodes), - $nextId - ); -} - -function generateUserFixture(array $fixture) -{ - $users = array(); - foreach (getFixtureTable('ezuser', $fixture) as $data) { - $users[] = array( - '_id' => $data['contentobject_id'], - 'login' => $data['login'], - 'email' => $data['email'], - 'passwordHash' => $data['password_hash'], - 'hashAlgorithm' => $data['password_hash_type'], - 'enabled' => true, - 'content' => '$this->getContentService()->loadContent( ' . $data['contentobject_id'] . ' )' - ); - } - - return generateReturnArray( - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Tests\\Stubs\\Values\\User\\UserStub', $users) - ); -} - -function generateUserGroupFixture(array $fixture) -{ - $classId = null; - foreach (getFixtureTable('ezcontentclass', $fixture) as $data) { - if ('user_group' === $data['identifier']) { - $classId = $data['id']; - break; - } - } - - $node2parentId = array(); - $content2nodeId = array(); - foreach (getFixtureTable('ezcontentobject_tree', $fixture) as $data) { - $content2nodeId[$data['contentobject_id']] = $data['node_id']; - $node2parentId[$data['node_id']] = $data['parent_node_id']; - } - - $groups = array(); - foreach (getFixtureTable('ezcontentobject', $fixture) as $data) { - if ($data['contentclass_id'] != $classId) { - continue; - } - - $parentId = null; - if (isset($content2nodeId[$data['id']])) { - $nodeId = $content2nodeId[$data['id']]; - if (isset($node2parentId[$nodeId])) { - $parentId = array_search($node2parentId[$nodeId], $content2nodeId); - } - } - - $groups[$data['id']] = array( - '_id' => $data['id'], - 'parentId' => is_numeric($parentId) ? $parentId : 'null', - 'content' => '$this->getContentService()->loadContent( ' . $data['id'] . ' )' - ); - } - - return generateReturnArray( - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Tests\\Stubs\\Values\\User\\UserGroupStub', $groups) - ); -} - -function generateRoleFixture(array $fixture) -{ - $nextId = 0; - $names = array(); - $roles = array(); - - foreach (getFixtureTable('ezrole', $fixture) as $data) { - $roles[$data['id']] = array( - 'id' => $data['id'], - 'identifier' => $data['name'] - ); - - $names[$data['name']] = $data['id']; - - $nextId = max($nextId, $data['id']); - } - - $content2role = array(); - $roleLimitations = array(); - - foreach (getFixtureTable('ezuser_role', $fixture) as $data) { - if (false === isset($content2role[$data['contentobject_id']])) { - $content2role[$data['contentobject_id']] = array(); - } - $content2role[$data['contentobject_id']][$data['id']] = $data['role_id']; - - if ('' === trim($data['limit_identifier'])) { - continue; - } - - $roleLimitations[$data['id']] = array( - 'id' => $data['id'], - 'roleId' => $data['role_id'], - 'contentId' => $data['contentobject_id'], - 'identifier' => $data['limit_identifier'], - 'value' => array( $data['limit_value'] ) - ); - } - - $role2policy = array(); - $policies = array(); - $policyNextId = 0; - foreach (getFixtureTable('ezpolicy', $fixture) as $data) { - if (false === isset($role2policy[$data['role_id']])) { - $role2policy[$data['role_id']] = array(); - } - $role2policy[$data['role_id']][] = $data['id']; - - $policies[$data['id']] = array( - 'id' => $data['id'], - 'roleId' => $data['role_id'], - 'module' => $data['module_name'], - 'function' => $data['function_name'], - 'limitations' => array() - ); - - $policyNextId = max($policyNextId, $data['id']); - } - - return generateReturnArray( - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Tests\\Stubs\\Values\\User\\RoleStub', $roles), - generateMapping($names), - $nextId, - generateMapping($content2role), - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Tests\\Stubs\\Values\\User\\PolicyStub', $policies), - $policyNextId, - generateMapping($role2policy), - generateMapping($roleLimitations) - ); -} - -function generateURLAliasFixture(array $fixture) -{ - $typeMap = array( - 'nop' => 2, - 'eznode' => 0, - 'module' => 1, - ); - - $languageCodes = getLanguageCodes($fixture); - - $nextId = 0; - $aliases = array(); - foreach (getFixtureTable('ezurlalias_ml', $fixture) as $data) { - if ($data['id'] == 46 || $data['id'] == 16 || $data['id'] == 206) { - // These 2 aliases are broken in the standard database, since they - // don't have a valid location assigned - continue; - } - - $destination = null; - switch ($data['action_type']) { - case 'nop': - $destination = null; - break; - - case 'eznode': - $destination = substr($data['action'], 7); - break; - - case 'module': - $destination = substr($data['action'], 7); - break; - } - - $aliases[$data['id']] = array( - 'id' => $data['id'], - 'type' => $typeMap[$data['action_type']], - 'destination' => $destination, - 'path' => getBaseUrlPath($aliases, $data['parent']) . '/' . $data['text'], - 'languageCodes' => resolveLanguageMask($languageCodes, $data['lang_mask']), - 'alwaysAvailable' => $data['lang_mask'] & 1, - 'isHistory' => !($data['is_original']), - 'isCustom' => (bool)$data['is_alias'], - 'forward' => (bool)$data['alias_redirects'], - ); - $nextId = max($nextId, $data['id']); - } - - return generateReturnArray( - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Values\\Content\\URLAlias', $aliases), - $nextId - ); -} - -function generateObjectStateGroupFixture(array $fixture) -{ - $languageCodes = getLanguageCodes($fixture); - - $nextId = 0; - $groups = array(); - foreach (getFixtureTable('ezcobj_state_group', $fixture) as $data) { - $groups[$data['id']] = array( - 'id' => $data['id'], - 'identifier' => $data['identifier'], - 'mainLanguageCode' => $languageCodes[$data['default_language_id']], - 'names' => array(), - 'descriptions' => array(), - ); - $nextId = max($nextId, $data['id']); - } - - foreach (getFixtureTable('ezcobj_state_group_language', $fixture) as $data) { - $groupId = $data['contentobject_state_group_id']; - // Set lowest bit to 0 (always_available) - $langCode = $languageCodes[($data['language_id'] & -2)]; - - $groups[$groupId]['names'][$langCode] = $data['name']; - $groups[$groupId]['descriptions'][$langCode] = $data['description']; - } - - return generateReturnArray( - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Tests\\Stubs\\Values\\ObjectState\\ObjectStateGroupStub', $groups), - $nextId - ); -} - -function generateObjectStateFixture(array $fixture) -{ - $languageCodes = getLanguageCodes($fixture); - - $nextId = 0; - $states = array(); - $groupStateMap = array(); - $objectStateMap = array(); - // For internal use only - $stateGroupMap = array(); - - foreach (getFixtureTable('ezcobj_state', $fixture) as $data) { - $states[$data['id']] = array( - 'id' => $data['id'], - 'identifier' => $data['identifier'], - 'priority' => (int)$data['priority'], - 'mainLanguageCode' => $languageCodes[$data['default_language_id']], - 'languageCodes' => resolveLanguageMask($languageCodes, $data['language_mask']), - 'stateGroup' => '$scopeValues["groups"][' . valueToString($data['group_id']) . ']', - 'names' => array(), - 'descriptions' => array(), - ); - $nextId = max($nextId, $data['id']); - - $groupId = $data['group_id']; - if (!isset($groupStateMap[$groupId])) { - $groupStateMap[$groupId] = array(); - } - $groupStateMap[$groupId][$data['id']] = $data['id']; - - // For internal use only - $stateGroupMap[$data['id']] = $groupId; - } - - foreach (getFixtureTable('ezcobj_state_language', $fixture) as $data) { - $stateId = $data['contentobject_state_id']; - // Set lowest bit to 0 (always_available) - $langCode = $languageCodes[($data['language_id'] & -2)]; - - $states[$stateId]['names'][$langCode] = $data['name']; - $states[$stateId]['descriptions'][$langCode] = $data['description']; - } - - foreach (getFixtureTable('ezcobj_state_link', $fixture) as $data) { - $stateId = $data['contentobject_state_id']; - $objectId = $data['contentobject_id']; - - $groupId = $stateGroupMap[$stateId]; - - if (!isset($objectStateMap[$objectId])) { - $objectStateMap[$objectId] = array(); - } - $objectStateMap[$objectId][$groupId] = $stateId; - } - - return generateReturnArray( - generateValueObjects('\\eZ\\Publish\\API\\Repository\\Tests\\Stubs\\Values\\ObjectState\\ObjectStateStub', $states), - var_export($groupStateMap, true), - var_export($objectStateMap, true), - $nextId - ); -} - -function resolveLanguageMask(array $languageCodes, $mask) -{ - $assignedCodes = array(); - foreach ($languageCodes as $id => $languageCode) { - if ($mask & $id) { - $assignedCodes[] = $languageCode; - } - } - return $assignedCodes; -} - -function getBaseUrlPath(array $aliases, $parentId) -{ - if ($parentId == 0) { - return ''; - } - if (!isset($aliases[$parentId])) { - throw new RuntimeException(sprintf('Alias with ID %s not found.', $parentId)); - } - return $aliases[$parentId]['path']; -} - -function getUser2GroupMapping(array $fixture) -{ - $users = array(); - foreach (getFixtureTable('ezuser', $fixture) as $data) { - $users[] = (int)$data['contentobject_id']; - } - - $nodes = array(); - $contents = array(); - foreach (getFixtureTable('ezcontentobject_tree', $fixture) as $data) { - $nodes[$data['node_id']] = $data['parent_node_id']; - $contents[$data['node_id']] = $data['contentobject_id']; - } - - $groups = array(); - foreach ($contents as $key => $contentId) { - if (false === in_array($contentId, $users)) { - continue; - } - - if (false === isset($groups[$contentId])) { - $groups[$contentId] = array(); - } - $groups[$contentId][] = $contents[$nodes[$key]]; - } - - return $groups; -} - -function generateReturnArray() -{ - return '<?php' . PHP_EOL . - 'return array(' . PHP_EOL . - implode(',' . PHP_EOL . ' ', func_get_args()) . PHP_EOL . - ');' . PHP_EOL; -} - -function generateMapping(array $mapping) -{ - $code = 'array(' . PHP_EOL; - foreach ($mapping as $key => $value) { - $code .= ' "' . $key . '" => ' . valueToString($value) . ',' . PHP_EOL; - } - $code .= ' )'; - - return $code; -} - -function generateValueObjects($class, array $objects) -{ - $code = ' array(' . PHP_EOL; - foreach ($objects as $object) { - $code .= generateValueObject($class, $object); - } - $code .= ' )'; - - return $code; -} - -function generateValueObject($class, array $object) -{ - $id = isset($object['id']) - ? $object['id'] - : $object['_id']; - $code = ' ' . $id . ' => new ' . $class . '(' . PHP_EOL . - ' array(' . PHP_EOL; - foreach ($object as $name => $value) { - if ($name[0] == '_') { - continue; - } - - $code .= ' "' . $name . '" => ' . valueToString($value) . ',' . PHP_EOL; - } - - $code .= ' )' . PHP_EOL . - ' ),' . PHP_EOL; - - return $code; -} - -function valueToString($value, $indent = 4) -{ - if (is_numeric($value)) { - $value = $value; - } elseif (is_bool($value)) { - $value = $value ? 'true' : 'false'; - } elseif (null === $value) { - $value = 'null'; - } elseif (is_string($value) && 0 !== strpos($value, 'new \\') && 0 !== strpos($value, '$scopeValues[') && 0 !== strpos($value, 'array(') && 0 !== strpos($value, '$this')) { - $value = '"' . str_replace('"', '\"', $value) . '"'; - } elseif (is_array($value)) { - $code = 'array(' . PHP_EOL; - foreach ($value as $key => $val) { - $code .= str_repeat(' ', $indent + 1) . - valueToString($key) . - ' => ' . - valueToString($val, $indent + 1) . - ',' . PHP_EOL; - } - $value = $code . str_repeat(' ', $indent) . ')'; - } - - return $value; -} - -function getFixtureTable($tableName, array $fixture) -{ - if (isset($fixture[$tableName])) { - return $fixture[$tableName]; - } - return array(); -} - -function writeFixtureFile($code, $file, $dir) -{ - file_put_contents("{$dir}/{$file}Fixture.php", $code); -} diff --git a/eZ/Publish/API/Repository/Translatable.php b/eZ/Publish/API/Repository/Translatable.php deleted file mode 100644 index ba29d5d9a4..0000000000 --- a/eZ/Publish/API/Repository/Translatable.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository; - -/** - * Interface implemented by everything which should be translatable. This - * should for example be implemented by any exception, which might bubble up to - * a user, or validation errors. - */ -interface Translatable -{ - /** - * Returns a translatable Message. - * - * @return \eZ\Publish\API\Repository\Values\Translation - */ - public function getTranslatableMessage(); -} diff --git a/eZ/Publish/API/Repository/TranslationService.php b/eZ/Publish/API/Repository/TranslationService.php deleted file mode 100644 index 4958b31b81..0000000000 --- a/eZ/Publish/API/Repository/TranslationService.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository; - -use eZ\Publish\API\Repository\Values\Translation; - -/** - * Interface for a translation service. - * - * Implement this to use translation backends like Symfony2 Translate, gettext - * or ezcTranslation. - * - * Call the translation method with the current target locale from your - * templates, for example. - */ -interface TranslationService -{ - /** - * Translate. - * - * Translate a Translation value object. - * - * @param \eZ\Publish\API\Repository\Values\Translation $translation - * @param string $locale - * - * @return string - */ - public function translate(Translation $translation, $locale); - - /** - * Translate string. - * - * Translate a string. Strings could be useful for the simplest cases. - * Usually you will always use Translation value objects for this. - * - * @param string $translation - * @param string $locale - * - * @return string - */ - public function translateString($translation, $locale); -} diff --git a/eZ/Publish/API/Repository/TrashService.php b/eZ/Publish/API/Repository/TrashService.php deleted file mode 100644 index 936c33e898..0000000000 --- a/eZ/Publish/API/Repository/TrashService.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository; - -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Trash\SearchResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResultList; -use eZ\Publish\API\Repository\Values\Content\TrashItem; - -/** - * Trash service, used for managing trashed content. - */ -interface TrashService -{ - /** - * Loads a trashed location object from its $id. - * - * Note that $id is identical to original location, which has been previously trashed - * - * @param int $trashItemId - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the trashed location - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the location with the given id does not exist - * - * @return \eZ\Publish\API\Repository\Values\Content\TrashItem - */ - public function loadTrashItem(int $trashItemId): TrashItem; - - /** - * Sends $location and all its children to trash and returns the corresponding trash item. - * - * The current user may not have access to the returned trash item, check before using it. - * Content is left untouched. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to trash the given location - * - * @return \eZ\Publish\API\Repository\Values\Content\TrashItem|null null if location was deleted, otherwise TrashItem - */ - public function trash(Location $location): ?TrashItem; - - /** - * Recovers the $trashedLocation at its original place if possible. - * - * If $newParentLocation is provided, $trashedLocation will be restored under it. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to recover the trash item at the parent location location - * - * @param \eZ\Publish\API\Repository\Values\Content\TrashItem $trashItem - * @param \eZ\Publish\API\Repository\Values\Content\Location $newParentLocation - * - * @return \eZ\Publish\API\Repository\Values\Content\Location the newly created or recovered location - */ - public function recover(TrashItem $trashItem, Location $newParentLocation = null): Location; - - /** - * Empties trash. - * - * All locations contained in the trash will be removed. Content objects will be removed - * if all locations of the content are gone. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to empty the trash - * - * @return \eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResultList - */ - public function emptyTrash(): TrashItemDeleteResultList; - - /** - * Deletes a trash item. - * - * The corresponding content object will be removed - * - * @param \eZ\Publish\API\Repository\Values\Content\TrashItem $trashItem - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete this trash item - * - * @return \eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult - */ - public function deleteTrashItem(TrashItem $trashItem): TrashItemDeleteResult; - - /** - * Returns a collection of Trashed locations contained in the trash, which are readable by the current user. - * - * $query allows to filter/sort the elements to be contained in the collection. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - * - * @return \eZ\Publish\API\Repository\Values\Content\Trash\SearchResult - */ - public function findTrashItems(Query $query): SearchResult; -} diff --git a/eZ/Publish/API/Repository/URLAliasService.php b/eZ/Publish/API/Repository/URLAliasService.php deleted file mode 100644 index 180fe89709..0000000000 --- a/eZ/Publish/API/Repository/URLAliasService.php +++ /dev/null @@ -1,177 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository; - -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\URLAlias; - -/** - * URLAlias service. - */ -interface URLAliasService -{ - /** - * Create a user chosen $alias pointing to $location in $languageCode. - * - * This method runs URL filters and transformers before storing them. - * Hence the path returned in the URLAlias Value may differ from the given. - * $alwaysAvailable makes the alias available in all languages. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param string $path - * @param string $languageCode the languageCode for which this alias is valid - * @param bool $forwarding if true a redirect is performed - * @param bool $alwaysAvailable - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create url alias - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the path already exists for the given context - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - public function createUrlAlias( - Location $location, - string $path, - string $languageCode, - bool $forwarding = false, - bool $alwaysAvailable = false - ): URLAlias; - - /** - * Create a user chosen $alias pointing to a resource in $languageCode. - * - * This method does not handle location resources - if a user enters a location target - * the createCustomUrlAlias method has to be used. - * This method runs URL filters and and transformers before storing them. - * Hence the path returned in the URLAlias Value may differ from the given. - * - * $alwaysAvailable makes the alias available in all languages. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create global - * url alias - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the path already exists for the given - * language or if resource is not valid - * - * @param string $resource - * @param string $path - * @param string $languageCode - * @param bool $forwarding - * @param bool $alwaysAvailable - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - public function createGlobalUrlAlias( - string $resource, - string $path, - string $languageCode, - bool $forwarding = false, - bool $alwaysAvailable = false - ): URLAlias; - - /** - * List of url aliases pointing to $location, sorted by language priority. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param bool $custom if true the user generated aliases are listed otherwise the autogenerated - * @param string|null $languageCode filters those which are valid for the given language - * @param bool|null $showAllTranslations If enabled will include all alias as if they where always available. - * @param string[]|null $prioritizedLanguages If set used as prioritized language codes, returning first match. - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias[] - */ - public function listLocationAliases( - Location $location, - bool $custom = true, - ?string $languageCode = null, - ?bool $showAllTranslations = null, - ?array $prioritizedLanguages = null - ): iterable; - - /** - * List global aliases. - * - * @param string|null $languageCode filters those which are valid for the given language - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias[] - */ - public function listGlobalAliases(?string $languageCode = null, int $offset = 0, int $limit = -1): iterable; - - /** - * Removes urls aliases. - * - * This method does not remove autogenerated aliases for locations. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove url alias - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if alias list contains - * autogenerated alias - * - * @param \eZ\Publish\API\Repository\Values\Content\URLAlias[] $aliasList - */ - public function removeAliases(array $aliasList): void; - - /** - * looks up the URLAlias for the given url. - * - * @param string $url - * @param string|null $languageCode - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the path does not exist or is not valid for the given language - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the path exceeded maximum depth level - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - public function lookup(string $url, ?string $languageCode = null): URLAlias; - - /** - * Returns the URL alias for the given location in the given language. - * - * If $languageCode is null the method returns the url alias in the most prioritized language. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if no url alias exist for the given language - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param string|null $languageCode filters those which are valid for the given language - * @param bool|null $showAllTranslations If enabled will include all alias as if they where always available. - * @param string[]|null $prioritizedLanguages If set used as prioritized language codes, returning first match. - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - public function reverseLookup( - Location $location, - ?string $languageCode = null, - ?bool $showAllTranslations = null, - ?array $prioritizedLanguages = null - ): URLAlias; - - /** - * Loads URL alias by given $id. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * - * @param string $id - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - public function load(string $id): URLAlias; - - /** - * Refresh all system URL aliases for the given Location (and historize outdated if needed). - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - */ - public function refreshSystemUrlAliasesForLocation(Location $location): void; - - /** - * Delete global, system or custom URL alias pointing to non-existent Locations. - * - * @return int Number of deleted URL aliases - */ - public function deleteCorruptedUrlAliases(): int; -} diff --git a/eZ/Publish/API/Repository/URLService.php b/eZ/Publish/API/Repository/URLService.php deleted file mode 100644 index 455a709443..0000000000 --- a/eZ/Publish/API/Repository/URLService.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository; - -use eZ\Publish\API\Repository\Values\URL\SearchResult; -use eZ\Publish\API\Repository\Values\URL\URL; -use eZ\Publish\API\Repository\Values\URL\URLQuery; -use eZ\Publish\API\Repository\Values\URL\URLUpdateStruct; -use eZ\Publish\API\Repository\Values\URL\UsageSearchResult; - -/** - * URL Service. - */ -interface URLService -{ - /** - * Instantiates a new URL update struct. - * - * @return \eZ\Publish\API\Repository\Values\URL\URLUpdateStruct - */ - public function createUpdateStruct(): URLUpdateStruct; - - /** - * Find URLs. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * - * @param \eZ\Publish\API\Repository\Values\URL\URLQuery $query - * - * @return \eZ\Publish\API\Repository\Values\URL\SearchResult - */ - public function findUrls(URLQuery $query): SearchResult; - - /** - * Find content objects using URL. - * - * Content is filter by user permissions. - * - * @param \eZ\Publish\API\Repository\Values\URL\URL $url - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\API\Repository\Values\URL\UsageSearchResult - */ - public function findUsages(URL $url, int $offset = 0, int $limit = -1): UsageSearchResult; - - /** - * Load single URL (by ID). - * - * @param int $id ID of URL - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * - * @return \eZ\Publish\API\Repository\Values\URL\URL - */ - public function loadById(int $id): URL; - - /** - * Load single URL (by URL). - * - * @param string $url URL - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * - * @return \eZ\Publish\API\Repository\Values\URL\URL - */ - public function loadByUrl(string $url): URL; - - /** - * Updates URL. - * - * @param \eZ\Publish\API\Repository\Values\URL\URL $url - * @param \eZ\Publish\API\Repository\Values\URL\URLUpdateStruct $struct - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the url already exists - * - * @return \eZ\Publish\API\Repository\Values\URL\URL - */ - public function updateUrl(URL $url, URLUpdateStruct $struct): URL; -} diff --git a/eZ/Publish/API/Repository/URLWildcardService.php b/eZ/Publish/API/Repository/URLWildcardService.php deleted file mode 100644 index e34e674d6d..0000000000 --- a/eZ/Publish/API/Repository/URLWildcardService.php +++ /dev/null @@ -1,111 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository; - -use eZ\Publish\API\Repository\Values\Content\URLWildcard; -use eZ\Publish\API\Repository\Values\Content\URLWildcardTranslationResult; -use eZ\Publish\API\Repository\Values\Content\URLWildcardUpdateStruct; -use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\SearchResult; -use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\URLWildcardQuery; - -/** - * URLAlias service. - * - * @example Examples/urlalias.php - */ -interface URLWildcardService -{ - /** - * Creates a new url wildcard. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the $sourceUrl pattern already exists - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create url wildcards - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if the number of "*" patterns in $sourceUrl and - * the number of {\d} placeholders in $destinationUrl doesn't match or - * if the placeholders aren't a valid number sequence({1}/{2}/{3}), starting with 1. - * - * @param string $sourceUrl - * @param string $destinationUrl - * @param bool $forward - * - * @return \eZ\Publish\API\Repository\Values\Content\UrlWildcard - */ - public function create(string $sourceUrl, string $destinationUrl, bool $forward = false): UrlWildcard; - - /** - * Update an url wildcard. - * - * @param \eZ\Publish\API\Repository\Values\Content\URLWildcard $urlWildcard - * @param \eZ\Publish\API\Repository\Values\Content\URLWildcardUpdateStruct $updateStruct - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update url wildcards - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if the number of "*" patterns in $sourceUrl and - * the number of {\d} placeholders in $destinationUrl doesn't match or - * if the placeholders aren't a valid number sequence({1}/{2}/{3}), starting with 1. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the $sourceUrl pattern already exists - */ - public function update( - URLWildcard $urlWildcard, - URLWildcardUpdateStruct $updateStruct - ): void; - - /** - * Removes an url wildcard. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove url wildcards - * - * @param \eZ\Publish\API\Repository\Values\Content\UrlWildcard $urlWildcard the url wildcard to remove - */ - public function remove(URLWildcard $urlWildcard): void; - - /** - * Loads a url wild card. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the url wild card was not found - * - * @param int $id - * - * @return \eZ\Publish\API\Repository\Values\Content\UrlWildcard - */ - public function load(int $id): UrlWildcard; - - /** - * Loads all url wild card (paged). - * - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\API\Repository\Values\Content\UrlWildcard[] - */ - public function loadAll(int $offset = 0, int $limit = -1): iterable; - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function findUrlWildcards(URLWildcardQuery $query): SearchResult; - - /** - * Translates an url to an existing uri resource based on the - * source/destination patterns of the url wildcard. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the url could not be translated - * - * @param string $url - * - * @return \eZ\Publish\API\Repository\Values\Content\URLWildcardTranslationResult - */ - public function translate(string $url): URLWildcardTranslationResult; - - /** - * Counts URL Wildcards. - */ - public function countAll(): int; -} diff --git a/eZ/Publish/API/Repository/UserPreferenceService.php b/eZ/Publish/API/Repository/UserPreferenceService.php deleted file mode 100644 index 26c8fc714e..0000000000 --- a/eZ/Publish/API/Repository/UserPreferenceService.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository; - -use eZ\Publish\API\Repository\Values\UserPreference\UserPreference; -use eZ\Publish\API\Repository\Values\UserPreference\UserPreferenceList; - -/** - * User Preference Service. - * - * This service provides methods for managing user preferences. It works in the context of a current User (obtained from the PermissionResolver). - */ -interface UserPreferenceService -{ - /** - * Set user preference. - * - * @param \eZ\Publish\API\Repository\Values\UserPreference\UserPreferenceSetStruct[] $userPreferenceSetStructs - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to set user preference - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException If the $userPreferenceSetStruct is invalid - */ - public function setUserPreference(array $userPreferenceSetStructs): void; - - /** - * Get currently logged user preference by key. - * - * @param string $userPreferenceName - * - * @return \eZ\Publish\API\Repository\Values\UserPreference\UserPreference - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current user user is not allowed to fetch user preference - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function getUserPreference(string $userPreferenceName): UserPreference; - - /** - * Get currently logged user preferences. - * - * @param int $offset the start offset for paging - * @param int $limit the number of user preferences returned - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @return \eZ\Publish\API\Repository\Values\UserPreference\UserPreferenceList - */ - public function loadUserPreferences(int $offset = 0, int $limit = 25): UserPreferenceList; - - /** - * Get count of total preferences for currently logged user. - * - * @return int - */ - public function getUserPreferenceCount(): int; -} diff --git a/eZ/Publish/API/Repository/UserService.php b/eZ/Publish/API/Repository/UserService.php deleted file mode 100644 index 962e97bc31..0000000000 --- a/eZ/Publish/API/Repository/UserService.php +++ /dev/null @@ -1,409 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\User\PasswordInfo; -use eZ\Publish\API\Repository\Values\User\PasswordValidationContext; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserTokenUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserUpdateStruct; - -/** - * This service provides methods for managing users and user groups. - */ -interface UserService -{ - /** - * Creates a new user group using the data provided in the ContentCreateStruct parameter. - * - * In 4.x in the content type parameter in the profile is ignored - * - the content type is determined via configuration and can be set to null. - * The returned version is published. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct $userGroupCreateStruct a structure for setting all necessary data to create this user group - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $parentGroup - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the input structure has invalid data - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userGroupCreateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is missing or set to an empty value - */ - public function createUserGroup(UserGroupCreateStruct $userGroupCreateStruct, UserGroup $parentGroup): UserGroup; - - /** - * Loads a user group for the given id. - * - * @param int $id - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to load a user group - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the user group with the given id was not found - */ - public function loadUserGroup(int $id, array $prioritizedLanguages = []): UserGroup; - - /** - * Loads a user group for the given remote id. - * - * @param string $remoteId - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to load a user group - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the user group with the given id was not found - */ - public function loadUserGroupByRemoteId(string $remoteId, array $prioritizedLanguages = []): UserGroup; - - /** - * Loads the sub groups of a user group. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * @param int $offset the start offset for paging - * @param int $limit the number of user groups returned - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read the user group - */ - public function loadSubUserGroups(UserGroup $userGroup, int $offset = 0, int $limit = 25, array $prioritizedLanguages = []): iterable; - - /** - * Removes a user group. - * - * the users which are not assigned to other groups will be deleted. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group - * - * @return int[] Affected Location Id's (List of Locations of the Content that was deleted) - */ - public function deleteUserGroup(UserGroup $userGroup): iterable; - - /** - * Moves the user group to another parent. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $newParent - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to move the user group - */ - public function moveUserGroup(UserGroup $userGroup, UserGroup $newParent): void; - - /** - * Updates the group profile with fields and meta data. - * - * 4.x: If the versionUpdateStruct is set in $userGroupUpdateStruct, this method internally creates a content draft, updates ts with the provided data - * and publishes the draft. If a draft is explicitly required, the user group can be updated via the content service methods. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * @param \eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct $userGroupUpdateStruct - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to move the user group - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userGroupUpdateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is set empty - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if a field value is not accepted by the field type - */ - public function updateUserGroup(UserGroup $userGroup, UserGroupUpdateStruct $userGroupUpdateStruct): UserGroup; - - /** - * Create a new user. The created user is published by this method. - * - * @param \eZ\Publish\API\Repository\Values\User\UserCreateStruct $userCreateStruct the data used for creating the user - * @param array $parentGroups the groups of type {@link \eZ\Publish\API\Repository\Values\User\UserGroup} which are assigned to the user after creation - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to move the user group - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userCreateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is missing or set to an empty value - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if a field value is not accepted by the field type - * if a user with provided login already exists - */ - public function createUser(UserCreateStruct $userCreateStruct, array $parentGroups): User; - - /** - * Loads a user. - * - * @param mixed $userId - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given id was not found - */ - public function loadUser(int $userId, array $prioritizedLanguages = []): User; - - /** - * Loads a user for the given login. - * - * Since 6.1 login is case-insensitive across all storage engines and database backends, like was the case - * with mysql before in eZ Publish 3.x/4.x/5.x. - * - * @param string $login - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given credentials was not found - */ - public function loadUserByLogin(string $login, array $prioritizedLanguages = []): User; - - /** - * Checks if credentials are valid for provided User. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param string $credentials - * - * @return bool - */ - public function checkUserCredentials(User $user, string $credentials): bool; - - /** - * Loads a user for the given email. - * - * @param string $email - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function loadUserByEmail(string $email, array $prioritizedLanguages = []): User; - - /** - * Loads a users for the given email. - * - * Note: This method loads user by $email where $email might be case-insensitive on certain storage engines! - * - * Returns an array of Users since eZ Publish has under certain circumstances allowed - * several users having same email in the past (by means of a configuration option). - * - * @param string $email - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\User[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function loadUsersByEmail(string $email, array $prioritizedLanguages = []): iterable; - - /** - * Loads a user with user hash key. - * - * @param string $hash - * @param string[] $prioritizedLanguages - * - * @return \eZ\Publish\API\Repository\Values\User\User - */ - public function loadUserByToken(string $hash, array $prioritizedLanguages = []): User; - - /** - * This method deletes a user. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete the user - * - * @return int[] Affected Location Id's (List of Locations of the Content that was deleted) - */ - public function deleteUser(User $user): iterable; - - /** - * Updates a user. - * - * 4.x: If the versionUpdateStruct is set in the user update structure, this method internally creates a content draft, updates ts with the provided data - * and publishes the draft. If a draft is explicitly required, the user group can be updated via the content service methods. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param \eZ\Publish\API\Repository\Values\User\UserUpdateStruct $userUpdateStruct - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - *@throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userUpdateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is set empty - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if a field value is not accepted by the field type - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update the user - */ - public function updateUser(User $user, UserUpdateStruct $userUpdateStruct): User; - - /** - * Validates and updates just the user's password. - * - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if new password does not pass validation - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update the user - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \Exception - */ - public function updateUserPassword(User $user, string $newPassword): User; - - /** - * Update the user token information specified by the user token struct. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param \eZ\Publish\API\Repository\Values\User\UserTokenUpdateStruct $userTokenUpdateStruct - * - * @return \eZ\Publish\API\Repository\Values\User\User - */ - public function updateUserToken(User $user, UserTokenUpdateStruct $userTokenUpdateStruct): User; - - /** - * Expires user token with user hash. - * - * @param string $hash - */ - public function expireUserToken(string $hash): void; - - /** - * Assigns a new user group to the user. - * - * If the user is already in the given user group this method does nothing. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign the user group to the user - */ - public function assignUserToUserGroup(User $user, UserGroup $userGroup): void; - - /** - * Removes a user group from the user. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove the user group from the user - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the user is not in the given user group - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If $userGroup is the last assigned user group - */ - public function unAssignUserFromUserGroup(User $user, UserGroup $userGroup): void; - - /** - * Loads the user groups the user belongs to. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed read the user or user group - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param int $offset the start offset for paging - * @param int $limit the number of user groups returned - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup[] - */ - public function loadUserGroupsOfUser(User $user, int $offset = 0, int $limit = 25, array $prioritizedLanguages = []): iterable; - - /** - * Loads the users of a user group. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read the users or user group - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * @param int $offset the start offset for paging - * @param int $limit the number of users returned - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\User[] - */ - public function loadUsersOfUserGroup(UserGroup $userGroup, int $offset = 0, int $limit = 25, array $prioritizedLanguages = []): iterable; - - /** - * Checks if Content is a user. - * - * @since 7.4 - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @return bool - */ - public function isUser(Content $content): bool; - - /** - * Checks if Content is a user group. - * - * @since 7.4 - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @return bool - */ - public function isUserGroup(Content $content): bool; - - /** - * Instantiate a user create class. - * - * @param string $login the login of the new user - * @param string $email the email of the new user - * @param string $password the plain password of the new user - * @param string $mainLanguageCode the main language for the underlying content object - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType 5.x the content type for the underlying content object. In 4.x it is ignored and taken from the configuration - * - * @return \eZ\Publish\API\Repository\Values\User\UserCreateStruct - */ - public function newUserCreateStruct(string $login, string $email, string $password, string $mainLanguageCode, ?ContentType $contentType = null): \eZ\Publish\API\Repository\Values\User\UserCreateStruct; - - /** - * Instantiate a user group create class. - * - * @param string $mainLanguageCode The main language for the underlying content object - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType|null $contentType 5.x the content type for the underlying content object. In 4.x it is ignored and taken from the configuration - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct - */ - public function newUserGroupCreateStruct(string $mainLanguageCode, ?ContentType $contentType = null): UserGroupCreateStruct; - - /** - * Instantiate a new user update struct. - * - * @return \eZ\Publish\API\Repository\Values\User\UserUpdateStruct - */ - public function newUserUpdateStruct(): UserUpdateStruct; - - /** - * Instantiate a new user group update struct. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct - */ - public function newUserGroupUpdateStruct(): UserGroupUpdateStruct; - - /** - * Validates given password. - * - * @param string $password - * @param \eZ\Publish\API\Repository\Values\User\PasswordValidationContext|null $context - * - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validatePassword(string $password, PasswordValidationContext $context = null): array; - - /** - * Returns information about password for a given user. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * - * @return \eZ\Publish\API\Repository\Values\User\PasswordInfo - */ - public function getPasswordInfo(User $user): PasswordInfo; -} diff --git a/eZ/Publish/API/Repository/Values/Bookmark/BookmarkList.php b/eZ/Publish/API/Repository/Values/Bookmark/BookmarkList.php deleted file mode 100644 index 990a6d200e..0000000000 --- a/eZ/Publish/API/Repository/Values/Bookmark/BookmarkList.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Bookmark; - -use ArrayIterator; -use eZ\Publish\API\Repository\Values\ValueObject; -use IteratorAggregate; -use Traversable; - -/** - * List of bookmarked locations. - */ -class BookmarkList extends ValueObject implements IteratorAggregate -{ - /** - * The total number of bookmarks. - * - * @var int - */ - public $totalCount = 0; - - /** - * List of bookmarked locations. - * - * @var \eZ\Publish\API\Repository\Values\Content\Location[] - */ - public $items = []; - - /** - * {@inheritdoc} - */ - public function getIterator(): Traversable - { - return new ArrayIterator($this->items); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Content.php b/eZ/Publish/API/Repository/Values/Content/Content.php deleted file mode 100644 index 407bce09c1..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Content.php +++ /dev/null @@ -1,113 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * this class represents a content object in a specific version. - * - * @property-read \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo convenience getter for getVersionInfo()->getContentInfo() - * @property-read int $id convenience getter for retrieving the contentId: $versionInfo->contentInfo->id - * @property-read \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo calls getVersionInfo() - * @property-read \eZ\Publish\API\Repository\Values\Content\Field[] $fields access fields, calls getFields() - * @property-read \eZ\Publish\API\Repository\Values\Content\Thumbnail|null $thumbnail calls getThumbnail() - */ -abstract class Content extends ValueObject -{ - /** - * Returns the VersionInfo for this version. - * - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo - */ - abstract public function getVersionInfo(): VersionInfo; - - /** - * Shorthand method for getVersionInfo()->getName(). - * - * @see \eZ\Publish\API\Repository\Values\Content\VersionInfo::getName() - * - * @param string|null $languageCode - * - * @return string|null The name for a given language, or null if $languageCode is not set - * or does not exist. - */ - public function getName(?string $languageCode = null): ?string - { - return $this->getVersionInfo()->getName($languageCode); - } - - /** - * Returns a field value for the given value. - * - * - If $languageCode is defined, - * return if available, otherwise null - * - If not pick using the following languages codes when applicable: - * 1. Prioritized languages (if provided to api on object retrieval) - * 2. Main language - * - * On non translatable fields this method ignores the languageCode parameter, and return main language field value. - * - * @param string $fieldDefIdentifier - * @param string|null $languageCode - * - * @return \eZ\Publish\SPI\FieldType\Value|null a primitive type or a field type Value object depending on the field type. - */ - abstract public function getFieldValue(string $fieldDefIdentifier, ?string $languageCode = null): ?\eZ\Publish\SPI\FieldType\Value; - - /** - * This method returns the complete fields collection. - * - * @return \eZ\Publish\API\Repository\Values\Content\Field[] An array of {@link Field} - */ - abstract public function getFields(): iterable; - - /** - * This method returns the fields for a given language and non translatable fields. - * - * - If $languageCode is defined, return if available - * - If not pick using prioritized languages (if provided to api on object retrieval) - * - Otherwise return in main language - * - * @param string|null $languageCode - * - * @return \eZ\Publish\API\Repository\Values\Content\Field[] An array of {@link Field} with field identifier as keys - */ - abstract public function getFieldsByLanguage(?string $languageCode = null): iterable; - - /** - * This method returns the field for a given field definition identifier and language. - * - * - If $languageCode is defined, - * return if available, otherwise null - * - If not pick using the following languages codes when applicable: - * 1. Prioritized languages (if provided to api on object retrieval) - * 2. Main language - * - * On non translatable fields this method ignores the languageCode parameter, and return main language field. - * - * @param string $fieldDefIdentifier - * @param string|null $languageCode - * - * @return \eZ\Publish\API\Repository\Values\Content\Field|null A {@link Field} or null if nothing is found - */ - abstract public function getField(string $fieldDefIdentifier, ?string $languageCode = null): ?Field; - - /** - * Returns the ContentType for this content. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType - */ - abstract public function getContentType(): ContentType; - - abstract public function getThumbnail(): ?Thumbnail; - - abstract public function getDefaultLanguageCode(): string; -} diff --git a/eZ/Publish/API/Repository/Values/Content/ContentCreateStruct.php b/eZ/Publish/API/Repository/Values/Content/ContentCreateStruct.php deleted file mode 100644 index a31d478a73..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/ContentCreateStruct.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -/** - * This class is used for creating a new content object. - * - * @property \eZ\Publish\API\Repository\Values\Content\Field[] $fields - */ -abstract class ContentCreateStruct extends ContentStruct -{ - /** - * The content type for which the new content is created. - * - * Required. - * - * @var \eZ\Publish\API\Repository\Values\ContentType\ContentType - */ - public $contentType; - - /** - * The section the content is assigned to. - * If not set the section of the parent is used or a default section. - * - * @var mixed - */ - public $sectionId; - - /** - * The owner of the content. If not given the current authenticated user is set as owner. - * - * @var mixed - */ - public $ownerId; - - /** - * Indicates if the content object is shown in the mainlanguage if its not present in an other requested language. - * - * @var bool - */ - public $alwaysAvailable; - - /** - * Remote identifier used as a custom identifier for the object. - * - * Needs to be a unique Content->remoteId string value. - * - * @var string - */ - public $remoteId; - - /** - * the main language code for the content. This language will also - * be used for as initial language for the first created version. - * It is also used as default language for added fields. - * - * Required. - * - * @var string - */ - public $mainLanguageCode; - - /** - * Modification date. If not given the current timestamp is used. - * - * @var \DateTime - */ - public $modificationDate; -} diff --git a/eZ/Publish/API/Repository/Values/Content/ContentDraftList.php b/eZ/Publish/API/Repository/Values/Content/ContentDraftList.php deleted file mode 100644 index 09c73b380b..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/ContentDraftList.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use ArrayIterator; -use eZ\Publish\API\Repository\Values\ValueObject; -use IteratorAggregate; -use Traversable; - -/** - * List of content drafts. - */ -class ContentDraftList extends ValueObject implements IteratorAggregate -{ - /** - * @var int - */ - public $totalCount = 0; - - /** - * @var \eZ\Publish\API\Repository\Values\Content\DraftList\ContentDraftListItemInterface[] - */ - public $items = []; - - /** - * {@inheritdoc} - */ - public function getIterator(): Traversable - { - return new ArrayIterator($this->items); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/ContentInfo.php b/eZ/Publish/API/Repository/Values/Content/ContentInfo.php deleted file mode 100644 index d9d67ae5be..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/ContentInfo.php +++ /dev/null @@ -1,227 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class provides all version independent information of the Content object. - * - * @property-read int $id @deprecated Use {@see ContentInfo::getId} instead. The unique id of the Content object - * @property-read int $contentTypeId The unique id of the Content Type object the Content object is an instance of - * @property-read string $name the computed name (via name schema) in the main language of the Content object - * @property-read int $sectionId the section to which the Content object is assigned - * @property-read int $currentVersionNo Current Version number is the version number of the published version or the version number of a newly created draft (which is 1). - * @property-read bool $published true if there exists a published version false otherwise - * @property-read int $ownerId the user id of the owner of the Content object - * @property-read \DateTime $modificationDate Content object modification date - * @property-read \DateTime $publishedDate date of the first publish - * @property-read bool $alwaysAvailable Indicates if the Content object is shown in the mainlanguage if its not present in an other requested language - * @property-read string $remoteId a global unique id of the Content object - * @property-read string $mainLanguageCode The main language code of the Content object. If the available flag is set to true the Content is shown in this language if the requested language does not exist. - * @property-read int|null $mainLocationId @deprecated Use {@see ContentInfo::getMainLocationId} instead - * @property-read int $status status of the Content object - * @property-read bool $isHidden status of the Content object - */ -class ContentInfo extends ValueObject -{ - public const STATUS_DRAFT = 0; - public const STATUS_PUBLISHED = 1; - public const STATUS_TRASHED = 2; - - /** - * The unique id of the Content object. - * - * @var int - */ - protected $id; - - /** - * The Content Type id of the Content object. - * - * @var int - */ - protected $contentTypeId; - - /** - * The computed name (via name schema) in the main language of the Content object. - * - * For names in other languages then main see {@see \eZ\Publish\API\Repository\Values\Content\VersionInfo} - * - * @var string - */ - protected $name; - - /** - * The section to which the Content object is assigned. - * - * @var int - */ - protected $sectionId; - - /** - * Current Version number is the version number of the published version or the version number of - * a newly created draft (which is 1). - * - * @var int - */ - protected $currentVersionNo; - - /** - * True if there exists a published version, false otherwise. - * - * @var bool Constant. - */ - protected $published; - - /** - * The owner of the Content object. - * - * @var int - */ - protected $ownerId; - - /** - * Content modification date. - * - * @var \DateTime - */ - protected $modificationDate; - - /** - * Content publication date. - * - * @var \DateTime - */ - protected $publishedDate; - - /** - * Indicates if the Content object is shown in the mainlanguage if its not present in an other requested language. - * - * @var bool - */ - protected $alwaysAvailable; - - /** - * Remote identifier used as a custom identifier for the object. - * - * @var string - */ - protected $remoteId; - - /** - * The main language code of the Content object. - * - * @var string - */ - protected $mainLanguageCode; - - /** - * Identifier of the main location. - * - * If the Content object has multiple locations, - * $mainLocationId will point to the main one. - * - * @var int|null - */ - protected $mainLocationId; - - /** - * Status of the content. - * - * Replaces deprecated API\ContentInfo::$published. - * - * @var int - */ - protected $status; - - /** @var bool */ - protected $isHidden; - - /** @var \eZ\Publish\API\Repository\Values\ContentType\ContentType */ - protected $contentType; - - /** @var \eZ\Publish\API\Repository\Values\Content\Section */ - protected $section; - - /** @var \eZ\Publish\API\Repository\Values\Content\Language */ - protected $mainLanguage; - - /** @var \eZ\Publish\API\Repository\Values\Content\Location|null */ - protected $mainLocation; - - /** @var \eZ\Publish\API\Repository\Values\User\User */ - protected $owner; - - /** - * @return bool - */ - public function isDraft(): bool - { - return $this->status === self::STATUS_DRAFT; - } - - /** - * @return bool - */ - public function isPublished(): bool - { - return $this->status === self::STATUS_PUBLISHED; - } - - /** - * @return bool - */ - public function isTrashed(): bool - { - return $this->status === self::STATUS_TRASHED; - } - - public function getContentType(): ContentType - { - return $this->contentType; - } - - public function getSection(): Section - { - return $this->section; - } - - public function getMainLanguage(): Language - { - return $this->mainLanguage; - } - - public function getMainLanguageCode(): string - { - return $this->mainLanguageCode; - } - - public function getMainLocation(): ?Location - { - return $this->mainLocation; - } - - public function getOwner(): User - { - return $this->owner; - } - - public function getMainLocationId(): ?int - { - return $this->mainLocationId; - } - - public function getId(): int - { - return $this->id; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/ContentList.php b/eZ/Publish/API/Repository/Values/Content/ContentList.php deleted file mode 100644 index 8ba8008485..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/ContentList.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use ArrayIterator; -use Ibexa\Contracts\Core\Repository\Collections\TotalCountAwareInterface; -use IteratorAggregate; -use Traversable; - -/** - * A filtered Content items list iterator. - */ -final class ContentList implements IteratorAggregate, TotalCountAwareInterface -{ - /** @var int */ - private $totalCount; - - /** @var \eZ\Publish\API\Repository\Values\Content\Content[] */ - private $contentItems; - - /** - * @internal for internal use by Repository - */ - public function __construct(int $totalCount, array $contentItems) - { - $this->totalCount = $totalCount; - $this->contentItems = $contentItems; - } - - public function getTotalCount(): int - { - return $this->totalCount; - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Content[]|\Traversable - */ - public function getIterator(): Traversable - { - return new ArrayIterator($this->contentItems); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/ContentUpdateStruct.php b/eZ/Publish/API/Repository/Values/Content/ContentUpdateStruct.php deleted file mode 100644 index 46f2b27779..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/ContentUpdateStruct.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -/** - * This class is used for updating the fields of a content object draft. - * - * @property \eZ\Publish\API\Repository\Values\Content\Field[] $fields - */ -abstract class ContentUpdateStruct extends ContentStruct -{ - /** - * The language code of the version. In 4.x this code will be used as the language code of the translation - * (which is shown in the admin interface). - * It is also used as default language for added fields. - * - * @var string - */ - public $initialLanguageCode; - - /** - * Creator user ID. - * - * Creator of the version, in the search API this is referred to as the modifier of the published content. - * - * WARNING: Standard permission rules applies, only the user set here will be able to change the draft after being - * set as modifier, and only if (s)he has access to edit the draft in the first place. - * - * @since 5.4 - * - * @var mixed Optional creator of version, current user will be used if null - */ - public $creatorId; -} diff --git a/eZ/Publish/API/Repository/Values/Content/DraftList/ContentDraftListItemInterface.php b/eZ/Publish/API/Repository/Values/Content/DraftList/ContentDraftListItemInterface.php deleted file mode 100644 index fc9022fa38..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/DraftList/ContentDraftListItemInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\DraftList; - -use eZ\Publish\API\Repository\Values\Content\VersionInfo; - -interface ContentDraftListItemInterface -{ - /** - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo|null - */ - public function getVersionInfo(): ?VersionInfo; - - /** - * @return bool - */ - public function hasVersionInfo(): bool; -} diff --git a/eZ/Publish/API/Repository/Values/Content/DraftList/Item/ContentDraftListItem.php b/eZ/Publish/API/Repository/Values/Content/DraftList/Item/ContentDraftListItem.php deleted file mode 100644 index 787d13ac1d..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/DraftList/Item/ContentDraftListItem.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\DraftList\Item; - -use eZ\Publish\API\Repository\Values\Content\DraftList\ContentDraftListItemInterface; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; - -/** - * Item of content drafts list. - */ -class ContentDraftListItem implements ContentDraftListItemInterface -{ - /** - * @var \eZ\Publish\API\Repository\Values\Content\VersionInfo - */ - private $versionInfo; - - /** - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - */ - public function __construct(VersionInfo $versionInfo) - { - $this->versionInfo = $versionInfo; - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo|null - */ - public function getVersionInfo(): ?VersionInfo - { - return $this->versionInfo; - } - - /** - * @return bool - */ - public function hasVersionInfo(): bool - { - return $this->versionInfo instanceof VersionInfo; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Field.php b/eZ/Publish/API/Repository/Values/Content/Field.php deleted file mode 100644 index a4af0a3215..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Field.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class represents a field of a content object. - * - * @property-read mixed $id an internal id of the field - * @property-read string $fieldDefIdentifier the field definition identifier - * @property-read mixed $value the value of the field - * @property-read string $languageCode the language code of the field - * @property-read string $fieldTypeIdentifier field type identifier - */ -class Field extends ValueObject -{ - /** - * The field id. - * - * Value of `null` indicates the field is virtual - * and is not persisted (yet). - * - * @var int|null - */ - protected $id; - - /** - * The field definition identifier. - * - * @var string - */ - protected $fieldDefIdentifier; - - /** - * A field type value or a value type which can be converted by the corresponding field type. - * - * @var mixed - */ - protected $value; - - /** - * the language code. - * - * @var string|null - */ - protected $languageCode; - - /** - * Field type identifier. - * - * @var string - */ - protected $fieldTypeIdentifier; - - public function getId(): ?int - { - return $this->id; - } - - public function getFieldDefinitionIdentifier(): string - { - return $this->fieldDefIdentifier; - } - - /** - * @return mixed - */ - public function getValue() - { - return $this->value; - } - - public function getLanguageCode(): ?string - { - return $this->languageCode; - } - - public function getFieldTypeIdentifier(): string - { - return $this->fieldTypeIdentifier; - } - - /** - * @phpstan-assert-if-true !null $this->getId() - */ - public function isVirtual(): bool - { - return null === $this->id; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Language.php b/eZ/Publish/API/Repository/Values/Content/Language.php deleted file mode 100644 index dbf148246d..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Language.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class represents a language in the repository. - * - * @property-read mixed $id the language id - * @property-read string $languageCode the language code in - * @property-read string $name human readable name of the language - * @property-read bool $enabled indicates if the language is enabled or not. - */ -class Language extends ValueObject -{ - /** - * Constant for use in API's to specify that you want to load all languages. - */ - public const ALL = []; - - /** - * The language id (auto generated). - * - * @var int - */ - protected $id; - - /** @var string */ - protected $languageCode; - - /** - * Human-readable name of the language. - * - * @var string - */ - protected $name; - - /** @var bool */ - protected $enabled; - - public function getId(): int - { - return $this->id; - } - - public function getLanguageCode(): string - { - return $this->languageCode; - } - - public function getName(): string - { - return $this->name; - } - - public function isEnabled(): bool - { - return $this->enabled; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Location.php b/eZ/Publish/API/Repository/Values/Content/Location.php deleted file mode 100644 index b0510d15e6..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Location.php +++ /dev/null @@ -1,251 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class represents a location in the repository. - * - * @property-read \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo calls getContentInfo() - * @property-read int $contentId calls getContentInfo()->id - * @property-read int $id the id of the location - * @property-read int $priority Position of the Location among its siblings when sorted using priority - * @property-read bool $hidden Indicates that the Location entity is hidden (explicitly or hidden by content). - * @property-read bool $invisible Indicates that the Location is not visible, being either marked as hidden itself, or implicitly hidden by its Content or an ancestor Location - * @property-read bool $explicitlyHidden Indicates that the Location entity has been explicitly marked as hidden. - * @property-read string $remoteId a global unique id of the content object - * @property-read int $parentLocationId the id of the parent location - * @property-read string $pathString @deprecated use {@see Location::getPathString()} instead. - * @property-read array $path Same as $pathString but as array, e.g. [ 1, 2, 4, 23 ] - * @property-read int $depth Depth location has in the location tree - * @property-read int $sortField Specifies which property the child locations should be sorted on. Valid values are found at {@link Location::SORT_FIELD_*} - * @property-read int $sortOrder Specifies whether the sort order should be ascending or descending. Valid values are {@link Location::SORT_ORDER_*} - */ -abstract class Location extends ValueObject -{ - // @todo Rename these to better fit current naming, also reuse these in Persistence or copy the change over. - public const SORT_FIELD_PATH = 1; - public const SORT_FIELD_PUBLISHED = 2; - public const SORT_FIELD_MODIFIED = 3; - public const SORT_FIELD_SECTION = 4; - public const SORT_FIELD_DEPTH = 5; - public const SORT_FIELD_CLASS_IDENTIFIER = 6; - public const SORT_FIELD_CLASS_NAME = 7; - public const SORT_FIELD_PRIORITY = 8; - public const SORT_FIELD_NAME = 9; - - /** - * @deprecated - */ - public const SORT_FIELD_MODIFIED_SUBNODE = 10; - - public const SORT_FIELD_NODE_ID = 11; - public const SORT_FIELD_CONTENTOBJECT_ID = 12; - - public const SORT_ORDER_DESC = 0; - public const SORT_ORDER_ASC = 1; - - public const STATUS_DRAFT = 0; - public const STATUS_PUBLISHED = 1; - - /** - * Map for Location sort fields to their respective SortClauses. - * - * Those not here (class name/identifier and modified subnode) are - * missing/deprecated and will most likely be removed in the future. - */ - public const SORT_FIELD_MAP = [ - self::SORT_FIELD_PATH => SortClause\Location\Path::class, - self::SORT_FIELD_PUBLISHED => SortClause\DatePublished::class, - self::SORT_FIELD_MODIFIED => SortClause\DateModified::class, - self::SORT_FIELD_SECTION => SortClause\SectionIdentifier::class, - self::SORT_FIELD_DEPTH => SortClause\Location\Depth::class, - //self::SORT_FIELD_CLASS_IDENTIFIER => false, - //self::SORT_FIELD_CLASS_NAME => false, - self::SORT_FIELD_PRIORITY => SortClause\Location\Priority::class, - self::SORT_FIELD_NAME => SortClause\ContentName::class, - //self::SORT_FIELD_MODIFIED_SUBNODE => false, - self::SORT_FIELD_NODE_ID => SortClause\Location\Id::class, - self::SORT_FIELD_CONTENTOBJECT_ID => SortClause\ContentId::class, - ]; - - /** - * Map for Location sort order to their respective Query SORT constants. - */ - public const SORT_ORDER_MAP = [ - self::SORT_ORDER_DESC => Query::SORT_DESC, - self::SORT_ORDER_ASC => Query::SORT_ASC, - ]; - - /** - * Location ID. - * - * @var int Location ID. - */ - protected $id; - - /** - * the status of the location. - * - * a location gets the status DRAFT on newly created content which is not published. When content is published the - * location gets the status STATUS_PUBLISHED - * - * @var int - */ - public $status = self::STATUS_PUBLISHED; - - /** - * Location priority. - * - * Position of the Location among its siblings when sorted using priority - * sort order. - * - * @var int - */ - protected $priority; - - /** - * Indicates that the Location entity is hidden (explicitly or hidden by content). - * - * @var bool - */ - protected $hidden; - - /** - * Indicates that the Location is not visible, being either marked as hidden itself, - * or implicitly hidden by its Content or an ancestor Location. - * - * @var bool - */ - protected $invisible; - - /** - * Indicates that the Location entity has been explicitly marked as hidden. - * - * @var bool - */ - protected $explicitlyHidden; - - /** - * Remote ID. - * - * A universally unique identifier. - * - * @var string - */ - protected $remoteId; - - /** - * Parent ID. - * - * @var int Location ID. - */ - protected $parentLocationId; - - /** - * The materialized path of the location entry, eg: /1/2/. - * - * @var string - */ - protected $pathString; - - /** - * Depth location has in the location tree. - * - * @var int - */ - protected $depth; - - /** - * Specifies which property the child locations should be sorted on. - * - * Valid values are found at {@link Location::SORT_FIELD_*} - * - * @var int - */ - protected $sortField; - - /** - * Specifies whether the sort order should be ascending or descending. - * - * Valid values are {@link Location::SORT_ORDER_*} - * - * @var int - */ - protected $sortOrder; - - /** @var \eZ\Publish\API\Repository\Values\Content\Content */ - protected $content; - - /** - * Returns the content info of the content object of this location. - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - abstract public function getContentInfo(): ContentInfo; - - /** - * Return the parent location of of this location. - * - * @return \eZ\Publish\API\Repository\Values\Content\Location|null - */ - abstract public function getParentLocation(): ?Location; - - /** - * Returns true if current location is a draft. - * - * @return bool - */ - public function isDraft(): bool - { - return $this->status === self::STATUS_DRAFT; - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function getContent(): Content - { - return $this->content; - } - - /** - * Get SortClause objects built from Locations' sort options. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException If sort field has a deprecated/unsupported value which does not have a Sort Clause. - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] - */ - public function getSortClauses(): array - { - $map = self::SORT_FIELD_MAP; - if (!isset($map[$this->sortField])) { - throw new NotImplementedException( - "Sort Clause not implemented for Location sort Field {$this->sortField}" - ); - } - - $sortClause = new $map[$this->sortField](); - $sortClause->direction = self::SORT_ORDER_MAP[$this->sortOrder]; - - return [$sortClause]; - } - - /** - * The path to the Location represented by the current instance, - * e.g. /1/2/4/23 where 23 is current id. - */ - public function getPathString(): string - { - return $this->pathString; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/LocationList.php b/eZ/Publish/API/Repository/Values/Content/LocationList.php deleted file mode 100644 index b4b7843bff..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/LocationList.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use ArrayIterator; -use eZ\Publish\API\Repository\Values\ValueObject; -use Ibexa\Contracts\Core\Repository\Collections\TotalCountAwareInterface; -use IteratorAggregate; -use Traversable; - -/** - * This class represents a queried location list holding a totalCount and a partial list of locations - * (by offset/limit parameters and permission filters). - * - * @property-read int $totalCount - the total count of found locations (filtered by permissions) - * @property-read \eZ\Publish\API\Repository\Values\Content\Location[] $locations - the partial list of - * Locations controlled by offset/limit. - **/ -class LocationList extends ValueObject implements IteratorAggregate, TotalCountAwareInterface -{ - /** - * The total count of non-paginated Locations (filtered by permissions). - * - * Use {@see getTotalCount} to fetch it. - * - * @var int - */ - protected $totalCount = 0; - - /** - * the partial list of locations controlled by offset/limit. - * - * @var \eZ\Publish\API\Repository\Values\Content\Location[] - */ - protected $locations = []; - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Location[]|\Traversable - */ - public function getIterator(): Traversable - { - return new ArrayIterator($this->locations); - } - - public function getTotalCount(): int - { - return $this->totalCount; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/LocationQuery.php b/eZ/Publish/API/Repository/Values/Content/LocationQuery.php deleted file mode 100644 index 611a6431eb..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/LocationQuery.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -/** - * This class is used to perform a Location query. - */ -class LocationQuery extends Query -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation.php deleted file mode 100644 index ef3f00fc9e..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query; - -interface Aggregation -{ - public function getName(): string; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/AbstractRangeAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/AbstractRangeAggregation.php deleted file mode 100644 index 2eaf52e5d7..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/AbstractRangeAggregation.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -abstract class AbstractRangeAggregation implements Aggregation -{ - /** - * The name of the aggregation. - * - * @var string - */ - protected $name; - - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Range[] */ - protected $ranges; - - public function __construct(string $name, array $ranges = []) - { - $this->name = $name; - $this->ranges = $ranges; - } - - public function getRanges(): array - { - return $this->ranges; - } - - public function getName(): string - { - return $this->name; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/AbstractStatsAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/AbstractStatsAggregation.php deleted file mode 100644 index f953430f05..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/AbstractStatsAggregation.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -abstract class AbstractStatsAggregation implements Aggregation -{ - /** - * The name of the aggregation. - * - * @var string - */ - protected $name; - - public function __construct(string $name) - { - $this->name = $name; - } - - public function getName(): string - { - return $this->name; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/ContentTypeGroupTermAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/ContentTypeGroupTermAggregation.php deleted file mode 100644 index f6ca21b471..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/ContentTypeGroupTermAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -final class ContentTypeGroupTermAggregation extends AbstractTermAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/ContentTypeTermAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/ContentTypeTermAggregation.php deleted file mode 100644 index 24b3464c2b..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/ContentTypeTermAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -final class ContentTypeTermAggregation extends AbstractTermAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/DateMetadataRangeAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/DateMetadataRangeAggregation.php deleted file mode 100644 index 6ebd4ca948..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/DateMetadataRangeAggregation.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -final class DateMetadataRangeAggregation extends AbstractRangeAggregation -{ - public const MODIFIED = 'modified'; - public const CREATED = 'created'; - public const PUBLISHED = 'published'; - - /** @var string */ - private $type; - - public function __construct(string $name, string $type, array $ranges = []) - { - parent::__construct($name, $ranges); - $this->type = $type; - } - - public function getType(): string - { - return $this->type; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldRangeAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldRangeAggregation.php deleted file mode 100644 index 0dca08fb77..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldRangeAggregation.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractRangeAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\FieldAggregation; - -abstract class AbstractFieldRangeAggregation extends AbstractRangeAggregation implements FieldAggregation -{ - use FieldAggregationTrait; - - public function __construct( - string $name, - string $contentTypeIdentifier, - string $fieldDefinitionIdentifier, - array $ranges = [] - ) { - parent::__construct($name, $ranges); - - $this->contentTypeIdentifier = $contentTypeIdentifier; - $this->fieldDefinitionIdentifier = $fieldDefinitionIdentifier; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldStatsAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldStatsAggregation.php deleted file mode 100644 index 6fa869bf72..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldStatsAggregation.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractStatsAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\FieldAggregation; - -abstract class AbstractFieldStatsAggregation extends AbstractStatsAggregation implements FieldAggregation -{ - use FieldAggregationTrait; - - public function __construct( - string $name, - string $contentTypeIdentifier, - string $fieldDefinitionIdentifier - ) { - parent::__construct($name); - - $this->contentTypeIdentifier = $contentTypeIdentifier; - $this->fieldDefinitionIdentifier = $fieldDefinitionIdentifier; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldTermAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldTermAggregation.php deleted file mode 100644 index ba3cc2c916..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldTermAggregation.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractTermAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\FieldAggregation; - -abstract class AbstractFieldTermAggregation extends AbstractTermAggregation implements FieldAggregation -{ - use FieldAggregationTrait; - - public function __construct( - string $name, - string $contentTypeIdentifier, - string $fieldDefinitionIdentifier - ) { - parent::__construct($name); - - $this->contentTypeIdentifier = $contentTypeIdentifier; - $this->fieldDefinitionIdentifier = $fieldDefinitionIdentifier; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/AuthorTermAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/AuthorTermAggregation.php deleted file mode 100644 index 2033939c10..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/AuthorTermAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -final class AuthorTermAggregation extends AbstractFieldTermAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/CheckboxTermAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/CheckboxTermAggregation.php deleted file mode 100644 index a917ade836..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/CheckboxTermAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -final class CheckboxTermAggregation extends AbstractFieldTermAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/DateRangeAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/DateRangeAggregation.php deleted file mode 100644 index 3727b18aeb..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/DateRangeAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -final class DateRangeAggregation extends AbstractFieldRangeAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/DateTimeRangeAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/DateTimeRangeAggregation.php deleted file mode 100644 index 1a20f4ad77..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/DateTimeRangeAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -final class DateTimeRangeAggregation extends AbstractFieldRangeAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/FieldAggregationTrait.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/FieldAggregationTrait.php deleted file mode 100644 index 406557e1ca..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/FieldAggregationTrait.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -trait FieldAggregationTrait -{ - /** @var string */ - public $contentTypeIdentifier; - - /** @var string */ - public $fieldDefinitionIdentifier; - - public function getContentTypeIdentifier(): string - { - return $this->contentTypeIdentifier; - } - - public function getFieldDefinitionIdentifier(): string - { - return $this->fieldDefinitionIdentifier; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/FloatRangeAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/FloatRangeAggregation.php deleted file mode 100644 index cb438322f5..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/FloatRangeAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -final class FloatRangeAggregation extends AbstractFieldRangeAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/FloatStatsAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/FloatStatsAggregation.php deleted file mode 100644 index 2b8f47be23..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/FloatStatsAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -final class FloatStatsAggregation extends AbstractFieldStatsAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/IntegerRangeAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/IntegerRangeAggregation.php deleted file mode 100644 index e8abd8b805..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/IntegerRangeAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -final class IntegerRangeAggregation extends AbstractFieldRangeAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/IntegerStatsAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/IntegerStatsAggregation.php deleted file mode 100644 index e96a345195..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/IntegerStatsAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -final class IntegerStatsAggregation extends AbstractFieldStatsAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/KeywordTermAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/KeywordTermAggregation.php deleted file mode 100644 index 9544966627..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/KeywordTermAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -final class KeywordTermAggregation extends AbstractFieldTermAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/SelectionTermAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/SelectionTermAggregation.php deleted file mode 100644 index 14753ff886..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/SelectionTermAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -final class SelectionTermAggregation extends AbstractFieldTermAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/TimeRangeAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/TimeRangeAggregation.php deleted file mode 100644 index 6c2480651c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Field/TimeRangeAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field; - -final class TimeRangeAggregation extends AbstractFieldRangeAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/FieldAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/FieldAggregation.php deleted file mode 100644 index 07d254238f..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/FieldAggregation.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -interface FieldAggregation -{ - public function getContentTypeIdentifier(): string; - - public function getFieldDefinitionIdentifier(): string; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/LanguageTermAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/LanguageTermAggregation.php deleted file mode 100644 index 03e2e5f3fb..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/LanguageTermAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -final class LanguageTermAggregation extends AbstractTermAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Location/LocationChildrenTermAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Location/LocationChildrenTermAggregation.php deleted file mode 100644 index 1cd80c7e62..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Location/LocationChildrenTermAggregation.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractTermAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\LocationAggregation; - -final class LocationChildrenTermAggregation extends AbstractTermAggregation implements LocationAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Location/SubtreeTermAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Location/SubtreeTermAggregation.php deleted file mode 100644 index e8dd9cf414..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Location/SubtreeTermAggregation.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Location; - -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractTermAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\LocationAggregation; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; - -final class SubtreeTermAggregation extends AbstractTermAggregation implements LocationAggregation -{ - /** @var string */ - private $pathString; - - public function __construct(string $name, string $pathString) - { - parent::__construct($name); - - if (!$this->isValidPathString($pathString)) { - throw new InvalidArgumentException( - '$pathString', - "'$pathString' value must follow the path string format, e.g. /1/2/" - ); - } - - $this->pathString = $pathString; - } - - public function getPathString(): string - { - return $this->pathString; - } - - private function isValidPathString(string $pathString): bool - { - return preg_match('/^(\/\w+)+\/$/', $pathString) === 1; - } - - public static function fromLocation(string $name, Location $location): self - { - return new self($name, $location->pathString); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/LocationAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/LocationAggregation.php deleted file mode 100644 index 3ff0c6e4ac..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/LocationAggregation.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -/** - * Marker interface for location based aggregation. - */ -interface LocationAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Range.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Range.php deleted file mode 100644 index f401e3bda5..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/Range.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -use DateTimeInterface; -use eZ\Publish\API\Repository\Values\ValueObject; - -final class Range extends ValueObject -{ - /** - * Beginning of the range (included). - * - * @var int|float|\DateTimeInterface|null - */ - private $from; - - /** - * End of the range (excluded). - * - * @var int|float|\DateTimeInterface|null - */ - private $to; - - public function __construct($from, $to) - { - parent::__construct(); - - $this->from = $from; - $this->to = $to; - } - - public function getFrom() - { - return $this->from; - } - - public function getTo() - { - return $this->to; - } - - public function __toString(): string - { - return sprintf( - '[%s;%s)', - $this->getRangeValueAsString($this->from), - $this->getRangeValueAsString($this->to) - ); - } - - private function getRangeValueAsString($value): string - { - if ($value === null) { - return '*'; - } - - if ($value instanceof DateTimeInterface) { - return $value->format(DateTimeInterface::ISO8601); - } - - return (string)$value; - } - - public static function ofInt(?int $from, ?int $to): self - { - return new self($from, $to); - } - - public static function ofFloat(?float $from, ?float $to): self - { - return new self($from, $to); - } - - public static function ofDateTime(?DateTimeInterface $from, ?DateTimeInterface $to): self - { - return new self($from, $to); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/RawAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/RawAggregation.php deleted file mode 100644 index fa5fdd020f..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/RawAggregation.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -interface RawAggregation -{ - public function getFieldName(): string; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/RawRangeAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/RawRangeAggregation.php deleted file mode 100644 index cc465ec74c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/RawRangeAggregation.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -final class RawRangeAggregation extends AbstractRangeAggregation implements RawAggregation -{ - /** @var string */ - private $fieldName; - - public function __construct(string $name, string $fieldName, array $ranges = []) - { - parent::__construct($name, $ranges); - - $this->fieldName = $fieldName; - } - - public function getFieldName(): string - { - return $this->fieldName; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/RawStatsAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/RawStatsAggregation.php deleted file mode 100644 index a8289631d1..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/RawStatsAggregation.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -final class RawStatsAggregation extends AbstractStatsAggregation implements RawAggregation -{ - /** @var string */ - private $fieldName; - - public function __construct(string $name, string $fieldName) - { - parent::__construct($name); - - $this->fieldName = $fieldName; - } - - public function getFieldName(): string - { - return $this->fieldName; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/SectionTermAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/SectionTermAggregation.php deleted file mode 100644 index 3b4b56a4dc..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/SectionTermAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -final class SectionTermAggregation extends AbstractTermAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/VisibilityTermAggregation.php b/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/VisibilityTermAggregation.php deleted file mode 100644 index 7844f9bd80..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/VisibilityTermAggregation.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; - -final class VisibilityTermAggregation extends AbstractTermAggregation -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion.php deleted file mode 100644 index 469c90bfa1..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion.php +++ /dev/null @@ -1,180 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Value; -use InvalidArgumentException; - -abstract class Criterion implements CriterionInterface -{ - /** - * The operator used by the Criterion. - * - * @var string - */ - public $operator; - - /** - * The value(s) matched by the criteria. - * - * @var string[]|int[]|int|string|bool - */ - public $value; - - /** - * The target used by the criteria (field, metadata...). - * - * @var string - */ - public $target; - - /** - * Additional value data, required by some criterions, MapLocationDistance for instance. - * - * @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Value - */ - public $valueData; - - /** - * Performs operator validation based on the Criterion specifications returned by {@see getSpecifications()}. - * - * @param string|null $target The target the Criterion applies to: metadata identifier, field identifier... - * @param string|null $operator - * The operator the Criterion uses. If null is given, will default to Operator::IN if $value is an array, - * Operator::EQ if it is not. - * @param string[]|int[]|int|string|bool $value - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Value|null $valueData - * - * @todo Add a dedicated exception - * - * @throws \InvalidArgumentException if the provided operator isn't supported - */ - public function __construct(?string $target, ?string $operator, $value, ?Value $valueData = null) - { - if ($operator === null) { - $operator = is_array($value) ? Operator::IN : Operator::EQ; - } - - $operatorFound = false; - - // we loop on each specified operator. - // If the provided operator ain't found, an exception will be thrown at the end - foreach ($this->getSpecifications() as $operatorSpecifications) { - if ($operatorSpecifications->operator != $operator) { - continue; - } - $operatorFound = true; - - // input format check (single/array) - switch ($operatorSpecifications->valueFormat) { - case Specifications::FORMAT_SINGLE: - if (is_array($value)) { - throw new InvalidArgumentException('The Criterion expects a single value'); - } - break; - - case Specifications::FORMAT_ARRAY: - if (!is_array($value)) { - throw new InvalidArgumentException('The Criterion expects an array of values'); - } - break; - } - - // input value check - if ($operatorSpecifications->valueTypes !== null) { - $callback = $this->getValueTypeCheckCallback($operatorSpecifications->valueTypes); - if (!is_array($value)) { - $value = [$value]; - } - foreach ($value as $item) { - if ($callback($item) === false) { - throw new InvalidArgumentException('Unsupported value (' . gettype($item) . ")$item"); - } - } - } - } - - // Operator wasn't found in the criterion specifications - if ($operatorFound === false) { - throw new InvalidArgumentException("Operator $operator isn't supported by Criterion " . static::class); - } - - $this->operator = $operator; - $this->value = $value; - $this->target = $target; - $this->valueData = $valueData; - } - - /** - * Criterion description function. - * - * Returns the combination of the Criterion's supported operator/value, - * as an array of eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications objects - * - Operator is one supported Operator, as an Operator::* constant - * - ValueType is the type of input value this operator requires, either array or single - * - SupportedTypes is an array of types the operator will accept - * - ValueCountLimitation is an integer saying how many values are expected. - * - * <code> - * // IN and EQ are supported - * return [ - * // The EQ operator expects a single value, either as an integer or a string - * new Specifications( - * Operator::EQ, - * Specifications::FORMAT_SINGLE, - * Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - * ), - * // The IN operator expects an array of values, of either integers or strings - * new Specifications( - * Operator::IN, - * Specifications::FORMAT_ARRAY, - * Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - * ) - * ] - * </code> - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications[] - */ - abstract public function getSpecifications(): array; - - /** - * Returns a callback that checks the values types depending on the operator specifications. - * - * @param int $valueTypes The accepted values, as a bit field of Specifications::TYPE_* constants - * - * @return callable - */ - private function getValueTypeCheckCallback(int $valueTypes): callable - { - $callback = static function ($value) { - return false; - }; - - // the callback code will return true as soon as an accepted value type is found - if ($valueTypes & Specifications::TYPE_INTEGER) { - $callback = static function ($value) use ($callback) { - return is_numeric($value) || $callback($value); - }; - } - if ($valueTypes & Specifications::TYPE_STRING) { - $callback = static function ($value) use ($callback) { - return is_string($value) || $callback($value); - }; - } - if ($valueTypes & Specifications::TYPE_BOOLEAN) { - $callback = static function ($value) use ($callback) { - return is_bool($value) || $callback($value); - }; - } - - return $callback; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Ancestor.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Ancestor.php deleted file mode 100644 index d51691ce33..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Ancestor.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use InvalidArgumentException; - -/** - * A criterion that matches content that is ancestor to the given Location path string. - * - * Content will be matched if it is part of at least one of the given subtree path strings. - */ -class Ancestor extends Criterion implements FilteringCriterion -{ - /** - * Creates a new Ancestor criterion. - * - * @param string|string[] $value Location path string - * - * @throws \InvalidArgumentException if a non integer or string id is given - * @throws \InvalidArgumentException if the value type doesn't match the operator - */ - public function __construct($value) - { - foreach ((array)$value as $pathString) { - if (preg_match('/^(\/\w+)+\/$/', $pathString) !== 1) { - throw new InvalidArgumentException( - "'$pathString' value must follow the pathString format, e.g. '/1/2/'" - ); - } - } - - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_STRING - ), - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_STRING - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/CompositeCriterion.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/CompositeCriterion.php deleted file mode 100644 index 9fcc822494..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/CompositeCriterion.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -abstract class CompositeCriterion extends Criterion -{ - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion */ - public $criteria; - - public function __construct(Criterion $criteria) - { - $this->criteria = $criteria; - } - - public function getSpecifications(): array - { - throw new NotImplementedException('getSpecifications() not implemented for CompositeCriterion'); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentId.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentId.php deleted file mode 100644 index b2d5ab10cf..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentId.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * A criterion that matches content based on its id. - * - * Supported operators: - * - IN: will match from a list of ContentId - * - EQ: will match against one ContentId - */ -class ContentId extends Criterion implements FilteringCriterion -{ - /** - * Creates a new ContentId criterion. - * - * @param int|int[] $value One or more content Id that must be matched. - * - * @throws \InvalidArgumentException if a non numeric id is given - * @throws \InvalidArgumentException if the value type doesn't match the operator - */ - public function __construct($value) - { - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - $types = Specifications::TYPE_INTEGER | Specifications::TYPE_STRING; - - return [ - new Specifications(Operator::IN, Specifications::FORMAT_ARRAY, $types), - new Specifications(Operator::EQ, Specifications::FORMAT_SINGLE, $types), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentTypeGroupId.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentTypeGroupId.php deleted file mode 100644 index 9ea4dd488a..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentTypeGroupId.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * A criterion that will match content based on its ContentTypeGroup id. - * The ContentType must belong to at least one of the matched ContentTypeGroups. - * - * Supported operators: - * - IN: will match from a list of ContentTypeGroup id - * - EQ: will match against one ContentTypeGroup id - */ -class ContentTypeGroupId extends Criterion implements FilteringCriterion -{ - /** - * Creates a new ContentTypeGroup criterion. - * - * Content will be matched if it matches one of the contentTypeGroupId in $value - * - * @param int|int[] $value One or more contentTypeGroupId that must be matched - * - * @throws \InvalidArgumentException if the parameters don't match what the criterion expects - */ - public function __construct($value) - { - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - $types = Specifications::TYPE_INTEGER | Specifications::TYPE_STRING; - - return [ - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - $types - ), - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - $types - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentTypeId.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentTypeId.php deleted file mode 100644 index 0c3f3c9e03..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentTypeId.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use eZ\Publish\SPI\Repository\Values\Trash\Query\Criterion as TrashCriterion; - -/** - * A criterion that matches content based on its ContentType id. - * - * Supported operators: - * - IN: will match from a list of ContentTypeId - * - EQ: will match against one ContentTypeId - */ -class ContentTypeId extends Criterion implements TrashCriterion, FilteringCriterion -{ - /** - * Creates a new ContentType criterion. - * - * Content will be matched if it matches one of the contentTypeId in $value - * - * @param int|int[] $value One or more content type Id that must be matched - * - * @throws \InvalidArgumentException if a non numeric id is given - * @throws \InvalidArgumentException if the value type doesn't match the operator - */ - public function __construct($value) - { - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - $types = Specifications::TYPE_INTEGER | Specifications::TYPE_STRING; - - return [ - new Specifications(Operator::IN, Specifications::FORMAT_ARRAY, $types), - new Specifications(Operator::EQ, Specifications::FORMAT_SINGLE, $types), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentTypeIdentifier.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentTypeIdentifier.php deleted file mode 100644 index a5e3ae7649..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ContentTypeIdentifier.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * A criterion that matches content based on its ContentType Identifier. - * - * Supported operators: - * - IN: will match from a list of ContentTypeIdentifier - * - EQ: will match against one ContentTypeIdentifier - */ -class ContentTypeIdentifier extends Criterion implements FilteringCriterion -{ - /** - * Creates a new ContentType criterion. - * - * Content will be matched if it matches one of the contentTypeIdentifier in $value - * - * @param string|string[] $value One or more content type identifiers that must be matched - * - * @throws \InvalidArgumentException if the value type doesn't match the operator - */ - public function __construct($value) - { - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_STRING - ), - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_STRING - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/CustomField.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/CustomField.php deleted file mode 100644 index a243e1a59d..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/CustomField.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; - -/** - * The Field Criterion class. - * - * Provides content filtering based on Fields contents & values. - */ -class CustomField extends Criterion -{ - public function getSpecifications(): array - { - return [ - new Specifications(Operator::IN, Specifications::FORMAT_ARRAY), - new Specifications(Operator::EQ, Specifications::FORMAT_SINGLE), - new Specifications(Operator::GT, Specifications::FORMAT_SINGLE), - new Specifications(Operator::GTE, Specifications::FORMAT_SINGLE), - new Specifications(Operator::LT, Specifications::FORMAT_SINGLE), - new Specifications(Operator::LTE, Specifications::FORMAT_SINGLE), - new Specifications(Operator::LIKE, Specifications::FORMAT_SINGLE), - new Specifications(Operator::BETWEEN, Specifications::FORMAT_ARRAY, null, 2), - new Specifications(Operator::CONTAINS, Specifications::FORMAT_SINGLE), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/DateMetadata.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/DateMetadata.php deleted file mode 100644 index 1742e8c50c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/DateMetadata.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use eZ\Publish\SPI\Repository\Values\Trash\Query\Criterion as TrashCriterion; -use InvalidArgumentException; - -/** - * A criterion that matches content based on one of the date metadata (created or modified). - * - * Supported Operators: - * EQ, IN: matches content whose date is or belongs to a list of timestamps - * GT, GTE: matches content whose date is greater than/greater than or equals the given timestamp - * LT, LTE: matches content whose date is lower than/lower than or equals the given timestamp - * BETWEEN: matches content whose date is between (included) the TWO given timestamps - * - * Example: - * <code> - * $createdCriterion = new Criterion\DateMetadata( - * Criterion\DateMetadata::CREATED, - * Operator::GTE, - * strtotime( 'yesterday' ) - * ); - * </code> - */ -class DateMetadata extends Criterion implements TrashCriterion, FilteringCriterion -{ - public const MODIFIED = 'modified'; - - public const CREATED = 'created'; - - public const PUBLISHED = 'published'; - - /** - * (applies to TrashService::findTrashItems only). - */ - public const TRASHED = 'trashed'; - - public const TARGETS = [ - self::MODIFIED, - self::CREATED, - self::PUBLISHED, - self::TRASHED, - ]; - - /** - * Creates a new DateMetadata criterion on $metadata. - * - * @throws \InvalidArgumentException If target is unknown - * - * @param string $target One of DateMetadata::CREATED, DateMetadata::MODIFIED or DateMetadata::TRASHED (applies to TrashService::findTrashItems only) - * @param string $operator One of the Operator constants - * @param mixed $value The match value, either as an array of as a single value, depending on the operator - */ - public function __construct(string $target, string $operator, $value) - { - if (!in_array($target, self::TARGETS)) { - throw new InvalidArgumentException("Unknown DateMetadata $target"); - } - parent::__construct($target, $operator, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications(Operator::EQ, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), - new Specifications(Operator::GT, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), - new Specifications(Operator::GTE, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), - new Specifications(Operator::LT, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), - new Specifications(Operator::LTE, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), - new Specifications(Operator::IN, Specifications::FORMAT_ARRAY, Specifications::TYPE_INTEGER), - new Specifications(Operator::BETWEEN, Specifications::FORMAT_ARRAY, Specifications::TYPE_INTEGER, 2), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Field.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Field.php deleted file mode 100644 index 8d7fe0d5af..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Field.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\API\Repository\Values\Content\Query\CustomFieldInterface; - -/** - * The Field Criterion class. - * - * Provides content filtering based on Fields contents & values. - */ -class Field extends Criterion implements CustomFieldInterface -{ - /** - * Custom field definitions to query instead of default field. - * - * @var array - */ - protected $customFields = []; - - public function getSpecifications(): array - { - return [ - new Specifications(Operator::IN, Specifications::FORMAT_ARRAY), - new Specifications(Operator::EQ, Specifications::FORMAT_SINGLE), - new Specifications(Operator::GT, Specifications::FORMAT_SINGLE), - new Specifications(Operator::GTE, Specifications::FORMAT_SINGLE), - new Specifications(Operator::LT, Specifications::FORMAT_SINGLE), - new Specifications(Operator::LTE, Specifications::FORMAT_SINGLE), - new Specifications(Operator::LIKE, Specifications::FORMAT_SINGLE), - new Specifications(Operator::BETWEEN, Specifications::FORMAT_ARRAY, null, 2), - new Specifications(Operator::CONTAINS, Specifications::FORMAT_SINGLE), - ]; - } - - /** - * Set a custom field to query. - * - * Set a custom field to query for a defined field in a defined type. - * - * @param string $type - * @param string $field - * @param string $customField - */ - public function setCustomField(string $type, string $field, string $customField): void - { - $this->customFields[$type][$field] = $customField; - } - - /** - * Return custom field. - * - * If no custom field is set, return null - * - * @param string $type - * @param string $field - * - * @return string|null - */ - public function getCustomField(string $type, string $field): ?string - { - if (!isset($this->customFields[$type]) || - !isset($this->customFields[$type][$field])) { - return null; - } - - return $this->customFields[$type][$field]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/FieldRelation.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/FieldRelation.php deleted file mode 100644 index 8948b4122f..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/FieldRelation.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; - -/** - * A criterion that matches Content based on the relations in relation field. - * This includes Relation and RelationList field types in standard installation, but also any - * other field type storing {@link \eZ\Publish\API\Repository\Values\Content\Relation::FIELD} - * type relation. - * - * Supported operators: - * - IN: will match if Content relates to one or more of the given ids through given relation field - * - CONTAINS: will match if Content relates to all of the given ids through given relation field - */ -class FieldRelation extends Criterion -{ - public function getSpecifications(): array - { - $types = Specifications::TYPE_INTEGER | Specifications::TYPE_STRING; - - return [ - new Specifications(Operator::CONTAINS, Specifications::FORMAT_SINGLE | Specifications::FORMAT_ARRAY, $types), - new Specifications(Operator::IN, Specifications::FORMAT_ARRAY, $types), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/FullText.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/FullText.php deleted file mode 100644 index 2968ca9964..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/FullText.php +++ /dev/null @@ -1,158 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\API\Repository\Values\Content\Query\CustomFieldInterface; - -/** - * Full text search criterion. - * - * The string provided in this criterion is matched as a full text query - * against all indexed content objects in the storage layer. - * - * Normalization and querying capabilities might depend on the system - * configuration or the used search engine and might differ. To find about - * supported querying capabilities you can use - * {@link \eZ\Publish\API\Repository\SearchService::supports()} method. - * - * If supported, advanced full text query has the following semantics: - * - * - If multiple words are specified an OR query is performed. - * - Boolean operators are supported: AND (&&), OR (||), NOT (!). - * - Required/prohibit operators are supported: +, -. - * - Grouping is supported through parentheses. - * - Phrases are supported using double quotes. - * - Simple wild cards are supported. If an asterisk (*) is used at the end or - * beginning of a word this is translated into a wild card query. Thus "fo*" - * would match "foo" and "foobar", for example. - * - Advanced language analysis (like stemming, synonym expansion and stop word - * removal) might be applied to the words provided in the query. - * - * If advanced full text query is not supported, basic query format will be - * available: - * - * - If multiple words are specified an AND query is performed. OR queries are - * not supported. - * - Simple wild cards are supported. If an asterisk (*) is used at the end or - * beginning of a word this is translated into a wild card query. Thus "fo*" - * would match "foo" and "foobar", for example. - * - Simple stop word removal might be applied to the words provided in the - * query. - */ -class FullText extends Criterion implements CustomFieldInterface -{ - /** - * Fuzziness of the fulltext search. - * - * May be a value between 0. (fuzzy) and 1. (sharp). - * - * @var float - */ - public $fuzziness = 1.; - - /** - * Boost for certain fields. - * - * Array of boosts to apply for certain fields – the array should look like - * this: - * - * <code> - * array( - * 'title' => 2, - * … - * ) - * </code> - * - * @var array - */ - public $boost = []; - - /** - * Analyzer configuration. - * - * @TODO: Define how this could look like - * - * @var mixed - */ - public $analyzers; - - /** - * Analyzer wildcard handling configuration. - * - * @TODO: Define how this could look like - * - * @var mixed - */ - public $wildcards; - - /** - * Custom field definitions to query instead of default field. - * - * @var array - */ - protected $customFields = []; - - public function __construct($value, array $properties = []) - { - parent::__construct(null, Operator::LIKE, $value); - - // Assign additional properties, ugly but with the existing constructor - // API the only sensible way, I guess. - foreach ($properties as $name => $value) { - if (!isset($this->$name)) { - throw new \InvalidArgumentException("Unknown property $name."); - } - - $this->$name = $value; - } - } - - public function getSpecifications(): array - { - return [ - new Specifications(Operator::LIKE, Specifications::FORMAT_SINGLE), - ]; - } - - /** - * Set a custom field to query. - * - * Set a custom field to query for a defined field in a defined type. - * - * @param string $type - * @param string $field - * @param string $customField - */ - public function setCustomField(string $type, string $field, string $customField): void - { - $this->customFields[$type][$field] = $customField; - } - - /** - * Retun custom field. - * - * If no custom field is set, return null - * - * @param string $type - * @param string $field - * - * @return mixed - */ - public function getCustomField(string $type, string $field): ?string - { - if (!isset($this->customFields[$type]) || - !isset($this->customFields[$type][$field])) { - return null; - } - - return $this->customFields[$type][$field]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/IsFieldEmpty.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/IsFieldEmpty.php deleted file mode 100644 index 2d46137764..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/IsFieldEmpty.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; - -/** - * A criterion that matches content based on if Field is empty. - */ -class IsFieldEmpty extends Criterion -{ - /** - * @param string $fieldDefinitionIdentifier - * @param bool $value - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function __construct(string $fieldDefinitionIdentifier, bool $value = true) - { - parent::__construct($fieldDefinitionIdentifier, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications(Operator::EQ, Specifications::FORMAT_SINGLE, Specifications::TYPE_BOOLEAN), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/IsUserBased.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/IsUserBased.php deleted file mode 100644 index 531f009747..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/IsUserBased.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -class IsUserBased extends Criterion implements FilteringCriterion -{ - public function __construct(bool $value = true) - { - parent::__construct(null, null, $value); - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications[] - */ - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_BOOLEAN - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/IsUserEnabled.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/IsUserEnabled.php deleted file mode 100644 index b82a55fdac..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/IsUserEnabled.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -class IsUserEnabled extends Criterion implements FilteringCriterion -{ - public function __construct(bool $value = true) - { - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_BOOLEAN - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LanguageCode.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LanguageCode.php deleted file mode 100644 index d688fea1c5..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LanguageCode.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * A criterion that matches content based on its language code and always-available state. - * - * Supported operators: - * - IN: matches against a list of language codes - * - EQ: matches against one language code - */ -class LanguageCode extends Criterion implements FilteringCriterion -{ - /** - * Switch for matching Content that is always-available. - * - * @var bool - */ - public $matchAlwaysAvailable; - - /** - * Creates a new LanguageCode criterion. - * - * @param string|string[] $value One or more language codes that must be matched - * @param bool $matchAlwaysAvailable Denotes if always-available Content is to be matched regardless - * of language codes, this is the default behaviour - * - * @throws \InvalidArgumentException if non string value is given - * @throws \InvalidArgumentException if the value type doesn't match the operator - */ - public function __construct($value, bool $matchAlwaysAvailable = true) - { - if (!is_bool($matchAlwaysAvailable)) { - throw new InvalidArgumentType('matchAlwaysAvailable', 'boolean', $matchAlwaysAvailable); - } - - $this->matchAlwaysAvailable = $matchAlwaysAvailable; - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_STRING - ), - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_STRING - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Location.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Location.php deleted file mode 100644 index 66dd753f27..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Location.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -/** - * This is the base class for Location criterions. - */ -abstract class Location extends Criterion -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Location/Depth.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Location/Depth.php deleted file mode 100644 index aba6564ca8..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Location/Depth.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * The Depth Criterion class. - * - * Provides Location filtering based on depth - */ -class Depth extends Location implements FilteringCriterion -{ - /** - * Creates a new Depth criterion. - * - * @throws \InvalidArgumentException if a non numeric id is given - * @throws \InvalidArgumentException if the value type doesn't match the operator - * - * @param string $operator One of the Operator constants - * @param mixed $value The match value, either as an array of as a single value, depending on the operator - */ - public function __construct(string $operator, $value) - { - parent::__construct(null, $operator, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_INTEGER - ), - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER - ), - new Specifications( - Operator::GT, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER - ), - new Specifications( - Operator::GTE, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER - ), - new Specifications( - Operator::LT, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER - ), - new Specifications( - Operator::LTE, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER - ), - new Specifications( - Operator::BETWEEN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_INTEGER, - 2 - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Location/IsMainLocation.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Location/IsMainLocation.php deleted file mode 100644 index 25d20d776f..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Location/IsMainLocation.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use InvalidArgumentException; - -/** - * A criterion that matches Location based on if it is main Location or not. - */ -class IsMainLocation extends Location implements FilteringCriterion -{ - /** - * Main constant: is main. - */ - public const MAIN = 0; - - /** - * Main constant: is not main. - */ - public const NOT_MAIN = 1; - - /** - * Creates a new IsMainLocation criterion. - * - * @throws \InvalidArgumentException - * - * @param int $value one of self::MAIN and self::NOT_MAIN - */ - public function __construct($value) - { - if ($value !== self::MAIN && $value !== self::NOT_MAIN) { - throw new InvalidArgumentException("Invalid main status value $value"); - } - - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER - ), - ]; - } - - /** - * @deprecated since 7.2, will be removed in 8.0. Use the constructor directly instead. - */ - public static function createFromQueryBuilder($target, $operator, $value) - { - @trigger_error('The ' . __METHOD__ . ' method is deprecated since version 7.2 and will be removed in 8.0.', E_USER_DEPRECATED); - - return new self($value); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Location/Priority.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Location/Priority.php deleted file mode 100644 index d5f0c9d08f..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Location/Priority.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * A criterion that matches Location based on its priority. - * - * Supported operators: - * - BETWEEN: matches location whose priority is between (included) the TWO given priorities - * - GT, GTE: matches location whose priority is greater than/greater than or equals the given priority - * - LT, LTE: matches location whose priority is lower than/lower than or equals the given priority - */ -class Priority extends Location implements FilteringCriterion -{ - /** - * Creates a new LocationPriority criterion. - * - * @param string $operator One of the Operator constants - * @param mixed $value The match value, either as an array of as a single value, depending on the operator - */ - public function __construct(string $operator, $value) - { - parent::__construct(null, $operator, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications(Operator::BETWEEN, Specifications::FORMAT_ARRAY, Specifications::TYPE_INTEGER), - new Specifications(Operator::GT, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), - new Specifications(Operator::GTE, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), - new Specifications(Operator::LT, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), - new Specifications(Operator::LTE, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), - ]; - } - - /** - * @deprecated since 7.2, will be removed in 8.0. Use the constructor directly instead. - */ - public static function createFromQueryBuilder($target, $operator, $value) - { - @trigger_error('The ' . __METHOD__ . ' method is deprecated since version 7.2 and will be removed in 8.0.', E_USER_DEPRECATED); - - return new self($operator, $value); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LocationId.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LocationId.php deleted file mode 100644 index bfd736027a..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LocationId.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * A criterion that matches content based on its own location id. - * - * Parent location id is done using {@see ParentLocationId} - * - * Supported operators: - * - IN: matches against a list of location ids - * - EQ: matches against a unique location id - */ -class LocationId extends Criterion implements FilteringCriterion -{ - /** - * Creates a new LocationId criterion. - * - * @param int|int[] $value One or more locationId that must be matched - * - * @throws \InvalidArgumentException if a non numeric id is given - * @throws \InvalidArgumentException if the value type doesn't match the operator - */ - public function __construct($value) - { - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LocationRemoteId.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LocationRemoteId.php deleted file mode 100644 index 8f3d86fbca..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LocationRemoteId.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * A criterion that matches content based on remote ID of its locations. - * - * Supported operators: - * - IN: will match from a list of location remote IDs - * - EQ: will match against one location remote ID - */ -class LocationRemoteId extends Criterion implements FilteringCriterion -{ - /** - * Creates a new locationRemoteId criterion. - * - * @param string|string[] $value One or more locationRemoteId that must be matched - * - * @throws \InvalidArgumentException if a non numeric id is given - * @throws \InvalidArgumentException if the value type doesn't match the operator - */ - public function __construct($value) - { - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LogicalAnd.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LogicalAnd.php deleted file mode 100644 index d862b6b9b1..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LogicalAnd.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use eZ\Publish\SPI\Repository\Values\Trash\Query\Criterion as TrashCriterion; - -/** - * This criterion implements a logical AND criterion and will only match - * if ALL of the given criteria match. - */ -class LogicalAnd extends LogicalOperator implements TrashCriterion, FilteringCriterion -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LogicalNot.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LogicalNot.php deleted file mode 100644 index 8ca1ea295c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LogicalNot.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use eZ\Publish\SPI\Repository\Values\Trash\Query\Criterion as TrashCriterion; - -/** - * A NOT logical criterion. - */ -class LogicalNot extends LogicalOperator implements FilteringCriterion, TrashCriterion -{ - /** - * Creates a new NOT logic criterion. - * - * Will match of the given criterion doesn't match - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion[] $criteria One criterion, as an array - * - * @throws \InvalidArgumentException if more than one criterion is given in the array parameter - */ - public function __construct(Criterion $criterion) - { - parent::__construct([$criterion]); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LogicalOperator.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LogicalOperator.php deleted file mode 100644 index 9738056d1c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LogicalOperator.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException; - -/** - * Note that the class should ideally have been in a Logical namespace, but it would have then be named 'And', - * and 'And' is a PHP reserved word. - */ -abstract class LogicalOperator extends Criterion -{ - /** - * The set of criteria combined by the logical operator. - * - * @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion[] - */ - public $criteria = []; - - /** - * Creates a Logic operation with the given criteria. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion[] $criteria - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException - */ - public function __construct(array $criteria) - { - foreach ($criteria as $key => $criterion) { - if (!$criterion instanceof Criterion) { - throw new InvalidCriterionArgumentException($key, $criterion, Criterion::class); - } - - $this->criteria[] = $criterion; - } - } - - /** - * @return array - * - * @deprecated in LogicalOperators since 7.2. - * It will be removed in 8.0 when Logical Operator no longer extends Criterion. - */ - public function getSpecifications(): array - { - @trigger_error('The ' . __METHOD__ . ' method is deprecated since version 7.2 and will be removed in 8.0.', E_USER_DEPRECATED); - - throw new NotImplementedException('getSpecifications() not implemented for LogicalOperators'); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LogicalOr.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LogicalOr.php deleted file mode 100644 index 9b1e6e95b3..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/LogicalOr.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use eZ\Publish\SPI\Repository\Values\Trash\Query\Criterion as TrashCriterion; - -/** - * This criterion implements a logical OR criterion and will only match - * if AT LEAST ONE of the given criteria match. - */ -class LogicalOr extends LogicalOperator implements FilteringCriterion, TrashCriterion -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/MapLocationDistance.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/MapLocationDistance.php deleted file mode 100644 index beb9037b6c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/MapLocationDistance.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Value\MapLocationValue; -use eZ\Publish\API\Repository\Values\Content\Query\CustomFieldInterface; - -/** - * The MapLocationDistance Criterion class. - * - * Provides content filtering based on distance from geographical location. - */ -class MapLocationDistance extends Criterion implements CustomFieldInterface -{ - /** - * Custom field definitions to query instead of default field. - * - * @var array - */ - protected $customFields = []; - - /** - * @todo needs to be defined, could be a string identifying one of the predefined easing methods - * - * @var array - */ - protected $boost; - - /** - * @param string $target FieldDefinition identifier - * @param string $operator One of the supported Operator constants - * @param float|float[] $distance The match value in kilometers, either as an array - * or as a single value, depending on the operator - * @param float $latitude Latitude of the location that distance is calculated from - * @param float $longitude Longitude of the location that distance is calculated from - */ - public function __construct(string $target, string $operator, $distance, float $latitude, float $longitude) - { - $distanceStart = new MapLocationValue($latitude, $longitude); - parent::__construct($target, $operator, $distance, $distanceStart); - } - - public function getSpecifications(): array - { - return [ - new Specifications(Operator::IN, Specifications::FORMAT_ARRAY), - new Specifications(Operator::EQ, Specifications::FORMAT_SINGLE), - new Specifications(Operator::GT, Specifications::FORMAT_SINGLE), - new Specifications(Operator::GTE, Specifications::FORMAT_SINGLE), - new Specifications(Operator::LT, Specifications::FORMAT_SINGLE), - new Specifications(Operator::LTE, Specifications::FORMAT_SINGLE), - new Specifications(Operator::BETWEEN, Specifications::FORMAT_ARRAY, null, 2), - ]; - } - - /** - * Set a custom field to query. - * - * Set a custom field to query for a defined field in a defined type. - * - * @param string $type - * @param string $field - * @param string $customField - */ - public function setCustomField(string $type, string $field, string $customField): void - { - $this->customFields[$type][$field] = $customField; - } - - /** - * Return custom field. - * - * If no custom field is set, return null - * - * @param string $type - * @param string $field - * - * @return ?string - */ - public function getCustomField(string $type, string $field): ?string - { - if (!isset($this->customFields[$type]) || - !isset($this->customFields[$type][$field])) { - return null; - } - - return $this->customFields[$type][$field]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/MatchAll.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/MatchAll.php deleted file mode 100644 index c03c1e4747..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/MatchAll.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use eZ\Publish\SPI\Repository\Values\Trash\Query\Criterion as TrashCriterion; - -/** - * A criterion that just matches everything. - */ -class MatchAll extends Criterion implements FilteringCriterion, TrashCriterion -{ - /** - * Creates a new MatchAll criterion. - */ - public function __construct() - { - // Do NOT call parent constructor. It tries to be too smart. - } - - public function getSpecifications(): array - { - return []; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/MatchNone.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/MatchNone.php deleted file mode 100644 index 345e94fe50..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/MatchNone.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use eZ\Publish\SPI\Repository\Values\Trash\Query\Criterion as TrashCriterion; - -/** - * A criterion that just matches nothing. - * - * Useful for BlockingLimitation type, where a limitation is typically missing and needs to - * tell the system should block everything within the OR conditions it might be part of. - */ -class MatchNone extends Criterion implements FilteringCriterion, TrashCriterion -{ - public function __construct() - { - // Do NOT call parent constructor. It tries to be too smart. - } - - public function getSpecifications(): array - { - return []; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ObjectStateId.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ObjectStateId.php deleted file mode 100644 index 6d9cc7f915..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ObjectStateId.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * A criterion that matches content based on its state. - * - * Supported operators: - * - IN: matches against a list of object state IDs - * - EQ: matches against one object state ID - */ -class ObjectStateId extends Criterion implements FilteringCriterion -{ - /** - * Creates a new ObjectStateId criterion. - * - * @param int|int[] $value One or more object state IDs that must be matched - * - * @throws \InvalidArgumentException if a non numeric id is given - * @throws \InvalidArgumentException if the value type doesn't match the operator - */ - public function __construct($value) - { - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ObjectStateIdentifier.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ObjectStateIdentifier.php deleted file mode 100644 index cca353dfce..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ObjectStateIdentifier.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -class ObjectStateIdentifier extends Criterion implements FilteringCriterion -{ - /** - * @param string|string[] $value - * @param string|null $target - */ - public function __construct($value, ?string $target = null) - { - parent::__construct($target, null, $value); - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications[] - */ - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY - ), - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Operator/Specifications.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Operator/Specifications.php deleted file mode 100644 index cc0501c6f1..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Operator/Specifications.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; - -/** - * This class is used by Criteria to describe which operators they support. - * - * Instances of this class are returned in an array by the {@see Criterion::getSpecifications()} method - */ -class Specifications -{ - /** - * Criterion input type description constants. - */ - public const FORMAT_SINGLE = 1; - public const FORMAT_ARRAY = 2; - - /** - * Criterion input value type description constants. - * Used by {@see getDescription()} to say which type of values an operator expects. - */ - public const TYPE_INTEGER = 1; - public const TYPE_STRING = 2; - public const TYPE_BOOLEAN = 4; - - /** - * Specified operator, as one of the Operator::* constants. - */ - public $operator; - - /** - * Format supported for the Criterion value, either {@see self::FORMAT_SINGLE} for single - * or {@see self::FORMAT_ARRAY} for multiple. - * - * @see self::FORMAT_* - * - * @var int - */ - public $valueFormat; - - /** - * Accepted values types, specifying what type of variables are accepted as a value. - * - * @see self::TYPE_* - * - * @var int - */ - public $valueTypes; - - /** - * Limitation on the number of items as the value. - * - * Only usable if {@see $valueFormat} is {@see self::FORMAT_ARRAY}. - * Not setting it means that 1...n will be required - * - * @var int - */ - public $valueCount; - - /** - * Creates a new Specifications object. - * - * @param string $operator The specified operator, as one of the Operator::* constants - * @param int $valueFormat The accepted value format, either {@see self::FORMAT_ARRAY} or {@see self::FORMAT_SINGLE} - * @param int $valueTypes The supported value types, as a bit field of the {@see self::TYPE_*} constants - * @param int $valueCount The required number of values, when the accepted format is {@see self::FORMAT_ARRAY} - */ - public function __construct(string $operator, int $valueFormat, ?int $valueTypes = null, ?int $valueCount = null) - { - $this->operator = $operator; - $this->valueFormat = $valueFormat; - $this->valueTypes = $valueTypes; - $this->valueCount = $valueCount; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ParentLocationId.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ParentLocationId.php deleted file mode 100644 index a1825ce88c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/ParentLocationId.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * A criterion that matches content based on its parent location id. - * - * Own location id is done using {@see LocationId} - * - * Supported operators: - * - IN: matches against a list of location ids - * - EQ: matches against a unique location id - */ -class ParentLocationId extends Criterion implements FilteringCriterion -{ - /** - * Creates a new ParentLocationId criterion. - * - * @param int|int[] $value One or more locationId parent locations must be matched against - * - * @throws \InvalidArgumentException if a non numeric id is given - * @throws \InvalidArgumentException if the value type doesn't match the operator - */ - public function __construct($value) - { - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/RemoteId.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/RemoteId.php deleted file mode 100644 index a4ee38cd86..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/RemoteId.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * A criterion that matches content based on its RemoteId. - * - * Supported operators: - * - IN: will match from a list of RemoteId - * - EQ: will match against one RemoteId - */ -class RemoteId extends Criterion implements FilteringCriterion -{ - /** - * Creates a new remoteId criterion. - * - * @param string|string[] $value One or more remoteId that must be matched - * - * @throws \InvalidArgumentException if a non numeric id is given - * @throws \InvalidArgumentException if the value type doesn't match the operator - */ - public function __construct($value) - { - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/SectionId.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/SectionId.php deleted file mode 100644 index 2e2c5657ae..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/SectionId.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use eZ\Publish\SPI\Repository\Values\Trash\Query\Criterion as TrashCriterion; - -/** - * SectionId Criterion. - * - * Will match content that belongs to one of the given sections - */ -class SectionId extends Criterion implements TrashCriterion, FilteringCriterion -{ - /** - * Creates a new Section criterion. - * - * Matches the content against one or more sectionId - * - * @param int|int[] $value One or more sectionId that must be matched - * - * @throws \InvalidArgumentException if a non numeric id is given - * @throws \InvalidArgumentException if the value type doesn't match the operator - */ - public function __construct($value) - { - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/SectionIdentifier.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/SectionIdentifier.php deleted file mode 100644 index 59d6024666..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/SectionIdentifier.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -class SectionIdentifier extends Criterion implements FilteringCriterion -{ - /** - * @param string|string[] $value - */ - public function __construct($value) - { - parent::__construct(null, null, $value); - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications[] - */ - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY - ), - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Sibling.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Sibling.php deleted file mode 100644 index 7cc228b7f0..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Sibling.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * A criterion that matches content that is sibling to the given Location. - */ -class Sibling extends CompositeCriterion implements FilteringCriterion -{ - public function __construct(int $locationId, int $parentLocationId) - { - $criteria = new LogicalAnd([ - new ParentLocationId($parentLocationId), - new LogicalNot( - new LocationId($locationId) - ), - ]); - - parent::__construct($criteria); - } - - public static function fromLocation(Location $location): self - { - return new self($location->id, $location->parentLocationId); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Subtree.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Subtree.php deleted file mode 100644 index 4514a32b0c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Subtree.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use InvalidArgumentException; - -/** - * Criterion that matches content that belongs to a given (list of) Subtree(s). - * - * Content will be matched if it is part of at least one of the given subtree path strings - */ -class Subtree extends Criterion implements FilteringCriterion -{ - /** - * Creates a new SubTree criterion. - * - * @param string|string[] $value an array of subtree path strings, eg: /1/2/ - * - * @throws \InvalidArgumentException if a non path string is given - * @throws \InvalidArgumentException if the value type doesn't match the operator - */ - public function __construct($value) - { - foreach ((array)$value as $pathString) { - if (preg_match('/^(\/\w+)+\/$/', $pathString) !== 1) { - throw new InvalidArgumentException("'$pathString' value must follow the pathString format, e.g. /1/2/"); - } - } - - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_STRING - ), - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_STRING - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserEmail.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserEmail.php deleted file mode 100644 index cfd644bd3a..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserEmail.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -class UserEmail extends Criterion implements FilteringCriterion -{ - /** - * @param string|string[] $value - * @param string|null $operator - */ - public function __construct($value, ?string $operator = null) - { - parent::__construct(null, $operator, $value); - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications[] - */ - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE - ), - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY - ), - new Specifications( - Operator::LIKE, - Specifications::FORMAT_SINGLE - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserId.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserId.php deleted file mode 100644 index ccc0c7b6b3..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserId.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -class UserId extends Criterion implements FilteringCriterion -{ - /** - * @param int|int[] $value - */ - public function __construct($value) - { - parent::__construct(null, null, $value); - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications[] - */ - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE - ), - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserLogin.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserLogin.php deleted file mode 100644 index 2f58ee602f..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserLogin.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -class UserLogin extends Criterion implements FilteringCriterion -{ - /** - * @param string|string[] $value - * @param string|null $operator - */ - public function __construct($value, ?string $operator = null) - { - parent::__construct(null, $operator, $value); - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications[] - */ - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE - ), - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY - ), - new Specifications( - Operator::LIKE, - Specifications::FORMAT_SINGLE - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserMetadata.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserMetadata.php deleted file mode 100644 index cffbc9fab7..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/UserMetadata.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use eZ\Publish\SPI\Repository\Values\Trash\Query\Criterion as TrashCriterion; -use InvalidArgumentException; - -/** - * A criterion that matches content based on one of the user metadata (owner, - * group, modifier). - * - * Supported Operators: - * EQ, IN: Matches the provided user ID(s) against the user IDs in the database - * - * Example: - * <code> - * $createdCriterion = new Criterion\UserMetadata( - * Criterion\UserMetadata::OWNER, - * Operator::IN, - * array( 10, 14 ) - * ); - * </code> - */ -class UserMetadata extends Criterion implements TrashCriterion, FilteringCriterion -{ - /** - * UserMetadata target: Owner user. - */ - public const OWNER = 'owner'; - - /** - * UserMetadata target: Owner user group. - */ - public const GROUP = 'group'; - - /** - * UserMetadata target: Modifier. - */ - public const MODIFIER = 'modifier'; - - /** - * Creates a new UserMetadata criterion on $metadata. - * - * @throws \InvalidArgumentException If target is unknown - * - * @param string $target One of UserMetadata::OWNER, UserMetadata::GROUP or UserMetadata::MODIFIED - * @param string|null $operator The operator the Criterion uses. If null is given, will default to Operator::IN if $value is an array, Operator::EQ if it is not. - * @param mixed $value The match value, either as an array of as a single value, depending on the operator - */ - public function __construct(string $target, ?string $operator, $value) - { - switch ($target) { - case self::OWNER: - case self::GROUP: - case self::MODIFIER: - parent::__construct($target, $operator, $value); - - return; - } - - throw new InvalidArgumentException("Unknown UserMetadata $target"); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - new Specifications( - Operator::IN, - Specifications::FORMAT_ARRAY, - Specifications::TYPE_INTEGER | Specifications::TYPE_STRING - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Value.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Value.php deleted file mode 100644 index 4f2e91f6c5..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Value.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -/** - * Struct that stores extra value information for a Criterion object. - */ -abstract class Value -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Visibility.php b/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Visibility.php deleted file mode 100644 index 8c10a5562c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/Criterion/Visibility.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use InvalidArgumentException; - -/** - * A criterion that matches content based on its visibility. - * - * Warning: This Criterion acts on all locations of a Content, so it will include hidden - * content within the tree you are searching for if content has visible location elsewhere. - * This is intentional and you should rather use LocationSearch if this is not the behaviour you want. - */ -class Visibility extends Criterion implements FilteringCriterion -{ - /** - * Visibility constant: visible. - */ - public const VISIBLE = 0; - - /** - * Visibility constant: hidden. - */ - public const HIDDEN = 1; - - /** - * Creates a new Visibility criterion. - * - * @param int $value Visibility: self::VISIBLE, self::HIDDEN - * - * @throws \InvalidArgumentException - */ - public function __construct(int $value) - { - if ($value !== self::VISIBLE && $value !== self::HIDDEN) { - throw new InvalidArgumentException("Invalid visibility value $value"); - } - - parent::__construct(null, null, $value); - } - - public function getSpecifications(): array - { - return [ - new Specifications( - Operator::EQ, - Specifications::FORMAT_SINGLE, - Specifications::TYPE_INTEGER - ), - ]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/CriterionInterface.php b/eZ/Publish/API/Repository/Values/Content/Query/CriterionInterface.php deleted file mode 100644 index b2004c46e8..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/CriterionInterface.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query; - -/** - * Base interface for Criterion implementations. - */ -interface CriterionInterface -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder.php b/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder.php deleted file mode 100644 index a68863bc2c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class is the base class for facet builders. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -abstract class FacetBuilder extends ValueObject -{ - /** - * The name of the facet. - * - * @var string - */ - public $name; - - /** - * If true the facet runs in a global mode not restricted by the query. - * - * @var bool - */ - public $global = false; - - /** - * An additional facet filter that will further filter the documents the facet will be executed on. - * - * @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion - */ - public $filter = null; - - /** - * Number of facets (terms) returned. - * - * @var int - */ - public $limit = 10; - - /** - * Specifies the minimum count. Only facet groups with more or equal results are returned. - * - * @var int - */ - public $minCount = 1; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/ContentTypeFacetBuilder.php b/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/ContentTypeFacetBuilder.php deleted file mode 100644 index 5df22c5711..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/ContentTypeFacetBuilder.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -/** - * Building a content type facet. - * - * If provided the search service returns a ContentTypeFacet - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class ContentTypeFacetBuilder extends FacetBuilder -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/CriterionFacetBuilder.php b/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/CriterionFacetBuilder.php deleted file mode 100644 index e7e9cb2f97..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/CriterionFacetBuilder.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -/** - * Build a criterion facet. - * - * If provided the search service returns a CriterionFacet based on the criterion provided - * in the FacetBuilder class. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class CriterionFacetBuilder extends FacetBuilder -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/Location.php b/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/Location.php deleted file mode 100644 index 506be6c01f..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/Location.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -/** - * This is the base class for Location facet builders. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -abstract class Location extends FacetBuilder -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/Location/LocationFacetBuilder.php b/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/Location/LocationFacetBuilder.php deleted file mode 100644 index 2aa657b10d..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/Location/LocationFacetBuilder.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder\Location; - -/** - * Build a Subtree facet. - * - * If provided the search service returns a LocationFacet which - * contains the counts for all Locations below the child Locations. - * - * @todo: check hierarchical facets - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class LocationFacetBuilder extends Location -{ - /** - * The parent location. - * - * @var \eZ\Publish\API\Repository\Values\Content\Location - */ - public $location; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/LocationFacetBuilder.php b/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/LocationFacetBuilder.php deleted file mode 100644 index 152d26c38c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/LocationFacetBuilder.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -/** - * Build a Subtree facet. - * - * If provided the search service returns a LocationFacet - * which contains the counts for all content objects below the child locations. - * - * @todo: check hierarchical facets - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class LocationFacetBuilder extends FacetBuilder -{ - /** - * The parent location. - * - * @var \eZ\Publish\API\Repository\Values\Content\Location - */ - public $location; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/SectionFacetBuilder.php b/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/SectionFacetBuilder.php deleted file mode 100644 index cbc69c5b0d..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/SectionFacetBuilder.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -/** - * Build a section facet. - * - * If provided the search service returns a SectionFacet. Which contains the counts for - * content in the existing sections. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class SectionFacetBuilder extends FacetBuilder -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/TermFacetBuilder.php b/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/TermFacetBuilder.php deleted file mode 100644 index d665d55c23..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/TermFacetBuilder.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -/** - * Build a term facet. - * - * If provided the search service returns a TermFacet which contains the counts for - * content containing terms in arbitrary fields - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class TermFacetBuilder extends FacetBuilder -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/UserFacetBuilder.php b/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/UserFacetBuilder.php deleted file mode 100644 index ae94931f3d..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/FacetBuilder/UserFacetBuilder.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; - -/** - * Build a user facet. - * - * If provided the search service returns a UserFacet for the given type. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class UserFacetBuilder extends FacetBuilder -{ - /** - * Owner user. - */ - public const OWNER = 'owner'; - - /** - * Owner user group. - */ - public const GROUP = 'group'; - - /** - * Modifier. - */ - public const MODIFIER = 'modifier'; - - /** - * The type of the user facet. - * - * @var string - */ - public $type = self::OWNER; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause.php deleted file mode 100644 index e7e04c746c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target; -use InvalidArgumentException; - -/** - * This class is the base for SortClause classes, used to set sorting of content queries. - */ -abstract class SortClause -{ - /** - * Sort direction - * One of Query::SORT_ASC or Query::SORT_DESC;. - * - * @var string - */ - public $direction = Query::SORT_ASC; - - /** - * Sort target, high level: section_identifier, attribute_value, etc. - * - * @var string - */ - public $target; - - /** - * Extra target data, required by some sort clauses, field for instance. - * - * @var \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target|null - */ - public $targetData; - - /** - * Constructs a new SortClause on $sortTarget in direction $sortDirection. - * - * @param string $sortTarget - * @param string $sortDirection one of Query::SORT_ASC or Query::SORT_DESC - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target|null $targetData Extra target data, used by some clauses (field for instance) - * - * @throws \InvalidArgumentException if the given sort order isn't one of Query::SORT_ASC or Query::SORT_DESC - */ - public function __construct(string $sortTarget, string $sortDirection, ?Target $targetData = null) - { - if ($sortDirection !== Query::SORT_ASC && $sortDirection !== Query::SORT_DESC) { - throw new InvalidArgumentException('Sort direction must be one of Query::SORT_ASC or Query::SORT_DESC'); - } - - $this->direction = $sortDirection; - $this->target = $sortTarget; - - if ($targetData !== null) { - $this->targetData = $targetData; - } - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/ContentId.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/ContentId.php deleted file mode 100644 index 93349e75c7..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/ContentId.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; - -/** - * Sets sort direction on Content ID for a content query. - * - * Especially useful to get reproducible search results in tests. - * - * Note: order will vary per search engine, depending on how Content ID is stored in the search - * backend. For Legacy search engine IDs are stored as integers, while with Solr search engine - * they are stored as strings. Hence the difference will be basically the one between - * numerical and alphabetical order of sorting. - * - * This reflects API definition of IDs as mixed type (integer or string). - */ -class ContentId extends SortClause implements FilteringSortClause -{ - /** - * Constructs a new ContentId SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('content_id', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/ContentName.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/ContentName.php deleted file mode 100644 index c6aa4eafc3..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/ContentName.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use eZ\Publish\SPI\Repository\Values\Trash\Query\SortClause as TrashSortClause; - -/** - * Sets sort direction on Content name for a content query. - */ -class ContentName extends SortClause implements FilteringSortClause, TrashSortClause -{ - /** - * Constructs a new ContentName SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('content_name', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/ContentTranslatedName.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/ContentTranslatedName.php deleted file mode 100644 index e787421e9b..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/ContentTranslatedName.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -final class ContentTranslatedName extends SortClause -{ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('content_translated_name', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/CustomField.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/CustomField.php deleted file mode 100644 index 9334b94e31..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/CustomField.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target\CustomFieldTarget; - -/** - * Sorts search results by raw search index field. - */ -final class CustomField extends SortClause -{ - public function __construct(string $field, string $sortDirection = Query::SORT_ASC) - { - parent::__construct('custom_field', $sortDirection, new CustomFieldTarget($field)); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/DateModified.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/DateModified.php deleted file mode 100644 index dee5659716..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/DateModified.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; - -/** - * Sets sort direction on the content modification date for a content query. - */ -class DateModified extends SortClause implements FilteringSortClause -{ - /** - * Constructs a new DateModified SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('date_modified', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/DatePublished.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/DatePublished.php deleted file mode 100644 index 9911436596..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/DatePublished.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; - -/** - * Sets sort direction on the content creation date for a content query. - */ -class DatePublished extends SortClause implements FilteringSortClause -{ - /** - * Constructs a new DatePublished SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('date_published', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Field.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Field.php deleted file mode 100644 index 06ee47929f..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Field.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\CustomFieldInterface; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target\FieldTarget; - -/** - * Sets sort direction on a field value for a content query. - * - * Note: for fields of some field types order will vary per search engine. This comes from the - * different way of storing IDs in the search backend, and therefore relates to the field types - * that store ID value for sorting (Relation field type). For Legacy search engine IDs are stored as - * integers, while with Solr search engine they are stored as strings. In that case the - * difference will be basically the one between numerical and alphabetical order of sorting. - * - * This reflects API definition of IDs as mixed type (integer or string). - */ -class Field extends SortClause implements CustomFieldInterface -{ - /** - * Custom fields to sort by instead of the default field. - * - * @var array - */ - protected $customFields = []; - - /** - * Constructs a new Field SortClause on Type $typeIdentifier and Field $fieldIdentifier. - * - * @param string $typeIdentifier - * @param string $fieldIdentifier - * @param string $sortDirection - */ - public function __construct(string $typeIdentifier, string $fieldIdentifier, string $sortDirection = Query::SORT_ASC) - { - parent::__construct( - 'field', - $sortDirection, - new FieldTarget($typeIdentifier, $fieldIdentifier) - ); - } - - /** - * Set a custom field to sort by. - * - * Set a custom field to sort by for a defined field in a defined type. - * - * @param string $type - * @param string $field - * @param string $customField - */ - public function setCustomField(string $type, string $field, string $customField): void - { - $this->customFields[$type][$field] = $customField; - } - - /** - * Return custom field. - * - * If no custom field is set, return null - * - * @param string $type - * @param string $field - * - * @return mixed - */ - public function getCustomField(string $type, string $field): ?string - { - if (!isset($this->customFields[$type][$field])) { - return null; - } - - return $this->customFields[$type][$field]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location.php deleted file mode 100644 index 1cb902dc10..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -/** - * This is the base for Location SortClause classes, used to set sorting of Location queries. - */ -abstract class Location extends SortClause -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Depth.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Depth.php deleted file mode 100644 index ae6e3adc66..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Depth.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use eZ\Publish\SPI\Repository\Values\Trash\Query\SortClause as TrashSortClause; - -/** - * Sets sort direction on the Location depth for a Location query. - */ -class Depth extends Location implements FilteringSortClause, TrashSortClause -{ - /** - * Constructs a new LocationDepth SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('location_depth', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Id.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Id.php deleted file mode 100644 index 6823c4dd1f..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Id.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; - -/** - * Sets sort direction on Location id for a Location query. - * - * Especially useful to get reproducible search results in tests. - */ -class Id extends Location implements FilteringSortClause -{ - /** - * Constructs a new LocationId SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('location_id', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/IsMainLocation.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/IsMainLocation.php deleted file mode 100644 index 5ea2cd19ea..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/IsMainLocation.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; - -/** - * Sets sort direction on the Location main status for a Location query. - */ -class IsMainLocation extends Location -{ - /** - * Constructs a new Location IsMainLocation SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('location_is_main', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Path.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Path.php deleted file mode 100644 index ed75393e12..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Path.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use eZ\Publish\SPI\Repository\Values\Trash\Query\SortClause as TrashSortClause; - -/** - * Sets sort direction on the Location path for a Location query. - */ -class Path extends Location implements FilteringSortClause, TrashSortClause -{ - /** - * Constructs a new LocationPath SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('location_path', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Priority.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Priority.php deleted file mode 100644 index e2e88b891b..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Priority.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use eZ\Publish\SPI\Repository\Values\Trash\Query\SortClause as TrashSortClause; - -/** - * Sets sort direction on the Location priority for a Location query. - */ -class Priority extends Location implements FilteringSortClause, TrashSortClause -{ - /** - * Constructs a new LocationPriority SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('location_priority', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Visibility.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Visibility.php deleted file mode 100644 index 2415499cf2..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Location/Visibility.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; - -/** - * Sets sort direction on the Location visibility for a Location query. - */ -class Visibility extends Location implements FilteringSortClause -{ - /** - * Constructs a new Location Visibility SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('location_visibility', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/MapLocationDistance.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/MapLocationDistance.php deleted file mode 100644 index da021dd31f..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/MapLocationDistance.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\CustomFieldInterface; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target\MapLocationTarget; - -/** - * Sets sort direction on the MapLocation distance for a content query. - */ -class MapLocationDistance extends SortClause implements CustomFieldInterface -{ - /** - * Custom fields to sort by instead of the default field. - * - * @var array - */ - protected $customFields = []; - - /** - * Constructs a new MapLocationDistance SortClause on Type $typeIdentifier and Field $fieldIdentifier. - * - * @param string $typeIdentifier ContentType identifier - * @param string $fieldIdentifier FieldDefinition identifier - * @param float $latitude Latitude of the location that distance is calculated from - * @param float $longitude Longitude of the location that distance is calculated from - * @param string $sortDirection - */ - public function __construct( - string $typeIdentifier, - string $fieldIdentifier, - float $latitude, - float $longitude, - string $sortDirection = Query::SORT_ASC - ) { - parent::__construct( - 'maplocation_distance', - $sortDirection, - new MapLocationTarget( - $latitude, - $longitude, - $typeIdentifier, - $fieldIdentifier - ) - ); - } - - /** - * Set a custom field to sort by. - * - * Set a custom field to sort by for a defined field in a defined type. - * - * @param string $type - * @param string $field - * @param string $customField - */ - public function setCustomField(string $type, string $field, string $customField): void - { - $this->customFields[$type][$field] = $customField; - } - - /** - * Return custom field. - * - * If no custom field is set, return null - * - * @param string $type - * @param string $field - * - * @return mixed - */ - public function getCustomField(string $type, string $field): ?string - { - if (!isset($this->customFields[$type]) || - !isset($this->customFields[$type][$field])) { - return null; - } - - return $this->customFields[$type][$field]; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Random.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Random.php deleted file mode 100644 index 964b4904cc..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Random.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target\RandomTarget; - -/** - * Sets sort random on a content query. - */ -class Random extends SortClause -{ - /** - * @param int|null $seed as this depends on storage implementation. - */ - public function __construct(?int $seed = null, string $sortDirection = Query::SORT_ASC) - { - parent::__construct('random', $sortDirection, new RandomTarget($seed)); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Score.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Score.php deleted file mode 100644 index de3d8af2ec..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Score.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -/** - * Sorts search results by relevance score. - */ -class Score extends SortClause -{ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('score', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/SectionIdentifier.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/SectionIdentifier.php deleted file mode 100644 index 1107411452..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/SectionIdentifier.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; - -/** - * Sets sort direction on Section identifier for a content query. - */ -class SectionIdentifier extends SortClause implements FilteringSortClause -{ - /** - * Constructs a new SectionIdentifier SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('section_identifier', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/SectionName.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/SectionName.php deleted file mode 100644 index cf6bd1f65f..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/SectionName.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use eZ\Publish\SPI\Repository\Values\Trash\Query\SortClause as TrashSortClause; - -/** - * Sets sort direction on Section name for a content query. - */ -class SectionName extends SortClause implements FilteringSortClause, TrashSortClause -{ - /** - * Constructs a new SectionName SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('section_name', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target.php deleted file mode 100644 index 743667d935..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -/** - * Struct that stores extra target informations for a SortClause object. - */ -abstract class Target -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target/CustomFieldTarget.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target/CustomFieldTarget.php deleted file mode 100644 index 4be3ab2665..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target/CustomFieldTarget.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target; - -final class CustomFieldTarget extends Target -{ - /** @var string */ - public $fieldName; - - public function __construct(string $fieldName) - { - $this->fieldName = $fieldName; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target/RandomTarget.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target/RandomTarget.php deleted file mode 100644 index ab80946905..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target/RandomTarget.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target; - -/** - * Struct that stores extra target informations for a RandomTarget object. - */ -class RandomTarget extends Target -{ - /** - * @var int|null - * - * For storage which does not support seed in this type, - * it should be normalized to proper value inside storage implementation. - */ - public $seed; - - public function __construct(?int $seed) - { - $this->seed = $seed; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Trash/ContentTypeName.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Trash/ContentTypeName.php deleted file mode 100644 index 6fa5faff08..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Trash/ContentTypeName.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause\Trash; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\SPI\Repository\Values\Trash\Query\SortClause as TrashSortClause; - -class ContentTypeName extends SortClause implements TrashSortClause -{ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('content_type_name', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Trash/DateTrashed.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Trash/DateTrashed.php deleted file mode 100644 index 9b65862d3a..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Trash/DateTrashed.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause\Trash; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\SPI\Repository\Values\Trash\Query\SortClause as TrashSortClause; - -class DateTrashed extends SortClause implements TrashSortClause -{ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('trashed', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Trash/UserLogin.php b/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Trash/UserLogin.php deleted file mode 100644 index 83fe0ca6f4..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Trash/UserLogin.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause\Trash; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\SPI\Repository\Values\Trash\Query\SortClause as TrashSortClause; - -class UserLogin extends SortClause implements TrashSortClause -{ - public function __construct(string $sortDirection = Query::SORT_ASC) - { - parent::__construct('user_name', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Relation.php b/eZ/Publish/API/Repository/Values/Content/Relation.php deleted file mode 100644 index 8c90b37432..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Relation.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * Class representing a relation between content. - * - * @property-read mixed $id the internal id of the relation - * @property-read string $sourceFieldDefinitionIdentifier the field definition identifier of the field where this relation is anchored if the relation is of type EMBED, LINK, or ATTRIBUTE - * @property-read \eZ\Publish\API\Repository\Values\Content\ContentInfo $sourceContentInfo - calls {@link getSourceContentInfo()} - * @property-read \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContentInfo - calls {@link getDestinationContentInfo()} - * @property-read int $type The relation type bitmask containing one or more of Relation::COMMON, Relation::EMBED, Relation::LINK, Relation::FIELD - */ -abstract class Relation extends ValueObject -{ - /** - * The relation type COMMON is a general relation between object set by a user. - * - * @var int - */ - public const COMMON = 1; - - /** - * the relation type EMBED is set for a relation which is anchored as embedded link in an attribute value. - * - * @var int - */ - public const EMBED = 2; - - /** - * the relation type LINK is set for a relation which is anchored as link in an attribute value. - * - * @var int - */ - public const LINK = 4; - - /** - * the relation type FIELD is set for a relation which is part of an relation attribute value. - * - * @var int - */ - public const FIELD = 8; - - /** - * the relation type ASSET is set for a relation to asset in an attribute value. - * - * @var int - */ - public const ASSET = 16; - - /** - * Id of the relation. - * - * @var mixed - */ - protected $id; - - /** - * Source Content Type Field Definition Id. - * For relation not of type RelationType::COMMON this field denotes the field definition id - * of the attribute where the relation is anchored. - * - * @var string - */ - protected $sourceFieldDefinitionIdentifier; - - /** - * the content of the source content of the relation. - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - abstract public function getSourceContentInfo(): ContentInfo; - - /** - * the content of the destination content of the relation. - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - abstract public function getDestinationContentInfo(): ContentInfo; - - /** - * The relation type bitmask. - * - * @see Relation::COMMON, Relation::EMBED, Relation::LINK, Relation::FIELD, Relation::ASSET - * - * @var int - */ - protected $type; -} diff --git a/eZ/Publish/API/Repository/Values/Content/RelationList.php b/eZ/Publish/API/Repository/Values/Content/RelationList.php deleted file mode 100644 index 1e97f5e655..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/RelationList.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use ArrayIterator; -use eZ\Publish\API\Repository\Values\ValueObject; -use IteratorAggregate; -use Traversable; - -/** - * List of relations. - */ -class RelationList extends ValueObject implements IteratorAggregate -{ - /** - * @var int - */ - public $totalCount = 0; - - /** - * @var \eZ\Publish\API\Repository\Values\Content\RelationList\RelationListItemInterface[] - */ - public $items = []; - - public function getIterator(): Traversable - { - return new ArrayIterator($this->items); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/RelationList/Item/RelationListItem.php b/eZ/Publish/API/Repository/Values/Content/RelationList/Item/RelationListItem.php deleted file mode 100644 index 545a57ead1..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/RelationList/Item/RelationListItem.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\RelationList\Item; - -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\Content\RelationList\RelationListItemInterface; - -/** - * Item of relation list. - */ -class RelationListItem implements RelationListItemInterface -{ - /** @var \eZ\Publish\API\Repository\Values\Content\Relation */ - private $relation; - - public function __construct(Relation $relation) - { - $this->relation = $relation; - } - - public function getRelation(): ?Relation - { - return $this->relation; - } - - public function hasRelation(): bool - { - return true; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/RelationList/Item/UnauthorizedRelationListItem.php b/eZ/Publish/API/Repository/Values/Content/RelationList/Item/UnauthorizedRelationListItem.php deleted file mode 100644 index 7558f01b1c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/RelationList/Item/UnauthorizedRelationListItem.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\RelationList\Item; - -use eZ\Publish\API\Repository\Lists\UnauthorizedListItem; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\Content\RelationList\RelationListItemInterface; - -/** - * Item of relation list. - */ -final class UnauthorizedRelationListItem extends UnauthorizedListItem implements RelationListItemInterface -{ - public function getRelation(): ?Relation - { - return null; - } - - public function hasRelation(): bool - { - return false; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/RelationList/RelationListItemInterface.php b/eZ/Publish/API/Repository/Values/Content/RelationList/RelationListItemInterface.php deleted file mode 100644 index a735b7294c..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/RelationList/RelationListItemInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\RelationList; - -use eZ\Publish\API\Repository\Values\Content\Relation; - -interface RelationListItemInterface -{ - /** - * @return \eZ\Publish\API\Repository\Values\Content\Relation|null - */ - public function getRelation(): ?Relation; - - /** - * @return bool - */ - public function hasRelation(): bool; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult.php b/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult.php deleted file mode 100644 index b355bc209a..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search; - -use eZ\Publish\API\Repository\Values\ValueObject; - -abstract class AggregationResult extends ValueObject -{ - /** - * The name of the aggregation. - * - * @var string - */ - private $name; - - public function __construct(string $name) - { - parent::__construct(); - - $this->name = $name; - } - - public function getName(): string - { - return $this->name; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/RangeAggregationResult.php b/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/RangeAggregationResult.php deleted file mode 100644 index e8c7908a7a..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/RangeAggregationResult.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search\AggregationResult; - -use ArrayIterator; -use Countable; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Range; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult; -use Iterator; -use IteratorAggregate; - -final class RangeAggregationResult extends AggregationResult implements IteratorAggregate, Countable -{ - /** @var \eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry[] */ - private $entries; - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry[] $entries - */ - public function __construct(string $name, iterable $entries = []) - { - parent::__construct($name); - - $this->entries = $entries; - } - - public function isEmpty(): bool - { - return empty($this->entries); - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry[] - */ - public function getEntries(): iterable - { - return $this->entries; - } - - public function getEntry(Range $key): ?RangeAggregationResultEntry - { - foreach ($this->entries as $entry) { - if ($entry->getKey() == $key) { - return $entry; - } - } - - return null; - } - - public function hasEntry(Range $key): bool - { - return $this->getEntry($key) !== null; - } - - public function count(): int - { - return count($this->entries); - } - - public function getIterator(): Iterator - { - if (empty($this->entries)) { - return new ArrayIterator(); - } - - foreach ($this->entries as $entry) { - yield $entry->getKey() => $entry->getCount(); - } - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/RangeAggregationResultEntry.php b/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/RangeAggregationResultEntry.php deleted file mode 100644 index 4a111c39cb..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/RangeAggregationResultEntry.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search\AggregationResult; - -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Range; -use eZ\Publish\API\Repository\Values\ValueObject; - -final class RangeAggregationResultEntry extends ValueObject -{ - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Range */ - private $key; - - /** @var int */ - private $count; - - public function __construct(Range $key, int $count) - { - parent::__construct(); - - $this->key = $key; - $this->count = $count; - } - - public function getKey(): Range - { - return $this->key; - } - - public function getCount(): int - { - return $this->count; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/TermAggregationResult.php b/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/TermAggregationResult.php deleted file mode 100644 index e497b60e3d..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/TermAggregationResult.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search\AggregationResult; - -use ArrayIterator; -use Countable; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult; -use Iterator; -use IteratorAggregate; - -class TermAggregationResult extends AggregationResult implements IteratorAggregate, Countable -{ - /** @var \eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry[] */ - private $entries; - - public function __construct(string $name, iterable $entries = []) - { - parent::__construct($name); - - $this->entries = $entries; - } - - public function isEmpty(): bool - { - return empty($this->entries); - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry[] - */ - public function getEntries(): iterable - { - return $this->entries; - } - - /** - * @param object|string|int $key - */ - public function getEntry($key): ?TermAggregationResultEntry - { - foreach ($this->entries as $entry) { - if ($entry->getKey() == $key) { - return $entry; - } - } - - return null; - } - - /** - * @param object|string|int $key - */ - public function hasEntry($key): bool - { - return $this->getEntry($key) !== null; - } - - public function count(): int - { - return count($this->entries); - } - - public function getIterator(): Iterator - { - if (empty($this->entries)) { - return new ArrayIterator(); - } - - foreach ($this->entries as $entry) { - yield $entry->getKey() => $entry->getCount(); - } - } - - public static function createForAggregation(Aggregation $aggregation, iterable $entries = []): self - { - return new self($aggregation->getName(), $entries); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/TermAggregationResultEntry.php b/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/TermAggregationResultEntry.php deleted file mode 100644 index 5f91f41665..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/TermAggregationResultEntry.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search\AggregationResult; - -use eZ\Publish\API\Repository\Values\ValueObject; - -final class TermAggregationResultEntry extends ValueObject -{ - /** @var mixed */ - private $key; - - /** @var int */ - private $count; - - public function __construct($key, int $count) - { - parent::__construct(); - - $this->key = $key; - $this->count = $count; - } - - /** - * @return mixed - */ - public function getKey() - { - return $this->key; - } - - public function getCount(): int - { - return $this->count; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/Facet.php b/eZ/Publish/API/Repository/Values/Content/Search/Facet.php deleted file mode 100644 index 030b0c20e3..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/Facet.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * Base class for facets. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -abstract class Facet extends ValueObject -{ - /** - * The name of the facet. - * - * @var string - */ - public $name; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/Facet/ContentTypeFacet.php b/eZ/Publish/API/Repository/Values/Content/Search/Facet/ContentTypeFacet.php deleted file mode 100644 index 8fb1405592..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/Facet/ContentTypeFacet.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search\Facet; - -use eZ\Publish\API\Repository\Values\Content\Search\Facet; - -/** - * This class holds counts of content with content type. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class ContentTypeFacet extends Facet -{ - /** - * An array with contentTypeId as key and count of matching content objects as value. - * - * @var array - */ - public $entries; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/Facet/CriterionFacet.php b/eZ/Publish/API/Repository/Values/Content/Search/Facet/CriterionFacet.php deleted file mode 100644 index 330de498f6..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/Facet/CriterionFacet.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search\Facet; - -use eZ\Publish\API\Repository\Values\Content\Search\Facet; - -/** - * This class holds the count of content matching the criterion. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class CriterionFacet extends Facet -{ - /** - * The count of objects matching the criterion. - * - * @var int - */ - public $count; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/Facet/DateRangeFacet.php b/eZ/Publish/API/Repository/Values/Content/Search/Facet/DateRangeFacet.php deleted file mode 100644 index 1526d05647..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/Facet/DateRangeFacet.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search\Facet; - -use eZ\Publish\API\Repository\Values\Content\Search\Facet; - -/** - * This class represents a date range facet holding counts for content in the built date ranges. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class DateRangeFacet extends Facet -{ - /** - * The date intervals with statistical data. - * - * @var \eZ\Publish\API\Repository\Values\Content\Search\Facet\RangeFacetEntry - */ - public $entries; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/Facet/FieldFacet.php b/eZ/Publish/API/Repository/Values/Content/Search/Facet/FieldFacet.php deleted file mode 100644 index 7067b4cf43..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/Facet/FieldFacet.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search\Facet; - -use eZ\Publish\API\Repository\Values\Content\Search\Facet; - -/** - * This class represents a field facet which holds the collected terms for one or more fields. - * - * @author christianbacher - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class FieldFacet extends Facet -{ - /** - * Number of documents not containing any terms in the queried fields. - * - * @var int - */ - public $missingCount; - - /** - * The number of terms which are not in the queried top list. - * - * @var int - */ - public $otherCount; - - /** - * The total count of terms found. - * - * @var int - */ - public $totalCount; - - /** - * An array of terms (key) and counts (value). - * - * @var array - */ - public $entries; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/Facet/FieldRangeFacet.php b/eZ/Publish/API/Repository/Values/Content/Search/Facet/FieldRangeFacet.php deleted file mode 100644 index 79e0a74cd1..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/Facet/FieldRangeFacet.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search\Facet; - -use eZ\Publish\API\Repository\Values\Content\Search\Facet; - -/** - * This class represents a field range facet. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class FieldRangeFacet extends Facet -{ - /** - * Number of documents not containing any terms in the queried fields. - * - * @var int - */ - public $missingCount; - - /** - * The number of terms which are not in the queried top list. - * - * @var int - */ - public $otherCount; - - /** - * The total count of terms found. - * - * @var int - */ - public $totalCount; - - /** - * For each interval there is an entry with statistical data. - * - * @var \eZ\Publish\API\Repository\Values\Content\Search\Facet\RangeFacetEntry[] - */ - public $entries; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/Facet/LocationFacet.php b/eZ/Publish/API/Repository/Values/Content/Search/Facet/LocationFacet.php deleted file mode 100644 index 3a6118e5dd..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/Facet/LocationFacet.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search\Facet; - -use eZ\Publish\API\Repository\Values\Content\Search\Facet; - -/** - * Facet containing counts for content below child locations. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class LocationFacet extends Facet -{ - /** - * An array with location id as key and count of matching content objects which are below this location as value. - * - * @var array - */ - public $entries; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/Facet/SectionFacet.php b/eZ/Publish/API/Repository/Values/Content/Search/Facet/SectionFacet.php deleted file mode 100644 index f83fe1e0b8..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/Facet/SectionFacet.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search\Facet; - -use eZ\Publish\API\Repository\Values\Content\Search\Facet; - -/** - * this class hold counts for content in sections. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class SectionFacet extends Facet -{ - /** - * An array with sectionId as key and count of matching content objects as value. - * - * @var array - */ - public $entries; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/Facet/TermFacet.php b/eZ/Publish/API/Repository/Values/Content/Search/Facet/TermFacet.php deleted file mode 100644 index 90e77a17e9..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/Facet/TermFacet.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search\Facet; - -use eZ\Publish\API\Repository\Values\Content\Search\Facet; - -/** - * this class hold counts for content in sections. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class TermFacet extends Facet -{ - /** - * An array with term as key and count of matching content objects as value. - * - * @var array - */ - public $entries; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/Facet/UserFacet.php b/eZ/Publish/API/Repository/Values/Content/Search/Facet/UserFacet.php deleted file mode 100644 index d27f61bc15..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/Facet/UserFacet.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search\Facet; - -use eZ\Publish\API\Repository\Values\Content\Search\Facet; - -/** - * This class holds counts for content owned, created or modified by users. - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ -class UserFacet extends Facet -{ - /** - * An array with user id as key and count of matching content objects as value. - * - * @var array - */ - public $entries; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Search/SearchResult.php b/eZ/Publish/API/Repository/Values/Content/Search/SearchResult.php deleted file mode 100644 index 8e6a1c3362..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Search/SearchResult.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Search; - -use ArrayIterator; -use eZ\Publish\API\Repository\Values\ValueObject; -use Iterator; -use IteratorAggregate; - -/** - * This class represents a search result. - */ -class SearchResult extends ValueObject implements IteratorAggregate -{ - /** - * The facets for this search. - * - * @var \eZ\Publish\API\Repository\Values\Content\Search\Facet[] - * - * @deprecated since eZ Platform 3.2.0, to be removed in eZ Platform 4.0.0. - */ - public $facets = []; - - /** - * @var \eZ\Publish\API\Repository\Values\Content\Search\AggregationResultCollection - */ - public $aggregations; - - /** - * The value objects found for the query. - * - * @var \eZ\Publish\API\Repository\Values\Content\Search\SearchHit[] - */ - public $searchHits = []; - - /** - * If spellcheck is on this field contains a collated query suggestion where in the appropriate - * criterions the wrong spelled value is replaced by a corrected one (TBD). - * - * @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion - */ - public $spellSuggestion; - - /** - * The duration of the search processing in ms. - * - * @var int - */ - public $time; - - /** - * Indicates if the search has timed out. - * - * @var bool - */ - public $timedOut; - - /** - * The maximum score of this query. - * - * @var float - */ - public $maxScore; - - /** - * The total number of searchHits. - * - * `null` if Query->performCount was set to false and search engine avoids search lookup. - * - * @var int|null - */ - public $totalCount; - - public function __construct(array $properties = []) - { - if (!isset($properties['aggregations'])) { - $properties['aggregations'] = new AggregationResultCollection(); - } - - parent::__construct($properties); - } - - public function getIterator(): Iterator - { - return new ArrayIterator($this->searchHits); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Section.php b/eZ/Publish/API/Repository/Values/Content/Section.php deleted file mode 100644 index 3fdbfd7ddf..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Section.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class represents a section. - * - * @property-read mixed $id the id of the section - * @property-read string $identifier the identifier of the section - * @property-read string $name human readable name of the section - */ -class Section extends ValueObject -{ - /** - * Id of the section. - * - * @var mixed - */ - protected $id; - - /** - * Unique identifier of the section. - * - * @var string - */ - protected $identifier; - - /** - * Name of the section. - * - * @var string - */ - protected $name; -} diff --git a/eZ/Publish/API/Repository/Values/Content/SectionCreateStruct.php b/eZ/Publish/API/Repository/Values/Content/SectionCreateStruct.php deleted file mode 100644 index a67caf23f8..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/SectionCreateStruct.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -/** - * This class represents a section. - * $identifier and $name are required. - */ -class SectionCreateStruct extends SectionStruct -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/SectionStruct.php b/eZ/Publish/API/Repository/Values/Content/SectionStruct.php deleted file mode 100644 index e35975b833..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/SectionStruct.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\ValueObject; - -abstract class SectionStruct extends ValueObject -{ - /** - * If set the Unique identifier of the section is changes. - * - * Needs to be a unique Section->identifier string value. - * - * @var string - */ - public $identifier; - - /** - * If set the name of the section is changed. - * - * @var string - */ - public $name; -} diff --git a/eZ/Publish/API/Repository/Values/Content/SectionUpdateStruct.php b/eZ/Publish/API/Repository/Values/Content/SectionUpdateStruct.php deleted file mode 100644 index 66b4a0e9d8..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/SectionUpdateStruct.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -/** - * This class is used to provide data for updating a section. At least one property has to set. - */ -class SectionUpdateStruct extends SectionStruct -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/Trash/SearchResult.php b/eZ/Publish/API/Repository/Values/Content/Trash/SearchResult.php deleted file mode 100644 index ea6b66993b..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Trash/SearchResult.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Trash; - -use ArrayIterator; -use eZ\Publish\API\Repository\Values\ValueObject; -use Traversable; - -class SearchResult extends ValueObject implements \IteratorAggregate -{ - public function __construct(array $properties = []) - { - if (isset($properties['totalCount'])) { - $this->count = $properties['totalCount']; - } - - parent::__construct($properties); - } - - /** - * The total number of Trash items. - * - * @var int - */ - public $totalCount = 0; - - /** - * The total number of Trash items. - * - * @deprecated Property is here purely for BC with 5.x/6.x. - * - * @var int - */ - public $count = 0; - - /** - * The Trash items found for the query. - * - * @var \eZ\Publish\API\Repository\Values\Content\TrashItem[] - */ - public $items = []; - - public function getIterator(): Traversable - { - return new ArrayIterator($this->items); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/Trash/TrashItemDeleteResult.php b/eZ/Publish/API/Repository/Values/Content/Trash/TrashItemDeleteResult.php deleted file mode 100644 index 96d95ab596..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Trash/TrashItemDeleteResult.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Trash; - -use eZ\Publish\API\Repository\Values\ValueObject; - -class TrashItemDeleteResult extends ValueObject -{ - /** @var int */ - public $trashItemId; - - /** @var int */ - public $contentId; - - /** - * Flag indicating content was removed. - * - * @var bool - */ - public $contentRemoved = false; - - /** @var array<int> */ - public $reverseRelationContentIds = []; -} diff --git a/eZ/Publish/API/Repository/Values/Content/Trash/TrashItemDeleteResultList.php b/eZ/Publish/API/Repository/Values/Content/Trash/TrashItemDeleteResultList.php deleted file mode 100644 index b9035ee99a..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/Trash/TrashItemDeleteResultList.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content\Trash; - -use ArrayIterator; -use eZ\Publish\API\Repository\Values\ValueObject; -use Traversable; - -class TrashItemDeleteResultList extends ValueObject implements \IteratorAggregate -{ - /** @var \eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult[] */ - public $items = []; - - /** - * @return \ArrayIterator - */ - public function getIterator(): Traversable - { - return new ArrayIterator($this->items); - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/TrashItem.php b/eZ/Publish/API/Repository/Values/Content/TrashItem.php deleted file mode 100644 index 6f0545d23d..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/TrashItem.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -/** - * this class represents a trash item, which is actually a trashed location. - */ -abstract class TrashItem extends Location -{ - /** - * Trashed timestamp. - * - * @var \DateTime - */ - protected $trashed; - - /** @var array<int, int> */ - protected $removedLocationContentIdMap = []; - - /** - * @return array<int, int> - */ - public function getRemovedLocationContentIdMap(): array - { - return $this->removedLocationContentIdMap; - } -} diff --git a/eZ/Publish/API/Repository/Values/Content/URLWildcardStruct.php b/eZ/Publish/API/Repository/Values/Content/URLWildcardStruct.php deleted file mode 100644 index 707e6b9ac9..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/URLWildcardStruct.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\ValueObject; - -class URLWildcardStruct extends ValueObject -{ - /** @var string */ - public $destinationUrl; - - /** @var string */ - public $sourceUrl; - - /** @var bool */ - public $forward; -} diff --git a/eZ/Publish/API/Repository/Values/Content/URLWildcardTranslationResult.php b/eZ/Publish/API/Repository/Values/Content/URLWildcardTranslationResult.php deleted file mode 100644 index 308cc4922f..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/URLWildcardTranslationResult.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class represents a result of a translated url wildcard which is not an URLAlias. - * - * @property-read string $uri The found resource uri - * @property-read bool $forward indicates if the url is redirected or not - */ -class URLWildcardTranslationResult extends ValueObject -{ - /** - * The found resource uri. - * - * @var string - */ - protected $uri; - - /** - * Indicates if the url is redirected or not. - * - * @var bool - */ - protected $forward; -} diff --git a/eZ/Publish/API/Repository/Values/Content/URLWildcardUpdateStruct.php b/eZ/Publish/API/Repository/Values/Content/URLWildcardUpdateStruct.php deleted file mode 100644 index 4590adf9b9..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/URLWildcardUpdateStruct.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -final class URLWildcardUpdateStruct extends URLWildcardStruct -{ -} diff --git a/eZ/Publish/API/Repository/Values/Content/VersionInfo.php b/eZ/Publish/API/Repository/Values/Content/VersionInfo.php deleted file mode 100644 index c4da9dc8c1..0000000000 --- a/eZ/Publish/API/Repository/Values/Content/VersionInfo.php +++ /dev/null @@ -1,139 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\SPI\Repository\Values\MultiLanguageName; - -/** - * This class holds version information data. It also contains the corresponding {@link Content} to - * which the version belongs to. - * - * @property-read \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo calls getContentInfo() - * @property-read mixed $id the internal id of the version - * @property-read int $versionNo the version number of this version (which only increments in scope of a single Content object) - * @property-read \DateTime $modificationDate the last modified date of this version - * @property-read \DateTime $creationDate the creation date of this version - * @property-read mixed $creatorId the user id of the user which created this version - * @property-read int $status the status of this version. One of VersionInfo::STATUS_DRAFT, VersionInfo::STATUS_PUBLISHED, VersionInfo::STATUS_ARCHIVED - * @property-read string $initialLanguageCode the language code of the version. This value is used to flag a version as a translation to specific language - * @property-read string[] $languageCodes a collection of all languages which exist in this version. - */ -abstract class VersionInfo extends ValueObject implements MultiLanguageName -{ - public const STATUS_DRAFT = 0; - public const STATUS_PUBLISHED = 1; - public const STATUS_ARCHIVED = 3; - - /** - * Version ID. - * - * @var mixed - */ - protected $id; - - /** - * Version number. - * - * In contrast to {@link $id}, this is the version number, which only - * increments in scope of a single Content object. - * - * @var int - */ - protected $versionNo; - - /** - * the last modified date of this version. - * - * @var \DateTime - */ - protected $modificationDate; - - /** - * Creator user ID. - * - * Creator of the version, in the search API this is referred to as the modifier of the published content. - * - * @var mixed - */ - protected $creatorId; - - /** @var \DateTime */ - protected $creationDate; - - /** - * One of VersionInfo::STATUS_DRAFT, VersionInfo::STATUS_PUBLISHED, VersionInfo::STATUS_ARCHIVED. - * - * @var int Constant. - */ - protected $status; - - /** - * In 4.x this is the language code which is used for labeling a translation. - * - * @var string - */ - protected $initialLanguageCode; - - /** - * List of languages in this version. - * - * Reflects which languages fields exists in for this version. - * - * @var string[] - */ - protected $languageCodes = []; - - /** - * Content of the content this version belongs to. - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - abstract public function getContentInfo(): ContentInfo; - - abstract public function getCreator(): User; - - abstract public function getInitialLanguage(): Language; - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Language[] - */ - abstract public function getLanguages(): iterable; - - /** - * Returns true if version is a draft. - * - * @return bool - */ - public function isDraft(): bool - { - return $this->status === self::STATUS_DRAFT; - } - - /** - * Returns true if version is published. - * - * @return bool - */ - public function isPublished(): bool - { - return $this->status === self::STATUS_PUBLISHED; - } - - /** - * Returns true if version is archived. - * - * @return bool - */ - public function isArchived(): bool - { - return $this->status === self::STATUS_ARCHIVED; - } -} diff --git a/eZ/Publish/API/Repository/Values/ContentType/ContentType.php b/eZ/Publish/API/Repository/Values/ContentType/ContentType.php deleted file mode 100644 index 865f0aa0ea..0000000000 --- a/eZ/Publish/API/Repository/Values/ContentType/ContentType.php +++ /dev/null @@ -1,236 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\ContentType; - -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\SPI\Repository\Values\MultiLanguageDescription; -use eZ\Publish\SPI\Repository\Values\MultiLanguageName; - -/** - * this class represents a content type value. - * - * @property-read \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup[] $contentTypeGroups calls getContentTypeGroups - * @property-read \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCollection $fieldDefinitions calls getFieldDefinitions() or on access getFieldDefinition($fieldDefIdentifier) - * @property-read mixed $id the id of the content type - * @property-read int $status the status of the content type. One of ContentType::STATUS_DEFINED|ContentType::STATUS_DRAFT|ContentType::STATUS_MODIFIED - * @property-read string $identifier the identifier of the content type - * @property-read \DateTime $creationDate the date of the creation of this content type - * @property-read \DateTime $modificationDate the date of the last modification of this content type - * @property-read mixed $creatorId the user id of the creator of this content type - * @property-read mixed $modifierId the user id of the user which has last modified this content type - * @property-read string $remoteId a global unique id of the content object - * @property-read string $urlAliasSchema URL alias schema. If nothing is provided, $nameSchema will be used instead. - * @property-read string $nameSchema The name schema. - * @property-read bool $isContainer @deprecated use strict getter {@see ContentType::isContainer} instead. - * @property-read string $mainLanguageCode the main language of the content type names and description used for fallback. - * @property-read bool $defaultAlwaysAvailable if an instance of a content type is created the always available flag is set by default this this value. - * @property-read string[] $languageCodes array of language codes used by content type translations. - * @property-read int $defaultSortField Specifies which property the child locations should be sorted on by default when created. Valid values are found at {@link Location::SORT_FIELD_*} - * @property-read int $defaultSortOrder Specifies whether the sort order should be ascending or descending by default when created. Valid values are {@link Location::SORT_ORDER_*} - */ -abstract class ContentType extends ValueObject implements MultiLanguageName, MultiLanguageDescription -{ - /** @var int Status constant for defined (aka "published") Type */ - public const STATUS_DEFINED = 0; - - /** @var int Status constant for draft (aka "temporary") Type */ - public const STATUS_DRAFT = 1; - - /** @var int Status constant for modified (aka "deferred for publishing") Type */ - public const STATUS_MODIFIED = 2; - - /** - * Content type ID. - * - * @var mixed - */ - protected $id; - - /** - * The status of the content type. - * - * @var int One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - */ - protected $status; - - /** - * String identifier of a content type. - * - * @var string - */ - protected $identifier; - - /** - * Creation date of the content type. - * - * @var \DateTime - */ - protected $creationDate; - - /** - * Modification date of the content type. - * - * @var \DateTime - */ - protected $modificationDate; - - /** - * Creator user id of the content type. - * - * @var mixed - */ - protected $creatorId; - - /** - * Modifier user id of the content type. - * - * @var mixed - */ - protected $modifierId; - - /** - * Unique remote ID of the content type. - * - * @var string - */ - protected $remoteId; - - /** - * URL alias schema. - * - * If nothing is provided, $nameSchema will be used instead. - * - * @var string - */ - protected $urlAliasSchema; - - /** - * Name schema. - * - * Can be composed of FieldDefinition identifier place holders. - * These place holders must comply this pattern : <field_definition_identifier>. - * An OR condition can be used : - * <field_def|other_field_def> - * In this example, field_def will be used if available. If not, other_field_def will be used for content name generation - * - * @var string - */ - protected $nameSchema; - - /** - * A flag used to hint if content of this type may have children or not. It is highly recommended to respect this flag and not create/move content below non-containers. - * But this flag is not considered as part of the content model and the API will not in any way enforce this flag to be respected. - * - * @var bool - */ - protected $isContainer; - - /** - * If an instance of a content type is created the always available flag is set - * by default to this value. - * - * @var bool - */ - protected $defaultAlwaysAvailable = true; - - /** - * Specifies which property the child locations should be sorted on by default when created. - * - * Valid values are found at {@link Location::SORT_FIELD_*} - * - * @var int - */ - protected $defaultSortField; - - /** - * Specifies whether the sort order should be ascending or descending by default when created. - * - * Valid values are {@link Location::SORT_ORDER_*} - * - * @var int - */ - protected $defaultSortOrder; - - /** - * List of language codes used by translations. - * - * @var string[] - */ - protected $languageCodes; - - /** - * This method returns the content type groups this content type is assigned to. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup[] - */ - abstract public function getContentTypeGroups(); - - /** - * This method returns the content type field definitions from this type. - */ - abstract public function getFieldDefinitions(): FieldDefinitionCollection; - - /** - * This method returns the field definition for the given identifier. - * - * @param string $fieldDefinitionIdentifier - * - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition|null - */ - public function getFieldDefinition($fieldDefinitionIdentifier): ?FieldDefinition - { - if ($this->hasFieldDefinition($fieldDefinitionIdentifier)) { - return $this->getFieldDefinitions()->get($fieldDefinitionIdentifier); - } - - return null; - } - - /** - * This method returns true if the field definition for the given identifier exists. - */ - public function hasFieldDefinition(string $fieldDefinitionIdentifier): bool - { - return $this->getFieldDefinitions()->has($fieldDefinitionIdentifier); - } - - /** - * Returns true if field definition with given field type identifier exists. - */ - public function hasFieldDefinitionOfType(string $fieldTypeIdentifier): bool - { - return $this->getFieldDefinitions()->anyOfType($fieldTypeIdentifier); - } - - /** - * Returns collection of the field definition for the given field type identifier. - */ - public function getFieldDefinitionsOfType(string $fieldTypeIdentifier): FieldDefinitionCollection - { - return $this->getFieldDefinitions()->filterByType($fieldTypeIdentifier); - } - - /** - * Returns true if field definition with given field type identifier or null. - */ - public function getFirstFieldDefinitionOfType(string $fieldTypeIdentifier): ?FieldDefinition - { - $fieldDefinitionsOfType = $this->getFieldDefinitionsOfType($fieldTypeIdentifier); - if (!$fieldDefinitionsOfType->isEmpty()) { - return $fieldDefinitionsOfType->first(); - } - - return null; - } - - public function isContainer(): bool - { - return $this->isContainer; - } -} diff --git a/eZ/Publish/API/Repository/Values/ContentType/ContentTypeCreateStruct.php b/eZ/Publish/API/Repository/Values/ContentType/ContentTypeCreateStruct.php deleted file mode 100644 index 00697899dc..0000000000 --- a/eZ/Publish/API/Repository/Values/ContentType/ContentTypeCreateStruct.php +++ /dev/null @@ -1,129 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\ContentType; - -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class is used for creating content types. - * - * @property \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct[] $fieldDefinitions the collection of field definitions - */ -abstract class ContentTypeCreateStruct extends ValueObject -{ - /** - * String unique identifier of a type. - * - * Required. - * - * @var string - */ - public $identifier; - - /** - * Main language Code. - * - * Required. - * - * @var string - */ - public $mainLanguageCode; - - /** - * The remote id. - * - * @var string - */ - public $remoteId; - - /** - * URL alias schema. - * - * @var string - */ - public $urlAliasSchema; - - /** - * Name schema. - * - * @var string - */ - public $nameSchema; - - /** - * Determines if the type is a container. - * - * @var bool - */ - public $isContainer = false; - - /** - * Specifies which property the child locations should be sorted on by default when created. - * - * Valid values are found at {@link Location::SORT_FIELD_*} - * - * @var mixed - */ - public $defaultSortField = Location::SORT_FIELD_PUBLISHED; - - /** - * Specifies whether the sort order should be ascending or descending by default when created. - * - * Valid values are {@link Location::SORT_ORDER_*} - * - * @var mixed - */ - public $defaultSortOrder = Location::SORT_ORDER_DESC; - - /** - * If an instance of a content type is created the always available flag is set - * by default this this value. - * - * @var bool - */ - public $defaultAlwaysAvailable = true; - - /** - * An array of names with languageCode keys. - * - * Required. - at least one name in the main language is required - * - * @var array an array of string - */ - public $names; - - /** - * An array of descriptions with languageCode keys. - * - * @var array an array of string - */ - public $descriptions; - - /** - * Adds a new field definition. - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct $fieldDef - */ - abstract public function addFieldDefinition(FieldDefinitionCreateStruct $fieldDef): void; - - /** - * If set this value overrides the current user as creator. - * - * @var mixed - */ - public $creatorId = null; - - /** - * If set this value overrides the current time for creation. - * - * @var \DateTime - */ - public $creationDate = null; -} diff --git a/eZ/Publish/API/Repository/Values/ContentType/ContentTypeDraft.php b/eZ/Publish/API/Repository/Values/ContentType/ContentTypeDraft.php deleted file mode 100644 index 559ecabb68..0000000000 --- a/eZ/Publish/API/Repository/Values/ContentType/ContentTypeDraft.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\ContentType; - -/** - * This class represents a draft of a content type. - */ -abstract class ContentTypeDraft extends ContentType -{ -} diff --git a/eZ/Publish/API/Repository/Values/ContentType/ContentTypeGroup.php b/eZ/Publish/API/Repository/Values/ContentType/ContentTypeGroup.php deleted file mode 100644 index 5a2538e120..0000000000 --- a/eZ/Publish/API/Repository/Values/ContentType/ContentTypeGroup.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\ContentType; - -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\SPI\Repository\Values\MultiLanguageDescription; -use eZ\Publish\SPI\Repository\Values\MultiLanguageName; - -/** - * This class represents a content type group value. - * - * @property-read mixed $id the id of the content type group - * @property-read string $identifier the identifier of the content type group - * @property-read \DateTime $creationDate the date of the creation of this content type group - * @property-read \DateTime $modificationDate the date of the last modification of this content type group - * @property-read mixed $creatorId the user id of the creator of this content type group - * @property-read mixed $modifierId the user id of the user which has last modified this content type group - */ -abstract class ContentTypeGroup extends ValueObject implements MultiLanguageName, MultiLanguageDescription -{ - /** - * Primary key. - * - * @var mixed - */ - protected $id; - - /** - * Readable string identifier of a group. - * - * @var string - */ - protected $identifier; - - /** - * Created date (timestamp). - * - * @var \DateTime - */ - protected $creationDate; - - /** - * Modified date (timestamp). - * - * @var \DateTime - */ - protected $modificationDate; - - /** - * Creator user id. - * - * @var mixed - */ - protected $creatorId; - - /** - * Modifier user id. - * - * @var mixed - */ - protected $modifierId; -} diff --git a/eZ/Publish/API/Repository/Values/ContentType/ContentTypeGroupStruct.php b/eZ/Publish/API/Repository/Values/ContentType/ContentTypeGroupStruct.php deleted file mode 100644 index 54529ff614..0000000000 --- a/eZ/Publish/API/Repository/Values/ContentType/ContentTypeGroupStruct.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\ContentType; - -use eZ\Publish\API\Repository\Values\ValueObject; - -abstract class ContentTypeGroupStruct extends ValueObject -{ - /** - * Readable and unique string identifier of a group. - * - * @var string - */ - public $identifier; -} diff --git a/eZ/Publish/API/Repository/Values/ContentType/FieldDefinition.php b/eZ/Publish/API/Repository/Values/ContentType/FieldDefinition.php deleted file mode 100644 index 0c3acd723e..0000000000 --- a/eZ/Publish/API/Repository/Values/ContentType/FieldDefinition.php +++ /dev/null @@ -1,132 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\ContentType; - -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\SPI\Repository\Values\MultiLanguageDescription; -use eZ\Publish\SPI\Repository\Values\MultiLanguageName; - -/** - * This class represents a field definition. - * - * @property-read array $fieldSettings calls getFieldSettings() - * @property-read array $validatorConfiguration calls getValidatorConfiguration() - * @property-read mixed $id the id of the field definition - * @property-read string $identifier the identifier of the field definition - * @property-read string $fieldGroup the field group name - * @property-read int $position the position of the field definition in the content type - * @property-read string $fieldTypeIdentifier String identifier of the field type - * @property-read bool $isTranslatable indicates if fields of this definition are translatable - * @property-read bool $isRequired indicates if this field is required in the content object - * @property-read bool $isSearchable indicates if the field is searchable - * @property-read bool $isThumbnail indicates if the field can be thumbnail - * @property-read bool $isInfoCollector indicates if this field is used for information collection - * @property-read mixed $defaultValue the default value of the field - * @property-read string $mainLanguageCode main Translation (language code) of a multilingual Field Definition - */ -abstract class FieldDefinition extends ValueObject implements MultiLanguageName, MultiLanguageDescription -{ - /** - * the unique id of this field definition. - * - * @var mixed - */ - protected $id; - - /** - * Readable string identifier of a field definition. - * - * @var string - */ - protected $identifier; - - /** - * Field group name. - * - * @var string - */ - protected $fieldGroup; - - /** - * the position of the field definition in the content type. - * - * @var int - */ - protected $position; - - /** - * String identifier of the field type. - * - * @var string - */ - protected $fieldTypeIdentifier; - - /** - * If the field is translatable. - * - * @var bool - */ - protected $isTranslatable; - - /** - * Indicates if the field can be a thumbnail. - * - * @var bool - */ - protected $isThumbnail; - - /** - * Is the field required. - * - * @var bool - */ - protected $isRequired; - - /** - * the flag if this field is used for information collection. - * - * @var bool - */ - protected $isInfoCollector; - - /** - * This method returns the validator configuration of this field definition supported by the field type. - * - * @return array - */ - abstract public function getValidatorConfiguration(): array; - - /** - * This method returns settings for the field definition supported by the field type. - * - * @return array - */ - abstract public function getFieldSettings(): array; - - /** - * Default value of the field. - * - * @var mixed - */ - protected $defaultValue; - - /** - * Indicates if th the content is searchable by this attribute. - * - * @var bool - */ - protected $isSearchable; - - /** - * Based on mainLanguageCode of contentType. - * - * @var string - */ - protected $mainLanguageCode; -} diff --git a/eZ/Publish/API/Repository/Values/ContentType/FieldDefinitionCollection.php b/eZ/Publish/API/Repository/Values/ContentType/FieldDefinitionCollection.php deleted file mode 100644 index 0bbd97194a..0000000000 --- a/eZ/Publish/API/Repository/Values/ContentType/FieldDefinitionCollection.php +++ /dev/null @@ -1,110 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\ContentType; - -use ArrayAccess; -use Closure; -use Countable; -use IteratorAggregate; - -interface FieldDefinitionCollection extends Countable, IteratorAggregate, ArrayAccess -{ - /** - * This method returns the field definition for the given identifier. - * - * @throws \eZ\Publish\API\Repository\Exceptions\OutOfBoundsException - */ - public function get(string $fieldDefinitionIdentifier): FieldDefinition; - - /** - * This method returns true if the field definition for the given identifier exists. - */ - public function has(string $fieldDefinitionIdentifier): bool; - - /** - * Return first element of collection. - * - * @throws \eZ\Publish\API\Repository\Exceptions\OutOfBoundsException - */ - public function first(): FieldDefinition; - - /** - * Return last element of collection. - * - * @throws \eZ\Publish\API\Repository\Exceptions\OutOfBoundsException - */ - public function last(): FieldDefinition; - - /** - * Checks whether the collection is empty (contains no elements). - * - * @return bool TRUE if the collection is empty, FALSE otherwise. - */ - public function isEmpty(): bool; - - /** - * Returns all the elements of this collection that satisfy the predicate p. - * The order of the elements is preserved. - */ - public function filter(Closure $predicate): FieldDefinitionCollection; - - /** - * Returns field definitions with given field type identifier. - */ - public function filterByType(string $fieldTypeIdentifier): FieldDefinitionCollection; - - /** - * Returns field definitions with given group. - */ - public function filterByGroup(string $fieldGroup): FieldDefinitionCollection; - - /** - * Applies the given function to each element in the collection and returns - * a new collection with the elements returned by the function. - */ - public function map(Closure $predicate): array; - - /** - * Tests whether the given predicate holds for all elements of this collection. - */ - public function all(Closure $predicate): bool; - - /** - * Tests for the existence of an element that satisfies the given predicate. - */ - public function any(Closure $predicate): bool; - - /** - * Tests for the existence of an field definition with given field type identifier. - */ - public function anyOfType(string $fieldTypeIdentifier): bool; - - /** - * Tests for the existence of an field definition in given field group. - */ - public function anyInGroup(string $fieldGroup): bool; - - /** - * Partitions this collection in two collections according to a predicate. - * - * Result is an array with two elements. The first element contains the collection - * of elements where the predicate returned TRUE, the second element - * contains the collection of elements where the predicate returned FALSE. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCollection[] - */ - public function partition(Closure $predicate): array; - - /** - * Gets a native PHP array representation of the collection. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] - */ - public function toArray(): array; -} diff --git a/eZ/Publish/API/Repository/Values/Notification/CreateStruct.php b/eZ/Publish/API/Repository/Values/Notification/CreateStruct.php deleted file mode 100644 index 7ba1ba719f..0000000000 --- a/eZ/Publish/API/Repository/Values/Notification/CreateStruct.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Notification; - -use eZ\Publish\API\Repository\Values\ValueObject; - -class CreateStruct extends ValueObject -{ - /** @var int */ - public $ownerId; - - /** @var string */ - public $type; - - /** @var bool */ - public $isPending = true; - - /** @var array */ - public $data = []; -} diff --git a/eZ/Publish/API/Repository/Values/Notification/Notification.php b/eZ/Publish/API/Repository/Values/Notification/Notification.php deleted file mode 100644 index 0d9dddb04e..0000000000 --- a/eZ/Publish/API/Repository/Values/Notification/Notification.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Notification; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class represents a notification value. - * - * @property-read int $id The ID of notification - * @property-read int $ownerId The ID of notification owner - * @property-read bool $isPending True if notification is unreaded - * @property-read string $type Notification type - * @property-read \DateTimeInterface $created Creation date. - * @property-read array $data Optional context data - */ -class Notification extends ValueObject -{ - /** @var int */ - protected $id; - - /** @var int */ - protected $ownerId; - - /** @var bool */ - protected $isPending; - - /** @var string */ - protected $type; - - /** @var \DateTimeInterface */ - protected $created; - - /** @var array */ - protected $data = []; -} diff --git a/eZ/Publish/API/Repository/Values/Notification/NotificationList.php b/eZ/Publish/API/Repository/Values/Notification/NotificationList.php deleted file mode 100644 index 32bfa523d5..0000000000 --- a/eZ/Publish/API/Repository/Values/Notification/NotificationList.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Notification; - -use ArrayIterator; -use eZ\Publish\API\Repository\Values\ValueObject; -use IteratorAggregate; -use Traversable; - -class NotificationList extends ValueObject implements IteratorAggregate -{ - /** @var int */ - public $totalCount = 0; - - /** @var \eZ\Publish\API\Repository\Values\Notification\Notification[] */ - public $items = []; - - /** - * {@inheritdoc} - */ - public function getIterator(): Traversable - { - return new ArrayIterator($this->items); - } -} diff --git a/eZ/Publish/API/Repository/Values/ObjectState/ObjectState.php b/eZ/Publish/API/Repository/Values/ObjectState/ObjectState.php deleted file mode 100644 index 0faebbf026..0000000000 --- a/eZ/Publish/API/Repository/Values/ObjectState/ObjectState.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\ObjectState; - -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\SPI\Repository\Values\MultiLanguageDescription; -use eZ\Publish\SPI\Repository\Values\MultiLanguageName; - -/** - * This class represents a object state value. - * - * @property-read mixed $id the id of the content type group - * @property-read string $identifier the identifier of the content type group - * @property-read int $priority the priority in the group ordering - * @property-read string $mainLanguageCode the default language of the object state names and descriptions used for fallback. - * @property-read string[] $languageCodes the available languages - */ -abstract class ObjectState extends ValueObject implements MultiLanguageName, MultiLanguageDescription -{ - /** - * Primary key. - * - * @var mixed - */ - protected $id; - - /** - * Readable string identifier of the object state. - * - * @var string - */ - protected $identifier; - - /** - * Priority for ordering. - * - * @var int - */ - protected $priority; - - /** - * The available language codes for names an descriptions. - * - * @var string[] - */ - protected $languageCodes; - - /** - * The object state group this object state belongs to. - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup - */ - abstract public function getObjectStateGroup(): ObjectStateGroup; -} diff --git a/eZ/Publish/API/Repository/Values/ObjectState/ObjectStateGroup.php b/eZ/Publish/API/Repository/Values/ObjectState/ObjectStateGroup.php deleted file mode 100644 index a5c43f8d96..0000000000 --- a/eZ/Publish/API/Repository/Values/ObjectState/ObjectStateGroup.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\ObjectState; - -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\SPI\Repository\Values\MultiLanguageDescription; -use eZ\Publish\SPI\Repository\Values\MultiLanguageName; - -/** - * This class represents an object state group value. - * - * @property-read mixed $id the id of the content type group - * @property-read string $identifier the identifier of the content type group - * @property-read string $mainLanguageCode the default language of the object state group names and description used for fallback. - * @property-read string $defaultLanguageCode the default language code. - * @property-read string[] $languageCodes the available languages - */ -abstract class ObjectStateGroup extends ValueObject implements MultiLanguageName, MultiLanguageDescription -{ - /** - * Primary key. - * - * @var mixed - */ - protected $id; - - /** - * Readable string identifier of a group. - * - * @var string - */ - protected $identifier; - - /** - * The default language code. - * - * @var string - */ - protected $defaultLanguageCode; - - /** - * The available language codes for names an descriptions. - * - * @var string[] - */ - protected $languageCodes; -} diff --git a/eZ/Publish/API/Repository/Values/Setting/Setting.php b/eZ/Publish/API/Repository/Values/Setting/Setting.php deleted file mode 100644 index 627f47b1f4..0000000000 --- a/eZ/Publish/API/Repository/Values/Setting/Setting.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Setting; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * @property-read string $group - * @property-read string $identifier - * @property-read mixed $value - */ -class Setting extends ValueObject -{ - /** @var string */ - protected $group; - - /** @var string */ - protected $identifier; - - /** @var mixed */ - protected $value; -} diff --git a/eZ/Publish/API/Repository/Values/Setting/SettingUpdateStruct.php b/eZ/Publish/API/Repository/Values/Setting/SettingUpdateStruct.php deleted file mode 100644 index af77e5222a..0000000000 --- a/eZ/Publish/API/Repository/Values/Setting/SettingUpdateStruct.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\Setting; - -class SettingUpdateStruct extends Setting -{ - /** - * @param mixed $value - */ - public function setValue($value): void - { - $this->value = $value; - } -} diff --git a/eZ/Publish/API/Repository/Values/Translation.php b/eZ/Publish/API/Repository/Values/Translation.php deleted file mode 100644 index 1dc32eb19e..0000000000 --- a/eZ/Publish/API/Repository/Values/Translation.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values; - -/** - * Base class fro translation messages. - * - * Use its extensions: Translation\Singular, Translation\Plural. - */ -abstract class Translation extends ValueObject -{ -} diff --git a/eZ/Publish/API/Repository/Values/Translation/Message.php b/eZ/Publish/API/Repository/Values/Translation/Message.php deleted file mode 100644 index 42a67e4b5c..0000000000 --- a/eZ/Publish/API/Repository/Values/Translation/Message.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Values\Translation; - -use eZ\Publish\API\Repository\Values\Translation; - -/** - * Class for translatable messages, which only occur in singular form. - * - * The message might include replacements, in the form %[A-Za-z]%. Those are - * replaced by the values provided. A raw % can be escaped like %%. - */ -class Message extends Translation -{ - /** - * Message string. Might use replacements like %foo%, which are replaced by - * the values specified in the values array. - * - * @var string - */ - protected $message; - - /** - * Translation value objects. May not contain any numbers, which might - * result in requiring plural forms. Use Plural for that. - * - * @var array - */ - protected $values; - - /** - * Construct singular only message from string and optional value array. - * - * @param string $message - * @param array $values - */ - public function __construct($message, array $values = []) - { - $this->message = $message; - $this->values = $values; - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return strtr($this->message, $this->values); - } -} diff --git a/eZ/Publish/API/Repository/Values/Translation/Plural.php b/eZ/Publish/API/Repository/Values/Translation/Plural.php deleted file mode 100644 index f3c7f6b67b..0000000000 --- a/eZ/Publish/API/Repository/Values/Translation/Plural.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Values\Translation; - -use eZ\Publish\API\Repository\Values\Translation; - -/** - * Class for translatable messages, which may contain plural forms. - * - * The message might include replacements, in the form %[A-Za-z]%. Those are - * replaced by the values provided. A raw % can be escaped like %%. - * - * You need to provide a singular and plural variant for the string. The - * strings provided should be english and will be translated depending on the - * environment language. - * - * This interface follows the interfaces of XLiff, gettext, Symfony2 - * Translations and Zend_Translate. For singular forms you just provide a plain - * string (with optional placeholders without effects on the plural forms). For - * potential plural forms you always provide a singular variant and an english - * simple plural variant. No implementation supports multiple different plural - * forms in one single message. - * - * The singular / plural string could, for Symfony2, for example be converted - * to "$singular|$plural", and you would call gettext like: ngettext( - * $singular, $plural, $count ). - */ -class Plural extends Translation -{ - /** - * Singular string. Might use replacements like %foo%, which are replaced by - * the values specified in the values array. - * - * @var string - */ - protected $singular; - - /** - * Message string. Might use replacements like %foo%, which are replaced by - * the values specified in the values array. - * - * @var string - */ - protected $plural; - - /** - * Translation value objects. May not contain any numbers, which might - * result in requiring plural forms. Use MessagePlural for that. - * - * @var array - */ - protected $values; - - /** - * Construct plural message from singular, plural and value array. - * - * @param string $singular - * @param string $plural - * @param array $values - */ - public function __construct($singular, $plural, array $values) - { - $this->singular = $singular; - $this->plural = $plural; - $this->values = $values; - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return strtr(current($this->values) == 1 ? $this->plural : $this->singular, $this->values); - } -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/Criterion.php b/eZ/Publish/API/Repository/Values/URL/Query/Criterion.php deleted file mode 100644 index 968535e497..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/Criterion.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query; - -abstract class Criterion -{ -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/LogicalAnd.php b/eZ/Publish/API/Repository/Values/URL/Query/Criterion/LogicalAnd.php deleted file mode 100644 index c9a469c264..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/LogicalAnd.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -class LogicalAnd extends LogicalOperator -{ -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/LogicalNot.php b/eZ/Publish/API/Repository/Values/URL/Query/Criterion/LogicalNot.php deleted file mode 100644 index 6426a15483..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/LogicalNot.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -class LogicalNot extends LogicalOperator -{ - /** - * Creates a new NOT logic criterion. - * - * Will match of the given criterion doesn't match - * - * @param \eZ\Publish\API\Repository\Values\URL\Query\Criterion $criterion criterion - * - * @throws \InvalidArgumentException if more than one criterion is given in the array parameter - */ - public function __construct(Criterion $criterion) - { - parent::__construct([$criterion]); - } -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/LogicalOperator.php b/eZ/Publish/API/Repository/Values/URL/Query/Criterion/LogicalOperator.php deleted file mode 100644 index 17faa70f8a..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/LogicalOperator.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException; - -abstract class LogicalOperator extends Criterion -{ - /** - * The set of criteria combined by the logical operator. - * - * @var \eZ\Publish\API\Repository\Values\URL\Query\Criterion[] - */ - public $criteria = []; - - /** - * Creates a Logic operation with the given criteria. - * - * @param \eZ\Publish\API\Repository\Values\URL\Query\Criterion[] $criteria - * - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException - */ - public function __construct(array $criteria) - { - foreach ($criteria as $key => $criterion) { - if (!$criterion instanceof Criterion) { - throw new InvalidCriterionArgumentException($key, $criterion, Criterion::class); - } - - $this->criteria[] = $criterion; - } - } -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/LogicalOr.php b/eZ/Publish/API/Repository/Values/URL/Query/Criterion/LogicalOr.php deleted file mode 100644 index 8ab2978586..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/LogicalOr.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -class LogicalOr extends LogicalOperator -{ -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/MatchAll.php b/eZ/Publish/API/Repository/Values/URL/Query/Criterion/MatchAll.php deleted file mode 100644 index ac1f415f29..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/MatchAll.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -class MatchAll extends Matcher -{ -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/MatchNone.php b/eZ/Publish/API/Repository/Values/URL/Query/Criterion/MatchNone.php deleted file mode 100644 index aea309db9d..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/MatchNone.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -class MatchNone extends Matcher -{ -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/Matcher.php b/eZ/Publish/API/Repository/Values/URL/Query/Criterion/Matcher.php deleted file mode 100644 index 139a9bc154..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/Matcher.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -abstract class Matcher extends Criterion -{ -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/Pattern.php b/eZ/Publish/API/Repository/Values/URL/Query/Criterion/Pattern.php deleted file mode 100644 index ff1bb965b5..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/Pattern.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -/** - * Matches URLs which contains the pattern. - */ -class Pattern extends Matcher -{ - /** - * String which needs to part of URL e.g. ez.no. - * - * @var string - */ - public $pattern; - - /** - * Pattern constructor. - * - * @param string $pattern - */ - public function __construct(string $pattern) - { - if ($pattern === '') { - throw new \InvalidArgumentException('URL pattern cannot be empty.'); - } - - $this->pattern = $pattern; - } -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/SectionId.php b/eZ/Publish/API/Repository/Values/URL/Query/Criterion/SectionId.php deleted file mode 100644 index 5341b4fdcc..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/SectionId.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -/** - * Matches URLs which used by content placed in specified section ids. - */ -class SectionId extends Matcher -{ - /** - * IDs of related content section. - * - * @var int[] - */ - public $sectionIds; - - /** - * @param int[] $sectionIds - */ - public function __construct(array $sectionIds) - { - $this->sectionIds = $sectionIds; - } -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/SectionIdentifier.php b/eZ/Publish/API/Repository/Values/URL/Query/Criterion/SectionIdentifier.php deleted file mode 100644 index b4cd2f37ff..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/SectionIdentifier.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -/** - * Matches URLs which used by content placed in specified section identifiers. - */ -class SectionIdentifier extends Matcher -{ - /** - * Identifiers of related content section. - * - * @var string[] - */ - public $sectionIdentifiers; - - /** - * @param string[] $sectionIdentifiers - */ - public function __construct(array $sectionIdentifiers) - { - $this->sectionIdentifiers = $sectionIdentifiers; - } -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/Validity.php b/eZ/Publish/API/Repository/Values/URL/Query/Criterion/Validity.php deleted file mode 100644 index 81e2e73098..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/Validity.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -/** - * Matches URLs based on validity flag. - */ -class Validity extends Matcher -{ - /** - * If true the matcher will selects only valid URLs. - * - * @var bool - */ - public $isValid; - - /** - * Validity constructor. - * - * @param bool $isValid - */ - public function __construct(bool $isValid) - { - $this->isValid = $isValid; - } -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/VisibleOnly.php b/eZ/Publish/API/Repository/Values/URL/Query/Criterion/VisibleOnly.php deleted file mode 100644 index f5cb5d409d..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/Criterion/VisibleOnly.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -/** - * Matches URLs which are used in published content. - */ -class VisibleOnly extends Matcher -{ -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/SortClause.php b/eZ/Publish/API/Repository/Values/URL/Query/SortClause.php deleted file mode 100644 index f03d631f18..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/SortClause.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query; - -use InvalidArgumentException; - -/** - * This class is the base for SortClause classes, used to set sorting of URL queries. - */ -abstract class SortClause -{ - public const SORT_ASC = 'ascending'; - public const SORT_DESC = 'descending'; - - /** - * Sort direction. - * - * @var string - */ - public $direction = self::SORT_ASC; - - /** - * Sort target. - * - * @var string - */ - public $target; - - /** - * Constructs a new SortClause on $sortTarget in direction $sortDirection. - * - * @param string $sortTarget - * @param string $sortDirection one of SortClause::SORT_ASC or SortClause::SORT_DESC - * - * @throws \InvalidArgumentException if the given sort order isn't one of SortClause::SORT_ASC or SortClause::SORT_DESC - */ - public function __construct(string $sortTarget, string $sortDirection) - { - if ($sortDirection !== self::SORT_ASC && $sortDirection !== self::SORT_DESC) { - throw new InvalidArgumentException('Sort direction must be either SortClause::SORT_ASC or SortClause::SORT_DESC'); - } - - $this->direction = $sortDirection; - $this->target = $sortTarget; - } -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/SortClause/Id.php b/eZ/Publish/API/Repository/Values/URL/Query/SortClause/Id.php deleted file mode 100644 index edff94e4d9..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/SortClause/Id.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\SortClause; - -use eZ\Publish\API\Repository\Values\URL\Query\SortClause; - -class Id extends SortClause -{ - /** - * Constructs a new Id SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = self::SORT_ASC) - { - parent::__construct('id', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/URL/Query/SortClause/URL.php b/eZ/Publish/API/Repository/Values/URL/Query/SortClause/URL.php deleted file mode 100644 index 6d7dd607b8..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/Query/SortClause/URL.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL\Query\SortClause; - -use eZ\Publish\API\Repository\Values\URL\Query\SortClause; - -class URL extends SortClause -{ - /** - * Constructs a new URL SortClause. - * - * @param string $sortDirection - */ - public function __construct(string $sortDirection = self::SORT_ASC) - { - parent::__construct('url', $sortDirection); - } -} diff --git a/eZ/Publish/API/Repository/Values/URL/SearchResult.php b/eZ/Publish/API/Repository/Values/URL/SearchResult.php deleted file mode 100644 index b8dedf4db0..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/SearchResult.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL; - -use ArrayIterator; -use eZ\Publish\API\Repository\Values\ValueObject; -use Traversable; - -class SearchResult extends ValueObject implements \IteratorAggregate -{ - /** - * The total number of URLs. - * - * @var int|null - */ - public $totalCount = 0; - - /** - * The value objects found for the query. - * - * @var \eZ\Publish\API\Repository\Values\URL\URL[] - */ - public $items = []; - - /** - * {@inheritdoc} - */ - public function getIterator(): Traversable - { - return new ArrayIterator($this->items); - } -} diff --git a/eZ/Publish/API/Repository/Values/URL/URL.php b/eZ/Publish/API/Repository/Values/URL/URL.php deleted file mode 100644 index 7ea996af82..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/URL.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL; - -use eZ\Publish\API\Repository\Values\ValueObject; - -class URL extends ValueObject -{ - /** - * The unique id of the URL. - * - * @var int - */ - protected $id; - - /** - * URL itself e.g. "http://ez.no". - * - * @var string - */ - protected $url; - - /** - * Is URL valid ? - * - * @var bool - */ - protected $isValid; - - /** - * Date of last check. - * - * @var \DateTimeInterface - */ - protected $lastChecked; - - /** - * Creation date. - * - * @var \DateTimeInterface - */ - protected $created; - - /** - * Modified date. - * - * @var \DateTimeInterface - */ - protected $modified; -} diff --git a/eZ/Publish/API/Repository/Values/URL/URLUpdateStruct.php b/eZ/Publish/API/Repository/Values/URL/URLUpdateStruct.php deleted file mode 100644 index 2886cb0f66..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/URLUpdateStruct.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * Structure used to update URL data. - */ -class URLUpdateStruct extends ValueObject -{ - /** - * URL itself e.g. "http://ez.no". - * - * @var string|null - */ - public $url; - - /** - * Is URL valid ? - * - * @var bool|null - */ - public $isValid; - - /** - * Modified date. - * - * @var \DateTimeInterface|null - */ - public $lastChecked; -} diff --git a/eZ/Publish/API/Repository/Values/URL/UsageSearchResult.php b/eZ/Publish/API/Repository/Values/URL/UsageSearchResult.php deleted file mode 100644 index 71c76cf3ce..0000000000 --- a/eZ/Publish/API/Repository/Values/URL/UsageSearchResult.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\URL; - -use ArrayIterator; -use eZ\Publish\API\Repository\Values\ValueObject; -use Traversable; - -class UsageSearchResult extends ValueObject implements \IteratorAggregate -{ - /** - * The total number of content objects using URL. - * - * @var int - */ - public $totalCount = 0; - - /** - * The value objects found for the query. - * - * @var \eZ\Publish\API\Repository\Values\Content\ContentInfo[] - */ - public $items = []; - - public function getIterator(): Traversable - { - return new ArrayIterator($this->items); - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/ContentTypeLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/ContentTypeLimitation.php deleted file mode 100644 index 33db5b8490..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/ContentTypeLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class ContentTypeLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::CONTENTTYPE; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/LanguageLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/LanguageLimitation.php deleted file mode 100644 index 0afeb2b431..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/LanguageLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class LanguageLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::LANGUAGE; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/LocationLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/LocationLimitation.php deleted file mode 100644 index a0f2b28e0b..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/LocationLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class LocationLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::LOCATION; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/NewObjectStateLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/NewObjectStateLimitation.php deleted file mode 100644 index 36e2f58f01..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/NewObjectStateLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class NewObjectStateLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::NEWSTATE; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/NewSectionLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/NewSectionLimitation.php deleted file mode 100644 index 304db58f41..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/NewSectionLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class NewSectionLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::NEWSECTION; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/ObjectStateLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/ObjectStateLimitation.php deleted file mode 100644 index 5449450feb..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/ObjectStateLimitation.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -/** - * Class ObjectStateLimitation. - * - * This Object state serves as API limitation for "StateGroup" from legacy, - * StateGroup stored a combination of StateGroup identifier as well as State id(s) while this one - * only cares about the state id's. - */ -class ObjectStateLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::STATE; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/OwnerLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/OwnerLimitation.php deleted file mode 100644 index f7a66b5f06..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/OwnerLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class OwnerLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::OWNER; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/ParentContentTypeLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/ParentContentTypeLimitation.php deleted file mode 100644 index 09fe175e2c..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/ParentContentTypeLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class ParentContentTypeLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::PARENTCONTENTTYPE; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/ParentDepthLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/ParentDepthLimitation.php deleted file mode 100644 index 90d0a498a0..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/ParentDepthLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class ParentDepthLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::PARENTDEPTH; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/ParentOwnerLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/ParentOwnerLimitation.php deleted file mode 100644 index bad13d7c68..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/ParentOwnerLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class ParentOwnerLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::PARENTOWNER; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/ParentUserGroupLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/ParentUserGroupLimitation.php deleted file mode 100644 index ab329762fa..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/ParentUserGroupLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class ParentUserGroupLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::PARENTUSERGROUP; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/RoleLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/RoleLimitation.php deleted file mode 100644 index eaec32a48f..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/RoleLimitation.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -abstract class RoleLimitation extends Limitation -{ -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/SectionLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/SectionLimitation.php deleted file mode 100644 index e6325ea2e8..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/SectionLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class SectionLimitation extends RoleLimitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::SECTION; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/SiteAccessLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/SiteAccessLimitation.php deleted file mode 100644 index 3b4ea8846b..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/SiteAccessLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class SiteAccessLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::SITEACCESS; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/StatusLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/StatusLimitation.php deleted file mode 100644 index e1d332f548..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/StatusLimitation.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -/** - * Status Limitation is used to limit the access to Content based on its version status. - */ -class StatusLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::STATUS; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/SubtreeLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/SubtreeLimitation.php deleted file mode 100644 index 89ad8f0b6c..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/SubtreeLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class SubtreeLimitation extends RoleLimitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::SUBTREE; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/UserGroupLimitation.php b/eZ/Publish/API/Repository/Values/User/Limitation/UserGroupLimitation.php deleted file mode 100644 index bf18cd0b11..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Limitation/UserGroupLimitation.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; - -class UserGroupLimitation extends Limitation -{ - /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() - * - * @return string - */ - public function getIdentifier(): string - { - return Limitation::USERGROUP; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/LookupLimitationResult.php b/eZ/Publish/API/Repository/Values/User/LookupLimitationResult.php deleted file mode 100644 index 5b9c879b6b..0000000000 --- a/eZ/Publish/API/Repository/Values/User/LookupLimitationResult.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class represents a LookupLimitation for module and function in the context of current User. - */ -final class LookupLimitationResult extends ValueObject -{ - /** @var bool */ - protected $hasAccess; - - /** @var \eZ\Publish\API\Repository\Values\User\Limitation[] */ - protected $roleLimitations; - - /** @var \eZ\Publish\API\Repository\Values\User\LookupPolicyLimitations[] */ - protected $lookupPolicyLimitations; - - /** - * @param bool $hasAccess - * @param \eZ\Publish\API\Repository\Values\User\Limitation[] $roleLimitations - * @param \eZ\Publish\API\Repository\Values\User\LookupPolicyLimitations[] $lookupPolicyLimitations - */ - public function __construct( - bool $hasAccess, - array $roleLimitations = [], - array $lookupPolicyLimitations = [] - ) { - parent::__construct(); - - $this->hasAccess = $hasAccess; - $this->lookupPolicyLimitations = $lookupPolicyLimitations; - $this->roleLimitations = $roleLimitations; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/LookupPolicyLimitations.php b/eZ/Publish/API/Repository/Values/User/LookupPolicyLimitations.php deleted file mode 100644 index ff62bf53d2..0000000000 --- a/eZ/Publish/API/Repository/Values/User/LookupPolicyLimitations.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class represents a result of lookup limitation for module and function in the context of current User. - */ -final class LookupPolicyLimitations extends ValueObject -{ - /** @var \eZ\Publish\API\Repository\Values\User\Policy */ - protected $policy; - - /** @var \eZ\Publish\API\Repository\Values\User\Limitation[] */ - protected $limitations; - - /** - * @param \eZ\Publish\API\Repository\Values\User\Policy $policy - * @param \eZ\Publish\API\Repository\Values\User\Limitation[] $limitations - */ - public function __construct(Policy $policy, array $limitations = []) - { - parent::__construct(); - - $this->policy = $policy; - $this->limitations = $limitations; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/PasswordValidationContext.php b/eZ/Publish/API/Repository/Values/User/PasswordValidationContext.php deleted file mode 100644 index 241a64471e..0000000000 --- a/eZ/Publish/API/Repository/Values/User/PasswordValidationContext.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * Context of the password validation. - * - * @property-read \eZ\Publish\API\Repository\Values\ContentType\ContentType|null $contentType - * @property-read \eZ\Publish\API\Repository\Values\User\User|null $user - */ -class PasswordValidationContext extends ValueObject -{ - /** - * Content type of the password owner. - * - * @var \eZ\Publish\API\Repository\Values\ContentType\ContentType|null - */ - protected $contentType; - - /** - * Owner of the password. - * - * @var \eZ\Publish\API\Repository\Values\User\User|null - */ - protected $user; -} diff --git a/eZ/Publish/API/Repository/Values/User/Policy.php b/eZ/Publish/API/Repository/Values/User/Policy.php deleted file mode 100644 index d337e274e1..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Policy.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class represents a policy value. - * - * @property-read mixed $id internal id of the policy - * @property-read mixed $roleId the role id this policy belongs to - * @property-read string $module Name of module, associated with the Policy - * @property-read string $function Name of the module function Or all functions with '*' - * @property-read \eZ\Publish\API\Repository\Values\User\Limitation[] $limitations an array of \eZ\Publish\API\Repository\Values\User\Limitation - */ -abstract class Policy extends ValueObject -{ - /** - * ID of the policy. - * - * @var int - */ - protected $id; - - /** - * the ID of the role this policy belongs to. - * - * @var int - */ - protected $roleId; - - /** - * Name of module, associated with the Policy. - * - * Eg: content - * - * @var string - */ - protected $module; - - /** - * Name of the module function Or all functions with '*'. - * - * Eg: read - * - * @var string - */ - protected $function; - - /** - * @return \eZ\Publish\API\Repository\Values\User\Limitation[] - */ - abstract public function getLimitations(): iterable; -} diff --git a/eZ/Publish/API/Repository/Values/User/PolicyCreateStruct.php b/eZ/Publish/API/Repository/Values/User/PolicyCreateStruct.php deleted file mode 100644 index 7c15d69940..0000000000 --- a/eZ/Publish/API/Repository/Values/User/PolicyCreateStruct.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -/** - * This class is used to create a policy. - */ -abstract class PolicyCreateStruct extends PolicyStruct -{ - /** - * Name of module, associated with the Policy. - * - * Eg: content - * - * @var string - */ - public $module; - - /** - * Name of the module function Or all functions with '*'. - * - * Eg: read - * - * @var string - */ - public $function; -} diff --git a/eZ/Publish/API/Repository/Values/User/PolicyDraft.php b/eZ/Publish/API/Repository/Values/User/PolicyDraft.php deleted file mode 100644 index 4ebf311537..0000000000 --- a/eZ/Publish/API/Repository/Values/User/PolicyDraft.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -/** - * @property-read mixed $originalId Original policy ID the policy was created from. - */ -abstract class PolicyDraft extends Policy -{ - /** - * Original policy ID the policy was created from. - * Used when role status is Role::STATUS_DRAFT. - * - * @var int - */ - protected $originalId; -} diff --git a/eZ/Publish/API/Repository/Values/User/PolicyStruct.php b/eZ/Publish/API/Repository/Values/User/PolicyStruct.php deleted file mode 100644 index 63a3f47f85..0000000000 --- a/eZ/Publish/API/Repository/Values/User/PolicyStruct.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\ValueObject; - -abstract class PolicyStruct extends ValueObject -{ - /** - * Returns list of limitations added to policy. - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation[] - */ - abstract public function getLimitations(): iterable; - - /** - * Adds a limitation with the given identifier and list of values. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - */ - abstract public function addLimitation(Limitation $limitation): void; -} diff --git a/eZ/Publish/API/Repository/Values/User/PolicyUpdateStruct.php b/eZ/Publish/API/Repository/Values/User/PolicyUpdateStruct.php deleted file mode 100644 index 6821ef0936..0000000000 --- a/eZ/Publish/API/Repository/Values/User/PolicyUpdateStruct.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -/** - * This class is used for updating a policy. The limitations of the policy are replaced - * with those which are added in instances of this class. - */ -abstract class PolicyUpdateStruct extends PolicyStruct -{ -} diff --git a/eZ/Publish/API/Repository/Values/User/Role.php b/eZ/Publish/API/Repository/Values/User/Role.php deleted file mode 100644 index d7aada5ba0..0000000000 --- a/eZ/Publish/API/Repository/Values/User/Role.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class represents a role. - * - * @property-read int $id the internal id of the role - * @property-read string $identifier the identifier of the role - * @property-read \eZ\Publish\API\Repository\Values\User\Policy[] $policies an array of the policies {@link \eZ\Publish\API\Repository\Values\User\Policy} of the role. - */ -abstract class Role extends ValueObject -{ - /** @var int Status constant for defined (aka "published") role */ - public const STATUS_DEFINED = 0; - - /** @var int Status constant for draft (aka "temporary") role */ - public const STATUS_DRAFT = 1; - - /** - * ID of the user role. - * - * @var int - */ - protected $id; - - /** - * Readable string identifier of a role - * in 4.x. this is mapped to the role name. - * - * @var string - */ - protected $identifier; - - /** - * The status of the role. - * - * @var int One of Role::STATUS_DEFINED|Role::STATUS_DRAFT - */ - protected $status; - - public function getStatus(): int - { - return $this->status; - } - - /** - * Returns the list of policies of this role. - * - * @return \eZ\Publish\API\Repository\Values\User\Policy[] - */ - abstract public function getPolicies(): iterable; -} diff --git a/eZ/Publish/API/Repository/Values/User/RoleAssignment.php b/eZ/Publish/API/Repository/Values/User/RoleAssignment.php deleted file mode 100644 index 321b131a78..0000000000 --- a/eZ/Publish/API/Repository/Values/User/RoleAssignment.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation; -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This value object represents an assignment od a user or user group to a role including a limitation. - * - * @property-read \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation|null $limitation the limitation of this role assignment - * @property-read \eZ\Publish\API\Repository\Values\User\Role $role the role which is assigned to the user or user group - */ -abstract class RoleAssignment extends ValueObject -{ - /** - * The unique id of the role assignment. - * - * @var int - */ - protected $id; - - /** - * Returns the limitation of the role assignment. - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation|null - */ - abstract public function getRoleLimitation(): ?RoleLimitation; - - /** - * Returns the role to which the user or user group is assigned to. - * - * @return \eZ\Publish\API\Repository\Values\User\Role - */ - abstract public function getRole(): Role; -} diff --git a/eZ/Publish/API/Repository/Values/User/RoleCopyStruct.php b/eZ/Publish/API/Repository/Values/User/RoleCopyStruct.php deleted file mode 100644 index afa2a27fd0..0000000000 --- a/eZ/Publish/API/Repository/Values/User/RoleCopyStruct.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -/** - * This class is used to copy an existing role. - */ -abstract class RoleCopyStruct extends RoleCreateStruct -{ - /** - * Readable string identifier of a new role. - * - * @var string - */ - public $newIdentifier; - - /** - * Status of a new role. - * - * @var int - */ - public $status; -} diff --git a/eZ/Publish/API/Repository/Values/User/RoleCreateStruct.php b/eZ/Publish/API/Repository/Values/User/RoleCreateStruct.php deleted file mode 100644 index f77ec838c5..0000000000 --- a/eZ/Publish/API/Repository/Values/User/RoleCreateStruct.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class is used to create a new role. - */ -abstract class RoleCreateStruct extends ValueObject -{ - /** - * Readable string identifier of a role. - * - * @var string - */ - public $identifier; - - /** - * Returns policies associated with the role. - * - * @return \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct[] - */ - abstract public function getPolicies(): iterable; - - /** - * Adds a policy to this role. - * - * @param \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct $policyCreateStruct - */ - abstract public function addPolicy(PolicyCreateStruct $policyCreateStruct): void; -} diff --git a/eZ/Publish/API/Repository/Values/User/RoleDraft.php b/eZ/Publish/API/Repository/Values/User/RoleDraft.php deleted file mode 100644 index d5ea94335d..0000000000 --- a/eZ/Publish/API/Repository/Values/User/RoleDraft.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -/** - * This class represents a draft of a role. - * - * @property-read \eZ\Publish\API\Repository\Values\User\PolicyDraft[] $policies - */ -abstract class RoleDraft extends Role -{ -} diff --git a/eZ/Publish/API/Repository/Values/User/RoleUpdateStruct.php b/eZ/Publish/API/Repository/Values/User/RoleUpdateStruct.php deleted file mode 100644 index 87353fb275..0000000000 --- a/eZ/Publish/API/Repository/Values/User/RoleUpdateStruct.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class is used to update a role. - */ -class RoleUpdateStruct extends ValueObject -{ - /** - * Readable string identifier of a role. - * - * @var string - */ - public $identifier; -} diff --git a/eZ/Publish/API/Repository/Values/User/User.php b/eZ/Publish/API/Repository/Values/User/User.php deleted file mode 100644 index acda51f0f0..0000000000 --- a/eZ/Publish/API/Repository/Values/User/User.php +++ /dev/null @@ -1,107 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use DateTime; -use eZ\Publish\API\Repository\Values\Content\Content; - -/** - * This class represents a user value. - * - * @property-read string $login - * @property-read string $email - * @property-read string $passwordHash - * @property-read string $hashAlgorithm Hash algorithm used to hash the password - * @property-read \DateTimeInterface|null $passwordUpdatedAt - * @property-read bool $enabled User can not login if false - * @property-read int $maxLogin Max number of time user is allowed to login - */ -abstract class User extends Content implements UserReference -{ - /** - * @var int[] List of supported (by default) hash types. - */ - public const SUPPORTED_PASSWORD_HASHES = [ - self::PASSWORD_HASH_BCRYPT, - self::PASSWORD_HASH_PHP_DEFAULT, - ]; - - /** @var int Passwords in bcrypt */ - public const PASSWORD_HASH_BCRYPT = 6; - - /** @var int Passwords hashed by PHPs default algorithm, which may change over time */ - public const PASSWORD_HASH_PHP_DEFAULT = 7; - - /** @var int Default password hash, used when none is specified, may change over time */ - public const DEFAULT_PASSWORD_HASH = self::PASSWORD_HASH_PHP_DEFAULT; - - /** - * User login. - * - * @var string - */ - protected $login; - - /** - * User E-Mail address. - * - * @var string - */ - protected $email; - - /** - * User password hash. - * - * @var string - */ - protected $passwordHash; - - /** - * Datetime of last password update. - * - * @var \DateTimeInterface|null - */ - protected $passwordUpdatedAt; - - /** - * Hash algorithm used to hash the password. - * - * @var int - */ - protected $hashAlgorithm; - - /** - * Flag to signal if user is enabled or not. - * - * User can not login if false - * - * @var bool - */ - protected $enabled = false; - - /** - * Max number of time user is allowed to login. - * - * @todo: Not used in kernel, should probably be a number of login allowed before changing password. - * But new users gets 0 before they activate, admin has 10, and anonymous has 1000 in clean data. - * - * @var int - */ - protected $maxLogin; - - /** - * The User id of the User. - * - * @return int - */ - public function getUserId(): int - { - return $this->id; - } -} diff --git a/eZ/Publish/API/Repository/Values/User/UserCreateStruct.php b/eZ/Publish/API/Repository/Values/User/UserCreateStruct.php deleted file mode 100644 index c5ddf6e1c4..0000000000 --- a/eZ/Publish/API/Repository/Values/User/UserCreateStruct.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; - -/** - * This class is used to create a new user in the repository. - */ -abstract class UserCreateStruct extends ContentCreateStruct -{ - /** - * User login. - * - * Required. - * - * @var string - */ - public $login; - - /** - * User E-Mail address. - * - * Required. - * - * @var string - */ - public $email; - - /** - * The plain password. - * - * Required. - * - * @var string - */ - public $password; - - /** - * Indicates if the user is enabled after creation. - * - * @var bool - */ - public $enabled = true; -} diff --git a/eZ/Publish/API/Repository/Values/User/UserGroup.php b/eZ/Publish/API/Repository/Values/User/UserGroup.php deleted file mode 100644 index c7f49b77d4..0000000000 --- a/eZ/Publish/API/Repository/Values/User/UserGroup.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\Content\Content; - -/** - * This class represents a user group. - * - * @property-read mixed $parentId - */ -abstract class UserGroup extends Content -{ - /** - * the parent id of the user group. - * - * @var mixed - */ - protected $parentId; -} diff --git a/eZ/Publish/API/Repository/Values/User/UserGroupCreateStruct.php b/eZ/Publish/API/Repository/Values/User/UserGroupCreateStruct.php deleted file mode 100644 index 80c0bb3b26..0000000000 --- a/eZ/Publish/API/Repository/Values/User/UserGroupCreateStruct.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; - -/** - * This class is used to create a new user group in the repository. - */ -abstract class UserGroupCreateStruct extends ContentCreateStruct -{ -} diff --git a/eZ/Publish/API/Repository/Values/User/UserGroupRoleAssignment.php b/eZ/Publish/API/Repository/Values/User/UserGroupRoleAssignment.php deleted file mode 100644 index 769c153af8..0000000000 --- a/eZ/Publish/API/Repository/Values/User/UserGroupRoleAssignment.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -/** - * This class represents a user group to role assignment. - * - * @property-read \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup calls getUserGroup() - */ -abstract class UserGroupRoleAssignment extends RoleAssignment -{ - /** - * Returns the user group to which the role is assigned to. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - */ - abstract public function getUserGroup(): UserGroup; -} diff --git a/eZ/Publish/API/Repository/Values/User/UserGroupUpdateStruct.php b/eZ/Publish/API/Repository/Values/User/UserGroupUpdateStruct.php deleted file mode 100644 index 79ce0edde0..0000000000 --- a/eZ/Publish/API/Repository/Values/User/UserGroupUpdateStruct.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class is used to update a user group in the repository. - */ -class UserGroupUpdateStruct extends ValueObject -{ - /** - * The update structure for the profile content. - * - * @var \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct - */ - public $contentUpdateStruct = null; - - /** - * The update structure for the profile meta data. - * - * @var \eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct - */ - public $contentMetadataUpdateStruct = null; -} diff --git a/eZ/Publish/API/Repository/Values/User/UserReference.php b/eZ/Publish/API/Repository/Values/User/UserReference.php deleted file mode 100644 index e47ae4938e..0000000000 --- a/eZ/Publish/API/Repository/Values/User/UserReference.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -/** - * This interface represents a user reference for use in sessions and Repository. - */ -interface UserReference -{ - /** - * The User id of the User this reference represent. - * - * @return int - */ - public function getUserId(): int; -} diff --git a/eZ/Publish/API/Repository/Values/User/UserRoleAssignment.php b/eZ/Publish/API/Repository/Values/User/UserRoleAssignment.php deleted file mode 100644 index 84edda713a..0000000000 --- a/eZ/Publish/API/Repository/Values/User/UserRoleAssignment.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -/** - * This class represents a user to role assignment. - * - * @property-read \eZ\Publish\API\Repository\Values\User\User $user calls getUser() - */ -abstract class UserRoleAssignment extends RoleAssignment -{ - /** - * Returns the user to which the role is assigned to. - * - * @return \eZ\Publish\API\Repository\Values\User\User - */ - abstract public function getUser(): User; -} diff --git a/eZ/Publish/API/Repository/Values/User/UserTokenUpdateStruct.php b/eZ/Publish/API/Repository/Values/User/UserTokenUpdateStruct.php deleted file mode 100644 index 31815955a0..0000000000 --- a/eZ/Publish/API/Repository/Values/User/UserTokenUpdateStruct.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class is used to update a user token in the repository. - */ -class UserTokenUpdateStruct extends ValueObject -{ - /** - * Hash key date for user account. - * - * @var string - */ - public $hashKey; - - /** - * Time to which the token is valid. - * - * @var \DateTime|null - */ - public $time; -} diff --git a/eZ/Publish/API/Repository/Values/UserPreference/UserPreference.php b/eZ/Publish/API/Repository/Values/UserPreference/UserPreference.php deleted file mode 100644 index 25e98bd059..0000000000 --- a/eZ/Publish/API/Repository/Values/UserPreference/UserPreference.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\UserPreference; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class represents a user preference value. - * - * @property-read string $name name of user preference - * @property-read string $value value of user preference - */ -class UserPreference extends ValueObject -{ - /** - * Name of user preference. - * - * Eg: timezone - * - * @var string - */ - protected $name; - - /** - * Value of user preference. - * - * Eg: America/New_York - * - * @var string - */ - protected $value; -} diff --git a/eZ/Publish/API/Repository/Values/UserPreference/UserPreferenceList.php b/eZ/Publish/API/Repository/Values/UserPreference/UserPreferenceList.php deleted file mode 100644 index cb4ce80084..0000000000 --- a/eZ/Publish/API/Repository/Values/UserPreference/UserPreferenceList.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\UserPreference; - -use ArrayIterator; -use eZ\Publish\API\Repository\Values\ValueObject; -use IteratorAggregate; - -/** - * List of user preferences. - */ -class UserPreferenceList extends ValueObject implements IteratorAggregate -{ - /** - * The total number of user preferences. - * - * @var int - */ - public $totalCount = 0; - - /** - * List of user preferences. - * - * @var \eZ\Publish\API\Repository\Values\UserPreference\UserPreference[] - */ - public $items = []; - - /** - * {@inheritdoc} - */ - public function getIterator(): \Traversable - { - return new ArrayIterator($this->items); - } -} diff --git a/eZ/Publish/API/Repository/Values/UserPreference/UserPreferenceSetStruct.php b/eZ/Publish/API/Repository/Values/UserPreference/UserPreferenceSetStruct.php deleted file mode 100644 index 24c9edfcf6..0000000000 --- a/eZ/Publish/API/Repository/Values/UserPreference/UserPreferenceSetStruct.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\API\Repository\Values\UserPreference; - -use eZ\Publish\API\Repository\Values\ValueObject; - -class UserPreferenceSetStruct extends ValueObject -{ - /** @var string */ - public $name; - - /** @var string */ - public $value; -} diff --git a/eZ/Publish/API/Repository/Values/ValueObject.php b/eZ/Publish/API/Repository/Values/ValueObject.php deleted file mode 100644 index fe8313ede2..0000000000 --- a/eZ/Publish/API/Repository/Values/ValueObject.php +++ /dev/null @@ -1,209 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\API\Repository\Values; - -use eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException; -use eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException; - -/** - * The base class for all value objects and structs. - * - * Supports readonly properties by marking them as protected. - * In this case they will only be writable using constructor, and need to be documented - * using @property-read <type> <$var> in class doc in addition to inline property doc. - * Writable properties must be public and must be documented inline. - */ -abstract class ValueObject -{ - /** - * Construct object optionally with a set of properties. - * - * Readonly properties values must be set using $properties as they are not writable anymore - * after object has been created. - * - * @param array $properties - */ - public function __construct(array $properties = []) - { - foreach ($properties as $property => $value) { - $this->$property = $value; - } - } - - /** - * Function where list of properties are returned. - * - * Used by {@see attributes()}, override to add dynamic properties - * - * @uses ::__isset() - * - * @todo Make object traversable and reuse this function there (hence why this is not exposed) - * - * @param array $dynamicProperties Additional dynamic properties exposed on the object - * - * @return array - */ - protected function getProperties($dynamicProperties = []) - { - $properties = $dynamicProperties; - foreach (get_object_vars($this) as $property => $propertyValue) { - if ($this->__isset($property)) { - $properties[] = $property; - } - } - - return $properties; - } - - /** - * Magic set function handling writes to non public properties. - * - * @ignore This method is for internal use - * - * @throws \eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException When property does not exist - * @throws \eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException When property is readonly (protected) - * - * @param string $property Name of the property - * @param string $value - */ - public function __set($property, $value) - { - if (property_exists($this, $property)) { - throw new PropertyReadOnlyException($property, static::class); - } - throw new PropertyNotFoundException($property, static::class); - } - - /** - * Magic get function handling read to non public properties. - * - * Returns value for all readonly (protected) properties. - * - * @ignore This method is for internal use - * - * @throws \eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException exception on all reads to undefined properties so typos are not silently accepted. - * - * @param string $property Name of the property - * - * @return mixed - */ - public function __get($property) - { - if (property_exists($this, $property)) { - return $this->$property; - } - throw new PropertyNotFoundException($property, static::class); - } - - /** - * Magic isset function handling isset() to non public properties. - * - * Returns true for all (public/)protected/private properties. - * - * @ignore This method is for internal use - * - * @param string $property Name of the property - * - * @return bool - */ - public function __isset($property) - { - return property_exists($this, $property); - } - - /** - * Magic unset function handling unset() to non public properties. - * - * @ignore This method is for internal use - * - * @throws \eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException exception on all writes to undefined properties so typos are not silently accepted and - * @throws \eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException exception on readonly (protected) properties. - * - * @uses ::__set() - * - * @param string $property Name of the property - * - * @return bool - */ - public function __unset($property) - { - $this->__set($property, null); - } - - /** - * Returns a new instance of this class with the data specified by $array. - * - * $array contains all the data members of this class in the form: - * array('member_name'=>value). - * - * __set_state makes this class exportable with var_export. - * var_export() generates code, that calls this method when it - * is parsed with PHP. - * - * @ignore This method is for internal use - * - * @param mixed[] $array - * - * @return ValueObject - */ - public static function __set_state(array $array) - { - return new static($array); - } - - /** - * Internal function for Legacy template engine compatibility to get property value. - * - * @ignore This method is for internal use - * - * @deprecated Since 5.0, available purely for legacy eZTemplate compatibility - * - * @uses ::__get() - * - * @param string $property - * - * @return mixed - */ - final public function attribute($property) - { - return $this->__get($property); - } - - /** - * Internal function for Legacy template engine compatibility to get properties. - * - * @ignore This method is for internal use - * - * @deprecated Since 5.0, available purely for legacy eZTemplate compatibility - * - * @uses ::__isset() - * - * @return array - */ - final public function attributes() - { - return $this->getProperties(); - } - - /** - * Internal function for Legacy template engine compatibility to check existence of property. - * - * @ignore This method is for internal use - * - * @deprecated Since 5.0, available purely for legacy eZTemplate compatibility - * - * @uses ::__isset() - * - * @param string $property - * - * @return bool - */ - final public function hasAttribute($property) - { - return $this->__isset($property); - } -} diff --git a/eZ/Publish/Core/Base/Container/ApiLoader/RepositoryFactory.php b/eZ/Publish/Core/Base/Container/ApiLoader/RepositoryFactory.php deleted file mode 100644 index 7e302a128e..0000000000 --- a/eZ/Publish/Core/Base/Container/ApiLoader/RepositoryFactory.php +++ /dev/null @@ -1,134 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Container\ApiLoader; - -use eZ\Publish\API\Repository\LanguageResolver; -use eZ\Publish\API\Repository\PasswordHashService; -use eZ\Publish\API\Repository\PermissionService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\Repository\Helper\RelationProcessor; -use eZ\Publish\Core\Repository\Mapper; -use eZ\Publish\Core\Repository\Permission\LimitationService; -use eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperFactoryInterface; -use eZ\Publish\Core\Repository\User\PasswordValidatorInterface; -use eZ\Publish\Core\Search\Common\BackgroundIndexer; -use eZ\Publish\SPI\Persistence\Filter\Content\Handler as ContentFilteringHandler; -use eZ\Publish\SPI\Persistence\Filter\Location\Handler as LocationFilteringHandler; -use eZ\Publish\SPI\Persistence\Handler as PersistenceHandler; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy; -use eZ\Publish\SPI\Repository\Validator\ContentValidator; -use eZ\Publish\SPI\Search\Handler as SearchHandler; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerAwareTrait; - -class RepositoryFactory implements ContainerAwareInterface -{ - use ContainerAwareTrait; - - /** @var string */ - private $repositoryClass; - - /** - * Policies map. - * - * @var array - */ - protected $policyMap = []; - - /** @var \eZ\Publish\API\Repository\LanguageResolver */ - private $languageResolver; - - public function __construct( - $repositoryClass, - array $policyMap, - LanguageResolver $languageResolver - ) { - $this->repositoryClass = $repositoryClass; - $this->policyMap = $policyMap; - $this->languageResolver = $languageResolver; - } - - /** - * Builds the main repository, heart of eZ Publish API. - * - * This always returns the true inner Repository, please depend on ezpublish.api.repository and not this method - * directly to make sure you get an instance wrapped inside Event / Cache / * functionality. - * - * @param string[] $languages - */ - public function buildRepository( - PersistenceHandler $persistenceHandler, - SearchHandler $searchHandler, - BackgroundIndexer $backgroundIndexer, - RelationProcessor $relationProcessor, - FieldTypeRegistry $fieldTypeRegistry, - PasswordHashService $passwordHashService, - ThumbnailStrategy $thumbnailStrategy, - ProxyDomainMapperFactoryInterface $proxyDomainMapperFactory, - Mapper\ContentDomainMapper $contentDomainMapper, - Mapper\ContentTypeDomainMapper $contentTypeDomainMapper, - Mapper\RoleDomainMapper $roleDomainMapper, - Mapper\ContentMapper $contentMapper, - ContentValidator $contentValidator, - LimitationService $limitationService, - PermissionService $permissionService, - ContentFilteringHandler $contentFilteringHandler, - LocationFilteringHandler $locationFilteringHandler, - PasswordValidatorInterface $passwordValidator, - array $languages - ): Repository { - return new $this->repositoryClass( - $persistenceHandler, - $searchHandler, - $backgroundIndexer, - $relationProcessor, - $fieldTypeRegistry, - $passwordHashService, - $thumbnailStrategy, - $proxyDomainMapperFactory, - $contentDomainMapper, - $contentTypeDomainMapper, - $roleDomainMapper, - $contentMapper, - $contentValidator, - $limitationService, - $this->languageResolver, - $permissionService, - $contentFilteringHandler, - $locationFilteringHandler, - $passwordValidator, - [ - 'role' => [ - 'policyMap' => $this->policyMap, - ], - 'languages' => $languages, - ], - ); - } - - /** - * Returns a service based on a name string (content => contentService, etc). - * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param string $serviceName - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - * - * @return mixed - */ - public function buildService(Repository $repository, $serviceName) - { - $methodName = 'get' . $serviceName . 'Service'; - if (!method_exists($repository, $methodName)) { - throw new InvalidArgumentException($serviceName, 'No such service'); - } - - return $repository->$methodName(); - } -} diff --git a/eZ/Publish/Core/Base/Container/Compiler/AbstractFieldTypeBasedPass.php b/eZ/Publish/Core/Base/Container/Compiler/AbstractFieldTypeBasedPass.php deleted file mode 100644 index 16ff3eecfc..0000000000 --- a/eZ/Publish/Core/Base/Container/Compiler/AbstractFieldTypeBasedPass.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Container\Compiler; - -use eZ\Publish\Core\Base\Container\Compiler\TaggedServiceIdsIterator\BackwardCompatibleIterator; -use LogicException; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; - -abstract class AbstractFieldTypeBasedPass implements CompilerPassInterface -{ - public const FIELD_TYPE_SERVICE_TAG = 'ezplatform.field_type'; - public const DEPRECATED_FIELD_TYPE_SERVICE_TAG = 'ezpublish.fieldType'; - - public const FIELD_TYPE_SERVICE_TAGS = [ - self::FIELD_TYPE_SERVICE_TAG, - self::DEPRECATED_FIELD_TYPE_SERVICE_TAG, - ]; - - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * - * @return array - * - * @throws \LogicException - */ - public function getFieldTypeServiceIds(ContainerBuilder $container): iterable - { - $fieldTypesIterator = new BackwardCompatibleIterator( - $container, - self::FIELD_TYPE_SERVICE_TAG, - self::DEPRECATED_FIELD_TYPE_SERVICE_TAG - ); - - foreach ($fieldTypesIterator as $id => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['alias'])) { - throw new LogicException( - sprintf( - 'The %s or %s service tag needs an "alias" attribute to identify the Field Type.', - self::DEPRECATED_FIELD_TYPE_SERVICE_TAG, - self::FIELD_TYPE_SERVICE_TAG - ) - ); - } - } - } - - return $fieldTypesIterator; - } - - abstract public function process(ContainerBuilder $container); -} diff --git a/eZ/Publish/Core/Base/Container/Compiler/FieldTypeRegistryPass.php b/eZ/Publish/Core/Base/Container/Compiler/FieldTypeRegistryPass.php deleted file mode 100644 index 942c325249..0000000000 --- a/eZ/Publish/Core/Base/Container/Compiler/FieldTypeRegistryPass.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Container\Compiler; - -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\FieldType\Null\Type; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; - -/** - * This compiler pass will register eZ Platform Field Types. - */ -class FieldTypeRegistryPass extends AbstractFieldTypeBasedPass -{ - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * - * @throws \LogicException - */ - public function process(ContainerBuilder $container): void - { - if (!$container->hasDefinition(FieldTypeRegistry::class)) { - return; - } - - $fieldTypeRegistryDefinition = $container->getDefinition(FieldTypeRegistry::class); - - foreach ($this->getFieldTypeServiceIds($container) as $id => $attributes) { - foreach ($attributes as $attribute) { - $fieldTypeRegistryDefinition->addMethodCall( - 'registerFieldType', - [ - $attribute['alias'], - new Reference($id), - ] - ); - - // Add FieldType to the "concrete" list if it's not a fake. - if (!is_a($container->findDefinition($id)->getClass(), Type::class, true)) { - $fieldTypeRegistryDefinition->addMethodCall( - 'registerConcreteFieldTypeIdentifier', - [$attribute['alias']] - ); - } - } - } - } -} diff --git a/eZ/Publish/Core/Base/Container/Compiler/Persistence/FieldTypeRegistryPass.php b/eZ/Publish/Core/Base/Container/Compiler/Persistence/FieldTypeRegistryPass.php deleted file mode 100644 index 0a801156b0..0000000000 --- a/eZ/Publish/Core/Base/Container/Compiler/Persistence/FieldTypeRegistryPass.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Container\Compiler\Persistence; - -use eZ\Publish\Core\Base\Container\Compiler\AbstractFieldTypeBasedPass; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; - -class FieldTypeRegistryPass extends AbstractFieldTypeBasedPass -{ - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * - * @throws \LogicException - */ - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('ezpublish.persistence.field_type_registry')) { - return; - } - - $fieldTypeRegistryDefinition = $container->getDefinition('ezpublish.persistence.field_type_registry'); - - foreach ($this->getFieldTypeServiceIds($container) as $id => $attributes) { - foreach ($attributes as $attribute) { - $fieldTypeRegistryDefinition->addMethodCall( - 'register', - [ - $attribute['alias'], - new Reference($id), - ] - ); - } - } - } -} diff --git a/eZ/Publish/Core/Base/Container/Compiler/Search/AggregateFieldValueMapperPass.php b/eZ/Publish/Core/Base/Container/Compiler/Search/AggregateFieldValueMapperPass.php deleted file mode 100644 index 024a866992..0000000000 --- a/eZ/Publish/Core/Base/Container/Compiler/Search/AggregateFieldValueMapperPass.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Container\Compiler\Search; - -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; - -/** - * This compiler pass will register Search Engine field value mappers. - */ -class AggregateFieldValueMapperPass implements CompilerPassInterface -{ - public const SERVICE_ID = 'ezpublish.search.common.field_value_mapper.aggregate'; - public const TAG = 'ezpublish.search.common.field_value_mapper'; - - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - */ - public function process(ContainerBuilder $container): void - { - if (!$container->hasDefinition(self::SERVICE_ID)) { - return; - } - - $aggregateFieldValueMapperDefinition = $container->getDefinition(self::SERVICE_ID); - $taggedServiceIds = $container->findTaggedServiceIds(self::TAG); - foreach ($taggedServiceIds as $id => $tags) { - foreach ($tags as $tagAttributes) { - $aggregateFieldValueMapperDefinition->addMethodCall( - 'addMapper', - [new Reference($id), $tagAttributes['maps'] ?? null] - ); - } - } - } -} diff --git a/eZ/Publish/Core/Base/Container/Compiler/Search/FieldRegistryPass.php b/eZ/Publish/Core/Base/Container/Compiler/Search/FieldRegistryPass.php deleted file mode 100644 index 728aac568e..0000000000 --- a/eZ/Publish/Core/Base/Container/Compiler/Search/FieldRegistryPass.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Container\Compiler\Search; - -use eZ\Publish\Core\Base\Container\Compiler\TaggedServiceIdsIterator\BackwardCompatibleIterator; -use LogicException; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; - -/** - * This compiler pass will register eZ Publish indexable field types. - */ -class FieldRegistryPass implements CompilerPassInterface -{ - public const FIELD_TYPE_INDEXABLE_SERVICE_TAG = 'ezplatform.field_type.indexable'; - public const DEPRECATED_FIELD_TYPE_INDEXABLE_SERVICE_TAG = 'ezpublish.fieldType.indexable'; - - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * - * @throws \LogicException - */ - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('ezpublish.search.common.field_registry')) { - return; - } - - $fieldRegistryDefinition = $container->getDefinition('ezpublish.search.common.field_registry'); - - $fieldTypesIterator = new BackwardCompatibleIterator( - $container, - self::FIELD_TYPE_INDEXABLE_SERVICE_TAG, - self::DEPRECATED_FIELD_TYPE_INDEXABLE_SERVICE_TAG - ); - - foreach ($fieldTypesIterator as $id => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['alias'])) { - throw new LogicException( - sprintf( - 'The %s or %s service tag needs an "alias" attribute to identify the indexable Field Type.', - self::DEPRECATED_FIELD_TYPE_INDEXABLE_SERVICE_TAG, - self::FIELD_TYPE_INDEXABLE_SERVICE_TAG - ) - ); - } - - $fieldRegistryDefinition->addMethodCall( - 'registerType', - [ - $attribute['alias'], - new Reference($id), - ] - ); - } - } - } -} diff --git a/eZ/Publish/Core/Base/Container/Compiler/Search/Legacy/CriteriaConverterPass.php b/eZ/Publish/Core/Base/Container/Compiler/Search/Legacy/CriteriaConverterPass.php deleted file mode 100644 index f59d35c2b2..0000000000 --- a/eZ/Publish/Core/Base/Container/Compiler/Search/Legacy/CriteriaConverterPass.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Container\Compiler\Search\Legacy; - -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; - -/** - * This compiler pass will register Legacy Search Engine criterion handlers. - */ -class CriteriaConverterPass implements CompilerPassInterface -{ - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - */ - public function process(ContainerBuilder $container) - { - if ( - !$container->hasDefinition('ezpublish.search.legacy.gateway.criteria_converter.content') && - !$container->hasDefinition('ezpublish.search.legacy.gateway.criteria_converter.location') && - !$container->hasDefinition('ezplatform.trash.search.legacy.gateway.criteria_converter') && - !$container->hasDefinition('ezpublish.spi.persistence.legacy.url.criterion_converter') - ) { - return; - } - - if ($container->hasDefinition('ezpublish.search.legacy.gateway.criteria_converter.content')) { - $criteriaConverterContent = $container->getDefinition('ezpublish.search.legacy.gateway.criteria_converter.content'); - - $contentHandlers = $container->findTaggedServiceIds('ezpublish.search.legacy.gateway.criterion_handler.content'); - - $this->addHandlers($criteriaConverterContent, $contentHandlers); - } - - if ($container->hasDefinition('ezpublish.search.legacy.gateway.criteria_converter.location')) { - $criteriaConverterLocation = $container->getDefinition('ezpublish.search.legacy.gateway.criteria_converter.location'); - - $locationHandlers = $container->findTaggedServiceIds('ezpublish.search.legacy.gateway.criterion_handler.location'); - - $this->addHandlers($criteriaConverterLocation, $locationHandlers); - } - - if ($container->hasDefinition('ezplatform.trash.search.legacy.gateway.criteria_converter')) { - $trashCriteriaConverter = $container->getDefinition('ezplatform.trash.search.legacy.gateway.criteria_converter'); - $trashCriteriaHandlers = $container->findTaggedServiceIds('ezplatform.trash.search.legacy.gateway.criterion_handler'); - - $this->addHandlers($trashCriteriaConverter, $trashCriteriaHandlers); - } - - if ($container->hasDefinition('ezpublish.spi.persistence.legacy.url.criterion_converter')) { - $urlCriteriaConverter = $container->getDefinition('ezpublish.spi.persistence.legacy.url.criterion_converter'); - $urlCriteriaHandlers = $container->findTaggedServiceIds('ezpublish.persistence.legacy.url.criterion_handler'); - - $this->addHandlers($urlCriteriaConverter, $urlCriteriaHandlers); - } - } - - protected function addHandlers(Definition $definition, $handlers) - { - foreach ($handlers as $id => $attributes) { - $definition->addMethodCall('addHandler', [new Reference($id)]); - } - } -} diff --git a/eZ/Publish/Core/Base/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPass.php b/eZ/Publish/Core/Base/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPass.php deleted file mode 100644 index 9ed0d80f1f..0000000000 --- a/eZ/Publish/Core/Base/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPass.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Container\Compiler\Search\Legacy; - -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Exception\LogicException; -use Symfony\Component\DependencyInjection\Reference; - -/** - * This compiler pass will register Legacy Search Engine criterion field value handlers. - */ -class CriterionFieldValueHandlerRegistryPass implements CompilerPassInterface -{ - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - */ - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('ezpublish.search.legacy.gateway.criterion_field_value_handler.registry')) { - return; - } - - $registry = $container->getDefinition('ezpublish.search.legacy.gateway.criterion_field_value_handler.registry'); - - foreach ($container->findTaggedServiceIds('ezpublish.search.legacy.gateway.criterion_field_value_handler') as $id => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['alias'])) { - throw new LogicException( - 'ezpublish.search.legacy.gateway.criterion_field_value_handler service tag needs an "alias" attribute to identify the Field Type.' - ); - } - - $registry->addMethodCall( - 'register', - [ - $attribute['alias'], - new Reference($id), - ] - ); - } - } - } -} diff --git a/eZ/Publish/Core/Base/Container/Compiler/Search/Legacy/SortClauseConverterPass.php b/eZ/Publish/Core/Base/Container/Compiler/Search/Legacy/SortClauseConverterPass.php deleted file mode 100644 index 9a7e860415..0000000000 --- a/eZ/Publish/Core/Base/Container/Compiler/Search/Legacy/SortClauseConverterPass.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Container\Compiler\Search\Legacy; - -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; - -/** - * This compiler pass will register Legacy Storage sort clause handlers. - */ -class SortClauseConverterPass implements CompilerPassInterface -{ - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - */ - public function process(ContainerBuilder $container) - { - if ( - !$container->hasDefinition('ezpublish.search.legacy.gateway.sort_clause_converter.content') && - !$container->hasDefinition('ezpublish.search.legacy.gateway.sort_clause_converter.location') && - !$container->hasDefinition('ezplatform.trash.search.legacy.gateway.sort_clause_converter') - ) { - return; - } - - if ($container->hasDefinition('ezpublish.search.legacy.gateway.sort_clause_converter.content')) { - $sortClauseConverterContent = $container->getDefinition('ezpublish.search.legacy.gateway.sort_clause_converter.content'); - - $contentHandlers = $container->findTaggedServiceIds('ezpublish.search.legacy.gateway.sort_clause_handler.content'); - - $this->addHandlers($sortClauseConverterContent, $contentHandlers); - } - - if ($container->hasDefinition('ezpublish.search.legacy.gateway.sort_clause_converter.location')) { - $sortClauseConverterLocation = $container->getDefinition('ezpublish.search.legacy.gateway.sort_clause_converter.location'); - - $locationHandlers = $container->findTaggedServiceIds('ezpublish.search.legacy.gateway.sort_clause_handler.location'); - - $this->addHandlers($sortClauseConverterLocation, $locationHandlers); - } - - if ($container->hasDefinition('ezplatform.trash.search.legacy.gateway.sort_clause_converter')) { - $sortClauseConverterTrash = $container->getDefinition('ezplatform.trash.search.legacy.gateway.sort_clause_converter'); - - $trashHandlers = $container->findTaggedServiceIds('ezplatform.trash.search.legacy.gateway.sort_clause_handler'); - - $this->addHandlers($sortClauseConverterTrash, $trashHandlers); - } - } - - protected function addHandlers(Definition $definition, $handlers) - { - foreach ($handlers as $id => $attributes) { - $definition->addMethodCall('addHandler', [new Reference($id)]); - } - } -} diff --git a/eZ/Publish/Core/Base/Container/Compiler/Storage/ExternalStorageRegistryPass.php b/eZ/Publish/Core/Base/Container/Compiler/Storage/ExternalStorageRegistryPass.php deleted file mode 100644 index f33afa973f..0000000000 --- a/eZ/Publish/Core/Base/Container/Compiler/Storage/ExternalStorageRegistryPass.php +++ /dev/null @@ -1,141 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Container\Compiler\Storage; - -use eZ\Publish\Core\Base\Container\Compiler\TaggedServiceIdsIterator\BackwardCompatibleIterator; -use LogicException; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; - -/** - * This compiler pass will register eZ Publish external storage handlers and gateways. - */ -class ExternalStorageRegistryPass implements CompilerPassInterface -{ - public const EXTERNAL_STORAGE_HANDLER_SERVICE_TAG = 'ezplatform.field_type.external_storage_handler'; - public const EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG = 'ezplatform.field_type.external_storage_handler.gateway'; - - public const DEPRECATED_EXTERNAL_STORAGE_HANDLER_SERVICE_TAG = 'ezpublish.fieldType.externalStorageHandler'; - public const DEPRECATED_EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG = 'ezpublish.fieldType.externalStorageHandler.gateway'; - - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * - * @throws \LogicException - */ - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('ezpublish.persistence.external_storage_registry')) { - return; - } - - $externalStorageRegistryDefinition = $container->getDefinition( - 'ezpublish.persistence.external_storage_registry' - ); - - // Gateways for external storage handlers. - // Alias attribute is the corresponding field type string. - $externalStorageGateways = []; - - $externalStorageHandlerGatewayIterator = new BackwardCompatibleIterator( - $container, - self::EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG, - self::DEPRECATED_EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG, - ); - - // Referencing the services by alias (field type string) - foreach ($externalStorageHandlerGatewayIterator as $id => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['alias'])) { - throw new LogicException( - sprintf( - 'The %s or %s service tag needs an "alias" attribute to identify the Field Type.', - self::DEPRECATED_EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG, - self::EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG - ) - ); - } - - if (!isset($attribute['identifier'])) { - throw new LogicException( - sprintf( - 'The %s or %s service tag needs an "identifier" attribute to identify the gateway.', - self::DEPRECATED_EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG, - self::EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG - ) - ); - } - - $externalStorageGateways[$attribute['alias']] = [ - 'id' => $id, - 'identifier' => $attribute['identifier'], - ]; - } - } - - $externalStorageHandlerIterator = new BackwardCompatibleIterator( - $container, - self::EXTERNAL_STORAGE_HANDLER_SERVICE_TAG, - self::DEPRECATED_EXTERNAL_STORAGE_HANDLER_SERVICE_TAG - ); - - // External storage handlers for field types that need them. - // Alias attribute is the field type string. - foreach ($externalStorageHandlerIterator as $id => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['alias'])) { - throw new LogicException( - sprintf( - 'The %s or %s service tag needs an "alias" attribute to identify the Field Type.', - self::DEPRECATED_EXTERNAL_STORAGE_HANDLER_SERVICE_TAG, - self::EXTERNAL_STORAGE_HANDLER_SERVICE_TAG - ) - ); - } - - // If the storage handler is gateway based, then we need to add a corresponding gateway to it. - // Will throw a LogicException if no gateway is defined for this field type. - $storageHandlerDef = $container->findDefinition($id); - $storageHandlerClass = $storageHandlerDef->getClass(); - if (preg_match('/^%([^%\s]+)%$/', (string)$storageHandlerClass, $match)) { - $storageHandlerClass = $container->getParameter($match[1]); - } - - if ( - is_subclass_of( - $storageHandlerClass, - 'eZ\\Publish\\Core\\FieldType\\GatewayBasedStorage' - ) - ) { - if (!isset($externalStorageGateways[$attribute['alias']])) { - throw new LogicException( - "External storage handler '$id' for Field Type {$attribute['alias']} needs a storage gateway. - Consider defining a storage gateway as a service for this Field Type and add the 'ezplatform.field_type.external_storage_handler.gateway tag'" - ); - } - - $storageHandlerDef->addMethodCall( - 'addGateway', - [ - $externalStorageGateways[$attribute['alias']]['identifier'], - new Reference($externalStorageGateways[$attribute['alias']]['id']), - ] - ); - } - - $externalStorageRegistryDefinition->addMethodCall( - 'register', - [ - $attribute['alias'], - new Reference($id), - ] - ); - } - } - } -} diff --git a/eZ/Publish/Core/Base/Container/Compiler/Storage/Legacy/FieldValueConverterRegistryPass.php b/eZ/Publish/Core/Base/Container/Compiler/Storage/Legacy/FieldValueConverterRegistryPass.php deleted file mode 100644 index 378b62aac0..0000000000 --- a/eZ/Publish/Core/Base/Container/Compiler/Storage/Legacy/FieldValueConverterRegistryPass.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Container\Compiler\Storage\Legacy; - -use eZ\Publish\Core\Base\Container\Compiler\TaggedServiceIdsIterator\BackwardCompatibleIterator; -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Exception\LogicException; -use Symfony\Component\DependencyInjection\Reference; - -/** - * This compiler pass will register Legacy Storage field value converters. - */ -class FieldValueConverterRegistryPass implements CompilerPassInterface -{ - public const CONVERTER_REGISTRY_SERVICE_ID = 'ezpublish.persistence.legacy.field_value_converter.registry'; - - public const CONVERTER_SERVICE_TAG = 'ezplatform.field_type.legacy_storage.converter'; - public const DEPRECATED_CONVERTER_SERVICE_TAG = 'ezpublish.storageEngine.legacy.converter'; - - public const CONVERTER_SERVICE_TAGS = [ - self::DEPRECATED_CONVERTER_SERVICE_TAG, - self::CONVERTER_SERVICE_TAG, - ]; - - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - */ - public function process(ContainerBuilder $container): void - { - if (!$container->hasDefinition(self::CONVERTER_REGISTRY_SERVICE_ID)) { - return; - } - - $registry = $container->getDefinition(self::CONVERTER_REGISTRY_SERVICE_ID); - - $iterator = new BackwardCompatibleIterator( - $container, - self::CONVERTER_SERVICE_TAG, - self::DEPRECATED_CONVERTER_SERVICE_TAG - ); - - foreach ($iterator as $id => $attributes) { - foreach ($attributes as $attribute) { - if (!isset($attribute['alias'])) { - throw new LogicException( - sprintf( - 'The %s or %s service tag needs an "alias" attribute to identify the Field Type.', - self::DEPRECATED_CONVERTER_SERVICE_TAG, - self::CONVERTER_SERVICE_TAG - ) - ); - } - - $registry->addMethodCall( - 'register', - [ - $attribute['alias'], - new Reference($id), - ] - ); - } - } - } -} diff --git a/eZ/Publish/Core/Base/Container/Compiler/Storage/Legacy/RoleLimitationConverterPass.php b/eZ/Publish/Core/Base/Container/Compiler/Storage/Legacy/RoleLimitationConverterPass.php deleted file mode 100644 index 569535d969..0000000000 --- a/eZ/Publish/Core/Base/Container/Compiler/Storage/Legacy/RoleLimitationConverterPass.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Container\Compiler\Storage\Legacy; - -use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Reference; - -/** - * This compiler pass will register Legacy Storage role limitation converters. - */ -class RoleLimitationConverterPass implements CompilerPassInterface -{ - /** - * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container - * - * @throws \LogicException - */ - public function process(ContainerBuilder $container) - { - if (!$container->hasDefinition('ezpublish.persistence.legacy.role.limitation.converter')) { - return; - } - - $roleLimitationConverter = $container->getDefinition('ezpublish.persistence.legacy.role.limitation.converter'); - - foreach ($container->findTaggedServiceIds('ezpublish.persistence.legacy.role.limitation.handler') as $id => $attributes) { - foreach ($attributes as $attribute) { - $roleLimitationConverter->addMethodCall( - 'addHandler', - [new Reference($id)] - ); - } - } - } -} diff --git a/eZ/Publish/Core/Base/Exceptions/BadStateException.php b/eZ/Publish/Core/Base/Exceptions/BadStateException.php deleted file mode 100644 index 20bde212da..0000000000 --- a/eZ/Publish/Core/Base/Exceptions/BadStateException.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Exceptions; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\BadStateException as APIBadStateException; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; - -/** - * BadState Exception implementation. - * - * Usage: throw new BadState( 'nodes', 'array' ); - */ -class BadStateException extends APIBadStateException implements Translatable -{ - use TranslatableBase; - - /** - * Generates: "Argument '{$argumentName}' has a bad state: {$whatIsWrong}". - * - * @param string $argumentName - * @param string $whatIsWrong - * @param \Exception|null $previous - */ - public function __construct($argumentName, $whatIsWrong, Exception $previous = null) - { - $this->setMessageTemplate("Argument '%argumentName%' has a bad state: %whatIsWrong%"); - $this->setParameters(['%argumentName%' => $argumentName, '%whatIsWrong%' => $whatIsWrong]); - parent::__construct($this->getBaseTranslation(), 0, $previous); - } -} diff --git a/eZ/Publish/Core/Base/Exceptions/ContentFieldValidationException.php b/eZ/Publish/Core/Base/Exceptions/ContentFieldValidationException.php deleted file mode 100644 index 46cabf9b8e..0000000000 --- a/eZ/Publish/Core/Base/Exceptions/ContentFieldValidationException.php +++ /dev/null @@ -1,115 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Exceptions; - -use eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException as APIContentFieldValidationException; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; - -/** - * This Exception is thrown on create or update content one or more given fields are not valid. - */ -class ContentFieldValidationException extends APIContentFieldValidationException implements Translatable -{ - use TranslatableBase; - - private const MAX_MESSAGES_NUMBER = 32; - - /** - * Contains an array of field ValidationError objects indexed with FieldDefinition id and language code. - * - * Example: - * <code> - * $fieldErrors = $exception->getFieldErrors(); - * $fieldErrors["43"]["eng-GB"]->getTranslatableMessage(); - * </code> - * - * @var array<array-key, array<string, \eZ\Publish\Core\FieldType\ValidationError>> - */ - protected $errors; - - /** @var string|null */ - protected $contentName; - - /** - * Generates: Content fields did not validate. - * - * Also sets the given $fieldErrors to the internal property, retrievable by getFieldErrors() - * - * @param array<array-key, array<string, \eZ\Publish\Core\FieldType\ValidationError>> $errors - */ - public function __construct(array $errors) - { - $this->errors = $errors; - $this->setMessageTemplate('Content fields did not validate'); - parent::__construct($this->getBaseTranslation()); - } - - /** - * Generates: Content fields did not validate exception with additional information on affected fields. - * - * @param array<array-key, array<string, \eZ\Publish\Core\FieldType\ValidationError>> $errors - */ - public static function createNewWithMultiline(array $errors, ?string $contentName = null): self - { - $exception = new self($errors); - $exception->contentName = $contentName; - - $exception->setMessageTemplate('Content "%contentName%" fields did not validate: %errors%'); - $exception->setParameters([ - '%errors%' => $exception->generateValidationErrorsMessages(), - '%contentName%' => $exception->contentName !== null ? $exception->contentName : '', - ]); - $exception->message = $exception->getBaseTranslation(); - - return $exception; - } - - /** - * Returns an array of field validation error messages. - * - * @return array<array-key, array<string, \eZ\Publish\Core\FieldType\ValidationError>> - */ - public function getFieldErrors() - { - return $this->errors; - } - - private function generateValidationErrorsMessages(): string - { - $validationErrors = $this->collectValidationErrors(); - $maxMessagesNumber = self::MAX_MESSAGES_NUMBER; - - if (count($validationErrors) > $maxMessagesNumber) { - array_splice($validationErrors, $maxMessagesNumber); - $validationErrors[] = sprintf('Limit: %d of validation errors has been exceeded.', $maxMessagesNumber); - } - - return "\n- " . implode("\n- ", $validationErrors); - } - - /** - * @return array<\eZ\Publish\Core\FieldType\ValidationError> - */ - private function collectValidationErrors(): array - { - $messages = []; - foreach ($this->getFieldErrors() as $validationErrors) { - foreach ($validationErrors as $validationError) { - if (is_array($validationError)) { - foreach ($validationError as $item) { - $messages[] = $item->getTranslatableMessage(); - } - } else { - $messages[] = $validationError->getTranslatableMessage(); - } - } - } - - return $messages; - } -} diff --git a/eZ/Publish/Core/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php b/eZ/Publish/Core/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php deleted file mode 100644 index c72c86a8be..0000000000 --- a/eZ/Publish/Core/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Exceptions; - -use eZ\Publish\API\Repository\Exceptions\ContentTypeFieldDefinitionValidationException as APIContentTypeFieldDefinitionValidationException; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; - -/** - * This Exception is thrown on create or update content one or more given fields are not valid. - */ -class ContentTypeFieldDefinitionValidationException extends APIContentTypeFieldDefinitionValidationException implements Translatable -{ - use TranslatableBase; - - /** - * Contains an array of field ValidationError objects indexed with FieldDefinition id and language code. - * - * Example: - * <code> - * $fieldErrors = $exception->getFieldErrors(); - * $fieldErrors["43"]["eng-GB"]->getTranslatableMessage(); - * </code> - * - * @var \eZ\Publish\Core\FieldType\ValidationError[] - */ - protected $errors; - - /** - * Generates: Content fields did not validate. - * - * Also sets the given $fieldErrors to the internal property, retrievable by getFieldErrors() - * - * @param \eZ\Publish\Core\FieldType\ValidationError[] $errors - */ - public function __construct(array $errors) - { - $this->errors = $errors; - $this->setMessageTemplate('Content Type Field definitions did not validate'); - parent::__construct($this->getBaseTranslation()); - } - - /** - * Returns an array of field validation error messages. - * - * @return \eZ\Publish\Core\FieldType\ValidationError[] - */ - public function getFieldErrors() - { - return $this->errors; - } -} diff --git a/eZ/Publish/Core/Base/Exceptions/ContentTypeValidationException.php b/eZ/Publish/Core/Base/Exceptions/ContentTypeValidationException.php deleted file mode 100644 index b2240c22b1..0000000000 --- a/eZ/Publish/Core/Base/Exceptions/ContentTypeValidationException.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Exceptions; - -use eZ\Publish\API\Repository\Exceptions\ContentTypeValidationException as APIContentTypeValidationException; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; - -/** - * This Exception is thrown on create or update content type when content type is not valid. - */ -class ContentTypeValidationException extends APIContentTypeValidationException implements Translatable -{ - use TranslatableBase; - - /** - * @param string $messageTemplate The message template, with placeholders for parameters. - * E.g. "Content with ID %contentId% could not be found". - * @param array $parameters Hash map with param placeholder as key and its corresponding value. - * E.g. array('%contentId%' => 123). - */ - public function __construct($messageTemplate, array $parameters = []) - { - /** @Ignore */ - $this->setMessageTemplate($messageTemplate); - $this->setParameters($parameters); - - parent::__construct($this->getBaseTranslation()); - } -} diff --git a/eZ/Publish/Core/Base/Exceptions/ContentValidationException.php b/eZ/Publish/Core/Base/Exceptions/ContentValidationException.php deleted file mode 100644 index 5d6aa2d5dd..0000000000 --- a/eZ/Publish/Core/Base/Exceptions/ContentValidationException.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Exceptions; - -use eZ\Publish\API\Repository\Exceptions\ContentValidationException as APIContentValidationException; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; - -/** - * This Exception is thrown on create or update content one or more given fields are not valid. - */ -class ContentValidationException extends APIContentValidationException implements Translatable -{ - use TranslatableBase; - - /** - * @param string $messageTemplate The message template, with placeholders for parameters. - * E.g. "Content with ID %contentId% could not be found". - * @param array $parameters Hash map with param placeholder as key and its corresponding value. - * E.g. array('%contentId%' => 123). - */ - public function __construct($messageTemplate, array $parameters = []) - { - /** @Ignore */ - $this->setMessageTemplate($messageTemplate); - $this->setParameters($parameters); - - parent::__construct($this->getBaseTranslation()); - } -} diff --git a/eZ/Publish/Core/Base/Exceptions/ForbiddenException.php b/eZ/Publish/Core/Base/Exceptions/ForbiddenException.php deleted file mode 100644 index 015ea37fef..0000000000 --- a/eZ/Publish/Core/Base/Exceptions/ForbiddenException.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Exceptions; - -use eZ\Publish\API\Repository\Exceptions\ForbiddenException as APIForbiddenException; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; - -/** - * Forbidden Exception implementation. - */ -class ForbiddenException extends APIForbiddenException implements Translatable -{ - use TranslatableBase; - - /** - * @param string $messageTemplate The message template, with placeholders for parameters. - * E.g. "Content with ID %contentId% could not be found". - * @param array $parameters Hash map with param placeholder as key and its corresponding value. - * E.g. array('%contentId%' => 123). - */ - public function __construct($messageTemplate, array $parameters = []) - { - /** @Ignore */ - $this->setMessageTemplate($messageTemplate); - $this->setParameters($parameters); - - parent::__construct($this->getBaseTranslation()); - } -} diff --git a/eZ/Publish/Core/Base/Exceptions/InvalidArgumentException.php b/eZ/Publish/Core/Base/Exceptions/InvalidArgumentException.php deleted file mode 100644 index 5b4090df10..0000000000 --- a/eZ/Publish/Core/Base/Exceptions/InvalidArgumentException.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Exceptions; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException as APIInvalidArgumentException; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; - -/** - * Invalid Argument Type Exception implementation. - * - * Usage: throw new InvalidArgumentException( 'nodes', 'array' ); - */ -class InvalidArgumentException extends APIInvalidArgumentException implements Translatable -{ - use TranslatableBase; - - /** - * Generates: "Argument '{$argumentName}' is invalid: {$whatIsWrong}". - * - * @param string $argumentName - * @param string $whatIsWrong - * @param \Exception|null $previous - */ - public function __construct($argumentName, $whatIsWrong, Exception $previous = null) - { - $this->setMessageTemplate("Argument '%argumentName%' is invalid: %whatIsWrong%"); - $this->setParameters(['%argumentName%' => $argumentName, '%whatIsWrong%' => $whatIsWrong]); - parent::__construct($this->getBaseTranslation(), 0, $previous); - } -} diff --git a/eZ/Publish/Core/Base/Exceptions/InvalidArgumentType.php b/eZ/Publish/Core/Base/Exceptions/InvalidArgumentType.php deleted file mode 100644 index fd4608d97a..0000000000 --- a/eZ/Publish/Core/Base/Exceptions/InvalidArgumentType.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Exceptions; - -use Exception; - -/** - * Invalid Argument Type Exception implementation. - * - * Usage: throw new InvalidArgument( 'nodes', 'array' ); - */ -class InvalidArgumentType extends InvalidArgumentException -{ - /** - * Generates: "Argument '{$argumentName}' is invalid: expected value to be of type '{$expectedType}'[, got '{$value}']". - * - * @param string $argumentName - * @param string $expectedType - * @param mixed|null $value Optionally to output the type that was received - * @param \Exception|null $previous - */ - public function __construct($argumentName, $expectedType, $value = null, Exception $previous = null) - { - $parameters = ['%argumentName%' => $argumentName, '%expectedType%' => $expectedType]; - $this->setMessageTemplate("Argument '%argumentName%' is invalid: value must be of type '%expectedType%'"); - if ($value) { - $this->setMessageTemplate("Argument '%argumentName%' is invalid: value must be of type '%expectedType%', not '%actualType%'"); - $actualType = is_object($value) ? get_class($value) : gettype($value); - $parameters['%actualType%'] = $actualType; - } - - /** @Ignore */ - $this->addParameters($parameters); - $this->message = $this->getBaseTranslation(); - } -} diff --git a/eZ/Publish/Core/Base/Exceptions/LimitationValidationException.php b/eZ/Publish/Core/Base/Exceptions/LimitationValidationException.php deleted file mode 100644 index 3e2a8f357d..0000000000 --- a/eZ/Publish/Core/Base/Exceptions/LimitationValidationException.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Exceptions; - -use eZ\Publish\API\Repository\Exceptions\LimitationValidationException as APILimitationValidationException; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; - -/** - * This Exception is thrown on create, update or assign policy or role - * when one or more given limitations are not valid. - */ -class LimitationValidationException extends APILimitationValidationException implements Translatable -{ - use TranslatableBase; - - /** - * Contains an array of limitation ValidationError objects. - * - * @var \eZ\Publish\Core\FieldType\ValidationError[] - */ - protected $errors; - - /** - * Generates: Limitations did not validate. - * - * Also sets the given $errors to the internal property, retrievable by getValidationErrors() - * - * @param \eZ\Publish\Core\FieldType\ValidationError[] $errors - */ - public function __construct(array $errors) - { - $this->validationErrors = $errors; - $this->setMessageTemplate('Limitations did not validate'); - parent::__construct($this->getBaseTranslation()); - } - - /** - * Returns an array of limitation ValidationError objects. - * - * @return \eZ\Publish\Core\FieldType\ValidationError[] - */ - public function getLimitationErrors() - { - return $this->errors; - } -} diff --git a/eZ/Publish/Core/Base/Exceptions/MissingUserFieldTypeException.php b/eZ/Publish/Core/Base/Exceptions/MissingUserFieldTypeException.php deleted file mode 100644 index 90dfa56f6a..0000000000 --- a/eZ/Publish/Core/Base/Exceptions/MissingUserFieldTypeException.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Exceptions; - -use eZ\Publish\API\Repository\Values\ContentType\ContentType; - -/** - * @internal - */ -final class MissingUserFieldTypeException extends ContentValidationException -{ - public function __construct(ContentType $contentType, string $fieldType) - { - parent::__construct( - 'The provided Content Type "%contentType%" does not contain the %fieldType% Field Type', - [ - 'contentType' => $contentType->identifier, - 'fieldType' => $fieldType, - ] - ); - } -} diff --git a/eZ/Publish/Core/Base/Exceptions/NotFoundException.php b/eZ/Publish/Core/Base/Exceptions/NotFoundException.php deleted file mode 100644 index 56c411322a..0000000000 --- a/eZ/Publish/Core/Base/Exceptions/NotFoundException.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Exceptions; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; - -/** - * Not Found Exception implementation. - * - * Use: - * throw new NotFound( 'Content', 42 ); - */ -class NotFoundException extends APINotFoundException implements Httpable, Translatable -{ - use TranslatableBase; - - /** - * Generates: Could not find '{$what}' with identifier '{$identifier}'. - * - * @param string $what - * @param mixed $identifier - * @param \Exception|null $previous - */ - public function __construct($what, $identifier, Exception $previous = null) - { - $identifierStr = is_string($identifier) ? $identifier : var_export($identifier, true); - $this->setMessageTemplate("Could not find '%what%' with identifier '%identifier%'"); - $this->setParameters(['%what%' => $what, '%identifier%' => $identifierStr]); - parent::__construct($this->getBaseTranslation(), self::NOT_FOUND, $previous); - } -} diff --git a/eZ/Publish/Core/Base/Exceptions/UnauthorizedException.php b/eZ/Publish/Core/Base/Exceptions/UnauthorizedException.php deleted file mode 100644 index 8442bcc003..0000000000 --- a/eZ/Publish/Core/Base/Exceptions/UnauthorizedException.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Exceptions; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException as APIUnauthorizedException; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; - -/** - * UnauthorizedException Exception implementation. - * - * Use: - * throw new UnauthorizedException( 'content', 'read', array( 'contentId' => 42 ) ); - */ -class UnauthorizedException extends APIUnauthorizedException implements Httpable, Translatable -{ - use TranslatableBase; - - /** - * Generates: User does not have access to '{$function}' '{$module}'[ with: %property.key% '%property.value%']. - * - * Example: User does not have access to 'read' 'content' with: id '44', type 'article' - * - * @param string $module The module name should be in sync with the name of the domain object in question - * @param string $function - * @param array $properties Key value pair with non sensitive data on what kind of data user does not have access to - * @param \Exception|null $previous - */ - public function __construct($module, $function, array $properties = null, Exception $previous = null) - { - $this->setMessageTemplate("The User does not have the '%function%' '%module%' permission"); - $this->setParameters(['%module%' => $module, '%function%' => $function]); - - if ($properties) { - $this->setMessageTemplate("The User does not have the '%function%' '%module%' permission with: %with%"); - $with = []; - foreach ($properties as $name => $value) { - $with[] = "{$name} '{$value}'"; - } - $this->addParameter('%with%', implode(', ', $with)); - } - - parent::__construct($this->getBaseTranslation(), self::UNAUTHORIZED, $previous); - } -} diff --git a/eZ/Publish/Core/Base/Tests/Container/Compiler/FieldTypeRegistryPassTest.php b/eZ/Publish/Core/Base/Tests/Container/Compiler/FieldTypeRegistryPassTest.php deleted file mode 100644 index ad0fb66419..0000000000 --- a/eZ/Publish/Core/Base/Tests/Container/Compiler/FieldTypeRegistryPassTest.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Tests\Container\Compiler; - -use eZ\Publish\Core\Base\Container\Compiler\FieldTypeRegistryPass; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; - -class FieldTypeRegistryPassTest extends AbstractCompilerPassTestCase -{ - protected function setUp(): void - { - parent::setUp(); - $this->setDefinition(FieldTypeRegistry::class, new Definition()); - } - - /** - * Register the compiler pass under test, just like you would do inside a bundle's load() - * method:. - * - * $container->addCompilerPass(new MyCompilerPass()); - */ - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new FieldTypeRegistryPass()); - } - - /** - * @dataProvider tagsProvider - */ - public function testRegisterFieldType(string $tag) - { - $fieldTypeIdentifier = 'field_type_identifier'; - $serviceId = 'service_id'; - $def = new Definition(); - $def->addTag($tag, ['alias' => $fieldTypeIdentifier]); - $this->setDefinition($serviceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - FieldTypeRegistry::class, - 'registerFieldType', - [$fieldTypeIdentifier, new Reference($serviceId)] - ); - } - - /** - * @dataProvider tagsProvider - * - * @param string $tag - */ - public function testRegisterFieldTypeNoAlias(string $tag) - { - $this->expectException(\LogicException::class); - - $fieldTypeIdentifier = 'field_type_identifier'; - $serviceId = 'service_id'; - $def = new Definition(); - $def->addTag($tag); - $this->setDefinition($serviceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - FieldTypeRegistry::class, - 'registerFieldType', - [$serviceId, $fieldTypeIdentifier] - ); - } - - public function tagsProvider(): array - { - return [ - [FieldTypeRegistryPass::DEPRECATED_FIELD_TYPE_SERVICE_TAG], - [FieldTypeRegistryPass::FIELD_TYPE_SERVICE_TAG], - ]; - } -} diff --git a/eZ/Publish/Core/Base/Tests/Container/Compiler/Search/FieldTypeRegistryPassTest.php b/eZ/Publish/Core/Base/Tests/Container/Compiler/Search/FieldTypeRegistryPassTest.php deleted file mode 100644 index 088d0440b6..0000000000 --- a/eZ/Publish/Core/Base/Tests/Container/Compiler/Search/FieldTypeRegistryPassTest.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Tests\Container\Compiler\Search; - -use eZ\Publish\Core\Base\Container\Compiler\Search\FieldRegistryPass; -use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; - -class FieldTypeRegistryPassTest extends AbstractCompilerPassTestCase -{ - protected function setUp(): void - { - parent::setUp(); - $this->setDefinition('ezpublish.search.common.field_registry', new Definition()); - } - - /** - * Register the compiler pass under test, just like you would do inside a bundle's load() - * method:. - * - * $container->addCompilerPass(new MyCompilerPass()); - */ - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new FieldRegistryPass()); - } - - public function testRegisterFieldType() - { - $fieldTypeIdentifier = 'field_type_identifier'; - $serviceId = 'service_id'; - $def = new Definition(); - $def->addTag('ezpublish.fieldType.indexable', ['alias' => $fieldTypeIdentifier]); - $this->setDefinition($serviceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.search.common.field_registry', - 'registerType', - [$fieldTypeIdentifier, new Reference($serviceId)] - ); - } - - public function testRegisterFieldTypeNoAlias() - { - $this->expectException(\LogicException::class); - - $fieldTypeIdentifier = 'field_type_identifier'; - $serviceId = 'service_id'; - $def = new Definition(); - $def->addTag('ezpublish.fieldType.indexable'); - $this->setDefinition($serviceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.search.common.field_registry', - 'registerType', - [$fieldTypeIdentifier, new Reference($serviceId)] - ); - } -} diff --git a/eZ/Publish/Core/Base/Tests/Container/Compiler/Search/Legacy/CriteriaConverterPassTest.php b/eZ/Publish/Core/Base/Tests/Container/Compiler/Search/Legacy/CriteriaConverterPassTest.php deleted file mode 100644 index 24981061d8..0000000000 --- a/eZ/Publish/Core/Base/Tests/Container/Compiler/Search/Legacy/CriteriaConverterPassTest.php +++ /dev/null @@ -1,133 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Tests\Container\Compiler\Search\Legacy; - -use eZ\Publish\Core\Base\Container\Compiler\Search\Legacy\CriteriaConverterPass; -use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; - -class CriteriaConverterPassTest extends AbstractCompilerPassTestCase -{ - /** - * Register the compiler pass under test, just like you would do inside a bundle's load() - * method:. - * - * $container->addCompilerPass(new MyCompilerPass()); - */ - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new CriteriaConverterPass()); - } - - public function testAddContentHandlers() - { - $this->setDefinition( - 'ezpublish.search.legacy.gateway.criteria_converter.content', - new Definition() - ); - - $serviceId = 'service_id'; - $def = new Definition(); - $def->addTag('ezpublish.search.legacy.gateway.criterion_handler.content'); - $this->setDefinition($serviceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.search.legacy.gateway.criteria_converter.content', - 'addHandler', - [new Reference($serviceId)] - ); - } - - public function testAddLocationHandlers() - { - $this->setDefinition( - 'ezpublish.search.legacy.gateway.criteria_converter.location', - new Definition() - ); - - $serviceId = 'service_id'; - $def = new Definition(); - $def->addTag('ezpublish.search.legacy.gateway.criterion_handler.location'); - $this->setDefinition($serviceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.search.legacy.gateway.criteria_converter.location', - 'addHandler', - [new Reference($serviceId)] - ); - } - - public function testAddTrashHandlers(): void - { - $this->setDefinition( - 'ezplatform.trash.search.legacy.gateway.criteria_converter', - new Definition() - ); - - $serviceId = 'service_id'; - $def = new Definition(); - $def->addTag('ezplatform.trash.search.legacy.gateway.criterion_handler'); - $this->setDefinition($serviceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezplatform.trash.search.legacy.gateway.criteria_converter', - 'addHandler', - [new Reference($serviceId)] - ); - } - - public function testAddMultipleHandlers(): void - { - $this->setDefinition( - 'ezpublish.search.legacy.gateway.criteria_converter.content', - new Definition() - ); - $this->setDefinition( - 'ezpublish.search.legacy.gateway.criteria_converter.location', - new Definition() - ); - $this->setDefinition( - 'ezplatform.trash.search.legacy.gateway.criteria_converter', - new Definition() - ); - - $commonServiceId = 'common_service_id'; - $def = new Definition(); - $def->addTag('ezpublish.search.legacy.gateway.criterion_handler.content'); - $def->addTag('ezpublish.search.legacy.gateway.criterion_handler.location'); - $def->addTag('ezplatform.trash.search.legacy.gateway.criterion_handler'); - $this->setDefinition($commonServiceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.search.legacy.gateway.criteria_converter.content', - 'addHandler', - [new Reference($commonServiceId)] - ); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.search.legacy.gateway.criteria_converter.location', - 'addHandler', - [new Reference($commonServiceId)] - ); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezplatform.trash.search.legacy.gateway.criteria_converter', - 'addHandler', - [new Reference($commonServiceId)] - ); - } -} diff --git a/eZ/Publish/Core/Base/Tests/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPassTest.php b/eZ/Publish/Core/Base/Tests/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPassTest.php deleted file mode 100644 index e5e641352d..0000000000 --- a/eZ/Publish/Core/Base/Tests/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPassTest.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Tests\Container\Compiler\Search\Legacy; - -use eZ\Publish\Core\Base\Container\Compiler\Search\Legacy\CriterionFieldValueHandlerRegistryPass; -use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; - -class CriterionFieldValueHandlerRegistryPassTest extends AbstractCompilerPassTestCase -{ - protected function setUp(): void - { - parent::setUp(); - $this->setDefinition( - 'ezpublish.search.legacy.gateway.criterion_field_value_handler.registry', - new Definition() - ); - } - - /** - * Register the compiler pass under test, just like you would do inside a bundle's load() - * method:. - * - * $container->addCompilerPass(new MyCompilerPass()); - */ - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new CriterionFieldValueHandlerRegistryPass()); - } - - public function testRegisterValueHandler() - { - $fieldTypeIdentifier = 'field_type_identifier'; - $serviceId = 'service_id'; - $def = new Definition(); - $def->addTag( - 'ezpublish.search.legacy.gateway.criterion_field_value_handler', - ['alias' => $fieldTypeIdentifier] - ); - $this->setDefinition($serviceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.search.legacy.gateway.criterion_field_value_handler.registry', - 'register', - [$fieldTypeIdentifier, new Reference($serviceId)] - ); - } - - public function testRegisterValueHandlerNoAlias() - { - $this->expectException(\LogicException::class); - - $fieldTypeIdentifier = 'field_type_identifier'; - $serviceId = 'service_id'; - $def = new Definition(); - $def->addTag('ezpublish.search.legacy.gateway.criterion_field_value_handler'); - $this->setDefinition($serviceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.search.legacy.gateway.criterion_field_value_handler.registry', - 'register', - [$fieldTypeIdentifier, new Reference($serviceId)] - ); - } -} diff --git a/eZ/Publish/Core/Base/Tests/Container/Compiler/Search/Legacy/SortClauseConverterPassTest.php b/eZ/Publish/Core/Base/Tests/Container/Compiler/Search/Legacy/SortClauseConverterPassTest.php deleted file mode 100644 index c158a74f4d..0000000000 --- a/eZ/Publish/Core/Base/Tests/Container/Compiler/Search/Legacy/SortClauseConverterPassTest.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Tests\Container\Compiler\Search\Legacy; - -use eZ\Publish\Core\Base\Container\Compiler\Search\Legacy\SortClauseConverterPass; -use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; - -class SortClauseConverterPassTest extends AbstractCompilerPassTestCase -{ - /** - * Register the compiler pass under test, just like you would do inside a bundle's load() - * method:. - * - * $container->addCompilerPass(new MyCompilerPass()); - */ - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new SortClauseConverterPass()); - } - - public function testAddContentHandlers() - { - $this->setDefinition( - 'ezpublish.search.legacy.gateway.sort_clause_converter.content', - new Definition() - ); - - $serviceId = 'service_id'; - $def = new Definition(); - $def->addTag('ezpublish.search.legacy.gateway.sort_clause_handler.content'); - $this->setDefinition($serviceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.search.legacy.gateway.sort_clause_converter.content', - 'addHandler', - [new Reference($serviceId)] - ); - } - - public function testAddLocationHandlers() - { - $this->setDefinition( - 'ezpublish.search.legacy.gateway.sort_clause_converter.location', - new Definition() - ); - - $serviceId = 'service_id'; - $def = new Definition(); - $def->addTag('ezpublish.search.legacy.gateway.sort_clause_handler.location'); - $this->setDefinition($serviceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.search.legacy.gateway.sort_clause_converter.location', - 'addHandler', - [new Reference($serviceId)] - ); - } - - public function testAddLocationAndContentHandlers() - { - $this->setDefinition( - 'ezpublish.search.legacy.gateway.sort_clause_converter.content', - new Definition() - ); - $this->setDefinition( - 'ezpublish.search.legacy.gateway.sort_clause_converter.location', - new Definition() - ); - - $commonServiceId = 'common_service_id'; - $def = new Definition(); - $def->addTag('ezpublish.search.legacy.gateway.sort_clause_handler.content'); - $def->addTag('ezpublish.search.legacy.gateway.sort_clause_handler.location'); - $this->setDefinition($commonServiceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.search.legacy.gateway.sort_clause_converter.content', - 'addHandler', - [new Reference($commonServiceId)] - ); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.search.legacy.gateway.sort_clause_converter.location', - 'addHandler', - [new Reference($commonServiceId)] - ); - } -} diff --git a/eZ/Publish/Core/Base/Tests/Container/Compiler/Storage/Legacy/RoleLimitationConverterPassTest.php b/eZ/Publish/Core/Base/Tests/Container/Compiler/Storage/Legacy/RoleLimitationConverterPassTest.php deleted file mode 100644 index 6cc9062ac7..0000000000 --- a/eZ/Publish/Core/Base/Tests/Container/Compiler/Storage/Legacy/RoleLimitationConverterPassTest.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Tests\Container\Compiler\Storage\Legacy; - -use eZ\Publish\Core\Base\Container\Compiler\Storage\Legacy\RoleLimitationConverterPass; -use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Definition; -use Symfony\Component\DependencyInjection\Reference; - -class RoleLimitationConverterPassTest extends AbstractCompilerPassTestCase -{ - protected function setUp(): void - { - parent::setUp(); - $this->setDefinition( - 'ezpublish.persistence.legacy.role.limitation.converter', - new Definition() - ); - } - - /** - * Register the compiler pass under test, just like you would do inside a bundle's load() - * method:. - * - * $container->addCompilerPass(new MyCompilerPass()); - */ - protected function registerCompilerPass(ContainerBuilder $container): void - { - $container->addCompilerPass(new RoleLimitationConverterPass()); - } - - public function testRegisterRoleLimitationConverter() - { - $serviceId = 'service_id'; - $def = new Definition(); - $def->addTag('ezpublish.persistence.legacy.role.limitation.handler'); - $this->setDefinition($serviceId, $def); - - $this->compile(); - - $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.persistence.legacy.role.limitation.converter', - 'addHandler', - [new Reference($serviceId)] - ); - } -} diff --git a/eZ/Publish/Core/Base/Tests/Container/Compiler/Stubs/GatewayBasedStorageHandler.php b/eZ/Publish/Core/Base/Tests/Container/Compiler/Stubs/GatewayBasedStorageHandler.php deleted file mode 100644 index d206391105..0000000000 --- a/eZ/Publish/Core/Base/Tests/Container/Compiler/Stubs/GatewayBasedStorageHandler.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Tests\Container\Compiler\Stubs; - -use eZ\Publish\Core\FieldType\GatewayBasedStorage; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Stub implementation of GatewayBasedStorage. - */ -class GatewayBasedStorageHandler extends GatewayBasedStorage -{ - public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context) - { - } - - public function getFieldData(VersionInfo $versionInfo, Field $field, array $context) - { - } - - public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context) - { - } - - public function hasFieldData() - { - } - - public function getIndexData(VersionInfo $versionInfo, Field $field, array $context) - { - } -} diff --git a/eZ/Publish/Core/Base/Tests/Container/Compiler/Stubs/GenericFieldType.php b/eZ/Publish/Core/Base/Tests/Container/Compiler/Stubs/GenericFieldType.php deleted file mode 100644 index 93a279e9fc..0000000000 --- a/eZ/Publish/Core/Base/Tests/Container/Compiler/Stubs/GenericFieldType.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Base\Tests\Container\Compiler\Stubs; - -use eZ\Publish\SPI\FieldType\Generic\Type; - -final class GenericFieldType extends Type -{ - public function getFieldTypeIdentifier(): string - { - return 'field_type_identifier'; - } -} diff --git a/eZ/Publish/Core/Base/Tests/PHPUnit5CompatTrait.php b/eZ/Publish/Core/Base/Tests/PHPUnit5CompatTrait.php deleted file mode 100644 index 4f50326644..0000000000 --- a/eZ/Publish/Core/Base/Tests/PHPUnit5CompatTrait.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Tests; - -/** - * Trait for PHPUnit 5 Forward Compatibility, for PHPUnit 4.8 use and up. - * - * @deprecated since 7.1, will be removed in 8.0. We are using PHPUnit 6, so this trait is obsolete. - * Trait was used with PHPUnit v5 and v4, so basically trait can be removed when support period for 6.7 ends. - */ -trait PHPUnit5CompatTrait -{ - /** - * @deprecated Since PHPUnit 5.4, marked as deprecated here to make it clear when working on 6.7/5.4 branches - * {@inheritdoc} - */ - public function getMock($originalClassName, $methods = [], array $arguments = [], $mockClassName = '', $callOriginalConstructor = true, $callOriginalClone = true, $callAutoload = true, $cloneArguments = false, $callOriginalMethods = false, $proxyTarget = null) - { - return parent::getMock( - $originalClassName, - $methods, - $arguments, - $mockClassName, - $callOriginalConstructor, - $callOriginalClone, - $callAutoload, - $cloneArguments, - $callOriginalMethods, - $proxyTarget - ); - } -} diff --git a/eZ/Publish/Core/Base/Translatable.php b/eZ/Publish/Core/Base/Translatable.php deleted file mode 100644 index 6fabf0346a..0000000000 --- a/eZ/Publish/Core/Base/Translatable.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base; - -/** - * Interface for translatable value objects. - */ -interface Translatable -{ - /** - * Returns the message template, with placeholders for parameters. - * E.g. "Content with ID %contentId% could not be found". - * - * @return string - */ - public function getMessageTemplate(); - - /** - * Injects the message template. - * - * @param string $messageTemplate - */ - public function setMessageTemplate($messageTemplate); - - /** - * Returns a hash map with param placeholder as key and its corresponding value. - * E.g. array('%contentId%' => 123). - * - * @return array - */ - public function getParameters(); - - /** - * Injects the hash map, with param placeholder as key and its corresponding value. - * E.g. array('%contentId%' => 123). - * If parameters already existed, they will be replaced by the passed here. - * - * @param array $parameters - */ - public function setParameters(array $parameters); - - /** - * Adds a parameter to existing hash map. - * - * @param string $name - * @param string $value - */ - public function addParameter($name, $value); - - /** - * Adds $parameters to existing hash map. - * - * @param array $parameters - */ - public function addParameters(array $parameters); - - /** - * Returns base translation, computed with message template and parameters. - * - * @return string - */ - public function getBaseTranslation(); -} diff --git a/eZ/Publish/Core/Base/Utils/DeprecationWarner.php b/eZ/Publish/Core/Base/Utils/DeprecationWarner.php deleted file mode 100644 index ac83c3c09a..0000000000 --- a/eZ/Publish/Core/Base/Utils/DeprecationWarner.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Base\Utils; - -class DeprecationWarner implements DeprecationWarnerInterface -{ - public function log($message) - { - @trigger_error($message, E_USER_DEPRECATED); - } -} diff --git a/eZ/Publish/Core/Event/BookmarkService.php b/eZ/Publish/Core/Event/BookmarkService.php deleted file mode 100644 index 3028f5095e..0000000000 --- a/eZ/Publish/Core/Event/BookmarkService.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\BookmarkService as BookmarkServiceInterface; -use eZ\Publish\API\Repository\Events\Bookmark\BeforeCreateBookmarkEvent; -use eZ\Publish\API\Repository\Events\Bookmark\BeforeDeleteBookmarkEvent; -use eZ\Publish\API\Repository\Events\Bookmark\CreateBookmarkEvent; -use eZ\Publish\API\Repository\Events\Bookmark\DeleteBookmarkEvent; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\SPI\Repository\Decorator\BookmarkServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class BookmarkService extends BookmarkServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - BookmarkServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function createBookmark(Location $location): void - { - $eventData = [$location]; - - $beforeEvent = new BeforeCreateBookmarkEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->createBookmark($location); - - $this->eventDispatcher->dispatch(new CreateBookmarkEvent(...$eventData)); - } - - public function deleteBookmark(Location $location): void - { - $eventData = [$location]; - - $beforeEvent = new BeforeDeleteBookmarkEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteBookmark($location); - - $this->eventDispatcher->dispatch(new DeleteBookmarkEvent(...$eventData)); - } -} diff --git a/eZ/Publish/Core/Event/ContentService.php b/eZ/Publish/Core/Event/ContentService.php deleted file mode 100644 index 4516e825dd..0000000000 --- a/eZ/Publish/Core/Event/ContentService.php +++ /dev/null @@ -1,383 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\ContentService as ContentServiceInterface; -use eZ\Publish\API\Repository\Events\Content\AddRelationEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeAddRelationEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeCopyContentEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeCreateContentDraftEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeCreateContentEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeDeleteContentEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeDeleteRelationEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeDeleteTranslationEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeDeleteVersionEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeHideContentEvent; -use eZ\Publish\API\Repository\Events\Content\BeforePublishVersionEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeRevealContentEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeUpdateContentEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeUpdateContentMetadataEvent; -use eZ\Publish\API\Repository\Events\Content\CopyContentEvent; -use eZ\Publish\API\Repository\Events\Content\CreateContentDraftEvent; -use eZ\Publish\API\Repository\Events\Content\CreateContentEvent; -use eZ\Publish\API\Repository\Events\Content\DeleteContentEvent; -use eZ\Publish\API\Repository\Events\Content\DeleteRelationEvent; -use eZ\Publish\API\Repository\Events\Content\DeleteTranslationEvent; -use eZ\Publish\API\Repository\Events\Content\DeleteVersionEvent; -use eZ\Publish\API\Repository\Events\Content\HideContentEvent; -use eZ\Publish\API\Repository\Events\Content\PublishVersionEvent; -use eZ\Publish\API\Repository\Events\Content\RevealContentEvent; -use eZ\Publish\API\Repository\Events\Content\UpdateContentEvent; -use eZ\Publish\API\Repository\Events\Content\UpdateContentMetadataEvent; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\SPI\Repository\Decorator\ContentServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class ContentService extends ContentServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - ContentServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function createContent( - ContentCreateStruct $contentCreateStruct, - array $locationCreateStructs = [], - ?array $fieldIdentifiersToValidate = null - ): Content { - $eventData = [ - $contentCreateStruct, - $locationCreateStructs, - $fieldIdentifiersToValidate, - ]; - - $beforeEvent = new BeforeCreateContentEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getContent(); - } - - $content = $beforeEvent->hasContent() - ? $beforeEvent->getContent() - : $this->innerService->createContent($contentCreateStruct, $locationCreateStructs, $fieldIdentifiersToValidate); - - $this->eventDispatcher->dispatch( - new CreateContentEvent($content, ...$eventData) - ); - - return $content; - } - - public function updateContentMetadata( - ContentInfo $contentInfo, - ContentMetadataUpdateStruct $contentMetadataUpdateStruct - ): Content { - $eventData = [ - $contentInfo, - $contentMetadataUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdateContentMetadataEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getContent(); - } - - $content = $beforeEvent->hasContent() - ? $beforeEvent->getContent() - : $this->innerService->updateContentMetadata($contentInfo, $contentMetadataUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdateContentMetadataEvent($content, ...$eventData) - ); - - return $content; - } - - public function deleteContent(ContentInfo $contentInfo): iterable - { - $eventData = [$contentInfo]; - - $beforeEvent = new BeforeDeleteContentEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getLocations(); - } - - $locations = $beforeEvent->hasLocations() - ? $beforeEvent->getLocations() - : $this->innerService->deleteContent($contentInfo); - - $this->eventDispatcher->dispatch( - new DeleteContentEvent($locations, ...$eventData) - ); - - return $locations; - } - - public function createContentDraft( - ContentInfo $contentInfo, - ?VersionInfo $versionInfo = null, - ?User $creator = null, - ?Language $language = null - ): Content { - $eventData = [ - $contentInfo, - $versionInfo, - $creator, - $language, - ]; - - $beforeEvent = new BeforeCreateContentDraftEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getContentDraft(); - } - - $contentDraft = $beforeEvent->hasContentDraft() - ? $beforeEvent->getContentDraft() - : $this->innerService->createContentDraft($contentInfo, $versionInfo, $creator, $language); - - $this->eventDispatcher->dispatch( - new CreateContentDraftEvent($contentDraft, ...$eventData) - ); - - return $contentDraft; - } - - public function updateContent( - VersionInfo $versionInfo, - ContentUpdateStruct $contentUpdateStruct, - ?array $fieldIdentifiersToValidate = null - ): Content { - $eventData = [ - $versionInfo, - $contentUpdateStruct, - $fieldIdentifiersToValidate, - ]; - - $beforeEvent = new BeforeUpdateContentEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getContent(); - } - - $content = $beforeEvent->hasContent() - ? $beforeEvent->getContent() - : $this->innerService->updateContent($versionInfo, $contentUpdateStruct, $fieldIdentifiersToValidate); - - $this->eventDispatcher->dispatch( - new UpdateContentEvent($content, ...$eventData) - ); - - return $content; - } - - public function publishVersion(VersionInfo $versionInfo, array $translations = Language::ALL): Content - { - $eventData = [ - $versionInfo, - $translations, - ]; - - $beforeEvent = new BeforePublishVersionEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getContent(); - } - - $content = $beforeEvent->hasContent() - ? $beforeEvent->getContent() - : $this->innerService->publishVersion($versionInfo, $translations); - - $this->eventDispatcher->dispatch( - new PublishVersionEvent($content, ...$eventData) - ); - - return $content; - } - - public function deleteVersion(VersionInfo $versionInfo): void - { - $eventData = [$versionInfo]; - - $beforeEvent = new BeforeDeleteVersionEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteVersion($versionInfo); - - $this->eventDispatcher->dispatch( - new DeleteVersionEvent(...$eventData) - ); - } - - public function copyContent( - ContentInfo $contentInfo, - LocationCreateStruct $destinationLocationCreateStruct, - ?VersionInfo $versionInfo = null - ): Content { - $eventData = [ - $contentInfo, - $destinationLocationCreateStruct, - $versionInfo, - ]; - - $beforeEvent = new BeforeCopyContentEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getContent(); - } - - $content = $beforeEvent->hasContent() - ? $beforeEvent->getContent() - : $this->innerService->copyContent($contentInfo, $destinationLocationCreateStruct, $versionInfo); - - $this->eventDispatcher->dispatch( - new CopyContentEvent($content, ...$eventData) - ); - - return $content; - } - - public function addRelation( - VersionInfo $sourceVersion, - ContentInfo $destinationContent - ): Relation { - $eventData = [ - $sourceVersion, - $destinationContent, - ]; - - $beforeEvent = new BeforeAddRelationEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getRelation(); - } - - $relation = $beforeEvent->hasRelation() - ? $beforeEvent->getRelation() - : $this->innerService->addRelation($sourceVersion, $destinationContent); - - $this->eventDispatcher->dispatch( - new AddRelationEvent($relation, ...$eventData) - ); - - return $relation; - } - - public function deleteRelation( - VersionInfo $sourceVersion, - ContentInfo $destinationContent - ): void { - $eventData = [ - $sourceVersion, - $destinationContent, - ]; - - $beforeEvent = new BeforeDeleteRelationEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteRelation($sourceVersion, $destinationContent); - - $this->eventDispatcher->dispatch( - new DeleteRelationEvent(...$eventData) - ); - } - - public function deleteTranslation( - ContentInfo $contentInfo, - string $languageCode - ): void { - $eventData = [ - $contentInfo, - $languageCode, - ]; - - $beforeEvent = new BeforeDeleteTranslationEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteTranslation($contentInfo, $languageCode); - - $this->eventDispatcher->dispatch( - new DeleteTranslationEvent(...$eventData) - ); - } - - public function hideContent(ContentInfo $contentInfo): void - { - $eventData = [$contentInfo]; - - $beforeEvent = new BeforeHideContentEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->hideContent($contentInfo); - - $this->eventDispatcher->dispatch( - new HideContentEvent(...$eventData) - ); - } - - public function revealContent(ContentInfo $contentInfo): void - { - $eventData = [$contentInfo]; - - $beforeEvent = new BeforeRevealContentEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->revealContent($contentInfo); - - $this->eventDispatcher->dispatch( - new RevealContentEvent(...$eventData) - ); - } -} diff --git a/eZ/Publish/Core/Event/ContentTypeService.php b/eZ/Publish/Core/Event/ContentTypeService.php deleted file mode 100644 index f8ab782852..0000000000 --- a/eZ/Publish/Core/Event/ContentTypeService.php +++ /dev/null @@ -1,411 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\ContentTypeService as ContentTypeServiceInterface; -use eZ\Publish\API\Repository\Events\ContentType\AddFieldDefinitionEvent; -use eZ\Publish\API\Repository\Events\ContentType\AssignContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeAddFieldDefinitionEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeAssignContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeCopyContentTypeEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeCreateContentTypeDraftEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeCreateContentTypeEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeCreateContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeDeleteContentTypeEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeDeleteContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforePublishContentTypeDraftEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeRemoveContentTypeTranslationEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeRemoveFieldDefinitionEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeUnassignContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeUpdateContentTypeDraftEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeUpdateContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeUpdateFieldDefinitionEvent; -use eZ\Publish\API\Repository\Events\ContentType\CopyContentTypeEvent; -use eZ\Publish\API\Repository\Events\ContentType\CreateContentTypeDraftEvent; -use eZ\Publish\API\Repository\Events\ContentType\CreateContentTypeEvent; -use eZ\Publish\API\Repository\Events\ContentType\CreateContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\DeleteContentTypeEvent; -use eZ\Publish\API\Repository\Events\ContentType\DeleteContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\PublishContentTypeDraftEvent; -use eZ\Publish\API\Repository\Events\ContentType\RemoveContentTypeTranslationEvent; -use eZ\Publish\API\Repository\Events\ContentType\RemoveFieldDefinitionEvent; -use eZ\Publish\API\Repository\Events\ContentType\UnassignContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\UpdateContentTypeDraftEvent; -use eZ\Publish\API\Repository\Events\ContentType\UpdateContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\UpdateFieldDefinitionEvent; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\SPI\Repository\Decorator\ContentTypeServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class ContentTypeService extends ContentTypeServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - ContentTypeServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function createContentTypeGroup(ContentTypeGroupCreateStruct $contentTypeGroupCreateStruct): ContentTypeGroup - { - $eventData = [$contentTypeGroupCreateStruct]; - - $beforeEvent = new BeforeCreateContentTypeGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getContentTypeGroup(); - } - - $contentTypeGroup = $beforeEvent->hasContentTypeGroup() - ? $beforeEvent->getContentTypeGroup() - : $this->innerService->createContentTypeGroup($contentTypeGroupCreateStruct); - - $this->eventDispatcher->dispatch( - new CreateContentTypeGroupEvent($contentTypeGroup, ...$eventData) - ); - - return $contentTypeGroup; - } - - public function updateContentTypeGroup( - ContentTypeGroup $contentTypeGroup, - ContentTypeGroupUpdateStruct $contentTypeGroupUpdateStruct - ): void { - $eventData = [ - $contentTypeGroup, - $contentTypeGroupUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdateContentTypeGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->updateContentTypeGroup($contentTypeGroup, $contentTypeGroupUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdateContentTypeGroupEvent(...$eventData) - ); - } - - public function deleteContentTypeGroup(ContentTypeGroup $contentTypeGroup): void - { - $eventData = [$contentTypeGroup]; - - $beforeEvent = new BeforeDeleteContentTypeGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteContentTypeGroup($contentTypeGroup); - - $this->eventDispatcher->dispatch( - new DeleteContentTypeGroupEvent(...$eventData) - ); - } - - public function createContentType( - ContentTypeCreateStruct $contentTypeCreateStruct, - array $contentTypeGroups - ): ContentTypeDraft { - $eventData = [ - $contentTypeCreateStruct, - $contentTypeGroups, - ]; - - $beforeEvent = new BeforeCreateContentTypeEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getContentTypeDraft(); - } - - $contentTypeDraft = $beforeEvent->hasContentTypeDraft() - ? $beforeEvent->getContentTypeDraft() - : $this->innerService->createContentType($contentTypeCreateStruct, $contentTypeGroups); - - $this->eventDispatcher->dispatch( - new CreateContentTypeEvent($contentTypeDraft, ...$eventData) - ); - - return $contentTypeDraft; - } - - public function createContentTypeDraft(ContentType $contentType): ContentTypeDraft - { - $eventData = [$contentType]; - - $beforeEvent = new BeforeCreateContentTypeDraftEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getContentTypeDraft(); - } - - $contentTypeDraft = $beforeEvent->hasContentTypeDraft() - ? $beforeEvent->getContentTypeDraft() - : $this->innerService->createContentTypeDraft($contentType); - - $this->eventDispatcher->dispatch( - new CreateContentTypeDraftEvent($contentTypeDraft, ...$eventData) - ); - - return $contentTypeDraft; - } - - public function updateContentTypeDraft( - ContentTypeDraft $contentTypeDraft, - ContentTypeUpdateStruct $contentTypeUpdateStruct - ): void { - $eventData = [ - $contentTypeDraft, - $contentTypeUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdateContentTypeDraftEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->updateContentTypeDraft($contentTypeDraft, $contentTypeUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdateContentTypeDraftEvent(...$eventData) - ); - } - - public function deleteContentType(ContentType $contentType): void - { - $eventData = [$contentType]; - - $beforeEvent = new BeforeDeleteContentTypeEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteContentType($contentType); - - $this->eventDispatcher->dispatch( - new DeleteContentTypeEvent(...$eventData) - ); - } - - public function copyContentType( - ContentType $contentType, - User $creator = null - ): ContentType { - $eventData = [ - $contentType, - $creator, - ]; - - $beforeEvent = new BeforeCopyContentTypeEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getContentTypeCopy(); - } - - $contentTypeCopy = $beforeEvent->hasContentTypeCopy() - ? $beforeEvent->getContentTypeCopy() - : $this->innerService->copyContentType($contentType, $creator); - - $this->eventDispatcher->dispatch( - new CopyContentTypeEvent($contentTypeCopy, ...$eventData) - ); - - return $contentTypeCopy; - } - - public function assignContentTypeGroup( - ContentType $contentType, - ContentTypeGroup $contentTypeGroup - ): void { - $eventData = [ - $contentType, - $contentTypeGroup, - ]; - - $beforeEvent = new BeforeAssignContentTypeGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->assignContentTypeGroup($contentType, $contentTypeGroup); - - $this->eventDispatcher->dispatch( - new AssignContentTypeGroupEvent(...$eventData) - ); - } - - public function unassignContentTypeGroup( - ContentType $contentType, - ContentTypeGroup $contentTypeGroup - ): void { - $eventData = [ - $contentType, - $contentTypeGroup, - ]; - - $beforeEvent = new BeforeUnassignContentTypeGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->unassignContentTypeGroup($contentType, $contentTypeGroup); - - $this->eventDispatcher->dispatch( - new UnassignContentTypeGroupEvent(...$eventData) - ); - } - - public function addFieldDefinition( - ContentTypeDraft $contentTypeDraft, - FieldDefinitionCreateStruct $fieldDefinitionCreateStruct - ): void { - $eventData = [ - $contentTypeDraft, - $fieldDefinitionCreateStruct, - ]; - - $beforeEvent = new BeforeAddFieldDefinitionEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->addFieldDefinition($contentTypeDraft, $fieldDefinitionCreateStruct); - - $this->eventDispatcher->dispatch( - new AddFieldDefinitionEvent(...$eventData) - ); - } - - public function removeFieldDefinition( - ContentTypeDraft $contentTypeDraft, - FieldDefinition $fieldDefinition - ): void { - $eventData = [ - $contentTypeDraft, - $fieldDefinition, - ]; - - $beforeEvent = new BeforeRemoveFieldDefinitionEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->removeFieldDefinition($contentTypeDraft, $fieldDefinition); - - $this->eventDispatcher->dispatch( - new RemoveFieldDefinitionEvent(...$eventData) - ); - } - - public function updateFieldDefinition( - ContentTypeDraft $contentTypeDraft, - FieldDefinition $fieldDefinition, - FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct - ): void { - $eventData = [ - $contentTypeDraft, - $fieldDefinition, - $fieldDefinitionUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdateFieldDefinitionEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->updateFieldDefinition($contentTypeDraft, $fieldDefinition, $fieldDefinitionUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdateFieldDefinitionEvent(...$eventData) - ); - } - - public function publishContentTypeDraft(ContentTypeDraft $contentTypeDraft): void - { - $eventData = [$contentTypeDraft]; - - $beforeEvent = new BeforePublishContentTypeDraftEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->publishContentTypeDraft($contentTypeDraft); - - $this->eventDispatcher->dispatch( - new PublishContentTypeDraftEvent(...$eventData) - ); - } - - public function removeContentTypeTranslation( - ContentTypeDraft $contentTypeDraft, - string $languageCode - ): ContentTypeDraft { - $eventData = [ - $contentTypeDraft, - $languageCode, - ]; - - $beforeEvent = new BeforeRemoveContentTypeTranslationEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getNewContentTypeDraft(); - } - - $newContentTypeDraft = $beforeEvent->hasNewContentTypeDraft() - ? $beforeEvent->getNewContentTypeDraft() - : $this->innerService->removeContentTypeTranslation($contentTypeDraft, $languageCode); - - $this->eventDispatcher->dispatch( - new RemoveContentTypeTranslationEvent($newContentTypeDraft, ...$eventData) - ); - - return $newContentTypeDraft; - } -} diff --git a/eZ/Publish/Core/Event/FieldTypeService.php b/eZ/Publish/Core/Event/FieldTypeService.php deleted file mode 100644 index 9824d29176..0000000000 --- a/eZ/Publish/Core/Event/FieldTypeService.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\FieldTypeService as FieldTypeServiceInterface; -use eZ\Publish\SPI\Repository\Decorator\FieldTypeServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class FieldTypeService extends FieldTypeServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - FieldTypeServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } -} diff --git a/eZ/Publish/Core/Event/LanguageService.php b/eZ/Publish/Core/Event/LanguageService.php deleted file mode 100644 index 87bae55f6b..0000000000 --- a/eZ/Publish/Core/Event/LanguageService.php +++ /dev/null @@ -1,151 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\Events\Language\BeforeCreateLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\BeforeDeleteLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\BeforeDisableLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\BeforeEnableLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\BeforeUpdateLanguageNameEvent; -use eZ\Publish\API\Repository\Events\Language\CreateLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\DeleteLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\DisableLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\EnableLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\UpdateLanguageNameEvent; -use eZ\Publish\API\Repository\LanguageService as LanguageServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\LanguageCreateStruct; -use eZ\Publish\SPI\Repository\Decorator\LanguageServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class LanguageService extends LanguageServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - LanguageServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function createLanguage(LanguageCreateStruct $languageCreateStruct): Language - { - $eventData = [$languageCreateStruct]; - - $beforeEvent = new BeforeCreateLanguageEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getLanguage(); - } - - $language = $beforeEvent->hasLanguage() - ? $beforeEvent->getLanguage() - : $this->innerService->createLanguage($languageCreateStruct); - - $this->eventDispatcher->dispatch( - new CreateLanguageEvent($language, ...$eventData) - ); - - return $language; - } - - public function updateLanguageName( - Language $language, - string $newName - ): Language { - $eventData = [ - $language, - $newName, - ]; - - $beforeEvent = new BeforeUpdateLanguageNameEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedLanguage(); - } - - $updatedLanguage = $beforeEvent->hasUpdatedLanguage() - ? $beforeEvent->getUpdatedLanguage() - : $this->innerService->updateLanguageName($language, $newName); - - $this->eventDispatcher->dispatch( - new UpdateLanguageNameEvent($updatedLanguage, ...$eventData) - ); - - return $updatedLanguage; - } - - public function enableLanguage(Language $language): Language - { - $eventData = [$language]; - - $beforeEvent = new BeforeEnableLanguageEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getEnabledLanguage(); - } - - $enabledLanguage = $beforeEvent->hasEnabledLanguage() - ? $beforeEvent->getEnabledLanguage() - : $this->innerService->enableLanguage($language); - - $this->eventDispatcher->dispatch( - new EnableLanguageEvent($enabledLanguage, ...$eventData) - ); - - return $enabledLanguage; - } - - public function disableLanguage(Language $language): Language - { - $eventData = [$language]; - - $beforeEvent = new BeforeDisableLanguageEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getDisabledLanguage(); - } - - $disabledLanguage = $beforeEvent->hasDisabledLanguage() - ? $beforeEvent->getDisabledLanguage() - : $this->innerService->disableLanguage($language); - - $this->eventDispatcher->dispatch( - new DisableLanguageEvent($disabledLanguage, ...$eventData) - ); - - return $disabledLanguage; - } - - public function deleteLanguage(Language $language): void - { - $eventData = [$language]; - - $beforeEvent = new BeforeDeleteLanguageEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteLanguage($language); - - $this->eventDispatcher->dispatch( - new DeleteLanguageEvent(...$eventData) - ); - } -} diff --git a/eZ/Publish/Core/Event/LocationService.php b/eZ/Publish/Core/Event/LocationService.php deleted file mode 100644 index 058fca4771..0000000000 --- a/eZ/Publish/Core/Event/LocationService.php +++ /dev/null @@ -1,237 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\Events\Location\BeforeCopySubtreeEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeCreateLocationEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeDeleteLocationEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeHideLocationEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeMoveSubtreeEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeSwapLocationEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeUnhideLocationEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeUpdateLocationEvent; -use eZ\Publish\API\Repository\Events\Location\CopySubtreeEvent; -use eZ\Publish\API\Repository\Events\Location\CreateLocationEvent; -use eZ\Publish\API\Repository\Events\Location\DeleteLocationEvent; -use eZ\Publish\API\Repository\Events\Location\HideLocationEvent; -use eZ\Publish\API\Repository\Events\Location\MoveSubtreeEvent; -use eZ\Publish\API\Repository\Events\Location\SwapLocationEvent; -use eZ\Publish\API\Repository\Events\Location\UnhideLocationEvent; -use eZ\Publish\API\Repository\Events\Location\UpdateLocationEvent; -use eZ\Publish\API\Repository\LocationService as LocationServiceInterface; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct; -use eZ\Publish\SPI\Repository\Decorator\LocationServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class LocationService extends LocationServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - LocationServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function copySubtree( - Location $subtree, - Location $targetParentLocation - ): Location { - $eventData = [ - $subtree, - $targetParentLocation, - ]; - - $beforeEvent = new BeforeCopySubtreeEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getLocation(); - } - - $location = $beforeEvent->hasLocation() - ? $beforeEvent->getLocation() - : $this->innerService->copySubtree($subtree, $targetParentLocation); - - $this->eventDispatcher->dispatch( - new CopySubtreeEvent($location, ...$eventData) - ); - - return $location; - } - - public function createLocation( - ContentInfo $contentInfo, - LocationCreateStruct $locationCreateStruct - ): Location { - $eventData = [ - $contentInfo, - $locationCreateStruct, - ]; - - $beforeEvent = new BeforeCreateLocationEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getLocation(); - } - - $location = $beforeEvent->hasLocation() - ? $beforeEvent->getLocation() - : $this->innerService->createLocation($contentInfo, $locationCreateStruct); - - $this->eventDispatcher->dispatch( - new CreateLocationEvent($location, ...$eventData) - ); - - return $location; - } - - public function updateLocation( - Location $location, - LocationUpdateStruct $locationUpdateStruct - ): Location { - $eventData = [ - $location, - $locationUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdateLocationEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedLocation(); - } - - $updatedLocation = $beforeEvent->hasUpdatedLocation() - ? $beforeEvent->getUpdatedLocation() - : $this->innerService->updateLocation($location, $locationUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdateLocationEvent($updatedLocation, ...$eventData) - ); - - return $updatedLocation; - } - - public function swapLocation( - Location $location1, - Location $location2 - ): void { - $eventData = [ - $location1, - $location2, - ]; - - $beforeEvent = new BeforeSwapLocationEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->swapLocation($location1, $location2); - - $this->eventDispatcher->dispatch( - new SwapLocationEvent(...$eventData) - ); - } - - public function hideLocation(Location $location): Location - { - $eventData = [$location]; - - $beforeEvent = new BeforeHideLocationEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getHiddenLocation(); - } - - $hiddenLocation = $beforeEvent->hasHiddenLocation() - ? $beforeEvent->getHiddenLocation() - : $this->innerService->hideLocation($location); - - $this->eventDispatcher->dispatch( - new HideLocationEvent($hiddenLocation, ...$eventData) - ); - - return $hiddenLocation; - } - - public function unhideLocation(Location $location): Location - { - $eventData = [$location]; - - $beforeEvent = new BeforeUnhideLocationEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getRevealedLocation(); - } - - $revealedLocation = $beforeEvent->hasRevealedLocation() - ? $beforeEvent->getRevealedLocation() - : $this->innerService->unhideLocation($location); - - $this->eventDispatcher->dispatch( - new UnhideLocationEvent($revealedLocation, ...$eventData) - ); - - return $revealedLocation; - } - - public function moveSubtree( - Location $location, - Location $newParentLocation - ): void { - $eventData = [ - $location, - $newParentLocation, - ]; - - $beforeEvent = new BeforeMoveSubtreeEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->moveSubtree($location, $newParentLocation); - - $this->eventDispatcher->dispatch( - new MoveSubtreeEvent(...$eventData) - ); - } - - public function deleteLocation(Location $location): void - { - $eventData = [$location]; - - $beforeEvent = new BeforeDeleteLocationEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteLocation($location); - - $this->eventDispatcher->dispatch( - new DeleteLocationEvent(...$eventData) - ); - } -} diff --git a/eZ/Publish/Core/Event/NotificationService.php b/eZ/Publish/Core/Event/NotificationService.php deleted file mode 100644 index 5a1675fc7f..0000000000 --- a/eZ/Publish/Core/Event/NotificationService.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\Events\Notification\BeforeCreateNotificationEvent; -use eZ\Publish\API\Repository\Events\Notification\BeforeDeleteNotificationEvent; -use eZ\Publish\API\Repository\Events\Notification\BeforeMarkNotificationAsReadEvent; -use eZ\Publish\API\Repository\Events\Notification\CreateNotificationEvent; -use eZ\Publish\API\Repository\Events\Notification\DeleteNotificationEvent; -use eZ\Publish\API\Repository\Events\Notification\MarkNotificationAsReadEvent; -use eZ\Publish\API\Repository\NotificationService as NotificationServiceInterface; -use eZ\Publish\API\Repository\Values\Notification\CreateStruct; -use eZ\Publish\API\Repository\Values\Notification\Notification; -use eZ\Publish\SPI\Repository\Decorator\NotificationServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class NotificationService extends NotificationServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - NotificationServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function markNotificationAsRead(Notification $notification): void - { - $eventData = [$notification]; - - $beforeEvent = new BeforeMarkNotificationAsReadEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->markNotificationAsRead($notification); - - $this->eventDispatcher->dispatch( - new MarkNotificationAsReadEvent(...$eventData) - ); - } - - public function createNotification(CreateStruct $createStruct): Notification - { - $eventData = [$createStruct]; - - $beforeEvent = new BeforeCreateNotificationEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getNotification(); - } - - $notification = $beforeEvent->hasNotification() - ? $beforeEvent->getNotification() - : $this->innerService->createNotification($createStruct); - - $this->eventDispatcher->dispatch( - new CreateNotificationEvent($notification, ...$eventData) - ); - - return $notification; - } - - public function deleteNotification(Notification $notification): void - { - $eventData = [$notification]; - - $beforeEvent = new BeforeDeleteNotificationEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteNotification($notification); - - $this->eventDispatcher->dispatch( - new DeleteNotificationEvent(...$eventData) - ); - } -} diff --git a/eZ/Publish/Core/Event/ObjectStateService.php b/eZ/Publish/Core/Event/ObjectStateService.php deleted file mode 100644 index 10ed436b94..0000000000 --- a/eZ/Publish/Core/Event/ObjectStateService.php +++ /dev/null @@ -1,238 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\Events\ObjectState\BeforeCreateObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeCreateObjectStateGroupEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeDeleteObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeDeleteObjectStateGroupEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeSetContentStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeSetPriorityOfObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeUpdateObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeUpdateObjectStateGroupEvent; -use eZ\Publish\API\Repository\Events\ObjectState\CreateObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\CreateObjectStateGroupEvent; -use eZ\Publish\API\Repository\Events\ObjectState\DeleteObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\DeleteObjectStateGroupEvent; -use eZ\Publish\API\Repository\Events\ObjectState\SetContentStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\SetPriorityOfObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\UpdateObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\UpdateObjectStateGroupEvent; -use eZ\Publish\API\Repository\ObjectStateService as ObjectStateServiceInterface; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectState; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateUpdateStruct; -use eZ\Publish\SPI\Repository\Decorator\ObjectStateServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class ObjectStateService extends ObjectStateServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - ObjectStateServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function createObjectStateGroup(ObjectStateGroupCreateStruct $objectStateGroupCreateStruct): ObjectStateGroup - { - $eventData = [$objectStateGroupCreateStruct]; - - $beforeEvent = new BeforeCreateObjectStateGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getObjectStateGroup(); - } - - $objectStateGroup = $beforeEvent->hasObjectStateGroup() - ? $beforeEvent->getObjectStateGroup() - : $this->innerService->createObjectStateGroup($objectStateGroupCreateStruct); - - $this->eventDispatcher->dispatch( - new CreateObjectStateGroupEvent($objectStateGroup, ...$eventData) - ); - - return $objectStateGroup; - } - - public function updateObjectStateGroup( - ObjectStateGroup $objectStateGroup, - ObjectStateGroupUpdateStruct $objectStateGroupUpdateStruct - ): ObjectStateGroup { - $eventData = [ - $objectStateGroup, - $objectStateGroupUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdateObjectStateGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedObjectStateGroup(); - } - - $updatedObjectStateGroup = $beforeEvent->hasUpdatedObjectStateGroup() - ? $beforeEvent->getUpdatedObjectStateGroup() - : $this->innerService->updateObjectStateGroup($objectStateGroup, $objectStateGroupUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdateObjectStateGroupEvent($updatedObjectStateGroup, ...$eventData) - ); - - return $updatedObjectStateGroup; - } - - public function deleteObjectStateGroup(ObjectStateGroup $objectStateGroup): void - { - $eventData = [$objectStateGroup]; - - $beforeEvent = new BeforeDeleteObjectStateGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteObjectStateGroup($objectStateGroup); - - $this->eventDispatcher->dispatch( - new DeleteObjectStateGroupEvent(...$eventData) - ); - } - - public function createObjectState( - ObjectStateGroup $objectStateGroup, - ObjectStateCreateStruct $objectStateCreateStruct - ): ObjectState { - $eventData = [ - $objectStateGroup, - $objectStateCreateStruct, - ]; - - $beforeEvent = new BeforeCreateObjectStateEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getObjectState(); - } - - $objectState = $beforeEvent->hasObjectState() - ? $beforeEvent->getObjectState() - : $this->innerService->createObjectState($objectStateGroup, $objectStateCreateStruct); - - $this->eventDispatcher->dispatch( - new CreateObjectStateEvent($objectState, ...$eventData) - ); - - return $objectState; - } - - public function updateObjectState( - ObjectState $objectState, - ObjectStateUpdateStruct $objectStateUpdateStruct - ): ObjectState { - $eventData = [ - $objectState, - $objectStateUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdateObjectStateEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedObjectState(); - } - - $updatedObjectState = $beforeEvent->hasUpdatedObjectState() - ? $beforeEvent->getUpdatedObjectState() - : $this->innerService->updateObjectState($objectState, $objectStateUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdateObjectStateEvent($updatedObjectState, ...$eventData) - ); - - return $updatedObjectState; - } - - public function setPriorityOfObjectState( - ObjectState $objectState, - int $priority - ): void { - $eventData = [ - $objectState, - $priority, - ]; - - $beforeEvent = new BeforeSetPriorityOfObjectStateEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->setPriorityOfObjectState($objectState, $priority); - - $this->eventDispatcher->dispatch( - new SetPriorityOfObjectStateEvent(...$eventData) - ); - } - - public function deleteObjectState(ObjectState $objectState): void - { - $eventData = [$objectState]; - - $beforeEvent = new BeforeDeleteObjectStateEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteObjectState($objectState); - - $this->eventDispatcher->dispatch( - new DeleteObjectStateEvent(...$eventData) - ); - } - - public function setContentState( - ContentInfo $contentInfo, - ObjectStateGroup $objectStateGroup, - ObjectState $objectState - ): void { - $eventData = [ - $contentInfo, - $objectStateGroup, - $objectState, - ]; - - $beforeEvent = new BeforeSetContentStateEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->setContentState($contentInfo, $objectStateGroup, $objectState); - - $this->eventDispatcher->dispatch( - new SetContentStateEvent(...$eventData) - ); - } -} diff --git a/eZ/Publish/Core/Event/Repository.php b/eZ/Publish/Core/Event/Repository.php deleted file mode 100644 index 3735314655..0000000000 --- a/eZ/Publish/Core/Event/Repository.php +++ /dev/null @@ -1,236 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\BookmarkService as BookmarkServiceInterface; -use eZ\Publish\API\Repository\ContentService as ContentServiceInterface; -use eZ\Publish\API\Repository\ContentTypeService as ContentTypeServiceInterface; -use eZ\Publish\API\Repository\FieldTypeService as FieldTypeServiceInterface; -use eZ\Publish\API\Repository\LanguageService as LanguageServiceInterface; -use eZ\Publish\API\Repository\LocationService as LocationServiceInterface; -use eZ\Publish\API\Repository\NotificationService as NotificationServiceInterface; -use eZ\Publish\API\Repository\ObjectStateService as ObjectStateServiceInterface; -use eZ\Publish\API\Repository\PermissionResolver as PermissionResolverInterface; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\RoleService as RoleServiceInterface; -use eZ\Publish\API\Repository\SearchService as SearchServiceInterface; -use eZ\Publish\API\Repository\SectionService as SectionServiceInterface; -use eZ\Publish\API\Repository\TrashService as TrashServiceInterface; -use eZ\Publish\API\Repository\URLAliasService as URLAliasServiceInterface; -use eZ\Publish\API\Repository\URLService as URLServiceInterface; -use eZ\Publish\API\Repository\URLWildcardService as URLWildcardServiceInterface; -use eZ\Publish\API\Repository\UserPreferenceService as UserPreferenceServiceInterface; -use eZ\Publish\API\Repository\UserService as UserServiceInterface; - -final class Repository implements RepositoryInterface -{ - /** @var \eZ\Publish\API\Repository\Repository */ - private $repository; - - /** @var \eZ\Publish\API\Repository\BookmarkService */ - private $bookmarkService; - - /** @var \eZ\Publish\API\Repository\ContentService */ - private $contentService; - - /** @var \eZ\Publish\API\Repository\ContentTypeService */ - private $contentTypeService; - - /** @var \eZ\Publish\API\Repository\FieldTypeService */ - private $fieldTypeService; - - /** @var \eZ\Publish\API\Repository\LanguageService */ - private $languageService; - - /** @var \eZ\Publish\API\Repository\LocationService */ - private $locationService; - - /** @var \eZ\Publish\API\Repository\NotificationService */ - private $notificationService; - - /** @var \eZ\Publish\API\Repository\ObjectStateService */ - private $objectStateService; - - /** @var \eZ\Publish\API\Repository\RoleService */ - private $roleService; - - /** @var \eZ\Publish\API\Repository\SearchService */ - private $searchService; - - /** @var \eZ\Publish\API\Repository\SectionService */ - private $sectionService; - - /** @var \eZ\Publish\API\Repository\TrashService */ - private $trashService; - - /** @var \eZ\Publish\API\Repository\URLAliasService */ - private $urlAliasService; - - /** @var \eZ\Publish\API\Repository\URLService */ - private $urlService; - - /** @var \eZ\Publish\API\Repository\URLWildcardService */ - private $urlWildcardService; - - /** @var \eZ\Publish\API\Repository\UserPreferenceService */ - private $userPreferenceService; - - /** @var \eZ\Publish\API\Repository\UserService */ - private $userService; - - public function __construct( - RepositoryInterface $repository, - BookmarkServiceInterface $bookmarkService, - ContentServiceInterface $contentService, - ContentTypeServiceInterface $contentTypeService, - FieldTypeServiceInterface $fieldTypeService, - LanguageServiceInterface $languageService, - LocationServiceInterface $locationService, - NotificationServiceInterface $notificationService, - ObjectStateServiceInterface $objectStateService, - RoleServiceInterface $roleService, - SearchServiceInterface $searchService, - SectionServiceInterface $sectionService, - TrashServiceInterface $trashService, - URLAliasServiceInterface $urlAliasService, - URLServiceInterface $urlService, - URLWildcardServiceInterface $urlWildcardService, - UserPreferenceServiceInterface $userPreferenceService, - UserServiceInterface $userService - ) { - $this->repository = $repository; - $this->bookmarkService = $bookmarkService; - $this->contentService = $contentService; - $this->contentTypeService = $contentTypeService; - $this->fieldTypeService = $fieldTypeService; - $this->languageService = $languageService; - $this->locationService = $locationService; - $this->notificationService = $notificationService; - $this->objectStateService = $objectStateService; - $this->roleService = $roleService; - $this->searchService = $searchService; - $this->sectionService = $sectionService; - $this->trashService = $trashService; - $this->urlAliasService = $urlAliasService; - $this->urlService = $urlService; - $this->urlWildcardService = $urlWildcardService; - $this->userPreferenceService = $userPreferenceService; - $this->userService = $userService; - } - - public function sudo(callable $callback, ?RepositoryInterface $outerRepository = null) - { - return $this->repository->sudo($callback, $outerRepository); - } - - public function beginTransaction(): void - { - $this->repository->beginTransaction(); - } - - public function commit(): void - { - $this->repository->commit(); - } - - public function rollback(): void - { - $this->repository->rollback(); - } - - public function getPermissionResolver(): PermissionResolverInterface - { - return $this->repository->getPermissionResolver(); - } - - public function getBookmarkService(): BookmarkServiceInterface - { - return $this->bookmarkService; - } - - public function getContentService(): ContentServiceInterface - { - return $this->contentService; - } - - public function getContentTypeService(): ContentTypeServiceInterface - { - return $this->contentTypeService; - } - - public function getFieldTypeService(): FieldTypeServiceInterface - { - return $this->fieldTypeService; - } - - public function getContentLanguageService(): LanguageServiceInterface - { - return $this->languageService; - } - - public function getLocationService(): LocationServiceInterface - { - return $this->locationService; - } - - public function getNotificationService(): NotificationServiceInterface - { - return $this->notificationService; - } - - public function getObjectStateService(): ObjectStateServiceInterface - { - return $this->objectStateService; - } - - public function getRoleService(): RoleServiceInterface - { - return $this->roleService; - } - - public function getSearchService(): SearchServiceInterface - { - return $this->searchService; - } - - public function getSectionService(): SectionServiceInterface - { - return $this->sectionService; - } - - public function getTrashService(): TrashServiceInterface - { - return $this->trashService; - } - - public function getURLAliasService(): URLAliasServiceInterface - { - return $this->urlAliasService; - } - - public function getURLService(): URLServiceInterface - { - return $this->urlService; - } - - public function getURLWildcardService(): URLWildcardServiceInterface - { - return $this->urlWildcardService; - } - - public function getUserPreferenceService(): UserPreferenceServiceInterface - { - return $this->userPreferenceService; - } - - public function getUserService(): UserServiceInterface - { - return $this->userService; - } -} diff --git a/eZ/Publish/Core/Event/RoleService.php b/eZ/Publish/Core/Event/RoleService.php deleted file mode 100644 index 5106dc12f4..0000000000 --- a/eZ/Publish/Core/Event/RoleService.php +++ /dev/null @@ -1,369 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\Events\Role\AddPolicyByRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\AssignRoleToUserEvent; -use eZ\Publish\API\Repository\Events\Role\AssignRoleToUserGroupEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeAddPolicyByRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeAssignRoleToUserEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeAssignRoleToUserGroupEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeCopyRoleEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeCreateRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeCreateRoleEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeDeleteRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeDeleteRoleEvent; -use eZ\Publish\API\Repository\Events\Role\BeforePublishRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeRemovePolicyByRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeRemoveRoleAssignmentEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeUpdatePolicyByRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeUpdateRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\CopyRoleEvent; -use eZ\Publish\API\Repository\Events\Role\CreateRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\CreateRoleEvent; -use eZ\Publish\API\Repository\Events\Role\DeleteRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\DeleteRoleEvent; -use eZ\Publish\API\Repository\Events\Role\PublishRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\RemovePolicyByRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\RemoveRoleAssignmentEvent; -use eZ\Publish\API\Repository\Events\Role\UpdatePolicyByRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\UpdateRoleDraftEvent; -use eZ\Publish\API\Repository\RoleService as RoleServiceInterface; -use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation; -use eZ\Publish\API\Repository\Values\User\PolicyCreateStruct; -use eZ\Publish\API\Repository\Values\User\PolicyDraft; -use eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct; -use eZ\Publish\API\Repository\Values\User\Role; -use eZ\Publish\API\Repository\Values\User\RoleAssignment; -use eZ\Publish\API\Repository\Values\User\RoleCopyStruct; -use eZ\Publish\API\Repository\Values\User\RoleCreateStruct; -use eZ\Publish\API\Repository\Values\User\RoleDraft; -use eZ\Publish\API\Repository\Values\User\RoleUpdateStruct; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\SPI\Repository\Decorator\RoleServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class RoleService extends RoleServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - RoleServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function createRole(RoleCreateStruct $roleCreateStruct): RoleDraft - { - $eventData = [$roleCreateStruct]; - - $beforeEvent = new BeforeCreateRoleEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getRoleDraft(); - } - - $roleDraft = $beforeEvent->hasRoleDraft() - ? $beforeEvent->getRoleDraft() - : $this->innerService->createRole($roleCreateStruct); - - $this->eventDispatcher->dispatch( - new CreateRoleEvent($roleDraft, ...$eventData) - ); - - return $roleDraft; - } - - public function createRoleDraft(Role $role): RoleDraft - { - $eventData = [$role]; - - $beforeEvent = new BeforeCreateRoleDraftEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getRoleDraft(); - } - - $roleDraft = $beforeEvent->hasRoleDraft() - ? $beforeEvent->getRoleDraft() - : $this->innerService->createRoleDraft($role); - - $this->eventDispatcher->dispatch( - new CreateRoleDraftEvent($roleDraft, ...$eventData) - ); - - return $roleDraft; - } - - public function copyRole( - Role $role, - RoleCopyStruct $roleCopyStruct - ): Role { - $eventData = [ - $role, - $roleCopyStruct, - ]; - - $beforeEvent = new BeforeCopyRoleEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getCopiedRole(); - } - - $copiedRole = $beforeEvent->hasCopiedRole() - ? $beforeEvent->getCopiedRole() - : $this->innerService->copyRole($role, $roleCopyStruct); - - $this->eventDispatcher->dispatch( - new CopyRoleEvent($copiedRole, ...$eventData) - ); - - return $copiedRole; - } - - public function updateRoleDraft( - RoleDraft $roleDraft, - RoleUpdateStruct $roleUpdateStruct - ): RoleDraft { - $eventData = [ - $roleDraft, - $roleUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdateRoleDraftEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedRoleDraft(); - } - - $updatedRoleDraft = $beforeEvent->hasUpdatedRoleDraft() - ? $beforeEvent->getUpdatedRoleDraft() - : $this->innerService->updateRoleDraft($roleDraft, $roleUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdateRoleDraftEvent($updatedRoleDraft, ...$eventData) - ); - - return $updatedRoleDraft; - } - - public function addPolicyByRoleDraft( - RoleDraft $roleDraft, - PolicyCreateStruct $policyCreateStruct - ): RoleDraft { - $eventData = [ - $roleDraft, - $policyCreateStruct, - ]; - - $beforeEvent = new BeforeAddPolicyByRoleDraftEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedRoleDraft(); - } - - $updatedRoleDraft = $beforeEvent->hasUpdatedRoleDraft() - ? $beforeEvent->getUpdatedRoleDraft() - : $this->innerService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct); - - $this->eventDispatcher->dispatch( - new AddPolicyByRoleDraftEvent($updatedRoleDraft, ...$eventData) - ); - - return $updatedRoleDraft; - } - - public function removePolicyByRoleDraft( - RoleDraft $roleDraft, - PolicyDraft $policyDraft - ): RoleDraft { - $eventData = [ - $roleDraft, - $policyDraft, - ]; - - $beforeEvent = new BeforeRemovePolicyByRoleDraftEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedRoleDraft(); - } - - $updatedRoleDraft = $beforeEvent->hasUpdatedRoleDraft() - ? $beforeEvent->getUpdatedRoleDraft() - : $this->innerService->removePolicyByRoleDraft($roleDraft, $policyDraft); - - $this->eventDispatcher->dispatch( - new RemovePolicyByRoleDraftEvent($updatedRoleDraft, ...$eventData) - ); - - return $updatedRoleDraft; - } - - public function updatePolicyByRoleDraft( - RoleDraft $roleDraft, - PolicyDraft $policy, - PolicyUpdateStruct $policyUpdateStruct - ): PolicyDraft { - $eventData = [ - $roleDraft, - $policy, - $policyUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdatePolicyByRoleDraftEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedPolicyDraft(); - } - - $updatedPolicyDraft = $beforeEvent->hasUpdatedPolicyDraft() - ? $beforeEvent->getUpdatedPolicyDraft() - : $this->innerService->updatePolicyByRoleDraft($roleDraft, $policy, $policyUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdatePolicyByRoleDraftEvent($updatedPolicyDraft, ...$eventData) - ); - - return $updatedPolicyDraft; - } - - public function deleteRoleDraft(RoleDraft $roleDraft): void - { - $eventData = [$roleDraft]; - - $beforeEvent = new BeforeDeleteRoleDraftEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteRoleDraft($roleDraft); - - $this->eventDispatcher->dispatch( - new DeleteRoleDraftEvent(...$eventData) - ); - } - - public function publishRoleDraft(RoleDraft $roleDraft): void - { - $eventData = [$roleDraft]; - - $beforeEvent = new BeforePublishRoleDraftEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->publishRoleDraft($roleDraft); - - $this->eventDispatcher->dispatch( - new PublishRoleDraftEvent(...$eventData) - ); - } - - public function deleteRole(Role $role): void - { - $eventData = [$role]; - - $beforeEvent = new BeforeDeleteRoleEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteRole($role); - - $this->eventDispatcher->dispatch( - new DeleteRoleEvent(...$eventData) - ); - } - - public function assignRoleToUserGroup( - Role $role, - UserGroup $userGroup, - RoleLimitation $roleLimitation = null - ): void { - $eventData = [ - $role, - $userGroup, - $roleLimitation, - ]; - - $beforeEvent = new BeforeAssignRoleToUserGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->assignRoleToUserGroup($role, $userGroup, $roleLimitation); - - $this->eventDispatcher->dispatch( - new AssignRoleToUserGroupEvent(...$eventData) - ); - } - - public function assignRoleToUser( - Role $role, - User $user, - RoleLimitation $roleLimitation = null - ): void { - $eventData = [ - $role, - $user, - $roleLimitation, - ]; - - $beforeEvent = new BeforeAssignRoleToUserEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->assignRoleToUser($role, $user, $roleLimitation); - - $this->eventDispatcher->dispatch( - new AssignRoleToUserEvent(...$eventData) - ); - } - - public function removeRoleAssignment(RoleAssignment $roleAssignment): void - { - $eventData = [$roleAssignment]; - - $beforeEvent = new BeforeRemoveRoleAssignmentEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->removeRoleAssignment($roleAssignment); - - $this->eventDispatcher->dispatch( - new RemoveRoleAssignmentEvent(...$eventData) - ); - } -} diff --git a/eZ/Publish/Core/Event/SearchService.php b/eZ/Publish/Core/Event/SearchService.php deleted file mode 100644 index af43e7bc0f..0000000000 --- a/eZ/Publish/Core/Event/SearchService.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\SearchService as SearchServiceInterface; -use eZ\Publish\SPI\Repository\Decorator\SearchServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class SearchService extends SearchServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - SearchServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } -} diff --git a/eZ/Publish/Core/Event/SectionService.php b/eZ/Publish/Core/Event/SectionService.php deleted file mode 100644 index 3009a74ff6..0000000000 --- a/eZ/Publish/Core/Event/SectionService.php +++ /dev/null @@ -1,156 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\Events\Section\AssignSectionEvent; -use eZ\Publish\API\Repository\Events\Section\AssignSectionToSubtreeEvent; -use eZ\Publish\API\Repository\Events\Section\BeforeAssignSectionEvent; -use eZ\Publish\API\Repository\Events\Section\BeforeAssignSectionToSubtreeEvent; -use eZ\Publish\API\Repository\Events\Section\BeforeCreateSectionEvent; -use eZ\Publish\API\Repository\Events\Section\BeforeDeleteSectionEvent; -use eZ\Publish\API\Repository\Events\Section\BeforeUpdateSectionEvent; -use eZ\Publish\API\Repository\Events\Section\CreateSectionEvent; -use eZ\Publish\API\Repository\Events\Section\DeleteSectionEvent; -use eZ\Publish\API\Repository\Events\Section\UpdateSectionEvent; -use eZ\Publish\API\Repository\SectionService as SectionServiceInterface; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Section; -use eZ\Publish\API\Repository\Values\Content\SectionCreateStruct; -use eZ\Publish\API\Repository\Values\Content\SectionUpdateStruct; -use eZ\Publish\SPI\Repository\Decorator\SectionServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class SectionService extends SectionServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - SectionServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function createSection(SectionCreateStruct $sectionCreateStruct): Section - { - $eventData = [$sectionCreateStruct]; - - $beforeEvent = new BeforeCreateSectionEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getSection(); - } - - $section = $beforeEvent->hasSection() - ? $beforeEvent->getSection() - : $this->innerService->createSection($sectionCreateStruct); - - $this->eventDispatcher->dispatch( - new CreateSectionEvent($section, ...$eventData) - ); - - return $section; - } - - public function updateSection( - Section $section, - SectionUpdateStruct $sectionUpdateStruct - ): Section { - $eventData = [ - $section, - $sectionUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdateSectionEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedSection(); - } - - $updatedSection = $beforeEvent->hasUpdatedSection() - ? $beforeEvent->getUpdatedSection() - : $this->innerService->updateSection($section, $sectionUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdateSectionEvent($updatedSection, ...$eventData) - ); - - return $updatedSection; - } - - public function assignSection( - ContentInfo $contentInfo, - Section $section - ): void { - $eventData = [ - $contentInfo, - $section, - ]; - - $beforeEvent = new BeforeAssignSectionEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->assignSection($contentInfo, $section); - - $this->eventDispatcher->dispatch( - new AssignSectionEvent(...$eventData) - ); - } - - public function assignSectionToSubtree( - Location $location, - Section $section - ): void { - $eventData = [ - $location, - $section, - ]; - - $beforeEvent = new BeforeAssignSectionToSubtreeEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->assignSectionToSubtree($location, $section); - - $this->eventDispatcher->dispatch( - new AssignSectionToSubtreeEvent(...$eventData) - ); - } - - public function deleteSection(Section $section): void - { - $eventData = [$section]; - - $beforeEvent = new BeforeDeleteSectionEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteSection($section); - - $this->eventDispatcher->dispatch( - new DeleteSectionEvent(...$eventData) - ); - } -} diff --git a/eZ/Publish/Core/Event/SettingService.php b/eZ/Publish/Core/Event/SettingService.php deleted file mode 100644 index fe036e7ed9..0000000000 --- a/eZ/Publish/Core/Event/SettingService.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\Events\Setting\BeforeCreateSettingEvent; -use eZ\Publish\API\Repository\Events\Setting\BeforeDeleteSettingEvent; -use eZ\Publish\API\Repository\Events\Setting\BeforeUpdateSettingEvent; -use eZ\Publish\API\Repository\Events\Setting\CreateSettingEvent; -use eZ\Publish\API\Repository\Events\Setting\DeleteSettingEvent; -use eZ\Publish\API\Repository\Events\Setting\UpdateSettingEvent; -use eZ\Publish\API\Repository\SettingService as SettingServiceInterface; -use eZ\Publish\API\Repository\Values\Setting\Setting; -use eZ\Publish\API\Repository\Values\Setting\SettingCreateStruct; -use eZ\Publish\API\Repository\Values\Setting\SettingUpdateStruct; -use eZ\Publish\SPI\Repository\Decorator\SettingServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -final class SettingService extends SettingServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - private $eventDispatcher; - - public function __construct( - SettingServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function createSetting(SettingCreateStruct $settingCreateStruct): Setting - { - $eventData = [$settingCreateStruct]; - - $beforeEvent = new BeforeCreateSettingEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getSetting(); - } - - $setting = $beforeEvent->hasSetting() - ? $beforeEvent->getSetting() - : $this->innerService->createSetting($settingCreateStruct); - - $this->eventDispatcher->dispatch( - new CreateSettingEvent($setting, ...$eventData) - ); - - return $setting; - } - - public function updateSetting(Setting $setting, SettingUpdateStruct $settingUpdateStruct): Setting - { - $eventData = [ - $setting, - $settingUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdateSettingEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedSetting(); - } - - $updatedSetting = $beforeEvent->hasUpdatedSetting() - ? $beforeEvent->getUpdatedSetting() - : $this->innerService->updateSetting($setting, $settingUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdateSettingEvent($updatedSetting, ...$eventData) - ); - - return $updatedSetting; - } - - public function deleteSetting(Setting $setting): void - { - $eventData = [$setting]; - - $beforeEvent = new BeforeDeleteSettingEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->deleteSetting($setting); - - $this->eventDispatcher->dispatch( - new DeleteSettingEvent(...$eventData) - ); - } -} diff --git a/eZ/Publish/Core/Event/Tests/AbstractServiceTest.php b/eZ/Publish/Core/Event/Tests/AbstractServiceTest.php deleted file mode 100644 index 9b69ea86f6..0000000000 --- a/eZ/Publish/Core/Event/Tests/AbstractServiceTest.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\SPI\Repository\Event\AfterEvent; -use eZ\Publish\SPI\Repository\Event\BeforeEvent; -use PHPUnit\Framework\TestCase; -use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\Stopwatch\Stopwatch; - -abstract class AbstractServiceTest extends TestCase -{ - public function getEventDispatcher(string $beforeEventName, string $eventName): TraceableEventDispatcher - { - $eventDispatcher = new EventDispatcher(); - $eventDispatcher->addListener($beforeEventName, static function (BeforeEvent $event) {}); - $eventDispatcher->addListener($eventName, static function (AfterEvent $event) {}); - - return new TraceableEventDispatcher( - $eventDispatcher, - new Stopwatch() - ); - } - - public function getListenersStack(array $listeners): array - { - $stack = []; - - foreach ($listeners as $listener) { - $stack[] = [$listener['event'], $listener['priority']]; - } - - return $stack; - } -} diff --git a/eZ/Publish/Core/Event/Tests/BookmarkServiceTest.php b/eZ/Publish/Core/Event/Tests/BookmarkServiceTest.php deleted file mode 100644 index 8d79f1f2ca..0000000000 --- a/eZ/Publish/Core/Event/Tests/BookmarkServiceTest.php +++ /dev/null @@ -1,132 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\BookmarkService as BookmarkServiceInterface; -use eZ\Publish\API\Repository\Events\Bookmark\BeforeCreateBookmarkEvent; -use eZ\Publish\API\Repository\Events\Bookmark\BeforeDeleteBookmarkEvent; -use eZ\Publish\API\Repository\Events\Bookmark\CreateBookmarkEvent; -use eZ\Publish\API\Repository\Events\Bookmark\DeleteBookmarkEvent; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\Event\BookmarkService; - -class BookmarkServiceTest extends AbstractServiceTest -{ - public function testCreateBookmarkEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateBookmarkEvent::class, - CreateBookmarkEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $innerServiceMock = $this->createMock(BookmarkServiceInterface::class); - - $service = new BookmarkService($innerServiceMock, $traceableEventDispatcher); - $service->createBookmark(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeCreateBookmarkEvent::class, 0], - [CreateBookmarkEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateBookmarkStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateBookmarkEvent::class, - CreateBookmarkEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $innerServiceMock = $this->createMock(BookmarkServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeCreateBookmarkEvent::class, static function (BeforeCreateBookmarkEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new BookmarkService($innerServiceMock, $traceableEventDispatcher); - $service->createBookmark(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeCreateBookmarkEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateBookmarkEvent::class, 0], - [CreateBookmarkEvent::class, 0], - ]); - } - - public function testDeleteBookmarkEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteBookmarkEvent::class, - DeleteBookmarkEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $innerServiceMock = $this->createMock(BookmarkServiceInterface::class); - - $service = new BookmarkService($innerServiceMock, $traceableEventDispatcher); - $service->deleteBookmark(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteBookmarkEvent::class, 0], - [DeleteBookmarkEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteBookmarkStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteBookmarkEvent::class, - DeleteBookmarkEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $innerServiceMock = $this->createMock(BookmarkServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteBookmarkEvent::class, static function (BeforeDeleteBookmarkEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new BookmarkService($innerServiceMock, $traceableEventDispatcher); - $service->deleteBookmark(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteBookmarkEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteBookmarkEvent::class, 0], - [DeleteBookmarkEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/ContentServiceTest.php b/eZ/Publish/Core/Event/Tests/ContentServiceTest.php deleted file mode 100644 index fdccee87e4..0000000000 --- a/eZ/Publish/Core/Event/Tests/ContentServiceTest.php +++ /dev/null @@ -1,1156 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\ContentService as ContentServiceInterface; -use eZ\Publish\API\Repository\Events\Content\AddRelationEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeAddRelationEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeCopyContentEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeCreateContentDraftEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeCreateContentEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeDeleteContentEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeDeleteRelationEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeDeleteTranslationEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeDeleteVersionEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeHideContentEvent; -use eZ\Publish\API\Repository\Events\Content\BeforePublishVersionEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeRevealContentEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeUpdateContentEvent; -use eZ\Publish\API\Repository\Events\Content\BeforeUpdateContentMetadataEvent; -use eZ\Publish\API\Repository\Events\Content\CopyContentEvent; -use eZ\Publish\API\Repository\Events\Content\CreateContentDraftEvent; -use eZ\Publish\API\Repository\Events\Content\CreateContentEvent; -use eZ\Publish\API\Repository\Events\Content\DeleteContentEvent; -use eZ\Publish\API\Repository\Events\Content\DeleteRelationEvent; -use eZ\Publish\API\Repository\Events\Content\DeleteTranslationEvent; -use eZ\Publish\API\Repository\Events\Content\DeleteVersionEvent; -use eZ\Publish\API\Repository\Events\Content\HideContentEvent; -use eZ\Publish\API\Repository\Events\Content\PublishVersionEvent; -use eZ\Publish\API\Repository\Events\Content\RevealContentEvent; -use eZ\Publish\API\Repository\Events\Content\UpdateContentEvent; -use eZ\Publish\API\Repository\Events\Content\UpdateContentMetadataEvent; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\Core\Event\ContentService; - -class ContentServiceTest extends AbstractServiceTest -{ - public function testDeleteContentEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteContentEvent::class, - DeleteContentEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - ]; - - $locations = []; - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('deleteContent')->willReturn($locations); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->deleteContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($locations, $result); - $this->assertSame($calledListeners, [ - [BeforeDeleteContentEvent::class, 0], - [DeleteContentEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnDeleteContentResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteContentEvent::class, - DeleteContentEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - ]; - - $locations = []; - $eventLocations = []; - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('deleteContent')->willReturn($locations); - - $traceableEventDispatcher->addListener(BeforeDeleteContentEvent::class, static function (BeforeDeleteContentEvent $event) use ($eventLocations) { - $event->setLocations($eventLocations); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->deleteContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventLocations, $result); - $this->assertSame($calledListeners, [ - [BeforeDeleteContentEvent::class, 10], - [BeforeDeleteContentEvent::class, 0], - [DeleteContentEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteContentStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteContentEvent::class, - DeleteContentEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - ]; - - $locations = []; - $eventLocations = []; - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('deleteContent')->willReturn($locations); - - $traceableEventDispatcher->addListener(BeforeDeleteContentEvent::class, static function (BeforeDeleteContentEvent $event) use ($eventLocations) { - $event->setLocations($eventLocations); - $event->stopPropagation(); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->deleteContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventLocations, $result); - $this->assertSame($calledListeners, [ - [BeforeDeleteContentEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteContentEvent::class, 0], - [DeleteContentEvent::class, 0], - ]); - } - - public function testCopyContentEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCopyContentEvent::class, - CopyContentEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(LocationCreateStruct::class), - $this->createMock(VersionInfo::class), - ]; - - $content = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('copyContent')->willReturn($content); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->copyContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($content, $result); - $this->assertSame($calledListeners, [ - [BeforeCopyContentEvent::class, 0], - [CopyContentEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCopyContentResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCopyContentEvent::class, - CopyContentEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(LocationCreateStruct::class), - $this->createMock(VersionInfo::class), - ]; - - $content = $this->createMock(Content::class); - $eventContent = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('copyContent')->willReturn($content); - - $traceableEventDispatcher->addListener(BeforeCopyContentEvent::class, static function (BeforeCopyContentEvent $event) use ($eventContent) { - $event->setContent($eventContent); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->copyContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventContent, $result); - $this->assertSame($calledListeners, [ - [BeforeCopyContentEvent::class, 10], - [BeforeCopyContentEvent::class, 0], - [CopyContentEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCopyContentStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCopyContentEvent::class, - CopyContentEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(LocationCreateStruct::class), - $this->createMock(VersionInfo::class), - ]; - - $content = $this->createMock(Content::class); - $eventContent = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('copyContent')->willReturn($content); - - $traceableEventDispatcher->addListener(BeforeCopyContentEvent::class, static function (BeforeCopyContentEvent $event) use ($eventContent) { - $event->setContent($eventContent); - $event->stopPropagation(); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->copyContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventContent, $result); - $this->assertSame($calledListeners, [ - [BeforeCopyContentEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCopyContentEvent::class, 0], - [CopyContentEvent::class, 0], - ]); - } - - public function testUpdateContentEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateContentEvent::class, - UpdateContentEvent::class - ); - - $parameters = [ - $this->createMock(VersionInfo::class), - $this->createMock(ContentUpdateStruct::class), - ]; - - $content = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('updateContent')->willReturn($content); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($content, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateContentEvent::class, 0], - [UpdateContentEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdateContentResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateContentEvent::class, - UpdateContentEvent::class - ); - - $parameters = [ - $this->createMock(VersionInfo::class), - $this->createMock(ContentUpdateStruct::class), - ]; - - $content = $this->createMock(Content::class); - $eventContent = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('updateContent')->willReturn($content); - - $traceableEventDispatcher->addListener(BeforeUpdateContentEvent::class, static function (BeforeUpdateContentEvent $event) use ($eventContent) { - $event->setContent($eventContent); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventContent, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateContentEvent::class, 10], - [BeforeUpdateContentEvent::class, 0], - [UpdateContentEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateContentStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateContentEvent::class, - UpdateContentEvent::class - ); - - $parameters = [ - $this->createMock(VersionInfo::class), - $this->createMock(ContentUpdateStruct::class), - ]; - - $content = $this->createMock(Content::class); - $eventContent = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('updateContent')->willReturn($content); - - $traceableEventDispatcher->addListener(BeforeUpdateContentEvent::class, static function (BeforeUpdateContentEvent $event) use ($eventContent) { - $event->setContent($eventContent); - $event->stopPropagation(); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventContent, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateContentEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateContentEvent::class, 0], - [UpdateContentEvent::class, 0], - ]); - } - - public function testDeleteRelationEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteRelationEvent::class, - DeleteRelationEvent::class - ); - - $parameters = [ - $this->createMock(VersionInfo::class), - $this->createMock(ContentInfo::class), - ]; - - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $service->deleteRelation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteRelationEvent::class, 0], - [DeleteRelationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteRelationStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteRelationEvent::class, - DeleteRelationEvent::class - ); - - $parameters = [ - $this->createMock(VersionInfo::class), - $this->createMock(ContentInfo::class), - ]; - - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteRelationEvent::class, static function (BeforeDeleteRelationEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $service->deleteRelation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteRelationEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteRelationEvent::class, 0], - [DeleteRelationEvent::class, 0], - ]); - } - - public function testCreateContentEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentEvent::class, - CreateContentEvent::class - ); - - $parameters = [ - $this->createMock(ContentCreateStruct::class), - [], - ]; - - $content = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('createContent')->willReturn($content); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($content, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentEvent::class, 0], - [CreateContentEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateContentResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentEvent::class, - CreateContentEvent::class - ); - - $parameters = [ - $this->createMock(ContentCreateStruct::class), - [], - ]; - - $content = $this->createMock(Content::class); - $eventContent = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('createContent')->willReturn($content); - - $traceableEventDispatcher->addListener(BeforeCreateContentEvent::class, static function (BeforeCreateContentEvent $event) use ($eventContent) { - $event->setContent($eventContent); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventContent, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentEvent::class, 10], - [BeforeCreateContentEvent::class, 0], - [CreateContentEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateContentStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentEvent::class, - CreateContentEvent::class - ); - - $parameters = [ - $this->createMock(ContentCreateStruct::class), - [], - ]; - - $content = $this->createMock(Content::class); - $eventContent = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('createContent')->willReturn($content); - - $traceableEventDispatcher->addListener(BeforeCreateContentEvent::class, static function (BeforeCreateContentEvent $event) use ($eventContent) { - $event->setContent($eventContent); - $event->stopPropagation(); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventContent, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateContentEvent::class, 0], - [CreateContentEvent::class, 0], - ]); - } - - public function testHideContentEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeHideContentEvent::class, - HideContentEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - ]; - - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $service->hideContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeHideContentEvent::class, 0], - [HideContentEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testHideContentStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeHideContentEvent::class, - HideContentEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - ]; - - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeHideContentEvent::class, static function (BeforeHideContentEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $service->hideContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeHideContentEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeHideContentEvent::class, 0], - [HideContentEvent::class, 0], - ]); - } - - public function testDeleteVersionEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteVersionEvent::class, - DeleteVersionEvent::class - ); - - $parameters = [ - $this->createMock(VersionInfo::class), - ]; - - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $service->deleteVersion(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteVersionEvent::class, 0], - [DeleteVersionEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteVersionStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteVersionEvent::class, - DeleteVersionEvent::class - ); - - $parameters = [ - $this->createMock(VersionInfo::class), - ]; - - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteVersionEvent::class, static function (BeforeDeleteVersionEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $service->deleteVersion(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteVersionEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteVersionEvent::class, 0], - [DeleteVersionEvent::class, 0], - ]); - } - - public function testAddRelationEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAddRelationEvent::class, - AddRelationEvent::class - ); - - $parameters = [ - $this->createMock(VersionInfo::class), - $this->createMock(ContentInfo::class), - ]; - - $relation = $this->createMock(Relation::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('addRelation')->willReturn($relation); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->addRelation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($relation, $result); - $this->assertSame($calledListeners, [ - [BeforeAddRelationEvent::class, 0], - [AddRelationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnAddRelationResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAddRelationEvent::class, - AddRelationEvent::class - ); - - $parameters = [ - $this->createMock(VersionInfo::class), - $this->createMock(ContentInfo::class), - ]; - - $relation = $this->createMock(Relation::class); - $eventRelation = $this->createMock(Relation::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('addRelation')->willReturn($relation); - - $traceableEventDispatcher->addListener(BeforeAddRelationEvent::class, static function (BeforeAddRelationEvent $event) use ($eventRelation) { - $event->setRelation($eventRelation); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->addRelation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventRelation, $result); - $this->assertSame($calledListeners, [ - [BeforeAddRelationEvent::class, 10], - [BeforeAddRelationEvent::class, 0], - [AddRelationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testAddRelationStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAddRelationEvent::class, - AddRelationEvent::class - ); - - $parameters = [ - $this->createMock(VersionInfo::class), - $this->createMock(ContentInfo::class), - ]; - - $relation = $this->createMock(Relation::class); - $eventRelation = $this->createMock(Relation::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('addRelation')->willReturn($relation); - - $traceableEventDispatcher->addListener(BeforeAddRelationEvent::class, static function (BeforeAddRelationEvent $event) use ($eventRelation) { - $event->setRelation($eventRelation); - $event->stopPropagation(); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->addRelation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventRelation, $result); - $this->assertSame($calledListeners, [ - [BeforeAddRelationEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [AddRelationEvent::class, 0], - [BeforeAddRelationEvent::class, 0], - ]); - } - - public function testUpdateContentMetadataEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateContentMetadataEvent::class, - UpdateContentMetadataEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(ContentMetadataUpdateStruct::class), - ]; - - $content = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('updateContentMetadata')->willReturn($content); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateContentMetadata(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($content, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateContentMetadataEvent::class, 0], - [UpdateContentMetadataEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdateContentMetadataResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateContentMetadataEvent::class, - UpdateContentMetadataEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(ContentMetadataUpdateStruct::class), - ]; - - $content = $this->createMock(Content::class); - $eventContent = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('updateContentMetadata')->willReturn($content); - - $traceableEventDispatcher->addListener(BeforeUpdateContentMetadataEvent::class, static function (BeforeUpdateContentMetadataEvent $event) use ($eventContent) { - $event->setContent($eventContent); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateContentMetadata(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventContent, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateContentMetadataEvent::class, 10], - [BeforeUpdateContentMetadataEvent::class, 0], - [UpdateContentMetadataEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateContentMetadataStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateContentMetadataEvent::class, - UpdateContentMetadataEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(ContentMetadataUpdateStruct::class), - ]; - - $content = $this->createMock(Content::class); - $eventContent = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('updateContentMetadata')->willReturn($content); - - $traceableEventDispatcher->addListener(BeforeUpdateContentMetadataEvent::class, static function (BeforeUpdateContentMetadataEvent $event) use ($eventContent) { - $event->setContent($eventContent); - $event->stopPropagation(); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateContentMetadata(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventContent, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateContentMetadataEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateContentMetadataEvent::class, 0], - [UpdateContentMetadataEvent::class, 0], - ]); - } - - public function testDeleteTranslationEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteTranslationEvent::class, - DeleteTranslationEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - 'random_value_5cff79c31a2f31.74205767', - ]; - - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $service->deleteTranslation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteTranslationEvent::class, 0], - [DeleteTranslationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteTranslationStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteTranslationEvent::class, - DeleteTranslationEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - 'random_value_5cff79c31a2fc0.71971617', - ]; - - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteTranslationEvent::class, static function (BeforeDeleteTranslationEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $service->deleteTranslation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteTranslationEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteTranslationEvent::class, 0], - [DeleteTranslationEvent::class, 0], - ]); - } - - public function testPublishVersionEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforePublishVersionEvent::class, - PublishVersionEvent::class - ); - - $parameters = [ - $this->createMock(VersionInfo::class), - [], - ]; - - $content = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('publishVersion')->willReturn($content); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->publishVersion(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($content, $result); - $this->assertSame($calledListeners, [ - [BeforePublishVersionEvent::class, 0], - [PublishVersionEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnPublishVersionResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforePublishVersionEvent::class, - PublishVersionEvent::class - ); - - $parameters = [ - $this->createMock(VersionInfo::class), - [], - ]; - - $content = $this->createMock(Content::class); - $eventContent = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('publishVersion')->willReturn($content); - - $traceableEventDispatcher->addListener(BeforePublishVersionEvent::class, static function (BeforePublishVersionEvent $event) use ($eventContent) { - $event->setContent($eventContent); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->publishVersion(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventContent, $result); - $this->assertSame($calledListeners, [ - [BeforePublishVersionEvent::class, 10], - [BeforePublishVersionEvent::class, 0], - [PublishVersionEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testPublishVersionStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforePublishVersionEvent::class, - PublishVersionEvent::class - ); - - $parameters = [ - $this->createMock(VersionInfo::class), - [], - ]; - - $content = $this->createMock(Content::class); - $eventContent = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('publishVersion')->willReturn($content); - - $traceableEventDispatcher->addListener(BeforePublishVersionEvent::class, static function (BeforePublishVersionEvent $event) use ($eventContent) { - $event->setContent($eventContent); - $event->stopPropagation(); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->publishVersion(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventContent, $result); - $this->assertSame($calledListeners, [ - [BeforePublishVersionEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforePublishVersionEvent::class, 0], - [PublishVersionEvent::class, 0], - ]); - } - - public function testCreateContentDraftEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentDraftEvent::class, - CreateContentDraftEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(VersionInfo::class), - $this->createMock(User::class), - ]; - - $contentDraft = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('createContentDraft')->willReturn($contentDraft); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContentDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($contentDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentDraftEvent::class, 0], - [CreateContentDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateContentDraftResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentDraftEvent::class, - CreateContentDraftEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(VersionInfo::class), - $this->createMock(User::class), - ]; - - $contentDraft = $this->createMock(Content::class); - $eventContentDraft = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('createContentDraft')->willReturn($contentDraft); - - $traceableEventDispatcher->addListener(BeforeCreateContentDraftEvent::class, static function (BeforeCreateContentDraftEvent $event) use ($eventContentDraft) { - $event->setContentDraft($eventContentDraft); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContentDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventContentDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentDraftEvent::class, 10], - [BeforeCreateContentDraftEvent::class, 0], - [CreateContentDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateContentDraftStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentDraftEvent::class, - CreateContentDraftEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(VersionInfo::class), - $this->createMock(User::class), - ]; - - $contentDraft = $this->createMock(Content::class); - $eventContentDraft = $this->createMock(Content::class); - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - $innerServiceMock->method('createContentDraft')->willReturn($contentDraft); - - $traceableEventDispatcher->addListener(BeforeCreateContentDraftEvent::class, static function (BeforeCreateContentDraftEvent $event) use ($eventContentDraft) { - $event->setContentDraft($eventContentDraft); - $event->stopPropagation(); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContentDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventContentDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentDraftEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateContentDraftEvent::class, 0], - [CreateContentDraftEvent::class, 0], - ]); - } - - public function testRevealContentEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRevealContentEvent::class, - RevealContentEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - ]; - - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $service->revealContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeRevealContentEvent::class, 0], - [RevealContentEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testRevealContentStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRevealContentEvent::class, - RevealContentEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - ]; - - $innerServiceMock = $this->createMock(ContentServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeRevealContentEvent::class, static function (BeforeRevealContentEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentService($innerServiceMock, $traceableEventDispatcher); - $service->revealContent(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeRevealContentEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeRevealContentEvent::class, 0], - [RevealContentEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/ContentTypeServiceTest.php b/eZ/Publish/Core/Event/Tests/ContentTypeServiceTest.php deleted file mode 100644 index 3dd92d4c03..0000000000 --- a/eZ/Publish/Core/Event/Tests/ContentTypeServiceTest.php +++ /dev/null @@ -1,1144 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\ContentTypeService as ContentTypeServiceInterface; -use eZ\Publish\API\Repository\Events\ContentType\AddFieldDefinitionEvent; -use eZ\Publish\API\Repository\Events\ContentType\AssignContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeAddFieldDefinitionEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeAssignContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeCopyContentTypeEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeCreateContentTypeDraftEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeCreateContentTypeEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeCreateContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeDeleteContentTypeEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeDeleteContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforePublishContentTypeDraftEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeRemoveContentTypeTranslationEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeRemoveFieldDefinitionEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeUnassignContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeUpdateContentTypeDraftEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeUpdateContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\BeforeUpdateFieldDefinitionEvent; -use eZ\Publish\API\Repository\Events\ContentType\CopyContentTypeEvent; -use eZ\Publish\API\Repository\Events\ContentType\CreateContentTypeDraftEvent; -use eZ\Publish\API\Repository\Events\ContentType\CreateContentTypeEvent; -use eZ\Publish\API\Repository\Events\ContentType\CreateContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\DeleteContentTypeEvent; -use eZ\Publish\API\Repository\Events\ContentType\DeleteContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\PublishContentTypeDraftEvent; -use eZ\Publish\API\Repository\Events\ContentType\RemoveContentTypeTranslationEvent; -use eZ\Publish\API\Repository\Events\ContentType\RemoveFieldDefinitionEvent; -use eZ\Publish\API\Repository\Events\ContentType\UnassignContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\UpdateContentTypeDraftEvent; -use eZ\Publish\API\Repository\Events\ContentType\UpdateContentTypeGroupEvent; -use eZ\Publish\API\Repository\Events\ContentType\UpdateFieldDefinitionEvent; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\Core\Event\ContentTypeService; - -class ContentTypeServiceTest extends AbstractServiceTest -{ - public function testAddFieldDefinitionEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAddFieldDefinitionEvent::class, - AddFieldDefinitionEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeDraft::class), - $this->createMock(FieldDefinitionCreateStruct::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->addFieldDefinition(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAddFieldDefinitionEvent::class, 0], - [AddFieldDefinitionEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testAddFieldDefinitionStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAddFieldDefinitionEvent::class, - AddFieldDefinitionEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeDraft::class), - $this->createMock(FieldDefinitionCreateStruct::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeAddFieldDefinitionEvent::class, static function (BeforeAddFieldDefinitionEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->addFieldDefinition(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAddFieldDefinitionEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [AddFieldDefinitionEvent::class, 0], - [BeforeAddFieldDefinitionEvent::class, 0], - ]); - } - - public function testDeleteContentTypeGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteContentTypeGroupEvent::class, - DeleteContentTypeGroupEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeGroup::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->deleteContentTypeGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteContentTypeGroupEvent::class, 0], - [DeleteContentTypeGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteContentTypeGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteContentTypeGroupEvent::class, - DeleteContentTypeGroupEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeGroup::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteContentTypeGroupEvent::class, static function (BeforeDeleteContentTypeGroupEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->deleteContentTypeGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteContentTypeGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteContentTypeGroupEvent::class, 0], - [DeleteContentTypeGroupEvent::class, 0], - ]); - } - - public function testCreateContentTypeDraftEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentTypeDraftEvent::class, - CreateContentTypeDraftEvent::class - ); - - $parameters = [ - $this->createMock(ContentType::class), - ]; - - $contentTypeDraft = $this->createMock(ContentTypeDraft::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('createContentTypeDraft')->willReturn($contentTypeDraft); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContentTypeDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($contentTypeDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentTypeDraftEvent::class, 0], - [CreateContentTypeDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateContentTypeDraftResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentTypeDraftEvent::class, - CreateContentTypeDraftEvent::class - ); - - $parameters = [ - $this->createMock(ContentType::class), - ]; - - $contentTypeDraft = $this->createMock(ContentTypeDraft::class); - $eventContentTypeDraft = $this->createMock(ContentTypeDraft::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('createContentTypeDraft')->willReturn($contentTypeDraft); - - $traceableEventDispatcher->addListener(BeforeCreateContentTypeDraftEvent::class, static function (BeforeCreateContentTypeDraftEvent $event) use ($eventContentTypeDraft) { - $event->setContentTypeDraft($eventContentTypeDraft); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContentTypeDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventContentTypeDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentTypeDraftEvent::class, 10], - [BeforeCreateContentTypeDraftEvent::class, 0], - [CreateContentTypeDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateContentTypeDraftStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentTypeDraftEvent::class, - CreateContentTypeDraftEvent::class - ); - - $parameters = [ - $this->createMock(ContentType::class), - ]; - - $contentTypeDraft = $this->createMock(ContentTypeDraft::class); - $eventContentTypeDraft = $this->createMock(ContentTypeDraft::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('createContentTypeDraft')->willReturn($contentTypeDraft); - - $traceableEventDispatcher->addListener(BeforeCreateContentTypeDraftEvent::class, static function (BeforeCreateContentTypeDraftEvent $event) use ($eventContentTypeDraft) { - $event->setContentTypeDraft($eventContentTypeDraft); - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContentTypeDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventContentTypeDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentTypeDraftEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateContentTypeDraftEvent::class, 0], - [CreateContentTypeDraftEvent::class, 0], - ]); - } - - public function testCreateContentTypeGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentTypeGroupEvent::class, - CreateContentTypeGroupEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeGroupCreateStruct::class), - ]; - - $contentTypeGroup = $this->createMock(ContentTypeGroup::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('createContentTypeGroup')->willReturn($contentTypeGroup); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContentTypeGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($contentTypeGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentTypeGroupEvent::class, 0], - [CreateContentTypeGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateContentTypeGroupResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentTypeGroupEvent::class, - CreateContentTypeGroupEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeGroupCreateStruct::class), - ]; - - $contentTypeGroup = $this->createMock(ContentTypeGroup::class); - $eventContentTypeGroup = $this->createMock(ContentTypeGroup::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('createContentTypeGroup')->willReturn($contentTypeGroup); - - $traceableEventDispatcher->addListener(BeforeCreateContentTypeGroupEvent::class, static function (BeforeCreateContentTypeGroupEvent $event) use ($eventContentTypeGroup) { - $event->setContentTypeGroup($eventContentTypeGroup); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContentTypeGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventContentTypeGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentTypeGroupEvent::class, 10], - [BeforeCreateContentTypeGroupEvent::class, 0], - [CreateContentTypeGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateContentTypeGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentTypeGroupEvent::class, - CreateContentTypeGroupEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeGroupCreateStruct::class), - ]; - - $contentTypeGroup = $this->createMock(ContentTypeGroup::class); - $eventContentTypeGroup = $this->createMock(ContentTypeGroup::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('createContentTypeGroup')->willReturn($contentTypeGroup); - - $traceableEventDispatcher->addListener(BeforeCreateContentTypeGroupEvent::class, static function (BeforeCreateContentTypeGroupEvent $event) use ($eventContentTypeGroup) { - $event->setContentTypeGroup($eventContentTypeGroup); - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContentTypeGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventContentTypeGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentTypeGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateContentTypeGroupEvent::class, 0], - [CreateContentTypeGroupEvent::class, 0], - ]); - } - - public function testUpdateContentTypeGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateContentTypeGroupEvent::class, - UpdateContentTypeGroupEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeGroup::class), - $this->createMock(ContentTypeGroupUpdateStruct::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->updateContentTypeGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeUpdateContentTypeGroupEvent::class, 0], - [UpdateContentTypeGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateContentTypeGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateContentTypeGroupEvent::class, - UpdateContentTypeGroupEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeGroup::class), - $this->createMock(ContentTypeGroupUpdateStruct::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeUpdateContentTypeGroupEvent::class, static function (BeforeUpdateContentTypeGroupEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->updateContentTypeGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeUpdateContentTypeGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateContentTypeGroupEvent::class, 0], - [UpdateContentTypeGroupEvent::class, 0], - ]); - } - - public function testCreateContentTypeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentTypeEvent::class, - CreateContentTypeEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeCreateStruct::class), - [], - ]; - - $contentTypeDraft = $this->createMock(ContentTypeDraft::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('createContentType')->willReturn($contentTypeDraft); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContentType(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($contentTypeDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentTypeEvent::class, 0], - [CreateContentTypeEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateContentTypeResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentTypeEvent::class, - CreateContentTypeEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeCreateStruct::class), - [], - ]; - - $contentTypeDraft = $this->createMock(ContentTypeDraft::class); - $eventContentTypeDraft = $this->createMock(ContentTypeDraft::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('createContentType')->willReturn($contentTypeDraft); - - $traceableEventDispatcher->addListener(BeforeCreateContentTypeEvent::class, static function (BeforeCreateContentTypeEvent $event) use ($eventContentTypeDraft) { - $event->setContentTypeDraft($eventContentTypeDraft); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContentType(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventContentTypeDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentTypeEvent::class, 10], - [BeforeCreateContentTypeEvent::class, 0], - [CreateContentTypeEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateContentTypeStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateContentTypeEvent::class, - CreateContentTypeEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeCreateStruct::class), - [], - ]; - - $contentTypeDraft = $this->createMock(ContentTypeDraft::class); - $eventContentTypeDraft = $this->createMock(ContentTypeDraft::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('createContentType')->willReturn($contentTypeDraft); - - $traceableEventDispatcher->addListener(BeforeCreateContentTypeEvent::class, static function (BeforeCreateContentTypeEvent $event) use ($eventContentTypeDraft) { - $event->setContentTypeDraft($eventContentTypeDraft); - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createContentType(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventContentTypeDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateContentTypeEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateContentTypeEvent::class, 0], - [CreateContentTypeEvent::class, 0], - ]); - } - - public function testRemoveContentTypeTranslationEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemoveContentTypeTranslationEvent::class, - RemoveContentTypeTranslationEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeDraft::class), - 'random_value_5cff79c318f864.57583321', - ]; - - $newContentTypeDraft = $this->createMock(ContentTypeDraft::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('removeContentTypeTranslation')->willReturn($newContentTypeDraft); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->removeContentTypeTranslation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($newContentTypeDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeRemoveContentTypeTranslationEvent::class, 0], - [RemoveContentTypeTranslationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnRemoveContentTypeTranslationResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemoveContentTypeTranslationEvent::class, - RemoveContentTypeTranslationEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeDraft::class), - 'random_value_5cff79c318f913.11826610', - ]; - - $newContentTypeDraft = $this->createMock(ContentTypeDraft::class); - $eventNewContentTypeDraft = $this->createMock(ContentTypeDraft::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('removeContentTypeTranslation')->willReturn($newContentTypeDraft); - - $traceableEventDispatcher->addListener(BeforeRemoveContentTypeTranslationEvent::class, static function (BeforeRemoveContentTypeTranslationEvent $event) use ($eventNewContentTypeDraft) { - $event->setNewContentTypeDraft($eventNewContentTypeDraft); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->removeContentTypeTranslation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventNewContentTypeDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeRemoveContentTypeTranslationEvent::class, 10], - [BeforeRemoveContentTypeTranslationEvent::class, 0], - [RemoveContentTypeTranslationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testRemoveContentTypeTranslationStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemoveContentTypeTranslationEvent::class, - RemoveContentTypeTranslationEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeDraft::class), - 'random_value_5cff79c318f983.61112462', - ]; - - $newContentTypeDraft = $this->createMock(ContentTypeDraft::class); - $eventNewContentTypeDraft = $this->createMock(ContentTypeDraft::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('removeContentTypeTranslation')->willReturn($newContentTypeDraft); - - $traceableEventDispatcher->addListener(BeforeRemoveContentTypeTranslationEvent::class, static function (BeforeRemoveContentTypeTranslationEvent $event) use ($eventNewContentTypeDraft) { - $event->setNewContentTypeDraft($eventNewContentTypeDraft); - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->removeContentTypeTranslation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventNewContentTypeDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeRemoveContentTypeTranslationEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeRemoveContentTypeTranslationEvent::class, 0], - [RemoveContentTypeTranslationEvent::class, 0], - ]); - } - - public function testUnassignContentTypeGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUnassignContentTypeGroupEvent::class, - UnassignContentTypeGroupEvent::class - ); - - $parameters = [ - $this->createMock(ContentType::class), - $this->createMock(ContentTypeGroup::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->unassignContentTypeGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeUnassignContentTypeGroupEvent::class, 0], - [UnassignContentTypeGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUnassignContentTypeGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUnassignContentTypeGroupEvent::class, - UnassignContentTypeGroupEvent::class - ); - - $parameters = [ - $this->createMock(ContentType::class), - $this->createMock(ContentTypeGroup::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeUnassignContentTypeGroupEvent::class, static function (BeforeUnassignContentTypeGroupEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->unassignContentTypeGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeUnassignContentTypeGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUnassignContentTypeGroupEvent::class, 0], - [UnassignContentTypeGroupEvent::class, 0], - ]); - } - - public function testPublishContentTypeDraftEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforePublishContentTypeDraftEvent::class, - PublishContentTypeDraftEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeDraft::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->publishContentTypeDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforePublishContentTypeDraftEvent::class, 0], - [PublishContentTypeDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testPublishContentTypeDraftStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforePublishContentTypeDraftEvent::class, - PublishContentTypeDraftEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeDraft::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforePublishContentTypeDraftEvent::class, static function (BeforePublishContentTypeDraftEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->publishContentTypeDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforePublishContentTypeDraftEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforePublishContentTypeDraftEvent::class, 0], - [PublishContentTypeDraftEvent::class, 0], - ]); - } - - public function testUpdateFieldDefinitionEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateFieldDefinitionEvent::class, - UpdateFieldDefinitionEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeDraft::class), - $this->createMock(FieldDefinition::class), - $this->createMock(FieldDefinitionUpdateStruct::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->updateFieldDefinition(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeUpdateFieldDefinitionEvent::class, 0], - [UpdateFieldDefinitionEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateFieldDefinitionStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateFieldDefinitionEvent::class, - UpdateFieldDefinitionEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeDraft::class), - $this->createMock(FieldDefinition::class), - $this->createMock(FieldDefinitionUpdateStruct::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeUpdateFieldDefinitionEvent::class, static function (BeforeUpdateFieldDefinitionEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->updateFieldDefinition(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeUpdateFieldDefinitionEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateFieldDefinitionEvent::class, 0], - [UpdateFieldDefinitionEvent::class, 0], - ]); - } - - public function testRemoveFieldDefinitionEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemoveFieldDefinitionEvent::class, - RemoveFieldDefinitionEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeDraft::class), - $this->createMock(FieldDefinition::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->removeFieldDefinition(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeRemoveFieldDefinitionEvent::class, 0], - [RemoveFieldDefinitionEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testRemoveFieldDefinitionStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemoveFieldDefinitionEvent::class, - RemoveFieldDefinitionEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeDraft::class), - $this->createMock(FieldDefinition::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeRemoveFieldDefinitionEvent::class, static function (BeforeRemoveFieldDefinitionEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->removeFieldDefinition(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeRemoveFieldDefinitionEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeRemoveFieldDefinitionEvent::class, 0], - [RemoveFieldDefinitionEvent::class, 0], - ]); - } - - public function testAssignContentTypeGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAssignContentTypeGroupEvent::class, - AssignContentTypeGroupEvent::class - ); - - $parameters = [ - $this->createMock(ContentType::class), - $this->createMock(ContentTypeGroup::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->assignContentTypeGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAssignContentTypeGroupEvent::class, 0], - [AssignContentTypeGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testAssignContentTypeGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAssignContentTypeGroupEvent::class, - AssignContentTypeGroupEvent::class - ); - - $parameters = [ - $this->createMock(ContentType::class), - $this->createMock(ContentTypeGroup::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeAssignContentTypeGroupEvent::class, static function (BeforeAssignContentTypeGroupEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->assignContentTypeGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAssignContentTypeGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [AssignContentTypeGroupEvent::class, 0], - [BeforeAssignContentTypeGroupEvent::class, 0], - ]); - } - - public function testUpdateContentTypeDraftEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateContentTypeDraftEvent::class, - UpdateContentTypeDraftEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeDraft::class), - $this->createMock(ContentTypeUpdateStruct::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->updateContentTypeDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeUpdateContentTypeDraftEvent::class, 0], - [UpdateContentTypeDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateContentTypeDraftStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateContentTypeDraftEvent::class, - UpdateContentTypeDraftEvent::class - ); - - $parameters = [ - $this->createMock(ContentTypeDraft::class), - $this->createMock(ContentTypeUpdateStruct::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeUpdateContentTypeDraftEvent::class, static function (BeforeUpdateContentTypeDraftEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->updateContentTypeDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeUpdateContentTypeDraftEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateContentTypeDraftEvent::class, 0], - [UpdateContentTypeDraftEvent::class, 0], - ]); - } - - public function testDeleteContentTypeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteContentTypeEvent::class, - DeleteContentTypeEvent::class - ); - - $parameters = [ - $this->createMock(ContentType::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->deleteContentType(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteContentTypeEvent::class, 0], - [DeleteContentTypeEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteContentTypeStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteContentTypeEvent::class, - DeleteContentTypeEvent::class - ); - - $parameters = [ - $this->createMock(ContentType::class), - ]; - - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteContentTypeEvent::class, static function (BeforeDeleteContentTypeEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $service->deleteContentType(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteContentTypeEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteContentTypeEvent::class, 0], - [DeleteContentTypeEvent::class, 0], - ]); - } - - public function testCopyContentTypeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCopyContentTypeEvent::class, - CopyContentTypeEvent::class - ); - - $parameters = [ - $this->createMock(ContentType::class), - $this->createMock(User::class), - ]; - - $contentTypeCopy = $this->createMock(ContentType::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('copyContentType')->willReturn($contentTypeCopy); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->copyContentType(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($contentTypeCopy, $result); - $this->assertSame($calledListeners, [ - [BeforeCopyContentTypeEvent::class, 0], - [CopyContentTypeEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCopyContentTypeResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCopyContentTypeEvent::class, - CopyContentTypeEvent::class - ); - - $parameters = [ - $this->createMock(ContentType::class), - $this->createMock(User::class), - ]; - - $contentTypeCopy = $this->createMock(ContentType::class); - $eventContentTypeCopy = $this->createMock(ContentType::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('copyContentType')->willReturn($contentTypeCopy); - - $traceableEventDispatcher->addListener(BeforeCopyContentTypeEvent::class, static function (BeforeCopyContentTypeEvent $event) use ($eventContentTypeCopy) { - $event->setContentTypeCopy($eventContentTypeCopy); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->copyContentType(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventContentTypeCopy, $result); - $this->assertSame($calledListeners, [ - [BeforeCopyContentTypeEvent::class, 10], - [BeforeCopyContentTypeEvent::class, 0], - [CopyContentTypeEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCopyContentTypeStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCopyContentTypeEvent::class, - CopyContentTypeEvent::class - ); - - $parameters = [ - $this->createMock(ContentType::class), - $this->createMock(User::class), - ]; - - $contentTypeCopy = $this->createMock(ContentType::class); - $eventContentTypeCopy = $this->createMock(ContentType::class); - $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); - $innerServiceMock->method('copyContentType')->willReturn($contentTypeCopy); - - $traceableEventDispatcher->addListener(BeforeCopyContentTypeEvent::class, static function (BeforeCopyContentTypeEvent $event) use ($eventContentTypeCopy) { - $event->setContentTypeCopy($eventContentTypeCopy); - $event->stopPropagation(); - }, 10); - - $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); - $result = $service->copyContentType(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventContentTypeCopy, $result); - $this->assertSame($calledListeners, [ - [BeforeCopyContentTypeEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCopyContentTypeEvent::class, 0], - [CopyContentTypeEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/LanguageServiceTest.php b/eZ/Publish/Core/Event/Tests/LanguageServiceTest.php deleted file mode 100644 index 224866057b..0000000000 --- a/eZ/Publish/Core/Event/Tests/LanguageServiceTest.php +++ /dev/null @@ -1,481 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\Events\Language\BeforeCreateLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\BeforeDeleteLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\BeforeDisableLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\BeforeEnableLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\BeforeUpdateLanguageNameEvent; -use eZ\Publish\API\Repository\Events\Language\CreateLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\DeleteLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\DisableLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\EnableLanguageEvent; -use eZ\Publish\API\Repository\Events\Language\UpdateLanguageNameEvent; -use eZ\Publish\API\Repository\LanguageService as LanguageServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\LanguageCreateStruct; -use eZ\Publish\Core\Event\LanguageService; - -class LanguageServiceTest extends AbstractServiceTest -{ - public function testDeleteLanguageEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteLanguageEvent::class, - DeleteLanguageEvent::class - ); - - $parameters = [ - $this->createMock(Language::class), - ]; - - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $service->deleteLanguage(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteLanguageEvent::class, 0], - [DeleteLanguageEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteLanguageStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteLanguageEvent::class, - DeleteLanguageEvent::class - ); - - $parameters = [ - $this->createMock(Language::class), - ]; - - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteLanguageEvent::class, static function (BeforeDeleteLanguageEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $service->deleteLanguage(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteLanguageEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteLanguageEvent::class, 0], - [DeleteLanguageEvent::class, 0], - ]); - } - - public function testCreateLanguageEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateLanguageEvent::class, - CreateLanguageEvent::class - ); - - $parameters = [ - $this->createMock(LanguageCreateStruct::class), - ]; - - $language = $this->createMock(Language::class); - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - $innerServiceMock->method('createLanguage')->willReturn($language); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createLanguage(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($language, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateLanguageEvent::class, 0], - [CreateLanguageEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateLanguageResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateLanguageEvent::class, - CreateLanguageEvent::class - ); - - $parameters = [ - $this->createMock(LanguageCreateStruct::class), - ]; - - $language = $this->createMock(Language::class); - $eventLanguage = $this->createMock(Language::class); - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - $innerServiceMock->method('createLanguage')->willReturn($language); - - $traceableEventDispatcher->addListener(BeforeCreateLanguageEvent::class, static function (BeforeCreateLanguageEvent $event) use ($eventLanguage) { - $event->setLanguage($eventLanguage); - }, 10); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createLanguage(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventLanguage, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateLanguageEvent::class, 10], - [BeforeCreateLanguageEvent::class, 0], - [CreateLanguageEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateLanguageStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateLanguageEvent::class, - CreateLanguageEvent::class - ); - - $parameters = [ - $this->createMock(LanguageCreateStruct::class), - ]; - - $language = $this->createMock(Language::class); - $eventLanguage = $this->createMock(Language::class); - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - $innerServiceMock->method('createLanguage')->willReturn($language); - - $traceableEventDispatcher->addListener(BeforeCreateLanguageEvent::class, static function (BeforeCreateLanguageEvent $event) use ($eventLanguage) { - $event->setLanguage($eventLanguage); - $event->stopPropagation(); - }, 10); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createLanguage(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventLanguage, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateLanguageEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateLanguageEvent::class, 0], - [CreateLanguageEvent::class, 0], - ]); - } - - public function testUpdateLanguageNameEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateLanguageNameEvent::class, - UpdateLanguageNameEvent::class - ); - - $parameters = [ - $this->createMock(Language::class), - 'random_value_5cff79c3161276.87987683', - ]; - - $updatedLanguage = $this->createMock(Language::class); - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - $innerServiceMock->method('updateLanguageName')->willReturn($updatedLanguage); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateLanguageName(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($updatedLanguage, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateLanguageNameEvent::class, 0], - [UpdateLanguageNameEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdateLanguageNameResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateLanguageNameEvent::class, - UpdateLanguageNameEvent::class - ); - - $parameters = [ - $this->createMock(Language::class), - 'random_value_5cff79c3161312.94068030', - ]; - - $updatedLanguage = $this->createMock(Language::class); - $eventUpdatedLanguage = $this->createMock(Language::class); - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - $innerServiceMock->method('updateLanguageName')->willReturn($updatedLanguage); - - $traceableEventDispatcher->addListener(BeforeUpdateLanguageNameEvent::class, static function (BeforeUpdateLanguageNameEvent $event) use ($eventUpdatedLanguage) { - $event->setUpdatedLanguage($eventUpdatedLanguage); - }, 10); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateLanguageName(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUpdatedLanguage, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateLanguageNameEvent::class, 10], - [BeforeUpdateLanguageNameEvent::class, 0], - [UpdateLanguageNameEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateLanguageNameStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateLanguageNameEvent::class, - UpdateLanguageNameEvent::class - ); - - $parameters = [ - $this->createMock(Language::class), - 'random_value_5cff79c3161386.01414999', - ]; - - $updatedLanguage = $this->createMock(Language::class); - $eventUpdatedLanguage = $this->createMock(Language::class); - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - $innerServiceMock->method('updateLanguageName')->willReturn($updatedLanguage); - - $traceableEventDispatcher->addListener(BeforeUpdateLanguageNameEvent::class, static function (BeforeUpdateLanguageNameEvent $event) use ($eventUpdatedLanguage) { - $event->setUpdatedLanguage($eventUpdatedLanguage); - $event->stopPropagation(); - }, 10); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateLanguageName(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUpdatedLanguage, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateLanguageNameEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateLanguageNameEvent::class, 0], - [UpdateLanguageNameEvent::class, 0], - ]); - } - - public function testDisableLanguageEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDisableLanguageEvent::class, - DisableLanguageEvent::class - ); - - $parameters = [ - $this->createMock(Language::class), - ]; - - $disabledLanguage = $this->createMock(Language::class); - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - $innerServiceMock->method('disableLanguage')->willReturn($disabledLanguage); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $result = $service->disableLanguage(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($disabledLanguage, $result); - $this->assertSame($calledListeners, [ - [BeforeDisableLanguageEvent::class, 0], - [DisableLanguageEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnDisableLanguageResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDisableLanguageEvent::class, - DisableLanguageEvent::class - ); - - $parameters = [ - $this->createMock(Language::class), - ]; - - $disabledLanguage = $this->createMock(Language::class); - $eventDisabledLanguage = $this->createMock(Language::class); - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - $innerServiceMock->method('disableLanguage')->willReturn($disabledLanguage); - - $traceableEventDispatcher->addListener(BeforeDisableLanguageEvent::class, static function (BeforeDisableLanguageEvent $event) use ($eventDisabledLanguage) { - $event->setDisabledLanguage($eventDisabledLanguage); - }, 10); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $result = $service->disableLanguage(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventDisabledLanguage, $result); - $this->assertSame($calledListeners, [ - [BeforeDisableLanguageEvent::class, 10], - [BeforeDisableLanguageEvent::class, 0], - [DisableLanguageEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDisableLanguageStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDisableLanguageEvent::class, - DisableLanguageEvent::class - ); - - $parameters = [ - $this->createMock(Language::class), - ]; - - $disabledLanguage = $this->createMock(Language::class); - $eventDisabledLanguage = $this->createMock(Language::class); - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - $innerServiceMock->method('disableLanguage')->willReturn($disabledLanguage); - - $traceableEventDispatcher->addListener(BeforeDisableLanguageEvent::class, static function (BeforeDisableLanguageEvent $event) use ($eventDisabledLanguage) { - $event->setDisabledLanguage($eventDisabledLanguage); - $event->stopPropagation(); - }, 10); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $result = $service->disableLanguage(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventDisabledLanguage, $result); - $this->assertSame($calledListeners, [ - [BeforeDisableLanguageEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDisableLanguageEvent::class, 0], - [DisableLanguageEvent::class, 0], - ]); - } - - public function testEnableLanguageEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeEnableLanguageEvent::class, - EnableLanguageEvent::class - ); - - $parameters = [ - $this->createMock(Language::class), - ]; - - $enabledLanguage = $this->createMock(Language::class); - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - $innerServiceMock->method('enableLanguage')->willReturn($enabledLanguage); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $result = $service->enableLanguage(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($enabledLanguage, $result); - $this->assertSame($calledListeners, [ - [BeforeEnableLanguageEvent::class, 0], - [EnableLanguageEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnEnableLanguageResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeEnableLanguageEvent::class, - EnableLanguageEvent::class - ); - - $parameters = [ - $this->createMock(Language::class), - ]; - - $enabledLanguage = $this->createMock(Language::class); - $eventEnabledLanguage = $this->createMock(Language::class); - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - $innerServiceMock->method('enableLanguage')->willReturn($enabledLanguage); - - $traceableEventDispatcher->addListener(BeforeEnableLanguageEvent::class, static function (BeforeEnableLanguageEvent $event) use ($eventEnabledLanguage) { - $event->setEnabledLanguage($eventEnabledLanguage); - }, 10); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $result = $service->enableLanguage(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventEnabledLanguage, $result); - $this->assertSame($calledListeners, [ - [BeforeEnableLanguageEvent::class, 10], - [BeforeEnableLanguageEvent::class, 0], - [EnableLanguageEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testEnableLanguageStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeEnableLanguageEvent::class, - EnableLanguageEvent::class - ); - - $parameters = [ - $this->createMock(Language::class), - ]; - - $enabledLanguage = $this->createMock(Language::class); - $eventEnabledLanguage = $this->createMock(Language::class); - $innerServiceMock = $this->createMock(LanguageServiceInterface::class); - $innerServiceMock->method('enableLanguage')->willReturn($enabledLanguage); - - $traceableEventDispatcher->addListener(BeforeEnableLanguageEvent::class, static function (BeforeEnableLanguageEvent $event) use ($eventEnabledLanguage) { - $event->setEnabledLanguage($eventEnabledLanguage); - $event->stopPropagation(); - }, 10); - - $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); - $result = $service->enableLanguage(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventEnabledLanguage, $result); - $this->assertSame($calledListeners, [ - [BeforeEnableLanguageEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeEnableLanguageEvent::class, 0], - [EnableLanguageEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/LocationServiceTest.php b/eZ/Publish/Core/Event/Tests/LocationServiceTest.php deleted file mode 100644 index d615badb20..0000000000 --- a/eZ/Publish/Core/Event/Tests/LocationServiceTest.php +++ /dev/null @@ -1,712 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\Events\Location\BeforeCopySubtreeEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeCreateLocationEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeDeleteLocationEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeHideLocationEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeMoveSubtreeEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeSwapLocationEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeUnhideLocationEvent; -use eZ\Publish\API\Repository\Events\Location\BeforeUpdateLocationEvent; -use eZ\Publish\API\Repository\Events\Location\CopySubtreeEvent; -use eZ\Publish\API\Repository\Events\Location\CreateLocationEvent; -use eZ\Publish\API\Repository\Events\Location\DeleteLocationEvent; -use eZ\Publish\API\Repository\Events\Location\HideLocationEvent; -use eZ\Publish\API\Repository\Events\Location\MoveSubtreeEvent; -use eZ\Publish\API\Repository\Events\Location\SwapLocationEvent; -use eZ\Publish\API\Repository\Events\Location\UnhideLocationEvent; -use eZ\Publish\API\Repository\Events\Location\UpdateLocationEvent; -use eZ\Publish\API\Repository\LocationService as LocationServiceInterface; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct; -use eZ\Publish\Core\Event\LocationService; - -class LocationServiceTest extends AbstractServiceTest -{ - public function testCopySubtreeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCopySubtreeEvent::class, - CopySubtreeEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - $this->createMock(Location::class), - ]; - - $location = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('copySubtree')->willReturn($location); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->copySubtree(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($location, $result); - $this->assertSame($calledListeners, [ - [BeforeCopySubtreeEvent::class, 0], - [CopySubtreeEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCopySubtreeResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCopySubtreeEvent::class, - CopySubtreeEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - $this->createMock(Location::class), - ]; - - $location = $this->createMock(Location::class); - $eventLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('copySubtree')->willReturn($location); - - $traceableEventDispatcher->addListener(BeforeCopySubtreeEvent::class, static function (BeforeCopySubtreeEvent $event) use ($eventLocation) { - $event->setLocation($eventLocation); - }, 10); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->copySubtree(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeCopySubtreeEvent::class, 10], - [BeforeCopySubtreeEvent::class, 0], - [CopySubtreeEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCopySubtreeStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCopySubtreeEvent::class, - CopySubtreeEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - $this->createMock(Location::class), - ]; - - $location = $this->createMock(Location::class); - $eventLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('copySubtree')->willReturn($location); - - $traceableEventDispatcher->addListener(BeforeCopySubtreeEvent::class, static function (BeforeCopySubtreeEvent $event) use ($eventLocation) { - $event->setLocation($eventLocation); - $event->stopPropagation(); - }, 10); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->copySubtree(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeCopySubtreeEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCopySubtreeEvent::class, 0], - [CopySubtreeEvent::class, 0], - ]); - } - - public function testDeleteLocationEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteLocationEvent::class, - DeleteLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $service->deleteLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteLocationEvent::class, 0], - [DeleteLocationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteLocationStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteLocationEvent::class, - DeleteLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteLocationEvent::class, static function (BeforeDeleteLocationEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $service->deleteLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteLocationEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteLocationEvent::class, 0], - [DeleteLocationEvent::class, 0], - ]); - } - - public function testUnhideLocationEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUnhideLocationEvent::class, - UnhideLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $revealedLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('unhideLocation')->willReturn($revealedLocation); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->unhideLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($revealedLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeUnhideLocationEvent::class, 0], - [UnhideLocationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUnhideLocationResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUnhideLocationEvent::class, - UnhideLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $revealedLocation = $this->createMock(Location::class); - $eventRevealedLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('unhideLocation')->willReturn($revealedLocation); - - $traceableEventDispatcher->addListener(BeforeUnhideLocationEvent::class, static function (BeforeUnhideLocationEvent $event) use ($eventRevealedLocation) { - $event->setRevealedLocation($eventRevealedLocation); - }, 10); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->unhideLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventRevealedLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeUnhideLocationEvent::class, 10], - [BeforeUnhideLocationEvent::class, 0], - [UnhideLocationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUnhideLocationStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUnhideLocationEvent::class, - UnhideLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $revealedLocation = $this->createMock(Location::class); - $eventRevealedLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('unhideLocation')->willReturn($revealedLocation); - - $traceableEventDispatcher->addListener(BeforeUnhideLocationEvent::class, static function (BeforeUnhideLocationEvent $event) use ($eventRevealedLocation) { - $event->setRevealedLocation($eventRevealedLocation); - $event->stopPropagation(); - }, 10); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->unhideLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventRevealedLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeUnhideLocationEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUnhideLocationEvent::class, 0], - [UnhideLocationEvent::class, 0], - ]); - } - - public function testHideLocationEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeHideLocationEvent::class, - HideLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $hiddenLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('hideLocation')->willReturn($hiddenLocation); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->hideLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($hiddenLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeHideLocationEvent::class, 0], - [HideLocationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnHideLocationResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeHideLocationEvent::class, - HideLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $hiddenLocation = $this->createMock(Location::class); - $eventHiddenLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('hideLocation')->willReturn($hiddenLocation); - - $traceableEventDispatcher->addListener(BeforeHideLocationEvent::class, static function (BeforeHideLocationEvent $event) use ($eventHiddenLocation) { - $event->setHiddenLocation($eventHiddenLocation); - }, 10); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->hideLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventHiddenLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeHideLocationEvent::class, 10], - [BeforeHideLocationEvent::class, 0], - [HideLocationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testHideLocationStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeHideLocationEvent::class, - HideLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $hiddenLocation = $this->createMock(Location::class); - $eventHiddenLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('hideLocation')->willReturn($hiddenLocation); - - $traceableEventDispatcher->addListener(BeforeHideLocationEvent::class, static function (BeforeHideLocationEvent $event) use ($eventHiddenLocation) { - $event->setHiddenLocation($eventHiddenLocation); - $event->stopPropagation(); - }, 10); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->hideLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventHiddenLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeHideLocationEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeHideLocationEvent::class, 0], - [HideLocationEvent::class, 0], - ]); - } - - public function testSwapLocationEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeSwapLocationEvent::class, - SwapLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - $this->createMock(Location::class), - ]; - - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $service->swapLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeSwapLocationEvent::class, 0], - [SwapLocationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testSwapLocationStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeSwapLocationEvent::class, - SwapLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - $this->createMock(Location::class), - ]; - - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeSwapLocationEvent::class, static function (BeforeSwapLocationEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $service->swapLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeSwapLocationEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeSwapLocationEvent::class, 0], - [SwapLocationEvent::class, 0], - ]); - } - - public function testMoveSubtreeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeMoveSubtreeEvent::class, - MoveSubtreeEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - $this->createMock(Location::class), - ]; - - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $service->moveSubtree(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeMoveSubtreeEvent::class, 0], - [MoveSubtreeEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testMoveSubtreeStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeMoveSubtreeEvent::class, - MoveSubtreeEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - $this->createMock(Location::class), - ]; - - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeMoveSubtreeEvent::class, static function (BeforeMoveSubtreeEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $service->moveSubtree(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeMoveSubtreeEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeMoveSubtreeEvent::class, 0], - [MoveSubtreeEvent::class, 0], - ]); - } - - public function testUpdateLocationEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateLocationEvent::class, - UpdateLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - $this->createMock(LocationUpdateStruct::class), - ]; - - $updatedLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('updateLocation')->willReturn($updatedLocation); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($updatedLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateLocationEvent::class, 0], - [UpdateLocationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdateLocationResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateLocationEvent::class, - UpdateLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - $this->createMock(LocationUpdateStruct::class), - ]; - - $updatedLocation = $this->createMock(Location::class); - $eventUpdatedLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('updateLocation')->willReturn($updatedLocation); - - $traceableEventDispatcher->addListener(BeforeUpdateLocationEvent::class, static function (BeforeUpdateLocationEvent $event) use ($eventUpdatedLocation) { - $event->setUpdatedLocation($eventUpdatedLocation); - }, 10); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUpdatedLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateLocationEvent::class, 10], - [BeforeUpdateLocationEvent::class, 0], - [UpdateLocationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateLocationStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateLocationEvent::class, - UpdateLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - $this->createMock(LocationUpdateStruct::class), - ]; - - $updatedLocation = $this->createMock(Location::class); - $eventUpdatedLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('updateLocation')->willReturn($updatedLocation); - - $traceableEventDispatcher->addListener(BeforeUpdateLocationEvent::class, static function (BeforeUpdateLocationEvent $event) use ($eventUpdatedLocation) { - $event->setUpdatedLocation($eventUpdatedLocation); - $event->stopPropagation(); - }, 10); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUpdatedLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateLocationEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateLocationEvent::class, 0], - [UpdateLocationEvent::class, 0], - ]); - } - - public function testCreateLocationEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateLocationEvent::class, - CreateLocationEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(LocationCreateStruct::class), - ]; - - $location = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('createLocation')->willReturn($location); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($location, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateLocationEvent::class, 0], - [CreateLocationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateLocationResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateLocationEvent::class, - CreateLocationEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(LocationCreateStruct::class), - ]; - - $location = $this->createMock(Location::class); - $eventLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('createLocation')->willReturn($location); - - $traceableEventDispatcher->addListener(BeforeCreateLocationEvent::class, static function (BeforeCreateLocationEvent $event) use ($eventLocation) { - $event->setLocation($eventLocation); - }, 10); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateLocationEvent::class, 10], - [BeforeCreateLocationEvent::class, 0], - [CreateLocationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateLocationStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateLocationEvent::class, - CreateLocationEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(LocationCreateStruct::class), - ]; - - $location = $this->createMock(Location::class); - $eventLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(LocationServiceInterface::class); - $innerServiceMock->method('createLocation')->willReturn($location); - - $traceableEventDispatcher->addListener(BeforeCreateLocationEvent::class, static function (BeforeCreateLocationEvent $event) use ($eventLocation) { - $event->setLocation($eventLocation); - $event->stopPropagation(); - }, 10); - - $service = new LocationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateLocationEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateLocationEvent::class, 0], - [CreateLocationEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/NotificationServiceTest.php b/eZ/Publish/Core/Event/Tests/NotificationServiceTest.php deleted file mode 100644 index f8f7d037c9..0000000000 --- a/eZ/Publish/Core/Event/Tests/NotificationServiceTest.php +++ /dev/null @@ -1,234 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\Events\Notification\BeforeCreateNotificationEvent; -use eZ\Publish\API\Repository\Events\Notification\BeforeDeleteNotificationEvent; -use eZ\Publish\API\Repository\Events\Notification\BeforeMarkNotificationAsReadEvent; -use eZ\Publish\API\Repository\Events\Notification\CreateNotificationEvent; -use eZ\Publish\API\Repository\Events\Notification\DeleteNotificationEvent; -use eZ\Publish\API\Repository\Events\Notification\MarkNotificationAsReadEvent; -use eZ\Publish\API\Repository\NotificationService as NotificationServiceInterface; -use eZ\Publish\API\Repository\Values\Notification\CreateStruct; -use eZ\Publish\API\Repository\Values\Notification\Notification; -use eZ\Publish\Core\Event\NotificationService; - -class NotificationServiceTest extends AbstractServiceTest -{ - public function testCreateNotificationEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateNotificationEvent::class, - CreateNotificationEvent::class - ); - - $parameters = [ - $this->createMock(CreateStruct::class), - ]; - - $notification = $this->createMock(Notification::class); - $innerServiceMock = $this->createMock(NotificationServiceInterface::class); - $innerServiceMock->method('createNotification')->willReturn($notification); - - $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createNotification(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($notification, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateNotificationEvent::class, 0], - [CreateNotificationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateNotificationResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateNotificationEvent::class, - CreateNotificationEvent::class - ); - - $parameters = [ - $this->createMock(CreateStruct::class), - ]; - - $notification = $this->createMock(Notification::class); - $eventNotification = $this->createMock(Notification::class); - $innerServiceMock = $this->createMock(NotificationServiceInterface::class); - $innerServiceMock->method('createNotification')->willReturn($notification); - - $traceableEventDispatcher->addListener(BeforeCreateNotificationEvent::class, static function (BeforeCreateNotificationEvent $event) use ($eventNotification) { - $event->setNotification($eventNotification); - }, 10); - - $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createNotification(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventNotification, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateNotificationEvent::class, 10], - [BeforeCreateNotificationEvent::class, 0], - [CreateNotificationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateNotificationStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateNotificationEvent::class, - CreateNotificationEvent::class - ); - - $parameters = [ - $this->createMock(CreateStruct::class), - ]; - - $notification = $this->createMock(Notification::class); - $eventNotification = $this->createMock(Notification::class); - $innerServiceMock = $this->createMock(NotificationServiceInterface::class); - $innerServiceMock->method('createNotification')->willReturn($notification); - - $traceableEventDispatcher->addListener(BeforeCreateNotificationEvent::class, static function (BeforeCreateNotificationEvent $event) use ($eventNotification) { - $event->setNotification($eventNotification); - $event->stopPropagation(); - }, 10); - - $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createNotification(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventNotification, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateNotificationEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateNotificationEvent::class, 0], - [CreateNotificationEvent::class, 0], - ]); - } - - public function testDeleteNotificationEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteNotificationEvent::class, - DeleteNotificationEvent::class - ); - - $parameters = [ - $this->createMock(Notification::class), - ]; - - $innerServiceMock = $this->createMock(NotificationServiceInterface::class); - - $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); - $service->deleteNotification(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteNotificationEvent::class, 0], - [DeleteNotificationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteNotificationStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteNotificationEvent::class, - DeleteNotificationEvent::class - ); - - $parameters = [ - $this->createMock(Notification::class), - ]; - - $innerServiceMock = $this->createMock(NotificationServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteNotificationEvent::class, static function (BeforeDeleteNotificationEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); - $service->deleteNotification(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteNotificationEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteNotificationEvent::class, 0], - [DeleteNotificationEvent::class, 0], - ]); - } - - public function testMarkNotificationAsReadEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeMarkNotificationAsReadEvent::class, - MarkNotificationAsReadEvent::class - ); - - $parameters = [ - $this->createMock(Notification::class), - ]; - - $innerServiceMock = $this->createMock(NotificationServiceInterface::class); - - $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); - $service->markNotificationAsRead(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeMarkNotificationAsReadEvent::class, 0], - [MarkNotificationAsReadEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testMarkNotificationAsReadStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeMarkNotificationAsReadEvent::class, - MarkNotificationAsReadEvent::class - ); - - $parameters = [ - $this->createMock(Notification::class), - ]; - - $innerServiceMock = $this->createMock(NotificationServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeMarkNotificationAsReadEvent::class, static function (BeforeMarkNotificationAsReadEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); - $service->markNotificationAsRead(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeMarkNotificationAsReadEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeMarkNotificationAsReadEvent::class, 0], - [MarkNotificationAsReadEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/ObjectStateServiceTest.php b/eZ/Publish/Core/Event/Tests/ObjectStateServiceTest.php deleted file mode 100644 index dd55a723e7..0000000000 --- a/eZ/Publish/Core/Event/Tests/ObjectStateServiceTest.php +++ /dev/null @@ -1,675 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\Events\ObjectState\BeforeCreateObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeCreateObjectStateGroupEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeDeleteObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeDeleteObjectStateGroupEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeSetContentStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeSetPriorityOfObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeUpdateObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\BeforeUpdateObjectStateGroupEvent; -use eZ\Publish\API\Repository\Events\ObjectState\CreateObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\CreateObjectStateGroupEvent; -use eZ\Publish\API\Repository\Events\ObjectState\DeleteObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\DeleteObjectStateGroupEvent; -use eZ\Publish\API\Repository\Events\ObjectState\SetContentStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\SetPriorityOfObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\UpdateObjectStateEvent; -use eZ\Publish\API\Repository\Events\ObjectState\UpdateObjectStateGroupEvent; -use eZ\Publish\API\Repository\ObjectStateService as ObjectStateServiceInterface; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectState; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateUpdateStruct; -use eZ\Publish\Core\Event\ObjectStateService; - -class ObjectStateServiceTest extends AbstractServiceTest -{ - public function testSetContentStateEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeSetContentStateEvent::class, - SetContentStateEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(ObjectStateGroup::class), - $this->createMock(ObjectState::class), - ]; - - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $service->setContentState(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeSetContentStateEvent::class, 0], - [SetContentStateEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testSetContentStateStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeSetContentStateEvent::class, - SetContentStateEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(ObjectStateGroup::class), - $this->createMock(ObjectState::class), - ]; - - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeSetContentStateEvent::class, static function (BeforeSetContentStateEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $service->setContentState(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeSetContentStateEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeSetContentStateEvent::class, 0], - [SetContentStateEvent::class, 0], - ]); - } - - public function testCreateObjectStateGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateObjectStateGroupEvent::class, - CreateObjectStateGroupEvent::class - ); - - $parameters = [ - $this->createMock(ObjectStateGroupCreateStruct::class), - ]; - - $objectStateGroup = $this->createMock(ObjectStateGroup::class); - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - $innerServiceMock->method('createObjectStateGroup')->willReturn($objectStateGroup); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createObjectStateGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($objectStateGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateObjectStateGroupEvent::class, 0], - [CreateObjectStateGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateObjectStateGroupResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateObjectStateGroupEvent::class, - CreateObjectStateGroupEvent::class - ); - - $parameters = [ - $this->createMock(ObjectStateGroupCreateStruct::class), - ]; - - $objectStateGroup = $this->createMock(ObjectStateGroup::class); - $eventObjectStateGroup = $this->createMock(ObjectStateGroup::class); - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - $innerServiceMock->method('createObjectStateGroup')->willReturn($objectStateGroup); - - $traceableEventDispatcher->addListener(BeforeCreateObjectStateGroupEvent::class, static function (BeforeCreateObjectStateGroupEvent $event) use ($eventObjectStateGroup) { - $event->setObjectStateGroup($eventObjectStateGroup); - }, 10); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createObjectStateGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventObjectStateGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateObjectStateGroupEvent::class, 10], - [BeforeCreateObjectStateGroupEvent::class, 0], - [CreateObjectStateGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateObjectStateGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateObjectStateGroupEvent::class, - CreateObjectStateGroupEvent::class - ); - - $parameters = [ - $this->createMock(ObjectStateGroupCreateStruct::class), - ]; - - $objectStateGroup = $this->createMock(ObjectStateGroup::class); - $eventObjectStateGroup = $this->createMock(ObjectStateGroup::class); - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - $innerServiceMock->method('createObjectStateGroup')->willReturn($objectStateGroup); - - $traceableEventDispatcher->addListener(BeforeCreateObjectStateGroupEvent::class, static function (BeforeCreateObjectStateGroupEvent $event) use ($eventObjectStateGroup) { - $event->setObjectStateGroup($eventObjectStateGroup); - $event->stopPropagation(); - }, 10); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createObjectStateGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventObjectStateGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateObjectStateGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateObjectStateGroupEvent::class, 0], - [CreateObjectStateGroupEvent::class, 0], - ]); - } - - public function testUpdateObjectStateEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateObjectStateEvent::class, - UpdateObjectStateEvent::class - ); - - $parameters = [ - $this->createMock(ObjectState::class), - $this->createMock(ObjectStateUpdateStruct::class), - ]; - - $updatedObjectState = $this->createMock(ObjectState::class); - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - $innerServiceMock->method('updateObjectState')->willReturn($updatedObjectState); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateObjectState(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($updatedObjectState, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateObjectStateEvent::class, 0], - [UpdateObjectStateEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdateObjectStateResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateObjectStateEvent::class, - UpdateObjectStateEvent::class - ); - - $parameters = [ - $this->createMock(ObjectState::class), - $this->createMock(ObjectStateUpdateStruct::class), - ]; - - $updatedObjectState = $this->createMock(ObjectState::class); - $eventUpdatedObjectState = $this->createMock(ObjectState::class); - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - $innerServiceMock->method('updateObjectState')->willReturn($updatedObjectState); - - $traceableEventDispatcher->addListener(BeforeUpdateObjectStateEvent::class, static function (BeforeUpdateObjectStateEvent $event) use ($eventUpdatedObjectState) { - $event->setUpdatedObjectState($eventUpdatedObjectState); - }, 10); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateObjectState(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUpdatedObjectState, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateObjectStateEvent::class, 10], - [BeforeUpdateObjectStateEvent::class, 0], - [UpdateObjectStateEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateObjectStateStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateObjectStateEvent::class, - UpdateObjectStateEvent::class - ); - - $parameters = [ - $this->createMock(ObjectState::class), - $this->createMock(ObjectStateUpdateStruct::class), - ]; - - $updatedObjectState = $this->createMock(ObjectState::class); - $eventUpdatedObjectState = $this->createMock(ObjectState::class); - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - $innerServiceMock->method('updateObjectState')->willReturn($updatedObjectState); - - $traceableEventDispatcher->addListener(BeforeUpdateObjectStateEvent::class, static function (BeforeUpdateObjectStateEvent $event) use ($eventUpdatedObjectState) { - $event->setUpdatedObjectState($eventUpdatedObjectState); - $event->stopPropagation(); - }, 10); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateObjectState(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUpdatedObjectState, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateObjectStateEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateObjectStateEvent::class, 0], - [UpdateObjectStateEvent::class, 0], - ]); - } - - public function testCreateObjectStateEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateObjectStateEvent::class, - CreateObjectStateEvent::class - ); - - $parameters = [ - $this->createMock(ObjectStateGroup::class), - $this->createMock(ObjectStateCreateStruct::class), - ]; - - $objectState = $this->createMock(ObjectState::class); - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - $innerServiceMock->method('createObjectState')->willReturn($objectState); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createObjectState(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($objectState, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateObjectStateEvent::class, 0], - [CreateObjectStateEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateObjectStateResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateObjectStateEvent::class, - CreateObjectStateEvent::class - ); - - $parameters = [ - $this->createMock(ObjectStateGroup::class), - $this->createMock(ObjectStateCreateStruct::class), - ]; - - $objectState = $this->createMock(ObjectState::class); - $eventObjectState = $this->createMock(ObjectState::class); - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - $innerServiceMock->method('createObjectState')->willReturn($objectState); - - $traceableEventDispatcher->addListener(BeforeCreateObjectStateEvent::class, static function (BeforeCreateObjectStateEvent $event) use ($eventObjectState) { - $event->setObjectState($eventObjectState); - }, 10); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createObjectState(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventObjectState, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateObjectStateEvent::class, 10], - [BeforeCreateObjectStateEvent::class, 0], - [CreateObjectStateEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateObjectStateStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateObjectStateEvent::class, - CreateObjectStateEvent::class - ); - - $parameters = [ - $this->createMock(ObjectStateGroup::class), - $this->createMock(ObjectStateCreateStruct::class), - ]; - - $objectState = $this->createMock(ObjectState::class); - $eventObjectState = $this->createMock(ObjectState::class); - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - $innerServiceMock->method('createObjectState')->willReturn($objectState); - - $traceableEventDispatcher->addListener(BeforeCreateObjectStateEvent::class, static function (BeforeCreateObjectStateEvent $event) use ($eventObjectState) { - $event->setObjectState($eventObjectState); - $event->stopPropagation(); - }, 10); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createObjectState(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventObjectState, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateObjectStateEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateObjectStateEvent::class, 0], - [CreateObjectStateEvent::class, 0], - ]); - } - - public function testUpdateObjectStateGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateObjectStateGroupEvent::class, - UpdateObjectStateGroupEvent::class - ); - - $parameters = [ - $this->createMock(ObjectStateGroup::class), - $this->createMock(ObjectStateGroupUpdateStruct::class), - ]; - - $updatedObjectStateGroup = $this->createMock(ObjectStateGroup::class); - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - $innerServiceMock->method('updateObjectStateGroup')->willReturn($updatedObjectStateGroup); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateObjectStateGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($updatedObjectStateGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateObjectStateGroupEvent::class, 0], - [UpdateObjectStateGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdateObjectStateGroupResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateObjectStateGroupEvent::class, - UpdateObjectStateGroupEvent::class - ); - - $parameters = [ - $this->createMock(ObjectStateGroup::class), - $this->createMock(ObjectStateGroupUpdateStruct::class), - ]; - - $updatedObjectStateGroup = $this->createMock(ObjectStateGroup::class); - $eventUpdatedObjectStateGroup = $this->createMock(ObjectStateGroup::class); - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - $innerServiceMock->method('updateObjectStateGroup')->willReturn($updatedObjectStateGroup); - - $traceableEventDispatcher->addListener(BeforeUpdateObjectStateGroupEvent::class, static function (BeforeUpdateObjectStateGroupEvent $event) use ($eventUpdatedObjectStateGroup) { - $event->setUpdatedObjectStateGroup($eventUpdatedObjectStateGroup); - }, 10); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateObjectStateGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUpdatedObjectStateGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateObjectStateGroupEvent::class, 10], - [BeforeUpdateObjectStateGroupEvent::class, 0], - [UpdateObjectStateGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateObjectStateGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateObjectStateGroupEvent::class, - UpdateObjectStateGroupEvent::class - ); - - $parameters = [ - $this->createMock(ObjectStateGroup::class), - $this->createMock(ObjectStateGroupUpdateStruct::class), - ]; - - $updatedObjectStateGroup = $this->createMock(ObjectStateGroup::class); - $eventUpdatedObjectStateGroup = $this->createMock(ObjectStateGroup::class); - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - $innerServiceMock->method('updateObjectStateGroup')->willReturn($updatedObjectStateGroup); - - $traceableEventDispatcher->addListener(BeforeUpdateObjectStateGroupEvent::class, static function (BeforeUpdateObjectStateGroupEvent $event) use ($eventUpdatedObjectStateGroup) { - $event->setUpdatedObjectStateGroup($eventUpdatedObjectStateGroup); - $event->stopPropagation(); - }, 10); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateObjectStateGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUpdatedObjectStateGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateObjectStateGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateObjectStateGroupEvent::class, 0], - [UpdateObjectStateGroupEvent::class, 0], - ]); - } - - public function testSetPriorityOfObjectStateEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeSetPriorityOfObjectStateEvent::class, - SetPriorityOfObjectStateEvent::class - ); - - $parameters = [ - $this->createMock(ObjectState::class), - 100, - ]; - - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $service->setPriorityOfObjectState(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeSetPriorityOfObjectStateEvent::class, 0], - [SetPriorityOfObjectStateEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testSetPriorityOfObjectStateStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeSetPriorityOfObjectStateEvent::class, - SetPriorityOfObjectStateEvent::class - ); - - $parameters = [ - $this->createMock(ObjectState::class), - 100, - ]; - - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeSetPriorityOfObjectStateEvent::class, static function (BeforeSetPriorityOfObjectStateEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $service->setPriorityOfObjectState(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeSetPriorityOfObjectStateEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeSetPriorityOfObjectStateEvent::class, 0], - [SetPriorityOfObjectStateEvent::class, 0], - ]); - } - - public function testDeleteObjectStateGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteObjectStateGroupEvent::class, - DeleteObjectStateGroupEvent::class - ); - - $parameters = [ - $this->createMock(ObjectStateGroup::class), - ]; - - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $service->deleteObjectStateGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteObjectStateGroupEvent::class, 0], - [DeleteObjectStateGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteObjectStateGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteObjectStateGroupEvent::class, - DeleteObjectStateGroupEvent::class - ); - - $parameters = [ - $this->createMock(ObjectStateGroup::class), - ]; - - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteObjectStateGroupEvent::class, static function (BeforeDeleteObjectStateGroupEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $service->deleteObjectStateGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteObjectStateGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteObjectStateGroupEvent::class, 0], - [DeleteObjectStateGroupEvent::class, 0], - ]); - } - - public function testDeleteObjectStateEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteObjectStateEvent::class, - DeleteObjectStateEvent::class - ); - - $parameters = [ - $this->createMock(ObjectState::class), - ]; - - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $service->deleteObjectState(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteObjectStateEvent::class, 0], - [DeleteObjectStateEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteObjectStateStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteObjectStateEvent::class, - DeleteObjectStateEvent::class - ); - - $parameters = [ - $this->createMock(ObjectState::class), - ]; - - $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteObjectStateEvent::class, static function (BeforeDeleteObjectStateEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); - $service->deleteObjectState(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteObjectStateEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteObjectStateEvent::class, 0], - [DeleteObjectStateEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/RoleServiceTest.php b/eZ/Publish/Core/Event/Tests/RoleServiceTest.php deleted file mode 100644 index 948a821404..0000000000 --- a/eZ/Publish/Core/Event/Tests/RoleServiceTest.php +++ /dev/null @@ -1,1007 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\Events\Role\AddPolicyByRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\AssignRoleToUserEvent; -use eZ\Publish\API\Repository\Events\Role\AssignRoleToUserGroupEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeAddPolicyByRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeAssignRoleToUserEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeAssignRoleToUserGroupEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeCreateRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeCreateRoleEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeDeleteRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeDeleteRoleEvent; -use eZ\Publish\API\Repository\Events\Role\BeforePublishRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeRemovePolicyByRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeRemoveRoleAssignmentEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeUpdatePolicyByRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\BeforeUpdateRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\CreateRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\CreateRoleEvent; -use eZ\Publish\API\Repository\Events\Role\DeleteRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\DeleteRoleEvent; -use eZ\Publish\API\Repository\Events\Role\PublishRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\RemovePolicyByRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\RemoveRoleAssignmentEvent; -use eZ\Publish\API\Repository\Events\Role\UpdatePolicyByRoleDraftEvent; -use eZ\Publish\API\Repository\Events\Role\UpdateRoleDraftEvent; -use eZ\Publish\API\Repository\RoleService as RoleServiceInterface; -use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation; -use eZ\Publish\API\Repository\Values\User\PolicyCreateStruct; -use eZ\Publish\API\Repository\Values\User\PolicyDraft; -use eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct; -use eZ\Publish\API\Repository\Values\User\Role; -use eZ\Publish\API\Repository\Values\User\RoleAssignment; -use eZ\Publish\API\Repository\Values\User\RoleCreateStruct; -use eZ\Publish\API\Repository\Values\User\RoleDraft; -use eZ\Publish\API\Repository\Values\User\RoleUpdateStruct; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\Core\Event\RoleService; - -class RoleServiceTest extends AbstractServiceTest -{ - public function testPublishRoleDraftEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforePublishRoleDraftEvent::class, - PublishRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - ]; - - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $service->publishRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforePublishRoleDraftEvent::class, 0], - [PublishRoleDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testPublishRoleDraftStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforePublishRoleDraftEvent::class, - PublishRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - ]; - - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforePublishRoleDraftEvent::class, static function (BeforePublishRoleDraftEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $service->publishRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforePublishRoleDraftEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforePublishRoleDraftEvent::class, 0], - [PublishRoleDraftEvent::class, 0], - ]); - } - - public function testAssignRoleToUserEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAssignRoleToUserEvent::class, - AssignRoleToUserEvent::class - ); - - $parameters = [ - $this->createMock(Role::class), - $this->createMock(User::class), - $this->createMock(RoleLimitation::class), - ]; - - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $service->assignRoleToUser(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAssignRoleToUserEvent::class, 0], - [AssignRoleToUserEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testAssignRoleToUserStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAssignRoleToUserEvent::class, - AssignRoleToUserEvent::class - ); - - $parameters = [ - $this->createMock(Role::class), - $this->createMock(User::class), - $this->createMock(RoleLimitation::class), - ]; - - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeAssignRoleToUserEvent::class, static function (BeforeAssignRoleToUserEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $service->assignRoleToUser(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAssignRoleToUserEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [AssignRoleToUserEvent::class, 0], - [BeforeAssignRoleToUserEvent::class, 0], - ]); - } - - public function testUpdateRoleDraftEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateRoleDraftEvent::class, - UpdateRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - $this->createMock(RoleUpdateStruct::class), - ]; - - $updatedRoleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('updateRoleDraft')->willReturn($updatedRoleDraft); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($updatedRoleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateRoleDraftEvent::class, 0], - [UpdateRoleDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdateRoleDraftResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateRoleDraftEvent::class, - UpdateRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - $this->createMock(RoleUpdateStruct::class), - ]; - - $updatedRoleDraft = $this->createMock(RoleDraft::class); - $eventUpdatedRoleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('updateRoleDraft')->willReturn($updatedRoleDraft); - - $traceableEventDispatcher->addListener(BeforeUpdateRoleDraftEvent::class, static function (BeforeUpdateRoleDraftEvent $event) use ($eventUpdatedRoleDraft) { - $event->setUpdatedRoleDraft($eventUpdatedRoleDraft); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUpdatedRoleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateRoleDraftEvent::class, 10], - [BeforeUpdateRoleDraftEvent::class, 0], - [UpdateRoleDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateRoleDraftStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateRoleDraftEvent::class, - UpdateRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - $this->createMock(RoleUpdateStruct::class), - ]; - - $updatedRoleDraft = $this->createMock(RoleDraft::class); - $eventUpdatedRoleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('updateRoleDraft')->willReturn($updatedRoleDraft); - - $traceableEventDispatcher->addListener(BeforeUpdateRoleDraftEvent::class, static function (BeforeUpdateRoleDraftEvent $event) use ($eventUpdatedRoleDraft) { - $event->setUpdatedRoleDraft($eventUpdatedRoleDraft); - $event->stopPropagation(); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUpdatedRoleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateRoleDraftEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateRoleDraftEvent::class, 0], - [UpdateRoleDraftEvent::class, 0], - ]); - } - - public function testAssignRoleToUserGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAssignRoleToUserGroupEvent::class, - AssignRoleToUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(Role::class), - $this->createMock(UserGroup::class), - $this->createMock(RoleLimitation::class), - ]; - - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $service->assignRoleToUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAssignRoleToUserGroupEvent::class, 0], - [AssignRoleToUserGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testAssignRoleToUserGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAssignRoleToUserGroupEvent::class, - AssignRoleToUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(Role::class), - $this->createMock(UserGroup::class), - $this->createMock(RoleLimitation::class), - ]; - - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeAssignRoleToUserGroupEvent::class, static function (BeforeAssignRoleToUserGroupEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $service->assignRoleToUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAssignRoleToUserGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [AssignRoleToUserGroupEvent::class, 0], - [BeforeAssignRoleToUserGroupEvent::class, 0], - ]); - } - - public function testUpdatePolicyByRoleDraftEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdatePolicyByRoleDraftEvent::class, - UpdatePolicyByRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - $this->createMock(PolicyDraft::class), - $this->createMock(PolicyUpdateStruct::class), - ]; - - $updatedPolicyDraft = $this->createMock(PolicyDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('updatePolicyByRoleDraft')->willReturn($updatedPolicyDraft); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updatePolicyByRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($updatedPolicyDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdatePolicyByRoleDraftEvent::class, 0], - [UpdatePolicyByRoleDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdatePolicyByRoleDraftResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdatePolicyByRoleDraftEvent::class, - UpdatePolicyByRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - $this->createMock(PolicyDraft::class), - $this->createMock(PolicyUpdateStruct::class), - ]; - - $updatedPolicyDraft = $this->createMock(PolicyDraft::class); - $eventUpdatedPolicyDraft = $this->createMock(PolicyDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('updatePolicyByRoleDraft')->willReturn($updatedPolicyDraft); - - $traceableEventDispatcher->addListener(BeforeUpdatePolicyByRoleDraftEvent::class, static function (BeforeUpdatePolicyByRoleDraftEvent $event) use ($eventUpdatedPolicyDraft) { - $event->setUpdatedPolicyDraft($eventUpdatedPolicyDraft); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updatePolicyByRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUpdatedPolicyDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdatePolicyByRoleDraftEvent::class, 10], - [BeforeUpdatePolicyByRoleDraftEvent::class, 0], - [UpdatePolicyByRoleDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdatePolicyByRoleDraftStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdatePolicyByRoleDraftEvent::class, - UpdatePolicyByRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - $this->createMock(PolicyDraft::class), - $this->createMock(PolicyUpdateStruct::class), - ]; - - $updatedPolicyDraft = $this->createMock(PolicyDraft::class); - $eventUpdatedPolicyDraft = $this->createMock(PolicyDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('updatePolicyByRoleDraft')->willReturn($updatedPolicyDraft); - - $traceableEventDispatcher->addListener(BeforeUpdatePolicyByRoleDraftEvent::class, static function (BeforeUpdatePolicyByRoleDraftEvent $event) use ($eventUpdatedPolicyDraft) { - $event->setUpdatedPolicyDraft($eventUpdatedPolicyDraft); - $event->stopPropagation(); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updatePolicyByRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUpdatedPolicyDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdatePolicyByRoleDraftEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdatePolicyByRoleDraftEvent::class, 0], - [UpdatePolicyByRoleDraftEvent::class, 0], - ]); - } - - public function testCreateRoleEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateRoleEvent::class, - CreateRoleEvent::class - ); - - $parameters = [ - $this->createMock(RoleCreateStruct::class), - ]; - - $roleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('createRole')->willReturn($roleDraft); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createRole(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($roleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateRoleEvent::class, 0], - [CreateRoleEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateRoleResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateRoleEvent::class, - CreateRoleEvent::class - ); - - $parameters = [ - $this->createMock(RoleCreateStruct::class), - ]; - - $roleDraft = $this->createMock(RoleDraft::class); - $eventRoleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('createRole')->willReturn($roleDraft); - - $traceableEventDispatcher->addListener(BeforeCreateRoleEvent::class, static function (BeforeCreateRoleEvent $event) use ($eventRoleDraft) { - $event->setRoleDraft($eventRoleDraft); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createRole(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventRoleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateRoleEvent::class, 10], - [BeforeCreateRoleEvent::class, 0], - [CreateRoleEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateRoleStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateRoleEvent::class, - CreateRoleEvent::class - ); - - $parameters = [ - $this->createMock(RoleCreateStruct::class), - ]; - - $roleDraft = $this->createMock(RoleDraft::class); - $eventRoleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('createRole')->willReturn($roleDraft); - - $traceableEventDispatcher->addListener(BeforeCreateRoleEvent::class, static function (BeforeCreateRoleEvent $event) use ($eventRoleDraft) { - $event->setRoleDraft($eventRoleDraft); - $event->stopPropagation(); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createRole(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventRoleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateRoleEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateRoleEvent::class, 0], - [CreateRoleEvent::class, 0], - ]); - } - - public function testRemovePolicyByRoleDraftEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemovePolicyByRoleDraftEvent::class, - RemovePolicyByRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - $this->createMock(PolicyDraft::class), - ]; - - $updatedRoleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('removePolicyByRoleDraft')->willReturn($updatedRoleDraft); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->removePolicyByRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($updatedRoleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeRemovePolicyByRoleDraftEvent::class, 0], - [RemovePolicyByRoleDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnRemovePolicyByRoleDraftResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemovePolicyByRoleDraftEvent::class, - RemovePolicyByRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - $this->createMock(PolicyDraft::class), - ]; - - $updatedRoleDraft = $this->createMock(RoleDraft::class); - $eventUpdatedRoleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('removePolicyByRoleDraft')->willReturn($updatedRoleDraft); - - $traceableEventDispatcher->addListener(BeforeRemovePolicyByRoleDraftEvent::class, static function (BeforeRemovePolicyByRoleDraftEvent $event) use ($eventUpdatedRoleDraft) { - $event->setUpdatedRoleDraft($eventUpdatedRoleDraft); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->removePolicyByRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUpdatedRoleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeRemovePolicyByRoleDraftEvent::class, 10], - [BeforeRemovePolicyByRoleDraftEvent::class, 0], - [RemovePolicyByRoleDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testRemovePolicyByRoleDraftStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemovePolicyByRoleDraftEvent::class, - RemovePolicyByRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - $this->createMock(PolicyDraft::class), - ]; - - $updatedRoleDraft = $this->createMock(RoleDraft::class); - $eventUpdatedRoleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('removePolicyByRoleDraft')->willReturn($updatedRoleDraft); - - $traceableEventDispatcher->addListener(BeforeRemovePolicyByRoleDraftEvent::class, static function (BeforeRemovePolicyByRoleDraftEvent $event) use ($eventUpdatedRoleDraft) { - $event->setUpdatedRoleDraft($eventUpdatedRoleDraft); - $event->stopPropagation(); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->removePolicyByRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUpdatedRoleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeRemovePolicyByRoleDraftEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeRemovePolicyByRoleDraftEvent::class, 0], - [RemovePolicyByRoleDraftEvent::class, 0], - ]); - } - - public function testAddPolicyByRoleDraftEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAddPolicyByRoleDraftEvent::class, - AddPolicyByRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - $this->createMock(PolicyCreateStruct::class), - ]; - - $updatedRoleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('addPolicyByRoleDraft')->willReturn($updatedRoleDraft); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->addPolicyByRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($updatedRoleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeAddPolicyByRoleDraftEvent::class, 0], - [AddPolicyByRoleDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnAddPolicyByRoleDraftResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAddPolicyByRoleDraftEvent::class, - AddPolicyByRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - $this->createMock(PolicyCreateStruct::class), - ]; - - $updatedRoleDraft = $this->createMock(RoleDraft::class); - $eventUpdatedRoleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('addPolicyByRoleDraft')->willReturn($updatedRoleDraft); - - $traceableEventDispatcher->addListener(BeforeAddPolicyByRoleDraftEvent::class, static function (BeforeAddPolicyByRoleDraftEvent $event) use ($eventUpdatedRoleDraft) { - $event->setUpdatedRoleDraft($eventUpdatedRoleDraft); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->addPolicyByRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUpdatedRoleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeAddPolicyByRoleDraftEvent::class, 10], - [BeforeAddPolicyByRoleDraftEvent::class, 0], - [AddPolicyByRoleDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testAddPolicyByRoleDraftStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAddPolicyByRoleDraftEvent::class, - AddPolicyByRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - $this->createMock(PolicyCreateStruct::class), - ]; - - $updatedRoleDraft = $this->createMock(RoleDraft::class); - $eventUpdatedRoleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('addPolicyByRoleDraft')->willReturn($updatedRoleDraft); - - $traceableEventDispatcher->addListener(BeforeAddPolicyByRoleDraftEvent::class, static function (BeforeAddPolicyByRoleDraftEvent $event) use ($eventUpdatedRoleDraft) { - $event->setUpdatedRoleDraft($eventUpdatedRoleDraft); - $event->stopPropagation(); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->addPolicyByRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUpdatedRoleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeAddPolicyByRoleDraftEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [AddPolicyByRoleDraftEvent::class, 0], - [BeforeAddPolicyByRoleDraftEvent::class, 0], - ]); - } - - public function testDeleteRoleEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteRoleEvent::class, - DeleteRoleEvent::class - ); - - $parameters = [ - $this->createMock(Role::class), - ]; - - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $service->deleteRole(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteRoleEvent::class, 0], - [DeleteRoleEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteRoleStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteRoleEvent::class, - DeleteRoleEvent::class - ); - - $parameters = [ - $this->createMock(Role::class), - ]; - - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteRoleEvent::class, static function (BeforeDeleteRoleEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $service->deleteRole(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteRoleEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteRoleEvent::class, 0], - [DeleteRoleEvent::class, 0], - ]); - } - - public function testDeleteRoleDraftEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteRoleDraftEvent::class, - DeleteRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - ]; - - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $service->deleteRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteRoleDraftEvent::class, 0], - [DeleteRoleDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteRoleDraftStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteRoleDraftEvent::class, - DeleteRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(RoleDraft::class), - ]; - - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteRoleDraftEvent::class, static function (BeforeDeleteRoleDraftEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $service->deleteRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteRoleDraftEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteRoleDraftEvent::class, 0], - [DeleteRoleDraftEvent::class, 0], - ]); - } - - public function testRemoveRoleAssignmentEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemoveRoleAssignmentEvent::class, - RemoveRoleAssignmentEvent::class - ); - - $parameters = [ - $this->createMock(RoleAssignment::class), - ]; - - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $service->removeRoleAssignment(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeRemoveRoleAssignmentEvent::class, 0], - [RemoveRoleAssignmentEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testRemoveRoleAssignmentStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemoveRoleAssignmentEvent::class, - RemoveRoleAssignmentEvent::class - ); - - $parameters = [ - $this->createMock(RoleAssignment::class), - ]; - - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeRemoveRoleAssignmentEvent::class, static function (BeforeRemoveRoleAssignmentEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $service->removeRoleAssignment(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeRemoveRoleAssignmentEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeRemoveRoleAssignmentEvent::class, 0], - [RemoveRoleAssignmentEvent::class, 0], - ]); - } - - public function testCreateRoleDraftEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateRoleDraftEvent::class, - CreateRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(Role::class), - ]; - - $roleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('createRoleDraft')->willReturn($roleDraft); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($roleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateRoleDraftEvent::class, 0], - [CreateRoleDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateRoleDraftResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateRoleDraftEvent::class, - CreateRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(Role::class), - ]; - - $roleDraft = $this->createMock(RoleDraft::class); - $eventRoleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('createRoleDraft')->willReturn($roleDraft); - - $traceableEventDispatcher->addListener(BeforeCreateRoleDraftEvent::class, static function (BeforeCreateRoleDraftEvent $event) use ($eventRoleDraft) { - $event->setRoleDraft($eventRoleDraft); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventRoleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateRoleDraftEvent::class, 10], - [BeforeCreateRoleDraftEvent::class, 0], - [CreateRoleDraftEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateRoleDraftStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateRoleDraftEvent::class, - CreateRoleDraftEvent::class - ); - - $parameters = [ - $this->createMock(Role::class), - ]; - - $roleDraft = $this->createMock(RoleDraft::class); - $eventRoleDraft = $this->createMock(RoleDraft::class); - $innerServiceMock = $this->createMock(RoleServiceInterface::class); - $innerServiceMock->method('createRoleDraft')->willReturn($roleDraft); - - $traceableEventDispatcher->addListener(BeforeCreateRoleDraftEvent::class, static function (BeforeCreateRoleDraftEvent $event) use ($eventRoleDraft) { - $event->setRoleDraft($eventRoleDraft); - $event->stopPropagation(); - }, 10); - - $service = new RoleService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createRoleDraft(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventRoleDraft, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateRoleDraftEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateRoleDraftEvent::class, 0], - [CreateRoleDraftEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/SectionServiceTest.php b/eZ/Publish/Core/Event/Tests/SectionServiceTest.php deleted file mode 100644 index 9f3b3c7fd2..0000000000 --- a/eZ/Publish/Core/Event/Tests/SectionServiceTest.php +++ /dev/null @@ -1,404 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\Events\Section\AssignSectionEvent; -use eZ\Publish\API\Repository\Events\Section\AssignSectionToSubtreeEvent; -use eZ\Publish\API\Repository\Events\Section\BeforeAssignSectionEvent; -use eZ\Publish\API\Repository\Events\Section\BeforeAssignSectionToSubtreeEvent; -use eZ\Publish\API\Repository\Events\Section\BeforeCreateSectionEvent; -use eZ\Publish\API\Repository\Events\Section\BeforeDeleteSectionEvent; -use eZ\Publish\API\Repository\Events\Section\BeforeUpdateSectionEvent; -use eZ\Publish\API\Repository\Events\Section\CreateSectionEvent; -use eZ\Publish\API\Repository\Events\Section\DeleteSectionEvent; -use eZ\Publish\API\Repository\Events\Section\UpdateSectionEvent; -use eZ\Publish\API\Repository\SectionService as SectionServiceInterface; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Section; -use eZ\Publish\API\Repository\Values\Content\SectionCreateStruct; -use eZ\Publish\API\Repository\Values\Content\SectionUpdateStruct; -use eZ\Publish\Core\Event\SectionService; - -class SectionServiceTest extends AbstractServiceTest -{ - public function testAssignSectionEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAssignSectionEvent::class, - AssignSectionEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(Section::class), - ]; - - $innerServiceMock = $this->createMock(SectionServiceInterface::class); - - $service = new SectionService($innerServiceMock, $traceableEventDispatcher); - $service->assignSection(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAssignSectionEvent::class, 0], - [AssignSectionEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testAssignSectionStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAssignSectionEvent::class, - AssignSectionEvent::class - ); - - $parameters = [ - $this->createMock(ContentInfo::class), - $this->createMock(Section::class), - ]; - - $innerServiceMock = $this->createMock(SectionServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeAssignSectionEvent::class, static function (BeforeAssignSectionEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new SectionService($innerServiceMock, $traceableEventDispatcher); - $service->assignSection(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAssignSectionEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [AssignSectionEvent::class, 0], - [BeforeAssignSectionEvent::class, 0], - ]); - } - - public function testUpdateSectionEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateSectionEvent::class, - UpdateSectionEvent::class - ); - - $parameters = [ - $this->createMock(Section::class), - $this->createMock(SectionUpdateStruct::class), - ]; - - $updatedSection = $this->createMock(Section::class); - $innerServiceMock = $this->createMock(SectionServiceInterface::class); - $innerServiceMock->method('updateSection')->willReturn($updatedSection); - - $service = new SectionService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateSection(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($updatedSection, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateSectionEvent::class, 0], - [UpdateSectionEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdateSectionResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateSectionEvent::class, - UpdateSectionEvent::class - ); - - $parameters = [ - $this->createMock(Section::class), - $this->createMock(SectionUpdateStruct::class), - ]; - - $updatedSection = $this->createMock(Section::class); - $eventUpdatedSection = $this->createMock(Section::class); - $innerServiceMock = $this->createMock(SectionServiceInterface::class); - $innerServiceMock->method('updateSection')->willReturn($updatedSection); - - $traceableEventDispatcher->addListener(BeforeUpdateSectionEvent::class, static function (BeforeUpdateSectionEvent $event) use ($eventUpdatedSection) { - $event->setUpdatedSection($eventUpdatedSection); - }, 10); - - $service = new SectionService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateSection(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUpdatedSection, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateSectionEvent::class, 10], - [BeforeUpdateSectionEvent::class, 0], - [UpdateSectionEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateSectionStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateSectionEvent::class, - UpdateSectionEvent::class - ); - - $parameters = [ - $this->createMock(Section::class), - $this->createMock(SectionUpdateStruct::class), - ]; - - $updatedSection = $this->createMock(Section::class); - $eventUpdatedSection = $this->createMock(Section::class); - $innerServiceMock = $this->createMock(SectionServiceInterface::class); - $innerServiceMock->method('updateSection')->willReturn($updatedSection); - - $traceableEventDispatcher->addListener(BeforeUpdateSectionEvent::class, static function (BeforeUpdateSectionEvent $event) use ($eventUpdatedSection) { - $event->setUpdatedSection($eventUpdatedSection); - $event->stopPropagation(); - }, 10); - - $service = new SectionService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateSection(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUpdatedSection, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateSectionEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateSectionEvent::class, 0], - [UpdateSectionEvent::class, 0], - ]); - } - - public function testAssignSectionToSubtreeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAssignSectionToSubtreeEvent::class, - AssignSectionToSubtreeEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - $this->createMock(Section::class), - ]; - - $innerServiceMock = $this->createMock(SectionServiceInterface::class); - - $service = new SectionService($innerServiceMock, $traceableEventDispatcher); - $service->assignSectionToSubtree(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAssignSectionToSubtreeEvent::class, 0], - [AssignSectionToSubtreeEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testAssignSectionToSubtreeStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAssignSectionToSubtreeEvent::class, - AssignSectionToSubtreeEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - $this->createMock(Section::class), - ]; - - $innerServiceMock = $this->createMock(SectionServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeAssignSectionToSubtreeEvent::class, static function (BeforeAssignSectionToSubtreeEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new SectionService($innerServiceMock, $traceableEventDispatcher); - $service->assignSectionToSubtree(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAssignSectionToSubtreeEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [AssignSectionToSubtreeEvent::class, 0], - [BeforeAssignSectionToSubtreeEvent::class, 0], - ]); - } - - public function testDeleteSectionEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteSectionEvent::class, - DeleteSectionEvent::class - ); - - $parameters = [ - $this->createMock(Section::class), - ]; - - $innerServiceMock = $this->createMock(SectionServiceInterface::class); - - $service = new SectionService($innerServiceMock, $traceableEventDispatcher); - $service->deleteSection(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteSectionEvent::class, 0], - [DeleteSectionEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteSectionStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteSectionEvent::class, - DeleteSectionEvent::class - ); - - $parameters = [ - $this->createMock(Section::class), - ]; - - $innerServiceMock = $this->createMock(SectionServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeDeleteSectionEvent::class, static function (BeforeDeleteSectionEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new SectionService($innerServiceMock, $traceableEventDispatcher); - $service->deleteSection(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeDeleteSectionEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteSectionEvent::class, 0], - [DeleteSectionEvent::class, 0], - ]); - } - - public function testCreateSectionEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateSectionEvent::class, - CreateSectionEvent::class - ); - - $parameters = [ - $this->createMock(SectionCreateStruct::class), - ]; - - $section = $this->createMock(Section::class); - $innerServiceMock = $this->createMock(SectionServiceInterface::class); - $innerServiceMock->method('createSection')->willReturn($section); - - $service = new SectionService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createSection(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($section, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateSectionEvent::class, 0], - [CreateSectionEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateSectionResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateSectionEvent::class, - CreateSectionEvent::class - ); - - $parameters = [ - $this->createMock(SectionCreateStruct::class), - ]; - - $section = $this->createMock(Section::class); - $eventSection = $this->createMock(Section::class); - $innerServiceMock = $this->createMock(SectionServiceInterface::class); - $innerServiceMock->method('createSection')->willReturn($section); - - $traceableEventDispatcher->addListener(BeforeCreateSectionEvent::class, static function (BeforeCreateSectionEvent $event) use ($eventSection) { - $event->setSection($eventSection); - }, 10); - - $service = new SectionService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createSection(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventSection, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateSectionEvent::class, 10], - [BeforeCreateSectionEvent::class, 0], - [CreateSectionEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateSectionStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateSectionEvent::class, - CreateSectionEvent::class - ); - - $parameters = [ - $this->createMock(SectionCreateStruct::class), - ]; - - $section = $this->createMock(Section::class); - $eventSection = $this->createMock(Section::class); - $innerServiceMock = $this->createMock(SectionServiceInterface::class); - $innerServiceMock->method('createSection')->willReturn($section); - - $traceableEventDispatcher->addListener(BeforeCreateSectionEvent::class, static function (BeforeCreateSectionEvent $event) use ($eventSection) { - $event->setSection($eventSection); - $event->stopPropagation(); - }, 10); - - $service = new SectionService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createSection(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventSection, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateSectionEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateSectionEvent::class, 0], - [CreateSectionEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/SettingServiceTest.php b/eZ/Publish/Core/Event/Tests/SettingServiceTest.php deleted file mode 100644 index a29c2925dc..0000000000 --- a/eZ/Publish/Core/Event/Tests/SettingServiceTest.php +++ /dev/null @@ -1,332 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\Events\Setting\BeforeCreateSettingEvent; -use eZ\Publish\API\Repository\Events\Setting\BeforeDeleteSettingEvent; -use eZ\Publish\API\Repository\Events\Setting\BeforeUpdateSettingEvent; -use eZ\Publish\API\Repository\Events\Setting\CreateSettingEvent; -use eZ\Publish\API\Repository\Events\Setting\DeleteSettingEvent; -use eZ\Publish\API\Repository\Events\Setting\UpdateSettingEvent; -use eZ\Publish\API\Repository\SettingService as SettingServiceInterface; -use eZ\Publish\API\Repository\Values\Setting\Setting; -use eZ\Publish\API\Repository\Values\Setting\SettingCreateStruct; -use eZ\Publish\API\Repository\Values\Setting\SettingUpdateStruct; -use eZ\Publish\Core\Event\SettingService; -use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; - -class SettingServiceTest extends AbstractServiceTest -{ - public function testUpdateSettingEvents(): void - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateSettingEvent::class, - UpdateSettingEvent::class - ); - $updatedSetting = $this->createMock(Setting::class); - - $result = $this->updateSetting($updatedSetting, $traceableEventDispatcher); - - $calledListeners = $this->getListenersStack( - $traceableEventDispatcher->getCalledListeners() - ); - - self::assertSame($updatedSetting, $result); - self::assertSame( - $calledListeners, - [ - [BeforeUpdateSettingEvent::class, 0], - [UpdateSettingEvent::class, 0], - ] - ); - self::assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdateSettingResultInBeforeEvents(): void - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateSettingEvent::class, - UpdateSettingEvent::class - ); - $eventUpdatedSetting = $this->createMock(Setting::class); - - $traceableEventDispatcher->addListener( - BeforeUpdateSettingEvent::class, - static function (BeforeUpdateSettingEvent $event) use ($eventUpdatedSetting) { - $event->setUpdatedSetting($eventUpdatedSetting); - }, - 10 - ); - $updatedSetting = $this->createMock(Setting::class); - - $result = $this->updateSetting($updatedSetting, $traceableEventDispatcher); - - $calledListeners = $this->getListenersStack( - $traceableEventDispatcher->getCalledListeners() - ); - - self::assertSame($eventUpdatedSetting, $result); - self::assertSame( - $calledListeners, - [ - [BeforeUpdateSettingEvent::class, 10], - [BeforeUpdateSettingEvent::class, 0], - [UpdateSettingEvent::class, 0], - ] - ); - self::assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateSettingStopPropagationInBeforeEvents(): void - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateSettingEvent::class, - UpdateSettingEvent::class - ); - $eventUpdatedSetting = $this->createMock(Setting::class); - $traceableEventDispatcher->addListener( - BeforeUpdateSettingEvent::class, - static function (BeforeUpdateSettingEvent $event) use ($eventUpdatedSetting) { - $event->setUpdatedSetting($eventUpdatedSetting); - $event->stopPropagation(); - }, - 10 - ); - $updatedSetting = $this->createMock(Setting::class); - - $result = $this->updateSetting($updatedSetting, $traceableEventDispatcher); - - $calledListeners = $this->getListenersStack( - $traceableEventDispatcher->getCalledListeners() - ); - $notCalledListeners = $this->getListenersStack( - $traceableEventDispatcher->getNotCalledListeners() - ); - - self::assertSame($eventUpdatedSetting, $result); - self::assertSame( - $calledListeners, - [ - [BeforeUpdateSettingEvent::class, 10], - ] - ); - self::assertSame( - $notCalledListeners, - [ - [BeforeUpdateSettingEvent::class, 0], - [UpdateSettingEvent::class, 0], - ] - ); - } - - public function testDeleteSettingEvents(): void - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteSettingEvent::class, - DeleteSettingEvent::class - ); - - $parameters = [ - $this->createMock(Setting::class), - ]; - - $innerServiceMock = $this->createMock(SettingServiceInterface::class); - - $service = new SettingService($innerServiceMock, $traceableEventDispatcher); - $service->deleteSetting(...$parameters); - - $calledListeners = $this->getListenersStack( - $traceableEventDispatcher->getCalledListeners() - ); - - self::assertSame( - $calledListeners, - [ - [BeforeDeleteSettingEvent::class, 0], - [DeleteSettingEvent::class, 0], - ] - ); - self::assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteSettingStopPropagationInBeforeEvents(): void - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteSettingEvent::class, - DeleteSettingEvent::class - ); - - $parameters = [ - $this->createMock(Setting::class), - ]; - - $innerServiceMock = $this->createMock(SettingServiceInterface::class); - - $traceableEventDispatcher->addListener( - BeforeDeleteSettingEvent::class, - static function (BeforeDeleteSettingEvent $event) { - $event->stopPropagation(); - }, - 10 - ); - - $service = new SettingService($innerServiceMock, $traceableEventDispatcher); - $service->deleteSetting(...$parameters); - - $calledListeners = $this->getListenersStack( - $traceableEventDispatcher->getCalledListeners() - ); - $notCalledListeners = $this->getListenersStack( - $traceableEventDispatcher->getNotCalledListeners() - ); - - self::assertSame( - $calledListeners, - [ - [BeforeDeleteSettingEvent::class, 10], - ] - ); - self::assertSame( - $notCalledListeners, - [ - [BeforeDeleteSettingEvent::class, 0], - [DeleteSettingEvent::class, 0], - ] - ); - } - - public function testCreateSettingEvents(): void - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateSettingEvent::class, - CreateSettingEvent::class - ); - $setting = $this->createMock(Setting::class); - - $result = $this->createSetting($setting, $traceableEventDispatcher); - - $calledListeners = $this->getListenersStack( - $traceableEventDispatcher->getCalledListeners() - ); - - self::assertSame($setting, $result); - self::assertSame( - $calledListeners, - [ - [BeforeCreateSettingEvent::class, 0], - [CreateSettingEvent::class, 0], - ] - ); - self::assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateSettingResultInBeforeEvents(): void - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateSettingEvent::class, - CreateSettingEvent::class - ); - $eventSetting = $this->createMock(Setting::class); - $setting = $this->createMock(Setting::class); - $traceableEventDispatcher->addListener( - BeforeCreateSettingEvent::class, - static function (BeforeCreateSettingEvent $event) use ($eventSetting) { - $event->setSetting($eventSetting); - }, - 10 - ); - - $result = $this->createSetting($setting, $traceableEventDispatcher); - - $calledListeners = $this->getListenersStack( - $traceableEventDispatcher->getCalledListeners() - ); - - self::assertSame($eventSetting, $result); - self::assertSame( - $calledListeners, - [ - [BeforeCreateSettingEvent::class, 10], - [BeforeCreateSettingEvent::class, 0], - [CreateSettingEvent::class, 0], - ] - ); - self::assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateSettingStopPropagationInBeforeEvents(): void - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateSettingEvent::class, - CreateSettingEvent::class - ); - $eventSetting = $this->createMock(Setting::class); - $setting = $this->createMock(Setting::class); - $traceableEventDispatcher->addListener( - BeforeCreateSettingEvent::class, - static function (BeforeCreateSettingEvent $event) use ($eventSetting) { - $event->setSetting($eventSetting); - $event->stopPropagation(); - }, - 10 - ); - - $result = $this->createSetting($setting, $traceableEventDispatcher); - - $calledListeners = $this->getListenersStack( - $traceableEventDispatcher->getCalledListeners() - ); - $notCalledListeners = $this->getListenersStack( - $traceableEventDispatcher->getNotCalledListeners() - ); - - self::assertSame($eventSetting, $result); - self::assertSame( - $calledListeners, - [ - [BeforeCreateSettingEvent::class, 10], - ] - ); - self::assertSame( - $notCalledListeners, - [ - [BeforeCreateSettingEvent::class, 0], - [CreateSettingEvent::class, 0], - ] - ); - } - - private function createSetting( - Setting $setting, - TraceableEventDispatcher $traceableEventDispatcher - ): Setting { - $parameters = [ - $this->createMock(SettingCreateStruct::class), - ]; - $innerServiceMock = $this->createMock(SettingServiceInterface::class); - $innerServiceMock->method('createSetting')->willReturn($setting); - $service = new SettingService($innerServiceMock, $traceableEventDispatcher); - - return $service->createSetting(...$parameters); - } - - private function updateSetting( - Setting $updatedSetting, - TraceableEventDispatcher $traceableEventDispatcher - ): Setting { - $parameters = [ - $this->createMock(Setting::class), - $this->createMock(SettingUpdateStruct::class), - ]; - $innerServiceMock = $this->createMock(SettingServiceInterface::class); - $innerServiceMock->method('updateSetting')->willReturn($updatedSetting); - - $service = new SettingService($innerServiceMock, $traceableEventDispatcher); - - return $service->updateSetting(...$parameters); - } -} diff --git a/eZ/Publish/Core/Event/Tests/TrashServiceTest.php b/eZ/Publish/Core/Event/Tests/TrashServiceTest.php deleted file mode 100644 index c1e4204fb5..0000000000 --- a/eZ/Publish/Core/Event/Tests/TrashServiceTest.php +++ /dev/null @@ -1,421 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\Events\Trash\BeforeDeleteTrashItemEvent; -use eZ\Publish\API\Repository\Events\Trash\BeforeEmptyTrashEvent; -use eZ\Publish\API\Repository\Events\Trash\BeforeRecoverEvent; -use eZ\Publish\API\Repository\Events\Trash\BeforeTrashEvent; -use eZ\Publish\API\Repository\Events\Trash\DeleteTrashItemEvent; -use eZ\Publish\API\Repository\Events\Trash\EmptyTrashEvent; -use eZ\Publish\API\Repository\Events\Trash\RecoverEvent; -use eZ\Publish\API\Repository\Events\Trash\TrashEvent; -use eZ\Publish\API\Repository\TrashService as TrashServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResultList; -use eZ\Publish\API\Repository\Values\Content\TrashItem; -use eZ\Publish\Core\Event\TrashService; - -class TrashServiceTest extends AbstractServiceTest -{ - public function testEmptyTrashEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeEmptyTrashEvent::class, - EmptyTrashEvent::class - ); - - $parameters = [ - ]; - - $resultList = $this->createMock(TrashItemDeleteResultList::class); - $innerServiceMock = $this->createMock(TrashServiceInterface::class); - $innerServiceMock->method('emptyTrash')->willReturn($resultList); - - $service = new TrashService($innerServiceMock, $traceableEventDispatcher); - $result = $service->emptyTrash(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($resultList, $result); - $this->assertSame($calledListeners, [ - [BeforeEmptyTrashEvent::class, 0], - [EmptyTrashEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnEmptyTrashResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeEmptyTrashEvent::class, - EmptyTrashEvent::class - ); - - $parameters = [ - ]; - - $resultList = $this->createMock(TrashItemDeleteResultList::class); - $eventResultList = $this->createMock(TrashItemDeleteResultList::class); - $innerServiceMock = $this->createMock(TrashServiceInterface::class); - $innerServiceMock->method('emptyTrash')->willReturn($resultList); - - $traceableEventDispatcher->addListener(BeforeEmptyTrashEvent::class, static function (BeforeEmptyTrashEvent $event) use ($eventResultList) { - $event->setResultList($eventResultList); - }, 10); - - $service = new TrashService($innerServiceMock, $traceableEventDispatcher); - $result = $service->emptyTrash(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventResultList, $result); - $this->assertSame($calledListeners, [ - [BeforeEmptyTrashEvent::class, 10], - [BeforeEmptyTrashEvent::class, 0], - [EmptyTrashEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testEmptyTrashStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeEmptyTrashEvent::class, - EmptyTrashEvent::class - ); - - $parameters = [ - ]; - - $resultList = $this->createMock(TrashItemDeleteResultList::class); - $eventResultList = $this->createMock(TrashItemDeleteResultList::class); - $innerServiceMock = $this->createMock(TrashServiceInterface::class); - $innerServiceMock->method('emptyTrash')->willReturn($resultList); - - $traceableEventDispatcher->addListener(BeforeEmptyTrashEvent::class, static function (BeforeEmptyTrashEvent $event) use ($eventResultList) { - $event->setResultList($eventResultList); - $event->stopPropagation(); - }, 10); - - $service = new TrashService($innerServiceMock, $traceableEventDispatcher); - $result = $service->emptyTrash(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventResultList, $result); - $this->assertSame($calledListeners, [ - [BeforeEmptyTrashEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeEmptyTrashEvent::class, 0], - [EmptyTrashEvent::class, 0], - ]); - } - - public function testTrashEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeTrashEvent::class, - TrashEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $trashItem = $this->createMock(TrashItem::class); - $innerServiceMock = $this->createMock(TrashServiceInterface::class); - $innerServiceMock->method('trash')->willReturn($trashItem); - - $service = new TrashService($innerServiceMock, $traceableEventDispatcher); - $result = $service->trash(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($trashItem, $result); - $this->assertSame($calledListeners, [ - [BeforeTrashEvent::class, 0], - [TrashEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnTrashResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeTrashEvent::class, - TrashEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $trashItem = $this->createMock(TrashItem::class); - $eventTrashItem = $this->createMock(TrashItem::class); - $innerServiceMock = $this->createMock(TrashServiceInterface::class); - $innerServiceMock->method('trash')->willReturn($trashItem); - - $traceableEventDispatcher->addListener(BeforeTrashEvent::class, static function (BeforeTrashEvent $event) use ($eventTrashItem) { - $event->setResult($eventTrashItem); - }, 10); - - $service = new TrashService($innerServiceMock, $traceableEventDispatcher); - $result = $service->trash(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventTrashItem, $result); - $this->assertSame($calledListeners, [ - [BeforeTrashEvent::class, 10], - [BeforeTrashEvent::class, 0], - [TrashEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testTrashStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeTrashEvent::class, - TrashEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $trashItem = $this->createMock(TrashItem::class); - $eventTrashItem = $this->createMock(TrashItem::class); - $innerServiceMock = $this->createMock(TrashServiceInterface::class); - $innerServiceMock->method('trash')->willReturn($trashItem); - - $traceableEventDispatcher->addListener(BeforeTrashEvent::class, static function (BeforeTrashEvent $event) use ($eventTrashItem) { - $event->setResult($eventTrashItem); - $event->stopPropagation(); - }, 10); - - $service = new TrashService($innerServiceMock, $traceableEventDispatcher); - $result = $service->trash(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventTrashItem, $result); - $this->assertSame($calledListeners, [ - [BeforeTrashEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeTrashEvent::class, 0], - [TrashEvent::class, 0], - ]); - } - - public function testRecoverEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRecoverEvent::class, - RecoverEvent::class - ); - - $parameters = [ - $this->createMock(TrashItem::class), - $this->createMock(Location::class), - ]; - - $location = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(TrashServiceInterface::class); - $innerServiceMock->method('recover')->willReturn($location); - - $service = new TrashService($innerServiceMock, $traceableEventDispatcher); - $result = $service->recover(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($location, $result); - $this->assertSame($calledListeners, [ - [BeforeRecoverEvent::class, 0], - [RecoverEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnRecoverResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRecoverEvent::class, - RecoverEvent::class - ); - - $parameters = [ - $this->createMock(TrashItem::class), - $this->createMock(Location::class), - ]; - - $location = $this->createMock(Location::class); - $eventLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(TrashServiceInterface::class); - $innerServiceMock->method('recover')->willReturn($location); - - $traceableEventDispatcher->addListener(BeforeRecoverEvent::class, static function (BeforeRecoverEvent $event) use ($eventLocation) { - $event->setLocation($eventLocation); - }, 10); - - $service = new TrashService($innerServiceMock, $traceableEventDispatcher); - $result = $service->recover(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeRecoverEvent::class, 10], - [BeforeRecoverEvent::class, 0], - [RecoverEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testRecoverStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRecoverEvent::class, - RecoverEvent::class - ); - - $parameters = [ - $this->createMock(TrashItem::class), - $this->createMock(Location::class), - ]; - - $location = $this->createMock(Location::class); - $eventLocation = $this->createMock(Location::class); - $innerServiceMock = $this->createMock(TrashServiceInterface::class); - $innerServiceMock->method('recover')->willReturn($location); - - $traceableEventDispatcher->addListener(BeforeRecoverEvent::class, static function (BeforeRecoverEvent $event) use ($eventLocation) { - $event->setLocation($eventLocation); - $event->stopPropagation(); - }, 10); - - $service = new TrashService($innerServiceMock, $traceableEventDispatcher); - $result = $service->recover(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventLocation, $result); - $this->assertSame($calledListeners, [ - [BeforeRecoverEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeRecoverEvent::class, 0], - [RecoverEvent::class, 0], - ]); - } - - public function testDeleteTrashItemEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteTrashItemEvent::class, - DeleteTrashItemEvent::class - ); - - $parameters = [ - $this->createMock(TrashItem::class), - ]; - - $result = $this->createMock(TrashItemDeleteResult::class); - $innerServiceMock = $this->createMock(TrashServiceInterface::class); - $innerServiceMock->method('deleteTrashItem')->willReturn($result); - - $service = new TrashService($innerServiceMock, $traceableEventDispatcher); - $result = $service->deleteTrashItem(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($result, $result); - $this->assertSame($calledListeners, [ - [BeforeDeleteTrashItemEvent::class, 0], - [DeleteTrashItemEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnDeleteTrashItemResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteTrashItemEvent::class, - DeleteTrashItemEvent::class - ); - - $parameters = [ - $this->createMock(TrashItem::class), - ]; - - $result = $this->createMock(TrashItemDeleteResult::class); - $eventResult = $this->createMock(TrashItemDeleteResult::class); - $innerServiceMock = $this->createMock(TrashServiceInterface::class); - $innerServiceMock->method('deleteTrashItem')->willReturn($result); - - $traceableEventDispatcher->addListener(BeforeDeleteTrashItemEvent::class, static function (BeforeDeleteTrashItemEvent $event) use ($eventResult) { - $event->setResult($eventResult); - }, 10); - - $service = new TrashService($innerServiceMock, $traceableEventDispatcher); - $result = $service->deleteTrashItem(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventResult, $result); - $this->assertSame($calledListeners, [ - [BeforeDeleteTrashItemEvent::class, 10], - [BeforeDeleteTrashItemEvent::class, 0], - [DeleteTrashItemEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteTrashItemStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteTrashItemEvent::class, - DeleteTrashItemEvent::class - ); - - $parameters = [ - $this->createMock(TrashItem::class), - ]; - - $result = $this->createMock(TrashItemDeleteResult::class); - $eventResult = $this->createMock(TrashItemDeleteResult::class); - $innerServiceMock = $this->createMock(TrashServiceInterface::class); - $innerServiceMock->method('deleteTrashItem')->willReturn($result); - - $traceableEventDispatcher->addListener(BeforeDeleteTrashItemEvent::class, static function (BeforeDeleteTrashItemEvent $event) use ($eventResult) { - $event->setResult($eventResult); - $event->stopPropagation(); - }, 10); - - $service = new TrashService($innerServiceMock, $traceableEventDispatcher); - $result = $service->deleteTrashItem(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventResult, $result); - $this->assertSame($calledListeners, [ - [BeforeDeleteTrashItemEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteTrashItemEvent::class, 0], - [DeleteTrashItemEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/URLAliasServiceTest.php b/eZ/Publish/Core/Event/Tests/URLAliasServiceTest.php deleted file mode 100644 index 1fb98a9c27..0000000000 --- a/eZ/Publish/Core/Event/Tests/URLAliasServiceTest.php +++ /dev/null @@ -1,359 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\Events\URLAlias\BeforeCreateGlobalUrlAliasEvent; -use eZ\Publish\API\Repository\Events\URLAlias\BeforeCreateUrlAliasEvent; -use eZ\Publish\API\Repository\Events\URLAlias\BeforeRefreshSystemUrlAliasesForLocationEvent; -use eZ\Publish\API\Repository\Events\URLAlias\BeforeRemoveAliasesEvent; -use eZ\Publish\API\Repository\Events\URLAlias\CreateGlobalUrlAliasEvent; -use eZ\Publish\API\Repository\Events\URLAlias\CreateUrlAliasEvent; -use eZ\Publish\API\Repository\Events\URLAlias\RefreshSystemUrlAliasesForLocationEvent; -use eZ\Publish\API\Repository\Events\URLAlias\RemoveAliasesEvent; -use eZ\Publish\API\Repository\URLAliasService as URLAliasServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use eZ\Publish\Core\Event\URLAliasService; - -class URLAliasServiceTest extends AbstractServiceTest -{ - public function testCreateGlobalUrlAliasEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateGlobalUrlAliasEvent::class, - CreateGlobalUrlAliasEvent::class - ); - - $parameters = [ - 'random_value_5cff79c3183471.48198669', - 'random_value_5cff79c3183491.90712521', - 'random_value_5cff79c31834a2.27245619', - 'random_value_5cff79c31834b7.17763784', - 'random_value_5cff79c31834c3.69513526', - ]; - - $urlAlias = $this->createMock(URLAlias::class); - $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); - $innerServiceMock->method('createGlobalUrlAlias')->willReturn($urlAlias); - - $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createGlobalUrlAlias(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($urlAlias, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateGlobalUrlAliasEvent::class, 0], - [CreateGlobalUrlAliasEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateGlobalUrlAliasResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateGlobalUrlAliasEvent::class, - CreateGlobalUrlAliasEvent::class - ); - - $parameters = [ - 'random_value_5cff79c3183999.45723962', - 'random_value_5cff79c31839a0.16919746', - 'random_value_5cff79c31839b6.04657069', - 'random_value_5cff79c31839c8.99027893', - 'random_value_5cff79c31839d9.22502123', - ]; - - $urlAlias = $this->createMock(URLAlias::class); - $eventUrlAlias = $this->createMock(URLAlias::class); - $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); - $innerServiceMock->method('createGlobalUrlAlias')->willReturn($urlAlias); - - $traceableEventDispatcher->addListener(BeforeCreateGlobalUrlAliasEvent::class, static function (BeforeCreateGlobalUrlAliasEvent $event) use ($eventUrlAlias) { - $event->setUrlAlias($eventUrlAlias); - }, 10); - - $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createGlobalUrlAlias(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUrlAlias, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateGlobalUrlAliasEvent::class, 10], - [BeforeCreateGlobalUrlAliasEvent::class, 0], - [CreateGlobalUrlAliasEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateGlobalUrlAliasStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateGlobalUrlAliasEvent::class, - CreateGlobalUrlAliasEvent::class - ); - - $parameters = [ - 'random_value_5cff79c3183a40.78467503', - 'random_value_5cff79c3183a52.60688594', - 'random_value_5cff79c3183a62.37338343', - 'random_value_5cff79c3183a74.31062414', - 'random_value_5cff79c3183a85.16422549', - ]; - - $urlAlias = $this->createMock(URLAlias::class); - $eventUrlAlias = $this->createMock(URLAlias::class); - $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); - $innerServiceMock->method('createGlobalUrlAlias')->willReturn($urlAlias); - - $traceableEventDispatcher->addListener(BeforeCreateGlobalUrlAliasEvent::class, static function (BeforeCreateGlobalUrlAliasEvent $event) use ($eventUrlAlias) { - $event->setUrlAlias($eventUrlAlias); - $event->stopPropagation(); - }, 10); - - $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createGlobalUrlAlias(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUrlAlias, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateGlobalUrlAliasEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateGlobalUrlAliasEvent::class, 0], - [CreateGlobalUrlAliasEvent::class, 0], - ]); - } - - public function testRefreshSystemUrlAliasesForLocationEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRefreshSystemUrlAliasesForLocationEvent::class, - RefreshSystemUrlAliasesForLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); - - $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); - $service->refreshSystemUrlAliasesForLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeRefreshSystemUrlAliasesForLocationEvent::class, 0], - [RefreshSystemUrlAliasesForLocationEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testRefreshSystemUrlAliasesForLocationStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRefreshSystemUrlAliasesForLocationEvent::class, - RefreshSystemUrlAliasesForLocationEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - ]; - - $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeRefreshSystemUrlAliasesForLocationEvent::class, static function (BeforeRefreshSystemUrlAliasesForLocationEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); - $service->refreshSystemUrlAliasesForLocation(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeRefreshSystemUrlAliasesForLocationEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeRefreshSystemUrlAliasesForLocationEvent::class, 0], - [RefreshSystemUrlAliasesForLocationEvent::class, 0], - ]); - } - - public function testCreateUrlAliasEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateUrlAliasEvent::class, - CreateUrlAliasEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - 'random_value_5cff79c3184f05.03459159', - 'random_value_5cff79c3184f14.18292216', - 'random_value_5cff79c3184f24.01158164', - 'random_value_5cff79c3184f32.03833593', - ]; - - $urlAlias = $this->createMock(URLAlias::class); - $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); - $innerServiceMock->method('createUrlAlias')->willReturn($urlAlias); - - $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createUrlAlias(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($urlAlias, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateUrlAliasEvent::class, 0], - [CreateUrlAliasEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateUrlAliasResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateUrlAliasEvent::class, - CreateUrlAliasEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - 'random_value_5cff79c3184fd7.07408772', - 'random_value_5cff79c3184fe2.98616568', - 'random_value_5cff79c3184ff0.62652505', - 'random_value_5cff79c3185003.87499400', - ]; - - $urlAlias = $this->createMock(URLAlias::class); - $eventUrlAlias = $this->createMock(URLAlias::class); - $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); - $innerServiceMock->method('createUrlAlias')->willReturn($urlAlias); - - $traceableEventDispatcher->addListener(BeforeCreateUrlAliasEvent::class, static function (BeforeCreateUrlAliasEvent $event) use ($eventUrlAlias) { - $event->setUrlAlias($eventUrlAlias); - }, 10); - - $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createUrlAlias(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUrlAlias, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateUrlAliasEvent::class, 10], - [BeforeCreateUrlAliasEvent::class, 0], - [CreateUrlAliasEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateUrlAliasStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateUrlAliasEvent::class, - CreateUrlAliasEvent::class - ); - - $parameters = [ - $this->createMock(Location::class), - 'random_value_5cff79c3185072.24449261', - 'random_value_5cff79c3185080.62311461', - 'random_value_5cff79c3185095.31877612', - 'random_value_5cff79c31850a4.20254218', - ]; - - $urlAlias = $this->createMock(URLAlias::class); - $eventUrlAlias = $this->createMock(URLAlias::class); - $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); - $innerServiceMock->method('createUrlAlias')->willReturn($urlAlias); - - $traceableEventDispatcher->addListener(BeforeCreateUrlAliasEvent::class, static function (BeforeCreateUrlAliasEvent $event) use ($eventUrlAlias) { - $event->setUrlAlias($eventUrlAlias); - $event->stopPropagation(); - }, 10); - - $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createUrlAlias(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUrlAlias, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateUrlAliasEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateUrlAliasEvent::class, 0], - [CreateUrlAliasEvent::class, 0], - ]); - } - - public function testRemoveAliasesEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemoveAliasesEvent::class, - RemoveAliasesEvent::class - ); - - $parameters = [ - [], - ]; - - $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); - - $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); - $service->removeAliases(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeRemoveAliasesEvent::class, 0], - [RemoveAliasesEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testRemoveAliasesStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemoveAliasesEvent::class, - RemoveAliasesEvent::class - ); - - $parameters = [ - [], - ]; - - $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeRemoveAliasesEvent::class, static function (BeforeRemoveAliasesEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); - $service->removeAliases(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeRemoveAliasesEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeRemoveAliasesEvent::class, 0], - [RemoveAliasesEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/URLServiceTest.php b/eZ/Publish/Core/Event/Tests/URLServiceTest.php deleted file mode 100644 index 040f8c61be..0000000000 --- a/eZ/Publish/Core/Event/Tests/URLServiceTest.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\Events\URL\BeforeUpdateUrlEvent; -use eZ\Publish\API\Repository\Events\URL\UpdateUrlEvent; -use eZ\Publish\API\Repository\URLService as URLServiceInterface; -use eZ\Publish\API\Repository\Values\URL\URL; -use eZ\Publish\API\Repository\Values\URL\URLUpdateStruct; -use eZ\Publish\Core\Event\URLService; - -class URLServiceTest extends AbstractServiceTest -{ - public function testUpdateUrlEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateUrlEvent::class, - UpdateUrlEvent::class - ); - - $parameters = [ - $this->createMock(URL::class), - $this->createMock(URLUpdateStruct::class), - ]; - - $updatedUrl = $this->createMock(URL::class); - $innerServiceMock = $this->createMock(URLServiceInterface::class); - $innerServiceMock->method('updateUrl')->willReturn($updatedUrl); - - $service = new URLService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateUrl(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($updatedUrl, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateUrlEvent::class, 0], - [UpdateUrlEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdateUrlResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateUrlEvent::class, - UpdateUrlEvent::class - ); - - $parameters = [ - $this->createMock(URL::class), - $this->createMock(URLUpdateStruct::class), - ]; - - $updatedUrl = $this->createMock(URL::class); - $eventUpdatedUrl = $this->createMock(URL::class); - $innerServiceMock = $this->createMock(URLServiceInterface::class); - $innerServiceMock->method('updateUrl')->willReturn($updatedUrl); - - $traceableEventDispatcher->addListener(BeforeUpdateUrlEvent::class, static function (BeforeUpdateUrlEvent $event) use ($eventUpdatedUrl) { - $event->setUpdatedUrl($eventUpdatedUrl); - }, 10); - - $service = new URLService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateUrl(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUpdatedUrl, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateUrlEvent::class, 10], - [BeforeUpdateUrlEvent::class, 0], - [UpdateUrlEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateUrlStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateUrlEvent::class, - UpdateUrlEvent::class - ); - - $parameters = [ - $this->createMock(URL::class), - $this->createMock(URLUpdateStruct::class), - ]; - - $updatedUrl = $this->createMock(URL::class); - $eventUpdatedUrl = $this->createMock(URL::class); - $innerServiceMock = $this->createMock(URLServiceInterface::class); - $innerServiceMock->method('updateUrl')->willReturn($updatedUrl); - - $traceableEventDispatcher->addListener(BeforeUpdateUrlEvent::class, static function (BeforeUpdateUrlEvent $event) use ($eventUpdatedUrl) { - $event->setUpdatedUrl($eventUpdatedUrl); - $event->stopPropagation(); - }, 10); - - $service = new URLService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateUrl(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUpdatedUrl, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateUrlEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateUrlEvent::class, 0], - [UpdateUrlEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/URLWildcardServiceTest.php b/eZ/Publish/Core/Event/Tests/URLWildcardServiceTest.php deleted file mode 100644 index 08c8c801c9..0000000000 --- a/eZ/Publish/Core/Event/Tests/URLWildcardServiceTest.php +++ /dev/null @@ -1,364 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\Events\URLWildcard\BeforeCreateEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\BeforeRemoveEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\BeforeTranslateEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\BeforeUpdateEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\CreateEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\RemoveEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\TranslateEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\UpdateEvent; -use eZ\Publish\API\Repository\URLWildcardService as URLWildcardServiceInterface; -use eZ\Publish\API\Repository\Values\Content\URLWildcard; -use eZ\Publish\API\Repository\Values\Content\URLWildcardTranslationResult; -use eZ\Publish\API\Repository\Values\Content\URLWildcardUpdateStruct; -use eZ\Publish\Core\Event\URLWildcardService; - -class URLWildcardServiceTest extends AbstractServiceTest -{ - /** - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testRemoveEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemoveEvent::class, - RemoveEvent::class - ); - - $parameters = [ - $this->createMock(URLWildcard::class), - ]; - - $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); - - $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); - $service->remove(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeRemoveEvent::class, 0], - [RemoveEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testRemoveStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeRemoveEvent::class, - RemoveEvent::class - ); - - $parameters = [ - $this->createMock(URLWildcard::class), - ]; - - $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeRemoveEvent::class, static function (BeforeRemoveEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); - $service->remove(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeRemoveEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeRemoveEvent::class, 0], - [RemoveEvent::class, 0], - ]); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testUpdateEvents(): void - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateEvent::class, - UpdateEvent::class - ); - - $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); - - $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); - $service->update( - $this->createMock(URLWildcard::class), - new URLWildcardUpdateStruct() - ); - - $calledListeners = $this->getListenersStack( - $traceableEventDispatcher->getCalledListeners() - ); - - $this->assertSame($calledListeners, [ - [BeforeUpdateEvent::class, 0], - [UpdateEvent::class, 0], - ]); - - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateStopPropagationInBeforeEvents(): void - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateEvent::class, - UpdateEvent::class - ); - - $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); - - $traceableEventDispatcher->addListener( - BeforeUpdateEvent::class, - static function (BeforeUpdateEvent $event) { - $event->stopPropagation(); - }, - 10 - ); - - $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); - $service->update( - $this->createMock(URLWildcard::class), - new URLWildcardUpdateStruct() - ); - - $calledListeners = $this->getListenersStack( - $traceableEventDispatcher->getCalledListeners() - ); - $notCalledListeners = $this->getListenersStack( - $traceableEventDispatcher->getNotCalledListeners() - ); - - $this->assertSame($calledListeners, [ - [BeforeUpdateEvent::class, 10], - ]); - - $this->assertSame($notCalledListeners, [ - [BeforeUpdateEvent::class, 0], - [UpdateEvent::class, 0], - ]); - } - - public function testCreateEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateEvent::class, - CreateEvent::class - ); - - $parameters = [ - 'random_value_5cff79c316c1f5.58580131', - 'random_value_5cff79c316c223.93334332', - 'random_value_5cff79c316c237.08397355', - ]; - - $urlWildcard = $this->createMock(URLWildcard::class); - $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); - $innerServiceMock->method('create')->willReturn($urlWildcard); - - $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); - $result = $service->create(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($urlWildcard, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateEvent::class, 0], - [CreateEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateEvent::class, - CreateEvent::class - ); - - $parameters = [ - 'random_value_5cff79c316c2d5.26653678', - 'random_value_5cff79c316c2e7.55400833', - 'random_value_5cff79c316c2f8.59874187', - ]; - - $urlWildcard = $this->createMock(URLWildcard::class); - $eventUrlWildcard = $this->createMock(URLWildcard::class); - $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); - $innerServiceMock->method('create')->willReturn($urlWildcard); - - $traceableEventDispatcher->addListener(BeforeCreateEvent::class, static function (BeforeCreateEvent $event) use ($eventUrlWildcard) { - $event->setUrlWildcard($eventUrlWildcard); - }, 10); - - $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); - $result = $service->create(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUrlWildcard, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateEvent::class, 10], - [BeforeCreateEvent::class, 0], - [CreateEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateEvent::class, - CreateEvent::class - ); - - $parameters = [ - 'random_value_5cff79c316c359.46056769', - 'random_value_5cff79c316c361.53134429', - 'random_value_5cff79c316c374.82657815', - ]; - - $urlWildcard = $this->createMock(URLWildcard::class); - $eventUrlWildcard = $this->createMock(URLWildcard::class); - $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); - $innerServiceMock->method('create')->willReturn($urlWildcard); - - $traceableEventDispatcher->addListener(BeforeCreateEvent::class, static function (BeforeCreateEvent $event) use ($eventUrlWildcard) { - $event->setUrlWildcard($eventUrlWildcard); - $event->stopPropagation(); - }, 10); - - $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); - $result = $service->create(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUrlWildcard, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateEvent::class, 0], - [CreateEvent::class, 0], - ]); - } - - public function testTranslateEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeTranslateEvent::class, - TranslateEvent::class - ); - - $parameters = [ - 'random_value_5cff79c316cfa7.72466150', - ]; - - $result = $this->createMock(URLWildcardTranslationResult::class); - $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); - $innerServiceMock->method('translate')->willReturn($result); - - $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); - $result = $service->translate(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($result, $result); - $this->assertSame($calledListeners, [ - [BeforeTranslateEvent::class, 0], - [TranslateEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnTranslateResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeTranslateEvent::class, - TranslateEvent::class - ); - - $parameters = [ - 'random_value_5cff79c316d370.25863709', - ]; - - $result = $this->createMock(URLWildcardTranslationResult::class); - $eventResult = $this->createMock(URLWildcardTranslationResult::class); - $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); - $innerServiceMock->method('translate')->willReturn($result); - - $traceableEventDispatcher->addListener(BeforeTranslateEvent::class, static function (BeforeTranslateEvent $event) use ($eventResult) { - $event->setResult($eventResult); - }, 10); - - $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); - $result = $service->translate(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventResult, $result); - $this->assertSame($calledListeners, [ - [BeforeTranslateEvent::class, 10], - [BeforeTranslateEvent::class, 0], - [TranslateEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testTranslateStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeTranslateEvent::class, - TranslateEvent::class - ); - - $parameters = [ - 'random_value_5cff79c316d3f9.73226122', - ]; - - $result = $this->createMock(URLWildcardTranslationResult::class); - $eventResult = $this->createMock(URLWildcardTranslationResult::class); - $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); - $innerServiceMock->method('translate')->willReturn($result); - - $traceableEventDispatcher->addListener(BeforeTranslateEvent::class, static function (BeforeTranslateEvent $event) use ($eventResult) { - $event->setResult($eventResult); - $event->stopPropagation(); - }, 10); - - $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); - $result = $service->translate(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventResult, $result); - $this->assertSame($calledListeners, [ - [BeforeTranslateEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeTranslateEvent::class, 0], - [TranslateEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/UserPreferenceServiceTest.php b/eZ/Publish/Core/Event/Tests/UserPreferenceServiceTest.php deleted file mode 100644 index 6137560bac..0000000000 --- a/eZ/Publish/Core/Event/Tests/UserPreferenceServiceTest.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\Events\UserPreference\BeforeSetUserPreferenceEvent; -use eZ\Publish\API\Repository\Events\UserPreference\SetUserPreferenceEvent; -use eZ\Publish\API\Repository\UserPreferenceService as UserPreferenceServiceInterface; -use eZ\Publish\Core\Event\UserPreferenceService; - -class UserPreferenceServiceTest extends AbstractServiceTest -{ - public function testSetUserPreferenceEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeSetUserPreferenceEvent::class, - SetUserPreferenceEvent::class - ); - - $parameters = [ - [], - ]; - - $innerServiceMock = $this->createMock(UserPreferenceServiceInterface::class); - - $service = new UserPreferenceService($innerServiceMock, $traceableEventDispatcher); - $service->setUserPreference(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeSetUserPreferenceEvent::class, 0], - [SetUserPreferenceEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testSetUserPreferenceStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeSetUserPreferenceEvent::class, - SetUserPreferenceEvent::class - ); - - $parameters = [ - [], - ]; - - $innerServiceMock = $this->createMock(UserPreferenceServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeSetUserPreferenceEvent::class, static function (BeforeSetUserPreferenceEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new UserPreferenceService($innerServiceMock, $traceableEventDispatcher); - $service->setUserPreference(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeSetUserPreferenceEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeSetUserPreferenceEvent::class, 0], - [SetUserPreferenceEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/Tests/UserServiceTest.php b/eZ/Publish/Core/Event/Tests/UserServiceTest.php deleted file mode 100644 index c45bd7f5e4..0000000000 --- a/eZ/Publish/Core/Event/Tests/UserServiceTest.php +++ /dev/null @@ -1,925 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Event\Tests; - -use eZ\Publish\API\Repository\Events\User\AssignUserToUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeAssignUserToUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeCreateUserEvent; -use eZ\Publish\API\Repository\Events\User\BeforeCreateUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeDeleteUserEvent; -use eZ\Publish\API\Repository\Events\User\BeforeDeleteUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeMoveUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeUnAssignUserFromUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeUpdateUserEvent; -use eZ\Publish\API\Repository\Events\User\BeforeUpdateUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeUpdateUserTokenEvent; -use eZ\Publish\API\Repository\Events\User\CreateUserEvent; -use eZ\Publish\API\Repository\Events\User\CreateUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\DeleteUserEvent; -use eZ\Publish\API\Repository\Events\User\DeleteUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\MoveUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\UnAssignUserFromUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\UpdateUserEvent; -use eZ\Publish\API\Repository\Events\User\UpdateUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\UpdateUserTokenEvent; -use eZ\Publish\API\Repository\UserService as UserServiceInterface; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserTokenUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserUpdateStruct; -use eZ\Publish\Core\Event\UserService; - -class UserServiceTest extends AbstractServiceTest -{ - public function testUpdateUserGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateUserGroupEvent::class, - UpdateUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(UserGroup::class), - $this->createMock(UserGroupUpdateStruct::class), - ]; - - $updatedUserGroup = $this->createMock(UserGroup::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('updateUserGroup')->willReturn($updatedUserGroup); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($updatedUserGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateUserGroupEvent::class, 0], - [UpdateUserGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdateUserGroupResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateUserGroupEvent::class, - UpdateUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(UserGroup::class), - $this->createMock(UserGroupUpdateStruct::class), - ]; - - $updatedUserGroup = $this->createMock(UserGroup::class); - $eventUpdatedUserGroup = $this->createMock(UserGroup::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('updateUserGroup')->willReturn($updatedUserGroup); - - $traceableEventDispatcher->addListener(BeforeUpdateUserGroupEvent::class, static function (BeforeUpdateUserGroupEvent $event) use ($eventUpdatedUserGroup) { - $event->setUpdatedUserGroup($eventUpdatedUserGroup); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUpdatedUserGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateUserGroupEvent::class, 10], - [BeforeUpdateUserGroupEvent::class, 0], - [UpdateUserGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateUserGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateUserGroupEvent::class, - UpdateUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(UserGroup::class), - $this->createMock(UserGroupUpdateStruct::class), - ]; - - $updatedUserGroup = $this->createMock(UserGroup::class); - $eventUpdatedUserGroup = $this->createMock(UserGroup::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('updateUserGroup')->willReturn($updatedUserGroup); - - $traceableEventDispatcher->addListener(BeforeUpdateUserGroupEvent::class, static function (BeforeUpdateUserGroupEvent $event) use ($eventUpdatedUserGroup) { - $event->setUpdatedUserGroup($eventUpdatedUserGroup); - $event->stopPropagation(); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUpdatedUserGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateUserGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateUserGroupEvent::class, 0], - [UpdateUserGroupEvent::class, 0], - ]); - } - - public function testUpdateUserEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateUserEvent::class, - UpdateUserEvent::class - ); - - $parameters = [ - $this->createMock(User::class), - $this->createMock(UserUpdateStruct::class), - ]; - - $updatedUser = $this->createMock(User::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('updateUser')->willReturn($updatedUser); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateUser(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($updatedUser, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateUserEvent::class, 0], - [UpdateUserEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdateUserResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateUserEvent::class, - UpdateUserEvent::class - ); - - $parameters = [ - $this->createMock(User::class), - $this->createMock(UserUpdateStruct::class), - ]; - - $updatedUser = $this->createMock(User::class); - $eventUpdatedUser = $this->createMock(User::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('updateUser')->willReturn($updatedUser); - - $traceableEventDispatcher->addListener(BeforeUpdateUserEvent::class, static function (BeforeUpdateUserEvent $event) use ($eventUpdatedUser) { - $event->setUpdatedUser($eventUpdatedUser); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateUser(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUpdatedUser, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateUserEvent::class, 10], - [BeforeUpdateUserEvent::class, 0], - [UpdateUserEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateUserStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateUserEvent::class, - UpdateUserEvent::class - ); - - $parameters = [ - $this->createMock(User::class), - $this->createMock(UserUpdateStruct::class), - ]; - - $updatedUser = $this->createMock(User::class); - $eventUpdatedUser = $this->createMock(User::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('updateUser')->willReturn($updatedUser); - - $traceableEventDispatcher->addListener(BeforeUpdateUserEvent::class, static function (BeforeUpdateUserEvent $event) use ($eventUpdatedUser) { - $event->setUpdatedUser($eventUpdatedUser); - $event->stopPropagation(); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateUser(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUpdatedUser, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateUserEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateUserEvent::class, 0], - [UpdateUserEvent::class, 0], - ]); - } - - public function testUnAssignUserFromUserGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUnAssignUserFromUserGroupEvent::class, - UnAssignUserFromUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(User::class), - $this->createMock(UserGroup::class), - ]; - - $innerServiceMock = $this->createMock(UserServiceInterface::class); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $service->unAssignUserFromUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeUnAssignUserFromUserGroupEvent::class, 0], - [UnAssignUserFromUserGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUnAssignUserFromUserGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUnAssignUserFromUserGroupEvent::class, - UnAssignUserFromUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(User::class), - $this->createMock(UserGroup::class), - ]; - - $innerServiceMock = $this->createMock(UserServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeUnAssignUserFromUserGroupEvent::class, static function (BeforeUnAssignUserFromUserGroupEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $service->unAssignUserFromUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeUnAssignUserFromUserGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUnAssignUserFromUserGroupEvent::class, 0], - [UnAssignUserFromUserGroupEvent::class, 0], - ]); - } - - public function testDeleteUserGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteUserGroupEvent::class, - DeleteUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(UserGroup::class), - ]; - - $locations = []; - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('deleteUserGroup')->willReturn($locations); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->deleteUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($locations, $result); - $this->assertSame($calledListeners, [ - [BeforeDeleteUserGroupEvent::class, 0], - [DeleteUserGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnDeleteUserGroupResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteUserGroupEvent::class, - DeleteUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(UserGroup::class), - ]; - - $locations = []; - $eventLocations = []; - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('deleteUserGroup')->willReturn($locations); - - $traceableEventDispatcher->addListener(BeforeDeleteUserGroupEvent::class, static function (BeforeDeleteUserGroupEvent $event) use ($eventLocations) { - $event->setLocations($eventLocations); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->deleteUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventLocations, $result); - $this->assertSame($calledListeners, [ - [BeforeDeleteUserGroupEvent::class, 10], - [BeforeDeleteUserGroupEvent::class, 0], - [DeleteUserGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteUserGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteUserGroupEvent::class, - DeleteUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(UserGroup::class), - ]; - - $locations = []; - $eventLocations = []; - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('deleteUserGroup')->willReturn($locations); - - $traceableEventDispatcher->addListener(BeforeDeleteUserGroupEvent::class, static function (BeforeDeleteUserGroupEvent $event) use ($eventLocations) { - $event->setLocations($eventLocations); - $event->stopPropagation(); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->deleteUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventLocations, $result); - $this->assertSame($calledListeners, [ - [BeforeDeleteUserGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteUserGroupEvent::class, 0], - [DeleteUserGroupEvent::class, 0], - ]); - } - - public function testAssignUserToUserGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAssignUserToUserGroupEvent::class, - AssignUserToUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(User::class), - $this->createMock(UserGroup::class), - ]; - - $innerServiceMock = $this->createMock(UserServiceInterface::class); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $service->assignUserToUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAssignUserToUserGroupEvent::class, 0], - [AssignUserToUserGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testAssignUserToUserGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeAssignUserToUserGroupEvent::class, - AssignUserToUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(User::class), - $this->createMock(UserGroup::class), - ]; - - $innerServiceMock = $this->createMock(UserServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeAssignUserToUserGroupEvent::class, static function (BeforeAssignUserToUserGroupEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $service->assignUserToUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeAssignUserToUserGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [AssignUserToUserGroupEvent::class, 0], - [BeforeAssignUserToUserGroupEvent::class, 0], - ]); - } - - public function testDeleteUserEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteUserEvent::class, - DeleteUserEvent::class - ); - - $parameters = [ - $this->createMock(User::class), - ]; - - $locations = []; - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('deleteUser')->willReturn($locations); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->deleteUser(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($locations, $result); - $this->assertSame($calledListeners, [ - [BeforeDeleteUserEvent::class, 0], - [DeleteUserEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnDeleteUserResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteUserEvent::class, - DeleteUserEvent::class - ); - - $parameters = [ - $this->createMock(User::class), - ]; - - $locations = []; - $eventLocations = []; - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('deleteUser')->willReturn($locations); - - $traceableEventDispatcher->addListener(BeforeDeleteUserEvent::class, static function (BeforeDeleteUserEvent $event) use ($eventLocations) { - $event->setLocations($eventLocations); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->deleteUser(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventLocations, $result); - $this->assertSame($calledListeners, [ - [BeforeDeleteUserEvent::class, 10], - [BeforeDeleteUserEvent::class, 0], - [DeleteUserEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testDeleteUserStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeDeleteUserEvent::class, - DeleteUserEvent::class - ); - - $parameters = [ - $this->createMock(User::class), - ]; - - $locations = []; - $eventLocations = []; - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('deleteUser')->willReturn($locations); - - $traceableEventDispatcher->addListener(BeforeDeleteUserEvent::class, static function (BeforeDeleteUserEvent $event) use ($eventLocations) { - $event->setLocations($eventLocations); - $event->stopPropagation(); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->deleteUser(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventLocations, $result); - $this->assertSame($calledListeners, [ - [BeforeDeleteUserEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeDeleteUserEvent::class, 0], - [DeleteUserEvent::class, 0], - ]); - } - - public function testMoveUserGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeMoveUserGroupEvent::class, - MoveUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(UserGroup::class), - $this->createMock(UserGroup::class), - ]; - - $innerServiceMock = $this->createMock(UserServiceInterface::class); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $service->moveUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeMoveUserGroupEvent::class, 0], - [MoveUserGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testMoveUserGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeMoveUserGroupEvent::class, - MoveUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(UserGroup::class), - $this->createMock(UserGroup::class), - ]; - - $innerServiceMock = $this->createMock(UserServiceInterface::class); - - $traceableEventDispatcher->addListener(BeforeMoveUserGroupEvent::class, static function (BeforeMoveUserGroupEvent $event) { - $event->stopPropagation(); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $service->moveUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($calledListeners, [ - [BeforeMoveUserGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeMoveUserGroupEvent::class, 0], - [MoveUserGroupEvent::class, 0], - ]); - } - - public function testCreateUserEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateUserEvent::class, - CreateUserEvent::class - ); - - $parameters = [ - $this->createMock(UserCreateStruct::class), - [], - ]; - - $user = $this->createMock(User::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('createUser')->willReturn($user); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createUser(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($user, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateUserEvent::class, 0], - [CreateUserEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateUserResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateUserEvent::class, - CreateUserEvent::class - ); - - $parameters = [ - $this->createMock(UserCreateStruct::class), - [], - ]; - - $user = $this->createMock(User::class); - $eventUser = $this->createMock(User::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('createUser')->willReturn($user); - - $traceableEventDispatcher->addListener(BeforeCreateUserEvent::class, static function (BeforeCreateUserEvent $event) use ($eventUser) { - $event->setUser($eventUser); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createUser(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUser, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateUserEvent::class, 10], - [BeforeCreateUserEvent::class, 0], - [CreateUserEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateUserStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateUserEvent::class, - CreateUserEvent::class - ); - - $parameters = [ - $this->createMock(UserCreateStruct::class), - [], - ]; - - $user = $this->createMock(User::class); - $eventUser = $this->createMock(User::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('createUser')->willReturn($user); - - $traceableEventDispatcher->addListener(BeforeCreateUserEvent::class, static function (BeforeCreateUserEvent $event) use ($eventUser) { - $event->setUser($eventUser); - $event->stopPropagation(); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createUser(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUser, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateUserEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateUserEvent::class, 0], - [CreateUserEvent::class, 0], - ]); - } - - public function testCreateUserGroupEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateUserGroupEvent::class, - CreateUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(UserGroupCreateStruct::class), - $this->createMock(UserGroup::class), - ]; - - $userGroup = $this->createMock(UserGroup::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('createUserGroup')->willReturn($userGroup); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($userGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateUserGroupEvent::class, 0], - [CreateUserGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnCreateUserGroupResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateUserGroupEvent::class, - CreateUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(UserGroupCreateStruct::class), - $this->createMock(UserGroup::class), - ]; - - $userGroup = $this->createMock(UserGroup::class); - $eventUserGroup = $this->createMock(UserGroup::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('createUserGroup')->willReturn($userGroup); - - $traceableEventDispatcher->addListener(BeforeCreateUserGroupEvent::class, static function (BeforeCreateUserGroupEvent $event) use ($eventUserGroup) { - $event->setUserGroup($eventUserGroup); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUserGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateUserGroupEvent::class, 10], - [BeforeCreateUserGroupEvent::class, 0], - [CreateUserGroupEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testCreateUserGroupStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeCreateUserGroupEvent::class, - CreateUserGroupEvent::class - ); - - $parameters = [ - $this->createMock(UserGroupCreateStruct::class), - $this->createMock(UserGroup::class), - ]; - - $userGroup = $this->createMock(UserGroup::class); - $eventUserGroup = $this->createMock(UserGroup::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('createUserGroup')->willReturn($userGroup); - - $traceableEventDispatcher->addListener(BeforeCreateUserGroupEvent::class, static function (BeforeCreateUserGroupEvent $event) use ($eventUserGroup) { - $event->setUserGroup($eventUserGroup); - $event->stopPropagation(); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->createUserGroup(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUserGroup, $result); - $this->assertSame($calledListeners, [ - [BeforeCreateUserGroupEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeCreateUserGroupEvent::class, 0], - [CreateUserGroupEvent::class, 0], - ]); - } - - public function testUpdateUserTokenEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateUserTokenEvent::class, - UpdateUserTokenEvent::class - ); - - $parameters = [ - $this->createMock(User::class), - $this->createMock(UserTokenUpdateStruct::class), - ]; - - $updatedUser = $this->createMock(User::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('updateUserToken')->willReturn($updatedUser); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateUserToken(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($updatedUser, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateUserTokenEvent::class, 0], - [UpdateUserTokenEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testReturnUpdateUserTokenResultInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateUserTokenEvent::class, - UpdateUserTokenEvent::class - ); - - $parameters = [ - $this->createMock(User::class), - $this->createMock(UserTokenUpdateStruct::class), - ]; - - $updatedUser = $this->createMock(User::class); - $eventUpdatedUser = $this->createMock(User::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('updateUserToken')->willReturn($updatedUser); - - $traceableEventDispatcher->addListener(BeforeUpdateUserTokenEvent::class, static function (BeforeUpdateUserTokenEvent $event) use ($eventUpdatedUser) { - $event->setUpdatedUser($eventUpdatedUser); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateUserToken(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - - $this->assertSame($eventUpdatedUser, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateUserTokenEvent::class, 10], - [BeforeUpdateUserTokenEvent::class, 0], - [UpdateUserTokenEvent::class, 0], - ]); - $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); - } - - public function testUpdateUserTokenStopPropagationInBeforeEvents() - { - $traceableEventDispatcher = $this->getEventDispatcher( - BeforeUpdateUserTokenEvent::class, - UpdateUserTokenEvent::class - ); - - $parameters = [ - $this->createMock(User::class), - $this->createMock(UserTokenUpdateStruct::class), - ]; - - $updatedUser = $this->createMock(User::class); - $eventUpdatedUser = $this->createMock(User::class); - $innerServiceMock = $this->createMock(UserServiceInterface::class); - $innerServiceMock->method('updateUserToken')->willReturn($updatedUser); - - $traceableEventDispatcher->addListener(BeforeUpdateUserTokenEvent::class, static function (BeforeUpdateUserTokenEvent $event) use ($eventUpdatedUser) { - $event->setUpdatedUser($eventUpdatedUser); - $event->stopPropagation(); - }, 10); - - $service = new UserService($innerServiceMock, $traceableEventDispatcher); - $result = $service->updateUserToken(...$parameters); - - $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); - $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); - - $this->assertSame($eventUpdatedUser, $result); - $this->assertSame($calledListeners, [ - [BeforeUpdateUserTokenEvent::class, 10], - ]); - $this->assertSame($notCalledListeners, [ - [BeforeUpdateUserTokenEvent::class, 0], - [UpdateUserTokenEvent::class, 0], - ]); - } -} diff --git a/eZ/Publish/Core/Event/TranslationService.php b/eZ/Publish/Core/Event/TranslationService.php deleted file mode 100644 index 392f8ed55a..0000000000 --- a/eZ/Publish/Core/Event/TranslationService.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\TranslationService as TranslationServiceInterface; -use eZ\Publish\SPI\Repository\Decorator\TranslationServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class TranslationService extends TranslationServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - TranslationServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } -} diff --git a/eZ/Publish/Core/Event/TrashService.php b/eZ/Publish/Core/Event/TrashService.php deleted file mode 100644 index 231c4d8e4a..0000000000 --- a/eZ/Publish/Core/Event/TrashService.php +++ /dev/null @@ -1,131 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\Events\Trash\BeforeDeleteTrashItemEvent; -use eZ\Publish\API\Repository\Events\Trash\BeforeEmptyTrashEvent; -use eZ\Publish\API\Repository\Events\Trash\BeforeRecoverEvent; -use eZ\Publish\API\Repository\Events\Trash\BeforeTrashEvent; -use eZ\Publish\API\Repository\Events\Trash\DeleteTrashItemEvent; -use eZ\Publish\API\Repository\Events\Trash\EmptyTrashEvent; -use eZ\Publish\API\Repository\Events\Trash\RecoverEvent; -use eZ\Publish\API\Repository\Events\Trash\TrashEvent; -use eZ\Publish\API\Repository\TrashService as TrashServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResultList; -use eZ\Publish\API\Repository\Values\Content\TrashItem; -use eZ\Publish\SPI\Repository\Decorator\TrashServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class TrashService extends TrashServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - TrashServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function trash(Location $location): ?TrashItem - { - $eventData = [$location]; - - $beforeEvent = new BeforeTrashEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getResult(); - } - - $trashItem = $beforeEvent->isResultSet() - ? $beforeEvent->getResult() - : $this->innerService->trash($location); - - $this->eventDispatcher->dispatch( - new TrashEvent($trashItem, ...$eventData) - ); - - return $trashItem; - } - - public function recover( - TrashItem $trashItem, - ?Location $newParentLocation = null - ): Location { - $eventData = [ - $trashItem, - $newParentLocation, - ]; - - $beforeEvent = new BeforeRecoverEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getLocation(); - } - - $location = $beforeEvent->hasLocation() - ? $beforeEvent->getLocation() - : $this->innerService->recover($trashItem, $newParentLocation); - - $this->eventDispatcher->dispatch( - new RecoverEvent($location, ...$eventData) - ); - - return $location; - } - - public function emptyTrash(): TrashItemDeleteResultList - { - $beforeEvent = new BeforeEmptyTrashEvent(); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getResultList(); - } - - $resultList = $beforeEvent->hasResultList() - ? $beforeEvent->getResultList() - : $this->innerService->emptyTrash(); - - $this->eventDispatcher->dispatch( - new EmptyTrashEvent($resultList) - ); - - return $resultList; - } - - public function deleteTrashItem(TrashItem $trashItem): TrashItemDeleteResult - { - $eventData = [$trashItem]; - - $beforeEvent = new BeforeDeleteTrashItemEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getResult(); - } - - $result = $beforeEvent->hasResult() - ? $beforeEvent->getResult() - : $this->innerService->deleteTrashItem($trashItem); - - $this->eventDispatcher->dispatch( - new DeleteTrashItemEvent($result, ...$eventData) - ); - - return $result; - } -} diff --git a/eZ/Publish/Core/Event/URLAliasService.php b/eZ/Publish/Core/Event/URLAliasService.php deleted file mode 100644 index 3aaa3d8665..0000000000 --- a/eZ/Publish/Core/Event/URLAliasService.php +++ /dev/null @@ -1,140 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\Events\URLAlias\BeforeCreateGlobalUrlAliasEvent; -use eZ\Publish\API\Repository\Events\URLAlias\BeforeCreateUrlAliasEvent; -use eZ\Publish\API\Repository\Events\URLAlias\BeforeRefreshSystemUrlAliasesForLocationEvent; -use eZ\Publish\API\Repository\Events\URLAlias\BeforeRemoveAliasesEvent; -use eZ\Publish\API\Repository\Events\URLAlias\CreateGlobalUrlAliasEvent; -use eZ\Publish\API\Repository\Events\URLAlias\CreateUrlAliasEvent; -use eZ\Publish\API\Repository\Events\URLAlias\RefreshSystemUrlAliasesForLocationEvent; -use eZ\Publish\API\Repository\Events\URLAlias\RemoveAliasesEvent; -use eZ\Publish\API\Repository\URLAliasService as URLAliasServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use eZ\Publish\SPI\Repository\Decorator\URLAliasServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class URLAliasService extends URLAliasServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - URLAliasServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function createUrlAlias( - Location $location, - string $path, - string $languageCode, - bool $forwarding = false, - bool $alwaysAvailable = false - ): URLAlias { - $eventData = [ - $location, - $path, - $languageCode, - $forwarding, - $alwaysAvailable, - ]; - - $beforeEvent = new BeforeCreateUrlAliasEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUrlAlias(); - } - - $urlAlias = $beforeEvent->hasUrlAlias() - ? $beforeEvent->getUrlAlias() - : $this->innerService->createUrlAlias($location, $path, $languageCode, $forwarding, $alwaysAvailable); - - $this->eventDispatcher->dispatch( - new CreateUrlAliasEvent($urlAlias, ...$eventData) - ); - - return $urlAlias; - } - - public function createGlobalUrlAlias( - string $resource, - string $path, - string $languageCode, - bool $forwarding = false, - bool $alwaysAvailable = false - ): URLAlias { - $eventData = [ - $resource, - $path, - $languageCode, - $forwarding, - $alwaysAvailable, - ]; - - $beforeEvent = new BeforeCreateGlobalUrlAliasEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUrlAlias(); - } - - $urlAlias = $beforeEvent->hasUrlAlias() - ? $beforeEvent->getUrlAlias() - : $this->innerService->createGlobalUrlAlias($resource, $path, $languageCode, $forwarding, $alwaysAvailable); - - $this->eventDispatcher->dispatch( - new CreateGlobalUrlAliasEvent($urlAlias, ...$eventData) - ); - - return $urlAlias; - } - - public function removeAliases(array $aliasList): void - { - $eventData = [$aliasList]; - - $beforeEvent = new BeforeRemoveAliasesEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->removeAliases($aliasList); - - $this->eventDispatcher->dispatch( - new RemoveAliasesEvent(...$eventData) - ); - } - - public function refreshSystemUrlAliasesForLocation(Location $location): void - { - $eventData = [$location]; - - $beforeEvent = new BeforeRefreshSystemUrlAliasesForLocationEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->refreshSystemUrlAliasesForLocation($location); - - $this->eventDispatcher->dispatch( - new RefreshSystemUrlAliasesForLocationEvent(...$eventData) - ); - } -} diff --git a/eZ/Publish/Core/Event/URLService.php b/eZ/Publish/Core/Event/URLService.php deleted file mode 100644 index 9072d70ff0..0000000000 --- a/eZ/Publish/Core/Event/URLService.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\Events\URL\BeforeUpdateUrlEvent; -use eZ\Publish\API\Repository\Events\URL\UpdateUrlEvent; -use eZ\Publish\API\Repository\URLService as URLServiceInterface; -use eZ\Publish\API\Repository\Values\URL\URL; -use eZ\Publish\API\Repository\Values\URL\URLUpdateStruct; -use eZ\Publish\SPI\Repository\Decorator\URLServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class URLService extends URLServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - URLServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function updateUrl( - URL $url, - URLUpdateStruct $struct - ): URL { - $eventData = [ - $url, - $struct, - ]; - - $beforeEvent = new BeforeUpdateUrlEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedUrl(); - } - - $updatedUrl = $beforeEvent->hasUpdatedUrl() - ? $beforeEvent->getUpdatedUrl() - : $this->innerService->updateUrl($url, $struct); - - $this->eventDispatcher->dispatch( - new UpdateUrlEvent($updatedUrl, ...$eventData) - ); - - return $updatedUrl; - } -} diff --git a/eZ/Publish/Core/Event/URLWildcardService.php b/eZ/Publish/Core/Event/URLWildcardService.php deleted file mode 100644 index 8656412a82..0000000000 --- a/eZ/Publish/Core/Event/URLWildcardService.php +++ /dev/null @@ -1,131 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\Events\URLWildcard\BeforeCreateEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\BeforeRemoveEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\BeforeTranslateEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\BeforeUpdateEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\CreateEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\RemoveEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\TranslateEvent; -use eZ\Publish\API\Repository\Events\URLWildcard\UpdateEvent; -use eZ\Publish\API\Repository\URLWildcardService as URLWildcardServiceInterface; -use eZ\Publish\API\Repository\Values\Content\URLWildcard; -use eZ\Publish\API\Repository\Values\Content\URLWildcardTranslationResult; -use eZ\Publish\API\Repository\Values\Content\URLWildcardUpdateStruct; -use eZ\Publish\SPI\Repository\Decorator\URLWildcardServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class URLWildcardService extends URLWildcardServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - URLWildcardServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function create( - string $sourceUrl, - string $destinationUrl, - bool $forward = false - ): UrlWildcard { - $eventData = [ - $sourceUrl, - $destinationUrl, - $forward, - ]; - - $beforeEvent = new BeforeCreateEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUrlWildcard(); - } - - $urlWildcard = $beforeEvent->hasUrlWildcard() - ? $beforeEvent->getUrlWildcard() - : $this->innerService->create($sourceUrl, $destinationUrl, $forward); - - $this->eventDispatcher->dispatch( - new CreateEvent($urlWildcard, ...$eventData) - ); - - return $urlWildcard; - } - - public function update( - URLWildcard $urlWildcard, - URLWildcardUpdateStruct $updateStruct - ): void { - $eventData = [ - $urlWildcard, - $updateStruct, - ]; - - $beforeEvent = new BeforeUpdateEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->update($urlWildcard, $updateStruct); - - $this->eventDispatcher->dispatch( - new UpdateEvent(...$eventData) - ); - } - - public function remove(URLWildcard $urlWildcard): void - { - $eventData = [$urlWildcard]; - - $beforeEvent = new BeforeRemoveEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->remove($urlWildcard); - - $this->eventDispatcher->dispatch( - new RemoveEvent(...$eventData) - ); - } - - public function translate(string $url): URLWildcardTranslationResult - { - $eventData = [$url]; - - $beforeEvent = new BeforeTranslateEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getResult(); - } - - $result = $beforeEvent->hasResult() - ? $beforeEvent->getResult() - : $this->innerService->translate($url); - - $this->eventDispatcher->dispatch( - new TranslateEvent($result, ...$eventData) - ); - - return $result; - } -} diff --git a/eZ/Publish/Core/Event/UserPreferenceService.php b/eZ/Publish/Core/Event/UserPreferenceService.php deleted file mode 100644 index 1dcd657484..0000000000 --- a/eZ/Publish/Core/Event/UserPreferenceService.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\Events\UserPreference\BeforeSetUserPreferenceEvent; -use eZ\Publish\API\Repository\Events\UserPreference\SetUserPreferenceEvent; -use eZ\Publish\API\Repository\UserPreferenceService as UserPreferenceServiceInterface; -use eZ\Publish\SPI\Repository\Decorator\UserPreferenceServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class UserPreferenceService extends UserPreferenceServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - UserPreferenceServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function setUserPreference(array $userPreferenceSetStructs): void - { - $eventData = [$userPreferenceSetStructs]; - - $beforeEvent = new BeforeSetUserPreferenceEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->setUserPreference($userPreferenceSetStructs); - - $this->eventDispatcher->dispatch( - new SetUserPreferenceEvent(...$eventData) - ); - } -} diff --git a/eZ/Publish/Core/Event/UserService.php b/eZ/Publish/Core/Event/UserService.php deleted file mode 100644 index 3f32c77616..0000000000 --- a/eZ/Publish/Core/Event/UserService.php +++ /dev/null @@ -1,357 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Event; - -use eZ\Publish\API\Repository\Events\User\AssignUserToUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeAssignUserToUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeCreateUserEvent; -use eZ\Publish\API\Repository\Events\User\BeforeCreateUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeDeleteUserEvent; -use eZ\Publish\API\Repository\Events\User\BeforeDeleteUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeMoveUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeUnAssignUserFromUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeUpdateUserEvent; -use eZ\Publish\API\Repository\Events\User\BeforeUpdateUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeUpdateUserPasswordEvent; -use eZ\Publish\API\Repository\Events\User\BeforeUpdateUserTokenEvent; -use eZ\Publish\API\Repository\Events\User\CreateUserEvent; -use eZ\Publish\API\Repository\Events\User\CreateUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\DeleteUserEvent; -use eZ\Publish\API\Repository\Events\User\DeleteUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\MoveUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\UnAssignUserFromUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\UpdateUserEvent; -use eZ\Publish\API\Repository\Events\User\UpdateUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\UpdateUserPasswordEvent; -use eZ\Publish\API\Repository\Events\User\UpdateUserTokenEvent; -use eZ\Publish\API\Repository\UserService as UserServiceInterface; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserTokenUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserUpdateStruct; -use eZ\Publish\SPI\Repository\Decorator\UserServiceDecorator; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -class UserService extends UserServiceDecorator -{ - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct( - UserServiceInterface $innerService, - EventDispatcherInterface $eventDispatcher - ) { - parent::__construct($innerService); - - $this->eventDispatcher = $eventDispatcher; - } - - public function createUserGroup( - UserGroupCreateStruct $userGroupCreateStruct, - UserGroup $parentGroup - ): UserGroup { - $eventData = [ - $userGroupCreateStruct, - $parentGroup, - ]; - - $beforeEvent = new BeforeCreateUserGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUserGroup(); - } - - $userGroup = $beforeEvent->hasUserGroup() - ? $beforeEvent->getUserGroup() - : $this->innerService->createUserGroup($userGroupCreateStruct, $parentGroup); - - $this->eventDispatcher->dispatch( - new CreateUserGroupEvent($userGroup, ...$eventData) - ); - - return $userGroup; - } - - public function deleteUserGroup(UserGroup $userGroup): iterable - { - $eventData = [$userGroup]; - - $beforeEvent = new BeforeDeleteUserGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getLocations(); - } - - $locations = $beforeEvent->hasLocations() - ? $beforeEvent->getLocations() - : $this->innerService->deleteUserGroup($userGroup); - - $this->eventDispatcher->dispatch( - new DeleteUserGroupEvent($locations, ...$eventData) - ); - - return $locations; - } - - public function moveUserGroup( - UserGroup $userGroup, - UserGroup $newParent - ): void { - $eventData = [ - $userGroup, - $newParent, - ]; - - $beforeEvent = new BeforeMoveUserGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->moveUserGroup($userGroup, $newParent); - - $this->eventDispatcher->dispatch( - new MoveUserGroupEvent(...$eventData) - ); - } - - public function updateUserGroup( - UserGroup $userGroup, - UserGroupUpdateStruct $userGroupUpdateStruct - ): UserGroup { - $eventData = [ - $userGroup, - $userGroupUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdateUserGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedUserGroup(); - } - - $updatedUserGroup = $beforeEvent->hasUpdatedUserGroup() - ? $beforeEvent->getUpdatedUserGroup() - : $this->innerService->updateUserGroup($userGroup, $userGroupUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdateUserGroupEvent($updatedUserGroup, ...$eventData) - ); - - return $updatedUserGroup; - } - - public function createUser( - UserCreateStruct $userCreateStruct, - array $parentGroups - ): User { - $eventData = [ - $userCreateStruct, - $parentGroups, - ]; - - $beforeEvent = new BeforeCreateUserEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUser(); - } - - $user = $beforeEvent->hasUser() - ? $beforeEvent->getUser() - : $this->innerService->createUser($userCreateStruct, $parentGroups); - - $this->eventDispatcher->dispatch( - new CreateUserEvent($user, ...$eventData) - ); - - return $user; - } - - public function deleteUser(User $user): iterable - { - $eventData = [$user]; - - $beforeEvent = new BeforeDeleteUserEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getLocations(); - } - - $locations = $beforeEvent->hasLocations() - ? $beforeEvent->getLocations() - : $this->innerService->deleteUser($user); - - $this->eventDispatcher->dispatch( - new DeleteUserEvent($locations, ...$eventData) - ); - - return $locations; - } - - public function updateUser( - User $user, - UserUpdateStruct $userUpdateStruct - ): User { - $eventData = [ - $user, - $userUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdateUserEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedUser(); - } - - $updatedUser = $beforeEvent->hasUpdatedUser() - ? $beforeEvent->getUpdatedUser() - : $this->innerService->updateUser($user, $userUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdateUserEvent($updatedUser, ...$eventData) - ); - - return $updatedUser; - } - - public function updateUserPassword( - User $user, - string $newPassword - ): User { - $eventData = [ - $user, - new UserUpdateStruct([ - 'password' => $newPassword, - ]), - ]; - - /** - * @deprecated since eZ Platform by Ibexa v3.1. listening on BeforeUpdateUserEvent when updating password has been deprecated. Use BeforeUpdateUserPasswordEvent instead. - */ - $beforeEvent = new BeforeUpdateUserEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedUser(); - } - - $beforePasswordEvent = new BeforeUpdateUserPasswordEvent($user, $newPassword); - - $this->eventDispatcher->dispatch($beforePasswordEvent); - if ($beforePasswordEvent->isPropagationStopped()) { - return $beforePasswordEvent->getUpdatedUser(); - } - - if ($beforeEvent->hasUpdatedUser()) { - $updatedUser = $beforeEvent->getUpdatedUser(); - } elseif ($beforePasswordEvent->hasUpdatedUser()) { - $updatedUser = $beforePasswordEvent->getUpdatedUser(); - } else { - $updatedUser = $this->innerService->updateUserPassword($user, $newPassword); - } - - /** - * @deprecated since eZ Platform by Ibexa v3.1. Listening on UpdateUserEvent when updating password has been deprecated. Use UpdateUserPasswordEvent instead. - */ - $afterEvent = new UpdateUserEvent($updatedUser, ...$eventData); - $this->eventDispatcher->dispatch( - $afterEvent - ); - - $afterPasswordEvent = new UpdateUserPasswordEvent($updatedUser, $user, $newPassword); - $this->eventDispatcher->dispatch( - $afterPasswordEvent - ); - - return $updatedUser; - } - - public function updateUserToken( - User $user, - UserTokenUpdateStruct $userTokenUpdateStruct - ): User { - $eventData = [ - $user, - $userTokenUpdateStruct, - ]; - - $beforeEvent = new BeforeUpdateUserTokenEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return $beforeEvent->getUpdatedUser(); - } - - $updatedUser = $beforeEvent->hasUpdatedUser() - ? $beforeEvent->getUpdatedUser() - : $this->innerService->updateUserToken($user, $userTokenUpdateStruct); - - $this->eventDispatcher->dispatch( - new UpdateUserTokenEvent($updatedUser, ...$eventData) - ); - - return $updatedUser; - } - - public function assignUserToUserGroup( - User $user, - UserGroup $userGroup - ): void { - $eventData = [ - $user, - $userGroup, - ]; - - $beforeEvent = new BeforeAssignUserToUserGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->assignUserToUserGroup($user, $userGroup); - - $this->eventDispatcher->dispatch( - new AssignUserToUserGroupEvent(...$eventData) - ); - } - - public function unAssignUserFromUserGroup( - User $user, - UserGroup $userGroup - ): void { - $eventData = [ - $user, - $userGroup, - ]; - - $beforeEvent = new BeforeUnAssignUserFromUserGroupEvent(...$eventData); - - $this->eventDispatcher->dispatch($beforeEvent); - if ($beforeEvent->isPropagationStopped()) { - return; - } - - $this->innerService->unAssignUserFromUserGroup($user, $userGroup); - - $this->eventDispatcher->dispatch( - new UnAssignUserFromUserGroupEvent(...$eventData) - ); - } -} diff --git a/eZ/Publish/Core/FieldType/Author/AuthorCollection.php b/eZ/Publish/Core/FieldType/Author/AuthorCollection.php deleted file mode 100644 index f87c96da56..0000000000 --- a/eZ/Publish/Core/FieldType/Author/AuthorCollection.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Author; - -use ArrayObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; - -/** - * Author collection. - * This collection can only hold {@link \eZ\Publish\Core\FieldType\Author\Author} objects. - */ -class AuthorCollection extends ArrayObject -{ - /** - * @param \eZ\Publish\Core\FieldType\Author\Author[] $elements - */ - public function __construct(array $elements = []) - { - // Call parent constructor without $elements because all author elements - // must be given an id by $this->offsetSet() - parent::__construct(); - foreach ($elements as $i => $author) { - $this->offsetSet($i, $author); - } - } - - /** - * Adds a new author to the collection. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType When $value is not of type Author - * - * @param int $offset - * @param \eZ\Publish\Core\FieldType\Author\Author $value - */ - public function offsetSet($offset, $value): void - { - if (!$value instanceof Author) { - throw new InvalidArgumentType( - '$value', - 'eZ\\Publish\\Core\\FieldType\\Author\\Author', - $value - ); - } - - $aAuthors = $this->getArrayCopy(); - parent::offsetSet($offset, $value); - if (!isset($value->id) || $value->id == -1) { - if (!empty($aAuthors)) { - $value->id = end($aAuthors)->id + 1; - } else { - $value->id = 1; - } - } - } - - /** - * Removes authors from current collection with a list of Ids. - * - * @param array $authorIds Author's Ids to remove from current collection - */ - public function removeAuthorsById(array $authorIds) - { - $aAuthors = $this->getArrayCopy(); - foreach ($aAuthors as $i => $author) { - if (in_array($author->id, $authorIds)) { - unset($aAuthors[$i]); - } - } - - $this->exchangeArray($aAuthors); - } -} diff --git a/eZ/Publish/Core/FieldType/Author/SearchField.php b/eZ/Publish/Core/FieldType/Author/SearchField.php deleted file mode 100644 index 5d50fde58e..0000000000 --- a/eZ/Publish/Core/FieldType/Author/SearchField.php +++ /dev/null @@ -1,129 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Author; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; -use eZ\Publish\SPI\Search\FieldType\MultipleIdentifierField; - -/** - * Indexable definition for Author field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - $name = []; - $id = []; - $email = []; - $aggregationValues = []; - - foreach ($field->value->data as $author) { - $name[] = $author['name']; - $id[] = $author['id']; - $email[] = $author['email']; - - $aggregationValues[] = json_encode([ - 'name' => $author['name'], - 'email' => $author['email'], - ]); - } - - return [ - new Search\Field( - 'name', - $name, - new Search\FieldType\MultipleStringField() - ), - new Search\Field( - 'id', - $id, - new Search\FieldType\MultipleIntegerField() - ), - new Search\Field( - 'email', - $email, - new Search\FieldType\MultipleStringField() - ), - new Search\Field( - 'count', - count($field->value->data), - new Search\FieldType\IntegerField() - ), - new Search\Field( - 'aggregation_value', - $aggregationValues, - new MultipleIdentifierField(['raw' => true]), - ), - new Search\Field( - 'sort_value', - implode('-', $name), - new Search\FieldType\StringField() - ), - new Search\Field( - 'fulltext', - $name, - new Search\FieldType\FullTextField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'name' => new Search\FieldType\MultipleStringField(), - 'id' => new Search\FieldType\MultipleIntegerField(), - 'email' => new Search\FieldType\MultipleStringField(), - 'count' => new Search\FieldType\IntegerField(), - 'aggregation_value' => new MultipleIdentifierField(['raw' => true]), - 'sort_value' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'name'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return 'sort_value'; - } -} diff --git a/eZ/Publish/Core/FieldType/Author/Type.php b/eZ/Publish/Core/FieldType/Author/Type.php deleted file mode 100644 index 8ef3ac0507..0000000000 --- a/eZ/Publish/Core/FieldType/Author/Type.php +++ /dev/null @@ -1,250 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Author; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -/** - * Author field type. - * - * Field type representing a list of authors, consisting of author name, and - * author email. - */ -class Type extends FieldType -{ - /** - * Flag which stands for setting Author FieldType empty by default. - * It is used in a Content Type edit view. - */ - public const DEFAULT_VALUE_EMPTY = 0; - - /** - * Flag which stands for prefilling Author FieldType with current user by default. - * It is used in a Content Type edit view. - */ - public const DEFAULT_CURRENT_USER = 1; - - protected $settingsSchema = [ - 'defaultAuthor' => [ - 'type' => 'choice', - 'default' => self::DEFAULT_VALUE_EMPTY, - ], - ]; - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezauthor'; - } - - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return $value->authors[0]->name ?? ''; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Author\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param array|\eZ\Publish\Core\FieldType\Author\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\Author\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_array($inputValue)) { - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Author\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!$value->authors instanceof AuthorCollection) { - throw new InvalidArgumentType( - '$value->authors', - AuthorCollection::class, - $value->authors - ); - } - } - - /** - * {@inheritdoc} - */ - protected function getSortInfo(BaseValue $value) - { - if (empty($value->authors)) { - return false; - } - - $authors = []; - foreach ($value->authors as $author) { - $authors[] = $this->transformationProcessor->transformByGroup($author->name, 'lowercase'); - } - - sort($authors); - - return implode(',', $authors); - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\Author\Value $value - */ - public function fromHash($hash) - { - return new Value( - array_map( - static function ($author) { - return new Author($author); - }, - $hash - ) - ); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Author\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - return array_map( - static function ($author) { - return (array)$author; - }, - $value->authors->getArrayCopy() - ); - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } - - /** - * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param array $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings) - { - $validationErrors = []; - - foreach ($fieldSettings as $name => $value) { - $settingNameError = $this->validateSettingName($name); - - if ($settingNameError instanceof ValidationError) { - $validationErrors[] = $settingNameError; - } - - switch ($name) { - case 'defaultAuthor': - $settingValueError = $this->validateDefaultAuthorSetting($name, $value); - if ($settingValueError instanceof ValidationError) { - $validationErrors[] = $settingValueError; - } - break; - } - } - - return $validationErrors; - } - - /** - * Validates the fieldSetting name. - * - * @param string $name - * - * @return \eZ\Publish\SPI\FieldType\ValidationError|null - */ - private function validateSettingName($name) - { - if (!isset($this->settingsSchema[$name])) { - return new ValidationError( - "Setting '%setting%' is unknown", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - - return null; - } - - /** - * Validates if the defaultAuthor setting has one of the defined values. - * - * @param string $name - * @param string $value - * - * @return \eZ\Publish\SPI\FieldType\ValidationError|null - */ - private function validateDefaultAuthorSetting($name, $value) - { - $definedValues = [ - self::DEFAULT_VALUE_EMPTY, - self::DEFAULT_CURRENT_USER, - ]; - - if (!in_array($value, $definedValues, true)) { - return new ValidationError( - "Setting '%setting%' has unknown default value", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - - return null; - } -} diff --git a/eZ/Publish/Core/FieldType/Author/Value.php b/eZ/Publish/Core/FieldType/Author/Value.php deleted file mode 100644 index e8eceada78..0000000000 --- a/eZ/Publish/Core/FieldType/Author/Value.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Author; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for Author field type. - */ -class Value extends BaseValue -{ - /** - * List of authors. - * - * @var \eZ\Publish\Core\FieldType\Author\AuthorCollection - */ - public $authors; - - /** - * Construct a new Value object and initialize with $authors. - * - * @param \eZ\Publish\Core\FieldType\Author\Author[] $authors - */ - public function __construct(array $authors = []) - { - $this->authors = new AuthorCollection($authors); - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - if (empty($this->authors)) { - return ''; - } - - $authorNames = []; - - if ($this->authors instanceof AuthorCollection) { - foreach ($this->authors as $author) { - $authorNames[] = $author->name; - } - } - - return implode(', ', $authorNames); - } -} diff --git a/eZ/Publish/Core/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php b/eZ/Publish/Core/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php deleted file mode 100644 index 34d22a7d8c..0000000000 --- a/eZ/Publish/Core/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage; - -use eZ\Publish\SPI\FieldType\StorageGateway; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -abstract class Gateway extends StorageGateway -{ - /** - * Stores the file reference in $field for $versionNo. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - */ - abstract public function storeFileReference(VersionInfo $versionInfo, Field $field); - - /** - * Returns the file reference data for the given $fieldId in $versionNo. - * - * @param mixed $fieldId - * @param int $versionNo - * - * @return array|void - */ - abstract public function getFileReferenceData($fieldId, $versionNo); - - /** - * Removes all file references for the given $fieldIds. - * - * @param array $fieldIds - * @param int $versionNo - */ - abstract public function removeFileReferences(array $fieldIds, $versionNo); - - /** - * Removes a specific file reference for $fieldId and $versionId. - * - * @param mixed $fieldId - * @param int $versionNo - */ - abstract public function removeFileReference($fieldId, $versionNo); - - /** - * Returns a map of files referenced by the given $fieldIds. - * - * @param array $fieldIds - * @param int $versionNo - * - * @return array - */ - abstract public function getReferencedFiles(array $fieldIds, $versionNo); - - /** - * Returns a map with the number of references each file from $files has. - * - * @param array $files - * - * @return array - */ - abstract public function countFileReferences(array $files); -} diff --git a/eZ/Publish/Core/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php b/eZ/Publish/Core/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php deleted file mode 100644 index 628515f423..0000000000 --- a/eZ/Publish/Core/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php +++ /dev/null @@ -1,480 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage\Gateway; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use PDO; - -/** - * Base class for binary files external storage DoctrineStorage gateways. - */ -abstract class DoctrineStorage extends Gateway -{ - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - public function __construct(Connection $connection) - { - $this->connection = $connection; - } - - /** - * Return the table name to store data in. - * - * @return string - */ - abstract protected function getStorageTable(); - - /** - * Return a column to property mapping for the storage table. - * - * @return array - */ - protected function getPropertyMapping() - { - return [ - 'filename' => [ - 'name' => 'id', - 'cast' => 'strval', - ], - 'mime_type' => [ - 'name' => 'mimeType', - 'cast' => 'strval', - ], - 'original_filename' => [ - 'name' => 'fileName', - 'cast' => 'strval', - ], - ]; - } - - /** - * Set columns to be fetched from the database. - * - * This method is intended to be overwritten by derived classes in order to - * add additional columns to be fetched from the database. Please do not - * forget to call the parent when overwriting this method. - * - * @param \Doctrine\DBAL\Query\QueryBuilder $queryBuilder - * @param int $fieldId - * @param int $versionNo - */ - protected function setFetchColumns(QueryBuilder $queryBuilder, $fieldId, $versionNo) - { - $queryBuilder->select( - $this->connection->quoteIdentifier('filename'), - $this->connection->quoteIdentifier('mime_type'), - $this->connection->quoteIdentifier('original_filename') - ); - } - - /** - * Set the required insert columns to insert query builder. - * - * This method is intended to be overwritten by derived classes in order to - * add additional columns to be set in the database. Please do not forget - * to call the parent when overwriting this method. - * - * @param \Doctrine\DBAL\Query\QueryBuilder $queryBuilder - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - */ - protected function setInsertColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field) - { - $queryBuilder - ->setValue('contentobject_attribute_id', ':fieldId') - ->setValue('filename', ':filename') - ->setValue('mime_type', ':mimeType') - ->setValue('original_filename', ':originalFilename') - ->setValue('version', ':versionNo') - ->setParameter(':fieldId', $field->id, PDO::PARAM_INT) - ->setParameter(':filename', $this->removeMimeFromPath($field->value->externalData['id'])) - ->setParameter(':mimeType', $field->value->externalData['mimeType']) - ->setParameter(':originalFilename', $field->value->externalData['fileName']) - ->setParameter(':versionNo', $versionInfo->versionNo, PDO::PARAM_INT) - ; - } - - /** - * @param \Doctrine\DBAL\Query\QueryBuilder $queryBuilder - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - */ - protected function setUpdateColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field) - { - $queryBuilder - ->set('contentobject_attribute_id', ':fieldId') - ->set('filename', ':filename') - ->set('mime_type', ':mimeType') - ->set('original_filename', ':originalFilename') - ->set('version', ':versionNo') - ->setParameter(':fieldId', $field->id, ParameterType::INTEGER) - ->setParameter(':filename', $this->removeMimeFromPath($field->value->externalData['id'])) - ->setParameter(':mimeType', $field->value->externalData['mimeType']) - ->setParameter(':originalFilename', $field->value->externalData['fileName']) - ->setParameter(':versionNo', $versionInfo->versionNo, ParameterType::INTEGER) - ; - } - - /** - * Store the file reference in $field for $versionNo. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * - * @return bool - */ - public function storeFileReference(VersionInfo $versionInfo, Field $field) - { - $referencedData = $this->getFileReferenceData($field->id, $versionInfo->versionNo); - - if ($referencedData === null) { - $this->storeNewFieldData($versionInfo, $field); - } elseif (is_array($referencedData) && !empty(array_diff_assoc($referencedData, $field->value->externalData))) { - $this->updateFieldData($versionInfo, $field); - } - - return false; - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - */ - protected function updateFieldData(VersionInfo $versionInfo, Field $field) - { - $updateQuery = $this->connection->createQueryBuilder(); - $updateQuery->update( - $this->connection->quoteIdentifier($this->getStorageTable()) - ); - - $this->setUpdateColumns($updateQuery, $versionInfo, $field); - $updateQuery - ->where( - $updateQuery->expr()->andX( - $updateQuery->expr()->eq( - $this->connection->quoteIdentifier('contentobject_attribute_id'), - ':fieldId' - ), - $updateQuery->expr()->eq( - $this->connection->quoteIdentifier('version'), - ':versionNo' - ) - ) - ) - ->setParameter(':fieldId', $field->id, ParameterType::INTEGER) - ->setParameter(':versionNo', $versionInfo->versionNo, ParameterType::INTEGER) - ; - - $updateQuery->execute(); - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - */ - protected function storeNewFieldData(VersionInfo $versionInfo, Field $field) - { - $insertQuery = $this->connection->createQueryBuilder(); - $insertQuery->insert( - $this->connection->quoteIdentifier($this->getStorageTable()) - ); - - $this->setInsertColumns($insertQuery, $versionInfo, $field); - - $insertQuery->execute(); - } - - /** - * Remove the prepended mime-type directory from $path for legacy storage. - * - * @param string $path - * - * @return string - */ - public function removeMimeFromPath($path) - { - $path = (string)$path; - - return substr($path, strpos($path, '/') + 1); - } - - /** - * Return the file reference data for the given $fieldId in $versionNo. - * - * @param int $fieldId - * @param int $versionNo - * - * @return array|null - */ - public function getFileReferenceData($fieldId, $versionNo) - { - $selectQuery = $this->connection->createQueryBuilder(); - - $this->setFetchColumns($selectQuery, $fieldId, $versionNo); - - $selectQuery - ->from($this->connection->quoteIdentifier($this->getStorageTable())) - ->where( - $selectQuery->expr()->andX( - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('contentobject_attribute_id'), - ':fieldId' - ), - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('version'), - ':versionNo' - ) - ) - ) - ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) - ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) - ; - - $statement = $selectQuery->execute(); - - $result = $statement->fetchAll(PDO::FETCH_ASSOC); - - if (count($result) < 1) { - return null; - } - - $convertedResult = []; - foreach (reset($result) as $column => $value) { - $convertedResult[$this->toPropertyName($column)] = $this->castToPropertyValue($value, $column); - } - $convertedResult['id'] = $this->prependMimeToPath( - $convertedResult['id'], - $convertedResult['mimeType'] - ); - - return $convertedResult; - } - - /** - * Return the property name for the given $columnName. - * - * @param string $columnName - * - * @return string - */ - protected function toPropertyName($columnName) - { - $propertyMap = $this->getPropertyMapping(); - - return $propertyMap[$columnName]['name']; - } - - /** - * Return $value casted as specified by {@link getPropertyMapping()}. - * - * @param mixed $value - * @param string $columnName - * - * @return mixed - */ - protected function castToPropertyValue($value, $columnName) - { - $propertyMap = $this->getPropertyMapping(); - $castFunction = $propertyMap[$columnName]['cast']; - - return $castFunction($value); - } - - /** - * Prepend $path with the first part of the given $mimeType. - * - * @param string $path - * @param string $mimeType - * - * @return string - */ - public function prependMimeToPath($path, $mimeType) - { - $res = substr($mimeType, 0, strpos($mimeType, '/')) . '/' . $path; - - return $res; - } - - /** - * Remove all file references for the given $fieldIds. - * - * @param array $fieldIds - * @param int $versionNo - */ - public function removeFileReferences(array $fieldIds, $versionNo) - { - if (empty($fieldIds)) { - return; - } - - $deleteQuery = $this->connection->createQueryBuilder(); - $deleteQuery - ->delete($this->connection->quoteIdentifier($this->getStorageTable())) - ->where( - $deleteQuery->expr()->andX( - $deleteQuery->expr()->in( - $this->connection->quoteIdentifier('contentobject_attribute_id'), - ':fieldIds' - ), - $deleteQuery->expr()->eq( - $this->connection->quoteIdentifier('version'), - ':versionNo' - ) - ) - ) - ->setParameter(':fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY) - ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) - ; - - $deleteQuery->execute(); - } - - /** - * Remove a specific file reference for $fieldId and $versionId. - * - * @param int $fieldId - * @param int $versionNo - */ - public function removeFileReference($fieldId, $versionNo) - { - $deleteQuery = $this->connection->createQueryBuilder(); - $deleteQuery - ->delete($this->connection->quoteIdentifier($this->getStorageTable())) - ->where( - $deleteQuery->expr()->andX( - $deleteQuery->expr()->eq( - $this->connection->quoteIdentifier('contentobject_attribute_id'), - ':fieldId' - ), - $deleteQuery->expr()->eq( - $this->connection->quoteIdentifier('version'), - ':versionNo' - ) - ) - ) - ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) - ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) - ; - - $deleteQuery->execute(); - } - - /** - * Return a set o file references, referenced by the given $fieldIds. - * - * @param array $fieldIds - * - * @return array - */ - public function getReferencedFiles(array $fieldIds, $versionNo) - { - if (empty($fieldIds)) { - return []; - } - - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery - ->select( - $this->connection->quoteIdentifier('filename'), - $this->connection->quoteIdentifier('mime_type') - ) - ->from($this->connection->quoteIdentifier($this->getStorageTable())) - ->where( - $selectQuery->expr()->andX( - $selectQuery->expr()->in( - $this->connection->quoteIdentifier('contentobject_attribute_id'), - ':fieldIds' - ), - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('version'), - ':versionNo' - ) - ) - ) - ->setParameter(':fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY) - ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) - ; - - $statement = $selectQuery->execute(); - - return array_map( - function ($row) { - return $this->prependMimeToPath($row['filename'], $row['mime_type']); - }, - $statement->fetchAll(PDO::FETCH_ASSOC) - ); - } - - /** - * Return a map with the number of references each file from $files has. - * - * @param array $files - * - * @return array - */ - public function countFileReferences(array $files) - { - if (empty($files)) { - return []; - } - - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery - ->select( - $this->connection->quoteIdentifier('filename'), - $this->connection->quoteIdentifier('mime_type'), - sprintf( - 'COUNT(%s) AS count', - $this->connection->quoteIdentifier('contentobject_attribute_id') - ) - ) - ->from($this->connection->quoteIdentifier($this->getStorageTable())) - ->where( - $selectQuery->expr()->in( - $this->connection->quoteIdentifier('filename'), - ':filenames' - ) - ) - ->groupBy( - $this->connection->quoteIdentifier('filename'), - $this->connection->quoteIdentifier('mime_type') - ) - ->setParameter( - ':filenames', - array_map( - [$this, 'removeMimeFromPath'], - $files - ), - Connection::PARAM_STR_ARRAY - ) - ; - - $statement = $selectQuery->execute(); - - $countMap = []; - foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $row) { - $path = $this->prependMimeToPath($row['filename'], $row['mime_type']); - $countMap[$path] = (int)$row['count']; - } - - // Complete counts - foreach ($files as $path) { - // This is already the correct path - if (!isset($countMap[$path])) { - $countMap[$path] = 0; - } - } - - return $countMap; - } -} diff --git a/eZ/Publish/Core/FieldType/BinaryBase/PathGenerator/LegacyPathGenerator.php b/eZ/Publish/Core/FieldType/BinaryBase/PathGenerator/LegacyPathGenerator.php deleted file mode 100644 index 056c025ece..0000000000 --- a/eZ/Publish/Core/FieldType/BinaryBase/PathGenerator/LegacyPathGenerator.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\BinaryBase\PathGenerator; - -use eZ\Publish\SPI\FieldType\BinaryBase\PathGenerator; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -class LegacyPathGenerator extends PathGenerator -{ - public function getStoragePathForField(Field $field, VersionInfo $versionInfo) - { - $extension = pathinfo($field->value->externalData['fileName'], PATHINFO_EXTENSION); - - return $this->getFirstPartOfMimeType($field->value->externalData['mimeType']) - . '/' . bin2hex(random_bytes(16)) - . (!empty($extension) ? '.' . $extension : ''); - } - - /** - * Extracts the first part (before the '/') from the given $mimeType. - * - * @param string $mimeType - * - * @return string - */ - protected function getFirstPartOfMimeType($mimeType) - { - return substr($mimeType, 0, strpos($mimeType, '/')); - } -} diff --git a/eZ/Publish/Core/FieldType/BinaryBase/Type.php b/eZ/Publish/Core/FieldType/BinaryBase/Type.php deleted file mode 100644 index 5ed00f4ba3..0000000000 --- a/eZ/Publish/Core/FieldType/BinaryBase/Type.php +++ /dev/null @@ -1,401 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\BinaryBase; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\Media\Value; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\BinaryBase\RouteAwarePathGenerator; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue as PersistenceValue; - -/** - * Base FileType class for Binary field types (i.e. BinaryBase & Media). - */ -abstract class Type extends FieldType -{ - /** - * @see \eZ\Publish\Core\FieldType\FieldType::$validatorConfigurationSchema - */ - protected $validatorConfigurationSchema = [ - 'FileSizeValidator' => [ - 'maxFileSize' => [ - 'type' => 'int', - 'default' => null, - ], - ], - ]; - - /** @var \eZ\Publish\Core\FieldType\Validator[] */ - private $validators; - - /** @var \eZ\Publish\SPI\FieldType\BinaryBase\RouteAwarePathGenerator|null */ - protected $routeAwarePathGenerator; - - /** - * @param \eZ\Publish\Core\FieldType\Validator[] $validators - */ - public function __construct(array $validators, ?RouteAwarePathGenerator $routeAwarePathGenerator = null) - { - $this->validators = $validators; - $this->routeAwarePathGenerator = $routeAwarePathGenerator; - } - - /** - * Creates a specific value of the derived class from $inputValue. - * - * @param array $inputValue - * - * @return \eZ\Publish\Core\FieldType\Media\Value - */ - abstract protected function createValue(array $inputValue); - - final protected function regenerateUri(array $inputValue): array - { - if (isset($this->routeAwarePathGenerator, $inputValue['route'])) { - $inputValue['uri'] = $this->routeAwarePathGenerator->generate( - $inputValue['route'], - $inputValue['route_parameters'] ?? [] - ); - } - - unset($inputValue['route'], $inputValue['route_parameters']); - - return $inputValue; - } - - /** - * @param \eZ\Publish\Core\FieldType\BinaryBase\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return (string)$value->fileName; - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param string|array|\eZ\Publish\Core\FieldType\BinaryBase\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\BinaryBase\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - // construction only from path - if (is_string($inputValue)) { - $inputValue = ['inputUri' => $inputValue]; - } - - // default construction from array - if (is_array($inputValue)) { - $inputValue = $this->createValue($inputValue); - } - - $this->completeValue($inputValue); - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\BinaryBase\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - // Input file URI, if set needs to point to existing file - if (isset($value->inputUri)) { - if (!file_exists($value->inputUri)) { - throw new InvalidArgumentValue( - '$value->inputUri', - $value->inputUri, - static::class - ); - } - } elseif (!isset($value->id)) { - throw new InvalidArgumentValue( - '$value->id', - $value->id, - static::class - ); - } - - // Required parameter $fileName - if (!isset($value->fileName) || !is_string($value->fileName)) { - throw new InvalidArgumentValue( - '$value->fileName', - $value->fileName, - static::class - ); - } - - // Optional parameter $fileSize - if (isset($value->fileSize) && !is_int($value->fileSize)) { - throw new InvalidArgumentValue( - '$value->fileSize', - $value->fileSize, - static::class - ); - } - } - - /** - * Attempts to complete the data in $value. - * - * @param \eZ\Publish\Core\FieldType\BinaryBase\Value|\eZ\Publish\Core\FieldType\Value $value - */ - protected function completeValue(BaseValue $value) - { - if (!isset($value->inputUri) || !file_exists($value->inputUri)) { - return; - } - - if (!isset($value->fileName)) { - // @todo this may not always work... - $value->fileName = basename($value->inputUri); - } - - if (!isset($value->fileSize)) { - $value->fileSize = filesize($value->inputUri); - } - } - - /** - * BinaryBase does not support sorting, yet. - * - * @param \eZ\Publish\Core\FieldType\BinaryBase\Value $value - * - * @return mixed - */ - protected function getSortInfo(BaseValue $value) - { - return false; - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\BinaryBase\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - return $this->createValue($hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\BinaryBase\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - return [ - 'id' => $value->id, - // Kept for BC with eZ Publish 5.0 (EZP-20948, EZP-22808) - 'path' => $value->inputUri, - 'inputUri' => $value->inputUri, - 'fileName' => $value->fileName, - 'fileSize' => $value->fileSize, - 'mimeType' => $value->mimeType, - 'uri' => $value->uri, - ]; - } - - /** - * Converts a $value to a persistence value. - * - * In this method the field type puts the data which is stored in the field of content in the repository - * into the property FieldValue::data. The format of $data is a primitive, an array (map) or an object, which - * is then canonically converted to e.g. json/xml structures by future storage engines without - * further conversions. For mapping the $data to the legacy database an appropriate Converter - * (implementing eZ\Publish\Core\Persistence\Legacy\FieldValue\Converter) has implemented for the field - * type. Note: $data should only hold data which is actually stored in the field. It must not - * hold data which is stored externally. - * - * The $externalData property in the FieldValue is used for storing data externally by the - * FieldStorage interface method storeFieldData. - * - * The FieldValuer::sortKey is build by the field type for using by sort operations. - * - * @see \eZ\Publish\SPI\Persistence\Content\FieldValue - * - * @param \eZ\Publish\Core\FieldType\BinaryBase\Value $value The value of the field type - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue the value processed by the storage engine - */ - public function toPersistenceValue(SPIValue $value) - { - // Store original data as external (to indicate they need to be stored) - return new PersistenceValue( - [ - 'data' => null, - 'externalData' => $this->toHash($value), - 'sortKey' => $this->getSortInfo($value), - ] - ); - } - - /** - * Converts a persistence $fieldValue to a Value. - * - * This method builds a field type value from the $data and $externalData properties. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - * - * @return \eZ\Publish\Core\FieldType\BinaryBase\Value - */ - public function fromPersistenceValue(PersistenceValue $fieldValue) - { - // Restored data comes in $data, since it has already been processed - // there might be more data in the persistence value than needed here - $hash = [ - 'id' => $fieldValue->externalData['id'] ?? null, - 'fileName' => $fieldValue->externalData['fileName'] ?? null, - 'fileSize' => $fieldValue->externalData['fileSize'] ?? null, - 'mimeType' => $fieldValue->externalData['mimeType'] ?? null, - 'uri' => $fieldValue->externalData['uri'] ?? null, - ]; - - if (isset($fieldValue->externalData['route'])) { - $hash['route'] = $fieldValue->externalData['route']; - } - - if (isset($fieldValue->externalData['route_parameters'])) { - $hash['route_parameters'] = $fieldValue->externalData['route_parameters']; - } - - return $this->fromHash($hash); - } - - /** - * Validates a field based on the validators in the field definition. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field - * @param \eZ\Publish\Core\FieldType\BinaryBase\Value $fieldValue The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) - { - $errors = []; - - if ($this->isEmptyValue($fieldValue)) { - return $errors; - } - - foreach ($this->validators as $externalValidator) { - if (!$externalValidator->validate($fieldValue)) { - $errors = array_merge($errors, $externalValidator->getMessage()); - } - } - - foreach ((array)$fieldDefinition->getValidatorConfiguration() as $validatorIdentifier => $parameters) { - switch ($validatorIdentifier) { - // @todo There is a risk if we rely on a user built Value, since the FileSize - // property can be set manually, making this validation pointless - case 'FileSizeValidator': - if (empty($parameters['maxFileSize'])) { - // No file size limit - break; - } - // Database stores maxFileSize in MB - if (($parameters['maxFileSize'] * 1024 * 1024) < $fieldValue->fileSize) { - $errors[] = new ValidationError( - 'The file size cannot exceed %size% megabyte.', - 'The file size cannot exceed %size% megabytes.', - [ - '%size%' => $parameters['maxFileSize'], - ], - 'fileSize' - ); - } - break; - } - } - - return $errors; - } - - /** - * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $validatorConfiguration - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateValidatorConfiguration($validatorConfiguration) - { - $validationErrors = []; - - foreach ($validatorConfiguration as $validatorIdentifier => $parameters) { - switch ($validatorIdentifier) { - case 'FileSizeValidator': - if (!array_key_exists('maxFileSize', $parameters)) { - $validationErrors[] = new ValidationError( - 'Validator %validator% expects parameter %parameter% to be set.', - null, - [ - '%validator%' => $validatorIdentifier, - '%parameter%' => 'maxFileSize', - ], - "[$validatorIdentifier][maxFileSize]" - ); - break; - } - if (!is_int($parameters['maxFileSize']) && $parameters['maxFileSize'] !== null) { - $validationErrors[] = new ValidationError( - 'Validator %validator% expects parameter %parameter% to be of %type%.', - null, - [ - '%validator%' => $validatorIdentifier, - '%parameter%' => 'maxFileSize', - '%type%' => 'integer', - "[$validatorIdentifier][maxFileSize]", - ] - ); - } - break; - default: - $validationErrors[] = new ValidationError( - "Validator '%validator%' is unknown", - null, - [ - '%validator%' => $validatorIdentifier, - ], - "[$validatorIdentifier]" - ); - } - } - - return $validationErrors; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } -} diff --git a/eZ/Publish/Core/FieldType/BinaryBase/Value.php b/eZ/Publish/Core/FieldType/BinaryBase/Value.php deleted file mode 100644 index 281d40b156..0000000000 --- a/eZ/Publish/Core/FieldType/BinaryBase/Value.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\BinaryBase; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Base value for binary field types. - * - * @property string $path Used for BC with 5.0 (EZP-20948). Equivalent to $id. - * @property-read string $id Unique file ID, set by storage. Read only since 5.3 (EZP-22808). - */ -abstract class Value extends BaseValue -{ - /** - * Unique file ID, set by storage. - * - * Since 5.3 this is not used for input, use self::$inputUri instead - * - * @var string|null - */ - protected $id; - - /** - * Input file URI, as a path to a file on a disk. - * - * @var string|null - */ - public $inputUri; - - /** - * Display file name. - * - * @var string|null - */ - public $fileName; - - /** - * Size of the image file. - * - * @var int|null - */ - public $fileSize; - - /** - * Mime type of the file. - * - * @var string|null - */ - public $mimeType; - - /** - * HTTP URI. - * - * @var string|null - */ - public $uri; - - /** - * Construct a new Value object. - * - * @param array $fileData - */ - public function __construct(array $fileData = []) - { - // BC with 5.0 (EZP-20948) - if (isset($fileData['path'])) { - $fileData['id'] = $fileData['path']; - unset($fileData['path']); - } - - // BC with 5.2 (EZP-22808) - if (isset($fileData['id']) && file_exists($fileData['id'])) { - $fileData['inputUri'] = $fileData['id']; - unset($fileData['id']); - } - - parent::__construct($fileData); - } - - /** - * Returns a string representation of the field value. - * - * @return string - */ - public function __toString() - { - return (string)$this->uri; - } - - public function __get($propertyName) - { - if ($propertyName === 'path') { - return $this->inputUri; - } - - return parent::__get($propertyName); - } - - public function __set($propertyName, $propertyValue) - { - // BC with 5.0 (EZP-20948) - if ($propertyName === 'path') { - $this->inputUri = $propertyValue; - } elseif ($propertyName === 'id' && file_exists($propertyValue)) { // BC with 5.2 (EZP-22808) - $this->inputUri = $propertyValue; - } else { - parent::__set($propertyName, $propertyValue); - } - } - - public function __isset($propertyName) - { - if ($propertyName === 'path') { - return true; - } - - return parent::__isset($propertyName); - } -} diff --git a/eZ/Publish/Core/FieldType/BinaryFile/BinaryFileStorage.php b/eZ/Publish/Core/FieldType/BinaryFile/BinaryFileStorage.php deleted file mode 100644 index 334d7c94e3..0000000000 --- a/eZ/Publish/Core/FieldType/BinaryFile/BinaryFileStorage.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\BinaryFile; - -use eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage; - -/** - * Description of BinaryFileStorage. - */ -class BinaryFileStorage extends BinaryBaseStorage -{ -} diff --git a/eZ/Publish/Core/FieldType/BinaryFile/BinaryFileStorage/Gateway/DoctrineStorage.php b/eZ/Publish/Core/FieldType/BinaryFile/BinaryFileStorage/Gateway/DoctrineStorage.php deleted file mode 100644 index d0adb7d42c..0000000000 --- a/eZ/Publish/Core/FieldType/BinaryFile/BinaryFileStorage/Gateway/DoctrineStorage.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\BinaryFile\BinaryFileStorage\Gateway; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage\Gateway\DoctrineStorage as BaseDoctrineStorage; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use PDO; - -/** - * Binary File Field Type external storage DoctrineStorage gateway. - */ -class DoctrineStorage extends BaseDoctrineStorage -{ - /** - * {@inheritdoc} - */ - protected function getStorageTable() - { - return 'ezbinaryfile'; - } - - /** - * {@inheritdoc} - */ - protected function getPropertyMapping() - { - $propertyMap = parent::getPropertyMapping(); - $propertyMap['download_count'] = [ - 'name' => 'downloadCount', - 'cast' => 'intval', - ]; - - return $propertyMap; - } - - /** - * {@inheritdoc} - */ - protected function setFetchColumns(QueryBuilder $queryBuilder, $fieldId, $versionNo) - { - parent::setFetchColumns($queryBuilder, $fieldId, $versionNo); - - $queryBuilder->addSelect( - $this->connection->quoteIdentifier('download_count') - ); - } - - /** - * {@inheritdoc} - */ - protected function setInsertColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field) - { - parent::setInsertColumns($queryBuilder, $versionInfo, $field); - - $queryBuilder - ->setValue('download_count', ':downloadCount') - ->setParameter( - ':downloadCount', - $field->value->externalData['downloadCount'], - PDO::PARAM_INT - ) - ; - } -} diff --git a/eZ/Publish/Core/FieldType/BinaryFile/SearchField.php b/eZ/Publish/Core/FieldType/BinaryFile/SearchField.php deleted file mode 100644 index 7d53a7732d..0000000000 --- a/eZ/Publish/Core/FieldType/BinaryFile/SearchField.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\BinaryFile; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for BinaryFile field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'file_name', - $field->value->externalData['fileName'] ?? null, - new Search\FieldType\StringField() - ), - new Search\Field( - 'file_size', - $field->value->externalData['fileSize'] ?? null, - new Search\FieldType\IntegerField() - ), - new Search\Field( - 'mime_type', - $field->value->externalData['mimeType'] ?? null, - new Search\FieldType\StringField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'file_name' => new Search\FieldType\StringField(), - 'file_size' => new Search\FieldType\IntegerField(), - 'mime_type' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'file_name'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/BinaryFile/Type.php b/eZ/Publish/Core/FieldType/BinaryFile/Type.php deleted file mode 100644 index d8ab1a2a52..0000000000 --- a/eZ/Publish/Core/FieldType/BinaryFile/Type.php +++ /dev/null @@ -1,113 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\BinaryFile; - -use eZ\Publish\Core\FieldType\BinaryBase\Type as BinaryBaseType; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; - -/** - * The TextLine field type. - * - * This field type represents a simple string. - */ -class Type extends BinaryBaseType -{ - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezbinaryfile'; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\BinaryFile\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Creates a specific value of the derived class from $inputValue. - * - * @param array $inputValue - * - * @return \eZ\Publish\Core\FieldType\BinaryFile\Value - */ - protected function createValue(array $inputValue) - { - $inputValue = $this->regenerateUri($inputValue); - - return new Value($inputValue); - } - - /** - * Attempts to complete the data in $value. - * - * @param \eZ\Publish\Core\FieldType\BinaryFile\Value|\eZ\Publish\Core\FieldType\Value $value - */ - protected function completeValue(Basevalue $value) - { - parent::completeValue($value); - - if (isset($value->downloadCount) && $value->downloadCount === null) { - $value->downloadCount = 0; - } - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\BinaryFile\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - $hash = parent::toHash($value); - - $hash['downloadCount'] = $value->downloadCount; - - return $hash; - } - - /** - * Converts a persistence $fieldValue to a Value. - * - * This method builds a field type value from the $data and $externalData properties. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - * - * @return \eZ\Publish\Core\FieldType\BinaryFile\Value - */ - public function fromPersistenceValue(FieldValue $fieldValue) - { - if ($fieldValue->externalData === null) { - return $this->getEmptyValue(); - } - - $result = parent::fromPersistenceValue($fieldValue); - - $result->downloadCount = (isset($fieldValue->externalData['downloadCount']) - ? $fieldValue->externalData['downloadCount'] - : 0); - - return $result; - } -} diff --git a/eZ/Publish/Core/FieldType/BinaryFile/Value.php b/eZ/Publish/Core/FieldType/BinaryFile/Value.php deleted file mode 100644 index c2a128d5ec..0000000000 --- a/eZ/Publish/Core/FieldType/BinaryFile/Value.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\BinaryFile; - -use eZ\Publish\Core\FieldType\BinaryBase\Value as BaseValue; - -/** - * Value for BinaryFile field type. - */ -class Value extends BaseValue -{ - /** - * Number of times the file has been downloaded through content/download module. - * - * @var int - */ - public $downloadCount = 0; -} diff --git a/eZ/Publish/Core/FieldType/Checkbox/SearchField.php b/eZ/Publish/Core/FieldType/Checkbox/SearchField.php deleted file mode 100644 index 9e8cedf56e..0000000000 --- a/eZ/Publish/Core/FieldType/Checkbox/SearchField.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Checkbox; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for Checkbox field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value', - $field->value->data, - new Search\FieldType\BooleanField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value' => new Search\FieldType\BooleanField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/Checkbox/Type.php b/eZ/Publish/Core/FieldType/Checkbox/Type.php deleted file mode 100644 index 1d6be8ae16..0000000000 --- a/eZ/Publish/Core/FieldType/Checkbox/Type.php +++ /dev/null @@ -1,143 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Checkbox; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -/** - * Checkbox field type. - * - * Represent boolean values. - */ -class Type extends FieldType -{ - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezboolean'; - } - - /** - * @param \eZ\Publish\Core\FieldType\Checkbox\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return $value->bool ? '1' : '0'; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Checkbox\Value - */ - public function getEmptyValue() - { - return new Value(false); - } - - public function isEmptyValue(SPIValue $value): bool - { - return false; - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param bool|\eZ\Publish\Core\FieldType\Checkbox\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\Checkbox\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_bool($inputValue)) { - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Checkbox\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!$value instanceof Value) { - throw new InvalidArgumentType( - '$value', - 'eZ\\Publish\\Core\\FieldType\\Checkbox\\Value', - $value - ); - } - - if (!is_bool($value->bool)) { - throw new InvalidArgumentType( - '$value->bool', - 'boolean', - $value->bool - ); - } - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * - * @param \eZ\Publish\Core\FieldType\Checkbox\Value $value - * - * @return int - */ - protected function getSortInfo(BaseValue $value) - { - return (int)$value->bool; - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\Checkbox\Value $value - */ - public function fromHash($hash) - { - return new Value($hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Checkbox\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - return $value->bool; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } -} diff --git a/eZ/Publish/Core/FieldType/Checkbox/Value.php b/eZ/Publish/Core/FieldType/Checkbox/Value.php deleted file mode 100644 index 57013ef318..0000000000 --- a/eZ/Publish/Core/FieldType/Checkbox/Value.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Checkbox; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for Checkbox field type. - */ -class Value extends BaseValue -{ - /** - * Boolean value. - * - * @var bool - */ - public $bool; - - /** - * Construct a new Value object and initialize it $boolValue. - * - * @param bool $boolValue - */ - public function __construct($boolValue = false) - { - $this->bool = $boolValue; - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - * - * @return string - */ - public function __toString() - { - return $this->bool ? '1' : '0'; - } -} diff --git a/eZ/Publish/Core/FieldType/Country/Exception/InvalidValue.php b/eZ/Publish/Core/FieldType/Country/Exception/InvalidValue.php deleted file mode 100644 index b45b2a0167..0000000000 --- a/eZ/Publish/Core/FieldType/Country/Exception/InvalidValue.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Country\Exception; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; - -/** - * Exception thrown if an invalid identifier is used for a country. - */ -class InvalidValue extends InvalidArgumentException -{ - /** - * Creates a new exception when $value is invalid. - * - * @param mixed $value - */ - public function __construct($value) - { - parent::__construct('$value', "'" . var_export($value, true) . "' is not a valid country identifier"); - } -} diff --git a/eZ/Publish/Core/FieldType/Country/SearchField.php b/eZ/Publish/Core/FieldType/Country/SearchField.php deleted file mode 100644 index 944ade8fd5..0000000000 --- a/eZ/Publish/Core/FieldType/Country/SearchField.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Country; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for Country field type. - */ -class SearchField implements Indexable -{ - /** @var array */ - protected $countriesInfo; - - /** - * @param array $countriesInfo Array of countries data - */ - public function __construct(array $countriesInfo) - { - $this->countriesInfo = $countriesInfo; - } - - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - if (empty($field->value->data)) { - return []; - } - - $nameList = []; - $IDCList = []; - $alpha2List = []; - $alpha3List = []; - - foreach ($field->value->data as $alpha2) { - if (isset($this->countriesInfo[$alpha2])) { - $nameList[] = $this->countriesInfo[$alpha2]['Name']; - $IDCList[] = $this->countriesInfo[$alpha2]['IDC']; - $alpha2List[] = $this->countriesInfo[$alpha2]['Alpha2']; - $alpha3List[] = $this->countriesInfo[$alpha2]['Alpha3']; - } - } - - return [ - new Search\Field( - 'idc', - $IDCList, - new Search\FieldType\MultipleIntegerField() - ), - new Search\Field( - 'alpha2', - $alpha2List, - new Search\FieldType\MultipleStringField() - ), - new Search\Field( - 'alpha3', - $alpha3List, - new Search\FieldType\MultipleStringField() - ), - new Search\Field( - 'name', - $nameList, - new Search\FieldType\MultipleStringField() - ), - new Search\Field( - 'sort_value', - $field->value->sortKey, - new Search\FieldType\StringField() - ), - new Search\Field( - 'fulltext', - $nameList, - new Search\FieldType\FullTextField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'idc' => new Search\FieldType\MultipleIntegerField(), - 'alpha2' => new Search\FieldType\MultipleStringField(), - 'alpha3' => new Search\FieldType\MultipleStringField(), - 'name' => new Search\FieldType\MultipleStringField(), - 'sort_value' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'name'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return 'sort_value'; - } -} diff --git a/eZ/Publish/Core/FieldType/Country/Type.php b/eZ/Publish/Core/FieldType/Country/Type.php deleted file mode 100644 index fa8055ef62..0000000000 --- a/eZ/Publish/Core/FieldType/Country/Type.php +++ /dev/null @@ -1,267 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Country; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\Country\Exception\InvalidValue; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -/** - * The Country field type. - * - * This field type represents a simple string. - */ -class Type extends FieldType -{ - protected $settingsSchema = [ - 'isMultiple' => [ - 'type' => 'boolean', - 'default' => false, - ], - ]; - - /** @var array */ - protected $countriesInfo; - - /** - * @param array $countriesInfo Array of countries data - */ - public function __construct(array $countriesInfo) - { - $this->countriesInfo = $countriesInfo; - } - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezcountry'; - } - - /** - * @param \eZ\Publish\Core\FieldType\Country\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return (string)$value; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Country\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param array|\eZ\Publish\Core\FieldType\Country\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\Country\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_array($inputValue)) { - $inputValue = $this->fromHash($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Country\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!is_array($value->countries)) { - throw new InvalidArgumentType( - '$value->countries', - 'array', - $value->countries - ); - } - } - - /** - * Validates field value against 'isMultiple' setting. - * - * Does not use validators. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field - * @param \eZ\Publish\Core\FieldType\Country\Value $fieldValue The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) - { - $validationErrors = []; - - if ($this->isEmptyValue($fieldValue)) { - return $validationErrors; - } - - $fieldSettings = $fieldDefinition->getFieldSettings(); - - if ((!isset($fieldSettings['isMultiple']) || $fieldSettings['isMultiple'] === false) - && count($fieldValue->countries) > 1) { - $validationErrors[] = new ValidationError( - 'Field definition does not allow multiple countries to be selected.', - null, - [], - 'countries' - ); - } - - foreach ($fieldValue->countries as $alpha2 => $countryInfo) { - if (!isset($this->countriesInfo[$alpha2])) { - $validationErrors[] = new ValidationError( - "Country with Alpha2 code '%alpha2%' is not defined in FieldType settings.", - null, - [ - '%alpha2%' => $alpha2, - ], - 'countries' - ); - } - } - - return $validationErrors; - } - - /** - * {@inheritdoc} - */ - protected function getSortInfo(BaseValue $value) - { - $countries = []; - foreach ($value->countries as $countryInfo) { - $countries[] = $this->transformationProcessor->transformByGroup($countryInfo['Name'], 'lowercase'); - } - - sort($countries); - - return implode(',', $countries); - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\Country\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - $countries = []; - foreach ($hash as $country) { - foreach ($this->countriesInfo as $countryInfo) { - switch ($country) { - case $countryInfo['Name']: - case $countryInfo['Alpha2']: - case $countryInfo['Alpha3']: - $countries[$countryInfo['Alpha2']] = $countryInfo; - continue 3; - } - } - - throw new InvalidValue($country); - } - - return new Value($countries); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Country\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - return array_keys($value->countries); - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } - - /** - * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings) - { - $validationErrors = []; - - foreach ($fieldSettings as $name => $value) { - if (!isset($this->settingsSchema[$name])) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' is unknown", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - continue; - } - - switch ($name) { - case 'isMultiple': - if (!is_bool($value)) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' value must be of boolean type", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - break; - } - } - - return $validationErrors; - } -} diff --git a/eZ/Publish/Core/FieldType/Country/Value.php b/eZ/Publish/Core/FieldType/Country/Value.php deleted file mode 100644 index b262bf6109..0000000000 --- a/eZ/Publish/Core/FieldType/Country/Value.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Country; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for Country field type. - */ -class Value extends BaseValue -{ - /** - * Associative array with Alpha2 codes as keys and countries data as values. - * - * Example: - * <code> - * array( - * "JP" => array( - * "Name" => "Japan", - * "Alpha2" => "JP", - * "Alpha3" => "JPN", - * "IDC" => 81 - * ) - * ) - * </code> - * - * @var array[] - */ - public $countries = []; - - /** - * Construct a new Value object and initialize it with given $data. - * - * @param array[] $countries - */ - public function __construct(array $countries = []) - { - $this->countries = $countries; - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - return implode(', ', array_column($this->countries, 'Name')); - } -} diff --git a/eZ/Publish/Core/FieldType/Date/SearchField.php b/eZ/Publish/Core/FieldType/Date/SearchField.php deleted file mode 100644 index a54522c347..0000000000 --- a/eZ/Publish/Core/FieldType/Date/SearchField.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Date; - -use DateTime; -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for Date field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - if ($field->value->data !== null) { - $dateTime = new DateTime("@{$field->value->data['timestamp']}"); - - $value = $dateTime->format('Y-m-d\\Z'); - } else { - $value = null; - } - - return [ - new Search\Field( - 'value', - $value, - new Search\FieldType\DateField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value' => new Search\FieldType\DateField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/Date/Type.php b/eZ/Publish/Core/FieldType/Date/Type.php deleted file mode 100644 index 3dbba5d707..0000000000 --- a/eZ/Publish/Core/FieldType/Date/Type.php +++ /dev/null @@ -1,237 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Date; - -use DateTime; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -class Type extends FieldType -{ - /** - * Default value type empty. - */ - public const DEFAULT_EMPTY = 0; - - /** - * Default value type current date. - */ - public const DEFAULT_CURRENT_DATE = 1; - - protected $settingsSchema = [ - // One of the DEFAULT_* class constants - 'defaultType' => [ - 'type' => 'choice', - 'default' => self::DEFAULT_EMPTY, - ], - ]; - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezdate'; - } - - /** - * @param \eZ\Publish\Core\FieldType\Date\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - if ($this->isEmptyValue($value)) { - return ''; - } - - return $value->date->format('l d F Y'); - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Date\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param string|int|\DateTime|\eZ\Publish\Core\FieldType\Date\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\Date\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_string($inputValue)) { - $inputValue = Value::fromString($inputValue); - } - - if (is_int($inputValue)) { - $inputValue = Value::fromTimestamp($inputValue); - } - - if ($inputValue instanceof DateTime) { - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Date\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!$value->date instanceof DateTime) { - throw new InvalidArgumentType( - (string)$value->date, - 'DateTime', - $value->date - ); - } - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * - * @param \eZ\Publish\Core\FieldType\Date\Value $value - * - * @return mixed - */ - protected function getSortInfo(BaseValue $value) - { - if ($value->date === null) { - return null; - } - - return $value->date->getTimestamp(); - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash Null or associative array containing one of the following (first value found in the order below is picked): - * 'rfc850': Date in RFC 850 format (DateTime::RFC850) - * 'timestring': Date in parseable string format supported by DateTime (e.g. 'now', '+3 days') - * 'timestamp': Unix timestamp - * - * @return \eZ\Publish\Core\FieldType\Date\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - if (isset($hash['rfc850']) && $hash['rfc850']) { - return Value::fromString($hash['rfc850']); - } - - if (isset($hash['timestring']) && is_string($hash['timestring'])) { - return Value::fromString($hash['timestring']); - } - - return Value::fromTimestamp((int)$hash['timestamp']); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Date\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - if ($value->date instanceof DateTime) { - return [ - 'timestamp' => $value->date->getTimestamp(), - 'rfc850' => $value->date->format(DateTime::RFC850), - ]; - } - - return [ - 'timestamp' => 0, - 'rfc850' => null, - ]; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } - - /** - * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings) - { - $validationErrors = []; - - foreach ($fieldSettings as $name => $value) { - if (!isset($this->settingsSchema[$name])) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' is unknown", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - continue; - } - - switch ($name) { - case 'defaultType': - $definedTypes = [ - self::DEFAULT_EMPTY, - self::DEFAULT_CURRENT_DATE, - ]; - if (!in_array($value, $definedTypes, true)) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' is of unknown type", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - break; - } - } - - return $validationErrors; - } -} diff --git a/eZ/Publish/Core/FieldType/Date/Value.php b/eZ/Publish/Core/FieldType/Date/Value.php deleted file mode 100644 index a8bc25b81e..0000000000 --- a/eZ/Publish/Core/FieldType/Date/Value.php +++ /dev/null @@ -1,96 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Date; - -use DateTime; -use DateTimeZone; -use Exception; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for Date field type. - * Date should always be represented in UTC. - */ -class Value extends BaseValue -{ - /** - * Date content. - * - * @var \DateTime|null - */ - public $date; - - /** - * Date format to be used by {@link __toString()}. - * - * @var string - */ - public $stringFormat = 'l d F Y'; - - /** - * Construct a new Value object and initialize with $dateTime. - * - * @param \DateTime|null $dateTime Date as a DateTime object - */ - public function __construct(DateTime $dateTime = null) - { - if ($dateTime !== null) { - $dateTime = clone $dateTime; - $dateTime->setTime(0, 0, 0); - } - $this->date = $dateTime; - } - - /** - * Creates a Value from the given $dateString. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param string $dateString - * - * @return \eZ\Publish\Core\FieldType\Date\Value - */ - public static function fromString($dateString) - { - try { - return new static(new DateTime($dateString, new DateTimeZone('UTC'))); - } catch (Exception $e) { - throw new InvalidArgumentValue('$dateString', $dateString, __CLASS__, $e); - } - } - - /** - * Creates a Value from the given $timestamp. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param int $timestamp - * - * @return \eZ\Publish\Core\FieldType\Date\Value - */ - public static function fromTimestamp($timestamp) - { - try { - return new static(new DateTime("@{$timestamp}")); - } catch (Exception $e) { - throw new InvalidArgumentValue('$timestamp', $timestamp, __CLASS__, $e); - } - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - if (!$this->date instanceof DateTime) { - return ''; - } - - return $this->date->format($this->stringFormat); - } -} diff --git a/eZ/Publish/Core/FieldType/DateAndTime/SearchField.php b/eZ/Publish/Core/FieldType/DateAndTime/SearchField.php deleted file mode 100644 index 0a3f8d9f2f..0000000000 --- a/eZ/Publish/Core/FieldType/DateAndTime/SearchField.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\DateAndTime; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for DateAndTime field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value', - $field->value->data['timestamp'] ?? null, - new Search\FieldType\DateField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value' => new Search\FieldType\DateField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/DateAndTime/Type.php b/eZ/Publish/Core/FieldType/DateAndTime/Type.php deleted file mode 100644 index 2838d0c4d5..0000000000 --- a/eZ/Publish/Core/FieldType/DateAndTime/Type.php +++ /dev/null @@ -1,328 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\DateAndTime; - -use DateInterval; -use DateTime; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -class Type extends FieldType -{ - /** - * Default value types. - */ - public const DEFAULT_EMPTY = 0; - public const DEFAULT_CURRENT_DATE = 1; - public const DEFAULT_CURRENT_DATE_ADJUSTED = 2; - - protected $settingsSchema = [ - 'useSeconds' => [ - 'type' => 'bool', - 'default' => false, - ], - // One of the DEFAULT_* class constants - 'defaultType' => [ - 'type' => 'choice', - 'default' => self::DEFAULT_EMPTY, - ], - /* - * @var DateInterval - * Used only if defaultValueType is set to DEFAULT_CURRENT_DATE_ADJUSTED - */ - 'dateInterval' => [ - 'type' => 'dateInterval', - 'default' => null, - ], - ]; - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezdatetime'; - } - - /** - * @param \eZ\Publish\Core\FieldType\DateAndTime\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - if ($this->isEmptyValue($value)) { - return ''; - } - - return $value->value->format('D Y-d-m H:i:s'); - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\DateAndTime\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param string|int|\DateTime|\eZ\Publish\Core\FieldType\DateAndTime\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\DateAndTime\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_string($inputValue)) { - $inputValue = Value::fromString($inputValue); - } - - if (is_int($inputValue)) { - $inputValue = Value::fromTimestamp($inputValue); - } - - if ($inputValue instanceof DateTime) { - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\DateAndTime\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!$value->value instanceof DateTime) { - throw new InvalidArgumentType( - '$value->value', - 'DateTime', - $value->value - ); - } - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * - * @param \eZ\Publish\Core\FieldType\DateAndTime\Value $value - * - * @return int|null - */ - protected function getSortInfo(BaseValue $value) - { - if ($value->value === null) { - return null; - } - - return $value->value->getTimestamp(); - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash Null or associative array containing one of the following (first value found in the order below is picked): - * 'rfc850': Date in RFC 850 format (DateTime::RFC850) - * 'timestring': Date in parseable string format supported by DateTime (e.g. 'now', '+3 days') - * 'timestamp': Unix timestamp - * - * @return \eZ\Publish\Core\FieldType\DateAndTime\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - if (isset($hash['rfc850']) && $hash['rfc850']) { - return Value::fromString($hash['rfc850']); - } - - if (isset($hash['timestring']) && is_string($hash['timestring'])) { - return Value::fromString($hash['timestring']); - } - - return Value::fromTimestamp((int)$hash['timestamp']); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\DateAndTime\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - if ($value->value instanceof DateTime) { - return [ - 'timestamp' => $value->value->getTimestamp(), - 'rfc850' => $value->value->format(DateTime::RFC850), - ]; - } - - return [ - 'timestamp' => 0, - 'rfc850' => null, - ]; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } - - /** - * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings) - { - $validationErrors = []; - - foreach ($fieldSettings as $name => $value) { - if (isset($this->settingsSchema[$name])) { - switch ($name) { - case 'useSeconds': - if (!is_bool($value)) { - $validationErrors[] = new ValidationError( - "Setting 'Use seconds' value must be of boolean type", - null, - [], - "[$name]" - ); - } - break; - case 'defaultType': - $definedTypes = [ - self::DEFAULT_EMPTY, - self::DEFAULT_CURRENT_DATE, - self::DEFAULT_CURRENT_DATE_ADJUSTED, - ]; - if (!in_array($value, $definedTypes, true)) { - $validationErrors[] = new ValidationError( - "Setting 'Default value' is of unknown type", - null, - [], - "[$name]" - ); - } - break; - case 'dateInterval': - if (isset($value)) { - if ($value instanceof DateInterval) { - // String conversion of $value, because DateInterval objects cannot be compared directly - if ( - isset($fieldSettings['defaultType']) - && $fieldSettings['defaultType'] !== self::DEFAULT_CURRENT_DATE_ADJUSTED - && $value->format('%y%m%d%h%i%s') !== '000000' - ) { - $validationErrors[] = new ValidationError( - "Setting 'Current date and time adjusted by' can be used only when setting 'Default value' is set to 'Adjusted current datetime'", - null, - [], - "[$name]" - ); - } - } else { - $validationErrors[] = new ValidationError( - "Setting 'Current date and time adjusted by' value must be an instance of 'DateInterval' class", - null, - [], - "[$name]" - ); - } - } - break; - } - } else { - $validationErrors[] = new ValidationError( - "Setting '%setting%' is unknown", - null, - ['%setting%' => $name], - "[$name]" - ); - } - } - - return $validationErrors; - } - - /** - * Converts the given $fieldSettings to a simple hash format. - * - * This is the default implementation, which just returns the given - * $fieldSettings, assuming they are already in a hash format. Overwrite - * this in your specific implementation, if necessary. - * - * @param mixed $fieldSettings - * - * @return array|hash|scalar|null - */ - public function fieldSettingsToHash($fieldSettings) - { - $fieldSettingsHash = parent::fieldSettingsToHash($fieldSettings); - - if (isset($fieldSettingsHash['dateInterval'])) { - $fieldSettingsHash['dateInterval'] = $fieldSettingsHash['dateInterval']->format( - 'P%r%yY%r%mM%r%dDT%r%hH%iM%r%sS' - ); - } - - return $fieldSettingsHash; - } - - /** - * Converts the given $fieldSettingsHash to field settings of the type. - * - * This is the reverse operation of {@link fieldSettingsToHash()}. - * - * This is the default implementation, which just returns the given - * $fieldSettingsHash, assuming the supported field settings are already in - * a hash format. Overwrite this in your specific implementation, if - * necessary. - * - * @param array|hash|scalar|null $fieldSettingsHash - * - * @return mixed - */ - public function fieldSettingsFromHash($fieldSettingsHash) - { - $fieldSettings = parent::fieldSettingsFromHash($fieldSettingsHash); - - if (isset($fieldSettings['dateInterval'])) { - $fieldSettings['dateInterval'] = new DateInterval($fieldSettings['dateInterval']); - } - - return $fieldSettings; - } -} diff --git a/eZ/Publish/Core/FieldType/DateAndTime/Value.php b/eZ/Publish/Core/FieldType/DateAndTime/Value.php deleted file mode 100644 index e15e0a9423..0000000000 --- a/eZ/Publish/Core/FieldType/DateAndTime/Value.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\DateAndTime; - -use DateTime; -use Exception; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for DateAndTime field type. - */ -class Value extends BaseValue -{ - /** - * Date content. - * - * @var \DateTime|null - */ - public $value; - - /** - * Date format to be used by {@link __toString()}. - * - * @var string - */ - public $stringFormat = 'U'; - - /** - * Construct a new Value object and initialize with $dateTime. - * - * @param \DateTime|null $dateTime Date/Time as a DateTime object - */ - public function __construct(DateTime $dateTime = null) - { - $this->value = $dateTime; - } - - /** - * Creates a Value from the given $dateString. - * - * @param string $dateString - * - * @return \eZ\Publish\Core\FieldType\DateAndTime\Value - */ - public static function fromString($dateString) - { - try { - return new static(new DateTime($dateString)); - } catch (Exception $e) { - throw new InvalidArgumentValue('$dateString', $dateString, __CLASS__, $e); - } - } - - /** - * Creates a Value from the given $timestamp. - * - * @param int $timestamp - * - * @return \eZ\Publish\Core\FieldType\DateAndTime\Value - */ - public static function fromTimestamp($timestamp) - { - try { - return new static(new DateTime("@{$timestamp}")); - } catch (Exception $e) { - throw new InvalidArgumentValue('$timestamp', $timestamp, __CLASS__, $e); - } - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - if (!$this->value instanceof DateTime) { - return ''; - } - - return $this->value->format($this->stringFormat); - } -} diff --git a/eZ/Publish/Core/FieldType/EmailAddress/SearchField.php b/eZ/Publish/Core/FieldType/EmailAddress/SearchField.php deleted file mode 100644 index 2211ebb0ec..0000000000 --- a/eZ/Publish/Core/FieldType/EmailAddress/SearchField.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\EmailAddress; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for EmailAddress field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value', - $field->value->data, - new Search\FieldType\StringField() - ), - new Search\Field( - 'fulltext', - $field->value->data, - new Search\FieldType\FullTextField([ - 'space_normalize', - 'latin1_lowercase', - 'ascii_lowercase', - 'cyrillic_lowercase', - 'greek_lowercase', - 'latin_lowercase', - 'latin-exta_lowercase', - ], false) - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/EmailAddress/Type.php b/eZ/Publish/Core/FieldType/EmailAddress/Type.php deleted file mode 100644 index 29c4345bc0..0000000000 --- a/eZ/Publish/Core/FieldType/EmailAddress/Type.php +++ /dev/null @@ -1,208 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\EmailAddress; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Validator\EmailAddressValidator; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -/** - * The EMailAddress field type. - * - * This field type represents an email address. - */ -class Type extends FieldType -{ - protected $validatorConfigurationSchema = [ - 'EmailAddressValidator' => [], - ]; - - /** - * @param \eZ\Publish\Core\FieldType\EmailAddress\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return $this->transformationProcessor->transformByGroup((string)$value, 'lowercase'); - } - - /** - * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $validatorConfiguration - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateValidatorConfiguration($validatorConfiguration) - { - $validationErrors = []; - $validator = new EmailAddressValidator(); - - foreach ($validatorConfiguration as $validatorIdentifier => $constraints) { - if ($validatorIdentifier !== 'EmailAddressValidator') { - $validationErrors[] = new ValidationError( - "Validator '%validator%' is unknown", - null, - [ - '%validator%' => $validatorIdentifier, - ], - "[$validatorIdentifier]" - ); - continue; - } - $validationErrors += $validator->validateConstraints($constraints); - } - - return $validationErrors; - } - - /** - * Validates a field based on the validators in the field definition. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field - * @param \eZ\Publish\Core\FieldType\EmailAddress\Value $fieldValue The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) - { - $errors = []; - - if ($this->isEmptyValue($fieldValue)) { - return $errors; - } - - $validatorConfiguration = $fieldDefinition->getValidatorConfiguration(); - $constraints = isset($validatorConfiguration['EmailAddressValidator']) ? - $validatorConfiguration['EmailAddressValidator'] : - []; - $validator = new EmailAddressValidator(); - $validator->initializeWithConstraints($constraints); - - if (!$validator->validate($fieldValue)) { - return $validator->getMessage(); - } - - return []; - } - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezemail'; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\EmailAddress\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param string|\eZ\Publish\Core\FieldType\EmailAddress\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\EmailAddress\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_string($inputValue)) { - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\EmailAddress\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!is_string($value->email)) { - throw new InvalidArgumentType( - '$value->email', - 'string', - $value->email - ); - } - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * - * @todo String normalization should occur here. - * - * @param \eZ\Publish\Core\FieldType\EmailAddress\Value $value - * - * @return string - */ - protected function getSortInfo(BaseValue $value) - { - return $value->email; - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\EmailAddress\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - return new Value($hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\EmailAddress\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - return $value->email; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } -} diff --git a/eZ/Publish/Core/FieldType/EmailAddress/Value.php b/eZ/Publish/Core/FieldType/EmailAddress/Value.php deleted file mode 100644 index b969cfad2d..0000000000 --- a/eZ/Publish/Core/FieldType/EmailAddress/Value.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\EmailAddress; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for EMailAddress field type. - */ -class Value extends BaseValue -{ - /** - * Email address. - * - * @var string - */ - public $email; - - /** - * Construct a new Value object and initialize its $email. - * - * @param string $email - */ - public function __construct($email = '') - { - $this->email = $email; - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - return (string)$this->email; - } -} diff --git a/eZ/Publish/Core/FieldType/FieldSettings.php b/eZ/Publish/Core/FieldType/FieldSettings.php deleted file mode 100644 index 2ac2a6d595..0000000000 --- a/eZ/Publish/Core/FieldType/FieldSettings.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType; - -use ArrayObject; -use eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException; -use eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException; - -/** - * Container for field type specific properties. - * - * @internal - */ -class FieldSettings extends ArrayObject -{ - /** - * Only allows existing indexes to be updated. - * - * This is so that only settings specified by a field type can be set. - * - * @throws \eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException On non existing indexes - * - * @param string|int $index - * @param mixed $value - */ - public function offsetSet($index, $value): void - { - if (!parent::offsetExists($index)) { - throw new PropertyReadOnlyException($index, __CLASS__); - } - - parent::offsetSet($index, $value); - } - - /** - * Returns value from internal array, identified by $index. - * - * @param string $index - * - * @throws \eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException If $index is not found - * - * @return mixed - */ - #[\ReturnTypeWillChange] - public function offsetGet($index) - { - if (!parent::offsetExists($index)) { - throw new PropertyNotFoundException($index, __CLASS__); - } - - return parent::offsetGet($index); - } -} diff --git a/eZ/Publish/Core/FieldType/FieldType.php b/eZ/Publish/Core/FieldType/FieldType.php deleted file mode 100644 index 6815f5dfe6..0000000000 --- a/eZ/Publish/Core/FieldType/FieldType.php +++ /dev/null @@ -1,584 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\Persistence\TransformationProcessor; -use eZ\Publish\SPI\FieldType\Comparable; -use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue as PersistenceValue; - -/** - * Base class for field types, the most basic storage unit of data inside eZ Publish. - * - * All other field types extend FieldType providing the specific functionality - * desired in each case. - * - * The capabilities supported by each individual field type is decided by which - * interfaces the field type implements support for. These individual - * capabilities can also be checked via the supports*() methods. - * - * Field types are the base building blocks of Content Types, and serve as - * data containers for Content objects. Therefore, while field types can be used - * independently, they are designed to be used as a part of a Content object. - * - * Field types are primed and pre-configured with the Field Definitions found in - * Content Types. - */ -abstract class FieldType extends SPIFieldType implements Comparable -{ - /** - * The setting keys which are available on this field type. - * - * The key is the setting name, and the value is the default value for given - * setting, set to null if no particular default should be set. - * - * @var mixed - */ - protected $settingsSchema = []; - - /** - * The validator configuration schema. - * - * This is a base implementation, containing an empty array() that indicates - * that no validators are supported. Overwrite in derived types, if - * validation is supported. - * - * @see getValidatorConfigurationSchema() - * - * @var mixed - */ - protected $validatorConfigurationSchema = []; - - /** - * String transformation processor, used to normalize sort string as needed. - * - * @var \eZ\Publish\Core\Persistence\TransformationProcessor - */ - protected $transformationProcessor; - - /** - * @param \eZ\Publish\Core\Persistence\TransformationProcessor $transformationProcessor - */ - public function setTransformationProcessor(TransformationProcessor $transformationProcessor) - { - $this->transformationProcessor = $transformationProcessor; - } - - /** - * Returns a schema for the settings expected by the FieldType. - * - * This implementation returns an array. - * where the key is the setting name, and the value is the default value for given - * setting and set to null if no particular default should be set. - * - * @return mixed - */ - public function getSettingsSchema() - { - return $this->settingsSchema; - } - - /** - * Returns a schema for the validator configuration expected by the FieldType. - * - * @see FieldTypeInterface::getValidatorConfigurationSchema() - * - * This implementation returns a three dimensional map containing for each validator configuration - * referenced by identifier a map of supported parameters which are defined by a type and a default value - * (see example). - * - * <code> - * array( - * 'stringLength' => array( - * 'minStringLength' => array( - * 'type' => 'int', - * 'default' => 0, - * ), - * 'maxStringLength' => array( - * 'type' => 'int' - * 'default' => null, - * ) - * ), - * ); - * </code> - * - * @return mixed - */ - public function getValidatorConfigurationSchema() - { - return $this->validatorConfigurationSchema; - } - - /** - * Validates a field based on the validators in the field definition. - * - * This is a base implementation, returning an empty array() that indicates - * that no validation errors occurred. Overwrite in derived types, if - * validation is supported. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field - * @param \eZ\Publish\Core\FieldType\Value $value The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $value) - { - return []; - } - - /** - * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * This method expects that given $validatorConfiguration is complete, for this purpose method - * {@link self::applyDefaultValidatorConfiguration()} is provided. - * - * This is a base implementation, returning a validation error for each - * specified validator, since by default no validators are supported. - * Overwrite in derived types, if validation is supported. - * - * @param mixed $validatorConfiguration - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateValidatorConfiguration($validatorConfiguration) - { - $validationErrors = []; - - foreach ((array)$validatorConfiguration as $validatorIdentifier => $constraints) { - $validationErrors[] = new ValidationError( - "Validator '%validator%' is unknown", - null, - [ - 'validator' => $validatorIdentifier, - ], - "[$validatorIdentifier]" - ); - } - - return $validationErrors; - } - - /** - * Applies the default values to the given $validatorConfiguration of a FieldDefinitionCreateStruct. - * - * This is a base implementation, expecting best practice validator configuration format used by - * field types in standard eZ publish installation. Overwrite in derived types if needed. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param mixed $validatorConfiguration - */ - public function applyDefaultValidatorConfiguration(&$validatorConfiguration) - { - if ($validatorConfiguration !== null && !is_array($validatorConfiguration)) { - throw new InvalidArgumentType('$validatorConfiguration', 'array|null', $validatorConfiguration); - } - - foreach ($this->getValidatorConfigurationSchema() as $validatorName => $configurationSchema) { - // Set configuration of specific validator to empty array if it is not already provided - if (!isset($validatorConfiguration[$validatorName])) { - $validatorConfiguration[$validatorName] = []; - } - - foreach ($configurationSchema as $settingName => $settingConfiguration) { - // Check that a default entry exists in the configuration schema for the validator but that no value has been provided - if (!isset($validatorConfiguration[$validatorName][$settingName]) && array_key_exists('default', $settingConfiguration)) { - $validatorConfiguration[$validatorName][$settingName] = $settingConfiguration['default']; - } - } - } - } - - /** - * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * This method expects that given $fieldSettings are complete, for this purpose method - * {@link self::applyDefaultSettings()} is provided. - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings) - { - if (!empty($fieldSettings)) { - return [ - new ValidationError( - "FieldType '%fieldType%' does not accept settings", - null, - [ - 'fieldType' => $this->getFieldTypeIdentifier(), - ], - 'fieldType' - ), - ]; - } - - return []; - } - - /** - * Applies the default values to the fieldSettings of a FieldDefinitionCreateStruct. - * - * This is a base implementation, expecting best practice field settings format used by - * field types in standard eZ publish installation. Overwrite in derived types if needed. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param mixed $fieldSettings - */ - public function applyDefaultSettings(&$fieldSettings) - { - if ($fieldSettings !== null && !is_array($fieldSettings)) { - throw new InvalidArgumentType('$fieldSettings', 'array|null', $fieldSettings); - } - - foreach ($this->getSettingsSchema() as $settingName => $settingConfiguration) { - // Checking that a default entry exists in the settingsSchema but that no value has been provided - if (!array_key_exists($settingName, (array)$fieldSettings) && array_key_exists('default', $settingConfiguration)) { - $fieldSettings[$settingName] = $settingConfiguration['default']; - } - } - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * - * Return value is mixed. It should be something which is sensible for - * sorting. - * - * It is up to the persistence implementation to handle those values. - * Common string and integer values are safe. - * - * For the legacy storage it is up to the field converters to set this - * value in either sort_key_string or sort_key_int. - * - * In case of multi value, values should be string and separated by "-" or ",". - * - * @param \eZ\Publish\Core\FieldType\Value $value - * - * @return mixed - */ - protected function getSortInfo(Value $value) - { - return null; - } - - /** - * Converts a $value to a persistence value. - * - * @param \eZ\Publish\Core\FieldType\Value $value - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function toPersistenceValue(SPIValue $value) - { - // @todo Evaluate if creating the sortKey in every case is really needed - // Couldn't this be retrieved with a method, which would initialize - // that info on request only? - return new PersistenceValue( - [ - 'data' => $this->toHash($value), - 'externalData' => null, - 'sortKey' => $this->getSortInfo($value), - ] - ); - } - - /** - * Converts a persistence $fieldValue to a Value. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - * - * @return \eZ\Publish\Core\FieldType\Value - */ - public function fromPersistenceValue(PersistenceValue $fieldValue) - { - return $this->fromHash($fieldValue->data); - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return false; - } - - /** - * Indicates if the field definition of this type can appear only once in the same ContentType. - * - * @return bool - */ - public function isSingular() - { - return false; - } - - /** - * Indicates if the field definition of this type can be added to a ContentType with Content instances. - * - * @return bool - */ - public function onlyEmptyInstance() - { - return false; - } - - /** - * Returns if the given $value is considered empty by the field type. - * - * Default implementation, which performs a "==" check with the value - * returned by {@link getEmptyValue()}. Overwrite in the specific field - * type, if necessary. - * - * @param \eZ\Publish\Core\FieldType\Value $value - * - * @return bool - */ - public function isEmptyValue(SPIValue $value) - { - return $value == $this->getEmptyValue(); - } - - /** - * Potentially builds and checks the type and structure of the $inputValue. - * - * This method first inspects $inputValue and convert it into a dedicated - * value object. - * - * After that, the value is checked for structural validity. - * Note that this does not include validation after the rules - * from validators, but only plausibility checks for the general data - * format. - * - * Note that this method must also cope with the empty value for the field - * type as e.g. returned by {@link getEmptyValue()}. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the parameter is not of the supported value sub type - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the value does not match the expected structure - * - * @param mixed $inputValue - * - * @return \eZ\Publish\Core\FieldType\Value The potentially converted and structurally plausible value. - */ - final public function acceptValue($inputValue) - { - if ($inputValue === null) { - return $this->getEmptyValue(); - } - - $value = $this->createValueFromInput($inputValue); - - static::checkValueType($value); - - if ($this->isEmptyValue($value)) { - return $this->getEmptyValue(); - } - - $this->checkValueStructure($value); - - return $value; - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * If given $inputValue could not be converted or is already an instance of dedicate value object, - * the method should simply return it. - * - * This is an operation method for {@see acceptValue()}. - * - * Example implementation: - * <code> - * protected function createValueFromInput( $inputValue ) - * { - * if ( is_array( $inputValue ) ) - * { - * $inputValue = \eZ\Publish\Core\FieldType\CookieJar\Value( $inputValue ); - * } - * - * return $inputValue; - * } - * </code> - * - * @param mixed $inputValue - * - * @return mixed The potentially converted input value. - */ - abstract protected function createValueFromInput($inputValue); - - /** - * Throws an exception if the given $value is not an instance of the supported value subtype. - * - * This is an operation method for {@see acceptValue()}. - * - * Default implementation expects the value class to reside in the same namespace as its - * FieldType class and is named "Value". - * - * Example implementation: - * <code> - * static protected function checkValueType( $value ) - * { - * if ( !$inputValue instanceof \eZ\Publish\Core\FieldType\CookieJar\Value ) ) - * { - * throw new InvalidArgumentException( "Given value type is not supported." ); - * } - * } - * </code> - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the parameter is not an instance of the supported value subtype. - * - * @param mixed $value A value returned by {@see createValueFromInput()}. - */ - protected static function checkValueType($value) - { - $fieldTypeFQN = static::class; - $valueFQN = substr_replace($fieldTypeFQN, 'Value', strrpos($fieldTypeFQN, '\\') + 1); - - if (!$value instanceof $valueFQN) { - throw new InvalidArgumentType('$value', $valueFQN, $value); - } - } - - /** - * Throws an exception if value structure is not of expected format. - * - * Note that this does not include validation after the rules - * from validators, but only plausibility checks for the general data - * format. - * - * This is an operation method for {@see acceptValue()}. - * - * Example implementation: - * <code> - * protected function checkValueStructure( Value $value ) - * { - * if ( !is_array( $value->cookies ) ) - * { - * throw new InvalidArgumentException( "An array of assorted cookies was expected." ); - * } - * } - * </code> - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Value $value - */ - abstract protected function checkValueStructure(Value $value); - - /** - * Converts the given $fieldSettings to a simple hash format. - * - * This is the default implementation, which just returns the given - * $fieldSettings, assuming they are already in a hash format. Overwrite - * this in your specific implementation, if necessary. - * - * @param mixed $fieldSettings - * - * @return array|hash|scalar|null - */ - public function fieldSettingsToHash($fieldSettings) - { - return $fieldSettings; - } - - /** - * Converts the given $fieldSettingsHash to field settings of the type. - * - * This is the reverse operation of {@link fieldSettingsToHash()}. - * - * This is the default implementation, which just returns the given - * $fieldSettingsHash, assuming the supported field settings are already in - * a hash format. Overwrite this in your specific implementation, if - * necessary. - * - * @param array|hash|scalar|null $fieldSettingsHash - * - * @return mixed - */ - public function fieldSettingsFromHash($fieldSettingsHash) - { - return $fieldSettingsHash; - } - - /** - * Converts the given $validatorConfiguration to a simple hash format. - * - * Default implementation, which just returns the given - * $validatorConfiguration, which is by convention an array for all - * internal field types. Overwrite this method, if necessary. - * - * @param mixed $validatorConfiguration - * - * @return array|hash|scalar|null - */ - public function validatorConfigurationToHash($validatorConfiguration) - { - return $validatorConfiguration; - } - - /** - * Converts the given $validatorConfigurationHash to a validator - * configuration of the type. - * - * Default implementation, which just returns the given - * $validatorConfigurationHash, since the validator configuration is by - * convention an array for all internal field types. Overwrite this method, - * if necessary. - * - * @param array|hash|scalar|null $validatorConfigurationHash - * - * @return mixed - */ - public function validatorConfigurationFromHash($validatorConfigurationHash) - { - return $validatorConfigurationHash; - } - - /** - * Returns relation data extracted from value. - * - * Not intended for \eZ\Publish\API\Repository\Values\Content\Relation::COMMON type relations, - * there is an API for handling those. - * - * @param \eZ\Publish\Core\FieldType\Value $fieldValue - * - * @return array Hash with relation type as key and array of destination content ids as value. - * - * Example: - * <code> - * array( - * \eZ\Publish\API\Repository\Values\Content\Relation::LINK => array( - * "contentIds" => array( 12, 13, 14 ), - * "locationIds" => array( 24 ) - * ), - * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED => array( - * "contentIds" => array( 12 ), - * "locationIds" => array( 24, 45 ) - * ), - * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD => array( 12 ) - * ) - * </code> - */ - public function getRelations(SPIValue $fieldValue) - { - return []; - } - - public function valuesEqual(SPIValue $value1, SPIValue $value2): bool - { - return $this->toHash($value1) === $this->toHash($value2); - } -} diff --git a/eZ/Publish/Core/FieldType/FieldTypeRegistry.php b/eZ/Publish/Core/FieldType/FieldTypeRegistry.php deleted file mode 100644 index 0e0fe9d96d..0000000000 --- a/eZ/Publish/Core/FieldType/FieldTypeRegistry.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType; - -use eZ\Publish\Core\Base\Exceptions\NotFound\FieldTypeNotFoundException; -use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType; - -/** - * Registry for SPI FieldTypes. - * - * @internal Meant for internal use by Repository. - */ -class FieldTypeRegistry -{ - /** @var \eZ\Publish\SPI\FieldType\FieldType[] Hash of SPI FieldTypes where key is identifier */ - protected $fieldTypes; - - /** @var string[] */ - private $concreteFieldTypesIdentifiers; - - /** - * @param \eZ\Publish\SPI\FieldType\FieldType[] $fieldTypes Hash of SPI FieldTypes where key is identifier - */ - public function __construct(array $fieldTypes = []) - { - $this->fieldTypes = $fieldTypes; - } - - /** - * Returns a list of all SPI FieldTypes. - * - * @return \eZ\Publish\SPI\FieldType\FieldType[] - */ - public function getFieldTypes(): array - { - return $this->fieldTypes; - } - - /** - * Return a SPI FieldType object. - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFound\FieldTypeNotFoundException If $identifier was not found - * - * @param string $identifier - * - * @return \eZ\Publish\SPI\FieldType\FieldType - */ - public function getFieldType($identifier): SPIFieldType - { - if (!isset($this->fieldTypes[$identifier])) { - throw new FieldTypeNotFoundException($identifier); - } - - return $this->fieldTypes[$identifier]; - } - - public function registerFieldType(string $identifier, SPIFieldType $fieldType): void - { - $this->fieldTypes[$identifier] = $fieldType; - } - - /** - * Returns if there is a SPI FieldType registered under $identifier. - * - * @param string $identifier - * - * @return bool - */ - public function hasFieldType($identifier): bool - { - return isset($this->fieldTypes[$identifier]); - } - - /** - * Registers $fieldTypeIdentifier as "concrete" FieldType (i.e. not using NullFieldType). - */ - public function registerConcreteFieldTypeIdentifier(string $fieldTypeIdentifier): void - { - $this->concreteFieldTypesIdentifiers[] = $fieldTypeIdentifier; - } - - /** - * @return string[] - */ - public function getConcreteFieldTypesIdentifiers(): array - { - return $this->concreteFieldTypesIdentifiers; - } -} diff --git a/eZ/Publish/Core/FieldType/Float/SearchField.php b/eZ/Publish/Core/FieldType/Float/SearchField.php deleted file mode 100644 index ab48e44913..0000000000 --- a/eZ/Publish/Core/FieldType/Float/SearchField.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Float; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for Float field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value', - $field->value->data, - new Search\FieldType\FloatField() - ), - new Search\Field( - 'fulltext', - $field->value->data, - new Search\FieldType\FullTextField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value' => new Search\FieldType\FloatField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/Float/Type.php b/eZ/Publish/Core/FieldType/Float/Type.php deleted file mode 100644 index d87058c984..0000000000 --- a/eZ/Publish/Core/FieldType/Float/Type.php +++ /dev/null @@ -1,266 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Float; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -/** - * Float field types. - * - * Represents floats. - */ -class Type extends FieldType -{ - protected $validatorConfigurationSchema = [ - 'FloatValueValidator' => [ - 'minFloatValue' => [ - 'type' => 'float', - 'default' => null, - ], - 'maxFloatValue' => [ - 'type' => 'float', - 'default' => null, - ], - ], - ]; - - /** - * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $validatorConfiguration - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateValidatorConfiguration($validatorConfiguration) - { - $validationErrors = []; - - foreach ($validatorConfiguration as $validatorIdentifier => $constraints) { - if ($validatorIdentifier !== 'FloatValueValidator') { - $validationErrors[] = new ValidationError( - "Validator '%validator%' is unknown", - null, - [ - '%validator%' => $validatorIdentifier, - ], - "[$validatorIdentifier]" - ); - - continue; - } - - foreach ($constraints as $name => $value) { - switch ($name) { - case 'minFloatValue': - case 'maxFloatValue': - if ($value !== null && !is_numeric($value)) { - $validationErrors[] = new ValidationError( - "Validator parameter '%parameter%' value must be of numeric type", - null, - [ - '%parameter%' => $name, - ], - "[$validatorIdentifier][$name]" - ); - } - break; - default: - $validationErrors[] = new ValidationError( - "Validator parameter '%parameter%' is unknown", - null, - [ - '%parameter%' => $name, - ], - "[$validatorIdentifier][$name]" - ); - } - } - } - - return $validationErrors; - } - - /** - * Validates a field based on the validators in the field definition. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field - * @param \eZ\Publish\Core\FieldType\Float\Value $fieldValue The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) - { - $validationErrors = []; - - if ($this->isEmptyValue($fieldValue)) { - return $validationErrors; - } - - $validatorConfiguration = $fieldDefinition->getValidatorConfiguration(); - $constraints = isset($validatorConfiguration['FloatValueValidator']) ? - $validatorConfiguration['FloatValueValidator'] : - []; - - $validationErrors = []; - - if (isset($constraints['maxFloatValue']) && - $constraints['maxFloatValue'] !== null && $fieldValue->value > $constraints['maxFloatValue']) { - $validationErrors[] = new ValidationError( - 'The value can not be higher than %size%.', - null, - [ - '%size%' => $constraints['maxFloatValue'], - ], - 'value' - ); - } - - if (isset($constraints['minFloatValue']) && - $constraints['minFloatValue'] !== null && $fieldValue->value < $constraints['minFloatValue']) { - $validationErrors[] = new ValidationError( - 'The value can not be lower than %size%.', - null, - [ - '%size%' => $constraints['minFloatValue'], - ], - 'value' - ); - } - - return $validationErrors; - } - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezfloat'; - } - - /** - * @param \eZ\Publish\Core\FieldType\Float\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return (string)$value; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Float\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Implements the core of {@see isEmptyValue()}. - * - * @param mixed $value - * - * @return bool - */ - public function isEmptyValue(SPIValue $value) - { - return $value->value === null; - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param int|float|\eZ\Publish\Core\FieldType\Float\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\Float\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_numeric($inputValue)) { - $inputValue = (float)$inputValue; - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Float\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!is_float($value->value)) { - throw new InvalidArgumentType( - '$value->value', - 'float', - $value->value - ); - } - } - - /** - * {@inheritdoc} - * - * @param \eZ\Publish\Core\FieldType\Float\Value $value - */ - protected function getSortInfo(BaseValue $value) - { - return $value->value; - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\Float\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - return new Value((float)$hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Float\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - return $value->value; - } - - public function isSearchable(): bool - { - return true; - } -} diff --git a/eZ/Publish/Core/FieldType/Float/Value.php b/eZ/Publish/Core/FieldType/Float/Value.php deleted file mode 100644 index a3cd5d31a7..0000000000 --- a/eZ/Publish/Core/FieldType/Float/Value.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Float; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for Float field type. - */ -class Value extends BaseValue -{ - /** - * Float content. - * - * @var float|null - */ - public $value; - - /** - * Construct a new Value object and initialize with $value. - * - * @param float|null $value - */ - public function __construct($value = null) - { - $this->value = $value; - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - return (string)$this->value; - } -} diff --git a/eZ/Publish/Core/FieldType/GatewayBasedStorage.php b/eZ/Publish/Core/FieldType/GatewayBasedStorage.php deleted file mode 100644 index 261c115029..0000000000 --- a/eZ/Publish/Core/FieldType/GatewayBasedStorage.php +++ /dev/null @@ -1,117 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType; - -use eZ\Publish\SPI\FieldType\FieldStorage; -use eZ\Publish\SPI\FieldType\GatewayBasedStorage as SPIGatewayBasedStorage; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Storage gateway base class to be used by FieldType storages. - * - * @deprecated Since 6.11. Use {@link \eZ\Publish\SPI\FieldType\GatewayBasedStorage} - * - * This class gives a common basis to realized gateway based storage - * dispatching. It is intended to deal as a base class for FieldType storages, - * giving a common infrastructure to handle multiple gateways, based on the - * context provided by the SPI. - * - * The method {@link getGateway()} is used in derived classes to retrieve the - * correct gateway implementation, based on the context. - */ -abstract class GatewayBasedStorage implements FieldStorage -{ - /** - * Gateways. - * - * @var \eZ\Publish\Core\FieldType\StorageGateway[] - */ - protected $gateways; - - /** - * Construct from gateways. - * - * @param \eZ\Publish\Core\FieldType\StorageGateway[] $gateways - */ - public function __construct(array $gateways = []) - { - @trigger_error( - sprintf( - '%s extends deprecated %s. Extend %s instead', - static::class, - self::class, - SPIGatewayBasedStorage::class - ), - E_USER_DEPRECATED - ); - - foreach ($gateways as $identifier => $gateway) { - $this->addGateway($identifier, $gateway); - } - } - - /** - * Adds a storage $gateway assigned to the given $identifier. - * - * @param string $identifier - * @param \eZ\Publish\Core\FieldType\StorageGateway $gateway - */ - public function addGateway($identifier, StorageGateway $gateway) - { - $this->gateways[$identifier] = $gateway; - } - - /** - * Retrieve the fitting gateway, base on the identifier in $context. - * - * @deprecated Since 6.11. Retrieving gateway based on $context is deprecated - * and will be removed in 7.0. Inject gateway directly into FieldStorage - * - * @param array $context - * - * @return \eZ\Publish\Core\FieldType\StorageGateway - */ - protected function getGateway(array $context) - { - @trigger_error( - sprintf( - '%s: Retrieving gateway based on $context is deprecated and will be removed in 7.0. Inject gateway directly into FieldStorage', - static::class - ), - E_USER_DEPRECATED - ); - - if (!isset($this->gateways[$context['identifier']])) { - throw new \OutOfBoundsException("No gateway for {$context['identifier']} available."); - } - - $gateway = $this->gateways[$context['identifier']]; - $gateway->setConnection($context['connection']); - - return $gateway; - } - - /** - * This method is used exclusively by Legacy Storage to copy external data of existing field in main language to - * the untranslatable field not passed in create or update struct, but created implicitly in storage layer. - * - * By default the method falls back to the {@link \eZ\Publish\SPI\FieldType\FieldStorage::storeFieldData()}. - * External storages implement this method as needed. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Field $originalField - * @param array $context - * - * @return bool|null Same as {@link \eZ\Publish\SPI\FieldType\FieldStorage::storeFieldData()}. - */ - public function copyLegacyField(VersionInfo $versionInfo, Field $field, Field $originalField, array $context) - { - return $this->storeFieldData($versionInfo, $field, $context); - } -} diff --git a/eZ/Publish/Core/FieldType/Handler.php b/eZ/Publish/Core/FieldType/Handler.php deleted file mode 100644 index d0886d7d2f..0000000000 --- a/eZ/Publish/Core/FieldType/Handler.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType; - -/** - * Field type handler interface. - * - * Some field types provides handlers which help manipulate the field type value. - * These objects implement this interface. - */ -interface Handler -{ - /** - * Populates the field type handler with data from a field type. - * - * @param mixed $value - */ - public function initWithFieldTypeValue($value); - - /** - * Returns a compatible value to store in a field type after manipulation - * in the handler. - * - * @return mixed - */ - public function getFieldTypeValue(); -} diff --git a/eZ/Publish/Core/FieldType/ISBN/SearchField.php b/eZ/Publish/Core/FieldType/ISBN/SearchField.php deleted file mode 100644 index adfe66bcae..0000000000 --- a/eZ/Publish/Core/FieldType/ISBN/SearchField.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\ISBN; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for ISBN field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value', - $field->value->data, - new Search\FieldType\StringField() - ), - new Search\Field( - 'fulltext', - $field->value->data, - new Search\FieldType\FullTextField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/ISBN/Type.php b/eZ/Publish/Core/FieldType/ISBN/Type.php deleted file mode 100644 index 6e5d6a9e68..0000000000 --- a/eZ/Publish/Core/FieldType/ISBN/Type.php +++ /dev/null @@ -1,347 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\ISBN; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -/** - * The ISBN field type. - * - * This field type represents a simple string. - */ -class Type extends FieldType -{ - public const ISBN13_PREFIX_LENGTH = 3; - public const ISBN13_CHECK_LENGTH = 1; - public const ISBN13_LENGTH = 13; - public const ISBN13_PREFIX_978 = '978'; - public const ISBN13_PREFIX_979 = '979'; - - protected $settingsSchema = [ - 'isISBN13' => [ - 'type' => 'boolean', - 'default' => true, - ], - ]; - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezisbn'; - } - - /** - * @param \eZ\Publish\Core\FieldType\ISBN\Value|\eZ\Publish\SPI\FieldType\Value $value - * - * @return string - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return (string)$value->isbn; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\ISBN\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Returns if the given $value is considered empty by the field type. - * - * @param mixed $value - * - * @return bool - */ - public function isEmptyValue(SPIValue $value) - { - return $value->isbn === null || trim($value->isbn) === ''; - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param string|\eZ\Publish\Core\FieldType\ISBN\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\ISBN\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_string($inputValue)) { - $inputValue = $this->fromHash($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\ISBN\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!is_string($value->isbn)) { - throw new InvalidArgumentType( - '$value->isbn', - 'string', - $value->isbn - ); - } - } - - /** - * Validates a field based on the validators in the field definition. - * - * Does not use validators. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field - * @param \eZ\Publish\Core\FieldType\ISBN\Value $fieldValue The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) - { - $validationErrors = []; - if ($this->isEmptyValue($fieldValue)) { - return $validationErrors; - } - - $fieldSettings = $fieldDefinition->getFieldSettings(); - $isbnTestNumber = preg_replace("/[\s|\-]/", '', trim($fieldValue->isbn)); - - // Check if value and settings are inline - if ((!isset($fieldSettings['isISBN13']) || $fieldSettings['isISBN13'] === false) - && strlen($isbnTestNumber) !== 10) { - $validationErrors[] = new ValidationError( - 'ISBN-10 must be 10 character length', - null, - [], - 'isbn' - ); - } elseif (strlen($isbnTestNumber) === 10) { - // ISBN-10 check - if (!$this->validateISBNChecksum($isbnTestNumber)) { - $validationErrors[] = new ValidationError( - 'ISBN value must be in a valid ISBN-10 format', - null, - [], - 'isbn' - ); - } - } else { - // ISBN-13 check - if (!$this->validateISBN13Checksum($isbnTestNumber, $error)) { - $validationErrors[] = new ValidationError( - $error, - null, - [], - 'isbn' - ); - } - } - - return $validationErrors; - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * - * @param \eZ\Publish\Core\FieldType\ISBN\Value $value - * - * @return string - */ - protected function getSortInfo(BaseValue $value) - { - return $this->transformationProcessor->transformByGroup((string)$value, 'lowercase'); - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\ISBN\Value $value - */ - public function fromHash($hash) - { - if ($hash === null || $hash === '') { - return $this->getEmptyValue(); - } - - return new Value($hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\ISBN\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - return $value->isbn; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } - - /** - * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings) - { - $validationErrors = []; - - foreach ($fieldSettings as $name => $value) { - if (!isset($this->settingsSchema[$name])) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' is unknown", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - continue; - } - - switch ($name) { - case 'isISBN13': - if (!is_bool($value)) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' value must be of boolean type", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - break; - } - } - - return $validationErrors; - } - - /** - * Validates the ISBN number. - * All characters should be numeric except the last digit that may be the character X, - * which should be calculated as 10. - * - * @param string $isbnNr A string containing the number without any dashes. - * - * @return bool - */ - private function validateISBNChecksum($isbnNr) - { - $result = 0; - $isbnNr = strtoupper($isbnNr); - for ($i = 10; $i > 0; --$i) { - if (is_numeric($isbnNr[$i - 1]) || ($i === 10 && $isbnNr[$i - 1] === 'X')) { - if (($i === 1) && ($isbnNr[9] === 'X')) { - $result += 10 * $i; - } else { - $result += $isbnNr[10 - $i] * $i; - } - } else { - return false; - } - } - - return $result % 11 === 0; - } - - /** - * Validates the ISBN-13 number. - * - * @param string $isbnNr A string containing the number without any dashes. - * @param string $error is used to send back an error message that will be shown to the user if the - * ISBN number validated. - * - * @return bool - */ - private function validateISBN13Checksum($isbnNr, &$error) - { - if (!$isbnNr) { - return false; - } - - if (strlen($isbnNr) !== self::ISBN13_LENGTH) { - $error = 'ISBN-13 must be 13 digit, digit count is: ' . strlen($isbnNr); - - return false; - } - - if (substr($isbnNr, 0, self::ISBN13_PREFIX_LENGTH) !== self::ISBN13_PREFIX_978 && - substr($isbnNr, 0, self::ISBN13_PREFIX_LENGTH) !== self::ISBN13_PREFIX_979) { - $error = 'ISBN-13 value must start with 978 or 979, got: ' . substr($isbnNr, 0, self::ISBN13_PREFIX_LENGTH); - - return false; - } - - $checksum13 = 0; - $weight13 = 1; - //compute checksum - $val = 0; - for ($i = 0; $i < self::ISBN13_LENGTH; ++$i) { - $val = $isbnNr[$i]; - if (!is_numeric($isbnNr[$i])) { - $error = 'All ISBN-13 characters need to be numeric'; - - return false; - } - $checksum13 = $checksum13 + $weight13 * $val; - $weight13 = ($weight13 + 2) % 4; - } - if (($checksum13 % 10) !== 0) { - // Calculate the last digit from the 12 first numbers. - $checkDigit = (10 - (($checksum13 - (($weight13 + 2) % 4) * $val) % 10)) % 10; - //bad checksum - $error = 'Bad checksum, last digit of ISBN-13 should be ' . $checkDigit; - - return false; - } - - return true; - } -} diff --git a/eZ/Publish/Core/FieldType/ISBN/Value.php b/eZ/Publish/Core/FieldType/ISBN/Value.php deleted file mode 100644 index 3d8e26e510..0000000000 --- a/eZ/Publish/Core/FieldType/ISBN/Value.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\ISBN; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for ISBN field type. - */ -class Value extends BaseValue -{ - /** - * ISBN content. - * - * @var string - */ - public $isbn; - - /** - * Construct a new Value object and initialize it with its $isbn. - * - * @param string $isbn - */ - public function __construct($isbn = '') - { - $this->isbn = $isbn; - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - return (string)$this->isbn; - } -} diff --git a/eZ/Publish/Core/FieldType/Image/IO/Legacy.php b/eZ/Publish/Core/FieldType/Image/IO/Legacy.php deleted file mode 100644 index 063464d2cc..0000000000 --- a/eZ/Publish/Core/FieldType/Image/IO/Legacy.php +++ /dev/null @@ -1,267 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Image\IO; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\IO\Values\BinaryFileCreateStruct; -use eZ\Publish\Core\IO\Values\MissingBinaryFile; - -/** - * Legacy Image IOService. - * - * Acts as a dispatcher between the two IOService instances required by FieldType\Image in Legacy. - * - One is the usual one, as used in ImageStorage, that uses 'images' as the prefix - * - The other is a special one, that uses 'images-versioned' as the prefix, in order to cope with content created - * from the backoffice - * - * To load a binary file, this service will first try with the normal IOService, - * and on exception, will fall back to the draft IOService. - * - * In addition, loadBinaryFile() will also hide the need to explicitly call getExternalPath() - * on the internal path stored in legacy. - */ -class Legacy implements IOServiceInterface -{ - /** - * Published images IO Service. - * - * @var \eZ\Publish\Core\IO\IOServiceInterface - */ - private $publishedIOService; - - /** - * Draft images IO Service. - * - * @var \eZ\Publish\Core\IO\IOServiceInterface - */ - private $draftIOService; - - /** - * Prefix for published images. - * Example: var/ezdemo_site/storage/images. - * - * @var string - */ - private $publishedPrefix; - - /** - * Prefix for draft images. - * Example: var/ezdemo_site/storage/images-versioned. - * - * @var string - */ - private $draftPrefix; - - /** @var \eZ\Publish\Core\FieldType\Image\IO\OptionsProvider */ - private $optionsProvider; - - /** - * @param \eZ\Publish\Core\IO\IOServiceInterface $publishedIOService - * @param \eZ\Publish\Core\IO\IOServiceInterface $draftIOService - * @param array $options Path options. Known keys: var_dir, storage_dir, draft_images_dir, published_images_dir. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException if required options are missing - * @throws \Symfony\Component\OptionsResolver\Exception\InvalidOptionsException - * If any of the passed options has not been defined or does not contain an allowed value - * @throws \Symfony\Component\OptionsResolver\Exception\MissingOptionsException - * If a required option is missing. - */ - public function __construct(IOServiceInterface $publishedIOService, IOServiceInterface $draftIOService, OptionsProvider $optionsProvider) - { - $this->publishedIOService = $publishedIOService; - $this->draftIOService = $draftIOService; - $this->optionsProvider = $optionsProvider; - $this->setPrefixes(); - } - - /** - * Sets the IOService prefix. - */ - public function setPrefix($prefix) - { - $this->publishedIOService->setPrefix($prefix); - $this->draftIOService->setPrefix($prefix); - } - - /** - * Computes the paths to published & draft images path using the options from the provider. - */ - private function setPrefixes() - { - $pathArray = [$this->optionsProvider->getVarDir()]; - - // The storage dir itself might be null - if ($storageDir = $this->optionsProvider->getStorageDir()) { - $pathArray[] = $storageDir; - } - - $this->draftPrefix = implode('/', array_merge($pathArray, [$this->optionsProvider->getDraftImagesDir()])); - $this->publishedPrefix = implode('/', array_merge($pathArray, [$this->optionsProvider->getPublishedImagesDir()])); - } - - public function getExternalPath($internalId) - { - return $this->publishedIOService->getExternalPath($internalId); - } - - public function newBinaryCreateStructFromLocalFile($localFile) - { - return $this->publishedIOService->newBinaryCreateStructFromLocalFile($localFile); - } - - public function exists($binaryFileId) - { - return $this->publishedIOService->exists($binaryFileId); - } - - public function getInternalPath($externalId) - { - return $this->publishedIOService->getInternalPath($externalId); - } - - public function loadBinaryFile($binaryFileId) - { - // If the id is an internal (absolute) path to a draft image, use the draft service to get external path & load - if ($this->isDraftImagePath($binaryFileId)) { - return $this->draftIOService->loadBinaryFile($this->draftIOService->getExternalPath($binaryFileId)); - } - - // If the id is an internal path (absolute) to a published image, replace with the internal path - if ($this->isPublishedImagePath($binaryFileId)) { - $binaryFileId = $this->publishedIOService->getExternalPath($binaryFileId); - } - - try { - $image = $this->publishedIOService->loadBinaryFile($binaryFileId); - - if ($image instanceof MissingBinaryFile) { - throw new InvalidArgumentException('binaryFileId', sprintf('Cannot find file with ID %s', $binaryFileId)); - } - - return $image; - } catch (InvalidArgumentException $prefixException) { - // InvalidArgumentException means that the prefix didn't match, NotFound can pass through - try { - return $this->draftIOService->loadBinaryFile($binaryFileId); - } catch (InvalidArgumentException $e) { - throw $prefixException; - } - } - } - - /** - * Since both services should use the same uri, we can use any of them to *GET* the URI. - */ - public function loadBinaryFileByUri($binaryFileUri) - { - try { - $image = $this->publishedIOService->loadBinaryFileByUri($binaryFileUri); - - if ($image instanceof MissingBinaryFile) { - throw new InvalidArgumentException('binaryFileUri', sprintf('Cannot find file with URL %s', $binaryFileUri)); - } - - return $image; - } catch (InvalidArgumentException $prefixException) { - // InvalidArgumentException means that the prefix didn't match, NotFound can pass through - try { - return $this->draftIOService->loadBinaryFileByUri($binaryFileUri); - } catch (InvalidArgumentException $e) { - throw $prefixException; - } - } - } - - public function getFileContents(BinaryFile $binaryFile) - { - if ($this->draftIOService->exists($binaryFile->id)) { - return $this->draftIOService->getFileContents($binaryFile); - } - - return $this->publishedIOService->getFileContents($binaryFile); - } - - public function createBinaryFile(BinaryFileCreateStruct $binaryFileCreateStruct) - { - return $this->publishedIOService->createBinaryFile($binaryFileCreateStruct); - } - - public function getUri($binaryFileId) - { - return $this->publishedIOService->getUri($binaryFileId); - } - - public function getMimeType($binaryFileId) - { - // If the id is an internal (absolute) path to a draft image, use the draft service to get external path & load - if ($this->isDraftImagePath($binaryFileId)) { - return $this->draftIOService->getMimeType($this->draftIOService->getExternalPath($binaryFileId)); - } - - // If the id is an internal path (absolute) to a published image, replace with the internal path - if ($this->isPublishedImagePath($binaryFileId)) { - $binaryFileId = $this->publishedIOService->getExternalPath($binaryFileId); - } - - if ($this->draftIOService->exists($binaryFileId)) { - return $this->draftIOService->getMimeType($binaryFileId); - } - - return $this->publishedIOService->getMimeType($binaryFileId); - } - - public function getFileInputStream(BinaryFile $binaryFile) - { - return $this->publishedIOService->getFileInputStream($binaryFile); - } - - public function deleteBinaryFile(BinaryFile $binaryFile) - { - $this->publishedIOService->deleteBinaryFile($binaryFile); - } - - /** - * Deletes a directory. - * - * @param string $path - */ - public function deleteDirectory($path) - { - $this->publishedIOService->deleteDirectory($path); - } - - public function newBinaryCreateStructFromUploadedFile(array $uploadedFile) - { - return $this->publishedIOService->newBinaryCreateStructFromUploadedFile($uploadedFile); - } - - /** - * Checks if $internalPath is a published image path. - * - * @param string $internalPath - * - * @return bool true if $internalPath is the path to a published image - */ - protected function isPublishedImagePath($internalPath) - { - return strpos($internalPath, $this->publishedPrefix) === 0; - } - - /** - * Checks if $internalPath is a published image path. - * - * @param string $internalPath - * - * @return bool true if $internalPath is the path to a published image - */ - protected function isDraftImagePath($internalPath) - { - return strpos($internalPath, $this->draftPrefix) === 0; - } -} diff --git a/eZ/Publish/Core/FieldType/Image/ImageStorage/Gateway.php b/eZ/Publish/Core/FieldType/Image/ImageStorage/Gateway.php deleted file mode 100644 index 66dcb56736..0000000000 --- a/eZ/Publish/Core/FieldType/Image/ImageStorage/Gateway.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Image\ImageStorage; - -use eZ\Publish\SPI\FieldType\StorageGateway; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Image Field Type external storage gateway. - */ -abstract class Gateway extends StorageGateway -{ - /** - * Returns the node path string of $versionInfo. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * - * @return string - */ - abstract public function getNodePathString(VersionInfo $versionInfo); - - /** - * Stores a reference to the image in $path for $fieldId. - * - * @param string $uri File IO uri - * @param mixed $fieldId - */ - abstract public function storeImageReference($uri, $fieldId); - - /** - * Returns a the XML content stored for the given $fieldIds. - * - * @param int $versionNo - * @param array $fieldIds - * - * @return array - */ - abstract public function getXmlForImages($versionNo, array $fieldIds); - - /** - * Removes all references from $fieldId to a path that starts with $path. - * - * @param string $uri File IO uri (not legacy uri) - * @param int $versionNo - * @param mixed $fieldId - */ - abstract public function removeImageReferences($uri, $versionNo, $fieldId); - - /** - * Returns the number of recorded references to the given $path. - * - * @param string $uri File IO uri (not legacy uri) - * - * @return int - */ - abstract public function countImageReferences($uri); - - /** - * Returns true if there is reference to the given $uri. - */ - abstract public function isImageReferenced(string $uri): bool; - - /** - * Returns the public uris for the images stored in $xml. - */ - abstract public function extractFilesFromXml($xml); - - abstract public function getAllVersionsImageXmlForFieldId(int $fieldId): array; - - abstract public function updateImageData(int $fieldId, int $versionNo, string $xml): void; - - abstract public function getImagesData(int $offset, int $limit): array; - - abstract public function updateImagePath(int $fieldId, string $oldPath, string $newPath): void; - - abstract public function countDistinctImagesData(): int; - - abstract public function hasImageReference(string $uri, int $fieldId): bool; -} diff --git a/eZ/Publish/Core/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php b/eZ/Publish/Core/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php deleted file mode 100644 index f283429ea5..0000000000 --- a/eZ/Publish/Core/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php +++ /dev/null @@ -1,493 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Image\ImageStorage\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use DOMDocument; -use eZ\Publish\Core\FieldType\Image\ImageStorage\Gateway; -use eZ\Publish\Core\IO\UrlRedecoratorInterface; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use PDO; - -/** - * Image Field Type external storage DoctrineStorage gateway. - */ -class DoctrineStorage extends Gateway -{ - public const IMAGE_FILE_TABLE = 'ezimagefile'; - - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - /** - * Maps database field names to property names. - * - * @var array - */ - protected $fieldNameMap = [ - 'id' => 'fieldId', - 'version' => 'versionNo', - 'language_code' => 'languageCode', - 'path_identification_string' => 'nodePathString', - 'data_string' => 'xml', - ]; - - /** @var \eZ\Publish\Core\IO\UrlRedecoratorInterface */ - private $redecorator; - - public function __construct(UrlRedecoratorInterface $redecorator, Connection $connection) - { - $this->redecorator = $redecorator; - $this->connection = $connection; - } - - /** - * Return the node path string of $versionInfo. - * - * @return string - */ - public function getNodePathString(VersionInfo $versionInfo) - { - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery - ->select($this->connection->quoteIdentifier('path_identification_string')) - ->from($this->connection->quoteIdentifier('ezcontentobject_tree')) - ->where( - $selectQuery->expr()->andX( - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('contentobject_id'), - ':contentObjectId' - ), - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('contentobject_version'), - ':versionNo' - ), - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('node_id'), - $this->connection->quoteIdentifier('main_node_id') - ) - ) - ) - ->setParameter(':contentObjectId', $versionInfo->contentInfo->id, PDO::PARAM_INT) - ->setParameter(':versionNo', $versionInfo->versionNo, PDO::PARAM_INT) - ; - - $statement = $selectQuery->execute(); - - return $statement->fetchColumn(); - } - - /** - * Store a reference to the image in $path for $fieldId. - * - * @param string $uri File IO uri (not legacy) - * @param int $fieldId - */ - public function storeImageReference($uri, $fieldId) - { - // legacy stores the path to the image without a leading / - $path = $this->redecorator->redecorateFromSource($uri); - - $insertQuery = $this->connection->createQueryBuilder(); - $insertQuery - ->insert($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) - ->values( - [ - $this->connection->quoteIdentifier('contentobject_attribute_id') => ':fieldId', - $this->connection->quoteIdentifier('filepath') => ':path', - ] - ) - ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) - ->setParameter(':path', $path) - ; - - $insertQuery->execute(); - } - - /** - * Return an XML content stored for the given $fieldIds. - * - * @param int $versionNo - * - * @return array - */ - public function getXmlForImages($versionNo, array $fieldIds) - { - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery - ->select( - $this->connection->quoteIdentifier('attr.id'), - $this->connection->quoteIdentifier('attr.data_text') - ) - ->from($this->connection->quoteIdentifier('ezcontentobject_attribute'), 'attr') - ->where( - $selectQuery->expr()->andX( - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('attr.version'), - ':versionNo' - ), - $selectQuery->expr()->in( - $this->connection->quoteIdentifier('attr.id'), - ':fieldIds' - ) - ) - ) - ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) - ->setParameter(':fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY) - ; - - $statement = $selectQuery->execute(); - - $fieldLookup = []; - foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $row) { - $fieldLookup[$row['id']] = $row['data_text']; - } - - return $fieldLookup; - } - - public function getAllVersionsImageXmlForFieldId(int $fieldId): array - { - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery - ->select( - 'field.id', - 'field.version', - 'field.data_text' - ) - ->from($this->connection->quoteIdentifier('ezcontentobject_attribute'), 'field') - ->where( - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('id'), - ':field_id' - ) - ) - ->setParameter(':field_id', $fieldId, PDO::PARAM_INT) - ; - - $statement = $selectQuery->execute(); - - $fieldLookup = []; - foreach ($statement->fetchAllAssociative() as $row) { - $fieldLookup[] = [ - 'version' => $row['version'], - 'data_text' => $row['data_text'], - ]; - } - - return $fieldLookup; - } - - /** - * Remove all references from $fieldId to a path that starts with $path. - * - * @param string $uri File IO uri (not legacy) - * @param int $versionNo - * @param int $fieldId - * - * @throws \eZ\Publish\Core\IO\Exception\InvalidBinaryFileIdException - */ - public function removeImageReferences($uri, $versionNo, $fieldId): void - { - if (!$this->canRemoveImageReference($uri, $versionNo, $fieldId)) { - return; - } - - $path = $this->redecorator->redecorateFromSource($uri); - - $deleteQuery = $this->connection->createQueryBuilder(); - $deleteQuery - ->delete($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) - ->where( - $deleteQuery->expr()->andX( - $deleteQuery->expr()->eq( - $this->connection->quoteIdentifier('contentobject_attribute_id'), - ':fieldId' - ), - $deleteQuery->expr()->like( - $this->connection->quoteIdentifier('filepath'), - ':likePath' - ) - ) - ) - ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) - ->setParameter(':likePath', $path . '%') - ; - - $deleteQuery->execute(); - } - - /** - * Return the number of recorded references to the given $path. - * - * @param string $uri File IO uri (not legacy) - * - * @return int - */ - public function countImageReferences($uri) - { - $path = $this->redecorator->redecorateFromSource($uri); - - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery - ->select('COUNT(' . $this->connection->quoteIdentifier('id') . ')') - ->from($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) - ->where( - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('filepath'), - ':filepath' - ) - ) - ->setParameter(':filepath', $path) - ; - - $statement = $selectQuery->execute(); - - return (int) $statement->fetchColumn(); - } - - public function isImageReferenced(string $uri): bool - { - $path = $this->redecorator->redecorateFromSource($uri); - - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery - ->select(1) - ->from($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) - ->where( - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('filepath'), - ':likePath' - ) - ) - ->setParameter(':likePath', $path) - ; - - $statement = $selectQuery->execute(); - - return (bool)$statement->fetchOne(); - } - - public function countDistinctImagesData(): int - { - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery - ->select($this->connection->getDatabasePlatform()->getCountExpression('id')) - ->from($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) - ; - - $statement = $selectQuery->execute(); - - return (int) $statement->fetchOne(); - } - - /** - * Check if image $path can be removed when deleting $versionNo and $fieldId. - * - * @param string $path legacy image path (var/storage/images...) - * @param int $versionNo - * @param int $fieldId - */ - protected function canRemoveImageReference($path, $versionNo, $fieldId): bool - { - $selectQuery = $this->connection->createQueryBuilder(); - $expressionBuilder = $selectQuery->expr(); - $selectQuery - ->select('attr.data_text') - ->from($this->connection->quoteIdentifier('ezcontentobject_attribute'), 'attr') - ->innerJoin( - 'attr', - $this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE), - 'img', - $expressionBuilder->eq( - $this->connection->quoteIdentifier('img.contentobject_attribute_id'), - $this->connection->quoteIdentifier('attr.id') - ) - ) - ->where( - $expressionBuilder->eq( - $this->connection->quoteIdentifier('contentobject_attribute_id'), - ':fieldId' - ) - ) - ->andWhere( - $expressionBuilder->neq( - $this->connection->quoteIdentifier('version'), - ':versionNo' - ) - ) - ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) - ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) - ; - - $imageXMLs = $selectQuery->execute()->fetchAll(FetchMode::COLUMN); - foreach ($imageXMLs as $imageXML) { - $storedFilePath = $this->extractFilesFromXml($imageXML)['original'] ?? null; - if ($storedFilePath === $path) { - return false; - } - } - - return true; - } - - /** - * Extract, stored in DocBook XML, file paths. - * - * @param string $xml - * - * @return array|null - */ - public function extractFilesFromXml($xml) - { - if (empty($xml)) { - // Empty image value - return null; - } - - $files = []; - - $dom = new DOMDocument(); - $dom->loadXml($xml); - if ($dom->documentElement->hasAttribute('dirpath')) { - $url = $dom->documentElement->getAttribute('url'); - if (empty($url)) { - return null; - } - - $files['original'] = $this->redecorator->redecorateFromTarget($url); - foreach ($dom->documentElement->childNodes as $childNode) { - /** @var \DOMElement $childNode */ - if ($childNode->nodeName !== 'alias') { - continue; - } - - $files[$childNode->getAttribute('name')] = $this->redecorator->redecorateFromTarget( - $childNode->getAttribute('url') - ); - } - - return $files; - } - - return null; - } - - public function getImagesData(int $offset, int $limit): array - { - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery - ->select( - 'img.contentobject_attribute_id', - 'img.filepath' - ) - ->distinct() - ->from($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE), 'img') - ->setFirstResult($offset) - ->setMaxResults($limit); - - return $selectQuery->execute()->fetchAllAssociative(); - } - - public function updateImageData(int $fieldId, int $versionNo, string $xml): void - { - $updateQuery = $this->connection->createQueryBuilder(); - $expressionBuilder = $updateQuery->expr(); - $updateQuery - ->update( - $this->connection->quoteIdentifier('ezcontentobject_attribute') - ) - ->set( - $this->connection->quoteIdentifier('data_text'), - ':xml' - ) - ->where( - $expressionBuilder->eq( - $this->connection->quoteIdentifier('id'), - ':field_id' - ) - ) - ->andWhere( - $expressionBuilder->eq( - $this->connection->quoteIdentifier('version'), - ':version_no' - ) - ) - ->setParameter(':field_id', $fieldId, ParameterType::INTEGER) - ->setParameter(':version_no', $versionNo, ParameterType::INTEGER) - ->setParameter(':xml', $xml, ParameterType::STRING) - ->execute() - ; - } - - public function updateImagePath(int $fieldId, string $oldPath, string $newPath): void - { - $updateQuery = $this->connection->createQueryBuilder(); - $expressionBuilder = $updateQuery->expr(); - $updateQuery - ->update( - $this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE) - ) - ->set( - $this->connection->quoteIdentifier('filepath'), - ':new_path' - ) - ->where( - $expressionBuilder->eq( - $this->connection->quoteIdentifier('contentobject_attribute_id'), - ':field_id' - ) - ) - ->andWhere( - $expressionBuilder->eq( - $this->connection->quoteIdentifier('filepath'), - ':old_path' - ) - ) - ->setParameter(':field_id', $fieldId, ParameterType::INTEGER) - ->setParameter(':old_path', $oldPath, ParameterType::STRING) - ->setParameter(':new_path', $newPath, ParameterType::STRING) - ->execute() - ; - } - - /** - * @throws \eZ\Publish\Core\IO\Exception\InvalidBinaryFileIdException - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \Doctrine\DBAL\Exception - */ - public function hasImageReference(string $uri, int $fieldId): bool - { - $path = $this->redecorator->redecorateFromSource($uri); - - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery - ->select(1) - ->from($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) - ->andWhere( - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('filepath'), - ':path' - ) - ) - ->andWhere( - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('contentobject_attribute_id'), - ':field_id' - ) - ) - ->setParameter(':path', $path) - ->setParameter(':field_id', $fieldId) - ; - - $statement = $selectQuery->execute(); - - return (bool)$statement->fetchOne(); - } -} diff --git a/eZ/Publish/Core/FieldType/Image/ImageThumbnailProxyStrategy.php b/eZ/Publish/Core/FieldType/Image/ImageThumbnailProxyStrategy.php deleted file mode 100644 index f85487a1bd..0000000000 --- a/eZ/Publish/Core/FieldType/Image/ImageThumbnailProxyStrategy.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\FieldType\Image; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\ProxyFactory\ProxyGeneratorInterface; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field\FieldTypeBasedThumbnailStrategy; -use ProxyManager\Proxy\LazyLoadingInterface; - -final class ImageThumbnailProxyStrategy implements FieldTypeBasedThumbnailStrategy -{ - /** @var \eZ\Publish\Core\FieldType\Image\ImageThumbnailStrategy */ - private $imageThumbnailStrategy; - - /** @var \eZ\Publish\Core\Repository\ProxyFactory\ProxyGeneratorInterface */ - private $proxyGenerator; - - public function __construct( - ImageThumbnailStrategy $imageThumbnailStrategy, - ProxyGeneratorInterface $proxyGenerator - ) { - $this->imageThumbnailStrategy = $imageThumbnailStrategy; - $this->proxyGenerator = $proxyGenerator; - } - - public function getFieldTypeIdentifier(): string - { - return $this->imageThumbnailStrategy->getFieldTypeIdentifier(); - } - - public function getThumbnail(Field $field, ?VersionInfo $versionInfo = null): ?Thumbnail - { - $initializer = function ( - &$wrappedObject, - LazyLoadingInterface $proxy, - $method, - array $parameters, - &$initializer - ) use ($field, $versionInfo): bool { - $initializer = null; - - $wrappedObject = $this->imageThumbnailStrategy->getThumbnail($field, $versionInfo); - - return true; - }; - - return $this->proxyGenerator->createProxy(Thumbnail::class, $initializer); - } -} diff --git a/eZ/Publish/Core/FieldType/Image/PathGenerator.php b/eZ/Publish/Core/FieldType/Image/PathGenerator.php deleted file mode 100644 index 67fd625c9a..0000000000 --- a/eZ/Publish/Core/FieldType/Image/PathGenerator.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Image; - -abstract class PathGenerator -{ - /** - * Generates the storage path for the field identified by parameters. - * - * Returns a relative storage path. - * - * @param mixed $fieldId - * @param int $versionNo - * @param string $languageCode - * - * @return string - */ - abstract public function getStoragePathForField($fieldId, $versionNo, $languageCode); -} diff --git a/eZ/Publish/Core/FieldType/Image/PathGenerator/LegacyPathGenerator.php b/eZ/Publish/Core/FieldType/Image/PathGenerator/LegacyPathGenerator.php deleted file mode 100644 index fee867c4e1..0000000000 --- a/eZ/Publish/Core/FieldType/Image/PathGenerator/LegacyPathGenerator.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Image\PathGenerator; - -use eZ\Publish\Core\FieldType\Image\PathGenerator; - -class LegacyPathGenerator extends PathGenerator -{ - /** - * Generates the storage path for the field identified by parameters. - * - * Returns a relative storage path. - * - * @param mixed $fieldId - * @param int $versionNo - * @param string $languageCode - * - * @return string - */ - public function getStoragePathForField($fieldId, $versionNo, $languageCode) - { - return sprintf( - '%s/%s-%s-%s', - $this->getDirectoryStructure($fieldId), - $fieldId, - $versionNo, - $languageCode - ); - } - - /** - * Computes a 4 levels directory structure from $id. - * - * @param string $id - * - * @return string - */ - private function getDirectoryStructure($id) - { - // our base string is the last 4 digits, defaulting to 0, reversed - $idString = strrev( - substr(str_pad($id, 4, 0, STR_PAD_LEFT), -4) - ); - - return trim( - chunk_split($idString, 1, '/'), - '/' - ); - } -} diff --git a/eZ/Publish/Core/FieldType/Image/SearchField.php b/eZ/Publish/Core/FieldType/Image/SearchField.php deleted file mode 100644 index 98db879688..0000000000 --- a/eZ/Publish/Core/FieldType/Image/SearchField.php +++ /dev/null @@ -1,95 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Image; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for TextLine field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'filename', - $field->value->data['fileName'] ?? null, - new Search\FieldType\StringField() - ), - new Search\Field( - 'alternative_text', - $field->value->data['alternativeText'] ?? null, - new Search\FieldType\StringField() - ), - new Search\Field( - 'file_size', - $field->value->data['fileSize'] ?? null, - new Search\FieldType\IntegerField() - ), - new Search\Field( - 'mime_type', - $field->value->data['mime'] ?? null, - new Search\FieldType\StringField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'filename' => new Search\FieldType\StringField(), - 'alternative_text' => new Search\FieldType\StringField(), - 'file_size' => new Search\FieldType\IntegerField(), - 'mime_type' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'filename'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/Image/Type.php b/eZ/Publish/Core/FieldType/Image/Type.php deleted file mode 100644 index b21603b11c..0000000000 --- a/eZ/Publish/Core/FieldType/Image/Type.php +++ /dev/null @@ -1,406 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Image; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; - -/** - * The Image field type. - */ -class Type extends FieldType -{ - /** - * @see eZ\Publish\Core\FieldType::$validatorConfigurationSchema - */ - protected $validatorConfigurationSchema = [ - 'FileSizeValidator' => [ - 'maxFileSize' => [ - 'type' => 'int', - 'default' => null, - ], - ], - 'AlternativeTextValidator' => [ - 'required' => [ - 'type' => 'bool', - 'default' => false, - ], - ], - ]; - - /** @var \eZ\Publish\Core\FieldType\Validator[] */ - private $validators; - - /** - * @param \eZ\Publish\Core\FieldType\Validator[] $validators - */ - public function __construct(array $validators) - { - $this->validators = $validators; - } - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezimage'; - } - - /** - * @param \eZ\Publish\Core\FieldType\Image\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return $value->alternativeText ?? (string)$value->fileName; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Image\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param string|array|\eZ\Publish\Core\FieldType\Image\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\Image\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_string($inputValue)) { - $inputValue = Value::fromString($inputValue); - } - - if (is_array($inputValue)) { - if (isset($inputValue['inputUri']) && file_exists($inputValue['inputUri'])) { - $inputValue['fileSize'] = filesize($inputValue['inputUri']); - if (!isset($inputValue['fileName'])) { - $inputValue['fileName'] = basename($inputValue['inputUri']); - } - } - - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Image\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (isset($value->inputUri) && !is_string($value->inputUri)) { - throw new InvalidArgumentType('$value->inputUri', 'string', $value->inputUri); - } - - if (isset($value->id) && !is_string($value->id)) { - throw new InvalidArgumentType('$value->id', 'string', $value->id); - } - - // Required parameter $fileName - if (!isset($value->fileName) || !is_string($value->fileName)) { - throw new InvalidArgumentType('$value->fileName', 'string', $value->fileName); - } - - // Optional parameter $alternativeText - if (isset($value->alternativeText) && !is_string($value->alternativeText)) { - throw new InvalidArgumentType( - '$value->alternativeText', - 'string', - $value->alternativeText - ); - } - - if (isset($value->fileSize) && (!is_int($value->fileSize) || $value->fileSize < 0)) { - throw new InvalidArgumentType( - '$value->fileSize', - 'int', - $value->alternativeText - ); - } - - if (isset($value->additionalData) && !\is_array($value->additionalData)) { - throw new InvalidArgumentType('$value->additionalData', 'array', $value->additionalData); - } - } - - /** - * Validates a field based on the validators in the field definition. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field - * @param \eZ\Publish\Core\FieldType\Image\Value $fieldValue The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) - { - $errors = []; - - if ($this->isEmptyValue($fieldValue)) { - return $errors; - } - - foreach ($this->validators as $externalValidator) { - if (!$externalValidator->validate($fieldValue)) { - $errors = array_merge($errors, $externalValidator->getMessage()); - } - } - - foreach ((array)$fieldDefinition->getValidatorConfiguration() as $validatorIdentifier => $parameters) { - switch ($validatorIdentifier) { - case 'FileSizeValidator': - if (empty($parameters['maxFileSize'])) { - // No file size limit - break; - } - - // Database stores maxFileSize in MB - if (($parameters['maxFileSize'] * 1024 * 1024) < $fieldValue->fileSize) { - $errors[] = new ValidationError( - 'The file size cannot exceed %size% megabyte.', - 'The file size cannot exceed %size% megabytes.', - [ - '%size%' => $parameters['maxFileSize'], - ], - 'fileSize' - ); - } - break; - case 'AlternativeTextValidator': - if ($parameters['required'] && $fieldValue->isAlternativeTextEmpty()) { - $errors[] = new ValidationError( - 'Alternative text is required.', - null, - [], - 'alternativeText' - ); - } - break; - } - } - - return $errors; - } - - /** - * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $validatorConfiguration - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateValidatorConfiguration($validatorConfiguration) - { - $validationErrors = []; - - foreach ($validatorConfiguration as $validatorIdentifier => $parameters) { - switch ($validatorIdentifier) { - case 'FileSizeValidator': - if (!array_key_exists('maxFileSize', $parameters)) { - $validationErrors[] = new ValidationError( - 'Validator %validator% expects parameter %parameter% to be set.', - null, - [ - '%validator%' => $validatorIdentifier, - '%parameter%' => 'maxFileSize', - ], - "[$validatorIdentifier]" - ); - break; - } - if (!is_int($parameters['maxFileSize']) && $parameters['maxFileSize'] !== null) { - $validationErrors[] = new ValidationError( - 'Validator %validator% expects parameter %parameter% to be of %type%.', - null, - [ - '%validator%' => $validatorIdentifier, - '%parameter%' => 'maxFileSize', - '%type%' => 'integer', - ], - "[$validatorIdentifier][maxFileSize]" - ); - } - break; - case 'AlternativeTextValidator': - if (!array_key_exists('required', $parameters)) { - $validationErrors[] = new ValidationError( - 'Validator %validator% expects parameter %parameter% to be set.', - null, - [ - '%validator%' => $validatorIdentifier, - '%parameter%' => 'required', - ], - "[$validatorIdentifier]" - ); - } - break; - default: - $validationErrors[] = new ValidationError( - "Validator '%validator%' is unknown", - null, - [ - '%validator%' => $validatorIdentifier, - ], - "[$validatorIdentifier]" - ); - } - } - - return $validationErrors; - } - - /** - * {@inheritdoc} - */ - protected function getSortInfo(BaseValue $value) - { - return false; - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\Image\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - return new Value($hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Image\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - return [ - 'id' => $value->id, - 'path' => $value->inputUri ?: $value->id, - 'alternativeText' => $value->alternativeText, - 'fileName' => $value->fileName, - 'fileSize' => $value->fileSize, - 'imageId' => $value->imageId, - 'uri' => $value->uri, - 'inputUri' => $value->inputUri, - 'width' => $value->width, - 'height' => $value->height, - 'additionalData' => $value->additionalData, - ]; - } - - /** - * Converts a $value to a persistence value. - * - * @param \eZ\Publish\Core\FieldType\Image\Value $value - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function toPersistenceValue(SPIValue $value) - { - // Store original data as external (to indicate they need to be stored) - return new FieldValue( - [ - 'data' => null, - 'externalData' => $this->toHash($value), - 'sortKey' => $this->getSortInfo($value), - ] - ); - } - - /** - * Converts a persistence $fieldValue to a Value. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - * - * @return \eZ\Publish\Core\FieldType\Image\Value - */ - public function fromPersistenceValue(FieldValue $fieldValue) - { - if ($fieldValue->data === null) { - return $this->getEmptyValue(); - } - - // Restored data comes in $data, since it has already been processed - // there might be more data in the persistence value than needed here - $result = $this->fromHash( - [ - 'id' => (isset($fieldValue->data['id']) - ? $fieldValue->data['id'] - : null), - 'alternativeText' => (isset($fieldValue->data['alternativeText']) - ? $fieldValue->data['alternativeText'] - : null), - 'fileName' => (isset($fieldValue->data['fileName']) - ? $fieldValue->data['fileName'] - : null), - 'fileSize' => (isset($fieldValue->data['fileSize']) - ? $fieldValue->data['fileSize'] - : null), - 'uri' => (isset($fieldValue->data['uri']) - ? $fieldValue->data['uri'] - : null), - 'imageId' => (isset($fieldValue->data['imageId']) - ? $fieldValue->data['imageId'] - : null), - 'width' => (isset($fieldValue->data['width']) - ? $fieldValue->data['width'] - : null), - 'height' => (isset($fieldValue->data['height']) - ? $fieldValue->data['height'] - : null), - 'additionalData' => $fieldValue->data['additionalData'] ?? [], - ] - ); - - return $result; - } - - public function valuesEqual(SPIValue $value1, SPIValue $value2): bool - { - $hashValue1 = $this->toHash($value1); - $hashValue2 = $this->toHash($value2); - - unset($hashValue1['imageId'], $hashValue2['imageId']); - - return $hashValue1 === $hashValue2; - } -} diff --git a/eZ/Publish/Core/FieldType/Image/Value.php b/eZ/Publish/Core/FieldType/Image/Value.php deleted file mode 100644 index e862f5e64f..0000000000 --- a/eZ/Publish/Core/FieldType/Image/Value.php +++ /dev/null @@ -1,186 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Image; - -use eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for Image field type. - * - * @property string $path @deprecated BC with 5.0 (EZP-20948). Equivalent to $id or $inputUri, depending on which one is set - * . - * - * @todo Mime type? - * @todo Dimensions? - */ -class Value extends BaseValue -{ - /** - * Image id. - * - * Required. - * - * @var mixed|null - */ - public $id; - - /** - * The alternative image text (for example "Picture of an apple."). - * - * @var string|null - */ - public $alternativeText; - - /** - * Display file name of the image. - * - * Required. - * - * @var string|null - */ - public $fileName; - - /** - * Size of the image file. - * - * Required. - * - * @var int|null - */ - public $fileSize; - - /** - * The image's HTTP URI. - * - * @var string|null - */ - public $uri; - - /** - * External image ID (required by REST for now, see https://jira.ez.no/browse/EZP-20831). - * - * @var mixed|null - */ - public $imageId; - - /** - * Input image file URI. - * - * @var string|null - */ - public $inputUri; - - /** - * Original image width. - * - * @var int|null - */ - public $width; - - /** - * Original image height. - * - * @var int|null - */ - public $height; - - /** @var string[] */ - public $additionalData = []; - - /** - * Construct a new Value object. - */ - public function __construct(array $imageData = []) - { - foreach ($imageData as $key => $value) { - try { - $this->$key = $value; - } catch (PropertyNotFoundException $e) { - throw new InvalidArgumentType( - sprintf('Image\Value::$%s', $key), - 'Existing property', - $value - ); - } - } - } - - public function isAlternativeTextEmpty(): bool - { - return $this->alternativeText === null || trim($this->alternativeText) === ''; - } - - /** - * Creates a value only from a file path. - * - * @param string $path - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType - * - * @return Value - * - * @deprecated Starting with 5.3.3, handled by Image\Type::acceptValue() - */ - public static function fromString($path) - { - if (!file_exists($path)) { - throw new InvalidArgumentType( - '$path', - 'existing file', - $path - ); - } - - return new static( - [ - 'inputUri' => $path, - 'fileName' => basename($path), - 'fileSize' => filesize($path), - ] - ); - } - - /** - * Returns the image file size in byte. - * - * @return int - */ - public function getFileSize() - { - return $this->fileSize; - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - return (string)$this->fileName; - } - - public function __get($propertyName) - { - if ($propertyName === 'path') { - return $this->inputUri ?: $this->id; - } - - throw new PropertyNotFoundException($propertyName, static::class); - } - - public function __set($propertyName, $propertyValue) - { - if ($propertyName === 'path') { - $this->inputUri = $propertyValue; - - return; - } - - throw new PropertyNotFoundException($propertyName, static::class); - } -} diff --git a/eZ/Publish/Core/FieldType/ImageAsset/AssetMapper.php b/eZ/Publish/Core/FieldType/ImageAsset/AssetMapper.php deleted file mode 100644 index 52b2b18aa8..0000000000 --- a/eZ/Publish/Core/FieldType/ImageAsset/AssetMapper.php +++ /dev/null @@ -1,188 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\FieldType\ImageAsset; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Image\Value as ImageValue; -use eZ\Publish\Core\MVC\ConfigResolverInterface; - -class AssetMapper -{ - /** @var \eZ\Publish\API\Repository\ContentService */ - private $contentService; - - /** @var \eZ\Publish\API\Repository\LocationService */ - private $locationService; - - /** @var \eZ\Publish\API\Repository\ContentTypeService */ - private $contentTypeService; - - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ - private $configResolver; - - /** @var int */ - private $contentTypeId = null; - - public function __construct( - ContentService $contentService, - LocationService $locationService, - ContentTypeService $contentTypeService, - ConfigResolverInterface $configResolver - ) { - $this->contentService = $contentService; - $this->locationService = $locationService; - $this->contentTypeService = $contentTypeService; - $this->configResolver = $configResolver; - } - - /** - * Creates an Image Asset. - * - * @param string $name - * @param \eZ\Publish\Core\FieldType\Image\Value $image - * @param string $languageCode - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function createAsset(string $name, ImageValue $image, string $languageCode): Content - { - $mappings = $this->getMappings(); - - $contentType = $this->contentTypeService->loadContentTypeByIdentifier( - $mappings['content_type_identifier'] - ); - - $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, $languageCode); - $contentCreateStruct->setField($mappings['name_field_identifier'], $name); - $contentCreateStruct->setField($mappings['content_field_identifier'], $image); - - $contentDraft = $this->contentService->createContent($contentCreateStruct, [ - $this->locationService->newLocationCreateStruct($mappings['parent_location_id']), - ]); - - return $this->contentService->publishVersion($contentDraft->versionInfo); - } - - /** - * Returns field which is used to store the Image Asset value from specified content. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @return \eZ\Publish\API\Repository\Values\Content\Field - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function getAssetField(Content $content): Field - { - if (!$this->isAsset($content)) { - throw new InvalidArgumentException('contentId', "Content {$content->id} is not an image asset."); - } - - return $content->getField($this->getContentFieldIdentifier()); - } - - /** - * Returns definition of the field which is used to store value of the Image Asset. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition - */ - public function getAssetFieldDefinition(): FieldDefinition - { - $mappings = $this->getMappings(); - - $contentType = $this->contentTypeService->loadContentTypeByIdentifier( - $mappings['content_type_identifier'] - ); - - return $contentType->getFieldDefinition( - $mappings['content_field_identifier'] - ); - } - - /** - * Returns field value of the Image Asset from specified content. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @return \eZ\Publish\Core\FieldType\Image\Value - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function getAssetValue(Content $content): ImageValue - { - if (!$this->isAsset($content)) { - throw new InvalidArgumentException('contentId', "Content {$content->id} is not an image asset."); - } - - return $content->getFieldValue($this->getContentFieldIdentifier()); - } - - /** - * Returns TRUE if content is an Image Asset. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @return bool - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function isAsset(Content $content): bool - { - if ($this->contentTypeId === null) { - $contentType = $this->contentTypeService->loadContentTypeByIdentifier( - $this->getContentTypeIdentifier() - ); - - $this->contentTypeId = $contentType->id; - } - - return $content->contentInfo->contentTypeId === $this->contentTypeId; - } - - /** - * Return identifier of the Content Type used as Assets. - */ - public function getContentTypeIdentifier(): string - { - return $this->getMappings()['content_type_identifier']; - } - - /** - * Return identifier of the field used to store Image Asset value. - * - * @return string - */ - public function getContentFieldIdentifier(): string - { - return $this->getMappings()['content_field_identifier']; - } - - /** - * Return ID of the base location for the Image Assets. - * - * @return int - */ - public function getParentLocationId(): int - { - return $this->getMappings()['parent_location_id']; - } - - protected function getMappings(): array - { - return $this->configResolver->getParameter('fieldtypes.ezimageasset.mappings'); - } -} diff --git a/eZ/Publish/Core/FieldType/ImageAsset/ImageAssetThumbnailStrategy.php b/eZ/Publish/Core/FieldType/ImageAsset/ImageAssetThumbnailStrategy.php deleted file mode 100644 index 90c1a94f1b..0000000000 --- a/eZ/Publish/Core/FieldType/ImageAsset/ImageAssetThumbnailStrategy.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\FieldType\ImageAsset; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field\FieldTypeBasedThumbnailStrategy; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy as ContentThumbnailStrategy; - -class ImageAssetThumbnailStrategy implements FieldTypeBasedThumbnailStrategy -{ - /** @var string */ - private $fieldTypeIdentifier; - - /** @var \eZ\Publish\API\Repository\ContentService */ - private $contentService; - - /** @var \eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy */ - private $thumbnailStrategy; - - public function __construct( - string $fieldTypeIdentifier, - ContentThumbnailStrategy $thumbnailStrategy, - ContentService $contentService - ) { - $this->fieldTypeIdentifier = $fieldTypeIdentifier; - $this->contentService = $contentService; - $this->thumbnailStrategy = $thumbnailStrategy; - } - - public function getFieldTypeIdentifier(): string - { - return $this->fieldTypeIdentifier; - } - - public function getThumbnail(Field $field, ?VersionInfo $versionInfo = null): ?Thumbnail - { - try { - $content = $this->contentService->loadContent( - (int) $field->value->destinationContentId, - null, - $versionInfo ? $versionInfo->versionNo : null - ); - } catch (NotFoundException $e) { - return null; - } - - return $this->thumbnailStrategy->getThumbnail( - $content->getContentType(), - $content->getFields(), - $content->versionInfo - ); - } -} diff --git a/eZ/Publish/Core/FieldType/ImageAsset/SearchField.php b/eZ/Publish/Core/FieldType/ImageAsset/SearchField.php deleted file mode 100644 index 78cd11a43c..0000000000 --- a/eZ/Publish/Core/FieldType/ImageAsset/SearchField.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\FieldType\ImageAsset; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for Image Asset field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition): array - { - return [ - new Search\Field( - 'value', - reset($field->value->data), - new Search\FieldType\StringField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition(): array - { - return [ - 'value' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField(): string - { - return 'value'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField(): string - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/ImageAsset/Type.php b/eZ/Publish/Core/FieldType/ImageAsset/Type.php deleted file mode 100644 index 38bbff14ba..0000000000 --- a/eZ/Publish/Core/FieldType/ImageAsset/Type.php +++ /dev/null @@ -1,290 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\FieldType\ImageAsset; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\Handler as SPIContentHandler; - -class Type extends FieldType -{ - public const FIELD_TYPE_IDENTIFIER = 'ezimageasset'; - - /** @var \eZ\Publish\API\Repository\ContentService */ - private $contentService; - - /** @var \eZ\Publish\API\Repository\ContentTypeService */ - private $contentTypeService; - - /** @var \eZ\Publish\Core\FieldType\ImageAsset\AssetMapper */ - private $assetMapper; - - /** @var \eZ\Publish\SPI\Persistence\Content\Handler */ - private $handler; - - /** - * @param \eZ\Publish\API\Repository\ContentService $contentService - * @param \eZ\Publish\API\Repository\ContentTypeService $contentTypeService - * @param \eZ\Publish\Core\FieldType\ImageAsset\AssetMapper $mapper - * @param \eZ\Publish\SPI\Persistence\Content\Handler $handler - */ - public function __construct( - ContentService $contentService, - ContentTypeService $contentTypeService, - AssetMapper $mapper, - SPIContentHandler $handler - ) { - $this->contentService = $contentService; - $this->contentTypeService = $contentTypeService; - $this->assetMapper = $mapper; - $this->handler = $handler; - } - - /** - * Validates a field based on the validators in the field definition. - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field - * @param \eZ\Publish\Core\FieldType\ImageAsset\Value $fieldValue The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue): array - { - $errors = []; - - if ($this->isEmptyValue($fieldValue)) { - return $errors; - } - - $content = $this->contentService->loadContent( - (int)$fieldValue->destinationContentId - ); - - if (!$this->assetMapper->isAsset($content)) { - $currentContentType = $this->contentTypeService->loadContentType( - (int)$content->contentInfo->contentTypeId - ); - - $errors[] = new ValidationError( - 'Content %type% is not a valid asset target', - null, - [ - '%type%' => $currentContentType->identifier, - ], - 'destinationContentId' - ); - } - - return $errors; - } - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier(): string - { - return self::FIELD_TYPE_IDENTIFIER; - } - - /** - * @param \eZ\Publish\Core\FieldType\ImageAsset\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - if (empty($value->destinationContentId)) { - return ''; - } - - try { - $contentInfo = $this->handler->loadContentInfo($value->destinationContentId); - $versionInfo = $this->handler->loadVersionInfo($value->destinationContentId, $contentInfo->currentVersionNo); - } catch (NotFoundException $e) { - return ''; - } - - return $versionInfo->names[$languageCode] ?? $versionInfo->names[$contentInfo->mainLanguageCode]; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\ImageAsset\Value - */ - public function getEmptyValue(): Value - { - return new Value(); - } - - /** - * Returns if the given $value is considered empty by the field type. - * - * @param mixed $value - * - * @return bool - */ - public function isEmptyValue(SPIValue $value): bool - { - return null === $value->destinationContentId; - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param int|string|\eZ\Publish\API\Repository\Values\Content\ContentInfo|\eZ\Publish\Core\FieldType\Relation\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\ImageAsset\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if ($inputValue instanceof ContentInfo) { - $inputValue = new Value($inputValue->id); - } elseif (is_int($inputValue) || is_string($inputValue)) { - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\ImageAsset\Value $value - */ - protected function checkValueStructure(BaseValue $value): void - { - if (!is_int($value->destinationContentId) && !is_string($value->destinationContentId)) { - throw new InvalidArgumentType( - '$value->destinationContentId', - 'string|int', - $value->destinationContentId - ); - } - - if ($value->alternativeText !== null && !is_string($value->alternativeText)) { - throw new InvalidArgumentType( - '$value->alternativeText', - 'string|null', - $value->alternativeText - ); - } - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * For this FieldType, the related object's name is returned. - * - * @param \eZ\Publish\Core\FieldType\Relation\Value $value - * - * @return bool - */ - protected function getSortInfo(BaseValue $value): bool - { - return false; - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\ImageAsset\Value $value - */ - public function fromHash($hash): Value - { - if (!$hash) { - return new Value(); - } - - $destinationContentId = $hash['destinationContentId']; - if ($destinationContentId !== null) { - $destinationContentId = (int)$destinationContentId; - } - - return new Value($destinationContentId, $hash['alternativeText']); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\ImageAsset\Value $value - * - * @return array - */ - public function toHash(SPIValue $value): array - { - $destinationContentId = null; - if ($value->destinationContentId !== null) { - $destinationContentId = (int)$value->destinationContentId; - } - - return [ - 'destinationContentId' => $destinationContentId, - 'alternativeText' => $value->alternativeText, - ]; - } - - /** - * Returns relation data extracted from value. - * - * Not intended for \eZ\Publish\API\Repository\Values\Content\Relation::COMMON type relations, - * there is an API for handling those. - * - * @param \eZ\Publish\Core\FieldType\ImageAsset\Value $fieldValue - * - * @return array Hash with relation type as key and array of destination content ids as value. - * - * Example: - * <code> - * array( - * \eZ\Publish\API\Repository\Values\Content\Relation::LINK => array( - * "contentIds" => array( 12, 13, 14 ), - * "locationIds" => array( 24 ) - * ), - * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED => array( - * "contentIds" => array( 12 ), - * "locationIds" => array( 24, 45 ) - * ), - * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD => array( 12 ) - * ) - * </code> - */ - public function getRelations(SPIValue $fieldValue): array - { - $relations = []; - if ($fieldValue->destinationContentId !== null) { - $relations[Relation::ASSET] = [$fieldValue->destinationContentId]; - } - - return $relations; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable(): bool - { - return true; - } -} diff --git a/eZ/Publish/Core/FieldType/ImageAsset/Value.php b/eZ/Publish/Core/FieldType/ImageAsset/Value.php deleted file mode 100644 index 4525c05d83..0000000000 --- a/eZ/Publish/Core/FieldType/ImageAsset/Value.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\FieldType\ImageAsset; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -class Value extends BaseValue -{ - /** - * Related content id's. - * - * @var mixed|null - */ - public $destinationContentId; - - /** - * The alternative image text (for example "Picture of an apple."). - * - * @var string|null - */ - public $alternativeText; - - /** - * @param mixed|null $destinationContentId - * @param string|null $alternativeText - */ - public function __construct($destinationContentId = null, ?string $alternativeText = null) - { - parent::__construct([ - 'destinationContentId' => $destinationContentId, - 'alternativeText' => $alternativeText, - ]); - } - - /** - * {@inheritdoc} - */ - public function __toString() - { - return (string) $this->destinationContentId; - } -} diff --git a/eZ/Publish/Core/FieldType/Integer/SearchField.php b/eZ/Publish/Core/FieldType/Integer/SearchField.php deleted file mode 100644 index 6774ae7f63..0000000000 --- a/eZ/Publish/Core/FieldType/Integer/SearchField.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Integer; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for Integer field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value', - $field->value->data, - new Search\FieldType\IntegerField() - ), - new Search\Field( - 'fulltext', - $field->value->data, - new Search\FieldType\FullTextField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value' => new Search\FieldType\IntegerField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/Integer/Type.php b/eZ/Publish/Core/FieldType/Integer/Type.php deleted file mode 100644 index 135e9639ed..0000000000 --- a/eZ/Publish/Core/FieldType/Integer/Type.php +++ /dev/null @@ -1,271 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Integer; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -/** - * Integer field types. - * - * Represents integers. - */ -class Type extends FieldType -{ - protected $validatorConfigurationSchema = [ - 'IntegerValueValidator' => [ - 'minIntegerValue' => [ - 'type' => 'int', - 'default' => null, - ], - 'maxIntegerValue' => [ - 'type' => 'int', - 'default' => null, - ], - ], - ]; - - /** - * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $validatorConfiguration - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateValidatorConfiguration($validatorConfiguration) - { - $validationErrors = []; - - foreach ($validatorConfiguration as $validatorIdentifier => $constraints) { - if ($validatorIdentifier !== 'IntegerValueValidator') { - $validationErrors[] = new ValidationError( - "Validator '%validator%' is unknown", - null, - [ - '%validator%' => $validatorIdentifier, - ], - "[$validatorIdentifier]" - ); - - continue; - } - foreach ($constraints as $name => $value) { - switch ($name) { - case 'minIntegerValue': - case 'maxIntegerValue': - if ($value !== null && !is_int($value)) { - $validationErrors[] = new ValidationError( - "Validator parameter '%parameter%' value must be of integer type", - null, - [ - '%parameter%' => $name, - ], - "[$validatorIdentifier][$name]" - ); - } - break; - default: - $validationErrors[] = new ValidationError( - "Validator parameter '%parameter%' is unknown", - null, - [ - '%parameter%' => $name, - ], - "[$validatorIdentifier][$name]" - ); - } - } - } - - return $validationErrors; - } - - /** - * Validates a field based on the validators in the field definition. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field - * @param \eZ\Publish\Core\FieldType\Integer\Value $fieldValue The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) - { - $validationErrors = []; - - if ($this->isEmptyValue($fieldValue)) { - return $validationErrors; - } - - $validatorConfiguration = $fieldDefinition->getValidatorConfiguration(); - $constraints = isset($validatorConfiguration['IntegerValueValidator']) ? - $validatorConfiguration['IntegerValueValidator'] : - []; - - $validationErrors = []; - - // 0 and False are unlimited value for maxIntegerValue - if (isset($constraints['maxIntegerValue']) && - $constraints['maxIntegerValue'] !== 0 && - $constraints['maxIntegerValue'] !== false && - $fieldValue->value > $constraints['maxIntegerValue'] - ) { - $validationErrors[] = new ValidationError( - 'The value can not be higher than %size%.', - null, - [ - '%size%' => $constraints['maxIntegerValue'], - ], - 'value' - ); - } - - if (isset($constraints['minIntegerValue']) && - $constraints['minIntegerValue'] !== false && $fieldValue->value < $constraints['minIntegerValue']) { - $validationErrors[] = new ValidationError( - 'The value can not be lower than %size%.', - null, - [ - '%size%' => $constraints['minIntegerValue'], - ], - 'value' - ); - } - - return $validationErrors; - } - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezinteger'; - } - - /** - * @param \eZ\Publish\Core\FieldType\Integer\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return (string)$value; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Integer\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Returns if the given $value is considered empty by the field type. - * - * @param mixed $value - * - * @return bool - */ - public function isEmptyValue(SPIValue $value) - { - return $value->value === null; - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param int|\eZ\Publish\Core\FieldType\Integer\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\Integer\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_int($inputValue)) { - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Integer\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!is_int($value->value)) { - throw new InvalidArgumentType( - '$value->value', - 'integer', - $value->value - ); - } - } - - /** - * {@inheritdoc} - */ - protected function getSortInfo(BaseValue $value) - { - return $value->value; - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\Integer\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - return new Value($hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Integer\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - return $value->value; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } -} diff --git a/eZ/Publish/Core/FieldType/Integer/Value.php b/eZ/Publish/Core/FieldType/Integer/Value.php deleted file mode 100644 index 0f4ed3ac6a..0000000000 --- a/eZ/Publish/Core/FieldType/Integer/Value.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Integer; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for Integer field type. - */ -class Value extends BaseValue -{ - /** - * Content of the value. - * - * @var int|null - */ - public $value; - - /** - * Construct a new Value object and initialize with $value. - * - * @param int|null $value - */ - public function __construct($value = null) - { - $this->value = $value; - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - return (string)$this->value; - } -} diff --git a/eZ/Publish/Core/FieldType/Keyword/KeywordStorage.php b/eZ/Publish/Core/FieldType/Keyword/KeywordStorage.php deleted file mode 100644 index 9beb0ec7f1..0000000000 --- a/eZ/Publish/Core/FieldType/Keyword/KeywordStorage.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Keyword; - -use eZ\Publish\SPI\FieldType\GatewayBasedStorage; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Converter for Keyword field type external storage. - * - * The keyword storage ships a list (array) of keywords in - * $field->value->externalData. $field->value->data is simply empty, because no - * internal data is store. - */ -class KeywordStorage extends GatewayBasedStorage -{ - /** @var \eZ\Publish\Core\FieldType\Keyword\KeywordStorage\Gateway */ - protected $gateway; - - /** - * @see \eZ\Publish\SPI\FieldType\FieldStorage - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context - * - * @return mixed - */ - public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context) - { - $contentTypeId = $this->gateway->getContentTypeId($field); - - return $this->gateway->storeFieldData($field, $contentTypeId); - } - - /** - * Populates $field value property based on the external data. - * $field->value is a {@link eZ\Publish\SPI\Persistence\Content\FieldValue} object. - * This value holds the data as a {@link eZ\Publish\Core\FieldType\Value} based object, - * according to the field type (e.g. for TextLine, it will be a {@link eZ\Publish\Core\FieldType\TextLine\Value} object). - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context - */ - public function getFieldData(VersionInfo $versionInfo, Field $field, array $context) - { - // @todo: This should already retrieve the ContentType ID - return $this->gateway->getFieldData($field); - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param array $fieldIds - * @param array $context - * - * @return bool - */ - public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context) - { - foreach ($fieldIds as $fieldId) { - $this->gateway->deleteFieldData($fieldId, $versionInfo->versionNo); - } - - return true; - } - - /** - * Checks if field type has external data to deal with. - * - * @return bool - */ - public function hasFieldData() - { - return true; - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context - * - * @return \eZ\Publish\SPI\Search\Field[]|null - */ - public function getIndexData(VersionInfo $versionInfo, Field $field, array $context) - { - return null; - } -} diff --git a/eZ/Publish/Core/FieldType/Keyword/KeywordStorage/Gateway.php b/eZ/Publish/Core/FieldType/Keyword/KeywordStorage/Gateway.php deleted file mode 100644 index fdd165ed83..0000000000 --- a/eZ/Publish/Core/FieldType/Keyword/KeywordStorage/Gateway.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Keyword\KeywordStorage; - -use eZ\Publish\SPI\FieldType\StorageGateway; -use eZ\Publish\SPI\Persistence\Content\Field; - -/** - * Keyword Field Type external storage gateway. - */ -abstract class Gateway extends StorageGateway -{ - /** - * @see \eZ\Publish\SPI\FieldType\FieldStorage::storeFieldData() - */ - abstract public function storeFieldData(Field $field, $contentTypeId); - - /** - * Sets the list of assigned keywords into $field->value->externalData. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - */ - abstract public function getFieldData(Field $field); - - /** - * Retrieve the ContentType ID for the given $field. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * - * @return mixed - */ - abstract public function getContentTypeId(Field $field); - - /** - * @see \eZ\Publish\SPI\FieldType\FieldStorage::deleteFieldData() - */ - abstract public function deleteFieldData($fieldId, $versionNo); -} diff --git a/eZ/Publish/Core/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php b/eZ/Publish/Core/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php deleted file mode 100644 index 2a130b84b8..0000000000 --- a/eZ/Publish/Core/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php +++ /dev/null @@ -1,367 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Keyword\KeywordStorage\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ParameterType; -use eZ\Publish\Core\FieldType\Keyword\KeywordStorage\Gateway; -use eZ\Publish\SPI\Persistence\Content\Field; -use RuntimeException; - -class DoctrineStorage extends Gateway -{ - public const KEYWORD_TABLE = 'ezkeyword'; - public const KEYWORD_ATTRIBUTE_LINK_TABLE = 'ezkeyword_attribute_link'; - - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - public function __construct(Connection $connection) - { - $this->connection = $connection; - } - - /** - * Stores the keyword list from $field->value->externalData. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field - * @param int $contentTypeId - */ - public function storeFieldData(Field $field, $contentTypeId) - { - if (empty($field->value->externalData) && !empty($field->id)) { - $this->deleteFieldData($field->id, $field->versionNo); - - return; - } - - $existingKeywordMap = $this->getExistingKeywords( - $field->value->externalData, - $contentTypeId - ); - - $this->deleteOldKeywordAssignments($field->id, $field->versionNo); - - $this->assignKeywords( - $field->id, - $this->insertKeywords( - array_diff_key( - array_fill_keys($field->value->externalData, true), - $existingKeywordMap - ), - $contentTypeId - ) + $existingKeywordMap, - $field->versionNo - ); - - $this->deleteOrphanedKeywords(); - } - - /** - * Sets the list of assigned keywords into $field->value->externalData. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - */ - public function getFieldData(Field $field) - { - $field->value->externalData = $this->getAssignedKeywords($field->id, $field->versionNo); - } - - /** - * Retrieve the ContentType ID for the given $field. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * - * @return int - */ - public function getContentTypeId(Field $field) - { - return $this->loadContentTypeId($field->fieldDefinitionId); - } - - /** - * Deletes keyword data for the given $fieldId. - * - * @param int $fieldId - * @param int $versionNo - */ - public function deleteFieldData($fieldId, $versionNo) - { - $this->deleteOldKeywordAssignments($fieldId, $versionNo); - $this->deleteOrphanedKeywords(); - } - - /** - * Returns a list of keywords assigned to $fieldId. - */ - protected function getAssignedKeywords(int $fieldId, int $versionNo): array - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select($this->connection->quoteIdentifier('keyword')) - ->from($this->connection->quoteIdentifier(self::KEYWORD_TABLE), 'kwd') - ->innerJoin( - 'kwd', - $this->connection->quoteIdentifier(self::KEYWORD_ATTRIBUTE_LINK_TABLE), - 'attr', - $expr->eq( - $this->connection->quoteIdentifier('kwd.id'), - $this->connection->quoteIdentifier('attr.keyword_id') - ) - ) - ->where( - $expr->eq( - $this->connection->quoteIdentifier('attr.objectattribute_id'), - ':field_id' - ) - ) - ->andWhere( - $expr->eq( - $this->connection->quoteIdentifier('attr.version'), - ':version_no' - ) - ) - ->orderBy('kwd.id') - ->setParameter('field_id', $fieldId, ParameterType::INTEGER) - ->setParameter('version_no', $versionNo, ParameterType::INTEGER); - - return $query->execute()->fetchFirstColumn(); - } - - /** - * Retrieves the content type ID for the given $fieldDefinitionId. - * - * @param int $fieldDefinitionId - * - * @return int - */ - protected function loadContentTypeId($fieldDefinitionId) - { - $query = $this->connection->createQueryBuilder(); - $query - ->select($this->connection->quoteIdentifier('contentclass_id')) - ->from($this->connection->quoteIdentifier('ezcontentclass_attribute')) - ->where( - $query->expr()->eq('id', ':fieldDefinitionId') - ) - ->setParameter(':fieldDefinitionId', $fieldDefinitionId); - - $statement = $query->execute(); - - $row = $statement->fetch(\PDO::FETCH_ASSOC); - - if ($row === false) { - throw new RuntimeException( - sprintf( - 'Content Type ID cannot be retrieved based on the Field definition ID "%s"', - $fieldDefinitionId - ) - ); - } - - return (int) $row['contentclass_id']; - } - - /** - * Returns already existing keywords from $keywordList as a map. - * - * The map has the following format: - * <code> - * array( - * '<keyword>' => <id>, - * // ... - * ); - * </code> - * - * @param string[] $keywordList - * @param int $contentTypeId - * - * @return int[] - */ - protected function getExistingKeywords($keywordList, $contentTypeId) - { - // Retrieving potentially existing keywords - $query = $this->connection->createQueryBuilder(); - $query - ->select( - $this->connection->quoteIdentifier('id'), - $this->connection->quoteIdentifier('keyword') - ) - ->from($this->connection->quoteIdentifier(self::KEYWORD_TABLE)) - ->where( - $query->expr()->andX( - $query->expr()->in( - $this->connection->quoteIdentifier('keyword'), - ':keywordList' - ), - $query->expr()->eq( - $this->connection->quoteIdentifier('class_id'), - ':contentTypeId' - ) - ) - ) - ->setParameter(':keywordList', $keywordList, Connection::PARAM_STR_ARRAY) - ->setParameter(':contentTypeId', $contentTypeId); - - $statement = $query->execute(); - - $existingKeywordMap = []; - foreach ($statement->fetchAll(\PDO::FETCH_ASSOC) as $row) { - // filter out keywords that aren't the exact match (e.g. differ by case) - if (!in_array($row['keyword'], $keywordList)) { - continue; - } - $existingKeywordMap[$row['keyword']] = $row['id']; - } - - return $existingKeywordMap; - } - - /** - * Inserts $keywordsToInsert for $fieldDefinitionId and returns a map of - * these keywords to their ID. - * - * The returned array has the following format: - * <code> - * array( - * '<keyword>' => <id>, - * // ... - * ); - * </code> - * - * @param string[] $keywordsToInsert - * @param int $contentTypeId - * - * @return int[] - */ - protected function insertKeywords(array $keywordsToInsert, $contentTypeId) - { - $keywordIdMap = []; - // Inserting keywords not yet registered - if (!empty($keywordsToInsert)) { - $insertQuery = $this->connection->createQueryBuilder(); - $insertQuery - ->insert($this->connection->quoteIdentifier(self::KEYWORD_TABLE)) - ->values( - [ - $this->connection->quoteIdentifier('class_id') => ':contentTypeId', - $this->connection->quoteIdentifier('keyword') => ':keyword', - ] - ) - ->setParameter(':contentTypeId', $contentTypeId, \PDO::PARAM_INT); - - foreach (array_keys($keywordsToInsert) as $keyword) { - $insertQuery->setParameter(':keyword', $keyword); - $insertQuery->execute(); - $keywordIdMap[$keyword] = (int)$this->connection->lastInsertId( - $this->getSequenceName(self::KEYWORD_TABLE, 'id') - ); - } - } - - return $keywordIdMap; - } - - protected function deleteOldKeywordAssignments(int $fieldId, int $versionNo): void - { - $deleteQuery = $this->connection->createQueryBuilder(); - $deleteQuery - ->delete($this->connection->quoteIdentifier(self::KEYWORD_ATTRIBUTE_LINK_TABLE)) - ->where( - $deleteQuery->expr()->andX( - $deleteQuery->expr()->eq( - $this->connection->quoteIdentifier('objectattribute_id'), - ':fieldId' - ), - $deleteQuery->expr()->eq( - $this->connection->quoteIdentifier('version'), - ':versionNo' - ) - ) - ) - ->setParameter('fieldId', $fieldId, ParameterType::INTEGER) - ->setParameter('versionNo', $versionNo, ParameterType::INTEGER); - - $deleteQuery->execute(); - } - - /** - * Assigns keywords from $keywordMap to the field with $fieldId and specific $versionNo. - * - * $keywordMap has the format: - * <code> - * array( - * '<keyword>' => <id>, - * // ... - * ); - * </code> - */ - protected function assignKeywords(int $fieldId, array $keywordMap, int $versionNo): void - { - $insertQuery = $this->connection->createQueryBuilder(); - $insertQuery - ->insert($this->connection->quoteIdentifier(self::KEYWORD_ATTRIBUTE_LINK_TABLE)) - ->values( - [ - $this->connection->quoteIdentifier('keyword_id') => ':keywordId', - $this->connection->quoteIdentifier('objectattribute_id') => ':fieldId', - $this->connection->quoteIdentifier('version') => ':versionNo', - ] - ) - ; - - foreach ($keywordMap as $keyword => $keywordId) { - $insertQuery - ->setParameter('keywordId', $keywordId, ParameterType::INTEGER) - ->setParameter('fieldId', $fieldId, ParameterType::INTEGER) - ->setParameter('versionNo', $versionNo, ParameterType::INTEGER); - $insertQuery->execute(); - } - } - - /** - * Deletes all orphaned keywords. - * - * Keyword is orphaned if it is not linked to a content attribute through - * ezkeyword_attribute_link table. - */ - protected function deleteOrphanedKeywords() - { - $query = $this->connection->createQueryBuilder(); - $query - ->select($this->connection->quoteIdentifier('kwd.id')) - ->from($this->connection->quoteIdentifier(self::KEYWORD_TABLE), 'kwd') - ->leftJoin( - 'kwd', - $this->connection->quoteIdentifier(self::KEYWORD_ATTRIBUTE_LINK_TABLE), - 'attr', - $query->expr()->eq( - $this->connection->quoteIdentifier('attr.keyword_id'), - $this->connection->quoteIdentifier('kwd.id') - ) - ) - ->where($query->expr()->isNull('attr.id')); - - $statement = $query->execute(); - $ids = $statement->fetchAll(\PDO::FETCH_COLUMN); - - if (empty($ids)) { - return; - } - - $deleteQuery = $this->connection->createQueryBuilder(); - $deleteQuery - ->delete($this->connection->quoteIdentifier(self::KEYWORD_TABLE)) - ->where( - $deleteQuery->expr()->in($this->connection->quoteIdentifier('id'), ':ids') - ) - ->setParameter(':ids', $ids, Connection::PARAM_INT_ARRAY); - - $deleteQuery->execute(); - } -} diff --git a/eZ/Publish/Core/FieldType/Keyword/SearchField.php b/eZ/Publish/Core/FieldType/Keyword/SearchField.php deleted file mode 100644 index dc2b8ef8e7..0000000000 --- a/eZ/Publish/Core/FieldType/Keyword/SearchField.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Keyword; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for Keyword field type. - */ -class SearchField implements Indexable -{ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value', - $field->value->externalData, - new Search\FieldType\MultipleStringField() - ), - new Search\Field( - 'sort_value', - implode(' ', $field->value->externalData), - new Search\FieldType\StringField() - ), - new Search\Field( - 'fulltext', - $field->value->externalData, - new Search\FieldType\FullTextField() - ), - ]; - } - - public function getIndexDefinition() - { - return [ - 'value' => new Search\FieldType\MultipleStringField(), - 'sort_value' => new Search\FieldType\StringField(), - ]; - } - - public function getDefaultMatchField() - { - return 'value'; - } - - public function getDefaultSortField() - { - return 'sort_value'; - } -} diff --git a/eZ/Publish/Core/FieldType/Keyword/Type.php b/eZ/Publish/Core/FieldType/Keyword/Type.php deleted file mode 100644 index edb4c1dd98..0000000000 --- a/eZ/Publish/Core/FieldType/Keyword/Type.php +++ /dev/null @@ -1,161 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Keyword; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; - -/** - * Keyword field types. - * - * Represents keywords. - */ -class Type extends FieldType -{ - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezkeyword'; - } - - /** - * @param \eZ\Publish\Core\FieldType\Keyword\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return implode(', ', $value->values); - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Keyword\Value - */ - public function getEmptyValue() - { - return new Value([]); - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param mixed $inputValue - * - * @return \eZ\Publish\Core\FieldType\Keyword\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_string($inputValue)) { - $inputValue = [$inputValue]; - } - - if (is_array($inputValue)) { - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Keyword\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!is_array($value->values)) { - throw new InvalidArgumentType( - '$value->values', - 'array', - $value->values - ); - } - } - - /** - * {@inheritdoc} - */ - protected function getSortInfo(BaseValue $value) - { - return implode(',', $value->values); - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\Keyword\Value $value - */ - public function fromHash($hash) - { - return new Value($hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Keyword\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - return $value->values; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } - - /** - * Converts a $value to a persistence value. - * - * @param \eZ\Publish\Core\FieldType\Keyword\Value $value - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function toPersistenceValue(SPIValue $value) - { - return new FieldValue( - [ - 'data' => null, - 'externalData' => $value->values, - 'sortKey' => $this->getSortInfo($value), - ] - ); - } - - /** - * Converts a persistence $fieldValue to a Value. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - * - * @return \eZ\Publish\Core\FieldType\Keyword\Value - */ - public function fromPersistenceValue(FieldValue $fieldValue) - { - return new Value($fieldValue->externalData); - } -} diff --git a/eZ/Publish/Core/FieldType/Keyword/Value.php b/eZ/Publish/Core/FieldType/Keyword/Value.php deleted file mode 100644 index 830503264e..0000000000 --- a/eZ/Publish/Core/FieldType/Keyword/Value.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Keyword; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for Keyword field type. - */ -class Value extends BaseValue -{ - /** - * Content of the value. - * - * @var string[] - */ - public $values = []; - - /** - * Construct a new Value object and initialize with $values. - * - * @param string[]|string $values - */ - public function __construct($values = null) - { - if ($values !== null) { - if (!is_array($values)) { - $tags = []; - foreach (explode(',', $values) as $tag) { - $tag = trim($tag); - if ($tag) { - $tags[] = $tag; - } - } - $values = $tags; - } - - $this->values = array_unique($values); - } - } - - /** - * Returns a string representation of the keyword value. - * - * @return string A comma separated list of tags, eg: "php, eZ Publish, html5" - */ - public function __toString() - { - return implode(', ', $this->values); - } -} diff --git a/eZ/Publish/Core/FieldType/MapLocation/MapLocationStorage.php b/eZ/Publish/Core/FieldType/MapLocation/MapLocationStorage.php deleted file mode 100644 index 3c8f77594d..0000000000 --- a/eZ/Publish/Core/FieldType/MapLocation/MapLocationStorage.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\MapLocation; - -use eZ\Publish\SPI\FieldType\GatewayBasedStorage; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Storage for the MapLocation field type. - */ -class MapLocationStorage extends GatewayBasedStorage -{ - /** @var \eZ\Publish\Core\FieldType\MapLocation\MapLocationStorage\Gateway */ - protected $gateway; - - /** - * @see \eZ\Publish\SPI\FieldType\FieldStorage - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context - * - * @return mixed - */ - public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context) - { - return $this->gateway->storeFieldData($versionInfo, $field); - } - - /** - * Populates $field value property based on the external data. - * $field->value is a {@link eZ\Publish\SPI\Persistence\Content\FieldValue} object. - * This value holds the data as a {@link eZ\Publish\Core\FieldType\Value} based object, - * according to the field type (e.g. for TextLine, it will be a {@link eZ\Publish\Core\FieldType\TextLine\Value} object). - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context - */ - public function getFieldData(VersionInfo $versionInfo, Field $field, array $context) - { - $this->gateway->getFieldData($versionInfo, $field); - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param array $fieldIds - * @param array $context - * - * @return bool - */ - public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context) - { - $this->gateway->deleteFieldData($versionInfo, $fieldIds); - } - - /** - * Checks if field type has external data to deal with. - * - * @return bool - */ - public function hasFieldData() - { - return true; - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context - * - * @return \eZ\Publish\SPI\Search\Field[]|null - */ - public function getIndexData(VersionInfo $versionInfo, Field $field, array $context) - { - return is_array($field->value->externalData) ? $field->value->externalData['address'] : null; - } -} diff --git a/eZ/Publish/Core/FieldType/MapLocation/MapLocationStorage/Gateway.php b/eZ/Publish/Core/FieldType/MapLocation/MapLocationStorage/Gateway.php deleted file mode 100644 index 2113eabff9..0000000000 --- a/eZ/Publish/Core/FieldType/MapLocation/MapLocationStorage/Gateway.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\MapLocation\MapLocationStorage; - -use eZ\Publish\SPI\FieldType\StorageGateway; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * MapLocation Field Type external storage gateway. - */ -abstract class Gateway extends StorageGateway -{ - /** - * Stores the data stored in the given $field. - * - * Potentially rewrites data in $field and returns true, if the $field - * needs to be updated in the database. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * - * @return bool If restoring of the internal field data is required - */ - abstract public function storeFieldData(VersionInfo $versionInfo, Field $field); - - /** - * Sets the loaded field data into $field->externalData. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * - * @return array - */ - abstract public function getFieldData(VersionInfo $versionInfo, Field $field); - - /** - * Deletes the data for all given $fieldIds. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param array $fieldIds - */ - abstract public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds); -} diff --git a/eZ/Publish/Core/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php b/eZ/Publish/Core/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php deleted file mode 100644 index 91ddb4a15b..0000000000 --- a/eZ/Publish/Core/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php +++ /dev/null @@ -1,240 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\MapLocation\MapLocationStorage\Gateway; - -use Doctrine\DBAL\Connection; -use eZ\Publish\Core\FieldType\MapLocation\MapLocationStorage\Gateway; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use PDO; - -class DoctrineStorage extends Gateway -{ - public const MAP_LOCATION_TABLE = 'ezgmaplocation'; - - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - public function __construct(Connection $connection) - { - $this->connection = $connection; - } - - /** - * Store the data stored in the given $field. - * - * Potentially rewrites data in $field and returns true, if the $field - * needs to be updated in the database. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * - * @return bool If restoring of the internal field data is required - */ - public function storeFieldData(VersionInfo $versionInfo, Field $field) - { - if ($field->value->externalData === null) { - // Store empty value and return - $this->deleteFieldData($versionInfo, [$field->id]); - $field->value->data = [ - 'sortKey' => null, - 'hasData' => false, - ]; - - return false; - } - - if ($this->hasFieldData($field->id, $versionInfo->versionNo)) { - $this->updateFieldData($versionInfo, $field); - } else { - $this->storeNewFieldData($versionInfo, $field); - } - - $field->value->data = [ - 'sortKey' => $field->value->externalData['address'], - 'hasData' => true, - ]; - - return true; - } - - /** - * Perform an update on the field data. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * - * @return bool - */ - protected function updateFieldData(VersionInfo $versionInfo, Field $field) - { - $updateQuery = $this->connection->createQueryBuilder(); - $updateQuery->update($this->connection->quoteIdentifier(self::MAP_LOCATION_TABLE)) - ->set($this->connection->quoteIdentifier('latitude'), ':latitude') - ->set($this->connection->quoteIdentifier('longitude'), ':longitude') - ->set($this->connection->quoteIdentifier('address'), ':address') - ->where( - $updateQuery->expr()->andX( - $updateQuery->expr()->eq( - $this->connection->quoteIdentifier('contentobject_attribute_id'), - ':fieldId' - ), - $updateQuery->expr()->eq( - $this->connection->quoteIdentifier('contentobject_version'), - ':versionNo' - ) - ) - ) - ->setParameter(':latitude', $field->value->externalData['latitude']) - ->setParameter(':longitude', $field->value->externalData['longitude']) - ->setParameter(':address', $field->value->externalData['address']) - ->setParameter(':fieldId', $field->id, PDO::PARAM_INT) - ->setParameter(':versionNo', $versionInfo->versionNo, PDO::PARAM_INT) - ; - - $updateQuery->execute(); - } - - /** - * Store new field data. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - */ - protected function storeNewFieldData(VersionInfo $versionInfo, Field $field) - { - $insertQuery = $this->connection->createQueryBuilder(); - $insertQuery - ->insert($this->connection->quoteIdentifier(self::MAP_LOCATION_TABLE)) - ->values([ - 'latitude' => ':latitude', - 'longitude' => ':longitude', - 'address' => ':address', - 'contentobject_attribute_id' => ':fieldId', - 'contentobject_version' => ':versionNo', - ]) - ->setParameter(':latitude', $field->value->externalData['latitude']) - ->setParameter(':longitude', $field->value->externalData['longitude']) - ->setParameter(':address', $field->value->externalData['address']) - ->setParameter(':fieldId', $field->id) - ->setParameter(':versionNo', $versionInfo->versionNo) - ; - - $insertQuery->execute(); - } - - /** - * Set the loaded field data into $field->externalData. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * - * @return array - */ - public function getFieldData(VersionInfo $versionInfo, Field $field) - { - $field->value->externalData = $this->loadFieldData($field->id, $versionInfo->versionNo); - } - - /** - * Return the data for the given $fieldId. - * - * If no data is found, null is returned. - * - * @param int $fieldId - * @param int $versionNo - * - * @return array|null - */ - protected function loadFieldData($fieldId, $versionNo) - { - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery - ->select( - $this->connection->quoteIdentifier('latitude'), - $this->connection->quoteIdentifier('longitude'), - $this->connection->quoteIdentifier('address') - ) - ->from($this->connection->quoteIdentifier('ezgmaplocation')) - ->where( - $selectQuery->expr()->andX( - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('contentobject_attribute_id'), - ':fieldId' - ), - $selectQuery->expr()->eq( - $this->connection->quoteIdentifier('contentobject_version'), - ':versionNo' - ) - ) - ) - ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) - ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) - ; - - $statement = $selectQuery->execute(); - - $rows = $statement->fetchAll(PDO::FETCH_ASSOC); - if (!isset($rows[0])) { - return null; - } - - // Cast coordinates as the DB can return them as strings - $rows[0]['latitude'] = (float)$rows[0]['latitude']; - $rows[0]['longitude'] = (float)$rows[0]['longitude']; - - return $rows[0]; - } - - /** - * Return if field data exists for $fieldId. - * - * @param int $fieldId - * @param int $versionNo - * - * @return bool - */ - protected function hasFieldData($fieldId, $versionNo) - { - return $this->loadFieldData($fieldId, $versionNo) !== null; - } - - /** - * Delete the data for all given $fieldIds. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param int[] $fieldIds - */ - public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds) - { - if (empty($fieldIds)) { - // Nothing to do - return; - } - - $deleteQuery = $this->connection->createQueryBuilder(); - $deleteQuery - ->delete($this->connection->quoteIdentifier(self::MAP_LOCATION_TABLE)) - ->where( - $deleteQuery->expr()->andX( - $deleteQuery->expr()->in( - $this->connection->quoteIdentifier('contentobject_attribute_id'), - ':fieldIds' - ), - $deleteQuery->expr()->eq( - $this->connection->quoteIdentifier('contentobject_version'), - ':versionNo' - ) - ) - ) - ->setParameter(':fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY) - ->setParameter(':versionNo', $versionInfo->versionNo, PDO::PARAM_INT) - ; - - $deleteQuery->execute(); - } -} diff --git a/eZ/Publish/Core/FieldType/MapLocation/SearchField.php b/eZ/Publish/Core/FieldType/MapLocation/SearchField.php deleted file mode 100644 index 4802fb0d45..0000000000 --- a/eZ/Publish/Core/FieldType/MapLocation/SearchField.php +++ /dev/null @@ -1,91 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\MapLocation; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for MapLocation field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value_address', - $field->value->externalData['address'] ?? null, - new Search\FieldType\StringField() - ), - new Search\Field( - 'value_location', - [ - 'latitude' => $field->value->externalData['latitude'] ?? null, - 'longitude' => $field->value->externalData['longitude'] ?? null, - ], - new Search\FieldType\GeoLocationField() - ), - new Search\Field( - 'fulltext', - $field->value->externalData['address'] ?? null, - new Search\FieldType\FullTextField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value_address' => new Search\FieldType\StringField(), - 'value_location' => new Search\FieldType\GeoLocationField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value_address'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/MapLocation/Type.php b/eZ/Publish/Core/FieldType/MapLocation/Type.php deleted file mode 100644 index 6548aef61d..0000000000 --- a/eZ/Publish/Core/FieldType/MapLocation/Type.php +++ /dev/null @@ -1,203 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\MapLocation; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; - -/** - * MapLocation field types. - * - * Represents keywords. - */ -class Type extends FieldType -{ - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezgmaplocation'; - } - - /** - * @param \eZ\Publish\Core\FieldType\MapLocation\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return (string)$value->address; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\MapLocation\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Returns if the given $value is considered empty by the field type. - * - * @param mixed $value - * - * @return bool - */ - public function isEmptyValue(SPIValue $value) - { - return $value->latitude === null && $value->longitude === null; - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param array|\eZ\Publish\Core\FieldType\MapLocation\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\MapLocation\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_array($inputValue)) { - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\MapLocation\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!is_float($value->latitude) && !is_int($value->latitude)) { - throw new InvalidArgumentType( - '$value->latitude', - 'float', - $value->latitude - ); - } - if (!is_float($value->longitude) && !is_int($value->longitude)) { - throw new InvalidArgumentType( - '$value->longitude', - 'float', - $value->longitude - ); - } - if (!is_string($value->address)) { - throw new InvalidArgumentType( - '$value->address', - 'string', - $value->address - ); - } - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * - * @param \eZ\Publish\Core\FieldType\MapLocation\Value $value - * - * @return string - */ - protected function getSortInfo(BaseValue $value) - { - return $this->transformationProcessor->transformByGroup((string)$value, 'lowercase'); - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\MapLocation\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - return new Value($hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\MapLocation\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - return [ - 'latitude' => $value->latitude, - 'longitude' => $value->longitude, - 'address' => $value->address, - ]; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } - - /** - * Converts a $value to a persistence value. - * - * @param \eZ\Publish\Core\FieldType\MapLocation\Value $value - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function toPersistenceValue(SPIValue $value) - { - return new FieldValue( - [ - 'data' => null, - 'externalData' => $this->toHash($value), - 'sortKey' => $this->getSortInfo($value), - ] - ); - } - - /** - * Converts a persistence $fieldValue to a Value. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - * - * @return \eZ\Publish\Core\FieldType\MapLocation\Value - */ - public function fromPersistenceValue(FieldValue $fieldValue) - { - if ($fieldValue->externalData === null) { - return $this->getEmptyValue(); - } - - return $this->fromHash($fieldValue->externalData); - } -} diff --git a/eZ/Publish/Core/FieldType/MapLocation/Value.php b/eZ/Publish/Core/FieldType/MapLocation/Value.php deleted file mode 100644 index c2fe9e13f4..0000000000 --- a/eZ/Publish/Core/FieldType/MapLocation/Value.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\MapLocation; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for MapLocation field type. - */ -class Value extends BaseValue -{ - /** - * Latitude of the location. - * - * @var float|null - */ - public $latitude; - - /** - * Longitude of the location. - * - * @var float|null - */ - public $longitude; - - /** - * Display address for the location. - * - * @var string|null - */ - public $address; - - /** - * Construct a new Value object and initialize with $values. - * - * @param string[]|string $values - */ - public function __construct(array $values = null) - { - foreach ((array)$values as $key => $value) { - $this->$key = $value; - } - } - - /** - * Returns a string representation of the keyword value. - * - * @return string A comma separated list of tags, eg: "php, eZ Publish, html5" - */ - public function __toString() - { - if (is_array($this->address)) { - return implode(', ', $this->address); - } - - return (string)$this->address; - } -} diff --git a/eZ/Publish/Core/FieldType/Media/MediaStorage.php b/eZ/Publish/Core/FieldType/Media/MediaStorage.php deleted file mode 100644 index 5679eef2ae..0000000000 --- a/eZ/Publish/Core/FieldType/Media/MediaStorage.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Media; - -use eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage; - -/** - * Description of MediaStorage. - */ -class MediaStorage extends BinaryBaseStorage -{ -} diff --git a/eZ/Publish/Core/FieldType/Media/MediaStorage/Gateway/DoctrineStorage.php b/eZ/Publish/Core/FieldType/Media/MediaStorage/Gateway/DoctrineStorage.php deleted file mode 100644 index fb15594699..0000000000 --- a/eZ/Publish/Core/FieldType/Media/MediaStorage/Gateway/DoctrineStorage.php +++ /dev/null @@ -1,144 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Media\MediaStorage\Gateway; - -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage\Gateway\DoctrineStorage as BaseDoctrineStorage; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use PDO; - -/** - * Media Field Type external storage DoctrineStorage gateway. - */ -class DoctrineStorage extends BaseDoctrineStorage -{ - /** - * {@inheritdoc} - */ - protected function getStorageTable() - { - return 'ezmedia'; - } - - /** - * {@inheritdoc} - */ - protected function getPropertyMapping() - { - $propertyMap = parent::getPropertyMapping(); - $propertyMap['has_controller'] = [ - 'name' => 'hasController', - 'cast' => static function ($val) { - return (bool)$val; - }, - ]; - $propertyMap['is_autoplay'] = [ - 'name' => 'autoplay', - 'cast' => static function ($val) { - return (bool)$val; - }, - ]; - $propertyMap['is_loop'] = [ - 'name' => 'loop', - 'cast' => static function ($val) { - return (bool)$val; - }, - ]; - $propertyMap['width'] = [ - 'name' => 'width', - 'cast' => 'intval', - ]; - $propertyMap['height'] = [ - 'name' => 'height', - 'cast' => 'intval', - ]; - - return $propertyMap; - } - - /** - * {@inheritdoc} - */ - protected function setFetchColumns(QueryBuilder $queryBuilder, $fieldId, $versionNo) - { - parent::setFetchColumns($queryBuilder, $fieldId, $versionNo); - - $queryBuilder->addSelect( - [ - $this->connection->quoteIdentifier('has_controller'), - $this->connection->quoteIdentifier('is_autoplay'), - $this->connection->quoteIdentifier('is_loop'), - $this->connection->quoteIdentifier('width'), - $this->connection->quoteIdentifier('height'), - ] - ); - } - - /** - * {@inheritdoc} - */ - protected function setInsertColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field) - { - parent::setInsertColumns($queryBuilder, $versionInfo, $field); - - $queryBuilder - ->setValue('controls', ':controls') - ->setValue('has_controller', ':hasController') - ->setValue('height', ':height') - ->setValue('is_autoplay', ':isAutoplay') - ->setValue('is_loop', ':isLoop') - ->setValue('pluginspage', ':pluginsPage') - ->setValue('quality', ':quality') - ->setValue('width', ':width') - ->setParameter(':controls', '') - ->setParameter( - ':hasController', - $field->value->externalData['hasController'], - PDO::PARAM_INT - ) - ->setParameter(':height', $field->value->externalData['height'], PDO::PARAM_INT) - ->setParameter(':isAutoplay', $field->value->externalData['autoplay'], PDO::PARAM_INT) - ->setParameter(':isLoop', $field->value->externalData['loop'], PDO::PARAM_INT) - ->setParameter(':pluginsPage', '') - ->setParameter(':quality', 'high') - ->setParameter(':width', $field->value->externalData['width'], PDO::PARAM_INT) - ; - } - - /** - * {@inheritdoc} - */ - protected function setUpdateColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field) - { - parent::setUpdateColumns($queryBuilder, $versionInfo, $field); - - $queryBuilder - ->set('controls', ':controls') - ->set('has_controller', ':hasController') - ->set('height', ':height') - ->set('is_autoplay', ':isAutoplay') - ->set('is_loop', ':isLoop') - ->set('pluginspage', ':pluginsPage') - ->set('quality', ':quality') - ->set('width', ':width') - ->setParameter(':controls', '') - ->setParameter( - ':hasController', - $field->value->externalData['hasController'], - ParameterType::INTEGER - ) - ->setParameter(':height', $field->value->externalData['height'], ParameterType::INTEGER) - ->setParameter(':isAutoplay', $field->value->externalData['autoplay'], ParameterType::INTEGER) - ->setParameter(':isLoop', $field->value->externalData['loop'], ParameterType::INTEGER) - ->setParameter(':pluginsPage', '') - ->setParameter(':quality', 'high') - ->setParameter(':width', $field->value->externalData['width'], ParameterType::INTEGER) - ; - } -} diff --git a/eZ/Publish/Core/FieldType/Media/SearchField.php b/eZ/Publish/Core/FieldType/Media/SearchField.php deleted file mode 100644 index a939510e23..0000000000 --- a/eZ/Publish/Core/FieldType/Media/SearchField.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Media; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for Media field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'file_name', - $field->value->externalData['fileName'] ?? null, - new Search\FieldType\StringField() - ), - new Search\Field( - 'file_size', - $field->value->externalData['fileSize'] ?? null, - new Search\FieldType\IntegerField() - ), - new Search\Field( - 'mime_type', - $field->value->externalData['mimeType'] ?? null, - new Search\FieldType\StringField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'file_name' => new Search\FieldType\StringField(), - 'file_size' => new Search\FieldType\IntegerField(), - 'mime_type' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'file_name'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/Media/Type.php b/eZ/Publish/Core/FieldType/Media/Type.php deleted file mode 100644 index 6ef565ce20..0000000000 --- a/eZ/Publish/Core/FieldType/Media/Type.php +++ /dev/null @@ -1,267 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Media; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\BinaryBase\Type as BaseType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; - -/** - * The TextLine field type. - * - * This field type represents a simple string. - */ -class Type extends BaseType -{ - /** - * List of possible media type settings. - */ - public const TYPE_FLASH = 'flash'; - public const TYPE_QUICKTIME = 'quick_time'; - public const TYPE_REALPLAYER = 'real_player'; - public const TYPE_SILVERLIGHT = 'silverlight'; - public const TYPE_WINDOWSMEDIA = 'windows_media_player'; - public const TYPE_HTML5_VIDEO = 'html5_video'; - public const TYPE_HTML5_AUDIO = 'html5_audio'; - - /** - * Type constants for validation. - */ - private static $availableTypes = [ - self::TYPE_FLASH, - self::TYPE_QUICKTIME, - self::TYPE_REALPLAYER, - self::TYPE_SILVERLIGHT, - self::TYPE_WINDOWSMEDIA, - self::TYPE_HTML5_VIDEO, - self::TYPE_HTML5_AUDIO, - ]; - - /** @var array */ - protected $settingsSchema = [ - 'mediaType' => [ - 'type' => 'choice', - 'default' => self::TYPE_HTML5_VIDEO, - ], - ]; - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezmedia'; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Media\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings) - { - $validationErrors = []; - - foreach ($fieldSettings as $name => $value) { - if (isset($this->settingsSchema[$name])) { - switch ($name) { - case 'mediaType': - if (!in_array($value, self::$availableTypes)) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' is of unknown type", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - break; - } - } else { - $validationErrors[] = new ValidationError( - "Setting '%setting%' is unknown", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - } - - return $validationErrors; - } - - /** - * Creates a specific value of the derived class from $inputValue. - * - * @param array $inputValue - * - * @return \eZ\Publish\Core\FieldType\Media\Value - */ - protected function createValue(array $inputValue) - { - $inputValue = $this->regenerateUri($inputValue); - - return new Value($inputValue); - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Media\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - parent::checkValueStructure($value); - - if (!is_bool($value->hasController)) { - throw new InvalidArgumentType( - '$value->hasController', - 'bool', - $value->hasController - ); - } - if (!is_bool($value->autoplay)) { - throw new InvalidArgumentType( - '$value->autoplay', - 'bool', - $value->autoplay - ); - } - if (!is_bool($value->loop)) { - throw new InvalidArgumentType( - '$value->loop', - 'bool', - $value->loop - ); - } - - if (!is_int($value->height)) { - throw new InvalidArgumentType( - '$value->height', - 'int', - $value->height - ); - } - if (!is_int($value->width)) { - throw new InvalidArgumentType( - '$value->width', - 'int', - $value->width - ); - } - } - - /** - * Attempts to complete the data in $value. - * - * @param \eZ\Publish\Core\FieldType\Media\Value|\eZ\Publish\Core\FieldType\Value $value - */ - protected function completeValue(BaseValue $value) - { - parent::completeValue($value); - - if (isset($value->hasController) && $value->hasController === null) { - $value->hasController = false; - } - if (isset($value->autoplay) && $value->autoplay === null) { - $value->autoplay = false; - } - if (isset($value->loop) && $value->loop === null) { - $value->loop = false; - } - - if (isset($value->height) && $value->height === null) { - $value->height = 0; - } - if (isset($value->width) && $value->width === null) { - $value->width = 0; - } - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Media\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - $hash = parent::toHash($value); - - $hash['hasController'] = $value->hasController; - $hash['autoplay'] = $value->autoplay; - $hash['loop'] = $value->loop; - $hash['width'] = $value->width; - $hash['height'] = $value->height; - - return $hash; - } - - /** - * Converts a persistence $fieldValue to a Value. - * - * This method builds a field type value from the $data and $externalData properties. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - * - * @return \eZ\Publish\Core\FieldType\Media\Value - */ - public function fromPersistenceValue(FieldValue $fieldValue) - { - if ($fieldValue->externalData === null) { - return $this->getEmptyValue(); - } - - $result = parent::fromPersistenceValue($fieldValue); - - $result->hasController = $fieldValue->externalData['hasController'] ?? false; - $result->autoplay = $fieldValue->externalData['autoplay'] ?? false; - $result->loop = $fieldValue->externalData['loop'] ?? false; - $result->height = $fieldValue->externalData['height'] ?? 0; - $result->width = $fieldValue->externalData['width'] ?? 0; - - return $result; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return false; - } -} diff --git a/eZ/Publish/Core/FieldType/Media/Value.php b/eZ/Publish/Core/FieldType/Media/Value.php deleted file mode 100644 index 12678f418b..0000000000 --- a/eZ/Publish/Core/FieldType/Media/Value.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Media; - -use eZ\Publish\Core\FieldType\BinaryBase\Value as BaseValue; - -/** - * Value for Media field type. - */ -class Value extends BaseValue -{ - /** - * If the media has a controller when being displayed. - * - * @var bool - */ - public $hasController = false; - - /** - * If the media should be automatically played. - * - * @var bool - */ - public $autoplay = false; - - /** - * If the media should be played in a loop. - * - * @var bool - */ - public $loop = false; - - /** - * Height of the media. - * - * @var int - */ - public $height = 0; - - /** - * Width of the media. - * - * @var int - */ - public $width = 0; -} diff --git a/eZ/Publish/Core/FieldType/Null/Type.php b/eZ/Publish/Core/FieldType/Null/Type.php deleted file mode 100644 index b79677a96c..0000000000 --- a/eZ/Publish/Core/FieldType/Null/Type.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Null; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -/** - * ATTENTION: For testing purposes only! - */ -class Type extends FieldType -{ - /** - * Identifier for the field type this stuff is mocking. - * - * @var string - */ - protected $fieldTypeIdentifier; - - /** - * Constructs field type object, initializing internal data structures. - * - * @param string $fieldTypeIdentifier - * - * @return \eZ\Publish\Core\FieldType\Null\Type - */ - public function __construct($fieldTypeIdentifier) - { - $this->fieldTypeIdentifier = $fieldTypeIdentifier; - } - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return $this->fieldTypeIdentifier; - } - - /** - * @param \eZ\Publish\Core\FieldType\Null\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return (string)$value->value; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Null\Value - */ - public function getEmptyValue() - { - return new Value(null); - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param \eZ\Publish\Core\FieldType\Null\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\Null\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Null\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - // Does nothing - } - - /** - * {@inheritdoc} - */ - protected function getSortInfo(BaseValue $value) - { - return null; - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\Null\Value $value - */ - public function fromHash($hash) - { - return new Value($hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Null\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if (isset($value->value)) { - return $value->value; - } - - return null; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } -} diff --git a/eZ/Publish/Core/FieldType/Null/Value.php b/eZ/Publish/Core/FieldType/Null/Value.php deleted file mode 100644 index e75a3126b5..0000000000 --- a/eZ/Publish/Core/FieldType/Null/Value.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Null; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for Null field type. - */ -class Value extends BaseValue -{ - /** - * Content of the value. - * - * @var mixed - */ - public $value = null; - - /** - * Construct a new Value object and initialize with $value. - * - * @param int $value - */ - public function __construct($value = null) - { - $this->value = $value; - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - return (string)$this->value; - } -} diff --git a/eZ/Publish/Core/FieldType/NullStorage.php b/eZ/Publish/Core/FieldType/NullStorage.php deleted file mode 100644 index b6c0892412..0000000000 --- a/eZ/Publish/Core/FieldType/NullStorage.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType; - -use eZ\Publish\SPI\FieldType\FieldStorage; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Description of NullStorage. - */ -class NullStorage implements FieldStorage -{ - /** - * @see \eZ\Publish\SPI\FieldType\FieldStorage::storeFieldData() - */ - public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context) - { - return false; - } - - /** - * @see \eZ\Publish\SPI\FieldType\FieldStorage::getFieldData() - */ - public function getFieldData(VersionInfo $versionInfo, Field $field, array $context) - { - return; - } - - /** - * @see \eZ\Publish\SPI\FieldType\FieldStorage::deleteFieldData() - */ - public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context) - { - return true; - } - - /** - * @see \eZ\Publish\SPI\FieldType\FieldStorage::hasFieldData() - * - * @return bool - */ - public function hasFieldData() - { - return false; - } - - /** - * @see \eZ\Publish\SPI\FieldType\FieldStorage::getIndexData() - */ - public function getIndexData(VersionInfo $versionInfo, Field $field, array $context) - { - return false; - } - - /** - * This method is used exclusively by Legacy Storage to copy external data of existing field in main language to - * the untranslatable field not passed in create or update struct, but created implicitly in storage layer. - * - * By default the method falls back to the {@link \eZ\Publish\SPI\FieldType\FieldStorage::storeFieldData()}. - * External storages implement this method as needed. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Field $originalField - * @param array $context - * - * @return bool|null Same as {@link \eZ\Publish\SPI\FieldType\FieldStorage::storeFieldData()}. - */ - public function copyLegacyField(VersionInfo $versionInfo, Field $field, Field $originalField, array $context) - { - return; - } -} diff --git a/eZ/Publish/Core/FieldType/Relation/SearchField.php b/eZ/Publish/Core/FieldType/Relation/SearchField.php deleted file mode 100644 index 339ddd6716..0000000000 --- a/eZ/Publish/Core/FieldType/Relation/SearchField.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Relation; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for Relation field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value', - reset($field->value->data), - new Search\FieldType\StringField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/Relation/Type.php b/eZ/Publish/Core/FieldType/Relation/Type.php deleted file mode 100644 index 304a43b328..0000000000 --- a/eZ/Publish/Core/FieldType/Relation/Type.php +++ /dev/null @@ -1,345 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Relation; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\Handler as SPIContentHandler; -use Ibexa\Core\Repository\Validator\TargetContentValidatorInterface; - -/** - * The Relation field type. - * - * This field type represents a relation to a content. - * - * hash format ({@see fromhash()}, {@see toHash()}): - * array( 'destinationContentId' => (int)$destinationContentId ); - * - * @deprecated Since 7.0 and will be removed in 8.0. Use `RelationList\Type` instead. - */ -class Type extends FieldType -{ - public const SELECTION_BROWSE = 0; - public const SELECTION_DROPDOWN = 1; - - protected $settingsSchema = [ - 'selectionMethod' => [ - 'type' => 'int', - 'default' => self::SELECTION_BROWSE, - ], - 'selectionRoot' => [ - 'type' => 'string', - 'default' => null, - ], - 'selectionContentTypes' => [ - 'type' => 'array', - 'default' => [], - ], - ]; - - /** @var \eZ\Publish\SPI\Persistence\Content\Handler */ - private $handler; - - /** @var \Ibexa\Core\Repository\Validator\TargetContentValidatorInterface */ - private $targetContentValidator; - - public function __construct( - SPIContentHandler $handler, - TargetContentValidatorInterface $targetContentValidator - ) { - $this->handler = $handler; - $this->targetContentValidator = $targetContentValidator; - } - - /** - * @see \eZ\Publish\Core\FieldType\FieldType::validateFieldSettings() - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings) - { - $validationErrors = []; - - foreach ($fieldSettings as $name => $value) { - if (!isset($this->settingsSchema[$name])) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' is unknown", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - continue; - } - - switch ($name) { - case 'selectionMethod': - if ($value !== self::SELECTION_BROWSE && $value !== self::SELECTION_DROPDOWN) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' must be either %selection_browse% or %selection_dropdown%", - null, - [ - '%setting%' => $name, - '%selection_browse%' => self::SELECTION_BROWSE, - '%selection_dropdown%' => self::SELECTION_DROPDOWN, - ], - "[$name]" - ); - } - break; - case 'selectionRoot': - if (!is_int($value) && !is_string($value) && $value !== null) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' value must be of either null, string or integer", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - break; - case 'selectionContentTypes': - if (!is_array($value)) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' value must be of array type", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - break; - } - } - - return $validationErrors; - } - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezobjectrelation'; - } - - /** - * @param \eZ\Publish\Core\FieldType\Relation\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - if (empty($value->destinationContentId)) { - return ''; - } - - try { - $contentInfo = $this->handler->loadContentInfo($value->destinationContentId); - $versionInfo = $this->handler->loadVersionInfo($value->destinationContentId, $contentInfo->currentVersionNo); - } catch (NotFoundException $e) { - return ''; - } - - if (isset($versionInfo->names[$languageCode])) { - return $versionInfo->names[$languageCode]; - } - - return $versionInfo->names[$contentInfo->mainLanguageCode]; - } - - /** - * Validates a field based on the validators in the field definition. - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue): array - { - $validationErrors = []; - - if ($this->isEmptyValue($fieldValue)) { - return $validationErrors; - } - - $allowedContentTypes = $fieldDefinition->getFieldSettings()['selectionContentTypes'] ?? []; - - $validationError = $this->targetContentValidator->validate( - (int) $fieldValue->destinationContentId, - $allowedContentTypes - ); - - return $validationError === null ? $validationErrors : [$validationError]; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Relation\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Returns if the given $value is considered empty by the field type. - * - * @param mixed $value - * - * @return bool - */ - public function isEmptyValue(SPIValue $value) - { - return $value->destinationContentId === null; - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param int|string|\eZ\Publish\API\Repository\Values\Content\ContentInfo|\eZ\Publish\Core\FieldType\Relation\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\Relation\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - // ContentInfo - if ($inputValue instanceof ContentInfo) { - $inputValue = new Value($inputValue->id); - } elseif (is_int($inputValue) || is_string($inputValue)) { // content id - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Relation\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!is_int($value->destinationContentId) && !is_string($value->destinationContentId)) { - throw new InvalidArgumentType( - '$value->destinationContentId', - 'string|int', - $value->destinationContentId - ); - } - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * For this FieldType, the related object's name is returned. - * - * @param \eZ\Publish\Core\FieldType\Relation\Value $value - * - * @return string - */ - protected function getSortInfo(BaseValue $value) - { - return (string)$value; - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\Relation\Value $value - */ - public function fromHash($hash) - { - if ($hash !== null) { - $destinationContentId = $hash['destinationContentId']; - if ($destinationContentId !== null) { - return new Value((int)$destinationContentId); - } - } - - return $this->getEmptyValue(); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Relation\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - $destinationContentId = null; - if ($value->destinationContentId !== null) { - $destinationContentId = (int)$value->destinationContentId; - } - - return [ - 'destinationContentId' => $destinationContentId, - ]; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } - - /** - * Returns relation data extracted from value. - * - * Not intended for \eZ\Publish\API\Repository\Values\Content\Relation::COMMON type relations, - * there is an API for handling those. - * - * @param \eZ\Publish\Core\FieldType\Relation\Value $fieldValue - * - * @return array Hash with relation type as key and array of destination content ids as value. - * - * Example: - * <code> - * array( - * \eZ\Publish\API\Repository\Values\Content\Relation::LINK => array( - * "contentIds" => array( 12, 13, 14 ), - * "locationIds" => array( 24 ) - * ), - * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED => array( - * "contentIds" => array( 12 ), - * "locationIds" => array( 24, 45 ) - * ), - * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD => array( 12 ) - * ) - * </code> - */ - public function getRelations(SPIValue $fieldValue) - { - $relations = []; - if ($fieldValue->destinationContentId !== null) { - $relations[Relation::FIELD] = [$fieldValue->destinationContentId]; - } - - return $relations; - } -} diff --git a/eZ/Publish/Core/FieldType/Relation/Value.php b/eZ/Publish/Core/FieldType/Relation/Value.php deleted file mode 100644 index 50e201457b..0000000000 --- a/eZ/Publish/Core/FieldType/Relation/Value.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Relation; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for Relation field type. - * - * @deprecated Since 7.0 and will be removed in 8.0. Use `RelationList\Value` instead. - */ -class Value extends BaseValue -{ - /** - * Related content. - * - * @var mixed|null - */ - public $destinationContentId; - - /** - * Construct a new Value object and initialize it $destinationContent. - * - * @param mixed $destinationContentId Content id the relation is to - */ - public function __construct($destinationContentId = null) - { - $this->destinationContentId = $destinationContentId; - } - - /** - * Returns the related content's name. - * - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - return (string)$this->destinationContentId; - } -} diff --git a/eZ/Publish/Core/FieldType/RelationList/SearchField.php b/eZ/Publish/Core/FieldType/RelationList/SearchField.php deleted file mode 100644 index 917dc8c2de..0000000000 --- a/eZ/Publish/Core/FieldType/RelationList/SearchField.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\RelationList; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for RelationList field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value', - $field->value->data['destinationContentIds'], - new Search\FieldType\MultipleStringField() - ), - new Search\Field( - 'sort_value', - implode('-', $field->value->data['destinationContentIds']), - new Search\FieldType\StringField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value' => new Search\FieldType\MultipleStringField(), - 'sort_value' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return 'sort_value'; - } -} diff --git a/eZ/Publish/Core/FieldType/RelationList/Type.php b/eZ/Publish/Core/FieldType/RelationList/Type.php deleted file mode 100644 index 185411ae17..0000000000 --- a/eZ/Publish/Core/FieldType/RelationList/Type.php +++ /dev/null @@ -1,482 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\RelationList; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\Handler as SPIContentHandler; -use Ibexa\Core\Repository\Validator\TargetContentValidatorInterface; - -/** - * The RelationList field type. - * - * This field type represents a relation to a content. - * - * hash format ({@see fromhash()}, {@see toHash()}): - * array( 'destinationContentIds' => array( (int)$destinationContentId ) ); - */ -class Type extends FieldType -{ - public const SELECTION_BROWSE = 0; - /** - * @todo following selection methods comes from legacy and may be interpreted as SELECTION_BROWSE by UI. - * UI support will be evaluated on a case by case basis for future versions. - */ - public const SELECTION_DROPDOWN = 1; - public const SELECTION_LIST_WITH_RADIO_BUTTONS = 2; - public const SELECTION_LIST_WITH_CHECKBOXES = 3; - public const SELECTION_MULTIPLE_SELECTION_LIST = 4; - public const SELECTION_TEMPLATE_BASED_MULTIPLE = 5; - public const SELECTION_TEMPLATE_BASED_SINGLE = 6; - - protected $settingsSchema = [ - 'selectionMethod' => [ - 'type' => 'int', - 'default' => self::SELECTION_BROWSE, - ], - 'selectionDefaultLocation' => [ - 'type' => 'string', - 'default' => null, - ], - 'selectionContentTypes' => [ - 'type' => 'array', - 'default' => [], - ], - ]; - - protected $validatorConfigurationSchema = [ - 'RelationListValueValidator' => [ - 'selectionLimit' => [ - 'type' => 'int', - 'default' => 0, - ], - ], - ]; - - /** @var \eZ\Publish\SPI\Persistence\Content\Handler */ - private $handler; - - /** @var \Ibexa\Core\Repository\Validator\TargetContentValidatorInterface */ - private $targetContentValidator; - - public function __construct( - SPIContentHandler $handler, - TargetContentValidatorInterface $targetContentValidator - ) { - $this->handler = $handler; - $this->targetContentValidator = $targetContentValidator; - } - - /** - * @see \eZ\Publish\Core\FieldType\FieldType::validateFieldSettings() - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings) - { - $validationErrors = []; - - foreach ($fieldSettings as $name => $value) { - if (!isset($this->settingsSchema[$name])) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' is unknown", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - continue; - } - - switch ($name) { - case 'selectionMethod': - if (!$this->isValidSelectionMethod($value)) { - $validationErrors[] = new ValidationError( - "The following options are available for setting '%setting%': %selection_browse%, %selection_dropdown%, %selection_list_with_radio_buttons%, %selection_list_with_checkboxes%, %selection_multiple_selection_list%, %selection_template_based_multiple%, %selection_template_based_single%", - null, - [ - '%setting%' => $name, - '%selection_browse%' => self::SELECTION_BROWSE, - '%selection_dropdown%' => self::SELECTION_DROPDOWN, - '%selection_list_with_radio_buttons%' => self::SELECTION_LIST_WITH_RADIO_BUTTONS, - '%selection_list_with_checkboxes%' => self::SELECTION_LIST_WITH_CHECKBOXES, - '%selection_multiple_selection_list%' => self::SELECTION_MULTIPLE_SELECTION_LIST, - '%selection_template_based_multiple%' => self::SELECTION_TEMPLATE_BASED_MULTIPLE, - '%selection_template_based_single%' => self::SELECTION_TEMPLATE_BASED_SINGLE, - ], - "[$name]" - ); - } - break; - case 'selectionDefaultLocation': - if (!is_int($value) && !is_string($value) && $value !== null) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' value must be of either null, string or integer", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - break; - case 'selectionContentTypes': - if (!is_array($value)) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' value must be of array type", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - break; - case 'selectionLimit': - if (!is_int($value)) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' value must be of integer type", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - if ($value < 0) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' value cannot be lower than 0", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - break; - } - } - - return $validationErrors; - } - - /** - * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $validatorConfiguration - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateValidatorConfiguration($validatorConfiguration) - { - $validationErrors = []; - - foreach ($validatorConfiguration as $validatorIdentifier => $constraints) { - if ($validatorIdentifier !== 'RelationListValueValidator') { - $validationErrors[] = new ValidationError( - "Validator '%validator%' is unknown", - null, - [ - '%validator%' => $validatorIdentifier, - ], - "[$validatorIdentifier]" - ); - - continue; - } - - foreach ($constraints as $name => $value) { - if ($name === 'selectionLimit') { - if (!is_int($value) && !ctype_digit($value)) { - $validationErrors[] = new ValidationError( - "Validator parameter '%parameter%' value must be an integer", - null, - [ - '%parameter%' => $name, - ], - "[$validatorIdentifier][$name]" - ); - } - if ($value < 0) { - $validationErrors[] = new ValidationError( - "Validator parameter '%parameter%' value must be equal to/greater than 0", - null, - [ - '%parameter%' => $name, - ], - "[$validatorIdentifier][$name]" - ); - } - } else { - $validationErrors[] = new ValidationError( - "Validator parameter '%parameter%' is unknown", - null, - [ - '%parameter%' => $name, - ], - "[$validatorIdentifier][$name]" - ); - } - } - } - - return $validationErrors; - } - - /** - * Validates a field based on the validators in the field definition. - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue): array - { - $validationErrors = []; - - if ($this->isEmptyValue($fieldValue)) { - return $validationErrors; - } - - $validatorConfiguration = $fieldDefinition->getValidatorConfiguration(); - $constraints = $validatorConfiguration['RelationListValueValidator'] ?? []; - - $validationErrors = []; - - if (isset($constraints['selectionLimit']) && - $constraints['selectionLimit'] > 0 && count($fieldValue->destinationContentIds) > $constraints['selectionLimit']) { - $validationErrors[] = new ValidationError( - 'The selected content items number cannot be higher than %limit%.', - null, - [ - '%limit%' => $constraints['selectionLimit'], - ], - 'destinationContentIds' - ); - } - - $allowedContentTypes = $fieldDefinition->getFieldSettings()['selectionContentTypes'] ?? []; - - foreach ($fieldValue->destinationContentIds as $destinationContentId) { - $validationError = $this->targetContentValidator->validate((int) $destinationContentId, $allowedContentTypes); - if ($validationError !== null) { - $validationErrors[] = $validationError; - } - } - - return $validationErrors; - } - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezobjectrelationlist'; - } - - /** - * @param \eZ\Publish\Core\FieldType\RelationList\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - if (empty($value->destinationContentIds)) { - return ''; - } - - $names = []; - foreach ($value->destinationContentIds as $contentId) { - try { - $contentInfo = $this->handler->loadContentInfo($contentId); - $versionInfo = $this->handler->loadVersionInfo($contentId, $contentInfo->currentVersionNo); - } catch (NotFoundException $e) { - continue; - } - - if (isset($versionInfo->names[$languageCode])) { - $names[] = $versionInfo->names[$languageCode]; - } else { - $names[] = $versionInfo->names[$contentInfo->mainLanguageCode]; - } - } - - return implode(' ', $names); - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\RelationList\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param int|string|array|\eZ\Publish\API\Repository\Values\Content\ContentInfo|\eZ\Publish\Core\FieldType\RelationList\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\RelationList\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - // ContentInfo - if ($inputValue instanceof ContentInfo) { - $inputValue = new Value([$inputValue->id]); - } elseif (is_int($inputValue) || is_string($inputValue)) { - // content id - $inputValue = new Value([$inputValue]); - } elseif (is_array($inputValue)) { - // content id's - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\RelationList\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!is_array($value->destinationContentIds)) { - throw new InvalidArgumentType( - '$value->destinationContentIds', - 'array', - $value->destinationContentIds - ); - } - - foreach ($value->destinationContentIds as $key => $destinationContentId) { - if (!is_int($destinationContentId) && !is_string($destinationContentId)) { - throw new InvalidArgumentType( - "\$value->destinationContentIds[$key]", - 'string|int', - $destinationContentId - ); - } - } - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * - * For this FieldType, the related objects IDs are returned, separated by ",". - * - * @param \eZ\Publish\Core\FieldType\RelationList\Value $value - * - * @return string - */ - protected function getSortInfo(BaseValue $value) - { - return implode(',', $value->destinationContentIds); - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\RelationList\Value $value - */ - public function fromHash($hash) - { - return new Value($hash['destinationContentIds']); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\RelationList\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - return ['destinationContentIds' => $value->destinationContentIds]; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } - - /** - * Returns relation data extracted from value. - * - * Not intended for \eZ\Publish\API\Repository\Values\Content\Relation::COMMON type relations, - * there is an API for handling those. - * - * @param \eZ\Publish\Core\FieldType\RelationList\Value $value - * - * @return array Hash with relation type as key and array of destination content ids as value. - * - * Example: - * <code> - * array( - * \eZ\Publish\API\Repository\Values\Content\Relation::LINK => array( - * "contentIds" => array( 12, 13, 14 ), - * "locationIds" => array( 24 ) - * ), - * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED => array( - * "contentIds" => array( 12 ), - * "locationIds" => array( 24, 45 ) - * ), - * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD => array( 12 ) - * ) - * </code> - */ - public function getRelations(SPIValue $value) - { - /* @var \eZ\Publish\Core\FieldType\RelationList\Value $value */ - return [ - Relation::FIELD => $value->destinationContentIds, - ]; - } - - /** - * Checks whether given selectionMethod is valid. - * - * @param int $selectionMethod - * - * @return bool - */ - private function isValidSelectionMethod($selectionMethod) - { - return in_array($selectionMethod, [ - self::SELECTION_BROWSE, - self::SELECTION_DROPDOWN, - self::SELECTION_LIST_WITH_RADIO_BUTTONS, - self::SELECTION_LIST_WITH_CHECKBOXES, - self::SELECTION_MULTIPLE_SELECTION_LIST, - self::SELECTION_TEMPLATE_BASED_MULTIPLE, - self::SELECTION_TEMPLATE_BASED_SINGLE, - ], true); - } -} diff --git a/eZ/Publish/Core/FieldType/RelationList/Value.php b/eZ/Publish/Core/FieldType/RelationList/Value.php deleted file mode 100644 index 7b897ddfa5..0000000000 --- a/eZ/Publish/Core/FieldType/RelationList/Value.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\RelationList; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for RelationList field type. - */ -class Value extends BaseValue -{ - /** - * Related content id's. - * - * @var mixed[] - */ - public $destinationContentIds; - - /** - * Construct a new Value object and initialize it $text. - * - * @param mixed[] $destinationContentIds - */ - public function __construct(array $destinationContentIds = []) - { - $this->destinationContentIds = $destinationContentIds; - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - return implode(',', $this->destinationContentIds); - } -} diff --git a/eZ/Publish/Core/FieldType/Selection/SearchField.php b/eZ/Publish/Core/FieldType/Selection/SearchField.php deleted file mode 100644 index 63de330e52..0000000000 --- a/eZ/Publish/Core/FieldType/Selection/SearchField.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Selection; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for Selection field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - $indexes = []; - $values = []; - $fieldSettings = $fieldDefinition->fieldTypeConstraints->fieldSettings; - $positionSet = array_flip($field->value->data); - - $options = $fieldSettings['multilingualOptions'][$field->languageCode] ?? $fieldSettings['options']; - - foreach ($options as $index => $value) { - if (isset($positionSet[$index])) { - $values[] = $value; - $indexes[] = $index; - } - } - - return [ - new Search\Field( - 'selected_option_value', - $values, - new Search\FieldType\MultipleStringField() - ), - new Search\Field( - 'selected_option_index', - $indexes, - new Search\FieldType\MultipleIntegerField() - ), - new Search\Field( - 'selected_option_count', - count($indexes), - new Search\FieldType\IntegerField() - ), - new Search\Field( - 'sort_value', - implode('-', $indexes), - new Search\FieldType\StringField() - ), - new Search\Field( - 'fulltext', - $values, - new Search\FieldType\FullTextField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'selected_option_value' => new Search\FieldType\MultipleStringField(), - 'selected_option_index' => new Search\FieldType\MultipleIntegerField(), - 'selected_option_count' => new Search\FieldType\IntegerField(), - 'sort_value' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'selected_option_index'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return 'sort_value'; - } -} diff --git a/eZ/Publish/Core/FieldType/Selection/Type.php b/eZ/Publish/Core/FieldType/Selection/Type.php deleted file mode 100644 index 8962eddd22..0000000000 --- a/eZ/Publish/Core/FieldType/Selection/Type.php +++ /dev/null @@ -1,311 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Selection; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -/** - * The Selection field type. - * - * This field type represents a simple string. - */ -class Type extends FieldType -{ - /** - * The setting keys which are available on this field type. - * - * The key is the setting name, and the value is the default value for given - * setting, set to null if no particular default should be set. - * - * @var mixed - */ - protected $settingsSchema = [ - 'isMultiple' => [ - 'type' => 'bool', - 'default' => false, - ], - 'options' => [ - 'type' => 'hash', - 'default' => [], - ], - 'multilingualOptions' => [ - 'type' => 'hash', - 'default' => [], - ], - ]; - - /** - * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings) - { - $validationErrors = []; - - foreach ($fieldSettings as $settingKey => $settingValue) { - switch ($settingKey) { - case 'isMultiple': - if (!is_bool($settingValue)) { - $validationErrors[] = new ValidationError( - "FieldType '%fieldType%' expects setting '%setting%' to be of type '%type%'", - null, - [ - '%fieldType%' => $this->getFieldTypeIdentifier(), - '%setting%' => $settingKey, - '%type%' => 'bool', - ], - "[$settingKey]" - ); - } - break; - case 'options': - if (!is_array($settingValue)) { - $validationErrors[] = new ValidationError( - "FieldType '%fieldType%' expects setting '%setting%' to be of type '%type%'", - null, - [ - '%fieldType%' => $this->getFieldTypeIdentifier(), - '%setting%' => $settingKey, - '%type%' => 'hash', - ], - "[$settingKey]" - ); - } - break; - case 'multilingualOptions': - if (!is_array($settingValue) && !is_array(reset($settingValue))) { - $validationErrors[] = new ValidationError( - "FieldType '%fieldType%' expects setting '%setting%' to be of type '%type%'", - null, - [ - '%fieldType%' => $this->getFieldTypeIdentifier(), - '%setting%' => $settingKey, - '%type%' => 'hash', - ], - "[$settingKey]" - ); - } - break; - default: - $validationErrors[] = new ValidationError( - "Setting '%setting%' is unknown", - null, - [ - '%setting%' => $settingKey, - ], - "[$settingKey]" - ); - } - } - - return $validationErrors; - } - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezselection'; - } - - /** - * @param \eZ\Publish\Core\FieldType\Selection\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - if (empty($value->selection)) { - return ''; - } - - $names = []; - $fieldSettings = $fieldDefinition->getFieldSettings(); - - foreach ($value->selection as $optionIndex) { - if (isset($fieldSettings['multilingualOptions'][$languageCode][$optionIndex])) { - $names[] = $fieldSettings['multilingualOptions'][$languageCode][$optionIndex]; - } elseif (isset($fieldSettings['multilingualOptions'][$fieldDefinition->mainLanguageCode][$optionIndex])) { - $names[] = $fieldSettings['multilingualOptions'][$fieldDefinition->mainLanguageCode][$optionIndex]; - } elseif (isset($fieldSettings['options'][$optionIndex])) { - $names[] = $fieldSettings['options'][$optionIndex]; - } - } - - return implode(' ', $names); - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Selection\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param array|\eZ\Publish\Core\FieldType\Selection\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\Selection\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_array($inputValue)) { - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Selection\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!is_array($value->selection)) { - throw new InvalidArgumentType( - '$value->selection', - 'array', - $value->selection - ); - } - } - - /** - * Validates field value against 'isMultiple' and 'options' settings. - * - * Does not use validators. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field - * @param \eZ\Publish\Core\FieldType\Selection\Value $fieldValue The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) - { - $validationErrors = []; - - if ($this->isEmptyValue($fieldValue)) { - return $validationErrors; - } - - $fieldSettings = $fieldDefinition->getFieldSettings(); - - if ((!isset($fieldSettings['isMultiple']) || $fieldSettings['isMultiple'] === false) - && count($fieldValue->selection) > 1) { - $validationErrors[] = new ValidationError( - 'Field definition does not allow multiple options to be selected.', - null, - [], - 'selection' - ); - } - - foreach ($fieldValue->selection as $optionIndex) { - if (!isset($fieldSettings['options'][$optionIndex]) && empty($fieldSettings['multilingualOptions'])) { - $validationErrors[] = new ValidationError( - 'Option with index %index% does not exist in the field definition.', - null, - [ - '%index%' => $optionIndex, - ], - 'selection' - ); - } - } - - //@todo: find a way to include selection language - if (isset($fieldSettings['multilingualOptions'])) { - $possibleOptionIndexesByLanguage = array_map(static function ($languageOptionIndexes) { - return array_keys($languageOptionIndexes); - }, $fieldSettings['multilingualOptions']); - - $possibleOptionIndexes = array_merge(...array_values($possibleOptionIndexesByLanguage)); - - foreach ($fieldValue->selection as $optionIndex) { - if (!in_array($optionIndex, $possibleOptionIndexes)) { - $validationErrors[] = new ValidationError( - 'Option with index %index% does not exist in the field definition.', - null, - [ - '%index%' => $optionIndex, - ], - 'selection' - ); - } - } - } - - return $validationErrors; - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * - * @param \eZ\Publish\Core\FieldType\Selection\Value $value - * - * @return string - */ - protected function getSortInfo(BaseValue $value) - { - return implode('-', $value->selection); - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\Selection\Value $value - */ - public function fromHash($hash) - { - return new Value($hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Selection\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - return $value->selection; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } -} diff --git a/eZ/Publish/Core/FieldType/Selection/Value.php b/eZ/Publish/Core/FieldType/Selection/Value.php deleted file mode 100644 index 2e01275743..0000000000 --- a/eZ/Publish/Core/FieldType/Selection/Value.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Selection; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for Selection field type. - */ -class Value extends BaseValue -{ - /** - * Selection content. - * - * @var int[] - */ - public $selection; - - /** - * Construct a new Value object and initialize it $selection. - * - * @param int[] $selection - */ - public function __construct(array $selection = []) - { - $this->selection = $selection; - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - return implode(',', $this->selection); - } -} diff --git a/eZ/Publish/Core/FieldType/StorageGateway.php b/eZ/Publish/Core/FieldType/StorageGateway.php deleted file mode 100644 index 8722e53f0f..0000000000 --- a/eZ/Publish/Core/FieldType/StorageGateway.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType; - -use eZ\Publish\SPI\FieldType\StorageGateway as SPIStorageGateway; - -/** - * Abstract base class for storage gateways. - * - * @deprecated Since 6.11. Extend {@link \eZ\Publish\SPI\FieldType\StorageGateway} class instead. - */ -abstract class StorageGateway extends SPIStorageGateway -{ - /** - * Sets the data storage connection to use. - * - * @deprecated Since 6.11. Set gateway connection using Dependency Injection. - * - * Allows injection of the data storage connection to be used from external - * source. This can be a database connection resource or something else to - * define the storage, depending on the gateway implementation. - * - * @param mixed $connection - */ - abstract public function setConnection($connection); -} diff --git a/eZ/Publish/Core/FieldType/Tests/AuthorTest.php b/eZ/Publish/Core/FieldType/Tests/AuthorTest.php deleted file mode 100644 index 92ac530eef..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/AuthorTest.php +++ /dev/null @@ -1,568 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Author\Author; -use eZ\Publish\Core\FieldType\Author\AuthorCollection; -use eZ\Publish\Core\FieldType\Author\Type as AuthorType; -use eZ\Publish\Core\FieldType\Author\Value as AuthorValue; -use eZ\Publish\Core\FieldType\Value; - -/** - * @group fieldType - * @group ezauthor - */ -class AuthorTest extends FieldTypeTest -{ - /** @var \eZ\Publish\Core\FieldType\Author\Author[] */ - private $authors; - - protected function setUp(): void - { - parent::setUp(); - $this->authors = [ - new Author(['name' => 'Boba Fett', 'email' => 'boba.fett@bountyhunters.com']), - new Author(['name' => 'Darth Vader', 'email' => 'darth.vader@evilempire.biz']), - new Author(['name' => 'Luke Skywalker', 'email' => 'luke@imtheone.net']), - ]; - } - - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return \eZ\Publish\SPI\FieldType\FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new AuthorType(); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return []; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return [ - 'defaultAuthor' => [ - 'type' => 'choice', - 'default' => AuthorType::DEFAULT_VALUE_EMPTY, - ], - ]; - } - - /** - * Returns the empty value expected from the field type. - * - * @return \eZ\Publish\Core\FieldType\Author\Value - */ - protected function getEmptyValueExpectation() - { - return new AuthorValue(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - 'My name', - InvalidArgumentException::class, - ], - [ - 23, - InvalidArgumentException::class, - ], - [ - ['foo'], - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - [], - new AuthorValue([]), - ], - [ - [ - new Author(['name' => 'Boba Fett', 'email' => 'boba.fett@example.com']), - ], - new AuthorValue( - [ - new Author(['id' => 1, 'name' => 'Boba Fett', 'email' => 'boba.fett@example.com']), - ] - ), - ], - [ - [ - new Author(['name' => 'Boba Fett', 'email' => 'boba.fett@example.com']), - new Author(['name' => 'Darth Vader', 'email' => 'darth.vader@example.com']), - ], - new AuthorValue( - [ - new Author(['id' => 1, 'name' => 'Boba Fett', 'email' => 'boba.fett@example.com']), - new Author(['id' => 2, 'name' => 'Darth Vader', 'email' => 'darth.vader@example.com']), - ] - ), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new AuthorValue([]), - [], - ], - [ - new AuthorValue( - [ - new Author(['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com']), - ] - ), - [ - ['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com'], - ], - ], - [ - new AuthorValue( - [ - new Author(['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com']), - new Author(['id' => 2, 'name' => 'Joe Bielefeld', 'email' => 'bielefeld@example.com']), - ] - ), - [ - ['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com'], - ['id' => 2, 'name' => 'Joe Bielefeld', 'email' => 'bielefeld@example.com'], - ], - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - [], - new AuthorValue([]), - ], - [ - [ - ['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com'], - ], - new AuthorValue( - [ - new Author(['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com']), - ] - ), - ], - [ - [ - ['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com'], - ['id' => 2, 'name' => 'Joe Bielefeld', 'email' => 'bielefeld@example.com'], - ], - new AuthorValue( - [ - new Author(['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com']), - new Author(['id' => 2, 'name' => 'Joe Bielefeld', 'email' => 'bielefeld@example.com']), - ] - ), - ], - ]; - } - - /** - * Provide data sets with field settings which are considered valid by the - * {@link validateFieldSettings()} method. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( 'rows' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidFieldSettings() - { - return [ - [ - [], - ], - [ - [ - 'defaultAuthor' => AuthorType::DEFAULT_VALUE_EMPTY, - ], - ], - [ - [ - 'defaultAuthor' => AuthorType::DEFAULT_CURRENT_USER, - ], - ], - ]; - } - - /** - * Provide data sets with field settings which are considered invalid by the - * {@link validateFieldSettings()} method. The method must return a - * non-empty array of validation error when receiving such field settings. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * true, - * ), - * array( - * array( 'nonExistentKey' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInValidFieldSettings() - { - return [ - [ - [ - // non-existent setting - 'useSeconds' => 23, - ], - ], - [ - [ - //defaultAuthor must be constant - 'defaultAuthor' => 42, - ], - ], - ]; - } - - protected function tearDown(): void - { - unset($this->authors); - parent::tearDown(); - } - - /** - * @covers \eZ\Publish\Core\FieldType\FieldType::getValidatorConfigurationSchema - */ - public function testValidatorConfigurationSchema() - { - $ft = $this->createFieldTypeUnderTest(); - self::assertEmpty( - $ft->getValidatorConfigurationSchema(), - 'The validator configuration schema does not match what is expected.' - ); - } - - /** - * @covers \eZ\Publish\Core\FieldType\Author\Type::acceptValue - */ - public function testAcceptValueInvalidType() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $ft = $this->createFieldTypeUnderTest(); - $ft->acceptValue($this->createMock(Value::class)); - } - - /** - * @covers \eZ\Publish\Core\FieldType\Author\Type::acceptValue - */ - public function testAcceptValueInvalidFormat() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $ft = $this->createFieldTypeUnderTest(); - $value = new AuthorValue(); - $value->authors = 'This is not a valid author collection'; - $ft->acceptValue($value); - } - - /** - * @covers \eZ\Publish\Core\FieldType\Author\Type::acceptValue - */ - public function testAcceptValueValidFormat() - { - $ft = $this->createFieldTypeUnderTest(); - $author = new Author(); - $author->name = 'Boba Fett'; - $author->email = 'boba.fett@bountyhunters.com'; - $value = new AuthorValue([$author]); - $newValue = $ft->acceptValue($value); - self::assertSame($value, $newValue); - } - - /** - * @covers \eZ\Publish\Core\FieldType\Author\Value::__construct - */ - public function testBuildFieldValueWithoutParam() - { - $value = new AuthorValue(); - self::assertInstanceOf(AuthorCollection::class, $value->authors); - self::assertSame([], $value->authors->getArrayCopy()); - } - - /** - * @covers \eZ\Publish\Core\FieldType\Author\Value::__construct - */ - public function testBuildFieldValueWithParam() - { - $value = new AuthorValue($this->authors); - self::assertInstanceOf(AuthorCollection::class, $value->authors); - self::assertSame($this->authors, $value->authors->getArrayCopy()); - } - - /** - * @covers \eZ\Publish\Core\FieldType\Author\Value::__toString - */ - public function testFieldValueToString() - { - $value = new AuthorValue($this->authors); - - $authorsName = []; - foreach ($this->authors as $author) { - $authorsName[] = $author->name; - } - - self::assertSame(implode(', ', $authorsName), $value->__toString()); - } - - /** - * @covers \eZ\Publish\Core\FieldType\Author\AuthorCollection::offsetSet - */ - public function testAddAuthor() - { - $value = new AuthorValue(); - $value->authors[] = $this->authors[0]; - self::assertSame(1, $this->authors[0]->id); - self::assertCount(1, $value->authors); - - $this->authors[1]->id = 10; - $value->authors[] = $this->authors[1]; - self::assertSame(10, $this->authors[1]->id); - - $this->authors[2]->id = -1; - $value->authors[] = $this->authors[2]; - self::assertSame($this->authors[1]->id + 1, $this->authors[2]->id); - self::assertCount(3, $value->authors); - } - - /** - * @covers \eZ\Publish\Core\FieldType\Author\AuthorCollection::removeAuthorsById - */ - public function testRemoveAuthors() - { - $existingIds = []; - foreach ($this->authors as $author) { - $id = random_int(1, 100); - if (in_array($id, $existingIds)) { - continue; - } - $author->id = $id; - $existingIds[] = $id; - } - - $value = new AuthorValue($this->authors); - $value->authors->removeAuthorsById([$this->authors[1]->id, $this->authors[2]->id]); - self::assertSame(count($this->authors) - 2, count($value->authors)); - self::assertSame([$this->authors[0]], $value->authors->getArrayCopy()); - } - - /** - * Returns the identifier of the field type under test. - * - * @return string - */ - protected function provideFieldTypeIdentifier() - { - return 'ezauthor'; - } - - /** - * Provides data for the getName() test. - * - * @return array - */ - public function provideDataForGetName(): array - { - $authorList = new AuthorValue( - [ - new Author(['id' => 1, 'name' => 'Boba Fett', 'email' => 'boba.fett@example.com']), - new Author(['id' => 2, 'name' => 'Luke Skywalker', 'email' => 'luke.skywalker@example.com']), - ] - ); - - return [ - [$this->getEmptyValueExpectation(), '', [], 'en_GB'], - [$authorList, 'Boba Fett', [], 'en_GB'], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/CheckboxTest.php b/eZ/Publish/Core/FieldType/Tests/CheckboxTest.php deleted file mode 100644 index 492cdcc37b..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/CheckboxTest.php +++ /dev/null @@ -1,326 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Checkbox\Type as Checkbox; -use eZ\Publish\Core\FieldType\Checkbox\Value as CheckboxValue; - -/** - * @group fieldType - * @group ezboolean - */ -class CheckboxTest extends FieldTypeTest -{ - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return \eZ\Publish\SPI\FieldType\FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new Checkbox(); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return []; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return []; - } - - /** - * Returns the empty value expected from the field type. - * - * @return \eZ\Publish\Core\FieldType\Checkbox\Value - */ - protected function getEmptyValueExpectation() - { - return new CheckboxValue(false); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - 23, - InvalidArgumentException::class, - ], - [ - new CheckboxValue(42), - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - false, - new CheckboxValue(false), - ], - [ - true, - new CheckboxValue(true), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new CheckboxValue(true), - true, - ], - [ - new CheckboxValue(false), - false, - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - true, - new CheckboxValue(true), - ], - [ - false, - new CheckboxValue(false), - ], - ]; - } - - /** - * @covers \eZ\Publish\Core\FieldType\Checkbox\Type::toPersistenceValue - */ - public function testToPersistenceValue() - { - $ft = $this->createFieldTypeUnderTest(); - $fieldValue = $ft->toPersistenceValue(new CheckboxValue(true)); - - self::assertTrue($fieldValue->data); - self::assertSame(1, $fieldValue->sortKey); - } - - /** - * @covers \eZ\Publish\Core\FieldType\Checkbox\Value::__construct - */ - public function testBuildFieldValueWithParam() - { - $bool = true; - $value = new CheckboxValue($bool); - self::assertSame($bool, $value->bool); - } - - /** - * @covers \eZ\Publish\Core\FieldType\Checkbox\Value::__construct - */ - public function testBuildFieldValueWithoutParam() - { - $value = new CheckboxValue(); - self::assertFalse($value->bool); - } - - /** - * @covers \eZ\Publish\Core\FieldType\Checkbox\Value::__toString - */ - public function testFieldValueToString() - { - $valueTrue = new CheckboxValue(true); - $valueFalse = new CheckboxValue(false); - self::assertSame('1', (string)$valueTrue); - self::assertSame('0', (string)$valueFalse); - } - - protected function provideFieldTypeIdentifier() - { - return 'ezboolean'; - } - - public function provideDataForGetName(): array - { - return [ - [new CheckboxValue(true), '1', [], 'en_GB'], - [new CheckboxValue(false), '0', [], 'en_GB'], - ]; - } - - /** - * @dataProvider provideForValueIsNeverEmpty - */ - public function testValueIsNeverEmpty(CheckboxValue $value): void - { - $fieldType = $this->getFieldTypeUnderTest(); - - self::assertFalse($fieldType->isEmptyValue($value)); - } - - /** - * @return iterable<array{ - * \eZ\Publish\Core\FieldType\Checkbox\Value, - * }> - */ - public function provideForValueIsNeverEmpty(): iterable - { - yield [new CheckboxValue(true)]; - yield [new CheckboxValue(false)]; - } - - public function testEmptyValueIsEmpty(): void - { - self::markTestSkipped('Value of Checkbox fieldtype is never considered empty'); - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/CountryTest.php b/eZ/Publish/Core/FieldType/Tests/CountryTest.php deleted file mode 100644 index 9f4e4746db..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/CountryTest.php +++ /dev/null @@ -1,643 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Country\Exception\InvalidValue; -use eZ\Publish\Core\FieldType\Country\Type as Country; -use eZ\Publish\Core\FieldType\Country\Value as CountryValue; -use eZ\Publish\Core\FieldType\ValidationError; - -/** - * @group fieldType - * @group ezcountry - */ -class CountryTest extends FieldTypeTest -{ - protected function provideFieldTypeIdentifier() - { - return 'ezcountry'; - } - - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new Country( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - 'FR' => [ - 'Name' => 'France', - 'Alpha2' => 'FR', - 'Alpha3' => 'FRA', - 'IDC' => 33, - ], - 'NO' => [ - 'Name' => 'Norway', - 'Alpha2' => 'NO', - 'Alpha3' => 'NOR', - 'IDC' => 47, - ], - 'KP' => [ - 'Name' => "Korea, Democratic People's Republic of", - 'Alpha2' => 'KP', - 'Alpha3' => 'PRK', - 'IDC' => 850, - ], - 'TF' => [ - 'Name' => 'French Southern Territories', - 'Alpha2' => 'TF', - 'Alpha3' => 'ATF', - 'IDC' => 0, - ], - 'CF' => [ - 'Name' => 'Central African Republic', - 'Alpha2' => 'CF', - 'Alpha3' => 'CAF', - 'IDC' => 236, - ], - ] - ); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return []; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return [ - 'isMultiple' => [ - 'type' => 'boolean', - 'default' => false, - ], - ]; - } - - /** - * Returns the empty value expected from the field type. - * - * @return \eZ\Publish\Core\FieldType\Checkbox\Value - */ - protected function getEmptyValueExpectation() - { - return new CountryValue(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - 'LegoLand', - InvalidArgumentException::class, - ], - [ - ['Norway', 'France', 'LegoLand'], - InvalidValue::class, - ], - [ - ['FR', 'BE', 'LE'], - InvalidValue::class, - ], - [ - ['FRE', 'BEL', 'LEG'], - InvalidValue::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - ['BE', 'FR'], - new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - 'FR' => [ - 'Name' => 'France', - 'Alpha2' => 'FR', - 'Alpha3' => 'FRA', - 'IDC' => 33, - ], - ] - ), - ], - [ - ['Belgium'], - new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - ] - ), - ], - [ - ['BE'], - new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - ] - ), - ], - [ - ['BEL'], - new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - ] - ), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - ] - ), - ['BE'], - ], - [ - new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - 'FR' => [ - 'Name' => 'France', - 'Alpha2' => 'FR', - 'Alpha3' => 'FRA', - 'IDC' => 33, - ], - ] - ), - ['BE', 'FR'], - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - ['BE'], - new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - ] - ), - ], - [ - ['BE', 'FR'], - new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - 'FR' => [ - 'Name' => 'France', - 'Alpha2' => 'FR', - 'Alpha3' => 'FRA', - 'IDC' => 33, - ], - ] - ), - ], - ]; - } - - public function provideDataForGetName(): array - { - return [ - [new CountryValue(), '', [], 'en_GB'], - [new CountryValue(['FR' => ['Name' => 'France']]), 'France', [], 'en_GB'], - [ - new CountryValue(['FR' => ['Name' => 'France'], 'DE' => ['Name' => 'Deutschland']]), - 'France, Deutschland', - [], - 'en_GB', - ], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings and - * field value which are considered valid by the {@link validate()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten if - * a FieldType supports validation! - * - * For example: - * - * <code> - * return array( - * array( - * array( - * "validatorConfiguration" => array( - * "StringLengthValidator" => array( - * "minStringLength" => 2, - * "maxStringLength" => 10, - * ), - * ), - * ), - * new TextLineValue( "lalalala" ), - * ), - * array( - * array( - * "fieldSettings" => array( - * 'isMultiple' => true - * ), - * ), - * new CountryValue( - * array( - * "BE" => array( - * "Name" => "Belgium", - * "Alpha2" => "BE", - * "Alpha3" => "BEL", - * "IDC" => 32, - * ), - * ), - * ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidDataForValidate() - { - return [ - [ - [ - 'fieldSettings' => [ - 'isMultiple' => true, - ], - ], - new CountryValue(), - ], - [ - [ - 'fieldSettings' => [ - 'isMultiple' => false, - ], - ], - new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - ] - ), - ], - [ - [ - 'fieldSettings' => [ - 'isMultiple' => true, - ], - ], - new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - 'FR' => [ - 'Name' => 'France', - 'Alpha2' => 'FR', - 'Alpha3' => 'FRA', - 'IDC' => 33, - ], - ] - ), - ], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings, - * field value and corresponding validation errors returned by - * the {@link validate()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten - * if a FieldType supports validation! - * - * For example: - * - * <code> - * return array( - * array( - * array( - * "validatorConfiguration" => array( - * "IntegerValueValidator" => array( - * "minIntegerValue" => 5, - * "maxIntegerValue" => 10 - * ), - * ), - * ), - * new IntegerValue( 3 ), - * array( - * new ValidationError( - * "The value can not be lower than %size%.", - * null, - * array( - * "size" => 5 - * ), - * ), - * ), - * ), - * array( - * array( - * "fieldSettings" => array( - * "isMultiple" => false - * ), - * ), - * new CountryValue( - * "BE" => array( - * "Name" => "Belgium", - * "Alpha2" => "BE", - * "Alpha3" => "BEL", - * "IDC" => 32, - * ), - * "FR" => array( - * "Name" => "France", - * "Alpha2" => "FR", - * "Alpha3" => "FRA", - * "IDC" => 33, - * ), - * ) - * ), - * array( - * new ValidationError( - * "Field definition does not allow multiple countries to be selected." - * ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidDataForValidate() - { - return [ - [ - [ - 'fieldSettings' => [ - 'isMultiple' => false, - ], - ], - new CountryValue( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => 32, - ], - 'FR' => [ - 'Name' => 'France', - 'Alpha2' => 'FR', - 'Alpha3' => 'FRA', - 'IDC' => 33, - ], - ] - ), - [ - new ValidationError( - 'Field definition does not allow multiple countries to be selected.', - null, - [], - 'countries' - ), - ], - ], - [ - [ - 'fieldSettings' => [ - 'isMultiple' => true, - ], - ], - new CountryValue( - [ - 'LE' => [ - 'Name' => 'LegoLand', - 'Alpha2' => 'LE', - 'Alpha3' => 'LEG', - 'IDC' => 888, - ], - ] - ), - [ - new ValidationError( - "Country with Alpha2 code '%alpha2%' is not defined in FieldType settings.", - null, - [ - '%alpha2%' => 'LE', - ], - 'countries' - ), - ], - ], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/DateAndTimeTest.php b/eZ/Publish/Core/FieldType/Tests/DateAndTimeTest.php deleted file mode 100644 index be1ee022d8..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/DateAndTimeTest.php +++ /dev/null @@ -1,463 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use DateInterval; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\DateAndTime\Type as DateAndTime; -use eZ\Publish\Core\FieldType\DateAndTime\Value as DateAndTimeValue; -use stdClass; - -/** - * @group fieldType - * @group ezdatetime - */ -class DateAndTimeTest extends FieldTypeTest -{ - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new DateAndTime(); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return []; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return [ - 'useSeconds' => [ - 'type' => 'bool', - 'default' => false, - ], - 'defaultType' => [ - 'type' => 'choice', - 'default' => DateAndTime::DEFAULT_EMPTY, - ], - 'dateInterval' => [ - 'type' => 'dateInterval', - 'default' => null, - ], - ]; - } - - /** - * Returns the empty value expected from the field type. - */ - protected function getEmptyValueExpectation() - { - return new DateAndTimeValue(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - [], - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - null, - new DateAndTimeValue(), - ], - [ - '2012-08-28 12:20 Europe/Berlin', - DateAndTimeValue::fromString('2012-08-28 12:20 Europe/Berlin'), - ], - [ - 1346149200, - DateAndTimeValue::fromTimestamp(1346149200), - ], - [ - ($dateTime = new \DateTime()), - new DateAndTimeValue($dateTime), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new DateAndTimeValue(), - null, - ], - [ - new DateAndTimeValue($date = new \DateTime('Tue, 28 Aug 2012 12:20:00 +0200')), - [ - 'rfc850' => $date->format(\DateTime::RFC850), - 'timestamp' => $date->getTimeStamp(), - ], - ], - ]; - } - - /** - * @param mixed $inputValue - * @param array $expectedResult - * - * @dataProvider provideInputForFromHash - */ - public function testFromHash($inputHash, $expectedResult) - { - $this->assertIsValidHashValue($inputHash); - - $fieldType = $this->getFieldTypeUnderTest(); - - $actualResult = $fieldType->fromHash($inputHash); - - // Tests may run slowly. Allow 20 seconds margin of error. - $this->assertGreaterThanOrEqual( - $expectedResult, - $actualResult, - 'fromHash() method did not create expected result.' - ); - if ($expectedResult->value !== null) { - $this->assertLessThan( - $expectedResult->value->add(new DateInterval('PT20S')), - $actualResult->value, - 'fromHash() method did not create expected result.' - ); - } - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * - * @return array - */ - public function provideInputForFromHash() - { - $date = new \DateTime('Tue, 28 Aug 2012 12:20:00 +0200'); - - return [ - [ - null, - new DateAndTimeValue(), - ], - [ - [ - 'rfc850' => $date->format(\DateTime::RFC850), - 'timestamp' => $date->getTimeStamp(), - ], - new DateAndTimeValue($date), - ], - [ - [ - 'timestamp' => $date->getTimeStamp(), - ], - DateAndTimeValue::fromTimestamp($date->getTimeStamp()), - ], - ]; - } - - /** - * @param mixed $inputValue - * @param string $intervalSpec - * - * @dataProvider provideInputForTimeStringFromHash - */ - public function testTimeStringFromHash($inputHash, $intervalSpec) - { - $this->assertIsValidHashValue($inputHash); - - $fieldType = $this->getFieldTypeUnderTest(); - - $expectedResult = new DateAndTimeValue(new \DateTime()); - $expectedResult->value->add(new DateInterval($intervalSpec)); - - $actualResult = $fieldType->fromHash($inputHash); - - // Tests may run slowly. Allow 20 seconds margin of error. - $this->assertGreaterThanOrEqual( - $expectedResult, - $actualResult, - 'fromHash() method did not create expected result.' - ); - if ($expectedResult->value !== null) { - $this->assertLessThan( - $expectedResult->value->add(new DateInterval('PT20S')), - $actualResult->value, - 'fromHash() method did not create expected result.' - ); - } - } - - /** - * Provide input to testTimeStringFromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. A valid - * timestring input to fromHash(), 2. An interval specification string, - * from which can be created a DateInterval which can be added to the - * current DateTime, to be compared with the expected return value from - * fromHash(). - * - * @return array - */ - public function provideInputForTimeStringFromHash() - { - return [ - [ - [ - 'timestring' => 'now', - ], - 'P0Y', - ], - [ - [ - 'timestring' => '+42 seconds', - ], - 'PT42S', - ], - [ - [ - 'timestring' => '+3 months 2 days 5 hours', - ], - 'P3M2DT5H', - ], - ]; - } - - /** - * Provide data sets with field settings which are considered valid by the - * {@link validateFieldSettings()} method. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( 'rows' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidFieldSettings() - { - return [ - [ - [], - ], - [ - [ - 'useSeconds' => true, - 'defaultType' => DateAndTime::DEFAULT_EMPTY, - ], - ], - [ - [ - 'useSeconds' => false, - 'defaultType' => DateAndTime::DEFAULT_CURRENT_DATE, - ], - ], - [ - [ - 'useSeconds' => false, - 'defaultType' => DateAndTime::DEFAULT_CURRENT_DATE_ADJUSTED, - 'dateInterval' => new DateInterval('P2Y'), - ], - ], - ]; - } - - /** - * Provide data sets with field settings which are considered invalid by the - * {@link validateFieldSettings()} method. The method must return a - * non-empty array of validation error when receiving such field settings. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * true, - * ), - * array( - * array( 'nonExistentKey' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInValidFieldSettings() - { - return [ - [ - [ - // useSeconds must be bool - 'useSeconds' => 23, - ], - ], - [ - [ - // defaultType must be constant - 'defaultType' => 42, - ], - ], - [ - [ - // No dateInterval allowed with this defaultType - 'defaultType' => DateAndTime::DEFAULT_EMPTY, - 'dateInterval' => new DateInterval('P2Y'), - ], - ], - [ - [ - // dateInterval must be a \DateInterval - 'defaultType' => DateAndTime::DEFAULT_CURRENT_DATE_ADJUSTED, - 'dateInterval' => new stdClass(), - ], - ], - ]; - } - - protected function provideFieldTypeIdentifier() - { - return 'ezdatetime'; - } - - public function provideDataForGetName(): array - { - return [ - [$this->getEmptyValueExpectation(), '', [], 'en_GB'], - [DateAndTimeValue::fromTimestamp(438512400), 'Thu 1983-24-11 09:00:00', [], 'en_GB'], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/DateTest.php b/eZ/Publish/Core/FieldType/Tests/DateTest.php deleted file mode 100644 index 27f2ddb8ae..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/DateTest.php +++ /dev/null @@ -1,374 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use DateTime; -use DateTimeZone; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Date\Type as Date; -use eZ\Publish\Core\FieldType\Date\Value as DateValue; - -/** - * @group fieldType - * @group ezdate - */ -class DateTest extends FieldTypeTest -{ - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new Date(); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return []; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return [ - 'defaultType' => [ - 'type' => 'choice', - 'default' => Date::DEFAULT_EMPTY, - ], - ]; - } - - /** - * Returns the empty value expected from the field type. - */ - protected function getEmptyValueExpectation() - { - return new DateValue(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - [], - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - null, - new DateValue(), - ], - [ - ($dateString = '2012-08-28 12:20 EST'), - new DateValue(new DateTime($dateString)), - ], - [ - ($timestamp = 1346149200), - new DateValue( - new DateTime("@{$timestamp}") - ), - ], - [ - DateValue::fromTimestamp($timestamp = 1372895999), - new DateValue(new DateTime("@{$timestamp}")), - ], - [ - ($dateTime = new DateTime()), - new DateValue($dateTime), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new DateValue(), - null, - ], - [ - new DateValue($dateTime = new DateTime()), - [ - 'timestamp' => $dateTime->setTime(0, 0, 0)->getTimestamp(), - 'rfc850' => $dateTime->format(DateTime::RFC850), - ], - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - $dateTime = new DateTime(); - - return [ - [ - null, - new DateValue(), - ], - [ - [ - 'timestamp' => ($timestamp = 1362614400), - ], - new DateValue(new DateTime("@{$timestamp}")), - ], - [ - [ - // Timezone is not abbreviated because PHP converts it to non-abbreviated local name, - // but this is sufficient to test conversion - 'rfc850' => 'Thursday, 04-Jul-13 23:59:59 Europe/Zagreb', - ], - new DateValue( - $dateTime - ->setTimeZone(new DateTimeZone('Europe/Zagreb')) - ->setTimestamp(1372896000) - ), - ], - ]; - } - - /** - * Provide data sets with field settings which are considered valid by the - * {@link validateFieldSettings()} method. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( 'rows' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidFieldSettings() - { - return [ - [ - [], - ], - [ - [ - 'defaultType' => Date::DEFAULT_EMPTY, - ], - ], - [ - [ - 'defaultType' => Date::DEFAULT_CURRENT_DATE, - ], - ], - ]; - } - - /** - * Provide data sets with field settings which are considered invalid by the - * {@link validateFieldSettings()} method. The method must return a - * non-empty array of validation error when receiving such field settings. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * true, - * ), - * array( - * array( 'nonExistentKey' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInValidFieldSettings() - { - return [ - [ - [ - // non-existent setting - 'useSeconds' => 23, - ], - ], - [ - [ - // defaultType must be constant - 'defaultType' => 42, - ], - ], - ]; - } - - protected function provideFieldTypeIdentifier() - { - return 'ezdate'; - } - - public function provideDataForGetName(): array - { - return [ - [$this->getEmptyValueExpectation(), '', [], 'en_GB'], - [new DateValue(new DateTime('11/24/1983')), 'Thursday 24 November 1983', [], 'en_GB'], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/EmailAddressValidatorTest.php b/eZ/Publish/Core/FieldType/Tests/EmailAddressValidatorTest.php deleted file mode 100644 index 47411dfeb2..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/EmailAddressValidatorTest.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\Core\FieldType\EmailAddress\Value as EmailAddressValue; -use eZ\Publish\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\Validator\EmailAddressValidator; -use PHPUnit\Framework\TestCase; - -/** - * @todo add more tests, like on validateConstraints method - * @group fieldType - * @group validator - */ -class EmailAddressValidatorTest extends TestCase -{ - public function testConstructor() - { - $this->assertInstanceOf( - Validator::class, - new EmailAddressValidator() - ); - } - - /** - * Tests setting and getting constraints. - * - * @covers \eZ\Publish\Core\FieldType\Validator::initializeWithConstraints - * @covers \eZ\Publish\Core\FieldType\Validator::__get - */ - public function testConstraintsInitializeGet() - { - $constraints = [ - 'Extent' => 'regex', - ]; - $validator = new EmailAddressValidator(); - $validator->initializeWithConstraints( - $constraints - ); - $this->assertSame($constraints['Extent'], $validator->Extent); - } - - /** - * Test getting constraints schema. - * - * @covers \eZ\Publish\Core\FieldType\Validator::getConstraintsSchema - */ - public function testGetConstraintsSchema() - { - $constraintsSchema = [ - 'Extent' => [ - 'type' => 'string', - 'default' => 'regex', - ], - ]; - $validator = new EmailAddressValidator(); - $this->assertSame($constraintsSchema, $validator->getConstraintsSchema()); - } - - /** - * Tests setting and getting constraints. - * - * @covers \eZ\Publish\Core\FieldType\Validator::__set - * @covers \eZ\Publish\Core\FieldType\Validator::__get - */ - public function testConstraintsSetGet() - { - $constraints = [ - 'Extent' => 'regex', - ]; - $validator = new EmailAddressValidator(); - $validator->Extent = $constraints['Extent']; - $this->assertSame($constraints['Extent'], $validator->Extent); - } - - public function testValidateCorrectEmailAddresses() - { - $validator = new EmailAddressValidator(); - $validator->Extent = 'regex'; - $emailAddresses = ['john.doe@example.com', 'Info@eZ.No']; - foreach ($emailAddresses as $value) { - $this->assertTrue($validator->validate(new EmailAddressValue($value))); - $this->assertSame([], $validator->getMessage()); - } - } - - /** - * Tests validating a wrong value. - * - * @covers \eZ\Publish\Core\FieldType\Validator\EmailAddressValidator::validate - */ - public function testValidateWrongEmailAddresses() - { - $validator = new EmailAddressValidator(); - $validator->Extent = 'regex'; - $emailAddresses = ['.john.doe@example.com', 'Info-at-eZ.No']; - foreach ($emailAddresses as $value) { - $this->assertFalse($validator->validate(new EmailAddressValue($value))); - } - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/FieldTypeTest.php b/eZ/Publish/Core/FieldType/Tests/FieldTypeTest.php deleted file mode 100644 index 74e3611c41..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/FieldTypeTest.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\Core\Persistence\TransformationProcessor; -use eZ\Publish\SPI\FieldType\Tests\FieldTypeTest as BaseFieldTypeTest; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -abstract class FieldTypeTest extends BaseFieldTypeTest -{ - /** - * @return \eZ\Publish\Core\Persistence\TransformationProcessor|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getTransformationProcessorMock() - { - return $this->getMockForAbstractClass( - TransformationProcessor::class, - [], - '', - false, - true, - true, - ['transform', 'transformByGroup'] - ); - } - - public function provideInputForValuesEqual(): array - { - return $this->provideInputForFromHash(); - } - - /** - * @dataProvider provideInputForValuesEqual - * - * @param mixed $inputValue1Hash - */ - public function testValuesEqual($inputValue1Hash, SPIValue $inputValue2): void - { - $fieldType = $this->getFieldTypeUnderTest(); - - $inputValue1 = $fieldType->fromHash($inputValue1Hash); - - self::assertTrue( - $fieldType->valuesEqual($inputValue1, $inputValue2), - 'valuesEqual() method did not create expected result.' - ); - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/ISBNTest.php b/eZ/Publish/Core/FieldType/Tests/ISBNTest.php deleted file mode 100644 index 46db01699c..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/ISBNTest.php +++ /dev/null @@ -1,336 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\ISBN\Type as ISBN; -use eZ\Publish\Core\FieldType\ISBN\Value as ISBNValue; -use eZ\Publish\Core\FieldType\ValidationError; - -/** - * @group fieldType - * @group ezisbn - */ -class ISBNTest extends FieldTypeTest -{ - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new ISBN('9789722514095'); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return []; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return [ - 'isISBN13' => [ - 'type' => 'boolean', - 'default' => true, - ], - ]; - } - - /** - * Returns the empty value expected from the field type. - * - * @return \eZ\Publish\Core\FieldType\Checkbox\Value - */ - protected function getEmptyValueExpectation() - { - return new ISBNValue(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - 1234567890, - InvalidArgumentException::class, - ], - [ - [], - InvalidArgumentException::class, - ], - [ - new \stdClass(), - InvalidArgumentException::class, - ], - [ - 44.55, - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - '9789722514095', - new ISBNValue('9789722514095'), - ], - [ - '978-972-25-1409-5', - new ISBNValue('978-972-25-1409-5'), - ], - [ - '0-9752298-0-X', - new ISBNValue('0-9752298-0-X'), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new ISBNValue('9789722514095'), - '9789722514095', - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - '9789722514095', - new ISBNValue('9789722514095'), - ], - ]; - } - - protected function provideFieldTypeIdentifier() - { - return 'ezisbn'; - } - - public function provideDataForGetName(): array - { - return [ - [$this->getEmptyValueExpectation(), '', [], 'en_GB'], - [new ISBNValue('9789722514095'), '9789722514095', [], 'en_GB'], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings and - * field value which are considered valid by the {@link validate()} method. - * - * @return array - */ - public function provideValidDataForValidate() - { - return [ - [ - [ - 'fieldSettings' => [ - 'isISBN13' => true, - ], - ], - new ISBNValue(), - ], - [ - [ - 'fieldSettings' => [ - 'isISBN13' => false, - ], - ], - new ISBNValue(), - ], - [ - [ - 'fieldSettings' => [ - 'isISBN13' => true, - ], - ], - new ISBNValue('9789722514095'), - ], - [ - [ - 'fieldSettings' => [ - 'isISBN13' => false, - ], - ], - new ISBNValue('0-9752298-0-X'), - ], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings, - * field value and corresponding validation errors returned by - * the {@link validate()} method. - * - * @return array - */ - public function provideInvalidDataForValidate() - { - return [ - [ - [ - 'fieldSettings' => [ - 'isISBN13' => false, - ], - ], - new ISBNValue('9789722514095'), - [ - new ValidationError('ISBN-10 must be 10 character length', null, [], 'isbn'), - ], - ], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/ImageAssetTest.php b/eZ/Publish/Core/FieldType/Tests/ImageAssetTest.php deleted file mode 100644 index af59674a15..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/ImageAssetTest.php +++ /dev/null @@ -1,396 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\ImageAsset; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\Handler as SPIContentHandler; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * @group fieldType - * @group ezimageasset - */ -class ImageAssetTest extends FieldTypeTest -{ - private const DESTINATION_CONTENT_ID = 14; - - /** @var \eZ\Publish\API\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject */ - private $contentServiceMock; - - /** @var \eZ\Publish\API\Repository\ContentTypeService|\PHPUnit\Framework\MockObject\MockObject */ - private $contentTypeServiceMock; - - /** @var \eZ\Publish\Core\FieldType\ImageAsset\AssetMapper|\PHPUnit\Framework\MockObject\MockObject */ - private $assetMapperMock; - - /** @var \eZ\Publish\SPI\Persistence\Content\Handler|\PHPUnit\Framework\MockObject\MockObject */ - private $contentHandlerMock; - - /** - * {@inheritdoc} - */ - protected function setUp(): void - { - parent::setUp(); - - $this->contentServiceMock = $this->createMock(ContentService::class); - $this->contentTypeServiceMock = $this->createMock(ContentTypeService::class); - $this->assetMapperMock = $this->createMock(ImageAsset\AssetMapper::class); - $this->contentHandlerMock = $this->createMock(SPIContentHandler::class); - $versionInfo = new VersionInfo([ - 'versionNo' => 24, - 'names' => [ - 'en_GB' => 'name_en_GB', - 'de_DE' => 'Name_de_DE', - ], - ]); - $currentVersionNo = 28; - $destinationContentInfo = $this->createMock(ContentInfo::class); - $destinationContentInfo - ->method('__get') - ->willReturnMap([ - ['currentVersionNo', $currentVersionNo], - ['mainLanguageCode', 'en_GB'], - ]); - - $this->contentHandlerMock - ->method('loadContentInfo') - ->with(self::DESTINATION_CONTENT_ID) - ->willReturn($destinationContentInfo); - - $this->contentHandlerMock - ->method('loadVersionInfo') - ->with(self::DESTINATION_CONTENT_ID, $currentVersionNo) - ->willReturn($versionInfo); - } - - /** - * {@inheritdoc} - */ - protected function provideFieldTypeIdentifier(): string - { - return ImageAsset\Type::FIELD_TYPE_IDENTIFIER; - } - - /** - * {@inheritdoc} - */ - protected function createFieldTypeUnderTest() - { - return new ImageAsset\Type( - $this->contentServiceMock, - $this->contentTypeServiceMock, - $this->assetMapperMock, - $this->contentHandlerMock - ); - } - - /** - * {@inheritdoc} - */ - protected function getValidatorConfigurationSchemaExpectation(): array - { - return []; - } - - /** - * {@inheritdoc} - */ - protected function getSettingsSchemaExpectation(): array - { - return []; - } - - /** - * {@inheritdoc} - */ - protected function getEmptyValueExpectation() - { - return new ImageAsset\Value(); - } - - /** - * {@inheritdoc} - */ - public function provideInvalidInputForAcceptValue(): array - { - return [ - [ - true, - InvalidArgumentException::class, - ], - ]; - } - - /** - * {@inheritdoc} - */ - public function provideValidInputForAcceptValue(): array - { - $destinationContentId = 7; - - return [ - [ - null, - $this->getEmptyValueExpectation(), - ], - [ - $destinationContentId, - new ImageAsset\Value($destinationContentId), - ], - [ - new ContentInfo([ - 'id' => $destinationContentId, - ]), - new ImageAsset\Value($destinationContentId), - ], - ]; - } - - /** - * {@inheritdoc} - */ - public function provideInputForToHash(): array - { - $destinationContentId = 7; - $alternativeText = 'The alternative text for image'; - - return [ - [ - new ImageAsset\Value(), - [ - 'destinationContentId' => null, - 'alternativeText' => null, - ], - ], - [ - new ImageAsset\Value($destinationContentId), - [ - 'destinationContentId' => $destinationContentId, - 'alternativeText' => null, - ], - ], - [ - new ImageAsset\Value($destinationContentId, $alternativeText), - [ - 'destinationContentId' => $destinationContentId, - 'alternativeText' => $alternativeText, - ], - ], - ]; - } - - /** - * {@inheritdoc} - */ - public function provideInputForFromHash(): array - { - $destinationContentId = 7; - $alternativeText = 'The alternative text for image'; - - return [ - [ - null, - new ImageAsset\Value(), - ], - [ - [ - 'destinationContentId' => $destinationContentId, - 'alternativeText' => null, - ], - new ImageAsset\Value($destinationContentId), - ], - [ - [ - 'destinationContentId' => $destinationContentId, - 'alternativeText' => $alternativeText, - ], - new ImageAsset\Value($destinationContentId, $alternativeText), - ], - ]; - } - - /** - * {@inheritdoc} - */ - public function provideInvalidDataForValidate(): array - { - return []; - } - - /** - * {@inheritdoc} - */ - public function testValidateNonAsset() - { - $destinationContentId = 7; - $destinationContent = $this->createMock(Content::class); - $invalidContentTypeId = 789; - $invalidContentTypeIdentifier = 'article'; - $invalidContentType = $this->createMock(ContentType::class); - - $destinationContentInfo = $this->createMock(ContentInfo::class); - - $destinationContentInfo - ->expects($this->once()) - ->method('__get') - ->with('contentTypeId') - ->willReturn($invalidContentTypeId); - - $destinationContent - ->expects($this->once()) - ->method('__get') - ->with('contentInfo') - ->willReturn($destinationContentInfo); - - $this->contentServiceMock - ->expects($this->once()) - ->method('loadContent') - ->with($destinationContentId) - ->willReturn($destinationContent); - - $this->assetMapperMock - ->expects($this->once()) - ->method('isAsset') - ->with($destinationContent) - ->willReturn(false); - - $this->contentTypeServiceMock - ->expects($this->once()) - ->method('loadContentType') - ->with($invalidContentTypeId) - ->willReturn($invalidContentType); - - $invalidContentType - ->expects($this->once()) - ->method('__get') - ->with('identifier') - ->willReturn($invalidContentTypeIdentifier); - - $validationErrors = $this->doValidate([], new ImageAsset\Value($destinationContentId)); - - $this->assertIsArray($validationErrors); - $this->assertEquals([ - new ValidationError( - 'Content %type% is not a valid asset target', - null, - [ - '%type%' => $invalidContentTypeIdentifier, - ], - 'destinationContentId' - ), - ], $validationErrors); - } - - /** - * {@inheritdoc} - */ - public function provideValidDataForValidate(): array - { - return [ - [ - [], - $this->getEmptyValueExpectation(), - ], - ]; - } - - public function testValidateValidNonEmptyAssetValue() - { - $destinationContentId = 7; - $destinationContent = $this->createMock(Content::class); - - $this->contentServiceMock - ->expects($this->once()) - ->method('loadContent') - ->with($destinationContentId) - ->willReturn($destinationContent); - - $this->assetMapperMock - ->expects($this->once()) - ->method('isAsset') - ->with($destinationContent) - ->willReturn(true); - - $validationErrors = $this->doValidate([], new ImageAsset\Value($destinationContentId)); - - $this->assertIsArray($validationErrors); - $this->assertEmpty($validationErrors, "Got value:\n" . var_export($validationErrors, true)); - } - - /** - * {@inheritdoc} - */ - public function provideDataForGetName(): array - { - return [ - 'empty_destination_content_id' => [ - $this->getEmptyValueExpectation(), - '', - [], - 'en_GB', - ], - 'destination_content_id' => [ - new ImageAsset\Value(self::DESTINATION_CONTENT_ID), 'name_en_GB', [], 'en_GB', - ], - 'destination_content_id_de_DE' => [ - new ImageAsset\Value(self::DESTINATION_CONTENT_ID), 'Name_de_DE', [], 'de_DE', - ], - ]; - } - - /** - * @dataProvider provideDataForGetName - */ - public function testGetName( - SPIValue $value, - string $expected, - array $fieldSettings = [], - string $languageCode = 'en_GB' - ): void { - /** @var \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition|\PHPUnit\Framework\MockObject\MockObject $fieldDefinitionMock */ - $fieldDefinitionMock = $this->createMock(FieldDefinition::class); - $fieldDefinitionMock->method('getFieldSettings')->willReturn($fieldSettings); - - $name = $this->getFieldTypeUnderTest()->getName($value, $fieldDefinitionMock, $languageCode); - - self::assertSame($expected, $name); - } - - public function testIsSearchable() - { - $this->assertTrue($this->getFieldTypeUnderTest()->isSearchable()); - } - - /** - * @covers \eZ\Publish\Core\FieldType\Relation\Type::getRelations - */ - public function testGetRelations() - { - $destinationContentId = 7; - $fieldType = $this->createFieldTypeUnderTest(); - - $this->assertEquals( - [ - Relation::ASSET => [$destinationContentId], - ], - $fieldType->getRelations($fieldType->acceptValue($destinationContentId)) - ); - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/ImageTest.php b/eZ/Publish/Core/FieldType/Tests/ImageTest.php deleted file mode 100644 index 0556ac2df5..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/ImageTest.php +++ /dev/null @@ -1,915 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Image\Type as ImageType; -use eZ\Publish\Core\FieldType\Image\Value as ImageValue; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator; -use eZ\Publish\Core\FieldType\Validator\ImageValidator; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\SPI\FieldType\BinaryBase\MimeTypeDetector; - -/** - * @group fieldType - * @group ezfloat - */ -class ImageTest extends FieldTypeTest -{ - protected $blackListedExtensions = [ - 'php', - 'php3', - 'phar', - 'phpt', - 'pht', - 'phtml', - 'pgif', - ]; - - public function getImageInputPath() - { - return __DIR__ . '/squirrel-developers.jpg'; - } - - /** - * @return \eZ\Publish\SPI\FieldType\BinaryBase\MimeTypeDetector - */ - protected function getMimeTypeDetectorMock() - { - if (!isset($this->mimeTypeDetectorMock)) { - $this->mimeTypeDetectorMock = $this->createMock(MimeTypeDetector::class); - } - - return $this->mimeTypeDetectorMock; - } - - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new ImageType([ - $this->getBlackListValidatorMock(), - $this->getImageValidatorMock(), - ]); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - private function getBlackListValidatorMock() - { - return $this - ->getMockBuilder(FileExtensionBlackListValidator::class) - ->setConstructorArgs([ - $this->getConfigResolverMock(), - ]) - ->setMethods(null) - ->getMock(); - } - - private function getImageValidatorMock() - { - return $this - ->getMockBuilder(ImageValidator::class) - ->setMethods(null) - ->getMock(); - } - - private function getConfigResolverMock() - { - $configResolver = $this - ->createMock(ConfigResolverInterface::class); - - $configResolver - ->method('getParameter') - ->with('io.file_storage.file_type_blacklist') - ->willReturn($this->blackListedExtensions); - - return $configResolver; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return [ - 'FileSizeValidator' => [ - 'maxFileSize' => [ - 'type' => 'int', - 'default' => null, - ], - ], - 'AlternativeTextValidator' => [ - 'required' => [ - 'type' => 'bool', - 'default' => false, - ], - ], - ]; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return []; - } - - /** - * Returns the empty value expected from the field type. - * - * @return \eZ\Publish\Core\FieldType\Image\Value - */ - protected function getEmptyValueExpectation() - { - return new ImageValue(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - 'foo', - InvalidArgumentException::class, - ], - [ - new ImageValue( - [ - 'id' => 'non/existent/path', - ] - ), - InvalidArgumentException::class, - ], - [ - new ImageValue( - [ - 'id' => __FILE__, - 'fileName' => [], - ] - ), - InvalidArgumentException::class, - ], - [ - new ImageValue( - [ - 'id' => __FILE__, - 'fileName' => 'ImageTest.php', - 'fileSize' => 'truebar', - ] - ), - InvalidArgumentException::class, - ], - [ - new ImageValue( - [ - 'id' => __FILE__, - 'fileName' => 'ImageTest.php', - 'fileSize' => 23, - 'alternativeText' => [], - ] - ), - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'id' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - null, - new ImageValue(), - ], - [ - [], - new ImageValue(), - ], - [ - new ImageValue(), - new ImageValue(), - ], - [ - $this->getImageInputPath(), - new ImageValue( - [ - 'inputUri' => $this->getImageInputPath(), - 'fileName' => basename($this->getImageInputPath()), - 'fileSize' => filesize($this->getImageInputPath()), - 'alternativeText' => null, - ] - ), - ], - [ - [ - 'id' => $this->getImageInputPath(), - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'fileSize' => 23, - 'alternativeText' => 'This is so Sindelfingen!', - 'uri' => 'http://' . $this->getImageInputPath(), - ], - new ImageValue( - [ - 'id' => $this->getImageInputPath(), - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'fileSize' => 23, - 'alternativeText' => 'This is so Sindelfingen!', - 'uri' => 'http://' . $this->getImageInputPath(), - ] - ), - ], - [ - [ - 'inputUri' => $this->getImageInputPath(), - 'fileName' => 'My Fancy Filename', - 'fileSize' => 123, - ], - new ImageValue( - [ - 'inputUri' => $this->getImageInputPath(), - 'fileName' => 'My Fancy Filename', - 'fileSize' => filesize($this->getImageInputPath()), - ] - ), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new ImageValue(), - null, - ], - [ - new ImageValue( - [ - 'id' => $this->getImageInputPath(), - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'fileSize' => 23, - 'alternativeText' => 'This is so Sindelfingen!', - 'imageId' => '123-12345', - 'uri' => 'http://' . $this->getImageInputPath(), - 'width' => 123, - 'height' => 456, - ] - ), - [ - 'id' => $this->getImageInputPath(), - 'path' => $this->getImageInputPath(), - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'fileSize' => 23, - 'alternativeText' => 'This is so Sindelfingen!', - 'imageId' => '123-12345', - 'uri' => 'http://' . $this->getImageInputPath(), - 'inputUri' => null, - 'width' => 123, - 'height' => 456, - 'additionalData' => [], - ], - ], - // BC with 5.0 (EZP-20948). Path can be used as input instead of $inputUri. - [ - new ImageValue( - [ - 'path' => $this->getImageInputPath(), - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'fileSize' => 23, - 'alternativeText' => 'This is so Sindelfingen!', - 'imageId' => '123-12345', - 'uri' => 'http://' . $this->getImageInputPath(), - ] - ), - [ - 'id' => null, - 'path' => $this->getImageInputPath(), - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'fileSize' => 23, - 'alternativeText' => 'This is so Sindelfingen!', - 'imageId' => '123-12345', - 'uri' => 'http://' . $this->getImageInputPath(), - 'inputUri' => $this->getImageInputPath(), - 'width' => null, - 'height' => null, - 'additionalData' => [], - ], - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - null, - new ImageValue(), - ], - [ - [ - 'id' => $this->getImageInputPath(), - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'fileSize' => 23, - 'alternativeText' => 'This is so Sindelfingen!', - 'uri' => 'http://' . $this->getImageInputPath(), - ], - new ImageValue( - [ - 'id' => $this->getImageInputPath(), - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'fileSize' => 23, - 'alternativeText' => 'This is so Sindelfingen!', - 'uri' => 'http://' . $this->getImageInputPath(), - ] - ), - ], - // BC with 5.0 (EZP-20948). Path can be used as input instead of ID. - [ - [ - 'path' => $this->getImageInputPath(), - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'fileSize' => 23, - 'alternativeText' => 'This is so Sindelfingen!', - 'uri' => 'http://' . $this->getImageInputPath(), - ], - new ImageValue( - [ - 'inputUri' => $this->getImageInputPath(), - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'fileSize' => 23, - 'alternativeText' => 'This is so Sindelfingen!', - 'uri' => 'http://' . $this->getImageInputPath(), - ] - ), - ], - // @todo: Provide REST upload tests - ]; - } - - protected function provideFieldTypeIdentifier() - { - return 'ezimage'; - } - - public function provideDataForGetName(): array - { - return [ - [$this->getEmptyValueExpectation(), '', [], 'en_GB'], - [ - new ImageValue(['fileName' => 'Sindelfingen-Squirrels.jpg']), - 'Sindelfingen-Squirrels.jpg', - [], - 'en_GB', - ], - // Alternative text has priority - [ - new ImageValue( - [ - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'alternativeText' => 'This is so Sindelfingen!', - ] - ), - 'This is so Sindelfingen!', - [], - 'en_GB', - ], - [ - new ImageValue( - [ - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'alternativeText' => 'This is so Sindelfingen!', - ] - ), - 'This is so Sindelfingen!', - [], - 'en_GB', - ], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings and - * field value which are considered valid by the {@link validate()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten if - * a FieldType supports validation! - * - * For example: - * - * <code> - * return array( - * array( - * array( - * "validatorConfiguration" => array( - * "StringLengthValidator" => array( - * "minStringLength" => 2, - * "maxStringLength" => 10, - * ), - * ), - * ), - * new TextLineValue( "lalalala" ), - * ), - * array( - * array( - * "fieldSettings" => array( - * 'isMultiple' => true - * ), - * ), - * new CountryValue( - * array( - * "BE" => array( - * "Name" => "Belgium", - * "Alpha2" => "BE", - * "Alpha3" => "BEL", - * "IDC" => 32, - * ), - * ), - * ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidDataForValidate() - { - return [ - [ - [ - 'validatorConfiguration' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 1, - ], - ], - ], - new ImageValue( - [ - 'id' => $this->getImageInputPath(), - 'fileName' => basename($this->getImageInputPath()), - 'fileSize' => filesize($this->getImageInputPath()), - 'alternativeText' => null, - 'uri' => '', - ] - ), - ], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings, - * field value and corresponding validation errors returned by - * the {@link validate()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten - * if a FieldType supports validation! - * - * For example: - * - * <code> - * return array( - * array( - * array( - * "validatorConfiguration" => array( - * "IntegerValueValidator" => array( - * "minIntegerValue" => 5, - * "maxIntegerValue" => 10 - * ), - * ), - * ), - * new IntegerValue( 3 ), - * array( - * new ValidationError( - * "The value can not be lower than %size%.", - * null, - * array( - * "size" => 5 - * ), - * ), - * ), - * ), - * array( - * array( - * "fieldSettings" => array( - * "isMultiple" => false - * ), - * ), - * new CountryValue( - * "BE" => array( - * "Name" => "Belgium", - * "Alpha2" => "BE", - * "Alpha3" => "BEL", - * "IDC" => 32, - * ), - * "FR" => array( - * "Name" => "France", - * "Alpha2" => "FR", - * "Alpha3" => "FRA", - * "IDC" => 33, - * ), - * ) - * ), - * array( - * new ValidationError( - * "Field definition does not allow multiple countries to be selected." - * ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidDataForValidate() - { - return [ - 'file is too large' => [ - [ - 'validatorConfiguration' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 0.01, - ], - ], - ], - new ImageValue( - [ - 'id' => $this->getImageInputPath(), - 'fileName' => basename($this->getImageInputPath()), - 'fileSize' => filesize($this->getImageInputPath()), - 'alternativeText' => null, - 'uri' => '', - ] - ), - [ - new ValidationError( - 'The file size cannot exceed %size% megabyte.', - 'The file size cannot exceed %size% megabytes.', - [ - '%size%' => 0.01, - ], - 'fileSize' - ), - ], - ], - 'file is not an image file' => [ - [ - 'validatorConfiguration' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 1, - ], - ], - ], - new ImageValue( - [ - 'id' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'alternativeText' => null, - 'uri' => '', - ] - ), - [ - new ValidationError( - 'A valid file is required. The following file extensions are not allowed: %extensionsBlackList%', - null, - ['%extensionsBlackList%' => implode(', ', $this->blackListedExtensions)], - 'fileExtensionBlackList' - ), - new ValidationError( - 'A valid image file is required.', - null, - [], - 'id' - ), - ], - ], - 'file is too large and invalid' => [ - [ - 'validatorConfiguration' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 0.01, - ], - ], - ], - new ImageValue( - [ - 'id' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'alternativeText' => null, - 'uri' => '', - ] - ), - [ - new ValidationError( - 'A valid file is required. The following file extensions are not allowed: %extensionsBlackList%', - null, - ['%extensionsBlackList%' => implode(', ', $this->blackListedExtensions)], - 'fileExtensionBlackList' - ), - new ValidationError('A valid image file is required.', null, [], 'id'), - new ValidationError( - 'The file size cannot exceed %size% megabyte.', - 'The file size cannot exceed %size% megabytes.', - [ - '%size%' => 0.01, - ], - 'fileSize' - ), - ], - ], - 'file is an image file but filename ends with .php' => [ - [ - 'validatorConfiguration' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 1, - ], - ], - ], - new ImageValue( - [ - 'id' => __DIR__ . '/phppng.php', - 'fileName' => basename(__DIR__ . '/phppng.php'), - 'fileSize' => filesize(__DIR__ . '/phppng.php'), - 'alternativeText' => null, - 'uri' => '', - ] - ), - [ - new ValidationError( - 'A valid file is required. The following file extensions are not allowed: %extensionsBlackList%', - null, - ['%extensionsBlackList%' => implode(', ', $this->blackListedExtensions)], - 'fileExtensionBlackList' - ), - new ValidationError( - 'A valid image file is required.', - null, - [], - 'id' - ), - ], - ], - 'file is an image file but filename ends with .PHP (upper case)' => [ - [ - 'validatorConfiguration' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 1, - ], - ], - ], - new ImageValue( - [ - 'id' => __DIR__ . '/phppng2.PHP', - 'fileName' => basename(__DIR__ . '/phppng2.PHP'), - 'fileSize' => filesize(__DIR__ . '/phppng2.PHP'), - 'alternativeText' => null, - 'uri' => '', - ] - ), - [ - new ValidationError( - 'A valid file is required. The following file extensions are not allowed: %extensionsBlackList%', - null, - ['%extensionsBlackList%' => implode(', ', $this->blackListedExtensions)], - 'fileExtensionBlackList' - ), - new ValidationError( - 'A valid image file is required.', - null, - [], - 'id' - ), - ], - ], - 'alternative text is null' => [ - [ - 'validatorConfiguration' => [ - 'AlternativeTextValidator' => [ - 'required' => true, - ], - ], - ], - new ImageValue( - [ - 'id' => $this->getImageInputPath(), - 'fileName' => basename($this->getImageInputPath()), - 'fileSize' => filesize($this->getImageInputPath()), - 'alternativeText' => null, - 'uri' => '', - ] - ), - [ - new ValidationError( - 'Alternative text is required.', - null, - [], - 'alternativeText' - ), - ], - ], - 'alternative text is empty string' => [ - [ - 'validatorConfiguration' => [ - 'AlternativeTextValidator' => [ - 'required' => true, - ], - ], - ], - new ImageValue( - [ - 'id' => $this->getImageInputPath(), - 'fileName' => basename($this->getImageInputPath()), - 'fileSize' => filesize($this->getImageInputPath()), - 'alternativeText' => '', - 'uri' => '', - ] - ), - [ - new ValidationError( - 'Alternative text is required.', - null, - [], - 'alternativeText' - ), - ], - ], - ]; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function provideInputForValuesEqual(): array - { - return [ - [ - [ - 'id' => $this->getImageInputPath(), - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'fileSize' => 23, - 'alternativeText' => 'This is so Sindelfingen!', - 'imageId' => '123-12345', - 'uri' => 'http://' . $this->getImageInputPath(), - 'width' => 123, - 'height' => 456, - ], - new ImageValue( - [ - 'id' => $this->getImageInputPath(), - 'path' => $this->getImageInputPath(), - 'fileName' => 'Sindelfingen-Squirrels.jpg', - 'fileSize' => 23, - 'alternativeText' => 'This is so Sindelfingen!', - 'imageId' => '123-12317', - 'uri' => 'http://' . $this->getImageInputPath(), - 'inputUri' => null, - 'width' => 123, - 'height' => 456, - ] - ), - ], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/Integration/BinaryBase/BinaryBaseStorage/BinaryBaseStorageGatewayTest.php b/eZ/Publish/Core/FieldType/Tests/Integration/BinaryBase/BinaryBaseStorage/BinaryBaseStorageGatewayTest.php deleted file mode 100644 index 876891e79b..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/Integration/BinaryBase/BinaryBaseStorage/BinaryBaseStorageGatewayTest.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\FieldType\Tests\Integration\BinaryBase\BinaryBaseStorage; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage\Gateway as BinaryBaseStorageGateway; -use eZ\Publish\Core\FieldType\BinaryFile\BinaryFileStorage\Gateway\DoctrineStorage; -use eZ\Publish\Core\FieldType\Tests\Integration\BaseCoreFieldTypeIntegrationTest; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use eZ\Publish\SPI\Tests\Persistence\FixtureImporter; -use eZ\Publish\SPI\Tests\Persistence\YamlFixture; - -/** - * BinaryBase Field Type external storage gateway tests. - */ -class BinaryBaseStorageGatewayTest extends BaseCoreFieldTypeIntegrationTest -{ - protected function setUp(): void - { - parent::setUp(); - - $importer = new FixtureImporter($this->getDatabaseConnection()); - $importer->import(new YamlFixture(__DIR__ . '/_fixtures/ezbinaryfile.yaml')); - } - - protected function getGateway(): BinaryBaseStorageGateway - { - return new DoctrineStorage($this->getDatabaseConnection()); - } - - public function testGetFileReferenceWithFixture(): void - { - $data = $this->getGateway()->getFileReferenceData(10, 1); - - $expected = [ - 'id' => 'image/a6bbf351175ad9c2f27e5b17c2c5d105.png', - 'mimeType' => 'image/png', - 'fileName' => 'test.png', - 'downloadCount' => 0, - ]; - - self::assertEquals($expected, $data); - } - - public function testStoreFileReference(): void - { - $field = new Field(); - $field->id = 123; - $field->fieldDefinitionId = 231; - $field->type = 'ezbinaryfile'; - $field->versionNo = 1; - $field->value = new FieldValue([ - 'externalData' => [ - 'id' => 'image/809c753a26e11f363cd8c14d824d162a.jpg', - 'path' => '/tmp/phpR4tNSI', - 'inputUri' => '/tmp/phpR4tNSI', - 'fileName' => '1.jpg', - 'fileSize' => '372949', - 'mimeType' => 'image/jpg', - 'uri' => '/admin/content/download/75/320?version=1', - 'downloadCount' => 0, - ], - ]); - - $versionInfo = new VersionInfo([ - 'contentInfo' => new ContentInfo([ - 'id' => 1, - ]), - 'versionNo' => 1, - ]); - - $this->getGateway()->storeFileReference($versionInfo, $field); - - $data = $this->getGateway()->getFileReferenceData(123, 1); - - $expected = [ - 'id' => 'image/809c753a26e11f363cd8c14d824d162a.jpg', - 'mimeType' => 'image/jpg', - 'fileName' => '1.jpg', - 'downloadCount' => 0, - ]; - - self::assertEquals($expected, $data); - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/Integration/BinaryBase/BinaryBaseStorage/BinaryBaseStorageTest.php b/eZ/Publish/Core/FieldType/Tests/Integration/BinaryBase/BinaryBaseStorage/BinaryBaseStorageTest.php deleted file mode 100644 index 6e3eff2af2..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/Integration/BinaryBase/BinaryBaseStorage/BinaryBaseStorageTest.php +++ /dev/null @@ -1,171 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\FieldType\Tests\Integration\BinaryBase\BinaryBaseStorage; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage; -use eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage\Gateway; -use eZ\Publish\Core\FieldType\BinaryFile\BinaryFileStorage\Gateway\DoctrineStorage; -use eZ\Publish\Core\FieldType\Tests\Integration\BaseCoreFieldTypeIntegrationTest; -use eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\IO\Values\BinaryFileCreateStruct; -use eZ\Publish\SPI\FieldType\BinaryBase\PathGenerator; -use eZ\Publish\SPI\IO\MimeTypeDetector; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -class BinaryBaseStorageTest extends BaseCoreFieldTypeIntegrationTest -{ - /** @var \eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage\Gateway|\PHPUnit\Framework\MockObject\MockObject */ - protected $gateway; - - /** @var \eZ\Publish\SPI\FieldType\BinaryBase\PathGenerator|\PHPUnit\Framework\MockObject\MockObject */ - protected $pathGeneratorMock; - - /** @var \eZ\Publish\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ - protected $ioServiceMock; - - /** @var \eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage|\PHPUnit\Framework\MockObject\MockObject */ - protected $storage; - - /** @var \eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator&\PHPUnit\Framework\MockObject\MockObject */ - protected $fileExtensionBlackListValidatorMock; - - protected function setUp(): void - { - parent::setUp(); - - $this->gateway = $this->getStorageGateway(); - $this->pathGeneratorMock = $this->createMock(PathGenerator::class); - $this->ioServiceMock = $this->createMock(IOServiceInterface::class); - $this->fileExtensionBlackListValidatorMock = $this->createMock( - FileExtensionBlackListValidator::class - ); - $this->storage = $this->getMockBuilder(BinaryBaseStorage::class) - ->onlyMethods([]) - ->setConstructorArgs( - [ - $this->gateway, - $this->ioServiceMock, - $this->pathGeneratorMock, - $this->createMock(MimeTypeDetector::class), - $this->fileExtensionBlackListValidatorMock, - ] - ) - ->getMock(); - } - - protected function getContext(): array - { - return ['context']; - } - - public function testHasFieldData(): void - { - self::assertTrue($this->storage->hasFieldData()); - } - - /** - * @dataProvider providerOfFieldData - */ - public function testStoreFieldData(VersionInfo $versionInfo, Field $field): void - { - $binaryFileCreateStruct = new BinaryFileCreateStruct([ - 'id' => 'qwerty12345', - 'size' => '372949', - 'mimeType' => 'image/jpeg', - ]); - - $this->ioServiceMock - ->expects(self::once()) - ->method('newBinaryCreateStructFromLocalFile') - ->will($this->returnValue($binaryFileCreateStruct)); - - $this->pathGeneratorMock - ->expects(self::once()) - ->method('getStoragePathForField') - ->with($field, $versionInfo) - ->willReturn('image/qwerty12345.jpg'); - - $this->ioServiceMock - ->expects(self::once()) - ->method('createBinaryFile') - ->with($binaryFileCreateStruct) - ->willReturn(new BinaryFile()); - - $this->storage->storeFieldData($versionInfo, $field, $this->getContext()); - - $this->expectNotToPerformAssertions(); - } - - /** - * @depends testStoreFieldData - * - * @dataProvider providerOfFieldData - */ - public function testCopyLegacyField(VersionInfo $versionInfo, Field $originalField): void - { - $field = clone $originalField; - $field->id = 124; - $field->versionNo = 2; - $field->value = new FieldValue([ - 'externalData' => [ - 'fileName' => '123.jpg', - 'downloadCount' => 0, - 'mimeType' => null, - 'uri' => null, - ], - ]); - - $flag = $this->storage->copyLegacyField($versionInfo, $field, $originalField, $this->getContext()); - - self::assertFalse($flag); - } - - public function providerOfFieldData(): array - { - $field = new Field(); - $field->id = 124; - $field->fieldDefinitionId = 231; - $field->type = 'ezbinaryfile'; - $field->versionNo = 1; - $field->value = new FieldValue([ - 'externalData' => [ - 'id' => 'image/aaac753a26e11f363cd8c14d824d162a.jpg', - 'path' => '/tmp/phpR4tNSV', - 'inputUri' => '/tmp/phpR4tNSV', - 'fileName' => '123.jpg', - 'fileSize' => '12345', - 'mimeType' => 'image/jpeg', - 'uri' => '/admin/content/download/75/320?version=1', - 'downloadCount' => 0, - ], - ]); - - $versionInfo = new VersionInfo([ - 'contentInfo' => new ContentInfo([ - 'id' => 235, - 'contentTypeId' => 24, - ]), - 'versionNo' => 1, - ]); - - return [ - [$versionInfo, $field], - ]; - } - - protected function getStorageGateway(): Gateway - { - return new DoctrineStorage($this->getDatabaseConnection()); - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/KeywordTest.php b/eZ/Publish/Core/FieldType/Tests/KeywordTest.php deleted file mode 100644 index 4c058021c5..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/KeywordTest.php +++ /dev/null @@ -1,264 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Keyword\Type as KeywordType; -use eZ\Publish\Core\FieldType\Keyword\Value as KeywordValue; - -/** - * @group fieldType - * @group ezinteger - */ -class KeywordTest extends FieldTypeTest -{ - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new KeywordType(); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return []; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return []; - } - - /** - * Returns the empty value expected from the field type. - */ - protected function getEmptyValueExpectation() - { - return new KeywordValue([]); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - 23, - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - null, - new KeywordValue([]), - ], - [ - [], - new KeywordValue([]), - ], - [ - 'foo', - new KeywordValue(['foo']), - ], - [ - ['foo'], - new KeywordValue(['foo']), - ], - [ - new KeywordValue(['foo']), - new KeywordValue(['foo']), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new KeywordValue([]), - [], - ], - [ - new KeywordValue(['foo', 'bar']), - ['foo', 'bar'], - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - [], - new KeywordValue([]), - ], - [ - ['foo', 'bar'], - new KeywordValue(['foo', 'bar']), - ], - ]; - } - - protected function provideFieldTypeIdentifier() - { - return 'ezkeyword'; - } - - public function provideDataForGetName(): array - { - return [ - [$this->getEmptyValueExpectation(), '', [], 'en_GB'], - [new KeywordValue(['foo', 'bar']), 'foo, bar', [], 'en_GB'], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/MediaTest.php b/eZ/Publish/Core/FieldType/Tests/MediaTest.php deleted file mode 100644 index 4c61674bbf..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/MediaTest.php +++ /dev/null @@ -1,773 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\BinaryFile\Value as BinaryFileValue; -use eZ\Publish\Core\FieldType\Media\Type as MediaType; -use eZ\Publish\Core\FieldType\Media\Value as MediaValue; -use eZ\Publish\Core\FieldType\ValidationError; - -/** - * @group fieldType - * @group ezbinaryfile - */ -class MediaTest extends BinaryBaseTest -{ - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new MediaType([$this->getBlackListValidatorMock()]); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - protected function getEmptyValueExpectation() - { - return new MediaValue(); - } - - protected function getSettingsSchemaExpectation() - { - return [ - 'mediaType' => [ - 'type' => 'choice', - 'default' => MediaType::TYPE_HTML5_VIDEO, - ], - ]; - } - - public function provideInvalidInputForAcceptValue() - { - $baseInput = parent::provideInvalidInputForAcceptValue(); - $binaryFileInput = [ - [ - new MediaValue(['id' => '/foo/bar']), - InvalidArgumentException::class, - ], - [ - new MediaValue(['hasController' => 'yes']), - InvalidArgumentException::class, - ], - [ - new MediaValue(['autoplay' => 'yes']), - InvalidArgumentException::class, - ], - [ - new MediaValue(['loop' => 'yes']), - InvalidArgumentException::class, - ], - [ - new MediaValue(['height' => []]), - InvalidArgumentException::class, - ], - [ - new MediaValue(['width' => []]), - InvalidArgumentException::class, - ], - ]; - - return array_merge($baseInput, $binaryFileInput); - } - - public function provideValidInputForAcceptValue() - { - return [ - [ - null, - new MediaValue(), - ], - [ - new MediaValue(), - new MediaValue(), - ], - [ - __FILE__, - new MediaValue( - [ - 'inputUri' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'hasController' => false, - 'autoplay' => false, - 'loop' => false, - 'width' => 0, - 'height' => 0, - 'uri' => '', - ] - ), - ], - [ - ['inputUri' => __FILE__], - new MediaValue( - [ - 'inputUri' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'hasController' => false, - 'autoplay' => false, - 'loop' => false, - 'width' => 0, - 'height' => 0, - 'uri' => '', - ] - ), - ], - [ - [ - 'inputUri' => __FILE__, - 'fileSize' => 23, - ], - new MediaValue( - [ - 'inputUri' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => 23, - 'hasController' => false, - 'autoplay' => false, - 'loop' => false, - 'width' => 0, - 'height' => 0, - 'uri' => '', - ] - ), - ], - [ - [ - 'inputUri' => __FILE__, - 'mimeType' => 'application/text+php', - ], - new MediaValue( - [ - 'inputUri' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'mimeType' => 'application/text+php', - 'hasController' => false, - 'autoplay' => false, - 'loop' => false, - 'width' => 0, - 'height' => 0, - 'uri' => '', - ] - ), - ], - [ - [ - 'inputUri' => __FILE__, - 'hasController' => true, - ], - new MediaValue( - [ - 'inputUri' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'hasController' => true, - 'autoplay' => false, - 'loop' => false, - 'width' => 0, - 'height' => 0, - 'uri' => '', - ] - ), - ], - [ - [ - 'inputUri' => __FILE__, - 'autoplay' => true, - ], - new MediaValue( - [ - 'inputUri' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'hasController' => false, - 'autoplay' => true, - 'loop' => false, - 'width' => 0, - 'height' => 0, - 'uri' => '', - ] - ), - ], - [ - [ - 'inputUri' => __FILE__, - 'loop' => true, - ], - new MediaValue( - [ - 'inputUri' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - 'uri' => '', - ] - ), - ], - [ - [ - 'inputUri' => __FILE__, - 'width' => 23, - ], - new MediaValue( - [ - 'inputUri' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'hasController' => false, - 'autoplay' => false, - 'loop' => false, - 'width' => 23, - 'height' => 0, - 'uri' => '', - ] - ), - ], - [ - [ - 'inputUri' => __FILE__, - 'height' => 42, - ], - new MediaValue( - [ - 'inputUri' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'hasController' => false, - 'autoplay' => false, - 'loop' => false, - 'width' => 0, - 'height' => 42, - 'uri' => '', - ] - ), - ], - // BC with 5.2 (EZP-22808). Id can be used as input instead of inputUri. - [ - ['id' => __FILE__], - new MediaValue( - [ - 'inputUri' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'hasController' => false, - 'autoplay' => false, - 'loop' => false, - 'width' => 0, - 'height' => 0, - 'uri' => '', - ] - ), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( - * array( - * 'id' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * array( - * 'id' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new MediaValue(), - null, - ], - [ - new MediaValue( - [ - 'inputUri' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - 'uri' => 'http://' . basename(__FILE__), - ] - ), - [ - 'id' => null, - 'inputUri' => __FILE__, - 'path' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - 'uri' => 'http://' . basename(__FILE__), - ], - ], - // BC with 5.0 (EZP-20948). Path can be used as input instead of inputUri. - [ - new MediaValue( - [ - 'path' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - 'uri' => 'http://' . basename(__FILE__), - ] - ), - [ - 'id' => null, - 'inputUri' => __FILE__, - 'path' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - 'uri' => 'http://' . basename(__FILE__), - ], - ], - // BC with 5.2 (EZP-22808). Id can be used as input instead of inputUri. - [ - new MediaValue( - [ - 'id' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - 'uri' => 'http://' . basename(__FILE__), - ] - ), - [ - 'id' => null, - 'inputUri' => __FILE__, - 'path' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - 'uri' => 'http://' . basename(__FILE__), - ], - ], - // BC with 5.2 (EZP-22808). Id is recognized as such if not pointing to existing file. - [ - new MediaValue( - [ - 'id' => 'application/asdf1234.pdf', - 'fileName' => 'asdf1234.pdf', - 'fileSize' => 12345, - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - 'uri' => 'http://asdf1234.pdf', - ] - ), - [ - 'id' => 'application/asdf1234.pdf', - 'inputUri' => null, - 'path' => null, - 'fileName' => 'asdf1234.pdf', - 'fileSize' => 12345, - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - 'uri' => 'http://asdf1234.pdf', - ], - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'id' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( - * array( - * 'id' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - null, - new MediaValue(), - ], - [ - [ - 'id' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - ], - new MediaValue( - [ - 'id' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - ] - ), - ], - // BC with 5.0 (EZP-20948). Path can be used as input instead of ID. - [ - [ - 'path' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - ], - new MediaValue( - [ - 'id' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - ] - ), - ], - // BC with 5.2 (EZP-22808). Id can be used as input instead of inputUri. - [ - [ - 'id' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - ], - new MediaValue( - [ - 'id' => null, - 'inputUri' => __FILE__, - 'path' => __FILE__, - 'fileName' => basename(__FILE__), - 'fileSize' => filesize(__FILE__), - 'mimeType' => 'text/plain', - 'hasController' => false, - 'autoplay' => false, - 'loop' => true, - 'width' => 0, - 'height' => 0, - ] - ), - ], - // @todo: Test for REST upload hash - ]; - } - - /** - * Provide data sets with field settings which are considered valid by the - * {@link validateFieldSettings()} method. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( 'rows' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidFieldSettings() - { - return [ - [ - [], - ], - [ - [ - 'mediaType' => MediaType::TYPE_FLASH, - ], - ], - [ - [ - 'mediaType' => MediaType::TYPE_REALPLAYER, - ], - ], - ]; - } - - /** - * Provide data sets with field settings which are considered invalid by the - * {@link validateFieldSettings()} method. The method must return a - * non-empty array of validation error when receiving such field settings. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * true, - * ), - * array( - * array( 'nonExistentKey' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInValidFieldSettings() - { - return [ - [ - [ - 'not-existing' => 23, - ], - ], - [ - // mediaType must be constant - [ - 'mediaType' => 23, - ], - ], - ]; - } - - protected function provideFieldTypeIdentifier() - { - return 'ezmedia'; - } - - public function provideDataForGetName(): array - { - return [ - [ - new MediaValue(), - '', - [], - 'en_GB', - ], - [ - new MediaValue(['fileName' => 'sindelfingen.jpg']), - 'sindelfingen.jpg', - [], - 'en_GB', - ], - ]; - } - - public function provideValidDataForValidate() - { - return [ - [ - [ - 'validatorConfiguration' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 1, - ], - ], - ], - new BinaryFileValue( - [ - 'id' => 'some/file/here', - 'fileName' => 'sindelfingen.mp4', - 'fileSize' => 15000, - 'downloadCount' => 0, - 'mimeType' => 'video/mp4', - ] - ), - ], - ]; - } - - public function provideInvalidDataForValidate() - { - return [ - // File is too large - [ - [ - 'validatorConfiguration' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 0.01, - ], - ], - ], - new MediaValue( - [ - 'id' => 'some/file/here', - 'fileName' => 'sindelfingen.mp4', - 'fileSize' => 150000, - 'mimeType' => 'video/mp4', - ] - ), - [ - new ValidationError( - 'The file size cannot exceed %size% megabyte.', - 'The file size cannot exceed %size% megabytes.', - [ - '%size%' => 0.01, - ], - 'fileSize' - ), - ], - ], - - // file extension is in blacklist - [ - [ - 'validatorConfiguration' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 1, - ], - ], - ], - new MediaValue( - [ - 'id' => 'phppng.php', - 'fileName' => 'phppng.php', - 'fileSize' => 0.01, - 'mimeType' => 'video/mp4', - ] - ), - [ - new ValidationError( - 'A valid file is required. The following file extensions are not allowed: %extensionsBlackList%', - null, - ['%extensionsBlackList%' => implode(', ', $this->blackListedExtensions)], - 'fileExtensionBlackList' - ), - ], - ], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/RelationListTest.php b/eZ/Publish/Core/FieldType/Tests/RelationListTest.php deleted file mode 100644 index ba47b4341e..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/RelationListTest.php +++ /dev/null @@ -1,902 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\RelationList\Type as RelationList; -use eZ\Publish\Core\FieldType\RelationList\Value; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\Handler as SPIContentHandler; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use Ibexa\Core\Repository\Validator\TargetContentValidatorInterface; - -class RelationListTest extends FieldTypeTest -{ - private const DESTINATION_CONTENT_ID_14 = 14; - private const DESTINATION_CONTENT_ID_22 = 22; - - /** @var \eZ\Publish\SPI\Persistence\Content\Handler */ - private $contentHandler; - - /** @var \Ibexa\Core\Repository\Validator\TargetContentValidatorInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $targetContentValidator; - - protected function setUp(): void - { - parent::setUp(); - - $this->targetContentValidator = $this->createMock(TargetContentValidatorInterface::class); - - $versionInfo14 = new VersionInfo([ - 'versionNo' => 1, - 'names' => [ - 'en_GB' => 'name_14_en_GB', - 'de_DE' => 'Name_14_de_DE', - ], - ]); - $versionInfo22 = new VersionInfo([ - 'versionNo' => 1, - 'names' => [ - 'en_GB' => 'name_22_en_GB', - 'de_DE' => 'Name_22_de_DE', - ], - ]); - $currentVersionNoFor14 = 44; - $destinationContentInfo14 = $this->createMock(ContentInfo::class); - $destinationContentInfo14 - ->method('__get') - ->willReturnMap([ - ['currentVersionNo', $currentVersionNoFor14], - ['mainLanguageCode', 'en_GB'], - ]); - $currentVersionNoFor22 = 22; - $destinationContentInfo22 = $this->createMock(ContentInfo::class); - $destinationContentInfo22 - ->method('__get') - ->willReturnMap([ - ['currentVersionNo', $currentVersionNoFor22], - ['mainLanguageCode', 'en_GB'], - ]); - - $this->contentHandler = $this->createMock(SPIContentHandler::class); - $this->contentHandler - ->method('loadContentInfo') - ->willReturnMap([ - [self::DESTINATION_CONTENT_ID_14, $destinationContentInfo14], - [self::DESTINATION_CONTENT_ID_22, $destinationContentInfo22], - ]); - - $this->contentHandler - ->method('loadVersionInfo') - ->willReturnMap([ - [self::DESTINATION_CONTENT_ID_14, $currentVersionNoFor14, $versionInfo14], - [self::DESTINATION_CONTENT_ID_22, $currentVersionNoFor22, $versionInfo22], - ]); - } - - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return \eZ\Publish\Core\FieldType\Relation\Type - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new RelationList( - $this->contentHandler, - $this->targetContentValidator - ); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return [ - 'RelationListValueValidator' => [ - 'selectionLimit' => [ - 'type' => 'int', - 'default' => 0, - ], - ], - ]; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return [ - 'selectionMethod' => [ - 'type' => 'int', - 'default' => RelationList::SELECTION_BROWSE, - ], - 'selectionDefaultLocation' => [ - 'type' => 'string', - 'default' => null, - ], - 'selectionContentTypes' => [ - 'type' => 'array', - 'default' => [], - ], - ]; - } - - /** - * Returns the empty value expected from the field type. - * - * @return \eZ\Publish\Core\FieldType\RelationList\Value - */ - protected function getEmptyValueExpectation() - { - // @todo FIXME: Is this correct? - return new Value(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - true, - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - new Value(), - new Value(), - ], - [ - 23, - new Value([23]), - ], - [ - new ContentInfo(['id' => 23]), - new Value([23]), - ], - [ - [23, 42], - new Value([23, 42]), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new Value([23, 42]), - ['destinationContentIds' => [23, 42]], - ], - [ - new Value(), - ['destinationContentIds' => []], - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - ['destinationContentIds' => [23, 42]], - new Value([23, 42]), - ], - [ - ['destinationContentIds' => []], - new Value(), - ], - ]; - } - - /** - * Provide data sets with field settings which are considered valid by the - * {@link validateFieldSettings()} method. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( 'rows' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidFieldSettings() - { - return [ - [ - [ - 'selectionMethod' => RelationList::SELECTION_BROWSE, - 'selectionDefaultLocation' => 23, - ], - ], - [ - [ - 'selectionMethod' => RelationList::SELECTION_DROPDOWN, - 'selectionDefaultLocation' => 'foo', - ], - ], - [ - [ - 'selectionMethod' => RelationList::SELECTION_DROPDOWN, - 'selectionDefaultLocation' => 'foo', - 'selectionContentTypes' => [1, 2, 3], - ], - ], - [ - [ - 'selectionMethod' => RelationList::SELECTION_LIST_WITH_RADIO_BUTTONS, - 'selectionDefaultLocation' => 'foo', - 'selectionContentTypes' => [1, 2, 3], - ], - ], - [ - [ - 'selectionMethod' => RelationList::SELECTION_LIST_WITH_CHECKBOXES, - 'selectionDefaultLocation' => 'foo', - 'selectionContentTypes' => [1, 2, 3], - ], - ], - [ - [ - 'selectionMethod' => RelationList::SELECTION_MULTIPLE_SELECTION_LIST, - 'selectionDefaultLocation' => 'foo', - 'selectionContentTypes' => [1, 2, 3], - ], - ], - [ - [ - 'selectionMethod' => RelationList::SELECTION_TEMPLATE_BASED_MULTIPLE, - 'selectionDefaultLocation' => 'foo', - 'selectionContentTypes' => [1, 2, 3], - ], - ], - [ - [ - 'selectionMethod' => RelationList::SELECTION_TEMPLATE_BASED_SINGLE, - 'selectionDefaultLocation' => 'foo', - 'selectionContentTypes' => [1, 2, 3], - ], - ], - ]; - } - - /** - * Provide data sets with field settings which are considered invalid by the - * {@link validateFieldSettings()} method. The method must return a - * non-empty array of validation error when receiving such field settings. - * - * ATTENTION: This is a default implementation, which must be overwritten - * if a FieldType supports field settings! - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * true, - * ), - * array( - * array( 'nonExistentKey' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInValidFieldSettings() - { - return [ - [ - // Invalid value for 'selectionMethod' - [ - 'selectionMethod' => true, - 'selectionDefaultLocation' => 23, - ], - ], - [ - // Invalid value for 'selectionDefaultLocation' - [ - 'selectionMethod' => RelationList::SELECTION_DROPDOWN, - 'selectionDefaultLocation' => [], - ], - ], - [ - // Invalid value for 'selectionContentTypes' - [ - 'selectionMethod' => RelationList::SELECTION_DROPDOWN, - 'selectionDefaultLocation' => 23, - 'selectionContentTypes' => true, - ], - ], - [ - // Invalid value for 'selectionMethod' - [ - 'selectionMethod' => 9, - 'selectionDefaultLocation' => 23, - 'selectionContentTypes' => true, - ], - ], - ]; - } - - /** - * Provide data sets with validator configurations which are considered - * valid by the {@link validateValidatorConfiguration()} method. - * - * Returns an array of data provider sets with a single argument: A valid - * set of validator configurations. - * - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( - * 'IntegerValueValidator' => array( - * 'minIntegerValue' => 0, - * 'maxIntegerValue' => 23, - * ) - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidValidatorConfiguration() - { - return [ - [ - [], - ], - [ - [ - 'RelationListValueValidator' => [ - 'selectionLimit' => 0, - ], - ], - ], - [ - [ - 'RelationListValueValidator' => [ - 'selectionLimit' => 14, - ], - ], - ], - ]; - } - - /** - * Provide data sets with validator configurations which are considered - * invalid by the {@link validateValidatorConfiguration()} method. The - * method must return a non-empty array of validation errors when receiving - * one of the provided values. - * - * Returns an array of data provider sets with a single argument: A valid - * set of validator configurations. - * - * For example: - * - * <code> - * return array( - * array( - * array( - * 'NonExistentValidator' => array(), - * ), - * ), - * array( - * array( - * // Typos - * 'InTEgervALUeVALIdator' => array( - * 'iinIntegerValue' => 0, - * 'maxIntegerValue' => 23, - * ) - * ) - * ), - * array( - * array( - * 'IntegerValueValidator' => array( - * // Incorrect value types - * 'minIntegerValue' => true, - * 'maxIntegerValue' => false, - * ) - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidValidatorConfiguration() - { - return [ - [ - [ - 'NonExistentValidator' => [], - ], - ], - [ - [ - 'RelationListValueValidator' => [ - 'nonExistentValue' => 14, - ], - ], - ], - [ - [ - 'RelationListValueValidator' => [ - 'selectionLimit' => 'foo', - ], - ], - ], - [ - [ - 'RelationListValueValidator' => [ - 'selectionLimit' => -10, - ], - ], - ], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings and - * field value which are considered valid by the {@link validate()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten if - * a FieldType supports validation! - * - * For example: - * - * <code> - * return array( - * array( - * array( - * "validatorConfiguration" => array( - * "StringLengthValidator" => array( - * "minStringLength" => 2, - * "maxStringLength" => 10, - * ), - * ), - * ), - * new TextLineValue( "lalalala" ), - * ), - * array( - * array( - * "fieldSettings" => array( - * 'isMultiple' => true - * ), - * ), - * new CountryValue( - * array( - * "BE" => array( - * "Name" => "Belgium", - * "Alpha2" => "BE", - * "Alpha3" => "BEL", - * "IDC" => 32, - * ), - * ), - * ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidDataForValidate() - { - return [ - [ - [ - 'validatorConfiguration' => [ - 'RelationListValueValidator' => [ - 'selectionLimit' => 0, - ], - ], - ], - new Value([5, 6, 7]), - ], - [ - [ - 'validatorConfiguration' => [ - 'RelationListValueValidator' => [ - 'selectionLimit' => 1, - ], - ], - ], - new Value([5]), - ], - [ - [ - 'validatorConfiguration' => [ - 'RelationListValueValidator' => [ - 'selectionLimit' => 3, - ], - ], - ], - new Value([5, 6]), - ], - [ - [ - 'validatorConfiguration' => [ - 'RelationListValueValidator' => [ - 'selectionLimit' => 3, - ], - ], - ], - new Value([]), - ], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings, - * field value and corresponding validation errors returned by - * the {@link validate()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten - * if a FieldType supports validation! - * - * For example: - * - * <code> - * return array( - * array( - * array( - * "validatorConfiguration" => array( - * "IntegerValueValidator" => array( - * "minIntegerValue" => 5, - * "maxIntegerValue" => 10 - * ), - * ), - * ), - * new IntegerValue( 3 ), - * array( - * new ValidationError( - * "The value can not be lower than %size%.", - * null, - * array( - * "%size%" => 5 - * ), - * ), - * ), - * ), - * array( - * array( - * "fieldSettings" => array( - * "isMultiple" => false - * ), - * ), - * new CountryValue( - * "BE" => array( - * "Name" => "Belgium", - * "Alpha2" => "BE", - * "Alpha3" => "BEL", - * "IDC" => 32, - * ), - * "FR" => array( - * "Name" => "France", - * "Alpha2" => "FR", - * "Alpha3" => "FRA", - * "IDC" => 33, - * ), - * ) - * ), - * array( - * new ValidationError( - * "Field definition does not allow multiple countries to be selected." - * ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidDataForValidate() - { - return [ - [ - [ - 'validatorConfiguration' => [ - 'RelationListValueValidator' => [ - 'selectionLimit' => 3, - ], - ], - ], - new Value([1, 2, 3, 4]), - [ - new ValidationError( - 'The selected content items number cannot be higher than %limit%.', - null, - [ - '%limit%' => 3, - ], - 'destinationContentIds' - ), - ], - ], - ]; - } - - public function testValidateNotExistingContentRelations(): void - { - $invalidDestinationContentId = (int) 'invalid'; - $invalidDestinationContentId2 = (int) 'invalid-second'; - - $this->targetContentValidator - ->expects(self::exactly(2)) - ->method('validate') - ->withConsecutive([$invalidDestinationContentId], [$invalidDestinationContentId2]) - ->willReturnOnConsecutiveCalls( - $this->generateValidationError($invalidDestinationContentId), - $this->generateValidationError($invalidDestinationContentId2) - ); - - $validationErrors = $this->doValidate([], new Value([$invalidDestinationContentId, $invalidDestinationContentId2])); - - self::assertIsArray($validationErrors); - self::assertCount(2, $validationErrors); - } - - public function testValidateInvalidContentType(): void - { - $destinationContentId = 12; - $destinationContentId2 = 13; - $allowedContentTypes = ['article', 'folder']; - - $this->targetContentValidator - ->method('validate') - ->withConsecutive( - [$destinationContentId, $allowedContentTypes], - [$destinationContentId2, $allowedContentTypes] - ) - ->willReturnOnConsecutiveCalls( - $this->generateContentTypeValidationError('test'), - $this->generateContentTypeValidationError('test') - ); - - $validationErrors = $this->doValidate( - ['fieldSettings' => ['selectionContentTypes' => $allowedContentTypes]], - new Value([$destinationContentId, $destinationContentId2]) - ); - - self::assertIsArray($validationErrors); - self::assertCount(2, $validationErrors); - } - - private function generateValidationError(string $contentId): ValidationError - { - return new ValidationError( - 'Content with identifier %contentId% is not a valid relation target', - null, - [ - '%contentId%' => $contentId, - ], - 'targetContentId' - ); - } - - private function generateContentTypeValidationError(string $contentTypeIdentifier): ValidationError - { - return new ValidationError( - 'Content Type %contentTypeIdentifier% is not a valid relation target', - null, - [ - '%contentTypeIdentifier%' => $contentTypeIdentifier, - ], - 'targetContentId' - ); - } - - /** - * @covers \eZ\Publish\Core\FieldType\Relation\Type::getRelations - */ - public function testGetRelations() - { - $ft = $this->createFieldTypeUnderTest(); - $this->assertEquals( - [ - Relation::FIELD => [70, 72], - ], - $ft->getRelations($ft->acceptValue([70, 72])) - ); - } - - protected function provideFieldTypeIdentifier() - { - return 'ezobjectrelationlist'; - } - - /** - * @dataProvider provideDataForGetName - */ - public function testGetName( - SPIValue $value, - string $expected, - array $fieldSettings = [], - string $languageCode = 'en_GB' - ): void { - $fieldDefinitionMock = $this->getFieldDefinitionMock($fieldSettings); - - $name = $this->getFieldTypeUnderTest()->getName($value, $fieldDefinitionMock, $languageCode); - - self::assertSame($expected, $name); - } - - public function provideDataForGetName(): array - { - return [ - [$this->getEmptyValueExpectation(), '', [], 'en_GB'], - [new Value([self::DESTINATION_CONTENT_ID_14, self::DESTINATION_CONTENT_ID_22]), 'name_14_en_GB name_22_en_GB', [], 'en_GB'], - [new Value([self::DESTINATION_CONTENT_ID_14, self::DESTINATION_CONTENT_ID_22]), 'Name_14_de_DE Name_22_de_DE', [], 'de_DE'], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/RelationTest.php b/eZ/Publish/Core/FieldType/Tests/RelationTest.php deleted file mode 100644 index 8822039120..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/RelationTest.php +++ /dev/null @@ -1,523 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Relation\Type as RelationType; -use eZ\Publish\Core\FieldType\Relation\Value; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\Handler as SPIContentHandler; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use Ibexa\Core\Repository\Validator\TargetContentValidatorInterface; - -class RelationTest extends FieldTypeTest -{ - private const DESTINATION_CONTENT_ID = 14; - - private $contentHandler; - - /** @var \Ibexa\Core\Repository\Validator\TargetContentValidatorInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $targetContentValidator; - - protected function setUp(): void - { - parent::setUp(); - - $versionInfo = new VersionInfo([ - 'versionNo' => 24, - 'names' => [ - 'en_GB' => 'name_en_GB', - 'de_DE' => 'Name_de_DE', - ], - ]); - $currentVersionNo = 28; - $destinationContentInfo = $this->createMock(ContentInfo::class); - $destinationContentInfo - ->method('__get') - ->willReturnMap([ - ['currentVersionNo', $currentVersionNo], - ['mainLanguageCode', 'en_GB'], - ]); - - $this->contentHandler = $this->createMock(SPIContentHandler::class); - $this->contentHandler - ->method('loadContentInfo') - ->with(self::DESTINATION_CONTENT_ID) - ->willReturn($destinationContentInfo); - - $this->contentHandler - ->method('loadVersionInfo') - ->with(self::DESTINATION_CONTENT_ID, $currentVersionNo) - ->willReturn($versionInfo); - - $this->targetContentValidator = $this->createMock(TargetContentValidatorInterface::class); - } - - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return \eZ\Publish\Core\FieldType\Relation\Type - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new RelationType( - $this->contentHandler, - $this->targetContentValidator - ); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return []; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return [ - 'selectionMethod' => [ - 'type' => 'int', - 'default' => RelationType::SELECTION_BROWSE, - ], - 'selectionRoot' => [ - 'type' => 'string', - 'default' => null, - ], - 'selectionContentTypes' => [ - 'type' => 'array', - 'default' => [], - ], - ]; - } - - /** - * Returns the empty value expected from the field type. - * - * @return \eZ\Publish\Core\FieldType\Relation\Value - */ - protected function getEmptyValueExpectation() - { - return new Value(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - true, - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - new Value(), - new Value(), - ], - [ - 23, - new Value(23), - ], - [ - new ContentInfo(['id' => 23]), - new Value(23), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new Value(23), - ['destinationContentId' => 23], - ], - [ - new Value(), - ['destinationContentId' => null], - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - ['destinationContentId' => 23], - new Value(23), - ], - [ - ['destinationContentId' => null], - new Value(), - ], - ]; - } - - /** - * Provide data sets with field settings which are considered valid by the - * {@link validateFieldSettings()} method. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( 'rows' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidFieldSettings() - { - return [ - [ - [ - 'selectionMethod' => RelationType::SELECTION_BROWSE, - 'selectionRoot' => 42, - ], - ], - [ - [ - 'selectionMethod' => RelationType::SELECTION_DROPDOWN, - 'selectionRoot' => 'some-key', - ], - ], - ]; - } - - /** - * Provide data sets with field settings which are considered invalid by the - * {@link validateFieldSettings()} method. The method must return a - * non-empty array of validation error when receiving such field settings. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * true, - * ), - * array( - * array( 'nonExistentKey' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInValidFieldSettings() - { - return [ - [ - // Unknown key - [ - 'unknownKey' => 23, - 'selectionMethod' => RelationType::SELECTION_BROWSE, - 'selectionRoot' => 42, - ], - ], - [ - // Invalid selectionMethod - [ - 'selectionMethod' => 2342, - 'selectionRoot' => 42, - ], - ], - [ - // Invalid selectionRoot - [ - 'selectionMethod' => RelationType::SELECTION_DROPDOWN, - 'selectionRoot' => [], - ], - ], - ]; - } - - /** - * @covers \eZ\Publish\Core\FieldType\Relation\Type::getRelations - */ - public function testGetRelations() - { - $ft = $this->createFieldTypeUnderTest(); - $this->assertEquals( - [ - Relation::FIELD => [70], - ], - $ft->getRelations($ft->acceptValue(70)) - ); - } - - public function testValidateNotExistingContentRelation(): void - { - $destinationContentId = 'invalid'; - - $this->targetContentValidator - ->expects(self::once()) - ->method('validate') - ->with((int) $destinationContentId) - ->willReturn($this->generateValidationError($destinationContentId)); - - $validationErrors = $this->doValidate([], new Value($destinationContentId)); - - self::assertIsArray($validationErrors); - self::assertEquals([$this->generateValidationError($destinationContentId)], $validationErrors); - } - - public function testValidateInvalidContentType(): void - { - $destinationContentId = 12; - $allowedContentTypes = ['article', 'folder']; - - $this->targetContentValidator - ->expects(self::once()) - ->method('validate') - ->with($destinationContentId, $allowedContentTypes) - ->willReturn($this->generateContentTypeValidationError('test')); - - $validationErrors = $this->doValidate( - ['fieldSettings' => ['selectionContentTypes' => $allowedContentTypes]], - new Value($destinationContentId) - ); - - self::assertIsArray($validationErrors); - self::assertEquals([$this->generateContentTypeValidationError('test')], $validationErrors); - } - - private function generateValidationError(string $contentId): ValidationError - { - return new ValidationError( - 'Content with identifier %contentId% is not a valid relation target', - null, - [ - '%contentId%' => $contentId, - ], - 'targetContentId' - ); - } - - private function generateContentTypeValidationError(string $contentTypeIdentifier): ValidationError - { - return new ValidationError( - 'Content Type %contentTypeIdentifier% is not a valid relation target', - null, - [ - '%contentTypeIdentifier%' => $contentTypeIdentifier, - ], - 'targetContentId' - ); - } - - protected function provideFieldTypeIdentifier() - { - return 'ezobjectrelation'; - } - - /** - * @dataProvider provideDataForGetName - */ - public function testGetName( - SPIValue $value, - string $expected, - array $fieldSettings = [], - string $languageCode = 'en_GB' - ): void { - /** @var \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition|\PHPUnit\Framework\MockObject\MockObject $fieldDefinitionMock */ - $fieldDefinitionMock = $this->createMock(FieldDefinition::class); - $fieldDefinitionMock->method('getFieldSettings')->willReturn($fieldSettings); - - $name = $this->getFieldTypeUnderTest()->getName($value, $fieldDefinitionMock, $languageCode); - - self::assertSame($expected, $name); - } - - public function provideDataForGetName(): array - { - return [ - 'empty_destination_content_id' => [ - $this->getEmptyValueExpectation(), '', [], 'en_GB', - ], - 'destination_content_id' => [ - new Value(self::DESTINATION_CONTENT_ID), 'name_en_GB', [], 'en_GB', - ], - 'destination_content_id_de_DE' => [ - new Value(self::DESTINATION_CONTENT_ID), 'Name_de_DE', [], 'de_DE', - ], - ]; - } - - public function provideValidDataForValidate(): array - { - return [ - [[], new Value(5)], - ]; - } - - public function provideInvalidDataForValidate(): array - { - return [ - [[], new Value('invalid'), []], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/SelectionTest.php b/eZ/Publish/Core/FieldType/Tests/SelectionTest.php deleted file mode 100644 index d5eb309e71..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/SelectionTest.php +++ /dev/null @@ -1,644 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Selection\Type as Selection; -use eZ\Publish\Core\FieldType\Selection\Value as SelectionValue; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -/** - * @group fieldType - * @group ezselection - */ -class SelectionTest extends FieldTypeTest -{ - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new Selection(); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return []; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return [ - 'isMultiple' => [ - 'type' => 'bool', - 'default' => false, - ], - 'options' => [ - 'type' => 'hash', - 'default' => [], - ], - 'multilingualOptions' => [ - 'type' => 'hash', - 'default' => [], - ], - ]; - } - - /** - * Returns the empty value expected from the field type. - * - * @return \eZ\Publish\Core\FieldType\Selection\Value - */ - protected function getEmptyValueExpectation() - { - return new SelectionValue(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - 23, - InvalidArgumentException::class, - ], - [ - 'sindelfingen', - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - [], - new SelectionValue(), - ], - [ - [23], - new SelectionValue([23]), - ], - [ - [23, 42], - new SelectionValue([23, 42]), - ], - [ - new SelectionValue([23, 42]), - new SelectionValue([23, 42]), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new SelectionValue(), - [], - ], - [ - new SelectionValue([23, 42]), - [23, 42], - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - [], - new SelectionValue(), - ], - [ - [23, 42], - new SelectionValue([23, 42]), - ], - ]; - } - - /** - * Provide data sets with field settings which are considered valid by the - * {@link validateFieldSettings()} method. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( 'rows' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidFieldSettings() - { - return [ - [ - [], - ], - [ - [ - 'isMultiple' => true, - 'options' => ['foo', 'bar'], - ], - ], - [ - [ - 'isMultiple' => false, - 'options' => [23, 42], - ], - ], - ]; - } - - /** - * Provide data sets with field settings which are considered invalid by the - * {@link validateFieldSettings()} method. The method must return a - * non-empty array of validation error when receiving such field settings. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * true, - * ), - * array( - * array( 'nonExistentKey' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInValidFieldSettings() - { - return [ - [ - [ - // isMultiple must be bool - 'isMultiple' => 23, - ], - ], - [ - [ - // options must be array - 'options' => 23, - ], - ], - ]; - } - - protected function provideFieldTypeIdentifier() - { - return 'ezselection'; - } - - /** - * @dataProvider provideDataForGetName - */ - public function testGetName( - SPIValue $value, - string $expected, - array $fieldSettings = [], - string $languageCode = 'en_GB' - ): void { - $fieldDefinitionMock = $this->getFieldDefinitionMock($fieldSettings); - $fieldDefinitionMock - ->method('__get') - ->with('mainLanguageCode') - ->willReturn('de_DE'); - - $name = $this->getFieldTypeUnderTest()->getName($value, $fieldDefinitionMock, $languageCode); - - self::assertSame($expected, $name); - } - - public function provideDataForGetName(): array - { - return [ - 'empty_value_and_field_settings' => [$this->getEmptyValueExpectation(), '', [], 'en_GB'], - 'one_option' => [ - new SelectionValue(['optionIndex1']), - 'option_1', - ['options' => ['optionIndex1' => 'option_1']], - 'en_GB', - ], - 'two_options' => [ - new SelectionValue(['optionIndex1', 'optionIndex2']), - 'option_1 option_2', - ['options' => ['optionIndex1' => 'option_1', 'optionIndex2' => 'option_2']], - 'en_GB', - ], - 'multilingual_options' => [ - new SelectionValue(['optionIndex1', 'optionIndex2']), - 'option_1 option_2', - ['multilingualOptions' => ['en_GB' => ['optionIndex1' => 'option_1', 'optionIndex2' => 'option_2']]], - 'en_GB', - ], - 'multilingual_options_with_main_language_code' => [ - new SelectionValue(['optionIndex3', 'optionIndex4']), - 'option_3 option_4', - ['multilingualOptions' => [ - 'en_GB' => ['optionIndex1' => 'option_1', 'optionIndex2' => 'option_2'], - 'de_DE' => ['optionIndex3' => 'option_3', 'optionIndex4' => 'option_4'], - ]], - 'de_DE', - ], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings and - * field value which are considered valid by the {@link validate()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten if - * a FieldType supports validation! - * - * For example: - * - * <code> - * return array( - * array( - * array( - * "validatorConfiguration" => array( - * "StringLengthValidator" => array( - * "minStringLength" => 2, - * "maxStringLength" => 10, - * ), - * ), - * ), - * new TextLineValue( "lalalala" ), - * ), - * array( - * array( - * "fieldSettings" => array( - * 'isMultiple' => true - * ), - * ), - * new CountryValue( - * array( - * "BE" => array( - * "Name" => "Belgium", - * "Alpha2" => "BE", - * "Alpha3" => "BEL", - * "IDC" => 32, - * ), - * ), - * ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidDataForValidate() - { - return [ - [ - [ - 'fieldSettings' => [ - 'isMultiple' => true, - 'options' => [0 => 1, 1 => 2], - ], - ], - new SelectionValue([0, 1]), - ], - [ - [ - 'fieldSettings' => [ - 'isMultiple' => false, - 'options' => [0 => 1, 1 => 2], - ], - ], - new SelectionValue([1]), - ], - [ - [ - 'fieldSettings' => [ - 'isMultiple' => false, - 'options' => [0 => 1, 1 => 2], - ], - ], - new SelectionValue(), - ], - [ - [ - 'fieldSettings' => [ - 'isMultiple' => false, - 'options' => [0 => 1, 1 => 2], - 'multilingualOptions' => [ - 'en_GB' => [0 => 1, 1 => 2], - 'de_DE' => [0 => 1, 1 => 2], - ], - ], - ], - new SelectionValue([1]), - ], - [ - [ - 'fieldSettings' => [ - 'isMultiple' => false, - 'options' => [0 => 1, 1 => 2], - 'multilingualOptions' => [ - 'en_GB' => [0 => 1, 1 => 2], - 'de_DE' => [0 => 1], - ], - ], - ], - new SelectionValue([1]), - ], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings, - * field value and corresponding validation errors returned by - * the {@link validate()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten - * if a FieldType supports validation! - * - * For example: - * - * <code> - * return array( - * array( - * array( - * "validatorConfiguration" => array( - * "IntegerValueValidator" => array( - * "minIntegerValue" => 5, - * "maxIntegerValue" => 10 - * ), - * ), - * ), - * new IntegerValue( 3 ), - * array( - * new ValidationError( - * "The value can not be lower than %size%.", - * null, - * array( - * "size" => 5 - * ), - * ), - * ), - * ), - * array( - * array( - * "fieldSettings" => array( - * "isMultiple" => false - * ), - * ), - * new CountryValue( - * "BE" => array( - * "Name" => "Belgium", - * "Alpha2" => "BE", - * "Alpha3" => "BEL", - * "IDC" => 32, - * ), - * "FR" => array( - * "Name" => "France", - * "Alpha2" => "FR", - * "Alpha3" => "FRA", - * "IDC" => 33, - * ), - * ) - * ), - * array( - * new ValidationError( - * "Field definition does not allow multiple countries to be selected." - * ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidDataForValidate() - { - return [ - [ - [ - 'fieldSettings' => [ - 'isMultiple' => false, - 'options' => [0 => 1, 1 => 2], - ], - ], - new SelectionValue([0, 1]), - [ - new ValidationError( - 'Field definition does not allow multiple options to be selected.', - null, - [], - 'selection' - ), - ], - ], - [ - [ - 'fieldSettings' => [ - 'isMultiple' => false, - 'options' => [0 => 1, 1 => 2], - ], - ], - new SelectionValue([3]), - [ - new ValidationError( - 'Option with index %index% does not exist in the field definition.', - null, - [ - '%index%' => 3, - ], - 'selection' - ), - ], - ], - [ - [ - 'fieldSettings' => [ - 'isMultiple' => false, - 'options' => [0 => 1, 1 => 2], - 'multilingualOptions' => [ - 'en_GB' => [0 => 1, 1 => 2], - 'de_DE' => [0 => 1], - ], - ], - ], - new SelectionValue([3]), - [ - new ValidationError( - 'Option with index %index% does not exist in the field definition.', - null, - [ - '%index%' => 3, - ], - 'selection' - ), - ], - ], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/TextBlockTest.php b/eZ/Publish/Core/FieldType/Tests/TextBlockTest.php deleted file mode 100644 index 96b1f1ef6a..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/TextBlockTest.php +++ /dev/null @@ -1,355 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\TextBlock\Type as TextBlockType; -use eZ\Publish\Core\FieldType\TextBlock\Value as TextBlockValue; - -/** - * @group fieldType - * @group ezselection - */ -class TextBlockTest extends FieldTypeTest -{ - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return \eZ\Publish\SPI\FieldType\FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new TextBlockType(); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return []; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return [ - 'textRows' => [ - 'type' => 'int', - 'default' => 10, - ], - ]; - } - - /** - * Returns the empty value expected from the field type. - * - * @return \eZ\Publish\Core\FieldType\TextLine\Value - */ - protected function getEmptyValueExpectation() - { - return new TextBlockValue(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - 23, - InvalidArgumentException::class, - ], - [ - new TextBlockValue(23), - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - null, - new TextBlockValue(), - ], - [ - '', - new TextBlockValue(), - ], - [ - 'sindelfingen', - new TextBlockValue('sindelfingen'), - ], - [ - new TextBlockValue('sindelfingen'), - new TextBlockValue('sindelfingen'), - ], - [ - new TextBlockValue(''), - new TextBlockValue(), - ], - [ - new TextBlockValue(null), - new TextBlockValue(), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new TextBlockValue(), - null, - ], - [ - new TextBlockValue('sindelfingen'), - 'sindelfingen', - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - '', - new TextBlockValue(), - ], - [ - 'sindelfingen', - new TextBlockValue('sindelfingen'), - ], - ]; - } - - /** - * Provide data sets with field settings which are considered valid by the - * {@link validateFieldSettings()} method. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( 'rows' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidFieldSettings() - { - return [ - [ - [], - ], - [ - [ - 'textRows' => 23, - ], - ], - ]; - } - - /** - * Provide data sets with field settings which are considered invalid by the - * {@link validateFieldSettings()} method. The method must return a - * non-empty array of validation error when receiving such field settings. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * true, - * ), - * array( - * array( 'nonExistentKey' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInValidFieldSettings() - { - return [ - [ - [ - 'non-existent' => 'foo', - ], - ], - [ - [ - // textRows must be integer - 'textRows' => 'foo', - ], - ], - ]; - } - - protected function provideFieldTypeIdentifier() - { - return 'eztext'; - } - - public function provideDataForGetName(): array - { - return [ - [$this->getEmptyValueExpectation(), '', [], 'en_GB'], - [new TextBlockValue('This is a piece of text'), 'This is a piece of text', [], 'en_GB'], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/TextLineTest.php b/eZ/Publish/Core/FieldType/Tests/TextLineTest.php deleted file mode 100644 index f0b41f6388..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/TextLineTest.php +++ /dev/null @@ -1,720 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\TextLine\Type as TextLineType; -use eZ\Publish\Core\FieldType\TextLine\Value as TextLineValue; -use eZ\Publish\Core\FieldType\ValidationError; - -/** - * @group fieldType - * @group ezstring - */ -class TextLineTest extends FieldTypeTest -{ - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return \eZ\Publish\Core\FieldType\FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new TextLineType(); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return [ - 'StringLengthValidator' => [ - 'minStringLength' => [ - 'type' => 'int', - 'default' => 0, - ], - 'maxStringLength' => [ - 'type' => 'int', - 'default' => null, - ], - ], - ]; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return []; - } - - /** - * Returns the empty value expected from the field type. - * - * @return \eZ\Publish\Core\FieldType\TextLine\Value - */ - protected function getEmptyValueExpectation() - { - return new TextLineValue(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - 23, - InvalidArgumentException::class, - ], - [ - new TextLineValue(23), - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - null, - new TextLineValue(), - ], - [ - '', - new TextLineValue(), - ], - [ - ' ', - new TextLineValue(), - ], - [ - ' sindelfingen ', - new TextLineValue(' sindelfingen '), - ], - [ - new TextLineValue(' sindelfingen '), - new TextLineValue(' sindelfingen '), - ], - [ - // 11+ numbers - EZP-21771 - '12345678901', - new TextLineValue('12345678901'), - ], - [ - new TextLineValue(''), - new TextLineValue(), - ], - [ - new TextLineValue(' '), - new TextLineValue(), - ], - [ - new TextLineValue(null), - new TextLineValue(), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new TextLineValue(), - null, - ], - [ - new TextLineValue(''), - null, - ], - [ - new TextLineValue('sindelfingen'), - 'sindelfingen', - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - null, - new TextLineValue(), - ], - [ - '', - new TextLineValue(), - ], - [ - 'sindelfingen', - new TextLineValue('sindelfingen'), - ], - ]; - } - - /** - * Provide data sets with validator configurations which are considered - * valid by the {@link validateValidatorConfiguration()} method. - * - * Returns an array of data provider sets with a single argument: A valid - * set of validator configurations. - * - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( - * 'StringLengthValidator' => array( - * 'minStringLength' => 0, - * 'maxStringLength' => 23, - * ) - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidValidatorConfiguration() - { - return [ - [ - [], - ], - [ - [ - 'StringLengthValidator' => [ - 'minStringLength' => null, - ], - ], - ], - [ - [ - 'StringLengthValidator' => [ - 'minStringLength' => 23, - ], - ], - ], - [ - [ - 'StringLengthValidator' => [ - 'maxStringLength' => null, - ], - ], - ], - [ - [ - 'StringLengthValidator' => [ - 'maxStringLength' => 23, - ], - ], - ], - [ - [ - 'StringLengthValidator' => [ - 'minStringLength' => 23, - 'maxStringLength' => 42, - ], - ], - ], - ]; - } - - /** - * Provide data sets with validator configurations which are considered - * invalid by the {@link validateValidatorConfiguration()} method. The - * method must return a non-empty array of valiation errors when receiving - * one of the provided values. - * - * Returns an array of data provider sets with a single argument: A valid - * set of validator configurations. - * - * For example: - * - * <code> - * return array( - * array( - * array( - * 'NonExistentValidator' => array(), - * ), - * ), - * array( - * array( - * // Typos - * 'InTEgervALUeVALIdator' => array( - * 'iinStringLength' => 0, - * 'maxStringLength' => 23, - * ) - * ) - * ), - * array( - * array( - * 'StringLengthValidator' => array( - * // Incorrect value types - * 'minStringLength' => true, - * 'maxStringLength' => false, - * ) - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidValidatorConfiguration() - { - return [ - [ - [ - 'NonExistentValidator' => [], - ], - ], - [ - [ - 'StringLengthValidator' => [ - 'nonExistentValue' => 23, - ], - ], - ], - [ - [ - 'StringLengthValidator' => [ - 'minStringLength' => .23, - ], - ], - ], - [ - [ - 'StringLengthValidator' => [ - 'maxStringLength' => .42, - ], - ], - ], - [ - [ - 'StringLengthValidator' => [ - 'minStringLength' => -23, - ], - ], - ], - [ - [ - 'StringLengthValidator' => [ - 'maxStringLength' => -42, - ], - ], - ], - [ - [ - 'StringLengthValidator' => [ - 'maxStringLength' => 23, - 'minStringLength' => 42, - ], - ], - ], - ]; - } - - protected function provideFieldTypeIdentifier() - { - return 'ezstring'; - } - - public function provideDataForGetName(): array - { - return [ - [$this->getEmptyValueExpectation(), '', [], 'en_GB'], - [new TextLineValue('This is a line of text'), 'This is a line of text', [], 'en_GB'], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings and - * field value which are considered valid by the {@link validate()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten if - * a FieldType supports validation! - * - * For example: - * - * <code> - * return array( - * array( - * array( - * "validatorConfiguration" => array( - * "StringLengthValidator" => array( - * "minStringLength" => 2, - * "maxStringLength" => 10, - * ), - * ), - * ), - * new TextLineValue( "lalalala" ), - * ), - * array( - * array( - * "fieldSettings" => array( - * 'isMultiple' => true - * ), - * ), - * new CountryValue( - * array( - * "BE" => array( - * "Name" => "Belgium", - * "Alpha2" => "BE", - * "Alpha3" => "BEL", - * "IDC" => 32, - * ), - * ), - * ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidDataForValidate() - { - return [ - [ - [ - 'validatorConfiguration' => [ - 'StringLengthValidator' => [ - 'minStringLength' => 2, - 'maxStringLength' => 10, - ], - ], - ], - new TextLineValue('lalalala'), - ], - [ - [ - 'validatorConfiguration' => [ - 'StringLengthValidator' => [ - 'maxStringLength' => 10, - ], - ], - ], - new TextLineValue('lililili'), - ], - [ - [ - 'validatorConfiguration' => [ - 'StringLengthValidator' => [ - 'maxStringLength' => 10, - ], - ], - ], - new TextLineValue('♔♕♖♗♘♙♚♛♜♝'), - ], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings, - * field value and corresponding validation errors returned by - * the {@link validate()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten - * if a FieldType supports validation! - * - * For example: - * - * <code> - * return array( - * array( - * array( - * "validatorConfiguration" => array( - * "IntegerValueValidator" => array( - * "minIntegerValue" => 5, - * "maxIntegerValue" => 10 - * ), - * ), - * ), - * new IntegerValue( 3 ), - * array( - * new ValidationError( - * "The value can not be lower than %size%.", - * null, - * array( - * "size" => 5 - * ), - * ), - * ), - * ), - * array( - * array( - * "fieldSettings" => array( - * "isMultiple" => false - * ), - * ), - * new CountryValue( - * "BE" => array( - * "Name" => "Belgium", - * "Alpha2" => "BE", - * "Alpha3" => "BEL", - * "IDC" => 32, - * ), - * "FR" => array( - * "Name" => "France", - * "Alpha2" => "FR", - * "Alpha3" => "FRA", - * "IDC" => 33, - * ), - * ) - * ), - * array( - * new ValidationError( - * "Field definition does not allow multiple countries to be selected." - * ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidDataForValidate() - { - return [ - [ - [ - 'validatorConfiguration' => [ - 'StringLengthValidator' => [ - 'minStringLength' => 5, - 'maxStringLength' => 10, - ], - ], - ], - new TextLineValue('aaa'), - [ - new ValidationError( - 'The string cannot be shorter than %size% character.', - 'The string cannot be shorter than %size% characters.', - [ - '%size%' => 5, - ], - 'text' - ), - ], - ], - [ - [ - 'validatorConfiguration' => [ - 'StringLengthValidator' => [ - 'minStringLength' => 5, - 'maxStringLength' => 10, - ], - ], - ], - new TextLineValue('0123456789012345'), - [ - new ValidationError( - 'The string can not exceed %size% character.', - 'The string can not exceed %size% characters.', - [ - '%size%' => 10, - ], - 'text' - ), - ], - ], - [ - [ - 'validatorConfiguration' => [ - 'StringLengthValidator' => [ - 'minStringLength' => 10, - 'maxStringLength' => 5, - ], - ], - ], - new TextLineValue('1234567'), - [ - new ValidationError( - 'The string can not exceed %size% character.', - 'The string can not exceed %size% characters.', - [ - '%size%' => 5, - ], - 'text' - ), - new ValidationError( - 'The string cannot be shorter than %size% character.', - 'The string cannot be shorter than %size% characters.', - [ - '%size%' => 10, - ], - 'text' - ), - ], - ], - [ - [ - 'validatorConfiguration' => [ - 'StringLengthValidator' => [ - 'minStringLength' => 5, - 'maxStringLength' => 10, - ], - ], - ], - new TextLineValue('ABC♔'), - [ - new ValidationError( - 'The string cannot be shorter than %size% character.', - 'The string cannot be shorter than %size% characters.', - [ - '%size%' => 5, - ], - 'text' - ), - ], - ], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/TimeTest.php b/eZ/Publish/Core/FieldType/Tests/TimeTest.php deleted file mode 100644 index 9e07ff20d0..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/TimeTest.php +++ /dev/null @@ -1,383 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use DateTime; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Time\Type as Time; -use eZ\Publish\Core\FieldType\Time\Value as TimeValue; - -/** - * @group fieldType - * @group eztime - */ -class TimeTest extends FieldTypeTest -{ - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new Time(); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return []; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return [ - 'useSeconds' => [ - 'type' => 'bool', - 'default' => false, - ], - 'defaultType' => [ - 'type' => 'choice', - 'default' => Time::DEFAULT_EMPTY, - ], - ]; - } - - /** - * Returns the empty value expected from the field type. - */ - protected function getEmptyValueExpectation() - { - return new TimeValue(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - [], - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - $dateTime = new DateTime(); - // change timezone to UTC (+00:00) to be able to calculate proper TimeValue - $timestamp = $dateTime->setTimezone(new \DateTimeZone('UTC'))->getTimestamp(); - - return [ - [ - null, - new TimeValue(), - ], - [ - '2012-08-28 12:20', - new TimeValue(44400), - ], - [ - '2012-08-28 12:20 Europe/Berlin', - new TimeValue(44400), - ], - [ - '2012-08-28 12:20 Asia/Sakhalin', - new TimeValue(44400), - ], - [ - // create new DateTime object from timestamp w/o taking into account server timezone - (new DateTime('@1372896001'))->getTimestamp(), - new TimeValue(1), - ], - [ - TimeValue::fromTimestamp($timestamp), - new TimeValue( - $timestamp - $dateTime->setTime(0, 0, 0)->getTimestamp() - ), - ], - [ - clone $dateTime, - new TimeValue( - $dateTime->getTimestamp() - $dateTime->setTime(0, 0, 0)->getTimestamp() - ), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new TimeValue(), - null, - ], - [ - new TimeValue(0), - 0, - ], - [ - new TimeValue(200), - 200, - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - null, - new TimeValue(), - ], - [ - 0, - new TimeValue(0), - ], - [ - 200, - new TimeValue(200), - ], - ]; - } - - /** - * Provide data sets with field settings which are considered valid by the - * {@link validateFieldSettings()} method. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( 'rows' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidFieldSettings() - { - return [ - [ - [], - ], - [ - [ - 'useSeconds' => true, - 'defaultType' => Time::DEFAULT_EMPTY, - ], - ], - [ - [ - 'useSeconds' => false, - 'defaultType' => Time::DEFAULT_CURRENT_TIME, - ], - ], - ]; - } - - /** - * Provide data sets with field settings which are considered invalid by the - * {@link validateFieldSettings()} method. The method must return a - * non-empty array of validation error when receiving such field settings. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * true, - * ), - * array( - * array( 'nonExistentKey' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInValidFieldSettings() - { - return [ - [ - [ - // useSeconds must be bool - 'useSeconds' => 23, - ], - ], - [ - [ - // defaultType must be constant - 'defaultType' => 42, - ], - ], - ]; - } - - protected function provideFieldTypeIdentifier() - { - return 'eztime'; - } - - public function provideDataForGetName(): array - { - return [ - [$this->getEmptyValueExpectation(), '', [], 'en_GB'], - [new TimeValue(200), '12:03:20 am', [], 'en_GB'], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/Url/Gateway/_fixtures/urls.php b/eZ/Publish/Core/FieldType/Tests/Url/Gateway/_fixtures/urls.php deleted file mode 100644 index 1e414b992b..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/Url/Gateway/_fixtures/urls.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -return array( - 'ezurl' => array( - 0 => array( - 'created' => '1343140541', - 'id' => '23', - 'is_valid' => '1', - 'last_checked' => '0', - 'modified' => '1343140541', - 'original_url_md5' => '9b492048041e95b32de08bafc86d759b', - 'url' => '/content/view/sitemap/2', - ), - 1 => array( - 'created' => '1343140541', - 'id' => '24', - 'is_valid' => '1', - 'last_checked' => '0', - 'modified' => '1343140541', - 'original_url_md5' => 'c86bcb109d8e70f9db65c803baafd550', - 'url' => '/content/view/tagcloud/2', - ), - ), - 'ezurl_object_link' => array( - 0 => array( - 'contentobject_attribute_id' => 42, - 'contentobject_attribute_version' => 5, - 'url_id' => 23, - ), - 1 => array( - 'contentobject_attribute_id' => 43, - 'contentobject_attribute_version' => 6, - 'url_id' => 24, - ), - ), -); diff --git a/eZ/Publish/Core/FieldType/Tests/UrlTest.php b/eZ/Publish/Core/FieldType/Tests/UrlTest.php deleted file mode 100644 index 9e9a15e83e..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/UrlTest.php +++ /dev/null @@ -1,280 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Url\Type as UrlType; -use eZ\Publish\Core\FieldType\Url\Value as UrlValue; - -/** - * @group fieldType - * @group ezurl - */ -class UrlTest extends FieldTypeTest -{ - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return FieldType - */ - protected function createFieldTypeUnderTest() - { - $fieldType = new UrlType(); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return []; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return []; - } - - /** - * Returns the empty value expected from the field type. - */ - protected function getEmptyValueExpectation() - { - return new UrlValue(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - 23, - InvalidArgumentException::class, - ], - [ - new UrlValue(23), - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - null, - new UrlValue(), - ], - [ - 'http://example.com/sindelfingen', - new UrlValue('http://example.com/sindelfingen'), - ], - [ - new UrlValue('http://example.com/sindelfingen', 'Sindelfingen!'), - new UrlValue('http://example.com/sindelfingen', 'Sindelfingen!'), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - return [ - [ - new UrlValue(), - null, - ], - [ - new UrlValue('http://example.com/sindelfingen'), - [ - 'link' => 'http://example.com/sindelfingen', - 'text' => '', - ], - ], - [ - new UrlValue('http://example.com/sindelfingen', 'Sindelfingen!'), - [ - 'link' => 'http://example.com/sindelfingen', - 'text' => 'Sindelfingen!', - ], - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - null, - new UrlValue(), - ], - [ - [ - 'link' => 'http://example.com/sindelfingen', - 'text' => null, - ], - new UrlValue('http://example.com/sindelfingen'), - ], - [ - [ - 'link' => 'http://example.com/sindelfingen', - 'text' => 'Sindelfingen!', - ], - new UrlValue('http://example.com/sindelfingen', 'Sindelfingen!'), - ], - ]; - } - - protected function provideFieldTypeIdentifier() - { - return 'ezurl'; - } - - public function provideDataForGetName(): array - { - return [ - [$this->getEmptyValueExpectation(), '', [], 'en_GB'], - [new UrlValue('', 'Url text'), 'Url text', [], 'en_GB'], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/UserTest.php b/eZ/Publish/Core/FieldType/Tests/UserTest.php deleted file mode 100644 index cc2c3cbf94..0000000000 --- a/eZ/Publish/Core/FieldType/Tests/UserTest.php +++ /dev/null @@ -1,897 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Tests; - -use DateTimeImmutable; -use eZ\Publish\API\Repository\PasswordHashService; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\FieldType\User\Type; -use eZ\Publish\Core\FieldType\User\Type as UserType; -use eZ\Publish\Core\FieldType\User\Value as UserValue; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\Persistence\Cache\UserHandler; -use eZ\Publish\Core\Repository\User\PasswordValidatorInterface; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition as CoreFieldDefinition; -use eZ\Publish\Core\Repository\Values\User\User as RepositoryUser; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\User; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker; - -/** - * @group fieldType - * @group ezurl - */ -class UserTest extends FieldTypeTest -{ - private const UNSUPPORTED_HASH_TYPE = 0xDEADBEEF; - - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return \eZ\Publish\Core\FieldType\User\Type - */ - protected function createFieldTypeUnderTest(): UserType - { - $fieldType = new UserType( - $this->createMock(UserHandler::class), - $this->createMock(PasswordHashService::class), - $this->createMock(PasswordValidatorInterface::class) - ); - $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); - - return $fieldType; - } - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - protected function getValidatorConfigurationSchemaExpectation() - { - return [ - 'PasswordValueValidator' => [ - 'requireAtLeastOneUpperCaseCharacter' => [ - 'type' => 'int', - 'default' => 1, - ], - 'requireAtLeastOneLowerCaseCharacter' => [ - 'type' => 'int', - 'default' => 1, - ], - 'requireAtLeastOneNumericCharacter' => [ - 'type' => 'int', - 'default' => 1, - ], - 'requireAtLeastOneNonAlphanumericCharacter' => [ - 'type' => 'int', - 'default' => null, - ], - 'requireNewPassword' => [ - 'type' => 'int', - 'default' => null, - ], - 'minLength' => [ - 'type' => 'int', - 'default' => 10, - ], - ], - ]; - } - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - protected function getSettingsSchemaExpectation() - { - return [ - UserType::PASSWORD_TTL_SETTING => [ - 'type' => 'int', - 'default' => null, - ], - UserType::PASSWORD_TTL_WARNING_SETTING => [ - 'type' => 'int', - 'default' => null, - ], - UserType::REQUIRE_UNIQUE_EMAIL => [ - 'type' => 'bool', - 'default' => true, - ], - UserType::USERNAME_PATTERN => [ - 'type' => 'string', - 'default' => '^[^@]+$', - ], - ]; - } - - /** - * Returns the empty value expected from the field type. - */ - protected function getEmptyValueExpectation() - { - return new UserValue(); - } - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidInputForAcceptValue() - { - return [ - [ - 23, - InvalidArgumentException::class, - ], - ]; - } - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidInputForAcceptValue() - { - return [ - [ - null, - new UserValue(), - ], - [ - [], - new UserValue([]), - ], - [ - new UserValue(['login' => 'sindelfingen']), - new UserValue(['login' => 'sindelfingen']), - ], - [ - $userData = [ - 'hasStoredLogin' => true, - 'contentId' => 23, - 'login' => 'sindelfingen', - 'email' => 'sindelfingen@example.com', - 'passwordHash' => '1234567890abcdef', - 'passwordHashType' => 'md5', - 'enabled' => true, - 'maxLogin' => 1000, - ], - new UserValue($userData), - ], - [ - new UserValue( - $userData = [ - 'hasStoredLogin' => true, - 'contentId' => 23, - 'login' => 'sindelfingen', - 'email' => 'sindelfingen@example.com', - 'passwordHash' => '1234567890abcdef', - 'passwordHashType' => 'md5', - 'enabled' => true, - 'maxLogin' => 1000, - ] - ), - new UserValue($userData), - ], - ]; - } - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForToHash() - { - $passwordUpdatedAt = new DateTimeImmutable(); - - return [ - [ - new UserValue(), - null, - ], - [ - new UserValue( - $userData = [ - 'hasStoredLogin' => true, - 'contentId' => 23, - 'login' => 'sindelfingen', - 'email' => 'sindelfingen@example.com', - 'passwordHash' => '1234567890abcdef', - 'passwordHashType' => 'md5', - 'passwordUpdatedAt' => $passwordUpdatedAt, - 'enabled' => true, - 'maxLogin' => 1000, - 'plainPassword' => null, - ] - ), - [ - 'passwordUpdatedAt' => $passwordUpdatedAt->getTimestamp(), - ] + $userData, - ], - ]; - } - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInputForFromHash() - { - return [ - [ - null, - new UserValue(), - ], - [ - $userData = [ - 'hasStoredLogin' => true, - 'contentId' => 23, - 'login' => 'sindelfingen', - 'email' => 'sindelfingen@example.com', - 'passwordHash' => '1234567890abcdef', - 'passwordHashType' => 'md5', - 'passwordUpdatedAt' => 1567071092, - 'enabled' => true, - 'maxLogin' => 1000, - ], - new UserValue([ - 'passwordUpdatedAt' => new DateTimeImmutable('@1567071092'), - ] + $userData), - ], - ]; - } - - /** - * Returns empty data set. Validation tests were moved to testValidate method. - * - * @return array - */ - public function provideValidDataForValidate(): array - { - return []; - } - - /** - * Returns empty data set. Validation tests were moved to testValidate method. - * - * @see testValidate - * @see providerForTestValidate - * - * @return array - */ - public function provideInvalidDataForValidate(): array - { - return []; - } - - /** - * @covers \eZ\Publish\Core\FieldType\User\Type::validate - * - * @dataProvider providerForTestValidate - * - * @param \eZ\Publish\Core\FieldType\User\Value $userValue - * @param array $expectedValidationErrors - * @param callable|null $loadByLoginBehaviorCallback - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function testValidate( - UserValue $userValue, - array $expectedValidationErrors, - ?callable $loadByLoginBehaviorCallback - ): void { - $userHandlerMock = $this->createMock(UserHandler::class); - - if (null !== $loadByLoginBehaviorCallback) { - $loadByLoginBehaviorCallback( - $userHandlerMock - ->expects($this->once()) - ->method('loadByLogin') - ->with($userValue->login) - ); - } - - $userType = new UserType( - $userHandlerMock, - $this->createMock(PasswordHashService::class), - $this->createMock(PasswordValidatorInterface::class) - ); - - $fieldSettings = [ - Type::USERNAME_PATTERN => '.*', - Type::REQUIRE_UNIQUE_EMAIL => false, - ]; - - $fieldDefinitionMock = $this->createMock(FieldDefinition::class); - $fieldDefinitionMock->method('__get')->with('fieldSettings')->willReturn($fieldSettings); - $fieldDefinitionMock->method('getFieldSettings')->willReturn($fieldSettings); - - $validationErrors = $userType->validate($fieldDefinitionMock, $userValue); - - self::assertEquals($expectedValidationErrors, $validationErrors); - } - - public function testInvalidLoginFormat(): void - { - $validateUserValue = new UserValue([ - 'hasStoredLogin' => false, - 'contentId' => 46, - 'login' => 'validate@user', - 'email' => 'example@test.ez', - 'passwordHash' => '1234567890abcdef', - 'passwordHashType' => 'md5', - 'enabled' => true, - 'maxLogin' => 1000, - 'plainPassword' => 'testPassword', - ]); - - $userHandlerMock = $this->createMock(UserHandler::class); - - $userHandlerMock - ->expects($this->once()) - ->method('loadByLogin') - ->with($validateUserValue->login) - ->willThrowException(new NotFoundException('', '')); - - $userType = new UserType( - $userHandlerMock, - $this->createMock(PasswordHashService::class), - $this->createMock(PasswordValidatorInterface::class) - ); - - $fieldSettings = [ - UserType::REQUIRE_UNIQUE_EMAIL => false, - UserType::USERNAME_PATTERN => '^[^@]+$', - ]; - - $fieldDefinition = new CoreFieldDefinition(['fieldSettings' => $fieldSettings]); - - $validationErrors = $userType->validate($fieldDefinition, $validateUserValue); - - self::assertEquals([ - new ValidationError( - 'Invalid login format', - null, - [], - 'username' - ), - ], $validationErrors); - } - - public function testValidLoginFormat(): void - { - $validateUserValue = new UserValue([ - 'hasStoredLogin' => false, - 'contentId' => 46, - 'login' => 'validate_user', - 'email' => 'example@test.ez', - 'passwordHash' => '1234567890abcdef', - 'passwordHashType' => 'md5', - 'enabled' => true, - 'maxLogin' => 1000, - 'plainPassword' => 'testPassword', - ]); - - $userHandlerMock = $this->createMock(UserHandler::class); - - $userHandlerMock - ->expects($this->once()) - ->method('loadByLogin') - ->with($validateUserValue->login) - ->willThrowException(new NotFoundException('', '')); - - $userType = new UserType( - $userHandlerMock, - $this->createMock(PasswordHashService::class), - $this->createMock(PasswordValidatorInterface::class) - ); - - $fieldSettings = [ - UserType::REQUIRE_UNIQUE_EMAIL => false, - UserType::USERNAME_PATTERN => '^[^@]+$', - ]; - - $fieldDefinition = new CoreFieldDefinition(['fieldSettings' => $fieldSettings]); - - $validationErrors = $userType->validate($fieldDefinition, $validateUserValue); - - self::assertEquals([], $validationErrors); - } - - public function testEmailAlreadyTaken(): void - { - $existingUser = new User([ - 'id' => 23, - 'login' => 'existing_user', - 'email' => 'test@test.ez', - ]); - - $validateUserValue = new UserValue([ - 'hasStoredLogin' => false, - 'contentId' => 46, - 'login' => 'validate_user', - 'email' => 'test@test.ez', - 'passwordHash' => '1234567890abcdef', - 'passwordHashType' => 'md5', - 'enabled' => true, - 'maxLogin' => 1000, - 'plainPassword' => 'testPassword', - ]); - - $userHandlerMock = $this->createMock(UserHandler::class); - - $userHandlerMock - ->expects($this->once()) - ->method('loadByLogin') - ->with($validateUserValue->login) - ->willThrowException(new NotFoundException('', '')); - - $userHandlerMock - ->expects($this->once()) - ->method('loadByEmail') - ->with($validateUserValue->email) - ->willReturn($existingUser); - - $userType = new UserType( - $userHandlerMock, - $this->createMock(PasswordHashService::class), - $this->createMock(PasswordValidatorInterface::class) - ); - - $fieldSettings = [ - UserType::REQUIRE_UNIQUE_EMAIL => true, - UserType::USERNAME_PATTERN => '^[^@]+$', - ]; - - $fieldDefinition = new CoreFieldDefinition(['fieldSettings' => $fieldSettings]); - - $validationErrors = $userType->validate($fieldDefinition, $validateUserValue); - - self::assertEquals([ - new ValidationError( - "Email '%email%' is used by another user. You must enter a unique email.", - null, - [ - '%email%' => $validateUserValue->email, - ], - 'email' - ), - ], $validationErrors); - } - - /** - * @covers \eZ\Publish\Core\FieldType\User\Type::toPersistenceValue - * - * @dataProvider providerForTestCreatePersistenceValue - */ - public function testCreatePersistenceValue(array $userValueDate, array $expectedFieldValueExternalData): void - { - $passwordHashServiceMock = $this->createMock(PasswordHashService::class); - $passwordHashServiceMock->method('getDefaultHashType')->willReturn(RepositoryUser::DEFAULT_PASSWORD_HASH); - $userType = new UserType( - $this->createMock(UserHandler::class), - $passwordHashServiceMock, - $this->createMock(PasswordValidatorInterface::class) - ); - - $value = new UserValue($userValueDate); - $fieldValue = $userType->toPersistenceValue($value); - - $expected = new FieldValue( - [ - 'data' => null, - 'externalData' => $expectedFieldValueExternalData, - 'sortKey' => null, - ] - ); - self::assertEquals($expected, $fieldValue); - } - - public function providerForTestCreatePersistenceValue(): iterable - { - $passwordUpdatedAt = new DateTimeImmutable(); - $userData = [ - 'hasStoredLogin' => false, - 'contentId' => 46, - 'login' => 'validate_user', - 'email' => 'test@test.ez', - 'passwordHash' => '1234567890abcdef', - 'enabled' => true, - 'maxLogin' => 1000, - 'plainPassword' => '', - 'passwordUpdatedAt' => $passwordUpdatedAt, - ]; - - yield 'when password hash type is given' => [ - $userValueData = [ - 'passwordHashType' => RepositoryUser::PASSWORD_HASH_PHP_DEFAULT, - ] + $userData, - $expectedFieldValueExternalData = [ - 'passwordHashType' => RepositoryUser::PASSWORD_HASH_PHP_DEFAULT, - 'passwordUpdatedAt' => $passwordUpdatedAt->getTimestamp(), - ] + $userData, - ]; - yield 'when password hash type is null' => [ - $userValueData = [ - 'passwordHashType' => null, - ] + $userData, - $expectedFieldValueExternalData = [ - 'passwordHashType' => RepositoryUser::DEFAULT_PASSWORD_HASH, - 'passwordUpdatedAt' => $passwordUpdatedAt->getTimestamp(), - ] + $userData, - ]; - yield 'when password hash type is unsupported' => [ - $userValueData = [ - 'passwordHashType' => self::UNSUPPORTED_HASH_TYPE, - ] + $userData, - $expectedFieldValueExternalData = [ - 'passwordHashType' => RepositoryUser::DEFAULT_PASSWORD_HASH, - 'passwordUpdatedAt' => $passwordUpdatedAt->getTimestamp(), - ] + $userData, - ]; - } - - public function testEmailFreeToUse(): void - { - $validateUserValue = new UserValue([ - 'hasStoredLogin' => false, - 'contentId' => 46, - 'login' => 'validate_user', - 'email' => 'test@test.ez', - 'passwordHash' => '1234567890abcdef', - 'passwordHashType' => 'md5', - 'enabled' => true, - 'maxLogin' => 1000, - 'plainPassword' => 'testPassword', - ]); - - $userHandlerMock = $this->createMock(UserHandler::class); - - $userHandlerMock - ->expects($this->once()) - ->method('loadByLogin') - ->with($validateUserValue->login) - ->willThrowException(new NotFoundException('', '')); - - $userHandlerMock - ->expects($this->once()) - ->method('loadByEmail') - ->with($validateUserValue->email) - ->willThrowException(new NotFoundException('', '')); - - $userType = new UserType( - $userHandlerMock, - $this->createMock(PasswordHashService::class), - $this->createMock(PasswordValidatorInterface::class) - ); - - $fieldSettings = [ - UserType::REQUIRE_UNIQUE_EMAIL => true, - UserType::USERNAME_PATTERN => '^[^@]+$', - ]; - - $fieldDefinition = new CoreFieldDefinition(['fieldSettings' => $fieldSettings]); - - $validationErrors = $userType->validate($fieldDefinition, $validateUserValue); - - self::assertEquals([], $validationErrors); - } - - /** - * Data provider for testValidate test. - * - * @see testValidate - * - * @return array data sets for testValidate method (<code>$userValue, $expectedValidationErrors, $loadByLoginBehaviorCallback</code>) - */ - public function providerForTestValidate(): array - { - return [ - [ - new UserValue( - [ - 'hasStoredLogin' => false, - 'contentId' => 23, - 'login' => 'user', - 'email' => 'invalid', - 'passwordHash' => '1234567890abcdef', - 'passwordHashType' => 'md5', - 'enabled' => true, - 'maxLogin' => 1000, - 'plainPassword' => 'testPassword', - ] - ), - [ - new ValidationError( - "The given e-mail '%email%' is invalid", - null, - [ - '%email%' => 'invalid', - ], - 'email' - ), - ], - static function (InvocationMocker $loadByLoginInvocationMocker) { - $loadByLoginInvocationMocker->willThrowException( - new NotFoundException('user', 'user') - ); - }, - ], - [ - new UserValue([ - 'hasStoredLogin' => false, - 'contentId' => 23, - 'login' => 'sindelfingen', - 'email' => 'sindelfingen@example.com', - 'passwordHash' => '1234567890abcdef', - 'passwordHashType' => 'md5', - 'enabled' => true, - 'maxLogin' => 1000, - 'plainPassword' => 'testPassword', - ]), - [ - new ValidationError( - "The user login '%login%' is used by another user. You must enter a unique login.", - null, - [ - '%login%' => 'sindelfingen', - ], - 'username' - ), - ], - function (InvocationMocker $loadByLoginInvocationMocker) { - $loadByLoginInvocationMocker->willReturn( - $this->createMock(UserValue::class) - ); - }, - ], - [ - new UserValue([ - 'hasStoredLogin' => true, - 'contentId' => 23, - 'login' => 'sindelfingen', - 'email' => 'sindelfingen@example.com', - 'passwordHash' => '1234567890abcdef', - 'passwordHashType' => 'md5', - 'enabled' => true, - 'maxLogin' => 1000, - ]), - [], - null, - ], - ]; - } - - /** - * Provide data sets with field settings which are considered valid by the - * {@link validateFieldSettings()} method. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( 'rows' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidFieldSettings(): array - { - return [ - [ - [], - ], - [ - [ - UserType::PASSWORD_TTL_SETTING => 30, - ], - ], - [ - [ - UserType::PASSWORD_TTL_SETTING => 30, - UserType::PASSWORD_TTL_WARNING_SETTING => null, - ], - ], - [ - [ - UserType::PASSWORD_TTL_SETTING => 30, - UserType::PASSWORD_TTL_WARNING_SETTING => 14, - UserType::REQUIRE_UNIQUE_EMAIL => true, - UserType::USERNAME_PATTERN => '^[^!]+$', - ], - ], - ]; - } - - /** - * Provide data sets with field settings which are considered invalid by the - * {@link validateFieldSettings()} method. The method must return a - * non-empty array of validation error when receiving such field settings. - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * true, - * ), - * array( - * array( 'nonExistentKey' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInValidFieldSettings(): array - { - return [ - [ - [ - UserType::PASSWORD_TTL_WARNING_SETTING => 30, - ], - ], - [ - [ - UserType::PASSWORD_TTL_SETTING => null, - UserType::PASSWORD_TTL_WARNING_SETTING => 60, - ], - ], - [ - [ - UserType::PASSWORD_TTL_SETTING => 30, - UserType::PASSWORD_TTL_WARNING_SETTING => 60, - ], - ], - ]; - } - - protected function provideFieldTypeIdentifier() - { - return 'ezuser'; - } - - public function provideDataForGetName(): array - { - return [ - [$this->getEmptyValueExpectation(), '', [], 'en_GB'], - [new UserValue(['login' => 'johndoe']), 'johndoe', [], 'en_GB'], - ]; - } -} diff --git a/eZ/Publish/Core/FieldType/Tests/developer-got-hurt.m4v b/eZ/Publish/Core/FieldType/Tests/developer-got-hurt.m4v deleted file mode 100644 index 6d50c56173..0000000000 Binary files a/eZ/Publish/Core/FieldType/Tests/developer-got-hurt.m4v and /dev/null differ diff --git a/eZ/Publish/Core/FieldType/TextBlock/SearchField.php b/eZ/Publish/Core/FieldType/TextBlock/SearchField.php deleted file mode 100644 index 55fc46b75d..0000000000 --- a/eZ/Publish/Core/FieldType/TextBlock/SearchField.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\TextBlock; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for TextBlock field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value', - $this->extractShortText($field->value->data), - new Search\FieldType\StringField() - ), - new Search\Field( - 'fulltext', - $field->value->data, - new Search\FieldType\FullTextField() - ), - ]; - } - - /** - * Extracts short snippet of the given $string. - * - * @param string $string - * - * @return string - */ - private function extractShortText($string) - { - return mb_substr(strtok(trim((string)$string), "\r\n"), 0, 255); - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/TextBlock/Type.php b/eZ/Publish/Core/FieldType/TextBlock/Type.php deleted file mode 100644 index 1cb45b500b..0000000000 --- a/eZ/Publish/Core/FieldType/TextBlock/Type.php +++ /dev/null @@ -1,205 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\TextBlock; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -/** - * The TextBlock field type. - * - * Represents a larger body of text, such as text areas. - */ -class Type extends FieldType -{ - protected $settingsSchema = [ - 'textRows' => [ - 'type' => 'int', - 'default' => 10, - ], - ]; - - protected $validatorConfigurationSchema = []; - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'eztext'; - } - - /** - * @param \eZ\Publish\Core\FieldType\TextBlock\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return (string)$value->text; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\TextBlock\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Returns if the given $value is considered empty by the field type. - * - * @param mixed $value - * - * @return bool - */ - public function isEmptyValue(SPIValue $value) - { - return $value->text === null || trim($value->text) === ''; - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param string|\eZ\Publish\Core\FieldType\TextBlock\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\TextBlock\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_string($inputValue)) { - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\TextBlock\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!is_string($value->text)) { - throw new InvalidArgumentType( - '$value->text', - 'string', - $value->text - ); - } - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * - * @param \eZ\Publish\Core\FieldType\TextBlock\Value $value - * - * @return string - */ - protected function getSortInfo(BaseValue $value) - { - return $this->transformationProcessor->transformByGroup( - mb_substr(strtok(trim($value->text), "\r\n"), 0, 255), - 'lowercase' - ); - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\TextBlock\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - return new Value($hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\TextBlock\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - return $value->text; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } - - /** - * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings) - { - $validationErrors = []; - - foreach ($fieldSettings as $name => $value) { - if (isset($this->settingsSchema[$name])) { - switch ($name) { - case 'textRows': - if (!is_int($value)) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' value must be of integer type", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - break; - } - } else { - $validationErrors[] = new ValidationError( - "Setting '%setting%' is unknown", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - } - - return $validationErrors; - } -} diff --git a/eZ/Publish/Core/FieldType/TextBlock/Value.php b/eZ/Publish/Core/FieldType/TextBlock/Value.php deleted file mode 100644 index 6e2641ee6c..0000000000 --- a/eZ/Publish/Core/FieldType/TextBlock/Value.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\TextBlock; - -use eZ\Publish\Core\FieldType\TextLine\Value as TextLineValue; - -/** - * Value for TextBlock field type. - */ -class Value extends TextLineValue -{ -} diff --git a/eZ/Publish/Core/FieldType/TextLine/SearchField.php b/eZ/Publish/Core/FieldType/TextLine/SearchField.php deleted file mode 100644 index b8e041c0f6..0000000000 --- a/eZ/Publish/Core/FieldType/TextLine/SearchField.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\TextLine; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for TextLine field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value', - $field->value->data, - new Search\FieldType\StringField() - ), - new Search\Field( - 'fulltext', - $field->value->data, - new Search\FieldType\FullTextField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/TextLine/Type.php b/eZ/Publish/Core/FieldType/TextLine/Type.php deleted file mode 100644 index 133f889038..0000000000 --- a/eZ/Publish/Core/FieldType/TextLine/Type.php +++ /dev/null @@ -1,248 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\TextLine; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Validator\StringLengthValidator; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -/** - * The TextLine field type. - * - * This field type represents a simple string. - */ -class Type extends FieldType -{ - protected $validatorConfigurationSchema = [ - 'StringLengthValidator' => [ - 'minStringLength' => [ - 'type' => 'int', - 'default' => 0, - ], - 'maxStringLength' => [ - 'type' => 'int', - 'default' => null, - ], - ], - ]; - - /** - * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $validatorConfiguration - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateValidatorConfiguration($validatorConfiguration) - { - $validationErrors = []; - $validator = new StringLengthValidator(); - - foreach ($validatorConfiguration as $validatorIdentifier => $constraints) { - if ($validatorIdentifier !== 'StringLengthValidator') { - $validationErrors[] = new ValidationError( - "Validator '%validator%' is unknown", - null, - [ - '%validator%' => $validatorIdentifier, - ] - ); - continue; - } - $validationErrors += $validator->validateConstraints($constraints); - } - - return $validationErrors; - } - - /** - * Validates a field based on the validators in the field definition. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field - * @param \eZ\Publish\Core\FieldType\TextLine\Value $fieldValue The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) - { - $validationErrors = []; - - if ($this->isEmptyValue($fieldValue)) { - return $validationErrors; - } - - $validatorConfiguration = $fieldDefinition->getValidatorConfiguration(); - $constraints = isset($validatorConfiguration['StringLengthValidator']) - ? $validatorConfiguration['StringLengthValidator'] - : []; - - if (isset($constraints['maxStringLength']) && - $constraints['maxStringLength'] !== false && - $constraints['maxStringLength'] !== 0 && - mb_strlen($fieldValue->text) > $constraints['maxStringLength']) { - $validationErrors[] = new ValidationError( - 'The string can not exceed %size% character.', - 'The string can not exceed %size% characters.', - [ - '%size%' => $constraints['maxStringLength'], - ], - 'text' - ); - } - - if (isset($constraints['minStringLength']) && - $constraints['minStringLength'] !== false && - $constraints['minStringLength'] !== 0 && - mb_strlen($fieldValue->text) < $constraints['minStringLength']) { - $validationErrors[] = new ValidationError( - 'The string cannot be shorter than %size% character.', - 'The string cannot be shorter than %size% characters.', - [ - '%size%' => $constraints['minStringLength'], - ], - 'text' - ); - } - - return $validationErrors; - } - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezstring'; - } - - /** - * @param \eZ\Publish\Core\FieldType\TextLine\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return (string)$value->text; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\TextLine\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Returns if the given $value is considered empty by the field type. - * - * @param mixed $value - * - * @return bool - */ - public function isEmptyValue(SPIValue $value) - { - return $value->text === null || trim($value->text) === ''; - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param string|\eZ\Publish\Core\FieldType\TextLine\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\TextLine\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_string($inputValue)) { - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\TextLine\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!is_string($value->text)) { - throw new InvalidArgumentType( - '$value->text', - 'string', - $value->text - ); - } - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * - * @param \eZ\Publish\Core\FieldType\TextLine\Value $value - * - * @return string - */ - protected function getSortInfo(BaseValue $value) - { - return $this->transformationProcessor->transformByGroup((string)$value, 'lowercase'); - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\TextLine\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - return new Value($hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\TextLine\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - return $value->text; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } -} diff --git a/eZ/Publish/Core/FieldType/TextLine/Value.php b/eZ/Publish/Core/FieldType/TextLine/Value.php deleted file mode 100644 index b6dfde759a..0000000000 --- a/eZ/Publish/Core/FieldType/TextLine/Value.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\TextLine; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for TextLine field type. - */ -class Value extends BaseValue -{ - /** - * Text content. - * - * @var string - */ - public $text; - - /** - * Construct a new Value object and initialize it $text. - * - * @param string $text - */ - public function __construct($text = '') - { - $this->text = $text; - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - return (string)$this->text; - } -} diff --git a/eZ/Publish/Core/FieldType/Time/SearchField.php b/eZ/Publish/Core/FieldType/Time/SearchField.php deleted file mode 100644 index d7c475b9a3..0000000000 --- a/eZ/Publish/Core/FieldType/Time/SearchField.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Time; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for Time field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value', - $field->value->data, - new Search\FieldType\IntegerField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value' => new Search\FieldType\IntegerField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/Time/Type.php b/eZ/Publish/Core/FieldType/Time/Type.php deleted file mode 100644 index 27830b4b2d..0000000000 --- a/eZ/Publish/Core/FieldType/Time/Type.php +++ /dev/null @@ -1,249 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Time; - -use DateTime; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; - -class Type extends FieldType -{ - /** - * Default value type empty. - */ - public const DEFAULT_EMPTY = 0; - - /** - * Default value type current date. - */ - public const DEFAULT_CURRENT_TIME = 1; - - protected $settingsSchema = [ - 'useSeconds' => [ - 'type' => 'bool', - 'default' => false, - ], - // One of the DEFAULT_* class constants - 'defaultType' => [ - 'type' => 'choice', - 'default' => self::DEFAULT_EMPTY, - ], - ]; - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'eztime'; - } - - /** - * @param \eZ\Publish\Core\FieldType\Time\Value|\eZ\Publish\SPI\FieldType\Value $value - * - * @throws \Exception - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - if ($this->isEmptyValue($value)) { - return ''; - } - - $dateTime = new DateTime("@{$value->time}"); - - return $dateTime->format('g:i:s a'); - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Time\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param string|int|\DateTime|\eZ\Publish\Core\FieldType\Time\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\Time\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_string($inputValue)) { - $inputValue = Value::fromString($inputValue); - } - - if (is_int($inputValue)) { - $inputValue = Value::fromTimestamp($inputValue); - } - - if ($inputValue instanceof DateTime) { - $inputValue = Value::fromDateTime($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Time\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (!is_int($value->time)) { - throw new InvalidArgumentType( - '$value->time', - 'DateTime', - $value->time - ); - } - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * - * @param \eZ\Publish\Core\FieldType\Time\Value $value - * - * @return int - */ - protected function getSortInfo(BaseValue $value) - { - return $value->time; - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param int $hash Number of seconds since Unix Epoch - * - * @return \eZ\Publish\Core\FieldType\Time\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - return new Value($hash); - } - - /** - * Returns if the given $value is considered empty by the field type. - * - * - * @param \eZ\Publish\Core\FieldType\Value $value - * - * @return bool - */ - public function isEmptyValue(SPIValue $value) - { - if ($value->time === null) { - return true; - } - - return false; - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Time\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - return $value->time; - } - - /** - * Returns whether the field type is searchable. - * - * @return bool - */ - public function isSearchable() - { - return true; - } - - /** - * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings) - { - $validationErrors = []; - - foreach ($fieldSettings as $name => $value) { - if (!isset($this->settingsSchema[$name])) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' is unknown", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - continue; - } - - switch ($name) { - case 'useSeconds': - if (!is_bool($value)) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' value must be of boolean type", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - break; - case 'defaultType': - $definedTypes = [ - self::DEFAULT_EMPTY, - self::DEFAULT_CURRENT_TIME, - ]; - if (!in_array($value, $definedTypes, true)) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' is of unknown type", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - break; - } - } - - return $validationErrors; - } -} diff --git a/eZ/Publish/Core/FieldType/Time/Value.php b/eZ/Publish/Core/FieldType/Time/Value.php deleted file mode 100644 index 0f1c3b50ba..0000000000 --- a/eZ/Publish/Core/FieldType/Time/Value.php +++ /dev/null @@ -1,108 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Time; - -use DateTime; -use Exception; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for Time field type. - */ -class Value extends BaseValue -{ - /** - * Time of day as number of seconds. - * - * @var int|null - */ - public $time; - - /** - * Time format to be used by {@link __toString()}. - * - * @var string - */ - public $stringFormat = 'H:i:s'; - - /** - * Construct a new Value object and initialize it with $seconds as number of seconds from beginning of day. - * - * @param mixed $seconds - */ - public function __construct($seconds = null) - { - $this->time = $seconds; - } - - /** - * Creates a Value from the given $dateTime. - * - * @param \DateTime $dateTime - * - * @return \eZ\Publish\Core\FieldType\Time\Value - */ - public static function fromDateTime(DateTime $dateTime) - { - $dateTime = clone $dateTime; - - return new static($dateTime->getTimestamp() - $dateTime->setTime(0, 0, 0)->getTimestamp()); - } - - /** - * Creates a Value from the given $timeString. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param string $timeString - * - * @return \eZ\Publish\Core\FieldType\Time\Value - */ - public static function fromString($timeString) - { - try { - return static::fromDateTime(new DateTime($timeString)); - } catch (Exception $e) { - throw new InvalidArgumentValue('$timeString', $timeString, __CLASS__, $e); - } - } - - /** - * Creates a Value from the given $timestamp. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param int $timestamp - * - * @return static - */ - public static function fromTimestamp($timestamp) - { - try { - $dateTime = new DateTime("@{$timestamp}"); - - return static::fromDateTime($dateTime); - } catch (Exception $e) { - throw new InvalidArgumentValue('$timestamp', $timestamp, __CLASS__, $e); - } - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - if ($this->time === null) { - return ''; - } - - $dateTime = new DateTime("@{$this->time}"); - - return $dateTime->format($this->stringFormat); - } -} diff --git a/eZ/Publish/Core/FieldType/Unindexed.php b/eZ/Publish/Core/FieldType/Unindexed.php deleted file mode 100644 index e2823ff6c1..0000000000 --- a/eZ/Publish/Core/FieldType/Unindexed.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -/** - * Indexable definition for string field type. - */ -class Unindexed implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return []; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return []; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return null; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return null; - } -} diff --git a/eZ/Publish/Core/FieldType/Url/SearchField.php b/eZ/Publish/Core/FieldType/Url/SearchField.php deleted file mode 100644 index 8835656c1f..0000000000 --- a/eZ/Publish/Core/FieldType/Url/SearchField.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Url; - -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Search; - -/** - * Indexable definition for Url field type. - */ -class SearchField implements Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition) - { - return [ - new Search\Field( - 'value_url', - $field->value->externalData, - new Search\FieldType\StringField() - ), - new Search\Field( - 'value_id', - isset($field->value->data['urlId']) ? $field->value->data['urlId'] : '', - new Search\FieldType\StringField() - ), - new Search\Field( - 'value_text', - $text = (isset($field->value->data['text']) ? $field->value->data['text'] : ''), - new Search\FieldType\StringField() - ), - new Search\Field( - 'fulltext', - $text, - new Search\FieldType\FullTextField() - ), - ]; - } - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition() - { - return [ - 'value_url' => new Search\FieldType\StringField(), - 'value_id' => new Search\FieldType\StringField(), - 'value_text' => new Search\FieldType\StringField(), - ]; - } - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField() - { - return 'value_url'; - } - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField() - { - return $this->getDefaultMatchField(); - } -} diff --git a/eZ/Publish/Core/FieldType/Url/Type.php b/eZ/Publish/Core/FieldType/Url/Type.php deleted file mode 100644 index f090111c98..0000000000 --- a/eZ/Publish/Core/FieldType/Url/Type.php +++ /dev/null @@ -1,204 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Url; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; - -/** - * The Url field type. - * - * This field type represents a simple string. - */ -class Type extends FieldType -{ - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezurl'; - } - - /** - * @param \eZ\Publish\Core\FieldType\Url\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return (string)$value->text; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\Url\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param string|\eZ\Publish\Core\FieldType\Url\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\Url\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_string($inputValue)) { - $inputValue = new Value($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\Url\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - if (null !== $value->link && !is_string($value->link)) { - throw new InvalidArgumentType( - '$value->link', - 'string', - $value->link - ); - } - - if (null !== $value->text && !is_string($value->text)) { - throw new InvalidArgumentType( - '$value->text', - 'string', - $value->text - ); - } - } - - /** - * {@inheritdoc} - */ - protected function getSortInfo(BaseValue $value) - { - return false; - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\Url\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - if (isset($hash['text'])) { - return new Value($hash['link'], $hash['text']); - } - - return new Value($hash['link']); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\Url\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - return ['link' => $value->link, 'text' => $value->text]; - } - - /** - * Converts a $value to a persistence value. - * - * In this method the field type puts the data which is stored in the field of content in the repository - * into the property FieldValue::data. The format of $data is a primitive, an array (map) or an object, which - * is then canonically converted to e.g. json/xml structures by future storage engines without - * further conversions. For mapping the $data to the legacy database an appropriate Converter - * (implementing eZ\Publish\Core\Persistence\Legacy\FieldValue\Converter) has implemented for the field - * type. Note: $data should only hold data which is actually stored in the field. It must not - * hold data which is stored externally. - * - * The $externalData property in the FieldValue is used for storing data externally by the - * FieldStorage interface method storeFieldData. - * - * The FieldValuer::sortKey is build by the field type for using by sort operations. - * - * @see \eZ\Publish\SPI\Persistence\Content\FieldValue - * - * @param \eZ\Publish\Core\FieldType\Url\Value $value The value of the field type - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue the value processed by the storage engine - */ - public function toPersistenceValue(SPIValue $value) - { - if ($value === null) { - return new FieldValue( - [ - 'data' => [], - 'externalData' => null, - 'sortKey' => null, - ] - ); - } - - return new FieldValue( - [ - 'data' => [ - 'urlId' => null, - 'text' => $value->text, - ], - 'externalData' => $value->link, - 'sortKey' => $this->getSortInfo($value), - ] - ); - } - - /** - * Converts a persistence $fieldValue to a Value. - * - * This method builds a field type value from the $data and $externalData properties. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - * - * @return \eZ\Publish\Core\FieldType\Url\Value - */ - public function fromPersistenceValue(FieldValue $fieldValue) - { - if ($fieldValue->externalData === null) { - return $this->getEmptyValue(); - } - - return new Value( - $fieldValue->externalData, - $fieldValue->data['text'] - ); - } -} diff --git a/eZ/Publish/Core/FieldType/Url/UrlStorage.php b/eZ/Publish/Core/FieldType/Url/UrlStorage.php deleted file mode 100644 index 30973cd4b5..0000000000 --- a/eZ/Publish/Core/FieldType/Url/UrlStorage.php +++ /dev/null @@ -1,143 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Url; - -use eZ\Publish\SPI\FieldType\GatewayBasedStorage; -use eZ\Publish\SPI\FieldType\StorageGateway; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use Psr\Log\LoggerInterface; - -/** - * Converter for Url field type external storage. - */ -class UrlStorage extends GatewayBasedStorage -{ - /** @var \Psr\Log\LoggerInterface */ - protected $logger; - - /** @var \eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway */ - protected $gateway; - - /** - * Construct from gateways. - * - * @param \eZ\Publish\SPI\FieldType\StorageGateway $gateway - * @param \Psr\Log\LoggerInterface $logger - */ - public function __construct(StorageGateway $gateway, LoggerInterface $logger = null) - { - parent::__construct($gateway); - $this->logger = $logger; - } - - /** - * @see \eZ\Publish\SPI\FieldType\FieldStorage - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context - * - * @return bool|mixed - */ - public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context) - { - $url = $field->value->externalData; - - if (empty($url)) { - return false; - } - - $map = $this->gateway->getUrlIdMap([$url]); - - if (isset($map[$url])) { - $urlId = $map[$url]; - } else { - $urlId = $this->gateway->insertUrl($url); - } - - $this->gateway->linkUrl($urlId, $field->id, $versionInfo->versionNo); - - $this->gateway->unlinkUrl( - $field->id, - $versionInfo->versionNo, - [$urlId] - ); - - $field->value->data['urlId'] = $urlId; - - // Signals that the Value has been modified and that an update is to be performed - return true; - } - - /** - * Populates $field value property based on the external data. - * $field->value is a {@link eZ\Publish\SPI\Persistence\Content\FieldValue} object. - * This value holds the data as a {@link eZ\Publish\Core\FieldType\Value} based object, - * according to the field type (e.g. for TextLine, it will be a {@link eZ\Publish\Core\FieldType\TextLine\Value} object). - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context - */ - public function getFieldData(VersionInfo $versionInfo, Field $field, array $context) - { - $id = $field->value->data['urlId']; - if (empty($id)) { - $field->value->externalData = null; - - return; - } - - $map = $this->gateway->getIdUrlMap([$id]); - - // URL id is not in the DB - if (!isset($map[$id]) && isset($this->logger)) { - $this->logger->error("URL with ID '{$id}' not found"); - } - - $field->value->externalData = isset($map[$id]) ? $map[$id] : ''; - } - - /** - * Deletes field data for all $fieldIds in the version identified by - * $versionInfo. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param array $fieldIds - * @param array $context - * - * @return bool - */ - public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context) - { - foreach ($fieldIds as $fieldId) { - $this->gateway->unlinkUrl($fieldId, $versionInfo->versionNo); - } - } - - /** - * Checks if field type has external data to deal with. - * - * @return bool - */ - public function hasFieldData() - { - return true; - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(VersionInfo $versionInfo, Field $field, array $context) - { - } -} diff --git a/eZ/Publish/Core/FieldType/Url/UrlStorage/Gateway.php b/eZ/Publish/Core/FieldType/Url/UrlStorage/Gateway.php deleted file mode 100644 index abb0e96a7f..0000000000 --- a/eZ/Publish/Core/FieldType/Url/UrlStorage/Gateway.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Url\UrlStorage; - -use eZ\Publish\SPI\FieldType\StorageGateway; - -/** - * Abstract gateway class for Url field type. - * Handles URL data. - */ -abstract class Gateway extends StorageGateway -{ - /** - * Returns a list of URLs for a list of URL ids. - * - * Non-existent ids are ignored. - * - * @param int[]|string[] $ids An array of URL ids - * - * @return array An array of URLs, with ids as keys - */ - abstract public function getIdUrlMap(array $ids); - - /** - * Returns a list of URL ids for a list of URLs. - * - * Non-existent URLs are ignored. - * - * @param string[] $urls An array of URLs - * - * @return array An array of URL ids, with URLs as keys - */ - abstract public function getUrlIdMap(array $urls); - - /** - * Inserts a new $url and returns its id. - * - * @param string $url The URL to insert in the database - * - * @return int|string - */ - abstract public function insertUrl($url); - - /** - * Creates link to URL with $urlId for field with $fieldId in $versionNo. - * - * @param int|string $urlId - * @param int|string $fieldId - * @param int $versionNo - */ - abstract public function linkUrl($urlId, $fieldId, $versionNo); - - /** - * Removes link to URL for $fieldId in $versionNo and cleans up possibly orphaned URLs. - * - * @param int|string $fieldId - * @param int $versionNo - * @param int[] $excludeUrlIds - */ - abstract public function unlinkUrl($fieldId, $versionNo, array $excludeUrlIds = []); -} diff --git a/eZ/Publish/Core/FieldType/Url/UrlStorage/Gateway/DoctrineStorage.php b/eZ/Publish/Core/FieldType/Url/UrlStorage/Gateway/DoctrineStorage.php deleted file mode 100644 index 4629095095..0000000000 --- a/eZ/Publish/Core/FieldType/Url/UrlStorage/Gateway/DoctrineStorage.php +++ /dev/null @@ -1,276 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ParameterType; -use eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway; -use eZ\Publish\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase; -use PDO; - -class DoctrineStorage extends Gateway -{ - public const URL_TABLE = DoctrineDatabase::URL_TABLE; - public const URL_LINK_TABLE = DoctrineDatabase::URL_LINK_TABLE; - - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - public function __construct(Connection $connection) - { - $this->connection = $connection; - } - - /** - * Return a list of URLs for a list of URL ids. - * - * Non-existent ids are ignored. - * - * @param int[] $ids An array of URL ids - * - * @return array An array of URLs, with ids as keys - */ - public function getIdUrlMap(array $ids) - { - $map = []; - - if (!empty($ids)) { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - $this->connection->quoteIdentifier('id'), - $this->connection->quoteIdentifier('url') - ) - ->from(self::URL_TABLE) - ->where('id IN (:ids)') - ->setParameter(':ids', $ids, Connection::PARAM_INT_ARRAY); - - $statement = $query->execute(); - foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $row) { - $map[$row['id']] = $row['url']; - } - } - - return $map; - } - - /** - * Return a list of URL ids for a list of URLs. - * - * Non-existent URLs are ignored. - * - * @param string[] $urls An array of URLs - * - * @return array An array of URL ids, with URLs as keys - */ - public function getUrlIdMap(array $urls) - { - $map = []; - - if (!empty($urls)) { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - $this->connection->quoteIdentifier('id'), - $this->connection->quoteIdentifier('url') - ) - ->from(self::URL_TABLE) - ->where( - $query->expr()->in('url', ':urls') - ) - ->setParameter(':urls', $urls, Connection::PARAM_STR_ARRAY); - - $statement = $query->execute(); - foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $row) { - $map[$row['url']] = $row['id']; - } - } - - return $map; - } - - /** - * Insert a new $url and returns its id. - * - * @param string $url The URL to insert in the database - * - * @return string - */ - public function insertUrl($url) - { - $time = time(); - - $query = $this->connection->createQueryBuilder(); - - $query - ->insert($this->connection->quoteIdentifier(self::URL_TABLE)) - ->values( - [ - 'created' => ':created', - 'modified' => ':modified', - 'original_url_md5' => ':original_url_md5', - 'url' => ':url', - ] - ) - ->setParameter(':created', $time, PDO::PARAM_INT) - ->setParameter(':modified', $time, PDO::PARAM_INT) - ->setParameter(':original_url_md5', md5($url)) - ->setParameter(':url', $url) - ; - - $query->execute(); - - return (int)$this->connection->lastInsertId( - $this->getSequenceName(self::URL_TABLE, 'id') - ); - } - - /** - * Create link to URL with $urlId for field with $fieldId in $versionNo. - * - * @param int $urlId - * @param int $fieldId - * @param int $versionNo - */ - public function linkUrl($urlId, $fieldId, $versionNo) - { - $query = $this->connection->createQueryBuilder(); - - $query - ->insert($this->connection->quoteIdentifier(self::URL_LINK_TABLE)) - ->values( - [ - 'contentobject_attribute_id' => ':contentobject_attribute_id', - 'contentobject_attribute_version' => ':contentobject_attribute_version', - 'url_id' => ':url_id', - ] - ) - ->setParameter(':contentobject_attribute_id', $fieldId, PDO::PARAM_INT) - ->setParameter(':contentobject_attribute_version', $versionNo, PDO::PARAM_INT) - ->setParameter(':url_id', $urlId, PDO::PARAM_INT) - ; - - $query->execute(); - } - - /** - * Remove link to URL for $fieldId in $versionNo and cleans up possibly orphaned URLs. - * - * @param int $fieldId - * @param int $versionNo - * @param int[] $excludeUrlIds - */ - public function unlinkUrl($fieldId, $versionNo, array $excludeUrlIds = []): void - { - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery - ->select('link.url_id') - ->from($this->connection->quoteIdentifier(self::URL_LINK_TABLE), 'link') - ->where( - $selectQuery->expr()->andX( - $selectQuery->expr()->in( - 'link.contentobject_attribute_id', - ':contentobject_attribute_id' - ), - $selectQuery->expr()->in( - 'link.contentobject_attribute_version', - ':contentobject_attribute_version' - ) - ) - ) - ->setParameter(':contentobject_attribute_id', $fieldId, ParameterType::INTEGER) - ->setParameter(':contentobject_attribute_version', $versionNo, ParameterType::INTEGER); - - $statement = $selectQuery->execute(); - $potentiallyOrphanedUrls = $statement->fetchFirstColumn(); - - if (empty($potentiallyOrphanedUrls)) { - return; - } - - $deleteQuery = $this->connection->createQueryBuilder(); - $deleteQuery - ->delete($this->connection->quoteIdentifier(self::URL_LINK_TABLE)) - ->where( - $deleteQuery->expr()->and( - $deleteQuery->expr()->in( - 'contentobject_attribute_id', - ':contentobject_attribute_id' - ), - $deleteQuery->expr()->in( - 'contentobject_attribute_version', - ':contentobject_attribute_version' - ) - ) - ) - ->setParameter(':contentobject_attribute_id', $fieldId, ParameterType::INTEGER) - ->setParameter(':contentobject_attribute_version', $versionNo, ParameterType::INTEGER); - - if (empty($excludeUrlIds) === false) { - $deleteQuery - ->andWhere( - $deleteQuery->expr()->notIn( - 'url_id', - ':url_ids' - ) - ) - ->setParameter('url_ids', $excludeUrlIds, Connection::PARAM_INT_ARRAY); - } - - $deleteQuery->execute(); - - $this->deleteOrphanedUrls($potentiallyOrphanedUrls); - } - - /** - * Delete potentially orphaned URLs. - * - * That could be avoided if the feature is implemented there. - * - * URL is orphaned if it is not linked to a content attribute through ezurl_object_link table. - * - * @param int[] $potentiallyOrphanedUrls - */ - private function deleteOrphanedUrls(array $potentiallyOrphanedUrls): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->select($this->connection->quoteIdentifier('url.id')) - ->from($this->connection->quoteIdentifier(self::URL_TABLE), 'url') - ->leftJoin( - 'url', - $this->connection->quoteIdentifier(self::URL_LINK_TABLE), - 'link', - 'url.id = link.url_id' - ) - ->where( - $query->expr()->in( - 'url.id', - ':url_ids' - ) - ) - ->andWhere($query->expr()->isNull('link.url_id')) - ->setParameter('url_ids', $potentiallyOrphanedUrls, Connection::PARAM_INT_ARRAY) - ; - - $statement = $query->execute(); - - $ids = $statement->fetchAll(PDO::FETCH_COLUMN); - if (empty($ids)) { - return; - } - - $deleteQuery = $this->connection->createQueryBuilder(); - $deleteQuery - ->delete($this->connection->quoteIdentifier(self::URL_TABLE)) - ->where($deleteQuery->expr()->in('id', ':ids')) - ->setParameter(':ids', $ids, Connection::PARAM_STR_ARRAY) - ; - - $deleteQuery->execute(); - } -} diff --git a/eZ/Publish/Core/FieldType/Url/Value.php b/eZ/Publish/Core/FieldType/Url/Value.php deleted file mode 100644 index b2dbdf7ca9..0000000000 --- a/eZ/Publish/Core/FieldType/Url/Value.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Url; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for Url field type. - */ -class Value extends BaseValue -{ - /** - * Link content. - * - * @var string|null - */ - public $link; - - /** - * Text content. - * - * @var string|null - */ - public $text; - - /** - * Construct a new Value object and initialize it with its $link and optional $text. - * - * @param string $link - * @param string $text - */ - public function __construct($link = null, $text = null) - { - $this->link = $link; - $this->text = $text; - } - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - return (string)$this->link; - } -} diff --git a/eZ/Publish/Core/FieldType/User/Type.php b/eZ/Publish/Core/FieldType/User/Type.php deleted file mode 100644 index 502d0c7674..0000000000 --- a/eZ/Publish/Core/FieldType/User/Type.php +++ /dev/null @@ -1,562 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\User; - -use DateTimeImmutable; -use DateTimeInterface; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\PasswordHashService; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\Core\Repository\User\PasswordValidatorInterface; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\User\Handler as SPIUserHandler; -use LogicException; - -/** - * The User field type. - * - * This field type represents a simple string. - */ -class Type extends FieldType -{ - public const PASSWORD_TTL_SETTING = 'PasswordTTL'; - public const PASSWORD_TTL_WARNING_SETTING = 'PasswordTTLWarning'; - public const REQUIRE_UNIQUE_EMAIL = 'RequireUniqueEmail'; - public const USERNAME_PATTERN = 'UsernamePattern'; - - /** @var array */ - protected $settingsSchema = [ - self::PASSWORD_TTL_SETTING => [ - 'type' => 'int', - 'default' => null, - ], - self::PASSWORD_TTL_WARNING_SETTING => [ - 'type' => 'int', - 'default' => null, - ], - self::REQUIRE_UNIQUE_EMAIL => [ - 'type' => 'bool', - 'default' => true, - ], - self::USERNAME_PATTERN => [ - 'type' => 'string', - 'default' => '^[^@]+$', - ], - ]; - - /** @var array */ - protected $validatorConfigurationSchema = [ - 'PasswordValueValidator' => [ - 'requireAtLeastOneUpperCaseCharacter' => [ - 'type' => 'int', - 'default' => 1, - ], - 'requireAtLeastOneLowerCaseCharacter' => [ - 'type' => 'int', - 'default' => 1, - ], - 'requireAtLeastOneNumericCharacter' => [ - 'type' => 'int', - 'default' => 1, - ], - 'requireAtLeastOneNonAlphanumericCharacter' => [ - 'type' => 'int', - 'default' => null, - ], - 'requireNewPassword' => [ - 'type' => 'int', - 'default' => null, - ], - 'minLength' => [ - 'type' => 'int', - 'default' => 10, - ], - ], - ]; - - /** @var \eZ\Publish\SPI\Persistence\User\Handler */ - private $userHandler; - - /** @var \eZ\Publish\API\Repository\PasswordHashService */ - private $passwordHashService; - - /** @var \eZ\Publish\Core\Repository\User\PasswordValidatorInterface */ - private $passwordValidator; - - public function __construct( - SPIUserHandler $userHandler, - PasswordHashService $passwordHashGenerator, - PasswordValidatorInterface $passwordValidator - ) { - $this->userHandler = $userHandler; - $this->passwordHashService = $passwordHashGenerator; - $this->passwordValidator = $passwordValidator; - } - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier() - { - return 'ezuser'; - } - - /** - * @param \eZ\Publish\Core\FieldType\User\Value|\eZ\Publish\SPI\FieldType\Value $value - */ - public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return (string)$value->login; - } - - /** - * Indicates if the field definition of this type can appear only once in the same ContentType. - * - * @return bool - */ - public function isSingular() - { - return true; - } - - /** - * Indicates if the field definition of this type can be added to a ContentType with Content instances. - * - * @return bool - */ - public function onlyEmptyInstance() - { - return true; - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return \eZ\Publish\Core\FieldType\User\Value - */ - public function getEmptyValue() - { - return new Value(); - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * @param array|\eZ\Publish\Core\FieldType\User\Value $inputValue - * - * @return \eZ\Publish\Core\FieldType\User\Value The potentially converted and structurally plausible value. - */ - protected function createValueFromInput($inputValue) - { - if (is_array($inputValue)) { - $inputValue = $this->fromHash($inputValue); - } - - return $inputValue; - } - - /** - * Throws an exception if value structure is not of expected format. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. - * - * @param \eZ\Publish\Core\FieldType\User\Value $value - */ - protected function checkValueStructure(BaseValue $value) - { - // Does nothing - } - - /** - * {@inheritdoc} - */ - protected function getSortInfo(BaseValue $value) - { - return false; - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return \eZ\Publish\Core\FieldType\User\Value $value - */ - public function fromHash($hash) - { - if ($hash === null) { - return $this->getEmptyValue(); - } - - if (isset($hash['passwordUpdatedAt']) && $hash['passwordUpdatedAt'] !== null) { - $hash['passwordUpdatedAt'] = new DateTimeImmutable('@' . $hash['passwordUpdatedAt']); - } - - return new Value($hash); - } - - /** - * Converts a $Value to a hash. - * - * @param \eZ\Publish\Core\FieldType\User\Value $value - * - * @return mixed - */ - public function toHash(SPIValue $value) - { - if ($this->isEmptyValue($value)) { - return null; - } - - $hash = (array)$value; - if ($hash['passwordUpdatedAt'] instanceof DateTimeInterface) { - $hash['passwordUpdatedAt'] = $hash['passwordUpdatedAt']->getTimestamp(); - } - - return $hash; - } - - /** - * Converts a $value to a persistence value. - * - * In this method the field type puts the data which is stored in the field of content in the repository - * into the property FieldValue::data. The format of $data is a primitive, an array (map) or an object, which - * is then canonically converted to e.g. json/xml structures by future storage engines without - * further conversions. For mapping the $data to the legacy database an appropriate Converter - * (implementing eZ\Publish\Core\Persistence\Legacy\FieldValue\Converter) has implemented for the field - * type. Note: $data should only hold data which is actually stored in the field. It must not - * hold data which is stored externally. - * - * The $externalData property in the FieldValue is used for storing data externally by the - * FieldStorage interface method storeFieldData. - * - * The FieldValuer::sortKey is build by the field type for using by sort operations. - * - * @see \eZ\Publish\SPI\Persistence\Content\FieldValue - * - * @param \eZ\Publish\Core\FieldType\User\Value $value The value of the field type - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue the value processed by the storage engine - */ - public function toPersistenceValue(SPIValue $value) - { - $value->passwordHashType = $this->getPasswordHashTypeForPersistenceValue($value); - if ($value->plainPassword) { - $value->passwordHash = $this->passwordHashService->createPasswordHash( - $value->plainPassword, - $value->passwordHashType - ); - $value->passwordUpdatedAt = new DateTimeImmutable(); - } - - return new FieldValue( - [ - 'data' => null, - 'externalData' => $this->toHash($value), - 'sortKey' => null, - ] - ); - } - - private function getPasswordHashTypeForPersistenceValue(SPIValue $value): int - { - if (null === $value->passwordHashType) { - return $this->passwordHashService->getDefaultHashType(); - } - - if (!$this->passwordHashService->isHashTypeSupported($value->passwordHashType)) { - return $this->passwordHashService->getDefaultHashType(); - } - - return $value->passwordHashType; - } - - /** - * Converts a persistence $fieldValue to a Value. - * - * This method builds a field type value from the $data and $externalData properties. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - * - * @return \eZ\Publish\Core\FieldType\User\Value - */ - public function fromPersistenceValue(FieldValue $fieldValue) - { - return $this->acceptValue($fieldValue->externalData); - } - - /** - * Validates a field based on the validators in the field definition. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field - * @param \eZ\Publish\Core\FieldType\User\Value $fieldValue The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) - { - $errors = []; - - if ($this->isEmptyValue($fieldValue)) { - return $errors; - } - - if (!is_string($fieldValue->login) || empty($fieldValue->login)) { - $errors[] = new ValidationError( - 'Login required', - null, - [], - 'username' - ); - } - - $pattern = sprintf('/%s/', $fieldDefinition->fieldSettings[self::USERNAME_PATTERN]); - $loginFormatValid = preg_match($pattern, $fieldValue->login); - if (!$fieldValue->hasStoredLogin && !$loginFormatValid) { - $errors[] = new ValidationError( - 'Invalid login format', - null, - [], - 'username' - ); - } - - if (!is_string($fieldValue->email) || empty($fieldValue->email)) { - $errors[] = new ValidationError( - 'Email required', - null, - [], - 'email' - ); - } elseif (false === filter_var($fieldValue->email, FILTER_VALIDATE_EMAIL)) { - $errors[] = new ValidationError( - "The given e-mail '%email%' is invalid", - null, - ['%email%' => $fieldValue->email], - 'email' - ); - } - - if (!$fieldValue->hasStoredLogin && (!is_string($fieldValue->plainPassword) || empty($fieldValue->plainPassword))) { - $errors[] = new ValidationError( - 'Password required', - null, - [], - 'password' - ); - } - - if (!is_bool($fieldValue->enabled)) { - $errors[] = new ValidationError( - 'Enabled must be boolean value', - null, - [], - 'enabled' - ); - } - - if (!$fieldValue->hasStoredLogin && isset($fieldValue->login)) { - try { - $login = $fieldValue->login; - $this->userHandler->loadByLogin($login); - - // If you want to change this ValidationError message, please remember to change it also in Content Forms in lib/Validator/Constraints/FieldValueValidatorMessages class - $errors[] = new ValidationError( - "The user login '%login%' is used by another user. You must enter a unique login.", - null, - [ - '%login%' => $login, - ], - 'username' - ); - } catch (NotFoundException $e) { - // Do nothing - } - } - - if ($fieldDefinition->fieldSettings[self::REQUIRE_UNIQUE_EMAIL]) { - try { - $email = $fieldValue->email; - try { - $user = $this->userHandler->loadByEmail($email); - } catch (LogicException $exception) { - // There are multiple users with the same email - } - - // Don't prevent email update - if (empty($user) || $user->id != $fieldValue->contentId) { - // If you want to change this ValidationError message, please remember to change it also in Content Forms in lib/Validator/Constraints/FieldValueValidatorMessages class - $errors[] = new ValidationError( - "Email '%email%' is used by another user. You must enter a unique email.", - null, - [ - '%email%' => $email, - ], - 'email' - ); - } - } catch (NotFoundException $e) { - // Do nothing - } - } - - if (!empty($fieldValue->plainPassword)) { - $passwordValidationErrors = $this->passwordValidator->validatePassword( - $fieldValue->plainPassword, - $fieldDefinition - ); - - $errors = array_merge($errors, $passwordValidationErrors); - - if (!empty($fieldValue->passwordHash) && $this->isNewPasswordRequired($fieldDefinition)) { - $isPasswordReused = $this->passwordHashService->isValidPassword( - $fieldValue->plainPassword, - $fieldValue->passwordHash, - $fieldValue->passwordHashType - ); - - if ($isPasswordReused) { - $errors[] = new ValidationError('New password cannot be the same as old password', null, [], 'password'); - } - } - } - - return $errors; - } - - /** - * {@inheritdoc} - */ - public function validateValidatorConfiguration($validatorConfiguration) - { - $validationErrors = []; - - foreach ((array)$validatorConfiguration as $validatorIdentifier => $constraints) { - if ($validatorIdentifier !== 'PasswordValueValidator') { - $validationErrors[] = new ValidationError( - "Validator '%validator%' is unknown", - null, - [ - 'validator' => $validatorIdentifier, - ], - "[$validatorIdentifier]" - ); - } - } - - return $validationErrors; - } - - /** - * {@inheritdoc} - */ - public function validateFieldSettings($fieldSettings) - { - $validationErrors = []; - - foreach ($fieldSettings as $name => $value) { - if (!isset($this->settingsSchema[$name])) { - $validationErrors[] = new ValidationError( - "Setting '%setting%' is unknown", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - - continue; - } - - $error = null; - switch ($name) { - case self::PASSWORD_TTL_SETTING: - $error = $this->validatePasswordTTLSetting($name, $value); - break; - case self::PASSWORD_TTL_WARNING_SETTING: - $error = $this->validatePasswordTTLWarningSetting($name, $value, $fieldSettings); - break; - } - - if ($error !== null) { - $validationErrors[] = $error; - } - } - - return $validationErrors; - } - - private function validatePasswordTTLSetting(string $name, $value): ?ValidationError - { - if ($value !== null && !is_int($value)) { - return new ValidationError( - "Setting '%setting%' value must be of integer type", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - - return null; - } - - private function validatePasswordTTLWarningSetting(string $name, $value, $fieldSettings): ?ValidationError - { - if ($value !== null) { - if (!is_int($value)) { - return new ValidationError( - "Setting '%setting%' value must be of integer type", - null, - [ - '%setting%' => $name, - ], - "[$name]" - ); - } - - if ($value > 0) { - $passwordTTL = $fieldSettings[self::PASSWORD_TTL_SETTING] ?? null; - if ($value >= (int)$passwordTTL) { - return new ValidationError( - 'Password expiration warning value should be lower then password expiration value', - null, - [], - "[$name]" - ); - } - } - } - - return null; - } - - private function isNewPasswordRequired(FieldDefinition $fieldDefinition): bool - { - $isExplicitRequired = $fieldDefinition->validatorConfiguration['PasswordValueValidator']['requireNewPassword'] ?? false; - if ($isExplicitRequired) { - return true; - } - - return $this->isPasswordTTLEnabled($fieldDefinition); - } - - private function isPasswordTTLEnabled(FieldDefinition $fieldDefinition): bool - { - return ($fieldDefinition->fieldSettings[self::PASSWORD_TTL_SETTING] ?? null) > 0; - } -} diff --git a/eZ/Publish/Core/FieldType/User/UserStorage.php b/eZ/Publish/Core/FieldType/User/UserStorage.php deleted file mode 100644 index da289e4c23..0000000000 --- a/eZ/Publish/Core/FieldType/User/UserStorage.php +++ /dev/null @@ -1,123 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\User; - -use eZ\Publish\SPI\FieldType\GatewayBasedStorage; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Description of UserStorage. - * - * Methods in this interface are called by storage engine. - * Proper Gateway and its Connection is injected via Dependency Injection. - * - * The User storage handles the following attributes, following the user field - * type in eZ Publish 4: - * - account_key - * - has_stored_login - * - is_enabled - * - is_locked - * - last_visit - * - login_count - */ -class UserStorage extends GatewayBasedStorage -{ - /** - * Field Type External Storage Gateway. - * - * @var \eZ\Publish\Core\FieldType\User\UserStorage\Gateway - */ - protected $gateway; - - /** - * Allows custom field types to store data in an external source (e.g. another DB table). - * - * Stores value for $field in an external data source. - * The whole {@link eZ\Publish\SPI\Persistence\Content\Field} object is passed and its value - * is accessible through the {@link eZ\Publish\SPI\Persistence\Content\FieldValue} 'value' property. - * This value holds the data filled by the user as a {@link eZ\Publish\Core\FieldType\Value} based object, - * according to the field type (e.g. for TextLine, it will be a {@link eZ\Publish\Core\FieldType\TextLine\Value} object). - * - * $field->id = unique ID from the attribute tables (needs to be generated by - * database back end on create, before the external data source may be - * called from storing). - * - * Database connection handler is injected into the Gateway via Dependency Injection. - * - * This method might return true if $field needs to be updated after storage done here (to store a PK for instance). - * In any other case, this method must not return anything (null). - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context - * - * @return true|null - */ - public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context) - { - return $this->gateway->storeFieldData($versionInfo, $field); - } - - /** - * Populates $field value property based on the external data. - * $field->value is a {@link eZ\Publish\SPI\Persistence\Content\FieldValue} object. - * This value holds the data as a {@link eZ\Publish\Core\FieldType\Value} based object, - * according to the field type (e.g. for TextLine, it will be a {@link eZ\Publish\Core\FieldType\TextLine\Value} object). - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context - */ - public function getFieldData(VersionInfo $versionInfo, Field $field, array $context) - { - $field->value->externalData = $this->gateway->getFieldData($field->id); - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param int[] $fieldIds Array of field Ids - * @param array $context - * - * @return bool - * - * @throws \Doctrine\DBAL\DBALException - */ - public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context) - { - return $this->gateway->deleteFieldData($versionInfo, $fieldIds); - } - - /** - * Checks if field type has external data to deal with. - * - * @return bool - */ - public function hasFieldData() - { - return true; - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(VersionInfo $versionInfo, Field $field, array $context) - { - } - - /** - * @param int[] $supportedHashTypes - */ - public function countUsersWithUnsupportedHashType(array $supportedHashTypes): int - { - return $this->gateway->countUsersWithUnsupportedHashType($supportedHashTypes); - } -} diff --git a/eZ/Publish/Core/FieldType/User/UserStorage/Gateway.php b/eZ/Publish/Core/FieldType/User/UserStorage/Gateway.php deleted file mode 100644 index cf85c4d2e2..0000000000 --- a/eZ/Publish/Core/FieldType/User/UserStorage/Gateway.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\User\UserStorage; - -use eZ\Publish\SPI\FieldType\StorageGateway; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -abstract class Gateway extends StorageGateway -{ - /** - * Get field data. - * - * The User storage handles the following attributes, following the user field - * type in eZ Publish 4: - * - account_key - * - has_stored_login - * - contentobject_id - * - login - * - email - * - password_hash - * - password_hash_type - * - password_updated_at - * - is_enabled - * - is_locked - * - last_visit - * - login_count - * - max_login - * - * @param mixed $fieldId - * @param mixed $userId - * - * @return array - */ - abstract public function getFieldData($fieldId, $userId = null); - - abstract public function storeFieldData(VersionInfo $versionInfo, Field $field): bool; - - /** - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param int[] $fieldIds - * - * @return bool - * - * @throws \Doctrine\DBAL\DBALException - */ - abstract public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds): bool; - - /** - * @param int[] $supportedHashTypes - */ - abstract public function countUsersWithUnsupportedHashType(array $supportedHashTypes): int; -} diff --git a/eZ/Publish/Core/FieldType/User/UserStorage/Gateway/DoctrineStorage.php b/eZ/Publish/Core/FieldType/User/UserStorage/Gateway/DoctrineStorage.php deleted file mode 100644 index bece6df1cb..0000000000 --- a/eZ/Publish/Core/FieldType/User/UserStorage/Gateway/DoctrineStorage.php +++ /dev/null @@ -1,442 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\User\UserStorage\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Exception\UniqueConstraintViolationException; -use Doctrine\DBAL\ParameterType; -use eZ\Publish\Core\Base\Exceptions\ForbiddenException; -use eZ\Publish\Core\FieldType\User\UserStorage\Gateway; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use PDO; - -/** - * User DoctrineStorage gateway. - */ -class DoctrineStorage extends Gateway -{ - public const USER_TABLE = 'ezuser'; - public const USER_SETTING_TABLE = 'ezuser_setting'; - - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - /** - * Default values for user fields. - * - * @var array - */ - protected $defaultValues = [ - 'hasStoredLogin' => false, - 'contentId' => null, - 'login' => null, - 'email' => null, - 'passwordHash' => null, - 'passwordHashType' => null, - 'passwordUpdatedAt' => null, - 'enabled' => false, - 'maxLogin' => null, - ]; - - public function __construct( - Connection $connection - ) { - $this->connection = $connection; - } - - /** - * {@inheritdoc} - */ - public function getFieldData($fieldId, $userId = null) - { - $userId = $userId ?: $this->fetchUserId($fieldId); - $userData = $this->fetchUserData($userId); - - if (!isset($userData['login'])) { - return $this->defaultValues; - } - - $result = array_merge( - $this->defaultValues, - [ - 'hasStoredLogin' => true, - ], - $userData, - $this->fetchUserSettings($userId) - ); - - return $result; - } - - /** - * Map legacy database column names to property names. - * - * @return array - */ - protected function getPropertyMap() - { - return [ - 'has_stored_login' => [ - 'name' => 'hasStoredlogin', - 'cast' => static function ($input) { - return $input == '1'; - }, - ], - 'contentobject_id' => [ - 'name' => 'contentId', - 'cast' => 'intval', - ], - 'login' => [ - 'name' => 'login', - 'cast' => 'strval', - ], - 'email' => [ - 'name' => 'email', - 'cast' => 'strval', - ], - 'password_hash' => [ - 'name' => 'passwordHash', - 'cast' => 'strval', - ], - 'password_hash_type' => [ - 'name' => 'passwordHashType', - 'cast' => 'strval', - ], - 'password_updated_at' => [ - 'name' => 'passwordUpdatedAt', - 'cast' => static function ($timestamp) { - return $timestamp ? (int)$timestamp : null; - }, - ], - 'is_enabled' => [ - 'name' => 'enabled', - 'cast' => static function ($input) { - return $input == '1'; - }, - ], - 'max_login' => [ - 'name' => 'maxLogin', - 'cast' => 'intval', - ], - ]; - } - - /** - * Convert the given database values to properties. - * - * @param array $databaseValues - * - * @return array - */ - protected function convertColumnsToProperties(array $databaseValues) - { - $propertyValues = []; - $propertyMap = $this->getPropertyMap(); - - foreach ($databaseValues as $columnName => $value) { - $conversionFunction = $propertyMap[$columnName]['cast']; - - $propertyValues[$propertyMap[$columnName]['name']] = $conversionFunction($value); - } - - return $propertyValues; - } - - /** - * Fetch user content object id for the given field id. - * - * @param int $fieldId - * - * @return int - */ - protected function fetchUserId($fieldId) - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - $this->connection->quoteIdentifier('contentobject_id') - ) - ->from($this->connection->quoteIdentifier('ezcontentobject_attribute')) - ->where( - $query->expr()->eq( - $this->connection->quoteIdentifier('id'), - ':fieldId' - ) - ) - ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) - ; - - $statement = $query->execute(); - - return (int) $statement->fetchColumn(); - } - - /** - * Fetch user data. - * - * @param int $userId - * - * @return array - */ - protected function fetchUserData($userId) - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - $this->connection->quoteIdentifier('usr.contentobject_id'), - $this->connection->quoteIdentifier('usr.login'), - $this->connection->quoteIdentifier('usr.email'), - $this->connection->quoteIdentifier('usr.password_hash'), - $this->connection->quoteIdentifier('usr.password_hash_type'), - $this->connection->quoteIdentifier('usr.password_updated_at') - ) - ->from($this->connection->quoteIdentifier(self::USER_TABLE), 'usr') - ->where( - $query->expr()->eq( - $this->connection->quoteIdentifier('usr.contentobject_id'), - ':userId' - ) - ) - ->setParameter(':userId', $userId, PDO::PARAM_INT) - ; - - $statement = $query->execute(); - - $rows = $statement->fetchAll(PDO::FETCH_ASSOC); - - return isset($rows[0]) ? $this->convertColumnsToProperties($rows[0]) : []; - } - - /** - * Fetch user settings. - * - * @param int $userId - * - * @return array - */ - protected function fetchUserSettings($userId) - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - $this->connection->quoteIdentifier('s.is_enabled'), - $this->connection->quoteIdentifier('s.max_login') - ) - ->from($this->connection->quoteIdentifier(self::USER_SETTING_TABLE), 's') - ->where( - $query->expr()->eq( - $this->connection->quoteIdentifier('s.user_id'), - ':userId' - ) - ) - ->setParameter(':userId', $userId, PDO::PARAM_INT) - ; - - $statement = $query->execute(); - - $rows = $statement->fetchAll(PDO::FETCH_ASSOC); - - return isset($rows[0]) ? $this->convertColumnsToProperties($rows[0]) : []; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - */ - public function storeFieldData(VersionInfo $versionInfo, Field $field): bool - { - if ($field->value->externalData === null) { - //to avoid unnecessary modifications when provided field is empty (like missing data for languageCode) - return false; - } - - try { - if (!empty($this->fetchUserData($versionInfo->contentInfo->id))) { - $this->updateFieldData($versionInfo, $field); - } else { - $this->insertFieldData($versionInfo, $field); - } - } catch (UniqueConstraintViolationException $e) { - throw new ForbiddenException( - 'User "%login%" already exists', - [ - '%login%' => $field->value->externalData['login'] ?? '?', - ] - ); - } - - return true; - } - - protected function insertFieldData(VersionInfo $versionInfo, Field $field): void - { - $insertQuery = $this->connection->createQueryBuilder(); - - $insertQuery - ->insert($this->connection->quoteIdentifier(self::USER_TABLE)) - ->setValue('contentobject_id', ':userId') - ->setValue('login', ':login') - ->setValue('email', ':email') - ->setValue('password_hash', ':passwordHash') - ->setValue('password_hash_type', ':passwordHashType') - ->setValue('password_updated_at', ':passwordUpdatedAt') - ->setParameter(':userId', $versionInfo->contentInfo->id, ParameterType::INTEGER) - ->setParameter(':login', $field->value->externalData['login'], ParameterType::STRING) - ->setParameter(':email', $field->value->externalData['email'], ParameterType::STRING) - ->setParameter(':passwordHash', $field->value->externalData['passwordHash'], ParameterType::STRING) - ->setParameter(':passwordHashType', $field->value->externalData['passwordHashType'], ParameterType::INTEGER) - ->setParameter(':passwordUpdatedAt', $field->value->externalData['passwordUpdatedAt']) - ; - - $insertQuery->execute(); - - $settingsQuery = $this->connection->createQueryBuilder(); - - $settingsQuery - ->insert($this->connection->quoteIdentifier(self::USER_SETTING_TABLE)) - ->setValue('user_id', ':userId') - ->setValue('is_enabled', ':isEnabled') - ->setValue('max_login', ':maxLogin') - ->setParameter(':userId', $versionInfo->contentInfo->id, ParameterType::INTEGER) - ->setParameter(':isEnabled', $field->value->externalData['enabled'], ParameterType::INTEGER) - ->setParameter(':maxLogin', $field->value->externalData['maxLogin'], ParameterType::INTEGER); - - $settingsQuery->execute(); - } - - protected function updateFieldData(VersionInfo $versionInfo, Field $field): void - { - $queryBuilder = $this->connection->createQueryBuilder(); - - $queryBuilder - ->update($this->connection->quoteIdentifier(self::USER_TABLE)) - ->set('login', ':login') - ->set('email', ':email') - ->set('password_hash', ':passwordHash') - ->set('password_hash_type', ':passwordHashType') - ->set('password_updated_at', ':passwordUpdatedAt') - ->setParameter(':login', $field->value->externalData['login'], ParameterType::STRING) - ->setParameter(':email', $field->value->externalData['email'], ParameterType::STRING) - ->setParameter(':passwordHash', $field->value->externalData['passwordHash'], ParameterType::STRING) - ->setParameter(':passwordHashType', $field->value->externalData['passwordHashType'], ParameterType::INTEGER) - ->setParameter(':passwordUpdatedAt', $field->value->externalData['passwordUpdatedAt']) - ->where( - $queryBuilder->expr()->eq( - $this->connection->quoteIdentifier('contentobject_id'), - ':userId' - ) - ) - ->setParameter(':userId', $versionInfo->contentInfo->id, ParameterType::INTEGER) - ; - - $queryBuilder->execute(); - - $settingsQuery = $this->connection->createQueryBuilder(); - - $settingsQuery - ->update($this->connection->quoteIdentifier(self::USER_SETTING_TABLE)) - ->set('is_enabled', ':isEnabled') - ->set('max_login', ':maxLogin') - ->setParameter(':isEnabled', $field->value->externalData['enabled'], ParameterType::INTEGER) - ->setParameter(':maxLogin', $field->value->externalData['maxLogin'], ParameterType::INTEGER) - ->where( - $queryBuilder->expr()->eq( - $this->connection->quoteIdentifier('user_id'), - ':userId' - ) - ) - ->setParameter(':userId', $versionInfo->contentInfo->id, ParameterType::INTEGER); - - $settingsQuery->execute(); - } - - public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds): bool - { - // Delete external storage only, when when deleting last relation to fieldType - // to avoid removing it when deleting draft, translation or by exceeding archive limit - if (!$this->isLastRelationToFieldType($fieldIds)) { - return false; - } - - $query = $this->connection->createQueryBuilder(); - $query - ->delete($this->connection->quoteIdentifier(self::USER_SETTING_TABLE)) - ->where( - $query->expr()->eq( - $this->connection->quoteIdentifier('user_id'), - ':userId' - ) - ) - ->setParameter(':userId', $versionInfo->contentInfo->id, ParameterType::INTEGER); - - $query->execute(); - - $query = $this->connection->createQueryBuilder(); - $query - ->delete($this->connection->quoteIdentifier(self::USER_TABLE)) - ->where( - $query->expr()->eq( - $this->connection->quoteIdentifier('contentobject_id'), - ':userId' - ) - ) - ->setParameter(':userId', $versionInfo->contentInfo->id, ParameterType::INTEGER); - - $query->execute(); - - return true; - } - - /** - * @param int[] $fieldIds - * - * @return bool - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function isLastRelationToFieldType(array $fieldIds): bool - { - $countExpr = $this->connection->getDatabasePlatform()->getCountExpression('id'); - - $checkQuery = $this->connection->createQueryBuilder(); - $checkQuery - ->select($countExpr) - ->from('ezcontentobject_attribute') - ->where( - $checkQuery->expr()->in( - $this->connection->quoteIdentifier('id'), - ':fieldIds' - ) - ) - ->setParameter(':fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY) - ->groupBy('id') - ->having($countExpr . ' > 1'); - - $numRows = (int)$checkQuery->execute()->fetchColumn(); - - return $numRows === 0; - } - - public function countUsersWithUnsupportedHashType(array $supportedHashTypes): int - { - $selectQuery = $this->connection->createQueryBuilder(); - - $selectQuery - ->select( - $this->connection->getDatabasePlatform()->getCountExpression('u.login') - ) - ->from(self::USER_TABLE, 'u') - ->andWhere( - $selectQuery->expr()->notIn('u.password_hash_type', ':supportedPasswordHashes') - ) - ->setParameter('supportedPasswordHashes', $supportedHashTypes, Connection::PARAM_INT_ARRAY); - - return $selectQuery - ->execute() - ->fetchColumn(); - } -} diff --git a/eZ/Publish/Core/FieldType/User/Value.php b/eZ/Publish/Core/FieldType/User/Value.php deleted file mode 100644 index 55ea6d5d56..0000000000 --- a/eZ/Publish/Core/FieldType/User/Value.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\User; - -use eZ\Publish\Core\FieldType\Value as BaseValue; - -/** - * Value for User field type. - */ -class Value extends BaseValue -{ - /** - * Has stored login. - * - * @var bool - */ - public $hasStoredLogin; - - /** - * Contentobject id. - * - * @var mixed - */ - public $contentId; - - /** - * Login. - * - * @var string - */ - public $login; - - /** - * Email. - * - * @var string - */ - public $email; - - /** - * Password hash. - * - * @var string - */ - public $passwordHash; - - /** - * Password hash type. - * - * @var mixed - */ - public $passwordHashType; - - /** - * @var \DateTimeImmutable|null - */ - public $passwordUpdatedAt; - - /** - * Is enabled. - * - * @var bool - */ - public $enabled; - - /** - * Max login. - * - * @var int - */ - public $maxLogin; - - /** - * @var string Write only property, takes a plain password for use when creating user or updating password. - */ - public $plainPassword; - - /** - * @see \eZ\Publish\Core\FieldType\Value - */ - public function __toString() - { - return (string)$this->login; - } -} diff --git a/eZ/Publish/Core/FieldType/ValidationError.php b/eZ/Publish/Core/FieldType/ValidationError.php deleted file mode 100644 index 8b9d637cf8..0000000000 --- a/eZ/Publish/Core/FieldType/ValidationError.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType; - -use eZ\Publish\API\Repository\Values\Translation\Message; -use eZ\Publish\API\Repository\Values\Translation\Plural; -use eZ\Publish\SPI\FieldType\ValidationError as ValidationErrorInterface; - -/** - * Class for validation errors. - */ -class ValidationError implements ValidationErrorInterface -{ - /** @var string */ - protected $singular; - - /** @var string */ - protected $plural; - - /** @var array */ - protected $values; - - /** - * Element on which the error occurred - * e.g. property name or property path compatible with Symfony PropertyAccess component. - * - * Example: StringLengthValidator[minStringLength] - * - * @var string - */ - protected $target; - - /** - * @param string $singular - * @param string $plural - * @param array $values - */ - public function __construct($singular, $plural = null, array $values = [], $target = null) - { - $this->singular = $singular; - $this->plural = $plural; - $this->values = $values; - $this->target = $target; - } - - /** - * Returns a translatable Message. - * - * @return \eZ\Publish\API\Repository\Values\Translation - */ - public function getTranslatableMessage() - { - if (isset($this->plural)) { - return new Plural( - $this->singular, - $this->plural, - $this->values - ); - } else { - return new Message( - $this->singular, - $this->values - ); - } - } - - public function setTarget($target) - { - $this->target = $target; - } - - public function getTarget() - { - return $this->target; - } -} diff --git a/eZ/Publish/Core/FieldType/Validator/FileExtensionBlackListValidator.php b/eZ/Publish/Core/FieldType/Validator/FileExtensionBlackListValidator.php deleted file mode 100644 index f2dc6e7348..0000000000 --- a/eZ/Publish/Core/FieldType/Validator/FileExtensionBlackListValidator.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Validator; - -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\Value as BaseValue; -use eZ\Publish\Core\MVC\ConfigResolverInterface; - -class FileExtensionBlackListValidator extends Validator -{ - protected $constraints = [ - 'extensionsBlackList' => [], - ]; - - protected $constraintsSchema = [ - 'extensionsBlackList' => [ - 'type' => 'array', - 'default' => [], - ], - ]; - - /** - * @param \eZ\Publish\Core\MVC\ConfigResolverInterface $configResolver - */ - public function __construct(ConfigResolverInterface $configResolver) - { - $this->constraints['extensionsBlackList'] = $configResolver->getParameter( - 'io.file_storage.file_type_blacklist' - ); - } - - /** - * {@inheritdoc} - */ - public function validateConstraints($constraints) - { - return []; - } - - /** - * {@inheritdoc} - */ - public function validate(BaseValue $value) - { - $this->errors = []; - - $this->validateFileExtension($value->fileName); - - return empty($this->errors); - } - - public function validateFileExtension(string $fileName): void - { - if ( - pathinfo($fileName, PATHINFO_BASENAME) !== $fileName - || in_array( - strtolower(pathinfo($fileName, PATHINFO_EXTENSION)), - $this->constraints['extensionsBlackList'], - true - ) - ) { - $this->errors[] = new ValidationError( - 'A valid file is required. The following file extensions are not allowed: %extensionsBlackList%', - null, - [ - '%extensionsBlackList%' => implode(', ', $this->constraints['extensionsBlackList']), - ], - 'fileExtensionBlackList' - ); - } - } - - /** - * @return array<\eZ\Publish\SPI\FieldType\ValidationError> - */ - public function getErrors(): array - { - return $this->errors; - } -} diff --git a/eZ/Publish/Core/FieldType/Validator/ImageValidator.php b/eZ/Publish/Core/FieldType/Validator/ImageValidator.php deleted file mode 100644 index edaaca518a..0000000000 --- a/eZ/Publish/Core/FieldType/Validator/ImageValidator.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType\Validator; - -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\Value; - -class ImageValidator extends Validator -{ - /** - * {@inheritdoc} - */ - public function validateConstraints($constraints) - { - return []; - } - - /** - * {@inheritdoc} - */ - public function validate(Value $value) - { - $isValid = true; - if (isset($value->inputUri) && !$this->innerValidate($value->inputUri)) { - $isValid = false; - } - - // BC: Check if file is a valid image if the value of 'id' matches a local file - if (isset($value->id) && file_exists($value->id) && !$this->innerValidate($value->id)) { - $isValid = false; - } - - return $isValid; - } - - private function innerValidate($filePath) - { - // silence `getimagesize` error as extension-wise valid image files might produce it anyway - // note that file extension checking is done using other validation which should be called before this one - if (!@getimagesize($filePath)) { - $this->errors[] = new ValidationError( - 'A valid image file is required.', - null, - [], - 'id' - ); - - return false; - } - - return true; - } -} diff --git a/eZ/Publish/Core/FieldType/Value.php b/eZ/Publish/Core/FieldType/Value.php deleted file mode 100644 index 7562bbd0e3..0000000000 --- a/eZ/Publish/Core/FieldType/Value.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\FieldType; - -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\SPI\FieldType\Value as ValueInterface; - -/** - * Abstract class for all field value classes. - * A field value object is to be understood with associated field type. - */ -abstract class Value extends ValueObject implements ValueInterface -{ - /** - * Returns a string representation of the field value. - * - * @return string - */ - abstract public function __toString(); -} diff --git a/eZ/Publish/Core/Helper/ContentInfoLocationLoader.php b/eZ/Publish/Core/Helper/ContentInfoLocationLoader.php deleted file mode 100644 index a1f9479430..0000000000 --- a/eZ/Publish/Core/Helper/ContentInfoLocationLoader.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Helper; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; - -/** - * Loads a location based on a ContentInfo. - */ -interface ContentInfoLocationLoader -{ - /** - * Loads a location from a ContentInfo. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return \eZ\Publish\API\Repository\Values\Content\Location - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the location doesn't have a contentId. - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the location failed to load. - */ - public function loadLocation(ContentInfo $contentInfo); -} diff --git a/eZ/Publish/Core/Helper/ContentInfoLocationLoader/SudoMainLocationLoader.php b/eZ/Publish/Core/Helper/ContentInfoLocationLoader/SudoMainLocationLoader.php deleted file mode 100644 index 9f18779eaf..0000000000 --- a/eZ/Publish/Core/Helper/ContentInfoLocationLoader/SudoMainLocationLoader.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Helper\ContentInfoLocationLoader; - -use Exception; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Helper\ContentInfoLocationLoader; - -/** - * Loads the main location of a given ContentInfo using sudo(). - */ -class SudoMainLocationLoader implements ContentInfoLocationLoader -{ - /** @var \eZ\Publish\API\Repository\Repository|\eZ\Publish\Core\Repository\Repository */ - private $repository; - - public function __construct(Repository $repository) - { - $this->repository = $repository; - } - - public function loadLocation(ContentInfo $contentInfo) - { - if (null === $contentInfo->mainLocationId) { - throw new NotFoundException('main location of content', $contentInfo->id); - } - - try { - return $this->repository->sudo( - static function (Repository $repository) use ($contentInfo) { - return $repository->getLocationService()->loadLocation($contentInfo->mainLocationId); - } - ); - } catch (Exception $e) { - throw new NotFoundException('main location of content', $contentInfo->id); - } - } -} diff --git a/eZ/Publish/Core/Helper/ContentPreviewHelper.php b/eZ/Publish/Core/Helper/ContentPreviewHelper.php deleted file mode 100644 index 7ee050bcb7..0000000000 --- a/eZ/Publish/Core/Helper/ContentPreviewHelper.php +++ /dev/null @@ -1,134 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Helper; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Event\ScopeChangeEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessAware; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; - -class ContentPreviewHelper implements SiteAccessAware -{ - /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface */ - protected $siteAccessRouter; - - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ - protected $originalSiteAccess; - - /** @var bool */ - private $previewActive = false; - - /** @var \eZ\Publish\API\Repository\Values\Content\Content */ - private $previewedContent; - - /** @var \eZ\Publish\API\Repository\Values\Content\Location */ - private $previewedLocation; - - public function __construct(EventDispatcherInterface $eventDispatcher, SiteAccessRouterInterface $siteAccessRouter) - { - $this->eventDispatcher = $eventDispatcher; - $this->siteAccessRouter = $siteAccessRouter; - } - - public function setSiteAccess(SiteAccess $siteAccess = null) - { - $this->originalSiteAccess = $siteAccess; - } - - /** - * Return original SiteAccess. - * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess - */ - public function getOriginalSiteAccess() - { - return $this->originalSiteAccess; - } - - /** - * Switches configuration scope to $siteAccessName and returns the new SiteAccess to use for preview. - * - * @param string $siteAccessName - * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess - */ - public function changeConfigScope($siteAccessName) - { - $event = new ScopeChangeEvent($this->siteAccessRouter->matchByName($siteAccessName)); - $this->eventDispatcher->dispatch($event, MVCEvents::CONFIG_SCOPE_CHANGE); - - return $event->getSiteAccess(); - } - - /** - * Restores original config scope. - * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess - */ - public function restoreConfigScope() - { - $event = new ScopeChangeEvent($this->originalSiteAccess); - $this->eventDispatcher->dispatch($event, MVCEvents::CONFIG_SCOPE_RESTORE); - - return $event->getSiteAccess(); - } - - /** - * @return bool - */ - public function isPreviewActive() - { - return $this->previewActive; - } - - /** - * @param bool $previewActive - */ - public function setPreviewActive($previewActive) - { - $this->previewActive = (bool)$previewActive; - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function getPreviewedContent() - { - return $this->previewedContent; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Content $previewedContent - */ - public function setPreviewedContent(Content $previewedContent) - { - $this->previewedContent = $previewedContent; - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Location - */ - public function getPreviewedLocation() - { - return $this->previewedLocation; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Location $previewedLocation - */ - public function setPreviewedLocation(Location $previewedLocation) - { - $this->previewedLocation = $previewedLocation; - } -} diff --git a/eZ/Publish/Core/Helper/FieldHelper.php b/eZ/Publish/Core/Helper/FieldHelper.php deleted file mode 100644 index 421ab04788..0000000000 --- a/eZ/Publish/Core/Helper/FieldHelper.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Helper; - -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\FieldTypeService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; - -class FieldHelper -{ - /** @var \eZ\Publish\API\Repository\ContentTypeService */ - private $contentTypeService; - - /** @var \eZ\Publish\API\Repository\FieldTypeService */ - private $fieldTypeService; - - /** @var TranslationHelper */ - private $translationHelper; - - public function __construct(TranslationHelper $translationHelper, ContentTypeService $contentTypeService, FieldTypeService $fieldTypeService) - { - $this->fieldTypeService = $fieldTypeService; - $this->contentTypeService = $contentTypeService; - $this->translationHelper = $translationHelper; - } - - /** - * Checks if provided field can be considered empty. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param string $fieldDefIdentifier - * @param null $forcedLanguage - * - * @return bool - */ - public function isFieldEmpty(Content $content, $fieldDefIdentifier, $forcedLanguage = null) - { - $field = $this->translationHelper->getTranslatedField($content, $fieldDefIdentifier, $forcedLanguage); - $fieldDefinition = $content->getContentType()->getFieldDefinition($fieldDefIdentifier); - - return $this - ->fieldTypeService - ->getFieldType($fieldDefinition->fieldTypeIdentifier) - ->isEmptyValue($field->value); - } - - /** - * Returns FieldDefinition object based on $contentInfo and $fieldDefIdentifier. - * - * @deprecated If you have Content you can instead do: $content->getContentType()->getFieldDefinition($identifier) - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param string $fieldDefIdentifier - * - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition - */ - public function getFieldDefinition(ContentInfo $contentInfo, $fieldDefIdentifier) - { - return $this - ->contentTypeService - ->loadContentType($contentInfo->contentTypeId) - ->getFieldDefinition($fieldDefIdentifier); - } -} diff --git a/eZ/Publish/Core/Helper/FieldsGroups/RepositoryConfigFieldsGroupsListFactory.php b/eZ/Publish/Core/Helper/FieldsGroups/RepositoryConfigFieldsGroupsListFactory.php deleted file mode 100644 index 96774420c9..0000000000 --- a/eZ/Publish/Core/Helper/FieldsGroups/RepositoryConfigFieldsGroupsListFactory.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Helper\FieldsGroups; - -use eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider; -use Symfony\Contracts\Translation\TranslatorInterface; - -/** - * Builds a SettingsFieldGroupsList. - */ -final class RepositoryConfigFieldsGroupsListFactory -{ - /** @var \eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider */ - private $configProvider; - - public function __construct(RepositoryConfigurationProvider $configProvider) - { - $this->configProvider = $configProvider; - } - - public function build(TranslatorInterface $translator) - { - $repositoryConfig = $this->configProvider->getRepositoryConfig(); - - return new ArrayTranslatorFieldsGroupsList( - $translator, - $repositoryConfig['fields_groups']['default'], - $repositoryConfig['fields_groups']['list'] - ); - } -} diff --git a/eZ/Publish/Core/Helper/PreviewLocationProvider.php b/eZ/Publish/Core/Helper/PreviewLocationProvider.php deleted file mode 100644 index 1d970140fc..0000000000 --- a/eZ/Publish/Core/Helper/PreviewLocationProvider.php +++ /dev/null @@ -1,115 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Helper; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as PersistenceLocationHandler; - -/** - * Provides location(s) for a content. Handles unpublished content that does not have an actual location yet. - */ -class PreviewLocationProvider -{ - /** @var \eZ\Publish\API\Repository\LocationService */ - private $locationService; - - /** @var \eZ\Publish\API\Repository\ContentService */ - private $contentService; - - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Handler */ - private $locationHandler; - - /** - * @param \eZ\Publish\API\Repository\LocationService $locationService - * @param \eZ\Publish\API\Repository\ContentService $contentService - * @param \eZ\Publish\SPI\Persistence\Content\Location\Handler $locationHandler - */ - public function __construct( - LocationService $locationService, - ContentService $contentService, - PersistenceLocationHandler $locationHandler - ) { - $this->locationService = $locationService; - $this->contentService = $contentService; - $this->locationHandler = $locationHandler; - } - - /** - * Loads the main location for $contentId. - * - * If the content does not have a location (yet), but has a Location draft, it is returned instead. - * Location drafts do not have an id (it is set to null), and can be tested using the isDraft() method. - * - * If the content doesn't have a location nor a location draft, null is returned. - * - * @deprecated Since 7.5.4, rather use loadMainLocationByContent. - * @see loadMainLocationByContent - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * - * @param mixed $contentId - * - * @return \eZ\Publish\API\Repository\Values\Content\Location|null - */ - public function loadMainLocation($contentId) - { - return $this->loadMainLocationByContent( - $this->contentService->loadContent($contentId) - ); - } - - /** - * Loads the main location for $content. - * - * If the content does not have a location (yet), but has a Location draft, it is returned instead. - * Location drafts do not have an id (it is set to null), and can be tested using the isDraft() method. - * - * If the content doesn't have a location nor a location draft, null is returned. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @return \eZ\Publish\API\Repository\Values\Content\Location|null - */ - public function loadMainLocationByContent(APIContent $content): ?APILocation - { - $location = null; - $contentInfo = $content - ->getVersionInfo() - ->getContentInfo(); - - // mainLocationId already exists, content has been published at least once. - if ($contentInfo->mainLocationId) { - $location = $this->locationService->loadLocation($contentInfo->mainLocationId); - } elseif (!$contentInfo->published) { - // New Content, never published, create a virtual location object. - // In cases content is missing locations this will return empty array - $parentLocations = $this->locationHandler->loadParentLocationsForDraftContent($contentInfo->id); - if (empty($parentLocations)) { - return null; - } - - // NOTE: Once Repository adds support for draft locations (and draft location ops), then this can be removed - $location = new Location( - [ - 'content' => $content, - 'contentInfo' => $contentInfo, - 'status' => Location::STATUS_DRAFT, - 'parentLocationId' => $parentLocations[0]->id, - 'depth' => $parentLocations[0]->depth + 1, - 'pathString' => $parentLocations[0]->pathString . 'x/', - ] - ); - } - - return $location; - } -} diff --git a/eZ/Publish/Core/IO/Adapter/LocalAdapter.php b/eZ/Publish/Core/IO/Adapter/LocalAdapter.php deleted file mode 100644 index 022f31e774..0000000000 --- a/eZ/Publish/Core/IO/Adapter/LocalAdapter.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\IO\Adapter; - -use eZ\Publish\Core\IO\IOConfigProvider; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Event\ScopeChangeEvent; -use eZ\Publish\SPI\MVC\EventSubscriber\ConfigScopeChangeSubscriber; -use League\Flysystem\Adapter\Local; -use LogicException; - -/** - * @internal - */ -final class LocalAdapter extends Local implements ConfigScopeChangeSubscriber -{ - /** @var \eZ\Publish\Core\IO\IOConfigProvider */ - private $ioConfigProvider; - - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ - private $configResolver; - - public function __construct(IOConfigProvider $ioConfigProvider, ConfigResolverInterface $configResolver) - { - $this->ioConfigProvider = $ioConfigProvider; - $this->configResolver = $configResolver; - - $filesPermissions = $this->configResolver->getParameter('io.permissions.files'); - $directoriesPermissions = $this->configResolver->getParameter('io.permissions.directories'); - - parent::__construct( - $this->ioConfigProvider->getRootDir(), - LOCK_EX, - Local::DISALLOW_LINKS, - ['file' => ['public' => $filesPermissions], 'dir' => ['public' => $directoriesPermissions]] - ); - } - - /** - * Reconfigure Adapter due to SiteAccess change which implies - * root dir and permissions could be different for new SiteAccess. - */ - public function onConfigScopeChange(ScopeChangeEvent $event): void - { - $root = $this->ioConfigProvider->getRootDir(); - $root = is_link($root) ? realpath($root) : $root; - $this->ensureDirectory($root); - - if (!is_dir($root) || !is_readable($root)) { - throw new LogicException(sprintf('The root path %s is not readable.', $root)); - } - - $this->setPathPrefix($root); - - $filesPermissions = $this->configResolver->getParameter('io.permissions.files'); - $directoriesPermissions = $this->configResolver->getParameter('io.permissions.directories'); - - $this->permissionMap = array_replace_recursive( - static::$permissions, - ['file' => ['public' => $filesPermissions], 'dir' => ['public' => $directoriesPermissions]] - ); - } -} diff --git a/eZ/Publish/Core/IO/Exception/BinaryFileNotFoundException.php b/eZ/Publish/Core/IO/Exception/BinaryFileNotFoundException.php deleted file mode 100644 index 65f22e0829..0000000000 --- a/eZ/Publish/Core/IO/Exception/BinaryFileNotFoundException.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO\Exception; - -use Exception; -use eZ\Publish\Core\Base\Exceptions\NotFoundException as BaseNotFoundException; - -class BinaryFileNotFoundException extends BaseNotFoundException -{ - public function __construct($path, Exception $previous = null) - { - parent::__construct('BinaryFile', $path, $previous); - } -} diff --git a/eZ/Publish/Core/IO/Exception/InvalidBinaryFileIdException.php b/eZ/Publish/Core/IO/Exception/InvalidBinaryFileIdException.php deleted file mode 100644 index 680bf0be5e..0000000000 --- a/eZ/Publish/Core/IO/Exception/InvalidBinaryFileIdException.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO\Exception; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; - -class InvalidBinaryFileIdException extends InvalidArgumentValue -{ - public function __construct($id) - { - parent::__construct('BinaryFile::id', $id, 'BinaryFile'); - } -} diff --git a/eZ/Publish/Core/IO/FilePathNormalizer/Flysystem.php b/eZ/Publish/Core/IO/FilePathNormalizer/Flysystem.php deleted file mode 100644 index dff3ed6062..0000000000 --- a/eZ/Publish/Core/IO/FilePathNormalizer/Flysystem.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\IO\FilePathNormalizer; - -use eZ\Publish\Core\IO\FilePathNormalizerInterface; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; -use League\Flysystem\Util; - -final class Flysystem implements FilePathNormalizerInterface -{ - private const HASH_PATTERN = '/^[0-9a-f]{12}-/'; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter */ - private $slugConverter; - - public function __construct(SlugConverter $slugConverter) - { - $this->slugConverter = $slugConverter; - } - - public function normalizePath(string $filePath, bool $doHash = true): string - { - $fileName = pathinfo($filePath, PATHINFO_BASENAME); - $directory = pathinfo($filePath, PATHINFO_DIRNAME); - - $fileName = $this->slugConverter->convert($fileName, '_1', 'urlalias'); - - $hash = $doHash - ? (preg_match(self::HASH_PATTERN, $fileName) ? '' : bin2hex(random_bytes(6)) . '-') - : ''; - - $filePath = $directory . \DIRECTORY_SEPARATOR . $hash; - $normalizedFileName = Util::normalizePath($fileName); - - return $filePath . $normalizedFileName; - } -} diff --git a/eZ/Publish/Core/IO/FilePathNormalizerInterface.php b/eZ/Publish/Core/IO/FilePathNormalizerInterface.php deleted file mode 100644 index c1f2fcd0a7..0000000000 --- a/eZ/Publish/Core/IO/FilePathNormalizerInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\IO; - -/** - * @internal - */ -interface FilePathNormalizerInterface -{ - public function normalizePath(string $filePath, bool $doHash = true): string; -} diff --git a/eZ/Publish/Core/IO/IOBinarydataHandler/Flysystem.php b/eZ/Publish/Core/IO/IOBinarydataHandler/Flysystem.php deleted file mode 100644 index e0575b0b85..0000000000 --- a/eZ/Publish/Core/IO/IOBinarydataHandler/Flysystem.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO\IOBinarydataHandler; - -use eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException; -use eZ\Publish\Core\IO\IOBinarydataHandler; -use eZ\Publish\Core\IO\UrlDecorator; -use eZ\Publish\SPI\IO\BinaryFileCreateStruct; -use League\Flysystem\AdapterInterface; -use League\Flysystem\CorruptedPathDetected; -use League\Flysystem\FileExistsException; -use League\Flysystem\FileNotFoundException as FlysystemNotFoundException; -use League\Flysystem\FilesystemInterface; - -class Flysystem implements IOBinaryDataHandler -{ - /** @var \League\Flysystem\FilesystemInterface */ - private $filesystem; - - /** @var \eZ\Publish\Core\IO\UrlDecorator */ - private $urlDecorator; - - public function __construct(FilesystemInterface $filesystem, UrlDecorator $urlDecorator = null) - { - $this->filesystem = $filesystem; - $this->urlDecorator = $urlDecorator; - } - - public function create(BinaryFileCreateStruct $binaryFileCreateStruct) - { - try { - $this->filesystem->writeStream( - $binaryFileCreateStruct->id, - $binaryFileCreateStruct->getInputStream(), - [ - 'mimetype' => $binaryFileCreateStruct->mimeType, - 'visibility' => AdapterInterface::VISIBILITY_PUBLIC, - ] - ); - } catch (FileExistsException $e) { - $this->filesystem->updateStream( - $binaryFileCreateStruct->id, - $binaryFileCreateStruct->getInputStream(), - [ - 'mimetype' => $binaryFileCreateStruct->mimeType, - 'visibility' => AdapterInterface::VISIBILITY_PUBLIC, - ] - ); - } - } - - public function delete($spiBinaryFileId) - { - try { - $this->filesystem->delete($spiBinaryFileId); - } catch (FlysystemNotFoundException|CorruptedPathDetected $e) { - throw new BinaryFileNotFoundException($spiBinaryFileId, $e); - } - } - - public function getContents($spiBinaryFileId) - { - try { - return $this->filesystem->read($spiBinaryFileId); - } catch (FlysystemNotFoundException $e) { - throw new BinaryFileNotFoundException($spiBinaryFileId, $e); - } - } - - public function getResource($spiBinaryFileId) - { - try { - return $this->filesystem->readStream($spiBinaryFileId); - } catch (FlysystemNotFoundException $e) { - throw new BinaryFileNotFoundException($spiBinaryFileId, $e); - } - } - - public function getUri($spiBinaryFileId) - { - if (isset($this->urlDecorator)) { - return $this->urlDecorator->decorate($spiBinaryFileId); - } else { - return '/' . $spiBinaryFileId; - } - } - - public function getIdFromUri($binaryFileUri) - { - if (isset($this->urlDecorator)) { - return $this->urlDecorator->undecorate($binaryFileUri); - } else { - return ltrim($binaryFileUri, '/'); - } - } - - public function deleteDirectory($spiPath) - { - $this->filesystem->deleteDir($spiPath); - } -} diff --git a/eZ/Publish/Core/IO/IOMetadataHandler.php b/eZ/Publish/Core/IO/IOMetadataHandler.php deleted file mode 100644 index e33d69ab7c..0000000000 --- a/eZ/Publish/Core/IO/IOMetadataHandler.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO; - -use eZ\Publish\SPI\IO\BinaryFileCreateStruct; - -/** - * Provides reading & writing of files meta data (size, modification time...). - */ -interface IOMetadataHandler -{ - /** - * Stores the file from $binaryFileCreateStruct. - * - * @param \eZ\Publish\SPI\IO\BinaryFileCreateStruct $spiBinaryFileCreateStruct - * - * @return \eZ\Publish\SPI\IO\BinaryFile - * - * @throws \RuntimeException if an error occured creating the file - */ - public function create(BinaryFileCreateStruct $spiBinaryFileCreateStruct); - - /** - * Deletes file $spiBinaryFileId. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If $spiBinaryFileId is not found - * - * @param string $spiBinaryFileId - */ - public function delete($spiBinaryFileId); - - /** - * Loads and returns metadata for $spiBinaryFileId. - * - * @param string $spiBinaryFileId - * - * @return \eZ\Publish\SPI\IO\BinaryFile - */ - public function load($spiBinaryFileId); - - /** - * Checks if a file $spiBinaryFileId exists. - * - * @param string $spiBinaryFileId - * - * @return bool - */ - public function exists($spiBinaryFileId); - - /** - * Returns the file's mimetype. Example: text/plain. - * - * @param $spiBinaryFileId - * - * @return string - */ - public function getMimeType($spiBinaryFileId); - - public function deleteDirectory($spiPath); -} diff --git a/eZ/Publish/Core/IO/IOMetadataHandler/Flysystem.php b/eZ/Publish/Core/IO/IOMetadataHandler/Flysystem.php deleted file mode 100644 index 80c9af1790..0000000000 --- a/eZ/Publish/Core/IO/IOMetadataHandler/Flysystem.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO\IOMetadataHandler; - -use DateTime; -use eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException; -use eZ\Publish\Core\IO\IOMetadataHandler; -use eZ\Publish\SPI\IO\BinaryFile as SPIBinaryFile; -use eZ\Publish\SPI\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct; -use League\Flysystem\CorruptedPathDetected; -use League\Flysystem\FileNotFoundException; -use League\Flysystem\FilesystemInterface; - -class Flysystem implements IOMetadataHandler -{ - /** @var \League\Flysystem\FilesystemInterface */ - private $filesystem; - - public function __construct(FilesystemInterface $filesystem) - { - $this->filesystem = $filesystem; - } - - /** - * Only reads & return metadata, since the binarydata handler took care of creating the file already. - * - * @throws \eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException - */ - public function create(SPIBinaryFileCreateStruct $spiBinaryFileCreateStruct) - { - return $this->load($spiBinaryFileCreateStruct->id); - } - - /** - * Does really nothing, the binary data handler takes care of it. - * - * @param $spiBinaryFileId - */ - public function delete($spiBinaryFileId) - { - } - - public function load($spiBinaryFileId) - { - try { - $info = $this->filesystem->getMetadata($spiBinaryFileId); - } catch (FileNotFoundException|CorruptedPathDetected $e) { - throw new BinaryFileNotFoundException($spiBinaryFileId); - } - - $spiBinaryFile = new SPIBinaryFile(); - $spiBinaryFile->id = $spiBinaryFileId; - $spiBinaryFile->size = $info['size']; - - if (isset($info['timestamp'])) { - $spiBinaryFile->mtime = new DateTime('@' . $info['timestamp']); - } - - return $spiBinaryFile; - } - - public function exists($spiBinaryFileId) - { - try { - return $this->filesystem->has($spiBinaryFileId); - } catch (CorruptedPathDetected $e) { - return false; - } - } - - public function getMimeType($spiBinaryFileId) - { - return $this->filesystem->getMimetype($spiBinaryFileId); - } - - /** - * Does nothing, as the binarydata handler takes care of it. - */ - public function deleteDirectory($spiPath) - { - } -} diff --git a/eZ/Publish/Core/IO/IOMetadataHandler/LegacyDFSCluster.php b/eZ/Publish/Core/IO/IOMetadataHandler/LegacyDFSCluster.php deleted file mode 100644 index 36d361465a..0000000000 --- a/eZ/Publish/Core/IO/IOMetadataHandler/LegacyDFSCluster.php +++ /dev/null @@ -1,326 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO\IOMetadataHandler; - -use DateTime; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Exception; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException; -use eZ\Publish\Core\IO\IOMetadataHandler; -use eZ\Publish\Core\IO\UrlDecorator; -use eZ\Publish\SPI\IO\BinaryFile as SPIBinaryFile; -use eZ\Publish\SPI\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct; - -/** - * Manages IO metadata in a mysql table, ezdfsfile. - * - * It will prevent simultaneous writes to the same file. - */ -class LegacyDFSCluster implements IOMetadataHandler -{ - /** @var \Doctrine\DBAL\Connection */ - private $db; - - /** @var \eZ\Publish\Core\IO\UrlDecorator */ - private $urlDecorator; - - /** - * @param \Doctrine\DBAL\Connection $connection Doctrine DBAL connection - * @param \eZ\Publish\Core\IO\UrlDecorator $urlDecorator The URL decorator used to add a prefix to files path - */ - public function __construct(Connection $connection, UrlDecorator $urlDecorator = null) - { - $this->db = $connection; - $this->urlDecorator = $urlDecorator; - } - - /** - * Inserts a new reference to file $spiBinaryFileId. - * - * @since 6.10 The mtime of the $binaryFileCreateStruct must be a DateTime, as specified in the struct doc. - * - * @param \eZ\Publish\SPI\IO\BinaryFileCreateStruct $binaryFileCreateStruct - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException if the $binaryFileCreateStruct is invalid - * @throws \RuntimeException if a DBAL error occurs - * - * @return \eZ\Publish\SPI\IO\BinaryFile - */ - public function create(SPIBinaryFileCreateStruct $binaryFileCreateStruct) - { - if (!($binaryFileCreateStruct->mtime instanceof DateTime)) { - throw new InvalidArgumentException('$binaryFileCreateStruct', 'Property \'mtime\' must be a DateTime'); - } - - $path = (string)$this->addPrefix($binaryFileCreateStruct->id); - $params = [ - 'name' => $path, - 'name_hash' => md5($path), - 'name_trunk' => $this->getNameTrunk($binaryFileCreateStruct), - 'mtime' => $binaryFileCreateStruct->mtime->getTimestamp(), - 'size' => $binaryFileCreateStruct->size, - 'scope' => $this->getScope($binaryFileCreateStruct), - 'datatype' => $binaryFileCreateStruct->mimeType, - ]; - - try { - $this->db->insert('ezdfsfile', $params); - } catch (Exception $e) { - $this->db->update('ezdfsfile', [ - 'mtime' => $params['mtime'], - 'size' => $params['size'], - 'scope' => $params['scope'], - 'datatype' => $params['datatype'], - ], [ - 'name_hash' => $params['name_hash'], - ]); - } - - return $this->mapSPIBinaryFileCreateStructToSPIBinaryFile($binaryFileCreateStruct); - } - - /** - * Deletes file $spiBinaryFileId. - * - * @throws \eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException If $spiBinaryFileId is not found - * - * @param string $spiBinaryFileId - */ - public function delete($spiBinaryFileId) - { - $path = (string)$this->addPrefix($spiBinaryFileId); - - // Unlike the legacy cluster, the file is directly deleted. It was inherited from the DB cluster anyway - $affectedRows = (int)$this->db->delete('ezdfsfile', [ - 'name_hash' => md5($path), - ]); - - if ($affectedRows !== 1) { - // Is this really necessary ? - throw new BinaryFileNotFoundException($path); - } - } - - /** - * Loads and returns metadata for $spiBinaryFileId. - * - * @param string $spiBinaryFileId - * - * @return \eZ\Publish\SPI\IO\BinaryFile - * - * @throws \eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException if no row is found for $spiBinaryFileId - * @throws \Doctrine\DBAL\DBALException Any unhandled DBAL exception - */ - public function load($spiBinaryFileId) - { - $path = (string)$this->addPrefix($spiBinaryFileId); - - $queryBuilder = $this->db->createQueryBuilder(); - $result = $queryBuilder - ->select( - 'e.name_hash', - 'e.name', - 'e.name_trunk', - 'e.datatype', - 'e.scope', - 'e.size', - 'e.mtime', - 'e.expired', - 'e.status', - ) - ->from('ezdfsfile', 'e') - ->andWhere('e.name_hash = :name_hash') - ->andWhere('e.expired != true') - ->andWhere('e.mtime > 0') - ->setParameter('name_hash', md5($path)) - ->execute() - ; - - if ($result->rowCount() === 0) { - throw new BinaryFileNotFoundException($path); - } - - $row = $result->fetchAssociative() + ['id' => $spiBinaryFileId]; - - return $this->mapArrayToSPIBinaryFile($row); - } - - /** - * Checks if a file $spiBinaryFileId exists. - * - * @param string $spiBinaryFileId - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - * @throws \Doctrine\DBAL\DBALException Any unhandled DBAL exception - * - * @return bool - */ - public function exists($spiBinaryFileId) - { - $path = (string)$this->addPrefix($spiBinaryFileId); - - $queryBuilder = $this->db->createQueryBuilder(); - $result = $queryBuilder - ->select( - 'e.name_hash', - 'e.name', - 'e.name_trunk', - 'e.datatype', - 'e.scope', - 'e.size', - 'e.mtime', - 'e.expired', - 'e.status', - ) - ->from('ezdfsfile', 'e') - ->andWhere('e.name_hash = :name_hash') - ->andWhere('e.expired != true') - ->andWhere('e.mtime > 0') - ->setParameter('name_hash', md5($path)) - ->execute() - ; - - return $result->rowCount() === 1; - } - - /** - * @param \eZ\Publish\SPI\IO\BinaryFileCreateStruct $binaryFileCreateStruct - * - * @return mixed - */ - protected function getNameTrunk(SPIBinaryFileCreateStruct $binaryFileCreateStruct) - { - return $this->addPrefix($binaryFileCreateStruct->id); - } - - /** - * Returns the value for the scope meta field, based on the created file's path. - * - * Note that this is slightly incorrect, as it will return binaryfile for media files as well. It is a bit - * of an issue, but shouldn't be a blocker given that this meta field isn't used that much. - * - * @param \eZ\Publish\SPI\IO\BinaryFileCreateStruct $binaryFileCreateStruct - * - * @return string - */ - protected function getScope(SPIBinaryFileCreateStruct $binaryFileCreateStruct) - { - [$filePrefix] = explode('/', $binaryFileCreateStruct->id); - - switch ($filePrefix) { - case 'images': - return 'image'; - - case 'original': - return 'binaryfile'; - } - - return 'UNKNOWN_SCOPE'; - } - - /** - * Adds the internal prefix string to $id. - * - * @param string $id - * - * @return string prefixed id - */ - protected function addPrefix($id) - { - return isset($this->urlDecorator) ? $this->urlDecorator->decorate($id) : $id; - } - - /** - * Removes the internal prefix string from $prefixedId. - * - * @param string $prefixedId - * - * @return string the id without the prefix - * - * @throws \eZ\Publish\Core\IO\Exception\InvalidBinaryFileIdException if the prefix isn't found in $prefixedId - */ - protected function removePrefix($prefixedId) - { - return isset($this->urlDecorator) ? $this->urlDecorator->undecorate($prefixedId) : $prefixedId; - } - - public function getMimeType($spiBinaryFileId) - { - $queryBuilder = $this->db->createQueryBuilder(); - $result = $queryBuilder - ->select('e.datatype') - ->from('ezdfsfile', 'e') - ->andWhere('e.name_hash = :name_hash') - ->andWhere('e.expired != true') - ->andWhere('e.mtime > 0') - ->setParameter('name_hash', md5($this->addPrefix($spiBinaryFileId))) - ->execute() - ; - - if ($result->rowCount() == 0) { - throw new BinaryFileNotFoundException($spiBinaryFileId); - } - - $row = $result->fetchAssociative(); - - return $row['datatype']; - } - - /** - * Delete directory and all the content under specified directory. - * - * @param string $spiPath SPI Path, not prefixed by URL decoration - */ - public function deleteDirectory($spiPath) - { - $query = $this->db->createQueryBuilder(); - $query - ->delete('ezdfsfile') - ->where('name LIKE :spiPath ESCAPE :esc') - ->setParameter(':esc', '\\') - ->setParameter( - ':spiPath', - addcslashes($this->addPrefix(rtrim($spiPath, '/')), '%_') . '/%' - ); - $query->execute(); - } - - /** - * Maps an array of data base properties (id, size, mtime, datatype, md5_path, path...) to an SPIBinaryFile object. - * - * @param array $properties database properties array - * - * @return \eZ\Publish\SPI\IO\BinaryFile - */ - protected function mapArrayToSPIBinaryFile(array $properties) - { - $spiBinaryFile = new SPIBinaryFile(); - $spiBinaryFile->id = $properties['id']; - $spiBinaryFile->size = $properties['size']; - $spiBinaryFile->mtime = new DateTime('@' . $properties['mtime']); - $spiBinaryFile->mimeType = $properties['datatype']; - - return $spiBinaryFile; - } - - /** - * @param \eZ\Publish\SPI\IO\BinaryFileCreateStruct $binaryFileCreateStruct - * - * @return \eZ\Publish\SPI\IO\BinaryFile - */ - protected function mapSPIBinaryFileCreateStructToSPIBinaryFile(SPIBinaryFileCreateStruct $binaryFileCreateStruct) - { - $spiBinaryFile = new SPIBinaryFile(); - $spiBinaryFile->id = $binaryFileCreateStruct->id; - $spiBinaryFile->mtime = $binaryFileCreateStruct->mtime; - $spiBinaryFile->size = $binaryFileCreateStruct->size; - $spiBinaryFile->mimeType = $binaryFileCreateStruct->mimeType; - - return $spiBinaryFile; - } -} diff --git a/eZ/Publish/Core/IO/IOServiceInterface.php b/eZ/Publish/Core/IO/IOServiceInterface.php deleted file mode 100644 index 469b182a66..0000000000 --- a/eZ/Publish/Core/IO/IOServiceInterface.php +++ /dev/null @@ -1,173 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO; - -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\IO\Values\BinaryFileCreateStruct; - -/** - * Interface for Input/Output handling of binary files. - */ -interface IOServiceInterface -{ - /** - * The the internal prefix added by the IO Service. - * - * @param string $prefix - */ - public function setPrefix($prefix); - - /** - * Returns the external path to $internalPath. - * - * @param string $internalId - * - * @deprecated Since 5.4. Use loadBinaryFileByUri. - * - * @return string - */ - public function getExternalPath($internalId); - - /** - * Creates a BinaryFileCreateStruct object from $localFile. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException When given a non existing / unreadable file - * - * @param string $localFile Path to local file - * - * @return \eZ\Publish\Core\IO\Values\BinaryFileCreateStruct - */ - public function newBinaryCreateStructFromLocalFile($localFile); - - /** - * Checks if a Binary File with $binaryFileId exists. - * - * @param string $binaryFileId - * - * @return bool - */ - public function exists($binaryFileId); - - /** - * Returns the internal, handler level path to $externalPath. - * - * @param string $externalId - * - * @deprecated Since 5.4. Use the uri property. - * - * @return string - */ - public function getInternalPath($externalId); - - /** - * Loads the binary file with $id. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue If the id is invalid - * - * @param string $binaryFileId - * - * @return \eZ\Publish\Core\IO\Values\BinaryFile the file, or false if it doesn't exist - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException If no file identified by $binaryFileId exists - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue If $binaryFileId is invalid - */ - public function loadBinaryFile($binaryFileId); - - /** - * Loads the binary file with uri $uri. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue If the id is invalid - * - * @param string $binaryFileUri - * - * @return \eZ\Publish\Core\IO\Values\BinaryFile the file, or false if it doesn't exist - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException If no file identified by $binaryFileId exists - */ - public function loadBinaryFileByUri($binaryFileUri); - - /** - * Returns the content of the binary file. - * - * @param \eZ\Publish\Core\IO\Values\BinaryFile $binaryFile - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException If $binaryFile isn't found - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue - * - * @return string - */ - public function getFileContents(BinaryFile $binaryFile); - - /** - * Creates a binary file in the repository. - * - * @param \eZ\Publish\Core\IO\Values\BinaryFileCreateStruct $binaryFileCreateStruct - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue - * - * @return \eZ\Publish\Core\IO\Values\BinaryFile The created BinaryFile object - */ - public function createBinaryFile(BinaryFileCreateStruct $binaryFileCreateStruct); - - /** - * Returns the public HTTP uri for $binaryFileId. - * - * @param string $binaryFileId - * - * @return string - */ - public function getUri($binaryFileId); - - /** - * Gets the mime-type of the BinaryFile. - * - * Example: text/xml - * - * @param string $binaryFileId - * - * @return string|null - */ - public function getMimeType($binaryFileId); - - /** - * Returns a read (mode: rb) file resource to the binary file identified by $path. - * - * @param \eZ\Publish\Core\IO\Values\BinaryFile $binaryFile - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue - * - * @return resource - */ - public function getFileInputStream(BinaryFile $binaryFile); - - /** - * Deletes the BinaryFile with $id. - * - * @param \eZ\Publish\Core\IO\Values\BinaryFile $binaryFile - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue - */ - public function deleteBinaryFile(BinaryFile $binaryFile); - - /** - * Creates a BinaryFileCreateStruct object from the uploaded file $uploadedFile. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException When given an invalid uploaded file - * - * @param array $uploadedFile The $_POST hash of an uploaded file - * - * @return \eZ\Publish\Core\IO\Values\BinaryFileCreateStruct - */ - public function newBinaryCreateStructFromUploadedFile(array $uploadedFile); - - /** - * Deletes a directory. - * - * @param string $path - */ - public function deleteDirectory($path); -} diff --git a/eZ/Publish/Core/IO/Tests/FilePathNormalizer/FlysystemTest.php b/eZ/Publish/Core/IO/Tests/FilePathNormalizer/FlysystemTest.php deleted file mode 100644 index f1cee07943..0000000000 --- a/eZ/Publish/Core/IO/Tests/FilePathNormalizer/FlysystemTest.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace Ibexa\Tests\Core\IO\FilePathNormalizer; - -use eZ\Publish\Core\IO\FilePathNormalizer\Flysystem; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; -use PHPUnit\Framework\TestCase; - -final class FlysystemTest extends TestCase -{ - /** @var \eZ\Publish\Core\IO\FilePathNormalizer\Flysystem */ - private $filePathNormalizer; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter|\PHPUnit\Framework\MockObject\MockObject */ - private $slugConverter; - - public function setUp(): void - { - $this->slugConverter = $this->createMock(SlugConverter::class); - $this->filePathNormalizer = new Flysystem($this->slugConverter); - } - - /** - * @dataProvider providerForTestNormalizePath - */ - public function testNormalizePath( - string $originalPath, - string $fileName, - string $sluggedFileName, - string $regex, - bool $doHash - ): void { - $this->slugConverter - ->expects(self::once()) - ->method('convert') - ->with($fileName) - ->willReturn($sluggedFileName); - - $normalizedPath = $this->filePathNormalizer->normalizePath($originalPath, $doHash); - - self::assertStringEndsWith($sluggedFileName, $normalizedPath); - self::assertRegExp($regex, $normalizedPath); - } - - public function providerForTestNormalizePath(): array - { - $defaultPattern = '/\/[0-9a-f]{12}-'; - - return [ - 'No special chars' => [ - '4/3/2/234/1/image.jpg', - 'image.jpg', - 'image.jpg', - $defaultPattern . 'image.jpg/', - true, - ], - 'Spaces in the filename' => [ - '4/3/2/234/1/image with spaces.jpg', - 'image with spaces.jpg', - 'image-with-spaces.jpg', - $defaultPattern . 'image-with-spaces.jpg/', - true, - ], - 'Encoded spaces in the name' => [ - '4/3/2/234/1/image%20+no+spaces.jpg', - 'image%20+no+spaces.jpg', - 'image-20-nospaces.jpg', - $defaultPattern . 'image-20-nospaces.jpg/', - true, - ], - 'Special chars in the name' => [ - '4/3/2/234/1/image%20+no+spaces?.jpg', - 'image%20+no+spaces?.jpg', - 'image-20-nospaces.jpg', - $defaultPattern . 'image-20-nospaces.jpg/', - true, - ], - 'Already hashed name' => [ - '4/3/2/234/1/14ff44718877-hashed.jpg', - '14ff44718877-hashed.jpg', - '14ff44718877-hashed.jpg', - '/^4\/3\/2\/234\/1\/14ff44718877-hashed.jpg$/', - true, - ], - 'No special chars and no hashing' => [ - '4/3/2/234/1/image.jpg', - 'image.jpg', - 'image.jpg', - '/\/image.jpg/', - false, - ], - 'Special chars and no hashing' => [ - '4/3/2/234/1/image%20+no+spaces.jpg', - 'image%20+no+spaces.jpg', - 'image-20-nospaces.jpg', - '/\/image-20-nospaces.jpg/', - false, - ], - 'Already hashed name and no hashing' => [ - '4/3/2/234/1/14ff44718877-hashed.jpg', - '14ff44718877-hashed.jpg', - '14ff44718877-hashed.jpg', - '/^4\/3\/2\/234\/1\/14ff44718877-hashed.jpg$/', - false, - ], - ]; - } -} diff --git a/eZ/Publish/Core/IO/Tests/IOBinarydataHandler/FlysystemTest.php b/eZ/Publish/Core/IO/Tests/IOBinarydataHandler/FlysystemTest.php deleted file mode 100644 index 09974bb90f..0000000000 --- a/eZ/Publish/Core/IO/Tests/IOBinarydataHandler/FlysystemTest.php +++ /dev/null @@ -1,183 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO\Tests\IOBinarydataHandler; - -use eZ\Publish\Core\IO\IOBinarydataHandler\Flysystem; -use eZ\Publish\SPI\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct; -use League\Flysystem\FileExistsException; -use League\Flysystem\FileNotFoundException; -use League\Flysystem\FilesystemInterface; -use PHPUnit\Framework\TestCase; - -class FlysystemTest extends TestCase -{ - /** @var \eZ\Publish\Core\IO\IOBinarydataHandler|\PHPUnit\Framework\MockObject\MockObject */ - private $handler; - - /** @var \League\Flysystem\FilesystemInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $filesystem; - - protected function setUp(): void - { - $this->filesystem = $this->createMock(FilesystemInterface::class); - $this->handler = new Flysystem($this->filesystem); - } - - public function testCreate() - { - $stream = fopen('php://memory', 'rb'); - $spiBinaryFileCreateStruct = new SPIBinaryFileCreateStruct(); - $spiBinaryFileCreateStruct->id = 'prefix/my/file.png'; - $spiBinaryFileCreateStruct->mimeType = 'image/png'; - $spiBinaryFileCreateStruct->size = 123; - $spiBinaryFileCreateStruct->mtime = 1307155200; - $spiBinaryFileCreateStruct->setInputStream($stream); - - $this->filesystem - ->expects($this->once()) - ->method('writeStream') - ->with( - $this->equalTo($spiBinaryFileCreateStruct->id), - $this->equalTo($stream), - $this->equalTo(['mimetype' => 'image/png', 'visibility' => 'public']) - ); - - $this->handler->create($spiBinaryFileCreateStruct); - } - - public function testCreateOverwritesIfExists() - { - $stream = fopen('php://memory', 'rb'); - $spiBinaryFileCreateStruct = new SPIBinaryFileCreateStruct(); - $spiBinaryFileCreateStruct->id = 'prefix/my/file.png'; - $spiBinaryFileCreateStruct->mimeType = 'image/png'; - $spiBinaryFileCreateStruct->size = 123; - $spiBinaryFileCreateStruct->mtime = 1307155200; - $spiBinaryFileCreateStruct->setInputStream($stream); - - $this->filesystem - ->expects($this->once()) - ->method('writeStream') - ->with( - $this->equalTo($spiBinaryFileCreateStruct->id), - $this->equalTo($stream), - $this->equalTo(['mimetype' => 'image/png', 'visibility' => 'public']) - ) - ->will($this->throwException(new FileExistsException('prefix/my/file.png'))); - - $this->filesystem - ->expects($this->once()) - ->method('updateStream') - ->with( - $this->equalTo($spiBinaryFileCreateStruct->id), - $this->equalTo($stream), - $this->equalTo(['mimetype' => 'image/png', 'visibility' => 'public']) - ); - - $this->handler->create($spiBinaryFileCreateStruct); - } - - public function testDelete() - { - $this->filesystem - ->expects($this->once()) - ->method('delete') - ->with('prefix/my/file.png'); - - $this->handler->delete('prefix/my/file.png'); - } - - public function testDeleteNotFound() - { - $this->expectException(\eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException::class); - - $this->filesystem - ->expects($this->once()) - ->method('delete') - ->with('prefix/my/file.png') - ->will($this->throwException(new FileNotFoundException('prefix/my/file.png'))); - - $this->handler->delete('prefix/my/file.png'); - } - - public function testGetContents() - { - $this->filesystem - ->expects($this->once()) - ->method('read') - ->with('prefix/my/file.png') - ->will($this->returnValue('This is my contents')); - - self::assertEquals( - 'This is my contents', - $this->handler->getContents('prefix/my/file.png') - ); - } - - public function testGetContentsNotFound() - { - $this->expectException(\eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException::class); - - $this->filesystem - ->expects($this->once()) - ->method('read') - ->with('prefix/my/file.png') - ->will($this->throwException(new FileNotFoundException('prefix/my/file.png'))); - - self::assertEquals( - 'This is my contents', - $this->handler->getContents('prefix/my/file.png') - ); - } - - public function testGetResource() - { - $resource = fopen('php://temp', 'rb'); - - $this->filesystem - ->expects($this->once()) - ->method('readStream') - ->with('prefix/my/file.png') - ->will($this->returnValue($resource)); - - self::assertEquals( - $resource, - $this->handler->getResource('prefix/my/file.png') - ); - } - - public function testGetResourceNotFound() - { - $this->expectException(\eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException::class); - - $this->filesystem - ->expects($this->once()) - ->method('readStream') - ->with('prefix/my/file.png') - ->will($this->throwException(new FileNotFoundException('prefix/my/file.png'))); - - $this->handler->getResource('prefix/my/file.png'); - } - - public function testGetUri() - { - self::assertEquals( - '/prefix/my/file.png', - $this->handler->getUri('prefix/my/file.png') - ); - } - - public function testDeleteDirectory() - { - $this->filesystem - ->expects($this->once()) - ->method('deleteDir') - ->with('some/path'); - - $this->handler->deleteDirectory('some/path'); - } -} diff --git a/eZ/Publish/Core/IO/Tests/IOMetadataHandler/FlysystemTest.php b/eZ/Publish/Core/IO/Tests/IOMetadataHandler/FlysystemTest.php deleted file mode 100644 index 560e02a1e8..0000000000 --- a/eZ/Publish/Core/IO/Tests/IOMetadataHandler/FlysystemTest.php +++ /dev/null @@ -1,167 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO\Tests\IOMetadataHandler; - -use DateTime; -use eZ\Publish\Core\IO\IOMetadataHandler\Flysystem; -use eZ\Publish\SPI\IO\BinaryFile as SPIBinaryFile; -use eZ\Publish\SPI\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct; -use League\Flysystem\FileNotFoundException; -use League\Flysystem\FilesystemInterface; -use PHPUnit\Framework\TestCase; - -class FlysystemTest extends TestCase -{ - /** @var \eZ\Publish\Core\IO\IOMetadataHandler|\PHPUnit\Framework\MockObject\MockObject */ - private $handler; - - /** @var \League\Flysystem\FilesystemInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $filesystem; - - protected function setUp(): void - { - $this->filesystem = $this->createMock(FilesystemInterface::class); - $this->handler = new Flysystem($this->filesystem); - } - - public function testCreate() - { - // good example of bad responsibilities... since create also loads, we test the same thing twice - $spiCreateStruct = new SPIBinaryFileCreateStruct(); - $spiCreateStruct->id = 'prefix/my/file.png'; - $spiCreateStruct->size = 123; - $spiCreateStruct->mtime = new DateTime('@1307155200'); - - $expectedSpiBinaryFile = new SPIBinaryFile(); - $expectedSpiBinaryFile->id = 'prefix/my/file.png'; - $expectedSpiBinaryFile->size = 123; - $expectedSpiBinaryFile->mtime = new DateTime('@1307155200'); - - $this->filesystem - ->expects($this->once()) - ->method('getMetadata') - ->with($spiCreateStruct->id) - ->will( - $this->returnValue( - [ - 'timestamp' => 1307155200, - 'size' => 123, - ] - ) - ); - - $spiBinaryFile = $this->handler->create($spiCreateStruct); - - $this->assertInstanceOf(SPIBinaryFile::class, $spiBinaryFile); - $this->assertEquals($expectedSpiBinaryFile, $spiBinaryFile); - } - - public function testDelete() - { - $this->filesystem->expects($this->never())->method('delete'); - $this->handler->delete('prefix/my/file.png'); - } - - public function testLoad() - { - $expectedSpiBinaryFile = new SPIBinaryFile(); - $expectedSpiBinaryFile->id = 'prefix/my/file.png'; - $expectedSpiBinaryFile->size = 123; - $expectedSpiBinaryFile->mtime = new DateTime('@1307155200'); - - $this->filesystem - ->expects($this->once()) - ->method('getMetadata') - ->with('prefix/my/file.png') - ->will( - $this->returnValue( - [ - 'timestamp' => 1307155200, - 'size' => 123, - ] - ) - ); - - $spiBinaryFile = $this->handler->load('prefix/my/file.png'); - - $this->assertInstanceOf(SPIBinaryFile::class, $spiBinaryFile); - $this->assertEquals($expectedSpiBinaryFile, $spiBinaryFile); - } - - /** - * The timestamp index can be unset with some handlers, like AWS/S3. - */ - public function testLoadNoTimestamp() - { - $this->filesystem - ->expects($this->once()) - ->method('getMetadata') - ->with('prefix/my/file.png') - ->will( - $this->returnValue( - [ - 'size' => 123, - ] - ) - ); - - $spiBinaryFile = $this->handler->load('prefix/my/file.png'); - $this->assertNull($spiBinaryFile->mtime); - } - - public function testLoadNotFound() - { - $this->expectException(\eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException::class); - - $this->filesystem - ->expects($this->once()) - ->method('getMetadata') - ->with('prefix/my/file.png') - ->will($this->throwException(new FileNotFoundException('prefix/my/file.png'))); - - $this->handler->load('prefix/my/file.png'); - } - - public function testExists() - { - $this->filesystem - ->expects($this->once()) - ->method('has') - ->with('prefix/my/file.png') - ->will($this->returnValue(true)); - - self::assertTrue($this->handler->exists('prefix/my/file.png')); - } - - public function testExistsNot() - { - $this->filesystem - ->expects($this->once()) - ->method('has') - ->with('prefix/my/file.png') - ->will($this->returnValue(false)); - - self::assertFalse($this->handler->exists('prefix/my/file.png')); - } - - public function testGetMimeType() - { - $this->filesystem - ->expects($this->once()) - ->method('getMimeType') - ->with('file.txt') - ->will($this->returnValue('text/plain')); - - self::assertEquals('text/plain', $this->handler->getMimeType('file.txt')); - } - - public function testDeleteDirectory() - { - $this->filesystem->expects($this->never())->method('deleteDir'); - $this->handler->deleteDirectory('some/path'); - } -} diff --git a/eZ/Publish/Core/IO/Tests/IOMetadataHandler/LegacyDFSClusterTest.php b/eZ/Publish/Core/IO/Tests/IOMetadataHandler/LegacyDFSClusterTest.php deleted file mode 100644 index 856ee5f5b8..0000000000 --- a/eZ/Publish/Core/IO/Tests/IOMetadataHandler/LegacyDFSClusterTest.php +++ /dev/null @@ -1,252 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO\Tests\IOMetadataHandler; - -use DateTime; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Driver\Statement; -use Doctrine\DBAL\Query\QueryBuilder; -use Doctrine\DBAL\Result; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException; -use eZ\Publish\Core\IO\IOMetadataHandler\LegacyDFSCluster; -use eZ\Publish\Core\IO\UrlDecorator; -use eZ\Publish\SPI\IO\BinaryFile as SPIBinaryFile; -use eZ\Publish\SPI\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct; -use PHPUnit\Framework\TestCase; - -class LegacyDFSClusterTest extends TestCase -{ - /** @var \eZ\Publish\Core\IO\IOMetadataHandler&\PHPUnit\Framework\MockObject\MockObject */ - private $handler; - - /** @var \Doctrine\DBAL\Connection&\PHPUnit\Framework\MockObject\MockObject */ - private $dbalMock; - - /** @var \Doctrine\DBAL\Query\QueryBuilder&\PHPUnit\Framework\MockObject\MockObject */ - private $qbMock; - - /** @var \eZ\Publish\Core\IO\UrlDecorator&\PHPUnit\Framework\MockObject\MockObject */ - private $urlDecoratorMock; - - protected function setUp(): void - { - $this->dbalMock = $this->createMock(Connection::class); - - $this->qbMock = $this->getMockBuilder(QueryBuilder::class) - ->disableOriginalConstructor() - ->getMock(); - - $this->dbalMock->method('createQueryBuilder')->willReturn($this->qbMock); - $this->urlDecoratorMock = $this->createMock(UrlDecorator::class); - - $this->handler = new LegacyDFSCluster( - $this->dbalMock, - $this->urlDecoratorMock - ); - } - - /** - * @return iterable<array{string, string, int, \DateTime, \DateTime}> - */ - public function providerCreate(): iterable - { - return [ - ['prefix/my/file.png', 'image/png', 123, new DateTime('@1307155200'), new DateTime('@1307155200')], - ['prefix/my/file.png', 'image/png', 123, new DateTime('@1307155200'), new DateTime('@1307155200')], // Duplicate, should not fail - ['prefix/my/file.png', 'image/png', 123, new DateTime('@1307155242'), new DateTime('@1307155242')], - ]; - } - - /** - * @dataProvider providerCreate - */ - public function testCreate(string $id, string $mimeType, int $size, \DateTime $mtime, \DateTime $mtimeExpected): void - { - $this->dbalMock - ->expects(self::once()) - ->method('insert') - ->with('ezdfsfile'); - - $spiCreateStruct = new SPIBinaryFileCreateStruct(); - $spiCreateStruct->id = $id; - $spiCreateStruct->mimeType = $mimeType; - $spiCreateStruct->size = $size; - $spiCreateStruct->mtime = $mtime; - - $spiBinary = $this->handler->create($spiCreateStruct); - - self::assertInstanceOf(SPIBinaryFile::class, $spiBinary); - self::assertEquals($mtimeExpected, $spiBinary->mtime); - } - - public function testCreateInvalidArgument(): void - { - $this->dbalMock - ->expects(self::never()) - ->method('insert'); - - $spiCreateStruct = new SPIBinaryFileCreateStruct(); - $spiCreateStruct->id = 'prefix/my/file.png'; - $spiCreateStruct->mimeType = 'image/png'; - $spiCreateStruct->size = 123; - $spiCreateStruct->mtime = 1307155242; // Invalid, should be a DateTime - - $this->expectException(InvalidArgumentException::class); - $this->handler->create($spiCreateStruct); - } - - public function testDelete(): void - { - $this->dbalMock - ->expects(self::once()) - ->method('delete') - ->with('ezdfsfile') - ->willReturn(1); - - $this->handler->delete('prefix/my/file.png'); - } - - public function testDeleteNotFound(): void - { - $this->dbalMock - ->expects(self::once()) - ->method('delete') - ->with('ezdfsfile') - ->willReturn(0); - - $this->expectException(BinaryFileNotFoundException::class); - $this->handler->delete('prefix/my/file.png'); - } - - public function testLoad(): void - { - $this->setupQueryBuilderLoad(1, ['size' => 123, 'datatype' => 'image/png', 'mtime' => 1307155200]); - - $expectedSpiBinaryFile = new SPIBinaryFile(); - $expectedSpiBinaryFile->id = 'prefix/my/file.png'; - $expectedSpiBinaryFile->size = 123; - $expectedSpiBinaryFile->mtime = new DateTime('@1307155200'); - $expectedSpiBinaryFile->mimeType = 'image/png'; - - self::assertEquals( - $expectedSpiBinaryFile, - $this->handler->load('prefix/my/file.png') - ); - } - - public function testLoadNotFound(): void - { - $this->setupQueryBuilderLoad(0, null); - - $this->expectException(BinaryFileNotFoundException::class); - $this->handler->load('prefix/my/file.png'); - } - - public function testExists(): void - { - $this->setupQueryBuilderLoad(1, null); - - self::assertTrue($this->handler->exists('prefix/my/file.png')); - } - - public function testExistsNot(): void - { - $this->setupQueryBuilderLoad(0, null); - - self::assertFalse($this->handler->exists('prefix/my/file.png')); - } - - public function testDeletedirectory(): void - { - $this->urlDecoratorMock - ->expects(self::once()) - ->method('decorate') - ->willReturn('prefix/images/_alias/subfolder'); - - $this->qbMock - ->expects(self::once()) - ->method('delete') - ->with('ezdfsfile') - ->willReturnSelf(); - - $this->qbMock - ->expects(self::once()) - ->method('where') - ->with('name LIKE :spiPath ESCAPE :esc') - ->willReturnSelf(); - - $this->qbMock - ->expects(self::exactly(2)) - ->method('setParameter') - ->withConsecutive( - [':esc', '\\'], - [':spiPath', 'prefix/images/\_alias/subfolder/%'], - ) - ->willReturnSelf(); - - $this->qbMock - ->expects(self::once()) - ->method('execute') - ->willReturn(1); - - $this->handler->deleteDirectory('images/_alias/subfolder/'); - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject - */ - protected function createDbalStatementMock() - { - return $this->createMock(Statement::class); - } - - /** - * @param array<mixed>|null $result - */ - private function setupQueryBuilderLoad(int $rowCount, ?array $result): void - { - $resultMock = $this->createMock(Result::class); - $resultMock - ->expects(self::once()) - ->method('rowCount') - ->willReturn($rowCount); - - if ($result === null) { - $resultMock - ->expects(self::never()) - ->method('fetchAssociative'); - } else { - $resultMock - ->expects(self::once()) - ->method('fetchAssociative') - ->willReturn($result); - } - - $this->qbMock - ->expects(self::once()) - ->method('select') - ->willReturnSelf(); - - $this->qbMock - ->expects(self::once()) - ->method('from') - ->willReturnSelf(); - - $this->qbMock - ->method('andWhere') - ->willReturnSelf(); - - $this->qbMock - ->method('setParameter') - ->willReturnSelf(); - - $this->qbMock - ->method('execute') - ->willReturn($resultMock); - } -} diff --git a/eZ/Publish/Core/IO/Tests/MetadataHandler/ImageSizeTest.php b/eZ/Publish/Core/IO/Tests/MetadataHandler/ImageSizeTest.php deleted file mode 100644 index 9885e0fd84..0000000000 --- a/eZ/Publish/Core/IO/Tests/MetadataHandler/ImageSizeTest.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO\Tests\MetadataHandler; - -use eZ\Publish\Core\IO\MetadataHandler\ImageSize as ImageSizeMetadataHandler; -use PHPUnit\Framework\TestCase; - -/** - * @group fieldType - * @group ezimage - */ -class ImageSizeTest extends TestCase -{ - public function testExtract() - { - $metadataHandler = new ImageSizeMetadataHandler(); - $file = 'eZ/Publish/Core/Repository/Tests/Service/Integration/ezplogo.png'; - self::assertEquals( - ['width' => 189, 'height' => 200, 'mime' => 'image/png'], - $metadataHandler->extract($file) - ); - } -} diff --git a/eZ/Publish/Core/IO/Tests/MimeTypeDetector/FileInfoTest.php b/eZ/Publish/Core/IO/Tests/MimeTypeDetector/FileInfoTest.php deleted file mode 100644 index f70f7dfcab..0000000000 --- a/eZ/Publish/Core/IO/Tests/MimeTypeDetector/FileInfoTest.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO\Tests\MimeTypeDetector; - -use eZ\Publish\Core\IO\MimeTypeDetector\FileInfo as MimeTypeDetector; -use PHPUnit\Framework\TestCase; - -class FileInfoTest extends TestCase -{ - /** @var \eZ\Publish\Core\IO\MimeTypeDetector\FileInfo */ - protected $mimeTypeDetector; - - protected function setUp(): void - { - $this->mimeTypeDetector = new MimeTypeDetector(); - } - - protected function getFixture() - { - return __DIR__ . '/../_fixtures/squirrel-developers.jpg'; - } - - public function testGetFromPath() - { - self::assertEquals( - $this->mimeTypeDetector->getFromPath( - $this->getFixture() - ), - 'image/jpeg' - ); - } - - public function testGetFromBuffer() - { - self::assertEquals( - $this->mimeTypeDetector->getFromBuffer( - file_get_contents($this->getFixture()) - ), - 'image/jpeg' - ); - } -} diff --git a/eZ/Publish/Core/IO/Tests/_fixtures/squirrel-developers.jpg b/eZ/Publish/Core/IO/Tests/_fixtures/squirrel-developers.jpg deleted file mode 100644 index 8bab89919d..0000000000 Binary files a/eZ/Publish/Core/IO/Tests/_fixtures/squirrel-developers.jpg and /dev/null differ diff --git a/eZ/Publish/Core/IO/UrlRedecoratorInterface.php b/eZ/Publish/Core/IO/UrlRedecoratorInterface.php deleted file mode 100644 index 2c0cb0c20b..0000000000 --- a/eZ/Publish/Core/IO/UrlRedecoratorInterface.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO; - -/** - * Converts an URL from one decorator to another. - * - * ```php - * $redecorator = new UrlRedecorator( - * new Prefix( 'a' ), - * new Prefix( 'b' ) - * ); - * - * $redecorator->redecorateFromSource( 'a/url' ); - * // 'b/url' - * - * $redecorator->redecorateFromTarget( 'b/url' ); - * // 'a/url' - * ``` - */ -interface UrlRedecoratorInterface -{ - /** - * Redecorates $uri from source to target. - * - * @param string $uri - * - * @return string - * - * @throws \eZ\Publish\Core\IO\Exception\InvalidBinaryFileIdException If $uri couldn't be interpreted b y the target decorator - */ - public function redecorateFromSource($uri); - - /** - * Redecorates $uri from source to target. - * - * @param string $uri - * - * @return string - * - * @throws \eZ\Publish\Core\IO\Exception\InvalidBinaryFileIdException If $uri couldn't be interpreted b y the target decorator - */ - public function redecorateFromTarget($uri); -} diff --git a/eZ/Publish/Core/IO/Values/BinaryFile.php b/eZ/Publish/Core/IO/Values/BinaryFile.php deleted file mode 100644 index db7ed613bf..0000000000 --- a/eZ/Publish/Core/IO/Values/BinaryFile.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO\Values; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * This class provides an abstract access to binary files. - * - * It allows reading & writing of files in a unified way - * - * @property-read string $id The id of the binary file - * @property-read int $mtime File modification time - * @property-read string $uri HTTP URI to the binary file - * @property-read int $size File size - */ -class BinaryFile extends ValueObject -{ - /** - * Unique ID - * Ex: media/images/ez-logo/209-1-eng-GB/eZ-Logo.gif, or application/2b042138835bb5f48beb9c9df6e86de4.pdf. - * - * @var mixed - */ - protected $id; - - /** - * File size, in bytes. - * - * @var int - */ - protected $size; - - /** - * File modification time. - * - * @var \DateTime - */ - protected $mtime; - - /** - * URI to the binary file. - * - * @var string - */ - protected $uri; - - /** - * The file's mime type. - * - * Example: text/xml - * - * @deprecated Since 5.3.3, use IOService::getMimeType() if you want to be 6.0 compatible as opposed to < 5.3.3 - * - * @var string - */ - public $mimeType; -} diff --git a/eZ/Publish/Core/IO/Values/BinaryFileCreateStruct.php b/eZ/Publish/Core/IO/Values/BinaryFileCreateStruct.php deleted file mode 100644 index b0ec2fcf96..0000000000 --- a/eZ/Publish/Core/IO/Values/BinaryFileCreateStruct.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\IO\Values; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * Create struct for BinaryFile objects. - */ -class BinaryFileCreateStruct extends ValueObject -{ - /** - * URI the binary file should be stored to. - * - * @var string - */ - public $id; - - /** - * The size of the file. - * - * @var int - */ - public $size; - - /** - * the input stream. - * - * @var resource - */ - public $inputStream; - - /** - * The file's mime type - * If not provided, will be auto-detected by the IOService - * Example: text/xml. - * - * @var string - */ - public $mimeType; -} diff --git a/eZ/Publish/Core/Limitation/AbstractPersistenceLimitationType.php b/eZ/Publish/Core/Limitation/AbstractPersistenceLimitationType.php deleted file mode 100644 index 7a1671b802..0000000000 --- a/eZ/Publish/Core/Limitation/AbstractPersistenceLimitationType.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\SPI\Persistence\Handler as SPIPersistenceHandler; - -/** - * LocationLimitation is a Content limitation. - */ -class AbstractPersistenceLimitationType -{ - /** @var \eZ\Publish\SPI\Persistence\Handler */ - protected $persistence; - - /** - * @param \eZ\Publish\SPI\Persistence\Handler $persistence - */ - public function __construct(SPIPersistenceHandler $persistence) - { - $this->persistence = $persistence; - } -} diff --git a/eZ/Publish/Core/Limitation/BlockingLimitationType.php b/eZ/Publish/Core/Limitation/BlockingLimitationType.php deleted file mode 100644 index 9a1a721776..0000000000 --- a/eZ/Publish/Core/Limitation/BlockingLimitationType.php +++ /dev/null @@ -1,151 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\BlockingLimitation as APIBlockingLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; - -/** - * BlockingLimitationType is a limitation type that always says no to the permission system. - * - * It is for use in cases where a limitation is not implemented, or limitation is legacy specific - * and it is then not possible to know when to say yes, so we need to say no. - */ -class BlockingLimitationType implements SPILimitationTypeInterface -{ - /** @var string */ - private $identifier; - - /** - * Create new Blocking Limitation with identifier injected dynamically. - * - * @throws \InvalidArgumentException If $identifier is empty - * - * @param string $identifier The identifier of the limitation - */ - public function __construct($identifier) - { - if (empty($identifier)) { - throw new \InvalidArgumentException('Argument $identifier cannot be empty'); - } - - $this->identifier = $identifier; - } - - /** - * Accepts a Blocking Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APIBlockingLimitation) { - throw new InvalidArgumentType('$limitationValue', 'BlockingLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - if (empty($limitationValue->limitationValues)) { - $validationErrors[] = new ValidationError( - "\$limitationValue->limitationValues => '%value%' can not be empty", - null, - [ - 'value' => $limitationValue->limitationValues, - ] - ); - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APIBlockingLimitation($this->identifier, $limitationValues); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - if (!$value instanceof APIBlockingLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: BlockingLimitation'); - } - - return false; - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - return new Criterion\MatchNone(); - } - - /** - * Returns info on valid $limitationValues. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException Values will need to be injected in ctor. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - throw new NotImplementedException(__METHOD__); - } -} diff --git a/eZ/Publish/Core/Limitation/ContentTypeLimitationType.php b/eZ/Publish/Core/Limitation/ContentTypeLimitationType.php deleted file mode 100644 index f38dc4c92d..0000000000 --- a/eZ/Publish/Core/Limitation/ContentTypeLimitationType.php +++ /dev/null @@ -1,234 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation as APIContentTypeLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Target; -use eZ\Publish\SPI\Limitation\TargetAwareType as SPITargetAwareLimitationType; - -/** - * ContentTypeLimitation is a Content limitation. - */ -class ContentTypeLimitationType extends AbstractPersistenceLimitationType implements SPITargetAwareLimitationType -{ - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APIContentTypeLimitation) { - throw new InvalidArgumentType('$limitationValue', 'APIContentTypeLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $id) { - if (!is_string($id) && !is_int($id)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - foreach ($limitationValue->limitationValues as $key => $id) { - try { - $this->persistence->contentTypeHandler()->load($id); - } catch (APINotFoundException $e) { - $validationErrors[] = new ValidationError( - "limitationValues[%key%] => '%value%' does not exist in the backend", - null, - [ - 'value' => $id, - 'key' => $key, - ] - ); - } - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APIContentTypeLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool|null - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null): ?bool - { - $targets = $targets ?? []; - foreach ($targets as $target) { - if (!$target instanceof Target\Version) { - continue; - } - - $accessVote = $this->evaluateVersionTarget($target, $value); - - // continue evaluation of targets if there was no explicit grant/deny - if ($accessVote === self::ACCESS_ABSTAIN) { - continue; - } - - return $accessVote; - } - - if (!$value instanceof APIContentTypeLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: APIContentTypeLimitation'); - } - - if ($object instanceof Content) { - $object = $object->getVersionInfo()->getContentInfo(); - } elseif ($object instanceof VersionInfo) { - $object = $object->getContentInfo(); - } elseif (!$object instanceof ContentInfo && !$object instanceof ContentCreateStruct) { - throw new InvalidArgumentException( - '$object', - 'Must be of type: ContentCreateStruct, Content, VersionInfo or ContentInfo' - ); - } - - if (empty($value->limitationValues)) { - return false; - } - - if ($object instanceof ContentCreateStruct) { - return in_array($object->contentType->id, $value->limitationValues); - } - - /* - * @var $object ContentInfo - */ - return in_array($object->contentTypeId, $value->limitationValues); - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - if (empty($value->limitationValues)) { - // A Policy should not have empty limitationValues stored - throw new \RuntimeException('$value->limitationValues is empty'); - } - - if (!isset($value->limitationValues[1])) { - // 1 limitation value: EQ operation - return new Criterion\ContentTypeId($value->limitationValues[0]); - } - - // several limitation values: IN operation - return new Criterion\ContentTypeId($value->limitationValues); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } - - /** - * Evaluate permissions to create new Version. - * - * @param \eZ\Publish\SPI\Limitation\Target\Version $version - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * - * @return bool|null - */ - private function evaluateVersionTarget( - Target\Version $version, - APILimitationValue $value - ): ?bool { - $accessVote = self::ACCESS_ABSTAIN; - - // ... unless there's a specific list of target translations - if (!empty($version->allContentTypeIdsList)) { - $accessVote = $this->evaluateMatchingAnyLimitation( - $version->allContentTypeIdsList, - $value->limitationValues - ); - } - - return $accessVote; - } - - /** - * Allow access if any of the given ContentTypes matches any of the limitation values. - * - * @param int[] $contentTypeIdsList - * @param string[] $limitationValues - * - * @return bool - */ - private function evaluateMatchingAnyLimitation( - array $contentTypeIdsList, - array $limitationValues - ): bool { - return empty(array_intersect(array_map('strval', $contentTypeIdsList), $limitationValues)) - ? self::ACCESS_DENIED - : self::ACCESS_GRANTED; - } -} diff --git a/eZ/Publish/Core/Limitation/LanguageLimitation/NewDraftEvaluator.php b/eZ/Publish/Core/Limitation/LanguageLimitation/NewDraftEvaluator.php deleted file mode 100644 index 15d23fe235..0000000000 --- a/eZ/Publish/Core/Limitation/LanguageLimitation/NewDraftEvaluator.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Limitation\LanguageLimitation; - -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\Core\Limitation\LanguageLimitationType; -use eZ\Publish\SPI\Limitation\Target; - -/** - * @internal for internal use by LanguageLimitation - */ -final class NewDraftEvaluator implements VersionTargetEvaluator -{ - public function accept(Target\Version $targetVersion): bool - { - return $targetVersion->newStatus === VersionInfo::STATUS_DRAFT; - } - - public function evaluate(Target\Version $targetVersion, Limitation $limitationValue): ?bool - { - return LanguageLimitationType::ACCESS_GRANTED; - } -} diff --git a/eZ/Publish/Core/Limitation/LanguageLimitation/VersionPublishingEvaluator.php b/eZ/Publish/Core/Limitation/LanguageLimitation/VersionPublishingEvaluator.php deleted file mode 100644 index 0b44c2156a..0000000000 --- a/eZ/Publish/Core/Limitation/LanguageLimitation/VersionPublishingEvaluator.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Limitation\LanguageLimitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\Core\Limitation\LanguageLimitationType; -use eZ\Publish\SPI\Limitation\Target; - -/** - * @internal for internal use by LanguageLimitation - */ -final class VersionPublishingEvaluator implements VersionTargetEvaluator -{ - public function accept(Target\Version $targetVersion): bool - { - return !empty($targetVersion->forPublishLanguageCodesList); - } - - /** - * Evaluate publishing a specific translation of a Version. - */ - public function evaluate(Target\Version $targetVersion, Limitation $limitationValue): ?bool - { - $diff = array_diff( - $targetVersion->forPublishLanguageCodesList, - $limitationValue->limitationValues - ); - - return empty($diff) - ? LanguageLimitationType::ACCESS_GRANTED - : LanguageLimitationType::ACCESS_DENIED; - } -} diff --git a/eZ/Publish/Core/Limitation/LanguageLimitation/VersionTargetEvaluator.php b/eZ/Publish/Core/Limitation/LanguageLimitation/VersionTargetEvaluator.php deleted file mode 100644 index 716a2e6e56..0000000000 --- a/eZ/Publish/Core/Limitation/LanguageLimitation/VersionTargetEvaluator.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Limitation\LanguageLimitation; - -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\SPI\Limitation\Target; - -/** - * @internal for internal use by LanguageLimitation - */ -interface VersionTargetEvaluator -{ - public function accept(Target\Version $targetVersion): bool; - - public function evaluate(Target\Version $targetVersion, Limitation $limitationValue): ?bool; -} diff --git a/eZ/Publish/Core/Limitation/LanguageLimitationType.php b/eZ/Publish/Core/Limitation/LanguageLimitationType.php deleted file mode 100644 index a4b1a260b9..0000000000 --- a/eZ/Publish/Core/Limitation/LanguageLimitationType.php +++ /dev/null @@ -1,340 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\LanguageLimitation as APILanguageLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Target; -use eZ\Publish\SPI\Limitation\TargetAwareType as SPITargetAwareLimitationType; -use eZ\Publish\SPI\Persistence\Content\Handler as SPIPersistenceContentHandler; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as SPIPersistenceLanguageHandler; -use eZ\Publish\SPI\Persistence\Content\VersionInfo as SPIVersionInfo; -use Ibexa\Contracts\Core\Limitation\Target\DestinationLocation as DestinationLocationTarget; - -/** - * LanguageLimitation is a Content limitation. - */ -class LanguageLimitationType implements SPITargetAwareLimitationType -{ - /** @var \eZ\Publish\SPI\Persistence\Content\Language\Handler */ - private $persistenceLanguageHandler; - - /** @var \eZ\Publish\SPI\Persistence\Content\Handler */ - private $persistenceContentHandler; - - /** @var \eZ\Publish\Core\Limitation\LanguageLimitation\VersionTargetEvaluator[] */ - private $versionTargetEvaluators; - - /** - * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $persistenceLanguageHandler - * @param \eZ\Publish\SPI\Persistence\Content\Handler $persistenceContentHandler - * @param \eZ\Publish\Core\Limitation\LanguageLimitation\VersionTargetEvaluator[] $versionTargetEvaluators - */ - public function __construct( - SPIPersistenceLanguageHandler $persistenceLanguageHandler, - SPIPersistenceContentHandler $persistenceContentHandler, - iterable $versionTargetEvaluators - ) { - $this->persistenceLanguageHandler = $persistenceLanguageHandler; - $this->persistenceContentHandler = $persistenceContentHandler; - $this->versionTargetEvaluators = $versionTargetEvaluators; - } - - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - */ - public function acceptValue(APILimitationValue $limitationValue): void - { - if (!$limitationValue instanceof APILanguageLimitation) { - throw new InvalidArgumentType( - '$limitationValue', - APILanguageLimitation::class, - $limitationValue - ); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType( - '$limitationValue->limitationValues', - 'array', - $limitationValue->limitationValues - ); - } - - foreach ($limitationValue->limitationValues as $key => $value) { - if (!is_string($value)) { - throw new InvalidArgumentType( - "\$limitationValue->limitationValues[{$key}]", - 'string', - $value - ); - } - } - } - - /** - * Makes sure every language code defined as limitation exists. - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue): array - { - $validationErrors = []; - $existingLanguages = $this->persistenceLanguageHandler->loadListByLanguageCodes( - $limitationValue->limitationValues - ); - $missingLanguages = array_diff( - $limitationValue->limitationValues, - array_keys($existingLanguages) - ); - if (!empty($missingLanguages)) { - $validationErrors[] = new ValidationError( - "limitationValues[] => '%languageCodes%' translation(s) do not exist", - null, - [ - 'languageCodes' => implode(', ', $missingLanguages), - ] - ); - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param array[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues): APILimitationValue - { - return new APILanguageLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target. - * - * {@inheritdoc} - */ - public function evaluate( - APILimitationValue $value, - APIUserReference $currentUser, - ValueObject $object, - array $targets = null - ): ?bool { - if (null === $targets) { - $targets = []; - } - - // the main focus here is an intent to update to a new Version and if not, validate if target is Location - foreach ($targets as $target) { - if (!$target instanceof Target) { - continue; - } - - if ($target instanceof Target\Version) { - $accessVote = $this->evaluateVersionTarget($target, $value); - } elseif ($target instanceof DestinationLocationTarget) { - $accessVote = $this->evaluateLocationTarget($target, $value); - } else { - continue; - } - - // continue evaluation of targets if there was no explicit grant/deny - if ($accessVote === self::ACCESS_ABSTAIN) { - continue; - } - - return $accessVote; - } - - // in other cases we need to evaluate object - return $this->evaluateObject($object, $value); - } - - /** - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * - * @return bool|null - */ - private function evaluateObject(ValueObject $object, APILimitationValue $value): ?bool - { - // by default abstain from making decision for unknown object - $accessVote = self::ACCESS_ABSTAIN; - - // load for evaluation VersionInfo for Content & ContentInfo objects - if ($object instanceof Content) { - $object = $object->getVersionInfo(); - } elseif ($object instanceof ContentInfo) { - $object = $this->tryLoadingVersionInfo($object); - if ($object === null) { - return self::ACCESS_DENIED; - } - } - - // cover creating Content Draft for new Content item - if ($object instanceof ContentCreateStruct) { - $accessVote = $this->evaluateContentCreateStruct($object, $value); - } elseif ($object instanceof VersionInfo || $object instanceof SPIVersionInfo) { - $accessVote = in_array($object->initialLanguageCode, $value->limitationValues, true) - ? self::ACCESS_GRANTED - : self::ACCESS_DENIED; - } - - return $accessVote; - } - - private function tryLoadingVersionInfo(ContentInfo $contentInfo): ?SPIVersionInfo - { - try { - return $this->persistenceContentHandler->loadVersionInfo( - $contentInfo->id, - $contentInfo->currentVersionNo - ); - } catch (NotFoundException $e) { - return null; - } - } - - /** - * Evaluate language codes of allowed translations for ContentCreateStruct. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $object - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * - * @return bool|null - */ - private function evaluateContentCreateStruct( - ContentCreateStruct $object, - APILimitationValue $value - ): ?bool { - $languageCodes = $this->getAllLanguageCodesFromCreateStruct($object); - - // check if object contains only allowed language codes - return empty(array_diff($languageCodes, $value->limitationValues)) - ? self::ACCESS_GRANTED - : self::ACCESS_DENIED; - } - - /** - * Evaluate permissions to create new Version. - * - * @param \eZ\Publish\SPI\Limitation\Target\Version $version - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * - * @return bool|null - */ - private function evaluateVersionTarget( - Target\Version $version, - APILimitationValue $value - ): ?bool { - $accessVote = self::ACCESS_ABSTAIN; - - foreach ($this->versionTargetEvaluators as $evaluator) { - if ($evaluator->accept($version)) { - $accessVote = $evaluator->evaluate($version, $value); - if ($accessVote === self::ACCESS_DENIED) { - return $accessVote; - } - } - } - - return $accessVote; - } - - private function evaluateLocationTarget( - DestinationLocationTarget $location, - APILimitationValue $value - ): ?bool { - $versionInfo = $this->tryLoadingVersionInfo($location->getTargetContentInfo()); - if ($versionInfo === null) { - return self::ACCESS_DENIED; - } - - return empty(array_diff($versionInfo->languageCodes, $value->limitationValues)) - ? self::ACCESS_GRANTED - : self::ACCESS_DENIED; - } - - /** - * Get unique list of language codes for all used translations, including mainLanguageCode. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct - * - * @return string[] - */ - private function getAllLanguageCodesFromCreateStruct( - ContentCreateStruct $contentCreateStruct - ): array { - $languageCodes = [$contentCreateStruct->mainLanguageCode]; - foreach ($contentCreateStruct->fields as $field) { - $languageCodes[] = $field->languageCode; - } - - return array_unique($languageCodes); - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - */ - public function getCriterion( - APILimitationValue $value, - APIUserReference $currentUser - ): CriterionInterface { - if (empty($value->limitationValues)) { - // A Policy should not have empty limitationValues stored - throw new BadStateException( - '$value', - '$value->limitationValues is empty' - ); - } - - // several limitation values: IN operation - return new Criterion\LanguageCode($value->limitationValues); - } - - /** - * For LanguageLimitationType it returns an empty array because schema is not deterministic. - * - * @see validate for business logic. - */ - public function valueSchema(): array - { - return []; - } -} diff --git a/eZ/Publish/Core/Limitation/LocationLimitationType.php b/eZ/Publish/Core/Limitation/LocationLimitationType.php deleted file mode 100644 index 237e19378c..0000000000 --- a/eZ/Publish/Core/Limitation/LocationLimitationType.php +++ /dev/null @@ -1,236 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation as APILocationLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; - -/** - * LocationLimitation is a Content limitation. - */ -class LocationLimitationType extends AbstractPersistenceLimitationType implements SPILimitationTypeInterface -{ - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APILocationLimitation) { - throw new InvalidArgumentType('$limitationValue', 'APILocationLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $id) { - if (!is_string($id) && !is_int($id)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - foreach ($limitationValue->limitationValues as $key => $id) { - try { - $this->persistence->locationHandler()->load($id); - } catch (APINotFoundException $e) { - $validationErrors[] = new ValidationError( - "limitationValues[%key%] => '%value%' does not exist in the backend", - null, - [ - 'value' => $id, - 'key' => $key, - ] - ); - } - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APILocationLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - if (!$value instanceof APILocationLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: APILocationLimitation'); - } - - if ($object instanceof ContentCreateStruct) { - return $this->evaluateForContentCreateStruct($value, $targets); - } elseif ($object instanceof Content) { - $object = $object->getVersionInfo()->getContentInfo(); - } elseif ($object instanceof VersionInfo) { - $object = $object->getContentInfo(); - } elseif (!$object instanceof ContentInfo) { - throw new InvalidArgumentException( - '$object', - 'Must be of type: ContentCreateStruct, Content, VersionInfo or ContentInfo' - ); - } - - // Load locations if no specific placement was provided - if ($targets === null) { - if ($object->published) { - $targets = $this->persistence->locationHandler()->loadLocationsByContent($object->id); - } elseif ($object->isTrashed()) { - $targets = $this->persistence->locationHandler()->loadLocationsByTrashContent($object->id); - } else { - // @todo Need support for draft locations to to work correctly - $targets = $this->persistence->locationHandler()->loadParentLocationsForDraftContent($object->id); - } - } - /** @var \eZ\Publish\API\Repository\Values\Content\Location[]|\eZ\Publish\SPI\Persistence\Content\Location[] $targets */ - $targets = array_filter($targets, static function ($target) { - return $target instanceof Location || $target instanceof SPILocation; - }); - - if (empty($targets)) { - throw new InvalidArgumentException('$targets', 'Must contain at least one Location object'); - } - - foreach ($targets as $target) { - if (in_array($target->id, $value->limitationValues)) { - return self::ACCESS_GRANTED; - } - } - - return self::ACCESS_DENIED; - } - - /** - * Evaluate permissions for ContentCreateStruct against LocationCreateStruct placements. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If $targets does not contain - * objects of type LocationCreateStruct - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param array|null $targets - * - * @return bool - */ - protected function evaluateForContentCreateStruct(APILimitationValue $value, array $targets = null) - { - // If targets is empty/null return false as user does not have access - // to content w/o location with this limitation - if (empty($targets)) { - return false; - } - - $hasMandatoryTarget = false; - foreach ($targets as $target) { - if ($target instanceof LocationCreateStruct) { - $hasMandatoryTarget = true; - - // For ContentCreateStruct all placements must match - if (!in_array($target->parentLocationId, $value->limitationValues)) { - return false; - } - } - } - - if (false === $hasMandatoryTarget) { - throw new InvalidArgumentException( - '$targets', - 'If $object is ContentCreateStruct, it must contain LocationCreateStruct objects' - ); - } - - return true; - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - if (empty($value->limitationValues)) { - // A Policy should not have empty limitationValues stored - throw new \RuntimeException('$value->limitationValues is empty'); - } - - if (!isset($value->limitationValues[1])) { - // 1 limitation value: EQ operation - return new Criterion\LocationId($value->limitationValues[0]); - } - - // several limitation values: IN operation - return new Criterion\LocationId($value->limitationValues); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - return self::VALUE_SCHEMA_LOCATION_ID; - } -} diff --git a/eZ/Publish/Core/Limitation/NewObjectStateLimitationType.php b/eZ/Publish/Core/Limitation/NewObjectStateLimitationType.php deleted file mode 100644 index 7d6acce2db..0000000000 --- a/eZ/Publish/Core/Limitation/NewObjectStateLimitationType.php +++ /dev/null @@ -1,168 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectState; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\NewObjectStateLimitation as APINewObjectStateLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; -use eZ\Publish\SPI\Persistence\Content\ObjectState as SPIObjectState; - -/** - * NewObjectStateLimitationType is a Content Limitation used on 'state' 'assign' function. - */ -class NewObjectStateLimitationType extends AbstractPersistenceLimitationType implements SPILimitationTypeInterface -{ - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APINewObjectStateLimitation) { - throw new InvalidArgumentType('$limitationValue', 'NewObjectStateLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $id) { - if (!is_string($id) && !is_int($id)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - foreach ($limitationValue->limitationValues as $key => $id) { - try { - $this->persistence->objectStateHandler()->load($id); - } catch (APINotFoundException $e) { - $validationErrors[] = new ValidationError( - "limitationValues[%key%] => '%value%' does not exist in the backend", - null, - [ - 'value' => $id, - 'key' => $key, - ] - ); - } - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APINewObjectStateLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - if (!$value instanceof APINewObjectStateLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: NewObjectStateLimitation'); - } - - if (!$object instanceof ContentInfo && !$object instanceof Content && !$object instanceof VersionInfo) { - throw new InvalidArgumentException('$object', 'Must be of type: Content, VersionInfo or ContentInfo'); - } - - if (empty($targets)) { - throw new InvalidArgumentException('$targets', 'Must contain ObjectState objects'); - } - - if (empty($value->limitationValues)) { - return false; - } - - foreach ($targets as $target) { - if (!$target instanceof ObjectState && !$target instanceof SPIObjectState) { - throw new InvalidArgumentException('$targets', 'Must contain ObjectState objects'); - } - - if (!in_array($target->id, $value->limitationValues)) { - return false; - } - } - - return true; - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException Not applicable, needs context of new state. - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - throw new NotImplementedException(__METHOD__); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - throw new NotImplementedException(__METHOD__); - } -} diff --git a/eZ/Publish/Core/Limitation/NewSectionLimitationType.php b/eZ/Publish/Core/Limitation/NewSectionLimitationType.php deleted file mode 100644 index f0c26a2151..0000000000 --- a/eZ/Publish/Core/Limitation/NewSectionLimitationType.php +++ /dev/null @@ -1,200 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchAll; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchNone; -use eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface; -use eZ\Publish\API\Repository\Values\Content\Section; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\NewSectionLimitation as APINewSectionLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; -use eZ\Publish\SPI\Persistence\Content\Section as SPISection; - -/** - * NewSectionLimitation is a Content Limitation used on 'section' 'assign' function. - */ -class NewSectionLimitationType extends AbstractPersistenceLimitationType implements SPILimitationTypeInterface, TargetOnlyLimitationType -{ - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APINewSectionLimitation) { - throw new InvalidArgumentType('$limitationValue', 'APINewSectionLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $id) { - if (!is_string($id) && !is_int($id)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - foreach ($limitationValue->limitationValues as $key => $id) { - try { - $this->persistence->sectionHandler()->load($id); - } catch (APINotFoundException $e) { - $validationErrors[] = new ValidationError( - "limitationValues[%key%] => '%value%' does not exist in the backend", - null, - [ - 'value' => $id, - 'key' => $key, - ] - ); - } - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APINewSectionLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - if (!$value instanceof APINewSectionLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: APINewSectionLimitation'); - } - - if (!$object instanceof ContentInfo && !$object instanceof Content && !$object instanceof VersionInfo) { - throw new InvalidArgumentException('$object', 'Must be of type: Content, VersionInfo, ContentInfo'); - } - - if (empty($targets)) { - throw new InvalidArgumentException('$targets', 'Must contain Section objects'); - } - - if (empty($value->limitationValues)) { - return false; - } - - return $this->doEvaluate($value, $targets); - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException Not applicable, needs context of new section. - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } - - /** - * {@inheritdoc} - */ - public function getCriterionByTarget(APILimitationValue $value, APIUserReference $currentUser, ?array $targets): CriterionInterface - { - if (empty($targets)) { - throw new InvalidArgumentException('$targets', 'Must contain Section objects'); - } - - if ($this->doEvaluate($value, $targets)) { - return new MatchAll(); - } else { - return new MatchNone(); - } - } - - /** - * Returns true if given limitation value allows all given sections. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param array|null $targets - * - * @return bool - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - */ - private function doEvaluate(APILimitationValue $value, array $targets): bool - { - foreach ($targets as $target) { - if (!$target instanceof Section && !$target instanceof SPISection) { - throw new InvalidArgumentException('$targets', 'Must contain Section objects'); - } - - if (!in_array($target->id, $value->limitationValues)) { - return false; - } - } - - return true; - } -} diff --git a/eZ/Publish/Core/Limitation/OwnerLimitationType.php b/eZ/Publish/Core/Limitation/OwnerLimitationType.php deleted file mode 100644 index aa1d0e425a..0000000000 --- a/eZ/Publish/Core/Limitation/OwnerLimitationType.php +++ /dev/null @@ -1,189 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\OwnerLimitation as APIOwnerLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; - -/** - * OwnerLimitation is a Content limitation. - */ -class OwnerLimitationType extends AbstractPersistenceLimitationType implements SPILimitationTypeInterface -{ - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APIOwnerLimitation) { - throw new InvalidArgumentType('$limitationValue', 'APIOwnerLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $value) { - // Cast integers passed as string to int - if (is_string($value) && ctype_digit($value)) { - $limitationValue->limitationValues[$key] = (int)$value; - } elseif (!is_int($value)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int', $value); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - foreach ($limitationValue->limitationValues as $key => $value) { - if ($value !== 1 && $value !== 2) { - $validationErrors[] = new ValidationError( - "limitationValues[%key%] => '%value%' must be either 1 (owner) or 2 (session)", - null, - [ - 'value' => $value, - 'key' => $key, - ] - ); - } - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APIOwnerLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - * - * @todo Add support for $limitationValues[0] == 2 when session values can be injected somehow, or deprecate - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - if (!$value instanceof APIOwnerLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: APIOwnerLimitation'); - } - - if ($value->limitationValues[0] != 1 && $value->limitationValues[0] != 2) { - throw new BadStateException( - 'Owner limitation', - 'Expected Limitation value to be 1 or 2 instead of' . $value->limitationValues[0] - ); - } - - if ($object instanceof Content) { - $object = $object->getVersionInfo()->getContentInfo(); - } elseif ($object instanceof VersionInfo) { - $object = $object->getContentInfo(); - } elseif (!$object instanceof ContentInfo && !$object instanceof ContentCreateStruct) { - throw new InvalidArgumentException( - '$object', - 'Must be of type: ContentCreateStruct, Content, VersionInfo or ContentInfo' - ); - } - - $userId = $currentUser->getUserId(); - - /* - * @var $object ContentInfo - */ - $isOwner = $object->ownerId === $userId; - $isSelf = $object instanceof ContentInfo && $object->id === $userId; - - return $isOwner || $isSelf; - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - * - * @todo Add support for $limitationValues[0] == 2 when session values can be injected somehow, or deprecate - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - if (empty($value->limitationValues)) { - // A Policy should not have empty limitationValues stored - throw new \RuntimeException('$value->limitationValues is empty'); - } - - if ($value->limitationValues[0] != 1 && $value->limitationValues[0] != 2) { - throw new BadStateException( - 'Parent User Group limitation', - 'Expected Limitation value to be 1 or 2 instead of' . $value->limitationValues[0] - ); - } - - return new Criterion\UserMetadata( - Criterion\UserMetadata::OWNER, - Criterion\Operator::EQ, - $currentUser->getUserId() - ); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } -} diff --git a/eZ/Publish/Core/Limitation/ParentContentTypeLimitationType.php b/eZ/Publish/Core/Limitation/ParentContentTypeLimitationType.php deleted file mode 100644 index ff93905f77..0000000000 --- a/eZ/Publish/Core/Limitation/ParentContentTypeLimitationType.php +++ /dev/null @@ -1,258 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\ParentContentTypeLimitation as APIParentContentTypeLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; - -/** - * ParentContentTypeLimitation is a Content limitation. - */ -class ParentContentTypeLimitationType extends AbstractPersistenceLimitationType implements SPILimitationTypeInterface -{ - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APIParentContentTypeLimitation) { - throw new InvalidArgumentType('$limitationValue', 'APIParentContentTypeLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $id) { - if (!is_string($id) && !is_int($id)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - foreach ($limitationValue->limitationValues as $key => $id) { - try { - $this->persistence->contentTypeHandler()->load($id); - } catch (APINotFoundException $e) { - $validationErrors[] = new ValidationError( - "limitationValues[%key%] => '%value%' does not exist in the backend", - null, - [ - 'value' => $id, - 'key' => $key, - ] - ); - } - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APIParentContentTypeLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - if (!$value instanceof APIParentContentTypeLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: APIParentContentTypeLimitation'); - } - - if ($object instanceof ContentCreateStruct) { - return $this->evaluateForContentCreateStruct($value, $targets); - } elseif ($object instanceof Content) { - $object = $object->getVersionInfo()->getContentInfo(); - } elseif ($object instanceof VersionInfo) { - $object = $object->getContentInfo(); - } elseif (!$object instanceof ContentInfo) { - throw new InvalidArgumentException( - '$object', - 'Must be of type: ContentCreateStruct, Content, VersionInfo or ContentInfo' - ); - } - - // Try to load locations if no targets were provided - if (empty($targets)) { - if ($object->published) { - $targets = $this->loadParentLocations($object); - } else { - // @todo Need support for draft locations to to work correctly - $targets = $this->persistence->locationHandler()->loadParentLocationsForDraftContent($object->id); - } - } - - // If targets is empty/null return false as user does not have access - // to content w/o location with this limitation - if (empty($targets)) { - return false; - } - - foreach ($targets as $target) { - if ($target instanceof LocationCreateStruct) { - $target = $this->persistence->locationHandler()->load($target->parentLocationId); - } - - if ($target instanceof Location) { - $contentTypeId = $target->getContentInfo()->contentTypeId; - } elseif ($target instanceof SPILocation) { - $spiContentInfo = $this->persistence->contentHandler()->loadContentInfo($target->contentId); - $contentTypeId = $spiContentInfo->contentTypeId; - } else { - throw new InvalidArgumentException( - '$targets', - 'Must contain Location or LocationCreateStruct objects' - ); - } - - if (!in_array($contentTypeId, $value->limitationValues)) { - return false; - } - } - - return true; - } - - /** - * Evaluate permissions for ContentCreateStruct against LocationCreateStruct placements. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If $targets does not contain - * objects of type LocationCreateStruct - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param array $targets - * - * @return bool - */ - protected function evaluateForContentCreateStruct(APILimitationValue $value, array $targets = null) - { - // If targets is empty/null return false as user does not have access - // to content w/o location with this limitation - if (empty($targets)) { - return false; - } - - $hasMandatoryTarget = false; - foreach ($targets as $target) { - if ($target instanceof LocationCreateStruct) { - $hasMandatoryTarget = true; - $location = $this->persistence->locationHandler()->load($target->parentLocationId); - $contentTypeId = $this->persistence->contentHandler()->loadContentInfo($location->contentId)->contentTypeId; - - if (!in_array($contentTypeId, $value->limitationValues)) { - return false; - } - } - } - - if (false === $hasMandatoryTarget) { - throw new InvalidArgumentException( - '$targets', - 'If $object is ContentCreateStruct, it must contain LocationCreateStruct objects' - ); - } - - return true; - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return \eZ\Publish\SPI\Persistence\Content\Location[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - private function loadParentLocations(ContentInfo $contentInfo) - { - $locations = $this->persistence->locationHandler()->loadLocationsByContent($contentInfo->id); - $parentLocations = []; - foreach ($locations as $location) { - if ($location->depth > 0) { - $parentLocations[] = $this->persistence->locationHandler()->load($location->parentId); - } - } - - return $parentLocations; - } -} diff --git a/eZ/Publish/Core/Limitation/ParentDepthLimitationType.php b/eZ/Publish/Core/Limitation/ParentDepthLimitationType.php deleted file mode 100644 index bbeb9748a6..0000000000 --- a/eZ/Publish/Core/Limitation/ParentDepthLimitationType.php +++ /dev/null @@ -1,217 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\ParentDepthLimitation as APIParentDepthLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; - -/** - * ParentDepthLimitation is a Content limitation. - */ -class ParentDepthLimitationType extends AbstractPersistenceLimitationType implements SPILimitationTypeInterface -{ - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APIParentDepthLimitation) { - throw new InvalidArgumentType('$limitationValue', 'APIParentDepthLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $value) { - // Cast integers passed as string to int - if (is_string($value) && ctype_digit($value)) { - $limitationValue->limitationValues[$key] = (int)$value; - } elseif (!is_int($value)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int', $value); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APIParentDepthLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - if (!$value instanceof APIParentDepthLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: APIParentDepthLimitation'); - } - - if ($object instanceof ContentCreateStruct) { - return $this->evaluateForContentCreateStruct($value, $targets); - } elseif ($object instanceof Content) { - $object = $object->getVersionInfo()->getContentInfo(); - } elseif ($object instanceof VersionInfo) { - $object = $object->getContentInfo(); - } elseif (!$object instanceof ContentInfo) { - throw new InvalidArgumentException( - '$object', - 'Must be of type: ContentCreateStruct, Content, VersionInfo or ContentInfo' - ); - } - - // Load locations if no specific placement was provided - if (empty($targets)) { - if ($object->published) { - $targets = $this->persistence->locationHandler()->loadLocationsByContent($object->id); - } else { - // @todo Need support for draft locations to to work correctly - $targets = $this->persistence->locationHandler()->loadParentLocationsForDraftContent($object->id); - } - } - - // Parent Limitations are usually used by content/create where target is specified, - // so we return false if not provided. - if (empty($targets)) { - return false; - } - - foreach ($targets as $target) { - if ($target instanceof Location || $target instanceof SPILocation) { - $depth = $target->depth; - } else { - throw new InvalidArgumentException( - '$targets', - 'Must contain Location objects' - ); - } - - // All placements must match - if (!in_array($depth, $value->limitationValues)) { - return false; - } - } - - return true; - } - - /** - * Evaluate permissions for ContentCreateStruct against LocationCreateStruct placements. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If $targets does not contain - * objects of type LocationCreateStruct - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param array|null $targets - * - * @return bool - */ - protected function evaluateForContentCreateStruct(APILimitationValue $value, array $targets = null) - { - // If targets is empty/null return false as user does not have access - // to content w/o location with this limitation - if (empty($targets)) { - return false; - } - $hasMandatoryTarget = false; - foreach ($targets as $target) { - if ($target instanceof LocationCreateStruct) { - $hasMandatoryTarget = true; - $depth = $this->persistence->locationHandler()->load($target->parentLocationId)->depth; - - // All placements must match - if (!in_array($depth, $value->limitationValues)) { - return false; - } - } - } - - if (false === $hasMandatoryTarget) { - throw new InvalidArgumentException( - '$targets', - 'If $object is ContentCreateStruct, it must contain LocationCreateStruct objects' - ); - } - - return true; - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } -} diff --git a/eZ/Publish/Core/Limitation/ParentOwnerLimitationType.php b/eZ/Publish/Core/Limitation/ParentOwnerLimitationType.php deleted file mode 100644 index 90c90c547a..0000000000 --- a/eZ/Publish/Core/Limitation/ParentOwnerLimitationType.php +++ /dev/null @@ -1,190 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\ParentOwnerLimitation as APIParentOwnerLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Target; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; - -/** - * ParentOwnerLimitation is a Content limitation. - */ -class ParentOwnerLimitationType extends AbstractPersistenceLimitationType implements SPILimitationTypeInterface -{ - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APIParentOwnerLimitation) { - throw new InvalidArgumentType('$limitationValue', 'APIParentOwnerLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $value) { - // Cast integers passed as string to int - if (is_string($value) && ctype_digit($value)) { - $limitationValue->limitationValues[$key] = (int)$value; - } elseif (!is_int($value)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int', $value); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - foreach ($limitationValue->limitationValues as $key => $value) { - if ($value !== 1 && $value !== 2) { - $validationErrors[] = new ValidationError( - "limitationValues[%key%] => '%value%' must be either 1 (owner) or 2 (session)", - null, - [ - 'value' => $value, - 'key' => $key, - ] - ); - } - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APIParentOwnerLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - * - * @todo Add support for $limitationValues[0] == 2 when session values can be injected somehow - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - if (!$value instanceof APIParentOwnerLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: APIParentOwnerLimitation'); - } - - if ($value->limitationValues[0] != 1 && $value->limitationValues[0] != 2) { - throw new BadStateException( - 'Parent Owner limitation', - 'Expected Limitation value to be 1 or 2 instead of' . $value->limitationValues[0] - ); - } - - // Parent Limitations are usually used by content/create where target is specified, so we return false if not provided. - if (empty($targets)) { - return false; - } - - $hasMandatoryTarget = false; - foreach ($targets as $target) { - if ($target instanceof LocationCreateStruct) { - $hasMandatoryTarget = true; - $target = $this->persistence->locationHandler()->load($target->parentLocationId); - } - - if ($target instanceof Location) { - $hasMandatoryTarget = true; - $targetContentInfo = $target->getContentInfo(); - } elseif ($target instanceof SPILocation) { - $hasMandatoryTarget = true; - $targetContentInfo = $this->persistence->contentHandler()->loadContentInfo($target->contentId); - } else { - continue; - } - - $userId = $currentUser->getUserId(); - - $isOwner = $targetContentInfo->ownerId === $userId; - $isSelf = $targetContentInfo->id === $userId; - - if (!($isOwner || $isSelf)) { - return false; - } - } - - if (false === $hasMandatoryTarget) { - throw new InvalidArgumentException( - '$targets', - 'Must contain Location or LocationCreateStruct objects' - ); - } - - return true; - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } -} diff --git a/eZ/Publish/Core/Limitation/ParentUserGroupLimitationType.php b/eZ/Publish/Core/Limitation/ParentUserGroupLimitationType.php deleted file mode 100644 index b9077d3a2d..0000000000 --- a/eZ/Publish/Core/Limitation/ParentUserGroupLimitationType.php +++ /dev/null @@ -1,214 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\ParentUserGroupLimitation as APIParentUserGroupLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Target; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; - -/** - * ParentUserGroupLimitation is a Content limitation. - */ -class ParentUserGroupLimitationType extends AbstractPersistenceLimitationType implements SPILimitationTypeInterface -{ - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APIParentUserGroupLimitation) { - throw new InvalidArgumentType('$limitationValue', 'APIParentUserGroupLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $value) { - // Accept a true value for b/c with 5.0 - if ($value === true) { - $limitationValue->limitationValues[$key] = 1; - } elseif (is_string($value) && ctype_digit($value)) { - // Cast integers passed as string to int - $limitationValue->limitationValues[$key] = (int)$value; - } elseif (!is_int($value)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int', $value); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - foreach ($limitationValue->limitationValues as $key => $value) { - if ($value !== 1) { - $validationErrors[] = new ValidationError( - "limitationValues[%key%] => '%value%' must be 1 (owner)", - null, - [ - 'value' => $value, - 'key' => $key, - ] - ); - } - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APIParentUserGroupLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - if (!$value instanceof APIParentUserGroupLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: APIParentUserGroupLimitation'); - } - - if ($value->limitationValues[0] != 1) { - throw new BadStateException( - 'Parent User Group limitation', - 'Expected Limitation value to be 1 instead of' . $value->limitationValues[0] - ); - } - - // Parent Limitations are usually used by content/create where target is specified, so we return false if not provided. - if (empty($targets)) { - return false; - } - - $locationHandler = $this->persistence->locationHandler(); - $currentUserLocations = $locationHandler->loadLocationsByContent($currentUser->getUserId()); - if (empty($currentUserLocations)) { - return false; - } - - $hasMandatoryTarget = false; - foreach ($targets as $target) { - if ($target instanceof LocationCreateStruct) { - $hasMandatoryTarget = true; - $target = $locationHandler->load($target->parentLocationId); - } - - if ($target instanceof Location) { - $hasMandatoryTarget = true; - // $target is assumed to be parent in this case - $parentOwnerId = $target->getContentInfo()->ownerId; - } elseif ($target instanceof SPILocation) { - $hasMandatoryTarget = true; - // $target is assumed to be parent in this case - $spiContentInfo = $this->persistence->contentHandler()->loadContentInfo($target->contentId); - $parentOwnerId = $spiContentInfo->ownerId; - } else { - continue; - } - - if ($parentOwnerId === $currentUser->getUserId()) { - continue; - } - - /* - * As long as SPI userHandler and API UserService does not speak the same language, this is the ugly truth; - */ - $locationHandler = $this->persistence->locationHandler(); - $parentOwnerLocations = $locationHandler->loadLocationsByContent($parentOwnerId); - if (empty($parentOwnerLocations)) { - return false; - } - - foreach ($parentOwnerLocations as $parentOwnerLocation) { - foreach ($currentUserLocations as $currentUserLocation) { - if ($parentOwnerLocation->parentId === $currentUserLocation->parentId) { - continue 3; - } - } - } - - return false; - } - - if (false === $hasMandatoryTarget) { - throw new InvalidArgumentException( - '$targets', - 'Must contain Location or LocationCreateStruct objects' - ); - } - - return true; - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } -} diff --git a/eZ/Publish/Core/Limitation/SectionLimitationType.php b/eZ/Publish/Core/Limitation/SectionLimitationType.php deleted file mode 100644 index 4851fee373..0000000000 --- a/eZ/Publish/Core/Limitation/SectionLimitationType.php +++ /dev/null @@ -1,180 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Section; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation as APISectionLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; - -/** - * SectionLimitation is a Content Limitation & a Role Limitation. - */ -class SectionLimitationType extends AbstractPersistenceLimitationType implements SPILimitationTypeInterface -{ - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APISectionLimitation) { - throw new InvalidArgumentType('$limitationValue', 'APISectionLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $id) { - if (!is_string($id) && !is_int($id)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - foreach ($limitationValue->limitationValues as $key => $id) { - try { - $this->persistence->sectionHandler()->load($id); - } catch (APINotFoundException $e) { - $validationErrors[] = new ValidationError( - "limitationValues[%key%] => '%value%' does not exist in the backend", - null, - [ - 'value' => $id, - 'key' => $key, - ] - ); - } - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APISectionLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - if (!$value instanceof APISectionLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: APISectionLimitation'); - } - - if (empty($value->limitationValues)) { - return false; - } - - /** - * Two cases supported: - * 1. $object is Section, for possible future support on Section limitations, i.e. be able to limit section/edit - * 2. $object is Content[Info]/VersionInfo, for all existing content policies, to limit by Section. - */ - if ($object instanceof Section) { - return in_array($object->id, $value->limitationValues); - } elseif ($object instanceof Content) { - $object = $object->getVersionInfo()->getContentInfo(); - } elseif ($object instanceof VersionInfo) { - $object = $object->getContentInfo(); - } elseif (!$object instanceof ContentInfo && !$object instanceof ContentCreateStruct) { - // As this is Role limitation we need to signal abstain on unsupported $object - return self::ACCESS_ABSTAIN; - } - - /* - * We ignore Targets here, they are only interesting in NewState limitation as we on this one is more interested - * the section already assigned to object. - * - * @var $object ContentInfo|ContentCreateStruct - */ - return in_array($object->sectionId, $value->limitationValues); - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - if (empty($value->limitationValues)) { - // A Policy should not have empty limitationValues stored - throw new \RuntimeException('$value->limitationValues is empty'); - } - - if (!isset($value->limitationValues[1])) { - // 1 limitation value: EQ operation - return new Criterion\SectionId($value->limitationValues[0]); - } - - // several limitation values: IN operation - return new Criterion\SectionId($value->limitationValues); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } -} diff --git a/eZ/Publish/Core/Limitation/SiteAccessLimitationType.php b/eZ/Publish/Core/Limitation/SiteAccessLimitationType.php deleted file mode 100644 index 85b666717b..0000000000 --- a/eZ/Publish/Core/Limitation/SiteAccessLimitationType.php +++ /dev/null @@ -1,184 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\SiteAccessLimitation as APISiteAccessLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; - -/** - * SiteAccessLimitation is a User limitation. - */ -class SiteAccessLimitationType implements SPILimitationTypeInterface -{ - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface */ - private $siteAccessService; - - public function __construct( - SiteAccess\SiteAccessServiceInterface $siteAccessService - ) { - $this->siteAccessService = $siteAccessService; - } - - /** - * Generates the SiteAccess value as CRC32. - */ - public function generateSiteAccessValue(string $sa): string - { - return sprintf('%u', crc32($sa)); - } - - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APISiteAccessLimitation) { - throw new InvalidArgumentType('$limitationValue', 'APISiteAccessLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $value) { - // Value must be a CRC32, so can be either as string or integer. - if (!is_string($value) && !is_int($value)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'string or integer', $value); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - $siteAccessList = $this->getSiteAccessList(); - foreach ($limitationValue->limitationValues as $key => $value) { - if (!isset($siteAccessList[$value])) { - $validationErrors[] = new ValidationError( - "\$limitationValue->limitationValues[%key%] => Invalid SiteAccess value \"$value\"", - null, - [ - 'value' => $value, - 'key' => $key, - ] - ); - } - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APISiteAccessLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * SiteAccess limitation takes a SiteAccess as ValueObject, and is hence like in legacy only suitable for user/login - * and similar policies. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - if (!$value instanceof APISiteAccessLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: APISiteAccessLimitation'); - } - - if (!$object instanceof SiteAccess) { - throw new InvalidArgumentException('$object', 'Must be of type: SiteAccess'); - } - - if (empty($value->limitationValues)) { - return false; - } - - if (empty($object->name)) { - return false; - } - - $currentSiteAccessHash = $this->generateSiteAccessValue($object->name); - - return in_array($currentSiteAccessHash, $value->limitationValues); - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } - - /** - * @return string[] - */ - private function getSiteAccessList(): array - { - $siteAccessList = []; - foreach ($this->siteAccessService->getAll() as $sa) { - $siteAccessList[$this->generateSiteAccessValue($sa->name)] = $sa->name; - } - - return $siteAccessList; - } -} diff --git a/eZ/Publish/Core/Limitation/StatusLimitationType.php b/eZ/Publish/Core/Limitation/StatusLimitationType.php deleted file mode 100644 index a08f489275..0000000000 --- a/eZ/Publish/Core/Limitation/StatusLimitationType.php +++ /dev/null @@ -1,163 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\StatusLimitation as APIStatusLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; - -/** - * StatusLimitation is a Content Limitation. - */ -class StatusLimitationType implements SPILimitationTypeInterface -{ - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APIStatusLimitation) { - throw new InvalidArgumentType('$limitationValue', 'APIStatusLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $id) { - if (!is_string($id) && !is_int($id)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - // For limitation values used here see \eZ\Publish\SPI\Persistence\Content\VersionInfo::Status_* constants. - $availableStatusSet = [ - VersionInfo::STATUS_DRAFT => true, - VersionInfo::STATUS_PUBLISHED => true, - VersionInfo::STATUS_ARCHIVED => true, - ]; - - $validationErrors = []; - foreach ($limitationValue->limitationValues as $key => $status) { - if (!isset($availableStatusSet[$status])) { - $validationErrors[] = new ValidationError( - "limitationValues[%key%] => '%value%' does not exist in the backend", - null, - [ - 'value' => $status, - 'key' => $key, - ] - ); - } - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APIStatusLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - if (!$value instanceof APIStatusLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: APIStatusLimitation'); - } - - if ($object instanceof Content) { - $object = $object->getVersionInfo(); - } elseif (!$object instanceof VersionInfo) { - throw new InvalidArgumentException( - '$object', - 'Must be of type: Content or VersionInfo' - ); - } - - if (empty($value->limitationValues)) { - return false; - } - - /* - * @var $object \eZ\Publish\API\Repository\Values\Content\VersionInfo - */ - return in_array($object->status, $value->limitationValues); - } - - /** - * Returns Criterion for use in find() query. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException If the limitation does not support - * being used as a Criterion. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - throw new NotImplementedException('Status Limitation Criterion'); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - throw new NotImplementedException(__METHOD__); - } -} diff --git a/eZ/Publish/Core/Limitation/SubtreeLimitationType.php b/eZ/Publish/Core/Limitation/SubtreeLimitationType.php deleted file mode 100644 index 427e6543aa..0000000000 --- a/eZ/Publish/Core/Limitation/SubtreeLimitationType.php +++ /dev/null @@ -1,270 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation as APISubtreeLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\Repository\Values\Content\Query\Criterion\PermissionSubtree; -use eZ\Publish\SPI\Limitation\Target\Version; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; - -/** - * SubtreeLimitation is a Content Limitation & a Role Limitation. - */ -class SubtreeLimitationType extends AbstractPersistenceLimitationType implements SPILimitationTypeInterface -{ - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APISubtreeLimitation) { - throw new InvalidArgumentType('$limitationValue', 'APISubtreeLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $path) { - if (!is_string($path)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'string', $path); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - foreach ($limitationValue->limitationValues as $key => $path) { - try { - $pathArray = explode('/', trim($path, '/')); - $subtreeRootLocationId = end($pathArray); - $spiLocation = $this->persistence->locationHandler()->load($subtreeRootLocationId); - } catch (APINotFoundException $e) { - $validationErrors[] = new ValidationError( - "limitationValues[%key%] => '%value%' does not exist in the backend", - null, - [ - 'value' => $path, - 'key' => $key, - ] - ); - - continue; - } - - if (strpos($spiLocation->pathString, $path) !== 0) { - $validationErrors[] = new ValidationError( - "limitationValues[%key%] => '%value%' does not equal Location's path string: '%path_string%'", - null, - [ - 'value' => $path, - 'key' => $key, - 'path_string' => $spiLocation->pathString, - ] - ); - } - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APISubtreeLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - $targets = $targets ?? []; - - if (!$value instanceof APISubtreeLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: APISubtreeLimitation'); - } - - if ($object instanceof ContentCreateStruct) { - return $this->evaluateForContentCreateStruct($value, $targets); - } elseif ($object instanceof Content) { - $object = $object->getVersionInfo()->getContentInfo(); - } elseif ($object instanceof VersionInfo) { - $object = $object->getContentInfo(); - } elseif (!$object instanceof ContentInfo) { - // As this is Role limitation we need to signal abstain on unsupported $object - return self::ACCESS_ABSTAIN; - } - - $targets = array_filter($targets, static function ($target) { - return !$target instanceof Version; - }); - - // Load locations if no specific placement was provided - if (empty($targets)) { - if ($object->isTrashed()) { - $targets = $this->persistence->locationHandler()->loadLocationsByTrashContent($object->id); - } elseif ($object->isPublished()) { - $targets = $this->persistence->locationHandler()->loadLocationsByContent($object->id); - } else { - // @todo Need support for draft locations to work correctly - $targets = $this->persistence->locationHandler()->loadParentLocationsForDraftContent($object->id); - } - } - - foreach ($targets as $target) { - if (!$target instanceof Location && !$target instanceof SPILocation) { - // As this is Role limitation we need to signal abstain on unsupported $targets - return self::ACCESS_ABSTAIN; - } - - foreach ($value->limitationValues as $limitationPathString) { - if ($target->pathString === $limitationPathString) { - return true; - } - if (strpos($target->pathString, $limitationPathString) === 0) { - return true; - } - } - } - - return false; - } - - /** - * Evaluate permissions for ContentCreateStruct against LocationCreateStruct placements. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If $targets does not contain - * objects of type LocationCreateStruct - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param array $targets - * - * @return bool - */ - protected function evaluateForContentCreateStruct(APILimitationValue $value, array $targets) - { - // If targets is empty/null return false as user does not have access - // to content w/o location with this limitation - if (empty($targets)) { - return false; - } - - $hasLocationCreateStruct = false; - foreach ($targets as $target) { - if (!$target instanceof LocationCreateStruct) { - continue; - } - - $hasLocationCreateStruct = true; - $target = $this->persistence->locationHandler()->load($target->parentLocationId); - - // For ContentCreateStruct all placements must match - foreach ($value->limitationValues as $limitationPathString) { - if ($target->pathString === $limitationPathString) { - continue 2; - } - if (strpos($target->pathString, $limitationPathString) === 0) { - continue 2; - } - } - - return false; - } - - if (false === $hasLocationCreateStruct) { - throw new InvalidArgumentException( - '$targets', - 'If $object is ContentCreateStruct, it must contain LocationCreateStruct objects' - ); - } - - return true; - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - if (empty($value->limitationValues)) { - // A Policy should not have empty limitationValues store - throw new \RuntimeException('$value->limitationValues is empty'); - } - - if (!isset($value->limitationValues[1])) { - // 1 limitation value: EQ operation - return new PermissionSubtree($value->limitationValues[0]); - } - - // several limitation values: IN operation - return new PermissionSubtree($value->limitationValues); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - return self::VALUE_SCHEMA_LOCATION_PATH; - } -} diff --git a/eZ/Publish/Core/Limitation/TargetOnlyLimitationType.php b/eZ/Publish/Core/Limitation/TargetOnlyLimitationType.php deleted file mode 100644 index 2b717f23b8..0000000000 --- a/eZ/Publish/Core/Limitation/TargetOnlyLimitationType.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\SPI\Limitation\Type as LimitationTypeInterface; - -/** - * Limitation type which doesn't take $object into consideration while evaluation. - * - * @see \eZ\Publish\Core\Repository\Permission\PermissionCriterionResolver::getPermissionsCriterion - */ -interface TargetOnlyLimitationType extends LimitationTypeInterface -{ - /** - * Returns criterion based on given $target for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param array|null $targets - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function getCriterionByTarget(APILimitationValue $value, APIUserReference $currentUser, ?array $targets): CriterionInterface; -} diff --git a/eZ/Publish/Core/Limitation/Tests/Base.php b/eZ/Publish/Core/Limitation/Tests/Base.php deleted file mode 100644 index 8592587a88..0000000000 --- a/eZ/Publish/Core/Limitation/Tests/Base.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation\Tests; - -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\SPI\Persistence\Handler as SPIHandler; -use PHPUnit\Framework\TestCase; - -abstract class Base extends TestCase -{ - /** @var \eZ\Publish\SPI\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject */ - private $persistenceHandlerMock; - - /** @var \eZ\Publish\API\Repository\Values\User\User|\PHPUnit\Framework\MockObject\MockObject */ - private $userMock; - - /** - * @param array $mockMethods For specifying the methods to mock, all by default - * - * @return \eZ\Publish\SPI\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject - */ - public function getPersistenceMock(array $mockMethods = []) - { - if ($this->persistenceHandlerMock !== null) { - return $this->persistenceHandlerMock; - } - - return $this->persistenceHandlerMock = $this->createMock(SPIHandler::class); - } - - /** - * @param array $mockMethods For specifying the methods to mock, all by default - * - * @return \eZ\Publish\API\Repository\Values\User\User|\PHPUnit\Framework\MockObject\MockObject - */ - public function getUserMock(array $mockMethods = []) - { - if ($this->userMock !== null) { - return $this->userMock; - } - - return $this->userMock = $this->getMockBuilder(APIUser::class) - ->setConstructorArgs([]) - ->setMethods($mockMethods) - ->getMock(); - } - - /** - * unset properties. - */ - protected function tearDown(): void - { - if ($this->persistenceHandlerMock !== null) { - unset($this->persistenceHandlerMock); - } - - if ($this->userMock !== null) { - unset($this->userMock); - } - - parent::tearDown(); - } -} diff --git a/eZ/Publish/Core/Limitation/UserGroupLimitationType.php b/eZ/Publish/Core/Limitation/UserGroupLimitationType.php deleted file mode 100644 index 8b2d380733..0000000000 --- a/eZ/Publish/Core/Limitation/UserGroupLimitationType.php +++ /dev/null @@ -1,222 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\UserGroupLimitation as APIUserGroupLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; - -/** - * UserGroupLimitation is a Content Limitation. - */ -class UserGroupLimitationType extends AbstractPersistenceLimitationType implements SPILimitationTypeInterface -{ - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue) - { - if (!$limitationValue instanceof APIUserGroupLimitation) { - throw new InvalidArgumentType('$limitationValue', 'APIUserGroupLimitation', $limitationValue); - } elseif (!is_array($limitationValue->limitationValues)) { - throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); - } - - foreach ($limitationValue->limitationValues as $key => $value) { - // Accept a true value for b/c with 5.0 - if ($value === true) { - $limitationValue->limitationValues[$key] = 1; - } elseif (is_string($value) && ctype_digit($value)) { - // Cast integers passed as string to int - $limitationValue->limitationValues[$key] = (int)$value; - } elseif (!is_int($value)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int', $value); - } - } - } - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue) - { - $validationErrors = []; - foreach ($limitationValue->limitationValues as $key => $value) { - if ($value !== 1) { - $validationErrors[] = new ValidationError( - "limitationValues[%key%] => '%value%' must be 1 (owner)", - null, - [ - 'value' => $value, - 'key' => $key, - ] - ); - } - } - - return $validationErrors; - } - - /** - * Create the Limitation Value. - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues) - { - return new APIUserGroupLimitation(['limitationValues' => $limitationValues]); - } - - /** - * Evaluate permission against content & target(placement/parent/assignment). - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller - * - * @return bool - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) - { - if (!$value instanceof APIUserGroupLimitation) { - throw new InvalidArgumentException('$value', 'Must be of type: APIUserGroupLimitation'); - } - - if ($value->limitationValues[0] != 1) { - throw new BadStateException( - 'Parent User Group limitation', - 'Expected Limitation value to be 1 instead of' . $value->limitationValues[0] - ); - } - - if ($object instanceof Content) { - $object = $object->getVersionInfo()->getContentInfo(); - } elseif ($object instanceof VersionInfo) { - $object = $object->getContentInfo(); - } elseif (!$object instanceof ContentInfo && !$object instanceof ContentCreateStruct) { - throw new InvalidArgumentException( - '$object', - 'Must be of type: ContentCreateStruct, Content, VersionInfo or ContentInfo' - ); - } - - /** @var \eZ\Publish\API\Repository\Values\Content\ContentInfo|\eZ\Publish\API\Repository\Values\Content\ContentCreateStruct */ - if ($object->ownerId === $currentUser->getUserId()) { - return true; - } - - /* - * As long as SPI userHandler and API UserService does not speak the same language, this is the ugly truth; - */ - $locationHandler = $this->persistence->locationHandler(); - $ownerLocations = $locationHandler->loadLocationsByContent($object->ownerId); - if (empty($ownerLocations)) { - return false; - } - - $currentUserLocations = $locationHandler->loadLocationsByContent($currentUser->getUserId()); - if (empty($currentUserLocations)) { - return false; - } - - // @todo Needs to take care of inherited groups as well when UserHandler gets knowledge about user groups - foreach ($ownerLocations as $ownerLocation) { - foreach ($currentUserLocations as $currentUserLocation) { - if ($ownerLocation->parentId === $currentUserLocation->parentId) { - return true; - } - } - } - - return false; - } - - /** - * Returns Criterion for use in find() query. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) - { - if (empty($value->limitationValues)) { - // A Policy should not have empty limitationValues stored - throw new \RuntimeException('$value->limitationValues is empty'); - } - - if ($value->limitationValues[0] != 1) { - throw new BadStateException( - 'Parent User Group limitation', - 'Expected Limitation value to be 1 instead of' . $value->limitationValues[0] - ); - } - - $groupIds = []; - $locationHandler = $this->persistence->locationHandler(); - $currentUserLocations = $locationHandler->loadLocationsByContent($currentUser->getUserId()); - foreach ($currentUserLocations as $currentUserLocation) { - try { - $parentLocation = $locationHandler->load($currentUserLocation->parentId); - $groupIds[] = $parentLocation->contentId; - } catch (NotFoundException $e) { - // there is no need for any action - carrying on with checking other user locations - continue; - } - } - - return new Criterion\UserMetadata( - Criterion\UserMetadata::GROUP, - Criterion\Operator::IN, - $groupIds - ); - } - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_ constants. - */ - public function valueSchema() - { - throw new \eZ\Publish\API\Repository\Exceptions\NotImplementedException(__METHOD__); - } -} diff --git a/eZ/Publish/Core/MVC/Exception/HiddenLocationException.php b/eZ/Publish/Core/MVC/Exception/HiddenLocationException.php deleted file mode 100644 index ba800e4d5c..0000000000 --- a/eZ/Publish/Core/MVC/Exception/HiddenLocationException.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Exception; - -use eZ\Publish\API\Repository\Values\Content\Location; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; - -class HiddenLocationException extends NotFoundHttpException -{ - /** @var \eZ\Publish\API\Repository\Values\Content\Location */ - private $location; - - public function __construct(Location $location, $message = null, \Exception $previous = null, $code = 0) - { - $this->location = $location; - parent::__construct($message, $previous, $code); - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Location - */ - public function getLocation() - { - return $this->location; - } -} diff --git a/eZ/Publish/Core/MVC/Exception/SourceImageNotFoundException.php b/eZ/Publish/Core/MVC/Exception/SourceImageNotFoundException.php deleted file mode 100644 index d8c8ac7beb..0000000000 --- a/eZ/Publish/Core/MVC/Exception/SourceImageNotFoundException.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Exception; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; - -class SourceImageNotFoundException extends NotFoundException -{ -} diff --git a/eZ/Publish/Core/MVC/RepositoryAware.php b/eZ/Publish/Core/MVC/RepositoryAware.php deleted file mode 100644 index 6427fa1303..0000000000 --- a/eZ/Publish/Core/MVC/RepositoryAware.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC; - -use eZ\Publish\API\Repository\Repository; - -abstract class RepositoryAware implements RepositoryAwareInterface -{ - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - /** - * @param \eZ\Publish\API\Repository\Repository $repository - */ - public function setRepository(Repository $repository) - { - $this->repository = $repository; - } -} diff --git a/eZ/Publish/Core/MVC/RepositoryAwareInterface.php b/eZ/Publish/Core/MVC/RepositoryAwareInterface.php deleted file mode 100644 index 9a8c233f28..0000000000 --- a/eZ/Publish/Core/MVC/RepositoryAwareInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC; - -use eZ\Publish\API\Repository\Repository; - -interface RepositoryAwareInterface -{ - /** - * @param \eZ\Publish\API\Repository\Repository $repository - */ - public function setRepository(Repository $repository); -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/CompoundMatcherNormalizer.php b/eZ/Publish/Core/MVC/Symfony/Component/Serializer/CompoundMatcherNormalizer.php deleted file mode 100644 index fec54a4259..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/CompoundMatcherNormalizer.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Component\Serializer; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; - -class CompoundMatcherNormalizer extends AbstractPropertyWhitelistNormalizer -{ - /** - * @see \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound::__sleep. - */ - public function normalize($object, string $format = null, array $context = []) - { - $data = parent::normalize($object, $format, $context); - $data['config'] = []; - $data['matchersMap'] = []; - - return $data; - } - - protected function getAllowedProperties(): array - { - return ['subMatchers']; - } - - public function supportsNormalization($data, string $format = null) - { - return $data instanceof Matcher\Compound; - } - - public function supportsDenormalization($data, string $type, string $format = null) - { - return $type === Matcher\Compound::class; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/HostElementNormalizer.php b/eZ/Publish/Core/MVC/Symfony/Component/Serializer/HostElementNormalizer.php deleted file mode 100644 index 2f2ed7b298..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/HostElementNormalizer.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Serializer; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\HostElement; - -final class HostElementNormalizer extends AbstractPropertyWhitelistNormalizer -{ - public function supportsNormalization($data, string $format = null) - { - return $data instanceof HostElement; - } - - /** - * @see \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\HostElement::__sleep - */ - protected function getAllowedProperties(): array - { - return ['elementNumber', 'hostElements']; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/HostTextNormalizer.php b/eZ/Publish/Core/MVC/Symfony/Component/Serializer/HostTextNormalizer.php deleted file mode 100644 index 8ae3c93ae6..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/HostTextNormalizer.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Serializer; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\HostText; - -final class HostTextNormalizer extends AbstractPropertyWhitelistNormalizer -{ - protected function getAllowedProperties(): array - { - return ['siteAccessesConfiguration']; - } - - public function supportsNormalization($data, string $format = null) - { - return $data instanceof HostText; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/MapNormalizer.php b/eZ/Publish/Core/MVC/Symfony/Component/Serializer/MapNormalizer.php deleted file mode 100644 index 60b3bf1b8c..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/MapNormalizer.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Serializer; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map; -use Symfony\Component\Serializer\Normalizer\PropertyNormalizer; - -final class MapNormalizer extends PropertyNormalizer -{ - /** - * @see \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map::__sleep - */ - public function normalize($object, string $format = null, array $context = []) - { - return [ - 'key' => $object->getMapKey(), - 'map' => [], - 'reverseMap' => [], - ]; - } - - public function supportsNormalization($data, string $format = null) - { - return $data instanceof Map; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/RegexHostNormalizer.php b/eZ/Publish/Core/MVC/Symfony/Component/Serializer/RegexHostNormalizer.php deleted file mode 100644 index 8c9502566d..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/RegexHostNormalizer.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Serializer; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex\Host; - -final class RegexHostNormalizer extends AbstractPropertyWhitelistNormalizer -{ - protected function getAllowedProperties(): array - { - return ['siteAccessesConfiguration']; - } - - public function supportsNormalization($data, string $format = null) - { - return $data instanceof Host; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/RegexNormalizer.php b/eZ/Publish/Core/MVC/Symfony/Component/Serializer/RegexNormalizer.php deleted file mode 100644 index f0caa130b2..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/RegexNormalizer.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Serializer; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex; - -final class RegexNormalizer extends AbstractPropertyWhitelistNormalizer -{ - public function supportsNormalization($data, string $format = null) - { - return $data instanceof Regex; - } - - /** - * @see \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex::__sleep - */ - protected function getAllowedProperties(): array - { - return ['regex', 'itemNumber', 'matchedSiteAccess']; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/RegexURINormalizer.php b/eZ/Publish/Core/MVC/Symfony/Component/Serializer/RegexURINormalizer.php deleted file mode 100644 index d67cf4f216..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/RegexURINormalizer.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Serializer; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex\URI; - -final class RegexURINormalizer extends AbstractPropertyWhitelistNormalizer -{ - protected function getAllowedProperties(): array - { - return ['siteAccessesConfiguration']; - } - - public function supportsNormalization($data, string $format = null) - { - return $data instanceof URI; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/URIElementNormalizer.php b/eZ/Publish/Core/MVC/Symfony/Component/Serializer/URIElementNormalizer.php deleted file mode 100644 index daf4fe516a..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/URIElementNormalizer.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Serializer; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIElement; - -final class URIElementNormalizer extends AbstractPropertyWhitelistNormalizer -{ - public function supportsNormalization($data, string $format = null) - { - return $data instanceof URIElement; - } - - /** - * @see \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIElement::__sleep - */ - protected function getAllowedProperties(): array - { - return ['elementNumber', 'uriElements']; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/URITextNormalizer.php b/eZ/Publish/Core/MVC/Symfony/Component/Serializer/URITextNormalizer.php deleted file mode 100644 index b84613ad5b..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Serializer/URITextNormalizer.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Serializer; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIText; - -final class URITextNormalizer extends AbstractPropertyWhitelistNormalizer -{ - protected function getAllowedProperties(): array - { - return ['siteAccessesConfiguration']; - } - - public function supportsNormalization($data, string $format = null) - { - return $data instanceof URIText; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/HostElementNormalizerTest.php b/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/HostElementNormalizerTest.php deleted file mode 100644 index 3dab51e44f..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/HostElementNormalizerTest.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer; - -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\HostElementNormalizer; -use eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\SerializerStub; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\HostElement; -use PHPUnit\Framework\TestCase; - -final class HostElementNormalizerTest extends TestCase -{ - public function testNormalization(): void - { - $normalizer = new HostElementNormalizer(); - $normalizer->setSerializer(new SerializerStub()); - - $matcher = new HostElement(2); - // Set request and invoke match to initialize HostElement::$hostElements - $matcher->setRequest(SimplifiedRequest::fromUrl('http://ezpublish.dev/foo/bar')); - $matcher->match(); - - $this->assertEquals( - [ - 'elementNumber' => 2, - 'hostElements' => [ - 'ezpublish', - 'dev', - ], - ], - $normalizer->normalize($matcher) - ); - } - - public function testSupportsNormalization(): void - { - $normalizer = new HostElementNormalizer(); - - $this->assertTrue($normalizer->supportsNormalization($this->createMock(HostElement::class))); - $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/HostTextNormalizerTest.php b/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/HostTextNormalizerTest.php deleted file mode 100644 index ed3c2eb010..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/HostTextNormalizerTest.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer; - -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\HostTextNormalizer; -use eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\SerializerStub; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\HostText; -use eZ\Publish\Core\Search\Tests\TestCase; - -final class HostTextNormalizerTest extends TestCase -{ - public function testNormalize(): void - { - $normalizer = new HostTextNormalizer(); - $normalizer->setSerializer(new SerializerStub()); - - $matcher = new HostText([ - 'prefix' => 'foo', - 'suffix' => 'bar', - ]); - - $this->assertEquals( - [ - 'siteAccessesConfiguration' => [ - 'prefix' => 'foo', - 'suffix' => 'bar', - ], - ], - $normalizer->normalize($matcher) - ); - } - - public function testSupportsNormalization(): void - { - $normalizer = new HostTextNormalizer(); - - $this->assertTrue($normalizer->supportsNormalization($this->createMock(HostText::class))); - $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/MapNormalizerTest.php b/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/MapNormalizerTest.php deleted file mode 100644 index 6135afa3e1..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/MapNormalizerTest.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer; - -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\MapNormalizer; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map as MapMatcher; -use PHPUnit\Framework\TestCase; - -final class MapNormalizerTest extends TestCase -{ - public function testNormalization(): void - { - $normalizer = new MapNormalizer(); - - $matcher = $this->createMock(MapMatcher::class); - $matcher->method('getMapKey')->willReturn('foo'); - - $this->assertEquals( - [ - 'key' => 'foo', - 'map' => [], - 'reverseMap' => [], - ], - $normalizer->normalize($matcher) - ); - } - - public function testSupportsNormalization(): void - { - $normalizer = new MapNormalizer(); - - $this->assertTrue($normalizer->supportsNormalization($this->createMock(MapMatcher::class))); - $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/RegexHostNormalizerTest.php b/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/RegexHostNormalizerTest.php deleted file mode 100644 index c02477c9ef..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/RegexHostNormalizerTest.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer; - -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\RegexHostNormalizer; -use eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\SerializerStub; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex\Host; -use eZ\Publish\Core\Search\Tests\TestCase; - -final class RegexHostNormalizerTest extends TestCase -{ - public function testNormalize(): void - { - $normalizer = new RegexHostNormalizer(); - $normalizer->setSerializer(new SerializerStub()); - - $matcher = new Host([ - 'regex' => '/^Foo(.*)/(.*)/', - 'itemNumber' => 2, - ]); - - $this->assertEquals( - [ - 'siteAccessesConfiguration' => [ - 'regex' => '/^Foo(.*)/(.*)/', - 'itemNumber' => 2, - ], - ], - $normalizer->normalize($matcher) - ); - } - - public function testSupportsNormalization(): void - { - $normalizer = new RegexHostNormalizer(); - - $this->assertTrue($normalizer->supportsNormalization($this->createMock(Host::class))); - $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/RegexNormalizerTest.php b/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/RegexNormalizerTest.php deleted file mode 100644 index 952e5f2fc4..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/RegexNormalizerTest.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer; - -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\RegexNormalizer; -use eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\RegexMatcher as RegexMatcherStub; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex as RegexMatcher; -use PHPUnit\Framework\TestCase; - -final class RegexNormalizerTest extends TestCase -{ - public function testNormalize(): void - { - $normalizer = new RegexNormalizer(); - $matcher = new RegexMatcherStub('/^Foo(.*)/(.*)/', 2); - - $this->assertEquals( - [ - 'regex' => '/^Foo(.*)/(.*)/', - 'itemNumber' => 2, - 'matchedSiteAccess' => null, - ], - $normalizer->normalize($matcher) - ); - } - - public function testSupportsNormalization(): void - { - $normalizer = new RegexNormalizer(); - - $this->assertTrue($normalizer->supportsNormalization($this->createMock(RegexMatcher::class))); - $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/RegexURINormalizerTest.php b/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/RegexURINormalizerTest.php deleted file mode 100644 index 8b4f599e01..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/RegexURINormalizerTest.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer; - -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\RegexURINormalizer; -use eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\SerializerStub; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex\URI; -use eZ\Publish\Core\Search\Tests\TestCase; - -final class RegexURINormalizerTest extends TestCase -{ - public function testNormalize(): void - { - $normalizer = new RegexURINormalizer(); - $normalizer->setSerializer(new SerializerStub()); - - $matcher = new URI([ - 'regex' => '/^Foo(.*)/(.*)/', - 'itemNumber' => 2, - ]); - - $this->assertEquals( - [ - 'siteAccessesConfiguration' => [ - 'regex' => '/^Foo(.*)/(.*)/', - 'itemNumber' => 2, - ], - ], - $normalizer->normalize($matcher) - ); - } - - public function testSupportsNormalization(): void - { - $normalizer = new RegexURINormalizer(); - - $this->assertTrue($normalizer->supportsNormalization($this->createMock(URI::class))); - $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/Stubs/CompoundStub.php b/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/Stubs/CompoundStub.php deleted file mode 100644 index bc396d29b2..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/Stubs/CompoundStub.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs; - -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound; - -final class CompoundStub extends Compound -{ - public function __construct(array $subMatchers) - { - parent::__construct([]); - $this->subMatchers = $subMatchers; - } - - public function match() - { - throw new NotImplementedException(__METHOD__); - } - - public function reverseMatch($siteAccessName) - { - throw new NotImplementedException(__METHOD__); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/Stubs/MatcherStub.php b/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/Stubs/MatcherStub.php deleted file mode 100644 index bf3ef51600..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/Stubs/MatcherStub.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs; - -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; - -final class MatcherStub implements Matcher -{ - /** @var mixed */ - private $data; - - public function __construct($data = null) - { - $this->data = $data; - } - - public function setRequest(SimplifiedRequest $request) - { - throw new NotImplementedException(__METHOD__); - } - - public function match() - { - throw new NotImplementedException(__METHOD__); - } - - public function getName() - { - throw new NotImplementedException(__METHOD__); - } - - public function getData() - { - return $this->data; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/Stubs/RegexMatcher.php b/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/Stubs/RegexMatcher.php deleted file mode 100644 index d9c3d168ca..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/Stubs/RegexMatcher.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs; - -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex as BaseRegex; - -final class RegexMatcher extends BaseRegex -{ - public function getName() - { - throw new NotImplementedException(__METHOD__); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/URIElementNormalizerTest.php b/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/URIElementNormalizerTest.php deleted file mode 100644 index 0d78b7533c..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/URIElementNormalizerTest.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer; - -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\URIElementNormalizer; -use eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\SerializerStub; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIElement; -use PHPUnit\Framework\TestCase; - -final class URIElementNormalizerTest extends TestCase -{ - public function testNormalization(): void - { - $normalizer = new URIElementNormalizer(); - $normalizer->setSerializer(new SerializerStub()); - - $matcher = new URIElement(2); - // Set request and invoke match to initialize HostElement::$hostElements - $matcher->setRequest(SimplifiedRequest::fromUrl('http://ezpublish.dev/foo/bar')); - $matcher->match(); - - $this->assertEquals( - [ - 'elementNumber' => 2, - 'uriElements' => ['foo', 'bar'], - ], - $normalizer->normalize($matcher) - ); - } - - public function testSupportsNormalization(): void - { - $normalizer = new URIElementNormalizer(); - - $this->assertTrue($normalizer->supportsNormalization($this->createMock(URIElement::class))); - $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/URITextNormalizerTest.php b/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/URITextNormalizerTest.php deleted file mode 100644 index f9ffe36ef5..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/URITextNormalizerTest.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer; - -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\URITextNormalizer; -use eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\SerializerStub; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIText; -use eZ\Publish\Core\Search\Tests\TestCase; - -final class URITextNormalizerTest extends TestCase -{ - public function testNormalize(): void - { - $normalizer = new URITextNormalizer(); - $normalizer->setSerializer(new SerializerStub()); - - $matcher = new URIText([ - 'prefix' => 'foo', - 'suffix' => 'bar', - ]); - - $this->assertEquals( - [ - 'siteAccessesConfiguration' => [ - 'prefix' => 'foo', - 'suffix' => 'bar', - ], - ], - $normalizer->normalize($matcher) - ); - } - - public function testSupportsNormalization(): void - { - $normalizer = new URITextNormalizer(); - - $this->assertTrue($normalizer->supportsNormalization($this->createMock(URIText::class))); - $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Configuration/VersatileScopeInterface.php b/eZ/Publish/Core/MVC/Symfony/Configuration/VersatileScopeInterface.php deleted file mode 100644 index 697f42dee2..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Configuration/VersatileScopeInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Configuration; - -use eZ\Publish\Core\MVC\ConfigResolverInterface; - -/** - * Allows a ConfigResolver to dynamically change their default scope. - */ -interface VersatileScopeInterface extends ConfigResolverInterface -{ - public function getDefaultScope(): string; - - public function setDefaultScope(string $scope): void; -} diff --git a/eZ/Publish/Core/MVC/Symfony/Controller/Content/DownloadController.php b/eZ/Publish/Core/MVC/Symfony/Controller/Content/DownloadController.php deleted file mode 100644 index f637b690a4..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Controller/Content/DownloadController.php +++ /dev/null @@ -1,121 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Controller\Content; - -use eZ\Bundle\EzPublishIOBundle\BinaryStreamResponse; -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Helper\TranslationHelper; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\MVC\Symfony\Controller\Controller; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\ResponseHeaderBag; - -class DownloadController extends Controller -{ - /** @var \eZ\Publish\API\Repository\ContentService */ - private $contentService; - - /** @var \eZ\Publish\Core\IO\IOServiceInterface */ - private $ioService; - - /** @var \eZ\Publish\Core\Helper\TranslationHelper */ - private $translationHelper; - - public function __construct(ContentService $contentService, IOServiceInterface $ioService, TranslationHelper $translationHelper) - { - $this->contentService = $contentService; - $this->ioService = $ioService; - $this->translationHelper = $translationHelper; - } - - /** - * Download binary file identified by field ID. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the field $fieldId can't be found, or the translation can't be found. - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If the content is trashed, or can't be found. - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions. - */ - public function downloadBinaryFileByIdAction(Request $request, int $contentId, int $fieldId): BinaryStreamResponse - { - $content = $this->contentService->loadContent($contentId); - try { - $field = $this->findFieldInContent($fieldId, $content); - } catch (InvalidArgumentException $e) { - throw new NotFoundException('File', $fieldId); - } - - return $this->downloadBinaryFileAction($contentId, $field->fieldDefIdentifier, $field->value->fileName, $request); - } - - /** - * Finds the field with id $fieldId in $content. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the field $fieldId can't be found, or the translation can't be found. - */ - protected function findFieldInContent(int $fieldId, Content $content): Field - { - foreach ($content->getFields() as $field) { - if ($field->getId() === $fieldId) { - return $field; - } - } - - throw new InvalidArgumentException( - '$fieldId', - "Field with id $fieldId not found in Content with id {$content->id}" - ); - } - - /** - * Download binary file identified by field identifier. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the field $fieldIdentifier can't be found, or the translation can't be found. - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If the content is trashed, or can't be found. - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions. - */ - public function downloadBinaryFileAction(int $contentId, string $fieldIdentifier, string $filename, Request $request): BinaryStreamResponse - { - if ($request->query->has('version')) { - $version = (int) $request->query->get('version'); - if ($version <= 0) { - throw new NotFoundException('File', $filename); - } - $content = $this->contentService->loadContent($contentId, null, $version); - } else { - $content = $this->contentService->loadContent($contentId); - } - - if ($content->contentInfo->isTrashed()) { - throw new NotFoundException('File', $filename); - } - - $field = $this->translationHelper->getTranslatedField( - $content, - $fieldIdentifier, - $request->query->has('inLanguage') ? $request->query->get('inLanguage') : null - ); - if (!$field instanceof Field) { - throw new InvalidArgumentException( - '$fieldIdentifier', - "'{$fieldIdentifier}' field not present on content #{$content->contentInfo->id} '{$content->contentInfo->name}'" - ); - } - - $response = new BinaryStreamResponse($this->ioService->loadBinaryFile($field->value->id), $this->ioService); - $response->setContentDisposition( - ResponseHeaderBag::DISPOSITION_ATTACHMENT, - $field->value->fileName, - bin2hex(random_bytes(8)) - ); - - return $response; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Controller/Content/PreviewController.php b/eZ/Publish/Core/MVC/Symfony/Controller/Content/PreviewController.php deleted file mode 100644 index 6ec36517f2..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Controller/Content/PreviewController.php +++ /dev/null @@ -1,189 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Controller\Content; - -use Exception; -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\Helper\ContentPreviewHelper; -use eZ\Publish\Core\Helper\PreviewLocationProvider; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator; -use eZ\Publish\Core\MVC\Symfony\Routing\UrlAliasRouter; -use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute as AuthorizationAttribute; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\View\CustomLocationControllerChecker; -use eZ\Publish\Core\MVC\Symfony\View\ViewManagerInterface; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; -use Symfony\Component\Security\Core\Exception\AccessDeniedException; - -class PreviewController -{ - public const PREVIEW_PARAMETER_NAME = 'isPreview'; - public const CONTENT_VIEW_ROUTE = '_ez_content_view'; - - /** @var \eZ\Publish\API\Repository\ContentService */ - private $contentService; - - /** @var \eZ\Publish\API\Repository\LocationService */ - private $locationService; - - /** @var \eZ\Publish\Core\Helper\PreviewLocationProvider */ - private $locationProvider; - - /** @var \Symfony\Component\HttpKernel\HttpKernelInterface */ - private $kernel; - - /** @var \eZ\Publish\Core\Helper\ContentPreviewHelper */ - private $previewHelper; - - /** @var \Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface */ - private $authorizationChecker; - - /** @var \eZ\Publish\Core\MVC\Symfony\View\CustomLocationControllerChecker */ - private $controllerChecker; - - public function __construct( - ContentService $contentService, - LocationService $locationService, - HttpKernelInterface $kernel, - ContentPreviewHelper $previewHelper, - AuthorizationCheckerInterface $authorizationChecker, - PreviewLocationProvider $locationProvider, - CustomLocationControllerChecker $controllerChecker - ) { - $this->contentService = $contentService; - $this->locationService = $locationService; - $this->kernel = $kernel; - $this->previewHelper = $previewHelper; - $this->authorizationChecker = $authorizationChecker; - $this->locationProvider = $locationProvider; - $this->controllerChecker = $controllerChecker; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException If Content is missing location as this is not supported in current version - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function previewContentAction( - Request $request, - $contentId, - $versionNo, - $language, - $siteAccessName = null, - ?int $locationId = null - ) { - $this->previewHelper->setPreviewActive(true); - - try { - $content = $this->contentService->loadContent($contentId, [$language], $versionNo); - $location = $locationId !== null - ? $this->locationService->loadLocation($locationId) - : $this->locationProvider->loadMainLocationByContent($content); - - if (!$location instanceof Location) { - throw new NotImplementedException('Preview for content without Locations'); - } - - $this->previewHelper->setPreviewedContent($content); - $this->previewHelper->setPreviewedLocation($location); - } catch (UnauthorizedException $e) { - throw new AccessDeniedException(); - } - - if (!$this->authorizationChecker->isGranted(new AuthorizationAttribute('content', 'versionread', ['valueObject' => $content]))) { - throw new AccessDeniedException(); - } - - $siteAccess = $this->previewHelper->getOriginalSiteAccess(); - // Only switch if $siteAccessName is set and different from original - if ($siteAccessName !== null && $siteAccessName !== $siteAccess->name) { - $siteAccess = $this->previewHelper->changeConfigScope($siteAccessName); - } - - try { - $response = $this->kernel->handle( - $this->getForwardRequest($location, $content, $siteAccess, $request, $language), - HttpKernelInterface::SUB_REQUEST, - false - ); - } catch (\Exception $e) { - if ($location->isDraft() && $this->controllerChecker->usesCustomController($content, $location)) { - // @todo This should probably be an exception that embeds the original one - $message = <<<EOF -<p>The view that rendered this location draft uses a custom controller, and resulted in a fatal error.</p> -<p>Location View is deprecated, as it causes issues with preview, such as an empty location id when previewing the first version of a content.</p> -EOF; - - throw new Exception($message, 0, $e); - } else { - throw $e; - } - } - $response->setPrivate(); - - $this->previewHelper->restoreConfigScope(); - $this->previewHelper->setPreviewActive(false); - - return $response; - } - - /** - * Returns the Request object that will be forwarded to the kernel for previewing the content. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess $previewSiteAccess - * @param \Symfony\Component\HttpFoundation\Request $request - * @param string $language - * - * @return \Symfony\Component\HttpFoundation\Request - */ - protected function getForwardRequest(Location $location, Content $content, SiteAccess $previewSiteAccess, Request $request, $language) - { - $forwardRequestParameters = [ - '_controller' => UrlAliasRouter::VIEW_ACTION, - // specify a route for RouteReference generator - '_route' => UrlAliasGenerator::INTERNAL_CONTENT_VIEW_ROUTE, - '_route_params' => [ - 'contentId' => $content->id, - 'locationId' => $location->id, - ], - 'location' => $location, - 'content' => $content, - 'viewType' => ViewManagerInterface::VIEW_TYPE_FULL, - 'layout' => true, - 'params' => [ - 'content' => $content, - 'location' => $location, - self::PREVIEW_PARAMETER_NAME => true, - 'language' => $language, - ], - 'siteaccess' => $previewSiteAccess, - 'semanticPathinfo' => $request->attributes->get('semanticPathinfo'), - ]; - - if ($this->controllerChecker->usesCustomController($content, $location)) { - $forwardRequestParameters = [ - '_controller' => 'ez_content:viewAction', - '_route' => self::CONTENT_VIEW_ROUTE, - ] + $forwardRequestParameters; - } - - return $request->duplicate( - null, - null, - $forwardRequestParameters - ); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Controller/Content/QueryController.php b/eZ/Publish/Core/MVC/Symfony/Controller/Content/QueryController.php deleted file mode 100644 index c763362476..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Controller/Content/QueryController.php +++ /dev/null @@ -1,146 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Controller\Content; - -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; -use eZ\Publish\Core\Pagination\Pagerfanta\ContentSearchHitAdapter; -use eZ\Publish\Core\Pagination\Pagerfanta\LocationSearchHitAdapter; -use eZ\Publish\Core\Pagination\Pagerfanta\Pagerfanta; -use eZ\Publish\Core\QueryType\ContentViewQueryTypeMapper; -use Pagerfanta\Adapter\AdapterInterface; -use Symfony\Component\HttpFoundation\Request; - -/** - * A content view controller that runs queries based on the matched view configuration. - * - * The action used depends on which type of search is needed: location, content or contentInfo. - */ -class QueryController -{ - /** @var \eZ\Publish\API\Repository\SearchService */ - private $searchService; - - /** @var \eZ\Publish\Core\QueryType\ContentViewQueryTypeMapper */ - private $contentViewQueryTypeMapper; - - public function __construct( - ContentViewQueryTypeMapper $contentViewQueryTypeMapper, - SearchService $searchService - ) { - $this->contentViewQueryTypeMapper = $contentViewQueryTypeMapper; - $this->searchService = $searchService; - } - - /** - * Runs a content search. - * - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentView $view - * - * @return \eZ\Publish\Core\MVC\Symfony\View\ContentView - */ - public function contentQueryAction(ContentView $view) - { - $this->runQuery($view, 'findContent'); - - return $view; - } - - /** - * Runs a location search. - * - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentView $view - * - * @return \eZ\Publish\Core\MVC\Symfony\View\ContentView - */ - public function locationQueryAction(ContentView $view) - { - $this->runQuery($view, 'findLocations'); - - return $view; - } - - /** - * Runs a contentInfo search. - * - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentView $view - * - * @return \eZ\Publish\Core\MVC\Symfony\View\ContentView - */ - public function contentInfoQueryAction(ContentView $view) - { - $this->runQuery($view, 'findContentInfo'); - - return $view; - } - - /** - * Runs the Query defined in $view using $method on SearchService. - * - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentView $view - * @param string $method Name of the SearchService method to run. - */ - private function runQuery(ContentView $view, $method) - { - $searchResults = $this->searchService->$method( - $this->contentViewQueryTypeMapper->map($view) - ); - $view->addParameters([$view->getParameter('query')['assign_results_to'] => $searchResults]); - } - - /** - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentView $view - * @param \Symfony\Component\HttpFoundation\Request $request - * - * @return \eZ\Publish\Core\MVC\Symfony\View\ContentView - */ - public function pagingQueryAction(ContentView $view, Request $request) - { - $this->runPagingQuery($view, $request); - - return $view; - } - - /** - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentView $view - * @param \Symfony\Component\HttpFoundation\Request $request - */ - private function runPagingQuery(ContentView $view, Request $request) - { - $queryParameters = $view->getParameter('query'); - - $limit = $queryParameters['limit'] ?? 10; - $pageParam = $queryParameters['page_param'] ?? 'page'; - - $page = $request->get($pageParam, 1); - - $pager = new Pagerfanta( - $this->getAdapter($this->contentViewQueryTypeMapper->map($view)) - ); - - $pager->setMaxPerPage($limit); - $pager->setCurrentPage($page); - - $view->addParameters([$queryParameters['assign_results_to'] => $pager]); - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - * - * @return \Pagerfanta\Adapter\AdapterInterface - */ - private function getAdapter(Query $query): AdapterInterface - { - if ($query instanceof LocationQuery) { - return new LocationSearchHitAdapter($query, $this->searchService); - } - - return new ContentSearchHitAdapter($query, $this->searchService); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Controller/Controller.php b/eZ/Publish/Core/MVC/Symfony/Controller/Controller.php deleted file mode 100644 index 7e7812b089..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Controller/Controller.php +++ /dev/null @@ -1,127 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Controller; - -use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute as AuthorizationAttribute; -use Symfony\Component\DependencyInjection\ContainerAwareInterface; -use Symfony\Component\DependencyInjection\ContainerAwareTrait; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpFoundation\Response; - -abstract class Controller implements ContainerAwareInterface -{ - use ContainerAwareTrait; - - /** - * Returns value for $parameterName and fallbacks to $defaultValue if not defined. - * - * @param string $parameterName - * @param mixed $defaultValue - * - * @return mixed - */ - public function getParameter($parameterName, $defaultValue = null) - { - if ($this->getConfigResolver()->hasParameter($parameterName)) { - return $this->getConfigResolver()->getParameter($parameterName); - } - - return $defaultValue; - } - - /** - * Checks if $parameterName is defined. - * - * @param string $parameterName - * - * @return bool - */ - public function hasParameter($parameterName) - { - return $this->getConfigResolver()->hasParameter($parameterName); - } - - /** - * @return \eZ\Publish\Core\MVC\ConfigResolverInterface - */ - public function getConfigResolver() - { - return $this->container->get('ezpublish.config.resolver'); - } - - /** - * Renders a view. - * - * @param string $view The view name - * @param array $parameters An array of parameters to pass to the view - * @param \Symfony\Component\HttpFoundation\Response $response - * - * @return \Symfony\Component\HttpFoundation\Response - */ - public function render($view, array $parameters = [], Response $response = null) - { - if (!isset($response)) { - $response = new Response(); - } - - $response->setContent($this->getTemplateEngine()->render($view, $parameters)); - - return $response; - } - - /** - * @return \Symfony\Component\Templating\EngineInterface - */ - public function getTemplateEngine() - { - return $this->container->get('templating'); - } - - /** - * @return \Psr\Log\LoggerInterface|null - */ - public function getLogger() - { - return $this->container->get('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE); - } - - /** - * @return \eZ\Publish\API\Repository\Repository - */ - public function getRepository() - { - return $this->container->get('ezpublish.api.repository'); - } - - /** - * @return \Symfony\Component\HttpFoundation\Request - */ - public function getRequest() - { - return $this->container->get('request_stack')->getCurrentRequest(); - } - - /** - * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface - */ - public function getEventDispatcher() - { - return $this->container->get('event_dispatcher'); - } - - /** - * Checks if current user has granted access to provided attribute. - * - * @param \eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute $attribute - * - * @return bool - */ - public function isGranted(AuthorizationAttribute $attribute) - { - return $this->container->get('security.authorization_checker')->isGranted($attribute); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Controller/Tests/Controller/Content/PreviewControllerTest.php b/eZ/Publish/Core/MVC/Symfony/Controller/Tests/Controller/Content/PreviewControllerTest.php deleted file mode 100644 index 64f8f7e5d8..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Controller/Tests/Controller/Content/PreviewControllerTest.php +++ /dev/null @@ -1,386 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Controller\Tests\Controller\Content; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\Core\Helper\ContentPreviewHelper; -use eZ\Publish\Core\Helper\PreviewLocationProvider; -use eZ\Publish\Core\MVC\Symfony\Controller\Content\PreviewController; -use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute as AuthorizationAttribute; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\View\CustomLocationControllerChecker; -use eZ\Publish\Core\MVC\Symfony\View\ViewManagerInterface; -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; - -class PreviewControllerTest extends TestCase -{ - /** @var \eZ\Publish\API\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject */ - protected $contentService; - - /** @var \eZ\Publish\API\Repository\LocationService|\PHPUnit\Framework\MockObject\MockObject */ - protected $locationService; - - /** @var \eZ\Publish\Core\Helper\PreviewLocationProvider|\PHPUnit\Framework\MockObject\MockObject */ - protected $httpKernel; - - /** @var \eZ\Publish\Core\Helper\ContentPreviewHelper|\PHPUnit\Framework\MockObject\MockObject */ - protected $previewHelper; - - /** @var \Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface|\PHPUnit\Framework\MockObject\MockObject */ - protected $authorizationChecker; - - /** @var \eZ\Publish\Core\Helper\PreviewLocationProvider|\PHPUnit\Framework\MockObject\MockObject */ - protected $locationProvider; - - /** @var \eZ\Publish\Core\MVC\Symfony\View\CustomLocationControllerChecker|\PHPUnit\Framework\MockObject\MockObject */ - protected $controllerChecker; - - protected function setUp(): void - { - parent::setUp(); - - $this->contentService = $this->createMock(ContentService::class); - $this->locationService = $this->createMock(LocationService::class); - $this->httpKernel = $this->createMock(HttpKernelInterface::class); - $this->previewHelper = $this->createMock(ContentPreviewHelper::class); - $this->authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); - $this->locationProvider = $this->createMock(PreviewLocationProvider::class); - $this->controllerChecker = $this->createMock(CustomLocationControllerChecker::class); - } - - protected function getPreviewController(): PreviewController - { - return new PreviewController( - $this->contentService, - $this->locationService, - $this->httpKernel, - $this->previewHelper, - $this->authorizationChecker, - $this->locationProvider, - $this->controllerChecker - ); - } - - public function testPreviewUnauthorized() - { - $this->expectException(\Symfony\Component\Security\Core\Exception\AccessDeniedException::class); - - $controller = $this->getPreviewController(); - $contentId = 123; - $lang = 'eng-GB'; - $versionNo = 3; - $this->contentService - ->expects($this->once()) - ->method('loadContent') - ->with($contentId, [$lang], $versionNo) - ->will($this->throwException(new UnauthorizedException('foo', 'bar'))); - $controller->previewContentAction(new Request(), $contentId, $versionNo, $lang, 'test'); - } - - public function testPreviewCanUserFail() - { - $this->expectException(\Symfony\Component\Security\Core\Exception\AccessDeniedException::class); - - $controller = $this->getPreviewController(); - $contentId = 123; - $lang = 'eng-GB'; - $versionNo = 3; - $content = $this->createMock(Content::class); - $contentInfo = $this->getMockBuilder(ContentInfo::class) - ->setConstructorArgs([['id' => $contentId]]) - ->getMockForAbstractClass(); - - $this->locationProvider - ->expects($this->once()) - ->method('loadMainLocationByContent') - ->with($content) - ->will($this->returnValue($this->createMock(Location::class))); - $this->contentService - ->expects($this->once()) - ->method('loadContent') - ->with($contentId, [$lang], $versionNo) - ->will($this->returnValue($content)); - $this->authorizationChecker - ->expects($this->once()) - ->method('isGranted') - ->with($this->equalTo(new AuthorizationAttribute('content', 'versionread', ['valueObject' => $content]))) - ->will($this->returnValue(false)); - - $controller->previewContentAction(new Request(), $contentId, $versionNo, $lang, 'test'); - } - - public function testPreview() - { - $contentId = 123; - $lang = 'eng-GB'; - $versionNo = 3; - $locationId = 456; - $content = $this->createMock(Content::class); - $location = $this->getMockBuilder(Location::class) - ->setConstructorArgs([['id' => $locationId]]) - ->getMockForAbstractClass(); - - // Repository expectations - $this->locationProvider - ->expects($this->once()) - ->method('loadMainLocationByContent') - ->with($content) - ->will($this->returnValue($location)); - $this->contentService - ->expects($this->once()) - ->method('loadContent') - ->with($contentId, [$lang], $versionNo) - ->will($this->returnValue($content)); - $this->authorizationChecker - ->expects($this->once()) - ->method('isGranted') - ->with($this->equalTo(new AuthorizationAttribute('content', 'versionread', ['valueObject' => $content]))) - ->will($this->returnValue(true)); - - $previewSiteAccessName = 'test'; - $previewSiteAccess = new SiteAccess($previewSiteAccessName, 'preview'); - $previousSiteAccessName = 'foo'; - $previousSiteAccess = new SiteAccess($previousSiteAccessName); - $request = $this->getMockBuilder(Request::class) - ->setMethods(['duplicate']) - ->getMock(); - - // PreviewHelper expectations - $this->previewHelper - ->expects($this->exactly(2)) - ->method('setPreviewActive') - ->will( - $this->returnValueMap( - [ - [true, null], - [false, null], - ] - ) - ); - $this->previewHelper - ->expects($this->once()) - ->method('setPreviewedContent') - ->with($content); - $this->previewHelper - ->expects($this->once()) - ->method('setPreviewedLocation') - ->with($location); - $this->previewHelper - ->expects($this->once()) - ->method('getOriginalSiteAccess') - ->will($this->returnValue($previousSiteAccess)); - $this->previewHelper - ->expects($this->once()) - ->method('changeConfigScope') - ->with($previewSiteAccessName) - ->will($this->returnValue($previewSiteAccess)); - $this->previewHelper - ->expects($this->once()) - ->method('restoreConfigScope'); - - // Request expectations - $duplicatedRequest = $this->getDuplicatedRequest($location, $content, $previewSiteAccess); - $request - ->expects($this->once()) - ->method('duplicate') - ->will($this->returnValue($duplicatedRequest)); - - // Kernel expectations - $expectedResponse = new Response(); - $this->httpKernel - ->expects($this->once()) - ->method('handle') - ->with($duplicatedRequest, HttpKernelInterface::SUB_REQUEST) - ->will($this->returnValue($expectedResponse)); - - $controller = $this->getPreviewController(); - $this->assertSame( - $expectedResponse, - $controller->previewContentAction($request, $contentId, $versionNo, $lang, $previewSiteAccessName) - ); - } - - public function testPreviewDefaultSiteaccess() - { - $contentId = 123; - $lang = 'eng-GB'; - $versionNo = 3; - $locationId = 456; - $content = $this->createMock(Content::class); - $location = $this->getMockBuilder(Location::class) - ->setConstructorArgs([['id' => $locationId]]) - ->getMockForAbstractClass(); - - // Repository expectations - $this->locationProvider - ->expects($this->once()) - ->method('loadMainLocationByContent') - ->with($content) - ->will($this->returnValue($location)); - $this->contentService - ->expects($this->once()) - ->method('loadContent') - ->with($contentId, [$lang], $versionNo) - ->will($this->returnValue($content)); - $this->authorizationChecker - ->expects($this->once()) - ->method('isGranted') - ->with($this->equalTo(new AuthorizationAttribute('content', 'versionread', ['valueObject' => $content]))) - ->will($this->returnValue(true)); - - $previousSiteAccessName = 'foo'; - $previousSiteAccess = new SiteAccess($previousSiteAccessName); - $request = $this->getMockBuilder(Request::class) - ->setMethods(['duplicate']) - ->getMock(); - - $this->previewHelper - ->expects($this->once()) - ->method('getOriginalSiteAccess') - ->will($this->returnValue($previousSiteAccess)); - $this->previewHelper - ->expects($this->once()) - ->method('restoreConfigScope'); - - // Request expectations - $duplicatedRequest = $this->getDuplicatedRequest($location, $content, $previousSiteAccess); - $request - ->expects($this->once()) - ->method('duplicate') - ->will($this->returnValue($duplicatedRequest)); - - // Kernel expectations - $expectedResponse = new Response(); - $this->httpKernel - ->expects($this->once()) - ->method('handle') - ->with($duplicatedRequest, HttpKernelInterface::SUB_REQUEST) - ->will($this->returnValue($expectedResponse)); - - $controller = $this->getPreviewController(); - $this->assertSame( - $expectedResponse, - $controller->previewContentAction($request, $contentId, $versionNo, $lang) - ); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function testPreviewWithLocationId(): void - { - $contentId = 123; - $lang = 'eng-GB'; - $versionNo = 3; - $locationId = 456; - $content = $this->createMock(Content::class); - $location = $this->getMockBuilder(Location::class) - ->setConstructorArgs([['id' => $locationId]]) - ->getMockForAbstractClass(); - - $this->contentService - ->expects(self::once()) - ->method('loadContent') - ->with($contentId, [$lang], $versionNo) - ->willReturn($content); - - $this->locationService - ->expects(self::once()) - ->method('loadLocation') - ->with($locationId) - ->willReturn($location); - - $this->authorizationChecker - ->expects(self::once()) - ->method('isGranted') - ->with(new AuthorizationAttribute('content', 'versionread', ['valueObject' => $content])) - ->willReturn(true); - - $previousSiteAccessName = 'foo'; - $previousSiteAccess = new SiteAccess($previousSiteAccessName); - $request = $this->getMockBuilder(Request::class) - ->onlyMethods(['duplicate']) - ->getMock(); - - $this->previewHelper - ->expects(self::once()) - ->method('getOriginalSiteAccess') - ->willReturn($previousSiteAccess); - - $this->previewHelper - ->expects(self::once()) - ->method('restoreConfigScope'); - - $duplicatedRequest = $this->getDuplicatedRequest($location, $content, $previousSiteAccess); - $request - ->expects(self::once()) - ->method('duplicate') - ->willReturn($duplicatedRequest); - - $expectedResponse = new Response(); - $this->httpKernel - ->expects(self::once()) - ->method('handle') - ->with($duplicatedRequest, HttpKernelInterface::SUB_REQUEST) - ->willReturn($expectedResponse); - - $controller = $this->getPreviewController(); - - self::assertSame( - $expectedResponse, - $controller->previewContentAction( - $request, - $contentId, - $versionNo, - $lang, - null, - $locationId - ) - ); - } - - /** - * @param $location - * @param $content - * @param $previewSiteAccess - * - * @return \Symfony\Component\HttpFoundation\Request - */ - protected function getDuplicatedRequest(Location $location, Content $content, SiteAccess $previewSiteAccess) - { - $duplicatedRequest = new Request(); - $duplicatedRequest->attributes->add( - [ - '_controller' => 'ez_content:viewAction', - 'contentId' => $content->id, - 'location' => $location, - 'viewType' => ViewManagerInterface::VIEW_TYPE_FULL, - 'layout' => true, - 'semanticPathinfo' => '/foo/bar', - 'params' => [ - 'content' => $content, - 'location' => $location, - 'isPreview' => true, - 'siteaccess' => $previewSiteAccess, - ], - ] - ); - - return $duplicatedRequest; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Event/ContentCacheClearEvent.php b/eZ/Publish/Core/MVC/Symfony/Event/ContentCacheClearEvent.php deleted file mode 100644 index b6c0113d38..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Event/ContentCacheClearEvent.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Event; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use Symfony\Contracts\EventDispatcher\Event; - -/** - * Class ContentCacheClearEvent. - * - * @deprecated Since 6.12, not triggered anymore when using ezplatform-http-cache, as this never worked for for instance - * deleted content. - */ -class ContentCacheClearEvent extends Event -{ - /** @var \eZ\Publish\API\Repository\Values\Content\ContentInfo */ - private $contentInfo; - - /** @var \eZ\Publish\API\Repository\Values\Content\Location[] */ - private $locationsToClear = []; - - public function __construct(ContentInfo $contentInfo) - { - $this->contentInfo = $contentInfo; - } - - /** - * Returns ContentInfo object we're clearing the cache for. - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - public function getContentInfo() - { - return $this->contentInfo; - } - - /** - * Returns all location objects registered to the cache clear process. - * - * @return \eZ\Publish\API\Repository\Values\Content\Location[] - */ - public function getLocationsToClear() - { - return $this->locationsToClear; - } - - /** - * Adds a location that needs to be cleared. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - */ - public function addLocationToClear(Location $location) - { - $this->locationsToClear[] = $location; - } - - /** - * Replaces the list of locations to clear. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location[] $locationsToClear - */ - public function setLocationsToClear(array $locationsToClear) - { - $this->locationsToClear = $locationsToClear; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Event/ResolveRenderOptionsEvent.php b/eZ/Publish/Core/MVC/Symfony/Event/ResolveRenderOptionsEvent.php deleted file mode 100644 index dcf1391cd7..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Event/ResolveRenderOptionsEvent.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Event; - -use eZ\Publish\Core\MVC\Symfony\Templating\RenderOptions; -use Symfony\Contracts\EventDispatcher\Event; - -final class ResolveRenderOptionsEvent extends Event -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Templating\RenderOptions */ - private $renderOptions; - - public function __construct( - RenderOptions $renderOptions - ) { - $this->renderOptions = $renderOptions; - } - - public function getRenderOptions(): RenderOptions - { - return $this->renderOptions; - } - - public function setRenderOptions(RenderOptions $renderOptions): void - { - $this->renderOptions = $renderOptions; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Event/RouteReferenceGenerationEvent.php b/eZ/Publish/Core/MVC/Symfony/Event/RouteReferenceGenerationEvent.php deleted file mode 100644 index 97fda680a7..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Event/RouteReferenceGenerationEvent.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Event; - -use eZ\Publish\Core\MVC\Symfony\Routing\RouteReference; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Contracts\EventDispatcher\Event; - -/** - * Event dispatched when generating a RouteReference. - */ -class RouteReferenceGenerationEvent extends Event -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Routing\RouteReference */ - private $routeReference; - - /** @var \Symfony\Component\HttpFoundation\Request */ - private $request; - - public function __construct(RouteReference $routeReference, Request $request) - { - $this->routeReference = $routeReference; - $this->request = $request; - } - - /** - * @return \Symfony\Component\HttpFoundation\Request - */ - public function getRequest() - { - return $this->request; - } - - /** - * @return \eZ\Publish\Core\MVC\Symfony\Routing\RouteReference - */ - public function getRouteReference() - { - return $this->routeReference; - } - - /** - * @param \eZ\Publish\Core\MVC\Symfony\Routing\RouteReference $routeReference - */ - public function setRouteReference($routeReference) - { - $this->routeReference = $routeReference; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Event/ScopeChangeEvent.php b/eZ/Publish/Core/MVC/Symfony/Event/ScopeChangeEvent.php deleted file mode 100644 index 2822bc16b0..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Event/ScopeChangeEvent.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Event; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use Symfony\Contracts\EventDispatcher\Event; - -/** - * This event is sent when configuration scope is changed (e.g. for content preview in a given siteaccess). - */ -class ScopeChangeEvent extends Event -{ - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ - private $siteAccess; - - public function __construct(SiteAccess $siteAccess) - { - $this->siteAccess = $siteAccess; - } - - /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess - */ - public function getSiteAccess() - { - return $this->siteAccess; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Event/Tests/InteractiveLoginEventTest.php b/eZ/Publish/Core/MVC/Symfony/Event/Tests/InteractiveLoginEventTest.php deleted file mode 100644 index 3be202602b..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Event/Tests/InteractiveLoginEventTest.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Event\Tests; - -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\Core\MVC\Symfony\Event\InteractiveLoginEvent; -use PHPUnit\Framework\TestCase; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; - -class InteractiveLoginEventTest extends TestCase -{ - public function testGetSetAPIUser() - { - $event = new InteractiveLoginEvent(new Request(), $this->createMock(TokenInterface::class)); - $this->assertFalse($event->hasAPIUser()); - $apiUser = $this->createMock(User::class); - $event->setApiUser($apiUser); - $this->assertTrue($event->hasAPIUser()); - $this->assertSame($apiUser, $event->getAPIUser()); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Event/Tests/ScopeChangeEventTest.php b/eZ/Publish/Core/MVC/Symfony/Event/Tests/ScopeChangeEventTest.php deleted file mode 100644 index 8f04e0fb05..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Event/Tests/ScopeChangeEventTest.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Event\Tests; - -use eZ\Publish\Core\MVC\Symfony\Event\ScopeChangeEvent; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use PHPUnit\Framework\TestCase; - -class ScopeChangeEventTest extends TestCase -{ - public function testGetSiteAccess() - { - $siteAccess = new SiteAccess('foo', 'test'); - $event = new ScopeChangeEvent($siteAccess); - $this->assertSame($siteAccess, $event->getSiteAccess()); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/EventListener/LanguageSwitchListener.php b/eZ/Publish/Core/MVC/Symfony/EventListener/LanguageSwitchListener.php deleted file mode 100644 index 6c16f4299e..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/EventListener/LanguageSwitchListener.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\EventListener; - -use eZ\Publish\Core\Helper\TranslationHelper; -use eZ\Publish\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * Listener for language switcher. - * Will be triggered when generating a RouteReference. - */ -class LanguageSwitchListener implements EventSubscriberInterface -{ - /** @var \eZ\Publish\Core\Helper\TranslationHelper */ - private $translationHelper; - - public function __construct(TranslationHelper $translationHelper) - { - $this->translationHelper = $translationHelper; - } - - public static function getSubscribedEvents() - { - return [ - MVCEvents::ROUTE_REFERENCE_GENERATION => 'onRouteReferenceGeneration', - ]; - } - - /** - * If "language" parameter is present, will try to get corresponding SiteAccess for translation. - * If found, it will add "siteaccess" parameter to the RouteReference, to trigger SiteAccess switch when generating - * the final link. - * - * @see \eZ\Publish\Core\MVC\Symfony\Routing\Generator::generate() - * @see \eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator::doGenerate() - * - * @param \eZ\Publish\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent $event - */ - public function onRouteReferenceGeneration(RouteReferenceGenerationEvent $event) - { - $routeReference = $event->getRouteReference(); - if (!$routeReference->has('language')) { - return; - } - - $language = $routeReference->get('language'); - $routeReference->remove('language'); - $siteAccess = $this->translationHelper->getTranslationSiteAccess($language); - if ($siteAccess !== null) { - $routeReference->set('siteaccess', $siteAccess); - } - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/BinaryBase/ContentDownloadUrlGenerator.php b/eZ/Publish/Core/MVC/Symfony/FieldType/BinaryBase/ContentDownloadUrlGenerator.php deleted file mode 100644 index 94aada260a..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/BinaryBase/ContentDownloadUrlGenerator.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\FieldType\BinaryBase; - -use eZ\Publish\SPI\FieldType\BinaryBase\PathGenerator; -use eZ\Publish\SPI\FieldType\BinaryBase\RouteAwarePathGenerator; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use Symfony\Component\Routing\RouterInterface; - -class ContentDownloadUrlGenerator extends PathGenerator implements RouteAwarePathGenerator -{ - /** @var \Symfony\Component\Routing\RouterInterface */ - private $router; - - /** @var string */ - private $route = 'ez_content_download_field_id'; - - public function __construct(RouterInterface $router) - { - $this->router = $router; - } - - public function getStoragePathForField(Field $field, VersionInfo $versionInfo) - { - return $this->generate($this->route, $this->getParameters($field, $versionInfo)); - } - - public function generate(string $route, ?array $parameters = []): string - { - return $this->router->generate($route, $parameters ?? []); - } - - public function getRoute(Field $field, VersionInfo $versionInfo): string - { - return $this->route; - } - - public function getParameters(Field $field, VersionInfo $versionInfo): array - { - return [ - 'contentId' => $versionInfo->contentInfo->id, - 'fieldId' => $field->id, - 'version' => $versionInfo->versionNo, - ]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/ImageAsset/ParameterProvider.php b/eZ/Publish/Core/MVC/Symfony/FieldType/ImageAsset/ParameterProvider.php deleted file mode 100644 index 33a3d9b33d..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/ImageAsset/ParameterProvider.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\FieldType\ImageAsset; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderInterface; - -class ParameterProvider implements ParameterProviderInterface -{ - /** @var \eZ\Publish\API\Repository\Repository */ - private $repository; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionsResolver; - - /** @var \eZ\Publish\Core\Repository\FieldTypeService */ - private $fieldTypeService; - - /** - * @param \eZ\Publish\API\Repository\Repository $repository - */ - public function __construct(Repository $repository) - { - $this->repository = $repository; - $this->permissionsResolver = $repository->getPermissionResolver(); - $this->fieldTypeService = $repository->getFieldTypeService(); - } - - /** - * {@inheritdoc} - */ - public function getViewParameters(Field $field): array - { - $fieldType = $this->fieldTypeService->getFieldType($field->fieldTypeIdentifier); - - if ($fieldType->isEmptyValue($field->value)) { - return [ - 'available' => null, - ]; - } - - try { - $contentInfo = $this->loadContentInfo( - (int)$field->value->destinationContentId - ); - - return [ - 'available' => !$contentInfo->isTrashed() && $this->userHasPermissions($contentInfo), - ]; - } catch (NotFoundException $exception) { - return [ - 'available' => false, - ]; - } - } - - /** - * @param int $id - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - private function loadContentInfo(int $id): ContentInfo - { - return $this->repository->sudo( - static function (Repository $repository) use ($id) { - return $repository->getContentService()->loadContentInfo($id); - } - ); - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - private function userHasPermissions(ContentInfo $contentInfo): bool - { - if ($this->permissionsResolver->canUser('content', 'read', $contentInfo)) { - return true; - } - - if ($this->permissionsResolver->canUser('content', 'view_embed', $contentInfo)) { - return true; - } - - return false; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/Relation/ParameterProvider.php b/eZ/Publish/Core/MVC/Symfony/FieldType/Relation/ParameterProvider.php deleted file mode 100644 index e376f06c9f..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/Relation/ParameterProvider.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\FieldType\Relation; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderInterface; - -class ParameterProvider implements ParameterProviderInterface -{ - /** @var \eZ\Publish\API\Repository\ContentService */ - private $contentService; - - /** - * @param \eZ\Publish\API\Repository\ContentService $contentService - */ - public function __construct(ContentService $contentService) - { - $this->contentService = $contentService; - } - - /** - * Returns a hash of parameters to inject to the associated fieldtype's view template. - * Returned parameters will only be available for associated field type. - * - * Key is the parameter name (the variable name exposed in the template, in the 'parameters' array). - * Value is the parameter's value. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field The field parameters are provided for. - * - * @return array - */ - public function getViewParameters(Field $field): array - { - try { - $contentInfo = null; - if ($field->value->destinationContentId !== null) { - $contentInfo = $this->contentService->loadContentInfo( - $field->value->destinationContentId - ); - } - - return [ - 'available' => $contentInfo !== null && !$contentInfo->isTrashed(), - ]; - } catch (NotFoundException | UnauthorizedException $exception) { - return [ - 'available' => false, - ]; - } - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/RelationList/ParameterProvider.php b/eZ/Publish/Core/MVC/Symfony/FieldType/RelationList/ParameterProvider.php deleted file mode 100644 index fdabbd019b..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/RelationList/ParameterProvider.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\FieldType\RelationList; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderInterface; - -class ParameterProvider implements ParameterProviderInterface -{ - /** @var \eZ\Publish\API\Repository\ContentService */ - private $contentService; - - /** - * @param \eZ\Publish\API\Repository\ContentService $contentService - */ - public function __construct(ContentService $contentService) - { - $this->contentService = $contentService; - } - - /** - * Returns a hash of parameters to inject to the associated fieldtype's view template. - * Returned parameters will only be available for associated field type. - * - * Key is the parameter name (the variable name exposed in the template, in the 'parameters' array). - * Value is the parameter's value. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field The field parameters are provided for. - * - * @return array - */ - public function getViewParameters(Field $field) - { - $ids = $field->value->destinationContentIds; - $list = $this->contentService->loadContentInfoList($ids); - - // Start by setting missing ids as false on $available - $available = array_fill_keys( - array_diff($ids, array_keys($list)), - false - ); - - // Check if loaded items are in trash or not, for availability - foreach ($list as $contentId => $contentInfo) { - $available[$contentId] = !$contentInfo->isTrashed(); - } - - return [ - 'available' => $available, - ]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/ImageAsset/ParameterProviderTest.php b/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/ImageAsset/ParameterProviderTest.php deleted file mode 100644 index 429233317a..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/ImageAsset/ParameterProviderTest.php +++ /dev/null @@ -1,200 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\FieldType\Tests\ImageAsset; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\FieldType; -use eZ\Publish\API\Repository\FieldTypeService; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\ImageAsset\Value as ImageAssetValue; -use eZ\Publish\Core\MVC\Symfony\FieldType\ImageAsset\ParameterProvider; -use eZ\Publish\Core\Repository\SiteAccessAware\Repository; -use PHPUnit\Framework\TestCase; - -class ParameterProviderTest extends TestCase -{ - /** @var \eZ\Publish\Core\Repository\SiteAccessAware\Repository|\PHPUnit\Framework\MockObject\MockObject */ - private $repository; - - /** @var \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ - private $permissionsResolver; - - /** @var \eZ\Publish\Core\MVC\Symfony\FieldType\ImageAsset\ParameterProvider */ - private $parameterProvider; - - /** @var \eZ\Publish\API\Repository\FieldType||\PHPUnit\Framework\MockObject\MockObject */ - private $fieldType; - - protected function setUp(): void - { - $this->repository = $this->createMock(Repository::class); - $this->permissionsResolver = $this->createMock(PermissionResolver::class); - $this->fieldType = $this->createMock(FieldType::class); - - $this->repository - ->method('getPermissionResolver') - ->willReturn($this->permissionsResolver); - - $fieldTypeService = $this->createMock(FieldTypeService::class); - - $this->repository - ->method('getFieldTypeService') - ->willReturn($fieldTypeService); - - $fieldTypeService - ->method('getFieldType') - ->with('ezimageasset') - ->willReturn($this->fieldType); - - $this->parameterProvider = new ParameterProvider($this->repository); - } - - public function dataProviderForTestGetViewParameters(): array - { - return [ - [ContentInfo::STATUS_PUBLISHED, ['available' => true]], - [ContentInfo::STATUS_TRASHED, ['available' => false]], - ]; - } - - /** - * @dataProvider dataProviderForTestGetViewParameters - */ - public function testGetViewParameters($status, array $expected): void - { - $destinationContentId = 1; - - $this->fieldType - ->method('isEmptyValue') - ->willReturn(false); - - $closure = static function (Repository $repository) use ($destinationContentId) { - return $repository->getContentService()->loadContentInfo($destinationContentId); - }; - - $this->repository - ->method('sudo') - ->with($closure) - ->willReturn(new ContentInfo([ - 'status' => $status, - ])); - - $this->permissionsResolver - ->method('canUser') - ->willReturn(true); - - $actual = $this->parameterProvider->getViewParameters($this->createField($destinationContentId)); - - $this->assertEquals($expected, $actual); - } - - public function testGetViewParametersHandleNotFoundException(): void - { - $destinationContentId = 1; - - $this->fieldType - ->method('isEmptyValue') - ->willReturn(false); - - $closure = static function (Repository $repository) use ($destinationContentId) { - return $repository->getContentService()->loadContentInfo($destinationContentId); - }; - - $this->repository - ->expects($this->once()) - ->method('sudo') - ->with($closure) - ->willThrowException($this->createMock(NotFoundException::class)); - - $actual = $this->parameterProvider->getViewParameters( - $this->createField($destinationContentId) - ); - - $this->assertEquals([ - 'available' => false, - ], $actual); - } - - public function testGetViewParametersHandleUnauthorizedAccess(): void - { - $destinationContentId = 1; - - $this->fieldType - ->method('isEmptyValue') - ->willReturn(false); - - $contentInfo = $this->createMock(ContentInfo::class); - - $this->repository - ->method('sudo') - ->willReturn($contentInfo) - ; - - $this->permissionsResolver - ->expects($this->at(0)) - ->method('canUser') - ->with('content', 'read', $contentInfo) - ->willReturn(false) - ; - - $this->permissionsResolver - ->expects($this->at(1)) - ->method('canUser') - ->with('content', 'view_embed', $contentInfo) - ->willReturn(false) - ; - - $actual = $this->parameterProvider->getViewParameters( - $this->createField($destinationContentId) - ); - - $this->assertEquals([ - 'available' => false, - ], $actual); - } - - public function testGetViewParametersHandleEmptyValue(): void - { - $destinationContentId = 1; - - $this->fieldType - ->method('isEmptyValue') - ->willReturn(true); - - $contentInfo = $this->createMock(ContentInfo::class); - - $this->repository - ->method('sudo') - ->willReturn($contentInfo) - ; - - $actual = $this->parameterProvider->getViewParameters( - $this->createField($destinationContentId) - ); - - $this->assertEquals([ - 'available' => null, - ], $actual); - } - - /** - * @param int $destinationContentId - * - * @return \eZ\Publish\API\Repository\Values\Content\Field - */ - private function createField(int $destinationContentId): Field - { - return new Field([ - 'value' => new ImageAssetValue($destinationContentId), - 'fieldTypeIdentifier' => 'ezimageasset', - ]); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/Relation/ParameterProviderTest.php b/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/Relation/ParameterProviderTest.php deleted file mode 100644 index db5c7f08ea..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/Relation/ParameterProviderTest.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\FieldType\Tests\Relation; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\Core\FieldType\Relation\Value; -use eZ\Publish\Core\MVC\Symfony\FieldType\Relation\ParameterProvider; -use PHPUnit\Framework\TestCase; - -class ParameterProviderTest extends TestCase -{ - public function providerForTestGetViewParameters() - { - return [ - [ContentInfo::STATUS_DRAFT, ['available' => true]], - [ContentInfo::STATUS_PUBLISHED, ['available' => true]], - [ContentInfo::STATUS_TRASHED, ['available' => false]], - ]; - } - - /** - * @dataProvider providerForTestGetViewParameters - */ - public function testGetViewParameters($status, array $expected) - { - $contentServiceMock = $this->createMock(ContentService::class); - $contentServiceMock - ->method('loadContentInfo') - ->will(TestCase::returnValue( - new ContentInfo(['status' => $status]) - )); - - $parameterProvider = new ParameterProvider($contentServiceMock); - $parameters = $parameterProvider->getViewParameters(new Field([ - 'value' => new Value(123), - ])); - - TestCase::assertSame($parameters, $expected); - } - - public function testNotFoundGetViewParameters() - { - $contentId = 123; - - $contentServiceMock = $this->createMock(ContentService::class); - $contentServiceMock - ->method('loadContentInfo') - ->will(TestCase::throwException(new NotFoundException('ContentInfo', $contentId))); - - $parameterProvider = new ParameterProvider($contentServiceMock); - $parameters = $parameterProvider->getViewParameters(new Field([ - 'value' => new Value($contentId), - ])); - - TestCase::assertSame($parameters, ['available' => false]); - } - - public function testUnauthorizedGetViewParameters() - { - $contentId = 123; - - $contentServiceMock = $this->createMock(ContentService::class); - $contentServiceMock - ->method('loadContentInfo') - ->will(TestCase::throwException(new UnauthorizedException('content', 'read'))); - - $parameterProvider = new ParameterProvider($contentServiceMock); - $parameters = $parameterProvider->getViewParameters(new Field([ - 'value' => new Value($contentId), - ])); - - TestCase::assertSame($parameters, ['available' => false]); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/RelationList/ParameterProviderTest.php b/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/RelationList/ParameterProviderTest.php deleted file mode 100644 index c9778a47fa..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/RelationList/ParameterProviderTest.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\FieldType\Tests\RelationList; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\RelationList\Value; -use eZ\Publish\Core\MVC\Symfony\FieldType\RelationList\ParameterProvider; -use PHPUnit\Framework\TestCase; - -class ParameterProviderTest extends TestCase -{ - public function providerForTestGetViewParameters() - { - return [ - [[123, 456, 789], ['available' => [123 => true, 456 => true, 789 => false]]], - [[123, 456], ['available' => [123 => true, 456 => true]]], - [[789], ['available' => [789 => false]]], - [[], ['available' => []]], - ]; - } - - /** - * @dataProvider providerForTestGetViewParameters - */ - public function testGetViewParameters(array $desinationContentIds, array $expected) - { - $contentServiceMock = $this->createMock(ContentService::class); - $contentServiceMock - ->method('loadContentInfoList') - ->with($desinationContentIds) - ->will($this->returnCallback(static function ($arg) { - $return = []; - if (in_array(123, $arg)) { - $return[123] = new ContentInfo(['status' => ContentInfo::STATUS_DRAFT]); - } - - if (in_array(456, $arg)) { - $return[456] = new ContentInfo(['status' => ContentInfo::STATUS_PUBLISHED]); - } - - if (in_array(789, $arg)) { - $return[789] = new ContentInfo(['status' => ContentInfo::STATUS_TRASHED]); - } - - return $return; - })); - - $parameterProvider = new ParameterProvider($contentServiceMock); - $parameters = $parameterProvider->getViewParameters(new Field([ - 'value' => new Value($desinationContentIds), - ])); - - TestCase::assertSame($parameters, $expected); - } - - public function testNotFoundGetViewParameters() - { - $contentId = 123; - - $contentServiceMock = $this->createMock(ContentService::class); - $contentServiceMock - ->method('loadContentInfoList') - ->with([$contentId]) - ->willReturn([]); - - $parameterProvider = new ParameterProvider($contentServiceMock); - $parameters = $parameterProvider->getViewParameters(new Field([ - 'value' => new Value([$contentId]), - ])); - - TestCase::assertSame($parameters, ['available' => [$contentId => false]]); - } - - public function testUnauthorizedGetViewParameters() - { - $contentId = 123; - - $contentServiceMock = $this->createMock(ContentService::class); - $contentServiceMock - ->method('loadContentInfoList') - ->with([$contentId]) - ->willReturn([]); - - $parameterProvider = new ParameterProvider($contentServiceMock); - $parameters = $parameterProvider->getViewParameters(new Field([ - 'value' => new Value([$contentId]), - ])); - - TestCase::assertSame($parameters, ['available' => [$contentId => false]]); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/User/ParameterProviderTest.php b/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/User/ParameterProviderTest.php deleted file mode 100644 index 3b648acff2..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/User/ParameterProviderTest.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\FieldType\Tests\User; - -use DateInterval; -use DateTimeImmutable; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\User\PasswordInfo; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\Core\FieldType\User\Value; -use eZ\Publish\Core\MVC\Symfony\FieldType\User\ParameterProvider; -use PHPUnit\Framework\TestCase; - -class ParameterProviderTest extends TestCase -{ - private const EXAMPLE_USER_ID = 1; - - /** @var \eZ\Publish\API\Repository\UserService|\PHPUnit\Framework\MockObject\MockObject */ - private $userService; - - /** @var \eZ\Publish\API\Repository\Values\User\User|\PHPUnit\Framework\MockObject\MockObject */ - private $user; - - /** @var \eZ\Publish\Core\MVC\Symfony\FieldType\User\ParameterProvider */ - private $parameterProvider; - - protected function setUp(): void - { - $this->user = $this->createMock(User::class); - - $this->userService = $this->createMock(UserService::class); - $this->userService - ->method('loadUser') - ->with(self::EXAMPLE_USER_ID, []) - ->willReturn($this->user); - - $this->parameterProvider = new ParameterProvider($this->userService); - } - - /** - * @requires PHP < 8.1 - */ - public function testGetViewParameters(): void - { - $passwordExpiresIn = 14; - $passwordExpiresAt = (new DateTimeImmutable())->add(new DateInterval('P14D')); - - $this->userService - ->method('getPasswordInfo') - ->with($this->user) - ->willReturn(new PasswordInfo($passwordExpiresAt)); - - $parameters = $this->parameterProvider->getViewParameters( - $this->createFieldMock(self::EXAMPLE_USER_ID) - ); - - $this->assertFalse($parameters['is_password_expired']); - $this->assertEquals($passwordExpiresAt, $parameters['password_expires_at']); - $this->assertEquals($passwordExpiresIn, $parameters['password_expires_in']->days); - } - - public function testGetViewParametersWhenPasswordExpirationDateIsNull(): void - { - $field = $this->createFieldMock(self::EXAMPLE_USER_ID); - - $this->userService - ->method('getPasswordInfo') - ->with($this->user) - ->willReturn(new PasswordInfo()); - - $this->assertEquals([ - 'is_password_expired' => false, - 'password_expires_at' => null, - 'password_expires_in' => null, - ], $this->parameterProvider->getViewParameters($field)); - } - - private function createFieldMock(int $userId): Field - { - $field = $this->createMock(Field::class); - $field->method('__get')->with('value')->willReturn(new Value([ - 'contentId' => $userId, - ])); - - return $field; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/View/ParameterProviderRegistryTest.php b/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/View/ParameterProviderRegistryTest.php deleted file mode 100644 index 7a84cedec0..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/View/ParameterProviderRegistryTest.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\FieldType\Tests\View; - -use eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderInterface; -use eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry; -use PHPUnit\Framework\TestCase; - -class ParameterProviderRegistryTest extends TestCase -{ - /** - * @covers \eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry::setParameterProvider - * @covers \eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry::hasParameterProvider - */ - public function testSetHasParameterProvider() - { - $registry = new ParameterProviderRegistry(); - $this->assertFalse($registry->hasParameterProvider('foo')); - $registry->setParameterProvider( - $this->createMock(ParameterProviderInterface::class), - 'foo' - ); - $this->assertTrue($registry->hasParameterProvider('foo')); - } - - /** - * @covers \eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry::getParameterProvider - */ - public function testGetParameterProviderFail() - { - $this->expectException(\InvalidArgumentException::class); - - $registry = new ParameterProviderRegistry(); - $registry->getParameterProvider('foo'); - } - - /** - * @covers \eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry::setParameterProvider - * @covers \eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry::getParameterProvider - */ - public function testGetParameterProvider() - { - $provider = $this->createMock(ParameterProviderInterface::class); - $registry = new ParameterProviderRegistry(); - $registry->setParameterProvider($provider, 'foo'); - $this->assertSame($provider, $registry->getParameterProvider('foo')); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/User/ParameterProvider.php b/eZ/Publish/Core/MVC/Symfony/FieldType/User/ParameterProvider.php deleted file mode 100644 index e5ae8570c9..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/User/ParameterProvider.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\FieldType\User; - -use DateTime; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderInterface; - -class ParameterProvider implements ParameterProviderInterface -{ - /** @var \eZ\Publish\API\Repository\UserService */ - private $userService; - - public function __construct(UserService $userService) - { - $this->userService = $userService; - } - - public function getViewParameters(Field $field): array - { - $passwordInfo = $this->userService->getPasswordInfo( - $this->userService->loadUser($field->value->contentId) - ); - - $passwordExpiresIn = null; - if (!$passwordInfo->isPasswordExpired() && $passwordInfo->hasExpirationDate()) { - $passwordExpiresIn = $passwordInfo->getExpirationDate()->diff(new DateTime()); - } - - return [ - 'is_password_expired' => $passwordInfo->isPasswordExpired(), - 'password_expires_at' => $passwordInfo->getExpirationDate(), - 'password_expires_in' => $passwordExpiresIn, - ]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProvider.php b/eZ/Publish/Core/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProvider.php deleted file mode 100644 index d6c08de095..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProvider.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProvider; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderInterface; -use eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface; -use eZ\Publish\Core\MVC\Symfony\RequestStackAware; -use Symfony\Component\HttpFoundation\Request; - -/** - * Locale view parameter provider. - */ -class LocaleParameterProvider implements ParameterProviderInterface -{ - use RequestStackAware; - - /** @var \eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface */ - protected $localeConverter; - - public function __construct(LocaleConverterInterface $localeConverter) - { - $this->localeConverter = $localeConverter; - } - - /** - * Returns a hash with 'locale' as key and locale string in POSIX format as value. - * - * Locale from request object will be used as locale if set, otherwise field language code - * will be converted to locale string. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - * - * @return array - */ - public function getViewParameters(Field $field) - { - $parameters = []; - - $request = $this->getCurrentRequest(); - if ($request && $request->attributes->has('_locale')) { - $parameters['locale'] = $request->attributes->get('_locale'); - } else { - $parameters['locale'] = $this->localeConverter->convertToPOSIX($field->languageCode); - } - - return $parameters; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/View/ParameterProviderInterface.php b/eZ/Publish/Core/MVC/Symfony/FieldType/View/ParameterProviderInterface.php deleted file mode 100644 index 6fa5b79421..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/View/ParameterProviderInterface.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\FieldType\View; - -use eZ\Publish\API\Repository\Values\Content\Field; - -/** - * Interface for services providing additional parameters to a fieldtype's view template (using ez_render_field() helper). - * Each instance of this interface needs to be correctly registered in the ParameterProviderRegistry. - * - * @see \eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistryInterface - */ -interface ParameterProviderInterface -{ - /** - * Returns a hash of parameters to inject to the associated fieldtype's view template. - * Returned parameters will only be available for associated field type. - * - * Key is the parameter name (the variable name exposed in the template, in the 'parameters' array). - * Value is the parameter's value. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field The field parameters are provided for. - * - * @return array - */ - public function getViewParameters(Field $field); -} diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/View/ParameterProviderRegistryInterface.php b/eZ/Publish/Core/MVC/Symfony/FieldType/View/ParameterProviderRegistryInterface.php deleted file mode 100644 index 5b1231c5ea..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/View/ParameterProviderRegistryInterface.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\FieldType\View; - -/** - * Interface for fieldtypes view parameter provider registry. - */ -interface ParameterProviderRegistryInterface -{ - /** - * Checks if a parameter provider is set for a given field type identifier. - * - * @param string $fieldTypeIdentifier - * - * @return bool - */ - public function hasParameterProvider($fieldTypeIdentifier); - - /** - * Returns parameter provider for given field type identifier. - * - * @param string $fieldTypeIdentifier - * - * @throws \InvalidArgumentException If no parameter provider is provided for $fieldTypeIdentifier. - * - * @return \eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderInterface - */ - public function getParameterProvider($fieldTypeIdentifier); - - /** - * Sets a parameter provider for given field type identifier. - * - * @param \eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderInterface $parameterProvider - * @param string $fieldTypeIdentifier - */ - public function setParameterProvider(ParameterProviderInterface $parameterProvider, $fieldTypeIdentifier); -} diff --git a/eZ/Publish/Core/MVC/Symfony/Locale/LocaleConverter.php b/eZ/Publish/Core/MVC/Symfony/Locale/LocaleConverter.php deleted file mode 100644 index 8857a6bf0c..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Locale/LocaleConverter.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Locale; - -use Psr\Log\LoggerInterface; - -class LocaleConverter implements LocaleConverterInterface -{ - /** - * Conversion map, indexed by eZ Publish locale. - * See locale.yml. - * - * @var array - */ - private $conversionMap; - - /** - * Conversion map, indexed by POSIX locale. - * - * @var array - */ - private $reverseConversionMap; - - /** @var \Psr\Log\LoggerInterface */ - private $logger; - - public function __construct(array $conversionMap, LoggerInterface $logger) - { - $this->conversionMap = $conversionMap; - $this->reverseConversionMap = array_flip($conversionMap); - $this->logger = $logger; - } - - /** - * Converts a locale in eZ Publish internal format to POSIX format. - * Returns null if conversion cannot be made. - * - * @param string $ezpLocale - * - * @return string|null - */ - public function convertToPOSIX($ezpLocale) - { - if (!isset($this->conversionMap[$ezpLocale])) { - $this->logger->warning("Could not convert locale '$ezpLocale' to POSIX format. Please check your locale configuration in ezplatform.yml"); - - return; - } - - return $this->conversionMap[$ezpLocale]; - } - - /** - * Converts a locale in POSIX format to eZ Publish internal format. - * Returns null if conversion cannot be made. - * - * @param string $posixLocale - * - * @return string|null - */ - public function convertToEz($posixLocale) - { - if (!isset($this->reverseConversionMap[$posixLocale])) { - $this->logger->warning("Could not convert locale '$posixLocale' to eZ Publish format. Please check your locale configuration in ezplatform.yml"); - - return; - } - - return $this->reverseConversionMap[$posixLocale]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Locale/LocaleConverterInterface.php b/eZ/Publish/Core/MVC/Symfony/Locale/LocaleConverterInterface.php deleted file mode 100644 index a8d4f597b9..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Locale/LocaleConverterInterface.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Locale; - -/** - * Interface for locale converters. - * eZ Platform uses <ISO639-2/B>-<ISO3166-Alpha2> locale format (mostly, some supported locales being out of this format, e.g. cro-HR). - * Symfony uses the standard POSIX locale format (<ISO639-1>_<ISO3166-Alpha2>), which is supported by Intl PHP extension. - * - * Locale converters are meant to convert in those 2 formats back and forth. - */ -interface LocaleConverterInterface -{ - /** - * Converts a locale in eZ Publish internal format to POSIX format. - * Returns null if conversion cannot be made. - * - * @param string $ezpLocale - * - * @return string|null - */ - public function convertToPOSIX($ezpLocale); - - /** - * Converts a locale in POSIX format to eZ Publish internal format. - * Returns null if conversion cannot be made. - * - * @param string $posixLocale - * - * @return string|null - */ - public function convertToEz($posixLocale); -} diff --git a/eZ/Publish/Core/MVC/Symfony/Locale/Tests/LocaleConverterTest.php b/eZ/Publish/Core/MVC/Symfony/Locale/Tests/LocaleConverterTest.php deleted file mode 100644 index 835f694b92..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Locale/Tests/LocaleConverterTest.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Locale\Tests; - -use eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverter; -use PHPUnit\Framework\TestCase; -use Psr\Log\LoggerInterface; - -class LocaleConverterTest extends TestCase -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverter */ - private $localeConverter; - - /** @var \PHPUnit\Framework\MockObject\MockObject */ - private $logger; - - private $conversionMap; - - protected function setUp(): void - { - parent::setUp(); - $this->conversionMap = [ - 'eng-GB' => 'en_GB', - 'eng-US' => 'en_US', - 'fre-FR' => 'fr_FR', - 'ger-DE' => 'de_DE', - 'nor-NO' => 'no_NO', - 'cro-HR' => 'hr_HR', - ]; - - $this->logger = $this->createMock(LoggerInterface::class); - $this->localeConverter = new LocaleConverter($this->conversionMap, $this->logger); - } - - /** - * @dataProvider convertToPOSIXProvider - * - * @covers \eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverter::convertToPOSIX - * - * @param $ezpLocale - * @param $expected - */ - public function testConvertToPOSIX($ezpLocale, $expected) - { - if ($expected === null) { - $this->logger - ->expects($this->once()) - ->method('warning'); - } - - $this->assertSame($expected, $this->localeConverter->convertToPOSIX($ezpLocale)); - } - - public function convertToPOSIXProvider() - { - return [ - ['eng-GB', 'en_GB'], - ['eng-US', 'en_US'], - ['fre-FR', 'fr_FR'], - ['chi-CN', null], - ['epo-EO', null], - ['nor-NO', 'no_NO'], - ]; - } - - /** - * @dataProvider convertToEzProvider - * - * @covers \eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverter::convertToEz - * - * @param $posixLocale - * @param $expected - */ - public function testConvertToEz($posixLocale, $expected) - { - if ($expected === null) { - $this->logger - ->expects($this->once()) - ->method('warning'); - } - - $this->assertSame($expected, $this->localeConverter->convertToEz($posixLocale)); - } - - public function convertToEzProvider() - { - return [ - ['en_GB', 'eng-GB'], - ['en_US', 'eng-US'], - ['fr_FR', 'fre-FR'], - ['zh-CN', null], - ['eo', null], - ['no_NO', 'nor-NO'], - ]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/MVCEvents.php b/eZ/Publish/Core/MVC/Symfony/MVCEvents.php deleted file mode 100644 index 4247b52819..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/MVCEvents.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony; - -final class MVCEvents -{ - /** - * The SITEACCESS event occurs after the SiteAccess matching has occurred. - * This event gives further control on the matched SiteAccess. - * - * The event listener method receives a \eZ\Publish\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent - */ - public const SITEACCESS = 'ezpublish.siteaccess'; - - /** - * The PRE_CONTENT_VIEW event occurs right before a view is rendered for a content, via the content view controller. - * This event is triggered by the view manager and allows you to inject additional parameters to the content view template. - * - * The event listener method receives a \eZ\Publish\Core\MVC\Symfony\Event\PreContentViewEvent - * - * @see eZ\Publish\Core\MVC\Symfony\View\Manager - */ - public const PRE_CONTENT_VIEW = 'ezpublish.pre_content_view'; - - /** - * The API_CONTENT_EXCEPTION event occurs when the API throws an exception that could not be caught internally - * (missing field type, internal error...). - * It allows further programmatic handling (like rendering a custom view) for the exception thrown. - * - * The event listener method receives an \eZ\Publish\Core\MVC\Symfony\Event\APIContentExceptionEvent. - */ - public const API_CONTENT_EXCEPTION = 'ezpublish.api.contentException'; - - /** - * CONFIG_SCOPE_CHANGE event occurs when configuration scope is changed (e.g. for content preview in a given siteaccess). - * - * The event listener method receives a eZ\Publish\Core\MVC\Symfony\Event\ScopeChangeEvent instance. - */ - public const CONFIG_SCOPE_CHANGE = 'ezpublish.config.scope_change'; - - /** - * CONFIG_SCOPE_RESTORE event occurs when original configuration scope is restored. - * It always happens after a scope change (see CONFIG_SCOPE_CHANGE). - * - * The event listener method receives a eZ\Publish\Core\MVC\Symfony\Event\ScopeChangeEvent instance. - */ - public const CONFIG_SCOPE_RESTORE = 'ezpublish.config.scope_restore'; - - /** - * INTERACTIVE_LOGIN event occurs when a user has been authenticated by a foreign user provider. - * Listening to this event gives a chance to retrieve a valid API user to be injected in repository. - * - * The event listener method receives a eZ\Publish\Core\MVC\Symfony\Event\InteractiveLoginEvent instance. - */ - public const INTERACTIVE_LOGIN = 'ezpublish.security.interactive_login'; - - /** - * ROUTE_REFERENCE_GENERATION event occurs when a RouteReference is generated, and gives an opportunity to - * alter the RouteReference, e.g. by adding parameters. - * - * The event listener method receives a eZ\Publish\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent instance. - */ - public const ROUTE_REFERENCE_GENERATION = 'ezpublish.routing.reference_generation'; - - /** - * CACHE_CLEAR_CONTENT event occurs when cache needs to be cleared for a content. - * It gives the opportunity to add related locations to clear (aka "smart cache clearing"). - * - * The event listener method receives a eZ\Publish\Core\MVC\Symfony\Event\ContentCacheClearEvent instance. - * - * @deprecated Since 6.12, not triggered anymore when using ezplatform-http-cache package. - */ - public const CACHE_CLEAR_CONTENT = 'ezpublish.cache_clear.content'; -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ConfigurableMatcherFactoryInterface.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ConfigurableMatcherFactoryInterface.php deleted file mode 100644 index 13dbc37078..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ConfigurableMatcherFactoryInterface.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher; - -/** - * @internal - */ -interface ConfigurableMatcherFactoryInterface extends MatcherFactoryInterface -{ - public function setMatchConfig(array $matchConfig): void; -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Depth.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Depth.php deleted file mode 100644 index ef5ce4f2b4..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Depth.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\View\ContentValueView; -use eZ\Publish\Core\MVC\Symfony\View\LocationValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class Depth extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(Location $location) - { - return isset($this->values[$location->depth]); - } - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - $location = $this->repository->sudo( - static function (Repository $repository) use ($contentInfo) { - return $repository->getLocationService()->loadLocation($contentInfo->mainLocationId); - } - ); - - return isset($this->values[$location->depth]); - } - - public function match(View $view) - { - if ($view instanceof LocationValueView) { - return isset($this->values[$view->getLocation()->depth]); - } - - if ($view instanceof ContentValueView) { - return $this->matchContentInfo($view->getContent()->contentInfo); - } - - return false; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/Content.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/Content.php deleted file mode 100644 index e6f366c8a7..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/Content.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; -use eZ\Publish\Core\MVC\Symfony\View\ContentValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class Content extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(APILocation $location) - { - return isset($this->values[$location->getContentInfo()->id]); - } - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - return isset($this->values[$contentInfo->id]); - } - - public function match(View $view) - { - if (!$view instanceof ContentValueView) { - return false; - } - - return isset($this->values[$view->getContent()->contentInfo->id]); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/ContentType.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/ContentType.php deleted file mode 100644 index 4e86751476..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/ContentType.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; -use eZ\Publish\Core\MVC\Symfony\View\ContentValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class ContentType extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(APILocation $location) - { - return isset($this->values[$location->getContentInfo()->contentTypeId]); - } - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - return isset($this->values[$contentInfo->contentTypeId]); - } - - public function match(View $view) - { - if (!$view instanceof ContentValueView) { - return false; - } - - return isset($this->values[$view->getContent()->contentInfo->contentTypeId]); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroup.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroup.php deleted file mode 100644 index 7fc4a95039..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroup.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; -use eZ\Publish\Core\MVC\Symfony\View\ContentValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class ContentTypeGroup extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(APILocation $location) - { - return $this->matchContentTypeId($location->getContentInfo()->contentTypeId); - } - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - return $this->matchContentTypeId($contentInfo->contentTypeId); - } - - public function match(View $view) - { - if (!$view instanceof ContentValueView) { - return false; - } - - return $this->matchContentTypeId($view->getContent()->contentInfo->contentTypeId); - } - - /** - * @return bool - */ - private function matchContentTypeId($contentTypeId) - { - $contentTypeGroups = $this->repository - ->getContentTypeService() - ->loadContentType($contentTypeId) - ->getContentTypeGroups(); - - foreach ($contentTypeGroups as $group) { - if (isset($this->values[$group->id])) { - return true; - } - } - - return false; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/Location.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/Location.php deleted file mode 100644 index e1759870f4..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/Location.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; -use eZ\Publish\Core\MVC\Symfony\View\LocationValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class Location extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(APILocation $location) - { - return isset($this->values[$location->id]); - } - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - return isset($this->values[$contentInfo->mainLocationId]); - } - - public function match(View $view) - { - if (!$view instanceof LocationValueView) { - return false; - } - - if (null === $view->getLocation()) { - return false; - } - - return isset($this->values[$view->getLocation()->id]); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/LocationRemote.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/LocationRemote.php deleted file mode 100644 index b1eb20c0ca..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/LocationRemote.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; -use eZ\Publish\Core\MVC\Symfony\View\LocationValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class LocationRemote extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(APILocation $location) - { - } - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - } - - public function match(View $view) - { - if (!$view instanceof LocationValueView) { - return false; - } - - return isset($this->values[$view->getLocation()->remoteId]); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/ParentContentType.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/ParentContentType.php deleted file mode 100644 index baf3bbe15e..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/ParentContentType.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; -use eZ\Publish\Core\MVC\Symfony\View\LocationValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class ParentContentType extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(APILocation $location) - { - $parent = $this->repository->sudo( - static function (Repository $repository) use ($location) { - return $repository->getLocationService()->loadLocation($location->parentLocationId); - } - ); - - return isset($this->values[$parent->getContentInfo()->contentTypeId]); - } - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - $location = $this->repository->sudo( - static function (Repository $repository) use ($contentInfo) { - return $repository->getLocationService()->loadLocation($contentInfo->mainLocationId); - } - ); - - return $this->matchLocation($location); - } - - public function match(View $view) - { - if (!$view instanceof LocationValueView) { - return false; - } - $parent = $this->loadParentLocation( - $view->getLocation()->parentLocationId - ); - - return isset($this->values[$parent->getContentInfo()->contentTypeId]); - } - - /** - * @return Location - */ - private function loadParentLocation($locationId) - { - return $this->repository->sudo( - static function (Repository $repository) use ($locationId) { - return $repository->getLocationService()->loadLocation($locationId); - } - ); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/ParentLocation.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/ParentLocation.php deleted file mode 100644 index 09a8cee234..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/ParentLocation.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; -use eZ\Publish\Core\MVC\Symfony\View\LocationValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class ParentLocation extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(APILocation $location) - { - return isset($this->values[$location->parentLocationId]); - } - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - $location = $this->repository->sudo( - static function (Repository $repository) use ($contentInfo) { - return $repository->getLocationService()->loadLocation($contentInfo->mainLocationId); - } - ); - - return isset($this->values[$location->parentLocationId]); - } - - public function match(View $view) - { - if (!$view instanceof LocationValueView) { - return false; - } - - return isset($this->values[$view->getLocation()->parentLocationId]); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/Remote.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/Remote.php deleted file mode 100644 index 5a67d0e38d..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/Remote.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; -use eZ\Publish\Core\MVC\Symfony\View\ContentValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class Remote extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(APILocation $location) - { - return isset($this->values[$location->remoteId]); - } - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - return isset($this->values[$contentInfo->remoteId]); - } - - public function match(View $view) - { - if (!$view instanceof ContentValueView) { - return false; - } - - return isset($this->values[$view->getContent()->contentInfo->remoteId]); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/Section.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/Section.php deleted file mode 100644 index a9e7f4d8c1..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/Section.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; -use eZ\Publish\Core\MVC\Symfony\View\ContentValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class Section extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(APILocation $location) - { - return isset($this->values[$location->getContentInfo()->sectionId]); - } - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - return isset($this->values[$contentInfo->sectionId]); - } - - public function match(View $view) - { - if (!$view instanceof ContentValueView) { - return false; - } - - return isset($this->values[$view->getContent()->contentInfo->sectionId]); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Identifier/ContentType.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Identifier/ContentType.php deleted file mode 100644 index af49e9a40a..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Identifier/ContentType.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; -use eZ\Publish\Core\MVC\Symfony\View\ContentValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class ContentType extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(Location $location) - { - $contentType = $this->repository - ->getContentTypeService() - ->loadContentType($location->getContentInfo()->contentTypeId); - - return isset($this->values[$contentType->identifier]); - } - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - $contentType = $this->repository - ->getContentTypeService() - ->loadContentType($contentInfo->contentTypeId); - - return isset($this->values[$contentType->identifier]); - } - - public function match(View $view) - { - if (!$view instanceof ContentValueView) { - return false; - } - - $contentType = $this->repository - ->getContentTypeService() - ->loadContentType($view->getContent()->contentInfo->contentTypeId); - - return isset($this->values[$contentType->identifier]); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentType.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentType.php deleted file mode 100644 index 612f2e6616..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentType.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; -use eZ\Publish\Core\MVC\Symfony\View\ContentValueView; -use eZ\Publish\Core\MVC\Symfony\View\LocationValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class ParentContentType extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(APILocation $location) - { - $parentContentType = $this->repository->sudo( - static function (Repository $repository) use ($location) { - $parent = $repository->getLocationService()->loadLocation($location->parentLocationId); - - return $repository - ->getContentTypeService() - ->loadContentType($parent->getContentInfo()->contentTypeId); - } - ); - - return isset($this->values[$parentContentType->identifier]); - } - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - $location = $this->repository->sudo( - static function (Repository $repository) use ($contentInfo) { - return $repository->getLocationService()->loadLocation($contentInfo->mainLocationId); - } - ); - - return $this->matchLocation($location); - } - - public function match(View $view) - { - if ($view instanceof LocationValueView) { - return $this->matchLocation($view->getLocation()); - } - - if ($view instanceof ContentValueView) { - return $this->matchContentInfo($view->getContent()->contentInfo); - } - - return false; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Identifier/Section.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Identifier/Section.php deleted file mode 100644 index 32fab2e864..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Identifier/Section.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; -use eZ\Publish\Core\MVC\Symfony\View\ContentValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class Section extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(Location $location) - { - $section = $this->repository->sudo( - static function (Repository $repository) use ($location) { - return $repository->getSectionService()->loadSection( - $location->getContentInfo()->sectionId - ); - } - ); - - return isset($this->values[$section->identifier]); - } - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - $section = $this->repository->sudo( - static function (Repository $repository) use ($contentInfo) { - return $repository->getSectionService()->loadSection( - $contentInfo->sectionId - ); - } - ); - - return isset($this->values[$section->identifier]); - } - - public function match(View $view) - { - if (!$view instanceof ContentValueView) { - return false; - } - - $contentInfo = $view->getContent()->contentInfo; - $section = $this->repository->sudo( - static function (Repository $repository) use ($contentInfo) { - return $repository->getSectionService()->loadSection( - $contentInfo->sectionId - ); - } - ); - - return isset($this->values[$section->identifier]); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/MatcherInterface.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/MatcherInterface.php deleted file mode 100644 index 1ad7963121..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/MatcherInterface.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Matcher\ViewMatcherInterface; - -/** - * Main interface for content/location matchers. - */ -interface MatcherInterface extends ViewMatcherInterface -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(Location $location); - - /** - * Checks if a ContentInfo object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo); -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/UrlAlias.php b/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/UrlAlias.php deleted file mode 100644 index 45228c0954..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/UrlAlias.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\View\LocationValueView; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class UrlAlias extends MultipleValued -{ - /** - * Checks if a Location object matches. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - */ - public function matchLocation(Location $location) - { - $urlAliasService = $this->repository->getURLAliasService(); - $locationUrls = array_merge( - $urlAliasService->listLocationAliases($location), - $urlAliasService->listLocationAliases($location, false) - ); - - foreach ($this->values as $pattern => $val) { - foreach ($locationUrls as $urlAlias) { - if (strpos((string)$urlAlias->path, "/$pattern") === 0) { - return true; - } - } - } - - return false; - } - - /** - * Not supported since UrlAlias is meaningful for location objects only. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @throws \RuntimeException - * - * @return bool - */ - public function matchContentInfo(ContentInfo $contentInfo) - { - throw new \RuntimeException('matchContentInfo() is not supported by the UrlAlias matcher'); - } - - public function setMatchingConfig($matchingConfig) - { - if (!is_array($matchingConfig)) { - $matchingConfig = [$matchingConfig]; - } - - array_walk( - $matchingConfig, - static function (&$item) { - $item = trim($item, '/ '); - } - ); - - parent::setMatchingConfig($matchingConfig); - } - - public function match(View $view) - { - if (!$view instanceof LocationValueView) { - return false; - } - - return $this->matchLocation($view->getLocation()); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecorator.php b/eZ/Publish/Core/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecorator.php deleted file mode 100644 index d76ad5288c..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecorator.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher; - -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\View\View; - -/** - * Injects dynamic configuration before every matching operation. - */ -class DynamicallyConfiguredMatcherFactoryDecorator implements MatcherFactoryInterface -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\MatcherFactoryInterface|\eZ\Publish\Core\MVC\Symfony\Matcher\ConfigurableMatcherFactoryInterface */ - private $innerConfigurableMatcherFactory; - - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ - private $configResolver; - - /** @var string */ - private $parameterName; - - /** @var string|null */ - private $namespace; - - /** @var string|null */ - private $scope; - - public function __construct( - MatcherFactoryInterface $innerConfigurableMatcherFactory, - ConfigResolverInterface $configResolver, - string $parameterName, - ?string $namespace = null, - ?string $scope = null - ) { - $this->innerConfigurableMatcherFactory = $innerConfigurableMatcherFactory; - $this->configResolver = $configResolver; - $this->parameterName = $parameterName; - $this->namespace = $namespace; - $this->scope = $scope; - } - - public function match(View $view) - { - $matchConfig = $this->configResolver->getParameter($this->parameterName, $this->namespace, $this->scope); - $this->innerConfigurableMatcherFactory->setMatchConfig($matchConfig); - - return $this->innerConfigurableMatcherFactory->match($view); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/MatcherFactoryInterface.php b/eZ/Publish/Core/MVC/Symfony/Matcher/MatcherFactoryInterface.php deleted file mode 100644 index b39e99a99d..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/MatcherFactoryInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher; - -use eZ\Publish\Core\MVC\Symfony\View\View; - -interface MatcherFactoryInterface -{ - /** - * Checks if $valueObject has a usable configuration for $viewType. - * If so, the configuration hash will be returned. - * - * $valueObject can be for example a Location or a Content object. - * - * @param \eZ\Publish\Core\MVC\Symfony\View\View $view - * - * @return array|null The matched configuration as a hash, containing template or controller to use, or null if not matched. - */ - public function match(View $view); -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/AbstractMatcherFactoryTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/AbstractMatcherFactoryTest.php deleted file mode 100644 index 74e05cd245..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/AbstractMatcherFactoryTest.php +++ /dev/null @@ -1,190 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; -use eZ\Publish\Core\Repository\Repository; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use PHPUnit\Framework\TestCase; - -abstract class AbstractMatcherFactoryTest extends TestCase -{ - /** - * Returns a valid ValueObject (supported by current MatcherFactory), that will match the test rules. - * i.e. Should return eZ\Publish\API\Repository\Values\Content\Location for LocationMatcherFactory. - * - * @return \eZ\Publish\API\Repository\Values\ValueObject - */ - abstract protected function getMatchableValueObject(); - - /** - * Returns a valid ValueObject (supported by current MatcherFactory), that won't match the test rules. - * i.e. Should return eZ\Publish\API\Repository\Values\Content\Location for LocationMatcherFactory. - * - * @return \eZ\Publish\API\Repository\Values\ValueObject - */ - abstract protected function getNonMatchableValueObject(); - - /** - * Returns the matcher class to use in test configuration. - * Must be relative to the matcher's ::MATCHER_RELATIVE_NAMESPACE constant. - * i.e.: Id\\Location. - * - * @return string - */ - abstract protected function getMatcherClass(); - - /** - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::__construct - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::match - */ - public function testMatchFailNoViewType() - { - $matcherFactory = new $this->matcherFactoryClass($this->getRepositoryMock(), []); - $this->assertNull($matcherFactory->match($this->getContentView(), 'full')); - } - - /** - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::__construct - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::match - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::getMatcher - */ - public function testMatchInvalidMatcher() - { - $this->expectException(\InvalidArgumentException::class); - - $matcherFactory = new $this->matcherFactoryClass( - $this->getRepositoryMock(), - [ - 'full' => [ - 'test' => [ - 'template' => 'foo.html.twig', - 'match' => [ - 'NonExistingMatcher' => true, - ], - ], - ], - ] - ); - $matcherFactory->match($this->getMatchableValueObject(), 'full'); - } - - /** - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::__construct - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::match - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::getMatcher - */ - public function testMatch() - { - $expectedConfigHash = [ - 'template' => 'foo.html.twig', - 'match' => [ - $this->getMatcherClass() => 456, - ], - ]; - $matcherFactory = new $this->matcherFactoryClass( - $this->getRepositoryMock(), - [ - 'full' => [ - 'not_matching' => [ - 'template' => 'bar.html.twig', - 'match' => [ - $this->getMatcherClass() => 123, - ], - ], - 'test' => $expectedConfigHash, - ], - ] - ); - $configHash = $matcherFactory->match($this->getMatchableValueObject()); - $this->assertArrayHasKey('matcher', $configHash); - $this->assertInstanceOf( - constant("$this->matcherFactoryClass::MATCHER_RELATIVE_NAMESPACE") . '\\' . $this->getMatcherClass(), - $configHash['matcher'] - ); - // Calling a 2nd time to check if the result has been properly cached in memory - $this->assertSame( - $configHash, - $matcherFactory->match( - $this->getMatchableValueObject(), - 'full' - ) - ); - - unset($configHash['matcher']); - $this->assertSame($expectedConfigHash, $configHash); - } - - /** - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::__construct - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::match - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::getMatcher - */ - public function testMatchFail() - { - $matcherFactory = new $this->matcherFactoryClass( - $this->getRepositoryMock(), - [ - 'full' => [ - 'not_matching' => [ - 'template' => 'bar.html.twig', - 'match' => [ - $this->getMatcherClass() => 123, - ], - ], - 'test' => [ - 'template' => 'foo.html.twig', - 'match' => [ - $this->getMatcherClass() => 456, - ], - ], - ], - ] - ); - $this->assertNull( - $matcherFactory->match( - $this->getNonMatchableValueObject(), - 'full' - ) - ); - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject - */ - protected function getRepositoryMock() - { - return $this->createMock(Repository::class); - } - - /** - * @param array $properties - * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\MVC\Symfony\View\ContentView - */ - protected function getContentView(array $contentInfoProperties = [], array $locationProperties = []) - { - $view = new ContentView(); - $view->setContent( - new Content( - [ - 'versionInfo' => new VersionInfo( - [ - 'contentInfo' => new ContentInfo($contentInfoProperties), - ] - ), - ] - ) - ); - $view->setLocation(new Location($locationProperties)); - - return $view; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/BaseTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/BaseTest.php deleted file mode 100644 index d321389d2d..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/BaseTest.php +++ /dev/null @@ -1,117 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\View\Provider\Location\Configured; -use eZ\Publish\Core\Repository\Mapper\RoleDomainMapper; -use eZ\Publish\Core\Repository\Permission\LimitationService; -use eZ\Publish\Core\Repository\Permission\PermissionResolver; -use eZ\Publish\Core\Repository\Repository; -use eZ\Publish\SPI\Persistence\User\Handler as SPIUserHandler; -use PHPUnit\Framework\TestCase; - -abstract class BaseTest extends TestCase -{ - /** @var \PHPUnit\Framework\MockObject\MockObject */ - protected $repositoryMock; - - protected function setUp(): void - { - parent::setUp(); - $this->repositoryMock = $this->getRepositoryMock(); - } - - /** - * @param array $matchingConfig - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - protected function getPartiallyMockedViewProvider(array $matchingConfig = []) - { - return $this - ->getMockBuilder(Configured::class) - ->setConstructorArgs( - [ - $this->repositoryMock, - $matchingConfig, - ] - ) - ->setMethods(['getMatcher']) - ->getMock(); - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject - */ - protected function getRepositoryMock() - { - $repositoryClass = Repository::class; - - return $this - ->getMockBuilder($repositoryClass) - ->disableOriginalConstructor() - ->setMethods( - array_diff( - get_class_methods($repositoryClass), - ['sudo'] - ) - ) - ->getMock(); - } - - /** - * @param array $properties - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - protected function getLocationMock(array $properties = []) - { - return $this - ->getMockBuilder(Location::class) - ->setConstructorArgs([$properties]) - ->getMockForAbstractClass(); - } - - /** - * @param array $properties - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - protected function getContentInfoMock(array $properties = []) - { - return $this-> - getMockBuilder(ContentInfo::class) - ->setConstructorArgs([$properties]) - ->getMockForAbstractClass(); - } - - protected function getPermissionResolverMock() - { - $configResolverMock = $this->createMock(ConfigResolverInterface::class); - $configResolverMock - ->method('getParameter') - ->with('anonymous_user_id') - ->willReturn(10); - - return $this - ->getMockBuilder(PermissionResolver::class) - ->setMethods(null) - ->setConstructorArgs( - [ - $this->createMock(RoleDomainMapper::class), - $this->createMock(LimitationService::class), - $this->createMock(SPIUserHandler::class), - $configResolverMock, - [], - ] - ) - ->getMock(); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ContentTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ContentTest.php deleted file mode 100644 index 6f85984722..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ContentTest.php +++ /dev/null @@ -1,128 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Content as ContentIdMatcher; -use eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\BaseTest; - -class ContentTest extends BaseTest -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Content */ - private $matcher; - - protected function setUp(): void - { - parent::setUp(); - $this->matcher = new ContentIdMatcher(); - } - - /** - * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Content::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * - * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param bool $expectedResult - */ - public function testMatchLocation($matchingConfig, Location $location, $expectedResult) - { - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame($expectedResult, $this->matcher->matchLocation($location)); - } - - public function matchLocationProvider() - { - return [ - [ - 123, - $this->generateLocationForContentId(123), - true, - ], - [ - 123, - $this->generateLocationForContentId(456), - false, - ], - [ - [123, 789], - $this->generateLocationForContentId(456), - false, - ], - [ - [123, 789], - $this->generateLocationForContentId(789), - true, - ], - ]; - } - - /** - * Generates a Location mock in respect of a given content Id. - * - * @param int $contentId - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - private function generateLocationForContentId($contentId) - { - $location = $this->getLocationMock(); - $location - ->expects($this->any()) - ->method('getContentInfo') - ->will( - $this->returnValue( - $this->getContentInfoMock(['id' => $contentId]) - ) - ); - - return $location; - } - - /** - * @dataProvider matchContentInfoProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Content::matchContentInfo - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * - * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param bool $expectedResult - */ - public function testMatchContentInfo($matchingConfig, ContentInfo $contentInfo, $expectedResult) - { - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame($expectedResult, $this->matcher->matchContentInfo($contentInfo)); - } - - public function matchContentInfoProvider() - { - return [ - [ - 123, - $this->getContentInfoMock(['id' => 123]), - true, - ], - [ - 123, - $this->getContentInfoMock(['id' => 456]), - false, - ], - [ - [123, 789], - $this->getContentInfoMock(['id' => 456]), - false, - ], - [ - [123, 789], - $this->getContentInfoMock(['id' => 789]), - true, - ], - ]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ContentTypeTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ContentTypeTest.php deleted file mode 100644 index 3f2a8ee313..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ContentTypeTest.php +++ /dev/null @@ -1,150 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentType as ContentTypeIdMatcher; -use eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\BaseTest; - -class ContentTypeTest extends BaseTest -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentType */ - private $matcher; - - protected function setUp(): void - { - parent::setUp(); - $this->matcher = new ContentTypeIdMatcher(); - } - - /** - * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentType::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * - * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param bool $expectedResult - */ - public function testMatchLocation($matchingConfig, Location $location, $expectedResult) - { - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame($expectedResult, $this->matcher->matchLocation($location)); - } - - public function matchLocationProvider() - { - $data = []; - - $data[] = [ - 123, - $this->generateLocationForContentType(123), - true, - ]; - - $data[] = [ - 123, - $this->generateLocationForContentType(456), - false, - ]; - - $data[] = [ - [123, 789], - $this->generateLocationForContentType(456), - false, - ]; - - $data[] = [ - [123, 789], - $this->generateLocationForContentType(789), - true, - ]; - - return $data; - } - - /** - * Generates a Location object in respect of a given content type identifier. - * - * @param int $contentTypeId - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - private function generateLocationForContentType($contentTypeId) - { - $location = $this->getLocationMock(); - $location - ->expects($this->any()) - ->method('getContentInfo') - ->will( - $this->returnValue( - $this->generateContentInfoForContentType($contentTypeId) - ) - ); - - return $location; - } - - /** - * Generates a ContentInfo object in respect of a given content type identifier. - * - * @param int $contentTypeId - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - private function generateContentInfoForContentType($contentTypeId) - { - return $this->getContentInfoMock(['contentTypeId' => $contentTypeId]); - } - - /** - * @dataProvider matchContentInfoProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentType::matchContentInfo - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * - * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param bool $expectedResult - */ - public function testMatchContentInfo($matchingConfig, ContentInfo $contentInfo, $expectedResult) - { - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame($expectedResult, $this->matcher->matchContentInfo($contentInfo)); - } - - public function matchContentInfoProvider() - { - $data = []; - - $data[] = [ - 123, - $this->generateContentInfoForContentType(123), - true, - ]; - - $data[] = [ - 123, - $this->generateContentInfoForContentType(456), - false, - ]; - - $data[] = [ - [123, 789], - $this->generateContentInfoForContentType(456), - false, - ]; - - $data[] = [ - [123, 789], - $this->generateContentInfoForContentType(789), - true, - ]; - - return $data; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/LocationTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/LocationTest.php deleted file mode 100644 index 82b04fba35..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/LocationTest.php +++ /dev/null @@ -1,106 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Location as LocationIdMatcher; -use eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\BaseTest; - -class LocationTest extends BaseTest -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Location */ - private $matcher; - - protected function setUp(): void - { - parent::setUp(); - $this->matcher = new LocationIdMatcher(); - } - - /** - * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Location::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * - * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param bool $expectedResult - */ - public function testMatchLocation($matchingConfig, Location $location, $expectedResult) - { - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame($expectedResult, $this->matcher->matchLocation($location)); - } - - public function matchLocationProvider() - { - return [ - [ - 123, - $this->getLocationMock(['id' => 123]), - true, - ], - [ - 123, - $this->getLocationMock(['id' => 456]), - false, - ], - [ - [123, 789], - $this->getLocationMock(['id' => 456]), - false, - ], - [ - [123, 789], - $this->getLocationMock(['id' => 789]), - true, - ], - ]; - } - - /** - * @dataProvider matchContentInfoProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Location::matchContentInfo - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * - * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param bool $expectedResult - */ - public function testMatchContentInfo($matchingConfig, ContentInfo $contentInfo, $expectedResult) - { - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame($expectedResult, $this->matcher->matchContentInfo($contentInfo)); - } - - public function matchContentInfoProvider() - { - return [ - [ - 123, - $this->getContentInfoMock(['mainLocationId' => 123]), - true, - ], - [ - 123, - $this->getContentInfoMock(['mainLocationId' => 456]), - false, - ], - [ - [123, 789], - $this->getContentInfoMock(['mainLocationId' => 456]), - false, - ], - [ - [123, 789], - $this->getContentInfoMock(['mainLocationId' => 789]), - true, - ], - ]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ParentContentTypeTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ParentContentTypeTest.php deleted file mode 100644 index 7f37305ee7..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ParentContentTypeTest.php +++ /dev/null @@ -1,146 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id; - -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentContentType as ParentContentTypeMatcher; -use eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\BaseTest; - -class ParentContentTypeTest extends BaseTest -{ - private const EXAMPLE_LOCATION_ID = 54; - private const EXAMPLE_PARENT_LOCATION_ID = 2; - - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentContentType */ - private $matcher; - - protected function setUp(): void - { - parent::setUp(); - $this->matcher = new ParentContentTypeMatcher(); - } - - /** - * Returns a Repository mock configured to return the appropriate ContentType object with given id. - * - * @param int $contentTypeId - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - private function generateRepositoryMockForContentTypeId($contentTypeId) - { - $parentContentInfo = $this->getContentInfoMock([ - 'contentTypeId' => $contentTypeId, - 'mainLocationId' => self::EXAMPLE_LOCATION_ID, - ]); - - $parentLocation = $this->getLocationMock([ - 'parentLocationId' => self::EXAMPLE_PARENT_LOCATION_ID, - ]); - $parentLocation->expects($this->once()) - ->method('getContentInfo') - ->will( - $this->returnValue($parentContentInfo) - ); - - $locationServiceMock = $this->createMock(LocationService::class); - - $locationServiceMock->expects($this->atLeastOnce()) - ->method('loadLocation') - ->will( - $this->returnValue($parentLocation) - ); - // The following is used in the case of a match by contentInfo - $locationServiceMock->expects($this->any()) - ->method('loadLocation') - ->will( - $this->returnValue($this->getLocationMock()) - ); - - $repository = $this->getRepositoryMock(); - $repository - ->expects($this->any()) - ->method('getLocationService') - ->will($this->returnValue($locationServiceMock)); - $repository - ->expects($this->any()) - ->method('getPermissionResolver') - ->will($this->returnValue($this->getPermissionResolverMock())); - - return $repository; - } - - /** - * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentContentType::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * @covers \eZ\Publish\Core\MVC\RepositoryAware::setRepository - * - * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Repository $repository - * @param bool $expectedResult - */ - public function testMatchLocation($matchingConfig, Repository $repository, $expectedResult) - { - $this->matcher->setRepository($repository); - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame( - $expectedResult, - $this->matcher->matchLocation($this->getLocationMock(['parentLocationId' => self::EXAMPLE_LOCATION_ID])) - ); - } - - public function matchLocationProvider() - { - return [ - [ - 123, - $this->generateRepositoryMockForContentTypeId(123), - true, - ], - [ - 123, - $this->generateRepositoryMockForContentTypeId(456), - false, - ], - [ - [123, 789], - $this->generateRepositoryMockForContentTypeId(456), - false, - ], - [ - [123, 789], - $this->generateRepositoryMockForContentTypeId(789), - true, - ], - ]; - } - - /** - * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentContentType::matchContentInfo - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * @covers \eZ\Publish\Core\MVC\RepositoryAware::setRepository - * - * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Repository $repository - * @param bool $expectedResult - */ - public function testMatchContentInfo($matchingConfig, Repository $repository, $expectedResult) - { - $this->matcher->setRepository($repository); - $this->matcher->setMatchingConfig($matchingConfig); - - $this->assertSame( - $expectedResult, - $this->matcher->matchContentInfo($this->getContentInfoMock([ - 'mainLocationId' => self::EXAMPLE_LOCATION_ID, - ])) - ); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/RemoteTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/RemoteTest.php deleted file mode 100644 index 143ac550d6..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/RemoteTest.php +++ /dev/null @@ -1,106 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Remote as RemoteIdMatcher; -use eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\BaseTest; - -class RemoteTest extends BaseTest -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Remote */ - private $matcher; - - protected function setUp(): void - { - parent::setUp(); - $this->matcher = new RemoteIdMatcher(); - } - - /** - * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Remote::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * - * @param string|string[] $matchingConfig - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param bool $expectedResult - */ - public function testMatchLocation($matchingConfig, Location $location, $expectedResult) - { - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame($expectedResult, $this->matcher->matchLocation($location)); - } - - public function matchLocationProvider() - { - return [ - [ - 'foo', - $this->getLocationMock(['remoteId' => 'foo']), - true, - ], - [ - 'foo', - $this->getLocationMock(['remoteId' => 'bar']), - false, - ], - [ - ['foo', 'baz'], - $this->getLocationMock(['remoteId' => 'bar']), - false, - ], - [ - ['foo', 'baz'], - $this->getLocationMock(['remoteId' => 'baz']), - true, - ], - ]; - } - - /** - * @dataProvider matchContentInfoProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Remote::matchContentInfo - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * - * @param string|string[] $matchingConfig - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param bool $expectedResult - */ - public function testMatchContentInfo($matchingConfig, ContentInfo $contentInfo, $expectedResult) - { - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame($expectedResult, $this->matcher->matchContentInfo($contentInfo)); - } - - public function matchContentInfoProvider() - { - return [ - [ - 'foo', - $this->getContentInfoMock(['remoteId' => 'foo']), - true, - ], - [ - 'foo', - $this->getContentInfoMock(['remoteId' => 'bar']), - false, - ], - [ - ['foo', 'baz'], - $this->getContentInfoMock(['remoteId' => 'bar']), - false, - ], - [ - ['foo', 'baz'], - $this->getContentInfoMock(['remoteId' => 'baz']), - true, - ], - ]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/SectionTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/SectionTest.php deleted file mode 100644 index 9a33b8dcb9..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/SectionTest.php +++ /dev/null @@ -1,128 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Section as SectionIdMatcher; -use eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\BaseTest; - -class SectionTest extends BaseTest -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Section */ - private $matcher; - - protected function setUp(): void - { - parent::setUp(); - $this->matcher = new SectionIdMatcher(); - } - - /** - * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Section::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * - * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param bool $expectedResult - */ - public function testMatchLocation($matchingConfig, Location $location, $expectedResult) - { - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame($expectedResult, $this->matcher->matchLocation($location)); - } - - public function matchLocationProvider() - { - return [ - [ - 123, - $this->generateLocationForSectionId(123), - true, - ], - [ - 123, - $this->generateLocationForSectionId(456), - false, - ], - [ - [123, 789], - $this->generateLocationForSectionId(456), - false, - ], - [ - [123, 789], - $this->generateLocationForSectionId(789), - true, - ], - ]; - } - - /** - * Generates a Location mock in respect of a given content Id. - * - * @param int $sectionId - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - private function generateLocationForSectionId($sectionId) - { - $location = $this->getLocationMock(); - $location - ->expects($this->any()) - ->method('getContentInfo') - ->will( - $this->returnValue( - $this->getContentInfoMock(['sectionId' => $sectionId]) - ) - ); - - return $location; - } - - /** - * @dataProvider matchContentInfoProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Section::matchContentInfo - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * - * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param bool $expectedResult - */ - public function testMatchContentInfo($matchingConfig, ContentInfo $contentInfo, $expectedResult) - { - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame($expectedResult, $this->matcher->matchContentInfo($contentInfo)); - } - - public function matchContentInfoProvider() - { - return [ - [ - 123, - $this->getContentInfoMock(['sectionId' => 123]), - true, - ], - [ - 123, - $this->getContentInfoMock(['sectionId' => 456]), - false, - ], - [ - [123, 789], - $this->getContentInfoMock(['sectionId' => 456]), - false, - ], - [ - [123, 789], - $this->getContentInfoMock(['sectionId' => 789]), - true, - ], - ]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Identifier/ContentTypeTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Identifier/ContentTypeTest.php deleted file mode 100644 index f7cc73bafc..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Identifier/ContentTypeTest.php +++ /dev/null @@ -1,181 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Identifier; - -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ContentType as ContentTypeIdentifierMatcher; -use eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\BaseTest; - -class ContentTypeTest extends BaseTest -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ContentType */ - private $matcher; - - protected function setUp(): void - { - parent::setUp(); - $this->matcher = new ContentTypeIdentifierMatcher(); - } - - /** - * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ContentType::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * - * @param string|string[] $matchingConfig - * @param \eZ\Publish\API\Repository\Repository $repository - * @param bool $expectedResult - */ - public function testMatchLocation($matchingConfig, Repository $repository, $expectedResult) - { - $this->matcher->setRepository($repository); - $this->matcher->setMatchingConfig($matchingConfig); - - $this->assertSame( - $expectedResult, - $this->matcher->matchLocation($this->generateLocationMock()) - ); - } - - public function matchLocationProvider() - { - $data = []; - - $data[] = [ - 'foo', - $this->generateRepositoryMockForContentTypeIdentifier('foo'), - true, - ]; - - $data[] = [ - 'foo', - $this->generateRepositoryMockForContentTypeIdentifier('bar'), - false, - ]; - - $data[] = [ - ['foo', 'baz'], - $this->generateRepositoryMockForContentTypeIdentifier('bar'), - false, - ]; - - $data[] = [ - ['foo', 'baz'], - $this->generateRepositoryMockForContentTypeIdentifier('baz'), - true, - ]; - - return $data; - } - - /** - * Generates a Location object in respect of a given content type identifier. - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - private function generateLocationMock() - { - $location = $this->getLocationMock(); - $location - ->expects($this->any()) - ->method('getContentInfo') - ->will( - $this->returnValue( - $this->getContentInfoMock(['contentTypeId' => 42]) - ) - ); - - return $location; - } - - /** - * @dataProvider matchContentInfoProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ContentType::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * - * @param string|string[] $matchingConfig - * @param \eZ\Publish\API\Repository\Repository $repository - * @param bool $expectedResult - */ - public function testMatchContentInfo($matchingConfig, Repository $repository, $expectedResult) - { - $this->matcher->setRepository($repository); - $this->matcher->setMatchingConfig($matchingConfig); - - $this->assertSame( - $expectedResult, - $this->matcher->matchContentInfo( - $this->getContentInfoMock(['contentTypeId' => 42]) - ) - ); - } - - public function matchContentInfoProvider() - { - $data = []; - - $data[] = [ - 'foo', - $this->generateRepositoryMockForContentTypeIdentifier('foo'), - true, - ]; - - $data[] = [ - 'foo', - $this->generateRepositoryMockForContentTypeIdentifier('bar'), - false, - ]; - - $data[] = [ - ['foo', 'baz'], - $this->generateRepositoryMockForContentTypeIdentifier('bar'), - false, - ]; - - $data[] = [ - ['foo', 'baz'], - $this->generateRepositoryMockForContentTypeIdentifier('baz'), - true, - ]; - - return $data; - } - - /** - * Returns a Repository mock configured to return the appropriate ContentType object with given identifier. - * - * @param int $contentTypeIdentifier - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - private function generateRepositoryMockForContentTypeIdentifier($contentTypeIdentifier) - { - $contentTypeMock = $this - ->getMockBuilder(ContentType::class) - ->setConstructorArgs( - [['identifier' => $contentTypeIdentifier]] - ) - ->getMockForAbstractClass(); - $contentTypeServiceMock = $this->createMock(ContentTypeService::class); - $contentTypeServiceMock->expects($this->once()) - ->method('loadContentType') - ->with(42) - ->will( - $this->returnValue($contentTypeMock) - ); - - $repository = $this->getRepositoryMock(); - $repository - ->expects($this->any()) - ->method('getContentTypeService') - ->will($this->returnValue($contentTypeServiceMock)); - - return $repository; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Identifier/ParentContentTypeTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Identifier/ParentContentTypeTest.php deleted file mode 100644 index 4e7e83e839..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Identifier/ParentContentTypeTest.php +++ /dev/null @@ -1,168 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Identifier; - -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ParentContentType as ParentContentTypeMatcher; -use eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\BaseTest; - -class ParentContentTypeTest extends BaseTest -{ - private const EXAMPLE_LOCATION_ID = 54; - private const EXAMPLE_PARENT_LOCATION_ID = 2; - - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ParentContentType */ - private $matcher; - - protected function setUp(): void - { - parent::setUp(); - $this->matcher = new ParentContentTypeMatcher(); - } - - /** - * Returns a Repository mock configured to return the appropriate Section object with given section identifier. - * - * @param string $contentTypeIdentifier - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - private function generateRepositoryMockForContentTypeIdentifier($contentTypeIdentifier) - { - $parentContentInfo = $this->getContentInfoMock([ - 'mainLocationId' => self::EXAMPLE_LOCATION_ID, - 'contentTypeId' => 42, - ]); - $parentLocation = $this->getLocationMock([ - 'parentLocationId' => self::EXAMPLE_PARENT_LOCATION_ID, - ]); - $parentLocation->expects($this->once()) - ->method('getContentInfo') - ->will( - $this->returnValue($parentContentInfo) - ); - - $locationServiceMock = $this->createMock(LocationService::class); - $locationServiceMock->expects($this->atLeastOnce()) - ->method('loadLocation') - ->will( - $this->returnValue($parentLocation) - ); - // The following is used in the case of a match by contentInfo - $locationServiceMock->expects($this->any()) - ->method('loadLocation') - ->will( - $this->returnValue($this->getLocationMock()) - ); - - $contentTypeServiceMock = $this->createMock(ContentTypeService::class); - $contentTypeServiceMock->expects($this->once()) - ->method('loadContentType') - ->with(42) - ->will( - $this->returnValue( - $this - ->getMockBuilder(ContentType::class) - ->setConstructorArgs( - [ - ['identifier' => $contentTypeIdentifier], - ] - ) - ->getMockForAbstractClass() - ) - ); - - $repository = $this->getRepositoryMock(); - $repository - ->expects($this->any()) - ->method('getLocationService') - ->will($this->returnValue($locationServiceMock)); - $repository - ->expects($this->once()) - ->method('getContentTypeService') - ->will($this->returnValue($contentTypeServiceMock)); - $repository - ->expects($this->any()) - ->method('getPermissionResolver') - ->will($this->returnValue($this->getPermissionResolverMock())); - - return $repository; - } - - /** - * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ParentContentType::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * @covers \eZ\Publish\Core\MVC\RepositoryAware::setRepository - * - * @param string|string[] $matchingConfig - * @param \eZ\Publish\API\Repository\Repository $repository - * @param bool $expectedResult - */ - public function testMatchLocation($matchingConfig, Repository $repository, $expectedResult) - { - $this->matcher->setRepository($repository); - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame( - $expectedResult, - $this->matcher->matchLocation($this->getLocationMock([ - 'parentLocationId' => self::EXAMPLE_LOCATION_ID, - ])) - ); - } - - public function matchLocationProvider() - { - return [ - [ - 'foo', - $this->generateRepositoryMockForContentTypeIdentifier('foo'), - true, - ], - [ - 'foo', - $this->generateRepositoryMockForContentTypeIdentifier('bar'), - false, - ], - [ - ['foo', 'baz'], - $this->generateRepositoryMockForContentTypeIdentifier('bar'), - false, - ], - [ - ['foo', 'baz'], - $this->generateRepositoryMockForContentTypeIdentifier('baz'), - true, - ], - ]; - } - - /** - * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ParentContentType::matchContentInfo - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * @covers \eZ\Publish\Core\MVC\RepositoryAware::setRepository - * - * @param string|string[] $matchingConfig - * @param \eZ\Publish\API\Repository\Repository $repository - * @param bool $expectedResult - */ - public function testMatchContentInfo($matchingConfig, Repository $repository, $expectedResult) - { - $this->matcher->setRepository($repository); - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame( - $expectedResult, - $this->matcher->matchContentInfo($this->getContentInfoMock([ - 'mainLocationId' => self::EXAMPLE_LOCATION_ID, - ])) - ); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Identifier/SectionTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Identifier/SectionTest.php deleted file mode 100644 index de9330e77b..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Identifier/SectionTest.php +++ /dev/null @@ -1,142 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Identifier; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\SectionService; -use eZ\Publish\API\Repository\Values\Content\Section; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\Section as SectionIdentifierMatcher; -use eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\BaseTest; - -class SectionTest extends BaseTest -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\Section */ - private $matcher; - - protected function setUp(): void - { - parent::setUp(); - $this->matcher = new SectionIdentifierMatcher(); - } - - /** - * Returns a Repository mock configured to return the appropriate Section object with given section identifier. - * - * @param string $sectionIdentifier - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - private function generateRepositoryMockForSectionIdentifier($sectionIdentifier) - { - $sectionServiceMock = $this->createMock(SectionService::class); - $sectionServiceMock->expects($this->once()) - ->method('loadSection') - ->will( - $this->returnValue( - $this - ->getMockBuilder(Section::class) - ->setConstructorArgs( - [ - ['identifier' => $sectionIdentifier], - ] - ) - ->getMockForAbstractClass() - ) - ); - - $repository = $this->getRepositoryMock(); - $repository - ->expects($this->once()) - ->method('getSectionService') - ->will($this->returnValue($sectionServiceMock)); - $repository - ->expects($this->any()) - ->method('getPermissionResolver') - ->will($this->returnValue($this->getPermissionResolverMock())); - - return $repository; - } - - /** - * @dataProvider matchSectionProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\Section::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * @covers \eZ\Publish\Core\MVC\RepositoryAware::setRepository - * - * @param string|string[] $matchingConfig - * @param \eZ\Publish\API\Repository\Repository $repository - * @param bool $expectedResult - */ - public function testMatchLocation($matchingConfig, Repository $repository, $expectedResult) - { - $this->matcher->setRepository($repository); - $this->matcher->setMatchingConfig($matchingConfig); - - $location = $this->getLocationMock(); - $location - ->expects($this->once()) - ->method('getContentInfo') - ->will( - $this->returnValue( - $this->getContentInfoMock(['sectionId' => 1]) - ) - ); - - $this->assertSame( - $expectedResult, - $this->matcher->matchLocation($location) - ); - } - - public function matchSectionProvider() - { - return [ - [ - 'foo', - $this->generateRepositoryMockForSectionIdentifier('foo'), - true, - ], - [ - 'foo', - $this->generateRepositoryMockForSectionIdentifier('bar'), - false, - ], - [ - ['foo', 'baz'], - $this->generateRepositoryMockForSectionIdentifier('bar'), - false, - ], - [ - ['foo', 'baz'], - $this->generateRepositoryMockForSectionIdentifier('baz'), - true, - ], - ]; - } - - /** - * @dataProvider matchSectionProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\Section::matchContentInfo - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * @covers \eZ\Publish\Core\MVC\RepositoryAware::setRepository - * - * @param string|string[] $matchingConfig - * @param \eZ\Publish\API\Repository\Repository $repository - * @param bool $expectedResult - */ - public function testMatchContentInfo($matchingConfig, Repository $repository, $expectedResult) - { - $this->matcher->setRepository($repository); - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame( - $expectedResult, - $this->matcher->matchContentInfo($this->getContentInfoMock(['sectionId' => 1])) - ); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/MultipleValuedTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/MultipleValuedTest.php deleted file mode 100644 index 9d046c3763..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/MultipleValuedTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased; - -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; - -class MultipleValuedTest extends BaseTest -{ - /** - * @dataProvider matchingConfigProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::getValues - */ - public function testSetMatchingConfig($matchingConfig) - { - $matcher = $this->getMultipleValuedMatcherMock(); - $matcher->setMatchingConfig($matchingConfig); - $values = $matcher->getValues(); - $this->assertIsArray($values); - - $matchingConfig = is_array($matchingConfig) ? $matchingConfig : [$matchingConfig]; - foreach ($matchingConfig as $val) { - $this->assertContains($val, $values); - } - } - - /** - * Returns a set of matching values, either single or multiple. - * - * @return array - */ - public function matchingConfigProvider() - { - return [ - [ - 'singleValue', - ['one', 'two', 'three'], - [123, 'nous irons au bois'], - 456, - ], - ]; - } - - /** - * @covers \eZ\Publish\Core\MVC\RepositoryAware::setRepository - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::getRepository - */ - public function testInjectRepository() - { - $matcher = $this->getMultipleValuedMatcherMock(); - $matcher->setRepository($this->repositoryMock); - $this->assertSame($this->repositoryMock, $matcher->getRepository()); - } - - private function getMultipleValuedMatcherMock() - { - return $this->getMockForAbstractClass(MultipleValued::class); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/UrlAliasTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/UrlAliasTest.php deleted file mode 100644 index f799f056e3..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/UrlAliasTest.php +++ /dev/null @@ -1,160 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\URLAliasService; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias as UrlAliasMatcher; - -class UrlAliasTest extends BaseTest -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias */ - private $matcher; - - protected function setUp(): void - { - parent::setUp(); - $this->matcher = new UrlAliasMatcher(); - } - - /** - * @dataProvider setMatchingConfigProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias::setMatchingConfig - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * - * @param string $matchingConfig - * @param string[] $expectedValues - */ - public function testSetMatchingConfig($matchingConfig, $expectedValues) - { - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame( - $this->matcher->getValues(), - $expectedValues - ); - } - - public function setMatchingConfigProvider() - { - return [ - ['/foo/bar/', ['foo/bar']], - ['/foo/bar/', ['foo/bar']], - ['/foo/bar', ['foo/bar']], - [['/foo/bar/', 'baz/biz/'], ['foo/bar', 'baz/biz']], - [['foo/bar', 'baz/biz'], ['foo/bar', 'baz/biz']], - ]; - } - - /** - * Returns a Repository mock configured to return the appropriate Section object with given section identifier. - * - * @param string $path - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - private function generateRepositoryMockForUrlAlias($path) - { - // First an url alias that will never match, then the right url alias. - // This ensures to test even if the location has several url aliases. - $urlAliasList = [ - $this->createMock(URLAlias::class), - $this - ->getMockBuilder(URLAlias::class) - ->setConstructorArgs([['path' => $path]]) - ->getMockForAbstractClass(), - ]; - - $urlAliasServiceMock = $this->createMock(URLAliasService::class); - $urlAliasServiceMock->expects($this->at(0)) - ->method('listLocationAliases') - ->with( - $this->isInstanceOf(Location::class), - true - ) - ->will($this->returnValue([])); - $urlAliasServiceMock->expects($this->at(1)) - ->method('listLocationAliases') - ->with( - $this->isInstanceOf(Location::class), - false - ) - ->will($this->returnValue($urlAliasList)); - - $repository = $this->getRepositoryMock(); - $repository - ->expects($this->once()) - ->method('getURLAliasService') - ->will($this->returnValue($urlAliasServiceMock)); - - return $repository; - } - - /** - * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias::setMatchingConfig - * @covers \eZ\Publish\Core\MVC\RepositoryAware::setRepository - * - * @param string|string[] $matchingConfig - * @param \eZ\Publish\API\Repository\Repository $repository - * @param bool $expectedResult - */ - public function testMatchLocation($matchingConfig, Repository $repository, $expectedResult) - { - $this->matcher->setRepository($repository); - $this->matcher->setMatchingConfig($matchingConfig); - $this->assertSame( - $expectedResult, - $this->matcher->matchLocation($this->getLocationMock()) - ); - } - - public function matchLocationProvider() - { - return [ - [ - 'foo/url', - $this->generateRepositoryMockForUrlAlias('/foo/url'), - true, - ], - [ - '/foo/url', - $this->generateRepositoryMockForUrlAlias('/foo/url'), - true, - ], - [ - 'foo/url', - $this->generateRepositoryMockForUrlAlias('/bar/url'), - false, - ], - [ - ['foo/url', 'baz'], - $this->generateRepositoryMockForUrlAlias('/bar/url'), - false, - ], - [ - ['foo/url ', 'baz '], - $this->generateRepositoryMockForUrlAlias('/baz'), - true, - ], - ]; - } - - /** - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias::matchContentInfo - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias::setMatchingConfig - */ - public function testMatchContentInfo() - { - $this->expectException(\RuntimeException::class); - - $this->matcher->setMatchingConfig('foo/bar'); - $this->matcher->matchContentInfo($this->getContentInfoMock()); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBasedMatcherFactoryTest.php b/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBasedMatcherFactoryTest.php deleted file mode 100644 index 40862bcfb6..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBasedMatcherFactoryTest.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests; - -abstract class ContentBasedMatcherFactoryTest extends AbstractMatcherFactoryTest -{ - /** - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::__construct - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::match - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\AbstractMatcherFactory::getMatcher - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBasedMatcherFactory::getMatcher - */ - public function testMatchNonContentBasedMatcher() - { - $this->expectException(\InvalidArgumentException::class); - - $matcherFactory = new $this->matcherFactoryClass( - $this->getRepositoryMock(), - [ - 'full' => [ - 'test' => [ - 'template' => 'foo.html.twig', - 'match' => [ - \stdClass::class => true, - ], - ], - ], - ] - ); - $matcherFactory->match($this->getMatchableValueObject(), 'full'); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/ChainRouter.php b/eZ/Publish/Core/MVC/Symfony/Routing/ChainRouter.php deleted file mode 100644 index 5657f063c5..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Routing/ChainRouter.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Routing; - -use Symfony\Cmf\Component\Routing\ChainRouter as BaseChainRouter; - -class ChainRouter extends BaseChainRouter -{ -} diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/SimplifiedRequest.php b/eZ/Publish/Core/MVC/Symfony/Routing/SimplifiedRequest.php deleted file mode 100644 index c6c6b9540c..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Routing/SimplifiedRequest.php +++ /dev/null @@ -1,161 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Routing; - -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * @property-read string $scheme The request scheme - http or https - * @property-read string $host The host name - * @property-read string $port The port the request is made on - * @property-read string $pathinfo The path being requested relative to the executed script - * @property-read array $queryParams Array of parameters extracted from the query string - * @property-read array $languages List of languages acceptable by the client browser - * @property-read array $headers Hash of request headers - */ -class SimplifiedRequest extends ValueObject -{ - /** - * The request scheme (http or https). - * - * @var string - */ - protected $scheme; - - /** - * The host name. - * - * @var string - */ - protected $host; - - /** - * The port the request is made on. - * - * @var string - */ - protected $port; - - /** - * The path being requested relative to the executed script. - * The path info always starts with a /. - * - * @var string - */ - protected $pathinfo; - - /** - * Array of parameters extracted from the query string. - * - * @var array - */ - protected $queryParams; - - /** - * List of languages acceptable by the client browser. - * The languages are ordered in the user browser preferences. - * - * @var array - */ - protected $languages; - - /** - * Hash of request headers. - * - * @var array - */ - protected $headers; - - /** - * @param array $headers - */ - public function setHeaders(array $headers) - { - $this->headers = $headers; - } - - /** - * @param string $host - */ - public function setHost($host) - { - $this->host = $host; - } - - /** - * @param array $languages - */ - public function setLanguages(array $languages) - { - $this->languages = $languages; - } - - /** - * @param string $pathinfo - */ - public function setPathinfo($pathinfo) - { - $this->pathinfo = $pathinfo; - } - - /** - * @param string $port - */ - public function setPort($port) - { - $this->port = $port; - } - - /** - * @param array $queryParams - */ - public function setQueryParams(array $queryParams) - { - $this->queryParams = $queryParams; - } - - /** - * @param string $scheme - */ - public function setScheme($scheme) - { - $this->scheme = $scheme; - } - - /** - * Constructs a SimplifiedRequest object from a standard URL (http://www.example.com/foo/bar?queryParam=value). - * - * @param string $url - * - * @internal - * - * @return \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest - */ - public static function fromUrl($url) - { - $elements = parse_url($url); - $elements['pathinfo'] = isset($elements['path']) ? $elements['path'] : ''; - - if (isset($elements['query'])) { - parse_str($elements['query'], $queryParams); - $elements['queryParams'] = $queryParams; - } - - // Remove unwanted keys returned by parse_url() so that we don't have them as properties. - unset($elements['path'], $elements['query'], $elements['user'], $elements['pass'], $elements['fragment']); - - return new static($elements); - } - - public function __sleep() - { - // Clean up headers for serialization not have a too heavy string (i.e. for ESI/Hinclude tags). - $this->headers = []; - - return ['scheme', 'host', 'port', 'pathinfo', 'queryParams', 'languages', 'headers']; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/Tests/SimplifiedRequestTest.php b/eZ/Publish/Core/MVC/Symfony/Routing/Tests/SimplifiedRequestTest.php deleted file mode 100644 index 557e01a693..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Routing/Tests/SimplifiedRequestTest.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Routing\Tests; - -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use PHPUnit\Framework\TestCase; - -class SimplifiedRequestTest extends TestCase -{ - /** - * @param string $url - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $expectedRequest - * - * @dataProvider fromUrlProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest::fromUrl - */ - public function testFromUrl($url, $expectedRequest) - { - self::assertEquals( - $expectedRequest, - SimplifiedRequest::fromUrl($url) - ); - } - - public function fromUrlProvider() - { - return [ - [ - 'http://www.example.com/foo/bar', - new SimplifiedRequest( - [ - 'scheme' => 'http', - 'host' => 'www.example.com', - 'pathinfo' => '/foo/bar', - ] - ), - ], - [ - 'https://www.example.com/', - new SimplifiedRequest( - [ - 'scheme' => 'https', - 'host' => 'www.example.com', - 'pathinfo' => '/', - ] - ), - ], - [ - 'http://www.example.com/foo?param=value&this=that', - new SimplifiedRequest( - [ - 'scheme' => 'http', - 'host' => 'www.example.com', - 'pathinfo' => '/foo', - 'queryParams' => ['param' => 'value', 'this' => 'that'], - ] - ), - ], - ]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/Tests/UrlAliasRouterTest.php b/eZ/Publish/Core/MVC/Symfony/Routing/Tests/UrlAliasRouterTest.php deleted file mode 100644 index a0ace6b9bf..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Routing/Tests/UrlAliasRouterTest.php +++ /dev/null @@ -1,798 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Routing\Tests; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\URLAliasService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator; -use eZ\Publish\Core\MVC\Symfony\Routing\UrlAliasRouter; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\View\Manager as ViewManager; -use eZ\Publish\Core\Repository\Repository; -use eZ\Publish\Core\Repository\Values\Content\Location; -use PHPUnit\Framework\TestCase; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Routing\RequestContext; -use Symfony\Component\Routing\RouteCollection; -use Symfony\Component\Routing\RouterInterface; - -class UrlAliasRouterTest extends TestCase -{ - /** @var \PHPUnit\Framework\MockObject\MockObject */ - protected $repository; - - /** @var \PHPUnit\Framework\MockObject\MockObject */ - protected $urlAliasService; - - /** @var \PHPUnit\Framework\MockObject\MockObject */ - protected $locationService; - - /** @var \PHPUnit\Framework\MockObject\MockObject */ - protected $contentService; - - /** @var \PHPUnit\Framework\MockObject\MockObject */ - protected $urlALiasGenerator; - - protected $requestContext; - - /** @var \eZ\Publish\Core\MVC\Symfony\Routing\UrlAliasRouter */ - protected $router; - - protected function setUp(): void - { - parent::setUp(); - $repositoryClass = Repository::class; - $this->repository = $repository = $this - ->getMockBuilder($repositoryClass) - ->disableOriginalConstructor() - ->setMethods( - array_diff( - get_class_methods($repositoryClass), - ['sudo'] - ) - ) - ->getMock(); - $this->urlAliasService = $this->createMock(URLAliasService::class); - $this->locationService = $this->createMock(LocationService::class); - $this->contentService = $this->createMock(ContentService::class); - $this->urlALiasGenerator = $this - ->getMockBuilder(UrlAliasGenerator::class) - ->setConstructorArgs( - [ - $repository, - $this->createMock(RouterInterface::class), - $this->createMock(ConfigResolverInterface::class), - ] - ) - ->getMock(); - $this->requestContext = new RequestContext(); - - $this->router = $this->getRouter($this->locationService, $this->urlAliasService, $this->contentService, $this->urlALiasGenerator, $this->requestContext); - } - - /** - * @param \eZ\Publish\API\Repository\LocationService $locationService - * @param \eZ\Publish\API\Repository\URLAliasService $urlAliasService - * @param \eZ\Publish\API\Repository\ContentService $contentService - * @param \eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator $urlAliasGenerator - * @param \Symfony\Component\Routing\RequestContext $requestContext - * - * @return \eZ\Publish\Core\MVC\Symfony\Routing\UrlAliasRouter - */ - protected function getRouter(LocationService $locationService, URLAliasService $urlAliasService, ContentService $contentService, UrlAliasGenerator $urlAliasGenerator, RequestContext $requestContext) - { - return new UrlAliasRouter($locationService, $urlAliasService, $contentService, $urlAliasGenerator, $requestContext); - } - - public function testRequestContext() - { - $this->assertSame($this->requestContext, $this->router->getContext()); - $newContext = new RequestContext(); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('setRequestContext') - ->with($newContext); - $this->router->setContext($newContext); - $this->assertSame($newContext, $this->router->getContext()); - } - - public function testMatch() - { - $this->expectException(\RuntimeException::class); - - $this->router->match('/foo'); - } - - /** - * @dataProvider providerTestSupports - */ - public function testSupports($routeReference, $isSupported) - { - $this->assertSame($isSupported, $this->router->supports($routeReference)); - } - - public function providerTestSupports() - { - return [ - [new Location(), true], - [new \stdClass(), false], - [UrlAliasRouter::URL_ALIAS_ROUTE_NAME, true], - ['some_route_name', false], - ]; - } - - public function testGetRouteCollection() - { - $this->assertInstanceOf(RouteCollection::class, $this->router->getRouteCollection()); - } - - /** - * @param $pathInfo - * - * @return \Symfony\Component\HttpFoundation\Request - */ - protected function getRequestByPathInfo($pathInfo) - { - $request = Request::create($pathInfo); - $request->attributes->set('semanticPathinfo', $pathInfo); - $request->attributes->set( - 'siteaccess', - new SiteAccess( - 'test', - 'fake', - $this->createMock(Matcher::class) - ) - ); - - return $request; - } - - public function testMatchRequestLocation() - { - $pathInfo = '/foo/bar'; - $destinationId = 123; - $urlAlias = new URLAlias( - [ - 'path' => $pathInfo, - 'type' => UrlAlias::LOCATION, - 'destination' => $destinationId, - 'isHistory' => false, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('loadLocation') - ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - '_controller' => UrlAliasRouter::VIEW_ACTION, - 'locationId' => $destinationId, - 'contentId' => 456, - 'viewType' => ViewManager::VIEW_TYPE_FULL, - 'layout' => true, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestLocationWithCaseRedirect() - { - $pathInfo = '/Foo/bAR'; - $urlAliasPath = '/foo/bar'; - $destinationId = 123; - $urlAlias = new URLAlias( - [ - 'path' => $urlAliasPath, - 'type' => UrlAlias::LOCATION, - 'destination' => $destinationId, - 'isHistory' => false, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('isUriPrefixExcluded') - ->with($pathInfo) - ->will($this->returnValue(false)); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('loadLocation') - ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - '_controller' => UrlAliasRouter::VIEW_ACTION, - 'locationId' => $destinationId, - 'contentId' => 456, - 'viewType' => ViewManager::VIEW_TYPE_FULL, - 'layout' => true, - 'needsRedirect' => true, - 'semanticPathinfo' => $urlAliasPath, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestLocationWrongCaseUriPrefixExcluded() - { - $pathInfo = '/Foo/bAR'; - $urlAliasPath = '/foo/bar'; - $destinationId = 123; - $urlAlias = new URLAlias( - [ - 'path' => $urlAliasPath, - 'type' => UrlAlias::LOCATION, - 'destination' => $destinationId, - 'isHistory' => false, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('isUriPrefixExcluded') - ->with($pathInfo) - ->will($this->returnValue(true)); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('loadLocation') - ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - '_controller' => UrlAliasRouter::VIEW_ACTION, - 'contentId' => 456, - 'locationId' => $destinationId, - 'viewType' => ViewManager::VIEW_TYPE_FULL, - 'layout' => true, - 'needsRedirect' => true, - 'semanticPathinfo' => $urlAliasPath, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestLocationCorrectCaseUriPrefixExcluded() - { - $pathInfo = $urlAliasPath = '/foo/bar'; - $destinationId = 123; - $urlAlias = new URLAlias( - [ - 'path' => $urlAliasPath, - 'type' => UrlAlias::LOCATION, - 'destination' => $destinationId, - 'isHistory' => false, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('isUriPrefixExcluded') - ->with($pathInfo) - ->will($this->returnValue(true)); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('loadLocation') - ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - '_controller' => UrlAliasRouter::VIEW_ACTION, - 'locationId' => $destinationId, - 'contentId' => 456, - 'viewType' => ViewManager::VIEW_TYPE_FULL, - 'layout' => true, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - $this->assertFalse($request->attributes->has('needsRedirect')); - $this->assertSame($pathInfo, $request->attributes->get('semanticPathinfo')); - } - - public function testMatchRequestLocationHistory() - { - $pathInfo = '/foo/bar'; - $newPathInfo = '/foo/bar-new'; - $destinationId = 123; - $destinationLocation = new Location([ - 'id' => $destinationId, - 'contentInfo' => new ContentInfo(['id' => 456]), - ]); - $urlAlias = new URLAlias( - [ - 'path' => $pathInfo, - 'type' => UrlAlias::LOCATION, - 'destination' => $destinationId, - 'isHistory' => true, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('generate') - ->with($destinationLocation) - ->will($this->returnValue($newPathInfo)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('loadLocation') - ->will($this->returnValue($destinationLocation)); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - '_controller' => UrlAliasRouter::VIEW_ACTION, - 'locationId' => $destinationId, - 'contentId' => 456, - 'viewType' => ViewManager::VIEW_TYPE_FULL, - 'layout' => true, - 'needsRedirect' => true, - 'semanticPathinfo' => $newPathInfo, - 'prependSiteaccessOnRedirect' => false, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestLocationCustom() - { - $pathInfo = '/foo/bar'; - $destinationId = 123; - $urlAlias = new URLAlias( - [ - 'path' => $pathInfo, - 'type' => UrlAlias::LOCATION, - 'destination' => $destinationId, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => false, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('loadLocation') - ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - '_controller' => UrlAliasRouter::VIEW_ACTION, - 'locationId' => $destinationId, - 'contentId' => 456, - 'viewType' => ViewManager::VIEW_TYPE_FULL, - 'layout' => true, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestLocationCustomForward() - { - $pathInfo = '/foo/bar'; - $newPathInfo = '/foo/bar-new'; - $destinationId = 123; - $destinationLocation = new Location([ - 'id' => $destinationId, - 'contentInfo' => new ContentInfo(['id' => 456]), - ]); - $urlAlias = new URLAlias( - [ - 'path' => $pathInfo, - 'type' => UrlAlias::LOCATION, - 'destination' => $destinationId, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => true, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('loadLocation') - ->with($destinationId) - ->will( - $this->returnValue($destinationLocation) - ); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('generate') - ->with($destinationLocation) - ->will($this->returnValue($newPathInfo)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('loadLocation') - ->will($this->returnValue($destinationLocation)); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - '_controller' => UrlAliasRouter::VIEW_ACTION, - 'locationId' => $destinationId, - 'contentId' => 456, - 'viewType' => ViewManager::VIEW_TYPE_FULL, - 'layout' => true, - 'needsRedirect' => true, - 'semanticPathinfo' => $newPathInfo, - 'prependSiteaccessOnRedirect' => false, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestFail() - { - $this->expectException(\Symfony\Component\Routing\Exception\ResourceNotFoundException::class); - - $pathInfo = '/foo/bar'; - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->throwException(new NotFoundException('URLAlias', $pathInfo))); - $this->router->matchRequest($request); - } - - public function testMatchRequestResource() - { - $pathInfo = '/hello_content/hello_search'; - $destination = '/content/search'; - $urlAlias = new URLAlias( - [ - 'destination' => $destination, - 'path' => $pathInfo, - 'type' => UrlAlias::RESOURCE, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - 'semanticPathinfo' => $destination, - 'needsForward' => true, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestResourceWithRedirect() - { - $pathInfo = '/hello_content/hello_search'; - $destination = '/content/search'; - $urlAlias = new URLAlias( - [ - 'destination' => $destination, - 'path' => $pathInfo, - 'type' => UrlAlias::RESOURCE, - 'forward' => true, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - 'needsRedirect' => true, - 'semanticPathinfo' => $destination, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestResourceWithCaseRedirect() - { - $pathInfo = '/heLLo_contEnt/hEllo_SEarch'; - $urlAliasPath = '/hello_content/hello_search'; - $destination = '/content/search'; - $urlAlias = new URLAlias( - [ - 'destination' => $destination, - 'path' => $urlAliasPath, - 'type' => UrlAlias::RESOURCE, - 'forward' => false, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('isUriPrefixExcluded') - ->with($pathInfo) - ->will($this->returnValue(false)); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - 'semanticPathinfo' => $urlAliasPath, - 'needsRedirect' => true, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - /** - * Tests that forwarding custom alias will redirect to the resource destination rather than - * to the case-corrected alias. - */ - public function testMatchRequestResourceCaseIncorrectWithForwardRedirect() - { - $pathInfo = '/heLLo_contEnt/hEllo_SEarch'; - $urlAliasPath = '/hello_content/hello_search'; - $destination = '/content/search'; - $urlAlias = new URLAlias( - [ - 'destination' => $destination, - 'path' => $urlAliasPath, - 'type' => UrlAlias::RESOURCE, - 'forward' => true, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - 'semanticPathinfo' => $destination, - 'needsRedirect' => true, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestVirtual() - { - $pathInfo = '/foo/bar'; - $urlAlias = new URLAlias( - [ - 'path' => $pathInfo, - 'type' => UrlAlias::VIRTUAL, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - 'semanticPathinfo' => '/', - 'needsForward' => true, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testMatchRequestVirtualWithCaseRedirect() - { - $pathInfo = '/Foo/bAR'; - $urlAliasPath = '/foo/bar'; - $urlAlias = new URLAlias( - [ - 'path' => $urlAliasPath, - 'type' => UrlAlias::VIRTUAL, - ] - ); - $request = $this->getRequestByPathInfo($pathInfo); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('isUriPrefixExcluded') - ->with($pathInfo) - ->will($this->returnValue(false)); - $this->urlAliasService - ->expects($this->once()) - ->method('lookup') - ->with($pathInfo) - ->will($this->returnValue($urlAlias)); - - $expected = [ - '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - 'semanticPathinfo' => $urlAliasPath, - 'needsRedirect' => true, - ]; - $this->assertEquals($expected, $this->router->matchRequest($request)); - } - - public function testGenerateFail() - { - $this->expectException(\Symfony\Component\Routing\Exception\RouteNotFoundException::class); - - $this->router->generate('invalidRoute'); - } - - public function testGenerateWithRouteObject(): void - { - $location = new Location(['id' => 54]); - $parameters = [ - 'some' => 'thing', - ]; - - $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH; - $generatedLink = '/foo/bar'; - - $this->urlALiasGenerator - ->expects($this->once()) - ->method('generate') - ->with($location, $parameters, $referenceType) - ->will($this->returnValue($generatedLink)); - - $this->assertSame( - $generatedLink, - $this->router->generate( - '', - $parameters + [ - RouteObjectInterface::ROUTE_OBJECT => $location, - ], - $referenceType - ) - ); - } - - public function testGenerateNoLocation() - { - $this->expectException(\InvalidArgumentException::class); - - $this->router->generate(UrlAliasRouter::URL_ALIAS_ROUTE_NAME, ['foo' => 'bar']); - } - - public function testGenerateInvalidLocation() - { - $this->expectException(\LogicException::class); - - $this->router->generate(UrlAliasRouter::URL_ALIAS_ROUTE_NAME, ['location' => new \stdClass()]); - } - - public function testGenerateWithLocationId() - { - $locationId = 123; - $location = new Location(['id' => $locationId]); - $parameters = ['some' => 'thing']; - $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH; - $generatedLink = '/foo/bar'; - $this->locationService - ->expects($this->once()) - ->method('loadLocation') - ->with($locationId) - ->will($this->returnValue($location)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('generate') - ->with($location, $parameters, $referenceType) - ->will($this->returnValue($generatedLink)); - $this->assertSame( - $generatedLink, - $this->router->generate( - UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - $parameters + ['locationId' => $locationId], - $referenceType - ) - ); - } - - public function testGenerateWithLocationAsParameter() - { - $locationId = 123; - $location = new Location(['id' => $locationId]); - $parameters = ['some' => 'thing']; - $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH; - $generatedLink = '/foo/bar'; - $this->urlALiasGenerator - ->expects($this->once()) - ->method('generate') - ->with($location, $parameters, $referenceType) - ->will($this->returnValue($generatedLink)); - $this->assertSame( - $generatedLink, - $this->router->generate( - UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - $parameters + ['location' => $location], - $referenceType - ) - ); - } - - public function testGenerateWithContentId() - { - $locationId = 123; - $contentId = 456; - $location = new Location(['id' => $locationId]); - $contentInfo = new ContentInfo(['id' => $contentId, 'mainLocationId' => $locationId]); - $parameters = ['some' => 'thing']; - $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH; - $generatedLink = '/foo/bar'; - $this->contentService - ->expects($this->once()) - ->method('loadContentInfo') - ->with($contentId) - ->will($this->returnValue($contentInfo)); - $this->locationService - ->expects($this->once()) - ->method('loadLocation') - ->with($contentInfo->mainLocationId) - ->will($this->returnValue($location)); - $this->urlALiasGenerator - ->expects($this->once()) - ->method('generate') - ->with($location, $parameters, $referenceType) - ->will($this->returnValue($generatedLink)); - $this->assertSame( - $generatedLink, - $this->router->generate( - UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - $parameters + ['contentId' => $contentId], - $referenceType - ) - ); - } - - public function testGenerateWithContentIdWithMissingMainLocation() - { - $this->expectException(\LogicException::class); - - $contentId = 456; - $contentInfo = new ContentInfo(['id' => $contentId, 'mainLocationId' => null]); - $parameters = ['some' => 'thing']; - $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH; - $this->contentService - ->expects($this->once()) - ->method('loadContentInfo') - ->with($contentId) - ->will($this->returnValue($contentInfo)); - - $this->router->generate( - UrlAliasRouter::URL_ALIAS_ROUTE_NAME, - $parameters + ['contentId' => $contentId], - $referenceType - ); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/UrlAliasRouter.php b/eZ/Publish/Core/MVC/Symfony/Routing/UrlAliasRouter.php deleted file mode 100644 index edfbb52f45..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Routing/UrlAliasRouter.php +++ /dev/null @@ -1,405 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Routing; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\URLAliasService; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator; -use eZ\Publish\Core\MVC\Symfony\View\Manager as ViewManager; -use InvalidArgumentException; -use LogicException; -use Psr\Log\LoggerInterface; -use Symfony\Cmf\Component\Routing\ChainedRouterInterface; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\Routing\Exception\ResourceNotFoundException; -use Symfony\Component\Routing\Exception\RouteNotFoundException; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Symfony\Component\Routing\Matcher\RequestMatcherInterface; -use Symfony\Component\Routing\RequestContext; -use Symfony\Component\Routing\Route as SymfonyRoute; -use Symfony\Component\Routing\RouteCollection; - -class UrlAliasRouter implements ChainedRouterInterface, RequestMatcherInterface -{ - public const URL_ALIAS_ROUTE_NAME = 'ez_urlalias'; - - public const VIEW_ACTION = 'ez_content:viewAction'; - - /** @var \Symfony\Component\Routing\RequestContext */ - protected $requestContext; - - /** @var \eZ\Publish\API\Repository\LocationService */ - protected $locationService; - - /** @var \eZ\Publish\API\Repository\URLAliasService */ - protected $urlAliasService; - - /** @var \eZ\Publish\API\Repository\ContentService */ - protected $contentService; - - /** @var \eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator */ - protected $generator; - - /** - * Holds current root Location id. - * - * @var int|string - */ - protected $rootLocationId; - - /** @var \Psr\Log\LoggerInterface */ - protected $logger; - - public function __construct( - LocationService $locationService, - URLAliasService $urlAliasService, - ContentService $contentService, - UrlAliasGenerator $generator, - RequestContext $requestContext, - LoggerInterface $logger = null - ) { - $this->locationService = $locationService; - $this->urlAliasService = $urlAliasService; - $this->contentService = $contentService; - $this->generator = $generator; - $this->requestContext = $requestContext !== null ? $requestContext : new RequestContext(); - $this->logger = $logger; - } - - /** - * Injects current root Location id. - * - * @param int|string $rootLocationId - */ - public function setRootLocationId($rootLocationId) - { - $this->rootLocationId = $rootLocationId; - } - - /** - * Tries to match a request with a set of routes. - * - * If the matcher can not find information, it must throw one of the exceptions documented - * below. - * - * @param \Symfony\Component\HttpFoundation\Request $request The request to match - * - * @return array An array of parameters - * - * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException If no matching resource could be found - */ - public function matchRequest(Request $request) - { - try { - $requestedPath = $request->attributes->get('semanticPathinfo', $request->getPathInfo()); - $urlAlias = $this->getUrlAlias($requestedPath); - if ($this->rootLocationId === null) { - $pathPrefix = '/'; - } else { - $pathPrefix = $this->generator->getPathPrefixByRootLocationId($this->rootLocationId); - } - - $params = [ - '_route' => self::URL_ALIAS_ROUTE_NAME, - ]; - switch ($urlAlias->type) { - case URLAlias::LOCATION: - $location = $this->generator->loadLocation($urlAlias->destination); - $params += [ - '_controller' => static::VIEW_ACTION, - 'contentId' => $location->contentId, - 'locationId' => $urlAlias->destination, - 'viewType' => ViewManager::VIEW_TYPE_FULL, - 'layout' => true, - ]; - - // For Location alias setup 301 redirect to Location's current URL when: - // 1. alias is history - // 2. alias is custom with forward flag true - // 3. requested URL is not case-sensitive equal with the one loaded - if ($urlAlias->isHistory === true || ($urlAlias->isCustom === true && $urlAlias->forward === true)) { - $params += [ - 'semanticPathinfo' => $this->generator->generate($location, []), - 'needsRedirect' => true, - // Specify not to prepend siteaccess while redirecting when applicable since it would be already present (see UrlAliasGenerator::doGenerate()) - 'prependSiteaccessOnRedirect' => false, - ]; - } elseif ($this->needsCaseRedirect($urlAlias, $requestedPath, $pathPrefix)) { - $params += [ - 'semanticPathinfo' => $this->removePathPrefix($urlAlias->path, $pathPrefix), - 'needsRedirect' => true, - ]; - - if ($urlAlias->destination instanceof Location) { - $params += ['locationId' => $urlAlias->destination->id]; - } - } - - if (isset($this->logger)) { - $this->logger->info("UrlAlias matched location #{$urlAlias->destination}. Forwarding to ViewController"); - } - - break; - - case URLAlias::RESOURCE: - // In URLAlias terms, "forward" means "redirect". - if ($urlAlias->forward) { - $params += [ - 'semanticPathinfo' => '/' . trim($urlAlias->destination, '/'), - 'needsRedirect' => true, - ]; - } elseif ($this->needsCaseRedirect($urlAlias, $requestedPath, $pathPrefix)) { - // Handle case-correction redirect - $params += [ - 'semanticPathinfo' => $this->removePathPrefix($urlAlias->path, $pathPrefix), - 'needsRedirect' => true, - ]; - } else { - $params += [ - 'semanticPathinfo' => '/' . trim($urlAlias->destination, '/'), - 'needsForward' => true, - ]; - } - - break; - - case URLAlias::VIRTUAL: - // Handle case-correction redirect - if ($this->needsCaseRedirect($urlAlias, $requestedPath, $pathPrefix)) { - $params += [ - 'semanticPathinfo' => $this->removePathPrefix($urlAlias->path, $pathPrefix), - 'needsRedirect' => true, - ]; - } else { - // Virtual aliases should load the Content at homepage URL - $params += [ - 'semanticPathinfo' => '/', - 'needsForward' => true, - ]; - } - - break; - } - - return $params; - } catch (NotFoundException $e) { - throw new ResourceNotFoundException($e->getMessage(), $e->getCode(), $e); - } - } - - /** - * Removes prefix from path. - * - * Checks for presence of $prefix and removes it from $path if found. - * - * @param string $path - * @param string $prefix - * - * @return string - */ - protected function removePathPrefix($path, $prefix) - { - if ($prefix !== '/' && mb_stripos($path, $prefix) === 0) { - $path = mb_substr($path, mb_strlen($prefix)); - } - - return $path; - } - - /** - * Returns true of false on comparing $urlAlias->path and $path with case sensitivity. - * - * Used to determine if redirect is needed because requested path is case-different - * from the stored one. - * - * @param \eZ\Publish\API\Repository\Values\Content\URLAlias $loadedUrlAlias - * @param string $requestedPath - * @param string $pathPrefix - * - * @return bool - */ - protected function needsCaseRedirect(URLAlias $loadedUrlAlias, $requestedPath, $pathPrefix) - { - // If requested path is excluded from tree root jail, compare it to loaded UrlAlias directly. - if ($this->generator->isUriPrefixExcluded($requestedPath)) { - return strcmp($loadedUrlAlias->path, $requestedPath) !== 0; - } - - // Compare loaded UrlAlias with requested path, prefixed with configured path prefix. - return - strcmp( - $loadedUrlAlias->path, - $pathPrefix . ($pathPrefix === '/' ? trim($requestedPath, '/') : rtrim($requestedPath, '/')) - ) !== 0 - ; - } - - /** - * Returns the UrlAlias object to use, starting from the request. - * - * @param $pathinfo - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the path does not exist or is not valid for the given language - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - protected function getUrlAlias($pathinfo) - { - return $this->urlAliasService->lookup($pathinfo); - } - - /** - * Gets the RouteCollection instance associated with this Router. - * - * @return \Symfony\Component\Routing\RouteCollection A RouteCollection instance - */ - public function getRouteCollection() - { - return new RouteCollection(); - } - - /** - * Generates a URL for a location, from the given parameters. - * - * It is possible to directly pass a Location object as the route name, as the ChainRouter allows it through ChainedRouterInterface. - * - * If $name is a route name, the "location" key in $parameters must be set to a valid eZ\Publish\API\Repository\Values\Content\Location object. - * "locationId" can also be provided. - * - * If the generator is not able to generate the url, it must throw the RouteNotFoundException - * as documented below. - * - * @see UrlAliasRouter::supports() - * - * @param string $name The name of the route or a Location instance - * @param array $parameters An array of parameters - * @param int $referenceType The type of reference to be generated (one of the constants) - * - * @throws \LogicException - * @throws \Symfony\Component\Routing\Exception\RouteNotFoundException - * @throws \InvalidArgumentException - * - * @return string The generated URL - * - * @api - */ - public function generate(string $name, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string - { - if ($name === '' && - array_key_exists(RouteObjectInterface::ROUTE_OBJECT, $parameters) && - $this->supportsObject($parameters[RouteObjectInterface::ROUTE_OBJECT]) - ) { - $location = $parameters[RouteObjectInterface::ROUTE_OBJECT]; - unset($parameters[RouteObjectInterface::ROUTE_OBJECT]); - - return $this->generator->generate($location, $parameters, $referenceType); - } - - // Normal route name - if ($name === self::URL_ALIAS_ROUTE_NAME) { - if (isset($parameters['location']) || isset($parameters['locationId'])) { - // Check if location is a valid Location object - if (isset($parameters['location']) && !$parameters['location'] instanceof Location) { - throw new LogicException( - "When generating a UrlAlias route, the 'location' parameter must be a valid " . Location::class . '.' - ); - } - - $location = isset($parameters['location']) ? $parameters['location'] : $this->locationService->loadLocation($parameters['locationId']); - unset($parameters['location'], $parameters['locationId'], $parameters['viewType'], $parameters['layout']); - - return $this->generator->generate($location, $parameters, $referenceType); - } - - if (isset($parameters['contentId'])) { - $contentInfo = $this->contentService->loadContentInfo($parameters['contentId']); - unset($parameters['contentId'], $parameters['viewType'], $parameters['layout']); - - if (empty($contentInfo->mainLocationId)) { - throw new LogicException('Cannot generate a UrlAlias route for content without main Location.'); - } - - return $this->generator->generate( - $this->locationService->loadLocation($contentInfo->mainLocationId), - $parameters, - $referenceType - ); - } - - throw new InvalidArgumentException( - "When generating a UrlAlias route, either 'location', 'locationId', or 'contentId' must be provided." - ); - } - - throw new RouteNotFoundException('Could not match route'); - } - - public function setContext(RequestContext $context) - { - $this->requestContext = $context; - $this->generator->setRequestContext($context); - } - - public function getContext() - { - return $this->requestContext; - } - - /** - * Not supported. Please use matchRequest() instead. - * - * @param $pathinfo - * - * @throws \RuntimeException - */ - public function match($pathinfo) - { - throw new \RuntimeException("The UrlAliasRouter doesn't support the match() method. Use matchRequest() instead."); - } - - /** - * Whether the router supports the thing in $name to generate a route. - * - * This check does not need to look if the specific instance can be - * resolved to a route, only whether the router can generate routes from - * objects of this class. - * - * @param mixed $name The route name or route object - * - * @return bool - */ - public function supports($name) - { - return $name === self::URL_ALIAS_ROUTE_NAME || $this->supportsObject($name); - } - - private function supportsObject($object): bool - { - return $object instanceof Location; - } - - /** - * @see \Symfony\Cmf\Component\Routing\VersatileGeneratorInterface::getRouteDebugMessage() - */ - public function getRouteDebugMessage($name, array $parameters = []) - { - if ($name instanceof RouteObjectInterface) { - return 'Route with key ' . $name->getRouteKey(); - } - - if ($name instanceof SymfonyRoute) { - return 'Route with pattern ' . $name->getPath(); - } - - return $name; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Authentication/AnonymousAuthenticationProvider.php b/eZ/Publish/Core/MVC/Symfony/Security/Authentication/AnonymousAuthenticationProvider.php deleted file mode 100644 index 8ac9ab0bb5..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Security/Authentication/AnonymousAuthenticationProvider.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Authentication; - -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\Repository\Values\User\UserReference; -use Symfony\Component\Security\Core\Authentication\Provider\AnonymousAuthenticationProvider as BaseAnonymousProvider; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; - -class AnonymousAuthenticationProvider extends BaseAnonymousProvider -{ - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ - private $configResolver; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionResolver; - - public function setConfigResolver(ConfigResolverInterface $configResolver) - { - $this->configResolver = $configResolver; - } - - public function setPermissionResolver(PermissionResolver $permissionResolver) - { - $this->permissionResolver = $permissionResolver; - } - - public function authenticate(TokenInterface $token) - { - $token = parent::authenticate($token); - $this->permissionResolver->setCurrentUserReference(new UserReference($this->configResolver->getParameter('anonymous_user_id'))); - - return $token; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Authentication/DefaultAuthenticationSuccessHandler.php b/eZ/Publish/Core/MVC/Symfony/Security/Authentication/DefaultAuthenticationSuccessHandler.php deleted file mode 100644 index c4136a74de..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Security/Authentication/DefaultAuthenticationSuccessHandler.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Authentication; - -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use Symfony\Component\Security\Http\Authentication\DefaultAuthenticationSuccessHandler as BaseSuccessHandler; - -class DefaultAuthenticationSuccessHandler extends BaseSuccessHandler -{ - /** - * Injects the ConfigResolver to potentially override default_target_path for redirections after authentication success. - * - * @param \eZ\Publish\Core\MVC\ConfigResolverInterface $configResolver - */ - public function setConfigResolver(ConfigResolverInterface $configResolver) - { - $defaultPage = $configResolver->getParameter('default_page'); - if ($defaultPage !== null) { - $this->options['default_target_path'] = $defaultPage; - } - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/InteractiveLoginToken.php b/eZ/Publish/Core/MVC/Symfony/Security/InteractiveLoginToken.php deleted file mode 100644 index e74251063d..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Security/InteractiveLoginToken.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Security; - -use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; - -/** - * This token is used when a user has been matched by a foreign user provider. - * It is injected in SecurityContext to replace the original token as this one holds a new user. - */ -class InteractiveLoginToken extends UsernamePasswordToken -{ - /** @var string */ - private $originalTokenType; - - public function __construct(UserInterface $user, $originalTokenType, $credentials, $providerKey, array $roles = []) - { - parent::__construct($user, $credentials, $providerKey, $roles); - $this->originalTokenType = $originalTokenType; - } - - /** - * @return string - */ - public function getOriginalTokenType() - { - return $this->originalTokenType; - } - - public function __serialize(): array - { - return [$this->originalTokenType, parent::__serialize()]; - } - - public function __unserialize($serialized): void - { - [$this->originalTokenType, $parentStr] = $serialized; - parent::__unserialize($parentStr); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php b/eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php deleted file mode 100644 index 4effef4c0c..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/DefaultAuthenticationSuccessHandlerTest.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests\Authentication; - -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Security\Authentication\DefaultAuthenticationSuccessHandler; -use eZ\Publish\Core\MVC\Symfony\Security\HttpUtils; -use PHPUnit\Framework\TestCase; -use ReflectionObject; - -class DefaultAuthenticationSuccessHandlerTest extends TestCase -{ - public function testSetConfigResolver() - { - $successHandler = new DefaultAuthenticationSuccessHandler(new HttpUtils(), []); - $refHandler = new ReflectionObject($successHandler); - $refOptions = $refHandler->getProperty('options'); - $refOptions->setAccessible(true); - $options = $refOptions->getValue($successHandler); - $this->assertSame('/', $options['default_target_path']); - - $defaultPage = '/foo/bar'; - $configResolver = $this->createMock(ConfigResolverInterface::class); - $configResolver - ->expects($this->once()) - ->method('getParameter') - ->with('default_page') - ->will($this->returnValue($defaultPage)); - $successHandler->setConfigResolver($configResolver); - $options = $refOptions->getValue($successHandler); - $this->assertSame($defaultPage, $options['default_target_path']); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/UserTest.php b/eZ/Publish/Core/MVC/Symfony/Security/Tests/UserTest.php deleted file mode 100644 index 70bf78e347..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/UserTest.php +++ /dev/null @@ -1,131 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\Core\MVC\Symfony\Security\ReferenceUserInterface; -use eZ\Publish\Core\MVC\Symfony\Security\User; -use eZ\Publish\Core\Repository\Values\User\UserReference; -use PHPUnit\Framework\TestCase; - -class UserTest extends TestCase -{ - public function testConstruct() - { - $login = 'my_username'; - $passwordHash = 'encoded_password'; - $apiUser = $this - ->getMockBuilder(APIUser::class) - ->setConstructorArgs( - [ - [ - 'login' => $login, - 'passwordHash' => $passwordHash, - 'enabled' => true, - ], - ] - ) - ->setMethods(['getUserId']) - ->getMockForAbstractClass(); - - $roles = ['ROLE_USER']; - $apiUser - ->expects($this->once()) - ->method('getUserId') - ->will($this->returnValue(42)); - - $user = new User($apiUser, $roles); - $this->assertSame($apiUser, $user->getAPIUser()); - $this->assertSame($login, $user->getUsername()); - $this->assertSame($passwordHash, $user->getPassword()); - $this->assertSame($roles, $user->getRoles()); - $this->assertNull($user->getSalt()); - } - - public function testIsEqualTo() - { - $userId = 123; - $apiUser = $this->createMock(APIUser::class); - $apiUser - ->expects($this->once()) - ->method('getUserId') - ->will($this->returnValue($userId)); - $roles = ['ROLE_USER']; - - $user = new User($apiUser, $roles); - - $apiUser2 = $this->createMock(APIUser::class); - $apiUser2 - ->expects($this->once()) - ->method('getUserId') - ->will($this->returnValue($userId)); - $user2 = new User($apiUser2, []); - - $this->assertTrue($user->isEqualTo($user2)); - } - - public function testIsNotEqualTo() - { - $apiUser = $this->createMock(APIUser::class); - $apiUser - ->expects($this->once()) - ->method('getUserId') - ->will($this->returnValue(123)); - $roles = ['ROLE_USER']; - - $user = new User($apiUser, $roles); - - $apiUser2 = $this->createMock(APIUser::class); - $apiUser2 - ->expects($this->once()) - ->method('getUserId') - ->will($this->returnValue(456)); - $user2 = new User($apiUser2, []); - - $this->assertFalse($user->isEqualTo($user2)); - } - - public function testIsEqualToNotSameUserType() - { - $user = new User($this->createMock(APIUser::class)); - $user2 = $this->createMock(ReferenceUserInterface::class); - $user2 - ->expects($this->once()) - ->method('getAPIUserReference') - ->willReturn(new UserReference(456)); - $this->assertFalse($user->isEqualTo($user2)); - } - - public function testSetAPIUser() - { - $apiUserA = $this->createMock(APIUser::class); - $apiUserB = $this->createMock(APIUser::class); - - $user = new User($apiUserA); - $user->setAPIUser($apiUserB); - $this->assertSame($apiUserB, $user->getAPIUser()); - } - - public function testToString() - { - $fullName = 'My full name'; - $userContentInfo = $this - ->getMockBuilder(ContentInfo::class) - ->setConstructorArgs([['name' => $fullName]]) - ->getMockForAbstractClass(); - $apiUser = $this->createMock(APIUser::class); - $apiUser - ->expects($this->any()) - ->method('__get') - ->with('contentInfo') - ->will($this->returnValue($userContentInfo)); - - $user = new User($apiUser); - $this->assertSame($fullName, (string)$user); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/User.php b/eZ/Publish/Core/MVC/Symfony/Security/User.php deleted file mode 100644 index 467a91fff4..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Security/User.php +++ /dev/null @@ -1,159 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Security; - -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\Core\Repository\Values\User\UserReference; -use Symfony\Component\Security\Core\User\EquatableInterface; -use Symfony\Component\Security\Core\User\UserInterface as BaseUserInterface; - -class User implements ReferenceUserInterface, EquatableInterface -{ - /** @var \eZ\Publish\API\Repository\Values\User\User */ - private $user; - - /** @var \eZ\Publish\API\Repository\Values\User\UserReference */ - private $reference; - - /** @var array */ - private $roles; - - public function __construct(APIUser $user, array $roles = []) - { - $this->user = $user; - $this->reference = new UserReference($user->getUserId()); - $this->roles = $roles; - } - - /** - * Returns the roles granted to the user. - * - * <code> - * public function getRoles() - * { - * return array( 'ROLE_USER' ); - * } - * </code> - * - * Alternatively, the roles might be stored on a ``roles`` property, - * and populated in any number of different ways when the user object - * is created. - * - * @return Role[] The user roles - */ - public function getRoles() - { - return $this->roles; - } - - /** - * Returns the password used to authenticate the user. - * - * This should be the encoded password. On authentication, a plain-text - * password will be salted, encoded, and then compared to this value. - * - * @return string The password - */ - public function getPassword() - { - return $this->getAPIUser()->passwordHash; - } - - /** - * Returns the salt that was originally used to encode the password. - * - * This can return null if the password was not encoded using a salt. - * - * @return string The salt - */ - public function getSalt() - { - return null; - } - - /** - * Returns the username used to authenticate the user. - * - * @return string The username - */ - public function getUsername() - { - return $this->getAPIUser()->login; - } - - /** - * Removes sensitive data from the user. - * - * This is important if, at any given point, sensitive information like - * the plain-text password is stored on this object. - */ - public function eraseCredentials() - { - } - - /** - * @return \eZ\Publish\API\Repository\Values\User\UserReference - */ - public function getAPIUserReference() - { - return $this->reference; - } - - /** - * @return \eZ\Publish\API\Repository\Values\User\User - */ - public function getAPIUser() - { - if (!$this->user instanceof APIUser) { - throw new \LogicException( - 'Attempted to get APIUser before it has been set by UserProvider, APIUser is not serialized to session' - ); - } - - return $this->user; - } - - /** - * @param \eZ\Publish\API\Repository\Values\User\User $user - */ - public function setAPIUser(APIUser $user) - { - $this->user = $user; - $this->reference = new UserReference($user->getUserId()); - } - - public function isEqualTo(BaseUserInterface $user) - { - // Check for the lighter ReferenceUserInterface first - if ($user instanceof ReferenceUserInterface) { - return $user->getAPIUserReference()->getUserId() === $this->reference->getUserId(); - } elseif ($user instanceof UserInterface) { - return $user->getAPIUser()->getUserId() === $this->reference->getUserId(); - } - - return false; - } - - public function __toString() - { - return $this->getAPIUser()->contentInfo->name; - } - - /** - * Make sure we don't serialize the whole API user object given it's a full fledged api content object. We set - * (& either way refresh) the user object in \eZ\Publish\Core\MVC\Symfony\Security\User\Provider->refreshUser() - * when object wakes back up from session. - * - * @return array - */ - public function __sleep() - { - return ['reference', 'roles']; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/User/APIUserProviderInterface.php b/eZ/Publish/Core/MVC/Symfony/Security/User/APIUserProviderInterface.php deleted file mode 100644 index 8f7c57c7d2..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Security/User/APIUserProviderInterface.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Security\User; - -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use Symfony\Component\Security\Core\User\UserProviderInterface; - -/** - * Interface adding eZ Publish API specific methods to Symfony UserProviderInterface. - */ -interface APIUserProviderInterface extends UserProviderInterface -{ - /** - * Loads a regular user object, usable by Symfony Security component, from a user object returned by Public API. - * - * @param \eZ\Publish\API\Repository\Values\User\User $apiUser - * - * @return \eZ\Publish\Core\MVC\Symfony\Security\User - */ - public function loadUserByAPIUser(APIUser $apiUser); -} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/User/BaseProvider.php b/eZ/Publish/Core/MVC/Symfony/Security/User/BaseProvider.php deleted file mode 100644 index 3d45cae645..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Security/User/BaseProvider.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Security\User; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\Core\MVC\Symfony\Security\ReferenceUserInterface; -use eZ\Publish\Core\MVC\Symfony\Security\User; -use eZ\Publish\Core\MVC\Symfony\Security\UserInterface; -use eZ\Publish\Core\Repository\Values\User\UserReference; -use Symfony\Component\Security\Core\Exception\UnsupportedUserException; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; -use Symfony\Component\Security\Core\User\UserInterface as CoreUserInterface; - -abstract class BaseProvider implements APIUserProviderInterface -{ - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - protected $permissionResolver; - - /** @var \eZ\Publish\API\Repository\UserService */ - protected $userService; - - public function __construct( - UserService $userService, - PermissionResolver $permissionResolver - ) { - $this->permissionResolver = $permissionResolver; - $this->userService = $userService; - } - - public function refreshUser(CoreUserInterface $user) - { - if (!$user instanceof UserInterface) { - throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user))); - } - - try { - $refreshedAPIUser = $this->userService->loadUser( - $user instanceof ReferenceUserInterface - ? $user->getAPIUserReference()->getUserId() - : $user->getAPIUser()->id - ); - $user->setAPIUser($refreshedAPIUser); - $this->permissionResolver->setCurrentUserReference( - new UserReference($refreshedAPIUser->getUserId()) - ); - - return $user; - } catch (NotFoundException $e) { - throw new UsernameNotFoundException($e->getMessage(), 0, $e); - } - } - - /** - * Whether this provider supports the given user class. - * - * @param string $class - * - * @return bool - */ - public function supportsClass($class) - { - return $class === UserInterface::class || is_subclass_of($class, UserInterface::class); - } - - /** - * Loads a regular user object, usable by Symfony Security component, from a user object returned by Public API. - * - * @param \eZ\Publish\API\Repository\Values\User\User $apiUser - * - * @return \eZ\Publish\Core\MVC\Symfony\Security\User - */ - public function loadUserByAPIUser(APIUser $apiUser) - { - return $this->createSecurityUser($apiUser); - } - - /** - * Creates user object, usable by Symfony Security component, from a user object returned by Public API. - * - * @param \eZ\Publish\API\Repository\Values\User\User $apiUser - * - * @return \eZ\Publish\Core\MVC\Symfony\Security\User - */ - protected function createSecurityUser(APIUser $apiUser): User - { - return new User($apiUser, ['ROLE_USER']); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/User/EmailProvider.php b/eZ/Publish/Core/MVC/Symfony/Security/User/EmailProvider.php deleted file mode 100644 index bfedcb6604..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Security/User/EmailProvider.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Security\User; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\Core\MVC\Symfony\Security\UserInterface; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; - -final class EmailProvider extends BaseProvider -{ - public function loadUserByUsername($user) - { - try { - // SecurityContext always tries to authenticate anonymous users when checking granted access. - // In that case $user is an instance of \eZ\Publish\Core\MVC\Symfony\Security\User. - // We don't need to reload the user here. - if ($user instanceof UserInterface) { - return $user; - } - - return $this->createSecurityUser( - $this->userService->loadUserByEmail($user) - ); - } catch (NotFoundException $e) { - throw new UsernameNotFoundException($e->getMessage(), 0, $e); - } - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/User/UsernameProvider.php b/eZ/Publish/Core/MVC/Symfony/Security/User/UsernameProvider.php deleted file mode 100644 index 4136988299..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Security/User/UsernameProvider.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Security\User; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\Core\MVC\Symfony\Security\UserInterface; -use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; - -final class UsernameProvider extends BaseProvider -{ - public function loadUserByUsername($user) - { - try { - // SecurityContext always tries to authenticate anonymous users when checking granted access. - // In that case $user is an instance of \eZ\Publish\Core\MVC\Symfony\Security\User. - // We don't need to reload the user here. - if ($user instanceof UserInterface) { - return $user; - } - - return $this->createSecurityUser( - $this->userService->loadUserByLogin($user) - ); - } catch (NotFoundException $e) { - throw new UsernameNotFoundException($e->getMessage(), 0, $e); - } - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/UserInterface.php b/eZ/Publish/Core/MVC/Symfony/Security/UserInterface.php deleted file mode 100644 index 0427ba89c9..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Security/UserInterface.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Security; - -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use Symfony\Component\Security\Core\User\UserInterface as BaseUserInterface; - -/** - * Interface for Repository based users. - */ -interface UserInterface extends BaseUserInterface -{ - /** - * @return \eZ\Publish\API\Repository\Values\User\User - */ - public function getAPIUser(); - - /** - * @deprecated Will be replaced by {@link ReferenceUserInterface::getAPIUser()}, adding LogicException to signature. - * - * @param \eZ\Publish\API\Repository\Values\User\User $apiUser - */ - public function setAPIUser(APIUser $apiUser); -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher.php deleted file mode 100644 index d0b613bd06..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess; - -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; - -/** - * Interface for SiteAccess matchers. - */ -interface Matcher -{ - /** - * Injects the request object to match against. - * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request - */ - public function setRequest(SimplifiedRequest $request); - - /** - * Returns matched Siteaccess or false if no siteaccess could be matched. - * - * @return string|false - */ - public function match(); - - /** - * Returns the matcher's name. - * This information will be stored in the SiteAccess object itself to quickly be able to identify the matcher type. - * - * @return string - */ - public function getName(); -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Compound/LogicalAnd.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Compound/LogicalAnd.php deleted file mode 100644 index 656f4443ff..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Compound/LogicalAnd.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\VersatileMatcher; - -/** - * Siteaccess matcher that allows a combination of matchers, with a logical AND. - */ -class LogicalAnd extends Compound implements VersatileMatcher -{ - public const NAME = 'logicalAnd'; - - public function match() - { - foreach ($this->config as $i => $rule) { - foreach ($rule['matchers'] as $subMatcherClass => $matchingConfig) { - // If at least one sub matcher doesn't match, jump to the next rule set. - if ($this->matchersMap[$i][$subMatcherClass]->match() === false) { - continue 2; - } - } - - $this->subMatchers = $this->matchersMap[$i]; - - return $rule['match']; - } - - return false; - } - - public function reverseMatch($siteAccessName) - { - foreach ($this->config as $i => $rule) { - if ($rule['match'] === $siteAccessName) { - $subMatchers = []; - foreach ($this->matchersMap[$i] as $subMatcher) { - if (!$subMatcher instanceof VersatileMatcher) { - return null; - } - - $reverseMatcher = $subMatcher->reverseMatch($siteAccessName); - if (!$reverseMatcher) { - return null; - } - - $subMatchers[] = $subMatcher; - } - - $this->setSubMatchers($subMatchers); - - return $this; - } - } - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Compound/LogicalOr.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Compound/LogicalOr.php deleted file mode 100644 index e0e1c4e8c0..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Compound/LogicalOr.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\VersatileMatcher; - -/** - * Siteaccess matcher that allows a combination of matchers, with a logical OR. - */ -class LogicalOr extends Compound -{ - public const NAME = 'logicalOr'; - - public function match() - { - foreach ($this->config as $i => $rule) { - foreach ($rule['matchers'] as $subMatcherClass => $matchingConfig) { - if ($this->matchersMap[$i][$subMatcherClass]->match()) { - $this->subMatchers = $this->matchersMap[$i]; - - return $rule['match']; - } - } - } - - return false; - } - - public function reverseMatch($siteAccessName) - { - foreach ($this->config as $i => $rule) { - if ($rule['match'] === $siteAccessName) { - foreach ($this->matchersMap[$i] as $subMatcher) { - if (!$subMatcher instanceof VersatileMatcher) { - continue; - } - - $reverseMatcher = $subMatcher->reverseMatch($siteAccessName); - if (!$reverseMatcher) { - continue; - } - - $this->setSubMatchers([$subMatcher]); - - return $this; - } - } - } - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/CompoundInterface.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/CompoundInterface.php deleted file mode 100644 index 2ed74f888b..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/CompoundInterface.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\VersatileMatcher; - -interface CompoundInterface extends VersatileMatcher -{ - /** - * Injects the matcher builder, to allow the Compound matcher to properly build the underlying matchers. - * - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface $matcherBuilder - */ - public function setMatcherBuilder(MatcherBuilderInterface $matcherBuilder); - - /** - * Returns all used sub-matchers. - * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher[] - */ - public function getSubMatchers(); - - /** - * Replaces sub-matchers. - * - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher[] $subMatchers - */ - public function setSubMatchers(array $subMatchers); -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Map/Host.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Map/Host.php deleted file mode 100644 index 82f8e4c092..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Map/Host.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map; - -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map; - -class Host extends Map -{ - public function getName() - { - return 'host:map'; - } - - /** - * Injects the request object to match against. - * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request - */ - public function setRequest(SimplifiedRequest $request) - { - if (!$this->key) { - $this->setMapKey($request->host); - } - - parent::setRequest($request); - } - - public function reverseMatch($siteAccessName) - { - $matcher = parent::reverseMatch($siteAccessName); - if ($matcher instanceof self) { - $matcher->getRequest()->setHost($matcher->getMapKey()); - } - - return $matcher; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Map/URI.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Map/URI.php deleted file mode 100644 index beafc761c1..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Map/URI.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map; - -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\URILexer; - -class URI extends Map implements URILexer -{ - /** - * Injects the request object to match against. - * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request - */ - public function setRequest(SimplifiedRequest $request) - { - if (!$this->key) { - sscanf($request->pathinfo, '/%[^/]', $key); - $this->setMapKey(rawurldecode((string)$key)); - } - - parent::setRequest($request); - } - - public function getName() - { - return 'uri:map'; - } - - /** - * Fixes up $uri to remove the siteaccess part, if needed. - * - * @param string $uri The original URI - * - * @return string - */ - public function analyseURI($uri) - { - if (($siteaccessPart = "/$this->key") === $uri) { - return '/'; - } - - if (mb_strpos($uri, $siteaccessPart) === 0) { - return mb_substr($uri, mb_strlen($siteaccessPart)); - } - - return $uri; - } - - /** - * Analyses $linkUri when generating a link to a route, in order to have the siteaccess part back in the URI. - * - * @param string $linkUri - * - * @return string The modified link URI - */ - public function analyseLink($linkUri) - { - // Joining slash between uriElements and actual linkUri must be present, except if $linkUri is empty or is just the slash root. - $joiningSlash = empty($linkUri) || ($linkUri === '/') ? '' : '/'; - $linkUri = ltrim($linkUri, '/'); - // Removing query string to analyse as SiteAccess might be in it. - $qsPos = strpos($linkUri, '?'); - $queryString = ''; - if ($qsPos !== false) { - $queryString = substr($linkUri, $qsPos); - $linkUri = substr($linkUri, 0, $qsPos); - } - - return "/{$this->key}{$joiningSlash}{$linkUri}{$queryString}"; - } - - public function reverseMatch($siteAccessName) - { - $matcher = parent::reverseMatch($siteAccessName); - if ($matcher instanceof self) { - $request = $matcher->getRequest(); - // Clean up "old" siteaccess prefix and add the new prefix. - $request->setPathinfo($this->analyseLink($request->pathinfo)); - } - - return $matcher; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Regex/Host.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Regex/Host.php deleted file mode 100644 index 31fb444e62..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Regex/Host.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex; - -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex; - -/** - * @deprecated since 5.3 as it cannot be reverted. - */ -class Host extends Regex implements Matcher -{ - /** - * The property needed to allow correct deserialization with Symfony serializer. - * - * @var array - */ - private $siteAccessesConfiguration; - - /** - * Constructor. - * - * @param array $siteAccessesConfiguration SiteAccesses configuration. - */ - public function __construct(array $siteAccessesConfiguration) - { - parent::__construct( - isset($siteAccessesConfiguration['regex']) ? $siteAccessesConfiguration['regex'] : '', - isset($siteAccessesConfiguration['itemNumber']) ? (int)$siteAccessesConfiguration['itemNumber'] : 1 - ); - $this->siteAccessesConfiguration = $siteAccessesConfiguration; - } - - public function getName() - { - return 'host:regexp'; - } - - /** - * Injects the request object to match against. - * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request - */ - public function setRequest(SimplifiedRequest $request) - { - if (!$this->element) { - $this->setMatchElement($request->host); - } - - parent::setRequest($request); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Regex/URI.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Regex/URI.php deleted file mode 100644 index 1176250a5b..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Regex/URI.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex; - -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex; - -/** - * @deprecated since 5.3 as it cannot be reverted. - */ -class URI extends Regex implements Matcher -{ - /** - * The property needed to allow correct deserialization with Symfony serializer. - * - * @var array - */ - private $siteAccessesConfiguration; - - /** - * Constructor. - * - * @param array $siteAccessesConfiguration SiteAccesses configuration. - */ - public function __construct(array $siteAccessesConfiguration) - { - parent::__construct( - isset($siteAccessesConfiguration['regex']) ? $siteAccessesConfiguration['regex'] : '', - isset($siteAccessesConfiguration['itemNumber']) ? (int)$siteAccessesConfiguration['itemNumber'] : 1 - ); - $this->siteAccessesConfiguration = $siteAccessesConfiguration; - } - - public function getName() - { - return 'uri:regexp'; - } - - /** - * Injects the request object to match against. - * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request - */ - public function setRequest(SimplifiedRequest $request) - { - if (!$this->element) { - $this->setMatchElement($request->pathinfo); - } - - parent::setRequest($request); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/MatcherBuilder.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/MatcherBuilder.php deleted file mode 100644 index 23843809c4..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/MatcherBuilder.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess; - -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; - -/** - * Siteaccess matcher builder, based on class names. - */ -class MatcherBuilder implements MatcherBuilderInterface -{ - /** - * Builds siteaccess matcher. - * In the siteaccess configuration, if the matcher class begins with a "\" (FQ class name), it will be used as is, passing the matching configuration in the constructor. - * Otherwise, given matching class will be relative to eZ\Publish\Core\MVC\Symfony\SiteAccess namespace. - * - * @param string $matcherIdentifier "Identifier" of the matcher to build (i.e. its FQ class name). - * @param mixed $matchingConfiguration Configuration to pass to the matcher. Can be anything the matcher supports. - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request The request to match against. - * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher - */ - public function buildMatcher($matcherIdentifier, $matchingConfiguration, SimplifiedRequest $request) - { - // If class begins with a '\' it means it's a FQ class name, - // otherwise it is relative to this namespace. - if ($matcherIdentifier[0] !== '\\') { - $matcherIdentifier = __NAMESPACE__ . "\\Matcher\\$matcherIdentifier"; - } - - /** @var $matcher \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher */ - $matcher = new $matcherIdentifier($matchingConfiguration); - $matcher->setRequest($request); - - return $matcher; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/MatcherBuilderInterface.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/MatcherBuilderInterface.php deleted file mode 100644 index 709094cd24..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/MatcherBuilderInterface.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess; - -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; - -interface MatcherBuilderInterface -{ - /** - * Builds siteaccess matcher. - * - * @param string $matcherIdentifier "Identifier" of the matcher to build (i.e. its FQ class name). - * @param mixed $matchingConfiguration Configuration to pass to the matcher. Can be anything the matcher supports. - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request The request to match against. - * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher - * - * @throws \RuntimeException - */ - public function buildMatcher($matcherIdentifier, $matchingConfiguration, SimplifiedRequest $request); -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Provider/ChainSiteAccessProvider.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/Provider/ChainSiteAccessProvider.php deleted file mode 100644 index dc355a2194..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Provider/ChainSiteAccessProvider.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Provider; - -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface; -use Traversable; - -final class ChainSiteAccessProvider implements SiteAccessProviderInterface -{ - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface[] */ - private $providers; - - /** - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface[] $providers - */ - public function __construct(iterable $providers = []) - { - $this->providers = $providers; - } - - public function getSiteAccesses(): Traversable - { - foreach ($this->providers as $provider) { - foreach ($provider->getSiteAccesses() as $siteAccess) { - yield $siteAccess; - } - } - - yield from []; - } - - public function isDefined(string $name): bool - { - foreach ($this->providers as $provider) { - if ($provider->isDefined($name)) { - return true; - } - } - - return false; - } - - /** - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - */ - public function getSiteAccess(string $name): SiteAccess - { - foreach ($this->providers as $provider) { - if ($provider->isDefined($name)) { - return $provider->getSiteAccess($name); - } - } - - throw new NotFoundException('Site Access', $name); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessAware.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessAware.php deleted file mode 100644 index aa3f901798..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessAware.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess; - -/** - * Interface for SiteAccess aware services. - */ -interface SiteAccessAware -{ - public function setSiteAccess(SiteAccess $siteAccess = null); -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessProviderInterface.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessProviderInterface.php deleted file mode 100644 index d00827bffe..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessProviderInterface.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use Traversable; - -/** - * @internal - */ -interface SiteAccessProviderInterface -{ - public function isDefined(string $name): bool; - - public function getSiteAccess(string $name): SiteAccess; - - public function getSiteAccesses(): Traversable; -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessRouterInterface.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessRouterInterface.php deleted file mode 100644 index d7a110b38c..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessRouterInterface.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess; - -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; - -interface SiteAccessRouterInterface -{ - /** - * Performs SiteAccess matching given the $request. - * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request - * - * @throws \eZ\Publish\Core\MVC\Exception\InvalidSiteAccessException - * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess - */ - public function match(SimplifiedRequest $request); - - /** - * Matches a SiteAccess by name. - * Returns corresponding SiteAccess object, according to configuration, with corresponding matcher. - * If no matcher can be found (e.g. non versatile), matcher property will be "default". - * - * @param string $siteAccessName - * - * @throws \InvalidArgumentException If $siteAccessName is invalid (i.e. not present in configured list). - * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess - */ - public function matchByName($siteAccessName); -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessService.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessService.php deleted file mode 100644 index 4d468414d0..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessService.php +++ /dev/null @@ -1,94 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess; - -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use function iterator_to_array; - -class SiteAccessService implements SiteAccessServiceInterface, SiteAccessAware -{ - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface */ - private $provider; - - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ - private $siteAccess; - - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ - private $configResolver; - - public function __construct( - SiteAccessProviderInterface $provider, - ConfigResolverInterface $configResolver - ) { - $this->provider = $provider; - $this->configResolver = $configResolver; - } - - public function setSiteAccess(SiteAccess $siteAccess = null): void - { - $this->siteAccess = $siteAccess; - } - - public function exists(string $name): bool - { - return $this->provider->isDefined($name); - } - - public function get(string $name): SiteAccess - { - if ($this->provider->isDefined($name)) { - return $this->provider->getSiteAccess($name); - } - - throw new NotFoundException('SiteAccess', $name); - } - - public function getAll(): iterable - { - return $this->provider->getSiteAccesses(); - } - - public function getCurrent(): ?SiteAccess - { - return $this->siteAccess ?? null; - } - - public function getSiteAccessesRelation(?SiteAccess $siteAccess = null): array - { - $siteAccess = $siteAccess ?? $this->siteAccess; - $saRelationMap = []; - - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess[] $saList */ - $saList = iterator_to_array($this->provider->getSiteAccesses()); - // First build the SiteAccess relation map, indexed by repository and rootLocationId. - foreach ($saList as $sa) { - $siteAccessName = $sa->name; - - $repository = $this->configResolver->getParameter('repository', 'ezsettings', $siteAccessName); - if (!isset($saRelationMap[$repository])) { - $saRelationMap[$repository] = []; - } - - $rootLocationId = $this->configResolver->getParameter('content.tree_root.location_id', 'ezsettings', $siteAccessName); - if (!isset($saRelationMap[$repository][$rootLocationId])) { - $saRelationMap[$repository][$rootLocationId] = []; - } - - $saRelationMap[$repository][$rootLocationId][] = $siteAccessName; - } - - $siteAccessName = $siteAccess->name; - $repository = $this->configResolver->getParameter('repository', 'ezsettings', $siteAccessName); - $rootLocationId = $this->configResolver->getParameter('content.tree_root.location_id', 'ezsettings', $siteAccessName); - - return $saRelationMap[$repository][$rootLocationId]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessServiceInterface.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessServiceInterface.php deleted file mode 100644 index 3210dafec8..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/SiteAccessServiceInterface.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess; - -use eZ\Publish\Core\MVC\Symfony\SiteAccess; - -/** - * Provides methods for accessing Site Access information. - */ -interface SiteAccessServiceInterface -{ - public function exists(string $name): bool; - - /** - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - */ - public function get(string $name): SiteAccess; - - /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess[] - */ - public function getAll(): iterable; - - public function getCurrent(): ?SiteAccess; - - /** - * Handles relation between SiteAccesses. Related SiteAccesses share the same repository and root location id. - * - * @return string[] - */ - public function getSiteAccessesRelation(?SiteAccess $siteAccess = null): array; -} diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/SiteAccessServiceTest.php b/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/SiteAccessServiceTest.php deleted file mode 100644 index a2378cacc1..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/SiteAccessServiceTest.php +++ /dev/null @@ -1,163 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; - -use ArrayIterator; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Provider\StaticSiteAccessProvider; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessService; -use PHPUnit\Framework\TestCase; - -class SiteAccessServiceTest extends TestCase -{ - private const EXISTING_SA_NAME = 'existing_sa'; - private const UNDEFINED_SA_NAME = 'undefined_sa'; - private const SA_GROUP = 'group'; - - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $provider; - - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $configResolver; - - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ - private $siteAccess; - - /** @var \ArrayIterator */ - private $availableSiteAccesses; - - /** @var array */ - private $configResolverParameters; - - protected function setUp(): void - { - parent::setUp(); - $this->provider = $this->createMock(SiteAccessProviderInterface::class); - $this->configResolver = $this->createMock(ConfigResolverInterface::class); - $this->siteAccess = new SiteAccess('current'); - $this->availableSiteAccesses = $this->getAvailableSitAccesses(['current', 'first_sa', 'second_sa', 'default']); - $this->configResolverParameters = $this->getConfigResolverParameters(); - } - - public function testGetCurrentSiteAccess(): void - { - $service = new SiteAccessService( - $this->createMock(SiteAccessProviderInterface::class), - $this->createMock(ConfigResolverInterface::class) - ); - - self::assertNull($service->getCurrent()); - - $siteAccess = new SiteAccess('default'); - $service->setSiteAccess($siteAccess); - self::assertSame($siteAccess, $service->getCurrent()); - - $service->setSiteAccess(null); - self::assertNull($service->getCurrent()); - } - - public function testGetSiteAccess(): void - { - $staticSiteAccessProvider = new StaticSiteAccessProvider( - [self::EXISTING_SA_NAME], - [self::EXISTING_SA_NAME => [self::SA_GROUP]], - ); - $service = new SiteAccessService( - $staticSiteAccessProvider, - $this->createMock(ConfigResolverInterface::class) - ); - - self::assertEquals( - self::EXISTING_SA_NAME, - $service->get(self::EXISTING_SA_NAME)->name - ); - } - - public function testGetSiteAccessThrowsNotFoundException(): void - { - $staticSiteAccessProvider = new StaticSiteAccessProvider( - [self::EXISTING_SA_NAME], - [self::EXISTING_SA_NAME => [self::SA_GROUP]], - ); - $service = new SiteAccessService( - $staticSiteAccessProvider, - $this->createMock(ConfigResolverInterface::class) - ); - - $this->expectException(NotFoundException::class); - $service->get(self::UNDEFINED_SA_NAME); - } - - public function testGetCurrentSiteAccessesRelation(): void - { - $this->configResolver - ->method('getParameter') - ->willReturnMap($this->configResolverParameters); - - $this->provider - ->method('getSiteAccesses') - ->willReturn($this->availableSiteAccesses); - - $this->assertSame(['current', 'first_sa'], $this->getSiteAccessService()->getSiteAccessesRelation()); - } - - public function testGetFirstSiteAccessesRelation(): void - { - $this->configResolver - ->method('getParameter') - ->willReturnMap($this->configResolverParameters); - - $this->provider - ->method('getSiteAccesses') - ->willReturn($this->availableSiteAccesses); - - $this->assertSame( - ['current', 'first_sa'], - $this->getSiteAccessService()->getSiteAccessesRelation(new SiteAccess('first_sa')) - ); - } - - private function getSiteAccessService(): SiteAccessService - { - $siteAccessService = new SiteAccessService($this->provider, $this->configResolver); - $siteAccessService->setSiteAccess($this->siteAccess); - - return $siteAccessService; - } - - /** - * @param string[] $siteAccessNames - */ - private function getAvailableSitAccesses(array $siteAccessNames): ArrayIterator - { - $availableSitAccesses = []; - foreach ($siteAccessNames as $siteAccessName) { - $availableSitAccesses[] = new SiteAccess($siteAccessName); - } - - return new ArrayIterator($availableSitAccesses); - } - - private function getConfigResolverParameters(): array - { - return [ - ['repository', 'ezsettings', 'current', 'repository_1'], - ['content.tree_root.location_id', 'ezsettings', 'current', 1], - ['repository', 'ezsettings', 'first_sa', 'repository_1'], - ['content.tree_root.location_id', 'ezsettings', 'first_sa', 1], - ['repository', 'ezsettings', 'second_sa', 'repository_1'], - ['content.tree_root.location_id', 'ezsettings', 'second_sa', 2], - ['repository', 'ezsettings', 'default', ''], - ['content.tree_root.location_id', 'ezsettings', 'default', 3], - ]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Exception/InvalidResponseException.php b/eZ/Publish/Core/MVC/Symfony/Templating/Exception/InvalidResponseException.php deleted file mode 100644 index d0a95d6eac..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Exception/InvalidResponseException.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Templating\Exception; - -use eZ\Publish\Core\Base\Exceptions\ForbiddenException; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; - -class InvalidResponseException extends ForbiddenException implements Translatable -{ - use TranslatableBase; - - public function __construct(string $whatIsWrong) - { - parent::__construct( - 'Response is invalid: %whatIsWrong%', - [ - '%whatIsWrong%' => $whatIsWrong, - ] - ); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Exception/MissingFieldBlockException.php b/eZ/Publish/Core/MVC/Symfony/Templating/Exception/MissingFieldBlockException.php deleted file mode 100644 index 3c315ad03c..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Exception/MissingFieldBlockException.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Templating\Exception; - -use RuntimeException; - -class MissingFieldBlockException extends RuntimeException -{ -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/FieldBlockRendererInterface.php b/eZ/Publish/Core/MVC/Symfony/Templating/FieldBlockRendererInterface.php deleted file mode 100644 index e6c410055c..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/FieldBlockRendererInterface.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Templating; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; - -/** - * Interface for content fields/fieldDefinitions renderers. - * Implementors can render view and edit views for fields/fieldDefinitions. - */ -interface FieldBlockRendererInterface -{ - /** - * Renders the HTML view markup for a given field. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - * @param string $fieldTypeIdentifier FieldType identifier for $field - * @param array $params An array of parameters to pass to the field view - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException If no field with provided $fieldIdentifier can be found in $content. - * - * @return string - */ - public function renderContentFieldView(Field $field, $fieldTypeIdentifier, array $params = []); - - /** - * Renders the HTML edit markup for a given field. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - * @param string $fieldTypeIdentifier FieldType identifier for $field - * @param array $params An array of parameters to pass to the field edit view - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException If no field with provided $fieldIdentifier can be found in $content. - * - * @return string - */ - public function renderContentFieldEdit(Field $field, $fieldTypeIdentifier, array $params = []); - - /** - * Renders the HTML view markup for the given field definition. - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition - * - * @return string - */ - public function renderFieldDefinitionView(FieldDefinition $fieldDefinition, array $params = []); - - /** - * Renders the HTML edot markup for the given field definition. - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition - * - * @return string - */ - public function renderFieldDefinitionEdit(FieldDefinition $fieldDefinition, array $params = []); -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/RenderContentStrategy.php b/eZ/Publish/Core/MVC/Symfony/Templating/RenderContentStrategy.php deleted file mode 100644 index 8ff3a74cb0..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/RenderContentStrategy.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Templating; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\SPI\MVC\Templating\BaseRenderStrategy; -use eZ\Publish\SPI\MVC\Templating\RenderStrategy; -use Symfony\Component\HttpKernel\Controller\ControllerReference; - -final class RenderContentStrategy extends BaseRenderStrategy implements RenderStrategy -{ - private const DEFAULT_VIEW_TYPE = 'embed'; - - public function supports(ValueObject $valueObject): bool - { - return $valueObject instanceof Content; - } - - public function render(ValueObject $valueObject, RenderOptions $options): string - { - if (!$this->supports($valueObject)) { - throw new InvalidArgumentException( - 'valueObject', - 'Must be a type of ' . Location::class - ); - } - - /** @var \eZ\Publish\API\Repository\Values\Content\Content $content */ - $content = $valueObject; - - $currentRequest = $this->requestStack->getCurrentRequest(); - $controllerReference = new ControllerReference('ez_content::viewAction', [ - 'contentId' => $content->id, - 'viewType' => $options->get('viewType', self::DEFAULT_VIEW_TYPE), - ]); - - $renderer = $this->getFragmentRenderer($options->get('method', $this->defaultRenderer)); - - return $renderer->render($controllerReference, $currentRequest)->getContent(); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/RenderLocationStrategy.php b/eZ/Publish/Core/MVC/Symfony/Templating/RenderLocationStrategy.php deleted file mode 100644 index 79b2e307bc..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/RenderLocationStrategy.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Templating; - -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\SPI\MVC\Templating\BaseRenderStrategy; -use eZ\Publish\SPI\MVC\Templating\RenderStrategy; -use Symfony\Component\HttpKernel\Controller\ControllerReference; - -final class RenderLocationStrategy extends BaseRenderStrategy implements RenderStrategy -{ - private const DEFAULT_VIEW_TYPE = 'embed'; - - public function supports(ValueObject $valueObject): bool - { - return $valueObject instanceof Location; - } - - public function render(ValueObject $valueObject, RenderOptions $options): string - { - if (!$this->supports($valueObject)) { - throw new InvalidArgumentException( - 'valueObject', - 'Must be a type of ' . Location::class - ); - } - - /** @var \eZ\Publish\API\Repository\Values\Content\Location $location */ - $location = $valueObject; - $content = $location->getContent(); - - $currentRequest = $this->requestStack->getCurrentRequest(); - $controllerReference = new ControllerReference('ez_content::viewAction', [ - 'contentId' => $content->id, - 'locationId' => $location->id, - 'viewType' => $options->get('viewType', self::DEFAULT_VIEW_TYPE), - ]); - - $renderer = $this->getFragmentRenderer($options->get('method', $this->defaultRenderer)); - - return $renderer->render($controllerReference, $currentRequest)->getContent(); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/RenderStrategy.php b/eZ/Publish/Core/MVC/Symfony/Templating/RenderStrategy.php deleted file mode 100644 index d35f5c7bbf..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/RenderStrategy.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Templating; - -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\SPI\MVC\Templating\RenderStrategy as SPIRenderStrategy; - -final class RenderStrategy implements SPIRenderStrategy -{ - /** @var \eZ\Publish\SPI\MVC\Templating\RenderStrategy[] */ - private $strategies; - - public function __construct(iterable $strategies) - { - $this->strategies = $strategies; - } - - public function supports(ValueObject $valueObject): bool - { - foreach ($this->strategies as $strategy) { - if ($strategy->supports($valueObject)) { - return true; - } - } - - return false; - } - - public function render(ValueObject $valueObject, RenderOptions $options): string - { - foreach ($this->strategies as $strategy) { - if ($strategy->supports($valueObject)) { - return $strategy->render($valueObject, $options); - } - } - - throw new InvalidArgumentException('valueObject', sprintf( - "Method '%s' is not supported for %s.", - $options->get('method'), - get_class($valueObject) - )); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/DataAttributesExtensionTest.php b/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/DataAttributesExtensionTest.php deleted file mode 100644 index 5a9841c420..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/DataAttributesExtensionTest.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension; - -use eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\DataAttributesExtension; -use Twig\Test\IntegrationTestCase; - -class DataAttributesExtensionTest extends IntegrationTestCase -{ - public function getExtensions(): array - { - return [ - new DataAttributesExtension(), - ]; - } - - protected function getFixturesDir(): string - { - return __DIR__ . '/_fixtures/filters'; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/RoutingExtensionTest.php b/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/RoutingExtensionTest.php deleted file mode 100644 index ef73d615ba..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/RoutingExtensionTest.php +++ /dev/null @@ -1,108 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension; - -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator\RouteReferenceGeneratorInterface; -use eZ\Publish\Core\MVC\Symfony\Routing\RouteReference; -use eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\RoutingExtension; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use stdClass; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Twig\Test\IntegrationTestCase; - -final class RoutingExtensionTest extends IntegrationTestCase -{ - protected function getExtensions(): array - { - return [ - new RoutingExtension( - $this->getRouteReferenceGenerator(), - $this->getUrlGenerator() - ), - ]; - } - - protected function getFixturesDir(): string - { - return __DIR__ . '/_fixtures/routing_functions'; - } - - protected function getExampleContent(int $id): APIContent - { - return new Content([ - 'versionInfo' => new VersionInfo([ - 'contentInfo' => $this->getExampleContentInfo($id), - ]), - ]); - } - - protected function getExampleContentInfo(int $id): ContentInfo - { - return new ContentInfo([ - 'id' => $id, - ]); - } - - protected function getExampleLocation(int $id): APILocation - { - return new Location(['id' => $id]); - } - - protected function getExampleRouteReference($name, array $parameters = []): RouteReference - { - return new RouteReference($name, $parameters); - } - - protected function getExampleUnsupportedObject(): object - { - $object = new stdClass(); - $object->foo = 'foo'; - $object->bar = 'bar'; - - return $object; - } - - private function getRouteReferenceGenerator(): RouteReferenceGeneratorInterface - { - $generator = new RouteReferenceGenerator( - $this->createMock(EventDispatcherInterface::class) - ); - $request = new Request(); - $requestStack = new RequestStack(); - $requestStack->push($request); - $generator->setRequestStack($requestStack); - - return $generator; - } - - private function getUrlGenerator(): UrlGeneratorInterface - { - $generator = $this->createMock(UrlGeneratorInterface::class); - $generator - ->method('generate') - ->willReturnCallback(static function ($name, $parameters, $referenceType): string { - return json_encode([ - '$name' => $name, - '$parameters' => $parameters, - '$referenceType' => $referenceType, - ]); - }); - - return $generator; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/functions/ez_file_size/ez_file_size.test b/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/functions/ez_file_size/ez_file_size.test deleted file mode 100644 index b844d3d02a..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/functions/ez_file_size/ez_file_size.test +++ /dev/null @@ -1,65 +0,0 @@ ---TEST-- -"twig" filter ---TEMPLATE-- -{{ 10|ez_file_size( 2 ) }} -{{ 1024|ez_file_size( 0 ) }} -{{ 5120|ez_file_size( 3 ) }} -{{ 12288|ez_file_size( 1 ) }} -{{ 155648|ez_file_size( 0 ) }} -{{ 27421583|ez_file_size( 5 ) }} -{{ 129103927|ez_file_size( 4 ) }} -{{ 490163142656|ez_file_size( 1 ) }} -{{ 868383057603765|ez_file_size( 6 ) }} -{{ 889224250749591400|ez_file_size( 10 ) }} -{{ 910565875123441600000|ez_file_size( 2 ) }} -{{ 910565632767581700000000000|ez_file_size( 4 ) }} ---DATA-- -$this->setConfigurationLocale( array( 'wrong local' ), 'eng-GB' ); -return array() ---EXPECT-- -10 B wrong local so we take the default one which is en-GB here -1 kB wrong local so we take the default one which is en-GB here -5 kB wrong local so we take the default one which is en-GB here -12 kB wrong local so we take the default one which is en-GB here -152 kB wrong local so we take the default one which is en-GB here -26.15126 MB wrong local so we take the default one which is en-GB here -123.1231 MB wrong local so we take the default one which is en-GB here -456.5 GB wrong local so we take the default one which is en-GB here -789.78979 TB wrong local so we take the default one which is en-GB here -789.7897897898 PB wrong local so we take the default one which is en-GB here -789.79 EB wrong local so we take the default one which is en-GB here -789789789.7898 EB wrong local so we take the default one which is en-GB here ---DATA-- -return array() ---CONFIG-- -$this->locale = "fre-FR"; return array(); ---EXPECT-- -10 B French version -1 kB French version -5 kB French version -12 kB French version -152 kB French version -26,15126 MB French version -123,1231 MB French version -456,5 GB French version -789,78979 TB French version -789,7897897898 PB French version -789,79 EB French version -789789789,7898 EB French version ---DATA-- -return array() ---CONFIG-- -$this->locale = "eng-GB"; return array(); ---EXPECT-- -10 B English version -1 kB English version -5 kB English version -12 kB English version -152 kB English version -26.15126 MB English version -123.1231 MB English version -456.5 GB English version -789.78979 TB English version -789.7897897898 PB English version -789.79 EB English version -789789789.7898 EB English version diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/routing_functions/ez_path.test b/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/routing_functions/ez_path.test deleted file mode 100644 index 7d2e602a33..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/routing_functions/ez_path.test +++ /dev/null @@ -1,48 +0,0 @@ ---TEST-- -"ez_path" function ---TEMPLATE-- -{{ ez_path(location) }} -{{ ez_path(location, {}, true) }} -{{ ez_path(location, {'foo': 'foo'}) }} -{{ ez_path(content) }} -{{ ez_path(content, {}, true) }} -{{ ez_path(content, {'foo': 'foo'}) }} -{{ ez_path(content_info) }} -{{ ez_path(content_info, {}, true) }} -{{ ez_path(content_info, {'foo': 'foo'}) }} -{{ ez_path(route_ref) }} -{{ ez_path(route_ref, {}, true) }} -{{ ez_path(route_ref, {'baz': 'baz'}) }} -{{ ez_path(unsupported_object) }} -{{ ez_path(unsupported_object, {}, true) }} -{{ ez_path(unsupported_object, {'baz': 'baz'}) }} ---DATA-- -return [ - 'location' => $this->getExampleLocation(54), - 'content' => $this->getExampleContent(2), - 'content_info' => $this->getExampleContentInfo(2), - 'route_ref' => $this->getExampleRouteReference( - 'example_route', - [ - 'foo' => 'foo', - 'bar' => 'bar' - ] - ), - 'unsupported_object' => $this->getExampleUnsupportedObject(), -]; ---EXPECT-- -{"$name":"ez_urlalias","$parameters":{"locationId":54},"$referenceType":1} -{"$name":"ez_urlalias","$parameters":{"locationId":54},"$referenceType":2} -{"$name":"ez_urlalias","$parameters":{"foo":"foo","locationId":54},"$referenceType":1} -{"$name":"ez_urlalias","$parameters":{"contentId":2},"$referenceType":1} -{"$name":"ez_urlalias","$parameters":{"contentId":2},"$referenceType":2} -{"$name":"ez_urlalias","$parameters":{"foo":"foo","contentId":2},"$referenceType":1} -{"$name":"ez_urlalias","$parameters":{"contentId":2},"$referenceType":1} -{"$name":"ez_urlalias","$parameters":{"contentId":2},"$referenceType":2} -{"$name":"ez_urlalias","$parameters":{"foo":"foo","contentId":2},"$referenceType":1} -{"$name":"example_route","$parameters":{"foo":"foo","bar":"bar"},"$referenceType":1} -{"$name":"example_route","$parameters":{"foo":"foo","bar":"bar"},"$referenceType":2} -{"$name":"example_route","$parameters":{"baz":"baz","foo":"foo","bar":"bar"},"$referenceType":1} -{"$name":"","$parameters":{"_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":1} -{"$name":"","$parameters":{"_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":2} -{"$name":"","$parameters":{"baz":"baz","_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":1} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/routing_functions/ez_route.test b/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/routing_functions/ez_route.test deleted file mode 100644 index 703870af5f..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/routing_functions/ez_route.test +++ /dev/null @@ -1,21 +0,0 @@ ---TEST-- -"twig" filter ---TEMPLATE-- -{% set route_ref1 = ez_route( "foo_route" ) %} -{% set route_ref2 = ez_route( "bar_route", {"some": "thing"} ) %} -{% set route_ref3 = ez_route( "route_66", {"direction": "highway to hell"} ) %} -{{ route_ref1.route }} -{{ route_ref1.get( "param", "test" ) }} -{{ route_ref2.route }} -{{ route_ref2.get( "some" ) }} -{{ route_ref3.route }} -{{ route_ref3.get( "direction", "highway to hell" ) }} ---DATA-- -return array(); ---EXPECT-- -foo_route -test -bar_route -thing -route_66 -highway to hell diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/routing_functions/ez_url.test b/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/routing_functions/ez_url.test deleted file mode 100644 index c06c9433e0..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/routing_functions/ez_url.test +++ /dev/null @@ -1,48 +0,0 @@ ---TEST-- -"ez_url" function ---TEMPLATE-- -{{ ez_url(location) }} -{{ ez_url(location, {}, true) }} -{{ ez_url(location, {'foo': 'foo'}) }} -{{ ez_url(content) }} -{{ ez_url(content, {}, true) }} -{{ ez_url(content, {'foo': 'foo'}) }} -{{ ez_url(content_info) }} -{{ ez_url(content_info, {}, true) }} -{{ ez_url(content_info, {'foo': 'foo'}) }} -{{ ez_url(route_ref) }} -{{ ez_url(route_ref, {}, true) }} -{{ ez_url(route_ref, {'baz': 'baz'}) }} -{{ ez_path(unsupported_object) }} -{{ ez_path(unsupported_object, {}, true) }} -{{ ez_path(unsupported_object, {'baz': 'baz'}) }} ---DATA-- -return [ - 'location' => $this->getExampleLocation(54), - 'content' => $this->getExampleContent(2), - 'content_info' => $this->getExampleContentInfo(2), - 'route_ref' => $this->getExampleRouteReference( - 'example_route', - [ - 'foo' => 'foo', - 'bar' => 'bar' - ] - ), - 'unsupported_object' => $this->getExampleUnsupportedObject(), -]; ---EXPECT-- -{"$name":"ez_urlalias","$parameters":{"locationId":54},"$referenceType":0} -{"$name":"ez_urlalias","$parameters":{"locationId":54},"$referenceType":3} -{"$name":"ez_urlalias","$parameters":{"foo":"foo","locationId":54},"$referenceType":0} -{"$name":"ez_urlalias","$parameters":{"contentId":2},"$referenceType":0} -{"$name":"ez_urlalias","$parameters":{"contentId":2},"$referenceType":3} -{"$name":"ez_urlalias","$parameters":{"foo":"foo","contentId":2},"$referenceType":0} -{"$name":"ez_urlalias","$parameters":{"contentId":2},"$referenceType":0} -{"$name":"ez_urlalias","$parameters":{"contentId":2},"$referenceType":3} -{"$name":"ez_urlalias","$parameters":{"foo":"foo","contentId":2},"$referenceType":0} -{"$name":"example_route","$parameters":{"foo":"foo","bar":"bar"},"$referenceType":0} -{"$name":"example_route","$parameters":{"foo":"foo","bar":"bar"},"$referenceType":3} -{"$name":"example_route","$parameters":{"baz":"baz","foo":"foo","bar":"bar"},"$referenceType":0} -{"$name":"","$parameters":{"_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":1} -{"$name":"","$parameters":{"_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":2} -{"$name":"","$parameters":{"baz":"baz","_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":1} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php b/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php deleted file mode 100644 index 8f319b5e52..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php +++ /dev/null @@ -1,255 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\Helper\FieldHelper; -use eZ\Publish\Core\Helper\TranslationHelper; -use Psr\Log\LoggerInterface; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; - -/** - * Twig content extension for eZ Publish specific usage. - * Exposes helpers to play with public API objects. - */ -class ContentExtension extends AbstractExtension -{ - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\Core\Helper\TranslationHelper */ - protected $translationHelper; - - /** @var \eZ\Publish\Core\Helper\FieldHelper */ - protected $fieldHelper; - - /** @var \Psr\Log\LoggerInterface */ - protected $logger; - - public function __construct( - Repository $repository, - TranslationHelper $translationHelper, - FieldHelper $fieldHelper, - LoggerInterface $logger = null - ) { - $this->repository = $repository; - $this->translationHelper = $translationHelper; - $this->fieldHelper = $fieldHelper; - $this->logger = $logger; - } - - /** - * Returns a list of functions to add to the existing list. - * - * @return array - */ - public function getFunctions() - { - return [ - new TwigFunction( - 'ez_content_name', - [$this, 'getTranslatedContentName'] - ), - new TwigFunction( - 'ez_field_value', - [$this, 'getTranslatedFieldValue'] - ), - new TwigFunction( - 'ez_field', - [$this, 'getTranslatedField'] - ), - new TwigFunction( - 'ez_field_is_empty', - [$this, 'isFieldEmpty'] - ), - new TwigFunction( - 'ez_field_name', - [$this, 'getTranslatedFieldDefinitionName'] - ), - new TwigFunction( - 'ez_field_description', - [$this, 'getTranslatedFieldDefinitionDescription'] - ), - new TwigFunction( - 'ez_content_field_identifier_first_filled_image', - [$this, 'getFirstFilledImageFieldIdentifier'] - ), - ]; - } - - /** - * Returns the name of the extension. - * - * @return string The extension name - */ - public function getName() - { - return 'ezpublish.content'; - } - - /** - * @param \eZ\Publish\API\Repository\Values\ValueObject $content Must be a valid Content or ContentInfo object. - * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType When $content is not a valid Content or ContentInfo object. - * - * @return string - */ - public function getTranslatedContentName(ValueObject $content, $forcedLanguage = null) - { - if ($content instanceof Content) { - return $this->translationHelper->getTranslatedContentName($content, $forcedLanguage); - } elseif ($content instanceof ContentInfo) { - return $this->translationHelper->getTranslatedContentNameByContentInfo($content, $forcedLanguage); - } - - throw new InvalidArgumentType('$content', 'eZ\Publish\API\Repository\Values\Content\Content or eZ\Publish\API\Repository\Values\Content\ContentInfo', $content); - } - - /** - * Returns the translated field, very similar to getTranslatedFieldValue but this returns the whole field. - * To be used with ez_image_alias for example, which requires the whole field. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param string $fieldDefIdentifier Identifier for the field we want to get. - * @param string $forcedLanguage Locale we want the field in (e.g. "cro-HR"). Null by default (takes current locale). - * - * @return \eZ\Publish\API\Repository\Values\Content\Field - */ - public function getTranslatedField(Content $content, $fieldDefIdentifier, $forcedLanguage = null) - { - return $this->translationHelper->getTranslatedField($content, $fieldDefIdentifier, $forcedLanguage); - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param string $fieldDefIdentifier Identifier for the field we want to get the value from. - * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale). - * - * @return mixed A primitive type or a field type Value object depending on the field type. - */ - public function getTranslatedFieldValue(Content $content, $fieldDefIdentifier, $forcedLanguage = null) - { - return $this->translationHelper->getTranslatedField($content, $fieldDefIdentifier, $forcedLanguage)->value; - } - - /** - * Gets name of a FieldDefinition name by loading ContentType based on Content/ContentInfo object. - * - * @param \eZ\Publish\API\Repository\Values\ValueObject $content Must be Content or ContentInfo object - * @param string $fieldDefIdentifier Identifier for the field we want to get the name from - * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType When $content is not a valid Content object. - * - * @return string|null - */ - public function getTranslatedFieldDefinitionName(ValueObject $content, $fieldDefIdentifier, $forcedLanguage = null) - { - if ($contentType = $this->getContentType($content)) { - return $this->translationHelper->getTranslatedFieldDefinitionProperty( - $contentType, - $fieldDefIdentifier, - 'name', - $forcedLanguage - ); - } - - throw new InvalidArgumentType('$content', 'Content|ContentInfo', $content); - } - - /** - * Gets name of a FieldDefinition description by loading ContentType based on Content/ContentInfo object. - * - * @param \eZ\Publish\API\Repository\Values\ValueObject $content Must be Content or ContentInfo object - * @param string $fieldDefIdentifier Identifier for the field we want to get the name from - * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType When $content is not a valid Content object. - * - * @return string|null - */ - public function getTranslatedFieldDefinitionDescription(ValueObject $content, $fieldDefIdentifier, $forcedLanguage = null) - { - if ($contentType = $this->getContentType($content)) { - return $this->translationHelper->getTranslatedFieldDefinitionProperty( - $contentType, - $fieldDefIdentifier, - 'description', - $forcedLanguage - ); - } - - throw new InvalidArgumentType('$content', 'Content|ContentInfo', $content); - } - - /** - * Checks if a given field is considered empty. - * This method accepts field as Objects or by identifiers. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param \eZ\Publish\API\Repository\Values\Content\Field|string $fieldDefIdentifier Field or Field Identifier to - * get the value from. - * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). - * Null by default (takes current locale). - * - * @return bool - */ - public function isFieldEmpty(Content $content, $fieldDefIdentifier, $forcedLanguage = null) - { - if ($fieldDefIdentifier instanceof Field) { - $fieldDefIdentifier = $fieldDefIdentifier->fieldDefIdentifier; - } - - return $this->fieldHelper->isFieldEmpty($content, $fieldDefIdentifier, $forcedLanguage); - } - - /** - * Get ContentType by Content/ContentInfo. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content|\eZ\Publish\API\Repository\Values\Content\ContentInfo $content - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType|null - */ - private function getContentType(ValueObject $content) - { - if ($content instanceof Content) { - return $this->repository->getContentTypeService()->loadContentType( - $content->getVersionInfo()->getContentInfo()->contentTypeId - ); - } elseif ($content instanceof ContentInfo) { - return $this->repository->getContentTypeService()->loadContentType($content->contentTypeId); - } - } - - public function getFirstFilledImageFieldIdentifier(Content $content) - { - foreach ($content->getFieldsByLanguage() as $field) { - $fieldTypeIdentifier = $content->getContentType() - ->getFieldDefinition($field->fieldDefIdentifier) - ->fieldTypeIdentifier; - - if ($fieldTypeIdentifier !== 'ezimage') { - continue; - } - - if ($this->fieldHelper->isFieldEmpty($content, $field->fieldDefIdentifier)) { - continue; - } - - return $field->fieldDefIdentifier; - } - - return null; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/CoreExtension.php b/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/CoreExtension.php deleted file mode 100644 index 4b1084c6df..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/CoreExtension.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension; - -use eZ\Publish\Core\MVC\Symfony\Templating\GlobalHelper; -use Twig\Extension\AbstractExtension; -use Twig\Extension\GlobalsInterface; - -class CoreExtension extends AbstractExtension implements GlobalsInterface -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Templating\GlobalHelper */ - private $globalHelper; - - public function __construct(GlobalHelper $globalHelper) - { - $this->globalHelper = $globalHelper; - } - - /** - * Returns the name of the extension. - * - * @return string The extension name - */ - public function getName() - { - return 'ezpublish.core'; - } - - /** - * @return array - */ - public function getGlobals(): array - { - return ['ezplatform' => $this->globalHelper]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/DataAttributesExtension.php b/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/DataAttributesExtension.php deleted file mode 100644 index d4f066a4fd..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/DataAttributesExtension.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension; - -use Twig\Extension\AbstractExtension; -use Twig\TwigFilter; - -/** - * Twig common extension for general use helpers. - */ -class DataAttributesExtension extends AbstractExtension -{ - /** - * Returns a list of functions to add to the existing list. - * - * @return array - */ - public function getFilters(): array - { - return [ - new TwigFilter( - 'ez_data_attributes_serialize', - [$this, 'serializeDataAttributes'], - ['is_safe' => ['html']] - ), - ]; - } - - /** - * Processes an associative list of data attributes and returns them as HTML attributes list - * in the form of <code>data-<attribute_name>="<attribute_value>"</code>. - * - * @param array $dataAttributes - * - * @return string - */ - public function serializeDataAttributes(array $dataAttributes): string - { - $result = ''; - foreach ($dataAttributes as $attributeName => $attributeValue) { - if (!\is_string($attributeValue)) { - $attributeValue = json_encode($attributeValue); - } - - $result .= sprintf('data-%s="%s" ', $attributeName, htmlspecialchars($attributeValue)); - } - - return rtrim($result); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php b/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php deleted file mode 100644 index 279bdbc8eb..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php +++ /dev/null @@ -1,190 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Helper\TranslationHelper; -use eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistryInterface; -use eZ\Publish\Core\MVC\Symfony\Templating\FieldBlockRendererInterface; -use Twig\Environment; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; - -/** - * Twig extension for content fields/fieldDefinitions rendering (view and edit). - */ -class FieldRenderingExtension extends AbstractExtension -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Templating\FieldBlockRendererInterface|\eZ\Publish\Core\MVC\Symfony\Templating\Twig\FieldBlockRenderer */ - private $fieldBlockRenderer; - - /** @var \eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistryInterface */ - private $parameterProviderRegistry; - - /** @var \eZ\Publish\Core\Helper\TranslationHelper */ - private $translationHelper; - - /** - * Hash of field type identifiers (i.e. "ezstring"), indexed by field definition identifier. - * - * @var array - */ - private $fieldTypeIdentifiers = []; - - public function __construct( - FieldBlockRendererInterface $fieldBlockRenderer, - ParameterProviderRegistryInterface $parameterProviderRegistry, - TranslationHelper $translationHelper - ) { - $this->fieldBlockRenderer = $fieldBlockRenderer; - $this->parameterProviderRegistry = $parameterProviderRegistry; - $this->translationHelper = $translationHelper; - } - - public function getName() - { - return 'ezpublish.field_rendering'; - } - - public function getFunctions() - { - return [ - new TwigFunction( - 'ez_render_field', - function (Environment $environment, Content $content, $fieldIdentifier, array $params = []) { - $this->fieldBlockRenderer->setTwig($environment); - - return $this->renderField($content, $fieldIdentifier, $params); - }, - ['is_safe' => ['html'], 'needs_environment' => true] - ), - new TwigFunction( - 'ez_render_field_definition_settings', - function (Environment $environment, FieldDefinition $fieldDefinition, array $params = []) { - $this->fieldBlockRenderer->setTwig($environment); - - return $this->renderFieldDefinitionSettings($fieldDefinition, $params); - }, - ['is_safe' => ['html'], 'needs_environment' => true] - ), - ]; - } - - /** - * Renders the HTML for the settings for the given field definition - * $definition. - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition - * - * @return string - */ - public function renderFieldDefinitionSettings(FieldDefinition $fieldDefinition, array $params = []) - { - return $this->fieldBlockRenderer->renderFieldDefinitionView($fieldDefinition, $params); - } - - /** - * Renders the HTML for a given field. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param string $fieldIdentifier Identifier for the field we want to render - * @param array $params An array of parameters to pass to the field view - * - * @return string The HTML markup - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - */ - public function renderField(Content $content, $fieldIdentifier, array $params = []) - { - $field = $this->translationHelper->getTranslatedField($content, $fieldIdentifier, isset($params['lang']) ? $params['lang'] : null); - if (!$field instanceof Field) { - throw new InvalidArgumentException( - '$fieldIdentifier', - "'{$fieldIdentifier}' Field does not exist in Content item {$content->contentInfo->id} '{$content->contentInfo->name}'" - ); - } - - $params = $this->getRenderFieldBlockParameters($content, $field, $params); - $fieldTypeIdentifier = $this->getFieldTypeIdentifier($content, $field); - - return $this->fieldBlockRenderer->renderContentFieldView($field, $fieldTypeIdentifier, $params); - } - - /** - * Generates the array of parameter to pass to the field template. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param \eZ\Publish\API\Repository\Values\Content\Field $field the Field to display - * @param array $params An array of parameters to pass to the field view - * - * @return array - */ - private function getRenderFieldBlockParameters(Content $content, Field $field, array $params = []) - { - // Merging passed parameters to default ones - $params += [ - 'parameters' => [], // parameters dedicated to template processing - 'attr' => [], // attributes to add on the enclosing HTML tags - ]; - - $versionInfo = $content->getVersionInfo(); - $contentInfo = $versionInfo->getContentInfo(); - $contentType = $content->getContentType(); - $fieldDefinition = $contentType->getFieldDefinition($field->fieldDefIdentifier); - // Adding Field, FieldSettings and ContentInfo objects to - // parameters to be passed to the template - $params += [ - 'field' => $field, - 'content' => $content, - 'contentInfo' => $contentInfo, - 'versionInfo' => $versionInfo, - 'fieldSettings' => $fieldDefinition->getFieldSettings(), - ]; - - // Adding field type specific parameters if any. - if ($this->parameterProviderRegistry->hasParameterProvider($fieldDefinition->fieldTypeIdentifier)) { - $params['parameters'] += $this->parameterProviderRegistry - ->getParameterProvider($fieldDefinition->fieldTypeIdentifier) - ->getViewParameters($field); - } - - // make sure we can easily add class="<fieldtypeidentifier>-field" to the - // generated HTML - if (isset($params['attr']['class'])) { - $params['attr']['class'] .= ' ' . $this->getFieldTypeIdentifier($content, $field) . '-field'; - } else { - $params['attr']['class'] = $this->getFieldTypeIdentifier($content, $field) . '-field'; - } - - return $params; - } - - /** - * Returns the field type identifier for $field. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - * - * @return string - */ - private function getFieldTypeIdentifier(Content $content, Field $field) - { - $contentType = $content->getContentType(); - $key = $contentType->id . ' ' . $field->fieldDefIdentifier; - - if (!isset($this->fieldTypeIdentifiers[$key])) { - $this->fieldTypeIdentifiers[$key] = $contentType - ->getFieldDefinition($field->fieldDefIdentifier) - ->fieldTypeIdentifier; - } - - return $this->fieldTypeIdentifiers[$key]; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php b/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php deleted file mode 100644 index 9824a85408..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php +++ /dev/null @@ -1,117 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension; - -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface; -use Locale; -use NumberFormatter; -use Symfony\Contracts\Translation\TranslatorInterface; -use Twig\Extension\AbstractExtension; -use Twig\TwigFilter; - -/** - * Class FileSizeExtension. - */ -class FileSizeExtension extends AbstractExtension -{ - /** - * @param \Symfony\Contracts\Translation\TranslatorInterface $translator - */ - protected $translator; - - /** - * @param array $suffixes - */ - protected $suffixes; - - /** - * @param \eZ\Publish\Core\MVC\ConfigResolverInterface $configResolver - */ - protected $configResolver; - - /** - * @param \eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface $localeConverter - */ - protected $localeConverter; - - /** - * @param \Symfony\Contracts\Translation\TranslatorInterface $translator - * @param \eZ\Publish\Core\MVC\ConfigResolverInterface $configResolver - * @param \eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface $localeConverter - * @param array $suffixes - */ - public function __construct(TranslatorInterface $translator, array $suffixes, ConfigResolverInterface $configResolver, LocaleConverterInterface $localeConverter) - { - $this->translator = $translator; - $this->suffixes = $suffixes; - $this->configResolver = $configResolver; - $this->localeConverter = $localeConverter; - } - - private function getLocale() - { - foreach ($this->configResolver->getParameter('languages') as $locale) { - $convertedLocale = $this->localeConverter->convertToPOSIX($locale); - if ($convertedLocale !== null) { - return $convertedLocale; - } - } - - return Locale::getDefault(); - } - - /** - * Returns a list of filters to add to the existing list. - * - * @return array An array of filters - */ - public function getFilters() - { - return [ - new TwigFilter('ez_file_size', [$this, 'sizeFilter']), - ]; - } - - /** - * Returns the binary file size, $precision will determine the decimal number precision, - * and the Locale will alter the format of the result by choosing between coma or point pattern. - * - * @param int $number - * @param int $precision - * - * @return string - */ - public function sizeFilter($number, $precision) - { - $mod = 1024; - $index = count($this->suffixes); - if ($number < ($mod ** $index)) { - for ($i = 0; $number >= $mod; ++$i) { - $number /= $mod; - } - } else { - $number /= $mod ** ($index - 1); - $i = ($index - 1); - } - $formatter = new NumberFormatter($this->getLocale(), NumberFormatter::PATTERN_DECIMAL); - $formatter->setPattern($formatter->getPattern() . ' ' . $this->translator->trans($this->suffixes[$i])); - $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $precision); - - return $formatter->format($number); - } - - /** - * Returns the name of the extension. - * - * @return string The extension name - */ - public function getName() - { - return 'ezpublish.fileSize'; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/ImageExtension.php b/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/ImageExtension.php deleted file mode 100644 index 190201b5ab..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/ImageExtension.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension; - -use eZ\Publish\API\Repository\Exceptions\InvalidVariationException; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\FieldType\ImageAsset\AssetMapper; -use eZ\Publish\Core\MVC\Exception\SourceImageNotFoundException; -use eZ\Publish\SPI\Variation\VariationHandler; -use InvalidArgumentException; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; - -class ImageExtension extends AbstractExtension -{ - /** @var \eZ\Publish\SPI\Variation\VariationHandler */ - private $imageVariationService; - - /** @var \eZ\Publish\Core\FieldType\ImageAsset\AssetMapper */ - protected $assetMapper; - - public function __construct(VariationHandler $imageVariationService, AssetMapper $assetMapper) - { - $this->imageVariationService = $imageVariationService; - $this->assetMapper = $assetMapper; - } - - public function getName() - { - return 'ezpublish.image'; - } - - public function getFunctions() - { - return [ - new TwigFunction( - 'ez_image_alias', - [$this, 'getImageVariation'], - ['is_safe' => ['html']] - ), - new TwigFunction( - 'ez_content_field_identifier_image_asset', - [$this, 'getImageAssetContentFieldIdentifier'], - ['is_safe' => ['html']] - ), - ]; - } - - /** - * Returns the image variation object for $field/$versionInfo. - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - * @param string $variationName - * - * @return \eZ\Publish\SPI\Variation\Values\Variation|null - */ - public function getImageVariation(Field $field, VersionInfo $versionInfo, $variationName) - { - try { - return $this->imageVariationService->getVariation($field, $versionInfo, $variationName); - } catch (InvalidVariationException $e) { - if (isset($this->logger)) { - $this->logger->error("Couldn't get variation '{$variationName}' for image with id {$field->value->id}"); - } - } catch (SourceImageNotFoundException $e) { - if (isset($this->logger)) { - $this->logger->error( - "Couldn't create variation '{$variationName}' for image with id {$field->value->id} because source image can't be found" - ); - } - } catch (InvalidArgumentException $e) { - if (isset($this->logger)) { - $this->logger->error( - "Couldn't create variation '{$variationName}' for image with id {$field->value->id} because an image could not be created from the given input" - ); - } - } - } - - /** - * Return identifier of the field used to store Image Asset value. - * - * Typically used to create generic view of the Image Asset field. - * - * @return string - */ - public function getImageAssetContentFieldIdentifier(): string - { - return $this->assetMapper->getContentFieldIdentifier(); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/QueryRenderingExtension.php b/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/QueryRenderingExtension.php deleted file mode 100644 index ad91f078cc..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/QueryRenderingExtension.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use Symfony\Component\HttpKernel\Controller\ControllerReference; -use Symfony\Component\HttpKernel\Fragment\FragmentHandler; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; - -class QueryRenderingExtension extends AbstractExtension -{ - private const VALID_TYPES = ['content', 'location']; - - /** @var \Symfony\Component\HttpKernel\Fragment\FragmentHandler */ - private $fragmentHandler; - - public function __construct(FragmentHandler $fragmentHandler) - { - $this->fragmentHandler = $fragmentHandler; - } - - public function getFunctions(): array - { - return [ - new TwigFunction( - 'ez_render_*_query', - function (string $type, array $options): ?string { - $this->assertTypeIsValid($type); - - return $this->fragmentHandler->render( - $this->createControllerReference($options) - ); - }, - ['is_safe' => ['html']] - ), - new TwigFunction( - 'ez_render_*_query_*', - function (string $type, string $renderer, array $options): ?string { - $this->assertTypeIsValid($type); - - return $this->fragmentHandler->render( - $this->createControllerReference($options), - $renderer - ); - }, - ['is_safe' => ['html']] - ), - ]; - } - - private function createControllerReference(array $options): ControllerReference - { - return new ControllerReference('ez_query_render::renderQuery', [ - 'options' => $options, - ]); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - private function assertTypeIsValid(string $type): void - { - if (!in_array($type, self::VALID_TYPES)) { - throw new InvalidArgumentException( - '$type', - 'Expected value to be of ' . implode(', ', self::VALID_TYPES) - ); - } - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/RenderContentExtension.php b/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/RenderContentExtension.php deleted file mode 100644 index a09de99405..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/RenderContentExtension.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\Core\MVC\Symfony\Event\ResolveRenderOptionsEvent; -use eZ\Publish\Core\MVC\Symfony\Templating\RenderContentStrategy; -use eZ\Publish\Core\MVC\Symfony\Templating\RenderOptions; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; - -/** - * @internal - */ -final class RenderContentExtension extends AbstractExtension -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Templating\RenderContentStrategy */ - private $renderContentStrategy; - - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - private $eventDispatcher; - - public function __construct( - RenderContentStrategy $renderContentStrategy, - EventDispatcherInterface $eventDispatcher - ) { - $this->renderContentStrategy = $renderContentStrategy; - $this->eventDispatcher = $eventDispatcher; - } - - public function getFunctions(): array - { - return [ - new TwigFunction( - 'ez_render_content', - [$this, 'renderContent'], - ['is_safe' => ['html']] - ), - ]; - } - - public function renderContent(Content $content, array $options = []): string - { - $renderOptions = new RenderOptions($options); - $event = $this->eventDispatcher->dispatch( - new ResolveRenderOptionsEvent($renderOptions) - ); - - return $this->renderContentStrategy->render($content, $event->getRenderOptions()); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/RenderExtension.php b/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/RenderExtension.php deleted file mode 100644 index e58dcb1859..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/RenderExtension.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension; - -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\MVC\Symfony\Event\ResolveRenderOptionsEvent; -use eZ\Publish\Core\MVC\Symfony\Templating\RenderOptions; -use eZ\Publish\SPI\MVC\Templating\RenderStrategy; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; - -/** - * @internal - */ -final class RenderExtension extends AbstractExtension -{ - /** @var \eZ\Publish\SPI\MVC\Templating\RenderStrategy */ - private $renderStrategy; - - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - private $eventDispatcher; - - public function __construct( - RenderStrategy $renderStrategy, - EventDispatcherInterface $eventDispatcher - ) { - $this->renderStrategy = $renderStrategy; - $this->eventDispatcher = $eventDispatcher; - } - - public function getFunctions(): array - { - return [ - new TwigFunction( - 'ez_render', - [$this, 'render'], - ['is_safe' => ['html']] - ), - ]; - } - - public function render(ValueObject $valueObject, array $options = []): string - { - if (!$this->renderStrategy->supports($valueObject)) { - throw new InvalidArgumentException( - 'valueObject', - sprintf('%s is not supported.', get_class($valueObject)) - ); - } - - $renderOptions = new RenderOptions($options); - $event = $this->eventDispatcher->dispatch( - new ResolveRenderOptionsEvent($renderOptions) - ); - - return $this->renderStrategy->render($valueObject, $event->getRenderOptions()); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/RenderLocationExtension.php b/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/RenderLocationExtension.php deleted file mode 100644 index f83af5069e..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/RenderLocationExtension.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension; - -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Event\ResolveRenderOptionsEvent; -use eZ\Publish\Core\MVC\Symfony\Templating\RenderLocationStrategy; -use eZ\Publish\Core\MVC\Symfony\Templating\RenderOptions; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; -use Twig\Extension\AbstractExtension; -use Twig\TwigFunction; - -/** - * @internal - */ -final class RenderLocationExtension extends AbstractExtension -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Templating\RenderLocationStrategy */ - private $renderLocationStrategy; - - /** @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface */ - private $eventDispatcher; - - public function __construct( - RenderLocationStrategy $renderLocationStrategy, - EventDispatcherInterface $eventDispatcher - ) { - $this->renderLocationStrategy = $renderLocationStrategy; - $this->eventDispatcher = $eventDispatcher; - } - - public function getFunctions(): array - { - return [ - new TwigFunction( - 'ez_render_location', - [$this, 'renderLocation'], - ['is_safe' => ['html']] - ), - ]; - } - - public function renderLocation(Location $location, array $options = []): string - { - $renderOptions = new RenderOptions($options); - $event = $this->eventDispatcher->dispatch( - new ResolveRenderOptionsEvent($renderOptions) - ); - - return $this->renderLocationStrategy->render($location, $event->getRenderOptions()); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/RoutingExtension.php b/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/RoutingExtension.php deleted file mode 100644 index 689b9469a4..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/Extension/RoutingExtension.php +++ /dev/null @@ -1,147 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator\RouteReferenceGeneratorInterface; -use eZ\Publish\Core\MVC\Symfony\Routing\RouteReference; -use eZ\Publish\Core\MVC\Symfony\Routing\UrlAliasRouter; -use Symfony\Cmf\Component\Routing\RouteObjectInterface; -use Symfony\Component\Routing\Generator\UrlGeneratorInterface; -use Twig\Extension\AbstractExtension; -use Twig\Node\Expression\ArrayExpression; -use Twig\Node\Expression\ConstantExpression; -use Twig\Node\Node; -use Twig\TwigFunction; - -class RoutingExtension extends AbstractExtension -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Routing\Generator\RouteReferenceGeneratorInterface */ - private $routeReferenceGenerator; - - /** @var \Symfony\Component\Routing\Generator\UrlGeneratorInterface */ - private $urlGenerator; - - public function __construct( - RouteReferenceGeneratorInterface $routeReferenceGenerator, - UrlGeneratorInterface $urlGenerator - ) { - $this->routeReferenceGenerator = $routeReferenceGenerator; - $this->urlGenerator = $urlGenerator; - } - - public function getFunctions(): array - { - return [ - new TwigFunction( - 'ez_route', - [$this, 'getRouteReference'] - ), - new TwigFunction( - 'ez_path', - [$this, 'getPath'], - ['is_safe_callback' => [$this, 'isUrlGenerationSafe']] - ), - new TwigFunction( - 'ez_url', - [$this, 'getUrl'], - ['is_safe_callback' => [$this, 'isUrlGenerationSafe']] - ), - ]; - } - - public function getName(): string - { - return 'ezpublish.routing'; - } - - /** - * @param mixed $resource - * @param array $params - * - * @return \eZ\Publish\Core\MVC\Symfony\Routing\RouteReference - */ - public function getRouteReference($resource = null, $params = []): RouteReference - { - return $this->routeReferenceGenerator->generate($resource, $params); - } - - public function getPath(object $name, array $parameters = [], bool $relative = false): string - { - $referenceType = $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH; - - return $this->tryGeneratingUrlForObject($name, $parameters, $referenceType); - } - - public function getUrl(object $name, array $parameters = [], bool $schemeRelative = false): string - { - $referenceType = $schemeRelative ? UrlGeneratorInterface::NETWORK_PATH : UrlGeneratorInterface::ABSOLUTE_URL; - - return $this->tryGeneratingUrlForObject($name, $parameters, $referenceType); - } - - private function tryGeneratingUrlForObject(object $object, array $parameters, int $referenceType): string - { - try { - return $this->generateUrlForObject($object, $parameters, $referenceType); - } catch (NotFoundException $e) { - return ''; - } - } - - private function generateUrlForObject(object $object, array $parameters, int $referenceType): string - { - if ($object instanceof Location) { - $routeName = UrlAliasRouter::URL_ALIAS_ROUTE_NAME; - $parameters += [ - 'locationId' => $object->id, - ]; - } elseif ($object instanceof Content || $object instanceof ContentInfo) { - $routeName = UrlAliasRouter::URL_ALIAS_ROUTE_NAME; - $parameters += [ - 'contentId' => $object->id, - ]; - } elseif ($object instanceof RouteReference) { - $routeName = $object->getRoute(); - $parameters += $object->getParams(); - } else { - $routeName = ''; - $parameters += [ - RouteObjectInterface::ROUTE_OBJECT => $object, - ]; - } - - return $this->urlGenerator->generate($routeName, $parameters, $referenceType); - } - - /** - * Determines at compile time whether the generated URL will be safe and thus - * saving the unneeded automatic escaping for performance reasons. - * - * @see \Symfony\Bridge\Twig\Extension\RoutingExtension::isUrlGenerationSafe - */ - public function isUrlGenerationSafe(Node $argsNode): array - { - // support named arguments - $paramsNode = $argsNode->hasNode('parameters') ? $argsNode->getNode('parameters') : ( - $argsNode->hasNode('1') ? $argsNode->getNode('1') : null - ); - - if (null === $paramsNode || $paramsNode instanceof ArrayExpression && \count($paramsNode) <= 2 && - (!$paramsNode->hasNode('1') || $paramsNode->getNode('1') instanceof ConstantExpression) - ) { - return ['html']; - } - - return []; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php b/eZ/Publish/Core/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php deleted file mode 100644 index 5deb92d5ef..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\Translation; - -use JMS\TranslationBundle\Translation\Extractor\File\DefaultPhpFileExtractor; - -class ExceptionMessageTemplateFileVisitor extends DefaultPhpFileExtractor -{ - protected $methodsToExtractFrom = ['setmessagetemplate' => -1]; - - protected $defaultDomain = 'repository_exceptions'; -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Builder/ParametersFilter/RequestAttributes.php b/eZ/Publish/Core/MVC/Symfony/View/Builder/ParametersFilter/RequestAttributes.php deleted file mode 100644 index 2b3d636f09..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/Builder/ParametersFilter/RequestAttributes.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View\Builder\ParametersFilter; - -use eZ\Publish\Core\MVC\Symfony\View\Event\FilterViewBuilderParametersEvent; -use eZ\Publish\Core\MVC\Symfony\View\ViewEvents; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -/** - * Collects parameters for the ViewBuilder from the Request. - */ -class RequestAttributes implements EventSubscriberInterface -{ - public static function getSubscribedEvents() - { - return [ViewEvents::FILTER_BUILDER_PARAMETERS => 'addRequestAttributes']; - } - - /** - * Adds all the request attributes to the parameters. - * - * @param \eZ\Publish\Core\MVC\Symfony\View\Event\FilterViewBuilderParametersEvent $e - */ - public function addRequestAttributes(FilterViewBuilderParametersEvent $e) - { - $parameterBag = $e->getParameters(); - $parameterBag->add($e->getRequest()->attributes->all()); - - // maybe this should be in its own listener ? The ViewBuilder needs it. - if (!$parameterBag->has('viewType')) { - $parameterBag->add(['viewType' => null]); - } - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Builder/Registry/ControllerMatch.php b/eZ/Publish/Core/MVC/Symfony/View/Builder/Registry/ControllerMatch.php deleted file mode 100644 index 02b805d648..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/Builder/Registry/ControllerMatch.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View\Builder\Registry; - -use eZ\Publish\Core\MVC\Symfony\View\Builder\ViewBuilderRegistry; - -/** - * A registry of ViewBuilders that uses the ViewBuilder's match() method to identify the builder against - * a controller string. - */ -class ControllerMatch implements ViewBuilderRegistry -{ - /** @var \eZ\Publish\Core\MVC\Symfony\View\Builder\ViewBuilder[] */ - private $registry = []; - - public function __construct(iterable $viewBuilders = []) - { - $toAdd = []; - foreach ($viewBuilders as $viewBuilder) { - $toAdd[] = $viewBuilder; - } - $this->addToRegistry($toAdd); - } - - /** - * @param \eZ\Publish\Core\MVC\Symfony\View\Builder\ViewBuilder[] $viewBuilders - */ - public function addToRegistry(array $viewBuilders) - { - $this->registry = array_merge($this->registry, $viewBuilders); - } - - /** - * Returns the ViewBuilder that matches the given controller string. - * - * @param string $controllerString A controller string to match against. Example: ez_content:viewAction. - * - * @return \eZ\Publish\Core\MVC\Symfony\View\Builder\ViewBuilder|null - */ - public function getFromRegistry($controllerString) - { - if (!is_string($controllerString)) { - return null; - } - - foreach ($this->registry as $viewBuilder) { - if ($viewBuilder->matches($controllerString)) { - return $viewBuilder; - } - } - - return null; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Builder/ViewBuilder.php b/eZ/Publish/Core/MVC/Symfony/View/Builder/ViewBuilder.php deleted file mode 100644 index 75aea0bede..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/Builder/ViewBuilder.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View\Builder; - -use eZ\Publish\Core\MVC\Symfony\View\View; - -/** - * Builds View objects based on an array of parameters. - */ -interface ViewBuilder -{ - /** - * Tests if the builder matches the given argument. - * - * @param mixed $argument Anything the builder can decide against. Example: a controller's request string. - * - * @return bool true if the ViewBuilder matches the argument, false otherwise. - */ - public function matches($argument); - - /** - * Builds the View based on $parameters. - * - * @param array $parameters - * - * @return \eZ\Publish\Core\MVC\Symfony\View\View An implementation of the View interface - */ - public function buildView(array $parameters); -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Builder/ViewBuilderRegistry.php b/eZ/Publish/Core/MVC/Symfony/View/Builder/ViewBuilderRegistry.php deleted file mode 100644 index 52febddd47..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/Builder/ViewBuilderRegistry.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View\Builder; - -/** - * A simple registry of ViewBuilders that uses the ViewBuilder's match() method to identify the builder. - */ -interface ViewBuilderRegistry -{ - /** - * Returns the ViewBuilder matching the argument. - * - * @param mixed $argument - * - * @return \eZ\Publish\Core\MVC\Symfony\View\Builder\ViewBuilder|null The ViewBuilder, or null if there's none. - */ - public function getFromRegistry($argument); - - /** - * Adds ViewBuilders from the $objects array to the registry. - * - * @param \eZ\Publish\Core\MVC\Symfony\View\Builder\ViewBuilder[] $objects - */ - public function addToRegistry(array $objects); -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Configurator.php b/eZ/Publish/Core/MVC/Symfony/View/Configurator.php deleted file mode 100644 index e79c67683e..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/Configurator.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View; - -/** - * Configures a View object. - * - * Example: set the template, add extra parameters. - */ -interface Configurator -{ - public function configure(View $view); -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Configurator/ViewProvider.php b/eZ/Publish/Core/MVC/Symfony/View/Configurator/ViewProvider.php deleted file mode 100644 index dc728c3b8f..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/Configurator/ViewProvider.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View\Configurator; - -use eZ\Publish\Core\MVC\Symfony\View\Configurator; -use eZ\Publish\Core\MVC\Symfony\View\Provider\Registry; -use eZ\Publish\Core\MVC\Symfony\View\View; - -/** - * Configures a view based on the ViewProviders. - * - * Typically, the Configured ViewProvider will be included, meaning that Views will be customized based on the - * view rules defined in the siteaccess aware configuration (content_view, block_view, ...). - */ -class ViewProvider implements Configurator -{ - /** @var \eZ\Publish\Core\MVC\Symfony\View\Provider\Registry */ - private $providerRegistry; - - /** - * ViewProvider constructor. - * - * @param \eZ\Publish\Core\MVC\Symfony\View\Provider\Registry $providersRegistry - */ - public function __construct(Registry $providersRegistry) - { - $this->providerRegistry = $providersRegistry; - } - - public function configure(View $view) - { - foreach ($this->providerRegistry->getViewProviders($view) as $viewProvider) { - if ($providerView = $viewProvider->getView($view)) { - $view->setConfigHash($providerView->getConfigHash()); - if (($templateIdentifier = $providerView->getTemplateIdentifier()) !== null) { - $view->setTemplateIdentifier($templateIdentifier); - } - - if (($controllerReference = $providerView->getControllerReference()) !== null) { - $view->setControllerReference($controllerReference); - } - - $view->addParameters($providerView->getParameters()); - - return; - } - } - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/ContentValueView.php b/eZ/Publish/Core/MVC/Symfony/View/ContentValueView.php deleted file mode 100644 index 16dfa3ce13..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/ContentValueView.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View; - -/** - * A view that contains a Content. - */ -interface ContentValueView -{ - /** - * Returns the Content. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function getContent(); -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/ContentView.php b/eZ/Publish/Core/MVC/Symfony/View/ContentView.php deleted file mode 100644 index f259947361..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/ContentView.php +++ /dev/null @@ -1,113 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Location; - -/** - * Main object to be rendered by the View Manager when viewing a content. - * Holds the path to the template to be rendered by the view manager and the parameters to inject in it. - * - * The template path can be a closure. In that case, the view manager will invoke it instead of loading a template. - * $parameters will be passed to the callable in addition to the Content or Location object (depending on the context). - * The prototype of the closure must be : - * <code> - * namespace Foo; - * use eZ\Publish\API\Repository\Values\Content\ContentInfo; - * use eZ\Publish\API\Repository\Values\Content\Location; - * - * // For a content - * function ( ContentInfo $contentInfo, array $parameters = array() ) - * { - * // Do something to render - * // Must return a string to display - * } - * - * // For a location - * function ( Location $location, array $parameters = array() ) - * { - * // Do something to render - * // Must return a string to display - * } - * </code> - */ -class ContentView extends BaseView implements View, ContentValueView, LocationValueView, EmbedView, CachableView -{ - /** @var \eZ\Publish\API\Repository\Values\Content\Content */ - private $content; - - /** @var \eZ\Publish\API\Repository\Values\Content\Location|null */ - private $location; - - /** @var bool */ - private $isEmbed = false; - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - public function setContent(Content $content) - { - $this->content = $content; - } - - /** - * Returns the Content. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function getContent() - { - return $this->content; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - */ - public function setLocation(Location $location) - { - $this->location = $location; - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Location|null - */ - public function getLocation() - { - return $this->location; - } - - protected function getInternalParameters() - { - $parameters = ['content' => $this->content]; - if ($this->location !== null) { - $parameters['location'] = $this->location; - } - - return $parameters; - } - - /** - * Sets the value as embed / not embed. - * - * @param bool $value - */ - public function setIsEmbed($value) - { - $this->isEmbed = (bool)$value; - } - - /** - * Is the view an embed or not. - * - * @return bool True if the view is an embed, false if it is not. - */ - public function isEmbed() - { - return $this->isEmbed; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/LocationValueView.php b/eZ/Publish/Core/MVC/Symfony/View/LocationValueView.php deleted file mode 100644 index 350cbd9258..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/LocationValueView.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View; - -interface LocationValueView -{ - /** - * @return \eZ\Publish\API\Repository\Values\Content\Location - */ - public function getLocation(); -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Manager.php b/eZ/Publish/Core/MVC/Symfony/View/Manager.php deleted file mode 100644 index 4f4290247e..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/Manager.php +++ /dev/null @@ -1,256 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Event\PreContentViewEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use Psr\Log\LoggerInterface; -use RuntimeException; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Twig\Environment; - -class Manager implements ViewManagerInterface -{ - /** @var \Twig\Environment */ - protected $templateEngine; - - /** @var \Psr\Log\LoggerInterface */ - protected $logger; - - /** - * @var array Array indexed by priority. - * Each priority key is an array of Content View Provider objects having this priority. - * The highest priority number is the highest priority - */ - protected $contentViewProviders = []; - - /** - * @var array Array indexed by priority. - * Each priority key is an array of Location View Provider objects having this priority. - * The highest priority number is the highest priority - */ - protected $locationViewProviders = []; - - /** @var \eZ\Publish\Core\MVC\Symfony\View\Provider\Content[] */ - protected $sortedContentViewProviders; - - /** @var \eZ\Publish\Core\MVC\Symfony\View\Provider\Location[] */ - protected $sortedLocationViewProviders; - - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - /** - * The base layout template to use when the view is requested to be generated - * outside of the pagelayout. - * - * @var string - */ - protected $viewBaseLayout; - - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ - protected $configResolver; - - /** @var \eZ\Publish\Core\MVC\Symfony\View\Configurator */ - private $viewConfigurator; - - public function __construct( - Environment $templateEngine, - EventDispatcherInterface $eventDispatcher, - Repository $repository, - ConfigResolverInterface $configResolver, - $viewBaseLayout, - $viewConfigurator, - LoggerInterface $logger = null - ) { - $this->templateEngine = $templateEngine; - $this->eventDispatcher = $eventDispatcher; - $this->repository = $repository; - $this->configResolver = $configResolver; - $this->viewBaseLayout = $viewBaseLayout; - $this->logger = $logger; - $this->viewConfigurator = $viewConfigurator; - } - - /** - * Helper for {@see addContentViewProvider()} and {@see addLocationViewProvider()}. - * - * @param array $property - * @param \eZ\Publish\Core\MVC\Symfony\View\ViewProvider $viewProvider - * @param int $priority - */ - private function addViewProvider(&$property, $viewProvider, $priority) - { - $priority = (int)$priority; - if (!isset($property[$priority])) { - $property[$priority] = []; - } - - $property[$priority][] = $viewProvider; - } - - /** - * Registers $viewProvider as a valid content view provider. - * When this view provider will be called in the chain depends on $priority. The highest $priority is, the earliest the router will be called. - * - * @param \eZ\Publish\Core\MVC\Symfony\View\ViewProvider $viewProvider - * @param int $priority - */ - public function addContentViewProvider(ViewProvider $viewProvider, $priority = 0) - { - $this->addViewProvider($this->contentViewProviders, $viewProvider, $priority); - } - - /** - * Registers $viewProvider as a valid location view provider. - * When this view provider will be called in the chain depends on $priority. The highest $priority is, the earliest the router will be called. - * - * @param \eZ\Publish\Core\MVC\Symfony\View\ViewProvider $viewProvider - * @param int $priority - */ - public function addLocationViewProvider(ViewProvider $viewProvider, $priority = 0) - { - $this->addViewProvider($this->locationViewProviders, $viewProvider, $priority); - } - - /** - * @return \eZ\Publish\Core\MVC\Symfony\View\ViewProvider[] - */ - public function getAllContentViewProviders() - { - if (empty($this->sortedContentViewProviders)) { - $this->sortedContentViewProviders = $this->sortViewProviders($this->contentViewProviders); - } - - return $this->sortedContentViewProviders; - } - - /** - * @return \eZ\Publish\Core\MVC\Symfony\View\ViewProvider[] - */ - public function getAllLocationViewProviders() - { - if (empty($this->sortedLocationViewProviders)) { - $this->sortedLocationViewProviders = $this->sortViewProviders($this->locationViewProviders); - } - - return $this->sortedLocationViewProviders; - } - - /** - * Sort the registered view providers by priority. - * The highest priority number is the highest priority (reverse sorting). - * - * @param array $property view providers to sort - * - * @return \eZ\Publish\Core\MVC\Symfony\View\Provider\Content[]|\eZ\Publish\Core\MVC\Symfony\View\Provider\Location[]|\eZ\Publish\Core\MVC\Symfony\View\Provider\Block[] - */ - protected function sortViewProviders($property) - { - $sortedViewProviders = []; - krsort($property); - - foreach ($property as $viewProvider) { - $sortedViewProviders = array_merge($sortedViewProviders, $viewProvider); - } - - return $sortedViewProviders; - } - - /** - * Renders $content by selecting the right template. - * $content will be injected in the selected template. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param string $viewType Variation of display for your content. Default is 'full'. - * @param array $parameters Parameters to pass to the template called to - * render the view. By default, it's empty. 'content' entry is - * reserved for the Content that is rendered. - * - * @throws \RuntimeException - * - * @return string - */ - public function renderContent(Content $content, $viewType = ViewManagerInterface::VIEW_TYPE_FULL, $parameters = []) - { - $view = new ContentView(null, $parameters, $viewType); - $view->setContent($content); - if (isset($parameters['location'])) { - $view->setLocation($parameters['location']); - } - - $this->viewConfigurator->configure($view); - - if ($view->getTemplateIdentifier() === null) { - throw new RuntimeException('Unable to find a template for #' . $content->contentInfo->id); - } - - return $this->renderContentView($view, $parameters); - } - - /** - * Renders $location by selecting the right template for $viewType. - * $content and $location will be injected in the selected template. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param string $viewType Variation of display for your content. Default is 'full'. - * @param array $parameters Parameters to pass to the template called to - * render the view. By default, it's empty. 'location' and 'content' - * entries are reserved for the Location (and its Content) that is - * viewed. - * - * @throws \RuntimeException - * - * @return string - */ - public function renderLocation(Location $location, $viewType = ViewManagerInterface::VIEW_TYPE_FULL, $parameters = []) - { - if (!isset($parameters['location'])) { - $parameters['location'] = $location; - } - - if (!isset($parameters['content'])) { - $parameters['content'] = $this->repository->getContentService()->loadContentByContentInfo( - $location->contentInfo, - $this->configResolver->getParameter('languages') - ); - } - - return $this->renderContent($parameters['content'], $viewType, $parameters); - } - - /** - * Renders passed ContentView object via the template engine. - * If $view's template identifier is a closure, then it is called directly and the result is returned as is. - * - * @param \eZ\Publish\Core\MVC\Symfony\View\View $view - * @param array $defaultParams - * - * @return string - */ - public function renderContentView(View $view, array $defaultParams = []) - { - $defaultParams['view_base_layout'] = $this->viewBaseLayout; - $view->addParameters($defaultParams); - $this->eventDispatcher->dispatch(new PreContentViewEvent($view), MVCEvents::PRE_CONTENT_VIEW); - - $templateIdentifier = $view->getTemplateIdentifier(); - $params = $view->getParameters(); - if ($templateIdentifier instanceof \Closure) { - return $templateIdentifier($params); - } - - return $this->templateEngine->render($templateIdentifier, $params); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/ParametersInjector.php b/eZ/Publish/Core/MVC/Symfony/View/ParametersInjector.php deleted file mode 100644 index 775765e1db..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/ParametersInjector.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View; - -/** - * Collects parameters that will be injected into View objects. - */ -interface ParametersInjector -{ - public function injectViewParameters(View $view, array $parameters); -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/ParametersInjector/EventDispatcherInjector.php b/eZ/Publish/Core/MVC/Symfony/View/ParametersInjector/EventDispatcherInjector.php deleted file mode 100644 index ce5ea22277..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/ParametersInjector/EventDispatcherInjector.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View\ParametersInjector; - -use eZ\Publish\Core\MVC\Symfony\View\Event\FilterViewParametersEvent; -use eZ\Publish\Core\MVC\Symfony\View\ParametersInjector; -use eZ\Publish\Core\MVC\Symfony\View\View; -use eZ\Publish\Core\MVC\Symfony\View\ViewEvents; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; - -/** - * Injects into a View parameters that were collected via the EventDispatcher. - */ -class EventDispatcherInjector implements ParametersInjector -{ - /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ - private $eventDispatcher; - - public function __construct(EventDispatcherInterface $eventDispatcher) - { - $this->eventDispatcher = $eventDispatcher; - } - - public function injectViewParameters(View $view, array $parameters) - { - $event = new FilterViewParametersEvent($view, $parameters); - $this->eventDispatcher->dispatch($event, ViewEvents::FILTER_VIEW_PARAMETERS); - $view->addParameters($event->getViewParameters()); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Provider/Configured.php b/eZ/Publish/Core/MVC/Symfony/View/Provider/Configured.php deleted file mode 100644 index c472cd10f1..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/Provider/Configured.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View\Provider; - -use eZ\Publish\Core\MVC\Symfony\Matcher\MatcherFactoryInterface; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; -use eZ\Publish\Core\MVC\Symfony\View\View; -use eZ\Publish\Core\MVC\Symfony\View\ViewProvider; -use Symfony\Component\HttpKernel\Controller\ControllerReference; - -/** - * Base for View Providers. - */ -class Configured implements ViewProvider -{ - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\MatcherFactoryInterface */ - protected $matcherFactory; - - /** - * @param \eZ\Publish\Core\MVC\Symfony\Matcher\MatcherFactoryInterface $matcherFactory - */ - public function __construct(MatcherFactoryInterface $matcherFactory) - { - $this->matcherFactory = $matcherFactory; - } - - public function getView(View $view) - { - if (($configHash = $this->matcherFactory->match($view)) === null) { - return null; - } - - return $this->buildContentView($configHash); - } - - /** - * Builds a ContentView object from $viewConfig. - * - * @param array $viewConfig - * - * @return \eZ\Publish\Core\MVC\Symfony\View\ContentView - */ - protected function buildContentView(array $viewConfig) - { - $view = new ContentView(); - $view->setConfigHash($viewConfig); - if (isset($viewConfig['template'])) { - $view->setTemplateIdentifier($viewConfig['template']); - } - if (isset($viewConfig['controller'])) { - $view->setControllerReference(new ControllerReference($viewConfig['controller'])); - } - if (isset($viewConfig['params']) && is_array($viewConfig['params'])) { - $view->addParameters($viewConfig['params']); - } - - return $view; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Provider/Content.php b/eZ/Publish/Core/MVC/Symfony/View/Provider/Content.php deleted file mode 100644 index c3d788aa60..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/Provider/Content.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View\Provider; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; - -/** - * Interface for content view providers. - * - * Content view providers select a view for a given content, depending on its own internal rules. - * - * @deprecated since 6.0.0 - */ -interface Content -{ - /** - * Returns a ContentView object corresponding to $contentInfo, or null if not applicable. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param string $viewType Variation of display for your content - * - * @return \eZ\Publish\Core\MVC\Symfony\View\ContentView|null - */ - public function getView(ContentInfo $contentInfo, $viewType); -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Provider/Location.php b/eZ/Publish/Core/MVC/Symfony/View/Provider/Location.php deleted file mode 100644 index a9c0d7ad5f..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/Provider/Location.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View\Provider; - -use eZ\Publish\API\Repository\Values\Content\Location as APIContentLocation; - -/** - * Interface for location view providers. - * - * Location view providers select a view for a given location, depending on its own internal rules. - * - * @deprecated since 6.0.0 - */ -interface Location -{ - /** - * Returns a ContentView object corresponding to $location, or null if not applicable. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param string $viewType Variation of display for your content. - * - * @return \eZ\Publish\Core\MVC\Symfony\View\ContentView|null - */ - public function getView(APIContentLocation $location, $viewType); -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Provider/Registry.php b/eZ/Publish/Core/MVC/Symfony/View/Provider/Registry.php deleted file mode 100644 index d9548d4f72..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/Provider/Registry.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View\Provider; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\MVC\Symfony\View\View; - -class Registry -{ - /** - * Array of ViewProvider, indexed by handled type. - * - * @var \eZ\Publish\Core\MVC\Symfony\View\ViewProvider[][] - */ - private $viewProviders; - - /** - * @param \eZ\Publish\Core\MVC\Symfony\View\View $view - * - * @return \eZ\Publish\Core\MVC\Symfony\View\ViewProvider[] - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - */ - public function getViewProviders(View $view) - { - foreach (array_keys($this->viewProviders) as $type) { - if ($view instanceof $type) { - return $this->viewProviders[$type]; - } - } - throw new InvalidArgumentException('view', 'No compatible ViewProvider found for ' . gettype($view)); - } - - /** - * Sets the complete list of view providers. - * - * @param array $viewProviders ['type' => [ViewProvider1, ViewProvider2]] - */ - public function setViewProviders(array $viewProviders) - { - $this->viewProviders = $viewProviders; - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/QueryView.php b/eZ/Publish/Core/MVC/Symfony/View/QueryView.php deleted file mode 100644 index ac9cb9d239..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/QueryView.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\View; - -final class QueryView extends BaseView -{ -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Renderer.php b/eZ/Publish/Core/MVC/Symfony/View/Renderer.php deleted file mode 100644 index bab36c5f81..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/Renderer.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View; - -/** - * Renders a View to a string representation. - */ -interface Renderer -{ - /** - * @param \eZ\Publish\Core\MVC\Symfony\View\View $view - * - * @return string - */ - public function render(View $view); -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Renderer/TemplateRenderer.php b/eZ/Publish/Core/MVC/Symfony/View/Renderer/TemplateRenderer.php deleted file mode 100644 index eef9fd86a2..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/Renderer/TemplateRenderer.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View\Renderer; - -use Closure; -use eZ\Publish\Core\MVC\Exception\NoViewTemplateException; -use eZ\Publish\Core\MVC\Symfony\Event\PreContentViewEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\View\Renderer; -use eZ\Publish\Core\MVC\Symfony\View\View; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Twig\Environment; - -class TemplateRenderer implements Renderer -{ - /** @var \Twig\Environment */ - protected $templateEngine; - - /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ - protected $eventDispatcher; - - public function __construct(Environment $templateEngine, EventDispatcherInterface $eventDispatcher) - { - $this->templateEngine = $templateEngine; - $this->eventDispatcher = $eventDispatcher; - } - - /** - * @param \eZ\Publish\Core\MVC\Symfony\View\View $view - * - * @throws \eZ\Publish\Core\MVC\Exception\NoViewTemplateException - * - * @return string - */ - public function render(View $view) - { - $this->eventDispatcher->dispatch(new PreContentViewEvent($view), MVCEvents::PRE_CONTENT_VIEW); - - $templateIdentifier = $view->getTemplateIdentifier(); - if ($templateIdentifier instanceof Closure) { - return $templateIdentifier($view->getParameters()); - } - - if ($view->getTemplateIdentifier() === null) { - throw new NoViewTemplateException($view); - } - - return $this->templateEngine->render( - $view->getTemplateIdentifier(), - $view->getParameters() - ); - } -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/VariableProviderRegistry.php b/eZ/Publish/Core/MVC/Symfony/View/VariableProviderRegistry.php deleted file mode 100644 index 98106c0202..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/VariableProviderRegistry.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\MVC\Symfony\View; - -use eZ\Publish\SPI\MVC\View\VariableProvider; - -interface VariableProviderRegistry -{ - public function setTwigVariableProvider(VariableProvider $twigVariableProvider): void; - - public function getTwigVariableProvider(string $identifier): VariableProvider; - - public function hasTwigVariableProvider(string $identifier): bool; -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/View.php b/eZ/Publish/Core/MVC/Symfony/View/View.php deleted file mode 100644 index 2058beaa9f..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/View.php +++ /dev/null @@ -1,130 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View; - -use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\HttpKernel\Controller\ControllerReference; - -/** - * Main interface for Views of Value objects (content, block...). - */ -interface View -{ - /** - * Sets $templateIdentifier to the content view. - * Can be either a valid template identifier such as "MyBundle:subfolder:my_template.html.twig" or a closure. - * In the case of a closure, it will receive an array of parameters as an argument and must return the result to display. - * - * The prototype of the closure must be : - * <code> - * function (array $params = []) - * { - * // Do something to render - * // Must return a string to display - * } - * </code> - * Must throw a \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType exception if $templateIdentifier is invalid. - * - * @param string|\Closure $templateIdentifier - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType - */ - public function setTemplateIdentifier($templateIdentifier); - - /** - * Returns the registered template identifier. - * - * @return string|\Closure - */ - public function getTemplateIdentifier(); - - /** - * Sets $parameters that will later be injected to the template/closure. - * If some parameters were already present, $parameters will replace them. - * - * @param array $parameters Hash of parameters - */ - public function setParameters(array $parameters); - - /** - * Adds a hash of parameters to the existing parameters. - * - * @param array $parameters - */ - public function addParameters(array $parameters); - - /** - * Returns registered parameters. - * - * @return array - */ - public function getParameters(); - - /** - * Checks if $parameterName exists. - * - * @param string $parameterName - * - * @return bool - */ - public function hasParameter($parameterName); - - /** - * Returns parameter value by $parameterName. - * Throws an \InvalidArgumentException if $parameterName is not set. - * - * @param string $parameterName - * - * @throws \InvalidArgumentException - * - * @return mixed - */ - public function getParameter($parameterName); - - /** - * Injects the config hash that was used to match and generate the current view. - * Typically, the hash would have as keys: - * - template : The template that has been matched - * - match : The matching configuration, including the matcher "identifier" and what has been passed to it. - * - matcher : The matcher object. - * - * @param array $config - */ - public function setConfigHash(array $config); - - /** - * Returns the config hash. - * - * @return array|null - */ - public function getConfigHash(); - - public function setViewType($viewType); - - public function getViewType(); - - public function setControllerReference(ControllerReference $controllerReference); - - /** - * @return \Symfony\Component\HttpKernel\Controller\ControllerReference - */ - public function getControllerReference(); - - /** - * Sets a pre-configured Response that will be used to render the View. - * - * @param \Symfony\Component\HttpFoundation\Response $response - */ - public function setResponse(Response $response); - - /** - * Returns the pre-configured Response. - * - * @return \Symfony\Component\HttpFoundation\Response|null - */ - public function getResponse(); -} diff --git a/eZ/Publish/Core/MVC/Symfony/View/ViewProvider.php b/eZ/Publish/Core/MVC/Symfony/View/ViewProvider.php deleted file mode 100644 index af318fdc52..0000000000 --- a/eZ/Publish/Core/MVC/Symfony/View/ViewProvider.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\MVC\Symfony\View; - -interface ViewProvider -{ - /** - * @return View - */ - public function getView(View $view); -} diff --git a/eZ/Publish/Core/Notification/Renderer/NotificationRenderer.php b/eZ/Publish/Core/Notification/Renderer/NotificationRenderer.php deleted file mode 100644 index 227811541b..0000000000 --- a/eZ/Publish/Core/Notification/Renderer/NotificationRenderer.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Notification\Renderer; - -use eZ\Publish\API\Repository\Values\Notification\Notification; - -interface NotificationRenderer -{ - /** - * @param \eZ\Publish\API\Repository\Values\Notification\Notification $notification - * - * @return string - */ - public function render(Notification $notification): string; - - /** - * @param \eZ\Publish\API\Repository\Values\Notification\Notification $notification - * - * @return string|null - */ - public function generateUrl(Notification $notification): ?string; -} diff --git a/eZ/Publish/Core/Notification/Renderer/Registry.php b/eZ/Publish/Core/Notification/Renderer/Registry.php deleted file mode 100644 index 54aeabc8a2..0000000000 --- a/eZ/Publish/Core/Notification/Renderer/Registry.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Notification\Renderer; - -class Registry -{ - /** @var \eZ\Publish\Core\Notification\Renderer\NotificationRenderer[] */ - protected $registry = []; - - /** - * @param string $alias - * @param \eZ\Publish\Core\Notification\Renderer\NotificationRenderer $notificationRenderer - */ - public function addRenderer(string $alias, NotificationRenderer $notificationRenderer): void - { - $this->registry[$alias] = $notificationRenderer; - } - - /** - * @param string $alias - * - * @return \eZ\Publish\Core\Notification\Renderer\NotificationRenderer - */ - public function getRenderer(string $alias): NotificationRenderer - { - return $this->registry[$alias]; - } - - /** - * @param string $alias - * - * @return bool - */ - public function hasRenderer(string $alias): bool - { - return isset($this->registry[$alias]); - } -} diff --git a/eZ/Publish/Core/Pagination/Pagerfanta/AbstractSearchResultAdapter.php b/eZ/Publish/Core/Pagination/Pagerfanta/AbstractSearchResultAdapter.php deleted file mode 100644 index 2c59639b43..0000000000 --- a/eZ/Publish/Core/Pagination/Pagerfanta/AbstractSearchResultAdapter.php +++ /dev/null @@ -1,149 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Pagination\Pagerfanta; - -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResultCollection; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use Pagerfanta\Adapter\AdapterInterface; - -abstract class AbstractSearchResultAdapter implements AdapterInterface, SearchResultAdapter -{ - /** @var \eZ\Publish\API\Repository\SearchService */ - private $searchService; - - /** @var \eZ\Publish\API\Repository\Values\Content\Query */ - private $query; - - /** @var array */ - private $languageFilter; - - /** @var \eZ\Publish\API\Repository\Values\Content\Search\AggregationResultCollection|null */ - private $aggregations; - - /** @var float|null */ - private $time; - - /** @var bool|null */ - private $timedOut; - - /** @var float|null */ - private $maxScore; - - /** @var int|null */ - private $totalCount; - - public function __construct(Query $query, SearchService $searchService, array $languageFilter = []) - { - $this->query = $query; - $this->searchService = $searchService; - $this->languageFilter = $languageFilter; - } - - /** - * Returns the number of results. - * - * @return int The number of results. - */ - public function getNbResults() - { - if (isset($this->totalCount)) { - return $this->totalCount; - } - - $countQuery = clone $this->query; - $countQuery->limit = 0; - // Skip facets/aggregations computing - $countQuery->facetBuilders = []; - $countQuery->aggregations = []; - - $searchResults = $this->executeQuery( - $this->searchService, - $countQuery, - $this->languageFilter - ); - - return $this->totalCount = $searchResults->totalCount; - } - - /** - * Returns a slice of the results, as SearchHit objects. - * - * @param int $offset The offset. - * @param int $length The length. - * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchHit[] - */ - public function getSlice($offset, $length) - { - $query = clone $this->query; - $query->offset = $offset; - $query->limit = $length; - $query->performCount = false; - - $searchResult = $this->executeQuery( - $this->searchService, - $query, - $this->languageFilter - ); - - $this->aggregations = $searchResult->aggregations; - $this->time = $searchResult->time; - $this->timedOut = $searchResult->timedOut; - $this->maxScore = $searchResult->maxScore; - - // Set count for further use if returned by search engine despite !performCount (Solr, ES) - if (!isset($this->totalCount) && isset($searchResult->totalCount)) { - $this->totalCount = $searchResult->totalCount; - } - - return $searchResult->searchHits; - } - - public function getAggregations(): AggregationResultCollection - { - if ($this->aggregations === null) { - $aggregationQuery = clone $this->query; - $aggregationQuery->offset = 0; - $aggregationQuery->limit = 0; - - $searchResults = $this->executeQuery( - $this->searchService, - $aggregationQuery, - $this->languageFilter - ); - - $this->aggregations = $searchResults->aggregations; - } - - return $this->aggregations; - } - - public function getTime(): ?float - { - return $this->time; - } - - public function getTimedOut(): ?bool - { - return $this->timedOut; - } - - public function getMaxScore(): ?float - { - return $this->maxScore; - } - - abstract protected function executeQuery( - SearchService $searchService, - Query $query, - array $languageFilter - ): SearchResult; -} diff --git a/eZ/Publish/Core/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php b/eZ/Publish/Core/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php deleted file mode 100644 index 16d6ba5b49..0000000000 --- a/eZ/Publish/Core/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Pagination\Pagerfanta\AdapterFactory; - -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\Core\Pagination\Pagerfanta\ContentSearchHitAdapter; -use eZ\Publish\Core\Pagination\Pagerfanta\FixedSearchResultHitAdapter; -use eZ\Publish\Core\Pagination\Pagerfanta\LocationSearchHitAdapter; -use Pagerfanta\Adapter\AdapterInterface; - -/** - * @internal - */ -final class SearchHitAdapterFactory implements SearchHitAdapterFactoryInterface -{ - /** @var \eZ\Publish\API\Repository\SearchService */ - private $searchService; - - public function __construct(SearchService $searchService) - { - $this->searchService = $searchService; - } - - public function createAdapter(Query $query, array $languageFilter = []): AdapterInterface - { - if ($query instanceof LocationQuery) { - return new LocationSearchHitAdapter($query, $this->searchService, $languageFilter); - } - - return new ContentSearchHitAdapter($query, $this->searchService, $languageFilter); - } - - public function createFixedAdapter(Query $query, array $languageFilter = []): AdapterInterface - { - if ($query instanceof LocationQuery) { - $searchResults = $this->searchService->findLocations($query, $languageFilter); - } else { - $searchResults = $this->searchService->findContent($query, $languageFilter); - } - - return new FixedSearchResultHitAdapter($searchResults); - } -} diff --git a/eZ/Publish/Core/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php b/eZ/Publish/Core/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php deleted file mode 100644 index 7d67c28415..0000000000 --- a/eZ/Publish/Core/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Pagination\Pagerfanta\AdapterFactory; - -use eZ\Publish\API\Repository\Values\Content\Query; -use Pagerfanta\Adapter\AdapterInterface; - -/** - * @internal - */ -interface SearchHitAdapterFactoryInterface -{ - public function createAdapter(Query $query, array $languageFilter = []): AdapterInterface; - - public function createFixedAdapter(Query $query, array $languageFilter = []): AdapterInterface; -} diff --git a/eZ/Publish/Core/Pagination/Pagerfanta/ContentFilteringAdapter.php b/eZ/Publish/Core/Pagination/Pagerfanta/ContentFilteringAdapter.php deleted file mode 100644 index b0cdc14e86..0000000000 --- a/eZ/Publish/Core/Pagination/Pagerfanta/ContentFilteringAdapter.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Pagination\Pagerfanta; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use Pagerfanta\Adapter\AdapterInterface; - -/** - * Pagerfanta adapter for content filtering. - */ -final class ContentFilteringAdapter implements AdapterInterface -{ - /** @var \eZ\Publish\API\Repository\ContentService */ - private $contentService; - - /** @var \eZ\Publish\API\Repository\Values\Filter\Filter */ - private $filter; - - /** @var array|null */ - private $languageFilter; - - /** @var int|null */ - private $totalCount; - - public function __construct( - ContentService $contentService, - Filter $filter, - ?array $languageFilter = null - ) { - $this->contentService = $contentService; - $this->filter = $filter; - $this->languageFilter = $languageFilter; - } - - public function getNbResults(): int - { - if ($this->totalCount === null) { - $this->totalCount = $this->contentService->count($this->filter, $this->languageFilter); - } - - return $this->totalCount; - } - - public function getSlice($offset, $length): iterable - { - $selectFilter = clone $this->filter; - $selectFilter->sliceBy($length, $offset); - - $results = $this->contentService->find($selectFilter, $this->languageFilter); - if ($this->totalCount === null) { - $this->totalCount = $results->getTotalCount(); - } - - return $results; - } -} diff --git a/eZ/Publish/Core/Pagination/Pagerfanta/ContentSearchAdapter.php b/eZ/Publish/Core/Pagination/Pagerfanta/ContentSearchAdapter.php deleted file mode 100644 index 841f2f2c2b..0000000000 --- a/eZ/Publish/Core/Pagination/Pagerfanta/ContentSearchAdapter.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Pagination\Pagerfanta; - -/** - * Pagerfanta adapter for eZ Publish content search. - * Will return results as Content objects. - */ -class ContentSearchAdapter extends ContentSearchHitAdapter -{ - /** - * Returns a slice of the results as Content objects. - * - * @param int $offset The offset. - * @param int $length The length. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content[] - */ - public function getSlice($offset, $length) - { - $list = []; - foreach (parent::getSlice($offset, $length) as $hit) { - $list[] = $hit->valueObject; - } - - return $list; - } -} diff --git a/eZ/Publish/Core/Pagination/Pagerfanta/ContentSearchHitAdapter.php b/eZ/Publish/Core/Pagination/Pagerfanta/ContentSearchHitAdapter.php deleted file mode 100644 index 1994bb5586..0000000000 --- a/eZ/Publish/Core/Pagination/Pagerfanta/ContentSearchHitAdapter.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Pagination\Pagerfanta; - -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; - -/** - * Pagerfanta adapter for eZ Publish content search. - * Will return results as SearchHit objects. - */ -class ContentSearchHitAdapter extends AbstractSearchResultAdapter -{ - protected function executeQuery( - SearchService $searchService, - Query $query, - array $languageFilter - ): SearchResult { - return $searchService->findContent($query, $languageFilter); - } -} diff --git a/eZ/Publish/Core/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php b/eZ/Publish/Core/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php deleted file mode 100644 index 6c54f34bfe..0000000000 --- a/eZ/Publish/Core/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Pagination\Pagerfanta; - -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResultCollection; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; - -final class FixedSearchResultHitAdapter implements SearchResultAdapter -{ - /** @var \eZ\Publish\API\Repository\Values\Content\Search\SearchResult */ - private $searchResult; - - public function __construct(SearchResult $searchResult) - { - $this->searchResult = $searchResult; - } - - public function getNbResults(): int - { - return $this->searchResult->totalCount ?? -1; - } - - public function getSlice($offset, $length) - { - return $this->searchResult->searchHits; - } - - public function getAggregations(): AggregationResultCollection - { - return $this->searchResult->aggregations; - } - - public function getTime(): ?float - { - return $this->searchResult->time; - } - - public function getTimedOut(): ?bool - { - return $this->searchResult->timedOut; - } - - public function getMaxScore(): ?float - { - return $this->searchResult->maxScore; - } -} diff --git a/eZ/Publish/Core/Pagination/Pagerfanta/LocationFilteringAdapter.php b/eZ/Publish/Core/Pagination/Pagerfanta/LocationFilteringAdapter.php deleted file mode 100644 index e752f6812c..0000000000 --- a/eZ/Publish/Core/Pagination/Pagerfanta/LocationFilteringAdapter.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Pagination\Pagerfanta; - -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use Pagerfanta\Adapter\AdapterInterface; - -final class LocationFilteringAdapter implements AdapterInterface -{ - /** @var \eZ\Publish\API\Repository\LocationService */ - private $locationService; - - /** @var \eZ\Publish\API\Repository\Values\Filter\Filter */ - private $filter; - - /** @var array|null */ - private $languageFilter; - - /** @var int|null */ - private $totalCount; - - public function __construct( - LocationService $locationService, - Filter $filter, - ?array $languageFilter = null - ) { - $this->locationService = $locationService; - $this->filter = $filter; - $this->languageFilter = $languageFilter; - } - - public function getNbResults(): int - { - if ($this->totalCount === null) { - $this->totalCount = $this->locationService->count($this->filter, $this->languageFilter); - } - - return $this->totalCount; - } - - public function getSlice($offset, $length): iterable - { - $selectFilter = clone $this->filter; - $selectFilter->sliceBy($length, $offset); - - $results = $this->locationService->find($selectFilter, $this->languageFilter); - if ($this->totalCount === null) { - $this->totalCount = $results->getTotalCount(); - } - - return $results; - } -} diff --git a/eZ/Publish/Core/Pagination/Pagerfanta/LocationSearchAdapter.php b/eZ/Publish/Core/Pagination/Pagerfanta/LocationSearchAdapter.php deleted file mode 100644 index 4dbed959ee..0000000000 --- a/eZ/Publish/Core/Pagination/Pagerfanta/LocationSearchAdapter.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Pagination\Pagerfanta; - -/** - * Pagerfanta adapter for eZ Publish content search. - * Will return results as Location objects. - */ -class LocationSearchAdapter extends LocationSearchHitAdapter -{ - /** - * Returns a slice of the results as Location objects. - * - * @param int $offset The offset. - * @param int $length The length. - * - * @return \eZ\Publish\API\Repository\Values\Content\Location[] - */ - public function getSlice($offset, $length) - { - $list = []; - foreach (parent::getSlice($offset, $length) as $hit) { - $list[] = $hit->valueObject; - } - - return $list; - } -} diff --git a/eZ/Publish/Core/Pagination/Pagerfanta/LocationSearchHitAdapter.php b/eZ/Publish/Core/Pagination/Pagerfanta/LocationSearchHitAdapter.php deleted file mode 100644 index d1dd16f844..0000000000 --- a/eZ/Publish/Core/Pagination/Pagerfanta/LocationSearchHitAdapter.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Pagination\Pagerfanta; - -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; - -/** - * Pagerfanta adapter for eZ Publish location search. - * Will return results as SearchHit objects. - */ -class LocationSearchHitAdapter extends AbstractSearchResultAdapter -{ - public function __construct(LocationQuery $query, SearchService $searchService, array $languageFilter = []) - { - parent::__construct($query, $searchService, $languageFilter); - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\LocationQuery $query - */ - protected function executeQuery(SearchService $searchService, Query $query, array $languageFilter): SearchResult - { - return $searchService->findLocations($query, $languageFilter); - } -} diff --git a/eZ/Publish/Core/Pagination/Tests/AdapterFactory/SearchHitAdapterFactoryTest.php b/eZ/Publish/Core/Pagination/Tests/AdapterFactory/SearchHitAdapterFactoryTest.php deleted file mode 100644 index 9b819ebe76..0000000000 --- a/eZ/Publish/Core/Pagination/Tests/AdapterFactory/SearchHitAdapterFactoryTest.php +++ /dev/null @@ -1,108 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Pagination\Tests\AdapterFactory; - -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use eZ\Publish\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactory; -use eZ\Publish\Core\Pagination\Pagerfanta\ContentSearchHitAdapter; -use eZ\Publish\Core\Pagination\Pagerfanta\FixedSearchResultHitAdapter; -use eZ\Publish\Core\Pagination\Pagerfanta\LocationSearchHitAdapter; -use PHPUnit\Framework\TestCase; - -final class SearchHitAdapterFactoryTest extends TestCase -{ - private const EXAMPLE_LANGUAGE_FILTER = [ - 'language' => 'eng-GB', - ]; - - /** @var \eZ\Publish\API\Repository\SearchService|\PHPUnit\Framework\MockObject\MockObject */ - private $searchService; - - /** @var \eZ\Publish\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactory */ - private $searchHitAdapterFactory; - - protected function setUp(): void - { - $this->searchService = $this->createMock(SearchService::class); - $this->searchHitAdapterFactory = new SearchHitAdapterFactory($this->searchService); - } - - public function testCreateAdapterForContentQuery(): void - { - $query = new Query(); - - $this->assertEquals( - new ContentSearchHitAdapter( - $query, - $this->searchService, - self::EXAMPLE_LANGUAGE_FILTER - ), - $this->searchHitAdapterFactory->createAdapter($query, self::EXAMPLE_LANGUAGE_FILTER) - ); - } - - public function testCreateAdapterForLocationQuery(): void - { - $query = new LocationQuery(); - - $this->assertEquals( - new LocationSearchHitAdapter( - $query, - $this->searchService, - self::EXAMPLE_LANGUAGE_FILTER - ), - $this->searchHitAdapterFactory->createAdapter($query, self::EXAMPLE_LANGUAGE_FILTER) - ); - } - - /** - * @dataProvider dataProviderForCreateFixedAdapter - */ - public function testCreateFixedAdapter(Query $query, string $expectedSearchMethod): void - { - $hits = [ - new SearchHit(), - new SearchHit(), - new SearchHit(), - ]; - - $searchResult = new SearchResult([ - 'searchHits' => $hits, - 'totalCount' => count($hits), - ]); - - $this->searchService - ->expects($this->once()) - ->method($expectedSearchMethod) - ->with($query, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($searchResult); - - $this->assertEquals( - new FixedSearchResultHitAdapter($searchResult), - $this->searchHitAdapterFactory->createFixedAdapter($query, self::EXAMPLE_LANGUAGE_FILTER) - ); - } - - public function dataProviderForCreateFixedAdapter(): iterable - { - yield 'content query' => [ - new Query(), - 'findContent', - ]; - - yield 'location query' => [ - new LocationQuery(), - 'findLocations', - ]; - } -} diff --git a/eZ/Publish/Core/Pagination/Tests/ContentFilteringAdapterTest.php b/eZ/Publish/Core/Pagination/Tests/ContentFilteringAdapterTest.php deleted file mode 100644 index b0e88c0603..0000000000 --- a/eZ/Publish/Core/Pagination/Tests/ContentFilteringAdapterTest.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Pagination\Tests; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentList; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\Core\Pagination\Pagerfanta\ContentFilteringAdapter; -use eZ\Publish\Core\Search\Tests\TestCase; - -final class ContentFilteringAdapterTest extends TestCase -{ - private const EXAMPLE_LANGUAGE_FILTER = [ - 'languages' => ['eng-GB', 'pol-PL'], - 'useAlwaysAvailable' => true, - ]; - - /** @var \eZ\Publish\API\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject */ - private $contentService; - - protected function setUp(): void - { - $this->contentService = $this->createMock(ContentService::class); - } - - public function testGetNbResults(): void - { - $expectedNumberOfItems = 10; - - $filter = new Filter(); - $filter->sliceBy(5, 0); - - // Make sure that count query doesn't fetch results - $this->contentService - ->expects(self::never()) - ->method('find'); - - $this->contentService - ->method('count') - ->with( - $filter, - self::EXAMPLE_LANGUAGE_FILTER - ) - ->willReturn($expectedNumberOfItems); - - $adapter = new ContentFilteringAdapter( - $this->contentService, - $filter, - self::EXAMPLE_LANGUAGE_FILTER - ); - - self::assertEquals( - $expectedNumberOfItems, - $adapter->getNbResults() - ); - } - - public function testGetSlice(): void - { - $expectedContentList = $this->createExpectedContentList(10); - - $filter = new Filter(); - $filter->sliceBy(20, 10); - - $this->contentService - ->method('find') - ->with($filter, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($expectedContentList); - - $adapter = new ContentFilteringAdapter( - $this->contentService, - $filter, - self::EXAMPLE_LANGUAGE_FILTER - ); - - $this->assertEquals( - $expectedContentList, - $adapter->getSlice(10, 20) - ); - } - - private function createExpectedContentList(int $numberOfItems): ContentList - { - $items = []; - for ($i = 0; $i < $numberOfItems; ++$i) { - $items[] = $this->createMock(Content::class); - } - - return new ContentList($numberOfItems, $items); - } -} diff --git a/eZ/Publish/Core/Pagination/Tests/ContentSearchAdapterTest.php b/eZ/Publish/Core/Pagination/Tests/ContentSearchAdapterTest.php deleted file mode 100644 index 828a40b2be..0000000000 --- a/eZ/Publish/Core/Pagination/Tests/ContentSearchAdapterTest.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Pagination\Tests; - -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\Core\Pagination\Pagerfanta\ContentSearchAdapter; - -class ContentSearchAdapterTest extends ContentSearchHitAdapterTest -{ - /** - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - * @param \eZ\Publish\API\Repository\SearchService $searchService - * @param array $languageFilter - * - * @return \eZ\Publish\Core\Pagination\Pagerfanta\ContentSearchAdapter - */ - protected function getAdapter(Query $query, SearchService $searchService, array $languageFilter = []) - { - return new ContentSearchAdapter($query, $searchService, $languageFilter); - } - - /** - * Returns expected result from adapter from search hits. - * - * @param $hits - * - * @return mixed - */ - protected function getExpectedFinalResultFromHits($hits) - { - $expectedResult = []; - - /** @var \eZ\Publish\API\Repository\Values\Content\Search\SearchHit[] $hits */ - foreach ($hits as $hit) { - $expectedResult[] = $hit->valueObject; - } - - return $expectedResult; - } -} diff --git a/eZ/Publish/Core/Pagination/Tests/FixedSearchResultHitAdapterTest.php b/eZ/Publish/Core/Pagination/Tests/FixedSearchResultHitAdapterTest.php deleted file mode 100644 index 24f472750f..0000000000 --- a/eZ/Publish/Core/Pagination/Tests/FixedSearchResultHitAdapterTest.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Pagination\Tests; - -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use eZ\Publish\Core\Pagination\Pagerfanta\FixedSearchResultHitAdapter; -use PHPUnit\Framework\TestCase; - -final class FixedSearchResultHitAdapterTest extends TestCase -{ - public function testFixedSearchResultHitAdapter(): void - { - $searchResult = $this->createExampleSearchResult(); - - $adapter = new FixedSearchResultHitAdapter($searchResult); - - $this->assertEquals($searchResult->totalCount, $adapter->getNbResults()); - $this->assertEquals($searchResult->searchHits, $adapter->getSlice(0, 10)); - $this->assertEquals($searchResult->aggregations, $adapter->getAggregations()); - $this->assertEquals($searchResult->maxScore, $adapter->getMaxScore()); - $this->assertEquals($searchResult->time, $adapter->getTime()); - $this->assertEquals($searchResult->timedOut, $adapter->getTimedOut()); - } - - private function createExampleSearchResult(): SearchResult - { - $searchResult = new SearchResult(); - $searchResult->totalCount = 3; - $searchResult->searchHits = [ - new SearchHit(), - new SearchHit(), - new SearchHit(), - ]; - $searchResult->timedOut = true; - $searchResult->time = 30; - $searchResult->maxScore = 5.234; - - return $searchResult; - } -} diff --git a/eZ/Publish/Core/Pagination/Tests/LocationFilteringAdapterTest.php b/eZ/Publish/Core/Pagination/Tests/LocationFilteringAdapterTest.php deleted file mode 100644 index be40ec00da..0000000000 --- a/eZ/Publish/Core/Pagination/Tests/LocationFilteringAdapterTest.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Pagination\Tests; - -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationList; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\Core\Pagination\Pagerfanta\LocationFilteringAdapter; -use eZ\Publish\Core\Search\Tests\TestCase; - -final class LocationFilteringAdapterTest extends TestCase -{ - private const EXAMPLE_LANGUAGE_FILTER = [ - 'languages' => ['eng-GB', 'pol-PL'], - 'useAlwaysAvailable' => true, - ]; - - /** @var \eZ\Publish\API\Repository\LocationService|\PHPUnit\Framework\MockObject\MockObject */ - private $locationService; - - protected function setUp(): void - { - $this->locationService = $this->createMock(LocationService::class); - } - - public function testGetNbResults(): void - { - $expectedNumberOfItems = 10; - - $filter = new Filter(); - $filter->sliceBy(5, 0); - - // Make sure that count query doesn't fetch results - $this->locationService - ->expects(self::never()) - ->method('find'); - - $this->locationService - ->method('count') - ->with( - $filter, - self::EXAMPLE_LANGUAGE_FILTER - ) - ->willReturn($expectedNumberOfItems); - - $adapter = new LocationFilteringAdapter( - $this->locationService, - $filter, - self::EXAMPLE_LANGUAGE_FILTER - ); - - self::assertEquals( - $expectedNumberOfItems, - $adapter->getNbResults() - ); - } - - public function testGetSlice(): void - { - $expectedContentList = $this->createExpectedLocationList(10); - - $filter = new Filter(); - $filter->sliceBy(20, 10); - - $this->locationService - ->method('find') - ->with($filter, self::EXAMPLE_LANGUAGE_FILTER) - ->willReturn($expectedContentList); - - $adapter = new LocationFilteringAdapter( - $this->locationService, - $filter, - self::EXAMPLE_LANGUAGE_FILTER - ); - - $this->assertEquals( - $expectedContentList, - $adapter->getSlice(10, 20) - ); - } - - private function createExpectedLocationList(int $numberOfItems): LocationList - { - $items = []; - for ($i = 0; $i < $numberOfItems; ++$i) { - $items[] = $this->createMock(Location::class); - } - - return new LocationList([ - 'totalCount' => $numberOfItems, - 'locations' => $items, - ]); - } -} diff --git a/eZ/Publish/Core/Pagination/Tests/LocationSearchAdapterTest.php b/eZ/Publish/Core/Pagination/Tests/LocationSearchAdapterTest.php deleted file mode 100644 index b26135e9c7..0000000000 --- a/eZ/Publish/Core/Pagination/Tests/LocationSearchAdapterTest.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Pagination\Tests; - -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\Core\Pagination\Pagerfanta\LocationSearchAdapter; - -class LocationSearchAdapterTest extends LocationSearchHitAdapterTest -{ - /** - * @param \eZ\Publish\API\Repository\Values\Content\LocationQuery $query - * @param \eZ\Publish\API\Repository\SearchService $searchService - * - * @return \eZ\Publish\Core\Pagination\Pagerfanta\LocationSearchAdapter - */ - protected function getAdapter(LocationQuery $query, SearchService $searchService, array $languageFilter = []) - { - return new LocationSearchAdapter($query, $searchService, $languageFilter); - } - - /** - * Returns expected result from adapter from search hits. - * - * @param $hits - * - * @return mixed - */ - protected function getExpectedFinalResultFromHits($hits) - { - $expectedResult = []; - - /** @var \eZ\Publish\API\Repository\Values\Content\Search\SearchHit[] $hits */ - foreach ($hits as $hit) { - $expectedResult[] = $hit->valueObject; - } - - return $expectedResult; - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/ContentHandler.php b/eZ/Publish/Core/Persistence/Cache/ContentHandler.php deleted file mode 100644 index eacaa7fcd8..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/ContentHandler.php +++ /dev/null @@ -1,612 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache; - -use eZ\Publish\API\Repository\Values\Content\Relation as APIRelation; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Handler as ContentHandlerInterface; -use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\UpdateStruct; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * @see \eZ\Publish\SPI\Persistence\Content\Handler - */ -class ContentHandler extends AbstractInMemoryPersistenceHandler implements ContentHandlerInterface -{ - private const CONTENT_IDENTIFIER = 'content'; - private const LOCATION_IDENTIFIER = 'location'; - private const LOCATION_PATH_IDENTIFIER = 'location_path'; - private const CONTENT_INFO_IDENTIFIER = 'content_info'; - private const CONTENT_INFO_BY_REMOTE_ID_IDENTIFIER = 'content_info_by_remote_id'; - private const CONTENT_FIELDS_TYPE_IDENTIFIER = 'content_fields_type'; - private const CONTENT_VERSION_LIST_IDENTIFIER = 'content_version_list'; - private const CONTENT_VERSION_INFO_IDENTIFIER = 'content_version_info'; - private const CONTENT_VERSION_IDENTIFIER = 'content_version'; - - public const ALL_TRANSLATIONS_KEY = '0'; - - /** @var callable */ - private $getContentInfoTags; - - /** @var callable */ - private $getContentInfoKeys; - - /** @var callable */ - private $getContentTags; - - protected function init(): void - { - $this->getContentInfoTags = function (ContentInfo $info, array $tags = []) { - $tags[] = $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$info->id]); - - if ($info->mainLocationId) { - $locations = $this->persistenceHandler->locationHandler()->loadLocationsByContent($info->id); - foreach ($locations as $location) { - $tags[] = $this->cacheIdentifierGenerator->generateTag(self::LOCATION_IDENTIFIER, [$location->id]); - $pathIds = $this->locationPathConverter->convertToPathIds($location->pathString); - foreach ($pathIds as $pathId) { - $tags[] = $this->cacheIdentifierGenerator->generateTag(self::LOCATION_PATH_IDENTIFIER, [$pathId]); - } - } - } - - return $tags; - }; - $this->getContentInfoKeys = function (ContentInfo $info) { - return [ - $this->cacheIdentifierGenerator->generateKey(self::CONTENT_INFO_IDENTIFIER, [$info->id], true), - $this->cacheIdentifierGenerator->generateKey( - self::CONTENT_INFO_BY_REMOTE_ID_IDENTIFIER, - [$this->cacheIdentifierSanitizer->escapeForCacheKey($info->remoteId)], - true - ), - ]; - }; - - $this->getContentTags = function (Content $content) { - $versionInfo = $content->versionInfo; - $tags = [ - $this->cacheIdentifierGenerator->generateTag( - self::CONTENT_FIELDS_TYPE_IDENTIFIER, - [$versionInfo->contentInfo->contentTypeId] - ), - ]; - - return $this->getCacheTagsForVersion($versionInfo, $tags); - }; - } - - /** - * {@inheritdoc} - */ - public function create(CreateStruct $struct) - { - // Cached on demand when published or loaded - $this->logger->logCall(__METHOD__, ['struct' => $struct]); - - return $this->persistenceHandler->contentHandler()->create($struct); - } - - /** - * {@inheritdoc} - */ - public function createDraftFromVersion($contentId, $srcVersion, $userId, ?string $languageCode = null) - { - $this->logger->logCall(__METHOD__, ['content' => $contentId, 'version' => $srcVersion, 'user' => $userId]); - $draft = $this->persistenceHandler->contentHandler()->createDraftFromVersion($contentId, $srcVersion, $userId, $languageCode); - $this->cache->deleteItems([ - $this->cacheIdentifierGenerator->generateKey(self::CONTENT_VERSION_LIST_IDENTIFIER, [$contentId], true), - ]); - - return $draft; - } - - /** - * {@inheritdoc} - */ - public function copy($contentId, $versionNo = null, $newOwnerId = null) - { - $this->logger->logCall(__METHOD__, [ - 'content' => $contentId, - 'version' => $versionNo, - 'newOwner' => $newOwnerId, - ]); - - return $this->persistenceHandler->contentHandler()->copy($contentId, $versionNo, $newOwnerId); - } - - /** - * {@inheritdoc} - */ - public function load($contentId, $versionNo = null, array $translations = null) - { - $keySuffix = $versionNo ? "-{$versionNo}-" : '-'; - $keySuffix .= empty($translations) ? self::ALL_TRANSLATIONS_KEY : implode('|', $translations); - - return $this->getCacheValue( - (int) $contentId, - $this->cacheIdentifierGenerator->generateKey(self::CONTENT_IDENTIFIER, [], true) . '-', - function ($id) use ($versionNo, $translations) { - return $this->persistenceHandler->contentHandler()->load($id, $versionNo, $translations); - }, - $this->getContentTags, - function (Content $content) use ($keySuffix) { - // Version number & translations is part of keySuffix here and depends on what user asked for - return [ - $this->cacheIdentifierGenerator->generateKey( - self::CONTENT_IDENTIFIER, - [$content->versionInfo->contentInfo->id], - true - ) . $keySuffix, - ]; - }, - $keySuffix, - ['content' => $contentId, 'version' => $versionNo, 'translations' => $translations] - ); - } - - public function loadContentList(array $contentIds, array $translations = null): array - { - $keySuffix = '-' . (empty($translations) ? self::ALL_TRANSLATIONS_KEY : implode('|', $translations)); - - return $this->getMultipleCacheValues( - $contentIds, - $this->cacheIdentifierGenerator->generateKey(self::CONTENT_IDENTIFIER, [], true) . '-', - function (array $cacheMissIds) use ($translations) { - return $this->persistenceHandler->contentHandler()->loadContentList($cacheMissIds, $translations); - }, - $this->getContentTags, - function (Content $content) use ($keySuffix) { - // Translations is part of keySuffix here and depends on what user asked for - return [ - $this->cacheIdentifierGenerator->generateKey( - self::CONTENT_IDENTIFIER, - [$content->versionInfo->contentInfo->id], - true - ) . $keySuffix, - ]; - }, - $keySuffix, - ['content' => $contentIds, 'translations' => $translations] - ); - } - - /** - * {@inheritdoc} - */ - public function loadContentInfo($contentId) - { - return $this->getCacheValue( - $contentId, - $this->cacheIdentifierGenerator->generateKey(self::CONTENT_INFO_IDENTIFIER, [], true) . '-', - function ($contentId) { - return $this->persistenceHandler->contentHandler()->loadContentInfo($contentId); - }, - $this->getContentInfoTags, - $this->getContentInfoKeys, - '', - ['content' => $contentId] - ); - } - - public function loadContentInfoList(array $contentIds) - { - return $this->getMultipleCacheValues( - $contentIds, - $this->cacheIdentifierGenerator->generateKey(self::CONTENT_INFO_IDENTIFIER, [], true) . '-', - function (array $cacheMissIds) { - return $this->persistenceHandler->contentHandler()->loadContentInfoList($cacheMissIds); - }, - $this->getContentInfoTags, - $this->getContentInfoKeys, - '', - ['content' => $contentIds] - ); - } - - /** - * {@inheritdoc} - */ - public function loadContentInfoByRemoteId($remoteId) - { - return $this->getCacheValue( - $this->cacheIdentifierSanitizer->escapeForCacheKey($remoteId), - $this->cacheIdentifierGenerator->generateKey(self::CONTENT_INFO_BY_REMOTE_ID_IDENTIFIER, [], true) . '-', - function () use ($remoteId) { - return $this->persistenceHandler->contentHandler()->loadContentInfoByRemoteId($remoteId); - }, - $this->getContentInfoTags, - $this->getContentInfoKeys, - '', - ['content' => $remoteId] - ); - } - - /** - * {@inheritdoc} - */ - public function loadVersionInfo($contentId, $versionNo = null) - { - $keySuffix = $versionNo ? "-{$versionNo}" : ''; - $cacheItem = $this->cache->getItem( - $this->cacheIdentifierGenerator->generateKey( - self::CONTENT_VERSION_INFO_IDENTIFIER, - [$contentId], - true - ) . $keySuffix - ); - - if ($cacheItem->isHit()) { - $this->logger->logCacheHit(['content' => $contentId, 'version' => $versionNo]); - - return $cacheItem->get(); - } - - $this->logger->logCacheMiss(['content' => $contentId, 'version' => $versionNo]); - $versionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo($contentId, $versionNo); - $cacheItem->set($versionInfo); - $cacheItem->tag($this->getCacheTagsForVersion($versionInfo)); - $this->cache->save($cacheItem); - - return $versionInfo; - } - - public function countDraftsForUser(int $userId): int - { - $this->logger->logCall(__METHOD__, ['user' => $userId]); - - return $this->persistenceHandler->contentHandler()->countDraftsForUser($userId); - } - - /** - * {@inheritdoc} - */ - public function loadDraftsForUser($userId) - { - $this->logger->logCall(__METHOD__, ['user' => $userId]); - - return $this->persistenceHandler->contentHandler()->loadDraftsForUser($userId); - } - - public function loadDraftListForUser(int $userId, int $offset = 0, int $limit = -1): array - { - $this->logger->logCall(__METHOD__, ['user' => $userId, 'offset' => $offset, 'limit' => $limit]); - - return $this->persistenceHandler->contentHandler()->loadDraftListForUser($userId, $offset, $limit); - } - - /** - * {@inheritdoc} - */ - public function setStatus($contentId, $status, $versionNo) - { - $this->logger->logCall(__METHOD__, ['content' => $contentId, 'status' => $status, 'version' => $versionNo]); - $return = $this->persistenceHandler->contentHandler()->setStatus($contentId, $status, $versionNo); - - if ($status === VersionInfo::STATUS_PUBLISHED) { - $this->cache->invalidateTags([ - $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]), - ]); - } else { - $this->cache->invalidateTags([ - $this->cacheIdentifierGenerator->generateTag( - self::CONTENT_VERSION_IDENTIFIER, - [$contentId, $versionNo] - ), - ]); - } - - return $return; - } - - /** - * {@inheritdoc} - */ - public function updateMetadata($contentId, MetadataUpdateStruct $struct) - { - $this->logger->logCall(__METHOD__, ['content' => $contentId, 'struct' => $struct]); - $contentInfo = $this->persistenceHandler->contentHandler()->updateMetadata($contentId, $struct); - $this->cache->invalidateTags([ - $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]), - ]); - - return $contentInfo; - } - - /** - * {@inheritdoc} - */ - public function updateContent($contentId, $versionNo, UpdateStruct $struct) - { - $this->logger->logCall(__METHOD__, ['content' => $contentId, 'version' => $versionNo, 'struct' => $struct]); - $content = $this->persistenceHandler->contentHandler()->updateContent($contentId, $versionNo, $struct); - $this->cache->invalidateTags([ - $this->cacheIdentifierGenerator->generateTag( - self::CONTENT_VERSION_IDENTIFIER, - [$contentId, $versionNo] - ), - ]); - - return $content; - } - - /** - * {@inheritdoc} - */ - public function deleteContent($contentId) - { - $this->logger->logCall(__METHOD__, ['content' => $contentId]); - - // Load reverse field relations first - $reverseRelations = $this->persistenceHandler->contentHandler()->loadReverseRelations( - $contentId, - APIRelation::FIELD | APIRelation::ASSET - ); - - $return = $this->persistenceHandler->contentHandler()->deleteContent($contentId); - - if (!empty($reverseRelations)) { - $tags = \array_map( - function ($relation) { - // only the full content object *with* fields is affected by this - return $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$relation->sourceContentId]); - }, - $reverseRelations - ); - } else { - $tags = []; - } - $tags[] = $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]); - $this->cache->invalidateTags($tags); - - return $return; - } - - /** - * {@inheritdoc} - */ - public function deleteVersion($contentId, $versionNo) - { - $this->logger->logCall(__METHOD__, ['content' => $contentId, 'version' => $versionNo]); - $return = $this->persistenceHandler->contentHandler()->deleteVersion($contentId, $versionNo); - $this->cache->invalidateTags([ - $this->cacheIdentifierGenerator->generateTag( - self::CONTENT_VERSION_IDENTIFIER, - [$contentId, $versionNo] - ), - ]); - - return $return; - } - - /** - * {@inheritdoc} - */ - public function listVersions($contentId, $status = null, $limit = -1) - { - // Don't cache non typical lookups to avoid filling up cache and tags. - if ($status !== null || $limit !== -1) { - $this->logger->logCall(__METHOD__, ['content' => $contentId, 'status' => $status]); - - return $this->persistenceHandler->contentHandler()->listVersions($contentId, $status, $limit); - } - - // Cache default lookups - $cacheItem = $this->cache->getItem( - $this->cacheIdentifierGenerator->generateKey(self::CONTENT_VERSION_LIST_IDENTIFIER, [$contentId], true) - ); - - if ($cacheItem->isHit()) { - $this->logger->logCacheHit(['content' => $contentId]); - - return $cacheItem->get(); - } - - $this->logger->logCacheMiss(['content' => $contentId]); - $versions = $this->persistenceHandler->contentHandler()->listVersions($contentId, $status, $limit); - $cacheItem->set($versions); - $tags = [ - $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]), - ]; - - foreach ($versions as $version) { - $tags = $this->getCacheTagsForVersion($version, $tags); - } - $cacheItem->tag($tags); - $this->cache->save($cacheItem); - - return $versions; - } - - /** - * {@inheritdoc} - */ - public function addRelation(RelationCreateStruct $relation) - { - $this->logger->logCall(__METHOD__, ['struct' => $relation]); - - return $this->persistenceHandler->contentHandler()->addRelation($relation); - } - - /** - * {@inheritdoc} - */ - public function removeRelation($relationId, $type) - { - $this->logger->logCall(__METHOD__, ['relation' => $relationId, 'type' => $type]); - $this->persistenceHandler->contentHandler()->removeRelation($relationId, $type); - } - - /** - * {@inheritdoc} - */ - public function loadRelations($sourceContentId, $sourceContentVersionNo = null, $type = null) - { - $this->logger->logCall( - __METHOD__, - [ - 'content' => $sourceContentId, - 'version' => $sourceContentVersionNo, - 'type' => $type, - ] - ); - - return $this->persistenceHandler->contentHandler()->loadRelations($sourceContentId, $sourceContentVersionNo, $type); - } - - /** - * {@inheritdoc} - */ - public function countReverseRelations(int $destinationContentId, ?int $type = null): int - { - $this->logger->logCall(__METHOD__, ['content' => $destinationContentId, 'type' => $type]); - - return $this->persistenceHandler->contentHandler()->countReverseRelations($destinationContentId, $type); - } - - /** - * {@inheritdoc} - */ - public function loadReverseRelations($destinationContentId, $type = null) - { - $this->logger->logCall(__METHOD__, ['content' => $destinationContentId, 'type' => $type]); - - return $this->persistenceHandler->contentHandler()->loadReverseRelations($destinationContentId, $type); - } - - /** - * {@inheritdoc} - */ - public function loadReverseRelationList( - int $destinationContentId, - int $offset = 0, - int $limit = -1, - ?int $type = null - ): array { - $this->logger->logCall(__METHOD__, [ - 'content' => $destinationContentId, - 'offset' => $offset, - 'limit' => $limit, - 'type' => $type, - ]); - - return $this->persistenceHandler->contentHandler()->loadReverseRelationList( - $destinationContentId, - $offset, - $limit, - $type - ); - } - - /** - * {@inheritdoc} - */ - public function publish($contentId, $versionNo, MetadataUpdateStruct $struct) - { - $this->logger->logCall(__METHOD__, ['content' => $contentId, 'version' => $versionNo, 'struct' => $struct]); - $content = $this->persistenceHandler->contentHandler()->publish($contentId, $versionNo, $struct); - $this->cache->invalidateTags([ - $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]), - ]); - - return $content; - } - - /** - * {@inheritdoc} - */ - public function deleteTranslationFromContent($contentId, $languageCode) - { - $this->logger->logCall( - __METHOD__, - [ - 'contentId' => $contentId, - 'languageCode' => $languageCode, - ] - ); - - $this->persistenceHandler->contentHandler()->deleteTranslationFromContent($contentId, $languageCode); - $this->cache->invalidateTags([ - $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]), - ]); - } - - /** - * {@inheritdoc} - */ - public function deleteTranslationFromDraft($contentId, $versionNo, $languageCode) - { - $this->logger->logCall( - __METHOD__, - ['content' => $contentId, 'version' => $versionNo, 'languageCode' => $languageCode] - ); - $content = $this->persistenceHandler->contentHandler()->deleteTranslationFromDraft( - $contentId, - $versionNo, - $languageCode - ); - - $this->cache->invalidateTags([ - $this->cacheIdentifierGenerator->generateTag(self::CONTENT_VERSION_IDENTIFIER, [$contentId, $versionNo]), - ]); - - return $content; - } - - /** - * Return relevant content and location tags so cache can be purged reliably. - * - * For use when generating cache, not on invalidation. - * - * @param array $tags Optional, can be used to specify other tags. - */ - private function getCacheTagsForVersion(VersionInfo $versionInfo, array $tags = []): array - { - $contentInfo = $versionInfo->contentInfo; - $tags[] = $this->cacheIdentifierGenerator->generateTag( - self::CONTENT_VERSION_IDENTIFIER, - [$contentInfo->id, $versionInfo->versionNo] - ); - - $getContentInfoTagsFn = $this->getContentInfoTags; - - return $getContentInfoTagsFn($contentInfo, $tags); - } - - public function loadVersionInfoList(array $contentIds): array - { - return $this->getMultipleCacheValues( - $contentIds, - $this->cacheIdentifierGenerator->generateKey( - self::CONTENT_VERSION_INFO_IDENTIFIER, - [], - true - ) . '-', - function (array $cacheMissIds): array { - return $this->persistenceHandler->contentHandler()->loadVersionInfoList($cacheMissIds); - }, - function (VersionInfo $versionInfo): array { - return $this->getCacheTagsForVersion($versionInfo); - }, - function (VersionInfo $versionInfo) { - return [ - $this->cacheIdentifierGenerator->generateKey( - self::CONTENT_VERSION_INFO_IDENTIFIER, - [$versionInfo->contentInfo->id], - true - ), - ]; - }, - '', - ['content' => $contentIds] - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Handler.php b/eZ/Publish/Core/Persistence/Cache/Handler.php deleted file mode 100644 index e4aa4f7638..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Handler.php +++ /dev/null @@ -1,296 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Cache; - -use eZ\Publish\Core\Persistence\Cache\BookmarkHandler as CacheBookmarkHandler; -use eZ\Publish\Core\Persistence\Cache\ContentHandler as CacheContentHandler; -use eZ\Publish\Core\Persistence\Cache\ContentLanguageHandler as CacheContentLanguageHandler; -use eZ\Publish\Core\Persistence\Cache\ContentTypeHandler as CacheContentTypeHandler; -use eZ\Publish\Core\Persistence\Cache\LocationHandler as CacheLocationHandler; -use eZ\Publish\Core\Persistence\Cache\NotificationHandler as CacheNotificationHandler; -use eZ\Publish\Core\Persistence\Cache\ObjectStateHandler as CacheObjectStateHandler; -use eZ\Publish\Core\Persistence\Cache\SectionHandler as CacheSectionHandler; -use eZ\Publish\Core\Persistence\Cache\SettingHandler as SettingHandler; -use eZ\Publish\Core\Persistence\Cache\TransactionHandler as CacheTransactionHandler; -use eZ\Publish\Core\Persistence\Cache\TrashHandler as CacheTrashHandler; -use eZ\Publish\Core\Persistence\Cache\UrlAliasHandler as CacheUrlAliasHandler; -use eZ\Publish\Core\Persistence\Cache\URLHandler as CacheUrlHandler; -use eZ\Publish\Core\Persistence\Cache\UrlWildcardHandler as CacheUrlWildcardHandler; -use eZ\Publish\Core\Persistence\Cache\UserHandler as CacheUserHandler; -use eZ\Publish\Core\Persistence\Cache\UserPreferenceHandler as CacheUserPreferenceHandler; -use eZ\Publish\SPI\Persistence\Handler as PersistenceHandlerInterface; -use eZ\Publish\SPI\Persistence\Setting\Handler as SPISettingHandler; - -/** - * Persistence Cache Handler class. - */ -class Handler implements PersistenceHandlerInterface -{ - /** @var \eZ\Publish\SPI\Persistence\Handler */ - protected $persistenceHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\SectionHandler */ - protected $sectionHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\ContentHandler */ - protected $contentHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\ContentLanguageHandler */ - protected $contentLanguageHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\ContentTypeHandler */ - protected $contentTypeHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\LocationHandler */ - protected $locationHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\UserHandler */ - protected $userHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\TrashHandler */ - protected $trashHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\UrlAliasHandler */ - protected $urlAliasHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\ObjectStateHandler */ - protected $objectStateHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\TransactionHandler */ - protected $transactionHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\URLHandler */ - protected $urlHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\BookmarkHandler */ - protected $bookmarkHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\NotificationHandler */ - protected $notificationHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\UserPreferenceHandler */ - protected $userPreferenceHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\UrlWildcardHandler */ - private $urlWildcardHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\PersistenceLogger */ - protected $logger; - - /** @var \eZ\Publish\Core\Persistence\Cache\SettingHandler */ - private $settingHandler; - - /** - * @param \eZ\Publish\SPI\Persistence\Handler $persistenceHandler Must be factory for inner persistence, ie: legacy - * @param \eZ\Publish\Core\Persistence\Cache\SectionHandler $sectionHandler - * @param \eZ\Publish\Core\Persistence\Cache\LocationHandler $locationHandler - * @param \eZ\Publish\Core\Persistence\Cache\ContentHandler $contentHandler - * @param \eZ\Publish\Core\Persistence\Cache\ContentLanguageHandler $contentLanguageHandler - * @param \eZ\Publish\Core\Persistence\Cache\ContentTypeHandler $contentTypeHandler - * @param \eZ\Publish\Core\Persistence\Cache\UserHandler $userHandler - * @param \eZ\Publish\Core\Persistence\Cache\TransactionHandler $transactionHandler - * @param \eZ\Publish\Core\Persistence\Cache\TrashHandler $trashHandler - * @param \eZ\Publish\Core\Persistence\Cache\UrlAliasHandler $urlAliasHandler - * @param \eZ\Publish\Core\Persistence\Cache\ObjectStateHandler $objectStateHandler - * @param \eZ\Publish\Core\Persistence\Cache\URLHandler $urlHandler - * @param \eZ\Publish\Core\Persistence\Cache\BookmarkHandler $bookmarkHandler - * @param \eZ\Publish\Core\Persistence\Cache\NotificationHandler $notificationHandler - * @param \eZ\Publish\Core\Persistence\Cache\UserPreferenceHandler $userPreferenceHandler - * @param \eZ\Publish\Core\Persistence\Cache\UrlWildcardHandler $urlWildcardHandler - * @param \eZ\Publish\Core\Persistence\Cache\PersistenceLogger $logger - */ - public function __construct( - PersistenceHandlerInterface $persistenceHandler, - CacheSectionHandler $sectionHandler, - CacheLocationHandler $locationHandler, - CacheContentHandler $contentHandler, - CacheContentLanguageHandler $contentLanguageHandler, - CacheContentTypeHandler $contentTypeHandler, - CacheUserHandler $userHandler, - CacheTransactionHandler $transactionHandler, - CacheTrashHandler $trashHandler, - CacheUrlAliasHandler $urlAliasHandler, - CacheObjectStateHandler $objectStateHandler, - CacheUrlHandler $urlHandler, - CacheBookmarkHandler $bookmarkHandler, - CacheNotificationHandler $notificationHandler, - CacheUserPreferenceHandler $userPreferenceHandler, - CacheUrlWildcardHandler $urlWildcardHandler, - SettingHandler $settingHandler, - PersistenceLogger $logger - ) { - $this->persistenceHandler = $persistenceHandler; - $this->sectionHandler = $sectionHandler; - $this->locationHandler = $locationHandler; - $this->contentHandler = $contentHandler; - $this->contentLanguageHandler = $contentLanguageHandler; - $this->contentTypeHandler = $contentTypeHandler; - $this->userHandler = $userHandler; - $this->transactionHandler = $transactionHandler; - $this->trashHandler = $trashHandler; - $this->urlAliasHandler = $urlAliasHandler; - $this->objectStateHandler = $objectStateHandler; - $this->urlHandler = $urlHandler; - $this->bookmarkHandler = $bookmarkHandler; - $this->notificationHandler = $notificationHandler; - $this->userPreferenceHandler = $userPreferenceHandler; - $this->urlWildcardHandler = $urlWildcardHandler; - $this->settingHandler = $settingHandler; - $this->logger = $logger; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Handler - */ - public function contentHandler() - { - return $this->contentHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Type\Handler - */ - public function contentTypeHandler() - { - return $this->contentTypeHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Language\Handler - */ - public function contentLanguageHandler() - { - return $this->contentLanguageHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Location\Handler - */ - public function locationHandler() - { - return $this->locationHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Handler - */ - public function objectStateHandler() - { - return $this->objectStateHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\User\Handler - */ - public function userHandler() - { - return $this->userHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Section\Handler - */ - public function sectionHandler() - { - return $this->sectionHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Location\Trash\Handler - */ - public function trashHandler() - { - return $this->trashHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler - */ - public function urlAliasHandler() - { - return $this->urlAliasHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler - */ - public function urlWildcardHandler() - { - return $this->urlWildcardHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\TransactionHandler - */ - public function transactionHandler() - { - return $this->transactionHandler; - } - - public function settingHandler(): SPISettingHandler - { - return $this->settingHandler; - } - - /** - * @return \eZ\Publish\Core\Persistence\Cache\URLHandler - */ - public function urlHandler() - { - return $this->urlHandler; - } - - /** - * @return \eZ\Publish\Core\Persistence\Cache\BookmarkHandler - */ - public function bookmarkHandler() - { - return $this->bookmarkHandler; - } - - /** - * @return \eZ\Publish\Core\Persistence\Cache\NotificationHandler - */ - public function notificationHandler() - { - return $this->notificationHandler; - } - - /** - * @return \eZ\Publish\Core\Persistence\Cache\UserPreferenceHandler - */ - public function userPreferenceHandler() - { - return $this->userPreferenceHandler; - } - - /** - * {@inheritdoc} - */ - public function beginTransaction() - { - $this->transactionHandler->beginTransaction(); - } - - /** - * {@inheritdoc} - */ - public function commit() - { - $this->transactionHandler->commit(); - } - - /** - * {@inheritdoc} - */ - public function rollback() - { - $this->transactionHandler->rollback(); - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/ObjectStateHandler.php b/eZ/Publish/Core/Persistence/Cache/ObjectStateHandler.php deleted file mode 100644 index 97bdd1adee..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/ObjectStateHandler.php +++ /dev/null @@ -1,373 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache; - -use eZ\Publish\SPI\Persistence\Content\ObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Handler as ObjectStateHandlerInterface; -use eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct; - -/** - * @see \eZ\Publish\SPI\Persistence\Content\ObjectState\Handler - */ -class ObjectStateHandler extends AbstractInMemoryPersistenceHandler implements ObjectStateHandlerInterface -{ - private const STATE_GROUP_ALL_IDENTIFIER = 'state_group_all'; - private const STATE_GROUP_IDENTIFIER = 'state_group'; - private const STATE_GROUP_WITH_ID_SUFFIX_IDENTIFIER = 'state_group_with_id_suffix'; - private const BY_IDENTIFIER_SUFFIX = 'by_identifier_suffix'; - private const STATE_IDENTIFIER = 'state'; - private const STATE_LIST_BY_GROUP_IDENTIFIER = 'state_list_by_group'; - private const STATE_ID_IDENTIFIER = 'state_identifier'; - private const STATE_ID_IDENTIFIER_WITH_BY_GROUP_SUFFIX_IDENTIFIER = 'state_identifier_with_by_group_suffix'; - private const BY_GROUP_IDENTIFIER = 'by_group'; - private const STATE_BY_GROUP_ON_CONTENT_IDENTIFIER = 'state_by_group_on_content'; - private const STATE_BY_GROUP_IDENTIFIER = 'state_by_group'; - private const CONTENT_IDENTIFIER = 'content'; - private const ON_CONTENT_IDENTIFIER = 'on_content'; - - /** - * {@inheritdoc} - */ - public function createGroup(InputStruct $input) - { - $this->logger->logCall(__METHOD__, ['struct' => $input]); - $group = $this->persistenceHandler->objectStateHandler()->createGroup($input); - - $this->cache->deleteItem( - $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_ALL_IDENTIFIER, [], true) - ); - - return $group; - } - - /** - * {@inheritdoc} - */ - public function loadGroup($groupId) - { - return $this->getCacheValue( - (int) $groupId, - $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_IDENTIFIER, [], true) . '-', - function (int $groupId): Group { - $this->logger->logCall(__METHOD__, ['groupId' => (int) $groupId]); - - return $this->persistenceHandler->objectStateHandler()->loadGroup($groupId); - }, - function () use ($groupId): array { - return [ - $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [(int) $groupId]), - ]; - }, - function () use ($groupId) { - return [ - $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_IDENTIFIER, [(int) $groupId], true), - ]; - } - ); - } - - /** - * {@inheritdoc} - */ - public function loadGroupByIdentifier($identifier) - { - $escapedIdentifier = $this->cacheIdentifierSanitizer->escapeForCacheKey($identifier); - - return $this->getCacheValue( - $identifier, - $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_IDENTIFIER, [], true) . '-', - function (string $identifier): Group { - $this->logger->logCall(__METHOD__, ['groupId' => $identifier]); - - return $this->persistenceHandler->objectStateHandler()->loadGroupByIdentifier($identifier); - }, - function (Group $group): array { - return [ - $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$group->id]), - ]; - }, - function (Group $group) use ($escapedIdentifier): array { - return [ - $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_WITH_ID_SUFFIX_IDENTIFIER, [$escapedIdentifier], true), - $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_IDENTIFIER, [$group->id], true), - ]; - }, - $this->cacheIdentifierGenerator->generateKey(self::BY_IDENTIFIER_SUFFIX) - ); - } - - /** - * {@inheritdoc} - */ - public function loadAllGroups($offset = 0, $limit = -1) - { - $stateGroups = $this->getListCacheValue( - $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_ALL_IDENTIFIER, [], true), - function () use ($offset, $limit): array { - $this->logger->logCall(__METHOD__, ['offset' => (int) $offset, 'limit' => (int) $limit]); - - return $this->persistenceHandler->objectStateHandler()->loadAllGroups(0, -1); - }, - function (Group $group): array { - return [ - $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$group->id]), - ]; - }, - function (Group $group): array { - return [ - $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_IDENTIFIER, [$group->id], true), - $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_WITH_ID_SUFFIX_IDENTIFIER, [$group->id], true), - ]; - } - ); - - return \array_slice($stateGroups, $offset, $limit > -1 ? $limit : null); - } - - /** - * {@inheritdoc} - */ - public function loadObjectStates($groupId) - { - return $this->getCacheValue( - $groupId, - $this->cacheIdentifierGenerator->generateKey(self::STATE_LIST_BY_GROUP_IDENTIFIER, [], true) . '-', - function (int $groupId): array { - $this->logger->logCall(__METHOD__, ['groupId' => (int) $groupId]); - - return $this->persistenceHandler->objectStateHandler()->loadObjectStates($groupId); - }, - function (array $objectStates) use ($groupId): array { - $cacheTags = []; - $cacheTags[] = $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$groupId]); - foreach ($objectStates as $state) { - $cacheTags[] = $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$state->id]); - } - - return $cacheTags; - }, - function () use ($groupId): array { - return [ - $this->cacheIdentifierGenerator->generateKey(self::STATE_LIST_BY_GROUP_IDENTIFIER, [$groupId], true), - ]; - } - ); - } - - /** - * {@inheritdoc} - */ - public function updateGroup($groupId, InputStruct $input) - { - $this->logger->logCall(__METHOD__, ['groupId' => $groupId, 'struct' => $input]); - $return = $this->persistenceHandler->objectStateHandler()->updateGroup($groupId, $input); - - $this->cache->invalidateTags([ - $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$groupId]), - ]); - - return $return; - } - - /** - * {@inheritdoc} - */ - public function deleteGroup($groupId) - { - $this->logger->logCall(__METHOD__, ['groupId' => $groupId]); - $return = $this->persistenceHandler->objectStateHandler()->deleteGroup($groupId); - - $this->cache->invalidateTags([ - $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$groupId]), - ]); - - return $return; - } - - /** - * {@inheritdoc} - */ - public function create($groupId, InputStruct $input) - { - $this->logger->logCall(__METHOD__, ['groupId' => $groupId, 'struct' => $input]); - $return = $this->persistenceHandler->objectStateHandler()->create($groupId, $input); - - $this->cache->deleteItem( - $this->cacheIdentifierGenerator->generateKey(self::STATE_LIST_BY_GROUP_IDENTIFIER, [$groupId], true) - ); - - return $return; - } - - /** - * {@inheritdoc} - */ - public function load($stateId) - { - return $this->getCacheValue( - (int) $stateId, - $this->cacheIdentifierGenerator->generateKey(self::STATE_IDENTIFIER, [], true) . '-', - function (int $stateId): ObjectState { - $this->logger->logCall(__METHOD__, ['stateId' => $stateId]); - - return $this->persistenceHandler->objectStateHandler()->load($stateId); - }, - function (ObjectState $objectState): array { - return [ - $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$objectState->id]), - $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$objectState->groupId]), - ]; - }, - function () use ($stateId): array { - return [ - $this->cacheIdentifierGenerator->generateKey(self::STATE_IDENTIFIER, [$stateId], true), - ]; - } - ); - } - - /** - * {@inheritdoc} - */ - public function loadByIdentifier($identifier, $groupId) - { - $escapedIdentifier = $this->cacheIdentifierSanitizer->escapeForCacheKey($identifier); - - return $this->getCacheValue( - $identifier, - $this->cacheIdentifierGenerator->generateKey(self::STATE_ID_IDENTIFIER, [], true) . '-', - function (string $identifier) use ($groupId): ObjectState { - $this->logger->logCall(__METHOD__, ['identifier' => $identifier, 'groupId' => (int) $groupId]); - - return $this->persistenceHandler->objectStateHandler()->loadByIdentifier($identifier, (int) $groupId); - }, - function (ObjectState $objectState): array { - return [ - $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$objectState->id]), - $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$objectState->groupId]), - ]; - }, - function () use ($escapedIdentifier, $groupId): array { - return [ - $this->cacheIdentifierGenerator->generateKey( - self::STATE_ID_IDENTIFIER_WITH_BY_GROUP_SUFFIX_IDENTIFIER, - [$escapedIdentifier, $groupId], - true - ), - ]; - }, - '-' . $this->cacheIdentifierGenerator->generateKey(self::BY_GROUP_IDENTIFIER, [$groupId]) - ); - } - - /** - * {@inheritdoc} - */ - public function update($stateId, InputStruct $input) - { - $this->logger->logCall(__METHOD__, ['stateId' => $stateId, 'struct' => $input]); - $return = $this->persistenceHandler->objectStateHandler()->update($stateId, $input); - - $this->cache->invalidateTags([ - $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$stateId]), - ]); - - return $return; - } - - /** - * {@inheritdoc} - */ - public function setPriority($stateId, $priority) - { - $this->logger->logCall(__METHOD__, ['stateId' => $stateId, 'priority' => $priority]); - $return = $this->persistenceHandler->objectStateHandler()->setPriority($stateId, $priority); - - $this->cache->invalidateTags([ - $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$stateId]), - ]); - - return $return; - } - - /** - * {@inheritdoc} - */ - public function delete($stateId) - { - $this->logger->logCall(__METHOD__, ['stateId' => $stateId]); - $return = $this->persistenceHandler->objectStateHandler()->delete($stateId); - - $this->cache->invalidateTags([ - $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$stateId]), - ]); - - return $return; - } - - /** - * {@inheritdoc} - */ - public function setContentState($contentId, $groupId, $stateId) - { - $this->logger->logCall(__METHOD__, ['contentId' => $contentId, 'groupId' => $groupId, 'stateId' => $stateId]); - $return = $this->persistenceHandler->objectStateHandler()->setContentState($contentId, $groupId, $stateId); - - $this->cache->deleteItem( - $this->cacheIdentifierGenerator->generateKey( - self::STATE_BY_GROUP_ON_CONTENT_IDENTIFIER, - [$groupId, $contentId], - true - ) - ); - - return $return; - } - - /** - * {@inheritdoc} - */ - public function getContentState($contentId, $stateGroupId) - { - return $this->getCacheValue( - (int) $stateGroupId, - $this->cacheIdentifierGenerator->generateKey(self::STATE_BY_GROUP_IDENTIFIER, [], true) . '-', - function (int $stateGroupId) use ($contentId): ObjectState { - $this->logger->logCall(__METHOD__, ['contentId' => (int) $contentId, 'stateGroupId' => $stateGroupId]); - - return $this->persistenceHandler->objectStateHandler()->getContentState((int) $contentId, $stateGroupId); - }, - function (ObjectState $contentState) use ($contentId): array { - return [ - $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$contentState->id]), - $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]), - ]; - }, - function () use ($contentId, $stateGroupId): array { - return [ - $this->cacheIdentifierGenerator->generateKey( - self::STATE_BY_GROUP_ON_CONTENT_IDENTIFIER, - [$stateGroupId, $contentId], - true - ), - ]; - }, - '-' . $this->cacheIdentifierGenerator->generateKey(self::ON_CONTENT_IDENTIFIER, [$contentId]) - ); - } - - /** - * {@inheritdoc} - */ - public function getContentCount($stateId) - { - $this->logger->logCall(__METHOD__, ['stateId' => $stateId]); - - return $this->persistenceHandler->objectStateHandler()->getContentCount($stateId); - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Readme.md b/eZ/Publish/Core/Persistence/Cache/Readme.md deleted file mode 100644 index 49bd7842f5..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Readme.md +++ /dev/null @@ -1,72 +0,0 @@ -# CORE DEV DOC: Persistence\Cache - -SPI Persistence Cache is a layer aiming to cache calls to Persistence (backend database) deemed costly under load. It -tries to balance this need up against the complexity and additional [system overhead](#guidelines-for-core-development) imposed by caching. - -## Design - -Handlers using `AbstractHandler` internally consist of one cache layer: - -- "Shared cache": A Symfony Cache based cache pool, supporting a range of cache adapters like filesystem, Redis, Memcached. - Note: Due to being shared by design, clusters need remote cache, thus multi lookups are strongly advised to reduce round trip latency. - -Handlers using `AbstractInMemoryHandler` / `AbstractInMemoryPersistenceHandler` in addition add a second cache layer in front of "Shared cache": - -- "InMemory cache": A burst cache in PHP aiming at covering the most heavily used parts of the Content model to reduce repeated lookups to remote cache system. - Note: It's not shared but per request/process. To keep risk of race condition negligible, it has own milliseconds ttl & item limits. - -There are abstract test classes for the respective abstract classes above, these are opinionated and enforce conventions to: -- Avoid too much logic in cache logic _(e.g. warm-up logic)_, which can be a source of bugs. -- Avoids having to write error-prone test cases for every method. - -_This ensures the cache layer is far less complex to maintain and evolve than what was the case in 1.x._ - - -### Tags - -List of content tags that can be somewhat safely "semi-officially" used to clear related entities in cache: -- `c-<id>`: Cache tag which refers to Content/ContentInfo entity. -- `l-<id>`: Cache tag which refers to Locations and/or their assigned Content/ContentInfo entities. -- `lp-<id>`: Like the tag above but applied to all Content/Locations in the subtree of this ID, so it can be used by tree operations. -- `cft-<type-id>`: Cache tag which refers to entries containing Field data. It's used on Content Type changes that affect all Content items of the type. - -_For further tags used for other internal use cases, see the *Handlers for how they are used._ - - -## Guidelines for core development - -### Shared Cache: When to use, when not to use - -It's worth noting that shared cache comes at a cost: -- Increased complexity -- Latency per round trip -- Memory use - -Because of that, *typically* avoid introducing cache if: -- Lookup is per user => _it will consume a lot of memory and have very low hit ratio_ -- For drafts => _usually belongs to a single user and is short-lived_ -- Infrequently used lookups -- Lookups that are fast against DB even under load _(see also notes in "Possible future plans")_ - - -### Tags: When to use, when not to use - -Like cache, tags also comes at a cost: -- Slower invalidation -- Memory cost - - _E.g.: ATM on RedisTagAwareAdapter tag relation data is even non-expiring as it needs to guarantee surviving cache._ - -For those reasons, only introduce use a tag: -- Mainly to represent an entity _(e.g. `c-<id>`)_ -- Only if it's represented on many different cache keys or if a key can have a lot of different variants. - - _Tip: Otherwise prefer to delete by cache key(s) when cache clear is needed, it will be faster and consume less memory._ - -### Possible future considerations - -Ideally and for best effect, the cache should be in place to cache results of complex calculations based on multiple backend -lookups. Caching lightweight SPI lookups sometimes might not provide benefits, while still consume Redis/Memcached memory. - -This is why it's been discussed to move some of the caching to API instead in a future major release. -However, that would require that permissions are split into their own Repo layer => In order to not end -up having to cache per user => Which would result in waste of memory and low cache hit ratio. -It would also need to deal with serialization of API value objects, which are more complex (lazy properties, xmldocument, ...). diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/AbstractBaseHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/AbstractBaseHandlerTest.php deleted file mode 100644 index b4ef05e931..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/AbstractBaseHandlerTest.php +++ /dev/null @@ -1,189 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter; -use eZ\Publish\Core\Persistence\Cache\BookmarkHandler as CacheBookmarkHandler; -use eZ\Publish\Core\Persistence\Cache\ContentHandler as CacheContentHandler; -use eZ\Publish\Core\Persistence\Cache\ContentLanguageHandler as CacheContentLanguageHandler; -use eZ\Publish\Core\Persistence\Cache\ContentTypeHandler as CacheContentTypeHandler; -use eZ\Publish\Core\Persistence\Cache\Handler as CacheHandler; -use eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache; -use eZ\Publish\Core\Persistence\Cache\LocationHandler as CacheLocationHandler; -use eZ\Publish\Core\Persistence\Cache\NotificationHandler as CacheNotificationHandler; -use eZ\Publish\Core\Persistence\Cache\ObjectStateHandler as CacheObjectStateHandler; -use eZ\Publish\Core\Persistence\Cache\PersistenceLogger; -use eZ\Publish\Core\Persistence\Cache\SectionHandler as CacheSectionHandler; -use eZ\Publish\Core\Persistence\Cache\SettingHandler as CacheSettingHandler; -use eZ\Publish\Core\Persistence\Cache\TransactionHandler as CacheTransactionHandler; -use eZ\Publish\Core\Persistence\Cache\TrashHandler as CacheTrashHandler; -use eZ\Publish\Core\Persistence\Cache\UrlAliasHandler as CacheUrlAliasHandler; -use eZ\Publish\Core\Persistence\Cache\URLHandler as CacheUrlHandler; -use eZ\Publish\Core\Persistence\Cache\UrlWildcardHandler as CacheUrlWildcardHandler; -use eZ\Publish\Core\Persistence\Cache\UserHandler as CacheUserHandler; -use eZ\Publish\Core\Persistence\Cache\UserPreferenceHandler as CacheUserPreferenceHandler; -use eZ\Publish\SPI\Persistence\Handler; -use Ibexa\Core\Persistence\Cache\CacheIndicesValidatorInterface; -use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface; -use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierSanitizer; -use Ibexa\Core\Persistence\Cache\LocationPathConverter; -use PHPUnit\Framework\TestCase; -use Symfony\Component\Cache\CacheItem; - -/** - * Abstract test case for spi cache impl. - */ -abstract class AbstractBaseHandlerTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter|\PHPUnit\Framework\MockObject\MockObject */ - protected $cacheMock; - - /** @var \eZ\Publish\SPI\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject */ - protected $persistenceHandlerMock; - - /** @var \eZ\Publish\Core\Persistence\Cache\Handler */ - protected $persistenceCacheHandler; - - /** @var \eZ\Publish\Core\Persistence\Cache\PersistenceLogger|\PHPUnit\Framework\MockObject\MockObject */ - protected $loggerMock; - - /** @var \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache|\PHPUnit\Framework\MockObject\MockObject */ - protected $inMemoryMock; - - /** @var \Closure */ - protected $cacheItemsClosure; - - /** @var \Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface|\PHPUnit\Framework\MockObject\MockObject */ - protected $cacheIdentifierGeneratorMock; - - /** @var \Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierSanitizer */ - protected $cacheIdentifierSanitizer; - - /** @var \Ibexa\Core\Persistence\Cache\LocationPathConverter */ - protected $locationPathConverter; - - /** @var \Ibexa\Core\Persistence\Cache\CacheIndicesValidatorInterface */ - protected $cacheIndicesValidator; - - /** - * Setup the HandlerTest. - */ - protected function setUp(): void - { - parent::setUp(); - - $this->persistenceHandlerMock = $this->createMock(Handler::class); - $this->cacheMock = $this->createMock(TransactionalInMemoryCacheAdapter::class); - $this->loggerMock = $this->createMock(PersistenceLogger::class); - $this->inMemoryMock = $this->createMock(InMemoryCache::class); - $this->cacheIdentifierGeneratorMock = $this->createMock(CacheIdentifierGeneratorInterface::class); - $this->cacheIdentifierSanitizer = new CacheIdentifierSanitizer(); - $this->locationPathConverter = new LocationPathConverter(); - $this->cacheIndicesValidator = $this->createMock(CacheIndicesValidatorInterface::class); - - $cacheAbstractHandlerArguments = $this->provideAbstractCacheHandlerArguments(); - $cacheInMemoryHandlerArguments = $this->provideInMemoryCacheHandlerArguments(); - - $this->persistenceCacheHandler = new CacheHandler( - $this->persistenceHandlerMock, - new CacheSectionHandler(...$cacheAbstractHandlerArguments), - new CacheLocationHandler(...$cacheInMemoryHandlerArguments), - new CacheContentHandler(...$cacheInMemoryHandlerArguments), - new CacheContentLanguageHandler(...$cacheInMemoryHandlerArguments), - new CacheContentTypeHandler(...$cacheInMemoryHandlerArguments), - new CacheUserHandler(...$cacheInMemoryHandlerArguments), - new CacheTransactionHandler(...$cacheInMemoryHandlerArguments), - new CacheTrashHandler(...$cacheAbstractHandlerArguments), - new CacheUrlAliasHandler(...$cacheInMemoryHandlerArguments), - new CacheObjectStateHandler(...$cacheInMemoryHandlerArguments), - new CacheUrlHandler(...$cacheAbstractHandlerArguments), - new CacheBookmarkHandler(...$cacheAbstractHandlerArguments), - new CacheNotificationHandler(...$cacheAbstractHandlerArguments), - new CacheUserPreferenceHandler(...$cacheInMemoryHandlerArguments), - new CacheUrlWildcardHandler(...$cacheAbstractHandlerArguments), - new CacheSettingHandler(...$cacheInMemoryHandlerArguments), - $this->loggerMock - ); - - $this->cacheItemsClosure = \Closure::bind( - static function ($key, $value, $isHit, $defaultLifetime = 0) { - $item = new CacheItem(); - $item->key = $key; - $item->value = $value; - $item->isHit = $isHit; - $item->defaultLifetime = $defaultLifetime; - $item->isTaggable = true; - - return $item; - }, - null, - CacheItem::class - ); - } - - /** - * Tear down test (properties). - */ - protected function tearDown(): void - { - unset( - $this->cacheMock, - $this->persistenceHandlerMock, - $this->persistenceCacheHandler, - $this->loggerMock, - $this->cacheItemsClosure, - $this->inMemoryMock, - $this->cacheIdentifierGeneratorMock, - $this->cacheIdentifierSanitizer, - $this->locationPathConverter - ); - - parent::tearDown(); - } - - /** - * @param $key - * @param null $value If null the cache item will be assumed to be a cache miss here. - * @param int $defaultLifetime - * - * @return \Symfony\Component\Cache\CacheItem - */ - final protected function getCacheItem($key, $value = null, $defaultLifetime = 0) - { - $cacheItemsClosure = $this->cacheItemsClosure; - - return $cacheItemsClosure($key, $value, (bool)$value, $defaultLifetime); - } - - private function provideAbstractCacheHandlerArguments(): array - { - return [ - $this->cacheMock, - $this->persistenceHandlerMock, - $this->loggerMock, - $this->cacheIdentifierGeneratorMock, - $this->cacheIdentifierSanitizer, - $this->locationPathConverter, - ]; - } - - private function provideInMemoryCacheHandlerArguments(): array - { - return [ - $this->cacheMock, - $this->loggerMock, - $this->inMemoryMock, - $this->persistenceHandlerMock, - $this->cacheIdentifierGeneratorMock, - $this->cacheIdentifierSanitizer, - $this->locationPathConverter, - $this->cacheIndicesValidator, - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/ContentHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/ContentHandlerTest.php deleted file mode 100644 index fb93c6ac15..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/ContentHandlerTest.php +++ /dev/null @@ -1,373 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\API\Repository\Values\Content\Relation as APIRelation; -use eZ\Publish\Core\Persistence\Cache\ContentHandler; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Handler as SPIContentHandler; -use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Relation as SPIRelation; -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\UpdateStruct; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Test case for Persistence\Cache\ContentHandler. - */ -class ContentHandlerTest extends AbstractInMemoryCacheHandlerTest -{ - public function getHandlerMethodName(): string - { - return 'contentHandler'; - } - - public function getHandlerClassName(): string - { - return SPIContentHandler::class; - } - - /** - * @return array - */ - public function providerForUnCachedMethods(): array - { - // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue - return [ - ['create', [new CreateStruct()]], - ['createDraftFromVersion', [2, 1, 14], null, [['content_version_list', [2], true]], [], ['ibx-c-2-vl']], - ['copy', [2, 1]], - ['loadDraftsForUser', [14]], - ['setStatus', [2, 0, 1], [['content_version', [2, 1], false]], null, ['c-2-v-1']], - ['setStatus', [2, 1, 1], [['content', [2], false]], null, ['c-2']], - ['updateMetadata', [2, new MetadataUpdateStruct()], [['content', [2], false]], null, ['c-2']], - ['updateContent', [2, 1, new UpdateStruct()], [['content_version', [2, 1], false]], null, ['c-2-v-1']], - //['deleteContent', [2]], own tests for relations complexity - ['deleteVersion', [2, 1], [['content_version', [2, 1], false]], null, ['c-2-v-1']], - ['addRelation', [new RelationCreateStruct()]], - ['removeRelation', [66, APIRelation::COMMON]], - ['loadRelations', [2, 1, 3]], - ['loadReverseRelations', [2, 3]], - ['publish', [2, 3, new MetadataUpdateStruct()], [['content', [2], false]], null, ['c-2']], - [ - 'listVersions', - [2, 1], - [['content', [2], false]], - [['content_version_list', [2], true]], - [], - [], - [ - new VersionInfo([ - 'versionNo' => 1, - 'contentInfo' => new ContentInfo([ - 'id' => 2, - ]), - ]), - ], - ], - ]; - } - - /** - * @return array - */ - public function providerForCachedLoadMethodsHit(): array - { - $info = new ContentInfo(['id' => 2]); - $version = new VersionInfo(['versionNo' => 1, 'contentInfo' => $info]); - $content = new Content(['fields' => [], 'versionInfo' => $version]); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi = false, array $additionalCalls - return [ - ['load', [2, 1], 'ibx-c-2-1-' . ContentHandler::ALL_TRANSLATIONS_KEY, null, null, [['content', [], true]], ['ibx-c'], $content], - ['load', [2, 1, ['eng-GB', 'eng-US']], 'ibx-c-2-1-eng-GB|eng-US', null, null, [['content', [], true]], ['ibx-c'], $content], - ['load', [2], 'ibx-c-2-' . ContentHandler::ALL_TRANSLATIONS_KEY, null, null, [['content', [], true]], ['ibx-c'], $content], - ['load', [2, null, ['eng-GB', 'eng-US']], 'ibx-c-2-eng-GB|eng-US', null, null, [['content', [], true]], ['ibx-c'], $content], - ['loadContentList', [[2]], 'ibx-c-2-' . ContentHandler::ALL_TRANSLATIONS_KEY, null, null, [['content', [], true]], ['ibx-c'], [2 => $content], true], - ['loadContentList', [[5], ['eng-GB', 'eng-US']], 'ibx-c-5-eng-GB|eng-US', null, null, [['content', [], true]], ['ibx-c'], [5 => $content], true], - ['loadContentInfo', [2], 'ibx-ci-2', null, null, [['content_info', [], true]], ['ibx-ci'], $info], - ['loadContentInfoList', [[2]], 'ibx-ci-2', null, null, [['content_info', [], true]], ['ibx-ci'], [2 => $info], true], - ['loadContentInfoByRemoteId', ['3d8jrj'], 'ibx-cibri-3d8jrj', null, null, [['content_info_by_remote_id', [], true]], ['ibx-cibri'], $info], - ['loadVersionInfo', [2, 1], 'ibx-cvi-2-1', null, null, [['content_version_info', [2], true]], ['ibx-cvi-2'], $version], - ['loadVersionInfo', [2], 'ibx-cvi-2', null, null, [['content_version_info', [2], true]], ['ibx-cvi-2'], $version], - ['listVersions', [2], 'ibx-c-2-vl', null, null, [['content_version_list', [2], true]], ['ibx-c-2-vl'], [$version]], - ['loadVersionInfoList', [[2]], 'ibx-cvi-2', null, null, [['content_version_info', [], true]], ['ibx-cvi'], [2 => $version], true], - ]; - } - - /** - * @return array - */ - public function providerForCachedLoadMethodsMiss(): array - { - $info = new ContentInfo([ - 'id' => 2, - 'contentTypeId' => 3, - ]); - $version = new VersionInfo(['versionNo' => 1, 'contentInfo' => $info]); - $content = new Content(['fields' => [], 'versionInfo' => $version]); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi = false, array $additionalCalls - return [ - [ - 'load', - [2, 1], - 'ibx-c-2-1-' . ContentHandler::ALL_TRANSLATIONS_KEY, - [ - ['content_fields_type', [3], false], - ['content_version', [2, 1], false], - ['content', [2], false], - ], - ['cft-3', 'c-2-v-1', 'c-2'], - [ - ['content', [], true], - ], - ['ibx-c'], - $content, - ], - [ - 'load', - [2, 1, ['eng-GB', 'eng-US']], - 'ibx-c-2-1-eng-GB|eng-US', - [ - ['content_fields_type', [3], false], - ['content_version', [2, 1], false], - ['content', [2], false], - ], - ['cft-2', 'c-2-v-1', 'c-2'], - [ - ['content', [], true], - ], - ['ibx-c'], - $content, - ], - [ - 'load', - [2], - 'ibx-c-2-' . ContentHandler::ALL_TRANSLATIONS_KEY, - [ - ['content_fields_type', [3], false], - ['content_version', [2, 1], false], - ['content', [2], false], - ], - ['cft-2', 'c-2-v-1', 'c-2'], - [ - ['content', [], true], - ], - ['ibx-c'], - $content, - ], - [ - 'load', - [2, null, ['eng-GB', 'eng-US']], - 'ibx-c-2-eng-GB|eng-US', - [ - ['content_fields_type', [3], false], - ['content_version', [2, 1], false], - ['content', [2], false], - ], - ['cft-2', 'c-2-v-1', 'c-2'], - [ - ['content', [], true], - ], - ['ibx-c'], - $content, - ], - [ - 'loadContentList', - [[2]], - 'ibx-c-2-' . ContentHandler::ALL_TRANSLATIONS_KEY, - [ - ['content_fields_type', [3], false], - ['content_version', [2, 1], false], - ['content', [2], false], - ], - ['cft-2', 'c-2-v-1', 'c-2'], - [ - ['content', [], true], - ], - ['ibx-c'], - [2 => $content], - true, - ], - [ - 'loadContentList', - [[5], ['eng-GB', 'eng-US']], - 'ibx-c-5-eng-GB|eng-US', - [ - ['content_fields_type', [3], false], - ['content_version', [2, 1], false], - ['content', [2], false], - ], - ['cft-2', 'c-2-v-1', 'c-2'], - [ - ['content', [], true], - ], - ['ibx-c'], - [5 => $content], - true, - ], - [ - 'loadContentInfo', - [2], - 'ibx-ci-2', - [ - ['content', [2], false], - ], - ['c-2'], - [ - ['content_info', [], true], - ], - ['ibx-ci'], - $info, - ], - [ - 'loadContentInfoList', - [[2]], - 'ibx-ci-2', - [ - ['content', [2], false], - ], - ['c-2'], - [ - ['content_info', [], true], - ], - ['ibx-ci'], - [2 => $info], - true, - ], - [ - 'loadContentInfoByRemoteId', - ['3d8jrj'], 'ibx-cibri-3d8jrj', - [ - ['content', [2], false], - ], - ['c-2'], - [ - ['content_info_by_remote_id', [], true], - ], - ['ibx-cibri'], - $info, - ], - [ - 'loadVersionInfo', - [2, 1], - 'ibx-cvi-2-1', - [ - ['content_version', [2, 1], false], - ['content', [2], false], - ], - ['c-2-v-1', 'c-2'], - [ - ['content_version_info', [2], true], - ], - ['ibx-cvi-2'], - $version, - ], - [ - 'loadVersionInfo', - [2], - 'ibx-cvi-2', - [ - ['content_version', [2, 1], false], - ['content', [2], false], - ], - ['c-2-v-1', 'c-2'], - [ - ['content_version_info', [2], true], - ], - ['ibx-cvi-2'], - $version, - ], - [ - 'listVersions', - [2], - 'ibx-c-2-vl', - [ - ['content', [2], false], - ['content_version', [2, 1], false], - ['content', [2], false], - ], - ['c-2', 'c-2-v-1', 'c-2'], - [ - ['content_version_list', [2], true], - ], - ['ibx-c-2-vl'], - [$version], - ], - [ - 'loadVersionInfoList', - [[2]], - 'ibx-cvi-2', - [ - ['content_version', [2, 1], false], - ['content', [2], false], - ], - ['c-2-v-1', 'c-2'], - [ - ['content_version_info', [], true], - ], - ['ibx-cvi'], - [2 => $version], - true, - ], - ]; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Cache\ContentHandler::deleteContent - */ - public function testDeleteContent() - { - $this->loggerMock->expects($this->once())->method('logCall'); - - $innerHandlerMock = $this->createMock(SPIContentHandler::class); - $this->persistenceHandlerMock - ->expects($this->exactly(2)) - ->method('contentHandler') - ->willReturn($innerHandlerMock); - - $innerHandlerMock - ->expects($this->once()) - ->method('loadReverseRelations') - ->with(2, APIRelation::FIELD | APIRelation::ASSET) - ->willReturn( - [ - new SPIRelation(['sourceContentId' => 42]), - ] - ); - - $innerHandlerMock - ->expects($this->once()) - ->method('deleteContent') - ->with(2) - ->willReturn(true); - - $this->cacheMock - ->expects($this->never()) - ->method('deleteItem'); - - $this->cacheIdentifierGeneratorMock - ->expects($this->exactly(2)) - ->method('generateTag') - ->withConsecutive( - ['content', [42], false], - ['content', [2], false] - ) - ->willReturnOnConsecutiveCalls('c-42', 'c-2'); - - $this->cacheMock - ->expects($this->once()) - ->method('invalidateTags') - ->with(['c-42', 'c-2']); - - $handler = $this->persistenceCacheHandler->contentHandler(); - $handler->deleteContent(2); - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/ContentTypeHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/ContentTypeHandlerTest.php deleted file mode 100644 index e966e0a030..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/ContentTypeHandlerTest.php +++ /dev/null @@ -1,477 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\SPI\Persistence\Content\Type as SPIType; -use eZ\Publish\SPI\Persistence\Content\Type\CreateStruct as SPITypeCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as SPITypeFieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\Group as SPITypeGroup; -use eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct as SPITypeGroupCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct as SPITypeGroupUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as SPIContentTypeHandler; -use eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct as SPITypeUpdateStruct; - -/** - * Test case for Persistence\Cache\ContentTypeHandler. - */ -class ContentTypeHandlerTest extends AbstractInMemoryCacheHandlerTest -{ - public function getHandlerMethodName(): string - { - return 'contentTypeHandler'; - } - - public function getHandlerClassName(): string - { - return SPIContentTypeHandler::class; - } - - /** - * @return array - */ - public function providerForUnCachedMethods(): array - { - $groupUpdate = new SPITypeGroupUpdateStruct(['id' => 3, 'identifier' => 'media']); - $typeUpdate = new SPITypeUpdateStruct(['identifier' => 'article', 'remoteId' => '34o9tj8394t']); - - // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue - return [ - ['createGroup', [new SPITypeGroupCreateStruct()], null, [['content_type_group_list', [], true]], null, ['ibx-ctgl']], - [ - 'updateGroup', - [$groupUpdate], - null, - [ - ['content_type_group_list', [], true], - ['content_type_group', [3], true], - ['content_type_group_with_id_suffix', ['media'], true], - ], - null, - ['ibx-ctgl', 'ibx-ctg-3', 'ibx-ctg-media-bi'], - ], - ['deleteGroup', [3], [['type_group', [3], false]], null, ['tg-3']], - ['loadContentTypes', [3, 1]], // also listed for cached cases in providerForCachedLoadMethods - ['load', [5, 1]], // also listed for cached case in providerForCachedLoadMethods - [ - 'create', - [new SPITypeCreateStruct(['groupIds' => [2, 3]])], - null, - [ - ['content_type_list_by_group', [2], true], - ['content_type_list_by_group', [3], true], - ], - null, - ['ibx-ctlbg-2', 'ibx-ctlbg-3'], - ], - [ - 'update', - [5, 0, $typeUpdate], - [ - ['type', [5], false], - ['type_map', [], false], - ['content_fields_type', [5], false], - ], - null, - ['t-5', 'tm', 'cft-5'], - ], - ['update', [5, 1, $typeUpdate]], - [ - 'delete', - [5, 0], - [ - ['type', [5], false], - ['type_map', [], false], - ['content_fields_type', [5], false], - ], - null, - ['t-5', 'tm', 'cft-5'], - ], - ['delete', [5, 1]], - ['createDraft', [10, 5]], - [ - 'copy', - [10, 5, 0], - null, - [ - ['content_type_list_by_group', [1], true], - ['content_type_list_by_group', [2], true], - ], - null, - ['ibx-ctlbg-1', 'ibx-ctlbg-2'], - new SPIType(['groupIds' => [1, 2]]), - ], - ['copy', [10, 5, 1], null, [['content_type_list_by_group', [3], true]], null, ['ibx-ctlbg-3'], new SPIType(['groupIds' => [3]])], - ['unlink', [3, 5, 0], [['type', [5], false]], null, ['t-5']], - ['unlink', [3, 5, 1]], - [ - 'link', - [3, 5, 0], - [ - ['type', [5], false], - ], - [ - ['content_type_list_by_group', [3], true], - ], - ['t-5'], - ['ibx-ctlbg-3'], - ], - ['link', [3, 5, 1]], - ['getFieldDefinition', [7, 1]], - ['getFieldDefinition', [7, 0]], - ['getContentCount', [5]], - [ - 'addFieldDefinition', - [5, 0, new SPITypeFieldDefinition()], - [ - ['type', [5], false], - ['type_map', [], false], - ['content_fields_type', [5], false], - ], - null, - ['t-5', 'tm', 'cft-5'], - ], - ['addFieldDefinition', [5, 1, new SPITypeFieldDefinition()]], - [ - 'removeFieldDefinition', - [5, 0, 7], - [ - ['type', [5], false], - ['type_map', [], false], - ['content_fields_type', [5], false], - ], - null, - ['t-5', 'tm', 'cft-5'], - ], - ['removeFieldDefinition', [5, 1, 7]], - [ - 'updateFieldDefinition', - [5, 0, new SPITypeFieldDefinition()], - [ - ['type', [5], false], - ['type_map', [], false], - ['content_fields_type', [5], false], - ], - null, - ['t-5', 'tm', 'cft-5'], - ], - ['updateFieldDefinition', [5, 1, new SPITypeFieldDefinition()]], - [ - 'removeContentTypeTranslation', - [5, 'eng-GB'], - [ - ['type', [5], false], - ['type_map', [], false], - ['content_fields_type', [5], false], - ], - null, - ['t-5', 'tm', 'cft-5'], - null, - new SPIType(), - ], - ['deleteByUserAndStatus', [12, 0], [['type_without_value', [], false]], null, ['t']], - ['deleteByUserAndStatus', [12, 1]], - ]; - } - - /** - * @return array - */ - public function providerForCachedLoadMethodsHit(): array - { - $group = new SPITypeGroup(['id' => 3, 'identifier' => 'media']); - $type = new SPIType(['id' => 5, 'identifier' => 'article', 'remoteId' => '34o9tj8394t']); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - ['loadGroup', [3], 'ibx-ctg-3', null, null, [['content_type_group', [], true]], ['ibx-ctg'], $group], - ['loadGroups', [[3]], 'ibx-ctg-3', null, null, [['content_type_group', [], true]], ['ibx-ctg'], [3 => $group], true], - [ - 'loadGroupByIdentifier', - ['content'], - 'ibx-ctg-content-bi', - null, - null, - [ - ['content_type_group', [], true], - ['by_identifier_suffix', [], false], - ], - ['ibx-ctg', 'bi'], - $group, - ], - ['loadAllGroups', [], 'ibx-ctgl', null, null, [['content_type_group_list', [], true]], ['ibx-ctgl'], [3 => $group]], - ['loadContentTypes', [3, 0], 'ibx-ctlbg-3', null, null, [['content_type_list_by_group', [3], true]], ['ibx-ctlbg-3'], [$type]], - ['loadContentTypeList', [[5]], 'ibx-ct-5', null, null, [['content_type', [], true]], ['ibx-ct'], [5 => $type], true], - ['load', [5, 0], 'ibx-ct-5', null, null, [['content_type', [], true]], ['ibx-ct'], $type], - [ - 'loadByIdentifier', - ['article'], - 'ibx-ct-article-bi', - null, - null, - [ - ['content_type', [], true], - ['by_identifier_suffix', [], false], - ], - ['ibx-ct', 'bi'], - $type, - ], - [ - 'loadByRemoteId', - ['f34tg45gf'], - 'ibx-ct-f34tg45gf-br', - null, - null, - [ - ['content_type', [], true], - ['by_remote_suffix', [], false], - ], - ['ibx-ct', 'br'], - $type, - ], - ['getSearchableFieldMap', [], 'ibx-ctfm', null, null, [['content_type_field_map', [], true]], ['ibx-ctfm'], [$type]], - ]; - } - - public function providerForCachedLoadMethodsMiss(): array - { - $group = new SPITypeGroup(['id' => 3, 'identifier' => 'media']); - $type = new SPIType(['id' => 5, 'identifier' => 'article', 'remoteId' => '34o9tj8394t']); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - [ - 'loadGroup', - [3], - 'ibx-ctg-3', - [ - ['type_group', [3], false], - ], - ['tg-3'], - [ - ['content_type_group', [], true], - ], - ['ibx-ctg'], - $group, - ], - [ - 'loadGroups', - [[3]], - 'ibx-ctg-3', - [ - ['type_group', [3], false], - ], - ['tg-3'], - [ - ['content_type_group', [], true], - ], - ['ibx-ctg'], - [3 => $group], - true, - ], - [ - 'loadGroupByIdentifier', - ['content'], - 'ibx-ctg-content-bi', - [ - ['type_group', [3], false], - ], - ['tg-3'], - [ - ['content_type_group', [], true], - ['by_identifier_suffix', [], false], - ], - ['ibx-ctg', 'bi'], - $group, - ], - [ - 'loadAllGroups', - [], - 'ibx-ctgl', - [ - ['type_group', [3], false], - ], - ['tg-3'], - [ - ['content_type_group_list', [], true], - ], - ['ibx-ctgl'], - [3 => $group], - ], - [ - 'loadContentTypes', - [3, 0], - 'ibx-ctlbg-3', - [ - ['type_group', [3], false], - ['type', [], false], - ['type', [5], false], - ], - ['tg-3', 't', 't-5'], - [ - ['content_type_list_by_group', [3], true], - ], - ['ibx-ctlbg-3'], - [$type], - ], - [ - 'loadContentTypeList', - [[5]], - 'ibx-ct-5', - [ - ['type', [], false], - ['type', [5], false], - ], - ['t-3', 't-5'], - [ - ['content_type', [], true], - ], - ['ibx-ct'], - [5 => $type], - true, - ], - [ - 'load', - [5, 0], - 'ibx-ct-5', - [ - ['type', [], false], - ['type', [5], false], - ], - ['t-3', 't-5'], - [ - ['content_type', [], true], - ], - ['ibx-ct'], - $type, - ], - [ - 'loadByIdentifier', - ['article'], - 'ibx-ct-article-bi', - [ - ['type', [], false], - ['type', [5], false], - ], - ['t', 't-5'], - [ - ['content_type', [], true], - ['by_identifier_suffix', [], false], - ], - ['ibx-ct', 'bi'], - $type, - ], - [ - 'loadByRemoteId', - ['f34tg45gf'], - 'ibx-ct-f34tg45gf-br', - [ - ['type', [], false], - ['type', [5], false], - ], - ['t', 't-5'], - [ - ['content_type', [], true], - ['by_remote_suffix', [], false], - ], - ['ibx-ct', 'br'], - $type, - ], - [ - 'getSearchableFieldMap', - [], - 'ibx-ctfm', - [ - ['type_map', [], false], - ], - ['tm'], - [ - ['content_type_field_map', [], true], - ], - ['ibx-ctfm'], - [$type], - ], - ]; - } - - /** - * Test cache invalidation when publishing Content Type. - * - * @covers \eZ\Publish\Core\Persistence\Cache\ContentTypeHandler::publish - */ - public function testPublish() - { - $tags = ['t-5', 'tm', 'cft-5']; - $method = 'publish'; - $arguments = [5]; - $type = new SPIType(['id' => 5, 'groupIds' => [3, 4]]); - $cacheItem = $this->getCacheItem('ibx-ct-5', $type); - - $handlerMethodName = $this->getHandlerMethodName(); - - $this->loggerMock->expects($this->once())->method('logCall'); - - $innerHandler = $this->createMock($this->getHandlerClassName()); - $this->persistenceHandlerMock - ->expects($this->once()) - ->method($handlerMethodName) - ->willReturn($innerHandler); - - $innerHandler - ->expects($this->once()) - ->method($method) - ->with(...$arguments) - ->willReturn(null); - - $this->cacheIdentifierGeneratorMock - ->expects($this->exactly(3)) - ->method('generateTag') - ->withConsecutive( - ['type', [5], false], - ['type_map', [], false], - ['content_fields_type', [5], false] - ) - ->willReturnOnConsecutiveCalls( - 't-5', - 'tm', - 'cft-5' - ); - - $this->cacheIdentifierGeneratorMock - ->expects($this->exactly(3)) - ->method('generateKey') - ->withConsecutive( - ['content_type', [], true], - ['content_type_list_by_group', [3], true], - ['content_type_list_by_group', [4], true] - ) - ->willReturnOnConsecutiveCalls( - 'ibx-ct', - 'ibx-ctlbg-3', - 'ibx-ctlbg-4' - ); - - $this->cacheMock - ->expects(!empty($tags) ? $this->once() : $this->never()) - ->method('invalidateTags') - ->with($tags); - - $this->cacheMock - ->expects($this->once()) - ->method('getItem') - ->with($cacheItem->getKey()) - ->willReturn($cacheItem); - - $this->cacheMock - ->expects($this->once()) - ->method('deleteItems') - ->with(['ibx-ctlbg-3', 'ibx-ctlbg-4']) - ->willReturn(true); - - $handler = $this->persistenceCacheHandler->$handlerMethodName(); - call_user_func_array([$handler, $method], $arguments); - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/LocationHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/LocationHandlerTest.php deleted file mode 100644 index 29a6f873f3..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/LocationHandlerTest.php +++ /dev/null @@ -1,321 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as SPILocationHandler; -use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct; - -/** - * Test case for Persistence\Cache\LocationHandler. - */ -class LocationHandlerTest extends AbstractInMemoryCacheHandlerTest -{ - public function getHandlerMethodName(): string - { - return 'locationHandler'; - } - - public function getHandlerClassName(): string - { - return SPILocationHandler::class; - } - - public function providerForUnCachedMethods(): array - { - // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue - return [ - ['copySubtree', [12, 45]], - ['move', [12, 45], [['location_path', [12], false]], null, ['lp-12']], - ['markSubtreeModified', [12]], - ['hide', [12], [['location_path', [12], false]], null, ['lp-12']], - ['unHide', [12], [['location_path', [12], false]], null, ['lp-12']], - [ - 'swap', - [12, 45], - [ - ['location', [12], false], - ['location', [45], false], - ], - null, - ['l-12', 'l-45'], - ], - ['update', [new UpdateStruct(), 12], [['location', [12], false]], null, ['l-12']], - [ - 'create', - [new CreateStruct(['contentId' => 4, 'mainLocationId' => true])], - [ - ['content', [4], false], - ['role_assignment_group_list', [4], false], - ], - null, - ['c-4', 'ragl-4'], - ], - [ - 'create', - [new CreateStruct(['contentId' => 4, 'mainLocationId' => false])], - [ - ['content', [4], false], - ['role_assignment_group_list', [4], false], - ], - null, - ['c-4', 'ragl-4'], - ], - ['removeSubtree', [12], [['location_path', [12], false]], null, ['lp-12']], - ['setSectionForSubtree', [12, 2], [['location_path', [12], false]], null, ['lp-12']], - ['changeMainLocation', [4, 12], [['content', [4], false]], null, ['c-4']], - ['countLocationsByContent', [4]], - ]; - } - - public function providerForCachedLoadMethodsHit(): array - { - $location = new Location(['id' => 12]); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - ['load', [12], 'ibx-l-12-1', null, null, [['location', [], true]], ['ibx-l'], $location], - ['load', [12, ['eng-GB', 'bra-PG'], false], 'ibx-l-12-bra-PG|eng-GB|0', null, null, [['location', [], true]], ['ibx-l'], $location], - ['loadList', [[12]], 'ibx-l-12-1', null, null, [['location', [], true]], ['ibx-l'], [12 => $location], true], - ['loadList', [[12], ['eng-GB', 'bra-PG'], false], 'ibx-l-12-bra-PG|eng-GB|0', null, null, [['location', [], true]], ['ibx-l'], [12 => $location], true], - ['loadSubtreeIds', [12], 'ibx-ls-12', null, null, [['location_subtree', [], true]], ['ibx-ls'], [33, 44]], - [ - 'loadLocationsByContent', - [4, 12], - 'ibx-cl-4-root-12', - [ - ['content', [4], false], - ['location', [12], false], - ['location_path', [12], false], - ], - ['c-4', 'l-12', 'lp-12'], - [ - ['content_locations', [], true], - ], - ['ibx-cl'], - [$location], - ], - [ - 'loadLocationsByContent', - [4], - 'ibx-cl-4', - [ - ['content', [4], false], - ], - ['c-4'], - [ - ['content_locations', [], true], - ], - ['ibx-cl'], - [$location], - ], - [ - 'loadParentLocationsForDraftContent', - [4], - 'ibx-cl-4-pfd', - null, - null, - [ - ['content_locations', [], true], - ['parent_for_draft_suffix', [], false], - ], - ['ibx-cl', '-pfd'], - [$location], - ], - ['loadByRemoteId', ['34fe5y4'], 'ibx-lri-34fe5y4-1', null, null, [['location_remote_id', [], true]], ['ibx-lri'], $location], - ['loadByRemoteId', ['34fe5y4', ['eng-GB', 'arg-ES']], 'ibx-lri-34fe5y4-arg-ES|eng-GB|1', null, null, [['location_remote_id', [], true]], ['ibx-lri'], $location], - ]; - } - - public function providerForCachedLoadMethodsMiss(): array - { - $location = new Location( - [ - 'id' => 12, - 'contentId' => 15, - 'pathString' => '/1/2', - ] - ); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - [ - 'load', - [12], - 'ibx-l-12-1', - [ - ['content', [15], false], - ['location', [12], false], - ['location_path', ['2'], false], - ], - ['c-15', 'l-12', 'lp-2'], - [ - ['location', [], true], - ], - ['ibx-l'], - $location, - ], - [ - 'load', - [12, ['eng-GB', 'bra-PG'], false], - 'ibx-l-12-bra-PG|eng-GB|0', - [ - ['content', [15], false], - ['location', [12], false], - ['location_path', ['2'], false], - ], - ['c-15', 'l-12', 'lp-2'], - [ - ['location', [], true], - ], - ['ibx-l'], - $location, - ], - [ - 'loadList', - [[12]], - 'ibx-l-12-1', - [ - ['content', [15], false], - ['location', [12], false], - ['location_path', ['2'], false], - ], - ['c-15', 'l-12', 'lp-2'], - [ - ['location', [], true], - ], - ['ibx-l'], - [12 => $location], - true, - ], - [ - 'loadList', - [[12], - ['eng-GB', 'bra-PG'], false, ], - 'ibx-l-12-bra-PG|eng-GB|0', - [ - ['content', [15], false], - ['location', [12], false], - ['location_path', ['2'], false], - ], - ['c-15', 'l-12', 'lp-2'], - [ - ['location', [], true], - ], - ['ibx-l'], - [12 => $location], - true, - ], - [ - 'loadSubtreeIds', - [12], - 'ibx-ls-12', - [ - ['location', [12], false], - ['location_path', [12], false], - ['location', [33], false], - ['location_path', [33], false], - ['location', [44], false], - ['location_path', [44], false], - ], - ['l-12', 'lp-12', 'l-33', 'lp-33', 'l-44', 'lp-44'], - [ - ['location_subtree', [], true], - ], - ['ibx-ls'], - [33, 44], - ], - [ - 'loadLocationsByContent', - [4, 12], - 'ibx-cl-4-root-12', - [ - ['content', [4], false], - ['location', [12], false], - ['location_path', [12], false], - ['content', [15], false], - ['location', [12], false], - ['location_path', ['2'], false], - ], - ['c-4', 'l-12', 'lp-12', 'c-15', 'l-12', 'lp-2'], - [ - ['content_locations', [], true], - ], - ['ibx-cl'], - [$location], - ], - [ - 'loadLocationsByContent', - [4], - 'ibx-cl-4', - [ - ['content', [4], false], - ['content', [15], false], - ['location', [12], false], - ['location_path', ['2'], false], - ], - ['c-4', 'c-15', 'l-12', 'lp-2'], - [ - ['content_locations', [], true], - ], - ['ibx-cl'], - [$location], - ], - [ - 'loadParentLocationsForDraftContent', - [4], - 'ibx-cl-4-pfd', - [ - ['content', [4], false], - ['content', [15], false], - ['location', [12], false], - ['location_path', ['2'], false], - ], - ['c-4', 'c-15', 'l-12', 'lp-2'], - [ - ['content_locations', [], true], - ['parent_for_draft_suffix', [], false], - ], - ['ibx-cl', '-pfd'], - [$location], - ], - [ - 'loadByRemoteId', - ['34fe5y4'], - 'ibx-lri-34fe5y4-1', - [ - ['content', [15], false], - ['location', [12], false], - ['location_path', ['2'], false], - ], - ['c-15', 'l-12', 'lp-2'], - [ - ['location_remote_id', [], true], - ], - ['ibx-lri'], - $location, - ], - [ - 'loadByRemoteId', - ['34fe5y4', ['eng-GB', 'arg-ES']], - 'ibx-lri-34fe5y4-arg-ES|eng-GB|1', - [ - ['content', [15], false], - ['location', [12], false], - ['location_path', ['2'], false], - ], - ['c-15', 'l-12', 'lp-2'], - [ - ['location_remote_id', [], true], - ], - ['ibx-lri'], - $location, - ], - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/ObjectStateHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/ObjectStateHandlerTest.php deleted file mode 100644 index 228e1c6371..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/ObjectStateHandlerTest.php +++ /dev/null @@ -1,214 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\SPI\Persistence\Content\ObjectState as SPIObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group as SPIObjectStateGroup; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Handler as SPIObjectStateHandler; -use eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct as SPIInputStruct; - -/** - * Test case for Persistence\Cache\ObjectStateHandler. - */ -class ObjectStateHandlerTest extends AbstractCacheHandlerTest -{ - public function getHandlerMethodName(): string - { - return 'objectStateHandler'; - } - - public function getHandlerClassName(): string - { - return SPIObjectStateHandler::class; - } - - public function providerForUnCachedMethods(): array - { - // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue - return [ - ['createGroup', [new SPIInputStruct()], null, [['state_group_all', [], true]], null, 'ibx-sga'], - ['updateGroup', [5, new SPIInputStruct()], [['state_group', [5], false]], null, ['sg-5']], - ['deleteGroup', [5], [['state_group', [5], false]], null, ['sg-5']], - ['create', [5, new SPIInputStruct()], null, [['state_list_by_group', [5], true]], [], 'ibx-slbg-5'], - ['update', [7, new SPIInputStruct()], [['state', [7], false]], null, ['s-7']], - ['setPriority', [7, 99], [['state', [7], false]], null, ['s-7']], - ['delete', [7], [['state', [7], false]], null, ['s-7']], - ['setContentState', [4, 5, 7], null, [['state_by_group_on_content', [5, 4], true]], [], 'ibx-sbg-5-oc-4'], - ]; - } - - public function providerForCachedLoadMethodsHit(): array - { - $group = new SPIObjectStateGroup(['id' => 5]); - $state = new SPIObjectState(['id' => 7]); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - ['loadGroup', [5], 'ibx-sg-5', null, null, [['state_group', [], true]], ['ibx-sg'], $group], - [ - 'loadGroupByIdentifier', - ['lock'], - 'ibx-sg-lock-bi', - null, - null, - [ - ['state_group', [], true], - ['by_identifier_suffix', [], false], - ], - ['ibx-sg', '-bi'], - $group, - ], - ['loadAllGroups', [], 'ibx-sga', null, null, [['state_group_all', [], true]], ['ibx-sga'], [$group]], - ['loadObjectStates', [5], 'ibx-slbg-5', null, null, [['state_list_by_group', [], true]], ['ibx-slbg'], [$state]], - ['load', [7], 'ibx-s-7', null, null, [['state', [], true]], ['ibx-s'], $state], - [ - 'loadByIdentifier', - ['lock', 5], - 'ibx-si-lock-bg-5', - null, - null, - [ - ['state_identifier', [], true], - ['by_group', [5], false], - ], - ['ibx-si', 'bg-5'], - $state, - ], - [ - 'getContentState', - [4, 5], - 'ibx-sbg-5-oc-4', - null, - null, - [ - ['state_by_group', [], true], - ['on_content', [4], false], - ], - ['ibx-sbg', 'oc-4'], - $state, - ], - ]; - } - - public function providerForCachedLoadMethodsMiss(): array - { - $group = new SPIObjectStateGroup(['id' => 5]); - $state = new SPIObjectState([ - 'id' => 7, - 'groupId' => 5, - ]); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - [ - 'loadGroup', - [5], - 'ibx-sg-5', - [ - ['state_group', [5], false], - ], - ['sg-5'], - [ - ['state_group', [], true], - ], - ['ibx-sg'], - $group, - ], - [ - 'loadGroupByIdentifier', - ['lock'], - 'ibx-sg-lock-bi', - [ - ['state_group', [5], false], - ], - ['sg-5'], - [ - ['state_group', [], true], - ['by_identifier_suffix', [], false], - ], - ['ibx-sg', '-bi'], - $group, - ], - [ - 'loadAllGroups', - [], - 'ibx-sga', - [ - ['state_group', [5], false], - ], - ['sg-5'], - [ - ['state_group_all', [], true], - ], - ['ibx-sga'], - [$group], - ], - [ - 'loadObjectStates', - [5], - 'ibx-slbg-5', - [ - ['state_group', [5], false], - ['state', [7], false], - ], - ['sg-5', 's-7'], - [ - ['state_list_by_group', [], true], - ], - ['ibx-slbg'], - [$state], - ], - [ - 'load', - [7], - 'ibx-s-7', - [ - ['state', [7], false], - ['state_group', [5], false], - ], - ['s-7', 'sg-5'], - [ - ['state', [], true], - ], - ['ibx-s'], - $state, - ], - [ - 'loadByIdentifier', - ['lock', 5], - 'ibx-si-lock-bg-5', - [ - ['state', [7], false], - ['state_group', [5], false], - ], - ['s-7', 'sg-5'], - [ - ['state_identifier', [], true], - ['by_group', [5], false], - ], - ['ibx-si', 'bg-5'], - $state, - ], - [ - 'getContentState', - [4, 5], - 'ibx-sbg-5-oc-4', - [ - ['state', [7], false], - ['content', [4], false], - ], - ['s-7', 'c-4'], - [ - ['state_by_group', [], true], - ['on_content', [4], false], - ], - ['ibx-sbg', 'oc-4'], - $state, - ], - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/PersistenceHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/PersistenceHandlerTest.php deleted file mode 100644 index c24ba26e6e..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/PersistenceHandlerTest.php +++ /dev/null @@ -1,170 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\Core\Persistence\Cache; -use eZ\Publish\SPI\Persistence as SPIPersistence; - -/** - * Test case for Persistence\Cache\Handler. - */ -class PersistenceHandlerTest extends AbstractBaseHandlerTest -{ - /** - * Test that instance is of correct type. - * - * @covers \eZ\Publish\Core\Persistence\Cache\Handler::__construct - */ - public function testHandler() - { - $this->assertInstanceOf(SPIPersistence\Handler::class, $this->persistenceCacheHandler); - $this->assertInstanceOf(Cache\Handler::class, $this->persistenceCacheHandler); - } - - /** - * Test that instance is of correct type. - * - * @covers \eZ\Publish\Core\Persistence\Cache\Handler::contentHandler - */ - public function testContentHandler() - { - $this->loggerMock->expects($this->never())->method($this->anything()); - $handler = $this->persistenceCacheHandler->contentHandler(); - $this->assertInstanceOf(SPIPersistence\Content\Handler::class, $handler); - $this->assertInstanceOf(Cache\ContentHandler::class, $handler); - } - - /** - * Test that instance is of correct type. - * - * @covers \eZ\Publish\Core\Persistence\Cache\Handler::contentLanguageHandler - */ - public function testLanguageHandler() - { - $this->loggerMock->expects($this->never())->method($this->anything()); - $handler = $this->persistenceCacheHandler->contentLanguageHandler(); - $this->assertInstanceOf(SPIPersistence\Content\Language\Handler::class, $handler); - $this->assertInstanceOf(Cache\ContentLanguageHandler::class, $handler); - } - - /** - * Test that instance is of correct type. - * - * @covers \eZ\Publish\Core\Persistence\Cache\Handler::contentTypeHandler - */ - public function testContentTypeHandler() - { - $this->loggerMock->expects($this->never())->method($this->anything()); - $handler = $this->persistenceCacheHandler->contentTypeHandler(); - $this->assertInstanceOf(SPIPersistence\Content\Type\Handler::class, $handler); - $this->assertInstanceOf(Cache\ContentTypeHandler::class, $handler); - } - - /** - * Test that instance is of correct type. - * - * @covers \eZ\Publish\Core\Persistence\Cache\Handler::locationHandler - */ - public function testContentLocationHandler() - { - $this->loggerMock->expects($this->never())->method($this->anything()); - $handler = $this->persistenceCacheHandler->locationHandler(); - $this->assertInstanceOf(SPIPersistence\Content\Location\Handler::class, $handler); - $this->assertInstanceOf(Cache\LocationHandler::class, $handler); - } - - /** - * Test that instance is of correct type. - * - * @covers \eZ\Publish\Core\Persistence\Cache\Handler::trashHandler - */ - public function testTrashHandler() - { - $this->loggerMock->expects($this->never())->method($this->anything()); - $handler = $this->persistenceCacheHandler->trashHandler(); - $this->assertInstanceOf(SPIPersistence\Content\Location\Trash\Handler::class, $handler); - $this->assertInstanceOf(Cache\TrashHandler::class, $handler); - } - - /** - * Test that instance is of correct type. - * - * @covers \eZ\Publish\Core\Persistence\Cache\Handler::objectStateHandler - */ - public function testObjectStateHandler() - { - $this->loggerMock->expects($this->never())->method($this->anything()); - $handler = $this->persistenceCacheHandler->objectStateHandler(); - $this->assertInstanceOf(SPIPersistence\Content\ObjectState\Handler::class, $handler); - $this->assertInstanceOf(Cache\ObjectStateHandler::class, $handler); - } - - /** - * Test that instance is of correct type. - * - * @covers \eZ\Publish\Core\Persistence\Cache\Handler::sectionHandler - */ - public function testSectionHandler() - { - $this->loggerMock->expects($this->never())->method($this->anything()); - $handler = $this->persistenceCacheHandler->sectionHandler(); - $this->assertInstanceOf(SPIPersistence\Content\Section\Handler::class, $handler); - $this->assertInstanceOf(Cache\SectionHandler::class, $handler); - } - - /** - * Test that instance is of correct type. - * - * @covers \eZ\Publish\Core\Persistence\Cache\Handler::userHandler - */ - public function testUserHandler() - { - $this->loggerMock->expects($this->never())->method($this->anything()); - $handler = $this->persistenceCacheHandler->userHandler(); - $this->assertInstanceOf(SPIPersistence\User\Handler::class, $handler); - $this->assertInstanceOf(Cache\UserHandler::class, $handler); - } - - /** - * Test that instance is of correct type. - * - * @covers \eZ\Publish\Core\Persistence\Cache\Handler::urlAliasHandler - */ - public function testUrlAliasHandler() - { - $this->loggerMock->expects($this->never())->method($this->anything()); - $handler = $this->persistenceCacheHandler->urlAliasHandler(); - $this->assertInstanceOf(SPIPersistence\Content\UrlAlias\Handler::class, $handler); - $this->assertInstanceOf(Cache\UrlAliasHandler::class, $handler); - } - - /** - * Test that instance is of correct type. - * - * @covers \eZ\Publish\Core\Persistence\Cache\Handler::urlWildcardHandler - */ - public function testUrlWildcardHandler() - { - $this->loggerMock->expects($this->never())->method($this->anything()); - $handler = $this->persistenceCacheHandler->urlWildcardHandler(); - $this->assertInstanceOf(SPIPersistence\Content\UrlWildcard\Handler::class, $handler); - $this->assertInstanceOf(Cache\UrlWildcardHandler::class, $handler); - } - - /** - * Test that instance is of correct type. - * - * @covers \eZ\Publish\Core\Persistence\Cache\Handler::transactionHandler - */ - public function testTransactionHandler() - { - $this->loggerMock->expects($this->never())->method($this->anything()); - $handler = $this->persistenceCacheHandler->transactionHandler(); - $this->assertInstanceOf(SPIPersistence\TransactionHandler::class, $handler); - $this->assertInstanceOf(Cache\TransactionHandler::class, $handler); - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/PersistenceLoggerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/PersistenceLoggerTest.php deleted file mode 100644 index b3687aa4a6..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/PersistenceLoggerTest.php +++ /dev/null @@ -1,112 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\Core\Persistence\Cache\PersistenceLogger; -use PHPUnit\Framework\TestCase; - -/** - * Test case for PersistenceLogger. - */ -class PersistenceLoggerTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Cache\PersistenceLogger */ - protected $logger; - - /** - * Setup the HandlerTest. - */ - protected function setUp(): void - { - parent::setUp(); - $this->logger = new PersistenceLogger(); - } - - /** - * Tear down test (properties). - */ - protected function tearDown(): void - { - unset($this->logger); - parent::tearDown(); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Cache\PersistenceLogger::getName - */ - public function testGetName() - { - $this->assertEquals(PersistenceLogger::NAME, $this->logger->getName()); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Cache\PersistenceLogger::getCount - */ - public function testGetCount() - { - $this->assertEquals(0, $this->logger->getCount()); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Cache\PersistenceLogger::getCalls - */ - public function testGetCalls() - { - $this->assertEquals([], $this->logger->getCalls()); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Cache\PersistenceLogger::logCall - */ - public function testLogCall() - { - $this->assertNull($this->logger->logCall(__METHOD__)); - $this->logger->logCall(__METHOD__); - $this->logger->logCall(__METHOD__); - $this->logger->logCall(__METHOD__, [33]); - - return $this->logger; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Cache\PersistenceLogger::getCount - * @depends testLogCall - * - * @param \eZ\Publish\Core\Persistence\Cache\PersistenceLogger $logger - */ - public function testGetCountValues($logger) - { - $this->assertEquals(4, $logger->getCount()); - - return $logger; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Cache\PersistenceLogger::getCalls - * @depends testGetCountValues - * - * @param \eZ\Publish\Core\Persistence\Cache\PersistenceLogger $logger - */ - public function testGetCallValues($logger) - { - $calls = $logger->getCalls(); - // As we don't care about the hash index we get the array values instead - $calls = array_values($calls); - - $method = __CLASS__ . '::testLogCall'; - - $this->assertEquals($method, $calls[0]['method']); - $this->assertEquals([], $calls[0]['arguments']); - $this->assertCount(1, $calls[0]['traces']); - $this->assertEquals(['uncached' => 3, 'miss' => 0, 'hit' => 0, 'memory' => 0], $calls[0]['stats']); - - $this->assertEquals($method, $calls[1]['method']); - $this->assertEquals([33], $calls[1]['arguments']); - $this->assertCount(1, $calls[1]['traces']); - $this->assertEquals(['uncached' => 1, 'miss' => 0, 'hit' => 0, 'memory' => 0], $calls[1]['stats']); - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/SectionHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/SectionHandlerTest.php deleted file mode 100644 index 87a70dd826..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/SectionHandlerTest.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\SPI\Persistence\Content\Section as SPISection; -use eZ\Publish\SPI\Persistence\Content\Section\Handler as SPISectionHandler; - -/** - * Test case for Persistence\Cache\SectionHandler. - */ -class SectionHandlerTest extends AbstractCacheHandlerTest -{ - public function getHandlerMethodName(): string - { - return 'sectionHandler'; - } - - public function getHandlerClassName(): string - { - return SPISectionHandler::class; - } - - public function providerForUnCachedMethods(): array - { - // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue - return [ - ['create', ['Standard', 'standard']], - ['update', [5, 'Standard', 'standard'], [['section', [5], false]], null, ['se-5']], - ['loadAll', []], - ['delete', [5], [['section', [5], false]], null, ['se-5']], - ['assign', [5, 42], [['content', [42], false]], null, ['c-42']], - ['assignmentsCount', [5]], - ['policiesCount', [5]], - ['countRoleAssignmentsUsingSection', [5]], - ]; - } - - public function providerForCachedLoadMethodsHit(): array - { - $object = new SPISection(['id' => 5]); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - ['load', [5], 'ibx-se-5', null, null, [['section', [5], true]], ['ibx-se-5'], $object], - ['loadByIdentifier', ['standard'], 'ibx-se-standard-bi', null, null, [['section_with_by_id', ['standard'], true]], ['ibx-se-standard-bi'], $object], - ]; - } - - public function providerForCachedLoadMethodsMiss(): array - { - $object = new SPISection(['id' => 5]); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - [ - 'load', - [5], - 'ibx-se-5', - [ - ['section', [5], false], - ], - ['se-5'], - [ - ['section', [5], true], - ], - ['ibx-se-5'], - $object, - ], - [ - 'loadByIdentifier', - ['standard'], - 'ibx-se-standard-bi', - [ - ['section', [5], false], - ], - ['se-5'], - [ - ['section_with_by_id', ['standard'], true], - ], - ['ibx-se-standard-bi'], - $object, - ], - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/SettingHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/SettingHandlerTest.php deleted file mode 100644 index 9cfecceb89..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/SettingHandlerTest.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\SPI\Persistence\Setting\Handler as SettingHandler; -use eZ\Publish\SPI\Persistence\Setting\Setting; - -/** - * Test case for Persistence\Cache\SettingHandler. - */ -class SettingHandlerTest extends AbstractCacheHandlerTest -{ - public function getHandlerMethodName(): string - { - return 'settingHandler'; - } - - public function getHandlerClassName(): string - { - return SettingHandler::class; - } - - public function providerForUnCachedMethods(): array - { - // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue - return [ - [ - 'create', - ['group_a1', 'identifier_b2', 'value_c3'], - null, - null, - null, - null, - new Setting(), - ], - [ - 'update', - ['group_a1', 'identifier_b2', 'update_value_c3'], - [['setting', ['group_a1', 'identifier_b2'], true]], - null, - ['ibx-set-group_a1-identifier_b2'], - null, - new Setting(), - ], - [ - 'delete', - ['group_a1', 'identifier_b2'], - [['setting', ['group_a1', 'identifier_b2'], true]], - null, - ['ibx-set-group_a1-identifier_b2'], - ], - ]; - } - - public function providerForCachedLoadMethodsHit(): array - { - $object = new Setting(['group' => 'group_a1', 'identifier' => 'identifier_b2']); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - [ - 'load', - ['group_a1', 'identifier_b2'], - 'ibx-set-group_a1-identifier_b2', - [['setting', ['group_a1', 'identifier_b2'], true]], - ['ibx-set-group_a1-identifier_b2'], - null, - null, - $object, - ], - ]; - } - - public function providerForCachedLoadMethodsMiss(): array - { - $object = new Setting(['group' => 'group_a1', 'identifier' => 'identifier_b2']); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - [ - 'load', - ['group_a1', 'identifier_b2'], - 'ibx-set-group_a1-identifier_b2', - [ - ['setting', ['group_a1', 'identifier_b2'], true], - ['setting', ['group_a1', 'identifier_b2'], true], - ], - ['ibx-set-group_a1-identifier_b2', 'ibx-set-group_a1-identifier_b2'], - null, - null, - $object, - ], - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/TransactionHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/TransactionHandlerTest.php deleted file mode 100644 index 3bf0d8a8cf..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/TransactionHandlerTest.php +++ /dev/null @@ -1,133 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\SPI\Persistence\TransactionHandler; - -/** - * Test case for Persistence\Cache\TransactionHandler. - */ -class TransactionHandlerTest extends AbstractCacheHandlerTest -{ - public function getHandlerMethodName(): string - { - return 'transactionHandler'; - } - - public function getHandlerClassName(): string - { - return TransactionHandler::class; - } - - public function providerForUnCachedMethods(): array - { - // string $method, array $arguments, array $arguments, array? $cacheTagGeneratingArguments, array? $cacheKeyGeneratingArguments, array? $tags, string? $key - return [ - ['beginTransaction', []], - ['commit', []], - ]; - } - - public function providerForCachedLoadMethodsHit(): array - { - // string $method, array $arguments, array? $cacheIdentifierGeneratorArguments, array? $cacheIdentifierGeneratorResults, string $key, mixed? $data - return [ - ]; - } - - public function providerForCachedLoadMethodsMiss(): array - { - // string $method, array $arguments, array? $cacheIdentifierGeneratorArguments, array? $cacheIdentifierGeneratorResults, string $key, mixed? $data - return [ - ]; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Cache\TransactionHandler::rollback - */ - public function testRollback() - { - $this->loggerMock - ->expects($this->once()) - ->method('logCall'); - - $this->cacheMock - ->expects($this->never()) - ->method('clear'); - - $this->cacheMock - ->expects($this->once()) - ->method('rollbackTransaction'); - - $innerHandlerMock = $this->createMock(TransactionHandler::class); - $this->persistenceHandlerMock - ->expects($this->once()) - ->method('transactionHandler') - ->willReturn($innerHandlerMock); - - $innerHandlerMock - ->expects($this->once()) - ->method('rollback'); - - $handler = $this->persistenceCacheHandler->transactionHandler(); - $handler->rollback(); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Cache\TransactionHandler::commit - */ - public function testCommitStopsCacheTransaction() - { - $this->loggerMock - ->expects($this->once()) - ->method('logCall'); - - $this->cacheMock - ->expects($this->once()) - ->method('commitTransaction'); - - $innerHandlerMock = $this->createMock(TransactionHandler::class); - $this->persistenceHandlerMock - ->expects($this->once()) - ->method('transactionHandler') - ->willReturn($innerHandlerMock); - - $innerHandlerMock - ->expects($this->once()) - ->method('commit'); - - $handler = $this->persistenceCacheHandler->transactionHandler(); - $handler->commit(); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Cache\TransactionHandler::beginTransaction - */ - public function testBeginTransactionStartsCacheTransaction() - { - $this->loggerMock - ->expects($this->once()) - ->method('logCall'); - - $this->cacheMock - ->expects($this->once()) - ->method('beginTransaction'); - - $innerHandlerMock = $this->createMock(TransactionHandler::class); - $this->persistenceHandlerMock - ->expects($this->once()) - ->method('transactionHandler') - ->willReturn($innerHandlerMock); - - $innerHandlerMock - ->expects($this->once()) - ->method('beginTransaction'); - - $handler = $this->persistenceCacheHandler->transactionHandler(); - $handler->beginTransaction(); - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/TrashHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/TrashHandlerTest.php deleted file mode 100644 index 6dea7fc036..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/TrashHandlerTest.php +++ /dev/null @@ -1,308 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult; -use eZ\Publish\Core\Persistence\Cache\ContentHandler; -use eZ\Publish\Core\Persistence\Cache\LocationHandler; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\Trash\Handler as TrashHandler; -use eZ\Publish\SPI\Persistence\Content\Location\Trashed; -use eZ\Publish\SPI\Persistence\Content\Relation; - -/** - * Test case for Persistence\Cache\SectionHandler. - */ -class TrashHandlerTest extends AbstractCacheHandlerTest -{ - public function getHandlerMethodName(): string - { - return 'trashHandler'; - } - - public function getHandlerClassName(): string - { - return TrashHandler::class; - } - - public function providerForUnCachedMethods(): array - { - // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue - return [ - ['loadTrashItem', [6]], - ]; - } - - public function providerForCachedLoadMethodsHit(): array - { - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - ]; - } - - public function providerForCachedLoadMethodsMiss(): array - { - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - ]; - } - - public function testRecover() - { - $originalLocationId = 6; - $targetLocationId = 2; - $contentId = 42; - - $tags = [ - 'c-' . $contentId, - 'lp-' . $originalLocationId, - ]; - - $handlerMethodName = $this->getHandlerMethodName(); - - $this->loggerMock->expects($this->once())->method('logCall'); - - $innerHandler = $this->createMock($this->getHandlerClassName()); - $contentHandlerMock = $this->createMock(ContentHandler::class); - $locationHandlerMock = $this->createMock(LocationHandler::class); - - $locationHandlerMock - ->method('load') - ->willReturn(new Location(['id' => $originalLocationId, 'contentId' => $contentId])); - - $this->persistenceHandlerMock - ->method('contentHandler') - ->willReturn($contentHandlerMock); - - $this->persistenceHandlerMock - ->method('locationHandler') - ->willReturn($locationHandlerMock); - - $this->persistenceHandlerMock - ->expects($this->once()) - ->method($handlerMethodName) - ->willReturn($innerHandler); - - $innerHandler - ->expects($this->once()) - ->method('recover') - ->with($originalLocationId, $targetLocationId) - ->willReturn(null); - - $this->cacheIdentifierGeneratorMock - ->expects($this->exactly(2)) - ->method('generateTag') - ->withConsecutive( - ['content', [$contentId], false], - ['location_path', [$originalLocationId], false] - ) - ->willReturnOnConsecutiveCalls( - 'c-' . $contentId, - 'lp-' . $originalLocationId - ); - - $this->cacheMock - ->expects($this->once()) - ->method('invalidateTags') - ->with($tags); - - $handler = $this->persistenceCacheHandler->$handlerMethodName(); - $handler->recover($originalLocationId, $targetLocationId); - } - - public function testTrashSubtree() - { - $locationId = 6; - $contentId = 42; - - $tags = [ - 'c-' . $contentId, - 'lp-' . $locationId, - ]; - - $handlerMethodName = $this->getHandlerMethodName(); - - $this->loggerMock->expects($this->once())->method('logCall'); - - $innerHandler = $this->createMock($this->getHandlerClassName()); - $contentHandlerMock = $this->createMock(ContentHandler::class); - $locationHandlerMock = $this->createMock(LocationHandler::class); - - $locationHandlerMock - ->method('load') - ->willReturn(new Location(['id' => $locationId, 'contentId' => $contentId])); - - $this->persistenceHandlerMock - ->method('contentHandler') - ->willReturn($contentHandlerMock); - - $this->persistenceHandlerMock - ->method('locationHandler') - ->willReturn($locationHandlerMock); - - $this->persistenceHandlerMock - ->expects($this->once()) - ->method($handlerMethodName) - ->willReturn($innerHandler); - - $innerHandler - ->expects($this->once()) - ->method('trashSubtree') - ->with($locationId) - ->willReturn(null); - - $this->cacheIdentifierGeneratorMock - ->expects($this->exactly(2)) - ->method('generateTag') - ->withConsecutive( - ['content', [$contentId], false], - ['location_path', [$locationId], false] - ) - ->willReturnOnConsecutiveCalls(...$tags); - - $this->cacheMock - ->expects($this->once()) - ->method('invalidateTags') - ->with($tags); - - $handler = $this->persistenceCacheHandler->$handlerMethodName(); - $handler->trashSubtree($locationId); - } - - public function testDeleteTrashItem() - { - $trashedId = 6; - $contentId = 42; - $relationSourceContentId = 44; - - $handlerMethodName = $this->getHandlerMethodName(); - - $innerHandler = $this->createMock($this->getHandlerClassName()); - - $trashed = new Trashed(['id' => $trashedId, 'contentId' => $contentId]); - $innerHandler - ->expects($this->once()) - ->method('deleteTrashItem') - ->with($trashedId) - ->willReturn(new TrashItemDeleteResult(['trashItemId' => $trashedId, 'contentId' => $contentId])); - - $innerHandler - ->expects($this->once()) - ->method('loadTrashItem') - ->with($trashedId) - ->willReturn($trashed); - - $this->persistenceHandlerMock - ->method($handlerMethodName) - ->willReturn($innerHandler); - - $contentHandlerMock = $this->createMock(ContentHandler::class); - - $contentHandlerMock - ->expects($this->once()) - ->method('loadReverseRelations') - ->with($contentId) - ->willReturn([new Relation(['sourceContentId' => $relationSourceContentId])]); - - $this->persistenceHandlerMock - ->method('contentHandler') - ->willReturn($contentHandlerMock); - - $tags = [ - 'c-' . $contentId, - 'lp-' . $trashedId, - 'c-' . $relationSourceContentId, - ]; - - $this->cacheIdentifierGeneratorMock - ->expects($this->exactly(3)) - ->method('generateTag') - ->withConsecutive( - ['content', [$relationSourceContentId], false], - ['content', [$contentId], false], - ['location_path', [$trashedId], false] - ) - ->willReturnOnConsecutiveCalls( - 'c-' . $relationSourceContentId, - 'c-' . $contentId, - 'lp-' . $trashedId - ); - - $this->cacheMock - ->expects($this->once()) - ->method('invalidateTags') - ->with($tags); - - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Trash\Handler $handler */ - $handler = $this->persistenceCacheHandler->$handlerMethodName(); - $handler->deleteTrashItem($trashedId); - } - - public function testEmptyTrash() - { - $trashedId = 6; - $contentId = 42; - $relationSourceContentId = 44; - - $handlerMethodName = $this->getHandlerMethodName(); - - $innerHandler = $this->createMock($this->getHandlerClassName()); - - $innerHandler - ->expects($this->exactly(2)) - ->method('findTrashItems') - ->willReturn(new Location\Trash\TrashResult([ - 'items' => [new Trashed(['id' => $trashedId, 'contentId' => $contentId])], - // trigger the bulk loading several times to have some minimal coverage on the loop exit logic - 'totalCount' => 101, - ])); - - $this->persistenceHandlerMock - ->method($handlerMethodName) - ->willReturn($innerHandler); - - $contentHandlerMock = $this->createMock(ContentHandler::class); - - $contentHandlerMock - ->expects($this->exactly(2)) - ->method('loadReverseRelations') - ->with($contentId) - ->willReturn([new Relation(['sourceContentId' => $relationSourceContentId])]); - - $this->persistenceHandlerMock - ->method('contentHandler') - ->willReturn($contentHandlerMock); - - $cacheIdentifierGeneratorArguments = [ - ['content', [$relationSourceContentId], false], - ['content', [$contentId], false], - ['location_path', [$trashedId], false], - ]; - - $tags = [ - 'c-' . $relationSourceContentId, - 'c-' . $contentId, - 'lp-' . $trashedId, - ]; - - //one set of arguments and tags for each relation - $this->cacheIdentifierGeneratorMock - ->expects($this->exactly(6)) - ->method('generateTag') - ->withConsecutive(...array_merge($cacheIdentifierGeneratorArguments, $cacheIdentifierGeneratorArguments)) - ->willReturnOnConsecutiveCalls(...array_merge($tags, $tags)); - - $this->cacheMock - ->expects($this->once()) - ->method('invalidateTags') - ->with($tags); - - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Trash\Handler $handler */ - $handler = $this->persistenceCacheHandler->$handlerMethodName(); - $handler->emptyTrash(); - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/UrlAliasHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/UrlAliasHandlerTest.php deleted file mode 100644 index ed403e643d..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/UrlAliasHandlerTest.php +++ /dev/null @@ -1,222 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\SPI\Persistence\Content\UrlAlias; -use eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler as SPIUrlAliasHandler; - -/** - * Test case for Persistence\Cache\UrlAliasHandler. - */ -class UrlAliasHandlerTest extends AbstractInMemoryCacheHandlerTest -{ - public function getHandlerMethodName(): string - { - return 'urlAliasHandler'; - } - - public function getHandlerClassName(): string - { - return SPIUrlAliasHandler::class; - } - - public function providerForUnCachedMethods(): array - { - // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue - return [ - [ - 'publishUrlAliasForLocation', - [44, 2, 'name', 'eng-GB', true, false], - [ - ['url_alias_location', [44], false], - ['url_alias_location_path', [44], false], - ['url_alias', ['44-abc'], false], - ['url_alias_not_found', [], false], - ], - null, - ['urlal-44', 'urlalp-44', 'urla-44-abc', 'urlanf'], - null, - '44-abc', - ], - [ - 'createCustomUrlAlias', - [44, '1/2/44', true, null, false], - [ - ['url_alias_location', [44], false], - ['url_alias_location_path', [44], false], - ['url_alias_not_found', [], false], - ['url_alias', [5], false], - ], - null, - ['urlal-44', 'urlalp-44', 'urlanf', 'urla-5'], - null, - new UrlAlias(['id' => 5]), - ], - ['createGlobalUrlAlias', ['something', '1/2/44', true, null, false], [['url_alias_not_found', [], false]], null, ['urlanf']], - ['createGlobalUrlAlias', ['something', '1/2/44', true, 'eng-GB', false], [['url_alias_not_found', [], false]], null, ['urlanf']], - ['listGlobalURLAliases', ['eng-GB', 10, 50]], - [ - 'removeURLAliases', - [[new UrlAlias(['id' => 5, 'type' => UrlAlias::LOCATION, 'isCustom' => true, 'destination' => 21])]], - [ - ['url_alias', [5], false], - ['url_alias_location', [21], false], - ['url_alias_location_path', [21], false], - ['url_alias_custom', [21], false], - ], - null, - ['urla-5', 'urlal-21', 'urlalp-21', 'urlac-21'], - ], - [ - 'locationMoved', - [21, 45, 12], - [ - ['url_alias_location', [21], false], - ['url_alias_location_path', [21], false], - ], - null, - ['urlal-21', 'urlalp-21'], - ], - [ - 'locationCopied', - [21, 33, 12], - [ - ['url_alias_location', [21], false], - ['url_alias_location', [33], false], - ], - null, - ['urlal-21', 'urlal-33'], - ], - [ - 'locationDeleted', - [21], - [ - ['url_alias_location', [21], false], - ['url_alias_location_path', [21], false], - ], - null, - ['urlal-21', 'urlalp-21'], - null, - [], - ], - [ - 'locationSwapped', - [21, 2, 33, 45], - [ - ['url_alias_location', [21], false], - ['url_alias_location_path', [21], false], - ['url_alias_location', [33], false], - ['url_alias_location_path', [33], false], - ], - null, - ['urlal-21', 'urlalp-21', 'urlal-33', 'urlalp-33'], - ], - [ - 'translationRemoved', - [[21, 33], 'eng-GB'], - [ - ['url_alias_location', [21], false], - ['url_alias_location_path', [21], false], - ['url_alias_location', [33], false], - ['url_alias_location_path', [33], false], - ], - null, - ['urlal-21', 'urlalp-21', 'urlal-33', 'urlalp-33'], - ], - [ - 'archiveUrlAliasesForDeletedTranslations', - [21, 33, ['eng-GB']], - [ - ['url_alias_location', [21], false], - ['url_alias_location_path', [21], false], - ], - null, - ['urlal-21', 'urlalp-21'], - ], - ]; - } - - public function providerForCachedLoadMethodsHit(): array - { - $object = new UrlAlias(['id' => 5]); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - ['listURLAliasesForLocation', [5], 'ibx-urlall-5', null, null, [['url_alias_location_list', [5], true]], ['ibx-urlall-5'], [$object]], - ['listURLAliasesForLocation', [5, true], 'ibx-urlall-5-c', null, null, [['url_alias_location_list_custom', [5], true]], ['ibx-urlall-5-c'], [$object]], - ['lookup', ['/Home'], 'ibx-urlau-_SHome', null, null, [['url_alias_url', ['_SHome'], true]], ['ibx-urlau-_SHome'], $object], - ['loadUrlAlias', [5], 'ibx-urla-5', null, null, [['url_alias', [5], true]], ['ibx-urla-5'], $object], - ]; - } - - public function providerForCachedLoadMethodsMiss(): array - { - $object = new UrlAlias(['id' => 5]); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - [ - 'listURLAliasesForLocation', - [5], - 'ibx-urlall-5', - [ - ['url_alias_location', [5], false], - ['url_alias', [5], false], - ], - ['urlal-5', 'urla-5'], - [ - ['url_alias_location_list', [5], true], - ], - ['ibx-urlall-5'], - [$object], - ], - [ - 'listURLAliasesForLocation', - [5, true], - 'ibx-urlall-5-c', - [ - ['url_alias_location', [5], false], - ['url_alias', [5], false], - ], - ['urlal-5', 'urla-5'], - [ - ['url_alias_location_list_custom', [5], true], - ], - ['ibx-urlall-5-c'], - [$object], - ], - [ - 'lookup', - ['/Home'], - 'ibx-urlau-_SHome', - [ - ['url_alias', [5], false], - ], - ['urla-5'], - [ - ['url_alias_url', ['_SHome'], true], - ], - ['ibx-urlau-_SHome'], - $object, - ], - [ - 'loadUrlAlias', - [5], - 'ibx-urla-5', - [ - ['url_alias', [5], false], - ], - ['urla-5'], - [ - ['url_alias', [5], true], - ], - ['ibx-urla-5'], - $object, - ], - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/UrlWildcardHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/UrlWildcardHandlerTest.php deleted file mode 100644 index f5ee3838ac..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/UrlWildcardHandlerTest.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\SPI\Persistence\Content\UrlWildcard; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler as SpiUrlWildcardHandler; - -class UrlWildcardHandlerTest extends AbstractCacheHandlerTest -{ - public function getHandlerMethodName(): string - { - return 'urlWildcardHandler'; - } - - public function getHandlerClassName(): string - { - return SpiUrlWildcardHandler::class; - } - - public function providerForUnCachedMethods(): array - { - $wildcard = new UrlWildcard(['id' => 1]); - - // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue - return [ - ['create', ['/home/about', '/web3/some/page/link', true], [['url_wildcard_not_found', [], false]], null, ['urlwnf'], null, $wildcard], - ['remove', [1], [['url_wildcard', [1], false]], null, ['urlw-1']], - ['loadAll', [], null, null, null, null, [$wildcard]], - ['exactSourceUrlExists', ['/home/about'], null, null, null, null, true], - ]; - } - - public function providerForCachedLoadMethodsHit(): array - { - $wildcard = new UrlWildcard(['id' => 1]); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - ['load', [1], 'ibx-urlw-1', null, null, [['url_wildcard', [1], true]], ['ibx-urlw-1'], $wildcard], - ['translate', ['/home/about'], 'ibx-urlws-_Shome_Sabout', null, null, [['url_wildcard_source', ['_Shome_Sabout'], true]], ['ibx-urlws-_Shome_Sabout'], $wildcard], - ]; - } - - public function providerForCachedLoadMethodsMiss(): array - { - $wildcard = new UrlWildcard(['id' => 1]); - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - [ - 'load', - [1], - 'ibx-urlw-1', - [ - ['url_wildcard', [1], false], - ], - ['urlw-1'], - [ - ['url_wildcard', [1], true], - ], - ['ibx-urlw-1'], - $wildcard, - ], - [ - 'translate', - ['/home/about'], - 'ibx-urlws-_Shome_Sabout', - [ - ['url_wildcard', [1], false], - ], - ['urlw-1'], - [ - ['url_wildcard_source', ['_Shome_Sabout'], true], - ], - ['ibx-urlws-_Shome_Sabout'], - $wildcard, - ], - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/UserHandlerTest.php b/eZ/Publish/Core/Persistence/Cache/Tests/UserHandlerTest.php deleted file mode 100644 index 8babcfbb9d..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/Tests/UserHandlerTest.php +++ /dev/null @@ -1,638 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; - -use eZ\Publish\Core\Persistence\Cache\UserHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler as SPILocationHandler; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\User; -use eZ\Publish\SPI\Persistence\User\Handler as SPIUserHandler; -use eZ\Publish\SPI\Persistence\User\Policy; -use eZ\Publish\SPI\Persistence\User\Role; -use eZ\Publish\SPI\Persistence\User\RoleAssignment; -use eZ\Publish\SPI\Persistence\User\RoleCreateStruct; -use eZ\Publish\SPI\Persistence\User\RoleUpdateStruct; - -/** - * Test case for Persistence\Cache\UserHandler. - */ -class UserHandlerTest extends AbstractInMemoryCacheHandlerTest -{ - public function getHandlerMethodName(): string - { - return 'userHandler'; - } - - public function getHandlerClassName(): string - { - return SPIUserHandler::class; - } - - public function providerForUnCachedMethods(): array - { - $user = new User(['id' => 14, 'login' => 'otto', 'email' => 'otto@ez.no']); - $policy = new Policy(['id' => 13, 'roleId' => 9]); - $userToken = new User\UserTokenUpdateStruct(['userId' => 14, 'hashKey' => '4irj8t43r']); - $escapedLogin = str_replace('@', '_A', $user->login); - $escapedEmail = str_replace('@', '_A', $user->email); - - // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, returned $returnValue, bool $callInnerHandler - return [ - [ - 'create', - [$user], - [ - ['content', [14], false], - ], - [ - ['user', [14], true], - ['user_with_by_login_suffix', [$escapedLogin], true], - ['user_with_by_email_suffix', [$escapedEmail], true], - ['users_with_by_email_suffix', [$escapedEmail], true], - ], - ['c-14'], - [ - 'ibx-u-14', - 'ibx-u-' . $escapedLogin . '-bl', - 'ibx-u-' . $escapedEmail . '-be', - 'ibx-us-' . $escapedEmail . '-be', - ], - $user, - false, - ], - [ - 'update', - [$user], - [ - ['content', [14], false], - ['user', [14], false], - ], - [ - ['user_with_by_email_suffix', [$escapedEmail], true], - ['users_with_by_email_suffix', [$escapedEmail], true], - ], - ['c-14', 'u-14'], - [ - 'ibx-u-' . $escapedEmail . '-be', - 'ibx-us-' . $escapedEmail . '-be', - ], - $user, - false, - ], - [ - 'updateUserToken', - [$userToken], - [ - ['user_with_account_key_suffix', [14], false], - ], - [ - ['user_with_by_account_key_suffix', ['4irj8t43r'], true], - ], - ['u-14-ak'], - ['ibx-u-4irj8t43r-bak'], - ], - ['expireUserToken', ['4irj8t43r'], null, [['user_with_by_account_key_suffix', ['4irj8t43r'], true]], null, ['ibx-u-4irj8t43r-bak']], - [ - 'delete', - [14], - [ - ['content', [14], false], - ['user', [14], false], - ], - null, - ['c-14', 'u-14'], - null, - null, - false, - ], - ['countRoleAssignments', [9], null, [], null, [], 1], - ['createRole', [new RoleCreateStruct()]], - ['createRoleDraft', [new RoleCreateStruct()]], - ['loadRole', [9, 1]], - ['loadRoleByIdentifier', ['member', 1]], - ['loadRoleDraftByRoleId', [9]], - ['loadRoles', []], - ['updateRole', [new RoleUpdateStruct(['id' => 9])], [['role', [9], false]], null, ['r-9']], - [ - 'deleteRole', - [9], - [ - ['role', [9], false], - ['role_assignment_role_list', [9], false], - ], - null, - ['r-9', 'rarl-9'], - ], - ['deleteRole', [9, 1]], - ['addPolicyByRoleDraft', [9, $policy]], - ['addPolicy', [9, $policy], [['role', [9], false]], null, ['r-9']], - [ - 'updatePolicy', - [$policy], - [ - ['policy', [13], false], - ['role', [9], false], - ], - null, - ['p-13', 'r-9'], - ], - [ - 'deletePolicy', - [13, 9], - [ - ['policy', [13], false], - ['role', [9], false], - ], - null, - ['p-13', 'r-9'], - ], - ['loadPoliciesByUserId', [14]], - [ - 'unassignRole', - [14, 9], - [ - ['role_assignment_group_list', [14], false], - ['role_assignment_role_list', [9], false], - ], - null, - ['ragl-14', 'rarl-9'], - ], - ]; - } - - public function providerForCachedLoadMethodsHit(): array - { - $user = new User(['id' => 14]); - $role = new Role(['id' => 9]); - $roleAssignment = new RoleAssignment(['id' => 11, 'roleId' => 9, 'contentId' => 14]); - $calls = [['locationHandler', Location\Handler::class, 'loadLocationsByContent', [new Location(['pathString' => '/1/2/43/'])]]]; - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - ['load', [14], 'ibx-u-14', null, null, [['user', [], true]], ['ibx-u'], $user], - [ - 'loadByLogin', - ['admin'], - 'ibx-u-admin-bl', - null, - null, - [ - ['user', [], true], - ['by_login_suffix', [], false], - ], - ['ibx-u', 'bl'], - $user, - ], - [ - 'loadByEmail', - ['admin@link.invalid'], - 'ibx-u-admin_Alink.invalid-be', - null, - null, - [ - ['user', [], true], - ['by_email_suffix', [], false], - ], - ['ibx-u', 'be'], - $user, - ], - [ - 'loadUserByToken', - ['hash'], - 'ibx-u-hash-bak', - null, - null, - [ - ['user', [], true], - ['by_account_key_suffix', [], false], - ], - ['ibx-u', '-bak'], - $user, - ], - ['loadRole', [9], 'ibx-r-9', null, null, [['role', [], true]], ['ibx-r'], $role], - [ - 'loadRoleByIdentifier', - ['member'], - 'ibx-r-member-bi', - null, - null, - [ - ['role', [], true], - ['by_identifier_suffix', [], false], - ], - ['ibx-r', '-bi'], - $role, - ], - ['loadRoleAssignment', [11], 'ibx-ra-11', null, null, [['role_assignment', [], true]], ['ibx-ra'], $roleAssignment], - ['loadRoleAssignmentsByRoleId', [$role->id], 'ibx-ra-9-bro', null, null, [['role_assignment_with_by_role_suffix', [9], true]], ['ibx-ra-9-bro'], [$roleAssignment]], - [ - 'loadRoleAssignmentsByRoleIdWithOffsetAndLimit', - [9, 0, 10], - 'ibx-ra-9-bro-0-10', - null, - null, - [['role_assignment_with_by_role_offset_limit_suffix', [9, 0, 10], true]], - ['ibx-ra-9-bro-0-10'], - [$roleAssignment], - ], - [ - 'loadRoleAssignmentsByGroupId', - [14], - 'ibx-ra-14-bg', - null, - null, - [ - ['role_assignment_with_by_group_suffix', [14], true], - ], - ['ibx-ra-14-bg'], - [$roleAssignment], - false, - $calls, - ], - [ - 'loadRoleAssignmentsByGroupId', - [14, true], - 'ibx-ra-14-bgi', - null, - null, - [['role_assignment_with_by_group_inherited_suffix', [14], true]], - ['ibx-ra-14-bgi'], - [$roleAssignment], - false, - $calls, - ], - ]; - } - - public function providerForCachedLoadMethodsMiss(): array - { - $user = new User(['id' => 14]); - $role = new Role(['id' => 9]); - $roleAssignment = new RoleAssignment(['id' => 11, 'roleId' => 9, 'contentId' => 14]); - $calls = [['locationHandler', Location\Handler::class, 'loadLocationsByContent', [new Location(['pathString' => '/1/2/43/'])]]]; - - // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi - return [ - [ - 'load', - [14], - 'ibx-u-14', - [ - ['content', [14], false], - ['user', [14], false], - ], - ['c-14', 'u-14'], - [ - ['user', [], true], - ], - ['ibx-u'], - $user, - ], - [ - 'loadByLogin', - ['admin'], - 'ibx-u-admin-bl', - [ - ['content', [14], false], - ['user', [14], false], - ], - ['c-14', 'u-14'], - [ - ['user', [], true], - ['by_login_suffix', [], false], - ], - ['ibx-u', 'bl'], - $user, - ], - [ - 'loadByEmail', - ['admin@link.invalid'], - 'ibx-u-admin_Alink.invalid-be', - [ - ['content', [14], false], - ['user', [14], false], - ], - ['c-14', 'u-14'], - [ - ['user', [], true], - ['by_email_suffix', [], false], - ], - ['ibx-u', 'be'], - $user, - ], - [ - 'loadUserByToken', - ['hash'], - 'ibx-u-hash-bak', - [ - ['content', [14], false], - ['user', [14], false], - ['user_with_account_key_suffix', [14], false], - ], - ['c-14', 'u-14', 'u-14-bak'], - [ - ['user', [], true], - ['by_account_key_suffix', [], false], - ], - ['ibx-u', '-bak'], - $user, - ], - [ - 'loadRole', - [9], - 'ibx-r-9', - [ - ['role', [9], false], - ], - ['r-9'], - [ - ['role', [], true], - ], - ['ibx-r'], - $role, - ], - [ - 'loadRoleByIdentifier', - ['member'], - 'ibx-r-member-bi', - [ - ['role', [9], false], - ], - ['r-9'], - [ - ['role', [], true], - ['by_identifier_suffix', [], false], - ], - ['ibx-r', '-bi'], - $role, - ], - [ - 'loadRoleAssignment', - [11], - 'ibx-ra-11', - [ - ['role_assignment', [11], false], - ['role_assignment_group_list', [14], false], - ['role_assignment_role_list', [9], false], - ], - ['ra-11', 'ragl-14', 'rarl-9'], - [ - ['role_assignment', [], true], - ], - ['ibx-ra'], - $roleAssignment, - ], - [ - 'loadRoleAssignmentsByRoleId', - [9], - 'ibx-ra-9-bro', - [ - ['role_assignment_role_list', [9], false], - ['role', [9], false], - ['role_assignment', [11], false], - ['role_assignment_group_list', [14], false], - ['role_assignment_role_list', [9], false], - ], - ['rarl-9', 'r-9', 'ra-11', 'ragl-14', 'rarl-9'], - [ - ['role_assignment_with_by_role_suffix', [9], true], - ], - ['ibx-ra-9-bro'], - [$roleAssignment], - ], - [ - 'loadRoleAssignmentsByRoleIdWithOffsetAndLimit', - [9, 0, 10], - 'ibx-ra-9-bro-0-10', - [ - ['role_assignment_role_list', [9], false], - ['role', [9], false], - ['role_assignment', [11], false], - ['role_assignment_group_list', [14], false], - ['role_assignment_role_list', [9], false], - ], - ['rarl-9', 'r-9', 'ra-11', 'ragl-14', 'rarl-9'], - [ - ['role_assignment_with_by_role_offset_limit_suffix', [9, 0, 10], true], - ], - ['ibx-ra-9-bro-0-10'], - [$roleAssignment], - ], - [ - 'loadRoleAssignmentsByGroupId', - [14], - 'ibx-ra-14-bg', - [ - ['role_assignment_group_list', [14], false], - ['location_path', ['2'], false], - ['location_path', ['43'], false], - ['role_assignment', [11], false], - ['role_assignment_group_list', [14], false], - ['role_assignment_role_list', [9], false], - ], - ['ragl-14', 'lp-2', 'lp-43', 'ra-11', 'ragl-14', 'rarl-9'], - [ - ['role_assignment_with_by_group_suffix', [14], true], - ], - ['ibx-ra-14-bg'], - [$roleAssignment], - false, - $calls, - ], - [ - 'loadRoleAssignmentsByGroupId', - [14, true], - 'ibx-ra-14-bgi', - [ - ['role_assignment_group_list', [14], false], - ['location_path', ['2'], false], - ['location_path', ['43'], false], - ['role_assignment', [11], false], - ['role_assignment_group_list', [14], false], - ['role_assignment_role_list', [9], false], - ], - ['ragl-14', 'lp-2', 'lp-43', 'ra-11', 'ragl-14', 'rarl-9'], - [ - ['role_assignment_with_by_group_inherited_suffix', [14], true], - ], - ['ibx-ra-14-bgi'], - [$roleAssignment], - false, - $calls, - ], - ]; - } - - public function testPublishRoleDraftFromExistingRole() - { - $this->loggerMock->expects($this->once())->method('logCall'); - $innerHandlerMock = $this->createMock(SPIUserHandler::class); - - $this->persistenceHandlerMock - ->expects($this->once()) - ->method('userHandler') - ->willReturn($innerHandlerMock); - - $roleDraftId = 33; - $originalRoleId = 30; - - $innerHandlerMock - ->expects($this->once()) - ->method('loadRole') - ->with($roleDraftId, Role::STATUS_DRAFT) - ->willReturn(new Role(['originalId' => $originalRoleId])); - - $innerHandlerMock - ->expects($this->once()) - ->method('publishRoleDraft') - ->with($roleDraftId); - - $roleTag = 'r-' . $originalRoleId; - - $this->cacheIdentifierGeneratorMock - ->expects($this->once()) - ->method('generateTag') - ->with('role', [$originalRoleId], false) - ->willReturn($roleTag); - - $this->cacheMock - ->expects($this->once()) - ->method('invalidateTags') - ->with([$roleTag]); - - $this->cacheMock - ->expects($this->never()) - ->method('deleteItem'); - - $handler = $this->persistenceCacheHandler->userHandler(); - $handler->publishRoleDraft($roleDraftId); - } - - public function testPublishNewRoleDraft() - { - $this->loggerMock->expects($this->once())->method('logCall'); - $innerHandlerMock = $this->createMock(SPIUserHandler::class); - $this->persistenceHandlerMock - ->expects($this->once()) - ->method('userHandler') - ->willReturn($innerHandlerMock); - $roleDraftId = 33; - $innerHandlerMock - ->expects($this->at(0)) - ->method('loadRole') - ->with($roleDraftId, Role::STATUS_DRAFT) - ->willReturn(new Role(['originalId' => -1])); - $innerHandlerMock - ->expects($this->at(1)) - ->method('publishRoleDraft') - ->with($roleDraftId); - $this->cacheMock - ->expects($this->never()) - ->method($this->anything()); - $handler = $this->persistenceCacheHandler->userHandler(); - $handler->publishRoleDraft($roleDraftId); - } - - public function testAssignRole() - { - $innerUserHandlerMock = $this->createMock(SPIUserHandler::class); - $innerLocationHandlerMock = $this->createMock(SPILocationHandler::class); - - $contentId = 14; - $roleId = 9; - - $this->loggerMock->expects($this->once())->method('logCall'); - - $this->persistenceHandlerMock - ->expects($this->once()) - ->method('userHandler') - ->willReturn($innerUserHandlerMock); - - $innerUserHandlerMock - ->expects($this->once()) - ->method('assignRole') - ->with($contentId, $roleId) - ->willReturn(null); - - $this->persistenceHandlerMock - ->expects($this->once()) - ->method('locationHandler') - ->willReturn($innerLocationHandlerMock); - - $innerLocationHandlerMock - ->expects($this->once()) - ->method('loadLocationsByContent') - ->with($contentId) - ->willReturn([new Location(['id' => '43'])]); - - $tags = ['ragl-14', 'rarl-9', 'lp-43']; - - $this->cacheIdentifierGeneratorMock - ->expects($this->exactly(3)) - ->method('generateTag') - ->withConsecutive( - ['role_assignment_group_list', [14], false], - ['role_assignment_role_list', [9], false], - ['location_path', [43], false] - ) - ->willReturnOnConsecutiveCalls(...$tags); - - $this->cacheMock - ->expects($this->once()) - ->method('invalidateTags') - ->with($tags); - - $this->cacheMock - ->expects($this->never()) - ->method('deleteItem'); - - $handler = $this->persistenceCacheHandler->userHandler(); - $handler->assignRole($contentId, $roleId); - } - - public function testRemoveRoleAssignment(): void - { - $handler = $this->persistenceCacheHandler->userHandler(); - $methodName = 'removeRoleAssignment'; - - $innerHandler = $this->createMock(SPIUserHandler::class); - $this->persistenceHandlerMock->method('userHandler')->willReturn($innerHandler); - $roleAssignmentId = 1; - $contentId = 2; - $roleId = 3; - $innerHandler - ->method('loadRoleAssignment') - ->willReturn( - new RoleAssignment(['id' => $roleAssignmentId, 'contentId' => $contentId, 'roleId' => $roleId]) - ); - - $this->loggerMock->method('logCall')->with( - UserHandler::class . "::$methodName", - [ - 'assignment' => $roleAssignmentId, - 'contentId' => $contentId, - 'roleId' => $roleId, - ] - ); - $innerHandler->method($methodName)->with($roleAssignmentId); - - $tags = [ - "ra-$roleAssignmentId", - "ragl-$contentId", - "rarl-$roleId", - ]; - $this->cacheIdentifierGeneratorMock - ->expects(self::exactly(count($tags))) - ->method('generateTag') - ->withConsecutive(['role_assignment'], ['role_assignment_group_list'], ['role_assignment_role_list']) - ->willReturnOnConsecutiveCalls(...$tags); - - $this->cacheMock->method('invalidateTags')->with($tags); - - $handler->removeRoleAssignment($roleAssignmentId); - } -} diff --git a/eZ/Publish/Core/Persistence/Cache/TransactionHandler.php b/eZ/Publish/Core/Persistence/Cache/TransactionHandler.php deleted file mode 100644 index ffa6c6e22c..0000000000 --- a/eZ/Publish/Core/Persistence/Cache/TransactionHandler.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Cache; - -use eZ\Publish\SPI\Persistence\TransactionHandler as TransactionHandlerInterface; - -/** - * Persistence Transaction Cache Handler class. - */ -class TransactionHandler extends AbstractInMemoryPersistenceHandler implements TransactionHandlerInterface -{ - /** - * {@inheritdoc} - */ - public function beginTransaction() - { - $this->cache->beginTransaction(); - - $this->logger->logCall(__METHOD__); - $this->persistenceHandler->transactionHandler()->beginTransaction(); - } - - /** - * {@inheritdoc} - */ - public function commit() - { - $this->logger->logCall(__METHOD__); - $this->persistenceHandler->transactionHandler()->commit(); - - $this->cache->commitTransaction(); - } - - /** - * {@inheritdoc} - */ - public function rollback() - { - $this->logger->logCall(__METHOD__); - $this->persistenceHandler->transactionHandler()->rollback(); - - $this->cache->rollbackTransaction(); - } -} diff --git a/eZ/Publish/Core/Persistence/FieldType.php b/eZ/Publish/Core/Persistence/FieldType.php deleted file mode 100644 index 33d29e3a20..0000000000 --- a/eZ/Publish/Core/Persistence/FieldType.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence; - -use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\FieldType as FieldTypeInterface; - -/** - * This class represents a FieldType available to SPI users. - * - * @see \eZ\Publish\SPI\FieldType\FieldType - */ -class FieldType implements FieldTypeInterface, FieldTypeInterface\IsEmptyValue -{ - /** - * Holds internal FieldType object. - * - * @var \eZ\Publish\SPI\FieldType\FieldType - */ - protected $internalFieldType; - - /** - * Creates a new FieldType object. - * - * @param \eZ\Publish\SPI\FieldType\FieldType $fieldType - */ - public function __construct(SPIFieldType $fieldType) - { - $this->internalFieldType = $fieldType; - } - - /** - * Returns the empty value for the field type that can be processed by the storage engine. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getEmptyValue() - { - return $this->internalFieldType->toPersistenceValue( - $this->internalFieldType->getEmptyValue() - ); - } - - public function isEmptyValue(FieldValue $fieldValue): bool - { - return $this->internalFieldType->isEmptyValue( - $this->internalFieldType->fromPersistenceValue($fieldValue) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/FieldTypeRegistry.php b/eZ/Publish/Core/Persistence/FieldTypeRegistry.php deleted file mode 100644 index c81d3d2b97..0000000000 --- a/eZ/Publish/Core/Persistence/FieldTypeRegistry.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence; - -use eZ\Publish\Core\Base\Exceptions\NotFound\FieldTypeNotFoundException; -use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType; -use eZ\Publish\SPI\Persistence\FieldType as FieldTypeInterface; - -/** - * Registry for field types available to storage engines. - */ -class FieldTypeRegistry -{ - /** - * Map of FieldTypes where key is field type identifier and value is FieldType object complying - * to {@link \eZ\Publish\SPI\FieldType\FieldType} interface. - * - * @var \eZ\Publish\SPI\FieldType\FieldType[] - */ - protected $coreFieldTypes; - - /** - * Map of FieldTypes where key is field type identifier and value is FieldType object. - * - * @var \eZ\Publish\SPI\Persistence\FieldType[] - */ - protected $fieldTypes; - - /** - * Creates FieldType registry. - * - * In $fieldTypes a mapping of field type identifier to object is expected. - * The FieldType object must comply to the {@link \eZ\Publish\SPI\FieldType\FieldType} interface. - * - * @param \eZ\Publish\Core\Persistence\FieldType[] $coreFieldTypes - * @param \eZ\Publish\SPI\FieldType\FieldType[] $fieldTypes A map where key is field type identifier and value is - * a callable factory to get FieldType OR FieldType object. - */ - public function __construct(array $coreFieldTypes = [], array $fieldTypes = []) - { - $this->coreFieldTypes = $coreFieldTypes; - $this->fieldTypes = $fieldTypes; - } - - /** - * Returns the FieldType object for given $identifier. - * - * @param string $identifier - * - * @return \eZ\Publish\SPI\Persistence\FieldType - * - * @throws \RuntimeException If field type for given $identifier is not instance or callable. - * @throws \eZ\Publish\Core\Base\Exceptions\NotFound\FieldTypeNotFoundException If field type for given $identifier is not found. - */ - public function getFieldType(string $identifier): FieldTypeInterface - { - if (!isset($this->fieldTypes[$identifier])) { - $this->fieldTypes[$identifier] = new FieldType($this->getCoreFieldType($identifier)); - } - - return $this->fieldTypes[$identifier]; - } - - public function register(string $identifier, SPIFieldType $fieldType): void - { - $this->coreFieldTypes[$identifier] = $fieldType; - } - - protected function getCoreFieldType(string $identifier): SPIFieldType - { - if (!isset($this->coreFieldTypes[$identifier])) { - throw new FieldTypeNotFoundException($identifier); - } - - return $this->coreFieldTypes[$identifier]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Bookmark/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/Bookmark/Gateway.php deleted file mode 100644 index 8e69147284..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Bookmark/Gateway.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Bookmark; - -use eZ\Publish\SPI\Persistence\Bookmark\Bookmark; - -/** - * Base class for bookmark gateways. - */ -abstract class Gateway -{ - /** - * Insert a bookmark. - * - * @param \eZ\Publish\SPI\Persistence\Bookmark\Bookmark $bookmark - * - * @return int ID - */ - abstract public function insertBookmark(Bookmark $bookmark): int; - - /** - * Delete bookmark with the given $id. - * - * @param int $id ID of bookmark - */ - abstract public function deleteBookmark(int $id): void; - - /** - * Load data for an bookmark with the given $userId and $locationId. - * - * @param int $userId ID of user - * @param array $locationIds ID of location - * - * @return array - */ - abstract public function loadBookmarkDataByUserIdAndLocationId(int $userId, array $locationIds): array; - - /** - * Load data for all bookmarks owned by given $userId. - * - * @param int $userId ID of user - * @param int $offset Offset to start listing from, 0 by default - * @param int $limit Limit for the listing. -1 by default (no limit) - * - * @return array - */ - abstract public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1): array; - - /** - * Count bookmarks owned by given $userId. - * - * @param int $userId ID of user - * - * @return int - */ - abstract public function countUserBookmarks(int $userId): int; - - /** - * Updates related bookmarks when location was swapped. - * - * @param int $location1Id ID of first location - * @param int $location2Id ID of second location - */ - abstract public function locationSwapped(int $location1Id, int $location2Id): void; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabase.php deleted file mode 100644 index 7673435d89..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,169 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway; - -use Doctrine\DBAL\Connection; -use eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway; -use eZ\Publish\SPI\Persistence\Bookmark\Bookmark; -use PDO; - -class DoctrineDatabase extends Gateway -{ - public const TABLE_BOOKMARKS = 'ezcontentbrowsebookmark'; - - public const COLUMN_ID = 'id'; - public const COLUMN_USER_ID = 'user_id'; - public const COLUMN_LOCATION_ID = 'node_id'; - public const COLUMN_NAME = 'name'; - - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - public function __construct(Connection $connection) - { - $this->connection = $connection; - } - - /** - * {@inheritdoc} - */ - public function insertBookmark(Bookmark $bookmark): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::TABLE_BOOKMARKS) - ->values([ - self::COLUMN_NAME => ':name', - self::COLUMN_USER_ID => ':user_id', - self::COLUMN_LOCATION_ID => ':location_id', - ]) - ->setParameter(':name', $bookmark->name, PDO::PARAM_STR) - ->setParameter(':user_id', $bookmark->userId, PDO::PARAM_INT) - ->setParameter(':location_id', $bookmark->locationId, PDO::PARAM_INT); - - $query->execute(); - - return (int) $this->connection->lastInsertId(); - } - - /** - * {@inheritdoc} - */ - public function deleteBookmark(int $id): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::TABLE_BOOKMARKS) - ->where($query->expr()->eq(self::COLUMN_ID, ':id')) - ->setParameter(':id', $id, PDO::PARAM_INT); - - $query->execute(); - } - - /** - * {@inheritdoc} - */ - public function loadBookmarkDataById(int $id): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select(...$this->getColumns()) - ->from(self::TABLE_BOOKMARKS) - ->where($query->expr()->eq(self::COLUMN_ID, ':id')) - ->setParameter(':id', $id, PDO::PARAM_INT); - - return $query->execute()->fetchAll(PDO::FETCH_ASSOC); - } - - /** - * {@inheritdoc} - */ - public function loadBookmarkDataByUserIdAndLocationId(int $userId, array $locationIds): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select(...$this->getColumns()) - ->from(self::TABLE_BOOKMARKS) - ->where($query->expr()->andX( - $query->expr()->eq(self::COLUMN_USER_ID, ':user_id'), - $query->expr()->in(self::COLUMN_LOCATION_ID, ':location_id') - )) - ->setParameter(':user_id', $userId, PDO::PARAM_INT) - ->setParameter(':location_id', $locationIds, Connection::PARAM_INT_ARRAY); - - return $query->execute()->fetchAll(PDO::FETCH_ASSOC); - } - - /** - * {@inheritdoc} - */ - public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select(...$this->getColumns()) - ->from(self::TABLE_BOOKMARKS) - ->where($query->expr()->eq(self::COLUMN_USER_ID, ':user_id')) - ->setFirstResult($offset); - - if ($limit > 0) { - $query->setMaxResults($limit); - } - - $query->orderBy(self::COLUMN_ID, 'DESC'); - $query->setParameter(':user_id', $userId, PDO::PARAM_INT); - - return $query->execute()->fetchAll(PDO::FETCH_ASSOC); - } - - /** - * {@inheritdoc} - */ - public function countUserBookmarks(int $userId): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('COUNT(' . self::COLUMN_ID . ')') - ->from(self::TABLE_BOOKMARKS) - ->where($query->expr()->eq(self::COLUMN_USER_ID, ':user_id')) - ->setParameter(':user_id', $userId, PDO::PARAM_INT); - - return (int) $query->execute()->fetchColumn(); - } - - /** - * {@inheritdoc} - */ - public function locationSwapped(int $location1Id, int $location2Id): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::TABLE_BOOKMARKS) - ->set(self::COLUMN_LOCATION_ID, '(CASE WHEN node_id = :source_id THEN :target_id ELSE :source_id END)') - ->where($query->expr()->orX( - $query->expr()->eq(self::COLUMN_LOCATION_ID, ':source_id'), - $query->expr()->eq(self::COLUMN_LOCATION_ID, ':target_id') - )); - - $stmt = $this->connection->prepare($query->getSQL()); - $stmt->bindValue('source_id', $location1Id, PDO::PARAM_INT); - $stmt->bindValue('target_id', $location2Id, PDO::PARAM_INT); - $stmt->execute(); - } - - private function getColumns(): array - { - return [ - self::COLUMN_ID, - self::COLUMN_NAME, - self::COLUMN_USER_ID, - self::COLUMN_LOCATION_ID, - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Bookmark/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/Bookmark/Gateway/ExceptionConversion.php deleted file mode 100644 index 9aaf17e437..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Bookmark/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway; -use eZ\Publish\SPI\Persistence\Bookmark\Bookmark; -use PDOException; - -class ExceptionConversion extends Gateway -{ - /** - * @var \eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway - */ - protected $innerGateway; - - /** - * @param \eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function insertBookmark(Bookmark $bookmark): int - { - try { - return $this->innerGateway->insertBookmark($bookmark); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteBookmark(int $id): void - { - try { - $this->innerGateway->deleteBookmark($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadBookmarkDataByUserIdAndLocationId(int $userId, array $locationId): array - { - try { - return $this->innerGateway->loadBookmarkDataByUserIdAndLocationId($userId, $locationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1): array - { - try { - return $this->innerGateway->loadUserBookmarks($userId, $offset, $limit); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countUserBookmarks(int $userId): int - { - try { - return $this->innerGateway->countUserBookmarks($userId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function locationSwapped(int $location1Id, int $location2Id): void - { - try { - $this->innerGateway->locationSwapped($location1Id, $location2Id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Bookmark/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Bookmark/Handler.php deleted file mode 100644 index 935f9d1632..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Bookmark/Handler.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Bookmark; - -use eZ\Publish\SPI\Persistence\Bookmark\Bookmark; -use eZ\Publish\SPI\Persistence\Bookmark\CreateStruct; -use eZ\Publish\SPI\Persistence\Bookmark\Handler as HandlerInterface; - -/** - * Storage Engine handler for bookmarks. - */ -class Handler implements HandlerInterface -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway */ - private $gateway; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Bookmark\Mapper */ - private $mapper; - - /** - * Handler constructor. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway $gateway - * @param \eZ\Publish\Core\Persistence\Legacy\Bookmark\Mapper $mapper - */ - public function __construct(Gateway $gateway, Mapper $mapper) - { - $this->gateway = $gateway; - $this->mapper = $mapper; - } - - /** - * {@inheritdoc} - */ - public function create(CreateStruct $createStruct): Bookmark - { - $bookmark = $this->mapper->createBookmarkFromCreateStruct( - $createStruct - ); - $bookmark->id = $this->gateway->insertBookmark($bookmark); - - return $bookmark; - } - - /** - * {@inheritdoc} - */ - public function delete(int $bookmarkId): void - { - $this->gateway->deleteBookmark($bookmarkId); - } - - /** - * {@inheritdoc} - */ - public function loadByUserIdAndLocationId(int $userId, array $locationIds): array - { - $bookmarks = $this->mapper->extractBookmarksFromRows( - $this->gateway->loadBookmarkDataByUserIdAndLocationId($userId, $locationIds) - ); - - $list = []; - foreach ($bookmarks as $bookmark) { - $list[$bookmark->locationId] = $bookmark; - } - - return $list; - } - - /** - * {@inheritdoc} - */ - public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1): array - { - return $this->mapper->extractBookmarksFromRows( - $this->gateway->loadUserBookmarks($userId, $offset, $limit) - ); - } - - /** - * {@inheritdoc} - */ - public function countUserBookmarks(int $userId): int - { - return $this->gateway->countUserBookmarks($userId); - } - - /** - * {@inheritdoc} - */ - public function locationSwapped(int $location1Id, int $location2Id): void - { - $this->gateway->locationSwapped($location1Id, $location2Id); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Bookmark/Mapper.php b/eZ/Publish/Core/Persistence/Legacy/Bookmark/Mapper.php deleted file mode 100644 index d63dd2a911..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Bookmark/Mapper.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Bookmark; - -use eZ\Publish\SPI\Persistence\Bookmark\Bookmark; -use eZ\Publish\SPI\Persistence\Bookmark\CreateStruct; - -/** - * Bookmark mapper. - */ -class Mapper -{ - /** - * Creates a Bookmark from $createStruct. - * - * @param \eZ\Publish\SPI\Persistence\Bookmark\CreateStruct $createStruct - * - * @return \eZ\Publish\SPI\Persistence\Bookmark\Bookmark - */ - public function createBookmarkFromCreateStruct(CreateStruct $createStruct): Bookmark - { - $bookmark = new Bookmark(); - $bookmark->name = $createStruct->name; - $bookmark->locationId = $createStruct->locationId; - $bookmark->userId = $createStruct->userId; - - return $bookmark; - } - - /** - * Extracts Bookmark objects from $rows. - * - * @param array $rows - * - * @return \eZ\Publish\SPI\Persistence\Bookmark\Bookmark[] - */ - public function extractBookmarksFromRows(array $rows): array - { - $bookmarks = []; - foreach ($rows as $row) { - $bookmarks[] = $this->extractBookmarkFromRow($row); - } - - return $bookmarks; - } - - /** - * Extract Bookmark object from $row. - * - * @param array $row - * - * @return \eZ\Publish\SPI\Persistence\Bookmark\Bookmark - */ - private function extractBookmarkFromRow(array $row): Bookmark - { - $bookmark = new Bookmark(); - $bookmark->id = (int)$row['id']; - $bookmark->name = $row['name']; - $bookmark->userId = (int)$row['user_id']; - $bookmark->locationId = (int)$row['node_id']; - - return $bookmark; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter.php deleted file mode 100644 index 2e85505e70..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue; - -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -/** - * Converter for field values in legacy storage. - */ -interface Converter -{ - /** - * Converts data from $value to $storageFieldValue. - * - * Note: You should not throw on validation here, as it is implicitly used by ContentService->createContentDraft(). - * Rather allow invalid value or omit it to let validation layer in FieldType handle issues when user tried - * to publish the given draft. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue); - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue); - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef); - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef); - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * If the indexing is not supported, this method must return false. - * - * @return string|false - */ - public function getIndexColumn(); -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php deleted file mode 100644 index 6e30e8e274..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php +++ /dev/null @@ -1,157 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use DOMDocument; -use eZ\Publish\Core\FieldType\Author\Type as AuthorType; -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -class AuthorConverter implements Converter -{ - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - $storageFieldValue->dataText = $this->generateXmlString($value->data); - $storageFieldValue->sortKeyString = $value->sortKey; - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - $fieldValue->data = $this->restoreValueFromXmlString($value->dataText); - $fieldValue->sortKey = $value->sortKeyString; - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - $fieldSettings = $fieldDef->fieldTypeConstraints->fieldSettings; - - if ($fieldSettings !== null) { - $storageDef->dataInt1 = (int)$fieldSettings['defaultAuthor']; - } - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'defaultAuthor' => $storageDef->dataInt1 ?? AuthorType::DEFAULT_VALUE_EMPTY, - ] - ); - - $fieldDef->defaultValue->data = []; - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return 'sort_key_string'; - } - - /** - * Generates XML string from $authorValue to be stored in storage engine. - * - * @param array $authorValue - * - * @return string The generated XML string - */ - private function generateXmlString(array $authorValue) - { - $doc = new DOMDocument('1.0', 'utf-8'); - - $root = $doc->createElement('ezauthor'); - $doc->appendChild($root); - - $authors = $doc->createElement('authors'); - $root->appendChild($authors); - - foreach ($authorValue as $author) { - $authorNode = $doc->createElement('author'); - $authorNode->setAttribute('id', $author['id']); - $authorNode->setAttribute('name', $author['name']); - $authorNode->setAttribute('email', $author['email']); - $authors->appendChild($authorNode); - unset($authorNode); - } - - return $doc->saveXML(); - } - - /** - * Restores an author Value object from $xmlString. - * - * @param string $xmlString XML String stored in storage engine - * - * @return \eZ\Publish\Core\FieldType\Author\Value[] - */ - private function restoreValueFromXmlString($xmlString) - { - $dom = new DOMDocument('1.0', 'utf-8'); - $authors = []; - - if ($dom->loadXML($xmlString) === true) { - foreach ($dom->getElementsByTagName('author') as $author) { - $authors[] = [ - 'id' => $author->getAttribute('id'), - 'name' => $author->getAttribute('name'), - 'email' => $author->getAttribute('email'), - ]; - } - } - - return $authors; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/BinaryFileConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/BinaryFileConverter.php deleted file mode 100644 index 438417eeaa..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/BinaryFileConverter.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -class BinaryFileConverter implements Converter -{ - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\BinaryFileConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - $storageDef->dataInt1 = (isset($fieldDef->fieldTypeConstraints->validators['FileSizeValidator']['maxFileSize']) - ? $fieldDef->fieldTypeConstraints->validators['FileSizeValidator']['maxFileSize'] - : 0); - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - $fieldDef->fieldTypeConstraints = new FieldTypeConstraints( - [ - 'validators' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => ($storageDef->dataInt1 != 0 - ? $storageDef->dataInt1 - : null), - ], - ], - ] - ); - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - // @todo: Correct? - return 'sort_key_string'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/CheckboxConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/CheckboxConverter.php deleted file mode 100644 index 6818e907b0..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/CheckboxConverter.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -class CheckboxConverter implements Converter -{ - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - $storageFieldValue->dataInt = (int)$value->data; - $storageFieldValue->sortKeyInt = (int)$value->data; - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - $fieldValue->data = (bool)$value->dataInt; - $fieldValue->sortKey = $value->dataInt; - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - $storageDef->dataInt3 = (int)$fieldDef->defaultValue->data; - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - $fieldDef->defaultValue->data = !empty($storageDef->dataInt3) ? (bool)$storageDef->dataInt3 : false; - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return 'sort_key_int'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php deleted file mode 100644 index 518e6143e7..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php +++ /dev/null @@ -1,108 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -class CountryConverter implements Converter -{ - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - $storageFieldValue->dataText = empty($value->data) ? '' : implode(',', $value->data); - $storageFieldValue->sortKeyString = $value->sortKey; - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - $fieldValue->data = empty($value->dataText) ? null : explode(',', $value->dataText); - $fieldValue->sortKey = $value->sortKeyString; - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - if (isset($fieldDef->fieldTypeConstraints->fieldSettings['isMultiple'])) { - $storageDef->dataInt1 = (int)$fieldDef->fieldTypeConstraints->fieldSettings['isMultiple']; - } - - $storageDef->dataText5 = $fieldDef->defaultValue->data === null - ? '' - : implode(',', $fieldDef->defaultValue->data); - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'isMultiple' => !empty($storageDef->dataInt1) ? (bool)$storageDef->dataInt1 : false, - ] - ); - - $fieldDef->defaultValue->data = empty($storageDef->dataText5) - ? null - : explode(',', $storageDef->dataText5); - // TODO This will contain comma separated country codes, which is correct for value but not for sort key. - // Sort key should contain comma separated lowercased country names. - $fieldDef->defaultValue->sortKey = $storageDef->dataText5; - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return 'sort_key_string'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/DateConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/DateConverter.php deleted file mode 100644 index c2123d4994..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/DateConverter.php +++ /dev/null @@ -1,121 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\FieldType\Date\Type as DateType; -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -/** - * Date field value converter class. - */ -class DateConverter implements Converter -{ - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - $storageFieldValue->dataInt = ($value->data !== null ? $value->data['timestamp'] : null); - $storageFieldValue->sortKeyInt = (int)$value->sortKey; - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - if ($value->dataInt === null || $value->dataInt == 0) { - return; - } - - $fieldValue->data = [ - 'timestamp' => $value->dataInt, - 'rfc850' => null, - ]; - $fieldValue->sortKey = $value->sortKeyInt; - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - $storageDef->dataInt1 = $fieldDef->fieldTypeConstraints->fieldSettings['defaultType'] ?? null; - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'defaultType' => $storageDef->dataInt1, - ] - ); - - // Building default value - switch ($fieldDef->fieldTypeConstraints->fieldSettings['defaultType']) { - case DateType::DEFAULT_CURRENT_DATE: - $data = [ - 'timestamp' => time(), // @deprecated timestamp is no longer used and will be removed in a future version. - 'rfc850' => null, - 'timestring' => 'now', - ]; - break; - default: - $data = null; - } - - $fieldDef->defaultValue->data = $data; - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return 'sort_key_int'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php deleted file mode 100644 index 397aaf8054..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php +++ /dev/null @@ -1,95 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -class EmailAddressConverter implements Converter -{ - public const VALIDATOR_IDENTIFIER = 'EmailAddressValidator'; - - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\EmailAddressConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - $storageFieldValue->dataText = $value->data; - $storageFieldValue->sortKeyString = $value->sortKey; - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - $fieldValue->data = $value->dataText; - $fieldValue->sortKey = $value->sortKeyString; - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - $storageDef->dataText1 = $fieldDef->defaultValue->data; - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - $validatorConstraints = [self::VALIDATOR_IDENTIFIER => []]; - $fieldDef->fieldTypeConstraints->validators = $validatorConstraints; - $fieldDef->defaultValue->data = isset($storageDef->dataText1) ? $storageDef->dataText1 : ''; - $fieldDef->defaultValue->sortKey = $fieldDef->defaultValue->data; - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return 'sort_key_string'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/Exception/NotFound.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/Exception/NotFound.php deleted file mode 100644 index 07acc216b1..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/Exception/NotFound.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception; - -use eZ\Publish\Core\Base\Exceptions\NotFoundException; - -/** - * Exception thrown if no converter for a type was found. - */ -class NotFound extends NotFoundException -{ - /** - * Creates a new exception for $typeName. - * - * @param mixed $typeName - */ - public function __construct($typeName) - { - parent::__construct( - 'eZ\\Publish\\SPI\\Persistence\\Content\\FieldValue\\Converter\\*', - $typeName - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/ISBNConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/ISBNConverter.php deleted file mode 100644 index 15a6699f32..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/ISBNConverter.php +++ /dev/null @@ -1,104 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -class ISBNConverter implements Converter -{ - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ISBNConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - $storageFieldValue->dataText = $value->data; - $storageFieldValue->sortKeyString = $value->sortKey; - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - $fieldValue->data = $value->dataText; - $fieldValue->sortKey = $value->sortKeyString; - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - if (isset($fieldDef->fieldTypeConstraints->fieldSettings['isISBN13'])) { - $storageDef->dataInt1 = $fieldDef->fieldTypeConstraints->fieldSettings['isISBN13']; - } else { - $storageDef->dataInt1 = 1; - } - - $storageDef->dataText1 = $fieldDef->defaultValue->data; - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'isISBN13' => !empty($storageDef->dataInt1) ? (bool)$storageDef->dataInt1 : false, - ] - ); - - $fieldDef->defaultValue->data = $storageDef->dataText1 ?: null; - $fieldDef->defaultValue->sortKey = $storageDef->dataText1 ?: ''; - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return 'sort_key_string'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/ImageAssetConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/ImageAssetConverter.php deleted file mode 100644 index aca3865bbf..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/ImageAssetConverter.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -class ImageAssetConverter implements Converter -{ - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - $storageFieldValue->dataInt = !empty($value->data['destinationContentId']) - ? $value->data['destinationContentId'] - : null; - $storageFieldValue->dataText = !empty($value->data['alternativeText']) - ? $value->data['alternativeText'] - : null; - $storageFieldValue->sortKeyInt = (int)$value->sortKey; - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - $fieldValue->data = [ - 'destinationContentId' => $value->dataInt ?: null, - 'alternativeText' => $value->dataText ?: null, - ]; - $fieldValue->sortKey = (int)$value->sortKeyInt; - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return 'sort_key_string'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/KeywordConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/KeywordConverter.php deleted file mode 100644 index ad89dc15df..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/KeywordConverter.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -class KeywordConverter implements Converter -{ - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - $storageFieldValue->sortKeyString = $value->sortKey; - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - $fieldValue->data = []; - $fieldValue->sortKey = $value->sortKeyString; - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return 'sort_key_string'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/MapLocationConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/MapLocationConverter.php deleted file mode 100644 index 0cec9061d3..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/MapLocationConverter.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -class MapLocationConverter implements Converter -{ - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\MapLocationConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - $storageFieldValue->dataInt = isset($value->externalData['address']) ? 1 : 0; - $storageFieldValue->dataText = ''; - $storageFieldValue->sortKeyString = (string)$value->sortKey; - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return 'sort_key_string'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/MediaConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/MediaConverter.php deleted file mode 100644 index 47523b7cd7..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/MediaConverter.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\FieldType\Media\Type as MediaType; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -class MediaConverter extends BinaryFileConverter -{ - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\MediaConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - parent::toStorageFieldDefinition($fieldDef, $storageDef); - - $storageDef->dataText1 = (isset($fieldDef->fieldTypeConstraints->fieldSettings['mediaType']) - ? $fieldDef->fieldTypeConstraints->fieldSettings['mediaType'] - : MediaType::TYPE_HTML5_VIDEO); - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - parent::toFieldDefinition($storageDef, $fieldDef); - $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'mediaType' => $storageDef->dataText1, - ] - ); - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return false; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/NullConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/NullConverter.php deleted file mode 100644 index 431b41f78f..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/NullConverter.php +++ /dev/null @@ -1,95 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -/** - * The Null converter does not perform any conversions at all. - */ -class NullConverter implements Converter -{ - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - // There is no contained data. All data is external. So we just do - // nothing here. - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - // There is no contained data. All data is external. So we just do - // nothing here. - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - // There is no contained data. All data is external. So we just do - // nothing here. - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - // There is no contained data. All data is external. So we just do - // nothing here. - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return false; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverter.php deleted file mode 100644 index 5f0c9cc00b..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverter.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter as ConverterInterface; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\FieldType\ValueSerializerInterface; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -final class SerializableConverter implements ConverterInterface -{ - /** @var \eZ\Publish\SPI\FieldType\ValueSerializerInterface */ - private $serializer; - - public function __construct(ValueSerializerInterface $serializer) - { - $this->serializer = $serializer; - } - - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue): void - { - $data = $value->data; - if ($data !== null) { - $data = $this->serializer->encode($data); - } - - $storageFieldValue->dataText = $data; - $storageFieldValue->sortKeyString = (string)$value->sortKey; - } - - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue): void - { - $data = $value->dataText; - if ($data !== null) { - $data = $this->serializer->decode($data); - } - - $fieldValue->data = $data; - $fieldValue->sortKey = $value->sortKeyString; - } - - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef): void - { - $settings = $fieldDef->fieldTypeConstraints->fieldSettings; - if ($settings !== null) { - $settings = $this->serializer->encode((array)$settings); - } - - $storageDef->dataText5 = $settings; - } - - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef): void - { - $settings = $storageDef->dataText5; - if ($settings !== null) { - $settings = new FieldSettings($this->serializer->decode($settings)); - } - - $fieldDef->fieldTypeConstraints->fieldSettings = $settings; - } - - public function getIndexColumn(): string - { - return 'sort_key_string'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/TextBlockConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/TextBlockConverter.php deleted file mode 100644 index af414c8446..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/TextBlockConverter.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -class TextBlockConverter implements Converter -{ - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - $storageFieldValue->dataText = $value->data; - $storageFieldValue->sortKeyString = $value->sortKey; - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - $fieldValue->data = $value->dataText; - $fieldValue->sortKey = $value->sortKeyString; - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - if (isset($fieldDef->fieldTypeConstraints->fieldSettings['textRows'])) { - $storageDef->dataInt1 = $fieldDef->fieldTypeConstraints->fieldSettings['textRows']; - } - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'textRows' => $storageDef->dataInt1, - ] - ); - $fieldDef->defaultValue->data = null; - $fieldDef->defaultValue->sortKey = ''; - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return 'sort_key_string'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php deleted file mode 100644 index 63c48a4ca2..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php +++ /dev/null @@ -1,121 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -class TextLineConverter implements Converter -{ - public const STRING_LENGTH_VALIDATOR_IDENTIFIER = 'StringLengthValidator'; - - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - $storageFieldValue->dataText = $value->data; - $storageFieldValue->sortKeyString = $value->sortKey; - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - $fieldValue->data = $value->dataText; - $fieldValue->sortKey = $value->sortKeyString; - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - if (isset($fieldDef->fieldTypeConstraints->validators[self::STRING_LENGTH_VALIDATOR_IDENTIFIER]['maxStringLength'])) { - $storageDef->dataInt1 = $fieldDef->fieldTypeConstraints->validators[self::STRING_LENGTH_VALIDATOR_IDENTIFIER]['maxStringLength']; - } else { - $storageDef->dataInt1 = 0; - } - - if (isset($fieldDef->fieldTypeConstraints->validators[self::STRING_LENGTH_VALIDATOR_IDENTIFIER]['minStringLength'])) { - $storageDef->dataInt2 = $fieldDef->fieldTypeConstraints->validators[self::STRING_LENGTH_VALIDATOR_IDENTIFIER]['minStringLength']; - } else { - $storageDef->dataInt2 = 0; - } - - $storageDef->dataText1 = $fieldDef->defaultValue->data; - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - $validatorConstraints = []; - - if (isset($storageDef->dataInt1)) { - $validatorConstraints[self::STRING_LENGTH_VALIDATOR_IDENTIFIER]['maxStringLength'] = - $storageDef->dataInt1 != 0 ? - (int)$storageDef->dataInt1 : - null; - } - if (isset($storageDef->dataInt2)) { - $validatorConstraints[self::STRING_LENGTH_VALIDATOR_IDENTIFIER]['minStringLength'] = - $storageDef->dataInt2 != 0 ? - (int)$storageDef->dataInt2 : - null; - } - - $fieldDef->fieldTypeConstraints->validators = $validatorConstraints; - $fieldDef->defaultValue->data = $storageDef->dataText1 ?: null; - $fieldDef->defaultValue->sortKey = $storageDef->dataText1 ?: ''; - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return 'sort_key_string'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/TimeConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/TimeConverter.php deleted file mode 100644 index c7065b99e2..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/TimeConverter.php +++ /dev/null @@ -1,122 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use DateTime; -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\FieldType\Time\Type as TimeType; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -/** - * Time field value converter class. - */ -class TimeConverter implements Converter -{ - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - $storageFieldValue->dataInt = $value->data; - $storageFieldValue->sortKeyInt = (int)$value->sortKey; - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - if ($value->dataInt === null) { - return; - } - - $fieldValue->data = $value->dataInt; - $fieldValue->sortKey = $value->sortKeyInt; - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - $fieldSettings = $fieldDef->fieldTypeConstraints->fieldSettings; - - if ($fieldSettings !== null) { - $storageDef->dataInt1 = $fieldSettings['defaultType']; - $storageDef->dataInt2 = $fieldSettings['useSeconds'] ? 1 : 0; - } - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'defaultType' => $storageDef->dataInt1, - 'useSeconds' => (bool)$storageDef->dataInt2, - ] - ); - - // Building default value - switch ($fieldDef->fieldTypeConstraints->fieldSettings['defaultType']) { - case TimeType::DEFAULT_CURRENT_TIME: - $dateTime = new DateTime(); - $data = $dateTime->getTimestamp() - $dateTime->setTime(0, 0, 0)->getTimestamp(); - break; - default: - $data = null; - } - - $fieldDef->defaultValue->data = $data; - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return string - */ - public function getIndexColumn() - { - return 'sort_key_int'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/UrlConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/UrlConverter.php deleted file mode 100644 index 4704a96950..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/UrlConverter.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -class UrlConverter implements Converter -{ - /** - * Factory for current class. - * - * Note: Class should instead be configured as service if it gains dependencies. - * - * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter - */ - public static function create() - { - return new self(); - } - - /** - * Converts data from $value to $storageFieldValue. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue - */ - public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) - { - $storageFieldValue->dataText = isset($value->data['text']) - ? $value->data['text'] - : null; - $storageFieldValue->dataInt = isset($value->data['urlId']) - ? $value->data['urlId'] - : null; - } - - /** - * Converts data from $value to $fieldValue. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - */ - public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) - { - $fieldValue->data = [ - 'urlId' => $value->dataInt, - 'text' => $value->dataText, - ]; - $fieldValue->sortKey = false; - } - - /** - * Converts field definition data in $fieldDef into $storageFieldDef. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - */ - public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) - { - } - - /** - * Converts field definition data in $storageDef into $fieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) - { - // @todo: Is it possible to store a default value in the DB? - $fieldDef->defaultValue = new FieldValue(); - $fieldDef->defaultValue->data = ['text' => null]; - } - - /** - * Returns the name of the index column in the attribute table. - * - * Returns the name of the index column the datatype uses, which is either - * "sort_key_int" or "sort_key_string". This column is then used for - * filtering and sorting for this type. - * - * @return false - */ - public function getIndexColumn() - { - return false; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/Content/Gateway.php deleted file mode 100644 index e591b2b0aa..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Gateway.php +++ /dev/null @@ -1,469 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content; - -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\UpdateStruct; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Base class for content gateways. - * - * @internal For internal use by Persistence Handlers. - */ -abstract class Gateway -{ - public const CONTENT_ITEM_TABLE = 'ezcontentobject'; - public const CONTENT_NAME_TABLE = 'ezcontentobject_name'; - public const CONTENT_FIELD_TABLE = 'ezcontentobject_attribute'; - public const CONTENT_VERSION_TABLE = 'ezcontentobject_version'; - public const CONTENT_RELATION_TABLE = 'ezcontentobject_link'; - - public const CONTENT_ITEM_SEQ = 'ezcontentobject_id_seq'; - public const CONTENT_VERSION_SEQ = 'ezcontentobject_version_id_seq'; - public const CONTENT_FIELD_SEQ = 'ezcontentobject_attribute_id_seq'; - public const CONTENT_RELATION_SEQ = 'ezcontentobject_link_id_seq'; - - /** - * Insert a new Content item. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function insertContentObject( - CreateStruct $struct, - int $currentVersionNo = 1 - ): int; - - /** - * Insert a new Version. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field[] $fields - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function insertVersion(VersionInfo $versionInfo, array $fields): int; - - /** - * Update an existing content identified by $contentId based on $struct. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function updateContent( - int $contentId, - MetadataUpdateStruct $struct, - ?VersionInfo $prePublishVersionInfo = null - ): void; - - /** - * Updates version $versionNo for content identified by $contentId, in respect to $struct. - */ - abstract public function updateVersion(int $contentId, int $versionNo, UpdateStruct $struct): void; - - /** - * Update "always available" flag for content identified by $contentId based on $alwaysAvailable. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function updateAlwaysAvailableFlag( - int $contentId, - ?bool $newAlwaysAvailable = null - ): void; - - /** - * Set the state of object identified by $contentId and $version to $state. - * - * @param int $status the one of STATUS_DRAFT, STATUS_PUBLISHED, STATUS_ARCHIVED - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - */ - abstract public function setStatus(int $contentId, int $version, int $status): bool; - - /** - * Dedicated operation which sets Version status as published, similar to setStatus, but checking - * state of all versions to avoid race conditions. - * - * IMPORTANT: This method expects prior published version to have been set to another status then published before called, otherwise you'll get a BadStateException. - * - * @see setStatus - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if other operation affected publishing process - */ - abstract public function setPublishedStatus(int $contentId, int $status): void; - - /** - * Insert a new field. - * - * Only used when a new field is created (i.e. a new object or a field in a - * new language!). After that, field IDs need to stay the same, only the - * version number changes. - */ - abstract public function insertNewField( - Content $content, - Field $field, - StorageFieldValue $value - ): int; - - /** - * Insert an existing field. - * - * Used to insert a field with an existing ID but a new version number. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function insertExistingField( - Content $content, - Field $field, - StorageFieldValue $value - ): void; - - /** - * Update an existing field. - */ - abstract public function updateField(Field $field, StorageFieldValue $value): void; - - /** - * Update an existing, non-translatable field. - */ - abstract public function updateNonTranslatableField( - Field $field, - StorageFieldValue $value, - int $contentId - ): void; - - /** - * Load data for a content object. - * - * Returns an array with the relevant data. - * - * @param int|null $version Current version on null value. - * @param string[]|null $translations - */ - abstract public function load( - int $contentId, - ?int $version = null, - ?array $translations = null - ): array; - - /** - * Load current versions for a list of content item numeric IDs. - * - * @param int[] $contentIds - * @param string[]|null $translations If languages is not set, ALL will be loaded. - * - * @return array[] - */ - abstract public function loadContentList(array $contentIds, ?array $translations = null): array; - - /** - * Load info for a content object identified by its remote ID. - * - * Returns an array with the relevant data. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function loadContentInfoByRemoteId(string $remoteId): array; - - /** - * Load info for a content object identified by its location ID (node ID). - * - * Returns an array with the relevant data. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function loadContentInfoByLocationId(int $locationId): array; - - /** - * Load info for content identified by $contentId. - * Will basically return a hash containing all field values for ezcontentobject table plus following keys: - * - always_available => Boolean indicating if content's language mask contains alwaysAvailable bit field - * - main_language_code => Language code for main (initial) language. E.g. "eng-GB". - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function loadContentInfo(int $contentId): array; - - /** - * Loads rows of info for content identified by $contentIds. - * - * @see loadContentInfo For the returned structure. - * @see \eZ\Publish\SPI\Persistence\Content\Handler::loadContentInfoList For how this will only return items found and not throw. - * - * @param int[] $contentIds - */ - abstract public function loadContentInfoList(array $contentIds): array; - - /** - * Load version info for content identified by $contentId and $versionNo. - * Will basically return a hash containing all field values from ezcontentobject_version table plus following keys: - * - names => Hash of content object names. Key is the language code, value is the name. - * - languages => Hash of language ids. Key is the language code (e.g. "eng-GB"), value is the language numeric id without the always available bit. - * - initial_language_code => Language code for initial language in this version. - * - * @param int|null $versionNo Load current version if null. - */ - abstract public function loadVersionInfo(int $contentId, ?int $versionNo = null): array; - - /** - * Return the number of all versions with given status created by the given $userId. - */ - abstract public function countVersionsForUser( - int $userId, - int $status = VersionInfo::STATUS_DRAFT - ): int; - - /** - * Returns data for all versions with given status created by the given $userId. - * - * @return string[][] - */ - abstract public function listVersionsForUser( - int $userId, - int $status = VersionInfo::STATUS_DRAFT - ); - - /** - * Return data for all versions with given status created by the given $userId when content is not in the trash. - * - * The list is sorted by modification date. - */ - abstract public function loadVersionsForUser( - int $userId, - int $status = VersionInfo::STATUS_DRAFT, - int $offset = 0, - int $limit = -1 - ): array; - - /** - * Returns all version data for the given $contentId. - * - * Result is returned with oldest version first (using version id as it has index and is auto increment). - * - * @param int|null $status Optional argument to filter versions by status, like {@see VersionInfo::STATUS_ARCHIVED}. - * @param int $limit Limit for items returned, -1 means none. - * - * @return string[][] - */ - abstract public function listVersions( - int $contentId, - ?int $status = null, - int $limit = -1 - ): array; - - /** - * Return all version numbers for the given $contentId. - * - * @return int[] - */ - abstract public function listVersionNumbers(int $contentId): array; - - /** - * Return last version number for content identified by $contentId. - */ - abstract public function getLastVersionNumber(int $contentId): int; - - /** - * Returns all IDs for locations that refer to $contentId. - * - * @return int[] - */ - abstract public function getAllLocationIds(int $contentId): array; - - /** - * Returns all field IDs of $contentId grouped by their type. - * If $versionNo is set only field IDs for that version are returned. - * If $languageCode is set, only field IDs for that language are returned. - * - * @return int[][] - */ - abstract public function getFieldIdsByType( - int $contentId, - ?int $versionNo = null, - ?string $languageCode = null - ): array; - - /** - * Deletes relations to and from $contentId. - * If $versionNo is set only relations for that version are deleted. - */ - abstract public function deleteRelations(int $contentId, ?int $versionNo = null): void; - - /** - * Remove relations to Content with $contentId from Relation and RelationList field type fields. - */ - abstract public function removeReverseFieldRelations(int $contentId): void; - - /** - * Removes orphaned relations resulting from deleted relation fieldtype. - */ - abstract public function removeRelationsByFieldDefinitionId(int $fieldDefinitionId): void; - - /** - * Delete the field with the given $fieldId. - */ - abstract public function deleteField(int $fieldId): void; - - /** - * Delete all fields of $contentId in all versions. - * - * If $versionNo is set only fields for that version are deleted. - */ - abstract public function deleteFields(int $contentId, ?int $versionNo = null): void; - - /** - * Delete all versions of $contentId. - * - * If $versionNo is set only that version is deleted. - */ - abstract public function deleteVersions(int $contentId, ?int $versionNo = null): void; - - /** - * Delete all names of $contentId. - * - * If $versionNo is set only names for that version are deleted. - */ - abstract public function deleteNames(int $contentId, ?int $versionNo = null): void; - - /** - * Set the content object name. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function setName( - int $contentId, - int $version, - string $name, - string $languageCode - ): void; - - /** - * Delete the actual content object referred to by $contentId. - */ - abstract public function deleteContent(int $contentId): void; - - /** - * Load data of related to/from $contentId. - * - * @return array Content data, array structured like {@see \eZ\Publish\Core\Persistence\Legacy\Content\Gateway::load()} - */ - abstract public function loadRelations( - int $contentId, - ?int $contentVersionNo = null, - ?int $relationType = null - ): array; - - /** - * Count number of related to/from $contentId. - */ - abstract public function countReverseRelations(int $contentId, ?int $relationType = null): int; - - /** - * Load data of related to/from $contentId. - * - * @return array Content data, array structured like {@see \eZ\Publish\Core\Persistence\Legacy\Content\Gateway::load()} - */ - abstract public function loadReverseRelations(int $contentId, ?int $relationType = null): array; - - /** - * Load paginated data of related to/from $contentId. - */ - abstract public function listReverseRelations( - int $contentId, - int $offset = 0, - int $limit = -1, - ?int $relationType = null - ): array; - - /** - * Delete the relation with the given $relationId. - * - * @param int $type one of Relation type constants. - * - * @see \eZ\Publish\API\Repository\Values\Content\Relation - */ - abstract public function deleteRelation(int $relationId, int $type): void; - - /** - * Insert a new content relation. - */ - abstract public function insertRelation(RelationCreateStruct $createStruct): int; - - /** - * Return all Content IDs for the given $contentTypeId. - * - * @return int[] - */ - abstract public function getContentIdsByContentTypeId(int $contentTypeId): array; - - /** - * Load name data for set of content id's and corresponding version number. - * - * @param array[] $rows array of hashes with 'id' and 'version' to load names for - */ - abstract public function loadVersionedNameData(array $rows): array; - - /** - * Bulk-copy all relations meta data for a copied Content item. - * - * Is meant to be used during content copy, so assumes the following: - * - version number is the same - * - content type, and hence content type attribute is the same - * - relation type is the same - * - target relation is the same - * - * @param int|null $versionNo If specified only copy for a given version number, otherwise all. - */ - abstract public function copyRelations( - int $originalContentId, - int $copiedContentId, - ?int $versionNo = null - ): void; - - /** - * Remove the specified translation from the Content Object Version. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - */ - abstract public function deleteTranslationFromContent( - int $contentId, - string $languageCode - ): void; - - /** - * Delete Content fields (attributes) for the given Translation. - * - * If $versionNo is given, fields for that Version only will be deleted. - */ - abstract public function deleteTranslatedFields( - string $languageCode, - int $contentId, - ?int $versionNo = null - ): void; - - /** - * Delete the specified Translation from the given Version. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - */ - abstract public function deleteTranslationFromVersion( - int $contentId, - int $versionNo, - string $languageCode - ): void; - - /** - * @param array<int> $contentIds - * - * @throws \eZ\Publish\Core\Base\Exceptions\DatabaseException - */ - abstract public function loadVersionInfoList(array $contentIds): array; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php deleted file mode 100644 index d4692a7d19..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,2006 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder as DoctrineQueryBuilder; -use DOMDocument; -use DOMXPath; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException as NotFound; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\Core\Persistence\Legacy\SharedGateway\Gateway as SharedGateway; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\UpdateStruct; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Doctrine database based content gateway. - * - * @internal Gateway implementation is considered internal. Use Persistence Content Handler instead. - * - * @see \eZ\Publish\SPI\Persistence\Content\Handler - */ -final class DoctrineDatabase extends Gateway -{ - /** - * Pre-computed integer constant which, when combined with proper bit-wise operator, - * removes always available flag from the mask. - */ - private const REMOVE_ALWAYS_AVAILABLE_LANG_MASK_OPERAND = -2; - - /** - * The native Doctrine connection. - * - * Meant to be used to transition from eZ/Zeta interface to Doctrine. - * - * @var \Doctrine\DBAL\Connection - */ - protected $connection; - - /** - * Query builder. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder - */ - protected $queryBuilder; - - /** - * Caching language handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler - */ - protected $languageHandler; - - /** - * Language mask generator. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator - */ - protected $languageMaskGenerator; - - /** @var \eZ\Publish\Core\Persistence\Legacy\SharedGateway\Gateway */ - private $sharedGateway; - - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */ - private $databasePlatform; - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct( - Connection $connection, - SharedGateway $sharedGateway, - QueryBuilder $queryBuilder, - LanguageHandler $languageHandler, - LanguageMaskGenerator $languageMaskGenerator - ) { - $this->connection = $connection; - $this->databasePlatform = $connection->getDatabasePlatform(); - $this->sharedGateway = $sharedGateway; - $this->queryBuilder = $queryBuilder; - $this->languageHandler = $languageHandler; - $this->languageMaskGenerator = $languageMaskGenerator; - } - - public function insertContentObject(CreateStruct $struct, int $currentVersionNo = 1): int - { - $initialLanguageId = !empty($struct->mainLanguageId) ? $struct->mainLanguageId : $struct->initialLanguageId; - $initialLanguageCode = $this->languageHandler->load($initialLanguageId)->languageCode; - - $name = $struct->name[$initialLanguageCode] ?? ''; - - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::CONTENT_ITEM_TABLE) - ->values( - [ - 'current_version' => $query->createPositionalParameter( - $currentVersionNo, - ParameterType::INTEGER - ), - 'name' => $query->createPositionalParameter($name), - 'contentclass_id' => $query->createPositionalParameter( - $struct->typeId, - ParameterType::INTEGER - ), - 'section_id' => $query->createPositionalParameter( - $struct->sectionId, - ParameterType::INTEGER - ), - 'owner_id' => $query->createPositionalParameter( - $struct->ownerId, - ParameterType::INTEGER - ), - 'initial_language_id' => $query->createPositionalParameter( - $initialLanguageId, - ParameterType::INTEGER - ), - 'remote_id' => $query->createPositionalParameter($struct->remoteId), - 'modified' => $query->createPositionalParameter(0, ParameterType::INTEGER), - 'published' => $query->createPositionalParameter(0, ParameterType::INTEGER), - 'status' => $query->createPositionalParameter( - ContentInfo::STATUS_DRAFT, - ParameterType::INTEGER - ), - 'language_mask' => $query->createPositionalParameter( - $this->languageMaskGenerator->generateLanguageMaskForFields( - $struct->fields, - $initialLanguageCode, - $struct->alwaysAvailable - ), - ParameterType::INTEGER - ), - ] - ); - - $query->execute(); - - return (int)$this->connection->lastInsertId(self::CONTENT_ITEM_SEQ); - } - - public function insertVersion(VersionInfo $versionInfo, array $fields): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::CONTENT_VERSION_TABLE) - ->values( - [ - 'version' => $query->createPositionalParameter( - $versionInfo->versionNo, - ParameterType::INTEGER - ), - 'modified' => $query->createPositionalParameter( - $versionInfo->modificationDate, - ParameterType::INTEGER - ), - 'creator_id' => $query->createPositionalParameter( - $versionInfo->creatorId, - ParameterType::INTEGER - ), - 'created' => $query->createPositionalParameter( - $versionInfo->creationDate, - ParameterType::INTEGER - ), - 'status' => $query->createPositionalParameter( - $versionInfo->status, - ParameterType::INTEGER - ), - 'initial_language_id' => $query->createPositionalParameter( - $this->languageHandler->loadByLanguageCode( - $versionInfo->initialLanguageCode - )->id, - ParameterType::INTEGER - ), - 'contentobject_id' => $query->createPositionalParameter( - $versionInfo->contentInfo->id, - ParameterType::INTEGER - ), - 'language_mask' => $query->createPositionalParameter( - $this->languageMaskGenerator->generateLanguageMaskForFields( - $fields, - $versionInfo->initialLanguageCode, - $versionInfo->contentInfo->alwaysAvailable - ), - ParameterType::INTEGER - ), - ] - ); - - $query->execute(); - - return (int)$this->connection->lastInsertId(self::CONTENT_VERSION_SEQ); - } - - public function updateContent( - int $contentId, - MetadataUpdateStruct $struct, - ?VersionInfo $prePublishVersionInfo = null - ): void { - $query = $this->connection->createQueryBuilder(); - $query->update(self::CONTENT_ITEM_TABLE); - - $fieldsForUpdateMap = [ - 'name' => [ - 'value' => $struct->name, - 'type' => ParameterType::STRING, - ], - 'initial_language_id' => [ - 'value' => $struct->mainLanguageId, - 'type' => ParameterType::INTEGER, - ], - 'modified' => [ - 'value' => $struct->modificationDate, - 'type' => ParameterType::INTEGER, - ], - 'owner_id' => [ - 'value' => $struct->ownerId, - 'type' => ParameterType::INTEGER, - ], - 'published' => [ - 'value' => $struct->publicationDate, - 'type' => ParameterType::INTEGER, - ], - 'remote_id' => [ - 'value' => $struct->remoteId, - 'type' => ParameterType::STRING, - ], - 'is_hidden' => [ - 'value' => $struct->isHidden, - 'type' => ParameterType::BOOLEAN, - ], - ]; - - foreach ($fieldsForUpdateMap as $fieldName => $field) { - if (null === $field['value']) { - continue; - } - $query->set( - $fieldName, - $query->createNamedParameter($field['value'], $field['type'], ":{$fieldName}") - ); - } - - if ($prePublishVersionInfo !== null) { - $mask = $this->languageMaskGenerator->generateLanguageMaskFromLanguageCodes( - $prePublishVersionInfo->languageCodes, - $struct->alwaysAvailable ?? $prePublishVersionInfo->contentInfo->alwaysAvailable - ); - $query->set( - 'language_mask', - $query->createNamedParameter($mask, ParameterType::INTEGER, ':languageMask') - ); - } - - $query->where( - $query->expr()->eq( - 'id', - $query->createNamedParameter($contentId, ParameterType::INTEGER, ':contentId') - ) - ); - - if (!empty($query->getQueryPart('set'))) { - $query->execute(); - } - - // Handle alwaysAvailable flag update separately as it's a more complex task and has impact on several tables - if (isset($struct->alwaysAvailable) || isset($struct->mainLanguageId)) { - $this->updateAlwaysAvailableFlag($contentId, $struct->alwaysAvailable); - } - } - - /** - * Updates version $versionNo for content identified by $contentId, in respect to $struct. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function updateVersion(int $contentId, int $versionNo, UpdateStruct $struct): void - { - $query = $this->connection->createQueryBuilder(); - - $query - ->update(self::CONTENT_VERSION_TABLE) - ->set('creator_id', ':creator_id') - ->set('modified', ':modified') - ->set('initial_language_id', ':initial_language_id') - ->set( - 'language_mask', - $this->databasePlatform->getBitOrComparisonExpression( - 'language_mask', - ':language_mask' - ) - ) - ->setParameter('creator_id', $struct->creatorId, ParameterType::INTEGER) - ->setParameter('modified', $struct->modificationDate, ParameterType::INTEGER) - ->setParameter( - 'initial_language_id', - $struct->initialLanguageId, - ParameterType::INTEGER - ) - ->setParameter( - 'language_mask', - $this->languageMaskGenerator->generateLanguageMaskForFields( - $struct->fields, - $this->languageHandler->load($struct->initialLanguageId)->languageCode, - false - ), - ParameterType::INTEGER - ) - ->where('contentobject_id = :content_id') - ->andWhere('version = :version_no') - ->setParameter('content_id', $contentId, ParameterType::INTEGER) - ->setParameter('version_no', $versionNo, ParameterType::INTEGER); - - $query->execute(); - } - - public function updateAlwaysAvailableFlag(int $contentId, ?bool $alwaysAvailable = null): void - { - // We will need to know some info on the current language mask to update the flag - // everywhere needed - $contentInfoRow = $this->loadContentInfo($contentId); - $versionNo = (int)$contentInfoRow['current_version']; - $languageMask = (int)$contentInfoRow['language_mask']; - $initialLanguageId = (int)$contentInfoRow['initial_language_id']; - if (!isset($alwaysAvailable)) { - $alwaysAvailable = 1 === ($languageMask & 1); - } - - $this->updateContentItemAlwaysAvailableFlag($contentId, $alwaysAvailable); - $this->updateContentNameAlwaysAvailableFlag( - $contentId, - $versionNo, - $alwaysAvailable - ); - $this->updateContentFieldsAlwaysAvailableFlag( - $contentId, - $versionNo, - $alwaysAvailable, - $languageMask, - $initialLanguageId - ); - } - - private function updateContentItemAlwaysAvailableFlag( - int $contentId, - bool $alwaysAvailable - ): void { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->update(self::CONTENT_ITEM_TABLE); - $this - ->setLanguageMaskForUpdateQuery($alwaysAvailable, $query, 'language_mask') - ->where( - $expr->eq( - 'id', - $query->createNamedParameter($contentId, ParameterType::INTEGER, ':contentId') - ) - ); - $query->execute(); - } - - private function updateContentNameAlwaysAvailableFlag( - int $contentId, - int $versionNo, - bool $alwaysAvailable - ): void { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->update(self::CONTENT_NAME_TABLE); - $this - ->setLanguageMaskForUpdateQuery($alwaysAvailable, $query, 'language_id') - ->where( - $expr->eq( - 'contentobject_id', - $query->createNamedParameter($contentId, ParameterType::INTEGER, ':contentId') - ) - ) - ->andWhere( - $expr->eq( - 'content_version', - $query->createNamedParameter($versionNo, ParameterType::INTEGER, ':versionNo') - ) - ); - $query->execute(); - } - - private function updateContentFieldsAlwaysAvailableFlag( - int $contentId, - int $versionNo, - bool $alwaysAvailable, - int $languageMask, - int $initialLanguageId - ): void { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->update(self::CONTENT_FIELD_TABLE) - ->where( - $expr->eq( - 'contentobject_id', - $query->createNamedParameter($contentId, ParameterType::INTEGER, ':contentId') - ) - ) - ->andWhere( - $expr->eq( - 'version', - $query->createNamedParameter($versionNo, ParameterType::INTEGER, ':versionNo') - ) - ); - - // If there is only a single language, update all fields and return - if (!$this->languageMaskGenerator->isLanguageMaskComposite($languageMask)) { - $this->setLanguageMaskForUpdateQuery($alwaysAvailable, $query, 'language_id'); - - $query->execute(); - - return; - } - - // Otherwise: - // 1. Remove always available flag on all fields - $query - ->set( - 'language_id', - $this->databasePlatform->getBitAndComparisonExpression( - 'language_id', - ':languageMaskOperand' - ) - ) - ->setParameter('languageMaskOperand', self::REMOVE_ALWAYS_AVAILABLE_LANG_MASK_OPERAND) - ; - $query->execute(); - $query->resetQueryPart('set'); - - // 2. If Content is always available set the flag only on fields in main language - if ($alwaysAvailable) { - $query - ->set( - 'language_id', - $this->databasePlatform->getBitOrComparisonExpression( - 'language_id', - ':languageMaskOperand' - ) - ) - ->setParameter( - 'languageMaskOperand', - $alwaysAvailable ? 1 : self::REMOVE_ALWAYS_AVAILABLE_LANG_MASK_OPERAND - ); - - $query->andWhere( - $expr->gt( - $this->databasePlatform->getBitAndComparisonExpression( - 'language_id', - $query->createNamedParameter($initialLanguageId, ParameterType::INTEGER, ':initialLanguageId') - ), - $query->createNamedParameter(0, ParameterType::INTEGER, ':zero') - ) - ); - $query->execute(); - } - } - - public function setStatus(int $contentId, int $version, int $status): bool - { - if ($status !== APIVersionInfo::STATUS_PUBLISHED) { - $query = $this->queryBuilder->getSetVersionStatusQuery($contentId, $version, $status); - $rowCount = $query->execute(); - - return $rowCount > 0; - } else { - // If the version's status is PUBLISHED, we use dedicated method for publishing - $this->setPublishedStatus($contentId, $version); - - return true; - } - } - - public function setPublishedStatus(int $contentId, int $versionNo): void - { - $query = $this->queryBuilder->getSetVersionStatusQuery( - $contentId, - $versionNo, - VersionInfo::STATUS_PUBLISHED - ); - - /* this part allows set status `published` only if there is no other published version of the content */ - $notExistPublishedVersion = <<<SQL - NOT EXISTS ( - SELECT 1 FROM ( - SELECT 1 FROM ezcontentobject_version - WHERE contentobject_id = :contentId AND status = :status - ) as V - ) - SQL; - - $query->andWhere($notExistPublishedVersion); - if (0 === $query->execute()) { - throw new BadStateException( - '$contentId', - "Someone just published another version of Content item {$contentId}" - ); - } - $this->markContentAsPublished($contentId, $versionNo); - } - - private function markContentAsPublished(int $contentId, int $versionNo): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update('ezcontentobject') - ->set('status', ':status') - ->set('current_version', ':versionNo') - ->where('id =:contentId') - ->setParameter('status', ContentInfo::STATUS_PUBLISHED, ParameterType::INTEGER) - ->setParameter('versionNo', $versionNo, ParameterType::INTEGER) - ->setParameter('contentId', $contentId, ParameterType::INTEGER); - $query->execute(); - } - - /** - * @return int ID - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function insertNewField(Content $content, Field $field, StorageFieldValue $value): int - { - $query = $this->connection->createQueryBuilder(); - - $this->setInsertFieldValues($query, $content, $field, $value); - - // Insert with auto increment ID - $nextId = $this->sharedGateway->getColumnNextIntegerValue( - self::CONTENT_FIELD_TABLE, - 'id', - self::CONTENT_FIELD_SEQ - ); - // avoid trying to insert NULL to trigger default column value behavior - if (null !== $nextId) { - $query - ->setValue('id', ':field_id') - ->setParameter('field_id', $nextId, ParameterType::INTEGER); - } - - $query->execute(); - - return (int)$this->sharedGateway->getLastInsertedId(self::CONTENT_FIELD_SEQ); - } - - public function insertExistingField( - Content $content, - Field $field, - StorageFieldValue $value - ): void { - $query = $this->connection->createQueryBuilder(); - - $this->setInsertFieldValues($query, $content, $field, $value); - - $query - ->setValue('id', ':field_id') - ->setParameter('field_id', $field->id, ParameterType::INTEGER); - - $query->execute(); - } - - /** - * Set the given query field (ezcontentobject_attribute) values. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - private function setInsertFieldValues( - DoctrineQueryBuilder $query, - Content $content, - Field $field, - StorageFieldValue $value - ): void { - $query - ->insert(self::CONTENT_FIELD_TABLE) - ->values( - [ - 'contentobject_id' => ':content_id', - 'contentclassattribute_id' => ':field_definition_id', - 'data_type_string' => ':data_type_string', - 'language_code' => ':language_code', - 'version' => ':version_no', - 'data_float' => ':data_float', - 'data_int' => ':data_int', - 'data_text' => ':data_text', - 'sort_key_int' => ':sort_key_int', - 'sort_key_string' => ':sort_key_string', - 'language_id' => ':language_id', - ] - ) - ->setParameter( - 'content_id', - $content->versionInfo->contentInfo->id, - ParameterType::INTEGER - ) - ->setParameter('field_definition_id', $field->fieldDefinitionId, ParameterType::INTEGER) - ->setParameter('data_type_string', $field->type, ParameterType::STRING) - ->setParameter('language_code', $field->languageCode, ParameterType::STRING) - ->setParameter('version_no', $field->versionNo, ParameterType::INTEGER) - ->setParameter('data_float', $value->dataFloat) - ->setParameter('data_int', $value->dataInt, ParameterType::INTEGER) - ->setParameter('data_text', $value->dataText, ParameterType::STRING) - ->setParameter('sort_key_int', $value->sortKeyInt, ParameterType::INTEGER) - ->setParameter( - 'sort_key_string', - mb_substr((string)$value->sortKeyString, 0, 255), - ParameterType::STRING - ) - ->setParameter( - 'language_id', - $this->languageMaskGenerator->generateLanguageIndicator( - $field->languageCode, - $this->isLanguageAlwaysAvailable($content, $field->languageCode) - ), - ParameterType::INTEGER - ); - } - - /** - * Check if $languageCode is always available in $content. - */ - private function isLanguageAlwaysAvailable(Content $content, string $languageCode): bool - { - return - $content->versionInfo->contentInfo->alwaysAvailable && - $content->versionInfo->contentInfo->mainLanguageCode === $languageCode - ; - } - - public function updateField(Field $field, StorageFieldValue $value): void - { - // Note, no need to care for language_id here, since Content->$alwaysAvailable - // cannot change on update - $query = $this->connection->createQueryBuilder(); - $this->setFieldUpdateValues($query, $value); - $query - ->where('id = :field_id') - ->andWhere('version = :version_no') - ->setParameter('field_id', $field->id, ParameterType::INTEGER) - ->setParameter('version_no', $field->versionNo, ParameterType::INTEGER); - - $query->execute(); - } - - /** - * Set update fields on $query based on $value. - */ - private function setFieldUpdateValues( - DoctrineQueryBuilder $query, - StorageFieldValue $value - ): void { - $query - ->update(self::CONTENT_FIELD_TABLE) - ->set('data_float', ':data_float') - ->set('data_int', ':data_int') - ->set('data_text', ':data_text') - ->set('sort_key_int', ':sort_key_int') - ->set('sort_key_string', ':sort_key_string') - ->setParameter('data_float', $value->dataFloat) - ->setParameter('data_int', $value->dataInt, ParameterType::INTEGER) - ->setParameter('data_text', $value->dataText, ParameterType::STRING) - ->setParameter('sort_key_int', $value->sortKeyInt, ParameterType::INTEGER) - ->setParameter('sort_key_string', mb_substr((string)$value->sortKeyString, 0, 255)) - ; - } - - /** - * Update an existing, non-translatable field. - */ - public function updateNonTranslatableField( - Field $field, - StorageFieldValue $value, - int $contentId - ): void { - // Note, no need to care for language_id here, since Content->$alwaysAvailable - // cannot change on update - $query = $this->connection->createQueryBuilder(); - $this->setFieldUpdateValues($query, $value); - $query - ->where('contentclassattribute_id = :field_definition_id') - ->andWhere('contentobject_id = :content_id') - ->andWhere('version = :version_no') - ->setParameter('field_definition_id', $field->fieldDefinitionId, ParameterType::INTEGER) - ->setParameter('content_id', $contentId, ParameterType::INTEGER) - ->setParameter('version_no', $field->versionNo, ParameterType::INTEGER); - - $query->execute(); - } - - public function load(int $contentId, ?int $version = null, ?array $translations = null): array - { - return $this->internalLoadContent([$contentId], $version, $translations); - } - - public function loadContentList(array $contentIds, ?array $translations = null): array - { - return $this->internalLoadContent($contentIds, null, $translations); - } - - /** - * Build query for the <code>load</code> and <code>loadContentList</code> methods. - * - * @param int[] $contentIds - * @param string[]|null $translations a list of language codes - * - * @see load(), loadContentList() - */ - private function internalLoadContent( - array $contentIds, - ?int $version = null, - ?array $translations = null - ): array { - $queryBuilder = $this->connection->createQueryBuilder(); - $expr = $queryBuilder->expr(); - $queryBuilder - ->select( - 'c.id AS ezcontentobject_id', - 'c.contentclass_id AS ezcontentobject_contentclass_id', - 'c.section_id AS ezcontentobject_section_id', - 'c.owner_id AS ezcontentobject_owner_id', - 'c.remote_id AS ezcontentobject_remote_id', - 'c.current_version AS ezcontentobject_current_version', - 'c.initial_language_id AS ezcontentobject_initial_language_id', - 'c.modified AS ezcontentobject_modified', - 'c.published AS ezcontentobject_published', - 'c.status AS ezcontentobject_status', - 'c.name AS ezcontentobject_name', - 'c.language_mask AS ezcontentobject_language_mask', - 'c.is_hidden AS ezcontentobject_is_hidden', - 'v.id AS ezcontentobject_version_id', - 'v.version AS ezcontentobject_version_version', - 'v.modified AS ezcontentobject_version_modified', - 'v.creator_id AS ezcontentobject_version_creator_id', - 'v.created AS ezcontentobject_version_created', - 'v.status AS ezcontentobject_version_status', - 'v.language_mask AS ezcontentobject_version_language_mask', - 'v.initial_language_id AS ezcontentobject_version_initial_language_id', - 'a.id AS ezcontentobject_attribute_id', - 'a.contentclassattribute_id AS ezcontentobject_attribute_contentclassattribute_id', - 'a.data_type_string AS ezcontentobject_attribute_data_type_string', - 'a.language_code AS ezcontentobject_attribute_language_code', - 'a.language_id AS ezcontentobject_attribute_language_id', - 'a.data_float AS ezcontentobject_attribute_data_float', - 'a.data_int AS ezcontentobject_attribute_data_int', - 'a.data_text AS ezcontentobject_attribute_data_text', - 'a.sort_key_int AS ezcontentobject_attribute_sort_key_int', - 'a.sort_key_string AS ezcontentobject_attribute_sort_key_string', - 't.main_node_id AS ezcontentobject_tree_main_node_id' - ) - ->from('ezcontentobject', 'c') - ->innerJoin( - 'c', - 'ezcontentobject_version', - 'v', - $expr->andX( - $expr->eq('c.id', 'v.contentobject_id'), - $expr->eq('v.version', $version ?? 'c.current_version') - ) - ) - ->innerJoin( - 'v', - 'ezcontentobject_attribute', - 'a', - $expr->andX( - $expr->eq('v.contentobject_id', 'a.contentobject_id'), - $expr->eq('v.version', 'a.version') - ) - ) - ->leftJoin( - 'c', - 'ezcontentobject_tree', - 't', - $expr->andX( - $expr->eq('c.id', 't.contentobject_id'), - $expr->eq('t.node_id', 't.main_node_id') - ) - ); - - $queryBuilder->where( - $expr->in( - 'c.id', - $queryBuilder->createNamedParameter($contentIds, Connection::PARAM_INT_ARRAY) - ) - ); - - if (!empty($translations)) { - $queryBuilder->andWhere( - $expr->in( - 'a.language_code', - $queryBuilder->createNamedParameter($translations, Connection::PARAM_STR_ARRAY) - ) - ); - } - - return $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadContentInfo(int $contentId): array - { - $queryBuilder = $this->queryBuilder->createLoadContentInfoQueryBuilder(); - $queryBuilder - ->where('c.id = :id') - ->setParameter('id', $contentId, ParameterType::INTEGER); - - $results = $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); - if (empty($results)) { - throw new NotFound('content', "id: $contentId"); - } - - return $results[0]; - } - - public function loadContentInfoList(array $contentIds): array - { - $queryBuilder = $this->queryBuilder->createLoadContentInfoQueryBuilder(); - $queryBuilder - ->where('c.id IN (:ids)') - ->setParameter('ids', $contentIds, Connection::PARAM_INT_ARRAY); - - return $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadContentInfoByRemoteId(string $remoteId): array - { - $queryBuilder = $this->queryBuilder->createLoadContentInfoQueryBuilder(); - $queryBuilder - ->where('c.remote_id = :id') - ->setParameter('id', $remoteId, ParameterType::STRING); - - $results = $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); - if (empty($results)) { - throw new NotFound('content', "remote_id: $remoteId"); - } - - return $results[0]; - } - - public function loadContentInfoByLocationId(int $locationId): array - { - $queryBuilder = $this->queryBuilder->createLoadContentInfoQueryBuilder(false); - $queryBuilder - ->where('t.node_id = :id') - ->setParameter('id', $locationId, ParameterType::INTEGER); - - $results = $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); - if (empty($results)) { - throw new NotFound('content', "node_id: $locationId"); - } - - return $results[0]; - } - - public function loadVersionInfo(int $contentId, ?int $versionNo = null): array - { - $queryBuilder = $this->queryBuilder->createVersionInfoFindQueryBuilder(); - $expr = $queryBuilder->expr(); - - $queryBuilder - ->where( - $expr->eq( - 'v.contentobject_id', - $queryBuilder->createNamedParameter( - $contentId, - ParameterType::INTEGER, - ':content_id' - ) - ) - ); - - if (null !== $versionNo) { - $queryBuilder - ->andWhere( - $expr->eq( - 'v.version', - $queryBuilder->createNamedParameter( - $versionNo, - ParameterType::INTEGER, - ':version_no' - ) - ) - ); - } else { - $queryBuilder->andWhere($expr->eq('v.version', 'c.current_version')); - } - - return $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function countVersionsForUser(int $userId, int $status = VersionInfo::STATUS_DRAFT): int - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select($this->databasePlatform->getCountExpression('v.id')) - ->from('ezcontentobject_version', 'v') - ->innerJoin( - 'v', - 'ezcontentobject', - 'c', - $expr->andX( - $expr->eq('c.id', 'v.contentobject_id'), - $expr->neq('c.status', ContentInfo::STATUS_TRASHED) - ) - ) - ->where( - $query->expr()->andX( - $query->expr()->eq('v.status', ':status'), - $query->expr()->eq('v.creator_id', ':user_id') - ) - ) - ->setParameter(':status', $status, ParameterType::INTEGER) - ->setParameter(':user_id', $userId, ParameterType::INTEGER); - - return (int) $query->execute()->fetchColumn(); - } - - /** - * Return data for all versions with the given status created by the given $userId. - * - * @return string[][] - */ - public function listVersionsForUser(int $userId, int $status = VersionInfo::STATUS_DRAFT): array - { - $query = $this->queryBuilder->createVersionInfoFindQueryBuilder(); - $query - ->where('v.status = :status') - ->andWhere('v.creator_id = :user_id') - ->setParameter('status', $status, ParameterType::INTEGER) - ->setParameter('user_id', $userId, ParameterType::INTEGER) - ->orderBy('v.id'); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadVersionsForUser( - int $userId, - int $status = VersionInfo::STATUS_DRAFT, - int $offset = 0, - int $limit = -1 - ): array { - $query = $this->queryBuilder->createVersionInfoFindQueryBuilder(); - $expr = $query->expr(); - $query->where( - $expr->andX( - $expr->eq('v.status', ':status'), - $expr->eq('v.creator_id', ':user_id'), - $expr->neq('c.status', ContentInfo::STATUS_TRASHED) - ) - ) - ->setFirstResult($offset) - ->setParameter(':status', $status, ParameterType::INTEGER) - ->setParameter(':user_id', $userId, ParameterType::INTEGER); - - if ($limit > 0) { - $query->setMaxResults($limit); - } - - $query->orderBy('v.modified', 'DESC'); - $query->addOrderBy('v.id', 'DESC'); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function listVersions(int $contentId, ?int $status = null, int $limit = -1): array - { - $query = $this->queryBuilder->createVersionInfoFindQueryBuilder(); - $query - ->where('v.contentobject_id = :content_id') - ->setParameter('content_id', $contentId, ParameterType::INTEGER); - - if ($status !== null) { - $query - ->andWhere('v.status = :status') - ->setParameter('status', $status); - } - - if ($limit > 0) { - $query->setMaxResults($limit); - } - - $query->orderBy('v.id'); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - /** - * @return int[] - */ - public function listVersionNumbers(int $contentId): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('version') - ->from(self::CONTENT_VERSION_TABLE) - ->where('contentobject_id = :contentId') - ->groupBy('version') - ->setParameter('contentId', $contentId, ParameterType::INTEGER); - - return array_map('intval', $query->execute()->fetchAll(FetchMode::COLUMN)); - } - - public function getLastVersionNumber(int $contentId): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select($this->databasePlatform->getMaxExpression('version')) - ->from(self::CONTENT_VERSION_TABLE) - ->where('contentobject_id = :content_id') - ->setParameter('content_id', $contentId, ParameterType::INTEGER); - - $statement = $query->execute(); - - return (int)$statement->fetchColumn(); - } - - /** - * @return int[] - */ - public function getAllLocationIds(int $contentId): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('node_id') - ->from('ezcontentobject_tree') - ->where('contentobject_id = :content_id') - ->setParameter('content_id', $contentId, ParameterType::INTEGER); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::COLUMN); - } - - /** - * @return int[][] - */ - public function getFieldIdsByType( - int $contentId, - ?int $versionNo = null, - ?string $languageCode = null - ): array { - $query = $this->connection->createQueryBuilder(); - $query - ->select('id', 'data_type_string') - ->from(self::CONTENT_FIELD_TABLE) - ->where('contentobject_id = :content_id') - ->setParameter('content_id', $contentId, ParameterType::INTEGER); - - if (null !== $versionNo) { - $query - ->andWhere('version = :version_no') - ->setParameter('version_no', $versionNo, ParameterType::INTEGER); - } - - if (!empty($languageCode)) { - $query - ->andWhere('language_code = :language_code') - ->setParameter('language_code', $languageCode, ParameterType::STRING); - } - - $statement = $query->execute(); - - $result = []; - foreach ($statement->fetchAll(FetchMode::ASSOCIATIVE) as $row) { - if (!isset($result[$row['data_type_string']])) { - $result[$row['data_type_string']] = []; - } - $result[$row['data_type_string']][] = (int)$row['id']; - } - - return $result; - } - - public function deleteRelations(int $contentId, ?int $versionNo = null): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::CONTENT_RELATION_TABLE) - ->where('from_contentobject_id = :content_id') - ->setParameter('content_id', $contentId, ParameterType::INTEGER); - - if (null !== $versionNo) { - $query - ->andWhere('from_contentobject_version = :version_no') - ->setParameter('version_no', $versionNo, ParameterType::INTEGER); - } else { - $query->orWhere('to_contentobject_id = :content_id'); - } - - $query->execute(); - } - - public function removeReverseFieldRelations(int $contentId): void - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select(['a.id', 'a.version', 'a.data_type_string', 'a.data_text']) - ->from(self::CONTENT_FIELD_TABLE, 'a') - ->innerJoin( - 'a', - 'ezcontentobject_link', - 'l', - $expr->andX( - 'l.from_contentobject_id = a.contentobject_id', - 'l.from_contentobject_version = a.version', - 'l.contentclassattribute_id = a.contentclassattribute_id' - ) - ) - ->where('l.to_contentobject_id = :content_id') - ->andWhere( - $expr->gt( - $this->databasePlatform->getBitAndComparisonExpression( - 'l.relation_type', - ':relation_type' - ), - 0 - ) - ) - ->setParameter('content_id', $contentId, ParameterType::INTEGER) - ->setParameter('relation_type', Relation::FIELD, ParameterType::INTEGER); - - $statement = $query->execute(); - - while ($row = $statement->fetch(FetchMode::ASSOCIATIVE)) { - if ($row['data_type_string'] === 'ezobjectrelation') { - $this->removeRelationFromRelationField($row); - } - - if ($row['data_type_string'] === 'ezobjectrelationlist') { - $this->removeRelationFromRelationListField($contentId, $row); - } - } - } - - public function removeRelationsByFieldDefinitionId(int $fieldDefinitionId): void - { - $query = $this->connection->createQueryBuilder(); - $query->delete(self::CONTENT_RELATION_TABLE) - ->where('contentclassattribute_id = :field_definition_id') - ->setParameter('field_definition_id', $fieldDefinitionId, ParameterType::INTEGER); - - $query->execute(); - } - - /** - * Update field value of RelationList field type identified by given $row data, - * removing relations toward given $contentId. - * - * @param array $row - */ - private function removeRelationFromRelationListField(int $contentId, array $row): void - { - $document = new DOMDocument('1.0', 'utf-8'); - $document->loadXML($row['data_text']); - - $xpath = new DOMXPath($document); - $xpathExpression = "//related-objects/relation-list/relation-item[@contentobject-id='{$contentId}']"; - - $relationItems = $xpath->query($xpathExpression); - foreach ($relationItems as $relationItem) { - $relationItem->parentNode->removeChild($relationItem); - } - - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::CONTENT_FIELD_TABLE) - ->set('data_text', ':data_text') - ->setParameter('data_text', $document->saveXML(), ParameterType::STRING) - ->where('id = :attribute_id') - ->andWhere('version = :version_no') - ->setParameter('attribute_id', (int)$row['id'], ParameterType::INTEGER) - ->setParameter('version_no', (int)$row['version'], ParameterType::INTEGER); - - $query->execute(); - } - - /** - * Update field value of Relation field type identified by given $row data, - * removing relation data. - * - * @param array $row - */ - private function removeRelationFromRelationField(array $row): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::CONTENT_FIELD_TABLE) - ->set('data_int', ':data_int') - ->set('sort_key_int', ':sort_key_int') - ->setParameter('data_int', null, ParameterType::NULL) - ->setParameter('sort_key_int', 0, ParameterType::INTEGER) - ->where('id = :attribute_id') - ->andWhere('version = :version_no') - ->setParameter('attribute_id', (int)$row['id'], ParameterType::INTEGER) - ->setParameter('version_no', (int)$row['version'], ParameterType::INTEGER); - - $query->execute(); - } - - public function deleteField(int $fieldId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::CONTENT_FIELD_TABLE) - ->where('id = :field_id') - ->setParameter('field_id', $fieldId, ParameterType::INTEGER) - ; - - $query->execute(); - } - - public function deleteFields(int $contentId, ?int $versionNo = null): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::CONTENT_FIELD_TABLE) - ->where('contentobject_id = :content_id') - ->setParameter('content_id', $contentId, ParameterType::INTEGER); - - if (null !== $versionNo) { - $query - ->andWhere('version = :version_no') - ->setParameter('version_no', $versionNo, ParameterType::INTEGER); - } - - $query->execute(); - } - - public function deleteVersions(int $contentId, ?int $versionNo = null): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::CONTENT_VERSION_TABLE) - ->where('contentobject_id = :content_id') - ->setParameter('content_id', $contentId, ParameterType::INTEGER); - - if (null !== $versionNo) { - $query - ->andWhere('version = :version_no') - ->setParameter('version_no', $versionNo, ParameterType::INTEGER); - } - - $query->execute(); - } - - public function deleteNames(int $contentId, int $versionNo = null): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::CONTENT_NAME_TABLE) - ->where('contentobject_id = :content_id') - ->setParameter('content_id', $contentId, ParameterType::INTEGER); - - if (isset($versionNo)) { - $query - ->andWhere('content_version = :version_no') - ->setParameter('version_no', $versionNo, ParameterType::INTEGER); - } - - $query->execute(); - } - - /** - * Query Content name table to find if a name record for the given parameters exists. - */ - private function contentNameExists(int $contentId, int $version, string $languageCode): bool - { - $query = $this->connection->createQueryBuilder(); - $query - ->select($this->databasePlatform->getCountExpression('contentobject_id')) - ->from(self::CONTENT_NAME_TABLE) - ->where('contentobject_id = :content_id') - ->andWhere('content_version = :version_no') - ->andWhere('content_translation = :language_code') - ->setParameter('content_id', $contentId, ParameterType::INTEGER) - ->setParameter('version_no', $version, ParameterType::INTEGER) - ->setParameter('language_code', $languageCode, ParameterType::STRING); - - $stmt = $query->execute(); - - return (int)$stmt->fetch(FetchMode::COLUMN) > 0; - } - - public function setName(int $contentId, int $version, string $name, string $languageCode): void - { - $language = $this->languageHandler->loadByLanguageCode($languageCode); - - $query = $this->connection->createQueryBuilder(); - - // prepare parameters - $query - ->setParameter('name', $name, ParameterType::STRING) - ->setParameter('content_id', $contentId, ParameterType::INTEGER) - ->setParameter('version_no', $version, ParameterType::INTEGER) - ->setParameter('language_id', $language->id, ParameterType::INTEGER) - ->setParameter('language_code', $language->languageCode, ParameterType::STRING) - ; - - if (!$this->contentNameExists($contentId, $version, $language->languageCode)) { - $query - ->insert(self::CONTENT_NAME_TABLE) - ->values( - [ - 'contentobject_id' => ':content_id', - 'content_version' => ':version_no', - 'content_translation' => ':language_code', - 'name' => ':name', - 'language_id' => $this->getSetNameLanguageMaskSubQuery(), - 'real_translation' => ':language_code', - ] - ); - } else { - $query - ->update(self::CONTENT_NAME_TABLE) - ->set('name', ':name') - ->set('language_id', $this->getSetNameLanguageMaskSubQuery()) - ->set('real_translation', ':language_code') - ->where('contentobject_id = :content_id') - ->andWhere('content_version = :version_no') - ->andWhere('content_translation = :language_code'); - } - - $query->execute(); - } - - /** - * Return a language sub select query for setName. - * - * The query generates the proper language mask at the runtime of the INSERT/UPDATE query - * generated by setName. - * - * @see setName - */ - private function getSetNameLanguageMaskSubQuery(): string - { - return <<<SQL - (SELECT - CASE - WHEN (initial_language_id = :language_id AND (language_mask & :language_id) <> 0 ) - THEN (:language_id | 1) - ELSE :language_id - END - FROM ezcontentobject - WHERE id = :content_id) - SQL; - } - - public function deleteContent(int $contentId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::CONTENT_ITEM_TABLE) - ->where('id = :content_id') - ->setParameter('content_id', $contentId, ParameterType::INTEGER) - ; - - $query->execute(); - } - - public function loadRelations( - int $contentId, - ?int $contentVersionNo = null, - ?int $relationType = null - ): array { - $query = $this->queryBuilder->createRelationFindQueryBuilder(); - $expr = $query->expr(); - $query - ->innerJoin( - 'l', - 'ezcontentobject', - 'ezcontentobject_to', - $expr->andX( - 'l.to_contentobject_id = ezcontentobject_to.id', - 'ezcontentobject_to.status = :status' - ) - ) - ->where( - 'l.from_contentobject_id = :content_id' - ) - ->setParameter( - 'status', - ContentInfo::STATUS_PUBLISHED, - ParameterType::INTEGER - ) - ->setParameter('content_id', $contentId, ParameterType::INTEGER); - - // source version number - if (null !== $contentVersionNo) { - $query - ->andWhere('l.from_contentobject_version = :version_no') - ->setParameter('version_no', $contentVersionNo, ParameterType::INTEGER); - } else { - // from published version only - $query - ->innerJoin( - 'ezcontentobject_to', - 'ezcontentobject', - 'c', - $expr->andX( - 'c.id = l.from_contentobject_id', - 'c.current_version = l.from_contentobject_version' - ) - ); - } - - // relation type - if (null !== $relationType) { - $query - ->andWhere( - $expr->gt( - $this->databasePlatform->getBitAndComparisonExpression( - 'l.relation_type', - ':relation_type' - ), - 0 - ) - ) - ->setParameter('relation_type', $relationType, ParameterType::INTEGER); - } - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function countReverseRelations(int $toContentId, ?int $relationType = null): int - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select($this->databasePlatform->getCountExpression('l.id')) - ->from(self::CONTENT_RELATION_TABLE, 'l') - ->innerJoin( - 'l', - 'ezcontentobject', - 'c', - $expr->andX( - $expr->eq('l.from_contentobject_id', 'c.id'), - $expr->eq('l.from_contentobject_version', 'c.current_version'), - $expr->eq('c.status', ':status') - ) - ) - ->where( - $expr->eq('l.to_contentobject_id', ':to_content_id') - ) - ->setParameter('to_content_id', $toContentId, ParameterType::INTEGER) - ->setParameter('status', ContentInfo::STATUS_PUBLISHED, ParameterType::INTEGER) - ; - - // relation type - if ($relationType !== null) { - $query->andWhere( - $expr->gt( - $this->databasePlatform->getBitAndComparisonExpression( - 'l.relation_type', - $relationType - ), - 0 - ) - ); - } - - return (int)$query->execute()->fetchColumn(); - } - - public function loadReverseRelations(int $toContentId, ?int $relationType = null): array - { - $query = $this->queryBuilder->createRelationFindQueryBuilder(); - $expr = $query->expr(); - $query - ->join( - 'l', - 'ezcontentobject', - 'c', - $expr->andX( - 'c.id = l.from_contentobject_id', - 'c.current_version = l.from_contentobject_version', - 'c.status = :status' - ) - ) - ->where('l.to_contentobject_id = :to_content_id') - ->setParameter('to_content_id', $toContentId, ParameterType::INTEGER) - ->setParameter( - 'status', - ContentInfo::STATUS_PUBLISHED, - ParameterType::INTEGER - ); - - // relation type - if (null !== $relationType) { - $query->andWhere( - $expr->gt( - $this->databasePlatform->getBitAndComparisonExpression( - 'l.relation_type', - ':relation_type' - ), - 0 - ) - ) - ->setParameter('relation_type', $relationType, ParameterType::INTEGER); - } - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function listReverseRelations( - int $toContentId, - int $offset = 0, - int $limit = -1, - ?int $relationType = null - ): array { - $query = $this->queryBuilder->createRelationFindQueryBuilder(); - $expr = $query->expr(); - $query - ->innerJoin( - 'l', - 'ezcontentobject', - 'c', - $expr->andX( - $expr->eq('l.from_contentobject_id', 'c.id'), - $expr->eq('l.from_contentobject_version', 'c.current_version'), - $expr->eq('c.status', ContentInfo::STATUS_PUBLISHED) - ) - ) - ->where( - $expr->eq('l.to_contentobject_id', ':toContentId') - ) - ->setParameter(':toContentId', $toContentId, ParameterType::INTEGER); - - // relation type - if ($relationType !== null) { - $query->andWhere( - $expr->gt( - $this->databasePlatform->getBitAndComparisonExpression( - 'l.relation_type', - $relationType - ), - 0 - ) - ); - } - $query->setFirstResult($offset); - if ($limit > 0) { - $query->setMaxResults($limit); - } - $query->orderBy('l.id', 'DESC'); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function insertRelation(RelationCreateStruct $createStruct): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::CONTENT_RELATION_TABLE) - ->values( - [ - 'contentclassattribute_id' => ':field_definition_id', - 'from_contentobject_id' => ':from_content_id', - 'from_contentobject_version' => ':from_version_no', - 'relation_type' => ':relation_type', - 'to_contentobject_id' => ':to_content_id', - ] - ) - ->setParameter( - 'field_definition_id', - (int)$createStruct->sourceFieldDefinitionId, - ParameterType::INTEGER - ) - ->setParameter( - 'from_content_id', - $createStruct->sourceContentId, - ParameterType::INTEGER - ) - ->setParameter( - 'from_version_no', - $createStruct->sourceContentVersionNo, - ParameterType::INTEGER - ) - ->setParameter('relation_type', $createStruct->type, ParameterType::INTEGER) - ->setParameter( - 'to_content_id', - $createStruct->destinationContentId, - ParameterType::INTEGER - ); - - $query->execute(); - - return (int)$this->connection->lastInsertId(self::CONTENT_RELATION_SEQ); - } - - public function deleteRelation(int $relationId, int $type): void - { - // Legacy Storage stores COMMON, LINK and EMBED types using bitmask, therefore first load - // existing relation type by given $relationId for comparison - $query = $this->connection->createQueryBuilder(); - $query - ->select('relation_type') - ->from(self::CONTENT_RELATION_TABLE) - ->where('id = :relation_id') - ->setParameter('relation_id', $relationId, ParameterType::INTEGER) - ; - - $loadedRelationType = $query->execute()->fetchColumn(); - - if (!$loadedRelationType) { - return; - } - - $query = $this->connection->createQueryBuilder(); - // If relation type matches then delete - if (((int)$loadedRelationType) === ((int)$type)) { - $query - ->delete(self::CONTENT_RELATION_TABLE) - ->where('id = :relation_id') - ->setParameter('relation_id', $relationId, ParameterType::INTEGER) - ; - - $query->execute(); - } elseif ($loadedRelationType & $type) { - // If relation type is composite update bitmask - - $query - ->update(self::CONTENT_RELATION_TABLE) - ->set( - 'relation_type', - // make & operation removing given $type from the bitmask - $this->databasePlatform->getBitAndComparisonExpression( - 'relation_type', - ':relation_type' - ) - ) - // set the relation type as needed for the above & expression - ->setParameter('relation_type', ~$type, ParameterType::INTEGER) - ->where('id = :relation_id') - ->setParameter('relation_id', $relationId, ParameterType::INTEGER) - ; - - $query->execute(); - } - } - - /** - * @return int[] - */ - public function getContentIdsByContentTypeId(int $contentTypeId): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('id') - ->from(self::CONTENT_ITEM_TABLE) - ->where('contentclass_id = :content_type_id') - ->setParameter('content_type_id', $contentTypeId, ParameterType::INTEGER); - - $statement = $query->execute(); - - return array_map('intval', $statement->fetchAll(FetchMode::COLUMN)); - } - - public function loadVersionedNameData(array $rows): array - { - $query = $this->queryBuilder->createNamesQuery(); - $expr = $query->expr(); - $conditions = []; - foreach ($rows as $row) { - $conditions[] = $expr->andX( - $expr->eq( - 'contentobject_id', - $query->createPositionalParameter($row['id'], ParameterType::INTEGER) - ), - $expr->eq( - 'content_version', - $query->createPositionalParameter($row['version'], ParameterType::INTEGER) - ), - ); - } - - $query->where($expr->orX(...$conditions)); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function copyRelations( - int $originalContentId, - int $copiedContentId, - ?int $versionNo = null - ): void { - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery - ->select( - 'l.contentclassattribute_id', - ':copied_id', - 'l.from_contentobject_version', - 'l.relation_type', - 'l.to_contentobject_id' - ) - ->from(self::CONTENT_RELATION_TABLE, 'l') - ->where('l.from_contentobject_id = :original_id') - ->setParameter('copied_id', $copiedContentId, ParameterType::INTEGER) - ->setParameter('original_id', $originalContentId, ParameterType::INTEGER); - - if ($versionNo) { - $selectQuery - ->andWhere('l.from_contentobject_version = :version') - ->setParameter(':version', $versionNo, ParameterType::INTEGER); - } - // Given we can retain all columns, we just create copies with new `from_contentobject_id` using INSERT INTO SELECT - $insertQuery = <<<SQL - INSERT INTO ezcontentobject_link ( - contentclassattribute_id, - from_contentobject_id, - from_contentobject_version, - relation_type, - to_contentobject_id - ) - SQL; - - $insertQuery .= $selectQuery->getSQL(); - - $this->connection->executeUpdate( - $insertQuery, - $selectQuery->getParameters(), - $selectQuery->getParameterTypes() - ); - } - - /** - * {@inheritdoc} - * - * @throws \Doctrine\DBAL\ConnectionException - * @throws \Doctrine\DBAL\DBALException - */ - public function deleteTranslationFromContent(int $contentId, string $languageCode): void - { - $language = $this->languageHandler->loadByLanguageCode($languageCode); - - $this->connection->beginTransaction(); - try { - $this->deleteTranslationFromContentVersions($contentId, $language->id); - $this->deleteTranslationFromContentNames($contentId, $languageCode); - $this->deleteTranslationFromContentObject($contentId, $language->id); - - $this->connection->commit(); - } catch (DBALException $e) { - $this->connection->rollBack(); - throw $e; - } - } - - public function deleteTranslatedFields( - string $languageCode, - int $contentId, - ?int $versionNo = null - ): void { - $query = $this->connection->createQueryBuilder(); - $query - ->delete('ezcontentobject_attribute') - ->where('contentobject_id = :contentId') - ->andWhere('language_code = :languageCode') - ->setParameters( - [ - ':contentId' => $contentId, - ':languageCode' => $languageCode, - ] - ) - ; - - if (null !== $versionNo) { - $query - ->andWhere('version = :versionNo') - ->setParameter(':versionNo', $versionNo) - ; - } - - $query->execute(); - } - - /** - * {@inheritdoc} - * - * @throws \Doctrine\DBAL\DBALException - */ - public function deleteTranslationFromVersion( - int $contentId, - int $versionNo, - string $languageCode - ): void { - $language = $this->languageHandler->loadByLanguageCode($languageCode); - - $this->connection->beginTransaction(); - try { - $this->deleteTranslationFromContentVersions($contentId, $language->id, $versionNo); - $this->deleteTranslationFromContentNames($contentId, $languageCode, $versionNo); - - $this->connection->commit(); - } catch (DBALException $e) { - $this->connection->rollBack(); - throw $e; - } - } - - /** - * Delete translation from the ezcontentobject_name table. - * - * @param int $versionNo optional, if specified, apply to this Version only. - */ - private function deleteTranslationFromContentNames( - int $contentId, - string $languageCode, - ?int $versionNo = null - ) { - $query = $this->connection->createQueryBuilder(); - $query - ->delete('ezcontentobject_name') - ->where('contentobject_id=:contentId') - ->andWhere('real_translation=:languageCode') - ->setParameters( - [ - ':languageCode' => $languageCode, - ':contentId' => $contentId, - ] - ) - ; - - if (null !== $versionNo) { - $query - ->andWhere('content_version = :versionNo') - ->setParameter(':versionNo', $versionNo) - ; - } - - $query->execute(); - } - - /** - * Remove language from language_mask of ezcontentobject. - * - * @param int $contentId - * @param int $languageId - * - * @throws \eZ\Publish\Core\Base\Exceptions\BadStateException - */ - private function deleteTranslationFromContentObject($contentId, $languageId) - { - $query = $this->connection->createQueryBuilder(); - $query->update('ezcontentobject') - // parameter for bitwise operation has to be placed verbatim (w/o binding) for this to work cross-DBMS - ->set('language_mask', 'language_mask & ~ ' . $languageId) - ->set('modified', ':now') - ->where('id = :contentId') - ->andWhere( - // make sure removed translation is not the last one (incl. alwaysAvailable) - $query->expr()->andX( - 'language_mask & ~ ' . $languageId . ' <> 0', - 'language_mask & ~ ' . $languageId . ' <> 1' - ) - ) - ->setParameter(':now', time()) - ->setParameter(':contentId', $contentId) - ; - - $rowCount = $query->execute(); - - // no rows updated means that most likely somehow it was the last remaining translation - if ($rowCount === 0) { - throw new BadStateException( - '$languageCode', - 'The provided translation is the only translation in this version' - ); - } - } - - /** - * Remove language from language_mask of ezcontentobject_version and update initialLanguageId - * if it matches the removed one. - * - * @param int|null $versionNo optional, if specified, apply to this Version only. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - */ - private function deleteTranslationFromContentVersions( - int $contentId, - int $languageId, - ?int $versionNo = null - ) { - $query = $this->connection->createQueryBuilder(); - $query->update('ezcontentobject_version') - // parameter for bitwise operation has to be placed verbatim (w/o binding) for this to work cross-DBMS - ->set('language_mask', 'language_mask & ~ ' . $languageId) - ->set('modified', ':now') - // update initial_language_id only if it matches removed translation languageId - ->set( - 'initial_language_id', - 'CASE WHEN initial_language_id = :languageId ' . - 'THEN (SELECT initial_language_id AS main_language_id FROM ezcontentobject c WHERE c.id = :contentId) ' . - 'ELSE initial_language_id END' - ) - ->where('contentobject_id = :contentId') - ->andWhere( - // make sure removed translation is not the last one (incl. alwaysAvailable) - $query->expr()->andX( - 'language_mask & ~ ' . $languageId . ' <> 0', - 'language_mask & ~ ' . $languageId . ' <> 1' - ) - ) - ->setParameter(':now', time()) - ->setParameter(':contentId', $contentId) - ->setParameter(':languageId', $languageId) - ; - - if (null !== $versionNo) { - $query - ->andWhere('version = :versionNo') - ->setParameter(':versionNo', $versionNo) - ; - } - - $rowCount = $query->execute(); - - // no rows updated means that most likely somehow it was the last remaining translation - if ($rowCount === 0) { - throw new BadStateException( - '$languageCode', - 'The provided translation is the only translation in this version' - ); - } - } - - /** - * Compute language mask and append it to a QueryBuilder (both column and parameter). - * - * **Can be used on UPDATE queries only!** - */ - private function setLanguageMaskForUpdateQuery( - bool $alwaysAvailable, - DoctrineQueryBuilder $query, - string $languageMaskColumnName - ): DoctrineQueryBuilder { - if ($alwaysAvailable) { - $languageMaskExpr = $this->databasePlatform->getBitOrComparisonExpression( - $languageMaskColumnName, - ':languageMaskOperand' - ); - } else { - $languageMaskExpr = $this->databasePlatform->getBitAndComparisonExpression( - $languageMaskColumnName, - ':languageMaskOperand' - ); - } - - $query - ->set($languageMaskColumnName, $languageMaskExpr) - ->setParameter( - 'languageMaskOperand', - $alwaysAvailable ? 1 : self::REMOVE_ALWAYS_AVAILABLE_LANG_MASK_OPERAND - ); - - return $query; - } - - /** - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \Doctrine\DBAL\Exception - */ - public function loadVersionInfoList(array $contentIds): array - { - $queryBuilder = $this->queryBuilder->createVersionInfoFindQueryBuilder(); - $expr = $queryBuilder->expr(); - - $queryBuilder - ->andWhere( - $expr->in( - 'c.id', - $queryBuilder->createNamedParameter($contentIds, Connection::PARAM_INT_ARRAY) - ) - ) - ->andWhere( - $expr->eq('v.version', 'c.current_version') - ); - - return $queryBuilder->execute()->fetchAllAssociative(); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/Content/Gateway/ExceptionConversion.php deleted file mode 100644 index 638377469b..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,528 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\UpdateStruct; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use PDOException; - -/** - * @internal Internal exception conversion layer. - */ -final class ExceptionConversion extends Gateway -{ - /** - * The wrapped gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway - */ - protected $innerGateway; - - /** - * Creates a new exception conversion gateway around $innerGateway. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function insertContentObject(CreateStruct $struct, int $currentVersionNo = 1): int - { - try { - return $this->innerGateway->insertContentObject($struct, $currentVersionNo); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function insertVersion(VersionInfo $versionInfo, array $fields): int - { - try { - return $this->innerGateway->insertVersion($versionInfo, $fields); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateContent( - int $contentId, - MetadataUpdateStruct $struct, - ?VersionInfo $prePublishVersionInfo = null - ): void { - try { - $this->innerGateway->updateContent($contentId, $struct, $prePublishVersionInfo); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - /** - * Updates version $versionNo for content identified by $contentId, in respect to $struct. - */ - public function updateVersion(int $contentId, int $versionNo, UpdateStruct $struct): void - { - try { - $this->innerGateway->updateVersion($contentId, $versionNo, $struct); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateAlwaysAvailableFlag( - int $contentId, - ?bool $newAlwaysAvailable = null - ): void { - try { - $this->innerGateway->updateAlwaysAvailableFlag($contentId, $newAlwaysAvailable); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function setStatus(int $contentId, int $version, int $status): bool - { - try { - return $this->innerGateway->setStatus($contentId, $version, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function setPublishedStatus(int $contentId, int $status): void - { - try { - $this->innerGateway->setPublishedStatus($contentId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function insertNewField(Content $content, Field $field, StorageFieldValue $value): int - { - try { - return $this->innerGateway->insertNewField($content, $field, $value); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function insertExistingField( - Content $content, - Field $field, - StorageFieldValue $value - ): void { - try { - $this->innerGateway->insertExistingField($content, $field, $value); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateField(Field $field, StorageFieldValue $value): void - { - try { - $this->innerGateway->updateField($field, $value); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateNonTranslatableField( - Field $field, - StorageFieldValue $value, - int $contentId - ): void { - try { - $this->innerGateway->updateNonTranslatableField($field, $value, $contentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function load(int $contentId, ?int $version = null, ?array $translations = null): array - { - try { - return $this->innerGateway->load($contentId, $version, $translations); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadContentList(array $contentIds, ?array $translations = null): array - { - try { - return $this->innerGateway->loadContentList($contentIds, $translations); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadContentInfoByRemoteId(string $remoteId): array - { - try { - return $this->innerGateway->loadContentInfoByRemoteId($remoteId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadContentInfoByLocationId(int $locationId): array - { - try { - return $this->innerGateway->loadContentInfoByLocationId($locationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadContentInfo(int $contentId): array - { - try { - return $this->innerGateway->loadContentInfo($contentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadContentInfoList(array $contentIds): array - { - try { - return $this->innerGateway->loadContentInfoList($contentIds); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadVersionInfo(int $contentId, ?int $versionNo = null): array - { - try { - return $this->innerGateway->loadVersionInfo($contentId, $versionNo); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countVersionsForUser(int $userId, int $status = VersionInfo::STATUS_DRAFT): int - { - try { - return $this->innerGateway->countVersionsForUser($userId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - /** - * @return string[][] - */ - public function listVersionsForUser(int $userId, int $status = VersionInfo::STATUS_DRAFT): array - { - try { - return $this->innerGateway->listVersionsForUser($userId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadVersionsForUser( - int $userId, - int $status = VersionInfo::STATUS_DRAFT, - int $offset = 0, - int $limit = -1 - ): array { - try { - return $this->innerGateway->loadVersionsForUser($userId, $status, $offset, $limit); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function listVersions(int $contentId, ?int $status = null, int $limit = -1): array - { - try { - return $this->innerGateway->listVersions($contentId, $status, $limit); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function listVersionNumbers(int $contentId): array - { - try { - return $this->innerGateway->listVersionNumbers($contentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getLastVersionNumber(int $contentId): int - { - try { - return $this->innerGateway->getLastVersionNumber($contentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getAllLocationIds(int $contentId): array - { - try { - return $this->innerGateway->getAllLocationIds($contentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getFieldIdsByType( - int $contentId, - ?int $versionNo = null, - ?string $languageCode = null - ): array { - try { - return $this->innerGateway->getFieldIdsByType($contentId, $versionNo, $languageCode); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteRelations(int $contentId, ?int $versionNo = null): void - { - try { - $this->innerGateway->deleteRelations($contentId, $versionNo); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function removeReverseFieldRelations(int $contentId): void - { - try { - $this->innerGateway->removeReverseFieldRelations($contentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function removeRelationsByFieldDefinitionId(int $fieldDefinitionId): void - { - try { - $this->innerGateway->removeRelationsByFieldDefinitionId($fieldDefinitionId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteField(int $fieldId): void - { - try { - $this->innerGateway->deleteField($fieldId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteFields(int $contentId, ?int $versionNo = null): void - { - try { - $this->innerGateway->deleteFields($contentId, $versionNo); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteVersions(int $contentId, ?int $versionNo = null): void - { - try { - $this->innerGateway->deleteVersions($contentId, $versionNo); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteNames(int $contentId, ?int $versionNo = null): void - { - try { - $this->innerGateway->deleteNames($contentId, $versionNo); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function setName(int $contentId, int $version, string $name, string $languageCode): void - { - try { - $this->innerGateway->setName($contentId, $version, $name, $languageCode); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteContent(int $contentId): void - { - try { - $this->innerGateway->deleteContent($contentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadRelations( - int $contentId, - ?int $contentVersionNo = null, - ?int $relationType = null - ): array { - try { - return $this->innerGateway->loadRelations($contentId, $contentVersionNo, $relationType); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countReverseRelations(int $contentId, ?int $relationType = null): int - { - try { - return $this->innerGateway->countReverseRelations($contentId, $relationType); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadReverseRelations(int $contentId, ?int $relationType = null): array - { - try { - return $this->innerGateway->loadReverseRelations($contentId, $relationType); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function listReverseRelations( - int $contentId, - int $offset = 0, - int $limit = -1, - ?int $relationType = null - ): array { - try { - return $this->innerGateway->listReverseRelations( - $contentId, - $offset, - $limit, - $relationType - ); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteRelation(int $relationId, int $type): void - { - try { - $this->innerGateway->deleteRelation($relationId, $type); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function insertRelation(RelationCreateStruct $struct): int - { - try { - return $this->innerGateway->insertRelation($struct); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getContentIdsByContentTypeId($contentTypeId): array - { - try { - return $this->innerGateway->getContentIdsByContentTypeId($contentTypeId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadVersionedNameData(array $rows): array - { - try { - return $this->innerGateway->loadVersionedNameData($rows); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function copyRelations( - int $originalContentId, - int $copiedContentId, - ?int $versionNo = null - ): void { - try { - $this->innerGateway->copyRelations($originalContentId, $copiedContentId, $versionNo); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteTranslationFromContent(int $contentId, string $languageCode): void - { - try { - $this->innerGateway->deleteTranslationFromContent($contentId, $languageCode); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteTranslatedFields( - string $languageCode, - int $contentId, - ?int $versionNo = null - ): void { - try { - $this->innerGateway->deleteTranslatedFields( - $languageCode, - $contentId, - $versionNo - ); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteTranslationFromVersion( - int $contentId, - int $versionNo, - string $languageCode - ): void { - try { - $this->innerGateway->deleteTranslationFromVersion( - $contentId, - $versionNo, - $languageCode - ); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadVersionInfoList(array $contentIds): array - { - try { - return $this->innerGateway->loadVersionInfoList($contentIds); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Content/Handler.php deleted file mode 100644 index 430f3b7b8a..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Handler.php +++ /dev/null @@ -1,963 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content; - -use Exception; -use eZ\Publish\Core\Base\Exceptions\NotFoundException as NotFound; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway as UrlAliasGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Handler as BaseContentHandler; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandler; -use eZ\Publish\SPI\Persistence\Content\UpdateStruct; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use Psr\Log\LoggerInterface; -use Psr\Log\NullLogger; - -/** - * The Content Handler stores Content and ContentType objects. - */ -class Handler implements BaseContentHandler -{ - /** - * Content gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway - */ - protected $contentGateway; - - /** - * Location gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway - */ - protected $locationGateway; - - /** - * Mapper. - * - * @var Mapper - */ - protected $mapper; - - /** - * FieldHandler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler - */ - protected $fieldHandler; - - /** - * URL slug converter. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter - */ - protected $slugConverter; - - /** - * UrlAlias gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway - */ - protected $urlAliasGateway; - - /** - * ContentType handler. - * - * @var \eZ\Publish\SPI\Persistence\Content\Type\Handler - */ - protected $contentTypeHandler; - - /** - * Tree handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler - */ - protected $treeHandler; - - /** - * @var \eZ\Publish\SPI\Persistence\Content\Language\Handler - */ - protected $languageHandler; - - /** @var \Psr\Log\LoggerInterface */ - private $logger; - - /** - * Creates a new content handler. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Gateway $contentGateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway $locationGateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Mapper $mapper - * @param \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler $fieldHandler - * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter $slugConverter - * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway $urlAliasGateway - * @param \eZ\Publish\SPI\Persistence\Content\Type\Handler $contentTypeHandler - * @param \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler $treeHandler - * @param \Psr\Log\LoggerInterface|null $logger - */ - public function __construct( - Gateway $contentGateway, - LocationGateway $locationGateway, - Mapper $mapper, - FieldHandler $fieldHandler, - SlugConverter $slugConverter, - UrlAliasGateway $urlAliasGateway, - ContentTypeHandler $contentTypeHandler, - TreeHandler $treeHandler, - LanguageHandler $languageHandler, - LoggerInterface $logger = null - ) { - $this->contentGateway = $contentGateway; - $this->locationGateway = $locationGateway; - $this->mapper = $mapper; - $this->fieldHandler = $fieldHandler; - $this->slugConverter = $slugConverter; - $this->urlAliasGateway = $urlAliasGateway; - $this->contentTypeHandler = $contentTypeHandler; - $this->treeHandler = $treeHandler; - $this->languageHandler = $languageHandler; - $this->logger = null !== $logger ? $logger : new NullLogger(); - } - - /** - * Creates a new Content entity in the storage engine. - * - * The values contained inside the $content will form the basis of stored - * entity. - * - * Will contain always a complete list of fields. - * - * @param \eZ\Publish\SPI\Persistence\Content\CreateStruct $struct Content creation struct. - * - * @return \eZ\Publish\SPI\Persistence\Content Content value object - */ - public function create(CreateStruct $struct) - { - return $this->internalCreate($struct); - } - - /** - * Creates a new Content entity in the storage engine. - * - * The values contained inside the $content will form the basis of stored - * entity. - * - * Will contain always a complete list of fields. - * - * @param \eZ\Publish\SPI\Persistence\Content\CreateStruct $struct Content creation struct. - * @param mixed $versionNo Used by self::copy() to maintain version numbers - * - * @return \eZ\Publish\SPI\Persistence\Content Content value object - */ - protected function internalCreate(CreateStruct $struct, $versionNo = 1) - { - $content = new Content(); - - $content->fields = $struct->fields; - $content->versionInfo = $this->mapper->createVersionInfoFromCreateStruct($struct, $versionNo); - - $content->versionInfo->contentInfo->id = $this->contentGateway->insertContentObject($struct, $versionNo); - $content->versionInfo->id = $this->contentGateway->insertVersion( - $content->versionInfo, - $struct->fields - ); - - $contentType = $this->contentTypeHandler->load($struct->typeId); - $this->fieldHandler->createNewFields($content, $contentType); - - // Create node assignments - foreach ($struct->locations as $location) { - $location->contentId = $content->versionInfo->contentInfo->id; - $location->contentVersion = $content->versionInfo->versionNo; - $this->locationGateway->createNodeAssignment( - $location, - $location->parentId, - LocationGateway::NODE_ASSIGNMENT_OP_CODE_CREATE - ); - } - - // Create names - foreach ($content->versionInfo->names as $language => $name) { - $this->contentGateway->setName( - $content->versionInfo->contentInfo->id, - $content->versionInfo->versionNo, - $name, - $language - ); - } - - return $content; - } - - /** - * Performs the publishing operations required to set the version identified by $updateStruct->versionNo and - * $updateStruct->id as the published one. - * - * The publish procedure will: - * - Create location nodes based on the node assignments - * - Update the content object using the provided metadata update struct - * - Update the node assignments - * - Update location nodes of the content with the new published version - * - Set content and version status to published - * - * @param int $contentId - * @param int $versionNo - * @param \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct $metaDataUpdateStruct - * - * @return \eZ\Publish\SPI\Persistence\Content The published Content - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function publish($contentId, $versionNo, MetadataUpdateStruct $metaDataUpdateStruct) - { - // Archive currently published version - $versionInfo = $this->loadVersionInfo($contentId, $versionNo); - if ($versionInfo->contentInfo->currentVersionNo != $versionNo) { - $this->setStatus( - $contentId, - VersionInfo::STATUS_ARCHIVED, - $versionInfo->contentInfo->currentVersionNo - ); - } - - // Set always available name for the content - $metaDataUpdateStruct->name = $versionInfo->names[$versionInfo->contentInfo->mainLanguageCode]; - - $this->contentGateway->updateContent($contentId, $metaDataUpdateStruct, $versionInfo); - $this->locationGateway->createLocationsFromNodeAssignments( - $contentId, - $versionNo - ); - - $this->locationGateway->updateLocationsContentVersionNo($contentId, $versionNo); - $this->contentGateway->setPublishedStatus($contentId, $versionNo); - - return $this->load($contentId, $versionNo); - } - - /** - * Creates a new draft version from $contentId in $version. - * - * Copies all fields from $contentId in $srcVersion and creates a new - * version of the referred Content from it. - * - * Note: When creating a new draft in the old admin interface there will - * also be an entry in the `eznode_assignment` created for the draft. This - * is ignored in this implementation. - * - * @param mixed $contentId - * @param mixed $srcVersion - * @param mixed $userId - * @param string|null $languageCode - * - * @return \eZ\Publish\SPI\Persistence\Content - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function createDraftFromVersion($contentId, $srcVersion, $userId, ?string $languageCode = null) - { - $content = $this->load($contentId, $srcVersion); - - // Create new version - $content->versionInfo = $this->mapper->createVersionInfoForContent( - $content, - $this->contentGateway->getLastVersionNumber($contentId) + 1, - $userId, - $languageCode - ); - $content->versionInfo->id = $this->contentGateway->insertVersion( - $content->versionInfo, - $content->fields - ); - - // Clone fields from previous version and append them to the new one - $this->fieldHandler->createExistingFieldsInNewVersion($content); - - // Persist virtual fields - $contentType = $this->contentTypeHandler->load($content->versionInfo->contentInfo->contentTypeId); - $this->fieldHandler->updateFields($content, new UpdateStruct([ - 'initialLanguageId' => $this->languageHandler->loadByLanguageCode( - $content->versionInfo->initialLanguageCode - )->id, - ]), $contentType); - - // Create relations for new version - $relations = $this->contentGateway->loadRelations($contentId, $srcVersion); - foreach ($relations as $relation) { - $this->contentGateway->insertRelation( - new RelationCreateStruct( - [ - 'sourceContentId' => $contentId, - 'sourceContentVersionNo' => $content->versionInfo->versionNo, - 'sourceFieldDefinitionId' => $relation['ezcontentobject_link_contentclassattribute_id'], - 'destinationContentId' => $relation['ezcontentobject_link_to_contentobject_id'], - 'type' => (int)$relation['ezcontentobject_link_relation_type'], - ] - ) - ); - } - - // Create content names for new version - foreach ($content->versionInfo->names as $language => $name) { - $this->contentGateway->setName( - $contentId, - $content->versionInfo->versionNo, - $name, - $language - ); - } - - return $content; - } - - /** - * {@inheritdoc} - */ - public function load($id, $version = null, array $translations = null) - { - $rows = $this->contentGateway->load($id, $version, $translations); - - if (empty($rows)) { - throw new NotFound('content', "contentId: $id, versionNo: $version"); - } - - $contentObjects = $this->mapper->extractContentFromRows( - $rows, - $this->contentGateway->loadVersionedNameData([[ - 'id' => $id, - 'version' => $rows[0]['ezcontentobject_version_version'], - ]]), - 'ezcontentobject_', - $translations - ); - $content = $contentObjects[0]; - unset($rows, $contentObjects); - - $this->fieldHandler->loadExternalFieldData($content); - - return $content; - } - - /** - * {@inheritdoc} - */ - public function loadContentList(array $contentIds, array $translations = null): array - { - $rawList = $this->contentGateway->loadContentList($contentIds, $translations); - if (empty($rawList)) { - return []; - } - - $idVersionPairs = []; - foreach ($rawList as $row) { - // As there is only one version per id, set id as key to avoid duplicates - $idVersionPairs[$row['ezcontentobject_id']] = [ - 'id' => $row['ezcontentobject_id'], - 'version' => $row['ezcontentobject_version_version'], - ]; - } - - // group name data per Content Id - $nameData = $this->contentGateway->loadVersionedNameData(array_values($idVersionPairs)); - $contentItemNameData = []; - foreach ($nameData as $nameDataRow) { - $contentId = $nameDataRow['ezcontentobject_name_contentobject_id']; - $contentItemNameData[$contentId][] = $nameDataRow; - } - - // group rows per Content Id be able to ignore Content items with erroneous data - $contentItemsRows = []; - foreach ($rawList as $row) { - $contentId = $row['ezcontentobject_id']; - $contentItemsRows[$contentId][] = $row; - } - unset($rawList, $idVersionPairs); - - // try to extract Content from each Content data - $contentItems = []; - foreach ($contentItemsRows as $contentId => $contentItemsRow) { - try { - $contentList = $this->mapper->extractContentFromRows( - $contentItemsRow, - $contentItemNameData[$contentId], - 'ezcontentobject_', - $translations - ); - $contentItems[$contentId] = $contentList[0]; - } catch (Exception $e) { - $this->logger->warning( - sprintf( - '%s: Content %d not loaded: %s', - __METHOD__, - $contentId, - $e->getMessage() - ) - ); - } - } - - // try to load External Storage data for each Content, ignore Content items for which it failed - foreach ($contentItems as $contentId => $content) { - try { - $this->fieldHandler->loadExternalFieldData($content); - } catch (Exception $e) { - unset($contentItems[$contentId]); - $this->logger->warning( - sprintf( - '%s: Content %d not loaded: %s', - __METHOD__, - $contentId, - $e->getMessage() - ) - ); - } - } - - return $contentItems; - } - - /** - * Returns the metadata object for a content identified by $contentId. - * - * @param int|string $contentId - * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo - */ - public function loadContentInfo($contentId) - { - return $this->treeHandler->loadContentInfo($contentId); - } - - public function loadContentInfoList(array $contentIds) - { - $list = $this->mapper->extractContentInfoFromRows( - $this->contentGateway->loadContentInfoList($contentIds) - ); - - $listByContentId = []; - foreach ($list as $item) { - $listByContentId[$item->id] = $item; - } - - return $listByContentId; - } - - /** - * Returns the metadata object for a content identified by $remoteId. - * - * @param mixed $remoteId - * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo - */ - public function loadContentInfoByRemoteId($remoteId) - { - return $this->mapper->extractContentInfoFromRow( - $this->contentGateway->loadContentInfoByRemoteId($remoteId) - ); - } - - /** - * {@inheritdoc} - */ - public function loadVersionInfo($contentId, $versionNo = null) - { - $rows = $this->contentGateway->loadVersionInfo($contentId, $versionNo); - if (empty($rows)) { - throw new NotFound('content', $contentId); - } - - $versionInfo = $this->mapper->extractVersionInfoListFromRows( - $rows, - $this->contentGateway->loadVersionedNameData([['id' => $contentId, 'version' => $rows[0]['ezcontentobject_version_version']]]) - ); - - return reset($versionInfo); - } - - public function countDraftsForUser(int $userId): int - { - return $this->contentGateway->countVersionsForUser($userId, VersionInfo::STATUS_DRAFT); - } - - /** - * Returns all versions with draft status created by the given $userId. - * - * @param int $userId - * - * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo[] - */ - public function loadDraftsForUser($userId) - { - $rows = $this->contentGateway->listVersionsForUser($userId, VersionInfo::STATUS_DRAFT); - if (empty($rows)) { - return []; - } - - $idVersionPairs = array_map( - static function ($row) { - return [ - 'id' => $row['ezcontentobject_version_contentobject_id'], - 'version' => $row['ezcontentobject_version_version'], - ]; - }, - $rows - ); - $nameRows = $this->contentGateway->loadVersionedNameData($idVersionPairs); - - return $this->mapper->extractVersionInfoListFromRows($rows, $nameRows); - } - - public function loadDraftListForUser(int $userId, int $offset = 0, int $limit = -1): array - { - $rows = $this->contentGateway->loadVersionsForUser($userId, VersionInfo::STATUS_DRAFT, $offset, $limit); - if (empty($rows)) { - return []; - } - - $idVersionPairs = array_map( - static function (array $row): array { - return [ - 'id' => $row['ezcontentobject_version_contentobject_id'], - 'version' => $row['ezcontentobject_version_version'], - ]; - }, - $rows - ); - $nameRows = $this->contentGateway->loadVersionedNameData($idVersionPairs); - - return $this->mapper->extractVersionInfoListFromRows($rows, $nameRows); - } - - /** - * Sets the status of object identified by $contentId and $version to $status. - * - * The $status can be one of VersionInfo::STATUS_DRAFT, VersionInfo::STATUS_PUBLISHED, VersionInfo::STATUS_ARCHIVED - * When status is set to VersionInfo::STATUS_PUBLISHED content status is updated to ContentInfo::STATUS_PUBLISHED - * - * @param int $contentId - * @param int $status - * @param int $version - * - * @return bool - */ - public function setStatus($contentId, $status, $version) - { - return $this->contentGateway->setStatus($contentId, $version, $status); - } - - /** - * Updates a content object meta data, identified by $contentId. - * - * @param int $contentId - * @param \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct $content - * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo - */ - public function updateMetadata($contentId, MetadataUpdateStruct $content) - { - $this->contentGateway->updateContent($contentId, $content); - $this->updatePathIdentificationString($contentId, $content); - - return $this->loadContentInfo($contentId); - } - - /** - * Updates path identification string for locations of given $contentId if main language - * is set in update struct. - * - * This is specific to the Legacy storage engine, as path identification string is deprecated. - * - * @param int $contentId - * @param \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct $content - */ - protected function updatePathIdentificationString($contentId, MetadataUpdateStruct $content) - { - if (isset($content->mainLanguageId)) { - $contentLocationsRows = $this->locationGateway->loadLocationDataByContent($contentId); - foreach ($contentLocationsRows as $row) { - $locationName = ''; - $urlAliasRows = $this->urlAliasGateway->loadLocationEntries( - $row['node_id'], - false, - $content->mainLanguageId - ); - if (!empty($urlAliasRows)) { - $locationName = $urlAliasRows[0]['text']; - } - $this->locationGateway->updatePathIdentificationString( - $row['node_id'], - $row['parent_node_id'], - $this->slugConverter->convert( - $locationName, - 'node_' . $row['node_id'], - 'urlalias_compat' - ) - ); - } - } - } - - /** - * Updates a content version, identified by $contentId and $versionNo. - * - * @param int $contentId - * @param int $versionNo - * @param \eZ\Publish\SPI\Persistence\Content\UpdateStruct $updateStruct - * - * @return \eZ\Publish\SPI\Persistence\Content - */ - public function updateContent($contentId, $versionNo, UpdateStruct $updateStruct) - { - $content = $this->load($contentId, $versionNo); - $this->contentGateway->updateVersion($contentId, $versionNo, $updateStruct); - $contentType = $this->contentTypeHandler->load($content->versionInfo->contentInfo->contentTypeId); - $this->fieldHandler->updateFields($content, $updateStruct, $contentType); - foreach ($updateStruct->name as $language => $name) { - $this->contentGateway->setName( - $contentId, - $versionNo, - $name, - $language - ); - } - - return $this->load($contentId, $versionNo); - } - - /** - * Deletes all versions and fields, all locations (subtree), and all relations. - * - * Removes the relations, but not the related objects. All subtrees of the - * assigned nodes of this content objects are removed (recursively). - * - * @param int $contentId - * - * @return bool - */ - public function deleteContent($contentId) - { - $contentLocations = $this->contentGateway->getAllLocationIds($contentId); - if (empty($contentLocations)) { - $this->removeRawContent($contentId); - } else { - foreach ($contentLocations as $locationId) { - $this->treeHandler->removeSubtree($locationId); - } - } - } - - /** - * Deletes raw content data. - * - * @param int $contentId - */ - public function removeRawContent($contentId) - { - $this->treeHandler->removeRawContent($contentId); - } - - /** - * Deletes given version, its fields, node assignment, relations and names. - * - * Removes the relations, but not the related objects. - * - * @param int $contentId - * @param int $versionNo - * - * @return bool - */ - public function deleteVersion($contentId, $versionNo) - { - $versionInfo = $this->loadVersionInfo($contentId, $versionNo); - - $this->locationGateway->deleteNodeAssignment($contentId, $versionNo); - - $this->fieldHandler->deleteFields($contentId, $versionInfo); - - $this->contentGateway->deleteRelations($contentId, $versionNo); - $this->contentGateway->deleteVersions($contentId, $versionNo); - $this->contentGateway->deleteNames($contentId, $versionNo); - } - - /** - * Returns the versions for $contentId. - * - * Result is returned with oldest version first (sorted by created, alternatively version id if auto increment). - * - * @param int $contentId - * @param mixed|null $status Optional argument to filter versions by status, like {@see VersionInfo::STATUS_ARCHIVED}. - * @param int $limit Limit for items returned, -1 means none. - * - * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo[] - */ - public function listVersions($contentId, $status = null, $limit = -1) - { - return $this->treeHandler->listVersions($contentId, $status, $limit); - } - - /** - * Copy Content with Fields, Versions & Relations from $contentId in $version. - * - * {@inheritdoc} - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If content or version is not found - * - * @param mixed $contentId - * @param mixed|null $versionNo Copy all versions if left null - * @param int|null $newOwnerId - * - * @return \eZ\Publish\SPI\Persistence\Content - */ - public function copy($contentId, $versionNo = null, $newOwnerId = null) - { - $currentVersionNo = isset($versionNo) ? - $versionNo : - $this->loadContentInfo($contentId)->currentVersionNo; - - // Copy content in given version or current version - $createStruct = $this->mapper->createCreateStructFromContent( - $this->load($contentId, $currentVersionNo) - ); - if ($newOwnerId) { - $createStruct->ownerId = $newOwnerId; - } - $content = $this->internalCreate($createStruct, $currentVersionNo); - - // If version was not passed also copy other versions - if (!isset($versionNo)) { - $contentType = $this->contentTypeHandler->load($createStruct->typeId); - - foreach ($this->listVersions($contentId) as $versionInfo) { - if ($versionInfo->versionNo === $currentVersionNo) { - continue; - } - - $versionContent = $this->load($contentId, $versionInfo->versionNo); - - $versionContent->versionInfo->contentInfo->id = $content->versionInfo->contentInfo->id; - $versionContent->versionInfo->modificationDate = $createStruct->modified; - $versionContent->versionInfo->creationDate = $createStruct->modified; - $versionContent->versionInfo->creatorId = $createStruct->ownerId; - - $versionContent->versionInfo->id = $this->contentGateway->insertVersion( - $versionContent->versionInfo, - $versionContent->fields - ); - - $this->fieldHandler->createNewFields($versionContent, $contentType); - - // Create names - foreach ($versionContent->versionInfo->names as $language => $name) { - $this->contentGateway->setName( - $content->versionInfo->contentInfo->id, - $versionInfo->versionNo, - $name, - $language - ); - } - } - - // Batch copy relations for all versions - $this->contentGateway->copyRelations($contentId, $content->versionInfo->contentInfo->id); - } else { - // Batch copy relations for published version - $this->contentGateway->copyRelations($contentId, $content->versionInfo->contentInfo->id, $versionNo); - } - - return $content; - } - - /** - * Creates a relation between $sourceContentId in $sourceContentVersionNo - * and $destinationContentId with a specific $type. - * - * @todo Should the existence verifications happen here or is this supposed to be handled at a higher level? - * - * @param \eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct $createStruct - * - * @return \eZ\Publish\SPI\Persistence\Content\Relation - */ - public function addRelation(RelationCreateStruct $createStruct) - { - $relation = $this->mapper->createRelationFromCreateStruct($createStruct); - - $relation->id = $this->contentGateway->insertRelation($createStruct); - - return $relation; - } - - /** - * Removes a relation by relation Id. - * - * @todo Should the existence verifications happen here or is this supposed to be handled at a higher level? - * - * @param mixed $relationId - * @param int $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, - * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, - * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, - * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD} - */ - public function removeRelation($relationId, $type) - { - $this->contentGateway->deleteRelation($relationId, $type); - } - - /** - * Loads relations from $sourceContentId. Optionally, loads only those with $type and $sourceContentVersionNo. - * - * @param mixed $sourceContentId Source Content ID - * @param mixed|null $sourceContentVersionNo Source Content Version, null if not specified - * @param int|null $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, - * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, - * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, - * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD} - * - * @return \eZ\Publish\SPI\Persistence\Content\Relation[] - */ - public function loadRelations($sourceContentId, $sourceContentVersionNo = null, $type = null) - { - return $this->mapper->extractRelationsFromRows( - $this->contentGateway->loadRelations($sourceContentId, $sourceContentVersionNo, $type) - ); - } - - /** - * {@inheritdoc} - */ - public function countReverseRelations(int $destinationContentId, ?int $type = null): int - { - return $this->contentGateway->countReverseRelations($destinationContentId, $type); - } - - /** - * Loads relations from $contentId. Optionally, loads only those with $type. - * - * Only loads relations against published versions. - * - * @param mixed $destinationContentId Destination Content ID - * @param int|null $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, - * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, - * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, - * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD} - * - * @return \eZ\Publish\SPI\Persistence\Content\Relation[] - */ - public function loadReverseRelations($destinationContentId, $type = null) - { - return $this->mapper->extractRelationsFromRows( - $this->contentGateway->loadReverseRelations($destinationContentId, $type) - ); - } - - /** - * {@inheritdoc} - */ - public function loadReverseRelationList( - int $destinationContentId, - int $offset = 0, - int $limit = -1, - ?int $type = null - ): array { - return $this->mapper->extractRelationsFromRows( - $this->contentGateway->listReverseRelations($destinationContentId, $offset, $limit, $type) - ); - } - - /** - * {@inheritdoc} - */ - public function deleteTranslationFromContent($contentId, $languageCode) - { - $this->fieldHandler->deleteTranslationFromContentFields( - $contentId, - $this->listVersions($contentId), - $languageCode - ); - $this->contentGateway->deleteTranslationFromContent($contentId, $languageCode); - } - - /** - * {@inheritdoc} - */ - public function deleteTranslationFromDraft($contentId, $versionNo, $languageCode) - { - $versionInfo = $this->loadVersionInfo($contentId, $versionNo); - - $this->fieldHandler->deleteTranslationFromVersionFields( - $versionInfo, - $languageCode - ); - $this->contentGateway->deleteTranslationFromVersion( - $contentId, - $versionNo, - $languageCode - ); - - // get all [languageCode => name] entries except the removed Translation - $names = array_filter( - $versionInfo->names, - static function ($lang) use ($languageCode) { - return $lang !== $languageCode; - }, - ARRAY_FILTER_USE_KEY - ); - // set new Content name - foreach ($names as $language => $name) { - $this->contentGateway->setName( - $contentId, - $versionNo, - $name, - $language - ); - } - - // reload entire Version w/o removed Translation - return $this->load($contentId, $versionNo); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function loadVersionInfoList(array $contentIds): array - { - $rows = $this->contentGateway->loadVersionInfoList($contentIds); - - if (empty($rows)) { - return []; - } - - $mappedRows = array_map( - static function (array $row): array { - return [ - 'id' => $row['ezcontentobject_id'], - 'version' => $row['ezcontentobject_version_version'], - ]; - }, - $rows, - ); - - $versionInfoList = $this->mapper->extractVersionInfoListFromRows( - $rows, - $this->contentGateway->loadVersionedNameData($mappedRows) - ); - - $versionInfoListById = []; - foreach ($versionInfoList as $versionInfo) { - $versionInfoListById[$versionInfo->contentInfo->id] = $versionInfo; - } - - return $versionInfoListById; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Language/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/Content/Language/Gateway.php deleted file mode 100644 index 9822b2889e..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Language/Gateway.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\Language; - -use eZ\Publish\SPI\Persistence\Content\Language; - -/** - * Content Model language gateway. - * - * @internal For internal use by Persistence Handlers. - */ -abstract class Gateway -{ - public const CONTENT_LANGUAGE_TABLE = 'ezcontent_language'; - - /** - * A map of language-related table name to its language column. - * - * The first column is considered to be a language bitmask. - * The second, optional, column is an explicit language id. - * - * It depends on the schema defined in - * <code>eZ/Bundle/EzPublishCoreBundle/Resources/config/storage/legacy/schema.yaml</code> - */ - public const MULTILINGUAL_TABLES_COLUMNS = [ - 'ezcobj_state' => ['language_mask', 'default_language_id'], - 'ezcobj_state_group_language' => ['language_id'], - 'ezcobj_state_group' => ['language_mask', 'default_language_id'], - 'ezcobj_state_language' => ['language_id'], - 'ezcontentclass_attribute_ml' => ['language_id'], - 'ezcontentclass_name' => ['language_id'], - 'ezcontentclass' => ['language_mask', 'initial_language_id'], - 'ezcontentobject_attribute' => ['language_id'], - 'ezcontentobject_name' => ['language_id'], - 'ezcontentobject_version' => ['language_mask', 'initial_language_id'], - 'ezcontentobject' => ['language_mask', 'initial_language_id'], - 'ezurlalias_ml' => ['lang_mask'], - ]; - - /** - * Insert the given $language. - */ - abstract public function insertLanguage(Language $language): int; - - /** - * Update the data of the given $language. - */ - abstract public function updateLanguage(Language $language): void; - - /** - * Load data list for the Language with $ids. - * - * @param int[] $ids - * - * @return string[][]|iterable - */ - abstract public function loadLanguageListData(array $ids): iterable; - - /** - * Load data list for Languages by $languageCodes (eg: eng-GB). - * - * @param string[] $languageCodes - * - * @return string[][]|iterable - */ - abstract public function loadLanguageListDataByLanguageCode(array $languageCodes): iterable; - - /** - * Load the data for all languages. - */ - abstract public function loadAllLanguagesData(): array; - - /** - * Delete the language with $id. - */ - abstract public function deleteLanguage(int $id): void; - - /** - * Check whether a language may be deleted. - */ - abstract public function canDeleteLanguage(int $id): bool; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabase.php deleted file mode 100644 index 2877686f76..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,226 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway; -use eZ\Publish\SPI\Persistence\Content\Language; -use RuntimeException; - -/** - * Doctrine database based Language Gateway. - * - * @internal Gateway implementation is considered internal. Use Persistence Language Handler instead. - * - * @see \eZ\Publish\SPI\Persistence\Content\Language\Handler - */ -final class DoctrineDatabase extends Gateway -{ - /** - * The native Doctrine connection. - * - * @var \Doctrine\DBAL\Connection - */ - private $connection; - - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */ - private $dbPlatform; - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct(Connection $connection) - { - $this->connection = $connection; - $this->dbPlatform = $this->connection->getDatabasePlatform(); - } - - public function insertLanguage(Language $language): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - $this->dbPlatform->getMaxExpression('id') - ) - ->from(self::CONTENT_LANGUAGE_TABLE); - - $statement = $query->execute(); - - $lastId = (int)$statement->fetchColumn(); - - // Legacy only supports 8 * PHP_INT_SIZE - 2 languages: - // One bit cannot be used because PHP uses signed integers and a second one is reserved for the - // "always available flag". - if ($lastId == (2 ** (8 * PHP_INT_SIZE - 2))) { - throw new RuntimeException('Maximum number of languages reached.'); - } - // Next power of 2 for bit masks - $nextId = ($lastId !== 0 ? $lastId << 1 : 2); - - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::CONTENT_LANGUAGE_TABLE) - ->values( - [ - 'id' => ':id', - 'locale' => ':language_code', - 'name' => ':name', - 'disabled' => ':disabled', - ] - ) - ->setParameter('id', $nextId, ParameterType::INTEGER); - - $this->setLanguageQueryParameters($query, $language); - - $query->execute(); - - return $nextId; - } - - /** - * Set columns for $query based on $language. - */ - private function setLanguageQueryParameters(QueryBuilder $query, Language $language): void - { - $query - ->setParameter('language_code', $language->languageCode, ParameterType::STRING) - ->setParameter('name', $language->name, ParameterType::STRING) - ->setParameter('disabled', (int)!$language->isEnabled, ParameterType::INTEGER); - } - - public function updateLanguage(Language $language): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::CONTENT_LANGUAGE_TABLE) - ->set('locale', ':language_code') - ->set('name', ':name') - ->set('disabled', ':disabled'); - - $this->setLanguageQueryParameters($query, $language); - - $query->where( - $query->expr()->eq( - 'id', - $query->createNamedParameter($language->id, ParameterType::INTEGER, ':id') - ) - ); - - $query->execute(); - } - - public function loadLanguageListData(array $ids): iterable - { - $query = $this->createFindQuery(); - $query - ->where('id IN (:ids)') - ->setParameter('ids', $ids, Connection::PARAM_INT_ARRAY); - - return $query->execute()->fetchAll(); - } - - public function loadLanguageListDataByLanguageCode(array $languageCodes): iterable - { - $query = $this->createFindQuery(); - $query - ->where('locale IN (:locale)') - ->setParameter('locale', $languageCodes, Connection::PARAM_STR_ARRAY); - - return $query->execute()->fetchAll(); - } - - /** - * Build a Language find (fetch) query. - */ - private function createFindQuery(): QueryBuilder - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('id', 'locale', 'name', 'disabled') - ->from(self::CONTENT_LANGUAGE_TABLE); - - return $query; - } - - public function loadAllLanguagesData(): array - { - return $this->createFindQuery()->execute()->fetchAll(); - } - - public function deleteLanguage(int $id): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::CONTENT_LANGUAGE_TABLE) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - - $query->execute(); - } - - public function canDeleteLanguage(int $id): bool - { - // note: at some point this should be delegated to specific gateways - foreach (self::MULTILINGUAL_TABLES_COLUMNS as $tableName => $columns) { - $languageMaskColumn = $columns[0]; - $languageIdColumn = $columns[1] ?? null; - if ( - $this->countTableData($id, $tableName, $languageMaskColumn, $languageIdColumn) > 0 - ) { - return false; - } - } - - return true; - } - - /** - * Count table data rows related to the given language. - * - * @param string|null $languageIdColumn optional column name containing explicit language id - */ - private function countTableData( - int $languageId, - string $tableName, - string $languageMaskColumn, - ?string $languageIdColumn = null - ): int { - $query = $this->connection->createQueryBuilder(); - $query - // avoiding using "*" as count argument, but don't specify column name because it varies - ->select($this->dbPlatform->getCountExpression(1)) - ->from($tableName) - ->where( - $query->expr()->gt( - $this->dbPlatform->getBitAndComparisonExpression( - $languageMaskColumn, - $query->createPositionalParameter($languageId, ParameterType::INTEGER) - ), - 0 - ) - ); - if (null !== $languageIdColumn) { - $query - ->orWhere( - $query->expr()->eq( - $languageIdColumn, - $query->createPositionalParameter($languageId, ParameterType::INTEGER) - ) - ); - } - - return (int)$query->execute()->fetchColumn(); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Language/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/Content/Language/Gateway/ExceptionConversion.php deleted file mode 100644 index 48bd9bc61d..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Language/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway; -use eZ\Publish\SPI\Persistence\Content\Language; -use PDOException; - -/** - * @internal Internal exception conversion layer. - */ -final class ExceptionConversion extends Gateway -{ - /** - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway - */ - private $innerGateway; - - /** - * Creates a new exception conversion gateway around $innerGateway. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function insertLanguage(Language $language): int - { - try { - return $this->innerGateway->insertLanguage($language); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateLanguage(Language $language): void - { - try { - $this->innerGateway->updateLanguage($language); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadLanguageListData(array $ids): iterable - { - try { - return $this->innerGateway->loadLanguageListData($ids); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadLanguageListDataByLanguageCode(array $languageCodes): iterable - { - try { - return $this->innerGateway->loadLanguageListDataByLanguageCode($languageCodes); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadAllLanguagesData(): array - { - try { - return $this->innerGateway->loadAllLanguagesData(); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteLanguage(int $id): void - { - try { - $this->innerGateway->deleteLanguage($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function canDeleteLanguage(int $id): bool - { - try { - return $this->innerGateway->canDeleteLanguage($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Language/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Content/Language/Handler.php deleted file mode 100644 index 92a601ae1d..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Language/Handler.php +++ /dev/null @@ -1,165 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Language; - -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\SPI\Persistence\Content\Language; -use eZ\Publish\SPI\Persistence\Content\Language\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as BaseLanguageHandler; -use LogicException; - -/** - * Language Handler. - */ -class Handler implements BaseLanguageHandler -{ - /** - * Language Gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway - */ - protected $languageGateway; - - /** - * Language Mapper. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Mapper - */ - protected $languageMapper; - - /** - * Creates a new Language Handler. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway $languageGateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Language\Mapper $languageMapper - */ - public function __construct(Gateway $languageGateway, Mapper $languageMapper) - { - $this->languageGateway = $languageGateway; - $this->languageMapper = $languageMapper; - } - - /** - * Create a new language. - * - * @param \eZ\Publish\SPI\Persistence\Content\Language\CreateStruct $struct - * - * @return \eZ\Publish\SPI\Persistence\Content\Language - */ - public function create(CreateStruct $struct) - { - $language = $this->languageMapper->createLanguageFromCreateStruct( - $struct - ); - $language->id = $this->languageGateway->insertLanguage($language); - - return $language; - } - - /** - * Update language. - * - * @param \eZ\Publish\SPI\Persistence\Content\Language $language - */ - public function update(Language $language) - { - $this->languageGateway->updateLanguage($language); - } - - /** - * Get language by id. - * - * @param mixed $id - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language could not be found by $id - * - * @return \eZ\Publish\SPI\Persistence\Content\Language - */ - public function load($id) - { - $languages = $this->languageMapper->extractLanguagesFromRows( - $this->languageGateway->loadLanguageListData([$id]) - ); - - if (count($languages) < 1) { - throw new NotFoundException('Language', $id); - } - - return reset($languages); - } - - /** - * {@inheritdoc} - */ - public function loadList(array $ids): iterable - { - return $this->languageMapper->extractLanguagesFromRows( - $this->languageGateway->loadLanguageListData($ids), - 'id' - ); - } - - /** - * Get language by Language Code (eg: eng-GB). - * - * @param string $languageCode - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language could not be found by $languageCode - * - * @return \eZ\Publish\SPI\Persistence\Content\Language - */ - public function loadByLanguageCode($languageCode) - { - $languages = $this->languageMapper->extractLanguagesFromRows( - $this->languageGateway->loadLanguageListDataByLanguageCode([$languageCode]) - ); - - if (count($languages) < 1) { - throw new NotFoundException('Language', $languageCode); - } - - return reset($languages); - } - - /** - * {@inheritdoc} - */ - public function loadListByLanguageCodes(array $languageCodes): iterable - { - return $this->languageMapper->extractLanguagesFromRows( - $this->languageGateway->loadLanguageListDataByLanguageCode($languageCodes) - ); - } - - /** - * Get all languages. - * - * @return \eZ\Publish\SPI\Persistence\Content\Language[] - */ - public function loadAll() - { - return $this->languageMapper->extractLanguagesFromRows( - $this->languageGateway->loadAllLanguagesData() - ); - } - - /** - * Delete a language. - * - * @param mixed $id - * - * @throws \LogicException If language could not be deleted - */ - public function delete($id) - { - if (!$this->languageGateway->canDeleteLanguage($id)) { - throw new LogicException('Cannot delete language: some content still references the language'); - } - - $this->languageGateway->deleteLanguage($id); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Language/Mapper.php b/eZ/Publish/Core/Persistence/Legacy/Content/Language/Mapper.php deleted file mode 100644 index 380e9b8136..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Language/Mapper.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Language; - -use eZ\Publish\SPI\Persistence\Content\Language; -use eZ\Publish\SPI\Persistence\Content\Language\CreateStruct; - -/** - * Language Mapper. - */ -class Mapper -{ - /** - * Creates a Language from $struct. - * - * @param \eZ\Publish\SPI\Persistence\Content\Language\CreateStruct $struct - * - * @return \eZ\Publish\SPI\Persistence\Content\Language - */ - public function createLanguageFromCreateStruct(CreateStruct $struct): Language - { - $language = new Language(); - - $language->languageCode = $struct->languageCode; - $language->name = $struct->name; - $language->isEnabled = $struct->isEnabled; - - return $language; - } - - /** - * Extracts Language objects from $rows. - * - * @param array $rows - * @param string $key Column name for use as key in returned array. - * - * @return \eZ\Publish\SPI\Persistence\Content\Language[] - */ - public function extractLanguagesFromRows(array $rows, string $key = 'locale'): array - { - $languages = []; - - foreach ($rows as $row) { - $language = new Language(); - - $language->id = (int)$row['id']; - $language->languageCode = $row['locale']; - $language->name = $row['name']; - $language->isEnabled = !((int)$row['disabled']); - - $languages[$row[$key]] = $language; - } - - return $languages; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Location/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/Content/Location/Gateway.php deleted file mode 100644 index d49a1f0286..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Location/Gateway.php +++ /dev/null @@ -1,370 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct; - -/** - * Base class for location gateways. - * - * @internal For internal use by Persistence Handlers. - */ -abstract class Gateway -{ - public const TRASH_TABLE = 'ezcontentobject_trash'; - - /** - * Constants for node assignment op codes. - */ - public const NODE_ASSIGNMENT_OP_CODE_NOP = 0; - public const NODE_ASSIGNMENT_OP_CODE_EXECUTE = 1; - public const NODE_ASSIGNMENT_OP_CODE_CREATE_NOP = 2; - public const NODE_ASSIGNMENT_OP_CODE_CREATE = 3; - public const NODE_ASSIGNMENT_OP_CODE_MOVE_NOP = 4; - public const NODE_ASSIGNMENT_OP_CODE_MOVE = 5; - public const NODE_ASSIGNMENT_OP_CODE_REMOVE_NOP = 6; - public const NODE_ASSIGNMENT_OP_CODE_REMOVE = 7; - public const NODE_ASSIGNMENT_OP_CODE_SET_NOP = 8; - public const NODE_ASSIGNMENT_OP_CODE_SET = 9; - - public const CONTENT_TREE_TABLE = 'ezcontentobject_tree'; - public const CONTENT_TREE_SEQ = 'ezcontentobject_tree_node_id_seq'; - - /** - * Returns an array with basic node data. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * - * @param string[]|null $translations - * @param bool $useAlwaysAvailable Respect always available flag on content when filtering on $translations. - * - * @return array - */ - abstract public function getBasicNodeData( - int $nodeId, - array $translations = null, - bool $useAlwaysAvailable = true - ): array; - - /** - * Returns an array with node data for several locations. - * - * @param int[] $locationIds - * @param string[]|null $translations - * @param bool $useAlwaysAvailable Respect always available flag on content when filtering on $translations. - * - * @return array - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function getNodeDataList( - array $locationIds, - array $translations = null, - bool $useAlwaysAvailable = true - ): iterable; - - /** - * Returns an array with basic node data for the node with $remoteId. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * - * @param string[]|null $translations - * @param bool $useAlwaysAvailable Respect always available flag on content when filtering on $translations. - */ - abstract public function getBasicNodeDataByRemoteId( - string $remoteId, - array $translations = null, - bool $useAlwaysAvailable = true - ): array; - - /** - * Loads data for all Locations for $contentId, optionally only in the - * subtree starting at $rootLocationId. - */ - abstract public function loadLocationDataByContent( - int $contentId, - ?int $rootLocationId = null - ): array; - - /** - * Loads data for all Locations for $contentId in trash, optionally only in the - * subtree starting at $rootLocationId. - * - * @return string[] - */ - abstract public function loadLocationDataByTrashContent(int $contentId, ?int $rootLocationId = null): array; - - /** - * Loads data for all parent Locations for unpublished Content by given $contentId. - */ - abstract public function loadParentLocationsDataForDraftContent(int $contentId): array; - - /** - * Find all content in the given subtree. - */ - abstract public function getSubtreeContent(int $sourceId, bool $onlyIds = false): array; - - abstract public function getSubtreeSize(string $path): int; - - /** - * Returns data for the first level children of the location identified by given $locationId. - */ - abstract public function getChildren(int $locationId): array; - - /** - * Update path strings to move nodes in the ezcontentobject_tree table. - * - * This query can likely be optimized to use some more advanced string - * operations, which then depend on the respective database. - * - * @todo optimize - * - * @param array $fromPathString - * @param array $toPathString - */ - abstract public function moveSubtreeNodes(array $fromPathString, array $toPathString): void; - - /** - * Updated subtree modification time for all nodes on path. - * - * @deprecated Not supposed to be in use anymore. - */ - abstract public function updateSubtreeModificationTime( - string $pathString, - ?int $timestamp = null - ): void; - - /** - * Update node assignment table. - */ - abstract public function updateNodeAssignment( - int $contentObjectId, - int $oldParent, - int $newParent, - int $opcode - ): void; - - /** - * Create locations from node assignments. - * - * Convert existing node assignments into real locations. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if parent Location does not exist - */ - abstract public function createLocationsFromNodeAssignments( - int $contentId, - int $versionNo - ): void; - - /** - * Updates all Locations of content identified with $contentId with $versionNo. - */ - abstract public function updateLocationsContentVersionNo(int $contentId, int $versionNo): void; - - /** - * Sets a location to be hidden, and it self + all children to invisible. - */ - abstract public function hideSubtree(string $pathString): void; - - /** - * Sets a location to be unhidden, and self + children to visible unless a parent is hiding the tree. - * If not make sure only children down to first hidden node is marked visible. - */ - abstract public function unHideSubtree(string $pathString): void; - - abstract public function setNodeWithChildrenInvisible(string $pathString): void; - - /** - * Mark a Location and its children as visible unless a parent is hiding the tree. - */ - abstract public function setNodeWithChildrenVisible(string $pathString): void; - - abstract public function setNodeHidden(string $pathString): void; - - /** - * Mark a Location as not hidden. - */ - abstract public function setNodeUnhidden(string $pathString): void; - - /** - * Swaps the content object being pointed to by a location object. - * - * Make the location identified by $locationId1 refer to the Content - * referred to by $locationId2 and vice versa. - */ - abstract public function swap(int $locationId1, int $locationId2): bool; - - /** - * Creates a new location in given $parentNode. - * - * @param array $parentNode parent node raw data - */ - abstract public function create(CreateStruct $createStruct, array $parentNode): Location; - - /** - * Create an entry in the node assignment table. - */ - abstract public function createNodeAssignment( - CreateStruct $createStruct, - int $parentNodeId, - int $type = self::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP - ): void; - - /** - * Deletes node assignment for given $contentId and $versionNo. - * - * If $versionNo is not passed all node assignments for given $contentId are deleted - */ - abstract public function deleteNodeAssignment(int $contentId, ?int $versionNo = null): void; - - /** - * Updates an existing location. - * - * Will not throw anything if location id is invalid or no entries are affected. - */ - abstract public function update(UpdateStruct $location, int $locationId): void; - - /** - * Updates path identification string for given $locationId. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function updatePathIdentificationString( - int $locationId, - int $parentLocationId, - string $text - ): void; - - /** - * Deletes ezcontentobject_tree row for given $locationId (node_id). - */ - abstract public function removeLocation(int $locationId): void; - - /** - * Return data of the next in line node to be set as a new main node. - * - * This returns lowest node id for content identified by $contentId, and not of - * the node identified by given $locationId (current main node). - * Assumes that content has more than one location. - */ - abstract public function getFallbackMainNodeData(int $contentId, int $locationId): array; - - /** - * Sends a single location identified by given $locationId to the trash. - * - * The associated content object is left untouched. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function trashLocation(int $locationId): void; - - /** - * Returns a trashed location to normal state. - * - * Recreates the originally trashed location in the new position. If no new - * position has been specified, it will be tried to re-create the location - * at the old position. If this is not possible ( because the old location - * does not exist any more) and exception is thrown. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function untrashLocation(int $locationId, ?int $newParentId = null): Location; - - /** - * Loads trash data specified by location ID. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function loadTrashByLocation(int $locationId): array; - - /** - * Removes every entries in the trash. - * Will NOT remove associated content objects nor attributes. - * - * Basically truncates ezcontentobject_trash table. - */ - abstract public function cleanupTrash(): void; - - /** - * List trashed items. - * - * @param int $offset - * @param int|null $limit - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sort - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion|null $criterion - * - * @return array entries from ezcontentobject_trash. - */ - abstract public function listTrashed( - int $offset, - ?int $limit, - array $sort = null, - ?Criterion $criterion = null - ): array; - - /** - * Count trashed items. - */ - abstract public function countTrashed(?Criterion $criterion = null): int; - - /** - * Removes trashed element identified by $id from trash. - * Will NOT remove associated content object nor attributes. - * - * @param int $id The trashed location Id - */ - abstract public function removeElementFromTrash(int $id): void; - - /** - * Set section on all content objects in the subtree. - */ - abstract public function setSectionForSubtree(string $pathString, int $sectionId): bool; - - /** - * Returns how many locations given content object identified by $contentId has. - */ - abstract public function countLocationsByContentId(int $contentId): int; - - /** - * Changes main location of content identified by given $contentId to location identified by given $locationId. - * - * Updates ezcontentobject_tree table for the given $contentId and eznode_assignment table for the given - * $contentId, $parentLocationId and $versionNo - * - * @param int $versionNo version number, needed to update eznode_assignment table - * @param int $parentLocationId parent location of location identified by $locationId, needed to update - * eznode_assignment table - */ - abstract public function changeMainLocation( - int $contentId, - int $locationId, - int $versionNo, - int $parentLocationId - ): void; - - /** - * Get the total number of all Locations, except the Root node. - * - * @see loadAllLocationsData - * - * @return int - */ - abstract public function countAllLocations(): int; - - /** - * Load data of every Location, except the Root node. - * - * @param int $offset Paginator offset - * @param int $limit Paginator limit - * - * @return array - */ - abstract public function loadAllLocationsData(int $offset, int $limit): array; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php deleted file mode 100644 index 495947b5fa..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,1617 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Base\Exceptions\NotFoundException as NotFound; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct; -use PDO; -use RuntimeException; -use function time; - -/** - * Location gateway implementation using the Doctrine database. - * - * @internal Gateway implementation is considered internal. Use Persistence Location Handler instead. - * - * @see \eZ\Publish\SPI\Persistence\Content\Location\Handler - */ -final class DoctrineDatabase extends Gateway -{ - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator */ - private $languageMaskGenerator; - - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */ - private $dbPlatform; - - /** @var \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter */ - private $trashCriteriaConverter; - - /** @var \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter */ - private $trashSortClauseConverter; - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct( - Connection $connection, - MaskGenerator $languageMaskGenerator, - CriteriaConverter $trashCriteriaConverter, - SortClauseConverter $trashSortClauseConverter - ) { - $this->connection = $connection; - $this->dbPlatform = $this->connection->getDatabasePlatform(); - $this->languageMaskGenerator = $languageMaskGenerator; - $this->trashCriteriaConverter = $trashCriteriaConverter; - $this->trashSortClauseConverter = $trashSortClauseConverter; - } - - public function getBasicNodeData( - int $nodeId, - array $translations = null, - bool $useAlwaysAvailable = true - ): array { - $query = $this->createNodeQueryBuilder(['t.*'], $translations, $useAlwaysAvailable); - $query->andWhere( - $query->expr()->eq('t.node_id', $query->createNamedParameter($nodeId, ParameterType::INTEGER)) - ); - - if ($row = $query->execute()->fetch(FetchMode::ASSOCIATIVE)) { - return $row; - } - - throw new NotFound('location', $nodeId); - } - - public function getNodeDataList(array $locationIds, array $translations = null, bool $useAlwaysAvailable = true): iterable - { - $query = $this->createNodeQueryBuilder(['t.*'], $translations, $useAlwaysAvailable); - $query->andWhere( - $query->expr()->in( - 't.node_id', - $query->createNamedParameter($locationIds, Connection::PARAM_INT_ARRAY) - ) - ); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function getBasicNodeDataByRemoteId( - string $remoteId, - array $translations = null, - bool $useAlwaysAvailable = true - ): array { - $query = $this->createNodeQueryBuilder(['t.*'], $translations, $useAlwaysAvailable); - $query->andWhere( - $query->expr()->eq('t.remote_id', $query->createNamedParameter($remoteId, ParameterType::STRING)) - ); - - if ($row = $query->execute()->fetch(FetchMode::ASSOCIATIVE)) { - return $row; - } - - throw new NotFound('location', $remoteId); - } - - public function loadLocationDataByContent(int $contentId, ?int $rootLocationId = null): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('*') - ->from(self::CONTENT_TREE_TABLE, 't') - ->where( - $query->expr()->eq( - 't.contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ); - - if ($rootLocationId !== null) { - $query - ->andWhere( - $this->getSubtreeLimitationExpression($query, $rootLocationId) - ) - ; - } - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - /** - * {@inheritdoc} - */ - public function loadLocationDataByTrashContent(int $contentId, ?int $rootLocationId = null): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('*') - ->from($this->connection->quoteIdentifier('ezcontentobject_trash'), 't') - ->where('t.contentobject_id = :contentobject_id') - ->setParameter('contentobject_id', $contentId, ParameterType::INTEGER); - - if ($rootLocationId !== null) { - $query - ->andWhere( - $this->getSubtreeLimitationExpression($query, $rootLocationId) - ) - ; - } - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadParentLocationsDataForDraftContent(int $contentId): array - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select('DISTINCT t.*') - ->from(self::CONTENT_TREE_TABLE, 't') - ->innerJoin( - 't', - 'eznode_assignment', - 'a', - $expr->andX( - $expr->eq( - 't.node_id', - 'a.parent_node' - ), - $expr->eq( - 'a.contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ), - $expr->eq( - 'a.op_code', - $query->createPositionalParameter( - self::NODE_ASSIGNMENT_OP_CODE_CREATE, - ParameterType::INTEGER - ) - ) - ) - ) - ->innerJoin( - 'a', - 'ezcontentobject', - 'c', - $expr->andX( - $expr->eq( - 'a.contentobject_id', - 'c.id' - ), - $expr->eq( - 'c.status', - $query->createPositionalParameter( - ContentInfo::STATUS_DRAFT, - ParameterType::INTEGER - ) - ) - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function getSubtreeContent(int $sourceId, bool $onlyIds = false): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select($onlyIds ? 'node_id, contentobject_id, depth' : '*') - ->from(self::CONTENT_TREE_TABLE, 't') - ->where($this->getSubtreeLimitationExpression($query, $sourceId)) - ->orderBy('t.depth') - ->addOrderBy('t.node_id'); - $statement = $query->execute(); - - $results = $statement->fetchAll($onlyIds ? (FetchMode::COLUMN | PDO::FETCH_GROUP) : FetchMode::ASSOCIATIVE); - // array_map() is used to map all elements stored as $results[$i][0] to $results[$i] - return $onlyIds - ? array_map(static function (array $result) { - return $result[0]; - }, $results) - : $results; - } - - public function getSubtreeSize(string $path): int - { - $query = $this->createNodeQueryBuilder([$this->dbPlatform->getCountExpression('node_id')]); - $query->andWhere( - $query->expr()->like( - 't.path_string', - $query->createPositionalParameter( - $path . '%', - ) - ) - ); - - return (int) $query->execute()->fetchOne(); - } - - /** - * Return constraint which limits the given $query to the subtree starting at $rootLocationId. - */ - private function getSubtreeLimitationExpression( - QueryBuilder $query, - int $rootLocationId - ): string { - return $query->expr()->like( - 't.path_string', - $query->createPositionalParameter( - '%/' . ((string)$rootLocationId) . '/%', - ParameterType::STRING - ) - ); - } - - public function getChildren(int $locationId): array - { - $query = $this->connection->createQueryBuilder(); - $query->select('*')->from( - self::CONTENT_TREE_TABLE - )->where( - $query->expr()->eq( - 'ezcontentobject_tree.parent_node_id', - $query->createPositionalParameter($locationId, ParameterType::INTEGER) - ) - ); - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - private function getSubtreeNodesData(string $pathString): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - 'node_id', - 'parent_node_id', - 'path_string', - 'path_identification_string', - 'is_hidden' - ) - ->from(self::CONTENT_TREE_TABLE) - ->where( - $query->expr()->like( - 'path_string', - $query->createPositionalParameter($pathString . '%', ParameterType::STRING) - ) - ); - $statement = $query->execute(); - - return $statement->fetchAll(); - } - - /** - * @throws \Doctrine\DBAL\Exception - * @throws \Doctrine\DBAL\Driver\Exception - */ - public function moveSubtreeNodes(array $sourceNodeData, array $destinationNodeData): void - { - $fromPathString = $sourceNodeData['path_string']; - $contentObjectId = $sourceNodeData['contentobject_id']; - - $rows = $this->getSubtreeNodesData($fromPathString); - - $oldParentPathString = implode('/', array_slice(explode('/', $fromPathString), 0, -2)) . '/'; - $oldParentPathIdentificationString = implode( - '/', - array_slice(explode('/', $sourceNodeData['path_identification_string']), 0, -1) - ); - - $hiddenNodeIds = $this->getHiddenNodeIds($contentObjectId); - foreach ($rows as $row) { - // Prefixing ensures correct replacement when old parent is root node - $newPathString = str_replace( - 'prefix' . $oldParentPathString, - $destinationNodeData['path_string'], - 'prefix' . $row['path_string'] - ); - $replace = rtrim($destinationNodeData['path_identification_string'], '/'); - if (empty($oldParentPathIdentificationString)) { - $replace .= '/'; - } - $newPathIdentificationString = str_replace( - 'prefix' . $oldParentPathIdentificationString, - $replace, - 'prefix' . $row['path_identification_string'] - ); - $newParentId = $row['parent_node_id']; - if ($row['path_string'] === $fromPathString) { - $newParentId = (int)implode('', array_slice(explode('/', $newPathString), -3, 1)); - } - - $this->moveSingleSubtreeNode( - (int)$row['node_id'], - $sourceNodeData, - $destinationNodeData, - $newPathString, - $newPathIdentificationString, - $newParentId, - $hiddenNodeIds - ); - } - } - - /** - * @param int $contentObjectId - * - * @return int[] - * - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \Doctrine\DBAL\Exception - */ - private function getHiddenNodeIds(int $contentObjectId): array - { - $query = $this->buildHiddenSubtreeQuery('node_id'); - $expr = $query->expr(); - $query - ->andWhere( - $expr->eq( - 'id', - $query->createPositionalParameter( - $contentObjectId, - ParameterType::INTEGER - ) - ) - ); - $statement = $query->execute(); - - $result = $statement->fetchFirstColumn(); - - return array_map('intval', $result); - } - - /** - * @param int[] $hiddenNodeIds - */ - private function isHiddenByParentOrSelf(string $pathString, array $hiddenNodeIds): bool - { - $parentNodeIds = array_map('intval', explode('/', trim($pathString, '/'))); - foreach ($parentNodeIds as $parentNodeId) { - if (in_array($parentNodeId, $hiddenNodeIds, true)) { - return true; - } - } - - return false; - } - - /** - * @param array $sourceNodeData - * @param array $destinationNodeData - * @param int[] $hiddenNodeIds - */ - private function moveSingleSubtreeNode( - int $nodeId, - array $sourceNodeData, - array $destinationNodeData, - string $newPathString, - string $newPathIdentificationString, - int $newParentId, - array $hiddenNodeIds - ): void { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::CONTENT_TREE_TABLE) - ->set( - 'path_string', - $query->createPositionalParameter($newPathString, ParameterType::STRING) - ) - ->set( - 'path_identification_string', - $query->createPositionalParameter( - $newPathIdentificationString, - ParameterType::STRING - ) - ) - ->set( - 'depth', - $query->createPositionalParameter( - substr_count($newPathString, '/') - 2, - ParameterType::INTEGER - ) - ) - ->set( - 'parent_node_id', - $query->createPositionalParameter($newParentId, ParameterType::INTEGER) - ); - - if ($destinationNodeData['is_hidden'] || $destinationNodeData['is_invisible']) { - // CASE 1: Mark whole tree as invisible if destination is invisible and/or hidden - $query->set( - 'is_invisible', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ); - } elseif (!$sourceNodeData['is_hidden'] && $sourceNodeData['is_invisible']) { - // CASE 2: source is only invisible, we will need to re-calculate whole moved tree visibility - $query->set( - 'is_invisible', - $query->createPositionalParameter( - $this->isHiddenByParentOrSelf($newPathString, $hiddenNodeIds) ? 1 : 0, - ParameterType::INTEGER - ) - ); - } - - $query->where( - $query->expr()->eq( - 'node_id', - $query->createPositionalParameter($nodeId, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - public function updateSubtreeModificationTime(string $pathString, ?int $timestamp = null): void - { - $nodes = array_filter(explode('/', $pathString)); - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::CONTENT_TREE_TABLE) - ->set( - 'modified_subnode', - $query->createPositionalParameter( - $timestamp ?: time(), - ParameterType::INTEGER - ) - ) - ->where( - $query->expr()->in( - 'node_id', - $nodes - ) - ); - $query->execute(); - } - - public function hideSubtree(string $pathString): void - { - $this->setNodeWithChildrenInvisible($pathString); - $this->setNodeHidden($pathString); - } - - public function setNodeWithChildrenInvisible(string $pathString): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::CONTENT_TREE_TABLE) - ->set( - 'is_invisible', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ) - ->set( - 'modified_subnode', - $query->createPositionalParameter(time(), ParameterType::INTEGER) - ) - ->where( - $query->expr()->like( - 'path_string', - $query->createPositionalParameter($pathString . '%', ParameterType::STRING) - ) - ); - - $query->execute(); - } - - public function setNodeHidden(string $pathString): void - { - $this->setNodeHiddenStatus($pathString, true); - } - - private function setNodeHiddenStatus(string $pathString, bool $isHidden): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::CONTENT_TREE_TABLE) - ->set( - 'is_hidden', - $query->createPositionalParameter((int) $isHidden, ParameterType::INTEGER) - ) - ->where( - $query->expr()->eq( - 'path_string', - $query->createPositionalParameter($pathString, ParameterType::STRING) - ) - ); - - $query->execute(); - } - - public function unHideSubtree(string $pathString): void - { - $this->setNodeUnhidden($pathString); - $this->setNodeWithChildrenVisible($pathString); - } - - public function setNodeWithChildrenVisible(string $pathString): void - { - // Check if any parent nodes are explicitly hidden - if ($this->isAnyNodeInPathExplicitlyHidden($pathString)) { - // There are parent nodes set hidden, so that we can skip marking - // something visible again. - return; - } - - // Find nodes of explicitly hidden subtrees in the subtree which - // should remain unhidden - $hiddenSubtrees = $this->loadHiddenSubtreesByPath($pathString); - - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->update(self::CONTENT_TREE_TABLE) - ->set( - 'is_invisible', - $query->createPositionalParameter(0, ParameterType::INTEGER) - ) - ->set( - 'modified_subnode', - $query->createPositionalParameter(time(), ParameterType::INTEGER) - ); - - // Build where expression selecting the nodes, which should not be made hidden - $query - ->where( - $expr->like( - 'path_string', - $query->createPositionalParameter($pathString . '%', ParameterType::STRING) - ) - ); - if (count($hiddenSubtrees) > 0) { - foreach ($hiddenSubtrees as $subtreePathString) { - $query - ->andWhere( - $expr->notLike( - 'path_string', - $query->createPositionalParameter( - $subtreePathString . '%', - ParameterType::STRING - ) - ) - ); - } - } - - $query->execute(); - } - - private function isAnyNodeInPathExplicitlyHidden(string $pathString): bool - { - $query = $this->buildHiddenSubtreeQuery( - $this->dbPlatform->getCountExpression('path_string') - ); - $expr = $query->expr(); - $query - ->andWhere( - $expr->in( - 't.node_id', - $query->createPositionalParameter( - array_filter(explode('/', $pathString)), - Connection::PARAM_INT_ARRAY - ) - ) - ); - $count = (int)$query->execute()->fetchColumn(); - - return $count > 0; - } - - /** - * @return array list of path strings - */ - private function loadHiddenSubtreesByPath(string $pathString): array - { - $query = $this->buildHiddenSubtreeQuery('path_string'); - $expr = $query->expr(); - $query - ->andWhere( - $expr->like( - 'path_string', - $query->createPositionalParameter( - $pathString . '%', - ParameterType::STRING - ) - ) - ); - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::COLUMN); - } - - private function buildHiddenSubtreeQuery(string $selectExpr): QueryBuilder - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select($selectExpr) - ->from(self::CONTENT_TREE_TABLE, 't') - ->leftJoin('t', 'ezcontentobject', 'c', 't.contentobject_id = c.id') - ->where( - $expr->orX( - $expr->eq( - 't.is_hidden', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ), - $expr->eq( - 'c.is_hidden', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ) - ) - ); - - return $query; - } - - public function setNodeUnhidden(string $pathString): void - { - $this->setNodeHiddenStatus($pathString, false); - } - - public function swap(int $locationId1, int $locationId2): bool - { - $queryBuilder = $this->connection->createQueryBuilder(); - $expr = $queryBuilder->expr(); - $queryBuilder - ->select('node_id', 'main_node_id', 'contentobject_id', 'contentobject_version') - ->from(self::CONTENT_TREE_TABLE) - ->where( - $expr->in( - 'node_id', - ':locationIds' - ) - ) - ->setParameter('locationIds', [$locationId1, $locationId2], Connection::PARAM_INT_ARRAY) - ; - $statement = $queryBuilder->execute(); - $contentObjects = []; - foreach ($statement->fetchAll(FetchMode::ASSOCIATIVE) as $row) { - $row['is_main_node'] = (int)$row['main_node_id'] === (int)$row['node_id']; - $contentObjects[$row['node_id']] = $row; - } - - if (!isset($contentObjects[$locationId1], $contentObjects[$locationId2])) { - throw new RuntimeException( - sprintf( - '%s: failed to fetch either Location %d or Location %d', - __METHOD__, - $locationId1, - $locationId2 - ) - ); - } - $content1data = $contentObjects[$locationId1]; - $content2data = $contentObjects[$locationId2]; - - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder - ->update(self::CONTENT_TREE_TABLE) - ->set('contentobject_id', ':contentId') - ->set('contentobject_version', ':versionNo') - ->set('main_node_id', ':mainNodeId') - ->where( - $expr->eq('node_id', ':locationId') - ); - - $queryBuilder - ->setParameter(':contentId', $content2data['contentobject_id']) - ->setParameter(':versionNo', $content2data['contentobject_version']) - ->setParameter( - ':mainNodeId', - // make main Location main again, preserve main Location id of non-main one - $content2data['is_main_node'] - ? $content1data['node_id'] - : $content2data['main_node_id'] - ) - ->setParameter('locationId', $locationId1); - - // update Location 1 entry - $queryBuilder->execute(); - - $queryBuilder - ->setParameter(':contentId', $content1data['contentobject_id']) - ->setParameter(':versionNo', $content1data['contentobject_version']) - ->setParameter( - ':mainNodeId', - $content1data['is_main_node'] - // make main Location main again, preserve main Location id of non-main one - ? $content2data['node_id'] - : $content1data['main_node_id'] - ) - ->setParameter('locationId', $locationId2); - - // update Location 2 entry - $queryBuilder->execute(); - - return true; - } - - public function create(CreateStruct $createStruct, array $parentNode): Location - { - $location = $this->insertLocationIntoContentTree($createStruct, $parentNode); - - $mainLocationId = $createStruct->mainLocationId === true ? $location->id : $createStruct->mainLocationId; - $location->pathString = $parentNode['path_string'] . $location->id . '/'; - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::CONTENT_TREE_TABLE) - ->set( - 'path_string', - $query->createPositionalParameter($location->pathString, ParameterType::STRING) - ) - ->set( - 'main_node_id', - $query->createPositionalParameter($mainLocationId, ParameterType::INTEGER) - ) - ->where( - $query->expr()->eq( - 'node_id', - $query->createPositionalParameter($location->id, ParameterType::INTEGER) - ) - ); - - $query->execute(); - - return $location; - } - - public function createNodeAssignment( - CreateStruct $createStruct, - int $parentNodeId, - int $type = self::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP - ): void { - $isMain = ($createStruct->mainLocationId === true ? 1 : 0); - - $query = $this->connection->createQueryBuilder(); - $query - ->insert('eznode_assignment') - ->values( - [ - 'contentobject_id' => ':contentobject_id', - 'contentobject_version' => ':contentobject_version', - 'from_node_id' => ':from_node_id', - 'is_main' => ':is_main', - 'op_code' => ':op_code', - 'parent_node' => ':parent_node', - 'parent_remote_id' => ':parent_remote_id', - 'remote_id' => ':remote_id', - 'sort_field' => ':sort_field', - 'sort_order' => ':sort_order', - 'priority' => ':priority', - 'is_hidden' => ':is_hidden', - ] - ) - ->setParameters( - [ - 'contentobject_id' => $createStruct->contentId, - 'contentobject_version' => $createStruct->contentVersion, - // from_node_id: unused field - 'from_node_id' => 0, - // is_main: changed by the business layer, later - 'is_main' => $isMain, - 'op_code' => $type, - 'parent_node' => $parentNodeId, - // parent_remote_id column should contain the remote id of the corresponding Location - 'parent_remote_id' => $createStruct->remoteId, - // remote_id column should contain the remote id of the node assignment itself, - // however this was never implemented completely in Legacy Stack, so we just set - // it to default value '0' - 'remote_id' => '0', - 'sort_field' => $createStruct->sortField, - 'sort_order' => $createStruct->sortOrder, - 'priority' => $createStruct->priority, - 'is_hidden' => $createStruct->hidden, - ], - [ - 'contentobject_id' => ParameterType::INTEGER, - 'contentobject_version' => ParameterType::INTEGER, - 'from_node_id' => ParameterType::INTEGER, - 'is_main' => ParameterType::INTEGER, - 'op_code' => ParameterType::INTEGER, - 'parent_node' => ParameterType::INTEGER, - 'parent_remote_id' => ParameterType::STRING, - 'remote_id' => ParameterType::STRING, - 'sort_field' => ParameterType::INTEGER, - 'sort_order' => ParameterType::INTEGER, - 'priority' => ParameterType::INTEGER, - 'is_hidden' => ParameterType::INTEGER, - ] - ); - $query->execute(); - } - - public function deleteNodeAssignment(int $contentId, ?int $versionNo = null): void - { - $query = $this->connection->createQueryBuilder(); - $query->delete( - 'eznode_assignment' - )->where( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ); - if (isset($versionNo)) { - $query->andWhere( - $query->expr()->eq( - 'contentobject_version', - $query->createPositionalParameter($versionNo, ParameterType::INTEGER) - ) - ); - } - $query->execute(); - } - - public function updateNodeAssignment( - int $contentObjectId, - int $oldParent, - int $newParent, - int $opcode - ): void { - $query = $this->connection->createQueryBuilder(); - $query - ->update('eznode_assignment') - ->set( - 'parent_node', - $query->createPositionalParameter($newParent, ParameterType::INTEGER) - ) - ->set( - 'op_code', - $query->createPositionalParameter($opcode, ParameterType::INTEGER) - ) - ->where( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter( - $contentObjectId, - ParameterType::INTEGER - ) - ) - ) - ->andWhere( - $query->expr()->eq( - 'parent_node', - $query->createPositionalParameter( - $oldParent, - ParameterType::INTEGER - ) - ) - ); - $query->execute(); - } - - public function createLocationsFromNodeAssignments(int $contentId, int $versionNo): void - { - // select all node assignments with OP_CODE_CREATE (3) for this content - $query = $this->connection->createQueryBuilder(); - $query - ->select('*') - ->from('eznode_assignment') - ->where( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ) - ->andWhere( - $query->expr()->eq( - 'contentobject_version', - $query->createPositionalParameter($versionNo, ParameterType::INTEGER) - ) - ) - ->andWhere( - $query->expr()->eq( - 'op_code', - $query->createPositionalParameter( - self::NODE_ASSIGNMENT_OP_CODE_CREATE, - ParameterType::INTEGER - ) - ) - ) - ->orderBy('id'); - $statement = $query->execute(); - - // convert all these assignments to nodes - - while ($row = $statement->fetch(FetchMode::ASSOCIATIVE)) { - $isMain = (bool)$row['is_main']; - // set null for main to indicate that new Location ID is required - $mainLocationId = $isMain ? null : $this->getMainNodeId($contentId); - - $parentLocationData = $this->getBasicNodeData((int)$row['parent_node']); - $isInvisible = $row['is_hidden'] || $parentLocationData['is_hidden'] || $parentLocationData['is_invisible']; - $this->create( - new CreateStruct( - [ - 'contentId' => $row['contentobject_id'], - 'contentVersion' => $row['contentobject_version'], - // BC layer: for CreateStruct "true" means that a main Location should be created - 'mainLocationId' => $mainLocationId ?? true, - 'remoteId' => $row['parent_remote_id'], - 'sortField' => $row['sort_field'], - 'sortOrder' => $row['sort_order'], - 'priority' => $row['priority'], - 'hidden' => $row['is_hidden'], - 'invisible' => $isInvisible, - ] - ), - $parentLocationData - ); - - $this->updateNodeAssignment( - (int)$row['contentobject_id'], - (int)$row['parent_node'], - (int)$row['parent_node'], - self::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP - ); - } - } - - public function updateLocationsContentVersionNo(int $contentId, int $versionNo): void - { - $query = $this->connection->createQueryBuilder(); - $query->update( - self::CONTENT_TREE_TABLE - )->set( - 'contentobject_version', - $query->createPositionalParameter($versionNo, ParameterType::INTEGER) - )->where( - $query->expr()->eq( - 'contentobject_id', - $contentId - ) - ); - $query->execute(); - } - - /** - * Search for the main nodeId of $contentId. - */ - private function getMainNodeId(int $contentId): ?int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('node_id') - ->from(self::CONTENT_TREE_TABLE) - ->where( - $query->expr()->andX( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'node_id', - 'main_node_id' - ) - ) - ); - $statement = $query->execute(); - - $result = $statement->fetchColumn(); - - return false !== $result ? (int)$result : null; - } - - /** - * Updates an existing location. - * - * Will not throw anything if location id is invalid or no entries are affected. - * - * @param \eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct $location - * @param int $locationId - */ - public function update(UpdateStruct $location, $locationId): void - { - $query = $this->connection->createQueryBuilder(); - - $query - ->update(self::CONTENT_TREE_TABLE) - ->set( - 'priority', - $query->createPositionalParameter($location->priority, ParameterType::INTEGER) - ) - ->set( - 'remote_id', - $query->createPositionalParameter($location->remoteId, ParameterType::STRING) - ) - ->set( - 'sort_order', - $query->createPositionalParameter($location->sortOrder, ParameterType::INTEGER) - ) - ->set( - 'sort_field', - $query->createPositionalParameter($location->sortField, ParameterType::INTEGER) - ) - ->where( - $query->expr()->eq( - 'node_id', - $locationId - ) - ); - $query->execute(); - } - - public function updatePathIdentificationString($locationId, $parentLocationId, $text): void - { - $parentData = $this->getBasicNodeData($parentLocationId); - - $newPathIdentificationString = empty($parentData['path_identification_string']) ? - $text : - $parentData['path_identification_string'] . '/' . $text; - - $query = $this->connection->createQueryBuilder(); - $query->update( - self::CONTENT_TREE_TABLE - )->set( - 'path_identification_string', - $query->createPositionalParameter($newPathIdentificationString, ParameterType::STRING) - )->where( - $query->expr()->eq( - 'node_id', - $query->createPositionalParameter($locationId, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - /** - * Deletes ezcontentobject_tree row for given $locationId (node_id). - * - * @param mixed $locationId - */ - public function removeLocation($locationId): void - { - $query = $this->connection->createQueryBuilder(); - $query->delete( - self::CONTENT_TREE_TABLE - )->where( - $query->expr()->eq( - 'node_id', - $query->createPositionalParameter($locationId, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - /** - * Return data of the next in line node to be set as a new main node. - * - * This returns lowest node id for content identified by $contentId, and not of - * the node identified by given $locationId (current main node). - * Assumes that content has more than one location. - * - * @param mixed $contentId - * @param mixed $locationId - * - * @return array - */ - public function getFallbackMainNodeData($contentId, $locationId): array - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select( - 'node_id', - 'contentobject_version', - 'parent_node_id' - ) - ->from(self::CONTENT_TREE_TABLE) - ->where( - $expr->eq( - 'contentobject_id', - $query->createPositionalParameter( - $contentId, - ParameterType::INTEGER - ) - ) - ) - ->andWhere( - $expr->neq( - 'node_id', - $query->createPositionalParameter( - $locationId, - ParameterType::INTEGER - ) - ) - ) - ->orderBy('node_id', 'ASC') - ->setMaxResults(1); - - $statement = $query->execute(); - - return $statement->fetch(FetchMode::ASSOCIATIVE); - } - - public function trashLocation(int $locationId): void - { - $locationRow = $this->getBasicNodeData($locationId); - - $query = $this->connection->createQueryBuilder(); - $query->insert('ezcontentobject_trash'); - - unset($locationRow['contentobject_is_published']); - $locationRow['trashed'] = time(); - foreach ($locationRow as $key => $value) { - $query->setValue($key, $query->createPositionalParameter($value)); - } - - $query->execute(); - - $this->removeLocation($locationRow['node_id']); - $this->setContentStatus((int)$locationRow['contentobject_id'], ContentInfo::STATUS_TRASHED); - } - - public function untrashLocation(int $locationId, ?int $newParentId = null): Location - { - $row = $this->loadTrashByLocation($locationId); - - $newLocation = $this->create( - new CreateStruct( - [ - 'priority' => $row['priority'], - 'hidden' => $row['is_hidden'], - 'invisible' => $row['is_invisible'], - 'remoteId' => $row['remote_id'], - 'contentId' => $row['contentobject_id'], - 'contentVersion' => $row['contentobject_version'], - 'mainLocationId' => true, // Restored location is always main location - 'sortField' => $row['sort_field'], - 'sortOrder' => $row['sort_order'], - ] - ), - $this->getBasicNodeData($newParentId ?? (int)$row['parent_node_id']) - ); - - $this->removeElementFromTrash($locationId); - $this->setContentStatus((int)$row['contentobject_id'], ContentInfo::STATUS_PUBLISHED); - - return $newLocation; - } - - private function setContentStatus(int $contentId, int $status): void - { - $query = $this->connection->createQueryBuilder(); - $query->update( - 'ezcontentobject' - )->set( - 'status', - $query->createPositionalParameter($status, ParameterType::INTEGER) - )->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - public function loadTrashByLocation(int $locationId): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('*') - ->from('ezcontentobject_trash') - ->where( - $query->expr()->eq( - 'node_id', - $query->createPositionalParameter($locationId, ParameterType::INTEGER) - ) - ); - $statement = $query->execute(); - - if ($row = $statement->fetch(FetchMode::ASSOCIATIVE)) { - return $row; - } - - throw new NotFound('trash', $locationId); - } - - public function listTrashed( - int $offset, - ?int $limit, - array $sort = null, - ?Criterion $criterion = null - ): array { - $query = $this->connection->createQueryBuilder(); - $query - ->select('t.*') - ->from(self::TRASH_TABLE, 't') - ->leftJoin('t', ContentGateway::CONTENT_ITEM_TABLE, 'c', 't.contentobject_id = c.id'); - - $this->addSort($sort, $query); - $this->addConditionsByCriterion($criterion, $query); - - if ($limit !== null) { - $query->setMaxResults($limit); - $query->setFirstResult($offset); - } - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function countTrashed(?Criterion $criterion = null): int - { - $query = $this->connection->createQueryBuilder() - ->select($this->dbPlatform->getCountExpression(1)) - ->from(self::TRASH_TABLE, 't') - ->innerJoin('t', ContentGateway::CONTENT_ITEM_TABLE, 'c', 't.contentobject_id = c.id'); - - $this->addConditionsByCriterion($criterion, $query); - - return (int)$query->execute()->fetchColumn(); - } - - /** - * Removes every entries in the trash. - * Will NOT remove associated content objects nor attributes. - * - * Basically truncates ezcontentobject_trash table. - */ - public function cleanupTrash(): void - { - $query = $this->connection->createQueryBuilder(); - $query->delete('ezcontentobject_trash'); - $query->execute(); - } - - public function removeElementFromTrash(int $id): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete('ezcontentobject_trash') - ->where( - $query->expr()->eq( - 'node_id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - public function setSectionForSubtree(string $pathString, int $sectionId): bool - { - $selectContentIdsQuery = $this->connection->createQueryBuilder(); - $selectContentIdsQuery - ->select('t.contentobject_id') - ->from(self::CONTENT_TREE_TABLE, 't') - ->where( - $selectContentIdsQuery->expr()->like( - 't.path_string', - $selectContentIdsQuery->createPositionalParameter("{$pathString}%") - ) - ); - - $contentIds = array_map( - 'intval', - $selectContentIdsQuery->execute()->fetchAll(FetchMode::COLUMN) - ); - - if (empty($contentIds)) { - return false; - } - - $updateSectionQuery = $this->connection->createQueryBuilder(); - $updateSectionQuery - ->update('ezcontentobject') - ->set( - 'section_id', - $updateSectionQuery->createPositionalParameter($sectionId, ParameterType::INTEGER) - ) - ->where( - $updateSectionQuery->expr()->in( - 'id', - $contentIds - ) - ); - $affectedRows = $updateSectionQuery->execute(); - - return $affectedRows > 0; - } - - public function countLocationsByContentId(int $contentId): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - $this->dbPlatform->getCountExpression('*') - ) - ->from(self::CONTENT_TREE_TABLE) - ->where( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ); - $stmt = $query->execute(); - - return (int)$stmt->fetchColumn(); - } - - public function changeMainLocation( - int $contentId, - int $locationId, - int $versionNo, - int $parentLocationId - ): void { - // Update ezcontentobject_tree table - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::CONTENT_TREE_TABLE) - ->set( - 'main_node_id', - $query->createPositionalParameter($locationId, ParameterType::INTEGER) - ) - ->where( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ) - ; - $query->execute(); - - // Update is_main in eznode_assignment table - $this->setIsMainForContentVersionParentNodeAssignment( - $contentId, - $versionNo, - $parentLocationId - ); - } - - public function countAllLocations(): int - { - $query = $this->createNodeQueryBuilder(['count(node_id)']); - // exclude absolute Root Location (not to be confused with SiteAccess Tree Root) - $query->where($query->expr()->neq('node_id', 'parent_node_id')); - - $statement = $query->execute(); - - return (int) $statement->fetch(FetchMode::COLUMN); - } - - public function loadAllLocationsData(int $offset, int $limit): array - { - $query = $this - ->createNodeQueryBuilder( - [ - 'node_id', - 'priority', - 'is_hidden', - 'is_invisible', - 'remote_id', - 'contentobject_id', - 'parent_node_id', - 'path_identification_string', - 'path_string', - 'depth', - 'sort_field', - 'sort_order', - ] - ); - $query - // exclude absolute Root Location (not to be confused with SiteAccess Tree Root) - ->where($query->expr()->neq('node_id', 'parent_node_id')) - ->setFirstResult($offset) - ->setMaxResults($limit) - ->orderBy('depth', 'ASC') - ->addOrderBy('node_id', 'ASC') - ; - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - /** - * Create QueryBuilder for selecting Location (node) data. - * - * @param array $columns column or expression list - * @param array|null $translations Filters on language mask of content if provided. - * @param bool $useAlwaysAvailable Respect always available flag on content when filtering on $translations. - * - * @return \Doctrine\DBAL\Query\QueryBuilder - */ - private function createNodeQueryBuilder( - array $columns, - array $translations = null, - bool $useAlwaysAvailable = true - ): QueryBuilder { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder - ->select($columns) - ->from(self::CONTENT_TREE_TABLE, 't') - ; - - if (!empty($translations)) { - $this->appendContentItemTranslationsConstraint($queryBuilder, $translations, $useAlwaysAvailable); - } - - return $queryBuilder; - } - - private function appendContentItemTranslationsConstraint( - QueryBuilder $queryBuilder, - array $translations, - bool $useAlwaysAvailable - ): void { - $expr = $queryBuilder->expr(); - try { - $mask = $this->languageMaskGenerator->generateLanguageMaskFromLanguageCodes( - $translations, - $useAlwaysAvailable - ); - } catch (NotFoundException $e) { - return; - } - - $queryBuilder->leftJoin( - 't', - 'ezcontentobject', - 'c', - $expr->eq('t.contentobject_id', 'c.id') - ); - - $queryBuilder->andWhere( - $expr->orX( - $expr->gt( - $this->dbPlatform->getBitAndComparisonExpression('c.language_mask', $mask), - 0 - ), - // Root location doesn't have language mask - $expr->eq( - 't.node_id', - 't.parent_node_id' - ) - ) - ); - } - - /** - * Mark eznode_assignment entry, identified by Content ID and Version ID, as main for the given - * parent Location ID. - * - * **NOTE**: The method erases is_main from the other entries related to Content and Version IDs - */ - private function setIsMainForContentVersionParentNodeAssignment( - int $contentId, - int $versionNo, - int $parentLocationId - ): void { - $query = $this->connection->createQueryBuilder(); - $query - ->update('eznode_assignment') - ->set( - 'is_main', - // set is_main = 1 only for current parent, set 0 for other entries - 'CASE WHEN parent_node <> :parent_location_id THEN 0 ELSE 1 END' - ) - ->where('contentobject_id = :content_id') - ->andWhere('contentobject_version = :version_no') - ->setParameter('parent_location_id', $parentLocationId, ParameterType::INTEGER) - ->setParameter('content_id', $contentId, ParameterType::INTEGER) - ->setParameter('version_no', $versionNo, ParameterType::INTEGER); - - $query->execute(); - } - - /** - * @param array $parentNode raw Location data - */ - private function insertLocationIntoContentTree( - CreateStruct $createStruct, - array $parentNode - ): Location { - $location = new Location(); - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::CONTENT_TREE_TABLE) - ->values( - [ - 'contentobject_id' => ':content_id', - 'contentobject_is_published' => ':is_published', - 'contentobject_version' => ':version_no', - 'depth' => ':depth', - 'is_hidden' => ':is_hidden', - 'is_invisible' => ':is_invisible', - 'modified_subnode' => ':modified_subnode', - 'parent_node_id' => ':parent_node_id', - 'path_string' => ':path_string', - 'priority' => ':priority', - 'remote_id' => ':remote_id', - 'sort_field' => ':sort_field', - 'sort_order' => ':sort_order', - ] - ) - ->setParameters( - [ - 'content_id' => $location->contentId = $createStruct->contentId, - 'is_published' => 1, - 'version_no' => $createStruct->contentVersion, - 'depth' => $location->depth = $parentNode['depth'] + 1, - 'is_hidden' => $location->hidden = $createStruct->hidden, - 'is_invisible' => $location->invisible = $createStruct->invisible, - 'modified_subnode' => time(), - 'parent_node_id' => $location->parentId = $parentNode['node_id'], - 'path_string' => '', // Set later - 'priority' => $location->priority = $createStruct->priority, - 'remote_id' => $location->remoteId = $createStruct->remoteId, - 'sort_field' => $location->sortField = $createStruct->sortField, - 'sort_order' => $location->sortOrder = $createStruct->sortOrder, - ], - [ - 'contentobject_id' => ParameterType::INTEGER, - 'contentobject_is_published' => ParameterType::INTEGER, - 'contentobject_version' => ParameterType::INTEGER, - 'depth' => ParameterType::INTEGER, - 'is_hidden' => ParameterType::INTEGER, - 'is_invisible' => ParameterType::INTEGER, - 'modified_subnode' => ParameterType::INTEGER, - 'parent_node_id' => ParameterType::INTEGER, - 'path_string' => ParameterType::STRING, - 'priority' => ParameterType::INTEGER, - 'remote_id' => ParameterType::STRING, - 'sort_field' => ParameterType::INTEGER, - 'sort_order' => ParameterType::INTEGER, - ] - ); - $query->execute(); - - $location->id = (int)$this->connection->lastInsertId(self::CONTENT_TREE_SEQ); - - return $location; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - private function addConditionsByCriterion(?Criterion $criterion, QueryBuilder $query): void - { - if (null === $criterion) { - return; - } - - $languageSettings = []; - - $query->where( - $this->trashCriteriaConverter->convertCriteria($query, $criterion, $languageSettings) - ); - } - - private function addSort(?array $sort, QueryBuilder $query, array $languageSettings = []): void - { - if (empty($sort)) { - return; - } - - $this->trashSortClauseConverter->applySelect($query, $sort); - $this->trashSortClauseConverter->applyJoin($query, $sort, $languageSettings); - $this->trashSortClauseConverter->applyOrderBy($query); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php deleted file mode 100644 index ed1acf56e2..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,423 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct; -use PDOException; - -/** - * @internal Internal exception conversion layer. - */ -final class ExceptionConversion extends Gateway -{ - /** - * The wrapped gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway - */ - private $innerGateway; - - /** - * Creates a new exception conversion gateway around $innerGateway. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function getBasicNodeData( - int $nodeId, - array $translations = null, - bool $useAlwaysAvailable = true - ): array { - try { - return $this->innerGateway->getBasicNodeData($nodeId, $translations, $useAlwaysAvailable); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getNodeDataList(array $locationIds, array $translations = null, bool $useAlwaysAvailable = true): iterable - { - try { - return $this->innerGateway->getNodeDataList($locationIds, $translations, $useAlwaysAvailable); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getBasicNodeDataByRemoteId( - string $remoteId, - array $translations = null, - bool $useAlwaysAvailable = true - ): array { - try { - return $this->innerGateway->getBasicNodeDataByRemoteId($remoteId, $translations, $useAlwaysAvailable); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadLocationDataByContent(int $contentId, ?int $rootLocationId = null): array - { - try { - return $this->innerGateway->loadLocationDataByContent($contentId, $rootLocationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadLocationDataByTrashContent(int $contentId, ?int $rootLocationId = null): array - { - try { - return $this->innerGateway->loadLocationDataByTrashContent($contentId, $rootLocationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadParentLocationsDataForDraftContent(int $contentId): array - { - try { - return $this->innerGateway->loadParentLocationsDataForDraftContent($contentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getSubtreeContent(int $sourceId, bool $onlyIds = false): array - { - try { - return $this->innerGateway->getSubtreeContent($sourceId, $onlyIds); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getSubtreeSize(string $path): int - { - try { - return $this->innerGateway->getSubtreeSize($path); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getChildren(int $locationId): array - { - try { - return $this->innerGateway->getChildren($locationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function moveSubtreeNodes(array $fromPathString, array $toPathString): void - { - try { - $this->innerGateway->moveSubtreeNodes($fromPathString, $toPathString); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateSubtreeModificationTime(string $pathString, ?int $timestamp = null): void - { - try { - $this->innerGateway->updateSubtreeModificationTime($pathString, $timestamp); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateNodeAssignment( - int $contentObjectId, - int $oldParent, - int $newParent, - int $opcode - ): void { - try { - $this->innerGateway->updateNodeAssignment($contentObjectId, $oldParent, $newParent, $opcode); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function createLocationsFromNodeAssignments(int $contentId, int $versionNo): void - { - try { - $this->innerGateway->createLocationsFromNodeAssignments($contentId, $versionNo); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateLocationsContentVersionNo(int $contentId, int $versionNo): void - { - try { - $this->innerGateway->updateLocationsContentVersionNo($contentId, $versionNo); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function hideSubtree(string $pathString): void - { - try { - $this->innerGateway->hideSubtree($pathString); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function unHideSubtree(string $pathString): void - { - try { - $this->innerGateway->unHideSubtree($pathString); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function setNodeWithChildrenInvisible(string $pathString): void - { - try { - $this->innerGateway->setNodeWithChildrenInvisible($pathString); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function setNodeHidden(string $pathString): void - { - try { - $this->innerGateway->setNodeHidden($pathString); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function setNodeWithChildrenVisible(string $pathString): void - { - try { - $this->innerGateway->setNodeWithChildrenVisible($pathString); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function setNodeUnhidden(string $pathString): void - { - try { - $this->innerGateway->setNodeUnhidden($pathString); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function swap(int $locationId1, int $locationId2): bool - { - try { - return $this->innerGateway->swap($locationId1, $locationId2); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function create(CreateStruct $createStruct, array $parentNode): Location - { - try { - return $this->innerGateway->create($createStruct, $parentNode); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function createNodeAssignment( - CreateStruct $createStruct, - int $parentNodeId, - int $type = self::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP - ): void { - try { - $this->innerGateway->createNodeAssignment($createStruct, $parentNodeId, $type); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteNodeAssignment(int $contentId, ?int $versionNo = null): void - { - try { - $this->innerGateway->deleteNodeAssignment($contentId, $versionNo); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function update(UpdateStruct $location, int $locationId): void - { - try { - $this->innerGateway->update($location, $locationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updatePathIdentificationString( - int $locationId, - int $parentLocationId, - string $text - ): void { - try { - $this->innerGateway->updatePathIdentificationString($locationId, $parentLocationId, $text); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function removeLocation(int $locationId): void - { - try { - $this->innerGateway->removeLocation($locationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getFallbackMainNodeData(int $contentId, int $locationId): array - { - try { - return $this->innerGateway->getFallbackMainNodeData($contentId, $locationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function trashLocation(int $locationId): void - { - try { - $this->innerGateway->trashLocation($locationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function untrashLocation(int $locationId, ?int $newParentId = null): Location - { - try { - return $this->innerGateway->untrashLocation($locationId, $newParentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadTrashByLocation(int $locationId): array - { - try { - return $this->innerGateway->loadTrashByLocation($locationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function cleanupTrash(): void - { - try { - $this->innerGateway->cleanupTrash(); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function listTrashed( - int $offset, - ?int $limit, - array $sort = null, - ?Criterion $criterion = null - ): array { - try { - return $this->innerGateway->listTrashed($offset, $limit, $sort, $criterion); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countTrashed(?Criterion $criterion = null): int - { - try { - return $this->innerGateway->countTrashed($criterion); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function removeElementFromTrash(int $id): void - { - try { - $this->innerGateway->removeElementFromTrash($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function setSectionForSubtree(string $pathString, int $sectionId): bool - { - try { - return $this->innerGateway->setSectionForSubtree($pathString, $sectionId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countLocationsByContentId(int $contentId): int - { - try { - return $this->innerGateway->countLocationsByContentId($contentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function changeMainLocation( - int $contentId, - int $locationId, - int $versionNo, - int $parentLocationId - ): void { - try { - $this->innerGateway->changeMainLocation($contentId, $locationId, $versionNo, $parentLocationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countAllLocations(): int - { - try { - return $this->innerGateway->countAllLocations(); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadAllLocationsData(int $offset, int $limit): array - { - try { - return $this->innerGateway->loadAllLocationsData($offset, $limit); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Location/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Content/Location/Handler.php deleted file mode 100644 index b125238675..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Location/Handler.php +++ /dev/null @@ -1,619 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Location; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\Core\Persistence\Legacy\Content\Handler as ContentHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler as ObjectStateHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as BaseLocationHandler; -use eZ\Publish\SPI\Persistence\Content\Location\Trashed; -use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct; -use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct; - -/** - * The Location Handler interface defines operations on Location elements in the storage engine. - */ -class Handler implements BaseLocationHandler -{ - /** - * Gateway for handling location data. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway - */ - protected $locationGateway; - - /** - * Location locationMapper. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper - */ - protected $locationMapper; - - /** - * Content handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Handler - */ - protected $contentHandler; - - /** - * Object state handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler - */ - protected $objectStateHandler; - - /** - * Tree handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler - */ - protected $treeHandler; - - /** - * Construct from userGateway. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway $locationGateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper $locationMapper - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Handler $contentHandler - * @param \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler $objectStateHandler - * @param \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler $treeHandler - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler - */ - public function __construct( - LocationGateway $locationGateway, - LocationMapper $locationMapper, - ContentHandler $contentHandler, - ObjectStateHandler $objectStateHandler, - TreeHandler $treeHandler - ) { - $this->locationGateway = $locationGateway; - $this->locationMapper = $locationMapper; - $this->contentHandler = $contentHandler; - $this->objectStateHandler = $objectStateHandler; - $this->treeHandler = $treeHandler; - } - - /** - * Returns parent path string for a path string. - * - * @param string $pathString - * - * @return string - */ - protected function getParentPathString($pathString) - { - return implode('/', array_slice(explode('/', $pathString), 0, -2)) . '/'; - } - - /** - * {@inheritdoc} - */ - public function load($locationId, array $translations = null, bool $useAlwaysAvailable = true) - { - return $this->treeHandler->loadLocation($locationId, $translations, $useAlwaysAvailable); - } - - /** - * {@inheritdoc} - */ - public function loadList(array $locationIds, array $translations = null, bool $useAlwaysAvailable = true): iterable - { - $list = $this->locationGateway->getNodeDataList($locationIds, $translations, $useAlwaysAvailable); - - $locations = []; - foreach ($list as $row) { - $id = (int)$row['node_id']; - if (!isset($locations[$id])) { - $locations[$id] = $this->locationMapper->createLocationFromRow($row); - } - } - - return $locations; - } - - /** - * Loads the subtree ids of the location identified by $locationId. - * - * @param int $locationId - * - * @return array Location ids are in the index, Content ids in the value. - */ - public function loadSubtreeIds($locationId) - { - return $this->locationGateway->getSubtreeContent($locationId, true); - } - - /** - * {@inheritdoc} - */ - public function loadByRemoteId($remoteId, array $translations = null, bool $useAlwaysAvailable = true) - { - $data = $this->locationGateway->getBasicNodeDataByRemoteId($remoteId, $translations, $useAlwaysAvailable); - - return $this->locationMapper->createLocationFromRow($data); - } - - /** - * Loads all locations for $contentId, optionally limited to a sub tree - * identified by $rootLocationId. - * - * @param int $contentId - * @param int $rootLocationId - * - * @return \eZ\Publish\SPI\Persistence\Content\Location[] - */ - public function loadLocationsByContent($contentId, $rootLocationId = null) - { - $rows = $this->locationGateway->loadLocationDataByContent($contentId, $rootLocationId); - - return $this->locationMapper->createLocationsFromRows($rows); - } - - /** - * {@inheritdoc} - */ - public function loadLocationsByTrashContent(int $contentId, ?int $rootLocationId = null): array - { - $rows = $this->locationGateway->loadLocationDataByTrashContent($contentId, $rootLocationId); - - return $this->locationMapper->createLocationsFromRows($rows, '', new Trashed()); - } - - /** - * @see \eZ\Publish\SPI\Persistence\Content\Location\Handler::loadParentLocationsForDraftContent - */ - public function loadParentLocationsForDraftContent($contentId) - { - $rows = $this->locationGateway->loadParentLocationsDataForDraftContent($contentId); - - return $this->locationMapper->createLocationsFromRows($rows); - } - - /** - * Returns an array of default content states with content state group id as key. - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState[] - */ - protected function getDefaultContentStates() - { - $defaultObjectStatesMap = []; - - foreach ($this->objectStateHandler->loadAllGroups() as $objectStateGroup) { - foreach ($this->objectStateHandler->loadObjectStates($objectStateGroup->id) as $objectState) { - // Only register the first object state which is the default one. - $defaultObjectStatesMap[$objectStateGroup->id] = $objectState; - break; - } - } - - return $defaultObjectStatesMap; - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content $content - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState[] $contentStates - */ - protected function setContentStates(Content $content, array $contentStates) - { - foreach ($contentStates as $contentStateGroupId => $contentState) { - $this->objectStateHandler->setContentState( - $content->versionInfo->contentInfo->id, - $contentStateGroupId, - $contentState->id - ); - } - } - - /** - * Copy location object identified by $sourceId, into destination identified by $destinationParentId. - * - * Performs a deep copy of the location identified by $sourceId and all of - * its child locations, copying the most recent published content object - * for each location to a new content object without any additional version - * information. Relations are not copied. URLs are not touched at all. - * - * @todo Either move to async/batch or find ways toward optimizing away operations per object. - * @todo Optionally retain dates and set creator - * - * @param mixed $sourceId - * @param mixed $destinationParentId - * @param int|null $newOwnerId - * - * @return \eZ\Publish\SPI\Persistence\Content\Location the newly created Location. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function copySubtree($sourceId, $destinationParentId, $newOwnerId = null) - { - $children = $this->locationGateway->getSubtreeContent($sourceId); - $destinationParentData = $this->locationGateway->getBasicNodeData($destinationParentId); - $defaultObjectStates = $this->getDefaultContentStates(); - $contentMap = []; - $locationMap = [ - $children[0]['parent_node_id'] => [ - 'id' => $destinationParentId, - 'hidden' => (bool)$destinationParentData['is_hidden'], - 'invisible' => (bool)$destinationParentData['is_invisible'], - 'path_identification_string' => $destinationParentData['path_identification_string'], - ], - ]; - - $locations = []; - foreach ($children as $child) { - $locations[$child['contentobject_id']][$child['node_id']] = true; - } - - $time = time(); - $mainLocations = []; - $mainLocationsUpdate = []; - foreach ($children as $index => $child) { - // Copy content - if (!isset($contentMap[$child['contentobject_id']])) { - $content = $this->contentHandler->copy( - (int)$child['contentobject_id'], - (int)$child['contentobject_version'], - $newOwnerId - ); - - $this->setContentStates($content, $defaultObjectStates); - - $content = $this->contentHandler->publish( - $content->versionInfo->contentInfo->id, - $content->versionInfo->contentInfo->currentVersionNo, - new MetadataUpdateStruct( - [ - 'publicationDate' => $time, - 'modificationDate' => $time, - ] - ) - ); - - $contentMap[$child['contentobject_id']] = $content->versionInfo->contentInfo->id; - } - - $createStruct = $this->locationMapper->getLocationCreateStruct($child); - $createStruct->contentId = $contentMap[$child['contentobject_id']]; - $parentData = $locationMap[$child['parent_node_id']]; - $createStruct->parentId = $parentData['id']; - $createStruct->invisible = $createStruct->invisible - || $createStruct->hidden - || $parentData['hidden'] - || $parentData['invisible']; - $pathString = explode('/', $child['path_identification_string']); - $pathString = end($pathString); - $createStruct->pathIdentificationString = strlen($pathString) > 0 - ? $parentData['path_identification_string'] . '/' . $pathString - : null; - - // Use content main location if already set, otherwise create location as main - if (isset($mainLocations[$child['contentobject_id']])) { - $createStruct->mainLocationId = $locationMap[$mainLocations[$child['contentobject_id']]]['id']; - } else { - $createStruct->mainLocationId = true; - $mainLocations[$child['contentobject_id']] = $child['node_id']; - - // If needed mark for update - if ( - isset($locations[$child['contentobject_id']][$child['main_node_id']]) && - count($locations[$child['contentobject_id']]) > 1 && - $child['node_id'] !== $child['main_node_id'] - ) { - $mainLocationsUpdate[$child['contentobject_id']] = $child['main_node_id']; - } - } - - $newLocation = $this->create($createStruct); - - $locationMap[$child['node_id']] = [ - 'id' => $newLocation->id, - 'hidden' => $newLocation->hidden, - 'invisible' => $newLocation->invisible, - 'path_identification_string' => $newLocation->pathIdentificationString, - ]; - if ($index === 0) { - $copiedSubtreeRootLocation = $newLocation; - } - } - - // Update main locations - foreach ($mainLocationsUpdate as $contentId => $mainLocationId) { - $this->changeMainLocation( - $contentMap[$contentId], - $locationMap[$mainLocationId]['id'] - ); - } - - $destinationParentSectionId = $this->getSectionId($destinationParentId); - - // potentially it may occur that the destination Location doesn't have a section (like in Location ID = 1), - // therefore assigning any or empty section will be invalid here - if ($destinationParentSectionId !== null) { - $this->updateSubtreeSectionIfNecessary($copiedSubtreeRootLocation, $destinationParentSectionId); - } - - return $copiedSubtreeRootLocation; - } - - public function getSubtreeSize(string $path): int - { - return $this->locationGateway->getSubtreeSize($path); - } - - /** - * Retrieves section ID of the location's content. - * - * @param int $locationId - * - * @return int|null - */ - private function getSectionId($locationId) - { - $location = $this->load($locationId); - - try { - $locationContentInfo = $this->contentHandler->loadContentInfo($location->contentId); - - return $locationContentInfo->sectionId; - } catch (NotFoundException $e) { - return null; - } - } - - /** - * If the location is the main location for its content, updates subtree section. - * - * @param \eZ\Publish\SPI\Persistence\Content\Location $location - * @param int $sectionId - */ - private function updateSubtreeSectionIfNecessary(Location $location, $sectionId) - { - if ($this->isMainLocation($location)) { - $this->setSectionForSubtree($location->id, $sectionId); - } - } - - /** - * Checks if the location is the main location for its content. - * - * @param \eZ\Publish\SPI\Persistence\Content\Location $location - * - * @return bool - */ - private function isMainLocation(Location $location) - { - $locationContentInfo = $this->contentHandler->loadContentInfo($location->contentId); - - return $locationContentInfo->mainLocationId === $location->id; - } - - /** - * Moves location identified by $sourceId into new parent identified by $destinationParentId. - * - * Performs a full move of the location identified by $sourceId to a new - * destination, identified by $destinationParentId. Relations do not need - * to be updated, since they refer to Content. URLs are not touched. - * - * @param mixed $sourceId - * @param mixed $destinationParentId - * - * @return bool - */ - public function move($sourceId, $destinationParentId) - { - $sourceNodeData = $this->locationGateway->getBasicNodeData($sourceId); - $destinationNodeData = $this->locationGateway->getBasicNodeData($destinationParentId); - - $this->locationGateway->moveSubtreeNodes( - $sourceNodeData, - $destinationNodeData - ); - - $this->locationGateway->updateNodeAssignment( - $sourceNodeData['contentobject_id'], - $sourceNodeData['parent_node_id'], - $destinationParentId, - Gateway::NODE_ASSIGNMENT_OP_CODE_MOVE - ); - - $sourceLocation = $this->load($sourceId); - $destinationParentSectionId = $this->getSectionId($destinationParentId); - - // potentially it may occur that the destination Location doesn't have a section (like in Location ID = 1), - // therefore assigning any or empty section will be invalid here - if ($destinationParentSectionId !== null) { - $this->updateSubtreeSectionIfNecessary($sourceLocation, $destinationParentSectionId); - } - } - - /** - * Marks the given nodes and all ancestors as modified. - * - * Optionally a time stamp with the modification date may be specified, - * otherwise the current time is used. - * - * @param int|string $locationId - * @param int $timestamp - */ - public function markSubtreeModified($locationId, $timestamp = null) - { - $nodeData = $this->locationGateway->getBasicNodeData($locationId); - $timestamp = $timestamp ?: time(); - $this->locationGateway->updateSubtreeModificationTime($nodeData['path_string'], $timestamp); - } - - /** - * Sets a location to be hidden, and it self + all children to invisible. - * - * @param mixed $id Location ID - */ - public function hide($id) - { - $sourceNodeData = $this->locationGateway->getBasicNodeData($id); - - $this->locationGateway->hideSubtree($sourceNodeData['path_string']); - } - - /** - * Sets a location to be unhidden, and self + children to visible unless a parent is hiding the tree. - * If not make sure only children down to first hidden node is marked visible. - * - * @param mixed $id - */ - public function unHide($id) - { - $sourceNodeData = $this->locationGateway->getBasicNodeData($id); - - $this->locationGateway->unhideSubtree($sourceNodeData['path_string']); - } - - /** - * Sets a location + all children to invisible. - * - * @param int $id Location ID - */ - public function setInvisible(int $id): void - { - $sourceNodeData = $this->locationGateway->getBasicNodeData($id); - - $this->locationGateway->setNodeWithChildrenInvisible($sourceNodeData['path_string']); - } - - /** - * Sets a location + all children to visible. - * - * @param int $id Location ID - */ - public function setVisible(int $id): void - { - $sourceNodeData = $this->locationGateway->getBasicNodeData($id); - - $this->locationGateway->setNodeWithChildrenVisible($sourceNodeData['path_string']); - } - - /** - * Swaps the content object being pointed to by a location object. - * - * Make the location identified by $locationId1 refer to the Content - * referred to by $locationId2 and vice versa. - * - * @param mixed $locationId1 - * @param mixed $locationId2 - * - * @return bool - */ - public function swap($locationId1, $locationId2) - { - $this->locationGateway->swap($locationId1, $locationId2); - } - - /** - * Updates an existing location. - * - * @param \eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct $location - * @param int $locationId - */ - public function update(UpdateStruct $location, $locationId) - { - $this->locationGateway->update($location, $locationId); - } - - public function create(CreateStruct $createStruct) - { - $parentNodeData = $this->locationGateway->getBasicNodeData($createStruct->parentId); - $spiLocation = $this->locationGateway->create($createStruct, $parentNodeData); - $this->locationGateway->createNodeAssignment( - $createStruct, - $parentNodeData['node_id'], - LocationGateway::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP - ); - - return $spiLocation; - } - - /** - * Removes all Locations under and including $locationId. - * - * Performs a recursive delete on the location identified by $locationId, - * including all of its child locations. Content which is not referred to - * by any other location is automatically removed. Content which looses its - * main Location will get the first of its other Locations assigned as the - * new main Location. - * - * @param mixed $locationId - * - * @return bool - */ - public function removeSubtree($locationId) - { - $this->treeHandler->removeSubtree($locationId); - } - - /** - * Set section on all content objects in the subtree. - * - * @param mixed $locationId - * @param mixed $sectionId - */ - public function setSectionForSubtree($locationId, $sectionId) - { - $this->treeHandler->setSectionForSubtree($locationId, $sectionId); - } - - /** - * Changes main location of content identified by given $contentId to location identified by given $locationId. - * - * Updates ezcontentobject_tree and eznode_assignment tables (eznode_assignment for content current version number). - * - * @param mixed $contentId - * @param mixed $locationId - */ - public function changeMainLocation($contentId, $locationId) - { - $this->treeHandler->changeMainLocation($contentId, $locationId); - } - - /** - * Get the total number of all existing Locations. Can be combined with loadAllLocations. - * - * @return int - */ - public function countAllLocations() - { - return $this->locationGateway->countAllLocations(); - } - - /** - * Bulk-load all existing Locations, constrained by $limit and $offset to paginate results. - * - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\SPI\Persistence\Content\Location[] - */ - public function loadAllLocations($offset, $limit) - { - $rows = $this->locationGateway->loadAllLocationsData($offset, $limit); - - return $this->locationMapper->createLocationsFromRows($rows); - } - - /** - * {@inheritdoc} - */ - public function countLocationsByContent(int $contentId): int - { - return $this->locationGateway->countLocationsByContentId($contentId); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Location/Mapper.php b/eZ/Publish/Core/Persistence/Legacy/Content/Location/Mapper.php deleted file mode 100644 index b9d213c4a4..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Location/Mapper.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Location; - -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct; - -/** - * Mapper for Location objects. - */ -class Mapper -{ - /** - * Creates a Location from a $data row. - * - * $prefix can be used to define a table prefix for the location table. - * - * Optionally pass a Location object, which will be filled with the values. - * - * @param array $data - * @param string $prefix - * @param \eZ\Publish\SPI\Persistence\Content\Location|null $location - * - * @return \eZ\Publish\SPI\Persistence\Content\Location - */ - public function createLocationFromRow(array $data, $prefix = '', ?Location $location = null) - { - $location = $location ?: new Location(); - - $location->id = (int)$data[$prefix . 'node_id']; - $location->priority = (int)$data[$prefix . 'priority']; - $location->hidden = (bool)$data[$prefix . 'is_hidden']; - $location->invisible = (bool)$data[$prefix . 'is_invisible']; - $location->remoteId = $data[$prefix . 'remote_id']; - $location->contentId = (int)$data[$prefix . 'contentobject_id']; - $location->parentId = (int)$data[$prefix . 'parent_node_id']; - $location->pathIdentificationString = $data[$prefix . 'path_identification_string']; - $location->pathString = $data[$prefix . 'path_string']; - $location->depth = (int)$data[$prefix . 'depth']; - $location->sortField = (int)$data[$prefix . 'sort_field']; - $location->sortOrder = (int)$data[$prefix . 'sort_order']; - if (isset($data[$prefix . 'trashed'])) { - $location->trashed = (int)$data[$prefix . 'trashed']; - } - - return $location; - } - - /** - * Creates Location objects from the given $rows, optionally with key - * $prefix. - * - * @param array $rows - * @param string $prefix - * @param \eZ\Publish\SPI\Persistence\Content\Location|null $location - * - * @return \eZ\Publish\SPI\Persistence\Content\Location[] - */ - public function createLocationsFromRows(array $rows, $prefix = '', ?Location $location = null) - { - $locations = []; - - foreach ($rows as $row) { - $id = $row[$prefix . 'node_id']; - if (!isset($locations[$id])) { - $locations[$id] = $this->createLocationFromRow($row, $prefix, $location); - } - } - - return array_values($locations); - } - - /** - * Creates a Location CreateStruct from a $data row. - * - * @param array $data - * - * @return \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct - */ - public function getLocationCreateStruct(array $data) - { - $struct = new CreateStruct(); - - $struct->contentId = $data['contentobject_id']; - $struct->contentVersion = $data['contentobject_version']; - $struct->hidden = $data['is_hidden']; - $struct->invisible = $data['is_invisible']; - $struct->mainLocationId = $data['main_node_id']; - $struct->parentId = $data['parent_node_id']; - $struct->pathIdentificationString = $data['path_identification_string']; - $struct->priority = $data['priority']; - $struct->remoteId = md5(uniqid(static::class, true)); - $struct->sortField = $data['sort_field']; - $struct->sortOrder = $data['sort_order']; - - return $struct; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Location/Trash/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Content/Location/Trash/Handler.php deleted file mode 100644 index 0a347b6b3a..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Location/Trash/Handler.php +++ /dev/null @@ -1,261 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResultList; -use eZ\Publish\Core\Persistence\Legacy\Content\Handler as ContentHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler as LocationHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; -use eZ\Publish\SPI\Persistence\Content\Location\Trash\Handler as BaseTrashHandler; -use eZ\Publish\SPI\Persistence\Content\Location\Trash\TrashResult; -use eZ\Publish\SPI\Persistence\Content\Location\Trashed; - -/** - * The Location Handler interface defines operations on Location elements in the storage engine. - */ -class Handler implements BaseTrashHandler -{ - private const EMPTY_TRASH_BULK_SIZE = 100; - - /** - * Location handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler - */ - protected $locationHandler; - - /** - * Gateway for handling location data. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway - */ - protected $locationGateway; - - /** - * Mapper for handling location data. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper - */ - protected $locationMapper; - - /** - * Content handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Handler - */ - protected $contentHandler; - - /** - * Construct from userGateway. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler $locationHandler - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway $locationGateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper $locationMapper - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Handler $contentHandler - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler - */ - public function __construct( - LocationHandler $locationHandler, - LocationGateway $locationGateway, - LocationMapper $locationMapper, - ContentHandler $contentHandler - ) { - $this->locationHandler = $locationHandler; - $this->locationGateway = $locationGateway; - $this->locationMapper = $locationMapper; - $this->contentHandler = $contentHandler; - } - - /** - * Loads the data for the trashed location identified by $id. - * $id is the same as original location (which has been previously trashed). - * - * @param int $id - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * - * @return \eZ\Publish\SPI\Persistence\Content\Location\Trashed - */ - public function loadTrashItem($id) - { - $data = $this->locationGateway->loadTrashByLocation($id); - - return $this->locationMapper->createLocationFromRow($data, null, new Trashed()); - } - - /** - * Sends a subtree starting to $locationId to the trash - * and returns a Trashed object corresponding to $locationId. - * - * Moves all locations in the subtree to the Trash. The associated content - * objects are left untouched. - * - * @param mixed $locationId - * - * @todo Handle field types actions - * - * @return \eZ\Publish\SPI\Persistence\Content\Location\Trashed|null null if location was deleted, otherwise Trashed object - */ - public function trashSubtree($locationId) - { - $locationRows = $this->locationGateway->getSubtreeContent($locationId); - $isLocationRemoved = false; - $parentLocationId = null; - $removedLocationsContentMap = []; - - foreach ($locationRows as $locationRow) { - if ($locationRow['node_id'] == $locationId) { - $parentLocationId = $locationRow['parent_node_id']; - } - - if ($this->locationGateway->countLocationsByContentId($locationRow['contentobject_id']) == 1) { - $this->locationGateway->trashLocation($locationRow['node_id']); - $removedLocationsContentMap[(int)$locationRow['node_id']] = (int)$locationRow['contentobject_id']; - } else { - if ($locationRow['node_id'] == $locationId) { - $isLocationRemoved = true; - } - $this->locationGateway->removeLocation($locationRow['node_id']); - - if ($locationRow['node_id'] == $locationRow['main_node_id']) { - $newMainLocationRow = $this->locationGateway->getFallbackMainNodeData( - $locationRow['contentobject_id'], - $locationRow['node_id'] - ); - - $this->locationHandler->changeMainLocation( - $locationRow['contentobject_id'], - $newMainLocationRow['node_id'], - $newMainLocationRow['contentobject_version'], - $newMainLocationRow['parent_node_id'] - ); - } - } - } - - if (isset($parentLocationId)) { - $this->locationHandler->markSubtreeModified($parentLocationId, time()); - } - - if ($isLocationRemoved === true) { - return null; - } - - $trashItem = $this->loadTrashItem($locationId); - $trashItem->removedLocationContentIdMap = $removedLocationsContentMap; - - return $trashItem; - } - - /** - * Returns a trashed location to normal state. - * - * Recreates the originally trashed location in the new position. - * If this is not possible (because the old location does not exist any more), - * a ParentNotFound exception is thrown. - * - * Returns newly restored location Id. - * - * @param mixed $trashedId - * @param mixed $newParentId - * - * @return int Newly restored location id - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException If $newParentId is invalid - * - * @todo Handle field types actions - */ - public function recover($trashedId, $newParentId) - { - return $this->locationGateway->untrashLocation($trashedId, $newParentId)->id; - } - - /** - * {@inheritdoc}. - */ - public function findTrashItems(Criterion $criterion = null, $offset = 0, $limit = null, array $sort = null) - { - $totalCount = $this->locationGateway->countTrashed($criterion); - if ($totalCount === 0) { - return new TrashResult(); - } - - $rows = $this->locationGateway->listTrashed($offset, $limit, $sort, $criterion); - $items = []; - - foreach ($rows as $row) { - $items[] = $this->locationMapper->createLocationFromRow($row, null, new Trashed()); - } - - return new TrashResult([ - 'items' => $items, - 'totalCount' => $totalCount, - ]); - } - - /** - * {@inheritdoc} - */ - public function emptyTrash() - { - $resultList = new TrashItemDeleteResultList(); - do { - $trashedItems = $this->findTrashItems(null, 0, self::EMPTY_TRASH_BULK_SIZE); - foreach ($trashedItems as $item) { - $resultList->items[] = $this->delete($item); - } - } while ($trashedItems->totalCount > self::EMPTY_TRASH_BULK_SIZE); - - $this->locationGateway->cleanupTrash(); - - return $resultList; - } - - /** - * Removes a trashed location identified by $trashedLocationId from trash - * Associated content has to be deleted. - * - * @param int $trashedId - * - * @return \eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult - */ - public function deleteTrashItem($trashedId) - { - return $this->delete($this->loadTrashItem($trashedId)); - } - - /** - * Triggers delete operations for $trashItem. - * If there is no more locations for corresponding content, then it will be deleted as well. - * - * @param \eZ\Publish\SPI\Persistence\Content\Location\Trashed $trashItem - * - * @return \eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult - */ - protected function delete(Trashed $trashItem) - { - $result = new TrashItemDeleteResult(); - $result->trashItemId = $trashItem->id; - $result->contentId = $trashItem->contentId; - - $reverseRelations = $this->contentHandler->loadReverseRelations($trashItem->contentId); - - $this->locationGateway->removeElementFromTrash($trashItem->id); - - if ($this->locationGateway->countLocationsByContentId($trashItem->contentId) < 1) { - $this->contentHandler->deleteContent($trashItem->contentId); - $result->contentRemoved = true; - $result->reverseRelationContentIds = array_column($reverseRelations, 'sourceContentId'); - } - - return $result; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Mapper.php b/eZ/Publish/Core/Persistence/Legacy/Content/Mapper.php deleted file mode 100644 index ed9e8a8dbc..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Mapper.php +++ /dev/null @@ -1,732 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content; - -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry as Registry; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use eZ\Publish\SPI\Persistence\Content\Relation; -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandler; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use Ibexa\Contracts\Core\Event\Mapper\ResolveMissingFieldEvent; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -/** - * Mapper for Content Handler. - * - * Performs mapping of Content objects. - * - * @phpstan-type TVersionedLanguageFieldDefinitionsMap array< - * int, array< - * int, array< - * string, array< - * int, \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition, - * > - * > - * > - * > - * @phpstan-type TVersionedFieldMap array< - * int, array< - * int, array< - * int, \eZ\Publish\SPI\Persistence\Content\Field, - * > - * > - * > - * @phpstan-type TVersionedNameMap array< - * int, array< - * int, array< - * string, array<int, string> - * > - * > - * > - * @phpstan-type TContentInfoMap array<int, \eZ\Publish\SPI\Persistence\Content\ContentInfo> - * @phpstan-type TVersionInfoMap array< - * int, array< - * int, \eZ\Publish\SPI\Persistence\Content\VersionInfo, - * > - * > - */ -class Mapper -{ - /** - * FieldValue converter registry. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry - */ - protected $converterRegistry; - - /** - * Caching language handler. - * - * @var \eZ\Publish\SPI\Persistence\Content\Language\Handler - */ - protected $languageHandler; - - /** - * @var \eZ\Publish\SPI\Persistence\Content\Type\Handler - */ - private $contentTypeHandler; - - /** - * @var \Symfony\Contracts\EventDispatcher\EventDispatcherInterface - */ - private $eventDispatcher; - - public function __construct( - Registry $converterRegistry, - LanguageHandler $languageHandler, - ContentTypeHandler $contentTypeHandler, - EventDispatcherInterface $eventDispatcher - ) { - $this->converterRegistry = $converterRegistry; - $this->languageHandler = $languageHandler; - $this->contentTypeHandler = $contentTypeHandler; - $this->eventDispatcher = $eventDispatcher; - } - - /** - * Creates a Content from the given $struct and $currentVersionNo. - * - * @param \eZ\Publish\SPI\Persistence\Content\CreateStruct $struct - * @param mixed $currentVersionNo - * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo - */ - private function createContentInfoFromCreateStruct(CreateStruct $struct, $currentVersionNo = 1) - { - $contentInfo = new ContentInfo(); - - $contentInfo->id = null; - $contentInfo->contentTypeId = $struct->typeId; - $contentInfo->sectionId = $struct->sectionId; - $contentInfo->ownerId = $struct->ownerId; - $contentInfo->alwaysAvailable = $struct->alwaysAvailable; - $contentInfo->remoteId = $struct->remoteId; - $contentInfo->mainLanguageCode = $this->languageHandler - ->load(isset($struct->mainLanguageId) ? $struct->mainLanguageId : $struct->initialLanguageId) - ->languageCode; - $contentInfo->name = isset($struct->name[$contentInfo->mainLanguageCode]) - ? $struct->name[$contentInfo->mainLanguageCode] - : ''; - // For drafts published and modified timestamps should be 0 - $contentInfo->publicationDate = 0; - $contentInfo->modificationDate = 0; - $contentInfo->currentVersionNo = $currentVersionNo; - $contentInfo->status = ContentInfo::STATUS_DRAFT; - $contentInfo->isPublished = false; - $contentInfo->isHidden = $struct->isHidden; - - return $contentInfo; - } - - /** - * Creates a new version for the given $struct and $versionNo. - * - * @param \eZ\Publish\SPI\Persistence\Content\CreateStruct $struct - * @param mixed $versionNo - * - * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo - */ - public function createVersionInfoFromCreateStruct(CreateStruct $struct, $versionNo) - { - $versionInfo = new VersionInfo(); - - $versionInfo->id = null; - $versionInfo->contentInfo = $this->createContentInfoFromCreateStruct($struct, $versionNo); - $versionInfo->versionNo = $versionNo; - $versionInfo->creatorId = $struct->ownerId; - $versionInfo->status = VersionInfo::STATUS_DRAFT; - $versionInfo->initialLanguageCode = $this->languageHandler->load($struct->initialLanguageId)->languageCode; - $versionInfo->creationDate = $struct->modified; - $versionInfo->modificationDate = $struct->modified; - $versionInfo->names = $struct->name; - - $languages = []; - foreach ($struct->fields as $field) { - if (!isset($languages[$field->languageCode])) { - $languages[$field->languageCode] = true; - } - } - $versionInfo->languageCodes = array_keys($languages); - - return $versionInfo; - } - - /** - * Creates a new version for the given $content. - * - * @param \eZ\Publish\SPI\Persistence\Content $content - * @param mixed $versionNo - * @param mixed $userId - * @param string|null $languageCode - * - * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo - */ - public function createVersionInfoForContent(Content $content, $versionNo, $userId, ?string $languageCode = null) - { - $versionInfo = new VersionInfo(); - - $versionInfo->contentInfo = $content->versionInfo->contentInfo; - $versionInfo->versionNo = $versionNo; - $versionInfo->creatorId = $userId; - $versionInfo->status = VersionInfo::STATUS_DRAFT; - $versionInfo->initialLanguageCode = $languageCode ?? $content->versionInfo->initialLanguageCode; - $versionInfo->creationDate = time(); - $versionInfo->modificationDate = $versionInfo->creationDate; - $versionInfo->names = is_object($content->versionInfo) ? $content->versionInfo->names : []; - $versionInfo->languageCodes = $content->versionInfo->languageCodes; - - return $versionInfo; - } - - /** - * Converts value of $field to storage value. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue - */ - public function convertToStorageValue(Field $field) - { - $converter = $this->converterRegistry->getConverter( - $field->type - ); - $storageValue = new StorageFieldValue(); - $converter->toStorageValue( - $field->value, - $storageValue - ); - - return $storageValue; - } - - /** - * Extracts Content objects (and nested) from database result $rows. - * - * Expects database rows to be indexed by keys of the format - * - * "$tableName_$columnName" - * - * @param array<array<string, scalar>> $rows - * @param array<array<string, scalar>> $nameRows - * @param string $prefix - * @param array<string>|null $translations - * - * @return \eZ\Publish\SPI\Persistence\Content[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function extractContentFromRows( - array $rows, - array $nameRows, - string $prefix = 'ezcontentobject_', - ?array $translations = null - ): array { - $versionedNameData = []; - - foreach ($nameRows as $row) { - $contentId = (int)$row["{$prefix}name_contentobject_id"]; - $versionNo = (int)$row["{$prefix}name_content_version"]; - $languageCode = $row["{$prefix}name_content_translation"]; - $versionedNameData[$contentId][$versionNo][$languageCode] = $row["{$prefix}name_name"]; - } - - $contentInfos = []; - $versionInfos = []; - $fields = []; - - $fieldDefinitions = $this->loadCachedVersionFieldDefinitionsPerLanguage( - $rows, - $prefix, - $translations - ); - - foreach ($rows as $row) { - $contentId = (int)$row["{$prefix}id"]; - $versionId = (int)$row["{$prefix}version_id"]; - - if (!isset($contentInfos[$contentId])) { - $contentInfos[$contentId] = $this->extractContentInfoFromRow($row, $prefix); - } - - if (!isset($versionInfos[$contentId])) { - $versionInfos[$contentId] = []; - } - - if (!isset($versionInfos[$contentId][$versionId])) { - $versionInfos[$contentId][$versionId] = $this->extractVersionInfoFromRow($row); - } - - $fieldId = (int)$row["{$prefix}attribute_id"]; - $fieldDefinitionId = (int)$row["{$prefix}attribute_contentclassattribute_id"]; - $languageCode = $row["{$prefix}attribute_language_code"]; - - if (!isset($fields[$contentId][$versionId][$fieldId]) - && isset($fieldDefinitions[$contentId][$versionId][$languageCode][$fieldDefinitionId]) - ) { - $fields[$contentId][$versionId][$fieldId] = $this->extractFieldFromRow($row); - unset($fieldDefinitions[$contentId][$versionId][$languageCode][$fieldDefinitionId]); - } - } - - return $this->buildContentObjects( - $contentInfos, - $versionInfos, - $fields, - $fieldDefinitions, - $versionedNameData - ); - } - - /** - * @phpstan-param TContentInfoMap $contentInfos - * @phpstan-param TVersionInfoMap $versionInfos - * @phpstan-param TVersionedFieldMap $fields - * @phpstan-param TVersionedLanguageFieldDefinitionsMap $missingFieldDefinitions - * @phpstan-param TVersionedNameMap $versionedNames - * - * @return \eZ\Publish\SPI\Persistence\Content[] - */ - private function buildContentObjects( - array $contentInfos, - array $versionInfos, - array $fields, - array $missingFieldDefinitions, - array $versionedNames - ): array { - $results = []; - - foreach ($contentInfos as $contentId => $contentInfo) { - foreach ($versionInfos[$contentId] as $versionId => $versionInfo) { - // Fallback to just main language name if versioned name data is missing - $names = $versionedNames[$contentId][$versionInfo->versionNo] - ?? [$contentInfo->mainLanguageCode => $contentInfo->name]; - - $content = new Content(); - $content->versionInfo = $versionInfo; - $content->versionInfo->names = $names; - $content->versionInfo->contentInfo = $contentInfo; - $content->fields = array_values($fields[$contentId][$versionId] ?? []); - - $missingVersionFieldDefinitions = $missingFieldDefinitions[$contentId][$versionId] ?? []; - - foreach ($missingVersionFieldDefinitions as $languageCode => $versionFieldDefinitions) { - foreach ($versionFieldDefinitions as $fieldDefinition) { - $event = $this->eventDispatcher->dispatch( - new ResolveMissingFieldEvent( - $content, - $fieldDefinition, - $languageCode - ) - ); - - $field = $event->getField(); - if ($field !== null) { - $content->fields[] = $field; - } - } - } - - $results[] = $content; - } - } - - return $results; - } - - /** - * @param string[]|null $translations - * - * @phpstan-return TVersionedLanguageFieldDefinitionsMap - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - private function loadCachedVersionFieldDefinitionsPerLanguage( - array $rows, - string $prefix, - ?array $translations = null - ): array { - $fieldDefinitions = []; - $contentTypes = []; - $allLanguages = $this->loadAllLanguagesWithIdKey(); - - foreach ($rows as $row) { - $contentId = (int)$row["{$prefix}id"]; - $versionId = (int)$row["{$prefix}version_id"]; - $contentTypeId = (int)$row["{$prefix}contentclass_id"]; - $languageMask = (int)$row["{$prefix}version_language_mask"]; - - if (isset($fieldDefinitions[$contentId][$versionId])) { - continue; - } - - $allLanguagesCodes = $this->extractLanguageCodesFromMask($languageMask, $allLanguages); - $languageCodes = empty($translations) ? $allLanguagesCodes : array_intersect($translations, $allLanguagesCodes); - $contentTypes[$contentTypeId] = $contentTypes[$contentTypeId] ?? $this->contentTypeHandler->load($contentTypeId); - $contentType = $contentTypes[$contentTypeId]; - foreach ($contentType->fieldDefinitions as $fieldDefinition) { - foreach ($languageCodes as $languageCode) { - $id = (int)$fieldDefinition->id; - $languageCode = (string)$languageCode; - $fieldDefinitions[$contentId][$versionId][$languageCode][$id] = $fieldDefinition; - } - } - } - - return $fieldDefinitions; - } - - /** - * Extracts a ContentInfo object from $row. - * - * @param array $row - * @param string $prefix Prefix for row keys, which are initially mapped by ezcontentobject fields - * @param string $treePrefix Prefix for tree row key, which are initially mapped by ezcontentobject_tree_ fields - * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo - */ - public function extractContentInfoFromRow(array $row, $prefix = '', $treePrefix = 'ezcontentobject_tree_') - { - $contentInfo = new ContentInfo(); - $contentInfo->id = (int)$row["{$prefix}id"]; - $contentInfo->name = $row["{$prefix}name"]; - $contentInfo->contentTypeId = (int)$row["{$prefix}contentclass_id"]; - $contentInfo->sectionId = (int)$row["{$prefix}section_id"]; - $contentInfo->currentVersionNo = (int)$row["{$prefix}current_version"]; - $contentInfo->ownerId = (int)$row["{$prefix}owner_id"]; - $contentInfo->publicationDate = (int)$row["{$prefix}published"]; - $contentInfo->modificationDate = (int)$row["{$prefix}modified"]; - $contentInfo->alwaysAvailable = 1 === ($row["{$prefix}language_mask"] & 1); - $contentInfo->mainLanguageCode = $this->languageHandler->load($row["{$prefix}initial_language_id"])->languageCode; - $contentInfo->remoteId = $row["{$prefix}remote_id"]; - $contentInfo->mainLocationId = ($row["{$treePrefix}main_node_id"] !== null ? (int)$row["{$treePrefix}main_node_id"] : null); - $contentInfo->status = (int)$row["{$prefix}status"]; - $contentInfo->isPublished = ($contentInfo->status == ContentInfo::STATUS_PUBLISHED); - $contentInfo->isHidden = (bool)$row["{$prefix}is_hidden"]; - - return $contentInfo; - } - - /** - * Extracts ContentInfo objects from $rows. - * - * @param array $rows - * @param string $prefix Prefix for row keys, which are initially mapped by ezcontentobject fields - * @param string $treePrefix Prefix for tree row key, which are initially mapped by ezcontentobject_tree_ fields - * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo[] - */ - public function extractContentInfoFromRows(array $rows, $prefix = '', $treePrefix = 'ezcontentobject_tree_') - { - $contentInfoObjects = []; - foreach ($rows as $row) { - $contentInfoObjects[] = $this->extractContentInfoFromRow($row, $prefix, $treePrefix); - } - - return $contentInfoObjects; - } - - /** - * Extracts a VersionInfo object from $row. - * - * This method will return VersionInfo with incomplete data. It is intended to be used only by - * {@link self::extractContentFromRows} where missing data will be filled in. - * - * @param array $row - * @param array $names - * - * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo - */ - private function extractVersionInfoFromRow(array $row, array $names = []) - { - $versionInfo = new VersionInfo(); - $versionInfo->id = (int)$row['ezcontentobject_version_id']; - $versionInfo->contentInfo = null; - $versionInfo->versionNo = (int)$row['ezcontentobject_version_version']; - $versionInfo->creatorId = (int)$row['ezcontentobject_version_creator_id']; - $versionInfo->creationDate = (int)$row['ezcontentobject_version_created']; - $versionInfo->modificationDate = (int)$row['ezcontentobject_version_modified']; - $versionInfo->status = (int)$row['ezcontentobject_version_status']; - $versionInfo->names = $names; - - // Map language codes - $allLanguages = $this->loadAllLanguagesWithIdKey(); - $versionInfo->languageCodes = $this->extractLanguageCodesFromMask( - (int)$row['ezcontentobject_version_language_mask'], - $allLanguages, - $missing - ); - $initialLanguageId = (int)$row['ezcontentobject_version_initial_language_id']; - if (isset($allLanguages[$initialLanguageId])) { - $versionInfo->initialLanguageCode = $allLanguages[$initialLanguageId]->languageCode; - } else { - $missing[] = $initialLanguageId; - } - - if (!empty($missing)) { - throw new NotFoundException( - 'Language', - implode(', ', $missing) . "' when building content '" . $row['ezcontentobject_id'] - ); - } - - return $versionInfo; - } - - /** - * Extracts a VersionInfo object from $row. - * - * @param array $rows - * @param array $nameRows - * - * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo[] - */ - public function extractVersionInfoListFromRows(array $rows, array $nameRows) - { - $nameData = []; - foreach ($nameRows as $row) { - $versionId = $row['ezcontentobject_name_contentobject_id'] . '_' . $row['ezcontentobject_name_content_version']; - $nameData[$versionId][$row['ezcontentobject_name_content_translation']] = $row['ezcontentobject_name_name']; - } - - $allLanguages = $this->loadAllLanguagesWithIdKey(); - $versionInfoList = []; - foreach ($rows as $row) { - $versionId = $row['ezcontentobject_id'] . '_' . $row['ezcontentobject_version_version']; - if (!isset($versionInfoList[$versionId])) { - $versionInfo = new VersionInfo(); - $versionInfo->id = (int)$row['ezcontentobject_version_id']; - $versionInfo->contentInfo = $this->extractContentInfoFromRow($row, 'ezcontentobject_'); - $versionInfo->versionNo = (int)$row['ezcontentobject_version_version']; - $versionInfo->creatorId = (int)$row['ezcontentobject_version_creator_id']; - $versionInfo->creationDate = (int)$row['ezcontentobject_version_created']; - $versionInfo->modificationDate = (int)$row['ezcontentobject_version_modified']; - $versionInfo->status = (int)$row['ezcontentobject_version_status']; - $versionInfo->names = $nameData[$versionId]; - $versionInfoList[$versionId] = $versionInfo; - $versionInfo->languageCodes = $this->extractLanguageCodesFromMask( - (int)$row['ezcontentobject_version_language_mask'], - $allLanguages, - $missing - ); - $initialLanguageId = (int)$row['ezcontentobject_version_initial_language_id']; - if (isset($allLanguages[$initialLanguageId])) { - $versionInfo->initialLanguageCode = $allLanguages[$initialLanguageId]->languageCode; - } else { - $missing[] = $initialLanguageId; - } - - if (!empty($missing)) { - throw new NotFoundException( - 'Language', - implode(', ', $missing) . "' when building content '" . $row['ezcontentobject_id'] - ); - } - } - } - - return array_values($versionInfoList); - } - - /** - * @param int $languageMask - * @param \eZ\Publish\SPI\Persistence\Content\Language[] $allLanguages - * @param int[] &$missing - * - * @return string[] - */ - private function extractLanguageCodesFromMask(int $languageMask, array $allLanguages, &$missing = []) - { - $exp = 2; - $result = []; - - // Decomposition of $languageMask into its binary components to extract language codes - while ($exp <= $languageMask) { - if ($languageMask & $exp) { - if (isset($allLanguages[$exp])) { - $result[] = $allLanguages[$exp]->languageCode; - } else { - $missing[] = $exp; - } - } - - $exp *= 2; - } - - return $result; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Language[] - */ - private function loadAllLanguagesWithIdKey() - { - $languagesById = []; - foreach ($this->languageHandler->loadAll() as $language) { - $languagesById[$language->id] = $language; - } - - return $languagesById; - } - - /** - * Extracts a Field from $row. - * - * @param array $row - * - * @return \eZ\Publish\SPI\Persistence\Content\Field - */ - protected function extractFieldFromRow(array $row) - { - $field = new Field(); - - $field->id = (int)$row['ezcontentobject_attribute_id']; - $field->fieldDefinitionId = (int)$row['ezcontentobject_attribute_contentclassattribute_id']; - $field->type = $row['ezcontentobject_attribute_data_type_string']; - $field->value = $this->extractFieldValueFromRow($row, $field->type); - $field->languageCode = $row['ezcontentobject_attribute_language_code']; - $field->versionNo = isset($row['ezcontentobject_version_version']) ? - (int)$row['ezcontentobject_version_version'] : - (int)$row['ezcontentobject_attribute_version']; - - return $field; - } - - /** - * Extracts a FieldValue of $type from $row. - * - * @param array $row - * @param string $type - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - * - * @throws \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound - * if the necessary converter for $type could not be found. - */ - protected function extractFieldValueFromRow(array $row, $type) - { - $storageValue = new StorageFieldValue(); - - // Nullable field - $storageValue->dataFloat = isset($row['ezcontentobject_attribute_data_float']) - ? (float)$row['ezcontentobject_attribute_data_float'] - : null; - // Nullable field - $storageValue->dataInt = isset($row['ezcontentobject_attribute_data_int']) - ? (int)$row['ezcontentobject_attribute_data_int'] - : null; - $storageValue->dataText = $row['ezcontentobject_attribute_data_text']; - // Not nullable field - $storageValue->sortKeyInt = (int)$row['ezcontentobject_attribute_sort_key_int']; - $storageValue->sortKeyString = $row['ezcontentobject_attribute_sort_key_string']; - - $fieldValue = new FieldValue(); - - $converter = $this->converterRegistry->getConverter($type); - $converter->toFieldValue($storageValue, $fieldValue); - - return $fieldValue; - } - - /** - * Creates CreateStruct from $content. - * - * @param \eZ\Publish\SPI\Persistence\Content $content - * - * @return \eZ\Publish\SPI\Persistence\Content\CreateStruct - */ - public function createCreateStructFromContent(Content $content) - { - $struct = new CreateStruct(); - $struct->name = $content->versionInfo->names; - $struct->typeId = $content->versionInfo->contentInfo->contentTypeId; - $struct->sectionId = $content->versionInfo->contentInfo->sectionId; - $struct->ownerId = $content->versionInfo->contentInfo->ownerId; - $struct->locations = []; - $struct->alwaysAvailable = $content->versionInfo->contentInfo->alwaysAvailable; - $struct->remoteId = md5(uniqid(static::class, true)); - $struct->initialLanguageId = $this->languageHandler->loadByLanguageCode($content->versionInfo->initialLanguageCode)->id; - $struct->mainLanguageId = $this->languageHandler->loadByLanguageCode($content->versionInfo->contentInfo->mainLanguageCode)->id; - $struct->modified = time(); - $struct->isHidden = $content->versionInfo->contentInfo->isHidden; - - foreach ($content->fields as $field) { - $newField = clone $field; - $newField->id = null; - $struct->fields[] = $newField; - } - - return $struct; - } - - /** - * Extracts relation objects from $rows. - */ - public function extractRelationsFromRows(array $rows) - { - $relations = []; - - foreach ($rows as $row) { - $id = (int)$row['ezcontentobject_link_id']; - if (!isset($relations[$id])) { - $relations[$id] = $this->extractRelationFromRow($row); - } - } - - return $relations; - } - - /** - * Extracts a Relation object from a $row. - * - * @param array $row Associative array representing a relation - * - * @return \eZ\Publish\SPI\Persistence\Content\Relation - */ - protected function extractRelationFromRow(array $row) - { - $relation = new Relation(); - $relation->id = (int)$row['ezcontentobject_link_id']; - $relation->sourceContentId = (int)$row['ezcontentobject_link_from_contentobject_id']; - $relation->sourceContentVersionNo = (int)$row['ezcontentobject_link_from_contentobject_version']; - $relation->destinationContentId = (int)$row['ezcontentobject_link_to_contentobject_id']; - $relation->type = (int)$row['ezcontentobject_link_relation_type']; - - $contentClassAttributeId = (int)$row['ezcontentobject_link_contentclassattribute_id']; - if ($contentClassAttributeId > 0) { - $relation->sourceFieldDefinitionId = $contentClassAttributeId; - } - - return $relation; - } - - /** - * Creates a Content from the given $struct. - * - * @param \eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct $struct - * - * @return \eZ\Publish\SPI\Persistence\Content\Relation - */ - public function createRelationFromCreateStruct(RelationCreateStruct $struct) - { - $relation = new Relation(); - - $relation->destinationContentId = $struct->destinationContentId; - $relation->sourceContentId = $struct->sourceContentId; - $relation->sourceContentVersionNo = $struct->sourceContentVersionNo; - $relation->sourceFieldDefinitionId = $struct->sourceFieldDefinitionId; - $relation->type = $struct->type; - - return $relation; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/MultilingualStorageFieldDefinition.php b/eZ/Publish/Core/Persistence/Legacy/Content/MultilingualStorageFieldDefinition.php deleted file mode 100644 index 54b094735b..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/MultilingualStorageFieldDefinition.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class MultilingualStorageFieldDefinition extends ValueObject -{ - /** @var string */ - public $name; - - /** @var string */ - public $description; - - /** @var string */ - public $dataText; - - /** @var string */ - public $dataJson; - - /** @var int */ - public $languageId; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Gateway.php deleted file mode 100644 index 5a6e0a3a3d..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Gateway.php +++ /dev/null @@ -1,133 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\ObjectState; - -use eZ\Publish\SPI\Persistence\Content\ObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group; - -/** - * Base class for Object State gateways. - * - * @internal For internal use by Persistence Handlers. - */ -abstract class Gateway -{ - public const OBJECT_STATE_TABLE = 'ezcobj_state'; - public const OBJECT_STATE_LANGUAGE_TABLE = 'ezcobj_state_language'; - public const OBJECT_STATE_GROUP_TABLE = 'ezcobj_state_group'; - public const OBJECT_STATE_GROUP_LANGUAGE_TABLE = 'ezcobj_state_group_language'; - public const OBJECT_STATE_LINK_TABLE = 'ezcobj_state_link'; - - public const OBJECT_STATE_TABLE_SEQ = 'ezcobj_state_id_seq'; - public const OBJECT_STATE_GROUP_TABLE_SEQ = 'ezcobj_state_group_id_seq'; - - /** - * Load data for an object state. - */ - abstract public function loadObjectStateData(int $stateId): array; - - /** - * Load data for an object state by identifier. - */ - abstract public function loadObjectStateDataByIdentifier( - string $identifier, - int $groupId - ): array; - - /** - * Load data for all object states belonging to group with $groupId ID. - */ - abstract public function loadObjectStateListData(int $groupId): array; - - /** - * Load data for an object state group. - */ - abstract public function loadObjectStateGroupData(int $groupId): array; - - /** - * Load data for an object state group by identifier. - */ - abstract public function loadObjectStateGroupDataByIdentifier(string $identifier): array; - - /** - * Load data for all object state groups, filtered by $offset and $limit. - */ - abstract public function loadObjectStateGroupListData(int $offset, int $limit): array; - - /** - * Insert a new object state into database. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function insertObjectState(ObjectState $objectState, int $groupId): void; - - /** - * Update the stored object state with provided data. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function updateObjectState(ObjectState $objectState): void; - - /** - * Delete object state identified by $stateId. - */ - abstract public function deleteObjectState(int $stateId): void; - - /** - * Update object state links from $oldStateId to $newStateId. - */ - abstract public function updateObjectStateLinks(int $oldStateId, int $newStateId): void; - - /** - * Delete object state links identified by $stateId. - */ - abstract public function deleteObjectStateLinks(int $stateId): void; - - /** - * Insert a new object state group into database. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if Object State Group language does not exist - */ - abstract public function insertObjectStateGroup(Group $objectStateGroup): void; - - /** - * Update the stored object state group with provided data. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - abstract public function updateObjectStateGroup(Group $objectStateGroup): void; - - /** - * Delete the object state group identified by $groupId. - */ - abstract public function deleteObjectStateGroup(int $groupId): void; - - /** - * Set the object state $stateId to content with $contentId ID. - */ - abstract public function setContentState(int $contentId, int $groupId, int $stateId): void; - - /** - * Load object state data for $contentId content from $stateGroupId state group. - */ - abstract public function loadObjectStateDataForContent( - int $contentId, - int $stateGroupId - ): array; - - /** - * Return the number of objects which are in this state. - */ - abstract public function getContentCount(int $stateId): int; - - /** - * Update the object state priority to provided value. - */ - abstract public function updateObjectStatePriority(int $stateId, int $priority): void; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php deleted file mode 100644 index 6e82403dd1..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,769 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator; -use eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway; -use eZ\Publish\SPI\Persistence\Content\ObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group; - -/** - * Object State gateway implementation using the Doctrine database. - * - * @internal Gateway implementation is considered internal. Use Persistence Location Handler instead. - * - * @see \eZ\Publish\SPI\Persistence\Content\ObjectState\Handler - */ -final class DoctrineDatabase extends Gateway -{ - /** - * Language mask generator. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator - */ - private $maskGenerator; - - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */ - private $dbPlatform; - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct(Connection $connection, MaskGenerator $maskGenerator) - { - $this->connection = $connection; - $this->dbPlatform = $this->connection->getDatabasePlatform(); - $this->maskGenerator = $maskGenerator; - } - - public function loadObjectStateData(int $stateId): array - { - $query = $this->createObjectStateFindQuery(); - $query->where( - $query->expr()->eq( - 'state.id', - $query->createPositionalParameter($stateId, ParameterType::INTEGER) - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadObjectStateDataByIdentifier(string $identifier, int $groupId): array - { - $query = $this->createObjectStateFindQuery(); - $query->where( - $query->expr()->andX( - $query->expr()->eq( - 'state.identifier', - $query->createPositionalParameter($identifier, ParameterType::STRING) - ), - $query->expr()->eq( - 'state.group_id', - $query->createPositionalParameter($groupId, ParameterType::INTEGER) - ) - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadObjectStateListData(int $groupId): array - { - $query = $this->createObjectStateFindQuery(); - $query->where( - $query->expr()->eq( - 'state.group_id', - $query->createPositionalParameter($groupId, ParameterType::INTEGER) - ) - )->orderBy('state.priority', 'ASC'); - - $statement = $query->execute(); - - $rows = []; - while ($row = $statement->fetch(FetchMode::ASSOCIATIVE)) { - $rows[$row['ezcobj_state_id']][] = $row; - } - - return array_values($rows); - } - - public function loadObjectStateGroupData(int $groupId): array - { - $query = $this->createObjectStateGroupFindQuery(); - $query->where( - $query->expr()->eq( - 'state_group.id', - $query->createPositionalParameter($groupId, ParameterType::INTEGER) - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadObjectStateGroupDataByIdentifier(string $identifier): array - { - $query = $this->createObjectStateGroupFindQuery(); - $query->where( - $query->expr()->eq( - 'state_group.identifier', - $query->createPositionalParameter($identifier, ParameterType::STRING) - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadObjectStateGroupListData(int $offset, int $limit): array - { - $query = $this->createObjectStateGroupFindQuery(); - if ($limit > 0) { - $query->setMaxResults($limit); - $query->setFirstResult($offset); - } - - $statement = $query->execute(); - - $rows = []; - while ($row = $statement->fetch(FetchMode::ASSOCIATIVE)) { - $rows[$row['ezcobj_state_group_id']][] = $row; - } - - return array_values($rows); - } - - /** - * @throws \Doctrine\DBAL\DBALException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function insertObjectState(ObjectState $objectState, int $groupId): void - { - $maxPriority = $this->getMaxPriorityForObjectStatesInGroup($groupId); - - $objectState->priority = $maxPriority === null ? 0 : (int)$maxPriority + 1; - $objectState->groupId = (int)$groupId; - - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::OBJECT_STATE_TABLE) - ->values( - [ - 'group_id' => $query->createPositionalParameter( - $objectState->groupId, - ParameterType::INTEGER - ), - 'default_language_id' => $query->createPositionalParameter( - $this->maskGenerator->generateLanguageIndicator( - $objectState->defaultLanguage, - false - ), - ParameterType::INTEGER - ), - 'identifier' => $query->createPositionalParameter( - $objectState->identifier, - ParameterType::STRING - ), - 'language_mask' => $query->createPositionalParameter( - $this->maskGenerator->generateLanguageMaskFromLanguageCodes( - $objectState->languageCodes, - true - ), - ParameterType::INTEGER - ), - 'priority' => $query->createPositionalParameter( - $objectState->priority, - ParameterType::INTEGER - ), - ] - ); - - $query->execute(); - - $objectState->id = (int)$this->connection->lastInsertId(self::OBJECT_STATE_TABLE_SEQ); - - $this->insertObjectStateTranslations($objectState); - - // If this is a first state in group, assign it to all content objects - if ($maxPriority === null) { - $this->connection->executeUpdate( - 'INSERT INTO ezcobj_state_link (contentobject_id, contentobject_state_id) ' . - "SELECT id, {$objectState->id} FROM ezcontentobject" - ); - } - } - - /** - * @param string[] $languageCodes - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - private function updateObjectStateCommonFields( - string $tableName, - int $id, - string $identifier, - string $defaultLanguageCode, - array $languageCodes - ): void { - $query = $this->connection->createQueryBuilder(); - $query - ->update($tableName) - ->set( - 'default_language_id', - $query->createPositionalParameter( - $this->maskGenerator->generateLanguageIndicator( - $defaultLanguageCode, - false - ), - ParameterType::INTEGER - ) - ) - ->set( - 'identifier', - $query->createPositionalParameter( - $identifier, - ParameterType::STRING - ) - ) - ->set( - 'language_mask', - $query->createPositionalParameter( - $this->maskGenerator->generateLanguageMaskFromLanguageCodes( - $languageCodes, - true - ), - ParameterType::INTEGER - ) - ) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - - $query->execute(); - } - - public function updateObjectState(ObjectState $objectState): void - { - // First update the state - $this->updateObjectStateCommonFields( - self::OBJECT_STATE_TABLE, - $objectState->id, - $objectState->identifier, - $objectState->defaultLanguage, - $objectState->languageCodes - ); - - // And then refresh object state translations - // by removing existing ones and adding new ones - $this->deleteObjectStateTranslations($objectState->id); - $this->insertObjectStateTranslations($objectState); - } - - public function deleteObjectState(int $stateId): void - { - $this->deleteObjectStateTranslations($stateId); - - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::OBJECT_STATE_TABLE) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($stateId, ParameterType::INTEGER) - ) - ); - - $query->execute(); - } - - public function updateObjectStateLinks(int $oldStateId, int $newStateId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::OBJECT_STATE_LINK_TABLE) - ->set( - 'contentobject_state_id', - $query->createPositionalParameter($newStateId, ParameterType::INTEGER) - ) - ->where( - $query->expr()->eq( - 'contentobject_state_id', - $query->createPositionalParameter($oldStateId, ParameterType::INTEGER) - ) - ) - ; - - $query->execute(); - } - - /** - * Change Content to object state assignment. - */ - private function updateContentStateAssignment( - int $contentId, - int $stateId, - int $assignedStateId - ): void { - // no action required if there's no change - if ($stateId === $assignedStateId) { - return; - } - - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::OBJECT_STATE_LINK_TABLE) - ->set( - 'contentobject_state_id', - $query->createPositionalParameter($stateId, ParameterType::INTEGER) - ) - ->where( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ) - ->andWhere( - $query->expr()->eq( - 'contentobject_state_id', - $query->createPositionalParameter($assignedStateId, ParameterType::INTEGER) - ) - ) - ; - - $query->execute(); - } - - public function deleteObjectStateLinks(int $stateId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::OBJECT_STATE_LINK_TABLE) - ->where( - $query->expr()->eq( - 'contentobject_state_id', - $query->createPositionalParameter($stateId, ParameterType::INTEGER) - ) - ); - - $query->execute(); - } - - public function insertObjectStateGroup(Group $objectStateGroup): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::OBJECT_STATE_GROUP_TABLE) - ->values( - [ - 'default_language_id' => $query->createPositionalParameter( - $this->maskGenerator->generateLanguageIndicator( - $objectStateGroup->defaultLanguage, - false - ), - ParameterType::INTEGER - ), - 'identifier' => $query->createPositionalParameter( - $objectStateGroup->identifier, - ParameterType::STRING - ), - 'language_mask' => $query->createPositionalParameter( - $this->maskGenerator->generateLanguageMaskFromLanguageCodes( - $objectStateGroup->languageCodes, - true - ), - ParameterType::INTEGER - ), - ] - ) - ; - - $query->execute(); - - $objectStateGroup->id = (int)$this->connection->lastInsertId( - self::OBJECT_STATE_GROUP_TABLE_SEQ - ); - - $this->insertObjectStateGroupTranslations($objectStateGroup); - } - - public function updateObjectStateGroup(Group $objectStateGroup): void - { - // First update the group - $this->updateObjectStateCommonFields( - self::OBJECT_STATE_GROUP_TABLE, - $objectStateGroup->id, - $objectStateGroup->identifier, - $objectStateGroup->defaultLanguage, - $objectStateGroup->languageCodes - ); - - // And then refresh group translations - // by removing old ones and adding new ones - $this->deleteObjectStateGroupTranslations($objectStateGroup->id); - $this->insertObjectStateGroupTranslations($objectStateGroup); - } - - public function deleteObjectStateGroup(int $groupId): void - { - $this->deleteObjectStateGroupTranslations($groupId); - - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::OBJECT_STATE_GROUP_TABLE) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($groupId, ParameterType::INTEGER) - ) - ) - ; - - $query->execute(); - } - - public function setContentState(int $contentId, int $groupId, int $stateId): void - { - // First find out if $contentId is related to existing states in $groupId - $assignedStateId = $this->getContentStateId($contentId, $groupId); - - if (null !== $assignedStateId) { - // We already have a state assigned to $contentId, update to new one - $this->updateContentStateAssignment($contentId, $stateId, $assignedStateId); - } else { - // No state assigned to $contentId from specified group, create assignment - $this->insertContentStateAssignment($contentId, $stateId); - } - } - - public function loadObjectStateDataForContent(int $contentId, int $stateGroupId): array - { - $query = $this->createObjectStateFindQuery(); - $expr = $query->expr(); - $query - ->innerJoin( - 'state', - 'ezcobj_state_link', - 'link', - 'state.id = link.contentobject_state_id' - ) - ->where( - $expr->eq( - 'state.group_id', - $query->createPositionalParameter($stateGroupId, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - 'link.contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function getContentCount(int $stateId): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - $this->dbPlatform->getCountExpression('contentobject_id') - ) - ->from(self::OBJECT_STATE_LINK_TABLE) - ->where( - $query->expr()->eq( - 'contentobject_state_id', - $query->createPositionalParameter($stateId, ParameterType::INTEGER) - ) - ); - - return (int)$query->execute()->fetchColumn(); - } - - public function updateObjectStatePriority(int $stateId, int $priority): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::OBJECT_STATE_TABLE) - ->set('priority', $query->createPositionalParameter($priority, ParameterType::INTEGER)) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($stateId, ParameterType::INTEGER) - ) - ) - ; - - $query->execute(); - } - - /** - * Create a generic query for fetching object state(s). - */ - private function createObjectStateFindQuery(): QueryBuilder - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - // Object state - 'state.default_language_id AS ezcobj_state_default_language_id', - 'state.group_id AS ezcobj_state_group_id', - 'state.id AS ezcobj_state_id', - 'state.identifier AS ezcobj_state_identifier', - 'state.language_mask AS ezcobj_state_language_mask', - 'state.priority AS ezcobj_state_priority', - // Object state language - 'lang.description AS ezcobj_state_language_description', - 'lang.language_id AS ezcobj_state_language_language_id', - 'lang.name AS ezcobj_state_language_name' - ) - ->from(self::OBJECT_STATE_TABLE, 'state') - ->innerJoin( - 'state', - self::OBJECT_STATE_LANGUAGE_TABLE, - 'lang', - 'state.id = lang.contentobject_state_id', - ); - - return $query; - } - - /** - * Create a generic query for fetching object state group(s). - */ - private function createObjectStateGroupFindQuery(): QueryBuilder - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - // Object state group - 'state_group.default_language_id AS ezcobj_state_group_default_language_id', - 'state_group.id AS ezcobj_state_group_id', - 'state_group.identifier AS ezcobj_state_group_identifier', - 'state_group.language_mask AS ezcobj_state_group_language_mask', - // Object state group language - 'state_group_lang.description AS ezcobj_state_group_language_description', - 'state_group_lang.language_id AS ezcobj_state_group_language_language_id', - 'state_group_lang.real_language_id AS ezcobj_state_group_language_real_language_id', - 'state_group_lang.name AS ezcobj_state_group_language_name' - ) - ->from(self::OBJECT_STATE_GROUP_TABLE, 'state_group') - ->innerJoin( - 'state_group', - self::OBJECT_STATE_GROUP_LANGUAGE_TABLE, - 'state_group_lang', - 'state_group.id = state_group_lang.contentobject_state_group_id' - ); - - return $query; - } - - /** - * Insert object state group translations into database. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if Object State language does not exist - */ - private function insertObjectStateTranslations(ObjectState $objectState): void - { - foreach ($objectState->languageCodes as $languageCode) { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::OBJECT_STATE_LANGUAGE_TABLE) - ->values( - [ - 'contentobject_state_id' => $query->createPositionalParameter( - $objectState->id, - ParameterType::INTEGER - ), - 'description' => $query->createPositionalParameter( - $objectState->description[$languageCode], - ParameterType::STRING - ), - 'name' => $query->createPositionalParameter( - $objectState->name[$languageCode], - ParameterType::STRING - ), - 'language_id' => $query->createPositionalParameter( - $this->maskGenerator->generateLanguageIndicator( - $languageCode, - $languageCode === $objectState->defaultLanguage - ), - ParameterType::INTEGER - ), - ] - ); - - $query->execute(); - } - } - - /** - * Deletes all translations of the $stateId state. - * - * @param mixed $stateId - */ - private function deleteObjectStateTranslations(int $stateId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::OBJECT_STATE_LANGUAGE_TABLE) - ->where( - $query->expr()->eq( - 'contentobject_state_id', - $query->createPositionalParameter($stateId, ParameterType::INTEGER) - ) - ); - - $query->execute(); - } - - /** - * Insert object state group translations into database. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if Object State Group language does not exist - */ - private function insertObjectStateGroupTranslations(Group $objectStateGroup): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::OBJECT_STATE_GROUP_LANGUAGE_TABLE) - ->values( - [ - 'contentobject_state_group_id' => ':contentobject_state_group_id', - 'description' => ':description', - 'name' => ':name', - 'language_id' => ':language_id', - 'real_language_id' => ':real_language_id', - ] - ) - ; - foreach ($objectStateGroup->languageCodes as $languageCode) { - $languageId = $this->maskGenerator->generateLanguageIndicator( - $languageCode, - $languageCode === $objectStateGroup->defaultLanguage - ); - $query - ->setParameter('contentobject_state_group_id', $objectStateGroup->id, ParameterType::INTEGER) - ->setParameter('description', $objectStateGroup->description[$languageCode], ParameterType::STRING) - ->setParameter('name', $objectStateGroup->name[$languageCode], ParameterType::STRING) - ->setParameter('language_id', $languageId, ParameterType::INTEGER) - ->setParameter('real_language_id', $languageId & ~1, ParameterType::INTEGER); - - $query->execute(); - } - } - - /** - * Delete all translations of the $groupId state group. - */ - private function deleteObjectStateGroupTranslations(int $groupId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::OBJECT_STATE_GROUP_LANGUAGE_TABLE) - ->where( - $query->expr()->eq( - 'contentobject_state_group_id', - $query->createPositionalParameter($groupId, ParameterType::INTEGER) - ) - ); - - $query->execute(); - } - - private function getMaxPriorityForObjectStatesInGroup(int $groupId): ?int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - $this->dbPlatform->getMaxExpression('priority') - ) - ->from(self::OBJECT_STATE_TABLE) - ->where( - $query->expr()->eq( - 'group_id', - $query->createPositionalParameter($groupId, ParameterType::INTEGER) - ) - ); - - $priority = $query->execute()->fetchColumn(); - - return null !== $priority ? (int)$priority : null; - } - - private function getContentStateId(int $contentId, int $groupId): ?int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('state.id') - ->from(self::OBJECT_STATE_TABLE, 'state') - ->innerJoin( - 'state', - self::OBJECT_STATE_LINK_TABLE, - 'link', - 'state.id = link.contentobject_state_id' - ) - ->where( - $query->expr()->eq( - 'state.group_id', - $query->createPositionalParameter($groupId, ParameterType::INTEGER) - ) - ) - ->andWhere( - $query->expr()->eq( - 'link.contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ); - - $stateId = $query->execute()->fetch(FetchMode::COLUMN); - - return false !== $stateId ? (int)$stateId : null; - } - - private function insertContentStateAssignment(int $contentId, int $stateId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::OBJECT_STATE_LINK_TABLE) - ->values( - [ - 'contentobject_id' => $query->createPositionalParameter( - $contentId, - ParameterType::INTEGER - ), - 'contentobject_state_id' => $query->createPositionalParameter( - $stateId, - ParameterType::INTEGER - ), - ] - ); - - $query->execute(); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php deleted file mode 100644 index f2b45800e2..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,201 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway; -use eZ\Publish\SPI\Persistence\Content\ObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group; -use PDOException; - -/** - * @internal Internal exception conversion layer. - */ -final class ExceptionConversion extends Gateway -{ - /** - * The wrapped gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway - */ - private $innerGateway; - - /** - * Creates a new exception conversion gateway around $innerGateway. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function loadObjectStateData(int $stateId): array - { - try { - return $this->innerGateway->loadObjectStateData($stateId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadObjectStateDataByIdentifier(string $identifier, int $groupId): array - { - try { - return $this->innerGateway->loadObjectStateDataByIdentifier($identifier, $groupId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadObjectStateListData(int $groupId): array - { - try { - return $this->innerGateway->loadObjectStateListData($groupId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadObjectStateGroupData(int $groupId): array - { - try { - return $this->innerGateway->loadObjectStateGroupData($groupId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadObjectStateGroupDataByIdentifier(string $identifier): array - { - try { - return $this->innerGateway->loadObjectStateGroupDataByIdentifier($identifier); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadObjectStateGroupListData(int $offset, int $limit): array - { - try { - return $this->innerGateway->loadObjectStateGroupListData($offset, $limit); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function insertObjectState(ObjectState $objectState, int $groupId): void - { - try { - $this->innerGateway->insertObjectState($objectState, $groupId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateObjectState(ObjectState $objectState): void - { - try { - $this->innerGateway->updateObjectState($objectState); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteObjectState(int $stateId): void - { - try { - $this->innerGateway->deleteObjectState($stateId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateObjectStateLinks(int $oldStateId, int $newStateId): void - { - try { - $this->innerGateway->updateObjectStateLinks($oldStateId, $newStateId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteObjectStateLinks(int $stateId): void - { - try { - $this->innerGateway->deleteObjectStateLinks($stateId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function insertObjectStateGroup(Group $objectStateGroup): void - { - try { - $this->innerGateway->insertObjectStateGroup($objectStateGroup); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateObjectStateGroup(Group $objectStateGroup): void - { - try { - $this->innerGateway->updateObjectStateGroup($objectStateGroup); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteObjectStateGroup(int $groupId): void - { - try { - $this->innerGateway->deleteObjectStateGroup($groupId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function setContentState(int $contentId, int $groupId, int $stateId): void - { - try { - $this->innerGateway->setContentState($contentId, $groupId, $stateId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadObjectStateDataForContent(int $contentId, int $stateGroupId): array - { - try { - return $this->innerGateway->loadObjectStateDataForContent($contentId, $stateGroupId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getContentCount(int $stateId): int - { - try { - return $this->innerGateway->getContentCount($stateId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateObjectStatePriority(int $stateId, int $priority): void - { - try { - $this->innerGateway->updateObjectStatePriority($stateId, $priority); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Handler.php deleted file mode 100644 index 5820156403..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Handler.php +++ /dev/null @@ -1,352 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\ObjectState; - -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Handler as BaseObjectStateHandler; -use eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct; - -/** - * The Object State Handler class provides managing of object states and groups. - */ -class Handler implements BaseObjectStateHandler -{ - /** - * ObjectState Gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway - */ - protected $objectStateGateway; - - /** - * ObjectState Mapper. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper - */ - protected $objectStateMapper; - - /** - * Creates a new ObjectState Handler. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway $objectStateGateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper $objectStateMapper - */ - public function __construct(Gateway $objectStateGateway, Mapper $objectStateMapper) - { - $this->objectStateGateway = $objectStateGateway; - $this->objectStateMapper = $objectStateMapper; - } - - /** - * Creates a new object state group. - * - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct $input - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group - */ - public function createGroup(InputStruct $input) - { - $objectStateGroup = $this->objectStateMapper->createObjectStateGroupFromInputStruct($input); - $this->objectStateGateway->insertObjectStateGroup($objectStateGroup); - - return $objectStateGroup; - } - - /** - * Loads an object state group. - * - * @param mixed $groupId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the group was not found - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group - */ - public function loadGroup($groupId) - { - $data = $this->objectStateGateway->loadObjectStateGroupData($groupId); - - if (empty($data)) { - throw new NotFoundException('ObjectStateGroup', $groupId); - } - - return $this->objectStateMapper->createObjectStateGroupFromData($data); - } - - /** - * Loads a object state group by identifier. - * - * @param string $identifier - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the group was not found - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group - */ - public function loadGroupByIdentifier($identifier) - { - $data = $this->objectStateGateway->loadObjectStateGroupDataByIdentifier($identifier); - - if (empty($data)) { - throw new NotFoundException('ObjectStateGroup', $identifier); - } - - return $this->objectStateMapper->createObjectStateGroupFromData($data); - } - - /** - * Loads all object state groups. - * - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group[] - */ - public function loadAllGroups($offset = 0, $limit = -1) - { - $data = $this->objectStateGateway->loadObjectStateGroupListData($offset, $limit); - - return $this->objectStateMapper->createObjectStateGroupListFromData($data); - } - - /** - * This method returns the ordered list of object states of a group. - * - * @param mixed $groupId - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState[] - */ - public function loadObjectStates($groupId) - { - $data = $this->objectStateGateway->loadObjectStateListData($groupId); - - return $this->objectStateMapper->createObjectStateListFromData($data); - } - - /** - * Updates an object state group. - * - * @param mixed $groupId - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct $input - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group - */ - public function updateGroup($groupId, InputStruct $input) - { - $objectStateGroup = $this->objectStateMapper->createObjectStateGroupFromInputStruct($input); - $objectStateGroup->id = (int)$groupId; - - $this->objectStateGateway->updateObjectStateGroup($objectStateGroup); - - return $this->loadGroup($objectStateGroup->id); - } - - /** - * Deletes a object state group including all states and links to content. - * - * @param mixed $groupId - */ - public function deleteGroup($groupId) - { - $objectStates = $this->loadObjectStates($groupId); - foreach ($objectStates as $objectState) { - $this->objectStateGateway->deleteObjectStateLinks($objectState->id); - $this->objectStateGateway->deleteObjectState($objectState->id); - } - - $this->objectStateGateway->deleteObjectStateGroup($groupId); - } - - /** - * Creates a new object state in the given group. - * The new state gets the last priority. - * Note: in current kernel: If it is the first state all content objects will - * set to this state. - * - * @param mixed $groupId - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct $input - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - public function create($groupId, InputStruct $input) - { - $objectState = $this->objectStateMapper->createObjectStateFromInputStruct($input); - $this->objectStateGateway->insertObjectState($objectState, $groupId); - - return $objectState; - } - - /** - * Loads an object state. - * - * @param mixed $stateId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the state was not found - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - public function load($stateId) - { - $data = $this->objectStateGateway->loadObjectStateData($stateId); - - if (empty($data)) { - throw new NotFoundException('ObjectState', $stateId); - } - - return $this->objectStateMapper->createObjectStateFromData($data); - } - - /** - * Loads an object state by identifier and group it belongs to. - * - * @param string $identifier - * @param mixed $groupId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the state was not found - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - public function loadByIdentifier($identifier, $groupId) - { - $data = $this->objectStateGateway->loadObjectStateDataByIdentifier($identifier, $groupId); - - if (empty($data)) { - throw new NotFoundException('ObjectState', ['identifier' => $identifier, 'groupId' => $groupId]); - } - - return $this->objectStateMapper->createObjectStateFromData($data); - } - - /** - * Updates an object state. - * - * @param mixed $stateId - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct $input - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - public function update($stateId, InputStruct $input) - { - $objectState = $this->objectStateMapper->createObjectStateFromInputStruct($input); - $objectState->id = (int)$stateId; - - $this->objectStateGateway->updateObjectState($objectState); - - return $this->load($objectState->id); - } - - /** - * Changes the priority of the state. - * - * @param mixed $stateId - * @param int $priority - */ - public function setPriority($stateId, $priority) - { - $objectState = $this->load($stateId); - $groupObjectStates = $this->loadObjectStates($objectState->groupId); - - $priorityList = []; - foreach ($groupObjectStates as $index => $groupObjectState) { - // Update given state and push all other states with same or higher priority down - if ($objectState->id === $groupObjectState->id) { - $priorityList[$groupObjectState->id] = (int)$priority; - } else { - $priorityList[$groupObjectState->id] = ($index < $priority ? $index : $index + 1); - } - } - - asort($priorityList, SORT_NUMERIC); - - foreach (array_keys($priorityList) as $objectStatePriority => $objectStateId) { - $this->objectStateGateway->updateObjectStatePriority($objectStateId, $objectStatePriority); - } - } - - /** - * Deletes a object state. The state of the content objects is reset to the - * first object state in the group. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If state with $stateId doesn't exist - * - * @param mixed $stateId - */ - public function delete($stateId) - { - // Get the object state first as we need group ID - // to reorder the priorities and reassign content to another state in the group - $objectState = $this->load($stateId); - - $this->objectStateGateway->deleteObjectState($objectState->id); - - $remainingStates = $this->loadObjectStates($objectState->groupId); - if (empty($remainingStates)) { - // If there are no more states in the group, just remove the state links - $this->objectStateGateway->deleteObjectStateLinks($objectState->id); - - return; - } - - $priority = 0; - foreach ($remainingStates as $remainingState) { - $this->objectStateGateway->updateObjectStatePriority($remainingState->id, $priority); - ++$priority; - } - - $remainingStates = $this->loadObjectStates($objectState->groupId); - $this->objectStateGateway->updateObjectStateLinks($objectState->id, current($remainingStates)->id); - } - - /** - * Sets the object-state of a state group to $stateId for the given content. - * - * @param mixed $contentId - * @param mixed $groupId - * @param mixed $stateId - * - * @return bool - */ - public function setContentState($contentId, $groupId, $stateId) - { - $this->objectStateGateway->setContentState($contentId, $groupId, $stateId); - - return true; - } - - /** - * Gets the object-state of object identified by $contentId. - * - * The $state is the id of the state within one group. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If no state is found - * - * @param mixed $contentId - * @param mixed $stateGroupId - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - public function getContentState($contentId, $stateGroupId) - { - $data = $this->objectStateGateway->loadObjectStateDataForContent($contentId, $stateGroupId); - - if (empty($data)) { - throw new NotFoundException('ObjectState', ['groupId' => $stateGroupId]); - } - - return $this->objectStateMapper->createObjectStateFromData($data); - } - - /** - * Returns the number of objects which are in this state. - * - * @param mixed $stateId - * - * @return int - */ - public function getContentCount($stateId) - { - return $this->objectStateGateway->getContentCount($stateId); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Mapper.php b/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Mapper.php deleted file mode 100644 index 66e04377b6..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/ObjectState/Mapper.php +++ /dev/null @@ -1,193 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\ObjectState; - -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use eZ\Publish\SPI\Persistence\Content\ObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group; -use eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct; - -/** - * Mapper for ObjectState and object state Group objects. - */ -class Mapper -{ - /** - * Language handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler - */ - protected $languageHandler; - - /** - * Creates a new mapper. - * - * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $languageHandler - */ - public function __construct(LanguageHandler $languageHandler) - { - $this->languageHandler = $languageHandler; - } - - /** - * Creates ObjectState object from provided $data. - * - * @param array $data - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - public function createObjectStateFromData(array $data) - { - $objectState = new ObjectState(); - - $languageIds = [(int)$data[0]['ezcobj_state_default_language_id']]; - foreach ($data as $stateTranslation) { - $languageIds[] = (int)$stateTranslation['ezcobj_state_language_language_id'] & ~1; - } - $languages = $this->languageHandler->loadList($languageIds); - - $objectState->id = (int)$data[0]['ezcobj_state_id']; - $objectState->groupId = (int)$data[0]['ezcobj_state_group_id']; - $objectState->identifier = $data[0]['ezcobj_state_identifier']; - $objectState->priority = (int)$data[0]['ezcobj_state_priority']; - $objectState->defaultLanguage = $languages[(int)$data[0]['ezcobj_state_default_language_id']]->languageCode; - - $objectState->languageCodes = []; - $objectState->name = []; - $objectState->description = []; - - foreach ($data as $stateTranslation) { - $languageCode = $languages[$stateTranslation['ezcobj_state_language_language_id'] & ~1]->languageCode; - $objectState->languageCodes[] = $languageCode; - $objectState->name[$languageCode] = $stateTranslation['ezcobj_state_language_name']; - $objectState->description[$languageCode] = $stateTranslation['ezcobj_state_language_description']; - } - - return $objectState; - } - - /** - * Creates ObjectState array of objects from provided $data. - * - * @param array $data - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState[] - */ - public function createObjectStateListFromData(array $data) - { - $objectStates = []; - - foreach ($data as $objectStateData) { - $objectStates[] = $this->createObjectStateFromData($objectStateData); - } - - return $objectStates; - } - - /** - * Creates ObjectStateGroup object from provided $data. - * - * @param array $data - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group - */ - public function createObjectStateGroupFromData(array $data) - { - $objectStateGroup = new Group(); - - $languageIds = [(int)$data[0]['ezcobj_state_group_default_language_id']]; - foreach ($data as $groupTranslation) { - $languageIds[] = (int)$groupTranslation['ezcobj_state_group_language_real_language_id']; - } - $languages = $this->languageHandler->loadList($languageIds); - - $objectStateGroup->id = (int)$data[0]['ezcobj_state_group_id']; - $objectStateGroup->identifier = $data[0]['ezcobj_state_group_identifier']; - $objectStateGroup->defaultLanguage = $languages[ - (int)$data[0]['ezcobj_state_group_default_language_id'] - ]->languageCode; - - $objectStateGroup->languageCodes = []; - $objectStateGroup->name = []; - $objectStateGroup->description = []; - - foreach ($data as $groupTranslation) { - $languageCode = $languages[(int)$groupTranslation['ezcobj_state_group_language_real_language_id']]->languageCode; - $objectStateGroup->languageCodes[] = $languageCode; - $objectStateGroup->name[$languageCode] = $groupTranslation['ezcobj_state_group_language_name']; - $objectStateGroup->description[$languageCode] = $groupTranslation['ezcobj_state_group_language_description']; - } - - return $objectStateGroup; - } - - /** - * Creates ObjectStateGroup array of objects from provided $data. - * - * @param array $data - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group[] - */ - public function createObjectStateGroupListFromData(array $data) - { - $objectStateGroups = []; - - foreach ($data as $objectStateGroupData) { - $objectStateGroups[] = $this->createObjectStateGroupFromData($objectStateGroupData); - } - - return $objectStateGroups; - } - - /** - * Creates an instance of ObjectStateGroup object from provided $input struct. - * - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct $input - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group - */ - public function createObjectStateGroupFromInputStruct(InputStruct $input) - { - $objectStateGroup = new Group(); - - $objectStateGroup->identifier = $input->identifier; - $objectStateGroup->defaultLanguage = $input->defaultLanguage; - $objectStateGroup->name = $input->name; - $objectStateGroup->description = $input->description; - - $objectStateGroup->languageCodes = []; - foreach ($input->name as $languageCode => $name) { - $objectStateGroup->languageCodes[] = $languageCode; - } - - return $objectStateGroup; - } - - /** - * Creates an instance of ObjectState object from provided $input struct. - * - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct $input - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - public function createObjectStateFromInputStruct(InputStruct $input) - { - $objectState = new ObjectState(); - - $objectState->identifier = $input->identifier; - $objectState->defaultLanguage = $input->defaultLanguage; - $objectState->name = $input->name; - $objectState->description = $input->description; - - $objectState->languageCodes = []; - foreach ($input->name as $languageCode => $name) { - $objectState->languageCodes[] = $languageCode; - } - - return $objectState; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Section/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/Content/Section/Gateway.php deleted file mode 100644 index d646efc7c9..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Section/Gateway.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\Section; - -/** - * Base class for Section gateways. - * - * @internal For internal use by Persistence Handlers. - */ -abstract class Gateway -{ - public const CONTENT_SECTION_SEQ = 'ezsection_id_seq'; - public const CONTENT_SECTION_TABLE = 'ezsection'; - - /** - * Inserts a new section with $name and $identifier. - * - * @return int The ID of the new section - */ - abstract public function insertSection(string $name, string $identifier): int; - - /** - * Updates section with $id to have $name and $identifier. - */ - abstract public function updateSection(int $id, string $name, string $identifier): void; - - /** - * Loads data for section with $id. - */ - abstract public function loadSectionData(int $id): array; - - /** - * Loads data for all sections. - */ - abstract public function loadAllSectionData(): array; - - /** - * Loads data for section with $identifier. - */ - abstract public function loadSectionDataByIdentifier(string $identifier): array; - - /** - * Counts the number of content objects assigned to section with $id. - */ - abstract public function countContentObjectsInSection(int $id): int; - - /** - * Counts the number of role policies using section with $id in their limitations. - */ - abstract public function countPoliciesUsingSection(int $id): int; - - /** - * Counts the number of role assignments using section with $id in their limitations. - */ - abstract public function countRoleAssignmentsUsingSection(int $id): int; - - /** - * Deletes the Section with $id. - */ - abstract public function deleteSection(int $id): void; - - /** - * Inserts the assignment of $contentId to $sectionId. - */ - abstract public function assignSectionToContent(int $sectionId, int $contentId): void; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabase.php deleted file mode 100644 index 037eb9dfb1..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,234 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway; - -/** - * @internal Gateway implementation is considered internal. Use Persistence Section Handler instead. - * - * @see \eZ\Publish\SPI\Persistence\Content\Section\Handler - */ -final class DoctrineDatabase extends Gateway -{ - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */ - private $dbPlatform; - - /** - * Creates a new DoctrineDatabase Section Gateway. - * - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct(Connection $connection) - { - $this->connection = $connection; - $this->dbPlatform = $this->connection->getDatabasePlatform(); - } - - public function insertSection(string $name, string $identifier): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::CONTENT_SECTION_TABLE) - ->values( - [ - 'name' => $query->createPositionalParameter($name), - 'identifier' => $query->createPositionalParameter($identifier), - ] - ); - - $query->execute(); - - return (int)$this->connection->lastInsertId(Gateway::CONTENT_SECTION_SEQ); - } - - public function updateSection(int $id, string $name, string $identifier): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::CONTENT_SECTION_TABLE) - ->set('name', $query->createPositionalParameter($name)) - ->set('identifier', $query->createPositionalParameter($identifier)) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - - $query->execute(); - } - - public function loadSectionData(int $id): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select(['id', 'identifier', 'name']) - ->from(self::CONTENT_SECTION_TABLE) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadAllSectionData(): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select(['id', 'identifier', 'name']) - ->from(self::CONTENT_SECTION_TABLE); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadSectionDataByIdentifier(string $identifier): array - { - $query = $this->connection->createQueryBuilder(); - $query->select( - 'id', - 'identifier', - 'name' - )->from( - self::CONTENT_SECTION_TABLE - )->where( - $query->expr()->eq( - 'identifier', - $query->createPositionalParameter($identifier, ParameterType::STRING) - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function countContentObjectsInSection(int $id): int - { - $query = $this->connection->createQueryBuilder(); - $query->select( - $this->dbPlatform->getCountExpression('id') - )->from( - 'ezcontentobject' - )->where( - $query->expr()->eq( - 'section_id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - - $statement = $query->execute(); - - return (int)$statement->fetchColumn(); - } - - public function countPoliciesUsingSection(int $id): int - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select($this->dbPlatform->getCountExpression('l.id')) - ->from('ezpolicy_limitation', 'l') - ->join( - 'l', - 'ezpolicy_limitation_value', - 'lv', - $expr->eq( - 'l.id', - 'lv.limitation_id' - ) - ) - ->where( - $expr->eq( - 'l.identifier', - $query->createPositionalParameter('Section', ParameterType::STRING) - ) - ) - ->andWhere( - $expr->eq( - 'lv.value', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ) - ; - - return (int)$query->execute()->fetchColumn(); - } - - public function countRoleAssignmentsUsingSection(int $id): int - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select($this->dbPlatform->getCountExpression('ur.id')) - ->from('ezuser_role', 'ur') - ->where( - $expr->eq( - 'ur.limit_identifier', - $query->createPositionalParameter('Section', ParameterType::STRING) - ) - ) - ->andWhere( - $expr->eq( - 'ur.limit_value', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ) - ; - - return (int)$query->execute()->fetchColumn(); - } - - public function deleteSection(int $id): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::CONTENT_SECTION_TABLE) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - - $query->execute(); - } - - public function assignSectionToContent(int $sectionId, int $contentId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update('ezcontentobject') - ->set( - 'section_id', - $query->createPositionalParameter($sectionId, ParameterType::INTEGER) - ) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ); - - $query->execute(); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Section/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/Content/Section/Gateway/ExceptionConversion.php deleted file mode 100644 index 813ad0c47f..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Section/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,127 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway; -use PDOException; - -/** - * @internal Internal exception conversion layer. - */ -final class ExceptionConversion extends Gateway -{ - /** - * The wrapped gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway - */ - private $innerGateway; - - /** - * Creates a new exception conversion gateway around $innerGateway. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function insertSection(string $name, string $identifier): int - { - try { - return $this->innerGateway->insertSection($name, $identifier); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateSection(int $id, string $name, string $identifier): void - { - try { - $this->innerGateway->updateSection($id, $name, $identifier); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadSectionData(int $id): array - { - try { - return $this->innerGateway->loadSectionData($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadAllSectionData(): array - { - try { - return $this->innerGateway->loadAllSectionData(); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadSectionDataByIdentifier(string $identifier): array - { - try { - return $this->innerGateway->loadSectionDataByIdentifier($identifier); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countContentObjectsInSection(int $id): int - { - try { - return $this->innerGateway->countContentObjectsInSection($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countPoliciesUsingSection(int $id): int - { - try { - return $this->innerGateway->countPoliciesUsingSection($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countRoleAssignmentsUsingSection(int $id): int - { - try { - return $this->innerGateway->countRoleAssignmentsUsingSection($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteSection(int $id): void - { - try { - $this->innerGateway->deleteSection($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function assignSectionToContent(int $sectionId, int $contentId): void - { - try { - $this->innerGateway->assignSectionToContent($sectionId, $contentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Section/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Content/Section/Handler.php deleted file mode 100644 index 97556234c2..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Section/Handler.php +++ /dev/null @@ -1,231 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Section; - -use eZ\Publish\Core\Base\Exceptions\NotFoundException as NotFound; -use eZ\Publish\SPI\Persistence\Content\Section; -use eZ\Publish\SPI\Persistence\Content\Section\Handler as BaseSectionHandler; -use RuntimeException; - -/** - * Section Handler. - */ -class Handler implements BaseSectionHandler -{ - /** - * Section Gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway - */ - protected $sectionGateway; - - /** - * Creates a new Section Handler. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway $sectionGateway - */ - public function __construct(Gateway $sectionGateway) - { - $this->sectionGateway = $sectionGateway; - } - - /** - * Create a new section. - * - * @param string $name - * @param string $identifier - * - * @return \eZ\Publish\SPI\Persistence\Content\Section - */ - public function create($name, $identifier) - { - $section = new Section(); - - $section->name = $name; - $section->identifier = $identifier; - - $section->id = $this->sectionGateway->insertSection($name, $identifier); - - return $section; - } - - /** - * Update name and identifier of a section. - * - * @param mixed $id - * @param string $name - * @param string $identifier - * - * @return \eZ\Publish\SPI\Persistence\Content\Section - */ - public function update($id, $name, $identifier) - { - $this->sectionGateway->updateSection($id, $name, $identifier); - - $section = new Section(); - $section->id = $id; - $section->name = $name; - $section->identifier = $identifier; - - return $section; - } - - /** - * Get section data. - * - * @param mixed $id - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If section is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Section - */ - public function load($id) - { - $rows = $this->sectionGateway->loadSectionData($id); - - if (empty($rows)) { - throw new NotFound('Section', $id); - } - - return $this->createSectionFromArray(reset($rows)); - } - - /** - * Get all section data. - * - * @return \eZ\Publish\SPI\Persistence\Content\Section[] - */ - public function loadAll() - { - $rows = $this->sectionGateway->loadAllSectionData(); - - return $this->createSectionsFromArray($rows); - } - - /** - * Get section data by identifier. - * - * @param string $identifier - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If section is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Section - */ - public function loadByIdentifier($identifier) - { - $rows = $this->sectionGateway->loadSectionDataByIdentifier($identifier); - - if (empty($rows)) { - throw new NotFound('Section', $identifier); - } - - return $this->createSectionFromArray(reset($rows)); - } - - /** - * Creates a Section from the given $data. - * - * @param array $data - * - * @return \eZ\Publish\SPI\Persistence\Content\Section - */ - protected function createSectionFromArray(array $data) - { - $section = new Section(); - - $section->id = (int)$data['id']; - $section->name = $data['name']; - $section->identifier = $data['identifier']; - - return $section; - } - - /** - * Creates a Section from the given $data. - * - * @param array $data - * - * @return \eZ\Publish\SPI\Persistence\Content\Section[] - */ - protected function createSectionsFromArray(array $data) - { - $sections = []; - foreach ($data as $sectionData) { - $sections[] = $this->createSectionFromArray($sectionData); - } - - return $sections; - } - - /** - * Delete a section. - * - * Might throw an exception if the section is still associated with some - * content objects. Make sure that no content objects are associated with - * the section any more *before* calling this method. - * - * @param mixed $id - */ - public function delete($id) - { - $contentCount = $this->sectionGateway->countContentObjectsInSection($id); - - if ($contentCount > 0) { - throw new RuntimeException( - "Section with ID '{$id}' still has content assigned." - ); - } - $this->sectionGateway->deleteSection($id); - } - - /** - * Assigns section to single content object. - * - * @param mixed $sectionId - * @param mixed $contentId - */ - public function assign($sectionId, $contentId) - { - $this->sectionGateway->assignSectionToContent($sectionId, $contentId); - } - - /** - * Number of content assignments a Section has. - * - * @param mixed $sectionId - * - * @return int - */ - public function assignmentsCount($sectionId) - { - return $this->sectionGateway->countContentObjectsInSection($sectionId); - } - - /** - * Number of role policies using a Section in limitations. - * - * @param mixed $sectionId - * - * @return int - */ - public function policiesCount($sectionId) - { - return $this->sectionGateway->countPoliciesUsingSection($sectionId); - } - - /** - * Counts the number of role assignments using section with $sectionId in their limitations. - * - * @param int $sectionId - * - * @return int - */ - public function countRoleAssignmentsUsingSection($sectionId) - { - return $this->sectionGateway->countRoleAssignmentsUsingSection($sectionId); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/StorageHandler.php b/eZ/Publish/Core/Persistence/Legacy/Content/StorageHandler.php deleted file mode 100644 index 4101063fad..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/StorageHandler.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content; - -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Handler for external storages. - */ -class StorageHandler -{ - /** - * Storage registry. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry - */ - protected $storageRegistry; - - /** - * Array with database context. - * - * @var array - */ - protected $context; - - /** - * Creates a new storage handler. - * - * @param StorageRegistry $storageRegistry - * @param array $context - */ - public function __construct(StorageRegistry $storageRegistry, array $context) - { - $this->storageRegistry = $storageRegistry; - $this->context = $context; - } - - /** - * Stores data from $field in its corresponding external storage. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - */ - public function storeFieldData(VersionInfo $versionInfo, Field $field) - { - return $this->storageRegistry->getStorage($field->type)->storeFieldData( - $versionInfo, - $field, - $this->context - ); - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Field $originalField - */ - public function copyFieldData(VersionInfo $versionInfo, Field $field, Field $originalField) - { - return $this->storageRegistry->getStorage($field->type)->copyLegacyField( - $versionInfo, - $field, - $originalField, - $this->context - ); - } - - /** - * Fetches external data for $field from its corresponding external storage. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - */ - public function getFieldData(VersionInfo $versionInfo, Field $field) - { - $storage = $this->storageRegistry->getStorage($field->type); - if ($field->id !== null && $storage->hasFieldData()) { - $storage->getFieldData($versionInfo, $field, $this->context); - } - } - - /** - * Deletes data for field $ids from external storage of $fieldType. - * - * @param string $fieldType - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param mixed[] $ids - */ - public function deleteFieldData($fieldType, VersionInfo $versionInfo, array $ids) - { - $this->storageRegistry->getStorage($fieldType) - ->deleteFieldData($versionInfo, $ids, $this->context); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/StorageRegistry.php b/eZ/Publish/Core/Persistence/Legacy/Content/StorageRegistry.php deleted file mode 100644 index fb1d58e346..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/StorageRegistry.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content; - -use eZ\Publish\Core\FieldType\NullStorage; -use eZ\Publish\SPI\FieldType\FieldStorage; - -/** - * Registry for external storages. - */ -class StorageRegistry -{ - /** @var \eZ\Publish\SPI\FieldType\FieldStorage[] */ - protected $storageMap; - - /** - * @param array $storageMap A map where key is field type name, and value is - * a callable factory to get FieldStorage OR FieldStorage object - */ - public function __construct(array $storageMap = []) - { - $this->storageMap = $storageMap; - } - - public function register(string $typeName, FieldStorage $storage): void - { - $this->storageMap[$typeName] = $storage; - } - - public function getStorage(string $typeName): FieldStorage - { - if (!isset($this->storageMap[$typeName])) { - $this->storageMap[$typeName] = new NullStorage(); - } - - return $this->storageMap[$typeName]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Type/ContentUpdater.php b/eZ/Publish/Core/Persistence/Legacy/Content/Type/ContentUpdater.php deleted file mode 100644 index 806305aac5..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Type/ContentUpdater.php +++ /dev/null @@ -1,152 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Type; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry as Registry; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper as ContentMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -/** - * Class to update content objects to a new type version. - */ -class ContentUpdater -{ - /** - * Content gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway - */ - protected $contentGateway; - - /** - * FieldValue converter registry. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry - */ - protected $converterRegistry; - - /** - * Storage handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler - */ - protected $storageHandler; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Mapper */ - protected $contentMapper; - - /** - * Creates a new content updater. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Gateway $contentGateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry $converterRegistry - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler $storageHandler - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Mapper $contentMapper - */ - public function __construct( - ContentGateway $contentGateway, - Registry $converterRegistry, - StorageHandler $storageHandler, - ContentMapper $contentMapper - ) { - $this->contentGateway = $contentGateway; - $this->converterRegistry = $converterRegistry; - $this->storageHandler = $storageHandler; - $this->contentMapper = $contentMapper; - } - - /** - * Determines the necessary update actions. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type $fromType - * @param \eZ\Publish\SPI\Persistence\Content\Type $toType - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action[] - */ - public function determineActions(Type $fromType, Type $toType) - { - $actions = []; - foreach ($fromType->fieldDefinitions as $fieldDef) { - if (!$this->hasFieldDefinition($toType, $fieldDef)) { - $actions[] = new ContentUpdater\Action\RemoveField( - $this->contentGateway, - $fieldDef, - $this->storageHandler, - $this->contentMapper - ); - } - } - foreach ($toType->fieldDefinitions as $fieldDef) { - if (!$this->hasFieldDefinition($fromType, $fieldDef)) { - $actions[] = new ContentUpdater\Action\AddField( - $this->contentGateway, - $fieldDef, - $this->converterRegistry->getConverter( - $fieldDef->fieldType - ), - $this->storageHandler, - $this->contentMapper - ); - } - } - - return $actions; - } - - /** - * hasFieldDefinition. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type $type - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * - * @return bool - */ - protected function hasFieldDefinition(Type $type, FieldDefinition $fieldDef) - { - foreach ($type->fieldDefinitions as $existFieldDef) { - if ($existFieldDef->id == $fieldDef->id) { - return true; - } - } - - return false; - } - - /** - * Applies all given updates. - * - * @param mixed $contentTypeId - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action[] $actions - */ - public function applyUpdates($contentTypeId, array $actions) - { - if (empty($actions)) { - return; - } - - foreach ($this->getContentIdsByContentTypeId($contentTypeId) as $contentId) { - foreach ($actions as $action) { - $action->apply($contentId); - } - } - } - - /** - * Returns all content objects of $contentTypeId. - * - * @param mixed $contentTypeId - * - * @return int[] - */ - protected function getContentIdsByContentTypeId($contentTypeId) - { - return $this->contentGateway->getContentIdsByContentTypeId($contentTypeId); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Type/ContentUpdater/Action.php b/eZ/Publish/Core/Persistence/Legacy/Content/Type/ContentUpdater/Action.php deleted file mode 100644 index c23c8f4cbe..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Type/ContentUpdater/Action.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater; - -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; - -/** - * Updater action base class. - */ -abstract class Action -{ - /** - * Content gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway - */ - protected $contentGateway; - - /** - * Creates a new action. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Gateway $contentGateway - */ - public function __construct(ContentGateway $contentGateway) - { - $this->contentGateway = $contentGateway; - } - - /** - * Applies the action to the given $content. - * - * @param int $contentId - */ - abstract public function apply($contentId); -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveField.php b/eZ/Publish/Core/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveField.php deleted file mode 100644 index 06173c7d2a..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveField.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action; - -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper as ContentMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -/** - * Action to remove a field from content objects. - */ -class RemoveField extends Action -{ - /** - * Field definition of the field to remove. - * - * @var \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition - */ - protected $fieldDefinition; - - /** - * Storage handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler - */ - protected $storageHandler; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Mapper */ - protected $contentMapper; - - /** - * Creates a new action. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Gateway $contentGateway - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler $storageHandler - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Mapper $contentMapper - */ - public function __construct( - ContentGateway $contentGateway, - FieldDefinition $fieldDef, - StorageHandler $storageHandler, - ContentMapper $contentMapper - ) { - $this->contentGateway = $contentGateway; - $this->fieldDefinition = $fieldDef; - $this->storageHandler = $storageHandler; - $this->contentMapper = $contentMapper; - } - - /** - * Applies the action to the given $content. - * - * @param int $contentId - */ - public function apply($contentId) - { - $versionNumbers = $this->contentGateway->listVersionNumbers($contentId); - $fieldIdSet = []; - - $nameRows = $this->contentGateway->loadVersionedNameData( - array_map( - static function ($versionNo) use ($contentId) { - return ['id' => $contentId, 'version' => $versionNo]; - }, - $versionNumbers - ) - ); - - foreach ($versionNumbers as $versionNo) { - $contentRows = $this->contentGateway->load($contentId, $versionNo); - $contentList = $this->contentMapper->extractContentFromRows($contentRows, $nameRows); - $content = $contentList[0]; - $versionFieldIdSet = []; - - foreach ($content->fields as $field) { - if ($field->fieldDefinitionId == $this->fieldDefinition->id) { - $fieldIdSet[$field->id] = true; - $versionFieldIdSet[$field->id] = true; - } - } - - // Delete from external storage with list of IDs per version - $this->storageHandler->deleteFieldData( - $this->fieldDefinition->fieldType, - $content->versionInfo, - array_keys($versionFieldIdSet) - ); - } - - // Delete from relations storage - $this->contentGateway->removeRelationsByFieldDefinitionId($this->fieldDefinition->id); - - // Delete from internal storage -- field is always deleted from _all_ versions - foreach (array_keys($fieldIdSet) as $fieldId) { - $this->contentGateway->deleteField($fieldId); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/Content/Type/Gateway.php deleted file mode 100644 index 4cb976c1f0..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Gateway.php +++ /dev/null @@ -1,169 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\Type; - -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\Group; -use eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct as GroupUpdateStruct; - -/** - * Content Type Gateway. - * - * @internal For internal use by Persistence Handlers. - */ -abstract class Gateway -{ - public const CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE = 'ezcontentclass_classgroup'; - public const CONTENT_TYPE_GROUP_TABLE = 'ezcontentclassgroup'; - public const CONTENT_TYPE_TABLE = 'ezcontentclass'; - public const CONTENT_TYPE_NAME_TABLE = 'ezcontentclass_name'; - public const FIELD_DEFINITION_TABLE = 'ezcontentclass_attribute'; - public const MULTILINGUAL_FIELD_DEFINITION_TABLE = 'ezcontentclass_attribute_ml'; - - public const CONTENT_TYPE_GROUP_SEQ = 'ezcontentclassgroup_id_seq'; - public const CONTENT_TYPE_SEQ = 'ezcontentclass_id_seq'; - public const FIELD_DEFINITION_SEQ = 'ezcontentclass_attribute_id_seq'; - - abstract public function insertGroup(Group $group): int; - - abstract public function updateGroup(GroupUpdateStruct $group): void; - - abstract public function countTypesInGroup(int $groupId): int; - - abstract public function countGroupsForType(int $typeId, int $status): int; - - abstract public function deleteGroup(int $groupId): void; - - /** - * @param int[] $groupIds - */ - abstract public function loadGroupData(array $groupIds): array; - - abstract public function loadGroupDataByIdentifier(string $identifier): array; - - abstract public function loadAllGroupsData(): array; - - /** - * Load data for all Content Types of the given status, belonging to the given Group. - */ - abstract public function loadTypesDataForGroup(int $groupId, int $status): array; - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the given language does not exist - */ - abstract public function insertType(Type $type, ?int $typeId = null): int; - - /** - * Assign a Content Type of the given status (published, draft) to Content Type Group. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the given Group does not exist - */ - abstract public function insertGroupAssignment(int $groupId, int $typeId, int $status): void; - - /** - * Delete a Group assignments for Content Type of the given status (published, draft). - */ - abstract public function deleteGroupAssignment(int $groupId, int $typeId, int $status): void; - - /** - * @param int $id Field Definition ID - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - */ - abstract public function loadFieldDefinition(int $id, int $status): array; - - /** - * Insert a Field Definition into Content Type. - */ - abstract public function insertFieldDefinition( - int $typeId, - int $status, - FieldDefinition $fieldDefinition, - StorageFieldDefinition $storageFieldDef - ): int; - - abstract public function deleteFieldDefinition( - int $typeId, - int $status, - int $fieldDefinitionId - ): void; - - abstract public function updateFieldDefinition( - int $typeId, - int $status, - FieldDefinition $fieldDefinition, - StorageFieldDefinition $storageFieldDef - ): void; - - /** - * Update a Content Type based on the given SPI Persistence Type Value Object. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if at least one of the used languages does not exist - */ - abstract public function updateType(int $typeId, int $status, Type $type): void; - - /** - * Bulk-load an array with data about the given Content Types. - * - * @param int[] $typeIds - */ - abstract public function loadTypesListData(array $typeIds): array; - - abstract public function loadTypeData(int $typeId, int $status): array; - - abstract public function loadTypeDataByIdentifier(string $identifier, int $status): array; - - abstract public function loadTypeDataByRemoteId(string $remoteId, int $status): array; - - abstract public function countInstancesOfType(int $typeId): int; - - /** - * Permanently delete a Content Type of the given status. - */ - abstract public function delete(int $typeId, int $status): void; - - abstract public function deleteFieldDefinitionsForType(int $typeId, int $status): void; - - /** - * Delete a Content Type. - * - * Does not delete Field Definitions! - */ - abstract public function deleteType(int $typeId, int $status): void; - - abstract public function deleteGroupAssignmentsForType(int $typeId, int $status): void; - - /** - * Publish a Content Type including its Field Definitions. - */ - abstract public function publishTypeAndFields( - int $typeId, - int $sourceStatus, - int $targetStatus - ): void; - - abstract public function getSearchableFieldMapData(): array; - - /** - * Remove Field Definition data from multilingual table. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the given language does not exist - */ - abstract public function removeFieldDefinitionTranslation( - int $fieldDefinitionId, - string $languageCode, - int $status - ): void; - - /** - * Remove items created or modified by User. - */ - abstract public function removeByUserAndVersion(int $userId, int $version): void; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php deleted file mode 100644 index 564319926e..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,1454 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator; -use eZ\Publish\Core\Persistence\Legacy\Content\MultilingualStorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway; -use eZ\Publish\Core\Persistence\Legacy\SharedGateway\Gateway as SharedGateway; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\Group; -use eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct as GroupUpdateStruct; -use function sprintf; - -/** - * Content Type gateway implementation using the Doctrine database. - * - * @internal Gateway implementation is considered internal. Use Persistence Content Type Handler instead. - * - * @see \eZ\Publish\SPI\Persistence\Content\Type\Handler - */ -final class DoctrineDatabase extends Gateway -{ - /** - * Columns of database tables. - * - * @var array - */ - private $columns = [ - 'ezcontentclass' => [ - 'id', - 'always_available', - 'contentobject_name', - 'created', - 'creator_id', - 'modified', - 'modifier_id', - 'identifier', - 'initial_language_id', - 'is_container', - 'language_mask', - 'remote_id', - 'serialized_description_list', - 'serialized_name_list', - 'sort_field', - 'sort_order', - 'url_alias_name', - 'version', - ], - 'ezcontentclass_attribute' => [ - 'id', - 'can_translate', - 'category', - 'contentclass_id', - 'data_float1', - 'data_float2', - 'data_float3', - 'data_float4', - 'data_int1', - 'data_int2', - 'data_int3', - 'data_int4', - 'data_text1', - 'data_text2', - 'data_text3', - 'data_text4', - 'data_text5', - 'data_type_string', - 'identifier', - 'is_information_collector', - 'is_required', - 'is_searchable', - 'is_thumbnail', - 'placement', - 'serialized_data_text', - 'serialized_description_list', - 'serialized_name_list', - ], - ]; - - /** - * The native Doctrine connection. - * - * Meant to be used to transition from eZ/Zeta interface to Doctrine. - * - * @var \Doctrine\DBAL\Connection - */ - private $connection; - - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */ - private $dbPlatform; - - /** @var \eZ\Publish\Core\Persistence\Legacy\SharedGateway\Gateway */ - private $sharedGateway; - - /** - * Language mask generator. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator - */ - private $languageMaskGenerator; - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct( - Connection $connection, - SharedGateway $sharedGateway, - MaskGenerator $languageMaskGenerator - ) { - $this->connection = $connection; - $this->dbPlatform = $connection->getDatabasePlatform(); - $this->sharedGateway = $sharedGateway; - $this->languageMaskGenerator = $languageMaskGenerator; - } - - public function insertGroup(Group $group): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::CONTENT_TYPE_GROUP_TABLE) - ->values( - [ - 'created' => $query->createPositionalParameter( - $group->created, - ParameterType::INTEGER - ), - 'creator_id' => $query->createPositionalParameter( - $group->creatorId, - ParameterType::INTEGER - ), - 'modified' => $query->createPositionalParameter( - $group->modified, - ParameterType::INTEGER - ), - 'modifier_id' => $query->createPositionalParameter( - $group->modifierId, - ParameterType::INTEGER - ), - 'name' => $query->createPositionalParameter( - $group->identifier, - ParameterType::STRING - ), - ] - ); - $query->execute(); - - return (int)$this->connection->lastInsertId(self::CONTENT_TYPE_GROUP_SEQ); - } - - public function updateGroup(GroupUpdateStruct $group): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::CONTENT_TYPE_GROUP_TABLE) - ->set( - 'modified', - $query->createPositionalParameter($group->modified, ParameterType::INTEGER) - ) - ->set( - 'modifier_id', - $query->createPositionalParameter($group->modifierId, ParameterType::INTEGER) - ) - ->set( - 'name', - $query->createPositionalParameter($group->identifier, ParameterType::STRING) - )->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($group->id, ParameterType::INTEGER) - ) - ); - - $query->execute(); - } - - public function countTypesInGroup(int $groupId): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select($this->dbPlatform->getCountExpression('contentclass_id')) - ->from(self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE) - ->where( - $query->expr()->eq( - 'group_id', - $query->createPositionalParameter($groupId, ParameterType::INTEGER) - ) - ); - - return (int)$query->execute()->fetchColumn(); - } - - public function countGroupsForType(int $typeId, int $status): int - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select($this->dbPlatform->getCountExpression('group_id')) - ->from(self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE) - ->where( - $expr->eq( - 'contentclass_id', - $query->createPositionalParameter($typeId, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - 'contentclass_version', - $query->createPositionalParameter($status, ParameterType::INTEGER) - ) - ); - - return (int)$query->execute()->fetchColumn(); - } - - public function deleteGroup(int $groupId): void - { - $query = $this->connection->createQueryBuilder(); - $query->delete(self::CONTENT_TYPE_GROUP_TABLE) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($groupId, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - /** - * @param string[] $languages - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if at least one of the used languages does not exist - */ - private function insertTypeNameData(int $typeId, int $typeStatus, array $languages): void - { - $tmpLanguages = $languages; - if (isset($tmpLanguages['always-available'])) { - unset($tmpLanguages['always-available']); - } - - foreach ($tmpLanguages as $language => $name) { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::CONTENT_TYPE_NAME_TABLE) - ->values( - [ - 'contentclass_id' => $query->createPositionalParameter( - $typeId, - ParameterType::INTEGER - ), - 'contentclass_version' => $query->createPositionalParameter( - $typeStatus, - ParameterType::INTEGER - ), - 'language_id' => $query->createPositionalParameter( - $this->languageMaskGenerator->generateLanguageIndicator( - $language, - $this->languageMaskGenerator->isLanguageAlwaysAvailable( - $language, - $languages - ) - ), - ParameterType::INTEGER - ), - 'language_locale' => $query->createPositionalParameter( - $language, - ParameterType::STRING - ), - 'name' => $query->createPositionalParameter($name, ParameterType::STRING), - ] - ); - $query->execute(); - } - } - - private function setNextAutoIncrementedValueIfAvailable( - QueryBuilder $queryBuilder, - string $tableName, - string $idColumnName, - string $sequenceName, - ?int $defaultValue = null - ): void { - if (null === $defaultValue) { - // usually returns null to trigger default column value behavior - $defaultValue = $this->sharedGateway->getColumnNextIntegerValue( - $tableName, - $idColumnName, - $sequenceName - ); - } - // avoid trying to insert NULL to trigger default column value behavior - if (null !== $defaultValue) { - $queryBuilder->setValue( - $idColumnName, - $queryBuilder->createNamedParameter( - $defaultValue, - ParameterType::INTEGER, - ":{$idColumnName}" - ) - ); - } - } - - public function insertType(Type $type, ?int $typeId = null): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::CONTENT_TYPE_TABLE) - ->values( - [ - // Legacy Storage: "version" stores CT status (draft, published) - 'version' => $query->createNamedParameter( - $type->status, - ParameterType::INTEGER, - ':status' - ), - 'created' => $query->createNamedParameter( - $type->created, - ParameterType::INTEGER, - ':created' - ), - 'creator_id' => $query->createNamedParameter( - $type->creatorId, - ParameterType::INTEGER, - ':creator_id' - ), - ] - ); - $this->setNextAutoIncrementedValueIfAvailable( - $query, - self::CONTENT_TYPE_TABLE, - 'id', - self::CONTENT_TYPE_SEQ, - $typeId - ); - - $columnQueryValueAndTypeMap = $this->mapCommonContentTypeColumnsToQueryValuesAndTypes( - $type - ); - foreach ($columnQueryValueAndTypeMap as $columnName => $data) { - [$value, $parameterType] = $data; - $query - ->setValue( - $columnName, - $query->createNamedParameter($value, $parameterType, ":{$columnName}") - ); - } - - $query->setParameter('status', $type->status, ParameterType::INTEGER); - $query->setParameter('created', $type->created, ParameterType::INTEGER); - $query->setParameter('creator_id', $type->creatorId, ParameterType::INTEGER); - - $query->execute(); - - if (empty($typeId)) { - $typeId = $this->sharedGateway->getLastInsertedId(self::CONTENT_TYPE_SEQ); - } - - $this->insertTypeNameData($typeId, $type->status, $type->name); - - // $typeId passed as the argument could still be non-int - return (int)$typeId; - } - - /** - * Get a map of Content Type storage column name to its value and parameter type. - * - * Key value of the map is represented as a two-elements array with column value and its type. - */ - private function mapCommonContentTypeColumnsToQueryValuesAndTypes(Type $type): array - { - return [ - 'serialized_name_list' => [serialize($type->name), ParameterType::STRING], - 'serialized_description_list' => [serialize($type->description), ParameterType::STRING], - 'identifier' => [$type->identifier, ParameterType::STRING], - 'modified' => [$type->modified, ParameterType::INTEGER], - 'modifier_id' => [$type->modifierId, ParameterType::INTEGER], - 'remote_id' => [$type->remoteId, ParameterType::STRING], - 'url_alias_name' => [$type->urlAliasSchema, ParameterType::STRING], - 'contentobject_name' => [$type->nameSchema, ParameterType::STRING], - 'is_container' => [(int)$type->isContainer, ParameterType::INTEGER], - 'language_mask' => [ - $this->languageMaskGenerator->generateLanguageMaskFromLanguageCodes( - $type->languageCodes, - array_key_exists('always-available', $type->name) - ), - ParameterType::INTEGER, - ], - 'initial_language_id' => [$type->initialLanguageId, ParameterType::INTEGER], - 'sort_field' => [$type->sortField, ParameterType::INTEGER], - 'sort_order' => [$type->sortOrder, ParameterType::INTEGER], - 'always_available' => [(int)$type->defaultAlwaysAvailable, ParameterType::INTEGER], - ]; - } - - public function insertGroupAssignment(int $groupId, int $typeId, int $status): void - { - $groups = $this->loadGroupData([$groupId]); - if (empty($groups)) { - throw new NotFoundException('Content Type Group', $groupId); - } - $group = $groups[0]; - - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE) - ->values( - [ - 'contentclass_id' => $query->createPositionalParameter( - $typeId, - ParameterType::INTEGER - ), - 'contentclass_version' => $query->createPositionalParameter( - $status, - ParameterType::INTEGER - ), - 'group_id' => $query->createPositionalParameter( - $groupId, - ParameterType::INTEGER - ), - 'group_name' => $query->createPositionalParameter( - $group['name'], - ParameterType::STRING - ), - ] - ); - - $query->execute(); - } - - public function deleteGroupAssignment(int $groupId, int $typeId, int $status): void - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->delete(self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE) - ->where( - $expr->eq( - 'contentclass_id', - $query->createPositionalParameter($typeId, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - 'contentclass_version', - $query->createPositionalParameter($status, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - 'group_id', - $query->createPositionalParameter($groupId, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - public function loadGroupData(array $groupIds): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('created', 'creator_id', 'id', 'modified', 'modifier_id', 'name') - ->from(self::CONTENT_TYPE_GROUP_TABLE) - ->where($query->expr()->in('id', ':ids')) - ->setParameter('ids', $groupIds, Connection::PARAM_INT_ARRAY); - - return $query->execute()->fetchAll(); - } - - public function loadGroupDataByIdentifier(string $identifier): array - { - $query = $this->createGroupLoadQuery(); - $query->where( - $query->expr()->eq( - 'name', - $query->createPositionalParameter($identifier, ParameterType::STRING) - ) - ); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadAllGroupsData(): array - { - $query = $this->createGroupLoadQuery(); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - /** - * Create the basic query to load Group data. - */ - private function createGroupLoadQuery(): QueryBuilder - { - $query = $this->connection->createQueryBuilder(); - $query->select( - 'created', - 'creator_id', - 'id', - 'modified', - 'modifier_id', - 'name' - )->from(self::CONTENT_TYPE_GROUP_TABLE); - - return $query; - } - - public function loadTypesDataForGroup(int $groupId, int $status): array - { - $query = $this->getLoadTypeQueryBuilder(); - $expr = $query->expr(); - $query - ->where($expr->eq('g.group_id', ':gid')) - ->andWhere($expr->eq('c.version', ':version')) - ->addOrderBy('c.identifier') - ->setParameter('gid', $groupId, ParameterType::INTEGER) - ->setParameter('version', $status, ParameterType::INTEGER); - - return $query->execute()->fetchAll(); - } - - public function insertFieldDefinition( - int $typeId, - int $status, - FieldDefinition $fieldDefinition, - StorageFieldDefinition $storageFieldDef - ): int { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::FIELD_DEFINITION_TABLE) - ->values( - [ - 'contentclass_id' => $query->createNamedParameter( - $typeId, - ParameterType::INTEGER, - ':content_type_id' - ), - 'version' => $query->createNamedParameter( - $status, - ParameterType::INTEGER, - ':status' - ), - ] - ); - $this->setNextAutoIncrementedValueIfAvailable( - $query, - self::FIELD_DEFINITION_TABLE, - 'id', - self::FIELD_DEFINITION_SEQ, - $fieldDefinition->id - ); - $columnValueAndTypeMap = $this->mapCommonFieldDefinitionColumnsToQueryValuesAndTypes( - $fieldDefinition, - $storageFieldDef - ); - foreach ($columnValueAndTypeMap as $columnName => $data) { - [$columnValue, $parameterType] = $data; - $query - ->setValue($columnName, ":{$columnName}") - ->setParameter($columnName, $columnValue, $parameterType); - } - - $query->execute(); - - $fieldDefinitionId = $fieldDefinition->id ?? $this->sharedGateway->getLastInsertedId( - self::FIELD_DEFINITION_SEQ - ); - - foreach ($storageFieldDef->multilingualData as $multilingualData) { - $this->insertFieldDefinitionMultilingualData( - $fieldDefinitionId, - $multilingualData, - $status - ); - } - - return $fieldDefinitionId; - } - - private function insertFieldDefinitionMultilingualData( - int $fieldDefinitionId, - MultilingualStorageFieldDefinition $multilingualData, - int $status - ): void { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) - ->values( - [ - 'data_text' => ':data_text', - 'data_json' => ':data_json', - 'name' => ':name', - 'description' => ':description', - 'contentclass_attribute_id' => ':field_definition_id', - 'version' => ':status', - 'language_id' => ':language_id', - ] - ) - ->setParameter('data_text', $multilingualData->dataText) - ->setParameter('data_json', $multilingualData->dataJson) - ->setParameter('name', $multilingualData->name) - ->setParameter('description', $multilingualData->description) - ->setParameter('field_definition_id', $fieldDefinitionId, ParameterType::INTEGER) - ->setParameter('status', $status, ParameterType::INTEGER) - ->setParameter('language_id', $multilingualData->languageId, ParameterType::INTEGER); - - $query->execute(); - } - - /** - * Get a map of Field Definition storage column name to its value and parameter type. - * - * Key value of the map is represented as a two-elements array with column value and its type. - */ - private function mapCommonFieldDefinitionColumnsToQueryValuesAndTypes( - FieldDefinition $fieldDefinition, - StorageFieldDefinition $storageFieldDef - ): array { - return [ - 'serialized_name_list' => [serialize($fieldDefinition->name), ParameterType::STRING], - 'serialized_description_list' => [ - serialize($fieldDefinition->description), - ParameterType::STRING, - ], - 'serialized_data_text' => [ - serialize($storageFieldDef->serializedDataText), - ParameterType::STRING, - ], - 'identifier' => [$fieldDefinition->identifier, ParameterType::STRING], - 'category' => [$fieldDefinition->fieldGroup, ParameterType::STRING], - 'placement' => [$fieldDefinition->position, ParameterType::INTEGER], - 'data_type_string' => [$fieldDefinition->fieldType, ParameterType::STRING], - 'can_translate' => [(int)$fieldDefinition->isTranslatable, ParameterType::INTEGER], - 'is_thumbnail' => [(bool)$fieldDefinition->isThumbnail, ParameterType::INTEGER], - 'is_required' => [(int)$fieldDefinition->isRequired, ParameterType::INTEGER], - 'is_information_collector' => [ - (int)$fieldDefinition->isInfoCollector, - ParameterType::INTEGER, - ], - 'is_searchable' => [(int)$fieldDefinition->isSearchable, ParameterType::INTEGER], - 'data_float1' => [$storageFieldDef->dataFloat1, null], - 'data_float2' => [$storageFieldDef->dataFloat2, null], - 'data_float3' => [$storageFieldDef->dataFloat3, null], - 'data_float4' => [$storageFieldDef->dataFloat4, null], - 'data_int1' => [$storageFieldDef->dataInt1, ParameterType::INTEGER], - 'data_int2' => [$storageFieldDef->dataInt2, ParameterType::INTEGER], - 'data_int3' => [$storageFieldDef->dataInt3, ParameterType::INTEGER], - 'data_int4' => [$storageFieldDef->dataInt4, ParameterType::INTEGER], - 'data_text1' => [$storageFieldDef->dataText1, ParameterType::STRING], - 'data_text2' => [$storageFieldDef->dataText2, ParameterType::STRING], - 'data_text3' => [$storageFieldDef->dataText3, ParameterType::STRING], - 'data_text4' => [$storageFieldDef->dataText4, ParameterType::STRING], - 'data_text5' => [$storageFieldDef->dataText5, ParameterType::STRING], - ]; - } - - public function loadFieldDefinition(int $id, int $status): array - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $this - ->selectColumns($query, self::FIELD_DEFINITION_TABLE, 'f_def') - ->addSelect( - [ - 'ct.initial_language_id AS ezcontentclass_initial_language_id', - 'transl_f_def.name AS ezcontentclass_attribute_multilingual_name', - 'transl_f_def.description AS ezcontentclass_attribute_multilingual_description', - 'transl_f_def.language_id AS ezcontentclass_attribute_multilingual_language_id', - 'transl_f_def.data_text AS ezcontentclass_attribute_multilingual_data_text', - 'transl_f_def.data_json AS ezcontentclass_attribute_multilingual_data_json', - ] - ) - ->from(self::FIELD_DEFINITION_TABLE, 'f_def') - ->leftJoin( - 'f_def', - self::CONTENT_TYPE_TABLE, - 'ct', - $expr->andX( - $expr->eq('f_def.contentclass_id', 'ct.id'), - $expr->eq('f_def.version', 'ct.version') - ) - ) - ->leftJoin( - 'f_def', - self::MULTILINGUAL_FIELD_DEFINITION_TABLE, - 'transl_f_def', - $expr->andX( - $expr->eq( - 'f_def.id', - 'transl_f_def.contentclass_attribute_id' - ), - $expr->eq( - 'f_def.version', - 'transl_f_def.version' - ) - ) - ) - ->where( - $expr->eq( - 'f_def.id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - 'f_def.version', - $query->createPositionalParameter($status, ParameterType::INTEGER) - ) - ); - - $stmt = $query->execute(); - - return $stmt->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function deleteFieldDefinition( - int $typeId, - int $status, - int $fieldDefinitionId - ): void { - // Delete multilingual data first to keep DB integrity - $deleteQuery = $this->connection->createQueryBuilder(); - $deleteQuery - ->delete(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) - ->where('contentclass_attribute_id = :field_definition_id') - ->andWhere('version = :status') - ->setParameter('field_definition_id', $fieldDefinitionId, ParameterType::INTEGER) - ->setParameter('status', $status, ParameterType::INTEGER); - - $deleteQuery->execute(); - - // Delete legacy Field Definition data - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::FIELD_DEFINITION_TABLE) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($fieldDefinitionId, ParameterType::INTEGER) - ) - ) - // in Legacy Storage Field Definition table the "version" column stores status (e.g. draft, published, modified) - ->andWhere( - $query->expr()->eq( - 'version', - $query->createPositionalParameter($status, ParameterType::INTEGER) - ) - ) - ; - - $query->execute(); - } - - public function updateFieldDefinition( - int $typeId, - int $status, - FieldDefinition $fieldDefinition, - StorageFieldDefinition $storageFieldDef - ): void { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::FIELD_DEFINITION_TABLE) - ->where('id = :field_definition_id') - ->andWhere('version = :status') - ->setParameter('field_definition_id', $fieldDefinition->id, ParameterType::INTEGER) - ->setParameter('status', $status, ParameterType::INTEGER); - - $fieldDefinitionValueAndTypeMap = $this->mapCommonFieldDefinitionColumnsToQueryValuesAndTypes( - $fieldDefinition, - $storageFieldDef - ); - foreach ($fieldDefinitionValueAndTypeMap as $columnName => $data) { - [$value, $parameterType] = $data; - $query - ->set( - $columnName, - $query->createNamedParameter($value, $parameterType, ":{$columnName}") - ); - } - - $query->execute(); - - foreach ($storageFieldDef->multilingualData as $data) { - $dataExists = $this->fieldDefinitionMultilingualDataExist( - $fieldDefinition, - $data->languageId, - $status - ); - - if ($dataExists) { - $this->updateFieldDefinitionMultilingualData( - $fieldDefinition->id, - $data, - $status - ); - } else { - //When creating new translation there are no fields for update. - $this->insertFieldDefinitionMultilingualData( - $fieldDefinition->id, - $data, - $status - ); - } - } - } - - private function fieldDefinitionMultilingualDataExist( - FieldDefinition $fieldDefinition, - int $languageId, - int $status - ): bool { - $existQuery = $this->connection->createQueryBuilder(); - $existQuery - ->select($this->dbPlatform->getCountExpression('1')) - ->from(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) - ->where('contentclass_attribute_id = :field_definition_id') - ->andWhere('version = :status') - ->andWhere('language_id = :language_id') - ->setParameter('field_definition_id', $fieldDefinition->id, ParameterType::INTEGER) - ->setParameter('status', $status, ParameterType::INTEGER) - ->setParameter('language_id', $languageId, ParameterType::INTEGER); - - return 0 < (int)$existQuery->execute()->fetchColumn(); - } - - private function updateFieldDefinitionMultilingualData( - int $fieldDefinitionId, - MultilingualStorageFieldDefinition $multilingualData, - int $status - ): void { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) - ->set('data_text', ':data_text') - ->set('data_json', ':data_json') - ->set('name', ':name') - ->set('description', ':description') - ->where('contentclass_attribute_id = :field_definition_id') - ->andWhere('version = :status') - ->andWhere('language_id = :languageId') - ->setParameter('data_text', $multilingualData->dataText) - ->setParameter('data_json', $multilingualData->dataJson) - ->setParameter('name', $multilingualData->name) - ->setParameter('description', $multilingualData->description) - ->setParameter('field_definition_id', $fieldDefinitionId, ParameterType::INTEGER) - ->setParameter('status', $status, ParameterType::INTEGER) - ->setParameter('languageId', $multilingualData->languageId, ParameterType::INTEGER); - - $query->execute(); - } - - /** - * Delete entire name data for the given Content Type of the given status. - */ - private function deleteTypeNameData(int $typeId, int $typeStatus): void - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->delete(self::CONTENT_TYPE_NAME_TABLE) - ->where( - $expr->eq( - 'contentclass_id', - $query->createPositionalParameter($typeId, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - 'contentclass_version', - $query->createPositionalParameter($typeStatus, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - public function updateType(int $typeId, int $status, Type $type): void - { - $query = $this->connection->createQueryBuilder(); - $query->update(self::CONTENT_TYPE_TABLE); - - $columnQueryValueAndTypeMap = $this->mapCommonContentTypeColumnsToQueryValuesAndTypes( - $type - ); - foreach ($columnQueryValueAndTypeMap as $columnName => $data) { - [$value, $parameterType] = $data; - $query - ->set( - $columnName, - $query->createNamedParameter($value, $parameterType, ":{$columnName}") - ); - } - $expr = $query->expr(); - $query - ->where( - $expr->eq( - 'id', - $query->createNamedParameter($typeId, ParameterType::INTEGER, ':id') - ) - ) - ->andWhere( - $expr->eq( - 'version', - $query->createNamedParameter($status, ParameterType::INTEGER, ':status') - ) - ); - - $query->execute(); - - $this->deleteTypeNameData($typeId, $status); - $this->insertTypeNameData($typeId, $status, $type->name); - } - - public function loadTypesListData(array $typeIds): array - { - $query = $this->getLoadTypeQueryBuilder(); - - $query - ->where($query->expr()->in('c.id', ':ids')) - ->andWhere($query->expr()->eq('c.version', Type::STATUS_DEFINED)) - ->setParameter('ids', $typeIds, Connection::PARAM_INT_ARRAY); - - return $query->execute()->fetchAll(); - } - - public function loadTypeData(int $typeId, int $status): array - { - $query = $this->getLoadTypeQueryBuilder(); - $expr = $query->expr(); - $query - ->where($expr->eq('c.id', ':id')) - ->andWhere($expr->eq('c.version', ':version')) - ->setParameter('id', $typeId, ParameterType::INTEGER) - ->setParameter('version', $status, ParameterType::INTEGER); - - return $query->execute()->fetchAll(); - } - - public function loadTypeDataByIdentifier(string $identifier, int $status): array - { - $query = $this->getLoadTypeQueryBuilder(); - $expr = $query->expr(); - $query - ->where($expr->eq('c.identifier', ':identifier')) - ->andWhere($expr->eq('c.version', ':version')) - ->setParameter('identifier', $identifier, ParameterType::STRING) - ->setParameter('version', $status, ParameterType::INTEGER); - - return $query->execute()->fetchAll(); - } - - public function loadTypeDataByRemoteId(string $remoteId, int $status): array - { - $query = $this->getLoadTypeQueryBuilder(); - $query - ->where($query->expr()->eq('c.remote_id', ':remote')) - ->andWhere($query->expr()->eq('c.version', ':version')) - ->setParameter('remote', $remoteId, ParameterType::STRING) - ->setParameter('version', $status, ParameterType::INTEGER); - - return $query->execute()->fetchAll(); - } - - /** - * Return a basic query to retrieve Type data. - */ - private function getLoadTypeQueryBuilder(): QueryBuilder - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select( - [ - 'c.id AS ezcontentclass_id', - 'c.version AS ezcontentclass_version', - 'c.serialized_name_list AS ezcontentclass_serialized_name_list', - 'c.serialized_description_list AS ezcontentclass_serialized_description_list', - 'c.identifier AS ezcontentclass_identifier', - 'c.created AS ezcontentclass_created', - 'c.modified AS ezcontentclass_modified', - 'c.modifier_id AS ezcontentclass_modifier_id', - 'c.creator_id AS ezcontentclass_creator_id', - 'c.remote_id AS ezcontentclass_remote_id', - 'c.url_alias_name AS ezcontentclass_url_alias_name', - 'c.contentobject_name AS ezcontentclass_contentobject_name', - 'c.is_container AS ezcontentclass_is_container', - 'c.initial_language_id AS ezcontentclass_initial_language_id', - 'c.always_available AS ezcontentclass_always_available', - 'c.sort_field AS ezcontentclass_sort_field', - 'c.sort_order AS ezcontentclass_sort_order', - 'c.language_mask AS ezcontentclass_language_mask', - - 'a.id AS ezcontentclass_attribute_id', - 'a.serialized_name_list AS ezcontentclass_attribute_serialized_name_list', - 'a.serialized_description_list AS ezcontentclass_attribute_serialized_description_list', - 'a.identifier AS ezcontentclass_attribute_identifier', - 'a.category AS ezcontentclass_attribute_category', - 'a.data_type_string AS ezcontentclass_attribute_data_type_string', - 'a.can_translate AS ezcontentclass_attribute_can_translate', - 'a.is_required AS ezcontentclass_attribute_is_required', - 'a.is_information_collector AS ezcontentclass_attribute_is_information_collector', - 'a.is_searchable AS ezcontentclass_attribute_is_searchable', - 'a.is_thumbnail AS ezcontentclass_attribute_is_thumbnail', - 'a.placement AS ezcontentclass_attribute_placement', - 'a.data_float1 AS ezcontentclass_attribute_data_float1', - 'a.data_float2 AS ezcontentclass_attribute_data_float2', - 'a.data_float3 AS ezcontentclass_attribute_data_float3', - 'a.data_float4 AS ezcontentclass_attribute_data_float4', - 'a.data_int1 AS ezcontentclass_attribute_data_int1', - 'a.data_int2 AS ezcontentclass_attribute_data_int2', - 'a.data_int3 AS ezcontentclass_attribute_data_int3', - 'a.data_int4 AS ezcontentclass_attribute_data_int4', - 'a.data_text1 AS ezcontentclass_attribute_data_text1', - 'a.data_text2 AS ezcontentclass_attribute_data_text2', - 'a.data_text3 AS ezcontentclass_attribute_data_text3', - 'a.data_text4 AS ezcontentclass_attribute_data_text4', - 'a.data_text5 AS ezcontentclass_attribute_data_text5', - 'a.serialized_data_text AS ezcontentclass_attribute_serialized_data_text', - - 'g.group_id AS ezcontentclass_classgroup_group_id', - - 'ml.name AS ezcontentclass_attribute_multilingual_name', - 'ml.description AS ezcontentclass_attribute_multilingual_description', - 'ml.language_id AS ezcontentclass_attribute_multilingual_language_id', - 'ml.data_text AS ezcontentclass_attribute_multilingual_data_text', - 'ml.data_json AS ezcontentclass_attribute_multilingual_data_json', - ] - ) - ->from(self::CONTENT_TYPE_TABLE, 'c') - ->leftJoin( - 'c', - self::FIELD_DEFINITION_TABLE, - 'a', - $expr->andX( - $expr->eq('c.id', 'a.contentclass_id'), - $expr->eq('c.version', 'a.version') - ) - ) - ->leftJoin( - 'c', - self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE, - 'g', - $expr->andX( - $expr->eq('c.id', 'g.contentclass_id'), - $expr->eq('c.version', 'g.contentclass_version') - ) - ) - ->leftJoin( - 'a', - self::MULTILINGUAL_FIELD_DEFINITION_TABLE, - 'ml', - $expr->andX( - $expr->eq('a.id', 'ml.contentclass_attribute_id'), - $expr->eq('a.version', 'ml.version') - ) - ) - ->orderBy('a.placement'); - - return $query; - } - - public function countInstancesOfType(int $typeId): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select($this->dbPlatform->getCountExpression('id')) - ->from('ezcontentobject') - ->where( - $query->expr()->eq( - 'contentclass_id', - $query->createPositionalParameter($typeId, ParameterType::INTEGER) - ) - ); - - $stmt = $query->execute(); - - return (int)$stmt->fetchColumn(); - } - - public function deleteFieldDefinitionsForType(int $typeId, int $status): void - { - $subQuery = $this->connection->createQueryBuilder(); - $subQuery - ->select('f_def.id as ezcontentclass_attribute_id') - ->from(self::FIELD_DEFINITION_TABLE, 'f_def') - ->where('f_def.contentclass_id = :content_type_id') - ->andWhere('f_def.id = ezcontentclass_attribute_ml.contentclass_attribute_id'); - - $deleteQuery = $this->connection->createQueryBuilder(); - $deleteQuery - ->delete(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) - ->where( - sprintf('EXISTS (%s)', $subQuery->getSQL()) - ) - // note: not all drivers support aliasing tables in DELETE query, hence the following: - ->andWhere(sprintf('%s.version = :status', self::MULTILINGUAL_FIELD_DEFINITION_TABLE)) - ->setParameter('content_type_id', $typeId, ParameterType::INTEGER) - ->setParameter('status', $status, ParameterType::INTEGER); - - $deleteQuery->execute(); - - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->delete(self::FIELD_DEFINITION_TABLE) - ->where( - $query->expr()->eq( - 'contentclass_id', - $query->createPositionalParameter($typeId, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - 'version', - $query->createPositionalParameter($status, ParameterType::INTEGER) - ) - ); - - $query->execute(); - } - - public function delete(int $typeId, int $status): void - { - $this->deleteGroupAssignmentsForType($typeId, $status); - $this->deleteFieldDefinitionsForType($typeId, $status); - $this->deleteTypeNameData($typeId, $status); - $this->deleteType($typeId, $status); - } - - public function deleteType(int $typeId, int $status): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::CONTENT_TYPE_TABLE) - ->where( - $query->expr()->andX( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($typeId, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'version', - $query->createPositionalParameter($status, ParameterType::INTEGER) - ) - ) - ); - $query->execute(); - } - - public function deleteGroupAssignmentsForType(int $typeId, int $status): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE) - ->where( - $query->expr()->eq( - 'contentclass_id', - $query->createPositionalParameter($typeId, ParameterType::INTEGER) - ) - )->andWhere( - $query->expr()->eq( - 'contentclass_version', - $query->createPositionalParameter($status, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - /** - * Append all columns of a given table to the SELECT part of a query. - * - * Each column is aliased in the form of - * <code><column_name> AS <table_name>_<column_name></code>. - */ - private function selectColumns( - QueryBuilder $queryBuilder, - string $tableName, - string $tableAlias = '' - ): QueryBuilder { - if (empty($tableAlias)) { - $tableAlias = $tableName; - } - $queryBuilder - ->addSelect( - array_map( - function (string $columnName) use ($tableName, $tableAlias): string { - return sprintf( - '%s.%s as %s_%s', - $tableAlias, - $this->connection->quoteIdentifier($columnName), - $tableName, - $columnName - ); - }, - $this->columns[$tableName] - ) - ); - - return $queryBuilder; - } - - public function internalChangeContentTypeStatus( - int $typeId, - int $sourceStatus, - int $targetStatus, - string $tableName, - string $typeIdColumnName, - string $statusColumnName - ): void { - $query = $this->connection->createQueryBuilder(); - $query - ->update($tableName) - ->set( - $statusColumnName, - $query->createPositionalParameter($targetStatus, ParameterType::INTEGER) - ) - ->where( - $query->expr()->eq( - $typeIdColumnName, - $query->createPositionalParameter($typeId, ParameterType::INTEGER) - ) - )->andWhere( - $query->expr()->eq( - $statusColumnName, - $query->createPositionalParameter($sourceStatus, ParameterType::INTEGER) - ) - ); - - $query->execute(); - } - - public function publishTypeAndFields(int $typeId, int $sourceStatus, int $targetStatus): void - { - $this->internalChangeContentTypeStatus( - $typeId, - $sourceStatus, - $targetStatus, - self::CONTENT_TYPE_TABLE, - 'id', - 'version' - ); - - $this->internalChangeContentTypeStatus( - $typeId, - $sourceStatus, - $targetStatus, - self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE, - 'contentclass_id', - 'contentclass_version' - ); - - $this->internalChangeContentTypeStatus( - $typeId, - $sourceStatus, - $targetStatus, - self::FIELD_DEFINITION_TABLE, - 'contentclass_id', - 'version' - ); - - $this->internalChangeContentTypeStatus( - $typeId, - $sourceStatus, - $targetStatus, - self::CONTENT_TYPE_NAME_TABLE, - 'contentclass_id', - 'contentclass_version' - ); - - $subQuery = $this->connection->createQueryBuilder(); - $subQuery - ->select('f_def.id as ezcontentclass_attribute_id') - ->from(self::FIELD_DEFINITION_TABLE, 'f_def') - ->where('f_def.contentclass_id = :type_id') - ->andWhere('f_def.id = ezcontentclass_attribute_ml.contentclass_attribute_id'); - - $mlDataPublishQuery = $this->connection->createQueryBuilder(); - $mlDataPublishQuery - ->update(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) - ->set('version', ':target_status') - ->where( - sprintf('EXISTS (%s)', $subQuery->getSQL()) - ) - // note: not all drivers support aliasing tables in UPDATE query, hence the following: - ->andWhere( - sprintf('%s.version = :source_status', self::MULTILINGUAL_FIELD_DEFINITION_TABLE) - ) - ->setParameter('type_id', $typeId, ParameterType::INTEGER) - ->setParameter('target_status', $targetStatus, ParameterType::INTEGER) - ->setParameter('source_status', $sourceStatus, ParameterType::INTEGER); - - $mlDataPublishQuery->execute(); - } - - public function getSearchableFieldMapData(): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - 'f_def.identifier AS field_definition_identifier', - 'ct.identifier AS content_type_identifier', - 'f_def.id AS field_definition_id', - 'f_def.data_type_string AS field_type_identifier' - ) - ->from(self::FIELD_DEFINITION_TABLE, 'f_def') - ->innerJoin('f_def', self::CONTENT_TYPE_TABLE, 'ct', 'f_def.contentclass_id = ct.id') - ->where( - $query->expr()->eq( - 'f_def.is_searchable', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ) - ); - - $statement = $query->execute($query); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function removeFieldDefinitionTranslation( - int $fieldDefinitionId, - string $languageCode, - int $status - ): void { - $languageId = $this->languageMaskGenerator->generateLanguageMaskFromLanguageCodes( - [$languageCode] - ); - - $deleteQuery = $this->connection->createQueryBuilder(); - $deleteQuery - ->delete(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) - ->where('contentclass_attribute_id = :field_definition_id') - ->andWhere('version = :status') - ->andWhere('language_id = :language_id') - ->setParameter('field_definition_id', $fieldDefinitionId, ParameterType::INTEGER) - ->setParameter('status', $status, ParameterType::INTEGER) - ->setParameter('language_id', $languageId, ParameterType::INTEGER); - - $deleteQuery->execute(); - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function removeByUserAndVersion(int $userId, int $version): void - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder->delete(self::CONTENT_TYPE_TABLE) - ->where('creator_id = :user or modifier_id = :user') - ->andWhere('version = :version') - ->setParameter('user', $userId, ParameterType::INTEGER) - ->setParameter('version', $version, ParameterType::INTEGER) - ; - - try { - $this->connection->beginTransaction(); - - $queryBuilder->execute(); - $this->cleanupAssociations(); - - $this->connection->commit(); - } catch (DBALException $e) { - $this->connection->rollBack(); - - throw $e; - } - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - private function cleanupAssociations(): void - { - $this->cleanupClassAttributeTable(); - $this->cleanupClassAttributeMLTable(); - $this->cleanupClassGroupTable(); - $this->cleanupClassNameTable(); - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - private function cleanupClassAttributeTable(): void - { - $sql = <<<SQL - DELETE FROM ezcontentclass_attribute - WHERE NOT EXISTS ( - SELECT 1 FROM ezcontentclass - WHERE ezcontentclass.id = ezcontentclass_attribute.contentclass_id - AND ezcontentclass.version = ezcontentclass_attribute.version - ) -SQL; - $this->connection->executeUpdate($sql); - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - private function cleanupClassAttributeMLTable(): void - { - $sql = <<<SQL - DELETE FROM ezcontentclass_attribute_ml - WHERE NOT EXISTS ( - SELECT 1 FROM ezcontentclass_attribute - WHERE ezcontentclass_attribute.id = ezcontentclass_attribute_ml.contentclass_attribute_id - AND ezcontentclass_attribute.version = ezcontentclass_attribute_ml.version - ) -SQL; - $this->connection->executeUpdate($sql); - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - private function cleanupClassGroupTable(): void - { - $sql = <<<SQL - DELETE FROM ezcontentclass_classgroup - WHERE NOT EXISTS ( - SELECT 1 FROM ezcontentclass - WHERE ezcontentclass.id = ezcontentclass_classgroup.contentclass_id - AND ezcontentclass.version = ezcontentclass_classgroup.contentclass_version - ) -SQL; - $this->connection->executeUpdate($sql); - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - private function cleanupClassNameTable(): void - { - $sql = <<< SQL - DELETE FROM ezcontentclass_name - WHERE NOT EXISTS ( - SELECT 1 FROM ezcontentclass - WHERE ezcontentclass.id = ezcontentclass_name.contentclass_id - AND ezcontentclass.version = ezcontentclass_name.contentclass_version - ) -SQL; - $this->connection->executeUpdate($sql); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php deleted file mode 100644 index b588febfad..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,335 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\Group; -use eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct as GroupUpdateStruct; -use PDOException; - -/** - * @internal Internal exception conversion layer. - */ -final class ExceptionConversion extends Gateway -{ - /** - * The wrapped gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway - */ - private $innerGateway; - - /** - * Create a new exception conversion gateway around $innerGateway. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function insertGroup(Group $group): int - { - try { - return $this->innerGateway->insertGroup($group); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateGroup(GroupUpdateStruct $group): void - { - try { - $this->innerGateway->updateGroup($group); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countTypesInGroup(int $groupId): int - { - try { - return $this->innerGateway->countTypesInGroup($groupId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countGroupsForType(int $typeId, int $status): int - { - try { - return $this->innerGateway->countGroupsForType($typeId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteGroup(int $groupId): void - { - try { - $this->innerGateway->deleteGroup($groupId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadGroupData(array $groupIds): array - { - try { - return $this->innerGateway->loadGroupData($groupIds); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadGroupDataByIdentifier(string $identifier): array - { - try { - return $this->innerGateway->loadGroupDataByIdentifier($identifier); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadAllGroupsData(): array - { - try { - return $this->innerGateway->loadAllGroupsData(); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadTypesDataForGroup(int $groupId, int $status): array - { - try { - return $this->innerGateway->loadTypesDataForGroup($groupId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function insertType(Type $type, ?int $typeId = null): int - { - try { - return $this->innerGateway->insertType($type, $typeId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function insertGroupAssignment(int $groupId, int $typeId, int $status): void - { - try { - $this->innerGateway->insertGroupAssignment($groupId, $typeId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteGroupAssignment(int $groupId, int $typeId, int $status): void - { - try { - $this->innerGateway->deleteGroupAssignment($groupId, $typeId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadFieldDefinition(int $id, int $status): array - { - try { - return $this->innerGateway->loadFieldDefinition($id, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function insertFieldDefinition( - int $typeId, - int $status, - FieldDefinition $fieldDefinition, - StorageFieldDefinition $storageFieldDef - ): int { - try { - return $this->innerGateway->insertFieldDefinition( - $typeId, - $status, - $fieldDefinition, - $storageFieldDef - ); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteFieldDefinition( - int $typeId, - int $status, - int $fieldDefinitionId - ): void { - try { - $this->innerGateway->deleteFieldDefinition($typeId, $status, $fieldDefinitionId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateFieldDefinition( - int $typeId, - int $status, - FieldDefinition $fieldDefinition, - StorageFieldDefinition $storageFieldDef - ): void { - try { - $this->innerGateway->updateFieldDefinition($typeId, $status, $fieldDefinition, $storageFieldDef); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateType(int $typeId, int $status, Type $type): void - { - try { - $this->innerGateway->updateType($typeId, $status, $type); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadTypesListData(array $typeIds): array - { - try { - return $this->innerGateway->loadTypesListData($typeIds); - } catch (PDOException | DBALException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadTypeData(int $typeId, int $status): array - { - try { - return $this->innerGateway->loadTypeData($typeId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadTypeDataByIdentifier(string $identifier, int $status): array - { - try { - return $this->innerGateway->loadTypeDataByIdentifier($identifier, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadTypeDataByRemoteId(string $remoteId, int $status): array - { - try { - return $this->innerGateway->loadTypeDataByRemoteId($remoteId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countInstancesOfType(int $typeId): int - { - try { - return $this->innerGateway->countInstancesOfType($typeId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function delete(int $typeId, int $status): void - { - try { - $this->innerGateway->delete($typeId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteFieldDefinitionsForType(int $typeId, int $status): void - { - try { - $this->innerGateway->deleteFieldDefinitionsForType($typeId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteType(int $typeId, int $status): void - { - try { - $this->innerGateway->deleteType($typeId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteGroupAssignmentsForType(int $typeId, int $status): void - { - try { - $this->innerGateway->deleteGroupAssignmentsForType($typeId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function publishTypeAndFields(int $typeId, int $sourceStatus, int $targetStatus): void - { - try { - $this->innerGateway->publishTypeAndFields($typeId, $sourceStatus, $targetStatus); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getSearchableFieldMapData(): array - { - try { - return $this->innerGateway->getSearchableFieldMapData(); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function removeFieldDefinitionTranslation( - int $fieldDefinitionId, - string $languageCode, - int $status - ): void { - try { - $this->innerGateway->removeFieldDefinitionTranslation( - $fieldDefinitionId, - $languageCode, - $status - ); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function removeByUserAndVersion(int $userId, int $version): void - { - try { - $this->innerGateway->removeByUserAndVersion($userId, $version); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Content/Type/Handler.php deleted file mode 100644 index 54068a5393..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Handler.php +++ /dev/null @@ -1,667 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Type; - -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler as UpdateHandler; -use eZ\Publish\Core\Persistence\Legacy\Exception; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\Group; -use eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct as GroupCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct as GroupUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as BaseContentTypeHandler; -use eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct; - -class Handler implements BaseContentTypeHandler -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway */ - protected $contentTypeGateway; - - /** - * Mapper for Type objects. - * - * @var Mapper - */ - protected $mapper; - - /** - * Content Type update handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler - */ - protected $updateHandler; - - /** - * Creates a new content type handler. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway $contentTypeGateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper $mapper - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler $updateHandler - */ - public function __construct( - Gateway $contentTypeGateway, - Mapper $mapper, - UpdateHandler $updateHandler - ) { - $this->contentTypeGateway = $contentTypeGateway; - $this->mapper = $mapper; - $this->updateHandler = $updateHandler; - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct $createStruct - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group - */ - public function createGroup(GroupCreateStruct $createStruct) - { - $group = $this->mapper->createGroupFromCreateStruct( - $createStruct - ); - - $group->id = $this->contentTypeGateway->insertGroup( - $group - ); - - return $group; - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct $struct - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group - */ - public function updateGroup(GroupUpdateStruct $struct) - { - $this->contentTypeGateway->updateGroup( - $struct - ); - - return $this->loadGroup($struct->id); - } - - /** - * @param mixed $groupId - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If type group contains types - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If type group with id is not found - */ - public function deleteGroup($groupId) - { - if ($this->contentTypeGateway->countTypesInGroup($groupId) !== 0) { - throw new Exception\GroupNotEmpty($groupId); - } - $this->contentTypeGateway->deleteGroup($groupId); - } - - /** - * @param mixed $groupId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If type group with $groupId is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group - */ - public function loadGroup($groupId) - { - $groups = $this->mapper->extractGroupsFromRows( - $this->contentTypeGateway->loadGroupData([$groupId]) - ); - - if (count($groups) !== 1) { - throw new Exception\TypeGroupNotFound($groupId); - } - - return $groups[0]; - } - - /** - * {@inheritdoc} - */ - public function loadGroups(array $groupIds) - { - $groups = $this->mapper->extractGroupsFromRows( - $this->contentTypeGateway->loadGroupData($groupIds) - ); - - $listByGroupIds = []; - foreach ($groups as $group) { - $listByGroupIds[$group->id] = $group; - } - - return $listByGroupIds; - } - - /** - * @param string $identifier - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If type group with $identifier is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group - */ - public function loadGroupByIdentifier($identifier) - { - $groups = $this->mapper->extractGroupsFromRows( - $this->contentTypeGateway->loadGroupDataByIdentifier($identifier) - ); - - if (count($groups) !== 1) { - throw new Exception\TypeGroupNotFound($identifier); - } - - return $groups[0]; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group[] - */ - public function loadAllGroups() - { - return $this->mapper->extractGroupsFromRows( - $this->contentTypeGateway->loadAllGroupsData() - ); - } - - /** - * @param mixed $groupId - * @param int $status - * - * @return \eZ\Publish\SPI\Persistence\Content\Type[] - */ - public function loadContentTypes($groupId, $status = 0) - { - return $this->mapper->extractTypesFromRows( - $this->contentTypeGateway->loadTypesDataForGroup($groupId, $status) - ); - } - - public function loadContentTypeList(array $contentTypeIds): array - { - return $this->mapper->extractTypesFromRows( - $this->contentTypeGateway->loadTypesListData($contentTypeIds), - true - ); - } - - /** - * Loads a content type by id and status. - * - * Note: This method is responsible of having the Field Definitions of the loaded ContentType sorted by placement. - * - * @param int $contentTypeId - * @param int $status - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function load($contentTypeId, $status = Type::STATUS_DEFINED) - { - return $this->loadFromRows( - $this->contentTypeGateway->loadTypeData($contentTypeId, $status), - $contentTypeId, - $status - ); - } - - /** - * Loads a (defined) content type by identifier. - * - * Note: This method is responsible of having the Field Definitions of the loaded ContentType sorted by placement. - * - * @param string $identifier - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If defined type is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function loadByIdentifier($identifier) - { - $rows = $this->contentTypeGateway->loadTypeDataByIdentifier($identifier, Type::STATUS_DEFINED); - - return $this->loadFromRows($rows, $identifier, Type::STATUS_DEFINED); - } - - /** - * Loads a (defined) content type by remote id. - * - * Note: This method is responsible of having the Field Definitions of the loaded ContentType sorted by placement. - * - * @param mixed $remoteId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If defined type is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function loadByRemoteId($remoteId) - { - return $this->loadFromRows( - $this->contentTypeGateway->loadTypeDataByRemoteId($remoteId, Type::STATUS_DEFINED), - $remoteId, - Type::STATUS_DEFINED - ); - } - - /** - * Loads a single Type from $rows. - * - * @param array $rows - * @param mixed $typeIdentifier - * @param int $status - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - protected function loadFromRows(array $rows, $typeIdentifier, $status) - { - $types = $this->mapper->extractTypesFromRows($rows); - if (count($types) !== 1) { - throw new Exception\TypeNotFound($typeIdentifier, $status); - } - - return $types[0]; - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\Type\CreateStruct $createStruct - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function create(CreateStruct $createStruct) - { - return $this->internalCreate($createStruct); - } - - /** - * Internal method for creating ContentType. - * - * Used by self::create(), self::createDraft() and self::copy() - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\CreateStruct $createStruct - * @param mixed|null $contentTypeId Used by self::createDraft() to retain ContentType id in the draft - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - protected function internalCreate(CreateStruct $createStruct, $contentTypeId = null) - { - foreach ($createStruct->fieldDefinitions as $fieldDef) { - if (!is_int($fieldDef->position)) { - throw new InvalidArgumentException( - 'position', - "'" . var_export($fieldDef->position, true) . - "' is incorrect value in class FieldDefinition, an integer is required." - ); - } - } - - $createStruct = clone $createStruct; - $contentType = $this->mapper->createTypeFromCreateStruct( - $createStruct - ); - - $contentType->id = $this->contentTypeGateway->insertType( - $contentType, - $contentTypeId - ); - - foreach ($contentType->groupIds as $groupId) { - $this->contentTypeGateway->insertGroupAssignment( - $groupId, - $contentType->id, - $contentType->status - ); - } - - foreach ($contentType->fieldDefinitions as $fieldDef) { - $storageFieldDef = new StorageFieldDefinition(); - $this->mapper->toStorageFieldDefinition($fieldDef, $storageFieldDef); - $fieldDef->id = $this->contentTypeGateway->insertFieldDefinition( - $contentType->id, - $contentType->status, - $fieldDef, - $storageFieldDef - ); - } - - return $contentType; - } - - /** - * @param mixed $typeId - * @param int $status - * @param \eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct $contentType - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function update($typeId, $status, UpdateStruct $updateStruct) - { - $contentType = $this->mapper->createTypeFromUpdateStruct( - $updateStruct - ); - $this->contentTypeGateway->updateType($typeId, $status, $contentType); - - return $this->load($typeId, $status); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If type is defined and still has content - * - * @param mixed $contentTypeId - * @param int $status - * - * @return bool - */ - public function delete($contentTypeId, $status) - { - if (Type::STATUS_DEFINED === $status && $this->contentTypeGateway->countInstancesOfType($contentTypeId)) { - throw new BadStateException( - '$contentTypeId', - 'Content Type with the given ID still has Content items and cannot be deleted' - ); - } - - $this->contentTypeGateway->delete($contentTypeId, $status); - - // @todo FIXME: Return true only if deletion happened - return true; - } - - /** - * Creates a draft of existing defined content type. - * - * Updates modified date, sets $modifierId and status to Type::STATUS_DRAFT on the new returned draft. - * - * @param mixed $modifierId - * @param mixed $contentTypeId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If type with defined status is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function createDraft($modifierId, $contentTypeId) - { - $createStruct = $this->mapper->createCreateStructFromType( - $this->load($contentTypeId, Type::STATUS_DEFINED) - ); - $createStruct->status = Type::STATUS_DRAFT; - $createStruct->modifierId = $modifierId; - $createStruct->modified = time(); - - return $this->internalCreate($createStruct, $contentTypeId); - } - - /** - * @param mixed $userId - * @param mixed $contentTypeId - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function copy($userId, $contentTypeId, $status) - { - $createStruct = $this->mapper->createCreateStructFromType( - $this->load($contentTypeId, $status) - ); - $createStruct->modifierId = $userId; - $createStruct->created = $createStruct->modified = time(); - $createStruct->creatorId = $userId; - $createStruct->remoteId = md5(uniqid(get_class($createStruct), true)); - - // extract actual identifier name, without "copy_of_" and number - $originalIdentifier = preg_replace('/^copy_of_(.+)_\d+$/', '$1', $createStruct->identifier); - - // set temporary identifier - $createStruct->identifier = $createStruct->remoteId; - - // Set FieldDefinition ids to null to trigger creating new id - foreach ($createStruct->fieldDefinitions as $fieldDefinition) { - $fieldDefinition->id = null; - } - - $contentTypeCopy = $this->internalCreate($createStruct); - $updateStruct = $this->mapper->createUpdateStructFromType($contentTypeCopy); - $updateStruct->identifier = 'copy_of_' . $originalIdentifier . '_' . $contentTypeCopy->id; - - return $this->update($contentTypeCopy->id, $contentTypeCopy->status, $updateStruct); - } - - /** - * Unlink a content type group from a content type. - * - * @param mixed $groupId - * @param mixed $contentTypeId - * @param int $status - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If group or type with provided status is not found - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If $groupId is last group on $contentTypeId or - * not a group assigned to type - * - * @todo Add throws for NotFound and BadState when group is not assigned to type - */ - public function unlink($groupId, $contentTypeId, $status) - { - $groupCount = $this->contentTypeGateway->countGroupsForType($contentTypeId, $status); - if ($groupCount < 2) { - throw new Exception\RemoveLastGroupFromType($contentTypeId, $status); - } - - $this->contentTypeGateway->deleteGroupAssignment($groupId, $contentTypeId, $status); - // @todo FIXME: What is to be returned? - return true; - } - - /** - * Link a content type group with a content type. - * - * @param mixed $groupId - * @param mixed $contentTypeId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If group or type with provided status is not found - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If type is already part of group - * - * @todo Above throws are not implemented - */ - public function link($groupId, $contentTypeId, $status) - { - $this->contentTypeGateway->insertGroupAssignment($groupId, $contentTypeId, $status); - // @todo FIXME: What is to be returned? - return true; - } - - /** - * Returns field definition for the given field definition id. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If field definition is not found - * - * @param mixed $id - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition - */ - public function getFieldDefinition($id, $status) - { - $rows = $this->contentTypeGateway->loadFieldDefinition($id, $status); - - if (count($rows) === 0) { - throw new NotFoundException( - 'FieldDefinition', - [ - 'id' => $id, - 'status' => $status, - ] - ); - } - - $multilingualData = $this->mapper->extractMultilingualData($rows); - - return $this->mapper->extractFieldFromRow(reset($rows), $multilingualData); - } - - /** - * Counts the number of Content instances of the ContentType identified by given $contentTypeId. - * - * @param mixed $contentTypeId - * - * @return int - */ - public function getContentCount($contentTypeId) - { - return $this->contentTypeGateway->countInstancesOfType($contentTypeId); - } - - /** - * Adds a new field definition to an existing Type. - * - * This method creates a new status of the Type with the $fieldDefinition - * added. It does not update existing content objects depending on the - * field (default) values. - * - * @param mixed $contentTypeId - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - */ - public function addFieldDefinition($contentTypeId, $status, FieldDefinition $fieldDefinition) - { - $storageFieldDef = new StorageFieldDefinition(); - $this->mapper->toStorageFieldDefinition($fieldDefinition, $storageFieldDef); - $fieldDefinition->id = $this->contentTypeGateway->insertFieldDefinition( - $contentTypeId, - $status, - $fieldDefinition, - $storageFieldDef - ); - } - - /** - * Removes a field definition from an existing Type. - * - * This method creates a new status of the Type with the field definition - * referred to by $fieldDefinitionId removed. It does not update existing - * content objects depending on the field (default) values. - * - * @param mixed $contentTypeId - * @param mixed $fieldDefinitionId - * - * @return bool - */ - public function removeFieldDefinition($contentTypeId, $status, $fieldDefinitionId) - { - $this->contentTypeGateway->deleteFieldDefinition($contentTypeId, $status, $fieldDefinitionId); - // @todo FIXME: Return true only if deletion happened - return true; - } - - /** - * This method updates the given $fieldDefinition on a Type. - * - * This method creates a new status of the Type with the updated - * $fieldDefinition. It does not update existing content objects depending - * on the - * field (default) values. - * - * @param mixed $contentTypeId - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - */ - public function updateFieldDefinition($contentTypeId, $status, FieldDefinition $fieldDefinition) - { - $storageFieldDef = new StorageFieldDefinition(); - $this->mapper->toStorageFieldDefinition($fieldDefinition, $storageFieldDef); - $this->contentTypeGateway->updateFieldDefinition($contentTypeId, $status, $fieldDefinition, $storageFieldDef); - } - - /** - * Update content objects. - * - * Updates content objects, depending on the changed field definitions. - * - * A content type has a state which tells if its content objects yet have - * been adapted. - * - * Flags the content type as updated. - * - * @param mixed $contentTypeId - */ - public function publish($contentTypeId) - { - $toType = $this->load($contentTypeId, Type::STATUS_DRAFT); - - try { - $fromType = $this->load($contentTypeId, Type::STATUS_DEFINED); - $this->updateHandler->deleteOldType($fromType); - } catch (Exception\TypeNotFound $e) { - // If no old type is found, no updates are necessary to it - } - - $this->updateHandler->publishNewType($toType, Type::STATUS_DEFINED); - } - - /** - * @see \eZ\Publish\SPI\Persistence\Content\Type\Handler::getSearchableFieldMap - */ - public function getSearchableFieldMap() - { - $fieldMap = []; - $rows = $this->contentTypeGateway->getSearchableFieldMapData(); - - foreach ($rows as $row) { - $fieldMap[$row['content_type_identifier']][$row['field_definition_identifier']] = [ - 'field_type_identifier' => $row['field_type_identifier'], - 'field_definition_id' => $row['field_definition_id'], - ]; - } - - return $fieldMap; - } - - /** - * @param int $contentTypeId - * @param string $languageCode - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function removeContentTypeTranslation(int $contentTypeId, string $languageCode): Type - { - $type = $this->load($contentTypeId, Type::STATUS_DRAFT); - - unset($type->name[$languageCode]); - unset($type->description[$languageCode]); - - foreach ($type->fieldDefinitions as $fieldDefinition) { - $this->contentTypeGateway->removeFieldDefinitionTranslation( - $fieldDefinition->id, - $languageCode, - Type::STATUS_DRAFT - ); - - //Refresh FieldDefinition object after removing translation data. - $fieldDefinition = $this->getFieldDefinition( - $fieldDefinition->id, - Type::STATUS_DRAFT - ); - unset($fieldDefinition->name[$languageCode]); - unset($fieldDefinition->description[$languageCode]); - $storageFieldDefinition = new StorageFieldDefinition(); - $this->mapper->toStorageFieldDefinition($fieldDefinition, $storageFieldDefinition); - $this->contentTypeGateway->updateFieldDefinition( - $contentTypeId, - Type::STATUS_DRAFT, - $fieldDefinition, - $storageFieldDefinition - ); - } - - $updateStruct = $this->mapper->createUpdateStructFromType($type); - - return $this->update($type->id, Type::STATUS_DRAFT, $updateStruct); - } - - public function deleteByUserAndStatus(int $userId, int $status): void - { - $this->contentTypeGateway->removeByUserAndVersion($userId, $status); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Mapper.php b/eZ/Publish/Core/Persistence/Legacy/Content/Type/Mapper.php deleted file mode 100644 index 5d94d471b1..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Mapper.php +++ /dev/null @@ -1,520 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Type; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator; -use eZ\Publish\Core\Persistence\Legacy\Content\MultilingualStorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\Group; -use eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct as GroupCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct; - -/** - * Mapper for Content Type Handler. - * - * Performs mapping of Type objects. - */ -class Mapper -{ - /** - * Converter registry. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry - */ - protected $converterRegistry; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator */ - private $maskGenerator; - - /** - * Creates a new content type mapper. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry $converterRegistry - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator $maskGenerator - */ - public function __construct(ConverterRegistry $converterRegistry, MaskGenerator $maskGenerator) - { - $this->converterRegistry = $converterRegistry; - $this->maskGenerator = $maskGenerator; - } - - /** - * Creates a Group from its create struct. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct $struct - * - * @todo $description is not supported by database, yet - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group - */ - public function createGroupFromCreateStruct(GroupCreateStruct $struct) - { - $group = new Group(); - - $group->name = $struct->name; - - // $group->description is intentionally left out, since DB structure does not support it, yet - - $group->identifier = $struct->identifier; - $group->created = $struct->created; - $group->modified = $struct->modified; - $group->creatorId = $struct->creatorId; - $group->modifierId = $struct->modifierId; - - return $group; - } - - /** - * Extracts Group objects from the given $rows. - * - * @param array $rows - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group[] - */ - public function extractGroupsFromRows(array $rows) - { - $groups = []; - - foreach ($rows as $row) { - $group = new Group(); - $group->id = (int)$row['id']; - $group->created = (int)$row['created']; - $group->creatorId = (int)$row['creator_id']; - $group->modified = (int)$row['modified']; - $group->modifierId = (int)$row['modifier_id']; - $group->identifier = $row['name']; - - $groups[] = $group; - } - - return $groups; - } - - /** - * Extracts types and related data from the given $rows. - * - * @param array $rows - * @param bool $keepTypeIdAsKey - * - * @return array (Type) - */ - public function extractTypesFromRows(array $rows, bool $keepTypeIdAsKey = false) - { - $types = []; - $fields = []; - - foreach ($rows as $row) { - $typeId = (int)$row['ezcontentclass_id']; - if (!isset($types[$typeId])) { - $types[$typeId] = $this->extractTypeFromRow($row); - } - - $fieldId = (int)$row['ezcontentclass_attribute_id']; - - if ($fieldId && !isset($fields[$fieldId])) { - $fieldDataRows = array_filter($rows, static function (array $row) use ($fieldId) { - return (int) $row['ezcontentclass_attribute_id'] === (int) $fieldId; - }); - - $multilingualData = $this->extractMultilingualData($fieldDataRows); - - $types[$typeId]->fieldDefinitions[] = $fields[$fieldId] = $this->extractFieldFromRow($row, $multilingualData); - } - - $groupId = (int)$row['ezcontentclass_classgroup_group_id']; - if (!in_array($groupId, $types[$typeId]->groupIds)) { - $types[$typeId]->groupIds[] = $groupId; - } - } - - foreach ($types as $type) { - sort($type->groupIds); - } - - if ($keepTypeIdAsKey) { - return $types; - } - - // Re-index $types to avoid people relying on ID keys - return array_values($types); - } - - public function extractMultilingualData(array $fieldDefinitionRows): array - { - return array_map(static function (array $fieldData) { - return [ - 'ezcontentclass_attribute_multilingual_name' => $fieldData['ezcontentclass_attribute_multilingual_name'] ?? null, - 'ezcontentclass_attribute_multilingual_description' => $fieldData['ezcontentclass_attribute_multilingual_description'] ?? null, - 'ezcontentclass_attribute_multilingual_language_id' => $fieldData['ezcontentclass_attribute_multilingual_language_id'] ?? null, - 'ezcontentclass_attribute_multilingual_data_text' => $fieldData['ezcontentclass_attribute_multilingual_data_text'] ?? null, - 'ezcontentclass_attribute_multilingual_data_json' => $fieldData['ezcontentclass_attribute_multilingual_data_json'] ?? null, - ]; - }, $fieldDefinitionRows); - } - - /** - * Creates a Type from the data in $row. - * - * @param array $row - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - protected function extractTypeFromRow(array $row) - { - $type = new Type(); - - $type->id = (int)$row['ezcontentclass_id']; - $type->status = (int)$row['ezcontentclass_version']; - $type->name = $this->unserialize($row['ezcontentclass_serialized_name_list']); - $type->description = $this->unserialize($row['ezcontentclass_serialized_description_list']); - // Unset redundant data - unset( - $type->name['always-available'], - $type->name[0], - $type->description['always-available'], - $type->description[0] - ); - $type->identifier = $row['ezcontentclass_identifier']; - $type->created = (int)$row['ezcontentclass_created']; - $type->modified = (int)$row['ezcontentclass_modified']; - $type->modifierId = (int)$row['ezcontentclass_modifier_id']; - $type->creatorId = (int)$row['ezcontentclass_creator_id']; - $type->remoteId = $row['ezcontentclass_remote_id']; - $type->urlAliasSchema = $row['ezcontentclass_url_alias_name']; - $type->nameSchema = $row['ezcontentclass_contentobject_name']; - $type->isContainer = ($row['ezcontentclass_is_container'] == 1); - $type->initialLanguageId = (int)$row['ezcontentclass_initial_language_id']; - $type->defaultAlwaysAvailable = ($row['ezcontentclass_always_available'] == 1); - $type->sortField = (int)$row['ezcontentclass_sort_field']; - $type->sortOrder = (int)$row['ezcontentclass_sort_order']; - $type->languageCodes = $this->maskGenerator->extractLanguageCodesFromMask((int)$row['ezcontentclass_language_mask']); - - $type->groupIds = []; - $type->fieldDefinitions = []; - - return $type; - } - - /** - * Creates a FieldDefinition from the data in $row. - * - * @param array $row - * @param array $multilingualData - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition - */ - public function extractFieldFromRow(array $row, array $multilingualData = []) - { - $storageFieldDef = $this->extractStorageFieldFromRow($row, $multilingualData); - - $field = new FieldDefinition(); - - $field->id = (int)$row['ezcontentclass_attribute_id']; - $field->name = $this->unserialize($row['ezcontentclass_attribute_serialized_name_list']); - $field->description = $this->unserialize($row['ezcontentclass_attribute_serialized_description_list']); - // Unset redundant data - unset( - $field->name['always-available'], - $field->name[0], - $field->description['always-available'], - $field->description[0] - ); - $field->identifier = $row['ezcontentclass_attribute_identifier']; - $field->fieldGroup = $row['ezcontentclass_attribute_category']; - $field->fieldType = $row['ezcontentclass_attribute_data_type_string']; - $field->isTranslatable = ($row['ezcontentclass_attribute_can_translate'] == 1); - $field->isRequired = $row['ezcontentclass_attribute_is_required'] == 1; - $field->isThumbnail = !empty($row['ezcontentclass_attribute_is_thumbnail']); - $field->isInfoCollector = $row['ezcontentclass_attribute_is_information_collector'] == 1; - - $field->isSearchable = (bool)$row['ezcontentclass_attribute_is_searchable']; - $field->position = (int)$row['ezcontentclass_attribute_placement']; - - $mainLanguageCode = $this->maskGenerator->extractLanguageCodesFromMask((int)$row['ezcontentclass_initial_language_id']); - $field->mainLanguageCode = array_shift($mainLanguageCode); - - $this->toFieldDefinition($storageFieldDef, $field); - - return $field; - } - - /** - * Extracts a StorageFieldDefinition from $row. - * - * @param array $row - * @param array $multilingualDataRow - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition - */ - protected function extractStorageFieldFromRow(array $row, array $multilingualDataRow = []) - { - $storageFieldDef = new StorageFieldDefinition(); - - $storageFieldDef->dataFloat1 = isset($row['ezcontentclass_attribute_data_float1']) - ? (float)$row['ezcontentclass_attribute_data_float1'] - : null; - $storageFieldDef->dataFloat2 = isset($row['ezcontentclass_attribute_data_float2']) - ? (float)$row['ezcontentclass_attribute_data_float2'] - : null; - $storageFieldDef->dataFloat3 = isset($row['ezcontentclass_attribute_data_float3']) - ? (float)$row['ezcontentclass_attribute_data_float3'] - : null; - $storageFieldDef->dataFloat4 = isset($row['ezcontentclass_attribute_data_float4']) - ? (float)$row['ezcontentclass_attribute_data_float4'] - : null; - $storageFieldDef->dataInt1 = isset($row['ezcontentclass_attribute_data_int1']) - ? (int)$row['ezcontentclass_attribute_data_int1'] - : null; - $storageFieldDef->dataInt2 = isset($row['ezcontentclass_attribute_data_int2']) - ? (int)$row['ezcontentclass_attribute_data_int2'] - : null; - $storageFieldDef->dataInt3 = isset($row['ezcontentclass_attribute_data_int3']) - ? (int)$row['ezcontentclass_attribute_data_int3'] - : null; - $storageFieldDef->dataInt4 = isset($row['ezcontentclass_attribute_data_int4']) - ? (int)$row['ezcontentclass_attribute_data_int4'] - : null; - $storageFieldDef->dataText1 = $row['ezcontentclass_attribute_data_text1']; - $storageFieldDef->dataText2 = $row['ezcontentclass_attribute_data_text2']; - $storageFieldDef->dataText3 = $row['ezcontentclass_attribute_data_text3']; - $storageFieldDef->dataText4 = $row['ezcontentclass_attribute_data_text4']; - $storageFieldDef->dataText5 = $row['ezcontentclass_attribute_data_text5']; - $storageFieldDef->serializedDataText = $row['ezcontentclass_attribute_serialized_data_text']; - - foreach ($multilingualDataRow as $languageDataRow) { - $languageCodes = $this->maskGenerator->extractLanguageCodesFromMask((int)$languageDataRow['ezcontentclass_attribute_multilingual_language_id']); - - if (empty($languageCodes)) { - continue; - } - $languageCode = reset($languageCodes); - - $multilingualData = new MultilingualStorageFieldDefinition(); - - $nameList = $this->unserialize($row['ezcontentclass_attribute_serialized_name_list']); - $name = $nameList[$languageCode] ?? reset($nameList); - $description = $this->unserialize($row['ezcontentclass_attribute_serialized_description_list'])[$languageCode] ?? null; - - $multilingualData->name = $languageDataRow['ezcontentclass_attribute_multilingual_name'] ?? $name; - $multilingualData->description = $languageDataRow['ezcontentclass_attribute_multilingual_description'] ?? $description; - $multilingualData->dataText = $languageDataRow['ezcontentclass_attribute_multilingual_data_text']; - $multilingualData->dataJson = $languageDataRow['ezcontentclass_attribute_multilingual_data_json']; - $multilingualData->languageId = (int)$languageDataRow['ezcontentclass_attribute_multilingual_language_id']; - - $storageFieldDef->multilingualData[$languageCode] = $multilingualData; - } - - return $storageFieldDef; - } - - /** - * Maps properties from $struct to $type. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\CreateStruct $createStruct - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function createTypeFromCreateStruct(CreateStruct $createStruct) - { - $type = new Type(); - - $type->name = $createStruct->name; - $type->status = $createStruct->status; - $type->description = $createStruct->description; - $type->identifier = $createStruct->identifier; - $type->created = $createStruct->created; - $type->modified = $createStruct->modified; - $type->creatorId = $createStruct->creatorId; - $type->modifierId = $createStruct->modifierId; - $type->remoteId = $createStruct->remoteId; - $type->urlAliasSchema = $createStruct->urlAliasSchema; - $type->nameSchema = $createStruct->nameSchema; - $type->isContainer = $createStruct->isContainer; - $type->initialLanguageId = $createStruct->initialLanguageId; - $type->groupIds = $createStruct->groupIds; - $type->fieldDefinitions = $createStruct->fieldDefinitions; - $type->defaultAlwaysAvailable = $createStruct->defaultAlwaysAvailable; - $type->sortField = $createStruct->sortField; - $type->sortOrder = $createStruct->sortOrder; - $type->languageCodes = array_keys($createStruct->name); - - return $type; - } - - /** - * Creates a create struct from an existing $type. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type $type - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\CreateStruct - */ - public function createCreateStructFromType(Type $type) - { - $createStruct = new CreateStruct(); - - $createStruct->name = $type->name; - $createStruct->status = $type->status; - $createStruct->description = $type->description; - $createStruct->identifier = $type->identifier; - $createStruct->created = $type->created; - $createStruct->modified = $type->modified; - $createStruct->creatorId = $type->creatorId; - $createStruct->modifierId = $type->modifierId; - $createStruct->remoteId = $type->remoteId; - $createStruct->urlAliasSchema = $type->urlAliasSchema; - $createStruct->nameSchema = $type->nameSchema; - $createStruct->isContainer = $type->isContainer; - $createStruct->initialLanguageId = $type->initialLanguageId; - $createStruct->groupIds = $type->groupIds; - $createStruct->fieldDefinitions = $type->fieldDefinitions; - $createStruct->defaultAlwaysAvailable = $type->defaultAlwaysAvailable; - $createStruct->sortField = $type->sortField; - $createStruct->sortOrder = $type->sortOrder; - - return $createStruct; - } - - /** - * Creates an update struct from an existing $type. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type $type - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct - */ - public function createUpdateStructFromType(Type $type) - { - $updateStruct = new UpdateStruct(); - - $updateStruct->name = $type->name; - $updateStruct->description = $type->description; - $updateStruct->identifier = $type->identifier; - $updateStruct->modified = $type->modified; - $updateStruct->modifierId = $type->modifierId; - $updateStruct->remoteId = $type->remoteId; - $updateStruct->urlAliasSchema = $type->urlAliasSchema; - $updateStruct->nameSchema = $type->nameSchema; - $updateStruct->isContainer = $type->isContainer; - $updateStruct->initialLanguageId = $type->initialLanguageId; - $updateStruct->defaultAlwaysAvailable = $type->defaultAlwaysAvailable; - $updateStruct->sortField = $type->sortField; - $updateStruct->sortOrder = $type->sortOrder; - - return $updateStruct; - } - - /** - * Maps $fieldDef to the legacy storage specific StorageFieldDefinition. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageFieldDef - */ - public function toStorageFieldDefinition( - FieldDefinition $fieldDef, - StorageFieldDefinition $storageFieldDef - ) { - foreach (array_keys($fieldDef->name) as $languageCode) { - $multilingualData = new MultilingualStorageFieldDefinition(); - $multilingualData->name = $fieldDef->name[$languageCode]; - $multilingualData->description = $fieldDef->description[$languageCode] ?? null; - $multilingualData->languageId = - $this->maskGenerator->generateLanguageMaskFromLanguageCodes([$languageCode]); - - $storageFieldDef->multilingualData[$languageCode] = $multilingualData; - } - - $converter = $this->converterRegistry->getConverter( - $fieldDef->fieldType - ); - - $converter->toStorageFieldDefinition( - $fieldDef, - $storageFieldDef - ); - } - - /** - * Maps a FieldDefinition from the given $storageFieldDef. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageFieldDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - */ - public function toFieldDefinition( - StorageFieldDefinition $storageFieldDef, - FieldDefinition $fieldDef - ) { - $converter = $this->converterRegistry->getConverter( - $fieldDef->fieldType - ); - $converter->toFieldDefinition( - $storageFieldDef, - $fieldDef - ); - } - - /** - * Wrap unserialize to set default value in case of empty serialization. - * - * @param string $serialized Serialized structure to process - * @param mixed $default Default value in case of empty serialization - * - * @return array|mixed - */ - protected function unserialize($serialized, $default = []) - { - return $serialized - ? unserialize($serialized) - : $default; - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct $updateStruct - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function createTypeFromUpdateStruct(UpdateStruct $updateStruct): Type - { - $type = new Type(); - - $type->name = $updateStruct->name; - $type->description = $updateStruct->description; - $type->identifier = $updateStruct->identifier; - $type->modified = $updateStruct->modified; - $type->modifierId = $updateStruct->modifierId; - $type->remoteId = $updateStruct->remoteId; - $type->urlAliasSchema = $updateStruct->urlAliasSchema; - $type->nameSchema = $updateStruct->nameSchema; - $type->isContainer = $updateStruct->isContainer; - $type->initialLanguageId = $updateStruct->initialLanguageId; - $type->defaultAlwaysAvailable = $updateStruct->defaultAlwaysAvailable; - $type->sortField = $updateStruct->sortField; - $type->sortOrder = $updateStruct->sortOrder; - $type->languageCodes = array_keys($updateStruct->name); - - return $type; - } - - public function extractMultilingualDataFromRows(array $mlFieldDefinitionsRows): array - { - $mlFieldDefinitionData = []; - foreach ($mlFieldDefinitionsRows as $row) { - $mlStorageFieldDefinition = new MultilingualStorageFieldDefinition(); - $mlStorageFieldDefinition->name = $row['ezcontentclass_attribute_multilingual_name']; - $mlStorageFieldDefinition->description = $row['ezcontentclass_attribute_multilingual_description']; - $mlStorageFieldDefinition->languageId = $row['ezcontentclass_attribute_multilingual_language_id']; - $mlStorageFieldDefinition->dataText = $row['ezcontentclass_attribute_multilingual_data_text']; - $mlStorageFieldDefinition->dataJson = $row['ezcontentclass_attribute_multilingual_data_json']; - - $mlFieldDefinitionData[] = $mlStorageFieldDefinition; - } - - return $mlFieldDefinitionData; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Type/MemoryCachingHandler.php b/eZ/Publish/Core/Persistence/Legacy/Content/Type/MemoryCachingHandler.php deleted file mode 100644 index 6e6d06552c..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Type/MemoryCachingHandler.php +++ /dev/null @@ -1,456 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Type; - -use eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\Group; -use eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct as GroupCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct as GroupUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as BaseContentTypeHandler; -use eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct; - -class MemoryCachingHandler implements BaseContentTypeHandler -{ - /** - * Inner handler to dispatch calls to. - * - * @var \eZ\Publish\SPI\Persistence\Content\Type\Handler - */ - protected $innerHandler; - - /** - * Type cache. - * - * @var \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache - */ - protected $cache; - - /** - * Creates a new content type handler. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\Handler $handler - * @param \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache $cache - */ - public function __construct(BaseContentTypeHandler $handler, InMemoryCache $cache) - { - $this->innerHandler = $handler; - $this->cache = $cache; - } - - /** - * {@inheritdoc} - */ - public function createGroup(GroupCreateStruct $createStruct) - { - $group = $this->innerHandler->createGroup($createStruct); - $this->storeGroupCache([$group]); - $this->cache->deleteMulti(['ez-content-type-group-list']); - - return $group; - } - - /** - * {@inheritdoc} - */ - public function updateGroup(GroupUpdateStruct $struct) - { - $group = $this->innerHandler->updateGroup($struct); - $this->storeGroupCache([$group]); - $this->cache->deleteMulti(['ez-content-type-group-list']); - - return $group; - } - - /** - * {@inheritdoc} - */ - public function deleteGroup($groupId) - { - $this->innerHandler->deleteGroup($groupId); - // Delete by primary key will remove the object, so we don't need to clear `-by-identifier` variant here. - $this->cache->deleteMulti(['ez-content-type-group-' . $groupId, 'ez-content-type-group-list']); - } - - /** - * {@inheritdoc} - */ - public function loadGroup($groupId) - { - $group = $this->cache->get('ez-content-type-group-' . $groupId); - if ($group === null) { - $group = $this->innerHandler->loadGroup($groupId); - $this->storeGroupCache([$group]); - } - - return $group; - } - - /** - * {@inheritdoc} - */ - public function loadGroups(array $groupIds) - { - $groups = $missingIds = []; - foreach ($groupIds as $groupId) { - if ($group = $this->cache->get('ez-content-type-group-' . $groupId)) { - $groups[$groupId] = $group; - } else { - $missingIds[] = $groupId; - } - } - - if (!empty($missingIds)) { - $loaded = $this->innerHandler->loadGroups($missingIds); - $this->storeGroupCache($loaded); - /** @noinspection AdditionOperationOnArraysInspection */ - $groups += $loaded; - } - - return $groups; - } - - /** - * {@inheritdoc} - */ - public function loadGroupByIdentifier($identifier) - { - $group = $this->cache->get('ez-content-type-group-' . $identifier . '-by-identifier'); - if ($group === null) { - $group = $this->innerHandler->loadGroupByIdentifier($identifier); - $this->storeGroupCache([$group]); - } - - return $group; - } - - /** - * {@inheritdoc} - */ - public function loadAllGroups() - { - $groups = $this->cache->get('ez-content-type-group-list'); - if ($groups === null) { - $groups = $this->innerHandler->loadAllGroups(); - $this->storeGroupCache($groups, 'ez-content-type-group-list'); - } - - return $groups; - } - - /** - * {@inheritdoc} - */ - public function loadContentTypes($groupId, $status = Type::STATUS_DEFINED) - { - if ($status !== Type::STATUS_DEFINED) { - return $this->innerHandler->loadContentTypes($groupId, $status); - } - - $types = $this->cache->get('ez-content-type-list-by-group-' . $groupId); - if ($types === null) { - $types = $this->innerHandler->loadContentTypes($groupId, $status); - $this->storeTypeCache($types, 'ez-content-type-list-by-group-' . $groupId); - } - - return $types; - } - - /** - * {@inheritdoc} - */ - public function loadContentTypeList(array $contentTypeIds): array - { - $contentTypes = $missingIds = []; - foreach ($contentTypeIds as $contentTypeId) { - if ($contentType = $this->cache->get('ez-content-type-' . $contentTypeId . '-' . Type::STATUS_DEFINED)) { - $contentTypes[$contentTypeId] = $contentType; - } else { - $missingIds[] = $contentTypeId; - } - } - - if (!empty($missingIds)) { - $loaded = $this->innerHandler->loadContentTypeList($missingIds); - $this->storeTypeCache($loaded); - /** @noinspection AdditionOperationOnArraysInspection */ - $contentTypes += $loaded; - } - - return $contentTypes; - } - - /** - * {@inheritdoc} - */ - public function load($contentTypeId, $status = Type::STATUS_DEFINED) - { - $contentType = $this->cache->get('ez-content-type-' . $contentTypeId . '-' . $status); - if ($contentType === null) { - $contentType = $this->innerHandler->load($contentTypeId, $status); - $this->storeTypeCache([$contentType]); - } - - return $contentType; - } - - /** - * {@inheritdoc} - */ - public function loadByIdentifier($identifier) - { - $contentType = $this->cache->get('ez-content-type-' . $identifier . '-by-identifier'); - if ($contentType === null) { - $contentType = $this->innerHandler->loadByIdentifier($identifier); - $this->storeTypeCache([$contentType]); - } - - return $contentType; - } - - /** - * {@inheritdoc} - */ - public function loadByRemoteId($remoteId) - { - $contentType = $this->cache->get('ez-content-type-' . $remoteId . '-by-remote'); - if ($contentType === null) { - $contentType = $this->innerHandler->loadByRemoteId($remoteId); - $this->storeTypeCache([$contentType]); - } - - return $contentType; - } - - /** - * {@inheritdoc} - */ - public function create(CreateStruct $createStruct) - { - $contentType = $this->innerHandler->create($createStruct); - // Don't store as FieldTypeConstraints is not setup fully here from Legacy SE side - $this->deleteTypeCache($contentType->id, $contentType->status); - - return $contentType; - } - - /** - * {@inheritdoc} - */ - public function update($typeId, $status, UpdateStruct $contentType) - { - $contentType = $this->innerHandler->update($typeId, $status, $contentType); - $this->storeTypeCache([$contentType]); - - return $contentType; - } - - /** - * {@inheritdoc} - */ - public function delete($contentTypeId, $status) - { - $this->innerHandler->delete($contentTypeId, $status); - $this->deleteTypeCache($contentTypeId, $status); - } - - /** - * {@inheritdoc} - */ - public function createDraft($modifierId, $contentTypeId) - { - $contentType = $this->innerHandler->createDraft($modifierId, $contentTypeId); - // Don't store as FieldTypeConstraints is not setup fully here from Legacy SE side - $this->deleteTypeCache($contentType->id, $contentType->status); - - return $contentType; - } - - /** - * {@inheritdoc} - */ - public function copy($userId, $contentTypeId, $status) - { - $contentType = $this->innerHandler->copy($userId, $contentTypeId, $status); - $this->storeTypeCache([$contentType]); - - return $contentType; - } - - /** - * {@inheritdoc} - */ - public function unlink($groupId, $contentTypeId, $status) - { - $keys = ['ez-content-type-' . $contentTypeId . '-' . $status]; - if ($status === Type::STATUS_DEFINED) { - $keys[] = 'ez-content-type-list-by-group-' . $groupId; - } - $this->cache->deleteMulti($keys); - - return $this->innerHandler->unlink($groupId, $contentTypeId, $status); - } - - /** - * {@inheritdoc} - */ - public function link($groupId, $contentTypeId, $status) - { - $keys = ['ez-content-type-' . $contentTypeId . '-' . $status]; - if ($status === Type::STATUS_DEFINED) { - $keys[] = 'ez-content-type-list-by-group-' . $groupId; - } - $this->cache->deleteMulti($keys); - - return $this->innerHandler->link($groupId, $contentTypeId, $status); - } - - /** - * {@inheritdoc} - */ - public function getFieldDefinition($id, $status) - { - return $this->innerHandler->getFieldDefinition($id, $status); - } - - /** - * {@inheritdoc} - */ - public function getContentCount($contentTypeId) - { - return $this->innerHandler->getContentCount($contentTypeId); - } - - /** - * {@inheritdoc} - */ - public function addFieldDefinition($contentTypeId, $status, FieldDefinition $fieldDefinition) - { - $this->deleteTypeCache($contentTypeId, $status); - - return $this->innerHandler->addFieldDefinition($contentTypeId, $status, $fieldDefinition); - } - - /** - * {@inheritdoc} - */ - public function removeFieldDefinition($contentTypeId, $status, $fieldDefinitionId) - { - $this->deleteTypeCache($contentTypeId, $status); - - return $this->innerHandler->removeFieldDefinition($contentTypeId, $status, $fieldDefinitionId); - } - - /** - * {@inheritdoc} - */ - public function updateFieldDefinition($contentTypeId, $status, FieldDefinition $fieldDefinition) - { - $this->deleteTypeCache($contentTypeId, $status); - - return $this->innerHandler->updateFieldDefinition($contentTypeId, $status, $fieldDefinition); - } - - /** - * {@inheritdoc} - */ - public function publish($contentTypeId) - { - $this->clearCache(); - - return $this->innerHandler->publish($contentTypeId); - } - - /** - * {@inheritdoc} - */ - public function getSearchableFieldMap() - { - $map = $this->cache->get('ez-content-type-field-map'); - if ($map === null) { - $map = $this->innerHandler->getSearchableFieldMap(); - $this->cache->setMulti( - $map, - static function () { return []; }, - 'ez-content-type-field-map' - ); - } - - return $map; - } - - /** - * {@inheritdoc} - */ - public function removeContentTypeTranslation(int $contentTypeId, string $languageCode): Type - { - $this->clearCache(); - - return $this->innerHandler->removeContentTypeTranslation($contentTypeId, $languageCode); - } - - /** - * Clear internal caches. - */ - public function clearCache() - { - $this->cache->clear(); - } - - protected function deleteTypeCache(int $contentTypeId, int $status = Type::STATUS_DEFINED): void - { - if ($status !== Type::STATUS_DEFINED) { - // Delete by primary key will remove the object, so we don't need to clear other variants here. - $this->cache->deleteMulti(['ez-content-type-' . $contentTypeId . '-' . $status, 'ez-content-type-field-map']); - } else { - // We don't know group id in order to clear relevant "ez-content-type-list-by-group-$groupId". - $this->cache->clear(); - } - } - - protected function storeTypeCache(array $types, string $listIndex = null): void - { - $this->cache->setMulti( - $types, - static function (Type $type) { - if ($type->status !== Type::STATUS_DEFINED) { - return ['ez-content-type-' . $type->id . '-' . $type->status]; - } - - return [ - 'ez-content-type-' . $type->id . '-' . $type->status, - 'ez-content-type-' . $type->identifier . '-by-identifier', - 'ez-content-type-' . $type->remoteId . '-by-remote', - ]; - }, - $listIndex - ); - - $this->cache->deleteMulti(['ez-content-type-field-map']); - } - - protected function storeGroupCache(array $groups, string $listIndex = null): void - { - $this->cache->setMulti( - $groups, - static function (Group $group) { - return [ - 'ez-content-type-group-' . $group->id, - 'ez-content-type-group-' . $group->identifier . '-by-identifier', - ]; - }, - $listIndex - ); - } - - public function deleteByUserAndStatus(int $userId, int $status): void - { - $this->innerHandler->deleteByUserAndStatus($userId, $status); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Update/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Content/Type/Update/Handler.php deleted file mode 100644 index c1adb87384..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Update/Handler.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\Type\Update; - -use eZ\Publish\SPI\Persistence\Content\Type; - -/** - * Base class for update handlers. - * - * @internal For internal use by Repository. - */ -abstract class Handler -{ - /** - * Update existing Content items from one version of a Content Type to another one. - */ - abstract public function updateContentObjects(Type $fromType, Type $toType): void; - - /** - * Delete old version of a Content Type and all of its Field Definitions. - */ - abstract public function deleteOldType(Type $fromType): void; - - /** - * Change Content Type status. - */ - abstract public function publishNewType(Type $toType, int $newStatus): void; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabase.php deleted file mode 100644 index 9351df00bf..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabase.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler; - -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler; -use eZ\Publish\SPI\Persistence\Content\Type; - -/** - * Doctrine database based type update handler. - * - * @internal For internal use by Repository - */ -final class DoctrineDatabase extends Handler -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway */ - protected $contentTypeGateway; - - public function __construct(Gateway $contentTypeGateway) - { - $this->contentTypeGateway = $contentTypeGateway; - } - - public function updateContentObjects(Type $fromType, Type $toType): void - { - // Do nothing, content objects are no longer updated - } - - public function deleteOldType(Type $fromType): void - { - $this->contentTypeGateway->delete($fromType->id, $fromType->status); - } - - public function publishNewType(Type $toType, int $newStatus): void - { - $this->contentTypeGateway->publishTypeAndFields( - $toType->id, - $toType->status, - $newStatus - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Gateway.php deleted file mode 100644 index bd32999119..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Gateway.php +++ /dev/null @@ -1,246 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias; - -/** - * UrlAlias Gateway. - * - * @internal For internal use by Persistence Handlers. - */ -abstract class Gateway -{ - /** - * Default database table. - */ - public const TABLE = 'ezurlalias_ml'; - - public const INCR_TABLE = 'ezurlalias_ml_incr'; - public const INCR_TABLE_SEQ = 'ezurlalias_ml_incr_id_seq'; - - public const NOP = 'nop'; - public const NOP_ACTION = self::NOP . ':'; - - /** - * Changes the gateway database table. - * - * @internal - * - * @param string $name - */ - abstract public function setTable(string $name): void; - - /** - * Loads all list of aliases by given $locationId. - */ - abstract public function loadAllLocationEntries(int $locationId): array; - - /** - * Load list of aliases by given $locationId. - */ - abstract public function loadLocationEntries( - int $locationId, - bool $custom = false, - ?int $languageId = null - ): array; - - /** - * Load paged list of global aliases. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the given language doesn't exist - */ - abstract public function listGlobalEntries( - ?string $languageCode = null, - int $offset = 0, - int $limit = -1 - ): array; - - /** - * Return boolean indicating if the row with given $id is special root entry. - * - * Special root entry entry will have parentId=0 and text=''. - * In standard installation this entry will point to location with id=2. - */ - abstract public function isRootEntry(int $id): bool; - - /** - * Update single row data matched by composite primary key. - * - * @param array $values associative array with column names as keys and column values as values - */ - abstract public function updateRow(int $parentId, string $textMD5, array $values): void; - - /** - * Insert new row into urlalias_ml table. - * - * @param array $values - */ - abstract public function insertRow(array $values): int; - - /** - * Load single row matched by composite primary key. - */ - abstract public function loadRow(int $parentId, string $textMD5): array; - - /** - * Downgrade autogenerated entry matched by given $action and $languageId and negatively matched by - * composite primary key. - * - * If language mask of the found entry is composite (meaning it consists of multiple language ids) given - * $languageId will be removed from mask. Otherwise entry will be marked as history. - */ - abstract public function cleanupAfterPublish( - string $action, - int $languageId, - int $newId, - int $parentId, - string $textMD5 - ): void; - - /** - * Archive entry with $action by $languageMask. - * - * Used when swapping Location aliases, this ensures that given $languageMask matches a - * single entry (database row). - */ - abstract public function historizeBeforeSwap(string $action, int $languageMask): void; - - /** - * Mark all entries with given $id as history entries. - * - * This method is used by Handler::locationMoved(). Each row is separately historized - * because future publishing needs to be able to take over history entries safely. - */ - abstract public function historizeId(int $id, int $link): void; - - /** - * Update parent id of autogenerated entries. - * - * Update includes history entries. - */ - abstract public function reparent(int $oldParentId, int $newParentId): void; - - /** - * Load path data identified by given $id. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - */ - abstract public function loadPathData(int $id): array; - - /** - * Load path data identified by given ordered array of hierarchy data. - * - * The first entry in $hierarchyData corresponds to the top-most path element in the path, the second entry the - * child of the first path element and so on. - * This method is faster than self::getPath() since it can fetch all elements using only one query, but can be used - * only for autogenerated paths. - * - * @param array $hierarchyData hierarchy-ordered URL alias entries data - */ - abstract public function loadPathDataByHierarchy(array $hierarchyData): array; - - /** - * Load complete URL alias data by given array of path hashes. - * - * @param string[] $urlHashes URL string hashes - */ - abstract public function loadUrlAliasData(array $urlHashes): array; - - /** - * Load autogenerated entry id by given $action and optionally $parentId. - */ - abstract public function loadAutogeneratedEntry(string $action, ?int $parentId = null): array; - - /** - * Delete single custom alias row matched by composite primary key. - */ - abstract public function removeCustomAlias(int $parentId, string $textMD5): bool; - - /** - * Delete all rows with given $action and optionally $id. - * - * If $id is set only autogenerated entries will be removed. - */ - abstract public function remove(string $action, ?int $id = null): void; - - /** - * Load all autogenerated entries with given $parentId with optionally included history entries. - */ - abstract public function loadAutogeneratedEntries( - int $parentId, - bool $includeHistory = false - ): array; - - /** - * Return next value for "id" column. - */ - abstract public function getNextId(): int; - - /** - * Return main language ID of the Content on the Location with given $locationId. - */ - abstract public function getLocationContentMainLanguageId(int $locationId): int; - - /** - * Remove languageId of removed translation from lang_mask and deletes single language rows for multiple Locations. - * - * @param string[] $actions actions for which to perform the update - */ - abstract public function bulkRemoveTranslation(int $languageId, array $actions): void; - - /** - * Archive (remove or historize) URL aliases for removed Translations. - * - * @param int $parentId Parent alias used for linking historized entries - * @param int[] $languageIds Language IDs of removed Translations - */ - abstract public function archiveUrlAliasesForDeletedTranslations( - int $locationId, - int $parentId, - array $languageIds - ): void; - - /** - * Delete URL aliases pointing to non-existent Locations. - * - * @return int Number of affected rows. - */ - abstract public function deleteUrlAliasesWithoutLocation(): int; - - /** - * Delete URL aliases pointing to non-existent parent nodes. - * - * @return int Number of affected rows. - */ - abstract public function deleteUrlAliasesWithoutParent(): int; - - /** - * Delete URL aliases which do not link to any existing URL alias node. - * - * Note: Typically link column value is used to determine original alias for an archived entries. - * - * @return int Number of deleted rows. - */ - abstract public function deleteUrlAliasesWithBrokenLink(): int; - - /** - * Delete "nop" type actions URL aliases that don't have children. - */ - abstract public function deleteUrlNopAliasesWithoutChildren(): int; - - /** - * Return aliases which are connected with provided parentId. - */ - abstract public function getAllChildrenAliases(int $parentId): array; - - /** - * Attempt repairing data corruption for broken archived URL aliases for Location, - * assuming there exists restored original (current) entry. - */ - abstract public function repairBrokenUrlAliasesForLocation(int $locationId): void; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php deleted file mode 100644 index 1e403b8967..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,1450 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Exception\UniqueConstraintViolationException; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway; -use RuntimeException; - -/** - * UrlAlias gateway implementation using the Doctrine database. - * - * @internal Gateway implementation is considered internal. Use Persistence UrlAlias Handler instead. - * - * @see \eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler - */ -final class DoctrineDatabase extends Gateway -{ - /** - * 2^30, since PHP_INT_MAX can cause overflows in DB systems, if PHP is run - * on 64 bit systems. - */ - public const MAX_LIMIT = 1073741824; - - private const URL_ALIAS_DATA_COLUMN_TYPE_MAP = [ - 'id' => ParameterType::INTEGER, - 'link' => ParameterType::INTEGER, - 'is_alias' => ParameterType::INTEGER, - 'alias_redirects' => ParameterType::INTEGER, - 'is_original' => ParameterType::INTEGER, - 'action' => ParameterType::STRING, - 'action_type' => ParameterType::STRING, - 'lang_mask' => ParameterType::INTEGER, - 'text' => ParameterType::STRING, - 'parent' => ParameterType::INTEGER, - 'text_md5' => ParameterType::STRING, - ]; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator */ - private $languageMaskGenerator; - - /** - * Main URL database table name. - * - * @var string - */ - private $table; - - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */ - private $dbPlatform; - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct( - Connection $connection, - LanguageMaskGenerator $languageMaskGenerator - ) { - $this->connection = $connection; - $this->languageMaskGenerator = $languageMaskGenerator; - $this->table = static::TABLE; - $this->dbPlatform = $this->connection->getDatabasePlatform(); - } - - public function setTable(string $name): void - { - $this->table = $name; - } - - /** - * Loads all list of aliases by given $locationId. - */ - public function loadAllLocationEntries(int $locationId): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select(array_keys(self::URL_ALIAS_DATA_COLUMN_TYPE_MAP)) - ->from($this->connection->quoteIdentifier($this->table)) - ->where('action = :action') - ->andWhere('is_original = :is_original') - ->setParameter('action', "eznode:{$locationId}", ParameterType::STRING) - ->setParameter('is_original', 1, ParameterType::INTEGER); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadLocationEntries( - int $locationId, - bool $custom = false, - ?int $languageId = null - ): array { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select( - 'id', - 'link', - 'is_alias', - 'alias_redirects', - 'lang_mask', - 'is_original', - 'parent', - 'text', - 'text_md5', - 'action' - ) - ->from($this->connection->quoteIdentifier($this->table)) - ->where( - $expr->eq( - 'action', - $query->createPositionalParameter( - "eznode:{$locationId}", - ParameterType::STRING - ) - ) - ) - ->andWhere( - $expr->eq( - 'is_original', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - 'is_alias', - $query->createPositionalParameter($custom ? 1 : 0, ParameterType::INTEGER) - ) - ) - ; - - if (null !== $languageId) { - $query->andWhere( - $expr->gt( - $this->dbPlatform->getBitAndComparisonExpression( - 'lang_mask', - $query->createPositionalParameter($languageId, ParameterType::INTEGER) - ), - 0 - ) - ); - } - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function listGlobalEntries( - ?string $languageCode = null, - int $offset = 0, - int $limit = -1 - ): array { - $limit = $limit === -1 ? self::MAX_LIMIT : $limit; - - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select( - 'action', - 'id', - 'link', - 'is_alias', - 'alias_redirects', - 'lang_mask', - 'is_original', - 'parent', - 'text_md5' - ) - ->from($this->connection->quoteIdentifier($this->table)) - ->where( - $expr->eq( - 'action_type', - $query->createPositionalParameter( - 'module', - ParameterType::STRING - ) - ) - ) - ->andWhere( - $expr->eq( - 'is_original', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - 'is_alias', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ) - ) - ->setMaxResults( - $limit - ) - ->setFirstResult($offset); - - if (isset($languageCode)) { - $query->andWhere( - $expr->gt( - $this->dbPlatform->getBitAndComparisonExpression( - 'lang_mask', - $query->createPositionalParameter( - $this->languageMaskGenerator->generateLanguageIndicator( - $languageCode, - false - ), - ParameterType::INTEGER - ) - ), - 0 - ) - ); - } - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function isRootEntry(int $id): bool - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - 'text', - 'parent' - ) - ->from($this->connection->quoteIdentifier($this->table)) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - $statement = $query->execute(); - - $row = $statement->fetch(FetchMode::ASSOCIATIVE); - - return strlen($row['text']) == 0 && $row['parent'] == 0; - } - - public function cleanupAfterPublish( - string $action, - int $languageId, - int $newId, - int $parentId, - string $textMD5 - ): void { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select( - 'parent', - 'text_md5', - 'lang_mask' - ) - ->from($this->connection->quoteIdentifier($this->table)) - // 1) Autogenerated aliases that match action and language... - ->where( - $expr->eq( - 'action', - $query->createPositionalParameter($action, ParameterType::STRING) - ) - ) - ->andWhere( - $expr->eq( - 'is_original', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - 'is_alias', - $query->createPositionalParameter(0, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->gt( - $this->dbPlatform->getBitAndComparisonExpression( - 'lang_mask', - $query->createPositionalParameter($languageId, ParameterType::INTEGER) - ), - 0 - ) - ) - // 2) ...but not newly published entry - ->andWhere( - sprintf( - 'NOT (%s)', - $expr->andX( - $expr->eq( - 'parent', - $query->createPositionalParameter($parentId, ParameterType::INTEGER) - ), - $expr->eq( - 'text_md5', - $query->createPositionalParameter($textMD5, ParameterType::STRING) - ) - ) - ) - ); - - $statement = $query->execute(); - - $row = $statement->fetch(FetchMode::ASSOCIATIVE); - - if (!empty($row)) { - $this->archiveUrlAliasForDeletedTranslation( - (int)$row['lang_mask'], - (int)$languageId, - (int)$row['parent'], - $row['text_md5'], - (int)$newId - ); - } - } - - /** - * Archive (remove or historize) obsolete URL aliases (for translations that were removed). - * - * @param int $languageMask all languages bit mask - * @param int $languageId removed language Id - * @param string $textMD5 checksum - */ - private function archiveUrlAliasForDeletedTranslation( - int $languageMask, - int $languageId, - int $parent, - string $textMD5, - int $linkId - ): void { - // If language mask is composite (consists of multiple languages) then remove given language from entry - if ($languageMask & ~($languageId | 1)) { - $this->removeTranslation($parent, $textMD5, $languageId); - } else { - // Otherwise mark entry as history - $this->historize($parent, $textMD5, $linkId); - } - } - - public function historizeBeforeSwap(string $action, int $languageMask): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update($this->connection->quoteIdentifier($this->table)) - ->set( - 'is_original', - $query->createPositionalParameter(0, ParameterType::INTEGER) - ) - ->set( - 'id', - $query->createPositionalParameter( - $this->getNextId(), - ParameterType::INTEGER - ) - ) - ->where( - $query->expr()->andX( - $query->expr()->eq( - 'action', - $query->createPositionalParameter($action, ParameterType::STRING) - ), - $query->expr()->eq( - 'is_original', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ), - $query->expr()->gt( - $this->dbPlatform->getBitAndComparisonExpression( - 'lang_mask', - $query->createPositionalParameter( - $languageMask & ~1, - ParameterType::INTEGER - ) - ), - 0 - ) - ) - ); - - $query->execute(); - } - - /** - * Update single row matched by composite primary key. - * - * Sets "is_original" to 0 thus marking entry as history. - * - * Re-links history entries. - * - * When location alias is published we need to check for new history entries created with self::downgrade() - * with the same action and language, update their "link" column with id of the published entry. - * History entry "id" column is moved to next id value so that all active (non-history) entries are kept - * under the same id. - */ - private function historize(int $parentId, string $textMD5, int $newId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update($this->connection->quoteIdentifier($this->table)) - ->set( - 'is_original', - $query->createPositionalParameter(0, ParameterType::INTEGER) - ) - ->set( - 'link', - $query->createPositionalParameter($newId, ParameterType::INTEGER) - ) - ->set( - 'id', - $query->createPositionalParameter( - $this->getNextId(), - ParameterType::INTEGER - ) - ) - ->where( - $query->expr()->andX( - $query->expr()->eq( - 'parent', - $query->createPositionalParameter($parentId, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'text_md5', - $query->createPositionalParameter($textMD5, ParameterType::STRING) - ) - ) - ); - $query->execute(); - } - - /** - * Update single row data matched by composite primary key. - * - * Removes given $languageId from entry's language mask - */ - private function removeTranslation(int $parentId, string $textMD5, int $languageId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update($this->connection->quoteIdentifier($this->table)) - ->set( - 'lang_mask', - $this->dbPlatform->getBitAndComparisonExpression( - 'lang_mask', - $query->createPositionalParameter( - ~$languageId, - ParameterType::INTEGER - ) - ) - ) - ->where( - $query->expr()->eq( - 'parent', - $query->createPositionalParameter( - $parentId, - ParameterType::INTEGER - ) - ) - ) - ->andWhere( - $query->expr()->eq( - 'text_md5', - $query->createPositionalParameter( - $textMD5, - ParameterType::STRING - ) - ) - ) - ; - $query->execute(); - } - - public function historizeId(int $id, int $link): void - { - if ($id === $link) { - return; - } - - $query = $this->connection->createQueryBuilder(); - $query->select( - 'parent', - 'text_md5' - )->from( - $this->connection->quoteIdentifier($this->table) - )->where( - $query->expr()->andX( - $query->expr()->eq( - 'is_alias', - $query->createPositionalParameter(0, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'is_original', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'action_type', - $query->createPositionalParameter( - 'eznode', - ParameterType::STRING - ) - ), - $query->expr()->eq( - 'link', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ) - ); - - $statement = $query->execute(); - - $rows = $statement->fetchAll(FetchMode::ASSOCIATIVE); - - foreach ($rows as $row) { - $this->historize((int)$row['parent'], $row['text_md5'], $link); - } - } - - public function reparent(int $oldParentId, int $newParentId): void - { - $query = $this->connection->createQueryBuilder(); - $query->update( - $this->connection->quoteIdentifier($this->table) - )->set( - 'parent', - $query->createPositionalParameter($newParentId, ParameterType::INTEGER) - )->where( - $query->expr()->eq( - 'parent', - $query->createPositionalParameter( - $oldParentId, - ParameterType::INTEGER - ) - ) - ); - - $query->execute(); - } - - public function updateRow(int $parentId, string $textMD5, array $values): void - { - $query = $this->connection->createQueryBuilder(); - $query->update($this->connection->quoteIdentifier($this->table)); - foreach ($values as $columnName => $value) { - $query->set( - $columnName, - $query->createNamedParameter( - $value, - self::URL_ALIAS_DATA_COLUMN_TYPE_MAP[$columnName], - ":{$columnName}" - ) - ); - } - $query - ->where( - $query->expr()->eq( - 'parent', - $query->createNamedParameter($parentId, ParameterType::INTEGER, ':parent') - ) - ) - ->andWhere( - $query->expr()->eq( - 'text_md5', - $query->createNamedParameter($textMD5, ParameterType::STRING, ':text_md5') - ) - ); - $query->execute(); - } - - public function insertRow(array $values): int - { - if (!isset($values['id'])) { - $values['id'] = $this->getNextId(); - } - if (!isset($values['link'])) { - $values['link'] = $values['id']; - } - if (!isset($values['is_original'])) { - $values['is_original'] = ($values['id'] == $values['link'] ? 1 : 0); - } - if (!isset($values['is_alias'])) { - $values['is_alias'] = 0; - } - if (!isset($values['alias_redirects'])) { - $values['alias_redirects'] = 0; - } - if ( - !isset($values['action_type']) - && preg_match('#^(.+):.*#', $values['action'], $matches) - ) { - $values['action_type'] = $matches[1]; - } - if ($values['is_alias']) { - $values['is_original'] = 1; - } - if ($values['action'] === self::NOP_ACTION) { - $values['is_original'] = 0; - } - - $query = $this->connection->createQueryBuilder(); - $query->insert($this->connection->quoteIdentifier($this->table)); - foreach ($values as $columnName => $value) { - $query->setValue( - $columnName, - $query->createNamedParameter( - $value, - self::URL_ALIAS_DATA_COLUMN_TYPE_MAP[$columnName], - ":{$columnName}" - ) - ); - } - $query->execute(); - - return (int)$values['id']; - } - - public function getNextId(): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::INCR_TABLE) - ->values( - [ - 'id' => $this->dbPlatform->supportsSequences() - ? sprintf('NEXTVAL(\'%s\')', self::INCR_TABLE_SEQ) - : $query->createPositionalParameter(null, ParameterType::NULL), - ] - ); - - $query->execute(); - - return (int)$this->connection->lastInsertId(self::INCR_TABLE_SEQ); - } - - public function loadRow(int $parentId, string $textMD5): array - { - $query = $this->connection->createQueryBuilder(); - $query->select('*')->from( - $this->connection->quoteIdentifier($this->table) - )->where( - $query->expr()->andX( - $query->expr()->eq( - 'parent', - $query->createPositionalParameter( - $parentId, - ParameterType::INTEGER - ) - ), - $query->expr()->eq( - 'text_md5', - $query->createPositionalParameter( - $textMD5, - ParameterType::STRING - ) - ) - ) - ); - - $result = $query->execute()->fetch(FetchMode::ASSOCIATIVE); - - return false !== $result ? $result : []; - } - - public function loadUrlAliasData(array $urlHashes): array - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - - $count = count($urlHashes); - foreach ($urlHashes as $level => $urlPartHash) { - $tableAlias = $level !== $count - 1 ? $this->table . $level : 'u'; - $query - ->addSelect( - array_map( - static function (string $columnName) use ($tableAlias) { - // do not alias data for top level url part - $columnAlias = 'u' === $tableAlias - ? $columnName - : "{$tableAlias}_{$columnName}"; - $columnName = "{$tableAlias}.{$columnName}"; - - return "{$columnName} AS {$columnAlias}"; - }, - array_keys(self::URL_ALIAS_DATA_COLUMN_TYPE_MAP) - ) - ) - ->from($this->connection->quoteIdentifier($this->table), $tableAlias); - - $query - ->andWhere( - $expr->eq( - "{$tableAlias}.text_md5", - $query->createPositionalParameter($urlPartHash, ParameterType::STRING) - ) - ) - ->andWhere( - $expr->eq( - "{$tableAlias}.parent", - // root entry has parent column set to 0 - isset($previousTableName) ? $previousTableName . '.link' : $query->createPositionalParameter( - 0, - ParameterType::INTEGER - ) - ) - ); - - $previousTableName = $tableAlias; - } - $query->setMaxResults(1); - - $result = $query->execute()->fetch(FetchMode::ASSOCIATIVE); - - return false !== $result ? $result : []; - } - - public function loadAutogeneratedEntry(string $action, ?int $parentId = null): array - { - $query = $this->connection->createQueryBuilder(); - $query->select( - '*' - )->from( - $this->connection->quoteIdentifier($this->table) - )->where( - $query->expr()->andX( - $query->expr()->eq( - 'action', - $query->createPositionalParameter($action, ParameterType::STRING) - ), - $query->expr()->eq( - 'is_original', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'is_alias', - $query->createPositionalParameter(0, ParameterType::INTEGER) - ) - ) - ); - - if (isset($parentId)) { - $query->andWhere( - $query->expr()->eq( - 'parent', - $query->createPositionalParameter( - $parentId, - ParameterType::INTEGER - ) - ) - ); - } - - $entry = $query->execute()->fetch(FetchMode::ASSOCIATIVE); - - return false !== $entry ? $entry : []; - } - - public function loadPathData(int $id): array - { - $pathData = []; - - while ($id != 0) { - $query = $this->connection->createQueryBuilder(); - $query->select( - 'parent', - 'lang_mask', - 'text' - )->from( - $this->connection->quoteIdentifier($this->table) - )->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - - $statement = $query->execute(); - - $rows = $statement->fetchAll(FetchMode::ASSOCIATIVE); - if (empty($rows)) { - // Normally this should never happen - $pathDataArray = []; - foreach ($pathData as $path) { - if (!isset($path[0]['text'])) { - continue; - } - - $pathDataArray[] = $path[0]['text']; - } - - $path = implode('/', $pathDataArray); - throw new BadStateException( - 'id', - "Unable to load path data, path '{$path}' is broken, alias with ID '{$id}' not found. " . - 'To fix all broken paths run the ezplatform:urls:regenerate-aliases command' - ); - } - - $id = $rows[0]['parent']; - array_unshift($pathData, $rows); - } - - return $pathData; - } - - public function loadPathDataByHierarchy(array $hierarchyData): array - { - $query = $this->connection->createQueryBuilder(); - - $hierarchyConditions = []; - foreach ($hierarchyData as $levelData) { - $hierarchyConditions[] = $query->expr()->andX( - $query->expr()->eq( - 'parent', - $query->createPositionalParameter( - $levelData['parent'], - ParameterType::INTEGER - ) - ), - $query->expr()->eq( - 'action', - $query->createPositionalParameter( - $levelData['action'], - ParameterType::STRING - ) - ), - $query->expr()->eq( - 'id', - $query->createPositionalParameter( - $levelData['id'], - ParameterType::INTEGER - ) - ) - ); - } - - $query->select( - 'action', - 'lang_mask', - 'text' - )->from( - $this->connection->quoteIdentifier($this->table) - )->where( - $query->expr()->orX(...$hierarchyConditions) - ); - - $statement = $query->execute(); - - $rows = $statement->fetchAll(FetchMode::ASSOCIATIVE); - $rowsMap = []; - foreach ($rows as $row) { - $rowsMap[$row['action']][] = $row; - } - - if (count($rowsMap) !== count($hierarchyData)) { - throw new RuntimeException('The path is corrupted.'); - } - - $data = []; - foreach ($hierarchyData as $levelData) { - $data[] = $rowsMap[$levelData['action']]; - } - - return $data; - } - - public function removeCustomAlias(int $parentId, string $textMD5): bool - { - $query = $this->connection->createQueryBuilder(); - $query->delete( - $this->connection->quoteIdentifier($this->table) - )->where( - $query->expr()->andX( - $query->expr()->eq( - 'parent', - $query->createPositionalParameter( - $parentId, - ParameterType::INTEGER - ) - ), - $query->expr()->eq( - 'text_md5', - $query->createPositionalParameter( - $textMD5, - ParameterType::STRING - ) - ), - $query->expr()->eq( - 'is_alias', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ) - ) - ); - - return $query->execute() === 1; - } - - public function remove(string $action, ?int $id = null): void - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->delete($this->connection->quoteIdentifier($this->table)) - ->where( - $expr->eq( - 'action', - $query->createPositionalParameter($action, ParameterType::STRING) - ) - ); - - if ($id !== null) { - $query - ->andWhere( - $expr->eq( - 'is_alias', - $query->createPositionalParameter(0, ParameterType::INTEGER) - ), - ) - ->andWhere( - $expr->eq( - 'id', - $query->createPositionalParameter( - $id, - ParameterType::INTEGER - ) - ) - ); - } - - $query->execute(); - } - - public function loadAutogeneratedEntries(int $parentId, bool $includeHistory = false): array - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select('*') - ->from($this->connection->quoteIdentifier($this->table)) - ->where( - $expr->eq( - 'parent', - $query->createPositionalParameter( - $parentId, - ParameterType::INTEGER - ) - ), - ) - ->andWhere( - $expr->eq( - 'action_type', - $query->createPositionalParameter( - 'eznode', - ParameterType::STRING - ) - ) - ) - ->andWhere( - $expr->eq( - 'is_alias', - $query->createPositionalParameter(0, ParameterType::INTEGER) - ) - ); - - if (!$includeHistory) { - $query->andWhere( - $expr->eq( - 'is_original', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ) - ); - } - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function getLocationContentMainLanguageId(int $locationId): int - { - $queryBuilder = $this->connection->createQueryBuilder(); - $expr = $queryBuilder->expr(); - $queryBuilder - ->select('c.initial_language_id') - ->from('ezcontentobject', 'c') - ->join('c', 'ezcontentobject_tree', 't', $expr->eq('t.contentobject_id', 'c.id')) - ->where( - $expr->eq('t.node_id', ':locationId') - ) - ->setParameter('locationId', $locationId, ParameterType::INTEGER); - - $statement = $queryBuilder->execute(); - $languageId = $statement->fetchColumn(); - - if ($languageId === false) { - throw new RuntimeException("Could not find Content for Location #{$locationId}"); - } - - return (int)$languageId; - } - - public function bulkRemoveTranslation(int $languageId, array $actions): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update($this->connection->quoteIdentifier($this->table)) - // parameter for bitwise operation has to be placed verbatim (w/o binding) for this to work cross-DBMS - ->set('lang_mask', 'lang_mask & ~ ' . $languageId) - ->where('action IN (:actions)') - ->setParameter(':actions', $actions, Connection::PARAM_STR_ARRAY); - $query->execute(); - - // cleanup: delete single language rows (including alwaysAvailable) - $query = $this->connection->createQueryBuilder(); - $query - ->delete($this->connection->quoteIdentifier($this->table)) - ->where('action IN (:actions)') - ->andWhere('lang_mask IN (0, 1)') - ->setParameter(':actions', $actions, Connection::PARAM_STR_ARRAY); - $query->execute(); - } - - public function archiveUrlAliasesForDeletedTranslations( - int $locationId, - int $parentId, - array $languageIds - ): void { - // determine proper parent for linking historized entry - $existingLocationEntry = $this->loadAutogeneratedEntry( - 'eznode:' . $locationId, - $parentId - ); - - // filter existing URL alias entries by any of the specified removed languages - $rows = $this->loadLocationEntriesMatchingMultipleLanguages( - $locationId, - $languageIds - ); - - // remove specific languages from a bit mask - foreach ($rows as $row) { - // filter mask to reduce the number of calls to storage engine - $rowLanguageMask = (int)$row['lang_mask']; - $languageIdsToBeRemoved = array_filter( - $languageIds, - static function ($languageId) use ($rowLanguageMask) { - return $languageId & $rowLanguageMask; - } - ); - - if (empty($languageIdsToBeRemoved)) { - continue; - } - - // use existing entry to link archived alias or use current alias id - $linkToId = !empty($existingLocationEntry) - ? (int)$existingLocationEntry['id'] - : (int)$row['id']; - foreach ($languageIdsToBeRemoved as $languageId) { - $this->archiveUrlAliasForDeletedTranslation( - (int)$row['lang_mask'], - (int)$languageId, - (int)$row['parent'], - $row['text_md5'], - $linkToId - ); - } - } - } - - /** - * Load list of aliases for given $locationId matching any of the specified Languages. - * - * @param int[] $languageIds - */ - private function loadLocationEntriesMatchingMultipleLanguages( - int $locationId, - array $languageIds - ): array { - // note: alwaysAvailable for this use case is not relevant - $languageMask = $this->languageMaskGenerator->generateLanguageMaskFromLanguageIds( - $languageIds, - false - ); - - /** @var \Doctrine\DBAL\Connection $connection */ - $query = $this->connection->createQueryBuilder(); - $query - ->select('id', 'lang_mask', 'parent', 'text_md5') - ->from($this->connection->quoteIdentifier($this->table)) - ->where('action = :action') - // fetch rows matching any of the given Languages - ->andWhere('lang_mask & :languageMask <> 0') - ->setParameter(':action', 'eznode:' . $locationId) - ->setParameter(':languageMask', $languageMask); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function deleteUrlAliasesWithoutLocation(): int - { - $dbPlatform = $this->connection->getDatabasePlatform(); - - $subQuery = $this->connection->createQueryBuilder(); - $subQuery - ->select('node_id') - ->from('ezcontentobject_tree', 't') - ->where( - $subQuery->expr()->eq( - 't.node_id', - sprintf( - 'CAST(%s as %s)', - $dbPlatform->getSubstringExpression( - $this->connection->quoteIdentifier($this->table) . '.action', - 8 - ), - $this->getIntegerType() - ) - ) - ); - - $deleteQuery = $this->connection->createQueryBuilder(); - $deleteQuery - ->delete($this->connection->quoteIdentifier($this->table)) - ->where( - $deleteQuery->expr()->eq( - 'action_type', - $deleteQuery->createPositionalParameter('eznode') - ) - ) - ->andWhere( - sprintf('NOT EXISTS (%s)', $subQuery->getSQL()) - ); - - return $deleteQuery->execute(); - } - - public function deleteUrlAliasesWithoutParent(): int - { - $existingAliasesQuery = $this->getAllUrlAliasesQuery(); - - $query = $this->connection->createQueryBuilder(); - $query - ->delete($this->connection->quoteIdentifier($this->table)) - ->where( - $query->expr()->neq( - 'parent', - $query->createPositionalParameter(0, ParameterType::INTEGER) - ) - ) - ->andWhere( - $query->expr()->notIn( - 'parent', - $existingAliasesQuery - ) - ); - - return $query->execute(); - } - - public function deleteUrlAliasesWithBrokenLink(): int - { - $existingAliasesQuery = $this->getAllUrlAliasesQuery(); - - $query = $this->connection->createQueryBuilder(); - $query - ->delete($this->connection->quoteIdentifier($this->table)) - ->where( - $query->expr()->neq('id', 'link') - ) - ->andWhere( - $query->expr()->notIn( - 'link', - $existingAliasesQuery - ) - ); - - return (int)$query->execute(); - } - - public function repairBrokenUrlAliasesForLocation(int $locationId): void - { - $urlAliasesData = $this->getUrlAliasesForLocation($locationId); - - $originalUrlAliases = $this->filterOriginalAliases($urlAliasesData); - - if (count($originalUrlAliases) === count($urlAliasesData)) { - // no archived aliases - nothing to fix - return; - } - - $updateQueryBuilder = $this->connection->createQueryBuilder(); - $expr = $updateQueryBuilder->expr(); - $updateQueryBuilder - ->update($this->connection->quoteIdentifier($this->table)) - ->set('link', ':linkId') - ->set('parent', ':newParentId') - ->where( - $expr->eq('action', ':action') - ) - ->andWhere( - $expr->eq( - 'is_original', - $updateQueryBuilder->createNamedParameter(0, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq('parent', ':oldParentId') - ) - ->andWhere( - $expr->eq('text_md5', ':textMD5') - ) - ->setParameter(':action', "eznode:{$locationId}"); - - foreach ($urlAliasesData as $urlAliasData) { - if ($urlAliasData['is_original'] === 1 || !isset($originalUrlAliases[$urlAliasData['lang_mask']])) { - // ignore non-archived entries and deleted Translations - continue; - } - - $originalUrlAlias = $originalUrlAliases[$urlAliasData['lang_mask']]; - - if ($urlAliasData['link'] === $originalUrlAlias['link']) { - // ignore correct entries to avoid unnecessary updates - continue; - } - - $updateQueryBuilder - ->setParameter(':linkId', $originalUrlAlias['link'], ParameterType::INTEGER) - // attempt to fix missing parent case - ->setParameter( - ':newParentId', - $urlAliasData['existing_parent'] ?? $originalUrlAlias['parent'], - ParameterType::INTEGER - ) - ->setParameter(':oldParentId', $urlAliasData['parent'], ParameterType::INTEGER) - ->setParameter(':textMD5', $urlAliasData['text_md5']); - - try { - $updateQueryBuilder->execute(); - } catch (UniqueConstraintViolationException $e) { - // edge case: if such row already exists, there's no way to restore history - $this->deleteRow((int) $urlAliasData['parent'], $urlAliasData['text_md5']); - } - } - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function deleteUrlNopAliasesWithoutChildren(): int - { - $platform = $this->connection->getDatabasePlatform(); - $queryBuilder = $this->connection->createQueryBuilder(); - - // The wrapper select is needed for SQL "Derived Table Merge" issue for deleting - $wrapperQueryBuilder = clone $queryBuilder; - $selectQueryBuilder = clone $queryBuilder; - $expressionBuilder = $queryBuilder->expr(); - - $selectQueryBuilder - ->select('u_parent.id AS inner_id') - ->from($this->table, 'u_parent') - ->leftJoin( - 'u_parent', - $this->table, - 'u', - $expressionBuilder->eq('u_parent.id', 'u.parent') - ) - ->where( - $expressionBuilder->eq( - 'u_parent.action_type', - ':actionType' - ) - ) - ->groupBy('u_parent.id') - ->having( - $expressionBuilder->eq($platform->getCountExpression('u.id'), 0) - ); - - $wrapperQueryBuilder - ->select('inner_id') - ->from( - sprintf('(%s)', $selectQueryBuilder), - 'wrapper' - ) - ->where('id = inner_id'); - - $queryBuilder - ->delete($this->table) - ->where( - sprintf('EXISTS (%s)', $wrapperQueryBuilder) - ) - ->setParameter('actionType', self::NOP); - - return $queryBuilder->execute(); - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function getAllChildrenAliases(int $parentId): array - { - $queryBuilder = $this->connection->createQueryBuilder(); - $expressionBuilder = $queryBuilder->expr(); - - $queryBuilder - ->select('parent', 'text_md5') - ->from($this->table) - ->where( - $expressionBuilder->eq( - 'parent', - $queryBuilder->createPositionalParameter($parentId, ParameterType::INTEGER) - ) - )->andWhere( - $expressionBuilder->eq( - 'is_alias', - $queryBuilder->createPositionalParameter(1, ParameterType::INTEGER) - ) - ); - - return $queryBuilder->execute()->fetchAll(); - } - - /** - * Filter from the given result set original (current) only URL aliases and index them by language_mask. - * - * Note: each language_mask can have one URL Alias. - * - * @param array $urlAliasesData - */ - private function filterOriginalAliases(array $urlAliasesData): array - { - $originalUrlAliases = array_filter( - $urlAliasesData, - static function ($urlAliasData) { - // filter is_original=true ignoring broken parent records (cleaned up elsewhere) - return (bool)$urlAliasData['is_original'] && $urlAliasData['existing_parent'] !== null; - } - ); - - // return language_mask-indexed array - return array_combine( - array_column($originalUrlAliases, 'lang_mask'), - $originalUrlAliases - ); - } - - /** - * Get sub-query for IDs of all URL aliases. - */ - private function getAllUrlAliasesQuery(): string - { - $existingAliasesQueryBuilder = $this->connection->createQueryBuilder(); - $innerQueryBuilder = $this->connection->createQueryBuilder(); - - return $existingAliasesQueryBuilder - ->select('tmp.id') - ->from( - // nest sub-query to avoid same-table update error - '(' . $innerQueryBuilder->select('id')->from( - $this->connection->quoteIdentifier($this->table) - )->getSQL() . ')', - 'tmp' - ) - ->getSQL(); - } - - /** - * Get DBMS-specific integer type. - */ - private function getIntegerType(): string - { - return $this->dbPlatform->getName() === 'mysql' ? 'signed' : 'integer'; - } - - /** - * Get all URL aliases for the given Location (including archived ones). - */ - private function getUrlAliasesForLocation(int $locationId): array - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder - ->select( - 't1.id', - 't1.is_original', - 't1.lang_mask', - 't1.link', - 't1.parent', - // show existing parent only if its row exists, special case for root parent - 'CASE t1.parent WHEN 0 THEN 0 ELSE t2.id END AS existing_parent', - 't1.text_md5' - ) - ->from($this->connection->quoteIdentifier($this->table), 't1') - // selecting t2.id above will result in null if parent is broken - ->leftJoin( - 't1', - $this->connection->quoteIdentifier($this->table), - 't2', - $queryBuilder->expr()->eq('t1.parent', 't2.id') - ) - ->where( - $queryBuilder->expr()->eq( - 't1.action', - $queryBuilder->createPositionalParameter("eznode:{$locationId}") - ) - ); - - return $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - /** - * Delete URL alias row by its primary composite key. - */ - private function deleteRow(int $parentId, string $textMD5): int - { - $queryBuilder = $this->connection->createQueryBuilder(); - $expr = $queryBuilder->expr(); - $queryBuilder - ->delete($this->connection->quoteIdentifier($this->table)) - ->where( - $expr->eq( - 'parent', - $queryBuilder->createPositionalParameter($parentId, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - 'text_md5', - $queryBuilder->createPositionalParameter($textMD5) - ) - ) - ; - - return $queryBuilder->execute(); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php deleted file mode 100644 index 3d2bd7ff43..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,312 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway; -use PDOException; - -/** - * @internal Internal exception conversion layer. - */ -final class ExceptionConversion extends Gateway -{ - /** - * The wrapped gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway - */ - private $innerGateway; - - /** - * Creates a new exception conversion gateway around $innerGateway. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function setTable(string $name): void - { - try { - $this->innerGateway->setTable($name); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadAllLocationEntries(int $locationId): array - { - try { - return $this->innerGateway->loadAllLocationEntries($locationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadLocationEntries( - int $locationId, - bool $custom = false, - ?int $languageId = null - ): array { - try { - return $this->innerGateway->loadLocationEntries($locationId, $custom); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function isRootEntry(int $id): bool - { - try { - return $this->innerGateway->isRootEntry($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function cleanupAfterPublish( - string $action, - int $languageId, - int $newId, - int $parentId, - string $textMD5 - ): void { - try { - $this->innerGateway->cleanupAfterPublish($action, $languageId, $newId, $parentId, $textMD5); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function historizeBeforeSwap(string $action, int $languageMask): void - { - try { - $this->innerGateway->historizeBeforeSwap($action, $languageMask); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function historizeId(int $id, int $link): void - { - try { - $this->innerGateway->historizeId($id, $link); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function reparent(int $oldParentId, int $newParentId): void - { - try { - $this->innerGateway->reparent($oldParentId, $newParentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateRow(int $parentId, string $textMD5, array $values): void - { - try { - $this->innerGateway->updateRow($parentId, $textMD5, $values); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function insertRow(array $values): int - { - try { - return $this->innerGateway->insertRow($values); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadRow(int $parentId, string $textMD5): array - { - try { - return $this->innerGateway->loadRow($parentId, $textMD5); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadAutogeneratedEntry(string $action, ?int $parentId = null): array - { - try { - return $this->innerGateway->loadAutogeneratedEntry($action, $parentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function remove(string $action, ?int $id = null): void - { - try { - $this->innerGateway->remove($action, $id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function listGlobalEntries( - ?string $languageCode = null, - int $offset = 0, - int $limit = -1 - ): array { - try { - return $this->innerGateway->listGlobalEntries($languageCode, $offset, $limit); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function removeCustomAlias(int $parentId, string $textMD5): bool - { - try { - return $this->innerGateway->removeCustomAlias($parentId, $textMD5); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadUrlAliasData(array $urlHashes): array - { - try { - return $this->innerGateway->loadUrlAliasData($urlHashes); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadPathData(int $id): array - { - try { - return $this->innerGateway->loadPathData($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadPathDataByHierarchy(array $hierarchyData): array - { - try { - return $this->innerGateway->loadPathDataByHierarchy($hierarchyData); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadAutogeneratedEntries(int $parentId, bool $includeHistory = false): array - { - try { - return $this->innerGateway->loadAutogeneratedEntries($parentId, $includeHistory); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getNextId(): int - { - try { - return $this->innerGateway->getNextId(); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getLocationContentMainLanguageId(int $locationId): int - { - try { - return $this->innerGateway->getLocationContentMainLanguageId($locationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function bulkRemoveTranslation(int $languageId, array $actions): void - { - try { - $this->innerGateway->bulkRemoveTranslation($languageId, $actions); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function archiveUrlAliasesForDeletedTranslations( - int $locationId, - int $parentId, - array $languageIds - ): void { - try { - $this->innerGateway->archiveUrlAliasesForDeletedTranslations($locationId, $parentId, $languageIds); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteUrlAliasesWithoutLocation(): int - { - try { - return $this->innerGateway->deleteUrlAliasesWithoutLocation(); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteUrlAliasesWithoutParent(): int - { - try { - return $this->innerGateway->deleteUrlAliasesWithoutParent(); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteUrlAliasesWithBrokenLink(): int - { - try { - return $this->innerGateway->deleteUrlAliasesWithBrokenLink(); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function repairBrokenUrlAliasesForLocation(int $locationId): void - { - try { - $this->innerGateway->repairBrokenUrlAliasesForLocation($locationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteUrlNopAliasesWithoutChildren(): int - { - try { - return $this->innerGateway->deleteUrlNopAliasesWithoutChildren(); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function getAllChildrenAliases(int $parentId): array - { - try { - return $this->innerGateway->getAllChildrenAliases($parentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Handler.php deleted file mode 100644 index d9b8fc48c8..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Handler.php +++ /dev/null @@ -1,1203 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias; - -use Doctrine\DBAL\Exception\UniqueConstraintViolationException; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\ForbiddenException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\DTO\SwappedLocationProperties; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\DTO\UrlAliasForSwappedLocation; -use eZ\Publish\SPI\Persistence\Content\Language; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use eZ\Publish\SPI\Persistence\Content\UrlAlias; -use eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler as UrlAliasHandlerInterface; -use eZ\Publish\SPI\Persistence\TransactionHandler; - -/** - * The UrlAlias Handler provides nice urls management. - * - * Its methods operate on a representation of the url alias data structure held - * inside a storage engine. - */ -class Handler implements UrlAliasHandlerInterface -{ - public const ROOT_LOCATION_ID = 1; - - /** - * This is intentionally hardcoded for now as: - * 1. We don't implement this configuration option. - * 2. Such option should not be in this layer, should be handled higher up. - * - * @deprecated - */ - public const CONTENT_REPOSITORY_ROOT_LOCATION_ID = 2; - - /** - * The maximum level of alias depth. - */ - public const MAX_URL_ALIAS_DEPTH_LEVEL = 60; - - /** - * UrlAlias Gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway - */ - protected $gateway; - - /** - * Gateway for handling location data. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway - */ - protected $locationGateway; - - /** - * UrlAlias Mapper. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Mapper - */ - protected $mapper; - - /** - * Caching language handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler - */ - protected $languageHandler; - - /** - * URL slug converter. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter - */ - protected $slugConverter; - - /** - * Gateway for handling content data. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway - */ - protected $contentGateway; - - /** - * Language mask generator. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator - */ - protected $maskGenerator; - - /** @var \eZ\Publish\SPI\Persistence\TransactionHandler */ - private $transactionHandler; - - /** - * Creates a new UrlAlias Handler. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway $gateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Mapper $mapper - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway $locationGateway - * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $languageHandler - * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter $slugConverter - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Gateway $contentGateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator $maskGenerator - * @param \eZ\Publish\SPI\Persistence\TransactionHandler $transactionHandler - */ - public function __construct( - Gateway $gateway, - Mapper $mapper, - LocationGateway $locationGateway, - LanguageHandler $languageHandler, - SlugConverter $slugConverter, - ContentGateway $contentGateway, - MaskGenerator $maskGenerator, - TransactionHandler $transactionHandler - ) { - $this->gateway = $gateway; - $this->mapper = $mapper; - $this->locationGateway = $locationGateway; - $this->languageHandler = $languageHandler; - $this->slugConverter = $slugConverter; - $this->contentGateway = $contentGateway; - $this->maskGenerator = $maskGenerator; - $this->transactionHandler = $transactionHandler; - } - - public function publishUrlAliasForLocation( - $locationId, - $parentLocationId, - $name, - $languageCode, - $alwaysAvailable = false, - $updatePathIdentificationString = false - ): string { - $languageId = $this->languageHandler->loadByLanguageCode($languageCode)->id; - - return $this->internalPublishUrlAliasForLocation( - $locationId, - $parentLocationId, - $name, - $languageId, - $alwaysAvailable, - $updatePathIdentificationString - ); - } - - /** - * Internal publish method, accepting language ID instead of language code and optionally - * new alias ID (used when swapping Locations). - * - * @see \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationSwapped() - * - * @param int $locationId - * @param int $parentLocationId - * @param string $name - * @param int $languageId - * @param bool $alwaysAvailable - * @param bool $updatePathIdentificationString legacy storage specific for updating ezcontentobject_tree.path_identification_string - * @param int $newId - */ - private function internalPublishUrlAliasForLocation( - $locationId, - $parentLocationId, - $name, - $languageId, - $alwaysAvailable = false, - $updatePathIdentificationString = false, - $newId = null - ): string { - $parentId = $this->getRealAliasId($parentLocationId); - $name = $this->slugConverter->convert($name, 'location_' . $locationId); - $uniqueCounter = $this->slugConverter->getUniqueCounterValue($name, $parentId == 0); - $languageMask = $languageId | (int)$alwaysAvailable; - $action = 'eznode:' . $locationId; - $cleanup = false; - - // Exiting the loop with break; - while (true) { - $newText = ''; - if ($locationId != self::CONTENT_REPOSITORY_ROOT_LOCATION_ID) { - $newText = $name . ($name == '' || $uniqueCounter > 1 ? $uniqueCounter : ''); - } - $newTextMD5 = $this->getHash($newText); - - // Try to load existing entry - $row = $this->gateway->loadRow($parentId, $newTextMD5); - - // If nothing was returned insert new entry - if (empty($row)) { - // Check for existing active location entry on this level and reuse it's id - $existingLocationEntry = $this->gateway->loadAutogeneratedEntry($action, $parentId); - if (!empty($existingLocationEntry)) { - $cleanup = true; - $newId = $existingLocationEntry['id']; - } - - try { - $newId = $this->gateway->insertRow( - [ - 'id' => $newId, - 'link' => $newId, - 'parent' => $parentId, - 'action' => $action, - 'lang_mask' => $languageMask, - 'text' => $newText, - 'text_md5' => $newTextMD5, - ] - ); - } catch (\RuntimeException $e) { - while ($e->getPrevious() !== null) { - $e = $e->getPrevious(); - if ($e instanceof UniqueConstraintViolationException) { - // Concurrency! someone else inserted the same row that we where going to. - // let's do another loop pass - ++$uniqueCounter; - continue 2; - } - } - - throw $e; - } - - break; - } - - // Row exists, check if it is reusable. There are 3 cases when this is possible: - // 1. NOP entry - // 2. existing location or custom alias entry - // 3. history entry - if ( - $row['action'] === Gateway::NOP_ACTION || - $row['action'] === $action || - (int)$row['is_original'] === 0 - ) { - // Check for existing location entry on this level, if it exists and it's id differs from reusable - // entry id then reusable entry should be updated with the existing location entry id. - // Note: existing location entry may be downgraded and relinked later, depending on its language. - $existingLocationEntry = $this->gateway->loadAutogeneratedEntry($action, $parentId); - - if (!empty($existingLocationEntry)) { - // Always cleanup when active autogenerated entry exists on the same level - $cleanup = true; - $newId = $existingLocationEntry['id']; - if ($existingLocationEntry['id'] == $row['id']) { - // If we are reusing existing location entry merge existing language mask - $languageMask |= ($row['lang_mask'] & ~1); - } - } elseif ($newId === null) { - // Use reused row ID only if publishing normally, else use given $newId - $newId = $row['id']; - } - - $this->gateway->updateRow( - $parentId, - $newTextMD5, - [ - 'action' => $action, - // In case when NOP row was reused - 'action_type' => 'eznode', - 'lang_mask' => $languageMask, - // Updating text ensures that letter case changes are stored - 'text' => $newText, - // Set "id" and "link" for case when reusable entry is history - 'id' => $newId, - 'link' => $newId, - // Entry should be active location entry (original and not alias). - // Note: this takes care of taking over custom alias entry for the location on the same level - // and with same name and action. - 'alias_redirects' => 1, - 'is_original' => 1, - 'is_alias' => 0, - ] - ); - - break; - } - - // If existing row is not reusable, increment $uniqueCounter and try again - ++$uniqueCounter; - } - - /* @var $newText */ - if ($updatePathIdentificationString) { - $this->locationGateway->updatePathIdentificationString( - $locationId, - $parentLocationId, - $this->slugConverter->convert($newText, 'node_' . $locationId, 'urlalias_compat') - ); - } - - /* @var $newId */ - /* @var $newTextMD5 */ - // Note: cleanup does not touch custom and global entries - if ($cleanup) { - $this->gateway->cleanupAfterPublish($action, $languageId, $newId, $parentId, $newTextMD5); - } - - return $this->mapper->generateIdentityKey($parentId, $newTextMD5); - } - - /** - * Create a user chosen $alias pointing to $locationId in $languageCode. - * - * If $languageCode is null the $alias is created in the system's default - * language. $alwaysAvailable makes the alias available in all languages. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * - * @param mixed $locationId - * @param string $path - * @param bool $forwarding - * @param string $languageCode - * @param bool $alwaysAvailable - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias - */ - public function createCustomUrlAlias($locationId, $path, $forwarding = false, $languageCode = null, $alwaysAvailable = false) - { - return $this->createUrlAlias( - 'eznode:' . $locationId, - $path, - $forwarding, - $languageCode, - $alwaysAvailable - ); - } - - /** - * Create a user chosen $alias pointing to a resource in $languageCode. - * This method does not handle location resources - if a user enters a location target - * the createCustomUrlAlias method has to be used. - * - * If $languageCode is null the $alias is created in the system's default - * language. $alwaysAvailable makes the alias available in all languages. - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException if the path already exists for the given resource - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the path is broken - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * - * @param string $resource - * @param string $path - * @param bool $forwarding - * @param string $languageCode - * @param bool $alwaysAvailable - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias - */ - public function createGlobalUrlAlias($resource, $path, $forwarding = false, $languageCode = null, $alwaysAvailable = false) - { - return $this->createUrlAlias( - $resource, - $path, - $forwarding, - $languageCode, - $alwaysAvailable - ); - } - - /** - * Internal method for creating global or custom URL alias (these are handled in the same way). - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException if the path already exists for the given context - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * - * @param string $action - * @param string $path - * @param bool $forward - * @param string|null $languageCode - * @param bool $alwaysAvailable - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias - */ - protected function createUrlAlias($action, $path, $forward, $languageCode, $alwaysAvailable): UrlAlias - { - $pathElements = explode('/', $path); - $topElement = array_pop($pathElements); - $languageId = $this->languageHandler->loadByLanguageCode($languageCode)->id; - $parentId = 0; - - // Handle all path elements except topmost one - $isPathNew = false; - foreach ($pathElements as $level => $pathElement) { - $pathElement = $this->slugConverter->convert($pathElement, 'noname' . ($level + 1)); - $pathElementMD5 = $this->getHash($pathElement); - if (!$isPathNew) { - $row = $this->gateway->loadRow($parentId, $pathElementMD5); - if (empty($row)) { - $isPathNew = true; - } else { - $parentId = $row['link']; - } - } - - if ($isPathNew) { - $parentId = $this->insertNopEntry($parentId, $pathElement, $pathElementMD5); - } - } - - // Handle topmost path element - $topElement = $this->slugConverter->convert( - $topElement, - 'noname' . (count($pathElements) + 1) - ); - - // If last (next to topmost) entry parent is special root entry we handle topmost entry as first level entry - // That is why we need to reset $parentId to 0 - if ($parentId !== 0 && $this->gateway->isRootEntry($parentId)) { - $parentId = 0; - } - - $topElementMD5 = $this->getHash($topElement); - // Set common values for two cases below - $data = [ - 'action' => $action, - 'is_alias' => 1, - 'alias_redirects' => $forward ? 1 : 0, - 'parent' => $parentId, - 'text' => $topElement, - 'text_md5' => $topElementMD5, - 'is_original' => 1, - ]; - // Try to load topmost element - if (!$isPathNew) { - $row = $this->gateway->loadRow($parentId, $topElementMD5); - } - - // If nothing was returned perform insert - if ($isPathNew || empty($row)) { - $data['lang_mask'] = $languageId | (int)$alwaysAvailable; - $id = $this->gateway->insertRow($data); - } elseif ($row['action'] === Gateway::NOP_ACTION || (int)$row['is_original'] === 0) { - // Row exists, check if it is reusable. There are 2 cases when this is possible: - // 1. NOP entry - // 2. history entry - $data['lang_mask'] = $languageId | (int)$alwaysAvailable; - // If history is reused move link to id - $data['link'] = $id = $row['id']; - $this->gateway->updateRow( - $parentId, - $topElementMD5, - $data - ); - } elseif ( - $row['action'] === $action && - (int)$row['is_alias'] === 1 && - 0 === ((int)$row['lang_mask'] & $languageId) - ) { - // add another language to the same custom alias - $data['link'] = $id = $row['id']; - $data['lang_mask'] = $row['lang_mask'] | $languageId | (int)$alwaysAvailable; - $this->gateway->updateRow( - $parentId, - $topElementMD5, - $data - ); - } else { - throw new ForbiddenException( - "Path '%path%' already exists for the given context", - ['%path%' => $path] - ); - } - - $data['raw_path_data'] = $this->gateway->loadPathData($id); - - return $this->mapper->extractUrlAliasFromData($data); - } - - /** - * Convenience method for inserting nop type row. - * - * @param mixed $parentId - * @param string $text - * @param string $textMD5 - * - * @return mixed - */ - protected function insertNopEntry($parentId, $text, $textMD5) - { - return $this->gateway->insertRow( - [ - 'lang_mask' => 1, - 'action' => Gateway::NOP_ACTION, - 'parent' => $parentId, - 'text' => $text, - 'text_md5' => $textMD5, - ] - ); - } - - /** - * List of user generated or autogenerated url entries, pointing to $locationId. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * - * @param mixed $locationId - * @param bool $custom if true the user generated aliases are listed otherwise the autogenerated - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias[] - */ - public function listURLAliasesForLocation($locationId, $custom = false) - { - $data = $this->gateway->loadLocationEntries($locationId, $custom); - foreach ($data as &$entry) { - $entry['raw_path_data'] = $this->gateway->loadPathData($entry['id']); - } - - return $this->mapper->extractUrlAliasListFromData($data); - } - - /** - * List global aliases. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * - * @param string|null $languageCode - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias[] - */ - public function listGlobalURLAliases($languageCode = null, $offset = 0, $limit = -1) - { - $data = $this->gateway->listGlobalEntries($languageCode, $offset, $limit); - foreach ($data as &$entry) { - $entry['raw_path_data'] = $this->gateway->loadPathData($entry['id']); - } - - return $this->mapper->extractUrlAliasListFromData($data); - } - - /** - * Removes url aliases. - * - * Autogenerated aliases are not removed by this method. - * - * @param \eZ\Publish\SPI\Persistence\Content\UrlAlias[] $urlAliases - * - * @return bool - */ - public function removeURLAliases(array $urlAliases) - { - foreach ($urlAliases as $urlAlias) { - if ($urlAlias->isCustom) { - list($parentId, $textMD5) = explode('-', $urlAlias->id); - if (!$this->gateway->removeCustomAlias($parentId, $textMD5)) { - return false; - } - } - } - - return true; - } - - /** - * Looks up a url alias for the given url. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * - * @param string $url - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias - */ - public function lookup($url) - { - $urlHashes = []; - foreach (explode('/', $url) as $level => $text) { - $urlHashes[$level] = $this->getHash($text); - } - - $pathDepth = count($urlHashes); - if ($pathDepth > self::MAX_URL_ALIAS_DEPTH_LEVEL) { - throw new InvalidArgumentException('$urlHashes', 'Exceeded maximum depth level for content URL alias.'); - } - - $data = $this->gateway->loadUrlAliasData($urlHashes); - if (empty($data)) { - throw new NotFoundException('URLAlias', $url); - } - - $hierarchyData = []; - $isPathHistory = false; - for ($level = 0; $level < $pathDepth; ++$level) { - $prefix = $level === $pathDepth - 1 ? '' : 'ezurlalias_ml' . $level . '_'; - $isPathHistory = $isPathHistory ?: ($data[$prefix . 'link'] != $data[$prefix . 'id']); - $hierarchyData[$level] = [ - 'id' => $data[$prefix . 'id'], - 'parent' => $data[$prefix . 'parent'], - 'action' => $data[$prefix . 'action'], - ]; - } - - $data['is_path_history'] = $isPathHistory; - $data['raw_path_data'] = ($data['action_type'] == 'eznode' && !$data['is_alias']) - ? $this->gateway->loadPathDataByHierarchy($hierarchyData) - : $this->gateway->loadPathData($data['id']); - - return $this->mapper->extractUrlAliasFromData($data); - } - - /** - * Loads URL alias by given $id. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * - * @param string $id - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias - */ - public function loadUrlAlias($id) - { - list($parentId, $textMD5) = explode('-', $id); - $data = $this->gateway->loadRow((int)$parentId, $textMD5); - - if (empty($data)) { - throw new NotFoundException('URLAlias', $id); - } - - $data['raw_path_data'] = $this->gateway->loadPathData((int)$data['id']); - - return $this->mapper->extractUrlAliasFromData($data); - } - - /** - * Notifies the underlying engine that a location has moved. - * - * This method triggers the change of the autogenerated aliases. - * - * @param mixed $locationId - * @param mixed $oldParentId - * @param mixed $newParentId - */ - public function locationMoved($locationId, $oldParentId, $newParentId) - { - if ($oldParentId === $newParentId) { - return $newParentId; - } - - // @todo optimize: $newLocationAliasId is already available in self::publishUrlAliasForLocation() as $newId - $newParentLocationAliasId = $this->getRealAliasId($newParentId); - $newLocationAlias = $this->gateway->loadAutogeneratedEntry( - 'eznode:' . $locationId, - $newParentLocationAliasId - ); - - $oldParentLocationAliasId = $this->getRealAliasId($oldParentId); - $oldLocationAlias = $this->gateway->loadAutogeneratedEntry( - 'eznode:' . $locationId, - $oldParentLocationAliasId - ); - - // Historize alias for old location - $this->gateway->historizeId($oldLocationAlias['id'], $newLocationAlias['id']); - // Reparent subtree of old location to new location - $this->gateway->reparent($oldLocationAlias['id'], $newLocationAlias['id']); - } - - /** - * Notifies the underlying engine that a location was copied. - * - * This method triggers the creation of the autogenerated aliases for the copied locations - * - * @param mixed $locationId - * @param mixed $newLocationId - * @param mixed $newParentId - */ - public function locationCopied($locationId, $newLocationId, $newParentId) - { - $newParentAliasId = $this->getRealAliasId($newLocationId); - $oldParentAliasId = $this->getRealAliasId($locationId); - - $actionMap = $this->getCopiedLocationsMap($locationId, $newLocationId); - - $this->copySubtree( - $actionMap, - $oldParentAliasId, - $newParentAliasId - ); - } - - /** - * Notify the underlying engine that a Location has been swapped. - * - * This method triggers the change of the autogenerated aliases. - * - * @param int $location1Id - * @param int $location1ParentId - * @param int $location2Id - * @param int $location2ParentId - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - */ - public function locationSwapped($location1Id, $location1ParentId, $location2Id, $location2ParentId) - { - $location1 = new SwappedLocationProperties($location1Id, $location1ParentId); - $location2 = new SwappedLocationProperties($location2Id, $location2ParentId); - - $location1->entries = $this->gateway->loadAllLocationEntries($location1Id); - $location2->entries = $this->gateway->loadAllLocationEntries($location2Id); - - $location1->mainLanguageId = $this->gateway->getLocationContentMainLanguageId($location1Id); - $location2->mainLanguageId = $this->gateway->getLocationContentMainLanguageId($location2Id); - - // Load autogenerated entries to find alias ID - $location1->autogeneratedId = $this->gateway->loadAutogeneratedEntry("eznode:{$location1Id}")['id']; - $location2->autogeneratedId = $this->gateway->loadAutogeneratedEntry("eznode:{$location2Id}")['id']; - - $contentInfo1 = $this->contentGateway->loadContentInfoByLocationId($location1Id); - $contentInfo2 = $this->contentGateway->loadContentInfoByLocationId($location2Id); - - $names1 = $this->getNamesForAllLanguages($contentInfo1); - $names2 = $this->getNamesForAllLanguages($contentInfo2); - - $location1->isAlwaysAvailable = $this->maskGenerator->isAlwaysAvailable($contentInfo1['language_mask']); - $location2->isAlwaysAvailable = $this->maskGenerator->isAlwaysAvailable($contentInfo2['language_mask']); - - $languages = $this->languageHandler->loadAll(); - - // Historize everything first to avoid name conflicts in case swapped Locations are siblings - $this->historizeBeforeSwap($location1->entries, $location2->entries); - - foreach ($languages as $languageCode => $language) { - $location1->name = isset($names1[$languageCode]) ? $names1[$languageCode] : null; - $location2->name = isset($names2[$languageCode]) ? $names2[$languageCode] : null; - $urlAliasesForSwappedLocations = $this->getUrlAliasesForSwappedLocations( - $language, - $location1, - $location2 - ); - foreach ($urlAliasesForSwappedLocations as $urlAliasForLocation) { - $this->internalPublishUrlAliasForLocation( - $urlAliasForLocation->id, - $urlAliasForLocation->parentId, - $urlAliasForLocation->name, - $language->id, - $urlAliasForLocation->isAlwaysAvailable, - $urlAliasForLocation->isPathIdentificationStringModified, - $urlAliasForLocation->newId - ); - } - } - - $this->internalPublishCustomUrlAliasForLocation($location1, $contentInfo1['language_mask']); - $this->internalPublishCustomUrlAliasForLocation($location2, $contentInfo2['language_mask']); - } - - /** - * @param array $contentInfo - * - * @return array - */ - private function getNamesForAllLanguages(array $contentInfo) - { - $nameDataArray = $this->contentGateway->loadVersionedNameData([ - [ - 'id' => $contentInfo['id'], - 'version' => $contentInfo['current_version'], - ], - ]); - - $namesForAllLanguages = []; - foreach ($nameDataArray as $nameData) { - $namesForAllLanguages[$nameData['ezcontentobject_name_content_translation']] - = $nameData['ezcontentobject_name_name']; - } - - return $namesForAllLanguages; - } - - /** - * Historizes given existing active entries for two swapped Locations. - * - * This should be done before republishing URL aliases, in order to avoid unnecessary - * conflicts when swapped Locations are siblings. - * - * We need to historize everything separately per language (mask), in case the entries - * remain history future publishing reusages need to be able to take them over cleanly. - * - * @see \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationSwapped() - * - * @param array $location1Entries - * @param array $location2Entries - */ - private function historizeBeforeSwap($location1Entries, $location2Entries) - { - foreach ($location1Entries as $row) { - $this->gateway->historizeBeforeSwap($row['action'], $row['lang_mask']); - } - - foreach ($location2Entries as $row) { - $this->gateway->historizeBeforeSwap($row['action'], $row['lang_mask']); - } - } - - /** - * Decides if UrlAlias for $location2 should be published first. - * - * The order in which Locations are published only matters if swapped Locations are siblings and they have the same - * name in a given language. In this case, the UrlAlias for Location which previously had lower number at the end of - * its UrlAlias text (or no number at all) should be published first. This ensures that the number still stays lower - * for this Location after the swap. If it wouldn't stay lower, then swapping Locations in conjunction with swapping - * UrlAliases would effectively cancel each other. - * - * @param array $location1Entries - * @param int $location1ParentId - * @param string $name1 - * @param array $location2Entries - * @param int $location2ParentId - * @param string $name2 - * @param int $languageId - * - * @return bool - */ - private function shouldUrlAliasForSecondLocationBePublishedFirst( - array $location1Entries, - $location1ParentId, - $name1, - array $location2Entries, - $location2ParentId, - $name2, - $languageId - ) { - if ($location1ParentId === $location2ParentId && $name1 === $name2) { - $locationEntry1 = $this->getLocationEntryInLanguage($location1Entries, $languageId); - $locationEntry2 = $this->getLocationEntryInLanguage($location2Entries, $languageId); - - if ($locationEntry1 === null || $locationEntry2 === null) { - return false; - } - - if ($locationEntry2['text'] < $locationEntry1['text']) { - return true; - } - } - - return false; - } - - /** - * Get in a proper order - to be published - a list of URL aliases for swapped Locations. - * - * @see shouldUrlAliasForSecondLocationBePublishedFirst - * - * @param \eZ\Publish\SPI\Persistence\Content\Language $language - * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\DTO\SwappedLocationProperties $location1 - * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\DTO\SwappedLocationProperties $location2 - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\DTO\UrlAliasForSwappedLocation[] - */ - private function getUrlAliasesForSwappedLocations( - Language $language, - SwappedLocationProperties $location1, - SwappedLocationProperties $location2 - ) { - $isMainLanguage1 = $language->id == $location1->mainLanguageId; - $isMainLanguage2 = $language->id == $location2->mainLanguageId; - $urlAliases = []; - if (isset($location1->name)) { - $urlAliases[] = new UrlAliasForSwappedLocation( - $location1->id, - $location1->parentId, - $location1->name, - $isMainLanguage2 && $location1->isAlwaysAvailable, - $isMainLanguage2, - $location1->autogeneratedId - ); - } - - if (isset($location2->name)) { - $urlAliases[] = new UrlAliasForSwappedLocation( - $location2->id, - $location2->parentId, - $location2->name, - $isMainLanguage1 && $location2->isAlwaysAvailable, - $isMainLanguage1, - $location2->autogeneratedId - ); - - if (isset($location1->name) && $this->shouldUrlAliasForSecondLocationBePublishedFirst( - $location1->entries, - $location1->parentId, - $location1->name, - $location2->entries, - $location2->parentId, - $location2->name, - $language->id - )) { - $urlAliases = array_reverse($urlAliases); - } - } - - return $urlAliases; - } - - /** - * @param array $locationEntries - * @param int $languageId - * - * @return array|null - */ - private function getLocationEntryInLanguage(array $locationEntries, $languageId) - { - $entries = array_filter( - $locationEntries, - static function (array $row) use ($languageId) { - return (bool) ($row['lang_mask'] & $languageId); - } - ); - - return !empty($entries) ? array_shift($entries) : null; - } - - /** - * Returns possibly corrected alias id for given $locationId !! For use as parent id in logic. - * - * First level entries must have parent id set to 0 instead of their parent location alias id. - * There are two cases when alias id needs to be corrected: - * 1) location is special location without URL alias (location with id=1 in standard installation) - * 2) location is site root location, having special root entry in the ezurlalias_ml table (location with id=2 - * in standard installation) - * - * @param mixed $locationId - * - * @return mixed - */ - protected function getRealAliasId($locationId) - { - // Absolute root location does have a url alias entry so we can skip lookup - if ($locationId == self::ROOT_LOCATION_ID) { - return 0; - } - - $data = $this->gateway->loadAutogeneratedEntry('eznode:' . $locationId); - - // Root entries (URL wise) can return 0 as the returned value is used as parent (parent is 0 for root entries) - if (empty($data) || ($data['id'] != 0 && $data['parent'] == 0 && strlen($data['text']) == 0)) { - $id = 0; - } else { - $id = $data['id']; - } - - return $id; - } - - /** - * Recursively copies aliases from old parent under new parent. - * - * @param array $actionMap - * @param mixed $oldParentAliasId - * @param mixed $newParentAliasId - * @param string[] $alreadyGeneratedAliases - */ - protected function copySubtree($actionMap, $oldParentAliasId, $newParentAliasId, array $alreadyGeneratedAliases = []): array - { - $rows = $this->gateway->loadAutogeneratedEntries($oldParentAliasId); - $newIdsMap = []; - foreach ($rows as $row) { - $identifier = $row['parent'] . $row['text_md5']; - if (in_array($identifier, $alreadyGeneratedAliases)) { - continue; - } - - $oldParentAliasId = $row['id']; - - // Ensure that same action entries remain grouped by the same id - if (!isset($newIdsMap[$oldParentAliasId])) { - $newIdsMap[$oldParentAliasId] = $this->gateway->getNextId(); - } - - $row['action'] = $actionMap[$row['action']]; - $row['parent'] = $newParentAliasId; - $row['id'] = $row['link'] = $newIdsMap[$oldParentAliasId]; - $this->gateway->insertRow($row); - - $alreadyGeneratedAliases[] = $identifier; - - $alreadyGeneratedAliases = $this->copySubtree( - $actionMap, - $oldParentAliasId, - $row['id'], - $alreadyGeneratedAliases - ); - } - - return $alreadyGeneratedAliases; - } - - /** - * @param mixed $oldParentId - * @param mixed $newParentId - * - * @return array - */ - protected function getCopiedLocationsMap($oldParentId, $newParentId) - { - $originalLocations = $this->locationGateway->getSubtreeContent($oldParentId); - $copiedLocations = $this->locationGateway->getSubtreeContent($newParentId); - - $map = []; - foreach ($originalLocations as $index => $originalLocation) { - $map['eznode:' . $originalLocation['node_id']] = 'eznode:' . $copiedLocations[$index]['node_id']; - } - - return $map; - } - - public function locationDeleted($locationId): array - { - $action = 'eznode:' . $locationId; - $entry = $this->gateway->loadAutogeneratedEntry($action); - $entryId = $entry['id']; - - $this->removeSubtree($entryId, $action, $entry['is_original']); - - // after location-delete process completed - // check if entry had children; if yes - then restore them as nop-type - // for historical aliases relates to that entry - $notDeletedChildrenAliases = $this->gateway->getAllChildrenAliases($entryId); - if (count($notDeletedChildrenAliases) > 0) { - $this->insertAliasEntryAsNop($entry); - } - - return $notDeletedChildrenAliases; - } - - /** - * Notifies the underlying engine that Locations Content Translation was removed. - * - * @param int[] $locationIds all Locations of the Content that got Translation removed - * @param string $languageCode language code of the removed Translation - */ - public function translationRemoved(array $locationIds, $languageCode) - { - $languageId = $this->languageHandler->loadByLanguageCode($languageCode)->id; - - $actions = []; - foreach ($locationIds as $locationId) { - $actions[] = 'eznode:' . $locationId; - } - $this->gateway->bulkRemoveTranslation($languageId, $actions); - } - - /** - * Recursively removes aliases by given $id and $action. - * - * $original parameter is used to limit removal of moved Location aliases to history entries only. - * - * @param mixed $id - * @param string $action - * @param mixed $original - */ - protected function removeSubtree($id, $action, $original) - { - // Remove first to avoid unnecessary recursion. - if ($original) { - // If entry is original remove all for action (history and custom entries included). - $this->gateway->remove($action); - } else { - // Else entry is history, so remove only for action with the id. - // This means $id grouped history entries are removed, other history, active autogenerated - // and custom are left alone. - $this->gateway->remove($action, $id); - } - - // Load all autogenerated for parent $id, including history. - $entries = $this->gateway->loadAutogeneratedEntries($id, true); - - foreach ($entries as $entry) { - $this->removeSubtree($entry['id'], $entry['action'], $entry['is_original']); - } - } - - /** - * @param string $text - * - * @return string - */ - protected function getHash($text) - { - return md5(mb_strtolower($text, 'UTF-8')); - } - - /** - * {@inheritdoc} - */ - public function archiveUrlAliasesForDeletedTranslations($locationId, $parentLocationId, array $languageCodes) - { - $parentId = $this->getRealAliasId($parentLocationId); - - $data = $this->gateway->loadLocationEntries($locationId); - // filter removed Translations - $removedLanguages = array_diff( - $this->mapper->extractLanguageCodesFromData($data), - $languageCodes - ); - - if (empty($removedLanguages)) { - return; - } - - // map languageCodes to their IDs - $languageIds = array_map( - function ($languageCode) { - return $this->languageHandler->loadByLanguageCode($languageCode)->id; - }, - $removedLanguages - ); - - $this->gateway->archiveUrlAliasesForDeletedTranslations($locationId, $parentId, $languageIds); - } - - /** - * Remove corrupted URL aliases (global, custom and system). - * - * @return int Number of removed URL aliases - * - * @throws \Exception - */ - public function deleteCorruptedUrlAliases() - { - $this->transactionHandler->beginTransaction(); - try { - $totalCount = $this->gateway->deleteUrlAliasesWithoutLocation(); - $totalCount += $this->gateway->deleteUrlAliasesWithoutParent(); - $totalCount += $this->gateway->deleteUrlAliasesWithBrokenLink(); - $totalCount += $this->gateway->deleteUrlNopAliasesWithoutChildren(); - - $this->transactionHandler->commit(); - - return $totalCount; - } catch (\Exception $e) { - $this->transactionHandler->rollback(); - throw $e; - } - } - - /** - * Attempt repairing auto-generated URL aliases for the given Location (including history). - * - * Note: it is assumed that at this point original, working, URL Alias for Location is published. - * - * @param int $locationId - * - * @throws \eZ\Publish\Core\Base\Exceptions\BadStateException - */ - public function repairBrokenUrlAliasesForLocation(int $locationId) - { - try { - $this->gateway->repairBrokenUrlAliasesForLocation($locationId); - } catch (\RuntimeException $e) { - throw new BadStateException('locationId', $e->getMessage(), $e); - } - } - - private function insertAliasEntryAsNop(array $aliasEntry): void - { - $aliasEntry['action'] = Gateway::NOP_ACTION; - $aliasEntry['action_type'] = Gateway::NOP; - - $this->gateway->insertRow($aliasEntry); - } - - /** - * Internal publish custom aliases method, accepting language mask to set correct language mask on url aliases - * new alias ID (used when swapping Locations). - */ - private function internalPublishCustomUrlAliasForLocation(SwappedLocationProperties $location, int $languageMask) - { - foreach ($location->entries as $entry) { - if ((int)$entry['is_alias'] === 0) { - continue; - } - - $mask = (int)$entry['lang_mask'] & $languageMask; - - if ($mask <= 1) { - continue; - } - - $this->gateway->updateRow( - (int)$entry['parent'], - $entry['text_md5'], - [ - 'id' => (int)$entry['id'], - 'is_original' => 1, - 'is_alias' => 1, - 'lang_mask' => $mask, - ] - ); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Mapper.php b/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Mapper.php deleted file mode 100644 index cf70d82b2d..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/Mapper.php +++ /dev/null @@ -1,176 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias; - -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; -use eZ\Publish\SPI\Persistence\Content\UrlAlias; - -/** - * UrlAlias Mapper. - */ -class Mapper -{ - /** - * Language mask generator. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator - */ - protected $languageMaskGenerator; - - /** - * Creates a new UrlWildcard Handler. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator $languageMaskGenerator - */ - public function __construct(LanguageMaskGenerator $languageMaskGenerator) - { - $this->languageMaskGenerator = $languageMaskGenerator; - } - - /** - * Creates a UrlAlias object from database row data. - * - * @param mixed[] $data - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias - */ - public function extractUrlAliasFromData($data) - { - $urlAlias = new UrlAlias(); - - list($type, $destination) = $this->matchTypeAndDestination($data['action']); - $urlAlias->id = $this->generateIdentityKey((int)$data['parent'], $data['text_md5']); - $urlAlias->pathData = $this->normalizePathData($data['raw_path_data']); - $urlAlias->languageCodes = $this->languageMaskGenerator->extractLanguageCodesFromMask($data['lang_mask']); - $urlAlias->alwaysAvailable = $this->languageMaskGenerator->isAlwaysAvailable($data['lang_mask']); - $urlAlias->isHistory = isset($data['is_path_history']) ? $data['is_path_history'] : !$data['is_original']; - $urlAlias->isCustom = (bool)$data['is_alias']; - $urlAlias->forward = $data['is_alias'] && $data['alias_redirects']; - $urlAlias->destination = $destination; - $urlAlias->type = $type; - - return $urlAlias; - } - - /** - * Extracts UrlAlias objects from database $rows. - * - * @param array $rows - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias[] - */ - public function extractUrlAliasListFromData(array $rows) - { - $urlAliases = []; - foreach ($rows as $row) { - $urlAliases[] = $this->extractUrlAliasFromData($row); - } - - return $urlAliases; - } - - /** - * Extracts language codes from database $rows. - * - * @param array $rows - * - * @return string[] - */ - public function extractLanguageCodesFromData(array $rows): array - { - $languageMask = 0; - foreach ($rows as $row) { - $languageMask |= $row['lang_mask']; - } - - return $this->languageMaskGenerator->extractLanguageCodesFromMask($languageMask); - } - - public function generateIdentityKey(int $parentId, string $hash): string - { - return sprintf('%d-%s', $parentId, $hash); - } - - /** - * @throws \RuntimeException - * - * @param string $action - * - * @return array - */ - protected function matchTypeAndDestination($action) - { - if (preg_match('#^([a-zA-Z0-9_]+):(.+)?$#', $action, $matches)) { - $actionType = $matches[1]; - $actionValue = isset($matches[2]) ? $matches[2] : false; - - switch ($actionType) { - case 'eznode': - $type = UrlAlias::LOCATION; - $destination = (int)$actionValue; - break; - - case 'module': - $type = UrlAlias::RESOURCE; - $destination = $actionValue; - break; - - case 'nop': - $type = UrlAlias::VIRTUAL; - $destination = null; - break; - - default: - // @todo log message - throw new \RuntimeException("Action type '{$actionType}' is unknown"); - } - } else { - // @todo log message - throw new \RuntimeException("Action '{$action}' is not valid"); - } - - return [$type, $destination]; - } - - /** - * @param array $pathData - * - * @return array - */ - protected function normalizePathData(array $pathData) - { - $normalizedPathData = []; - foreach ($pathData as $level => $rows) { - $pathElementData = []; - foreach ($rows as $row) { - $this->normalizePathDataRow($pathElementData, $row); - } - - $normalizedPathData[$level] = $pathElementData; - } - - return $normalizedPathData; - } - - /** - * @param array $pathElementData - * @param array $row - */ - protected function normalizePathDataRow(array &$pathElementData, array $row) - { - $languageCodes = $this->languageMaskGenerator->extractLanguageCodesFromMask($row['lang_mask']); - $pathElementData['always-available'] = $this->languageMaskGenerator->isAlwaysAvailable($row['lang_mask']); - if (!empty($languageCodes)) { - foreach ($languageCodes as $languageCode) { - $pathElementData['translations'][$languageCode] = $row['text']; - } - } elseif ($pathElementData['always-available']) { - // NOP entry, lang_mask == 1 - $pathElementData['translations']['always-available'] = $row['text']; - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/SlugConverter.php b/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/SlugConverter.php deleted file mode 100644 index ae1843fa9e..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/SlugConverter.php +++ /dev/null @@ -1,382 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias; - -use eZ\Publish\Core\Persistence\TransformationProcessor; - -/** - * URL slug converter. - */ -class SlugConverter -{ - public const DEFAULT_CONFIGURATION = [ - 'wordSeparatorName' => 'dash', - 'urlAliasNameLimit' => 255, - 'transformation' => 'urlalias', - 'transformationGroups' => [ - 'urlalias' => [ - 'commands' => [ - //normalize - 'space_normalize', - 'hyphen_normalize', - 'apostrophe_normalize', - 'doublequote_normalize', - 'greek_normalize', - 'endline_search_normalize', - 'tab_search_normalize', - 'specialwords_search_normalize', - - //transform - 'apostrophe_to_doublequote', - 'math_to_ascii', - 'inverted_to_normal', - - //decompose - 'special_decompose', - 'latin_search_decompose', - - //transliterate - 'cyrillic_transliterate_ascii', - 'greek_transliterate_ascii', - 'hebrew_transliterate_ascii', - 'latin1_transliterate_ascii', - 'latin-exta_transliterate_ascii', - - //diacritical - 'cyrillic_diacritical', - 'greek_diacritical', - 'latin1_diacritical', - 'latin-exta_diacritical', - ], - 'cleanupMethod' => 'url_cleanup', - ], - 'urlalias_iri' => [ - 'commands' => [], - 'cleanupMethod' => 'url_cleanup_iri', - ], - 'urlalias_compat' => [ - 'commands' => [ - //normalize - 'space_normalize', - 'hyphen_normalize', - 'apostrophe_normalize', - 'doublequote_normalize', - 'greek_normalize', - 'endline_search_normalize', - 'tab_search_normalize', - 'specialwords_search_normalize', - - //transform - 'apostrophe_to_doublequote', - 'math_to_ascii', - 'inverted_to_normal', - - //decompose - 'special_decompose', - 'latin_search_decompose', - - //transliterate - 'cyrillic_transliterate_ascii', - 'greek_transliterate_ascii', - 'hebrew_transliterate_ascii', - 'latin1_transliterate_ascii', - 'latin-exta_transliterate_ascii', - - //diacritical - 'cyrillic_diacritical', - 'greek_diacritical', - 'latin1_diacritical', - 'latin-exta_diacritical', - - //lowercase - 'ascii_lowercase', - 'cyrillic_lowercase', - 'greek_lowercase', - 'latin1_lowercase', - 'latin-exta_lowercase', - 'latin_lowercase', - ], - 'cleanupMethod' => 'url_cleanup_compat', - ], - 'urlalias_lowercase' => [ - 'commands' => [ - 'ascii_lowercase', - 'cyrillic_lowercase', - 'greek_lowercase', - 'latin1_lowercase', - 'latin-exta_lowercase', - 'latin_lowercase', - - 'space_normalize', - 'hyphen_normalize', - 'apostrophe_normalize', - 'doublequote_normalize', - 'greek_normalize', - 'endline_search_normalize', - 'tab_search_normalize', - 'specialwords_search_normalize', - - 'apostrophe_to_doublequote', - 'math_to_ascii', - 'inverted_to_normal', - - 'special_decompose', - 'latin_search_decompose', - - 'cyrillic_transliterate_ascii', - 'greek_transliterate_ascii', - 'hebrew_transliterate_ascii', - 'latin1_transliterate_ascii', - 'latin-exta_transliterate_ascii', - - 'cyrillic_diacritical', - 'greek_diacritical', - 'latin1_diacritical', - 'latin-exta_diacritical', - ], - 'cleanupMethod' => 'url_cleanup', - ], - ], - 'reservedNames' => [ - 'class', - 'collaboration', - 'content', - 'error', - 'ezinfo', - 'infocollector', - 'layout', - 'notification', - 'oauth', - 'oauthadmin', - 'package', - 'pdf', - 'role', - 'rss', - 'search', - 'section', - 'settings', - 'setup', - 'shop', - 'state', - 'trigger', - 'url', - 'user', - 'visual', - 'workflow', - 'switchlanguage', - ], - ]; - - /** - * Transformation processor to normalize URL strings. - * - * @var \eZ\Publish\Core\Persistence\TransformationProcessor - */ - protected $transformationProcessor; - - /** @var array */ - protected $configuration; - - /** - * Creates a new URL slug converter. - * - * @param \eZ\Publish\Core\Persistence\TransformationProcessor $transformationProcessor - * @param array $configuration - */ - public function __construct( - TransformationProcessor $transformationProcessor, - array $configuration = [] - ) { - $this->transformationProcessor = $transformationProcessor; - $this->configuration = $configuration + self::DEFAULT_CONFIGURATION; - } - - /** - * Converts given $text into a URL slug consisting of URL valid characters. - * For non-Unicode setups this means character in the range a-z, numbers and _, for Unicode - * setups it means all characters except space, &, ;, /, :, =, ?, [, ], (, ), -. - * - * Invalid characters are converted to -. - * - * Example with a non-Unicode setup - * - * 'My car' => 'My-car' - * 'What is this?' => 'What-is-this' - * 'This & that' => 'This-that' - * 'myfile.tpl' => 'Myfile-tpl', - * 'øæå' => 'oeaeaa' - * - * @param string $text - * @param string $defaultText - * @param string|null $transformation - * - * @return string - */ - public function convert($text, $defaultText = '_1', $transformation = null) - { - if (!isset($transformation)) { - $transformation = $this->configuration['transformation']; - } - - if (strlen($text) === 0) { - $text = $defaultText; - } - - if (isset($this->configuration['transformationGroups'][$transformation]['commands']) - && !empty($this->configuration['transformationGroups'][$transformation]['commands']) - ) { - $text = $this->transformationProcessor->transform( - $text, - $this->configuration['transformationGroups'][$transformation]['commands'] - ); - } - - return $this->cleanupText( - $text, - $this->configuration['transformationGroups'][$transformation]['cleanupMethod'] - ); - } - - /** - * Returns unique counter number that is appended to the path element in order to make it unique - * against system reserved names and other entries on the same level. - * - * Comparison is done only if given $isRootLevel equals to true (it does by default), meaning that - * entry is at first level of URL. - * In a case when reserved name is matched method will return 2. - * When given $isRootLevel equals to false or when there is no match with reserved names this will - * return 1, which is default value not appended to name. - * - * Note: this is used only when publishing URL aliases, when creating global and custom aliases user - * is allowed to create first level entries that collide with reserved names. Also, in actual creation - * of the alias name will be further checked against existing elements under the same parent, using - * unique counter value determined here as starting unique counter value. - * - * @param string $text - * @param bool $isRootLevel - * - * @return int - */ - public function getUniqueCounterValue($text, $isRootLevel = true) - { - if ($isRootLevel) { - foreach ($this->configuration['reservedNames'] as $reservedName) { - // Case insensitive comparison - if (strcasecmp($text, $reservedName) === 0) { - return 2; - } - } - } - - return 1; - } - - /** - * Cleans up $text using given $method. - * - * @param string $text - * @param string $method - * - * @return string - */ - protected function cleanupText($text, $method) - { - switch ($method) { - case 'url_cleanup': - $sep = $this->getWordSeparator(); - $sepQ = preg_quote($sep); - $text = preg_replace( - [ - '#[^a-zA-Z0-9_!.-]+#', - '#^[.]+|[!.]+$#', // Remove dots at beginning/end - "#\.\.+#", // Remove double dots - "#[{$sepQ}]+#", // Turn multiple separators into one - "#^[{$sepQ}]+|[{$sepQ}]+$#", // Strip separator from beginning/end - ], - [ - $sep, - $sep, - $sep, - $sep, - '', - ], - $text - ); - break; - case 'url_cleanup_iri': - // With IRI support we keep all characters except some reserved ones, - // they are space, ampersand, semi-colon, forward slash, colon, equal sign, question mark, - // square brackets, parenthesis, plus. - // - // Note: Space is turned into a dash to make it easier for people to - // paste urls from the system and have the whole url recognized - // instead of being broken off - $sep = $this->getWordSeparator(); - $sepQ = preg_quote($sep); - $prepost = ' .' . $sepQ; - if ($sep != '-') { - $prepost .= '-'; - } - $text = preg_replace( - [ - "#[ \\\\%\#&;/:=?\[\]()+]+#", - '#^[.]+|[!.]+$#', // Remove dots at beginning/end - "#\.\.+#", // Remove double dots - "#[{$sepQ}]+#", // Turn multiple separators into one - "#^[{$prepost}]+|[{$prepost}]+$#", - ], - [ - $sep, - $sep, - $sep, - $sep, - '', - ], - $text - ); - break; - case 'url_cleanup_compat': - // Old style of url alias with lowercase only and underscores for separators - $text = strtolower($text); - $text = preg_replace( - [ - '#[^a-z0-9]+#', - '#^_+|_+$#', - ], - [ - '_', - '', - ], - $text - ); - break; - default: - // Nothing - } - - return $text; - } - - /** - * Returns word separator value. - * - * @return string - */ - protected function getWordSeparator() - { - switch ($this->configuration['wordSeparatorName']) { - case 'dash': - return '-'; - case 'underscore': - return '_'; - case 'space': - return ' '; - } - - return '-'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Gateway.php deleted file mode 100644 index 50924d84e3..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Gateway.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard; - -use eZ\Publish\SPI\Persistence\Content\UrlWildcard; -use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\Criterion; - -/** - * UrlWildcard Gateway. - * - * @internal For internal use by Persistence Handlers. - */ -abstract class Gateway -{ - public const URL_WILDCARD_TABLE = 'ezurlwildcard'; - public const URL_WILDCARD_SEQ = 'ezurlwildcard_id_seq'; - - /** - * Insert the given UrlWildcard. - */ - abstract public function insertUrlWildcard(UrlWildcard $urlWildcard): int; - - /** - * Update the given UrlWildcard. - */ - abstract public function updateUrlWildcard( - int $id, - string $sourceUrl, - string $destinationUrl, - bool $forward - ): void; - - /** - * Delete the UrlWildcard with given $id. - */ - abstract public function deleteUrlWildcard(int $id): void; - - /** - * Load an array with data about UrlWildcard with $id. - */ - abstract public function loadUrlWildcardData(int $id): array; - - /** - * Load an array with data about UrlWildcards (paged). - */ - abstract public function loadUrlWildcardsData(int $offset = 0, int $limit = -1): array; - - /** - * Selects URLWildcards matching specified criteria. - * - * @return array{ - * "rows": mixed, - * "count": int|null, - * } - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException if Criterion is not applicable to its target - */ - abstract public function find( - Criterion $criterion, - int $offset, - int $limit, - array $sortClauses = [], - bool $doCount = true - ): array; - - /** - * Load the UrlWildcard by source url $sourceUrl. - */ - abstract public function loadUrlWildcardBySourceUrl(string $sourceUrl): array; - - abstract public function countAll(): int; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabase.php deleted file mode 100644 index 8fc542b964..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,284 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard; -use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\Criterion; -use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\SortClause; -use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriteriaConverter; -use RuntimeException; - -/** - * URL wildcard gateway implementation using the Doctrine database. - * - * @internal Gateway implementation is considered internal. Use Persistence UrlWildcard Handler instead. - * - * @see \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler - */ -final class DoctrineDatabase extends Gateway -{ - /** - * 2^30, since PHP_INT_MAX can cause overflows in DB systems, if PHP is run - * on 64 bit systems. - */ - private const MAX_LIMIT = 1073741824; - - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** @var \Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriteriaConverter */ - protected $criteriaConverter; - - public const SORT_DIRECTION_MAP = [ - SortClause::SORT_ASC => 'ASC', - SortClause::SORT_DESC => 'DESC', - ]; - - public function __construct(Connection $connection, CriteriaConverter $criteriaConverter) - { - $this->connection = $connection; - $this->criteriaConverter = $criteriaConverter; - } - - public function insertUrlWildcard(UrlWildcard $urlWildcard): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::URL_WILDCARD_TABLE) - ->values( - [ - 'destination_url' => $query->createPositionalParameter( - $this->trimUrl($urlWildcard->destinationUrl), - ParameterType::STRING - ), - 'source_url' => $query->createPositionalParameter( - $this->trimUrl($urlWildcard->sourceUrl), - ParameterType::STRING - ), - 'type' => $query->createPositionalParameter( - $urlWildcard->forward ? 1 : 2, - ParameterType::INTEGER - ), - ] - ); - - $query->execute(); - - return (int)$this->connection->lastInsertId(self::URL_WILDCARD_SEQ); - } - - public function updateUrlWildcard( - int $id, - string $sourceUrl, - string $destinationUrl, - bool $forward - ): void { - $query = $this->connection->createQueryBuilder(); - - $query - ->update(self::URL_WILDCARD_TABLE) - ->set( - 'destination_url', - $query->createPositionalParameter( - $this->trimUrl($destinationUrl), - ParameterType::STRING - ), - )->set( - 'source_url', - $query->createPositionalParameter( - $this->trimUrl($sourceUrl), - ParameterType::STRING - ), - )->set( - 'type', - $query->createPositionalParameter( - $forward ? 1 : 2, - ParameterType::INTEGER - ) - ); - - $query->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter( - $id, - ParameterType::INTEGER - ) - ) - ); - - $query->execute(); - } - - public function deleteUrlWildcard(int $id): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::URL_WILDCARD_TABLE) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - private function buildLoadUrlWildcardDataQuery(): QueryBuilder - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('id', 'destination_url', 'source_url', 'type') - ->from(self::URL_WILDCARD_TABLE); - - return $query; - } - - public function loadUrlWildcardData(int $id): array - { - $query = $this->buildLoadUrlWildcardDataQuery(); - $query - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - $result = $query->execute()->fetch(FetchMode::ASSOCIATIVE); - - return false !== $result ? $result : []; - } - - public function loadUrlWildcardsData(int $offset = 0, int $limit = -1): array - { - $query = $this->buildLoadUrlWildcardDataQuery(); - $query - ->setMaxResults($limit > 0 ? $limit : self::MAX_LIMIT) - ->setFirstResult($offset); - - $stmt = $query->execute(); - - return $stmt->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function find( - Criterion $criterion, - int $offset, - int $limit, - array $sortClauses = [], - bool $doCount = true - ): array { - $count = $doCount ? $this->doCount($criterion) : null; - if (!$doCount && $limit === 0) { - throw new RuntimeException('Invalid query. Cannot disable count and request 0 items at the same time'); - } - - if ($limit === 0 || ($count !== null && $count <= $offset)) { - return [ - 'count' => $count, - 'rows' => [], - ]; - } - - if ($limit < 0) { - throw new InvalidArgumentException('$limit', 'The limit need be higher than 0'); - } - - $query = $this->buildLoadUrlWildcardDataQuery(); - $query - ->where($this->criteriaConverter->convertCriteria($query, $criterion)) - ->setMaxResults($limit) - ->setFirstResult($offset); - - foreach ($sortClauses as $sortClause) { - $query->addOrderBy($sortClause->target, $this->getQuerySortingDirection($sortClause->direction)); - } - - $statement = $query->execute(); - - return [ - 'count' => $count, - 'rows' => $statement->fetchAllAssociative(), - ]; - } - - public function loadUrlWildcardBySourceUrl(string $sourceUrl): array - { - $query = $this->buildLoadUrlWildcardDataQuery(); - $expr = $query->expr(); - $query - ->where( - $expr->eq( - 'source_url', - $query->createPositionalParameter($sourceUrl) - ) - ); - - $result = $query->execute()->fetch(FetchMode::ASSOCIATIVE); - - return false !== $result ? $result : []; - } - - public function countAll(): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select($this->connection->getDatabasePlatform()->getCountExpression('id')) - ->from(self::URL_WILDCARD_TABLE); - - return (int) $query->execute()->fetchColumn(); - } - - /** - * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\Criterion $criterion - * - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \Doctrine\DBAL\Exception - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - protected function doCount(Criterion $criterion): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select($this->connection->getDatabasePlatform()->getCountExpression('url_wildcard.id')) - ->from(self::URL_WILDCARD_TABLE, 'url_wildcard') - ->where($this->criteriaConverter->convertCriteria($query, $criterion)); - - return (int)$query->execute()->fetchOne(); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - private function getQuerySortingDirection(string $direction): string - { - if (!isset(self::SORT_DIRECTION_MAP[$direction])) { - throw new InvalidArgumentException( - '$sortClause->direction', - sprintf( - 'Unsupported "%s" sorting direction, use one of the SortClause::SORT_* constants instead', - $direction - ) - ); - } - - return self::SORT_DIRECTION_MAP[$direction]; - } - - private function trimUrl(string $url): string - { - return trim($url, '/'); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Gateway/ExceptionConversion.php deleted file mode 100644 index c0810eb309..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,125 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard; -use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\Criterion; -use PDOException; - -/** - * @internal Internal exception conversion layer. - */ -final class ExceptionConversion extends Gateway -{ - /** - * The wrapped gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway - */ - private $innerGateway; - - /** - * Create a new exception conversion gateway around $innerGateway. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function insertUrlWildcard(UrlWildcard $urlWildcard): int - { - try { - return $this->innerGateway->insertUrlWildcard($urlWildcard); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateUrlWildcard( - int $id, - string $sourceUrl, - string $destinationUrl, - bool $forward - ): void { - try { - $this->innerGateway->updateUrlWildcard( - $id, - $sourceUrl, - $destinationUrl, - $forward - ); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteUrlWildcard(int $id): void - { - try { - $this->innerGateway->deleteUrlWildcard($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadUrlWildcardData(int $id): array - { - try { - return $this->innerGateway->loadUrlWildcardData($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadUrlWildcardsData(int $offset = 0, int $limit = -1): array - { - try { - return $this->innerGateway->loadUrlWildcardsData($offset, $limit); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function find( - Criterion $criterion, - int $offset, - int $limit, - array $sortClauses = [], - bool $doCount = true - ): array { - try { - return $this->innerGateway->find($criterion, $offset, $limit, $sortClauses, $doCount); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadUrlWildcardBySourceUrl(string $sourceUrl): array - { - try { - return $this->innerGateway->loadUrlWildcardBySourceUrl($sourceUrl); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countAll(): int - { - try { - return $this->innerGateway->countAll(); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Handler.php deleted file mode 100644 index f3b9e8c703..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Handler.php +++ /dev/null @@ -1,270 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard; - -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler as BaseUrlWildcardHandler; -use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\URLWildcardQuery; - -/** - * The UrlWildcard Handler provides nice urls with wildcards management. - * - * Its methods operate on a representation of the url alias data structure held - * inside a storage engine. - */ -class Handler implements BaseUrlWildcardHandler -{ - private const PLACEHOLDERS_REGEXP = '(\{(\d+)\})'; - - /** - * UrlWildcard Gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway - */ - protected $gateway; - - /** - * UrlWildcard Mapper. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Mapper - */ - protected $mapper; - - /** - * Creates a new UrlWildcard Handler. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway $gateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Mapper $mapper - */ - public function __construct(Gateway $gateway, Mapper $mapper) - { - $this->gateway = $gateway; - $this->mapper = $mapper; - } - - /** - * Creates a new url wildcard. - * - * @param string $sourceUrl - * @param string $destinationUrl - * @param bool $forward - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard - */ - public function create($sourceUrl, $destinationUrl, $forward = false) - { - $urlWildcard = $this->mapper->createUrlWildcard( - $sourceUrl, - $destinationUrl, - $forward - ); - - $urlWildcard->id = $this->gateway->insertUrlWildcard($urlWildcard); - - return $urlWildcard; - } - - public function update( - int $id, - string $sourceUrl, - string $destinationUrl, - bool $forward - ): UrlWildcard { - $this->gateway->updateUrlWildcard( - $id, - $sourceUrl, - $destinationUrl, - $forward - ); - - $spiUrlWildcard = $this->mapper->createUrlWildcard( - $sourceUrl, - $destinationUrl, - $forward - ); - - $spiUrlWildcard->id = $id; - - return $spiUrlWildcard; - } - - /** - * removes an url wildcard. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the url wild card was not found - * - * @param mixed $id - */ - public function remove($id) - { - $this->gateway->deleteUrlWildcard($id); - } - - /** - * Loads a url wild card. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the url wild card was not found - * - * @param mixed $id - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard - */ - public function load($id) - { - $row = $this->gateway->loadUrlWildcardData($id); - - if (empty($row)) { - throw new NotFoundException('UrlWildcard', $id); - } - - return $this->mapper->extractUrlWildcardFromRow($row); - } - - /** - * Loads all url wild card (paged). - * - * @param mixed $offset - * @param mixed $limit - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard[] - */ - public function loadAll($offset = 0, $limit = -1) - { - return $this->mapper->extractUrlWildcardsFromRows( - $this->gateway->loadUrlWildcardsData($offset, $limit) - ); - } - - public function find(URLWildcardQuery $query): array - { - $results = $this->gateway->find( - $query->filter, - $query->offset, - $query->limit, - $query->sortClauses, - $query->performCount - ); - - return [ - 'count' => $results['count'], - 'items' => $this->mapper->extractUrlWildcardsFromRows($results['rows']), - ]; - } - - /** - * Performs lookup for given URL. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the url wild card was not found - * - * @param string $sourceUrl - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard - */ - public function translate(string $sourceUrl): UrlWildcard - { - $row = $this->gateway->loadUrlWildcardBySourceUrl($sourceUrl); - - if (!empty($row)) { - return $this->mapper->extractUrlWildcardFromRow($row); - } - - // can't find UrlWildcard by simple lookup, continue and try to translate - - $rows = $this->gateway->loadUrlWildcardsData(); - uasort( - $rows, - static function ($row1, $row2) { - return strlen($row2['source_url']) - strlen($row1['source_url']); - } - ); - - foreach ($rows as $row) { - if ($uri = $this->match($sourceUrl, $row)) { - $row['destination_url'] = $uri; - - return $this->mapper->extractUrlWildcardFromRow($row); - } - } - - throw new NotFoundException('URLWildcard', $sourceUrl); - } - - /** - * Checks whether UrlWildcard with given source url exits. - * - * @param string $sourceUrl - * - * @return bool - */ - public function exactSourceUrlExists(string $sourceUrl): bool - { - $row = $this->gateway->loadUrlWildcardBySourceUrl($sourceUrl); - - return !empty($row); - } - - /** - * {@inheritDoc} - */ - public function countAll(): int - { - return $this->gateway->countAll(); - } - - /** - * Tests if the given url matches against the given url wildcard. - * - * if the wildcard matches on the given url this method will return a ready - * to use destination url, otherwise this method will return <b>NULL</b>. - * - * @param string $url - * @param array $wildcard - * - * @return string|null - */ - private function match(string $url, array $wildcard): ?string - { - if (preg_match($this->compile($wildcard['source_url']), $url, $match)) { - return $this->substitute($wildcard['destination_url'], $match); - } - - return null; - } - - /** - * Compiles the given url pattern into a regular expression. - * - * @param string $sourceUrl - * - * @return string - */ - private function compile(string $sourceUrl): string - { - return '(^' . str_replace('\\*', '(.*)', preg_quote($sourceUrl)) . '$)U'; - } - - /** - * Substitutes all placeholders ({\d}) in the given <b>$destinationUrl</b> with - * the values from the given <b>$values</b> array. - * - * @param string $destinationUrl - * @param array $values - * - * @return string - */ - private function substitute(string $destinationUrl, array $values): string - { - preg_match_all(self::PLACEHOLDERS_REGEXP, $destinationUrl, $matches); - - foreach ($matches[1] as $match) { - $destinationUrl = str_replace("{{$match}}", $values[$match], $destinationUrl); - } - - return $destinationUrl; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Mapper.php b/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Mapper.php deleted file mode 100644 index b28f9f3e66..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Content/UrlWildcard/Mapper.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard; - -use eZ\Publish\SPI\Persistence\Content\UrlWildcard; - -/** - * UrlWildcard Mapper. - */ -class Mapper -{ - /** - * Creates a UrlWildcard object from given parameters. - * - * @param string $sourceUrl - * @param string $destinationUrl - * @param bool $forward - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard - */ - public function createUrlWildcard($sourceUrl, $destinationUrl, $forward) - { - $urlWildcard = new UrlWildcard(); - - $urlWildcard->destinationUrl = $this->cleanUrl($destinationUrl); - $urlWildcard->sourceUrl = $this->cleanUrl($sourceUrl); - $urlWildcard->forward = $forward; - - return $urlWildcard; - } - - /** - * Extracts UrlWildcard object from given database $row. - * - * @param array $row - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard - */ - public function extractUrlWildcardFromRow(array $row) - { - $urlWildcard = new UrlWildcard(); - - $urlWildcard->id = (int)$row['id']; - $urlWildcard->destinationUrl = $this->cleanUrl($row['destination_url']); - $urlWildcard->sourceUrl = $this->cleanUrl($row['source_url']); - $urlWildcard->forward = (int)$row['type'] === 1; - - return $urlWildcard; - } - - /** - * @param string $url - * - * @return string - */ - protected function cleanUrl($url) - { - // if given $url is an absolute URL, then it's not necessary to prepend it with slash - if (null !== parse_url($url, PHP_URL_SCHEME)) { - return trim($url); - } - - return '/' . trim($url, '/ '); - } - - /** - * Extracts UrlWildcard objects from database $rows. - * - * @param array $rows - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard[] - */ - public function extractUrlWildcardsFromRows(array $rows) - { - $urlWildcards = []; - - foreach ($rows as $row) { - $urlWildcards[] = $this->extractUrlWildcardFromRow($row); - } - - return $urlWildcards; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Exception/RoleNotFound.php b/eZ/Publish/Core/Persistence/Legacy/Exception/RoleNotFound.php deleted file mode 100644 index a64f8430f0..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Exception/RoleNotFound.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Exception; - -use eZ\Publish\Core\Base\Exceptions\NotFoundException; - -/** - * Exception thrown when a Role/RoleDraft to be loaded is not found. - */ -class RoleNotFound extends NotFoundException -{ - /** - * Creates a new exception for $roleId in $status. - * - * @param mixed $roleId - * @param mixed $status - */ - public function __construct($roleId, $status) - { - parent::__construct( - 'eZ\\Publish\\SPI\\Persistence\\User\\Role', - sprintf('ID: %s, Status: %s', $roleId, $status) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Exception/TypeGroupNotFound.php b/eZ/Publish/Core/Persistence/Legacy/Exception/TypeGroupNotFound.php deleted file mode 100644 index 9168dc7d5e..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Exception/TypeGroupNotFound.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Exception; - -use eZ\Publish\Core\Base\Exceptions\NotFoundException; - -/** - * Exception thrown when a Type to be loaded is not found. - */ -class TypeGroupNotFound extends NotFoundException -{ - /** - * Creates a new exception for $typeId in $status;. - * - * @param mixed $typeGroupId - * @param mixed $status - */ - public function __construct($typeGroupId) - { - parent::__construct( - 'eZ\\Publish\\SPI\\Persistence\\Content\\Type\\Group', - sprintf('ID: %s', $typeGroupId) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Exception/TypeNotFound.php b/eZ/Publish/Core/Persistence/Legacy/Exception/TypeNotFound.php deleted file mode 100644 index 341237f141..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Exception/TypeNotFound.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Exception; - -use eZ\Publish\Core\Base\Exceptions\NotFoundException; - -/** - * Exception thrown when a Type to be loaded is not found. - */ -class TypeNotFound extends NotFoundException -{ - /** - * Creates a new exception for $typeId in $status;. - * - * @param mixed $typeId - * @param mixed $status - */ - public function __construct($typeId, $status) - { - parent::__construct( - 'eZ\\Publish\\SPI\\Persistence\\Content\\Type', - sprintf('ID: %s, Status: %s', $typeId, $status) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ContentIdQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ContentIdQueryBuilder.php deleted file mode 100644 index 1eac84d5c5..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ContentIdQueryBuilder.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentId; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class ContentIdQueryBuilder implements CriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof ContentId; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentId $criterion */ - return $queryBuilder->expr()->in( - 'content.id', - $queryBuilder->createNamedParameter( - array_map('intval', $criterion->value), - Connection::PARAM_INT_ARRAY - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/DateMetadataQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/DateMetadataQueryBuilder.php deleted file mode 100644 index 9ea71c6d54..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/DateMetadataQueryBuilder.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\DateMetadata; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class DateMetadataQueryBuilder implements CriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof DateMetadata; - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\DateMetadata $criterion */ - $column = $criterion->target === DateMetadata::MODIFIED ? 'modified' : 'published'; - $column = "content.{$column}"; - - $value = (array)$criterion->value; - - return $queryBuilder->buildOperatorBasedCriterionConstraint( - $column, - $value, - $criterion->operator - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/LanguageCodeQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/LanguageCodeQueryBuilder.php deleted file mode 100644 index 6165e741eb..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/LanguageCodeQueryBuilder.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LanguageCode; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * Content Language Code Criterion visitor query builder. - * - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LanguageCode - * - * @internal for internal use by Repository Filtering - */ -final class LanguageCodeQueryBuilder implements CriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof LanguageCode; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LanguageCode $criterion */ - $queryBuilder - ->joinOnce( - 'version', - Gateway::CONTENT_LANGUAGE_TABLE, - 'language', - // bitwise and for exact language ID match - 'language.id & version.language_mask = language.id' - ); - - // at this point $criterion->value is guaranteed to be an array - $expr = $queryBuilder->expr()->in( - 'language.locale', - $queryBuilder->createNamedParameter( - $criterion->value, - Connection::PARAM_STR_ARRAY - ) - ); - - if ($criterion->matchAlwaysAvailable) { - $expr = (string)$queryBuilder->expr()->orX($expr, 'version.language_mask & 1 = 1'); - } - - return $expr; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ObjectStateIdQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ObjectStateIdQueryBuilder.php deleted file mode 100644 index a189f42847..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ObjectStateIdQueryBuilder.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ObjectStateId; -use eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class ObjectStateIdQueryBuilder implements CriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof ObjectStateId; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - $tableAlias = uniqid('osl_'); - - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ObjectStateId $criterion */ - $queryBuilder - ->join( - 'content', - Gateway::OBJECT_STATE_LINK_TABLE, - $tableAlias, - 'content.id = ' . $tableAlias . '.contentobject_id', - ); - - $value = (array)$criterion->value; - - return $queryBuilder->expr()->in( - $tableAlias . '.contentobject_state_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ObjectStateIdentifierQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ObjectStateIdentifierQueryBuilder.php deleted file mode 100644 index afc7a39026..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ObjectStateIdentifierQueryBuilder.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ObjectStateIdentifier; -use eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway as ObjectStateGateway; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class ObjectStateIdentifierQueryBuilder implements CriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof ObjectStateIdentifier; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentId $criterion */ - $queryBuilder - ->joinOnce( - 'content', - ObjectStateGateway::OBJECT_STATE_LINK_TABLE, - 'object_state_link', - 'content.id = object_state_link.contentobject_id', - ) - ->joinOnce( - 'content', - ObjectStateGateway::OBJECT_STATE_TABLE, - 'object_state', - 'object_state_link.contentobject_state_id = object_state.id' - ) - ->joinOnce( - 'object_state', - ObjectStateGateway::OBJECT_STATE_GROUP_TABLE, - 'object_state_group', - 'object_state.group_id = object_state_group.id' - ); - - $value = (array)$criterion->value; - - return $queryBuilder->expr()->in( - 'object_state.identifier', - $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/RemoteIdQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/RemoteIdQueryBuilder.php deleted file mode 100644 index 363b83f5ad..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/RemoteIdQueryBuilder.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\RemoteId; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class RemoteIdQueryBuilder implements CriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof RemoteId; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\RemoteId $criterion */ - return $queryBuilder->expr()->in( - 'content.remote_id', - $queryBuilder->createNamedParameter( - $criterion->value, - Connection::PARAM_STR_ARRAY - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Section/IdQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Section/IdQueryBuilder.php deleted file mode 100644 index 524fc16807..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Section/IdQueryBuilder.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Section; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\SectionId; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * Section ID Filtering Criterion Query Builder. - * - * @internal for internal use by Repository Filtering - */ -final class IdQueryBuilder implements CriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof SectionId; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentId $criterion */ - return $queryBuilder->expr()->in( - 'content.section_id', - $queryBuilder->createNamedParameter( - array_map('intval', $criterion->value), - Connection::PARAM_INT_ARRAY - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Section/IdentifierQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Section/IdentifierQueryBuilder.php deleted file mode 100644 index 4952149f78..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Section/IdentifierQueryBuilder.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Section; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\SectionIdentifier; -use eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * Section Identifier Filtering Criterion Query Builder. - * - * @internal for internal use by Repository Filtering - */ -final class IdentifierQueryBuilder implements CriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof SectionIdentifier; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - $queryBuilder - ->joinOnce( - 'content', - Gateway::CONTENT_SECTION_TABLE, - 'section', - 'content.section_id = section.id' - ); - - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\SectionIdentifier $criterion */ - return $queryBuilder->expr()->in( - 'section.identifier', - $queryBuilder->createNamedParameter( - (array)$criterion->value, - Connection::PARAM_STR_ARRAY - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/SiblingQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/SiblingQueryBuilder.php deleted file mode 100644 index 56384990e1..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/SiblingQueryBuilder.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Sibling; -use eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalAndQueryBuilder; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class SiblingQueryBuilder implements CriterionQueryBuilder -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalAndQueryBuilder */ - private $logicalAndQueryBuilder; - - /** - * Sibling is internally a composite LogicalAnd criterion, so is handled by delegation. - */ - public function __construct(LogicalAndQueryBuilder $logicalAndQueryBuilder) - { - $this->logicalAndQueryBuilder = $logicalAndQueryBuilder; - } - - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof Sibling; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Sibling $criterion */ - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd $_criterion */ - $_criterion = $criterion->criteria; - - return $this->logicalAndQueryBuilder->buildQueryConstraint($queryBuilder, $_criterion); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/BaseQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/BaseQueryBuilder.php deleted file mode 100644 index 9e118af597..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/BaseQueryBuilder.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type; - -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway as ContentTypeGateway; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * Content Type Criterion visitor query builder base. - * - * @internal for internal use by Repository Filtering - */ -abstract class BaseQueryBuilder implements CriterionQueryBuilder -{ - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - $queryBuilder - ->joinOnce( - 'content', - ContentTypeGateway::CONTENT_TYPE_TABLE, - 'content_type', - 'content.contentclass_id = content_type.id AND content_type.version = 0' - ); - - // the returned query constraint depends on concrete implementations - return null; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/GroupIdQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/GroupIdQueryBuilder.php deleted file mode 100644 index 4dae8e43ca..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/GroupIdQueryBuilder.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeGroupId; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway as ContentTypeGateway; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * Content Type Group ID Criterion visitor query builder. - * - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeGroupId - * - * @internal for internal use by Repository Filtering - */ -final class GroupIdQueryBuilder extends BaseQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof ContentTypeGroupId; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeGroupId $criterion */ - $queryBuilder - ->joinOnce( - 'content', - ContentTypeGateway::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE, - 'content_type_group_assignment', - 'content.contentclass_id = content_type_group_assignment.contentclass_id' - ); - - $queryBuilder - ->joinOnce( - 'content_type_group_assignment', - ContentTypeGateway::CONTENT_TYPE_GROUP_TABLE, - 'content_type_group', - 'content_type_group_assignment.group_id = content_type_group.id' - ); - - return $queryBuilder->expr()->in( - 'content_type_group.id', - $queryBuilder->createNamedParameter( - $criterion->value, - Connection::PARAM_INT_ARRAY - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/IdQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/IdQueryBuilder.php deleted file mode 100644 index 797edc9c91..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/IdQueryBuilder.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeId; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * Content Type ID Criterion visitor query builder. - * - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeId - * - * @internal for internal use by Repository Filtering - */ -final class IdQueryBuilder extends BaseQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof ContentTypeId; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - return $queryBuilder->expr()->in( - 'content_type.id', - $queryBuilder->createNamedParameter( - $criterion->value, - Connection::PARAM_INT_ARRAY - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/IdentifierQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/IdentifierQueryBuilder.php deleted file mode 100644 index 3ef2a59545..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/IdentifierQueryBuilder.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * Content Type Identifier Criterion visitor query builder. - * - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier - * - * @internal for internal use by Repository Filtering - */ -final class IdentifierQueryBuilder extends BaseQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof ContentTypeIdentifier; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - return $queryBuilder->expr()->in( - 'content_type.identifier', - $queryBuilder->createNamedParameter( - $criterion->value, - Connection::PARAM_STR_ARRAY - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilder.php deleted file mode 100644 index 584ba68124..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilder.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; - -use function array_filter; -use function array_map; -use function array_unique; -use function array_values; -use Doctrine\DBAL\Connection; -use function explode; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Ancestor; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use function trim; - -/** - * @internal for internal use by Repository Filtering - */ -final class AncestorQueryBuilder extends BaseLocationCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof Ancestor; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Ancestor $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - // extract numerical IDs from $criterion->value e.g. = ['/1/2/', '/1/4/10/'] - $locationIDs = array_merge( - ...array_map( - static function (string $pathString) { - return array_map( - 'intval', - array_filter(explode('/', trim($pathString, '/'))) - ); - }, - $criterion->value - ) - ); - - return $queryBuilder->expr()->in( - 'location.node_id', - $queryBuilder->createNamedParameter( - array_values(array_unique($locationIDs)), - Connection::PARAM_INT_ARRAY - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/BaseLocationCriterionQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/BaseLocationCriterionQueryBuilder.php deleted file mode 100644 index 49a3df3950..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/BaseLocationCriterionQueryBuilder.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; - -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -abstract class BaseLocationCriterionQueryBuilder implements CriterionQueryBuilder -{ - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - $queryBuilder->joinAllLocations(); - - return null; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/DepthQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/DepthQueryBuilder.php deleted file mode 100644 index 44c001db28..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/DepthQueryBuilder.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class DepthQueryBuilder extends BaseLocationCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof Location\Depth; - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location\Depth $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - return $queryBuilder->buildOperatorBasedCriterionConstraint( - 'location.depth', - $criterion->value, - $criterion->operator - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/IdQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/IdQueryBuilder.php deleted file mode 100644 index 7aefd97254..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/IdQueryBuilder.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LocationId; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class IdQueryBuilder extends BaseLocationCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof LocationId; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LocationId $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - return $queryBuilder->expr()->in( - 'location.node_id', - $queryBuilder->createNamedParameter( - $criterion->value, - Connection::PARAM_INT_ARRAY - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/IsMainLocationQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/IsMainLocationQueryBuilder.php deleted file mode 100644 index 2412e0ea45..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/IsMainLocationQueryBuilder.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class IsMainLocationQueryBuilder extends BaseLocationCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof Location\IsMainLocation; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location\IsMainLocation $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - return $criterion->value[0] === Location\IsMainLocation::MAIN - ? 'location.node_id = location.main_node_id' - : 'location.node_id <> location.main_node_id'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/ParentLocationIdQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/ParentLocationIdQueryBuilder.php deleted file mode 100644 index b038f7765c..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/ParentLocationIdQueryBuilder.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class ParentLocationIdQueryBuilder extends BaseLocationCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof ParentLocationId; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - return $queryBuilder->expr()->in( - 'location.parent_node_id', - $queryBuilder->createNamedParameter( - $criterion->value, - Connection::PARAM_INT_ARRAY - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/PriorityQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/PriorityQueryBuilder.php deleted file mode 100644 index b0da6de10a..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/PriorityQueryBuilder.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class PriorityQueryBuilder extends BaseLocationCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof Location\Priority; - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location\Priority $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - return $queryBuilder->buildOperatorBasedCriterionConstraint( - 'location.priority', - $criterion->value, - $criterion->operator - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/RemoteIdQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/RemoteIdQueryBuilder.php deleted file mode 100644 index 32f684efbe..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/RemoteIdQueryBuilder.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LocationRemoteId; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class RemoteIdQueryBuilder extends BaseLocationCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof LocationRemoteId; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LocationRemoteId $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - return $queryBuilder->expr()->in( - 'location.remote_id', - $queryBuilder->createNamedParameter( - $criterion->value, - Connection::PARAM_STR_ARRAY - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/SubtreeQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/SubtreeQueryBuilder.php deleted file mode 100644 index 912d6e1098..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/SubtreeQueryBuilder.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; - -use function array_map; -use Doctrine\DBAL\ParameterType; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class SubtreeQueryBuilder extends BaseLocationCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof Subtree; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - $expressionBuilder = $queryBuilder->expr(); - $statements = array_map( - static function (string $pathString) use ($queryBuilder, $expressionBuilder): string { - return $expressionBuilder->like( - 'location.path_string', - $queryBuilder->createNamedParameter($pathString . '%', ParameterType::STRING) - ); - }, - $criterion->value - ); - - return (string)$expressionBuilder->orX(...$statements); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/VisibilityQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/VisibilityQueryBuilder.php deleted file mode 100644 index 1a490b7acc..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/VisibilityQueryBuilder.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; - -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Visibility; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class VisibilityQueryBuilder extends BaseLocationCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof Visibility; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Visibility $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - $expressionBuilder = $queryBuilder->expr(); - $columnsExpressions = $this->getVisibilityColumnsExpressions( - $queryBuilder, - $criterion->value[0] - ); - - return $criterion->value[0] === Visibility::VISIBLE - ? (string)$expressionBuilder->andX(...$columnsExpressions) - : (string)$expressionBuilder->orX(...$columnsExpressions); - } - - private function getVisibilityColumnsExpressions( - QueryBuilder $queryBuilder, - int $visibleFlag - ): array { - $expressionBuilder = $queryBuilder->expr(); - - return [ - $expressionBuilder->eq( - 'location.is_hidden', - $queryBuilder->createNamedParameter($visibleFlag, ParameterType::INTEGER) - ), - $expressionBuilder->eq( - 'location.is_invisible', - $queryBuilder->createNamedParameter($visibleFlag, ParameterType::INTEGER) - ), - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalAndQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalAndQueryBuilder.php deleted file mode 100644 index affc59bf09..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalAndQueryBuilder.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\SPI\Persistence\Filter\CriterionVisitor; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class LogicalAndQueryBuilder implements CriterionQueryBuilder -{ - /** @var \eZ\Publish\SPI\Persistence\Filter\CriterionVisitor */ - private $criterionVisitor; - - public function __construct(CriterionVisitor $criterionVisitor) - { - $this->criterionVisitor = $criterionVisitor; - } - - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof LogicalAnd; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - $constraints = []; - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd $criterion */ - foreach ($criterion->criteria as $_criterion) { - /** @var \eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion $_criterion */ - $constraint = $this->criterionVisitor->visitCriteria($queryBuilder, $_criterion); - if (null !== $constraint) { - $constraints[] = $constraint; - } - } - - if (empty($constraints)) { - return null; - } - - return (string)$queryBuilder->expr()->andX(...$constraints); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalNotQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalNotQueryBuilder.php deleted file mode 100644 index 1ead0a5788..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalNotQueryBuilder.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\SPI\Persistence\Filter\CriterionVisitor; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class LogicalNotQueryBuilder implements CriterionQueryBuilder -{ - /** @var \eZ\Publish\SPI\Persistence\Filter\CriterionVisitor */ - private $criterionVisitor; - - public function __construct(CriterionVisitor $criterionVisitor) - { - $this->criterionVisitor = $criterionVisitor; - } - - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof LogicalNot; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot $criterion */ - if (!$criterion->criteria[0] instanceof FilteringCriterion) { - throw new InvalidArgumentException( - '$criterion', - sprintf( - 'Criterion needs to be a Filtering Criterion, got "%s"', - get_class($criterion->criteria[0]) - ) - ); - } - - $constraint = $this->criterionVisitor->visitCriteria( - $queryBuilder, - $criterion->criteria[0] - ); - - return null !== $constraint ? sprintf('NOT (%s)', $constraint) : null; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalOrQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalOrQueryBuilder.php deleted file mode 100644 index 36feb42754..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalOrQueryBuilder.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOr; -use eZ\Publish\SPI\Persistence\Filter\CriterionVisitor; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class LogicalOrQueryBuilder implements CriterionQueryBuilder -{ - /** @var \eZ\Publish\SPI\Persistence\Filter\CriterionVisitor */ - private $criterionVisitor; - - public function __construct(CriterionVisitor $criterionVisitor) - { - $this->criterionVisitor = $criterionVisitor; - } - - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof LogicalOr; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - $constraints = []; - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOr $criterion */ - foreach ($criterion->criteria as $_criterion) { - /** @var \eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion $_criterion */ - $constraint = $this->criterionVisitor->visitCriteria($queryBuilder, $_criterion); - if (null !== $constraint) { - $constraints[] = $constraint; - } - } - - if (empty($constraints)) { - return null; - } - - return (string)$queryBuilder->expr()->orX(...$constraints); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/MatchAllQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/MatchAllQueryBuilder.php deleted file mode 100644 index 72bd64c777..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/MatchAllQueryBuilder.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchAll; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class MatchAllQueryBuilder implements CriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof MatchAll; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - return '1=1'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/MatchNoneQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/MatchNoneQueryBuilder.php deleted file mode 100644 index dc05067336..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/MatchNoneQueryBuilder.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchNone; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class MatchNoneQueryBuilder implements CriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof MatchNone; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - return '1=0'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/BaseUserCriterionQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/BaseUserCriterionQueryBuilder.php deleted file mode 100644 index e05e7ddae7..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/BaseUserCriterionQueryBuilder.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User; - -use eZ\Publish\Core\FieldType\User\UserStorage\Gateway\DoctrineStorage; -use eZ\Publish\Core\Persistence\TransformationProcessor; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -abstract class BaseUserCriterionQueryBuilder implements CriterionQueryBuilder -{ - /** @var \eZ\Publish\Core\Persistence\TransformationProcessor */ - private $transformationProcessor; - - public function __construct(TransformationProcessor $transformationProcessor) - { - $this->transformationProcessor = $transformationProcessor; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - $queryBuilder - ->joinOnce( - 'content', - DoctrineStorage::USER_TABLE, - 'user_storage', - 'content.id = user_storage.contentobject_id' - ); - - return null; - } - - protected function transformCriterionValueForLikeExpression(string $value): string - { - return str_replace( - '*', - '%', - addcslashes( - $this->transformationProcessor->transformByGroup( - $value, - 'lowercase' - ), - '%_' - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserBasedQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserBasedQueryBuilder.php deleted file mode 100644 index 0f90b58f90..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserBasedQueryBuilder.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\IsUserBased; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class IsUserBasedQueryBuilder extends BaseUserCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof IsUserBased; - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\IsUserBased $criterion */ - // intentionally not using parent buildQueryConstraint - $queryBuilder - ->leftJoinOnce( - 'content', - 'ezuser', - 'user_storage', - 'content.id = user_storage.contentobject_id' - ); - - $isUserBased = (bool)reset($criterion->value); - $databasePlatform = $queryBuilder->getConnection()->getDatabasePlatform(); - - return $isUserBased - ? $databasePlatform->getIsNotNullExpression('user_storage.contentobject_id') - : $databasePlatform->getIsNullExpression('user_storage.contentobject_id'); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserEnabledQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserEnabledQueryBuilder.php deleted file mode 100644 index f67b59af7c..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserEnabledQueryBuilder.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User; - -use Doctrine\DBAL\ParameterType; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\IsUserEnabled; -use eZ\Publish\Core\FieldType\User\UserStorage\Gateway\DoctrineStorage; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class IsUserEnabledQueryBuilder extends BaseUserCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof IsUserEnabled; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\IsUserEnabled $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - $queryBuilder->joinOnce( - 'user_storage', - DoctrineStorage::USER_SETTING_TABLE, - 'user_settings', - 'user_storage.contentobject_id = user_settings.user_id' - ); - - return $queryBuilder->expr()->eq( - 'user_settings.is_enabled', - $queryBuilder->createNamedParameter( - (int)reset($criterion->value), - ParameterType::INTEGER - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/GroupQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/GroupQueryBuilder.php deleted file mode 100644 index 587a027e0c..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/GroupQueryBuilder.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User\Metadata; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\UserMetadata; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class GroupQueryBuilder implements CriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof UserMetadata && $criterion->target === UserMetadata::GROUP; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\UserMetadata $criterion */ - $value = (array)$criterion->value; - - $queryBuilder - ->joinOnce( - 'content', - LocationGateway::CONTENT_TREE_TABLE, - 'user_location', - 'content.owner_id = user_location.contentobject_id' - ) - ->joinOnce( - 'user_location', - LocationGateway::CONTENT_TREE_TABLE, - 'user_group_location', - 'user_location.parent_node_id = user_group_location.node_id' - ); - - return $queryBuilder->expr()->in( - 'user_group_location.contentobject_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/ModifierQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/ModifierQueryBuilder.php deleted file mode 100644 index 942877cc72..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/ModifierQueryBuilder.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User\Metadata; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\UserMetadata; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class ModifierQueryBuilder implements CriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof UserMetadata && $criterion->target === UserMetadata::MODIFIER; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\UserMetadata $criterion */ - $value = (array)$criterion->value; - - return $queryBuilder->expr()->in( - 'version.creator_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/OwnerQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/OwnerQueryBuilder.php deleted file mode 100644 index e2277610a5..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/OwnerQueryBuilder.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User\Metadata; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\UserMetadata; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class OwnerQueryBuilder implements CriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof UserMetadata && $criterion->target === UserMetadata::OWNER; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\UserMetadata $criterion */ - $value = (array)$criterion->value; - - return $queryBuilder->expr()->in( - 'content.owner_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserEmailQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserEmailQueryBuilder.php deleted file mode 100644 index b1cae4a6d1..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserEmailQueryBuilder.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\UserEmail; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class UserEmailQueryBuilder extends BaseUserCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof UserEmail; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\UserEmail $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - if (Operator::LIKE === $criterion->operator) { - $expression = $queryBuilder->expr()->like( - 'user_storage.email', - $queryBuilder->createNamedParameter( - $this->transformCriterionValueForLikeExpression($criterion->value) - ) - ); - } else { - $value = (array)$criterion->value; - $expression = $queryBuilder->expr()->in( - 'user_storage.email', - $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) - ); - } - - return $expression; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserIdQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserIdQueryBuilder.php deleted file mode 100644 index c7d710e7c3..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserIdQueryBuilder.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\UserId; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class UserIdQueryBuilder extends BaseUserCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof UserId; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\UserId $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - $value = (array)$criterion->value; - - return $queryBuilder->expr()->in( - 'user_storage.contentobject_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserLoginQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserLoginQueryBuilder.php deleted file mode 100644 index a6c1100287..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserLoginQueryBuilder.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User; - -use Doctrine\DBAL\Connection; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\UserLogin; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering - */ -final class UserLoginQueryBuilder extends BaseUserCriterionQueryBuilder -{ - public function accepts(FilteringCriterion $criterion): bool - { - return $criterion instanceof UserLogin; - } - - public function buildQueryConstraint( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): ?string { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\UserId $criterion */ - parent::buildQueryConstraint($queryBuilder, $criterion); - - $expr = $queryBuilder->expr(); - if (Operator::LIKE === $criterion->operator) { - return $expr->like( - 'user_storage.login', - $queryBuilder->createNamedParameter( - $this->transformCriterionValueForLikeExpression($criterion->value) - ) - ); - } - - $value = (array)$criterion->value; - - return $expr->in( - 'user_storage.login', - $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionVisitor.php b/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionVisitor.php deleted file mode 100644 index ae7a009842..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/CriterionVisitor.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter; - -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\SPI\Persistence\Filter\CriterionVisitor as FilteringCriterionVisitor; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use function sprintf; - -/** - * @internal Type-hint {@see \eZ\Publish\SPI\Persistence\Filter\CriterionVisitor} instead - */ -final class CriterionVisitor implements FilteringCriterionVisitor -{ - /** @var \eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder[] */ - private $criterionQueryBuilders; - - public function __construct(iterable $criterionQueryBuilders) - { - $this->setCriterionQueryBuilders($criterionQueryBuilders); - } - - public function setCriterionQueryBuilders(iterable $criterionQueryBuilders): void - { - $this->criterionQueryBuilders = $criterionQueryBuilders; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException if there's no builder for a criterion - */ - public function visitCriteria( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): string { - foreach ($this->criterionQueryBuilders as $criterionQueryBuilder) { - if ($criterionQueryBuilder->accepts($criterion)) { - return $criterionQueryBuilder->buildQueryConstraint( - $queryBuilder, - $criterion - ); - } - } - - throw new NotImplementedException( - sprintf( - 'There is no Filtering Criterion Query Builder for %s Criterion', - get_class($criterion) - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php b/eZ/Publish/Core/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php deleted file mode 100644 index 0cef49854a..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php +++ /dev/null @@ -1,292 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Content\Doctrine; - -use function array_filter; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\Platforms\AbstractPlatform; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Gateway; -use eZ\Publish\SPI\Persistence\Filter\CriterionVisitor; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Persistence\Filter\SortClauseVisitor; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use function iterator_to_array; -use function sprintf; -use Traversable; - -/** - * @internal for internal use by Legacy Storage - */ -final class DoctrineGateway implements Gateway -{ - public const COLUMN_MAP = [ - // Content Info - 'content_id' => 'content.id', - 'content_type_id' => 'content.contentclass_id', - 'content_current_version' => 'content.current_version', - 'content_initial_language_id' => 'content.initial_language_id', - 'content_language_mask' => 'content.language_mask', - 'content_modified' => 'content.modified', - 'content_name' => 'content.name', - 'content_owner_id' => 'content.owner_id', - 'content_published' => 'content.published', - 'content_remote_id' => 'content.remote_id', - 'content_section_id' => 'content.section_id', - 'content_status' => 'content.status', - 'content_is_hidden' => 'content.is_hidden', - // Version Info - 'content_version_id' => 'version.id', - 'content_version_no' => 'version.version', - 'content_version_creator_id' => 'version.creator_id', - 'content_version_created' => 'version.created', - 'content_version_modified' => 'version.modified', - 'content_version_status' => 'version.status', - 'content_version_language_mask' => 'version.language_mask', - 'content_version_initial_language_id' => 'version.initial_language_id', - // Main Location (nullable) - 'content_main_location_id' => 'main_location.main_node_id', - ]; - - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** @var \eZ\Publish\SPI\Persistence\Filter\CriterionVisitor */ - private $criterionVisitor; - - /** @var \eZ\Publish\SPI\Persistence\Filter\SortClauseVisitor */ - private $sortClauseVisitor; - - public function __construct( - Connection $connection, - CriterionVisitor $criterionVisitor, - SortClauseVisitor $sortClauseVisitor - ) { - $this->connection = $connection; - $this->criterionVisitor = $criterionVisitor; - $this->sortClauseVisitor = $sortClauseVisitor; - } - - private function getDatabasePlatform(): AbstractPlatform - { - try { - return $this->connection->getDatabasePlatform(); - } catch (DBALException $e) { - throw DatabaseException::wrap($e); - } - } - - public function count(FilteringCriterion $criterion): int - { - $query = $this->buildQuery( - [$this->getDatabasePlatform()->getCountExpression('DISTINCT content.id')], - $criterion - ); - - return (int)$query->execute()->fetch(FetchMode::COLUMN); - } - - public function find( - FilteringCriterion $criterion, - array $sortClauses, - int $limit, - int $offset - ): iterable { - $query = $this->buildQuery(iterator_to_array($this->getColumns()), $criterion); - $this->sortClauseVisitor->visitSortClauses($query, $sortClauses); - - // get additional data for the same query constraints - $names = $this->bulkFetchVersionNames(clone $query); - $fieldValues = $this->bulkFetchFieldValues(clone $query); - - // wrap query to avoid duplicate entries for multiple Locations - $wrappedQuery = $this->wrapMainQuery($query); - $wrappedQuery->setFirstResult($offset); - if ($limit > 0) { - $wrappedQuery->setMaxResults($limit); - } - - $resultStatement = $wrappedQuery->execute(); - while (false !== ($row = $resultStatement->fetch(FetchMode::ASSOCIATIVE))) { - $contentId = (int)$row['content_id']; - $versionNo = (int)$row['content_version_no']; - $row['content_version_names'] = $this->extractVersionNames( - $names, - $contentId, - $versionNo - ); - $row['content_version_fields'] = $this->extractFieldValues( - $fieldValues, - $contentId, - $versionNo - ); - - yield $row; - } - } - - private function buildQuery( - array $columns, - FilteringCriterion $criterion - ): FilteringQueryBuilder { - $queryBuilder = new FilteringQueryBuilder($this->connection); - - $expressionBuilder = $queryBuilder->expr(); - $queryBuilder - ->select($columns) - ->distinct() - ->from(ContentGateway::CONTENT_ITEM_TABLE, 'content') - ->joinPublishedVersion() - ->leftJoin( - 'content', - LocationGateway::CONTENT_TREE_TABLE, - 'main_location', - $expressionBuilder->andX( - 'content.id = main_location.contentobject_id', - 'main_location.main_node_id = main_location.node_id' - ) - ); - - $constraint = $this->criterionVisitor->visitCriteria($queryBuilder, $criterion); - if (null !== $constraint) { - $queryBuilder->where($constraint); - } - - return $queryBuilder; - } - - /** - * Return names as a map of <code>'<translation_language_code>' => '<name>'</code>. - * - * Process data fetched by {@see bulkFetchVersionNames} - */ - private function extractVersionNames(array $names, int $contentId, int $versionNo): array - { - $rawVersionNames = $this->extractVersionData($names, $contentId, $versionNo); - - $names = []; - foreach ($rawVersionNames as $nameRow) { - $names[$nameRow['real_translation']] = $nameRow['name']; - } - - return $names; - } - - private function extractFieldValues(array $fieldValues, int $contentId, int $versionNo): array - { - return $this->extractVersionData($fieldValues, $contentId, $versionNo); - } - - /** - * Extract Version-specific data from bulk-loaded rows. - */ - private function extractVersionData(array $rows, int $contentId, int $versionNo): array - { - return array_filter( - $rows, - static function (array $row) use ($contentId, $versionNo) { - return (int)$row['content_id'] === $contentId - && (int)$row['version_no'] === $versionNo; - } - ); - } - - private function bulkFetchVersionNames(FilteringQueryBuilder $query): array - { - $query - // completely reset SELECT part to get only needed data - ->select( - 'content.id AS content_id', - 'version.version AS version_no', - 'content_name.name', - 'content_name.real_translation' - ) - ->distinct() - // join names table to pre-existing query - ->joinOnce( - 'content', - ContentGateway::CONTENT_NAME_TABLE, - 'content_name', - (string)$query->expr()->andX( - 'content.id = content_name.contentobject_id', - 'version.version = content_name.content_version', - 'version.language_mask & content_name.language_id > 0' - ) - ) - // reset not needed parts, keeping FROM, other JOINs, and WHERE constraints - ->setMaxResults(null) - ->setFirstResult(null) - ->resetQueryPart('orderBy'); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - private function bulkFetchFieldValues(FilteringQueryBuilder $query): array - { - $query - // completely reset SELECT part to get only needed data - ->select( - 'content_field.contentobject_id AS content_id', - 'content_field.version AS version_no', - 'content_field.id AS field_id', - 'content_field.contentclassattribute_id AS field_definition_id', - 'content_field.data_type_string AS field_type', - 'content_field.language_code AS field_language_code', - 'content_field.data_float AS field_data_float', - 'content_field.data_int AS field_data_int', - 'content_field.data_text AS field_data_text', - 'content_field.sort_key_int AS field_sort_key_int', - 'content_field.sort_key_string AS field_sort_key_string' - ) - ->distinct() - ->joinOnce( - 'content', - ContentGateway::CONTENT_FIELD_TABLE, - 'content_field', - (string)$query->expr()->andX( - 'content.id = content_field.contentobject_id', - 'version.version = content_field.version', - 'version.language_mask & content_field.language_id = content_field.language_id' - ) - ) - // reset not needed parts, keeping FROM, other JOINs, and WHERE constraints - ->setMaxResults(null) - ->setFirstResult(null) - ->resetQueryPart('orderBy'); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - private function getColumns(): Traversable - { - foreach (self::COLUMN_MAP as $columnAlias => $columnName) { - yield "{$columnName} AS {$columnAlias}"; - } - } - - /** - * Wrap query to avoid duplicate entries for multiple Locations. - */ - private function wrapMainQuery(FilteringQueryBuilder $query): QueryBuilder - { - $wrappedQuery = $this->connection->createQueryBuilder(); - $wrappedQuery - ->select(array_keys(self::COLUMN_MAP)) - ->distinct() - ->from(sprintf('(%s)', $query->getSQL()), 'wrapped') - ->setParameters($query->getParameters(), $query->getParameterTypes()); - - return $wrappedQuery; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/Gateway/Content/GatewayDataMapper.php b/eZ/Publish/Core/Persistence/Legacy/Filter/Gateway/Content/GatewayDataMapper.php deleted file mode 100644 index ba6c39d8fa..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/Gateway/Content/GatewayDataMapper.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Content; - -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; - -/** - * @internal for internal use by Repository Filtering Handlers - */ -interface GatewayDataMapper -{ - /** - * Map raw database data to SPI Persistence ContentItem ValueObject. - */ - public function mapRawDataToPersistenceContentItem(array $row): Content\ContentItem; - - public function mapContentMetadataToPersistenceContentInfo(array $row): ContentInfo; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/Gateway/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/Filter/Gateway/Gateway.php deleted file mode 100644 index ec64507575..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/Gateway/Gateway.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\Gateway; - -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * Repository filtering gateway. - * - * @internal for internal use by Legacy Storage - */ -interface Gateway -{ - /** - * Return number of matched rows for the given Criteria (a total count w/o pagination constraints). - */ - public function count(FilteringCriterion $criterion): int; - - /** - * Return iterator for raw Repository data for the given Query result filtered by the given Criteria, - * sorted by the given Sort Clauses and constrained by the given pagination limit & offset. - * - * @param \eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause[] $sortClauses - */ - public function find( - FilteringCriterion $criterion, - array $sortClauses, - int $limit, - int $offset - ): iterable; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php b/eZ/Publish/Core/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php deleted file mode 100644 index 3f545d58d2..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php +++ /dev/null @@ -1,142 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Location\Doctrine; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\Platforms\AbstractPlatform; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Gateway; -use eZ\Publish\SPI\Persistence\Filter\CriterionVisitor; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Persistence\Filter\SortClauseVisitor; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Legacy Storage - */ -final class DoctrineGateway implements Gateway -{ - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** @var \eZ\Publish\SPI\Persistence\Filter\CriterionVisitor */ - private $criterionVisitor; - - /** @var \eZ\Publish\SPI\Persistence\Filter\SortClauseVisitor */ - private $sortClauseVisitor; - - public function __construct( - Connection $connection, - CriterionVisitor $criterionVisitor, - SortClauseVisitor $sortClauseVisitor - ) { - $this->connection = $connection; - $this->criterionVisitor = $criterionVisitor; - $this->sortClauseVisitor = $sortClauseVisitor; - } - - private function getDatabasePlatform(): AbstractPlatform - { - try { - return $this->connection->getDatabasePlatform(); - } catch (DBALException $e) { - throw DatabaseException::wrap($e); - } - } - - public function count(FilteringCriterion $criterion): int - { - $query = $this->buildQuery($criterion); - - $query->select($this->getDatabasePlatform()->getCountExpression('DISTINCT location.node_id')); - - return (int)$query->execute()->fetch(FetchMode::COLUMN); - } - - public function find( - FilteringCriterion $criterion, - array $sortClauses, - int $limit, - int $offset - ): iterable { - $query = $this->buildQuery($criterion); - $this->sortClauseVisitor->visitSortClauses($query, $sortClauses); - - $query->setFirstResult($offset); - if ($limit > 0) { - $query->setMaxResults($limit); - } - - $resultStatement = $query->execute(); - - while (false !== ($row = $resultStatement->fetch(FetchMode::ASSOCIATIVE))) { - yield $row; - } - } - - private function buildQuery(FilteringCriterion $criterion): FilteringQueryBuilder - { - $queryBuilder = new FilteringQueryBuilder($this->connection); - $queryBuilder - ->select( - [ - // Location - 'location.node_id AS location_node_id', - 'location.priority AS location_priority', - 'location.is_hidden AS location_is_hidden', - 'location.is_invisible AS location_is_invisible', - 'location.remote_id AS location_remote_id', - 'location.contentobject_id AS location_contentobject_id', - 'location.parent_node_id AS location_parent_node_id', - 'location.path_identification_string AS location_path_identification_string', - 'location.path_string AS location_path_string', - 'location.depth AS location_depth', - 'location.sort_field AS location_sort_field', - 'location.sort_order AS location_sort_order', - // Main Location (nullable) - 'location.main_node_id AS content_main_location_id', - // Content Info - 'content.id AS content_id', - 'content.contentclass_id AS content_type_id', - 'content.current_version AS content_current_version', - 'content.initial_language_id AS content_initial_language_id', - 'content.language_mask AS content_language_mask', - 'content.modified AS content_modified', - 'content.name AS content_name', - 'content.owner_id AS content_owner_id', - 'content.published AS content_published', - 'content.remote_id AS content_remote_id', - 'content.section_id AS content_section_id', - 'content.status AS content_status', - 'content.is_hidden AS content_is_hidden', - ] - ) - ->distinct() - ->from(LocationGateway::CONTENT_TREE_TABLE, 'location') - ->join( - 'location', - ContentGateway::CONTENT_ITEM_TABLE, - 'content', - 'content.id = location.contentobject_id' - ) - ->joinPublishedVersion() - ; - - $constraint = $this->criterionVisitor->visitCriteria($queryBuilder, $criterion); - if (null !== $constraint) { - $queryBuilder->where($constraint); - } - - return $queryBuilder; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/Handler/ContentFilteringHandler.php b/eZ/Publish/Core/Persistence/Legacy/Filter/Handler/ContentFilteringHandler.php deleted file mode 100644 index 488b7efdb9..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/Handler/ContentFilteringHandler.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\Handler; - -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler; -use eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Content\GatewayDataMapper; -use eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Gateway as FilteringGateway; -use eZ\Publish\SPI\Persistence\Content\ContentItem; -use eZ\Publish\SPI\Persistence\Filter\Content\Handler; -use eZ\Publish\SPI\Persistence\Filter\Content\LazyContentItemListIterator; - -/** - * @internal for internal use by Repository Storage abstraction - */ -final class ContentFilteringHandler implements Handler -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Gateway */ - private $gateway; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Content\GatewayDataMapper */ - private $mapper; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler */ - private $fieldHandler; - - public function __construct( - FilteringGateway $gateway, - GatewayDataMapper $mapper, - FieldHandler $fieldHandler - ) { - $this->gateway = $gateway; - $this->mapper = $mapper; - $this->fieldHandler = $fieldHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Filter\Content\LazyContentItemListIterator - */ - public function find(Filter $filter): iterable - { - $count = $this->gateway->count($filter->getCriterion()); - - // wrapped list before creating the actual API ContentList to pass totalCount - // for paginated result a total count is not going to be a number of items in a current page - $list = new LazyContentItemListIterator($count); - if ($count === 0) { - return $list; - } - - $list->prepareIterator( - $this->gateway->find( - $filter->getCriterion(), - $filter->getSortClauses(), - $filter->getLimit(), - $filter->getOffset() - ), - // called on each iteration of the iterator returned by find - function (array $row): ContentItem { - $contentItem = $this->mapper->mapRawDataToPersistenceContentItem($row); - $this->fieldHandler->loadExternalFieldData($contentItem->getContent()); - - return $contentItem; - } - ); - - return $list; - } - - public function count(Filter $filter): int - { - return $this->gateway->count($filter->getCriterion()); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/Handler/LocationFilteringHandler.php b/eZ/Publish/Core/Persistence/Legacy/Filter/Handler/LocationFilteringHandler.php deleted file mode 100644 index 7c27e62a16..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/Handler/LocationFilteringHandler.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\Handler; - -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper as LocationLegacyMapper; -use eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Content\GatewayDataMapper as ContentGatewayDataMapper; -use eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Gateway; -use eZ\Publish\SPI\Persistence\Content\LocationWithContentInfo; -use eZ\Publish\SPI\Persistence\Filter\Location\Handler; -use eZ\Publish\SPI\Persistence\Filter\Location\LazyLocationListIterator; - -class LocationFilteringHandler implements Handler -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Gateway */ - private $gateway; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper */ - private $locationMapper; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Content\GatewayDataMapper */ - private $contentGatewayDataMapper; - - public function __construct( - Gateway $gateway, - LocationLegacyMapper $locationMapper, - ContentGatewayDataMapper $contentGatewayDataMapper - ) { - $this->gateway = $gateway; - $this->locationMapper = $locationMapper; - $this->contentGatewayDataMapper = $contentGatewayDataMapper; - } - - public function find(Filter $filter): iterable - { - $count = $this->gateway->count($filter->getCriterion()); - - // wrapped list before creating the actual API LocationList to pass totalCount - // for paginated result a total count is not going to be a number of items in a current page - $list = new LazyLocationListIterator($count); - if ($count === 0) { - return $list; - } - - $list->prepareIterator( - $this->gateway->find( - $filter->getCriterion(), - $filter->getSortClauses(), - $filter->getLimit(), - $filter->getOffset() - ), - // called on each iteration of the iterator returned by find - function (array $row): LocationWithContentInfo { - return new LocationWithContentInfo( - $this->locationMapper->createLocationFromRow($row, 'location_'), - $this->contentGatewayDataMapper->mapContentMetadataToPersistenceContentInfo( - $row - ) - ); - } - ); - - return $list; - } - - public function count(Filter $filter): int - { - return $this->gateway->count($filter->getCriterion()); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/DateModifiedSortClauseQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/DateModifiedSortClauseQueryBuilder.php deleted file mode 100644 index 0d4192ebe1..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/DateModifiedSortClauseQueryBuilder.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Content; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\DateModified; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use eZ\Publish\SPI\Repository\Values\Filter\SortClauseQueryBuilder; - -class DateModifiedSortClauseQueryBuilder implements SortClauseQueryBuilder -{ - public function accepts(FilteringSortClause $sortClause): bool - { - return $sortClause instanceof DateModified; - } - - public function buildQuery( - FilteringQueryBuilder $queryBuilder, - FilteringSortClause $sortClause - ): void { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause */ - $queryBuilder->addOrderBy('content.modified', $sortClause->direction); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/DatePublishedSortClauseQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/DatePublishedSortClauseQueryBuilder.php deleted file mode 100644 index 6701bf76ba..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/DatePublishedSortClauseQueryBuilder.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Content; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\DatePublished; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use eZ\Publish\SPI\Repository\Values\Filter\SortClauseQueryBuilder; - -class DatePublishedSortClauseQueryBuilder implements SortClauseQueryBuilder -{ - public function accepts(FilteringSortClause $sortClause): bool - { - return $sortClause instanceof DatePublished; - } - - public function buildQuery( - FilteringQueryBuilder $queryBuilder, - FilteringSortClause $sortClause - ): void { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause */ - $queryBuilder->addOrderBy('content.published', $sortClause->direction); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/IdSortClauseQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/IdSortClauseQueryBuilder.php deleted file mode 100644 index c75d77f635..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/IdSortClauseQueryBuilder.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Content; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\ContentId; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use eZ\Publish\SPI\Repository\Values\Filter\SortClauseQueryBuilder; - -class IdSortClauseQueryBuilder implements SortClauseQueryBuilder -{ - public function accepts(FilteringSortClause $sortClause): bool - { - return $sortClause instanceof ContentId; - } - - public function buildQuery( - FilteringQueryBuilder $queryBuilder, - FilteringSortClause $sortClause - ): void { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause */ - $queryBuilder->addOrderBy('content.id', $sortClause->direction); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/NameSortClauseQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/NameSortClauseQueryBuilder.php deleted file mode 100644 index dad8f1d5c1..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/NameSortClauseQueryBuilder.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Content; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\ContentName; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use eZ\Publish\SPI\Repository\Values\Filter\SortClauseQueryBuilder; - -class NameSortClauseQueryBuilder implements SortClauseQueryBuilder -{ - public function accepts(FilteringSortClause $sortClause): bool - { - return $sortClause instanceof ContentName; - } - - public function buildQuery( - FilteringQueryBuilder $queryBuilder, - FilteringSortClause $sortClause - ): void { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause */ - $queryBuilder->addOrderBy('content.name', $sortClause->direction); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/SectionIdentifierSortClauseQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/SectionIdentifierSortClauseQueryBuilder.php deleted file mode 100644 index 4b8e796cdb..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/SectionIdentifierSortClauseQueryBuilder.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Content; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\SectionIdentifier; -use eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway as SectionGateway; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use eZ\Publish\SPI\Repository\Values\Filter\SortClauseQueryBuilder; - -class SectionIdentifierSortClauseQueryBuilder implements SortClauseQueryBuilder -{ - public function accepts(FilteringSortClause $sortClause): bool - { - return $sortClause instanceof SectionIdentifier; - } - - public function buildQuery( - FilteringQueryBuilder $queryBuilder, - FilteringSortClause $sortClause - ): void { - $queryBuilder - ->addSelect('section.identifier') - ->joinOnce( - 'content', - SectionGateway::CONTENT_SECTION_TABLE, - 'section', - 'content.section_id = section.id' - ); - - /** @var \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause */ - $queryBuilder->addOrderBy('section.identifier', $sortClause->direction); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/SectionNameSortClauseQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/SectionNameSortClauseQueryBuilder.php deleted file mode 100644 index 0de354b427..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/SectionNameSortClauseQueryBuilder.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Content; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\SectionName; -use eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway as SectionGateway; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use eZ\Publish\SPI\Repository\Values\Filter\SortClauseQueryBuilder; - -class SectionNameSortClauseQueryBuilder implements SortClauseQueryBuilder -{ - public function accepts(FilteringSortClause $sortClause): bool - { - return $sortClause instanceof SectionName; - } - - public function buildQuery( - FilteringQueryBuilder $queryBuilder, - FilteringSortClause $sortClause - ): void { - $queryBuilder - ->addSelect('section.name') - ->joinOnce( - 'content', - SectionGateway::CONTENT_SECTION_TABLE, - 'section', - 'content.section_id = section.id' - ); - - /** @var \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause */ - $queryBuilder->addOrderBy('section.name', $sortClause->direction); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/BaseLocationSortClauseQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/BaseLocationSortClauseQueryBuilder.php deleted file mode 100644 index eff8a40403..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/BaseLocationSortClauseQueryBuilder.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Location; - -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use eZ\Publish\SPI\Repository\Values\Filter\SortClauseQueryBuilder; - -/** - * @internal - */ -abstract class BaseLocationSortClauseQueryBuilder implements SortClauseQueryBuilder -{ - public function buildQuery( - FilteringQueryBuilder $queryBuilder, - FilteringSortClause $sortClause - ): void { - $sort = $this->getSortingExpression(); - $queryBuilder - ->addSelect($this->getSortingExpression()) - ->joinAllLocations(); - - /** @var \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause */ - $queryBuilder->addOrderBy($sort, $sortClause->direction); - } - - abstract protected function getSortingExpression(): string; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/DepthQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/DepthQueryBuilder.php deleted file mode 100644 index 3d758aefa4..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/DepthQueryBuilder.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; - -/** - * @internal - */ -final class DepthQueryBuilder extends BaseLocationSortClauseQueryBuilder -{ - public function accepts(FilteringSortClause $sortClause): bool - { - return $sortClause instanceof Location\Depth; - } - - protected function getSortingExpression(): string - { - return 'location.depth'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/IdQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/IdQueryBuilder.php deleted file mode 100644 index facef4e6ad..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/IdQueryBuilder.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; - -/** - * @internal - */ -final class IdQueryBuilder extends BaseLocationSortClauseQueryBuilder -{ - public function accepts(FilteringSortClause $sortClause): bool - { - return $sortClause instanceof Location\Id; - } - - protected function getSortingExpression(): string - { - return 'location.node_id'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/PathQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/PathQueryBuilder.php deleted file mode 100644 index c093c92852..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/PathQueryBuilder.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; - -class PathQueryBuilder extends BaseLocationSortClauseQueryBuilder -{ - public function accepts(FilteringSortClause $sortClause): bool - { - return $sortClause instanceof Location\Path; - } - - protected function getSortingExpression(): string - { - return 'location.path_string'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/PriorityQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/PriorityQueryBuilder.php deleted file mode 100644 index 92354dff28..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/PriorityQueryBuilder.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; - -class PriorityQueryBuilder extends BaseLocationSortClauseQueryBuilder -{ - public function accepts(FilteringSortClause $sortClause): bool - { - return $sortClause instanceof Location\Priority; - } - - protected function getSortingExpression(): string - { - return 'location.priority'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/VisibilityQueryBuilder.php b/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/VisibilityQueryBuilder.php deleted file mode 100644 index 214bd26763..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/VisibilityQueryBuilder.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; - -class VisibilityQueryBuilder extends BaseLocationSortClauseQueryBuilder -{ - public function accepts(FilteringSortClause $sortClause): bool - { - return $sortClause instanceof Location\Visibility; - } - - protected function getSortingExpression(): string - { - return 'location.is_invisible'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseVisitor.php b/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseVisitor.php deleted file mode 100644 index 16240d71d1..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Filter/SortClauseVisitor.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Filter; - -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Persistence\Filter\SortClauseVisitor as FilteringSortClauseVisitor; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use eZ\Publish\SPI\Repository\Values\Filter\SortClauseQueryBuilder; - -/** - * @internal Type-hint {@see \eZ\Publish\SPI\Persistence\Filter\SortClauseVisitor} instead. - */ -final class SortClauseVisitor implements FilteringSortClauseVisitor -{ - /** @var \eZ\Publish\SPI\Repository\Values\Filter\SortClauseQueryBuilder[] */ - private $sortClauseQueryBuilders; - - /** @var \eZ\Publish\SPI\Repository\Values\Filter\SortClauseQueryBuilder[] */ - private static $queryBuildersForSortClauses = []; - - public function __construct(iterable $sortClauseQueryBuilders) - { - $this->sortClauseQueryBuilders = $sortClauseQueryBuilders; - } - - /** - * @param \eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause[] $sortClauses - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException if there's no builder for a Sort Clause - */ - public function visitSortClauses(FilteringQueryBuilder $queryBuilder, array $sortClauses): void - { - foreach ($sortClauses as $sortClause) { - $this - ->getQueryBuilderForSortClause($sortClause) - ->buildQuery($queryBuilder, $sortClause); - } - } - - /** - * Cache Query Builders in-memory and get the one for the given Sort Clause. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - private function getQueryBuilderForSortClause( - FilteringSortClause $sortClause - ): SortClauseQueryBuilder { - $sortClauseFQCN = get_class($sortClause); - if (!isset(self::$queryBuildersForSortClauses[$sortClauseFQCN])) { - foreach ($this->sortClauseQueryBuilders as $sortClauseQueryBuilder) { - if ($sortClauseQueryBuilder->accepts($sortClause)) { - self::$queryBuildersForSortClauses[$sortClauseFQCN] = $sortClauseQueryBuilder; - break; - } - } - } - - if (!isset(self::$queryBuildersForSortClauses[$sortClauseFQCN])) { - throw new NotImplementedException( - "There are no Query Builders for {$sortClauseFQCN} Sort Clause" - ); - } - - return self::$queryBuildersForSortClauses[$sortClauseFQCN]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Handler.php deleted file mode 100644 index 30d76ed296..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Handler.php +++ /dev/null @@ -1,261 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy; - -use eZ\Publish\Core\Persistence\Legacy\URL\Handler as UrlHandler; -use eZ\Publish\SPI\Persistence\Bookmark\Handler as BookmarkHandler; -use eZ\Publish\SPI\Persistence\Content\Handler as ContentHandler; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as LocationHandler; -use eZ\Publish\SPI\Persistence\Content\Location\Trash\Handler as TrashHandler; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Handler as ObjectStateHandler; -use eZ\Publish\SPI\Persistence\Content\Section\Handler as SectionHandler; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandler; -use eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler as UrlAliasHandler; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler as UrlWildcardHandler; -use eZ\Publish\SPI\Persistence\Handler as HandlerInterface; -use eZ\Publish\SPI\Persistence\Notification\Handler as NotificationHandler; -use eZ\Publish\SPI\Persistence\Setting\Handler as SettingHandler; -use eZ\Publish\SPI\Persistence\TransactionHandler as SPITransactionHandler; -use eZ\Publish\SPI\Persistence\User\Handler as UserHandler; -use eZ\Publish\SPI\Persistence\UserPreference\Handler as UserPreferenceHandler; - -/** - * The main handler for Legacy Storage Engine. - */ -class Handler implements HandlerInterface -{ - /** @var \eZ\Publish\SPI\Persistence\Content\Handler */ - protected $contentHandler; - - /** @var \eZ\Publish\SPI\Persistence\Content\Type\Handler */ - protected $contentTypeHandler; - - /** @var \eZ\Publish\SPI\Persistence\Content\Language\Handler */ - protected $languageHandler; - - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Handler */ - protected $locationHandler; - - /** @var \eZ\Publish\SPI\Persistence\Content\ObjectState\Handler */ - protected $objectStateHandler; - - /** @var \eZ\Publish\SPI\Persistence\Content\Section\Handler */ - protected $sectionHandler; - - /** @var \eZ\Publish\SPI\Persistence\TransactionHandler */ - protected $transactionHandler; - - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Trash\Handler */ - protected $trashHandler; - - /** @var \eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler */ - protected $urlAliasHandler; - - /** @var \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler */ - protected $urlWildcardHandler; - - /** @var \eZ\Publish\SPI\Persistence\User\Handler */ - protected $userHandler; - - /** @var \eZ\Publish\Core\Persistence\Legacy\URL\Handler */ - protected $urlHandler; - - /** @var \eZ\Publish\SPI\Persistence\Bookmark\Handler */ - protected $bookmarkHandler; - - /** @var \eZ\Publish\SPI\Persistence\Notification\Handler */ - protected $notificationHandler; - - /** @var \eZ\Publish\SPI\Persistence\UserPreference\Handler */ - protected $userPreferenceHandler; - - /** @var \eZ\Publish\SPI\Persistence\Setting\Handler */ - private $settingHandler; - - /** - * @param \eZ\Publish\SPI\Persistence\Content\Handler $contentHandler - * @param \eZ\Publish\SPI\Persistence\Content\Type\Handler $contentTypeHandler - * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $languageHandler - * @param \eZ\Publish\SPI\Persistence\Content\Location\Handler $locationHandler - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\Handler $objectStateHandler - * @param \eZ\Publish\SPI\Persistence\Content\Section\Handler $sectionHandler - * @param \eZ\Publish\SPI\Persistence\TransactionHandler $transactionHandler - * @param \eZ\Publish\SPI\Persistence\Content\Location\Trash\Handler $trashHandler - * @param \eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler $urlAliasHandler - * @param \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler $urlWildcardHandler - * @param \eZ\Publish\SPI\Persistence\User\Handler $userHandler - * @param \eZ\Publish\Core\Persistence\Legacy\URL\Handler $urlHandler - * @param \eZ\Publish\SPI\Persistence\Bookmark\Handler $bookmarkHandler - * @param \eZ\Publish\SPI\Persistence\Notification\Handler $notificationHandler - * @param \eZ\Publish\SPI\Persistence\UserPreference\Handler $userPreferenceHandler - */ - public function __construct( - ContentHandler $contentHandler, - ContentTypeHandler $contentTypeHandler, - LanguageHandler $languageHandler, - LocationHandler $locationHandler, - ObjectStateHandler $objectStateHandler, - SectionHandler $sectionHandler, - SPITransactionHandler $transactionHandler, - TrashHandler $trashHandler, - UrlAliasHandler $urlAliasHandler, - UrlWildcardHandler $urlWildcardHandler, - UserHandler $userHandler, - UrlHandler $urlHandler, - BookmarkHandler $bookmarkHandler, - NotificationHandler $notificationHandler, - UserPreferenceHandler $userPreferenceHandler, - SettingHandler $settingHandler - ) { - $this->contentHandler = $contentHandler; - $this->contentTypeHandler = $contentTypeHandler; - $this->languageHandler = $languageHandler; - $this->locationHandler = $locationHandler; - $this->objectStateHandler = $objectStateHandler; - $this->sectionHandler = $sectionHandler; - $this->transactionHandler = $transactionHandler; - $this->trashHandler = $trashHandler; - $this->urlAliasHandler = $urlAliasHandler; - $this->urlWildcardHandler = $urlWildcardHandler; - $this->userHandler = $userHandler; - $this->urlHandler = $urlHandler; - $this->bookmarkHandler = $bookmarkHandler; - $this->notificationHandler = $notificationHandler; - $this->userPreferenceHandler = $userPreferenceHandler; - $this->settingHandler = $settingHandler; - } - - public function contentHandler() - { - return $this->contentHandler; - } - - public function contentTypeHandler() - { - return $this->contentTypeHandler; - } - - public function contentLanguageHandler() - { - return $this->languageHandler; - } - - public function locationHandler() - { - return $this->locationHandler; - } - - public function objectStateHandler() - { - return $this->objectStateHandler; - } - - public function sectionHandler() - { - return $this->sectionHandler; - } - - public function trashHandler() - { - return $this->trashHandler; - } - - public function urlAliasHandler() - { - return $this->urlAliasHandler; - } - - public function urlWildcardHandler() - { - return $this->urlWildcardHandler; - } - - public function userHandler() - { - return $this->userHandler; - } - - public function urlHandler() - { - return $this->urlHandler; - } - - public function bookmarkHandler() - { - return $this->bookmarkHandler; - } - - public function settingHandler(): SettingHandler - { - return $this->settingHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Notification\Handler - */ - public function notificationHandler(): NotificationHandler - { - return $this->notificationHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\UserPreference\Handler - */ - public function userPreferenceHandler(): UserPreferenceHandler - { - return $this->userPreferenceHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\TransactionHandler - */ - public function transactionHandler() - { - return $this->transactionHandler; - } - - /** - * Begin transaction. - * - * @deprecated Since 5.3 {@use transactionHandler()->beginTransaction()} - */ - public function beginTransaction() - { - $this->transactionHandler->beginTransaction(); - } - - /** - * Commit transaction. - * - * Commit transaction, or throw exceptions if no transactions has been started. - * - * @throws \RuntimeException If no transaction has been started - * - * @deprecated Since 5.3 {@use transactionHandler()->beginTransaction()} - */ - public function commit() - { - $this->transactionHandler->commit(); - } - - /** - * Rollback transaction. - * - * Rollback transaction, or throw exceptions if no transactions has been started. - * - * @throws \RuntimeException If no transaction has been started - * - * @deprecated Since 5.3 {@use transactionHandler()->beginTransaction()} - */ - public function rollback() - { - $this->transactionHandler->rollback(); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Notification/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/Notification/Gateway.php deleted file mode 100644 index c5eb9ef1fc..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Notification/Gateway.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Notification; - -use eZ\Publish\SPI\Persistence\Notification\CreateStruct; -use eZ\Publish\SPI\Persistence\Notification\Notification; - -abstract class Gateway -{ - /** - * Store Notification ValueObject in persistent storage. - * - * @param \eZ\Publish\SPI\Persistence\Notification\CreateStruct $notification - * - * @return int - */ - abstract public function insert(CreateStruct $notification): int; - - /** - * Get Notification by its id. - * - * @param int $notificationId - * - * @return array - */ - abstract public function getNotificationById(int $notificationId): array; - - /** - * Update Notification ValueObject in persistent storage. - * There's no edit feature but it's essential to mark Notification as read. - * - * @param \eZ\Publish\SPI\Persistence\Notification\Notification $notification - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - */ - abstract public function updateNotification(Notification $notification): void; - - /** - * @param int $userId - * - * @return int - */ - abstract public function countUserNotifications(int $userId): int; - - /** - * Count users unread Notifications. - * - * @param int $userId - * - * @return int - */ - abstract public function countUserPendingNotifications(int $userId): int; - - /** - * @param int $userId - * @param int $offset - * @param int $limit - * - * @return array - */ - abstract public function loadUserNotifications(int $userId, int $offset = 0, int $limit = -1): array; - - /** - * @param int $notificationId - */ - abstract public function delete(int $notificationId): void; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php deleted file mode 100644 index d0ce133e84..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,185 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Notification\Gateway; - -use Doctrine\DBAL\Connection; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Persistence\Legacy\Notification\Gateway; -use eZ\Publish\SPI\Persistence\Notification\CreateStruct; -use eZ\Publish\SPI\Persistence\Notification\Notification; -use PDO; - -class DoctrineDatabase extends Gateway -{ - public const TABLE_NOTIFICATION = 'eznotification'; - public const COLUMN_ID = 'id'; - public const COLUMN_OWNER_ID = 'owner_id'; - public const COLUMN_IS_PENDING = 'is_pending'; - public const COLUMN_TYPE = 'type'; - public const COLUMN_CREATED = 'created'; - public const COLUMN_DATA = 'data'; - - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** - * @param \Doctrine\DBAL\Connection $connection - */ - public function __construct(Connection $connection) - { - $this->connection = $connection; - } - - /** - * {@inheritdoc} - */ - public function insert(CreateStruct $createStruct): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::TABLE_NOTIFICATION) - ->values([ - self::COLUMN_IS_PENDING => ':is_pending', - self::COLUMN_OWNER_ID => ':user_id', - self::COLUMN_CREATED => ':created', - self::COLUMN_TYPE => ':type', - self::COLUMN_DATA => ':data', - ]) - ->setParameter(':is_pending', $createStruct->isPending, PDO::PARAM_BOOL) - ->setParameter(':user_id', $createStruct->ownerId, PDO::PARAM_INT) - ->setParameter(':created', $createStruct->created, PDO::PARAM_INT) - ->setParameter(':type', $createStruct->type, PDO::PARAM_STR) - ->setParameter(':data', json_encode($createStruct->data), PDO::PARAM_STR); - - $query->execute(); - - return (int) $this->connection->lastInsertId(); - } - - /** - * {@inheritdoc} - */ - public function getNotificationById(int $notificationId): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select(...$this->getColumns()) - ->from(self::TABLE_NOTIFICATION) - ->where($query->expr()->eq(self::COLUMN_ID, ':id')); - - $query->setParameter(':id', $notificationId, PDO::PARAM_INT); - - return $query->execute()->fetchAll(PDO::FETCH_ASSOC); - } - - /** - * {@inheritdoc} - */ - public function updateNotification(Notification $notification): void - { - if (!isset($notification->id) || !is_numeric($notification->id)) { - throw new InvalidArgumentException(self::COLUMN_ID, 'Cannot update the notification'); - } - - $query = $this->connection->createQueryBuilder(); - - $query - ->update(self::TABLE_NOTIFICATION) - ->set(self::COLUMN_IS_PENDING, ':is_pending') - ->where($query->expr()->eq(self::COLUMN_ID, ':id')) - ->setParameter(':is_pending', $notification->isPending, PDO::PARAM_BOOL) - ->setParameter(':id', $notification->id, PDO::PARAM_INT); - - $query->execute(); - } - - /** - * {@inheritdoc} - */ - public function countUserNotifications(int $userId): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('COUNT(' . self::COLUMN_ID . ')') - ->from(self::TABLE_NOTIFICATION) - ->where($query->expr()->eq(self::COLUMN_OWNER_ID, ':user_id')) - ->setParameter(':user_id', $userId, PDO::PARAM_INT); - - return (int)$query->execute()->fetchColumn(); - } - - /** - * {@inheritdoc} - */ - public function countUserPendingNotifications(int $userId): int - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select('COUNT(' . self::COLUMN_ID . ')') - ->from(self::TABLE_NOTIFICATION) - ->where($expr->eq(self::COLUMN_OWNER_ID, ':user_id')) - ->andWhere($expr->eq(self::COLUMN_IS_PENDING, ':is_pending')) - ->setParameter(':is_pending', true, PDO::PARAM_BOOL) - ->setParameter(':user_id', $userId, PDO::PARAM_INT); - - return (int)$query->execute()->fetchColumn(); - } - - /** - * {@inheritdoc} - */ - public function loadUserNotifications(int $userId, int $offset = 0, int $limit = -1): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select(...$this->getColumns()) - ->from(self::TABLE_NOTIFICATION) - ->where($query->expr()->eq(self::COLUMN_OWNER_ID, ':user_id')) - ->setFirstResult($offset); - - if ($limit > 0) { - $query->setMaxResults($limit); - } - - $query->orderBy(self::COLUMN_ID, 'DESC'); - $query->setParameter(':user_id', $userId, PDO::PARAM_INT); - - return $query->execute()->fetchAll(PDO::FETCH_ASSOC); - } - - /** - * {@inheritdoc} - */ - public function delete(int $notificationId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::TABLE_NOTIFICATION) - ->where($query->expr()->eq(self::COLUMN_ID, ':id')) - ->setParameter(':id', $notificationId, PDO::PARAM_INT); - - $query->execute(); - } - - /** - * @return array - */ - private function getColumns(): array - { - return [ - self::COLUMN_ID, - self::COLUMN_OWNER_ID, - self::COLUMN_IS_PENDING, - self::COLUMN_TYPE, - self::COLUMN_CREATED, - self::COLUMN_DATA, - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php deleted file mode 100644 index dbef0ac508..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Notification\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\Notification\Gateway; -use eZ\Publish\SPI\Persistence\Notification\CreateStruct; -use eZ\Publish\SPI\Persistence\Notification\Notification; -use PDOException; - -class ExceptionConversion extends Gateway -{ - /** - * The wrapped gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Notification\Gateway - */ - protected $innerGateway; - - /** - * ExceptionConversion constructor. - * - * @param \eZ\Publish\Core\Persistence\Legacy\Notification\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function getNotificationById(int $notificationId): array - { - try { - return $this->innerGateway->getNotificationById($notificationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateNotification(Notification $notification): void - { - try { - $this->innerGateway->updateNotification($notification); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countUserNotifications(int $userId): int - { - try { - return $this->innerGateway->countUserNotifications($userId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countUserPendingNotifications(int $userId): int - { - try { - return $this->innerGateway->countUserPendingNotifications($userId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadUserNotifications(int $userId, int $offset = 0, int $limit = -1): array - { - try { - return $this->innerGateway->loadUserNotifications($userId, $offset, $limit); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function insert(CreateStruct $notification): int - { - try { - return $this->innerGateway->insert($notification); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function delete(int $notificationId): void - { - try { - $this->innerGateway->delete($notificationId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Notification/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Notification/Handler.php deleted file mode 100644 index ef9c01efba..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Notification/Handler.php +++ /dev/null @@ -1,117 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Notification; - -use eZ\Publish\API\Repository\Values\Notification\Notification as APINotification; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\SPI\Persistence\Notification\CreateStruct; -use eZ\Publish\SPI\Persistence\Notification\Handler as HandlerInterface; -use eZ\Publish\SPI\Persistence\Notification\Notification; -use eZ\Publish\SPI\Persistence\Notification\UpdateStruct; - -class Handler implements HandlerInterface -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Notification\Gateway */ - protected $gateway; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Notification\Mapper */ - protected $mapper; - - /** - * @param \eZ\Publish\Core\Persistence\Legacy\Notification\Gateway $gateway - * @param \eZ\Publish\Core\Persistence\Legacy\Notification\Mapper $mapper - */ - public function __construct(Gateway $gateway, Mapper $mapper) - { - $this->gateway = $gateway; - $this->mapper = $mapper; - } - - /** - * {@inheritdoc} - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - */ - public function createNotification(CreateStruct $createStruct): Notification - { - $id = $this->gateway->insert($createStruct); - - return $this->getNotificationById($id); - } - - /** - * {@inheritdoc} - */ - public function countPendingNotifications(int $ownerId): int - { - return $this->gateway->countUserPendingNotifications($ownerId); - } - - /** - * {@inheritdoc} - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - */ - public function getNotificationById(int $notificationId): Notification - { - $notification = $this->mapper->extractNotificationsFromRows( - $this->gateway->getNotificationById($notificationId) - ); - - if (count($notification) < 1) { - throw new NotFoundException('Notification', $notificationId); - } - - return reset($notification); - } - - /** - * {@inheritdoc} - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - */ - public function updateNotification(APINotification $apiNotification, UpdateStruct $updateStruct): Notification - { - $notification = $this->mapper->createNotificationFromUpdateStruct( - $updateStruct - ); - $notification->id = $apiNotification->id; - - $this->gateway->updateNotification($notification); - - return $this->getNotificationById($notification->id); - } - - /** - * {@inheritdoc} - */ - public function countNotifications(int $userId): int - { - return $this->gateway->countUserNotifications($userId); - } - - /** - * {@inheritdoc} - */ - public function loadUserNotifications(int $userId, int $offset, int $limit): array - { - return $this->mapper->extractNotificationsFromRows( - $this->gateway->loadUserNotifications($userId, $offset, $limit) - ); - } - - /** - * {@inheritdoc} - */ - public function delete(APINotification $notification): void - { - $this->gateway->delete($notification->id); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Notification/Mapper.php b/eZ/Publish/Core/Persistence/Legacy/Notification/Mapper.php deleted file mode 100644 index 48d506b380..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Notification/Mapper.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Notification; - -use eZ\Publish\SPI\Persistence\Notification\Notification; -use eZ\Publish\SPI\Persistence\Notification\UpdateStruct; -use RuntimeException; - -class Mapper -{ - /** - * Extracts Bookmark objects from $rows. - * - * @param array $rows - * - * @return \eZ\Publish\SPI\Persistence\Notification\Notification[] - */ - public function extractNotificationsFromRows(array $rows): array - { - $notifications = []; - foreach ($rows as $row) { - $notifications[] = $this->extractNotificationFromRow($row); - } - - return $notifications; - } - - /** - * @param \eZ\Publish\SPI\Persistence\Notification\UpdateStruct $updateStruct - * - * @return \eZ\Publish\SPI\Persistence\Notification\Notification - */ - public function createNotificationFromUpdateStruct(UpdateStruct $updateStruct): Notification - { - $notification = new Notification(); - $notification->isPending = $updateStruct->isPending; - - return $notification; - } - - /** - * Extract Bookmark object from $row. - * - * @param array $row - * - * @return \eZ\Publish\SPI\Persistence\Notification\Notification - */ - private function extractNotificationFromRow(array $row): Notification - { - $notification = new Notification(); - $notification->id = (int)$row['id']; - $notification->ownerId = (int)$row['owner_id']; - $notification->type = $row['type']; - $notification->created = (int)$row['created']; - $notification->isPending = (bool) $row['is_pending']; - if ($row['data'] !== null) { - $notification->data = json_decode($row['data'], true); - if (json_last_error() !== JSON_ERROR_NONE) { - throw new RuntimeException('Error while decoding notification data: ' . json_last_error_msg()); - } - } - - return $notification; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Setting/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/Setting/Gateway.php deleted file mode 100644 index d82d4b1f72..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Setting/Gateway.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Setting; - -/** - * @internal For internal use by Persistence Handlers. - */ -abstract class Gateway -{ - public const SETTING_SEQ = 'ibexa_setting_id_seq'; - public const SETTING_TABLE = 'ibexa_setting'; - - /** - * @throws \Doctrine\DBAL\Exception - */ - abstract public function insertSetting( - string $group, - string $identifier, - string $serializedValue - ): int; - - /** - * @throws \Doctrine\DBAL\Exception - */ - abstract public function updateSetting( - string $group, - string $identifier, - string $serializedValue - ): void; - - /** - * @throws \Doctrine\DBAL\Exception - */ - abstract public function loadSetting( - string $group, - string $identifier - ): ?array; - - /** - * @throws \Doctrine\DBAL\Exception - */ - abstract public function loadSettingById( - int $id - ): ?array; - - /** - * @throws \Doctrine\DBAL\Exception - */ - abstract public function deleteSetting( - string $group, - string $identifier - ): void; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Setting/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/Setting/Gateway/DoctrineDatabase.php deleted file mode 100644 index 6b66c24e21..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Setting/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,144 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Setting\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ParameterType; -use eZ\Publish\Core\Persistence\Legacy\Setting\Gateway; - -/** - * @internal Gateway implementation is considered internal. Use Persistence Setting Handler instead. - * - * @see \eZ\Publish\SPI\Persistence\Setting\Handler - */ -final class DoctrineDatabase extends Gateway -{ - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - public function __construct(Connection $connection) - { - $this->connection = $connection; - } - - public function insertSetting(string $group, string $identifier, string $serializedValue): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::SETTING_TABLE) - ->values( - [ - $this->connection->quoteIdentifier('group') => $query->createPositionalParameter($group), - 'identifier' => $query->createPositionalParameter($identifier), - 'value' => $query->createPositionalParameter($serializedValue), - ] - ); - - $query->execute(); - - return (int)$this->connection->lastInsertId(Gateway::SETTING_SEQ); - } - - public function updateSetting(string $group, string $identifier, string $serializedValue): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::SETTING_TABLE) - ->set('value', $query->createPositionalParameter($serializedValue)) - ->where( - $query->expr()->eq( - $this->connection->quoteIdentifier('group'), - $query->createPositionalParameter($group, ParameterType::STRING) - ), - $query->expr()->eq( - 'identifier', - $query->createPositionalParameter($identifier, ParameterType::STRING) - ) - ); - - $query->execute(); - } - - public function loadSetting(string $group, string $identifier): ?array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select([ - $this->connection->quoteIdentifier('group'), - 'identifier', - 'value', - ]) - ->from(self::SETTING_TABLE) - ->where( - $query->expr()->eq( - $this->connection->quoteIdentifier('group'), - $query->createPositionalParameter($group, ParameterType::STRING) - ), - $query->expr()->eq( - 'identifier', - $query->createPositionalParameter($identifier, ParameterType::STRING) - ) - ); - - $statement = $query->execute(); - $result = $statement->fetchAssociative(); - - if (false === $result) { - return null; - } - - return $result; - } - - public function loadSettingById(int $id): ?array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select([ - $this->connection->quoteIdentifier('group'), - 'identifier', - 'value', - ]) - ->from(self::SETTING_TABLE) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - - $statement = $query->execute(); - $result = $statement->fetchAssociative(); - - if (false === $result) { - return null; - } - - return $result; - } - - public function deleteSetting(string $group, string $identifier): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::SETTING_TABLE) - ->where( - $query->expr()->eq( - $this->connection->quoteIdentifier('group'), - $query->createPositionalParameter($group, ParameterType::STRING) - ), - $query->expr()->eq( - 'identifier', - $query->createPositionalParameter($identifier, ParameterType::STRING) - ) - ); - - $query->execute(); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Setting/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/Setting/Gateway/ExceptionConversion.php deleted file mode 100644 index 814f29339b..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Setting/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Setting\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\Setting\Gateway; -use PDOException; - -/** - * @internal Internal exception conversion layer. - */ -final class ExceptionConversion extends Gateway -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Setting\Gateway */ - private $innerGateway; - - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - /** - * @throw \eZ\Publish\Core\Base\Exceptions\DatabaseException - */ - public function insertSetting(string $group, string $identifier, string $serializedValue): int - { - try { - return $this->innerGateway->insertSetting($group, $identifier, $serializedValue); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - /** - * @throw \eZ\Publish\Core\Base\Exceptions\DatabaseException - */ - public function updateSetting(string $group, string $identifier, string $serializedValue): void - { - try { - $this->innerGateway->updateSetting($group, $identifier, $serializedValue); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - /** - * @throw \eZ\Publish\Core\Base\Exceptions\DatabaseException - */ - public function loadSetting(string $group, string $identifier): ?array - { - try { - return $this->innerGateway->loadSetting($group, $identifier); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - /** - * @throw \eZ\Publish\Core\Base\Exceptions\DatabaseException - */ - public function loadSettingById(int $id): ?array - { - try { - return $this->innerGateway->loadSettingById($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - /** - * @throw \eZ\Publish\Core\Base\Exceptions\DatabaseException - */ - public function deleteSetting(string $group, string $identifier): void - { - try { - $this->innerGateway->deleteSetting($group, $identifier); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Setting/Handler.php b/eZ/Publish/Core/Persistence/Legacy/Setting/Handler.php deleted file mode 100644 index c20a0fb1ec..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Setting/Handler.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Setting; - -use eZ\Publish\Core\Base\Exceptions\NotFoundException as NotFound; -use eZ\Publish\SPI\Persistence\Setting\Handler as BaseSettingHandler; -use eZ\Publish\SPI\Persistence\Setting\Setting; - -class Handler implements BaseSettingHandler -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Setting\Gateway */ - protected $settingGateway; - - public function __construct(Gateway $settingGateway) - { - $this->settingGateway = $settingGateway; - } - - public function create(string $group, string $identifier, string $serializedValue): Setting - { - $lastId = $this->settingGateway->insertSetting( - $group, - $identifier, - $serializedValue - ); - - $setting = $this->settingGateway->loadSettingById($lastId); - - return new Setting([ - 'group' => $setting['group'], - 'identifier' => $setting['identifier'], - 'serializedValue' => $setting['value'], - ]); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function update(string $group, string $identifier, string $serializedValue): Setting - { - $this->settingGateway->updateSetting( - $group, - $identifier, - $serializedValue - ); - - $setting = $this->settingGateway->loadSetting($group, $identifier); - - return new Setting([ - 'group' => $setting['group'], - 'identifier' => $setting['identifier'], - 'serializedValue' => $setting['value'], - ]); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function load(string $group, string $identifier): Setting - { - $setting = $this->settingGateway->loadSetting($group, $identifier); - - if (empty($setting)) { - throw new NotFound('Setting', [ - 'group' => $group, - 'identifier' => $identifier, - ]); - } - - return new Setting([ - 'group' => $setting['group'], - 'identifier' => $setting['identifier'], - 'serializedValue' => $setting['value'], - ]); - } - - public function delete(string $group, string $identifier): void - { - $this->settingGateway->deleteSetting($group, $identifier); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/SharedGateway/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/SharedGateway/Gateway.php deleted file mode 100644 index 5a98345398..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/SharedGateway/Gateway.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\SharedGateway; - -/** - * Database platform-dependent shared Gateway. - * - * @internal For internal use by Legacy Storage Gateways. - */ -interface Gateway -{ - /** - * Get the next value for auto-incremented column. - * - * For the most database platforms it should be NULL to indicate that the storage engine - * for the given platform should make a decision. - * For some edge cases however, when the given platform has known issues, the value can be - * pre-determined. - * - * Note that it's the responsibility of a caller to pass a sequence name associated with the - * passed column. - */ - public function getColumnNextIntegerValue( - string $tableName, - string $columnName, - string $sequenceName - ): ?int; - - /** - * Get the most recently inserted id for the given sequence name. - * - * Note that sequence names are ignored by database drivers not supporting sequences, so the - * sequence name can be passed as a constant, regardless of the underlying database connection. - * - * It returns integer as all the IDs in the eZ Platform Legacy Storage are (big)integers - */ - public function getLastInsertedId(string $sequenceName): int; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Bookmark/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Bookmark/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index c8a7df5e07..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Bookmark/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,173 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Bookmark\Gateway; - -use eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Bookmark\Bookmark; -use PDO; - -class DoctrineDatabaseTest extends TestCase -{ - public const EXISTING_BOOKMARK_ID = 1; - public const EXISTING_BOOKMARK_DATA = [ - 'id' => 1, - 'name' => 'Lorem ipsum dolor', - 'node_id' => 5, - 'user_id' => 14, - ]; - - protected function setUp(): void - { - parent::setUp(); - - $this->insertDatabaseFixture(__DIR__ . '/../_fixtures/bookmarks.php'); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase::insertBookmark - */ - public function testInsertBookmark() - { - $id = $this->getGateway()->insertBookmark(new Bookmark([ - 'userId' => 14, - 'locationId' => 54, - 'name' => 'Lorem ipsum dolor...', - ])); - - $data = $this->loadBookmark($id); - - $this->assertEquals([ - 'id' => $id, - 'name' => 'Lorem ipsum dolor...', - 'node_id' => '54', - 'user_id' => '14', - ], $data); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase::deleteBookmark - */ - public function testDeleteBookmark() - { - $this->getGateway()->deleteBookmark(self::EXISTING_BOOKMARK_ID); - - $this->assertEmpty($this->loadBookmark(self::EXISTING_BOOKMARK_ID)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase::loadBookmarkDataById - */ - public function testLoadBookmarkDataById() - { - $this->assertEquals( - [self::EXISTING_BOOKMARK_DATA], - $this->getGateway()->loadBookmarkDataById(self::EXISTING_BOOKMARK_ID) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase::loadBookmarkDataByUserIdAndLocationId - */ - public function testLoadBookmarkDataByUserIdAndLocationId() - { - $data = $this->getGateway()->loadBookmarkDataByUserIdAndLocationId( - (int) self::EXISTING_BOOKMARK_DATA['user_id'], - [(int) self::EXISTING_BOOKMARK_DATA['node_id']] - ); - - $this->assertEquals([self::EXISTING_BOOKMARK_DATA], $data); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase::loadUserBookmarks - * @dataProvider dataProviderForLoadUserBookmarks - */ - public function testLoadUserBookmarks(int $userId, int $offset, int $limit, array $expected) - { - $this->assertEquals($expected, $this->getGateway()->loadUserBookmarks($userId, $offset, $limit)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase::countUserBookmarks - * @dataProvider dataProviderForLoadUserBookmarks - */ - public function testCountUserBookmarks(int $userId, int $offset, int $limit, array $expected) - { - $this->assertEquals(count($expected), $this->getGateway()->countUserBookmarks($userId)); - } - - public function dataProviderForLoadUserBookmarks(): array - { - $fixtures = (require __DIR__ . '/../_fixtures/bookmarks.php')[DoctrineDatabase::TABLE_BOOKMARKS]; - - $expectedRows = static function ($userId) use ($fixtures) { - $rows = array_filter($fixtures, static function (array $row) use ($userId) { - return $row['user_id'] == $userId; - }); - - usort($rows, static function ($a, $b) { - return $b['id'] <=> $a['id']; - }); - - return $rows; - }; - - $userId = self::EXISTING_BOOKMARK_DATA['user_id']; - - return [ - [ - $userId, 0, 10, $expectedRows($userId), - ], - ]; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase::locationSwapped - */ - public function testLocationSwapped() - { - $bookmark1Id = 3; - $bookmark2Id = 4; - - $bookmark1BeforeSwap = $this->loadBookmark($bookmark1Id); - $bookmark2BeforeSwap = $this->loadBookmark($bookmark2Id); - - $this->getGateway()->locationSwapped( - (int) $bookmark1BeforeSwap['node_id'], - (int) $bookmark2BeforeSwap['node_id'] - ); - - $bookmark1AfterSwap = $this->loadBookmark($bookmark1Id); - $bookmark2AfterSwap = $this->loadBookmark($bookmark2Id); - - $this->assertEquals($bookmark1BeforeSwap['node_id'], $bookmark2AfterSwap['node_id']); - $this->assertEquals($bookmark2BeforeSwap['node_id'], $bookmark1AfterSwap['node_id']); - } - - /** - * Return a ready to test DoctrineStorage gateway. - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function getGateway(): Gateway - { - return new DoctrineDatabase($this->getDatabaseConnection()); - } - - private function loadBookmark(int $id): array - { - $data = $this->connection - ->executeQuery('SELECT * FROM ezcontentbrowsebookmark WHERE id = :id', ['id' => $id]) - ->fetch(PDO::FETCH_ASSOC); - - return is_array($data) ? $data : []; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Bookmark/HandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Bookmark/HandlerTest.php deleted file mode 100644 index da6189bc97..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Bookmark/HandlerTest.php +++ /dev/null @@ -1,214 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Bookmark; - -use eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Bookmark\Handler; -use eZ\Publish\Core\Persistence\Legacy\Bookmark\Mapper; -use eZ\Publish\SPI\Persistence\Bookmark\Bookmark; -use eZ\Publish\SPI\Persistence\Bookmark\CreateStruct; -use PHPUnit\Framework\TestCase; - -class HandlerTest extends TestCase -{ - public const BOOKMARK_ID = 7; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway|\PHPUnit\Framework\MockObject\MockObject */ - private $gateway; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Bookmark\Mapper|\PHPUnit\Framework\MockObject\MockObject */ - private $mapper; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Bookmark\Handler */ - private $handler; - - protected function setUp(): void - { - $this->gateway = $this->createMock(Gateway::class); - $this->mapper = $this->createMock(Mapper::class); - $this->handler = new Handler($this->gateway, $this->mapper); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Handler::create - */ - public function testCreate() - { - $createStruct = new CreateStruct([ - 'name' => 'Contact', - 'locationId' => 54, - 'userId' => 87, - ]); - - $bookmark = new Bookmark([ - 'name' => 'Contact', - 'locationId' => 54, - 'userId' => 87, - ]); - - $this->mapper - ->expects($this->once()) - ->method('createBookmarkFromCreateStruct') - ->with($createStruct) - ->willReturn($bookmark); - - $this->gateway - ->expects($this->once()) - ->method('insertBookmark') - ->with($bookmark) - ->willReturn(self::BOOKMARK_ID); - - $this->handler->create($createStruct); - - $this->assertEquals($bookmark->id, self::BOOKMARK_ID); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Handler::delete - */ - public function testDelete() - { - $this->gateway - ->expects($this->once()) - ->method('deleteBookmark') - ->with(self::BOOKMARK_ID); - - $this->handler->delete(self::BOOKMARK_ID); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Handler::loadByUserIdAndLocationId - */ - public function testLoadByUserIdAndLocationIdExistingBookmark() - { - $userId = 87; - $locationId = 54; - - $rows = [ - [ - 'name' => 'Contact', - 'node_id' => $locationId, - 'user_id' => $userId, - ], - ]; - - $object = new Bookmark([ - 'name' => 'Contact', - 'locationId' => $locationId, - 'userId' => $userId, - ]); - - $this->gateway - ->expects($this->once()) - ->method('loadBookmarkDataByUserIdAndLocationId') - ->with($userId, [$locationId]) - ->willReturn($rows); - - $this->mapper - ->expects($this->once()) - ->method('extractBookmarksFromRows') - ->with($rows) - ->willReturn([$object]); - - $this->assertEquals([$locationId => $object], $this->handler->loadByUserIdAndLocationId($userId, [$locationId])); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Handler::loadByUserIdAndLocationId - */ - public function testLoadByUserIdAndLocationIdNonExistingBookmark() - { - $userId = 87; - $locationId = 54; - - $this->gateway - ->expects($this->once()) - ->method('loadBookmarkDataByUserIdAndLocationId') - ->with($userId, [$locationId]) - ->willReturn([]); - - $this->mapper - ->expects($this->once()) - ->method('extractBookmarksFromRows') - ->with([]) - ->willReturn([]); - - $this->assertEmpty($this->handler->loadByUserIdAndLocationId($userId, [$locationId])); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Handler::loadUserBookmarks - */ - public function testLoadUserBookmarks() - { - $userId = 87; - $offset = 50; - $limit = 25; - - $rows = [ - [ - 'id' => '12', - 'name' => 'Home', - 'node_id' => '2', - 'user_id' => $userId, - ], - [ - 'id' => '75', - 'name' => 'Contact', - 'node_id' => '54', - 'user_id' => $userId, - ], - ]; - - $objects = [ - new Bookmark([ - 'id' => 12, - 'name' => 'Home', - 'locationId' => 2, - 'userId' => 78, - ]), - new Bookmark([ - 'id' => 75, - 'name' => 'Contact', - 'locationId' => 54, - 'userId' => 87, - ]), - ]; - - $this->gateway - ->expects($this->once()) - ->method('loadUserBookmarks') - ->with($userId, $offset, $limit) - ->willReturn($rows); - - $this->mapper - ->expects($this->once()) - ->method('extractBookmarksFromRows') - ->with($rows) - ->willReturn($objects); - - $this->assertEquals($objects, $this->handler->loadUserBookmarks($userId, $offset, $limit)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Handler::locationSwapped - */ - public function testLocationSwapped() - { - $location1Id = 1; - $location2Id = 2; - - $this->gateway - ->expects($this->once()) - ->method('locationSwapped') - ->with($location1Id, $location2Id); - - $this->handler->locationSwapped($location1Id, $location2Id); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Bookmark/MapperTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Bookmark/MapperTest.php deleted file mode 100644 index ba8e7bd162..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Bookmark/MapperTest.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Bookmark; - -use eZ\Publish\Core\Persistence\Legacy\Bookmark\Mapper; -use eZ\Publish\SPI\Persistence\Bookmark\Bookmark; -use eZ\Publish\SPI\Persistence\Bookmark\CreateStruct; -use PHPUnit\Framework\TestCase; - -class MapperTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Bookmark\Mapper */ - private $mapper; - - protected function setUp(): void - { - $this->mapper = new Mapper(); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Mapper::createBookmarkFromCreateStruct - */ - public function testCreateBookmarkFromCreateStruct() - { - $createStruct = new CreateStruct([ - 'name' => 'Contact', - 'locationId' => 54, - 'userId' => 87, - ]); - - $this->assertEquals(new Bookmark([ - 'name' => 'Contact', - 'locationId' => 54, - 'userId' => 87, - ]), $this->mapper->createBookmarkFromCreateStruct($createStruct)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Bookmark\Mapper::extractBookmarksFromRows - */ - public function testExtractBookmarksFromRows() - { - $rows = [ - [ - 'id' => '12', - 'name' => 'Home', - 'node_id' => '2', - 'user_id' => '78', - ], - [ - 'id' => '75', - 'name' => 'Contact', - 'node_id' => '54', - 'user_id' => '87', - ], - ]; - - $objects = [ - new Bookmark([ - 'id' => 12, - 'name' => 'Home', - 'locationId' => 2, - 'userId' => 78, - ]), - new Bookmark([ - 'id' => 75, - 'name' => 'Contact', - 'locationId' => 54, - 'userId' => 87, - ]), - ]; - - $this->assertEquals($objects, $this->mapper->extractBookmarksFromRows($rows)); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/ContentHandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/ContentHandlerTest.php deleted file mode 100644 index 133e8904f5..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/ContentHandlerTest.php +++ /dev/null @@ -1,1732 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content; - -use eZ\Publish\API\Repository\Values\Content\Relation as RelationValue; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Handler; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler as LanguageHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway as ContentTypeGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler as ContentTypeHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway as UrlAliasGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct as LocationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Relation; -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\UpdateStruct; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use ReflectionException; - -/** - * Test case for Content Handler. - */ -class ContentHandlerTest extends TestCase -{ - /** - * Content handler to test. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Handler - */ - protected $contentHandler; - - /** - * Gateway mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway - */ - protected $gatewayMock; - - /** - * Location gateway mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway - */ - protected $locationGatewayMock; - - /** - * Type gateway mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway - */ - protected $typeGatewayMock; - - /** - * Mapper mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Mapper - */ - protected $mapperMock; - - /** - * Field handler mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler - */ - protected $fieldHandlerMock; - - /** - * Location handler mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler - */ - protected $treeHandlerMock; - - /** - * Slug converter mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter - */ - protected $slugConverterMock; - - /** - * Location handler mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway - */ - protected $urlAliasGatewayMock; - - /** - * ContentType handler mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler - */ - protected $contentTypeHandlerMock; - - /** - * @var \PHPUnit\Framework\MockObject\MockObject&\eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler - */ - private $languageHandlerMock; - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::create - * - * @todo Current method way to complex to test, refactor! - */ - public function testCreate() - { - $handler = $this->getContentHandler(); - - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - $fieldHandlerMock = $this->getFieldHandlerMock(); - $locationMock = $this->getLocationGatewayMock(); - $contentTypeHandlerMock = $this->getContentTypeHandlerMock(); - $contentTypeMock = $this->createMock(Type::class); - $createStruct = $this->getCreateStructFixture(); - - $contentTypeHandlerMock->expects($this->once()) - ->method('load') - ->with($createStruct->typeId) - ->will($this->returnValue($contentTypeMock)); - - $mapperMock->expects($this->once()) - ->method('createVersionInfoFromCreateStruct') - ->with( - $this->isInstanceOf( - CreateStruct::class - ) - )->will( - $this->returnValue( - new VersionInfo( - [ - 'names' => [], - 'contentInfo' => new ContentInfo(), - ] - ) - ) - ); - - $gatewayMock->expects($this->once()) - ->method('insertContentObject') - ->with( - $this->isInstanceOf(CreateStruct::class) - )->will($this->returnValue(23)); - - $gatewayMock->expects($this->once()) - ->method('insertVersion') - ->with( - $this->isInstanceOf(VersionInfo::class), - $this->isType('array') - )->will($this->returnValue(1)); - - $fieldHandlerMock->expects($this->once()) - ->method('createNewFields') - ->with( - $this->isInstanceOf(Content::class), - $this->isInstanceOf(Type::class) - ); - - $locationMock->expects($this->once()) - ->method('createNodeAssignment') - ->with( - $this->isInstanceOf( - LocationCreateStruct::class - ), - $this->equalTo(42), - $this->equalTo(3) // Location\Gateway::NODE_ASSIGNMENT_OP_CODE_CREATE - ); - - $res = $handler->create($createStruct); - - // @todo Make subsequent tests - - $this->assertInstanceOf( - Content::class, - $res, - 'Content not created' - ); - $this->assertEquals( - 23, - $res->versionInfo->contentInfo->id, - 'Content ID not set correctly' - ); - $this->assertInstanceOf( - VersionInfo::class, - $res->versionInfo, - 'Version infos not created' - ); - $this->assertEquals( - 1, - $res->versionInfo->id, - 'Version ID not set correctly' - ); - $this->assertCount( - 2, - $res->fields, - 'Fields not set correctly in version' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::publish - */ - public function testPublishFirstVersion() - { - $handler = $this->getPartlyMockedHandler(['loadVersionInfo']); - - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(); - $locationMock = $this->getLocationGatewayMock(); - $fieldHandlerMock = $this->getFieldHandlerMock(); - $metadataUpdateStruct = new MetadataUpdateStruct(); - - $handler->expects($this->at(0)) - ->method('loadVersionInfo') - ->with(23, 1) - ->will( - $this->returnValue( - new VersionInfo([ - 'contentInfo' => new ContentInfo([ - 'currentVersionNo' => 1, - 'mainLanguageCode' => 'eng-GB', - ]), - 'names' => [ - 'eng-GB' => '', - ], - ]) - ) - ); - - $contentRows = [['ezcontentobject_version_version' => 1]]; - - $gatewayMock->expects($this->once()) - ->method('load') - ->with( - $this->equalTo(23), - $this->equalTo(1), - $this->equalTo(null) - )->willReturn($contentRows); - - $gatewayMock->expects($this->once()) - ->method('loadVersionedNameData') - ->with( - $this->equalTo([['id' => 23, 'version' => 1]]) - )->will( - $this->returnValue([22]) - ); - - $mapperMock->expects($this->once()) - ->method('extractContentFromRows') - ->with($this->equalTo($contentRows), $this->equalTo([22])) - ->will($this->returnValue([$this->getContentFixtureForDraft()])); - - $fieldHandlerMock->expects($this->once()) - ->method('loadExternalFieldData') - ->with($this->isInstanceOf(Content::class)); - - $gatewayMock - ->expects($this->once()) - ->method('updateContent') - ->with(23, $metadataUpdateStruct); - - $locationMock - ->expects($this->once()) - ->method('createLocationsFromNodeAssignments') - ->with(23, 1); - - $locationMock - ->expects($this->once()) - ->method('updateLocationsContentVersionNo') - ->with(23, 1); - - $gatewayMock - ->expects($this->once()) - ->method('setPublishedStatus') - ->with(23, 1); - - $handler->publish(23, 1, $metadataUpdateStruct); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::publish - */ - public function testPublish() - { - $handler = $this->getPartlyMockedHandler(['loadVersionInfo', 'setStatus']); - - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(); - $locationMock = $this->getLocationGatewayMock(); - $fieldHandlerMock = $this->getFieldHandlerMock(); - $metadataUpdateStruct = new MetadataUpdateStruct(); - - $handler->expects($this->at(0)) - ->method('loadVersionInfo') - ->with(23, 2) - ->will( - $this->returnValue( - new VersionInfo([ - 'contentInfo' => new ContentInfo([ - 'currentVersionNo' => 1, - 'mainLanguageCode' => 'eng-GB', - ]), - 'names' => [ - 'eng-GB' => '', - ], - ]) - ) - ); - - $handler - ->expects($this->at(1)) - ->method('setStatus') - ->with(23, VersionInfo::STATUS_ARCHIVED, 1); - - $contentRows = [['ezcontentobject_version_version' => 2]]; - - $gatewayMock->expects($this->once()) - ->method('load') - ->with( - $this->equalTo(23), - $this->equalTo(2), - $this->equalTo(null) - ) - ->willReturn($contentRows); - - $gatewayMock->expects($this->once()) - ->method('loadVersionedNameData') - ->with( - $this->equalTo([['id' => 23, 'version' => 2]]) - )->will( - $this->returnValue([22]) - ); - - $mapperMock->expects($this->once()) - ->method('extractContentFromRows') - ->with($this->equalTo($contentRows), $this->equalTo([22])) - ->will($this->returnValue([$this->getContentFixtureForDraft()])); - - $fieldHandlerMock->expects($this->once()) - ->method('loadExternalFieldData') - ->with($this->isInstanceOf(Content::class)); - - $gatewayMock - ->expects($this->once()) - ->method('updateContent') - ->with(23, $metadataUpdateStruct, $this->isInstanceOf(VersionInfo::class)); - - $locationMock - ->expects($this->once()) - ->method('createLocationsFromNodeAssignments') - ->with(23, 2); - - $locationMock - ->expects($this->once()) - ->method('updateLocationsContentVersionNo') - ->with(23, 2); - - $gatewayMock - ->expects($this->once()) - ->method('setPublishedStatus') - ->with(23, 2); - - $handler->publish(23, 2, $metadataUpdateStruct); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::createDraftFromVersion - */ - public function testCreateDraftFromVersion() - { - $handler = $this->getPartlyMockedHandler(['load']); - - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - $fieldHandlerMock = $this->getFieldHandlerMock(); - $languageHandlerMock = $this->getLanguageHandlerMock(); - $contentTypeHandlerMock = $this->getContentTypeHandlerMock(); - - $handler->expects($this->once()) - ->method('load') - ->with(23, 2) - ->will($this->returnValue($this->getContentFixtureForDraft())); - - $mapperMock->expects($this->once()) - ->method('createVersionInfoForContent') - ->with( - $this->isInstanceOf(Content::class), - $this->equalTo(3), - $this->equalTo(14) - )->will( - $this->returnValue( - new VersionInfo( - [ - 'names' => [], - 'versionNo' => 3, - 'contentInfo' => new ContentInfo(), - ] - ) - ) - ); - - $languageHandlerMock->method('loadByLanguageCode') - ->willReturn(new Content\Language()); - - $contentTypeHandlerMock->method('load') - ->willReturn(new Type()); - - $gatewayMock->expects($this->once()) - ->method('insertVersion') - ->with( - $this->isInstanceOf(VersionInfo::class), - $this->getContentFixtureForDraft()->fields - )->will($this->returnValue(42)); - - $gatewayMock->expects($this->once()) - ->method('getLastVersionNumber') - ->with($this->equalTo(23)) - ->will($this->returnValue(2)); - - $fieldHandlerMock->expects($this->once()) - ->method('createExistingFieldsInNewVersion') - ->with($this->isInstanceOf(Content::class)); - - $relationData = [ - [ - 'ezcontentobject_link_contentclassattribute_id' => 0, - 'ezcontentobject_link_to_contentobject_id' => 42, - 'ezcontentobject_link_relation_type' => 1, - ], - ]; - - $gatewayMock->expects($this->once()) - ->method('loadRelations') - ->with( - $this->equalTo(23), - $this->equalTo(2) - ) - ->will($this->returnValue($relationData)); - - $relationStruct = new RelationCreateStruct( - [ - 'sourceContentId' => 23, - 'sourceContentVersionNo' => 3, - 'sourceFieldDefinitionId' => 0, - 'destinationContentId' => 42, - 'type' => 1, - ] - ); - - $gatewayMock->expects($this->once()) - ->method('insertRelation') - ->with($this->equalTo($relationStruct)); - - $result = $handler->createDraftFromVersion(23, 2, 14); - - $this->assertInstanceOf( - Content::class, - $result - ); - $this->assertEquals( - 42, - $result->versionInfo->id - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::load - */ - public function testLoad() - { - $handler = $this->getContentHandler(); - - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(); - $fieldHandlerMock = $this->getFieldHandlerMock(); - - $contentRows = [['ezcontentobject_version_version' => 2]]; - - $gatewayMock->expects($this->once()) - ->method('load') - ->with( - $this->equalTo(23), - $this->equalTo(2), - $this->equalTo(['eng-GB']) - )->will( - $this->returnValue($contentRows) - ); - - $gatewayMock->expects($this->once()) - ->method('loadVersionedNameData') - ->with( - $this->equalTo([['id' => 23, 'version' => 2]]) - )->will( - $this->returnValue([22]) - ); - - $mapperMock->expects($this->once()) - ->method('extractContentFromRows') - ->with($this->equalTo($contentRows), $this->equalTo([22])) - ->will($this->returnValue([$this->getContentFixtureForDraft()])); - - $fieldHandlerMock->expects($this->once()) - ->method('loadExternalFieldData') - ->with($this->isInstanceOf(Content::class)); - - $result = $handler->load(23, 2, ['eng-GB']); - - $this->assertEquals( - $result, - $this->getContentFixtureForDraft() - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::loadContentList - */ - public function testLoadContentList() - { - $handler = $this->getContentHandler(); - - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(); - $fieldHandlerMock = $this->getFieldHandlerMock(); - $contentRows = [ - ['ezcontentobject_id' => 2, 'ezcontentobject_version_version' => 2], - ['ezcontentobject_id' => 3, 'ezcontentobject_version_version' => 1], - ]; - $gatewayMock->expects($this->once()) - ->method('loadContentList') - ->with([2, 3], ['eng-GB', 'eng-US']) - ->willReturn($contentRows); - - $nameDataRows = [ - ['ezcontentobject_name_contentobject_id' => 2, 'ezcontentobject_name_content_version' => 2], - ['ezcontentobject_name_contentobject_id' => 3, 'ezcontentobject_name_content_version' => 1], - ]; - - $gatewayMock->expects($this->once()) - ->method('loadVersionedNameData') - ->with($this->equalTo([['id' => 2, 'version' => 2], ['id' => 3, 'version' => 1]])) - ->willReturn($nameDataRows); - - $expected = [ - 2 => $this->getContentFixtureForDraft(2, 2), - 3 => $this->getContentFixtureForDraft(3, 1), - ]; - $mapperMock->expects($this->at(0)) - ->method('extractContentFromRows') - ->with($this->equalTo([$contentRows[0]]), $this->equalTo([$nameDataRows[0]])) - ->willReturn([$expected[2]]); - - $mapperMock->expects($this->at(1)) - ->method('extractContentFromRows') - ->with($this->equalTo([$contentRows[1]]), $this->equalTo([$nameDataRows[1]])) - ->willReturn([$expected[3]]); - - $fieldHandlerMock->expects($this->exactly(2)) - ->method('loadExternalFieldData') - ->with($this->isInstanceOf(Content::class)); - - $result = $handler->loadContentList([2, 3], ['eng-GB', 'eng-US']); - - $this->assertEquals( - $expected, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::loadContentInfoByRemoteId - */ - public function testLoadContentInfoByRemoteId() - { - $contentInfoData = [new ContentInfo()]; - $this->getGatewayMock()->expects($this->once()) - ->method('loadContentInfoByRemoteId') - ->with( - $this->equalTo('15b256dbea2ae72418ff5facc999e8f9') - )->will( - $this->returnValue([42]) - ); - - $this->getMapperMock()->expects($this->once()) - ->method('extractContentInfoFromRow') - ->with($this->equalTo([42])) - ->will($this->returnValue($contentInfoData)); - - $this->assertSame( - $contentInfoData, - $this->getContentHandler()->loadContentInfoByRemoteId('15b256dbea2ae72418ff5facc999e8f9') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::load - */ - public function testLoadErrorNotFound() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\NotFoundException::class); - - $handler = $this->getContentHandler(); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('load') - ->will( - $this->returnValue([]) - ); - - $result = $handler->load(23, 2, ['eng-GB']); - } - - /** - * Returns a Content for {@link testCreateDraftFromVersion()}. - * - * @param int $id Optional id - * @param int $versionNo Optional version number - * - * @return \eZ\Publish\SPI\Persistence\Content - */ - protected function getContentFixtureForDraft(int $id = 23, int $versionNo = 2) - { - $content = new Content(); - $content->versionInfo = new VersionInfo(); - $content->versionInfo->versionNo = $versionNo; - - $content->versionInfo->contentInfo = new ContentInfo(['id' => $id]); - - $field = new Field(); - $field->versionNo = $versionNo; - - $content->fields = [$field]; - - return $content; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::updateContent - */ - public function testUpdateContent() - { - $handler = $this->getPartlyMockedHandler(['load', 'loadContentInfo']); - - $gatewayMock = $this->getGatewayMock(); - $fieldHandlerMock = $this->getFieldHandlerMock(); - $contentTypeHandlerMock = $this->getContentTypeHandlerMock(); - $contentTypeMock = $this->createMock(Type::class); - $contentStub = new Content( - [ - 'versionInfo' => new VersionInfo( - [ - 'contentInfo' => new ContentInfo( - [ - 'contentTypeId' => 4242, - ] - ), - ] - ), - ] - ); - - $contentTypeHandlerMock->expects($this->once()) - ->method('load') - ->with($contentStub->versionInfo->contentInfo->contentTypeId) - ->will($this->returnValue($contentTypeMock)); - - $gatewayMock->expects($this->once()) - ->method('updateContent') - ->with(14, $this->isInstanceOf(MetadataUpdateStruct::class)); - $gatewayMock->expects($this->once()) - ->method('updateVersion') - ->with(14, 4, $this->isInstanceOf(UpdateStruct::class)); - - $fieldHandlerMock->expects($this->once()) - ->method('updateFields') - ->with( - $this->isInstanceOf(Content::class), - $this->isInstanceOf(UpdateStruct::class), - $this->isInstanceOf(Type::class) - ); - - $handler->expects($this->at(0)) - ->method('load') - ->with(14, 4) - ->will($this->returnValue($contentStub)); - - $handler->expects($this->at(1)) - ->method('load') - ->with(14, 4); - - $handler->expects($this->at(2)) - ->method('loadContentInfo') - ->with(14); - - $resultContent = $handler->updateContent( - 14, // ContentId - 4, // VersionNo - new UpdateStruct( - [ - 'creatorId' => 14, - 'modificationDate' => time(), - 'initialLanguageId' => 2, - 'fields' => [ - new Field( - [ - 'id' => 23, - 'fieldDefinitionId' => 42, - 'type' => 'some-type', - 'value' => new FieldValue(), - ] - ), - new Field( - [ - 'id' => 23, - 'fieldDefinitionId' => 43, - 'type' => 'some-type', - 'value' => new FieldValue(), - ] - ), - ], - ] - ) - ); - - $resultContentInfo = $handler->updateMetadata( - 14, // ContentId - new MetadataUpdateStruct( - [ - 'ownerId' => 14, - 'name' => 'Some name', - 'modificationDate' => time(), - 'alwaysAvailable' => true, - ] - ) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::updateMetadata - */ - public function testUpdateMetadata() - { - $handler = $this->getPartlyMockedHandler(['load', 'loadContentInfo']); - - $gatewayMock = $this->getGatewayMock(); - $fieldHandlerMock = $this->getFieldHandlerMock(); - $updateStruct = new MetadataUpdateStruct( - [ - 'ownerId' => 14, - 'name' => 'Some name', - 'modificationDate' => time(), - 'alwaysAvailable' => true, - ] - ); - - $gatewayMock->expects($this->once()) - ->method('updateContent') - ->with(14, $updateStruct); - - $handler->expects($this->once()) - ->method('loadContentInfo') - ->with(14) - ->will( - $this->returnValue( - $this->createMock(ContentInfo::class) - ) - ); - - $resultContentInfo = $handler->updateMetadata( - 14, // ContentId - $updateStruct - ); - self::assertInstanceOf(ContentInfo::class, $resultContentInfo); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::updateMetadata - */ - public function testUpdateMetadataUpdatesPathIdentificationString() - { - $handler = $this->getPartlyMockedHandler(['load', 'loadContentInfo']); - $locationGatewayMock = $this->getLocationGatewayMock(); - $slugConverterMock = $this->getSlugConverterMock(); - $urlAliasGatewayMock = $this->getUrlAliasGatewayMock(); - $gatewayMock = $this->getGatewayMock(); - $updateStruct = new MetadataUpdateStruct(['mainLanguageId' => 2]); - - $gatewayMock->expects($this->once()) - ->method('updateContent') - ->with(14, $updateStruct); - - $locationGatewayMock->expects($this->once()) - ->method('loadLocationDataByContent') - ->with(14) - ->will( - $this->returnValue( - [ - [ - 'node_id' => 100, - 'parent_node_id' => 200, - ], - ] - ) - ); - - $urlAliasGatewayMock->expects($this->once()) - ->method('loadLocationEntries') - ->with(100, false, 2) - ->will( - $this->returnValue( - [ - [ - 'text' => 'slug', - ], - ] - ) - ); - - $slugConverterMock->expects($this->once()) - ->method('convert') - ->with('slug', 'node_100', 'urlalias_compat') - ->will($this->returnValue('transformed_slug')); - - $locationGatewayMock->expects($this->once()) - ->method('updatePathIdentificationString') - ->with(100, 200, 'transformed_slug'); - - $handler->expects($this->once()) - ->method('loadContentInfo') - ->with(14) - ->will( - $this->returnValue( - $this->createMock(ContentInfo::class) - ) - ); - - $handler->updateMetadata( - 14, // ContentId - $updateStruct - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::loadRelations - */ - public function testLoadRelations() - { - $handler = $this->getContentHandler(); - - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(); - - $gatewayMock->expects($this->once()) - ->method('loadRelations') - ->with( - $this->equalTo(23), - $this->equalTo(null), - $this->equalTo(null) - )->will( - $this->returnValue([42]) - ); - - $mapperMock->expects($this->once()) - ->method('extractRelationsFromRows') - ->with($this->equalTo([42])) - ->will($this->returnValue($this->getRelationFixture())); - - $result = $handler->loadRelations(23); - - $this->assertEquals( - $result, - $this->getRelationFixture() - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::loadReverseRelations - */ - public function testLoadReverseRelations() - { - $handler = $this->getContentHandler(); - - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(); - - $gatewayMock->expects($this->once()) - ->method('loadReverseRelations') - ->with( - $this->equalTo(23), - $this->equalTo(null) - )->will( - $this->returnValue([42]) - ); - - $mapperMock->expects($this->once()) - ->method('extractRelationsFromRows') - ->with($this->equalTo([42])) - ->will($this->returnValue($this->getRelationFixture())); - - $result = $handler->loadReverseRelations(23); - - $this->assertEquals( - $result, - $this->getRelationFixture() - ); - } - - public function testAddRelation() - { - // expected relation object after creation - $expectedRelationObject = new Relation(); - $expectedRelationObject->id = 42; // mocked value, not a real one - $expectedRelationObject->sourceContentId = 23; - $expectedRelationObject->sourceContentVersionNo = 1; - $expectedRelationObject->destinationContentId = 66; - $expectedRelationObject->type = RelationValue::COMMON; - - // relation create struct - $relationCreateStruct = new Relation\CreateStruct(); - $relationCreateStruct->destinationContentId = 66; - $relationCreateStruct->sourceContentId = 23; - $relationCreateStruct->sourceContentVersionNo = 1; - $relationCreateStruct->type = RelationValue::COMMON; - - $handler = $this->getContentHandler(); - - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(); - - $mapperMock->expects($this->once()) - ->method('createRelationFromCreateStruct') - // @todo Connected with the todo above - ->with($this->equalTo($relationCreateStruct)) - ->will($this->returnValue($expectedRelationObject)); - - $gatewayMock->expects($this->once()) - ->method('insertRelation') - ->with($this->equalTo($relationCreateStruct)) - ->will( - // @todo Should this return a row as if it was selected from the database, the id... ? Check with other, similar create methods - $this->returnValue(42) - ); - - $result = $handler->addRelation($relationCreateStruct); - - $this->assertEquals( - $result, - $expectedRelationObject - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::removeRelation - */ - public function testRemoveRelation() - { - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('deleteRelation') - ->with($this->equalTo(1, RelationValue::COMMON)); - - $this->getContentHandler()->removeRelation(1, RelationValue::COMMON); - } - - protected function getRelationFixture() - { - $relation = new Relation(); - $relation->sourceContentId = 23; - $relation->sourceContentVersionNo = 1; - $relation->destinationContentId = 69; - - return $relation; - } - - /** - * Returns a CreateStruct fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\CreateStruct - */ - public function getCreateStructFixture() - { - $struct = new CreateStruct(); - - $struct->typeId = 4242; - - $firstField = new Field(); - $firstField->type = 'some-type'; - $firstField->value = new FieldValue(); - - $secondField = clone $firstField; - - $struct->fields = [ - $firstField, $secondField, - ]; - - $struct->locations = [ - new LocationCreateStruct( - ['parentId' => 42] - ), - ]; - - $struct->name = [ - 'eng-GB' => 'This is a test name', - ]; - - return $struct; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::loadDraftsForUser - */ - public function testLoadDraftsForUser() - { - $handler = $this->getContentHandler(); - $rows = [['ezcontentobject_version_contentobject_id' => 42, 'ezcontentobject_version_version' => 2]]; - - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(); - - $gatewayMock->expects($this->once()) - ->method('listVersionsForUser') - ->with($this->equalTo(23)) - ->will($this->returnValue($rows)); - - $gatewayMock->expects($this->once()) - ->method('loadVersionedNameData') - ->with($this->equalTo([['id' => 42, 'version' => 2]])) - ->will($this->returnValue([])); - - $mapperMock->expects($this->once()) - ->method('extractVersionInfoListFromRows') - ->with($this->equalTo($rows), $this->equalTo([])) - ->will($this->returnValue([new VersionInfo()])); - - $res = $handler->loadDraftsForUser(23); - - $this->assertEquals( - [new VersionInfo()], - $res - ); - } - - public function testListVersions() - { - $handler = $this->getContentHandler(); - - $treeHandlerMock = $this->getTreeHandlerMock(); - - $treeHandlerMock - ->expects($this->once()) - ->method('listVersions') - ->with(23) - ->will($this->returnValue([new VersionInfo()])); - - $versions = $handler->listVersions(23); - - $this->assertEquals( - [new VersionInfo()], - $versions - ); - } - - public function testRemoveRawContent() - { - $handler = $this->getContentHandler(); - $treeHandlerMock = $this->getTreeHandlerMock(); - - $treeHandlerMock - ->expects($this->once()) - ->method('removeRawContent') - ->with(23); - - $handler->removeRawContent(23); - } - - /** - * Test for the deleteContent() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::deleteContent - */ - public function testDeleteContentWithLocations() - { - $handlerMock = $this->getPartlyMockedHandler(['getAllLocationIds']); - $gatewayMock = $this->getGatewayMock(); - $treeHandlerMock = $this->getTreeHandlerMock(); - - $gatewayMock->expects($this->once()) - ->method('getAllLocationIds') - ->with($this->equalTo(23)) - ->will($this->returnValue([42, 24])); - $treeHandlerMock->expects($this->exactly(2)) - ->method('removeSubtree') - ->with( - $this->logicalOr( - $this->equalTo(42), - $this->equalTo(24) - ) - ); - - $handlerMock->deleteContent(23); - } - - /** - * Test for the deleteContent() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::deleteContent - */ - public function testDeleteContentWithoutLocations() - { - $handlerMock = $this->getPartlyMockedHandler(['removeRawContent']); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('getAllLocationIds') - ->with($this->equalTo(23)) - ->will($this->returnValue([])); - $handlerMock->expects($this->once()) - ->method('removeRawContent') - ->with($this->equalTo(23)); - - $handlerMock->deleteContent(23); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::deleteVersion - */ - public function testDeleteVersion() - { - $handler = $this->getContentHandler(); - - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(); - $locationHandlerMock = $this->getLocationGatewayMock(); - $fieldHandlerMock = $this->getFieldHandlerMock(); - - $rows = [['ezcontentobject_version_version' => 2]]; - - // Load VersionInfo to delete fields - $gatewayMock->expects($this->once()) - ->method('loadVersionInfo') - ->with($this->equalTo(225), $this->equalTo(2)) - ->willReturn($rows); - - $gatewayMock->expects($this->once()) - ->method('loadVersionedNameData') - ->with($this->equalTo([['id' => 225, 'version' => 2]])) - ->will($this->returnValue([22])); - - $mapperMock->expects($this->once()) - ->method('extractVersionInfoListFromRows') - ->with($this->equalTo($rows), $this->equalTo([22])) - ->will($this->returnValue([new VersionInfo()])); - - $locationHandlerMock->expects($this->once()) - ->method('deleteNodeAssignment') - ->with( - $this->equalTo(225), - $this->equalTo(2) - ); - - $fieldHandlerMock->expects($this->once()) - ->method('deleteFields') - ->with( - $this->equalTo(225), - $this->isInstanceOf(VersionInfo::class) - ); - $gatewayMock->expects($this->once()) - ->method('deleteRelations') - ->with( - $this->equalTo(225), - $this->equalTo(2) - ); - $gatewayMock->expects($this->once()) - ->method('deleteVersions') - ->with( - $this->equalTo(225), - $this->equalTo(2) - ); - $gatewayMock->expects($this->once()) - ->method('deleteNames') - ->with( - $this->equalTo(225), - $this->equalTo(2) - ); - - $handler->deleteVersion(225, 2); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::copy - */ - public function testCopySingleVersion() - { - $handler = $this->getPartlyMockedHandler(['load', 'internalCreate']); - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(); - - $handler->expects( - $this->once() - )->method( - 'load' - )->with( - $this->equalTo(23), - $this->equalTo(32) - )->will( - $this->returnValue(new Content()) - ); - - $mapperMock->expects( - $this->once() - )->method( - 'createCreateStructFromContent' - )->with( - $this->isInstanceOf(Content::class) - )->will( - $this->returnValue(new CreateStruct()) - ); - - $handler->expects( - $this->once() - )->method( - 'internalCreate' - )->with( - $this->isInstanceOf(CreateStruct::class), - $this->equalTo(32) - )->will( - $this->returnValue( - new Content( - [ - 'versionInfo' => new VersionInfo(['contentInfo' => new ContentInfo(['id' => 24])]), - ] - ) - ) - ); - - $gatewayMock->expects($this->once()) - ->method('copyRelations') - ->with( - $this->equalTo(23), - $this->equalTo(24), - $this->equalTo(32) - ) - ->will($this->returnValue(null)); - - $result = $handler->copy(23, 32); - - $this->assertInstanceOf( - Content::class, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::copy - */ - public function testCopyAllVersions() - { - $handler = $this->getPartlyMockedHandler( - [ - 'loadContentInfo', - 'load', - 'internalCreate', - 'listVersions', - ] - ); - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(); - $fieldHandlerMock = $this->getFieldHandlerMock(); - $contentTypeHandlerMock = $this->getContentTypeHandlerMock(); - $contentTypeMock = $this->createMock(Type::class); - $time = time(); - $createStructStub = new CreateStruct( - [ - 'modified' => $time, - 'typeId' => 4242, - ] - ); - - $contentTypeHandlerMock->expects($this->once()) - ->method('load') - ->with($createStructStub->typeId) - ->will($this->returnValue($contentTypeMock)); - - $handler->expects($this->once()) - ->method('loadContentInfo') - ->with($this->equalTo(23)) - ->will($this->returnValue(new ContentInfo(['currentVersionNo' => 2]))); - - $handler->expects($this->at(1)) - ->method('load') - ->with($this->equalTo(23), $this->equalTo(2)) - ->will($this->returnValue(new Content())); - - $mapperMock->expects($this->once()) - ->method('createCreateStructFromContent') - ->with($this->isInstanceOf(Content::class)) - ->will( - $this->returnValue($createStructStub) - ); - - $handler->expects($this->once()) - ->method('internalCreate') - ->with( - $this->isInstanceOf(CreateStruct::class), - $this->equalTo(2) - )->will( - $this->returnValue( - new Content( - [ - 'versionInfo' => new VersionInfo( - [ - 'contentInfo' => new ContentInfo(['id' => 24]), - ] - ), - ] - ) - ) - ); - - $handler->expects($this->once()) - ->method('listVersions') - ->with($this->equalTo(23)) - ->will( - $this->returnValue( - [ - new VersionInfo(['versionNo' => 1]), - new VersionInfo(['versionNo' => 2]), - ] - ) - ); - - $versionInfo = new VersionInfo( - [ - 'names' => ['eng-US' => 'Test'], - 'contentInfo' => new ContentInfo( - [ - 'id' => 24, - 'alwaysAvailable' => true, - ] - ), - ] - ); - $handler->expects($this->at(4)) - ->method('load') - ->with($this->equalTo(23), $this->equalTo(1)) - ->will( - $this->returnValue( - new Content( - [ - 'versionInfo' => $versionInfo, - 'fields' => [], - ] - ) - ) - ); - - $versionInfo->creationDate = $time; - $versionInfo->modificationDate = $time; - $gatewayMock->expects($this->once()) - ->method('insertVersion') - ->with( - $this->equalTo($versionInfo), - $this->isType('array') - )->will($this->returnValue(42)); - - $versionInfo = clone $versionInfo; - $versionInfo->id = 42; - $fieldHandlerMock->expects($this->once()) - ->method('createNewFields') - ->with( - $this->equalTo( - new Content( - [ - 'versionInfo' => $versionInfo, - 'fields' => [], - ] - ) - ), - $this->isInstanceOf(Type::class) - ); - - $gatewayMock->expects($this->once()) - ->method('setName') - ->with( - $this->equalTo(24), - $this->equalTo(1), - $this->equalTo('Test'), - $this->equalTo('eng-US') - ); - - $gatewayMock->expects($this->once()) - ->method('copyRelations') - ->with( - $this->equalTo(23), - $this->equalTo(24), - $this->equalTo(null) - ) - ->will($this->returnValue(null)); - - $result = $handler->copy(23); - - $this->assertInstanceOf( - Content::class, - $result - ); - } - - public function testCopyThrowsNotFoundExceptionContentNotFound() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\NotFoundException::class); - - $handler = $this->getContentHandler(); - - $treeHandlerMock = $this->getTreeHandlerMock(); - $treeHandlerMock - ->expects($this->once()) - ->method('loadContentInfo') - ->with($this->equalTo(23)) - ->will( - $this->throwException(new NotFoundException('ContentInfo', 23)) - ); - - $handler->copy(23); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::copy - */ - public function testCopyThrowsNotFoundExceptionVersionNotFound() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\NotFoundException::class); - - $handler = $this->getContentHandler(); - - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('load') - ->with($this->equalTo(23, 32)) - ->will($this->returnValue([])); - - $result = $handler->copy(23, 32); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::setStatus - */ - public function testSetStatus() - { - $handler = $this->getContentHandler(); - - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('setStatus') - ->with(23, 5, 2) - ->will($this->returnValue(true)); - - $this->assertTrue( - $handler->setStatus(23, 2, 5) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::loadVersionInfoList - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function testLoadVersionInfoList(): void - { - $handler = $this->getContentHandler(); - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(); - - $contentIds = [2, 3]; - $versionInfo1 = new VersionInfo([ - 'contentInfo' => new ContentInfo(['id' => 2]), - ]); - $versionInfo2 = new VersionInfo([ - 'contentInfo' => new ContentInfo(['id' => 3]), - ]); - - $versionRows = [ - ['ezcontentobject_id' => 2, 'ezcontentobject_version_version' => 2], - ['ezcontentobject_id' => 3, 'ezcontentobject_version_version' => 1], - ]; - - $gatewayMock->expects(self::once()) - ->method('loadVersionInfoList') - ->with($contentIds) - ->willReturn($versionRows); - - $nameDataRows = [ - ['ezcontentobject_name_contentobject_id' => 2, 'ezcontentobject_name_content_version' => 2], - ['ezcontentobject_name_contentobject_id' => 3, 'ezcontentobject_name_content_version' => 1], - ]; - - $gatewayMock->expects(self::once()) - ->method('loadVersionedNameData') - ->with([['id' => 2, 'version' => 2], ['id' => 3, 'version' => 1]]) - ->willReturn($nameDataRows); - - $mapperMock->expects(self::once()) - ->method('extractVersionInfoListFromRows') - ->with($versionRows) - ->willReturn([ - $versionInfo1, - $versionInfo2, - ]); - - $expected = [ - 2 => $versionInfo1, - 3 => $versionInfo2, - ]; - - $result = $handler->loadVersionInfoList($contentIds); - - self::assertEquals($expected, $result); - } - - /** - * Returns the handler to test. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Handler - */ - protected function getContentHandler() - { - if (!isset($this->contentHandler)) { - $this->contentHandler = new Handler( - $this->getGatewayMock(), - $this->getLocationGatewayMock(), - $this->getMapperMock(), - $this->getFieldHandlerMock(), - $this->getSlugConverterMock(), - $this->getUrlAliasGatewayMock(), - $this->getContentTypeHandlerMock(), - $this->getTreeHandlerMock(), - $this->getLanguageHandlerMock(), - ); - } - - return $this->contentHandler; - } - - /** - * Returns the handler to test with $methods mocked. - * - * @param string[] $methods - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Handler - */ - protected function getPartlyMockedHandler(array $methods) - { - return $this->getMockBuilder(Handler::class) - ->setMethods($methods) - ->setConstructorArgs( - [ - $this->getGatewayMock(), - $this->getLocationGatewayMock(), - $this->getMapperMock(), - $this->getFieldHandlerMock(), - $this->getSlugConverterMock(), - $this->getUrlAliasGatewayMock(), - $this->getContentTypeHandlerMock(), - $this->getTreeHandlerMock(), - $this->getLanguageHandlerMock(), - ] - ) - ->getMock(); - } - - /** - * Returns a TreeHandler mock. - * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler - */ - protected function getTreeHandlerMock() - { - if (!isset($this->treeHandlerMock)) { - $this->treeHandlerMock = $this->createMock(TreeHandler::class); - } - - return $this->treeHandlerMock; - } - - /** - * Returns a ContentTypeHandler mock. - * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler - */ - protected function getContentTypeHandlerMock() - { - if (!isset($this->contentTypeHandlerMock)) { - $this->contentTypeHandlerMock = $this->createMock(ContentTypeHandler::class); - } - - return $this->contentTypeHandlerMock; - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject&\eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler - */ - protected function getLanguageHandlerMock(): LanguageHandler - { - if (!isset($this->languageHandlerMock)) { - $this->languageHandlerMock = $this->createMock(LanguageHandler::class); - } - - return $this->languageHandlerMock; - } - - /** - * Returns a FieldHandler mock. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler - */ - protected function getFieldHandlerMock() - { - if (!isset($this->fieldHandlerMock)) { - $this->fieldHandlerMock = $this->createMock(FieldHandler::class); - } - - return $this->fieldHandlerMock; - } - - /** - * Returns a Mapper mock. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Mapper - */ - protected function getMapperMock() - { - if (!isset($this->mapperMock)) { - $this->mapperMock = $this->createMock(Mapper::class); - } - - return $this->mapperMock; - } - - /** - * Returns a Location Gateway mock. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway - */ - protected function getLocationGatewayMock() - { - if (!isset($this->locationGatewayMock)) { - $this->locationGatewayMock = $this->createMock(LocationGateway::class); - } - - return $this->locationGatewayMock; - } - - /** - * Returns a Content Type gateway mock. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway - */ - protected function getTypeGatewayMock() - { - if (!isset($this->typeGatewayMock)) { - $this->typeGatewayMock = $this->createMock(ContentTypeGateway::class); - } - - return $this->typeGatewayMock; - } - - /** - * Returns a mock object for the Content Gateway. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Gateway|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getGatewayMock() - { - if (!isset($this->gatewayMock)) { - try { - $this->gatewayMock = $this->getMockForAbstractClass(ContentGateway::class); - } catch (ReflectionException $e) { - self::fail($e); - } - } - - return $this->gatewayMock; - } - - /** - * Returns a mock object for the UrlAlias Handler. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter - */ - protected function getSlugConverterMock() - { - if (!isset($this->slugConverterMock)) { - $this->slugConverterMock = $this->createMock(SlugConverter::class); - } - - return $this->slugConverterMock; - } - - /** - * Returns a mock object for the UrlAlias Gateway. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway - */ - protected function getUrlAliasGatewayMock() - { - if (!isset($this->urlAliasGatewayMock)) { - $this->urlAliasGatewayMock = $this->getMockForAbstractClass(UrlAliasGateway::class); - } - - return $this->urlAliasGatewayMock; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/AuthorTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/AuthorTest.php deleted file mode 100644 index 0a173be887..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/AuthorTest.php +++ /dev/null @@ -1,174 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use DOMDocument; -use eZ\Publish\Core\FieldType\Author\Type as AuthorType; -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as SPIFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test case for Author converter in Legacy storage. - * - * @group fieldType - * @group ezauthor - */ -class AuthorTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter */ - protected $converter; - - /** @var \eZ\Publish\Core\FieldType\Author\Author[] */ - private $authors; - - protected function setUp(): void - { - parent::setUp(); - $this->converter = new AuthorConverter(); - $this->authors = [ - ['id' => 21, 'name' => 'Boba Fett', 'email' => 'boba.fett@bountyhunters.com'], - ['id' => 42, 'name' => 'Darth Vader', 'email' => 'darth.vader@evilempire.biz'], - ['id' => 63, 'name' => 'Luke Skywalker', 'email' => 'luke@imtheone.net'], - ]; - } - - protected function tearDown(): void - { - unset($this->authors); - parent::tearDown(); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter::toStorageValue - */ - public function testToStorageValue() - { - $value = new FieldValue(); - $value->data = $this->authors; - $storageFieldValue = new StorageFieldValue(); - - $this->converter->toStorageValue($value, $storageFieldValue); - $doc = new DOMDocument('1.0', 'utf-8'); - self::assertTrue($doc->loadXML($storageFieldValue->dataText)); - - $authorsXml = $doc->getElementsByTagName('author'); - self::assertSame(count($this->authors), $authorsXml->length); - - // Loop against XML nodes and compare them to the real Author objects. - // Then remove Author from $this->authors - // This way, we can check if all authors have been converted in XML - foreach ($authorsXml as $authorXml) { - foreach ($this->authors as $i => $author) { - if ($authorXml->getAttribute('id') == $author['id']) { - self::assertSame($author['name'], $authorXml->getAttribute('name')); - self::assertSame($author['email'], $authorXml->getAttribute('email')); - unset($this->authors[$i]); - break; - } - } - } - - self::assertEmpty($this->authors, 'All authors have not been converted as expected'); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter::toFieldValue - */ - public function testToFieldValue() - { - $storageFieldValue = new StorageFieldValue(); - $storageFieldValue->dataText = <<<EOT -<?xml version="1.0" encoding="utf-8"?> -<ezauthor> - <authors> - <author id="1" name="Boba Fett" email="boba.fett@bountyhunters.com"/> - <author id="2" name="Darth Vader" email="darth.vader@evilempire.biz"/> - <author id="3" name="Luke Skywalker" email="luke@imtheone.net"/> - </authors> -</ezauthor> -EOT; - $doc = new DOMDocument('1.0', 'utf-8'); - self::assertTrue($doc->loadXML($storageFieldValue->dataText)); - $authorsXml = $doc->getElementsByTagName('author'); - $fieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $fieldValue); - self::assertIsArray($fieldValue->data); - - $authorsXml = $doc->getElementsByTagName('author'); - self::assertSame($authorsXml->length, count($fieldValue->data)); - - $aAuthors = $fieldValue->data; - foreach ($fieldValue->data as $i => $author) { - foreach ($authorsXml as $authorXml) { - if ($authorXml->getAttribute('id') == $author['id']) { - self::assertSame($authorXml->getAttribute('name'), $author['name']); - self::assertSame($authorXml->getAttribute('email'), $author['email']); - unset($aAuthors[$i]); - break; - } - } - } - self::assertEmpty($aAuthors, 'All authors have not been converted as expected from storage'); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionDefaultCurrentUser() - { - $storageFieldDef = new StorageFieldDefinition(); - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'defaultAuthor' => AuthorType::DEFAULT_CURRENT_USER, - ] - ); - $fieldDef = new SPIFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - self::assertSame( - AuthorType::DEFAULT_CURRENT_USER, - $storageFieldDef->dataInt1 - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionDefaultEmpty() - { - $storageFieldDef = new StorageFieldDefinition(); - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'defaultAuthor' => AuthorType::DEFAULT_VALUE_EMPTY, - ] - ); - $fieldDef = new SPIFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - self::assertSame( - AuthorType::DEFAULT_VALUE_EMPTY, - $storageFieldDef->dataInt1 - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/CheckboxTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/CheckboxTest.php deleted file mode 100644 index 28164150f0..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/CheckboxTest.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test case for Checkbox converter in Legacy storage. - */ -class CheckboxTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter */ - protected $converter; - - protected function setUp(): void - { - parent::setUp(); - $this->converter = new CheckboxConverter(); - } - - /** - * @group fieldType - * @group ezboolean - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter::toStorageValue - */ - public function testToStorageValue() - { - $value = new FieldValue(); - $value->data = true; - $value->sortKey = 1; - $storageFieldValue = new StorageFieldValue(); - - $this->converter->toStorageValue($value, $storageFieldValue); - self::assertSame((int)$value->data, $storageFieldValue->dataInt); - self::assertSame($value->sortKey, $storageFieldValue->sortKeyInt); - self::assertSame('', $storageFieldValue->sortKeyString); - } - - /** - * @group fieldType - * @group ezboolean - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter::toFieldValue - */ - public function testToFieldValue() - { - $storageFieldValue = new StorageFieldValue(); - $storageFieldValue->dataInt = 1; - $storageFieldValue->sortKeyInt = 1; - $storageFieldValue->sortKeyString = ''; - $fieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $fieldValue); - self::assertSame((bool)$storageFieldValue->dataInt, $fieldValue->data); - self::assertSame($storageFieldValue->sortKeyInt, $fieldValue->sortKey); - } - - /** - * @group fieldType - * @group ezboolean - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinition() - { - $defaultBool = false; - $storageFieldDef = new StorageFieldDefinition(); - $defaultValue = new FieldValue(); - $defaultValue->data = $defaultBool; - $fieldDef = new PersistenceFieldDefinition( - [ - 'defaultValue' => $defaultValue, - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - self::assertSame( - (int)$fieldDef->defaultValue->data, - $storageFieldDef->dataInt3 - ); - } - - /** - * @group fieldType - * @group ezboolean - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter::toFieldDefinition - */ - public function testToFieldDefinition() - { - $defaultBool = true; - $fieldDef = new PersistenceFieldDefinition(); - $storageDef = new StorageFieldDefinition( - [ - 'dataInt3' => 1, - ] - ); - - $this->converter->toFieldDefinition($storageDef, $fieldDef); - self::assertSame($defaultBool, $fieldDef->defaultValue->data); - self::assertNull($fieldDef->fieldTypeConstraints->fieldSettings); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/CountryTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/CountryTest.php deleted file mode 100644 index ccbf810850..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/CountryTest.php +++ /dev/null @@ -1,205 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test case for Country converter in Legacy storage. - */ -class CountryTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter */ - protected $converter; - - protected function setUp(): void - { - parent::setUp(); - $this->converter = new CountryConverter(); - } - - public function providerForTestToStorageValue() - { - return [ - [['BE', 'FR'], 'belgium,france', 'BE,FR', 'belgium,france'], - [null, '', '', ''], - ]; - } - - /** - * @group fieldType - * @group country - * @dataProvider providerForTestToStorageValue - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter::toStorageValue - */ - public function testToStorageValue($data, $sortKey, $dataText, $sortKeyString) - { - $value = new FieldValue(); - $value->data = $data; - $value->sortKey = $sortKey; - $storageFieldValue = new StorageFieldValue(); - - $this->converter->toStorageValue($value, $storageFieldValue); - self::assertSame($dataText, $storageFieldValue->dataText); - self::assertSame($sortKeyString, $storageFieldValue->sortKeyString); - } - - public function providerForTestToFieldValue() - { - return [ - ['BE,FR', 'belgium,france', ['BE', 'FR']], - ['', '', null], - ]; - } - - /** - * @group fieldType - * @group country - * @dataProvider providerForTestToFieldValue - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter::toFieldValue - */ - public function testToFieldValue($dataText, $sortKeyString, $data) - { - $storageFieldValue = new StorageFieldValue(); - $storageFieldValue->dataText = $dataText; - $storageFieldValue->sortKeyString = $sortKeyString; - $fieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $fieldValue); - self::assertSame($data, $fieldValue->data); - } - - /** - * @group fieldType - * @group country - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionMultiple() - { - $defaultValue = new FieldValue(); - $defaultValue->data = ['BE', 'FR']; - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'isMultiple' => true, - ] - ); - - $storageFieldDef = new StorageFieldDefinition(); - $this->converter->toStorageFieldDefinition( - new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - 'defaultValue' => $defaultValue, - ] - ), - $storageFieldDef - ); - self::assertSame( - 1, - $storageFieldDef->dataInt1 - ); - self::assertSame( - 'BE,FR', - $storageFieldDef->dataText5 - ); - } - - /** - * @group fieldType - * @group country - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionSingle() - { - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'isMultiple' => false, - ] - ); - - $storageFieldDef = new StorageFieldDefinition(); - $this->converter->toStorageFieldDefinition( - new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - ] - ), - $storageFieldDef - ); - self::assertSame( - 0, - $storageFieldDef->dataInt1 - ); - self::assertEmpty( - $storageFieldDef->dataText5 - ); - } - - /** - * @group fieldType - * @group country - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter::toFieldDefinition - */ - public function testToFieldDefinitionMultiple() - { - $fieldDef = new PersistenceFieldDefinition(); - - $this->converter->toFieldDefinition( - new StorageFieldDefinition( - [ - 'dataInt1' => 1, - 'dataText5' => 'BE,FR', - ] - ), - $fieldDef - ); - self::assertInstanceOf('eZ\\Publish\\Core\\FieldType\\FieldSettings', $fieldDef->fieldTypeConstraints->fieldSettings); - self::assertTrue( - $fieldDef->fieldTypeConstraints->fieldSettings['isMultiple'] - ); - self::assertEquals( - ['BE', 'FR'], - $fieldDef->defaultValue->data - ); - } - - /** - * @group fieldType - * @group country - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter::toFieldDefinition - */ - public function testToFieldDefinitionSingle() - { - $fieldDef = new PersistenceFieldDefinition(); - - $this->converter->toFieldDefinition( - new StorageFieldDefinition( - [ - 'dataInt1' => 0, - 'dataText5' => '', - ] - ), - $fieldDef - ); - self::assertInstanceOf('eZ\\Publish\\Core\\FieldType\\FieldSettings', $fieldDef->fieldTypeConstraints->fieldSettings); - self::assertFalse( - $fieldDef->fieldTypeConstraints->fieldSettings['isMultiple'] - ); - self::assertNull( - $fieldDef->defaultValue->data - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/DateAndTimeTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/DateAndTimeTest.php deleted file mode 100644 index 4cdf2e7f08..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/DateAndTimeTest.php +++ /dev/null @@ -1,394 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use DateInterval; -use DateTime; -use DOMDocument; -use eZ\Publish\Core\FieldType\DateAndTime\Type as DateAndTimeType; -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; -use ReflectionObject; -use SimpleXMLElement; - -/** - * Test case for DateAndTime converter in Legacy storage. - */ -class DateAndTimeTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter */ - protected $converter; - - /** @var \DateTime */ - protected $date; - - protected function setUp(): void - { - parent::setUp(); - $this->converter = new DateAndTimeConverter(); - $this->date = new DateTime('@1048633200'); - } - - /** - * @group fieldType - * @group dateTime - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter::toStorageValue - */ - public function testToStorageValue() - { - $value = new FieldValue(); - $value->data = [ - 'timestamp' => $this->date->getTimestamp(), - 'rfc850' => $this->date->format(\DateTime::RFC850), - ]; - $value->sortKey = $this->date->getTimestamp(); - $storageFieldValue = new StorageFieldValue(); - - $this->converter->toStorageValue($value, $storageFieldValue); - self::assertSame($value->data['timestamp'], $storageFieldValue->dataInt); - self::assertSame($value->sortKey, $storageFieldValue->sortKeyInt); - self::assertSame('', $storageFieldValue->sortKeyString); - } - - /** - * @group fieldType - * @group dateTime - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter::toFieldValue - */ - public function testToFieldValue() - { - $storageFieldValue = new StorageFieldValue(); - $storageFieldValue->dataInt = $this->date->getTimestamp(); - $storageFieldValue->sortKeyString = ''; - $storageFieldValue->sortKeyInt = $this->date->getTimestamp(); - $fieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $fieldValue); - self::assertSame( - [ - 'rfc850' => null, - 'timestamp' => 1048633200, - ], - $fieldValue->data - ); - self::assertSame($storageFieldValue->dataInt, $fieldValue->data['timestamp']); - self::assertSame($storageFieldValue->sortKeyInt, $fieldValue->sortKey); - } - - /** - * @group fieldType - * @group dateTime - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionWithAdjustment() - { - $storageFieldDef = new StorageFieldDefinition(); - $dateInterval = DateInterval::createFromDateString('+10 years, -1 month, +3 days, -13 hours'); - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'useSeconds' => true, - 'defaultType' => DateAndTimeType::DEFAULT_CURRENT_DATE_ADJUSTED, - 'dateInterval' => $dateInterval, - ] - ); - $fieldDef = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - self::assertSame( - DateAndTimeType::DEFAULT_CURRENT_DATE_ADJUSTED, - $storageFieldDef->dataInt1 - ); - self::assertSame( - 1, - $storageFieldDef->dataInt2 - ); - - $xml = new SimpleXMLElement($storageFieldDef->dataText5); - foreach ($this->getXMLToDateIntervalMap() as $xmlNode => $property) { - self::assertSame( - $dateInterval->format("%$property"), - (string)$xml->{$xmlNode}['value'] - ); - } - } - - /** - * @group fieldType - * @group dateTime - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionNoDefault() - { - $storageFieldDef = new StorageFieldDefinition(); - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'useSeconds' => true, - 'defaultType' => DateAndTimeType::DEFAULT_EMPTY, - 'dateInterval' => null, - ] - ); - $fieldDef = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - self::assertSame( - DateAndTimeType::DEFAULT_EMPTY, - $storageFieldDef->dataInt1 - ); - self::assertSame( - 1, - $storageFieldDef->dataInt2 - ); - self::assertNull($storageFieldDef->dataText5); - } - - /** - * @group fieldType - * @group dateTime - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionCurrentDate() - { - $storageFieldDef = new StorageFieldDefinition(); - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'useSeconds' => true, - 'defaultType' => DateAndTimeType::DEFAULT_CURRENT_DATE, - 'dateInterval' => null, - ] - ); - $fieldDef = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - self::assertSame( - DateAndTimeType::DEFAULT_CURRENT_DATE, - $storageFieldDef->dataInt1 - ); - self::assertSame( - 1, - $storageFieldDef->dataInt2 - ); - self::assertNull($storageFieldDef->dataText5); - } - - /** - * Returns map from internal XML nodes to DateInterval properties for date adjustment. - * - * @return array Key is the XML node name, value is the DateInterval property - */ - private function getXMLToDateIntervalMap() - { - return [ - 'year' => 'y', - 'month' => 'm', - 'day' => 'd', - 'hour' => 'h', - 'minute' => 'i', - 'second' => 's', - ]; - } - - /** - * @group fieldType - * @group dateTime - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter::toFieldDefinition - */ - public function testToFieldDefinitionNoDefault() - { - $fieldDef = new PersistenceFieldDefinition(); - $storageDef = new StorageFieldDefinition( - [ - 'dataInt1' => DateAndTimeType::DEFAULT_EMPTY, - 'dataInt2' => 1, - ] - ); - - $this->converter->toFieldDefinition($storageDef, $fieldDef); - self::assertNull($fieldDef->defaultValue->data); - } - - /** - * @group fieldType - * @group dateTime - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter::toFieldDefinition - */ - public function testToFieldDefinitionCurrentDate() - { - $time = time(); - $fieldDef = new PersistenceFieldDefinition(); - $storageDef = new StorageFieldDefinition( - [ - 'dataInt1' => DateAndTimeType::DEFAULT_CURRENT_DATE, - 'dataInt2' => 1, - ] - ); - - $this->converter->toFieldDefinition($storageDef, $fieldDef); - sleep(1); - $dateTimeFromString = new DateTime($fieldDef->defaultValue->data['timestring']); - - self::assertIsArray($fieldDef->defaultValue->data); - self::assertCount(3, $fieldDef->defaultValue->data); - self::assertNull($fieldDef->defaultValue->data['rfc850']); - self::assertGreaterThanOrEqual($time, $fieldDef->defaultValue->data['timestamp']); - self::assertEqualsWithDelta($time + 1, $dateTimeFromString->getTimestamp(), 1, 'Time does not match within 1s delta'); - } - - /** - * @group fieldType - * @group dateTime - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter::toFieldDefinition - */ - public function testToFieldDefinitionWithAdjustmentAndSeconds() - { - $fieldDef = new PersistenceFieldDefinition(); - $dateInterval = DateInterval::createFromDateString('2 years, 1 month, -4 days, 2 hours, 0 minute, 34 seconds'); - $date = new DateTime(); - $date->add($dateInterval); - $timestamp = $date->getTimestamp(); - - $storageDef = new StorageFieldDefinition( - [ - 'dataInt1' => DateAndTimeType::DEFAULT_CURRENT_DATE_ADJUSTED, - 'dataInt2' => 1, - 'dataText5' => $this->getXMLStringFromDateInterval($dateInterval), - ] - ); - - $this->converter->toFieldDefinition($storageDef, $fieldDef); - $dateTimeFromString = new DateTime($fieldDef->defaultValue->data['timestring']); - - self::assertIsArray($fieldDef->defaultValue->data); - self::assertCount(3, $fieldDef->defaultValue->data); - self::assertNull($fieldDef->defaultValue->data['rfc850']); - self::assertGreaterThanOrEqual($timestamp, $fieldDef->defaultValue->data['timestamp']); - self::assertGreaterThanOrEqual($timestamp, $dateTimeFromString->getTimestamp()); - // Giving a margin of 1 second for test execution - self::assertLessThanOrEqual($timestamp + 1, $fieldDef->defaultValue->data['timestamp']); - self::assertLessThanOrEqual($timestamp + 1, $dateTimeFromString->getTimestamp()); - } - - /** - * @group fieldType - * @group dateTime - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter::toFieldDefinition - */ - public function testToFieldDefinitionWithAdjustmentNoSeconds() - { - $fieldDef = new PersistenceFieldDefinition(); - $seconds = 34; - $dateInterval = DateInterval::createFromDateString("2 years, 1 month, -4 days, 2 hours, 0 minute, $seconds seconds"); - $date = new DateTime(); - $date->add($dateInterval); - // Removing $seconds as they're not supposed to be taken into account - $timestamp = $date->getTimestamp() - $seconds; - - $storageDef = new StorageFieldDefinition( - [ - 'dataInt1' => DateAndTimeType::DEFAULT_CURRENT_DATE_ADJUSTED, - 'dataInt2' => 0, - 'dataText5' => $this->getXMLStringFromDateInterval($dateInterval), - ] - ); - - $this->converter->toFieldDefinition($storageDef, $fieldDef); - $dateTimeFromString = new DateTime($fieldDef->defaultValue->data['timestring']); - - self::assertIsArray($fieldDef->defaultValue->data); - self::assertCount(3, $fieldDef->defaultValue->data); - self::assertNull($fieldDef->defaultValue->data['rfc850']); - self::assertGreaterThanOrEqual($timestamp, $fieldDef->defaultValue->data['timestamp']); - self::assertGreaterThanOrEqual($timestamp, $dateTimeFromString->getTimestamp()); - // Giving a margin of 1 second for test execution - self::assertLessThanOrEqual($timestamp + 1, $fieldDef->defaultValue->data['timestamp']); - self::assertLessThanOrEqual($timestamp + 1, $dateTimeFromString->getTimestamp()); - } - - /** - * Generates XML string from $dateInterval. - * - * @param \DateInterval $dateInterval - * - * @return string - */ - private function getXMLStringFromDateInterval(DateInterval $dateInterval) - { - $xmlString = <<<EOT -<?xml version="1.0" encoding="utf-8"?> -<adjustment> - <year value="$dateInterval->y"/> - <month value="$dateInterval->m"/> - <day value="$dateInterval->d"/> - <hour value="$dateInterval->h"/> - <minute value="$dateInterval->i"/> - <second value="$dateInterval->s"/> -</adjustment> -EOT; - - return $xmlString; - } - - /** - * @group fieldType - * @group dateTime - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter::getDateIntervalFromXML - */ - public function testGetDateIntervalFromXML() - { - $dateIntervalReference = DateInterval::createFromDateString('2 years, 1 month, -4 days, 2 hours, 0 minute, 34 seconds'); - - $refConverter = new ReflectionObject($this->converter); - $refMethod = $refConverter->getMethod('getDateIntervalFromXML'); - $refMethod->setAccessible(true); - $generatedDateInterval = $refMethod->invoke( - $this->converter, - $this->getXMLStringFromDateInterval($dateIntervalReference) - ); - self::assertEquals($dateIntervalReference, $generatedDateInterval); - } - - /** - * @group fieldType - * @group dateTime - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter::generateDateIntervalXML - */ - public function testGenerateDateIntervalXML() - { - $dateIntervalReference = DateInterval::createFromDateString('2 years, 1 month, -4 days, 2 hours, 0 minute, 34 seconds'); - $dom = new DOMDocument(); - $dom->preserveWhiteSpace = false; - $dom->loadXML($this->getXMLStringFromDateInterval($dateIntervalReference)); - - $refConverter = new ReflectionObject($this->converter); - $refMethod = $refConverter->getMethod('generateDateIntervalXML'); - $refMethod->setAccessible(true); - self::assertEquals( - $dom->saveXML(), - $refMethod->invoke($this->converter, $dateIntervalReference) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/DateTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/DateTest.php deleted file mode 100644 index 7ed419df67..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/DateTest.php +++ /dev/null @@ -1,169 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use DateTime; -use eZ\Publish\Core\FieldType\Date\Type as DateType; -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test case for Date converter in Legacy storage. - * - * @group fieldType - * @group date - */ -class DateTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter */ - protected $converter; - - /** @var \DateTime */ - protected $date; - - protected function setUp(): void - { - parent::setUp(); - $this->converter = new DateConverter(); - $this->date = new DateTime('@1362614400'); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter::toStorageValue - */ - public function testToStorageValue() - { - $value = new FieldValue(); - $value->data = [ - 'timestamp' => $this->date->getTimestamp(), - 'rfc850' => $this->date->format(\DateTime::RFC850), - ]; - $value->sortKey = $this->date->getTimestamp(); - $storageFieldValue = new StorageFieldValue(); - - $this->converter->toStorageValue($value, $storageFieldValue); - self::assertSame($value->data['timestamp'], $storageFieldValue->dataInt); - self::assertSame($value->sortKey, $storageFieldValue->sortKeyInt); - self::assertSame('', $storageFieldValue->sortKeyString); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter::toFieldValue - */ - public function testToFieldValue() - { - $storageFieldValue = new StorageFieldValue(); - $storageFieldValue->dataInt = $this->date->getTimestamp(); - $storageFieldValue->sortKeyString = ''; - $storageFieldValue->sortKeyInt = $this->date->getTimestamp(); - $fieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $fieldValue); - self::assertSame( - [ - 'timestamp' => $this->date->getTimestamp(), - 'rfc850' => null, - ], - $fieldValue->data - ); - self::assertSame($storageFieldValue->dataInt, $fieldValue->data['timestamp']); - self::assertSame($storageFieldValue->sortKeyInt, $fieldValue->sortKey); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionDefaultEmpty() - { - $storageFieldDef = new StorageFieldDefinition(); - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'defaultType' => DateType::DEFAULT_EMPTY, - ] - ); - $fieldDef = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - self::assertSame( - DateType::DEFAULT_EMPTY, - $storageFieldDef->dataInt1 - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionDefaultCurrentDate() - { - $storageFieldDef = new StorageFieldDefinition(); - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'defaultType' => DateType::DEFAULT_CURRENT_DATE, - ] - ); - $fieldDef = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - self::assertSame( - DateType::DEFAULT_CURRENT_DATE, - $storageFieldDef->dataInt1 - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter::toFieldDefinition - */ - public function testToFieldDefinitionDefaultEmpty() - { - $fieldDef = new PersistenceFieldDefinition(); - $storageDef = new StorageFieldDefinition( - [ - 'dataInt1' => DateType::DEFAULT_EMPTY, - ] - ); - - $this->converter->toFieldDefinition($storageDef, $fieldDef); - self::assertNull($fieldDef->defaultValue->data); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter::toFieldDefinition - */ - public function testToFieldDefinitionDefaultCurrentDate() - { - $timestamp = time(); - $fieldDef = new PersistenceFieldDefinition(); - $storageDef = new StorageFieldDefinition( - [ - 'dataInt1' => DateType::DEFAULT_CURRENT_DATE, - ] - ); - - $this->converter->toFieldDefinition($storageDef, $fieldDef); - self::assertIsArray($fieldDef->defaultValue->data); - self::assertCount(3, $fieldDef->defaultValue->data); - self::assertNull($fieldDef->defaultValue->data['rfc850']); - self::assertSame($timestamp, $fieldDef->defaultValue->data['timestamp']); - self::assertSame('now', $fieldDef->defaultValue->data['timestring']); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/ISBNTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/ISBNTest.php deleted file mode 100644 index 786ac28a83..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/ISBNTest.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ISBNConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test for ISBNConverter in Legacy Storage. - */ -class ISBNTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ISBNConverter */ - protected $converter; - - protected function setUp(): void - { - $this->converter = new ISBNConverter(); - } - - /** - * @dataProvider providerForTestToFieldDefinition - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ISBNConverter::toFieldDefinition - */ - public function testToFieldDefinition($dataInt, $excpectedIsbn13Value) - { - $fieldDef = new PersistenceFieldDefinition(); - $storageDefinition = new StorageFieldDefinition([ - 'dataInt1' => $dataInt, - ]); - - $this->converter->toFieldDefinition($storageDefinition, $fieldDef); - - /** @var \eZ\Publish\Core\FieldType\FieldSettings $fieldSettings */ - $fieldSettings = $fieldDef->fieldTypeConstraints->fieldSettings; - self::assertSame($excpectedIsbn13Value, $fieldSettings['isISBN13']); - } - - public function providerForTestToFieldDefinition() - { - return [ - [1, true], - [0, false], - [null, false], - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/KeywordTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/KeywordTest.php deleted file mode 100644 index 35288cf1ed..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/KeywordTest.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test case for Keyword converter in Legacy storage. - */ -class KeywordTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter */ - protected $converter; - - protected function setUp(): void - { - parent::setUp(); - $this->converter = new KeywordConverter(); - } - - /** - * @group fieldType - * @group keyword - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter::toStorageValue - */ - public function testToStorageValue() - { - $value = new FieldValue(); - $value->data = ['key1', 'key2']; - $value->sortKey = false; - $storageFieldValue = new StorageFieldValue(); - - $this->converter->toStorageValue($value, $storageFieldValue); - $this->assertNull($storageFieldValue->dataText); - $this->assertNull($storageFieldValue->dataInt); - $this->assertNull($storageFieldValue->dataFloat); - $this->assertEquals(0, $storageFieldValue->sortKeyInt); - $this->assertEquals('', $storageFieldValue->sortKeyString); - } - - /** - * @group fieldType - * @group keyword - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter::toFieldValue - */ - public function testToFieldValue() - { - $storageFieldValue = new StorageFieldValue(); - $fieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $fieldValue); - $this->assertSame([], $fieldValue->data); - $this->assertEquals('', $fieldValue->sortKey); - } - - /** - * @group fieldType - * @group keyword - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinition() - { - $this->converter->toStorageFieldDefinition(new PersistenceFieldDefinition(), new StorageFieldDefinition()); - } - - /** - * @group fieldType - * @group keyword - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter::toFieldDefinition - */ - public function testToFieldDefinition() - { - $this->converter->toFieldDefinition(new StorageFieldDefinition(), new PersistenceFieldDefinition()); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/MediaTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/MediaTest.php deleted file mode 100644 index d06c34d109..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/MediaTest.php +++ /dev/null @@ -1,96 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\FieldType\Media\Type as MediaType; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\MediaConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test case for MediaType converter in Legacy storage. - */ -class MediaTest extends TestCase -{ - protected $converter; - - protected function setUp(): void - { - $this->converter = new MediaConverter(); - } - - /** - * @group fieldType - * @group ezmedia - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\MediaConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinition() - { - $storageFieldDef = new StorageFieldDefinition(); - - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->validators = [ - // Setting max file size to 1MB (1.048.576 bytes) - 'FileSizeValidator' => ['maxFileSize' => 1048576], - ]; - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'mediaType' => MediaType::TYPE_HTML5_VIDEO, - ] - ); - - $fieldDef = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - 'defaultValue' => null, - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - - self::assertSame( - $fieldDef->fieldTypeConstraints->validators['FileSizeValidator'], - ['maxFileSize' => $storageFieldDef->dataInt1] - ); - self::assertSame( - $fieldDef->fieldTypeConstraints->fieldSettings['mediaType'], - $storageFieldDef->dataText1 - ); - } - - /** - * @group fieldType - * @group ezmedia - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\MediaConverter::toFieldDefinition - */ - public function testToFieldDefinition() - { - $fieldDef = new PersistenceFieldDefinition(); - $storageDef = new StorageFieldDefinition( - [ - 'dataInt1' => 1048576, - 'dataText1' => MediaType::TYPE_HTML5_VIDEO, - ] - ); - - $this->converter->toFieldDefinition($storageDef, $fieldDef); - self::assertSame( - [ - 'FileSizeValidator' => ['maxFileSize' => $storageDef->dataInt1], - ], - $fieldDef->fieldTypeConstraints->validators - ); - self::assertInstanceOf('eZ\\Publish\\Core\\FieldType\\FieldSettings', $fieldDef->fieldTypeConstraints->fieldSettings); - self::assertSame( - ['mediaType' => MediaType::TYPE_HTML5_VIDEO], - $fieldDef->fieldTypeConstraints->fieldSettings->getArrayCopy() - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/RelationListTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/RelationListTest.php deleted file mode 100644 index 3d9e2d8b3b..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/RelationListTest.php +++ /dev/null @@ -1,289 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use eZ\Publish\Core\FieldType\RelationList\Type; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test case for RelationList converter in Legacy storage. - */ -class RelationListTest extends TestCase -{ - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationListConverter */ - protected $converter; - - protected function setUp(): void - { - parent::setUp(); - $this->converter = $this - ->getMockBuilder('eZ\\Publish\\Core\\Persistence\\Legacy\\Content\\FieldValue\\Converter\\RelationListConverter') - ->disableOriginalConstructor() - ->setMethods(['getRelationXmlHashFromDB']) - ->getMock(); - } - - /** - * @group fieldType - * @group relationlist - */ - public function testToStorageValue() - { - $destinationContentIds = [3, 2, 1]; - $fieldValue = new FieldValue(); - $fieldValue->sortKey = false; - $fieldValue->data = ['destinationContentIds' => $destinationContentIds]; - - $expectedStorageFieldValue = new StorageFieldValue(); - $expectedStorageFieldValue->dataText = <<<EOT -<?xml version="1.0" encoding="utf-8"?> -<related-objects><relation-list><relation-item priority="1" contentobject-id="3" contentobject-version="33" node-id="35" parent-node-id="36" contentclass-id="34" contentclass-identifier="37" contentobject-remote-id="32"/><relation-item priority="2" contentobject-id="2" contentobject-version="23" node-id="25" parent-node-id="26" contentclass-id="24" contentclass-identifier="27" contentobject-remote-id="22"/><relation-item priority="3" contentobject-id="1" contentobject-version="13" node-id="15" parent-node-id="16" contentclass-id="14" contentclass-identifier="17" contentobject-remote-id="12"/></relation-list></related-objects> - -EOT; - - $actualStorageFieldValue = new StorageFieldValue(); - - $this->converter - ->expects($this->once()) - ->method('getRelationXmlHashFromDB') - ->with($destinationContentIds) - ->will( - $this->returnValue( - [ - '1' => [ - [ - 'ezcontentobject_remote_id' => '12', - 'ezcontentobject_current_version' => '13', - 'ezcontentobject_contentclass_id' => '14', - 'ezcontentobject_tree_node_id' => '15', - 'ezcontentobject_tree_parent_node_id' => '16', - 'ezcontentclass_identifier' => '17', - ], - ], - '3' => [ - [ - 'ezcontentobject_remote_id' => '32', - 'ezcontentobject_current_version' => '33', - 'ezcontentobject_contentclass_id' => '34', - 'ezcontentobject_tree_node_id' => '35', - 'ezcontentobject_tree_parent_node_id' => '36', - 'ezcontentclass_identifier' => '37', - ], - ], - '2' => [ - [ - 'ezcontentobject_remote_id' => '22', - 'ezcontentobject_current_version' => '23', - 'ezcontentobject_contentclass_id' => '24', - 'ezcontentobject_tree_node_id' => '25', - 'ezcontentobject_tree_parent_node_id' => '26', - 'ezcontentclass_identifier' => '27', - ], - ], - ] - ) - ); - - $this->converter->toStorageValue($fieldValue, $actualStorageFieldValue); - - $this->assertEquals( - $expectedStorageFieldValue, - $actualStorageFieldValue - ); - } - - /** - * @group fieldType - * @group relationlist - */ - public function testToStorageValueEmpty() - { - $destinationContentIds = []; - $fieldValue = new FieldValue(); - $fieldValue->sortKey = false; - $fieldValue->data = ['destinationContentIds' => $destinationContentIds]; - - $expectedStorageFieldValue = new StorageFieldValue(); - $expectedStorageFieldValue->dataText = <<<EOT -<?xml version="1.0" encoding="utf-8"?> -<related-objects><relation-list/></related-objects> - -EOT; - - $actualStorageFieldValue = new StorageFieldValue(); - - $this->converter - ->expects($this->once()) - ->method('getRelationXmlHashFromDB') - ->with($destinationContentIds) - ->will($this->returnValue([])); - - $this->converter->toStorageValue($fieldValue, $actualStorageFieldValue); - - $this->assertEquals( - $expectedStorageFieldValue, - $actualStorageFieldValue - ); - } - - /** - * @group fieldType - * @group relationlist - */ - public function testToFieldValue() - { - $storageFieldValue = new StorageFieldValue(); - $storageFieldValue->sortKeyString = ''; - $storageFieldValue->dataText = <<<EOT -<?xml version="1.0" encoding="utf-8"?> -<related-objects><relation-list><relation-item priority="2" contentobject-id="2" contentobject-version="23" node-id="25" parent-node-id="26" contentclass-id="24" contentclass-identifier="27" contentobject-remote-id="22"/><relation-item priority="3" contentobject-id="1" contentobject-version="13" node-id="15" parent-node-id="16" contentclass-id="14" contentclass-identifier="17" contentobject-remote-id="12"/><relation-item priority="1" contentobject-id="3" contentobject-version="33" node-id="35" parent-node-id="36" contentclass-id="34" contentclass-identifier="37" contentobject-remote-id="32"/></relation-list></related-objects> - -EOT; - - $expectedFieldValue = new FieldValue(); - $expectedFieldValue->data = ['destinationContentIds' => [3, 2, 1]]; - - $actualFieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $actualFieldValue); - - $this->assertEquals( - $expectedFieldValue, - $actualFieldValue - ); - } - - /** - * @group fieldType - * @group relationlist - */ - public function testToFieldValueEmpty() - { - $storageFieldValue = new StorageFieldValue(); - $storageFieldValue->sortKeyString = ''; - $storageFieldValue->dataText = <<<EOT -<?xml version="1.0" encoding="utf-8"?> -<related-objects><relation-list/></related-objects> - -EOT; - - $expectedFieldValue = new FieldValue(); - $expectedFieldValue->data = ['destinationContentIds' => []]; - - $actualFieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $actualFieldValue); - - $this->assertEquals( - $expectedFieldValue, - $actualFieldValue - ); - } - - /** - * @group fieldType - * @group relationlist - */ - public function testToStorageFieldDefinition() - { - $fieldDefinition = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => new FieldTypeConstraints( - [ - 'fieldSettings' => [ - 'selectionMethod' => Type::SELECTION_BROWSE, - 'selectionDefaultLocation' => 12345, - 'selectionContentTypes' => ['article', 'blog_post'], - ], - 'validators' => [ - 'RelationListValueValidator' => [ - 'selectionLimit' => 5, - ], - ], - ] - ), - ] - ); - - $expectedStorageFieldDefinition = new StorageFieldDefinition(); - $expectedStorageFieldDefinition->dataText5 = <<<EOT -<?xml version="1.0" encoding="utf-8"?> -<related-objects><constraints><allowed-class contentclass-identifier="article"/><allowed-class contentclass-identifier="blog_post"/></constraints><type value="2"/><object_class value=""/><selection_type value="0"/><contentobject-placement node-id="12345"/><selection_limit value="5"/></related-objects> - -EOT; - - $actualStorageFieldDefinition = new StorageFieldDefinition(); - - $this->converter->toStorageFieldDefinition($fieldDefinition, $actualStorageFieldDefinition); - - $this->assertEquals( - $expectedStorageFieldDefinition, - $actualStorageFieldDefinition - ); - } - - /** - * @group fieldType - * @group relationlist - */ - public function testToFieldDefinitionMultiple() - { - $storageFieldDefinition = new StorageFieldDefinition(); - $storageFieldDefinition->dataText5 = <<<EOT -<?xml version="1.0" encoding="utf-8"?> -<related-objects> - <constraints> - <allowed-class contentclass-identifier="forum"/> - <allowed-class contentclass-identifier="folder"/> - </constraints><type value="2"/> - <object_class value=""/> - <selection_type value="1"/> - <selection_limit value="1"/> - <contentobject-placement node-id="54321"/> -</related-objects> - -EOT; - - $expectedFieldDefinition = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => new FieldTypeConstraints( - [ - 'fieldSettings' => [ - 'selectionMethod' => Type::SELECTION_DROPDOWN, - 'selectionDefaultLocation' => 54321, - 'selectionContentTypes' => ['forum', 'folder'], - ], - 'validators' => [ - 'RelationListValueValidator' => [ - 'selectionLimit' => 1, - ], - ], - ] - ), - 'defaultValue' => new FieldValue( - [ - 'data' => ['destinationContentIds' => []], - ] - ), - ] - ); - - $actualFieldDefinition = new PersistenceFieldDefinition(); - - $this->converter->toFieldDefinition($storageFieldDefinition, $actualFieldDefinition); - - $this->assertEquals( - $expectedFieldDefinition, - $actualFieldDefinition - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/RelationTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/RelationTest.php deleted file mode 100644 index 6cc2b21df7..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/RelationTest.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use eZ\Publish\Core\FieldType\RelationList\Type; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test case for Relation converter in Legacy storage. - */ -class RelationTest extends TestCase -{ - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationConverter */ - protected $converter; - - protected function setUp(): void - { - parent::setUp(); - $this->converter = new RelationConverter(); - } - - /** - * @group fieldType - * @group relationlist - */ - public function testToStorageFieldDefinition() - { - $fieldDefinition = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => new FieldTypeConstraints( - [ - 'fieldSettings' => [ - 'selectionMethod' => Type::SELECTION_BROWSE, - 'selectionRoot' => 12345, - 'selectionContentTypes' => ['article', 'blog_post'], - ], - ] - ), - ] - ); - - $expectedStorageFieldDefinition = new StorageFieldDefinition(); - $expectedStorageFieldDefinition->dataText5 = <<<EOT -<?xml version="1.0" encoding="utf-8"?> -<related-objects><constraints><allowed-class contentclass-identifier="article"/><allowed-class contentclass-identifier="blog_post"/></constraints><selection_type value="0"/><contentobject-placement node-id="12345"/></related-objects> - -EOT; - // For BC these are still set - $expectedStorageFieldDefinition->dataInt1 = 0; - $expectedStorageFieldDefinition->dataInt2 = 12345; - - $actualStorageFieldDefinition = new StorageFieldDefinition(); - - $this->converter->toStorageFieldDefinition($fieldDefinition, $actualStorageFieldDefinition); - - $this->assertEquals( - $expectedStorageFieldDefinition, - $actualStorageFieldDefinition - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/SelectionTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/SelectionTest.php deleted file mode 100644 index bf88ea9404..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/SelectionTest.php +++ /dev/null @@ -1,338 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use eZ\Publish\API\Repository\LanguageService; -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test case for Selection converter in Legacy storage. - */ -class SelectionTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter */ - protected $converter; - - protected function setUp(): void - { - parent::setUp(); - $languageServiceMock = $this->createMock(LanguageService::class); - - $this->converter = new SelectionConverter($languageServiceMock); - } - - /** - * @group fieldType - * @group selection - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter::toStorageValue - */ - public function testToStorageValue() - { - $fieldValue = new FieldValue(); - $fieldValue->data = [1, 3]; - $fieldValue->sortKey = '1-3'; - - $expectedStorageFieldValue = new StorageFieldValue(); - $expectedStorageFieldValue->dataText = '1-3'; - $expectedStorageFieldValue->sortKeyString = '1-3'; - - $actualStorageFieldValue = new StorageFieldValue(); - - $this->converter->toStorageValue($fieldValue, $actualStorageFieldValue); - - $this->assertEquals( - $expectedStorageFieldValue, - $actualStorageFieldValue - ); - } - - /** - * @group fieldType - * @group selection - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter::toStorageValue - */ - public function testToStorageValueEmpty() - { - $fieldValue = new FieldValue(); - $fieldValue->data = []; - $fieldValue->sortKey = ''; - - $expectedStorageFieldValue = new StorageFieldValue(); - $expectedStorageFieldValue->dataText = ''; - $expectedStorageFieldValue->sortKeyString = ''; - - $actualStorageFieldValue = new StorageFieldValue(); - - $this->converter->toStorageValue($fieldValue, $actualStorageFieldValue); - - $this->assertEquals( - $expectedStorageFieldValue, - $actualStorageFieldValue - ); - } - - /** - * @group fieldType - * @group selection - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter::toFieldValue - */ - public function testToFieldValue() - { - $storageFieldValue = new StorageFieldValue(); - $storageFieldValue->dataText = '1-3'; - $storageFieldValue->sortKeyString = '1-3'; - - $expectedFieldValue = new FieldValue(); - $expectedFieldValue->data = [1, 3]; - $expectedFieldValue->sortKey = '1-3'; - - $actualFieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $actualFieldValue); - - $this->assertEquals( - $expectedFieldValue, - $actualFieldValue - ); - } - - /** - * @group fieldType - * @group selection - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter::toFieldValue - */ - public function testToFieldValueEmpty() - { - $storageFieldValue = new StorageFieldValue(); - $storageFieldValue->dataText = ''; - $storageFieldValue->sortKeyString = ''; - - $expectedFieldValue = new FieldValue(); - $expectedFieldValue->data = []; - $expectedFieldValue->sortKey = ''; - - $actualFieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $actualFieldValue); - - $this->assertEquals( - $expectedFieldValue, - $actualFieldValue - ); - } - - /** - * @group fieldType - * @group selection - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionMultiple() - { - $fieldDefinition = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => new FieldTypeConstraints( - [ - 'fieldSettings' => new FieldSettings( - [ - 'isMultiple' => true, - 'options' => [ - 0 => 'First', - 1 => 'Second', - 2 => 'Third', - ], - ] - ), - ] - ), - ] - ); - - $expectedStorageFieldDefinition = new StorageFieldDefinition(); - $expectedStorageFieldDefinition->dataInt1 = 1; - $expectedStorageFieldDefinition->dataText5 = <<<EOT -<?xml version="1.0" encoding="utf-8"?> -<ezselection><options><option id="0" name="First"/><option id="1" name="Second"/><option id="2" name="Third"/></options></ezselection> - -EOT; - - $actualStorageFieldDefinition = new StorageFieldDefinition(); - - $this->converter->toStorageFieldDefinition($fieldDefinition, $actualStorageFieldDefinition); - - $this->assertEquals($expectedStorageFieldDefinition, $actualStorageFieldDefinition); - } - - /** - * @group fieldType - * @group selection - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionSingle() - { - $fieldDefinition = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => new FieldTypeConstraints( - [ - 'fieldSettings' => new FieldSettings( - [ - 'isMultiple' => false, - 'options' => [ - 0 => 'First', - ], - ] - ), - ] - ), - ] - ); - - $expectedStorageFieldDefinition = new StorageFieldDefinition(); - $expectedStorageFieldDefinition->dataInt1 = 0; - $expectedStorageFieldDefinition->dataText5 = <<<EOT -<?xml version="1.0" encoding="utf-8"?> -<ezselection><options><option id="0" name="First"/></options></ezselection> - -EOT; - - $actualStorageFieldDefinition = new StorageFieldDefinition(); - - $this->converter->toStorageFieldDefinition($fieldDefinition, $actualStorageFieldDefinition); - - $this->assertEquals($expectedStorageFieldDefinition, $actualStorageFieldDefinition); - } - - /** - * @group fieldType - * @group selection - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter::toFieldDefinition - */ - public function testToFieldDefinitionMultiple() - { - $storageFieldDefinition = new StorageFieldDefinition(); - $storageFieldDefinition->dataInt1 = 1; - $storageFieldDefinition->dataText5 = <<<EOT -<?xml version="1.0" encoding="utf-8"?> -<ezselection> - <options> - <option id="0" name="First"/> - <option id="1" name="Second"/> - <option id="2" name="Third"/> - </options> -</ezselection> -EOT; - - $expectedFieldDefinition = new PersistenceFieldDefinition( - [ - 'name' => [ - 'eng-GB' => 'test name', - ], - 'mainLanguageCode' => 'eng-GB', - 'fieldTypeConstraints' => new FieldTypeConstraints( - [ - 'fieldSettings' => new FieldSettings( - [ - 'isMultiple' => true, - 'options' => [ - 0 => 'First', - 1 => 'Second', - 2 => 'Third', - ], - 'multilingualOptions' => [ - 'eng-GB' => [ - 0 => 'First', - 1 => 'Second', - 2 => 'Third', - ], - ], - ] - ), - ] - ), - 'defaultValue' => new FieldValue( - [ - 'data' => [], - 'sortKey' => '', - ] - ), - ] - ); - - $actualFieldDefinition = new PersistenceFieldDefinition( - [ - 'name' => [ - 'eng-GB' => 'test name', - ], - 'mainLanguageCode' => 'eng-GB', - ] - ); - - $this->converter->toFieldDefinition($storageFieldDefinition, $actualFieldDefinition); - - $this->assertEquals($expectedFieldDefinition, $actualFieldDefinition); - } - - /** - * @group fieldType - * @group selection - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter::toFieldDefinition - */ - public function testToFieldDefinitionSingleEmpty() - { - $storageFieldDefinition = new StorageFieldDefinition(); - $storageFieldDefinition->dataInt1 = 0; - $storageFieldDefinition->dataText5 = <<<EOT -<?xml version="1.0" encoding="utf-8"?> -<ezselection> - <options> - </options> -</ezselection> -EOT; - - $expectedFieldDefinition = new PersistenceFieldDefinition( - [ - 'name' => [ - 'eng-GB' => 'test name', - ], - 'mainLanguageCode' => 'eng-GB', - 'fieldTypeConstraints' => new FieldTypeConstraints( - [ - 'fieldSettings' => new FieldSettings( - [ - 'isMultiple' => false, - 'options' => [], - 'multilingualOptions' => [ - 'eng-GB' => [], - ], - ] - ), - ] - ), - 'defaultValue' => new FieldValue(['data' => []]), - ] - ); - - $actualFieldDefinition = new PersistenceFieldDefinition( - [ - 'name' => [ - 'eng-GB' => 'test name', - ], - 'mainLanguageCode' => 'eng-GB', - ] - ); - - $this->converter->toFieldDefinition($storageFieldDefinition, $actualFieldDefinition); - - $this->assertEquals($expectedFieldDefinition, $actualFieldDefinition); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/TextBlockTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/TextBlockTest.php deleted file mode 100644 index 1aeb92220f..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/TextBlockTest.php +++ /dev/null @@ -1,130 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\FieldType\TextBlock\Value as TextBlockValue; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test case for TextBlock converter in Legacy storage. - */ -class TextBlockTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter */ - protected $converter; - - protected $longText; - - protected function setUp(): void - { - parent::setUp(); - $this->converter = new TextBlockConverter(); - $this->longText = <<<EOT -Now that we know who you are, I know who I am. -I'm not a mistake! It all makes sense! In a comic, you know how you can tell who the arch-villain's going to be? -He's the exact opposite of the hero. And most times they're friends, like you and me! I should've known way back when... -You know why, David? Because of the kids. - -They called me Mr Glass. -EOT; - } - - /** - * @group fieldType - * @group textBlock - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter::toStorageValue - */ - public function testToStorageValue() - { - $value = new FieldValue(); - $value->data = $this->longText; - $value->sortKey = 'Now that we know who you are'; - $storageFieldValue = new StorageFieldValue(); - - $this->converter->toStorageValue($value, $storageFieldValue); - self::assertSame($value->data, $storageFieldValue->dataText); - self::assertSame($value->sortKey, $storageFieldValue->sortKeyString); - self::assertSame(0, $storageFieldValue->sortKeyInt); - } - - /** - * @group fieldType - * @group textBlock - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter::toFieldValue - */ - public function testToFieldValue() - { - $storageFieldValue = new StorageFieldValue(); - $storageFieldValue->dataText = $this->longText; - $storageFieldValue->sortKeyString = 'Now that we know who you are'; - $fieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $fieldValue); - self::assertSame($storageFieldValue->dataText, $fieldValue->data); - self::assertSame($storageFieldValue->sortKeyString, $fieldValue->sortKey); - } - - /** - * @group fieldType - * @group textBlock - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinition() - { - $storageFieldDef = new StorageFieldDefinition(); - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'textRows' => 15, - ] - ); - $fieldDef = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - 'defaultValue' => new TextBlockValue(), - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - self::assertSame( - 15, - $storageFieldDef->dataInt1 - ); - } - - /** - * @group fieldType - * @group textBlock - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter::toFieldDefinition - */ - public function testToFieldDefinition() - { - $fieldDef = new PersistenceFieldDefinition(); - $storageDef = new StorageFieldDefinition( - [ - 'dataInt1' => 20, - ] - ); - - $this->converter->toFieldDefinition($storageDef, $fieldDef); - - self::assertSame('', $fieldDef->defaultValue->sortKey); - self::assertNull($fieldDef->fieldTypeConstraints->validators); - self::assertInstanceOf('eZ\\Publish\\Core\\FieldType\\FieldSettings', $fieldDef->fieldTypeConstraints->fieldSettings); - self::assertSame( - ['textRows' => 20], - $fieldDef->fieldTypeConstraints->fieldSettings->getArrayCopy() - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/TextLineTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/TextLineTest.php deleted file mode 100644 index d84406bfec..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/TextLineTest.php +++ /dev/null @@ -1,162 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test case for TextLine converter in Legacy storage. - */ -class TextLineTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter */ - protected $converter; - - protected function setUp(): void - { - parent::setUp(); - $this->converter = new TextLineConverter(); - } - - /** - * @group fieldType - * @group textLine - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter::toStorageValue - */ - public function testToStorageValue() - { - $value = new FieldValue(); - $value->data = "He's holding a thermal detonator!"; - $value->sortKey = "He's holding"; - $storageFieldValue = new StorageFieldValue(); - - $this->converter->toStorageValue($value, $storageFieldValue); - self::assertSame($value->data, $storageFieldValue->dataText); - self::assertSame($value->sortKey, $storageFieldValue->sortKeyString); - self::assertSame(0, $storageFieldValue->sortKeyInt); - } - - /** - * @group fieldType - * @group textLine - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter::toFieldValue - */ - public function testToFieldValue() - { - $storageFieldValue = new StorageFieldValue(); - $storageFieldValue->dataText = 'When 900 years old, you reach... Look as good, you will not.'; - $storageFieldValue->sortKeyString = 'When 900 years old, you reach...'; - $storageFieldValue->sortKeyInt = 0; - $fieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $fieldValue); - self::assertSame($storageFieldValue->dataText, $fieldValue->data); - self::assertSame($storageFieldValue->sortKeyString, $fieldValue->sortKey); - } - - /** - * @group fieldType - * @group textLine - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionWithValidator() - { - $defaultText = 'This is a default text'; - $storageFieldDef = new StorageFieldDefinition(); - $defaultValue = new FieldValue(); - $defaultValue->data = $defaultText; - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->validators = [ - TextLineConverter::STRING_LENGTH_VALIDATOR_IDENTIFIER => ['maxStringLength' => 100], - ]; - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'defaultText' => $defaultText, - ] - ); - $fieldDef = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - 'defaultValue' => $defaultValue, - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - self::assertSame( - $fieldDef->fieldTypeConstraints->validators[TextLineConverter::STRING_LENGTH_VALIDATOR_IDENTIFIER], - ['maxStringLength' => $storageFieldDef->dataInt1] - ); - self::assertSame( - $fieldDef->fieldTypeConstraints->fieldSettings['defaultText'], - $storageFieldDef->dataText1 - ); - } - - /** - * @group fieldType - * @group textLine - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionNoValidator() - { - $defaultText = 'This is a default text'; - $storageFieldDef = new StorageFieldDefinition(); - $defaultValue = new FieldValue(); - $defaultValue->data = $defaultText; - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldDef = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - 'defaultValue' => $defaultValue, - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - self::assertSame( - 0, - $storageFieldDef->dataInt1 - ); - self::assertSame( - $fieldDef->defaultValue->data, - $storageFieldDef->dataText1 - ); - } - - /** - * @group fieldType - * @group textLine - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter::toFieldDefinition - */ - public function testToFieldDefinition() - { - $defaultText = 'This is a default value'; - $fieldDef = new PersistenceFieldDefinition(); - $storageDef = new StorageFieldDefinition( - [ - 'dataInt1' => 100, - 'dataText1' => $defaultText, - ] - ); - - $this->converter->toFieldDefinition($storageDef, $fieldDef); - self::assertSame( - [ - TextLineConverter::STRING_LENGTH_VALIDATOR_IDENTIFIER => ['maxStringLength' => 100], - ], - $fieldDef->fieldTypeConstraints->validators - ); - self::assertSame($defaultText, $fieldDef->defaultValue->data); - self::assertSame($defaultText, $fieldDef->defaultValue->sortKey); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/TimeTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/TimeTest.php deleted file mode 100644 index 08bc86d638..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/TimeTest.php +++ /dev/null @@ -1,175 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use DateTime; -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\FieldType\Time\Type as TimeType; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test case for Time converter in Legacy storage. - * - * @group fieldType - * @group time - */ -class TimeTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter */ - protected $converter; - - /** @var int */ - protected $time; - - protected function setUp(): void - { - parent::setUp(); - $this->converter = new TimeConverter(); - $this->time = 3661; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter::toStorageValue - */ - public function testToStorageValue() - { - $value = new FieldValue(); - $value->data = $this->time; - $value->sortKey = $this->time; - $storageFieldValue = new StorageFieldValue(); - - $this->converter->toStorageValue($value, $storageFieldValue); - self::assertSame($value->data, $storageFieldValue->dataInt); - self::assertSame($value->sortKey, $storageFieldValue->sortKeyInt); - self::assertSame('', $storageFieldValue->sortKeyString); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter::toFieldValue - */ - public function testToFieldValue() - { - $storageFieldValue = new StorageFieldValue(); - $storageFieldValue->dataInt = $this->time; - $storageFieldValue->sortKeyString = ''; - $storageFieldValue->sortKeyInt = $this->time; - $fieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $fieldValue); - self::assertSame($this->time, $fieldValue->data); - self::assertSame($storageFieldValue->dataInt, $fieldValue->data); - self::assertSame($storageFieldValue->sortKeyInt, $fieldValue->sortKey); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionDefaultEmpty() - { - $storageFieldDef = new StorageFieldDefinition(); - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'useSeconds' => true, - 'defaultType' => TimeType::DEFAULT_EMPTY, - ] - ); - $fieldDef = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - self::assertSame(TimeType::DEFAULT_EMPTY, $storageFieldDef->dataInt1); - self::assertSame(1, $storageFieldDef->dataInt2); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinitionDefaultCurrentTime() - { - $storageFieldDef = new StorageFieldDefinition(); - $fieldTypeConstraints = new FieldTypeConstraints(); - $fieldTypeConstraints->fieldSettings = new FieldSettings( - [ - 'useSeconds' => false, - 'defaultType' => TimeType::DEFAULT_CURRENT_TIME, - ] - ); - $fieldDef = new PersistenceFieldDefinition( - [ - 'fieldTypeConstraints' => $fieldTypeConstraints, - ] - ); - - $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); - self::assertSame(TimeType::DEFAULT_CURRENT_TIME, $storageFieldDef->dataInt1); - self::assertSame(0, $storageFieldDef->dataInt2); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter::toFieldDefinition - */ - public function testToFieldDefinitionDefaultEmpty() - { - $fieldDef = new PersistenceFieldDefinition(); - $storageDef = new StorageFieldDefinition( - [ - 'dataInt2' => 1, - 'dataInt1' => TimeType::DEFAULT_EMPTY, - ] - ); - - $this->converter->toFieldDefinition($storageDef, $fieldDef); - self::assertNull($fieldDef->defaultValue->data); - self::assertEquals( - new FieldSettings( - [ - 'useSeconds' => true, - 'defaultType' => TimeType::DEFAULT_EMPTY, - ] - ), - $fieldDef->fieldTypeConstraints->fieldSettings - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter::toFieldDefinition - */ - public function testToFieldDefinitionDefaultCurrentTime() - { - $fieldDef = new PersistenceFieldDefinition(); - $storageDef = new StorageFieldDefinition( - [ - 'dataInt2' => 0, - 'dataInt1' => TimeType::DEFAULT_CURRENT_TIME, - ] - ); - - $dateTime = new DateTime(); - $dateTime->setTime(0, 0, 0); - $this->converter->toFieldDefinition($storageDef, $fieldDef); - self::assertSame(time() - $dateTime->getTimestamp(), $fieldDef->defaultValue->data); - self::assertEquals( - new FieldSettings( - [ - 'useSeconds' => false, - 'defaultType' => TimeType::DEFAULT_CURRENT_TIME, - ] - ), - $fieldDef->fieldTypeConstraints->fieldSettings - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/UrlTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/UrlTest.php deleted file mode 100644 index dbf943e69a..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/UrlTest.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; -use PHPUnit\Framework\TestCase; - -/** - * Test case for Url converter in Legacy storage. - */ -class UrlTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter */ - protected $converter; - - protected function setUp(): void - { - parent::setUp(); - $this->converter = new UrlConverter(); - } - - /** - * @group fieldType - * @group url - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter::toStorageValue - */ - public function testToStorageValue() - { - $value = new FieldValue(); - $text = 'eZ Systems'; - $value->data = ['text' => $text]; - $value->externalData = 'http://ez.no/'; - $value->sortKey = false; - $storageFieldValue = new StorageFieldValue(); - - $this->converter->toStorageValue($value, $storageFieldValue); - self::assertSame($text, $storageFieldValue->dataText); - } - - /** - * @group fieldType - * @group url - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter::toFieldValue - */ - public function testToFieldValue() - { - $text = "A link's text"; - $urlId = 842; - $storageFieldValue = new StorageFieldValue(); - $storageFieldValue->dataText = $text; - $storageFieldValue->dataInt = $urlId; - $storageFieldValue->sortKeyString = false; - $storageFieldValue->sortKeyInt = false; - $fieldValue = new FieldValue(); - - $this->converter->toFieldValue($storageFieldValue, $fieldValue); - self::assertIsArray($fieldValue->data); - self::assertFalse($fieldValue->sortKey); - self::assertSame($text, $fieldValue->data['text']); - self::assertEquals($urlId, $fieldValue->data['urlId']); - } - - /** - * @group fieldType - * @group url - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter::toStorageFieldDefinition - */ - public function testToStorageFieldDefinition() - { - $this->converter->toStorageFieldDefinition(new PersistenceFieldDefinition(), new StorageFieldDefinition()); - } - - /** - * @group fieldType - * @group url - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter::toFieldDefinition - */ - public function testToFieldDefinition() - { - $this->converter->toFieldDefinition(new StorageFieldDefinition(), new PersistenceFieldDefinition()); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValueConverterRegistryTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValueConverterRegistryTest.php deleted file mode 100644 index 14c94411d1..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValueConverterRegistryTest.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry as Registry; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; - -/** - * Test case for FieldValue Converter Registry. - */ -class FieldValueConverterRegistryTest extends TestCase -{ - private const TYPE_NAME = 'some-type'; - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry::register - */ - public function testRegister() - { - $converter = $this->getFieldValueConverterMock(); - $registry = new Registry([self::TYPE_NAME => $converter]); - - $this->assertSame($converter, $registry->getConverter(self::TYPE_NAME)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry::getConverter - */ - public function testGetStorage() - { - $converter = $this->getFieldValueConverterMock(); - $registry = new Registry([self::TYPE_NAME => $converter]); - - $res = $registry->getConverter(self::TYPE_NAME); - - $this->assertSame( - $converter, - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry::getConverter - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound - */ - public function testGetNotFound() - { - $this->expectException(\eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound::class); - - $registry = new Registry([]); - - $registry->getConverter('not-found'); - } - - /** - * Returns a mock for Storage. - * - * @return Storage - */ - protected function getFieldValueConverterMock() - { - return $this->createMock(Converter::class); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index 82dbf5a8a2..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,2119 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Gateway; - -use Doctrine\DBAL\ParameterType; -use eZ\Publish\API\Repository\Values\Content\Relation as RelationValue; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\Core\Persistence\Legacy\Tests\Content\LanguageAwareTestCase; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\UpdateStruct; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Test case for eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase. - */ -class DoctrineDatabaseTest extends LanguageAwareTestCase -{ - /** - * Database gateway to test. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase - */ - protected $databaseGateway; - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::insertContentObject - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::generateLanguageMask - * - * @todo Fix not available fields - */ - public function testInsertContentObject() - { - $struct = $this->getCreateStructFixture(); - - $gateway = $this->getDatabaseGateway(); - $gateway->insertContentObject($struct); - - $this->assertQueryResult( - [ - [ - 'name' => 'Content name', - 'contentclass_id' => '23', - 'section_id' => '42', - 'owner_id' => '13', - 'current_version' => '1', - 'initial_language_id' => '2', - 'remote_id' => 'some_remote_id', - 'language_mask' => '3', - 'modified' => '0', - 'published' => '0', - 'status' => ContentInfo::STATUS_DRAFT, - ], - ], - $this->getDatabaseConnection() - ->createQueryBuilder() - ->select( - [ - 'name', - 'contentclass_id', - 'section_id', - 'owner_id', - 'current_version', - 'initial_language_id', - 'remote_id', - 'language_mask', - 'modified', - 'published', - 'status', - ] - )->from('ezcontentobject') - ); - } - - /** - * Returns a Content fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\CreateStruct - */ - protected function getCreateStructFixture() - { - $struct = new CreateStruct(); - - $struct->typeId = 23; - $struct->sectionId = 42; - $struct->ownerId = 13; - $struct->initialLanguageId = 2; - $struct->remoteId = 'some_remote_id'; - $struct->alwaysAvailable = true; - $struct->modified = 456; - $struct->name = [ - 'eng-US' => 'Content name', - ]; - $struct->fields = [ - new Field(['languageCode' => 'eng-US']), - ]; - $struct->locations = []; - - return $struct; - } - - /** - * Returns a Content fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content - */ - protected function getContentFixture() - { - $content = new Content(); - - $content->versionInfo = new VersionInfo(); - $content->versionInfo->names = [ - 'eng-US' => 'Content name', - ]; - $content->versionInfo->status = VersionInfo::STATUS_PENDING; - - $content->versionInfo->contentInfo = new ContentInfo(); - $content->versionInfo->contentInfo->contentTypeId = 23; - $content->versionInfo->contentInfo->sectionId = 42; - $content->versionInfo->contentInfo->ownerId = 13; - $content->versionInfo->contentInfo->currentVersionNo = 2; - $content->versionInfo->contentInfo->mainLanguageCode = 'eng-US'; - $content->versionInfo->contentInfo->remoteId = 'some_remote_id'; - $content->versionInfo->contentInfo->alwaysAvailable = true; - $content->versionInfo->contentInfo->publicationDate = 123; - $content->versionInfo->contentInfo->modificationDate = 456; - $content->versionInfo->contentInfo->isPublished = false; - $content->versionInfo->contentInfo->name = 'Content name'; - - return $content; - } - - /** - * Returns a Version fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo - */ - protected function getVersionFixture() - { - $version = new VersionInfo(); - - $version->id = null; - $version->versionNo = 1; - $version->creatorId = 13; - $version->status = 0; - $version->creationDate = 1312278322; - $version->modificationDate = 1312278323; - $version->initialLanguageCode = self::ENG_GB; - $version->contentInfo = new ContentInfo( - [ - 'id' => 2342, - 'alwaysAvailable' => true, - ] - ); - - return $version; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::insertVersion - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::generateLanguage - */ - public function testInsertVersion() - { - $version = $this->getVersionFixture(); - - $gateway = $this->getDatabaseGateway(); - $gateway->insertVersion($version, []); - - $this->assertQueryResult( - [ - [ - 'contentobject_id' => '2342', - 'created' => '1312278322', - 'creator_id' => '13', - 'modified' => '1312278323', - 'status' => '0', - 'workflow_event_pos' => '0', - 'version' => '1', - 'language_mask' => '5', - 'initial_language_id' => '4', - // Not needed, according to field mapping document - // 'user_id', - ], - ], - $this->getDatabaseConnection() - ->createQueryBuilder() - ->select( - [ - 'contentobject_id', - 'created', - 'creator_id', - 'modified', - 'status', - 'workflow_event_pos', - 'version', - 'language_mask', - 'initial_language_id', - ] - )->from('ezcontentobject_version') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::setStatus - */ - public function testSetStatus() - { - $gateway = $this->getDatabaseGateway(); - - // insert content - $struct = $this->getCreateStructFixture(); - $contentId = $gateway->insertContentObject($struct); - - // insert version - $version = $this->getVersionFixture(); - $version->contentInfo->id = $contentId; - $gateway->insertVersion($version, []); - - $this->assertTrue( - $gateway->setStatus($version->contentInfo->id, $version->versionNo, VersionInfo::STATUS_PENDING) - ); - - $this->assertQueryResult( - [[VersionInfo::STATUS_PENDING]], - $this->getDatabaseConnection() - ->createQueryBuilder() - ->select('status') - ->from('ezcontentobject_version') - ); - - // check that content status has not been set to published - $this->assertQueryResult( - [[VersionInfo::STATUS_DRAFT]], - $this->getDatabaseConnection() - ->createQueryBuilder() - ->select('status') - ->from('ezcontentobject') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::setStatus - */ - public function testSetStatusPublished() - { - $gateway = $this->getDatabaseGateway(); - - // insert content - $struct = $this->getCreateStructFixture(); - $contentId = $gateway->insertContentObject($struct); - - // insert version - $version = $this->getVersionFixture(); - $version->contentInfo->id = $contentId; - $gateway->insertVersion($version, []); - - $this->assertTrue( - $gateway->setStatus($version->contentInfo->id, $version->versionNo, VersionInfo::STATUS_PUBLISHED) - ); - - $this->assertQueryResult( - [[VersionInfo::STATUS_PUBLISHED]], - $this->getDatabaseConnection() - ->createQueryBuilder() - ->select('status') - ->from('ezcontentobject_version') - ); - - // check that content status has been set to published - $this->assertQueryResult( - [[ContentInfo::STATUS_PUBLISHED]], - $this->getDatabaseConnection() - ->createQueryBuilder() - ->select('status') - ->from('ezcontentobject') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::setStatus - */ - public function testSetStatusUnknownVersion() - { - $gateway = $this->getDatabaseGateway(); - - $this->assertFalse( - $gateway->setStatus(23, 42, 2) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::updateContent - */ - public function testUpdateContent() - { - $gateway = $this->getDatabaseGateway(); - - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $metadataStruct = $this->getMetadataUpdateStructFixture(); - - $gateway->updateContent(10, $metadataStruct); - - $this->assertQueryResult( - [ - [ - 'initial_language_id' => '3', - 'modified' => '234567', - 'owner_id' => '42', - 'published' => '123456', - 'remote_id' => 'ghjk1234567890ghjk1234567890', - 'name' => 'Thoth', - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select( - 'initial_language_id', - 'modified', - 'owner_id', - 'published', - 'remote_id', - 'name' - )->from('ezcontentobject') - ->where('id = 10') - ); - } - - /** - * Returns an UpdateStruct fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\UpdateStruct - */ - protected function getUpdateStructFixture() - { - $struct = new UpdateStruct(); - $struct->creatorId = 23; - $struct->fields = []; - $struct->modificationDate = 234567; - $struct->initialLanguageId = 2; - - return $struct; - } - - /** - * Returns a MetadataUpdateStruct fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct - */ - protected function getMetadataUpdateStructFixture() - { - $struct = new MetadataUpdateStruct(); - $struct->ownerId = 42; - $struct->publicationDate = 123456; - $struct->mainLanguageId = 3; - $struct->modificationDate = 234567; - $struct->remoteId = 'ghjk1234567890ghjk1234567890'; - $struct->name = 'Thoth'; - - return $struct; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::updateVersion - */ - public function testUpdateVersion() - { - $gateway = $this->getDatabaseGateway(); - - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway->updateVersion(10, 2, $this->getUpdateStructFixture()); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $expr = $query->expr(); - $this->assertQueryResult( - [ - [ - 'creator_id' => '23', - 'initial_language_id' => '2', - 'modified' => '234567', - ], - ], - $query - ->select( - [ - 'creator_id', - 'initial_language_id', - 'modified', - ] - )->from('ezcontentobject_version') - ->where( - $expr->andX( - $expr->eq('contentobject_id', 10), - $expr->eq('version', 2) - ) - ) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::insertNewField - */ - public function testInsertNewField() - { - $content = $this->getContentFixture(); - $content->versionInfo->contentInfo->id = 2342; - - $field = $this->getFieldFixture(); - $value = $this->getStorageValueFixture(); - - $gateway = $this->getDatabaseGateway(); - $gateway->insertNewField($content, $field, $value); - - $this->assertQueryResult( - [ - [ - 'contentclassattribute_id' => '231', - 'contentobject_id' => '2342', - 'data_float' => '24.42', - 'data_int' => '42', - 'data_text' => 'Test text', - 'data_type_string' => 'ezstring', - 'language_code' => self::ENG_GB, - 'language_id' => '4', - 'sort_key_int' => '23', - 'sort_key_string' => 'Test', - 'version' => '1', - ], - ], - $this->getDatabaseConnection() - ->createQueryBuilder() - ->select( - [ - 'contentclassattribute_id', - 'contentobject_id', - 'data_float', - 'data_int', - 'data_text', - 'data_type_string', - 'language_code', - 'language_id', - 'sort_key_int', - 'sort_key_string', - 'version', - ] - )->from('ezcontentobject_attribute') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::insertNewField - */ - public function testInsertNewAlwaysAvailableField() - { - $content = $this->getContentFixture(); - $content->versionInfo->contentInfo->id = 2342; - // Set main language to the one used in the field fixture - $content->versionInfo->contentInfo->mainLanguageCode = self::ENG_GB; - - $field = $this->getFieldFixture(); - $value = $this->getStorageValueFixture(); - - $gateway = $this->getDatabaseGateway(); - $gateway->insertNewField($content, $field, $value); - - $this->assertQueryResult( - [ - [ - 'contentclassattribute_id' => '231', - 'contentobject_id' => '2342', - 'data_float' => '24.42', - 'data_int' => '42', - 'data_text' => 'Test text', - 'data_type_string' => 'ezstring', - 'language_code' => self::ENG_GB, - 'language_id' => '5', - 'sort_key_int' => '23', - 'sort_key_string' => 'Test', - 'version' => '1', - ], - ], - $this->getDatabaseConnection() - ->createQueryBuilder() - ->select( - [ - 'contentclassattribute_id', - 'contentobject_id', - 'data_float', - 'data_int', - 'data_text', - 'data_type_string', - 'language_code', - 'language_id', - 'sort_key_int', - 'sort_key_string', - 'version', - ] - )->from('ezcontentobject_attribute') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::updateField - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::setFieldUpdateValues - */ - public function testUpdateField() - { - $content = $this->getContentFixture(); - $content->versionInfo->contentInfo->id = 2342; - - $field = $this->getFieldFixture(); - $value = $this->getStorageValueFixture(); - - $gateway = $this->getDatabaseGateway(); - $field->id = $gateway->insertNewField($content, $field, $value); - - $newValue = new StorageFieldValue( - [ - 'dataFloat' => 124.42, - 'dataInt' => 142, - 'dataText' => 'New text', - 'sortKeyInt' => 123, - 'sortKeyString' => 'new_text', - ] - ); - - $gateway->updateField($field, $newValue); - - $this->assertQueryResult( - [ - [ - 'data_float' => '124.42', - 'data_int' => '142', - 'data_text' => 'New text', - 'sort_key_int' => '123', - 'sort_key_string' => 'new_text', - ], - ], - $this->getDatabaseConnection() - ->createQueryBuilder() - ->select( - [ - 'data_float', - 'data_int', - 'data_text', - 'sort_key_int', - 'sort_key_string', - ] - )->from('ezcontentobject_attribute') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::updateNonTranslatableField - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::setFieldUpdateValues - */ - public function testUpdateNonTranslatableField() - { - $content = $this->getContentFixture(); - $content->versionInfo->contentInfo->id = 2342; - - $fieldGb = $this->getFieldFixture(); - $fieldUs = $this->getOtherLanguageFieldFixture(); - $value = $this->getStorageValueFixture(); - - $gateway = $this->getDatabaseGateway(); - $fieldGb->id = $gateway->insertNewField($content, $fieldGb, $value); - $fieldUs->id = $gateway->insertNewField($content, $fieldUs, $value); - - $updateStruct = new Content\UpdateStruct(); - - $newValue = new StorageFieldValue( - [ - 'dataFloat' => 124.42, - 'dataInt' => 142, - 'dataText' => 'New text', - 'sortKeyInt' => 123, - 'sortKeyString' => 'new_text', - ] - ); - - $gateway->updateNonTranslatableField($fieldGb, $newValue, $content->versionInfo->contentInfo->id); - - $this->assertQueryResult( - [ - // Both fields updated - [ - 'data_float' => '124.42', - 'data_int' => '142', - 'data_text' => 'New text', - 'sort_key_int' => '123', - 'sort_key_string' => 'new_text', - ], - [ - 'data_float' => '124.42', - 'data_int' => '142', - 'data_text' => 'New text', - 'sort_key_int' => '123', - 'sort_key_string' => 'new_text', - ], - ], - $this->getDatabaseConnection() - ->createQueryBuilder() - ->select( - [ - 'data_float', - 'data_int', - 'data_text', - 'sort_key_int', - 'sort_key_string', - ] - )->from('ezcontentobject_attribute') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::listVersions - */ - public function testListVersions(): void - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - $res = $gateway->listVersions(226); - - $this->assertCount( - 2, - $res - ); - - foreach ($res as $row) { - $this->assertCount( - 23, - $row - ); - } - - $this->assertEquals( - 675, - $res[0]['ezcontentobject_version_id'] - ); - $this->assertEquals( - 676, - $res[1]['ezcontentobject_version_id'] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::listVersionNumbers - */ - public function testListVersionNumbers() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - $res = $gateway->listVersionNumbers(226); - - $this->assertEquals([1, 2], $res); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::listVersionsForUser - */ - public function testListVersionsForUser() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - $res = $gateway->listVersionsForUser(14); - - $this->assertCount( - 2, - $res - ); - - foreach ($res as $row) { - $this->assertCount( - 23, - $row - ); - } - - $this->assertEquals( - 677, - $res[0]['ezcontentobject_version_id'] - ); - $this->assertEquals( - 0, - $res[0]['ezcontentobject_version_status'] - ); - $this->assertEquals( - 678, - $res[1]['ezcontentobject_version_id'] - ); - $this->assertEquals( - 0, - $res[1]['ezcontentobject_version_status'] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::load - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder - */ - public function testLoadWithAllTranslations() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - $res = $gateway->load(226, 2); - - $this->assertValuesInRows( - 'ezcontentobject_attribute_language_code', - ['eng-US', self::ENG_GB], - $res - ); - - $this->assertValuesInRows( - 'ezcontentobject_attribute_language_id', - ['2', '4'], - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::load - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder - */ - public function testCreateFixtureForMapperExtractContentFromRowsMultipleVersions() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - - $resFirst = $gateway->load(11, 1); - $resSecond = $gateway->load(11, 2); - - $res = array_merge($resFirst, $resSecond); - - $orig = include __DIR__ . '/../_fixtures/extract_content_from_rows_multiple_versions.php'; - - /*$this->storeFixture( - __DIR__ . '/../_fixtures/extract_content_from_rows_multiple_versions.php', - $res - );*/ - - $this->assertEquals($orig, $res, 'Fixtures differ between what was previously stored(expected) and what it now generates(actual), this hints either some mistake in impl or that the fixture (../_fixtures/extract_content_from_rows_multiple_versions.php) and tests needs to be adapted.'); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::load - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder - */ - public function testCreateFixtureForMapperExtractContentFromRows() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - - $res = array_merge($gateway->load(226, 2)); - - $orig = include __DIR__ . '/../_fixtures/extract_content_from_rows.php'; - - /*$this->storeFixture( - __DIR__ . '/../_fixtures/extract_content_from_rows.php', - $res - );*/ - - $this->assertEquals($orig, $res, 'Fixtures differ between what was previously stored(expected) and what it now generates(actual), this hints either some mistake in impl or that the fixture (../_fixtures/extract_content_from_rows.php) and tests needs to be adapted.'); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::load - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder - */ - public function testLoadWithSingleTranslation() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - $res = $gateway->load(226, 2, [self::ENG_GB]); - - $this->assertValuesInRows( - 'ezcontentobject_attribute_language_code', - [self::ENG_GB], - $res - ); - $this->assertValuesInRows( - 'ezcontentobject_attribute_language_id', - ['4'], - $res - ); - $this->assertCount( - 1, - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::load - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder - */ - public function testLoadNonExistentTranslation() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - $res = $gateway->load(226, 2, ['de-DE']); - - $this->assertCount( - 0, - $res - ); - } - - /** - * Asserts that $columnKey in $actualRows exactly contains $expectedValues. - * - * @param string $columnKey - * @param string[] $expectedValues - * @param string[][] $actualRows - */ - protected function assertValuesInRows($columnKey, array $expectedValues, array $actualRows) - { - $expectedValues = array_fill_keys( - array_values($expectedValues), - true - ); - - $containedValues = []; - - foreach ($actualRows as $row) { - if (isset($row[$columnKey])) { - $containedValues[$row[$columnKey]] = true; - } - } - - $this->assertEquals( - $expectedValues, - $containedValues - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::getAllLocationIds - */ - public function testGetAllLocationIds() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - - $this->assertEquals( - [228], - $gateway->getAllLocationIds(226) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::getFieldIdsByType - */ - public function testGetFieldIdsByType() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - - $this->assertEquals( - [ - 'ezstring' => [841], - 'ezimage' => [843], - 'ezkeyword' => [844], - ], - $gateway->getFieldIdsByType(149) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::getFieldIdsByType - */ - public function testGetFieldIdsByTypeWithSecondArgument() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - - $this->assertEquals( - [ - 'ezstring' => [4001, 4002], - ], - $gateway->getFieldIdsByType(225, 2) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::deleteRelations - */ - public function testDeleteRelationsTo() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $beforeCount = [ - 'all' => $this->countContentRelations(), - 'from' => $this->countContentRelations(149), - 'to' => $this->countContentRelations(null, 149), - ]; - - $gateway = $this->getDatabaseGateway(); - $gateway->deleteRelations(149); - - $this->assertEquals( - // yes, relates to itself! - [ - 'all' => $beforeCount['all'] - 2, - 'from' => $beforeCount['from'] - 1, - 'to' => $beforeCount['to'] - 2, - ], - [ - 'all' => $this->countContentRelations(), - 'from' => $this->countContentRelations(149), - 'to' => $this->countContentRelations(null, 149), - ] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::deleteRelations - */ - public function testDeleteRelationsFrom() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $beforeCount = [ - 'all' => $this->countContentRelations(), - 'from' => $this->countContentRelations(75), - 'to' => $this->countContentRelations(null, 75), - ]; - - $gateway = $this->getDatabaseGateway(); - $gateway->deleteRelations(75); - - $this->assertEquals( - [ - 'all' => $beforeCount['all'] - 6, - 'from' => $beforeCount['from'] - 6, - 'to' => $beforeCount['to'], - ], - [ - 'all' => $this->countContentRelations(), - 'from' => $this->countContentRelations(75), - 'to' => $this->countContentRelations(null, 75), - ] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::deleteRelations - */ - public function testDeleteRelationsWithSecondArgument() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $beforeCount = [ - 'all' => $this->countContentRelations(), - 'from' => $this->countContentRelations(225), - 'to' => $this->countContentRelations(null, 225), - ]; - - $gateway = $this->getDatabaseGateway(); - $gateway->deleteRelations(225, 2); - - $this->assertEquals( - [ - 'all' => $beforeCount['all'] - 1, - 'from' => $beforeCount['from'] - 1, - 'to' => $beforeCount['to'], - ], - [ - 'all' => $this->countContentRelations(), - 'from' => $this->countContentRelations(225), - 'to' => $this->countContentRelations(null, 225), - ] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::deleteField - * - * @throws \Doctrine\DBAL\DBALException - */ - public function testDeleteField(): void - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $beforeCount = $this->countContentFields(); - - $gateway = $this->getDatabaseGateway(); - $gateway->deleteField(22); - - $this->assertEquals( - $beforeCount - 2, - $this->countContentFields() - ); - - $this->assertQueryResult( - [], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('*') - ->from('ezcontentobject_attribute') - ->where('id=22') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::deleteFields - */ - public function testDeleteFields() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $beforeCount = [ - 'all' => $this->countContentFields(), - 'this' => $this->countContentFields(4), - ]; - - $gateway = $this->getDatabaseGateway(); - $gateway->deleteFields(4); - - $this->assertEquals( - [ - 'all' => $beforeCount['all'] - 2, - 'this' => 0, - ], - [ - 'all' => $this->countContentFields(), - 'this' => $this->countContentFields(4), - ] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::deleteFields - */ - public function testDeleteFieldsWithSecondArgument() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $beforeCount = [ - 'all' => $this->countContentFields(), - 'this' => $this->countContentFields(225), - ]; - - $gateway = $this->getDatabaseGateway(); - $gateway->deleteFields(225, 2); - - $this->assertEquals( - [ - 'all' => $beforeCount['all'] - 2, - 'this' => $beforeCount['this'] - 2, - ], - [ - 'all' => $this->countContentFields(), - 'this' => $this->countContentFields(225), - ] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::deleteVersions - */ - public function testDeleteVersions() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $beforeCount = [ - 'all' => $this->countContentVersions(), - 'this' => $this->countContentVersions(14), - ]; - - $gateway = $this->getDatabaseGateway(); - $gateway->deleteVersions(14); - - $this->assertEquals( - [ - 'all' => $beforeCount['all'] - 2, - 'this' => 0, - ], - [ - 'all' => $this->countContentVersions(), - 'this' => $this->countContentVersions(14), - ] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::deleteVersions - */ - public function testDeleteVersionsWithSecondArgument() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $beforeCount = [ - 'all' => $this->countContentVersions(), - 'this' => $this->countContentVersions(225), - ]; - - $gateway = $this->getDatabaseGateway(); - $gateway->deleteVersions(225, 2); - - $this->assertEquals( - [ - 'all' => $beforeCount['all'] - 1, - 'this' => $beforeCount['this'] - 1, - ], - [ - 'all' => $this->countContentVersions(), - 'this' => $this->countContentVersions(225), - ] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::setName - * - * @throws \Exception - */ - public function testSetName() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - - $gateway->setName(14, 2, 'Hello world!', self::ENG_GB); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [[self::ENG_GB, 2, 14, 4, 'Hello world!', self::ENG_GB]], - $query - ->select( - [ - 'content_translation', - 'content_version', - 'contentobject_id', - 'language_id', - 'name', - 'real_translation', - ] - ) - ->from('ezcontentobject_name') - ->where('contentobject_id = :content_id') - ->andWhere('content_version = :version_no') - ->andWhere('content_translation = :language_code') - ->setParameter('content_id', 14, ParameterType::INTEGER) - ->setParameter('version_no', 2, ParameterType::INTEGER) - ->setParameter('language_code', self::ENG_GB, ParameterType::STRING) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::deleteNames - */ - public function testDeleteNames() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $beforeCount = [ - 'all' => $this->countContentNames(), - 'this' => $this->countContentNames(14), - ]; - - $gateway = $this->getDatabaseGateway(); - $gateway->deleteNames(14); - - $this->assertEquals( - [ - 'all' => $beforeCount['all'] - 2, - 'this' => 0, - ], - [ - 'all' => $this->countContentNames(), - 'this' => $this->countContentNames(14), - ] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::deleteNames - */ - public function testDeleteNamesWithSecondArgument() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $beforeCount = [ - 'all' => $this->countContentNames(), - 'this' => $this->countContentNames(225), - ]; - - $gateway = $this->getDatabaseGateway(); - $gateway->deleteNames(225, 2); - - $this->assertEquals( - [ - 'all' => $beforeCount['all'] - 1, - 'this' => $beforeCount['this'] - 1, - ], - [ - 'all' => $this->countContentNames(), - 'this' => $this->countContentNames(225), - ] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::deleteContent - */ - public function testDeleteContent() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $beforeCount = $this->countContent(); - - $gateway = $this->getDatabaseGateway(); - $gateway->deleteContent(14); - - $this->assertEquals( - [ - 'all' => $beforeCount - 1, - 'this' => 0, - ], - [ - 'all' => $this->countContent(), - 'this' => $this->countContent(14), - ] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::loadRelations - * - * @throws \Doctrine\DBAL\DBALException - */ - public function testLoadRelations(): void - { - $this->insertRelationFixture(); - - $gateway = $this->getDatabaseGateway(); - - $relations = $gateway->loadRelations(57); - - $this->assertCount(3, $relations); - - $this->assertValuesInRows( - 'ezcontentobject_link_to_contentobject_id', - [58, 59, 60], - $relations - ); - - $this->assertValuesInRows( - 'ezcontentobject_link_from_contentobject_id', - [57], - $relations - ); - $this->assertValuesInRows( - 'ezcontentobject_link_from_contentobject_version', - [2], - $relations - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::loadRelations - */ - public function testLoadRelationsByType() - { - $this->insertRelationFixture(); - - $gateway = $this->getDatabaseGateway(); - - $relations = $gateway->loadRelations(57, null, \eZ\Publish\API\Repository\Values\Content\Relation::COMMON); - - $this->assertCount(1, $relations, 'Expecting one relation to be loaded'); - - $this->assertValuesInRows( - 'ezcontentobject_link_relation_type', - [\eZ\Publish\API\Repository\Values\Content\Relation::COMMON], - $relations - ); - - $this->assertValuesInRows( - 'ezcontentobject_link_to_contentobject_id', - [58], - $relations - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::loadRelations - */ - public function testLoadRelationsByVersion() - { - $this->insertRelationFixture(); - - $gateway = $this->getDatabaseGateway(); - - $relations = $gateway->loadRelations(57, 1); - - $this->assertCount(1, $relations, 'Expecting one relation to be loaded'); - - $this->assertValuesInRows( - 'ezcontentobject_link_to_contentobject_id', - [58], - $relations - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::loadRelations - */ - public function testLoadRelationsNoResult() - { - $this->insertRelationFixture(); - - $gateway = $this->getDatabaseGateway(); - - $relations = $gateway->loadRelations(57, 1, \eZ\Publish\API\Repository\Values\Content\Relation::EMBED); - - $this->assertCount(0, $relations, 'Expecting no relation to be loaded'); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::loadReverseRelations - */ - public function testLoadReverseRelations() - { - $this->insertRelationFixture(); - - $gateway = $this->getDatabaseGateway(); - - $relations = $gateway->loadReverseRelations(58); - - self::assertCount(2, $relations); - - $this->assertValuesInRows( - 'ezcontentobject_link_from_contentobject_id', - [57, 61], - $relations - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::loadReverseRelations - */ - public function testLoadReverseRelationsWithType() - { - $this->insertRelationFixture(); - - $gateway = $this->getDatabaseGateway(); - - $relations = $gateway->loadReverseRelations(58, \eZ\Publish\API\Repository\Values\Content\Relation::COMMON); - - self::assertCount(1, $relations); - - $this->assertValuesInRows( - 'ezcontentobject_link_from_contentobject_id', - [57], - $relations - ); - - $this->assertValuesInRows( - 'ezcontentobject_link_relation_type', - [\eZ\Publish\API\Repository\Values\Content\Relation::COMMON], - $relations - ); - } - - /** - * Inserts the relation database fixture from relation_data.php. - */ - protected function insertRelationFixture() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/relations_data.php' - ); - } - - /* - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::getLastVersionNumber - * - * @return void - */ - public function testGetLastVersionNumber() - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - - $this->assertEquals( - 1, - $gateway->getLastVersionNumber(4) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::insertRelation - */ - public function testInsertRelation() - { - $struct = $this->getRelationCreateStructFixture(); - $gateway = $this->getDatabaseGateway(); - $gateway->insertRelation($struct); - - $this->assertQueryResult( - [ - [ - 'id' => 1, - 'from_contentobject_id' => $struct->sourceContentId, - 'from_contentobject_version' => $struct->sourceContentVersionNo, - 'contentclassattribute_id' => $struct->sourceFieldDefinitionId, - 'to_contentobject_id' => $struct->destinationContentId, - 'relation_type' => $struct->type, - ], - ], - $this->getDatabaseConnection() - ->createQueryBuilder() - ->select( - [ - 'id', - 'from_contentobject_id', - 'from_contentobject_version', - 'contentclassattribute_id', - 'to_contentobject_id', - 'relation_type', - ] - )->from('ezcontentobject_link') - ->where('id = 1') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::deleteRelation - */ - public function testDeleteRelation() - { - $this->insertRelationFixture(); - - self::assertEquals(4, $this->countContentRelations(57)); - - $gateway = $this->getDatabaseGateway(); - $gateway->deleteRelation(2, RelationValue::COMMON); - - self::assertEquals(3, $this->countContentRelations(57)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::deleteRelation - * - * @throws \Doctrine\DBAL\DBALException - */ - public function testDeleteRelationWithCompositeBitmask(): void - { - $this->insertRelationFixture(); - - $gateway = $this->getDatabaseGateway(); - $gateway->deleteRelation(11, RelationValue::COMMON); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [['relation_type' => RelationValue::LINK]], - $query - ->select(['relation_type']) - ->from('ezcontentobject_link') - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter(11, ParameterType::INTEGER) - ) - ) - ); - } - - /** - * Test for the updateAlwaysAvailableFlag() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::updateAlwaysAvailableFlag - * - * @throws \Doctrine\DBAL\DBALException - */ - public function testUpdateAlwaysAvailableFlagRemove(): void - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - $gateway->updateAlwaysAvailableFlag(103, false); - - $connection = $this->getDatabaseConnection(); - $query = $connection->createQueryBuilder(); - $this->assertQueryResult( - [['id' => 2]], - $query - ->select(['language_mask']) - ->from('ezcontentobject') - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter(103, ParameterType::INTEGER) - ) - ) - ); - - $query = $connection->createQueryBuilder(); - $this->assertQueryResult( - [['language_id' => 2]], - $query - ->select( - ['language_id'] - )->from( - 'ezcontentobject_name' - )->where( - $query->expr()->andX( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter(103, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'content_version', - $query->createPositionalParameter(1, ParameterType::INTEGER) - ) - ) - ) - ); - - $query = $connection->createQueryBuilder(); - $this->assertQueryResult( - [ - ['language_id' => 2], - ], - $query - ->select('DISTINCT language_id') - ->from('ezcontentobject_attribute') - ->where( - $query->expr()->andX( - $query->expr()->eq('contentobject_id', 103), - $query->expr()->eq('version', 1) - ) - ) - ); - } - - /** - * Test for the updateAlwaysAvailableFlag() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::updateAlwaysAvailableFlag - * - * @throws \Doctrine\DBAL\DBALException - */ - public function testUpdateAlwaysAvailableFlagAdd(): void - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - $contentId = 102; - $gateway->updateAlwaysAvailableFlag($contentId, true); - - $connection = $this->getDatabaseConnection(); - $expectedLanguageId = 3; - $this->assertQueryResult( - [['id' => $expectedLanguageId]], - $connection->createQueryBuilder() - ->select(['language_mask']) - ->from('ezcontentobject') - ->where('id = 102') - ); - - $versionNo = 1; - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - ['language_id' => $expectedLanguageId], - ], - $query - ->select('language_id') - ->from('ezcontentobject_name') - ->where( - $query->expr()->andX( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'content_version', - $query->createPositionalParameter($versionNo, ParameterType::INTEGER) - ) - ) - ) - ); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - ['language_id' => $expectedLanguageId], - ], - $query - ->select('DISTINCT language_id') - ->from('ezcontentobject_attribute') - ->where( - $query->expr()->andX( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'version', - $query->createPositionalParameter($versionNo, ParameterType::INTEGER) - ) - ) - ) - ); - } - - /** - * Test for the updateAlwaysAvailableFlag() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::updateAlwaysAvailableFlag - * - * @throws \Doctrine\DBAL\DBALException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function testUpdateContentAddAlwaysAvailableFlagMultilingual(): void - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects_multilingual.php' - ); - - $gateway = $this->getDatabaseGateway(); - $contentMetadataUpdateStruct = new MetadataUpdateStruct( - [ - 'mainLanguageId' => 4, - 'alwaysAvailable' => true, - ] - ); - $gateway->updateContent(4, $contentMetadataUpdateStruct); - - $this->assertQueryResult( - [['id' => 7]], - $this->getDatabaseConnection()->createQueryBuilder()->select( - ['language_mask'] - )->from( - 'ezcontentobject' - )->where( - 'id = 4' - ) - ); - - $this->assertContentVersionAttributesLanguages( - 4, - 2, - [ - ['id' => '7', 'language_id' => 2], - ['id' => '8', 'language_id' => 5], - ] - ); - - $this->assertContentVersionAttributesLanguages( - 4, - 1, - [ - ['id' => '7', 'language_id' => 2], - ['id' => '8', 'language_id' => 5], - ] - ); - } - - /** - * Test for the updateAlwaysAvailableFlag() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::updateAlwaysAvailableFlag - * - * @throws \Doctrine\DBAL\DBALException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function testUpdateContentRemoveAlwaysAvailableFlagMultilingual(): void - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects_multilingual.php' - ); - - $gateway = $this->getDatabaseGateway(); - $contentMetadataUpdateStruct = new MetadataUpdateStruct( - [ - 'mainLanguageId' => 4, - 'alwaysAvailable' => false, - ] - ); - $gateway->updateContent(4, $contentMetadataUpdateStruct); - - $this->assertQueryResult( - [['id' => 6]], - $this->getDatabaseConnection()->createQueryBuilder()->select( - ['language_mask'] - )->from( - 'ezcontentobject' - )->where( - 'id = 4' - ) - ); - - $this->assertContentVersionAttributesLanguages( - 4, - 2, - [ - ['id' => '7', 'language_id' => 2], - ['id' => '8', 'language_id' => 4], - ] - ); - - $this->assertContentVersionAttributesLanguages( - 4, - 1, - [ - ['id' => '7', 'language_id' => 2], - ['id' => '8', 'language_id' => 5], - ] - ); - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function testLoadVersionInfo(): void - { - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - - $resFirst = $gateway->loadVersionInfo(11, 1); - $resSecond = $gateway->loadVersionInfo(11, 2); - - $res = array_merge($resFirst, $resSecond); - - $orig = include __DIR__ . '/../_fixtures/extract_version_info_from_rows_multiple_versions.php'; - - $this->assertEquals($orig, $res, 'Fixtures differ between what was previously stored(expected) and what it now generates(actual), this hints either some mistake in impl or that the fixture (../_fixtures/extract_content_from_rows_multiple_versions.php) and tests needs to be adapted.'); - } - - /** - * Counts the number of relations in the database. - * - * @param int $fromId - * @param int $toId - * - * @return int - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function countContentRelations(?int $fromId = null, ?int $toId = null): int - { - $connection = $this->getDatabaseConnection(); - $dbPlatform = $connection->getDatabasePlatform(); - $query = $connection->createQueryBuilder(); - $query - ->select($dbPlatform->getCountExpression('id')) - ->from('ezcontentobject_link'); - - if ($fromId !== null) { - $query->where( - $query->expr()->eq( - 'from_contentobject_id', - $query->createPositionalParameter($fromId) - ) - ); - } - if ($toId !== null) { - $query->andWhere( - $query->expr()->eq( - 'to_contentobject_id', - $query->createPositionalParameter($toId) - ) - ); - } - - $statement = $query->execute(); - - return (int)$statement->fetchColumn(); - } - - /** - * Counts the number of fields. - * - * @param int $contentId - * - * @return int - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function countContentFields(?int $contentId = null): int - { - $connection = $this->getDatabaseConnection(); - $dbPlatform = $connection->getDatabasePlatform(); - - $query = $connection->createQueryBuilder(); - $query - ->select($dbPlatform->getCountExpression('id')) - ->from('ezcontentobject_attribute'); - - if ($contentId !== null) { - $query->where( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ); - } - - $statement = $query->execute(); - - return (int)$statement->fetchColumn(); - } - - /** - * Counts the number of versions. - * - * @param int $contentId - * - * @return int - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function countContentVersions(?int $contentId = null): int - { - $connection = $this->getDatabaseConnection(); - $dbPlatform = $connection->getDatabasePlatform(); - - $query = $connection->createQueryBuilder(); - $query - ->select($dbPlatform->getCountExpression('id')) - ->from('ezcontentobject_version'); - - if ($contentId !== null) { - $query->where( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ); - } - - $statement = $query->execute(); - - return (int)$statement->fetchColumn(); - } - - /** - * Counts the number of content names. - * - * @param int $contentId - * - * @return int - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function countContentNames(?int $contentId = null): int - { - $connection = $this->getDatabaseConnection(); - $dbPlatform = $connection->getDatabasePlatform(); - - $query = $connection->createQueryBuilder(); - $query - ->select($dbPlatform->getCountExpression('contentobject_id')) - ->from('ezcontentobject_name'); - - if ($contentId !== null) { - $query->where( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ); - } - - $statement = $query->execute(); - - return (int)$statement->fetchColumn(); - } - - /** - * Counts the number of content objects. - * - * @param int|null $contentId - * - * @return int - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function countContent(?int $contentId = null): int - { - $connection = $this->getDatabaseConnection(); - $dbPlatform = $connection->getDatabasePlatform(); - $query = $connection->createQueryBuilder(); - $query - ->select($dbPlatform->getCountExpression('id')) - ->from('ezcontentobject'); - - if ($contentId !== null) { - $query->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ); - } - - $statement = $query->execute(); - - return (int)$statement->fetchColumn(); - } - - /** - * Stores $fixture in $file to be required as a fixture. - * - * @param string $file - * @param mixed $fixture - */ - protected function storeFixture($file, $fixture) - { - file_put_contents( - $file, - "<?php\n\nreturn " . str_replace(" \n", "\n", var_export($fixture, true)) . ";\n" - ); - } - - /** - * Returns a Field fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Field - */ - protected function getFieldFixture() - { - $field = new Field(); - - $field->fieldDefinitionId = 231; - $field->type = 'ezstring'; - $field->languageCode = self::ENG_GB; - $field->versionNo = 1; - - return $field; - } - - /** - * Returns a Field fixture in a different language. - * - * @return \eZ\Publish\SPI\Persistence\Content\Field - */ - protected function getOtherLanguageFieldFixture() - { - $field = $this->getFieldFixture(); - $field->languageCode = 'eng-US'; - - return $field; - } - - /** - * Returns a StorageFieldValue fixture. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue - */ - protected function getStorageValueFixture() - { - $value = new StorageFieldValue(); - - $value->dataFloat = 24.42; - $value->dataInt = 42; - $value->dataText = 'Test text'; - $value->sortKeyInt = 23; - $value->sortKeyString = 'Test'; - - return $value; - } - - /** - * Returns a ready to test DoctrineDatabase gateway. - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function getDatabaseGateway(): DoctrineDatabase - { - if (!isset($this->databaseGateway)) { - $connection = $this->getDatabaseConnection(); - $this->databaseGateway = new DoctrineDatabase( - $connection, - $this->getSharedGateway(), - new DoctrineDatabase\QueryBuilder($connection), - $this->getLanguageHandler(), - $this->getLanguageMaskGenerator() - ); - } - - return $this->databaseGateway; - } - - /** - * DoctrineDatabaseTest::getRelationCreateStructFixture(). - * - * @return \eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct - */ - protected function getRelationCreateStructFixture() - { - $struct = new RelationCreateStruct(); - - $struct->destinationContentId = 1; - $struct->sourceContentId = 1; - $struct->sourceContentVersionNo = 1; - $struct->sourceFieldDefinitionId = 0; - $struct->type = RelationValue::COMMON; - - return $struct; - } - - /** - * @param int $contentId - * @param int $versionNo - * @param array $expectation - * - * @throws \Doctrine\DBAL\DBALException - */ - private function assertContentVersionAttributesLanguages( - int $contentId, - int $versionNo, - array $expectation - ): void { - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - $expectation, - $query - ->select('DISTINCT id, language_id') - ->from('ezcontentobject_attribute') - ->where( - $query->expr()->andX( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'version', - $query->createPositionalParameter($versionNo, ParameterType::INTEGER) - ) - ) - ) - ->orderBy('id') - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Gateway/RandomSortClauseHandlerFactoryTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Gateway/RandomSortClauseHandlerFactoryTest.php deleted file mode 100644 index 4c25ff6e50..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Gateway/RandomSortClauseHandlerFactoryTest.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Platforms\AbstractPlatform; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Factory\RandomSortClauseHandlerFactory; -use PHPUnit\Framework\TestCase; - -class RandomSortClauseHandlerFactoryTest extends TestCase -{ - /** - * @dataProvider getGateways - * - * @param \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom[] $gateways - * - * @throws \Doctrine\DBAL\DBALException - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - */ - public function testGetGateway(array $gateways) - { - $platform = $this->createMock(AbstractPlatform::class); - - $platform - ->method('getName') - ->willReturn('testStorage'); - - $connection = $this->createMock(Connection::class); - - $connection - ->method('getDatabasePlatform') - ->willReturn($platform); - - $handlerFactory = new RandomSortClauseHandlerFactory($connection, $gateways); - $this->assertEquals( - 'testStorage', - $handlerFactory->getGateway()->getDriverName() - ); - } - - /** - * @dataProvider getGateways - * - * @param \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom[] $gateways - * - * @throws \Doctrine\DBAL\DBALException - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - */ - public function testGetGatewayNotImplemented(array $gateways) - { - $platform = $this->createMock(AbstractPlatform::class); - - $platform - ->method('getName') - ->willReturn('notImplemented'); - - $connection = $this->createMock(Connection::class); - - $connection - ->method('getDatabasePlatform') - ->willReturn($platform); - - $handlerFactory = new RandomSortClauseHandlerFactory($connection, $gateways); - - $this->expectException(InvalidArgumentException::class); - $handlerFactory->getGateway(); - } - - public function getGateways(): array - { - $goodGateway = $this - ->createMock(AbstractRandom::class); - $goodGateway - ->method('getDriverName') - ->willReturn('testStorage'); - - $badGateway = $this - ->createMock(AbstractRandom::class); - $badGateway - ->method('getDriverName') - ->willReturn('otherStorage'); - - return [ - [ - [ - $goodGateway, - $badGateway, - ], - ], - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index b360929758..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,207 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Language\Gateway; - -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Content\Language; - -/** - * Test case for eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase. - */ -class DoctrineDatabaseTest extends TestCase -{ - /** - * Database gateway to test. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase - */ - protected $databaseGateway; - - /** - * Inserts DB fixture. - */ - protected function setUp(): void - { - parent::setUp(); - - $this->insertDatabaseFixture( - __DIR__ . '/../../_fixtures/languages.php' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase::insertLanguage - */ - public function testInsertLanguage() - { - $gateway = $this->getDatabaseGateway(); - - $gateway->insertLanguage($this->getLanguageFixture()); - - $this->assertQueryResult( - [ - [ - 'id' => '8', - 'locale' => 'de-DE', - 'name' => 'Deutsch (Deutschland)', - 'disabled' => '0', - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('id', 'locale', 'name', 'disabled') - ->from('ezcontent_language') - ->where('id=8') - ); - } - - /** - * Returns a Language fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Language - */ - protected function getLanguageFixture() - { - $language = new Language(); - - $language->languageCode = 'de-DE'; - $language->name = 'Deutsch (Deutschland)'; - $language->isEnabled = true; - - return $language; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase::updateLanguage - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase::setLanguageQueryParameters - */ - public function testUpdateLanguage() - { - $gateway = $this->getDatabaseGateway(); - - $language = $this->getLanguageFixture(); - $language->id = 2; - - $gateway->updateLanguage($language); - - $this->assertQueryResult( - [ - [ - 'id' => '2', - 'locale' => 'de-DE', - 'name' => 'Deutsch (Deutschland)', - 'disabled' => '0', - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('id', 'locale', 'name', 'disabled') - ->from('ezcontent_language') - ->where('id=2') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase::loadLanguageListData - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase::createFindQuery - */ - public function testLoadLanguageListData() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->loadLanguageListData([2]); - - $this->assertEquals( - [ - [ - 'id' => '2', - 'locale' => 'eng-US', - 'name' => 'English (American)', - 'disabled' => '0', - ], - ], - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase::loadAllLanguagesData - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase::createFindQuery - */ - public function testLoadAllLanguagesData() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->loadAllLanguagesData(); - - $this->assertEquals( - [ - [ - 'id' => '2', - 'locale' => 'eng-US', - 'name' => 'English (American)', - 'disabled' => '0', - ], - [ - 'id' => '4', - 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)', - 'disabled' => '0', - ], - ], - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase::deleteLanguage - */ - public function testDeleteLanguage() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->deleteLanguage(2); - - $this->assertQueryResult( - [ - [ - 'count' => '1', - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('COUNT( * ) AS count') - ->from('ezcontent_language') - ); - - $this->assertQueryResult( - [ - [ - 'count' => '0', - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('COUNT( * ) AS count') - ->from('ezcontent_language') - ->where('id=2') - ); - } - - /** - * Return a ready to test DoctrineDatabase gateway. - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function getDatabaseGateway(): DoctrineDatabase - { - if (!isset($this->databaseGateway)) { - $this->databaseGateway = new DoctrineDatabase( - $this->getDatabaseConnection() - ); - } - - return $this->databaseGateway; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/MapperTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/MapperTest.php deleted file mode 100644 index 7061ec2a24..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/MapperTest.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Language; - -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Content\Language; -use eZ\Publish\SPI\Persistence\Content\Language\CreateStruct; - -/** - * Test case for Mapper. - */ -class MapperTest extends TestCase -{ - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Mapper::createLanguageFromCreateStruct - */ - public function testCreateLanguageFromCreateStruct() - { - $mapper = new Mapper(); - - $createStruct = $this->getCreateStructFixture(); - - $result = $mapper->createLanguageFromCreateStruct($createStruct); - - $this->assertStructsEqual( - $this->getLanguageFixture(), - $result, - ['languageCode', 'name', 'isEnabled'] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Mapper::extractLanguagesFromRows - */ - public function testExtractLanguagesFromRows() - { - $mapper = new Mapper(); - - $rows = $this->getRowsFixture(); - - $result = $mapper->extractLanguagesFromRows($rows); - - $this->assertEquals( - $this->getExtractReference(), - $result - ); - } - - /** - * Returns a result rows fixture. - * - * @return string[][] - */ - protected function getRowsFixture() - { - return [ - ['disabled' => '0', 'id' => '2', 'locale' => 'eng-US', 'name' => 'English (American)'], - ['disabled' => '0', 'id' => '4', 'locale' => 'eng-GB', 'name' => 'English (United Kingdom)'], - ]; - } - - /** - * Returns reference for the extraction from rows. - * - * @return \eZ\Publish\SPI\Persistence\Content\Language[] - */ - protected function getExtractReference() - { - $langUs = new Language(); - $langUs->id = 2; - $langUs->languageCode = 'eng-US'; - $langUs->name = 'English (American)'; - $langUs->isEnabled = true; - - $langGb = new Language(); - $langGb->id = 4; - $langGb->languageCode = 'eng-GB'; - $langGb->name = 'English (United Kingdom)'; - $langGb->isEnabled = true; - - return ['eng-US' => $langUs, 'eng-GB' => $langGb]; - } - - /** - * Returns a Language CreateStruct fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Language\CreateStruct - */ - protected function getCreateStructFixture() - { - $struct = new CreateStruct(); - - $struct->languageCode = 'de-DE'; - $struct->name = 'Deutsch (Deutschland)'; - $struct->isEnabled = true; - - return $struct; - } - - /** - * Returns a Language fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Language - */ - protected function getLanguageFixture() - { - $struct = new Language(); - - $struct->languageCode = 'de-DE'; - $struct->name = 'Deutsch (Deutschland)'; - $struct->isEnabled = true; - - return $struct; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index ecca84916d..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,1501 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Location\Gateway; - -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\Tests\Content\LanguageAwareTestCase; -use eZ\Publish\Core\Search\Legacy\Content; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct; - -/** - * Test case for eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase. - */ -class DoctrineDatabaseTest extends LanguageAwareTestCase -{ - protected function getLocationGateway() - { - return new DoctrineDatabase( - $this->getDatabaseConnection(), - $this->getLanguageMaskGenerator(), - $this->getTrashCriteriaConverterDependency(), - $this->getTrashSortClauseConverterDependency() - ); - } - - private static function getLoadLocationValues(): array - { - return [ - 'node_id' => 77, - 'priority' => 0, - 'is_hidden' => 0, - 'is_invisible' => 0, - 'remote_id' => 'dbc2f3c8716c12f32c379dbf0b1cb133', - 'contentobject_id' => 75, - 'parent_node_id' => 2, - 'path_identification_string' => 'solutions', - 'path_string' => '/1/2/77/', - 'modified_subnode' => 1311065017, - 'main_node_id' => 77, - 'depth' => 2, - 'sort_field' => 2, - 'sort_order' => 1, - ]; - } - - private function assertLoadLocationProperties(array $locationData): void - { - foreach (self::getLoadLocationValues() as $field => $expectedValue) { - self::assertEquals( - $expectedValue, - $locationData[$field], - "Value in property $field not as expected." - ); - } - } - - public function testLoadLocationByRemoteId() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $data = $gateway->getBasicNodeDataByRemoteId('dbc2f3c8716c12f32c379dbf0b1cb133'); - - self::assertLoadLocationProperties($data); - } - - public function testLoadLocation() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $data = $gateway->getBasicNodeData(77); - - self::assertLoadLocationProperties($data); - } - - public function testLoadLocationList() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $locationsData = $gateway->getNodeDataList([77]); - - self::assertCount(1, $locationsData); - - $locationRow = reset($locationsData); - - self::assertLoadLocationProperties($locationRow); - } - - public function testLoadInvalidLocation() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->getBasicNodeData(1337); - } - - public function testLoadLocationDataByContent() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - - $gateway = $this->getLocationGateway(); - - $locationsData = $gateway->loadLocationDataByContent(75); - - self::assertCount(1, $locationsData); - - $locationRow = reset($locationsData); - - self::assertLoadLocationProperties($locationRow); - } - - public function testLoadParentLocationDataForDraftContentAll() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - - $gateway = $this->getLocationGateway(); - - $locationsData = $gateway->loadParentLocationsDataForDraftContent(226); - - $this->assertCount(1, $locationsData); - - $locationRow = reset($locationsData); - - self::assertLoadLocationProperties($locationRow); - } - - public function testLoadLocationDataByContentLimitSubtree() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - - $gateway = $this->getLocationGateway(); - - $locationsData = $gateway->loadLocationDataByContent(75, 3); - - $this->assertCount(0, $locationsData); - } - - public function testMoveSubtreePathUpdate() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->moveSubtreeNodes( - [ - 'path_string' => '/1/2/69/', - 'contentobject_id' => 67, - 'path_identification_string' => 'products', - 'is_hidden' => 0, - 'is_invisible' => 0, - ], - [ - 'path_string' => '/1/2/77/', - 'path_identification_string' => 'solutions', - 'is_hidden' => 0, - 'is_invisible' => 0, - ] - ); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - [65, '/1/2/', '', 1, 1, 0, 0], - [67, '/1/2/77/69/', 'solutions/products', 77, 3, 0, 0], - [69, '/1/2/77/69/70/71/', 'solutions/products/software/os_type_i', 70, 5, 0, 0], - [73, '/1/2/77/69/72/75/', 'solutions/products/boxes/cd_dvd_box_iii', 72, 5, 0, 0], - [75, '/1/2/77/', 'solutions', 2, 2, 0, 0], - ], - $query - ->select( - 'contentobject_id', - 'path_string', - 'path_identification_string', - 'parent_node_id', - 'depth', - 'is_hidden', - 'is_invisible' - ) - ->from('ezcontentobject_tree') - ->where($query->expr()->in('node_id', [69, 71, 75, 77, 2])) - ->orderBy('contentobject_id') - ); - } - - public function testMoveHiddenDestinationUpdate() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->hideSubtree('/1/2/77/'); - $gateway->moveSubtreeNodes( - [ - 'path_string' => '/1/2/69/', - 'contentobject_id' => 67, - 'path_identification_string' => 'products', - 'is_hidden' => 0, - 'is_invisible' => 0, - ], - [ - 'path_string' => '/1/2/77/', - 'path_identification_string' => 'solutions', - 'is_hidden' => 1, - 'is_invisible' => 1, - ] - ); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - [65, '/1/2/', '', 1, 1, 0, 0], - [67, '/1/2/77/69/', 'solutions/products', 77, 3, 0, 1], - [69, '/1/2/77/69/70/71/', 'solutions/products/software/os_type_i', 70, 5, 0, 1], - [73, '/1/2/77/69/72/75/', 'solutions/products/boxes/cd_dvd_box_iii', 72, 5, 0, 1], - [75, '/1/2/77/', 'solutions', 2, 2, 1, 1], - ], - $query - ->select( - 'contentobject_id', - 'path_string', - 'path_identification_string', - 'parent_node_id', - 'depth', - 'is_hidden', - 'is_invisible' - ) - ->from('ezcontentobject_tree') - ->where($query->expr()->in('node_id', [69, 71, 75, 77, 2])) - ->orderBy('contentobject_id') - ); - } - - public function testMoveHiddenSourceUpdate() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->hideSubtree('/1/2/69/'); - $gateway->moveSubtreeNodes( - [ - 'path_string' => '/1/2/69/', - 'contentobject_id' => 67, - 'path_identification_string' => 'products', - 'is_hidden' => 1, - 'is_invisible' => 1, - ], - [ - 'path_string' => '/1/2/77/', - 'path_identification_string' => 'solutions', - 'is_hidden' => 0, - 'is_invisible' => 0, - ] - ); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - [65, '/1/2/', '', 1, 1, 0, 0], - [67, '/1/2/77/69/', 'solutions/products', 77, 3, 1, 1], - [69, '/1/2/77/69/70/71/', 'solutions/products/software/os_type_i', 70, 5, 0, 1], - [73, '/1/2/77/69/72/75/', 'solutions/products/boxes/cd_dvd_box_iii', 72, 5, 0, 1], - [75, '/1/2/77/', 'solutions', 2, 2, 0, 0], - ], - $query - ->select( - 'contentobject_id', - 'path_string', - 'path_identification_string', - 'parent_node_id', - 'depth', - 'is_hidden', - 'is_invisible' - ) - ->from('ezcontentobject_tree') - ->where($query->expr()->in('node_id', [69, 71, 75, 77, 2])) - ->orderBy('contentobject_id') - ); - } - - public function testMoveHiddenSourceChildUpdate() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->hideSubtree('/1/2/69/70/'); - - $gateway->moveSubtreeNodes( - [ - 'path_string' => '/1/2/69/', - 'contentobject_id' => 67, - 'path_identification_string' => 'products', - 'is_hidden' => 0, - 'is_invisible' => 0, - ], - [ - 'path_string' => '/1/2/77/', - 'path_identification_string' => 'solutions', - 'is_hidden' => 0, - 'is_invisible' => 0, - ] - ); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - [65, '/1/2/', '', 1, 1, 0, 0], - [67, '/1/2/77/69/', 'solutions/products', 77, 3, 0, 0], - [68, '/1/2/77/69/70/', 'solutions/products/software', 69, 4, 1, 1], - [69, '/1/2/77/69/70/71/', 'solutions/products/software/os_type_i', 70, 5, 0, 1], - [73, '/1/2/77/69/72/75/', 'solutions/products/boxes/cd_dvd_box_iii', 72, 5, 0, 0], - [75, '/1/2/77/', 'solutions', 2, 2, 0, 0], - ], - $query - ->select( - 'contentobject_id', - 'path_string', - 'path_identification_string', - 'parent_node_id', - 'depth', - 'is_hidden', - 'is_invisible' - ) - ->from('ezcontentobject_tree') - ->where($query->expr()->in('node_id', [69, 70, 71, 75, 77, 2])) - ->orderBy('contentobject_id') - ); - } - - /** - * @throws \Exception - */ - public function testMoveSubtreeAssignmentUpdate() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->updateNodeAssignment(67, 2, 77, 5); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - [67, 1, 0, 53, 1, 5, 77, '9cec85d730eec7578190ee95ce5a36f5', 0, 2, 1, 0, 0], - ], - $query - ->select( - [ - 'contentobject_id', - 'contentobject_version', - 'from_node_id', - 'id', - 'is_main', - 'op_code', - 'parent_node', - 'parent_remote_id', - 'remote_id', - 'sort_field', - 'sort_order', - 'priority', - 'is_hidden', - ] - ) - ->from('eznode_assignment') - ->where($query->expr()->eq('contentobject_id', 67)) - ); - } - - public function testUpdateSubtreeModificationTime() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $time = time(); - $gateway->updateSubtreeModificationTime('/1/2/69/'); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - ['/1/'], - ['/1/2/'], - ['/1/2/69/'], - ], - $query - ->select('path_string') - ->from('ezcontentobject_tree') - ->where($query->expr()->gte('modified_subnode', $time)) - ->orderBy('path_string') - ); - } - - public function testHideUpdateHidden() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->hideSubtree('/1/2/69/'); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - [1, 0, 0], - [2, 0, 0], - [69, 1, 1], - [75, 0, 1], - ], - $query - ->select('node_id', 'is_hidden', 'is_invisible') - ->from('ezcontentobject_tree') - ->where($query->expr()->in('node_id', [1, 2, 69, 75])) - ->orderBy('node_id') - ); - } - - /** - * @depends testHideUpdateHidden - */ - public function testHideUnhideUpdateHidden() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->hideSubtree('/1/2/69/'); - $gateway->unhideSubtree('/1/2/69/'); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - [1, 0, 0], - [2, 0, 0], - [69, 0, 0], - [75, 0, 0], - ], - $query - ->select('node_id', 'is_hidden', 'is_invisible') - ->from('ezcontentobject_tree') - ->where($query->expr()->in('node_id', [1, 2, 69, 75])) - ->orderBy('node_id') - ); - } - - /** - * @depends testHideUpdateHidden - */ - public function testHideUnhideParentTree() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->hideSubtree('/1/2/69/'); - $gateway->hideSubtree('/1/2/69/70/'); - $gateway->unhideSubtree('/1/2/69/'); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - [1, 0, 0], - [2, 0, 0], - [69, 0, 0], - [70, 1, 1], - [71, 0, 1], - [75, 0, 0], - ], - $query - ->select('node_id', 'is_hidden', 'is_invisible') - ->from('ezcontentobject_tree') - ->where($query->expr()->in('node_id', [1, 2, 69, 70, 71, 75])) - ->orderBy('node_id') - ); - } - - /** - * @depends testHideUpdateHidden - */ - public function testHideUnhidePartialSubtree() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->hideSubtree('/1/2/69/'); - $gateway->hideSubtree('/1/2/69/70/'); - $gateway->unhideSubtree('/1/2/69/70/'); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - [1, 0, 0], - [2, 0, 0], - [69, 1, 1], - [70, 0, 1], - [71, 0, 1], - [75, 0, 1], - ], - $query - ->select('node_id', 'is_hidden', 'is_invisible') - ->from('ezcontentobject_tree') - ->where($query->expr()->in('node_id', [1, 2, 69, 70, 71, 75])) - ->orderBy('node_id') - ); - } - - public function testSwapLocations() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->swap(70, 78); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - [70, 76], - [78, 68], - ], - $query - ->select('node_id', 'contentobject_id') - ->from('ezcontentobject_tree') - ->where($query->expr()->in('node_id', [70, 78])) - ->orderBy('node_id') - ); - } - - public function testCreateLocation() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->create( - new CreateStruct( - [ - 'contentId' => 68, - 'remoteId' => 'some_id', - ] - ), - [ - 'node_id' => '77', - 'depth' => '2', - 'path_string' => '/1/2/77/', - ] - ); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - [70, '/1/2/69/70/'], - [77, '/1/2/77/'], - [228, '/1/2/77/228/'], - ], - $query - ->select('node_id', 'path_string') - ->from('ezcontentobject_tree') - ->where($query->expr()->in('contentobject_id', [68, 75])) - ->orderBy('node_id') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::getMainNodeId - * @depends testCreateLocation - */ - public function testGetMainNodeId() - { - $gateway = $this->getLocationGateway(); - - $parentLocationData = [ - 'node_id' => '77', - 'depth' => '2', - 'path_string' => '/1/2/77/', - ]; - - // main location - $mainLocation = $gateway->create( - new CreateStruct( - [ - 'contentId' => 68, - 'contentVersion' => 1, - 'remoteId' => 'some_id', - 'mainLocationId' => true, - ] - ), - $parentLocationData - ); - - // secondary location - $gateway->create( - new CreateStruct( - [ - 'contentId' => 68, - 'contentVersion' => 1, - 'remoteId' => 'some_id', - 'mainLocationId' => $mainLocation->id, - ] - ), - $parentLocationData - ); - - $gatewayReflection = new \ReflectionObject($gateway); - $methodReflection = $gatewayReflection->getMethod('getMainNodeId'); - $methodReflection->setAccessible(true); - self::assertEquals($mainLocation->id, $res = $methodReflection->invoke($gateway, 68)); - } - - public static function getCreateLocationValues() - { - return [ - ['contentobject_id', 68], - ['contentobject_is_published', 1], - ['contentobject_version', 1], - ['depth', 3], - ['is_hidden', 0], - ['is_invisible', 0], - ['main_node_id', 42], - ['parent_node_id', 77], - ['path_identification_string', ''], - ['priority', 1], - ['remote_id', 'some_id'], - ['sort_field', 1], - ['sort_order', 1], - ]; - } - - /** - * @depends testCreateLocation - * @dataProvider getCreateLocationValues - */ - public function testCreateLocationValues($field, $value) - { - if ($value === null) { - $this->markTestIncomplete('Proper value setting yet unknown.'); - } - - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->create( - new CreateStruct( - [ - 'contentId' => 68, - 'contentVersion' => 1, - 'mainLocationId' => 42, - 'priority' => 1, - 'remoteId' => 'some_id', - 'sortField' => 1, - 'sortOrder' => 1, - ] - ), - [ - 'node_id' => '77', - 'depth' => '2', - 'path_string' => '/1/2/77/', - ] - ); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [[$value]], - $query - ->select($field) - ->from('ezcontentobject_tree') - ->where($query->expr()->eq('node_id', 228)) - ); - } - - public static function getCreateLocationReturnValues() - { - return [ - ['id', 228], - ['priority', 1], - ['hidden', false], - ['invisible', false], - ['remoteId', 'some_id'], - ['contentId', '68'], - ['parentId', '77'], - ['pathIdentificationString', ''], - ['pathString', '/1/2/77/228/'], - ['depth', 3], - ['sortField', 1], - ['sortOrder', 1], - ]; - } - - /** - * @depends testCreateLocation - * @dataProvider getCreateLocationReturnValues - */ - public function testCreateLocationReturnValues($field, $value) - { - if ($value === null) { - $this->markTestIncomplete('Proper value setting yet unknown.'); - } - - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $location = $gateway->create( - new CreateStruct( - [ - 'contentId' => 68, - 'contentVersion' => 1, - 'mainLocationId' => true, - 'priority' => 1, - 'remoteId' => 'some_id', - 'sortField' => 1, - 'sortOrder' => 1, - ] - ), - [ - 'node_id' => '77', - 'depth' => '2', - 'path_string' => '/1/2/77/', - ] - ); - - $this->assertTrue($location instanceof Location); - $this->assertEquals($value, $location->$field); - } - - public static function getUpdateLocationData() - { - return [ - ['priority', 23], - ['remote_id', 'someNewHash'], - ['sort_field', 4], - ['sort_order', 4], - ]; - } - - /** - * @dataProvider getUpdateLocationData - */ - public function testUpdateLocation($field, $value) - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->update( - new Location\UpdateStruct( - [ - 'priority' => 23, - 'remoteId' => 'someNewHash', - 'sortField' => 4, - 'sortOrder' => 4, - ] - ), - 70 - ); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [[$value]], - $query - ->select($field) - ->from('ezcontentobject_tree') - ->where($query->expr()->in('node_id', [70])) - ); - } - - public static function getNodeAssignmentValues() - { - return [ - ['contentobject_version', [1]], - ['from_node_id', [0]], - ['id', [215]], - ['is_main', [0]], - ['op_code', [3]], - ['parent_node', [77]], - ['parent_remote_id', ['some_id']], - ['remote_id', ['0']], - ['sort_field', [2]], - ['sort_order', [0]], - ['is_main', [0]], - ['priority', [1]], - ['is_hidden', [1]], - ]; - } - - private function buildGenericNodeSelectContentWithParentQuery( - int $contentId, - int $parentLocationId, - string $nodeTable, - string $parentNodeIdColumnName, - array $fields - ): QueryBuilder { - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select($fields) - ->from($nodeTable) - ->where( - $expr->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - $parentNodeIdColumnName, - $query->createPositionalParameter($parentLocationId, ParameterType::INTEGER) - ) - ); - - return $query; - } - - private function buildNodeAssignmentSelectContentWithParentQuery( - int $contentId, - int $parentLocationId, - array $fields - ): QueryBuilder { - return $this->buildGenericNodeSelectContentWithParentQuery( - $contentId, - $parentLocationId, - 'eznode_assignment', - 'parent_node', - $fields - ); - } - - private function buildContentTreeSelectContentWithParentQuery( - int $contentId, - int $parentLocationId, - array $fields - ): QueryBuilder { - return $this->buildGenericNodeSelectContentWithParentQuery( - $contentId, - $parentLocationId, - Gateway::CONTENT_TREE_TABLE, - 'parent_node_id', - $fields - ); - } - - /** - * @depends testCreateLocation - * @dataProvider getNodeAssignmentValues - * - * @param string $field - * @param array $expectedResult - */ - public function testCreateLocationNodeAssignmentCreation(string $field, array $expectedResult) - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->createNodeAssignment( - new CreateStruct( - [ - 'contentId' => 68, - 'contentVersion' => 1, - 'mainLocationId' => 1, - 'priority' => 1, - 'remoteId' => 'some_id', - 'sortField' => 2, - 'sortOrder' => 0, - 'hidden' => 1, - ] - ), - 77, - DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE - ); - - $this->assertQueryResult( - [$expectedResult], - $this->buildNodeAssignmentSelectContentWithParentQuery(68, 77, [$field]) - ); - } - - /** - * @depends testCreateLocation - */ - public function testCreateLocationNodeAssignmentCreationMainLocation() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - $gateway->createNodeAssignment( - new CreateStruct( - [ - 'contentId' => 68, - 'contentVersion' => 1, - 'mainLocationId' => true, - 'priority' => 1, - 'remoteId' => 'some_id', - 'sortField' => 1, - 'sortOrder' => 1, - ] - ), - '77', - DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE - ); - - $this->assertQueryResult( - [[1]], - $this->buildNodeAssignmentSelectContentWithParentQuery(68, 77, ['is_main']) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::updateLocationsContentVersionNo - */ - public function testUpdateLocationsContentVersionNo() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - - $gateway->create( - new CreateStruct( - [ - 'contentId' => 4096, - 'remoteId' => 'some_id', - 'contentVersion' => 1, - ] - ), - [ - 'node_id' => '77', - 'depth' => '2', - 'path_string' => '/1/2/77/', - ] - ); - - $gateway->updateLocationsContentVersionNo(4096, 2); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [ - [2], - ], - $query->select( - 'contentobject_version' - )->from( - 'ezcontentobject_tree' - )->where( - $query->expr()->eq( - 'contentobject_id', - 4096 - ) - ) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::deleteNodeAssignment - */ - public function testDeleteNodeAssignment() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - - $gateway->deleteNodeAssignment(11); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [[0]], - $query - ->select('count(*)') - ->from('eznode_assignment') - ->where( - $query->expr()->eq('contentobject_id', 11) - ) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::deleteNodeAssignment - */ - public function testDeleteNodeAssignmentWithSecondArgument() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - $gateway = $this->getLocationGateway(); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $query - ->select('count(*)') - ->from('eznode_assignment') - ->where( - $query->expr()->eq('contentobject_id', 11) - ); - $statement = $query->execute(); - $nodeAssignmentsCount = (int)$statement->fetchColumn(); - - $gateway->deleteNodeAssignment(11, 1); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [[$nodeAssignmentsCount - 1]], - $query - ->select('count(*)') - ->from('eznode_assignment') - ->where( - $query->expr()->eq('contentobject_id', 11) - ) - ); - } - - public static function getConvertNodeAssignmentsLocationValues() - { - return [ - ['contentobject_id', '68'], - ['contentobject_is_published', '1'], - ['contentobject_version', '1'], - ['depth', '3'], - ['is_hidden', '1'], - ['is_invisible', '1'], - ['main_node_id', '70'], - ['modified_subnode', time()], - ['node_id', '228'], - ['parent_node_id', '77'], - ['path_identification_string', null], - ['path_string', '/1/2/77/228/'], - ['priority', '101'], - ['remote_id', 'some_id'], - ['sort_field', '1'], - ['sort_order', '1'], - ]; - } - - /** - * @depends testCreateLocationNodeAssignmentCreation - * @dataProvider getConvertNodeAssignmentsLocationValues - */ - public function testConvertNodeAssignments($field, $value) - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - - $gateway = $this->getLocationGateway(); - $gateway->createNodeAssignment( - new CreateStruct( - [ - 'contentId' => 68, - 'contentVersion' => 1, - 'mainLocationId' => false, - 'priority' => 101, - 'remoteId' => 'some_id', - 'sortField' => 1, - 'sortOrder' => 1, - 'hidden' => true, - // Note: not stored in node assignment, will be calculated from parent - // visibility upon Location creation from node assignment - 'invisible' => false, - ] - ), - '77', - DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE - ); - - $gateway->createLocationsFromNodeAssignments(68, 1); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select($field) - ->from(Gateway::CONTENT_TREE_TABLE) - ->where( - $expr->eq( - 'contentobject_id', - $query->createPositionalParameter(68, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - 'parent_node_id', - $query->createPositionalParameter(77, ParameterType::INTEGER) - ) - ); - - if ($field === 'modified_subnode') { - $statement = $query->execute(); - $result = $statement->fetch(FetchMode::ASSOCIATIVE); - $this->assertGreaterThanOrEqual($value, $result); - } else { - $this->assertQueryResult( - [[$value]], - $query - ); - } - } - - /** - * @depends testCreateLocationNodeAssignmentCreation - */ - public function testConvertNodeAssignmentsMainLocation() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - - $gateway = $this->getLocationGateway(); - $gateway->createNodeAssignment( - new CreateStruct( - [ - 'contentId' => 68, - 'contentVersion' => 1, - 'mainLocationId' => true, - 'priority' => 101, - 'remoteId' => 'some_id', - 'sortField' => 1, - 'sortOrder' => 1, - 'hidden' => true, - // Note: not stored in node assignment, will be calculated from parent - // visibility upon Location creation from node assignment - 'invisible' => false, - ] - ), - 77, - DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE - ); - - $gateway->createLocationsFromNodeAssignments(68, 1); - - $this->assertQueryResult( - [[228]], - $this->buildContentTreeSelectContentWithParentQuery(68, 77, ['main_node_id']) - ); - } - - /** - * @depends testCreateLocationNodeAssignmentCreation - */ - public function testConvertNodeAssignmentsParentHidden() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - - $gateway = $this->getLocationGateway(); - $gateway->createNodeAssignment( - new CreateStruct( - [ - 'contentId' => 68, - 'contentVersion' => 1, - 'mainLocationId' => true, - 'priority' => 101, - 'remoteId' => 'some_id', - 'sortField' => 1, - 'sortOrder' => 1, - 'hidden' => false, - // Note: not stored in node assignment, will be calculated from parent - // visibility upon Location creation from node assignment - 'invisible' => false, - ] - ), - 224, - DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE - ); - - $gateway->createLocationsFromNodeAssignments(68, 1); - - $this->assertQueryResult( - [[0, 1]], - $this->buildContentTreeSelectContentWithParentQuery( - 68, - 224, - ['is_hidden, is_invisible'] - ) - ); - } - - /** - * @depends testCreateLocationNodeAssignmentCreation - */ - public function testConvertNodeAssignmentsParentInvisible() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - - $gateway = $this->getLocationGateway(); - $gateway->createNodeAssignment( - new CreateStruct( - [ - 'contentId' => 68, - 'contentVersion' => 1, - 'mainLocationId' => true, - 'priority' => 101, - 'remoteId' => 'some_id', - 'sortField' => 1, - 'sortOrder' => 1, - 'hidden' => false, - // Note: not stored in node assignment, will be calculated from parent - // visibility upon Location creation from node assignment - 'invisible' => false, - ] - ), - 225, - DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE - ); - - $gateway->createLocationsFromNodeAssignments(68, 1); - - $this->assertQueryResult( - [[0, 1]], - $this->buildContentTreeSelectContentWithParentQuery( - 68, - 225, - ['is_hidden, is_invisible'] - ) - ); - } - - /** - * @depends testCreateLocationNodeAssignmentCreation - */ - public function testConvertNodeAssignmentsUpdateAssignment() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - - $gateway = $this->getLocationGateway(); - $gateway->createNodeAssignment( - new CreateStruct( - [ - 'contentId' => 68, - 'contentVersion' => 1, - 'mainLocationId' => 1, - 'priority' => 1, - 'remoteId' => 'some_id', - 'sortField' => 1, - 'sortOrder' => 1, - ] - ), - '77', - DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE - ); - - $gateway->createLocationsFromNodeAssignments(68, 1); - - $this->assertQueryResult( - [[DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP]], - $this->buildNodeAssignmentSelectContentWithParentQuery(68, 77, ['op_code']) - ); - } - - /** - * Test for the setSectionForSubtree() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::setSectionForSubtree - */ - public function testSetSectionForSubtree() - { - $this->insertDatabaseFixture(__DIR__ . '/../../_fixtures/contentobjects.php'); - $gateway = $this->getLocationGateway(); - $gateway->setSectionForSubtree('/1/2/69/70/', 23); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [[68], [69]], - $query - ->select('id') - ->from('ezcontentobject') - ->where($query->expr()->eq('section_id', 23)) - ); - } - - /** - * Test for the changeMainLocation() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::changeMainLocation - * - * @throws \Doctrine\DBAL\DBALException - */ - public function testChangeMainLocation() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - // Create additional location and assignment for test purpose - $connection = $this->getDatabaseConnection(); - $query = $connection->createQueryBuilder(); - $query - ->insert('ezcontentobject_tree') - ->values( - [ - 'contentobject_id' => $query->createPositionalParameter( - 10, - ParameterType::INTEGER - ), - 'contentobject_version' => $query->createPositionalParameter( - 2, - ParameterType::INTEGER - ), - 'main_node_id' => $query->createPositionalParameter(15, ParameterType::INTEGER), - 'node_id' => $query->createPositionalParameter(228, ParameterType::INTEGER), - 'parent_node_id' => $query->createPositionalParameter( - 227, - ParameterType::INTEGER - ), - 'path_string' => $query->createPositionalParameter( - '/1/5/13/228/', - ParameterType::STRING - ), - 'remote_id' => $query->createPositionalParameter( - 'asdfg123437', - ParameterType::STRING - ), - ] - ); - $query->execute(); - - $query = $connection->createQueryBuilder(); - $query - ->insert('eznode_assignment') - ->values( - [ - 'contentobject_id' => $query->createPositionalParameter( - 10, - ParameterType::INTEGER - ), - 'contentobject_version' => $query->createPositionalParameter( - 2, - ParameterType::INTEGER - ), - 'id' => $query->createPositionalParameter(0, ParameterType::INTEGER), - 'is_main' => $query->createPositionalParameter(0, ParameterType::INTEGER), - 'parent_node' => $query->createPositionalParameter(227, ParameterType::INTEGER), - 'parent_remote_id' => $query->createPositionalParameter( - '5238a276bf8231fbcf8a986cdc82a6a5', - ParameterType::STRING - ), - ] - ); - $query->execute(); - - $gateway = $this->getLocationGateway(); - - $gateway->changeMainLocation( - 10, // content id - 228, // new main location id - 2, // content version number - 227 // new main location parent id - ); - - $query = $connection->createQueryBuilder(); - $this->assertQueryResult( - [[228], [228]], - $query - ->select('main_node_id') - ->from('ezcontentobject_tree') - ->where( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter(10, ParameterType::INTEGER) - ) - ) - ); - - $query = $connection->createQueryBuilder(); - $this->assertQueryResult( - [[1]], - $query - ->select('is_main') - ->from('eznode_assignment') - ->where( - $query->expr()->andX( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter(10, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'contentobject_version', - $query->createPositionalParameter(2, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'parent_node', - $query->createPositionalParameter(227, ParameterType::INTEGER) - ) - ) - ) - ); - - $query = $connection->createQueryBuilder(); - $this->assertQueryResult( - [[0]], - $query - ->select('is_main') - ->from('eznode_assignment') - ->where( - $query->expr()->andX( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter(10, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'contentobject_version', - $query->createPositionalParameter(2, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'parent_node', - $query->createPositionalParameter(44, ParameterType::INTEGER) - ) - ) - ) - ); - } - - /** - * Test for the getChildren() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::getChildren - */ - public function testGetChildren() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - - $gateway = $this->getLocationGateway(); - $childrenRows = $gateway->getChildren(213); - - $this->assertCount(2, $childrenRows); - $this->assertCount(16, $childrenRows[0]); - $this->assertEquals(214, $childrenRows[0]['node_id']); - $this->assertCount(16, $childrenRows[1]); - $this->assertEquals(215, $childrenRows[1]['node_id']); - } - - /** - * Test for the getFallbackMainNodeData() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::getFallbackMainNodeData - * - * @throws \Doctrine\DBAL\DBALException - */ - public function testGetFallbackMainNodeData(): void - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - // Create additional location for test purpose - $connection = $this->getDatabaseConnection(); - $query = $connection->createQueryBuilder(); - $query - ->insert('ezcontentobject_tree') - ->values( - [ - 'contentobject_id' => $query->createPositionalParameter( - 12, - ParameterType::INTEGER - ), - 'contentobject_version' => $query->createPositionalParameter( - 1, - ParameterType::INTEGER - ), - 'main_node_id' => $query->createPositionalParameter(13, ParameterType::INTEGER), - 'node_id' => $query->createPositionalParameter(228, ParameterType::INTEGER), - 'parent_node_id' => $query->createPositionalParameter( - 227, - ParameterType::INTEGER - ), - 'path_string' => $query->createPositionalParameter( - '/1/5/13/228/', - ParameterType::STRING - ), - 'remote_id' => $query->createPositionalParameter( - 'asdfg123437', - ParameterType::STRING - ), - ] - ); - $query->execute(); - - $gateway = $this->getLocationGateway(); - $data = $gateway->getFallbackMainNodeData(12, 13); - - $this->assertEquals(228, $data['node_id']); - $this->assertEquals(1, $data['contentobject_version']); - $this->assertEquals(227, $data['parent_node_id']); - } - - /** - * Test for the removeLocation() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::removeLocation - */ - public function testRemoveLocation() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - - $gateway = $this->getLocationGateway(); - $gateway->removeLocation(13); - - try { - $gateway->getBasicNodeData(13); - $this->fail('Location was not deleted!'); - } catch (NotFoundException $e) { - // Do nothing - } - } - - public function providerForTestUpdatePathIdentificationString() - { - return [ - [77, 2, 'new_solutions', 'new_solutions'], - [75, 69, 'stylesheets', 'products/stylesheets'], - ]; - } - - /** - * Test for the updatePathIdentificationString() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::updatePathIdentificationString - * @dataProvider providerForTestUpdatePathIdentificationString - */ - public function testUpdatePathIdentificationString( - $locationId, - $parentLocationId, - $text, - $expected - ) { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); - - $gateway = $this->getLocationGateway(); - $gateway->updatePathIdentificationString($locationId, $parentLocationId, $text); - - $query = $this->getDatabaseConnection()->createQueryBuilder(); - $this->assertQueryResult( - [[$expected]], - $query->select( - 'path_identification_string' - )->from( - 'ezcontentobject_tree' - )->where( - $query->expr()->eq('node_id', $locationId) - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/Gateway/_fixtures/full_example_tree.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/Gateway/_fixtures/full_example_tree.php deleted file mode 100644 index f4e4742182..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/Gateway/_fixtures/full_example_tree.php +++ /dev/null @@ -1,392 +0,0 @@ -<?php - -return array( - 'ezcontentobject_tree' => array( - array( 'contentobject_id' => 0, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 0, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 1, 'modified_subnode' => 1311065058, 'node_id' => 1, 'parent_node_id' => 1, 'path_identification_string' => '', 'path_string' => '/1/', 'priority' => 0, 'remote_id' => '629709ba256fe317c3ddcee35453a96a', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 65, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 2, 'modified_subnode' => 1311065058, 'node_id' => 2, 'parent_node_id' => 1, 'path_identification_string' => '', 'path_string' => '/1/2/', 'priority' => 0, 'remote_id' => 'f3e90596361e31d496d4026eb624c983', 'sort_field' => 8, 'sort_order' => 1 ), - array( 'contentobject_id' => 4, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 5, 'modified_subnode' => 1311065058, 'node_id' => 5, 'parent_node_id' => 1, 'path_identification_string' => 'users', 'path_string' => '/1/5/', 'priority' => 0, 'remote_id' => '3f6d92f8044aed134f32153517850f5a', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 11, 'contentobject_is_published' => 1, 'contentobject_version' => 2, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 12, 'modified_subnode' => 1311065058, 'node_id' => 12, 'parent_node_id' => 5, 'path_identification_string' => 'users/members', 'path_string' => '/1/5/12/', 'priority' => 0, 'remote_id' => '602dcf84765e56b7f999eaafd3821dd3', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 12, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 13, 'modified_subnode' => 1311065057, 'node_id' => 13, 'parent_node_id' => 5, 'path_identification_string' => 'users/administrator_users', 'path_string' => '/1/5/13/', 'priority' => 0, 'remote_id' => '769380b7aa94541679167eab817ca893', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 13, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 14, 'modified_subnode' => 1081860719, 'node_id' => 14, 'parent_node_id' => 5, 'path_identification_string' => 'users/editors', 'path_string' => '/1/5/14/', 'priority' => 0, 'remote_id' => 'f7dda2854fc68f7c8455d9cb14bd04a9', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 14, 'contentobject_is_published' => 1, 'contentobject_version' => 4, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 15, 'modified_subnode' => 1311065057, 'node_id' => 15, 'parent_node_id' => 13, 'path_identification_string' => 'users/administrator_users/administrator_user', 'path_string' => '/1/5/13/15/', 'priority' => 0, 'remote_id' => 'e5161a99f733200b9ed4e80f9c16187b', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 41, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 43, 'modified_subnode' => 1311065012, 'node_id' => 43, 'parent_node_id' => 1, 'path_identification_string' => 'media', 'path_string' => '/1/43/', 'priority' => 0, 'remote_id' => '75c715a51699d2d309a924eca6a95145', 'sort_field' => 9, 'sort_order' => 1 ), - array( 'contentobject_id' => 42, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 44, 'modified_subnode' => 1081860719, 'node_id' => 44, 'parent_node_id' => 5, 'path_identification_string' => 'users/anonymous_users', 'path_string' => '/1/5/44/', 'priority' => 0, 'remote_id' => '4fdf0072da953bb276c0c7e0141c5c9b', 'sort_field' => 9, 'sort_order' => 1 ), - array( 'contentobject_id' => 10, 'contentobject_is_published' => 1, 'contentobject_version' => 2, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 45, 'modified_subnode' => 1081860719, 'node_id' => 45, 'parent_node_id' => 44, 'path_identification_string' => 'users/anonymous_users/anonymous_user', 'path_string' => '/1/5/44/45/', 'priority' => 0, 'remote_id' => '2cf8343bee7b482bab82b269d8fecd76', 'sort_field' => 9, 'sort_order' => 1 ), - array( 'contentobject_id' => 45, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 48, 'modified_subnode' => 1184592117, 'node_id' => 48, 'parent_node_id' => 1, 'path_identification_string' => 'setup2', 'path_string' => '/1/48/', 'priority' => 0, 'remote_id' => '182ce1b5af0c09fa378557c462ba2617', 'sort_field' => 9, 'sort_order' => 1 ), - array( 'contentobject_id' => 49, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 51, 'modified_subnode' => 1311065012, 'node_id' => 51, 'parent_node_id' => 43, 'path_identification_string' => 'media/images', 'path_string' => '/1/43/51/', 'priority' => 0, 'remote_id' => '1b26c0454b09bb49dfb1b9190ffd67cb', 'sort_field' => 9, 'sort_order' => 1 ), - array( 'contentobject_id' => 50, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 52, 'modified_subnode' => 1081860720, 'node_id' => 52, 'parent_node_id' => 43, 'path_identification_string' => 'media/files', 'path_string' => '/1/43/52/', 'priority' => 0, 'remote_id' => '0b113a208f7890f9ad3c24444ff5988c', 'sort_field' => 9, 'sort_order' => 1 ), - array( 'contentobject_id' => 51, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 53, 'modified_subnode' => 1081860720, 'node_id' => 53, 'parent_node_id' => 43, 'path_identification_string' => 'media/multimedia', 'path_string' => '/1/43/53/', 'priority' => 0, 'remote_id' => '4f18b82c75f10aad476cae5adf98c11f', 'sort_field' => 9, 'sort_order' => 1 ), - array( 'contentobject_id' => 52, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 54, 'modified_subnode' => 1184592117, 'node_id' => 54, 'parent_node_id' => 48, 'path_identification_string' => 'setup2/common_ini_settings', 'path_string' => '/1/48/54/', 'priority' => 0, 'remote_id' => 'fa9f3cff9cf90ecfae335718dcbddfe2', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 54, 'contentobject_is_published' => 1, 'contentobject_version' => 2, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 56, 'modified_subnode' => 1311065058, 'node_id' => 56, 'parent_node_id' => 58, 'path_identification_string' => 'design/plain_site', 'path_string' => '/1/58/56/', 'priority' => 0, 'remote_id' => '772da20ecf88b3035d73cbdfcea0f119', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 56, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 58, 'modified_subnode' => 1311065058, 'node_id' => 58, 'parent_node_id' => 1, 'path_identification_string' => 'design', 'path_string' => '/1/58/', 'priority' => 0, 'remote_id' => '79f2d67372ab56f59b5d65bb9e0ca3b9', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 57, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 59, 'modified_subnode' => 1311065011, 'node_id' => 59, 'parent_node_id' => 43, 'path_identification_string' => 'media/banners', 'path_string' => '/1/43/59/', 'priority' => 0, 'remote_id' => '437ef9d0a9b7ae326ec83fa3bb73956d', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 58, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 60, 'modified_subnode' => 1311065010, 'node_id' => 60, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/woman_communities', 'path_string' => '/1/43/59/60/', 'priority' => 0, 'remote_id' => 'eaa7f2f48c3f35801961abad12151db4', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 59, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 61, 'modified_subnode' => 1311065010, 'node_id' => 61, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/woman_frontpage', 'path_string' => '/1/43/59/61/', 'priority' => 0, 'remote_id' => 'b8c85fd926d61dab6e68fa1865cee987', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 60, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 62, 'modified_subnode' => 1311065011, 'node_id' => 62, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/woman_products', 'path_string' => '/1/43/59/62/', 'priority' => 0, 'remote_id' => 'c65aba2485585bdd09dfb66afccf645e', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 61, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 63, 'modified_subnode' => 1311065011, 'node_id' => 63, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/woman_partners', 'path_string' => '/1/43/59/63/', 'priority' => 0, 'remote_id' => '64bb803471e53898aa38a7c29e482370', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 62, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 64, 'modified_subnode' => 1311065011, 'node_id' => 64, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/pencils_support', 'path_string' => '/1/43/59/64/', 'priority' => 0, 'remote_id' => '95e29503817570c6458fa0f37d227306', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 63, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 65, 'modified_subnode' => 1311065011, 'node_id' => 65, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/woman_training', 'path_string' => '/1/43/59/65/', 'priority' => 0, 'remote_id' => 'aa4a1afd9c02d00f2f31186e8a271332', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 64, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 66, 'modified_subnode' => 1311065011, 'node_id' => 66, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/woman_solutions', 'path_string' => '/1/43/59/66/', 'priority' => 0, 'remote_id' => '93d5115082a23b266613868051b8d803', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 66, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 68, 'modified_subnode' => 1311065012, 'node_id' => 68, 'parent_node_id' => 51, 'path_identification_string' => 'media/images/rest_api', 'path_string' => '/1/43/51/68/', 'priority' => 0, 'remote_id' => '31fd28362c18a36cb56223f3609d5d90', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 67, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 69, 'modified_subnode' => 1311065014, 'node_id' => 69, 'parent_node_id' => 2, 'path_identification_string' => 'products', 'path_string' => '/1/2/69/', 'priority' => 0, 'remote_id' => '9cec85d730eec7578190ee95ce5a36f5', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 68, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 70, 'modified_subnode' => 1311065013, 'node_id' => 70, 'parent_node_id' => 69, 'path_identification_string' => 'products/software', 'path_string' => '/1/2/69/70/', 'priority' => 0, 'remote_id' => 'b0b85c15125ca1732e5e528de2717599', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 69, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 71, 'modified_subnode' => 1311065013, 'node_id' => 71, 'parent_node_id' => 70, 'path_identification_string' => 'products/software/os_type_i', 'path_string' => '/1/2/69/70/71/', 'priority' => 0, 'remote_id' => '087adb763245e0cdcac593fb4a5996cf', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 70, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 72, 'modified_subnode' => 1311065014, 'node_id' => 72, 'parent_node_id' => 69, 'path_identification_string' => 'products/boxes', 'path_string' => '/1/2/69/72/', 'priority' => 0, 'remote_id' => 'e607aab6e924091909f3def02415bc53', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 71, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 73, 'modified_subnode' => 1311065013, 'node_id' => 73, 'parent_node_id' => 72, 'path_identification_string' => 'products/boxes/cd_dvd_box_i', 'path_string' => '/1/2/69/72/73/', 'priority' => 0, 'remote_id' => '054d9f10c6fa97689c0fc3b2ac412ebd', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 72, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 74, 'modified_subnode' => 1311065014, 'node_id' => 74, 'parent_node_id' => 72, 'path_identification_string' => 'products/boxes/cd_dvd_box_ii', 'path_string' => '/1/2/69/72/74/', 'priority' => 0, 'remote_id' => '9801bda46e5f8b9d692e1120d50fc7b3', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 73, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 75, 'modified_subnode' => 1311065014, 'node_id' => 75, 'parent_node_id' => 72, 'path_identification_string' => 'products/boxes/cd_dvd_box_iii', 'path_string' => '/1/2/69/72/75/', 'priority' => 0, 'remote_id' => '005067a5eee6505aa0f601cca30681d0', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 74, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 76, 'modified_subnode' => 1311065014, 'node_id' => 76, 'parent_node_id' => 69, 'path_identification_string' => 'products/products_sheets', 'path_string' => '/1/2/69/76/', 'priority' => 0, 'remote_id' => '18f14551cc555c094b15a732ccd27fb2', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 75, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 77, 'modified_subnode' => 1311065017, 'node_id' => 77, 'parent_node_id' => 2, 'path_identification_string' => 'solutions', 'path_string' => '/1/2/77/', 'priority' => 0, 'remote_id' => 'dbc2f3c8716c12f32c379dbf0b1cb133', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 76, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 78, 'modified_subnode' => 1311065016, 'node_id' => 78, 'parent_node_id' => 77, 'path_identification_string' => 'solutions/web_publishing', 'path_string' => '/1/2/77/78/', 'priority' => 0, 'remote_id' => 'bc766fe955437def220a3fa2966a34ee', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 77, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 79, 'modified_subnode' => 1311065015, 'node_id' => 79, 'parent_node_id' => 78, 'path_identification_string' => 'solutions/web_publishing/fusce_sagittis_sagittis', 'path_string' => '/1/2/77/78/79/', 'priority' => 0, 'remote_id' => 'f0c2216ecb29600cd8ae93951a0c8f3a', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 78, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 80, 'modified_subnode' => 1311065015, 'node_id' => 80, 'parent_node_id' => 78, 'path_identification_string' => 'solutions/web_publishing/etiam_posuere_sodales_arcu', 'path_string' => '/1/2/77/78/80/', 'priority' => 0, 'remote_id' => 'eaf16bddfd36206dad265aadfbc98f17', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 79, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 81, 'modified_subnode' => 1311065016, 'node_id' => 81, 'parent_node_id' => 78, 'path_identification_string' => 'solutions/web_publishing/in_hac_habitasse_platea', 'path_string' => '/1/2/77/78/81/', 'priority' => 0, 'remote_id' => 'd2a11e56093b77eb7a347229361c3377', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 80, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 82, 'modified_subnode' => 1311065017, 'node_id' => 82, 'parent_node_id' => 77, 'path_identification_string' => 'solutions/content_management', 'path_string' => '/1/2/77/82/', 'priority' => 0, 'remote_id' => '17d65b568e3500cf1f8b42bc5de2d12b', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 81, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 83, 'modified_subnode' => 1311065016, 'node_id' => 83, 'parent_node_id' => 82, 'path_identification_string' => 'solutions/content_management/fusce_sagittis_sagittis_urna', 'path_string' => '/1/2/77/82/83/', 'priority' => 0, 'remote_id' => 'ecc4f0e94b05bf10f4f783d660ff0ad0', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 82, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 84, 'modified_subnode' => 1311065016, 'node_id' => 84, 'parent_node_id' => 82, 'path_identification_string' => 'solutions/content_management/class_aptent_taciti_sociosqu', 'path_string' => '/1/2/77/82/84/', 'priority' => 0, 'remote_id' => '74280af2cba9002ea9660749225562b6', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 83, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 85, 'modified_subnode' => 1311065017, 'node_id' => 85, 'parent_node_id' => 82, 'path_identification_string' => 'solutions/content_management/aenean_malesuada_ligula', 'path_string' => '/1/2/77/82/85/', 'priority' => 0, 'remote_id' => '4e526426523e47aeacf353541284cbf8', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 84, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 86, 'modified_subnode' => 1311065019, 'node_id' => 86, 'parent_node_id' => 2, 'path_identification_string' => 'training', 'path_string' => '/1/2/86/', 'priority' => 0, 'remote_id' => '95f3c4261719ea27ab67f980fbee0694', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 85, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 87, 'modified_subnode' => 1311065017, 'node_id' => 87, 'parent_node_id' => 86, 'path_identification_string' => 'training/certification', 'path_string' => '/1/2/86/87/', 'priority' => 0, 'remote_id' => '4a1391d3563d056c9d9ea2653093ae3e', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 86, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 88, 'modified_subnode' => 1311065018, 'node_id' => 88, 'parent_node_id' => 86, 'path_identification_string' => 'training/professional_workshops', 'path_string' => '/1/2/86/88/', 'priority' => 0, 'remote_id' => '8889727909b5f34b6aa23f7eee32606b', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 87, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 89, 'modified_subnode' => 1311065017, 'node_id' => 89, 'parent_node_id' => 88, 'path_identification_string' => 'training/professional_workshops/etiam_sodales_mauris', 'path_string' => '/1/2/86/88/89/', 'priority' => 0, 'remote_id' => '1721dbee55639fe280f6a54195f9577c', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 88, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 90, 'modified_subnode' => 1311065018, 'node_id' => 90, 'parent_node_id' => 88, 'path_identification_string' => 'training/professional_workshops/class_aptent_taciti', 'path_string' => '/1/2/86/88/90/', 'priority' => 0, 'remote_id' => '7fabb9ee5bcb6630a3947d1c5585d995', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 89, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 91, 'modified_subnode' => 1311065018, 'node_id' => 91, 'parent_node_id' => 88, 'path_identification_string' => 'training/professional_workshops/duis_auctor_vehicula_erat', 'path_string' => '/1/2/86/88/91/', 'priority' => 0, 'remote_id' => 'a95ae3e4a64f08eb2002b31680fe8989', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 90, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 92, 'modified_subnode' => 1311065018, 'node_id' => 92, 'parent_node_id' => 86, 'path_identification_string' => 'training/events_and_seminars', 'path_string' => '/1/2/86/92/', 'priority' => 0, 'remote_id' => '2acc8bb8f7eda4de2cf74f6dc277661f', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 91, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 93, 'modified_subnode' => 1311065019, 'node_id' => 93, 'parent_node_id' => 86, 'path_identification_string' => 'training/self_paced_courses', 'path_string' => '/1/2/86/93/', 'priority' => 0, 'remote_id' => '6d8cc9831b86d79b8c184507f8e0cbb6', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 92, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 94, 'modified_subnode' => 1311065019, 'node_id' => 94, 'parent_node_id' => 86, 'path_identification_string' => 'training/instructor_led_courses', 'path_string' => '/1/2/86/94/', 'priority' => 0, 'remote_id' => '30f12ecd29608eceb20bb903ddb780c7', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 93, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 95, 'modified_subnode' => 1311065019, 'node_id' => 95, 'parent_node_id' => 86, 'path_identification_string' => 'training/additional_learning_resources', 'path_string' => '/1/2/86/95/', 'priority' => 0, 'remote_id' => '341e63bdce0f1601519d1b8e82e62766', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 94, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 96, 'modified_subnode' => 1311065021, 'node_id' => 96, 'parent_node_id' => 2, 'path_identification_string' => 'support', 'path_string' => '/1/2/96/', 'priority' => 0, 'remote_id' => '0d55a3f510cc7cd9a7b8eb838f50ff5c', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 95, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 97, 'modified_subnode' => 1311065020, 'node_id' => 97, 'parent_node_id' => 96, 'path_identification_string' => 'support/knowledgebase', 'path_string' => '/1/2/96/97/', 'priority' => 0, 'remote_id' => '89ce8eed8fa06c4105fd612aa83d87d6', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 96, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 98, 'modified_subnode' => 1311065020, 'node_id' => 98, 'parent_node_id' => 97, 'path_identification_string' => 'support/knowledgebase/sed_suscipit', 'path_string' => '/1/2/96/97/98/', 'priority' => 0, 'remote_id' => '78f3b9a9268c7b0206bf1c4d39211495', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 97, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 99, 'modified_subnode' => 1311065020, 'node_id' => 99, 'parent_node_id' => 98, 'path_identification_string' => 'support/knowledgebase/sed_suscipit/ut_interdum', 'path_string' => '/1/2/96/97/98/99/', 'priority' => 0, 'remote_id' => 'a1f9b32547e58064e645388512c16a39', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 98, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 100, 'modified_subnode' => 1311065020, 'node_id' => 100, 'parent_node_id' => 96, 'path_identification_string' => 'support/documentation', 'path_string' => '/1/2/96/100/', 'priority' => 0, 'remote_id' => '0f9c7380f8af1f29f1017e412bdd4016', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 99, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 101, 'modified_subnode' => 1311065020, 'node_id' => 101, 'parent_node_id' => 100, 'path_identification_string' => 'support/documentation/mauris_pretium', 'path_string' => '/1/2/96/100/101/', 'priority' => 0, 'remote_id' => '036394ec8b160f0782bec2dda452d798', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 100, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 102, 'modified_subnode' => 1311065020, 'node_id' => 102, 'parent_node_id' => 101, 'path_identification_string' => 'support/documentation/mauris_pretium/aliquam_posuere', 'path_string' => '/1/2/96/100/101/102/', 'priority' => 0, 'remote_id' => '78da6a4fae1c2eaabe1dbe7af818d970', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 101, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 103, 'modified_subnode' => 1311065021, 'node_id' => 103, 'parent_node_id' => 96, 'path_identification_string' => 'support/books', 'path_string' => '/1/2/96/103/', 'priority' => 0, 'remote_id' => '06cdeb27be466ea8330de5df16144263', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 102, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 104, 'modified_subnode' => 1311065021, 'node_id' => 104, 'parent_node_id' => 103, 'path_identification_string' => 'support/books/aliquam_pulvinar_suscipit_tellus', 'path_string' => '/1/2/96/103/104/', 'priority' => 0, 'remote_id' => 'ab30399896b8e54442c3a619ba7eeecb', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 103, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 105, 'modified_subnode' => 1311065021, 'node_id' => 105, 'parent_node_id' => 96, 'path_identification_string' => 'support/customer_service', 'path_string' => '/1/2/96/105/', 'priority' => 0, 'remote_id' => 'ca2ae9d0f0322798f632e896325f22c3', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 104, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 106, 'modified_subnode' => 1311065021, 'node_id' => 106, 'parent_node_id' => 96, 'path_identification_string' => 'support/support_programs', 'path_string' => '/1/2/96/106/', 'priority' => 0, 'remote_id' => '7ba1d48c0151bae62366095ef6f64c28', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 105, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 107, 'modified_subnode' => 1311065034, 'node_id' => 107, 'parent_node_id' => 2, 'path_identification_string' => 'getting_started', 'path_string' => '/1/2/107/', 'priority' => 0, 'remote_id' => 'e81507d3446726ebd7361352fef5fad3', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 106, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 108, 'modified_subnode' => 1311065022, 'node_id' => 108, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/tutorials_for', 'path_string' => '/1/2/107/108/', 'priority' => 0, 'remote_id' => '51278360f39d5b8ce1d9249953f4de98', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 107, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 109, 'modified_subnode' => 1311065023, 'node_id' => 109, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/get_involved', 'path_string' => '/1/2/107/109/', 'priority' => 0, 'remote_id' => '33151e24acea9c837d2b9fc52e03b1de', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 108, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 110, 'modified_subnode' => 1311065024, 'node_id' => 110, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/how_to_use_ez_publish', 'path_string' => '/1/2/107/110/', 'priority' => 0, 'remote_id' => '6b3bcab0f149c5acc2e3728df7c66b73', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 109, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 111, 'modified_subnode' => 1311065023, 'node_id' => 111, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics1', 'path_string' => '/1/2/107/110/111/', 'priority' => 0, 'remote_id' => '28f9dfe5c0680955eec7a2dec4ebc642', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 110, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 112, 'modified_subnode' => 1311065023, 'node_id' => 112, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics12', 'path_string' => '/1/2/107/110/112/', 'priority' => 0, 'remote_id' => '191faba79dc108a19893944befa50d94', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 111, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 113, 'modified_subnode' => 1311065024, 'node_id' => 113, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics13', 'path_string' => '/1/2/107/110/113/', 'priority' => 0, 'remote_id' => '8679b5fa3ad0ec216293419ab9834e44', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 112, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 114, 'modified_subnode' => 1311065024, 'node_id' => 114, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics14', 'path_string' => '/1/2/107/110/114/', 'priority' => 0, 'remote_id' => '7f32cdefd0cf55b966a44aa52181e30d', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 113, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 115, 'modified_subnode' => 1311065024, 'node_id' => 115, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics15', 'path_string' => '/1/2/107/110/115/', 'priority' => 0, 'remote_id' => '29db3c1fcdb497e5cb2c74eb85c0906a', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 114, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 116, 'modified_subnode' => 1311065024, 'node_id' => 116, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics16', 'path_string' => '/1/2/107/110/116/', 'priority' => 0, 'remote_id' => '96e30009f712a0315217fed93eba6a18', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 115, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 117, 'modified_subnode' => 1311065024, 'node_id' => 117, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics17', 'path_string' => '/1/2/107/110/117/', 'priority' => 0, 'remote_id' => '3a6b87470a25b19a6ad15caa5e24e719', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 116, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 118, 'modified_subnode' => 1311065024, 'node_id' => 118, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics18', 'path_string' => '/1/2/107/110/118/', 'priority' => 0, 'remote_id' => 'd0bc77a21920b63543d7b0accab81b24', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 117, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 119, 'modified_subnode' => 1311065024, 'node_id' => 119, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics19', 'path_string' => '/1/2/107/110/119/', 'priority' => 0, 'remote_id' => 'c75b4b4b870e0e3611e19e1323103282', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 118, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 120, 'modified_subnode' => 1311065027, 'node_id' => 120, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/how_to_manage_ez_publish', 'path_string' => '/1/2/107/120/', 'priority' => 0, 'remote_id' => '262d8c936d3757ff81e7bb49392b703f', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 119, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 121, 'modified_subnode' => 1311065025, 'node_id' => 121, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics1', 'path_string' => '/1/2/107/120/121/', 'priority' => 0, 'remote_id' => '220d4e10bf4619525c3165823079482c', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 120, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 122, 'modified_subnode' => 1311065025, 'node_id' => 122, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics12', 'path_string' => '/1/2/107/120/122/', 'priority' => 0, 'remote_id' => 'f13a938ce2c79a9d438548299220d6dd', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 121, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 123, 'modified_subnode' => 1311065025, 'node_id' => 123, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics13', 'path_string' => '/1/2/107/120/123/', 'priority' => 0, 'remote_id' => '04f5e08293954b1851a4dd1cbd976cff', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 122, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 124, 'modified_subnode' => 1311065026, 'node_id' => 124, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics14', 'path_string' => '/1/2/107/120/124/', 'priority' => 0, 'remote_id' => 'dc80ae3d9de55855a16218b032062c62', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 123, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 125, 'modified_subnode' => 1311065026, 'node_id' => 125, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics15', 'path_string' => '/1/2/107/120/125/', 'priority' => 0, 'remote_id' => '4cb6d4015dd80474074043ab28f96e36', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 124, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 126, 'modified_subnode' => 1311065026, 'node_id' => 126, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics16', 'path_string' => '/1/2/107/120/126/', 'priority' => 0, 'remote_id' => '74ce7d6cbae81d90592b4a8a242a284a', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 125, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 127, 'modified_subnode' => 1311065027, 'node_id' => 127, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics17', 'path_string' => '/1/2/107/120/127/', 'priority' => 0, 'remote_id' => '6f76d2f7e5812acf4f7c5c70258f4f7e', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 126, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 128, 'modified_subnode' => 1311065027, 'node_id' => 128, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics18', 'path_string' => '/1/2/107/120/128/', 'priority' => 0, 'remote_id' => '5ba07103968a6bb3f05c275c212440f6', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 127, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 129, 'modified_subnode' => 1311065028, 'node_id' => 129, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/how_to_develop_with_ez_publish', 'path_string' => '/1/2/107/129/', 'priority' => 0, 'remote_id' => '3579b6c5cd166d5137eada55274892d3', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 128, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 130, 'modified_subnode' => 1311065028, 'node_id' => 130, 'parent_node_id' => 129, 'path_identification_string' => 'getting_started/how_to_develop_with_ez_publish/graphics1', 'path_string' => '/1/2/107/129/130/', 'priority' => 0, 'remote_id' => 'e38a43ce35832e5af2df4f1e9272b926', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 129, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 131, 'modified_subnode' => 1311065028, 'node_id' => 131, 'parent_node_id' => 129, 'path_identification_string' => 'getting_started/how_to_develop_with_ez_publish/graphics12', 'path_string' => '/1/2/107/129/131/', 'priority' => 0, 'remote_id' => '1ba7889f63e32acf70f66349ce1a8953', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 130, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 132, 'modified_subnode' => 1311065028, 'node_id' => 132, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/adding_siteaccesses_in_ez_publish', 'path_string' => '/1/2/107/132/', 'priority' => 0, 'remote_id' => '19ae9fcb7334a0e407b2781920247122', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 131, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 133, 'modified_subnode' => 1311065034, 'node_id' => 133, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/new_features', 'path_string' => '/1/2/107/133/', 'priority' => 0, 'remote_id' => '423b6d20325263abaa2012536cc736a4', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 132, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 134, 'modified_subnode' => 1311065030, 'node_id' => 134, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/improved_user_registration_workflow', 'path_string' => '/1/2/107/133/134/', 'priority' => 0, 'remote_id' => '07f0de334c83d631a59defe61f1a7cbf', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 133, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 135, 'modified_subnode' => 1311065029, 'node_id' => 135, 'parent_node_id' => 134, 'path_identification_string' => 'getting_started/new_features/improved_user_registration_workflow/graphics1', 'path_string' => '/1/2/107/133/134/135/', 'priority' => 0, 'remote_id' => 'a7ca1a8878210a9978df0b00e9184b20', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 134, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 136, 'modified_subnode' => 1311065029, 'node_id' => 136, 'parent_node_id' => 134, 'path_identification_string' => 'getting_started/new_features/improved_user_registration_workflow/graphics12', 'path_string' => '/1/2/107/133/134/136/', 'priority' => 0, 'remote_id' => '12c86be2151c426b990d47efd023cc40', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 135, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 137, 'modified_subnode' => 1311065030, 'node_id' => 137, 'parent_node_id' => 134, 'path_identification_string' => 'getting_started/new_features/improved_user_registration_workflow/graphics13', 'path_string' => '/1/2/107/133/134/137/', 'priority' => 0, 'remote_id' => 'ff4473534c7cef8212a11feab57a5f0e', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 136, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 138, 'modified_subnode' => 1311065030, 'node_id' => 138, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/improved_block_editing', 'path_string' => '/1/2/107/133/138/', 'priority' => 0, 'remote_id' => 'e0a1b1b2a3d248bf8d43a83581c22ae8', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 137, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 139, 'modified_subnode' => 1311065030, 'node_id' => 139, 'parent_node_id' => 138, 'path_identification_string' => 'getting_started/new_features/improved_block_editing/graphics1', 'path_string' => '/1/2/107/133/138/139/', 'priority' => 0, 'remote_id' => '8e6f0ba5195410b03a84ef3fdcc3ff0b', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 138, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 140, 'modified_subnode' => 1311065030, 'node_id' => 140, 'parent_node_id' => 138, 'path_identification_string' => 'getting_started/new_features/improved_block_editing/graphics12', 'path_string' => '/1/2/107/133/138/140/', 'priority' => 0, 'remote_id' => 'bc416b97e1592c7a396f6f5f85e131ac', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 139, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 141, 'modified_subnode' => 1311065032, 'node_id' => 141, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/improved_front_end_editing', 'path_string' => '/1/2/107/133/141/', 'priority' => 0, 'remote_id' => 'facfd2587054ea863cbe53768da04a47', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 140, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 142, 'modified_subnode' => 1311065031, 'node_id' => 142, 'parent_node_id' => 141, 'path_identification_string' => 'getting_started/new_features/improved_front_end_editing/graphics1', 'path_string' => '/1/2/107/133/141/142/', 'priority' => 0, 'remote_id' => 'd159e8d4d590e481b06ecb441c0f14dc', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 141, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 143, 'modified_subnode' => 1311065031, 'node_id' => 143, 'parent_node_id' => 141, 'path_identification_string' => 'getting_started/new_features/improved_front_end_editing/graphics12', 'path_string' => '/1/2/107/133/141/143/', 'priority' => 0, 'remote_id' => '7457db5919dd349bd878b60592fc009b', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 142, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 144, 'modified_subnode' => 1311065032, 'node_id' => 144, 'parent_node_id' => 141, 'path_identification_string' => 'getting_started/new_features/improved_front_end_editing/graphics13', 'path_string' => '/1/2/107/133/141/144/', 'priority' => 0, 'remote_id' => 'ebe1e9d1242a3aeedfda25da7cc25564', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 143, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 145, 'modified_subnode' => 1311065032, 'node_id' => 145, 'parent_node_id' => 141, 'path_identification_string' => 'getting_started/new_features/improved_front_end_editing/graphics14', 'path_string' => '/1/2/107/133/141/145/', 'priority' => 0, 'remote_id' => 'b3acf4c0f2df8977f1c6932725339a1d', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 144, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 146, 'modified_subnode' => 1311065032, 'node_id' => 146, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/fastcgi', 'path_string' => '/1/2/107/133/146/', 'priority' => 0, 'remote_id' => 'a324a48fea49eb3e6826b3295dc5f413', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 145, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 147, 'modified_subnode' => 1311065033, 'node_id' => 147, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/support_for_red_hat_enterprise', 'path_string' => '/1/2/107/133/147/', 'priority' => 0, 'remote_id' => '8c0133434d7bea077a614e1a85fca5b7', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 146, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 148, 'modified_subnode' => 1311065033, 'node_id' => 148, 'parent_node_id' => 147, 'path_identification_string' => 'getting_started/new_features/support_for_red_hat_enterprise/graphics1', 'path_string' => '/1/2/107/133/147/148/', 'priority' => 0, 'remote_id' => '46653e0e5fa60ab86788cad8084a759d', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 147, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 149, 'modified_subnode' => 1311065033, 'node_id' => 149, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/canonical_links', 'path_string' => '/1/2/107/133/149/', 'priority' => 0, 'remote_id' => '9b17e8d0b2cee501b75f7b481155e64f', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 148, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 150, 'modified_subnode' => 1311065034, 'node_id' => 150, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/asynchronous_publishing', 'path_string' => '/1/2/107/133/150/', 'priority' => 0, 'remote_id' => '3983d3aa7604f45a8ecbb953a98d646f', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 149, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 151, 'modified_subnode' => 1311065034, 'node_id' => 151, 'parent_node_id' => 150, 'path_identification_string' => 'getting_started/new_features/asynchronous_publishing/graphics1', 'path_string' => '/1/2/107/133/150/151/', 'priority' => 0, 'remote_id' => '92756622f207c0b1902ca156947a77de', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 150, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 152, 'modified_subnode' => 1311065034, 'node_id' => 152, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/rest_api_interface', 'path_string' => '/1/2/107/133/152/', 'priority' => 0, 'remote_id' => '60adbb1e37a6524246d3af372366754f', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 151, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 153, 'modified_subnode' => 1311065035, 'node_id' => 153, 'parent_node_id' => 2, 'path_identification_string' => 'ez_publish_enterprise', 'path_string' => '/1/2/153/', 'priority' => 0, 'remote_id' => 'f4bf2e6c1cf75e15b15f0123a82778a1', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 152, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 154, 'modified_subnode' => 1311065035, 'node_id' => 154, 'parent_node_id' => 153, 'path_identification_string' => 'ez_publish_enterprise/graphics1', 'path_string' => '/1/2/153/154/', 'priority' => 0, 'remote_id' => '1b95356b68bd59ef32b0fd7bb2c29130', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 153, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 155, 'modified_subnode' => 1311065035, 'node_id' => 155, 'parent_node_id' => 153, 'path_identification_string' => 'ez_publish_enterprise/graphics12', 'path_string' => '/1/2/153/155/', 'priority' => 0, 'remote_id' => '94b7059eb5e0fe667f7c63eadd53a62b', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 154, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 156, 'modified_subnode' => 1311065039, 'node_id' => 156, 'parent_node_id' => 2, 'path_identification_string' => 'partners', 'path_string' => '/1/2/156/', 'priority' => 3, 'remote_id' => '0c523dfd6b1eccbfb0d1bfe632ee7411', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 155, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 157, 'modified_subnode' => 1311065036, 'node_id' => 157, 'parent_node_id' => 156, 'path_identification_string' => 'partners/partner_news', 'path_string' => '/1/2/156/157/', 'priority' => 0, 'remote_id' => 'f5db8acf1150afc15eb514d380e3855d', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 156, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 158, 'modified_subnode' => 1311065036, 'node_id' => 158, 'parent_node_id' => 157, 'path_identification_string' => 'partners/partner_news/pellentesque_quam_mauris', 'path_string' => '/1/2/156/157/158/', 'priority' => 0, 'remote_id' => '6366a99ac1cec2133e87fdd805d108c8', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 157, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 159, 'modified_subnode' => 1311065036, 'node_id' => 159, 'parent_node_id' => 157, 'path_identification_string' => 'partners/partner_news/penatibus_et_magnis_dis', 'path_string' => '/1/2/156/157/159/', 'priority' => 0, 'remote_id' => '7ee406cb6e5f0b2a29bc9f97f5a5b9db', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 158, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 160, 'modified_subnode' => 1311065038, 'node_id' => 160, 'parent_node_id' => 156, 'path_identification_string' => 'partners/news', 'path_string' => '/1/2/156/160/', 'priority' => 0, 'remote_id' => '007109e7f353c86dfd48d844cb18ce73', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 159, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 161, 'modified_subnode' => 1311065037, 'node_id' => 161, 'parent_node_id' => 160, 'path_identification_string' => 'partners/news/curabitur_hendrerit_dignissim', 'path_string' => '/1/2/156/160/161/', 'priority' => 0, 'remote_id' => '63769c39380a45a731aa44068c0f4e33', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 160, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 162, 'modified_subnode' => 1311065037, 'node_id' => 162, 'parent_node_id' => 160, 'path_identification_string' => 'partners/news/proin_condimentum_risus', 'path_string' => '/1/2/156/160/162/', 'priority' => 0, 'remote_id' => 'f761918d15e356e50ce94294cad79765', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 161, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 163, 'modified_subnode' => 1311065038, 'node_id' => 163, 'parent_node_id' => 160, 'path_identification_string' => 'partners/news/morbi_tristique_senectus', 'path_string' => '/1/2/156/160/163/', 'priority' => 0, 'remote_id' => '68187b44ef9051a4db10bd9bb7f228a4', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 162, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 164, 'modified_subnode' => 1311065038, 'node_id' => 164, 'parent_node_id' => 156, 'path_identification_string' => 'partners/partner_products', 'path_string' => '/1/2/156/164/', 'priority' => 0, 'remote_id' => '472e9bc321148baa71d2070063f24bb9', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 163, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 165, 'modified_subnode' => 1311065039, 'node_id' => 165, 'parent_node_id' => 156, 'path_identification_string' => 'partners/partners', 'path_string' => '/1/2/156/165/', 'priority' => 0, 'remote_id' => 'b1a68df10cdf379cf78999c8012db679', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 164, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 166, 'modified_subnode' => 1311065039, 'node_id' => 166, 'parent_node_id' => 165, 'path_identification_string' => 'partners/partners/neque_orci_malesuada_felis', 'path_string' => '/1/2/156/165/166/', 'priority' => 0, 'remote_id' => 'e9ee6724fffb012f918300de66af27e1', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 165, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 167, 'modified_subnode' => 1311065044, 'node_id' => 167, 'parent_node_id' => 2, 'path_identification_string' => 'community', 'path_string' => '/1/2/167/', 'priority' => 4, 'remote_id' => 'c4604fb2e100a6681a4f53fbe6e5eeae', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 166, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 168, 'modified_subnode' => 1311065040, 'node_id' => 168, 'parent_node_id' => 167, 'path_identification_string' => 'community/forum', 'path_string' => '/1/2/167/168/', 'priority' => 0, 'remote_id' => '5e6762a083ab4c0357bad163902ba8f7', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 167, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 169, 'modified_subnode' => 1311065039, 'node_id' => 169, 'parent_node_id' => 168, 'path_identification_string' => 'community/forum/nulla_vitae_tellus_sit_amet', 'path_string' => '/1/2/167/168/169/', 'priority' => 0, 'remote_id' => '2549eee03064d55ae7fc84fe41ae1ef6', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 168, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 170, 'modified_subnode' => 1311065040, 'node_id' => 170, 'parent_node_id' => 168, 'path_identification_string' => 'community/forum/ut_mollis_sodales_nibh', 'path_string' => '/1/2/167/168/170/', 'priority' => 0, 'remote_id' => '7bfca4bdde619ac9104e235629e2dbc7', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 169, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 171, 'modified_subnode' => 1311065040, 'node_id' => 171, 'parent_node_id' => 168, 'path_identification_string' => 'community/forum/nam_risus_leo', 'path_string' => '/1/2/167/168/171/', 'priority' => 0, 'remote_id' => '81fd930afe94962395074b762592a356', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 170, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 172, 'modified_subnode' => 1311065041, 'node_id' => 172, 'parent_node_id' => 167, 'path_identification_string' => 'community/wiki', 'path_string' => '/1/2/167/172/', 'priority' => 0, 'remote_id' => '8b570ac03318b7e29eb5fd1bae15efde', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 171, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 173, 'modified_subnode' => 1311065041, 'node_id' => 173, 'parent_node_id' => 172, 'path_identification_string' => 'community/wiki/sed_suscipit', 'path_string' => '/1/2/167/172/173/', 'priority' => 0, 'remote_id' => '4329dcc6a8d441aa7253660512dfd5b6', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 172, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 174, 'modified_subnode' => 1311065041, 'node_id' => 174, 'parent_node_id' => 173, 'path_identification_string' => 'community/wiki/sed_suscipit/ut_interdum', 'path_string' => '/1/2/167/172/173/174/', 'priority' => 0, 'remote_id' => '6cf7b9370a0d9800ed49ba3be13b6be5', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 173, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 175, 'modified_subnode' => 1311065041, 'node_id' => 175, 'parent_node_id' => 173, 'path_identification_string' => 'community/wiki/sed_suscipit/fusce_pulvinar', 'path_string' => '/1/2/167/172/173/175/', 'priority' => 0, 'remote_id' => 'f71191fcce35c83530cddde467a908b2', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 174, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 176, 'modified_subnode' => 1311065041, 'node_id' => 176, 'parent_node_id' => 172, 'path_identification_string' => 'community/wiki/duis_id_tortor', 'path_string' => '/1/2/167/172/176/', 'priority' => 0, 'remote_id' => '2d3cf4fe8d439e16e0ed75ef947b5e3d', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 175, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 177, 'modified_subnode' => 1311065041, 'node_id' => 177, 'parent_node_id' => 176, 'path_identification_string' => 'community/wiki/duis_id_tortor/curabitur_lacinia', 'path_string' => '/1/2/167/172/176/177/', 'priority' => 0, 'remote_id' => '49c9ad954328ab5f5a76d580873961a4', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 176, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 178, 'modified_subnode' => 1311065044, 'node_id' => 178, 'parent_node_id' => 167, 'path_identification_string' => 'community/pictures', 'path_string' => '/1/2/167/178/', 'priority' => 0, 'remote_id' => 'ae32173cedf3a5ec441e3afcadc4abcb', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 177, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 179, 'modified_subnode' => 1311065042, 'node_id' => 179, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/ivo_on_ez_tags', 'path_string' => '/1/2/167/178/179/', 'priority' => 0, 'remote_id' => 'c6c32c884db955ea09338c90eee84746', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 178, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 180, 'modified_subnode' => 1311065042, 'node_id' => 180, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/are_you_ready_for_the_community_day', 'path_string' => '/1/2/167/178/180/', 'priority' => 0, 'remote_id' => 'db191e87b9b6ac22a18742cd32d2b3d4', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 179, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 181, 'modified_subnode' => 1311065042, 'node_id' => 181, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/let_s_work_together', 'path_string' => '/1/2/167/178/181/', 'priority' => 0, 'remote_id' => '02a6d1506bf437eda1317d0c36a8545b', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 180, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 182, 'modified_subnode' => 1311065043, 'node_id' => 182, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/the_mediterranean_seen_from_nice', 'path_string' => '/1/2/167/178/182/', 'priority' => 0, 'remote_id' => '40d743025e4ec4d813bd864dad08e0cc', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 181, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 183, 'modified_subnode' => 1311065043, 'node_id' => 183, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/hotel_lobby_at_isola2000', 'path_string' => '/1/2/167/178/183/', 'priority' => 0, 'remote_id' => 'eb2a8ef7d4b1fb8ffb68f0764b615de1', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 182, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 184, 'modified_subnode' => 1311065043, 'node_id' => 184, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/alexander_on_the_past_gaby_on_today_and_tomorrow', 'path_string' => '/1/2/167/178/184/', 'priority' => 0, 'remote_id' => '9e45f3ee0f15995b670fb3a117b6ee49', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 183, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 185, 'modified_subnode' => 1311065043, 'node_id' => 185, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/isola2000_in_white_beautiful_skiing', 'path_string' => '/1/2/167/178/185/', 'priority' => 0, 'remote_id' => 'f368c4ae469c596344e15fca8c7370c2', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 184, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 186, 'modified_subnode' => 1311065044, 'node_id' => 186, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/ceo_future_pitch_adaptable_open_reliable', 'path_string' => '/1/2/167/178/186/', 'priority' => 0, 'remote_id' => '7e153b75dd0ee0476ac7463d4a3daaef', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 185, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 187, 'modified_subnode' => 1311065044, 'node_id' => 187, 'parent_node_id' => 167, 'path_identification_string' => 'community/blog', 'path_string' => '/1/2/167/187/', 'priority' => 0, 'remote_id' => '09fb4147e4b124b9770a527bdd66799c', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 186, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 188, 'modified_subnode' => 1311065044, 'node_id' => 188, 'parent_node_id' => 187, 'path_identification_string' => 'community/blog/visit_paris', 'path_string' => '/1/2/167/187/188/', 'priority' => 0, 'remote_id' => '8f73d6d4ffca33c619d2f2645340ccc0', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 187, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 189, 'modified_subnode' => 1311065044, 'node_id' => 189, 'parent_node_id' => 187, 'path_identification_string' => 'community/blog/paris_in_france', 'path_string' => '/1/2/167/187/189/', 'priority' => 0, 'remote_id' => '5cdc175d45aeb3fa054a95f40df56e79', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 188, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 190, 'modified_subnode' => 1311065055, 'node_id' => 190, 'parent_node_id' => 2, 'path_identification_string' => 'company', 'path_string' => '/1/2/190/', 'priority' => 6, 'remote_id' => 'e060ff40aa17eb21dc1e4595c9a5eb9a', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 189, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 191, 'modified_subnode' => 1311065045, 'node_id' => 191, 'parent_node_id' => 190, 'path_identification_string' => 'company/about_company', 'path_string' => '/1/2/190/191/', 'priority' => 0, 'remote_id' => '7bf290af79b3b51e6ccc1f95f321618c', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 190, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 192, 'modified_subnode' => 1311065053, 'node_id' => 192, 'parent_node_id' => 190, 'path_identification_string' => 'company/new_features', 'path_string' => '/1/2/190/192/', 'priority' => 0, 'remote_id' => '9f804eee139dcee343dbe4175d14654c', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 191, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 193, 'modified_subnode' => 1311065046, 'node_id' => 193, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/separate_content_design_in_ez_publish', 'path_string' => '/1/2/190/192/193/', 'priority' => 0, 'remote_id' => '10d57795c986ca61689e6c2c11382eb3', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 192, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 194, 'modified_subnode' => 1311065046, 'node_id' => 194, 'parent_node_id' => 193, 'path_identification_string' => 'company/new_features/separate_content_design_in_ez_publish/separation_of_content_and_design', 'path_string' => '/1/2/190/192/193/194/', 'priority' => 0, 'remote_id' => '87417129b080d24d335eb2653dd1a40c', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 193, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 195, 'modified_subnode' => 1311065048, 'node_id' => 195, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/how_to_use_ez_publish', 'path_string' => '/1/2/190/192/195/', 'priority' => 0, 'remote_id' => 'fd56d839df6e2b52055d4a62f4df7c3e', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 194, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 196, 'modified_subnode' => 1311065047, 'node_id' => 196, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/openoffice_import_large', 'path_string' => '/1/2/190/192/195/196/', 'priority' => 0, 'remote_id' => 'ab2f2093f16c56ac2e61f40f25d2dbe0', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 195, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 197, 'modified_subnode' => 1311065047, 'node_id' => 197, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/translation_large', 'path_string' => '/1/2/190/192/195/197/', 'priority' => 0, 'remote_id' => '185a1737e9012e7c3025df876bbb4f9b', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 196, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 198, 'modified_subnode' => 1311065047, 'node_id' => 198, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/multiupload_large', 'path_string' => '/1/2/190/192/195/198/', 'priority' => 0, 'remote_id' => 'efdca2f6ddd2c8befc283ae3465b9acd', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 197, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 199, 'modified_subnode' => 1311065047, 'node_id' => 199, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/gallery_large', 'path_string' => '/1/2/190/192/195/199/', 'priority' => 0, 'remote_id' => 'f81b0e4c31ea13d94b9838e9e7e7ad7e', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 198, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 200, 'modified_subnode' => 1311065048, 'node_id' => 200, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/search_result_large', 'path_string' => '/1/2/190/192/195/200/', 'priority' => 0, 'remote_id' => '666efc49c267cddf55ca707408f4ac42', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 199, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 201, 'modified_subnode' => 1311065048, 'node_id' => 201, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/ez_tool_bar_large', 'path_string' => '/1/2/190/192/195/201/', 'priority' => 0, 'remote_id' => 'afb51a5f898c549c9e1f35afc75d66f3', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 200, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 202, 'modified_subnode' => 1311065048, 'node_id' => 202, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/quicktime_large', 'path_string' => '/1/2/190/192/195/202/', 'priority' => 0, 'remote_id' => '94b71866ede0d2776c140e85a823d31d', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 201, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 203, 'modified_subnode' => 1311065048, 'node_id' => 203, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/web_2_0_large', 'path_string' => '/1/2/190/192/195/203/', 'priority' => 0, 'remote_id' => 'e74475a240fb7061b40c75f5e6fcd52c', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 202, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 204, 'modified_subnode' => 1311065051, 'node_id' => 204, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish', 'path_string' => '/1/2/190/192/204/', 'priority' => 0, 'remote_id' => '42a3b07037390457ad0f2c65494bfd97', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 203, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 205, 'modified_subnode' => 1311065049, 'node_id' => 205, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/usermanagement_large', 'path_string' => '/1/2/190/192/204/205/', 'priority' => 0, 'remote_id' => '6da1a82ba3d962d2ad953fff8ee26362', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 204, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 206, 'modified_subnode' => 1311065049, 'node_id' => 206, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/workflow_large', 'path_string' => '/1/2/190/192/204/206/', 'priority' => 0, 'remote_id' => 'b284abd88bdcb1887a86e40304ffc66f', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 205, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 207, 'modified_subnode' => 1311065050, 'node_id' => 207, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/versioning_large', 'path_string' => '/1/2/190/192/204/207/', 'priority' => 0, 'remote_id' => 'e3400bb44aae5fcb416156e7450f3f32', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 206, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 208, 'modified_subnode' => 1311065050, 'node_id' => 208, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/datatypes_attributes_content_class_and_objects_large', 'path_string' => '/1/2/190/192/204/208/', 'priority' => 0, 'remote_id' => '2b2b27bbaaec688aaec1a5d39b4b13ae', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 207, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 209, 'modified_subnode' => 1311065050, 'node_id' => 209, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/multiple_location_large', 'path_string' => '/1/2/190/192/204/209/', 'priority' => 0, 'remote_id' => '37e5feed6574d1d8da4b4531569a27a2', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 208, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 210, 'modified_subnode' => 1311065050, 'node_id' => 210, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/multisite_deployment_large', 'path_string' => '/1/2/190/192/204/210/', 'priority' => 0, 'remote_id' => '5625695a66034f53b9b6e14029e06e80', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 209, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 211, 'modified_subnode' => 1311065051, 'node_id' => 211, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/seo_large', 'path_string' => '/1/2/190/192/204/211/', 'priority' => 0, 'remote_id' => '824aba4122ee51d9638847e55642feec', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 210, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 212, 'modified_subnode' => 1311065051, 'node_id' => 212, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/admin_site_large', 'path_string' => '/1/2/190/192/204/212/', 'priority' => 0, 'remote_id' => '73e95051b800ce051c3851c60807787b', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 211, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 213, 'modified_subnode' => 1311065052, 'node_id' => 213, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/how_to_develop_with_ez_publish', 'path_string' => '/1/2/190/192/213/', 'priority' => 0, 'remote_id' => '3f921d3c490a6c17870b1f01fcfbceed', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 212, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 214, 'modified_subnode' => 1311065052, 'node_id' => 214, 'parent_node_id' => 213, 'path_identification_string' => 'company/new_features/how_to_develop_with_ez_publish/datatypes_attributes_content_class_and_objects_large', 'path_string' => '/1/2/190/192/213/214/', 'priority' => 0, 'remote_id' => '97f7b391f1522bf404ff672ac338b91c', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 213, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 215, 'modified_subnode' => 1311065052, 'node_id' => 215, 'parent_node_id' => 213, 'path_identification_string' => 'company/new_features/how_to_develop_with_ez_publish/site_style_large', 'path_string' => '/1/2/190/192/213/215/', 'priority' => 0, 'remote_id' => 'c5549bf68962a5384bd2225931cba302', 'sort_field' => 2, 'sort_order' => 0 ), - array( 'contentobject_id' => 214, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 216, 'modified_subnode' => 1311065052, 'node_id' => 216, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/rest_api_interface', 'path_string' => '/1/2/190/192/216/', 'priority' => 0, 'remote_id' => '54adfd9f7c80638f5560eb3f0618c57b', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 215, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 217, 'modified_subnode' => 1311065052, 'node_id' => 217, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/asynchronous_publishing', 'path_string' => '/1/2/190/192/217/', 'priority' => 0, 'remote_id' => 'a7dbafbf963821b71e5522c21e09d9dd', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 216, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 218, 'modified_subnode' => 1311065053, 'node_id' => 218, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/canonical_links', 'path_string' => '/1/2/190/192/218/', 'priority' => 0, 'remote_id' => '61e3b15f020590df0759b779e5f1fb9d', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 217, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 219, 'modified_subnode' => 1311065053, 'node_id' => 219, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/support_for_red_hat_enterprise', 'path_string' => '/1/2/190/192/219/', 'priority' => 0, 'remote_id' => '42560fa30a55ac5ddf1f76a52d44a613', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 218, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 220, 'modified_subnode' => 1311065053, 'node_id' => 220, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/fastcgi', 'path_string' => '/1/2/190/192/220/', 'priority' => 0, 'remote_id' => '416c28c9ba34a9114381a6ec3ae197e0', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 219, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 221, 'modified_subnode' => 1311065053, 'node_id' => 221, 'parent_node_id' => 190, 'path_identification_string' => 'company/events', 'path_string' => '/1/2/190/221/', 'priority' => 0, 'remote_id' => 'ae6afe955af19209125961b6c0d9a840', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 220, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 222, 'modified_subnode' => 1311065054, 'node_id' => 222, 'parent_node_id' => 190, 'path_identification_string' => 'company/career', 'path_string' => '/1/2/190/222/', 'priority' => 0, 'remote_id' => '28b7fd81eaf6b9702c9f8ff8268daac9', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 221, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 223, 'modified_subnode' => 1311065054, 'node_id' => 223, 'parent_node_id' => 190, 'path_identification_string' => 'company/investors_relation', 'path_string' => '/1/2/190/223/', 'priority' => 0, 'remote_id' => '627c1c5cdf124dc2f92489e686cd04bb', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 222, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 1, 'is_invisible' => 1, 'main_node_id' => 224, 'modified_subnode' => 1311065055, 'node_id' => 224, 'parent_node_id' => 190, 'path_identification_string' => 'company/contact', 'path_string' => '/1/2/190/224/', 'priority' => 0, 'remote_id' => 'daa3004ede25b0130ca5bef83c1922c3', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 223, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 1, 'main_node_id' => 225, 'modified_subnode' => 1311065055, 'node_id' => 225, 'parent_node_id' => 224, 'path_identification_string' => 'company/contact/morbi_neque', 'path_string' => '/1/2/190/224/225/', 'priority' => 0, 'remote_id' => '9d2bdcb8b35464ee03651c191ac4d1ff', 'sort_field' => 2, 'sort_order' => 1 ), - array( 'contentobject_id' => 224, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 226, 'modified_subnode' => 1311065055, 'node_id' => 226, 'parent_node_id' => 190, 'path_identification_string' => 'company/company_banner', 'path_string' => '/1/2/190/226/', 'priority' => 0, 'remote_id' => '9ae96e2ecc3a5a79964405273cc8542a', 'sort_field' => 1, 'sort_order' => 1 ), - array( 'contentobject_id' => 225, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 227, 'modified_subnode' => 1311065058, 'node_id' => 227, 'parent_node_id' => 5, 'path_identification_string' => 'users/partners', 'path_string' => '/1/5/227/', 'priority' => 0, 'remote_id' => '5238a276bf8231fbcf8a986cdc82a6a5', 'sort_field' => 1, 'sort_order' => 1 ), - ), - 'eznode_assignment' => array( - array( 'contentobject_id' => 8, 'contentobject_version' => 2, 'from_node_id' => 0, 'id' => 4, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 42, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 5, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 9, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 10, 'contentobject_version' => 2, 'from_node_id' => -1, 'id' => 6, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 44, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 9, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 4, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 7, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 1, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 12, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 8, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 13, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 9, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 41, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 11, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 1, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 11, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 12, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 45, 'contentobject_version' => 1, 'from_node_id' => -1, 'id' => 16, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 1, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 9, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 49, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 27, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 43, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 9, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 50, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 28, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 43, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 9, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 51, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 29, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 43, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 9, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 52, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 30, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 48, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 56, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 34, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 1, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 14, 'contentobject_version' => 3, 'from_node_id' => -1, 'id' => 38, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 13, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 54, 'contentobject_version' => 2, 'from_node_id' => -1, 'id' => 39, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 58, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 57, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 43, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 43, 'parent_remote_id' => '437ef9d0a9b7ae326ec83fa3bb73956d', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 58, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 44, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => 'eaa7f2f48c3f35801961abad12151db4', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 59, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 45, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => 'b8c85fd926d61dab6e68fa1865cee987', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 60, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 46, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => 'c65aba2485585bdd09dfb66afccf645e', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 61, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 47, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => '64bb803471e53898aa38a7c29e482370', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 62, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 48, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => '95e29503817570c6458fa0f37d227306', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 63, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 49, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => 'aa4a1afd9c02d00f2f31186e8a271332', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 64, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 50, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => '93d5115082a23b266613868051b8d803', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 65, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 51, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => '1a8744c6d6be8675f7b27fa8b7b8437d', 'remote_id' => 0, 'sort_field' => 8, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 66, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 52, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 51, 'parent_remote_id' => '31fd28362c18a36cb56223f3609d5d90', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 67, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 53, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => '9cec85d730eec7578190ee95ce5a36f5', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 68, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 54, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 69, 'parent_remote_id' => 'b0b85c15125ca1732e5e528de2717599', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 69, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 55, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 70, 'parent_remote_id' => '087adb763245e0cdcac593fb4a5996cf', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 70, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 56, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 69, 'parent_remote_id' => 'e607aab6e924091909f3def02415bc53', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 71, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 57, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 72, 'parent_remote_id' => '054d9f10c6fa97689c0fc3b2ac412ebd', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 72, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 58, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 72, 'parent_remote_id' => '9801bda46e5f8b9d692e1120d50fc7b3', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 73, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 59, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 72, 'parent_remote_id' => '005067a5eee6505aa0f601cca30681d0', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 74, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 60, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 69, 'parent_remote_id' => '18f14551cc555c094b15a732ccd27fb2', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 75, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 61, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => 'dbc2f3c8716c12f32c379dbf0b1cb133', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 76, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 62, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 77, 'parent_remote_id' => 'bc766fe955437def220a3fa2966a34ee', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 77, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 63, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 78, 'parent_remote_id' => 'f0c2216ecb29600cd8ae93951a0c8f3a', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 78, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 64, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 78, 'parent_remote_id' => 'eaf16bddfd36206dad265aadfbc98f17', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 79, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 65, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 78, 'parent_remote_id' => 'd2a11e56093b77eb7a347229361c3377', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 80, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 66, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 77, 'parent_remote_id' => '17d65b568e3500cf1f8b42bc5de2d12b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 81, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 67, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 82, 'parent_remote_id' => 'ecc4f0e94b05bf10f4f783d660ff0ad0', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 82, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 68, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 82, 'parent_remote_id' => '74280af2cba9002ea9660749225562b6', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 83, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 69, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 82, 'parent_remote_id' => '4e526426523e47aeacf353541284cbf8', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 84, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 70, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => '95f3c4261719ea27ab67f980fbee0694', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 85, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 71, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 86, 'parent_remote_id' => '4a1391d3563d056c9d9ea2653093ae3e', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 86, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 72, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 86, 'parent_remote_id' => '8889727909b5f34b6aa23f7eee32606b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 87, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 73, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 88, 'parent_remote_id' => '1721dbee55639fe280f6a54195f9577c', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 88, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 74, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 88, 'parent_remote_id' => '7fabb9ee5bcb6630a3947d1c5585d995', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 89, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 75, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 88, 'parent_remote_id' => 'a95ae3e4a64f08eb2002b31680fe8989', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 90, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 76, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 86, 'parent_remote_id' => '2acc8bb8f7eda4de2cf74f6dc277661f', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 91, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 77, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 86, 'parent_remote_id' => '6d8cc9831b86d79b8c184507f8e0cbb6', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 92, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 78, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 86, 'parent_remote_id' => '30f12ecd29608eceb20bb903ddb780c7', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 93, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 79, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 86, 'parent_remote_id' => '341e63bdce0f1601519d1b8e82e62766', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 94, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 80, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => '0d55a3f510cc7cd9a7b8eb838f50ff5c', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 95, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 81, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 96, 'parent_remote_id' => '89ce8eed8fa06c4105fd612aa83d87d6', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 96, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 82, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 97, 'parent_remote_id' => '78f3b9a9268c7b0206bf1c4d39211495', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 97, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 83, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 98, 'parent_remote_id' => 'a1f9b32547e58064e645388512c16a39', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 98, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 84, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 96, 'parent_remote_id' => '0f9c7380f8af1f29f1017e412bdd4016', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 99, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 85, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 100, 'parent_remote_id' => '036394ec8b160f0782bec2dda452d798', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 100, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 86, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 101, 'parent_remote_id' => '78da6a4fae1c2eaabe1dbe7af818d970', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 101, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 87, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 96, 'parent_remote_id' => '06cdeb27be466ea8330de5df16144263', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 102, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 88, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 103, 'parent_remote_id' => 'ab30399896b8e54442c3a619ba7eeecb', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 103, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 89, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 96, 'parent_remote_id' => 'ca2ae9d0f0322798f632e896325f22c3', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 104, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 90, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 96, 'parent_remote_id' => '7ba1d48c0151bae62366095ef6f64c28', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 105, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 91, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => 'e81507d3446726ebd7361352fef5fad3', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 106, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 92, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '51278360f39d5b8ce1d9249953f4de98', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 107, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 93, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '33151e24acea9c837d2b9fc52e03b1de', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 108, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 94, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '6b3bcab0f149c5acc2e3728df7c66b73', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 109, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 95, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '28f9dfe5c0680955eec7a2dec4ebc642', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 110, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 96, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '191faba79dc108a19893944befa50d94', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 111, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 97, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '8679b5fa3ad0ec216293419ab9834e44', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 112, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 98, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '7f32cdefd0cf55b966a44aa52181e30d', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 113, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 99, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '29db3c1fcdb497e5cb2c74eb85c0906a', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 114, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 100, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '96e30009f712a0315217fed93eba6a18', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 115, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 101, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '3a6b87470a25b19a6ad15caa5e24e719', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 116, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 102, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => 'd0bc77a21920b63543d7b0accab81b24', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 117, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 103, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => 'c75b4b4b870e0e3611e19e1323103282', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 118, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 104, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '262d8c936d3757ff81e7bb49392b703f', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 119, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 105, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => '220d4e10bf4619525c3165823079482c', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 120, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 106, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => 'f13a938ce2c79a9d438548299220d6dd', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 121, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 107, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => '04f5e08293954b1851a4dd1cbd976cff', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 122, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 108, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => 'dc80ae3d9de55855a16218b032062c62', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 123, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 109, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => '4cb6d4015dd80474074043ab28f96e36', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 124, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 110, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => '74ce7d6cbae81d90592b4a8a242a284a', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 125, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 111, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => '6f76d2f7e5812acf4f7c5c70258f4f7e', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 126, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 112, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => '5ba07103968a6bb3f05c275c212440f6', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 127, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 113, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '3579b6c5cd166d5137eada55274892d3', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 128, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 114, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 129, 'parent_remote_id' => 'e38a43ce35832e5af2df4f1e9272b926', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 129, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 115, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 129, 'parent_remote_id' => '1ba7889f63e32acf70f66349ce1a8953', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 130, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 116, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '19ae9fcb7334a0e407b2781920247122', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 131, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 117, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '423b6d20325263abaa2012536cc736a4', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 132, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 118, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => '07f0de334c83d631a59defe61f1a7cbf', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 133, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 119, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 134, 'parent_remote_id' => 'a7ca1a8878210a9978df0b00e9184b20', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 134, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 120, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 134, 'parent_remote_id' => '12c86be2151c426b990d47efd023cc40', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 135, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 121, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 134, 'parent_remote_id' => 'ff4473534c7cef8212a11feab57a5f0e', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 136, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 122, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => 'e0a1b1b2a3d248bf8d43a83581c22ae8', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 137, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 123, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 138, 'parent_remote_id' => '8e6f0ba5195410b03a84ef3fdcc3ff0b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 138, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 124, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 138, 'parent_remote_id' => 'bc416b97e1592c7a396f6f5f85e131ac', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 139, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 125, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => 'facfd2587054ea863cbe53768da04a47', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 140, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 126, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 141, 'parent_remote_id' => 'd159e8d4d590e481b06ecb441c0f14dc', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 141, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 127, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 141, 'parent_remote_id' => '7457db5919dd349bd878b60592fc009b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 142, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 128, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 141, 'parent_remote_id' => 'ebe1e9d1242a3aeedfda25da7cc25564', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 143, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 129, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 141, 'parent_remote_id' => 'b3acf4c0f2df8977f1c6932725339a1d', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 144, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 130, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => 'a324a48fea49eb3e6826b3295dc5f413', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 145, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 131, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => '8c0133434d7bea077a614e1a85fca5b7', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 146, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 132, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 147, 'parent_remote_id' => '46653e0e5fa60ab86788cad8084a759d', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 147, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 133, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => '9b17e8d0b2cee501b75f7b481155e64f', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 148, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 134, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => '3983d3aa7604f45a8ecbb953a98d646f', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 149, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 135, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 150, 'parent_remote_id' => '92756622f207c0b1902ca156947a77de', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 150, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 136, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => '60adbb1e37a6524246d3af372366754f', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 151, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 137, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => 'f4bf2e6c1cf75e15b15f0123a82778a1', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 152, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 138, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 153, 'parent_remote_id' => '1b95356b68bd59ef32b0fd7bb2c29130', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 153, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 139, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 153, 'parent_remote_id' => '94b7059eb5e0fe667f7c63eadd53a62b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 154, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 140, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => '0c523dfd6b1eccbfb0d1bfe632ee7411', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 3, 'is_hidden' => 0 ), - array( 'contentobject_id' => 155, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 141, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 156, 'parent_remote_id' => 'f5db8acf1150afc15eb514d380e3855d', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 156, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 142, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 157, 'parent_remote_id' => '6366a99ac1cec2133e87fdd805d108c8', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 157, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 143, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 157, 'parent_remote_id' => '7ee406cb6e5f0b2a29bc9f97f5a5b9db', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 158, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 144, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 156, 'parent_remote_id' => '007109e7f353c86dfd48d844cb18ce73', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 159, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 145, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 160, 'parent_remote_id' => '63769c39380a45a731aa44068c0f4e33', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 160, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 146, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 160, 'parent_remote_id' => 'f761918d15e356e50ce94294cad79765', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 161, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 147, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 160, 'parent_remote_id' => '68187b44ef9051a4db10bd9bb7f228a4', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 162, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 148, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 156, 'parent_remote_id' => '472e9bc321148baa71d2070063f24bb9', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 163, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 149, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 156, 'parent_remote_id' => 'b1a68df10cdf379cf78999c8012db679', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 164, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 150, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 165, 'parent_remote_id' => 'e9ee6724fffb012f918300de66af27e1', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 165, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 151, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => 'c4604fb2e100a6681a4f53fbe6e5eeae', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 4, 'is_hidden' => 0 ), - array( 'contentobject_id' => 166, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 152, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 167, 'parent_remote_id' => '5e6762a083ab4c0357bad163902ba8f7', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 167, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 153, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 168, 'parent_remote_id' => '2549eee03064d55ae7fc84fe41ae1ef6', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 168, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 154, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 168, 'parent_remote_id' => '7bfca4bdde619ac9104e235629e2dbc7', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 169, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 155, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 168, 'parent_remote_id' => '81fd930afe94962395074b762592a356', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 170, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 156, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 167, 'parent_remote_id' => '8b570ac03318b7e29eb5fd1bae15efde', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 171, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 157, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 172, 'parent_remote_id' => '4329dcc6a8d441aa7253660512dfd5b6', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 172, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 158, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 173, 'parent_remote_id' => '6cf7b9370a0d9800ed49ba3be13b6be5', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 173, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 159, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 173, 'parent_remote_id' => 'f71191fcce35c83530cddde467a908b2', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 174, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 160, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 172, 'parent_remote_id' => '2d3cf4fe8d439e16e0ed75ef947b5e3d', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 175, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 161, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 176, 'parent_remote_id' => '49c9ad954328ab5f5a76d580873961a4', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 176, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 162, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 167, 'parent_remote_id' => 'ae32173cedf3a5ec441e3afcadc4abcb', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 177, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 163, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => 'c6c32c884db955ea09338c90eee84746', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 178, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 164, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => 'db191e87b9b6ac22a18742cd32d2b3d4', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 179, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 165, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => '02a6d1506bf437eda1317d0c36a8545b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 180, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 166, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => '40d743025e4ec4d813bd864dad08e0cc', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 181, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 167, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => 'eb2a8ef7d4b1fb8ffb68f0764b615de1', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 182, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 168, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => '9e45f3ee0f15995b670fb3a117b6ee49', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 183, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 169, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => 'f368c4ae469c596344e15fca8c7370c2', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 184, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 170, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => '7e153b75dd0ee0476ac7463d4a3daaef', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 185, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 171, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 167, 'parent_remote_id' => '09fb4147e4b124b9770a527bdd66799c', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 186, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 172, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 187, 'parent_remote_id' => '8f73d6d4ffca33c619d2f2645340ccc0', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 187, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 173, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 187, 'parent_remote_id' => '5cdc175d45aeb3fa054a95f40df56e79', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 188, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 174, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => 'e060ff40aa17eb21dc1e4595c9a5eb9a', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 6, 'is_hidden' => 0 ), - array( 'contentobject_id' => 189, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 175, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => '7bf290af79b3b51e6ccc1f95f321618c', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 190, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 176, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => '9f804eee139dcee343dbe4175d14654c', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 191, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 177, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '10d57795c986ca61689e6c2c11382eb3', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 192, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 178, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 193, 'parent_remote_id' => '87417129b080d24d335eb2653dd1a40c', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 193, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 179, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => 'fd56d839df6e2b52055d4a62f4df7c3e', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 194, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 180, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => 'ab2f2093f16c56ac2e61f40f25d2dbe0', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 195, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 181, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => '185a1737e9012e7c3025df876bbb4f9b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 196, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 182, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => 'efdca2f6ddd2c8befc283ae3465b9acd', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 197, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 183, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => 'f81b0e4c31ea13d94b9838e9e7e7ad7e', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 198, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 184, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => '666efc49c267cddf55ca707408f4ac42', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 199, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 185, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => 'afb51a5f898c549c9e1f35afc75d66f3', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 200, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 186, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => '94b71866ede0d2776c140e85a823d31d', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 201, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 187, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => 'e74475a240fb7061b40c75f5e6fcd52c', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 202, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 188, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '42a3b07037390457ad0f2c65494bfd97', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 203, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 189, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => '6da1a82ba3d962d2ad953fff8ee26362', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 204, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 190, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => 'b284abd88bdcb1887a86e40304ffc66f', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 205, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 191, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => 'e3400bb44aae5fcb416156e7450f3f32', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 206, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 192, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => '2b2b27bbaaec688aaec1a5d39b4b13ae', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 207, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 193, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => '37e5feed6574d1d8da4b4531569a27a2', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 208, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 194, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => '5625695a66034f53b9b6e14029e06e80', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 209, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 195, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => '824aba4122ee51d9638847e55642feec', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 210, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 196, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => '73e95051b800ce051c3851c60807787b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 211, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 197, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '3f921d3c490a6c17870b1f01fcfbceed', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 212, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 198, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 213, 'parent_remote_id' => '97f7b391f1522bf404ff672ac338b91c', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 213, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 199, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 213, 'parent_remote_id' => 'c5549bf68962a5384bd2225931cba302', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 214, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 200, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '54adfd9f7c80638f5560eb3f0618c57b', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 215, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 201, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => 'a7dbafbf963821b71e5522c21e09d9dd', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 216, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 202, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '61e3b15f020590df0759b779e5f1fb9d', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 217, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 203, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '42560fa30a55ac5ddf1f76a52d44a613', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 218, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 204, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '416c28c9ba34a9114381a6ec3ae197e0', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 219, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 205, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => 'ae6afe955af19209125961b6c0d9a840', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 220, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 206, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => '28b7fd81eaf6b9702c9f8ff8268daac9', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 221, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 207, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => '627c1c5cdf124dc2f92489e686cd04bb', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 222, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 208, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => 'daa3004ede25b0130ca5bef83c1922c3', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 223, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 209, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 224, 'parent_remote_id' => '9d2bdcb8b35464ee03651c191ac4d1ff', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 224, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 210, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => '9ae96e2ecc3a5a79964405273cc8542a', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 14, 'contentobject_version' => 4, 'from_node_id' => -1, 'id' => 211, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 13, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 225, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 212, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 11, 'contentobject_version' => 2, 'from_node_id' => -1, 'id' => 213, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - array( 'contentobject_id' => 226, 'contentobject_version' => 1, 'from_node_id' => -1, 'id' => 214, 'is_main' => 1, 'op_code' => 3, 'parent_node' => 77, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0 ), - ), - 'ezcontentobject' => array( - ['id' => 226, 'status' => 0], - ['id' => 67, 'status' => 0], - ['id' => 68, 'status' => 0], - ['id' => 69, 'status' => 0], - ['id' => 70, 'status' => 0], - ['id' => 71, 'status' => 0], - ['id' => 72, 'status' => 0], - ['id' => 73, 'status' => 0], - ['id' => 74, 'status' => 0], - ), -); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/MapperTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/MapperTest.php deleted file mode 100644 index acc649980e..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/MapperTest.php +++ /dev/null @@ -1,180 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Location; - -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; -use eZ\Publish\SPI\Persistence\Content\Location\Trashed; - -/** - * Test case for Location\Mapper. - */ -class MapperTest extends TestCase -{ - /** - * Location data from the database. - * - * @var array - */ - protected $locationRow = [ - 'node_id' => 77, - 'priority' => 0, - 'is_hidden' => 0, - 'is_invisible' => 0, - 'remote_id' => 'dbc2f3c8716c12f32c379dbf0b1cb133', - 'contentobject_id' => 75, - 'contentobject_version' => 1, - 'parent_node_id' => 2, - 'path_identification_string' => 'solutions', - 'path_string' => '/1/2/77/', - 'modified_subnode' => 1311065017, - 'main_node_id' => 77, - 'depth' => 2, - 'sort_field' => 2, - 'sort_order' => 1, - ]; - - /** - * Expected Location object properties values. - * - * @var array - */ - protected $locationValues = [ - 'id' => 77, - 'priority' => 0, - 'hidden' => false, - 'invisible' => false, - 'remoteId' => 'dbc2f3c8716c12f32c379dbf0b1cb133', - 'contentId' => 75, - 'parentId' => 2, - 'pathIdentificationString' => 'solutions', - 'pathString' => '/1/2/77/', - 'depth' => 2, - 'sortField' => 2, - 'sortOrder' => 1, - ]; - - /** - * Expected Location CreateStruct object properties values. - * - * @var array - */ - protected $locationCreateStructValues = [ - 'contentId' => 75, - 'contentVersion' => 1, - 'hidden' => 0, - 'invisible' => 0, - 'parentId' => 2, - 'pathIdentificationString' => 'solutions', - 'priority' => 0, - 'sortField' => 2, - 'sortOrder' => 1, - ]; - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper::createLocationFromRow - */ - public function testCreateLocationFromRow() - { - $mapper = new Mapper(); - - $location = $mapper->createLocationFromRow( - $this->locationRow - ); - - $this->assertPropertiesCorrect( - $this->locationValues, - $location - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper::createLocationsFromRows - */ - public function testCreateLocationsFromRows() - { - $inputRows = []; - for ($i = 0; $i < 3; ++$i) { - $row = $this->locationRow; - $row['node_id'] += $i; - $inputRows[] = $row; - } - - $mapper = new Mapper(); - - $locations = $mapper->createLocationsFromRows($inputRows); - - $this->assertCount(3, $locations); - foreach ($locations as $location) { - $this->assertInstanceOf( - SPILocation::class, - $location - ); - } - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper::createLocationFromRow - */ - public function testCreateTrashedFromRow() - { - $mapper = new Mapper(); - - $location = $mapper->createLocationFromRow( - $this->locationRow, - null, - new Trashed() - ); - - $this->assertTrue($location instanceof Trashed); - $this->assertPropertiesCorrect( - $this->locationValues, - $location - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper::createLocationFromRow - */ - public function testCreateLocationFromRowWithPrefix() - { - $prefix = 'some_prefix_'; - - $data = []; - foreach ($this->locationRow as $key => $val) { - $data[$prefix . $key] = $val; - } - - $mapper = new Mapper(); - - $location = $mapper->createLocationFromRow($data, $prefix); - - $this->assertPropertiesCorrect( - $this->locationValues, - $location - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper::getLocationCreateStruct - */ - public function testGetLocationCreateStruct() - { - $mapper = new Mapper(); - - $createStruct = $mapper->getLocationCreateStruct( - $this->locationRow - ); - - $this->assertNotEquals($this->locationRow['remote_id'], $createStruct->remoteId); - $this->assertPropertiesCorrect( - $this->locationCreateStructValues, - $createStruct - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/TrashHandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/TrashHandlerTest.php deleted file mode 100644 index ae7d8d57c7..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/TrashHandlerTest.php +++ /dev/null @@ -1,609 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Location; - -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResultList; -use eZ\Publish\Core\Persistence\Legacy\Content as CoreContent; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Content\Location\Trash\TrashResult; -use eZ\Publish\SPI\Persistence\Content\Location\Trashed; - -/** - * Test case for TrashHandlerTest. - */ -class TrashHandlerTest extends TestCase -{ - /** - * Mocked location handler instance. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler - */ - protected $locationHandler; - - /** - * Mocked location gateway instance. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway - */ - protected $locationGateway; - - /** - * Mocked location mapper instance. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper - */ - protected $locationMapper; - - /** - * Mocked content handler instance. - * - * @var \PHPUnit\Framework\MockObject\MockObject - */ - protected $contentHandler; - - protected function getTrashHandler() - { - return new Handler( - $this->locationHandler = $this->createMock(CoreContent\Location\Handler::class), - $this->locationGateway = $this->createMock(CoreContent\Location\Gateway::class), - $this->locationMapper = $this->createMock(CoreContent\Location\Mapper::class), - $this->contentHandler = $this->createMock(CoreContent\Handler::class) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler::trashSubtree - */ - public function testTrashSubtree() - { - $handler = $this->getTrashHandler(); - - $this->locationGateway - ->expects($this->at(0)) - ->method('getSubtreeContent') - ->with(20) - ->will( - $this->returnValue( - [ - [ - 'contentobject_id' => 10, - 'node_id' => 20, - 'main_node_id' => 30, - 'parent_node_id' => 40, - ], - [ - 'contentobject_id' => 11, - 'node_id' => 21, - 'main_node_id' => 31, - 'parent_node_id' => 41, - ], - ] - ) - ); - - $this->locationGateway - ->expects($this->at(1)) - ->method('countLocationsByContentId') - ->with(10) - ->will($this->returnValue(1)); - - $this->locationGateway - ->expects($this->at(2)) - ->method('trashLocation') - ->with(20); - - $this->locationGateway - ->expects($this->at(3)) - ->method('countLocationsByContentId') - ->with(11) - ->will($this->returnValue(2)); - - $this->locationGateway - ->expects($this->at(4)) - ->method('removeLocation') - ->with(21); - - $this->locationHandler - ->expects($this->once()) - ->method('markSubtreeModified') - ->with(40); - - $this->locationGateway - ->expects($this->at(5)) - ->method('loadTrashByLocation') - ->with(20) - ->will($this->returnValue($array = ['data…'])); - - $this->locationMapper - ->expects($this->once()) - ->method('createLocationFromRow') - ->with($array, null, new Trashed()) - ->will($this->returnValue(new Trashed(['id' => 20]))); - - $trashedObject = $handler->trashSubtree(20); - self::assertInstanceOf(Trashed::class, $trashedObject); - self::assertSame(20, $trashedObject->id); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler::trashSubtree - */ - public function testTrashSubtreeReturnsNull() - { - $handler = $this->getTrashHandler(); - - $this->locationGateway - ->expects($this->at(0)) - ->method('getSubtreeContent') - ->with(20) - ->will( - $this->returnValue( - [ - [ - 'contentobject_id' => 10, - 'node_id' => 20, - 'main_node_id' => 30, - 'parent_node_id' => 40, - ], - [ - 'contentobject_id' => 11, - 'node_id' => 21, - 'main_node_id' => 31, - 'parent_node_id' => 41, - ], - ] - ) - ); - - $this->locationGateway - ->expects($this->at(1)) - ->method('countLocationsByContentId') - ->with(10) - ->will($this->returnValue(2)); - - $this->locationGateway - ->expects($this->at(2)) - ->method('removeLocation') - ->with(20); - - $this->locationGateway - ->expects($this->at(3)) - ->method('countLocationsByContentId') - ->with(11) - ->will($this->returnValue(1)); - - $this->locationGateway - ->expects($this->at(4)) - ->method('trashLocation') - ->with(21); - - $this->locationHandler - ->expects($this->once()) - ->method('markSubtreeModified') - ->with(40); - - $returnValue = $handler->trashSubtree(20); - self::assertNull($returnValue); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler::trashSubtree - */ - public function testTrashSubtreeUpdatesMainLocation() - { - $handler = $this->getTrashHandler(); - - $this->locationGateway - ->expects($this->at(0)) - ->method('getSubtreeContent') - ->with(20) - ->will( - $this->returnValue( - [ - [ - 'contentobject_id' => 10, - 'node_id' => 20, - 'main_node_id' => 30, - 'parent_node_id' => 40, - ], - [ - 'contentobject_id' => 11, - 'node_id' => 21, - 'main_node_id' => 21, - 'parent_node_id' => 41, - ], - ] - ) - ); - - $this->locationGateway - ->expects($this->at(1)) - ->method('countLocationsByContentId') - ->with(10) - ->will($this->returnValue(1)); - - $this->locationGateway - ->expects($this->at(2)) - ->method('trashLocation') - ->with(20); - - $this->locationGateway - ->expects($this->at(3)) - ->method('countLocationsByContentId') - ->with(11) - ->will($this->returnValue(2)); - - $this->locationGateway - ->expects($this->at(4)) - ->method('removeLocation') - ->with(21); - - $this->locationGateway - ->expects($this->at(5)) - ->method('getFallbackMainNodeData') - ->with(11, 21) - ->will( - $this->returnValue( - [ - 'node_id' => 100, - 'contentobject_version' => 101, - 'parent_node_id' => 102, - ] - ) - ); - - $this->locationHandler - ->expects($this->once()) - ->method('changeMainLocation') - ->with(11, 100, 101, 102); - - $this->locationHandler - ->expects($this->once()) - ->method('markSubtreeModified') - ->with(40); - - $this->locationGateway - ->expects($this->at(6)) - ->method('loadTrashByLocation') - ->with(20) - ->will($this->returnValue($array = ['data…'])); - - $this->locationMapper - ->expects($this->once()) - ->method('createLocationFromRow') - ->with($array, null, new Trashed()) - ->will($this->returnValue(new Trashed(['id' => 20]))); - - $trashedObject = $handler->trashSubtree(20); - self::assertInstanceOf(Trashed::class, $trashedObject); - self::assertSame(20, $trashedObject->id); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler::recover - */ - public function testRecover() - { - $handler = $this->getTrashHandler(); - - $this->locationGateway - ->expects($this->at(0)) - ->method('untrashLocation') - ->with(69, 23) - ->will( - $this->returnValue( - new Trashed(['id' => 70]) - ) - ); - - self::assertSame(70, $handler->recover(69, 23)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler::loadTrashItem - */ - public function testLoadTrashItem() - { - $handler = $this->getTrashHandler(); - - $this->locationGateway - ->expects($this->at(0)) - ->method('loadTrashByLocation') - ->with(69) - ->will($this->returnValue($array = ['data…'])); - - $this->locationMapper - ->expects($this->at(0)) - ->method('createLocationFromRow') - ->with($array, null, new Trashed()); - - $handler->loadTrashItem(69); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler::emptyTrash - */ - public function testEmptyTrash(): void - { - $handler = $this->getTrashHandler(); - - $expectedTrashed = [ - [ - 'node_id' => 69, - 'path_string' => '/1/2/69/', - 'contentobject_id' => 67, - ], - [ - 'node_id' => 70, - 'path_string' => '/1/2/70/', - 'contentobject_id' => 68, - ], - ]; - - // Index for locationGateway calls - $i = 0; - // Index for contentHandler calls - $iContent = 0; - // Index for locationMapper calls - $iLocation = 0; - - $this->locationGateway - ->expects(self::at($i++)) - ->method('countTrashed') - ->willReturn(2); - - $this->locationGateway - ->expects(self::at($i++)) - ->method('listTrashed') - ->willReturn($expectedTrashed); - - $trashedItemIds = []; - $trashedContentIds = []; - - foreach ($expectedTrashed as $trashedElement) { - $this->locationMapper - ->expects(self::at($iLocation++)) - ->method('createLocationFromRow') - ->willReturn( - new Trashed( - [ - 'id' => $trashedElement['node_id'], - 'contentId' => $trashedElement['contentobject_id'], - 'pathString' => $trashedElement['path_string'], - ] - ) - ); - - $this->contentHandler - ->expects(self::at($iContent++)) - ->method('loadReverseRelations') - ->with($trashedElement['contentobject_id']) - ->willReturn([]); - - $this->locationGateway - ->expects(self::at($i++)) - ->method('removeElementFromTrash') - ->with($trashedElement['node_id']); - - $this->locationGateway - ->expects(self::at($i++)) - ->method('countLocationsByContentId') - ->with($trashedElement['contentobject_id']) - ->willReturn(0); - - $this->contentHandler - ->expects(self::at($iContent++)) - ->method('deleteContent') - ->with($trashedElement['contentobject_id']); - - $trashedItemIds[] = $trashedElement['node_id']; - $trashedContentIds[] = $trashedElement['contentobject_id']; - } - - $returnValue = $handler->emptyTrash(); - - self::assertInstanceOf(TrashItemDeleteResultList::class, $returnValue); - - foreach ($returnValue->items as $key => $trashItemDeleteResult) { - self::assertEquals($trashItemDeleteResult->trashItemId, $trashedItemIds[$key]); - self::assertEquals($trashItemDeleteResult->contentId, $trashedContentIds[$key]); - self::assertTrue($trashItemDeleteResult->contentRemoved); - } - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler::deleteTrashItem - */ - public function testDeleteTrashItemNoMoreLocations(): void - { - $handler = $this->getTrashHandler(); - - $trashItemId = 69; - $contentId = 67; - - $this->locationGateway - ->expects(self::once()) - ->method('loadTrashByLocation') - ->with($trashItemId) - ->willReturn( - [ - 'node_id' => $trashItemId, - 'contentobject_id' => $contentId, - 'path_string' => '/1/2/69', - ] - ); - - $this->locationMapper - ->expects(self::once()) - ->method('createLocationFromRow') - ->willReturn( - new Trashed( - [ - 'id' => $trashItemId, - 'contentId' => $contentId, - 'pathString' => '/1/2/69', - ] - ) - ); - - $this->contentHandler - ->expects(self::once()) - ->method('loadReverseRelations') - ->with($contentId) - ->willReturn([]); - - $this->locationGateway - ->expects(self::once()) - ->method('removeElementFromTrash') - ->with($trashItemId); - - $this->locationGateway - ->expects(self::once()) - ->method('countLocationsByContentId') - ->with($contentId) - ->willReturn(0); - - $this->contentHandler - ->expects(self::once()) - ->method('deleteContent') - ->with($contentId); - - $trashItemDeleteResult = $handler->deleteTrashItem($trashItemId); - - self::assertInstanceOf(TrashItemDeleteResult::class, $trashItemDeleteResult); - self::assertEquals($trashItemId, $trashItemDeleteResult->trashItemId); - self::assertEquals($contentId, $trashItemDeleteResult->contentId); - self::assertTrue($trashItemDeleteResult->contentRemoved); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler::deleteTrashItem - */ - public function testDeleteTrashItemStillHaveLocations() - { - $handler = $this->getTrashHandler(); - - $trashItemId = 69; - $contentId = 67; - $this->locationGateway - ->expects($this->once()) - ->method('loadTrashByLocation') - ->with($trashItemId) - ->will( - $this->returnValue( - [ - 'node_id' => $trashItemId, - 'contentobject_id' => $contentId, - 'path_string' => '/1/2/69', - ] - ) - ); - - $this->locationMapper - ->expects($this->once()) - ->method('createLocationFromRow') - ->will( - $this->returnValue( - new Trashed( - [ - 'id' => $trashItemId, - 'contentId' => $contentId, - 'pathString' => '/1/2/69', - ] - ) - ) - ); - - $this->locationGateway - ->expects($this->once()) - ->method('removeElementFromTrash') - ->with($trashItemId); - - $this->locationGateway - ->expects($this->once()) - ->method('countLocationsByContentId') - ->with($contentId) - ->will($this->returnValue(1)); - - $this->contentHandler - ->expects($this->never()) - ->method('deleteContent'); - - $trashItemDeleteResult = $handler->deleteTrashItem($trashItemId); - - $this->assertInstanceOf(TrashItemDeleteResult::class, $trashItemDeleteResult); - $this->assertEquals($trashItemId, $trashItemDeleteResult->trashItemId); - $this->assertEquals($contentId, $trashItemDeleteResult->contentId); - $this->assertFalse($trashItemDeleteResult->contentRemoved); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler::findTrashItems - */ - public function testFindTrashItemsWhenEmpty() - { - $handler = $this->getTrashHandler(); - - $this->locationGateway - ->expects($this->once()) - ->method('countTrashed') - ->willReturn(0); - - $this->locationGateway - ->expects($this->never()) - ->method('listTrashed'); - - $this->locationMapper - ->expects($this->never()) - ->method($this->anything()); - - $trashResult = $handler->findTrashItems(); - - $this->assertInstanceOf(TrashResult::class, $trashResult); - $this->assertEquals(0, $trashResult->totalCount); - $this->assertIsArray($trashResult->items); - $this->assertEmpty($trashResult->items); - $this->assertIsIterable($trashResult); - $this->assertCount(0, $trashResult);// Can't assert as empty, however we can count it. - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler::findTrashItems - */ - public function testFindTrashItemsWithLimits() - { - $handler = $this->getTrashHandler(); - - $this->locationGateway - ->expects($this->once()) - ->method('countTrashed') - ->willReturn(2); - - $this->locationGateway - ->expects($this->once()) - ->method('listTrashed') - ->with(1, 1, null) - ->willReturn([['fake data']]); - - $this->locationMapper - ->expects($this->once()) - ->method('createLocationFromRow') - ->with(['fake data']) - ->willReturn(new \stdClass()); - - $trashResult = $handler->findTrashItems(null, 1, 1); - - $this->assertInstanceOf(TrashResult::class, $trashResult); - $this->assertEquals(2, $trashResult->totalCount); - $this->assertIsArray($trashResult->items); - $this->assertCount(1, $trashResult->items); - $this->assertIsIterable($trashResult); - $this->assertCount(1, $trashResult); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/LocationHandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/LocationHandlerTest.php deleted file mode 100644 index 2c83392ba0..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/LocationHandlerTest.php +++ /dev/null @@ -1,719 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content; - -use eZ\Publish\Core\Persistence\Legacy\Content\Handler as ContentHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler as LocationHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler as ObjectStateHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct; -use eZ\Publish\SPI\Persistence\Content\ObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group as ObjectStateGroup; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Test case for LocationHandlerTest. - */ -class LocationHandlerTest extends TestCase -{ - /** - * Mocked location gateway instance. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway - */ - protected $locationGateway; - - /** - * Mocked location mapper instance. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper - */ - protected $locationMapper; - - /** - * Mocked content handler instance. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Handler - */ - protected $contentHandler; - - /** - * Mocked object state handler instance. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler|\PHPUnit\Framework\MockObject\MockObject - */ - protected $objectStateHandler; - - /** - * Mocked Tree handler instance. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler|\PHPUnit\Framework\MockObject\MockObject - */ - protected $treeHandler; - - protected function setUp(): void - { - parent::setUp(); - - $this->locationGateway = $this->createMock(Gateway::class); - $this->locationMapper = $this->createMock(Mapper::class); - $this->treeHandler = $this->createMock(TreeHandler::class); - $this->contentHandler = $this->createMock(ContentHandler::class); - } - - protected function getLocationHandler() - { - return new Handler( - $this->locationGateway, - $this->locationMapper, - $this->contentHandler, - $this->createMock(ObjectStateHandler::class), - $this->treeHandler - ); - } - - public function testLoadLocation() - { - $handler = $this->getLocationHandler(); - - $this->treeHandler - ->expects($this->once()) - ->method('loadLocation') - ->with(77) - ->will($this->returnValue(new \eZ\Publish\SPI\Persistence\Content\Location())); - - $location = $handler->load(77); - - $this->assertTrue($location instanceof \eZ\Publish\SPI\Persistence\Content\Location); - } - - public function testLoadLocationSubtree() - { - $this->locationGateway - ->expects($this->once()) - ->method('getSubtreeContent') - ->with(77, true) - ->will( - $this->returnValue( - [ - [77 => 100], - [78 => 101], - ] - ) - ); - - $this->assertCount(2, $this->getLocationHandler()->loadSubtreeIds(77)); - } - - public function testLoadLocationByRemoteId() - { - $handler = $this->getLocationHandler(); - - $this->locationGateway - ->expects($this->once()) - ->method('getBasicNodeDataByRemoteId') - ->with('abc123') - ->will( - $this->returnValue( - [ - 'node_id' => 77, - ] - ) - ); - - $this->locationMapper - ->expects($this->once()) - ->method('createLocationFromRow') - ->with(['node_id' => 77]) - ->will($this->returnValue(new \eZ\Publish\SPI\Persistence\Content\Location())); - - $location = $handler->loadByRemoteId('abc123'); - - $this->assertTrue($location instanceof \eZ\Publish\SPI\Persistence\Content\Location); - } - - public function testLoadLocationsByContent() - { - $handler = $this->getLocationHandler(); - - $this->locationGateway - ->expects($this->once()) - ->method('loadLocationDataByContent') - ->with(23, 42) - ->will( - $this->returnValue( - [] - ) - ); - - $this->locationMapper - ->expects($this->once()) - ->method('createLocationsFromRows') - ->with([]) - ->will($this->returnValue(['a', 'b'])); - - $locations = $handler->loadLocationsByContent(23, 42); - - $this->assertIsArray($locations); - } - - public function loadParentLocationsForDraftContent() - { - $handler = $this->getLocationHandler(); - - $this->locationGateway - ->expects($this->once()) - ->method('loadParentLocationsDataForDraftContent') - ->with(23) - ->will( - $this->returnValue( - [] - ) - ); - - $this->locationMapper - ->expects($this->once()) - ->method('createLocationsFromRows') - ->with([]) - ->will($this->returnValue(['a', 'b'])); - - $locations = $handler->loadParentLocationsForDraftContent(23); - - $this->assertIsArray($locations); - } - - public function testMoveSubtree() - { - $handler = $this->getLocationHandler(); - - $sourceData = [ - 'node_id' => 69, - 'path_string' => '/1/2/69/', - 'parent_node_id' => 2, - 'contentobject_id' => 67, - ]; - $this->locationGateway - ->expects($this->at(0)) - ->method('getBasicNodeData') - ->with(69) - ->will($this->returnValue($sourceData)); - - $destinationData = [ - 'node_id' => 77, - 'path_string' => '/1/2/77/', - 'contentobject_id' => 68, - ]; - $this->locationGateway - ->expects($this->at(1)) - ->method('getBasicNodeData') - ->with(77) - ->will($this->returnValue($destinationData)); - - $this->locationGateway - ->expects($this->once()) - ->method('moveSubtreeNodes') - ->with($sourceData, $destinationData); - - $this->locationGateway - ->expects($this->once()) - ->method('updateNodeAssignment') - ->with(67, 2, 77, 5); - - $this->treeHandler - ->expects($this->at(0)) - ->method('loadLocation') - ->with($sourceData['node_id']) - ->will($this->returnValue( - new Location( - [ - 'id' => $sourceData['node_id'], - 'contentId' => $sourceData['contentobject_id'], - ] - ) - )); - - $this->treeHandler - ->expects($this->at(1)) - ->method('loadLocation') - ->with($destinationData['node_id']) - ->will($this->returnValue(new Location(['contentId' => $destinationData['contentobject_id']]))); - - $this->contentHandler - ->expects($this->at(0)) - ->method('loadContentInfo') - ->with($destinationData['contentobject_id']) - ->will($this->returnValue(new ContentInfo(['sectionId' => 12345]))); - - $this->contentHandler - ->expects($this->at(1)) - ->method('loadContentInfo') - ->with($sourceData['contentobject_id']) - ->will($this->returnValue(new ContentInfo(['mainLocationId' => 69]))); - - $this->treeHandler - ->expects($this->once()) - ->method('setSectionForSubtree') - ->with(69, 12345); - - $handler->move(69, 77); - } - - public function testHideUpdateHidden() - { - $handler = $this->getLocationHandler(); - - $this->locationGateway - ->expects($this->at(0)) - ->method('getBasicNodeData') - ->with(69) - ->will( - $this->returnValue( - [ - 'node_id' => 69, - 'path_string' => '/1/2/69/', - 'contentobject_id' => 67, - ] - ) - ); - - $this->locationGateway - ->expects($this->once()) - ->method('hideSubtree') - ->with('/1/2/69/'); - - $handler->hide(69); - } - - /** - * @depends testHideUpdateHidden - */ - public function testHideUnhideUpdateHidden() - { - $handler = $this->getLocationHandler(); - - $this->locationGateway - ->expects($this->at(0)) - ->method('getBasicNodeData') - ->with(69) - ->will( - $this->returnValue( - [ - 'node_id' => 69, - 'path_string' => '/1/2/69/', - 'contentobject_id' => 67, - ] - ) - ); - - $this->locationGateway - ->expects($this->once()) - ->method('unhideSubtree') - ->with('/1/2/69/'); - - $handler->unhide(69); - } - - public function testSwapLocations() - { - $handler = $this->getLocationHandler(); - - $this->locationGateway - ->expects($this->once()) - ->method('swap') - ->with(70, 78); - - $handler->swap(70, 78); - } - - public function testCreateLocation() - { - $handler = $this->getLocationHandler(); - - $createStruct = new CreateStruct(); - $createStruct->parentId = 77; - $spiLocation = new Location(); - $spiLocation->id = 78; - $spiLocation->parentId = 77; - $spiLocation->pathString = '/1/2/77/78/'; - - $this->locationGateway - ->expects($this->once()) - ->method('getBasicNodeData') - ->with(77) - ->will( - $this->returnValue( - $parentInfo = [ - 'node_id' => 77, - 'path_string' => '/1/2/77/', - ] - ) - ); - - $this->locationGateway - ->expects($this->once()) - ->method('create') - ->with($createStruct, $parentInfo) - ->will($this->returnValue($spiLocation)); - - $this->locationGateway - ->expects($this->once()) - ->method('createNodeAssignment') - ->with($createStruct, 77, 2); - - $handler->create($createStruct); - } - - public function testUpdateLocation() - { - $handler = $this->getLocationHandler(); - - $updateStruct = new UpdateStruct(); - $updateStruct->priority = 77; - - $this->locationGateway - ->expects($this->once()) - ->method('update') - ->with($updateStruct, 23); - - $handler->update($updateStruct, 23); - } - - public function testSetSectionForSubtree() - { - $handler = $this->getLocationHandler(); - - $this->treeHandler - ->expects($this->once()) - ->method('setSectionForSubtree') - ->with(69, 3); - - $handler->setSectionForSubtree(69, 3); - } - - public function testMarkSubtreeModified() - { - $handler = $this->getLocationHandler(); - - $this->locationGateway - ->expects($this->at(0)) - ->method('getBasicNodeData') - ->with(69) - ->will( - $this->returnValue( - [ - 'node_id' => 69, - 'path_string' => '/1/2/69/', - 'contentobject_id' => 67, - ] - ) - ); - - $this->locationGateway - ->expects($this->at(1)) - ->method('updateSubtreeModificationTime') - ->with('/1/2/69/'); - - $handler->markSubtreeModified(69); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler::changeMainLocation - */ - public function testChangeMainLocation() - { - $handler = $this->getLocationHandler(); - - $this->treeHandler - ->expects($this->once()) - ->method('changeMainLocation') - ->with(12, 34); - - $handler->changeMainLocation(12, 34); - } - - /** - * Test for the removeSubtree() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler::removeSubtree - */ - public function testRemoveSubtree() - { - $handler = $this->getLocationHandler(); - - $this->treeHandler - ->expects($this->once()) - ->method('removeSubtree') - ->with(42); - - $handler->removeSubtree(42); - } - - /** - * Test for the copySubtree() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler::copySubtree - */ - public function testCopySubtree() - { - $handler = $this->getPartlyMockedHandler( - [ - 'load', - 'changeMainLocation', - 'setSectionForSubtree', - 'create', - ] - ); - $subtreeContentRows = [ - ['node_id' => 10, 'main_node_id' => 1, 'parent_node_id' => 3, 'contentobject_id' => 21, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_10', 'sort_field' => 2, 'sort_order' => 1], - ['node_id' => 11, 'main_node_id' => 11, 'parent_node_id' => 10, 'contentobject_id' => 211, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_11', 'sort_field' => 2, 'sort_order' => 1], - ['node_id' => 12, 'main_node_id' => 15, 'parent_node_id' => 10, 'contentobject_id' => 215, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_12', 'sort_field' => 2, 'sort_order' => 1], - ['node_id' => 13, 'main_node_id' => 2, 'parent_node_id' => 10, 'contentobject_id' => 22, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_13', 'sort_field' => 2, 'sort_order' => 1], - ['node_id' => 14, 'main_node_id' => 11, 'parent_node_id' => 13, 'contentobject_id' => 211, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_14', 'sort_field' => 2, 'sort_order' => 1], - ['node_id' => 15, 'main_node_id' => 15, 'parent_node_id' => 13, 'contentobject_id' => 215, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_15', 'sort_field' => 2, 'sort_order' => 1], - ['node_id' => 16, 'main_node_id' => 16, 'parent_node_id' => 15, 'contentobject_id' => 216, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_16', 'sort_field' => 2, 'sort_order' => 1], - ]; - $destinationData = ['node_id' => 5, 'main_node_id' => 5, 'parent_node_id' => 4, 'contentobject_id' => 200, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 1, 'path_identification_string' => 'test_destination']; - $mainLocationsMap = [true, true, true, true, 1011, 1012, true]; - $updateMainLocationsMap = [1215 => 1015]; - $offset = 1000; - - $this->locationGateway - ->expects($this->once()) - ->method('getSubtreeContent') - ->with($subtreeContentRows[0]['node_id']) - ->will($this->returnValue($subtreeContentRows)); - $this->locationGateway - ->expects($this->once()) - ->method('getBasicNodeData') - ->with($destinationData['node_id']) - ->will($this->returnValue($destinationData)); - - $objectStateHandlerCall = 0; - $this->objectStateHandler->expects($this->at($objectStateHandlerCall++)) - ->method('loadAllGroups') - ->will( - $this->returnValue( - [ - new ObjectStateGroup(['id' => 10]), - new ObjectStateGroup(['id' => 20]), - ] - ) - ); - $this->objectStateHandler->expects($this->at($objectStateHandlerCall++)) - ->method('loadObjectStates') - ->with($this->equalTo(10)) - ->will( - $this->returnValue( - [ - new ObjectState(['id' => 11, 'groupId' => 10]), - new ObjectState(['id' => 12, 'groupId' => 10]), - ] - ) - ); - $this->objectStateHandler->expects($this->at($objectStateHandlerCall++)) - ->method('loadObjectStates') - ->with($this->equalTo(20)) - ->will( - $this->returnValue( - [ - new ObjectState(['id' => 21, 'groupId' => 20]), - new ObjectState(['id' => 22, 'groupId' => 20]), - ] - ) - ); - $defaultObjectStates = [ - new ObjectState(['id' => 11, 'groupId' => 10]), - new ObjectState(['id' => 21, 'groupId' => 20]), - ]; - - $contentIds = array_values( - array_unique( - array_column($subtreeContentRows, 'contentobject_id') - ) - ); - foreach ($contentIds as $index => $contentId) { - $this->contentHandler - ->expects($this->at($index * 2)) - ->method('copy') - ->with($contentId, 1) - ->will( - $this->returnValue( - new Content( - [ - 'versionInfo' => new VersionInfo( - [ - 'contentInfo' => new ContentInfo( - [ - 'id' => $contentId + $offset, - 'currentVersionNo' => 1, - ] - ), - ] - ), - ] - ) - ) - ); - - foreach ($defaultObjectStates as $objectState) { - $this->objectStateHandler->expects($this->at($objectStateHandlerCall++)) - ->method('setContentState') - ->with( - $contentId + $offset, - $objectState->groupId, - $objectState->id - ); - } - - $this->contentHandler - ->expects($this->at($index * 2 + 1)) - ->method('publish') - ->with( - $contentId + $offset, - 1, - $this->isInstanceOf(Content\MetadataUpdateStruct::class) - ) - ->will( - $this->returnValue( - new Content( - [ - 'versionInfo' => new VersionInfo( - [ - 'contentInfo' => new ContentInfo( - [ - 'id' => ($contentId + $offset), - ] - ), - ] - ), - ] - ) - ) - ); - } - $lastContentHandlerIndex = $index * 2 + 1; - - $pathStrings = [$destinationData['node_id'] => $destinationData['path_identification_string']]; - foreach ($subtreeContentRows as $index => $row) { - $mapper = new Mapper(); - $createStruct = $mapper->getLocationCreateStruct($row); - $this->locationMapper - ->expects($this->at($index)) - ->method('getLocationCreateStruct') - ->with($row) - ->will($this->returnValue($createStruct)); - - $createStruct = clone $createStruct; - $createStruct->contentId = $createStruct->contentId + $offset; - $createStruct->parentId = $index === 0 ? $destinationData['node_id'] : $createStruct->parentId + $offset; - $createStruct->invisible = true; - $createStruct->mainLocationId = $mainLocationsMap[$index]; - $createStruct->pathIdentificationString = $pathStrings[$createStruct->parentId] . '/' . $row['path_identification_string']; - $pathStrings[$row['node_id'] + $offset] = $createStruct->pathIdentificationString; - $handler - ->expects($this->at($index)) - ->method('create') - ->with($createStruct) - ->will( - $this->returnValue( - new Location( - [ - 'id' => $row['node_id'] + $offset, - 'contentId' => $row['contentobject_id'], - 'hidden' => false, - 'invisible' => true, - 'pathIdentificationString' => $createStruct->pathIdentificationString, - ] - ) - ) - ); - } - - foreach ($updateMainLocationsMap as $contentId => $locationId) { - $handler - ->expects($this->any()) - ->method('changeMainLocation') - ->with($contentId, $locationId); - } - - $handler - ->expects($this->once()) - ->method('load') - ->with($destinationData['node_id']) - ->will($this->returnValue(new Location(['contentId' => $destinationData['contentobject_id']]))); - - $this->contentHandler - ->expects($this->at($lastContentHandlerIndex + 1)) - ->method('loadContentInfo') - ->with($destinationData['contentobject_id']) - ->will($this->returnValue(new ContentInfo(['sectionId' => 12345]))); - - $this->contentHandler - ->expects($this->at($lastContentHandlerIndex + 2)) - ->method('loadContentInfo') - ->with(21) - ->will($this->returnValue(new ContentInfo(['mainLocationId' => 1010]))); - - $handler - ->expects($this->once()) - ->method('setSectionForSubtree') - ->with($subtreeContentRows[0]['node_id'] + $offset, 12345); - - $handler->copySubtree( - $subtreeContentRows[0]['node_id'], - $destinationData['node_id'] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location::countLocationsByContent - */ - public function testCountLocationsByContent(): void - { - $handler = $this->getLocationHandler(); - - $contentId = 41; - - $this->locationGateway - ->expects(self::once()) - ->method('countLocationsByContentId') - ->with($contentId); - - $handler->countLocationsByContent($contentId); - } - - /** - * Returns the handler to test with $methods mocked. - * - * @param string[] $methods - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler - */ - protected function getPartlyMockedHandler(array $methods) - { - return $this->getMockBuilder(LocationHandler::class) - ->setMethods($methods) - ->setConstructorArgs( - [ - $this->locationGateway = $this->createMock(Gateway::class), - $this->locationMapper = $this->createMock(Mapper::class), - $this->contentHandler = $this->createMock(ContentHandler::class), - $this->objectStateHandler = $this->createMock(ObjectStateHandler::class), - $this->treeHandler = $this->createMock(TreeHandler::class), - ] - ) - ->getMock(); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/MapperTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/MapperTest.php deleted file mode 100644 index f4e59b9b9b..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/MapperTest.php +++ /dev/null @@ -1,825 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content; - -use function count; -use eZ\Publish\API\Repository\Values\Content\Relation as RelationValue; -use eZ\Publish\Core\Persistence\Legacy\Bookmark\Handler; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry as Registry; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Language; -use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct as LocationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Relation as SPIRelation; -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use Ibexa\Core\Persistence\Legacy\Content\Mapper\ResolveVirtualFieldSubscriber; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; - -/** - * Test case for Mapper. - */ -class MapperTest extends LanguageAwareTestCase -{ - /** - * Value converter registry mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry - */ - protected $valueConverterRegistryMock; - - /** - * Returns a eZ\Publish\SPI\Persistence\Content\CreateStruct fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\CreateStruct - */ - protected function getCreateStructFixture() - { - $struct = new CreateStruct(); - - $struct->name = 'Content name'; - $struct->typeId = 23; - $struct->sectionId = 42; - $struct->ownerId = 13; - $struct->initialLanguageId = 2; - $struct->locations = [ - new LocationCreateStruct( - ['parentId' => 2] - ), - new LocationCreateStruct( - ['parentId' => 3] - ), - new LocationCreateStruct( - ['parentId' => 4] - ), - ]; - $struct->fields = [new Field()]; - - return $struct; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::createVersionInfoForContent - */ - public function testCreateVersionInfoForContent() - { - $content = $this->getFullContentFixture(); - $time = time(); - - $mapper = $this->getMapper(); - - $versionInfo = $mapper->createVersionInfoForContent( - $content, - 1, - 14 - ); - - $this->assertPropertiesCorrect( - [ - 'id' => null, - 'versionNo' => 1, - 'creatorId' => 14, - 'status' => 0, - 'initialLanguageCode' => 'eng-GB', - 'languageCodes' => ['eng-GB'], - ], - $versionInfo - ); - self::assertGreaterThanOrEqual($time, $versionInfo->creationDate); - self::assertGreaterThanOrEqual($time, $versionInfo->modificationDate); - } - - /** - * Returns a Content fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content - */ - protected function getFullContentFixture() - { - $content = new Content(); - - $content->fields = [ - new Field(['languageCode' => 'eng-GB']), - ]; - $content->versionInfo = new VersionInfo( - [ - 'versionNo' => 1, - 'initialLanguageCode' => 'eng-GB', - 'languageCodes' => ['eng-GB'], - ] - ); - - $content->versionInfo->contentInfo = new ContentInfo(); - $content->versionInfo->contentInfo->id = 2342; - $content->versionInfo->contentInfo->contentTypeId = 23; - $content->versionInfo->contentInfo->sectionId = 42; - $content->versionInfo->contentInfo->ownerId = 13; - - return $content; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::convertToStorageValue - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue - */ - public function testConvertToStorageValue() - { - $convMock = $this->createMock(Converter::class); - $convMock->expects($this->once()) - ->method('toStorageValue') - ->with( - $this->isInstanceOf( - FieldValue::class - ), - $this->isInstanceOf( - StorageFieldValue::class - ) - )->will($this->returnValue(new StorageFieldValue())); - - $reg = new Registry(['some-type' => $convMock]); - - $field = new Field(); - $field->type = 'some-type'; - $field->value = new FieldValue(); - - $mapper = new Mapper( - $reg, - $this->getLanguageHandler(), - $this->getContentTypeHandler(), - $this->getEventDispatcher(), - ); - $res = $mapper->convertToStorageValue($field); - - self::assertInstanceOf( - StorageFieldValue::class, - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::extractContentFromRows - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::extractFieldFromRow - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::extractFieldValueFromRow - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue - */ - public function testExtractContentFromRows() - { - $rowsFixture = $this->getContentExtractFixture(); - $nameRowsFixture = $this->getNamesExtractFixture(); - - $contentType = $this->getContentTypeFromRows($rowsFixture); - - $contentTypeHandlerMock = $this->getContentTypeHandler(); - $contentTypeHandlerMock->method('load')->willReturn($contentType); - - $reg = $this->getFieldRegistry([ - 'ezauthor', - 'ezstring', - 'ezboolean', - 'ezimage', - 'ezdatetime', - 'ezkeyword', - ], count($rowsFixture) - 1); - - $mapper = new Mapper( - $reg, - $this->getLanguageHandler(), - $contentTypeHandlerMock, - $this->getEventDispatcher() - ); - $result = $mapper->extractContentFromRows($rowsFixture, $nameRowsFixture); - - $expected = [$this->getContentExtractReference()]; - - self::assertEquals( - $expected, - $result - ); - } - - public function testExtractContentFromRowsWithNewFieldDefinitions(): void - { - $rowsFixture = $this->getContentExtractFixture(); - $nameRowsFixture = $this->getNamesExtractFixture(); - - $contentType = $this->getContentTypeFromRows($rowsFixture); - $contentType->fieldDefinitions[] = new Content\Type\FieldDefinition([ - 'fieldType' => 'eznumber', - ]); - - $contentTypeHandlerMock = $this->getContentTypeHandler(); - $contentTypeHandlerMock->method('load')->willReturn($contentType); - - $reg = $this->getFieldRegistry([ - 'ezauthor', - 'ezstring', - 'ezboolean', - 'ezimage', - 'ezdatetime', - 'ezkeyword', - 'eznumber', - ], count($rowsFixture) - 1); - - $mapper = new Mapper( - $reg, - $this->getLanguageHandler(), - $contentTypeHandlerMock, - $this->getEventDispatcher() - ); - $result = $mapper->extractContentFromRows($rowsFixture, $nameRowsFixture); - - $expectedContent = $this->getContentExtractReference(); - $expectedContent->fields[] = new Field([ - 'type' => 'eznumber', - 'languageCode' => 'eng-US', - 'value' => new FieldValue(), - 'versionNo' => 2, - ]); - - self::assertEquals( - [ - $expectedContent, - ], - $result - ); - } - - public function testExtractContentFromRowsWithRemovedFieldDefinitions(): void - { - $rowsFixture = $this->getContentExtractFixture(); - $nameRowsFixture = $this->getNamesExtractFixture(); - - $contentType = $this->getContentTypeFromRows($rowsFixture); - $contentType->fieldDefinitions = array_filter( - $contentType->fieldDefinitions, - static function (Content\Type\FieldDefinition $fieldDefinition): bool { - // ref. fixtures, ezauthor - return $fieldDefinition->id !== 185; - } - ); - - $contentTypeHandlerMock = $this->getContentTypeHandler(); - $contentTypeHandlerMock->method('load')->willReturn($contentType); - - $reg = $this->getFieldRegistry([ - 'ezstring', - 'ezboolean', - 'ezimage', - 'ezdatetime', - 'ezkeyword', - ], count($rowsFixture) - 2); - - $mapper = new Mapper( - $reg, - $this->getLanguageHandler(), - $contentTypeHandlerMock, - $this->getEventDispatcher() - ); - $result = $mapper->extractContentFromRows($rowsFixture, $nameRowsFixture); - - $expectedContent = $this->getContentExtractReference(); - $expectedContent->fields = array_values( - array_filter($expectedContent->fields, static function (Field $field): bool { - return $field->fieldDefinitionId !== 185; - }) - ); - - self::assertEquals( - [ - $expectedContent, - ], - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::extractContentFromRows - */ - public function testExtractContentFromRowsMultipleVersions() - { - $reg = $this->getFieldRegistry([ - 'ezstring', - 'ezdatetime', - ]); - - $rowsFixture = $this->getMultipleVersionsExtractFixture(); - $nameRowsFixture = $this->getMultipleVersionsNamesExtractFixture(); - - $contentType = $this->getContentTypeFromRows($rowsFixture); - - $contentTypeHandlerMock = $this->getContentTypeHandler(); - $contentTypeHandlerMock->method('load')->willReturn($contentType); - - $mapper = new Mapper( - $reg, - $this->getLanguageHandler(), - $contentTypeHandlerMock, - $this->getEventDispatcher() - ); - $result = $mapper->extractContentFromRows($rowsFixture, $nameRowsFixture); - - self::assertCount( - 2, - $result - ); - - self::assertEquals( - 11, - $result[0]->versionInfo->contentInfo->id - ); - self::assertEquals( - 11, - $result[1]->versionInfo->contentInfo->id - ); - - self::assertEquals( - 1, - $result[0]->versionInfo->versionNo - ); - self::assertEquals( - 2, - $result[1]->versionInfo->versionNo - ); - } - - /** - * @param string[] $fieldTypeIdentifiers - */ - private function getFieldRegistry( - array $fieldTypeIdentifiers = [], - ?int $expectedConverterCalls = null - ): Registry { - $converterMock = $this->createMock(Converter::class); - $converterMock->expects( - $expectedConverterCalls === null - ? self::any() - : self::exactly($expectedConverterCalls) - ) - ->method('toFieldValue') - ->willReturn(new FieldValue()); - - $converters = []; - foreach ($fieldTypeIdentifiers as $fieldTypeIdentifier) { - $converters[$fieldTypeIdentifier] = $converterMock; - } - - return new Registry($converters); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::createCreateStructFromContent - */ - public function testCreateCreateStructFromContent() - { - $time = time(); - $mapper = $this->getMapper(); - - $content = $this->getContentExtractReference(); - - $struct = $mapper->createCreateStructFromContent($content); - - self::assertInstanceOf(CreateStruct::class, $struct); - - return [ - 'original' => $content, - 'result' => $struct, - 'time' => $time, - ]; - - // parentLocations - // fields - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::createCreateStructFromContent - * @depends testCreateCreateStructFromContent - */ - public function testCreateCreateStructFromContentBasicProperties($data) - { - $content = $data['original']; - $struct = $data['result']; - $time = $data['time']; - $this->assertStructsEqual( - $content->versionInfo->contentInfo, - $struct, - ['sectionId', 'ownerId'] - ); - self::assertNotEquals($content->versionInfo->contentInfo->remoteId, $struct->remoteId); - self::assertSame($content->versionInfo->contentInfo->contentTypeId, $struct->typeId); - self::assertSame(2, $struct->initialLanguageId); - self::assertSame($content->versionInfo->contentInfo->alwaysAvailable, $struct->alwaysAvailable); - self::assertGreaterThanOrEqual($time, $struct->modified); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::createCreateStructFromContent - * @depends testCreateCreateStructFromContent - */ - public function testCreateCreateStructFromContentParentLocationsEmpty($data) - { - self::assertEquals( - [], - $data['result']->locations - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::createCreateStructFromContent - * @depends testCreateCreateStructFromContent - */ - public function testCreateCreateStructFromContentFieldCount($data) - { - self::assertEquals( - count($data['original']->fields), - count($data['result']->fields) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::createCreateStructFromContent - * @depends testCreateCreateStructFromContent - */ - public function testCreateCreateStructFromContentFieldsNoId($data) - { - foreach ($data['result']->fields as $field) { - self::assertNull($field->id); - } - } - - public function testExtractRelationsFromRows() - { - $mapper = $this->getMapper(); - - $rows = $this->getRelationExtractFixture(); - - $res = $mapper->extractRelationsFromRows($rows); - - self::assertEquals( - $this->getRelationExtractReference(), - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::createCreateStructFromContent - */ - public function testCreateCreateStructFromContentWithPreserveOriginalLanguage() - { - $time = time(); - $mapper = $this->getMapper(); - - $content = $this->getContentExtractReference(); - $content->versionInfo->contentInfo->mainLanguageCode = 'eng-GB'; - - $struct = $mapper->createCreateStructFromContent($content, true); - - self::assertInstanceOf(CreateStruct::class, $struct); - $this->assertStructsEqual($content->versionInfo->contentInfo, $struct, ['sectionId', 'ownerId']); - self::assertNotEquals($content->versionInfo->contentInfo->remoteId, $struct->remoteId); - self::assertSame($content->versionInfo->contentInfo->contentTypeId, $struct->typeId); - self::assertSame(2, $struct->initialLanguageId); - self::assertSame(4, $struct->mainLanguageId); - self::assertSame($content->versionInfo->contentInfo->alwaysAvailable, $struct->alwaysAvailable); - self::assertGreaterThanOrEqual($time, $struct->modified); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::extractContentInfoFromRow - * @dataProvider extractContentInfoFromRowProvider - * - * @param array $fixtures - * @param string $prefix - */ - public function testExtractContentInfoFromRow(array $fixtures, $prefix) - { - $contentInfoReference = $this->getContentExtractReference()->versionInfo->contentInfo; - $mapper = new Mapper( - $this->getValueConverterRegistryMock(), - $this->getLanguageHandler(), - $this->getContentTypeHandler(), - $this->getEventDispatcher() - ); - self::assertEquals($contentInfoReference, $mapper->extractContentInfoFromRow($fixtures, $prefix)); - } - - /** - * Returns test data for {@link testExtractContentInfoFromRow()}. - * - * @return array - */ - public function extractContentInfoFromRowProvider() - { - $fixtures = $this->getContentExtractFixture(); - $fixturesNoPrefix = []; - foreach ($fixtures[0] as $key => $value) { - $keyNoPrefix = $key === 'ezcontentobject_tree_main_node_id' - ? $key - : str_replace('ezcontentobject_', '', $key); - $fixturesNoPrefix[$keyNoPrefix] = $value; - } - - return [ - [$fixtures[0], 'ezcontentobject_'], - [$fixturesNoPrefix, ''], - ]; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Mapper::createRelationFromCreateStruct - */ - public function testCreateRelationFromCreateStruct() - { - $struct = $this->getRelationCreateStructFixture(); - - $mapper = $this->getMapper(); - $relation = $mapper->createRelationFromCreateStruct($struct); - - self::assertInstanceOf(SPIRelation::class, $relation); - foreach ($struct as $property => $value) { - self::assertSame($value, $relation->$property); - } - } - - /** - * Returns test data for {@link testExtractVersionInfoFromRow()}. - * - * @return array - */ - public function extractVersionInfoFromRowProvider() - { - $fixturesAll = $this->getContentExtractFixture(); - $fixtures = $fixturesAll[0]; - $fixtures['ezcontentobject_version_names'] = [ - ['content_translation' => 'eng-US', 'name' => 'Something'], - ]; - $fixtures['ezcontentobject_version_languages'] = [2]; - $fixtures['ezcontentobject_version_initial_language_code'] = 'eng-US'; - $fixturesNoPrefix = []; - foreach ($fixtures as $key => $value) { - $keyNoPrefix = str_replace('ezcontentobject_version_', '', $key); - $fixturesNoPrefix[$keyNoPrefix] = $value; - } - - return [ - [$fixtures, 'ezcontentobject_version_'], - [$fixturesNoPrefix, ''], - ]; - } - - /** - * Returns a fixture of database rows for content extraction. - * - * Fixture is stored in _fixtures/extract_content_from_rows.php - * - * @return array - */ - protected function getContentExtractFixture() - { - return require __DIR__ . '/_fixtures/extract_content_from_rows.php'; - } - - /** - * Returns a fixture of database rows for content names extraction. - * - * Fixture is stored in _fixtures/extract_names_from_rows.php - * - * @return array - */ - protected function getNamesExtractFixture() - { - return require __DIR__ . '/_fixtures/extract_names_from_rows.php'; - } - - /** - * Returns a reference result for content extraction. - * - * Fixture is stored in _fixtures/extract_content_from_rows_result.php - * - * @return \eZ\Publish\SPI\Persistence\Content - */ - protected function getContentExtractReference() - { - return require __DIR__ . '/_fixtures/extract_content_from_rows_result.php'; - } - - /** - * Returns a fixture for mapping multiple versions of a content object. - * - * @return string[][] - */ - protected function getMultipleVersionsExtractFixture() - { - return require __DIR__ . '/_fixtures/extract_content_from_rows_multiple_versions.php'; - } - - /** - * Returns a fixture of database rows for content names extraction across multiple versions. - * - * Fixture is stored in _fixtures/extract_names_from_rows_multiple_versions.php - * - * @return array - */ - protected function getMultipleVersionsNamesExtractFixture() - { - return require __DIR__ . '/_fixtures/extract_names_from_rows_multiple_versions.php'; - } - - /** - * Returns a fixture of database rows for relations extraction. - * - * Fixture is stored in _fixtures/relations.php - * - * @return array - */ - protected function getRelationExtractFixture() - { - return require __DIR__ . '/_fixtures/relations_rows.php'; - } - - /** - * Returns a reference result for content extraction. - * - * Fixture is stored in _fixtures/relations_results.php - * - * @return \eZ\Publish\SPI\Persistence\Content - */ - protected function getRelationExtractReference() - { - return require __DIR__ . '/_fixtures/relations_results.php'; - } - - /** - * Returns a Mapper. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Mapper - */ - protected function getMapper($valueConverter = null) - { - return new Mapper( - $this->getValueConverterRegistryMock(), - $this->getLanguageHandler(), - $this->getContentTypeHandler(), - $this->getEventDispatcher() - ); - } - - /** - * Returns a FieldValue converter registry mock. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry - */ - protected function getValueConverterRegistryMock() - { - if (!isset($this->valueConverterRegistryMock)) { - $this->valueConverterRegistryMock = $this->getMockBuilder(Registry::class) - ->setMethods([]) - ->getMock(); - - $this->valueConverterRegistryMock - ->method('getConverter') - ->willReturn($this->createMock(Converter::class)); - } - - return $this->valueConverterRegistryMock; - } - - /** - * Returns a eZ\Publish\SPI\Persistence\Content\CreateStruct fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct - */ - protected function getRelationCreateStructFixture() - { - $struct = new RelationCreateStruct(); - - $struct->destinationContentId = 0; - $struct->sourceContentId = 0; - $struct->sourceContentVersionNo = 1; - $struct->sourceFieldDefinitionId = 1; - $struct->type = RelationValue::COMMON; - - return $struct; - } - - protected function getEventDispatcher(): EventDispatcherInterface - { - $eventDispatcher = new EventDispatcher(); - $eventDispatcher->addSubscriber( - new ResolveVirtualFieldSubscriber( - $this->getValueConverterRegistryMock(), - $this->createMock(StorageRegistry::class), - $this->createMock(Gateway::class) - ) - ); - - return $eventDispatcher; - } - - /** - * Returns a language handler mock. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler - */ - protected function getLanguageHandler() - { - $languages = [ - 'eng-US' => new Language( - [ - 'id' => 2, - 'languageCode' => 'eng-US', - 'name' => 'US english', - ] - ), - 'eng-GB' => new Language( - [ - 'id' => 4, - 'languageCode' => 'eng-GB', - 'name' => 'British english', - ] - ), - ]; - - if (!isset($this->languageHandler)) { - $this->languageHandler = $this->createMock(Language\Handler::class); - $this->languageHandler->expects($this->any()) - ->method('load') - ->will( - $this->returnCallback( - static function ($id) use ($languages) { - foreach ($languages as $language) { - if ($language->id == $id) { - return $language; - } - } - } - ) - ); - $this->languageHandler->expects($this->any()) - ->method('loadByLanguageCode') - ->will( - $this->returnCallback( - static function ($languageCode) use ($languages) { - foreach ($languages as $language) { - if ($language->languageCode == $languageCode) { - return $language; - } - } - } - ) - ); - $this->languageHandler->expects($this->any()) - ->method('loadAll') - ->willReturn($languages); - } - - return $this->languageHandler; - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Type\Handler&\PHPUnit\Framework\MockObject\MockObject - */ - protected function getContentTypeHandler(): Content\Type\Handler - { - return $this->createMock(Content\Type\Handler::class); - } - - /** - * @param array<int, array<string, mixed>> $rows - */ - protected function getContentTypeFromRows(array $rows): Content\Type - { - $contentType = new Content\Type(); - $fieldDefinitions = []; - - foreach ($rows as $row) { - $fieldDefinitionId = $row['ezcontentobject_attribute_contentclassattribute_id']; - $fieldType = $row['ezcontentobject_attribute_data_type_string']; - - if (isset($fieldDefinitions[$fieldDefinitionId])) { - continue; - } - - $fieldDefinitions[$fieldDefinitionId] = new Content\Type\FieldDefinition([ - 'id' => $fieldDefinitionId, - 'fieldType' => $fieldType, - ]); - } - - $contentType->fieldDefinitions = array_values($fieldDefinitions); - - return $contentType; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/ObjectState/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/ObjectState/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index 67458501c3..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/ObjectState/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,577 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\ObjectState\Gateway; - -use eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\Tests\Content\LanguageAwareTestCase; -use eZ\Publish\SPI\Persistence\Content\ObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group; - -/** - * Test case for eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase. - */ -class DoctrineDatabaseTest extends LanguageAwareTestCase -{ - /** - * Database gateway to test. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase - */ - protected $databaseGateway; - - /** - * Language mask generator. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator - */ - protected $languageMaskGenerator; - - /** - * Inserts DB fixture. - */ - protected function setUp(): void - { - parent::setUp(); - - $this->insertDatabaseFixture( - __DIR__ . '/../../_fixtures/contentobjects.php' - ); - - $this->insertDatabaseFixture( - __DIR__ . '/../../_fixtures/objectstates.php' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::loadObjectStateData - */ - public function testLoadObjectStateData() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->loadObjectStateData(1); - - $this->assertEquals( - [ - [ - 'ezcobj_state_default_language_id' => 2, - 'ezcobj_state_group_id' => 2, - 'ezcobj_state_id' => 1, - 'ezcobj_state_identifier' => 'not_locked', - 'ezcobj_state_language_mask' => 3, - 'ezcobj_state_priority' => 0, - 'ezcobj_state_language_description' => '', - 'ezcobj_state_language_language_id' => 3, - 'ezcobj_state_language_name' => 'Not locked', - ], - ], - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::loadObjectStateDataByIdentifier - */ - public function testLoadObjectStateDataByIdentifier() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->loadObjectStateDataByIdentifier('not_locked', 2); - - $this->assertEquals( - [ - [ - 'ezcobj_state_default_language_id' => 2, - 'ezcobj_state_group_id' => 2, - 'ezcobj_state_id' => 1, - 'ezcobj_state_identifier' => 'not_locked', - 'ezcobj_state_language_mask' => 3, - 'ezcobj_state_priority' => 0, - 'ezcobj_state_language_description' => '', - 'ezcobj_state_language_language_id' => 3, - 'ezcobj_state_language_name' => 'Not locked', - ], - ], - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::loadObjectStateListData - */ - public function testLoadObjectStateListData() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->loadObjectStateListData(2); - - $this->assertEquals( - [ - [ - [ - 'ezcobj_state_default_language_id' => 2, - 'ezcobj_state_group_id' => 2, - 'ezcobj_state_id' => 1, - 'ezcobj_state_identifier' => 'not_locked', - 'ezcobj_state_language_mask' => 3, - 'ezcobj_state_priority' => 0, - 'ezcobj_state_language_description' => '', - 'ezcobj_state_language_language_id' => 3, - 'ezcobj_state_language_name' => 'Not locked', - ], - ], - [ - [ - 'ezcobj_state_default_language_id' => 2, - 'ezcobj_state_group_id' => 2, - 'ezcobj_state_id' => 2, - 'ezcobj_state_identifier' => 'locked', - 'ezcobj_state_language_mask' => 3, - 'ezcobj_state_priority' => 1, - 'ezcobj_state_language_description' => '', - 'ezcobj_state_language_language_id' => 3, - 'ezcobj_state_language_name' => 'Locked', - ], - ], - ], - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::loadObjectStateGroupData - */ - public function testLoadObjectStateGroupData() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->loadObjectStateGroupData(2); - - $this->assertEquals( - [ - [ - 'ezcobj_state_group_default_language_id' => 2, - 'ezcobj_state_group_id' => 2, - 'ezcobj_state_group_identifier' => 'ez_lock', - 'ezcobj_state_group_language_mask' => 3, - 'ezcobj_state_group_language_description' => '', - 'ezcobj_state_group_language_language_id' => 3, - 'ezcobj_state_group_language_real_language_id' => 2, - 'ezcobj_state_group_language_name' => 'Lock', - ], - ], - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::loadObjectStateGroupDataByIdentifier - */ - public function testLoadObjectStateGroupDataByIdentifier() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->loadObjectStateGroupDataByIdentifier('ez_lock'); - - $this->assertEquals( - [ - [ - 'ezcobj_state_group_default_language_id' => 2, - 'ezcobj_state_group_id' => 2, - 'ezcobj_state_group_identifier' => 'ez_lock', - 'ezcobj_state_group_language_mask' => 3, - 'ezcobj_state_group_language_description' => '', - 'ezcobj_state_group_language_language_id' => 3, - 'ezcobj_state_group_language_real_language_id' => 2, - 'ezcobj_state_group_language_name' => 'Lock', - ], - ], - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::loadObjectStateGroupListData - */ - public function testLoadObjectStateGroupListData() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->loadObjectStateGroupListData(0, -1); - - $this->assertEquals( - [ - [ - [ - 'ezcobj_state_group_default_language_id' => 2, - 'ezcobj_state_group_id' => 2, - 'ezcobj_state_group_identifier' => 'ez_lock', - 'ezcobj_state_group_language_mask' => 3, - 'ezcobj_state_group_language_description' => '', - 'ezcobj_state_group_language_language_id' => 3, - 'ezcobj_state_group_language_real_language_id' => 2, - 'ezcobj_state_group_language_name' => 'Lock', - ], - ], - ], - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::insertObjectState - */ - public function testInsertObjectState() - { - $gateway = $this->getDatabaseGateway(); - - $gateway->insertObjectState($this->getObjectStateFixture(), 2); - - $this->assertEquals( - [ - [ - 'ezcobj_state_default_language_id' => 4, - 'ezcobj_state_group_id' => 2, - // The new state should be added with state ID = 3 - 'ezcobj_state_id' => 3, - 'ezcobj_state_identifier' => 'test_state', - 'ezcobj_state_language_mask' => 5, - // The new state should have priority = 2 - 'ezcobj_state_priority' => 2, - 'ezcobj_state_language_description' => 'Test state description', - 'ezcobj_state_language_language_id' => 5, - 'ezcobj_state_language_name' => 'Test state', - ], - ], - // The new state should be added with state ID = 3 - $this->getDatabaseGateway()->loadObjectStateData(3) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::insertObjectState - */ - public function testInsertObjectStateInEmptyGroup() - { - $gateway = $this->getDatabaseGateway(); - - $gateway->insertObjectStateGroup($this->getObjectStateGroupFixture()); - $gateway->insertObjectState($this->getObjectStateFixture(), 3); - - $this->assertEquals( - [ - [ - 'ezcobj_state_default_language_id' => 4, - // New group should be added with group ID = 3 - 'ezcobj_state_group_id' => 3, - // The new state should be added with state ID = 3 - 'ezcobj_state_id' => 3, - 'ezcobj_state_identifier' => 'test_state', - 'ezcobj_state_language_mask' => 5, - // The new state should have priority = 0 - 'ezcobj_state_priority' => 0, - 'ezcobj_state_language_description' => 'Test state description', - 'ezcobj_state_language_language_id' => 5, - 'ezcobj_state_language_name' => 'Test state', - ], - ], - // The new state should be added with state ID = 3 - $this->getDatabaseGateway()->loadObjectStateData(3) - ); - - $this->assertEquals( - // 185 is the number of objects in the fixture - 185, - $gateway->getContentCount(3) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::updateObjectState - */ - public function testUpdateObjectState() - { - $gateway = $this->getDatabaseGateway(); - - $objectStateFixture = $this->getObjectStateFixture(); - $objectStateFixture->id = 1; - - $gateway->updateObjectState($objectStateFixture); - - $this->assertEquals( - [ - [ - 'ezcobj_state_default_language_id' => 4, - 'ezcobj_state_group_id' => 2, - 'ezcobj_state_id' => 1, - 'ezcobj_state_identifier' => 'test_state', - 'ezcobj_state_language_mask' => 5, - 'ezcobj_state_priority' => 0, - 'ezcobj_state_language_description' => 'Test state description', - 'ezcobj_state_language_language_id' => 5, - 'ezcobj_state_language_name' => 'Test state', - ], - ], - $this->getDatabaseGateway()->loadObjectStateData(1) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::deleteObjectState - */ - public function testDeleteObjectState() - { - $gateway = $this->getDatabaseGateway(); - - $gateway->deleteObjectState(1); - - $this->assertEquals( - [], - $this->getDatabaseGateway()->loadObjectStateData(1) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::updateObjectStateLinks - */ - public function testUpdateObjectStateLinks() - { - $gateway = $this->getDatabaseGateway(); - - $gateway->updateObjectStateLinks(1, 2); - - self::assertSame(0, $gateway->getContentCount(1)); - self::assertSame(185, $gateway->getContentCount(2)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::deleteObjectStateLinks - */ - public function testDeleteObjectStateLinks() - { - $gateway = $this->getDatabaseGateway(); - - $gateway->deleteObjectStateLinks(1); - - self::assertSame(0, $gateway->getContentCount(1)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::insertObjectStateGroup - */ - public function testInsertObjectStateGroup() - { - $gateway = $this->getDatabaseGateway(); - - $gateway->insertObjectStateGroup($this->getObjectStateGroupFixture()); - - $this->assertEquals( - [ - [ - 'ezcobj_state_group_default_language_id' => 4, - // The new state group should be added with state group ID = 3 - 'ezcobj_state_group_id' => 3, - 'ezcobj_state_group_identifier' => 'test_group', - 'ezcobj_state_group_language_mask' => 5, - 'ezcobj_state_group_language_description' => 'Test group description', - 'ezcobj_state_group_language_language_id' => 5, - 'ezcobj_state_group_language_real_language_id' => 4, - 'ezcobj_state_group_language_name' => 'Test group', - ], - ], - // The new state group should be added with state group ID = 3 - $this->getDatabaseGateway()->loadObjectStateGroupData(3) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::updateObjectStateGroup - */ - public function testUpdateObjectStateGroup() - { - $gateway = $this->getDatabaseGateway(); - - $groupFixture = $this->getObjectStateGroupFixture(); - $groupFixture->id = 2; - - $gateway->updateObjectStateGroup($groupFixture); - - $this->assertEquals( - [ - [ - 'ezcobj_state_group_default_language_id' => 4, - 'ezcobj_state_group_id' => 2, - 'ezcobj_state_group_identifier' => 'test_group', - 'ezcobj_state_group_language_mask' => 5, - 'ezcobj_state_group_language_description' => 'Test group description', - 'ezcobj_state_group_language_language_id' => 5, - 'ezcobj_state_group_language_real_language_id' => 4, - 'ezcobj_state_group_language_name' => 'Test group', - ], - ], - $this->getDatabaseGateway()->loadObjectStateGroupData(2) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::deleteObjectStateGroup - */ - public function testDeleteObjectStateGroup() - { - $gateway = $this->getDatabaseGateway(); - - $gateway->deleteObjectStateGroup(2); - - $this->assertEquals( - [], - $this->getDatabaseGateway()->loadObjectStateGroupData(2) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::setContentState - */ - public function testSetContentState() - { - $gateway = $this->getDatabaseGateway(); - - $gateway->setContentState(42, 2, 2); - - $this->assertQueryResult( - [ - [ - 'contentobject_id' => 42, - 'contentobject_state_id' => 2, - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('contentobject_id', 'contentobject_state_id') - ->from('ezcobj_state_link') - ->where('contentobject_id = 42') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::loadObjectStateDataForContent - */ - public function testLoadObjectStateDataForContent() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->loadObjectStateDataForContent(42, 2); - - $this->assertEquals( - [ - [ - 'ezcobj_state_default_language_id' => 2, - 'ezcobj_state_group_id' => 2, - 'ezcobj_state_id' => 1, - 'ezcobj_state_identifier' => 'not_locked', - 'ezcobj_state_language_mask' => 3, - 'ezcobj_state_priority' => 0, - 'ezcobj_state_language_description' => '', - 'ezcobj_state_language_language_id' => 3, - 'ezcobj_state_language_name' => 'Not locked', - ], - ], - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::getContentCount - */ - public function testGetContentCount() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->getContentCount(1); - - // 185 is the number of objects in the fixture - $this->assertEquals(185, $result); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase::updateObjectStatePriority - */ - public function testUpdateObjectStatePriority() - { - $gateway = $this->getDatabaseGateway(); - - $gateway->updateObjectStatePriority(1, 10); - - $objectStateData = $gateway->loadObjectStateData(1); - - $this->assertEquals( - [ - [ - 'ezcobj_state_default_language_id' => 2, - 'ezcobj_state_group_id' => 2, - 'ezcobj_state_id' => 1, - 'ezcobj_state_identifier' => 'not_locked', - 'ezcobj_state_language_mask' => 3, - 'ezcobj_state_priority' => 10, - 'ezcobj_state_language_description' => '', - 'ezcobj_state_language_language_id' => 3, - 'ezcobj_state_language_name' => 'Not locked', - ], - ], - $objectStateData - ); - } - - /** - * Returns an object state fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - protected function getObjectStateFixture() - { - $objectState = new ObjectState(); - $objectState->identifier = 'test_state'; - $objectState->defaultLanguage = 'eng-GB'; - $objectState->languageCodes = ['eng-GB']; - $objectState->name = ['eng-GB' => 'Test state']; - $objectState->description = ['eng-GB' => 'Test state description']; - - return $objectState; - } - - /** - * Returns an object state group fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group - */ - protected function getObjectStateGroupFixture() - { - $group = new Group(); - $group->identifier = 'test_group'; - $group->defaultLanguage = 'eng-GB'; - $group->languageCodes = ['eng-GB']; - $group->name = ['eng-GB' => 'Test group']; - $group->description = ['eng-GB' => 'Test group description']; - - return $group; - } - - /** - * Returns a ready to test DoctrineDatabase gateway. - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function getDatabaseGateway(): DoctrineDatabase - { - if (!isset($this->databaseGateway)) { - $this->databaseGateway = new DoctrineDatabase( - $this->getDatabaseConnection(), - $this->getLanguageMaskGenerator() - ); - } - - return $this->databaseGateway; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/ObjectState/MapperTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/ObjectState/MapperTest.php deleted file mode 100644 index 049dae0aff..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/ObjectState/MapperTest.php +++ /dev/null @@ -1,250 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\ObjectState; - -use eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Tests\Content\LanguageAwareTestCase; -use eZ\Publish\SPI\Persistence\Content\ObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group; -use eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct; - -/** - * Test case for Mapper. - */ -class MapperTest extends LanguageAwareTestCase -{ - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper::createObjectStateFromData - */ - public function testCreateObjectStateFromData() - { - $mapper = $this->getMapper(); - - $rows = $this->getObjectStateRowsFixture(); - - $result = $mapper->createObjectStateFromData($rows); - - $this->assertStructsEqual( - $this->getObjectStateFixture(), - $result, - ['identifier', 'defaultLanguage', 'languageCodes', 'name', 'description'] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper::createObjectStateListFromData - */ - public function testCreateObjectStateListFromData() - { - $mapper = $this->getMapper(); - - $rows = [$this->getObjectStateRowsFixture()]; - - $result = $mapper->createObjectStateListFromData($rows); - - $this->assertStructsEqual( - $this->getObjectStateFixture(), - $result[0], - ['identifier', 'defaultLanguage', 'languageCodes', 'name', 'description'] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper::createObjectStateGroupFromData - */ - public function testCreateObjectStateGroupFromData() - { - $mapper = $this->getMapper(); - - $rows = $this->getObjectStateGroupRowsFixture(); - - $result = $mapper->createObjectStateGroupFromData($rows); - - $this->assertStructsEqual( - $this->getObjectStateGroupFixture(), - $result, - ['identifier', 'defaultLanguage', 'languageCodes', 'name', 'description'] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper::createObjectStateGroupListFromData - */ - public function testCreateObjectStateGroupListFromData() - { - $mapper = $this->getMapper(); - - $rows = [$this->getObjectStateGroupRowsFixture()]; - - $result = $mapper->createObjectStateGroupListFromData($rows); - - $this->assertStructsEqual( - $this->getObjectStateGroupFixture(), - $result[0], - ['identifier', 'defaultLanguage', 'languageCodes', 'name', 'description'] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper::createObjectStateFromInputStruct - */ - public function testCreateObjectStateFromInputStruct() - { - $mapper = $this->getMapper(); - - $inputStruct = $this->getObjectStateInputStructFixture(); - - $result = $mapper->createObjectStateFromInputStruct($inputStruct); - - $this->assertStructsEqual( - $this->getObjectStateFixture(), - $result, - ['identifier', 'defaultLanguage', 'languageCodes', 'name', 'description'] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper::createObjectStateGroupFromInputStruct - */ - public function testCreateObjectStateGroupFromInputStruct() - { - $mapper = $this->getMapper(); - - $inputStruct = $this->getObjectStateGroupInputStructFixture(); - - $result = $mapper->createObjectStateGroupFromInputStruct($inputStruct); - - $this->assertStructsEqual( - $this->getObjectStateGroupFixture(), - $result, - ['identifier', 'defaultLanguage', 'languageCodes', 'name', 'description'] - ); - } - - /** - * Returns a Mapper. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper - */ - protected function getMapper() - { - return new Mapper( - $this->getLanguageHandler() - ); - } - - /** - * Returns an object state result rows fixture. - * - * @return array[][] - */ - protected function getObjectStateRowsFixture() - { - return [ - [ - 'ezcobj_state_default_language_id' => 2, - 'ezcobj_state_group_id' => 2, - 'ezcobj_state_id' => 1, - 'ezcobj_state_identifier' => 'not_locked', - 'ezcobj_state_language_mask' => 3, - 'ezcobj_state_priority' => 0, - 'ezcobj_state_language_description' => '', - 'ezcobj_state_language_language_id' => 3, - 'ezcobj_state_language_name' => 'Not locked', - ], - ]; - } - - /** - * Returns an object state group result rows fixture. - * - * @return array[][] - */ - protected function getObjectStateGroupRowsFixture() - { - return [ - [ - 'ezcobj_state_group_default_language_id' => 2, - 'ezcobj_state_group_id' => 1, - 'ezcobj_state_group_identifier' => 'ez_lock', - 'ezcobj_state_group_language_mask' => 3, - 'ezcobj_state_group_language_description' => '', - 'ezcobj_state_group_language_language_id' => 3, - 'ezcobj_state_group_language_real_language_id' => 2, - 'ezcobj_state_group_language_name' => 'Lock', - ], - ]; - } - - /** - * Returns an object state fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - protected function getObjectStateFixture() - { - $objectState = new ObjectState(); - $objectState->identifier = 'not_locked'; - $objectState->defaultLanguage = 'eng-US'; - $objectState->languageCodes = ['eng-US']; - $objectState->name = ['eng-US' => 'Not locked']; - $objectState->description = ['eng-US' => '']; - - return $objectState; - } - - /** - * Returns an object state group fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group - */ - protected function getObjectStateGroupFixture() - { - $group = new Group(); - $group->identifier = 'ez_lock'; - $group->defaultLanguage = 'eng-US'; - $group->languageCodes = ['eng-US']; - $group->name = ['eng-US' => 'Lock']; - $group->description = ['eng-US' => '']; - - return $group; - } - - /** - * Returns the InputStruct fixture for creating object states. - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct - */ - protected function getObjectStateInputStructFixture() - { - $inputStruct = new InputStruct(); - - $inputStruct->defaultLanguage = 'eng-US'; - $inputStruct->identifier = 'not_locked'; - $inputStruct->name = ['eng-US' => 'Not locked']; - $inputStruct->description = ['eng-US' => '']; - - return $inputStruct; - } - - /** - * Returns the InputStruct fixture for creating object state groups. - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct - */ - protected function getObjectStateGroupInputStructFixture() - { - $inputStruct = new InputStruct(); - - $inputStruct->defaultLanguage = 'eng-US'; - $inputStruct->identifier = 'ez_lock'; - $inputStruct->name = ['eng-US' => 'Lock']; - $inputStruct->description = ['eng-US' => '']; - - return $inputStruct; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/ObjectState/ObjectStateHandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/ObjectState/ObjectStateHandlerTest.php deleted file mode 100644 index 6f97b599a4..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/ObjectState/ObjectStateHandlerTest.php +++ /dev/null @@ -1,719 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\ObjectState; - -use eZ\Publish\API\Repository\Tests\BaseTest as APIBaseTest; -use eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler; -use eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Tests\Content\LanguageAwareTestCase; -use eZ\Publish\SPI\Persistence\Content\ObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group; -use eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct; - -/** - * Test case for Object state Handler. - */ -class ObjectStateHandlerTest extends LanguageAwareTestCase -{ - /** - * Object state handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler - */ - protected $objectStateHandler; - - /** - * Object state gateway mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway - */ - protected $gatewayMock; - - /** - * Object state mapper mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper - */ - protected $mapperMock; - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::createGroup - */ - public function testCreateGroup() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $mapperMock->expects($this->once()) - ->method('createObjectStateGroupFromInputStruct') - ->with($this->equalTo($this->getInputStructFixture())) - ->will($this->returnValue($this->getObjectStateGroupFixture())); - - $gatewayMock->expects($this->once()) - ->method('insertObjectStateGroup') - ->with($this->equalTo($this->getObjectStateGroupFixture())) - ->will($this->returnValue($this->getObjectStateGroupFixture())); - - $result = $handler->createGroup($this->getInputStructFixture()); - - $this->assertInstanceOf( - 'eZ\\Publish\\SPI\\Persistence\\Content\\ObjectState\\Group', - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::loadGroup - */ - public function testLoadGroup() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateGroupData') - ->with($this->equalTo(2)) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->once()) - ->method('createObjectStateGroupFromData') - ->with($this->equalTo([[]])) - ->will($this->returnValue($this->getObjectStateGroupFixture())); - - $result = $handler->loadGroup(2); - - $this->assertInstanceOf( - Group::class, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::loadGroup - */ - public function testLoadGroupThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $handler = $this->getObjectStateHandler(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateGroupData') - ->with($this->equalTo(APIBaseTest::DB_INT_MAX)) - ->will($this->returnValue([])); - - $handler->loadGroup(APIBaseTest::DB_INT_MAX); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::loadGroupByIdentifier - */ - public function testLoadGroupByIdentifier() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateGroupDataByIdentifier') - ->with($this->equalTo('ez_lock')) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->once()) - ->method('createObjectStateGroupFromData') - ->with($this->equalTo([[]])) - ->will($this->returnValue($this->getObjectStateGroupFixture())); - - $result = $handler->loadGroupByIdentifier('ez_lock'); - - $this->assertInstanceOf( - Group::class, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::loadGroupByIdentifier - */ - public function testLoadGroupByIdentifierThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $handler = $this->getObjectStateHandler(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateGroupDataByIdentifier') - ->with($this->equalTo('unknown')) - ->will($this->returnValue([])); - - $handler->loadGroupByIdentifier('unknown'); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::loadAllGroups - */ - public function testLoadAllGroups() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateGroupListData') - ->with($this->equalTo(0), $this->equalTo(-1)) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->once()) - ->method('createObjectStateGroupListFromData') - ->with($this->equalTo([[]])) - ->will($this->returnValue([$this->getObjectStateGroupFixture()])); - - $result = $handler->loadAllGroups(); - - foreach ($result as $resultItem) { - $this->assertInstanceOf( - Group::class, - $resultItem - ); - } - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::loadObjectStates - */ - public function testLoadObjectStates() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateListData') - ->with($this->equalTo(2)) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->once()) - ->method('createObjectStateListFromData') - ->with($this->equalTo([[]])) - ->will($this->returnValue([$this->getObjectStateFixture(), $this->getObjectStateFixture()])); - - $result = $handler->loadObjectStates(2); - - foreach ($result as $resultItem) { - $this->assertInstanceOf( - ObjectState::class, - $resultItem - ); - } - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::updateGroup - */ - public function testUpdateGroup() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $mapperMock->expects($this->once()) - ->method('createObjectStateGroupFromInputStruct') - ->with($this->equalTo($this->getInputStructFixture())) - ->will($this->returnValue($this->getObjectStateGroupFixture())); - - $gatewayMock->expects($this->once()) - ->method('updateObjectStateGroup') - ->with($this->equalTo(new Group(['id' => 2]))); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateGroupData') - ->with($this->equalTo(2)) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->once()) - ->method('createObjectStateGroupFromData') - ->with($this->equalTo([[]])) - ->will($this->returnValue($this->getObjectStateGroupFixture())); - - $result = $handler->updateGroup(2, $this->getInputStructFixture()); - - $this->assertInstanceOf( - Group::class, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::deleteGroup - */ - public function testDeleteGroup() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateListData') - ->with($this->equalTo(2)) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->once()) - ->method('createObjectStateListFromData') - ->with($this->equalTo([[]])) - ->will( - $this->returnValue( - [ - new ObjectState(['id' => 1]), - new ObjectState(['id' => 2]), - ] - ) - ); - - $gatewayMock->expects($this->exactly(2)) - ->method('deleteObjectStateLinks'); - - $gatewayMock->expects($this->exactly(2)) - ->method('deleteObjectState'); - - $gatewayMock->expects($this->at(1)) - ->method('deleteObjectStateLinks') - ->with($this->equalTo(1)); - - $gatewayMock->expects($this->at(2)) - ->method('deleteObjectState') - ->with($this->equalTo(1)); - - $gatewayMock->expects($this->at(3)) - ->method('deleteObjectStateLinks') - ->with($this->equalTo(2)); - - $gatewayMock->expects($this->at(4)) - ->method('deleteObjectState') - ->with($this->equalTo(2)); - - $gatewayMock->expects($this->once()) - ->method('deleteObjectStateGroup') - ->with($this->equalTo(2)); - - $handler->deleteGroup(2); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::create - */ - public function testCreate() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $mapperMock->expects($this->once()) - ->method('createObjectStateFromInputStruct') - ->with($this->equalTo($this->getInputStructFixture())) - ->will($this->returnValue($this->getObjectStateFixture())); - - $gatewayMock->expects($this->once()) - ->method('insertObjectState') - ->with($this->equalTo($this->getObjectStateFixture()), $this->equalTo(2)) - ->will($this->returnValue($this->getObjectStateFixture())); - - $result = $handler->create(2, $this->getInputStructFixture()); - - $this->assertInstanceOf( - ObjectState::class, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::load - */ - public function testLoad() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateData') - ->with($this->equalTo(1)) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->once()) - ->method('createObjectStateFromData') - ->with($this->equalTo([[]])) - ->will($this->returnValue($this->getObjectStateFixture())); - - $result = $handler->load(1); - - $this->assertInstanceOf( - ObjectState::class, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::load - */ - public function testLoadThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $handler = $this->getObjectStateHandler(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateData') - ->with($this->equalTo(APIBaseTest::DB_INT_MAX)) - ->will($this->returnValue([])); - - $handler->load(APIBaseTest::DB_INT_MAX); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::loadByIdentifier - */ - public function testLoadByIdentifier() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateDataByIdentifier') - ->with($this->equalTo('not_locked'), $this->equalTo(2)) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->once()) - ->method('createObjectStateFromData') - ->with($this->equalTo([[]])) - ->will($this->returnValue($this->getObjectStateFixture())); - - $result = $handler->loadByIdentifier('not_locked', 2); - - $this->assertInstanceOf( - ObjectState::class, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::loadByIdentifier - */ - public function testLoadByIdentifierThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $handler = $this->getObjectStateHandler(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateDataByIdentifier') - ->with($this->equalTo('unknown'), $this->equalTo(2)) - ->will($this->returnValue([])); - - $handler->loadByIdentifier('unknown', 2); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::update - */ - public function testUpdate() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $mapperMock->expects($this->once()) - ->method('createObjectStateFromInputStruct') - ->with($this->equalTo($this->getInputStructFixture())) - ->will($this->returnValue($this->getObjectStateFixture())); - - $gatewayMock->expects($this->once()) - ->method('updateObjectState') - ->with($this->equalTo(new ObjectState(['id' => 1]))); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateData') - ->with($this->equalTo(1)) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->once()) - ->method('createObjectStateFromData') - ->with($this->equalTo([[]])) - ->will($this->returnValue($this->getObjectStateFixture())); - - $result = $handler->update(1, $this->getInputStructFixture()); - - $this->assertInstanceOf( - ObjectState::class, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::setPriority - */ - public function testSetPriority() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateData') - ->with($this->equalTo(2)) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->once()) - ->method('createObjectStateFromData') - ->with($this->equalTo([[]])) - ->will($this->returnValue(new ObjectState(['id' => 2, 'groupId' => 2]))); - - $gatewayMock->expects($this->any()) - ->method('loadObjectStateListData') - ->with($this->equalTo(2)) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->any()) - ->method('createObjectStateListFromData') - ->with($this->equalTo([[]])) - ->will( - $this->returnValue( - [ - new ObjectState(['id' => 1, 'groupId' => 2]), - new ObjectState(['id' => 2, 'groupId' => 2]), - new ObjectState(['id' => 3, 'groupId' => 2]), - ] - ) - ); - - $gatewayMock->expects($this->exactly(3)) - ->method('updateObjectStatePriority'); - - $gatewayMock->expects($this->at(2)) - ->method('updateObjectStatePriority') - ->with($this->equalTo(2), $this->equalTo(0)); - - $gatewayMock->expects($this->at(3)) - ->method('updateObjectStatePriority') - ->with($this->equalTo(1), $this->equalTo(1)); - - $gatewayMock->expects($this->at(4)) - ->method('updateObjectStatePriority') - ->with($this->equalTo(3), $this->equalTo(2)); - - $handler->setPriority(2, 0); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::delete - */ - public function testDelete() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateData') - ->with($this->equalTo(1)) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->once()) - ->method('createObjectStateFromData') - ->with($this->equalTo([[]])) - ->will($this->returnValue(new ObjectState(['id' => 1, 'groupId' => 2]))); - - $gatewayMock->expects($this->once()) - ->method('deleteObjectState') - ->with($this->equalTo(1)); - - $gatewayMock->expects($this->any()) - ->method('loadObjectStateListData') - ->with($this->equalTo(2)) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->any()) - ->method('createObjectStateListFromData') - ->with($this->equalTo([[]])) - ->will($this->returnValue([new ObjectState(['id' => 2, 'groupId' => 2])])); - - $gatewayMock->expects($this->once()) - ->method('updateObjectStatePriority') - ->with($this->equalTo(2), $this->equalTo(0)); - - $gatewayMock->expects($this->once()) - ->method('updateObjectStateLinks') - ->with($this->equalTo(1), $this->equalTo(2)); - - $handler->delete(1); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::delete - */ - public function testDeleteThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $handler = $this->getObjectStateHandler(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateData') - ->with($this->equalTo(APIBaseTest::DB_INT_MAX)) - ->will($this->returnValue([])); - - $handler->delete(APIBaseTest::DB_INT_MAX); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::setContentState - */ - public function testSetContentState() - { - $handler = $this->getObjectStateHandler(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('setContentState') - ->with($this->equalTo(42), $this->equalTo(2), $this->equalTo(2)); - - $result = $handler->setContentState(42, 2, 2); - - $this->assertTrue($result); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::getContentState - */ - public function testGetContentState() - { - $handler = $this->getObjectStateHandler(); - $mapperMock = $this->getMapperMock(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadObjectStateDataForContent') - ->with($this->equalTo(42), $this->equalTo(2)) - ->will($this->returnValue([[]])); - - $mapperMock->expects($this->once()) - ->method('createObjectStateFromData') - ->with($this->equalTo([[]])) - ->will($this->returnValue($this->getObjectStateFixture())); - - $result = $handler->getContentState(42, 2); - - $this->assertInstanceOf( - ObjectState::class, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler::getContentCount - */ - public function testGetContentCount() - { - $handler = $this->getObjectStateHandler(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('getContentCount') - ->with($this->equalTo(1)) - ->will($this->returnValue(185)); - - $result = $handler->getContentCount(1); - - $this->assertEquals(185, $result); - } - - /** - * Returns an object state. - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - protected function getObjectStateFixture() - { - return new ObjectState(); - } - - /** - * Returns an object state group. - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group - */ - protected function getObjectStateGroupFixture() - { - return new Group(); - } - - /** - * Returns the InputStruct. - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct - */ - protected function getInputStructFixture() - { - return new InputStruct(); - } - - /** - * Returns the object state handler to test. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler - */ - protected function getObjectStateHandler() - { - if (!isset($this->objectStateHandler)) { - $this->objectStateHandler = new Handler( - $this->getGatewayMock(), - $this->getMapperMock() - ); - } - - return $this->objectStateHandler; - } - - /** - * Returns an object state mapper mock. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper - */ - protected function getMapperMock() - { - if (!isset($this->mapperMock)) { - $this->mapperMock = $this->getMockBuilder(Mapper::class) - ->setConstructorArgs([$this->getLanguageHandler()]) - ->setMethods([]) - ->getMock(); - } - - return $this->mapperMock; - } - - /** - * Returns a mock for the object state gateway. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway - */ - protected function getGatewayMock() - { - if (!isset($this->gatewayMock)) { - $this->gatewayMock = $this->getMockForAbstractClass(Gateway::class); - } - - return $this->gatewayMock; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Section/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Section/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index 997e6c24e5..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Section/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,295 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Section\Gateway; - -use eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; - -/** - * Test case for eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase. - */ -class DoctrineDatabaseTest extends TestCase -{ - /** - * Database gateway to test. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase - */ - protected $databaseGateway; - - /** - * Inserts DB fixture. - */ - protected function setUp(): void - { - parent::setUp(); - - $this->insertDatabaseFixture( - __DIR__ . '/../../_fixtures/sections.php' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase::insertSection - */ - public function testInsertSection() - { - $gateway = $this->getDatabaseGateway(); - - $gateway->insertSection('New Section', 'new_section'); - $query = $this->getDatabaseConnection()->createQueryBuilder(); - - $this->assertQueryResult( - [ - [ - 'id' => '7', - 'identifier' => 'new_section', - 'name' => 'New Section', - 'locale' => '', - ], - ], - $query - ->select('id', 'identifier', 'name', 'locale') - ->from('ezsection') - ->where( - $query->expr()->eq( - 'identifier', - $query->createPositionalParameter('new_section') - ) - ) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase::updateSection - */ - public function testUpdateSection() - { - $gateway = $this->getDatabaseGateway(); - - $gateway->updateSection(2, 'New Section', 'new_section'); - - $this->assertQueryResult( - [ - [ - 'id' => '2', - 'identifier' => 'new_section', - 'name' => 'New Section', - 'locale' => '', - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('id', 'identifier', 'name', 'locale') - ->from('ezsection') - ->where('id=2') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase::loadSectionData - */ - public function testLoadSectionData() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->loadSectionData(2); - - $this->assertEquals( - [ - [ - 'id' => '2', - 'identifier' => 'users', - 'name' => 'Users', - ], - ], - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase::loadAllSectionData - */ - public function testLoadAllSectionData() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->loadAllSectionData(); - - $expected = [ - [ - 'id' => '1', - 'identifier' => 'standard', - 'name' => 'Standard', - ], - - [ - 'id' => '2', - 'identifier' => 'users', - 'name' => 'Users', - ], - - [ - 'id' => '3', - 'identifier' => 'media', - 'name' => 'Media', - ], - - [ - 'id' => '4', - 'identifier' => 'setup', - 'name' => 'Setup', - ], - - [ - 'id' => '5', - 'identifier' => 'design', - 'name' => 'Design', - ], - - [ - 'id' => '6', - 'identifier' => '', - 'name' => 'Restricted', - ], - ]; - $this->assertEquals( - $expected, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase::loadSectionDataByIdentifier - */ - public function testLoadSectionDataByIdentifier() - { - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->loadSectionDataByIdentifier('users'); - - $this->assertEquals( - [ - [ - 'id' => '2', - 'identifier' => 'users', - 'name' => 'Users', - ], - ], - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase::countContentObjectsInSection - */ - public function testCountContentObjectsInSection() - { - $this->insertDatabaseFixture( - __DIR__ . '/../../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->countContentObjectsInSection(2); - - $this->assertSame( - 7, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase::countRoleAssignmentsUsingSection - */ - public function testCountRoleAssignmentsUsingSection() - { - $this->insertDatabaseFixture( - __DIR__ . '/../../../User/_fixtures/roles.php' - ); - - $gateway = $this->getDatabaseGateway(); - - $result = $gateway->countRoleAssignmentsUsingSection(2); - - $this->assertSame( - 1, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase::deleteSection - */ - public function testDeleteSection() - { - $gateway = $this->getDatabaseGateway(); - - $gateway->deleteSection(2); - - $this->assertQueryResult( - [ - [ - 'count' => '5', - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('COUNT( * ) AS count') - ->from('ezsection') - ); - - $this->assertQueryResult( - [ - [ - 'count' => '0', - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('COUNT( * ) AS count') - ->from('ezsection') - ->where('id=2') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase::assignSectionToContent - * @depends testCountContentObjectsInSection - */ - public function testAssignSectionToContent() - { - $this->insertDatabaseFixture( - __DIR__ . '/../../_fixtures/contentobjects.php' - ); - - $gateway = $this->getDatabaseGateway(); - - $beforeCount = $gateway->countContentObjectsInSection(4); - - $result = $gateway->assignSectionToContent(4, 10); - - $this->assertSame( - $beforeCount + 1, - $gateway->countContentObjectsInSection(4) - ); - } - - /** - * Returns a ready to test DoctrineDatabase gateway. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function getDatabaseGateway(): Gateway - { - if (!isset($this->databaseGateway)) { - $this->databaseGateway = new DoctrineDatabase($this->getDatabaseConnection()); - } - - return $this->databaseGateway; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Section/SectionHandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Section/SectionHandlerTest.php deleted file mode 100644 index b4d4a4f6fd..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Section/SectionHandlerTest.php +++ /dev/null @@ -1,353 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Section; - -use eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Content\Section; - -/** - * Test case for Section Handler. - */ -class SectionHandlerTest extends TestCase -{ - /** - * Section handler. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler - */ - protected $sectionHandler; - - /** - * Section gateway mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway - */ - protected $gatewayMock; - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::create - */ - public function testCreate() - { - $handler = $this->getSectionHandler(); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('insertSection') - ->with( - $this->equalTo('New Section'), - $this->equalTo('new_section') - )->will($this->returnValue(23)); - - $sectionRef = new Section(); - $sectionRef->id = 23; - $sectionRef->name = 'New Section'; - $sectionRef->identifier = 'new_section'; - - $result = $handler->create('New Section', 'new_section'); - - $this->assertEquals( - $sectionRef, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::update - */ - public function testUpdate() - { - $handler = $this->getSectionHandler(); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('updateSection') - ->with( - $this->equalTo(23), - $this->equalTo('New Section'), - $this->equalTo('new_section') - ); - - $sectionRef = new Section(); - $sectionRef->id = 23; - $sectionRef->name = 'New Section'; - $sectionRef->identifier = 'new_section'; - - $result = $handler->update(23, 'New Section', 'new_section'); - - $this->assertEquals( - $sectionRef, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::load - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::createSectionFromArray - */ - public function testLoad() - { - $handler = $this->getSectionHandler(); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadSectionData') - ->with( - $this->equalTo(23) - )->will( - $this->returnValue( - [ - [ - 'id' => '23', - 'identifier' => 'new_section', - 'name' => 'New Section', - ], - ] - ) - ); - - $sectionRef = new Section(); - $sectionRef->id = 23; - $sectionRef->name = 'New Section'; - $sectionRef->identifier = 'new_section'; - - $result = $handler->load(23); - - $this->assertEquals( - $sectionRef, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::loadAll - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::createSectionFromArray - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::createSectionsFromArray - */ - public function testLoadAll() - { - $handler = $this->getSectionHandler(); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadAllSectionData') - ->will( - $this->returnValue( - [ - [ - 'id' => '23', - 'identifier' => 'new_section', - 'name' => 'New Section', - ], - [ - 'id' => '46', - 'identifier' => 'new_section2', - 'name' => 'New Section2', - ], - ] - ) - ); - - $sectionRef = new Section(); - $sectionRef->id = 23; - $sectionRef->name = 'New Section'; - $sectionRef->identifier = 'new_section'; - - $sectionRef2 = new Section(); - $sectionRef2->id = 46; - $sectionRef2->name = 'New Section2'; - $sectionRef2->identifier = 'new_section2'; - - $result = $handler->loadAll(); - - $this->assertEquals( - [$sectionRef, $sectionRef2], - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::loadByIdentifier - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::createSectionFromArray - */ - public function testLoadByIdentifier() - { - $handler = $this->getSectionHandler(); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadSectionDataByIdentifier') - ->with( - $this->equalTo('new_section') - )->will( - $this->returnValue( - [ - [ - 'id' => '23', - 'identifier' => 'new_section', - 'name' => 'New Section', - ], - ] - ) - ); - - $sectionRef = new Section(); - $sectionRef->id = 23; - $sectionRef->name = 'New Section'; - $sectionRef->identifier = 'new_section'; - - $result = $handler->loadByIdentifier('new_section'); - - $this->assertEquals( - $sectionRef, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::delete - */ - public function testDelete() - { - $handler = $this->getSectionHandler(); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('countContentObjectsInSection') - ->with($this->equalTo(23)) - ->will($this->returnValue(0)); - - $gatewayMock->expects($this->once()) - ->method('deleteSection') - ->with( - $this->equalTo(23) - ); - - $result = $handler->delete(23); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::delete - */ - public function testDeleteFailure() - { - $this->expectException(\RuntimeException::class); - - $handler = $this->getSectionHandler(); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('countContentObjectsInSection') - ->with($this->equalTo(23)) - ->will($this->returnValue(2)); - - $gatewayMock->expects($this->never()) - ->method('deleteSection'); - - $result = $handler->delete(23); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::assign - */ - public function testAssign() - { - $handler = $this->getSectionHandler(); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('assignSectionToContent') - ->with( - $this->equalTo(23), - $this->equalTo(42) - ); - - $result = $handler->assign(23, 42); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::policiesCount - */ - public function testPoliciesCount() - { - $handler = $this->getSectionHandler(); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('countPoliciesUsingSection') - ->with( - $this->equalTo(1) - ) - ->will( - $this->returnValue(7) - ); - - $result = $handler->policiesCount(1); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler::countRoleAssignmentsUsingSection - */ - public function testCountRoleAssignmentsUsingSection() - { - $handler = $this->getSectionHandler(); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('countRoleAssignmentsUsingSection') - ->with( - $this->equalTo(1) - ) - ->will( - $this->returnValue(0) - ); - - $handler->countRoleAssignmentsUsingSection(1); - } - - /** - * Returns the section handler to test. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler - */ - protected function getSectionHandler() - { - if (!isset($this->sectionHandler)) { - $this->sectionHandler = new Handler( - $this->getGatewayMock() - ); - } - - return $this->sectionHandler; - } - - /** - * Returns a mock for the section gateway. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway - */ - protected function getGatewayMock() - { - if (!isset($this->gatewayMock)) { - $this->gatewayMock = $this->getMockForAbstractClass(Gateway::class); - } - - return $this->gatewayMock; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/StorageRegistryTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/StorageRegistryTest.php deleted file mode 100644 index 4b97f7b56d..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/StorageRegistryTest.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content; - -use eZ\Publish\Core\FieldType\NullStorage; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\FieldType\FieldStorage; - -/** - * Test case for StorageRegistry. - */ -class StorageRegistryTest extends TestCase -{ - private const TYPE_NAME = 'some-type'; - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry::register - */ - public function testRegister(): void - { - $storage = $this->getStorageMock(); - $registry = new StorageRegistry([self::TYPE_NAME => $storage]); - - $this->assertSame($storage, $registry->getStorage(self::TYPE_NAME)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry::getStorage - */ - public function testGetStorage() - { - $storage = $this->getStorageMock(); - $registry = new StorageRegistry([self::TYPE_NAME => $storage]); - - $res = $registry->getStorage(self::TYPE_NAME); - - $this->assertSame( - $storage, - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry::getStorage - * @covers \eZ\Publish\Core\Persistence\Legacy\Exception\StorageNotFound - */ - public function testGetNotFound() - { - $registry = new StorageRegistry([]); - self::assertInstanceOf( - NullStorage::class, - $registry->getStorage('not-found') - ); - } - - /** - * Returns a mock for Storage. - * - * @return Storage - */ - protected function getStorageMock() - { - return $this->createMock(FieldStorage::class); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/ContentTypeHandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/ContentTypeHandlerTest.php deleted file mode 100644 index a2eaf589d5..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/ContentTypeHandlerTest.php +++ /dev/null @@ -1,1260 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type; - -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler as UpdateHandler; -use eZ\Publish\Core\Persistence\Legacy\Exception; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\Group; -use eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct as GroupCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct as GroupUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct; -use PHPUnit\Framework\TestCase; - -/** - * Test case for Content Type Handler. - */ -class ContentTypeHandlerTest extends TestCase -{ - /** - * Gateway mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway - */ - protected $gatewayMock; - - /** - * Mapper mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper - */ - protected $mapperMock; - - /** - * Update\Handler mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler - */ - protected $updateHandlerMock; - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::createGroup - */ - public function testCreateGroup() - { - $createStruct = new GroupCreateStruct(); - - $mapperMock = $this->getMapperMock(); - $mapperMock->expects($this->once()) - ->method('createGroupFromCreateStruct') - ->with( - $this->isInstanceOf( - GroupCreateStruct::class - ) - ) - ->will( - $this->returnValue(new Group()) - ); - - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('insertGroup') - ->with( - $this->isInstanceOf( - Group::class - ) - ) - ->will($this->returnValue(23)); - - $handler = $this->getHandler(); - $group = $handler->createGroup( - new GroupCreateStruct() - ); - - $this->assertInstanceOf( - Group::class, - $group - ); - $this->assertEquals( - 23, - $group->id - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::updateGroup - */ - public function testUpdateGroup() - { - $updateStruct = new GroupUpdateStruct(); - $updateStruct->id = 23; - - $mapperMock = $this->getMapperMock(); - - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('updateGroup') - ->with( - $this->isInstanceOf( - GroupUpdateStruct::class - ) - ); - - $handlerMock = $this->getMockBuilder(Handler::class) - ->setMethods(['loadGroup']) - ->setConstructorArgs([$gatewayMock, $mapperMock, $this->getUpdateHandlerMock()]) - ->getMock(); - - $handlerMock->expects($this->once()) - ->method('loadGroup') - ->with( - $this->equalTo(23) - )->will( - $this->returnValue(new Group()) - ); - - $res = $handlerMock->updateGroup( - $updateStruct - ); - - $this->assertInstanceOf( - Group::class, - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::deleteGroup - */ - public function testDeleteGroupSuccess() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('countTypesInGroup') - ->with($this->equalTo(23)) - ->will($this->returnValue(0)); - $gatewayMock->expects($this->once()) - ->method('deleteGroup') - ->with($this->equalTo(23)); - - $handler = $this->getHandler(); - $handler->deleteGroup(23); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::deleteGroup - * @covers \eZ\Publish\Core\Persistence\Legacy\Exception\GroupNotEmpty - */ - public function testDeleteGroupFailure() - { - $this->expectException(\eZ\Publish\Core\Persistence\Legacy\Exception\GroupNotEmpty::class); - $this->expectExceptionMessage('Group with ID "23" is not empty.'); - - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('countTypesInGroup') - ->with($this->equalTo(23)) - ->will($this->returnValue(42)); - $gatewayMock->expects($this->never()) - ->method('deleteGroup'); - - $handler = $this->getHandler(); - $handler->deleteGroup(23); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::loadGroup - */ - public function testLoadGroup() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('loadGroupData') - ->with($this->equalTo([23])) - ->will($this->returnValue([])); - - $mapperMock = $this->getMapperMock(); - $mapperMock->expects($this->once()) - ->method('extractGroupsFromRows') - ->with($this->equalTo([])) - ->will($this->returnValue([new Group()])); - - $handler = $this->getHandler(); - $res = $handler->loadGroup(23); - - $this->assertEquals( - new Group(), - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::loadGroupByIdentifier - */ - public function testLoadGroupByIdentifier() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('loadGroupDataByIdentifier') - ->with($this->equalTo('content')) - ->will($this->returnValue([])); - - $mapperMock = $this->getMapperMock(); - $mapperMock->expects($this->once()) - ->method('extractGroupsFromRows') - ->with($this->equalTo([])) - ->will($this->returnValue([new Group()])); - - $handler = $this->getHandler(); - $res = $handler->loadGroupByIdentifier('content'); - - $this->assertEquals( - new Group(), - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::loadAllGroups - */ - public function testLoadAllGroups() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('loadAllGroupsData') - ->will($this->returnValue([])); - - $mapperMock = $this->getMapperMock(); - $mapperMock->expects($this->once()) - ->method('extractGroupsFromRows') - ->with($this->equalTo([])) - ->will($this->returnValue([new Group()])); - - $handler = $this->getHandler(); - $res = $handler->loadAllGroups(); - - $this->assertEquals( - [new Group()], - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::loadContentTypes - */ - public function testLoadContentTypes() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('loadTypesDataForGroup') - ->with($this->equalTo(23), $this->equalTo(0)) - ->will($this->returnValue([])); - - $mapperMock = $this->getMapperMock(); - $mapperMock->expects($this->once()) - ->method('extractTypesFromRows') - ->with($this->equalTo([])) - ->will($this->returnValue([new Type()])); - - $handler = $this->getHandler(); - $res = $handler->loadContentTypes(23, 0); - - $this->assertEquals( - [new Type()], - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::loadContentTypeList - */ - public function testLoadContentTypeList(): void - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('loadTypesListData') - ->with($this->equalTo([23, 24])) - ->willReturn([]); - - $mapperMock = $this->getMapperMock(); - $mapperMock->expects($this->once()) - ->method('extractTypesFromRows') - ->with($this->equalTo([])) - ->willReturn([23 => new Type()]); - - $handler = $this->getHandler(); - $types = $handler->loadContentTypeList([23, 24]); - - $this->assertEquals( - [23 => new Type()], - $types, - 'Types not loaded correctly' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::load - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::loadFromRows - */ - public function testLoad() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('loadTypeData') - ->with( - $this->equalTo(23), - $this->equalTo(1) - ) - ->will($this->returnValue([])); - - $mapperMock = $this->getMapperMock(); - $mapperMock->expects($this->once()) - ->method('extractTypesFromRows') - ->with($this->equalTo([])) - ->will( - $this->returnValue( - [new Type()] - ) - ); - - $handler = $this->getHandler(); - $type = $handler->load(23, 1); - - $this->assertEquals( - new Type(), - $type, - 'Type not loaded correctly' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::load - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::loadFromRows - */ - public function testLoadNotFound() - { - $this->expectException(\eZ\Publish\Core\Persistence\Legacy\Exception\TypeNotFound::class); - - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('loadTypeData') - ->with( - $this->equalTo(23), - $this->equalTo(1) - ) - ->will($this->returnValue([])); - - $mapperMock = $this->getMapperMock(); - $mapperMock->expects($this->once()) - ->method('extractTypesFromRows') - ->with($this->equalTo([])) - ->will( - $this->returnValue( - [] - ) - ); - - $handler = $this->getHandler(); - $type = $handler->load(23, 1); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::load - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::loadFromRows - */ - public function testLoadDefaultVersion() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('loadTypeData') - ->with( - $this->equalTo(23), - $this->equalTo(0) - ) - ->will($this->returnValue([])); - - $mapperMock = $this->getMapperMock(); - $mapperMock->expects($this->once()) - ->method('extractTypesFromRows') - ->will( - $this->returnValue( - [new Type()] - ) - ); - - $handler = $this->getHandler(); - $type = $handler->load(23); - - $this->assertEquals( - new Type(), - $type, - 'Type not loaded correctly' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::loadByIdentifier - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::loadFromRows - */ - public function testLoadByIdentifier() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('loadTypeDataByIdentifier') - ->with( - $this->equalTo('blogentry'), - $this->equalTo(0) - ) - ->will($this->returnValue([])); - - $mapperMock = $this->getMapperMock(); - $mapperMock->expects($this->once()) - ->method('extractTypesFromRows') - ->will( - $this->returnValue( - [new Type()] - ) - ); - - $handler = $this->getHandler(); - $type = $handler->loadByIdentifier('blogentry'); - - $this->assertEquals( - new Type(), - $type, - 'Type not loaded correctly' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::loadByRemoteId - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::loadFromRows - */ - public function testLoadByRemoteId() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('loadTypeDataByRemoteId') - ->with( - $this->equalTo('someLongHash'), - $this->equalTo(0) - ) - ->will($this->returnValue([])); - - $mapperMock = $this->getMapperMock(); - $mapperMock->expects($this->once()) - ->method('extractTypesFromRows') - ->will( - $this->returnValue( - [new Type()] - ) - ); - - $handler = $this->getHandler(); - $type = $handler->loadByRemoteId('someLongHash'); - - $this->assertEquals( - new Type(), - $type, - 'Type not loaded correctly' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::create - */ - public function testCreate() - { - $createStructFix = $this->getContentTypeCreateStructFixture(); - $createStructClone = clone $createStructFix; - - $mapperMock = $this->getMapperMock( - [ - 'toStorageFieldDefinition', - ] - ); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('insertType') - ->with( - $this->isInstanceOf( - Type::class - ) - ) - ->will($this->returnValue(23)); - $gatewayMock->expects($this->once()) - ->method('insertGroupAssignment') - ->with( - $this->equalTo(42), - $this->equalTo(23), - $this->equalTo(1) - ); - $gatewayMock->expects($this->exactly(2)) - ->method('insertFieldDefinition') - ->with( - $this->equalTo(23), - $this->equalTo(1), - $this->isInstanceOf(FieldDefinition::class), - $this->isInstanceOf(StorageFieldDefinition::class) - ) - ->will($this->returnValue(42)); - - $mapperMock->expects($this->exactly(2)) - ->method('toStorageFieldDefinition') - ->with( - $this->isInstanceOf(FieldDefinition::class), - $this->isInstanceOf(StorageFieldDefinition::class) - ); - - $handler = $this->getHandler(); - $type = $handler->create($createStructFix); - - $this->assertInstanceOf( - Type::class, - $type, - 'Incorrect type returned from create()' - ); - $this->assertEquals( - 23, - $type->id, - 'Incorrect ID for Type.' - ); - - $this->assertEquals( - 42, - $type->fieldDefinitions[0]->id, - 'Field definition ID not set correctly' - ); - $this->assertEquals( - 42, - $type->fieldDefinitions[1]->id, - 'Field definition ID not set correctly' - ); - - $this->assertEquals( - $createStructClone, - $createStructFix, - 'Create struct manipulated' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::update - */ - public function testUpdate() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('updateType') - ->with( - $this->equalTo(23), - $this->equalTo(1), - $this->isInstanceOf( - Type::class - ) - ); - - $handlerMock = $this->getMockBuilder(Handler::class) - ->setMethods(['load']) - ->setConstructorArgs([$gatewayMock, $this->getMapperMock(), $this->getUpdateHandlerMock()]) - ->getMock(); - - $handlerMock->expects($this->once()) - ->method('load') - ->with( - $this->equalTo(23), - $this->equalTo(1) - ) - ->will($this->returnValue(new Type())); - - $res = $handlerMock->update(23, 1, new UpdateStruct()); - - $this->assertInstanceOf( - Type::class, - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::delete - */ - public function testDeleteSuccess() - { - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects( - $this->once() - )->method( - 'countInstancesOfType' - )->with( - $this->equalTo(23) - )->will( - $this->returnValue(0) - ); - - $gatewayMock->expects( - $this->once() - )->method( - 'delete' - )->with( - $this->equalTo(23), - $this->equalTo(0) - ); - - $handler = $this->getHandler(); - $res = $handler->delete(23, 0); - - $this->assertTrue($res); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::delete - */ - public function testDeleteThrowsBadStateException() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\BadStateException::class); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects( - $this->once() - )->method( - 'countInstancesOfType' - )->with( - $this->equalTo(23) - )->will( - $this->returnValue(1) - ); - - $gatewayMock->expects($this->never())->method('delete'); - - $handler = $this->getHandler(); - $res = $handler->delete(23, 0); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::createDraft - */ - public function testCreateVersion() - { - $userId = 42; - $contentTypeId = 23; - - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(); - $mapperMock->expects($this->once()) - ->method('createCreateStructFromType') - ->with( - $this->isInstanceOf( - Type::class - ) - )->will( - $this->returnValue(new CreateStruct()) - ); - - $handlerMock = $this->getMockBuilder(Handler::class) - ->setMethods(['load', 'internalCreate']) - ->setConstructorArgs([$gatewayMock, $mapperMock, $this->getUpdateHandlerMock()]) - ->getMock(); - - $handlerMock->expects($this->once()) - ->method('load') - ->with( - $this->equalTo($contentTypeId, Type::STATUS_DEFINED) - )->will( - $this->returnValue( - new Type() - ) - ); - - $typeDraft = new Type(); - $handlerMock->expects($this->once()) - ->method('internalCreate') - ->with( - $this->isInstanceOf(CreateStruct::class), - $this->equalTo($contentTypeId) - )->will( - $this->returnValue($typeDraft) - ); - - $res = $handlerMock->createDraft($userId, $contentTypeId); - - $this->assertSame( - $typeDraft, - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::copy - */ - public function testCopy() - { - $gatewayMock = $this->getGatewayMock(); - $mapperMock = $this->getMapperMock(['createCreateStructFromType']); - $mapperMock->expects($this->once()) - ->method('createCreateStructFromType') - ->with( - $this->isInstanceOf( - Type::class - ) - )->willReturn( - new CreateStruct(['identifier' => 'testCopy']) - ); - - $handlerMock = $this->getMockBuilder(Handler::class) - ->setMethods(['load', 'internalCreate', 'update']) - ->setConstructorArgs([$gatewayMock, $mapperMock, $this->getUpdateHandlerMock()]) - ->getMock(); - - $userId = 42; - $type = new Type([ - 'id' => 23, - 'identifier' => md5(uniqid(get_class($handlerMock), true)), - 'status' => Type::STATUS_DEFINED, - ]); - - $handlerMock->expects($this->once()) - ->method('load') - ->with( - $this->equalTo($type->id, Type::STATUS_DEFINED) - )->willReturn( - $type - ); - - $typeCopy = clone $type; - $typeCopy->id = 24; - $typeCopy->identifier = 'copy_of' . $type->identifier . '_' . $type->id; - - $handlerMock->expects($this->once()) - ->method('internalCreate') - ->with( - $this->isInstanceOf(CreateStruct::class), - )->willReturn( - $typeCopy - ); - - $handlerMock->expects($this->once()) - ->method('update') - ->with( - $this->equalTo($typeCopy->id), - $this->equalTo(Type::STATUS_DEFINED), - $this->isInstanceOf(UpdateStruct::class) - ) - ->will( - $this->returnValue($typeCopy) - ); - - $res = $handlerMock->copy($userId, $type->id, Type::STATUS_DEFINED); - - $this->assertEquals( - $typeCopy, - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::link - */ - public function testLink() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('insertGroupAssignment') - ->with( - $this->equalTo(3), - $this->equalTo(23), - $this->equalTo(1) - ); - - $mapperMock = $this->getMapperMock(); - - $handler = $this->getHandler(); - $res = $handler->link(3, 23, 1); - - $this->assertTrue($res); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::unlink - */ - public function testUnlinkSuccess() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('countGroupsForType') - ->with( - $this->equalTo(23), - $this->equalTo(1) - )->will($this->returnValue(2)); - - $gatewayMock->expects($this->once()) - ->method('deleteGroupAssignment') - ->with( - $this->equalTo(3), - $this->equalTo(23), - $this->equalTo(1) - ); - - $mapperMock = $this->getMapperMock(); - - $handler = $this->getHandler(); - $res = $handler->unlink(3, 23, 1); - - $this->assertTrue($res); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::unlink - * @covers \eZ\Publish\Core\Persistence\Legacy\Exception\RemoveLastGroupFromType - */ - public function testUnlinkFailure() - { - $this->expectException(\eZ\Publish\Core\Persistence\Legacy\Exception\RemoveLastGroupFromType::class); - $this->expectExceptionMessage('Type with ID "23" in status "1" cannot be unlinked from its last group.'); - - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('countGroupsForType') - ->with( - $this->equalTo(23), - $this->equalTo(1) - ) - // Only 1 group assigned - ->will($this->returnValue(1)); - - $mapperMock = $this->getMapperMock(); - - $handler = $this->getHandler(); - $res = $handler->unlink(3, 23, 1); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::getFieldDefinition - */ - public function testGetFieldDefinition() - { - $mapperMock = $this->getMapperMock( - [ - 'extractFieldFromRow', - 'extractMultilingualData', - ] - ); - $mapperMock->expects($this->once()) - ->method('extractFieldFromRow') - ->with( - $this->equalTo([]) - )->will( - $this->returnValue(new FieldDefinition()) - ); - - $mapperMock->expects($this->once()) - ->method('extractMultilingualData') - ->with( - $this->equalTo([ - [], - ]) - )->will( - $this->returnValue([]) - ); - - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('loadFieldDefinition') - ->with( - $this->equalTo(42), - $this->equalTo(Type::STATUS_DEFINED) - )->will( - $this->returnValue([ - [], - ]) - ); - - $handler = $this->getHandler(); - $fieldDefinition = $handler->getFieldDefinition(42, Type::STATUS_DEFINED); - - $this->assertInstanceOf( - FieldDefinition::class, - $fieldDefinition - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::addFieldDefinition - */ - public function testAddFieldDefinition() - { - $mapperMock = $this->getMapperMock( - ['toStorageFieldDefinition'] - ); - $mapperMock->expects($this->once()) - ->method('toStorageFieldDefinition') - ->with( - $this->isInstanceOf( - FieldDefinition::class - ), - $this->isInstanceOf( - StorageFieldDefinition::class - ) - ); - - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('insertFieldDefinition') - ->with( - $this->equalTo(23), - $this->equalTo(1), - $this->isInstanceOf( - FieldDefinition::class - ), - $this->isInstanceOf( - StorageFieldDefinition::class - ) - )->will( - $this->returnValue(42) - ); - - $fieldDef = new FieldDefinition(); - - $handler = $this->getHandler(); - $handler->addFieldDefinition(23, 1, $fieldDef); - - $this->assertEquals( - 42, - $fieldDef->id - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::getContentCount - */ - public function testGetContentCount() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('countInstancesOfType') - ->with( - $this->equalTo(23) - )->will( - $this->returnValue(42) - ); - - $handler = $this->getHandler(); - - $this->assertEquals( - 42, - $handler->getContentCount(23) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::removeFieldDefinition - */ - public function testRemoveFieldDefinition() - { - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('deleteFieldDefinition') - ->with( - $this->equalTo(23), - $this->equalTo(1), - $this->equalTo(42) - ); - - $handler = $this->getHandler(); - $res = $handler->removeFieldDefinition(23, 1, 42); - - $this->assertTrue($res); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::updateFieldDefinition - */ - public function testUpdateFieldDefinition() - { - $mapperMock = $this->getMapperMock( - ['toStorageFieldDefinition'] - ); - $mapperMock->expects($this->once()) - ->method('toStorageFieldDefinition') - ->with( - $this->isInstanceOf( - FieldDefinition::class - ), - $this->isInstanceOf( - StorageFieldDefinition::class - ) - ); - - $gatewayMock = $this->getGatewayMock(); - $gatewayMock->expects($this->once()) - ->method('updateFieldDefinition') - ->with( - $this->equalTo(23), - $this->equalTo(1), - $this->isInstanceOf( - FieldDefinition::class - ) - ); - - $fieldDef = new FieldDefinition(); - - $handler = $this->getHandler(); - $res = $handler->updateFieldDefinition(23, 1, $fieldDef); - - $this->assertNull($res); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::publish - */ - public function testPublish() - { - $handler = $this->getPartlyMockedHandler(['load']); - $updateHandlerMock = $this->getUpdateHandlerMock(); - - $handler->expects($this->exactly(2)) - ->method('load') - ->with( - $this->equalTo(23), - $this->logicalOr( - $this->equalTo(0), - $this->equalTo(1) - ) - )->will( - $this->returnValue(new Type()) - ); - - $updateHandlerMock->expects($this->never()) - ->method('updateContentObjects'); - - $updateHandlerMock->expects($this->once()) - ->method('deleteOldType') - ->with( - $this->isInstanceOf(Type::class) - ); - $updateHandlerMock->expects($this->once()) - ->method('publishNewType') - ->with( - $this->isInstanceOf(Type::class), - $this->equalTo(0) - ); - - $handler->publish(23); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::publish - */ - public function testPublishNoOldType() - { - $handler = $this->getPartlyMockedHandler(['load']); - $updateHandlerMock = $this->getUpdateHandlerMock(); - - $handler->expects($this->at(0)) - ->method('load') - ->with( - $this->equalTo(23), - $this->equalTo(1) - )->will( - $this->returnValue(new Type()) - ); - - $handler->expects($this->at(1)) - ->method('load') - ->with( - $this->equalTo(23), - $this->equalTo(0) - )->will( - $this->throwException(new Exception\TypeNotFound(23, 0)) - ); - - $updateHandlerMock->expects($this->never()) - ->method('updateContentObjects'); - $updateHandlerMock->expects($this->never()) - ->method('deleteOldType'); - $updateHandlerMock->expects($this->once()) - ->method('publishNewType') - ->with( - $this->isInstanceOf(Type::class), - $this->equalTo(0) - ); - - $handler->publish(23); - } - - /** - * Returns a handler to test, based on mock objects. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler - */ - protected function getHandler() - { - return new Handler( - $this->getGatewayMock(), - $this->getMapperMock(), - $this->getUpdateHandlerMock() - ); - } - - /** - * Returns a handler to test with $methods mocked. - * - * @param array $methods - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler - */ - protected function getPartlyMockedHandler(array $methods) - { - return $this->getMockBuilder(Handler::class) - ->setMethods($methods) - ->setConstructorArgs( - [ - $this->getGatewayMock(), - $this->getMapperMock(), - $this->getUpdateHandlerMock(), - ] - ) - ->getMock(); - } - - /** - * Returns a gateway mock. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway - */ - protected function getGatewayMock() - { - if (!isset($this->gatewayMock)) { - $this->gatewayMock = $this->getMockForAbstractClass( - Gateway::class - ); - } - - return $this->gatewayMock; - } - - /** - * Returns a mapper mock. - * - * @param array $methods - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper - */ - protected function getMapperMock($methods = []) - { - if (!isset($this->mapperMock)) { - $this->mapperMock = $this->getMockBuilder(Mapper::class) - ->disableOriginalConstructor() - ->setMethods($methods) - ->getMock(); - } - - return $this->mapperMock; - } - - /** - * Returns a Update\Handler mock. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler - */ - public function getUpdateHandlerMock() - { - if (!isset($this->updateHandlerMock)) { - $this->updateHandlerMock = $this->getMockBuilder(UpdateHandler::class) - ->disableOriginalConstructor() - ->setMethods([]) - ->getMock(); - } - - return $this->updateHandlerMock; - } - - /** - * Returns a CreateStruct fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\CreateStruct - */ - protected function getContentTypeCreateStructFixture() - { - $struct = new CreateStruct(); - $struct->status = 1; - $struct->groupIds = [ - 42, - ]; - $struct->name = [ - 'eng-GB' => 'test name', - ]; - - $fieldDefName = new FieldDefinition(['position' => 1]); - $fieldDefShortDescription = new FieldDefinition(['position' => 2]); - - $struct->fieldDefinitions = [ - $fieldDefName, - $fieldDefShortDescription, - ]; - - return $struct; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler::removeContentTypeTranslation - */ - public function testRemoveContentTypeTranslation() - { - $mapperMock = $this->getMapperMock(); - $mapperMock->expects($this->once()) - ->method('createUpdateStructFromType') - ->with( - $this->isInstanceOf( - Type::class - ) - ) - ->will( - $this->returnValue(new UpdateStruct()) - ); - - $handlerMock = $this->getMockBuilder(Handler::class) - ->setMethods(['load', 'update']) - ->setConstructorArgs([$this->getGatewayMock(), $mapperMock, $this->getUpdateHandlerMock()]) - ->getMock(); - - $handlerMock->expects($this->once()) - ->method('load') - ->with( - $this->equalTo(23), - $this->equalTo(1) - ) - ->will($this->returnValue(new Type(['id' => 23]))); - - $handlerMock->expects($this->once()) - ->method('update') - ->with( - $this->equalTo(23), - $this->equalTo(1), - $this->isInstanceOf( - UpdateStruct::class - ) - ) - ->will($this->returnValue(new Type())); - - $res = $handlerMock->removeContentTypeTranslation(23, 'eng-GB'); - - $this->assertInstanceOf( - Type::class, - $res - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index 524a334df8..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,1251 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type\Gateway; - -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase; -// For SORT_ORDER_* constants -use eZ\Publish\Core\Persistence\Legacy\Tests\Content\LanguageAwareTestCase; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\Group; -use eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct as GroupUpdateStruct; - -/** - * Test case for eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase. - */ -class DoctrineDatabaseTest extends LanguageAwareTestCase -{ - /** - * The DoctrineDatabase gateway to test. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase - */ - protected $gateway; - - protected function setUp(): void - { - parent::setUp(); - - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/languages.php'); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::insertGroup - */ - public function testInsertGroup() - { - $gateway = $this->getGateway(); - - $group = $this->getGroupFixture(); - - $id = $gateway->insertGroup($group); - - $this->assertQueryResult( - [ - [ - 'id' => '1', - 'created' => '1032009743', - 'creator_id' => '14', - 'modified' => '1033922120', - 'modifier_id' => '14', - 'name' => 'Media', - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select( - 'id', - 'created', - 'creator_id', - 'modified', - 'modifier_id', - 'name' - ) - ->from('ezcontentclassgroup') - ); - } - - /** - * Returns a Group fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group - */ - protected function getGroupFixture() - { - $group = new Group(); - - $group->name = [ - 'always-available' => 'eng-GB', - 'eng-GB' => 'Media', - ]; - $group->description = [ - 'always-available' => 'eng-GB', - 'eng-GB' => '', - ]; - $group->identifier = 'Media'; - $group->created = 1032009743; - $group->modified = 1033922120; - $group->creatorId = 14; - $group->modifierId = 14; - - return $group; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::updateGroup - */ - public function testUpdateGroup() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_groups.php' - ); - - $gateway = $this->getGateway(); - - $struct = $this->getGroupUpdateStructFixture(); - - $res = $gateway->updateGroup($struct); - - $this->assertQueryResult( - [ - ['3'], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('COUNT(*)') - ->from('ezcontentclassgroup') - ); - - $q = $this->getDatabaseConnection()->createQueryBuilder(); - $q - ->select( - 'id', - 'created', - 'creator_id', - 'modified', - 'modifier_id', - 'name' - ) - ->from('ezcontentclassgroup') - ->orderBy('id'); - $this->assertQueryResult( - [ - [ - 'id' => 1, - 'created' => 1031216928, - 'creator_id' => 14, - 'modified' => 1033922106, - 'modifier_id' => 14, - 'name' => 'Content', - ], - [ - 'id' => 2, - 'created' => 1031216941, - 'creator_id' => 14, - 'modified' => 1311454096, - 'modifier_id' => 23, - 'name' => 'UpdatedGroup', - ], - [ - 'id' => 3, - 'created' => 1032009743, - 'creator_id' => 14, - 'modified' => 1033922120, - 'modifier_id' => 14, - 'name' => 'Media', - ], - ], - $q - ); - } - - /** - * Returns a Group update struct fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct - */ - protected function getGroupUpdateStructFixture() - { - $struct = new GroupUpdateStruct(); - - $struct->id = 2; - $struct->name = [ - 'always-available' => 'eng-GB', - 'eng-GB' => 'UpdatedGroupName', - ]; - $struct->description = [ - 'always-available' => 'eng-GB', - 'eng-GB' => '', - ]; - $struct->identifier = 'UpdatedGroup'; - $struct->modified = 1311454096; - $struct->modifierId = 23; - - return $struct; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::countTypesInGroup - */ - public function testCountTypesInGroup() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - - $this->assertEquals( - 3, - $gateway->countTypesInGroup(1) - ); - $this->assertEquals( - 0, - $gateway->countTypesInGroup(23) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::countGroupsForType - */ - public function testCountGroupsForType() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - - $this->assertEquals( - 1, - $gateway->countGroupsForType(1, 1) - ); - $this->assertEquals( - 0, - $gateway->countGroupsForType(23, 0) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::deleteGroup - */ - public function testDeleteGroup() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_groups.php' - ); - - $gateway = $this->getGateway(); - - $gateway->deleteGroup(2); - - $this->assertQueryResult( - [ - ['1'], - ['3'], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('id') - ->from('ezcontentclassgroup') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::loadGroupData - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::createGroupLoadQuery - */ - public function testLoadGroupData() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_groups.php' - ); - - $gateway = $this->getGateway(); - $data = $gateway->loadGroupData([2]); - - $this->assertEquals( - [ - [ - 'created' => '1031216941', - 'creator_id' => '14', - 'id' => '2', - 'modified' => '1033922113', - 'modifier_id' => '14', - 'name' => 'Users', - ], - ], - $data - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::loadGroupDataByIdentifier - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::createGroupLoadQuery - */ - public function testLoadGroupDataByIdentifier() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_groups.php' - ); - - $gateway = $this->getGateway(); - $data = $gateway->loadGroupDataByIdentifier('Users'); - - $this->assertEquals( - [ - [ - 'created' => '1031216941', - 'creator_id' => '14', - 'id' => '2', - 'modified' => '1033922113', - 'modifier_id' => '14', - 'name' => 'Users', - ], - ], - $data - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::loadAllGroupsData - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::createGroupLoadQuery - */ - public function testLoadAllGroupsData() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_groups.php' - ); - - $gateway = $this->getGateway(); - $data = $gateway->loadAllGroupsData(); - - $this->assertCount( - 3, - $data - ); - - $this->assertEquals( - [ - 'created' => '1031216941', - 'creator_id' => '14', - 'id' => '2', - 'modified' => '1033922113', - 'modifier_id' => '14', - 'name' => 'Users', - ], - $data[1] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::loadTypesDataForGroup - */ - public function testLoadTypesDataForGroup() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - $rows = $gateway->loadTypesDataForGroup(1, 0); - - $this->assertCount( - 4, - $rows - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::loadTypeData - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::getLoadTypeQuery - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::selectColumns - */ - public function testLoadTypeData() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - $rows = $gateway->loadTypeData(1, 0); - - $this->assertCount( - 3, - $rows - ); - $this->assertCount( - 50, - $rows[0] - ); - - /* - * Store mapper fixture - * - file_put_contents( - dirname( __DIR__ ) . '/_fixtures/map_load_type.php', - "<?php\n\nreturn " . var_export( $rows, true ) . ";\n" - ); - */ - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::loadTypeDataByIdentifier - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::getLoadTypeQuery - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::selectColumns - */ - public function testLoadTypeDataByIdentifier() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - $rows = $gateway->loadTypeDataByIdentifier('folder', 0); - - $this->assertCount( - 3, - $rows - ); - $this->assertCount( - 50, - $rows[0] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::loadTypeDataByRemoteId - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::getLoadTypeQuery - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::selectColumns - */ - public function testLoadTypeDataByRemoteId() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - $rows = $gateway->loadTypeDataByRemoteId('a3d405b81be900468eb153d774f4f0d2', 0); - - $this->assertCount( - 3, - $rows - ); - $this->assertCount( - 50, - $rows[0] - ); - } - - /** - * Returns the expected data from creating a type. - * - * @return string[][] - */ - public static function getTypeCreationExpectations() - { - return [ - ['always_available', 0], - ['contentobject_name', '<short_name|name>'], - ['created', '1024392098'], - ['creator_id', '14'], - ['identifier', 'folder'], - ['initial_language_id', '2'], - ['is_container', '1'], - ['language_mask', 7], - ['modified', '1082454875'], - ['modifier_id', '14'], - ['remote_id', 'a3d405b81be900468eb153d774f4f0d2'], - ['serialized_description_list', 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}'], - ['serialized_name_list', 'a:3:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:6:"Folder";s:6:"eng-GB";s:11:"Folder (GB)";}'], - ['sort_field', 7], - ['sort_order', 1], - ['url_alias_name', ''], - ['version', '0'], - ]; - } - - /** - * @dataProvider getTypeCreationExpectations - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::insertType - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::insertTypeNameData - */ - public function testInsertType($column, $expectation) - { - $gateway = $this->getGateway(); - $type = $this->getTypeFixture(); - - $gateway->insertType($type); - - $this->assertQueryResult( - [[$expectation]], - $this->getDatabaseConnection()->createQueryBuilder() - ->select($column) - ->from('ezcontentclass'), - 'Inserted Type data incorrect in column ' . $column - ); - } - - /** - * Returns the data expected to be inserted in ezcontentclass_name. - * - * @return string[][] - */ - public static function getTypeCreationContentClassNameExpectations() - { - return [ - ['contentclass_version', [0, 0]], - ['language_id', [3, 4]], - ['language_locale', ['eng-US', 'eng-GB']], - ['name', ['Folder', 'Folder (GB)']], - ]; - } - - /** - * @dataProvider getTypeCreationContentClassNameExpectations - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::insertType - */ - public function testInsertTypeContentClassName($column, $expectation) - { - $gateway = $this->getGateway(); - $type = $this->getTypeFixture(); - - $gateway->insertType($type); - - $this->assertQueryResult( - array_map( - static function ($value) { - return [$value]; - }, - $expectation - ), - $this->getDatabaseConnection()->createQueryBuilder() - ->select($column) - ->from('ezcontentclass_name'), - 'Inserted Type data incorrect in column ' . $column - ); - } - - /** - * Returns a Type fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - protected function getTypeFixture() - { - $type = new Type(); - - $type->status = 0; - $type->name = [ - 'always-available' => 'eng-US', - 'eng-US' => 'Folder', - 'eng-GB' => 'Folder (GB)', - ]; - $type->description = [ - 0 => '', - 'always-available' => false, - ]; - $type->identifier = 'folder'; - $type->created = 1024392098; - $type->modified = 1082454875; - $type->creatorId = 14; - $type->modifierId = 14; - $type->remoteId = 'a3d405b81be900468eb153d774f4f0d2'; - $type->urlAliasSchema = ''; - $type->nameSchema = '<short_name|name>'; - $type->isContainer = true; - $type->initialLanguageId = 2; - $type->sortField = Location::SORT_FIELD_CLASS_NAME; - $type->sortOrder = Location::SORT_ORDER_ASC; - $type->languageCodes = [ - 'eng-US', - 'eng-GB', - ]; - - return $type; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::insertFieldDefinition - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::setCommonFieldColumns - */ - public function testInsertFieldDefinition() - { - $gateway = $this->getGateway(); - - $field = $this->getFieldDefinitionFixture(); - $storageField = $this->getStorageFieldDefinitionFixture(); - - $gateway->insertFieldDefinition(23, 1, $field, $storageField); - - $this->assertQueryResult( - [ - [ - 'contentclass_id' => '23', - 'serialized_name_list' => 'a:2:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:11:"Description";}', - 'serialized_description_list' => 'a:2:{s:16:"always-available";s:6:"eng-GB";s:6:"eng-GB";s:16:"Some description";}', - 'identifier' => 'description', - 'category' => 'meta', - 'placement' => '4', - 'data_type_string' => 'ezstring', - 'can_translate' => '1', - 'is_required' => '1', - 'is_information_collector' => '1', - 'version' => '1', - - 'data_float1' => '0.1', - 'data_float2' => '0.2', - 'data_float3' => '0.3', - 'data_float4' => '0.4', - 'data_int1' => '1', - 'data_int2' => '2', - 'data_int3' => '3', - 'data_int4' => '4', - 'data_text1' => 'a', - 'data_text2' => 'b', - 'data_text3' => 'c', - 'data_text4' => 'd', - 'data_text5' => 'e', - 'serialized_data_text' => 'a:2:{i:0;s:3:"foo";i:1;s:3:"bar";}', - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select( - 'contentclass_id', - 'serialized_name_list', - 'serialized_description_list', - 'identifier', - 'category', - 'placement', - 'data_type_string', - 'can_translate', - 'is_required', - 'is_information_collector', - 'version', - 'data_float1', - 'data_float2', - 'data_float3', - 'data_float4', - 'data_int1', - 'data_int2', - 'data_int3', - 'data_int4', - 'data_text1', - 'data_text2', - 'data_text3', - 'data_text4', - 'data_text5', - 'serialized_data_text' - ) - ->from('ezcontentclass_attribute'), - 'FieldDefinition not inserted correctly' - ); - } - - /** - * Returns a FieldDefinition fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition - */ - protected function getFieldDefinitionFixture() - { - $field = new FieldDefinition(); - - $field->name = [ - 'always-available' => 'eng-US', - 'eng-US' => 'Description', - ]; - $field->description = [ - 'always-available' => 'eng-GB', - 'eng-GB' => 'Some description', - ]; - $field->identifier = 'description'; - $field->fieldGroup = 'meta'; - $field->position = 4; - $field->fieldType = 'ezstring'; - $field->isTranslatable = true; - $field->isRequired = true; - $field->isInfoCollector = true; - // $field->fieldTypeConstraints ??? - $field->defaultValue = [ - 0 => '', - 'always-available' => false, - ]; - - return $field; - } - - /** - * Returns a StorageFieldDefinition fixture. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition - */ - protected function getStorageFieldDefinitionFixture() - { - $fieldDef = new StorageFieldDefinition(); - - $fieldDef->dataFloat1 = 0.1; - $fieldDef->dataFloat2 = 0.2; - $fieldDef->dataFloat3 = 0.3; - $fieldDef->dataFloat4 = 0.4; - - $fieldDef->dataInt1 = 1; - $fieldDef->dataInt2 = 2; - $fieldDef->dataInt3 = 3; - $fieldDef->dataInt4 = 4; - - $fieldDef->dataText1 = 'a'; - $fieldDef->dataText2 = 'b'; - $fieldDef->dataText3 = 'c'; - $fieldDef->dataText4 = 'd'; - $fieldDef->dataText5 = 'e'; - - $fieldDef->serializedDataText = [ - 'foo', 'bar', - ]; - - return $fieldDef; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::deleteFieldDefinition - */ - public function testDeleteFieldDefinition() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - - $gateway->deleteFieldDefinition(1, 0, 119); - - $this->assertQueryResult( - [[5]], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('COUNT(*)') - ->from('ezcontentclass_attribute') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::updateFieldDefinition - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::setCommonFieldColumns - */ - public function testUpdateFieldDefinition() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - $fieldDefinitionFixture = $this->getFieldDefinitionFixture(); - $fieldDefinitionFixture->id = 160; - $storageFieldDefinitionFixture = $this->getStorageFieldDefinitionFixture(); - - $gateway = $this->getGateway(); - $gateway->updateFieldDefinition(2, 0, $fieldDefinitionFixture, $storageFieldDefinitionFixture); - - $this->assertQueryResult( - [ - // "random" sample - [ - 'category' => 'meta', - 'contentclass_id' => '2', - 'version' => '0', - 'data_type_string' => 'ezstring', - 'identifier' => 'description', - 'is_information_collector' => '1', - 'placement' => '4', - 'serialized_description_list' => 'a:2:{s:16:"always-available";s:6:"eng-GB";s:6:"eng-GB";s:16:"Some description";}', - - 'data_float1' => '0.1', - 'data_float2' => '0.2', - 'data_float3' => '0.3', - 'data_float4' => '0.4', - 'data_int1' => '1', - 'data_int2' => '2', - 'data_int3' => '3', - 'data_int4' => '4', - 'data_text1' => 'a', - 'data_text2' => 'b', - 'data_text3' => 'c', - 'data_text4' => 'd', - 'data_text5' => 'e', - 'serialized_data_text' => 'a:2:{i:0;s:3:"foo";i:1;s:3:"bar";}', - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select( - 'category', - 'contentclass_id', - 'version', - 'data_type_string', - 'identifier', - 'is_information_collector', - 'placement', - 'serialized_description_list', - 'data_float1', - 'data_float2', - 'data_float3', - 'data_float4', - 'data_int1', - 'data_int2', - 'data_int3', - 'data_int4', - 'data_text1', - 'data_text2', - 'data_text3', - 'data_text4', - 'data_text5', - 'serialized_data_text' - ) - ->from('ezcontentclass_attribute') - ->where('id = 160'), - 'FieldDefinition not updated correctly' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::insertGroupAssignment - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::loadGroupData - */ - public function testInsertGroupAssignment() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_groups.php' - ); - - $gateway = $this->getGateway(); - - $gateway->insertGroupAssignment(3, 42, 1); - - $this->assertQueryResult( - [ - [ - 'contentclass_id' => '42', - 'contentclass_version' => '1', - 'group_id' => '3', - 'group_name' => 'Media', - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select( - 'contentclass_id', - 'contentclass_version', - 'group_id', - 'group_name' - )->from('ezcontentclass_classgroup') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::deleteGroupAssignment - */ - public function testDeleteGroupAssignment() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - - $gateway->deleteGroupAssignment(1, 1, 0); - - $this->assertQueryResult( - [['1']], - $this->getDatabaseConnection()->createQueryBuilder() - ->select( - 'COUNT(*)' - )->from('ezcontentclass_classgroup') - ->where('contentclass_id = 1') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::updateType - * @dataProvider getTypeUpdateExpectations - */ - public function testUpdateType($fieldName, $expectedValue) - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - - $type = $this->getUpdateTypeFixture(); - - $gateway->updateType(1, 0, $type); - - $this->assertQueryResult( - [ - [ - $fieldName => $expectedValue, - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select( - $fieldName - )->from('ezcontentclass') - ->where('id = 1 AND version = 0'), - "Incorrect value stored for '{$fieldName}'." - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::deleteTypeNameData - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::insertTypeNameData - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::updateType - */ - public function testUpdateTypeName() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - - $type = $this->getUpdateTypeFixture(); - - $gateway->updateType(1, 0, $type); - - $this->assertQueryResult( - [ - [ - 'contentclass_id' => 1, - 'contentclass_version' => 0, - 'language_id' => 3, - 'language_locale' => 'eng-US', - 'name' => 'New Folder', - ], - [ - 'contentclass_id' => 1, - 'contentclass_version' => 0, - 'language_id' => 4, - 'language_locale' => 'eng-GB', - 'name' => 'New Folder for you', - ], - ], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('*') - ->from('ezcontentclass_name') - ->where('contentclass_id = 1 AND contentclass_version = 0') - ); - } - - /** - * Returns expected data after update. - * - * Data provider for {@link testUpdateType()}. - * - * @return string[][] - */ - public static function getTypeUpdateExpectations() - { - return [ - ['serialized_name_list', 'a:3:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:10:"New Folder";s:6:"eng-GB";s:18:"New Folder for you";}'], - ['serialized_description_list', 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}'], - ['identifier', 'new_folder'], - ['modified', '1311621548'], - ['modifier_id', '42'], - ['remote_id', 'foobar'], - ['url_alias_name', 'some scheke'], - ['contentobject_name', '<short_name>'], - ['is_container', '0'], - ['initial_language_id', '23'], - ['sort_field', '3'], - ['sort_order', '0'], - ['always_available', '1'], - ]; - } - - /** - * Returns a eZ\Publish\SPI\Persistence\Content\Type fixture for update operation. - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - protected function getUpdateTypeFixture(): Type - { - $type = new Type(); - - $type->name = [ - 'always-available' => 'eng-US', - 'eng-US' => 'New Folder', - 'eng-GB' => 'New Folder for you', - ]; - $type->description = [ - 0 => '', - 'always-available' => false, - ]; - $type->identifier = 'new_folder'; - $type->modified = 1311621548; - $type->modifierId = 42; - $type->remoteId = 'foobar'; - $type->urlAliasSchema = 'some scheke'; - $type->nameSchema = '<short_name>'; - $type->isContainer = false; - $type->initialLanguageId = 23; - $type->sortField = 3; - $type->sortOrder = Location::SORT_ORDER_DESC; - $type->defaultAlwaysAvailable = true; - - return $type; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::countInstancesOfType - */ - public function testCountInstancesOfTypeExist() - { - $this->insertDatabaseFixture( - // Fixture for content objects - __DIR__ . '/../../_fixtures/contentobjects.php' - ); - - $gateway = $this->getGateway(); - $res = $gateway->countInstancesOfType(3, 0); - - $this->assertEquals( - 6, - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::countInstancesOfType - */ - public function testCountInstancesOfTypeNotExist() - { - $this->insertDatabaseFixture( - // Fixture for content objects - __DIR__ . '/../../_fixtures/contentobjects.php' - ); - - $gateway = $this->getGateway(); - $res = $gateway->countInstancesOfType(23422342, 1); - - $this->assertEquals( - 0, - $res - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::deleteFieldDefinitionsForType - */ - public function testDeleteFieldDefinitionsForTypeExisting() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - - $gateway->deleteFieldDefinitionsForType(1, 0); - - $countAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); - $countAffectedAttr - ->select('COUNT(*)') - ->from('ezcontentclass_attribute') - ->where( - $countAffectedAttr->expr()->eq( - 'contentclass_id', - 1 - ) - ); - // 1 left with version 1 - $this->assertQueryResult( - [[1]], - $countAffectedAttr - ); - - $countNotAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); - $countNotAffectedAttr->select('COUNT(*)') - ->from('ezcontentclass_attribute'); - - $this->assertQueryResult( - [[2]], - $countNotAffectedAttr - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::deleteFieldDefinitionsForType - */ - public function testDeleteFieldDefinitionsForTypeNotExisting() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - - $gateway->deleteFieldDefinitionsForType(23, 1); - - $countNotAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); - $countNotAffectedAttr->select('COUNT(*)') - ->from('ezcontentclass_attribute'); - - $this->assertQueryResult( - [[5]], - $countNotAffectedAttr - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::deleteGroupAssignmentsForType - */ - public function testDeleteGroupAssignmentsForTypeExisting() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - - $gateway->deleteGroupAssignmentsForType(1, 0); - - $countAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); - $countAffectedAttr->select('COUNT(*)') - ->from('ezcontentclass_classgroup'); - - $this->assertQueryResult( - [[2]], - $countAffectedAttr - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::deleteGroupAssignmentsForType - */ - public function testDeleteGroupAssignmentsForTypeNotExisting() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - - $gateway->deleteType(23, 1); - - $countAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); - $countAffectedAttr->select('COUNT(*)') - ->from('ezcontentclass_classgroup'); - - $this->assertQueryResult( - [[3]], - $countAffectedAttr - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::deleteType - */ - public function testDeleteTypeExisting() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - - $gateway->deleteType(1, 0); - - $countAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); - $countAffectedAttr->select('COUNT(*)') - ->from('ezcontentclass'); - - $this->assertQueryResult( - [[1]], - $countAffectedAttr - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::deleteType - */ - public function testDeleteTypeNotExisting() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/existing_types.php' - ); - - $gateway = $this->getGateway(); - - $gateway->deleteType(23, 1); - - $countAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); - $countAffectedAttr->select('COUNT(*)') - ->from('ezcontentclass'); - - $this->assertQueryResult( - [[2]], - $countAffectedAttr - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase::publishTypeAndFields - */ - public function testPublishTypeAndFields() - { - $this->insertDatabaseFixture( - __DIR__ . '/_fixtures/type_to_publish.php' - ); - - $gateway = $this->getGateway(); - $gateway->publishTypeAndFields(1, 1, 0); - - $this->assertQueryResult( - [[1]], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('COUNT( * )') - ->from('ezcontentclass') - ->where('id = 1 AND version = 0') - ); - - $this->assertQueryResult( - [[2]], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('COUNT( * )') - ->from('ezcontentclass_classgroup') - ->where('contentclass_id = 1 AND contentclass_version = 0') - ); - - $this->assertQueryResult( - [[3]], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('COUNT( * )') - ->from('ezcontentclass_attribute') - ->where('contentclass_id = 1 AND version = 0') - ); - - $this->assertQueryResult( - [[1]], - $this->getDatabaseConnection()->createQueryBuilder() - ->select('COUNT( * )') - ->from('ezcontentclass_name') - ->where('contentclass_id = 1 AND contentclass_version = 0') - ); - } - - /** - * Return the DoctrineDatabase gateway to test. - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function getGateway(): DoctrineDatabase - { - if (!isset($this->gateway)) { - $this->gateway = new DoctrineDatabase( - $this->getDatabaseConnection(), - $this->getSharedGateway(), - $this->getLanguageMaskGenerator() - ); - } - - return $this->gateway; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/_fixtures/existing_groups.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/_fixtures/existing_groups.php deleted file mode 100644 index 00e1f8bf65..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/_fixtures/existing_groups.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -return array( - 'ezcontentclassgroup' => array( - array( - 'created' => 1031216928, - 'creator_id' => 14, - 'id' => 1, - 'modified' => 1033922106, - 'modifier_id' => 14, - 'name' => 'Content', - ), - array( - 'created' => 1031216941, - 'creator_id' => 14, - 'id' => 2, - 'modified' => 1033922113, - 'modifier_id' => 14, - 'name' => 'Users', - ), - array( - 'created' => 1032009743, - 'creator_id' => 14, - 'id' => 3, - 'modified' => 1033922120, - 'modifier_id' => 14, - 'name' => 'Media', - ), - ), -); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/_fixtures/languages.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/_fixtures/languages.php deleted file mode 100644 index b3519fdda6..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/_fixtures/languages.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -return array( - 'ezcontent_language' => - array( - 0 => - array( - 'disabled' => '0', - 'id' => '2', - 'locale' => 'eng-US', - 'name' => 'English (American)', - ), - 1 => - array( - 'disabled' => '0', - 'id' => '4', - 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)', - ), - ), -); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/MapperTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/MapperTest.php deleted file mode 100644 index 32dca92140..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/MapperTest.php +++ /dev/null @@ -1,494 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -// Needed for $sortOrder and $sortField properties -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\Group; -use eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct as GroupCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct; - -/** - * Test case for Mapper. - */ -class MapperTest extends TestCase -{ - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper::createGroupFromCreateStruct - */ - public function testCreateGroupFromCreateStruct() - { - $createStruct = $this->getGroupCreateStructFixture(); - - $mapper = new Mapper($this->getConverterRegistryMock(), $this->getMaskGeneratorMock()); - - $group = $mapper->createGroupFromCreateStruct($createStruct); - - $this->assertInstanceOf( - Group::class, - $group - ); - $this->assertPropertiesCorrect( - [ - 'id' => null, - 'name' => [ - 'eng-GB' => 'Media', - ], - 'description' => [], - 'identifier' => 'Media', - 'created' => 1032009743, - 'modified' => 1033922120, - 'creatorId' => 14, - 'modifierId' => 14, - ], - $group - ); - } - - /** - * Returns a GroupCreateStruct fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct - */ - protected function getGroupCreateStructFixture() - { - $struct = new GroupCreateStruct(); - - $struct->name = [ - 'eng-GB' => 'Media', - ]; - $struct->description = []; - $struct->identifier = 'Media'; - $struct->created = 1032009743; - $struct->modified = 1033922120; - $struct->creatorId = 14; - $struct->modifierId = 14; - - return $struct; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper::createTypeFromCreateStruct - */ - public function testTypeFromCreateStruct() - { - $struct = $this->getContentTypeCreateStructFixture(); - - $mapper = new Mapper($this->getConverterRegistryMock(), $this->getMaskGeneratorMock()); - $type = $mapper->createTypeFromCreateStruct($struct); - - foreach ($struct as $propName => $propVal) { - $this->assertEquals( - $struct->$propName, - $type->$propName, - "Property \${$propName} not equal" - ); - } - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper::createTypeFromUpdateStruct - */ - public function testTypeFromUpdateStruct() - { - $struct = $this->getContentTypeUpdateStructFixture(); - - $mapper = new Mapper($this->getConverterRegistryMock(), $this->getMaskGeneratorMock()); - $type = $mapper->createTypeFromUpdateStruct($struct); - - $this->assertStructsEqual( - $struct, - $type - ); - } - - /** - * Returns a CreateStruct fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\CreateStruct - */ - protected function getContentTypeCreateStructFixture() - { - // Taken from example DB - $struct = new CreateStruct(); - $struct->name = [ - 'eng-US' => 'Folder', - ]; - $struct->status = 0; - $struct->description = []; - $struct->identifier = 'folder'; - $struct->created = 1024392098; - $struct->modified = 1082454875; - $struct->creatorId = 14; - $struct->modifierId = 14; - $struct->remoteId = 'a3d405b81be900468eb153d774f4f0d2'; - $struct->urlAliasSchema = ''; - $struct->nameSchema = '<short_name|name>'; - $struct->isContainer = true; - $struct->initialLanguageId = 2; - $struct->sortField = Location::SORT_FIELD_MODIFIED_SUBNODE; - $struct->sortOrder = Location::SORT_ORDER_ASC; - $struct->defaultAlwaysAvailable = true; - - $struct->groupIds = [ - 1, - ]; - - $fieldDefName = new FieldDefinition(); - - $fieldDefShortDescription = new FieldDefinition(); - - $struct->fieldDefinitions = [ - $fieldDefName, - $fieldDefShortDescription, - ]; - - return $struct; - } - - /** - * Returns a CreateStruct fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct - */ - protected function getContentTypeUpdateStructFixture(): UpdateStruct - { - // Taken from example DB - $struct = new UpdateStruct(); - $struct->name = [ - 'eng-US' => 'Folder', - ]; - $struct->description = []; - $struct->identifier = 'folder'; - $struct->modified = 1082454875; - $struct->modifierId = 14; - $struct->remoteId = md5(microtime() . uniqid()); - $struct->urlAliasSchema = ''; - $struct->nameSchema = '<short_name|name>'; - $struct->isContainer = true; - $struct->initialLanguageId = 2; - $struct->sortField = Location::SORT_FIELD_MODIFIED_SUBNODE; - $struct->sortOrder = Location::SORT_ORDER_ASC; - $struct->defaultAlwaysAvailable = true; - - return $struct; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper::createCreateStructFromType - */ - public function testCreateStructFromType() - { - $type = $this->getContentTypeFixture(); - - $mapper = new Mapper($this->getConverterRegistryMock(), $this->getMaskGeneratorMock()); - $struct = $mapper->createCreateStructFromType($type); - - // Iterate through struct, since it has fewer props - foreach ($struct as $propName => $propVal) { - $this->assertEquals( - $struct->$propName, - $type->$propName, - "Property \${$propName} not equal" - ); - } - } - - /** - * Returns a Type fixture. - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - protected function getContentTypeFixture() - { - // Taken from example DB - $type = new Type(); - $type->id = 23; - $type->name = [ - 'eng-US' => 'Folder', - ]; - $type->status = 0; - $type->description = []; - $type->identifier = 'folder'; - $type->created = 1024392098; - $type->modified = 1082454875; - $type->creatorId = 14; - $type->modifierId = 14; - $type->remoteId = 'a3d405b81be900468eb153d774f4f0d2'; - $type->urlAliasSchema = ''; - $type->nameSchema = '<short_name|name>'; - $type->isContainer = true; - $type->initialLanguageId = 2; - $type->sortField = Location::SORT_FIELD_MODIFIED_SUBNODE; - $type->sortOrder = Location::SORT_ORDER_ASC; - $type->defaultAlwaysAvailable = true; - $type->groupIds = [ - 1, - ]; - - $fieldDefName = new FieldDefinition(); - $fieldDefName->id = 42; - - $fieldDefShortDescription = new FieldDefinition(); - $fieldDefName->id = 128; - - $type->fieldDefinitions = [ - $fieldDefName, - $fieldDefShortDescription, - ]; - - return $type; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper::extractGroupsFromRows - */ - public function testExtractGroupsFromRows() - { - $rows = $this->getLoadGroupFixture(); - - $mapper = new Mapper($this->getConverterRegistryMock(), $this->getMaskGeneratorMock()); - $groups = $mapper->extractGroupsFromRows($rows); - - $groupFixture = new Group(); - $groupFixture->created = 1032009743; - $groupFixture->creatorId = 14; - $groupFixture->id = 3; - $groupFixture->modified = 1033922120; - $groupFixture->modifierId = 14; - $groupFixture->identifier = 'Media'; - - $this->assertEquals( - [$groupFixture], - $groups - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper::extractTypesFromRows - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper::extractTypeFromRow - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper::extractStorageFieldFromRow - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper::extractFieldFromRow - */ - public function testExtractTypesFromRowsSingle() - { - $rows = $this->getLoadTypeFixture(); - - $mapper = $this->getNonConvertingMapper(); - $types = $mapper->extractTypesFromRows($rows); - - $this->assertCount( - 1, - $types, - 'Incorrect number of types extracted' - ); - - $this->assertPropertiesCorrect( - [ - 'id' => 1, - 'status' => 0, - 'name' => [ - 'eng-US' => 'Folder', - ], - 'description' => [], - 'created' => 1024392098, - 'creatorId' => 14, - 'modified' => 1082454875, - 'modifierId' => 14, - 'identifier' => 'folder', - 'remoteId' => 'a3d405b81be900468eb153d774f4f0d2', - 'urlAliasSchema' => '', - 'nameSchema' => '<short_name|name>', - 'isContainer' => true, - 'initialLanguageId' => 2, - 'groupIds' => [1], - 'sortField' => 1, - 'sortOrder' => 1, - 'defaultAlwaysAvailable' => true, - ], - $types[0] - ); - - $this->assertCount( - 4, - $types[0]->fieldDefinitions, - 'Incorrect number of field definitions' - ); - $this->assertPropertiesCorrect( - [ - 'id' => 155, - 'name' => [ - 'eng-US' => 'Short name', - ], - 'description' => [], - 'identifier' => 'short_name', - 'fieldGroup' => '', - 'fieldType' => 'ezstring', - 'isTranslatable' => true, - 'isRequired' => false, - 'isInfoCollector' => false, - 'isSearchable' => true, - 'position' => 2, - ], - $types[0]->fieldDefinitions[1] - ); - - $this->assertPropertiesCorrect( - [ - 'id' => 159, - 'name' => [], - 'description' => [], - 'identifier' => 'show_children', - 'fieldGroup' => '', - 'fieldType' => 'ezboolean', - 'isTranslatable' => false, - 'isRequired' => false, - 'isInfoCollector' => false, - 'isSearchable' => false, - 'position' => 6, - ], - $types[0]->fieldDefinitions[3] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper::toStorageFieldDefinition - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition - */ - public function testToStorageFieldDefinition() - { - $converterMock = $this->createMock(Converter::class); - $converterMock->expects($this->once()) - ->method('toStorageFieldDefinition') - ->with( - $this->isInstanceOf( - FieldDefinition::class - ), - $this->isInstanceOf( - StorageFieldDefinition::class - ) - ); - - $converterRegistry = new ConverterRegistry(['some_type' => $converterMock]); - - $mapper = new Mapper($converterRegistry, $this->getMaskGeneratorMock()); - - $fieldDef = new FieldDefinition(); - $fieldDef->fieldType = 'some_type'; - $fieldDef->name = [ - 'eng-GB' => 'some name', - ]; - - $storageFieldDef = new StorageFieldDefinition(); - - $mapper->toStorageFieldDefinition($fieldDef, $storageFieldDef); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper::toFieldDefinition - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition - */ - public function testToFieldDefinition() - { - $converterMock = $this->createMock(Converter::class); - $converterMock->expects($this->once()) - ->method('toFieldDefinition') - ->with( - $this->isInstanceOf( - StorageFieldDefinition::class - ), - $this->isInstanceOf( - FieldDefinition::class - ) - ); - - $converterRegistry = new ConverterRegistry(['some_type' => $converterMock]); - - $mapper = new Mapper($converterRegistry, $this->getMaskGeneratorMock()); - - $storageFieldDef = new StorageFieldDefinition(); - - $fieldDef = new FieldDefinition(); - $fieldDef->fieldType = 'some_type'; - - $mapper->toFieldDefinition($storageFieldDef, $fieldDef); - } - - /** - * Returns a Mapper with conversion methods mocked. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper - */ - protected function getNonConvertingMapper() - { - $mapper = $this->getMockBuilder(Mapper::class) - ->setMethods(['toFieldDefinition']) - ->setConstructorArgs([$this->getConverterRegistryMock(), $this->getMaskGeneratorMock()]) - ->getMock(); - - // Dedicatedly tested test - $mapper->expects($this->atLeastOnce()) - ->method('toFieldDefinition') - ->with( - $this->isInstanceOf( - StorageFieldDefinition::class - ) - )->will( - $this->returnCallback( - static function () { - return new FieldDefinition(); - } - ) - ); - - return $mapper; - } - - /** - * Returns a converter registry mock. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry - */ - protected function getConverterRegistryMock() - { - return $this->createMock(ConverterRegistry::class); - } - - /** - * Returns fixture for {@link testExtractTypesFromRowsSingle()}. - * - * @return array - */ - protected function getLoadTypeFixture() - { - return require __DIR__ . '/_fixtures/map_load_type.php'; - } - - /** - * Returns fixture for {@link testExtractGroupsFromRows()}. - * - * @return array - */ - protected function getLoadGroupFixture() - { - return require __DIR__ . '/_fixtures/map_load_group.php'; - } - - protected function getMaskGeneratorMock() - { - return $this->createMock(MaskGenerator::class); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Update/Handler/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Update/Handler/DoctrineDatabaseTest.php deleted file mode 100644 index 608ad8a80a..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Update/Handler/DoctrineDatabaseTest.php +++ /dev/null @@ -1,151 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type\Update\Handler; - -use eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase; -use eZ\Publish\SPI\Persistence\Content\Type; -use PHPUnit\Framework\TestCase; - -/** - * Test case for Content Type Handler. - */ -class DoctrineDatabaseTest extends TestCase -{ - /** - * Gateway mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway - */ - protected $gatewayMock; - - /** - * Content Updater mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater - */ - protected $contentUpdaterMock; - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase::updateContentObjects - */ - public function testUpdateContentObjects() - { - $handler = $this->getUpdateHandler(); - - $updaterMock = $this->getContentUpdaterMock(); - - $updaterMock->expects($this->never()) - ->method('determineActions'); - - $updaterMock->expects($this->never()) - ->method('applyUpdates'); - - $types = $this->getTypeFixtures(); - - $handler->updateContentObjects($types['from'], $types['to']); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase::deleteOldType - */ - public function testDeleteOldType() - { - $handler = $this->getUpdateHandler(); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('delete') - ->with( - $this->equalTo(23), - $this->equalTo(0) - ); - - $types = $this->getTypeFixtures(); - - $handler->deleteOldType($types['from'], $types['to']); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase::publishNewType - */ - public function testPublishNewType() - { - $handler = $this->getUpdateHandler(); - - $gatewayMock = $this->getGatewayMock(); - $updaterMock = $this->getContentUpdaterMock(); - - $gatewayMock->expects($this->once()) - ->method('publishTypeAndFields') - ->with($this->equalTo(23), $this->equalTo(1), $this->equalTo(0)); - - $types = $this->getTypeFixtures(); - - $handler->publishNewType($types['to'], 0); - } - - /** - * Returns an array with 'from' and 'to' types. - * - * @return \eZ\Publish\SPI\Persistence\Content\Type[] - */ - protected function getTypeFixtures() - { - $types = []; - - $types['from'] = new Type(); - $types['from']->id = 23; - $types['from']->status = Type::STATUS_DEFINED; - - $types['to'] = new Type(); - $types['to']->id = 23; - $types['to']->status = Type::STATUS_DRAFT; - - return $types; - } - - /** - * Returns the Update Handler to test. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase - */ - protected function getUpdateHandler() - { - return new DoctrineDatabase($this->getGatewayMock()); - } - - /** - * Returns a gateway mock. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway - */ - protected function getGatewayMock() - { - if (!isset($this->gatewayMock)) { - $this->gatewayMock = $this->getMockForAbstractClass(Gateway::class); - } - - return $this->gatewayMock; - } - - /** - * Returns a Content Updater mock. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater - */ - protected function getContentUpdaterMock() - { - if (!isset($this->contentUpdaterMock)) { - $this->contentUpdaterMock = $this->createMock(ContentUpdater::class); - } - - return $this->contentUpdaterMock; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/_fixtures/map_load_group.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/_fixtures/map_load_group.php deleted file mode 100644 index 33b97e06bd..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/_fixtures/map_load_group.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php - -return array( - array( - 'created' => 1032009743, - 'creator_id' => 14, - 'id' => 3, - 'modified' => 1033922120, - 'modifier_id' => 14, - 'name' => 'Media', - ), -); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index 0c9569921c..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,534 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlAlias\Gateway; - -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase as LanguageGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler as LanguageHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Mapper as LanguageMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; - -/** - * Test case for eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase. - * - * @group urlalias-gateway - */ -class DoctrineDatabaseTest extends TestCase -{ - /** - * Database gateway to test. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway - */ - protected $gateway; - - /** - * Test for the loadUrlAliasData() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::loadUrlAliasData - */ - public function testLoadUrlaliasDataNonExistent() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_simple.php'); - $gateway = $this->getGateway(); - - $rows = $gateway->loadUrlAliasData([md5('tri')]); - - self::assertEmpty($rows); - } - - /** - * Test for the loadUrlAliasData() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::loadUrlAliasData - */ - public function testLoadUrlaliasData() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_simple.php'); - $gateway = $this->getGateway(); - - $row = $gateway->loadUrlAliasData([md5('jedan'), md5('dva')]); - - self::assertEquals( - [ - 'ezurlalias_ml0_id' => '2', - 'ezurlalias_ml0_link' => '2', - 'ezurlalias_ml0_is_alias' => '0', - 'ezurlalias_ml0_alias_redirects' => '1', - 'ezurlalias_ml0_is_original' => '1', - 'ezurlalias_ml0_action' => 'eznode:314', - 'ezurlalias_ml0_action_type' => 'eznode', - 'ezurlalias_ml0_lang_mask' => '2', - 'ezurlalias_ml0_text' => 'jedan', - 'ezurlalias_ml0_parent' => '0', - 'ezurlalias_ml0_text_md5' => '6896260129051a949051c3847c34466f', - 'id' => '3', - 'link' => '3', - 'is_alias' => '0', - 'alias_redirects' => '1', - 'is_original' => '1', - 'action' => 'eznode:315', - 'action_type' => 'eznode', - 'lang_mask' => '3', - 'text' => 'dva', - 'parent' => '2', - 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ], - $row - ); - } - - /** - * Test for the loadUrlAliasData() method. - * - * Test with fixture containing language mask with multiple languages. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::loadUrlAliasData - */ - public function testLoadUrlaliasDataMultipleLanguages() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_multilang.php'); - $gateway = $this->getGateway(); - - $row = $gateway->loadUrlAliasData([md5('jedan'), md5('dva')]); - - self::assertEquals( - [ - 'ezurlalias_ml0_id' => '2', - 'ezurlalias_ml0_link' => '2', - 'ezurlalias_ml0_is_alias' => '0', - 'ezurlalias_ml0_alias_redirects' => '1', - 'ezurlalias_ml0_is_original' => '1', - 'ezurlalias_ml0_action' => 'eznode:314', - 'ezurlalias_ml0_action_type' => 'eznode', - 'ezurlalias_ml0_lang_mask' => '3', - 'ezurlalias_ml0_text' => 'jedan', - 'ezurlalias_ml0_parent' => '0', - 'ezurlalias_ml0_text_md5' => '6896260129051a949051c3847c34466f', - 'id' => '3', - 'link' => '3', - 'is_alias' => '0', - 'alias_redirects' => '1', - 'is_original' => '1', - 'action' => 'eznode:315', - 'action_type' => 'eznode', - 'lang_mask' => '6', - 'text' => 'dva', - 'parent' => '2', - 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ], - $row - ); - } - - /** - * @return array - */ - public function providerForTestLoadPathData() - { - return [ - [ - 2, - [ - [ - ['parent' => '0', 'lang_mask' => '3', 'text' => 'jedan'], - ], - ], - ], - [ - 3, - [ - [ - ['parent' => '0', 'lang_mask' => '3', 'text' => 'jedan'], - ], - [ - ['parent' => '2', 'lang_mask' => '5', 'text' => 'two'], - ['parent' => '2', 'lang_mask' => '3', 'text' => 'dva'], - ], - ], - ], - [ - 4, - [ - [ - ['parent' => '0', 'lang_mask' => '3', 'text' => 'jedan'], - ], - [ - ['parent' => '2', 'lang_mask' => '5', 'text' => 'two'], - ['parent' => '2', 'lang_mask' => '3', 'text' => 'dva'], - ], - [ - ['parent' => '3', 'lang_mask' => '9', 'text' => 'drei'], - ['parent' => '3', 'lang_mask' => '5', 'text' => 'three'], - ['parent' => '3', 'lang_mask' => '3', 'text' => 'tri'], - ], - ], - ], - ]; - } - - /** - * Test for the loadPathData() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::loadPathData - * @dataProvider providerForTestLoadPathData - */ - public function testLoadPathData($id, $pathData) - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_fallback.php'); - $gateway = $this->getGateway(); - - $loadedPathData = $gateway->loadPathData($id); - - self::assertEquals( - $pathData, - $loadedPathData - ); - } - - /** - * @return array - */ - public function providerForTestLoadPathDataMultipleLanguages() - { - return [ - [ - 2, - [ - [ - ['parent' => '0', 'lang_mask' => '3', 'text' => 'jedan'], - ], - ], - ], - [ - 3, - [ - [ - ['parent' => '0', 'lang_mask' => '3', 'text' => 'jedan'], - ], - [ - ['parent' => '2', 'lang_mask' => '6', 'text' => 'dva'], - ], - ], - ], - [ - 4, - [ - [ - ['parent' => '0', 'lang_mask' => '3', 'text' => 'jedan'], - ], - [ - ['parent' => '2', 'lang_mask' => '6', 'text' => 'dva'], - ], - [ - ['parent' => '3', 'lang_mask' => '4', 'text' => 'three'], - ['parent' => '3', 'lang_mask' => '2', 'text' => 'tri'], - ], - ], - ], - ]; - } - - /** - * Test for the loadPathData() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::loadPathData - * @dataProvider providerForTestLoadPathDataMultipleLanguages - */ - public function testLoadPathDataMultipleLanguages($id, $pathData) - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_multilang.php'); - $gateway = $this->getGateway(); - - $loadedPathData = $gateway->loadPathData($id); - - self::assertEquals( - $pathData, - $loadedPathData - ); - } - - /** - * @return array - */ - public function providerForTestCleanupAfterPublishHistorize() - { - return [ - [ - 'action' => 'eznode:314', - 'languageId' => 2, - 'parentId' => 0, - 'textMD5' => '6896260129051a949051c3847c34466f', - ], - [ - 'action' => 'eznode:315', - 'languageId' => 2, - 'parentId' => 0, - 'textMD5' => 'c67ed9a09ab136fae610b6a087d82e21', - ], - ]; - } - - /** - * Data provider for testArchiveUrlAliasesForDeletedTranslations. - * - * @see testArchiveUrlAliasesForDeletedTranslations - * - * @return array - */ - public function providerForTestArchiveUrlAliasesForDeletedTranslations() - { - return [ - [314, [2]], - [315, [4]], - [316, [4]], - [317, [2, 8]], - [318, [2, 8]], - ]; - } - - /** - * Test for the cleanupAfterPublish() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::cleanupAfterPublish - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::historize - * @dataProvider providerForTestCleanupAfterPublishHistorize - */ - public function testCleanupAfterPublishHistorize($action, $languageId, $parentId, $textMD5) - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_downgrade.php'); - $gateway = $this->getGateway(); - - $loadedRow = $gateway->loadRow($parentId, $textMD5); - - $gateway->cleanupAfterPublish($action, $languageId, 42, $parentId, 'jabberwocky'); - - $reloadedRow = $gateway->loadRow($parentId, $textMD5); - $loadedRow['is_original'] = '0'; - $loadedRow['link'] = 42; - $loadedRow['id'] = 6; - - self::assertEquals($reloadedRow, $loadedRow); - } - - /** - * @return array - */ - public function providerForTestCleanupAfterPublishRemovesLanguage() - { - return [ - [ - 'action' => 'eznode:316', - 'languageId' => 2, - 'parentId' => 0, - 'textMD5' => 'd2cfe69af2d64330670e08efb2c86df7', - ], - [ - 'action' => 'eznode:317', - 'languageId' => 2, - 'parentId' => 0, - 'textMD5' => '538dca05643d220317ad233cd7be7a0a', - ], - ]; - } - - /** - * Test for the cleanupAfterPublish() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::cleanupAfterPublish - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::removeTranslation - * @dataProvider providerForTestCleanupAfterPublishRemovesLanguage - */ - public function testCleanupAfterPublishRemovesLanguage($action, $languageId, $parentId, $textMD5) - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_downgrade.php'); - $gateway = $this->getGateway(); - - $loadedRow = $gateway->loadRow($parentId, $textMD5); - - $gateway->cleanupAfterPublish($action, $languageId, 42, $parentId, 'jabberwocky'); - - $reloadedRow = $gateway->loadRow($parentId, $textMD5); - $loadedRow['lang_mask'] = $loadedRow['lang_mask'] & ~$languageId; - - self::assertEquals($reloadedRow, $loadedRow); - } - - /** - * Test for the reparent() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::reparent - */ - public function testReparent() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_simple.php'); - $gateway = $this->getGateway(); - - $gateway->reparent(2, 42); - - self::assertEquals( - [ - 'action' => 'eznode:315', - 'action_type' => 'eznode', - 'alias_redirects' => '1', - 'id' => '3', - 'is_alias' => '0', - 'is_original' => '1', - 'lang_mask' => '3', - 'link' => '3', - 'parent' => '42', - 'text' => 'dva', - 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ], - $gateway->loadRow(42, 'c67ed9a09ab136fae610b6a087d82e21') - ); - } - - /** - * Test for the remove() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::remove - */ - public function testRemove() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_remove.php'); - $gateway = $this->getGateway(); - - $gateway->remove('eznode:314'); - - self::assertEmpty($gateway->loadRow(0, 'd5189de027922f81005951e6efe0efd5')); - self::assertEmpty($gateway->loadRow(0, 'a59d9f07e3d5fcf77911155650956a73')); - self::assertEmpty($gateway->loadRow(0, '6449cba11bb134a57af94c8cb7f6c99c')); - self::assertNotEmpty($gateway->loadRow(0, '0a06c09b6dd9a4606b4eb6d60ab188f0')); - self::assertNotEmpty($gateway->loadRow(0, '82f2bce3283a0806a398fe78beda17d9')); - self::assertNotEmpty($gateway->loadRow(0, '863d659d9fec68e5ab117b5f585a4ee7')); - } - - /** - * Test for the remove() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::remove - */ - public function testRemoveWithId() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_remove.php'); - $gateway = $this->getGateway(); - - $gateway->remove('eznode:315', 6); - - self::assertEmpty($gateway->loadRow(0, '0a06c09b6dd9a4606b4eb6d60ab188f0')); - self::assertEmpty($gateway->loadRow(0, '82f2bce3283a0806a398fe78beda17d9')); - self::assertNotEmpty($gateway->loadRow(0, '863d659d9fec68e5ab117b5f585a4ee7')); - self::assertNotEmpty($gateway->loadRow(0, 'd5189de027922f81005951e6efe0efd5')); - self::assertNotEmpty($gateway->loadRow(0, 'a59d9f07e3d5fcf77911155650956a73')); - self::assertNotEmpty($gateway->loadRow(0, '6449cba11bb134a57af94c8cb7f6c99c')); - } - - /** - * Test for the removeCustomAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::removeCustomAlias - */ - public function testRemoveCustomAlias() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_remove.php'); - $gateway = $this->getGateway(); - - $result = $gateway->removeCustomAlias(0, '6449cba11bb134a57af94c8cb7f6c99c'); - - self::assertTrue($result); - self::assertNotEmpty($gateway->loadRow(0, 'd5189de027922f81005951e6efe0efd5')); - self::assertNotEmpty($gateway->loadRow(0, 'a59d9f07e3d5fcf77911155650956a73')); - self::assertEmpty($gateway->loadRow(0, '6449cba11bb134a57af94c8cb7f6c99c')); - } - - /** - * Test for the removeByAction() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::removeCustomAlias - */ - public function testRemoveCustomAliasFails() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_remove.php'); - $gateway = $this->getGateway(); - - $result = $gateway->removeCustomAlias(0, 'd5189de027922f81005951e6efe0efd5'); - - self::assertFalse($result); - self::assertNotEmpty($gateway->loadRow(0, 'd5189de027922f81005951e6efe0efd5')); - } - - /** - * Test for the getNextId() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::getNextId - */ - public function testGetNextId() - { - $gateway = $this->getGateway(); - - self::assertEquals(1, $gateway->getNextId()); - self::assertEquals(2, $gateway->getNextId()); - } - - /** - * @dataProvider providerForTestArchiveUrlAliasesForDeletedTranslations - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase::archiveUrlAliasesForDeletedTranslations - * - * @param int $locationId - * @param int[] $removedLanguageIds - */ - public function testArchiveUrlAliasesForDeletedTranslations($locationId, array $removedLanguageIds) - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_multilang.php'); - $gateway = $this->getGateway(); - - foreach ($gateway->loadLocationEntries($locationId) as $row) { - $gateway->archiveUrlAliasesForDeletedTranslations( - $locationId, - (int) $row['parent'], - $removedLanguageIds - ); - } - - // check results - $languageMask = 0; - foreach ($removedLanguageIds as $languageId) { - $languageMask |= $languageId; - } - foreach ($gateway->loadLocationEntries($locationId) as $row) { - self::assertNotEquals(0, (int) $row['lang_mask']); - self::assertNotEquals(1, (int) $row['lang_mask']); - self::assertEquals(0, (int) $row['lang_mask'] & $languageMask); - } - } - - /** - * Return the DoctrineDatabase gateway implementation to test. - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function getGateway(): DoctrineDatabase - { - if (!isset($this->gateway)) { - $languageHandler = new LanguageHandler( - new LanguageGateway($this->getDatabaseConnection()), - new LanguageMapper() - ); - $this->gateway = new DoctrineDatabase( - $this->getDatabaseConnection(), - new LanguageMaskGenerator($languageHandler) - ); - } - - return $this->gateway; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_relink.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_relink.php deleted file mode 100644 index bff942b953..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_relink.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -return array( - 'ezurlalias_ml' => array( - 0 => array( - 'action' => 'eznode:2', - 'action_type' => 'eznode', - 'alias_redirects' => '1', - 'id' => '1', - 'is_alias' => '0', - 'is_original' => '1', - 'lang_mask' => '3', - 'link' => '1', - 'parent' => '0', - 'text' => '', - 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( - 'action' => 'eznode:314', - 'action_type' => 'eznode', - 'alias_redirects' => '1', - 'id' => '2', - 'is_alias' => '0', - 'is_original' => '0', - 'lang_mask' => '2', - 'link' => '2', - 'parent' => '0', - 'text' => 'history', - 'text_md5' => '3cd15f8f2940aff879df34df4e5c2cd1', - ), - 2 => array( - 'action' => 'eznode:315', - 'action_type' => 'eznode', - 'alias_redirects' => '1', - 'id' => '3', - 'is_alias' => '0', - 'is_original' => '0', - 'lang_mask' => '2', - 'link' => '3', - 'parent' => '0', - 'text' => 'reused-history', - 'text_md5' => '51e775a611265b7b0cde62a413c91cdc', - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( - 'id' => '1', - ), - 1 => array( - 'id' => '2', - ), - 2 => array( - 'id' => '3', - ), - 3 => array( - 'id' => '4', - ), - ), -); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_simple.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_simple.php deleted file mode 100644 index d0b151bce6..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_simple.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php - -return array( - 'ezurlalias_ml' => array( - 0 => array( - 'action' => 'eznode:2', - 'action_type' => 'eznode', - 'alias_redirects' => '1', - 'id' => '1', - 'is_alias' => '0', - 'is_original' => '1', - 'lang_mask' => '3', - 'link' => '1', - 'parent' => '0', - 'text' => '', - 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( - 'action' => 'eznode:314', - 'action_type' => 'eznode', - 'alias_redirects' => '1', - 'id' => '2', - 'is_alias' => '0', - 'is_original' => '1', - 'lang_mask' => '2', - 'link' => '2', - 'parent' => '0', - 'text' => 'jedan', - 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( - 'action' => 'eznode:315', - 'action_type' => 'eznode', - 'alias_redirects' => '1', - 'id' => '3', - 'is_alias' => '0', - 'is_original' => '1', - 'lang_mask' => '3', - 'link' => '3', - 'parent' => '2', - 'text' => 'dva', - 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - ), - 'ezcontent_language' => array( - 0 => array( - 'disabled' => 0, - 'id' => 2, - 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( - 'id' => '1', - ), - 1 => array( - 'id' => '2', - ), - 2 => array( - 'id' => '3', - ), - ), -); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/UrlAliasHandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/UrlAliasHandlerTest.php deleted file mode 100644 index 08dfae57be..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/UrlAliasHandlerTest.php +++ /dev/null @@ -1,5603 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlAlias; - -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase as LanguageGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler as LanguageHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Mapper as LanguageMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase as DoctrineDatabaseLocation; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway as UrlAliasGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\Core\Persistence\TransformationProcessor\DefinitionBased; -use eZ\Publish\Core\Persistence\TransformationProcessor\DefinitionBased\Parser; -use eZ\Publish\Core\Persistence\TransformationProcessor\PcreCompiler; -use eZ\Publish\Core\Persistence\Utf8Converter; -use eZ\Publish\SPI\Persistence\Content\UrlAlias; -use eZ\Publish\SPI\Persistence\TransactionHandler; - -/** - * Test case for UrlAliasHandler. - * - * @group urlalias-handler - */ -class UrlAliasHandlerTest extends TestCase -{ - /** - * Test for the lookup() method. - * - * Simple lookup case. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::lookup - * @group location - * @group virtual - * @group resource - * @group case-correction - * @group multiple-languages - */ - public function testLookup() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location.php'); - - $urlAlias = $handler->lookup('jedan'); - self::assertInstanceOf(UrlAlias::class, $urlAlias); - } - - /** - * Test for the lookup() method. - * - * Trying to lookup non existent URL alias throws NotFoundException. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::lookup - * @group location - * @group virtual - * @group resource - */ - public function testLookupThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $handler = $this->getHandler(); - $handler->lookup('wooden/iron'); - } - - /** - * Test for the lookup() method. - * - * Trying to lookup URL alias with exceeded path segments limit - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::lookup - * @group location - * @group case-correction - */ - public function testLookupThrowsInvalidArgumentException() - { - $this->expectException(InvalidArgumentException::class); - - $handler = $this->getHandler(); - $handler->lookup(str_repeat('/1', 99)); - } - - public function providerForTestLookupLocationUrlAlias() - { - return [ - [ - 'jedan', - [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - ], - ['cro-HR'], - true, - 314, - '0-6896260129051a949051c3847c34466f', - ], - [ - 'jedan/dva', - [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ], - ], - ['cro-HR'], - false, - 315, - '2-c67ed9a09ab136fae610b6a087d82e21', - ], - [ - 'jedan/two', - [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ], - ], - ['eng-GB'], - false, - 315, - '2-b8a9f715dbb64fd5c56e7783c6820a61', - ], - [ - 'jedan/dva/tri', - [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - 'ger-DE' => 'drei', - ], - ], - ], - ['cro-HR'], - false, - 316, - '3-d2cfe69af2d64330670e08efb2c86df7', - ], - [ - 'jedan/two/three', - [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - 'ger-DE' => 'drei', - ], - ], - ], - ['eng-GB'], - false, - 316, - '3-35d6d33467aae9a2e3dccb4b6b027878', - ], - [ - 'jedan/dva/three', - [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - 'ger-DE' => 'drei', - ], - ], - ], - ['eng-GB'], - false, - 316, - '3-35d6d33467aae9a2e3dccb4b6b027878', - ], - [ - 'jedan/two/tri', - [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - 'ger-DE' => 'drei', - ], - ], - ], - ['cro-HR'], - false, - 316, - '3-d2cfe69af2d64330670e08efb2c86df7', - ], - [ - 'jedan/dva/drei', - [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - 'ger-DE' => 'drei', - ], - ], - ], - ['ger-DE'], - false, - 316, - '3-1d8d2fd0a99802b89eb356a86e029d25', - ], - [ - 'jedan/two/drei', - [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - 'ger-DE' => 'drei', - ], - ], - ], - ['ger-DE'], - false, - 316, - '3-1d8d2fd0a99802b89eb356a86e029d25', - ], - ]; - } - - /** - * Test for the lookup() method. - * - * Testing that UrlAlias is found and has expected state. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::lookup - * @dataProvider providerForTestLookupLocationUrlAlias - * @depends testLookup - * @group location - */ - public function testLookupLocationUrlAlias( - $url, - array $pathData, - array $languageCodes, - $alwaysAvailable, - $locationId, - $id - ) { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location.php'); - - $urlAlias = $handler->lookup($url); - - self::assertInstanceOf(UrlAlias::class, $urlAlias); - self::assertEquals( - new UrlAlias( - [ - 'id' => $id, - 'type' => UrlAlias::LOCATION, - 'destination' => $locationId, - 'languageCodes' => $languageCodes, - 'pathData' => $pathData, - 'alwaysAvailable' => $alwaysAvailable, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Testing that looking up case incorrect URL results in redirection to case correct path. - * - * Note that case corrected path is not always equal to case corrected case incorrect path, eg. "JEDAN/TWO/THREE" - * will not always redirect to "jedan/two/three". - * In some cases, depending on list of prioritized languages and if Content available in the different language - * higher in the list of prioritized languages, path showing to that Content will be used. - * Example: "JEDAN/TWO/DREI" with "eng-GB" and "ger-DE" as prioritized languages will produce redirection - * to the "jedan/two/three", as "eng-GB" is the most prioritized language and Content that URL alias is pointing - * to is available in it. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::lookup - * @dataProvider providerForTestLookupLocationUrlAlias - * @depends testLookup - * @group case-correction - * @group location - * - * @todo refactor, only forward pertinent - */ - public function testLookupLocationCaseCorrection( - $url, - array $pathData, - array $languageCodes, - $alwaysAvailable, - $locationId, - $id - ) { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location.php'); - - $urlAlias = $handler->lookup(strtoupper($url)); - - self::assertInstanceOf(UrlAlias::class, $urlAlias); - self::assertEquals( - new UrlAlias( - [ - 'id' => $id, - 'type' => UrlAlias::LOCATION, - 'destination' => $locationId, - 'languageCodes' => $languageCodes, - 'pathData' => $pathData, - 'alwaysAvailable' => $alwaysAvailable, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - public function providerForTestLookupLocationMultipleLanguages() - { - return [ - [ - 'jedan/dva', - [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'dva', - ], - ], - ], - ['cro-HR', 'eng-GB'], - false, - 315, - '2-c67ed9a09ab136fae610b6a087d82e21', - ], - [ - 'jedan/dva/tri', - [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - ], - ], - ], - ['cro-HR'], - false, - 316, - '3-d2cfe69af2d64330670e08efb2c86df7', - ], - [ - 'jedan/dva/three', - [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - ], - ], - ], - ['eng-GB'], - false, - 316, - '3-35d6d33467aae9a2e3dccb4b6b027878', - ], - ]; - } - - /** - * Test for the lookup() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::lookup - * @dataProvider providerForTestLookupLocationMultipleLanguages - * @depends testLookup - * @group multiple-languages - * @group location - */ - public function testLookupLocationMultipleLanguages( - $url, - array $pathData, - array $languageCodes, - $alwaysAvailable, - $locationId, - $id - ) { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_multilang.php'); - - $urlAlias = $handler->lookup($url); - - self::assertInstanceOf(UrlAlias::class, $urlAlias); - self::assertEquals( - new UrlAlias( - [ - 'id' => $id, - 'type' => UrlAlias::LOCATION, - 'destination' => $locationId, - 'languageCodes' => $languageCodes, - 'pathData' => $pathData, - 'alwaysAvailable' => $alwaysAvailable, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the lookup() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::lookup - * @depends testLookup - * @group history - * @group location - */ - public function testLookupLocationHistoryUrlAlias() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location.php'); - - $urlAlias = $handler->lookup('jedan/dva/tri-history'); - - self::assertEquals( - $this->getHistoryAlias(), - $urlAlias - ); - } - - public function providerForTestLookupCustomLocationUrlAlias() - { - return [ - [ - 'autogenerated-hello/everybody', - [ - [ - 'always-available' => true, - 'translations' => [ - 'eng-GB' => 'autogenerated-hello', - ], - ], - [ - 'always-available' => true, - 'translations' => [ - 'eng-GB' => 'everybody', - ], - ], - ], - ['eng-GB'], - false, - true, - 315, - '2-88150d7d17390010ba6222de68bfafb5', - ], - [ - 'hello', - [ - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'hello', - ], - ], - ], - ['eng-GB'], - true, - false, - 314, - '0-5d41402abc4b2a76b9719d911017c592', - ], - [ - 'hello/and/goodbye', - [ - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'hello', - ], - ], - [ - 'always-available' => true, - 'translations' => [ - 'always-available' => 'and', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'goodbye', - ], - ], - ], - ['eng-GB'], - true, - false, - 316, - '8-69faab6268350295550de7d587bc323d', - ], - [ - 'hello/everyone', - [ - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'hello', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'everyone', - ], - ], - ], - ['eng-GB'], - true, - false, - 315, - '6-ed881bac6397ede33c0a285c9f50bb83', - ], - [ - 'well/ha-ha-ha', - [ - [ - 'always-available' => true, - 'translations' => [ - 'always-available' => 'well', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'ha-ha-ha', - ], - ], - ], - ['eng-GB'], - false, - false, - 317, - '10-17a197f4bbe127c368b889a67effd1b3', - ], - ]; - } - - /** - * Test for the lookup() method. - * - * Testing that UrlAlias is found and has expected state. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::lookup - * @dataProvider providerForTestLookupCustomLocationUrlAlias - * @depends testLookup - * @group location - * @group custom - */ - public function testLookupCustomLocationUrlAlias( - $url, - array $pathData, - array $languageCodes, - $forward, - $alwaysAvailable, - $destination, - $id - ) { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_custom.php'); - - $urlAlias = $handler->lookup($url); - self::assertInstanceOf(UrlAlias::class, $urlAlias); - self::assertEquals( - new UrlAlias( - [ - 'id' => $id, - 'type' => UrlAlias::LOCATION, - 'destination' => $destination, - 'languageCodes' => $languageCodes, - 'pathData' => $pathData, - 'alwaysAvailable' => $alwaysAvailable, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => $forward, - ] - ), - $urlAlias - ); - } - - /** - * Test for the lookup() method. - * - * Testing that UrlAlias is found and has expected state. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::lookup - * @dataProvider providerForTestLookupCustomLocationUrlAlias - * @depends testLookup - * @group location - * @group custom - */ - public function testLookupCustomLocationUrlAliasCaseCorrection( - $url, - array $pathData, - array $languageCodes, - $forward, - $alwaysAvailable, - $destination, - $id - ) { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_custom.php'); - - $urlAlias = $handler->lookup(strtoupper($url)); - - self::assertInstanceOf(UrlAlias::class, $urlAlias); - self::assertEquals( - new UrlAlias( - [ - 'id' => $id, - 'type' => UrlAlias::LOCATION, - 'destination' => $destination, - 'languageCodes' => $languageCodes, - 'pathData' => $pathData, - 'alwaysAvailable' => $alwaysAvailable, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => $forward, - ] - ), - $urlAlias - ); - } - - public function providerForTestLookupVirtualUrlAlias() - { - return [ - [ - 'hello/and', - '6-be5d5d37542d75f93a87094459f76678', - ], - [ - 'HELLO/AND', - '6-be5d5d37542d75f93a87094459f76678', - ], - ]; - } - - /** - * Test for the lookup() method. - * - * Testing that NOP action redirects to site root. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::lookup - * @dataProvider providerForTestLookupVirtualUrlAlias - * @depends testLookup - * @group virtual - */ - public function testLookupVirtualUrlAlias($url, $id) - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_custom.php'); - - $urlAlias = $handler->lookup($url); - - $this->assertVirtualUrlAliasValid($urlAlias, $id); - } - - public function providerForTestLookupResourceUrlAlias() - { - return [ - [ - 'is-alive', - [ - [ - 'always-available' => true, - 'translations' => [ - 'eng-GB' => 'is-alive', - ], - ], - ], - ['eng-GB'], - true, - true, - 'ezinfo/isalive', - '0-d003895fa282a14c8ec3eddf23ca4ca2', - ], - [ - 'is-alive/then/search', - [ - [ - 'always-available' => true, - 'translations' => [ - 'eng-GB' => 'is-alive', - ], - ], - [ - 'always-available' => true, - 'translations' => [ - 'always-available' => 'then', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'search', - ], - ], - ], - ['cro-HR'], - false, - false, - 'content/search', - '3-06a943c59f33a34bb5924aaf72cd2995', - ], - ]; - } - - /** - * Test for the lookup() method. - * - * Testing that UrlAlias is found and has expected state. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::lookup - * @dataProvider providerForTestLookupResourceUrlAlias - * @depends testLookup - * @group resource - */ - public function testLookupResourceUrlAlias( - $url, - $pathData, - array $languageCodes, - $forward, - $alwaysAvailable, - $destination, - $id - ) { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); - - $urlAlias = $handler->lookup($url); - - self::assertInstanceOf(UrlAlias::class, $urlAlias); - self::assertEquals( - new UrlAlias( - [ - 'id' => $id, - 'type' => UrlAlias::RESOURCE, - 'destination' => $destination, - 'languageCodes' => $languageCodes, - 'pathData' => $pathData, - 'alwaysAvailable' => $alwaysAvailable, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => $forward, - ] - ), - $urlAlias - ); - } - - /** - * Test for the lookup() method. - * - * Testing that UrlAlias is found and has expected state. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::lookup - * @dataProvider providerForTestLookupResourceUrlAlias - * @depends testLookup - * @group resource - */ - public function testLookupResourceUrlAliasCaseInsensitive( - $url, - $pathData, - array $languageCodes, - $forward, - $alwaysAvailable, - $destination, - $id - ) { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); - - $urlAlias = $handler->lookup(strtoupper($url)); - - self::assertInstanceOf(UrlAlias::class, $urlAlias); - self::assertEquals( - new UrlAlias( - [ - 'id' => $id, - 'type' => UrlAlias::RESOURCE, - 'destination' => $destination, - 'languageCodes' => $languageCodes, - 'pathData' => $pathData, - 'alwaysAvailable' => $alwaysAvailable, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => $forward, - ] - ), - $urlAlias - ); - } - - /** - * Test for the lookup() method with uppercase utf8 characters. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::lookup - * @depends testLookup - */ - public function testLookupUppercaseIri() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_iri.php'); - - $urlAlias = $handler->lookup('ŒÄ'); - self::assertInstanceOf(UrlAlias::class, $urlAlias); - } - - protected function assertVirtualUrlAliasValid(UrlAlias $urlAlias, $id) - { - self::assertInstanceOf(UrlAlias::class, $urlAlias); - self::assertEquals($id, $urlAlias->id); - self::assertEquals(UrlAlias::VIRTUAL, $urlAlias->type); - /*self::assertEquals( - new UrlAlias( - array( - "id" => $id, - "type" => UrlAlias::VIRTUAL, - "destination" => null, - "languageCodes" => array(), - "pathData" => null, - "alwaysAvailable" => true, - "isHistory" => true, - "isCustom" => false, - "forward" => false - ) - ), - $urlAlias - );*/ - } - - /** - * Test for the listURLAliasesForLocation() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::listURLAliasesForLocation - */ - public function testListURLAliasesForLocation() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location.php'); - - $urlAliases = $handler->listURLAliasesForLocation(315); - - self::assertEquals( - [ - new UrlAlias( - [ - 'id' => '2-b8a9f715dbb64fd5c56e7783c6820a61', - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => true, - 'translations' => ['cro-HR' => 'jedan'], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - new UrlAlias( - [ - 'id' => '2-c67ed9a09ab136fae610b6a087d82e21', - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => ['cro-HR'], - 'pathData' => [ - [ - 'always-available' => true, - 'translations' => ['cro-HR' => 'jedan'], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - ], - $urlAliases - ); - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @depends testLookupLocationUrlAlias - * @group publish - */ - public function testPublishUrlAliasForLocation() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $handler->publishUrlAliasForLocation(314, 2, 'simple', 'eng-GB', true); - $publishedUrlAlias = $handler->lookup('simple'); - - self::assertEquals(4, $this->countRows()); - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('simple'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => true, - 'translations' => [ - 'eng-GB' => 'simple', - 'cro-HR' => 'path314', - ], - ], - ], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $publishedUrlAlias - ); - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @depends testPublishUrlAliasForLocation - * @group publish - */ - public function testPublishUrlAliasForLocationRepublish() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $handler->publishUrlAliasForLocation(314, 2, 'simple', 'eng-GB', true); - $publishedUrlAlias = $handler->lookup('simple'); - $handler->publishUrlAliasForLocation(314, 2, 'simple', 'eng-GB', true); - $republishedUrlAlias = $handler->lookup('simple'); - - self::assertEquals(4, $this->countRows()); - self::assertEquals( - $publishedUrlAlias, - $republishedUrlAlias - ); - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @depends testPublishUrlAliasForLocation - * @group publish - */ - public function testPublishUrlAliasCreatesUniqueAlias() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $handler->publishUrlAliasForLocation(314, 2, 'simple', 'eng-GB', true); - $handler->publishUrlAliasForLocation(315, 2, 'simple', 'eng-GB', true); - self::assertEquals(5, $this->countRows()); - - $urlAlias = $handler->lookup('simple2'); - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('simple2'), - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => true, - 'translations' => [ - 'eng-GB' => 'simple2', - ], - ], - ], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * @return array - */ - public function providerForTestPublishUrlAliasForLocationComplex() - { - return $this->providerForTestLookupLocationUrlAlias(); - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @dataProvider providerForTestPublishUrlAliasForLocationComplex - * @depends testPublishUrlAliasForLocation - * @group publish - */ - public function testPublishUrlAliasForLocationComplex( - $url, - $pathData, - array $languageCodes, - $alwaysAvailable, - $locationId, - $id - ) { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR', true); - $handler->publishUrlAliasForLocation(315, 314, 'dva', 'cro-HR', false); - $handler->publishUrlAliasForLocation(315, 314, 'two', 'eng-GB', false); - $handler->publishUrlAliasForLocation(316, 315, 'tri', 'cro-HR', false); - $handler->publishUrlAliasForLocation(316, 315, 'three', 'eng-GB', false); - $handler->publishUrlAliasForLocation(316, 315, 'drei', 'ger-DE', false); - - $urlAlias = $handler->lookup($url); - - self::assertInstanceOf(UrlAlias::class, $urlAlias); - self::assertEquals( - new UrlAlias( - [ - 'id' => $id, - 'type' => UrlAlias::LOCATION, - 'destination' => $locationId, - 'languageCodes' => $languageCodes, - 'pathData' => $pathData, - 'alwaysAvailable' => $alwaysAvailable, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @depends testPublishUrlAliasForLocation - * @group publish - */ - public function testPublishUrlAliasForLocationSameAliasForMultipleLanguages() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR', false); - $urlAlias1 = $handler->lookup('jedan'); - $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'eng-GB', false); - $urlAlias2 = $handler->lookup('jedan'); - - self::assertEquals(4, $this->countRows()); - - foreach ($urlAlias2 as $propertyName => $propertyValue) { - if ($propertyName === 'languageCodes') { - self::assertEquals( - ['cro-HR', 'eng-GB'], - $urlAlias2->languageCodes - ); - } elseif ($propertyName === 'pathData') { - self::assertEquals( - [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - 'eng-GB' => 'jedan', - ], - ], - ], - $urlAlias2->pathData - ); - } else { - self::assertEquals( - $urlAlias1->$propertyName, - $urlAlias2->$propertyName - ); - } - } - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @depends testPublishUrlAliasForLocation - * @group publish - */ - public function testPublishUrlAliasForLocationDowngradesOldEntryToHistory() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR', false); - $handler->publishUrlAliasForLocation(314, 2, 'dva', 'cro-HR', true); - - self::assertEquals(5, $this->countRows()); - - $newUrlAlias = $handler->lookup('dva'); - - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-c67ed9a09ab136fae610b6a087d82e21', - 'type' => 0, - 'destination' => 314, - 'languageCodes' => ['cro-HR'], - 'pathData' => [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - ], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $newUrlAlias - ); - - $historyUrlAlias = $handler->lookup('jedan'); - - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-6896260129051a949051c3847c34466f', - 'type' => 0, - 'destination' => 314, - 'languageCodes' => ['cro-HR'], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $historyUrlAlias - ); - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @depends testPublishUrlAliasForLocation - * @depends testPublishUrlAliasForLocationSameAliasForMultipleLanguages - * @group publish - * @group downgrade - */ - public function testPublishUrlAliasForLocationDowngradesOldEntryRemovesLanguage() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR'); - $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'eng-GB'); - $handler->publishUrlAliasForLocation(314, 2, 'dva', 'eng-GB'); - - self::assertEquals(5, $this->countRows()); - - $urlAlias = $handler->lookup('dva'); - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-c67ed9a09ab136fae610b6a087d82e21', - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - 'eng-GB' => 'dva', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $downgradedUrlAlias = $handler->lookup('jedan'); - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-6896260129051a949051c3847c34466f', - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => ['cro-HR'], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - 'eng-GB' => 'dva', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $downgradedUrlAlias - ); - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @depends testPublishUrlAliasForLocation - * @depends testPublishUrlAliasForLocationDowngradesOldEntryToHistory - * @group publish - */ - public function testPublishUrlAliasForLocationReusesHistory() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR'); - $urlAlias = $handler->lookup('jedan'); - $handler->publishUrlAliasForLocation(314, 2, 'dva', 'cro-HR'); - $countBeforeReusing = $this->countRows(); - $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR'); - $urlAliasReusesHistory = $handler->lookup('jedan'); - - self::assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - self::assertEquals( - $urlAlias, - $urlAliasReusesHistory - ); - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @depends testPublishUrlAliasForLocation - * @depends testPublishUrlAliasForLocationDowngradesOldEntryToHistory - * @group publish - */ - public function testPublishUrlAliasForLocationReusesHistoryOfDifferentLanguage() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR'); - $handler->publishUrlAliasForLocation(314, 2, 'one-history', 'eng-GB'); - $handler->publishUrlAliasForLocation(314, 2, 'one-new', 'eng-GB'); - $countBeforeReusing = $this->countRows(); - $handler->publishUrlAliasForLocation(314, 2, 'one-history', 'cro-HR'); - $urlAliasReusesHistory = $handler->lookup('one-history'); - - self::assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('one-history'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => ['cro-HR'], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'one-history', - 'eng-GB' => 'one-new', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAliasReusesHistory - ); - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @depends testPublishUrlAliasForLocation - * @group publish - */ - public function testPublishUrlAliasForLocationReusesCustomAlias() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); - - $countBeforeReusing = $this->countRows(); - $handler->publishUrlAliasForLocation(314, 2, 'custom-hello', 'eng-GB', false); - $urlAlias = $handler->lookup('custom-hello'); - - self::assertEquals( - $countBeforeReusing, - $this->countRows() - ); - self::assertFalse($urlAlias->isCustom); - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @depends testPublishUrlAliasForLocation - */ - public function testPublishUrlAliasForLocationReusingNopElement() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); - - $countBeforeReusing = $this->countRows(); - $virtualUrlAlias = $handler->lookup('nop-element/search'); - $handler->publishUrlAliasForLocation(315, 2, 'nop-element', 'eng-GB', false); - $publishedLocationUrlAlias = $handler->lookup('nop-element'); - - self::assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - self::assertInstanceOf(UrlAlias::class, $publishedLocationUrlAlias); - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-de55c2fff721217cc4cb67b58dc35f85', - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'nop-element'], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $publishedLocationUrlAlias - ); - - $virtualUrlAliasReloaded = $handler->lookup('nop-element/search'); - foreach ($virtualUrlAliasReloaded as $propertyName => $propertyValue) { - if ($propertyName === 'pathData') { - self::assertEquals( - [ - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'nop-element'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'search'], - ], - ], - $virtualUrlAliasReloaded->pathData - ); - } else { - self::assertEquals( - $virtualUrlAlias->$propertyName, - $virtualUrlAliasReloaded->$propertyName - ); - } - } - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @depends testPublishUrlAliasForLocation - * @depends testPublishUrlAliasForLocationReusingNopElement - */ - public function testPublishUrlAliasForLocationReusingNopElementChangesCustomPath() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); - - $countBeforeReusing = $this->countRows(); - $virtualUrlAlias = $handler->lookup('nop-element/search'); - $handler->publishUrlAliasForLocation(315, 2, 'nop-element', 'eng-GB', false); - $handler->publishUrlAliasForLocation(315, 2, 'nop-element-renamed', 'eng-GB', false); - $virtualUrlAliasChanged = $handler->lookup('nop-element-renamed/search'); - - self::assertEquals( - $countBeforeReusing + 1, - $this->countRows() - ); - - foreach ($virtualUrlAliasChanged as $propertyName => $propertyValue) { - if ($propertyName === 'pathData') { - self::assertEquals( - [ - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'nop-element-renamed'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'search'], - ], - ], - $virtualUrlAliasChanged->pathData - ); - } else { - self::assertEquals( - $virtualUrlAlias->$propertyName, - $virtualUrlAliasChanged->$propertyName - ); - } - } - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @todo document - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @depends testPublishUrlAliasForLocation - * @depends testPublishUrlAliasForLocationReusingNopElementChangesCustomPath - */ - public function testPublishUrlAliasForLocationReusingNopElementChangesCustomPathAndCreatesHistory() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); - - $handler->publishUrlAliasForLocation(315, 2, 'nop-element', 'eng-GB', false); - $handler->publishUrlAliasForLocation(315, 2, 'nop-element-renamed', 'eng-GB', false); - - $customUrlAliasChanged = $handler->lookup('nop-element-renamed/search'); - $customUrlAliasHistory = $handler->lookup('nop-element/search'); - - self::assertTrue($customUrlAliasHistory->isHistory); - $customUrlAliasHistory->isHistory = false; - self::assertEquals( - $customUrlAliasChanged, - $customUrlAliasHistory - ); - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - */ - public function testPublishUrlAliasForLocationUpdatesLocationPathIdentificationString() - { - $handler = $this->getHandler(); - $locationGateway = $this->getLocationGateway(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - // Publishes the alias indicating that language is main, triggering updating of path_identification_string - $handler->publishUrlAliasForLocation(316, 315, 'TEST TEST TEST', 'eng-GB', false, true); - - $locationData = $locationGateway->getBasicNodeData(316); - - self::assertEquals('path314/path315/test_test_test', $locationData['path_identification_string']); - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @group cleanup - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - */ - public function testPublishUrlAliasReuseNopCleanupCustomAliasIsDestroyed() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_cleanup_nop.php'); - - $handler->lookup('nop-element/search'); - $handler->publishUrlAliasForLocation(314, 2, 'nop-element', 'cro-HR', false); - - $urlAlias = $handler->lookup('jedan'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('jedan'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => ['cro-HR' => 'jedan'], - ], - ], - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('nop-element'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('nop-element'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'nop-element', - 'eng-GB' => 'dva', - ], - ], - ], - 'languageCodes' => [ - 'cro-HR', - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - try { - $handler->lookup('nop-element/search'); - $this->fail('Custom alias is not destroyed'); - } catch (NotFoundException $e) { - // Custom alias is destroyed by reusing NOP entry with existing autogenerated alias - // on the same level (that means link and ID are updated to the existing alias ID, - // so custom alias children entries are no longer properly linked (parent-link)) - } - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @group cleanup - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - */ - public function testPublishUrlAliasReuseHistoryCleanup() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_cleanup_history.php'); - - $handler->publishUrlAliasForLocation(314, 2, 'tri', 'cro-HR', false); - - $urlAlias = $handler->lookup('jedan'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('jedan'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => ['cro-HR' => 'jedan'], - ], - ], - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('tri'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('tri'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'dva', - ], - ], - ], - 'languageCodes' => [ - 'cro-HR', - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @group cleanup - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - */ - public function testPublishUrlAliasReuseAutogeneratedCleanup() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_cleanup_reusing.php'); - - $handler->publishUrlAliasForLocation(314, 2, 'dva', 'cro-HR', false); - - $urlAlias = $handler->lookup('jedan'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('jedan'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => ['cro-HR' => 'jedan'], - ], - ], - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('dva'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'dva', - ], - ], - ], - 'languageCodes' => [ - 'cro-HR', - 'eng-GB', - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the createCustomUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::createCustomUrlAlias - * @group create - * @group custom - */ - public function testCreateCustomUrlAliasBehaviour() - { - $handlerMock = $this->getPartlyMockedHandler(['createUrlAlias']); - - $handlerMock->expects( - $this->once() - )->method( - 'createUrlAlias' - )->with( - $this->equalTo('eznode:1'), - $this->equalTo('path'), - $this->equalTo(false), - $this->equalTo(null), - $this->equalTo(false) - )->will( - $this->returnValue( - new UrlAlias() - ) - ); - - $this->assertInstanceOf( - UrlAlias::class, - $handlerMock->createCustomUrlAlias(1, 'path') - ); - } - - /** - * Test for the createGlobalUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::createGlobalUrlAlias - * @group create - * @group global - */ - public function testCreateGlobalUrlAliasBehaviour() - { - $handlerMock = $this->getPartlyMockedHandler(['createUrlAlias']); - - $handlerMock->expects( - $this->once() - )->method( - 'createUrlAlias' - )->with( - $this->equalTo('module/module'), - $this->equalTo('path'), - $this->equalTo(false), - $this->equalTo(null), - $this->equalTo(false) - )->will( - $this->returnValue( - new UrlAlias() - ) - ); - - $this->assertInstanceOf( - UrlAlias::class, - $handlerMock->createGlobalUrlAlias('module/module', 'path') - ); - } - - /** - * Test for the createUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::createUrlAlias - * @group create - * @group custom - */ - public function testCreateCustomUrlAlias() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $path = 'custom-location-alias'; - $customUrlAlias = $handler->createCustomUrlAlias( - 314, - $path, - false, - 'cro-HR', - false - ); - - self::assertEquals(4, $this->countRows()); - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5($path), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'custom-location-alias', - ], - ], - ], - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => false, - ] - ), - $customUrlAlias - ); - } - - /** - * Test for the createUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::createUrlAlias - * @group create - * @group custom - */ - public function testCreateCustomUrlAliasWithNonameParts() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $path = 'there-is-a//custom-location-alias//here'; - $customUrlAlias = $handler->createCustomUrlAlias( - 314, - $path, - false, - 'cro-HR', - false - ); - - self::assertEquals(8, $this->countRows()); - - self::assertEquals( - new UrlAlias( - [ - 'id' => '7-' . md5('here'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'pathData' => [ - [ - 'always-available' => true, - 'translations' => [ - 'always-available' => 'there-is-a', - ], - ], - [ - 'always-available' => true, - 'translations' => [ - 'always-available' => 'noname2', - ], - ], - [ - 'always-available' => true, - 'translations' => [ - 'always-available' => 'custom-location-alias', - ], - ], - [ - 'always-available' => true, - 'translations' => [ - 'always-available' => 'noname4', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'here', - ], - ], - ], - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => false, - ] - ), - $customUrlAlias - ); - } - - /** - * Test for the createUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::createUrlAlias - * @group create - * @group custom - * - * @todo pathData - */ - public function testCreatedCustomUrlAliasIsLoadable() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $path = 'custom-location-alias'; - $customUrlAlias = $handler->createCustomUrlAlias( - 314, - $path, - false, - 'cro-HR', - false - ); - $loadedCustomUrlAlias = $handler->lookup($path); - - self::assertEquals(4, $this->countRows()); - - foreach ($loadedCustomUrlAlias as $propertyName => $propertyValue) { - if ($propertyName === 'pathData') { - self::assertEquals( - [ - [ - 'always-available' => false, - 'translations' => ['cro-HR' => $path], - ], - ], - $loadedCustomUrlAlias->$propertyName - ); - } else { - self::assertEquals( - $customUrlAlias->$propertyName, - $loadedCustomUrlAlias->$propertyName - ); - } - } - } - - /** - * Test for the createUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::createUrlAlias - * @group create - * @group custom - */ - public function testCreateCustomUrlAliasWithNopElement(): void - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $path = 'ribar/palunko'; - $customUrlAlias = $handler->createCustomUrlAlias( - 314, - $path, - false, - 'cro-HR', - true - ); - - self::assertEquals(5, $this->countRows()); - self::assertEquals( - new UrlAlias( - [ - 'id' => '4-' . md5('palunko'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'pathData' => [ - [ - 'always-available' => true, - 'translations' => [ - 'always-available' => 'ribar', - ], - ], - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'palunko', - ], - ], - ], - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => false, - ] - ), - $customUrlAlias - ); - - // test that valid NOP element has been created - $url = 'ribar'; - $urlAlias = $handler->lookup($url); - - $this->assertVirtualUrlAliasValid( - $urlAlias, - '0-' . md5($url) - ); - } - - /** - * Test for the createUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::createUrlAlias - * @group create - * @group custom - */ - public function testCreateCustomUrlAliasReusesHistory() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); - - $countBeforeReusing = $this->countRows(); - $handler->createCustomUrlAlias( - 314, - 'history-hello', - true, - 'eng-GB', - true - ); - - self::assertEquals( - $countBeforeReusing, - $this->countRows() - ); - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-da94285592c46d4396d3ca6904a4aa8f', - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => true, - 'translations' => ['eng-GB' => 'history-hello'], - ], - ], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => true, - ] - ), - $handler->lookup('history-hello') - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::createUrlAlias - * @group create - * @group custom - */ - public function testCreateCustomUrlAliasAddLanguage(): void - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - // create a new custom entry since the existing one is a system URL - $handler->createCustomUrlAlias(314, 'custom-path314', false, 'cro-HR', true); - - $countBeforeReusing = $this->countRows(); - $handler->createCustomUrlAlias( - 314, - 'custom-path314', - false, - 'eng-GB', - true - ); - - self::assertEquals( - $countBeforeReusing, - $this->countRows() - ); - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-e8797691eeba6b598a353e5c5af99438', - 'type' => UrlAlias::LOCATION, - 'destination' => '314', - 'languageCodes' => ['cro-HR', 'eng-GB'], - 'pathData' => [ - [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'custom-path314', - 'eng-GB' => 'custom-path314', - ], - ], - ], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => false, - ] - ), - $handler->lookup('custom-path314') - ); - } - - /** - * Test for the createUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::createUrlAlias - * @group create - * @group custom - */ - public function testCreateCustomUrlAliasReusesHistoryOfDifferentLanguage() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); - - $countBeforeReusing = $this->countRows(); - $handler->createCustomUrlAlias( - 314, - 'history-hello', - true, - 'cro-HR', - true - ); - - self::assertEquals( - $countBeforeReusing, - $this->countRows() - ); - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-da94285592c46d4396d3ca6904a4aa8f', - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => ['cro-HR'], - 'pathData' => [ - [ - 'always-available' => true, - 'translations' => ['cro-HR' => 'history-hello'], - ], - ], - 'alwaysAvailable' => true, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => true, - ] - ), - $handler->lookup('history-hello') - ); - } - - /** - * Test for the createUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::createUrlAlias - * @group create - * @group custom - */ - public function testCreateCustomUrlAliasReusesNopElement() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); - - $countBeforeReusing = $this->countRows(); - $handler->createCustomUrlAlias( - 314, - 'nop-element', - true, - 'cro-HR', - true - ); - - self::assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - // Check that custom alias whose nop element was reused still works as expected - self::assertEquals( - new UrlAlias( - [ - 'id' => '2-06a943c59f33a34bb5924aaf72cd2995', - 'type' => UrlAlias::RESOURCE, - 'destination' => 'content/search', - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => true, - 'translations' => ['cro-HR' => 'nop-element'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'search'], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => false, - ] - ), - $handler->lookup('nop-element/search') - ); - } - - /** - * Test for the createUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::createUrlAlias - * @group create - * @group custom - */ - public function testCreateCustomUrlAliasReusesLocationElement() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); - - $countBeforeReusing = $this->countRows(); - $locationUrlAlias = $handler->lookup('autogenerated-hello'); - $handler->createCustomUrlAlias( - 315, - 'autogenerated-hello/custom-location-alias-for-315', - true, - 'cro-HR', - true - ); - - self::assertEquals( - $countBeforeReusing + 1, - $this->countRows() - ); - - // Check that location alias still works as expected - self::assertEquals( - $locationUrlAlias, - $handler->lookup('autogenerated-hello') - ); - } - - /** - * Test for the listGlobalURLAliases() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::listGlobalURLAliases - * @depends testLookupResourceUrlAlias - */ - public function testListGlobalURLAliases() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); - - $globalAliasList = $handler->listGlobalURLAliases(); - - self::assertEquals( - [ - $handler->lookup('is-alive'), - $handler->lookup('is-alive/then/search'), - $handler->lookup('nop-element/search'), - ], - $globalAliasList - ); - } - - /** - * Test for the listGlobalURLAliases() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::listGlobalURLAliases - * @depends testLookupResourceUrlAlias - */ - public function testListGlobalURLAliasesWithLanguageCode() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); - - $globalAliasList = $handler->listGlobalURLAliases('eng-GB'); - - self::assertEquals( - [ - $handler->lookup('is-alive'), - $handler->lookup('nop-element/search'), - ], - $globalAliasList - ); - } - - /** - * Test for the listGlobalURLAliases() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::listGlobalURLAliases - * @depends testLookupResourceUrlAlias - */ - public function testListGlobalURLAliasesWithOffset() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); - - $globalAliasList = $handler->listGlobalURLAliases(null, 2); - - self::assertEquals( - [ - $handler->lookup('nop-element/search'), - ], - $globalAliasList - ); - } - - /** - * Test for the listGlobalURLAliases() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::listGlobalURLAliases - * @depends testLookupResourceUrlAlias - */ - public function testListGlobalURLAliasesWithOffsetAndLimit() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); - - $globalAliasList = $handler->listGlobalURLAliases(null, 1, 1); - - self::assertEquals( - [ - $handler->lookup('is-alive/then/search'), - ], - $globalAliasList - ); - } - - /** - * Test for the locationDeleted() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationDeleted - */ - public function testLocationDeleted() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_delete.php'); - - $countBeforeDeleting = $this->countRows(); - $countAfterFirstDeleting = $countBeforeDeleting - 5; - - $handler->locationDeleted(5); - - self::assertEquals( - $countAfterFirstDeleting, - $this->countRows() - ); - - self::assertEmpty( - $handler->listURLAliasesForLocation(5) - ); - - $removedAliases = [ - 'moved-original-parent/moved-history', - 'moved-original-parent/sub', - 'moved-original-parent', - 'moved-original-parent-history', - 'custom-below/moved-original-parent-custom', - ]; - foreach ($removedAliases as $path) { - try { - $handler->lookup($path); - $this->fail("Alias '$path' not removed!"); - } catch (NotFoundException $e) { - // Do nothing - } - } - - // Deleting location historically related with some relative alias - $handler->locationDeleted(9); - - self::assertEquals( - $countAfterFirstDeleting - 1, - $this->countRows() - ); - self::assertCount( - 1, - $handler->listURLAliasesForLocation(10) - ); - - $handler->lookup('to-delete-folder/article-to-move-relative-alias'); - } - - /** - * Test for the locationMoved() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationMoved - */ - public function testLocationMovedHistorize() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); - - $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); - $handler->locationMoved(4, 2, 3); - - $urlAlias = $handler->lookup('move-this'); - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('move-this'), - 'type' => UrlAlias::LOCATION, - 'destination' => '4', - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'move-this'], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationMoved() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationMoved - */ - public function testLocationMovedHistory() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); - - $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); - $handler->locationMoved(4, 2, 3); - - $urlAlias = $handler->lookup('move-this-history'); - self::assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('move-this-history'), - 'type' => UrlAlias::LOCATION, - 'destination' => '4', - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'move-this-history'], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationMoved() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationMoved - */ - public function testLocationMovedHistorySubtree() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); - - $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); - $handler->locationMoved(4, 2, 3); - - $urlAlias = $handler->lookup('move-this/sub1/sub2'); - self::assertEquals( - new UrlAlias( - [ - 'id' => '5-' . md5('sub2'), - 'type' => UrlAlias::LOCATION, - 'destination' => '6', - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'move-this'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'sub1'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'sub2'], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationMoved() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationMoved - */ - public function testLocationMovedReparent() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); - - $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); - $handler->locationMoved(4, 2, 3); - - $urlAlias = $handler->lookup('move-here/move-this/sub1'); - self::assertEquals( - new UrlAlias( - [ - 'id' => '9-' . md5('sub1'), - 'type' => UrlAlias::LOCATION, - 'destination' => '5', - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'move-here'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'move-this'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'sub1'], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * @throws \Doctrine\DBAL\DBALException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function testLocationMovedReparentWithCustomAlias(): void - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); - - $handler->createCustomUrlAlias( - 6, // Location's id of 'sub2' alias - 'move-this/sub1/sub2-alias', - false, - 'eng-GB', - false - ); - - $handler->publishUrlAliasForLocation( - 5, // Location's id of 'sub1' alias that we are moving - 3, // Location's id of 'move-here' alias that would be a parent - 'sub1', - 'eng-GB', - false - ); - - $handler->locationMoved( - 5, - 4, - 3 - ); - - $customUrlAlias = $handler->lookup('move-here/sub1/sub2-alias'); - - self::assertEquals( - new UrlAlias( - [ - 'id' => '10-' . md5('sub2-alias'), - 'type' => UrlAlias::LOCATION, - 'destination' => '6', - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'move-here'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'sub1'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'sub2-alias'], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => false, - ] - ), - $customUrlAlias - ); - } - - /** - * Test for the locationMoved() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationMoved - */ - public function testLocationMovedReparentHistory() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); - - $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); - $handler->locationMoved(4, 2, 3); - - $handler->lookup('move-here/move-this-history'); - } - - /** - * Test for the locationMoved() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationMoved - */ - public function testLocationMovedReparentSubtree() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); - - $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); - $handler->locationMoved(4, 2, 3); - - $urlAlias = $handler->lookup('move-here/move-this/sub1/sub2'); - self::assertEquals( - new UrlAlias( - [ - 'id' => '5-' . md5('sub2'), - 'type' => UrlAlias::LOCATION, - 'destination' => '6', - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'move-here'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'move-this'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'sub1'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'sub2'], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationMoved() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationMoved - */ - public function testLocationMovedReparentSubtreeHistory() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); - - $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); - $handler->locationMoved(4, 2, 3); - - $urlAlias = $handler->lookup('move-here/move-this/sub1/sub2-history'); - self::assertEquals( - new UrlAlias( - [ - 'id' => '5-' . md5('sub2-history'), - 'type' => UrlAlias::LOCATION, - 'destination' => '6', - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'move-here'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'move-this'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'sub1'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'sub2-history'], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationCopied() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationCopied - */ - public function testLocationCopiedCopiedLocationAliasIsValid() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_copy.php'); - - $urlAlias = $handler->lookup('move-this'); - - $handler->locationCopied(4, 400, 3); - - self::assertEquals( - $urlAlias, - $handler->lookup('move-this') - ); - } - - /** - * Test for the locationCopied() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationCopied - */ - public function testLocationCopiedCopiedSubtreeIsValid() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_copy.php'); - - $urlAlias = $handler->lookup('move-this/sub1/sub2'); - - $handler->locationCopied(4, 400, 3); - - self::assertEquals( - $urlAlias, - $handler->lookup('move-this/sub1/sub2') - ); - } - - /** - * Test for the locationCopied() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationCopied - */ - public function testLocationCopiedHistoryNotCopied() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_copy.php'); - - $handler->locationCopied(4, 400, 3); - - $handler->lookup('move-here/move-this-history'); - } - - /** - * Test for the locationCopied() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationCopied - */ - public function testLocationCopiedSubtreeHistoryNotCopied() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_copy.php'); - - $handler->locationCopied(4, 400, 3); - - $handler->lookup('move-here/move-this/sub1/sub2-history'); - } - - /** - * Test for the locationCopied() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationCopied - */ - public function testLocationCopiedSubtree() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_copy.php'); - - $countBeforeCopying = $this->countRows(); - - $handler->locationCopied(4, 400, 3); - - self::assertEquals( - $countBeforeCopying + 2, - $this->countRows() - ); - - $urlAlias = $handler->lookup('move-here/move-this/sub1/sub2'); - self::assertEquals( - new UrlAlias( - [ - 'id' => '10-' . md5('sub2'), - 'type' => UrlAlias::LOCATION, - 'destination' => 600, - 'languageCodes' => ['eng-GB'], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'move-here'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'move-this'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'sub1'], - ], - [ - 'always-available' => false, - 'translations' => ['eng-GB' => 'sub2'], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the loadUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::loadUrlAlias - * @dataProvider providerForTestLookupLocationMultipleLanguages - */ - public function testLoadAutogeneratedUrlAlias( - $url, - array $pathData, - array $languageCodes, - $alwaysAvailable, - $locationId, - $id - ) { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_multilang.php'); - - $urlAlias = $handler->loadUrlAlias($id); - - self::assertInstanceOf(UrlAlias::class, $urlAlias); - self::assertEquals( - new UrlAlias( - [ - 'id' => $id, - 'type' => UrlAlias::LOCATION, - 'destination' => $locationId, - 'languageCodes' => $languageCodes, - 'pathData' => $pathData, - 'alwaysAvailable' => $alwaysAvailable, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the loadUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::loadUrlAlias - * @dataProvider providerForTestLookupResourceUrlAlias - */ - public function testLoadResourceUrlAlias( - $url, - $pathData, - array $languageCodes, - $forward, - $alwaysAvailable, - $destination, - $id - ) { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); - - $urlAlias = $handler->loadUrlAlias($id); - - self::assertInstanceOf(UrlAlias::class, $urlAlias); - self::assertEquals( - new UrlAlias( - [ - 'id' => $id, - 'type' => UrlAlias::RESOURCE, - 'destination' => $destination, - 'languageCodes' => $languageCodes, - 'pathData' => $pathData, - 'alwaysAvailable' => $alwaysAvailable, - 'isHistory' => false, - 'isCustom' => true, - 'forward' => $forward, - ] - ), - $urlAlias - ); - } - - /** - * Test for the loadUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::loadUrlAlias - * @dataProvider providerForTestLookupVirtualUrlAlias - */ - public function testLoadVirtualUrlAlias($url, $id) - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_custom.php'); - - $urlAlias = $handler->loadUrlAlias($id); - - $this->assertVirtualUrlAliasValid($urlAlias, $id); - } - - protected function getHistoryAlias() - { - return new UrlAlias( - [ - 'id' => '3-5f46413bb0ba5998caef84ab1ea590e1', - 'type' => UrlAlias::LOCATION, - 'destination' => '316', - 'pathData' => [ - [ - 'always-available' => true, - 'translations' => ['cro-HR' => 'jedan'], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri-history', - ], - ], - ], - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ); - } - - /** - * Test for the loadUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::loadUrlAlias - */ - public function testLoadHistoryUrlAlias() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location.php'); - - $historyAlias = $this->getHistoryAlias(); - $urlAlias = $handler->loadUrlAlias($historyAlias->id); - - self::assertEquals( - $historyAlias, - $urlAlias - ); - } - - /** - * Test for the loadUrlAlias() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::loadUrlAlias - */ - public function testLoadUrlAliasThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $handler = $this->getHandler(); - - $handler->loadUrlAlias('non-existent'); - } - - public function providerForTestPublishUrlAliasForLocationSkipsReservedWord() - { - return [ - [ - 'section', - 'section2', - ], - [ - 'claß', - 'class2', - ], - ]; - } - - /** - * Test for the publishUrlAliasForLocation() method. - * - * @dataProvider providerForTestPublishUrlAliasForLocationSkipsReservedWord - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation - * @group publish - */ - public function testPublishUrlAliasForLocationSkipsReservedWord($text, $alias) - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); - - $handler->publishUrlAliasForLocation(314, 2, $text, 'kli-KR'); - - $urlAlias = $handler->lookup($alias); - - $this->assertEquals(314, $urlAlias->destination); - $this->assertEquals(['kli-KR'], $urlAlias->languageCodes); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedSimple() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_simple.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(316, 314, 317, 315); - - $this->assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - $urlAlias = $handler->lookup('jedan/swap'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedSimpleWithHistory() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_simple_history.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(316, 314, 317, 315); - - $this->assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - $urlAlias = $handler->lookup('jedan/swap'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('jedan/swap-new'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-new'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-new', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-new'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-new'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-new', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedSimpleWithConflict() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_simple_conflict.php'); - - $urlAlias1TakenExpected = $handler->lookup('jedan/swap-new-2'); - $urlAlias2TakenExpected = $handler->lookup('dva/swap-new-1'); - - $urlAlias1HistorizedExpected = $handler->lookup('jedan/swap-new-1'); - $urlAlias1HistorizedExpected->isHistory = true; - $urlAlias2HistorizedExpected = $handler->lookup('dva/swap-new-2'); - $urlAlias2HistorizedExpected->isHistory = true; - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(316, 314, 317, 315); - - $this->assertEquals( - $countBeforeReusing + 2, - $this->countRows() - ); - - $urlAlias1Taken = $handler->lookup('jedan/swap-new-2'); - $urlAlias2Taken = $handler->lookup('dva/swap-new-1'); - - $urlAlias1Historized = $handler->lookup('jedan/swap-new-1'); - $urlAlias2Historized = $handler->lookup('dva/swap-new-2'); - - $this->assertEquals($urlAlias1TakenExpected, $urlAlias1Taken); - $this->assertEquals($urlAlias2TakenExpected, $urlAlias2Taken); - - $this->assertEquals($urlAlias1HistorizedExpected, $urlAlias1Historized); - $this->assertEquals($urlAlias2HistorizedExpected, $urlAlias2Historized); - - $urlAlias1New = $handler->lookup('jedan/swap-new-22'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-new-22'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-new-22', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias1New - ); - - $urlAlias2New = $handler->lookup('dva/swap-new-12'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-new-12'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-new-12', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias2New - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedSiblingsSimple() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_simple.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(314, 2, 315, 2); - - $this->assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - $urlAlias = $handler->lookup('jedan'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('jedan'), - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('dva'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedSiblingsSimpleReverse() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_simple.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(315, 2, 314, 2); - - $this->assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - $urlAlias = $handler->lookup('jedan'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('jedan'), - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('dva'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedSiblingsSimpleWithHistory() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_simple_history.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(314, 2, 315, 2); - - $this->assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - $urlAlias = $handler->lookup('jedan'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('jedan'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('dva'), - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('jedan-new'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('jedan-new'), - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan-new', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva-new'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('dva-new'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva-new', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedSiblingsSimpleWithHistoryReverse() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_simple_history.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(315, 2, 314, 2); - - $this->assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - $urlAlias = $handler->lookup('jedan'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('jedan'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('dva'), - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('jedan-new'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('jedan-new'), - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan-new', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva-new'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('dva-new'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva-new', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedSiblingsSameName() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_same_name.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(314, 2, 315, 2); - - $this->assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - $urlAlias = $handler->lookup('swap'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('swap'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('swap2'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('swap2'), - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap2', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedSiblingsSameNameReverse() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_same_name.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(315, 2, 314, 2); - - $this->assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - $urlAlias = $handler->lookup('swap'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('swap'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('swap2'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('swap2'), - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap2', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedSiblingsSameNameMultipleLanguages() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_same_name_multilang.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(314, 2, 315, 2); - - $this->assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - $urlAlias = $handler->lookup('swap-hr'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('swap-hr'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-hr', - 'eng-GB' => 'swap-en2', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('swap-hr2'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('swap-hr2'), - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-hr2', - 'eng-GB' => 'swap-en', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('swap-en'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('swap-en'), - 'type' => UrlAlias::LOCATION, - 'destination' => 315, - 'languageCodes' => [ - 'eng-GB', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-hr2', - 'eng-GB' => 'swap-en', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('swap-en2'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '0-' . md5('swap-en2'), - 'type' => UrlAlias::LOCATION, - 'destination' => 314, - 'languageCodes' => [ - 'eng-GB', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-hr', - 'eng-GB' => 'swap-en2', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedMultipleLanguagesSimple() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_multilang_simple.php'); - - $urlAlias1HRExpected = $handler->lookup('jedan/swap-hr'); - $urlAlias1ENExpected = $handler->lookup('jedan/swap-en'); - $urlAlias2HRExpected = $handler->lookup('dva/swap-hr'); - $urlAlias2ENExpected = $handler->lookup('dva/swap-en'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(316, 314, 317, 315); - - $this->assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - $urlAlias1HR = $handler->lookup('jedan/swap-hr'); - $urlAlias1EN = $handler->lookup('jedan/swap-en'); - $urlAlias2HR = $handler->lookup('dva/swap-hr'); - $urlAlias2EN = $handler->lookup('dva/swap-en'); - - $this->assertEquals($urlAlias1HRExpected, $urlAlias1HR); - $this->assertEquals($urlAlias1ENExpected, $urlAlias1EN); - $this->assertEquals($urlAlias2HRExpected, $urlAlias2HR); - $this->assertEquals($urlAlias2ENExpected, $urlAlias2EN); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedMultipleLanguagesDifferentLanguagesSimple() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_multilang_diff_simple.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(316, 314, 317, 315); - - $this->assertEquals( - $countBeforeReusing + 2, - $this->countRows() - ); - - $urlAlias = $handler->lookup('jedan/swap-hr'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-hr'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-hr', - 'ger-DE' => 'swap-de', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('jedan/swap-de'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-de'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'ger-DE', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-hr', - 'ger-DE' => 'swap-de', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('jedan/swap-en'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-en'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'eng-GB', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'swap-en', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-hr'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-hr'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'swap-en', - 'cro-HR' => 'swap-hr', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-en'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-en'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'eng-GB', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'swap-en', - 'cro-HR' => 'swap-hr', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-de'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-de'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'ger-DE', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'ger-DE' => 'swap-de', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedMultipleLanguagesDifferentLanguages() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_multilang_diff.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(317, 315, 316, 314); - - $this->assertEquals( - $countBeforeReusing + 2, - $this->countRows() - ); - - $urlAlias = $handler->lookup('jedan/swap-this'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-this'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'ger-DE', - 'nor-NO', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-hr', - 'ger-DE' => 'swap-this', - 'nor-NO' => 'swap-this', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('jedan/swap-en'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-en'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'eng-GB', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'swap-en', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-hr'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-hr'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-hr', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-this'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-this'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'cro-HR', - 'ger-DE', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-this', - 'ger-DE' => 'swap-this', - 'eng-GB' => 'swap-en', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedMultipleLanguagesWithCompositeHistory() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_multilang_cleanup_composite.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(317, 315, 316, 314); - - $this->assertEquals( - $countBeforeReusing + 4, - $this->countRows() - ); - - $urlAlias = $handler->lookup('jedan/swap-this'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-this'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-this', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('jedan/swap-en'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-en'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'eng-GB', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'swap-en', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('jedan/swap-hr'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-hr'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-hr', - 'ger-DE' => 'swap-that', - 'nor-NO' => 'swap-that', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('jedan/swap-that'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-that'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'ger-DE', - 'nor-NO', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-hr', - 'ger-DE' => 'swap-that', - 'nor-NO' => 'swap-that', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-hr'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-hr'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-hr', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-that'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-that'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'ger-DE', - 'nor-NO', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'ger-DE' => 'swap-that', - 'nor-NO' => 'swap-that', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-this'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-this'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-this', - 'eng-GB' => 'swap-en', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-en'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-en'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'eng-GB', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-this', - 'eng-GB' => 'swap-en', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedWithReusingExternalHistory() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_reusing_external_history.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(318, 314, 319, 315); - - $this->assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - $urlAlias = $handler->lookup('jedan/swap-that'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-that'), - 'type' => UrlAlias::LOCATION, - 'destination' => 318, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-that', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-this'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-this'), - 'type' => UrlAlias::LOCATION, - 'destination' => 319, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-this', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-that'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-that'), - 'type' => UrlAlias::LOCATION, - 'destination' => 319, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-that', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('jedan/swap-this'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-this'), - 'type' => UrlAlias::LOCATION, - 'destination' => 318, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-this', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - */ - public function testLocationSwappedWithReusingNopEntry() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_reusing_nop.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(316, 314, 317, 315); - - $this->assertEquals( - $countBeforeReusing + 1, - $this->countRows() - ); - - $urlAlias = $handler->lookup('jedan/swap-that'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-that'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-that', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-this'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-this'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-this', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => false, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('dva/swap-that'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '3-' . md5('swap-that'), - 'type' => UrlAlias::LOCATION, - 'destination' => 317, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-that', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - - $urlAlias = $handler->lookup('jedan/swap-this'); - $this->assertEquals( - new UrlAlias( - [ - 'id' => '2-' . md5('swap-this'), - 'type' => UrlAlias::LOCATION, - 'destination' => 316, - 'languageCodes' => [ - 'cro-HR', - ], - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'swap-this', - ], - ], - ], - 'alwaysAvailable' => false, - 'isHistory' => true, - 'isCustom' => false, - 'forward' => false, - ] - ), - $urlAlias - ); - } - - /** - * Test for the locationSwapped() method. - * - * @depends testLocationSwappedWithReusingNopEntry - * @group swap - */ - public function testLocationSwappedWithReusingNopEntryCustomAliasIsDestroyed() - { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_reusing_nop.php'); - - $handler->lookup('jedan/swap-that/search'); - $handler->locationSwapped(316, 314, 317, 315); - - try { - $handler->lookup('jedan/swap-that/search'); - $this->fail('Custom alias is not destroyed'); - } catch (NotFoundException $e) { - // Custom alias is destroyed by reusing NOP entry with existing autogenerated alias - // on the same level (that means link and ID are updated to the existing alias ID, - // so custom alias children entries are no longer properly linked (parent-link)) - } - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationSwapped - */ - public function testLocationSwappedUpdatesLocationPathIdentificationString() - { - $handler = $this->getHandler(); - $locationGateway = $this->getLocationGateway(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_path_identification_string.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(314, 2, 315, 2); - - $this->assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - $locationData = $locationGateway->getBasicNodeData(314); - self::assertEquals('dva', $locationData['path_identification_string']); - - $locationData = $locationGateway->getBasicNodeData(315); - self::assertEquals('jedan', $locationData['path_identification_string']); - } - - /** - * Test for the locationSwapped() method. - * - * @group swap - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationSwapped - */ - public function testLocationSwappedMultipleLanguagesUpdatesLocationPathIdentificationString() - { - $handler = $this->getHandler(); - $locationGateway = $this->getLocationGateway(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_multilang_path_identification_string.php'); - - $countBeforeReusing = $this->countRows(); - - $handler->locationSwapped(314, 2, 315, 2); - - $this->assertEquals( - $countBeforeReusing, - $this->countRows() - ); - - $locationData = $locationGateway->getBasicNodeData(314); - self::assertEquals('zwei', $locationData['path_identification_string']); - - $locationData = $locationGateway->getBasicNodeData(315); - self::assertEquals('jedan', $locationData['path_identification_string']); - } - - protected function countRows(): int - { - $connection = $this->getDatabaseConnection(); - $query = $connection->createQueryBuilder(); - $query - ->select($connection->getDatabasePlatform()->getCountExpression('*')) - ->from(UrlAliasGateway::TABLE); - - $statement = $query->execute(); - - return (int)$statement->fetchColumn(); - } - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway */ - protected $locationGateway; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler */ - protected $languageHandler; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator */ - protected $languageMaskGenerator; - - /** - * @param array $methods - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getPartlyMockedHandler(array $methods) - { - return $this->getMockBuilder(Handler::class) - ->setConstructorArgs( - [ - $this->createMock(UrlAliasGateway::class), - $this->createMock(Mapper::class), - $this->createMock(LocationGateway::class), - $this->createMock(LanguageHandler::class), - $this->createMock(SlugConverter::class), - $this->createMock(Gateway::class), - $this->createMock(LanguageMaskGenerator::class), - $this->createMock(TransactionHandler::class), - ] - ) - ->setMethods($methods) - ->getMock(); - } - - /** - * @return \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function getHandler(): Handler - { - $languageHandler = $this->getLanguageHandler(); - $languageMaskGenerator = $this->getLanguageMaskGenerator(); - $gateway = new DoctrineDatabase( - $this->getDatabaseConnection(), - $languageMaskGenerator - ); - $mapper = new Mapper($languageMaskGenerator); - $slugConverter = new SlugConverter($this->getProcessor()); - $connection = $this->getDatabaseConnection(); - $contentGateway = new ContentGateway( - $connection, - $this->getSharedGateway(), - new ContentGateway\QueryBuilder($connection), - $languageHandler, - $languageMaskGenerator - ); - - return new Handler( - $gateway, - $mapper, - $this->getLocationGateway(), - $languageHandler, - $slugConverter, - $contentGateway, - $languageMaskGenerator, - $this->createMock(TransactionHandler::class) - ); - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - protected function getLanguageHandler(): LanguageHandler - { - if (!isset($this->languageHandler)) { - $this->languageHandler = new LanguageHandler( - new LanguageGateway( - $this->getDatabaseConnection() - ), - new LanguageMapper() - ); - } - - return $this->languageHandler; - } - - /** - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator - */ - protected function getLanguageMaskGenerator() - { - if (!isset($this->languageMaskGenerator)) { - $this->languageMaskGenerator = new LanguageMaskGenerator( - $this->getLanguageHandler() - ); - } - - return $this->languageMaskGenerator; - } - - /** - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway - */ - protected function getLocationGateway() - { - if (!isset($this->locationGateway)) { - $this->locationGateway = new DoctrineDatabaseLocation( - $this->getDatabaseConnection(), - $this->getLanguageMaskGenerator(), - $this->getTrashCriteriaConverterDependency(), - $this->getTrashSortClauseConverterDependency() - ); - } - - return $this->locationGateway; - } - - /** - * @return \eZ\Publish\Core\Persistence\TransformationProcessor - */ - public function getProcessor() - { - return new DefinitionBased( - new Parser(), - new PcreCompiler(new Utf8Converter()), - glob(__DIR__ . '/../../../../Tests/TransformationProcessor/_fixtures/transformations/*.tr') - ); - } - - /** - * Data provider for tests of archiveUrlAliasesForDeletedTranslations. - * - * @see testArchiveUrlAliasesForDeletedTranslations for the description of parameters - * - * @return array - */ - public function providerForArchiveUrlAliasesForDeletedTranslations() - { - return [ - [2, ['eng-GB', 'pol-PL'], 'pol-PL'], - [3, ['eng-GB', 'pol-PL', 'nor-NO'], 'pol-PL'], - ]; - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler::archiveUrlAliasesForDeletedTranslations() - * - * @dataProvider providerForArchiveUrlAliasesForDeletedTranslations - * - * @param int $locationId - * @param string[] $expectedLanguages expected language codes before deleting - * @param string $removeLanguage language code to be deleted - */ - public function testArchiveUrlAliasesForDeletedTranslations( - $locationId, - array $expectedLanguages, - $removeLanguage - ) { - $handler = $this->getHandler(); - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_multilingual.php'); - - // collect data persisted from fixtures - $urlAliases = $handler->listURLAliasesForLocation($locationId); - $collectedLanguages = []; - $collectedUrls = []; - foreach ($urlAliases as $urlAlias) { - // collect languages of all URL aliases - $collectedLanguages = array_merge($collectedLanguages, $urlAlias->languageCodes); - $isComposite = count($urlAlias->languageCodes) > 1; - foreach ($urlAlias->pathData as $pathData) { - // collect also actual unique URLs to be removed to check them after removal - if (!empty($pathData['translations'][$removeLanguage])) { - $url = $pathData['translations'][$removeLanguage]; - $collectedUrls[$url] = $isComposite; - } - } - } - // sanity check - self::assertEquals($expectedLanguages, $collectedLanguages); - - // remove language - $publishedLanguages = array_values(array_diff($collectedLanguages, [$removeLanguage])); - $handler->archiveUrlAliasesForDeletedTranslations($locationId, 1, $publishedLanguages); - - // check reloaded structures - $urlAliases = $handler->listURLAliasesForLocation($locationId); - foreach ($urlAliases as $urlAlias) { - self::assertNotContains($removeLanguage, $urlAlias->languageCodes); - foreach ($urlAlias->pathData as $pathData) { - self::assertNotContains($removeLanguage, $pathData['translations']); - foreach ($pathData['translations'] as $url) { - $lookupUrlAlias = $handler->lookup($url); - self::assertNotContains($removeLanguage, $lookupUrlAlias->languageCodes); - } - } - } - - // lookup removed URLs to check they're not found - foreach ($collectedUrls as $url => $isComposite) { - $urlAlias = $handler->lookup($url); - if ($isComposite) { - // check if alias no longer refers to removed Translation - self::assertNotContains($removeLanguage, $urlAlias->languageCodes); - foreach ($urlAlias->pathData as $pathData) { - self::assertNotContains($removeLanguage, $pathData['translations']); - } - } else { - // check if non composite alias for removed translation is historized - self::assertTrue($urlAlias->isHistory); - } - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/publish_base.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/publish_base.php deleted file mode 100644 index dfb30b1c11..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/publish_base.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php - -return array( - 'ezurlalias_ml' => array( - 0 => array( - 'action' => 'eznode:2', - 'action_type' => 'eznode', - 'alias_redirects' => '1', - 'id' => '1', - 'is_alias' => '0', - 'is_original' => '1', - 'lang_mask' => '3', - 'link' => '1', - 'parent' => '0', - 'text' => '', - 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( - 'action' => 'eznode:314', - 'action_type' => 'eznode', - 'alias_redirects' => '0', - 'id' => '2', - 'is_alias' => '0', - 'is_original' => '1', - 'lang_mask' => '3', - 'link' => '2', - 'parent' => '0', - 'text' => 'path314', - 'text_md5' => 'fdbbfa1e24e78ef56cb16ba4482c7771', - ), - 2 => array( - 'action' => 'eznode:315', - 'action_type' => 'eznode', - 'alias_redirects' => '0', - 'id' => '3', - 'is_alias' => '0', - 'is_original' => '1', - 'lang_mask' => '4', - 'link' => '3', - 'parent' => '2', - 'text' => 'path315', - 'text_md5' => 'afbe70de5f03a22e867723655a995279', - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( - 'id' => '1', - ), - 1 => array( - 'id' => '2', - ), - 2 => array( - 'id' => '3', - ), - ), - 'ezcontent_language' => array( - 0 => array( - 'disabled' => 0, - 'id' => 2, - 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( - 'disabled' => 0, - 'id' => 4, - 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - 2 => array( - 'disabled' => 0, - 'id' => 8, - 'locale' => 'ger-DE', - 'name' => 'German' - ), - 3 => array( - 'disabled' => 0, - 'id' => 16, - 'locale' => 'kli-KR', - 'name' => 'Klingon (Kronos)' - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( - 'node_id' => 1, - 'parent_node_id' => 1, - 'path_string' => '', - 'path_identification_string' => '', - 'remote_id' => '', - ), - 1 => array( - 'node_id' => 2, - 'parent_node_id' => 1, - 'path_string' => '', - 'path_identification_string' => '', - 'remote_id' => '', - ), - 2 => array( - 'node_id' => 314, - 'parent_node_id' => 2, - 'path_string' => '', - 'path_identification_string' => 'path314', - 'remote_id' => '', - ), - 3 => array( - 'node_id' => 315, - 'parent_node_id' => 314, - 'path_string' => '', - 'path_identification_string' => 'path314/path315', - 'remote_id' => '', - ), - 4 => array( - 'node_id' => 316, - 'parent_node_id' => 315, - 'path_string' => '', - 'path_identification_string' => 'path314/path315/path316', - 'remote_id' => '', - ), - ), -); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index 0278274490..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,189 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlWildcard\Gateway; - -use eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard; -use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriteriaConverter; -use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriterionHandler\MatchAll; - -/** - * Test case for eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase. - */ -class DoctrineDatabaseTest extends TestCase -{ - /** - * Database gateway to test. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase - */ - protected $gateway; - - protected $fixtureData = [ - 0 => [ - 'id' => '1', - 'source_url' => 'developer/*', - 'destination_url' => 'dev/{1}', - 'type' => '2', - ], - 1 => [ - 'id' => '2', - 'source_url' => 'repository/*', - 'destination_url' => 'repo/{1}', - 'type' => '2', - ], - 2 => [ - 'id' => '3', - 'source_url' => 'information/*', - 'destination_url' => 'info/{1}', - 'type' => '2', - ], - ]; - - /** - * Test for the loadUrlWildcardData() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase::loadUrlWildcardData - */ - public function testLoadUrlWildcardData() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlwildcards.php'); - $gateway = $this->getGateway(); - - $row = $gateway->loadUrlWildcardData(1); - - self::assertEquals( - $this->fixtureData[0], - $row - ); - } - - /** - * Test for the loadUrlWildcardsData() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase::loadUrlWildcardsData - */ - public function testLoadUrlWildcardsData() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlwildcards.php'); - $gateway = $this->getGateway(); - - $rows = $gateway->loadUrlWildcardsData(); - - self::assertEquals( - $this->fixtureData, - $rows - ); - } - - /** - * Test for the loadUrlWildcardsData() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase::loadUrlWildcardsData - */ - public function testLoadUrlWildcardsDataWithOffset() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlwildcards.php'); - $gateway = $this->getGateway(); - - $row = $gateway->loadUrlWildcardsData(1); - - self::assertEquals( - [ - 0 => $this->fixtureData[1], - 1 => $this->fixtureData[2], - ], - $row - ); - } - - /** - * Test for the loadUrlWildcardsData() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase::loadUrlWildcardsData - */ - public function testLoadUrlWildcardsDataWithOffsetAndLimit() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlwildcards.php'); - $gateway = $this->getGateway(); - - $row = $gateway->loadUrlWildcardsData(1, 1); - - self::assertEquals( - [ - 0 => $this->fixtureData[1], - ], - $row - ); - } - - /** - * Test for the insertUrlWildcard() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase::insertUrlWildcard - * @depends testLoadUrlWildcardData - */ - public function testInsertUrlWildcard() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlwildcards.php'); - $gateway = $this->getGateway(); - - $id = $gateway->insertUrlWildcard( - new UrlWildcard( - [ - 'sourceUrl' => '/contact-information/*', - 'destinationUrl' => '/contact/{1}', - 'forward' => true, - ] - ) - ); - - self::assertEquals( - [ - 'id' => $id, - 'source_url' => 'contact-information/*', - 'destination_url' => 'contact/{1}', - 'type' => '1', - ], - $gateway->loadUrlWildcardData($id) - ); - } - - /** - * Test for the deleteUrlWildcard() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase::deleteUrlWildcard - * @depends testLoadUrlWildcardData - */ - public function testDeleteUrlWildcard() - { - $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlwildcards.php'); - $gateway = $this->getGateway(); - - $gateway->deleteUrlWildcard(1); - - self::assertEmpty($gateway->loadUrlWildcardData(1)); - } - - /** - * Return the DoctrineDatabase gateway to test. - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function getGateway(): DoctrineDatabase - { - if (!isset($this->gateway)) { - $criteriaConverter = new CriteriaConverter([new MatchAll()]); - $this->gateway = new DoctrineDatabase($this->getDatabaseConnection(), $criteriaConverter); - } - - return $this->gateway; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlWildcard/Gateway/_fixtures/urlwildcards.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlWildcard/Gateway/_fixtures/urlwildcards.php deleted file mode 100644 index f792bea8fb..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlWildcard/Gateway/_fixtures/urlwildcards.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -return array( - 'ezurlwildcard' => array( - 0 => array( - 'id' => '1', - 'source_url' => 'developer/*', - 'destination_url' => 'dev/{1}', - 'type' => '2', - ), - 1 => array( - 'id' => '2', - 'source_url' => 'repository/*', - 'destination_url' => 'repo/{1}', - 'type' => '2', - ), - 2 => array( - 'id' => '3', - 'source_url' => 'information/*', - 'destination_url' => 'info/{1}', - 'type' => '2', - ), - ), -); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlWildcard/UrlWildcardHandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlWildcard/UrlWildcardHandlerTest.php deleted file mode 100644 index 7ce56c97c1..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlWildcard/UrlWildcardHandlerTest.php +++ /dev/null @@ -1,262 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlWildcard; - -use eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Handler; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard; -use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriteriaConverter; -use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriterionHandler\MatchAll; - -/** - * Test case for UrlWildcard Handler. - */ -class UrlWildcardHandlerTest extends TestCase -{ - /** - * Test for the load() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Handler::load - */ - public function testLoad() - { - $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); - $handler = $this->getHandler(); - - $urlWildcard = $handler->load(1); - - self::assertEquals( - new UrlWildcard( - [ - 'id' => 1, - 'sourceUrl' => '/developer/*', - 'destinationUrl' => '/dev/{1}', - 'forward' => false, - ] - ), - $urlWildcard - ); - } - - /** - * Test for the load() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Handler::load - */ - public function testLoadThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); - $handler = $this->getHandler(); - - $handler->load(100); - } - - /** - * Test for the create() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Handler::create - * @depends testLoad - */ - public function testCreate() - { - $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); - $handler = $this->getHandler(); - - $urlWildcard = $handler->create( - 'amber', - 'pattern', - true - ); - - self::assertEquals( - new UrlWildcard( - [ - 'id' => 4, - 'sourceUrl' => '/amber', - 'destinationUrl' => '/pattern', - 'forward' => true, - ] - ), - $urlWildcard - ); - - self::assertEquals( - $urlWildcard, - $handler->load(4) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Handler::update - * @depends testLoad - */ - public function testUpdate(): void - { - $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); - $handler = $this->getHandler(); - - $urlWildcard = $handler->load(1); - - $urlWildcardUpdated = $handler->update( - $urlWildcard->id, - 'amber-updated', - 'pattern-updated', - true - ); - - self::assertEquals( - new UrlWildcard( - [ - 'id' => 1, - 'sourceUrl' => '/amber-updated', - 'destinationUrl' => '/pattern-updated', - 'forward' => true, - ] - ), - $urlWildcardUpdated - ); - - self::assertEquals( - $urlWildcardUpdated, - $handler->load(1) - ); - } - - /** - * Test for the remove() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Handler::remove - * @depends testLoad - */ - public function testRemove() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); - $handler = $this->getHandler(); - - $handler->remove(1); - $handler->load(1); - } - - /** - * Test for the loadAll() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Handler::loadAll - */ - public function testLoadAll() - { - $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); - $handler = $this->getHandler(); - - $urlWildcards = $handler->loadAll(); - - self::assertEquals( - [ - new UrlWildcard($this->fixtureData[0]), - new UrlWildcard($this->fixtureData[1]), - new UrlWildcard($this->fixtureData[2]), - ], - $urlWildcards - ); - } - - /** - * Test for the loadAll() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Handler::loadAll - */ - public function testLoadAllWithOffset() - { - $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); - $handler = $this->getHandler(); - - $urlWildcards = $handler->loadAll(2); - - self::assertEquals( - [ - new UrlWildcard($this->fixtureData[2]), - ], - $urlWildcards - ); - } - - /** - * Test for the loadAll() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Handler::loadAll - */ - public function testLoadAllWithOffsetAndLimit() - { - $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); - $handler = $this->getHandler(); - - $urlWildcards = $handler->loadAll(1, 1); - - self::assertEquals( - [ - new UrlWildcard($this->fixtureData[1]), - ], - $urlWildcards - ); - } - - protected $fixtureData = [ - [ - 'id' => 1, - 'sourceUrl' => '/developer/*', - 'destinationUrl' => '/dev/{1}', - 'forward' => false, - ], - [ - 'id' => 2, - 'sourceUrl' => '/repository/*', - 'destinationUrl' => '/repo/{1}', - 'forward' => false, - ], - [ - 'id' => 3, - 'sourceUrl' => '/information/*', - 'destinationUrl' => '/info/{1}', - 'forward' => false, - ], - ]; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase */ - protected $gateway; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Mapper */ - protected $mapper; - - /** @var \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler */ - protected $urlWildcardHandler; - - /** - * @throws \Doctrine\DBAL\DBALException - */ - protected function getHandler(): UrlWildcard\Handler - { - if (!isset($this->urlWildcardHandler)) { - $criteriaConverter = new CriteriaConverter([new MatchAll()]); - $this->gateway = new DoctrineDatabase($this->getDatabaseConnection(), $criteriaConverter); - $this->mapper = new Mapper(); - - $this->urlWildcardHandler = new Handler( - $this->gateway, - $this->mapper - ); - } - - return $this->urlWildcardHandler; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_names_from_rows.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_names_from_rows.php deleted file mode 100644 index 7d51bdefc1..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_names_from_rows.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -return [ - 0 => [ - 'ezcontentobject_name_contentobject_id' => '226', - 'ezcontentobject_name_content_version' => '2', - 'ezcontentobject_name_name' => 'Something', - 'ezcontentobject_name_content_translation' => 'eng-GB', - ], - 1 => [ - 'ezcontentobject_name_contentobject_id' => '226', - 'ezcontentobject_name_content_version' => '2', - 'ezcontentobject_name_name' => 'Something', - 'ezcontentobject_name_content_translation' => 'eng-US', - ], -]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_names_from_rows_multiple_versions.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_names_from_rows_multiple_versions.php deleted file mode 100644 index fe711546eb..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_names_from_rows_multiple_versions.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -return [ - 0 => [ - 'ezcontentobject_name_contentobject_id' => '11', - 'ezcontentobject_name_content_version' => '1', - 'ezcontentobject_name_name' => 'Guest accounts', - 'ezcontentobject_name_content_translation' => 'eng-US', - ], - 1 => [ - 'ezcontentobject_name_contentobject_id' => '11', - 'ezcontentobject_name_content_version' => '2', - 'ezcontentobject_name_name' => 'Members', - 'ezcontentobject_name_content_translation' => 'eng-US', - ], -]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/languages.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/languages.php deleted file mode 100644 index 488f6fefc0..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/languages.php +++ /dev/null @@ -1,8 +0,0 @@ -<?php - -return [ - 'ezcontent_language' => [ - ['disabled' => 0, 'id' => 2, 'locale' => 'eng-US', 'name' => 'English (American)'], - ['disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', 'name' => 'English (United Kingdom)'], - ], -]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/relations_results.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/relations_results.php deleted file mode 100644 index 96ecb7ac39..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/relations_results.php +++ /dev/null @@ -1,11 +0,0 @@ -<?php -use eZ\Publish\SPI\Persistence\Content\Relation; - -$relation = new Relation(); -$relation->id = 1; -$relation->sourceContentId = 1; -$relation->sourceContentVersionNo = 1; -$relation->type = 1; -$relation->destinationContentId = 2; - -return [1 => $relation]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/relations_rows.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/relations_rows.php deleted file mode 100644 index ec069915d0..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/relations_rows.php +++ /dev/null @@ -1,12 +0,0 @@ -<?php - -return [ - [ - 'ezcontentobject_link_id' => 1, - 'ezcontentobject_link_contentclassattribute_id' => 0, - 'ezcontentobject_link_from_contentobject_id' => 1, - 'ezcontentobject_link_from_contentobject_version' => 1, - 'ezcontentobject_link_relation_type' => 1, - 'ezcontentobject_link_to_contentobject_id' => 2, - ], -]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/BaseCriterionVisitorQueryBuilderTestCase.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/BaseCriterionVisitorQueryBuilderTestCase.php deleted file mode 100644 index e3447e5a53..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/BaseCriterionVisitorQueryBuilderTestCase.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Filter; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\Expression\ExpressionBuilder; -use eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Filter\CriterionVisitor; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use PHPUnit\Framework\TestCase; - -abstract class BaseCriterionVisitorQueryBuilderTestCase extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionVisitor */ - private $criterionVisitor; - - /** - * @return \eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder[] - */ - abstract protected function getCriterionQueryBuilders(): iterable; - - /** - * Data provider for {@see testVisitCriteriaProducesQuery}. - */ - abstract public function getFilteringCriteriaQueryData(): iterable; - - protected function setUp(): void - { - $this->criterionVisitor = new CriterionVisitor([]); - $this->criterionVisitor->setCriterionQueryBuilders( - array_merge( - $this->getBaseCriterionQueryBuilders($this->criterionVisitor), - $this->getCriterionQueryBuilders() - ) - ); - } - - /** - * @dataProvider getFilteringCriteriaQueryData - * - * @covers \eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder::buildQueryConstraint - * @covers \eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder::accepts - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionVisitor::visitCriteria - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function testVisitCriteriaProducesQuery( - FilteringCriterion $criterion, - string $expectedQuery, - array $expectedParameterValues - ): void { - $queryBuilder = $this->getQueryBuilder(); - $actualQuery = $this->criterionVisitor->visitCriteria($queryBuilder, $criterion); - $criterionFQCN = get_class($criterion); - self::assertSame( - $expectedQuery, - $actualQuery, - sprintf( - 'Query Builder for %s Criterion does not produce expected query', - $criterionFQCN - ) - ); - self::assertSame( - $expectedParameterValues, - $queryBuilder->getParameters(), - sprintf( - 'Query Builder for %s Criterion does not bind expected query parameter values', - $criterionFQCN - ) - ); - } - - private function getQueryBuilder(): FilteringQueryBuilder - { - $connectionMock = $this->createMock(Connection::class); - $connectionMock - ->method('getExpressionBuilder') - ->willReturn( - new ExpressionBuilder($connectionMock) - ); - - return new FilteringQueryBuilder($connectionMock); - } - - /** - * Create Query Builders needed for every test case. - * - * @see getCriterionQueryBuilders - */ - private function getBaseCriterionQueryBuilders(CriterionVisitor $criterionVisitor): iterable - { - return [ - new CriterionQueryBuilder\LogicalAndQueryBuilder($criterionVisitor), - new CriterionQueryBuilder\LogicalOrQueryBuilder($criterionVisitor), - new CriterionQueryBuilder\LogicalNotQueryBuilder($criterionVisitor), - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/Content/LanguageCodeQueryBuilderQueryBuilderTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/Content/LanguageCodeQueryBuilderQueryBuilderTest.php deleted file mode 100644 index 2a5ded950e..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/Content/LanguageCodeQueryBuilderQueryBuilderTest.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Filter\CriterionQueryBuilder\Content; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\LanguageCodeQueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Tests\Filter\BaseCriterionVisitorQueryBuilderTestCase; - -/** - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\LanguageCodeQueryBuilder::buildQueryConstraint - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\LanguageCodeQueryBuilder::accepts - */ -final class LanguageCodeQueryBuilderQueryBuilderTest extends BaseCriterionVisitorQueryBuilderTestCase -{ - public function getFilteringCriteriaQueryData(): iterable - { - yield 'Language Code IN (eng-GB, eng-US), match always available' => [ - new Criterion\LanguageCode(['eng-GB', 'eng-US']), - '(language.locale IN (:dcValue1)) OR (version.language_mask & 1 = 1)', - ['dcValue1' => ['eng-GB', 'eng-US']], - ]; - - yield 'Language Code=pol-PL, don\'t match always available' => [ - new Criterion\LanguageCode('pol-PL', false), - 'language.locale IN (:dcValue1)', - ['dcValue1' => ['pol-PL']], - ]; - } - - protected function getCriterionQueryBuilders(): iterable - { - return [new LanguageCodeQueryBuilder()]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilderTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilderTest.php deleted file mode 100644 index 43244ea2c7..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilderTest.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Filter\CriterionQueryBuilder\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Ancestor; -use eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\AncestorQueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Tests\Filter\BaseCriterionVisitorQueryBuilderTestCase; - -class AncestorQueryBuilderTest extends BaseCriterionVisitorQueryBuilderTestCase -{ - protected function getCriterionQueryBuilders(): iterable - { - return [new AncestorQueryBuilder()]; - } - - public function getFilteringCriteriaQueryData(): iterable - { - yield 'Ancestor=/1/2/' => [ - new Ancestor('/1/2/'), - 'location.node_id IN (:dcValue1)', - ['dcValue1' => [1, 2]], - ]; - - yield 'Ancestor IN (/1/2/, /1/4/10/' => [ - new Ancestor(['/1/2/', '/1/4/10/']), - 'location.node_id IN (:dcValue1)', - ['dcValue1' => [1, 2, 4, 10]], - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/Location/LocationIdQueryBuilderTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/Location/LocationIdQueryBuilderTest.php deleted file mode 100644 index 306336b1ab..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/Location/LocationIdQueryBuilderTest.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Filter\CriterionQueryBuilder\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\IdQueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Tests\Filter\BaseCriterionVisitorQueryBuilderTestCase; - -/** - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\ParentLocationIdQueryBuilder::buildQueryConstraint - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\ParentLocationIdQueryBuilder::accepts - */ -final class LocationIdQueryBuilderTest extends BaseCriterionVisitorQueryBuilderTestCase -{ - public function getFilteringCriteriaQueryData(): iterable - { - yield 'Location ID=1' => [ - new Criterion\LocationId(1), - 'location.node_id IN (:dcValue1)', - ['dcValue1' => [1]], - ]; - - yield 'Location ID=1 OR Location ID=2' => [ - new Criterion\LogicalOr( - [ - new Criterion\LocationId(1), - new Criterion\LocationId(2), - ] - ), - '(location.node_id IN (:dcValue1)) OR (location.node_id IN (:dcValue2))', - ['dcValue1' => [1], 'dcValue2' => [2]], - ]; - } - - protected function getCriterionQueryBuilders(): iterable - { - return [new IdQueryBuilder()]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/Location/ParentLocationQueryBuilderTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/Location/ParentLocationQueryBuilderTest.php deleted file mode 100644 index 3910ecaab5..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/Location/ParentLocationQueryBuilderTest.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Filter\CriterionQueryBuilder\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\ParentLocationIdQueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Tests\Filter\BaseCriterionVisitorQueryBuilderTestCase; - -/** - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\ParentLocationIdQueryBuilder::buildQueryConstraint - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\ParentLocationIdQueryBuilder::accepts - */ -final class ParentLocationQueryBuilderTest extends BaseCriterionVisitorQueryBuilderTestCase -{ - public function getFilteringCriteriaQueryData(): iterable - { - yield 'Parent Location ID=1' => [ - new Criterion\ParentLocationId(1), - 'location.parent_node_id IN (:dcValue1)', - ['dcValue1' => [1]], - ]; - - yield 'Parent Location ID=1 OR Parent Location ID=2' => [ - new Criterion\LogicalOr( - [ - new Criterion\ParentLocationId(1), - new Criterion\ParentLocationId(2), - ] - ), - '(location.parent_node_id IN (:dcValue1)) OR (location.parent_node_id IN (:dcValue2))', - ['dcValue1' => [1], 'dcValue2' => [2]], - ]; - } - - protected function getCriterionQueryBuilders(): iterable - { - return [new ParentLocationIdQueryBuilder()]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/LogicalOperatorQueryBuilderQueryBuilderTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/LogicalOperatorQueryBuilderQueryBuilderTest.php deleted file mode 100644 index ee087acf2b..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Filter/CriterionQueryBuilder/LogicalOperatorQueryBuilderQueryBuilderTest.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Filter\CriterionQueryBuilder; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\ContentIdQueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\LanguageCodeQueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\ParentLocationIdQueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Tests\Filter\BaseCriterionVisitorQueryBuilderTestCase; - -/** - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalAndQueryBuilder::buildQueryConstraint - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalOrQueryBuilder::buildQueryConstraint - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalNotQueryBuilder::buildQueryConstraint - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalAndQueryBuilder::accepts - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalOrQueryBuilder::accepts - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalNotQueryBuilder::accepts - */ -final class LogicalOperatorQueryBuilderQueryBuilderTest extends BaseCriterionVisitorQueryBuilderTestCase -{ - public function getFilteringCriteriaQueryData(): iterable - { - yield 'Parent Location ID=1 AND Language Code=eng-GB' => [ - new Criterion\LogicalAnd( - [ - new Criterion\ParentLocationId(1), - new Criterion\LanguageCode('eng-GB'), - ] - ), - '(location.parent_node_id IN (:dcValue1)) AND ((language.locale IN (:dcValue2)) OR (version.language_mask & 1 = 1))', - ['dcValue1' => [1], 'dcValue2' => ['eng-GB']], - ]; - - yield 'Language Code=eng-US OR Parent Location ID=2' => [ - new Criterion\LogicalOr( - [ - new Criterion\LanguageCode('eng-GB'), - new Criterion\ParentLocationId(2), - ] - ), - '((language.locale IN (:dcValue1)) OR (version.language_mask & 1 = 1)) OR (location.parent_node_id IN (:dcValue2))', - ['dcValue1' => ['eng-GB'], 'dcValue2' => [2]], - ]; - - yield 'NOT(Content ID=1 OR (Parent Location ID=2 AND Content ID = 1)' => [ - new Criterion\LogicalNot( - new Criterion\LogicalOr( - [ - new Criterion\ContentId(1), - new Criterion\LogicalAnd( - [ - new Criterion\ParentLocationId(2), - new Criterion\ContentId(1), - ] - ), - ] - ) - ), - 'NOT ((content.id IN (:dcValue1)) OR ((location.parent_node_id IN (:dcValue2)) AND (content.id IN (:dcValue3))))', - ['dcValue1' => [1], 'dcValue2' => [2], 'dcValue3' => [1]], - ]; - } - - protected function getCriterionQueryBuilders(): iterable - { - return [ - new ParentLocationIdQueryBuilder(), - new LanguageCodeQueryBuilder(), - new ContentIdQueryBuilder(), - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/HandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/HandlerTest.php deleted file mode 100644 index 5b56f4bf9f..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/HandlerTest.php +++ /dev/null @@ -1,324 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests; - -use eZ\Publish\Core\Base\ServiceContainer; -use eZ\Publish\Core\Persistence\Legacy\Content\Handler as ContentHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler as LocationHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler as SectionHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler as UrlAliasHandler; -use eZ\Publish\Core\Persistence\Legacy\Handler; -use eZ\Publish\Core\Persistence\Legacy\TransactionHandler; -use eZ\Publish\Core\Persistence\Legacy\User\Handler as UserHandler; -use eZ\Publish\SPI\Persistence\Content\Handler as SPIContentHandler; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as SPILanguageHandler; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as SPILocationHandler; -use eZ\Publish\SPI\Persistence\Content\Section\Handler as SPISectionHandler; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as SPIContentTypeHandler; -use eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler as SPIUrlAliasHandler; -use eZ\Publish\SPI\Persistence\TransactionHandler as SPITransactionHandler; -use eZ\Publish\SPI\Persistence\User\Handler as SPIUserHandler; - -/** - * Test case for Repository Handler. - */ -class HandlerTest extends TestCase -{ - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::contentHandler - */ - public function testContentHandler() - { - $handler = $this->getHandlerFixture(); - $contentHandler = $handler->contentHandler(); - - $this->assertInstanceOf( - SPIContentHandler::class, - $contentHandler - ); - $this->assertInstanceOf( - ContentHandler::class, - $contentHandler - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::contentHandler - */ - public function testContentHandlerTwice() - { - $handler = $this->getHandlerFixture(); - - $this->assertSame( - $handler->contentHandler(), - $handler->contentHandler() - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::contentTypeHandler - */ - public function testContentTypeHandler() - { - $handler = $this->getHandlerFixture(); - $contentTypeHandler = $handler->contentTypeHandler(); - - $this->assertInstanceOf( - SPIContentTypeHandler::class, - $contentTypeHandler - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::contentLanguageHandler - */ - public function testContentLanguageHandler() - { - $handler = $this->getHandlerFixture(); - $contentLanguageHandler = $handler->contentLanguageHandler(); - - $this->assertInstanceOf( - SPILanguageHandler::class, - $contentLanguageHandler - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::contentTypeHandler - */ - public function testContentTypeHandlerTwice() - { - $handler = $this->getHandlerFixture(); - - $this->assertSame( - $handler->contentTypeHandler(), - $handler->contentTypeHandler() - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::locationHandler - */ - public function testLocationHandler() - { - $handler = $this->getHandlerFixture(); - $locationHandler = $handler->locationHandler(); - - $this->assertInstanceOf( - SPILocationHandler::class, - $locationHandler - ); - $this->assertInstanceOf( - LocationHandler::class, - $locationHandler - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::locationHandler - */ - public function testLocationHandlerTwice() - { - $handler = $this->getHandlerFixture(); - - $this->assertSame( - $handler->locationHandler(), - $handler->locationHandler() - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::userHandler - */ - public function testUserHandler() - { - $handler = $this->getHandlerFixture(); - $userHandler = $handler->userHandler(); - - $this->assertInstanceOf( - SPIUserHandler::class, - $userHandler - ); - $this->assertInstanceOf( - UserHandler::class, - $userHandler - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::userHandler - */ - public function testUserHandlerTwice() - { - $handler = $this->getHandlerFixture(); - - $this->assertSame( - $handler->userHandler(), - $handler->userHandler() - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::sectionHandler - */ - public function testSectionHandler() - { - $handler = $this->getHandlerFixture(); - $sectionHandler = $handler->sectionHandler(); - - $this->assertInstanceOf( - SPISectionHandler::class, - $sectionHandler - ); - $this->assertInstanceOf( - SectionHandler::class, - $sectionHandler - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::sectionHandler - */ - public function testSectionHandlerTwice() - { - $handler = $this->getHandlerFixture(); - - $this->assertSame( - $handler->sectionHandler(), - $handler->sectionHandler() - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::urlAliasHandler - */ - public function testUrlAliasHandler() - { - $handler = $this->getHandlerFixture(); - $urlAliasHandler = $handler->urlAliasHandler(); - - $this->assertInstanceOf( - SPIUrlAliasHandler::class, - $urlAliasHandler - ); - $this->assertInstanceOf( - UrlAliasHandler::class, - $urlAliasHandler - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::urlAliasHandler - */ - public function testUrlAliasHandlerTwice() - { - $handler = $this->getHandlerFixture(); - - $this->assertSame( - $handler->urlAliasHandler(), - $handler->urlAliasHandler() - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::notificationHandler() - */ - public function testNotificationHandlerTwice() - { - $handler = $this->getHandlerFixture(); - - $this->assertSame( - $handler->notificationHandler(), - $handler->notificationHandler() - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::transactionHandler - */ - public function testTransactionHandler() - { - $handler = $this->getHandlerFixture(); - $transactionHandler = $handler->transactionHandler(); - - $this->assertInstanceOf( - SPITransactionHandler::class, - $transactionHandler - ); - $this->assertInstanceOf( - TransactionHandler::class, - $transactionHandler - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Handler::transactionHandler - */ - public function testTransactionHandlerTwice() - { - $handler = $this->getHandlerFixture(); - - $this->assertSame( - $handler->transactionHandler(), - $handler->transactionHandler() - ); - } - - protected static $legacyHandler; - - /** - * Returns the Handler. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Handler - */ - protected function getHandlerFixture() - { - if (!isset(self::$legacyHandler)) { - $container = $this->getContainer(); - - self::$legacyHandler = $container->get('ezpublish.spi.persistence.legacy'); - } - - return self::$legacyHandler; - } - - protected static $container; - - protected function getContainer() - { - if (!isset(self::$container)) { - $config = include __DIR__ . '/../../../../../../config.php'; - $installDir = $config['install_dir']; - - /** @var \Symfony\Component\DependencyInjection\ContainerBuilder $containerBuilder */ - $containerBuilder = include $config['container_builder_path']; - - /* @var \Symfony\Component\DependencyInjection\Loader\YamlFileLoader $loader */ - $loader->load('search_engines/legacy.yml'); - $loader->load('tests/integration_legacy.yml'); - - $containerBuilder->setParameter( - 'languages', - ['eng-US', 'eng-GB'] - ); - $containerBuilder->setParameter( - 'legacy_dsn', - $this->getDsn() - ); - - self::$container = new ServiceContainer( - $containerBuilder, - $installDir, - $config['cache_dir'], - true, - true - ); - } - - return self::$container; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Notification/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Notification/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index b5bf9b3fee..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Notification/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,193 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Notification\Gateway; - -use Doctrine\DBAL\FetchMode; -use eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Notification\CreateStruct; -use eZ\Publish\SPI\Persistence\Notification\Notification; - -class DoctrineDatabaseTest extends TestCase -{ - public const EXISTING_NOTIFICATION_ID = 1; - public const EXISTING_NOTIFICATION_DATA = [ - 'id' => 1, - 'owner_id' => 14, - 'is_pending' => 1, - 'type' => 'Workflow:Review', - 'created' => 1529995052, - 'data' => null, - ]; - - protected function setUp(): void - { - parent::setUp(); - - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/notifications.php' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase::insert() - */ - public function testInsert() - { - $id = $this->getGateway()->insert(new CreateStruct([ - 'ownerId' => 14, - 'isPending' => true, - 'type' => 'Workflow:Review', - 'created' => 1529995052, - 'data' => null, - ])); - - $data = $this->loadNotification($id); - - $this->assertEquals([ - 'id' => $id, - 'owner_id' => '14', - 'is_pending' => 1, - 'type' => 'Workflow:Review', - 'created' => '1529995052', - 'data' => 'null', - ], $data); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase::getNotificationById() - */ - public function testGetNotificationById() - { - $data = $this->getGateway()->getNotificationById(self::EXISTING_NOTIFICATION_ID); - - $this->assertEquals([ - self::EXISTING_NOTIFICATION_DATA, - ], $data); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase::updateNotification() - */ - public function testUpdateNotification() - { - $notification = new Notification([ - 'id' => self::EXISTING_NOTIFICATION_ID, - 'ownerId' => 14, - 'isPending' => false, - 'type' => 'Workflow:Review', - 'created' => 1529995052, - 'data' => null, - ]); - - $this->getGateway()->updateNotification($notification); - - $this->assertEquals([ - 'id' => (string) self::EXISTING_NOTIFICATION_ID, - 'owner_id' => '14', - 'is_pending' => '0', - 'type' => 'Workflow:Review', - 'created' => '1529995052', - 'data' => null, - ], $this->loadNotification(self::EXISTING_NOTIFICATION_ID)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase::countUserNotifications() - */ - public function testCountUserNotifications() - { - $this->assertEquals(5, $this->getGateway()->countUserNotifications( - self::EXISTING_NOTIFICATION_DATA['owner_id'] - )); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase::countUserPendingNotifications() - */ - public function testCountUserPendingNotifications() - { - $this->assertEquals( - 3, - $this->getGateway()->countUserPendingNotifications( - self::EXISTING_NOTIFICATION_DATA['owner_id'] - ) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase::loadUserNotifications() - */ - public function testLoadUserNotifications() - { - $userId = 14; - $offset = 1; - $limit = 3; - - $results = $this->getGateway()->loadUserNotifications($userId, $offset, $limit); - - $this->assertEquals([ - [ - 'id' => '4', - 'owner_id' => '14', - 'is_pending' => 1, - 'type' => 'Workflow:Review', - 'created' => '1530005852', - 'data' => null, - ], - [ - 'id' => '3', - 'owner_id' => '14', - 'is_pending' => 0, - 'type' => 'Workflow:Reject', - 'created' => '1530002252', - 'data' => null, - ], - [ - 'id' => '2', - 'owner_id' => '14', - 'is_pending' => 0, - 'type' => 'Workflow:Approve', - 'created' => '1529998652', - 'data' => null, - ], - ], $results); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase::delete() - */ - public function testDelete() - { - $this->getGateway()->delete(self::EXISTING_NOTIFICATION_ID); - - $this->assertEmpty($this->loadNotification(self::EXISTING_NOTIFICATION_ID)); - } - - /** - * Return a ready to test DoctrineStorage gateway. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase - */ - protected function getGateway(): DoctrineDatabase - { - return new DoctrineDatabase( - $this->getDatabaseConnection() - ); - } - - private function loadNotification(int $id): array - { - $data = $this->connection - ->executeQuery('SELECT * FROM eznotification WHERE id = :id', ['id' => $id]) - ->fetch(FetchMode::ASSOCIATIVE); - - return is_array($data) ? $data : []; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Notification/HandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Notification/HandlerTest.php deleted file mode 100644 index 0f61f1f964..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Notification/HandlerTest.php +++ /dev/null @@ -1,226 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Notification; - -use eZ\Publish\API\Repository\Values\Notification\Notification as APINotification; -use eZ\Publish\Core\Persistence\Legacy\Notification\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Notification\Handler; -use eZ\Publish\Core\Persistence\Legacy\Notification\Mapper; -use eZ\Publish\SPI\Persistence\Notification\CreateStruct; -use eZ\Publish\SPI\Persistence\Notification\Notification; -use eZ\Publish\SPI\Persistence\Notification\UpdateStruct; -use PHPUnit\Framework\TestCase; - -class HandlerTest extends TestCase -{ - public const NOTIFICATION_ID = 1; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Notification\Gateway|\PHPUnit\Framework\MockObject\MockObject */ - private $gateway; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Notification\Mapper|\PHPUnit\Framework\MockObject\MockObject */ - private $mapper; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Notification\Handler */ - private $handler; - - protected function setUp(): void - { - $this->gateway = $this->createMock(Gateway::class); - $this->mapper = $this->createMock(Mapper::class); - $this->handler = new Handler($this->gateway, $this->mapper); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Handler::createNotification - */ - public function testCreateNotification() - { - $createStruct = new CreateStruct([ - 'ownerId' => 5, - 'type' => 'TEST', - 'isPending' => true, - 'data' => [], - 'created' => 0, - ]); - - $this->gateway - ->expects($this->once()) - ->method('insert') - ->with($createStruct) - ->willReturn(self::NOTIFICATION_ID); - - $this->mapper - ->expects($this->once()) - ->method('extractNotificationsFromRows') - ->willReturn([new Notification([ - 'id' => self::NOTIFICATION_ID, - ])]); - - $notification = $this->handler->createNotification($createStruct); - - $this->assertEquals($notification->id, self::NOTIFICATION_ID); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Handler::countPendingNotifications - */ - public function testCountPendingNotifications() - { - $ownerId = 10; - $expectedCount = 12; - - $this->gateway - ->expects($this->once()) - ->method('countUserPendingNotifications') - ->with($ownerId) - ->willReturn($expectedCount); - - $this->assertEquals($expectedCount, $this->handler->countPendingNotifications($ownerId)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Handler::getNotificationById - */ - public function testGetNotificationById() - { - $rows = [ - [ - 'id' => 1, /* ... */ - ], - ]; - - $object = new Notification([ - 'id' => 1, /* ... */ - ]); - - $this->gateway - ->expects($this->once()) - ->method('getNotificationById') - ->with(self::NOTIFICATION_ID) - ->willReturn($rows); - - $this->mapper - ->expects($this->once()) - ->method('extractNotificationsFromRows') - ->with($rows) - ->willReturn([$object]); - - $this->assertEquals($object, $this->handler->getNotificationById(self::NOTIFICATION_ID)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Handler::updateNotification - */ - public function testUpdateNotification() - { - $updateStruct = new UpdateStruct([ - 'isPending' => false, - ]); - - $data = [ - 'id' => self::NOTIFICATION_ID, - 'ownerId' => null, - 'isPending' => true, - 'type' => null, - 'created' => null, - 'data' => [], - ]; - - $apiNotification = new APINotification($data); - $spiNotification = new Notification($data); - - $this->mapper - ->expects($this->once()) - ->method('createNotificationFromUpdateStruct') - ->with($updateStruct) - ->willReturn($spiNotification); - - $this->gateway - ->expects($this->once()) - ->method('updateNotification') - ->with($spiNotification); - - $this->mapper - ->expects($this->once()) - ->method('extractNotificationsFromRows') - ->willReturn([new Notification([ - 'id' => self::NOTIFICATION_ID, - ])]); - - $this->handler->updateNotification($apiNotification, $updateStruct); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Handler::countNotifications - */ - public function testCountNotifications() - { - $ownerId = 10; - $expectedCount = 12; - - $this->gateway - ->expects($this->once()) - ->method('countUserNotifications') - ->with($ownerId) - ->willReturn($expectedCount); - - $this->assertEquals($expectedCount, $this->handler->countNotifications($ownerId)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Handler::loadUserNotifications - */ - public function testLoadUserNotifications() - { - $ownerId = 9; - $limit = 5; - $offset = 0; - - $rows = [ - ['id' => 1/* ... */], - ['id' => 2/* ... */], - ['id' => 3/* ... */], - ]; - - $objects = [ - new Notification(['id' => 1/* ... */]), - new Notification(['id' => 2/* ... */]), - new Notification(['id' => 3/* ... */]), - ]; - - $this->gateway - ->expects($this->once()) - ->method('loadUserNotifications') - ->with($ownerId, $offset, $limit) - ->willReturn($rows); - - $this->mapper - ->expects($this->once()) - ->method('extractNotificationsFromRows') - ->with($rows) - ->willReturn($objects); - - $this->assertEquals($objects, $this->handler->loadUserNotifications($ownerId, $offset, $limit)); - } - - public function testDelete() - { - $notification = new APINotification([ - 'id' => self::NOTIFICATION_ID, /* ... */ - ]); - - $this->gateway - ->expects($this->once()) - ->method('delete') - ->with($notification->id); - - $this->handler->delete($notification); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Notification/MapperTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Notification/MapperTest.php deleted file mode 100644 index 8d905cd6e8..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Notification/MapperTest.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Notification; - -use eZ\Publish\Core\Persistence\Legacy\Notification\Mapper; -use eZ\Publish\SPI\Persistence\Notification\Notification; -use eZ\Publish\SPI\Persistence\Notification\UpdateStruct; -use PHPUnit\Framework\TestCase; - -class MapperTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Notification\Mapper */ - private $mapper; - - protected function setUp(): void - { - $this->mapper = new Mapper(); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Mapper::extractNotificationsFromRows - */ - public function testExtractNotificationsFromRows() - { - $rows = [ - [ - 'id' => 1, - 'owner_id' => 5, - 'type' => 'FOO', - 'created' => 1529913161, - 'is_pending' => 0, - 'data' => null, - ], - [ - 'id' => 1, - 'owner_id' => 5, - 'type' => 'BAR', - 'created' => 1529910161, - 'is_pending' => 1, - 'data' => json_encode([ - 'foo' => 'Foo', - 'bar' => 'Bar', - 'baz' => ['B', 'A', 'Z'], - ]), - ], - ]; - - $objects = [ - new Notification([ - 'id' => 1, - 'ownerId' => 5, - 'type' => 'FOO', - 'created' => 1529913161, - 'isPending' => false, - 'data' => [], - ]), - new Notification([ - 'id' => 1, - 'ownerId' => 5, - 'type' => 'BAR', - 'created' => 1529910161, - 'isPending' => true, - 'data' => [ - 'foo' => 'Foo', - 'bar' => 'Bar', - 'baz' => ['B', 'A', 'Z'], - ], - ]), - ]; - - $this->assertEquals($objects, $this->mapper->extractNotificationsFromRows($rows)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Mapper::extractNotificationsFromRows - */ - public function testExtractNotificationsFromRowsThrowsRuntimeException() - { - $this->expectException(\RuntimeException::class); - - $rows = [ - [ - 'id' => 1, - 'owner_id' => 5, - 'type' => 'FOO', - 'created' => 1529913161, - 'is_pending' => false, - 'data' => '{ InvalidJSON }', - ], - ]; - - $this->mapper->extractNotificationsFromRows($rows); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Notification\Mapper::createNotificationFromUpdateStruct - */ - public function testCreateNotificationFromUpdateStruct() - { - $updateStruct = new UpdateStruct([ - 'isPending' => false, - ]); - - $this->assertEquals(new Notification([ - 'isPending' => false, - ]), $this->mapper->createNotificationFromUpdateStruct($updateStruct)); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Setting/SettingHandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/Setting/SettingHandlerTest.php deleted file mode 100644 index 9b30bf4532..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Setting/SettingHandlerTest.php +++ /dev/null @@ -1,201 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Setting; - -use eZ\Publish\Core\Persistence\Legacy\Setting\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Setting\Handler; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Setting\Setting; -use PHPUnit\Framework\MockObject\MockObject; - -class SettingHandlerTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Setting\Handler */ - protected $settingHandler; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Setting\Gateway */ - protected $gatewayMock; - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Setting\Handler::create - */ - public function testCreate() - { - $handler = $this->getSettingHandler(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('insertSetting') - ->with( - $this->equalTo('group_a1'), - $this->equalTo('identifier_b2'), - $this->equalTo('value_c3') - )->will($this->returnValue(123)); - - $gatewayMock->expects($this->once()) - ->method('loadSettingById') - ->with( - $this->equalTo(123) - ) - ->will($this->returnValue([ - 'group' => 'group_a1', - 'identifier' => 'identifier_b2', - 'value' => 'value_c3', - ])); - - $settingRef = new Setting([ - 'group' => 'group_a1', - 'identifier' => 'identifier_b2', - 'serializedValue' => 'value_c3', - ]); - - $result = $handler->create( - 'group_a1', - 'identifier_b2', - 'value_c3', - ); - - $this->assertEquals( - $settingRef, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Setting\Handler::update - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - */ - public function testUpdate() - { - $handler = $this->getSettingHandler(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('updateSetting') - ->with( - $this->equalTo('group_d1'), - $this->equalTo('identifier_e2'), - $this->equalTo('value_f3') - ); - - $gatewayMock->expects($this->once()) - ->method('loadSetting') - ->with( - $this->equalTo('group_d1'), - $this->equalTo('identifier_e2') - ) - ->will($this->returnValue([ - 'group' => 'group_d1', - 'identifier' => 'identifier_e2', - 'value' => 'value_f3', - ])); - - $settingRef = new Setting([ - 'group' => 'group_d1', - 'identifier' => 'identifier_e2', - 'serializedValue' => 'value_f3', - ]); - - $result = $handler->update( - 'group_d1', - 'identifier_e2', - 'value_f3' - ); - - $this->assertEquals( - $settingRef, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Setting\Handler::load - * @covers \eZ\Publish\Core\Persistence\Legacy\Setting\Handler::createSettingFromArray - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - */ - public function testLoad() - { - $handler = $this->getSettingHandler(); - - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('loadSetting') - ->with( - $this->equalTo('group_a1'), - $this->equalTo('identifier_b2') - )->will( - $this->returnValue([ - 'group' => 'group_a1', - 'identifier' => 'identifier_b2', - 'value' => 'value_c3', - ]) - ); - - $settingRef = new Setting([ - 'group' => 'group_a1', - 'identifier' => 'identifier_b2', - 'serializedValue' => 'value_c3', - ]); - - $result = $handler->load( - 'group_a1', - 'identifier_b2' - ); - - $this->assertEquals( - $settingRef, - $result - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Setting\Handler::delete - */ - public function testDelete() - { - $handler = $this->getSettingHandler(); - $gatewayMock = $this->getGatewayMock(); - - $gatewayMock->expects($this->once()) - ->method('deleteSetting') - ->with( - $this->equalTo('group_a1'), - $this->equalTo('identifier_b2') - ); - - $handler->delete( - 'group_a1', - 'identifier_b2' - ); - } - - protected function getSettingHandler(): Handler - { - if (!isset($this->settingHandler)) { - $this->settingHandler = new Handler( - $this->getGatewayMock() - ); - } - - return $this->settingHandler; - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Setting\Gateway - */ - protected function getGatewayMock(): MockObject - { - if (!isset($this->gatewayMock)) { - $this->gatewayMock = $this->getMockForAbstractClass(Gateway::class); - } - - return $this->gatewayMock; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/TestCase.php b/eZ/Publish/Core/Persistence/Legacy/Tests/TestCase.php deleted file mode 100644 index 055796815a..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/TestCase.php +++ /dev/null @@ -1,351 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests; - -use Doctrine\Common\EventManager as DoctrineEventManager; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ConnectionException; -use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Tests\LegacySchemaImporter; -use eZ\Publish\Core\Persistence\Legacy\SharedGateway; -use eZ\Publish\Core\Persistence\Tests\DatabaseConnectionFactory; -use eZ\Publish\Core\Search\Legacy\Content; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter; -use eZ\Publish\SPI\Tests\Persistence\FileFixtureFactory; -use eZ\Publish\SPI\Tests\Persistence\FixtureImporter; -use eZ\Publish\SPI\Tests\Persistence\YamlFixture; -use EzSystems\DoctrineSchema\Database\DbPlatform\SqliteDbPlatform; -use InvalidArgumentException; -use PDOException; -use PHPUnit\Framework\TestCase as BaseTestCase; -use ReflectionObject; -use ReflectionProperty; - -/** - * Base test case for database related tests. - */ -abstract class TestCase extends BaseTestCase -{ - /** - * DSN used for the DB backend. - * - * @var string - */ - protected $dsn; - - /** - * Name of the DB, extracted from DSN. - * - * @var string - */ - protected $db; - - /** - * Doctrine Database connection -- to not be constructed twice for one test. - * - * @internal - * - * @var \Doctrine\DBAL\Connection - */ - protected $connection; - - /** @var \eZ\Publish\Core\Persistence\Legacy\SharedGateway\Gateway */ - private $sharedGateway; - - /** - * Get data source name. - * - * The database connection string is read from an optional environment - * variable "DATABASE" and defaults to an in-memory SQLite database. - * - * @return string - */ - protected function getDsn() - { - if (!$this->dsn) { - $this->dsn = getenv('DATABASE'); - if (!$this->dsn) { - $this->dsn = 'sqlite://:memory:'; - } - $this->db = preg_replace('(^([a-z]+).*)', '\\1', $this->dsn); - } - - return $this->dsn; - } - - /** - * Get native Doctrine database connection. - */ - final public function getDatabaseConnection(): Connection - { - if (!$this->connection) { - $eventManager = new DoctrineEventManager(); - $connectionFactory = new DatabaseConnectionFactory( - [new SqliteDbPlatform()], - $eventManager - ); - - try { - $this->connection = $connectionFactory->createConnection($this->getDsn()); - } catch (DBALException $e) { - self::fail('Connection failed: ' . $e->getMessage()); - } - } - - return $this->connection; - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - final public function getSharedGateway(): SharedGateway\Gateway - { - if (!$this->sharedGateway) { - $connection = $this->getDatabaseConnection(); - $factory = new SharedGateway\GatewayFactory( - new SharedGateway\DatabasePlatform\FallbackGateway($connection), - [ - 'sqlite' => new SharedGateway\DatabasePlatform\SqliteGateway(), - ] - ); - - $this->sharedGateway = $factory->buildSharedGateway($connection); - } - - return $this->sharedGateway; - } - - /** - * Resets the database on test setup, so we always operate on a clean - * database. - */ - protected function setUp(): void - { - try { - $schemaImporter = new LegacySchemaImporter($this->getDatabaseConnection()); - $schemaImporter->importSchema( - dirname(__DIR__, 5) . - '/Bundle/EzPublishCoreBundle/Resources/config/storage/legacy/schema.yaml' - ); - } catch (PDOException | ConnectionException $e) { - self::fail( - sprintf( - 'PDO session could not be created: %s: %s', - get_class($e), - $e->getMessage() - ) - ); - } - } - - protected function tearDown(): void - { - unset($this->connection); - } - - /** - * Get a text representation of a result set. - * - * @param array $result - * - * @return string - */ - protected static function getResultTextRepresentation(array $result) - { - return implode( - "\n", - array_map( - static function ($row) { - return implode(', ', $row); - }, - $result - ) - ); - } - - /** - * Insert a database fixture from the given file. - */ - protected function insertDatabaseFixture(string $file): void - { - try { - $fixtureImporter = new FixtureImporter($this->getDatabaseConnection()); - $fixtureImporter->import((new FileFixtureFactory())->buildFixture($file)); - } catch (DBALException $e) { - self::fail('Database fixture import failed: ' . $e->getMessage()); - } - } - - /** - * Insert test_data.yaml fixture, common for many test cases. - * - * See: eZ/Publish/API/Repository/Tests/_fixtures/Legacy/data/test_data.yaml - */ - protected function insertSharedDatabaseFixture(): void - { - try { - $fixtureImporter = new FixtureImporter($this->getDatabaseConnection()); - $fixtureImporter->import( - new YamlFixture( - __DIR__ . '/../../../../API/Repository/Tests/_fixtures/Legacy/data/test_data.yaml' - ) - ); - } catch (DBALException $e) { - self::fail('Database fixture import failed: ' . $e->getMessage()); - } - } - - /** - * Assert query result as correct. - * - * Builds text representations of the asserted and fetched query result, - * based on a QueryBuilder object. Compares them using classic diff for - * maximum readability of the differences between expectations and real - * results. - * - * The expectation MUST be passed as a two dimensional array containing - * rows of columns. - * - * @param array $expectation expected raw database rows - */ - public static function assertQueryResult( - array $expectation, - QueryBuilder $query, - string $message = '' - ): void { - $result = $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - - self::assertEquals( - self::getResultTextRepresentation($expectation), - self::getResultTextRepresentation($result), - $message - ); - } - - /** - * Asserts correct property values on $object. - * - * Asserts that for all keys in $properties a corresponding property - * exists in $object with the *same* value as in $properties. - * - * @param array $properties - * @param object $object - */ - protected function assertPropertiesCorrect(array $properties, $object) - { - if (!is_object($object)) { - throw new InvalidArgumentException( - 'Received ' . gettype($object) . ' instead of object as second parameter' - ); - } - foreach ($properties as $propName => $propVal) { - $this->assertSame( - $propVal, - $object->$propName, - "Incorrect value for \${$propName}" - ); - } - } - - /** - * Asserts $expStruct equals $actStruct in at least $propertyNames. - * - * Asserts that properties of $actStruct equal properties of $expStruct (not - * vice versa!). If $propertyNames is null, all properties are checked. - * Otherwise, $propertyNames provides a white list. - * - * @param object $expStruct - * @param object $actStruct - * @param array $propertyNames - */ - protected function assertStructsEqual( - $expStruct, - $actStruct, - array $propertyNames = null - ) { - if ($propertyNames === null) { - $propertyNames = $this->getPublicPropertyNames($expStruct); - } - foreach ($propertyNames as $propName) { - $this->assertEquals( - $expStruct->$propName, - $actStruct->$propName, - "Properties \${$propName} not same" - ); - } - } - - /** - * Returns public property names in $object. - * - * @param object $object - * - * @return array - */ - protected function getPublicPropertyNames($object) - { - $refl = new ReflectionObject($object); - - return array_map( - static function ($prop) { - return $prop->getName(); - }, - $refl->getProperties(ReflectionProperty::IS_PUBLIC) - ); - } - - /** - * @return string - */ - protected static function getInstallationDir() - { - static $installDir = null; - if ($installDir === null) { - $config = require 'config.php'; - $installDir = $config['install_dir']; - } - - return $installDir; - } - - protected function getTrashCriteriaConverterDependency(): CriteriaConverter - { - $connection = $this->getDatabaseConnection(); - - return new CriteriaConverter( - [ - new CriterionHandler\LogicalAnd($connection), - new CriterionHandler\SectionId($connection), - new CriterionHandler\ContentTypeId($connection), - new CriterionHandler\DateMetadata($connection), - new CriterionHandler\UserMetadata($connection), - ] - ); - } - - protected function getTrashSortClauseConverterDependency(): SortClauseConverter - { - $connection = $this->getDatabaseConnection(); - - return new SortClauseConverter( - [ - new Content\Common\Gateway\SortClauseHandler\SectionName($connection), - new Content\Common\Gateway\SortClauseHandler\ContentName($connection), - new Content\Common\Gateway\SortClauseHandler\Trash\ContentTypeName($connection), - new Content\Common\Gateway\SortClauseHandler\Trash\UserLogin($connection), - new Content\Common\Gateway\SortClauseHandler\Trash\DateTrashed($connection), - new Content\Location\Gateway\SortClauseHandler\Location\Path($connection), - new Content\Location\Gateway\SortClauseHandler\Location\Depth($connection), - new Content\Location\Gateway\SortClauseHandler\Location\Priority($connection), - ] - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/TransactionHandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/TransactionHandlerTest.php deleted file mode 100644 index 4af61e3632..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/TransactionHandlerTest.php +++ /dev/null @@ -1,196 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests; - -use Doctrine\DBAL\Connection; -use Exception; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler; -use eZ\Publish\Core\Persistence\Legacy\TransactionHandler; - -/** - * Test case for TransactionHandler. - */ -class TransactionHandlerTest extends \PHPUnit\Framework\TestCase -{ - /** - * Transaction handler to test. - * - * @var \eZ\Publish\Core\Persistence\Legacy\TransactionHandler - */ - protected $transactionHandler; - - /** @var \Doctrine\DBAL\Connection|\PHPUnit\Framework\MockObject\MockObject */ - protected $connectionMock; - - /** @var \eZ\Publish\SPI\Persistence\Content\Type\Handler|\PHPUnit\Framework\MockObject\MockObject */ - protected $contentTypeHandlerMock; - - /** @var \eZ\Publish\SPI\Persistence\Content\Language\Handler|\PHPUnit\Framework\MockObject\MockObject */ - protected $languageHandlerMock; - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\TransactionHandler::beginTransaction - */ - public function testBeginTransaction() - { - $handler = $this->getTransactionHandler(); - $this->getConnectionMock() - ->expects($this->once()) - ->method('beginTransaction'); - $this->getContentTypeHandlerMock() - ->expects($this->never()) - ->method($this->anything()); - $this->getLanguageHandlerMock() - ->expects($this->never()) - ->method($this->anything()); - - $handler->beginTransaction(); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\TransactionHandler::commit - */ - public function testCommit() - { - $handler = $this->getTransactionHandler(); - $this->getConnectionMock() - ->expects($this->once()) - ->method('commit'); - $this->getContentTypeHandlerMock() - ->expects($this->never()) - ->method($this->anything()); - $this->getLanguageHandlerMock() - ->expects($this->never()) - ->method($this->anything()); - - $handler->commit(); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\TransactionHandler::commit - */ - public function testCommitException() - { - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('test'); - - $handler = $this->getTransactionHandler(); - $this->getConnectionMock() - ->expects($this->once()) - ->method('commit') - ->will($this->throwException(new Exception('test'))); - $this->getContentTypeHandlerMock() - ->expects($this->never()) - ->method($this->anything()); - $this->getLanguageHandlerMock() - ->expects($this->never()) - ->method($this->anything()); - - $handler->commit(); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\TransactionHandler::rollback - */ - public function testRollback() - { - $handler = $this->getTransactionHandler(); - $this->getConnectionMock() - ->expects($this->once()) - ->method('rollback'); - $this->getContentTypeHandlerMock() - ->expects($this->once()) - ->method('clearCache'); - $this->getLanguageHandlerMock() - ->expects($this->once()) - ->method('clearCache'); - - $handler->rollback(); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\TransactionHandler::rollback - */ - public function testRollbackException() - { - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('test'); - - $handler = $this->getTransactionHandler(); - $this->getConnectionMock() - ->expects($this->once()) - ->method('rollback') - ->will($this->throwException(new Exception('test'))); - $this->getContentTypeHandlerMock() - ->expects($this->never()) - ->method($this->anything()); - $this->getLanguageHandlerMock() - ->expects($this->never()) - ->method($this->anything()); - - $handler->rollback(); - } - - /** - * Returns a mock object for the Content Gateway. - * - * @return \eZ\Publish\Core\Persistence\Legacy\TransactionHandler - */ - protected function getTransactionHandler() - { - if (!isset($this->transactionHandler)) { - $this->transactionHandler = new TransactionHandler( - $this->getConnectionMock(), - $this->getContentTypeHandlerMock(), - $this->getLanguageHandlerMock() - ); - } - - return $this->transactionHandler; - } - - /** - * @return \Doctrine\DBAL\Connection|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getConnectionMock(): Connection - { - if (!isset($this->connectionMock)) { - $this->connectionMock = $this->createMock(Connection::class); - } - - return $this->connectionMock; - } - - /** - * Returns a mock object for the Content Type Handler. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getContentTypeHandlerMock() - { - if (!isset($this->contentTypeHandlerMock)) { - $this->contentTypeHandlerMock = $this->createMock(MemoryCachingHandler::class); - } - - return $this->contentTypeHandlerMock; - } - - /** - * Returns a mock object for the Content Language Gateway. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getLanguageHandlerMock() - { - if (!isset($this->languageHandlerMock)) { - $this->languageHandlerMock = $this->createMock(CachingHandler::class); - } - - return $this->languageHandlerMock; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index fa3b9c23a7..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,113 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\URL\Gateway; - -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\MatchAll; - -/** - * @covers \eZ\Publish\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase - */ -class DoctrineDatabaseTest extends TestCase -{ - /** - * Database gateway to test. - * - * @var \eZ\Publish\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase - */ - private $gateway; - - /** @var array[] */ - private $fixtureData; - - protected function setUp(): void - { - parent::setUp(); - - $fixtureLocation = __DIR__ . '/_fixtures/urls.php'; - $this->fixtureData = (require $fixtureLocation)['ezurl']; - $this->insertDatabaseFixture($fixtureLocation); - $this->initGateway(); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase::loadUrlData - */ - public function testLoadUrlData(): void - { - $row = $this->gateway->loadUrlData(23); - - self::assertEquals( - $this->fixtureData[0], - $row[0] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase::loadUrlDataByUrl - */ - public function testLoadUrlDataByUrl(): void - { - $rows = $this->gateway->loadUrlDataByUrl('https://doc.ez.no/display/USER/'); - - self::assertEquals( - $this->fixtureData[0], - $rows[0] - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase::find - */ - public function testFind(): void - { - $criterion = new \eZ\Publish\API\Repository\Values\URL\Query\Criterion\MatchAll(); - $results = $this->gateway->find($criterion, 0, 10); - - self::assertEquals( - [ - 'count' => count($this->fixtureData), - 'rows' => $this->fixtureData, - ], - $results - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase::find - */ - public function testFindWithDisabledCounting(): void - { - $criterion = new \eZ\Publish\API\Repository\Values\URL\Query\Criterion\MatchAll(); - $results = $this->gateway->find($criterion, 0, 10, [], false); - - self::assertEquals( - [ - 'count' => null, - 'rows' => $this->fixtureData, - ], - $results - ); - } - - /** - * Return the DoctrineDatabase gateway to test. - */ - protected function initGateway(): DoctrineDatabase - { - if (!isset($this->gateway)) { - $criteriaConverter = new CriteriaConverter([new MatchAll()]); - $this->gateway = new DoctrineDatabase($this->getDatabaseConnection(), $criteriaConverter); - } - - return $this->gateway; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Gateway/_fixtures/urls.php b/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Gateway/_fixtures/urls.php deleted file mode 100644 index fd6a956c8e..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Gateway/_fixtures/urls.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -return [ - 'ezurl' => [ - [ - 'id' => 23, - 'created' => 1448832197, - 'is_valid' => 1, - 'last_checked' => 0, - 'modified' => 1448832197, - 'original_url_md5' => 'f76e41d421b2a72232264943026a6ee5', - 'url' => 'https://doc.ez.no/display/USER/', - ], - [ - 'id' => 24, - 'created' => 1448832277, - 'is_valid' => 1, - 'last_checked' => 0, - 'modified' => 1505717756, - 'original_url_md5' => 'a00ab36edb35bb641cc027eb27410934', - 'url' => 'https://doc.ezplatform.com/en/latest/', - ], - [ - 'id' => 25, - 'created' => 1448832412, - 'is_valid' => 1, - 'last_checked' => 0, - 'modified' => 1505717756, - 'original_url_md5' => '03c4188f5fdcb679192e25a7dad09c2d', - 'url' => 'https://doc.ezplatform.com/en/latest/tutorials/platform_beginner/building_a_bicycle_route_tracker_in_ez_platform/', - ], - ], -]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/HandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/URL/HandlerTest.php deleted file mode 100644 index 1f69288af8..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/HandlerTest.php +++ /dev/null @@ -1,201 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\URL; - -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\API\Repository\Values\URL\Query\SortClause; -use eZ\Publish\API\Repository\Values\URL\URLQuery; -use eZ\Publish\Core\Persistence\Legacy\URL\Gateway; -use eZ\Publish\Core\Persistence\Legacy\URL\Handler; -use eZ\Publish\Core\Persistence\Legacy\URL\Mapper; -use eZ\Publish\SPI\Persistence\URL\URL; -use eZ\Publish\SPI\Persistence\URL\URLUpdateStruct; -use PHPUnit\Framework\TestCase; - -class HandlerTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\URL\Gateway|\PHPUnit\Framework\MockObject\MockObject */ - private $gateway; - - /** @var \eZ\Publish\Core\Persistence\Legacy\URL\Mapper|\PHPUnit\Framework\MockObject\MockObject */ - private $mapper; - - /** @var \eZ\Publish\Core\Persistence\Legacy\URL\Handler */ - private $handler; - - protected function setUp(): void - { - parent::setUp(); - $this->gateway = $this->createMock(Gateway::class); - $this->mapper = $this->createMock(Mapper::class); - $this->handler = new Handler($this->gateway, $this->mapper); - } - - public function testUpdateUrl() - { - $urlUpdateStruct = new URLUpdateStruct(); - $url = $this->getUrl(1, 'http://ez.no'); - - $this->mapper - ->expects($this->once()) - ->method('createURLFromUpdateStruct') - ->with($urlUpdateStruct) - ->willReturn($url); - - $this->gateway - ->expects($this->once()) - ->method('updateUrl') - ->with($url); - - $this->assertEquals($url, $this->handler->updateUrl($url->id, $urlUpdateStruct)); - } - - public function testFind() - { - $query = new URLQuery(); - $query->filter = new Criterion\Validity(true); - $query->sortClauses = [ - new SortClause\Id(), - ]; - $query->offset = 2; - $query->limit = 10; - - $results = [ - 'count' => 1, - 'rows' => [ - [ - 'id' => 1, - 'url' => 'http://ez.no', - ], - ], - ]; - - $expected = [ - 'count' => 1, - 'items' => [ - $this->getUrl(1, 'http://ez.no'), - ], - ]; - - $this->gateway - ->expects($this->once()) - ->method('find') - ->with($query->filter, $query->offset, $query->limit, $query->sortClauses, $query->performCount) - ->willReturn($results); - - $this->mapper - ->expects($this->once()) - ->method('extractURLsFromRows') - ->with($results['rows']) - ->willReturn($expected['items']); - - $this->assertEquals($expected, $this->handler->find($query)); - } - - public function testLoadByIdWithoutUrlData() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\NotFoundException::class); - - $id = 1; - - $this->gateway - ->expects($this->once()) - ->method('loadUrlData') - ->with($id) - ->willReturn([]); - - $this->mapper - ->expects($this->once()) - ->method('extractURLsFromRows') - ->with([]) - ->willReturn([]); - - $this->handler->loadById($id); - } - - public function testLoadByIdWithUrlData() - { - $url = $this->getUrl(1, 'http://ez.no'); - - $this->gateway - ->expects($this->once()) - ->method('loadUrlData') - ->with($url->id) - ->willReturn([$url]); - - $this->mapper - ->expects($this->once()) - ->method('extractURLsFromRows') - ->with([$url]) - ->willReturn([$url]); - - $this->assertEquals($url, $this->handler->loadById($url->id)); - } - - public function testLoadByUrlWithoutUrlData() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\NotFoundException::class); - - $url = 'http://ez.no'; - - $this->gateway - ->expects($this->once()) - ->method('loadUrlDataByUrl') - ->with($url) - ->willReturn([]); - - $this->mapper - ->expects($this->once()) - ->method('extractURLsFromRows') - ->with([]) - ->willReturn([]); - - $this->handler->loadByUrl($url); - } - - public function testLoadByUrlWithUrlData() - { - $url = $this->getUrl(1, 'http://ez.no'); - - $this->gateway - ->expects($this->once()) - ->method('loadUrlDataByUrl') - ->with($url->url) - ->willReturn([$url]); - - $this->mapper - ->expects($this->once()) - ->method('extractURLsFromRows') - ->with([$url]) - ->willReturn([$url]); - - $this->assertEquals($url, $this->handler->loadByUrl($url->url)); - } - - public function testFindUsages() - { - $url = $this->getUrl(); - $ids = [1, 2, 3]; - - $this->gateway - ->expects($this->once()) - ->method('findUsages') - ->with($url->id) - ->will($this->returnValue($ids)); - - $this->assertEquals($ids, $this->handler->findUsages($url->id)); - } - - private function getUrl($id = 1, $urlAddr = 'http://ez.no') - { - $url = new URL(); - $url->id = $id; - $url->url = $url; - - return $url; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/MapperTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/URL/MapperTest.php deleted file mode 100644 index f8f6a053ff..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/MapperTest.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\URL; - -use eZ\Publish\Core\Persistence\Legacy\URL\Mapper; -use eZ\Publish\SPI\Persistence\URL\URL; -use eZ\Publish\SPI\Persistence\URL\URLUpdateStruct; -use PHPUnit\Framework\TestCase; - -class MapperTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\URL\Mapper */ - private $mapper; - - protected function setUp(): void - { - parent::setUp(); - $this->mapper = new Mapper(); - } - - public function testCreateURLFromUpdateStruct() - { - $urlUpdateStruct = new URLUpdateStruct(); - $urlUpdateStruct->url = 'https://ez.no'; - $urlUpdateStruct->isValid = true; - $urlUpdateStruct->lastChecked = 0; - $urlUpdateStruct->modified = time(); - - $expected = new URL(); - $expected->url = $urlUpdateStruct->url; - $expected->originalUrlMd5 = md5($urlUpdateStruct->url); - $expected->isValid = $urlUpdateStruct->isValid; - $expected->lastChecked = $urlUpdateStruct->lastChecked; - $expected->created = 0; - $expected->modified = $urlUpdateStruct->modified; - - $this->assertEquals($expected, $this->mapper->createURLFromUpdateStruct($urlUpdateStruct)); - } - - public function testExtractURLsFromRows() - { - $rows = [ - [ - 'id' => 12, - 'url' => 'https://ez.no', - 'original_url_md5' => 'd74110041197e107722d8821f5f4d89c', - 'is_valid' => 0, - 'last_checked' => 0, - 'created' => 1510770207, - 'modified' => 0, - ], - [ - 'id' => 52, - 'url' => 'https://ezplatform.com', - 'original_url_md5' => '59697373afe0a059dc424ea2fc6946d5', - 'is_valid' => 1, - 'last_checked' => 0, - 'created' => 1510770293, - 'modified' => 0, - ], - ]; - - $urlEzNo = new URL(); - $urlEzNo->id = (int)$rows[0]['id']; - $urlEzNo->url = $rows[0]['url']; - $urlEzNo->originalUrlMd5 = $rows[0]['original_url_md5']; - $urlEzNo->isValid = (bool)$rows[0]['is_valid']; - $urlEzNo->lastChecked = (int)$rows[0]['last_checked']; - $urlEzNo->created = (int)$rows[0]['created']; - $urlEzNo->modified = (int)$rows[0]['modified']; - - $urlEzplatformCom = new URL(); - $urlEzplatformCom->id = (int)$rows[1]['id']; - $urlEzplatformCom->url = $rows[1]['url']; - $urlEzplatformCom->originalUrlMd5 = $rows[1]['original_url_md5']; - $urlEzplatformCom->isValid = (bool)$rows[1]['is_valid']; - $urlEzplatformCom->lastChecked = (int)$rows[1]['last_checked']; - $urlEzplatformCom->created = (int)$rows[1]['created']; - $urlEzplatformCom->modified = (int)$rows[1]['modified']; - - $this->assertEquals([$urlEzNo, $urlEzplatformCom], $this->mapper->extractURLsFromRows($rows)); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriteriaConverterTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriteriaConverterTest.php deleted file mode 100644 index 55b58331a7..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriteriaConverterTest.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; -use PHPUnit\Framework\TestCase; - -class CriteriaConverterTest extends TestCase -{ - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function testConvertCriteriaSuccess(): void - { - $fooCriterionHandler = $this->createMock(CriterionHandler::class); - $barCriterionHandler = $this->createMock(CriterionHandler::class); - - $criteriaConverter = new CriteriaConverter([ - $fooCriterionHandler, - $barCriterionHandler, - ]); - - $barCriterion = $this->createMock(Criterion::class); - - $selectQuery = $this->createMock(QueryBuilder::class); - - $fooCriterionHandler - ->expects($this->once()) - ->method('accept') - ->with($barCriterion) - ->willReturn(false); - - $fooCriterionHandler - ->expects($this->never()) - ->method('handle'); - - $barCriterionHandler - ->expects($this->once()) - ->method('accept') - ->with($barCriterion) - ->willReturn(true); - - $sqlExpression = 'SQL EXPRESSION'; - $barCriterionHandler - ->expects($this->once()) - ->method('handle') - ->with($criteriaConverter, $selectQuery, $barCriterion) - ->willReturn($sqlExpression); - - $this->assertEquals( - $sqlExpression, - $criteriaConverter->convertCriteria( - $selectQuery, - $barCriterion - ) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter::convertCriteria - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function testConvertCriteriaFailure(): void - { - $this->expectException(NotImplementedException::class); - - $criteriaConverter = new CriteriaConverter(); - $criteriaConverter->convertCriteria( - $this->createMock(QueryBuilder::class), - $this->createMock(Criterion::class) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/LogicalAndTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/LogicalAndTest.php deleted file mode 100644 index d718d0755d..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/LogicalAndTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler; - -use Doctrine\DBAL\Query\Expression\CompositeExpression; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion\LogicalAnd; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalAnd as LogicalAndHandler; - -class LogicalAndTest extends CriterionHandlerTest -{ - /** - * {@inheritdoc} - */ - public function testAccept() - { - $handler = new LogicalAndHandler(); - - $this->assertTrue($handler->accept($this->createMock(LogicalAnd::class))); - $this->assertFalse($handler->accept($this->createMock(Criterion::class))); - } - - /** - * {@inheritdoc} - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function testHandle(): void - { - $foo = $this->createMock(Criterion::class); - $bar = $this->createMock(Criterion::class); - - $fooExpr = 'FOO'; - $barExpr = 'BAR'; - - $expected = '(FOO) AND (BAR)'; - - $queryBuilder = $this->createMock(QueryBuilder::class); - $converter = $this->mockConverterForLogicalOperator( - CompositeExpression::TYPE_AND, - $queryBuilder, - 'andX', - $fooExpr, - $barExpr, - $foo, - $bar - ); - - $handler = new LogicalAndHandler(); - $actual = (string)$handler->handle( - $converter, - $queryBuilder, - new LogicalAnd([$foo, $bar]) - ); - - $this->assertEquals($expected, $actual); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/LogicalNotTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/LogicalNotTest.php deleted file mode 100644 index 45f45b11b4..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/LogicalNotTest.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion\LogicalNot; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalNot as LogicalNotHandler; - -class LogicalNotTest extends CriterionHandlerTest -{ - /** - * {@inheritdoc} - */ - public function testAccept() - { - $handler = new LogicalNotHandler(); - - $this->assertHandlerAcceptsCriterion($handler, LogicalNot::class); - $this->assertHandlerRejectsCriterion($handler, Criterion::class); - } - - /** - * {@inheritdoc} - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function testHandle(): void - { - $foo = $this->createMock(Criterion::class); - $fooExpr = 'FOO'; - $expected = 'NOT (FOO)'; - - $queryBuilder = $this->createMock(QueryBuilder::class); - - $converter = $this->createMock(CriteriaConverter::class); - $converter - ->expects($this->at(0)) - ->method('convertCriteria') - ->with($queryBuilder, $foo) - ->willReturn($fooExpr); - - $handler = new LogicalNotHandler(); - $actual = $handler->handle( - $converter, - $queryBuilder, - new LogicalNot($foo) - ); - - $this->assertEquals($expected, $actual); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/LogicalOrTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/LogicalOrTest.php deleted file mode 100644 index 6866e77247..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/LogicalOrTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler; - -use Doctrine\DBAL\Query\Expression\CompositeExpression; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion\LogicalOr; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalOr as LogicalOrHandler; - -class LogicalOrTest extends CriterionHandlerTest -{ - /** - * {@inheritdoc} - */ - public function testAccept() - { - $handler = new LogicalOrHandler(); - - $this->assertHandlerAcceptsCriterion($handler, LogicalOr::class); - $this->assertHandlerRejectsCriterion($handler, Criterion::class); - } - - /** - * {@inheritdoc} - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function testHandle(): void - { - $foo = $this->createMock(Criterion::class); - $bar = $this->createMock(Criterion::class); - - $fooExpr = 'FOO'; - $barExpr = 'BAR'; - - $expected = '(FOO) OR (BAR)'; - - $queryBuilder = $this->createMock(QueryBuilder::class); - $converter = $this->mockConverterForLogicalOperator( - CompositeExpression::TYPE_OR, - $queryBuilder, - 'orX', - $fooExpr, - $barExpr, - $foo, - $bar - ); - - $handler = new LogicalOrHandler(); - $actual = (string)$handler->handle( - $converter, - $queryBuilder, - new LogicalOr([$foo, $bar]) - ); - - $this->assertEquals($expected, $actual); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/MatchAllTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/MatchAllTest.php deleted file mode 100644 index 846c71bd45..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/MatchAllTest.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion\MatchAll; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\MatchAll as MatchAllHandler; - -class MatchAllTest extends CriterionHandlerTest -{ - /** - * {@inheritdoc} - */ - public function testAccept() - { - $handler = new MatchAllHandler(); - - $this->assertHandlerAcceptsCriterion($handler, MatchAll::class); - $this->assertHandlerRejectsCriterion($handler, Criterion::class); - } - - /** - * {@inheritdoc} - */ - public function testHandle() - { - $criterion = new MatchAll(); - $expected = '1 = 1'; - - $queryBuilder = $this->createMock(QueryBuilder::class); - $converter = $this->createMock(CriteriaConverter::class); - - $handler = new MatchAllHandler(); - $actual = $handler->handle($converter, $queryBuilder, $criterion); - - $this->assertEquals($expected, $actual); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/MatchNoneTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/MatchNoneTest.php deleted file mode 100644 index 00bd1a2f71..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/MatchNoneTest.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion\MatchNone; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\MatchNone as MatchNoneHandler; - -class MatchNoneTest extends CriterionHandlerTest -{ - /** - * {@inheritdoc} - */ - public function testAccept() - { - $handler = new MatchNoneHandler(); - - $this->assertHandlerAcceptsCriterion($handler, MatchNone::class); - $this->assertHandlerRejectsCriterion($handler, Criterion::class); - } - - /** - * {@inheritdoc} - */ - public function testHandle() - { - $criterion = new MatchNone(); - $expected = '1 = 0'; - - $query = $this->createMock(QueryBuilder::class); - $converter = $this->createMock(CriteriaConverter::class); - - $handler = new MatchNoneHandler(); - $actual = $handler->handle($converter, $query, $criterion); - - $this->assertEquals($expected, $actual); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/User/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/User/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index f607199380..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/User/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\User\Gateway; - -use Doctrine\DBAL\ParameterType; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\Core\Persistence\Legacy\User\Gateway\DoctrineDatabase; - -/** - * Test case for eZ\Publish\Core\Persistence\Legacy\User\Gateway\DoctrineDatabase. - */ -class DoctrineDatabaseTest extends TestCase -{ - /** - * Database gateway to test. - * - * @var \eZ\Publish\Core\Persistence\Legacy\User\Gateway\DoctrineDatabase - */ - protected $databaseGateway; - - /** - * Inserts DB fixture. - */ - protected function setUp(): void - { - parent::setUp(); - - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/roles.php' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\User\Gateway\DoctrineDatabase::removeRoleAssignmentById - */ - public function testRemoveRoleByAssignmentId(): void - { - $gateway = $this->getDatabaseGateway(); - - $gateway->removeRoleAssignmentById(38); - $query = $this->getDatabaseConnection()->createQueryBuilder(); - - $this->assertQueryResult( - [ - [ - 'contentobject_id' => '11', - 'id' => '34', - 'limit_identifier' => '', - 'limit_value' => '', - 'role_id' => '5', - ], - [ - 'contentobject_id' => '59', - 'id' => '36', - 'limit_identifier' => '', - 'limit_value' => '', - 'role_id' => '5', - ], - [ - 'contentobject_id' => '13', - 'id' => '39', - 'limit_identifier' => 'Section', - 'limit_value' => '2', - 'role_id' => '5', - ], - ], - $query - ->select('contentobject_id', 'id', 'limit_identifier', 'limit_value', 'role_id') - ->from('ezuser_role') - ->where( - $query->expr()->eq( - 'role_id', - $query->createPositionalParameter(5, ParameterType::INTEGER) - ) - ) - ); - } - - /** - * Returns a ready to test DoctrineDatabase gateway. - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function getDatabaseGateway(): DoctrineDatabase - { - if (!isset($this->databaseGateway)) { - $this->databaseGateway = new DoctrineDatabase( - $this->getDatabaseConnection() - ); - } - - return $this->databaseGateway; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/User/Role/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/User/Role/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index bb49af783d..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/User/Role/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,220 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\User\Role\Gateway; - -use Doctrine\DBAL\ParameterType; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase; -use eZ\Publish\SPI\Persistence\User\Role; - -/** - * Test case for eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase. - */ -class DoctrineDatabaseTest extends TestCase -{ - /** - * Database gateway to test. - * - * @var \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase - */ - protected $databaseGateway; - - /** - * Inserts DB fixture. - * - * @throws \Exception - */ - protected function setUp(): void - { - parent::setUp(); - - $this->insertDatabaseFixture( - __DIR__ . '/../../_fixtures/roles.php' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase::createRole - * - * @throws \Doctrine\DBAL\DBALException - */ - public function testCreateRole(): void - { - $gateway = $this->getDatabaseGateway(); - - $spiRole = new Role([ - 'identifier' => 'new_role', - 'status' => Role::STATUS_DRAFT, - ]); - $gateway->createRole($spiRole); - $query = $this->getDatabaseConnection()->createQueryBuilder(); - - $this->assertQueryResult( - [ - [ - 'id' => '6', - 'name' => 'new_role', - 'version' => -1, - ], - ], - $query - ->select('id', 'name', 'version') - ->from('ezrole') - ->where( - $query->expr()->eq( - 'name', - $query->createPositionalParameter('new_role', ParameterType::STRING) - ) - ) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase::loadRoleAssignment - * - * @throws \Doctrine\DBAL\DBALException - */ - public function testLoadRoleAssignment(): void - { - $gateway = $this->getDatabaseGateway(); - - $this->assertEquals( - [ - [ - 'contentobject_id' => '12', - 'id' => '25', - 'limit_identifier' => '', - 'limit_value' => '', - 'role_id' => '2', - ], - ], - $gateway->loadRoleAssignment(25) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase::loadRoleAssignmentsByGroupId - * - * @throws \Doctrine\DBAL\DBALException - */ - public function testLoadRoleAssignmentsByGroupId(): void - { - $gateway = $this->getDatabaseGateway(); - - $this->assertEquals( - [ - [ - 'contentobject_id' => '11', - 'id' => '28', - 'limit_identifier' => '', - 'limit_value' => '', - 'role_id' => '1', - ], - [ - 'contentobject_id' => '11', - 'id' => '34', - 'limit_identifier' => '', - 'limit_value' => '', - 'role_id' => '5', - ], - [ - 'contentobject_id' => '11', - 'id' => '40', - 'limit_identifier' => 'Section', - 'limit_value' => '3', - 'role_id' => '4', - ], - ], - $gateway->loadRoleAssignmentsByGroupId(11) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase::loadRoleAssignmentsByRoleId - * - * @throws \Doctrine\DBAL\DBALException - */ - public function testLoadRoleAssignmentsByRoleId(): void - { - $gateway = $this->getDatabaseGateway(); - - $this->assertEquals( - [ - [ - 'contentobject_id' => '11', - 'id' => '28', - 'limit_identifier' => '', - 'limit_value' => '', - 'role_id' => '1', - ], - [ - 'contentobject_id' => '42', - 'id' => '31', - 'limit_identifier' => '', - 'limit_value' => '', - 'role_id' => '1', - ], - [ - 'contentobject_id' => '59', - 'id' => '37', - 'limit_identifier' => '', - 'limit_value' => '', - 'role_id' => '1', - ], - ], - $gateway->loadRoleAssignmentsByRoleId(1) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase::loadRoleAssignmentsByRoleIdWithOffsetAndLimit - */ - public function testLoadRoleAssignmentsByRoleIdWithOffsetAndLimit(): void - { - $gateway = $this->getDatabaseGateway(); - - self::assertEquals( - [ - [ - 'contentobject_id' => '11', - 'id' => '28', - 'limit_identifier' => '', - 'limit_value' => '', - 'role_id' => '1', - ], - [ - 'contentobject_id' => '42', - 'id' => '31', - 'limit_identifier' => '', - 'limit_value' => '', - 'role_id' => '1', - ], - ], - $gateway->loadRoleAssignmentsByRoleIdWithOffsetAndLimit(1, 0, 2) - ); - } - - /** - * Returns a ready to test DoctrineDatabase gateway. - * - * @return \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function getDatabaseGateway(): DoctrineDatabase - { - if (!isset($this->databaseGateway)) { - $this->databaseGateway = new DoctrineDatabase( - $this->getDatabaseConnection() - ); - } - - return $this->databaseGateway; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/User/UserHandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/User/UserHandlerTest.php deleted file mode 100644 index d57a68af75..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/User/UserHandlerTest.php +++ /dev/null @@ -1,1220 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\User; - -use DateInterval; -use DateTime; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\User\Role as APIRole; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\Core\Persistence\Legacy\User; -use eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter; -use eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationHandler\ObjectStateHandler as ObjectStateLimitationHandler; -use eZ\Publish\SPI\Persistence; -use eZ\Publish\SPI\Persistence\User\Handler; -use eZ\Publish\SPI\Persistence\User\Role; -use LogicException; - -/** - * Test case for UserHandlerTest. - */ -class UserHandlerTest extends TestCase -{ - private const TEST_USER_ID = 42; - - /** - * @throws \Doctrine\DBAL\DBALException - */ - protected function getUserHandler(User\Gateway $userGateway = null): Handler - { - $connection = $this->getDatabaseConnection(); - - return new User\Handler( - $userGateway ?? new User\Gateway\DoctrineDatabase($connection), - new User\Role\Gateway\DoctrineDatabase($connection), - new User\Mapper(), - new LimitationConverter([new ObjectStateLimitationHandler($connection)]) - ); - } - - protected function getValidUser() - { - $user = new Persistence\User(); - $user->id = self::TEST_USER_ID; - $user->login = 'kore'; - $user->email = 'kore@example.org'; - $user->passwordHash = '1234567890'; - $user->hashAlgorithm = 2; - $user->isEnabled = true; - $user->maxLogin = 23; - $user->passwordUpdatedAt = 1569229200; - - return $user; - } - - protected function getValidUserToken($time = null) - { - $userToken = new Persistence\User\UserTokenUpdateStruct(); - $userToken->userId = self::TEST_USER_ID; - $userToken->hashKey = md5('hash'); - $userToken->time = $time ?? (new DateTime())->add(new DateInterval('P1D'))->getTimestamp(); - - return $userToken; - } - - public function testCreateUser() - { - $handler = $this->getUserHandler(); - - $this->expectException(NotImplementedException::class); - $handler->create($this->getValidUser()); - } - - protected function getGatewayReturnValue(): array - { - return [ - $this->getDummyUser( - self::TEST_USER_ID, - 'kore', - 'kore@example.org' - ), - ]; - } - - protected function getDummyUser( - int $id, - string $login, - string $email - ): array { - return [ - 'contentobject_id' => $id, - 'login' => $login, - 'email' => $email, - 'password_hash' => '1234567890', - 'password_hash_type' => 2, - 'is_enabled' => true, - 'max_login' => 23, - 'password_updated_at' => 1569229200, - ]; - } - - public function testLoadUser() - { - $gatewayMock = $this - ->createMock(User\Gateway::class); - - $gatewayMock - ->method('load') - ->with(self::TEST_USER_ID) - ->willReturn($this->getGatewayReturnValue()); - - $handler = $this->getUserHandler($gatewayMock); - - $user = $this->getValidUser(); - - $this->assertEquals( - $user, - $handler->load($user->id) - ); - } - - public function testLoadUnknownUser() - { - $this->expectException(NotFoundException::class); - $gatewayMock = $this - ->createMock(User\Gateway::class); - - $gatewayMock - ->method('load') - ->with(1337) - ->willReturn([]); - - $handler = $this->getUserHandler($gatewayMock); - - $handler->load(1337); - } - - public function testLoadUserByLogin() - { - $gatewayMock = $this - ->createMock(User\Gateway::class); - - $gatewayMock - ->method('loadByLogin') - ->with('kore') - ->willReturn($this->getGatewayReturnValue()); - - $handler = $this->getUserHandler($gatewayMock); - $user = $this->getValidUser(); - - $loadedUser = $handler->loadByLogin($user->login); - $this->assertEquals( - $user, - $loadedUser - ); - } - - public function testLoadMultipleUsersByLogin() - { - $this->expectException(LogicException::class); - - $gatewayMock = $this - ->createMock(User\Gateway::class); - - $gatewayMock - ->method('loadByLogin') - ->with('kore') - ->willReturn([ - $this->getDummyUser(self::TEST_USER_ID, 'kore', 'kore@example.org'), - $this->getDummyUser(self::TEST_USER_ID + 1, 'kore', 'kore@example.org'), - ]); - - $handler = $this->getUserHandler($gatewayMock); - $user = $this->getValidUser(); - - $handler->loadByLogin($user->login); - } - - public function testLoadMultipleUsersByEmail() - { - $this->expectException(LogicException::class); - - $gatewayMock = $this - ->createMock(User\Gateway::class); - - $gatewayMock - ->method('loadByEmail') - ->with('kore@example.org') - ->willReturn([ - $this->getDummyUser(self::TEST_USER_ID, 'kore_a', 'kore@example.org'), - $this->getDummyUser(self::TEST_USER_ID + 1, 'kore_b', 'kore@example.org'), - ]); - - $handler = $this->getUserHandler($gatewayMock); - $user = $this->getValidUser(); - - $handler->loadByEmail($user->email); - } - - public function testLoadUserByEmailNotFound() - { - $this->expectException(NotFoundException::class); - - $handler = $this->getUserHandler(); - $user = $this->getValidUser(); - - $handler->loadByLogin($user->email); - } - - public function testLoadUserByEmail() - { - $gatewayMock = $this - ->createMock(User\Gateway::class); - - $gatewayMock - ->method('loadByEmail') - ->with('kore@example.org') - ->willReturn($this->getGatewayReturnValue()); - - $handler = $this->getUserHandler($gatewayMock); - $validUser = $this->getValidUser(); - - $user = $handler->loadByEmail($validUser->email); - $this->assertEquals( - $validUser, - $user - ); - } - - public function testLoadUsersByEmail() - { - $gatewayMock = $this - ->createMock(User\Gateway::class); - - $gatewayMock - ->method('loadByEmail') - ->with('kore@example.org') - ->willReturn($this->getGatewayReturnValue()); - - $handler = $this->getUserHandler($gatewayMock); - $user = $this->getValidUser(); - - $users = $handler->loadUsersByEmail($user->email); - $this->assertEquals( - $user, - $users[0] - ); - } - - public function testLoadUserByTokenNotFound() - { - $this->expectException(NotFoundException::class); - - $handler = $this->getUserHandler(); - $handler->updateUserToken($this->getValidUserToken()); - - $handler->loadUserByToken('asd'); - } - - public function testLoadUserByToken() - { - $gatewayMock = $this - ->createMock(User\Gateway::class); - - $userToken = $this->getValidUserToken(); - $gatewayMock - ->method('loadUserByToken') - ->with($userToken->hashKey) - ->willReturn($this->getGatewayReturnValue()); - - $handler = $this->getUserHandler($gatewayMock); - $user = $this->getValidUser(); - $handler->updateUserToken($userToken); - - $loadedUser = $handler->loadUserByToken($userToken->hashKey); - $this->assertEquals( - $user, - $loadedUser - ); - } - - public function testUpdateUserToken() - { - $handler = $this->getUserHandler(); - - $handler->updateUserToken($this->getValidUserToken(1234567890)); - - $this->assertQueryResult( - [['0800fc577294c34e0b28ad2839435945', 1, 1234567890, self::TEST_USER_ID]], - $this->getDatabaseConnection()->createQueryBuilder()->select( - ['hash_key', 'id', 'time', 'user_id'] - )->from('ezuser_accountkey'), - 'Expected user data to be updated.' - ); - - $handler->updateUserToken($this->getValidUserToken(2234567890)); - - $this->assertQueryResult( - [['0800fc577294c34e0b28ad2839435945', 1, 2234567890, self::TEST_USER_ID]], - $this->getDatabaseConnection()->createQueryBuilder()->select( - ['hash_key', 'id', 'time', 'user_id'] - )->from('ezuser_accountkey'), - 'Expected user token data to be updated.' - ); - } - - public function testExpireUserToken() - { - $handler = $this->getUserHandler(); - - $handler->updateUserToken($userToken = $this->getValidUserToken(1234567890)); - - $this->assertQueryResult( - [['0800fc577294c34e0b28ad2839435945', 1, 1234567890, self::TEST_USER_ID]], - $this->getDatabaseConnection()->createQueryBuilder()->select( - ['hash_key', 'id', 'time', 'user_id'] - )->from('ezuser_accountkey'), - 'Expected user data to be updated.' - ); - - $handler->expireUserToken($userToken->hashKey); - - $this->assertQueryResult( - [['0800fc577294c34e0b28ad2839435945', 1, 0, self::TEST_USER_ID]], - $this->getDatabaseConnection()->createQueryBuilder()->select( - ['hash_key', 'id', 'time', 'user_id'] - )->from('ezuser_accountkey'), - 'Expected user token to be expired.' - ); - } - - public function testDeleteNonExistingUser() - { - $handler = $this->getUserHandler(); - - $this->expectException(NotImplementedException::class); - $handler->delete(1337); - } - - public function testUpdateUser() - { - $handler = $this->getUserHandler(); - $user = $this->getValidUser(); - - $user->login = 'New_lögin'; - $this->expectException(NotImplementedException::class); - $handler->update($user); - } - - public function testUpdateUserSettings() - { - $handler = $this->getUserHandler(); - $user = $this->getValidUser(); - - $user->maxLogin = 42; - $this->expectException(NotImplementedException::class); - $handler->update($user); - } - - public function testCreateNewRoleWithoutPolicies() - { - $handler = $this->getUserHandler(); - - $createStruct = new Persistence\User\RoleCreateStruct(); - $createStruct->identifier = 'Test'; - - $handler->createRole($createStruct); - - $this->assertQueryResult( - [[1, 'Test', -1]], - $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'name', 'version')->from('ezrole'), - 'Expected a new role draft.' - ); - } - - public function testCreateRoleDraftWithoutPolicies() - { - $handler = $this->getUserHandler(); - - $createStruct = new Persistence\User\RoleCreateStruct(); - $createStruct->identifier = 'Test'; - - $roleDraft = $handler->createRole($createStruct); - $handler->publishRoleDraft($roleDraft->id); - - $handler->createRoleDraft($roleDraft->id); - - $publishedRoleId = 1; - $this->assertQueryResult( - [ - [$publishedRoleId, 'Test', APIRole::STATUS_DEFINED], - [2, 'Test', $publishedRoleId], - ], - $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'name', 'version')->from('ezrole'), - 'Expected a role and a role draft.' - ); - } - - public function testCreateNewRoleRoleId() - { - $handler = $this->getUserHandler(); - - $createStruct = new Persistence\User\RoleCreateStruct(); - $createStruct->identifier = 'Test'; - - $roleDraft = $handler->createRole($createStruct); - - $this->assertSame(1, $roleDraft->id); - } - - public function testLoadRole() - { - $handler = $this->getUserHandler(); - - $createStruct = new Persistence\User\RoleCreateStruct(); - $createStruct->identifier = 'Test'; - - $roleDraft = $handler->createRole($createStruct); - $handler->publishRoleDraft($roleDraft->id); - $role = $handler->loadRole($roleDraft->id); - - $this->assertEquals( - $roleDraft->id, - $role->id - ); - } - - public function testLoadRoleWithPolicies() - { - $handler = $this->getUserHandler(); - - $createStruct = new Persistence\User\RoleCreateStruct(); - $createStruct->identifier = 'Test'; - - $roleDraft = $handler->createRole($createStruct); - - $policy = new Persistence\User\Policy(); - $policy->module = 'foo'; - $policy->function = 'bar'; - - $handler->addPolicyByRoleDraft($roleDraft->id, $policy); - $handler->publishRoleDraft($roleDraft->id); - - $loaded = $handler->loadRole($roleDraft->id); - $this->assertEquals( - [ - new Persistence\User\Policy( - [ - 'id' => 1, - 'roleId' => 1, - 'module' => 'foo', - 'function' => 'bar', - 'limitations' => '*', - 'originalId' => null, - ] - ), - ], - $loaded->policies - ); - } - - public function testLoadRoleWithPoliciesAndGroups() - { - $handler = $this->getUserHandler(); - - $createStruct = new Persistence\User\RoleCreateStruct(); - $createStruct->identifier = 'Test'; - - $roleDraft = $handler->createRole($createStruct); - - $policy = new Persistence\User\Policy(); - $policy->module = 'foo'; - $policy->function = 'bar'; - - $handler->addPolicyByRoleDraft($roleDraft->id, $policy); - - $handler->assignRole(23, $roleDraft->id); - $handler->assignRole(42, $roleDraft->id); - - $handler->publishRoleDraft($roleDraft->id); - - $loaded = $handler->loadRole($roleDraft->id); - $this->assertEquals( - [ - new Persistence\User\Policy( - [ - 'id' => 1, - 'roleId' => 1, - 'module' => 'foo', - 'function' => 'bar', - 'limitations' => '*', - 'originalId' => null, - ] - ), - ], - $loaded->policies - ); - } - - public function testLoadRoleWithPolicyLimitations() - { - $handler = $this->getUserHandler(); - - $createStruct = new Persistence\User\RoleCreateStruct(); - $createStruct->identifier = 'Test'; - - $roleDraft = $handler->createRole($createStruct); - - $policy = new Persistence\User\Policy(); - $policy->module = 'foo'; - $policy->function = 'bar'; - $policy->limitations = [ - 'Subtree' => ['/1', '/1/2'], - 'Foo' => ['Bar'], - ]; - - $handler->addPolicyByRoleDraft($roleDraft->id, $policy); - $handler->publishRoleDraft($roleDraft->id); - - $loaded = $handler->loadRole($roleDraft->id); - $this->assertEquals( - [ - new Persistence\User\Policy( - [ - 'id' => 1, - 'roleId' => 1, - 'module' => 'foo', - 'function' => 'bar', - 'limitations' => [ - 'Subtree' => ['/1', '/1/2'], - 'Foo' => ['Bar'], - ], - 'originalId' => null, - ] - ), - ], - $loaded->policies - ); - } - - public function testLoadRoles() - { - $handler = $this->getUserHandler(); - - $this->assertEquals( - [], - $handler->loadRoles() - ); - - $role = $this->createTestRole($handler); - - $this->assertEquals( - [$role], - $handler->loadRoles() - ); - } - - public function testUpdateRole() - { - $handler = $this->getUserHandler(); - - $role = $this->createTestRole($handler); - - $update = new Persistence\User\RoleUpdateStruct(); - $update->id = $role->id; - $update->identifier = 'Changed'; - - $handler->updateRole($update); - - $this->assertQueryResult( - [[1, 'Changed']], - $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'name')->from('ezrole'), - 'Expected a changed role.' - ); - } - - public function testDeleteRole() - { - $this->insertSharedDatabaseFixture(); - $handler = $this->getUserHandler(); - - // 3 is the ID of Editor role - $handler->deleteRole(3); - - $this->assertQueryResult( - [], - $this->getDatabaseConnection()->createQueryBuilder()->select('id')->from('ezrole')->where('id = 3'), - 'Expected an empty set.' - ); - - $this->assertQueryResult( - [], - $this->getDatabaseConnection()->createQueryBuilder()->select('role_id')->from('ezpolicy')->where('role_id = 3'), - 'Expected an empty set.' - ); - - $this->assertQueryResult( - [], - $this->getDatabaseConnection()->createQueryBuilder()->select('role_id')->from('ezuser_role')->where('role_id = 3'), - 'Expected an empty set.' - ); - } - - public function testDeleteRoleDraft() - { - $this->insertSharedDatabaseFixture(); - $handler = $this->getUserHandler(); - - // 3 is the ID of Editor role - $roleDraft = $handler->createRoleDraft(3); - $handler->deleteRole($roleDraft->id, APIRole::STATUS_DRAFT); - - $this->assertQueryResult( - [['3', APIRole::STATUS_DEFINED]], - $this->getDatabaseConnection()->createQueryBuilder()->select('id, version')->from('ezrole')->where('id = 3'), - 'Expected a published role.' - ); - - $this->assertQueryResult( - [[implode("\n", array_fill(0, 28, '3, ' . APIRole::STATUS_DEFINED))]], - $this->getDatabaseConnection()->createQueryBuilder()->select('role_id, original_id')->from('ezpolicy')->where('role_id = 3'), - 'Expected 28 policies for the published role.' - ); - - $this->assertQueryResult( - [[3], [3]], - $this->getDatabaseConnection()->createQueryBuilder()->select('role_id')->from('ezuser_role')->where('role_id = 3'), - 'Expected that role assignments still exist.' - ); - } - - public function testAddPolicyToRoleLimitations() - { - $handler = $this->getUserHandler(); - - $role = $this->createTestRole($handler); - - $policy = new Persistence\User\Policy(); - $policy->module = 'foo'; - $policy->function = 'bar'; - - $handler->addPolicy($role->id, $policy); - - $this->assertQueryResult( - [[1, 'foo', 'bar', 1]], - $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'module_name', 'function_name', 'role_id')->from('ezpolicy'), - 'Expected a new policy.' - ); - } - - public function testAddPolicyPolicyId() - { - $handler = $this->getUserHandler(); - - $role = $this->createTestRole($handler); - - $policy = new Persistence\User\Policy(); - $policy->module = 'foo'; - $policy->function = 'bar'; - - $policy = $handler->addPolicy($role->id, $policy); - - $this->assertEquals(1, $policy->id); - } - - public function testAddPolicyLimitations() - { - $this->createTestRoleWithTestPolicy(); - - $this->assertQueryResult( - [ - [1, 'Subtree', 1], - [2, 'Foo', 1], - ], - $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'identifier', 'policy_id')->from('ezpolicy_limitation'), - 'Expected a new policy.' - ); - } - - public function testAddPolicyLimitationValues() - { - $this->createTestRoleWithTestPolicy(); - - $this->assertQueryResult( - [ - [1, '/1', 1], - [2, '/1/2', 1], - [3, 'Bar', 2], - ], - $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'value', 'limitation_id')->from('ezpolicy_limitation_value'), - 'Expected a new policy.' - ); - } - - protected function createRole() - { - $handler = $this->getUserHandler(); - - $policy1 = new Persistence\User\Policy(); - $policy1->module = 'foo'; - $policy1->function = 'bar'; - $policy1->limitations = [ - 'Subtree' => ['/1', '/1/2'], - 'Foo' => ['Bar'], - ]; - - $policy2 = new Persistence\User\Policy(); - $policy2->module = 'foo'; - $policy2->function = 'blubb'; - $policy2->limitations = [ - 'Foo' => ['Blubb'], - ]; - - $createStruct = new Persistence\User\RoleCreateStruct(); - $createStruct->identifier = 'Test'; - $createStruct->policies = [$policy1, $policy2]; - - return $handler->createRole($createStruct); - } - - public function testImplicitlyCreatePolicies() - { - $this->createRole(); - - $this->assertQueryResult( - [ - [1, 'foo', 'bar', 1], - [2, 'foo', 'blubb', 1], - ], - $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'module_name', 'function_name', 'role_id')->from('ezpolicy'), - 'Expected a new policy.' - ); - } - - public function testDeletePolicy() - { - $handler = $this->getUserHandler(); - - $roleDraft = $this->createRole(); - $handler->publishRoleDraft($roleDraft->id); - $handler->deletePolicy($roleDraft->policies[0]->id, $roleDraft->policies[0]->roleId); - - $this->assertQueryResult( - [ - [2, 'foo', 'blubb', 1], - ], - $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'module_name', 'function_name', 'role_id')->from('ezpolicy')->where('original_id = 0'), - 'Expected a new policy.' - ); - } - - public function testDeletePolicyLimitations() - { - $handler = $this->getUserHandler(); - - $roleDraft = $this->createRole(); - $handler->deletePolicy($roleDraft->policies[0]->id, $roleDraft->policies[0]->roleId); - - $this->assertQueryResult( - [[3, 'Foo', 2]], - $this->getDatabaseConnection()->createQueryBuilder()->select('*')->from('ezpolicy_limitation') - ); - } - - public function testDeletePolicyLimitationValues() - { - $handler = $this->getUserHandler(); - - $roleDraft = $this->createRole(); - $handler->deletePolicy($roleDraft->policies[0]->id, $roleDraft->policies[0]->roleId); - - $this->assertQueryResult( - [[4, 3, 'Blubb']], - $this->getDatabaseConnection()->createQueryBuilder()->select('*')->from('ezpolicy_limitation_value') - ); - } - - public function testUpdatePolicies() - { - $handler = $this->getUserHandler(); - - $roleDraft = $this->createRole(); - - $policy = $roleDraft->policies[0]; - $policy->limitations = [ - 'new' => ['something'], - ]; - - $handler->updatePolicy($policy); - - $this->assertQueryResult( - [ - [3, 'Foo', 2], - [4, 'new', 1], - ], - $this->getDatabaseConnection()->createQueryBuilder()->select('*')->from('ezpolicy_limitation') - ); - - $this->assertQueryResult( - [ - [4, 3, 'Blubb'], - [5, 4, 'something'], - ], - $this->getDatabaseConnection()->createQueryBuilder()->select('*')->from('ezpolicy_limitation_value') - ); - } - - public function testAddRoleToUser() - { - $handler = $this->getUserHandler(); - - $roleDraft = $this->createRole(); - $handler->publishRoleDraft($roleDraft->id); - $role = $handler->loadRole($roleDraft->id); - $user = $this->getValidUser(); - - $handler->assignRole($user->id, $role->id, []); - - $this->assertQueryResult( - [ - [1, self::TEST_USER_ID, 1, null, null], - ], - $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'contentobject_id', 'role_id', 'limit_identifier', 'limit_value')->from('ezuser_role'), - 'Expected a new user policy association.' - ); - } - - public function testAddRoleToUserWithLimitation() - { - $handler = $this->getUserHandler(); - - $roleDraft = $this->createRole(); - $handler->publishRoleDraft($roleDraft->id); - $role = $handler->loadRole($roleDraft->id); - $user = $this->getValidUser(); - - $handler->assignRole( - $user->id, - $role->id, - [ - 'Subtree' => ['/1'], - ] - ); - - $this->assertQueryResult( - [ - [1, self::TEST_USER_ID, 1, 'Subtree', '/1'], - ], - $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'contentobject_id', 'role_id', 'limit_identifier', 'limit_value')->from('ezuser_role'), - 'Expected a new user policy association.' - ); - } - - public function testAddRoleToUserWithComplexLimitation() - { - $handler = $this->getUserHandler(); - - $roleDraft = $this->createRole(); - $handler->publishRoleDraft($roleDraft->id); - $role = $handler->loadRole($roleDraft->id); - $user = $this->getValidUser(); - - $handler->assignRole( - $user->id, - $role->id, - [ - 'Subtree' => ['/1', '/1/2'], - 'Foo' => ['Bar'], - ] - ); - - $this->assertQueryResult( - [ - [1, self::TEST_USER_ID, 1, 'Subtree', '/1'], - [2, self::TEST_USER_ID, 1, 'Subtree', '/1/2'], - [3, self::TEST_USER_ID, 1, 'Foo', 'Bar'], - ], - $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'contentobject_id', 'role_id', 'limit_identifier', 'limit_value')->from('ezuser_role'), - 'Expected a new user policy association.' - ); - } - - public function testRemoveUserRoleAssociation() - { - $handler = $this->getUserHandler(); - - $roleDraft = $this->createRole(); - $handler->publishRoleDraft($roleDraft->id); - $role = $handler->loadRole($roleDraft->id); - $user = $this->getValidUser(); - - $handler->assignRole( - $user->id, - $role->id, - [ - 'Subtree' => ['/1', '/1/2'], - 'Foo' => ['Bar'], - ] - ); - - $handler->unassignRole($user->id, $role->id); - - $this->assertQueryResult( - [], - $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'contentobject_id', 'role_id', 'limit_identifier', 'limit_value')->from('ezuser_role'), - 'Expected no user policy associations.' - ); - } - - public function testLoadPoliciesForUser() - { - $this->insertSharedDatabaseFixture(); - $handler = $this->getUserHandler(); - - $policies = $handler->loadPoliciesByUserId(10); // Anonymous user - - // Verify, that we received an array of Policy objects - $this->assertTrue( - array_reduce( - array_map( - static function ($policy) { - return $policy instanceof Persistence\User\Policy; - }, - $policies - ), - static function ($a, $b) { - return $a && $b; - }, - true - ) - ); - $this->assertCount(8, $policies); - } - - public function testLoadRoleAssignmentsByGroupId() - { - $this->insertSharedDatabaseFixture(); - $handler = $this->getUserHandler(); - - $this->assertEquals( - [ - new Persistence\User\RoleAssignment( - [ - 'id' => 28, - 'roleId' => 1, - 'contentId' => 11, - ] - ), - new Persistence\User\RoleAssignment( - [ - 'id' => 34, - 'roleId' => 5, - 'contentId' => 11, - ] - ), - ], - $handler->loadRoleAssignmentsByGroupId(11)// 11: Members - ); - - $this->assertEquals( - [ - new Persistence\User\RoleAssignment( - [ - 'id' => 31, - 'roleId' => 1, - 'contentId' => 42, - ] - ), - ], - $handler->loadRoleAssignmentsByGroupId(42)// 42: Anonymous Users - ); - - $this->assertEquals( - [], - $handler->loadRoleAssignmentsByGroupId(10)// 10: Anonymous User - ); - } - - public function testLoadRoleAssignmentsByGroupIdInherited() - { - $this->insertSharedDatabaseFixture(); - $handler = $this->getUserHandler(); - - $this->assertEquals( - [ - new Persistence\User\RoleAssignment( - [ - 'id' => 31, - 'roleId' => 1, - 'contentId' => 42, - ] - ), - ], - $handler->loadRoleAssignmentsByGroupId(10, true)// 10: Anonymous User - ); - } - - public function testCountRoleAssignments(): void - { - $this->insertSharedDatabaseFixture(); - $handler = $this->getUserHandler(); - - self::assertEquals(3, $handler->countRoleAssignments(1)); - } - - public function testLoadComplexRoleAssignments() - { - $this->insertSharedDatabaseFixture(); - $handler = $this->getUserHandler(); - - $this->assertEquals( - [ - new Persistence\User\RoleAssignment( - [ - 'id' => 32, - 'roleId' => 3, - 'contentId' => 13, - 'limitationIdentifier' => 'Subtree', - 'values' => ['/1/2/'], - ] - ), - new Persistence\User\RoleAssignment( - [ - 'id' => 33, - 'roleId' => 3, - 'contentId' => 13, - 'limitationIdentifier' => 'Subtree', - 'values' => ['/1/43/'], - ] - ), - new Persistence\User\RoleAssignment( - [ - 'id' => 38, - 'roleId' => 5, - 'contentId' => 13, - ] - ), - ], - $handler->loadRoleAssignmentsByGroupId(13) - ); - - $this->assertEquals( - [ - new Persistence\User\RoleAssignment( - [ - 'id' => 32, - 'roleId' => 3, - 'contentId' => 13, - 'limitationIdentifier' => 'Subtree', - 'values' => ['/1/2/'], - ] - ), - new Persistence\User\RoleAssignment( - [ - 'id' => 33, - 'roleId' => 3, - 'contentId' => 13, - 'limitationIdentifier' => 'Subtree', - 'values' => ['/1/43/'], - ] - ), - new Persistence\User\RoleAssignment( - [ - 'id' => 38, - 'roleId' => 5, - 'contentId' => 13, - ] - ), - ], - $handler->loadRoleAssignmentsByGroupId(13, true) - ); - } - - public function testLoadRoleAssignmentsByRoleId(): void - { - $this->insertSharedDatabaseFixture(); - $handler = $this->getUserHandler(); - - self::assertEquals( - [ - new Persistence\User\RoleAssignment( - [ - 'id' => 28, - 'roleId' => 1, - 'contentId' => 11, - ] - ), - new Persistence\User\RoleAssignment( - [ - 'id' => 31, - 'roleId' => 1, - 'contentId' => 42, - ] - ), - new Persistence\User\RoleAssignment( - [ - 'id' => 37, - 'roleId' => 1, - 'contentId' => 59, - ] - ), - ], - $handler->loadRoleAssignmentsByRoleId(1) - ); - } - - public function testLoadRoleAssignmentsByRoleIdWithOffsetAndLimit(): void - { - $this->insertSharedDatabaseFixture(); - $handler = $this->getUserHandler(); - - self::assertEquals( - [ - new Persistence\User\RoleAssignment( - [ - 'id' => 28, - 'roleId' => 1, - 'contentId' => 11, - ] - ), - new Persistence\User\RoleAssignment( - [ - 'id' => 31, - 'roleId' => 1, - 'contentId' => 42, - ] - ), - ], - $handler->loadRoleAssignmentsByRoleIdWithOffsetAndLimit(1, 0, 2) - ); - } - - public function testLoadRoleDraftByRoleId() - { - $this->insertSharedDatabaseFixture(); - $handler = $this->getUserHandler(); - - // 3 is the ID of Editor role - $originalRoleId = 3; - $draft = $handler->createRoleDraft($originalRoleId); - $loadedDraft = $handler->loadRoleDraftByRoleId($originalRoleId); - self::assertSame($loadedDraft->originalId, $originalRoleId); - self::assertEquals($draft, $loadedDraft); - } - - public function testRoleDraftOnlyHavePolicyDraft() - { - $this->insertSharedDatabaseFixture(); - $handler = $this->getUserHandler(); - $originalRoleId = 3; - $originalRole = $handler->loadRole($originalRoleId); - $originalPolicies = []; - foreach ($originalRole->policies as $policy) { - $originalPolicies[$policy->id] = $policy; - } - - $draft = $handler->createRoleDraft($originalRoleId); - $loadedDraft = $handler->loadRole($draft->id, Role::STATUS_DRAFT); - self::assertSame($loadedDraft->originalId, $originalRoleId); - self::assertEquals($draft, $loadedDraft); - foreach ($loadedDraft->policies as $policy) { - self::assertTrue(isset($originalPolicies[$policy->originalId])); - } - - // Now add a new policy. Original ID of the new one must be the same as its actual ID. - $newPolicyModule = 'foo'; - $newPolicyFunction = 'bar'; - $policy = new Persistence\User\Policy(['module' => $newPolicyModule, 'function' => $newPolicyFunction]); - $policyDraft = $handler->addPolicyByRoleDraft($loadedDraft->id, $policy); - - // Test again by reloading the draft. - $loadedDraft = $handler->loadRole($draft->id, Role::STATUS_DRAFT); - foreach ($loadedDraft->policies as $policy) { - if ($policy->id != $policyDraft->id) { - continue; - } - - self::assertNotNull($policy->originalId); - self::assertSame($policy->id, $policy->originalId); - } - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - private function createTestRole(User\Handler $handler): Role - { - $createStruct = new Persistence\User\RoleCreateStruct(); - $createStruct->identifier = 'Test'; - - $roleDraft = $handler->createRole($createStruct); - $handler->publishRoleDraft($roleDraft->id); - - return $handler->loadRole($roleDraft->id); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - private function createTestRoleWithTestPolicy(): void - { - $handler = $this->getUserHandler(); - - $role = $this->createTestRole($handler); - - $policy = new Persistence\User\Policy(); - $policy->module = 'foo'; - $policy->function = 'bar'; - $policy->limitations = [ - 'Subtree' => ['/1', '/1/2'], - 'Foo' => ['Bar'], - ]; - - $handler->addPolicy($role->id, $policy); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/UserPreference/Gateway/DoctrineDatabaseTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/UserPreference/Gateway/DoctrineDatabaseTest.php deleted file mode 100644 index 03080b919c..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/UserPreference/Gateway/DoctrineDatabaseTest.php +++ /dev/null @@ -1,149 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\UserPreference\Gateway; - -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway; -use eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway\DoctrineDatabase; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreferenceSetStruct; - -class DoctrineDatabaseTest extends TestCase -{ - public const EXISTING_USER_PREFERENCE_ID = 1; - public const EXISTING_USER_PREFERENCE_DATA = [ - 'id' => 1, - 'user_id' => 14, - 'name' => 'timezone', - 'value' => 'America/New_York', - ]; - - protected function setUp(): void - { - parent::setUp(); - - $this->insertDatabaseFixture( - __DIR__ . '/../_fixtures/user_preferences.php' - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway::setUserPreference() - */ - public function testInsert() - { - $id = $this->getGateway()->setUserPreference(new UserPreferenceSetStruct([ - 'userId' => 14, - 'name' => 'setting_3', - 'value' => 'value_3', - ])); - - $data = $this->loadUserPreference($id); - - $this->assertEquals([ - 'id' => $id, - 'user_id' => '14', - 'name' => 'setting_3', - 'value' => 'value_3', - ], $data); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway::setUserPreference() - */ - public function testUpdateUserPreference() - { - $userPreference = new UserPreferenceSetStruct([ - 'userId' => 14, - 'name' => 'timezone', - 'value' => 'Europe/Warsaw', - ]); - - $this->getGateway()->setUserPreference($userPreference); - - $this->assertEquals([ - 'id' => (string) self::EXISTING_USER_PREFERENCE_ID, - 'user_id' => '14', - 'name' => 'timezone', - 'value' => 'Europe/Warsaw', - ], $this->loadUserPreference(self::EXISTING_USER_PREFERENCE_ID)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway::countUserPreferences() - */ - public function testCountUserPreferences() - { - $this->assertEquals(3, $this->getGateway()->countUserPreferences( - self::EXISTING_USER_PREFERENCE_DATA['user_id'] - )); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway::loadUserPreferences() - */ - public function testLoadUserPreferences() - { - $userId = 14; - $offset = 1; - $limit = 2; - - $results = $this->getGateway()->loadUserPreferences($userId, $offset, $limit); - - $this->assertEquals([ - [ - 'id' => '2', - 'user_id' => '14', - 'name' => 'setting_1', - 'value' => 'value_1', - ], - [ - 'id' => '3', - 'user_id' => '14', - 'name' => 'setting_2', - 'value' => 'value_2', - ], - ], $results); - } - - /** - * Return a ready to test DoctrineStorage gateway. - * - * @return \eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway - */ - protected function getGateway(): Gateway - { - return new DoctrineDatabase( - $this->getDatabaseConnection() - ); - } - - /** - * @param int $id - * - * @return array - */ - private function loadUserPreference(int $id): array - { - $queryBuilder = $this->connection->createQueryBuilder(); - $queryBuilder - ->select('id', 'user_id', 'name', 'value') - ->from('ezpreferences', 'p') - ->where( - $queryBuilder->expr()->eq( - 'p.id', - $queryBuilder->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - $result = $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); - - return reset($result); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/UserPreference/HandlerTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/UserPreference/HandlerTest.php deleted file mode 100644 index 1cc1c294ac..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/UserPreference/HandlerTest.php +++ /dev/null @@ -1,119 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\UserPreference; - -use eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway; -use eZ\Publish\Core\Persistence\Legacy\UserPreference\Handler; -use eZ\Publish\Core\Persistence\Legacy\UserPreference\Mapper; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreference; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreferenceSetStruct; -use PHPUnit\Framework\TestCase; - -class HandlerTest extends TestCase -{ - public const USER_PREFERENCE_ID = 1; - - /** @var \eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway|\PHPUnit\Framework\MockObject\MockObject */ - private $gateway; - - /** @var \eZ\Publish\Core\Persistence\Legacy\UserPreference\Mapper|\PHPUnit\Framework\MockObject\MockObject */ - private $mapper; - - /** @var \eZ\Publish\Core\Persistence\Legacy\UserPreference\Handler */ - private $handler; - - protected function setUp(): void - { - $this->gateway = $this->createMock(Gateway::class); - $this->mapper = $this->createMock(Mapper::class); - $this->handler = new Handler($this->gateway, $this->mapper); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\UserPreference\Handler::setUserPreference() - */ - public function testSetUserPreference() - { - $setStruct = new UserPreferenceSetStruct([ - 'userId' => 5, - 'name' => 'setting', - 'value' => 'value', - ]); - - $this->gateway - ->expects($this->once()) - ->method('setUserPreference') - ->with($setStruct) - ->willReturn(self::USER_PREFERENCE_ID); - - $this->mapper - ->expects($this->once()) - ->method('extractUserPreferencesFromRows') - ->willReturn([new UserPreference([ - 'id' => self::USER_PREFERENCE_ID, - ])]); - - $userPreference = $this->handler->setUserPreference($setStruct); - - $this->assertEquals($userPreference->id, self::USER_PREFERENCE_ID); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\UserPreference\Handler::countUserPreferences - */ - public function testCountUserPreferences() - { - $ownerId = 10; - $expectedCount = 12; - - $this->gateway - ->expects($this->once()) - ->method('countUserPreferences') - ->with($ownerId) - ->willReturn($expectedCount); - - $this->assertEquals($expectedCount, $this->handler->countUserPreferences($ownerId)); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\UserPreference\Handler::loadUserPreferences - */ - public function testLoadUserPreferences() - { - $ownerId = 9; - $limit = 5; - $offset = 0; - - $rows = [ - ['id' => 1/* ... */], - ['id' => 2/* ... */], - ['id' => 3/* ... */], - ]; - - $objects = [ - new UserPreference(['id' => 1/* ... */]), - new UserPreference(['id' => 2/* ... */]), - new UserPreference(['id' => 3/* ... */]), - ]; - - $this->gateway - ->expects($this->once()) - ->method('loadUserPreferences') - ->with($ownerId, $offset, $limit) - ->willReturn($rows); - - $this->mapper - ->expects($this->once()) - ->method('extractUserPreferencesFromRows') - ->with($rows) - ->willReturn($objects); - - $this->assertEquals($objects, $this->handler->loadUserPreferences($ownerId, $offset, $limit)); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/UserPreference/MapperTest.php b/eZ/Publish/Core/Persistence/Legacy/Tests/UserPreference/MapperTest.php deleted file mode 100644 index 79c6be4049..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/UserPreference/MapperTest.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\Tests\UserPreference; - -use eZ\Publish\Core\Persistence\Legacy\UserPreference\Mapper; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreference; -use PHPUnit\Framework\TestCase; - -class MapperTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\UserPreference\Mapper */ - private $mapper; - - protected function setUp(): void - { - $this->mapper = new Mapper(); - } - - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\UserPreference\Mapper::extractUserPreferencesFromRows - */ - public function testExtractUserPreferencesFromRows() - { - $rows = [ - [ - 'id' => 1, - 'user_id' => 5, - 'name' => 'setting_1', - 'value' => 'value_1', - ], - [ - 'id' => 1, - 'user_id' => 5, - 'name' => 'setting_2', - 'value' => 'value_2', - ], - ]; - - $objects = [ - new UserPreference([ - 'id' => 1, - 'userId' => 5, - 'name' => 'setting_1', - 'value' => 'value_1', - ]), - new UserPreference([ - 'id' => 1, - 'userId' => 5, - 'name' => 'setting_2', - 'value' => 'value_2', - ]), - ]; - - $this->assertEquals($objects, $this->mapper->extractUserPreferencesFromRows($rows)); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/_fixtures/setval.pgsql.sql b/eZ/Publish/Core/Persistence/Legacy/Tests/_fixtures/setval.pgsql.sql deleted file mode 100644 index 5270013515..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/_fixtures/setval.pgsql.sql +++ /dev/null @@ -1,30 +0,0 @@ -SELECT setval('ezcobj_state_group_id_seq',max(id)) FROM ezcobj_state_group; -SELECT setval('ezcobj_state_id_seq',max(id)) FROM ezcobj_state; -SELECT setval('ezcontentclass_attribute_id_seq',max(id)) FROM ezcontentclass_attribute; -SELECT setval('ezcontentclass_id_seq',max(id)) FROM ezcontentclass; -SELECT setval('ezcontentclassgroup_id_seq',max(id)) FROM ezcontentclassgroup; -SELECT setval('ezcontentobject_attribute_id_seq',max(id)) FROM ezcontentobject_attribute; -SELECT setval('ezcontentobject_link_id_seq',max(id)) FROM ezcontentobject_link; -SELECT setval('ezcontentobject_id_seq',max(id)) FROM ezcontentobject; -SELECT setval('ezcontentobject_tree_node_id_seq',max(node_id)) FROM ezcontentobject_tree; -SELECT setval('ezcontentobject_version_id_seq',max(id)) FROM ezcontentobject_version; -SELECT setval('ezimagefile_id_seq',max(id)) FROM ezimagefile; -SELECT setval('ezkeyword_attribute_link_id_seq',max(id)) FROM ezkeyword_attribute_link; -SELECT setval('ezkeyword_id_seq',max(id)) FROM ezkeyword; -SELECT setval('eznode_assignment_id_seq',max(id)) FROM eznode_assignment; -SELECT setval('ezpolicy_limitation_id_seq',max(id)) FROM ezpolicy_limitation; -SELECT setval('ezpolicy_limitation_value_id_seq',max(id)) FROM ezpolicy_limitation_value; -SELECT setval('ezpolicy_id_seq',max(id)) FROM ezpolicy; -SELECT setval('ezrole_id_seq',max(id)) FROM ezrole; -SELECT setval('ezsearch_object_word_link_id_seq',max(id)) FROM ezsearch_object_word_link; -SELECT setval('ezsearch_word_id_seq',max(id)) FROM ezsearch_word; -SELECT setval('ezsection_id_seq',max(id)) FROM ezsection; -SELECT setval('ezurl_id_seq',max(id)) FROM ezurl; -SELECT setval('ezurlalias_ml_incr_id_seq',max(id)) FROM ezurlalias_ml_incr; -SELECT setval('ezurlalias_id_seq',max(id)) FROM ezurlalias; -SELECT setval('ezurlwildcard_id_seq',max(id)) FROM ezurlwildcard; -SELECT setval('ezuser_accountkey_id_seq',max(id)) FROM ezuser_accountkey; -SELECT setval('ezuser_role_id_seq',max(id)) FROM ezuser_role; -SELECT setval('ezcontentbrowsebookmark_id_seq',max(id)) FROM ezcontentbrowsebookmark; -SELECT setval('eznotification_id_seq',max(id)) FROM eznotification; -SELECT setval('ezpreferences_id_seq',max(id)) FROM ezpreferences; diff --git a/eZ/Publish/Core/Persistence/Legacy/TransactionHandler.php b/eZ/Publish/Core/Persistence/Legacy/TransactionHandler.php deleted file mode 100644 index cffc155f74..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/TransactionHandler.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy; - -use Doctrine\DBAL\Connection; -use Exception; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler as CachingLanguageHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler as CachingContentTypeHandler; -use eZ\Publish\SPI\Persistence\TransactionHandler as TransactionHandlerInterface; -use RuntimeException; - -/** - * The Transaction handler for Legacy Storage Engine. - * - * @since 5.3 - */ -class TransactionHandler implements TransactionHandlerInterface -{ - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - /** @var \eZ\Publish\SPI\Persistence\Content\Type\Handler */ - protected $contentTypeHandler; - - /** @var \eZ\Publish\SPI\Persistence\Content\Language\Handler */ - protected $languageHandler; - - public function __construct( - Connection $connection, - CachingContentTypeHandler $contentTypeHandler = null, - CachingLanguageHandler $languageHandler = null - ) { - $this->connection = $connection; - $this->contentTypeHandler = $contentTypeHandler; - $this->languageHandler = $languageHandler; - } - - /** - * Begin transaction. - */ - public function beginTransaction() - { - $this->connection->beginTransaction(); - } - - /** - * Commit transaction. - * - * Commit transaction, or throw exceptions if no transactions has been started. - * - * @throws \RuntimeException If no transaction has been started - */ - public function commit() - { - try { - $this->connection->commit(); - } catch (Exception $e) { - throw new RuntimeException($e->getMessage(), 0, $e); - } - } - - /** - * Rollback transaction. - * - * Rollback transaction, or throw exceptions if no transactions has been started. - * - * @throws \RuntimeException If no transaction has been started - */ - public function rollback() - { - try { - $this->connection->rollback(); - - // Clear all caches after rollback - if ($this->contentTypeHandler instanceof CachingContentTypeHandler) { - $this->contentTypeHandler->clearCache(); - } - - if ($this->languageHandler instanceof CachingLanguageHandler) { - $this->languageHandler->clearCache(); - } - } catch (Exception $e) { - throw new RuntimeException($e->getMessage(), 0, $e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/URL/Gateway.php deleted file mode 100644 index 3dfc41dde5..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Gateway.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL; - -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\SPI\Persistence\URL\URL; - -abstract class Gateway -{ - /** - * Update the URL. - * - * @param \eZ\Publish\SPI\Persistence\URL\URL $url - */ - abstract public function updateUrl(URL $url); - - /** - * Selects URLs matching specified criteria. - * - * @param \eZ\Publish\API\Repository\Values\URL\Query\Criterion $criterion - * @param int $offset - * @param int $limit - * @param \eZ\Publish\API\Repository\Values\URL\Query\SortClause[] $sortClauses - * @param bool $doCount - * - * @return array{ - * "rows": mixed, - * "count": int|null, - * } - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException if Criterion is not applicable to its target - */ - abstract public function find(Criterion $criterion, $offset, $limit, array $sortClauses = [], $doCount = true); - - /** - * Returns IDs of Content Objects using URL identified by $id. - * - * @param int $id - * - * @return array - */ - abstract public function findUsages($id); - - /** - * Loads URL with url id. - * - * @param int $id - * - * @return array - */ - abstract public function loadUrlData($id); - - /** - * Loads URL with url address. - * - * @param int $url - * - * @return array - */ - abstract public function loadUrlDataByUrl($url); -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/URL/Gateway/DoctrineDatabase.php deleted file mode 100644 index a215d42e96..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,270 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\API\Repository\Values\URL\Query\SortClause; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\URL\Gateway; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\SPI\Persistence\URL\URL; -use RuntimeException; - -/** - * URL gateway implementation using the Doctrine. - */ -class DoctrineDatabase extends Gateway -{ - /** @internal */ - public const URL_TABLE = 'ezurl'; - - /** @internal */ - public const URL_LINK_TABLE = 'ezurl_object_link'; - - public const COLUMN_ID = 'id'; - public const COLUMN_URL = 'url'; - public const COLUMN_ORIGINAL_URL_MD5 = 'original_url_md5'; - public const COLUMN_IS_VALID = 'is_valid'; - public const COLUMN_LAST_CHECKED = 'last_checked'; - public const COLUMN_MODIFIED = 'modified'; - public const COLUMN_CREATED = 'created'; - - public const SORT_DIRECTION_MAP = [ - SortClause::SORT_ASC => 'ASC', - SortClause::SORT_DESC => 'DESC', - ]; - - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - /** - * Criteria converter. - * - * @var \eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter - */ - protected $criteriaConverter; - - public function __construct(Connection $connection, CriteriaConverter $criteriaConverter) - { - $this->connection = $connection; - $this->criteriaConverter = $criteriaConverter; - } - - /** - * {@inheritdoc} - */ - public function find(Criterion $criterion, $offset, $limit, array $sortClauses = [], $doCount = true) - { - $count = $doCount ? $this->doCount($criterion) : null; - if (!$doCount && $limit === 0) { - throw new RuntimeException('Invalid query. Cannot disable count and request 0 items at the same time'); - } - - if ($limit === 0 || ($count !== null && $count <= $offset)) { - return [ - 'count' => $count, - 'rows' => [], - ]; - } - - $query = $this->createSelectDistinctQuery(); - $query - ->where($this->criteriaConverter->convertCriteria($query, $criterion)) - ->setMaxResults($limit > 0 ? $limit : PHP_INT_MAX) - ->setFirstResult($offset); - - foreach ($sortClauses as $sortClause) { - $column = sprintf('url.%s', $sortClause->target); - $query->addOrderBy($column, $this->getQuerySortingDirection($sortClause->direction)); - } - - $statement = $query->execute(); - - return [ - 'count' => $count, - 'rows' => $statement->fetchAllAssociative(), - ]; - } - - /** - * {@inheritdoc} - */ - public function findUsages($id): array - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select('DISTINCT c.id') - ->from(ContentGateway::CONTENT_ITEM_TABLE, 'c') - ->innerJoin( - 'c', - ContentGateway::CONTENT_FIELD_TABLE, - 'f_def', - $expr->andX( - 'c.id = f_def.contentobject_id', - 'c.current_version = f_def.version' - ) - ) - ->innerJoin( - 'f_def', - self::URL_LINK_TABLE, - 'u_lnk', - $expr->andX( - 'f_def.id = u_lnk.contentobject_attribute_id', - 'f_def.version = u_lnk.contentobject_attribute_version' - ) - ) - ->where( - $expr->eq( - 'u_lnk.url_id', - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - - return $query->execute()->fetchAll(FetchMode::COLUMN); - } - - /** - * {@inheritdoc} - */ - public function updateUrl(URL $url) - { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::URL_TABLE) - ->set( - self::COLUMN_URL, - $query->createPositionalParameter($url->url, ParameterType::STRING) - )->set( - self::COLUMN_ORIGINAL_URL_MD5, - $query->createPositionalParameter($url->originalUrlMd5, ParameterType::STRING) - ) - ->set( - self::COLUMN_MODIFIED, - $query->createPositionalParameter($url->modified, ParameterType::INTEGER) - ) - ->set( - self::COLUMN_IS_VALID, - $query->createPositionalParameter((int)$url->isValid, ParameterType::INTEGER) - ) - ->set( - self::COLUMN_LAST_CHECKED, - $query->createPositionalParameter($url->lastChecked, ParameterType::INTEGER) - ) - ->where( - $query->expr()->eq( - self::COLUMN_ID, - $query->createPositionalParameter($url->id, ParameterType::INTEGER) - ) - ); - - $query->execute(); - } - - /** - * {@inheritdoc} - */ - public function loadUrlData($id): array - { - $query = $this->createSelectQuery(); - $query->where( - $query->expr()->eq( - self::COLUMN_ID, - $query->createPositionalParameter($id, ParameterType::INTEGER) - ) - ); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - /** - * {@inheritdoc} - */ - public function loadUrlDataByUrl($url): array - { - $query = $this->createSelectQuery(); - $query->where( - $query->expr()->eq( - self::COLUMN_URL, - $query->createPositionalParameter($url, ParameterType::STRING) - ) - ); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - protected function doCount(Criterion $criterion): int - { - $columnName = self::COLUMN_ID; - - $query = $this->connection->createQueryBuilder(); - $query - ->select("COUNT(DISTINCT url.{$columnName})") - ->from(self::URL_TABLE, 'url') - ->where($this->criteriaConverter->convertCriteria($query, $criterion)); - - return (int)$query->execute()->fetchColumn(); - } - - /** - * Creates a Url find query. - */ - protected function createSelectQuery(): QueryBuilder - { - return $this->connection - ->createQueryBuilder() - ->select($this->getSelectColumns()) - ->from(self::URL_TABLE, 'url'); - } - - private function createSelectDistinctQuery(): QueryBuilder - { - return $this->connection - ->createQueryBuilder() - ->select(sprintf('DISTINCT %s', implode(', ', $this->getSelectColumns()))) - ->from(self::URL_TABLE, 'url'); - } - - private function getSelectColumns(): array - { - return [ - sprintf('url.%s', self::COLUMN_ID), - sprintf('url.%s', self::COLUMN_URL), - sprintf('url.%s', self::COLUMN_ORIGINAL_URL_MD5), - sprintf('url.%s', self::COLUMN_IS_VALID), - sprintf('url.%s', self::COLUMN_LAST_CHECKED), - sprintf('url.%s', self::COLUMN_CREATED), - sprintf('url.%s', self::COLUMN_MODIFIED), - ]; - } - - /** - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - */ - private function getQuerySortingDirection(string $direction): string - { - if (!isset(self::SORT_DIRECTION_MAP[$direction])) { - throw new InvalidArgumentException( - '$sortClause->direction', - sprintf( - 'Unsupported "%s" sorting directions, use one of the SortClause::SORT_* constants instead', - $direction - ) - ); - } - - return self::SORT_DIRECTION_MAP[$direction]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/URL/Gateway/ExceptionConversion.php deleted file mode 100644 index d08b8dd1c9..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\URL\Gateway; -use eZ\Publish\SPI\Persistence\URL\URL; -use PDOException; - -class ExceptionConversion extends Gateway -{ - /** - * The wrapped gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\URL\Gateway - */ - protected $innerGateway; - - /** - * ExceptionConversion constructor. - * - * @param \eZ\Publish\Core\Persistence\Legacy\URL\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function updateUrl(URL $url) - { - try { - return $this->innerGateway->updateUrl($url); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function find(Criterion $criterion, $offset, $limit, array $sortClauses = [], $doCount = true) - { - try { - return $this->innerGateway->find($criterion, $offset, $limit, $sortClauses, $doCount); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function findUsages($id) - { - try { - return $this->innerGateway->findUsages($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadUrlData($id) - { - try { - return $this->innerGateway->loadUrlData($id); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadUrlDataByUrl($url) - { - try { - return $this->innerGateway->loadUrlDataByUrl($url); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Handler.php b/eZ/Publish/Core/Persistence/Legacy/URL/Handler.php deleted file mode 100644 index 2fbc5afd23..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Handler.php +++ /dev/null @@ -1,112 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL; - -use eZ\Publish\API\Repository\Values\URL\URLQuery; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\SPI\Persistence\URL\Handler as HandlerInterface; -use eZ\Publish\SPI\Persistence\URL\URLUpdateStruct; - -/** - * Storage Engine handler for URLs. - */ -class Handler implements HandlerInterface -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\URL\Gateway */ - private $urlGateway; - - /** @var \eZ\Publish\Core\Persistence\Legacy\URL\Mapper */ - private $urlMapper; - - /** - * Handler constructor. - * - * @param \eZ\Publish\Core\Persistence\Legacy\URL\Gateway $gateway - * @param \eZ\Publish\Core\Persistence\Legacy\URL\Mapper $mapper - */ - public function __construct(Gateway $gateway, Mapper $mapper) - { - $this->urlGateway = $gateway; - $this->urlMapper = $mapper; - } - - /** - * {@inheritdoc} - */ - public function updateUrl($id, URLUpdateStruct $urlUpdateStruct) - { - $url = $this->urlMapper->createURLFromUpdateStruct( - $urlUpdateStruct - ); - $url->id = $id; - - $this->urlGateway->updateUrl($url); - - return $url; - } - - /** - * {@inheritdoc} - */ - public function find(URLQuery $query) - { - $results = $this->urlGateway->find( - $query->filter, - $query->offset, - $query->limit, - $query->sortClauses, - $query->performCount - ); - - return [ - 'count' => $results['count'], - 'items' => $this->urlMapper->extractURLsFromRows($results['rows']), - ]; - } - - /** - * {@inheritdoc} - */ - public function loadById($id) - { - $url = $this->urlMapper->extractURLsFromRows( - $this->urlGateway->loadUrlData($id) - ); - - if (count($url) < 1) { - throw new NotFoundException('URL', $id); - } - - return reset($url); - } - - /** - * {@inheritdoc} - */ - public function loadByUrl($url) - { - $urls = $this->urlMapper->extractURLsFromRows( - $this->urlGateway->loadUrlDataByUrl($url) - ); - - if (count($urls) < 1) { - throw new NotFoundException('URL', $url); - } - - return reset($urls); - } - - /** - * {@inheritdoc} - */ - public function findUsages($id) - { - $ids = $this->urlGateway->findUsages($id); - - return $ids; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Mapper.php b/eZ/Publish/Core/Persistence/Legacy/URL/Mapper.php deleted file mode 100644 index 704c9c59c6..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Mapper.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL; - -use eZ\Publish\SPI\Persistence\URL\URL; -use eZ\Publish\SPI\Persistence\URL\URLUpdateStruct; - -/** - * URL Mapper. - */ -class Mapper -{ - /** - * Creates a URL from the given update $struct. - * - * @param \eZ\Publish\SPI\Persistence\URL\URLUpdateStruct $struct - * - * @return \eZ\Publish\SPI\Persistence\URL\URL - */ - public function createURLFromUpdateStruct(URLUpdateStruct $struct) - { - $url = new URL(); - $url->url = $struct->url; - $url->originalUrlMd5 = md5($struct->url); - $url->isValid = $struct->isValid; - $url->lastChecked = $struct->lastChecked; - $url->modified = time(); - - return $url; - } - - /** - * Extracts URL objects from $rows. - * - * @param array $rows - * - * @return \eZ\Publish\SPI\Persistence\URL\URL[] - */ - public function extractURLsFromRows(array $rows) - { - $urls = []; - - foreach ($rows as $row) { - $url = new URL(); - $url->id = (int)$row['id']; - $url->url = $row['url']; - $url->originalUrlMd5 = $row['original_url_md5']; - $url->isValid = (bool)$row['is_valid']; - $url->lastChecked = (int)$row['last_checked']; - $url->created = (int)$row['created']; - $url->modified = (int)$row['modified']; - - $urls[] = $url; - } - - return $urls; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriteriaConverter.php b/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriteriaConverter.php deleted file mode 100644 index 9a986a44a5..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriteriaConverter.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL\Query; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -class CriteriaConverter -{ - /** - * Criterion handlers. - * - * @var \eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler[] - */ - protected $handlers; - - /** - * Construct from an optional array of Criterion handlers. - * - * @param \eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler[] $handlers - */ - public function __construct(array $handlers = []) - { - $this->handlers = $handlers; - } - - /** - * Adds handler. - * - * @param \eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler $handler - */ - public function addHandler(CriterionHandler $handler) - { - $this->handlers[] = $handler; - } - - /** - * Generic converter of criteria into query fragments. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException if Criterion is not applicable to its target - * - * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|string - */ - public function convertCriteria(QueryBuilder $queryBuilder, Criterion $criterion) - { - foreach ($this->handlers as $handler) { - if ($handler->accept($criterion)) { - return $handler->handle($this, $queryBuilder, $criterion); - } - } - - throw new NotImplementedException( - 'No visitor available for: ' . get_class($criterion) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler.php b/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler.php deleted file mode 100644 index 77785e1af2..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL\Query; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; - -interface CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\URL\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion); - - /** - * Generate query expression for a Criterion this handler accepts. - * - * accept() must be called before calling this method. - * - * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|string - */ - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion - ); -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/Base.php b/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/Base.php deleted file mode 100644 index c890799649..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/Base.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -abstract class Base implements CriterionHandler -{ - /** - * Inner join `ezurl_object_link` table if not joined yet. - */ - protected function joinContentObjectLink(QueryBuilder $query): void - { - if (!$this->hasJoinedTable($query, DoctrineDatabase::URL_LINK_TABLE)) { - $query->innerJoin( - 'url', - DoctrineDatabase::URL_LINK_TABLE, - 'u_lnk', - 'url.id = u_lnk.url_id' - ); - } - } - - /** - * Inner join `ezcontentobject` table if not joined yet. - */ - protected function joinContentObject(QueryBuilder $query): void - { - if (!$this->hasJoinedTable($query, ContentGateway::CONTENT_ITEM_TABLE)) { - $query->innerJoin( - 'f_def', - ContentGateway::CONTENT_ITEM_TABLE, - 'c', - 'c.id = f_def.contentobject_id' - ); - } - } - - /** - * Inner join `ezcontentobject_attribute` table if not joined yet. - */ - protected function joinContentObjectAttribute(QueryBuilder $query): void - { - if (!$this->hasJoinedTable($query, ContentGateway::CONTENT_FIELD_TABLE)) { - $query->innerJoin( - 'u_lnk', - ContentGateway::CONTENT_FIELD_TABLE, - 'f_def', - $query->expr()->andX( - 'u_lnk.contentobject_attribute_id = f_def.id', - 'u_lnk.contentobject_attribute_version = f_def.version' - ) - ); - } - } - - protected function hasJoinedTable(QueryBuilder $queryBuilder, string $tableName): bool - { - // find table name in a structure: ['fromAlias' => [['joinTable' => '<table_name>'], ...]] - $joinedParts = $queryBuilder->getQueryPart('join'); - foreach ($joinedParts as $joinedTables) { - foreach ($joinedTables as $join) { - if ($join['joinTable'] === $tableName) { - return true; - } - } - } - - return false; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/LogicalAnd.php b/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/LogicalAnd.php deleted file mode 100644 index c82545576f..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/LogicalAnd.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -class LogicalAnd implements CriterionHandler -{ - /** - * {@inheritdoc} - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\LogicalAnd; - } - - /** - * {@inheritdoc} - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function handle(CriteriaConverter $converter, QueryBuilder $queryBuilder, Criterion $criterion) - { - $subexpressions = []; - foreach ($criterion->criteria as $subCriterion) { - $subexpressions[] = $converter->convertCriteria($queryBuilder, $subCriterion); - } - - return $queryBuilder->expr()->andX(...$subexpressions); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/LogicalNot.php b/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/LogicalNot.php deleted file mode 100644 index 79a52abc57..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/LogicalNot.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -class LogicalNot implements CriterionHandler -{ - /** - * {@inheritdoc} - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\LogicalNot; - } - - /** - * {@inheritdoc} - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion - ) { - return sprintf( - 'NOT (%s)', - $converter->convertCriteria($queryBuilder, $criterion->criteria[0]) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/LogicalOr.php b/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/LogicalOr.php deleted file mode 100644 index 00d37496e7..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/LogicalOr.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -class LogicalOr implements CriterionHandler -{ - /** - * {@inheritdoc} - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\LogicalOr; - } - - /** - * {@inheritdoc} - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion - ) { - $subexpressions = []; - foreach ($criterion->criteria as $subCriterion) { - $subexpressions[] = $converter->convertCriteria($queryBuilder, $subCriterion); - } - - return $queryBuilder->expr()->orX(...$subexpressions); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/MatchAll.php b/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/MatchAll.php deleted file mode 100644 index 9501da97ae..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/MatchAll.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -class MatchAll implements CriterionHandler -{ - /** - * {@inheritdoc} - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\MatchAll; - } - - /** - * {@inheritdoc} - */ - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion - ) { - return '1 = 1'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/MatchNone.php b/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/MatchNone.php deleted file mode 100644 index 34a1d15a30..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/MatchNone.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -class MatchNone implements CriterionHandler -{ - /** - * {@inheritdoc} - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\MatchNone; - } - - /** - * {@inheritdoc} - */ - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion - ) { - return '1 = 0'; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/Pattern.php b/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/Pattern.php deleted file mode 100644 index 0437d98235..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/Pattern.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -class Pattern implements CriterionHandler -{ - /** - * {@inheritdoc} - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\Pattern; - } - - /** - * {@inheritdoc} - */ - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion - ) { - /** @var \eZ\Publish\API\Repository\Values\URL\Query\Criterion\Pattern $criterion */ - return $queryBuilder->expr()->like( - 'url', - $queryBuilder->createNamedParameter( - '%' . $criterion->pattern . '%', - ParameterType::STRING, - ':pattern' - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/SectionId.php b/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/SectionId.php deleted file mode 100644 index 2945826aa1..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/SectionId.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; - -class SectionId extends Base -{ - /** - * {@inheritdoc} - */ - public function accept(Criterion $criterion): bool - { - return $criterion instanceof Criterion\SectionId; - } - - /** - * {@inheritdoc} - */ - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion - ) { - $this->joinContentObjectLink($queryBuilder); - $this->joinContentObjectAttribute($queryBuilder); - $this->joinContentObject($queryBuilder); - - return $queryBuilder->expr()->in( - 'c.section_id', - $queryBuilder->createNamedParameter( - $criterion->sectionIds, - Connection::PARAM_INT_ARRAY, - ':section_ids' - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/SectionIdentifier.php b/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/SectionIdentifier.php deleted file mode 100644 index b19f50ef04..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/SectionIdentifier.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway as SectionGateway; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; - -class SectionIdentifier extends Base -{ - /** - * {@inheritdoc} - */ - public function accept(Criterion $criterion): bool - { - return $criterion instanceof Criterion\SectionIdentifier; - } - - /** - * {@inheritdoc} - */ - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion - ) { - $this->joinContentObjectLink($queryBuilder); - $this->joinContentObjectAttribute($queryBuilder); - $this->joinContentObject($queryBuilder); - - $queryBuilder->innerJoin( - 'c', - SectionGateway::CONTENT_SECTION_TABLE, - 's', - 'c.section_id = s.id' - ); - - return $queryBuilder->expr()->in( - 's.identifier', - $queryBuilder->createNamedParameter( - $criterion->sectionIdentifiers, - Connection::PARAM_STR_ARRAY, - ':section_identifiers' - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/Validity.php b/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/Validity.php deleted file mode 100644 index c9d3b1a748..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/Validity.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -class Validity implements CriterionHandler -{ - /** - * {@inheritdoc} - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\Validity; - } - - /** - * {@inheritdoc} - */ - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion - ) { - /** @var \eZ\Publish\API\Repository\Values\URL\Query\Criterion\Validity $criterion */ - return $queryBuilder->expr()->eq( - 'is_valid', - $queryBuilder->createNamedParameter( - (int)$criterion->isValid, - ParameterType::INTEGER, - ':is_valid' - ) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/VisibleOnly.php b/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/VisibleOnly.php deleted file mode 100644 index 3491042e72..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/URL/Query/CriterionHandler/VisibleOnly.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; - -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; - -class VisibleOnly extends Base -{ - /** - * {@inheritdoc} - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\VisibleOnly; - } - - /** - * {@inheritdoc} - */ - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion - ) { - $this->joinContentObjectLink($queryBuilder); - $this->joinContentObjectAttribute($queryBuilder); - - $queryBuilder->innerJoin( - 'f_def', - Gateway::CONTENT_TREE_TABLE, - 't', - $queryBuilder->expr()->andX( - 't.contentobject_id = f_def.contentobject_id', - 't.contentobject_version = f_def.version' - ) - ); - - return $queryBuilder->expr()->eq( - 't.is_invisible', - $queryBuilder->createNamedParameter(0, ParameterType::INTEGER, ':location_is_invisible') - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/User/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/User/Gateway.php deleted file mode 100644 index 1017950dbf..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/User/Gateway.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\User; - -use eZ\Publish\SPI\Persistence\User; -use eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct; - -/** - * User Gateway. - * - * @internal For internal use by Persistence Handlers. - */ -abstract class Gateway -{ - public const USER_TABLE = 'ezuser'; - - /** - * Load a User by User ID. - */ - abstract public function load(int $userId): array; - - /** - * Load a User by User login. - */ - abstract public function loadByLogin(string $login): array; - - /** - * Load a User by User e-mail. - */ - abstract public function loadByEmail(string $email): array; - - /** - * Load a User by User token. - */ - abstract public function loadUserByToken(string $hash): array; - - /** - * Update the user password as specified by the user struct. - * - * @param \eZ\Publish\SPI\Persistence\User $user - */ - abstract public function updateUserPassword(User $user): void; - - /** - * Update a User token specified by UserTokenUpdateStruct. - * - * @see \eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct - */ - abstract public function updateUserToken(UserTokenUpdateStruct $userTokenUpdateStruct): void; - - /** - * Expire the given User token. - */ - abstract public function expireUserToken(string $hash): void; - - /** - * Assign, with the given Limitation, a Role to a User. - * - * @param array $limitation a map of the Limitation identifiers to raw Limitation values. - */ - abstract public function assignRole(int $contentId, int $roleId, array $limitation): void; - - /** - * Remove a Role from User or User group. - */ - abstract public function removeRole(int $contentId, int $roleId): void; - - /** - * Remove a Role from User or User group, by assignment ID. - */ - abstract public function removeRoleAssignmentById(int $roleAssignmentId): void; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/User/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/User/Gateway/DoctrineDatabase.php deleted file mode 100644 index d218338234..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/User/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,308 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\User\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\User\Gateway; -use eZ\Publish\SPI\Persistence\User; -use eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct; -use function time; - -/** - * User gateway implementation using the Doctrine database. - * - * @internal Gateway implementation is considered internal. Use Persistence User Handler instead. - * - * @see \eZ\Publish\SPI\Persistence\User\Handler - */ -final class DoctrineDatabase extends Gateway -{ - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */ - private $dbPlatform; - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct(Connection $connection) - { - $this->connection = $connection; - $this->dbPlatform = $this->connection->getDatabasePlatform(); - } - - public function load(int $userId): array - { - $query = $this->getLoadUserQueryBuilder(); - $query - ->where( - $query->expr()->eq( - 'u.contentobject_id', - $query->createPositionalParameter($userId, ParameterType::INTEGER) - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadByLogin(string $login): array - { - $query = $this->getLoadUserQueryBuilder(); - $expr = $query->expr(); - $query - ->where( - $expr->eq( - $this->dbPlatform->getLowerExpression('u.login'), - // Index is case in-sensitive, on some db's lowercase, so we lowercase $login - $query->createPositionalParameter( - mb_strtolower($login, 'UTF-8'), - ParameterType::STRING - ) - ) - ); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadByEmail(string $email): array - { - $query = $this->getLoadUserQueryBuilder(); - $query->where( - $query->expr()->eq( - 'u.email', - $query->createPositionalParameter($email, ParameterType::STRING) - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadUserByToken(string $hash): array - { - $query = $this->getLoadUserQueryBuilder(); - $query - ->leftJoin( - 'u', - 'ezuser_accountkey', - 'token', - $query->expr()->eq( - 'token.user_id', - 'u.contentobject_id' - ) - ) - ->where( - $query->expr()->eq( - 'token.hash_key', - $query->createPositionalParameter($hash, ParameterType::STRING) - ) - ) - ->andWhere( - $query->expr()->gte( - 'token.time', - $query->createPositionalParameter(time(), ParameterType::INTEGER) - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function updateUserPassword(User $user): void - { - $queryBuilder = $this->connection->createQueryBuilder(); - - $queryBuilder - ->update($this->connection->quoteIdentifier(self::USER_TABLE)) - ->set('password_hash', ':passwordHash') - ->set('password_hash_type', ':passwordHashType') - ->set('password_updated_at', ':passwordUpdatedAt') - ->setParameter(':passwordHash', $user->passwordHash, ParameterType::STRING) - ->setParameter(':passwordHashType', $user->hashAlgorithm, ParameterType::INTEGER) - ->setParameter(':passwordUpdatedAt', $user->passwordUpdatedAt) - ->where( - $queryBuilder->expr()->eq( - $this->connection->quoteIdentifier('contentobject_id'), - ':userId' - ) - ) - ->setParameter(':userId', $user->id, ParameterType::INTEGER); - - $queryBuilder->execute(); - } - - public function updateUserToken(UserTokenUpdateStruct $userTokenUpdateStruct): void - { - $query = $this->connection->createQueryBuilder(); - if (false === $this->userHasToken($userTokenUpdateStruct->userId)) { - $query - ->insert('ezuser_accountkey') - ->values( - [ - 'hash_key' => ':hash_key', - 'time' => ':time', - 'user_id' => ':user_id', - ] - ); - } else { - $query - ->update('ezuser_accountkey') - ->set('hash_key', ':hash_key') - ->set('time', ':time') - ->where('user_id = :user_id'); - } - - $query->setParameter('hash_key', $userTokenUpdateStruct->hashKey, ParameterType::STRING); - $query->setParameter('time', $userTokenUpdateStruct->time, ParameterType::INTEGER); - $query->setParameter('user_id', $userTokenUpdateStruct->userId, ParameterType::INTEGER); - - $query->execute(); - } - - public function expireUserToken(string $hash): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update('ezuser_accountkey') - ->set( - 'time', - $query->createPositionalParameter(0, ParameterType::INTEGER) - )->where( - $query->expr()->eq( - 'hash_key', - $query->createPositionalParameter($hash, ParameterType::STRING) - ) - ); - $query->execute(); - } - - public function assignRole(int $contentId, int $roleId, array $limitation): void - { - foreach ($limitation as $identifier => $values) { - foreach ($values as $value) { - $query = $this->connection->createQueryBuilder(); - $query - ->insert('ezuser_role') - ->values( - [ - 'contentobject_id' => $query->createPositionalParameter( - $contentId, - ParameterType::INTEGER - ), - 'role_id' => $query->createPositionalParameter( - $roleId, - ParameterType::INTEGER - ), - 'limit_identifier' => $query->createPositionalParameter( - $identifier, - ParameterType::STRING - ), - 'limit_value' => $query->createPositionalParameter( - $value, - ParameterType::STRING - ), - ] - ); - $query->execute(); - } - } - } - - public function removeRole(int $contentId, int $roleId): void - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->delete('ezuser_role') - ->where( - $expr->eq( - 'contentobject_id', - $query->createPositionalParameter($contentId, ParameterType::INTEGER) - ) - ) - ->andWhere( - $expr->eq( - 'role_id', - $query->createPositionalParameter($roleId, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - public function removeRoleAssignmentById(int $roleAssignmentId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete('ezuser_role') - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($roleAssignmentId, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - private function getLoadUserQueryBuilder(): QueryBuilder - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select( - 'u.contentobject_id', - 'u.login', - 'u.email', - 'u.password_hash', - 'u.password_hash_type', - 'u.password_updated_at', - 's.is_enabled', - 's.max_login' - ) - ->from('ezuser', 'u') - ->leftJoin( - 'u', - 'ezuser_setting', - 's', - $expr->eq( - 's.user_id', - 'u.contentobject_id' - ) - ); - - return $query; - } - - private function userHasToken(int $userId): bool - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select('token.id') - ->from('ezuser_accountkey', 'token') - ->where( - $expr->eq( - 'token.user_id', - $query->createPositionalParameter( - $userId, - ParameterType::INTEGER - ) - ) - ); - - return !empty($query->execute()->fetch(FetchMode::ASSOCIATIVE)); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/User/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/User/Gateway/ExceptionConversion.php deleted file mode 100644 index c2475d6c8d..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/User/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,129 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\User\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\User\Gateway; -use eZ\Publish\SPI\Persistence\User; -use eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct; -use PDOException; - -/** - * @internal Internal exception conversion layer. - */ -final class ExceptionConversion extends Gateway -{ - /** - * The wrapped gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\User\Gateway - */ - private $innerGateway; - - /** - * Create a new exception conversion gateway around $innerGateway. - * - * @param \eZ\Publish\Core\Persistence\Legacy\User\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function load(int $userId): array - { - try { - return $this->innerGateway->load($userId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadByLogin(string $login): array - { - try { - return $this->innerGateway->loadByLogin($login); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadByEmail(string $email): array - { - try { - return $this->innerGateway->loadByEmail($email); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadUserByToken(string $hash): array - { - try { - return $this->innerGateway->loadUserByToken($hash); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateUserPassword(User $user): void - { - try { - $this->innerGateway->updateUserPassword($user); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateUserToken(UserTokenUpdateStruct $userTokenUpdateStruct): void - { - try { - $this->innerGateway->updateUserToken($userTokenUpdateStruct); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function expireUserToken(string $hash): void - { - try { - $this->innerGateway->expireUserToken($hash); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function assignRole(int $contentId, int $roleId, array $limitation): void - { - try { - $this->innerGateway->assignRole($contentId, $roleId, $limitation); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function removeRole(int $contentId, int $roleId): void - { - try { - $this->innerGateway->removeRole($contentId, $roleId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function removeRoleAssignmentById(int $roleAssignmentId): void - { - try { - $this->innerGateway->removeRoleAssignmentById($roleAssignmentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/User/Handler.php b/eZ/Publish/Core/Persistence/Legacy/User/Handler.php deleted file mode 100644 index 5f0a05924a..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/User/Handler.php +++ /dev/null @@ -1,693 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\User; - -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException as NotFound; -use eZ\Publish\Core\Persistence\Legacy\Exception\RoleNotFound; -use eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway as RoleGateway; -use eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter; -use eZ\Publish\SPI\Persistence\User; -use eZ\Publish\SPI\Persistence\User\Handler as BaseUserHandler; -use eZ\Publish\SPI\Persistence\User\Policy; -use eZ\Publish\SPI\Persistence\User\Role; -use eZ\Publish\SPI\Persistence\User\RoleCopyStruct; -use eZ\Publish\SPI\Persistence\User\RoleCreateStruct; -use eZ\Publish\SPI\Persistence\User\RoleUpdateStruct; -use eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct; -use LogicException; - -/** - * Storage Engine handler for user module. - */ -class Handler implements BaseUserHandler -{ - /** - * Gateway for storing user data. - * - * @var \eZ\Publish\Core\Persistence\Legacy\User\Gateway - */ - protected $userGateway; - - /** - * Gateway for storing role data. - * - * @var \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway - */ - protected $roleGateway; - - /** - * Mapper for user related objects. - * - * @var \eZ\Publish\Core\Persistence\Legacy\User\Mapper - */ - protected $mapper; - - /** @var \eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter */ - protected $limitationConverter; - - /** - * Construct from userGateway. - * - * @param \eZ\Publish\Core\Persistence\Legacy\User\Gateway $userGateway - * @param \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway $roleGateway - * @param \eZ\Publish\Core\Persistence\Legacy\User\Mapper $mapper - * @param \eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter $limitationConverter - */ - public function __construct(Gateway $userGateway, RoleGateway $roleGateway, Mapper $mapper, LimitationConverter $limitationConverter) - { - $this->userGateway = $userGateway; - $this->roleGateway = $roleGateway; - $this->mapper = $mapper; - $this->limitationConverter = $limitationConverter; - } - - /** - * Create a user. - * - * The User struct used to create the user will contain an ID which is used - * to reference the user. - * - * @param \eZ\Publish\SPI\Persistence\User $user - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function create(User $user) - { - throw new NotImplementedException('This method should not be called, creation is done via content handler.'); - } - - /** - * Loads user with user ID. - * - * @param mixed $userId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found - * - * @return \eZ\Publish\SPI\Persistence\User - */ - public function load($userId) - { - $data = $this->userGateway->load($userId); - - if (empty($data)) { - throw new NotFound('user', $userId); - } - - return $this->mapper->mapUser(reset($data)); - } - - /** - * Loads user with user login. - * - * @param string $login - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found - * - * @return \eZ\Publish\SPI\Persistence\User - */ - public function loadByLogin($login) - { - $data = $this->userGateway->loadByLogin($login); - - if (empty($data)) { - throw new NotFound('user', $login); - } elseif (count($data) > 1) { - throw new LogicException("Found more then one user with login '{$login}'"); - } - - return $this->mapper->mapUser($data[0]); - } - - /** - * Loads user(s) with user email. - * - * As earlier eZ Publish versions supported several users having same email (ini config), - * this function may return several users. - * - * @param string $email - * - * @return \eZ\Publish\SPI\Persistence\User - */ - public function loadByEmail(string $email): User - { - $data = $this->userGateway->loadByEmail($email); - - if (empty($data)) { - throw new NotFound('user', $email); - } elseif (count($data) > 1) { - throw new LogicException("Found more then one user with login '{$email}'"); - } - - return $this->mapper->mapUser($data[0]); - } - - /** - * Loads user(s) with user email. - * - * As earlier eZ Publish versions supported several users having same email (ini config), - * this function may return several users. - * - * @param string $email - * - * @return \eZ\Publish\SPI\Persistence\User[] - */ - public function loadUsersByEmail(string $email): array - { - $data = $this->userGateway->loadByEmail($email); - - if (empty($data)) { - return []; - } - - return $this->mapper->mapUsers($data); - } - - /** - * Loads user with user hash. - * - * @param string $hash - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found - * - * @return \eZ\Publish\SPI\Persistence\User - */ - public function loadUserByToken($hash) - { - $data = $this->userGateway->loadUserByToken($hash); - - if (empty($data)) { - throw new NotFound('user', $hash); - } - - return $this->mapper->mapUser(reset($data)); - } - - /** - * Update the user information specified by the user struct. - * - * @param \eZ\Publish\SPI\Persistence\User $user - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function update(User $user) - { - throw new NotImplementedException('This method should not be called, update is done via content handler.'); - } - - public function updatePassword(User $user): void - { - $this->userGateway->updateUserPassword($user); - } - - /** - * Update the user token information specified by the userToken struct. - * - * @param \eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct $userTokenUpdateStruct - */ - public function updateUserToken(UserTokenUpdateStruct $userTokenUpdateStruct) - { - $this->userGateway->updateUserToken($userTokenUpdateStruct); - } - - /** - * Expires user account key with user hash. - * - * @param string $hash - */ - public function expireUserToken($hash) - { - $this->userGateway->expireUserToken($hash); - } - - /** - * Delete user with the given ID. - * - * @param mixed $userId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function delete($userId) - { - throw new NotImplementedException('This method should not be called, delete is done via content handler.'); - } - - /** - * Create new role draft. - * - * Sets status to Role::STATUS_DRAFT on the new returned draft. - * - * @param \eZ\Publish\SPI\Persistence\User\RoleCreateStruct $createStruct - * - * @return \eZ\Publish\SPI\Persistence\User\Role - */ - public function createRole(RoleCreateStruct $createStruct) - { - return $this->internalCreateRole($createStruct); - } - - /** - * Creates a draft of existing defined role. - * - * Sets status to Role::STATUS_DRAFT on the new returned draft. - * - * @param mixed $roleId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role with defined status is not found - * - * @return \eZ\Publish\SPI\Persistence\User\Role - */ - public function createRoleDraft($roleId) - { - $createStruct = $this->mapper->createCreateStructFromRole( - $this->loadRole($roleId) - ); - - return $this->internalCreateRole($createStruct, $roleId); - } - - /** - * Internal method for creating Role. - * - * Used by self::createRole() and self::createRoleDraft() - * - * @param \eZ\Publish\SPI\Persistence\User\RoleCreateStruct $createStruct - * @param mixed|null $roleId Used by self::createRoleDraft() to retain Role id in the draft - * - * @return \eZ\Publish\SPI\Persistence\User\Role - */ - protected function internalCreateRole(RoleCreateStruct $createStruct, $roleId = null) - { - $createStruct = clone $createStruct; - $role = $this->mapper->createRoleFromCreateStruct( - $createStruct - ); - $role->id = $roleId; - $role->status = Role::STATUS_DRAFT; - - $this->roleGateway->createRole($role); - - foreach ($role->policies as $policy) { - $this->addPolicyByRoleDraft($role->id, $policy); - } - - return $role; - } - - public function copyRole(RoleCopyStruct $copyStruct): Role - { - $role = $this->mapper->createRoleFromCopyStruct( - $copyStruct - ); - - $this->roleGateway->copyRole($role); - - foreach ($role->policies as $policy) { - $this->addPolicy($role->id, $policy); - } - - return $role; - } - - /** - * Loads a specified role (draft) by $roleId and $status. - * - * @param mixed $roleId - * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role with given status does not exist - * - * @return \eZ\Publish\SPI\Persistence\User\Role - */ - public function loadRole($roleId, $status = Role::STATUS_DEFINED) - { - $data = $this->roleGateway->loadRole($roleId, $status); - - if (empty($data)) { - throw new RoleNotFound($roleId, $status); - } - - $role = $this->mapper->mapRole($data); - foreach ($role->policies as $policy) { - $this->limitationConverter->toSPI($policy); - } - - return $role; - } - - /** - * Loads a specified role (draft) by $identifier and $status. - * - * @param string $identifier - * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role is not found - * - * @return \eZ\Publish\SPI\Persistence\User\Role - */ - public function loadRoleByIdentifier($identifier, $status = Role::STATUS_DEFINED) - { - $data = $this->roleGateway->loadRoleByIdentifier($identifier, $status); - - if (empty($data)) { - throw new RoleNotFound($identifier, $status); - } - - $role = $this->mapper->mapRole($data); - foreach ($role->policies as $policy) { - $this->limitationConverter->toSPI($policy); - } - - return $role; - } - - /** - * Loads a role draft by the original role ID. - * - * @param mixed $roleId ID of the role the draft was created from. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role is not found - * - * @return \eZ\Publish\SPI\Persistence\User\Role - */ - public function loadRoleDraftByRoleId($roleId) - { - $data = $this->roleGateway->loadRoleDraftByRoleId($roleId); - - if (empty($data)) { - throw new RoleNotFound($roleId, Role::STATUS_DRAFT); - } - - $role = $this->mapper->mapRole($data); - foreach ($role->policies as $policy) { - $this->limitationConverter->toSPI($policy); - } - - return $role; - } - - /** - * Loads all roles. - * - * @return \eZ\Publish\SPI\Persistence\User\Role[] - */ - public function loadRoles() - { - $data = $this->roleGateway->loadRoles(); - - $roles = $this->mapper->mapRoles($data); - foreach ($roles as $role) { - foreach ($role->policies as $policy) { - $this->limitationConverter->toSPI($policy); - } - } - - return $roles; - } - - /** - * Update role (draft). - * - * @param \eZ\Publish\SPI\Persistence\User\RoleUpdateStruct $role - */ - public function updateRole(RoleUpdateStruct $role) - { - $this->roleGateway->updateRole($role); - } - - /** - * Delete the specified role (draft). - * - * @param mixed $roleId - * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT - */ - public function deleteRole($roleId, $status = Role::STATUS_DEFINED) - { - $role = $this->loadRole($roleId, $status); - - foreach ($role->policies as $policy) { - $this->roleGateway->removePolicy($policy->id); - } - - $this->roleGateway->deleteRole($role->id, $status); - } - - /** - * Publish the specified role draft. - * - * @param mixed $roleDraftId - */ - public function publishRoleDraft($roleDraftId) - { - $roleDraft = $this->loadRole($roleDraftId, Role::STATUS_DRAFT); - - try { - $originalRoleId = $roleDraft->originalId; - $role = $this->loadRole($originalRoleId); - $roleAssignments = $this->loadRoleAssignmentsByRoleId($role->id); - $this->deleteRole($role->id); - - foreach ($roleAssignments as $roleAssignment) { - if (empty($roleAssignment->limitationIdentifier)) { - $this->assignRole($roleAssignment->contentId, $originalRoleId); - } else { - $this->assignRole( - $roleAssignment->contentId, - $originalRoleId, - [$roleAssignment->limitationIdentifier => $roleAssignment->values] - ); - } - } - $this->roleGateway->publishRoleDraft($roleDraft->id, $role->id); - } catch (NotFound $e) { - // If no published role is found, only publishing is needed, without specifying original role ID as there is none. - $this->roleGateway->publishRoleDraft($roleDraft->id); - } - } - - /** - * Adds a policy to a role draft. - * - * @param mixed $roleId - * @param \eZ\Publish\SPI\Persistence\User\Policy $policy - * - * @return \eZ\Publish\SPI\Persistence\User\Policy - */ - public function addPolicyByRoleDraft($roleId, Policy $policy) - { - $legacyPolicy = clone $policy; - $legacyPolicy->originalId = $policy->id; - $this->limitationConverter->toLegacy($legacyPolicy); - - $this->roleGateway->addPolicy($roleId, $legacyPolicy); - $policy->id = $legacyPolicy->id; - $policy->originalId = $legacyPolicy->originalId; - $policy->roleId = $legacyPolicy->roleId; - - return $policy; - } - - /** - * Adds a policy to a role. - * - * @param mixed $roleId - * @param \eZ\Publish\SPI\Persistence\User\Policy $policy - * - * @return \eZ\Publish\SPI\Persistence\User\Policy - */ - public function addPolicy($roleId, Policy $policy) - { - $legacyPolicy = clone $policy; - $this->limitationConverter->toLegacy($legacyPolicy); - - $this->roleGateway->addPolicy($roleId, $legacyPolicy); - $policy->id = $legacyPolicy->id; - $policy->roleId = $legacyPolicy->roleId; - - return $policy; - } - - /** - * Update a policy. - * - * Replaces limitations values with new values. - * - * @param \eZ\Publish\SPI\Persistence\User\Policy $policy - */ - public function updatePolicy(Policy $policy) - { - $policy = clone $policy; - $this->limitationConverter->toLegacy($policy); - - $this->roleGateway->removePolicyLimitations($policy->id); - $this->roleGateway->addPolicyLimitations($policy->id, $policy->limitations === '*' ? [] : $policy->limitations); - } - - /** - * Removes a policy from a role. - * - * @param mixed $policyId - * @param mixed $roleId - */ - public function deletePolicy($policyId, $roleId) - { - // Each policy can only be associated to exactly one role. Thus it is - // sufficient to use the policyId for identification and just remove - // the policy completely. - $this->roleGateway->removePolicy($policyId); - } - - /** - * Returns the user policies associated with the user (including inherited policies from user groups). - * - * @param mixed $userId - * - * @return \eZ\Publish\SPI\Persistence\User\Policy[] - */ - public function loadPoliciesByUserId($userId) - { - $data = $this->roleGateway->loadPoliciesByUserId($userId); - - $policies = $this->mapper->mapPolicies($data); - - foreach ($policies as $policy) { - $this->limitationConverter->toSPI($policy); - } - - return $policies; - } - - /** - * Assigns role to a user or user group with given limitations. - * - * The limitation array looks like: - * <code> - * array( - * 'Subtree' => array( - * '/1/2/', - * '/1/4/', - * ), - * 'Foo' => array( 'Bar' ), - * … - * ) - * </code> - * - * Where the keys are the limitation identifiers, and the respective values - * are an array of limitation values. The limitation parameter is optional. - * - * @param mixed $contentId The groupId or userId to assign the role to. - * @param mixed $roleId - * @param array $limitation - */ - public function assignRole($contentId, $roleId, array $limitation = null) - { - $limitation = $limitation ?: ['' => ['']]; - $this->userGateway->assignRole($contentId, $roleId, $limitation); - } - - /** - * Un-assign a role. - * - * @param mixed $contentId The user or user group Id to un-assign the role from. - * @param mixed $roleId - */ - public function unassignRole($contentId, $roleId) - { - $this->userGateway->removeRole($contentId, $roleId); - } - - /** - * Un-assign a role by assignment ID. - * - * @param mixed $roleAssignmentId The assignment ID. - */ - public function removeRoleAssignment($roleAssignmentId) - { - $this->userGateway->removeRoleAssignmentById($roleAssignmentId); - } - - /** - * Loads role assignment for specified assignment ID. - * - * @param mixed $roleAssignmentId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role assignment is not found - * - * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment - */ - public function loadRoleAssignment($roleAssignmentId) - { - $data = $this->roleGateway->loadRoleAssignment($roleAssignmentId); - - if (empty($data)) { - throw new NotFound('roleAssignment', $roleAssignmentId); - } - - return $this->mapper->mapRoleAssignments($data)[0]; - } - - /** - * Loads roles assignments Role. - * - * Role Assignments with same roleId and limitationIdentifier will be merged together into one. - * - * @param mixed $roleId - * - * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment[] - */ - public function loadRoleAssignmentsByRoleId($roleId) - { - $data = $this->roleGateway->loadRoleAssignmentsByRoleId($roleId); - - if (empty($data)) { - return []; - } - - return $this->mapper->mapRoleAssignments($data); - } - - /** - * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment[] - */ - public function loadRoleAssignmentsByRoleIdWithOffsetAndLimit(int $roleId, int $offset, ?int $limit): array - { - $data = $this->roleGateway->loadRoleAssignmentsByRoleIdWithOffsetAndLimit($roleId, $offset, $limit); - - if (empty($data)) { - return []; - } - - return $this->mapper->mapRoleAssignments($data); - } - - public function countRoleAssignments(int $roleId): int - { - return $this->roleGateway->countRoleAssignments($roleId); - } - - /** - * Loads roles assignments to a user/group. - * - * Role Assignments with same roleId and limitationIdentifier will be merged together into one. - * - * @param mixed $groupId In legacy storage engine this is the content object id roles are assigned to in ezuser_role. - * By the nature of legacy this can currently also be used to get by $userId. - * @param bool $inherit If true also return inherited role assignments from user groups. - * - * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment[] - */ - public function loadRoleAssignmentsByGroupId($groupId, $inherit = false) - { - $data = $this->roleGateway->loadRoleAssignmentsByGroupId($groupId, $inherit); - - if (empty($data)) { - return []; - } - - return $this->mapper->mapRoleAssignments($data); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/User/Mapper.php b/eZ/Publish/Core/Persistence/Legacy/User/Mapper.php deleted file mode 100644 index c0b608c3e6..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/User/Mapper.php +++ /dev/null @@ -1,253 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\User; - -use eZ\Publish\SPI\Persistence\User; -use eZ\Publish\SPI\Persistence\User\Policy; -use eZ\Publish\SPI\Persistence\User\Role; -use eZ\Publish\SPI\Persistence\User\RoleAssignment; -use eZ\Publish\SPI\Persistence\User\RoleCreateStruct; - -/** - * mapper for User related objects. - */ -class Mapper -{ - /** - * Map user data into user object. - * - * @param array $data - * - * @return \eZ\Publish\SPI\Persistence\User - */ - public function mapUser(array $data) - { - $user = new User(); - $user->id = (int)$data['contentobject_id']; - $user->login = $data['login']; - $user->email = $data['email']; - $user->passwordHash = $data['password_hash']; - $user->hashAlgorithm = (int)$data['password_hash_type']; - $user->passwordUpdatedAt = $data['password_updated_at'] !== null ? (int)$data['password_updated_at'] : null; - $user->isEnabled = (bool)$data['is_enabled']; - $user->maxLogin = $data['max_login']; - - return $user; - } - - /** - * Map data for a set of user data. - * - * @param array $data - * - * @return \eZ\Publish\SPI\Persistence\User[] - */ - public function mapUsers(array $data) - { - $users = []; - foreach ($data as $row) { - $users[] = $this->mapUser($row); - } - - return $users; - } - - /** - * Map policy data to an array of policies. - * - * @param array $data - * - * @return \eZ\Publish\SPI\Persistence\User\Policy - */ - public function mapPolicies(array $data) - { - /** @var \eZ\Publish\SPI\Persistence\User\Policy[] */ - $policies = []; - foreach ($data as $row) { - $policyId = $row['ezpolicy_id']; - if (!isset($policies[$policyId]) && ($policyId !== null)) { - $originalId = null; - if ($row['ezpolicy_original_id']) { - $originalId = (int)$row['ezpolicy_original_id']; - } elseif ($row['ezrole_version']) { - $originalId = (int)$policyId; - } - - $policies[$policyId] = new Policy( - [ - 'id' => (int)$policyId, - 'roleId' => (int)$row['ezrole_id'], - 'originalId' => $originalId, - 'module' => $row['ezpolicy_module_name'], - 'function' => $row['ezpolicy_function_name'], - 'limitations' => '*', // limitations must be '*' if not a non empty array of limitations - ] - ); - } - - if (!$row['ezpolicy_limitation_identifier']) { - continue; - } elseif ($policies[$policyId]->limitations === '*') { - $policies[$policyId]->limitations = []; - } - - if (!isset($policies[$policyId]->limitations[$row['ezpolicy_limitation_identifier']])) { - $policies[$policyId]->limitations[$row['ezpolicy_limitation_identifier']] = [$row['ezpolicy_limitation_value_value']]; - } elseif (!in_array($row['ezpolicy_limitation_value_value'], $policies[$policyId]->limitations[$row['ezpolicy_limitation_identifier']])) { - $policies[$policyId]->limitations[$row['ezpolicy_limitation_identifier']][] = $row['ezpolicy_limitation_value_value']; - } - } - - return array_values($policies); - } - - /** - * Map role data to a role. - * - * @return \eZ\Publish\SPI\Persistence\User\Role - */ - public function mapRole(array $data) - { - $role = new Role(); - - foreach ($data as $row) { - if (empty($role->id)) { - $role->id = (int)$row['ezrole_id']; - $role->identifier = $row['ezrole_name']; - $role->status = $row['ezrole_version'] != 0 ? Role::STATUS_DRAFT : Role::STATUS_DEFINED; - $role->originalId = $row['ezrole_version'] ? (int)$row['ezrole_version'] : Role::STATUS_DEFINED; - // skip name and description as they don't exist in legacy - } - } - - $role->policies = $this->mapPolicies($data); - - return $role; - } - - /** - * Map data for a set of roles. - * - * @return \eZ\Publish\SPI\Persistence\User\Role[] - */ - public function mapRoles(array $data) - { - $roleData = []; - foreach ($data as $row) { - $roleData[$row['ezrole_id']][] = $row; - } - - $roles = []; - foreach ($roleData as $data) { - $roles[] = $this->mapRole($data); - } - - return $roles; - } - - /** - * Map data for a set of role assignments. - * - * @param array $data - * - * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment[] - */ - public function mapRoleAssignments(array $data) - { - $roleAssignmentData = []; - foreach ($data as $row) { - $id = (int)$row['id']; - $roleId = (int)$row['role_id']; - $contentId = (int)$row['contentobject_id']; - // if user already have full access to a role, continue - if (isset($roleAssignmentData[$roleId][$contentId]) - && $roleAssignmentData[$roleId][$contentId] instanceof RoleAssignment) { - continue; - } - - $limitIdentifier = $row['limit_identifier']; - if (!empty($limitIdentifier)) { - $roleAssignmentData[$roleId][$contentId][$limitIdentifier][$id] = new RoleAssignment( - [ - 'id' => $id, - 'roleId' => $roleId, - 'contentId' => $contentId, - 'limitationIdentifier' => $limitIdentifier, - 'values' => [$row['limit_value']], - ] - ); - } else { - $roleAssignmentData[$roleId][$contentId] = new RoleAssignment( - [ - 'id' => $id, - 'roleId' => $roleId, - 'contentId' => $contentId, - ] - ); - } - } - - $roleAssignments = []; - array_walk_recursive( - $roleAssignmentData, - static function ($roleAssignment) use (&$roleAssignments) { - $roleAssignments[] = $roleAssignment; - } - ); - - return $roleAssignments; - } - - /** - * Creates a create struct from an existing $role. - * - * @param \eZ\Publish\SPI\Persistence\User\Role $role - * - * @return \eZ\Publish\SPI\Persistence\User\RoleCreateStruct - */ - public function createCreateStructFromRole(Role $role) - { - $createStruct = new RoleCreateStruct(); - - $createStruct->identifier = $role->identifier; - $createStruct->policies = $role->policies; - - return $createStruct; - } - - /** - * Maps properties from $struct to $role. - * - * @param \eZ\Publish\SPI\Persistence\User\RoleCreateStruct $createStruct - * - * @return \eZ\Publish\SPI\Persistence\User\Role - */ - public function createRoleFromCreateStruct(RoleCreateStruct $createStruct) - { - $role = new Role(); - - $role->identifier = $createStruct->identifier; - $role->policies = $createStruct->policies; - $role->status = Role::STATUS_DRAFT; - - return $role; - } - - /** - * Maps properties from $struct to $role. - */ - public function createRoleFromCopyStruct(User\RoleCopyStruct $copyStruct): Role - { - $role = new Role(); - - $role->identifier = $copyStruct->newIdentifier; - $role->policies = $copyStruct->policies; - $role->status = Role::STATUS_DEFINED; - - return $role; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/User/Role/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/User/Role/Gateway.php deleted file mode 100644 index 45da2b19b5..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/User/Role/Gateway.php +++ /dev/null @@ -1,169 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\User\Role; - -use eZ\Publish\SPI\Persistence\User\Policy; -use eZ\Publish\SPI\Persistence\User\Role; -use eZ\Publish\SPI\Persistence\User\RoleUpdateStruct; - -/** - * User Role Gateway. - * - * @internal For internal use by Persistence Handlers. - */ -abstract class Gateway -{ - public const ROLE_TABLE = 'ezrole'; - public const POLICY_TABLE = 'ezpolicy'; - public const POLICY_LIMITATION_TABLE = 'ezpolicy_limitation'; - public const POLICY_LIMITATION_VALUE_TABLE = 'ezpolicy_limitation_value'; - public const USER_ROLE_TABLE = 'ezuser_role'; - public const ROLE_SEQ = 'ezrole_id_seq'; - public const POLICY_SEQ = 'ezpolicy_id_seq'; - public const POLICY_LIMITATION_SEQ = 'ezpolicy_limitation_id_seq'; - - /** - * Create a new role. - */ - abstract public function createRole(Role $role): Role; - - /** - * Copy an existing role. - */ - abstract public function copyRole(Role $role): Role; - - /** - * Load a specified role by $roleId. - * - * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT - * - * @return array - */ - abstract public function loadRole(int $roleId, int $status = Role::STATUS_DEFINED): array; - - /** - * Load a specified role by $identifier. - * - * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT - */ - abstract public function loadRoleByIdentifier( - string $identifier, - int $status = Role::STATUS_DEFINED - ): array; - - /** - * Load a role draft by the original role ID. - * - * @param int $roleId ID of the role the draft was created from. - */ - abstract public function loadRoleDraftByRoleId(int $roleId): array; - - /** - * Load all roles. - * - * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT - */ - abstract public function loadRoles(int $status = Role::STATUS_DEFINED): array; - - /** - * Load all roles associated with the given Content items. - * - * @param int[] $contentIds - * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT - */ - abstract public function loadRolesForContentObjects( - array $contentIds, - int $status = Role::STATUS_DEFINED - ): array; - - /** - * Load a role assignment for specified assignment ID. - */ - abstract public function loadRoleAssignment(int $roleAssignmentId): array; - - /** - * Load role assignment for specified User Group Content ID. - */ - abstract public function loadRoleAssignmentsByGroupId( - int $groupId, - bool $inherited = false - ): array; - - /** - * Load a Role assignments for given Role ID. - */ - abstract public function loadRoleAssignmentsByRoleId(int $roleId): array; - - /** - * Load a Role assignments for given Role ID with provided $offset and $limit arguments. - */ - abstract public function loadRoleAssignmentsByRoleIdWithOffsetAndLimit( - int $roleId, - int $offset, - ?int $limit - ): array; - - /** - * Count Role's assignments taking into consideration related and existing user and user group objects. - */ - abstract public function countRoleAssignments(int $roleId): int; - - /** - * Return User Policies data associated with User. - * - * @return array - */ - abstract public function loadPoliciesByUserId(int $userId): array; - - /** - * Update role (draft). - * - * Will not throw anything if location id is invalid. - */ - abstract public function updateRole(RoleUpdateStruct $role): void; - - /** - * Delete the specified role (draft). - * If it's not a draft, the role assignments will also be deleted. - * - * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT - */ - abstract public function deleteRole(int $roleId, int $status = Role::STATUS_DEFINED): void; - - /** - * Publish the specified role draft. - * If the draft was created from an existing role, published version will take the original role ID. - * - * @param int|null $originalRoleId ID of role the draft was created from. Will be null - * if the role draft was completely new. - */ - abstract public function publishRoleDraft(int $roleDraftId, ?int $originalRoleId = null): void; - - /** - * Add a Policy to Role. - */ - abstract public function addPolicy(int $roleId, Policy $policy): Policy; - - /** - * Add Limitations to an existing Policy. - * - * @param array $limitations a map of Limitation identifiers to their raw values - */ - abstract public function addPolicyLimitations(int $policyId, array $limitations): void; - - /** - * Remove a Policy from Role. - */ - abstract public function removePolicy(int $policyId): void; - - /** - * Remove a Policy from Role. - */ - abstract public function removePolicyLimitations(int $policyId): void; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php deleted file mode 100644 index ad5c84be99..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,799 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway; -use eZ\Publish\SPI\Persistence\User\Policy; -use eZ\Publish\SPI\Persistence\User\Role; -use eZ\Publish\SPI\Persistence\User\RoleUpdateStruct; - -/** - * User Role gateway implementation using the Doctrine database. - * - * @internal Gateway implementation is considered internal. Use Persistence User Handler instead. - * - * @see \eZ\Publish\SPI\Persistence\User\Handler - */ -final class DoctrineDatabase extends Gateway -{ - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */ - private $dbPlatform; - - /** - * Construct from database handler. - * - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct(Connection $connection) - { - $this->connection = $connection; - $this->dbPlatform = $this->connection->getDatabasePlatform(); - } - - public function createRole(Role $role): Role - { - // Role original ID is set when creating a draft from an existing role - if ($role->status === Role::STATUS_DRAFT && $role->id) { - $roleOriginalId = $role->id; - } elseif ($role->status === Role::STATUS_DRAFT) { - // Not using a constant here as this is legacy storage engine specific. - // -1 means "Newly created role". - $roleOriginalId = -1; - } else { - // Role::STATUS_DEFINED value is 0, which is the expected value for version column for this status. - $roleOriginalId = Role::STATUS_DEFINED; - } - - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::ROLE_TABLE) - ->values( - [ - 'is_new' => $query->createPositionalParameter(0, ParameterType::INTEGER), - 'name' => $query->createPositionalParameter( - $role->identifier, - ParameterType::STRING - ), - 'value' => $query->createPositionalParameter(0, ParameterType::INTEGER), - // BC: "version" stores originalId when creating a draft from an existing role - 'version' => $query->createPositionalParameter( - $roleOriginalId, - ParameterType::STRING - ), - ] - ); - $query->execute(); - - if (!isset($role->id) || (int)$role->id < 1 || $role->status === Role::STATUS_DRAFT) { - $role->id = (int)$this->connection->lastInsertId(self::ROLE_SEQ); - } - - $role->originalId = $roleOriginalId; - - return $role; - } - - public function copyRole(Role $role): Role - { - $status = $role->status; - - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::ROLE_TABLE) - ->values( - [ - 'is_new' => $query->createPositionalParameter(0, ParameterType::INTEGER), - 'name' => $query->createPositionalParameter( - $role->identifier, - ParameterType::STRING - ), - 'value' => $query->createPositionalParameter(0, ParameterType::INTEGER), - // BC: "version" stores originalId when creating a draft from an existing role - 'version' => $query->createPositionalParameter( - $status, - ParameterType::STRING - ), - ] - ); - $query->execute(); - - $role->id = (int)$this->connection->lastInsertId(self::ROLE_SEQ); - - return $role; - } - - private function getLoadRoleQueryBuilder(): QueryBuilder - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - 'r.id AS ezrole_id', - 'r.name AS ezrole_name', - 'r.version AS ezrole_version', - 'p.id AS ezpolicy_id', - 'p.function_name AS ezpolicy_function_name', - 'p.module_name AS ezpolicy_module_name', - 'p.original_id AS ezpolicy_original_id', - 'l.identifier AS ezpolicy_limitation_identifier', - 'v.value AS ezpolicy_limitation_value_value' - ) - ->from(self::ROLE_TABLE, 'r') - ->leftJoin('r', self::POLICY_TABLE, 'p', 'p.role_id = r.id') - ->leftJoin('p', self::POLICY_LIMITATION_TABLE, 'l', 'l.policy_id = p.id') - ->leftJoin('l', self::POLICY_LIMITATION_VALUE_TABLE, 'v', 'v.limitation_id = l.id'); - - return $query; - } - - public function loadRole(int $roleId, int $status = Role::STATUS_DEFINED): array - { - $query = $this->getLoadRoleQueryBuilder(); - $query - ->where( - $query->expr()->eq( - 'r.id', - $query->createPositionalParameter($roleId, ParameterType::INTEGER) - ) - ) - ->andWhere( - $this->buildRoleDraftQueryConstraint($status, $query) - ) - ->orderBy('p.id', 'ASC'); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadRoleByIdentifier( - string $identifier, - int $status = Role::STATUS_DEFINED - ): array { - $query = $this->getLoadRoleQueryBuilder(); - $query - ->where( - $query->expr()->eq( - 'r.name', - $query->createPositionalParameter($identifier, ParameterType::STRING) - ) - ) - ->andWhere( - $this->buildRoleDraftQueryConstraint($status, $query) - ) - ->orderBy('p.id', 'ASC'); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadRoleDraftByRoleId(int $roleId): array - { - $query = $this->getLoadRoleQueryBuilder(); - // BC: "version" stores originalId when creating a draft from an existing role - $query - ->where( - $query->expr()->eq( - 'r.version', - $query->createPositionalParameter($roleId, ParameterType::STRING) - ) - ) - ->orderBy('p.id', 'ASC'); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadRoles(int $status = Role::STATUS_DEFINED): array - { - $query = $this->getLoadRoleQueryBuilder(); - $query->where( - $this->buildRoleDraftQueryConstraint($status, $query) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadRolesForContentObjects( - array $contentIds, - int $status = Role::STATUS_DEFINED - ): array { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->select( - 'ur.contentobject_id AS ezuser_role_contentobject_id', - 'r.id AS ezrole_id', - 'r.name AS ezrole_name', - 'r.version AS ezrole_version', - 'p.id AS ezpolicy_id', - 'p.function_name AS ezpolicy_function_name', - 'p.module_name AS ezpolicy_module_name', - 'p.original_id AS ezpolicy_original_id', - 'l.identifier AS ezpolicy_limitation_identifier', - 'v.value AS ezpolicy_limitation_value_value' - ) - ->from(self::USER_ROLE_TABLE, 'urs') - ->leftJoin( - 'urs', - self::ROLE_TABLE, - 'r', - // BC: for drafts the "version" column contains the original role ID - $expr->eq( - $status === Role::STATUS_DEFINED ? 'r.id' : 'r.version', - 'urs.role_id' - ) - ) - ->leftJoin('r', self::USER_ROLE_TABLE, 'ur', 'ur.role_id = r.id') - ->leftJoin('r', self::POLICY_TABLE, 'p', 'p.role_id = r.id') - ->leftJoin('p', self::POLICY_LIMITATION_TABLE, 'l', 'l.policy_id = p.id') - ->leftJoin('l', self::POLICY_LIMITATION_VALUE_TABLE, 'v', 'v.limitation_id = l.id') - ->where( - $expr->in( - 'urs.contentobject_id', - $query->createPositionalParameter($contentIds, Connection::PARAM_INT_ARRAY) - ) - ); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadRoleAssignment(int $roleAssignmentId): array - { - $query = $this->connection->createQueryBuilder(); - $query->select( - 'id', - 'contentobject_id', - 'limit_identifier', - 'limit_value', - 'role_id' - )->from( - self::USER_ROLE_TABLE - )->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($roleAssignmentId, ParameterType::INTEGER) - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadRoleAssignmentsByGroupId(int $groupId, bool $inherited = false): array - { - $query = $this->connection->createQueryBuilder(); - $query->select( - 'id', - 'contentobject_id', - 'limit_identifier', - 'limit_value', - 'role_id' - )->from( - self::USER_ROLE_TABLE - ); - - if ($inherited) { - $groupIds = $this->fetchUserGroups($groupId); - $groupIds[] = $groupId; - $query->where( - $query->expr()->in( - 'contentobject_id', - $groupIds - ) - ); - } else { - $query->where( - $query->expr()->eq( - 'contentobject_id', - $query->createPositionalParameter($groupId, ParameterType::INTEGER) - ) - ); - } - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function loadRoleAssignmentsByRoleId(int $roleId): array - { - $query = $this->connection->createQueryBuilder(); - $query->select( - 'id', - 'contentobject_id', - 'limit_identifier', - 'limit_value', - 'role_id' - )->from( - self::USER_ROLE_TABLE - )->where( - $query->expr()->eq( - 'role_id', - $query->createPositionalParameter($roleId, ParameterType::INTEGER) - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } - - /** - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \Doctrine\DBAL\Exception - */ - public function loadRoleAssignmentsByRoleIdWithOffsetAndLimit(int $roleId, int $offset, ?int $limit): array - { - $query = $this - ->buildLoadRoleAssignmentsQuery( - [ - 'user_role.id', - 'user_role.contentobject_id', - 'user_role.limit_identifier', - 'user_role.limit_value', - 'user_role.role_id', - ], - $roleId - ) - ->setFirstResult($offset); - - if ($limit !== null) { - $query->setMaxResults($limit); - } - - return $query - ->execute() - ->fetchAllAssociative(); - } - - /** - * @throws \Doctrine\DBAL\Driver\Exception - * @throws \Doctrine\DBAL\Exception - */ - public function countRoleAssignments(int $roleId): int - { - $query = $this->buildLoadRoleAssignmentsQuery( - [$this->connection->getDatabasePlatform()->getCountExpression('user_role.id')], - $roleId - ); - - return (int)$query->execute()->fetchOne(); - } - - /** - * @param array<string> $columns - */ - private function buildLoadRoleAssignmentsQuery(array $columns, int $roleId): QueryBuilder - { - $query = $this->connection->createQueryBuilder(); - $query - ->select(...$columns) - ->from(self::USER_ROLE_TABLE, 'user_role') - ->innerJoin( - 'user_role', - ContentGateway::CONTENT_ITEM_TABLE, - 'content_object', - 'user_role.contentobject_id = content_object.id' - )->where( - $query->expr()->eq( - 'role_id', - $query->createPositionalParameter($roleId, ParameterType::INTEGER) - ) - ); - - return $query; - } - - public function loadPoliciesByUserId(int $userId): array - { - $groupIds = $this->fetchUserGroups($userId); - $groupIds[] = $userId; - - return $this->loadRolesForContentObjects($groupIds); - } - - /** - * Fetch all group IDs the user belongs to. - * - * This method will return Content ids of all ancestor Locations for the given $userId. - * Note that not all of these might be used as user groups, - * but we will need to check all of them. - * - * @param int $userId - * - * @return array - */ - private function fetchUserGroups(int $userId): array - { - $nodeIDs = $this->getAncestorLocationIdsForUser($userId); - - if (empty($nodeIDs)) { - return []; - } - - $query = $this->connection->createQueryBuilder(); - $query - ->select('c.id') - ->from('ezcontentobject_tree', 't') - ->innerJoin('t', 'ezcontentobject', 'c', 'c.id = t.contentobject_id') - ->where( - $query->expr()->in( - 't.node_id', - $nodeIDs - ) - ); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::COLUMN); - } - - public function updateRole(RoleUpdateStruct $role): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::ROLE_TABLE) - ->set( - 'name', - $query->createPositionalParameter($role->identifier, ParameterType::STRING) - ) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($role->id, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - public function deleteRole(int $roleId, int $status = Role::STATUS_DEFINED): void - { - $query = $this->connection->createQueryBuilder(); - $expr = $query->expr(); - $query - ->delete(self::ROLE_TABLE) - ->where( - $expr->eq( - 'id', - $query->createPositionalParameter($roleId, ParameterType::INTEGER) - ) - ) - ->andWhere( - $this->buildRoleDraftQueryConstraint($status, $query, self::ROLE_TABLE) - ); - - if ($status !== Role::STATUS_DRAFT) { - $this->deleteRoleAssignments($roleId); - } - $query->execute(); - } - - public function publishRoleDraft(int $roleDraftId, ?int $originalRoleId = null): void - { - $this->markRoleAsPublished($roleDraftId, $originalRoleId); - $this->publishRolePolicies($roleDraftId, $originalRoleId); - } - - public function addPolicy(int $roleId, Policy $policy): Policy - { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::POLICY_TABLE) - ->values( - [ - 'function_name' => $query->createPositionalParameter( - $policy->function, - ParameterType::STRING - ), - 'module_name' => $query->createPositionalParameter( - $policy->module, - ParameterType::STRING - ), - 'original_id' => $query->createPositionalParameter( - $policy->originalId ?? 0, - ParameterType::INTEGER - ), - 'role_id' => $query->createPositionalParameter($roleId, ParameterType::INTEGER), - ] - ); - $query->execute(); - - $policy->id = (int)$this->connection->lastInsertId(self::POLICY_SEQ); - $policy->roleId = $roleId; - - // Handle the only valid non-array value "*" by not inserting - // anything. Still has not been documented by eZ Systems. So we - // assume this is the right way to handle it. - if (is_array($policy->limitations)) { - $this->addPolicyLimitations($policy->id, $policy->limitations); - } - - return $policy; - } - - public function addPolicyLimitations(int $policyId, array $limitations): void - { - foreach ($limitations as $identifier => $values) { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::POLICY_LIMITATION_TABLE) - ->values( - [ - 'identifier' => $query->createPositionalParameter( - $identifier, - ParameterType::STRING - ), - 'policy_id' => $query->createPositionalParameter( - $policyId, - ParameterType::INTEGER - ), - ] - ); - $query->execute(); - - $limitationId = (int)$this->connection->lastInsertId(self::POLICY_LIMITATION_SEQ); - - foreach ($values as $value) { - $query = $this->connection->createQueryBuilder(); - $query - ->insert(self::POLICY_LIMITATION_VALUE_TABLE) - ->values( - [ - 'value' => $query->createPositionalParameter( - $value, - ParameterType::STRING - ), - 'limitation_id' => $query->createPositionalParameter( - $limitationId, - ParameterType::INTEGER - ), - ] - ); - $query->execute(); - } - } - } - - public function removePolicy(int $policyId): void - { - $this->removePolicyLimitations($policyId); - - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::POLICY_TABLE) - ->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($policyId, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - /** - * @param int[] $limitationIds - */ - private function deletePolicyLimitations(array $limitationIds): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::POLICY_LIMITATION_TABLE) - ->where( - $query->expr()->in( - 'id', - $query->createPositionalParameter( - $limitationIds, - Connection::PARAM_INT_ARRAY - ) - ) - ); - $query->execute(); - } - - /** - * @param int[] $limitationValueIds - */ - private function deletePolicyLimitationValues(array $limitationValueIds): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::POLICY_LIMITATION_VALUE_TABLE) - ->where( - $query->expr()->in( - 'id', - $query->createPositionalParameter( - $limitationValueIds, - Connection::PARAM_INT_ARRAY - ) - ) - ); - $query->execute(); - } - - private function loadPolicyLimitationValues(int $policyId): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - 'l.id AS ezpolicy_limitation_id', - 'v.id AS ezpolicy_limitation_value_id' - ) - ->from(self::POLICY_TABLE, 'p') - ->leftJoin('p', self::POLICY_LIMITATION_TABLE, 'l', 'l.policy_id = p.id') - ->leftJoin('l', self::POLICY_LIMITATION_VALUE_TABLE, 'v', 'v.limitation_id = l.id') - ->where( - $query->expr()->eq( - 'p.id', - $query->createPositionalParameter($policyId, ParameterType::INTEGER) - ) - ); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - public function removePolicyLimitations(int $policyId): void - { - $limitationValues = $this->loadPolicyLimitationValues($policyId); - - $limitationIds = array_map( - 'intval', - array_column($limitationValues, 'ezpolicy_limitation_id') - ); - $limitationValueIds = array_map( - 'intval', - array_column($limitationValues, 'ezpolicy_limitation_value_id') - ); - - if (!empty($limitationValueIds)) { - $this->deletePolicyLimitationValues($limitationValueIds); - } - - if (!empty($limitationIds)) { - $this->deletePolicyLimitations($limitationIds); - } - } - - /** - * Delete Role assignments to Users. - */ - private function deleteRoleAssignments(int $roleId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->delete(self::USER_ROLE_TABLE) - ->where( - $query->expr()->eq( - 'role_id', - $query->createPositionalParameter($roleId, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - /** - * Load all Ancestor Location IDs of the given User Location. - * - * @param int $userId - * - * @return int[] - */ - private function getAncestorLocationIdsForUser(int $userId): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('t.path_string') - ->from('ezcontentobject_tree', 't') - ->where( - $query->expr()->eq( - 't.contentobject_id', - $query->createPositionalParameter($userId, ParameterType::STRING) - ) - ); - - $paths = $query->execute()->fetchAll(FetchMode::COLUMN); - $nodeIds = array_unique( - array_reduce( - array_map( - static function ($pathString) { - return array_filter(explode('/', $pathString)); - }, - $paths - ), - 'array_merge_recursive', - [] - ) - ); - - return array_map('intval', $nodeIds); - } - - private function buildRoleDraftQueryConstraint( - int $status, - QueryBuilder $query, - string $columnAlias = 'r' - ): string { - if ($status === Role::STATUS_DEFINED) { - $draftCondition = $query->expr()->eq( - "{$columnAlias}.version", - $query->createPositionalParameter($status, ParameterType::INTEGER) - ); - } else { - // version stores original Role ID when Role is a draft... - $draftCondition = $query->expr()->neq( - "{$columnAlias}.version", - $query->createPositionalParameter(Role::STATUS_DEFINED, ParameterType::INTEGER) - ); - } - - return $draftCondition; - } - - private function markRoleAsPublished(int $roleDraftId, ?int $originalRoleId): void - { - $query = $this->connection->createQueryBuilder(); - $query - ->update(self::ROLE_TABLE) - ->set( - 'version', - $query->createPositionalParameter(Role::STATUS_DEFINED, ParameterType::INTEGER) - ); - // Draft was created from an existing role, so published role must get the original ID. - if ($originalRoleId !== null) { - $query->set( - 'id', - $query->createPositionalParameter($originalRoleId, ParameterType::INTEGER) - ); - } - - $query->where( - $query->expr()->eq( - 'id', - $query->createPositionalParameter($roleDraftId, ParameterType::INTEGER) - ) - ); - $query->execute(); - } - - private function publishRolePolicies(int $roleDraftId, ?int $originalRoleId): void - { - $policyQuery = $this->connection->createQueryBuilder(); - $policyQuery - ->update(self::POLICY_TABLE) - ->set( - 'original_id', - $policyQuery->createPositionalParameter(0, ParameterType::INTEGER) - ); - // Draft was created from an existing role, so published policies must get the original role ID. - if ($originalRoleId !== null) { - $policyQuery->set( - 'role_id', - $policyQuery->createPositionalParameter($originalRoleId, ParameterType::INTEGER) - ); - } - - $policyQuery->where( - $policyQuery->expr()->eq( - 'role_id', - $policyQuery->createPositionalParameter($roleDraftId, ParameterType::INTEGER) - ) - ); - $policyQuery->execute(); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php deleted file mode 100644 index a8a454def8..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,224 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway; -use eZ\Publish\SPI\Persistence\User\Policy; -use eZ\Publish\SPI\Persistence\User\Role; -use eZ\Publish\SPI\Persistence\User\RoleUpdateStruct; -use PDOException; - -/** - * @internal Internal exception conversion layer. - */ -final class ExceptionConversion extends Gateway -{ - /** - * The wrapped gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway - */ - private $innerGateway; - - /** - * Creates a new exception conversion gateway around $innerGateway. - * - * @param \eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function createRole(Role $role): Role - { - try { - return $this->innerGateway->createRole($role); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function copyRole(Role $role): Role - { - try { - return $this->innerGateway->copyRole($role); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadRole(int $roleId, int $status = Role::STATUS_DEFINED): array - { - try { - return $this->innerGateway->loadRole($roleId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadRoleByIdentifier( - string $identifier, - int $status = Role::STATUS_DEFINED - ): array { - try { - return $this->innerGateway->loadRoleByIdentifier($identifier, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadRoleDraftByRoleId(int $roleId): array - { - try { - return $this->innerGateway->loadRoleDraftByRoleId($roleId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadRoles(int $status = Role::STATUS_DEFINED): array - { - try { - return $this->innerGateway->loadRoles(); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadRolesForContentObjects( - array $contentIds, - int $status = Role::STATUS_DEFINED - ): array { - try { - return $this->innerGateway->loadRolesForContentObjects($contentIds); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadRoleAssignment(int $roleAssignmentId): array - { - try { - return $this->innerGateway->loadRoleAssignment($roleAssignmentId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadRoleAssignmentsByGroupId(int $groupId, bool $inherited = false): array - { - try { - return $this->innerGateway->loadRoleAssignmentsByGroupId($groupId, $inherited); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadRoleAssignmentsByRoleId(int $roleId): array - { - try { - return $this->innerGateway->loadRoleAssignmentsByRoleId($roleId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadRoleAssignmentsByRoleIdWithOffsetAndLimit(int $roleId, int $offset, ?int $limit): array - { - try { - return $this->innerGateway->loadRoleAssignmentsByRoleIdWithOffsetAndLimit($roleId, $offset, $limit); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function countRoleAssignments(int $roleId): int - { - try { - return $this->innerGateway->countRoleAssignments($roleId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function loadPoliciesByUserId(int $userId): array - { - try { - return $this->innerGateway->loadPoliciesByUserId($userId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function updateRole(RoleUpdateStruct $role): void - { - try { - $this->innerGateway->updateRole($role); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function deleteRole(int $roleId, int $status = Role::STATUS_DEFINED): void - { - try { - $this->innerGateway->deleteRole($roleId, $status); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function publishRoleDraft(int $roleDraftId, ?int $originalRoleId = null): void - { - try { - $this->innerGateway->publishRoleDraft($roleDraftId, $originalRoleId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function addPolicy(int $roleId, Policy $policy): Policy - { - try { - return $this->innerGateway->addPolicy($roleId, $policy); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function addPolicyLimitations(int $policyId, array $limitations): void - { - try { - $this->innerGateway->addPolicyLimitations($policyId, $limitations); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function removePolicy(int $policyId): void - { - try { - $this->innerGateway->removePolicy($policyId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } - - public function removePolicyLimitations(int $policyId): void - { - try { - $this->innerGateway->removePolicyLimitations($policyId); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/User/Role/LimitationConverter.php b/eZ/Publish/Core/Persistence/Legacy/User/Role/LimitationConverter.php deleted file mode 100644 index b072b310cf..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/User/Role/LimitationConverter.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\User\Role; - -use eZ\Publish\SPI\Persistence\User\Policy; - -/** - * Limitation converter. - * - * Takes care of Converting a Policy limitation from Legacy value to spi value accepted by API. - */ -class LimitationConverter -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationHandler[] */ - protected $limitationHandlers; - - /** - * Construct from LimitationConverter. - * - * @param \eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationHandler[] $limitationHandlers - */ - public function __construct(array $limitationHandlers = []) - { - $this->limitationHandlers = $limitationHandlers; - } - - /** - * Adds handler. - * - * @param \eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationHandler $handler - */ - public function addHandler(LimitationHandler $handler) - { - $this->limitationHandlers[] = $handler; - } - - /** - * @param \eZ\Publish\SPI\Persistence\User\Policy $policy - */ - public function toLegacy(Policy $policy) - { - foreach ($this->limitationHandlers as $limitationHandler) { - $limitationHandler->toLegacy($policy); - } - } - - /** - * @param \eZ\Publish\SPI\Persistence\User\Policy $policy - */ - public function toSPI(Policy $policy) - { - foreach ($this->limitationHandlers as $limitationHandler) { - $limitationHandler->toSPI($policy); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/User/Role/LimitationHandler.php b/eZ/Publish/Core/Persistence/Legacy/User/Role/LimitationHandler.php deleted file mode 100644 index 4685a55e4d..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/User/Role/LimitationHandler.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\User\Role; - -use Doctrine\DBAL\Connection; -use eZ\Publish\SPI\Persistence\User\Policy; - -/** - * Takes care of Converting a Policy limitation from Legacy value to spi value accepted by API. - */ -abstract class LimitationHandler -{ - protected $connection; - - public function __construct(Connection $connection) - { - $this->connection = $connection; - } - - abstract public function toLegacy(Policy $policy): void; - - abstract public function toSPI(Policy $policy): void; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/User/Role/LimitationHandler/ObjectStateHandler.php b/eZ/Publish/Core/Persistence/Legacy/User/Role/LimitationHandler/ObjectStateHandler.php deleted file mode 100644 index 4f3549f935..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/User/Role/LimitationHandler/ObjectStateHandler.php +++ /dev/null @@ -1,134 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationHandler; -use eZ\Publish\SPI\Persistence\User\Policy; - -/** - * Limitation Handler. - * - * Takes care of Converting a Policy limitation from Legacy value to spi value accepted by API. - */ -class ObjectStateHandler extends LimitationHandler -{ - public const STATE_GROUP = 'StateGroup_'; - - /** - * Translate API STATE limitation to Legacy StateGroup_<identifier> limitations. - */ - public function toLegacy(Policy $policy): void - { - if ($policy->limitations !== '*' && isset($policy->limitations[Limitation::STATE])) { - if ($policy->limitations[Limitation::STATE] === '*') { - $map = array_fill_keys(array_keys($this->getGroupMap()), '*'); - } else { - $map = $this->getGroupMap($policy->limitations[Limitation::STATE]); - } - $policy->limitations += $map; - unset($policy->limitations[Limitation::STATE]); - } - } - - /** - * Translate Legacy StateGroup_<identifier> limitations to API STATE limitation. - */ - public function toSPI(Policy $policy): void - { - if ($policy->limitations === '*' || empty($policy->limitations)) { - return; - } - - // First iterate to prepare for the range of possible conditions below - $hasStateGroup = false; - $allWildCard = true; - $someWildCard = false; - $map = []; - foreach ($policy->limitations as $identifier => $limitationsValues) { - if (strncmp($identifier, self::STATE_GROUP, 11) === 0) { - $hasStateGroup = true; - if ($limitationsValues !== '*') { - $allWildCard = false; - } else { - $someWildCard = true; - } - - $map[$identifier] = $limitationsValues; - unset($policy->limitations[$identifier]); - } - } - - if (!$hasStateGroup) { - return; - } - - if ($allWildCard) { - $policy->limitations[Limitation::STATE] = '*'; - - return; - } - - if ($someWildCard) { - $fullMap = $this->getGroupMap(); - foreach ($map as $identifier => $limitationsValues) { - if ($limitationsValues === '*') { - $map[$identifier] = $fullMap[$identifier]; - } - } - } - - $policy->limitations[Limitation::STATE] = []; - foreach ($map as $limitationValues) { - $policy->limitations[Limitation::STATE] = array_merge( - $policy->limitations[Limitation::STATE], - $limitationValues - ); - } - } - - /** - * Query for groups identifiers and id's. - */ - protected function getGroupMap(array $limitIds = null): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select('sg.identifier', 's.id') - ->from('ezcobj_state', 's') - ->innerJoin( - 's', - 'ezcobj_state_group', - 'sg', - 's.group_id = sg.id' - ); - - if ($limitIds !== null) { - $query->where( - $query->expr()->in( - 's.id', - $query->createPositionalParameter( - array_map('intval', $limitIds), - Connection::PARAM_INT_ARRAY - ) - ) - ); - } - - $statement = $query->execute(); - - $map = []; - $groupValues = $statement->fetchAll(FetchMode::ASSOCIATIVE); - foreach ($groupValues as $groupValue) { - $map[self::STATE_GROUP . $groupValue['identifier']][] = (int)$groupValue['id']; - } - - return $map; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/UserPreference/Gateway.php b/eZ/Publish/Core/Persistence/Legacy/UserPreference/Gateway.php deleted file mode 100644 index fb4eddc973..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/UserPreference/Gateway.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\UserPreference; - -use eZ\Publish\SPI\Persistence\UserPreference\UserPreferenceSetStruct; - -abstract class Gateway -{ - /** - * Store UserPreference ValueObject in persistent storage. - * - * @param \eZ\Publish\SPI\Persistence\UserPreference\UserPreferenceSetStruct $userPreference - * - * @return int - */ - abstract public function setUserPreference(UserPreferenceSetStruct $userPreference): int; - - /** - * Get UserPreference by its user ID and name. - * - * @param int $userId - * @param string $name - * - * @return array - */ - abstract public function getUserPreferenceByUserIdAndName(int $userId, string $name): array; - - /** - * @param int $userId - * - * @return int - */ - abstract public function countUserPreferences(int $userId): int; - - /** - * @param int $userId - * @param int $offset - * @param int $limit - * - * @return array - */ - abstract public function loadUserPreferences(int $userId, int $offset = 0, int $limit = -1): array; -} diff --git a/eZ/Publish/Core/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabase.php deleted file mode 100644 index 6bf047ce42..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,138 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreferenceSetStruct; - -class DoctrineDatabase extends Gateway -{ - public const TABLE_USER_PREFERENCES = 'ezpreferences'; - - public const COLUMN_ID = 'id'; - public const COLUMN_NAME = 'name'; - public const COLUMN_USER_ID = 'user_id'; - public const COLUMN_VALUE = 'value'; - - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - public function __construct(Connection $connection) - { - $this->connection = $connection; - } - - /** - * {@inheritdoc} - */ - public function setUserPreference(UserPreferenceSetStruct $userPreference): int - { - $query = $this->connection->createQueryBuilder(); - - $userPreferences = $this->getUserPreferenceByUserIdAndName($userPreference->userId, $userPreference->name); - - if (0 < count($userPreferences)) { - $currentUserPreference = reset($userPreferences); - $currentUserPreferenceId = (int)$currentUserPreference['id']; - - $query - ->update(self::TABLE_USER_PREFERENCES) - ->set(self::COLUMN_VALUE, ':value') - ->where($query->expr()->eq(self::COLUMN_ID, ':id')) - ->setParameter(':id', $currentUserPreferenceId, ParameterType::INTEGER) - ->setParameter(':value', $userPreference->value, ParameterType::STRING); - - $query->execute(); - - return $currentUserPreferenceId; - } - - $query - ->insert(self::TABLE_USER_PREFERENCES) - ->values([ - self::COLUMN_NAME => ':name', - self::COLUMN_USER_ID => ':user_id', - self::COLUMN_VALUE => ':value', - ]) - ->setParameter(':name', $userPreference->name, ParameterType::STRING) - ->setParameter(':user_id', $userPreference->userId, ParameterType::INTEGER) - ->setParameter(':value', $userPreference->value, ParameterType::STRING); - - $query->execute(); - - return (int) $this->connection->lastInsertId(); - } - - public function getUserPreferenceByUserIdAndName(int $userId, string $name): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select(...$this->getColumns()) - ->from(self::TABLE_USER_PREFERENCES) - ->where($query->expr()->eq(self::COLUMN_USER_ID, ':userId')) - ->andWhere($query->expr()->eq(self::COLUMN_NAME, ':name')); - - $query->setParameter(':userId', $userId, ParameterType::INTEGER); - $query->setParameter(':name', $name, ParameterType::STRING); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - /** - * {@inheritdoc} - */ - public function loadUserPreferences(int $userId, int $offset = 0, int $limit = -1): array - { - $query = $this->connection->createQueryBuilder(); - $query - ->select(...$this->getColumns()) - ->from(self::TABLE_USER_PREFERENCES) - ->where($query->expr()->eq(self::COLUMN_USER_ID, ':user_id')) - ->setFirstResult($offset); - - if ($limit > 0) { - $query->setMaxResults($limit); - } - - $query->orderBy(self::COLUMN_ID, 'ASC'); - $query->setParameter(':user_id', $userId, ParameterType::INTEGER); - - return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); - } - - /** - * {@inheritdoc} - */ - public function countUserPreferences(int $userId): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select( - $this->connection->getDatabasePlatform()->getCountExpression(self::COLUMN_ID) - ) - ->from(self::TABLE_USER_PREFERENCES) - ->where($query->expr()->eq(self::COLUMN_USER_ID, ':user_id')) - ->setParameter(':user_id', $userId, ParameterType::INTEGER); - - return (int) $query->execute()->fetchColumn(); - } - - private function getColumns(): array - { - return [ - self::COLUMN_ID, - self::COLUMN_NAME, - self::COLUMN_USER_ID, - self::COLUMN_VALUE, - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/UserPreference/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Persistence/Legacy/UserPreference/Gateway/ExceptionConversion.php deleted file mode 100644 index 3ef1c9f4e0..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/UserPreference/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreferenceSetStruct; -use PDOException; -use RuntimeException; - -class ExceptionConversion extends Gateway -{ - /** - * The wrapped gateway. - * - * @var \eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway - */ - protected $innerGateway; - - /** - * ExceptionConversion constructor. - * - * @param \eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - /** - * {@inheritdoc} - */ - public function getUserPreferenceByUserIdAndName(int $userId, string $name): array - { - try { - return $this->innerGateway->getUserPreferenceByUserIdAndName($userId, $name); - } catch (DBALException | PDOException $e) { - throw new RuntimeException('Database error', 0, $e); - } - } - - /** - * {@inheritdoc} - */ - public function countUserPreferences(int $userId): int - { - try { - return $this->innerGateway->countUserPreferences($userId); - } catch (DBALException | PDOException $e) { - throw new RuntimeException('Database error', 0, $e); - } - } - - /** - * {@inheritdoc} - */ - public function loadUserPreferences(int $userId, int $offset = 0, int $limit = -1): array - { - try { - return $this->innerGateway->loadUserPreferences($userId, $offset, $limit); - } catch (DBALException | PDOException $e) { - throw new RuntimeException('Database error', 0, $e); - } - } - - /** - * {@inheritdoc} - */ - public function setUserPreference(UserPreferenceSetStruct $setStruct): int - { - try { - return $this->innerGateway->setUserPreference($setStruct); - } catch (DBALException | PDOException $e) { - throw new RuntimeException('Database error', 0, $e); - } - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/UserPreference/Handler.php b/eZ/Publish/Core/Persistence/Legacy/UserPreference/Handler.php deleted file mode 100644 index 46e5667037..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/UserPreference/Handler.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\UserPreference; - -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\SPI\Persistence\UserPreference\Handler as HandlerInterface; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreference; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreferenceSetStruct; - -class Handler implements HandlerInterface -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway */ - protected $gateway; - - /** @var \eZ\Publish\Core\Persistence\Legacy\UserPreference\Mapper */ - protected $mapper; - - /** - * @param \eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway $gateway - * @param \eZ\Publish\Core\Persistence\Legacy\UserPreference\Mapper $mapper - */ - public function __construct(Gateway $gateway, Mapper $mapper) - { - $this->gateway = $gateway; - $this->mapper = $mapper; - } - - /** - * {@inheritdoc} - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - */ - public function setUserPreference(UserPreferenceSetStruct $setStruct): UserPreference - { - $this->gateway->setUserPreference($setStruct); - - return $this->getUserPreferenceByUserIdAndName($setStruct->userId, $setStruct->name); - } - - /** - * {@inheritdoc} - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - */ - public function getUserPreferenceByUserIdAndName(int $userId, string $name): UserPreference - { - $userPreference = $this->mapper->extractUserPreferencesFromRows( - $this->gateway->getUserPreferenceByUserIdAndName($userId, $name) - ); - - if (count($userPreference) < 1) { - throw new NotFoundException('User Preference', $userId . ',' . $name); - } - - return reset($userPreference); - } - - /** - * {@inheritdoc} - */ - public function countUserPreferences(int $userId): int - { - return $this->gateway->countUserPreferences($userId); - } - - /** - * {@inheritdoc} - */ - public function loadUserPreferences(int $userId, int $offset, int $limit): array - { - return $this->mapper->extractUserPreferencesFromRows( - $this->gateway->loadUserPreferences($userId, $offset, $limit) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/Legacy/UserPreference/Mapper.php b/eZ/Publish/Core/Persistence/Legacy/UserPreference/Mapper.php deleted file mode 100644 index 33f4c32bab..0000000000 --- a/eZ/Publish/Core/Persistence/Legacy/UserPreference/Mapper.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Legacy\UserPreference; - -use eZ\Publish\SPI\Persistence\UserPreference\UserPreference; - -class Mapper -{ - /** - * Extracts UserPreference objects from $rows. - * - * @param array $rows - * - * @return \eZ\Publish\SPI\Persistence\UserPreference\UserPreference[] - */ - public function extractUserPreferencesFromRows(array $rows): array - { - $userPreferences = []; - foreach ($rows as $row) { - $userPreferences[] = $this->extractUserPreferenceFromRow($row); - } - - return $userPreferences; - } - - /** - * Extract UserPreference object from $row. - * - * @param array $row - * - * @return \eZ\Publish\SPI\Persistence\UserPreference\UserPreference - */ - private function extractUserPreferenceFromRow(array $row): UserPreference - { - $userPreference = new UserPreference(); - $userPreference->id = (int)$row['id']; - $userPreference->userId = (int)$row['user_id']; - $userPreference->name = $row['name']; - $userPreference->value = $row['value']; - - return $userPreference; - } -} diff --git a/eZ/Publish/Core/Persistence/Readme.md b/eZ/Publish/Core/Persistence/Readme.md deleted file mode 100644 index 207ae47de7..0000000000 --- a/eZ/Publish/Core/Persistence/Readme.md +++ /dev/null @@ -1,12 +0,0 @@ -# Core Persistence - -Contains various implementations of SPI\Persistence, and common libraries for use by them. - -Folder | Description -------------------------|------------ -Cache | SPI Persistence implementation for cache using Symfony Cache (decorated) -Database | Interfaces for emulating Zeta Components Database as it was used in the beginning (planned to be removed) -Doctrine | Doctrine DBAL implementation of "Database" (planned to be removed in favour of direct Doctrine use) -Legacy | SPI Persistence implementation for sql database as used in eZ Publish 4.x "legacy" -Test | Test for common functionality in this folder, including TransformationProcessor (todo: move to ./Common) -TransformationProcessor | Common transformation processor for string transformation use by Storage and Search engines. diff --git a/eZ/Publish/Core/Persistence/Tests/FieldTypeRegistryTest.php b/eZ/Publish/Core/Persistence/Tests/FieldTypeRegistryTest.php deleted file mode 100644 index cfd59f7b2c..0000000000 --- a/eZ/Publish/Core/Persistence/Tests/FieldTypeRegistryTest.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Tests; - -use eZ\Publish\Core\Persistence\FieldTypeRegistry; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType; -use eZ\Publish\SPI\Persistence\FieldType as SPIPersistenceFieldType; - -/** - * Test case for FieldTypeRegistry. - */ -class FieldTypeRegistryTest extends TestCase -{ - private const FIELD_TYPE_IDENTIFIER = 'some-type'; - - /** - * @covers \eZ\Publish\Core\Persistence\FieldTypeRegistry::__construct - */ - public function testConstructor(): void - { - $fieldType = $this->getFieldTypeMock(); - $registry = new FieldTypeRegistry([self::FIELD_TYPE_IDENTIFIER => $fieldType]); - - $this->assertInstanceOf( - SPIPersistenceFieldType::class, - $registry->getFieldType(self::FIELD_TYPE_IDENTIFIER) - ); - } - - /** - * @covers \eZ\Publish\Core\Persistence\FieldTypeRegistry::getFieldType - */ - public function testGetFieldTypeInstance() - { - $instance = $this->getFieldTypeMock(); - $registry = new FieldTypeRegistry([self::FIELD_TYPE_IDENTIFIER => $instance]); - - $result = $registry->getFieldType(self::FIELD_TYPE_IDENTIFIER); - - $this->assertInstanceOf(SPIPersistenceFieldType::class, $result); - } - - /** - * @covers \eZ\Publish\Core\Persistence\FieldTypeRegistry::getFieldType - * - * @since 5.3.2 - */ - public function testGetNotFound() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\NotFound\FieldTypeNotFoundException::class); - - $registry = new FieldTypeRegistry([]); - $registry->getFieldType('not-found'); - } - - /** - * @covers \eZ\Publish\Core\Persistence\FieldTypeRegistry::getFieldType - * - * BC with 5.0-5.3.2 - */ - public function testGetNotFoundBCException() - { - $this->expectException(\RuntimeException::class); - - $registry = new FieldTypeRegistry([]); - $registry->getFieldType('not-found'); - } - - /** - * @covers \eZ\Publish\Core\Persistence\FieldTypeRegistry::getFieldType - */ - public function testGetNotInstance() - { - $this->expectException(\TypeError::class); - - $registry = new FieldTypeRegistry([self::FIELD_TYPE_IDENTIFIER => new \DateTime()]); - $registry->getFieldType(self::FIELD_TYPE_IDENTIFIER); - } - - /** - * @covers \eZ\Publish\Core\Persistence\FieldTypeRegistry::registerFieldType - */ - public function testRegister() - { - $fieldType = $this->getFieldTypeMock(); - $registry = new FieldTypeRegistry([]); - $registry->register(self::FIELD_TYPE_IDENTIFIER, $fieldType); - - $this->assertInstanceOf( - SPIPersistenceFieldType::class, - $registry->getFieldType(self::FIELD_TYPE_IDENTIFIER) - ); - } - - /** - * Returns a mock for persistence field type. - * - * @return \eZ\Publish\SPI\Persistence\FieldType - */ - protected function getFieldTypeMock() - { - return $this->createMock(SPIFieldType::class); - } -} diff --git a/eZ/Publish/Core/Persistence/Tests/FieldValue/Converter/ImageConverterTest.php b/eZ/Publish/Core/Persistence/Tests/FieldValue/Converter/ImageConverterTest.php deleted file mode 100644 index c5cbcede8f..0000000000 --- a/eZ/Publish/Core/Persistence/Tests/FieldValue/Converter/ImageConverterTest.php +++ /dev/null @@ -1,178 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Tests\FieldValue\Converter; - -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\UrlRedecoratorInterface; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use PHPUnit\Framework\TestCase; - -final class ImageConverterTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageConverter */ - private $imageConverter; - - /** @var \eZ\Publish\Core\IO\UrlRedecoratorInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $urlRedecorator; - - /** @var \eZ\Publish\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $ioService; - - protected function setUp(): void - { - $this->ioService = $this->createMock(IOServiceInterface::class); - $this->urlRedecorator = $this->createMock(UrlRedecoratorInterface::class); - - $this->imageConverter = new ImageConverter( - $this->ioService, - $this->urlRedecorator - ); - } - - /** - * @dataProvider dataProviderForTestToStorageFieldDefinition - */ - public function testToStorageFieldDefinition( - FieldDefinition $fieldDefinition, - StorageFieldDefinition $expectedStorageDef - ): void { - $storageFieldDefinition = new StorageFieldDefinition(); - - $this->imageConverter->toStorageFieldDefinition($fieldDefinition, $storageFieldDefinition); - - self::assertEquals( - $expectedStorageDef, - $storageFieldDefinition - ); - } - - public function dataProviderForTestToStorageFieldDefinition(): iterable - { - yield [ - new FieldDefinition([ - 'fieldTypeConstraints' => new FieldTypeConstraints([ - 'validators' => [], - ]), - ]), - new StorageFieldDefinition([ - 'dataInt1' => 0, - 'dataInt2' => 0, - ]), - ]; - - yield [ - new FieldDefinition([ - 'fieldTypeConstraints' => new FieldTypeConstraints([ - 'validators' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 1024, - ], - ], - ]), - ]), - new StorageFieldDefinition([ - 'dataInt1' => 1024, - 'dataInt2' => 0, - ]), - ]; - - yield [ - new FieldDefinition([ - 'fieldTypeConstraints' => new FieldTypeConstraints([ - 'validators' => [ - 'AlternativeTextValidator' => [ - 'required' => true, - ], - ], - ]), - ]), - new StorageFieldDefinition([ - 'dataInt1' => 0, - 'dataInt2' => 1, - ]), - ]; - - yield [ - new FieldDefinition([ - 'fieldTypeConstraints' => new FieldTypeConstraints([ - 'validators' => [ - 'AlternativeTextValidator' => [ - 'required' => false, - ], - ], - ]), - ]), - new StorageFieldDefinition([ - 'dataInt1' => 0, - 'dataInt2' => 0, - ]), - ]; - } - - /** - * @dataProvider dataProviderForTestToFieldDefinition - */ - public function testToFieldDefinition( - StorageFieldDefinition $storageDef, - FieldDefinition $expectedFieldDefinition - ): void { - $fieldDefinition = new FieldDefinition(); - - $this->imageConverter->toFieldDefinition($storageDef, $fieldDefinition); - - self::assertEquals( - $expectedFieldDefinition, - $fieldDefinition - ); - } - - public function dataProviderForTestToFieldDefinition(): iterable - { - yield [ - new StorageFieldDefinition([ - 'dataInt1' => 0, - 'dataInt2' => 0, - ]), - new FieldDefinition([ - 'fieldTypeConstraints' => new FieldTypeConstraints([ - 'validators' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => null, - ], - 'AlternativeTextValidator' => [ - 'required' => false, - ], - ], - ]), - ]), - ]; - - yield [ - new StorageFieldDefinition([ - 'dataInt1' => 1024, - 'dataInt2' => 1, - ]), - new FieldDefinition([ - 'fieldTypeConstraints' => new FieldTypeConstraints([ - 'validators' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 1024, - ], - 'AlternativeTextValidator' => [ - 'required' => true, - ], - ], - ]), - ]), - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Tests/Legacy/FieldValue/Converter/ImageConverterTest.php b/eZ/Publish/Core/Persistence/Tests/Legacy/FieldValue/Converter/ImageConverterTest.php deleted file mode 100644 index 485f33260c..0000000000 --- a/eZ/Publish/Core/Persistence/Tests/Legacy/FieldValue/Converter/ImageConverterTest.php +++ /dev/null @@ -1,228 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Persistence\Tests\Legacy\FieldValue\Converter; - -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\UrlRedecoratorInterface; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use PHPUnit\Framework\TestCase; -use Symfony\Bridge\PhpUnit\ClockMock; - -final class ImageConverterTest extends TestCase -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageConverter */ - private $imageConverter; - - /** @var \eZ\Publish\Core\IO\UrlRedecoratorInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $urlRedecorator; - - /** @var \eZ\Publish\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $ioService; - - protected function setUp(): void - { - $this->ioService = $this->createMock(IOServiceInterface::class); - $this->urlRedecorator = $this->createMock(UrlRedecoratorInterface::class); - - $this->imageConverter = new ImageConverter( - $this->ioService, - $this->urlRedecorator - ); - } - - /** - * @dataProvider fieldValueToXmlProvider - */ - public function testToStorageValue(FieldValue $fieldValue, string $expectedXml): void - { - ClockMock::register(ImageConverter::class); - ClockMock::withClockMock(true); - - $time = ClockMock::time(); - $expectedXml = str_replace('{timestampToReplace}', (string)$time, $expectedXml); - - $storageValue = new StorageFieldValue(); - - $pathToImg = __DIR__ . '/../_fixtures/ibexa_fav.png'; - $this - ->urlRedecorator - ->method('redecorateFromSource') - ->willReturn($pathToImg); - - $this->imageConverter->toStorageValue($fieldValue, $storageValue); - - $this->assertEquals( - $expectedXml, - $storageValue->dataText - ); - - ClockMock::withClockMock(false); - } - - public function fieldValueToXmlProvider(): array - { - $pathToImg = __DIR__ . '/../_fixtures/ibexa_fav.png'; - $dir = __DIR__ . '/../_fixtures'; - - return [ - 'with_additional_data' => [ - new FieldValue([ - 'data' => [ - 'width' => 100, - 'height' => 200, - 'alternativeText' => 'test', - 'mime' => 'image/png', - 'fieldId' => 1, - 'uri' => $pathToImg, - 'versionNo' => 1, - 'languageCode' => 'eng-GB', - 'additionalData' => [ - 'focalPointX' => 50, - 'focalPointY' => 100, - 'author' => 'John Smith', - ], - ], - ]), - <<< XML -<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="ibexa_fav.png" - suffix="png" basename="ibexa_fav" dirpath="{$dir}" url="{$pathToImg}" - original_filename="ibexa_fav.png" mime_type="image/png" width="100" - height="200" alternative_text="test" alias_key="1293033771" timestamp="{timestampToReplace}"> - <original attribute_id="1" attribute_version="1" attribute_language="eng-GB"/> - <information Height="200" Width="100" IsColor="1"/> - <additional_data><attribute key="focalPointX">50</attribute><attribute key="focalPointY">100</attribute><attribute key="author">John Smith</attribute></additional_data> -</ezimage> -XML, - ], - 'without_additional_data_stored' => [ - new FieldValue([ - 'data' => [ - 'width' => 100, - 'height' => 200, - 'alternativeText' => 'test', - 'mime' => 'image/png', - 'fieldId' => 1, - 'uri' => $pathToImg, - 'versionNo' => 1, - 'languageCode' => 'eng-GB', - ], - ]), - <<< XML -<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="ibexa_fav.png" - suffix="png" basename="ibexa_fav" dirpath="{$dir}" url="{$pathToImg}" - original_filename="ibexa_fav.png" mime_type="image/png" width="100" - height="200" alternative_text="test" alias_key="1293033771" timestamp="{timestampToReplace}"> - <original attribute_id="1" attribute_version="1" attribute_language="eng-GB"/> - <information Height="200" Width="100" IsColor="1"/> - <additional_data/> -</ezimage> -XML, - ], - ]; - } - - /** - * @dataProvider xmlToFieldValueProvider - */ - public function testToFieldValue(string $xml, FieldValue $expectedFieldValue): void - { - ClockMock::register(ImageConverter::class); - ClockMock::withClockMock(true); - - $time = ClockMock::time(); - $xml = str_replace('{timestampToReplace}', (string) $time, $xml); - $storageValue = new StorageFieldValue([ - 'dataText' => $xml, - ]); - - $this - ->ioService - ->method('loadBinaryFileByUri') - ->willReturn(new BinaryFile(['id' => 1])); - - $fieldValue = new FieldValue(); - $this->imageConverter->toFieldValue($storageValue, $fieldValue); - - $this->assertEquals( - $expectedFieldValue->data, - $fieldValue->data - ); - - ClockMock::withClockMock(false); - } - - public function xmlToFieldValueProvider(): array - { - $pathToImg = __DIR__ . '/../_fixtures/ibexa_fav.png'; - $dir = __DIR__ . '/../_fixtures'; - - return [ - 'with_additional_data' => [ -<<< XML -<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="ibexa_fav.png" - suffix="png" basename="ibexa_fav" dirpath="{$dir}" url="{$pathToImg}" - original_filename="ibexa_fav.png" mime_type="image/png" width="100" - height="200" alternative_text="test" alias_key="1293033771" timestamp="{timestampToReplace}"> - <original attribute_id="1" attribute_version="1" attribute_language="eng-GB"/> - <information Height="200" Width="100" IsColor="1"/> - <additional_data> - <attribute key="focalPointX">50</attribute> - <attribute key="focalPointY">100</attribute> - <attribute key="author">John Smith</attribute> - </additional_data> -</ezimage> -XML, - new FieldValue([ - 'data' => [ - 'width' => '100', - 'height' => '200', - 'alternativeText' => 'test', - 'mime' => 'image/png', - 'id' => 1, - 'fileName' => 'ibexa_fav.png', - 'additionalData' => [ - 'focalPointX' => 50, - 'focalPointY' => 100, - 'author' => 'John Smith', - ], - ], - ]), - ], - 'without_additional_data_stored' => [ -<<< XML -<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="ibexa_fav.png" - suffix="png" basename="ibexa_fav" dirpath="{$dir}" url="{$pathToImg}" - original_filename="ibexa_fav.png" mime_type="image/png" width="100" - height="200" alternative_text="test" alias_key="1293033771" timestamp="{timestampToReplace}"> - <original attribute_id="1" attribute_version="1" attribute_language="eng-GB"/> - <information Height="200" Width="100" IsColor="1"/> -</ezimage> -XML, - new FieldValue([ - 'data' => [ - 'width' => '100', - 'height' => '200', - 'alternativeText' => 'test', - 'mime' => 'image/png', - 'id' => 1, - 'fileName' => 'ibexa_fav.png', - 'additionalData' => [], - ], - ]), - ], - ]; - } -} diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/TransformationProcessorDefinitionBasedParserTest.php b/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/TransformationProcessorDefinitionBasedParserTest.php deleted file mode 100644 index c1b9f2d7b5..0000000000 --- a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/TransformationProcessorDefinitionBasedParserTest.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\Tests\TransformationProcessor; - -use eZ\Publish\Core\Persistence; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; - -/** - * Test case for LocationHandlerTest. - */ -class TransformationProcessorDefinitionBasedParserTest extends TestCase -{ - public static function getTestFiles() - { - return array_map( - static function ($file) { - return [realpath($file)]; - }, - glob(__DIR__ . '/_fixtures/transformations/*.tr') - ); - } - - /** - * @dataProvider getTestFiles - */ - public function testParse($file) - { - $parser = new Persistence\TransformationProcessor\DefinitionBased\Parser(); - - $fixture = include $file . '.result'; - $this->assertEquals( - $fixture, - $parser->parse($file) - ); - } -} diff --git a/eZ/Publish/Core/Persistence/TransformationProcessor/DefinitionBased.php b/eZ/Publish/Core/Persistence/TransformationProcessor/DefinitionBased.php deleted file mode 100644 index 38f3ddd1b2..0000000000 --- a/eZ/Publish/Core/Persistence/TransformationProcessor/DefinitionBased.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Persistence\TransformationProcessor; - -use eZ\Publish\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Persistence\TransformationProcessor\DefinitionBased\Parser; - -/** - * Class for processing a set of transformations, loaded from .tr files, on a string. - */ -class DefinitionBased extends TransformationProcessor -{ - /** - * Transformation parser. - * - * @var \eZ\Publish\Core\Persistence\TransformationProcessor\DefinitionBased\Parser - */ - protected $parser = null; - - /** - * Construct instance of TransformationProcessor\DefinitionBased. - * - * Through the $ruleFiles array, a list of files with full text - * transformation rules is given. These files are parsed by - * {@link \eZ\Publish\Core\Persistence\TransformationProcessor\DefinitionBased\Parser} - * and then used for normalization in the full text search. - * - * @param \eZ\Publish\Core\Persistence\TransformationProcessor\DefinitionBased\Parser $parser - * @param \eZ\Publish\Core\Persistence\TransformationProcessor\PcreCompiler $compiler - * @param array $ruleFiles - * - * @return \eZ\Publish\Core\Persistence\TransformationProcessor\DefinitionBased - */ - public function __construct(Parser $parser, PcreCompiler $compiler, array $ruleFiles = []) - { - parent::__construct($compiler, $ruleFiles); - $this->parser = $parser; - } - - /** - * Loads rules. - * - * @return array - */ - protected function getRules() - { - if ($this->compiledRules === null) { - $rules = []; - foreach ($this->ruleFiles as $file) { - $rules = array_merge( - $rules, - $this->parser->parse($file) - ); - } - - $this->compiledRules = $this->compiler->compile($rules); - } - - return $this->compiledRules; - } -} diff --git a/eZ/Publish/Core/Query/QueryFactory.php b/eZ/Publish/Core/Query/QueryFactory.php deleted file mode 100644 index 3e5eea36a1..0000000000 --- a/eZ/Publish/Core/Query/QueryFactory.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Query; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\Core\QueryType\QueryTypeRegistry; - -final class QueryFactory implements QueryFactoryInterface -{ - /** @var \eZ\Publish\Core\QueryType\QueryTypeRegistry */ - private $queryTypeRegistry; - - public function __construct(QueryTypeRegistry $queryTypeRegistry) - { - $this->queryTypeRegistry = $queryTypeRegistry; - } - - public function create(string $type, array $parameters = []): Query - { - return $this->queryTypeRegistry->getQueryType($type)->getQuery($parameters); - } -} diff --git a/eZ/Publish/Core/Query/QueryFactoryInterface.php b/eZ/Publish/Core/Query/QueryFactoryInterface.php deleted file mode 100644 index c2b8f50b40..0000000000 --- a/eZ/Publish/Core/Query/QueryFactoryInterface.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Query; - -use eZ\Publish\API\Repository\Values\Content\Query; - -interface QueryFactoryInterface -{ - public function create(string $type, array $parameters = []): Query; -} diff --git a/eZ/Publish/Core/Query/Tests/QueryFactoryTest.php b/eZ/Publish/Core/Query/Tests/QueryFactoryTest.php deleted file mode 100644 index 3a0219223e..0000000000 --- a/eZ/Publish/Core/Query/Tests/QueryFactoryTest.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Query\Tests; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\Core\Query\QueryFactory; -use eZ\Publish\Core\QueryType\QueryType; -use eZ\Publish\Core\QueryType\QueryTypeRegistry; -use eZ\Publish\Core\Search\Tests\TestCase; - -final class QueryFactoryTest extends TestCase -{ - private const EXAMPLE_QUERY_TYPE = 'Example'; - private const EXAMPLE_QUERY_PARAMS = [ - 'foo' => 'foo', - 'bar' => 'bar', - 'baz' => 'baz', - ]; - - /** @var \eZ\Publish\Core\QueryType\QueryTypeRegistry|\PHPUnit\Framework\MockObject\MockObject */ - private $queryTypeRegistry; - - /** @var \eZ\Publish\Core\Query\QueryFactory */ - private $queryFactory; - - protected function setUp(): void - { - $this->queryTypeRegistry = $this->createMock(QueryTypeRegistry::class); - $this->queryFactory = new QueryFactory($this->queryTypeRegistry); - } - - public function testCreate(): void - { - $expectedQuery = new Query(); - - $queryType = $this->createMock(QueryType::class); - $queryType - ->expects($this->once()) - ->method('getQuery') - ->with(self::EXAMPLE_QUERY_PARAMS) - ->willReturn($expectedQuery); - - $this->queryTypeRegistry - ->expects($this->once()) - ->method('getQueryType') - ->with(self::EXAMPLE_QUERY_TYPE) - ->willReturn($queryType); - - $actualQuery = $this->queryFactory->create( - self::EXAMPLE_QUERY_TYPE, - self::EXAMPLE_QUERY_PARAMS - ); - - $this->assertEquals($expectedQuery, $actualQuery); - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/AncestorsQueryType.php b/eZ/Publish/Core/QueryType/BuiltIn/AncestorsQueryType.php deleted file mode 100644 index 1030fa9a30..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/AncestorsQueryType.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Ancestor; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LocationId; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchNone; - -final class AncestorsQueryType extends AbstractLocationQueryType -{ - public static function getName(): string - { - return 'Ancestors'; - } - - protected function getQueryFilter(array $parameters): Criterion - { - $location = $this->resolveLocation($parameters); - - if ($location === null) { - return new MatchNone(); - } - - return new LogicalAnd([ - new Ancestor($location->pathString), - new LogicalNot( - new LocationId($location->id) - ), - ]); - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/ChildrenQueryType.php b/eZ/Publish/Core/QueryType/BuiltIn/ChildrenQueryType.php deleted file mode 100644 index ed583709c5..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/ChildrenQueryType.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchNone; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId; - -final class ChildrenQueryType extends AbstractLocationQueryType -{ - public static function getName(): string - { - return 'Children'; - } - - protected function getQueryFilter(array $parameters): Criterion - { - $location = $this->resolveLocation($parameters); - - if ($location === null) { - return new MatchNone(); - } - - return new ParentLocationId($location->id); - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SiblingsQueryType.php b/eZ/Publish/Core/QueryType/BuiltIn/SiblingsQueryType.php deleted file mode 100644 index c3a411dcb6..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SiblingsQueryType.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchNone; - -final class SiblingsQueryType extends AbstractLocationQueryType -{ - public static function getName(): string - { - return 'Siblings'; - } - - protected function getQueryFilter(array $parameters): Criterion - { - $location = $this->resolveLocation($parameters); - - if ($location === null) { - return new MatchNone(); - } - - return Criterion\Sibling::fromLocation($location); - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortClausesFactory.php b/eZ/Publish/Core/QueryType/BuiltIn/SortClausesFactory.php deleted file mode 100644 index bd3ac9b550..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortClausesFactory.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn; - -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecLexer; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecParser; - -/** - * @internal - */ -final class SortClausesFactory implements SortClausesFactoryInterface -{ - /** @var \eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface */ - private $sortClauseParser; - - public function __construct(SortClauseParserInterface $sortClauseArgsParser) - { - $this->sortClauseParser = $sortClauseArgsParser; - } - - /** - * @throws \eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Exception\SyntaxErrorException - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] - */ - public function createFromSpecification(string $specification): array - { - $lexer = new SortSpecLexer(); - $lexer->tokenize($specification); - - $parser = new SortSpecParser($this->sortClauseParser, $lexer); - - return $parser->parseSortClausesList(); - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortClausesFactoryInterface.php b/eZ/Publish/Core/QueryType/BuiltIn/SortClausesFactoryInterface.php deleted file mode 100644 index 1df2418030..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortClausesFactoryInterface.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn; - -/** - * @internal - */ -interface SortClausesFactoryInterface -{ - /** - * @return \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] - * - * @throws \eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Exception\SyntaxErrorException - */ - public function createFromSpecification(string $specification): array; -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Exception/UnsupportedSortClauseException.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Exception/UnsupportedSortClauseException.php deleted file mode 100644 index d86ca59ea3..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Exception/UnsupportedSortClauseException.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Exception; - -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface; -use RuntimeException; -use Throwable; - -final class UnsupportedSortClauseException extends RuntimeException -{ - public function __construct(string $name, $code = 0, Throwable $previous = null) - { - $message = sprintf( - 'Could not find %s for %s sort clause', - SortClauseParserInterface::class, - $name - ); - - parent::__construct($message, $code, $previous); - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/CustomFieldSortClauseParser.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/CustomFieldSortClauseParser.php deleted file mode 100644 index f792a40418..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/CustomFieldSortClauseParser.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\CustomField; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Token; - -/** - * Parser for \eZ\Publish\API\Repository\Values\Content\Query\SortClause\RawField sort clause. - * - * Example of correct input: - * - * custom_field custom_field_name ASC - */ -final class CustomFieldSortClauseParser implements SortClauseParserInterface -{ - private const SUPPORTED_CLAUSE_NAME = 'custom_field'; - - public function parse(SortSpecParserInterface $parser, string $name): SortClause - { - $args = []; - $args[] = $parser->match(Token::TYPE_ID)->getValue(); - $args[] = $parser->parseSortDirection(); - - return new CustomField(...$args); - } - - public function supports(string $name): bool - { - return $name === self::SUPPORTED_CLAUSE_NAME; - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/DefaultSortClauseParser.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/DefaultSortClauseParser.php deleted file mode 100644 index c075a0a40e..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/DefaultSortClauseParser.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Exception\UnsupportedSortClauseException; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; - -/** - * Parser for sort clauses which expect only sort direction in constructor parameter. - */ -final class DefaultSortClauseParser implements SortClauseParserInterface -{ - /** @var string[] */ - private $valueObjectClassMap; - - public function __construct(array $valueObjectClassMap) - { - $this->valueObjectClassMap = $valueObjectClassMap; - } - - public function parse(SortSpecParserInterface $parser, string $name): SortClause - { - if (isset($this->valueObjectClassMap[$name])) { - $class = $this->valueObjectClassMap[$name]; - - return new $class($parser->parseSortDirection()); - } - - throw new UnsupportedSortClauseException($name); - } - - public function supports(string $name): bool - { - return isset($this->valueObjectClassMap[$name]); - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/FieldSortClauseParser.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/FieldSortClauseParser.php deleted file mode 100644 index a58558d885..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/FieldSortClauseParser.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Field; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Token; - -/** - * Parser for \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Field sort clause. - * - * Example of correct input: - * - * field article.short_title DESC - */ -final class FieldSortClauseParser implements SortClauseParserInterface -{ - private const SUPPORTED_CLAUSE_NAME = 'field'; - - public function parse(SortSpecParserInterface $parser, string $name): SortClause - { - $args = []; - $args[] = $parser->match(Token::TYPE_ID)->getValue(); - $parser->match(Token::TYPE_DOT); - $args[] = $parser->match(Token::TYPE_ID)->getValue(); - $args[] = $parser->parseSortDirection(); - - return new Field(...$args); - } - - public function supports(string $name): bool - { - return $name === self::SUPPORTED_CLAUSE_NAME; - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/MapDistanceSortClauseParser.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/MapDistanceSortClauseParser.php deleted file mode 100644 index 2177b94040..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/MapDistanceSortClauseParser.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\MapLocationDistance; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Token; - -/** - * Parser for \eZ\Publish\API\Repository\Values\Content\Query\SortClause\MapLocationDistance sort clause. - * - * Example of correct input: - * - * map_distance place.location 45.0809 14.5926 ASC - */ -final class MapDistanceSortClauseParser implements SortClauseParserInterface -{ - private const SUPPORTED_CLAUSE_NAME = 'map_distance'; - - public function parse(SortSpecParserInterface $parser, string $name): SortClause - { - $args = []; - $args[] = $parser->match(Token::TYPE_ID)->getValue(); - $parser->match(Token::TYPE_DOT); - $args[] = $parser->match(Token::TYPE_ID)->getValue(); - $args[] = $parser->match(Token::TYPE_FLOAT)->getValueAsFloat(); - $args[] = $parser->match(Token::TYPE_FLOAT)->getValueAsFloat(); - $args[] = $parser->parseSortDirection(); - - return new MapLocationDistance(...$args); - } - - public function supports(string $name): bool - { - return $name === self::SUPPORTED_CLAUSE_NAME; - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/RandomSortClauseParser.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/RandomSortClauseParser.php deleted file mode 100644 index 806f6d14e2..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParser/RandomSortClauseParser.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Random; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Token; - -/** - * Parser for \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Random sort clause. - * - * Example of correct input: - * - * random 7 ASC - */ -final class RandomSortClauseParser implements SortClauseParserInterface -{ - private const SUPPORTED_CLAUSE_NAME = 'random'; - - public function parse(SortSpecParserInterface $parser, string $name): SortClause - { - $seed = null; - if ($parser->isNextToken(Token::TYPE_INT)) { - $seed = $parser->match(Token::TYPE_INT)->getValueAsInt(); - } - - $sortDirection = $parser->parseSortDirection(); - - return new Random($seed, $sortDirection); - } - - public function supports(string $name): bool - { - return $name === self::SUPPORTED_CLAUSE_NAME; - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParserDispatcher.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParserDispatcher.php deleted file mode 100644 index bf12bd3606..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParserDispatcher.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Exception\UnsupportedSortClauseException; - -final class SortClauseParserDispatcher implements SortClauseParserInterface -{ - /** @var \eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface[] */ - private $parsers; - - public function __construct(iterable $parsers = []) - { - $this->parsers = $parsers; - } - - public function parse(SortSpecParserInterface $parser, string $name): SortClause - { - $sortClauseParser = $this->findParser($name); - if ($sortClauseParser instanceof SortClauseParserInterface) { - return $sortClauseParser->parse($parser, $name); - } - - throw new UnsupportedSortClauseException($name); - } - - public function supports(string $name): bool - { - return $this->findParser($name) instanceof SortClauseParserInterface; - } - - private function findParser(string $name): ?SortClauseParserInterface - { - foreach ($this->parsers as $parser) { - if ($parser->supports($name)) { - return $parser; - } - } - - return null; - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParserInterface.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParserInterface.php deleted file mode 100644 index b04af92179..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortClauseParserInterface.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -interface SortClauseParserInterface -{ - /** - * @throws \eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Exception\UnsupportedSortClauseException If sort clause is not supported by parser - */ - public function parse(SortSpecParserInterface $parser, string $name): SortClause; - - public function supports(string $name): bool; -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortSpecParserInterface.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortSpecParserInterface.php deleted file mode 100644 index a312bbcb42..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/SortSpecParserInterface.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec; - -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; - -interface SortSpecParserInterface -{ - public function parseSortClausesList(): array; - - public function parseSortClause(): SortClause; - - public function parseSortDirection(): string; - - public function isNextToken(string ...$types): bool; - - public function match(string $type): Token; - - public function matchAnyOf(string ...$types): Token; -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/CustomFieldSortClauseParserTest.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/CustomFieldSortClauseParserTest.php deleted file mode 100644 index 57060a5796..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/CustomFieldSortClauseParserTest.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortClauseParser; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\CustomField; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\CustomFieldSortClauseParser; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Token; -use PHPUnit\Framework\TestCase; - -final class CustomFieldSortClauseParserTest extends TestCase -{ - private const EXAMPLE_SEARCH_INDEX_FIELD = 'custom_field_s'; - - /** @var \eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\CustomFieldSortClauseParser */ - private $parser; - - protected function setUp(): void - { - $this->parser = new CustomFieldSortClauseParser(); - } - - public function testParse(): void - { - $parser = $this->createMock(SortSpecParserInterface::class); - $parser - ->method('match') - ->with(Token::TYPE_ID) - ->willReturn( - new Token( - Token::TYPE_ID, - self::EXAMPLE_SEARCH_INDEX_FIELD - ), - ); - - $parser->method('parseSortDirection')->willReturn(Query::SORT_ASC); - - $this->assertEquals( - new CustomField( - self::EXAMPLE_SEARCH_INDEX_FIELD, - Query::SORT_ASC - ), - $this->parser->parse($parser, 'custom_field') - ); - } - - public function testSupports(): void - { - $this->assertTrue($this->parser->supports('custom_field')); - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/DefaultSortClauseParserTest.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/DefaultSortClauseParserTest.php deleted file mode 100644 index 0729a97e65..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/DefaultSortClauseParserTest.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortClauseParser; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Exception\UnsupportedSortClauseException; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\DefaultSortClauseParser; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; -use PHPUnit\Framework\TestCase; - -final class DefaultSortClauseParserTest extends TestCase -{ - /** @var \eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\DefaultSortClauseParser */ - private $defaultSortClauseParser; - - protected function setUp(): void - { - $this->defaultSortClauseParser = new DefaultSortClauseParser([ - 'depth' => Location\Depth::class, - 'priority' => Location\Priority::class, - 'id' => Location\Id::class, - ]); - } - - public function testParse(): void - { - $parser = $this->createMock(SortSpecParserInterface::class); - $parser->method('parseSortDirection')->willReturn(Query::SORT_ASC); - - $this->assertEquals( - new Location\Depth(Query::SORT_ASC), - $this->defaultSortClauseParser->parse($parser, 'depth') - ); - - $this->assertEquals( - new Location\Priority(Query::SORT_ASC), - $this->defaultSortClauseParser->parse($parser, 'priority') - ); - } - - public function testParseThrowsUnsupportedSortClauseException(): void - { - $this->expectException(UnsupportedSortClauseException::class); - $this->expectExceptionMessage(sprintf( - 'Could not find %s for unsupported sort clause', - SortClauseParserInterface::class - )); - - $this->defaultSortClauseParser->parse( - $this->createMock(SortSpecParserInterface::class), - 'unsupported' - ); - } - - public function testSupports(): void - { - $this->assertTrue($this->defaultSortClauseParser->supports('depth')); - $this->assertTrue($this->defaultSortClauseParser->supports('priority')); - $this->assertTrue($this->defaultSortClauseParser->supports('id')); - - $this->assertFalse($this->defaultSortClauseParser->supports('unsupported')); - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/FieldSortClauseParserTest.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/FieldSortClauseParserTest.php deleted file mode 100644 index c493591c53..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/FieldSortClauseParserTest.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortClauseParser; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Field; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\FieldSortClauseParser; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Token; -use PHPUnit\Framework\TestCase; - -final class FieldSortClauseParserTest extends TestCase -{ - private const EXAMPLE_CONTENT_TYPE_ID = 'article'; - private const EXAMPLE_FIELD_ID = 'title'; - - /** @var \eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\FieldSortClauseParser */ - private $fieldSortClauseParser; - - protected function setUp(): void - { - $this->fieldSortClauseParser = new FieldSortClauseParser(); - } - - public function testParse(): void - { - $parser = $this->createMock(SortSpecParserInterface::class); - $parser - ->method('match') - ->withConsecutive( - [Token::TYPE_ID], - [Token::TYPE_DOT], - [Token::TYPE_ID] - ) - ->willReturnOnConsecutiveCalls( - new Token(Token::TYPE_ID, self::EXAMPLE_CONTENT_TYPE_ID), - new Token(Token::TYPE_DOT), - new Token(Token::TYPE_ID, self::EXAMPLE_FIELD_ID) - ); - - $parser->method('parseSortDirection')->willReturn(Query::SORT_ASC); - - $this->assertEquals( - new Field(self::EXAMPLE_CONTENT_TYPE_ID, self::EXAMPLE_FIELD_ID, Query::SORT_ASC), - $this->fieldSortClauseParser->parse($parser, 'field') - ); - } - - public function testSupports(): void - { - $this->assertTrue($this->fieldSortClauseParser->supports('field')); - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/MapDistanceSortClauseParserTest.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/MapDistanceSortClauseParserTest.php deleted file mode 100644 index abcec93dfe..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/MapDistanceSortClauseParserTest.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortClauseParser; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\MapLocationDistance; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\MapDistanceSortClauseParser; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Token; -use PHPUnit\Framework\TestCase; - -final class MapDistanceSortClauseParserTest extends TestCase -{ - private const EXAMPLE_CONTENT_TYPE_ID = 'place'; - private const EXAMPLE_FIELD_ID = 'location'; - private const EXAMPLE_LAT = 50.0647; - private const EXAMPLE_LON = 19.9450; - - /** @var \eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\MapDistanceSortClauseParser */ - private $mapDistanceSortClauseParser; - - protected function setUp(): void - { - $this->mapDistanceSortClauseParser = new MapDistanceSortClauseParser(); - } - - public function testParse(): void - { - $parser = $this->createMock(SortSpecParserInterface::class); - $parser - ->method('match') - ->withConsecutive( - [Token::TYPE_ID], - [Token::TYPE_DOT], - [Token::TYPE_ID], - [Token::TYPE_FLOAT], - [Token::TYPE_FLOAT] - ) - ->willReturnOnConsecutiveCalls( - new Token(Token::TYPE_ID, self::EXAMPLE_CONTENT_TYPE_ID), - new Token(Token::TYPE_DOT), - new Token(Token::TYPE_ID, self::EXAMPLE_FIELD_ID), - new Token(Token::TYPE_FLOAT, (string)self::EXAMPLE_LAT), - new Token(Token::TYPE_FLOAT, (string)self::EXAMPLE_LON) - ); - - $parser->method('parseSortDirection')->willReturn(Query::SORT_ASC); - - $this->assertEquals( - new MapLocationDistance( - self::EXAMPLE_CONTENT_TYPE_ID, - self::EXAMPLE_FIELD_ID, - self::EXAMPLE_LAT, - self::EXAMPLE_LON, - Query::SORT_ASC - ), - $this->mapDistanceSortClauseParser->parse($parser, 'map_distance') - ); - } - - public function testSupports(): void - { - $this->assertTrue($this->mapDistanceSortClauseParser->supports('map_distance')); - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/RandomSortClauseParserTest.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/RandomSortClauseParserTest.php deleted file mode 100644 index 4ff4a9d4e5..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParser/RandomSortClauseParserTest.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortClauseParser; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Random; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\RandomSortClauseParser; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Token; -use PHPUnit\Framework\TestCase; - -final class RandomSortClauseParserTest extends TestCase -{ - private const EXAMPLE_SEED = 1; - - /** @var \eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\RandomSortClauseParser */ - private $randomSortClauseParser; - - protected function setUp(): void - { - $this->randomSortClauseParser = new RandomSortClauseParser(); - } - - public function testParse(): void - { - $parser = $this->createMock(SortSpecParserInterface::class); - $parser - ->method('isNextToken') - ->with(Token::TYPE_INT) - ->willReturn(true); - - $parser - ->method('match') - ->with(Token::TYPE_INT) - ->willReturn(new Token(Token::TYPE_INT, (string)self::EXAMPLE_SEED)); - - $parser->method('parseSortDirection')->willReturn(Query::SORT_ASC); - - $this->assertEquals( - new Random(self::EXAMPLE_SEED, Query::SORT_ASC), - $this->randomSortClauseParser->parse($parser, 'random') - ); - } - - public function testSupports(): void - { - $this->assertTrue($this->randomSortClauseParser->supports('random')); - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortSpecLexerStub.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortSpecLexerStub.php deleted file mode 100644 index d7c47f5039..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortSpecLexerStub.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests; - -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecLexerInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Token; - -/** - * Dummy \eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecLexerInterface implementation. - */ -final class SortSpecLexerStub implements SortSpecLexerInterface -{ - /** @var \eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Token[] */ - private $tokens; - - /** @var string|null */ - private $input; - - /** @var int */ - private $position; - - public function __construct(array $tokens = []) - { - $this->tokens = $tokens; - $this->position = -1; - } - - public function consume(): Token - { - ++$this->position; - - return $this->tokens[$this->position]; - } - - public function isEOF(): bool - { - return $this->position + 1 >= count($this->tokens) - 1; - } - - public function tokenize(string $input): void - { - $this->input = $input; - } - - public function getInput(): string - { - return (string)$this->input; - } - - public function peek(): ?Token - { - return $this->tokens[$this->position + 1] ?? null; - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Token.php b/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Token.php deleted file mode 100644 index 8f547d145b..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Token.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec; - -final class Token -{ - public const TYPE_NONE = '<none>'; - public const TYPE_ASC = '<asc>'; - public const TYPE_DESC = '<desc>'; - public const TYPE_ID = '<id>'; - public const TYPE_DOT = '<.>'; - public const TYPE_COMMA = '<,>'; - public const TYPE_INT = '<int>'; - public const TYPE_FLOAT = '<float>'; - public const TYPE_EOF = '<eof>'; - - /** @var string */ - private $type; - - /** @var string */ - private $value; - - /** @var int */ - private $position; - - public function __construct(string $type, string $value = '', int $position = -1) - { - $this->type = $type; - $this->value = $value; - $this->position = $position; - } - - public function isA(string $type): bool - { - return $this->type === $type; - } - - public function getType(): string - { - return $this->type; - } - - public function getValue(): string - { - return $this->value; - } - - public function getValueAsFloat(): float - { - return (float)$this->value; - } - - public function getValueAsInt(): int - { - return (int)$this->value; - } - - public function getPosition(): int - { - return $this->position; - } - - public function __toString(): string - { - if ($this->value !== null) { - return "{$this->value} ({$this->type})"; - } - - return "{$this->type}"; - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SubtreeQueryType.php b/eZ/Publish/Core/QueryType/BuiltIn/SubtreeQueryType.php deleted file mode 100644 index b94bc7e841..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/SubtreeQueryType.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location\Depth; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchNone; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree; -use Symfony\Component\OptionsResolver\OptionsResolver; - -final class SubtreeQueryType extends AbstractLocationQueryType -{ - public static function getName(): string - { - return 'Subtree'; - } - - protected function configureOptions(OptionsResolver $resolver): void - { - parent::configureOptions($resolver); - - $resolver->setDefaults([ - 'depth' => -1, - ]); - $resolver->setAllowedTypes('depth', 'int'); - } - - protected function getQueryFilter(array $parameters): Criterion - { - $location = $this->resolveLocation($parameters); - - if ($location === null) { - return new MatchNone(); - } - - if ($parameters['depth'] > -1) { - $depth = $location->depth + (int)$parameters['depth']; - - return new LogicalAnd([ - new Subtree($location->pathString), - new Depth(Operator::LTE, $depth), - ]); - } - - return new Subtree($location->pathString); - } -} diff --git a/eZ/Publish/Core/QueryType/BuiltIn/Tests/AbstractQueryTypeTest.php b/eZ/Publish/Core/QueryType/BuiltIn/Tests/AbstractQueryTypeTest.php deleted file mode 100644 index e7a7554637..0000000000 --- a/eZ/Publish/Core/QueryType/BuiltIn/Tests/AbstractQueryTypeTest.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\QueryType\BuiltIn\Tests; - -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortClausesFactoryInterface; -use eZ\Publish\Core\QueryType\QueryType; -use eZ\Publish\Core\Repository\Values\Content\Location; -use PHPUnit\Framework\TestCase; - -abstract class AbstractQueryTypeTest extends TestCase -{ - protected const ROOT_LOCATION_ID = 2; - protected const ROOT_LOCATION_PATH_STRING = '/1/2/'; - - /** @var \eZ\Publish\API\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject */ - private $repository; - - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $configResolver; - - /** @var \eZ\Publish\Core\QueryType\BuiltIn\SortClausesFactoryInterface|\PHPUnit\Framework\MockObject\MockObject */ - private $sortClausesFactory; - - /** @var \eZ\Publish\Core\QueryType\QueryType */ - private $queryType; - - protected function setUp(): void - { - $rootLocation = new Location([ - 'id' => self::ROOT_LOCATION_ID, - 'pathString' => self::ROOT_LOCATION_PATH_STRING, - ]); - - $locationService = $this->createMock(LocationService::class); - $locationService - ->method('loadLocation') - ->with(self::ROOT_LOCATION_ID) - ->willReturn($rootLocation); - - $this->repository = $this->createMock(Repository::class); - $this->repository->method('getLocationService')->willReturn($locationService); - - $this->configResolver = $this->createMock(ConfigResolverInterface::class); - $this->configResolver - ->method('getParameter') - ->with('content.tree_root.location_id') - ->willReturn(self::ROOT_LOCATION_ID); - - $this->sortClausesFactory = $this->createMock(SortClausesFactoryInterface::class); - - $this->queryType = $this->createQueryType( - $this->repository, - $this->configResolver, - $this->sortClausesFactory - ); - } - - /** - * @dataProvider dataProviderForGetQuery - */ - final public function testGetQuery(array $parameters, Query $expectedQuery): void - { - $this->assertEquals($expectedQuery, $this->queryType->getQuery($parameters)); - } - - final public function testGetName(): void - { - $this->assertEquals( - $this->getExpectedName(), - $this->queryType->getName() - ); - } - - final public function testGetSupportedParameters(): void - { - $this->assertEqualsCanonicalizing( - $this->getExpectedSupportedParameters(), - $this->queryType->getSupportedParameters() - ); - } - - abstract public function dataProviderForGetQuery(): iterable; - - abstract protected function createQueryType( - Repository $repository, - ConfigResolverInterface $configResolver, - SortClausesFactoryInterface $sortClausesFactory - ): QueryType; - - abstract protected function getExpectedName(): string; - - abstract protected function getExpectedSupportedParameters(): array; -} diff --git a/eZ/Publish/Core/QueryType/ContentViewQueryTypeMapper.php b/eZ/Publish/Core/QueryType/ContentViewQueryTypeMapper.php deleted file mode 100644 index 3455ee1a7a..0000000000 --- a/eZ/Publish/Core/QueryType/ContentViewQueryTypeMapper.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\QueryType; - -use eZ\Publish\Core\MVC\Symfony\View\ContentView; - -/** - * Maps a ContentView to a QueryType. - */ -interface ContentViewQueryTypeMapper -{ - /** - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentView $contentView - * - * @return \eZ\Publish\Core\QueryType\QueryType - */ - public function map(ContentView $contentView); -} diff --git a/eZ/Publish/Core/QueryType/QueryTypeRegistry.php b/eZ/Publish/Core/QueryType/QueryTypeRegistry.php deleted file mode 100644 index 93e21bdaac..0000000000 --- a/eZ/Publish/Core/QueryType/QueryTypeRegistry.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\QueryType; - -/** - * Registry of QueryType objects. - */ -interface QueryTypeRegistry -{ - /** - * Registers $queryType as $name. - * - * @param string $name - * @param \eZ\Publish\Core\QueryType\QueryType $queryType - */ - public function addQueryType($name, QueryType $queryType); - - /** - * Registers QueryTypes from the $queryTypes array. - * - * @param \eZ\Publish\Core\QueryType\QueryType[] $queryTypes An array of QueryTypes, with their name as the index - */ - public function addQueryTypes(array $queryTypes); - - /** - * Get the QueryType $name. - * - * @param string $name - * - * @return \eZ\Publish\Core\QueryType\QueryType - */ - public function getQueryType($name); -} diff --git a/eZ/Publish/Core/Repository/BookmarkService.php b/eZ/Publish/Core/Repository/BookmarkService.php deleted file mode 100644 index 05d5d08cde..0000000000 --- a/eZ/Publish/Core/Repository/BookmarkService.php +++ /dev/null @@ -1,133 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use Exception; -use eZ\Publish\API\Repository\BookmarkService as BookmarkServiceInterface; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\Values\Bookmark\BookmarkList; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\SPI\Persistence\Bookmark\Bookmark; -use eZ\Publish\SPI\Persistence\Bookmark\CreateStruct; -use eZ\Publish\SPI\Persistence\Bookmark\Handler as BookmarkHandler; - -class BookmarkService implements BookmarkServiceInterface -{ - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\SPI\Persistence\Bookmark\Handler */ - protected $bookmarkHandler; - - /** - * BookmarkService constructor. - * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\SPI\Persistence\Bookmark\Handler $bookmarkHandler - */ - public function __construct(RepositoryInterface $repository, BookmarkHandler $bookmarkHandler) - { - $this->repository = $repository; - $this->bookmarkHandler = $bookmarkHandler; - } - - /** - * {@inheritdoc} - */ - public function createBookmark(Location $location): void - { - $loadedLocation = $this->repository->getLocationService()->loadLocation($location->id); - - if ($this->isBookmarked($loadedLocation)) { - throw new InvalidArgumentException('$location', 'Location is already bookmarked.'); - } - - $createStruct = new CreateStruct(); - $createStruct->name = $loadedLocation->contentInfo->name; - $createStruct->locationId = $loadedLocation->id; - $createStruct->userId = $this->getCurrentUserId(); - - $this->repository->beginTransaction(); - try { - $this->bookmarkHandler->create($createStruct); - $this->repository->commit(); - } catch (Exception $ex) { - $this->repository->rollback(); - throw $ex; - } - } - - /** - * {@inheritdoc} - */ - public function deleteBookmark(Location $location): void - { - $loadedLocation = $this->repository->getLocationService()->loadLocation($location->id); - - $bookmarks = $this->bookmarkHandler->loadByUserIdAndLocationId( - $this->getCurrentUserId(), - [$loadedLocation->id] - ); - - if (empty($bookmarks)) { - throw new InvalidArgumentException('$location', 'Location is not bookmarked.'); - } - - $this->repository->beginTransaction(); - try { - $this->bookmarkHandler->delete(reset($bookmarks)->id); - $this->repository->commit(); - } catch (Exception $ex) { - $this->repository->rollback(); - throw $ex; - } - } - - /** - * {@inheritdoc} - */ - public function loadBookmarks(int $offset = 0, int $limit = 25): BookmarkList - { - $currentUserId = $this->getCurrentUserId(); - - $list = new BookmarkList(); - $list->totalCount = $this->bookmarkHandler->countUserBookmarks($currentUserId); - if ($list->totalCount > 0) { - $bookmarks = $this->bookmarkHandler->loadUserBookmarks($currentUserId, $offset, $limit); - - $list->items = array_map(function (Bookmark $bookmark) { - return $this->repository->getLocationService()->loadLocation($bookmark->locationId); - }, $bookmarks); - } - - return $list; - } - - /** - * {@inheritdoc} - */ - public function isBookmarked(Location $location): bool - { - $bookmarks = $this->bookmarkHandler->loadByUserIdAndLocationId( - $this->getCurrentUserId(), - [$location->id] - ); - - return !empty($bookmarks); - } - - private function getCurrentUserId(): int - { - return $this->repository - ->getPermissionResolver() - ->getCurrentUserReference() - ->getUserId(); - } -} diff --git a/eZ/Publish/Core/Repository/ContentService.php b/eZ/Publish/Core/Repository/ContentService.php deleted file mode 100644 index 370bba6c90..0000000000 --- a/eZ/Publish/Core/Repository/ContentService.php +++ /dev/null @@ -1,2651 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use function count; -use Exception; -use eZ\Publish\API\Repository\ContentService as ContentServiceInterface; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\PermissionService; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct as APIContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentDraftList; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\ContentList; -use eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct as APIContentUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\DraftList\Item\ContentDraftListItem; -use eZ\Publish\API\Repository\Values\Content\DraftList\Item\UnauthorizedContentDraftListItem; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LanguageCode; -use eZ\Publish\API\Repository\Values\Content\Relation as APIRelation; -use eZ\Publish\API\Repository\Values\Content\RelationList; -use eZ\Publish\API\Repository\Values\Content\RelationList\Item\RelationListItem; -use eZ\Publish\API\Repository\Values\Content\RelationList\Item\UnauthorizedRelationListItem; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\Repository\Mapper\ContentDomainMapper; -use eZ\Publish\Core\Repository\Mapper\ContentMapper; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\SPI\FieldType\Comparable; -use eZ\Publish\SPI\FieldType\FieldType; -use eZ\Publish\SPI\FieldType\Value; -use eZ\Publish\SPI\Limitation\Target; -use eZ\Publish\SPI\Persistence\Content\ContentInfo as SPIContentInfo; -use eZ\Publish\SPI\Persistence\Content\CreateStruct as SPIContentCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Field as SPIField; -use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct as SPIMetadataUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as SPIRelationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\UpdateStruct as SPIContentUpdateStruct; -use eZ\Publish\SPI\Persistence\Filter\Content\Handler as ContentFilteringHandler; -use eZ\Publish\SPI\Persistence\Handler; -use eZ\Publish\SPI\Repository\Validator\ContentValidator; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use Ibexa\Contracts\Core\Limitation\Target\DestinationLocation as DestinationLocationTarget; -use function sprintf; - -/** - * This class provides service methods for managing content. - */ -class ContentService implements ContentServiceInterface -{ - /** @var \eZ\Publish\Core\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\SPI\Persistence\Handler */ - protected $persistenceHandler; - - /** @var array */ - protected $settings; - - /** @var \eZ\Publish\Core\Repository\Mapper\ContentDomainMapper */ - protected $contentDomainMapper; - - /** @var \eZ\Publish\Core\Repository\Helper\RelationProcessor */ - protected $relationProcessor; - - /** @var \eZ\Publish\Core\Repository\Helper\NameSchemaService */ - protected $nameSchemaService; - - /** @var \eZ\Publish\Core\FieldType\FieldTypeRegistry */ - protected $fieldTypeRegistry; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionResolver; - - /** @var \eZ\Publish\Core\Repository\Mapper\ContentMapper */ - private $contentMapper; - - /** @var \eZ\Publish\SPI\Repository\Validator\ContentValidator */ - private $contentValidator; - - /** @var \eZ\Publish\SPI\Persistence\Filter\Content\Handler */ - private $contentFilteringHandler; - - public function __construct( - RepositoryInterface $repository, - Handler $handler, - ContentDomainMapper $contentDomainMapper, - Helper\RelationProcessor $relationProcessor, - Helper\NameSchemaService $nameSchemaService, - FieldTypeRegistry $fieldTypeRegistry, - PermissionService $permissionService, - ContentMapper $contentMapper, - ContentValidator $contentValidator, - ContentFilteringHandler $contentFilteringHandler, - array $settings = [] - ) { - $this->repository = $repository; - $this->persistenceHandler = $handler; - $this->contentDomainMapper = $contentDomainMapper; - $this->relationProcessor = $relationProcessor; - $this->nameSchemaService = $nameSchemaService; - $this->fieldTypeRegistry = $fieldTypeRegistry; - // Union makes sure default settings are ignored if provided in argument - $this->settings = $settings + [ - // Version archive limit (0-50), only enforced on publish, not on un-publish. - 'default_version_archive_limit' => 5, - 'remove_archived_versions_on_publish' => true, - ]; - $this->contentFilteringHandler = $contentFilteringHandler; - $this->permissionResolver = $permissionService; - $this->contentMapper = $contentMapper; - $this->contentValidator = $contentValidator; - } - - /** - * Loads a content info object. - * - * To load fields use loadContent - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given id does not exist - * - * @param int $contentId - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - public function loadContentInfo(int $contentId): ContentInfo - { - $contentInfo = $this->internalLoadContentInfoById($contentId); - if (!$this->permissionResolver->canUser('content', 'read', $contentInfo)) { - throw new UnauthorizedException('content', 'read', ['contentId' => $contentId]); - } - - return $contentInfo; - } - - /** - * {@inheritdoc} - */ - public function loadContentInfoList(array $contentIds): iterable - { - $contentInfoList = []; - $spiInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList($contentIds); - foreach ($spiInfoList as $id => $spiInfo) { - $contentInfo = $this->contentDomainMapper->buildContentInfoDomainObject($spiInfo); - if ($this->permissionResolver->canUser('content', 'read', $contentInfo)) { - $contentInfoList[$id] = $contentInfo; - } - } - - return $contentInfoList; - } - - /** - * Loads a content info object. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given id does not exist - * - * @param int $id - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - public function internalLoadContentInfoById(int $id): ContentInfo - { - try { - return $this->contentDomainMapper->buildContentInfoDomainObject( - $this->persistenceHandler->contentHandler()->loadContentInfo($id) - ); - } catch (APINotFoundException $e) { - throw new NotFoundException('Content', $id, $e); - } - } - - /** - * Loads a content info object by remote id. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given id does not exist - * - * @param string $remoteId - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - public function internalLoadContentInfoByRemoteId(string $remoteId): ContentInfo - { - try { - return $this->contentDomainMapper->buildContentInfoDomainObject( - $this->persistenceHandler->contentHandler()->loadContentInfoByRemoteId($remoteId) - ); - } catch (APINotFoundException $e) { - throw new NotFoundException('Content', $remoteId, $e); - } - } - - /** - * Loads a content info object for the given remoteId. - * - * To load fields use loadContent - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content with the given remote id does not exist - * - * @param string $remoteId - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - public function loadContentInfoByRemoteId(string $remoteId): ContentInfo - { - $contentInfo = $this->internalLoadContentInfoByRemoteId($remoteId); - - if (!$this->permissionResolver->canUser('content', 'read', $contentInfo)) { - throw new UnauthorizedException('content', 'read', ['remoteId' => $remoteId]); - } - - return $contentInfo; - } - - /** - * Loads a version info of the given content object. - * - * If no version number is given, the method returns the current version - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the version with the given number does not exist - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param int|null $versionNo the version number. If not given the current version is returned. - * - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo - */ - public function loadVersionInfo(ContentInfo $contentInfo, ?int $versionNo = null): APIVersionInfo - { - return $this->loadVersionInfoById($contentInfo->id, $versionNo); - } - - /** - * Loads a version info of the given content object id. - * - * If no version number is given, the method returns the current version - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the version with the given number does not exist - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version - * - * @param int $contentId - * @param int|null $versionNo the version number. If not given the current version is returned. - * - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo - */ - public function loadVersionInfoById(int $contentId, ?int $versionNo = null): APIVersionInfo - { - try { - $spiVersionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo( - $contentId, - $versionNo - ); - } catch (APINotFoundException $e) { - throw new NotFoundException( - 'VersionInfo', - [ - 'contentId' => $contentId, - 'versionNo' => $versionNo, - ], - $e - ); - } - - $versionInfo = $this->contentDomainMapper->buildVersionInfoDomainObject($spiVersionInfo); - - if ($versionInfo->isPublished()) { - $function = 'read'; - } else { - $function = 'versionread'; - } - - if (!$this->permissionResolver->canUser('content', $function, $versionInfo)) { - throw new UnauthorizedException('content', $function, ['contentId' => $contentId]); - } - - return $versionInfo; - } - - public function loadVersionInfoListByContentInfo(array $contentInfoList): array - { - foreach ($contentInfoList as $idx => $contentInfo) { - if (!$contentInfo instanceof ContentInfo) { - throw new InvalidArgumentException( - '$contentInfoList', - sprintf( - 'Element at position %d is not an instance of %s', - $idx, - ContentInfo::class - ) - ); - } - } - - $contentIds = array_map( - static function (ContentInfo $contentInfo): int { - return $contentInfo->getId(); - }, - $contentInfoList - ); - - $persistenceVersionInfos = $this->persistenceHandler - ->contentHandler() - ->loadVersionInfoList($contentIds); - - $versionInfoList = []; - foreach ($persistenceVersionInfos as $persistenceVersionInfo) { - $versionInfo = $this->contentDomainMapper->buildVersionInfoDomainObject($persistenceVersionInfo); - if ($this->permissionResolver->canUser('content', 'read', $versionInfo)) { - $versionInfoList[$versionInfo->getContentInfo()->getId()] = $versionInfo; - } - } - - return $versionInfoList; - } - - /** - * {@inheritdoc} - */ - public function loadContentByContentInfo(ContentInfo $contentInfo, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): APIContent - { - // Change $useAlwaysAvailable to false to avoid contentInfo lookup if we know alwaysAvailable is disabled - if ($useAlwaysAvailable && !$contentInfo->alwaysAvailable) { - $useAlwaysAvailable = false; - } - - return $this->loadContent( - $contentInfo->id, - $languages, - $versionNo,// On purpose pass as-is and not use $contentInfo, to make sure to return actual current version on null - $useAlwaysAvailable - ); - } - - /** - * {@inheritdoc} - */ - public function loadContentByVersionInfo(APIVersionInfo $versionInfo, array $languages = null, bool $useAlwaysAvailable = true): APIContent - { - // Change $useAlwaysAvailable to false to avoid contentInfo lookup if we know alwaysAvailable is disabled - if ($useAlwaysAvailable && !$versionInfo->getContentInfo()->alwaysAvailable) { - $useAlwaysAvailable = false; - } - - return $this->loadContent( - $versionInfo->getContentInfo()->id, - $languages, - $versionInfo->versionNo, - $useAlwaysAvailable - ); - } - - /** - * {@inheritdoc} - */ - public function loadContent(int $contentId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): APIContent - { - $content = $this->internalLoadContentById($contentId, $languages, $versionNo, $useAlwaysAvailable); - - if (!$this->permissionResolver->canUser('content', 'read', $content)) { - throw new UnauthorizedException('content', 'read', ['contentId' => $contentId]); - } - if ( - !$content->getVersionInfo()->isPublished() - && !$this->permissionResolver->canUser('content', 'versionread', $content) - ) { - throw new UnauthorizedException('content', 'versionread', ['contentId' => $contentId, 'versionNo' => $versionNo]); - } - - return $content; - } - - public function internalLoadContentById( - int $id, - ?array $languages = null, - int $versionNo = null, - bool $useAlwaysAvailable = true - ): APIContent { - try { - $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($id); - - return $this->internalLoadContentBySPIContentInfo( - $spiContentInfo, - $languages, - $versionNo, - $useAlwaysAvailable - ); - } catch (APINotFoundException $e) { - throw new NotFoundException( - 'Content', - [ - 'id' => $id, - 'languages' => $languages, - 'versionNo' => $versionNo, - ], - $e - ); - } - } - - public function internalLoadContentByRemoteId( - string $remoteId, - array $languages = null, - int $versionNo = null, - bool $useAlwaysAvailable = true - ): APIContent { - try { - $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfoByRemoteId($remoteId); - - return $this->internalLoadContentBySPIContentInfo( - $spiContentInfo, - $languages, - $versionNo, - $useAlwaysAvailable - ); - } catch (APINotFoundException $e) { - throw new NotFoundException( - 'Content', - [ - 'remoteId' => $remoteId, - 'languages' => $languages, - 'versionNo' => $versionNo, - ], - $e - ); - } - } - - private function internalLoadContentBySPIContentInfo(SPIContentInfo $spiContentInfo, array $languages = null, int $versionNo = null, bool $useAlwaysAvailable = true): APIContent - { - $loadLanguages = $languages; - $alwaysAvailableLanguageCode = null; - // Set main language on $languages filter if not empty (all) and $useAlwaysAvailable being true - // @todo Move use always available logic to SPI load methods, like done in location handler in 7.x - if (!empty($loadLanguages) && $useAlwaysAvailable && $spiContentInfo->alwaysAvailable) { - $loadLanguages[] = $alwaysAvailableLanguageCode = $spiContentInfo->mainLanguageCode; - $loadLanguages = array_unique($loadLanguages); - } - - $spiContent = $this->persistenceHandler->contentHandler()->load( - $spiContentInfo->id, - $versionNo, - $loadLanguages - ); - - if ($languages === null) { - $languages = []; - } - - return $this->contentDomainMapper->buildContentDomainObject( - $spiContent, - $this->repository->getContentTypeService()->loadContentType( - $spiContent->versionInfo->contentInfo->contentTypeId, - $languages - ), - $languages, - $alwaysAvailableLanguageCode - ); - } - - /** - * Loads content in a version for the content object reference by the given remote id. - * - * If no version is given, the method returns the current version - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the content or version with the given remote id does not exist - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions - * - * @param string $remoteId - * @param array $languages A language filter for fields. If not given all languages are returned - * @param int $versionNo the version number. If not given the current version is returned - * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function loadContentByRemoteId(string $remoteId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): APIContent - { - $content = $this->internalLoadContentByRemoteId($remoteId, $languages, $versionNo, $useAlwaysAvailable); - - if (!$this->permissionResolver->canUser('content', 'read', $content)) { - throw new UnauthorizedException('content', 'read', ['remoteId' => $remoteId]); - } - - if ( - !$content->getVersionInfo()->isPublished() - && !$this->permissionResolver->canUser('content', 'versionread', $content) - ) { - throw new UnauthorizedException('content', 'versionread', ['remoteId' => $remoteId, 'versionNo' => $versionNo]); - } - - return $content; - } - - /** - * Bulk-load Content items by the list of ContentInfo Value Objects. - * - * Note: it does not throw exceptions on load, just ignores erroneous Content item. - * Moreover, since the method works on pre-loaded ContentInfo list, it is assumed that user is - * allowed to access every Content on the list. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo[] $contentInfoList - * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on - * returned value object. If not given all languages are returned. - * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true, - * unless all languages have been asked for. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content[] list of Content items with Content Ids as keys - */ - public function loadContentListByContentInfo( - array $contentInfoList, - array $languages = [], - bool $useAlwaysAvailable = true - ): iterable { - $loadAllLanguages = $languages === Language::ALL; - $contentIds = []; - $contentTypeIds = []; - $translations = $languages; - foreach ($contentInfoList as $contentInfo) { - $contentIds[] = $contentInfo->id; - $contentTypeIds[] = $contentInfo->contentTypeId; - // Unless we are told to load all languages, we add main language to translations so they are loaded too - // Might in some case load more languages then intended, but prioritised handling will pick right one - if (!$loadAllLanguages && $useAlwaysAvailable && $contentInfo->alwaysAvailable) { - $translations[] = $contentInfo->mainLanguageCode; - } - } - - $contentList = []; - $translations = array_unique($translations); - $spiContentList = $this->persistenceHandler->contentHandler()->loadContentList( - $contentIds, - $translations - ); - $contentTypeList = $this->repository->getContentTypeService()->loadContentTypeList( - array_unique($contentTypeIds), - $languages - ); - foreach ($spiContentList as $contentId => $spiContent) { - $contentInfo = $spiContent->versionInfo->contentInfo; - $contentList[$contentId] = $this->contentDomainMapper->buildContentDomainObject( - $spiContent, - $contentTypeList[$contentInfo->contentTypeId], - $languages, - $contentInfo->alwaysAvailable ? $contentInfo->mainLanguageCode : null - ); - } - - return $contentList; - } - - /** - * Creates a new content draft assigned to the authenticated user. - * - * If a different userId is given in $contentCreateStruct it is assigned to the given user - * but this required special rights for the authenticated user - * (this is useful for content staging where the transfer process does not - * have to authenticate with the user which created the content object in the source server). - * The user has to publish the draft if it should be visible. - * In 4.x at least one location has to be provided in the location creation array. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create the content in the given location - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the provided remoteId exists in the system, required properties on - * struct are missing or invalid, or if multiple locations are under the - * same parent. - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid, - * or if a required field is missing / set to an empty value. - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType, - * or value is set for non-translatable field in language - * other than main. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct - * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs For each location parent under which a location should be created for the content - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - the newly created content draft - */ - public function createContent(APIContentCreateStruct $contentCreateStruct, array $locationCreateStructs = [], ?array $fieldIdentifiersToValidate = null): APIContent - { - if ($contentCreateStruct->mainLanguageCode === null) { - throw new InvalidArgumentException('$contentCreateStruct', "the 'mainLanguageCode' property must be set"); - } - - if ($contentCreateStruct->contentType === null) { - throw new InvalidArgumentException('$contentCreateStruct', "the 'contentType' property must be set"); - } - - $contentCreateStruct = clone $contentCreateStruct; - - if ($contentCreateStruct->ownerId === null) { - $contentCreateStruct->ownerId = $this->permissionResolver->getCurrentUserReference()->getUserId(); - } - - if ($contentCreateStruct->alwaysAvailable === null) { - $contentCreateStruct->alwaysAvailable = $contentCreateStruct->contentType->defaultAlwaysAvailable ?: false; - } - - $contentCreateStruct->contentType = $this->repository->getContentTypeService()->loadContentType( - $contentCreateStruct->contentType->id - ); - - $contentCreateStruct->fields = $this->contentMapper->getFieldsForCreate( - $contentCreateStruct->fields, - $contentCreateStruct->contentType - ); - - if (empty($contentCreateStruct->sectionId)) { - if (isset($locationCreateStructs[0])) { - $location = $this->repository->getLocationService()->loadLocation( - $locationCreateStructs[0]->parentLocationId - ); - $contentCreateStruct->sectionId = $location->contentInfo->sectionId; - } else { - $contentCreateStruct->sectionId = 1; - } - } - - if (!$this->permissionResolver->canUser('content', 'create', $contentCreateStruct, $locationCreateStructs)) { - throw new UnauthorizedException( - 'content', - 'create', - [ - 'parentLocationId' => isset($locationCreateStructs[0]) ? - $locationCreateStructs[0]->parentLocationId : - null, - 'sectionId' => $contentCreateStruct->sectionId, - ] - ); - } - - if (!empty($contentCreateStruct->remoteId)) { - try { - $this->loadContentByRemoteId($contentCreateStruct->remoteId); - - throw new InvalidArgumentException( - '$contentCreateStruct', - "Another Content item with remoteId '{$contentCreateStruct->remoteId}' exists" - ); - } catch (APINotFoundException $e) { - // Do nothing - } - } else { - $contentCreateStruct->remoteId = $this->contentDomainMapper->getUniqueHash($contentCreateStruct); - } - - $errors = $this->validate( - $contentCreateStruct, - [], - $fieldIdentifiersToValidate - ); - - if (!empty($errors)) { - throw new ContentFieldValidationException($errors); - } - - $spiLocationCreateStructs = $this->buildSPILocationCreateStructs( - $locationCreateStructs, - $contentCreateStruct->contentType - ); - - $languageCodes = $this->contentMapper->getLanguageCodesForCreate($contentCreateStruct); - $fields = $this->contentMapper->mapFieldsForCreate($contentCreateStruct); - - $fieldValues = []; - $spiFields = []; - $inputRelations = []; - $locationIdToContentIdMapping = []; - - foreach ($contentCreateStruct->contentType->getFieldDefinitions() as $fieldDefinition) { - /** @var $fieldType \eZ\Publish\Core\FieldType\FieldType */ - $fieldType = $this->fieldTypeRegistry->getFieldType( - $fieldDefinition->fieldTypeIdentifier - ); - - foreach ($languageCodes as $languageCode) { - $isEmptyValue = false; - $valueLanguageCode = $fieldDefinition->isTranslatable ? $languageCode : $contentCreateStruct->mainLanguageCode; - $isLanguageMain = $languageCode === $contentCreateStruct->mainLanguageCode; - - $fieldValue = $this->contentMapper->getFieldValueForCreate( - $fieldDefinition, - $fields[$fieldDefinition->identifier][$valueLanguageCode] ?? null - ); - - if ($fieldType->isEmptyValue($fieldValue)) { - $isEmptyValue = true; - } - - $this->relationProcessor->appendFieldRelations( - $inputRelations, - $locationIdToContentIdMapping, - $fieldType, - $fieldValue, - $fieldDefinition->id - ); - $fieldValues[$fieldDefinition->identifier][$languageCode] = $fieldValue; - - // Only non-empty value for: translatable field or in main language - if ( - (!$isEmptyValue && $fieldDefinition->isTranslatable) || - (!$isEmptyValue && $isLanguageMain) - ) { - $spiFields[] = new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => $fieldDefinition->id, - 'type' => $fieldDefinition->fieldTypeIdentifier, - 'value' => $fieldType->toPersistenceValue($fieldValue), - 'languageCode' => $languageCode, - 'versionNo' => null, - ] - ); - } - } - } - - $spiContentCreateStruct = new SPIContentCreateStruct( - [ - 'name' => $this->nameSchemaService->resolve( - $contentCreateStruct->contentType->nameSchema, - $contentCreateStruct->contentType, - $fieldValues, - $languageCodes - ), - 'typeId' => $contentCreateStruct->contentType->id, - 'sectionId' => $contentCreateStruct->sectionId, - 'ownerId' => $contentCreateStruct->ownerId, - 'locations' => $spiLocationCreateStructs, - 'fields' => $spiFields, - 'alwaysAvailable' => $contentCreateStruct->alwaysAvailable, - 'remoteId' => $contentCreateStruct->remoteId, - 'modified' => isset($contentCreateStruct->modificationDate) ? $contentCreateStruct->modificationDate->getTimestamp() : time(), - 'initialLanguageId' => $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode( - $contentCreateStruct->mainLanguageCode - )->id, - ] - ); - - $defaultObjectStates = $this->getDefaultObjectStates(); - - $this->repository->beginTransaction(); - try { - $spiContent = $this->persistenceHandler->contentHandler()->create($spiContentCreateStruct); - $this->relationProcessor->processFieldRelations( - $inputRelations, - $spiContent->versionInfo->contentInfo->id, - $spiContent->versionInfo->versionNo, - $contentCreateStruct->contentType - ); - - $objectStateHandler = $this->persistenceHandler->objectStateHandler(); - foreach ($defaultObjectStates as $objectStateGroupId => $objectState) { - $objectStateHandler->setContentState( - $spiContent->versionInfo->contentInfo->id, - $objectStateGroupId, - $objectState->id - ); - } - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->contentDomainMapper->buildContentDomainObject( - $spiContent, - $contentCreateStruct->contentType - ); - } - - /** - * Returns an array of default content states with content state group id as key. - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState[] - */ - protected function getDefaultObjectStates(): array - { - $defaultObjectStatesMap = []; - $objectStateHandler = $this->persistenceHandler->objectStateHandler(); - - foreach ($objectStateHandler->loadAllGroups() as $objectStateGroup) { - foreach ($objectStateHandler->loadObjectStates($objectStateGroup->id) as $objectState) { - // Only register the first object state which is the default one. - $defaultObjectStatesMap[$objectStateGroup->id] = $objectState; - break; - } - } - - return $defaultObjectStatesMap; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType|null $contentType - * - * @return \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct[] - */ - protected function buildSPILocationCreateStructs( - array $locationCreateStructs, - ?ContentType $contentType = null - ): array { - $spiLocationCreateStructs = []; - $parentLocationIdSet = []; - $mainLocation = true; - - foreach ($locationCreateStructs as $locationCreateStruct) { - if (isset($parentLocationIdSet[$locationCreateStruct->parentLocationId])) { - throw new InvalidArgumentException( - '$locationCreateStructs', - "You provided multiple LocationCreateStructs with the same parent Location '{$locationCreateStruct->parentLocationId}'" - ); - } - - if ($locationCreateStruct->sortField === null) { - $locationCreateStruct->sortField = $contentType->defaultSortField ?? Location::SORT_FIELD_NAME; - } - if ($locationCreateStruct->sortOrder === null) { - $locationCreateStruct->sortOrder = $contentType->defaultSortOrder ?? Location::SORT_ORDER_ASC; - } - - $parentLocationIdSet[$locationCreateStruct->parentLocationId] = true; - $parentLocation = $this->repository->getLocationService()->loadLocation( - $locationCreateStruct->parentLocationId - ); - - $spiLocationCreateStructs[] = $this->contentDomainMapper->buildSPILocationCreateStruct( - $locationCreateStruct, - $parentLocation, - $mainLocation, - // For Content draft contentId and contentVersionNo are set in ContentHandler upon draft creation - null, - null, - false - ); - - // First Location in the list will be created as main Location - $mainLocation = false; - } - - return $spiLocationCreateStructs; - } - - /** - * Updates the metadata. - * - * (see {@link ContentMetadataUpdateStruct}) of a content object - to update fields use updateContent - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update the content meta data - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the remoteId in $contentMetadataUpdateStruct is set but already exists - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param \eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct $contentMetadataUpdateStruct - * - * @return \eZ\Publish\API\Repository\Values\Content\Content the content with the updated attributes - */ - public function updateContentMetadata(ContentInfo $contentInfo, ContentMetadataUpdateStruct $contentMetadataUpdateStruct): APIContent - { - $propertyCount = 0; - foreach ($contentMetadataUpdateStruct as $propertyName => $propertyValue) { - if (isset($contentMetadataUpdateStruct->$propertyName)) { - ++$propertyCount; - } - } - if ($propertyCount === 0) { - throw new InvalidArgumentException( - '$contentMetadataUpdateStruct', - 'At least one property must be set' - ); - } - - $loadedContentInfo = $this->loadContentInfo($contentInfo->id); - - if (!$this->permissionResolver->canUser('content', 'edit', $loadedContentInfo)) { - throw new UnauthorizedException('content', 'edit', ['contentId' => $loadedContentInfo->id]); - } - - if (isset($contentMetadataUpdateStruct->remoteId)) { - try { - $existingContentInfo = $this->loadContentInfoByRemoteId($contentMetadataUpdateStruct->remoteId); - - if ($existingContentInfo->id !== $loadedContentInfo->id) { - throw new InvalidArgumentException( - '$contentMetadataUpdateStruct', - "Another Content item with remoteId '{$contentMetadataUpdateStruct->remoteId}' exists" - ); - } - } catch (APINotFoundException $e) { - // Do nothing - } - } - - $this->repository->beginTransaction(); - try { - if ($propertyCount > 1 || !isset($contentMetadataUpdateStruct->mainLocationId)) { - $this->persistenceHandler->contentHandler()->updateMetadata( - $loadedContentInfo->id, - new SPIMetadataUpdateStruct( - [ - 'ownerId' => $contentMetadataUpdateStruct->ownerId, - 'publicationDate' => isset($contentMetadataUpdateStruct->publishedDate) ? - $contentMetadataUpdateStruct->publishedDate->getTimestamp() : - null, - 'modificationDate' => isset($contentMetadataUpdateStruct->modificationDate) ? - $contentMetadataUpdateStruct->modificationDate->getTimestamp() : - null, - 'mainLanguageId' => isset($contentMetadataUpdateStruct->mainLanguageCode) ? - $this->repository->getContentLanguageService()->loadLanguage( - $contentMetadataUpdateStruct->mainLanguageCode - )->id : - null, - 'alwaysAvailable' => $contentMetadataUpdateStruct->alwaysAvailable, - 'remoteId' => $contentMetadataUpdateStruct->remoteId, - 'name' => $contentMetadataUpdateStruct->name, - ] - ) - ); - } - - // Change main location - if (isset($contentMetadataUpdateStruct->mainLocationId) - && $loadedContentInfo->mainLocationId !== $contentMetadataUpdateStruct->mainLocationId) { - $this->persistenceHandler->locationHandler()->changeMainLocation( - $loadedContentInfo->id, - $contentMetadataUpdateStruct->mainLocationId - ); - } - - // Republish URL aliases to update always-available flag - if (isset($contentMetadataUpdateStruct->alwaysAvailable) - && $loadedContentInfo->alwaysAvailable !== $contentMetadataUpdateStruct->alwaysAvailable) { - $content = $this->loadContent($loadedContentInfo->id); - $this->publishUrlAliasesForContent($content, false); - } - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return isset($content) ? $content : $this->loadContent($loadedContentInfo->id); - } - - /** - * Publishes URL aliases for all locations of a given content. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param bool $updatePathIdentificationString this parameter is legacy storage specific for updating - * ezcontentobject_tree.path_identification_string, it is ignored by other storage engines - */ - protected function publishUrlAliasesForContent(APIContent $content, bool $updatePathIdentificationString = true): void - { - $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content); - $locations = $this->repository->getLocationService()->loadLocations( - $content->getVersionInfo()->getContentInfo() - ); - $urlAliasHandler = $this->persistenceHandler->urlAliasHandler(); - foreach ($locations as $location) { - foreach ($urlAliasNames as $languageCode => $name) { - $urlAliasHandler->publishUrlAliasForLocation( - $location->id, - $location->parentLocationId, - $name, - $languageCode, - $content->contentInfo->alwaysAvailable, - $updatePathIdentificationString ? $languageCode === $content->contentInfo->mainLanguageCode : false - ); - } - // archive URL aliases of Translations that got deleted - $urlAliasHandler->archiveUrlAliasesForDeletedTranslations( - $location->id, - $location->parentLocationId, - $content->versionInfo->languageCodes - ); - } - } - - /** - * Deletes a content object including all its versions and locations including their subtrees. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete the content (in one of the locations of the given content object) - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return mixed[] Affected Location Id's - */ - public function deleteContent(ContentInfo $contentInfo): iterable - { - $contentInfo = $this->internalLoadContentInfoById($contentInfo->id); - $versionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo( - $contentInfo->id, - $contentInfo->currentVersionNo - ); - $translations = $versionInfo->languageCodes; - $target = (new Target\Version())->deleteTranslations($translations); - - if (!$this->permissionResolver->canUser('content', 'remove', $contentInfo, [$target])) { - throw new UnauthorizedException('content', 'remove', ['contentId' => $contentInfo->id]); - } - - $affectedLocations = []; - $this->repository->beginTransaction(); - try { - // Load Locations first as deleting Content also deletes belonging Locations - $spiLocations = $this->persistenceHandler->locationHandler()->loadLocationsByContent($contentInfo->id); - $this->persistenceHandler->contentHandler()->deleteContent($contentInfo->id); - $urlAliasHandler = $this->persistenceHandler->urlAliasHandler(); - foreach ($spiLocations as $spiLocation) { - $urlAliasHandler->locationDeleted($spiLocation->id); - $affectedLocations[] = $spiLocation->id; - } - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $affectedLocations; - } - - /** - * Creates a draft from a published or archived version. - * - * If no version is given, the current published version is used. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo|null $versionInfo - * @param \eZ\Publish\API\Repository\Values\User\User|null $creator if set given user is used to create the draft - otherwise the current-user is used - * @param \eZ\Publish\API\Repository\Values\Content\Language|null if not set the draft is created with the initialLanguage code of the source version or if not present with the main language. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - the newly created content draft - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the current-user is not allowed to create the draft - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to create the draft - */ - public function createContentDraft( - ContentInfo $contentInfo, - ?APIVersionInfo $versionInfo = null, - ?User $creator = null, - ?Language $language = null - ): APIContent { - $contentInfo = $this->loadContentInfo($contentInfo->id); - - if ($versionInfo !== null) { - // Check that given $contentInfo and $versionInfo belong to the same content - if ($versionInfo->getContentInfo()->id != $contentInfo->id) { - throw new InvalidArgumentException( - '$versionInfo', - 'VersionInfo does not belong to the same Content item as the given ContentInfo' - ); - } - - $versionInfo = $this->loadVersionInfoById($contentInfo->id, $versionInfo->versionNo); - - switch ($versionInfo->status) { - case VersionInfo::STATUS_PUBLISHED: - case VersionInfo::STATUS_ARCHIVED: - break; - - default: - // @todo: throw an exception here, to be defined - throw new BadStateException( - '$versionInfo', - 'Cannot create a draft from a draft version' - ); - } - - $versionNo = $versionInfo->versionNo; - } elseif ($contentInfo->published) { - $versionNo = $contentInfo->currentVersionNo; - } else { - // @todo: throw an exception here, to be defined - throw new BadStateException( - '$contentInfo', - 'Content is not published. A draft can be created only from a published or archived version.' - ); - } - - if ($creator === null) { - $creator = $this->permissionResolver->getCurrentUserReference(); - } - - $fallbackLanguageCode = $versionInfo->initialLanguageCode ?? $contentInfo->mainLanguageCode; - $languageCode = $language->languageCode ?? $fallbackLanguageCode; - - if (!$this->permissionResolver->canUser( - 'content', - 'edit', - $contentInfo, - [ - (new Target\Builder\VersionBuilder()) - ->changeStatusTo(APIVersionInfo::STATUS_DRAFT) - ->build(), - ] - )) { - throw new UnauthorizedException( - 'content', - 'edit', - ['contentId' => $contentInfo->id] - ); - } - - $this->repository->beginTransaction(); - try { - $spiContent = $this->persistenceHandler->contentHandler()->createDraftFromVersion( - $contentInfo->id, - $versionNo, - $creator->getUserId(), - $languageCode - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->contentDomainMapper->buildContentDomainObject( - $spiContent, - $this->repository->getContentTypeService()->loadContentType( - $spiContent->versionInfo->contentInfo->contentTypeId - ) - ); - } - - public function countContentDrafts(?User $user = null): int - { - if ($this->permissionResolver->hasAccess('content', 'versionread') === false) { - return 0; - } - - return $this->persistenceHandler->contentHandler()->countDraftsForUser( - $this->resolveUser($user)->getUserId() - ); - } - - /** - * Loads drafts for a user. - * - * If no user is given the drafts for the authenticated user are returned - * - * @param \eZ\Publish\API\Repository\Values\User\User|null $user - * - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo[] Drafts owned by the given user - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function loadContentDrafts(?User $user = null): iterable - { - // throw early if user has absolutely no access to versionread - if ($this->permissionResolver->hasAccess('content', 'versionread') === false) { - throw new UnauthorizedException('content', 'versionread'); - } - - $spiVersionInfoList = $this->persistenceHandler->contentHandler()->loadDraftsForUser( - $this->resolveUser($user)->getUserId() - ); - $versionInfoList = []; - foreach ($spiVersionInfoList as $spiVersionInfo) { - $versionInfo = $this->contentDomainMapper->buildVersionInfoDomainObject($spiVersionInfo); - // @todo: Change this to filter returned drafts by permissions instead of throwing - if (!$this->permissionResolver->canUser('content', 'versionread', $versionInfo)) { - throw new UnauthorizedException('content', 'versionread', ['contentId' => $versionInfo->contentInfo->id]); - } - - $versionInfoList[] = $versionInfo; - } - - return $versionInfoList; - } - - public function loadContentDraftList(?User $user = null, int $offset = 0, int $limit = -1): ContentDraftList - { - $list = new ContentDraftList(); - if ($this->permissionResolver->hasAccess('content', 'versionread') === false) { - return $list; - } - - $list->totalCount = $this->persistenceHandler->contentHandler()->countDraftsForUser( - $this->resolveUser($user)->getUserId() - ); - if ($list->totalCount > 0) { - $spiVersionInfoList = $this->persistenceHandler->contentHandler()->loadDraftListForUser( - $this->resolveUser($user)->getUserId(), - $offset, - $limit - ); - foreach ($spiVersionInfoList as $spiVersionInfo) { - $versionInfo = $this->contentDomainMapper->buildVersionInfoDomainObject($spiVersionInfo); - if ($this->permissionResolver->canUser('content', 'versionread', $versionInfo)) { - $list->items[] = new ContentDraftListItem($versionInfo); - } else { - $list->items[] = new UnauthorizedContentDraftListItem( - 'content', - 'versionread', - ['contentId' => $versionInfo->contentInfo->id] - ); - } - } - } - - return $list; - } - - /** - * Updates the fields of a draft. - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct - * - * @return \eZ\Publish\API\Repository\Values\Content\Content the content draft with the updated fields - * - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid, - * or if a required field is missing / set to an empty value. - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType, - * or value is set for non-translatable field in language - * other than main. - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update this version - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if a property on the struct is invalid. - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function updateContent(APIVersionInfo $versionInfo, APIContentUpdateStruct $contentUpdateStruct, ?array $fieldIdentifiersToValidate = null): APIContent - { - /** @var $content \eZ\Publish\Core\Repository\Values\Content\Content */ - $content = $this->loadContent( - $versionInfo->getContentInfo()->id, - null, - $versionInfo->versionNo - ); - - $updatedFields = $this->contentMapper->getFieldsForUpdate($contentUpdateStruct->fields, $content); - - if (!$this->repository->getPermissionResolver()->canUser( - 'content', - 'edit', - $content, - [ - (new Target\Builder\VersionBuilder()) - ->updateFields($updatedFields) - ->updateFieldsTo( - $contentUpdateStruct->initialLanguageCode, - $contentUpdateStruct->fields - ) - ->build(), - ] - )) { - throw new UnauthorizedException('content', 'edit', ['contentId' => $content->id]); - } - - return $this->internalUpdateContent($versionInfo, $contentUpdateStruct, $fieldIdentifiersToValidate); - } - - /** - * Updates the fields of a draft without checking the permissions. - * - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid, - * or if a required field is missing / set to an empty value. - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType, - * or value is set for non-translatable field in language - * other than main. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if a property on the struct is invalid. - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - protected function internalUpdateContent( - APIVersionInfo $versionInfo, - APIContentUpdateStruct $contentUpdateStruct, - ?array $fieldIdentifiersToValidate = null, - bool $copyEmptyField = false - ): Content { - $contentUpdateStruct = clone $contentUpdateStruct; - - /** @var $content \eZ\Publish\Core\Repository\Values\Content\Content */ - $content = $this->internalLoadContentById( - $versionInfo->getContentInfo()->id, - null, - $versionInfo->versionNo - ); - - if (!$content->versionInfo->isDraft()) { - throw new BadStateException( - '$versionInfo', - 'The version is not a draft and cannot be updated' - ); - } - - $errors = $this->validate( - $contentUpdateStruct, - ['content' => $content], - $fieldIdentifiersToValidate - ); - - if (!empty($errors)) { - throw ContentFieldValidationException::createNewWithMultiline($errors, $content->getName()); - } - - $mainLanguageCode = $content->contentInfo->mainLanguageCode; - if ($contentUpdateStruct->initialLanguageCode === null) { - $contentUpdateStruct->initialLanguageCode = $mainLanguageCode; - } - - $allLanguageCodes = $this->contentMapper->getLanguageCodesForUpdate($contentUpdateStruct, $content); - $contentLanguageHandler = $this->persistenceHandler->contentLanguageHandler(); - foreach ($allLanguageCodes as $languageCode) { - $contentLanguageHandler->loadByLanguageCode($languageCode); - } - - $contentType = $this->repository->getContentTypeService()->loadContentType( - $content->contentInfo->contentTypeId - ); - $fields = $this->contentMapper->mapFieldsForUpdate( - $contentUpdateStruct, - $contentType, - $mainLanguageCode - ); - - $fieldValues = []; - $spiFields = []; - $inputRelations = []; - $locationIdToContentIdMapping = []; - - foreach ($contentType->getFieldDefinitions() as $fieldDefinition) { - $fieldType = $this->fieldTypeRegistry->getFieldType( - $fieldDefinition->fieldTypeIdentifier - ); - - foreach ($allLanguageCodes as $languageCode) { - $isCopied = $isEmpty = $isRetained = false; - $isLanguageNew = !in_array($languageCode, $content->versionInfo->languageCodes); - $valueLanguageCode = $fieldDefinition->isTranslatable ? $languageCode : $mainLanguageCode; - $isFieldUpdated = isset($fields[$fieldDefinition->identifier][$valueLanguageCode]); - $isProcessed = isset($fieldValues[$fieldDefinition->identifier][$valueLanguageCode]); - - if (!$isFieldUpdated && !$isLanguageNew) { - $isRetained = true; - } elseif (!$isFieldUpdated && $isLanguageNew && !$fieldDefinition->isTranslatable) { - $isCopied = true; - } - - $fieldValue = $this->contentMapper->getFieldValueForUpdate( - $fields[$fieldDefinition->identifier][$valueLanguageCode] ?? null, - $content->getField($fieldDefinition->identifier, $valueLanguageCode), - $fieldDefinition, - $isLanguageNew - ); - - if ($fieldType->isEmptyValue($fieldValue)) { - $isEmpty = true; - } - - $this->relationProcessor->appendFieldRelations( - $inputRelations, - $locationIdToContentIdMapping, - $fieldType, - $fieldValue, - $fieldDefinition->id - ); - $fieldValues[$fieldDefinition->identifier][$languageCode] = $fieldValue; - - if ($isRetained || $isCopied || ($isLanguageNew && $isEmpty && !$copyEmptyField) || $isProcessed) { - continue; - } - - $spiFields[] = new SPIField( - [ - 'id' => $isLanguageNew ? - null : - $content->getField($fieldDefinition->identifier, $languageCode)->id, - 'fieldDefinitionId' => $fieldDefinition->id, - 'type' => $fieldDefinition->fieldTypeIdentifier, - 'value' => $fieldType->toPersistenceValue($fieldValue), - 'languageCode' => $languageCode, - 'versionNo' => $versionInfo->versionNo, - ] - ); - } - } - - $spiContentUpdateStruct = new SPIContentUpdateStruct( - [ - 'name' => $this->nameSchemaService->resolveNameSchema( - $content, - $fieldValues, - $allLanguageCodes, - $contentType - ), - 'creatorId' => $contentUpdateStruct->creatorId ?: $this->permissionResolver->getCurrentUserReference()->getUserId(), - 'fields' => $spiFields, - 'modificationDate' => time(), - 'initialLanguageId' => $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode( - $contentUpdateStruct->initialLanguageCode - )->id, - ] - ); - $existingRelations = $this->internalLoadRelations($versionInfo); - - $this->repository->beginTransaction(); - try { - $spiContent = $this->persistenceHandler->contentHandler()->updateContent( - $versionInfo->getContentInfo()->id, - $versionInfo->versionNo, - $spiContentUpdateStruct - ); - $this->relationProcessor->processFieldRelations( - $inputRelations, - $spiContent->versionInfo->contentInfo->id, - $spiContent->versionInfo->versionNo, - $contentType, - $existingRelations - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->contentDomainMapper->buildContentDomainObject( - $spiContent, - $contentType - ); - } - - /** - * Publishes a content version. - * - * Publishes a content version and deletes archive versions if they overflow max archive versions. - * Max archive versions are currently a configuration, but might be moved to be a param of ContentType in the future. - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - * @param string[] $translations - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function publishVersion(APIVersionInfo $versionInfo, array $translations = Language::ALL): APIContent - { - $content = $this->internalLoadContentById( - $versionInfo->contentInfo->id, - null, - $versionInfo->versionNo - ); - - $targets = []; - if (!empty($translations)) { - $targets[] = (new Target\Builder\VersionBuilder()) - ->publishTranslations($translations) - ->build(); - } - - if (!$this->permissionResolver->canUser( - 'content', - 'publish', - $content, - $targets - )) { - throw new UnauthorizedException( - 'content', - 'publish', - ['contentId' => $content->id] - ); - } - - $this->repository->beginTransaction(); - try { - $this->copyTranslationsFromPublishedVersion($content->versionInfo, $translations); - $this->copyNonTranslatableFieldsFromPublishedVersion($content); - $content = $this->internalPublishVersion($content->getVersionInfo(), null, $translations); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $content; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - protected function copyNonTranslatableFieldsFromPublishedVersion(APIContent $currentVersionContent): void - { - $versionInfo = $currentVersionContent->getVersionInfo(); - $contentType = $currentVersionContent->getContentType(); - - $publishedContent = $this->internalLoadContentById($versionInfo->getContentInfo()->getId()); - $publishedVersionInfo = $publishedContent->getVersionInfo(); - - if (!$publishedVersionInfo->isPublished()) { - return; - } - - $mainLanguageCode = $publishedContent->getVersionInfo()->getContentInfo()->getMainLanguageCode(); - $publishedContentFieldsInMainLanguage = $publishedContent->getFieldsByLanguage($mainLanguageCode); - - $fieldValues = []; - $persistenceFields = []; - foreach ($currentVersionContent->getFields() as $field) { - $fieldDefinition = $contentType->getFieldDefinition($field->fieldDefIdentifier); - $fieldValues[$fieldDefinition->identifier][$field->languageCode] = $field->getValue(); - - if ( - $fieldDefinition->isTranslatable - || $field->languageCode === $versionInfo->initialLanguageCode - ) { - continue; - } - - $fieldType = $this->fieldTypeRegistry->getFieldType( - $fieldDefinition->fieldTypeIdentifier - ); - - $newValue = ( - $versionInfo->versionNo >= $publishedVersionInfo->versionNo - && $versionInfo->initialLanguageCode === $mainLanguageCode - ) - ? $field->getValue() - : $publishedContentFieldsInMainLanguage[$field->fieldDefIdentifier]->getValue(); - - $fieldValues[$fieldDefinition->identifier][$field->languageCode] = $newValue; - - $persistenceFields[] = new SPIField( - [ - 'id' => $field->id, - 'fieldDefinitionId' => $fieldDefinition->id, - 'type' => $fieldDefinition->fieldTypeIdentifier, - 'value' => $fieldType->toPersistenceValue($newValue), - 'languageCode' => $field->languageCode, - 'versionNo' => $versionInfo->versionNo, - ] - ); - } - - if (count($persistenceFields) === 0) { - return; - } - - $updateStruct = new SPIContentUpdateStruct(); - $updateStruct->name = $this->nameSchemaService->resolveNameSchema( - $currentVersionContent, - $fieldValues, - $versionInfo->languageCodes, - $contentType - ); - $updateStruct->initialLanguageId = $this->persistenceHandler - ->contentLanguageHandler() - ->loadByLanguageCode( - $versionInfo->initialLanguageCode - )->id; - $updateStruct->creatorId = $versionInfo->creatorId; - $updateStruct->modificationDate = time(); - $updateStruct->fields = $persistenceFields; - - $this->persistenceHandler->contentHandler()->updateContent( - $versionInfo->getContentInfo()->getId(), - $versionInfo->versionNo, - $updateStruct - ); - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - * @param array $translations - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - protected function copyTranslationsFromPublishedVersion(APIVersionInfo $versionInfo, array $translations = []): void - { - $contendId = $versionInfo->contentInfo->id; - - $currentContent = $this->internalLoadContentById($contendId); - $currentVersionInfo = $currentContent->versionInfo; - - // Copying occurs only if: - // - There is published Version - // - Published version is older than the currently published one unless specific translations are provided. - if (!$currentVersionInfo->isPublished() || - ($versionInfo->versionNo >= $currentVersionInfo->versionNo && empty($translations))) { - return; - } - - if (empty($translations)) { - $languagesToCopy = array_diff( - $currentVersionInfo->languageCodes, - $versionInfo->languageCodes - ); - } else { - $languagesToCopy = array_diff( - $currentVersionInfo->languageCodes, - $translations - ); - } - - if (empty($languagesToCopy)) { - return; - } - - $contentType = $this->repository->getContentTypeService()->loadContentType( - $currentVersionInfo->contentInfo->contentTypeId - ); - - // Find only translatable fields to update with selected languages - $updateStruct = $this->newContentUpdateStruct(); - $updateStruct->initialLanguageCode = $versionInfo->initialLanguageCode; - - $contentToPublish = $this->internalLoadContentById($contendId, null, $versionInfo->versionNo); - $fallbackUpdateStruct = $this->newContentUpdateStruct(); - - foreach ($currentContent->getFields() as $field) { - $fieldDefinition = $contentType->getFieldDefinition($field->fieldDefIdentifier); - - if (!$fieldDefinition->isTranslatable || !\in_array($field->languageCode, $languagesToCopy)) { - continue; - } - - $fieldType = $this->fieldTypeRegistry->getFieldType( - $fieldDefinition->fieldTypeIdentifier - ); - - $newValue = $contentToPublish->getFieldValue( - $fieldDefinition->identifier, - $field->languageCode - ); - - $value = $field->value; - if ($fieldDefinition->isRequired && $fieldType->isEmptyValue($value)) { - if (!$fieldType->isEmptyValue($fieldDefinition->defaultValue)) { - $value = $fieldDefinition->defaultValue; - } else { - $value = $contentToPublish->getFieldValue($field->fieldDefIdentifier, $versionInfo->initialLanguageCode); - } - $fallbackUpdateStruct->setField( - $field->fieldDefIdentifier, - $value, - $field->languageCode - ); - continue; - } - - if ($newValue !== null - && $field->value !== null - && $this->fieldValuesAreEqual($fieldType, $newValue, $field->value) - ) { - continue; - } - - $updateStruct->setField($field->fieldDefIdentifier, $value, $field->languageCode); - } - - // Nothing to copy, skip update - if (empty($updateStruct->fields)) { - return; - } - - // Do fallback only if content needs to be updated - foreach ($fallbackUpdateStruct->fields as $fallbackField) { - $updateStruct->setField($fallbackField->fieldDefIdentifier, $fallbackField->value, $fallbackField->languageCode); - } - - $this->internalUpdateContent($versionInfo, $updateStruct, null, true); - } - - protected function fieldValuesAreEqual(FieldType $fieldType, Value $value1, Value $value2): bool - { - if ($fieldType instanceof Comparable) { - return $fieldType->valuesEqual($value1, $value2); - } else { - @trigger_error( - \sprintf( - 'In eZ Platform 2.5 and 3.x %s should implement %s. ' . - 'Since the 4.0 major release FieldType\Comparable contract will be a part of %s', - get_class($fieldType), - Comparable::class, - FieldType::class - ), - E_USER_DEPRECATED - ); - - return $fieldType->toHash($value1) === $fieldType->toHash($value2); - } - } - - /** - * Publishes a content version. - * - * Publishes a content version and deletes archive versions if they overflow max archive versions. - * Max archive versions are currently a configuration, but might be moved to be a param of ContentType in the future. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - * @param int|null $publicationDate If null existing date is kept if there is one, otherwise current time is used. - * @param string[] $translations - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - protected function internalPublishVersion(APIVersionInfo $versionInfo, $publicationDate = null, array $translations = Language::ALL) - { - if (!$versionInfo->isDraft()) { - throw new BadStateException('$versionInfo', 'Only versions in draft status can be published.'); - } - - $currentTime = $this->getUnixTimestamp(); - if ($publicationDate === null && $versionInfo->versionNo === 1) { - $publicationDate = $currentTime; - } - - $errors = $this->validate( - $versionInfo, - [ - 'content' => $this->internalLoadContentById( - $versionInfo->getContentInfo()->id, - null, - $versionInfo->versionNo - ), - 'translations' => $translations, - ] - ); - - if (!empty($errors)) { - throw ContentFieldValidationException::createNewWithMultiline($errors, $versionInfo->getContentInfo()->name); - } - - $contentInfo = $versionInfo->getContentInfo(); - $metadataUpdateStruct = new SPIMetadataUpdateStruct(); - $metadataUpdateStruct->publicationDate = $publicationDate; - $metadataUpdateStruct->modificationDate = $currentTime; - $metadataUpdateStruct->isHidden = $contentInfo->isHidden; - - $contentId = $contentInfo->id; - $spiContent = $this->persistenceHandler->contentHandler()->publish( - $contentId, - $versionInfo->versionNo, - $metadataUpdateStruct - ); - - $content = $this->contentDomainMapper->buildContentDomainObject( - $spiContent, - $this->repository->getContentTypeService()->loadContentType( - $spiContent->versionInfo->contentInfo->contentTypeId - ) - ); - - $this->publishUrlAliasesForContent($content); - - if ($this->settings['remove_archived_versions_on_publish']) { - $this->deleteArchivedVersionsOverLimit($contentId); - } - - return $content; - } - - protected function deleteArchivedVersionsOverLimit(int $contentId): void - { - // Delete version archive overflow if any, limit is 0-50 (however 0 will mean 1 if content is unpublished) - $archiveList = $this->persistenceHandler->contentHandler()->listVersions( - $contentId, - APIVersionInfo::STATUS_ARCHIVED, - 100 // Limited to avoid publishing taking to long, besides SE limitations this is why limit is max 50 - ); - - $maxVersionArchiveCount = max(0, min(50, $this->settings['default_version_archive_limit'])); - while (!empty($archiveList) && count($archiveList) > $maxVersionArchiveCount) { - /** @var \eZ\Publish\SPI\Persistence\Content\VersionInfo $archiveVersion */ - $archiveVersion = array_shift($archiveList); - $this->persistenceHandler->contentHandler()->deleteVersion( - $contentId, - $archiveVersion->versionNo - ); - } - } - - /** - * @return int - */ - protected function getUnixTimestamp(): int - { - return time(); - } - - /** - * Removes the given version. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is in - * published state or is a last version of Content in non draft state - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove this version - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - */ - public function deleteVersion(APIVersionInfo $versionInfo): void - { - $contentHandler = $this->persistenceHandler->contentHandler(); - - if ($versionInfo->isPublished()) { - throw new BadStateException( - '$versionInfo', - 'The Version is published and cannot be removed' - ); - } - - if (!$this->permissionResolver->canUser('content', 'versionremove', $versionInfo)) { - throw new UnauthorizedException( - 'content', - 'versionremove', - ['contentId' => $versionInfo->contentInfo->id, 'versionNo' => $versionInfo->versionNo] - ); - } - - $versionList = $contentHandler->listVersions( - $versionInfo->contentInfo->id, - null, - 2 - ); - $versionsCount = count($versionList); - - if ($versionsCount === 1 && !$versionInfo->isDraft()) { - throw new BadStateException( - '$versionInfo', - 'The Version is the last version of the Content item and cannot be removed' - ); - } - - $this->repository->beginTransaction(); - try { - if ($versionsCount === 1) { - $contentHandler->deleteContent($versionInfo->contentInfo->id); - } else { - $contentHandler->deleteVersion( - $versionInfo->getContentInfo()->id, - $versionInfo->versionNo - ); - } - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Loads all versions for the given content. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to list versions - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the given status is invalid - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param int|null $status - * - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo[] Sorted by creation date - */ - public function loadVersions(ContentInfo $contentInfo, ?int $status = null): iterable - { - if (!$this->permissionResolver->canUser('content', 'versionread', $contentInfo)) { - throw new UnauthorizedException('content', 'versionread', ['contentId' => $contentInfo->id]); - } - - if ($status !== null && !in_array((int)$status, [VersionInfo::STATUS_DRAFT, VersionInfo::STATUS_PUBLISHED, VersionInfo::STATUS_ARCHIVED], true)) { - throw new InvalidArgumentException( - 'status', - sprintf( - 'available statuses are: %d (draft), %d (published), %d (archived), %d given', - VersionInfo::STATUS_DRAFT, - VersionInfo::STATUS_PUBLISHED, - VersionInfo::STATUS_ARCHIVED, - $status - ) - ); - } - - $spiVersionInfoList = $this->persistenceHandler->contentHandler()->listVersions($contentInfo->id, $status); - - $versions = []; - foreach ($spiVersionInfoList as $spiVersionInfo) { - $versionInfo = $this->contentDomainMapper->buildVersionInfoDomainObject($spiVersionInfo); - if (!$this->permissionResolver->canUser('content', 'versionread', $versionInfo)) { - throw new UnauthorizedException('content', 'versionread', ['versionId' => $versionInfo->id]); - } - - $versions[] = $versionInfo; - } - - return $versions; - } - - /** - * Copies the content to a new location. If no version is given, - * all versions are copied, otherwise only the given version. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to copy the content to the given location - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct $destinationLocationCreateStruct the target location where the content is copied to - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function copyContent(ContentInfo $contentInfo, LocationCreateStruct $destinationLocationCreateStruct, ?APIVersionInfo $versionInfo = null): APIContent - { - $destinationLocation = $this->repository->getLocationService()->loadLocation( - $destinationLocationCreateStruct->parentLocationId - ); - - $locationTarget = (new DestinationLocationTarget($destinationLocation->id, $contentInfo)); - if (!$this->permissionResolver->canUser( - 'content', - 'create', - $contentInfo, - [$destinationLocation, $locationTarget], - )) { - throw new UnauthorizedException( - 'content', - 'create', - [ - 'parentLocationId' => $destinationLocationCreateStruct->parentLocationId, - 'sectionId' => $contentInfo->sectionId, - ] - ); - } - if (!$this->permissionResolver->canUser('content', 'manage_locations', $contentInfo, [$destinationLocation])) { - throw new UnauthorizedException('content', 'manage_locations', ['contentId' => $contentInfo->id]); - } - - $defaultObjectStates = $this->getDefaultObjectStates(); - - $this->repository->beginTransaction(); - try { - $spiContent = $this->persistenceHandler->contentHandler()->copy( - $contentInfo->id, - $versionInfo ? $versionInfo->versionNo : null, - $this->permissionResolver->getCurrentUserReference()->getUserId() - ); - - $objectStateHandler = $this->persistenceHandler->objectStateHandler(); - foreach ($defaultObjectStates as $objectStateGroupId => $objectState) { - $objectStateHandler->setContentState( - $spiContent->versionInfo->contentInfo->id, - $objectStateGroupId, - $objectState->id - ); - } - - $content = $this->internalPublishVersion( - $this->contentDomainMapper->buildVersionInfoDomainObject($spiContent->versionInfo), - $spiContent->versionInfo->creationDate - ); - - $this->repository->getLocationService()->createLocation( - $content->getVersionInfo()->getContentInfo(), - $destinationLocationCreateStruct - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->internalLoadContentById($content->id); - } - - /** - * Loads all outgoing relations for the given version. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - * - * @return \eZ\Publish\API\Repository\Values\Content\Relation[] - */ - public function loadRelations(APIVersionInfo $versionInfo): iterable - { - if ($versionInfo->isPublished()) { - $function = 'read'; - } else { - $function = 'versionread'; - } - - if (!$this->permissionResolver->canUser('content', $function, $versionInfo)) { - throw new UnauthorizedException('content', $function); - } - - return $this->internalLoadRelations($versionInfo); - } - - /** - * Loads all outgoing relations for the given version without checking the permissions. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * - * @return \eZ\Publish\API\Repository\Values\Content\Relation[] - */ - protected function internalLoadRelations(APIVersionInfo $versionInfo): array - { - $contentInfo = $versionInfo->getContentInfo(); - $spiRelations = $this->persistenceHandler->contentHandler()->loadRelations( - $contentInfo->id, - $versionInfo->versionNo - ); - - /** @var $relations \eZ\Publish\API\Repository\Values\Content\Relation[] */ - $relations = []; - foreach ($spiRelations as $spiRelation) { - $destinationContentInfo = $this->internalLoadContentInfoById($spiRelation->destinationContentId); - if (!$this->permissionResolver->canUser('content', 'read', $destinationContentInfo)) { - continue; - } - - $relations[] = $this->contentDomainMapper->buildRelationDomainObject( - $spiRelation, - $contentInfo, - $destinationContentInfo - ); - } - - return $relations; - } - - /** - * {@inheritdoc} - */ - public function countReverseRelations(ContentInfo $contentInfo): int - { - if (!$this->permissionResolver->canUser('content', 'reverserelatedlist', $contentInfo)) { - return 0; - } - - return $this->persistenceHandler->contentHandler()->countReverseRelations( - $contentInfo->id - ); - } - - /** - * Loads all incoming relations for a content object. - * - * The relations come only from published versions of the source content objects - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * - * @return \eZ\Publish\API\Repository\Values\Content\Relation[] - */ - public function loadReverseRelations(ContentInfo $contentInfo): iterable - { - if (!$this->permissionResolver->canUser('content', 'reverserelatedlist', $contentInfo)) { - throw new UnauthorizedException('content', 'reverserelatedlist', ['contentId' => $contentInfo->id]); - } - - $spiRelations = $this->persistenceHandler->contentHandler()->loadReverseRelations( - $contentInfo->id - ); - - $returnArray = []; - foreach ($spiRelations as $spiRelation) { - $sourceContentInfo = $this->internalLoadContentInfoById($spiRelation->sourceContentId); - if (!$this->permissionResolver->canUser('content', 'read', $sourceContentInfo)) { - continue; - } - - $returnArray[] = $this->contentDomainMapper->buildRelationDomainObject( - $spiRelation, - $sourceContentInfo, - $contentInfo - ); - } - - return $returnArray; - } - - /** - * {@inheritdoc} - */ - public function loadReverseRelationList(ContentInfo $contentInfo, int $offset = 0, int $limit = -1): RelationList - { - $list = new RelationList(); - if (!$this->repository->getPermissionResolver()->canUser('content', 'reverserelatedlist', $contentInfo)) { - return $list; - } - - $list->totalCount = $this->persistenceHandler->contentHandler()->countReverseRelations( - $contentInfo->id - ); - if ($list->totalCount > 0) { - $spiRelationList = $this->persistenceHandler->contentHandler()->loadReverseRelationList( - $contentInfo->id, - $offset, - $limit - ); - foreach ($spiRelationList as $spiRelation) { - $sourceContentInfo = $this->internalLoadContentInfoById($spiRelation->sourceContentId); - if ($this->repository->getPermissionResolver()->canUser('content', 'read', $sourceContentInfo)) { - $relation = $this->contentDomainMapper->buildRelationDomainObject( - $spiRelation, - $sourceContentInfo, - $contentInfo - ); - $list->items[] = new RelationListItem($relation); - } else { - $list->items[] = new UnauthorizedRelationListItem( - 'content', - 'read', - ['contentId' => $sourceContentInfo->id] - ); - } - } - } - - return $list; - } - - /** - * Adds a relation of type common. - * - * The source of the relation is the content and version - * referenced by $versionInfo. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit this version - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $sourceVersion - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContent the destination of the relation - * - * @return \eZ\Publish\API\Repository\Values\Content\Relation the newly created relation - */ - public function addRelation(APIVersionInfo $sourceVersion, ContentInfo $destinationContent): APIRelation - { - $sourceVersion = $this->loadVersionInfoById( - $sourceVersion->contentInfo->id, - $sourceVersion->versionNo - ); - - if (!$sourceVersion->isDraft()) { - throw new BadStateException( - '$sourceVersion', - 'Relations of type common can only be added to draft versions' - ); - } - - if (!$this->permissionResolver->canUser('content', 'edit', $sourceVersion)) { - throw new UnauthorizedException('content', 'edit', ['contentId' => $sourceVersion->contentInfo->id]); - } - - $sourceContentInfo = $sourceVersion->getContentInfo(); - - $this->repository->beginTransaction(); - try { - $spiRelation = $this->persistenceHandler->contentHandler()->addRelation( - new SPIRelationCreateStruct( - [ - 'sourceContentId' => $sourceContentInfo->id, - 'sourceContentVersionNo' => $sourceVersion->versionNo, - 'sourceFieldDefinitionId' => null, - 'destinationContentId' => $destinationContent->id, - 'type' => APIRelation::COMMON, - ] - ) - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->contentDomainMapper->buildRelationDomainObject($spiRelation, $sourceContentInfo, $destinationContent); - } - - /** - * Removes a relation of type COMMON from a draft. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed edit this version - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the version is not a draft - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is no relation of type COMMON for the given destination - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $sourceVersion - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContent - */ - public function deleteRelation(APIVersionInfo $sourceVersion, ContentInfo $destinationContent): void - { - $sourceVersion = $this->loadVersionInfoById( - $sourceVersion->contentInfo->id, - $sourceVersion->versionNo - ); - - if (!$sourceVersion->isDraft()) { - throw new BadStateException( - '$sourceVersion', - 'Relations of type common can only be added to draft versions' - ); - } - - if (!$this->permissionResolver->canUser('content', 'edit', $sourceVersion)) { - throw new UnauthorizedException('content', 'edit', ['contentId' => $sourceVersion->contentInfo->id]); - } - - $spiRelations = $this->persistenceHandler->contentHandler()->loadRelations( - $sourceVersion->getContentInfo()->id, - $sourceVersion->versionNo, - APIRelation::COMMON - ); - - if (empty($spiRelations)) { - throw new InvalidArgumentException( - '$sourceVersion', - 'There are no Relations of type COMMON for the given destination' - ); - } - - // there should be only one relation of type COMMON for each destination, - // but in case there were ever more then one, we will remove them all - // @todo: alternatively, throw BadStateException? - $this->repository->beginTransaction(); - try { - foreach ($spiRelations as $spiRelation) { - if ($spiRelation->destinationContentId == $destinationContent->id) { - $this->persistenceHandler->contentHandler()->removeRelation( - $spiRelation->id, - APIRelation::COMMON - ); - } - } - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Delete Content item Translation from all Versions (including archived ones) of a Content Object. - * - * NOTE: this operation is risky and permanent, so user interface should provide a warning before performing it. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the specified Translation - * is the Main Translation of a Content Item. - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed - * to delete the content (in one of the locations of the given Content Item). - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if languageCode argument - * is invalid for the given content. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param string $languageCode - * - * @since 6.13 - */ - public function deleteTranslation(ContentInfo $contentInfo, string $languageCode): void - { - if ($contentInfo->mainLanguageCode === $languageCode) { - throw new BadStateException( - '$languageCode', - 'The provided translation is the main translation of the Content item' - ); - } - - $translationWasFound = false; - $this->repository->beginTransaction(); - try { - $target = (new Target\Builder\VersionBuilder())->translateToAnyLanguageOf([$languageCode])->build(); - - foreach ($this->loadVersions($contentInfo) as $versionInfo) { - if (!$this->permissionResolver->canUser('content', 'remove', $versionInfo, [$target])) { - throw new UnauthorizedException( - 'content', - 'remove', - ['contentId' => $contentInfo->id, 'versionNo' => $versionInfo->versionNo, 'languageCode' => $languageCode] - ); - } - - if (!in_array($languageCode, $versionInfo->languageCodes)) { - continue; - } - - $translationWasFound = true; - - // If the translation is the version's only one, delete the version - if (count($versionInfo->languageCodes) < 2) { - $this->persistenceHandler->contentHandler()->deleteVersion( - $versionInfo->getContentInfo()->id, - $versionInfo->versionNo - ); - } - } - - if (!$translationWasFound) { - throw new InvalidArgumentException( - '$languageCode', - sprintf( - '%s does not exist in the Content item(id=%d)', - $languageCode, - $contentInfo->id - ) - ); - } - - $this->persistenceHandler->contentHandler()->deleteTranslationFromContent( - $contentInfo->id, - $languageCode - ); - $locationIds = array_map( - static function (Location $location) { - return $location->id; - }, - $this->repository->getLocationService()->loadLocations($contentInfo) - ); - $this->persistenceHandler->urlAliasHandler()->translationRemoved( - $locationIds, - $languageCode - ); - $this->repository->commit(); - } catch (InvalidArgumentException $e) { - $this->repository->rollback(); - throw $e; - } catch (BadStateException $e) { - $this->repository->rollback(); - throw $e; - } catch (UnauthorizedException $e) { - $this->repository->rollback(); - throw $e; - } catch (Exception $e) { - $this->repository->rollback(); - // cover generic unexpected exception to fulfill API promise on @throws - throw new BadStateException('$contentInfo', 'Translation removal failed', $e); - } - } - - /** - * Delete specified Translation from a Content Draft. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the specified Translation - * is the only one the Content Draft has or it is the main Translation of a Content Object. - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed - * to edit the Content (in one of the locations of the given Content Object). - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if languageCode argument - * is invalid for the given Draft. - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if specified Version was not found - * - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo Content Version Draft - * @param string $languageCode Language code of the Translation to be removed - * - * @return \eZ\Publish\API\Repository\Values\Content\Content Content Draft w/o the specified Translation - * - * @since 6.12 - */ - public function deleteTranslationFromDraft(APIVersionInfo $versionInfo, string $languageCode): APIContent - { - if (!$versionInfo->isDraft()) { - throw new BadStateException( - '$versionInfo', - 'The version is not a draft, so translations cannot be modified. Create a draft before proceeding' - ); - } - - if ($versionInfo->contentInfo->mainLanguageCode === $languageCode) { - throw new BadStateException( - '$languageCode', - 'the specified translation is the main translation of the Content item. Change it before proceeding.' - ); - } - - if (!$this->permissionResolver->canUser('content', 'edit', $versionInfo->contentInfo)) { - throw new UnauthorizedException( - 'content', - 'edit', - ['contentId' => $versionInfo->contentInfo->id] - ); - } - - if (!in_array($languageCode, $versionInfo->languageCodes)) { - throw new InvalidArgumentException( - '$languageCode', - sprintf( - 'The version (ContentId=%d, VersionNo=%d) is not translated into %s', - $versionInfo->contentInfo->id, - $versionInfo->versionNo, - $languageCode - ) - ); - } - - if (count($versionInfo->languageCodes) === 1) { - throw new BadStateException( - '$languageCode', - 'The provided translation is the only translation in this version' - ); - } - - $this->repository->beginTransaction(); - try { - $spiContent = $this->persistenceHandler->contentHandler()->deleteTranslationFromDraft( - $versionInfo->contentInfo->id, - $versionInfo->versionNo, - $languageCode - ); - $this->repository->commit(); - - return $this->contentDomainMapper->buildContentDomainObject( - $spiContent, - $this->repository->getContentTypeService()->loadContentType( - $spiContent->versionInfo->contentInfo->contentTypeId - ) - ); - } catch (APINotFoundException $e) { - // avoid wrapping expected NotFoundException in BadStateException handled below - $this->repository->rollback(); - throw $e; - } catch (Exception $e) { - $this->repository->rollback(); - // cover generic unexpected exception to fulfill API promise on @throws - throw new BadStateException('$contentInfo', 'Could not remove the translation', $e); - } - } - - /** - * Hides Content by making all the Locations appear hidden. - * It does not persist hidden state on Location object itself. - * - * Content hidden by this API can be revealed by revealContent API. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function hideContent(ContentInfo $contentInfo): void - { - $locationTarget = (new DestinationLocationTarget($contentInfo->mainLocationId, $contentInfo)); - if (!$this->permissionResolver->canUser( - 'content', - 'hide', - $contentInfo, - [$locationTarget] - )) { - throw new UnauthorizedException('content', 'hide', ['contentId' => $contentInfo->id]); - } - - $this->repository->beginTransaction(); - try { - $this->persistenceHandler->contentHandler()->updateMetadata( - $contentInfo->id, - new SPIMetadataUpdateStruct([ - 'isHidden' => true, - ]) - ); - $locationHandler = $this->persistenceHandler->locationHandler(); - $childLocations = $locationHandler->loadLocationsByContent($contentInfo->id); - foreach ($childLocations as $childLocation) { - $locationHandler->setInvisible($childLocation->id); - } - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Reveals Content hidden by hideContent API. - * Locations which were hidden before hiding Content will remain hidden. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function revealContent(ContentInfo $contentInfo): void - { - $locationTarget = (new DestinationLocationTarget($contentInfo->mainLocationId, $contentInfo)); - if (!$this->permissionResolver->canUser( - 'content', - 'hide', - $contentInfo, - [$locationTarget] - )) { - throw new UnauthorizedException('content', 'hide', ['contentId' => $contentInfo->id]); - } - - $this->repository->beginTransaction(); - try { - $this->persistenceHandler->contentHandler()->updateMetadata( - $contentInfo->id, - new SPIMetadataUpdateStruct([ - 'isHidden' => false, - ]) - ); - $locationHandler = $this->persistenceHandler->locationHandler(); - $childLocations = $locationHandler->loadLocationsByContent($contentInfo->id); - foreach ($childLocations as $childLocation) { - $locationHandler->setVisible($childLocation->id); - } - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Instantiates a new content create struct object. - * - * alwaysAvailable is set to the ContentType's defaultAlwaysAvailable - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * @param string $mainLanguageCode - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct - */ - public function newContentCreateStruct(ContentType $contentType, string $mainLanguageCode): APIContentCreateStruct - { - return new ContentCreateStruct( - [ - 'contentType' => $contentType, - 'mainLanguageCode' => $mainLanguageCode, - 'alwaysAvailable' => $contentType->defaultAlwaysAvailable, - ] - ); - } - - /** - * Instantiates a new content meta data update struct. - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct - */ - public function newContentMetadataUpdateStruct(): ContentMetadataUpdateStruct - { - return new ContentMetadataUpdateStruct(); - } - - /** - * Instantiates a new content update struct. - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct - */ - public function newContentUpdateStruct(): APIContentUpdateStruct - { - return new ContentUpdateStruct(); - } - - /** - * @param \eZ\Publish\API\Repository\Values\User\User|null $user - * - * @return \eZ\Publish\API\Repository\Values\User\UserReference - */ - private function resolveUser(?User $user): UserReference - { - if ($user === null) { - $user = $this->permissionResolver->getCurrentUserReference(); - } - - return $user; - } - - public function validate( - ValueObject $object, - array $context = [], - ?array $fieldIdentifiersToValidate = null - ): array { - return $this->contentValidator->validate( - $object, - $context, - $fieldIdentifiersToValidate - ); - } - - public function find(Filter $filter, ?array $languages = null): ContentList - { - $filter = clone $filter; - if (!empty($languages)) { - $filter->andWithCriterion(new LanguageCode($languages)); - } - - $permissionCriterion = $this->permissionResolver->getQueryPermissionsCriterion(); - if ($permissionCriterion instanceof Criterion\MatchNone) { - return new ContentList(0, []); - } - - if (!$permissionCriterion instanceof Criterion\MatchAll) { - if (!$permissionCriterion instanceof FilteringCriterion) { - return new ContentList(0, []); - } - $filter->andWithCriterion($permissionCriterion); - } - - $contentItems = []; - $contentItemsIterator = $this->contentFilteringHandler->find($filter); - foreach ($contentItemsIterator as $contentItem) { - $contentItems[] = $this->contentDomainMapper->buildContentDomainObjectFromPersistence( - $contentItem->content, - $contentItem->type, - $languages, - ); - } - - return new ContentList($contentItemsIterator->getTotalCount(), $contentItems); - } - - public function count(Filter $filter, ?array $languages = null): int - { - $filter = clone $filter; - if (!empty($languages)) { - $filter->andWithCriterion(new LanguageCode($languages)); - } - - $permissionCriterion = $this->permissionResolver->getQueryPermissionsCriterion(); - if ($permissionCriterion instanceof Criterion\MatchNone) { - return 0; - } - - if (!$permissionCriterion instanceof Criterion\MatchAll) { - if (!$permissionCriterion instanceof FilteringCriterion) { - return 0; - } - - $filter->andWithCriterion($permissionCriterion); - } - - return $this->contentFilteringHandler->count($filter); - } -} diff --git a/eZ/Publish/Core/Repository/ContentTypeService.php b/eZ/Publish/Core/Repository/ContentTypeService.php deleted file mode 100644 index 2140a5727c..0000000000 --- a/eZ/Publish/Core/Repository/ContentTypeService.php +++ /dev/null @@ -1,1649 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use DateTime; -use Exception; -use eZ\Publish\API\Repository\ContentTypeService as ContentTypeServiceInterface; -use eZ\Publish\API\Repository\Exceptions\BadStateException as APIBadStateException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\ContentType as APIContentType; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeCreateStruct as APIContentTypeCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft as APIContentTypeDraft; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup as APIContentTypeGroup; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\ContentTypeFieldDefinitionValidationException; -use eZ\Publish\Core\Base\Exceptions\ContentTypeValidationException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\Repository\Values\ContentType\ContentTypeCreateStruct; -use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType; -use eZ\Publish\SPI\Persistence\Content\Type as SPIContentType; -use eZ\Publish\SPI\Persistence\Content\Type\CreateStruct as SPIContentTypeCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct as SPIContentTypeGroupCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct as SPIContentTypeGroupUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Handler; -use eZ\Publish\SPI\Persistence\User\Handler as UserHandler; - -class ContentTypeService implements ContentTypeServiceInterface -{ - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\SPI\Persistence\Content\Type\Handler */ - protected $contentTypeHandler; - - /** @var \eZ\Publish\SPI\Persistence\User\Handler */ - protected $userHandler; - - /** @var array */ - protected $settings; - - /** @var \eZ\Publish\Core\Repository\Mapper\ContentDomainMapper */ - protected $contentDomainMapper; - - /** @var \eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper */ - protected $contentTypeDomainMapper; - - /** @var \eZ\Publish\Core\FieldType\FieldTypeRegistry */ - protected $fieldTypeRegistry; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionResolver; - - /** - * Setups service with reference to repository object that created it & corresponding handler. - * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\SPI\Persistence\Content\Type\Handler $contentTypeHandler - * @param \eZ\Publish\SPI\Persistence\User\Handler $userHandler - * @param \eZ\Publish\Core\Repository\Mapper\ContentDomainMapper $contentDomainMapper - * @param \eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper $contentTypeDomainMapper - * @param \eZ\Publish\Core\FieldType\FieldTypeRegistry $fieldTypeRegistry - * @param \eZ\Publish\API\Repository\PermissionResolver $permissionResolver - * @param array $settings - */ - public function __construct( - RepositoryInterface $repository, - Handler $contentTypeHandler, - UserHandler $userHandler, - Mapper\ContentDomainMapper $contentDomainMapper, - Mapper\ContentTypeDomainMapper $contentTypeDomainMapper, - FieldTypeRegistry $fieldTypeRegistry, - PermissionResolver $permissionResolver, - array $settings = [] - ) { - $this->repository = $repository; - $this->contentTypeHandler = $contentTypeHandler; - $this->userHandler = $userHandler; - $this->contentDomainMapper = $contentDomainMapper; - $this->contentTypeDomainMapper = $contentTypeDomainMapper; - $this->fieldTypeRegistry = $fieldTypeRegistry; - // Union makes sure default settings are ignored if provided in argument - $this->settings = $settings + [ - //'defaultSetting' => array(), - ]; - $this->permissionResolver = $permissionResolver; - } - - /** - * Create a Content Type Group object. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create a content type group - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If a group with the same identifier already exists - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupCreateStruct $contentTypeGroupCreateStruct - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup - */ - public function createContentTypeGroup(ContentTypeGroupCreateStruct $contentTypeGroupCreateStruct): APIContentTypeGroup - { - if (!$this->permissionResolver->canUser('class', 'create', $contentTypeGroupCreateStruct)) { - throw new UnauthorizedException('ContentType', 'create'); - } - - try { - $this->loadContentTypeGroupByIdentifier($contentTypeGroupCreateStruct->identifier); - - throw new InvalidArgumentException( - '$contentTypeGroupCreateStruct', - "A group with the identifier '{$contentTypeGroupCreateStruct->identifier}' already exists" - ); - } catch (APINotFoundException $e) { - // Do nothing - } - - if ($contentTypeGroupCreateStruct->creationDate === null) { - $timestamp = time(); - } else { - $timestamp = $contentTypeGroupCreateStruct->creationDate->getTimestamp(); - } - - if ($contentTypeGroupCreateStruct->creatorId === null) { - $userId = $this->permissionResolver->getCurrentUserReference()->getUserId(); - } else { - $userId = $contentTypeGroupCreateStruct->creatorId; - } - - $spiGroupCreateStruct = new SPIContentTypeGroupCreateStruct( - [ - 'identifier' => $contentTypeGroupCreateStruct->identifier, - 'created' => $timestamp, - 'modified' => $timestamp, - 'creatorId' => $userId, - 'modifierId' => $userId, - ] - ); - - $this->repository->beginTransaction(); - try { - $spiContentTypeGroup = $this->contentTypeHandler->createGroup( - $spiGroupCreateStruct - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->contentTypeDomainMapper->buildContentTypeGroupDomainObject($spiContentTypeGroup); - } - - /** - * {@inheritdoc} - */ - public function loadContentTypeGroup(int $contentTypeGroupId, array $prioritizedLanguages = []): APIContentTypeGroup - { - $spiGroup = $this->contentTypeHandler->loadGroup( - $contentTypeGroupId - ); - - return $this->contentTypeDomainMapper->buildContentTypeGroupDomainObject($spiGroup, $prioritizedLanguages); - } - - /** - * {@inheritdoc} - */ - public function loadContentTypeGroupByIdentifier(string $contentTypeGroupIdentifier, array $prioritizedLanguages = []): APIContentTypeGroup - { - $groups = $this->loadContentTypeGroups($prioritizedLanguages); - - foreach ($groups as $group) { - if ($group->identifier === $contentTypeGroupIdentifier) { - return $group; - } - } - - throw new NotFoundException('ContentTypeGroup', $contentTypeGroupIdentifier); - } - - /** - * {@inheritdoc} - */ - public function loadContentTypeGroups(array $prioritizedLanguages = []): iterable - { - $spiGroups = $this->contentTypeHandler->loadAllGroups(); - - $groups = []; - foreach ($spiGroups as $spiGroup) { - $groups[] = $this->contentTypeDomainMapper->buildContentTypeGroupDomainObject($spiGroup, $prioritizedLanguages); - } - - return $groups; - } - - /** - * Update a Content Type Group object. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create a content type group - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the given identifier (if set) already exists - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup $contentTypeGroup the content type group to be updated - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupUpdateStruct $contentTypeGroupUpdateStruct - */ - public function updateContentTypeGroup(APIContentTypeGroup $contentTypeGroup, ContentTypeGroupUpdateStruct $contentTypeGroupUpdateStruct): void - { - if (!$this->permissionResolver->canUser('class', 'update', $contentTypeGroup)) { - throw new UnauthorizedException('ContentType', 'update'); - } - - $loadedContentTypeGroup = $this->loadContentTypeGroup($contentTypeGroup->id); - - if ($contentTypeGroupUpdateStruct->identifier !== null - && $contentTypeGroupUpdateStruct->identifier !== $loadedContentTypeGroup->identifier) { - try { - $this->loadContentTypeGroupByIdentifier($contentTypeGroupUpdateStruct->identifier); - - throw new InvalidArgumentException( - '$contentTypeGroupUpdateStruct->identifier', - 'given identifier already exists' - ); - } catch (APINotFoundException $e) { - // Do nothing - } - } - - if ($contentTypeGroupUpdateStruct->modificationDate !== null) { - $modifiedTimestamp = $contentTypeGroupUpdateStruct->modificationDate->getTimestamp(); - } else { - $modifiedTimestamp = time(); - } - - $spiGroupUpdateStruct = new SPIContentTypeGroupUpdateStruct( - [ - 'id' => $loadedContentTypeGroup->id, - 'identifier' => $contentTypeGroupUpdateStruct->identifier === null ? - $loadedContentTypeGroup->identifier : - $contentTypeGroupUpdateStruct->identifier, - 'modified' => $modifiedTimestamp, - 'modifierId' => $contentTypeGroupUpdateStruct->modifierId === null ? - $this->permissionResolver->getCurrentUserReference()->getUserId() : - $contentTypeGroupUpdateStruct->modifierId, - ] - ); - - $this->repository->beginTransaction(); - try { - $this->contentTypeHandler->updateGroup( - $spiGroupUpdateStruct - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Delete a Content Type Group. - * - * This method only deletes an content type group which has content types without any content instances - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete a content type group - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If a to be deleted content type has instances - */ - public function deleteContentTypeGroup(APIContentTypeGroup $contentTypeGroup): void - { - if (!$this->permissionResolver->canUser('class', 'delete', $contentTypeGroup)) { - throw new UnauthorizedException('ContentType', 'delete'); - } - - $loadedContentTypeGroup = $this->loadContentTypeGroup($contentTypeGroup->id); - - $this->repository->beginTransaction(); - try { - $this->contentTypeHandler->deleteGroup( - $loadedContentTypeGroup->id - ); - $this->repository->commit(); - } catch (APIBadStateException $e) { - $this->repository->rollback(); - throw new InvalidArgumentException( - '$contentTypeGroup', - 'Content Type group contains Content Types', - $e - ); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Validates input ContentType create struct. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeCreateStruct $contentTypeCreateStruct - */ - protected function validateInputContentTypeCreateStruct(APIContentTypeCreateStruct $contentTypeCreateStruct): void - { - // Required properties - - if ($contentTypeCreateStruct->identifier === null) { - throw new InvalidArgumentException('$contentTypeCreateStruct', "Property 'identifier' is required"); - } - - if (!is_string($contentTypeCreateStruct->identifier)) { - throw new InvalidArgumentType( - '$contentTypeCreateStruct->identifier', - 'string', - $contentTypeCreateStruct->identifier - ); - } - - if ($contentTypeCreateStruct->identifier === '') { - throw new InvalidArgumentValue( - '$contentTypeCreateStruct->identifier', - $contentTypeCreateStruct->identifier - ); - } - - if ($contentTypeCreateStruct->mainLanguageCode === null) { - throw new InvalidArgumentException('$contentTypeCreateStruct', "Property 'mainLanguageCode' is required"); - } - - if (!is_string($contentTypeCreateStruct->mainLanguageCode)) { - throw new InvalidArgumentType( - '$contentTypeCreateStruct->mainLanguageCode', - 'string', - $contentTypeCreateStruct->mainLanguageCode - ); - } - - if ($contentTypeCreateStruct->mainLanguageCode === '') { - throw new InvalidArgumentValue( - '$contentTypeCreateStruct->mainLanguageCode', - $contentTypeCreateStruct->mainLanguageCode - ); - } - - if ($contentTypeCreateStruct->names !== null) { - $this->contentDomainMapper->validateTranslatedList( - $contentTypeCreateStruct->names, - '$contentTypeCreateStruct->names' - ); - } - - if (!isset($contentTypeCreateStruct->names[$contentTypeCreateStruct->mainLanguageCode]) || - $contentTypeCreateStruct->names[$contentTypeCreateStruct->mainLanguageCode] === '' - ) { - throw new InvalidArgumentException( - '$contentTypeCreateStruct->names', - 'At least one name in the main language is required' - ); - } - - // Optional properties - - if ($contentTypeCreateStruct->descriptions !== null) { - $this->contentDomainMapper->validateTranslatedList( - $contentTypeCreateStruct->descriptions, - '$contentTypeCreateStruct->descriptions' - ); - } - - if ($contentTypeCreateStruct->defaultSortField !== null && !$this->contentDomainMapper->isValidLocationSortField($contentTypeCreateStruct->defaultSortField)) { - throw new InvalidArgumentValue( - '$contentTypeCreateStruct->defaultSortField', - $contentTypeCreateStruct->defaultSortField - ); - } - - if ($contentTypeCreateStruct->defaultSortOrder !== null && !$this->contentDomainMapper->isValidLocationSortOrder($contentTypeCreateStruct->defaultSortOrder)) { - throw new InvalidArgumentValue( - '$contentTypeCreateStruct->defaultSortOrder', - $contentTypeCreateStruct->defaultSortOrder - ); - } - - if ($contentTypeCreateStruct->creatorId !== null) { - $this->repository->getUserService()->loadUser($contentTypeCreateStruct->creatorId); - } - - if ($contentTypeCreateStruct->creationDate !== null && !$contentTypeCreateStruct->creationDate instanceof DateTime) { - throw new InvalidArgumentType( - '$contentTypeCreateStruct->creationDate', - 'DateTime', - $contentTypeCreateStruct->creationDate - ); - } - - if ($contentTypeCreateStruct->defaultAlwaysAvailable !== null && !is_bool($contentTypeCreateStruct->defaultAlwaysAvailable)) { - throw new InvalidArgumentType( - '$contentTypeCreateStruct->defaultAlwaysAvailable', - 'boolean', - $contentTypeCreateStruct->defaultAlwaysAvailable - ); - } - - if ($contentTypeCreateStruct->isContainer !== null && !is_bool($contentTypeCreateStruct->isContainer)) { - throw new InvalidArgumentType( - '$contentTypeCreateStruct->isContainer', - 'boolean', - $contentTypeCreateStruct->isContainer - ); - } - - if ($contentTypeCreateStruct->remoteId !== null && !is_string($contentTypeCreateStruct->remoteId)) { - throw new InvalidArgumentType( - '$contentTypeCreateStruct->remoteId', - 'string', - $contentTypeCreateStruct->remoteId - ); - } - - if ($contentTypeCreateStruct->nameSchema !== null && !is_string($contentTypeCreateStruct->nameSchema)) { - throw new InvalidArgumentType( - '$contentTypeCreateStruct->nameSchema', - 'string', - $contentTypeCreateStruct->nameSchema - ); - } - - if ($contentTypeCreateStruct->urlAliasSchema !== null && !is_string($contentTypeCreateStruct->urlAliasSchema)) { - throw new InvalidArgumentType( - '$contentTypeCreateStruct->urlAliasSchema', - 'string', - $contentTypeCreateStruct->urlAliasSchema - ); - } - - foreach ($contentTypeCreateStruct->fieldDefinitions as $key => $fieldDefinitionCreateStruct) { - if (!$fieldDefinitionCreateStruct instanceof FieldDefinitionCreateStruct) { - throw new InvalidArgumentType( - "\$contentTypeCreateStruct->fieldDefinitions[$key]", - 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\FieldDefinitionCreateStruct', - $fieldDefinitionCreateStruct - ); - } - - $this->validateInputFieldDefinitionCreateStruct( - $fieldDefinitionCreateStruct, - "\$contentTypeCreateStruct->fieldDefinitions[$key]" - ); - } - } - - /** - * Validates input ContentTypeGroup array. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup[] $contentTypeGroups - */ - protected function validateInputContentTypeGroups(array $contentTypeGroups): void - { - if (empty($contentTypeGroups)) { - throw new InvalidArgumentException( - '$contentTypeGroups', - 'The argument must contain at least one Content Type group' - ); - } - - foreach ($contentTypeGroups as $key => $contentTypeGroup) { - if (!$contentTypeGroup instanceof APIContentTypeGroup) { - throw new InvalidArgumentType( - "\$contentTypeGroups[{$key}]", - 'eZ\\Publish\\API\\Repository\\Values\\ContentType\\ContentTypeGroup', - $contentTypeGroup - ); - } - } - } - - /** - * Validates input FieldDefinitionCreateStruct. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct $fieldDefinitionCreateStruct - * @param string $argumentName - */ - protected function validateInputFieldDefinitionCreateStruct( - FieldDefinitionCreateStruct $fieldDefinitionCreateStruct, - string $argumentName = '$fieldDefinitionCreateStruct' - ): void { - // Required properties - - if ($fieldDefinitionCreateStruct->fieldTypeIdentifier === null) { - throw new InvalidArgumentException($argumentName, "Property 'fieldTypeIdentifier' is required"); - } - - if (!is_string($fieldDefinitionCreateStruct->fieldTypeIdentifier)) { - throw new InvalidArgumentType( - $argumentName . '->fieldTypeIdentifier', - 'string', - $fieldDefinitionCreateStruct->fieldTypeIdentifier - ); - } - - if ($fieldDefinitionCreateStruct->fieldTypeIdentifier === '') { - throw new InvalidArgumentValue( - $argumentName . '->fieldTypeIdentifier', - $fieldDefinitionCreateStruct->fieldTypeIdentifier - ); - } - - if ($fieldDefinitionCreateStruct->identifier === null) { - throw new InvalidArgumentException($argumentName, "Property 'identifier' is required"); - } - - if (!is_string($fieldDefinitionCreateStruct->identifier)) { - throw new InvalidArgumentType( - $argumentName . '->identifier', - 'string', - $fieldDefinitionCreateStruct->identifier - ); - } - - if ($fieldDefinitionCreateStruct->identifier === '') { - throw new InvalidArgumentValue( - $argumentName . '->identifier', - $fieldDefinitionCreateStruct->identifier - ); - } - - // Optional properties - - if ($fieldDefinitionCreateStruct->names !== null) { - $this->contentDomainMapper->validateTranslatedList( - $fieldDefinitionCreateStruct->names, - $argumentName . '->names' - ); - } - - if ($fieldDefinitionCreateStruct->descriptions !== null) { - $this->contentDomainMapper->validateTranslatedList( - $fieldDefinitionCreateStruct->descriptions, - $argumentName . '->descriptions' - ); - } - - if ($fieldDefinitionCreateStruct->fieldGroup !== null && !is_string($fieldDefinitionCreateStruct->fieldGroup)) { - throw new InvalidArgumentType( - $argumentName . '->fieldGroup', - 'string', - $fieldDefinitionCreateStruct->fieldGroup - ); - } - - if ($fieldDefinitionCreateStruct->position !== null && !is_int($fieldDefinitionCreateStruct->position)) { - throw new InvalidArgumentType( - $argumentName . '->position', - 'integer', - $fieldDefinitionCreateStruct->position - ); - } - - if ($fieldDefinitionCreateStruct->isTranslatable !== null && !is_bool($fieldDefinitionCreateStruct->isTranslatable)) { - throw new InvalidArgumentType( - $argumentName . '->isTranslatable', - 'boolean', - $fieldDefinitionCreateStruct->isTranslatable - ); - } - - if ($fieldDefinitionCreateStruct->isRequired !== null && !is_bool($fieldDefinitionCreateStruct->isRequired)) { - throw new InvalidArgumentType( - $argumentName . '->isRequired', - 'boolean', - $fieldDefinitionCreateStruct->isRequired - ); - } - - if ($fieldDefinitionCreateStruct->isThumbnail !== null && !is_bool($fieldDefinitionCreateStruct->isThumbnail)) { - throw new InvalidArgumentType( - $argumentName . '->isThumbnail', - 'boolean', - $fieldDefinitionCreateStruct->isThumbnail - ); - } - - if ($fieldDefinitionCreateStruct->isInfoCollector !== null && !is_bool($fieldDefinitionCreateStruct->isInfoCollector)) { - throw new InvalidArgumentType( - $argumentName . '->isInfoCollector', - 'boolean', - $fieldDefinitionCreateStruct->isInfoCollector - ); - } - - if ($fieldDefinitionCreateStruct->isSearchable !== null && !is_bool($fieldDefinitionCreateStruct->isSearchable)) { - throw new InvalidArgumentType( - $argumentName . '->isSearchable', - 'boolean', - $fieldDefinitionCreateStruct->isSearchable - ); - } - - // These properties are of type 'mixed' and are validated separately by the corresponding field type - // validatorConfiguration - // fieldSettings - // defaultValue - } - - /** - * Create a Content Type object. - * - * The content type is created in the state STATUS_DRAFT. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create a content type - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException In case when - * - array of content type groups does not contain at least one content type group - * - identifier or remoteId in the content type create struct already exists - * - there is a duplicate field identifier in the content type create struct - * @throws \eZ\Publish\API\Repository\Exceptions\ContentTypeFieldDefinitionValidationException - * if a field definition in the $contentTypeCreateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\ContentTypeValidationException - * if a multiple field definitions of a same singular type are given - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeCreateStruct $contentTypeCreateStruct - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup[] $contentTypeGroups Required array of {@link APIContentTypeGroup} to link type with (must contain one) - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft - */ - public function createContentType(APIContentTypeCreateStruct $contentTypeCreateStruct, array $contentTypeGroups): APIContentTypeDraft - { - if (!$this->permissionResolver->canUser('class', 'create', $contentTypeCreateStruct, $contentTypeGroups)) { - throw new UnauthorizedException('ContentType', 'create'); - } - - // Prevent argument mutation - $contentTypeCreateStruct = clone $contentTypeCreateStruct; - $this->validateInputContentTypeCreateStruct($contentTypeCreateStruct); - $this->validateInputContentTypeGroups($contentTypeGroups); - $initialLanguageId = $this->repository->getContentLanguageService()->loadLanguage( - $contentTypeCreateStruct->mainLanguageCode - )->id; - - try { - $this->contentTypeHandler->loadByIdentifier( - $contentTypeCreateStruct->identifier - ); - - throw new InvalidArgumentException( - '$contentTypeCreateStruct', - "Another Content Type with identifier '{$contentTypeCreateStruct->identifier}' exists" - ); - } catch (APINotFoundException $e) { - // Do nothing - } - - if ($contentTypeCreateStruct->remoteId !== null) { - try { - $this->contentTypeHandler->loadByRemoteId( - $contentTypeCreateStruct->remoteId - ); - - throw new InvalidArgumentException( - '$contentTypeCreateStruct', - "Another Content Type with remoteId '{$contentTypeCreateStruct->remoteId}' exists" - ); - } catch (APINotFoundException $e) { - // Do nothing - } - } - - $fieldDefinitionIdentifierSet = []; - $fieldDefinitionPositionSet = []; - foreach ($contentTypeCreateStruct->fieldDefinitions as $fieldDefinitionCreateStruct) { - // Check for duplicate identifiers - if (!isset($fieldDefinitionIdentifierSet[$fieldDefinitionCreateStruct->identifier])) { - $fieldDefinitionIdentifierSet[$fieldDefinitionCreateStruct->identifier] = true; - } else { - throw new InvalidArgumentException( - '$contentTypeCreateStruct', - "The argument contains duplicate Field definition identifier '{$fieldDefinitionCreateStruct->identifier}'" - ); - } - - // Check for duplicate positions - if (!isset($fieldDefinitionPositionSet[$fieldDefinitionCreateStruct->position])) { - $fieldDefinitionPositionSet[$fieldDefinitionCreateStruct->position] = true; - } else { - throw new InvalidArgumentException( - '$contentTypeCreateStruct', - "The argument contains duplicate Field definition position '{$fieldDefinitionCreateStruct->position}'" - ); - } - } - - $allValidationErrors = []; - $spiFieldDefinitions = []; - $fieldTypeIdentifierSet = []; - foreach ($contentTypeCreateStruct->fieldDefinitions as $fieldDefinitionCreateStruct) { - /** @var $fieldType \eZ\Publish\SPI\FieldType\FieldType */ - $fieldType = $this->fieldTypeRegistry->getFieldType( - $fieldDefinitionCreateStruct->fieldTypeIdentifier - ); - - if ($fieldType->isSingular() && isset($fieldTypeIdentifierSet[$fieldDefinitionCreateStruct->fieldTypeIdentifier])) { - throw new ContentTypeValidationException( - "Field Type '%identifier%' is singular and cannot be used more than once in a Content Type", - ['%identifier%' => $fieldDefinitionCreateStruct->fieldTypeIdentifier] - ); - } - - $fieldTypeIdentifierSet[$fieldDefinitionCreateStruct->fieldTypeIdentifier] = true; - - $fieldType->applyDefaultSettings($fieldDefinitionCreateStruct->fieldSettings); - $fieldType->applyDefaultValidatorConfiguration($fieldDefinitionCreateStruct->validatorConfiguration); - $validationErrors = $this->validateFieldDefinitionCreateStruct( - $fieldDefinitionCreateStruct, - $fieldType - ); - - if (!empty($validationErrors)) { - $allValidationErrors[$fieldDefinitionCreateStruct->identifier] = $validationErrors; - } - - if (!empty($allValidationErrors)) { - continue; - } - - $spiFieldDefinitions[] = $this->contentTypeDomainMapper->buildSPIFieldDefinitionFromCreateStruct( - $fieldDefinitionCreateStruct, - $fieldType, - $contentTypeCreateStruct->mainLanguageCode - ); - } - - if (!empty($allValidationErrors)) { - throw new ContentTypeFieldDefinitionValidationException($allValidationErrors); - } - - $groupIds = array_map( - static function (APIContentTypeGroup $contentTypeGroup) { - return $contentTypeGroup->id; - }, - $contentTypeGroups - ); - - if ($contentTypeCreateStruct->creatorId === null) { - $contentTypeCreateStruct->creatorId = $this->permissionResolver->getCurrentUserReference()->getUserId(); - } - - if ($contentTypeCreateStruct->creationDate === null) { - $timestamp = time(); - } else { - $timestamp = $contentTypeCreateStruct->creationDate->getTimestamp(); - } - - if ($contentTypeCreateStruct->remoteId === null) { - $contentTypeCreateStruct->remoteId = $this->contentDomainMapper->getUniqueHash($contentTypeCreateStruct); - } - - $spiContentTypeCreateStruct = new SPIContentTypeCreateStruct( - [ - 'identifier' => $contentTypeCreateStruct->identifier, - 'name' => $contentTypeCreateStruct->names, - 'status' => APIContentType::STATUS_DRAFT, - 'description' => $contentTypeCreateStruct->descriptions ?? [], - 'created' => $timestamp, - 'modified' => $timestamp, - 'creatorId' => $contentTypeCreateStruct->creatorId, - 'modifierId' => $contentTypeCreateStruct->creatorId, - 'remoteId' => $contentTypeCreateStruct->remoteId, - 'urlAliasSchema' => $contentTypeCreateStruct->urlAliasSchema ?? '', - 'nameSchema' => $contentTypeCreateStruct->nameSchema ?? '', - 'isContainer' => $contentTypeCreateStruct->isContainer ?? false, - 'initialLanguageId' => $initialLanguageId, - 'sortField' => $contentTypeCreateStruct->defaultSortField ?? Location::SORT_FIELD_PUBLISHED, - 'sortOrder' => $contentTypeCreateStruct->defaultSortOrder ?? Location::SORT_ORDER_DESC, - 'groupIds' => $groupIds, - 'fieldDefinitions' => $spiFieldDefinitions, - 'defaultAlwaysAvailable' => $contentTypeCreateStruct->defaultAlwaysAvailable, - ] - ); - - $this->repository->beginTransaction(); - try { - $spiContentType = $this->contentTypeHandler->create( - $spiContentTypeCreateStruct - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->contentTypeDomainMapper->buildContentTypeDraftDomainObject($spiContentType); - } - - /** - * Validates FieldDefinitionCreateStruct. - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct $fieldDefinitionCreateStruct - * @param \eZ\Publish\SPI\FieldType\FieldType $fieldType - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - protected function validateFieldDefinitionCreateStruct(FieldDefinitionCreateStruct $fieldDefinitionCreateStruct, SPIFieldType $fieldType): array - { - $validationErrors = []; - - if ($fieldDefinitionCreateStruct->isSearchable && !$fieldType->isSearchable()) { - $validationErrors[] = new ValidationError( - "FieldType '{$fieldDefinitionCreateStruct->fieldTypeIdentifier}' is not searchable" - ); - } - - return array_merge( - $validationErrors, - $fieldType->validateValidatorConfiguration($fieldDefinitionCreateStruct->validatorConfiguration), - $fieldType->validateFieldSettings($fieldDefinitionCreateStruct->fieldSettings) - ); - } - - /** - * {@inheritdoc} - */ - public function loadContentType(int $contentTypeId, array $prioritizedLanguages = []): ContentType - { - $spiContentType = $this->contentTypeHandler->load($contentTypeId); - - return $this->contentTypeDomainMapper->buildContentTypeDomainObject( - $spiContentType, - $prioritizedLanguages - ); - } - - /** - * {@inheritdoc} - */ - public function loadContentTypeByIdentifier(string $identifier, array $prioritizedLanguages = []): ContentType - { - $spiContentType = $this->contentTypeHandler->loadByIdentifier( - $identifier - ); - - return $this->contentTypeDomainMapper->buildContentTypeDomainObject( - $spiContentType, - $prioritizedLanguages - ); - } - - /** - * {@inheritdoc} - */ - public function loadContentTypeByRemoteId(string $remoteId, array $prioritizedLanguages = []): ContentType - { - $spiContentType = $this->contentTypeHandler->loadByRemoteId($remoteId); - - return $this->contentTypeDomainMapper->buildContentTypeDomainObject( - $spiContentType, - $prioritizedLanguages - ); - } - - /** - * Get a Content Type object draft by id. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If the content type draft owned by the current user can not be found - * - * @param int $contentTypeId - * @param bool $ignoreOwnership if true, method will return draft even if the owner is different than currently logged in user - * - * @todo Use another exception when user of draft is someone else - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft - */ - public function loadContentTypeDraft(int $contentTypeId, bool $ignoreOwnership = false): APIContentTypeDraft - { - $spiContentType = $this->contentTypeHandler->load( - $contentTypeId, - SPIContentType::STATUS_DRAFT - ); - - if (!$ignoreOwnership && $spiContentType->modifierId != $this->permissionResolver->getCurrentUserReference()->getUserId()) { - throw new NotFoundException('The Content Type is owned by someone else', $contentTypeId); - } - - return $this->contentTypeDomainMapper->buildContentTypeDraftDomainObject($spiContentType); - } - - /** - * {@inheritdoc} - */ - public function loadContentTypeList(array $contentTypeIds, array $prioritizedLanguages = []): iterable - { - $spiContentTypes = $this->contentTypeHandler->loadContentTypeList($contentTypeIds); - $contentTypes = []; - - // @todo We could bulk load content type group proxies involved in the future & pass those relevant per type to mapper - foreach ($spiContentTypes as $spiContentType) { - $contentTypes[$spiContentType->id] = $this->contentTypeDomainMapper->buildContentTypeDomainObject( - $spiContentType, - $prioritizedLanguages - ); - } - - return $contentTypes; - } - - /** - * {@inheritdoc} - */ - public function loadContentTypes(APIContentTypeGroup $contentTypeGroup, array $prioritizedLanguages = []): iterable - { - $spiContentTypes = $this->contentTypeHandler->loadContentTypes( - $contentTypeGroup->id, - SPIContentType::STATUS_DEFINED - ); - $contentTypes = []; - - foreach ($spiContentTypes as $spiContentType) { - $contentTypes[] = $this->contentTypeDomainMapper->buildContentTypeDomainObject( - $spiContentType, - $prioritizedLanguages - ); - } - - return $contentTypes; - } - - /** - * Creates a draft from an existing content type. - * - * This is a complete copy of the content - * type which has the state STATUS_DRAFT. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit a content type - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If there is already a draft assigned to another user - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft - */ - public function createContentTypeDraft(APIContentType $contentType): APIContentTypeDraft - { - if (!$this->permissionResolver->canUser('class', 'create', $contentType)) { - throw new UnauthorizedException('ContentType', 'create'); - } - - try { - $this->contentTypeHandler->load( - $contentType->id, - SPIContentType::STATUS_DRAFT - ); - - throw new BadStateException( - '$contentType', - 'Draft of the Content Type already exists' - ); - } catch (APINotFoundException $e) { - $this->repository->beginTransaction(); - try { - $spiContentType = $this->contentTypeHandler->createDraft( - $this->permissionResolver->getCurrentUserReference()->getUserId(), - $contentType->id - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - return $this->contentTypeDomainMapper->buildContentTypeDraftDomainObject($spiContentType); - } - - /** - * Update a Content Type object. - * - * Does not update fields (fieldDefinitions), use {@link updateFieldDefinition()} to update them. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update a content type - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the given identifier or remoteId already exists - * or there is no draft assigned to the authenticated user - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeUpdateStruct $contentTypeUpdateStruct - */ - public function updateContentTypeDraft(APIContentTypeDraft $contentTypeDraft, ContentTypeUpdateStruct $contentTypeUpdateStruct): void - { - if (!$this->permissionResolver->canUser('class', 'update', $contentTypeDraft)) { - throw new UnauthorizedException('ContentType', 'update'); - } - - try { - $loadedContentTypeDraft = $this->loadContentTypeDraft($contentTypeDraft->id); - } catch (APINotFoundException $e) { - throw new InvalidArgumentException( - '$contentTypeDraft', - 'There is no Content Type draft assigned to the authenticated user', - $e - ); - } - - if ($contentTypeUpdateStruct->identifier !== null - && $contentTypeUpdateStruct->identifier != $loadedContentTypeDraft->identifier) { - try { - $this->loadContentTypeByIdentifier($contentTypeUpdateStruct->identifier); - - throw new InvalidArgumentException( - '$contentTypeUpdateStruct', - "Another Content Type with identifier '{$contentTypeUpdateStruct->identifier}' exists" - ); - } catch (APINotFoundException $e) { - // Do nothing - } - } - - if ($contentTypeUpdateStruct->remoteId !== null - && $contentTypeUpdateStruct->remoteId != $loadedContentTypeDraft->remoteId) { - try { - $this->loadContentTypeByRemoteId($contentTypeUpdateStruct->remoteId); - - throw new InvalidArgumentException( - '$contentTypeUpdateStruct', - "Another Content Type with remoteId '{$contentTypeUpdateStruct->remoteId}' exists" - ); - } catch (APINotFoundException $e) { - // Do nothing - } - } - - //Merge new translations into existing before update - $contentTypeUpdateStruct->names = array_merge($contentTypeDraft->getNames(), $contentTypeUpdateStruct->names ?? []); - $contentTypeUpdateStruct->descriptions = array_merge($contentTypeDraft->getDescriptions(), $contentTypeUpdateStruct->descriptions ?? []); - - $this->repository->beginTransaction(); - try { - $this->contentTypeHandler->update( - $contentTypeDraft->id, - $contentTypeDraft->status, - $this->contentTypeDomainMapper->buildSPIContentTypeUpdateStruct( - $loadedContentTypeDraft, - $contentTypeUpdateStruct, - $this->permissionResolver->getCurrentUserReference() - ) - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Delete a Content Type object. - * - * Deletes a content type if it has no instances. If content type in state STATUS_DRAFT is - * given, only the draft content type will be deleted. Otherwise, if content type in state - * STATUS_DEFINED is given, all content type data will be deleted. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If there exist content objects of this type - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete a content type - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - */ - public function deleteContentType(APIContentType $contentType): void - { - if (!$this->permissionResolver->canUser('class', 'delete', $contentType)) { - throw new UnauthorizedException('ContentType', 'delete'); - } - - $this->repository->beginTransaction(); - try { - if (!$contentType instanceof APIContentTypeDraft) { - $this->contentTypeHandler->delete( - $contentType->id, - APIContentTypeDraft::STATUS_DEFINED - ); - } - - $this->contentTypeHandler->delete( - $contentType->id, - APIContentTypeDraft::STATUS_DRAFT - ); - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Copy Type incl fields and groupIds to a new Type object. - * - * New Type will have $creator as creator / modifier, created / modified should be updated with current time, - * updated remoteId and identifier should be appended with '_' + unique string. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to copy a content type - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * @param \eZ\Publish\API\Repository\Values\User\User $creator if null the current-user is used - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType - */ - public function copyContentType(APIContentType $contentType, User $creator = null): ContentType - { - if (!$this->permissionResolver->canUser('class', 'create', $contentType)) { - throw new UnauthorizedException('ContentType', 'create'); - } - - if (empty($creator)) { - $creator = $this->permissionResolver->getCurrentUserReference(); - } - - $this->repository->beginTransaction(); - try { - $spiContentType = $this->contentTypeHandler->copy( - $creator->getUserId(), - $contentType->id, - SPIContentType::STATUS_DEFINED - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadContentType($spiContentType->id); - } - - /** - * Assigns a content type to a content type group. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to unlink a content type - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the content type is already assigned the given group - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup $contentTypeGroup - */ - public function assignContentTypeGroup(APIContentType $contentType, APIContentTypeGroup $contentTypeGroup): void - { - if (!$this->permissionResolver->canUser('class', 'update', $contentType)) { - throw new UnauthorizedException('ContentType', 'update'); - } - - $spiContentType = $this->contentTypeHandler->load( - $contentType->id, - $contentType->status - ); - - if (in_array($contentTypeGroup->id, $spiContentType->groupIds)) { - throw new InvalidArgumentException( - '$contentTypeGroup', - 'The provided Content Type is already assigned to the Content Type group' - ); - } - - $this->repository->beginTransaction(); - try { - $this->contentTypeHandler->link( - $contentTypeGroup->id, - $contentType->id, - $contentType->status - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Unassign a content type from a group. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to link a content type - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the content type is not assigned this the given group. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If $contentTypeGroup is the last group assigned to the content type - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup $contentTypeGroup - */ - public function unassignContentTypeGroup(APIContentType $contentType, APIContentTypeGroup $contentTypeGroup): void - { - if (!$this->permissionResolver->canUser('class', 'update', $contentType, [$contentTypeGroup])) { - throw new UnauthorizedException('ContentType', 'update'); - } - - $spiContentType = $this->contentTypeHandler->load( - $contentType->id, - $contentType->status - ); - - if (!in_array($contentTypeGroup->id, $spiContentType->groupIds)) { - throw new InvalidArgumentException( - '$contentTypeGroup', - 'The provided Content Type is not assigned the Content Type group' - ); - } - - $this->repository->beginTransaction(); - try { - $this->contentTypeHandler->unlink( - $contentTypeGroup->id, - $contentType->id, - $contentType->status - ); - $this->repository->commit(); - } catch (APIBadStateException $e) { - $this->repository->rollback(); - throw new BadStateException( - '$contentType', - 'The provided Content Type group is the last group assigned to the Content Type', - $e - ); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Adds a new field definition to an existing content type. - * - * The content type must be in state DRAFT. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the identifier in already exists in the content type - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit a content type - * @throws \eZ\Publish\API\Repository\Exceptions\ContentTypeFieldDefinitionValidationException - * if a field definition in the $contentTypeCreateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If field definition of the same non-repeatable type is being - * added to the ContentType that already contains one - * or field definition that can't be added to a ContentType that - * has Content instances is being added to such ContentType - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct $fieldDefinitionCreateStruct - */ - public function addFieldDefinition(APIContentTypeDraft $contentTypeDraft, FieldDefinitionCreateStruct $fieldDefinitionCreateStruct): void - { - if (!$this->permissionResolver->canUser('class', 'update', $contentTypeDraft)) { - throw new UnauthorizedException('ContentType', 'update'); - } - - $this->validateInputFieldDefinitionCreateStruct($fieldDefinitionCreateStruct); - $loadedContentTypeDraft = $this->loadContentTypeDraft($contentTypeDraft->id); - - if ($loadedContentTypeDraft->hasFieldDefinition($fieldDefinitionCreateStruct->identifier)) { - throw new InvalidArgumentException( - '$fieldDefinitionCreateStruct', - "Another Field definition with identifier '{$fieldDefinitionCreateStruct->identifier}' exists in the Content Type" - ); - } - //Fill default translations with default value for mainLanguageCode with fallback if no exist - if (is_array($fieldDefinitionCreateStruct->names)) { - foreach ($contentTypeDraft->languageCodes as $languageCode) { - if (!array_key_exists($languageCode, $fieldDefinitionCreateStruct->names)) { - $fieldDefinitionCreateStruct->names[$languageCode] = $fieldDefinitionCreateStruct->names[$contentTypeDraft->mainLanguageCode] ?? reset($fieldDefinitionCreateStruct->names); - } - } - } - - /** @var $fieldType \eZ\Publish\SPI\FieldType\FieldType */ - $fieldType = $this->fieldTypeRegistry->getFieldType( - $fieldDefinitionCreateStruct->fieldTypeIdentifier - ); - - $fieldType->applyDefaultSettings($fieldDefinitionCreateStruct->fieldSettings); - $fieldType->applyDefaultValidatorConfiguration($fieldDefinitionCreateStruct->validatorConfiguration); - $validationErrors = $this->validateFieldDefinitionCreateStruct($fieldDefinitionCreateStruct, $fieldType); - if (!empty($validationErrors)) { - $validationErrors = [$fieldDefinitionCreateStruct->identifier => $validationErrors]; - throw new ContentTypeFieldDefinitionValidationException($validationErrors); - } - - if ($fieldType->isSingular()) { - if ($loadedContentTypeDraft->hasFieldDefinitionOfType($fieldDefinitionCreateStruct->fieldTypeIdentifier)) { - throw new BadStateException( - '$contentTypeDraft', - "The Content Type already contains a Field definition of the singular Field Type '{$fieldDefinitionCreateStruct->fieldTypeIdentifier}'" - ); - } - } - - if ($fieldType->onlyEmptyInstance() && $this->contentTypeHandler->getContentCount($loadedContentTypeDraft->id) - ) { - throw new BadStateException( - '$contentTypeDraft', - "A Field definition of the '{$fieldDefinitionCreateStruct->fieldTypeIdentifier}' Field Type cannot be added because the Content Type already has Content items" - ); - } - - $spiFieldDefinition = $this->contentTypeDomainMapper->buildSPIFieldDefinitionFromCreateStruct( - $fieldDefinitionCreateStruct, - $fieldType, - $contentTypeDraft->mainLanguageCode - ); - - $this->repository->beginTransaction(); - try { - $this->contentTypeHandler->addFieldDefinition( - $contentTypeDraft->id, - $contentTypeDraft->status, - $spiFieldDefinition - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Remove a field definition from an existing Type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the given field definition does not belong to the given type - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit a content type - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition - */ - public function removeFieldDefinition(APIContentTypeDraft $contentTypeDraft, APIFieldDefinition $fieldDefinition): void - { - if (!$this->permissionResolver->canUser('class', 'update', $contentTypeDraft)) { - throw new UnauthorizedException('ContentType', 'update'); - } - - $loadedFieldDefinition = $this->loadContentTypeDraft( - $contentTypeDraft->id - )->getFieldDefinition( - $fieldDefinition->identifier - ); - - if (empty($loadedFieldDefinition) || $loadedFieldDefinition->id != $fieldDefinition->id) { - throw new InvalidArgumentException( - '$fieldDefinition', - 'The given Field definition does not belong to the Content Type' - ); - } - - $this->repository->beginTransaction(); - try { - $this->contentTypeHandler->removeFieldDefinition( - $contentTypeDraft->id, - SPIContentType::STATUS_DRAFT, - $fieldDefinition->id - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Update a field definition. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the field id in the update struct is not found or does not belong to the content type - * If the given identifier is used in an existing field of the given content type - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit a content type - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft the content type draft - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition the field definition which should be updated - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct - */ - public function updateFieldDefinition(APIContentTypeDraft $contentTypeDraft, APIFieldDefinition $fieldDefinition, FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct): void - { - if (!$this->permissionResolver->canUser('class', 'update', $contentTypeDraft)) { - throw new UnauthorizedException('ContentType', 'update'); - } - - $loadedContentTypeDraft = $this->loadContentTypeDraft($contentTypeDraft->id); - $foundFieldId = false; - foreach ($loadedContentTypeDraft->fieldDefinitions as $existingFieldDefinition) { - if ($existingFieldDefinition->id == $fieldDefinition->id) { - $foundFieldId = true; - } elseif ($existingFieldDefinition->identifier == $fieldDefinitionUpdateStruct->identifier) { - throw new InvalidArgumentException( - '$fieldDefinitionUpdateStruct', - "Another Field definition with identifier '{$fieldDefinitionUpdateStruct->identifier}' exists in the Content Type" - ); - } - } - if (!$foundFieldId) { - throw new InvalidArgumentException( - '$fieldDefinition', - 'The given Field definition does not belong to the Content Type' - ); - } - - $spiFieldDefinition = $this->contentTypeDomainMapper->buildSPIFieldDefinitionFromUpdateStruct( - $fieldDefinitionUpdateStruct, - $fieldDefinition, - $contentTypeDraft->mainLanguageCode - ); - - $this->repository->beginTransaction(); - try { - $this->contentTypeHandler->updateFieldDefinition( - $contentTypeDraft->id, - SPIContentType::STATUS_DRAFT, - $spiFieldDefinition - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Publish the content type and update content objects. - * - * This method updates content objects, depending on the changed field definitions. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If the content type has no draft - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the content type has no field definitions - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to publish a content type - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft - */ - public function publishContentTypeDraft(APIContentTypeDraft $contentTypeDraft): void - { - if (!$this->permissionResolver->canUser('class', 'update', $contentTypeDraft)) { - throw new UnauthorizedException('ContentType', 'update'); - } - - try { - $loadedContentTypeDraft = $this->loadContentTypeDraft($contentTypeDraft->id); - } catch (APINotFoundException $e) { - throw new BadStateException( - '$contentTypeDraft', - 'The Content Type does not have a draft.', - $e - ); - } - - if ($loadedContentTypeDraft->getFieldDefinitions()->isEmpty()) { - throw new InvalidArgumentException( - '$contentTypeDraft', - 'The Content Type draft should have at least one Field definition.' - ); - } - - $this->repository->beginTransaction(); - try { - if (empty($loadedContentTypeDraft->nameSchema)) { - $fieldDefinitions = $loadedContentTypeDraft->getFieldDefinitions(); - $this->contentTypeHandler->update( - $contentTypeDraft->id, - $contentTypeDraft->status, - $this->contentTypeDomainMapper->buildSPIContentTypeUpdateStruct( - $loadedContentTypeDraft, - new ContentTypeUpdateStruct( - [ - 'nameSchema' => '<' . $fieldDefinitions[0]->identifier . '>', - ] - ), - $this->permissionResolver->getCurrentUserReference() - ) - ); - } - - $this->contentTypeHandler->publish( - $loadedContentTypeDraft->id - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Instantiates a new content type group create class. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue if given identifier is not a string - * - * @param string $identifier - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupCreateStruct - */ - public function newContentTypeGroupCreateStruct(string $identifier): ContentTypeGroupCreateStruct - { - return new ContentTypeGroupCreateStruct( - [ - 'identifier' => $identifier, - ] - ); - } - - /** - * Instantiates a new content type create class. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue if given identifier is not a string - * - * @param string $identifier - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeCreateStruct - */ - public function newContentTypeCreateStruct(string $identifier): APIContentTypeCreateStruct - { - if (!is_string($identifier)) { - throw new InvalidArgumentValue('$identifier', $identifier); - } - - return new ContentTypeCreateStruct( - [ - 'identifier' => $identifier, - ] - ); - } - - /** - * Instantiates a new content type update struct. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeUpdateStruct - */ - public function newContentTypeUpdateStruct(): ContentTypeUpdateStruct - { - return new ContentTypeUpdateStruct(); - } - - /** - * Instantiates a new content type update struct. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupUpdateStruct - */ - public function newContentTypeGroupUpdateStruct(): ContentTypeGroupUpdateStruct - { - return new ContentTypeGroupUpdateStruct(); - } - - /** - * Instantiates a field definition create struct. - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue if given identifier is not a string - * or given fieldTypeIdentifier is not a string - * - * @param string $fieldTypeIdentifier the required field type identifier - * @param string $identifier the required identifier for the field definition - * - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct - */ - public function newFieldDefinitionCreateStruct(string $identifier, string $fieldTypeIdentifier): FieldDefinitionCreateStruct - { - return new FieldDefinitionCreateStruct( - [ - 'identifier' => $identifier, - 'fieldTypeIdentifier' => $fieldTypeIdentifier, - ] - ); - } - - /** - * Instantiates a field definition update class. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct - */ - public function newFieldDefinitionUpdateStruct(): FieldDefinitionUpdateStruct - { - return new FieldDefinitionUpdateStruct(); - } - - /** - * Returns true if the given content type $contentType has content instances. - * - * @since 6.0.1 - * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * - * @return bool - */ - public function isContentTypeUsed(APIContentType $contentType): bool - { - return $this->contentTypeHandler->getContentCount($contentType->id) > 0; - } - - /** - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft - * @param string $languageCode - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function removeContentTypeTranslation(APIContentTypeDraft $contentTypeDraft, string $languageCode): APIContentTypeDraft - { - if (!$this->permissionResolver->canUser('class', 'update', $contentTypeDraft)) { - throw new UnauthorizedException('ContentType', 'update'); - } - - $this->repository->beginTransaction(); - try { - $contentType = $this->contentTypeHandler->removeContentTypeTranslation( - $contentTypeDraft->id, - $languageCode - ); - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->contentTypeDomainMapper->buildContentTypeDraftDomainObject($contentType); - } - - public function deleteUserDrafts(int $userId): void - { - try { - $this->userHandler->load($userId); - } catch (APINotFoundException $e) { - $this->contentTypeHandler->deleteByUserAndStatus($userId, ContentType::STATUS_DRAFT); - - return; - } - - if ($this->repository->getPermissionResolver()->hasAccess('class', 'delete') !== true) { - throw new UnauthorizedException('ContentType', 'update'); - } - - $this->contentTypeHandler->deleteByUserAndStatus($userId, ContentType::STATUS_DRAFT); - } -} diff --git a/eZ/Publish/Core/Repository/EventSubscriber/DeleteUserSubscriber.php b/eZ/Publish/Core/Repository/EventSubscriber/DeleteUserSubscriber.php deleted file mode 100644 index a35ef45b83..0000000000 --- a/eZ/Publish/Core/Repository/EventSubscriber/DeleteUserSubscriber.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\EventSubscriber; - -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Events\User\DeleteUserEvent; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; - -class DeleteUserSubscriber implements EventSubscriberInterface -{ - /** @var \eZ\Publish\API\Repository\ContentTypeService */ - private $contentTypeService; - - public function __construct(ContentTypeService $contentTypeService) - { - $this->contentTypeService = $contentTypeService; - } - - public static function getSubscribedEvents(): array - { - return [ - DeleteUserEvent::class => 'onDeleteUser', - ]; - } - - public function onDeleteUser(DeleteUserEvent $event): void - { - $this->contentTypeService->deleteUserDrafts($event->getUser()->id); - } -} diff --git a/eZ/Publish/Core/Repository/FieldTypeService.php b/eZ/Publish/Core/Repository/FieldTypeService.php deleted file mode 100644 index 312dd3f82a..0000000000 --- a/eZ/Publish/Core/Repository/FieldTypeService.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use eZ\Publish\API\Repository\FieldType as APIFieldType; -use eZ\Publish\API\Repository\FieldTypeService as FieldTypeServiceInterface; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\Repository\Values\ContentType\FieldType; - -/** - * An implementation of this class provides access to FieldTypes. - * - * @see \eZ\Publish\API\Repository\FieldType - */ -class FieldTypeService implements FieldTypeServiceInterface -{ - /** @var \eZ\Publish\Core\FieldType\FieldTypeRegistry */ - protected $fieldTypeRegistry; - - /** - * Holds an array of FieldType objects to avoid re creating them all the time from SPI variants. - * - * @var \eZ\Publish\API\Repository\FieldType[] - */ - protected $fieldTypes = []; - - /** - * Setups service with reference to repository object that created it & corresponding handler. - * - * @param \eZ\Publish\Core\FieldType\FieldTypeRegistry $fieldTypeRegistry Registry for SPI FieldTypes - */ - public function __construct(FieldTypeRegistry $fieldTypeRegistry) - { - $this->fieldTypeRegistry = $fieldTypeRegistry; - } - - /** - * Returns a list of all field types. - * - * @return \eZ\Publish\API\Repository\FieldType[] - */ - public function getFieldTypes(): iterable - { - foreach ($this->fieldTypeRegistry->getFieldTypes() as $identifier => $spiFieldType) { - if (isset($this->fieldTypes[$identifier])) { - continue; - } - - $this->fieldTypes[$identifier] = new FieldType($spiFieldType); - } - - return $this->fieldTypes; - } - - /** - * Returns the FieldType registered with the given identifier. - * - * @param string $identifier - * - * @return \eZ\Publish\API\Repository\FieldType - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if there is no FieldType registered with $identifier - */ - public function getFieldType(string $identifier): APIFieldType - { - if (isset($this->fieldTypes[$identifier])) { - return $this->fieldTypes[$identifier]; - } - - return $this->fieldTypes[$identifier] = new FieldType($this->fieldTypeRegistry->getFieldType($identifier)); - } - - /** - * Returns if there is a FieldType registered under $identifier. - * - * @param string $identifier - * - * @return bool - */ - public function hasFieldType(string $identifier): bool - { - return $this->fieldTypeRegistry->hasFieldType($identifier); - } -} diff --git a/eZ/Publish/Core/Repository/Helper/NameSchemaService.php b/eZ/Publish/Core/Repository/Helper/NameSchemaService.php deleted file mode 100644 index a5a0f953e5..0000000000 --- a/eZ/Publish/Core/Repository/Helper/NameSchemaService.php +++ /dev/null @@ -1,444 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Helper; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper; -use eZ\Publish\SPI\Persistence\Content\Type as SPIContentType; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandler; - -/** - * NameSchemaService is internal service for resolving content name and url alias patterns. - * This code supports content name pattern groups. - * - * Syntax: - * <code> - * <attribute_identifier> - * <attribute_identifier> <2nd-identifier> - * User text <attribute_identifier>|(<2nd-identifier><3rd-identifier>) - * </code> - * - * Example: - * <code> - * <nickname|(<firstname> <lastname>)> - * </code> - * - * Tokens are looked up from left to right. If a match is found for the - * leftmost token, the 2nd token will not be used. Tokens are representations - * of fields. So a match means that that the current field has data. - * - * Tokens are the field definition identifiers which are used in the class edit-interface. - * - * @internal Meant for internal use by Repository. - */ -class NameSchemaService -{ - /** - * The string to use to signify group tokens. - * - * @var string - */ - public const META_STRING = 'EZMETAGROUP_'; - - /** @var \eZ\Publish\SPI\Persistence\Content\Type\Handler */ - protected $contentTypeHandler; - - /** @var \eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper */ - protected $contentTypeDomainMapper; - - /** @var \eZ\Publish\Core\Repository\Helper\FieldTypeRegistry */ - protected $fieldTypeRegistry; - - /** @var array */ - protected $settings; - - /** - * Constructs a object to resolve $nameSchema with $contentVersion fields values. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type\Handler $contentTypeHandler - * @param \eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper $contentTypeDomainMapper - * @param \eZ\Publish\Core\FieldType\FieldTypeRegistry $fieldTypeRegistry - * @param array $settings - */ - public function __construct( - ContentTypeHandler $contentTypeHandler, - ContentTypeDomainMapper $contentTypeDomainMapper, - FieldTypeRegistry $fieldTypeRegistry, - array $settings = [] - ) { - $this->contentTypeHandler = $contentTypeHandler; - $this->contentTypeDomainMapper = $contentTypeDomainMapper; - $this->fieldTypeRegistry = $fieldTypeRegistry; - // Union makes sure default settings are ignored if provided in argument - $this->settings = $settings + [ - 'limit' => 150, - 'sequence' => '...', - ]; - } - - /** - * Convenience method for resolving URL alias schema. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType|null $contentType - * - * @return array - */ - public function resolveUrlAliasSchema(Content $content, ContentType $contentType = null) - { - if ($contentType === null) { - $contentType = $this->contentTypeHandler->load($content->contentInfo->contentTypeId); - } - - return $this->resolve( - strlen($contentType->urlAliasSchema) === 0 ? $contentType->nameSchema : $contentType->urlAliasSchema, - $contentType, - $content->fields, - $content->versionInfo->languageCodes - ); - } - - /** - * Convenience method for resolving name schema. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param array $fieldMap - * @param array $languageCodes - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType|null $contentType - * - * @return array - */ - public function resolveNameSchema(Content $content, array $fieldMap = [], array $languageCodes = [], ContentType $contentType = null) - { - if ($contentType === null) { - $contentType = $this->contentTypeHandler->load($content->contentInfo->contentTypeId); - } - - $languageCodes = $languageCodes ?: $content->versionInfo->languageCodes; - - return $this->resolve( - $contentType->nameSchema, - $contentType, - $this->mergeFieldMap( - $content, - $fieldMap, - $languageCodes - ), - $languageCodes - ); - } - - /** - * Convenience method for resolving name schema. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param array $fieldMap - * @param array $languageCodes - * - * @return array - */ - protected function mergeFieldMap(Content $content, array $fieldMap, array $languageCodes) - { - if (empty($fieldMap)) { - return $content->fields; - } - - $mergedFieldMap = []; - - foreach ($content->fields as $fieldIdentifier => $fieldLanguageMap) { - foreach ($languageCodes as $languageCode) { - $mergedFieldMap[$fieldIdentifier][$languageCode] = isset($fieldMap[$fieldIdentifier][$languageCode]) - ? $fieldMap[$fieldIdentifier][$languageCode] - : $fieldLanguageMap[$languageCode]; - } - } - - return $mergedFieldMap; - } - - /** - * Returns the real name for a content name pattern. - * - * @param string $nameSchema - * @param \eZ\Publish\SPI\Persistence\Content\Type|\eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * @param array $fieldMap - * @param array $languageCodes - * - * @return string[] - */ - public function resolve($nameSchema, $contentType, array $fieldMap, array $languageCodes) - { - list($filteredNameSchema, $groupLookupTable) = $this->filterNameSchema($nameSchema); - $tokens = $this->extractTokens($filteredNameSchema); - $schemaIdentifiers = $this->getIdentifiers($nameSchema); - - $names = []; - - foreach ($languageCodes as $languageCode) { - // Fetch titles for language code - $titles = $this->getFieldTitles($schemaIdentifiers, $contentType, $fieldMap, $languageCode); - $name = $filteredNameSchema; - - // Replace tokens with real values - foreach ($tokens as $token) { - $string = $this->resolveToken($token, $titles, $groupLookupTable); - $name = str_replace($token, $string, $name); - } - - // Make sure length is not longer then $limit unless it's 0 - if ($this->settings['limit'] && mb_strlen($name) > $this->settings['limit']) { - $name = rtrim(mb_substr($name, 0, $this->settings['limit'] - strlen($this->settings['sequence']))) . $this->settings['sequence']; - } - - $names[$languageCode] = $name; - } - - return $names; - } - - /** - * Fetches the list of available Field identifiers in the token and returns - * an array of their current title value. - * - * @see \eZ\Publish\Core\Repository\FieldType::getName() - * - * @param string[] $schemaIdentifiers - * @param \eZ\Publish\SPI\Persistence\Content\Type|\eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * @param array $fieldMap - * @param string $languageCode - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType - * - * @return string[] Key is the field identifier, value is the title value - */ - protected function getFieldTitles(array $schemaIdentifiers, $contentType, array $fieldMap, $languageCode) - { - $fieldTitles = []; - - foreach ($schemaIdentifiers as $fieldDefinitionIdentifier) { - if (isset($fieldMap[$fieldDefinitionIdentifier][$languageCode])) { - if ($contentType instanceof SPIContentType) { - $fieldDefinition = null; - foreach ($contentType->fieldDefinitions as $spiFieldDefinition) { - if ($spiFieldDefinition->identifier === $fieldDefinitionIdentifier) { - $fieldDefinition = $this->contentTypeDomainMapper->buildFieldDefinitionDomainObject( - $spiFieldDefinition, - // This is probably not main language, but as we don't expose it, it's ok for now. - $languageCode - ); - break; - } - } - - if ($fieldDefinition === null) { - $fieldTitles[$fieldDefinitionIdentifier] = ''; - continue; - } - } elseif ($contentType instanceof ContentType) { - $fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier); - } else { - throw new InvalidArgumentType('$contentType', 'API or SPI variant of a Content Type'); - } - - $fieldTypeService = $this->fieldTypeRegistry->getFieldType( - $fieldDefinition->fieldTypeIdentifier - ); - - $fieldTitles[$fieldDefinitionIdentifier] = $fieldTypeService->getName( - $fieldMap[$fieldDefinitionIdentifier][$languageCode], - $fieldDefinition, - $languageCode - ); - } - } - - return $fieldTitles; - } - - /** - * Extract all tokens from $namePattern. - * - * Example: - * <code> - * Text <token> more text ==> <token> - * </code> - * - * @param string $nameSchema - * - * @return array - */ - protected function extractTokens($nameSchema) - { - preg_match_all( - '|<([^>]+)>|U', - $nameSchema, - $tokenArray - ); - - return $tokenArray[0]; - } - - /** - * Looks up the value $token should be replaced with and returns this as - * a string. Meta strings denoting token groups are automatically - * inferred. - * - * @param string $token - * @param array $titles - * @param array $groupLookupTable - * - * @return string - */ - protected function resolveToken($token, $titles, $groupLookupTable) - { - $replaceString = ''; - $tokenParts = $this->tokenParts($token); - - foreach ($tokenParts as $tokenPart) { - if ($this->isTokenGroup($tokenPart)) { - $replaceString = $groupLookupTable[$tokenPart]; - $groupTokenArray = $this->extractTokens($replaceString); - - foreach ($groupTokenArray as $groupToken) { - $replaceString = str_replace( - $groupToken, - $this->resolveToken( - $groupToken, - $titles, - $groupLookupTable - ), - $replaceString - ); - } - - // We want to stop after the first matching token part / identifier is found - // <id1|id2> if id1 has a value, id2 will not be used. - // In this case id1 or id1 is a token group. - break; - } else { - if (array_key_exists($tokenPart, $titles) && $titles[$tokenPart] !== '' && $titles[$tokenPart] !== null) { - $replaceString = $titles[$tokenPart]; - // We want to stop after the first matching token part / identifier is found - // <id1|id2> if id1 has a value, id2 will not be used. - break; - } - } - } - - return $replaceString; - } - - /** - * Checks whether $identifier is a placeholder for a token group. - * - * @param string $identifier - * - * @return bool - */ - protected function isTokenGroup($identifier) - { - if (strpos($identifier, self::META_STRING) === false) { - return false; - } - - return true; - } - - /** - * Returns the different constituents of $token in an array. - * The normal case here is that the different identifiers within one token - * will be tokenized and returned. - * - * Example: - * <code> - * "<title|text>" ==> array( 'title', 'text' ) - * </code> - * - * @param string $token - * - * @return array - */ - protected function tokenParts($token) - { - return preg_split('#\\W#', $token, -1, PREG_SPLIT_NO_EMPTY); - } - - /** - * Builds a lookup / translation table for groups in the $namePattern. - * The groups are referenced with a generated meta-token in the original - * name pattern. - * - * Returns intermediate name pattern where groups are replaced with meta- - * tokens. - * - * @param string $nameSchema - * - * @return string - */ - protected function filterNameSchema($nameSchema) - { - $retNamePattern = ''; - $foundGroups = preg_match_all('/[<|\\|](\\(.+\\))[\\||>]/U', $nameSchema, $groupArray); - $groupLookupTable = []; - - if ($foundGroups) { - $i = 0; - foreach ($groupArray[1] as $group) { - // Create meta-token for group - $metaToken = self::META_STRING . $i; - - // Insert the group with its placeholder token - $retNamePattern = str_replace($group, $metaToken, $nameSchema); - - // Remove the pattern "(" ")" from the tokens - $group = str_replace(['(', ')'], '', $group); - - $groupLookupTable[$metaToken] = $group; - ++$i; - } - $nameSchema = $retNamePattern; - } - - return [$nameSchema, $groupLookupTable]; - } - - /** - * Returns all identifiers from all tokens in the name schema. - * - * @param string $schemaString - * - * @return array - */ - protected function getIdentifiers($schemaString) - { - $allTokens = '#<(.*)>#U'; - $identifiers = '#\\W#'; - - $tmpArray = []; - preg_match_all($allTokens, $schemaString, $matches); - - foreach ($matches[1] as $match) { - $tmpArray[] = preg_split($identifiers, $match, -1, PREG_SPLIT_NO_EMPTY); - } - - $retArray = []; - foreach ($tmpArray as $matchGroup) { - if (is_array($matchGroup)) { - foreach ($matchGroup as $item) { - $retArray[] = $item; - } - } else { - $retArray[] = $matchGroup; - } - } - - return $retArray; - } -} diff --git a/eZ/Publish/Core/Repository/Helper/RoleDomainMapper.php b/eZ/Publish/Core/Repository/Helper/RoleDomainMapper.php deleted file mode 100644 index 23ba25ce5d..0000000000 --- a/eZ/Publish/Core/Repository/Helper/RoleDomainMapper.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Helper; - -use eZ\Publish\Core\Repository\Mapper\RoleDomainMapper as BaseRoleDomainMapper; - -/** - * Internal service to map Role objects between API and SPI values. - * - * @internal Meant for internal use by Repository. - * - * @deprecated since eZ Platform 3.0.1, to be removed in eZ Platform 3.0.x (it's internal - no BC promise) - */ -class RoleDomainMapper extends BaseRoleDomainMapper -{ -} diff --git a/eZ/Publish/Core/Repository/LanguageService.php b/eZ/Publish/Core/Repository/LanguageService.php deleted file mode 100644 index 4d820915da..0000000000 --- a/eZ/Publish/Core/Repository/LanguageService.php +++ /dev/null @@ -1,404 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\LanguageService as LanguageServiceInterface; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\LanguageCreateStruct; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\SPI\Persistence\Content\Language as SPILanguage; -use eZ\Publish\SPI\Persistence\Content\Language\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Language\Handler; -use LogicException; - -/** - * Language service, used for language operations. - */ -class LanguageService implements LanguageServiceInterface -{ - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\SPI\Persistence\Content\Language\Handler */ - protected $languageHandler; - - /** @var array */ - protected $settings; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionResolver; - - /** - * Setups service with reference to repository object that created it & corresponding handler. - * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $languageHandler - * @param array $settings - */ - public function __construct( - RepositoryInterface $repository, - Handler $languageHandler, - PermissionResolver $permissionResolver, - array $settings = [] - ) { - $this->repository = $repository; - $this->languageHandler = $languageHandler; - $this->permissionResolver = $permissionResolver; - // Union makes sure default settings are ignored if provided in argument - $this->settings = $settings + [ - 'languages' => ['eng-GB'], - ]; - } - - /** - * Creates the a new Language in the content repository. - * - * @param \eZ\Publish\API\Repository\Values\Content\LanguageCreateStruct $languageCreateStruct - * - * @return \eZ\Publish\API\Repository\Values\Content\Language - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If user does not have access to content translations - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the languageCode already exists - */ - public function createLanguage(LanguageCreateStruct $languageCreateStruct): Language - { - if (!is_string($languageCreateStruct->name) || empty($languageCreateStruct->name)) { - throw new InvalidArgumentValue('name', $languageCreateStruct->name, 'LanguageCreateStruct'); - } - - if (!is_string($languageCreateStruct->languageCode) || empty($languageCreateStruct->languageCode)) { - throw new InvalidArgumentValue('languageCode', $languageCreateStruct->languageCode, 'LanguageCreateStruct'); - } - - if (!is_bool($languageCreateStruct->enabled)) { - throw new InvalidArgumentValue('enabled', $languageCreateStruct->enabled, 'LanguageCreateStruct'); - } - - if (!$this->permissionResolver->canUser('content', 'translations', $languageCreateStruct)) { - throw new UnauthorizedException('content', 'translations'); - } - - try { - if ($this->loadLanguage($languageCreateStruct->languageCode) !== null) { - throw new InvalidArgumentException('languageCreateStruct', 'language with the specified language code already exists'); - } - } catch (APINotFoundException $e) { - // Do nothing - } - - $createStruct = new CreateStruct( - [ - 'languageCode' => $languageCreateStruct->languageCode, - 'name' => $languageCreateStruct->name, - 'isEnabled' => $languageCreateStruct->enabled, - ] - ); - - $this->repository->beginTransaction(); - try { - $createdLanguage = $this->languageHandler->create($createStruct); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->buildDomainObject($createdLanguage); - } - - /** - * Changes the name of the language in the content repository. - * - * @param \eZ\Publish\API\Repository\Values\Content\Language $language - * @param string $newName - * - * @return \eZ\Publish\API\Repository\Values\Content\Language - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if languageCode argument - * is not string - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If user does not have access to content translations - */ - public function updateLanguageName(Language $language, string $newName): Language - { - if (empty($newName)) { - throw new InvalidArgumentValue('newName', $newName); - } - - if (!$this->permissionResolver->canUser('content', 'translations', $language)) { - throw new UnauthorizedException('content', 'translations'); - } - - $loadedLanguage = $this->loadLanguageById($language->id); - - $updateLanguageStruct = new SPILanguage( - [ - 'id' => $loadedLanguage->id, - 'languageCode' => $loadedLanguage->languageCode, - 'name' => $newName, - 'isEnabled' => $loadedLanguage->enabled, - ] - ); - - $this->repository->beginTransaction(); - try { - $this->languageHandler->update($updateLanguageStruct); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadLanguageById($loadedLanguage->id); - } - - /** - * Enables a language. - * - * @param \eZ\Publish\API\Repository\Values\Content\Language $language - * - * @return \eZ\Publish\API\Repository\Values\Content\Language - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If user does not have access to content translations - */ - public function enableLanguage(Language $language): Language - { - if (!$this->permissionResolver->canUser('content', 'translations', $language)) { - throw new UnauthorizedException('content', 'translations'); - } - - $loadedLanguage = $this->loadLanguageById($language->id); - - $updateLanguageStruct = new SPILanguage( - [ - 'id' => $loadedLanguage->id, - 'languageCode' => $loadedLanguage->languageCode, - 'name' => $loadedLanguage->name, - 'isEnabled' => true, - ] - ); - - $this->repository->beginTransaction(); - try { - $this->languageHandler->update($updateLanguageStruct); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadLanguageById($loadedLanguage->id); - } - - /** - * Disables a language. - * - * @param \eZ\Publish\API\Repository\Values\Content\Language $language - * - * @return \eZ\Publish\API\Repository\Values\Content\Language - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If user does not have access to content translations - */ - public function disableLanguage(Language $language): Language - { - if (!$this->permissionResolver->canUser('content', 'translations', $language)) { - throw new UnauthorizedException('content', 'translations'); - } - - $loadedLanguage = $this->loadLanguageById($language->id); - - $updateLanguageStruct = new SPILanguage( - [ - 'id' => $loadedLanguage->id, - 'languageCode' => $loadedLanguage->languageCode, - 'name' => $loadedLanguage->name, - 'isEnabled' => false, - ] - ); - - $this->repository->beginTransaction(); - try { - $this->languageHandler->update($updateLanguageStruct); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadLanguageById($loadedLanguage->id); - } - - /** - * Loads a Language from its language code ($languageCode). - * - * @param string $languageCode - * - * @return \eZ\Publish\API\Repository\Values\Content\Language - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if languageCode argument - * is not string - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if language could not be found - */ - public function loadLanguage(string $languageCode): Language - { - if (empty($languageCode)) { - throw new InvalidArgumentException('languageCode', 'language code has an invalid value'); - } - - $language = $this->languageHandler->loadByLanguageCode($languageCode); - - return $this->buildDomainObject($language); - } - - /** - * Loads all Languages. - * - * @return \eZ\Publish\API\Repository\Values\Content\Language[] - */ - public function loadLanguages(): iterable - { - $languages = $this->languageHandler->loadAll(); - - $returnArray = []; - foreach ($languages as $language) { - $returnArray[] = $this->buildDomainObject($language); - } - - return $returnArray; - } - - /** - * Loads a Language by its id ($languageId). - * - * @param mixed $languageId - * - * @return \eZ\Publish\API\Repository\Values\Content\Language - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if language could not be found - */ - public function loadLanguageById(int $languageId): Language - { - $language = $this->languageHandler->load($languageId); - - return $this->buildDomainObject($language); - } - - /** - * {@inheritdoc} - */ - public function loadLanguageListByCode(array $languageCodes): iterable - { - $languages = $this->languageHandler->loadListByLanguageCodes($languageCodes); - - $returnArray = []; - foreach ($languages as $language) { - $returnArray[$language->languageCode] = $this->buildDomainObject($language); - } - - return $returnArray; - } - - /** - * {@inheritdoc} - */ - public function loadLanguageListById(array $languageIds): iterable - { - $languages = $this->languageHandler->loadList($languageIds); - - $returnArray = []; - foreach ($languages as $language) { - $returnArray[$language->id] = $this->buildDomainObject($language); - } - - return $returnArray; - } - - /** - * Deletes a language from content repository. - * - * @param \eZ\Publish\API\Repository\Values\Content\Language $language - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if language can not be deleted - * because it is still assigned to some content / type / (...). - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If user does not have access to content translations - */ - public function deleteLanguage(Language $language): void - { - if (!$this->permissionResolver->canUser('content', 'translations', $language)) { - throw new UnauthorizedException('content', 'translations'); - } - - $loadedLanguage = $this->loadLanguageById($language->id); - - $this->repository->beginTransaction(); - try { - $this->languageHandler->delete($loadedLanguage->id); - $this->repository->commit(); - } catch (LogicException $e) { - $this->repository->rollback(); - throw new InvalidArgumentException('language', $e->getMessage(), $e); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Returns a configured default language code. - * - * @return string - */ - public function getDefaultLanguageCode(): string - { - return $this->settings['languages'][0]; - } - - /** - * Returns a configured list of prioritized languageCodes. - * - * - * @return string[] - */ - public function getPrioritizedLanguageCodeList() - { - return $this->settings['languages']; - } - - /** - * Instantiates an object to be used for creating languages. - * - * @return \eZ\Publish\API\Repository\Values\Content\LanguageCreateStruct - */ - public function newLanguageCreateStruct(): LanguageCreateStruct - { - return new LanguageCreateStruct(); - } - - /** - * Builds Language domain object from ValueObject returned by Persistence API. - * - * @param \eZ\Publish\SPI\Persistence\Content\Language $spiLanguage - * - * @return \eZ\Publish\API\Repository\Values\Content\Language - */ - protected function buildDomainObject(SPILanguage $spiLanguage) - { - return new Language( - [ - 'id' => $spiLanguage->id, - 'languageCode' => $spiLanguage->languageCode, - 'name' => $spiLanguage->name, - 'enabled' => $spiLanguage->isEnabled, - ] - ); - } -} diff --git a/eZ/Publish/Core/Repository/LocationResolver/LocationResolver.php b/eZ/Publish/Core/Repository/LocationResolver/LocationResolver.php deleted file mode 100644 index cdad3e7455..0000000000 --- a/eZ/Publish/Core/Repository/LocationResolver/LocationResolver.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\LocationResolver; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; - -/** - * @internal For internal use by eZ Platform core packages - */ -interface LocationResolver -{ - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - */ - public function resolveLocation(ContentInfo $contentInfo): Location; -} diff --git a/eZ/Publish/Core/Repository/LocationResolver/PermissionAwareLocationResolver.php b/eZ/Publish/Core/Repository/LocationResolver/PermissionAwareLocationResolver.php deleted file mode 100644 index a5d93fbeb2..0000000000 --- a/eZ/Publish/Core/Repository/LocationResolver/PermissionAwareLocationResolver.php +++ /dev/null @@ -1,59 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\LocationResolver; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\Base\Exceptions\NotFoundException as CoreNotFoundException; - -/** - * @internal For internal use by eZ Platform core packages - */ -final class PermissionAwareLocationResolver implements LocationResolver -{ - /** @var \eZ\Publish\API\Repository\LocationService */ - private $locationService; - - public function __construct(LocationService $locationService) - { - $this->locationService = $locationService; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - */ - public function resolveLocation(ContentInfo $contentInfo): Location - { - try { - if (null === $contentInfo->mainLocationId) { - throw new CoreNotFoundException('location', $contentInfo->mainLocationId); - } - - $location = $this->locationService->loadLocation($contentInfo->mainLocationId); - } catch (NotFoundException | UnauthorizedException $e) { - // try different locations if main location is not accessible for the user - $locations = $this->locationService->loadLocations($contentInfo); - if (empty($locations) || null === $contentInfo->mainLocationId) { - throw $e; - } - - // foreach to keep forward compatibility with a type of returned loadLocations() result - foreach ($locations as $location) { - return $location; - } - } - - return $location; - } -} diff --git a/eZ/Publish/Core/Repository/LocationService.php b/eZ/Publish/Core/Repository/LocationService.php deleted file mode 100644 index 8fa272dd9c..0000000000 --- a/eZ/Publish/Core/Repository/LocationService.php +++ /dev/null @@ -1,1024 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use Exception; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\LocationService as LocationServiceInterface; -use eZ\Publish\API\Repository\PermissionCriterionResolver; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\LocationList; -use eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LanguageCode; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd as CriterionLogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot as CriterionLogicalNot; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree as CriterionSubtree; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\Core\Repository\Mapper\ContentDomainMapper; -use eZ\Publish\SPI\Limitation\Target; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; -use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct; -use eZ\Publish\SPI\Persistence\Filter\Location\Handler as LocationFilteringHandler; -use eZ\Publish\SPI\Persistence\Handler; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; -use Ibexa\Contracts\Core\Limitation\Target\DestinationLocation as DestinationLocationTarget; -use Psr\Log\LoggerInterface; -use Psr\Log\NullLogger; - -/** - * Location service, used for complex subtree operations. - * - * @example Examples/location.php - */ -class LocationService implements LocationServiceInterface -{ - /** @var \eZ\Publish\Core\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\SPI\Persistence\Handler */ - protected $persistenceHandler; - - /** @var array */ - protected $settings; - - /** @var \eZ\Publish\Core\Repository\Mapper\ContentDomainMapper */ - protected $contentDomainMapper; - - /** @var \eZ\Publish\Core\Repository\Helper\NameSchemaService */ - protected $nameSchemaService; - - /** @var \eZ\Publish\API\Repository\PermissionCriterionResolver */ - protected $permissionCriterionResolver; - - /** @var \Psr\Log\LoggerInterface */ - private $logger; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionResolver; - - /** @var \eZ\Publish\SPI\Persistence\Filter\Location\Handler */ - private $locationFilteringHandler; - - /** @var \eZ\Publish\API\Repository\ContentTypeService */ - protected $contentTypeService; - - /** - * Setups service with reference to repository object that created it & corresponding handler. - * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\SPI\Persistence\Handler $handler - * @param \eZ\Publish\Core\Repository\Mapper\ContentDomainMapper $contentDomainMapper - * @param \eZ\Publish\Core\Repository\Helper\NameSchemaService $nameSchemaService - * @param \eZ\Publish\API\Repository\PermissionCriterionResolver $permissionCriterionResolver - * @param \eZ\Publish\API\Repository\ContentTypeService $contentTypeService - * @param array $settings - * @param \Psr\Log\LoggerInterface|null $logger - */ - public function __construct( - RepositoryInterface $repository, - Handler $handler, - ContentDomainMapper $contentDomainMapper, - Helper\NameSchemaService $nameSchemaService, - PermissionCriterionResolver $permissionCriterionResolver, - PermissionResolver $permissionResolver, - LocationFilteringHandler $locationFilteringHandler, - ContentTypeService $contentTypeService, - array $settings = [], - LoggerInterface $logger = null - ) { - $this->repository = $repository; - $this->persistenceHandler = $handler; - $this->contentDomainMapper = $contentDomainMapper; - $this->nameSchemaService = $nameSchemaService; - $this->permissionResolver = $permissionResolver; - $this->locationFilteringHandler = $locationFilteringHandler; - // Union makes sure default settings are ignored if provided in argument - $this->settings = $settings + [ - //'defaultSetting' => array(), - ]; - $this->permissionCriterionResolver = $permissionCriterionResolver; - $this->contentTypeService = $contentTypeService; - $this->logger = null !== $logger ? $logger : new NullLogger(); - } - - /** - * Copies the subtree starting from $subtree as a new subtree of $targetLocation. - * - * Only the items on which the user has read access are copied. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed copy the subtree to the given parent location - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user does not have read access to the whole source subtree - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the target location is a sub location of the given location - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $subtree - the subtree denoted by the location to copy - * @param \eZ\Publish\API\Repository\Values\Content\Location $targetParentLocation - the target parent location for the copy operation - * - * @return \eZ\Publish\API\Repository\Values\Content\Location The newly created location of the copied subtree - */ - public function copySubtree(APILocation $subtree, APILocation $targetParentLocation): APILocation - { - $loadedSubtree = $this->loadLocation($subtree->id); - $loadedTargetLocation = $this->loadLocation($targetParentLocation->id); - - if (stripos($loadedTargetLocation->pathString, $loadedSubtree->pathString) !== false) { - throw new InvalidArgumentException('targetParentLocation', 'Cannot copy subtree to its own descendant Location'); - } - - $this->checkCreatePermissionOnSubtreeTarget($targetParentLocation, $loadedSubtree, $loadedTargetLocation); - - $this->repository->beginTransaction(); - try { - $newLocation = $this->persistenceHandler->locationHandler()->copySubtree( - $loadedSubtree->id, - $loadedTargetLocation->id, - $this->repository->getPermissionResolver()->getCurrentUserReference()->getUserId() - ); - - $content = $this->repository->getContentService()->loadContent($newLocation->contentId); - $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content); - foreach ($urlAliasNames as $languageCode => $name) { - $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation( - $newLocation->id, - $loadedTargetLocation->id, - $name, - $languageCode, - $content->contentInfo->alwaysAvailable - ); - } - - $this->persistenceHandler->urlAliasHandler()->locationCopied( - $loadedSubtree->id, - $newLocation->id, - $loadedTargetLocation->id - ); - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->contentDomainMapper->buildLocationWithContent($newLocation, $content); - } - - /** - * {@inheritdoc} - */ - public function loadLocation(int $locationId, ?array $prioritizedLanguages = null, ?bool $useAlwaysAvailable = null): APILocation - { - $spiLocation = $this->persistenceHandler->locationHandler()->load($locationId, $prioritizedLanguages, $useAlwaysAvailable ?? true); - $location = $this->contentDomainMapper->buildLocation($spiLocation, $prioritizedLanguages ?: [], $useAlwaysAvailable ?? true); - if (!$this->permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) { - throw new UnauthorizedException('content', 'read', ['locationId' => $location->id]); - } - - return $location; - } - - /** - * {@inheritdoc} - */ - public function loadLocationList(array $locationIds, ?array $prioritizedLanguages = null, ?bool $useAlwaysAvailable = null): iterable - { - $spiLocations = $this->persistenceHandler->locationHandler()->loadList( - $locationIds, - $prioritizedLanguages, - $useAlwaysAvailable ?? true - ); - if (empty($spiLocations)) { - return []; - } - - // Get content id's - $contentIds = []; - foreach ($spiLocations as $spiLocation) { - $contentIds[] = $spiLocation->contentId; - } - - // Load content info and Get content proxy - $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList($contentIds); - $contentProxyList = $this->contentDomainMapper->buildContentProxyList( - $spiContentInfoList, - $prioritizedLanguages ?? [], - $useAlwaysAvailable ?? true - ); - - // Build locations using the bulk retrieved content info and bulk lazy loaded content proxies. - $locations = []; - $permissionResolver = $this->repository->getPermissionResolver(); - foreach ($spiLocations as $spiLocation) { - $location = $this->contentDomainMapper->buildLocationWithContent( - $spiLocation, - $contentProxyList[$spiLocation->contentId] ?? null, - $spiContentInfoList[$spiLocation->contentId] ?? null - ); - - if ($permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) { - $locations[$spiLocation->id] = $location; - } - } - - return $locations; - } - - /** - * {@inheritdoc} - */ - public function loadLocationByRemoteId(string $remoteId, ?array $prioritizedLanguages = null, ?bool $useAlwaysAvailable = null): APILocation - { - $spiLocation = $this->persistenceHandler->locationHandler()->loadByRemoteId($remoteId, $prioritizedLanguages, $useAlwaysAvailable ?? true); - $location = $this->contentDomainMapper->buildLocation($spiLocation, $prioritizedLanguages ?: [], $useAlwaysAvailable ?? true); - if (!$this->permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) { - throw new UnauthorizedException('content', 'read', ['locationId' => $location->id]); - } - - return $location; - } - - /** - * {@inheritdoc} - */ - public function loadLocations(ContentInfo $contentInfo, ?APILocation $rootLocation = null, ?array $prioritizedLanguages = null): iterable - { - if (!$contentInfo->published) { - throw new BadStateException('$contentInfo', 'The Content item has no published versions'); - } - - $spiLocations = $this->persistenceHandler->locationHandler()->loadLocationsByContent( - $contentInfo->id, - $rootLocation !== null ? $rootLocation->id : null - ); - - $locations = []; - $spiInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($contentInfo->id); - $content = $this->contentDomainMapper->buildContentProxy($spiInfo, $prioritizedLanguages ?: []); - foreach ($spiLocations as $spiLocation) { - $location = $this->contentDomainMapper->buildLocationWithContent($spiLocation, $content, $spiInfo); - if ($this->permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) { - $locations[] = $location; - } - } - - return $locations; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function loadLocationChildren( - APILocation $location, - int $offset = 0, - int $limit = 25, - ?array $prioritizedLanguages = null - ): LocationList { - if (!$this->contentDomainMapper->isValidLocationSortField($location->sortField)) { - throw new InvalidArgumentValue('sortField', $location->sortField, 'Location'); - } - - if (!$this->contentDomainMapper->isValidLocationSortOrder($location->sortOrder)) { - throw new InvalidArgumentValue('sortOrder', $location->sortOrder, 'Location'); - } - - if (!is_int($offset)) { - throw new InvalidArgumentValue('offset', $offset); - } - - if (!is_int($limit)) { - throw new InvalidArgumentValue('limit', $limit); - } - - $filter = $this->buildLocationChildrenFilter($location); - $filter - ->withLimit($limit) - ->withOffset($offset); - - $sortClauses = $location->getSortClauses(); - foreach ($sortClauses as $sortClause) { - if ($sortClause instanceof FilteringSortClause) { - $filter->withSortClause($sortClause); - } - } - - return $this->find($filter, $prioritizedLanguages); - } - - /** - * {@inheritdoc} - */ - public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?array $prioritizedLanguages = null): iterable - { - if (!$versionInfo->isDraft()) { - throw new BadStateException( - '$contentInfo', - sprintf( - 'Content item [%d] %s is already published. Use LocationService::loadLocations instead.', - $versionInfo->contentInfo->id, - $versionInfo->contentInfo->name - ) - ); - } - - $spiLocations = $this->persistenceHandler - ->locationHandler() - ->loadParentLocationsForDraftContent($versionInfo->contentInfo->id); - - $contentIds = []; - foreach ($spiLocations as $spiLocation) { - $contentIds[] = $spiLocation->contentId; - } - - $locations = []; - $permissionResolver = $this->repository->getPermissionResolver(); - $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList($contentIds); - $contentList = $this->contentDomainMapper->buildContentProxyList($spiContentInfoList, $prioritizedLanguages ?: []); - foreach ($spiLocations as $spiLocation) { - $location = $this->contentDomainMapper->buildLocationWithContent( - $spiLocation, - $contentList[$spiLocation->contentId], - $spiContentInfoList[$spiLocation->contentId] - ); - - if ($permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) { - $locations[] = $location; - } - } - - return $locations; - } - - /** - * Returns the number of children which are readable by the current user of a Location object. - */ - public function getLocationChildCount(APILocation $location): int - { - $filter = $this->buildLocationChildrenFilter($location); - - return $this->count($filter); - } - - public function getSubtreeSize(APILocation $location): int - { - return $this->persistenceHandler->locationHandler()->getSubtreeSize( - $location->getPathString() - ); - } - - protected function buildLocationChildrenFilter(APILocation $location): Filter - { - $filter = new Filter(); - $filter - ->withCriterion(new Criterion\ParentLocationId($location->id)); - - return $filter; - } - - /** - * Creates the new $location in the content repository for the given content. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to create this location - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the content is already below the specified parent - * or the parent is a sub location of the location of the content - * or if set the remoteId exists already - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct $locationCreateStruct - * - * @return \eZ\Publish\API\Repository\Values\Content\Location the newly created Location - */ - public function createLocation(ContentInfo $contentInfo, LocationCreateStruct $locationCreateStruct): APILocation - { - $content = $this->contentDomainMapper->buildContentDomainObjectFromPersistence( - $this->persistenceHandler->contentHandler()->load($contentInfo->id), - $this->persistenceHandler->contentTypeHandler()->load($contentInfo->contentTypeId) - ); - - $parentLocation = $this->contentDomainMapper->buildLocation( - $this->persistenceHandler->locationHandler()->load($locationCreateStruct->parentLocationId) - ); - - $contentType = $content->getContentType(); - - $locationCreateStruct->sortField = $locationCreateStruct->sortField - ?? ($contentType->defaultSortField ?? Location::SORT_FIELD_NAME); - $locationCreateStruct->sortOrder = $locationCreateStruct->sortOrder - ?? ($contentType->defaultSortOrder ?? Location::SORT_ORDER_ASC); - - $contentInfo = $content->contentInfo; - - if (!$this->permissionResolver->canUser('content', 'manage_locations', $contentInfo, [$parentLocation])) { - throw new UnauthorizedException('content', 'manage_locations', ['contentId' => $contentInfo->id]); - } - - $locationTarget = (new DestinationLocationTarget($parentLocation->id, $contentInfo)); - if (!$this->permissionResolver->canUser('content', 'create', $contentInfo, [$parentLocation, $locationTarget])) { - throw new UnauthorizedException('content', 'create', ['locationId' => $parentLocation->id]); - } - - // Check if the parent is a sub location of one of the existing content locations (this also solves the - // situation where parent location actually one of the content locations), - // or if the content already has location below given location create struct parent - $existingContentLocations = $this->loadLocations($contentInfo); - if (!empty($existingContentLocations)) { - foreach ($existingContentLocations as $existingContentLocation) { - if (stripos($parentLocation->pathString, $existingContentLocation->pathString) !== false) { - throw new InvalidArgumentException( - '$locationCreateStruct', - 'Specified parent is a descendant of one of the existing Locations of this content.' - ); - } - if ($parentLocation->id == $existingContentLocation->parentLocationId) { - throw new InvalidArgumentException( - '$locationCreateStruct', - 'Content is already below the specified parent.' - ); - } - } - } - - $spiLocationCreateStruct = $this->contentDomainMapper->buildSPILocationCreateStruct( - $locationCreateStruct, - $parentLocation, - $contentInfo->mainLocationId ?? true, - $contentInfo->id, - $contentInfo->currentVersionNo, - $contentInfo->isHidden - ); - - $this->repository->beginTransaction(); - try { - $newLocation = $this->persistenceHandler->locationHandler()->create($spiLocationCreateStruct); - $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content); - foreach ($urlAliasNames as $languageCode => $name) { - $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation( - $newLocation->id, - $newLocation->parentId, - $name, - $languageCode, - $contentInfo->alwaysAvailable, - // @todo: this is legacy storage specific for updating ezcontentobject_tree.path_identification_string, to be removed - $languageCode === $contentInfo->mainLanguageCode - ); - } - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->contentDomainMapper->buildLocationWithContent($newLocation, $content); - } - - /** - * Updates $location in the content repository. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the remoteId exists already - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user is not allowed to update this location - */ - public function updateLocation(APILocation $location, LocationUpdateStruct $locationUpdateStruct): APILocation - { - if (!$this->contentDomainMapper->isValidLocationPriority($locationUpdateStruct->priority)) { - throw new InvalidArgumentValue('priority', $locationUpdateStruct->priority, 'LocationUpdateStruct'); - } - - if ($locationUpdateStruct->remoteId !== null && (!is_string($locationUpdateStruct->remoteId) || empty($locationUpdateStruct->remoteId))) { - throw new InvalidArgumentValue('remoteId', $locationUpdateStruct->remoteId, 'LocationUpdateStruct'); - } - - if ($locationUpdateStruct->sortField !== null && !$this->contentDomainMapper->isValidLocationSortField($locationUpdateStruct->sortField)) { - throw new InvalidArgumentValue('sortField', $locationUpdateStruct->sortField, 'LocationUpdateStruct'); - } - - if ($locationUpdateStruct->sortOrder !== null && !$this->contentDomainMapper->isValidLocationSortOrder($locationUpdateStruct->sortOrder)) { - throw new InvalidArgumentValue('sortOrder', $locationUpdateStruct->sortOrder, 'LocationUpdateStruct'); - } - - $loadedLocation = $this->loadLocation($location->id); - - if ($locationUpdateStruct->remoteId !== null) { - try { - $existingLocation = $this->loadLocationByRemoteId($locationUpdateStruct->remoteId); - if ($existingLocation !== null && $existingLocation->id !== $loadedLocation->id) { - throw new InvalidArgumentException('locationUpdateStruct', 'Location with the provided remote ID already exists'); - } - } catch (APINotFoundException $e) { - } - } - - $locationTarget = (new DestinationLocationTarget($loadedLocation->id, $loadedLocation->contentInfo)); - if (!$this->permissionResolver->canUser( - 'content', - 'edit', - $loadedLocation->getContentInfo(), - [$loadedLocation, $locationTarget] - )) { - throw new UnauthorizedException('content', 'edit', ['locationId' => $loadedLocation->id]); - } - - $updateStruct = new UpdateStruct(); - $updateStruct->priority = $locationUpdateStruct->priority !== null ? $locationUpdateStruct->priority : $loadedLocation->priority; - $updateStruct->remoteId = $locationUpdateStruct->remoteId !== null ? trim($locationUpdateStruct->remoteId) : $loadedLocation->remoteId; - $updateStruct->sortField = $locationUpdateStruct->sortField !== null ? $locationUpdateStruct->sortField : $loadedLocation->sortField; - $updateStruct->sortOrder = $locationUpdateStruct->sortOrder !== null ? $locationUpdateStruct->sortOrder : $loadedLocation->sortOrder; - - $this->repository->beginTransaction(); - try { - $this->persistenceHandler->locationHandler()->update($updateStruct, $loadedLocation->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadLocation($loadedLocation->id); - } - - /** - * Swaps the contents held by $location1 and $location2. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to swap content - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location1 - * @param \eZ\Publish\API\Repository\Values\Content\Location $location2 - */ - public function swapLocation(APILocation $location1, APILocation $location2): void - { - $loadedLocation1 = $this->loadLocation($location1->id); - $loadedLocation2 = $this->loadLocation($location2->id); - $locationTarget1 = (new DestinationLocationTarget($loadedLocation1->id, $loadedLocation1->contentInfo)); - $locationTarget2 = (new DestinationLocationTarget($loadedLocation2->id, $loadedLocation2->contentInfo)); - - if (!$this->permissionResolver->canUser( - 'content', - 'edit', - $loadedLocation1->getContentInfo(), - [$loadedLocation1, $locationTarget1] - )) { - throw new UnauthorizedException('content', 'edit', ['locationId' => $loadedLocation1->id]); - } - - if (!$this->permissionResolver->canUser( - 'content', - 'edit', - $loadedLocation2->getContentInfo(), - [$loadedLocation2, $locationTarget2] - )) { - throw new UnauthorizedException('content', 'edit', ['locationId' => $loadedLocation2->id]); - } - - $this->repository->beginTransaction(); - try { - $this->persistenceHandler->locationHandler()->swap($loadedLocation1->id, $loadedLocation2->id); - $this->persistenceHandler->urlAliasHandler()->locationSwapped( - $location1->id, - $location1->parentLocationId, - $location2->id, - $location2->parentLocationId - ); - $this->persistenceHandler->bookmarkHandler()->locationSwapped($loadedLocation1->id, $loadedLocation2->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Hides the $location and marks invisible all descendants of $location. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to hide this location - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return \eZ\Publish\API\Repository\Values\Content\Location $location, with updated hidden value - */ - public function hideLocation(APILocation $location): APILocation - { - $locationTarget = (new DestinationLocationTarget($location->id, $location->contentInfo)); - if (!$this->permissionResolver->canUser( - 'content', - 'hide', - $location->getContentInfo(), - [$location, $locationTarget] - )) { - throw new UnauthorizedException('content', 'hide', ['locationId' => $location->id]); - } - - $this->repository->beginTransaction(); - try { - $this->persistenceHandler->locationHandler()->hide($location->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadLocation($location->id); - } - - /** - * Unhides the $location. - * - * This method and marks visible all descendants of $locations - * until a hidden location is found. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to unhide this location - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return \eZ\Publish\API\Repository\Values\Content\Location $location, with updated hidden value - */ - public function unhideLocation(APILocation $location): APILocation - { - $locationTarget = (new DestinationLocationTarget($location->id, $location->contentInfo)); - if (!$this->permissionResolver->canUser( - 'content', - 'hide', - $location->getContentInfo(), - [$location, $locationTarget] - )) { - throw new UnauthorizedException('content', 'hide', ['locationId' => $location->id]); - } - - $this->repository->beginTransaction(); - try { - $this->persistenceHandler->locationHandler()->unHide($location->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadLocation($location->id); - } - - /** - * {@inheritdoc} - */ - public function moveSubtree(APILocation $location, APILocation $newParentLocation): void - { - $location = $this->loadLocation($location->id); - $newParentLocation = $this->loadLocation($newParentLocation->id); - - if ($newParentLocation->id === $location->parentLocationId) { - throw new InvalidArgumentException( - '$newParentLocation', - 'new parent Location is the same as the current one' - ); - } - if (strpos($newParentLocation->pathString, $location->pathString) === 0) { - throw new InvalidArgumentException( - '$newParentLocation', - 'new parent Location is a descendant of the given $location' - ); - } - $contentTypeId = $newParentLocation->contentInfo->contentTypeId; - if (!$this->contentTypeService->loadContentType($contentTypeId)->isContainer) { - throw new InvalidArgumentException( - '$newParentLocation', - 'Cannot move Location to a parent that is not a container' - ); - } - - $this->checkCreatePermissionOnSubtreeTarget($newParentLocation, $location, $newParentLocation); - - $this->repository->beginTransaction(); - try { - $this->persistenceHandler->locationHandler()->move($location->id, $newParentLocation->id); - - $content = $this->repository->getContentService()->loadContent($location->contentId); - $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content); - foreach ($urlAliasNames as $languageCode => $name) { - $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation( - $location->id, - $newParentLocation->id, - $name, - $languageCode, - $content->contentInfo->alwaysAvailable - ); - } - - $this->persistenceHandler->urlAliasHandler()->locationMoved( - $location->id, - $location->parentLocationId, - $newParentLocation->id - ); - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Deletes $location and all its descendants. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user is not allowed to delete this location or a descendant - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - */ - public function deleteLocation(APILocation $location): void - { - $location = $this->loadLocation($location->id); - $contentInfo = $location->contentInfo; - $versionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo( - $contentInfo->id, - $contentInfo->currentVersionNo - ); - $translations = $versionInfo->languageCodes; - $target = (new Target\Version())->deleteTranslations($translations); - - if (!$this->permissionResolver->canUser('content', 'manage_locations', $location->getContentInfo())) { - throw new UnauthorizedException('content', 'manage_locations', ['locationId' => $location->id]); - } - if (!$this->permissionResolver->canUser('content', 'remove', $location->getContentInfo(), [$location, $target])) { - throw new UnauthorizedException('content', 'remove', ['locationId' => $location->id]); - } - - // Check remove access to descendants - $contentReadCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'remove'); - if ($contentReadCriterion === false) { - throw new UnauthorizedException('content', 'remove'); - } elseif ($contentReadCriterion !== true) { - // Query if there are any content in subtree current user don't have access to - $query = new Query( - [ - 'limit' => 0, - 'filter' => new CriterionLogicalAnd( - [ - new CriterionSubtree($location->pathString), - new CriterionLogicalNot($contentReadCriterion), - ] - ), - ] - ); - $result = $this->repository->getSearchService()->findContent($query, [], false); - if ($result->totalCount > 0) { - throw new UnauthorizedException('content', 'remove'); - } - } - - $this->repository->beginTransaction(); - try { - $this->persistenceHandler->locationHandler()->removeSubtree($location->id); - $this->persistenceHandler->urlAliasHandler()->locationDeleted($location->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Instantiates a new location create class. - * - * @param mixed $parentLocationId the parent under which the new location should be created - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType|null $contentType - * - * @return \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct - */ - public function newLocationCreateStruct($parentLocationId, ContentType $contentType = null): LocationCreateStruct - { - $properties = [ - 'parentLocationId' => $parentLocationId, - ]; - if ($contentType) { - $properties['sortField'] = $contentType->defaultSortField; - $properties['sortOrder'] = $contentType->defaultSortOrder; - } - - return new LocationCreateStruct($properties); - } - - /** - * Instantiates a new location update class. - * - * @return \eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct - */ - public function newLocationUpdateStruct(): LocationUpdateStruct - { - return new LocationUpdateStruct(); - } - - /** - * Get the total number of all existing Locations. Can be combined with loadAllLocations. - * - * @see loadAllLocations - * - * @return int Total number of Locations - */ - public function getAllLocationsCount(): int - { - return $this->persistenceHandler->locationHandler()->countAllLocations(); - } - - /** - * Bulk-load all existing Locations, constrained by $limit and $offset to paginate results. - * - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\API\Repository\Values\Content\Location[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function loadAllLocations(int $offset = 0, int $limit = 25): array - { - $spiLocations = $this->persistenceHandler->locationHandler()->loadAllLocations( - $offset, - $limit - ); - $contentIds = array_unique( - array_map( - static function (SPILocation $spiLocation) { - return $spiLocation->contentId; - }, - $spiLocations - ) - ); - - $permissionResolver = $this->repository->getPermissionResolver(); - $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList( - $contentIds - ); - $contentList = $this->contentDomainMapper->buildContentProxyList( - $spiContentInfoList, - Language::ALL, - false - ); - $locations = []; - foreach ($spiLocations as $spiLocation) { - if (!isset($spiContentInfoList[$spiLocation->contentId], $contentList[$spiLocation->contentId])) { - $this->logger->warning( - sprintf( - 'Location %d has missing content %d', - $spiLocation->id, - $spiLocation->contentId - ) - ); - continue; - } - - $location = $this->contentDomainMapper->buildLocationWithContent( - $spiLocation, - $contentList[$spiLocation->contentId], - $spiContentInfoList[$spiLocation->contentId] - ); - - $contentInfo = $location->getContentInfo(); - if (!$permissionResolver->canUser('content', 'read', $contentInfo, [$location])) { - continue; - } - $locations[] = $location; - } - - return $locations; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function find(Filter $filter, ?array $languages = null): LocationList - { - $filter = clone $filter; - if (!empty($languages)) { - $filter->andWithCriterion(new LanguageCode($languages)); - } - - $permissionCriterion = $this->permissionCriterionResolver->getQueryPermissionsCriterion(); - if ($permissionCriterion instanceof Criterion\MatchNone) { - return new LocationList(); - } - - if (!$permissionCriterion instanceof Criterion\MatchAll) { - if (!$permissionCriterion instanceof FilteringCriterion) { - return new LocationList(); - } - $filter->andWithCriterion($permissionCriterion); - } - - $locations = []; - $locationListIterator = $this->locationFilteringHandler->find($filter); - foreach ($locationListIterator as $locationWithContentInfo) { - $spiContentInfo = $locationWithContentInfo->getContentInfo(); - $locations[] = $this->contentDomainMapper->buildLocationWithContent( - $locationWithContentInfo->getLocation(), - $this->contentDomainMapper->buildContentProxy($spiContentInfo), - $spiContentInfo, - ); - } - - return new LocationList( - [ - 'totalCount' => $locationListIterator->getTotalCount(), - 'locations' => $locations, - ] - ); - } - - public function count(Filter $filter, ?array $languages = null): int - { - $filter = clone $filter; - if (!empty($languages)) { - $filter->andWithCriterion(new LanguageCode($languages)); - } - - $permissionCriterion = $this->permissionCriterionResolver->getQueryPermissionsCriterion(); - if ($permissionCriterion instanceof Criterion\MatchNone) { - return 0; - } - - if (!$permissionCriterion instanceof Criterion\MatchAll) { - if (!$permissionCriterion instanceof FilteringCriterion) { - return 0; - } - - $filter->andWithCriterion($permissionCriterion); - } - - return $this->locationFilteringHandler->count($filter); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - private function checkCreatePermissionOnSubtreeTarget( - APILocation $targetParentLocation, - APILocation $loadedSubtree, - APILocation $loadedTargetLocation - ): void { - $locationTarget = (new DestinationLocationTarget($targetParentLocation->id, $loadedSubtree->contentInfo)); - if (!$this->permissionResolver->canUser( - 'content', - 'create', - $loadedSubtree->getContentInfo(), - [$loadedTargetLocation, $locationTarget] - )) { - throw new UnauthorizedException( - 'content', - 'create', - ['locationId' => $loadedTargetLocation->id] - ); - } - - // Check read access to whole source subtree - $contentReadCriterion = $this->permissionCriterionResolver->getPermissionsCriterion( - 'content', - 'read' - ); - if ($contentReadCriterion === false) { - throw new UnauthorizedException('content', 'read'); - } - - if ($contentReadCriterion !== true) { - // Query if there are any content in subtree current user don't have access to - $query = new Query( - [ - 'limit' => 0, - 'filter' => new CriterionLogicalAnd( - [ - new CriterionSubtree($loadedSubtree->getPathString()), - new CriterionLogicalNot($contentReadCriterion), - // Do not take the same content into consideration as it can have more than one location - new CriterionLogicalNot(new Criterion\ContentId($loadedSubtree->getContentInfo()->getId())), - ] - ), - ] - ); - $result = $this->repository->getSearchService()->findContent($query, [], false); - if ($result->totalCount > 0) { - throw new UnauthorizedException('content', 'read'); - } - } - } -} diff --git a/eZ/Publish/Core/Repository/Mapper/ProxyAwareDomainMapper.php b/eZ/Publish/Core/Repository/Mapper/ProxyAwareDomainMapper.php deleted file mode 100644 index 8c5602233e..0000000000 --- a/eZ/Publish/Core/Repository/Mapper/ProxyAwareDomainMapper.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Mapper; - -use eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperInterface; - -/** - * @internal For internal use by Domain Mappers - * - * Common abstraction for domain mappers providing properties loaded via proxy. - */ -abstract class ProxyAwareDomainMapper -{ - /** @var \eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperInterface */ - protected $proxyFactory; - - public function __construct(?ProxyDomainMapperInterface $proxyFactory = null) - { - $this->proxyFactory = $proxyFactory; - } - - /** - * Setter for Proxy Factory to work around cyclic dependency issue on Repository. - * - * Note: to be resolved by Repository decoupling. - */ - final public function setProxyFactory(ProxyDomainMapperInterface $proxyFactory): void - { - $this->proxyFactory = $proxyFactory; - } -} diff --git a/eZ/Publish/Core/Repository/Mapper/RoleDomainMapper.php b/eZ/Publish/Core/Repository/Mapper/RoleDomainMapper.php deleted file mode 100644 index fe00430bce..0000000000 --- a/eZ/Publish/Core/Repository/Mapper/RoleDomainMapper.php +++ /dev/null @@ -1,251 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Mapper; - -use eZ\Publish\API\Repository\Values\User\Role as APIRole; -use eZ\Publish\API\Repository\Values\User\RoleCopyStruct as APIRoleCopyStruct; -use eZ\Publish\API\Repository\Values\User\RoleCreateStruct as APIRoleCreateStruct; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\Core\Repository\Permission\LimitationService; -use eZ\Publish\Core\Repository\Values\User\Policy; -use eZ\Publish\Core\Repository\Values\User\PolicyDraft; -use eZ\Publish\Core\Repository\Values\User\Role; -use eZ\Publish\Core\Repository\Values\User\RoleDraft; -use eZ\Publish\Core\Repository\Values\User\UserGroupRoleAssignment; -use eZ\Publish\Core\Repository\Values\User\UserRoleAssignment; -use eZ\Publish\SPI\Persistence\User\Policy as SPIPolicy; -use eZ\Publish\SPI\Persistence\User\Role as SPIRole; -use eZ\Publish\SPI\Persistence\User\RoleAssignment as SPIRoleAssignment; -use eZ\Publish\SPI\Persistence\User\RoleCopyStruct as SPIRoleCopyStruct; -use eZ\Publish\SPI\Persistence\User\RoleCreateStruct as SPIRoleCreateStruct; - -/** - * Internal service to map Role objects between API and SPI values. - * - * @internal Meant for internal use by Repository. - */ -class RoleDomainMapper -{ - /** @var \eZ\Publish\Core\Repository\Permission\LimitationService */ - protected $limitationService; - - /** - * @param \eZ\Publish\Core\Repository\Permission\LimitationService $limitationService - */ - public function __construct(LimitationService $limitationService) - { - $this->limitationService = $limitationService; - } - - /** - * Maps provided SPI Role value object to API Role value object. - * - * @param \eZ\Publish\SPI\Persistence\User\Role $role - * - * @return \eZ\Publish\API\Repository\Values\User\Role - */ - public function buildDomainRoleObject(SPIRole $role) - { - $rolePolicies = []; - foreach ($role->policies as $spiPolicy) { - $rolePolicies[] = $this->buildDomainPolicyObject($spiPolicy); - } - - return new Role( - [ - 'id' => $role->id, - 'identifier' => $role->identifier, - 'status' => $role->status, - 'policies' => $rolePolicies, - ] - ); - } - - /** - * Builds a RoleDraft domain object from value object returned by persistence - * Decorates Role. - * - * @param \eZ\Publish\SPI\Persistence\User\Role $spiRole - * - * @return \eZ\Publish\API\Repository\Values\User\RoleDraft - */ - public function buildDomainRoleDraftObject(SPIRole $spiRole) - { - return new RoleDraft( - [ - 'innerRole' => $this->buildDomainRoleObject($spiRole), - ] - ); - } - - /** - * Maps provided SPI Policy value object to API Policy value object. - * - * @param \eZ\Publish\SPI\Persistence\User\Policy $spiPolicy - * - * @return \eZ\Publish\API\Repository\Values\User\Policy|\eZ\Publish\API\Repository\Values\User\PolicyDraft - */ - public function buildDomainPolicyObject(SPIPolicy $spiPolicy) - { - $policyLimitations = []; - if ($spiPolicy->module !== '*' && $spiPolicy->function !== '*' && $spiPolicy->limitations !== '*') { - foreach ($spiPolicy->limitations as $identifier => $values) { - $policyLimitations[] = $this->limitationService->getLimitationType($identifier)->buildValue($values); - } - } - - $policy = new Policy( - [ - 'id' => $spiPolicy->id, - 'roleId' => $spiPolicy->roleId, - 'module' => $spiPolicy->module, - 'function' => $spiPolicy->function, - 'limitations' => $policyLimitations, - ] - ); - - // Original ID is set on SPI policy, which means that it's a draft. - if ($spiPolicy->originalId) { - $policy = new PolicyDraft(['innerPolicy' => $policy, 'originalId' => $spiPolicy->originalId]); - } - - return $policy; - } - - /** - * Builds the API UserRoleAssignment object from provided SPI RoleAssignment object. - * - * @param \eZ\Publish\SPI\Persistence\User\RoleAssignment $spiRoleAssignment - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param \eZ\Publish\API\Repository\Values\User\Role $role - * - * @return \eZ\Publish\API\Repository\Values\User\UserRoleAssignment - */ - public function buildDomainUserRoleAssignmentObject(SPIRoleAssignment $spiRoleAssignment, User $user, APIRole $role) - { - $limitation = null; - if (!empty($spiRoleAssignment->limitationIdentifier)) { - $limitation = $this - ->limitationService - ->getLimitationType($spiRoleAssignment->limitationIdentifier) - ->buildValue($spiRoleAssignment->values); - } - - return new UserRoleAssignment( - [ - 'id' => $spiRoleAssignment->id, - 'limitation' => $limitation, - 'role' => $role, - 'user' => $user, - ] - ); - } - - /** - * Builds the API UserGroupRoleAssignment object from provided SPI RoleAssignment object. - * - * @param \eZ\Publish\SPI\Persistence\User\RoleAssignment $spiRoleAssignment - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * @param \eZ\Publish\API\Repository\Values\User\Role $role - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroupRoleAssignment - */ - public function buildDomainUserGroupRoleAssignmentObject(SPIRoleAssignment $spiRoleAssignment, UserGroup $userGroup, APIRole $role) - { - $limitation = null; - if (!empty($spiRoleAssignment->limitationIdentifier)) { - $limitation = $this - ->limitationService - ->getLimitationType($spiRoleAssignment->limitationIdentifier) - ->buildValue($spiRoleAssignment->values); - } - - return new UserGroupRoleAssignment( - [ - 'id' => $spiRoleAssignment->id, - 'limitation' => $limitation, - 'role' => $role, - 'userGroup' => $userGroup, - ] - ); - } - - /** - * Creates SPI Role create struct from provided API role create struct. - */ - public function buildPersistenceRoleCreateStruct(APIRoleCreateStruct $roleCreateStruct): SPIRoleCreateStruct - { - $policiesToCreate = $this->fillRoleStructWithPolicies($roleCreateStruct); - - return new SPIRoleCreateStruct( - [ - 'identifier' => $roleCreateStruct->identifier, - 'policies' => $policiesToCreate, - ] - ); - } - - /** - * Creates SPI Role copy struct from provided API role copy struct. - */ - public function buildPersistenceRoleCopyStruct(APIRoleCopyStruct $roleCopyStruct, int $clonedId, int $status): SPIRoleCopyStruct - { - $policiesToCopy = $this->fillRoleStructWithPolicies($roleCopyStruct); - - return new SPIRoleCopyStruct( - [ - 'clonedId' => $clonedId, - 'newIdentifier' => $roleCopyStruct->newIdentifier, - 'status' => $status, - 'policies' => $policiesToCopy, - ] - ); - } - - protected function fillRoleStructWithPolicies(APIRoleCreateStruct $struct): array - { - $policies = []; - foreach ($struct->getPolicies() as $policyStruct) { - $policies[] = $this->buildPersistencePolicyObject( - $policyStruct->module, - $policyStruct->function, - $policyStruct->getLimitations() - ); - } - - return $policies; - } - - /** - * Creates SPI Policy value object from provided module, function and limitations. - * - * @param string $module - * @param string $function - * @param \eZ\Publish\API\Repository\Values\User\Limitation[] $limitations - * - * @return \eZ\Publish\SPI\Persistence\User\Policy - */ - public function buildPersistencePolicyObject($module, $function, array $limitations) - { - $limitationsToCreate = '*'; - if ($module !== '*' && $function !== '*' && !empty($limitations)) { - $limitationsToCreate = []; - foreach ($limitations as $limitation) { - $limitationsToCreate[$limitation->getIdentifier()] = $limitation->limitationValues; - } - } - - return new SPIPolicy( - [ - 'module' => $module, - 'function' => $function, - 'limitations' => $limitationsToCreate, - ] - ); - } -} diff --git a/eZ/Publish/Core/Repository/NotificationService.php b/eZ/Publish/Core/Repository/NotificationService.php deleted file mode 100644 index 4cda2f9dee..0000000000 --- a/eZ/Publish/Core/Repository/NotificationService.php +++ /dev/null @@ -1,181 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use DateTime; -use eZ\Publish\API\Repository\NotificationService as NotificationServiceInterface; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Values\Notification\CreateStruct as APICreateStruct; -use eZ\Publish\API\Repository\Values\Notification\Notification as APINotification; -use eZ\Publish\API\Repository\Values\Notification\NotificationList; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\SPI\Persistence\Notification\CreateStruct; -use eZ\Publish\SPI\Persistence\Notification\Handler; -use eZ\Publish\SPI\Persistence\Notification\Notification; -use eZ\Publish\SPI\Persistence\Notification\UpdateStruct; - -class NotificationService implements NotificationServiceInterface -{ - /** @var \eZ\Publish\SPI\Persistence\Notification\Handler */ - protected $persistenceHandler; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - protected $permissionResolver; - - /** - * @param \eZ\Publish\SPI\Persistence\Notification\Handler $persistenceHandler - * @param \eZ\Publish\API\Repository\PermissionResolver $permissionResolver - */ - public function __construct(Handler $persistenceHandler, PermissionResolver $permissionResolver) - { - $this->persistenceHandler = $persistenceHandler; - $this->permissionResolver = $permissionResolver; - } - - /** - * {@inheritdoc} - */ - public function loadNotifications(int $offset = 0, int $limit = 25): NotificationList - { - $currentUserId = $this->getCurrentUserId(); - - $list = new NotificationList(); - $list->totalCount = $this->persistenceHandler->countNotifications($currentUserId); - if ($list->totalCount > 0) { - $list->items = array_map(function (Notification $spiNotification) { - return $this->buildDomainObject($spiNotification); - }, $this->persistenceHandler->loadUserNotifications($currentUserId, $offset, $limit)); - } - - return $list; - } - - /** - * {@inheritdoc} - */ - public function createNotification(APICreateStruct $createStruct): APINotification - { - $spiCreateStruct = new CreateStruct(); - - if (empty($createStruct->ownerId)) { - throw new InvalidArgumentException('ownerId', $createStruct->ownerId); - } - - $spiCreateStruct->ownerId = $createStruct->ownerId; - - if (empty($createStruct->type)) { - throw new InvalidArgumentException('type', $createStruct->type); - } - - $spiCreateStruct->type = $createStruct->type; - $spiCreateStruct->isPending = (bool) $createStruct->isPending; - $spiCreateStruct->data = $createStruct->data; - $spiCreateStruct->created = (new DateTime())->getTimestamp(); - - return $this->buildDomainObject( - $this->persistenceHandler->createNotification($spiCreateStruct) - ); - } - - /** - * {@inheritdoc} - */ - public function getNotification(int $notificationId): APINotification - { - $notification = $this->persistenceHandler->getNotificationById($notificationId); - - $currentUserId = $this->getCurrentUserId(); - if (!$notification->ownerId || $currentUserId != $notification->ownerId) { - throw new NotFoundException('Notification', $notificationId); - } - - return $this->buildDomainObject($notification); - } - - /** - * {@inheritdoc} - */ - public function markNotificationAsRead(APINotification $notification): void - { - $currentUserId = $this->getCurrentUserId(); - - if (!$notification->id) { - throw new NotFoundException('Notification', $notification->id); - } - - if ($notification->ownerId !== $currentUserId) { - throw new UnauthorizedException($notification->id, 'Notification'); - } - - if (!$notification->isPending) { - return; - } - - $updateStruct = new UpdateStruct(); - $updateStruct->isPending = false; - - $this->persistenceHandler->updateNotification($notification, $updateStruct); - } - - /** - * {@inheritdoc} - */ - public function getPendingNotificationCount(): int - { - return $this->persistenceHandler->countPendingNotifications( - $this->getCurrentUserId() - ); - } - - /** - * {@inheritdoc} - */ - public function getNotificationCount(): int - { - return $this->persistenceHandler->countNotifications( - $this->getCurrentUserId() - ); - } - - /** - * {@inheritdoc} - */ - public function deleteNotification(APINotification $notification): void - { - $this->persistenceHandler->delete($notification); - } - - /** - * Builds Notification domain object from ValueObject returned by Persistence API. - * - * @param \eZ\Publish\SPI\Persistence\Notification\Notification $spiNotification - * - * @return \eZ\Publish\API\Repository\Values\Notification\Notification - */ - protected function buildDomainObject(Notification $spiNotification): APINotification - { - return new APINotification([ - 'id' => $spiNotification->id, - 'ownerId' => $spiNotification->ownerId, - 'isPending' => $spiNotification->isPending, - 'type' => $spiNotification->type, - 'created' => new DateTime("@{$spiNotification->created}"), - 'data' => $spiNotification->data, - ]); - } - - private function getCurrentUserId(): int - { - return $this->permissionResolver - ->getCurrentUserReference() - ->getUserId(); - } -} diff --git a/eZ/Publish/Core/Repository/ObjectStateService.php b/eZ/Publish/Core/Repository/ObjectStateService.php deleted file mode 100644 index 9d16add23d..0000000000 --- a/eZ/Publish/Core/Repository/ObjectStateService.php +++ /dev/null @@ -1,837 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\ObjectStateService as ObjectStateServiceInterface; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectState as APIObjectState; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup as APIObjectStateGroup; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateUpdateStruct; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\Core\Repository\Values\ObjectState\ObjectState; -use eZ\Publish\Core\Repository\Values\ObjectState\ObjectStateGroup; -use eZ\Publish\SPI\Persistence\Content\ObjectState as SPIObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group as SPIObjectStateGroup; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Handler; -use eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct; - -/** - * ObjectStateService service. - * - * @example Examples/objectstates.php tbd. - */ -class ObjectStateService implements ObjectStateServiceInterface -{ - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\SPI\Persistence\Content\ObjectState\Handler */ - protected $objectStateHandler; - - /** @var array */ - protected $settings; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionResolver; - - /** - * Setups service with reference to repository object that created it & corresponding handler. - * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\Handler $objectStateHandler - * @param array $settings - */ - public function __construct( - RepositoryInterface $repository, - Handler $objectStateHandler, - PermissionResolver $permissionResolver, - array $settings = [] - ) { - $this->repository = $repository; - $this->objectStateHandler = $objectStateHandler; - $this->permissionResolver = $permissionResolver; - // Union makes sure default settings are ignored if provided in argument - $this->settings = $settings + [ - //'defaultSetting' => array(), - ]; - } - - /** - * Creates a new object state group. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create an object state group - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the object state group with provided identifier already exists - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupCreateStruct $objectStateGroupCreateStruct - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup - */ - public function createObjectStateGroup(ObjectStateGroupCreateStruct $objectStateGroupCreateStruct): APIObjectStateGroup - { - if (!$this->permissionResolver->canUser('state', 'administrate', $objectStateGroupCreateStruct)) { - throw new UnauthorizedException('state', 'administrate'); - } - - $inputStruct = $this->buildCreateInputStruct( - $objectStateGroupCreateStruct->identifier, - $objectStateGroupCreateStruct->defaultLanguageCode, - $objectStateGroupCreateStruct->names, - $objectStateGroupCreateStruct->descriptions - ); - - try { - $this->objectStateHandler->loadGroupByIdentifier($inputStruct->identifier); - throw new InvalidArgumentException( - 'objectStateGroupCreateStruct', - 'An Object state group with the provided identifier already exists' - ); - } catch (APINotFoundException $e) { - // Do nothing - } - - $this->repository->beginTransaction(); - try { - $spiObjectStateGroup = $this->objectStateHandler->createGroup($inputStruct); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->buildDomainObjectStateGroupObject($spiObjectStateGroup); - } - - /** - * {@inheritdoc} - */ - public function loadObjectStateGroup(int $objectStateGroupId, array $prioritizedLanguages = []): APIObjectStateGroup - { - $spiObjectStateGroup = $this->objectStateHandler->loadGroup($objectStateGroupId); - - return $this->buildDomainObjectStateGroupObject($spiObjectStateGroup, $prioritizedLanguages); - } - - public function loadObjectStateGroupByIdentifier( - string $objectStateGroupIdentifier, - array $prioritizedLanguages = [] - ): APIObjectStateGroup { - $spiObjectStateGroup = $this->objectStateHandler->loadGroupByIdentifier($objectStateGroupIdentifier); - - return $this->buildDomainObjectStateGroupObject($spiObjectStateGroup, $prioritizedLanguages); - } - - /** - * {@inheritdoc} - */ - public function loadObjectStateGroups(int $offset = 0, int $limit = -1, array $prioritizedLanguages = []): iterable - { - $spiObjectStateGroups = $this->objectStateHandler->loadAllGroups($offset, $limit); - - $objectStateGroups = []; - foreach ($spiObjectStateGroups as $spiObjectStateGroup) { - $objectStateGroups[] = $this->buildDomainObjectStateGroupObject( - $spiObjectStateGroup, - $prioritizedLanguages - ); - } - - return $objectStateGroups; - } - - /** - * This method returns the ordered list of object states of a group. - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup - * @param string[] $prioritizedLanguages - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectState[] - */ - public function loadObjectStates( - APIObjectStateGroup $objectStateGroup, - array $prioritizedLanguages = [] - ): iterable { - $spiObjectStates = $this->objectStateHandler->loadObjectStates($objectStateGroup->id); - - $objectStates = []; - foreach ($spiObjectStates as $spiObjectState) { - $objectStates[] = $this->buildDomainObjectStateObject( - $spiObjectState, - $objectStateGroup, - $prioritizedLanguages - ); - } - - return $objectStates; - } - - /** - * Updates an object state group. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update an object state group - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the object state group with provided identifier already exists - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct $objectStateGroupUpdateStruct - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup - */ - public function updateObjectStateGroup(APIObjectStateGroup $objectStateGroup, ObjectStateGroupUpdateStruct $objectStateGroupUpdateStruct): APIObjectStateGroup - { - if (!$this->permissionResolver->canUser('state', 'administrate', $objectStateGroup)) { - throw new UnauthorizedException('state', 'administrate'); - } - - $loadedObjectStateGroup = $this->loadObjectStateGroup($objectStateGroup->id); - - $inputStruct = $this->buildObjectStateGroupUpdateInputStruct( - $loadedObjectStateGroup, - $objectStateGroupUpdateStruct->identifier, - $objectStateGroupUpdateStruct->defaultLanguageCode, - $objectStateGroupUpdateStruct->names, - $objectStateGroupUpdateStruct->descriptions - ); - - if ($objectStateGroupUpdateStruct->identifier !== null) { - try { - $existingObjectStateGroup = $this->objectStateHandler->loadGroupByIdentifier($inputStruct->identifier); - if ($existingObjectStateGroup->id != $loadedObjectStateGroup->id) { - throw new InvalidArgumentException( - 'objectStateGroupUpdateStruct', - 'An Object state group with the provided identifier already exists' - ); - } - } catch (APINotFoundException $e) { - // Do nothing - } - } - - $this->repository->beginTransaction(); - try { - $spiObjectStateGroup = $this->objectStateHandler->updateGroup( - $loadedObjectStateGroup->id, - $inputStruct - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->buildDomainObjectStateGroupObject($spiObjectStateGroup, $objectStateGroup->prioritizedLanguages); - } - - /** - * Deletes a object state group including all states and links to content. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete an object state group - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup - */ - public function deleteObjectStateGroup(APIObjectStateGroup $objectStateGroup): void - { - if (!$this->permissionResolver->canUser('state', 'administrate', $objectStateGroup)) { - throw new UnauthorizedException('state', 'administrate'); - } - - $loadedObjectStateGroup = $this->loadObjectStateGroup($objectStateGroup->id); - - $this->repository->beginTransaction(); - try { - $this->objectStateHandler->deleteGroup($loadedObjectStateGroup->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Creates a new object state in the given group. - * - * Note: in current kernel: If it is the first state all content objects will - * set to this state. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create an object state - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the object state with provided identifier already exists in the same group - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateCreateStruct $objectStateCreateStruct - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectState - */ - public function createObjectState(APIObjectStateGroup $objectStateGroup, ObjectStateCreateStruct $objectStateCreateStruct): APIObjectState - { - if (!$this->permissionResolver->canUser('state', 'administrate', $objectStateCreateStruct, [$objectStateGroup])) { - throw new UnauthorizedException('state', 'administrate'); - } - - $inputStruct = $this->buildCreateInputStruct( - $objectStateCreateStruct->identifier, - $objectStateCreateStruct->defaultLanguageCode, - $objectStateCreateStruct->names, - $objectStateCreateStruct->descriptions - ); - - try { - $this->objectStateHandler->loadByIdentifier($inputStruct->identifier, $objectStateGroup->id); - throw new InvalidArgumentException( - 'objectStateCreateStruct', - 'An Object state with the provided identifier already exists in the provided Object state group' - ); - } catch (APINotFoundException $e) { - // Do nothing - } - - $this->repository->beginTransaction(); - try { - $spiObjectState = $this->objectStateHandler->create($objectStateGroup->id, $inputStruct); - - if (is_int($objectStateCreateStruct->priority)) { - $this->objectStateHandler->setPriority( - $spiObjectState->id, - $objectStateCreateStruct->priority - ); - - // Reload the object state to have the updated priority, - // considering that priorities are always incremental within a group - $spiObjectState = $this->objectStateHandler->load($spiObjectState->id); - } - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->buildDomainObjectStateObject($spiObjectState); - } - - /** - * {@inheritdoc} - */ - public function loadObjectState(int $stateId, array $prioritizedLanguages = []): APIObjectState - { - $spiObjectState = $this->objectStateHandler->load($stateId); - - return $this->buildDomainObjectStateObject($spiObjectState, null, $prioritizedLanguages); - } - - public function loadObjectStateByIdentifier( - APIObjectStateGroup $objectStateGroup, - string $objectStateIdentifier, - array $prioritizedLanguages = [] - ): APIObjectState { - $spiObjectState = $this->objectStateHandler->loadByIdentifier( - $objectStateIdentifier, - $objectStateGroup->id - ); - - return $this->buildDomainObjectStateObject($spiObjectState, null, $prioritizedLanguages); - } - - /** - * Updates an object state. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to update an object state - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the object state with provided identifier already exists in the same group - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectState $objectState - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateUpdateStruct $objectStateUpdateStruct - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectState - */ - public function updateObjectState(APIObjectState $objectState, ObjectStateUpdateStruct $objectStateUpdateStruct): APIObjectState - { - if (!$this->permissionResolver->canUser('state', 'administrate', $objectState)) { - throw new UnauthorizedException('state', 'administrate'); - } - - $loadedObjectState = $this->loadObjectState($objectState->id); - - $inputStruct = $this->buildObjectStateUpdateInputStruct( - $loadedObjectState, - $objectStateUpdateStruct->identifier, - $objectStateUpdateStruct->defaultLanguageCode, - $objectStateUpdateStruct->names, - $objectStateUpdateStruct->descriptions - ); - - if ($objectStateUpdateStruct->identifier !== null) { - try { - $existingObjectState = $this->objectStateHandler->loadByIdentifier( - $inputStruct->identifier, - $loadedObjectState->getObjectStateGroup()->id - ); - - if ($existingObjectState->id != $loadedObjectState->id) { - throw new InvalidArgumentException( - 'objectStateUpdateStruct', - 'An Object state with the provided identifier already exists in provided Object state group' - ); - } - } catch (APINotFoundException $e) { - // Do nothing - } - } - - $this->repository->beginTransaction(); - try { - $spiObjectState = $this->objectStateHandler->update( - $loadedObjectState->id, - $inputStruct - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->buildDomainObjectStateObject($spiObjectState, null, $objectState->prioritizedLanguages); - } - - /** - * Changes the priority of the state. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to change priority on an object state - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectState $objectState - * @param int $priority - */ - public function setPriorityOfObjectState(APIObjectState $objectState, int $priority): void - { - if (!$this->permissionResolver->canUser('state', 'administrate', $objectState)) { - throw new UnauthorizedException('state', 'administrate'); - } - - $loadedObjectState = $this->loadObjectState($objectState->id); - - $this->repository->beginTransaction(); - try { - $this->objectStateHandler->setPriority( - $loadedObjectState->id, - $priority - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Deletes a object state. The state of the content objects is reset to the - * first object state in the group. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete an object state - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectState $objectState - */ - public function deleteObjectState(APIObjectState $objectState): void - { - if (!$this->permissionResolver->canUser('state', 'administrate', $objectState)) { - throw new UnauthorizedException('state', 'administrate'); - } - - $loadedObjectState = $this->loadObjectState($objectState->id); - - $this->repository->beginTransaction(); - try { - $this->objectStateHandler->delete($loadedObjectState->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Sets the object-state of a state group to $state for the given content. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the object state does not belong to the given group - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to change the object state - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectState $objectState - */ - public function setContentState(ContentInfo $contentInfo, APIObjectStateGroup $objectStateGroup, APIObjectState $objectState): void - { - if (!$this->permissionResolver->canUser('state', 'assign', $contentInfo, [$objectState])) { - throw new UnauthorizedException('state', 'assign', ['contentId' => $contentInfo->id]); - } - - $loadedObjectState = $this->loadObjectState($objectState->id); - - if ($loadedObjectState->getObjectStateGroup()->id != $objectStateGroup->id) { - throw new InvalidArgumentException('objectState', 'Object state does not belong to the given group'); - } - - $this->repository->beginTransaction(); - try { - $this->objectStateHandler->setContentState( - $contentInfo->id, - $objectStateGroup->id, - $loadedObjectState->id - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Gets the object-state of object identified by $contentId. - * - * The $state is the id of the state within one group. - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectState - */ - public function getContentState(ContentInfo $contentInfo, APIObjectStateGroup $objectStateGroup): APIObjectState - { - $spiObjectState = $this->objectStateHandler->getContentState( - $contentInfo->id, - $objectStateGroup->id - ); - - return $this->buildDomainObjectStateObject($spiObjectState, $objectStateGroup); - } - - /** - * Returns the number of objects which are in this state. - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectState $objectState - * - * @return int - */ - public function getContentCount(APIObjectState $objectState): int - { - return $this->objectStateHandler->getContentCount( - $objectState->id - ); - } - - /** - * Instantiates a new Object State Group Create Struct and sets $identified in it. - * - * @param string $identifier - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupCreateStruct - */ - public function newObjectStateGroupCreateStruct(string $identifier): ObjectStateGroupCreateStruct - { - $objectStateGroupCreateStruct = new ObjectStateGroupCreateStruct(); - $objectStateGroupCreateStruct->identifier = $identifier; - - return $objectStateGroupCreateStruct; - } - - /** - * Instantiates a new Object State Group Update Struct. - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct - */ - public function newObjectStateGroupUpdateStruct(): ObjectStateGroupUpdateStruct - { - return new ObjectStateGroupUpdateStruct(); - } - - /** - * Instantiates a new Object State Create Struct and sets $identifier in it. - * - * @param string $identifier - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateCreateStruct - */ - public function newObjectStateCreateStruct(string $identifier): ObjectStateCreateStruct - { - $objectStateCreateStruct = new ObjectStateCreateStruct(); - $objectStateCreateStruct->identifier = $identifier; - - return $objectStateCreateStruct; - } - - /** - * Instantiates a new Object State Update Struct. - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateUpdateStruct - */ - public function newObjectStateUpdateStruct(): ObjectStateUpdateStruct - { - return new ObjectStateUpdateStruct(); - } - - /** - * Converts the object state SPI value object to API value object. - * - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState $spiObjectState - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup - * @param string[] $prioritizedLanguages - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectState - */ - protected function buildDomainObjectStateObject( - SPIObjectState $spiObjectState, - APIObjectStateGroup $objectStateGroup = null, - array $prioritizedLanguages = [] - ): APIObjectState { - $objectStateGroup = $objectStateGroup ?: $this->loadObjectStateGroup($spiObjectState->groupId, $prioritizedLanguages); - - return new ObjectState( - [ - 'id' => $spiObjectState->id, - 'identifier' => $spiObjectState->identifier, - 'priority' => $spiObjectState->priority, - 'mainLanguageCode' => $spiObjectState->defaultLanguage, - 'languageCodes' => $spiObjectState->languageCodes, - 'names' => $spiObjectState->name, - 'descriptions' => $spiObjectState->description, - 'objectStateGroup' => $objectStateGroup, - 'prioritizedLanguages' => $prioritizedLanguages, - ] - ); - } - - /** - * Converts the object state group SPI value object to API value object. - * - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\Group $spiObjectStateGroup - * @param array $prioritizedLanguages - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup - */ - protected function buildDomainObjectStateGroupObject( - SPIObjectStateGroup $spiObjectStateGroup, - array $prioritizedLanguages = [] - ): APIObjectStateGroup { - return new ObjectStateGroup( - [ - 'id' => $spiObjectStateGroup->id, - 'identifier' => $spiObjectStateGroup->identifier, - 'mainLanguageCode' => $spiObjectStateGroup->defaultLanguage, - 'languageCodes' => $spiObjectStateGroup->languageCodes, - 'names' => $spiObjectStateGroup->name, - 'descriptions' => $spiObjectStateGroup->description, - 'prioritizedLanguages' => $prioritizedLanguages, - ] - ); - } - - /** - * Validates input for creating object states/groups and builds the InputStruct object. - * - * @param string $identifier - * @param string $defaultLanguageCode - * @param string[] $names - * @param string[]|null $descriptions - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct - */ - protected function buildCreateInputStruct( - string $identifier, - string $defaultLanguageCode, - array $names, - ?array $descriptions - ): InputStruct { - if (!is_string($identifier) || empty($identifier)) { - throw new InvalidArgumentValue('identifier', $identifier); - } - - if (!is_string($defaultLanguageCode) || empty($defaultLanguageCode)) { - throw new InvalidArgumentValue('defaultLanguageCode', $defaultLanguageCode); - } - - if (!is_array($names) || empty($names)) { - throw new InvalidArgumentValue('names', $names); - } - - if (!isset($names[$defaultLanguageCode])) { - throw new InvalidArgumentValue('names', $names); - } - - foreach ($names as $languageCode => $name) { - try { - $this->repository->getContentLanguageService()->loadLanguage($languageCode); - } catch (NotFoundException $e) { - throw new InvalidArgumentValue('names', $names); - } - - if (!is_string($name) || empty($name)) { - throw new InvalidArgumentValue('names', $names); - } - } - - $descriptions = $descriptions !== null ? $descriptions : []; - - $inputStruct = new InputStruct(); - $inputStruct->identifier = $identifier; - $inputStruct->defaultLanguage = $defaultLanguageCode; - $inputStruct->name = $names; - - $inputStruct->description = []; - foreach ($names as $languageCode => $name) { - if (isset($descriptions[$languageCode]) && !empty($descriptions[$languageCode])) { - $inputStruct->description[$languageCode] = $descriptions[$languageCode]; - } else { - $inputStruct->description[$languageCode] = ''; - } - } - - return $inputStruct; - } - - /** - * Validates input for updating object states and builds the InputStruct object. - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectState $objectState - * @param string|null $identifier - * @param string|null $defaultLanguageCode - * @param string[]|null $names - * @param string[]|null $descriptions - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct - */ - protected function buildObjectStateUpdateInputStruct( - APIObjectState $objectState, - ?string $identifier, - ?string $defaultLanguageCode, - ?array $names, - ?array $descriptions - ): InputStruct { - $inputStruct = new InputStruct(); - - if ($identifier !== null && (!is_string($identifier) || empty($identifier))) { - throw new InvalidArgumentValue('identifier', $identifier); - } - - $inputStruct->identifier = $identifier !== null ? $identifier : $objectState->identifier; - - if ($defaultLanguageCode !== null && (!is_string($defaultLanguageCode) || empty($defaultLanguageCode))) { - throw new InvalidArgumentValue('defaultLanguageCode', $defaultLanguageCode); - } - - $inputStruct->defaultLanguage = $defaultLanguageCode !== null ? $defaultLanguageCode : $objectState->defaultLanguageCode; - - if ($names !== null && (!is_array($names) || empty($names))) { - throw new InvalidArgumentValue('names', $names); - } - - $inputStruct->name = $names !== null ? $names : $objectState->getNames(); - - if (!isset($inputStruct->name[$inputStruct->defaultLanguage])) { - throw new InvalidArgumentValue('names', $inputStruct->name); - } - - foreach ($inputStruct->name as $languageCode => $name) { - try { - $this->repository->getContentLanguageService()->loadLanguage($languageCode); - } catch (NotFoundException $e) { - throw new InvalidArgumentValue('names', $inputStruct->name); - } - - if (!is_string($name) || empty($name)) { - throw new InvalidArgumentValue('names', $inputStruct->name); - } - } - - $descriptions = $descriptions !== null ? $descriptions : $objectState->getDescriptions(); - $descriptions = $descriptions !== null ? $descriptions : []; - - $inputStruct->description = []; - foreach ($inputStruct->name as $languageCode => $name) { - if (isset($descriptions[$languageCode]) && !empty($descriptions[$languageCode])) { - $inputStruct->description[$languageCode] = $descriptions[$languageCode]; - } else { - $inputStruct->description[$languageCode] = ''; - } - } - - return $inputStruct; - } - - /** - * Validates input for updating object state groups and builds the InputStruct object. - * - * @param \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup - * @param string|null $identifier - * @param string|null $defaultLanguageCode - * @param string[]|null $names - * @param string[]|null $descriptions - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct - */ - protected function buildObjectStateGroupUpdateInputStruct( - APIObjectStateGroup $objectStateGroup, - ?string $identifier, - ?string $defaultLanguageCode, - ?array $names, - ?array $descriptions - ): InputStruct { - $inputStruct = new InputStruct(); - - if ($identifier !== null && empty($identifier)) { - throw new InvalidArgumentValue('identifier', $identifier); - } - - $inputStruct->identifier = $identifier !== null ? $identifier : $objectStateGroup->identifier; - - if ($defaultLanguageCode !== null && empty($defaultLanguageCode)) { - throw new InvalidArgumentValue('defaultLanguageCode', $defaultLanguageCode); - } - - $inputStruct->defaultLanguage = $defaultLanguageCode !== null ? $defaultLanguageCode : $objectStateGroup->defaultLanguageCode; - - if ($names !== null && empty($names)) { - throw new InvalidArgumentValue('names', $names); - } - - $inputStruct->name = $names !== null ? $names : $objectStateGroup->getNames(); - - if (!isset($inputStruct->name[$inputStruct->defaultLanguage])) { - throw new InvalidArgumentValue('names', $inputStruct->name); - } - - foreach ($inputStruct->name as $languageCode => $name) { - try { - $this->repository->getContentLanguageService()->loadLanguage($languageCode); - } catch (NotFoundException $e) { - throw new InvalidArgumentValue('names', $inputStruct->name); - } - - if (!is_string($name) || empty($name)) { - throw new InvalidArgumentValue('names', $inputStruct->name); - } - } - - $descriptions = $descriptions !== null ? $descriptions : $objectStateGroup->getDescriptions(); - $descriptions = $descriptions !== null ? $descriptions : []; - - $inputStruct->description = []; - foreach ($inputStruct->name as $languageCode => $name) { - if (isset($descriptions[$languageCode]) && !empty($descriptions[$languageCode])) { - $inputStruct->description[$languageCode] = $descriptions[$languageCode]; - } else { - $inputStruct->description[$languageCode] = ''; - } - } - - return $inputStruct; - } -} diff --git a/eZ/Publish/Core/Repository/Permission/LimitationService.php b/eZ/Publish/Core/Repository/Permission/LimitationService.php deleted file mode 100644 index e1081622bf..0000000000 --- a/eZ/Publish/Core/Repository/Permission/LimitationService.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Permission; - -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\NotFound\LimitationNotFoundException; -use eZ\Publish\SPI\Limitation\Type; -use Traversable; - -/** - * Internal service to deal with limitations and limitation types. - * - * @internal Meant for internal use by Repository. - */ -class LimitationService -{ - /** @var \eZ\Publish\SPI\Limitation\Type[] */ - private $limitationTypes; - - public function __construct(?Traversable $limitationTypes = null) - { - $this->limitationTypes = null !== $limitationTypes - ? iterator_to_array($limitationTypes) : - []; - } - - /** - * Returns the LimitationType registered with the given identifier. - * - * Returns the correct implementation of API Limitation value object - * based on provided identifier - * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFound\LimitationNotFoundException - */ - public function getLimitationType(string $identifier): Type - { - if (!isset($this->limitationTypes[$identifier])) { - throw new LimitationNotFoundException($identifier); - } - - return $this->limitationTypes[$identifier]; - } - - /** - * Validates an array of Limitations. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation[] $limitations - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[][] - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function validateLimitations(array $limitations): array - { - $allErrors = []; - foreach ($limitations as $limitation) { - $errors = $this->validateLimitation($limitation); - if (!empty($errors)) { - $allErrors[$limitation->getIdentifier()] = $errors; - } - } - - return $allErrors; - } - - /** - * Validates single Limitation. - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If the Role settings is in a bad state*@throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function validateLimitation(Limitation $limitation): array - { - $identifier = $limitation->getIdentifier(); - if (!isset($this->limitationTypes[$identifier])) { - throw new BadStateException( - '$identifier', - "limitationType[{$identifier}] is not configured" - ); - } - - $type = $this->limitationTypes[$identifier]; - - // This will throw if it does not pass - $type->acceptValue($limitation); - - // This return array of validation errors - return $type->validate($limitation); - } -} diff --git a/eZ/Publish/Core/Repository/Permission/PermissionCriterionResolver.php b/eZ/Publish/Core/Repository/Permission/PermissionCriterionResolver.php deleted file mode 100644 index bd9df2688f..0000000000 --- a/eZ/Publish/Core/Repository/Permission/PermissionCriterionResolver.php +++ /dev/null @@ -1,174 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Permission; - -use eZ\Publish\API\Repository\PermissionCriterionResolver as APIPermissionCriterionResolver; -use eZ\Publish\API\Repository\PermissionResolver as PermissionResolverInterface; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOr; -use eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\UserReference; -use eZ\Publish\Core\Limitation\TargetOnlyLimitationType; -use RuntimeException; - -/** - * Implementation of Permissions Criterion Resolver. - */ -class PermissionCriterionResolver implements APIPermissionCriterionResolver -{ - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $innerPermissionResolver; - - /** @var \eZ\Publish\Core\Repository\Permission\LimitationService */ - private $limitationService; - - /** - * Constructor. - * - * @param \eZ\Publish\API\Repository\PermissionResolver $innerPermissionResolver - * @param \eZ\Publish\Core\Repository\Permission\LimitationService $limitationService - */ - public function __construct( - PermissionResolverInterface $innerPermissionResolver, - LimitationService $limitationService - ) { - $this->innerPermissionResolver = $innerPermissionResolver; - $this->limitationService = $limitationService; - } - - /** - * Get permission criteria if needed and return false if no access at all. - * - * @uses \eZ\Publish\API\Repository\PermissionResolver::getCurrentUserReference() - * @uses \eZ\Publish\API\Repository\PermissionResolver::hasAccess() - * - * @param string $module - * @param string $function - * @param array $targets - * - * @return bool|\eZ\Publish\API\Repository\Values\Content\Query\Criterion - */ - public function getPermissionsCriterion(string $module = 'content', string $function = 'read', ?array $targets = null) - { - $permissionSets = $this->innerPermissionResolver->hasAccess($module, $function); - if (is_bool($permissionSets)) { - return $permissionSets; - } - - if (empty($permissionSets)) { - throw new RuntimeException("Received an empty array of limitations from hasAccess( '{$module}', '{$function}' )"); - } - - /* - * RoleAssignment is a OR condition, so is policy, while limitations is a AND condition - * - * If RoleAssignment has limitation then policy OR conditions are wrapped in a AND condition with the - * role limitation, otherwise it will be merged into RoleAssignment's OR condition. - */ - $currentUserRef = $this->innerPermissionResolver->getCurrentUserReference(); - $roleAssignmentOrCriteria = []; - foreach ($permissionSets as $permissionSet) { - // $permissionSet is a RoleAssignment, but in the form of role limitation & role policies hash - $policyOrCriteria = []; - /** @var \eZ\Publish\API\Repository\Values\User\Policy */ - foreach ($permissionSet['policies'] as $policy) { - $limitations = $policy->getLimitations(); - if (empty($limitations)) { - // Given policy gives full access, optimize away all role policies (but not role limitation if any) - // This should be optimized on create/update of Roles, however we keep this here for bc with older data - $policyOrCriteria = []; - break; - } - - $limitationsAndCriteria = []; - foreach ($limitations as $limitation) { - $limitationsAndCriteria[] = $this->getCriterionForLimitation($limitation, $currentUserRef, $targets); - } - - $policyOrCriteria[] = isset($limitationsAndCriteria[1]) ? - new LogicalAnd($limitationsAndCriteria) : - $limitationsAndCriteria[0]; - } - - /** - * Apply role limitations if there is one. - * - * @var \eZ\Publish\API\Repository\Values\User\Limitation[] - */ - if ($permissionSet['limitation'] instanceof Limitation) { - // We need to match both the limitation AND *one* of the policies, aka; roleLimit AND policies(OR) - if (!empty($policyOrCriteria)) { - $criterion = $this->getCriterionForLimitation($permissionSet['limitation'], $currentUserRef, $targets); - $roleAssignmentOrCriteria[] = new LogicalAnd( - [ - $criterion, - isset($policyOrCriteria[1]) ? new LogicalOr($policyOrCriteria) : $policyOrCriteria[0], - ] - ); - } else { - $roleAssignmentOrCriteria[] = $this->getCriterionForLimitation( - $permissionSet['limitation'], - $currentUserRef, - $targets - ); - } - } elseif (!empty($policyOrCriteria)) { - // Otherwise merge $policyOrCriteria into $roleAssignmentOrCriteria - // There is no role limitation, so any of the policies can globally match in the returned OR criteria - $roleAssignmentOrCriteria = empty($roleAssignmentOrCriteria) ? - $policyOrCriteria : - array_merge($roleAssignmentOrCriteria, $policyOrCriteria); - } - } - - if (empty($roleAssignmentOrCriteria)) { - return false; - } - - return isset($roleAssignmentOrCriteria[1]) ? - new LogicalOr($roleAssignmentOrCriteria) : - $roleAssignmentOrCriteria[0]; - } - - /** - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUserRef - * @param array|null $targets - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface|\eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOperator - */ - private function getCriterionForLimitation(Limitation $limitation, UserReference $currentUserRef, ?array $targets): CriterionInterface - { - $type = $this->limitationService->getLimitationType($limitation->getIdentifier()); - if ($type instanceof TargetOnlyLimitationType) { - return $type->getCriterionByTarget($limitation, $currentUserRef, $targets); - } - - return $type->getCriterion($limitation, $currentUserRef); - } - - public function getQueryPermissionsCriterion(): Criterion - { - // Permission Criterion handling work-around to avoid rewriting old architecture of perm. sys. - $permissionCriterion = $this->getPermissionsCriterion( - 'content', - 'read' - ); - if (true === $permissionCriterion) { - return new Criterion\MatchAll(); - } - if (false === $permissionCriterion) { - return new Criterion\MatchNone(); - } - - return $permissionCriterion; - } -} diff --git a/eZ/Publish/Core/Repository/Permission/PermissionResolver.php b/eZ/Publish/Core/Repository/Permission/PermissionResolver.php deleted file mode 100644 index 7180a0449c..0000000000 --- a/eZ/Publish/Core/Repository/Permission/PermissionResolver.php +++ /dev/null @@ -1,465 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Permission; - -use Exception; -use eZ\Publish\API\Repository\PermissionResolver as PermissionResolverInterface; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\LookupLimitationResult; -use eZ\Publish\API\Repository\Values\User\LookupPolicyLimitations; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\Repository\Mapper\RoleDomainMapper; -use eZ\Publish\Core\Repository\Values\User\UserReference; -use eZ\Publish\SPI\Limitation\Target; -use eZ\Publish\SPI\Limitation\TargetAwareType; -use eZ\Publish\SPI\Limitation\Type as LimitationType; -use eZ\Publish\SPI\Persistence\User\Handler as UserHandler; - -/** - * Core implementation of PermissionResolver interface. - */ -class PermissionResolver implements PermissionResolverInterface -{ - /** - * Counter for the current sudo nesting level {@see sudo()}. - * - * @var int - */ - private $sudoNestingLevel = 0; - - /** @var \eZ\Publish\Core\Repository\Mapper\RoleDomainMapper */ - private $roleDomainMapper; - - /** @var \eZ\Publish\Core\Repository\Permission\LimitationService */ - private $limitationService; - - /** @var \eZ\Publish\SPI\Persistence\User\Handler */ - private $userHandler; - - /** - * Currently logged in user reference for permission purposes. - * - * @var \eZ\Publish\API\Repository\Values\User\UserReference - */ - private $currentUserRef; - - /** - * Map of system configured policies, for validation usage. - * - * @var array - */ - private $policyMap; - - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ - private $configResolver; - - /** - * @param array $policyMap Map of system configured policies, for validation usage. - */ - public function __construct( - RoleDomainMapper $roleDomainMapper, - LimitationService $limitationService, - UserHandler $userHandler, - ConfigResolverInterface $configResolver, - array $policyMap - ) { - $this->roleDomainMapper = $roleDomainMapper; - $this->limitationService = $limitationService; - $this->userHandler = $userHandler; - $this->configResolver = $configResolver; - $this->policyMap = $policyMap; - } - - public function getCurrentUserReference(): APIUserReference - { - if (empty($this->currentUserRef)) { - $this->currentUserRef = new UserReference( - $this->configResolver->getParameter('anonymous_user_id') - ); - } - - return $this->currentUserRef; - } - - public function setCurrentUserReference(APIUserReference $userReference): void - { - $id = $userReference->getUserId(); - if (!$id) { - throw new InvalidArgumentValue('$user->getUserId()', $id); - } - - $this->currentUserRef = $userReference; - } - - public function hasAccess(string $module, string $function, ?APIUserReference $userReference = null) - { - if (!isset($this->policyMap[$module])) { - throw new InvalidArgumentValue('module', "module: {$module}/ function: {$function}"); - } elseif (!array_key_exists($function, $this->policyMap[$module])) { - throw new InvalidArgumentValue('function', "module: {$module}/ function: {$function}"); - } - - // Full access if sudo nesting level is set by {@see sudo()} - if ($this->sudoNestingLevel > 0) { - return true; - } - - if ($userReference === null) { - $userReference = $this->getCurrentUserReference(); - } - - // Uses SPI to avoid triggering permission checks in Role/User service - $permissionSets = []; - $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($userReference->getUserId(), true); - foreach ($spiRoleAssignments as $spiRoleAssignment) { - $permissionSet = ['limitation' => null, 'policies' => []]; - - $spiRole = $this->userHandler->loadRole($spiRoleAssignment->roleId); - foreach ($spiRole->policies as $spiPolicy) { - if ($spiPolicy->module === '*' && $spiRoleAssignment->limitationIdentifier === null) { - return true; - } - - if ($spiPolicy->module !== $module && $spiPolicy->module !== '*') { - continue; - } - - if ($spiPolicy->function === '*' && $spiRoleAssignment->limitationIdentifier === null) { - return true; - } - - if ($spiPolicy->function !== $function && $spiPolicy->function !== '*') { - continue; - } - - if ($spiPolicy->limitations === '*' && $spiRoleAssignment->limitationIdentifier === null) { - return true; - } - - $permissionSet['policies'][] = $this->roleDomainMapper->buildDomainPolicyObject($spiPolicy); - } - - if (!empty($permissionSet['policies'])) { - if ($spiRoleAssignment->limitationIdentifier !== null) { - $permissionSet['limitation'] = $this->limitationService - ->getLimitationType($spiRoleAssignment->limitationIdentifier) - ->buildValue($spiRoleAssignment->values); - } - - $permissionSets[] = $permissionSet; - } - } - - if (!empty($permissionSets)) { - return $permissionSets; - } - - return false; // No policies matching $module and $function, or they contained limitations - } - - public function canUser(string $module, string $function, ValueObject $object, array $targets = []): bool - { - $permissionSets = $this->hasAccess($module, $function); - if ($permissionSets === false || $permissionSets === true) { - return $permissionSets; - } - - if (empty($targets)) { - $targets = null; - } - - $currentUserRef = $this->getCurrentUserReference(); - foreach ($permissionSets as $permissionSet) { - /** - * First deal with Role limitation if any. - * - * Here we accept ACCESS_GRANTED and ACCESS_ABSTAIN, the latter in cases where $object and $targets - * are not supported by limitation. - * - * @var \eZ\Publish\API\Repository\Values\User\Limitation[] - */ - if ( - $permissionSet['limitation'] instanceof Limitation - && $this->isDeniedByRoleLimitation( - $permissionSet['limitation'], - $currentUserRef, - $object, - $targets - ) - ) { - continue; - } - - /** - * Loop over all policies. - * - * These are already filtered by hasAccess and given hasAccess did not return boolean - * there must be some, so only return true if one of them says yes. - * - * @var \eZ\Publish\API\Repository\Values\User\Policy - */ - foreach ($permissionSet['policies'] as $policy) { - $limitations = $policy->getLimitations(); - - /* - * Return true if policy gives full access (aka no limitations) - */ - if ($limitations === '*') { - return true; - } - - /* - * Loop over limitations, all must return ACCESS_GRANTED for policy to pass. - * If limitations was empty array this means same as '*' - */ - $limitationsPass = true; - foreach ($limitations as $limitation) { - $type = $this->limitationService->getLimitationType($limitation->getIdentifier()); - $accessVote = $type->evaluate( - $limitation, - $currentUserRef, - $object, - $this->prepareTargetsForType($targets, $type) - ); - /* - * For policy limitation atm only support ACCESS_GRANTED - * - * Reasoning: Right now, use of a policy limitation not valid for a policy is per definition a - * BadState. To reach this you would have to configure the "policyMap" wrongly, like using - * Node (Location) limitation on state/assign. So in this case Role Limitations will return - * ACCESS_ABSTAIN (== no access here), and other limitations will throw InvalidArgument above, - * both cases forcing dev to investigate to find miss configuration. This might be relaxed in - * the future if valid use cases for ACCESS_ABSTAIN on policy limitations becomes known. - */ - if ($accessVote !== LimitationType::ACCESS_GRANTED) { - $limitationsPass = false; - break; // Break to next policy, all limitations must pass - } - } - if ($limitationsPass) { - return true; - } - } - } - - return false; // None of the limitation sets wanted to let you in, sorry! - } - - /** - * {@inheritdoc} - */ - public function lookupLimitations( - string $module, - string $function, - ValueObject $object, - array $targets = [], - array $limitationsIdentifiers = [] - ): LookupLimitationResult { - $permissionSets = $this->hasAccess($module, $function); - - if (is_bool($permissionSets)) { - return new LookupLimitationResult($permissionSets); - } - - if (empty($targets)) { - $targets = null; - } - - $currentUserReference = $this->getCurrentUserReference(); - - $passedLimitations = []; - $passedRoleLimitations = []; - - foreach ($permissionSets as $permissionSet) { - if ($this->isDeniedByRoleLimitation($permissionSet['limitation'], $currentUserReference, $object, $targets)) { - continue; - } - - /** @var \eZ\Publish\API\Repository\Values\User\Policy $policy */ - foreach ($permissionSet['policies'] as $policy) { - $policyLimitations = $policy->getLimitations(); - - /** Return empty array if policy gives full access (aka no limitations) */ - if ($policyLimitations === '*') { - return new LookupLimitationResult(true); - } - - $limitationsPass = true; - $possibleLimitations = []; - $possibleRoleLimitation = $permissionSet['limitation']; - foreach ($policyLimitations as $limitation) { - $limitationsPass = $this->isGrantedByLimitation($limitation, $currentUserReference, $object, $targets); - if (!$limitationsPass) { - break; - } - - $possibleLimitations[] = $limitation; - } - - $limitationFilter = static function (Limitation $limitation) use ($limitationsIdentifiers) { - return \in_array($limitation->getIdentifier(), $limitationsIdentifiers, true); - }; - - if (!empty($limitationsIdentifiers)) { - $possibleLimitations = array_filter($possibleLimitations, $limitationFilter); - if (!\in_array($possibleRoleLimitation, $limitationsIdentifiers, true)) { - $possibleRoleLimitation = null; - } - } - - if ($limitationsPass) { - $passedLimitations[] = new LookupPolicyLimitations($policy, $possibleLimitations); - if (null !== $possibleRoleLimitation) { - $passedRoleLimitations[] = $possibleRoleLimitation; - } - } - } - } - - return new LookupLimitationResult(!empty($passedLimitations), $passedRoleLimitations, $passedLimitations); - } - - /** - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUserReference - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param array|null $targets - * - * @return bool - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - private function isGrantedByLimitation( - Limitation $limitation, - APIUserReference $currentUserReference, - ValueObject $object, - ?array $targets - ): bool { - $type = $this->limitationService->getLimitationType($limitation->getIdentifier()); - $accessVote = $type->evaluate( - $limitation, - $currentUserReference, - $object, - $this->prepareTargetsForType($targets, $type) - ); - - return $accessVote === LimitationType::ACCESS_GRANTED; - } - - /** - * @param \eZ\Publish\API\Repository\Values\User\Limitation|null $limitation - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUserReference - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param array|null $targets - * - * @return bool - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - private function isDeniedByRoleLimitation( - ?Limitation $limitation, - APIUserReference $currentUserReference, - ValueObject $object, - ?array $targets - ): bool { - if (null === $limitation) { - return false; - } - - $type = $this->limitationService->getLimitationType($limitation->getIdentifier()); - $accessVote = $type->evaluate( - $limitation, - $currentUserReference, - $object, - $this->prepareTargetsForType($targets, $type) - ); - - return $accessVote === LimitationType::ACCESS_DENIED; - } - - /** - * @internal For internal use only, do not depend on this method. - * - * Allows API execution to be performed with full access sand-boxed. - * - * The closure sandbox will do a catch all on exceptions and rethrow after - * re-setting the sudo flag. - * - * Example use: - * $location = $repository->sudo( - * function ( Repository $repo ) use ( $locationId ) - * { - * return $repo->getLocationService()->loadLocation( $locationId ) - * } - * ); - * - * @param \callable(\eZ\Publish\API\Repository\Repository): mixed $callback - * @param \eZ\Publish\API\Repository\Repository $outerRepository - * - * @throws \RuntimeException Thrown on recursive sudo() use. - * @throws \Exception Re throws exceptions thrown inside $callback - * - * @return mixed - */ - public function sudo(callable $callback, RepositoryInterface $outerRepository) - { - ++$this->sudoNestingLevel; - try { - $returnValue = $callback($outerRepository); - } catch (Exception $e) { - --$this->sudoNestingLevel; - throw $e; - } - - --$this->sudoNestingLevel; - - return $returnValue; - } - - /** - * Prepare list of targets for the given Type keeping BC. - * - * @param array|null $targets - * @param \eZ\Publish\SPI\Limitation\Type $type - * - * @return array|null - */ - private function prepareTargetsForType(?array $targets, LimitationType $type): ?array - { - $isTargetAware = $type instanceof TargetAwareType; - - // BC: null for empty targets is still expected by some Limitations, so needs to be preserved - if (null === $targets) { - return $isTargetAware ? [] : null; - } - - // BC: for TargetAware Limitations return only instances of Target, for others return only non-Target instances - $targets = array_filter( - $targets, - static function ($target) use ($isTargetAware) { - $isTarget = $target instanceof Target; - - return $isTargetAware ? $isTarget : !$isTarget; - } - ); - - // BC: treat empty targets after filtering as if they were empty the whole time - if (!$isTargetAware && empty($targets)) { - return null; - } - - return $targets; - } -} diff --git a/eZ/Publish/Core/Repository/PermissionsCriterionHandler.php b/eZ/Publish/Core/Repository/PermissionsCriterionHandler.php deleted file mode 100644 index b5de0349a8..0000000000 --- a/eZ/Publish/Core/Repository/PermissionsCriterionHandler.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\Core\Repository\Permission\PermissionCriterionResolver; - -/** - * Handler for permissions Criterion. - * - * @deprecated 6.7.7 - */ -class PermissionsCriterionHandler extends PermissionCriterionResolver -{ - /** - * Adds content, read Permission criteria if needed and return false if no access at all. - * - * @uses \eZ\Publish\Core\Repository\Permission\PermissionCriterionResolver::getPermissionsCriterion() - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool|\eZ\Publish\API\Repository\Values\Content\Query\Criterion - */ - public function addPermissionsCriterion(Criterion &$criterion) - { - $permissionCriterion = $this->getPermissionsCriterion(); - if ($permissionCriterion === true || $permissionCriterion === false) { - return $permissionCriterion; - } - - // Merge with original $criterion - if ($criterion instanceof LogicalAnd) { - $criterion->criteria[] = $permissionCriterion; - } else { - $criterion = new LogicalAnd( - [ - $criterion, - $permissionCriterion, - ] - ); - } - - return true; - } -} diff --git a/eZ/Publish/Core/Repository/ProxyFactory/ProxyDomainMapperFactory.php b/eZ/Publish/Core/Repository/ProxyFactory/ProxyDomainMapperFactory.php deleted file mode 100644 index fe039677d2..0000000000 --- a/eZ/Publish/Core/Repository/ProxyFactory/ProxyDomainMapperFactory.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\ProxyFactory; - -use eZ\Publish\API\Repository\Repository; - -/** - * @internal - */ -final class ProxyDomainMapperFactory implements ProxyDomainMapperFactoryInterface -{ - /** @var \eZ\Publish\Core\Repository\ProxyFactory\ProxyGeneratorInterface */ - private $proxyGenerator; - - public function __construct(ProxyGeneratorInterface $proxyGenerator) - { - $this->proxyGenerator = $proxyGenerator; - } - - public function create(Repository $repository): ProxyDomainMapperInterface - { - return new ProxyDomainMapper($repository, $this->proxyGenerator); - } -} diff --git a/eZ/Publish/Core/Repository/ProxyFactory/ProxyDomainMapperFactoryInterface.php b/eZ/Publish/Core/Repository/ProxyFactory/ProxyDomainMapperFactoryInterface.php deleted file mode 100644 index 1a57c69233..0000000000 --- a/eZ/Publish/Core/Repository/ProxyFactory/ProxyDomainMapperFactoryInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\ProxyFactory; - -use eZ\Publish\API\Repository\Repository; - -/** - * @internal - */ -interface ProxyDomainMapperFactoryInterface -{ - public function create(Repository $repository): ProxyDomainMapperInterface; -} diff --git a/eZ/Publish/Core/Repository/ProxyFactory/ProxyDomainMapperInterface.php b/eZ/Publish/Core/Repository/ProxyFactory/ProxyDomainMapperInterface.php deleted file mode 100644 index dab05e2fa2..0000000000 --- a/eZ/Publish/Core/Repository/ProxyFactory/ProxyDomainMapperInterface.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\ProxyFactory; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Section; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup; -use eZ\Publish\API\Repository\Values\User\User; - -/** - * @internal - */ -interface ProxyDomainMapperInterface -{ - public function createContentProxy( - int $contentId, - array $prioritizedLanguages = Language::ALL, - bool $useAlwaysAvailable = true, - ?int $versionNo = null - ): Content; - - public function createContentInfoProxy(int $contentId): ContentInfo; - - public function createContentTypeProxy(int $contentTypeId, array $prioritizedLanguages = Language::ALL): ContentType; - - public function createContentTypeGroupProxy(int $contentTypeGroupId, array $prioritizedLanguages = Language::ALL): ContentTypeGroup; - - public function createContentTypeGroupProxyList(array $contentTypeGroupIds, array $prioritizedLanguages = Language::ALL): array; - - public function createLanguageProxy(string $languageCode): Language; - - public function createLanguageProxyList(array $languageCodes): array; - - public function createLocationProxy(int $locationId, array $prioritizedLanguages = Language::ALL): Location; - - public function createSectionProxy(int $sectionId): Section; - - public function createUserProxy(int $userId, array $prioritizedLanguages = Language::ALL): User; -} diff --git a/eZ/Publish/Core/Repository/Repository.php b/eZ/Publish/Core/Repository/Repository.php deleted file mode 100644 index a1459a7a8f..0000000000 --- a/eZ/Publish/Core/Repository/Repository.php +++ /dev/null @@ -1,838 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use Exception; -use eZ\Publish\API\Repository\BookmarkService as BookmarkServiceInterface; -use eZ\Publish\API\Repository\ContentService as ContentServiceInterface; -use eZ\Publish\API\Repository\ContentTypeService as ContentTypeServiceInterface; -use eZ\Publish\API\Repository\FieldTypeService as FieldTypeServiceInterface; -use eZ\Publish\API\Repository\LanguageResolver; -use eZ\Publish\API\Repository\LanguageService as LanguageServiceInterface; -use eZ\Publish\API\Repository\LocationService as LocationServiceInterface; -use eZ\Publish\API\Repository\NotificationService as NotificationServiceInterface; -use eZ\Publish\API\Repository\ObjectStateService as ObjectStateServiceInterface; -use eZ\Publish\API\Repository\PasswordHashService; -use eZ\Publish\API\Repository\PermissionCriterionResolver as PermissionCriterionResolverInterface; -use eZ\Publish\API\Repository\PermissionResolver as PermissionResolverInterface; -use eZ\Publish\API\Repository\PermissionService; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\RoleService as RoleServiceInterface; -use eZ\Publish\API\Repository\SearchService as SearchServiceInterface; -use eZ\Publish\API\Repository\SectionService as SectionServiceInterface; -use eZ\Publish\API\Repository\TrashService as TrashServiceInterface; -use eZ\Publish\API\Repository\URLAliasService as URLAliasServiceInterface; -use eZ\Publish\API\Repository\URLService as URLServiceInterface; -use eZ\Publish\API\Repository\URLWildcardService as URLWildcardServiceInterface; -use eZ\Publish\API\Repository\UserPreferenceService as UserPreferenceServiceInterface; -use eZ\Publish\API\Repository\UserService as UserServiceInterface; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\Repository\Helper\NameSchemaService; -use eZ\Publish\Core\Repository\Helper\RelationProcessor; -use eZ\Publish\Core\Repository\Permission\LimitationService; -use eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperFactoryInterface; -use eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperInterface; -use eZ\Publish\Core\Repository\User\PasswordValidatorInterface; -use eZ\Publish\Core\Search\Common\BackgroundIndexer; -use eZ\Publish\SPI\Persistence\Filter\Content\Handler as ContentFilteringHandler; -use eZ\Publish\SPI\Persistence\Filter\Location\Handler as LocationFilteringHandler; -use eZ\Publish\SPI\Persistence\Handler as PersistenceHandler; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy; -use eZ\Publish\SPI\Repository\Validator\ContentValidator; -use eZ\Publish\SPI\Search\Handler as SearchHandler; -use Psr\Log\LoggerInterface; -use Psr\Log\NullLogger; -use RuntimeException; - -/** - * Repository class. - */ -class Repository implements RepositoryInterface -{ - /** - * Repository Handler object. - * - * @var \eZ\Publish\SPI\Persistence\Handler - */ - protected $persistenceHandler; - - /** - * Instance of main Search Handler. - * - * @var \eZ\Publish\SPI\Search\Handler - */ - protected $searchHandler; - - /** - * Instance of content service. - * - * @var \eZ\Publish\API\Repository\ContentService - */ - protected $contentService; - - /** - * Instance of section service. - * - * @var \eZ\Publish\API\Repository\SectionService - */ - protected $sectionService; - - /** - * Instance of role service. - * - * @var \eZ\Publish\API\Repository\RoleService - */ - protected $roleService; - - /** - * Instance of search service. - * - * @var \eZ\Publish\API\Repository\SearchService - */ - protected $searchService; - - /** - * Instance of user service. - * - * @var \eZ\Publish\API\Repository\UserService - */ - protected $userService; - - /** - * Instance of language service. - * - * @var \eZ\Publish\API\Repository\LanguageService - */ - protected $languageService; - - /** - * Instance of location service. - * - * @var \eZ\Publish\API\Repository\LocationService - */ - protected $locationService; - - /** - * Instance of Trash service. - * - * @var \eZ\Publish\API\Repository\TrashService - */ - protected $trashService; - - /** - * Instance of content type service. - * - * @var \eZ\Publish\API\Repository\ContentTypeService - */ - protected $contentTypeService; - - /** - * Instance of object state service. - * - * @var \eZ\Publish\API\Repository\ObjectStateService - */ - protected $objectStateService; - - /** - * Instance of field type service. - * - * @var \eZ\Publish\API\Repository\FieldTypeService - */ - protected $fieldTypeService; - - /** @var \eZ\Publish\Core\FieldType\FieldTypeRegistry */ - private $fieldTypeRegistry; - - /** - * Instance of name schema resolver service. - * - * @var \eZ\Publish\Core\Repository\Helper\NameSchemaService - */ - protected $nameSchemaService; - - /** - * Instance of relation processor service. - * - * @var \eZ\Publish\Core\Repository\Helper\RelationProcessor - */ - protected $relationProcessor; - - /** - * Instance of URL alias service. - * - * @var \eZ\Publish\Core\Repository\URLAliasService - */ - protected $urlAliasService; - - /** - * Instance of URL wildcard service. - * - * @var \eZ\Publish\Core\Repository\URLWildcardService - */ - protected $urlWildcardService; - - /** - * Instance of URL service. - * - * @var \eZ\Publish\Core\Repository\URLService - */ - protected $urlService; - - /** - * Instance of Bookmark service. - * - * @var \eZ\Publish\API\Repository\BookmarkService - */ - protected $bookmarkService; - - /** - * Instance of Notification service. - * - * @var \eZ\Publish\API\Repository\NotificationService - */ - protected $notificationService; - - /** - * Instance of User Preference service. - * - * @var \eZ\Publish\API\Repository\UserPreferenceService - */ - protected $userPreferenceService; - - /** - * Service settings, first level key is service name. - * - * @var array - */ - protected $serviceSettings; - - /** @var \eZ\Publish\Core\Repository\Permission\LimitationService */ - protected $limitationService; - - /** @var \eZ\Publish\Core\Repository\Mapper\RoleDomainMapper */ - protected $roleDomainMapper; - - /** @var \eZ\Publish\Core\Repository\Mapper\ContentDomainMapper */ - protected $contentDomainMapper; - - /** @var \eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper */ - protected $contentTypeDomainMapper; - - /** @var \eZ\Publish\Core\Search\Common\BackgroundIndexer|null */ - protected $backgroundIndexer; - - /** @var \Psr\Log\LoggerInterface */ - private $logger; - - /** @var \eZ\Publish\API\Repository\PasswordHashService */ - private $passwordHashService; - - /** @var \eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy */ - private $thumbnailStrategy; - - /** @var \eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperFactory */ - private $proxyDomainMapperFactory; - - /** @var \eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperInterface|null */ - private $proxyDomainMapper; - - /** @var \eZ\Publish\API\Repository\LanguageResolver */ - private $languageResolver; - - /** @var \eZ\Publish\API\Repository\PermissionService */ - private $permissionService; - - /** @var \eZ\Publish\Core\Repository\Mapper\ContentMapper */ - private $contentMapper; - - /** @var \eZ\Publish\SPI\Repository\Validator\ContentValidator */ - private $contentValidator; - - /** @var \eZ\Publish\SPI\Persistence\Filter\Content\Handler */ - private $contentFilteringHandler; - - /** @var \eZ\Publish\SPI\Persistence\Filter\Location\Handler */ - private $locationFilteringHandler; - - /** @var \eZ\Publish\Core\Repository\User\PasswordValidatorInterface */ - private $passwordValidator; - - public function __construct( - PersistenceHandler $persistenceHandler, - SearchHandler $searchHandler, - BackgroundIndexer $backgroundIndexer, - RelationProcessor $relationProcessor, - FieldTypeRegistry $fieldTypeRegistry, - PasswordHashService $passwordHashGenerator, - ThumbnailStrategy $thumbnailStrategy, - ProxyDomainMapperFactoryInterface $proxyDomainMapperFactory, - Mapper\ContentDomainMapper $contentDomainMapper, - Mapper\ContentTypeDomainMapper $contentTypeDomainMapper, - Mapper\RoleDomainMapper $roleDomainMapper, - Mapper\ContentMapper $contentMapper, - ContentValidator $contentValidator, - LimitationService $limitationService, - LanguageResolver $languageResolver, - PermissionService $permissionService, - ContentFilteringHandler $contentFilteringHandler, - LocationFilteringHandler $locationFilteringHandler, - PasswordValidatorInterface $passwordValidator, - array $serviceSettings = [], - ?LoggerInterface $logger = null - ) { - $this->persistenceHandler = $persistenceHandler; - $this->searchHandler = $searchHandler; - $this->backgroundIndexer = $backgroundIndexer; - $this->relationProcessor = $relationProcessor; - $this->fieldTypeRegistry = $fieldTypeRegistry; - $this->passwordHashService = $passwordHashGenerator; - $this->thumbnailStrategy = $thumbnailStrategy; - $this->proxyDomainMapperFactory = $proxyDomainMapperFactory; - $this->contentDomainMapper = $contentDomainMapper; - $this->contentTypeDomainMapper = $contentTypeDomainMapper; - $this->roleDomainMapper = $roleDomainMapper; - $this->limitationService = $limitationService; - $this->languageResolver = $languageResolver; - $this->contentFilteringHandler = $contentFilteringHandler; - $this->permissionService = $permissionService; - $this->locationFilteringHandler = $locationFilteringHandler; - - $this->serviceSettings = $serviceSettings + [ - 'content' => [], - 'contentType' => [], - 'location' => [], - 'section' => [], - 'role' => [], - 'user' => [ - 'anonymousUserID' => 10, - ], - 'language' => [], - 'trash' => [], - 'io' => [], - 'objectState' => [], - 'search' => [], - 'urlAlias' => [], - 'urlWildcard' => [], - 'nameSchema' => [], - 'languages' => [], - 'proxy_factory' => [], - ]; - - if (!empty($this->serviceSettings['languages'])) { - $this->serviceSettings['language']['languages'] = $this->serviceSettings['languages']; - } - - $this->logger = null !== $logger ? $logger : new NullLogger(); - $this->contentMapper = $contentMapper; - $this->contentValidator = $contentValidator; - $this->passwordValidator = $passwordValidator; - } - - public function sudo(callable $callback, ?RepositoryInterface $outerRepository = null) - { - return $this->getPermissionResolver()->sudo($callback, $outerRepository ?? $this); - } - - /** - * Get Content Service. - * - * Get service object to perform operations on Content objects and it's aggregate members. - * - * @return \eZ\Publish\API\Repository\ContentService - */ - public function getContentService(): ContentServiceInterface - { - if ($this->contentService !== null) { - return $this->contentService; - } - - $this->contentService = new ContentService( - $this, - $this->persistenceHandler, - $this->contentDomainMapper, - $this->getRelationProcessor(), - $this->getNameSchemaService(), - $this->fieldTypeRegistry, - $this->getPermissionService(), - $this->contentMapper, - $this->contentValidator, - $this->contentFilteringHandler, - $this->serviceSettings['content'], - ); - - return $this->contentService; - } - - /** - * Get Content Language Service. - * - * Get service object to perform operations on Content language objects - * - * @return \eZ\Publish\API\Repository\LanguageService - */ - public function getContentLanguageService(): LanguageServiceInterface - { - if ($this->languageService !== null) { - return $this->languageService; - } - - $this->languageService = new LanguageService( - $this, - $this->persistenceHandler->contentLanguageHandler(), - $this->getPermissionResolver(), - $this->serviceSettings['language'] - ); - - return $this->languageService; - } - - /** - * Get Content Type Service. - * - * Get service object to perform operations on Content Type objects and it's aggregate members. - * ( Group, Field & FieldCategory ) - * - * @return \eZ\Publish\API\Repository\ContentTypeService - */ - public function getContentTypeService(): ContentTypeServiceInterface - { - if ($this->contentTypeService !== null) { - return $this->contentTypeService; - } - - $this->contentTypeService = new ContentTypeService( - $this, - $this->persistenceHandler->contentTypeHandler(), - $this->persistenceHandler->userHandler(), - $this->contentDomainMapper, - $this->contentTypeDomainMapper, - $this->fieldTypeRegistry, - $this->getPermissionResolver(), - $this->serviceSettings['contentType'] - ); - - return $this->contentTypeService; - } - - /** - * Get Content Location Service. - * - * Get service object to perform operations on Location objects and subtrees - * - * @return \eZ\Publish\API\Repository\LocationService - */ - public function getLocationService(): LocationServiceInterface - { - if ($this->locationService !== null) { - return $this->locationService; - } - - $this->locationService = new LocationService( - $this, - $this->persistenceHandler, - $this->contentDomainMapper, - $this->getNameSchemaService(), - $this->getPermissionCriterionResolver(), - $this->getPermissionResolver(), - $this->locationFilteringHandler, - $this->getContentTypeService(), - $this->serviceSettings['location'], - $this->logger - ); - - return $this->locationService; - } - - /** - * Get Content Trash service. - * - * Trash service allows to perform operations related to location trash - * (trash/untrash, load/list from trash...) - * - * @return \eZ\Publish\API\Repository\TrashService - */ - public function getTrashService(): TrashServiceInterface - { - if ($this->trashService !== null) { - return $this->trashService; - } - - $this->trashService = new TrashService( - $this, - $this->persistenceHandler, - $this->getNameSchemaService(), - $this->getPermissionCriterionResolver(), - $this->getPermissionResolver(), - $this->getProxyDomainMapper(), - $this->serviceSettings['trash'] - ); - - return $this->trashService; - } - - /** - * Get Content Section Service. - * - * Get Section service that lets you manipulate section objects - * - * @return \eZ\Publish\API\Repository\SectionService - */ - public function getSectionService(): SectionServiceInterface - { - if ($this->sectionService !== null) { - return $this->sectionService; - } - - $this->sectionService = new SectionService( - $this, - $this->persistenceHandler->sectionHandler(), - $this->persistenceHandler->locationHandler(), - $this->getPermissionCriterionResolver(), - $this->serviceSettings['section'] - ); - - return $this->sectionService; - } - - /** - * Get User Service. - * - * Get service object to perform operations on Users and UserGroup - * - * @return \eZ\Publish\API\Repository\UserService - */ - public function getUserService(): UserServiceInterface - { - if ($this->userService !== null) { - return $this->userService; - } - - $this->userService = new UserService( - $this, - $this->getPermissionResolver(), - $this->persistenceHandler->userHandler(), - $this->persistenceHandler->locationHandler(), - $this->passwordHashService, - $this->passwordValidator, - $this->serviceSettings['user'] - ); - - return $this->userService; - } - - /** - * Get URLAliasService. - * - * @return \eZ\Publish\API\Repository\URLAliasService - */ - public function getURLAliasService(): URLAliasServiceInterface - { - if ($this->urlAliasService !== null) { - return $this->urlAliasService; - } - - $this->urlAliasService = new URLAliasService( - $this, - $this->persistenceHandler->urlAliasHandler(), - $this->getNameSchemaService(), - $this->getPermissionResolver(), - $this->languageResolver - ); - - return $this->urlAliasService; - } - - /** - * Get URLWildcardService. - * - * @return \eZ\Publish\API\Repository\URLWildcardService - */ - public function getURLWildcardService(): URLWildcardServiceInterface - { - if ($this->urlWildcardService !== null) { - return $this->urlWildcardService; - } - - $this->urlWildcardService = new URLWildcardService( - $this, - $this->persistenceHandler->urlWildcardHandler(), - $this->getPermissionResolver(), - $this->serviceSettings['urlWildcard'] - ); - - return $this->urlWildcardService; - } - - /** - * Get URLService. - * - * @return \eZ\Publish\API\Repository\URLService - */ - public function getURLService(): URLServiceInterface - { - if ($this->urlService !== null) { - return $this->urlService; - } - - $this->urlService = new URLService( - $this, - $this->persistenceHandler->urlHandler(), - $this->getPermissionResolver() - ); - - return $this->urlService; - } - - /** - * Get BookmarkService. - * - * @return \eZ\Publish\API\Repository\BookmarkService - */ - public function getBookmarkService(): BookmarkServiceInterface - { - if ($this->bookmarkService === null) { - $this->bookmarkService = new BookmarkService( - $this, - $this->persistenceHandler->bookmarkHandler() - ); - } - - return $this->bookmarkService; - } - - /** - * Get UserPreferenceService. - * - * @return \eZ\Publish\API\Repository\UserPreferenceService - */ - public function getUserPreferenceService(): UserPreferenceServiceInterface - { - if ($this->userPreferenceService === null) { - $this->userPreferenceService = new UserPreferenceService( - $this, - $this->persistenceHandler->userPreferenceHandler() - ); - } - - return $this->userPreferenceService; - } - - /** - * Get ObjectStateService. - * - * @return \eZ\Publish\API\Repository\ObjectStateService - */ - public function getObjectStateService(): ObjectStateServiceInterface - { - if ($this->objectStateService !== null) { - return $this->objectStateService; - } - - $this->objectStateService = new ObjectStateService( - $this, - $this->persistenceHandler->objectStateHandler(), - $this->getPermissionResolver(), - $this->serviceSettings['objectState'] - ); - - return $this->objectStateService; - } - - /** - * Get RoleService. - * - * @return \eZ\Publish\API\Repository\RoleService - */ - public function getRoleService(): RoleServiceInterface - { - if ($this->roleService !== null) { - return $this->roleService; - } - - $this->roleService = new RoleService( - $this, - $this->persistenceHandler->userHandler(), - $this->limitationService, - $this->getRoleDomainMapper(), - $this->serviceSettings['role'] - ); - - return $this->roleService; - } - - protected function getRoleDomainMapper(): Mapper\RoleDomainMapper - { - return $this->roleDomainMapper; - } - - /** - * Get SearchService. - * - * @return \eZ\Publish\API\Repository\SearchService - */ - public function getSearchService(): SearchServiceInterface - { - if ($this->searchService !== null) { - return $this->searchService; - } - - $this->searchService = new SearchService( - $this, - $this->searchHandler, - $this->contentDomainMapper, - $this->getPermissionCriterionResolver(), - $this->backgroundIndexer, - $this->serviceSettings['search'] - ); - - return $this->searchService; - } - - /** - * Get FieldTypeService. - * - * @return \eZ\Publish\API\Repository\FieldTypeService - */ - public function getFieldTypeService(): FieldTypeServiceInterface - { - if ($this->fieldTypeService !== null) { - return $this->fieldTypeService; - } - - $this->fieldTypeService = new FieldTypeService($this->fieldTypeRegistry); - - return $this->fieldTypeService; - } - - public function getPermissionService(): PermissionService - { - return $this->permissionService; - } - - public function getPermissionResolver(): PermissionResolverInterface - { - return $this->getPermissionService(); - } - - /** - * Get NameSchemaResolverService. - * - * - * @todo Move out from this & other repo instances when services becomes proper services in DIC terms using factory. - * - * @internal - * @private - * - * @return \eZ\Publish\Core\Repository\Helper\NameSchemaService - */ - public function getNameSchemaService(): NameSchemaService - { - if ($this->nameSchemaService !== null) { - return $this->nameSchemaService; - } - - $this->nameSchemaService = new Helper\NameSchemaService( - $this->persistenceHandler->contentTypeHandler(), - $this->contentTypeDomainMapper, - $this->fieldTypeRegistry, - $this->serviceSettings['nameSchema'] - ); - - return $this->nameSchemaService; - } - - /** - * @return \eZ\Publish\API\Repository\NotificationService - */ - public function getNotificationService(): NotificationServiceInterface - { - if (null !== $this->notificationService) { - return $this->notificationService; - } - - $this->notificationService = new NotificationService( - $this->persistenceHandler->notificationHandler(), - $this->getPermissionResolver() - ); - - return $this->notificationService; - } - - /** - * Get RelationProcessor. - * - * - * @todo Move out from this & other repo instances when services becomes proper services in DIC terms using factory. - * - * @return \eZ\Publish\Core\Repository\Helper\RelationProcessor - */ - protected function getRelationProcessor(): RelationProcessor - { - return $this->relationProcessor; - } - - protected function getProxyDomainMapper(): ProxyDomainMapperInterface - { - if ($this->proxyDomainMapper !== null) { - return $this->proxyDomainMapper; - } - - $this->proxyDomainMapper = $this->proxyDomainMapperFactory->create($this); - - return $this->proxyDomainMapper; - } - - protected function getPermissionCriterionResolver(): PermissionCriterionResolverInterface - { - return $this->getPermissionService(); - } - - /** - * Begin transaction. - * - * Begins an transaction, make sure you'll call commit or rollback when done, - * otherwise work will be lost. - */ - public function beginTransaction(): void - { - $this->persistenceHandler->beginTransaction(); - } - - /** - * Commit transaction. - * - * Commit transaction, or throw exceptions if no transactions has been started. - * - * @throws \RuntimeException If no transaction has been started - */ - public function commit(): void - { - try { - $this->persistenceHandler->commit(); - } catch (Exception $e) { - throw new RuntimeException($e->getMessage(), 0, $e); - } - } - - /** - * Rollback transaction. - * - * Rollback transaction, or throw exceptions if no transactions has been started. - * - * @throws \RuntimeException If no transaction has been started - */ - public function rollback(): void - { - try { - $this->persistenceHandler->rollback(); - } catch (Exception $e) { - throw new RuntimeException($e->getMessage(), 0, $e); - } - } -} diff --git a/eZ/Publish/Core/Repository/RoleService.php b/eZ/Publish/Core/Repository/RoleService.php deleted file mode 100644 index c68f097fd2..0000000000 --- a/eZ/Publish/Core/Repository/RoleService.php +++ /dev/null @@ -1,1297 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\RoleService as RoleServiceInterface; -use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation; -use eZ\Publish\API\Repository\Values\User\Policy as APIPolicy; -use eZ\Publish\API\Repository\Values\User\PolicyCreateStruct as APIPolicyCreateStruct; -use eZ\Publish\API\Repository\Values\User\PolicyDraft; -use eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct as APIPolicyUpdateStruct; -use eZ\Publish\API\Repository\Values\User\Role as APIRole; -use eZ\Publish\API\Repository\Values\User\RoleAssignment; -use eZ\Publish\API\Repository\Values\User\RoleCopyStruct as APIRoleCopyStruct; -use eZ\Publish\API\Repository\Values\User\RoleCreateStruct as APIRoleCreateStruct; -use eZ\Publish\API\Repository\Values\User\RoleDraft as APIRoleDraft; -use eZ\Publish\API\Repository\Values\User\RoleUpdateStruct; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\Base\Exceptions\LimitationValidationException; -use eZ\Publish\Core\Base\Exceptions\NotFound\LimitationNotFoundException; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\Core\Repository\Values\User\PolicyCreateStruct; -use eZ\Publish\Core\Repository\Values\User\PolicyUpdateStruct; -use eZ\Publish\Core\Repository\Values\User\Role; -use eZ\Publish\Core\Repository\Values\User\RoleCopyStruct; -use eZ\Publish\Core\Repository\Values\User\RoleCreateStruct; -use eZ\Publish\SPI\Limitation\Type; -use eZ\Publish\SPI\Persistence\User\Handler; -use eZ\Publish\SPI\Persistence\User\Role as SPIRole; -use eZ\Publish\SPI\Persistence\User\RoleUpdateStruct as SPIRoleUpdateStruct; - -/** - * This service provides methods for managing Roles and Policies. - */ -class RoleService implements RoleServiceInterface -{ - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\SPI\Persistence\User\Handler */ - protected $userHandler; - - /** @var \eZ\Publish\Core\Repository\Permission\LimitationService */ - protected $limitationService; - - /** @var \eZ\Publish\Core\Repository\Mapper\RoleDomainMapper */ - protected $roleDomainMapper; - - /** @var array */ - protected $settings; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionResolver; - - /** - * Setups service with reference to repository object that created it & corresponding handler. - * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\SPI\Persistence\User\Handler $userHandler - * @param \eZ\Publish\Core\Repository\Permission\LimitationService $limitationService - * @param \eZ\Publish\Core\Repository\Mapper\RoleDomainMapper $roleDomainMapper - * @param array $settings - */ - public function __construct( - RepositoryInterface $repository, - Handler $userHandler, - Permission\LimitationService $limitationService, - Mapper\RoleDomainMapper $roleDomainMapper, - array $settings = [] - ) { - $this->repository = $repository; - $this->userHandler = $userHandler; - $this->limitationService = $limitationService; - $this->roleDomainMapper = $roleDomainMapper; - $this->settings = $settings; - $this->permissionResolver = $repository->getPermissionResolver(); - } - - /** - * Creates a new RoleDraft. - * - * @since 6.0 - * - * @param \eZ\Publish\API\Repository\Values\User\RoleCreateStruct $roleCreateStruct - * - * @return \eZ\Publish\API\Repository\Values\User\RoleDraft - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the name of the role already exists or if limitation of the same type - * is repeated in the policy create struct or if limitation is not allowed on module/function - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a RoleDraft - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if a policy limitation in the $roleCreateStruct is not valid - */ - public function createRole(APIRoleCreateStruct $roleCreateStruct): APIRoleDraft - { - if (!is_string($roleCreateStruct->identifier) || empty($roleCreateStruct->identifier)) { - throw new InvalidArgumentValue('identifier', $roleCreateStruct->identifier, 'RoleCreateStruct'); - } - - if (!$this->permissionResolver->canUser('role', 'create', $roleCreateStruct)) { - throw new UnauthorizedException('role', 'create'); - } - - try { - $existingRole = $this->loadRoleByIdentifier($roleCreateStruct->identifier); - - throw new InvalidArgumentException( - '$roleCreateStruct', - "A Role '{$existingRole->id}' with identifier '{$roleCreateStruct->identifier}' " . - 'already exists' - ); - } catch (APINotFoundException $e) { - // Do nothing - } - - $limitationValidationErrors = $this->validateRoleCreateStruct($roleCreateStruct); - if (!empty($limitationValidationErrors)) { - throw new LimitationValidationException($limitationValidationErrors); - } - - $spiRoleCreateStruct = $this->roleDomainMapper->buildPersistenceRoleCreateStruct($roleCreateStruct); - - $this->repository->beginTransaction(); - try { - $spiRole = $this->userHandler->createRole($spiRoleCreateStruct); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole); - } - - /** - * Creates a new RoleDraft for an existing Role. - * - * @since 6.0 - * - * @param \eZ\Publish\API\Repository\Values\User\Role $role - * - * @return \eZ\Publish\API\Repository\Values\User\RoleDraft - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the Role already has a RoleDraft that will need to be removed first - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a RoleDraft - */ - public function createRoleDraft(APIRole $role): APIRoleDraft - { - if (!$this->permissionResolver->canUser('role', 'create', $role)) { - throw new UnauthorizedException('role', 'create'); - } - - try { - $this->userHandler->loadRole($role->id, Role::STATUS_DRAFT); - - // Throw exception, so platformui et al can do conflict management. Follow-up: EZP-24719 - throw new InvalidArgumentException( - '$role', - "Cannot create a draft for Role '{$role->identifier}' because another draft exists" - ); - } catch (APINotFoundException $e) { - $this->repository->beginTransaction(); - try { - $spiRole = $this->userHandler->createRoleDraft($role->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - return $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole); - } - - public function copyRole(APIRole $role, APIRoleCopyStruct $roleCopyStruct): APIRole - { - if (!is_string($roleCopyStruct->newIdentifier) || empty($roleCopyStruct->newIdentifier)) { - throw new InvalidArgumentValue('newIdentifier', $roleCopyStruct->newIdentifier, 'RoleCopyStruct'); - } - - if (!$this->permissionResolver->canUser('role', 'create', $roleCopyStruct)) { - throw new UnauthorizedException('role', 'create'); - } - - try { - $existingRole = $this->loadRoleByIdentifier($roleCopyStruct->newIdentifier); - - throw new InvalidArgumentException( - '$roleCopyStruct', - "Role '{$existingRole->id}' with the specified identifier '{$roleCopyStruct->newIdentifier}' " . - 'already exists' - ); - } catch (APINotFoundException $e) { - // Do nothing - } - - foreach ($role->getPolicies() as $policy) { - $policyCreateStruct = new PolicyCreateStruct([ - 'module' => $policy->module, - 'function' => $policy->function, - ]); - foreach ($policy->getLimitations() as $limitation) { - $policyCreateStruct->addLimitation($limitation); - } - $roleCopyStruct->addPolicy($policyCreateStruct); - } - - $limitationValidationErrors = $this->validateRoleCreateStruct($roleCopyStruct); - if (!empty($limitationValidationErrors)) { - throw new LimitationValidationException($limitationValidationErrors); - } - - $spiRoleCopyStruct = $this->roleDomainMapper->buildPersistenceRoleCopyStruct( - $roleCopyStruct, - $role->id, - $role->getStatus() - ); - - $this->repository->beginTransaction(); - try { - $spiRole = $this->userHandler->copyRole($spiRoleCopyStruct); - $this->repository->commit(); - } catch (\Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadRole($spiRole->id); - } - - /** - * Loads a RoleDraft for the given id. - * - * @since 6.0 - * - * @param int $id - * - * @return \eZ\Publish\API\Repository\Values\User\RoleDraft - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a RoleDraft with the given id was not found - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a RoleDraft - */ - public function loadRoleDraft(int $id): APIRoleDraft - { - $spiRole = $this->userHandler->loadRole($id, Role::STATUS_DRAFT); - - $role = $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole); - - if (!$this->permissionResolver->canUser('role', 'read', $role)) { - throw new UnauthorizedException('role', 'read'); - } - - return $role; - } - - /** - * Loads a RoleDraft by the ID of the role it was created from. - * - * @param int $roleId ID of the role the draft was created from. - * - * @return \eZ\Publish\API\Repository\Values\User\RoleDraft - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a RoleDraft with the given id was not found - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role - */ - public function loadRoleDraftByRoleId(int $roleId): APIRoleDraft - { - $spiRole = $this->userHandler->loadRoleDraftByRoleId($roleId); - - $role = $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole); - - if (!$this->permissionResolver->canUser('role', 'read', $role)) { - throw new UnauthorizedException('role', 'read'); - } - - return $role; - } - - /** - * Updates the properties of a RoleDraft. - * - * @since 6.0 - * - * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft - * @param \eZ\Publish\API\Repository\Values\User\RoleUpdateStruct $roleUpdateStruct - * - * @return \eZ\Publish\API\Repository\Values\User\RoleDraft - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the identifier of the RoleDraft already exists - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update a RoleDraft - */ - public function updateRoleDraft(APIRoleDraft $roleDraft, RoleUpdateStruct $roleUpdateStruct): APIRoleDraft - { - if ($roleUpdateStruct->identifier !== null && !is_string($roleUpdateStruct->identifier)) { - throw new InvalidArgumentValue('identifier', $roleUpdateStruct->identifier, 'RoleUpdateStruct'); - } - - $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id); - - if (!$this->permissionResolver->canUser('role', 'update', $loadedRoleDraft)) { - throw new UnauthorizedException('role', 'update'); - } - - if ($roleUpdateStruct->identifier !== null) { - try { - /* Throw exception if: - * - A published role with the same identifier exists, AND - * - The ID of the published role does not match the original ID of the draft - */ - $existingSPIRole = $this->userHandler->loadRoleByIdentifier($roleUpdateStruct->identifier); - $SPIRoleDraft = $this->userHandler->loadRole($loadedRoleDraft->id, Role::STATUS_DRAFT); - if ($existingSPIRole->id != $SPIRoleDraft->originalId) { - throw new InvalidArgumentException( - '$roleUpdateStruct', - "A Role '{$existingSPIRole->id}' with identifier '{$roleUpdateStruct->identifier}' " . - 'already exists' - ); - } - } catch (APINotFoundException $e) { - // Do nothing - } - } - - $this->repository->beginTransaction(); - try { - $this->userHandler->updateRole( - new SPIRoleUpdateStruct( - [ - 'id' => $loadedRoleDraft->id, - 'identifier' => $roleUpdateStruct->identifier ?: $loadedRoleDraft->identifier, - ] - ) - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadRoleDraft($loadedRoleDraft->id); - } - - /** - * Adds a new policy to the RoleDraft. - * - * @since 6.0 - * - * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft - * @param \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct $policyCreateStruct - * - * @return \eZ\Publish\API\Repository\Values\User\RoleDraft - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if limitation of the same type is repeated in policy create - * struct or if limitation is not allowed on module/function - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if a limitation in the $policyCreateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to add a policy - */ - public function addPolicyByRoleDraft(APIRoleDraft $roleDraft, APIPolicyCreateStruct $policyCreateStruct): APIRoleDraft - { - if (!is_string($policyCreateStruct->module) || empty($policyCreateStruct->module)) { - throw new InvalidArgumentValue('module', $policyCreateStruct->module, 'PolicyCreateStruct'); - } - - if (!is_string($policyCreateStruct->function) || empty($policyCreateStruct->function)) { - throw new InvalidArgumentValue('function', $policyCreateStruct->function, 'PolicyCreateStruct'); - } - - if ($policyCreateStruct->module === '*' && $policyCreateStruct->function !== '*') { - throw new InvalidArgumentValue('module', $policyCreateStruct->module, 'PolicyCreateStruct'); - } - - if (!$this->permissionResolver->canUser('role', 'update', $roleDraft)) { - throw new UnauthorizedException('role', 'update'); - } - - $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id); - - $limitations = $policyCreateStruct->getLimitations(); - $limitationValidationErrors = $this->validatePolicy( - $policyCreateStruct->module, - $policyCreateStruct->function, - $limitations - ); - if (!empty($limitationValidationErrors)) { - throw new LimitationValidationException($limitationValidationErrors); - } - - $spiPolicy = $this->roleDomainMapper->buildPersistencePolicyObject( - $policyCreateStruct->module, - $policyCreateStruct->function, - $limitations - ); - - $this->repository->beginTransaction(); - try { - $this->userHandler->addPolicyByRoleDraft($loadedRoleDraft->id, $spiPolicy); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadRoleDraft($loadedRoleDraft->id); - } - - /** - * Removes a policy from a RoleDraft. - * - * @since 6.0 - * - * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft - * @param \eZ\Publish\API\Repository\Values\User\PolicyDraft $policyDraft the policy to remove from the RoleDraft - * - * @return \eZ\Publish\API\Repository\Values\User\RoleDraft - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if policy does not belong to the given RoleDraft - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a policy - */ - public function removePolicyByRoleDraft(APIRoleDraft $roleDraft, PolicyDraft $policyDraft): APIRoleDraft - { - if (!$this->permissionResolver->canUser('role', 'update', $roleDraft)) { - throw new UnauthorizedException('role', 'update'); - } - - if ($policyDraft->roleId != $roleDraft->id) { - throw new InvalidArgumentException('$policy', 'The Policy does not belong to the given Role'); - } - - $this->internalDeletePolicy($policyDraft); - - return $this->loadRoleDraft($roleDraft->id); - } - - /** - * Updates the limitations of a policy. The module and function cannot be changed and - * the limitations are replaced by the ones in $roleUpdateStruct. - * - * @since 6.0 - * - * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft - * @param \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy - * @param \eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct $policyUpdateStruct - * - * @return \eZ\Publish\API\Repository\Values\User\PolicyDraft - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if limitation of the same type is repeated in policy update - * struct or if limitation is not allowed on module/function - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if a limitation in the $policyUpdateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update a policy - */ - public function updatePolicyByRoleDraft( - APIRoleDraft $roleDraft, - PolicyDraft $policy, - APIPolicyUpdateStruct $policyUpdateStruct - ): PolicyDraft { - if (!is_string($policy->module)) { - throw new InvalidArgumentValue('module', $policy->module, 'Policy'); - } - - if (!is_string($policy->function)) { - throw new InvalidArgumentValue('function', $policy->function, 'Policy'); - } - - if (!$this->permissionResolver->canUser('role', 'update', $roleDraft)) { - throw new UnauthorizedException('role', 'update'); - } - - if ($policy->roleId !== $roleDraft->id) { - throw new InvalidArgumentException('$policy', 'does not belong to the provided role draft'); - } - - $limitations = $policyUpdateStruct->getLimitations(); - $limitationValidationErrors = $this->validatePolicy( - $policy->module, - $policy->function, - $limitations - ); - if (!empty($limitationValidationErrors)) { - throw new LimitationValidationException($limitationValidationErrors); - } - - $spiPolicy = $this->roleDomainMapper->buildPersistencePolicyObject( - $policy->module, - $policy->function, - $limitations - ); - $spiPolicy->id = $policy->id; - $spiPolicy->roleId = $policy->roleId; - $spiPolicy->originalId = $policy->originalId; - - $this->repository->beginTransaction(); - try { - $this->userHandler->updatePolicy($spiPolicy); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->roleDomainMapper->buildDomainPolicyObject($spiPolicy); - } - - /** - * Deletes the given RoleDraft. - * - * @since 6.0 - * - * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete this RoleDraft - */ - public function deleteRoleDraft(APIRoleDraft $roleDraft): void - { - $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id); - - $this->repository->beginTransaction(); - try { - $this->userHandler->deleteRole($loadedRoleDraft->id, Role::STATUS_DRAFT); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Publishes a given RoleDraft. - * - * @since 6.0 - * - * @param \eZ\Publish\API\Repository\Values\User\RoleDraft $roleDraft - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the role draft cannot be loaded - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to publish this RoleDraft - */ - public function publishRoleDraft(APIRoleDraft $roleDraft): void - { - if (!$this->permissionResolver->canUser('role', 'update', $roleDraft)) { - throw new UnauthorizedException('role', 'update'); - } - - try { - $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id); - } catch (APINotFoundException $e) { - throw new BadStateException( - '$roleDraft', - 'The Role does not have a draft.', - $e - ); - } - - $this->repository->beginTransaction(); - try { - $this->userHandler->publishRoleDraft($loadedRoleDraft->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Loads a role for the given id. - * - * @param int $id - * - * @return \eZ\Publish\Core\Repository\Values\User\Role - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a role with the given id was not found - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role - */ - public function loadRole(int $id): APIRole - { - $spiRole = $this->userHandler->loadRole($id); - - $role = $this->roleDomainMapper->buildDomainRoleObject($spiRole); - - if (!$this->permissionResolver->canUser('role', 'read', $role)) { - throw new UnauthorizedException('role', 'read'); - } - - return $role; - } - - /** - * Loads a role for the given identifier. - * - * @param string $identifier - * - * @return \eZ\Publish\API\Repository\Values\User\Role - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a role with the given name was not found - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role - */ - public function loadRoleByIdentifier(string $identifier): APIRole - { - $spiRole = $this->userHandler->loadRoleByIdentifier($identifier); - - $role = $this->roleDomainMapper->buildDomainRoleObject($spiRole); - - if (!$this->permissionResolver->canUser('role', 'read', $role)) { - throw new UnauthorizedException('role', 'read'); - } - - return $role; - } - - /** - * Loads all roles, excluding the ones the current user is not allowed to read. - * - * @return \eZ\Publish\API\Repository\Values\User\Role[] - */ - public function loadRoles(): iterable - { - $roles = array_map( - function ($spiRole) { - return $this->roleDomainMapper->buildDomainRoleObject($spiRole); - }, - $this->userHandler->loadRoles() - ); - - return array_values( - array_filter( - $roles, - function ($role) { - return $this->permissionResolver->canUser('role', 'read', $role); - } - ) - ); - } - - /** - * Deletes the given role. - * - * @param \eZ\Publish\API\Repository\Values\User\Role $role - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete this role - */ - public function deleteRole(APIRole $role): void - { - if (!$this->permissionResolver->canUser('role', 'delete', $role)) { - throw new UnauthorizedException('role', 'delete'); - } - - $loadedRole = $this->loadRole($role->id); - - $this->repository->beginTransaction(); - try { - $this->userHandler->deleteRole($loadedRole->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Assigns a role to the given user group. - * - * @param \eZ\Publish\API\Repository\Values\User\Role $role - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * @param \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation $roleLimitation an optional role limitation (which is either a subtree limitation or section limitation) - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If assignment already exists - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign a role - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if $roleLimitation is not valid - */ - public function assignRoleToUserGroup(APIRole $role, UserGroup $userGroup, RoleLimitation $roleLimitation = null): void - { - if ($this->permissionResolver->canUser('role', 'assign', $userGroup, [$role]) !== true) { - throw new UnauthorizedException('role', 'assign'); - } - - if ($roleLimitation === null) { - $limitation = null; - } else { - $limitationValidationErrors = $this->limitationService->validateLimitation($roleLimitation); - if (!empty($limitationValidationErrors)) { - throw new LimitationValidationException($limitationValidationErrors); - } - - $limitation = [$roleLimitation->getIdentifier() => $roleLimitation->limitationValues]; - } - - // Check if objects exists - $spiRole = $this->userHandler->loadRole($role->id); - $loadedUserGroup = $this->repository->getUserService()->loadUserGroup($userGroup->id); - - $limitation = $this->checkAssignmentAndFilterLimitationValues($loadedUserGroup->id, $spiRole, $limitation); - - $this->repository->beginTransaction(); - try { - $this->userHandler->assignRole( - $loadedUserGroup->id, - $spiRole->id, - $limitation - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Assigns a role to the given user. - * - * @param \eZ\Publish\API\Repository\Values\User\Role $role - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation $roleLimitation an optional role limitation (which is either a subtree limitation or section limitation) - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If assignment already exists - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException if $roleLimitation is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign a role - */ - public function assignRoleToUser(APIRole $role, User $user, RoleLimitation $roleLimitation = null): void - { - if ($this->permissionResolver->canUser('role', 'assign', $user, [$role]) !== true) { - throw new UnauthorizedException('role', 'assign'); - } - - if ($roleLimitation === null) { - $limitation = null; - } else { - $limitationValidationErrors = $this->limitationService->validateLimitation($roleLimitation); - if (!empty($limitationValidationErrors)) { - throw new LimitationValidationException($limitationValidationErrors); - } - - $limitation = [$roleLimitation->getIdentifier() => $roleLimitation->limitationValues]; - } - - // Check if objects exists - $spiRole = $this->userHandler->loadRole($role->id); - $spiUser = $this->userHandler->load($user->id); - - $limitation = $this->checkAssignmentAndFilterLimitationValues($spiUser->id, $spiRole, $limitation); - - $this->repository->beginTransaction(); - try { - $this->userHandler->assignRole( - $spiUser->id, - $spiRole->id, - $limitation - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Removes the given role assignment. - * - * @param \eZ\Publish\API\Repository\Values\User\RoleAssignment $roleAssignment - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a role assignment - */ - public function removeRoleAssignment(RoleAssignment $roleAssignment): void - { - if ($this->permissionResolver->canUser('role', 'assign', $roleAssignment) !== true) { - throw new UnauthorizedException('role', 'assign'); - } - - $spiRoleAssignment = $this->userHandler->loadRoleAssignment($roleAssignment->id); - - $this->repository->beginTransaction(); - try { - $this->userHandler->removeRoleAssignment($spiRoleAssignment->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Loads a role assignment for the given id. - * - * @param int $roleAssignmentId - * - * @return \eZ\Publish\API\Repository\Values\User\RoleAssignment - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If the role assignment was not found - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role - */ - public function loadRoleAssignment(int $roleAssignmentId): RoleAssignment - { - $spiRoleAssignment = $this->userHandler->loadRoleAssignment($roleAssignmentId); - $userService = $this->repository->getUserService(); - $role = $this->loadRole($spiRoleAssignment->roleId); - - if (!$this->permissionResolver->canUser('role', 'read', $role)) { - throw new UnauthorizedException('role', 'read'); - } - - $roleAssignment = null; - - // First check if the Role is assigned to a User - // If no User is found, see if it belongs to a UserGroup - try { - $user = $userService->loadUser($spiRoleAssignment->contentId); - $roleAssignment = $this->roleDomainMapper->buildDomainUserRoleAssignmentObject( - $spiRoleAssignment, - $user, - $role - ); - } catch (APINotFoundException $e) { - try { - $userGroup = $userService->loadUserGroup($spiRoleAssignment->contentId); - $roleAssignment = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject( - $spiRoleAssignment, - $userGroup, - $role - ); - } catch (APINotFoundException $e) { - // Do nothing - } - } - - return $roleAssignment; - } - - /** - * @return \eZ\Publish\API\Repository\Values\User\RoleAssignment[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read a role - */ - public function getRoleAssignments(APIRole $role): iterable - { - if (!$this->permissionResolver->canUser('role', 'read', $role)) { - throw new UnauthorizedException('role', 'read'); - } - - $persistenceRoleAssignments = $this->userHandler->loadRoleAssignmentsByRoleId($role->id); - - return $this->buildRoleAssignmentsFromPersistence($role, $persistenceRoleAssignments); - } - - public function loadRoleAssignments(APIRole $role, int $offset = 0, ?int $limit = null): iterable - { - if (!$this->permissionResolver->canUser('role', 'read', $role)) { - throw new UnauthorizedException('role', 'read'); - } - - $persistenceRoleAssignments = $this->userHandler->loadRoleAssignmentsByRoleIdWithOffsetAndLimit( - $role->id, - $offset, - $limit - ); - - return $this->buildRoleAssignmentsFromPersistence($role, $persistenceRoleAssignments); - } - - /** - * @param array<int, \eZ\Publish\SPI\Persistence\User\RoleAssignment> $persistenceRoleAssignments - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - private function buildRoleAssignmentsFromPersistence( - APIRole $role, - array $persistenceRoleAssignments - ): array { - $userService = $this->repository->getUserService(); - - $roleAssignments = []; - foreach ($persistenceRoleAssignments as $persistenceRoleAssignment) { - // First check if the Role is assigned to a User - // If no User is found, see if it belongs to a UserGroup - try { - $user = $userService->loadUser($persistenceRoleAssignment->contentId); - $roleAssignments[] = $this->roleDomainMapper->buildDomainUserRoleAssignmentObject( - $persistenceRoleAssignment, - $user, - $role - ); - } catch (APINotFoundException $e) { - try { - $userGroup = $userService->loadUserGroup($persistenceRoleAssignment->contentId); - $roleAssignments[] = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject( - $persistenceRoleAssignment, - $userGroup, - $role - ); - } catch (APINotFoundException $e) { - // Do nothing - } - } - } - - return $roleAssignments; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read a role - */ - public function countRoleAssignments(APIRole $role): int - { - if (!$this->permissionResolver->canUser('role', 'read', $role)) { - throw new UnauthorizedException('role', 'read'); - } - - // Skipping building domain user role assignment object as done in `buildRoleAssignmentsFromPersistence` - // due to inner joining user content which is sufficient in this case - return $this->userHandler->countRoleAssignments($role->id); - } - - /** - * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignmentsForUser() - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param bool $inherited - * - * @return iterable - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function getRoleAssignmentsForUser(User $user, bool $inherited = false): iterable - { - $roleAssignments = []; - $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($user->id, $inherited); - foreach ($spiRoleAssignments as $spiRoleAssignment) { - $role = $this->loadRole($spiRoleAssignment->roleId); - - if (!$this->permissionResolver->canUser('role', 'read', $role)) { - continue; - } - - if (!$inherited || $spiRoleAssignment->contentId == $user->id) { - $roleAssignments[] = $this->roleDomainMapper->buildDomainUserRoleAssignmentObject( - $spiRoleAssignment, - $user, - $role - ); - } else { - $userGroup = $this->repository->getUserService()->loadUserGroup($spiRoleAssignment->contentId); - $roleAssignments[] = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject( - $spiRoleAssignment, - $userGroup, - $role - ); - } - } - - return $roleAssignments; - } - - /** - * Returns the roles assigned to the given user group, excluding the ones the current user is not allowed to read. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroupRoleAssignment[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function getRoleAssignmentsForUserGroup(UserGroup $userGroup): iterable - { - $roleAssignments = []; - $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($userGroup->id); - foreach ($spiRoleAssignments as $spiRoleAssignment) { - $role = $this->loadRole($spiRoleAssignment->roleId); - - if ($this->permissionResolver->canUser('role', 'read', $role)) { - $roleAssignments[] = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject( - $spiRoleAssignment, - $userGroup, - $role - ); - } - } - - return $roleAssignments; - } - - /** - * Instantiates a role create class. - * - * @param string $name - * - * @return \eZ\Publish\API\Repository\Values\User\RoleCreateStruct - */ - public function newRoleCreateStruct(string $name): APIRoleCreateStruct - { - return new RoleCreateStruct( - [ - 'identifier' => $name, - 'policies' => [], - ] - ); - } - - public function newRoleCopyStruct(string $name): APIRoleCopyStruct - { - return new RoleCopyStruct( - [ - 'newIdentifier' => $name, - 'policies' => [], - ] - ); - } - - /** - * Instantiates a policy create class. - * - * @param string $module - * @param string $function - * - * @return \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct - */ - public function newPolicyCreateStruct(string $module, string $function): APIPolicyCreateStruct - { - return new PolicyCreateStruct( - [ - 'module' => $module, - 'function' => $function, - 'limitations' => [], - ] - ); - } - - /** - * Instantiates a policy update class. - * - * @return \eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct - */ - public function newPolicyUpdateStruct(): APIPolicyUpdateStruct - { - return new PolicyUpdateStruct( - [ - 'limitations' => [], - ] - ); - } - - /** - * Instantiates a policy update class. - * - * @return \eZ\Publish\API\Repository\Values\User\RoleUpdateStruct - */ - public function newRoleUpdateStruct(): RoleUpdateStruct - { - return new RoleUpdateStruct(); - } - - /** - * Returns the LimitationType registered with the given identifier. - * - * Returns the correct implementation of API Limitation value object - * based on provided identifier - * - * @param string $identifier - * - * @return \eZ\Publish\SPI\Limitation\Type - * - * @throws \RuntimeException if there is no LimitationType with $identifier - */ - public function getLimitationType(string $identifier): Type - { - return $this->limitationService->getLimitationType($identifier); - } - - /** - * Returns the LimitationType's assigned to a given module/function. - * - * Typically used for: - * - Internal validation limitation value use on Policies - * - Role admin gui for editing policy limitations incl list limitation options via valueSchema() - * - * @param string $module Legacy name of "controller", it's a unique identifier like "content" - * @param string $function Legacy name of a controller "action", it's a unique within the controller like "read" - * - * @return \eZ\Publish\SPI\Limitation\Type[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If module/function to limitation type mapping - * refers to a non existing identifier. - */ - public function getLimitationTypesByModuleFunction(string $module, string $function): iterable - { - if (empty($this->settings['policyMap'][$module][$function])) { - return []; - } - - $types = []; - try { - foreach (array_keys($this->settings['policyMap'][$module][$function]) as $identifier) { - $types[$identifier] = $this->limitationService->getLimitationType($identifier); - } - } catch (LimitationNotFoundException $e) { - throw new BadStateException( - "{$module}/{$function}", - "policyMap configuration is referring to a non-existent identifier: {$identifier}", - $e - ); - } - - return $types; - } - - /** - * Validates Policies and Limitations in Role create struct. - * - * @uses ::validatePolicy() - * - * @param \eZ\Publish\API\Repository\Values\User\RoleCreateStruct $roleCreateStruct - * - * @return \eZ\Publish\Core\FieldType\ValidationError[][][] - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - protected function validateRoleCreateStruct(APIRoleCreateStruct $roleCreateStruct): iterable - { - $allErrors = []; - foreach ($roleCreateStruct->getPolicies() as $key => $policyCreateStruct) { - $errors = $this->validatePolicy( - $policyCreateStruct->module, - $policyCreateStruct->function, - $policyCreateStruct->getLimitations() - ); - - if (!empty($errors)) { - $allErrors[$key] = $errors; - } - } - - return $allErrors; - } - - /** - * Validates Policy context: Limitations on a module and function. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the same limitation is repeated or if - * limitation is not allowed on module/function - * - * @param string $module - * @param string $function - * @param \eZ\Publish\API\Repository\Values\User\Limitation[] $limitations - * - * @return \eZ\Publish\Core\FieldType\ValidationError[][] - */ - protected function validatePolicy(string $module, string $function, array $limitations): iterable - { - if ($module !== '*' && $function !== '*' && !empty($limitations)) { - $limitationSet = []; - foreach ($limitations as $limitation) { - if (isset($limitationSet[$limitation->getIdentifier()])) { - throw new InvalidArgumentException( - 'limitations', - "{$limitation->getIdentifier()}' was found multiple times among Limitations" - ); - } - - if (!isset($this->settings['policyMap'][$module][$function][$limitation->getIdentifier()])) { - throw new InvalidArgumentException( - 'policy', - "Limitation '{$limitation->getIdentifier()}' is not applicable on '{$module}/{$function}'" - ); - } - - $limitationSet[$limitation->getIdentifier()] = true; - } - } - - return $this->limitationService->validateLimitations($limitations); - } - - /** - * Validate that assignments not already exists and filter validations against existing. - * - * @param int $contentId - * @param \eZ\Publish\SPI\Persistence\User\Role $spiRole - * @param array|null $limitation - * - * @return array[]|null Filtered version of $limitation - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If assignment already exists - */ - protected function checkAssignmentAndFilterLimitationValues( - int $contentId, - SPIRole $spiRole, - ?array $limitation = null - ): ?array { - $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($contentId); - foreach ($spiRoleAssignments as $spiAssignment) { - // Ignore assignments to other roles - if ($spiAssignment->roleId !== $spiRole->id) { - continue; - } - - // Throw if Role is already assigned without limitations - if ($spiAssignment->limitationIdentifier === null) { - throw new InvalidArgumentException( - '$role', - "Role '{$spiRole->id}' already assigned without Limitations" - ); - } - - // Ignore if we are going to assign without limitations - if ($limitation === null) { - continue; - } - - // Ignore if not assigned with same limitation identifier - if (!isset($limitation[$spiAssignment->limitationIdentifier])) { - continue; - } - - // Throw if Role is already assigned with all the same limitations - $newValues = array_diff($limitation[$spiAssignment->limitationIdentifier], $spiAssignment->values); - if (empty($newValues)) { - throw new InvalidArgumentException( - '$role', - "Role '{$spiRole->id}' is already assigned with the same '{$spiAssignment->limitationIdentifier}' value" - ); - } - - // Continue using the filtered list of limitations - $limitation[$spiAssignment->limitationIdentifier] = $newValues; - } - - return $limitation; - } - - /** - * Deletes a policy. - * - * Used by {@link removePolicy()} and {@link deletePolicy()} - * - * @param \eZ\Publish\API\Repository\Values\User\Policy $policy - * - * @throws \Exception - */ - protected function internalDeletePolicy(APIPolicy $policy): void - { - $this->repository->beginTransaction(); - try { - $this->userHandler->deletePolicy($policy->id, $policy->roleId); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } -} diff --git a/eZ/Publish/Core/Repository/SearchService.php b/eZ/Publish/Core/Repository/SearchService.php deleted file mode 100644 index db182baa30..0000000000 --- a/eZ/Publish/Core/Repository/SearchService.php +++ /dev/null @@ -1,353 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use eZ\Publish\API\Repository\PermissionCriterionResolver; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\SearchService as SearchServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location as LocationCriterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOperator; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location as LocationSortClause; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Repository\Mapper\ContentDomainMapper; -use eZ\Publish\Core\Search\Common\BackgroundIndexer; -use eZ\Publish\SPI\Search\Capable; -use eZ\Publish\SPI\Search\Handler; - -/** - * Search service. - */ -class SearchService implements SearchServiceInterface -{ - /** @var \eZ\Publish\Core\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\SPI\Search\Handler */ - protected $searchHandler; - - /** @var array */ - protected $settings; - - /** @var \eZ\Publish\Core\Repository\Mapper\ContentDomainMapper */ - protected $contentDomainMapper; - - /** @var \eZ\Publish\API\Repository\PermissionCriterionResolver */ - protected $permissionCriterionResolver; - - /** @var \eZ\Publish\Core\Search\Common\BackgroundIndexer */ - protected $backgroundIndexer; - - /** - * Setups service with reference to repository object that created it & corresponding handler. - * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\SPI\Search\Handler $searchHandler - * @param \eZ\Publish\Core\Repository\Mapper\ContentDomainMapper $contentDomainMapper - * @param \eZ\Publish\API\Repository\PermissionCriterionResolver $permissionCriterionResolver - * @param \eZ\Publish\Core\Search\Common\BackgroundIndexer $backgroundIndexer - * @param array $settings - */ - public function __construct( - RepositoryInterface $repository, - Handler $searchHandler, - ContentDomainMapper $contentDomainMapper, - PermissionCriterionResolver $permissionCriterionResolver, - BackgroundIndexer $backgroundIndexer, - array $settings = [] - ) { - $this->repository = $repository; - $this->searchHandler = $searchHandler; - $this->contentDomainMapper = $contentDomainMapper; - // Union makes sure default settings are ignored if provided in argument - $this->settings = $settings + [ - //'defaultSetting' => array(), - ]; - $this->permissionCriterionResolver = $permissionCriterionResolver; - $this->backgroundIndexer = $backgroundIndexer; - } - - /** - * Finds content objects for the given query. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid - * - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code> - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations. - * @param bool $filterOnUserPermissions if true only the objects which the user is allowed to read are returned. - * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult - */ - public function findContent(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult - { - $result = $this->internalFindContentInfo($query, $languageFilter, $filterOnUserPermissions); - $missingContentList = $this->contentDomainMapper->buildContentDomainObjectsOnSearchResult($result, $languageFilter); - foreach ($missingContentList as $missingContent) { - $this->backgroundIndexer->registerContent($missingContent); - } - - return $result; - } - - /** - * Finds contentInfo objects for the given query. - * - * @see SearchServiceInterface::findContentInfo() - * @since 5.4.5 - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid - * - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - * @param array $languageFilter - a map of filters for the returned fields. - * Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code> - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations. - * @param bool $filterOnUserPermissions if true (default) only the objects which is the user allowed to read are returned. - * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult - */ - public function findContentInfo(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult - { - $result = $this->internalFindContentInfo($query, $languageFilter, $filterOnUserPermissions); - foreach ($result->searchHits as $hit) { - $hit->valueObject = $this->contentDomainMapper->buildContentInfoDomainObject( - $hit->valueObject - ); - } - - return $result; - } - - /** - * Finds SPI content info objects for the given query. - * - * Internal for use by {@link findContent} and {@link findContentInfo}. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid - * - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - * @param array $languageFilter - a map of filters for the returned fields. - * Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code> - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations. - * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. - * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult With "raw" SPI contentInfo objects in result - */ - protected function internalFindContentInfo(Query $query, array $languageFilter = [], $filterOnUserPermissions = true) - { - if (!is_int($query->offset)) { - throw new InvalidArgumentType( - '$query->offset', - 'integer', - $query->offset - ); - } - - if (!is_int($query->limit)) { - throw new InvalidArgumentType( - '$query->limit', - 'integer', - $query->limit - ); - } - - $query = clone $query; - $query->filter = $query->filter ?: new Criterion\MatchAll(); - - $this->validateContentCriteria([$query->query], '$query'); - $this->validateContentCriteria([$query->filter], '$query'); - $this->validateContentSortClauses($query); - - if ($filterOnUserPermissions && !$this->addPermissionsCriterion($query->filter)) { - return new SearchResult(['time' => 0, 'totalCount' => 0]); - } - - return $this->searchHandler->findContent($query, $languageFilter); - } - - /** - * Checks that $criteria does not contain Location criterions. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion[] $criteria - * @param string $argumentName - */ - protected function validateContentCriteria(array $criteria, $argumentName) - { - foreach ($criteria as $criterion) { - if ($criterion instanceof LocationCriterion) { - throw new InvalidArgumentException( - $argumentName, - 'Location Criteria cannot be used in Content search' - ); - } - if ($criterion instanceof LogicalOperator) { - $this->validateContentCriteria($criterion->criteria, $argumentName); - } - } - } - - /** - * Checks that $query does not contain Location sort clauses. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - */ - protected function validateContentSortClauses(Query $query) - { - foreach ($query->sortClauses as $sortClause) { - if ($sortClause instanceof LocationSortClause) { - throw new InvalidArgumentException('$query', 'Location Sort Clauses cannot be used in Content search'); - } - } - } - - /** - * Performs a query for a single content object. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if criterion is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is more than one result matching the criterions - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code> - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations. - * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - public function findSingle(Criterion $filter, array $languageFilter = [], bool $filterOnUserPermissions = true): Content - { - $this->validateContentCriteria([$filter], '$filter'); - - if ($filterOnUserPermissions && !$this->addPermissionsCriterion($filter)) { - throw new NotFoundException('Content', '*'); - } - - $contentInfo = $this->searchHandler->findSingle($filter, $languageFilter); - - return $this->repository->getContentService()->internalLoadContentById( - $contentInfo->id, - (!empty($languageFilter['languages']) ? $languageFilter['languages'] : null), - null, - (isset($languageFilter['useAlwaysAvailable']) ? $languageFilter['useAlwaysAvailable'] : true) - ); - } - - /** - * Suggests a list of values for the given prefix. - * - * @param string $prefix - * @param string[] $fieldPaths - * @param int $limit - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter - */ - public function suggest(string $prefix, array $fieldPaths = [], int $limit = 10, Criterion $filter = null) - { - } - - /** - * Finds Locations for the given query. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if query is not valid - * - * @param \eZ\Publish\API\Repository\Values\Content\LocationQuery $query - * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. - * Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code> - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. - * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult - */ - public function findLocations(LocationQuery $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult - { - if (!is_int($query->offset)) { - throw new InvalidArgumentType( - '$query->offset', - 'integer', - $query->offset - ); - } - - if (!is_int($query->limit)) { - throw new InvalidArgumentType( - '$query->limit', - 'integer', - $query->limit - ); - } - - $query = clone $query; - $query->filter = $query->filter ?: new Criterion\MatchAll(); - - if ($filterOnUserPermissions && !$this->addPermissionsCriterion($query->filter)) { - return new SearchResult(['time' => 0, 'totalCount' => 0]); - } - - $result = $this->searchHandler->findLocations($query, $languageFilter); - - $missingLocations = $this->contentDomainMapper->buildLocationDomainObjectsOnSearchResult($result, $languageFilter); - foreach ($missingLocations as $missingLocation) { - $this->backgroundIndexer->registerLocation($missingLocation); - } - - return $result; - } - - /** - * Adds content, read Permission criteria if needed and return false if no access at all. - * - * @uses \eZ\Publish\API\Repository\PermissionCriterionResolver::getPermissionsCriterion() - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool|\eZ\Publish\API\Repository\Values\Content\Query\Criterion - */ - protected function addPermissionsCriterion(Criterion &$criterion) - { - $permissionCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'read'); - if ($permissionCriterion === true || $permissionCriterion === false) { - return $permissionCriterion; - } - - // Merge with original $criterion - if ($criterion instanceof LogicalAnd) { - $criterion->criteria[] = $permissionCriterion; - } else { - $criterion = new LogicalAnd( - [ - $criterion, - $permissionCriterion, - ] - ); - } - - return true; - } - - public function supports(int $capabilityFlag): bool - { - if ($this->searchHandler instanceof Capable) { - return $this->searchHandler->supports($capabilityFlag); - } - - return false; - } -} diff --git a/eZ/Publish/Core/Repository/SectionService.php b/eZ/Publish/Core/Repository/SectionService.php deleted file mode 100644 index 1b9ec5b13d..0000000000 --- a/eZ/Publish/Core/Repository/SectionService.php +++ /dev/null @@ -1,458 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use function array_filter; -use Exception; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\PermissionCriterionResolver; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\SectionService as SectionServiceInterface; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd as CriterionLogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot as CriterionLogicalNot; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree as CriterionSubtree; -use eZ\Publish\API\Repository\Values\Content\Section; -use eZ\Publish\API\Repository\Values\Content\SectionCreateStruct; -use eZ\Publish\API\Repository\Values\Content\SectionUpdateStruct; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as LocationHandler; -use eZ\Publish\SPI\Persistence\Content\Section as SPISection; -use eZ\Publish\SPI\Persistence\Content\Section\Handler as SectionHandler; - -/** - * Section service, used for section operations. - */ -class SectionService implements SectionServiceInterface -{ - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - protected $permissionResolver; - - /** @var \eZ\Publish\API\Repository\PermissionCriterionResolver */ - protected $permissionCriterionResolver; - - /** @var \eZ\Publish\SPI\Persistence\Content\Section\Handler */ - protected $sectionHandler; - - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Handler */ - protected $locationHandler; - - /** @var array */ - protected $settings; - - /** - * Setups service with reference to repository object that created it & corresponding handler. - * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\SPI\Persistence\Content\Section\Handler $sectionHandler - * @param \eZ\Publish\SPI\Persistence\Content\Location\Handler $locationHandler - * @param \eZ\Publish\API\Repository\PermissionCriterionResolver $permissionCriterionResolver - * @param array $settings - */ - public function __construct(RepositoryInterface $repository, SectionHandler $sectionHandler, LocationHandler $locationHandler, PermissionCriterionResolver $permissionCriterionResolver, array $settings = []) - { - $this->repository = $repository; - $this->sectionHandler = $sectionHandler; - $this->locationHandler = $locationHandler; - $this->permissionResolver = $repository->getPermissionResolver(); - $this->permissionCriterionResolver = $permissionCriterionResolver; - // Union makes sure default settings are ignored if provided in argument - $this->settings = $settings + [ - //'defaultSetting' => array(), - ]; - } - - /** - * Creates a new Section in the content repository. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to create a section - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the new identifier in $sectionCreateStruct already exists - * - * @param \eZ\Publish\API\Repository\Values\Content\SectionCreateStruct $sectionCreateStruct - * - * @return \eZ\Publish\API\Repository\Values\Content\Section The newly created section - */ - public function createSection(SectionCreateStruct $sectionCreateStruct): Section - { - if (!is_string($sectionCreateStruct->name) || empty($sectionCreateStruct->name)) { - throw new InvalidArgumentValue('name', $sectionCreateStruct->name, 'SectionCreateStruct'); - } - - if (!is_string($sectionCreateStruct->identifier) || empty($sectionCreateStruct->identifier)) { - throw new InvalidArgumentValue('identifier', $sectionCreateStruct->identifier, 'SectionCreateStruct'); - } - - if (!$this->permissionResolver->canUser('section', 'edit', $sectionCreateStruct)) { - throw new UnauthorizedException('section', 'edit'); - } - - try { - $existingSection = $this->sectionHandler->loadByIdentifier($sectionCreateStruct->identifier); - if ($existingSection !== null) { - throw new InvalidArgumentException('sectionCreateStruct', 'A Section with the specified identifier already exists'); - } - } catch (APINotFoundException $e) { - // Do nothing - } - - $this->repository->beginTransaction(); - try { - $spiSection = $this->sectionHandler->create( - $sectionCreateStruct->name, - $sectionCreateStruct->identifier - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->buildDomainSectionObject($spiSection); - } - - /** - * Updates the given section in the content repository. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to create a section - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the new identifier already exists (if set in the update struct) - * - * @param \eZ\Publish\API\Repository\Values\Content\Section $section - * @param \eZ\Publish\API\Repository\Values\Content\SectionUpdateStruct $sectionUpdateStruct - * - * @return \eZ\Publish\API\Repository\Values\Content\Section - */ - public function updateSection(Section $section, SectionUpdateStruct $sectionUpdateStruct): Section - { - if ($sectionUpdateStruct->name !== null && !is_string($sectionUpdateStruct->name)) { - throw new InvalidArgumentValue('name', $section->name, 'Section'); - } - - if ($sectionUpdateStruct->identifier !== null && !is_string($sectionUpdateStruct->identifier)) { - throw new InvalidArgumentValue('identifier', $section->identifier, 'Section'); - } - - if (!$this->permissionResolver->canUser('section', 'edit', $section)) { - throw new UnauthorizedException('section', 'edit'); - } - - if ($sectionUpdateStruct->identifier !== null) { - try { - $existingSection = $this->sectionHandler->loadByIdentifier($sectionUpdateStruct->identifier); - - // Allowing identifier update only for the same section - if ($existingSection->id != $section->id) { - throw new InvalidArgumentException('sectionUpdateStruct', 'A Section with the specified identifier already exists'); - } - } catch (APINotFoundException $e) { - // Do nothing - } - } - - $loadedSection = $this->sectionHandler->load($section->id); - - $this->repository->beginTransaction(); - try { - $spiSection = $this->sectionHandler->update( - $loadedSection->id, - $sectionUpdateStruct->name ?: $loadedSection->name, - $sectionUpdateStruct->identifier ?: $loadedSection->identifier - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->buildDomainSectionObject($spiSection); - } - - /** - * Loads a Section from its id ($sectionId). - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if section could not be found - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to read a section - * - * @param int $sectionId - * - * @return \eZ\Publish\API\Repository\Values\Content\Section - */ - public function loadSection(int $sectionId): Section - { - $section = $this->buildDomainSectionObject( - $this->sectionHandler->load($sectionId) - ); - - if (!$this->permissionResolver->canUser('section', 'view', $section)) { - throw new UnauthorizedException('section', 'view'); - } - - return $section; - } - - /** - * Loads all sections, excluding the ones the current user is not allowed to read. - * - * @return \eZ\Publish\API\Repository\Values\Content\Section[] - */ - public function loadSections(): iterable - { - $sections = array_map(function ($spiSection) { - return $this->buildDomainSectionObject($spiSection); - }, $this->sectionHandler->loadAll()); - - return array_values(array_filter($sections, function ($section) { - return $this->permissionResolver->canUser('section', 'view', $section); - })); - } - - /** - * Loads a Section from its identifier ($sectionIdentifier). - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if section could not be found - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to read a section - * - * @param string $sectionIdentifier - * - * @return \eZ\Publish\API\Repository\Values\Content\Section - */ - public function loadSectionByIdentifier(string $sectionIdentifier): Section - { - if (empty($sectionIdentifier)) { - throw new InvalidArgumentValue('sectionIdentifier', $sectionIdentifier); - } - - $section = $this->buildDomainSectionObject( - $this->sectionHandler->loadByIdentifier($sectionIdentifier) - ); - - if (!$this->permissionResolver->canUser('section', 'view', $section)) { - throw new UnauthorizedException('section', 'view'); - } - - return $section; - } - - /** - * Counts the contents which $section is assigned to. - * - * @param \eZ\Publish\API\Repository\Values\Content\Section $section - * - * @return int - * - * @deprecated since 6.0 - */ - public function countAssignedContents(Section $section): int - { - return $this->sectionHandler->assignmentsCount($section->id); - } - - /** - * Returns true if the given section is assigned to contents, or used in role policies, or in role assignments. - * - * This does not check user permissions. - * - * @since 6.0 - * - * @param \eZ\Publish\API\Repository\Values\Content\Section $section - * - * @return bool - */ - public function isSectionUsed(Section $section): bool - { - return $this->sectionHandler->assignmentsCount($section->id) > 0 || - $this->sectionHandler->policiesCount($section->id) > 0 || - $this->sectionHandler->countRoleAssignmentsUsingSection($section->id) > 0; - } - - /** - * Assigns the content to the given section - * this method overrides the current assigned section. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If user does not have access to view provided object - * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param \eZ\Publish\API\Repository\Values\Content\Section $section - */ - public function assignSection(ContentInfo $contentInfo, Section $section): void - { - $loadedContentInfo = $this->repository->getContentService()->loadContentInfo($contentInfo->id); - $loadedSection = $this->loadSection($section->id); - - if (!$this->permissionResolver->canUser('section', 'assign', $loadedContentInfo, [$loadedSection])) { - throw new UnauthorizedException( - 'section', - 'assign', - [ - 'name' => $loadedSection->name, - 'content-name' => $loadedContentInfo->name, - ] - ); - } - - $this->repository->beginTransaction(); - try { - $this->sectionHandler->assign( - $loadedSection->id, - $loadedContentInfo->id - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Assigns the subtree to the given section - * this method overrides the current assigned section. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param \eZ\Publish\API\Repository\Values\Content\Section $section - */ - public function assignSectionToSubtree(Location $location, Section $section): void - { - $loadedSubtree = $this->repository->getLocationService()->loadLocation($location->id); - $loadedSection = $this->loadSection($section->id); - - /** - * Check read access to whole source subtree. - * - * @var bool|\eZ\Publish\API\Repository\Values\Content\Query\Criterion - */ - $sectionAssignCriterion = $this->permissionCriterionResolver->getPermissionsCriterion( - 'section', - 'assign', - [$loadedSection] - ); - if ($sectionAssignCriterion === false) { - throw new UnauthorizedException('section', 'assign', [ - 'name' => $loadedSection->name, - 'subtree' => $loadedSubtree->pathString, - ]); - } elseif ($sectionAssignCriterion !== true) { - // Query if there are any content in subtree current user don't have access to - $query = new Query( - [ - 'limit' => 0, - 'filter' => new CriterionLogicalAnd( - [ - new CriterionSubtree($loadedSubtree->pathString), - new CriterionLogicalNot($sectionAssignCriterion), - ] - ), - ] - ); - - $result = $this->repository->getSearchService()->findContent($query, [], false); - if ($result->totalCount > 0) { - throw new UnauthorizedException('section', 'assign', [ - 'name' => $loadedSection->name, - 'subtree' => $loadedSubtree->pathString, - ]); - } - } - - $this->repository->beginTransaction(); - try { - $this->locationHandler->setSectionForSubtree( - $loadedSubtree->id, - $loadedSection->id - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Deletes $section from content repository. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If the specified section is not found - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException If the current user is not allowed to delete a section - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If section can not be deleted - * because it is still assigned to some contents, - * or because it is still being used in policy limitations. - * - * @param \eZ\Publish\API\Repository\Values\Content\Section $section - */ - public function deleteSection(Section $section): void - { - $loadedSection = $this->loadSection($section->id); - - if (!$this->permissionResolver->canUser('section', 'edit', $loadedSection)) { - throw new UnauthorizedException('section', 'edit', ['sectionId' => $loadedSection->id]); - } - - if ($this->sectionHandler->assignmentsCount($loadedSection->id) > 0) { - throw new BadStateException('section', 'The Section still has content assigned'); - } - - if ($this->sectionHandler->policiesCount($loadedSection->id) > 0) { - throw new BadStateException('section', 'the Section is still being used in Policy Limitations'); - } - - $this->repository->beginTransaction(); - try { - $this->sectionHandler->delete($loadedSection->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Instantiates a new SectionCreateStruct. - * - * @return \eZ\Publish\API\Repository\Values\Content\SectionCreateStruct - */ - public function newSectionCreateStruct(): SectionCreateStruct - { - return new SectionCreateStruct(); - } - - /** - * Instantiates a new SectionUpdateStruct. - * - * @return \eZ\Publish\API\Repository\Values\Content\SectionUpdateStruct - */ - public function newSectionUpdateStruct(): SectionUpdateStruct - { - return new SectionUpdateStruct(); - } - - /** - * Builds API Section object from provided SPI Section object. - * - * @param \eZ\Publish\SPI\Persistence\Content\Section $spiSection - * - * @return \eZ\Publish\API\Repository\Values\Content\Section - */ - protected function buildDomainSectionObject(SPISection $spiSection) - { - return new Section( - [ - 'id' => $spiSection->id, - 'identifier' => $spiSection->identifier, - 'name' => $spiSection->name, - ] - ); - } -} diff --git a/eZ/Publish/Core/Repository/SettingService.php b/eZ/Publish/Core/Repository/SettingService.php deleted file mode 100644 index 8dd2d7b950..0000000000 --- a/eZ/Publish/Core/Repository/SettingService.php +++ /dev/null @@ -1,116 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\SettingService as SettingServiceInterface; -use eZ\Publish\API\Repository\Values\Setting\Setting; -use eZ\Publish\API\Repository\Values\Setting\SettingCreateStruct; -use eZ\Publish\API\Repository\Values\Setting\SettingUpdateStruct; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\SPI\Persistence\Setting\Handler as SettingHandler; -use eZ\Publish\SPI\Persistence\Setting\Setting as SPISetting; - -final class SettingService implements SettingServiceInterface -{ - /** @var \eZ\Publish\SPI\Persistence\Setting\Handler */ - private $settingHandler; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionResolver; - - public function __construct( - SettingHandler $settingHandler, - PermissionResolver $permissionResolver - ) { - $this->settingHandler = $settingHandler; - $this->permissionResolver = $permissionResolver; - } - - public function loadSetting(string $group, string $identifier): Setting - { - return $this->buildSettingDomainObject( - $this->settingHandler->load($group, $identifier) - ); - } - - public function updateSetting(Setting $setting, SettingUpdateStruct $settingUpdateStruct): Setting - { - if (!$this->permissionResolver->canUser('setting', 'update', $setting)) { - throw new UnauthorizedException('setting', 'update'); - } - - return $this->buildSettingDomainObject( - $this->settingHandler->update( - $setting->group, - $setting->identifier, - json_encode($settingUpdateStruct->value) - ) - ); - } - - public function createSetting(SettingCreateStruct $settingCreateStruct): Setting - { - if (!$this->permissionResolver->canUser('setting', 'create', $settingCreateStruct)) { - throw new UnauthorizedException('setting', 'create'); - } - - try { - $existingSetting = $this->settingHandler->load($settingCreateStruct->group, $settingCreateStruct->identifier); - if ($existingSetting !== null) { - throw new InvalidArgumentException('settingCreateStruct', 'A Setting with the specified group and identifier already exists'); - } - } catch (APINotFoundException $e) { - // Do nothing - } - - return $this->buildSettingDomainObject( - $this->settingHandler->create( - $settingCreateStruct->group, - $settingCreateStruct->identifier, - json_encode($settingCreateStruct->value) - ) - ); - } - - public function deleteSetting(Setting $setting): void - { - if (!$this->permissionResolver->canUser('setting', 'remove', $setting)) { - throw new UnauthorizedException('setting', 'remove'); - } - - $existingSetting = $this->settingHandler->load($setting->group, $setting->identifier); - - $this->settingHandler->delete( - $existingSetting->group, - $existingSetting->identifier - ); - } - - public function newSettingCreateStruct(array $properties = []): SettingCreateStruct - { - return new SettingCreateStruct($properties); - } - - public function newSettingUpdateStruct(array $properties = []): SettingUpdateStruct - { - return new SettingUpdateStruct($properties); - } - - private function buildSettingDomainObject(SPISetting $setting): Setting - { - return new Setting([ - 'group' => $setting->group, - 'identifier' => $setting->identifier, - 'value' => json_decode($setting->serializedValue, true), - ]); - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Config/IOConfigResolver.php b/eZ/Publish/Core/Repository/SiteAccessAware/Config/IOConfigResolver.php deleted file mode 100644 index f43741ef01..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Config/IOConfigResolver.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\SiteAccessAware\Config; - -use eZ\Publish\Core\IO\IOConfigProvider; - -/** - * @internal - */ -final class IOConfigResolver implements IOConfigProvider -{ - /** @var string */ - private $storageDir; - - /** @var string */ - private $legacyUrlPrefix; - - /** @var string */ - private $urlPrefix; - - public function __construct( - string $storageDir, - string $legacyUrlPrefix, - string $urlPrefix - ) { - $this->storageDir = $storageDir; - $this->legacyUrlPrefix = $legacyUrlPrefix; - $this->urlPrefix = $urlPrefix; - } - - public function getRootDir(): string - { - return $this->storageDir; - } - - public function getLegacyUrlPrefix(): string - { - return $this->legacyUrlPrefix; - } - - public function getUrlPrefix(): string - { - return $this->urlPrefix; - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/ContentService.php b/eZ/Publish/Core/Repository/SiteAccessAware/ContentService.php deleted file mode 100644 index 3c76b09317..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/ContentService.php +++ /dev/null @@ -1,294 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\SiteAccessAware; - -use eZ\Publish\API\Repository\ContentService as ContentServiceInterface; -use eZ\Publish\API\Repository\LanguageResolver; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentDraftList; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\ContentList; -use eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\Content\RelationList; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\ValueObject; - -/** - * SiteAccess aware implementation of ContentService injecting languages where needed. - */ -class ContentService implements ContentServiceInterface -{ - /** @var \eZ\Publish\API\Repository\ContentService */ - protected $service; - - /** @var \eZ\Publish\API\Repository\LanguageResolver */ - protected $languageResolver; - - /** - * Construct service object from aggregated service and LanguageResolver. - * - * @param \eZ\Publish\API\Repository\ContentService $service - * @param \eZ\Publish\API\Repository\LanguageResolver $languageResolver - */ - public function __construct( - ContentServiceInterface $service, - LanguageResolver $languageResolver - ) { - $this->service = $service; - $this->languageResolver = $languageResolver; - } - - public function loadContentInfo(int $contentId): ContentInfo - { - return $this->service->loadContentInfo($contentId); - } - - /** - * {@inheritdoc} - */ - public function loadContentInfoList(array $contentIds): iterable - { - return $this->service->loadContentInfoList($contentIds); - } - - public function loadContentInfoByRemoteId(string $remoteId): ContentInfo - { - return $this->service->loadContentInfoByRemoteId($remoteId); - } - - public function loadVersionInfo(ContentInfo $contentInfo, ?int $versionNo = null): VersionInfo - { - return $this->service->loadVersionInfo($contentInfo, $versionNo); - } - - public function loadVersionInfoById(int $contentId, ?int $versionNo = null): VersionInfo - { - return $this->service->loadVersionInfoById($contentId, $versionNo); - } - - public function loadVersionInfoListByContentInfo(array $contentInfoList): array - { - return $this->service->loadVersionInfoListByContentInfo($contentInfoList); - } - - public function loadContentByContentInfo(ContentInfo $contentInfo, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content - { - return $this->service->loadContentByContentInfo( - $contentInfo, - $this->languageResolver->getPrioritizedLanguages($languages), - $versionNo, - $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) - ); - } - - public function loadContentByVersionInfo(VersionInfo $versionInfo, array $languages = null, bool $useAlwaysAvailable = true): Content - { - return $this->service->loadContentByVersionInfo( - $versionInfo, - $this->languageResolver->getPrioritizedLanguages($languages), - $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) - ); - } - - public function loadContent(int $contentId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content - { - return $this->service->loadContent( - $contentId, - $this->languageResolver->getPrioritizedLanguages($languages), - $versionNo, - $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) - ); - } - - public function loadContentByRemoteId(string $remoteId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content - { - return $this->service->loadContentByRemoteId( - $remoteId, - $this->languageResolver->getPrioritizedLanguages($languages), - $versionNo, - $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) - ); - } - - public function createContent( - ContentCreateStruct $contentCreateStruct, - array $locationCreateStructs = [], - ?array $fieldIdentifiersToValidate = null - ): Content { - return $this->service->createContent($contentCreateStruct, $locationCreateStructs, $fieldIdentifiersToValidate); - } - - public function updateContentMetadata(ContentInfo $contentInfo, ContentMetadataUpdateStruct $contentMetadataUpdateStruct): Content - { - return $this->service->updateContentMetadata($contentInfo, $contentMetadataUpdateStruct); - } - - public function deleteContent(ContentInfo $contentInfo): iterable - { - return $this->service->deleteContent($contentInfo); - } - - public function createContentDraft( - ContentInfo $contentInfo, - ?VersionInfo $versionInfo = null, - ?User $creator = null, - ?Language $language = null - ): Content { - return $this->service->createContentDraft($contentInfo, $versionInfo, $creator, $language); - } - - public function countContentDrafts(?User $user = null): int - { - return $this->service->countContentDrafts($user); - } - - public function loadContentDrafts(?User $user = null): iterable - { - return $this->service->loadContentDrafts($user); - } - - public function loadContentDraftList(?User $user = null, int $offset = 0, int $limit = -1): ContentDraftList - { - return $this->service->loadContentDraftList($user, $offset, $limit); - } - - public function updateContent(VersionInfo $versionInfo, ContentUpdateStruct $contentUpdateStruct, ?array $fieldIdentifiersToValidate = null): Content - { - return $this->service->updateContent($versionInfo, $contentUpdateStruct, $fieldIdentifiersToValidate); - } - - public function publishVersion(VersionInfo $versionInfo, array $translations = Language::ALL): Content - { - return $this->service->publishVersion($versionInfo, $translations); - } - - public function deleteVersion(VersionInfo $versionInfo): void - { - $this->service->deleteVersion($versionInfo); - } - - public function loadVersions(ContentInfo $contentInfo, ?int $status = null): iterable - { - return $this->service->loadVersions($contentInfo, $status); - } - - public function copyContent(ContentInfo $contentInfo, LocationCreateStruct $destinationLocationCreateStruct, ?VersionInfo $versionInfo = null): Content - { - return $this->service->copyContent($contentInfo, $destinationLocationCreateStruct, $versionInfo); - } - - public function loadRelations(VersionInfo $versionInfo): iterable - { - return $this->service->loadRelations($versionInfo); - } - - /** - * {@inheritdoc} - */ - public function countReverseRelations(ContentInfo $contentInfo): int - { - return $this->service->countReverseRelations($contentInfo); - } - - public function loadReverseRelations(ContentInfo $contentInfo): iterable - { - return $this->service->loadReverseRelations($contentInfo); - } - - public function loadReverseRelationList(ContentInfo $contentInfo, int $offset = 0, int $limit = -1): RelationList - { - return $this->service->loadReverseRelationList($contentInfo, $offset, $limit); - } - - public function addRelation(VersionInfo $sourceVersion, ContentInfo $destinationContent): Relation - { - return $this->service->addRelation($sourceVersion, $destinationContent); - } - - public function deleteRelation(VersionInfo $sourceVersion, ContentInfo $destinationContent): void - { - $this->service->deleteRelation($sourceVersion, $destinationContent); - } - - public function deleteTranslation(ContentInfo $contentInfo, string $languageCode): void - { - $this->service->deleteTranslation($contentInfo, $languageCode); - } - - public function deleteTranslationFromDraft(VersionInfo $versionInfo, string $languageCode): Content - { - return $this->service->deleteTranslationFromDraft($versionInfo, $languageCode); - } - - public function loadContentListByContentInfo(array $contentInfoList, array $languages = null, bool $useAlwaysAvailable = true): iterable - { - return $this->service->loadContentListByContentInfo( - $contentInfoList, - $this->languageResolver->getPrioritizedLanguages($languages), - $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) - ); - } - - public function hideContent(ContentInfo $contentInfo): void - { - $this->service->hideContent($contentInfo); - } - - public function revealContent(ContentInfo $contentInfo): void - { - $this->service->revealContent($contentInfo); - } - - public function newContentCreateStruct(ContentType $contentType, string $mainLanguageCode): ContentCreateStruct - { - return $this->service->newContentCreateStruct($contentType, $mainLanguageCode); - } - - public function newContentMetadataUpdateStruct(): ContentMetadataUpdateStruct - { - return $this->service->newContentMetadataUpdateStruct(); - } - - public function newContentUpdateStruct(): ContentUpdateStruct - { - return $this->service->newContentUpdateStruct(); - } - - public function validate( - ValueObject $object, - array $context, - ?array $fieldIdentifiersToValidate = null - ): array { - return $this->service->validate($object, $context, $fieldIdentifiersToValidate); - } - - public function find(Filter $filter, ?array $languages = null): ContentList - { - return $this->service->find( - $filter, - $this->languageResolver->getPrioritizedLanguages($languages) - ); - } - - public function count(Filter $filter, ?array $languages = null): int - { - return $this->service->count( - $filter, - $this->languageResolver->getPrioritizedLanguages($languages) - ); - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/ContentTypeService.php b/eZ/Publish/Core/Repository/SiteAccessAware/ContentTypeService.php deleted file mode 100644 index 1ff9efd62d..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/ContentTypeService.php +++ /dev/null @@ -1,223 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\SiteAccessAware; - -use eZ\Publish\API\Repository\ContentTypeService as ContentTypeServiceInterface; -use eZ\Publish\API\Repository\LanguageResolver; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct; -use eZ\Publish\API\Repository\Values\User\User; - -/** - * SiteAccess aware implementation of ContentTypeService injecting languages where needed. - */ -class ContentTypeService implements ContentTypeServiceInterface -{ - /** @var \eZ\Publish\API\Repository\ContentTypeService */ - protected $service; - - /** @var \eZ\Publish\API\Repository\LanguageResolver */ - protected $languageResolver; - - /** - * Construct service object from aggregated service and LanguageResolver. - * - * @param \eZ\Publish\API\Repository\ContentTypeService $service - * @param \eZ\Publish\API\Repository\LanguageResolver $languageResolver - */ - public function __construct( - ContentTypeServiceInterface $service, - LanguageResolver $languageResolver - ) { - $this->service = $service; - $this->languageResolver = $languageResolver; - } - - public function createContentTypeGroup(ContentTypeGroupCreateStruct $contentTypeGroupCreateStruct): ContentTypeGroup - { - return $this->service->createContentTypeGroup($contentTypeGroupCreateStruct); - } - - public function loadContentTypeGroup(int $contentTypeGroupId, array $prioritizedLanguages = null): ContentTypeGroup - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadContentTypeGroup($contentTypeGroupId, $prioritizedLanguages); - } - - public function loadContentTypeGroupByIdentifier(string $contentTypeGroupIdentifier, array $prioritizedLanguages = null): ContentTypeGroup - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadContentTypeGroupByIdentifier($contentTypeGroupIdentifier, $prioritizedLanguages); - } - - public function loadContentTypeGroups(array $prioritizedLanguages = null): iterable - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadContentTypeGroups($prioritizedLanguages); - } - - public function updateContentTypeGroup(ContentTypeGroup $contentTypeGroup, ContentTypeGroupUpdateStruct $contentTypeGroupUpdateStruct): void - { - $this->service->updateContentTypeGroup($contentTypeGroup, $contentTypeGroupUpdateStruct); - } - - public function deleteContentTypeGroup(ContentTypeGroup $contentTypeGroup): void - { - $this->service->deleteContentTypeGroup($contentTypeGroup); - } - - public function createContentType(ContentTypeCreateStruct $contentTypeCreateStruct, array $contentTypeGroups): ContentTypeDraft - { - return $this->service->createContentType($contentTypeCreateStruct, $contentTypeGroups); - } - - public function loadContentType(int $contentTypeId, array $prioritizedLanguages = null): ContentType - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadContentType($contentTypeId, $prioritizedLanguages); - } - - public function loadContentTypeByIdentifier(string $identifier, array $prioritizedLanguages = null): ContentType - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadContentTypeByIdentifier($identifier, $prioritizedLanguages); - } - - public function loadContentTypeByRemoteId(string $remoteId, array $prioritizedLanguages = null): ContentType - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadContentTypeByRemoteId($remoteId, $prioritizedLanguages); - } - - public function loadContentTypeDraft(int $contentTypeId, bool $ignoreOwnership = false): ContentTypeDraft - { - return $this->service->loadContentTypeDraft($contentTypeId, $ignoreOwnership); - } - - public function loadContentTypeList(array $contentTypeIds, array $prioritizedLanguages = null): iterable - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadContentTypeList($contentTypeIds, $prioritizedLanguages); - } - - public function loadContentTypes(ContentTypeGroup $contentTypeGroup, array $prioritizedLanguages = null): iterable - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadContentTypes($contentTypeGroup, $prioritizedLanguages); - } - - public function createContentTypeDraft(ContentType $contentType): ContentTypeDraft - { - return $this->service->createContentTypeDraft($contentType); - } - - public function updateContentTypeDraft(ContentTypeDraft $contentTypeDraft, ContentTypeUpdateStruct $contentTypeUpdateStruct): void - { - $this->service->updateContentTypeDraft($contentTypeDraft, $contentTypeUpdateStruct); - } - - public function deleteContentType(ContentType $contentType): void - { - $this->service->deleteContentType($contentType); - } - - public function copyContentType(ContentType $contentType, User $creator = null): ContentType - { - return $this->service->copyContentType($contentType, $creator); - } - - public function assignContentTypeGroup(ContentType $contentType, ContentTypeGroup $contentTypeGroup): void - { - $this->service->assignContentTypeGroup($contentType, $contentTypeGroup); - } - - public function unassignContentTypeGroup(ContentType $contentType, ContentTypeGroup $contentTypeGroup): void - { - $this->service->unassignContentTypeGroup($contentType, $contentTypeGroup); - } - - public function addFieldDefinition(ContentTypeDraft $contentTypeDraft, FieldDefinitionCreateStruct $fieldDefinitionCreateStruct): void - { - $this->service->addFieldDefinition($contentTypeDraft, $fieldDefinitionCreateStruct); - } - - public function removeFieldDefinition(ContentTypeDraft $contentTypeDraft, FieldDefinition $fieldDefinition): void - { - $this->service->removeFieldDefinition($contentTypeDraft, $fieldDefinition); - } - - public function updateFieldDefinition(ContentTypeDraft $contentTypeDraft, FieldDefinition $fieldDefinition, FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct): void - { - $this->service->updateFieldDefinition($contentTypeDraft, $fieldDefinition, $fieldDefinitionUpdateStruct); - } - - public function publishContentTypeDraft(ContentTypeDraft $contentTypeDraft): void - { - $this->service->publishContentTypeDraft($contentTypeDraft); - } - - public function newContentTypeGroupCreateStruct(string $identifier): ContentTypeGroupCreateStruct - { - return $this->service->newContentTypeGroupCreateStruct($identifier); - } - - public function newContentTypeCreateStruct(string $identifier): ContentTypeCreateStruct - { - return $this->service->newContentTypeCreateStruct($identifier); - } - - public function newContentTypeUpdateStruct(): ContentTypeUpdateStruct - { - return $this->service->newContentTypeUpdateStruct(); - } - - public function newContentTypeGroupUpdateStruct(): ContentTypeGroupUpdateStruct - { - return $this->service->newContentTypeGroupUpdateStruct(); - } - - public function newFieldDefinitionCreateStruct(string $identifier, string $fieldTypeIdentifier): FieldDefinitionCreateStruct - { - return $this->service->newFieldDefinitionCreateStruct($identifier, $fieldTypeIdentifier); - } - - public function newFieldDefinitionUpdateStruct(): FieldDefinitionUpdateStruct - { - return $this->service->newFieldDefinitionUpdateStruct(); - } - - public function isContentTypeUsed(ContentType $contentType): bool - { - return $this->service->isContentTypeUsed($contentType); - } - - public function removeContentTypeTranslation(ContentTypeDraft $contentTypeDraft, string $languageCode): ContentTypeDraft - { - return $this->service->removeContentTypeTranslation($contentTypeDraft, $languageCode); - } - - public function deleteUserDrafts(int $userId): void - { - $this->service->deleteUserDrafts($userId); - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Language/LanguageResolver.php b/eZ/Publish/Core/Repository/SiteAccessAware/Language/LanguageResolver.php deleted file mode 100644 index ac8bbdc478..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Language/LanguageResolver.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\SiteAccessAware\Language; - -/** - * Resolves language settings for use in SiteAccess aware Repository. - */ -final class LanguageResolver extends AbstractLanguageResolver -{ - /** - * Values typically provided by configuration. - * - * @var string[] - */ - private $configLanguages; - - public function __construct( - array $configLanguages, - bool $defaultUseAlwaysAvailable = true, - bool $defaultShowAllTranslations = false - ) { - $this->configLanguages = $configLanguages; - parent::__construct($defaultUseAlwaysAvailable, $defaultShowAllTranslations); - } - - protected function getConfiguredLanguages(): array - { - return $this->configLanguages; - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/LanguageService.php b/eZ/Publish/Core/Repository/SiteAccessAware/LanguageService.php deleted file mode 100644 index 518d91a9a4..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/LanguageService.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\SiteAccessAware; - -use eZ\Publish\API\Repository\LanguageService as LanguageServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\LanguageCreateStruct; - -/** - * LanguageService for SiteAccessAware layer. - * - * Currently does nothing but hand over calls to aggregated service. - */ -class LanguageService implements LanguageServiceInterface -{ - /** @var \eZ\Publish\API\Repository\LanguageService */ - protected $service; - - /** - * Construct service object from aggregated service. - * - * @param \eZ\Publish\API\Repository\LanguageService $service - */ - public function __construct( - LanguageServiceInterface $service - ) { - $this->service = $service; - } - - public function createLanguage(LanguageCreateStruct $languageCreateStruct): Language - { - return $this->service->createLanguage($languageCreateStruct); - } - - public function updateLanguageName(Language $language, string $newName): Language - { - return $this->service->updateLanguageName($language, $newName); - } - - public function enableLanguage(Language $language): Language - { - return $this->service->enableLanguage($language); - } - - public function disableLanguage(Language $language): Language - { - return $this->service->disableLanguage($language); - } - - public function loadLanguage(string $languageCode): Language - { - return $this->service->loadLanguage($languageCode); - } - - public function loadLanguages(): iterable - { - return $this->service->loadLanguages(); - } - - public function loadLanguageById(int $languageId): Language - { - return $this->service->loadLanguageById($languageId); - } - - public function loadLanguageListByCode(array $languageCodes): iterable - { - return $this->service->loadLanguageListByCode($languageCodes); - } - - public function loadLanguageListById(array $languageIds): iterable - { - return $this->service->loadLanguageListById($languageIds); - } - - public function deleteLanguage(Language $language): void - { - $this->service->deleteLanguage($language); - } - - public function getDefaultLanguageCode(): string - { - return $this->service->getDefaultLanguageCode(); - } - - public function newLanguageCreateStruct(): LanguageCreateStruct - { - return $this->service->newLanguageCreateStruct(); - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/LocationService.php b/eZ/Publish/Core/Repository/SiteAccessAware/LocationService.php deleted file mode 100644 index 1a5700d2f1..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/LocationService.php +++ /dev/null @@ -1,202 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\SiteAccessAware; - -use eZ\Publish\API\Repository\LanguageResolver; -use eZ\Publish\API\Repository\LocationService as LocationServiceInterface; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\LocationList; -use eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\Filter\Filter; - -/** - * LocationService for SiteAccessAware layer. - * - * Currently does nothing but hand over calls to aggregated service. - */ -class LocationService implements LocationServiceInterface -{ - /** @var \eZ\Publish\API\Repository\LocationService */ - protected $service; - - /** @var \eZ\Publish\API\Repository\LanguageResolver */ - protected $languageResolver; - - /** - * Construct service object from aggregated service and LanguageResolver. - * - * @param \eZ\Publish\API\Repository\LocationService $service - * @param \eZ\Publish\API\Repository\LanguageResolver $languageResolver - */ - public function __construct( - LocationServiceInterface $service, - LanguageResolver $languageResolver - ) { - $this->service = $service; - $this->languageResolver = $languageResolver; - } - - public function copySubtree(Location $subtree, Location $targetParentLocation): Location - { - return $this->service->copySubtree($subtree, $targetParentLocation); - } - - public function loadLocation(int $locationId, ?array $prioritizedLanguages = null, ?bool $useAlwaysAvailable = null): Location - { - return $this->service->loadLocation( - $locationId, - $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages), - $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) - ); - } - - public function loadLocationList(array $locationIds, ?array $prioritizedLanguages = null, ?bool $useAlwaysAvailable = null): iterable - { - return $this->service->loadLocationList( - $locationIds, - $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages), - $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) - ); - } - - public function loadLocationByRemoteId(string $remoteId, ?array $prioritizedLanguages = null, ?bool $useAlwaysAvailable = null): Location - { - return $this->service->loadLocationByRemoteId( - $remoteId, - $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages), - $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) - ); - } - - public function loadLocations(ContentInfo $contentInfo, ?Location $rootLocation = null, ?array $prioritizedLanguages = null): iterable - { - return $this->service->loadLocations( - $contentInfo, - $rootLocation, - $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages) - ); - } - - public function loadLocationChildren(Location $location, int $offset = 0, int $limit = 25, ?array $prioritizedLanguages = null): LocationList - { - return $this->service->loadLocationChildren( - $location, - $offset, - $limit, - $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages) - ); - } - - public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?array $prioritizedLanguages = null): iterable - { - return $this->service->loadParentLocationsForDraftContent( - $versionInfo, - $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages) - ); - } - - public function getLocationChildCount(Location $location): int - { - return $this->service->getLocationChildCount($location); - } - - public function getSubtreeSize(Location $location): int - { - return $this->service->getSubtreeSize($location); - } - - public function createLocation(ContentInfo $contentInfo, LocationCreateStruct $locationCreateStruct): Location - { - return $this->service->createLocation($contentInfo, $locationCreateStruct); - } - - public function updateLocation(Location $location, LocationUpdateStruct $locationUpdateStruct): Location - { - return $this->service->updateLocation($location, $locationUpdateStruct); - } - - public function swapLocation(Location $location1, Location $location2): void - { - $this->service->swapLocation($location1, $location2); - } - - public function hideLocation(Location $location): Location - { - return $this->service->hideLocation($location); - } - - public function unhideLocation(Location $location): Location - { - return $this->service->unhideLocation($location); - } - - public function moveSubtree(Location $location, Location $newParentLocation): void - { - $this->service->moveSubtree($location, $newParentLocation); - } - - public function deleteLocation(Location $location): void - { - $this->service->deleteLocation($location); - } - - public function newLocationCreateStruct(int $parentLocationId): LocationCreateStruct - { - return $this->service->newLocationCreateStruct($parentLocationId); - } - - public function newLocationUpdateStruct(): LocationUpdateStruct - { - return $this->service->newLocationUpdateStruct(); - } - - /** - * Get the total number of all existing Locations. Can be combined with loadAllLocations. - * - * @see loadAllLocations - * - * @return int Total number of Locations - */ - public function getAllLocationsCount(): int - { - return $this->service->getAllLocationsCount(); - } - - /** - * Bulk-load all existing Locations, constrained by $limit and $offset to paginate results. - * - * @param int $limit - * @param int $offset - * - * @return \eZ\Publish\API\Repository\Values\Content\Location[] - */ - public function loadAllLocations(int $offset = 0, int $limit = 25): array - { - return $this->service->loadAllLocations($offset, $limit); - } - - public function find(Filter $filter, ?array $languages = null): LocationList - { - return $this->service->find( - $filter, - $this->languageResolver->getPrioritizedLanguages($languages) - ); - } - - public function count(Filter $filter, ?array $languages = null): int - { - return $this->service->count( - $filter, - $this->languageResolver->getPrioritizedLanguages($languages) - ); - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/NotificationService.php b/eZ/Publish/Core/Repository/SiteAccessAware/NotificationService.php deleted file mode 100644 index ec9f8108ec..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/NotificationService.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\SiteAccessAware; - -use eZ\Publish\API\Repository\NotificationService as NotificationServiceInterface; -use eZ\Publish\API\Repository\Values\Notification\CreateStruct; -use eZ\Publish\API\Repository\Values\Notification\Notification; -use eZ\Publish\API\Repository\Values\Notification\NotificationList; - -class NotificationService implements NotificationServiceInterface -{ - /** @var \eZ\Publish\API\Repository\NotificationService */ - protected $service; - - /** - * Construct service object from aggregated service. - * - * @param \eZ\Publish\API\Repository\NotificationService $service - */ - public function __construct( - NotificationServiceInterface $service - ) { - $this->service = $service; - } - - /** - * Get currently logged user notifications. - * - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\API\Repository\Values\Notification\NotificationList - */ - public function loadNotifications(int $offset, int $limit): NotificationList - { - return $this->service->loadNotifications($offset, $limit); - } - - /** - * @param int $notificationId - * - * @return \eZ\Publish\API\Repository\Values\Notification\Notification - */ - public function getNotification(int $notificationId): Notification - { - return $this->service->getNotification($notificationId); - } - - /** - * Mark notification as read so it no longer bother the user. - * - * @param \eZ\Publish\API\Repository\Values\Notification\Notification $notification - */ - public function markNotificationAsRead(Notification $notification): void - { - $this->service->markNotificationAsRead($notification); - } - - /** - * Get count of unread users notifications. - * - * @return int - */ - public function getPendingNotificationCount(): int - { - return $this->service->getPendingNotificationCount(); - } - - /** - * Get count of total users notifications. - * - * @return int - */ - public function getNotificationCount(): int - { - return $this->service->getNotificationCount(); - } - - /** - * @param \eZ\Publish\API\Repository\Values\Notification\Notification $notification - */ - public function deleteNotification(Notification $notification): void - { - $this->service->deleteNotification($notification); - } - - /** - * @param \eZ\Publish\API\Repository\Values\Notification\CreateStruct $createStruct - * - * @return \eZ\Publish\API\Repository\Values\Notification\Notification - */ - public function createNotification(CreateStruct $createStruct): Notification - { - return $this->service->createNotification($createStruct); - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/ObjectStateService.php b/eZ/Publish/Core/Repository/SiteAccessAware/ObjectStateService.php deleted file mode 100644 index 4a49b6b7b3..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/ObjectStateService.php +++ /dev/null @@ -1,164 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\SiteAccessAware; - -use eZ\Publish\API\Repository\LanguageResolver; -use eZ\Publish\API\Repository\ObjectStateService as ObjectStateServiceInterface; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectState; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateUpdateStruct; - -/** - * SiteAccess aware implementation of ObjectStateService injecting languages where needed. - */ -class ObjectStateService implements ObjectStateServiceInterface -{ - /** @var \eZ\Publish\API\Repository\ObjectStateService */ - protected $service; - - /** @var \eZ\Publish\API\Repository\LanguageResolver */ - protected $languageResolver; - - /** - * Construct service object from aggregated service and LanguageResolver. - * - * @param \eZ\Publish\API\Repository\ObjectStateService $service - * @param \eZ\Publish\API\Repository\LanguageResolver $languageResolver - */ - public function __construct( - ObjectStateServiceInterface $service, - LanguageResolver $languageResolver - ) { - $this->service = $service; - $this->languageResolver = $languageResolver; - } - - public function createObjectStateGroup(ObjectStateGroupCreateStruct $objectStateGroupCreateStruct): ObjectStateGroup - { - return $this->service->createObjectStateGroup($objectStateGroupCreateStruct); - } - - public function loadObjectStateGroup(int $objectStateGroupId, array $prioritizedLanguages = null): ObjectStateGroup - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadObjectStateGroup($objectStateGroupId, $prioritizedLanguages); - } - - public function loadObjectStateGroupByIdentifier( - string $objectStateGroupIdentifier, - array $prioritizedLanguages = null - ): ObjectStateGroup { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadObjectStateGroupByIdentifier($objectStateGroupIdentifier, $prioritizedLanguages); - } - - public function loadObjectStateGroups(int $offset = 0, int $limit = -1, array $prioritizedLanguages = null): iterable - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadObjectStateGroups($offset, $limit, $prioritizedLanguages); - } - - public function loadObjectStates(ObjectStateGroup $objectStateGroup, array $prioritizedLanguages = null): iterable - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadObjectStates($objectStateGroup, $prioritizedLanguages); - } - - public function updateObjectStateGroup(ObjectStateGroup $objectStateGroup, ObjectStateGroupUpdateStruct $objectStateGroupUpdateStruct): ObjectStateGroup - { - return $this->service->updateObjectStateGroup($objectStateGroup, $objectStateGroupUpdateStruct); - } - - public function deleteObjectStateGroup(ObjectStateGroup $objectStateGroup): void - { - $this->service->deleteObjectStateGroup($objectStateGroup); - } - - public function createObjectState(ObjectStateGroup $objectStateGroup, ObjectStateCreateStruct $objectStateCreateStruct): ObjectState - { - return $this->service->createObjectState($objectStateGroup, $objectStateCreateStruct); - } - - public function loadObjectState(int $stateId, array $prioritizedLanguages = null): ObjectState - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadObjectState($stateId, $prioritizedLanguages); - } - - public function loadObjectStateByIdentifier( - ObjectStateGroup $objectStateGroup, - string $objectStateIdentifier, - array $prioritizedLanguages = null - ): ObjectState { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadObjectStateByIdentifier( - $objectStateGroup, - $objectStateIdentifier, - $prioritizedLanguages - ); - } - - public function updateObjectState(ObjectState $objectState, ObjectStateUpdateStruct $objectStateUpdateStruct): ObjectState - { - return $this->service->updateObjectState($objectState, $objectStateUpdateStruct); - } - - public function setPriorityOfObjectState(ObjectState $objectState, int $priority): void - { - $this->service->setPriorityOfObjectState($objectState, $priority); - } - - public function deleteObjectState(ObjectState $objectState): void - { - $this->service->deleteObjectState($objectState); - } - - public function setContentState(ContentInfo $contentInfo, ObjectStateGroup $objectStateGroup, ObjectState $objectState): void - { - $this->service->setContentState($contentInfo, $objectStateGroup, $objectState); - } - - public function getContentState(ContentInfo $contentInfo, ObjectStateGroup $objectStateGroup): ObjectState - { - return $this->service->getContentState($contentInfo, $objectStateGroup); - } - - public function getContentCount(ObjectState $objectState): int - { - return $this->service->getContentCount($objectState); - } - - public function newObjectStateGroupCreateStruct(string $identifier): ObjectStateGroupCreateStruct - { - return $this->service->newObjectStateGroupCreateStruct($identifier); - } - - public function newObjectStateGroupUpdateStruct(): ObjectStateGroupUpdateStruct - { - return $this->service->newObjectStateGroupUpdateStruct(); - } - - public function newObjectStateCreateStruct(string $identifier): ObjectStateCreateStruct - { - return $this->service->newObjectStateCreateStruct($identifier); - } - - public function newObjectStateUpdateStruct(): ObjectStateUpdateStruct - { - return $this->service->newObjectStateUpdateStruct(); - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Repository.php b/eZ/Publish/Core/Repository/SiteAccessAware/Repository.php deleted file mode 100644 index c28b114278..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Repository.php +++ /dev/null @@ -1,212 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\SiteAccessAware; - -use eZ\Publish\API\Repository\BookmarkService as BookmarkServiceInterface; -use eZ\Publish\API\Repository\ContentService as ContentServiceInterface; -use eZ\Publish\API\Repository\ContentTypeService as ContentTypeServiceInterface; -use eZ\Publish\API\Repository\FieldTypeService as FieldTypeServiceInterface; -use eZ\Publish\API\Repository\LanguageService as LanguageServiceInterface; -use eZ\Publish\API\Repository\LocationService as LocationServiceInterface; -use eZ\Publish\API\Repository\NotificationService as NotificationServiceInterface; -use eZ\Publish\API\Repository\ObjectStateService as ObjectStateServiceInterface; -use eZ\Publish\API\Repository\PermissionResolver as PermissionResolverInterface; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\RoleService as RoleServiceInterface; -use eZ\Publish\API\Repository\SearchService as SearchServiceInterface; -use eZ\Publish\API\Repository\SectionService as SectionServiceInterface; -use eZ\Publish\API\Repository\TrashService as TrashServiceInterface; -use eZ\Publish\API\Repository\URLAliasService as URLAliasServiceInterface; -use eZ\Publish\API\Repository\URLService as URLServiceInterface; -use eZ\Publish\API\Repository\URLWildcardService as URLWildcardServiceInterface; -use eZ\Publish\API\Repository\UserPreferenceService as UserPreferenceServiceInterface; -use eZ\Publish\API\Repository\UserService as UserServiceInterface; - -/** - * Repository class. - */ -class Repository implements RepositoryInterface -{ - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\API\Repository\ContentService */ - protected $contentService; - - /** @var \eZ\Publish\API\Repository\SectionService */ - protected $sectionService; - - /** @var \eZ\Publish\API\Repository\SearchService */ - protected $searchService; - - /** @var \eZ\Publish\API\Repository\UserService */ - protected $userService; - - /** @var \eZ\Publish\API\Repository\LanguageService */ - protected $languageService; - - /** @var \eZ\Publish\API\Repository\LocationService */ - protected $locationService; - - /** @var \eZ\Publish\API\Repository\TrashService */ - protected $trashService; - - /** @var \eZ\Publish\API\Repository\ContentTypeService */ - protected $contentTypeService; - - /** @var \eZ\Publish\API\Repository\ObjectStateService */ - protected $objectStateService; - - /** @var \eZ\Publish\API\Repository\URLAliasService */ - protected $urlAliasService; - - /** @var \eZ\Publish\Core\Repository\NotificationService */ - protected $notificationService; - - /** - * Construct repository object from aggregated repository. - */ - public function __construct( - RepositoryInterface $repository, - ContentService $contentService, - ContentTypeService $contentTypeService, - ObjectStateService $objectStateService, - URLAliasService $urlAliasService, - UserService $userService, - SearchService $searchService, - SectionService $sectionService, - TrashService $trashService, - LocationService $locationService, - LanguageService $languageService, - NotificationService $notificationService - ) { - $this->repository = $repository; - $this->contentService = $contentService; - $this->contentTypeService = $contentTypeService; - $this->objectStateService = $objectStateService; - $this->urlAliasService = $urlAliasService; - $this->userService = $userService; - $this->searchService = $searchService; - $this->sectionService = $sectionService; - $this->trashService = $trashService; - $this->locationService = $locationService; - $this->languageService = $languageService; - $this->notificationService = $notificationService; - } - - public function sudo(callable $callback, ?RepositoryInterface $outerRepository = null) - { - return $this->repository->sudo($callback, $outerRepository ?? $this); - } - - public function getContentService(): ContentServiceInterface - { - return $this->contentService; - } - - public function getContentLanguageService(): LanguageServiceInterface - { - return $this->languageService; - } - - public function getContentTypeService(): ContentTypeServiceInterface - { - return $this->contentTypeService; - } - - public function getLocationService(): LocationServiceInterface - { - return $this->locationService; - } - - public function getTrashService(): TrashServiceInterface - { - return $this->trashService; - } - - public function getSectionService(): SectionServiceInterface - { - return $this->sectionService; - } - - public function getUserService(): UserServiceInterface - { - return $this->userService; - } - - public function getURLAliasService(): URLAliasServiceInterface - { - return $this->urlAliasService; - } - - public function getURLWildcardService(): URLWildcardServiceInterface - { - return $this->repository->getURLWildcardService(); - } - - public function getObjectStateService(): ObjectStateServiceInterface - { - return $this->objectStateService; - } - - public function getRoleService(): RoleServiceInterface - { - return $this->repository->getRoleService(); - } - - public function getSearchService(): SearchServiceInterface - { - return $this->searchService; - } - - public function getFieldTypeService(): FieldTypeServiceInterface - { - return $this->repository->getFieldTypeService(); - } - - public function getPermissionResolver(): PermissionResolverInterface - { - return $this->repository->getPermissionResolver(); - } - - public function getURLService(): URLServiceInterface - { - return $this->repository->getURLService(); - } - - public function getBookmarkService(): BookmarkServiceInterface - { - return $this->repository->getBookmarkService(); - } - - public function getNotificationService(): NotificationServiceInterface - { - return $this->repository->getNotificationService(); - } - - public function getUserPreferenceService(): UserPreferenceServiceInterface - { - return $this->repository->getUserPreferenceService(); - } - - public function beginTransaction(): void - { - $this->repository->beginTransaction(); - } - - public function commit(): void - { - $this->repository->commit(); - } - - public function rollback(): void - { - $this->repository->rollback(); - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/SearchService.php b/eZ/Publish/Core/Repository/SiteAccessAware/SearchService.php deleted file mode 100644 index b82c8955fc..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/SearchService.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\SiteAccessAware; - -use eZ\Publish\API\Repository\LanguageResolver; -use eZ\Publish\API\Repository\SearchService as SearchServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; - -/** - * SiteAccess aware implementation of SearchService injecting languages where needed. - */ -class SearchService implements SearchServiceInterface -{ - /** @var \eZ\Publish\API\Repository\SearchService */ - protected $service; - - /** @var \eZ\Publish\API\Repository\LanguageResolver */ - protected $languageResolver; - - /** - * Construct service object from aggregated service and LanguageResolver. - * - * @param \eZ\Publish\API\Repository\SearchService $service - * @param \eZ\Publish\API\Repository\LanguageResolver $languageResolver - */ - public function __construct( - SearchServiceInterface $service, - LanguageResolver $languageResolver - ) { - $this->service = $service; - $this->languageResolver = $languageResolver; - } - - public function findContent(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult - { - $languageFilter['languages'] = $this->languageResolver->getPrioritizedLanguages( - $languageFilter['languages'] ?? null - ); - - $languageFilter['useAlwaysAvailable'] = $this->languageResolver->getUseAlwaysAvailable( - $languageFilter['useAlwaysAvailable'] ?? null - ); - - return $this->service->findContent($query, $languageFilter, $filterOnUserPermissions); - } - - public function findContentInfo(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult - { - $languageFilter['languages'] = $this->languageResolver->getPrioritizedLanguages( - $languageFilter['languages'] ?? null - ); - - $languageFilter['useAlwaysAvailable'] = $this->languageResolver->getUseAlwaysAvailable( - $languageFilter['useAlwaysAvailable'] ?? null - ); - - return $this->service->findContentInfo($query, $languageFilter, $filterOnUserPermissions); - } - - public function findSingle(Criterion $filter, array $languageFilter = [], bool $filterOnUserPermissions = true): Content - { - $languageFilter['languages'] = $this->languageResolver->getPrioritizedLanguages( - $languageFilter['languages'] ?? null - ); - - $languageFilter['useAlwaysAvailable'] = $this->languageResolver->getUseAlwaysAvailable( - $languageFilter['useAlwaysAvailable'] ?? null - ); - - return $this->service->findSingle($filter, $languageFilter, $filterOnUserPermissions); - } - - public function suggest(string $prefix, array $fieldPaths = [], int $limit = 10, Criterion $filter = null) - { - return $this->service->suggest($prefix, $fieldPaths, $limit, $filter); - } - - public function findLocations(LocationQuery $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult - { - $languageFilter['languages'] = $this->languageResolver->getPrioritizedLanguages( - $languageFilter['languages'] ?? null - ); - - $languageFilter['useAlwaysAvailable'] = $this->languageResolver->getUseAlwaysAvailable( - $languageFilter['useAlwaysAvailable'] ?? null - ); - - return $this->service->findLocations($query, $languageFilter, $filterOnUserPermissions); - } - - public function supports(int $capabilityFlag): bool - { - return $this->service->supports($capabilityFlag); - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/SectionService.php b/eZ/Publish/Core/Repository/SiteAccessAware/SectionService.php deleted file mode 100644 index bbc16bf075..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/SectionService.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\SiteAccessAware; - -use eZ\Publish\API\Repository\SectionService as SectionServiceInterface; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Section; -use eZ\Publish\API\Repository\Values\Content\SectionCreateStruct; -use eZ\Publish\API\Repository\Values\Content\SectionUpdateStruct; - -/** - * SectionService for SiteAccessAware layer. - * - * Currently does nothing but hand over calls to aggregated service. - */ -class SectionService implements SectionServiceInterface -{ - /** @var \eZ\Publish\API\Repository\SectionService */ - protected $service; - - /** - * Construct service object from aggregated service. - * - * @param \eZ\Publish\API\Repository\SectionService $service - */ - public function __construct( - SectionServiceInterface $service - ) { - $this->service = $service; - } - - public function createSection(SectionCreateStruct $sectionCreateStruct): Section - { - return $this->service->createSection($sectionCreateStruct); - } - - public function updateSection(Section $section, SectionUpdateStruct $sectionUpdateStruct): Section - { - return $this->service->updateSection($section, $sectionUpdateStruct); - } - - public function loadSection(int $sectionId): Section - { - return $this->service->loadSection($sectionId); - } - - public function loadSections(): iterable - { - return $this->service->loadSections(); - } - - public function loadSectionByIdentifier(string $sectionIdentifier): Section - { - return $this->service->loadSectionByIdentifier($sectionIdentifier); - } - - public function countAssignedContents(Section $section): int - { - return $this->service->countAssignedContents($section); - } - - public function isSectionUsed(Section $section): bool - { - return $this->service->isSectionUsed($section); - } - - public function assignSection(ContentInfo $contentInfo, Section $section): void - { - $this->service->assignSection($contentInfo, $section); - } - - public function assignSectionToSubtree(Location $location, Section $section): void - { - $this->service->assignSectionToSubtree($location, $section); - } - - public function deleteSection(Section $section): void - { - $this->service->deleteSection($section); - } - - public function newSectionCreateStruct(): SectionCreateStruct - { - return $this->service->newSectionCreateStruct(); - } - - public function newSectionUpdateStruct(): SectionUpdateStruct - { - return $this->service->newSectionUpdateStruct(); - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/SettingService.php b/eZ/Publish/Core/Repository/SiteAccessAware/SettingService.php deleted file mode 100644 index 8054bcd109..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/SettingService.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\SiteAccessAware; - -use eZ\Publish\SPI\Repository\Decorator\SettingServiceDecorator; - -class SettingService extends SettingServiceDecorator -{ -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/AbstractServiceTest.php b/eZ/Publish/Core/Repository/SiteAccessAware/Tests/AbstractServiceTest.php deleted file mode 100644 index dd37fabc7b..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/AbstractServiceTest.php +++ /dev/null @@ -1,280 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\SiteAccessAware\Tests; - -use Closure; -use eZ\Publish\API\Repository\LanguageResolver; -use PHPUnit\Framework\TestCase; -use ReflectionClass; -use ReflectionMethod; - -/** - * Abstract tests for SiteAccessAware Services. - * - * Implies convention for methods on these services to either: - * - Do nothing, pass-through call and optionally (default:true) return value - * - lookup languages [IF not defined by callee] on one of the arguments given and pass it to next one. - */ -abstract class AbstractServiceTest extends TestCase -{ - /** - * Purely to attempt to make tests easier to read. - * - * As language parameter is ignored from providers and replced with values in tests, this is used to mark value of - * language argument instead of either askingproviders to use 0, or a valid language array which would then not be - * used. - */ - public const LANG_ARG = 0; - - /** @var \object|\PHPUnit\Framework\MockObject\MockObject */ - protected $innerApiServiceMock; - - /** @var object */ - protected $service; - - /** @var \eZ\Publish\API\Repository\LanguageResolver|\PHPUnit\Framework\MockObject\MockObject */ - protected $languageResolverMock; - - abstract public function getAPIServiceClassName(); - - abstract public function getSiteAccessAwareServiceClassName(); - - protected function setUp(): void - { - parent::setUp(); - $this->innerApiServiceMock = $this->getMockBuilder($this->getAPIServiceClassName())->getMock(); - $this->languageResolverMock = $this->getMockBuilder(LanguageResolver::class) - ->disableOriginalConstructor() - ->getMock(); - $serviceClassName = $this->getSiteAccessAwareServiceClassName(); - - $this->service = new $serviceClassName( - $this->innerApiServiceMock, - $this->languageResolverMock - ); - } - - protected function tearDown(): void - { - unset($this->service); - unset($this->languageResolverMock); - unset($this->innerApiServiceMock); - parent::tearDown(); - } - - /** - * @return array See signature on {@link testForPassTrough} for arguments and their type. - */ - abstract public function providerForPassTroughMethods(); - - /** - * Make sure these methods does nothing more then passing the arguments to inner service. - * - * Methods tested here are basically those without as languages argument. - * - * @dataProvider providerForPassTroughMethods - * - * @param string $method - * @param array $arguments - * @param mixed $return - */ - final public function testForPassTrough($method, array $arguments, $return = true) - { - if ($return) { - $this->innerApiServiceMock - ->expects($this->once()) - ->method($method) - ->with(...$arguments) - ->willReturn($return); - } else { - $this->innerApiServiceMock - ->expects($this->once()) - ->method($method) - ->with(...$arguments); - } - - $actualReturn = $this->service->$method(...$arguments); - - if ($return) { - $this->assertEquals($return, $actualReturn); - } - } - - /** - * @return array See signature on {@link testForLanguagesLookup} for arguments and their type. - * NOTE: languages / prioritizedLanguage, can be set to 0, it will be replaced by tests methods. - */ - abstract public function providerForLanguagesLookupMethods(); - - /** - * Method to be able to customize the logic for setting expected language argument during {@see testForLanguagesLookup()}. - * - * @param array $arguments - * @param int $languageArgumentIndex - * @param array $languages - * - * @return array - */ - protected function setLanguagesLookupExpectedArguments(array $arguments, $languageArgumentIndex, array $languages) - { - $arguments[$languageArgumentIndex] = $languages; - - return $arguments; - } - - /** - * Method to be able to customize the logic for setting expected language argument during {@see testForLanguagesLookup()}. - * - * @param array $arguments - * @param int $languageArgumentIndex - * - * @return array - */ - protected function setLanguagesLookupArguments(array $arguments, $languageArgumentIndex) - { - $arguments[$languageArgumentIndex] = []; - - return $arguments; - } - - /** - * Test that language aware methods does a language lookup when language is not set. - * - * @dataProvider providerForLanguagesLookupMethods - * - * @param string $method - * @param array $arguments - * @param mixed|null $return - * @param int $languageArgumentIndex From 0 and up, so the array index on $arguments. - */ - final public function testForLanguagesLookup($method, array $arguments, $return, $languageArgumentIndex, callable $callback = null, int $alwaysAvailableArgumentIndex = null) - { - $languages = ['eng-GB', 'eng-US']; - - $arguments = $this->setLanguagesLookupArguments($arguments, $languageArgumentIndex); - - $expectedArguments = $this->setLanguagesLookupExpectedArguments($arguments, $languageArgumentIndex, $languages); - - $this->languageResolverMock - ->expects($this->once()) - ->method('getPrioritizedLanguages') - ->with([]) - ->willReturn($languages); - - if ($alwaysAvailableArgumentIndex) { - $arguments[$alwaysAvailableArgumentIndex] = null; - $expectedArguments[$alwaysAvailableArgumentIndex] = true; - $this->languageResolverMock - ->expects($this->once()) - ->method('getUseAlwaysAvailable') - ->with(null) - ->willReturn(true); - } - - $this->innerApiServiceMock - ->expects($this->once()) - ->method($method) - ->with(...$expectedArguments) - ->willReturn($return); - - if ($callback instanceof Closure) { - $callback->bindTo($this, static::class)(true); - } - - $actualReturn = $this->service->$method(...$arguments); - - if ($return) { - $this->assertEquals($return, $actualReturn); - } - } - - /** - * Method to be able to customize the logic for setting expected language argument during {@see testForLanguagesPassTrough()}. - * - * @param array $arguments - * @param int $languageArgumentIndex - * @param array $languages - * - * @return array - */ - protected function setLanguagesPassTroughArguments(array $arguments, $languageArgumentIndex, array $languages) - { - $arguments[$languageArgumentIndex] = $languages; - - return $arguments; - } - - /** - * Make sure these methods does nothing more then passing the arguments to inner service. - * - * @dataProvider providerForLanguagesLookupMethods - * - * @param string $method - * @param array $arguments - * @param mixed|null $return - * @param int $languageArgumentIndex From 0 and up, so the array index on $arguments. - */ - final public function testForLanguagesPassTrough($method, array $arguments, $return, $languageArgumentIndex, callable $callback = null, int $alwaysAvailableArgumentIndex = null) - { - $languages = ['eng-GB', 'eng-US']; - $arguments = $this->setLanguagesPassTroughArguments($arguments, $languageArgumentIndex, $languages); - - $this->languageResolverMock - ->expects($this->once()) - ->method('getPrioritizedLanguages') - ->with($languages) - ->willReturn($languages); - - if ($alwaysAvailableArgumentIndex) { - $this->languageResolverMock - ->expects($this->once()) - ->method('getUseAlwaysAvailable') - ->with($arguments[$alwaysAvailableArgumentIndex]) - ->willReturn($arguments[$alwaysAvailableArgumentIndex]); - } - - $this->innerApiServiceMock - ->expects($this->once()) - ->method($method) - ->with(...$arguments) - ->willReturn($return); - - if ($callback instanceof Closure) { - $callback->bindTo($this, static::class)(false); - } - - $actualReturn = $this->service->$method(...$arguments); - - if ($return) { - $this->assertEquals($return, $actualReturn); - } - } - - /** - * @todo replace with coverage testing (see EZP-31035) - */ - final public function testIfThereIsMissingTest(): void - { - $tested = array_merge( - array_column($this->providerForLanguagesLookupMethods(), 0), - array_column($this->providerForPassTroughMethods(), 0) - ); - - $class = new ReflectionClass($this->getSiteAccessAwareServiceClassName()); - foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { - if (!$method->isConstructor() && !in_array($method->getShortName(), $tested)) { - $this->addWarning( - sprintf( - 'Test for the %s::%s method is missing', - $this->getSiteAccessAwareServiceClassName(), - $method->getName() - ) - ); - } - } - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/ContentServiceTest.php b/eZ/Publish/Core/Repository/SiteAccessAware/Tests/ContentServiceTest.php deleted file mode 100644 index 229d230787..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/ContentServiceTest.php +++ /dev/null @@ -1,167 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\SiteAccessAware\Tests; - -use eZ\Publish\API\Repository\ContentService as APIService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentDraftList; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\ContentList; -use eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentId; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\Content\RelationList; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\Core\Repository\SiteAccessAware\ContentService; -use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Values\User\User; - -/** - * @property \eZ\Publish\API\Repository\ContentService $service - */ -class ContentServiceTest extends AbstractServiceTest -{ - public function getAPIServiceClassName() - { - return APIService::class; - } - - public function getSiteAccessAwareServiceClassName() - { - return ContentService::class; - } - - public function providerForPassTroughMethods(): array - { - $contentInfo = new ContentInfo(); - $versionInfo = new VersionInfo(); - $content = $this->createMock(Content::class); - $relation = $this->createMock(Relation::class); - $relationList = new RelationList(); - $contentCreateStruct = new ContentCreateStruct(); - $contentUpdateStruct = new ContentUpdateStruct(); - $contentMetaStruct = new ContentMetadataUpdateStruct(); - $locationCreateStruct = new LocationCreateStruct(); - $user = new User(); - $contentType = new ContentType(); - $language = new Language(); - - // string $method, array $arguments, bool $return = true - return [ - ['loadContentInfo', [42], $contentInfo], - ['loadContentInfoList', [[42]], [$contentInfo]], - - ['loadContentInfoByRemoteId', ['f348tj4gorgji4'], $contentInfo], - - ['loadVersionInfo', [$contentInfo], $versionInfo], - ['loadVersionInfo', [$contentInfo, 3], $versionInfo], - - ['loadVersionInfoById', [42], $versionInfo], - ['loadVersionInfoById', [42, 3], $versionInfo], - - ['createContent', [$contentCreateStruct], $content], - ['createContent', [$contentCreateStruct, [44]], $content], - - ['updateContentMetadata', [$contentInfo, $contentMetaStruct], $content], - - ['deleteContent', [$contentInfo], null], - - ['createContentDraft', [$contentInfo], $content], - ['createContentDraft', [$contentInfo, $versionInfo], $content], - ['createContentDraft', [$contentInfo, $versionInfo, $user], $content], - ['createContentDraft', [$contentInfo, $versionInfo, $user, $language], $content], - - ['countContentDrafts', [], 0], - ['countContentDrafts', [$user], 0], - - ['loadContentDrafts', [], [$content]], - ['loadContentDrafts', [$user], [$content]], - - ['loadContentDraftList', [], new ContentDraftList()], - ['loadContentDraftList', [$user], new ContentDraftList()], - ['loadContentDraftList', [$user, 1], new ContentDraftList()], - ['loadContentDraftList', [$user, 1, 25], new ContentDraftList()], - - ['updateContent', [$versionInfo, $contentUpdateStruct], $content], - - ['publishVersion', [$versionInfo], $content], - - ['deleteVersion', [$versionInfo], null], - - ['loadVersions', [$contentInfo], [$versionInfo]], - - ['copyContent', [$contentInfo, $locationCreateStruct], $content], - ['copyContent', [$contentInfo, $locationCreateStruct, $versionInfo], $content], - - ['loadRelations', [$versionInfo], [$relation]], - - ['countReverseRelations', [$contentInfo], 0], - - ['loadReverseRelations', [$contentInfo], $relationList], - ['loadReverseRelationList', [$contentInfo], $relationList], - - ['addRelation', [$versionInfo, $contentInfo], null], - - ['deleteRelation', [$versionInfo, $contentInfo], null], - - ['deleteTranslation', [$contentInfo, 'eng-GB'], null], - - ['deleteTranslationFromDraft', [$versionInfo, 'eng-GB'], $content], - - ['hideContent', [$contentInfo], null], - ['revealContent', [$contentInfo], null], - - ['newContentCreateStruct', [$contentType, 'eng-GB'], $contentCreateStruct], - ['newContentMetadataUpdateStruct', [], $contentMetaStruct], - ['newContentUpdateStruct', [], $contentUpdateStruct], - ['validate', [$contentUpdateStruct, []], []], - - ['loadVersionInfoListByContentInfo', [[$contentInfo]], [$versionInfo]], - ]; - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - */ - public function providerForLanguagesLookupMethods(): array - { - $content = $this->createMock(Content::class); - $contentInfo = new ContentInfo(); - $versionInfo = new VersionInfo(); - - $filter = new Filter(new ContentId(1)); - - // string $method, array $arguments, bool $return, int $languageArgumentIndex - return [ - ['loadContentByContentInfo', [$contentInfo], $content, 1], - ['loadContentByContentInfo', [$contentInfo, self::LANG_ARG, 4, false], $content, 1], - - ['loadContentByVersionInfo', [$versionInfo], $content, 1], - ['loadContentByVersionInfo', [$versionInfo, self::LANG_ARG, false], $content, 1], - - ['loadContent', [42], $content, 1], - ['loadContent', [42, self::LANG_ARG, 4, false], $content, 1], - - ['loadContentByRemoteId', ['f348tj4gorgji4'], $content, 1], - ['loadContentByRemoteId', ['f348tj4gorgji4', self::LANG_ARG, 4, false], $content, 1], - - ['loadContentListByContentInfo', [[$contentInfo]], [], 1], - ['loadContentListByContentInfo', [[$contentInfo], self::LANG_ARG, false], [], 1], - - ['find', [$filter], new ContentList(1, [$content]), 1], - ['find', [$filter, self::LANG_ARG], new ContentList(1, [$content]), 1], - - ['count', [$filter], 0, 1], - ['count', [$filter, self::LANG_ARG], 0, 1], - ]; - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/ContentTypeServiceTest.php b/eZ/Publish/Core/Repository/SiteAccessAware/Tests/ContentTypeServiceTest.php deleted file mode 100644 index 7c822f8a03..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/ContentTypeServiceTest.php +++ /dev/null @@ -1,129 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\SiteAccessAware\Tests; - -use eZ\Publish\API\Repository\ContentTypeService as APIService; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct; -use eZ\Publish\Core\Repository\SiteAccessAware\ContentTypeService; -use eZ\Publish\Core\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Values\ContentType\ContentTypeCreateStruct; -use eZ\Publish\Core\Repository\Values\ContentType\ContentTypeDraft; -use eZ\Publish\Core\Repository\Values\ContentType\ContentTypeGroup; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Repository\Values\User\User; - -class ContentTypeServiceTest extends AbstractServiceTest -{ - public function getAPIServiceClassName() - { - return APIService::class; - } - - public function getSiteAccessAwareServiceClassName() - { - return ContentTypeService::class; - } - - public function providerForPassTroughMethods() - { - $contentTypeGroupCreateStruct = new ContentTypeGroupCreateStruct(); - $contentTypeGroupUpdateStruct = new ContentTypeGroupUpdateStruct(); - $contentTypeGroup = new ContentTypeGroup(); - - $contentTypeCreateStruct = new ContentTypeCreateStruct(); - $contentTypeUpdateStruct = new ContentTypeUpdateStruct(); - $contentType = new ContentType(); - $contentTypeDraft = new ContentTypeDraft(); - - $fieldDefinition = new FieldDefinition(); - $fieldDefinitionCreateStruct = new FieldDefinitionCreateStruct(); - $fieldDefinitionUpdateStruct = new FieldDefinitionUpdateStruct(); - - $user = new User(); - - // string $method, array $arguments, bool $return = true - return [ - ['createContentTypeGroup', [$contentTypeGroupCreateStruct], $contentTypeGroup], - - ['updateContentTypeGroup', [$contentTypeGroup, $contentTypeGroupUpdateStruct], null], - - ['deleteContentTypeGroup', [$contentTypeGroup], null], - - ['createContentType', [$contentTypeCreateStruct, [$contentTypeGroup]], $contentTypeDraft], - - ['loadContentTypeDraft', [22], $contentTypeDraft], - - ['createContentTypeDraft', [$contentType], $contentTypeDraft], - - ['updateContentTypeDraft', [$contentTypeDraft, $contentTypeUpdateStruct], null], - - ['deleteContentType', [$contentType], null], - - ['copyContentType', [$contentType], $contentType], - ['copyContentType', [$contentType, $user], $contentType], - - ['assignContentTypeGroup', [$contentType, $contentTypeGroup], null], - - ['unassignContentTypeGroup', [$contentType, $contentTypeGroup], null], - - ['addFieldDefinition', [$contentTypeDraft, $fieldDefinitionCreateStruct], null], - - ['removeFieldDefinition', [$contentTypeDraft, $fieldDefinition], null], - - ['updateFieldDefinition', [$contentTypeDraft, $fieldDefinition, $fieldDefinitionUpdateStruct], null], - - ['publishContentTypeDraft', [$contentTypeDraft], null], - - ['newContentTypeGroupCreateStruct', ['media'], $contentTypeGroupCreateStruct], - - ['newContentTypeCreateStruct', ['blog'], $contentTypeCreateStruct], - - ['newContentTypeUpdateStruct', [], $contentTypeUpdateStruct], - - ['newContentTypeGroupUpdateStruct', [], $contentTypeGroupUpdateStruct], - - ['newFieldDefinitionCreateStruct', ['body', 'ezstring'], $fieldDefinitionCreateStruct], - - ['newFieldDefinitionUpdateStruct', [], $fieldDefinitionUpdateStruct], - - ['isContentTypeUsed', [$contentType], true], - - ['removeContentTypeTranslation', [$contentTypeDraft, 'ger-DE'], $contentTypeDraft], - - ['deleteUserDrafts', [14], null], - ]; - } - - public function providerForLanguagesLookupMethods() - { - $contentType = new ContentType(); - $contentTypeGroup = new ContentTypeGroup(); - - // string $method, array $arguments, bool $return, int $languageArgumentIndex - return [ - ['loadContentTypeGroup', [33, self::LANG_ARG], $contentTypeGroup, 1], - - ['loadContentTypeGroupByIdentifier', ['content', self::LANG_ARG], $contentTypeGroup, 1], - - ['loadContentTypeGroups', [self::LANG_ARG], [$contentTypeGroup], 0], - - ['loadContentType', [22, self::LANG_ARG], $contentType, 1], - - ['loadContentTypeList', [[22, self::LANG_ARG]], [$contentType], 1], - - ['loadContentTypeByIdentifier', ['article', self::LANG_ARG], $contentType, 1], - - ['loadContentTypeByRemoteId', ['w4ini3tn4f', self::LANG_ARG], $contentType, 1], - - ['loadContentTypes', [$contentTypeGroup, self::LANG_ARG], [$contentType], 1], - ]; - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/LanguageServiceTest.php b/eZ/Publish/Core/Repository/SiteAccessAware/Tests/LanguageServiceTest.php deleted file mode 100644 index 6243fbf0b1..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/LanguageServiceTest.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\SiteAccessAware\Tests; - -use eZ\Publish\API\Repository\LanguageService as APIService; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\LanguageCreateStruct; -use eZ\Publish\Core\Repository\SiteAccessAware\LanguageService; - -class LanguageServiceTest extends AbstractServiceTest -{ - public function getAPIServiceClassName() - { - return APIService::class; - } - - public function getSiteAccessAwareServiceClassName() - { - return LanguageService::class; - } - - public function providerForPassTroughMethods() - { - $languageCreateStruct = new LanguageCreateStruct(); - $language = new Language(); - - // string $method, array $arguments, bool $return = true - return [ - ['createLanguage', [$languageCreateStruct], $language], - - ['updateLanguageName', [$language, 'Afrikaans'], $language], - - ['enableLanguage', [$language], $language], - - ['disableLanguage', [$language], $language], - - ['loadLanguage', ['eng-GB'], $language], - ['loadLanguageListByCode', [['eng-GB']], []], - - ['loadLanguages', [], []], - - ['loadLanguageById', [4], $language], - ['loadLanguageListById', [[4]], []], - - ['deleteLanguage', [$language], null], - - ['getDefaultLanguageCode', [], ''], - - ['newLanguageCreateStruct', [], $languageCreateStruct], - ]; - } - - public function providerForLanguagesLookupMethods() - { - // string $method, array $arguments, bool $return, int $languageArgumentIndex - return []; - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/LocationServiceTest.php b/eZ/Publish/Core/Repository/SiteAccessAware/Tests/LocationServiceTest.php deleted file mode 100644 index 1d2863e589..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/LocationServiceTest.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\SiteAccessAware\Tests; - -use eZ\Publish\API\Repository\LocationService as APIService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\LocationList; -use eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LocationId; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\Core\Repository\SiteAccessAware\LocationService; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; - -class LocationServiceTest extends AbstractServiceTest -{ - public function getAPIServiceClassName() - { - return APIService::class; - } - - public function getSiteAccessAwareServiceClassName() - { - return LocationService::class; - } - - public function providerForPassTroughMethods(): array - { - $location = new Location(); - $contentInfo = new ContentInfo(); - $locationCreateStruct = new LocationCreateStruct(); - $locationUpdateStruct = new LocationUpdateStruct(); - - // string $method, array $arguments, bool $return = true - return [ - ['copySubtree', [$location, $location], $location], - - ['getLocationChildCount', [$location], 100], - - ['getSubtreeSize', [$location], 100], - - ['createLocation', [$contentInfo, $locationCreateStruct], $location], - - ['updateLocation', [$location, $locationUpdateStruct], $location], - - ['swapLocation', [$location, $location], null], - - ['hideLocation', [$location], $location], - - ['unhideLocation', [$location], $location], - - ['moveSubtree', [$location, $location], null], - - ['deleteLocation', [$location], null], - - ['newLocationCreateStruct', [55], new LocationCreateStruct()], - - ['newLocationUpdateStruct', [], new LocationUpdateStruct()], - - ['getAllLocationsCount', [], 100], - ['loadAllLocations', [10, 100], [$location]], - ]; - } - - public function providerForLanguagesLookupMethods(): array - { - $location = new Location(); - $locationList = new LocationList(); - $contentInfo = new ContentInfo(); - $versionInfo = new VersionInfo(); - - $filter = new Filter(new LocationId(1)); - - // string $method, array $arguments, mixed|null $return, int $languageArgumentIndex, ?callable $callback, ?int $alwaysAvailableArgumentIndex - return [ - ['loadLocation', [55], $location, 1], - ['loadLocation', [55, self::LANG_ARG], $location, 1], - ['loadLocation', [55, self::LANG_ARG, true], $location, 1, null, 2], - - ['loadLocationList', [[55]], [55 => $location], 1], - ['loadLocationList', [[55], self::LANG_ARG], [55 => $location], 1], - ['loadLocationList', [[55], self::LANG_ARG, true], [55 => $location], 1, null, 2], - - ['loadLocationByRemoteId', ['ergemiotregf'], $location, 1], - ['loadLocationByRemoteId', ['ergemiotregf', self::LANG_ARG], $location, 1], - ['loadLocationByRemoteId', ['ergemiotregf', self::LANG_ARG, true], $location, 1, null, 2], - - ['loadLocations', [$contentInfo, null], [$location], 2], - ['loadLocations', [$contentInfo, $location, self::LANG_ARG], [$location], 2], - - ['loadLocationChildren', [$location, 0, 15], $locationList, 3], - ['loadLocationChildren', [$location, 50, 50, self::LANG_ARG], $locationList, 3], - - ['loadParentLocationsForDraftContent', [$versionInfo], [$location], 1], - ['loadParentLocationsForDraftContent', [$versionInfo, self::LANG_ARG], [$location], 1], - - ['find', [$filter], $locationList, 1], - ['find', [$filter, self::LANG_ARG], $locationList, 1], - - ['count', [$filter], 0, 1], - ['count', [$filter, self::LANG_ARG], 0, 1], - ]; - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/ObjectStateServiceTest.php b/eZ/Publish/Core/Repository/SiteAccessAware/Tests/ObjectStateServiceTest.php deleted file mode 100644 index fb38ab0fa6..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/ObjectStateServiceTest.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\SiteAccessAware\Tests; - -use eZ\Publish\API\Repository\ObjectStateService as APIService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateUpdateStruct; -use eZ\Publish\Core\Repository\SiteAccessAware\ObjectStateService; -use eZ\Publish\Core\Repository\Values\ObjectState\ObjectState; -use eZ\Publish\Core\Repository\Values\ObjectState\ObjectStateGroup; - -class ObjectStateServiceTest extends AbstractServiceTest -{ - public function getAPIServiceClassName() - { - return APIService::class; - } - - public function getSiteAccessAwareServiceClassName() - { - return ObjectStateService::class; - } - - public function providerForPassTroughMethods() - { - $objectStateGroupCreateStruct = new ObjectStateGroupCreateStruct(); - $objectStateGroupUpdateStruct = new ObjectStateGroupUpdateStruct(); - $objectStateGroup = new ObjectStateGroup(); - - $objectStateCreateStruct = new ObjectStateCreateStruct(); - $objectStateUpdateStruct = new ObjectStateUpdateStruct(); - $objectState = new ObjectState(); - - $contentInfo = new ContentInfo(); - - // string $method, array $arguments, mixed $return = true - return [ - ['createObjectStateGroup', [$objectStateGroupCreateStruct], $objectStateGroup], - ['updateObjectStateGroup', [$objectStateGroup, $objectStateGroupUpdateStruct], $objectStateGroup], - ['deleteObjectStateGroup', [$objectStateGroup], null], - - ['createObjectState', [$objectStateGroup, $objectStateCreateStruct], $objectState], - ['updateObjectState', [$objectState, $objectStateUpdateStruct], $objectState], - ['setPriorityOfObjectState', [$objectState, 4], null], - ['deleteObjectState', [$objectState], null], - - ['setContentState', [$contentInfo, $objectStateGroup, $objectState], null], - ['getContentState', [$contentInfo, $objectStateGroup], $objectState], - ['getContentCount', [$objectState], 100], - - ['newObjectStateGroupCreateStruct', ['locker'], $objectStateGroupCreateStruct], - ['newObjectStateGroupUpdateStruct', [], $objectStateGroupUpdateStruct], - ['newObjectStateCreateStruct', ['locked'], $objectStateCreateStruct], - ['newObjectStateUpdateStruct', [], $objectStateUpdateStruct], - ]; - } - - public function providerForLanguagesLookupMethods() - { - $objectStateGroup = new ObjectStateGroup(); - $objectState = new ObjectState(); - - // string $method, array $arguments, mixed $return, int $languageArgumentIndex - return [ - ['loadObjectStateGroup', [11, self::LANG_ARG], $objectStateGroup, 1], - ['loadObjectStateGroupByIdentifier', ['ez_lock', self::LANG_ARG], $objectStateGroup, 1], - ['loadObjectStateGroups', [50, 50, self::LANG_ARG], [$objectStateGroup], 2], - ['loadObjectStates', [$objectStateGroup, self::LANG_ARG], [$objectState], 1], - ['loadObjectState', [3, self::LANG_ARG], $objectState, 1], - ['loadObjectStateByIdentifier', [$objectStateGroup, 'locked', self::LANG_ARG], $objectState, 2], - ]; - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/SearchServiceTest.php b/eZ/Publish/Core/Repository/SiteAccessAware/Tests/SearchServiceTest.php deleted file mode 100644 index 02980102b3..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/SearchServiceTest.php +++ /dev/null @@ -1,91 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\SiteAccessAware\Tests; - -use eZ\Publish\API\Repository\SearchService as APIService; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use eZ\Publish\Core\Repository\SiteAccessAware\SearchService; -use eZ\Publish\Core\Repository\Values\Content\Content; - -class SearchServiceTest extends AbstractServiceTest -{ - public function getAPIServiceClassName() - { - return APIService::class; - } - - public function getSiteAccessAwareServiceClassName() - { - return SearchService::class; - } - - public function providerForPassTroughMethods() - { - // string $method, array $arguments, bool $return = true - return [ - ['suggest', ['prefix', [], 11]], - ['supports', [SearchService::CAPABILITY_ADVANCED_FULLTEXT]], - ]; - } - - public function providerForLanguagesLookupMethods() - { - $query = new Query(); - $locationQuery = new LocationQuery(); - $criterion = new Query\Criterion\ContentId(44); - $content = new Content(); - $searchResults = new SearchResult(); - - $callback = function ($languageLookup) { - $this->languageResolverMock - ->expects($this->once()) - ->method('getUseAlwaysAvailable') - ->with($languageLookup ? null : true) - ->willReturn(true); - }; - - // string $method, array $arguments, bool $return, int $languageArgumentIndex, callable $callback - return [ - ['findContent', [$query, self::LANG_ARG, false], $searchResults, 1, $callback], - ['findContentInfo', [$query, self::LANG_ARG, false], $searchResults, 1, $callback], - ['findSingle', [$criterion, self::LANG_ARG, false], $content, 1, $callback], - ['findLocations', [$locationQuery, self::LANG_ARG, false], $searchResults, 1, $callback], - ]; - } - - protected function setLanguagesLookupArguments(array $arguments, $languageArgumentIndex) - { - $arguments[$languageArgumentIndex] = [ - 'languages' => [], - 'useAlwaysAvailable' => null, - ]; - - return $arguments; - } - - protected function setLanguagesLookupExpectedArguments(array $arguments, $languageArgumentIndex, array $languages) - { - $arguments[$languageArgumentIndex] = [ - 'languages' => $languages, - 'useAlwaysAvailable' => true, - ]; - - return $arguments; - } - - protected function setLanguagesPassTroughArguments(array $arguments, $languageArgumentIndex, array $languages) - { - $arguments[$languageArgumentIndex] = [ - 'languages' => $languages, - 'useAlwaysAvailable' => true, - ]; - - return $arguments; - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/TrashServiceTest.php b/eZ/Publish/Core/Repository/SiteAccessAware/Tests/TrashServiceTest.php deleted file mode 100644 index be3810c3c3..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/TrashServiceTest.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\SiteAccessAware\Tests; - -use eZ\Publish\API\Repository\TrashService as APIService; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Trash\SearchResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResultList; -use eZ\Publish\Core\Repository\SiteAccessAware\TrashService; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\TrashItem; - -class TrashServiceTest extends AbstractServiceTest -{ - public function getAPIServiceClassName() - { - return APIService::class; - } - - public function getSiteAccessAwareServiceClassName() - { - return TrashService::class; - } - - public function providerForPassTroughMethods() - { - $location = new Location(); - $newLocation = new Location(); - $trashItem = new TrashItem(); - $query = new Query(); - $searchResult = new SearchResult(); - $trashItemDeleteResult = new TrashItemDeleteResult(); - $trashItemDeleteResultList = new TrashItemDeleteResultList(); - - // string $method, array $arguments, bool $return = true - return [ - ['loadTrashItem', [22], $trashItem], - ['trash', [$location], $trashItem], - ['recover', [$trashItem, $location], $newLocation], - ['emptyTrash', [], $trashItemDeleteResultList], - ['deleteTrashItem', [$trashItem], $trashItemDeleteResult], - ['findTrashItems', [$query], $searchResult], - ]; - } - - public function providerForLanguagesLookupMethods() - { - // string $method, array $arguments, bool $return, int $languageArgumentIndex - return []; - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/UserServiceTest.php b/eZ/Publish/Core/Repository/SiteAccessAware/Tests/UserServiceTest.php deleted file mode 100644 index 1544fc9b42..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/UserServiceTest.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\SiteAccessAware\Tests; - -use DateInterval; -use DateTime; -use eZ\Publish\API\Repository\UserService as APIService; -use eZ\Publish\API\Repository\Values\User\PasswordInfo; -use eZ\Publish\API\Repository\Values\User\PasswordValidationContext; -use eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserTokenUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserUpdateStruct; -use eZ\Publish\Core\Repository\SiteAccessAware\UserService; -use eZ\Publish\Core\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Values\User\User; -use eZ\Publish\Core\Repository\Values\User\UserCreateStruct; -use eZ\Publish\Core\Repository\Values\User\UserGroup; -use eZ\Publish\Core\Repository\Values\User\UserGroupCreateStruct; - -class UserServiceTest extends AbstractServiceTest -{ - public function getAPIServiceClassName() - { - return APIService::class; - } - - public function getSiteAccessAwareServiceClassName() - { - return UserService::class; - } - - public function providerForPassTroughMethods() - { - $userGroupCreateStruct = new UserGroupCreateStruct(); - $userGroupUpdateStruct = new UserGroupUpdateStruct(); - $userGroup = new UserGroup(); - $userGroupId = 1; - - $userCreateStruct = new UserCreateStruct(); - $userUpdateStruct = new UserUpdateStruct(); - $userTokenUpdateStruct = new UserTokenUpdateStruct(); - $user = new User(); - $userId = 14; - $contentType = $this->createMock(ContentType::class); - - $passwordValidationContext = new PasswordValidationContext(); - $passwordExpirationDate = (new DateTime())->add(new DateInterval('P30D')); - $passwordExpirationWarningDate = (new DateTime())->add(new DateInterval('P16D')); - - // string $method, array $arguments, bool $return = true - return [ - ['createUserGroup', [$userGroupCreateStruct, $userGroup], $userGroup], - ['deleteUserGroup', [$userGroup], [$userGroupId]], - ['moveUserGroup', [$userGroup, $userGroup], null], - ['updateUserGroup', [$userGroup, $userGroupUpdateStruct], $userGroup], - - ['createUser', [$userCreateStruct, [$userGroup]], $user], - ['deleteUser', [$user], [$userId]], - ['updateUser', [$user, $userUpdateStruct], $user], - ['updateUserPassword', [$user, 'H@xi0r!'], $user], - - ['assignUserToUserGroup', [$user, $userGroup], null], - ['unAssignUserFromUserGroup', [$user, $userGroup], null], - - ['updateUserToken', [$user, $userTokenUpdateStruct], $user], - ['expireUserToken', ['43ir43jrt43'], null], - - ['newUserCreateStruct', ['adam', 'adam@gmail.com', 'Eve', 'eng-AU', $contentType], $userCreateStruct], - ['newUserGroupCreateStruct', ['eng-AU', $contentType], $userGroupCreateStruct], - ['newUserUpdateStruct', [], $userUpdateStruct], - ['newUserGroupUpdateStruct', [], $userGroupUpdateStruct], - - ['isUser', [$userGroup]], - ['isUserGroup', [$userGroup]], - - ['checkUserCredentials', [$user, 'H@xi0r!']], - ['validatePassword', ['H@xi0r!', $passwordValidationContext], []], - ['getPasswordInfo', [$user], new PasswordInfo()], - ]; - } - - public function providerForLanguagesLookupMethods() - { - $userGroup = new UserGroup(); - $user = new User(); - - // string $method, array $arguments, bool $return, int $languageArgumentIndex - return [ - ['loadUserGroup', [4, self::LANG_ARG], $userGroup, 1], - ['loadUserGroupByRemoteId', ['5f7f0bdb3381d6a461d8c29ff53d908f', self::LANG_ARG], $userGroup, 1], - ['loadSubUserGroups', [$userGroup, 50, 50, self::LANG_ARG], [$userGroup], 3], - ['loadUser', [14, self::LANG_ARG], $user, 1], - ['loadUserByLogin', ['admin', self::LANG_ARG], $user, 1], - ['loadUserByEmail', ['admin@link.invalid', self::LANG_ARG], $user, 1], - ['loadUsersByEmail', ['admin@link.invalid', self::LANG_ARG], [$user], 1], - ['loadUserGroupsOfUser', [$user, 50, 50, self::LANG_ARG], [$userGroup], 3], - ['loadUsersOfUserGroup', [$userGroup, 50, 50, self::LANG_ARG], [$user], 3], - ['loadUserByToken', ['43ir43jrt43', self::LANG_ARG], $user, 1], - ]; - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/TrashService.php b/eZ/Publish/Core/Repository/SiteAccessAware/TrashService.php deleted file mode 100644 index b03e67b1cb..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/TrashService.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\SiteAccessAware; - -use eZ\Publish\API\Repository\TrashService as TrashServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Trash\SearchResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResultList; -use eZ\Publish\API\Repository\Values\Content\TrashItem; - -/** - * TrashService for SiteAccessAware layer. - * - * Currently does nothing but hand over calls to aggregated service. - */ -class TrashService implements TrashServiceInterface -{ - /** @var \eZ\Publish\API\Repository\TrashService */ - protected $service; - - /** - * Construct service object from aggregated service. - * - * @param \eZ\Publish\API\Repository\TrashService $service - */ - public function __construct( - TrashServiceInterface $service - ) { - $this->service = $service; - } - - public function loadTrashItem(int $trashItemId): TrashItem - { - return $this->service->loadTrashItem($trashItemId); - } - - public function trash(Location $location): ?TrashItem - { - return $this->service->trash($location); - } - - public function recover(TrashItem $trashItem, Location $newParentLocation = null): Location - { - return $this->service->recover($trashItem, $newParentLocation); - } - - public function emptyTrash(): TrashItemDeleteResultList - { - return $this->service->emptyTrash(); - } - - public function deleteTrashItem(TrashItem $trashItem): TrashItemDeleteResult - { - return $this->service->deleteTrashItem($trashItem); - } - - public function findTrashItems(Query $query): SearchResult - { - return $this->service->findTrashItems($query); - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/URLAliasService.php b/eZ/Publish/Core/Repository/SiteAccessAware/URLAliasService.php deleted file mode 100644 index 2532315be8..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/URLAliasService.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\SiteAccessAware; - -use eZ\Publish\API\Repository\LanguageResolver; -use eZ\Publish\API\Repository\URLAliasService as URLAliasServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\URLAlias; - -/** - * SiteAccess aware implementation of URLAliasService injecting languages where needed. - */ -class URLAliasService implements URLAliasServiceInterface -{ - /** @var \eZ\Publish\API\Repository\URLAliasService */ - protected $service; - - /** @var \eZ\Publish\API\Repository\LanguageResolver */ - protected $languageResolver; - - /** - * Construct service object from aggregated service and LanguageResolver. - * - * @param \eZ\Publish\API\Repository\URLAliasService $service - * @param \eZ\Publish\API\Repository\LanguageResolver $languageResolver - */ - public function __construct( - URLAliasServiceInterface $service, - LanguageResolver $languageResolver - ) { - $this->service = $service; - $this->languageResolver = $languageResolver; - } - - public function createUrlAlias( - Location $location, - string $path, - string $languageCode, - bool $forwarding = false, - bool $alwaysAvailable = false - ): URLAlias { - return $this->service->createUrlAlias($location, $path, $languageCode, $forwarding, $alwaysAvailable); - } - - public function createGlobalUrlAlias( - string $resource, - string $path, - string $languageCode, - bool $forwarding = false, - bool $alwaysAvailable = false - ): URLAlias { - return $this->service->createGlobalUrlAlias($resource, $path, $languageCode, $forwarding, $alwaysAvailable); - } - - public function listLocationAliases( - Location $location, - bool $custom = true, - ?string $languageCode = null, - ?bool $showAllTranslations = null, - ?array $prioritizedLanguages = null - ): iterable { - return $this->service->listLocationAliases( - $location, - $custom, - $languageCode, - $this->languageResolver->getShowAllTranslations($showAllTranslations), - $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages) - ); - } - - public function listGlobalAliases(?string $languageCode = null, int $offset = 0, int $limit = -1): iterable - { - return $this->service->listGlobalAliases($languageCode, $offset, $limit); - } - - public function removeAliases(array $aliasList): void - { - $this->service->removeAliases($aliasList); - } - - public function lookup(string $url, ?string $languageCode = null): URLAlias - { - return $this->service->lookup($url, $languageCode); - } - - public function reverseLookup( - Location $location, - ?string $languageCode = null, - ?bool $showAllTranslations = null, - ?array $prioritizedLanguages = null - ): URLAlias { - return $this->service->reverseLookup( - $location, - $languageCode, - $this->languageResolver->getShowAllTranslations($showAllTranslations), - $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages) - ); - } - - public function load(string $id): URLAlias - { - return $this->service->load($id); - } - - public function refreshSystemUrlAliasesForLocation(Location $location): void - { - $this->service->refreshSystemUrlAliasesForLocation($location); - } - - public function deleteCorruptedUrlAliases(): int - { - return $this->service->deleteCorruptedUrlAliases(); - } -} diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/UserService.php b/eZ/Publish/Core/Repository/SiteAccessAware/UserService.php deleted file mode 100644 index f99c7e624b..0000000000 --- a/eZ/Publish/Core/Repository/SiteAccessAware/UserService.php +++ /dev/null @@ -1,227 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\SiteAccessAware; - -use eZ\Publish\API\Repository\LanguageResolver; -use eZ\Publish\API\Repository\UserService as UserServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\User\PasswordInfo; -use eZ\Publish\API\Repository\Values\User\PasswordValidationContext; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserTokenUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserUpdateStruct; - -/** - * UserService for SiteAccessAware layer. - * - * Currently does nothing but hand over calls to aggregated service. - */ -class UserService implements UserServiceInterface -{ - /** @var \eZ\Publish\API\Repository\UserService */ - protected $service; - - /** @var \eZ\Publish\API\Repository\LanguageResolver */ - protected $languageResolver; - - /** - * Construct service object from aggregated service. - * - * @param \eZ\Publish\API\Repository\UserService $service - * @param \eZ\Publish\API\Repository\LanguageResolver $languageResolver - */ - public function __construct( - UserServiceInterface $service, - LanguageResolver $languageResolver - ) { - $this->service = $service; - $this->languageResolver = $languageResolver; - } - - public function createUserGroup(UserGroupCreateStruct $userGroupCreateStruct, UserGroup $parentGroup): UserGroup - { - return $this->service->createUserGroup($userGroupCreateStruct, $parentGroup); - } - - public function loadUserGroup(int $id, array $prioritizedLanguages = null): UserGroup - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadUserGroup($id, $prioritizedLanguages); - } - - public function loadUserGroupByRemoteId(string $remoteId, array $prioritizedLanguages = []): UserGroup - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadUserGroupByRemoteId($remoteId, $prioritizedLanguages); - } - - public function loadSubUserGroups(UserGroup $userGroup, int $offset = 0, int $limit = 25, array $prioritizedLanguages = null): iterable - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadSubUserGroups($userGroup, $offset, $limit, $prioritizedLanguages); - } - - public function deleteUserGroup(UserGroup $userGroup): iterable - { - return $this->service->deleteUserGroup($userGroup); - } - - public function moveUserGroup(UserGroup $userGroup, UserGroup $newParent): void - { - $this->service->moveUserGroup($userGroup, $newParent); - } - - public function updateUserGroup(UserGroup $userGroup, UserGroupUpdateStruct $userGroupUpdateStruct): UserGroup - { - return $this->service->updateUserGroup($userGroup, $userGroupUpdateStruct); - } - - public function createUser(UserCreateStruct $userCreateStruct, array $parentGroups): User - { - return $this->service->createUser($userCreateStruct, $parentGroups); - } - - public function loadUser(int $userId, array $prioritizedLanguages = null): User - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadUser($userId, $prioritizedLanguages); - } - - public function checkUserCredentials(User $user, string $credentials): bool - { - return $this->service->checkUserCredentials($user, $credentials); - } - - public function loadUserByLogin(string $login, array $prioritizedLanguages = null): User - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadUserByLogin($login, $prioritizedLanguages); - } - - public function loadUserByEmail(string $email, array $prioritizedLanguages = null): User - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadUserByEmail($email, $prioritizedLanguages); - } - - public function loadUsersByEmail(string $email, array $prioritizedLanguages = null): array - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadUsersByEmail($email, $prioritizedLanguages); - } - - public function deleteUser(User $user): iterable - { - return $this->service->deleteUser($user); - } - - public function updateUser(User $user, UserUpdateStruct $userUpdateStruct): User - { - return $this->service->updateUser($user, $userUpdateStruct); - } - - public function updateUserPassword(User $user, string $newPassword): User - { - return $this->service->updateUserPassword($user, $newPassword); - } - - public function assignUserToUserGroup(User $user, UserGroup $userGroup): void - { - $this->service->assignUserToUserGroup($user, $userGroup); - } - - public function unAssignUserFromUserGroup(User $user, UserGroup $userGroup): void - { - $this->service->unAssignUserFromUserGroup($user, $userGroup); - } - - public function loadUserGroupsOfUser(User $user, int $offset = 0, int $limit = 25, array $prioritizedLanguages = null): iterable - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadUserGroupsOfUser($user, $offset, $limit, $prioritizedLanguages); - } - - public function loadUsersOfUserGroup(UserGroup $userGroup, int $offset = 0, int $limit = 25, array $prioritizedLanguages = null): iterable - { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); - - return $this->service->loadUsersOfUserGroup($userGroup, $offset, $limit, $prioritizedLanguages); - } - - public function loadUserByToken(string $hash, array $prioritizedLanguages = null): User - { - return $this->service->loadUserByToken( - $hash, - $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages) - ); - } - - public function updateUserToken(User $user, UserTokenUpdateStruct $userTokenUpdateStruct): User - { - return $this->service->updateUserToken($user, $userTokenUpdateStruct); - } - - public function expireUserToken(string $hash): void - { - $this->service->expireUserToken($hash); - } - - public function isUser(Content $content): bool - { - return $this->service->isUser($content); - } - - public function isUserGroup(Content $content): bool - { - return $this->service->isUserGroup($content); - } - - public function newUserCreateStruct(string $login, string $email, string $password, string $mainLanguageCode, ?ContentType $contentType = null): UserCreateStruct - { - return $this->service->newUserCreateStruct($login, $email, $password, $mainLanguageCode, $contentType); - } - - public function newUserGroupCreateStruct(string $mainLanguageCode, ?ContentType $contentType = null): UserGroupCreateStruct - { - return $this->service->newUserGroupCreateStruct($mainLanguageCode, $contentType); - } - - public function newUserUpdateStruct(): UserUpdateStruct - { - return $this->service->newUserUpdateStruct(); - } - - public function newUserGroupUpdateStruct(): UserGroupUpdateStruct - { - return $this->service->newUserGroupUpdateStruct(); - } - - public function validatePassword(string $password, PasswordValidationContext $context = null): array - { - return $this->service->validatePassword($password, $context); - } - - public function getPasswordInfo(User $user): PasswordInfo - { - return $this->service->getPasswordInfo($user); - } -} diff --git a/eZ/Publish/Core/Repository/Strategy/ContentThumbnail/Field/ContentFieldStrategy.php b/eZ/Publish/Core/Repository/Strategy/ContentThumbnail/Field/ContentFieldStrategy.php deleted file mode 100644 index 442fe7242c..0000000000 --- a/eZ/Publish/Core/Repository/Strategy/ContentThumbnail/Field/ContentFieldStrategy.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Strategy\ContentThumbnail\Field; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field\FieldTypeBasedThumbnailStrategy; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field\ThumbnailStrategy; -use Traversable; - -final class ContentFieldStrategy implements ThumbnailStrategy -{ - /** @var \eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field\FieldTypeBasedThumbnailStrategy[] */ - private $strategies = []; - - /** - * @param \eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field\FieldTypeBasedThumbnailStrategy[]|\Traversable $strategies - */ - public function __construct(Traversable $strategies) - { - foreach ($strategies as $strategy) { - if ($strategy instanceof FieldTypeBasedThumbnailStrategy) { - $this->addStrategy($strategy->getFieldTypeIdentifier(), $strategy); - } - } - } - - /** - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - */ - public function getThumbnail(Field $field, ?VersionInfo $versionInfo = null): ?Thumbnail - { - if (!$this->hasStrategy($field->fieldTypeIdentifier)) { - throw new NotFoundException('Field\ThumbnailStrategy', $field->fieldTypeIdentifier); - } - - $fieldStrategies = $this->strategies[$field->fieldTypeIdentifier]; - - /** @var \eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field\FieldTypeBasedThumbnailStrategy $fieldStrategy */ - foreach ($fieldStrategies as $fieldStrategy) { - $thumbnail = $fieldStrategy->getThumbnail($field, $versionInfo); - - if ($thumbnail !== null) { - return $thumbnail; - } - } - - return null; - } - - public function hasStrategy(string $fieldTypeIdentifier): bool - { - return !empty($this->strategies[$fieldTypeIdentifier]); - } - - public function addStrategy(string $fieldTypeIdentifier, FieldTypeBasedThumbnailStrategy $thumbnailStrategy): void - { - $this->strategies[$fieldTypeIdentifier][] = $thumbnailStrategy; - } - - /** - * @param \eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field\FieldTypeBasedThumbnailStrategy[]|\Traversable $thumbnailStrategies - */ - public function setStrategies(array $thumbnailStrategies): void - { - $this->strategies = []; - - foreach ($thumbnailStrategies as $thumbnailStrategy) { - $this->addStrategy($thumbnailStrategy->getFieldTypeIdentifier(), $thumbnailStrategy); - } - } -} diff --git a/eZ/Publish/Core/Repository/Strategy/ContentThumbnail/FirstMatchingFieldStrategy.php b/eZ/Publish/Core/Repository/Strategy/ContentThumbnail/FirstMatchingFieldStrategy.php deleted file mode 100644 index b308341662..0000000000 --- a/eZ/Publish/Core/Repository/Strategy/ContentThumbnail/FirstMatchingFieldStrategy.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Strategy\ContentThumbnail; - -use eZ\Publish\API\Repository\FieldTypeService; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field\ThumbnailStrategy as ContentFieldThumbnailStrategy; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy; - -final class FirstMatchingFieldStrategy implements ThumbnailStrategy -{ - /** @var \eZ\Publish\API\Repository\FieldTypeService */ - private $fieldTypeService; - - /** @var \eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field\ThumbnailStrategy */ - private $contentFieldStrategy; - - public function __construct( - ContentFieldThumbnailStrategy $contentFieldStrategy, - FieldTypeService $fieldTypeService - ) { - $this->contentFieldStrategy = $contentFieldStrategy; - $this->fieldTypeService = $fieldTypeService; - } - - public function getThumbnail(ContentType $contentType, array $fields, ?VersionInfo $versionInfo = null): ?Thumbnail - { - $fieldDefinitions = $contentType->getFieldDefinitions(); - - foreach ($fieldDefinitions as $fieldDefinition) { - $field = $this->getFieldByIdentifier($fieldDefinition->identifier, $fields); - - if ($field === null) { - continue; - } - - $fieldType = $this->fieldTypeService->getFieldType($fieldDefinition->fieldTypeIdentifier); - - if ( - $fieldDefinition->isThumbnail - && $this->contentFieldStrategy->hasStrategy($field->fieldTypeIdentifier) - && !$fieldType->isEmptyValue($field->value) - ) { - return $this->contentFieldStrategy->getThumbnail($field, $versionInfo); - } - } - - return null; - } - - private function getFieldByIdentifier(string $identifier, array $fields): ?Field - { - /** @var \eZ\Publish\API\Repository\Values\Content\Field $field */ - foreach ($fields as $field) { - if ($field->fieldDefIdentifier === $identifier) { - return $field; - } - } - - return null; - } -} diff --git a/eZ/Publish/Core/Repository/Strategy/ContentThumbnail/StaticStrategy.php b/eZ/Publish/Core/Repository/Strategy/ContentThumbnail/StaticStrategy.php deleted file mode 100644 index e7a13b3333..0000000000 --- a/eZ/Publish/Core/Repository/Strategy/ContentThumbnail/StaticStrategy.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Strategy\ContentThumbnail; - -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy; - -final class StaticStrategy implements ThumbnailStrategy -{ - /** @var string */ - private $staticThumbnail; - - public function __construct(string $staticThumbnail) - { - $this->staticThumbnail = $staticThumbnail; - } - - public function getThumbnail(ContentType $contentType, array $fields, ?VersionInfo $versionInfo = null): Thumbnail - { - return new Thumbnail([ - 'resource' => $this->staticThumbnail, - ]); - } -} diff --git a/eZ/Publish/Core/Repository/Strategy/ContentThumbnail/ThumbnailChainStrategy.php b/eZ/Publish/Core/Repository/Strategy/ContentThumbnail/ThumbnailChainStrategy.php deleted file mode 100644 index 1b8a324c63..0000000000 --- a/eZ/Publish/Core/Repository/Strategy/ContentThumbnail/ThumbnailChainStrategy.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Strategy\ContentThumbnail; - -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy; - -final class ThumbnailChainStrategy implements ThumbnailStrategy -{ - /** @var \eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy[] */ - private $strategies; - - /** - * @param \eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy[] $strategies - */ - public function __construct(iterable $strategies) - { - $this->strategies = $strategies; - } - - public function getThumbnail(ContentType $contentType, array $fields, ?VersionInfo $versionInfo = null): ?Thumbnail - { - foreach ($this->strategies as $strategy) { - $thumbnail = $strategy->getThumbnail($contentType, $fields, $versionInfo); - - if ($thumbnail !== null) { - return $thumbnail; - } - } - - return null; - } -} diff --git a/eZ/Publish/Core/Repository/Tests/ContentThumbnail/StaticStrategyTest.php b/eZ/Publish/Core/Repository/Tests/ContentThumbnail/StaticStrategyTest.php deleted file mode 100644 index 87f22b78d7..0000000000 --- a/eZ/Publish/Core/Repository/Tests/ContentThumbnail/StaticStrategyTest.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Tests\ContentThumbnail; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Strategy\ContentThumbnail\StaticStrategy; -use PHPUnit\Framework\TestCase; - -class StaticStrategyTest extends TestCase -{ - public function testStaticStrategy() - { - $resource = 'static-test-resource'; - - $staticStrategy = new StaticStrategy($resource); - - $contentTypeMock = $this->createMock(ContentType::class); - $fieldMocks = [ - $this->createMock(Field::class), - $this->createMock(Field::class), - $this->createMock(Field::class), - ]; - - $result = $staticStrategy->getThumbnail( - $contentTypeMock, - $fieldMocks, - ); - - $this->assertEquals( - new Thumbnail([ - 'resource' => $resource, - ]), - $result - ); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Helper/FieldTypeRegistryTest.php b/eZ/Publish/Core/Repository/Tests/Helper/FieldTypeRegistryTest.php deleted file mode 100644 index 2f0c5870e8..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Helper/FieldTypeRegistryTest.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Helper; - -use eZ\Publish\Core\Base\Exceptions\NotFound\FieldTypeNotFoundException; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\SPI\FieldType\FieldType; -use PHPUnit\Framework\TestCase; - -/** - * Unit test case for FieldTypeRegistry helper. - */ -class FieldTypeRegistryTest extends TestCase -{ - private const FIELD_TYPE_ID = 'one'; - - public function testConstructor() - { - $fieldType = $this->getFieldTypeMock(); - $fieldTypes = [self::FIELD_TYPE_ID => $fieldType]; - - $registry = new FieldTypeRegistry($fieldTypes); - $this->assertTrue($registry->hasFieldType(self::FIELD_TYPE_ID)); - } - - protected function getFieldTypeMock() - { - return $this->createMock(FieldType::class); - } - - public function testGetFieldType() - { - $fieldTypes = [ - self::FIELD_TYPE_ID => $this->getFieldTypeMock(), - ]; - - $registry = new FieldTypeRegistry($fieldTypes); - - $fieldType = $registry->getFieldType(self::FIELD_TYPE_ID); - - $this->assertInstanceOf( - FieldType::class, - $fieldType - ); - } - - public function testGetFieldTypeThrowsNotFoundException() - { - $this->expectException(FieldTypeNotFoundException::class); - - $registry = new FieldTypeRegistry([]); - - $registry->getFieldType('none'); - } - - public function testGetFieldTypeThrowsRuntimeExceptionIncorrectType() - { - $this->expectException(\TypeError::class); - - $registry = new FieldTypeRegistry( - [ - 'none' => "I'm not a field type", - ] - ); - - $registry->getFieldType('none'); - } - - public function testGetFieldTypes() - { - $fieldTypes = [ - self::FIELD_TYPE_ID => $this->getFieldTypeMock(), - 'two' => $this->getFieldTypeMock(), - ]; - - $registry = new FieldTypeRegistry($fieldTypes); - - $fieldTypes = $registry->getFieldTypes(); - - $this->assertIsArray($fieldTypes); - $this->assertCount(2, $fieldTypes); - $this->assertArrayHasKey(self::FIELD_TYPE_ID, $fieldTypes); - $this->assertInstanceOf( - FieldType::class, - $fieldTypes[self::FIELD_TYPE_ID] - ); - $this->assertArrayHasKey('two', $fieldTypes); - $this->assertInstanceOf( - FieldType::class, - $fieldTypes['two'] - ); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/LocationResolver/PermissionAwareLocationResolverTest.php b/eZ/Publish/Core/Repository/Tests/LocationResolver/PermissionAwareLocationResolverTest.php deleted file mode 100644 index 50ea4728eb..0000000000 --- a/eZ/Publish/Core/Repository/Tests/LocationResolver/PermissionAwareLocationResolverTest.php +++ /dev/null @@ -1,133 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Tests\LocationResolver; - -use eZ\Publish\API\Repository\Exceptions\BadStateException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\Core\Repository\LocationResolver\PermissionAwareLocationResolver; -use eZ\Publish\Core\Repository\Values\Content\Location; -use PHPUnit\Framework\TestCase; - -final class PermissionAwareLocationResolverTest extends TestCase -{ - /** @var \eZ\Publish\API\Repository\LocationService */ - private $locationService; - - /** @var \eZ\Publish\Core\Repository\LocationResolver\LocationResolver */ - private $locationResolver; - - public function setUp(): void - { - $this->locationService = $this->createMock(LocationService::class); - - $this->locationResolver = new PermissionAwareLocationResolver($this->locationService); - } - - /** - * Test for the resolveLocation() method. - * - * @covers \eZ\Publish\Core\Repository\LocationResolver\PermissionAwareLocationResolver::resolveLocation - */ - public function testResolveMainLocation(): void - { - $contentInfo = new ContentInfo(['mainLocationId' => 42]); - $location = new Location(['id' => 42]); - - // User has access to the main Location - $this->locationService - ->method('loadLocation') - ->willReturn($location); - - $this->assertSame($location, $this->locationResolver->resolveLocation($contentInfo)); - } - - /** - * Test for the resolveLocation() method. - * - * @covers \eZ\Publish\Core\Repository\LocationResolver\PermissionAwareLocationResolver::resolveLocation - */ - public function testResolveSecondaryLocation(): void - { - $contentInfo = new ContentInfo(['mainLocationId' => 42]); - $location1 = new Location(['id' => 43]); - $location2 = new Location(['id' => 44]); - - // User doesn't have access to main location but to the third Content's location - $this->locationService - ->method('loadLocation') - ->willThrowException($this->createMock(UnauthorizedException::class)); - - $this->locationService - ->method('loadLocations') - ->willReturn([$location1, $location2]); - - $this->assertSame($location1, $this->locationResolver->resolveLocation($contentInfo)); - } - - /** - * Test for the resolveLocation() method when Locations don't exist. - * - * @covers \eZ\Publish\Core\Repository\LocationResolver\PermissionAwareLocationResolver::resolveLocation - */ - public function testExpectNotFoundExceptionWhenLocationDoesNotExist(): void - { - $contentInfo = new ContentInfo(['mainLocationId' => 42]); - - $this->locationService - ->method('loadLocation') - ->willThrowException($this->createMock(NotFoundException::class)); - - $this->locationService - ->method('loadLocations') - ->willReturn([]); - - $this->expectException(NotFoundException::class); - - $this->locationResolver->resolveLocation($contentInfo); - } - - /** - * Test for the resolveLocation() method when ContentInfo's mainLocationId is null. - * - * @covers \eZ\Publish\Core\Repository\LocationResolver\PermissionAwareLocationResolver::resolveLocation - */ - public function testExpectNotFoundExceptionWhenMainLocationIdIsNull(): void - { - $contentInfo = new ContentInfo(['mainLocationId' => null]); - - $this->expectException(NotFoundException::class); - - $this->locationResolver->resolveLocation($contentInfo); - } - - /** - * Test for the resolveLocation() method when Location is not yet published. - * - * @covers \eZ\Publish\Core\Repository\LocationResolver\PermissionAwareLocationResolver::resolveLocation - */ - public function testExpectBadStateExceptionWhenContentNotYetPublished(): void - { - $contentInfo = new ContentInfo(['mainLocationId' => 42, 'status' => ContentInfo::STATUS_DRAFT]); - - $this->locationService - ->method('loadLocation') - ->willThrowException($this->createMock(NotFoundException::class)); - - $this->locationService - ->method('loadLocations') - ->willThrowException($this->createMock(BadStateException::class)); - - $this->expectException(BadStateException::class); - - $this->locationResolver->resolveLocation($contentInfo); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/Base.php b/eZ/Publish/Core/Repository/Tests/Service/Mock/Base.php deleted file mode 100644 index ffe97da75d..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/Base.php +++ /dev/null @@ -1,442 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; - -use eZ\Publish\API\Repository\LanguageResolver; -use eZ\Publish\API\Repository\PasswordHashService; -use eZ\Publish\API\Repository\PermissionService; -use eZ\Publish\API\Repository\Repository as APIRepository; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\Repository\FieldTypeService; -use eZ\Publish\Core\Repository\Helper\RelationProcessor; -use eZ\Publish\Core\Repository\Mapper\ContentDomainMapper; -use eZ\Publish\Core\Repository\Mapper\ContentMapper; -use eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper; -use eZ\Publish\Core\Repository\Mapper\RoleDomainMapper; -use eZ\Publish\Core\Repository\Permission\LimitationService; -use eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperFactoryInterface; -use eZ\Publish\Core\Repository\Repository; -use eZ\Publish\Core\Repository\Strategy\ContentValidator\ContentValidatorStrategy; -use eZ\Publish\Core\Repository\User\PasswordValidatorInterface; -use eZ\Publish\Core\Repository\Validator\ContentCreateStructValidator; -use eZ\Publish\Core\Repository\Validator\ContentUpdateStructValidator; -use eZ\Publish\Core\Repository\Validator\VersionValidator; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\User\User; -use eZ\Publish\Core\Search\Common\BackgroundIndexer\NullIndexer; -use eZ\Publish\SPI\Persistence\Filter\Content\Handler as ContentFilteringHandler; -use eZ\Publish\SPI\Persistence\Filter\Location\Handler as LocationFilteringHandler; -use eZ\Publish\SPI\Persistence\Handler; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy; -use eZ\Publish\SPI\Repository\Validator\ContentValidator; -use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\TestCase; - -/** - * Base test case for tests on services using Mock testing. - */ -abstract class Base extends TestCase -{ - /** @var \eZ\Publish\API\Repository\Repository */ - private $repository; - - /** @var \eZ\Publish\API\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject */ - private $repositoryMock; - - /** @var \eZ\Publish\API\Repository\PermissionService|\PHPUnit\Framework\MockObject\MockObject */ - private $permissionServiceMock; - - /** @var \eZ\Publish\SPI\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject */ - private $persistenceMock; - - /** @var \eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy|\PHPUnit\Framework\MockObject\MockObject */ - private $thumbnailStrategyMock; - - /** - * The Content / Location / Search ... handlers for the persistence / Search / .. handler mocks. - * - * @var \PHPUnit\Framework\MockObject\MockObject[] Key is relative to "\eZ\Publish\SPI\" - * - * @see getPersistenceMockHandler() - */ - private $spiMockHandlers = []; - - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper */ - private $contentTypeDomainMapperMock; - - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Mapper\ContentDomainMapper */ - private $contentDomainMapperMock; - - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Permission\LimitationService */ - private $limitationServiceMock; - - /** @var \eZ\Publish\API\Repository\LanguageResolver|\PHPUnit\Framework\MockObject\MockObject */ - private $languageResolverMock; - - /** @var \eZ\Publish\Core\Repository\Mapper\RoleDomainMapper|\PHPUnit\Framework\MockObject\MockObject */ - protected $roleDomainMapperMock; - - /** @var \eZ\Publish\Core\Repository\Mapper\ContentMapper|\PHPUnit\Framework\MockObject\MockObject */ - protected $contentMapperMock; - - /** @var \eZ\Publish\SPI\Repository\Validator\ContentValidator|\PHPUnit\Framework\MockObject\MockObject */ - protected $contentValidatorStrategyMock; - - /** @var \eZ\Publish\SPI\Persistence\Filter\Content\Handler|\PHPUnit\Framework\MockObject\MockObject */ - private $contentFilteringHandlerMock; - - /** @var \eZ\Publish\SPI\Persistence\Filter\Location\Handler|\PHPUnit\Framework\MockObject\MockObject */ - private $locationFilteringHandlerMock; - - /** - * Get Real repository with mocked dependencies. - * - * @param array $serviceSettings If set then non shared instance of Repository is returned - * - * @return \eZ\Publish\API\Repository\Repository - */ - protected function getRepository(array $serviceSettings = []) - { - if ($this->repository === null || !empty($serviceSettings)) { - $repository = new Repository( - $this->getPersistenceMock(), - $this->getSPIMockHandler('Search\\Handler'), - new NullIndexer(), - $this->getRelationProcessorMock(), - $this->getFieldTypeRegistryMock(), - $this->createMock(PasswordHashService::class), - $this->getThumbnailStrategy(), - $this->createMock(ProxyDomainMapperFactoryInterface::class), - $this->getContentDomainMapperMock(), - $this->getContentTypeDomainMapperMock(), - $this->getRoleDomainMapperMock(), - $this->getContentMapper(), - $this->getContentValidatorStrategy(), - $this->getLimitationServiceMock(), - $this->getLanguageResolverMock(), - $this->getPermissionServiceMock(), - $this->getContentFilteringHandlerMock(), - $this->getLocationFilteringHandlerMock(), - $this->createMock(PasswordValidatorInterface::class), - $serviceSettings, - ); - - if (!empty($serviceSettings)) { - return $repository; - } - - $this->repository = $repository; - } - - return $this->repository; - } - - protected $fieldTypeServiceMock; - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\FieldTypeService - */ - protected function getFieldTypeServiceMock() - { - if (!isset($this->fieldTypeServiceMock)) { - $this->fieldTypeServiceMock = $this->createMock(FieldTypeService::class); - } - - return $this->fieldTypeServiceMock; - } - - protected $fieldTypeRegistryMock; - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\FieldType\FieldTypeRegistry - */ - protected function getFieldTypeRegistryMock() - { - if (!isset($this->fieldTypeRegistryMock)) { - $this->fieldTypeRegistryMock = $this->createMock(FieldTypeRegistry::class); - } - - return $this->fieldTypeRegistryMock; - } - - /** - * @return \eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getThumbnailStrategy() - { - if (!isset($this->thumbnailStrategyMock)) { - $this->thumbnailStrategyMock = $this->createMock(ThumbnailStrategy::class); - } - - return $this->thumbnailStrategyMock; - } - - /** - * @return \eZ\Publish\API\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getRepositoryMock() - { - if (!isset($this->repositoryMock)) { - $this->repositoryMock = $this->createMock(APIRepository::class); - } - - return $this->repositoryMock; - } - - /** - * @return \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getPermissionResolverMock() - { - return $this->getPermissionServiceMock(); - } - - /** - * @return \eZ\Publish\API\Repository\PermissionService|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getPermissionServiceMock(): PermissionService - { - if (!isset($this->permissionServiceMock)) { - $this->permissionServiceMock = $this->createMock(PermissionService::class); - } - - return $this->permissionServiceMock; - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Mapper\ContentDomainMapper - */ - protected function getContentDomainMapperMock(): MockObject - { - if (!isset($this->contentDomainMapperMock)) { - $this->contentDomainMapperMock = $this->createMock(ContentDomainMapper::class); - } - - return $this->contentDomainMapperMock; - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper - */ - protected function getContentTypeDomainMapperMock() - { - if (!isset($this->contentTypeDomainMapperMock)) { - $this->contentTypeDomainMapperMock = $this->createMock(ContentTypeDomainMapper::class); - } - - return $this->contentTypeDomainMapperMock; - } - - /** - * Returns a persistence Handler mock. - * - * @return \eZ\Publish\SPI\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getPersistenceMock() - { - if (!isset($this->persistenceMock)) { - $this->persistenceMock = $this->createMock(Handler::class); - - $this->persistenceMock->expects($this->any()) - ->method('contentHandler') - ->will($this->returnValue($this->getPersistenceMockHandler('Content\\Handler'))); - - $this->persistenceMock->expects($this->any()) - ->method('contentTypeHandler') - ->will($this->returnValue($this->getPersistenceMockHandler('Content\\Type\\Handler'))); - - $this->persistenceMock->expects($this->any()) - ->method('contentLanguageHandler') - ->will($this->returnValue($this->getPersistenceMockHandler('Content\\Language\\Handler'))); - - $this->persistenceMock->expects($this->any()) - ->method('locationHandler') - ->will($this->returnValue($this->getPersistenceMockHandler('Content\\Location\\Handler'))); - - $this->persistenceMock->expects($this->any()) - ->method('objectStateHandler') - ->will($this->returnValue($this->getPersistenceMockHandler('Content\\ObjectState\\Handler'))); - - $this->persistenceMock->expects($this->any()) - ->method('trashHandler') - ->will($this->returnValue($this->getPersistenceMockHandler('Content\\Location\\Trash\\Handler'))); - - $this->persistenceMock->expects($this->any()) - ->method('userHandler') - ->will($this->returnValue($this->getPersistenceMockHandler('User\\Handler'))); - - $this->persistenceMock->expects($this->any()) - ->method('sectionHandler') - ->will($this->returnValue($this->getPersistenceMockHandler('Content\\Section\\Handler'))); - - $this->persistenceMock->expects($this->any()) - ->method('urlAliasHandler') - ->will($this->returnValue($this->getPersistenceMockHandler('Content\\UrlAlias\\Handler'))); - - $this->persistenceMock->expects($this->any()) - ->method('urlWildcardHandler') - ->will($this->returnValue($this->getPersistenceMockHandler('Content\\UrlWildcard\\Handler'))); - - $this->persistenceMock->expects($this->any()) - ->method('urlWildcardHandler') - ->will($this->returnValue($this->getPersistenceMockHandler('URL\\Handler'))); - } - - return $this->persistenceMock; - } - - protected function getRelationProcessorMock() - { - return $this->createMock(RelationProcessor::class); - } - - /** - * Returns a SPI Handler mock. - * - * @param string $handler For instance "Content\Type\Handler" or "Search\Handler", must be relative to "eZ\Publish\SPI" - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - protected function getSPIMockHandler($handler) - { - if (!isset($this->spiMockHandlers[$handler])) { - $this->spiMockHandlers[$handler] = $this->getMockBuilder("eZ\\Publish\\SPI\\{$handler}") - ->setMethods([]) - ->disableOriginalConstructor() - ->setConstructorArgs([]) - ->getMock(); - } - - return $this->spiMockHandlers[$handler]; - } - - /** - * Returns a persistence Handler mock. - * - * @param string $handler For instance "Content\Type\Handler", must be relative to "eZ\Publish\SPI\Persistence" - * - * @return \PHPUnit\Framework\MockObject\MockObject - */ - protected function getPersistenceMockHandler($handler) - { - return $this->getSPIMockHandler("Persistence\\{$handler}"); - } - - /** - * Returns User stub with $id as User/Content id. - * - * @param int $id - * - * @return \eZ\Publish\API\Repository\Values\User\User - */ - protected function getStubbedUser($id) - { - return new User( - [ - 'content' => new Content( - [ - 'versionInfo' => new VersionInfo( - [ - 'contentInfo' => new ContentInfo(['id' => $id]), - ] - ), - 'internalFields' => [], - ] - ), - ] - ); - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Permission\LimitationService - */ - protected function getLimitationServiceMock(): MockObject - { - if ($this->limitationServiceMock === null) { - $this->limitationServiceMock = $this->createMock(LimitationService::class); - } - - return $this->limitationServiceMock; - } - - protected function getLanguageResolverMock(): LanguageResolver - { - if ($this->languageResolverMock === null) { - $this->languageResolverMock = $this->createMock(LanguageResolver::class); - } - - return $this->languageResolverMock; - } - - /** - * @param string[] $methods - * - * @return \eZ\Publish\Core\Repository\Mapper\RoleDomainMapper|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getRoleDomainMapperMock(array $methods = []): RoleDomainMapper - { - if ($this->roleDomainMapperMock === null) { - $mockBuilder = $this->getMockBuilder(RoleDomainMapper::class); - if (!empty($methods)) { - $mockBuilder->onlyMethods($methods); - } - $this->roleDomainMapperMock = $mockBuilder - ->disableOriginalConstructor() - ->getMock(); - } - - return $this->roleDomainMapperMock; - } - - protected function getContentMapper(): ContentMapper - { - return new ContentMapper( - $this->getPersistenceMock()->contentLanguageHandler(), - $this->getFieldTypeRegistryMock() - ); - } - - protected function getContentValidatorStrategy(): ContentValidator - { - $validators = [ - new ContentCreateStructValidator( - $this->getContentMapper(), - $this->getFieldTypeRegistryMock() - ), - new ContentUpdateStructValidator( - $this->getContentMapper(), - $this->getFieldTypeRegistryMock(), - $this->getPersistenceMock()->contentLanguageHandler() - ), - new VersionValidator( - $this->getFieldTypeRegistryMock(), - ), - ]; - - return new ContentValidatorStrategy($validators); - } - - protected function getContentFilteringHandlerMock(): ContentFilteringHandler - { - if (null === $this->contentFilteringHandlerMock) { - $this->contentFilteringHandlerMock = $this->createMock(ContentFilteringHandler::class); - } - - return $this->contentFilteringHandlerMock; - } - - private function getLocationFilteringHandlerMock(): LocationFilteringHandler - { - if (null === $this->locationFilteringHandlerMock) { - $this->locationFilteringHandlerMock = $this->createMock(LocationFilteringHandler::class); - } - - return $this->locationFilteringHandlerMock; - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/ContentTest.php b/eZ/Publish/Core/Repository/Tests/Service/Mock/ContentTest.php deleted file mode 100644 index 4ca72593b7..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/ContentTest.php +++ /dev/null @@ -1,6318 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; - -use Exception; -use eZ\Publish\API\Repository\ContentTypeService as APIContentTypeService; -use eZ\Publish\API\Repository\Exceptions\BadStateException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\LocationService as APILocationService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct as APIContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\ContentInfo as APIContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType as APIContentType; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition; -use eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException; -use eZ\Publish\Core\Base\Exceptions\ContentValidationException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Value; -use eZ\Publish\Core\Repository\ContentService; -use eZ\Publish\Core\Repository\Helper\NameSchemaService; -use eZ\Publish\Core\Repository\Helper\RelationProcessor; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection; -use eZ\Publish\Core\Repository\Values\User\UserReference; -use eZ\Publish\SPI\FieldType\FieldType; -use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use eZ\Publish\SPI\Persistence\Content as SPIContent; -use eZ\Publish\SPI\Persistence\Content\ContentInfo as SPIContentInfo; -use eZ\Publish\SPI\Persistence\Content\CreateStruct as SPIContentCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Field as SPIField; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; -use eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct as SPIMetadataUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\ObjectState as SPIObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group as SPIObjectStateGroup; -use eZ\Publish\SPI\Persistence\Content\UpdateStruct as SPIContentUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\VersionInfo as SPIVersionInfo; -use Ibexa\Contracts\Core\Limitation\Target\DestinationLocation; - -/** - * Mock test case for Content service. - */ -class ContentTest extends BaseServiceMockTest -{ - private const EMPTY_FIELD_VALUE = 'empty'; - - /** - * Test for the __construct() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::__construct - */ - public function testConstructor(): void - { - $repositoryMock = $this->getRepositoryMock(); - /** @var \eZ\Publish\SPI\Persistence\Handler $persistenceHandlerMock */ - $persistenceHandlerMock = $this->getPersistenceMockHandler('Handler'); - $contentDomainMapperMock = $this->getContentDomainMapperMock(); - $relationProcessorMock = $this->getRelationProcessorMock(); - $nameSchemaServiceMock = $this->getNameSchemaServiceMock(); - $fieldTypeRegistryMock = $this->getFieldTypeRegistryMock(); - $permissionServiceMock = $this->getPermissionServiceMock(); - $contentMapper = $this->getContentMapper(); - $contentValidatorStrategy = $this->getContentValidatorStrategy(); - $contentFilteringHandlerMock = $this->getContentFilteringHandlerMock(); - $settings = [ - 'default_version_archive_limit' => 10, - 'remove_archived_versions_on_publish' => true, - ]; - - new ContentService( - $repositoryMock, - $persistenceHandlerMock, - $contentDomainMapperMock, - $relationProcessorMock, - $nameSchemaServiceMock, - $fieldTypeRegistryMock, - $permissionServiceMock, - $contentMapper, - $contentValidatorStrategy, - $contentFilteringHandlerMock, - $settings - ); - } - - /** - * Test for the loadVersionInfo() method, of published version. - * - * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById - */ - public function testLoadVersionInfoById() - { - $contentServiceMock = $this->getPartlyMockedContentService(['loadContentInfo']); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - $domainMapperMock = $this->getContentDomainMapperMock(); - $versionInfoMock = $this->createMock(APIVersionInfo::class); - $permissionResolver = $this->getPermissionResolverMock(); - - $versionInfoMock->expects($this->once()) - ->method('isPublished') - ->willReturn(true); - - $contentServiceMock->expects($this->never()) - ->method('loadContentInfo'); - - $contentHandler->expects($this->once()) - ->method('loadVersionInfo') - ->with( - $this->equalTo(42), - $this->equalTo(null) - )->will( - $this->returnValue(new SPIVersionInfo()) - ); - - $domainMapperMock->expects($this->once()) - ->method('buildVersionInfoDomainObject') - ->with(new SPIVersionInfo()) - ->will($this->returnValue($versionInfoMock)); - - $permissionResolver->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('read'), - $this->equalTo($versionInfoMock) - )->will($this->returnValue(true)); - - $result = $contentServiceMock->loadVersionInfoById(42); - - $this->assertEquals($versionInfoMock, $result); - } - - /** - * Test for the loadVersionInfo() method, of a draft. - * - * @depends testLoadVersionInfoById - * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById - */ - public function testLoadVersionInfoByIdAndVersionNumber() - { - $permissionResolver = $this->getPermissionResolverMock(); - $contentServiceMock = $this->getPartlyMockedContentService(['loadContentInfo']); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - $domainMapperMock = $this->getContentDomainMapperMock(); - $versionInfoMock = $this->createMock(APIVersionInfo::class); - - $versionInfoMock->expects($this->any()) - ->method('__get') - ->with('status') - ->willReturn(APIVersionInfo::STATUS_DRAFT); - - $contentServiceMock->expects($this->never()) - ->method('loadContentInfo'); - - $contentHandler->expects($this->once()) - ->method('loadVersionInfo') - ->with( - $this->equalTo(42), - $this->equalTo(2) - )->willReturn(new SPIVersionInfo()); - - $domainMapperMock->expects($this->once()) - ->method('buildVersionInfoDomainObject') - ->with(new SPIVersionInfo()) - ->willReturn($versionInfoMock); - - $permissionResolver->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('versionread'), - $this->equalTo($versionInfoMock) - )->willReturn(true); - - $result = $contentServiceMock->loadVersionInfoById(42, 2); - - $this->assertEquals($versionInfoMock, $result); - } - - /** - * Test for the loadVersionInfo() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById - */ - public function testLoadVersionInfoByIdThrowsNotFoundException() - { - $this->expectException(NotFoundException::class); - - $contentServiceMock = $this->getPartlyMockedContentService(['loadContentInfo']); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - - $contentHandler->expects($this->once()) - ->method('loadVersionInfo') - ->with( - $this->equalTo(42), - $this->equalTo(24) - )->will( - $this->throwException( - new NotFoundException( - 'Content', - [ - 'contentId' => 42, - 'versionNo' => 24, - ] - ) - ) - ); - - $contentServiceMock->loadVersionInfoById(42, 24); - } - - /** - * Test for the loadVersionInfo() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById - */ - public function testLoadVersionInfoByIdThrowsUnauthorizedExceptionNonPublishedVersion() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class); - - $contentServiceMock = $this->getPartlyMockedContentService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - $domainMapperMock = $this->getContentDomainMapperMock(); - $versionInfoMock = $this->createMock(APIVersionInfo::class); - $permissionResolver = $this->getPermissionResolverMock(); - - $versionInfoMock->expects($this->any()) - ->method('isPublished') - ->willReturn(false); - - $contentHandler->expects($this->once()) - ->method('loadVersionInfo') - ->with( - $this->equalTo(42), - $this->equalTo(24) - )->will( - $this->returnValue(new SPIVersionInfo()) - ); - - $domainMapperMock->expects($this->once()) - ->method('buildVersionInfoDomainObject') - ->with(new SPIVersionInfo()) - ->will($this->returnValue($versionInfoMock)); - - $permissionResolver->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('versionread'), - $this->equalTo($versionInfoMock) - )->will($this->returnValue(false)); - - $contentServiceMock->loadVersionInfoById(42, 24); - } - - /** - * Test for the loadVersionInfo() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById - */ - public function testLoadVersionInfoByIdPublishedVersion() - { - $contentServiceMock = $this->getPartlyMockedContentService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - $domainMapperMock = $this->getContentDomainMapperMock(); - $versionInfoMock = $this->createMock(APIVersionInfo::class); - $permissionResolver = $this->getPermissionResolverMock(); - - $versionInfoMock->expects($this->once()) - ->method('isPublished') - ->willReturn(true); - - $contentHandler->expects($this->once()) - ->method('loadVersionInfo') - ->with( - $this->equalTo(42), - $this->equalTo(24) - )->will( - $this->returnValue(new SPIVersionInfo()) - ); - - $domainMapperMock->expects($this->once()) - ->method('buildVersionInfoDomainObject') - ->with(new SPIVersionInfo()) - ->will($this->returnValue($versionInfoMock)); - - $permissionResolver->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('read'), - $this->equalTo($versionInfoMock) - )->will($this->returnValue(true)); - - $result = $contentServiceMock->loadVersionInfoById(42, 24); - - $this->assertEquals($versionInfoMock, $result); - } - - /** - * Test for the loadVersionInfo() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfoById - */ - public function testLoadVersionInfoByIdNonPublishedVersion() - { - $contentServiceMock = $this->getPartlyMockedContentService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - $domainMapperMock = $this->getContentDomainMapperMock(); - $versionInfoMock = $this->createMock(APIVersionInfo::class); - $permissionResolver = $this->getPermissionResolverMock(); - - $versionInfoMock->expects($this->once()) - ->method('isPublished') - ->willReturn(false); - - $contentHandler->expects($this->once()) - ->method('loadVersionInfo') - ->with( - $this->equalTo(42), - $this->equalTo(24) - )->will( - $this->returnValue(new SPIVersionInfo()) - ); - - $domainMapperMock->expects($this->once()) - ->method('buildVersionInfoDomainObject') - ->with(new SPIVersionInfo()) - ->will($this->returnValue($versionInfoMock)); - - $permissionResolver->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('versionread'), - $this->equalTo($versionInfoMock) - )->will($this->returnValue(true)); - - $result = $contentServiceMock->loadVersionInfoById(42, 24); - - $this->assertEquals($versionInfoMock, $result); - } - - /** - * Test for the loadVersionInfo() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::loadVersionInfo - * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoById - * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdThrowsNotFoundException - * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdThrowsUnauthorizedExceptionNonPublishedVersion - * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdPublishedVersion - * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest::testLoadVersionInfoByIdNonPublishedVersion - */ - public function testLoadVersionInfo() - { - $expectedResult = $this->createMock(VersionInfo::class); - - $contentServiceMock = $this->getPartlyMockedContentService( - ['loadVersionInfoById'] - ); - $contentServiceMock->expects( - $this->once() - )->method( - 'loadVersionInfoById' - )->with( - $this->equalTo(42), - $this->equalTo(7) - )->will( - $this->returnValue($expectedResult) - ); - - $result = $contentServiceMock->loadVersionInfo( - new ContentInfo(['id' => 42]), - 7 - ); - - $this->assertEquals($expectedResult, $result); - } - - public function testLoadContent() - { - $contentService = $this->getPartlyMockedContentService(['internalLoadContentById']); - $content = $this->createMock(APIContent::class); - $versionInfo = $this->createMock(APIVersionInfo::class); - $permissionResolver = $this->getPermissionResolverMock(); - - $content - ->expects($this->once()) - ->method('getVersionInfo') - ->will($this->returnValue($versionInfo)); - $versionInfo - ->expects($this->once()) - ->method('isPublished') - ->willReturn(true); - $contentId = 123; - $contentService - ->expects($this->once()) - ->method('internalLoadContentById') - ->with($contentId) - ->will($this->returnValue($content)); - - $permissionResolver - ->expects($this->once()) - ->method('canUser') - ->with('content', 'read', $content) - ->will($this->returnValue(true)); - - $this->assertSame($content, $contentService->loadContent($contentId)); - } - - public function testLoadContentNonPublished() - { - $contentService = $this->getPartlyMockedContentService(['internalLoadContentById']); - $content = $this->createMock(APIContent::class); - $versionInfo = $this->createMock(APIVersionInfo::class); - $permissionResolver = $this->getPermissionResolverMock(); - - $content - ->expects($this->once()) - ->method('getVersionInfo') - ->will($this->returnValue($versionInfo)); - $contentId = 123; - $contentService - ->expects($this->once()) - ->method('internalLoadContentById') - ->with($contentId) - ->will($this->returnValue($content)); - - $permissionResolver - ->expects($this->exactly(2)) - ->method('canUser') - ->will( - $this->returnValueMap( - [ - ['content', 'read', $content, [], true], - ['content', 'versionread', $content, [], true], - ] - ) - ); - - $this->assertSame($content, $contentService->loadContent($contentId)); - } - - public function testLoadContentUnauthorized() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class); - - $permissionResolver = $this->getPermissionResolverMock(); - - $contentService = $this->getPartlyMockedContentService(['internalLoadContentById']); - $content = $this->createMock(APIContent::class); - $contentId = 123; - $contentService - ->expects($this->once()) - ->method('internalLoadContentById') - ->with($contentId) - ->will($this->returnValue($content)); - - $permissionResolver - ->expects($this->once()) - ->method('canUser') - ->with('content', 'read', $content) - ->will($this->returnValue(false)); - - $contentService->loadContent($contentId); - } - - public function testLoadContentNotPublishedStatusUnauthorized() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class); - - $permissionResolver = $this->getPermissionResolverMock(); - $contentService = $this->getPartlyMockedContentService(['internalLoadContentById']); - $content = $this->createMock(APIContent::class); - $versionInfo = $this - ->getMockBuilder(APIVersionInfo::class) - ->getMockForAbstractClass(); - $content - ->expects($this->once()) - ->method('getVersionInfo') - ->will($this->returnValue($versionInfo)); - $contentId = 123; - $contentService - ->expects($this->once()) - ->method('internalLoadContentById') - ->with($contentId) - ->will($this->returnValue($content)); - - $permissionResolver - ->expects($this->exactly(2)) - ->method('canUser') - ->will( - $this->returnValueMap( - [ - ['content', 'read', $content, [], true], - ['content', 'versionread', $content, [], false], - ] - ) - ); - - $contentService->loadContent($contentId); - } - - /** - * @dataProvider internalLoadContentProviderById - */ - public function testInternalLoadContentById(int $id, ?array $languages, ?int $versionNo, bool $useAlwaysAvailable): void - { - if (!empty($languages) && $useAlwaysAvailable) { - $spiContentInfo = new SPIContentInfo(['id' => $id, 'alwaysAvailable' => false]); - } else { - $spiContentInfo = new SPIContentInfo(['id' => $id]); - } - - $spiContent = new SPIContent([ - 'versionInfo' => new VersionInfo([ - 'contentInfo' => new ContentInfo([ - 'id' => 42, - 'contentTypeId' => 123, - ]), - ]), - ]); - - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - $contentHandler - ->expects($this->once()) - ->method('loadContentInfo') - ->with($id) - ->will($this->returnValue($spiContentInfo)); - - $contentHandler - ->expects($this->once()) - ->method('load') - ->with($id, $versionNo, $languages) - ->willReturn($spiContent); - - $contentService = $this->getPartlyMockedContentService(); - - $expectedContent = $this->mockBuildContentDomainObject($spiContent, $languages); - $actualContent = $contentService->internalLoadContentById($id, $languages, $versionNo, $useAlwaysAvailable); - - $this->assertSame($expectedContent, $actualContent); - } - - /** - * @dataProvider internalLoadContentProviderByRemoteId - */ - public function testInternalLoadContentByRemoteId(string $remoteId, ?array $languages, ?int $versionNo, bool $useAlwaysAvailable) - { - $realId = 123; - - $spiContentInfo = new SPIContentInfo([ - 'currentVersionNo' => $versionNo ?: 7, - 'id' => $realId, - ]); - - $spiContent = new SPIContent([ - 'versionInfo' => new VersionInfo([ - 'contentInfo' => new ContentInfo(['id' => 42, 'contentTypeId' => 123]), - ]), - ]); - - $contentService = $this->getPartlyMockedContentService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - $contentHandler - ->expects($this->once()) - ->method('loadContentInfoByRemoteId') - ->with($remoteId) - ->will($this->returnValue($spiContentInfo)); - - $contentHandler - ->expects($this->once()) - ->method('load') - ->with($realId, $versionNo, $languages) - ->willReturn($spiContent); - - $expectedContent = $this->mockBuildContentDomainObject($spiContent, $languages); - - $actualContent = $contentService->internalLoadContentByRemoteId( - $remoteId, - $languages, - $versionNo, - $useAlwaysAvailable - ); - - $this->assertSame($expectedContent, $actualContent); - } - - public function internalLoadContentProviderById(): array - { - return [ - [123, null, null, false], - [123, null, 456, false], - [456, null, 123, true], - [456, null, 2, false], - [456, ['eng-GB'], 2, true], - [456, ['eng-GB', 'fre-FR'], null, false], - [456, ['eng-GB', 'fre-FR', 'nor-NO'], 2, false], - ]; - } - - public function internalLoadContentProviderByRemoteId(): array - { - return [ - ['123', null, null, false], - ['someRemoteId', null, 456, false], - ['456', null, 123, false], - ['someRemoteId', null, 2, false], - ['someRemoteId', ['eng-GB'], 2, false], - ['456', ['eng-GB', 'fre-FR'], null, false], - ['someRemoteId', ['eng-GB', 'fre-FR', 'nor-NO'], 2, false], - ]; - } - - public function testInternalLoadContentByIdNotFound(): void - { - $this->expectException(NotFoundException::class); - - $id = 123; - $versionNo = 7; - $languages = null; - - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - $contentHandler - ->expects($this->once()) - ->method('loadContentInfo') - ->with($id) - ->willReturn(new SPIContent\ContentInfo(['id' => $id])); - - $contentHandler - ->expects($this->once()) - ->method('load') - ->with($id, $versionNo, $languages) - ->will( - $this->throwException( - $this->createMock(APINotFoundException::class) - ) - ); - - $contentService = $this->getPartlyMockedContentService(); - $contentService->internalLoadContentById($id, $languages, $versionNo); - } - - public function testInternalLoadContentByRemoteIdNotFound(): void - { - $this->expectException(NotFoundException::class); - - $remoteId = 'dca290623518d393126d3408b45af6ee'; - $id = 123; - $versionNo = 7; - $languages = null; - - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - $contentHandler - ->expects($this->once()) - ->method('loadContentInfoByRemoteId') - ->with($remoteId) - ->willReturn(new SPIContent\ContentInfo(['id' => $id])); - - $contentHandler - ->expects($this->once()) - ->method('load') - ->with($id, $versionNo, $languages) - ->willThrowException( - $this->createMock(APINotFoundException::class) - ); - - $contentService = $this->getPartlyMockedContentService(); - $contentService->internalLoadContentByRemoteId($remoteId, $languages, $versionNo); - } - - /** - * Test for the loadContentByContentInfo() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::loadContentByContentInfo - */ - public function testLoadContentByContentInfo() - { - $versionInfo = $this->createMock(APIVersionInfo::class); - $content = $this->createMock(APIContent::class); - $content->method('getVersionInfo') - ->will($this->returnValue($versionInfo)); - - $permissionResolver = $this->getPermissionResolverMock(); - $permissionResolver->expects($this->any()) - ->method('canUser') - ->will($this->returnValue(true)); - - $contentServiceMock = $this->getPartlyMockedContentService( - ['internalLoadContentById'] - ); - - $contentServiceMock - ->method( - 'internalLoadContentById' - )->with( - $this->equalTo(42), - $this->equalTo(['cro-HR']), - $this->equalTo(7), - $this->equalTo(false) - )->will( - $this->returnValue($content) - ); - - $result = $contentServiceMock->loadContentByContentInfo( - new ContentInfo(['id' => 42]), - ['cro-HR'], - 7 - ); - - $this->assertEquals($content, $result); - } - - /** - * Test for the loadContentByVersionInfo() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::loadContentByVersionInfo - */ - public function testLoadContentByVersionInfo() - { - $expectedResult = $this->createMock(Content::class); - - $contentServiceMock = $this->getPartlyMockedContentService( - ['loadContent'] - ); - $contentServiceMock->expects( - $this->once() - )->method( - 'loadContent' - )->with( - $this->equalTo(42), - $this->equalTo(['cro-HR']), - $this->equalTo(7), - $this->equalTo(false) - )->will( - $this->returnValue($expectedResult) - ); - - $result = $contentServiceMock->loadContentByVersionInfo( - new VersionInfo( - [ - 'contentInfo' => new ContentInfo(['id' => 42]), - 'versionNo' => 7, - ] - ), - ['cro-HR'] - ); - - $this->assertEquals($expectedResult, $result); - } - - /** - * Test for the deleteContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteContent - */ - public function testDeleteContentThrowsUnauthorizedException() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class); - - $permissionResolver = $this->getPermissionResolverMock(); - $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfoById']); - $contentInfo = $this->createMock(APIContentInfo::class); - - $contentInfo->expects($this->any()) - ->method('__get') - ->willReturnMap( - [ - ['id', 42], - ['currentVersionNo', 7], - ] - ); - - $persistenceHandlerMock = $this->getPersistenceMockHandler('Handler'); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - - $contentHandler - ->expects($this->once()) - ->method('loadVersionInfo') - ->with( - $this->equalTo(42), - $this->equalTo(7) - )->will( - $this->returnValue(new SPIVersionInfo()) - ); - - $contentService->expects($this->once()) - ->method('internalLoadContentInfoById') - ->with(42) - ->will($this->returnValue($contentInfo)); - - $permissionResolver->expects($this->once()) - ->method('canUser') - ->with('content', 'remove') - ->will($this->returnValue(false)); - - /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */ - $contentService->deleteContent($contentInfo); - } - - /** - * Test for the deleteContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteContent - */ - public function testDeleteContent() - { - $repository = $this->getRepositoryMock(); - $permissionResolver = $this->getPermissionResolverMock(); - - $permissionResolver->expects($this->once()) - ->method('canUser') - ->with('content', 'remove') - ->will($this->returnValue(true)); - - $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfoById']); - /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandler */ - $urlAliasHandler = $this->getPersistenceMock()->urlAliasHandler(); - /** @var \PHPUnit\Framework\MockObject\MockObject $locationHandler */ - $locationHandler = $this->getPersistenceMock()->locationHandler(); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - - $contentInfo = $this->createMock(APIContentInfo::class); - - $contentService->expects($this->once()) - ->method('internalLoadContentInfoById') - ->with(42) - ->will($this->returnValue($contentInfo)); - - $contentInfo->expects($this->any()) - ->method('__get') - ->willReturnMap( - [ - ['id', 42], - ['currentVersionNo', 7], - ] - ); - - $persistenceHandlerMock = $this->getPersistenceMockHandler('Handler'); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - - $contentHandler - ->expects($this->once()) - ->method('loadVersionInfo') - ->with( - $this->equalTo(42), - $this->equalTo(7) - )->will( - $this->returnValue(new SPIVersionInfo()) - ); - - $repository->expects($this->once())->method('beginTransaction'); - - $spiLocations = [ - new SPILocation(['id' => 1]), - new SPILocation(['id' => 2]), - ]; - $locationHandler->expects($this->once()) - ->method('loadLocationsByContent') - ->with(42) - ->will($this->returnValue($spiLocations)); - - $contentHandler->expects($this->once()) - ->method('deleteContent') - ->with(42); - - foreach ($spiLocations as $index => $spiLocation) { - $urlAliasHandler->expects($this->at($index)) - ->method('locationDeleted') - ->with($spiLocation->id); - } - - $repository->expects($this->once())->method('commit'); - - /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */ - $contentService->deleteContent($contentInfo); - } - - /** - * Test for the deleteContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteContent - */ - public function testDeleteContentWithRollback() - { - $this->expectException(\Exception::class); - - $repository = $this->getRepositoryMock(); - $permissionResolver = $this->getPermissionResolverMock(); - - $permissionResolver->expects($this->once()) - ->method('canUser') - ->with('content', 'remove') - ->will($this->returnValue(true)); - - $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfoById']); - /** @var \PHPUnit\Framework\MockObject\MockObject $locationHandler */ - $locationHandler = $this->getPersistenceMock()->locationHandler(); - - $contentInfo = $this->createMock(APIContentInfo::class); - - $contentService->expects($this->once()) - ->method('internalLoadContentInfoById') - ->with(42) - ->will($this->returnValue($contentInfo)); - - $contentInfo->expects($this->any()) - ->method('__get') - ->willReturnMap( - [ - ['id', 42], - ['currentVersionNo', 7], - ] - ); - - $persistenceHandlerMock = $this->getPersistenceMockHandler('Handler'); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - - $contentHandler - ->expects($this->once()) - ->method('loadVersionInfo') - ->with( - $this->equalTo(42), - $this->equalTo(7) - )->will( - $this->returnValue(new SPIVersionInfo()) - ); - - $repository->expects($this->once())->method('beginTransaction'); - - $locationHandler->expects($this->once()) - ->method('loadLocationsByContent') - ->with(42) - ->will($this->throwException(new \Exception())); - - $repository->expects($this->once())->method('rollback'); - - /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */ - $contentService->deleteContent($contentInfo); - } - - /** - * Test for the deleteVersion() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::deleteVersion - */ - public function testDeleteVersionThrowsBadStateExceptionLastVersion() - { - $this->expectException(BadStateException::class); - - $repository = $this->getRepositoryMock(); - $permissionResolver = $this->getPermissionResolverMock(); - - $permissionResolver - ->expects($this->once()) - ->method('canUser') - ->with('content', 'versionremove') - ->will($this->returnValue(true)); - $repository - ->expects($this->never()) - ->method('beginTransaction'); - - $contentService = $this->getPartlyMockedContentService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ - $contentHandler = $this->getPersistenceMock()->contentHandler(); - $contentInfo = $this->createMock(APIContentInfo::class); - $versionInfo = $this->createMock(APIVersionInfo::class); - - $contentInfo - ->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(42)); - - $versionInfo - ->expects($this->any()) - ->method('__get') - ->will( - $this->returnValueMap( - [ - ['versionNo', 123], - ['contentInfo', $contentInfo], - ] - ) - ); - $versionInfo - ->expects($this->once()) - ->method('isPublished') - ->willReturn(false); - - $contentHandler - ->expects($this->once()) - ->method('listVersions') - ->with(42) - ->will($this->returnValue(['version'])); - - /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo */ - $contentService->deleteVersion($versionInfo); - } - - /** - * Test for the createContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - */ - public function testCreateContentThrowsInvalidArgumentExceptionMainLanguageCodeNotSet() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$contentCreateStruct\' is invalid: the \'mainLanguageCode\' property must be set'); - - $mockedService = $this->getPartlyMockedContentService(); - $mockedService->createContent(new ContentCreateStruct(), []); - } - - /** - * Test for the createContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - */ - public function testCreateContentThrowsInvalidArgumentExceptionContentTypeNotSet() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$contentCreateStruct\' is invalid: the \'contentType\' property must be set'); - - $mockedService = $this->getPartlyMockedContentService(); - $mockedService->createContent( - new ContentCreateStruct(['mainLanguageCode' => 'eng-US']), - [] - ); - } - - /** - * Test for the createContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - */ - public function testCreateContentThrowsUnauthorizedException() - { - $this->expectException(UnauthorizedException::class); - - $repositoryMock = $this->getRepositoryMock(); - - $permissionResolver = $this->getPermissionResolverMock(); - $permissionResolver->expects($this->once()) - ->method('getCurrentUserReference') - ->will($this->returnValue(new UserReference(169))); - - $mockedService = $this->getPartlyMockedContentService(); - $contentTypeServiceMock = $this->getContentTypeServiceMock(); - $contentType = new ContentType( - [ - 'id' => 123, - 'fieldDefinitions' => [], - ] - ); - $contentCreateStruct = new ContentCreateStruct( - [ - 'ownerId' => 169, - 'alwaysAvailable' => false, - 'mainLanguageCode' => 'eng-US', - 'contentType' => $contentType, - ] - ); - - $contentTypeServiceMock->expects($this->once()) - ->method('loadContentType') - ->with($this->equalTo(123)) - ->will($this->returnValue($contentType)); - - $repositoryMock->expects($this->once()) - ->method('getContentTypeService') - ->will($this->returnValue($contentTypeServiceMock)); - - $permissionResolver->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('create'), - $this->isInstanceOf(get_class($contentCreateStruct)), - $this->equalTo([]) - )->will($this->returnValue(false)); - - $mockedService->createContent( - new ContentCreateStruct( - [ - 'mainLanguageCode' => 'eng-US', - 'contentType' => $contentType, - ] - ), - [] - ); - } - - /** - * Test for the createContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - * @exceptionMessage Argument '$contentCreateStruct' is invalid: Another content with remoteId 'faraday' exists - */ - public function testCreateContentThrowsInvalidArgumentExceptionDuplicateRemoteId() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $repositoryMock = $this->getRepositoryMock(); - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock - ->expects($this->once()) - ->method('getCurrentUserReference') - ->willReturn($this->createMock(UserReference::class)); - - $mockedService = $this->getPartlyMockedContentService(['loadContentByRemoteId']); - $contentTypeServiceMock = $this->getContentTypeServiceMock(); - $contentType = new ContentType( - [ - 'id' => 123, - 'fieldDefinitions' => [], - ] - ); - $contentCreateStruct = new ContentCreateStruct( - [ - 'ownerId' => 169, - 'alwaysAvailable' => false, - 'remoteId' => 'faraday', - 'mainLanguageCode' => 'eng-US', - 'contentType' => $contentType, - ] - ); - - $contentTypeServiceMock->expects($this->once()) - ->method('loadContentType') - ->with($this->equalTo(123)) - ->will($this->returnValue($contentType)); - - $repositoryMock->expects($this->once()) - ->method('getContentTypeService') - ->will($this->returnValue($contentTypeServiceMock)); - - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('create'), - $this->isInstanceOf(get_class($contentCreateStruct)), - $this->equalTo([]) - )->will($this->returnValue(true)); - - $mockedService->expects($this->once()) - ->method('loadContentByRemoteId') - ->with($contentCreateStruct->remoteId) - ->will($this->returnValue($this->createMock(Content::class))); - - $mockedService->createContent( - new ContentCreateStruct( - [ - 'remoteId' => 'faraday', - 'mainLanguageCode' => 'eng-US', - 'contentType' => $contentType, - ] - ), - [] - ); - } - - /** - * @param string $mainLanguageCode - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions - * - * @return array - */ - protected function mapStructFieldsForCreate($mainLanguageCode, $structFields, $fieldDefinitions) - { - $mappedFieldDefinitions = []; - foreach ($fieldDefinitions as $fieldDefinition) { - $mappedFieldDefinitions[$fieldDefinition->identifier] = $fieldDefinition; - } - - $mappedStructFields = []; - foreach ($structFields as $structField) { - if ($structField->languageCode === null) { - $languageCode = $mainLanguageCode; - } else { - $languageCode = $structField->languageCode; - } - - $mappedStructFields[$structField->fieldDefIdentifier][$languageCode] = (string)$structField->value; - } - - return $mappedStructFields; - } - - /** - * Returns full, possibly redundant array of field values, indexed by field definition - * identifier and language code. - * - * @param string $mainLanguageCode - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions - * @param array $languageCodes - * - * @return array - * - * @throws \RuntimeException Method is intended to be used only with consistent fixtures - */ - protected function determineValuesForCreate( - $mainLanguageCode, - array $structFields, - array $fieldDefinitions, - array $languageCodes - ) { - $mappedStructFields = $this->mapStructFieldsForCreate( - $mainLanguageCode, - $structFields, - $fieldDefinitions - ); - - $values = []; - - foreach ($fieldDefinitions as $fieldDefinition) { - $identifier = $fieldDefinition->identifier; - foreach ($languageCodes as $languageCode) { - if (!$fieldDefinition->isTranslatable) { - if (isset($mappedStructFields[$identifier][$mainLanguageCode])) { - $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$mainLanguageCode]; - } else { - $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue; - } - continue; - } - - if (isset($mappedStructFields[$identifier][$languageCode])) { - $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$languageCode]; - continue; - } - - $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue; - } - } - - return $this->stubValues($values); - } - - /** - * @param string $mainLanguageCode - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields - * - * @return string[] - */ - protected function determineLanguageCodesForCreate($mainLanguageCode, array $structFields) - { - $languageCodes = []; - - foreach ($structFields as $field) { - if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) { - continue; - } - - $languageCodes[$field->languageCode] = true; - } - - $languageCodes[$mainLanguageCode] = true; - - return array_keys($languageCodes); - } - - /** - * Asserts that calling createContent() with given API field set causes calling - * Handler::createContent() with given SPI field set. - * - * @param string $mainLanguageCode - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields - * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions - * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\Group[] $objectStateGroups - * @param bool $execute - * - * @return mixed - */ - protected function assertForTestCreateContentNonRedundantFieldSet( - $mainLanguageCode, - array $structFields, - array $spiFields, - array $fieldDefinitions, - array $locationCreateStructs = [], - $withObjectStates = false, - $execute = true - ): ContentCreateStruct { - $repositoryMock = $this->getRepositoryMock(); - $mockedService = $this->getPartlyMockedContentService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ - $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); - /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ - $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); - /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */ - $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler(); - $contentTypeServiceMock = $this->getContentTypeServiceMock(); - $domainMapperMock = $this->getContentDomainMapperMock(); - $relationProcessorMock = $this->getRelationProcessorMock(); - $nameSchemaServiceMock = $this->getNameSchemaServiceMock(); - $permissionResolverMock = $this->getPermissionResolverMock(); - $fieldTypeMock = $this->createMock(SPIFieldType::class); - $languageCodes = $this->determineLanguageCodesForCreate($mainLanguageCode, $structFields); - - list($contentType, $contentCreateStruct) = $this->provideCommonCreateContentObjects( - $fieldDefinitions, - $structFields, - $mainLanguageCode - ); - - $this->commonContentCreateMocks( - $languageHandlerMock, - $contentTypeServiceMock, - $repositoryMock, - $contentType - ); - - $repositoryMock->expects(self::once())->method('beginTransaction'); - $that = $this; - - $permissionResolverMock->expects(self::once()) - ->method('canUser') - ->with( - self::equalTo('content'), - self::equalTo('create'), - self::isInstanceOf(APIContentCreateStruct::class), - self::equalTo($locationCreateStructs) - )->will( - self::returnCallback( - static function () use ($that, $contentCreateStruct) { - $that->assertEquals($contentCreateStruct, func_get_arg(2)); - - return true; - } - ) - ); - - $this->getUniqueHashDomainMapperMock($domainMapperMock, $that, $contentCreateStruct); - $this->acceptFieldTypeValueMock($fieldTypeMock); - $this->toHashFieldTypeMock($fieldTypeMock); - - $fieldTypeMock->expects(self::any()) - ->method('toPersistenceValue') - ->will( - self::returnCallback( - static function (ValueStub $value) { - return (string)$value; - } - ) - ); - - $this->isEmptyValueFieldTypeMock($fieldTypeMock); - - $fieldTypeMock->expects(self::any()) - ->method('validate') - ->will(self::returnValue([])); - - $this->getFieldTypeRegistryMock()->expects(self::any()) - ->method('getFieldType') - ->will(self::returnValue($fieldTypeMock)); - - $relationProcessorMock - ->expects(self::exactly(count($fieldDefinitions) * count($languageCodes))) - ->method('appendFieldRelations') - ->with( - self::isType('array'), - self::isType('array'), - self::isInstanceOf(SPIFieldType::class), - self::isInstanceOf(Value::class), - self::anything() - ); - - $values = $this->determineValuesForCreate( - $mainLanguageCode, - $structFields, - $fieldDefinitions, - $languageCodes - ); - $nameSchemaServiceMock->expects(self::once()) - ->method('resolve') - ->with( - self::equalTo($contentType->nameSchema), - self::equalTo($contentType), - self::equalTo($values), - self::equalTo($languageCodes) - )->will(self::returnValue([])); - - $relationProcessorMock->expects(self::any()) - ->method('processFieldRelations') - ->with( - self::isType('array'), - self::equalTo(42), - self::isType('int'), - self::equalTo($contentType), - self::equalTo([]) - ); - - if (!$withObjectStates) { - $objectStateHandlerMock->expects(self::once()) - ->method('loadAllGroups') - ->will(self::returnValue([])); - } - - if ($execute) { - $spiContentCreateStruct = new SPIContentCreateStruct( - [ - 'name' => [], - 'typeId' => 123, - 'sectionId' => 1, - 'ownerId' => 169, - 'remoteId' => 'hash', - 'fields' => $spiFields, - 'modified' => time(), - 'initialLanguageId' => 4242, - ] - ); - $spiContentCreateStruct2 = clone $spiContentCreateStruct; - ++$spiContentCreateStruct2->modified; - - $spiContent = new SPIContent( - [ - 'versionInfo' => new SPIContent\VersionInfo( - [ - 'contentInfo' => new SPIContent\ContentInfo(['id' => 42]), - 'versionNo' => 7, - ] - ), - ] - ); - - $contentHandlerMock->expects(self::once()) - ->method('create') - ->with(self::logicalOr($spiContentCreateStruct, $spiContentCreateStruct2)) - ->will(self::returnValue($spiContent)); - - $repositoryMock->expects(self::once())->method('commit'); - $domainMapperMock->expects(self::once()) - ->method('buildContentDomainObject') - ->with( - self::isInstanceOf(SPIContent::class), - self::equalTo($contentType) - ) - ->willReturn(self::createMock(APIContent::class)); - - $mockedService->createContent($contentCreateStruct, []); - } - - return $contentCreateStruct; - } - - public function providerForTestCreateContentNonRedundantFieldSet1() - { - $spiFields = [ - new SPIField( - [ - 'fieldDefinitionId' => 'fieldDefinitionId', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'eng-US', - ] - ), - ]; - - return [ - // 0. Without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'eng-US', - ] - ), - ], - $spiFields, - ], - // 1. Without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => null, - ] - ), - ], - $spiFields, - ], - ]; - } - - /** - * Test for the createContent() method. - * - * Testing the simplest use case. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::cloneField - * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - * @dataProvider providerForTestCreateContentNonRedundantFieldSet1 - */ - public function testCreateContentNonRedundantFieldSet1($mainLanguageCode, $structFields, $spiFields) - { - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier', - 'isRequired' => false, - 'defaultValue' => new ValueStub('someValue'), - ] - ), - ]; - - $this->assertForTestCreateContentNonRedundantFieldSet( - $mainLanguageCode, - $structFields, - $spiFields, - $fieldDefinitions - ); - } - - public function providerForTestCreateContentNonRedundantFieldSet2() - { - $spiFields = [ - new SPIField( - [ - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - ] - ), - new SPIField( - [ - 'fieldDefinitionId' => 'fieldDefinitionId2', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'ger-DE', - ] - ), - ]; - - return [ - // 0. With language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'ger-DE', - ] - ), - ], - $spiFields, - ], - // 1. Without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => null, - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'ger-DE', - ] - ), - ], - $spiFields, - ], - ]; - } - - /** - * Test for the createContent() method. - * - * Testing multiple languages with multiple translatable fields with empty default value. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::cloneField - * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - * @dataProvider providerForTestCreateContentNonRedundantFieldSet2 - */ - public function testCreateContentNonRedundantFieldSet2($mainLanguageCode, $structFields, $spiFields) - { - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId1', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => true, - 'identifier' => 'identifier1', - 'isRequired' => false, - 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), - ] - ), - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId2', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => true, - 'identifier' => 'identifier2', - 'isRequired' => false, - 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), - ] - ), - ]; - - $this->assertForTestCreateContentNonRedundantFieldSet( - $mainLanguageCode, - $structFields, - $spiFields, - $fieldDefinitions - ); - } - - public function providerForTestCreateContentNonRedundantFieldSetComplex() - { - $spiFields0 = [ - new SPIField( - [ - 'fieldDefinitionId' => 'fieldDefinitionId2', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('defaultValue2'), - 'languageCode' => 'eng-US', - ] - ), - new SPIField( - [ - 'fieldDefinitionId' => 'fieldDefinitionId4', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('defaultValue4'), - 'languageCode' => 'eng-US', - ] - ), - ]; - $spiFields1 = [ - new SPIField( - [ - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'ger-DE', - ] - ), - new SPIField( - [ - 'fieldDefinitionId' => 'fieldDefinitionId2', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('defaultValue2'), - 'languageCode' => 'ger-DE', - ] - ), - new SPIField( - [ - 'fieldDefinitionId' => 'fieldDefinitionId2', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-US', - ] - ), - new SPIField( - [ - 'fieldDefinitionId' => 'fieldDefinitionId4', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue4'), - 'languageCode' => 'eng-US', - ] - ), - ]; - - return [ - // 0. Creating by default values only - [ - 'eng-US', - [], - $spiFields0, - ], - // 1. Multiple languages with language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'ger-DE', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-US', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier4', - 'value' => new ValueStub('newValue4'), - 'languageCode' => 'eng-US', - ] - ), - ], - $spiFields1, - ], - // 2. Multiple languages without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'ger-DE', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub('newValue2'), - 'languageCode' => null, - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier4', - 'value' => new ValueStub('newValue4'), - 'languageCode' => null, - ] - ), - ], - $spiFields1, - ], - ]; - } - - protected function fixturesForTestCreateContentNonRedundantFieldSetComplex() - { - return [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId1', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => true, - 'identifier' => 'identifier1', - 'isRequired' => false, - 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), - ] - ), - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId2', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => true, - 'identifier' => 'identifier2', - 'isRequired' => false, - 'defaultValue' => new ValueStub('defaultValue2'), - ] - ), - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId3', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier3', - 'isRequired' => false, - 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), - ] - ), - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId4', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier4', - 'isRequired' => false, - 'defaultValue' => new ValueStub('defaultValue4'), - ] - ), - ]; - } - - /** - * Test for the createContent() method. - * - * Testing multiple languages with multiple translatable fields with empty default value. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::cloneField - * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - * @dataProvider providerForTestCreateContentNonRedundantFieldSetComplex - */ - public function testCreateContentNonRedundantFieldSetComplex($mainLanguageCode, $structFields, $spiFields) - { - $fieldDefinitions = $this->fixturesForTestCreateContentNonRedundantFieldSetComplex(); - - $this->assertForTestCreateContentNonRedundantFieldSet( - $mainLanguageCode, - $structFields, - $spiFields, - $fieldDefinitions - ); - } - - public function providerForTestCreateContentWithInvalidLanguage() - { - return [ - [ - 'eng-GB', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'Klingon', - ] - ), - ], - ], - [ - 'Klingon', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'eng-GB', - ] - ), - ], - ], - ]; - } - - /** - * Test for the updateContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - * @dataProvider providerForTestCreateContentWithInvalidLanguage - */ - public function testCreateContentWithInvalidLanguage($mainLanguageCode, $structFields) - { - $this->expectException(APINotFoundException::class); - $this->expectExceptionMessage('Could not find \'Language\' with identifier \'Klingon\''); - - $repositoryMock = $this->getRepositoryMock(); - $mockedService = $this->getPartlyMockedContentService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ - $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); - $contentTypeServiceMock = $this->getContentTypeServiceMock(); - $domainMapperMock = $this->getContentDomainMapperMock(); - $permissionResolver = $this->getPermissionResolverMock(); - - $fieldTypeMock = $this->createMock(FieldType::class); - - $this->acceptFieldTypeValueMock($fieldTypeMock); - $this->toHashFieldTypeMock($fieldTypeMock); - $this->getFieldTypeFieldTypeRegistryMock($fieldTypeMock); - - $contentType = new ContentType( - [ - 'id' => 123, - 'fieldDefinitions' => new FieldDefinitionCollection([ - new FieldDefinition([ - 'id' => 'fieldDefinitionId', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier', - 'isRequired' => false, - 'defaultValue' => new ValueStub('someValue'), - ]), - ]), - ] - ); - $contentCreateStruct = new ContentCreateStruct( - [ - 'fields' => $structFields, - 'mainLanguageCode' => $mainLanguageCode, - 'contentType' => $contentType, - 'alwaysAvailable' => false, - 'ownerId' => 169, - 'sectionId' => 1, - ] - ); - - $languageHandlerMock->expects($this->any()) - ->method('loadByLanguageCode') - ->with($this->isType('string')) - ->will( - $this->returnCallback( - static function ($languageCode) { - if ($languageCode === 'Klingon') { - throw new NotFoundException('Language', 'Klingon'); - } - - return new Language(['id' => 4242]); - } - ) - ); - - $contentTypeServiceMock->expects($this->once()) - ->method('loadContentType') - ->with($this->equalTo($contentType->id)) - ->will($this->returnValue($contentType)); - - $repositoryMock->expects($this->once()) - ->method('getContentTypeService') - ->will($this->returnValue($contentTypeServiceMock)); - - $that = $this; - $permissionResolver->expects($this->any()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('create'), - $this->isInstanceOf(APIContentCreateStruct::class), - $this->equalTo([]) - )->will( - $this->returnCallback( - static function () use ($that, $contentCreateStruct) { - $that->assertEquals($contentCreateStruct, func_get_arg(2)); - - return true; - } - ) - ); - - $domainMapperMock->expects($this->once()) - ->method('getUniqueHash') - ->with($this->isInstanceOf(APIContentCreateStruct::class)) - ->will( - $this->returnCallback( - static function ($object) use ($that, $contentCreateStruct) { - $that->assertEquals($contentCreateStruct, $object); - - return 'hash'; - } - ) - ); - - $mockedService->createContent($contentCreateStruct, []); - } - - protected function assertForCreateContentContentValidationException( - $mainLanguageCode, - $structFields, - $fieldDefinitions = [] - ) { - $repositoryMock = $this->getRepositoryMock(); - $mockedService = $this->getPartlyMockedContentService(['loadContentByRemoteId']); - $contentTypeServiceMock = $this->getContentTypeServiceMock(); - $permissionResolver = $this->getPermissionResolverMock(); - - $fieldTypeMock = $this->createMock(FieldType::class); - $fieldTypeMock->expects($this->any()) - ->method('acceptValue') - ->will( - $this->returnCallback( - static function ($valueString) { - return new ValueStub($valueString); - } - ) - ); - - $this->toHashFieldTypeMock($fieldTypeMock); - $this->getFieldTypeFieldTypeRegistryMock($fieldTypeMock); - - $contentType = new ContentType( - [ - 'id' => 123, - 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), - ] - ); - $contentCreateStruct = new ContentCreateStruct( - [ - 'ownerId' => 169, - 'alwaysAvailable' => false, - 'remoteId' => 'faraday', - 'mainLanguageCode' => $mainLanguageCode, - 'fields' => $structFields, - 'contentType' => $contentType, - ] - ); - - $contentTypeServiceMock->expects($this->once()) - ->method('loadContentType') - ->with($this->equalTo(123)) - ->will($this->returnValue($contentType)); - - $repositoryMock->expects($this->once()) - ->method('getContentTypeService') - ->will($this->returnValue($contentTypeServiceMock)); - - $permissionResolver->expects($this->any()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('create'), - $this->isInstanceOf(get_class($contentCreateStruct)), - $this->equalTo([]) - )->will($this->returnValue(true)); - - $mockedService->expects($this->any()) - ->method('loadContentByRemoteId') - ->with($contentCreateStruct->remoteId) - ->will( - $this->throwException(new NotFoundException('Content', 'faraday')) - ); - - $mockedService->createContent($contentCreateStruct, []); - } - - public function providerForTestCreateContentThrowsContentValidationExceptionFieldDefinition() - { - return [ - [ - 'eng-GB', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'eng-GB', - ] - ), - ], - ], - ]; - } - - /** - * Test for the createContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionFieldDefinition - */ - public function testCreateContentThrowsContentValidationExceptionFieldDefinition($mainLanguageCode, $structFields) - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class); - $this->expectExceptionMessage('Field definition \'identifier\' does not exist in the given Content Type'); - - $this->assertForCreateContentContentValidationException( - $mainLanguageCode, - $structFields, - [] - ); - } - - public function providerForTestCreateContentThrowsContentValidationExceptionTranslation() - { - return [ - [ - 'eng-GB', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'eng-US', - ] - ), - ], - ], - ]; - } - - /** - * Test for the createContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionTranslation - */ - public function testCreateContentThrowsContentValidationExceptionTranslation($mainLanguageCode, $structFields) - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class); - $this->expectExceptionMessage('You cannot set a value for the non-translatable Field definition \'identifier\' in language \'eng-US\''); - - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId1', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier', - 'isRequired' => false, - 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), - ] - ), - ]; - - $this->assertForCreateContentContentValidationException( - $mainLanguageCode, - $structFields, - $fieldDefinitions - ); - } - - /** - * @param string $mainLanguageCode - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions - */ - private function provideCommonCreateContentObjects(array $fieldDefinitions, array $structFields, $mainLanguageCode): array - { - $contentType = new ContentType( - [ - 'id' => 123, - 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), - 'nameSchema' => '<nameSchema>', - ] - ); - $contentCreateStruct = new ContentCreateStruct( - [ - 'fields' => $structFields, - 'mainLanguageCode' => $mainLanguageCode, - 'contentType' => $contentType, - 'alwaysAvailable' => false, - 'ownerId' => 169, - 'sectionId' => 1, - ] - ); - - return [$contentType, $contentCreateStruct]; - } - - private function commonContentCreateMocks( - \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock, - \PHPUnit\Framework\MockObject\MockObject $contentTypeServiceMock, - \PHPUnit\Framework\MockObject\MockObject $repositoryMock, - ContentType $contentType - ): void { - $this->loadByLanguageCodeMock($languageHandlerMock); - - $contentTypeServiceMock->expects(self::once()) - ->method('loadContentType') - ->with(self::equalTo($contentType->id)) - ->will(self::returnValue($contentType)); - - $repositoryMock->expects(self::once()) - ->method('getContentTypeService') - ->will(self::returnValue($contentTypeServiceMock)); - } - - private function loadByLanguageCodeMock(\PHPUnit\Framework\MockObject\MockObject $languageHandlerMock): void - { - $languageHandlerMock->expects(self::any()) - ->method('loadByLanguageCode') - ->with(self::isType('string')) - ->will( - self::returnCallback( - static function () { - return new Language(['id' => 4242]); - } - ) - ); - } - - /** - * Asserts behaviour necessary for testing ContentFieldValidationException because of required - * field being empty. - * - * @param string $mainLanguageCode - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions - * - * @return mixed - */ - protected function assertForTestCreateContentRequiredField( - $mainLanguageCode, - array $structFields, - array $fieldDefinitions - ): ContentCreateStruct { - $repositoryMock = $this->getRepositoryMock(); - /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ - $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); - $contentTypeServiceMock = $this->getContentTypeServiceMock(); - $domainMapperMock = $this->getContentDomainMapperMock(); - $fieldTypeMock = $this->createMock(SPIFieldType::class); - $permissionResolver = $this->getPermissionResolverMock(); - - list($contentType, $contentCreateStruct) = $this->provideCommonCreateContentObjects( - $fieldDefinitions, - $structFields, - $mainLanguageCode - ); - - $this->commonContentCreateMocks( - $languageHandlerMock, - $contentTypeServiceMock, - $repositoryMock, - $contentType - ); - - $that = $this; - $permissionResolver->expects(self::once()) - ->method('canUser') - ->with( - self::equalTo('content'), - self::equalTo('create'), - self::isInstanceOf(APIContentCreateStruct::class), - self::equalTo([]) - )->will( - self::returnCallback( - static function () use ($that, $contentCreateStruct) { - $that->assertEquals($contentCreateStruct, func_get_arg(2)); - - return true; - } - ) - ); - - $this->getUniqueHashDomainMapperMock($domainMapperMock, $that, $contentCreateStruct); - - $this->acceptFieldTypeValueMock($fieldTypeMock); - $this->toHashFieldTypeMock($fieldTypeMock); - - $this->isEmptyValueFieldTypeMock($fieldTypeMock); - - $fieldTypeMock->expects(self::any()) - ->method('validate') - ->will(self::returnValue([])); - - $this->getFieldTypeRegistryMock()->expects(self::any()) - ->method('getFieldType') - ->will(self::returnValue($fieldTypeMock)); - - return $contentCreateStruct; - } - - public function providerForTestCreateContentThrowsContentValidationExceptionRequiredField() - { - return [ - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => null, - ] - ), - ], - 'identifier', - 'eng-US', - ], - ]; - } - - /** - * Test for the createContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionRequiredField - */ - public function testCreateContentRequiredField( - $mainLanguageCode, - $structFields, - $identifier, - $languageCode - ) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class); - - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => true, - 'identifier' => 'identifier', - 'isRequired' => true, - 'defaultValue' => new ValueStub('defaultValue'), - ] - ), - ]; - $contentCreateStruct = $this->assertForTestCreateContentRequiredField( - $mainLanguageCode, - $structFields, - $fieldDefinitions - ); - - $mockedService = $this->getPartlyMockedContentService(); - - try { - $mockedService->createContent($contentCreateStruct, []); - } catch (ContentValidationException $e) { - $this->assertEquals( - "Value for required field definition '{$identifier}' with language '{$languageCode}' is empty", - $e->getMessage() - ); - - throw $e; - } - } - - /** - * Asserts behaviour necessary for testing ContentFieldValidationException because of - * field not being valid. - * - * @param string $mainLanguageCode - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions - */ - protected function assertForTestCreateContentThrowsContentFieldValidationException( - $mainLanguageCode, - array $structFields, - array $fieldDefinitions - ): array { - $repositoryMock = $this->getRepositoryMock(); - /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ - $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); - $contentTypeServiceMock = $this->getContentTypeServiceMock(); - $domainMapperMock = $this->getContentDomainMapperMock(); - $relationProcessorMock = $this->getRelationProcessorMock(); - $fieldTypeMock = $this->createMock(SPIFieldType::class); - $languageCodes = $this->determineLanguageCodesForCreate($mainLanguageCode, $structFields); - $permissionResolver = $this->getPermissionResolverMock(); - - $contentType = new ContentType( - [ - 'id' => 123, - 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), - 'nameSchema' => '<nameSchema>', - ] - ); - $contentCreateStruct = new ContentCreateStruct( - [ - 'fields' => $structFields, - 'mainLanguageCode' => $mainLanguageCode, - 'contentType' => $contentType, - 'alwaysAvailable' => false, - 'ownerId' => 169, - 'sectionId' => 1, - ] - ); - - $this->commonContentCreateMocks( - $languageHandlerMock, - $contentTypeServiceMock, - $repositoryMock, - $contentType - ); - - $that = $this; - $permissionResolver->expects(self::once()) - ->method('canUser') - ->with( - self::equalTo('content'), - self::equalTo('create'), - self::isInstanceOf(APIContentCreateStruct::class), - self::equalTo([]) - )->will( - $this->returnCallback( - static function () use ($that, $contentCreateStruct) { - $that->assertEquals($contentCreateStruct, func_get_arg(2)); - - return true; - } - ) - ); - - $this->getUniqueHashDomainMapperMock($domainMapperMock, $that, $contentCreateStruct); - - $this->getFieldTypeRegistryMock()->expects(self::any()) - ->method('getFieldType') - ->will(self::returnValue($fieldTypeMock)); - - $relationProcessorMock - ->expects(self::any()) - ->method('appendFieldRelations') - ->with( - self::isType('array'), - self::isType('array'), - self::isInstanceOf(SPIFieldType::class), - self::isInstanceOf(Value::class), - self::anything() - ); - - $fieldValues = $this->determineValuesForCreate( - $mainLanguageCode, - $structFields, - $fieldDefinitions, - $languageCodes - ); - $allFieldErrors = []; - $emptyValue = new ValueStub(self::EMPTY_FIELD_VALUE); - - $fieldTypeMock - ->method('acceptValue') - ->will( - $this->returnCallback( - static function ($value) { - return $value instanceof SPIValue - ? $value - : new ValueStub($value); - } - ) - ); - - $fieldTypeMock - ->method('isEmptyValue') - ->will( - $this->returnCallback( - static function (ValueStub $value) use ($emptyValue) { - return (string)$emptyValue === (string)$value; - } - ) - ); - - $this->toHashFieldTypeMock($fieldTypeMock); - - $emptyValue = new ValueStub(self::EMPTY_FIELD_VALUE); - foreach ($contentType->getFieldDefinitions() as $fieldDefinition) { - foreach ($fieldValues[$fieldDefinition->identifier] as $languageCode => $value) { - if ((string)$emptyValue === (string)$value) { - continue; - } - - $fieldTypeMock - ->method('validate') - ->willReturn(new ValidationError(1)); - - $allFieldErrors[$fieldDefinition->id][$languageCode] = new ValidationError(1); - } - } - - return [$contentCreateStruct, $allFieldErrors]; - } - - public function providerForTestCreateContentThrowsContentFieldValidationException() - { - return $this->providerForTestCreateContentNonRedundantFieldSetComplex(); - } - - /** - * Test for the createContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - * @dataProvider providerForTestCreateContentThrowsContentFieldValidationException - */ - public function testCreateContentThrowsContentFieldValidationException($mainLanguageCode, $structFields): void - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class); - $this->expectExceptionMessage('Content fields did not validate'); - - $fieldDefinitions = $this->fixturesForTestCreateContentNonRedundantFieldSetComplex(); - list($contentCreateStruct, $allFieldErrors) = - $this->assertForTestCreateContentThrowsContentFieldValidationException( - $mainLanguageCode, - $structFields, - $fieldDefinitions - ); - - $mockedService = $this->getPartlyMockedContentService(); - - try { - $mockedService->createContent($contentCreateStruct); - } catch (ContentFieldValidationException $e) { - $this->assertEquals($allFieldErrors, $e->getFieldErrors()); - throw $e; - } - } - - /** - * Test for the createContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::buildSPILocationCreateStructs - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - */ - public function testCreateContentWithLocations() - { - $spiFields = [ - new SPIField( - [ - 'fieldDefinitionId' => 'fieldDefinitionId', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('defaultValue'), - 'languageCode' => 'eng-US', - ] - ), - ]; - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier', - 'isRequired' => false, - 'defaultValue' => new ValueStub('defaultValue'), - ] - ), - ]; - - // Set up a simple case that will pass - $locationCreateStruct1 = new LocationCreateStruct(['parentLocationId' => 321]); - $locationCreateStruct2 = new LocationCreateStruct(['parentLocationId' => 654]); - $locationCreateStructs = [$locationCreateStruct1, $locationCreateStruct2]; - $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet( - 'eng-US', - [], - $spiFields, - $fieldDefinitions, - $locationCreateStructs, - false, - // Do not execute - false - ); - - $repositoryMock = $this->getRepositoryMock(); - $mockedService = $this->getPartlyMockedContentService(); - $locationServiceMock = $this->getLocationServiceMock(); - /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */ - $handlerMock = $this->getPersistenceMock()->contentHandler(); - $domainMapperMock = $this->getContentDomainMapperMock(); - $spiLocationCreateStruct = new SPILocation\CreateStruct(); - $parentLocation = new Location(['contentInfo' => new ContentInfo(['sectionId' => 1])]); - - $locationServiceMock->expects($this->at(0)) - ->method('loadLocation') - ->with($this->equalTo(321)) - ->will($this->returnValue($parentLocation)); - - $locationServiceMock->expects($this->at(1)) - ->method('loadLocation') - ->with($this->equalTo(654)) - ->will($this->returnValue($parentLocation)); - - $repositoryMock->expects($this->atLeastOnce()) - ->method('getLocationService') - ->will($this->returnValue($locationServiceMock)); - - $domainMapperMock->expects($this->at(1)) - ->method('buildSPILocationCreateStruct') - ->with( - $this->equalTo($locationCreateStruct1), - $this->equalTo($parentLocation), - $this->equalTo(true), - $this->equalTo(null), - $this->equalTo(null), - $this->equalTo(false) - )->will($this->returnValue($spiLocationCreateStruct)); - - $domainMapperMock->expects($this->at(2)) - ->method('buildSPILocationCreateStruct') - ->with( - $this->equalTo($locationCreateStruct2), - $this->equalTo($parentLocation), - $this->equalTo(false), - $this->equalTo(null), - $this->equalTo(null), - $this->equalTo(false) - )->will($this->returnValue($spiLocationCreateStruct)); - - $spiContentCreateStruct = new SPIContentCreateStruct( - [ - 'name' => [], - 'typeId' => 123, - 'sectionId' => 1, - 'ownerId' => 169, - 'remoteId' => 'hash', - 'fields' => $spiFields, - 'modified' => time(), - 'initialLanguageId' => 4242, - 'locations' => [$spiLocationCreateStruct, $spiLocationCreateStruct], - ] - ); - $spiContentCreateStruct2 = clone $spiContentCreateStruct; - ++$spiContentCreateStruct2->modified; - - $spiContent = new SPIContent( - [ - 'versionInfo' => new SPIContent\VersionInfo( - [ - 'contentInfo' => new SPIContent\ContentInfo(['id' => 42]), - 'versionNo' => 7, - ] - ), - ] - ); - - $handlerMock->expects($this->once()) - ->method('create') - ->with($this->logicalOr($spiContentCreateStruct, $spiContentCreateStruct2)) - ->will($this->returnValue($spiContent)); - - $domainMapperMock->expects($this->once()) - ->method('buildContentDomainObject') - ->with( - $this->isInstanceOf(SPIContent::class), - $this->isInstanceOf(APIContentType::class) - ) - ->willReturn($this->createMock(APIContent::class)); - - $repositoryMock->expects($this->once())->method('commit'); - - // Execute - $mockedService->createContent($contentCreateStruct, $locationCreateStructs); - } - - private function acceptFieldTypeValueMock(\PHPUnit\Framework\MockObject\MockObject $fieldTypeMock): void - { - $fieldTypeMock->expects(self::any()) - ->method('acceptValue') - ->will( - self::returnCallback( - static function ($valueString) { - return new ValueStub($valueString); - } - ) - ); - } - - private function toHashFieldTypeMock(\PHPUnit\Framework\MockObject\MockObject $fieldTypeMock): void - { - $fieldTypeMock - ->method('toHash') - ->willReturnCallback(static function (SPIValue $value) { - return ['value' => $value->value]; - }); - } - - private function getFieldTypeFieldTypeRegistryMock(\PHPUnit\Framework\MockObject\MockObject $fieldTypeMock): void - { - $this->getFieldTypeRegistryMock()->expects(self::any()) - ->method('getFieldType') - ->will(self::returnValue($fieldTypeMock)); - } - - private function isEmptyValueFieldTypeMock(\PHPUnit\Framework\MockObject\MockObject $fieldTypeMock): void - { - $emptyValue = new ValueStub(self::EMPTY_FIELD_VALUE); - $fieldTypeMock->expects(self::any()) - ->method('isEmptyValue') - ->will( - self::returnCallback( - static function (ValueStub $value) use ($emptyValue) { - return (string)$emptyValue === (string)$value; - } - ) - ); - } - - private function getUniqueHashDomainMapperMock( - \PHPUnit\Framework\MockObject\MockObject $domainMapperMock, - self $that, - ContentCreateStruct $contentCreateStruct - ): void { - $domainMapperMock->expects(self::once()) - ->method('getUniqueHash') - ->with(self::isInstanceOf(APIContentCreateStruct::class)) - ->will( - self::returnCallback( - static function ($object) use ($that, $contentCreateStruct) { - $that->assertEquals($contentCreateStruct, $object); - - return 'hash'; - } - ) - ); - } - - /** - * Test for the createContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::buildSPILocationCreateStructs - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - */ - public function testCreateContentWithLocationsDuplicateUnderParent() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - $this->expectExceptionMessage('You provided multiple LocationCreateStructs with the same parent Location \'321\''); - - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier', - 'isRequired' => false, - 'defaultValue' => new ValueStub('defaultValue'), - ] - ), - ]; - - $repositoryMock = $this->getRepositoryMock(); - $mockedService = $this->getPartlyMockedContentService(); - $locationServiceMock = $this->getLocationServiceMock(); - $contentTypeServiceMock = $this->getContentTypeServiceMock(); - $domainMapperMock = $this->getContentDomainMapperMock(); - $permissionResolver = $this->getPermissionResolverMock(); - /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ - $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); - $spiLocationCreateStruct = new SPILocation\CreateStruct(); - $parentLocation = new Location(['id' => 321]); - $locationCreateStruct = new LocationCreateStruct(['parentLocationId' => 321]); - $locationCreateStructs = [$locationCreateStruct, clone $locationCreateStruct]; - $contentType = new ContentType( - [ - 'id' => 123, - 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), - 'nameSchema' => '<nameSchema>', - ] - ); - $contentCreateStruct = new ContentCreateStruct( - [ - 'fields' => [ - new Field([ - 'fieldDefIdentifier' => 'identifier', - 'value' => 123, - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'languageCode' => 'eng-US', - ]), - ], - 'mainLanguageCode' => 'eng-US', - 'contentType' => $contentType, - 'alwaysAvailable' => false, - 'ownerId' => 169, - 'sectionId' => 1, - ] - ); - - $languageHandlerMock->expects($this->any()) - ->method('loadByLanguageCode') - ->with($this->isType('string')) - ->will( - $this->returnCallback( - static function () { - return new Language(['id' => 4242]); - } - ) - ); - - $fieldTypeMock = $this->createMock(FieldType::class); - $fieldTypeMock->expects($this->any()) - ->method('acceptValue') - ->will( - $this->returnCallback( - static function ($valueString) { - return new ValueStub($valueString); - } - ) - ); - - $this->toHashFieldTypeMock($fieldTypeMock); - $this->getFieldTypeFieldTypeRegistryMock($fieldTypeMock); - - $contentTypeServiceMock->expects($this->once()) - ->method('loadContentType') - ->with($this->equalTo($contentType->id)) - ->will($this->returnValue($contentType)); - - $repositoryMock->expects($this->once()) - ->method('getContentTypeService') - ->will($this->returnValue($contentTypeServiceMock)); - - $that = $this; - $permissionResolver->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('create'), - $this->isInstanceOf(APIContentCreateStruct::class), - $this->equalTo($locationCreateStructs) - )->will( - $this->returnCallback( - static function () use ($that, $contentCreateStruct) { - $that->assertEquals($contentCreateStruct, func_get_arg(2)); - - return true; - } - ) - ); - - $domainMapperMock->expects($this->once()) - ->method('getUniqueHash') - ->with($this->isInstanceOf(APIContentCreateStruct::class)) - ->will( - $this->returnCallback( - static function ($object) use ($that, $contentCreateStruct) { - $that->assertEquals($contentCreateStruct, $object); - - return 'hash'; - } - ) - ); - - $locationServiceMock->expects($this->once()) - ->method('loadLocation') - ->with($this->equalTo(321)) - ->will($this->returnValue($parentLocation)); - - $repositoryMock->expects($this->any()) - ->method('getLocationService') - ->will($this->returnValue($locationServiceMock)); - - $domainMapperMock->expects($this->any()) - ->method('buildSPILocationCreateStruct') - ->with( - $this->equalTo($locationCreateStruct), - $this->equalTo($parentLocation), - $this->equalTo(true), - $this->equalTo(null), - $this->equalTo(null), - $this->equalTo(false) - )->will($this->returnValue($spiLocationCreateStruct)); - - $fieldTypeMock = $this->createMock(SPIFieldType::class); - $fieldTypeMock - ->method('acceptValue') - ->will( - $this->returnCallback( - static function ($valueString) { - return new ValueStub($valueString); - } - ) - ); - - $this->getFieldTypeRegistryMock() - ->method('getFieldType') - ->will($this->returnValue($fieldTypeMock)); - - $mockedService->createContent( - $contentCreateStruct, - $locationCreateStructs - ); - } - - /** - * Test for the createContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - */ - public function testCreateContentObjectStates() - { - $spiFields = [ - new SPIField( - [ - 'fieldDefinitionId' => 'fieldDefinitionId', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('defaultValue'), - 'languageCode' => 'eng-US', - ] - ), - ]; - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier', - 'isRequired' => false, - 'defaultValue' => new ValueStub('defaultValue'), - ] - ), - ]; - $objectStateGroups = [ - new SPIObjectStateGroup(['id' => 10]), - new SPIObjectStateGroup(['id' => 20]), - ]; - - // Set up a simple case that will pass - $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet( - 'eng-US', - [], - $spiFields, - $fieldDefinitions, - [], - true, - // Do not execute - false - ); - $timestamp = time(); - $contentCreateStruct->modificationDate = new \DateTime("@{$timestamp}"); - - $repositoryMock = $this->getRepositoryMock(); - $mockedService = $this->getPartlyMockedContentService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */ - $handlerMock = $this->getPersistenceMock()->contentHandler(); - $domainMapperMock = $this->getContentDomainMapperMock(); - - $this->mockGetDefaultObjectStates(); - $this->mockSetDefaultObjectStates(); - - $spiContentCreateStruct = new SPIContentCreateStruct( - [ - 'name' => [], - 'typeId' => 123, - 'sectionId' => 1, - 'ownerId' => 169, - 'remoteId' => 'hash', - 'fields' => $spiFields, - 'modified' => $timestamp, - 'initialLanguageId' => 4242, - 'locations' => [], - ] - ); - $spiContentCreateStruct2 = clone $spiContentCreateStruct; - ++$spiContentCreateStruct2->modified; - - $spiContent = new SPIContent( - [ - 'versionInfo' => new SPIContent\VersionInfo( - [ - 'contentInfo' => new SPIContent\ContentInfo(['id' => 42]), - 'versionNo' => 7, - ] - ), - ] - ); - - $handlerMock->expects($this->once()) - ->method('create') - ->with($this->equalTo($spiContentCreateStruct)) - ->will($this->returnValue($spiContent)); - - $domainMapperMock->expects($this->once()) - ->method('buildContentDomainObject') - ->with( - $this->isInstanceOf(SPIContent::class), - $this->isInstanceOf(APIContentType::class) - ) - ->willReturn($this->createMock(APIContent::class)); - - $repositoryMock->expects($this->once())->method('commit'); - - // Execute - $mockedService->createContent($contentCreateStruct, []); - } - - /** - * Test for the createContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForCreate - * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates - * @covers \eZ\Publish\Core\Repository\ContentService::createContent - * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionTranslation - */ - public function testCreateContentWithRollback() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Store failed'); - - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier', - 'isRequired' => false, - 'defaultValue' => new ValueStub('defaultValue'), - ] - ), - ]; - - // Setup a simple case that will pass - $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet( - 'eng-US', - [], - [], - $fieldDefinitions, - [], - false, - // Do not execute test - false - ); - - $repositoryMock = $this->getRepositoryMock(); - $repositoryMock->expects($this->never())->method('commit'); - $repositoryMock->expects($this->once())->method('rollback'); - - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ - $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); - $contentHandlerMock->expects($this->once()) - ->method('create') - ->with($this->anything()) - ->will($this->throwException(new \Exception('Store failed'))); - - // Execute - $this->partlyMockedContentService->createContent($contentCreateStruct, []); - } - - public function providerForTestUpdateContentThrowsBadStateException() - { - return [ - [VersionInfo::STATUS_PUBLISHED], - [VersionInfo::STATUS_ARCHIVED], - ]; - } - - /** - * Test for the updateContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent - * @dataProvider providerForTestUpdateContentThrowsBadStateException - */ - public function testUpdateContentThrowsBadStateException($status) - { - $this->expectException(BadStateException::class); - - $versionInfo = new VersionInfo( - [ - 'contentInfo' => new ContentInfo(['id' => 42]), - 'versionNo' => 7, - 'status' => $status, - ] - ); - $content = new Content( - [ - 'versionInfo' => $versionInfo, - 'internalFields' => [], - 'contentType' => new ContentType([]), - ] - ); - - $mockedService = $this->getPartlyMockedContentService(['loadContent', 'internalLoadContentById']); - $mockedService - ->method('loadContent') - ->with( - $this->equalTo(42), - $this->equalTo(null), - $this->equalTo(7) - )->will( - $this->returnValue($content) - ); - $mockedService - ->method('internalLoadContentById') - ->will( - $this->returnValue($content) - ); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock - ->method('canUser') - ->will($this->returnValue(true)); - - $contentUpdateStruct = new ContentUpdateStruct(); - - $mockedService->updateContent($versionInfo, $contentUpdateStruct); - } - - /** - * Test for the updateContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent - */ - public function testUpdateContentThrowsUnauthorizedException() - { - $this->expectException(UnauthorizedException::class); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $mockedService = $this->getPartlyMockedContentService(['loadContent']); - $contentUpdateStruct = new ContentUpdateStruct(); - $versionInfo = new VersionInfo( - [ - 'contentInfo' => new ContentInfo(['id' => 42]), - 'versionNo' => 7, - 'status' => VersionInfo::STATUS_DRAFT, - ] - ); - $content = new Content( - [ - 'versionInfo' => $versionInfo, - 'internalFields' => [], - 'contentType' => new ContentType([]), - ] - ); - - $mockedService->expects($this->once()) - ->method('loadContent') - ->with( - $this->equalTo(42), - $this->equalTo(null), - $this->equalTo(7) - )->will( - $this->returnValue($content) - ); - - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('edit'), - $this->equalTo($content), - $this->isType('array') - )->will($this->returnValue(false)); - - $mockedService->updateContent($versionInfo, $contentUpdateStruct); - } - - /** - * @param string $initialLanguageCode - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields - * @param string[] $existingLanguages - * - * @return string[] - */ - protected function determineLanguageCodesForUpdate($initialLanguageCode, array $structFields, $existingLanguages) - { - $languageCodes = array_fill_keys($existingLanguages, true); - if ($initialLanguageCode !== null) { - $languageCodes[$initialLanguageCode] = true; - } - - foreach ($structFields as $field) { - if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) { - continue; - } - - $languageCodes[$field->languageCode] = true; - } - - return array_keys($languageCodes); - } - - /** - * @param string $initialLanguageCode - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields - * @param string $mainLanguageCode - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions - * - * @return array - */ - protected function mapStructFieldsForUpdate($initialLanguageCode, $structFields, $mainLanguageCode, $fieldDefinitions) - { - $initialLanguageCode = $initialLanguageCode ?: $mainLanguageCode; - - $mappedFieldDefinitions = []; - foreach ($fieldDefinitions as $fieldDefinition) { - $mappedFieldDefinitions[$fieldDefinition->identifier] = $fieldDefinition; - } - - $mappedStructFields = []; - foreach ($structFields as $structField) { - $identifier = $structField->fieldDefIdentifier; - - if ($structField->languageCode !== null) { - $languageCode = $structField->languageCode; - } elseif ($mappedFieldDefinitions[$identifier]->isTranslatable) { - $languageCode = $initialLanguageCode; - } else { - $languageCode = $mainLanguageCode; - } - - $mappedStructFields[$identifier][$languageCode] = (string)$structField->value; - } - - return $mappedStructFields; - } - - /** - * Returns full, possibly redundant array of field values, indexed by field definition - * identifier and language code. - * - * @param string $initialLanguageCode - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields - * @param \eZ\Publish\Core\Repository\Values\Content\Content $content - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions - * @param array $languageCodes - * - * @return array - */ - protected function determineValuesForUpdate( - $initialLanguageCode, - array $structFields, - Content $content, - array $fieldDefinitions, - array $languageCodes - ) { - $mainLanguageCode = $content->versionInfo->contentInfo->mainLanguageCode; - - $mappedStructFields = $this->mapStructFieldsForUpdate( - $initialLanguageCode, - $structFields, - $mainLanguageCode, - $fieldDefinitions - ); - - $values = []; - - foreach ($fieldDefinitions as $fieldDefinition) { - $identifier = $fieldDefinition->identifier; - foreach ($languageCodes as $languageCode) { - if (!$fieldDefinition->isTranslatable) { - if (isset($mappedStructFields[$identifier][$mainLanguageCode])) { - $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$mainLanguageCode]; - } else { - $values[$identifier][$languageCode] = (string)$content->fields[$identifier][$mainLanguageCode]; - } - continue; - } - - if (isset($mappedStructFields[$identifier][$languageCode])) { - $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$languageCode]; - continue; - } - - if (isset($content->fields[$identifier][$languageCode])) { - $values[$identifier][$languageCode] = (string)$content->fields[$identifier][$languageCode]; - continue; - } - - $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue; - } - } - - return $this->stubValues($values); - } - - protected function stubValues(array $fieldValues) - { - foreach ($fieldValues as &$languageValues) { - foreach ($languageValues as &$value) { - $value = new ValueStub($value); - } - } - - return $fieldValues; - } - - /** - * Asserts that calling updateContent() with given API field set causes calling - * Handler::updateContent() with given SPI field set. - * - * @param string $initialLanguageCode - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $structFields - * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $existingFields - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions - * @param bool $execute - * - * @return mixed - */ - protected function assertForTestUpdateContentNonRedundantFieldSet( - $initialLanguageCode, - array $structFields, - array $spiFields, - array $existingFields, - array $fieldDefinitions, - $execute = true - ) { - $repositoryMock = $this->getRepositoryMock(); - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock - ->expects($this->once()) - ->method('getCurrentUserReference') - ->willReturn(new UserReference(169)); - $mockedService = $this->getPartlyMockedContentService(['internalLoadContentById', 'internalLoadRelations'], $permissionResolverMock); - $permissionResolverMock = $this->getPermissionResolverMock(); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ - $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); - /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ - $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); - $contentTypeServiceMock = $this->getContentTypeServiceMock(); - $domainMapperMock = $this->getContentDomainMapperMock(); - $relationProcessorMock = $this->getRelationProcessorMock(); - $nameSchemaServiceMock = $this->getNameSchemaServiceMock(); - $fieldTypeMock = $this->createMock(SPIFieldType::class); - $existingLanguageCodes = array_map( - static function (Field $field) { - return $field->languageCode; - }, - $existingFields - ); - $languageCodes = $this->determineLanguageCodesForUpdate( - $initialLanguageCode, - $structFields, - $existingLanguageCodes - ); - $versionInfo = new VersionInfo( - [ - 'contentInfo' => new ContentInfo( - [ - 'id' => 42, - 'contentTypeId' => 24, - 'mainLanguageCode' => 'eng-GB', - ] - ), - 'versionNo' => 7, - 'languageCodes' => $existingLanguageCodes, - 'status' => VersionInfo::STATUS_DRAFT, - ] - ); - - $contentType = new ContentType([ - 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), - ]); - - $content = new Content( - [ - 'versionInfo' => $versionInfo, - 'internalFields' => $existingFields, - 'contentType' => $contentType, - ] - ); - - $languageHandlerMock->expects($this->any()) - ->method('loadByLanguageCode') - ->with($this->isType('string')) - ->will( - $this->returnCallback( - static function () { - return new Language(['id' => 4242]); - } - ) - ); - - $mockedService - ->method('internalLoadContentById') - ->with( - $this->equalTo(42), - $this->equalTo(null), - $this->equalTo(7) - )->will( - $this->returnValue($content) - ); - - $repositoryMock->expects($this->once())->method('beginTransaction'); - - $permissionResolverMock->expects($this->any()) - ->method('canUser') - ->will($this->returnValue(true)); - - $contentTypeServiceMock->expects($this->once()) - ->method('loadContentType') - ->with($this->equalTo(24)) - ->will($this->returnValue($contentType)); - - $repositoryMock->expects($this->once()) - ->method('getContentTypeService') - ->will($this->returnValue($contentTypeServiceMock)); - - $fieldTypeMock->expects($this->any()) - ->method('acceptValue') - ->will( - $this->returnCallback( - static function ($value) { - return $value instanceof SPIValue - ? $value - : new ValueStub($value); - } - ) - ); - - $this->toHashFieldTypeMock($fieldTypeMock); - - $emptyValue = new ValueStub(self::EMPTY_FIELD_VALUE); - $fieldTypeMock->expects($this->any()) - ->method('toPersistenceValue') - ->will( - $this->returnCallback( - static function (ValueStub $value) { - return (string)$value; - } - ) - ); - - $fieldTypeMock->expects($this->any()) - ->method('isEmptyValue') - ->will( - $this->returnCallback( - static function (SPIValue $value) use ($emptyValue) { - return (string)$emptyValue === (string)$value; - } - ) - ); - - $fieldTypeMock->expects($this->any()) - ->method('validate') - ->will($this->returnValue([])); - - $this->getFieldTypeFieldTypeRegistryMock($fieldTypeMock); - - $relationProcessorMock - ->expects($this->exactly(count($fieldDefinitions) * count($languageCodes))) - ->method('appendFieldRelations') - ->with( - $this->isType('array'), - $this->isType('array'), - $this->isInstanceOf(SPIFieldType::class), - $this->isInstanceOf(Value::class), - $this->anything() - ); - - $values = $this->determineValuesForUpdate( - $initialLanguageCode, - $structFields, - $content, - $fieldDefinitions, - $languageCodes - ); - $nameSchemaServiceMock->expects($this->once()) - ->method('resolveNameSchema') - ->with( - $this->equalTo($content), - $this->equalTo($values), - $this->equalTo($languageCodes) - )->will($this->returnValue([])); - - $existingRelations = ['RELATIONS!!!']; - $mockedService - ->method('internalLoadRelations') - ->with($content->versionInfo) - ->will($this->returnValue($existingRelations)); - $relationProcessorMock->expects($this->any()) - ->method('processFieldRelations') - ->with( - $this->isType('array'), - $this->equalTo(42), - $this->isType('int'), - $this->equalTo($contentType), - $this->equalTo($existingRelations) - ); - - $contentUpdateStruct = new ContentUpdateStruct( - [ - 'fields' => $structFields, - 'initialLanguageCode' => $initialLanguageCode, - ] - ); - - if ($execute) { - $spiContentUpdateStruct = new SPIContentUpdateStruct( - [ - 'creatorId' => 169, - 'fields' => $spiFields, - 'modificationDate' => time(), - 'initialLanguageId' => 4242, - ] - ); - - // During code coverage runs, timestamp might differ 1-3 seconds - $spiContentUpdateStructTs1 = clone $spiContentUpdateStruct; - ++$spiContentUpdateStructTs1->modificationDate; - - $spiContentUpdateStructTs2 = clone $spiContentUpdateStructTs1; - ++$spiContentUpdateStructTs2->modificationDate; - - $spiContentUpdateStructTs3 = clone $spiContentUpdateStructTs2; - ++$spiContentUpdateStructTs3->modificationDate; - - $spiContent = new SPIContent( - [ - 'versionInfo' => new SPIContent\VersionInfo( - [ - 'contentInfo' => new SPIContent\ContentInfo(['id' => 42]), - 'versionNo' => 7, - ] - ), - ] - ); - - $contentHandlerMock->expects($this->once()) - ->method('updateContent') - ->with( - 42, - 7, - $this->logicalOr($spiContentUpdateStruct, $spiContentUpdateStructTs1, $spiContentUpdateStructTs2, $spiContentUpdateStructTs3) - ) - ->will($this->returnValue($spiContent)); - - $repositoryMock->expects($this->once())->method('commit'); - $domainMapperMock - ->method('buildContentDomainObject') - ->with( - $this->isInstanceOf(SPIContent::class), - $this->isInstanceOf(APIContentType::class) - ) - ->will($this->returnValue($content)); - - $mockedService->updateContent($content->versionInfo, $contentUpdateStruct); - } - - return [$content->versionInfo, $contentUpdateStruct]; - } - - public function providerForTestUpdateContentNonRedundantFieldSet1() - { - $spiFields = [ - new SPIField( - [ - 'id' => '100', - 'fieldDefinitionId' => 'fieldDefinitionId', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'eng-GB', - 'versionNo' => 7, - ] - ), - ]; - - return [ - // With languages set - [ - 'eng-GB', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields, - ], - // Without languages set - [ - null, - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => null, - ] - ), - ], - $spiFields, - ], - // Adding new language without fields - [ - 'eng-US', - [], - [], - ], - ]; - } - - /** - * Test for the updateContent() method. - * - * Testing the simplest use case. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent - * @dataProvider providerForTestUpdateContentNonRedundantFieldSet1 - */ - public function testUpdateContentNonRedundantFieldSet1($initialLanguageCode, $structFields, $spiFields) - { - $existingFields = [ - new Field( - [ - 'id' => '100', - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('id100'), - 'languageCode' => 'eng-GB', - ] - ), - ]; - - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier', - 'isRequired' => false, - 'defaultValue' => new ValueStub('defaultValue'), - ] - ), - ]; - - $this->assertForTestUpdateContentNonRedundantFieldSet( - $initialLanguageCode, - $structFields, - $spiFields, - $existingFields, - $fieldDefinitions - ); - } - - public function providerForTestUpdateContentNonRedundantFieldSet2() - { - $spiFields0 = [ - new SPIField( - [ - 'id' => '100', - 'fieldDefinitionId' => 'fieldDefinitionId', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'eng-GB', - 'versionNo' => 7, - ] - ), - ]; - $spiFields1 = [ - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - ]; - $spiFields2 = [ - new SPIField( - [ - 'id' => 100, - 'fieldDefinitionId' => 'fieldDefinitionId', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-GB', - 'versionNo' => 7, - ] - ), - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - ]; - - return [ - // 0. With languages set - [ - 'eng-GB', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields0, - ], - // 1. Without languages set - [ - null, - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => null, - ] - ), - ], - $spiFields0, - ], - // 2. New language with language set - [ - 'eng-GB', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'eng-US', - ] - ), - ], - $spiFields1, - ], - // 3. New language without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => null, - ] - ), - ], - $spiFields1, - ], - // 4. New language and existing language with language set - [ - 'eng-GB', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields2, - ], - // 5. New language and existing language without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue1'), - 'languageCode' => null, - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields2, - ], - // 6. Adding new language without fields - [ - 'eng-US', - [], - [ - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('defaultValue'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - ], - ], - ]; - } - - /** - * Test for the updateContent() method. - * - * Testing with translatable field. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent - * @dataProvider providerForTestUpdateContentNonRedundantFieldSet2 - */ - public function testUpdateContentNonRedundantFieldSet2($initialLanguageCode, $structFields, $spiFields) - { - $existingFields = [ - new Field( - [ - 'id' => '100', - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('id100'), - 'languageCode' => 'eng-GB', - ] - ), - ]; - - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => true, - 'identifier' => 'identifier', - 'isRequired' => false, - 'defaultValue' => new ValueStub('defaultValue'), - ] - ), - ]; - - $this->assertForTestUpdateContentNonRedundantFieldSet( - $initialLanguageCode, - $structFields, - $spiFields, - $existingFields, - $fieldDefinitions - ); - } - - public function providerForTestUpdateContentNonRedundantFieldSet3() - { - $spiFields0 = [ - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - ]; - $spiFields1 = [ - new SPIField( - [ - 'id' => 100, - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-GB', - 'versionNo' => 7, - ] - ), - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - ]; - $spiFields2 = [ - new SPIField( - [ - 'id' => 100, - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-GB', - 'versionNo' => 7, - ] - ), - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - new SPIField( - [ - 'id' => 101, - 'fieldDefinitionId' => 'fieldDefinitionId2', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue3'), - 'languageCode' => 'eng-GB', - 'versionNo' => 7, - ] - ), - ]; - $spiFields3 = [ - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('defaultValue1'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - ]; - - return [ - // 0. ew language with language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - ] - ), - ], - $spiFields0, - ], - // 1. New language without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => null, - ] - ), - ], - $spiFields0, - ], - // 2. New language and existing language with language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields1, - ], - // 3. New language and existing language without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => null, - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields1, - ], - // 4. New language and existing language with untranslatable field, with language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-GB', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub('newValue3'), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields2, - ], - // 5. New language and existing language with untranslatable field, without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => null, - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-GB', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub('newValue3'), - 'languageCode' => null, - ] - ), - ], - $spiFields2, - ], - // 6. Adding new language without fields - [ - 'eng-US', - [], - $spiFields3, - ], - ]; - } - - /** - * Test for the updateContent() method. - * - * Testing with new language and untranslatable field. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent - * @dataProvider providerForTestUpdateContentNonRedundantFieldSet3 - */ - public function testUpdateContentNonRedundantFieldSet3($initialLanguageCode, $structFields, $spiFields) - { - $existingFields = [ - new Field( - [ - 'id' => '100', - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('id100'), - 'languageCode' => 'eng-GB', - ] - ), - new Field( - [ - 'id' => '101', - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub('id101'), - 'languageCode' => 'eng-GB', - ] - ), - ]; - - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId1', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => true, - 'identifier' => 'identifier1', - 'isRequired' => false, - 'defaultValue' => new ValueStub('defaultValue1'), - ] - ), - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId2', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier2', - 'isRequired' => false, - 'defaultValue' => new ValueStub('defaultValue2'), - ] - ), - ]; - - $this->assertForTestUpdateContentNonRedundantFieldSet( - $initialLanguageCode, - $structFields, - $spiFields, - $existingFields, - $fieldDefinitions - ); - } - - public function providerForTestUpdateContentNonRedundantFieldSet4() - { - $spiFields0 = [ - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - ]; - $spiFields1 = [ - new SPIField( - [ - 'id' => 100, - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => 'eng-GB', - 'versionNo' => 7, - ] - ), - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - ]; - $spiFields2 = [ - new SPIField( - [ - 'id' => 100, - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => 'eng-GB', - 'versionNo' => 7, - ] - ), - ]; - - return [ - // 0. New translation with empty field by default - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - ] - ), - ], - $spiFields0, - ], - // 1. New translation with empty field by default, without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => null, - ] - ), - ], - $spiFields0, - ], - // 2. New translation with empty field given - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => 'eng-US', - ] - ), - ], - $spiFields0, - ], - // 3. New translation with empty field given, without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => null, - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => null, - ] - ), - ], - $spiFields0, - ], - // 4. Updating existing language with empty value - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => 'eng-US', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields1, - ], - // 5. Updating existing language with empty value, without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1'), - 'languageCode' => null, - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields1, - ], - // 6. Updating existing language with empty value and adding new language with empty value - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => 'eng-US', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields2, - ], - // 7. Updating existing language with empty value and adding new language with empty value, - // without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => null, - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields2, - ], - // 8. Adding new language with no fields given - [ - 'eng-US', - [], - [], - ], - // 9. Adding new language with fields - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => 'eng-US', - ] - ), - ], - [], - ], - // 10. Adding new language with fields, without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => null, - ] - ), - ], - [], - ], - ]; - } - - /** - * Test for the updateContent() method. - * - * Testing with empty values. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent - * @dataProvider providerForTestUpdateContentNonRedundantFieldSet4 - */ - public function testUpdateContentNonRedundantFieldSet4($initialLanguageCode, $structFields, $spiFields) - { - $existingFields = [ - new Field( - [ - 'id' => '100', - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('id100'), - 'languageCode' => 'eng-GB', - ] - ), - new Field( - [ - 'id' => '101', - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub('id101'), - 'languageCode' => 'eng-GB', - ] - ), - ]; - - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId1', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => true, - 'identifier' => 'identifier1', - 'isRequired' => false, - 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), - ] - ), - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId2', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => true, - 'identifier' => 'identifier2', - 'isRequired' => false, - 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), - ] - ), - ]; - - $this->assertForTestUpdateContentNonRedundantFieldSet( - $initialLanguageCode, - $structFields, - $spiFields, - $existingFields, - $fieldDefinitions - ); - } - - /** - * @return array - * - * @todo add first field empty - */ - public function providerForTestUpdateContentNonRedundantFieldSetComplex() - { - $spiFields0 = [ - new SPIField( - [ - 'id' => 100, - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue1-eng-GB'), - 'languageCode' => 'eng-GB', - 'versionNo' => 7, - ] - ), - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId4', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue4'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - ]; - $spiFields1 = [ - new SPIField( - [ - 'id' => 100, - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue1-eng-GB'), - 'languageCode' => 'eng-GB', - 'versionNo' => 7, - ] - ), - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId2', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId4', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('defaultValue4'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - ]; - $spiFields2 = [ - new SPIField( - [ - 'id' => 100, - 'fieldDefinitionId' => 'fieldDefinitionId1', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue1-eng-GB'), - 'languageCode' => 'eng-GB', - 'versionNo' => 7, - ] - ), - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId2', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId4', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('defaultValue4'), - 'languageCode' => 'ger-DE', - 'versionNo' => 7, - ] - ), - new SPIField( - [ - 'id' => null, - 'fieldDefinitionId' => 'fieldDefinitionId4', - 'type' => 'fieldTypeIdentifier', - 'value' => new ValueStub('defaultValue4'), - 'languageCode' => 'eng-US', - 'versionNo' => 7, - ] - ), - ]; - - return [ - // 0. Add new language and update existing - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier4', - 'value' => new ValueStub('newValue4'), - 'languageCode' => 'eng-US', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1-eng-GB'), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields0, - ], - // 1. Add new language and update existing, without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier4', - 'value' => new ValueStub('newValue4'), - 'languageCode' => null, - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1-eng-GB'), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields0, - ], - // 2. Add new language and update existing variant - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-US', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1-eng-GB'), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields1, - ], - // 3. Add new language and update existing variant, without language set - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub('newValue2'), - 'languageCode' => null, - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1-eng-GB'), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields1, - ], - // 4. Update with multiple languages - [ - 'ger-DE', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-US', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1-eng-GB'), - 'languageCode' => 'eng-GB', - ] - ), - ], - $spiFields2, - ], - // 5. Update with multiple languages without language set - [ - 'ger-DE', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub('newValue2'), - 'languageCode' => 'eng-US', - ] - ), - new Field( - [ - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('newValue1-eng-GB'), - 'languageCode' => null, - ] - ), - ], - $spiFields2, - ], - ]; - } - - protected function fixturesForTestUpdateContentNonRedundantFieldSetComplex() - { - $existingFields = [ - new Field( - [ - 'id' => '100', - 'fieldDefIdentifier' => 'identifier1', - 'value' => new ValueStub('initialValue1'), - 'languageCode' => 'eng-GB', - ] - ), - new Field( - [ - 'id' => '101', - 'fieldDefIdentifier' => 'identifier2', - 'value' => new ValueStub('initialValue2'), - 'languageCode' => 'eng-GB', - ] - ), - new Field( - [ - 'id' => '102', - 'fieldDefIdentifier' => 'identifier3', - 'value' => new ValueStub('initialValue3'), - 'languageCode' => 'eng-GB', - ] - ), - new Field( - [ - 'id' => '103', - 'fieldDefIdentifier' => 'identifier4', - 'value' => new ValueStub('initialValue4'), - 'languageCode' => 'eng-GB', - ] - ), - ]; - - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId1', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier1', - 'isRequired' => false, - 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), - ] - ), - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId2', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => true, - 'identifier' => 'identifier2', - 'isRequired' => false, - 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), - ] - ), - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId3', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier3', - 'isRequired' => false, - 'defaultValue' => new ValueStub('defaultValue3'), - ] - ), - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId4', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => true, - 'identifier' => 'identifier4', - 'isRequired' => false, - 'defaultValue' => new ValueStub('defaultValue4'), - ] - ), - ]; - - return [$existingFields, $fieldDefinitions]; - } - - /** - * Test for the updateContent() method. - * - * Testing more complex cases. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent - * @dataProvider providerForTestUpdateContentNonRedundantFieldSetComplex - */ - public function testUpdateContentNonRedundantFieldSetComplex($initialLanguageCode, $structFields, $spiFields) - { - list($existingFields, $fieldDefinitions) = $this->fixturesForTestUpdateContentNonRedundantFieldSetComplex(); - - $this->assertForTestUpdateContentNonRedundantFieldSet( - $initialLanguageCode, - $structFields, - $spiFields, - $existingFields, - $fieldDefinitions - ); - } - - public function providerForTestUpdateContentWithInvalidLanguage() - { - return [ - [ - 'eng-GB', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => $this->createMock(Value::class), - 'languageCode' => 'Klingon', - ] - ), - ], - ], - [ - 'Klingon', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => $this->createMock(Value::class), - 'languageCode' => 'eng-GB', - ] - ), - ], - ], - ]; - } - - /** - * Test for the updateContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent - * @dataProvider providerForTestUpdateContentWithInvalidLanguage - */ - public function testUpdateContentWithInvalidLanguage($initialLanguageCode, $structFields) - { - $this->expectException(APINotFoundException::class); - $this->expectExceptionMessage('Could not find \'Language\' with identifier \'Klingon\''); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $mockedService = $this->getPartlyMockedContentService(['loadContent', 'internalLoadContentById']); - $fieldTypeMock = $this->createMock(SPIFieldType::class); - /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ - $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); - $versionInfo = new VersionInfo( - [ - 'contentInfo' => new ContentInfo( - [ - 'id' => 42, - 'contentTypeId' => 24, - 'mainLanguageCode' => 'eng-GB', - ] - ), - 'versionNo' => 7, - 'languageCodes' => ['eng-GB'], - 'status' => VersionInfo::STATUS_DRAFT, - ] - ); - - $fieldValueMock = $this->createMock(Value::class); - - $content = new Content( - [ - 'versionInfo' => $versionInfo, - 'internalFields' => [ - new Field([ - 'fieldDefIdentifier' => 'identifier', - 'value' => $fieldValueMock, - 'languageCode' => 'eng-GB', - ]), - ], - 'contentType' => new ContentType([ - 'fieldDefinitions' => new FieldDefinitionCollection([ - new FieldDefinition([ - 'identifier' => 'identifier', - 'defaultValue' => $fieldValueMock, - ]), - ]), - ]), - ] - ); - - $fieldTypeMock->expects($this->any()) - ->method('acceptValue')->will($this->returnValue($fieldValueMock)); - - $this->getFieldTypeRegistryMock()->expects($this->any()) - ->method('getFieldType')->will($this->returnValue($fieldTypeMock)); - - $languageHandlerMock->expects($this->any()) - ->method('loadByLanguageCode') - ->with($this->isType('string')) - ->will( - $this->returnCallback( - static function ($languageCode) { - if ($languageCode === 'Klingon') { - throw new NotFoundException('Language', 'Klingon'); - } - - return new Language(['id' => 4242]); - } - ) - ); - - $mockedService - ->method('loadContent') - ->with( - $this->equalTo(42), - $this->equalTo(null), - $this->equalTo(7) - )->will( - $this->returnValue($content) - ); - - $mockedService - ->method('internalLoadContentById') - ->will( - $this->returnValue($content) - ); - - $permissionResolverMock - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('edit'), - $this->equalTo($content), - $this->isType('array') - )->will($this->returnValue(true)); - - $contentUpdateStruct = new ContentUpdateStruct( - [ - 'fields' => $structFields, - 'initialLanguageCode' => $initialLanguageCode, - ] - ); - - $mockedService->updateContent($content->versionInfo, $contentUpdateStruct); - } - - protected function assertForUpdateContentContentValidationException( - $initialLanguageCode, - $structFields, - $fieldDefinitions = [] - ) { - $permissionResolverMock = $this->getPermissionResolverMock(); - $mockedService = $this->getPartlyMockedContentService(['internalLoadContentById', 'loadContent']); - /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ - $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); - $versionInfo = new VersionInfo( - [ - 'contentInfo' => new ContentInfo( - [ - 'id' => 42, - 'contentTypeId' => 24, - 'mainLanguageCode' => 'eng-GB', - ] - ), - 'versionNo' => 7, - 'languageCodes' => ['eng-GB'], - 'status' => VersionInfo::STATUS_DRAFT, - ] - ); - $contentType = new ContentType([ - 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), - ]); - $content = new Content( - [ - 'versionInfo' => $versionInfo, - 'internalFields' => [], - 'contentType' => $contentType, - ] - ); - - $fieldTypeMock = $this->createMock(FieldType::class); - - $fieldTypeMock - ->method('acceptValue') - ->will( - $this->returnCallback( - static function ($value) { - return $value instanceof SPIValue - ? $value - : new ValueStub($value); - } - ) - ); - - $this->toHashFieldTypeMock($fieldTypeMock); - - $this->getFieldTypeRegistryMock()->expects($this->any()) - ->method('getFieldType')->will($this->returnValue($fieldTypeMock)); - - $languageHandlerMock->expects($this->any()) - ->method('loadByLanguageCode') - ->with($this->isType('string')) - ->will( - $this->returnCallback( - static function ($languageCode) { - if ($languageCode === 'Klingon') { - throw new NotFoundException('Language', 'Klingon'); - } - - return new Language(['id' => 4242]); - } - ) - ); - - $mockedService - ->method('loadContent') - ->with( - $this->equalTo(42), - $this->equalTo(null), - $this->equalTo(7) - )->will( - $this->returnValue($content) - ); - - $mockedService - ->method('internalLoadContentById') - ->will( - $this->returnValue($content) - ); - - $permissionResolverMock->expects($this->any()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('edit'), - $this->equalTo($content), - $this->isType('array') - )->will($this->returnValue(true)); - - /* - $contentTypeServiceMock->expects($this->once()) - ->method('loadContentType') - ->with($this->equalTo($contentType->id)) - ->will($this->returnValue($contentType)); - */ - - $contentUpdateStruct = new ContentUpdateStruct( - [ - 'fields' => $structFields, - 'initialLanguageCode' => $initialLanguageCode, - ] - ); - - $mockedService->updateContent($content->versionInfo, $contentUpdateStruct); - } - - public function providerForTestUpdateContentThrowsContentValidationExceptionFieldDefinition() - { - return [ - [ - 'eng-GB', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'eng-GB', - ] - ), - ], - ], - ]; - } - - /** - * Test for the updateContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent - * @dataProvider providerForTestUpdateContentThrowsContentValidationExceptionFieldDefinition - */ - public function testUpdateContentThrowsContentValidationExceptionFieldDefinition($initialLanguageCode, $structFields) - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class); - $this->expectExceptionMessage('Field definition \'identifier\' does not exist in given Content Type'); - - $this->assertForUpdateContentContentValidationException( - $initialLanguageCode, - $structFields, - [] - ); - } - - public function providerForTestUpdateContentThrowsContentValidationExceptionTranslation() - { - return [ - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('newValue'), - 'languageCode' => 'eng-US', - ] - ), - ], - ], - ]; - } - - /** - * Test for the updateContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent - * @dataProvider providerForTestUpdateContentThrowsContentValidationExceptionTranslation - */ - public function testUpdateContentThrowsContentValidationExceptionTranslation($initialLanguageCode, $structFields) - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class); - $this->expectExceptionMessage('You cannot set a value for the non-translatable Field definition \'identifier\' in language \'eng-US\''); - - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId1', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier', - 'isRequired' => false, - 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), - ] - ), - ]; - - $this->assertForUpdateContentContentValidationException( - $initialLanguageCode, - $structFields, - $fieldDefinitions - ); - } - - private function prepareContentForTestCreateAndUpdateContent( - array $existingLanguageCodes, - array $fieldDefinitions, - array $existingFields - ): Content { - $versionInfo = new VersionInfo( - [ - 'contentInfo' => new ContentInfo( - [ - 'id' => 42, - 'contentTypeId' => 24, - 'mainLanguageCode' => 'eng-GB', - ] - ), - 'versionNo' => 7, - 'languageCodes' => $existingLanguageCodes, - 'status' => VersionInfo::STATUS_DRAFT, - 'names' => [ - 'eng-GB' => 'Test', - ], - 'initialLanguageCode' => 'eng-GB', - ] - ); - $contentType = new ContentType([ - 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), - ]); - - return new Content( - [ - 'versionInfo' => $versionInfo, - 'internalFields' => $existingFields, - 'contentType' => $contentType, - ] - ); - } - - public function assertForTestUpdateContentRequiredField( - $initialLanguageCode, - $structFields, - $existingFields, - $fieldDefinitions - ): array { - $permissionResolver = $this->getPermissionResolverMock(); - $mockedService = $this->getPartlyMockedContentService(['internalLoadContentById', 'loadContent']); - /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ - $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); - $fieldTypeMock = $this->createMock(SPIFieldType::class); - $existingLanguageCodes = array_map( - static function (Field $field) { - return $field->languageCode; - }, - $existingFields - ); - $content = $this->prepareContentForTestCreateAndUpdateContent( - $existingLanguageCodes, - $fieldDefinitions, - $existingFields - ); - - $this->loadByLanguageCodeMock($languageHandlerMock); - - $mockedService - ->method('loadContent') - ->with( - self::equalTo(42), - self::equalTo(null), - self::equalTo(7) - )->will( - self::returnValue($content) - ); - - $mockedService - ->method('internalLoadContentById') - ->will( - self::returnValue($content) - ); - - $permissionResolver->expects(self::any()) - ->method('canUser') - ->with( - self::equalTo('content'), - self::equalTo('edit'), - self::equalTo($content), - self::isType('array') - )->will(self::returnValue(true)); - - $this->acceptFieldTypeValueMock($fieldTypeMock); - - $this->isEmptyValueFieldTypeMock($fieldTypeMock); - - $fieldTypeMock->expects(self::any()) - ->method('validate') - ->with( - self::isInstanceOf(APIFieldDefinition::class), - self::isInstanceOf(Value::class) - ); - - $this->getFieldTypeRegistryMock()->expects(self::any()) - ->method('getFieldType') - ->will($this->returnValue($fieldTypeMock)); - - $contentUpdateStruct = new ContentUpdateStruct( - [ - 'fields' => $structFields, - 'initialLanguageCode' => $initialLanguageCode, - ] - ); - - return [$content->versionInfo, $contentUpdateStruct]; - } - - public function providerForTestUpdateContentRequiredField() - { - return [ - [ - 'eng-US', - [ - new Field( - [ - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), - 'languageCode' => null, - ] - ), - ], - 'identifier', - 'eng-US', - ], - ]; - } - - /** - * Test for the updateContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent - * @dataProvider providerForTestUpdateContentRequiredField - */ - public function testUpdateContentRequiredField( - $initialLanguageCode, - $structFields, - $identifier, - $languageCode - ) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class); - - $existingFields = [ - new Field( - [ - 'id' => '100', - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('initialValue'), - 'languageCode' => 'eng-GB', - ] - ), - ]; - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => true, - 'identifier' => 'identifier', - 'isRequired' => true, - 'defaultValue' => new ValueStub('defaultValue'), - ] - ), - ]; - list($versionInfo, $contentUpdateStruct) = - $this->assertForTestUpdateContentRequiredField( - $initialLanguageCode, - $structFields, - $existingFields, - $fieldDefinitions - ); - - try { - $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct); - } catch (ContentValidationException $e) { - $this->assertEquals( - "Value for required field definition '{$identifier}' with language '{$languageCode}' is empty", - $e->getMessage() - ); - - throw $e; - } - } - - public function assertForTestUpdateContentThrowsContentFieldValidationException( - $initialLanguageCode, - $structFields, - $existingFields, - $fieldDefinitions, - array $allFieldErrors - ): array { - $permissionResolverMock = $this->getPermissionResolverMock(); - $mockedService = $this->getPartlyMockedContentService(['internalLoadContentById', 'loadContent']); - $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); - $fieldTypeMock = $this->createMock(SPIFieldType::class); - $existingLanguageCodes = array_map( - static function (Field $field) { - return $field->languageCode; - }, - $existingFields - ); - $content = $this->prepareContentForTestCreateAndUpdateContent($existingLanguageCodes, $fieldDefinitions, $existingFields); - - $this->loadByLanguageCodeMock($languageHandlerMock); - - $mockedService - ->method('internalLoadContentById') - ->will( - self::returnValue($content) - ); - - $mockedService - ->method('loadContent') - ->with( - self::equalTo(42), - self::equalTo(null), - self::equalTo(7) - ) - ->will( - self::returnValue($content) - ); - - $permissionResolverMock - ->method('canUser') - ->will(self::returnValue(true)); - - $emptyValue = new ValueStub(self::EMPTY_FIELD_VALUE); - - $fieldTypeMock - ->method('acceptValue') - ->will( - self::returnCallback( - static function ($value) { - return $value instanceof SPIValue - ? $value - : new ValueStub($value); - } - ) - ); - - $fieldTypeMock - ->method('isEmptyValue') - ->will( - self::returnCallback( - static function (ValueStub $value) use ($emptyValue) { - return (string)$emptyValue === (string)$value; - } - ) - ); - - $fieldTypeMock - ->expects(self::any()) - ->method('validate') - ->will( - self::returnCallback( - static function (FieldDefinition $fieldDefinition) use ($allFieldErrors, $structFields) { - foreach ($structFields as $structField) { - if ($structField->fieldDefIdentifier !== $fieldDefinition->identifier) { - continue; - } - - return $allFieldErrors[$fieldDefinition->id][$structField->languageCode] ?? null; - } - - return null; - } - ) - ); - - $this->getFieldTypeRegistryMock()->expects(self::any()) - ->method('getFieldType') - ->will(self::returnValue($fieldTypeMock)); - - $contentUpdateStruct = new ContentUpdateStruct( - [ - 'fields' => $structFields, - 'initialLanguageCode' => $initialLanguageCode, - ] - ); - - return [$content->versionInfo, $contentUpdateStruct, $allFieldErrors]; - } - - public function providerForTestUpdateContentThrowsContentFieldValidationException() - { - $newValue1engGBValidationError = new ValidationError('newValue1-eng-GB'); - $newValue2ValidationError = new ValidationError('newValue2'); - $newValue4ValidationError = new ValidationError('newValue4'); - - $allFieldErrors = [ - [ - 'fieldDefinitionId1' => [ - 'eng-GB' => $newValue1engGBValidationError, - 'eng-US' => $newValue1engGBValidationError, - ], - 'fieldDefinitionId4' => [ - 'eng-GB' => $newValue4ValidationError, - 'eng-US' => $newValue4ValidationError, - ], - ], - [ - 'fieldDefinitionId1' => [ - 'eng-GB' => $newValue1engGBValidationError, - 'eng-US' => $newValue1engGBValidationError, - ], - ], - [ - 'fieldDefinitionId1' => [ - 'eng-GB' => $newValue1engGBValidationError, - 'eng-US' => $newValue1engGBValidationError, - ], - 'fieldDefinitionId2' => [ - 'eng-GB' => $newValue2ValidationError, - 'eng-US' => $newValue2ValidationError, - ], - ], - [ - 'fieldDefinitionId1' => [ - 'eng-GB' => $newValue1engGBValidationError, - 'eng-US' => $newValue1engGBValidationError, - ], - ], - [ - 'fieldDefinitionId1' => [ - 'eng-GB' => $newValue1engGBValidationError, - 'ger-DE' => $newValue1engGBValidationError, - 'eng-US' => $newValue1engGBValidationError, - ], - 'fieldDefinitionId2' => [ - 'eng-GB' => $newValue2ValidationError, - 'eng-US' => $newValue2ValidationError, - ], - ], - [ - 'fieldDefinitionId2' => [ - 'eng-US' => $newValue2ValidationError, - ], - ], - ]; - - $data = $this->providerForTestUpdateContentNonRedundantFieldSetComplex(); - $count = count($data); - for ($i = 0; $i < $count; ++$i) { - $data[$i][] = $allFieldErrors[$i]; - } - - return $data; - } - - /** - * Test for the updateContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent - * @dataProvider providerForTestUpdateContentThrowsContentFieldValidationException - */ - public function testUpdateContentThrowsContentFieldValidationException( - $initialLanguageCode, - $structFields, - $spiField, - $allFieldErrors - ): void { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException::class); - $this->expectExceptionMessage('Content "Test" fields did not validate'); - list($existingFields, $fieldDefinitions) = $this->fixturesForTestUpdateContentNonRedundantFieldSetComplex(); - list($versionInfo, $contentUpdateStruct) = - $this->assertForTestUpdateContentThrowsContentFieldValidationException( - $initialLanguageCode, - $structFields, - $existingFields, - $fieldDefinitions, - $allFieldErrors - ); - - try { - $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct); - } catch (ContentFieldValidationException $e) { - $this->assertEquals($allFieldErrors, $e->getFieldErrors()); - throw $e; - } - } - - /** - * Test for the updateContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::getLanguageCodesForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::mapFieldsForUpdate - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent - */ - public function testUpdateContentTransactionRollback() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Store failed'); - - $existingFields = [ - new Field( - [ - 'id' => '100', - 'fieldDefIdentifier' => 'identifier', - 'value' => new ValueStub('initialValue'), - 'languageCode' => 'eng-GB', - ] - ), - ]; - - $fieldDefinitions = [ - new FieldDefinition( - [ - 'id' => 'fieldDefinitionId', - 'fieldTypeIdentifier' => 'fieldTypeIdentifier', - 'isTranslatable' => false, - 'identifier' => 'identifier', - 'isRequired' => false, - 'defaultValue' => new ValueStub('defaultValue'), - ] - ), - ]; - - // Setup a simple case that will pass - list($versionInfo, $contentUpdateStruct) = $this->assertForTestUpdateContentNonRedundantFieldSet( - 'eng-US', - [], - [], - $existingFields, - $fieldDefinitions, - // Do not execute test - false - ); - - $repositoryMock = $this->getRepositoryMock(); - $repositoryMock->expects($this->never())->method('commit'); - $repositoryMock->expects($this->once())->method('rollback'); - - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ - $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); - $contentHandlerMock->expects($this->once()) - ->method('updateContent') - ->with( - $this->anything(), - $this->anything(), - $this->anything() - )->will($this->throwException(new \Exception('Store failed'))); - - // Execute - $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct); - } - - /** - * Test for the copyContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::copyContent - */ - public function testCopyContentThrowsUnauthorizedException() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class); - - $repository = $this->getRepositoryMock(); - $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfo']); - $contentInfo = $this->createMock(APIContentInfo::class); - $locationCreateStruct = new LocationCreateStruct(); - $locationCreateStruct->parentLocationId = 1; - $location = new Location(['id' => $locationCreateStruct->parentLocationId]); - $locationServiceMock = $this->getLocationServiceMock(); - $permissionResolver = $this->getPermissionResolverMock(); - - $repository->expects(self::once()) - ->method('getLocationService') - ->will(self::returnValue($locationServiceMock)); - - $locationServiceMock->expects(self::once()) - ->method('loadLocation') - ->with( - $locationCreateStruct->parentLocationId - ) - ->will(self::returnValue($location)); - - $contentInfo->expects(self::any()) - ->method('__get') - ->with('sectionId') - ->will(self::returnValue(42)); - - $destinationLocationTarget = (new DestinationLocation($locationCreateStruct->parentLocationId, $contentInfo)); - $permissionResolver - ->method('canUser') - ->with( - 'content', - 'create', - $contentInfo, - [$location, $destinationLocationTarget] - ) - ->will(self::returnValue(false)); - - /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo */ - $contentService->copyContent($contentInfo, $locationCreateStruct); - } - - /** - * Test for the copyContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::copyContent - * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates - * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion - */ - public function testCopyContent() - { - $repositoryMock = $this->getRepositoryMock(); - $contentService = $this->getPartlyMockedContentService([ - 'internalLoadContentInfo', - 'internalLoadContentById', - 'loadContentByVersionInfo', - 'getUnixTimestamp', - ]); - $locationServiceMock = $this->getLocationServiceMock(); - $contentInfoMock = $this->createMock(APIContentInfo::class); - $locationCreateStruct = new LocationCreateStruct(); - $locationCreateStruct->parentLocationId = 2; - $location = new Location(['id' => $locationCreateStruct->parentLocationId]); - $user = $this->getStubbedUser(14); - - $permissionResolverMock = $this->getPermissionResolverMock(); - - $permissionResolverMock - ->method('getCurrentUserReference') - ->willReturn($user); - - $repositoryMock - ->method('getPermissionResolver') - ->willReturn($permissionResolverMock); - - $repositoryMock->expects($this->exactly(3)) - ->method('getLocationService') - ->will($this->returnValue($locationServiceMock)); - - $locationServiceMock->expects($this->once()) - ->method('loadLocation') - ->with($locationCreateStruct->parentLocationId) - ->will($this->returnValue($location)); - - $contentInfoMock->expects($this->any()) - ->method('__get') - ->will( - $this->returnValueMap( - [ - ['isHidden', true], - ['id', 42], - ] - ) - ); - $versionInfoMock = $this->createMock(APIVersionInfo::class); - - $versionInfoMock->expects($this->any()) - ->method('__get') - ->will( - $this->returnValueMap( - [ - ['versionNo', 123], - ] - ) - ); - - $versionInfoMock->expects($this->once()) - ->method('isDraft') - ->willReturn(true); - - $versionInfoMock - ->method('getContentInfo') - ->will($this->returnValue($contentInfoMock)); - - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ - $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); - $domainMapperMock = $this->getContentDomainMapperMock(); - - $repositoryMock->expects($this->once())->method('beginTransaction'); - $repositoryMock->expects($this->once())->method('commit'); - - $destinationLocationTarget = (new DestinationLocation($locationCreateStruct->parentLocationId, $contentInfoMock)); - $permissionResolverMock - ->method('canUser') - ->withConsecutive( - ['content', 'create', $contentInfoMock, [$location, $destinationLocationTarget]], - ['content', 'manage_locations', $contentInfoMock, [$location]], - ) - ->willReturnOnConsecutiveCalls(true, true); - - $spiContentInfo = new SPIContentInfo(['id' => 42]); - $spiVersionInfo = new SPIVersionInfo( - [ - 'contentInfo' => $spiContentInfo, - 'creationDate' => 123456, - ] - ); - $spiContent = new SPIContent(['versionInfo' => $spiVersionInfo]); - $contentHandlerMock->expects($this->once()) - ->method('copy') - ->with(42, null) - ->will($this->returnValue($spiContent)); - - $this->mockGetDefaultObjectStates(); - $this->mockSetDefaultObjectStates(); - - $domainMapperMock->expects($this->once()) - ->method('buildVersionInfoDomainObject') - ->with($spiVersionInfo) - ->will($this->returnValue($versionInfoMock)); - - /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfoMock */ - $content = $this->mockPublishVersion(123456, 126666, true); - $locationServiceMock->expects($this->once()) - ->method('createLocation') - ->with( - $content->getVersionInfo()->getContentInfo(), - $locationCreateStruct - ); - - $contentService - ->method('internalLoadContentById') - ->with( - $content->id - ) - ->will($this->returnValue($content)); - - $contentService->expects($this->once()) - ->method('getUnixTimestamp') - ->will($this->returnValue(126666)); - - $contentService - ->method('loadContentByVersionInfo') - ->will($this->returnValue($content)); - - /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */ - $contentService->copyContent($contentInfoMock, $locationCreateStruct, null); - } - - /** - * Test for the copyContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::copyContent - * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates - * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion - */ - public function testCopyContentWithVersionInfo() - { - $repositoryMock = $this->getRepositoryMock(); - $contentService = $this->getPartlyMockedContentService([ - 'internalLoadContentById', - 'getUnixTimestamp', - ]); - $locationServiceMock = $this->getLocationServiceMock(); - $contentInfoMock = $this->createMock(APIContentInfo::class); - $locationCreateStruct = new LocationCreateStruct(); - $locationCreateStruct->parentLocationId = 2; - $location = new Location(['id' => $locationCreateStruct->parentLocationId]); - $user = $this->getStubbedUser(14); - - $permissionResolverMock = $this->getPermissionResolverMock(); - - $permissionResolverMock - ->method('getCurrentUserReference') - ->willReturn($user); - - $repositoryMock - ->method('getPermissionResolver') - ->willReturn($permissionResolverMock); - - $repositoryMock->expects($this->exactly(3)) - ->method('getLocationService') - ->will($this->returnValue($locationServiceMock)); - - $locationServiceMock->expects($this->once()) - ->method('loadLocation') - ->with($locationCreateStruct->parentLocationId) - ->will($this->returnValue($location)); - - $contentInfoMock->expects($this->any()) - ->method('__get') - ->will( - $this->returnValueMap([ - ['isHidden', true], - ['id', 42], - ]) - ); - - $versionInfoMock = $this->createMock(APIVersionInfo::class); - - $versionInfoMock->expects($this->any()) - ->method('__get') - ->will( - $this->returnValueMap( - [ - ['versionNo', 123], - ] - ) - ); - $versionInfoMock->expects($this->once()) - ->method('isDraft') - ->willReturn(true); - $versionInfoMock - ->method('getContentInfo') - ->will($this->returnValue($contentInfoMock)); - - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ - $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); - $domainMapperMock = $this->getContentDomainMapperMock(); - - $repositoryMock->expects($this->once())->method('beginTransaction'); - $repositoryMock->expects($this->once())->method('commit'); - - $destinationLocationTarget = (new DestinationLocation($locationCreateStruct->parentLocationId, $contentInfoMock)); - $permissionResolverMock - ->method('canUser') - ->withConsecutive( - ['content', 'create', $contentInfoMock, [$location, $destinationLocationTarget]], - ['content', 'manage_locations', $contentInfoMock, [$location]], - ) - ->willReturnOnConsecutiveCalls(true, true); - - $spiContentInfo = new SPIContentInfo(['id' => 42]); - $spiVersionInfo = new SPIVersionInfo( - [ - 'contentInfo' => $spiContentInfo, - 'creationDate' => 123456, - ] - ); - $spiContent = new SPIContent(['versionInfo' => $spiVersionInfo]); - $contentHandlerMock->expects($this->once()) - ->method('copy') - ->with(42, 123) - ->will($this->returnValue($spiContent)); - - $this->mockGetDefaultObjectStates(); - $this->mockSetDefaultObjectStates(); - - $domainMapperMock->expects($this->once()) - ->method('buildVersionInfoDomainObject') - ->with($spiVersionInfo) - ->will($this->returnValue($versionInfoMock)); - - /* @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfoMock */ - $content = $this->mockPublishVersion(123456, 126666, true); - $locationServiceMock->expects($this->once()) - ->method('createLocation') - ->with( - $content->getVersionInfo()->getContentInfo(), - $locationCreateStruct - ); - - $contentService - ->method('internalLoadContentById') - ->with( - $content->id - ) - ->will($this->returnValue($content)); - - $contentService->expects($this->once()) - ->method('getUnixTimestamp') - ->will($this->returnValue(126666)); - - /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */ - $contentService->copyContent($contentInfoMock, $locationCreateStruct, $versionInfoMock); - } - - /** - * Test for the copyContent() method. - * - * @covers \eZ\Publish\Core\Repository\ContentService::copyContent - * @covers \eZ\Publish\Core\Repository\ContentService::getDefaultObjectStates - * @covers \eZ\Publish\Core\Repository\ContentService::internalPublishVersion - */ - public function testCopyContentWithRollback() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Handler threw an exception'); - - $repositoryMock = $this->getRepositoryMock(); - $contentService = $this->getPartlyMockedContentService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ - $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); - $locationCreateStruct = new LocationCreateStruct(); - $locationCreateStruct->parentLocationId = 2; - $location = new Location(['id' => $locationCreateStruct->parentLocationId]); - $locationServiceMock = $this->getLocationServiceMock(); - $user = $this->getStubbedUser(14); - - $permissionResolverMock = $this->getPermissionResolverMock(); - - $permissionResolverMock - ->method('getCurrentUserReference') - ->willReturn($user); - - $repositoryMock - ->method('getPermissionResolver') - ->willReturn($permissionResolverMock); - - $repositoryMock->expects($this->once()) - ->method('getLocationService') - ->will($this->returnValue($locationServiceMock)); - - $locationServiceMock->expects($this->once()) - ->method('loadLocation') - ->with($locationCreateStruct->parentLocationId) - ->will($this->returnValue($location)); - - $contentInfoMock = $this->createMock(APIContentInfo::class); - $contentInfoMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(42)); - - $this->mockGetDefaultObjectStates(); - - $repositoryMock->expects($this->once())->method('beginTransaction'); - $repositoryMock->expects($this->once())->method('rollback'); - - $destinationLocationTarget = (new DestinationLocation($locationCreateStruct->parentLocationId, $contentInfoMock)); - $permissionResolverMock - ->method('canUser') - ->withConsecutive( - ['content', 'create', $contentInfoMock, [$location, $destinationLocationTarget]], - ['content', 'manage_locations', $contentInfoMock, [$location]], - ) - ->willReturnOnConsecutiveCalls(true, true); - - $contentHandlerMock->expects($this->once()) - ->method('copy') - ->with(42, null) - ->will($this->throwException(new Exception('Handler threw an exception'))); - - /* @var \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfoMock */ - $contentService->copyContent($contentInfoMock, $locationCreateStruct, null); - } - - /** - * Reusable method for setting exceptions on buildContentDomainObject usage. - * - * Plain usage as in when content type is loaded directly. - * - * @param \eZ\Publish\SPI\Persistence\Content $spiContent - * @param array $translations - * @param bool $useAlwaysAvailable - * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\Values\Content\Content - */ - private function mockBuildContentDomainObject(SPIContent $spiContent, array $translations = null, bool $useAlwaysAvailable = null) - { - $contentTypeId = $spiContent->versionInfo->contentInfo->contentTypeId; - $contentTypeServiceMock = $this->getContentTypeServiceMock(); - $repositoryMock = $this->getRepositoryMock(); - - $contentType = new ContentType([ - 'id' => $contentTypeId, - 'fieldDefinitions' => new FieldDefinitionCollection([]), - ]); - - $repositoryMock->expects($this->once()) - ->method('getContentTypeService') - ->willReturn($contentTypeServiceMock); - - $contentTypeServiceMock - ->method('loadContentType') - ->with($this->equalTo($contentTypeId)) - ->willReturn($contentType); - - $content = $this->createMock(APIContent::class); - $content->method('getContentType') - ->willReturn($contentType); - - $this->getContentDomainMapperMock() - ->expects($this->once()) - ->method('buildContentDomainObject') - ->with($spiContent, $contentType, $translations ?? [], $useAlwaysAvailable) - ->willReturn($content); - - return $content; - } - - protected function mockGetDefaultObjectStates() - { - /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */ - $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler(); - - $objectStateGroups = [ - new SPIObjectStateGroup(['id' => 10]), - new SPIObjectStateGroup(['id' => 20]), - ]; - - /* @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */ - $objectStateHandlerMock->expects($this->once()) - ->method('loadAllGroups') - ->will($this->returnValue($objectStateGroups)); - - $objectStateHandlerMock->expects($this->at(1)) - ->method('loadObjectStates') - ->with($this->equalTo(10)) - ->will( - $this->returnValue( - [ - new SPIObjectState(['id' => 11, 'groupId' => 10]), - new SPIObjectState(['id' => 12, 'groupId' => 10]), - ] - ) - ); - - $objectStateHandlerMock->expects($this->at(2)) - ->method('loadObjectStates') - ->with($this->equalTo(20)) - ->will( - $this->returnValue( - [ - new SPIObjectState(['id' => 21, 'groupId' => 20]), - new SPIObjectState(['id' => 22, 'groupId' => 20]), - ] - ) - ); - } - - protected function mockSetDefaultObjectStates() - { - /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */ - $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler(); - - $defaultObjectStates = [ - new SPIObjectState(['id' => 11, 'groupId' => 10]), - new SPIObjectState(['id' => 21, 'groupId' => 20]), - ]; - foreach ($defaultObjectStates as $index => $objectState) { - $objectStateHandlerMock->expects($this->at($index + 3)) - ->method('setContentState') - ->with( - 42, - $objectState->groupId, - $objectState->id - ); - } - } - - /** - * @param int|null $publicationDate - * @param int|null $modificationDate - * @param bool $isHidden - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - protected function mockPublishVersion($publicationDate = null, $modificationDate = null, $isHidden = false) - { - $versionInfoMock = $this->createMock(APIVersionInfo::class); - $contentInfoMock = $this->createMock(APIContentInfo::class); - /* @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ - $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); - $metadataUpdateStruct = new SPIMetadataUpdateStruct(); - - $spiContent = new SPIContent([ - 'versionInfo' => new VersionInfo([ - 'contentInfo' => new ContentInfo(['id' => 42, 'contentTypeId' => 123]), - ]), - 'fields' => new FieldDefinitionCollection([]), - ]); - - $contentMock = $this->mockBuildContentDomainObject($spiContent); - $contentMock->expects($this->any()) - ->method('__get') - ->will( - $this->returnValueMap( - [ - ['id', 42], - ['contentInfo', $contentInfoMock], - ['versionInfo', $versionInfoMock], - ] - ) - ); - $contentMock->expects($this->any()) - ->method('getVersionInfo') - ->will($this->returnValue($versionInfoMock)); - $versionInfoMock->expects($this->any()) - ->method('getContentInfo') - ->will($this->returnValue($contentInfoMock)); - $versionInfoMock->expects($this->any()) - ->method('__get') - ->will( - $this->returnValueMap( - [ - ['languageCodes', ['eng-GB']], - ] - ) - ); - $contentInfoMock->expects($this->any()) - ->method('__get') - ->will( - $this->returnValueMap( - [ - ['alwaysAvailable', true], - ['mainLanguageCode', 'eng-GB'], - ] - ) - ); - - $currentTime = time(); - if ($publicationDate === null && $versionInfoMock->versionNo === 1) { - $publicationDate = $currentTime; - } - - // Account for 1 second of test execution time - $metadataUpdateStruct->publicationDate = $publicationDate; - $metadataUpdateStruct->modificationDate = $modificationDate ?? $currentTime; - $metadataUpdateStruct->isHidden = $isHidden; - - $contentHandlerMock->expects($this->once()) - ->method('publish') - ->with( - 42, - 123, - $metadataUpdateStruct - ) - ->will($this->returnValue($spiContent)); - - /* @var \eZ\Publish\API\Repository\Values\Content\Content $contentMock */ - $this->mockPublishUrlAliasesForContent($contentMock); - - return $contentMock; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - */ - protected function mockPublishUrlAliasesForContent(APIContent $content) - { - $nameSchemaServiceMock = $this->getNameSchemaServiceMock(); - /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ - $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); - $locationServiceMock = $this->getLocationServiceMock(); - $location = $this->createMock(APILocation::class); - - $location->expects($this->at(0)) - ->method('__get') - ->with('id') - ->will($this->returnValue(123)); - $location->expects($this->at(1)) - ->method('__get') - ->with('parentLocationId') - ->will($this->returnValue(456)); - - $urlAliasNames = ['eng-GB' => 'hello']; - $nameSchemaServiceMock->expects($this->once()) - ->method('resolveUrlAliasSchema') - ->with($content) - ->will($this->returnValue($urlAliasNames)); - - $locationServiceMock->expects($this->once()) - ->method('loadLocations') - ->with($content->getVersionInfo()->getContentInfo()) - ->will($this->returnValue([$location])); - - $urlAliasHandlerMock->expects($this->once()) - ->method('publishUrlAliasForLocation') - ->with(123, 456, 'hello', 'eng-GB', true, true); - - $location->expects($this->at(2)) - ->method('__get') - ->with('id') - ->will($this->returnValue(123)); - - $location->expects($this->at(3)) - ->method('__get') - ->with('parentLocationId') - ->will($this->returnValue(456)); - - $urlAliasHandlerMock->expects($this->once()) - ->method('archiveUrlAliasesForDeletedTranslations') - ->with(123, 456, ['eng-GB']); - } - - protected $relationProcessorMock; - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\RelationProcessor - */ - protected function getRelationProcessorMock() - { - if (!isset($this->relationProcessorMock)) { - $this->relationProcessorMock = $this->createMock(RelationProcessor::class); - } - - return $this->relationProcessorMock; - } - - protected $nameSchemaServiceMock; - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\NameSchemaService - */ - protected function getNameSchemaServiceMock() - { - if (!isset($this->nameSchemaServiceMock)) { - $this->nameSchemaServiceMock = $this->createMock(NameSchemaService::class); - } - - return $this->nameSchemaServiceMock; - } - - protected $contentTypeServiceMock; - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\ContentTypeService - */ - protected function getContentTypeServiceMock() - { - if (!isset($this->contentTypeServiceMock)) { - $this->contentTypeServiceMock = $this->createMock(APIContentTypeService::class); - } - - return $this->contentTypeServiceMock; - } - - protected $locationServiceMock; - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\LocationService - */ - protected function getLocationServiceMock() - { - if (!isset($this->locationServiceMock)) { - $this->locationServiceMock = $this->createMock(APILocationService::class); - } - - return $this->locationServiceMock; - } - - /** @var \eZ\Publish\Core\Repository\ContentService */ - protected $partlyMockedContentService; - - /** - * Returns the content service to test with $methods mocked. - * - * Injected Repository comes from {@see getRepositoryMock()} and persistence handler from {@see getPersistenceMock()} - * - * @param string[] $methods - * - * @return \eZ\Publish\Core\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getPartlyMockedContentService(array $methods = null) - { - if (!isset($this->partlyMockedContentService)) { - $this->partlyMockedContentService = $this->getMockBuilder(ContentService::class) - ->setMethods($methods) - ->setConstructorArgs( - [ - $this->getRepositoryMock(), - $this->getPersistenceMock(), - $this->getContentDomainMapperMock(), - $this->getRelationProcessorMock(), - $this->getNameSchemaServiceMock(), - $this->getFieldTypeRegistryMock(), - $this->getPermissionServiceMock(), - $this->getContentMapper(), - $this->getContentValidatorStrategy(), - $this->getContentFilteringHandlerMock(), - [], - ] - ) - ->getMock(); - } - - return $this->partlyMockedContentService; - } - - /** - * @return \eZ\Publish\API\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getRepositoryMock(): Repository - { - $repositoryMock = parent::getRepositoryMock(); - $repositoryMock - ->expects($this->any()) - ->method('getPermissionResolver') - ->willReturn($this->getPermissionResolverMock()); - - return $repositoryMock; - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/DomainMapperTest.php b/eZ/Publish/Core/Repository/Tests/Service/Mock/DomainMapperTest.php deleted file mode 100644 index 589f853a82..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/DomainMapperTest.php +++ /dev/null @@ -1,318 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; - -use DateTime; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\Core\Repository\Mapper\ContentDomainMapper; -use eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperInterface; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\ContentInfo as SPIContentInfo; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\VersionInfo as SPIVersionInfo; -use Psr\Log\LoggerInterface; - -/** - * Mock test case for internal ContentDomainMapper. - */ -class DomainMapperTest extends BaseServiceMockTest -{ - private const EXAMPLE_CONTENT_TYPE_ID = 1; - private const EXAMPLE_SECTION_ID = 1; - private const EXAMPLE_MAIN_LOCATION_ID = 1; - private const EXAMPLE_MAIN_LANGUAGE_CODE = 'ger-DE'; - private const EXAMPLE_OWNER_ID = 1; - private const EXAMPLE_INITIAL_LANGUAGE_CODE = 'eng-GB'; - private const EXAMPLE_CREATOR_ID = 23; - - /** - * @covers \eZ\Publish\Core\Repository\Mapper\ContentDomainMapper::buildVersionInfoDomainObject - * @dataProvider providerForBuildVersionInfo - */ - public function testBuildVersionInfo(SPIVersionInfo $spiVersionInfo) - { - $languageHandlerMock = $this->getLanguageHandlerMock(); - $languageHandlerMock->expects($this->never())->method('load'); - - $versionInfo = $this->getContentDomainMapper()->buildVersionInfoDomainObject($spiVersionInfo); - - $this->assertInstanceOf(APIVersionInfo::class, $versionInfo); - } - - /** - * @covers \eZ\Publish\Core\Repository\Mapper\ContentDomainMapper::buildLocationWithContent - */ - public function testBuildLocationWithContentForRootLocation() - { - $spiRootLocation = new Location(['id' => 1, 'parentId' => 1]); - $apiRootLocation = $this->getContentDomainMapper()->buildLocationWithContent($spiRootLocation, null); - - $legacyDateTime = new DateTime(); - $legacyDateTime->setTimestamp(1030968000); - - $expectedContentInfo = new \eZ\Publish\API\Repository\Values\Content\ContentInfo([ - 'id' => 0, - 'name' => 'Top Level Nodes', - 'sectionId' => 1, - 'mainLocationId' => 1, - 'contentTypeId' => 1, - 'currentVersionNo' => 1, - 'published' => 1, - 'ownerId' => 14, - 'modificationDate' => $legacyDateTime, - 'publishedDate' => $legacyDateTime, - 'alwaysAvailable' => 1, - 'remoteId' => null, - 'mainLanguageCode' => 'eng-GB', - ]); - - $expectedContent = new Content([ - 'versionInfo' => new VersionInfo([ - 'names' => [ - $expectedContentInfo->mainLanguageCode => $expectedContentInfo->name, - ], - 'contentInfo' => $expectedContentInfo, - 'versionNo' => $expectedContentInfo->currentVersionNo, - 'modificationDate' => $expectedContentInfo->modificationDate, - 'creationDate' => $expectedContentInfo->modificationDate, - 'creatorId' => $expectedContentInfo->ownerId, - ]), - ]); - - $this->assertInstanceOf(APILocation::class, $apiRootLocation); - $this->assertEquals($spiRootLocation->id, $apiRootLocation->id); - $this->assertEquals($expectedContentInfo->id, $apiRootLocation->getContentInfo()->id); - $this->assertEquals($expectedContent, $apiRootLocation->getContent()); - } - - /** - * @covers \eZ\Publish\Core\Repository\Mapper\ContentDomainMapper::buildLocationWithContent - */ - public function testBuildLocationWithContentThrowsInvalidArgumentException() - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$content\' is invalid: Location 2 has missing Content'); - - $nonRootLocation = new Location(['id' => 2, 'parentId' => 1]); - - $this->getContentDomainMapper()->buildLocationWithContent($nonRootLocation, null); - } - - public function testBuildLocationWithContentIsAlignedWithBuildLocation() - { - $spiRootLocation = new Location(['id' => 1, 'parentId' => 1]); - - $this->assertEquals( - $this->getContentDomainMapper()->buildLocationWithContent($spiRootLocation, null), - $this->getContentDomainMapper()->buildLocation($spiRootLocation) - ); - } - - public function providerForBuildVersionInfo() - { - $properties = [ - 'contentInfo' => new SPIContentInfo([ - 'contentTypeId' => self::EXAMPLE_CONTENT_TYPE_ID, - 'sectionId' => self::EXAMPLE_SECTION_ID, - 'mainLocationId' => self::EXAMPLE_MAIN_LOCATION_ID, - 'mainLanguageCode' => self::EXAMPLE_MAIN_LANGUAGE_CODE, - 'ownerId' => self::EXAMPLE_OWNER_ID, - ]), - 'creatorId' => self::EXAMPLE_CREATOR_ID, - 'initialLanguageCode' => self::EXAMPLE_INITIAL_LANGUAGE_CODE, - ]; - - return [ - [ - new SPIVersionInfo( - $properties + [ - 'status' => 44, - ] - ), - ], - [ - new SPIVersionInfo( - $properties + [ - 'status' => SPIVersionInfo::STATUS_DRAFT, - ] - ), - ], - [ - new SPIVersionInfo( - $properties + [ - 'status' => SPIVersionInfo::STATUS_PENDING, - ] - ), - ], - [ - new SPIVersionInfo( - $properties + [ - 'status' => SPIVersionInfo::STATUS_ARCHIVED, - 'languageCodes' => ['eng-GB', 'nor-NB', 'fre-FR'], - ] - ), - ], - [ - new SPIVersionInfo( - $properties + [ - 'status' => SPIVersionInfo::STATUS_PUBLISHED, - ] - ), - ], - ]; - } - - public function providerForBuildLocationDomainObjectsOnSearchResult() - { - $properties = [ - 'contentTypeId' => self::EXAMPLE_CONTENT_TYPE_ID, - 'sectionId' => self::EXAMPLE_SECTION_ID, - 'mainLocationId' => self::EXAMPLE_MAIN_LOCATION_ID, - 'mainLanguageCode' => self::EXAMPLE_MAIN_LANGUAGE_CODE, - 'ownerId' => self::EXAMPLE_OWNER_ID, - ]; - - $locationHits = [ - new Location(['id' => 21, 'contentId' => 32, 'parentId' => 1]), - new Location(['id' => 22, 'contentId' => 33, 'parentId' => 1]), - ]; - - return [ - [ - $locationHits, - [32, 33], - [], - [ - 32 => new ContentInfo($properties + ['id' => 32]), - 33 => new ContentInfo($properties + ['id' => 33]), - ], - 0, - ], - [ - $locationHits, - [32, 33], - ['languages' => ['eng-GB']], - [ - 32 => new ContentInfo($properties + ['id' => 32]), - ], - 1, - ], - [ - $locationHits, - [32, 33], - ['languages' => ['eng-GB']], - [], - 2, - ], - ]; - } - - /** - * @covers \eZ\Publish\Core\Repository\Mapper\ContentDomainMapper::buildLocationDomainObjectsOnSearchResult - * @dataProvider providerForBuildLocationDomainObjectsOnSearchResult - * - * @param array $locationHits - * @param array $contentIds - * @param array $languageFilter - * @param array $contentInfoList - * @param int $missing - */ - public function testBuildLocationDomainObjectsOnSearchResult( - array $locationHits, - array $contentIds, - array $languageFilter, - array $contentInfoList, - int $missing - ) { - $contentHandlerMock = $this->getContentHandlerMock(); - $contentHandlerMock - ->expects($this->once()) - ->method('loadContentInfoList') - ->with($contentIds) - ->willReturn($contentInfoList); - - $result = new SearchResult(['totalCount' => 10]); - foreach ($locationHits as $locationHit) { - $result->searchHits[] = new SearchHit(['valueObject' => $locationHit]); - } - - $spiResult = clone $result; - $missingLocations = $this->getContentDomainMapper()->buildLocationDomainObjectsOnSearchResult($result, $languageFilter); - $this->assertIsArray($missingLocations); - - if (!$missing) { - $this->assertEmpty($missingLocations); - } else { - $this->assertNotEmpty($missingLocations); - } - - $this->assertCount($missing, $missingLocations); - $this->assertEquals($spiResult->totalCount - $missing, $result->totalCount); - $this->assertCount(count($spiResult->searchHits) - $missing, $result->searchHits); - } - - /** - * Returns ContentDomainMapper. - * - * @return \eZ\Publish\Core\Repository\Mapper\ContentDomainMapper - */ - protected function getContentDomainMapper(): ContentDomainMapper - { - return new ContentDomainMapper( - $this->getContentHandlerMock(), - $this->getPersistenceMockHandler('Content\\Location\\Handler'), - $this->getTypeHandlerMock(), - $this->getContentTypeDomainMapperMock(), - $this->getLanguageHandlerMock(), - $this->getFieldTypeRegistryMock(), - $this->getThumbnailStrategy(), - $this->getLoggerMock(), - $this->getProxyFactoryMock() - ); - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Handler|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getContentHandlerMock() - { - return $this->getPersistenceMockHandler('Content\\Handler'); - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Language\Handler|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getLanguageHandlerMock() - { - return $this->getPersistenceMockHandler('Content\\Language\\Handler'); - } - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Type\Handler|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getTypeHandlerMock() - { - return $this->getPersistenceMockHandler('Content\\Type\\Handler'); - } - - protected function getProxyFactoryMock(): ProxyDomainMapperInterface - { - return $this->createMock(ProxyDomainMapperInterface::class); - } - - protected function getLoggerMock(): LoggerInterface - { - return $this->createMock(LoggerInterface::class); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/NameSchemaTest.php b/eZ/Publish/Core/Repository/Tests/Service/Mock/NameSchemaTest.php deleted file mode 100644 index 49c8ced339..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/NameSchemaTest.php +++ /dev/null @@ -1,395 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\TextLine\Value as TextLineValue; -use eZ\Publish\Core\Repository\Helper\NameSchemaService; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition; - -/** - * Mock Test case for NameSchema service. - */ -class NameSchemaTest extends BaseServiceMockTest -{ - /** - * Test eZ\Publish\Core\Repository\Helper\NameSchemaService method. - * - * @covers \eZ\Publish\Core\Repository\Helper\NameSchemaService::resolveUrlAliasSchema - */ - public function testResolveUrlAliasSchema() - { - $serviceMock = $this->getPartlyMockedNameSchemaService(['resolve']); - - $content = $this->buildTestContentObject(); - $contentType = $this->buildTestContentType(); - - $serviceMock->expects( - $this->once() - )->method( - 'resolve' - )->with( - '<urlalias_schema>', - $this->equalTo($contentType), - $this->equalTo($content->fields), - $this->equalTo($content->versionInfo->languageCodes) - )->will( - $this->returnValue(42) - ); - - $result = $serviceMock->resolveUrlAliasSchema($content, $contentType); - - self::assertEquals(42, $result); - } - - /** - * Test eZ\Publish\Core\Repository\Helper\NameSchemaService method. - * - * @covers \eZ\Publish\Core\Repository\Helper\NameSchemaService::resolveUrlAliasSchema - */ - public function testResolveUrlAliasSchemaFallbackToNameSchema() - { - $serviceMock = $this->getPartlyMockedNameSchemaService(['resolve']); - - $content = $this->buildTestContentObject(); - $contentType = $this->buildTestContentType('<name_schema>', ''); - - $serviceMock->expects( - $this->once() - )->method( - 'resolve' - )->with( - '<name_schema>', - $this->equalTo($contentType), - $this->equalTo($content->fields), - $this->equalTo($content->versionInfo->languageCodes) - )->will( - $this->returnValue(42) - ); - - $result = $serviceMock->resolveUrlAliasSchema($content, $contentType); - - self::assertEquals(42, $result); - } - - /** - * Test eZ\Publish\Core\Repository\Helper\NameSchemaService method. - * - * @covers \eZ\Publish\Core\Repository\Helper\NameSchemaService::resolveNameSchema - */ - public function testResolveNameSchema() - { - $serviceMock = $this->getPartlyMockedNameSchemaService(['resolve']); - - $content = $this->buildTestContentObject(); - $contentType = $this->buildTestContentType(); - - $serviceMock->expects( - $this->once() - )->method( - 'resolve' - )->with( - '<name_schema>', - $this->equalTo($contentType), - $this->equalTo($content->fields), - $this->equalTo($content->versionInfo->languageCodes) - )->will( - $this->returnValue(42) - ); - - $result = $serviceMock->resolveNameSchema($content, [], [], $contentType); - - self::assertEquals(42, $result); - } - - /** - * Test eZ\Publish\Core\Repository\Helper\NameSchemaService method. - * - * @covers \eZ\Publish\Core\Repository\Helper\NameSchemaService::resolveNameSchema - */ - public function testResolveNameSchemaWithFields() - { - $serviceMock = $this->getPartlyMockedNameSchemaService(['resolve']); - - $content = $this->buildTestContentObject(); - $contentType = $this->buildTestContentType(); - - $fields = []; - $fields['text3']['cro-HR'] = new TextLineValue('tri'); - $fields['text1']['ger-DE'] = new TextLineValue('ein'); - $fields['text2']['ger-DE'] = new TextLineValue('zwei'); - $fields['text3']['ger-DE'] = new TextLineValue('drei'); - $mergedFields = $fields; - $mergedFields['text1']['cro-HR'] = new TextLineValue('jedan'); - $mergedFields['text2']['cro-HR'] = new TextLineValue('dva'); - $mergedFields['text1']['eng-GB'] = new TextLineValue('one'); - $mergedFields['text2']['eng-GB'] = new TextLineValue('two'); - $mergedFields['text3']['eng-GB'] = new TextLineValue(''); - $languages = ['eng-GB', 'cro-HR', 'ger-DE']; - - $serviceMock->expects( - $this->once() - )->method( - 'resolve' - )->with( - '<name_schema>', - $this->equalTo($contentType), - $this->equalTo($mergedFields), - $this->equalTo($languages) - )->will( - $this->returnValue(42) - ); - - $result = $serviceMock->resolveNameSchema($content, $fields, $languages, $contentType); - - self::assertEquals(42, $result); - } - - /** - * Test eZ\Publish\Core\Repository\Helper\NameSchemaService::resolve method. - * - * @covers \eZ\Publish\Core\Repository\Helper\NameSchemaService::resolve - * @dataProvider \eZ\Publish\Core\Repository\Tests\Service\Mock\NameSchemaTest::resolveDataProvider - * - * @param string[] $schemaIdentifiers - * @param string $nameSchema - * @param string[] $languageFieldValues field value translations - * @param string[] $fieldTitles [language => [field_identifier => title]] - * @param array $settings NameSchemaService settings - */ - public function testResolve( - array $schemaIdentifiers, - $nameSchema, - $languageFieldValues, - $fieldTitles, - $settings = [] - ) { - $serviceMock = $this->getPartlyMockedNameSchemaService(['getFieldTitles'], $settings); - - $content = $this->buildTestContentObject(); - $contentType = $this->buildTestContentType(); - - $index = 0; - foreach ($languageFieldValues as $languageCode => $fieldValue) { - $serviceMock->expects( - $this->at($index++) - )->method( - 'getFieldTitles' - )->with( - $schemaIdentifiers, - $contentType, - $content->fields, - $languageCode - )->will( - $this->returnValue($fieldTitles[$languageCode]) - ); - } - - $result = $serviceMock->resolve($nameSchema, $contentType, $content->fields, $content->versionInfo->languageCodes); - - self::assertEquals($languageFieldValues, $result); - } - - /** - * Data provider for the @see testResolve method. - * - * @return array - */ - public function resolveDataProvider() - { - return [ - [ - ['text1'], - '<text1>', - [ - 'eng-GB' => 'one', - 'cro-HR' => 'jedan', - ], - [ - 'eng-GB' => ['text1' => 'one'], - 'cro-HR' => ['text1' => 'jedan'], - ], - ], - [ - ['text2'], - '<text2>', - [ - 'eng-GB' => 'two', - 'cro-HR' => 'dva', - ], - [ - 'eng-GB' => ['text2' => 'two'], - 'cro-HR' => ['text2' => 'dva'], - ], - ], - [ - ['text1', 'text2'], - 'Hello, <text1> and <text2> and then goodbye and hello again', - [ - 'eng-GB' => 'Hello, one and two and then goodbye...', - 'cro-HR' => 'Hello, jedan and dva and then goodb...', - ], - [ - 'eng-GB' => ['text1' => 'one', 'text2' => 'two'], - 'cro-HR' => ['text1' => 'jedan', 'text2' => 'dva'], - ], - [ - 'limit' => 38, - 'sequence' => '...', - ], - ], - ]; - } - - /** - * @return \eZ\Publish\API\Repository\Values\Content\Field[] - */ - protected function getFields() - { - return [ - new Field( - [ - 'languageCode' => 'eng-GB', - 'fieldDefIdentifier' => 'text1', - 'value' => new TextLineValue('one'), - ] - ), - new Field( - [ - 'languageCode' => 'eng-GB', - 'fieldDefIdentifier' => 'text2', - 'value' => new TextLineValue('two'), - ] - ), - new Field( - [ - 'languageCode' => 'eng-GB', - 'fieldDefIdentifier' => 'text3', - 'value' => new TextLineValue(''), - ] - ), - new Field( - [ - 'languageCode' => 'cro-HR', - 'fieldDefIdentifier' => 'text1', - 'value' => new TextLineValue('jedan'), - ] - ), - new Field( - [ - 'languageCode' => 'cro-HR', - 'fieldDefIdentifier' => 'text2', - 'value' => new TextLineValue('dva'), - ] - ), - new Field( - [ - 'languageCode' => 'cro-HR', - 'fieldDefIdentifier' => 'text3', - 'value' => new TextLineValue(''), - ] - ), - ]; - } - - /** - * @return \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition[] - */ - protected function getFieldDefinitions() - { - return [ - new FieldDefinition( - [ - 'id' => '1', - 'identifier' => 'text1', - 'fieldTypeIdentifier' => 'ezstring', - ] - ), - new FieldDefinition( - [ - 'id' => '2', - 'identifier' => 'text2', - 'fieldTypeIdentifier' => 'ezstring', - ] - ), - new FieldDefinition( - [ - 'id' => '3', - 'identifier' => 'text3', - 'fieldTypeIdentifier' => 'ezstring', - ] - ), - ]; - } - - /** - * Build Content Object stub for testing purpose. - * - * @return \eZ\Publish\API\Repository\Values\Content\Content - */ - protected function buildTestContentObject() - { - return new Content( - [ - 'internalFields' => $this->getFields(), - 'versionInfo' => new VersionInfo( - [ - 'languageCodes' => ['eng-GB', 'cro-HR'], - ] - ), - ] - ); - } - - /** - * Build ContentType stub for testing purpose. - * - * @param string $nameSchema - * @param string $urlAliasSchema - * - * @return \eZ\Publish\Core\Repository\Values\ContentType\ContentType - */ - protected function buildTestContentType($nameSchema = '<name_schema>', $urlAliasSchema = '<urlalias_schema>') - { - return new ContentType( - [ - 'nameSchema' => $nameSchema, - 'urlAliasSchema' => $urlAliasSchema, - 'fieldDefinitions' => $this->getFieldDefinitions(), - ] - ); - } - - /** - * Returns the content service to test with $methods mocked. - * - * Injected Repository comes from {@see getRepositoryMock()} - * - * @param string[] $methods - * @param array $settings - * - * @return \eZ\Publish\Core\Repository\Helper\NameSchemaService|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getPartlyMockedNameSchemaService(array $methods = null, array $settings = []) - { - return $this->getMockBuilder(NameSchemaService::class) - ->setMethods($methods) - ->setConstructorArgs( - [ - $this->getPersistenceMock()->contentTypeHandler(), - $this->getContentTypeDomainMapperMock(), - $this->getFieldTypeRegistryMock(), - $settings, - ] - ) - ->getMock(); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/RepositoryTest.php b/eZ/Publish/Core/Repository/Tests/Service/Mock/RepositoryTest.php deleted file mode 100644 index 44f0097add..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/RepositoryTest.php +++ /dev/null @@ -1,118 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; - -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; - -/** - * Mock test case for Repository. - */ -class RepositoryTest extends BaseServiceMockTest -{ - /** - * Test for the beginTransaction() method. - * - * @covers \eZ\Publish\API\Repository\Repository::beginTransaction - */ - public function testBeginTransaction() - { - $mockedRepository = $this->getRepository(); - $persistenceHandlerMock = $this->getPersistenceMock(); - - $persistenceHandlerMock->expects( - $this->once() - )->method( - 'beginTransaction' - ); - - $mockedRepository->beginTransaction(); - } - - /** - * Test for the commit() method. - * - * @covers \eZ\Publish\API\Repository\Repository::commit - */ - public function testCommit() - { - $mockedRepository = $this->getRepository(); - $persistenceHandlerMock = $this->getPersistenceMock(); - - $persistenceHandlerMock->expects( - $this->once() - )->method( - 'commit' - ); - - $mockedRepository->commit(); - } - - /** - * Test for the commit() method. - * - * @covers \eZ\Publish\API\Repository\Repository::commit - */ - public function testCommitThrowsRuntimeException() - { - $this->expectException(\RuntimeException::class); - - $mockedRepository = $this->getRepository(); - $persistenceHandlerMock = $this->getPersistenceMock(); - - $persistenceHandlerMock->expects( - $this->once() - )->method( - 'commit' - )->will( - $this->throwException(new \Exception()) - ); - - $mockedRepository->commit(); - } - - /** - * Test for the rollback() method. - * - * @covers \eZ\Publish\API\Repository\Repository::rollback - */ - public function testRollback() - { - $mockedRepository = $this->getRepository(); - $persistenceHandlerMock = $this->getPersistenceMock(); - - $persistenceHandlerMock->expects( - $this->once() - )->method( - 'rollback' - ); - - $mockedRepository->rollback(); - } - - /** - * Test for the rollback() method. - * - * @covers \eZ\Publish\API\Repository\Repository::rollback - */ - public function testRollbackThrowsRuntimeException() - { - $this->expectException(\RuntimeException::class); - - $mockedRepository = $this->getRepository(); - $persistenceHandlerMock = $this->getPersistenceMock(); - - $persistenceHandlerMock->expects( - $this->once() - )->method( - 'rollback' - )->will( - $this->throwException(new \Exception()) - ); - - $mockedRepository->rollback(); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/RoleTest.php b/eZ/Publish/Core/Repository/Tests/Service/Mock/RoleTest.php deleted file mode 100644 index e5134b2ba0..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/RoleTest.php +++ /dev/null @@ -1,1148 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; - -use ArrayIterator; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation; -use eZ\Publish\API\Repository\Values\User\PolicyCreateStruct; -use eZ\Publish\API\Repository\Values\User\PolicyDraft; -use eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct; -use eZ\Publish\API\Repository\Values\User\Role; -use eZ\Publish\API\Repository\Values\User\RoleCreateStruct; -use eZ\Publish\API\Repository\Values\User\RoleDraft; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Repository\Mapper\RoleDomainMapper; -use eZ\Publish\Core\Repository\Permission\LimitationService; -use eZ\Publish\Core\Repository\RoleService; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\SPI\Limitation\Type as SPIType; -use eZ\Publish\SPI\Persistence\User as SPIUser; -use eZ\Publish\SPI\Persistence\User\Role as SPIRole; - -/** - * Mock test case for Role service. - */ -class RoleTest extends BaseServiceMockTest -{ - /** - * Test for the createRole() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::createRole - * @covers \eZ\Publish\Core\Repository\RoleService::validateRoleCreateStruct - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitations - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitation - */ - public function testCreateRoleThrowsLimitationValidationException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\LimitationValidationException::class); - - $limitationMock = $this->createMock(RoleLimitation::class); - $limitationTypeMock = $this->createMock(SPIType::class); - - $limitationMock->expects($this->any()) - ->method('getIdentifier') - ->will($this->returnValue('mockIdentifier')); - - $limitationTypeMock->expects($this->once()) - ->method('acceptValue') - ->with($this->equalTo($limitationMock)); - $limitationTypeMock->expects($this->once()) - ->method('validate') - ->with($this->equalTo($limitationMock)) - ->will($this->returnValue([42])); - - $settings = [ - 'policyMap' => ['mockModule' => ['mockFunction' => ['mockIdentifier' => true]]], - 'limitationTypes' => ['mockIdentifier' => $limitationTypeMock], - ]; - - $roleServiceMock = $this->getPartlyMockedRoleService(['loadRoleByIdentifier'], $settings); - - /** @var \eZ\Publish\API\Repository\Values\User\RoleCreateStruct $roleCreateStructMock */ - $roleCreateStructMock = $this->createMock(RoleCreateStruct::class); - $policyCreateStructMock = $this->createMock(PolicyCreateStruct::class); - - /* @var \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct $policyCreateStructMock */ - $policyCreateStructMock->module = 'mockModule'; - $policyCreateStructMock->function = 'mockFunction'; - $roleCreateStructMock->identifier = 'mockIdentifier'; - $roleServiceMock->expects($this->once()) - ->method('loadRoleByIdentifier') - ->with($this->equalTo('mockIdentifier')) - ->will($this->throwException(new NotFoundException('Role', 'mockIdentifier'))); - - /* @var \PHPUnit\Framework\MockObject\MockObject $roleCreateStructMock */ - $roleCreateStructMock->expects($this->once()) - ->method('getPolicies') - ->will($this->returnValue([$policyCreateStructMock])); - - /* @var \PHPUnit\Framework\MockObject\MockObject $policyCreateStructMock */ - $policyCreateStructMock->expects($this->once()) - ->method('getLimitations') - ->will($this->returnValue([$limitationMock])); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('create'), - $this->equalTo($roleCreateStructMock) - )->will($this->returnValue(true)); - - /* @var \eZ\Publish\API\Repository\Values\User\RoleCreateStruct $roleCreateStructMock */ - $roleServiceMock->createRole($roleCreateStructMock); - } - - /** - * Test for the addPolicy() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::addPolicyByRoleDraft - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitations - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitation - */ - public function testAddPolicyThrowsLimitationValidationException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\LimitationValidationException::class); - - $limitationMock = $this->createMock(RoleLimitation::class); - $limitationTypeMock = $this->createMock(SPIType::class); - - $limitationTypeMock->expects($this->once()) - ->method('acceptValue') - ->with($this->equalTo($limitationMock)); - $limitationTypeMock->expects($this->once()) - ->method('validate') - ->with($this->equalTo($limitationMock)) - ->will($this->returnValue([42])); - - $limitationMock->expects($this->any()) - ->method('getIdentifier') - ->will($this->returnValue('mockIdentifier')); - - $settings = [ - 'policyMap' => ['mockModule' => ['mockFunction' => ['mockIdentifier' => true]]], - 'limitationTypes' => ['mockIdentifier' => $limitationTypeMock], - ]; - - $roleServiceMock = $this->getPartlyMockedRoleService(['loadRoleDraft'], $settings); - - $roleDraftMock = $this->createMock(RoleDraft::class); - $policyCreateStructMock = $this->createMock(PolicyCreateStruct::class); - - $roleDraftMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(42)); - /* @var \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct $policyCreateStructMock */ - $policyCreateStructMock->module = 'mockModule'; - $policyCreateStructMock->function = 'mockFunction'; - - $roleServiceMock->expects($this->once()) - ->method('loadRoleDraft') - ->with($this->equalTo(42)) - ->will($this->returnValue($roleDraftMock)); - - /* @var \PHPUnit\Framework\MockObject\MockObject $policyCreateStructMock */ - $policyCreateStructMock->expects($this->once()) - ->method('getLimitations') - ->will($this->returnValue([$limitationMock])); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('update'), - $this->equalTo($roleDraftMock) - )->will($this->returnValue(true)); - - /* @var \eZ\Publish\API\Repository\Values\User\Role $roleDraftMock */ - /* @var \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct $policyCreateStructMock */ - $roleServiceMock->addPolicyByRoleDraft($roleDraftMock, $policyCreateStructMock); - } - - /** - * Test for the updatePolicyByRoleDraft() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::updatePolicyByRoleDraft - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitations - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitation - */ - public function testUpdatePolicyThrowsLimitationValidationException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\LimitationValidationException::class); - - $limitationMock = $this->createMock(RoleLimitation::class); - $limitationTypeMock = $this->createMock(SPIType::class); - - $limitationTypeMock->expects($this->once()) - ->method('acceptValue') - ->with($this->equalTo($limitationMock)); - $limitationTypeMock->expects($this->once()) - ->method('validate') - ->with($this->equalTo($limitationMock)) - ->will($this->returnValue([42])); - - $limitationMock->expects($this->any()) - ->method('getIdentifier') - ->will($this->returnValue('mockIdentifier')); - - $settings = [ - 'policyMap' => ['mockModule' => ['mockFunction' => ['mockIdentifier' => true]]], - 'limitationTypes' => ['mockIdentifier' => $limitationTypeMock], - ]; - - $roleServiceMock = $this->getPartlyMockedRoleService(['loadRole'], $settings); - - $roleDraftMock = $this->createMock(RoleDraft::class); - $policyDraftMock = $this->createMock(PolicyDraft::class); - $policyUpdateStructMock = $this->createMock(PolicyUpdateStruct::class); - - $policyDraftMock->expects($this->any()) - ->method('__get') - ->will( - $this->returnCallback( - static function ($propertyName) { - switch ($propertyName) { - case 'module': - return 'mockModule'; - case 'function': - return 'mockFunction'; - } - - return null; - } - ) - ); - - /* @var \PHPUnit\Framework\MockObject\MockObject $policyCreateStructMock */ - $policyUpdateStructMock->expects($this->once()) - ->method('getLimitations') - ->will($this->returnValue([$limitationMock])); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('update'), - $this->equalTo($roleDraftMock) - )->will($this->returnValue(true)); - - /* @var \eZ\Publish\API\Repository\Values\User\Policy $policyDraftMock */ - /* @var \eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct $policyUpdateStructMock */ - $roleServiceMock->updatePolicyByRoleDraft( - $roleDraftMock, - $policyDraftMock, - $policyUpdateStructMock - ); - } - - /** - * Test for the assignRoleToUser() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::assignRoleToUser - */ - public function testAssignRoleToUserThrowsUnauthorizedException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); - - $roleServiceMock = $this->getPartlyMockedRoleService(); - /** @var \eZ\Publish\API\Repository\Values\User\Role $roleMock */ - $roleMock = $this->createMock(Role::class); - /** @var \eZ\Publish\API\Repository\Values\User\User $userMock */ - $userMock = $this->createMock(User::class); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('assign'), - $this->equalTo($userMock), - $this->equalTo([$roleMock]) - )->will($this->returnValue(false)); - - $roleServiceMock->assignRoleToUser($roleMock, $userMock, null); - } - - /** - * Test for the assignRoleToUser() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::assignRoleToUser - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitation - */ - public function testAssignRoleToUserThrowsLimitationValidationException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\LimitationValidationException::class); - - $limitationMock = $this->createMock(RoleLimitation::class); - $limitationTypeMock = $this->createMock(SPIType::class); - - $limitationTypeMock->expects($this->once()) - ->method('acceptValue') - ->with($this->equalTo($limitationMock)); - $limitationTypeMock->expects($this->once()) - ->method('validate') - ->with($this->equalTo($limitationMock)) - ->will($this->returnValue([42])); - - $limitationMock->expects($this->once()) - ->method('getIdentifier') - ->will($this->returnValue('testIdentifier')); - - $settings = [ - 'limitationTypes' => ['testIdentifier' => $limitationTypeMock], - ]; - - $roleServiceMock = $this->getPartlyMockedRoleService(null, $settings); - - /** @var \eZ\Publish\API\Repository\Values\User\Role $roleMock */ - $roleMock = $this->createMock(Role::class); - /** @var \eZ\Publish\API\Repository\Values\User\User $userMock */ - $userMock = $this->createMock(User::class); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('assign'), - $this->equalTo($userMock), - $this->equalTo([$roleMock]) - )->will($this->returnValue(true)); - - /* @var \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation $limitationMock */ - $roleServiceMock->assignRoleToUser($roleMock, $userMock, $limitationMock); - } - - /** - * Test for the assignRoleToUser() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::assignRoleToUser - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitation - */ - public function testAssignRoleToUserThrowsBadStateException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class); - - $roleServiceMock = $this->getPartlyMockedRoleService(); - /** @var \eZ\Publish\API\Repository\Values\User\Role $roleMock */ - $roleMock = $this->createMock(Role::class); - /** @var \eZ\Publish\API\Repository\Values\User\User $userMock */ - $userMock = $this->createMock(User::class); - $limitationMock = $this->createMock(RoleLimitation::class); - - $limitationMock->expects($this->once()) - ->method('getIdentifier') - ->will($this->returnValue('testIdentifier')); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('assign'), - $this->equalTo($userMock), - $this->equalTo([$roleMock]) - )->will($this->returnValue(true)); - - /* @var \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation $limitationMock */ - $roleServiceMock->assignRoleToUser($roleMock, $userMock, $limitationMock); - } - - /** - * Test for the assignRoleToUser() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::assignRoleToUser - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitation - */ - public function testAssignRoleToUser() - { - $limitationMock = $this->createMock(RoleLimitation::class); - $limitationTypeMock = $this->createMock(SPIType::class); - - $limitationTypeMock->expects($this->once()) - ->method('acceptValue') - ->with($this->equalTo($limitationMock)); - $limitationTypeMock->expects($this->once()) - ->method('validate') - ->with($this->equalTo($limitationMock)) - ->will($this->returnValue([])); - - $limitationMock->expects($this->exactly(2)) - ->method('getIdentifier') - ->will($this->returnValue('testIdentifier')); - - $settings = [ - 'limitationTypes' => ['testIdentifier' => $limitationTypeMock], - ]; - - $roleServiceMock = $this->getPartlyMockedRoleService(['checkAssignmentAndFilterLimitationValues'], $settings); - - $repository = $this->getRepositoryMock(); - $roleMock = $this->createMock(Role::class); - $userMock = $this->createMock(User::class); - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - - $userMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(24)); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('assign'), - $this->equalTo($userMock), - $this->equalTo([$roleMock]) - )->will($this->returnValue(true)); - - $roleMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(42)); - - $userHandlerMock->expects($this->once()) - ->method('loadRole') - ->with($this->equalTo(42)) - ->will($this->returnValue(new SPIRole(['id' => 42]))); - - $userHandlerMock->expects($this->once()) - ->method('load') - ->with($this->equalTo(24)) - ->will($this->returnValue(new SPIUser(['id' => 24]))); - - $roleServiceMock->expects($this->once()) - ->method('checkAssignmentAndFilterLimitationValues') - ->with(24, $this->isInstanceOf(SPIRole::class), ['testIdentifier' => []]) - ->will($this->returnValue(['testIdentifier' => []])); - - $repository->expects($this->once())->method('beginTransaction'); - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - $userHandlerMock->expects($this->once()) - ->method('assignRole') - ->with( - $this->equalTo(24), - $this->equalTo(42), - $this->equalTo(['testIdentifier' => []]) - ); - $repository->expects($this->once())->method('commit'); - - /* @var \eZ\Publish\API\Repository\Values\User\Role $roleMock */ - /* @var \eZ\Publish\API\Repository\Values\User\User $userMock */ - /* @var \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation $limitationMock */ - $roleServiceMock->assignRoleToUser($roleMock, $userMock, $limitationMock); - } - - /** - * Test for the assignRoleToUser() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::assignRoleToUser - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitation - */ - public function testAssignRoleToUserWithNullLimitation() - { - $repository = $this->getRepositoryMock(); - $roleServiceMock = $this->getPartlyMockedRoleService(['checkAssignmentAndFilterLimitationValues']); - $roleMock = $this->createMock(Role::class); - $userMock = $this->createMock(User::class); - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - - $userMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(24)); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('assign'), - $this->equalTo($userMock), - $this->equalTo([$roleMock]) - )->will($this->returnValue(true)); - - $roleMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(42)); - - $userHandlerMock->expects($this->once()) - ->method('loadRole') - ->with($this->equalTo(42)) - ->will($this->returnValue(new SPIRole(['id' => 42]))); - - $userHandlerMock->expects($this->once()) - ->method('load') - ->with($this->equalTo(24)) - ->will($this->returnValue(new SPIUser(['id' => 24]))); - - $roleServiceMock->expects($this->once()) - ->method('checkAssignmentAndFilterLimitationValues') - ->with(24, $this->isInstanceOf(SPIRole::class), null) - ->will($this->returnValue(null)); - - $repository->expects($this->once())->method('beginTransaction'); - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - $userHandlerMock->expects($this->once()) - ->method('assignRole') - ->with( - $this->equalTo(24), - $this->equalTo(42), - $this->equalTo(null) - ); - $repository->expects($this->once())->method('commit'); - - /* @var \eZ\Publish\API\Repository\Values\User\Role $roleMock */ - /* @var \eZ\Publish\API\Repository\Values\User\User $userMock */ - $roleServiceMock->assignRoleToUser($roleMock, $userMock, null); - } - - /** - * Test for the assignRoleToUser() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::assignRoleToUser - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitation - */ - public function testAssignRoleToUserWithRollback() - { - $this->expectException(\Exception::class); - - $repository = $this->getRepositoryMock(); - $roleServiceMock = $this->getPartlyMockedRoleService(['checkAssignmentAndFilterLimitationValues']); - $roleMock = $this->createMock(Role::class); - $userMock = $this->createMock(User::class); - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - - $userMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(24)); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('assign'), - $this->equalTo($userMock), - $this->equalTo([$roleMock]) - )->will($this->returnValue(true)); - - $roleMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(42)); - - $userHandlerMock->expects($this->once()) - ->method('loadRole') - ->with($this->equalTo(42)) - ->will($this->returnValue(new SPIRole(['id' => 42]))); - - $userHandlerMock->expects($this->once()) - ->method('load') - ->with($this->equalTo(24)) - ->will($this->returnValue(new SPIUser(['id' => 24]))); - - $roleServiceMock->expects($this->once()) - ->method('checkAssignmentAndFilterLimitationValues') - ->with(24, $this->isInstanceOf(SPIRole::class), null) - ->will($this->returnValue(null)); - - $repository->expects($this->once())->method('beginTransaction'); - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - $userHandlerMock->expects($this->once()) - ->method('assignRole') - ->with( - $this->equalTo(24), - $this->equalTo(42), - $this->equalTo(null) - )->will($this->throwException(new \Exception())); - $repository->expects($this->once())->method('rollback'); - - /* @var \eZ\Publish\API\Repository\Values\User\Role $roleMock */ - /* @var \eZ\Publish\API\Repository\Values\User\User $userMock */ - $roleServiceMock->assignRoleToUser($roleMock, $userMock, null); - } - - /** - * Test for the assignRoleToUserGroup() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::assignRoleToUserGroup - */ - public function testAssignRoleToUserGroupThrowsUnauthorizedException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); - - $repository = $this->getRepositoryMock(); - $roleServiceMock = $this->getPartlyMockedRoleService(); - /** @var \eZ\Publish\API\Repository\Values\User\Role $roleMock */ - $roleMock = $this->createMock(Role::class); - /** @var \eZ\Publish\API\Repository\Values\User\UserGroup $userGroupMock */ - $userGroupMock = $this->createMock(UserGroup::class); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('assign'), - $this->equalTo($userGroupMock), - $this->equalTo([$roleMock]) - )->will($this->returnValue(false)); - - $roleServiceMock->assignRoleToUserGroup($roleMock, $userGroupMock, null); - } - - /** - * Test for the assignRoleToUserGroup() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::assignRoleToUserGroup - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitation - */ - public function testAssignRoleToUserGroupThrowsLimitationValidationException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\LimitationValidationException::class); - - $limitationMock = $this->createMock(RoleLimitation::class); - $limitationTypeMock = $this->createMock(SPIType::class); - - $limitationTypeMock->expects($this->once()) - ->method('acceptValue') - ->with($this->equalTo($limitationMock)); - $limitationTypeMock->expects($this->once()) - ->method('validate') - ->with($this->equalTo($limitationMock)) - ->will($this->returnValue([42])); - - $limitationMock->expects($this->once()) - ->method('getIdentifier') - ->will($this->returnValue('testIdentifier')); - - $settings = [ - 'limitationTypes' => ['testIdentifier' => $limitationTypeMock], - ]; - - $roleServiceMock = $this->getPartlyMockedRoleService(null, $settings); - - $repository = $this->getRepositoryMock(); - /** @var \eZ\Publish\API\Repository\Values\User\Role $roleMock */ - $roleMock = $this->createMock(Role::class); - /** @var \eZ\Publish\API\Repository\Values\User\UserGroup $userGroupMock */ - $userGroupMock = $this->createMock(UserGroup::class); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('assign'), - $this->equalTo($userGroupMock), - $this->equalTo([$roleMock]) - )->will($this->returnValue(true)); - - /* @var \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation $limitationMock */ - $roleServiceMock->assignRoleToUserGroup($roleMock, $userGroupMock, $limitationMock); - } - - /** - * Test for the assignRoleToUserGroup() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::assignRoleToUserGroup - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitation - */ - public function testAssignRoleGroupToUserThrowsBadStateException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class); - - $repository = $this->getRepositoryMock(); - $roleServiceMock = $this->getPartlyMockedRoleService(); - /** @var \eZ\Publish\API\Repository\Values\User\Role $roleMock */ - $roleMock = $this->createMock(Role::class); - /** @var \eZ\Publish\API\Repository\Values\User\UserGroup $userGroupMock */ - $userGroupMock = $this->createMock(UserGroup::class); - $limitationMock = $this->createMock(RoleLimitation::class); - - $limitationMock->expects($this->once()) - ->method('getIdentifier') - ->will($this->returnValue('testIdentifier')); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('assign'), - $this->equalTo($userGroupMock), - $this->equalTo([$roleMock]) - )->will($this->returnValue(true)); - - /* @var \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation $limitationMock */ - $roleServiceMock->assignRoleToUserGroup($roleMock, $userGroupMock, $limitationMock); - } - - /** - * Test for the assignRoleToUserGroup() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::assignRoleToUserGroup - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitation - */ - public function testAssignRoleToUserGroup() - { - $limitationMock = $this->createMock(RoleLimitation::class); - $limitationTypeMock = $this->createMock(SPIType::class); - - $limitationTypeMock->expects($this->once()) - ->method('acceptValue') - ->with($this->equalTo($limitationMock)); - $limitationTypeMock->expects($this->once()) - ->method('validate') - ->with($this->equalTo($limitationMock)) - ->will($this->returnValue([])); - - $limitationMock->expects($this->exactly(2)) - ->method('getIdentifier') - ->will($this->returnValue('testIdentifier')); - - $settings = [ - 'limitationTypes' => ['testIdentifier' => $limitationTypeMock], - ]; - - $roleServiceMock = $this->getPartlyMockedRoleService(['checkAssignmentAndFilterLimitationValues'], $settings); - - $repository = $this->getRepositoryMock(); - $roleMock = $this->createMock(Role::class); - $userGroupMock = $this->createMock(UserGroup::class); - $userServiceMock = $this->createMock(UserService::class); - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - - $repository->expects($this->once()) - ->method('getUserService') - ->will($this->returnValue($userServiceMock)); - $userGroupMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(24)); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('assign'), - $this->equalTo($userGroupMock), - $this->equalTo([$roleMock]) - )->will($this->returnValue(true)); - - $roleMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(42)); - - $userHandlerMock->expects($this->once()) - ->method('loadRole') - ->with($this->equalTo(42)) - ->will($this->returnValue(new SPIRole(['id' => 42]))); - - $userServiceMock->expects($this->once()) - ->method('loadUserGroup') - ->with($this->equalTo(24)) - ->will($this->returnValue($userGroupMock)); - - $roleServiceMock->expects($this->once()) - ->method('checkAssignmentAndFilterLimitationValues') - ->with(24, $this->isInstanceOf(SPIRole::class), ['testIdentifier' => []]) - ->will($this->returnValue(['testIdentifier' => []])); - - $repository->expects($this->once())->method('beginTransaction'); - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - $userHandlerMock->expects($this->once()) - ->method('assignRole') - ->with( - $this->equalTo(24), - $this->equalTo(42), - $this->equalTo(['testIdentifier' => []]) - ); - $repository->expects($this->once())->method('commit'); - - /* @var \eZ\Publish\API\Repository\Values\User\Role $roleMock */ - /* @var \eZ\Publish\API\Repository\Values\User\UserGroup $userGroupMock */ - /* @var \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation $limitationMock */ - $roleServiceMock->assignRoleToUserGroup($roleMock, $userGroupMock, $limitationMock); - } - - /** - * Test for the assignRoleToUserGroup() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::assignRoleToUserGroup - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitation - */ - public function testAssignRoleToUserGroupWithNullLimitation() - { - $repository = $this->getRepositoryMock(); - $roleServiceMock = $this->getPartlyMockedRoleService(['checkAssignmentAndFilterLimitationValues']); - $roleMock = $this->createMock(Role::class); - $userGroupMock = $this->createMock(UserGroup::class); - $userServiceMock = $this->createMock(UserService::class); - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - - $repository->expects($this->once()) - ->method('getUserService') - ->will($this->returnValue($userServiceMock)); - $userGroupMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(24)); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('assign'), - $this->equalTo($userGroupMock), - $this->equalTo([$roleMock]) - )->will($this->returnValue(true)); - - $roleMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(42)); - - $userHandlerMock->expects($this->once()) - ->method('loadRole') - ->with($this->equalTo(42)) - ->will($this->returnValue(new SPIRole(['id' => 42]))); - - $userServiceMock->expects($this->once()) - ->method('loadUserGroup') - ->with($this->equalTo(24)) - ->will($this->returnValue($userGroupMock)); - - $roleServiceMock->expects($this->once()) - ->method('checkAssignmentAndFilterLimitationValues') - ->with(24, $this->isInstanceOf(SPIRole::class), null) - ->will($this->returnValue(null)); - - $repository->expects($this->once())->method('beginTransaction'); - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - $userHandlerMock->expects($this->once()) - ->method('assignRole') - ->with( - $this->equalTo(24), - $this->equalTo(42), - $this->equalTo(null) - ); - $repository->expects($this->once())->method('commit'); - - /* @var \eZ\Publish\API\Repository\Values\User\Role $roleMock */ - /* @var \eZ\Publish\API\Repository\Values\User\UserGroup $userGroupMock */ - $roleServiceMock->assignRoleToUserGroup($roleMock, $userGroupMock, null); - } - - /** - * Test for the assignRoleToUserGroup() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::assignRoleToUserGroup - * @covers \eZ\Publish\Core\Repository\Permission\LimitationService::validateLimitation - */ - public function testAssignRoleToUserGroupWithRollback() - { - $this->expectException(\Exception::class); - - $repository = $this->getRepositoryMock(); - $roleServiceMock = $this->getPartlyMockedRoleService(['checkAssignmentAndFilterLimitationValues']); - $roleMock = $this->createMock(Role::class); - $userGroupMock = $this->createMock(UserGroup::class); - $userServiceMock = $this->createMock(UserService::class); - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - - $repository->expects($this->once()) - ->method('getUserService') - ->will($this->returnValue($userServiceMock)); - $userGroupMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(24)); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('assign'), - $this->equalTo($userGroupMock), - $this->equalTo([$roleMock]) - )->will($this->returnValue(true)); - - $roleMock->expects($this->any()) - ->method('__get') - ->with('id') - ->will($this->returnValue(42)); - - $userHandlerMock->expects($this->once()) - ->method('loadRole') - ->with($this->equalTo(42)) - ->will($this->returnValue(new SPIRole(['id' => 42]))); - - $userServiceMock->expects($this->once()) - ->method('loadUserGroup') - ->with($this->equalTo(24)) - ->will($this->returnValue($userGroupMock)); - - $roleServiceMock->expects($this->once()) - ->method('checkAssignmentAndFilterLimitationValues') - ->with(24, $this->isInstanceOf(SPIRole::class), null) - ->will($this->returnValue(null)); - - $repository->expects($this->once())->method('beginTransaction'); - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - $userHandlerMock->expects($this->once()) - ->method('assignRole') - ->with( - $this->equalTo(24), - $this->equalTo(42), - $this->equalTo(null) - )->will($this->throwException(new \Exception())); - $repository->expects($this->once())->method('rollback'); - - /* @var \eZ\Publish\API\Repository\Values\User\Role $roleMock */ - /* @var \eZ\Publish\API\Repository\Values\User\UserGroup $userGroupMock */ - $roleServiceMock->assignRoleToUserGroup($roleMock, $userGroupMock, null); - } - - /** - * @covers \eZ\Publish\Core\Repository\RoleService::removePolicyByRoleDraft - */ - public function testRemovePolicyByRoleDraftThrowsUnauthorizedException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); - - $roleDraftMock = $this->createMock(RoleDraft::class); - $roleDomainMapper = $this->createMock(RoleDomainMapper::class); - $roleDomainMapper - ->method('buildDomainRoleObject') - ->willReturn($roleDraftMock); - - $roleServiceMock = $this->getPartlyMockedRoleService(null, [], $roleDomainMapper); - $policyDraftMock = $this->createMock(PolicyDraft::class); - - $policyDraftMock->expects($this->any()) - ->method('__get') - ->will( - $this->returnValueMap( - [ - ['roleId', 17], - ] - ) - ); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('update'), - $this->equalTo($roleDraftMock) - )->will($this->returnValue(false)); - - /* @var \eZ\Publish\API\Repository\Values\User\Policy $policyMock */ - $roleServiceMock->removePolicyByRoleDraft($roleDraftMock, $policyDraftMock); - } - - /** - * Test for the removePolicyByRoleDraft() method. - * - * @covers \eZ\Publish\Core\Repository\RoleService::removePolicyByRoleDraft - */ - public function testRemovePolicyByRoleDraftWithRollback() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Handler threw an exception'); - - $repository = $this->getRepositoryMock(); - $roleDraftMock = $this->createMock(RoleDraft::class); - $roleDraftMock->expects($this->any()) - ->method('__get') - ->with('id') - ->willReturn(17); - - $roleDomainMapper = $this->createMock(RoleDomainMapper::class); - $roleDomainMapper - ->method('buildDomainRoleObject') - ->willReturn($roleDraftMock); - $roleServiceMock = $this->getPartlyMockedRoleService(null, [], $roleDomainMapper); - - $policyDraftMock = $this->createMock(PolicyDraft::class); - $policyDraftMock->expects($this->any()) - ->method('__get') - ->will( - $this->returnValueMap( - [ - ['id', 42], - ['roleId', 17], - ] - ) - ); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('update'), - $this->equalTo($roleDraftMock) - )->will($this->returnValue(true)); - - $repository->expects($this->once())->method('beginTransaction'); - - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - - $userHandlerMock->expects($this->once()) - ->method('deletePolicy') - ->with( - $this->equalTo(42) - )->will($this->throwException(new \Exception('Handler threw an exception'))); - - $repository->expects($this->once())->method('rollback'); - - /* @var \eZ\Publish\API\Repository\Values\User\Policy $policyDraftMock */ - $roleServiceMock->removePolicyByRoleDraft($roleDraftMock, $policyDraftMock); - } - - /** - * @covers \eZ\Publish\Core\Repository\RoleService::removePolicyByRoleDraft - */ - public function testRemovePolicyByRoleDraft() - { - $repository = $this->getRepositoryMock(); - $roleDraftMock = $this->createMock(RoleDraft::class); - $roleDraftMock - ->expects($this->any()) - ->method('__get') - ->with('id') - ->willReturn(17); - - $roleDomainMapper = $this->createMock(RoleDomainMapper::class); - $roleDomainMapper - ->method('buildDomainRoleObject') - ->willReturn($roleDraftMock); - - $roleServiceMock = $this->getPartlyMockedRoleService(['loadRoleDraft'], [], $roleDomainMapper); - - $policyDraftMock = $this->createMock(PolicyDraft::class); - $policyDraftMock->expects($this->any()) - ->method('__get') - ->will( - $this->returnValueMap( - [ - ['id', 42], - ['roleId', 17], - ] - ) - ); - - $permissionResolverMock = $this->getPermissionResolverMock(); - $permissionResolverMock->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('role'), - $this->equalTo('update'), - $this->equalTo($roleDraftMock) - )->will($this->returnValue(true)); - - $repository->expects($this->once())->method('beginTransaction'); - - $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); - - $userHandlerMock->expects($this->once()) - ->method('deletePolicy') - ->with( - $this->equalTo(42) - ); - - $roleServiceMock->expects($this->once()) - ->method('loadRoleDraft') - ->with($this->equalTo(17)) - ->will($this->returnValue($roleDraftMock)); - - $repository->expects($this->once())->method('commit'); - - /* @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policyDraftMock */ - $roleServiceMock->removePolicyByRoleDraft($roleDraftMock, $policyDraftMock); - } - - /** @var \eZ\Publish\Core\Repository\RoleService */ - protected $partlyMockedRoleService; - - /** - * Returns the role service to test with $methods mocked. - * - * Injected Repository comes from {@see getRepositoryMock()} and persistence handler from {@see getPersistenceMock()} - * - * @param string[] $methods - * @param array $settings - * @param \eZ\Publish\Core\Repository\Mapper\RoleDomainMapper|null $roleDomainMapper - * - * @return \eZ\Publish\Core\Repository\RoleService|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getPartlyMockedRoleService( - array $methods = null, - array $settings = [], - ?RoleDomainMapper $roleDomainMapper = null - ) { - if (!isset($this->partlyMockedRoleService) || !empty($settings) || $roleDomainMapper) { - $limitationService = new LimitationService( - new ArrayIterator($settings['limitationTypes'] ?? []) - ); - if ($roleDomainMapper === null) { - $roleDomainMapper = $this->getMockBuilder(RoleDomainMapper::class) - ->setMethods([]) - ->setConstructorArgs([$limitationService]) - ->getMock(); - } - - $this->partlyMockedRoleService = $this->getMockBuilder(RoleService::class) - ->setMethods($methods) - ->setConstructorArgs( - [ - $this->getRepositoryMock(), - $this->getPersistenceMockHandler('User\\Handler'), - $limitationService, - $roleDomainMapper, - $settings, - ] - ) - ->getMock(); - } - - return $this->partlyMockedRoleService; - } - - /** - * @return \eZ\Publish\API\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getRepositoryMock(): Repository - { - $repositoryMock = parent::getRepositoryMock(); - $repositoryMock - ->expects($this->any()) - ->method('getPermissionResolver') - ->willReturn($this->getPermissionResolverMock()); - - return $repositoryMock; - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/UrlAliasTest.php b/eZ/Publish/Core/Repository/Tests/Service/Mock/UrlAliasTest.php deleted file mode 100644 index 51a3327aee..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/UrlAliasTest.php +++ /dev/null @@ -1,3413 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as ApiNotFoundException; -use eZ\Publish\API\Repository\LanguageResolver; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use eZ\Publish\Core\Base\Exceptions\ForbiddenException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Repository\Helper\NameSchemaService; -use eZ\Publish\Core\Repository\LocationService; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\Core\Repository\URLAliasService; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\SPI\Persistence\Content\UrlAlias as SPIUrlAlias; - -/** - * Mock test case for UrlAlias Service. - */ -class UrlAliasTest extends BaseServiceMockTest -{ - private const EXAMPLE_ID = 'eznode:42'; - private const EXAMPLE_LANGUAGE_CODE = 'pol-PL'; - private const EXAMPLE_PATH = 'folder/article'; - private const EXAMPLE_OFFSET = 10; - private const EXAMPLE_LIMIT = 100; - - /** @var \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ - private $permissionResolver; - - /** @var \eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler|\PHPUnit\Framework\MockObject\MockObject */ - private $urlAliasHandler; - - protected function setUp(): void - { - parent::setUp(); - $this->urlAliasHandler = $this->getPersistenceMockHandler('Content\\UrlAlias\\Handler'); - $this->permissionResolver = $this->getPermissionResolverMock(); - } - - /** - * Test for the __construct() method. - */ - public function testConstructor() - { - $repositoryMock = $this->getRepositoryMock(); - - new UrlALiasService( - $repositoryMock, - $this->urlAliasHandler, - $this->getNameSchemaServiceMock(), - $this->permissionResolver, - $this->getLanguageResolverMock() - ); - } - - /** - * Test for the load() method. - */ - public function testLoad() - { - $mockedService = $this->getPartlyMockedURLAliasServiceService(['extractPath']); - /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ - $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); - - $urlAliasHandlerMock - ->expects($this->once()) - ->method('loadUrlAlias') - ->with(self::EXAMPLE_ID) - ->willReturn(new SPIUrlAlias()); - - $mockedService - ->expects($this->once()) - ->method('extractPath') - ->with($this->isInstanceOf(SPIUrlAlias::class), null) - ->willReturn('path'); - - $urlAlias = $mockedService->load(self::EXAMPLE_ID); - - self::assertInstanceOf(URLAlias::class, $urlAlias); - } - - /** - * Test for the load() method. - */ - public function testLoadThrowsNotFoundException() - { - $mockedService = $this->getPartlyMockedURLAliasServiceService(['extractPath']); - /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ - $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); - - $urlAliasHandlerMock - ->expects($this->once()) - ->method('loadUrlAlias') - ->with(self::EXAMPLE_ID) - ->will($this->throwException(new NotFoundException('UrlAlias', self::EXAMPLE_ID))); - - $this->expectException(ApiNotFoundException::class); - $mockedService->load(self::EXAMPLE_ID); - } - - protected function getSpiUrlAlias() - { - $pathElement1 = [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ]; - $pathElement2 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ]; - $pathElement3 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - 'ger-DE' => 'drei', - ], - ]; - - return new SPIUrlAlias( - [ - 'id' => '3', - 'pathData' => [$pathElement1, $pathElement2, $pathElement3], - 'languageCodes' => ['ger-DE'], - 'alwaysAvailable' => false, - ] - ); - } - - /** - * Test for the load() method. - */ - public function testLoadThrowsNotFoundExceptionPath() - { - $spiUrlAlias = $this->getSpiUrlAlias(); - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - ['fre-FR'] - ); - - $this->urlAliasHandler - ->expects($this->once()) - ->method('loadUrlAlias') - ->with(self::EXAMPLE_ID) - ->willReturn($spiUrlAlias); - - $this->expectException(ApiNotFoundException::class); - - $urlAliasService->load(self::EXAMPLE_ID); - } - - /** - * Test for the removeAliases() method. - */ - public function testRemoveAliasesThrowsInvalidArgumentException() - { - $aliasList = [new URLAlias(['isCustom' => false])]; - $mockedService = $this->getPartlyMockedURLAliasServiceService(); - $this->permissionResolver - ->expects($this->once()) - ->method('hasAccess')->with( - $this->equalTo('content'), - $this->equalTo('urltranslator') - ) - ->willReturn(true); - - $this->expectException(InvalidArgumentException::class); - - $mockedService->removeAliases($aliasList); - } - - /** - * Test for the removeAliases() method. - */ - public function testRemoveAliases() - { - $aliasList = [new URLAlias(['isCustom' => true])]; - $spiAliasList = [new SPIUrlAlias(['isCustom' => true])]; - $this->permissionResolver - ->expects($this->once()) - ->method('hasAccess')->with( - $this->equalTo('content'), - $this->equalTo('urltranslator') - )->willReturn(true); - - $repositoryMock = $this->getRepositoryMock(); - - $mockedService = $this->getPartlyMockedURLAliasServiceService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ - $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); - - $repositoryMock - ->expects($this->once()) - ->method('beginTransaction'); - $repositoryMock - ->expects($this->once()) - ->method('commit'); - - $urlAliasHandlerMock - ->expects($this->once()) - ->method('removeURLAliases') - ->with($spiAliasList); - - $mockedService->removeAliases($aliasList); - } - - /** - * Test for the removeAliases() method. - */ - public function testRemoveAliasesWithRollback() - { - $aliasList = [new URLAlias(['isCustom' => true])]; - $spiAliasList = [new SPIUrlAlias(['isCustom' => true])]; - $this->permissionResolver - ->expects($this->once()) - ->method('hasAccess')->with( - $this->equalTo('content'), - $this->equalTo('urltranslator') - )->willReturn(true); - - $repositoryMock = $this->getRepositoryMock(); - - $mockedService = $this->getPartlyMockedURLAliasServiceService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ - $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); - - $repositoryMock - ->expects($this->once()) - ->method('beginTransaction'); - $repositoryMock - ->expects($this->once()) - ->method('rollback'); - - $urlAliasHandlerMock - ->expects($this->once()) - ->method('removeURLAliases') - ->with($spiAliasList) - ->will($this->throwException(new Exception('Handler threw an exception'))); - - $this->expectException(Exception::class); - $this->expectExceptionMessage('Handler threw an exception'); - - $mockedService->removeAliases($aliasList); - } - - public function providerForTestListAutogeneratedLocationAliasesPath() - { - $pathElement1 = [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ]; - $pathElement2 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ]; - $pathElement3 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - 'ger-DE' => 'drei', - ], - ]; - $pathData1 = [$pathElement1]; - $pathData2 = [$pathElement1, $pathElement2]; - $pathData3 = [$pathElement1, $pathElement2, $pathElement3]; - $spiUrlAliases1 = [ - new SPIUrlAlias( - [ - 'id' => '1', - 'pathData' => $pathData1, - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => true, - ] - ), - ]; - $spiUrlAliases2 = [ - new SPIUrlAlias( - [ - 'id' => '1', - 'pathData' => $pathData2, - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - ] - ), - new SPIUrlAlias( - [ - 'id' => '2', - 'pathData' => $pathData2, - 'languageCodes' => ['eng-GB'], - 'alwaysAvailable' => false, - ] - ), - ]; - $spiUrlAliases3 = [ - new SPIUrlAlias( - [ - 'id' => '1', - 'pathData' => $pathData3, - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - ] - ), - new SPIUrlAlias( - [ - 'id' => '2', - 'pathData' => $pathData3, - 'languageCodes' => ['eng-GB'], - 'alwaysAvailable' => false, - ] - ), - new SPIUrlAlias( - [ - 'id' => '3', - 'pathData' => $pathData3, - 'languageCodes' => ['ger-DE'], - 'alwaysAvailable' => false, - ] - ), - ]; - - return [ - [ - $spiUrlAliases1, - ['cro-HR'], - [ - 'cro-HR' => '/jedan', - ], - 'cro-HR', - ], - [ - $spiUrlAliases1, - ['eng-GB'], - [ - 'cro-HR' => '/jedan', - ], - 'cro-HR', - ], - [ - $spiUrlAliases1, - ['ger-DE'], - [ - 'cro-HR' => '/jedan', - ], - 'cro-HR', - ], - [ - $spiUrlAliases1, - ['cro-HR', 'eng-GB', 'ger-DE'], - [ - 'cro-HR' => '/jedan', - ], - 'cro-HR', - ], - [ - $spiUrlAliases2, - ['cro-HR'], - [ - 'cro-HR' => '/jedan/dva', - ], - 'cro-HR', - ], - [ - $spiUrlAliases2, - ['eng-GB'], - [ - 'eng-GB' => '/jedan/two', - ], - 'eng-GB', - ], - [ - $spiUrlAliases2, - ['cro-HR', 'eng-GB'], - [ - 'cro-HR' => '/jedan/dva', - 'eng-GB' => '/jedan/two', - ], - 'cro-HR', - ], - [ - $spiUrlAliases2, - ['cro-HR', 'ger-DE'], - [ - 'cro-HR' => '/jedan/dva', - ], - 'cro-HR', - ], - [ - $spiUrlAliases2, - ['eng-GB', 'cro-HR'], - [ - 'eng-GB' => '/jedan/two', - 'cro-HR' => '/jedan/dva', - ], - 'eng-GB', - ], - [ - $spiUrlAliases2, - ['eng-GB', 'ger-DE'], - [ - 'eng-GB' => '/jedan/two', - ], - 'eng-GB', - ], - [ - $spiUrlAliases2, - ['ger-DE', 'cro-HR'], - [ - 'cro-HR' => '/jedan/dva', - ], - 'cro-HR', - ], - [ - $spiUrlAliases2, - ['ger-DE', 'eng-GB'], - [ - 'eng-GB' => '/jedan/two', - ], - 'eng-GB', - ], - [ - $spiUrlAliases2, - ['cro-HR', 'eng-GB', 'ger-DE'], - [ - 'cro-HR' => '/jedan/dva', - 'eng-GB' => '/jedan/two', - ], - 'cro-HR', - ], - [ - $spiUrlAliases2, - ['cro-HR', 'ger-DE', 'eng-GB'], - [ - 'cro-HR' => '/jedan/dva', - 'eng-GB' => '/jedan/two', - ], - 'cro-HR', - ], - [ - $spiUrlAliases2, - ['eng-GB', 'cro-HR', 'ger-DE'], - [ - 'eng-GB' => '/jedan/two', - 'cro-HR' => '/jedan/dva', - ], - 'eng-GB', - ], - [ - $spiUrlAliases2, - ['eng-GB', 'ger-DE', 'cro-HR'], - [ - 'eng-GB' => '/jedan/two', - 'cro-HR' => '/jedan/dva', - ], - 'eng-GB', - ], - [ - $spiUrlAliases2, - ['ger-DE', 'cro-HR', 'eng-GB'], - [ - 'cro-HR' => '/jedan/dva', - 'eng-GB' => '/jedan/two', - ], - 'cro-HR', - ], - [ - $spiUrlAliases2, - ['ger-DE', 'eng-GB', 'cro-HR'], - [ - 'eng-GB' => '/jedan/two', - 'cro-HR' => '/jedan/dva', - ], - 'eng-GB', - ], - [ - $spiUrlAliases3, - ['cro-HR'], - [ - 'cro-HR' => '/jedan/dva/tri', - ], - 'cro-HR', - ], - [ - $spiUrlAliases3, - ['eng-GB'], - [ - 'eng-GB' => '/jedan/two/three', - ], - 'eng-GB', - ], - [ - $spiUrlAliases3, - ['cro-HR', 'eng-GB'], - [ - 'cro-HR' => '/jedan/dva/tri', - 'eng-GB' => '/jedan/dva/three', - ], - 'cro-HR', - ], - [ - $spiUrlAliases3, - ['cro-HR', 'ger-DE'], - [ - 'cro-HR' => '/jedan/dva/tri', - 'ger-DE' => '/jedan/dva/drei', - ], - 'cro-HR', - ], - [ - $spiUrlAliases3, - ['eng-GB', 'cro-HR'], - [ - 'eng-GB' => '/jedan/two/three', - 'cro-HR' => '/jedan/two/tri', - ], - 'eng-GB', - ], - [ - $spiUrlAliases3, - ['eng-GB', 'ger-DE'], - [ - 'eng-GB' => '/jedan/two/three', - 'ger-DE' => '/jedan/two/drei', - ], - 'eng-GB', - ], - [ - $spiUrlAliases3, - ['ger-DE', 'eng-GB'], - [ - 'ger-DE' => '/jedan/two/drei', - 'eng-GB' => '/jedan/two/three', - ], - 'ger-DE', - ], - [ - $spiUrlAliases3, - ['ger-DE', 'cro-HR'], - [ - 'ger-DE' => '/jedan/dva/drei', - 'cro-HR' => '/jedan/dva/tri', - ], - 'ger-DE', - ], - [ - $spiUrlAliases3, - ['cro-HR', 'eng-GB', 'ger-DE'], - [ - 'cro-HR' => '/jedan/dva/tri', - 'eng-GB' => '/jedan/dva/three', - 'ger-DE' => '/jedan/dva/drei', - ], - 'cro-HR', - ], - [ - $spiUrlAliases3, - ['cro-HR', 'ger-DE', 'eng-GB'], - [ - 'cro-HR' => '/jedan/dva/tri', - 'ger-DE' => '/jedan/dva/drei', - 'eng-GB' => '/jedan/dva/three', - ], - 'cro-HR', - ], - [ - $spiUrlAliases3, - ['eng-GB', 'cro-HR', 'ger-DE'], - [ - 'eng-GB' => '/jedan/two/three', - 'cro-HR' => '/jedan/two/tri', - 'ger-DE' => '/jedan/two/drei', - ], - 'eng-GB', - ], - [ - $spiUrlAliases3, - ['eng-GB', 'ger-DE', 'cro-HR'], - [ - 'eng-GB' => '/jedan/two/three', - 'ger-DE' => '/jedan/two/drei', - 'cro-HR' => '/jedan/two/tri', - ], - 'eng-GB', - ], - [ - $spiUrlAliases3, - ['ger-DE', 'cro-HR', 'eng-GB'], - [ - 'ger-DE' => '/jedan/dva/drei', - 'cro-HR' => '/jedan/dva/tri', - 'eng-GB' => '/jedan/dva/three', - ], - 'ger-DE', - ], - [ - $spiUrlAliases3, - ['ger-DE', 'eng-GB', 'cro-HR'], - [ - 'ger-DE' => '/jedan/two/drei', - 'eng-GB' => '/jedan/two/three', - 'cro-HR' => '/jedan/two/tri', - ], - 'ger-DE', - ], - ]; - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesPath - */ - public function testListAutogeneratedLocationAliasesPath($spiUrlAliases, $prioritizedLanguageCodes, $paths) - { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageCodes, - ); - - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases($location, false, null); - - self::assertEquals( - \count($paths), - \count($urlAliases) - ); - - foreach ($urlAliases as $index => $urlAlias) { - $pathKeys = array_keys($paths); - self::assertEquals( - $paths[$pathKeys[$index]], - $urlAlias->path - ); - self::assertEquals( - [$pathKeys[$index]], - $urlAlias->languageCodes - ); - } - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesPath - */ - public function testListAutogeneratedLocationAliasesPathCustomConfiguration( - $spiUrlAliases, - $prioritizedLanguageCodes, - $paths - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - [] - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases( - $location, - false, - null, - false, - $prioritizedLanguageCodes - ); - - self::assertEquals( - \count($paths), - \count($urlAliases) - ); - - foreach ($urlAliases as $index => $urlAlias) { - $pathKeys = array_keys($paths); - self::assertEquals( - $paths[$pathKeys[$index]], - $urlAlias->path - ); - self::assertEquals( - [$pathKeys[$index]], - $urlAlias->languageCodes - ); - } - } - - /** - * Test for the load() method. - */ - public function testListLocationAliasesWithShowAllTranslations() - { - $pathElement1 = [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ]; - $pathElement2 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ]; - $pathElement3 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - 'ger-DE' => 'drei', - ], - ]; - $spiUrlAlias = new SPIUrlAlias( - [ - 'id' => '3', - 'pathData' => [$pathElement1, $pathElement2, $pathElement3], - 'languageCodes' => ['ger-DE'], - 'alwaysAvailable' => false, - ] - ); - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - ['fre-FR'], - true - ); - - $this->urlAliasHandler - ->expects($this->once()) - ->method('listURLAliasesForLocation') - ->with( - $this->equalTo(42), - $this->equalTo(false) - ) - ->willReturn([$spiUrlAlias]); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases($location, false, null); - - self::assertCount(1, $urlAliases); - self::assertInstanceOf(URLAlias::class, $urlAliases[0]); - self::assertEquals('/jedan/dva/tri', $urlAliases[0]->path); - } - - /** - * Test for the load() method. - */ - public function testListLocationAliasesWithShowAllTranslationsCustomConfiguration() - { - $pathElement1 = [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ]; - $pathElement2 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ]; - $pathElement3 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - 'ger-DE' => 'drei', - ], - ]; - $spiUrlAlias = new SPIUrlAlias( - [ - 'id' => '3', - 'pathData' => [$pathElement1, $pathElement2, $pathElement3], - 'languageCodes' => ['ger-DE'], - 'alwaysAvailable' => false, - ] - ); - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - [] - ); - - $this->urlAliasHandler - ->expects($this->once()) - ->method('listURLAliasesForLocation') - ->with( - $this->equalTo(42), - $this->equalTo(false) - ) - ->willReturn([$spiUrlAlias]); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases( - $location, - false, - null, - true, - ['fre-FR'] - ); - - self::assertCount(1, $urlAliases); - self::assertInstanceOf(URLAlias::class, $urlAliases[0]); - self::assertEquals('/jedan/dva/tri', $urlAliases[0]->path); - } - - public function providerForTestListAutogeneratedLocationAliasesEmpty() - { - $pathElement1 = [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => '/jedan', - ], - ]; - $pathElement2 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ]; - $pathElement3 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - 'ger-DE' => 'drei', - ], - ]; - $pathData2 = [$pathElement1, $pathElement2]; - $pathData3 = [$pathElement1, $pathElement2, $pathElement3]; - $spiUrlAliases2 = [ - new SPIUrlAlias( - [ - 'pathData' => $pathData2, - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - ] - ), - new SPIUrlAlias( - [ - 'pathData' => $pathData2, - 'languageCodes' => ['eng-GB'], - 'alwaysAvailable' => false, - ] - ), - ]; - $spiUrlAliases3 = [ - new SPIUrlAlias( - [ - 'pathData' => $pathData3, - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - ] - ), - new SPIUrlAlias( - [ - 'pathData' => $pathData3, - 'languageCodes' => ['eng-GB'], - 'alwaysAvailable' => false, - ] - ), - new SPIUrlAlias( - [ - 'pathData' => $pathData3, - 'languageCodes' => ['ger-DE'], - 'alwaysAvailable' => false, - ] - ), - ]; - - return [ - [ - $spiUrlAliases2, - ['ger-DE'], - ], - [ - $spiUrlAliases3, - ['ger-DE'], - ], - ]; - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesEmpty - */ - public function testListAutogeneratedLocationAliasesEmpty($spiUrlAliases, $prioritizedLanguageCodes) - { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageCodes - ); - - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases($location, false, null); - - self::assertEmpty($urlAliases); - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesEmpty - */ - public function testListAutogeneratedLocationAliasesEmptyCustomConfiguration( - $spiUrlAliases, - $prioritizedLanguageCodes - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - [] - ); - - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases( - $location, - false, - null, - false, - $prioritizedLanguageCodes - ); - - self::assertEmpty($urlAliases); - } - - public function providerForTestListAutogeneratedLocationAliasesWithLanguageCodePath() - { - $pathElement1 = [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => 'jedan', - ], - ]; - $pathElement2 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ]; - $pathElement3 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - 'ger-DE' => 'drei', - ], - ]; - $pathData1 = [$pathElement1]; - $pathData2 = [$pathElement1, $pathElement2]; - $pathData3 = [$pathElement1, $pathElement2, $pathElement3]; - $spiUrlAliases1 = [ - new SPIUrlAlias( - [ - 'pathData' => $pathData1, - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => true, - ] - ), - ]; - $spiUrlAliases2 = [ - new SPIUrlAlias( - [ - 'pathData' => $pathData2, - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - ] - ), - new SPIUrlAlias( - [ - 'pathData' => $pathData2, - 'languageCodes' => ['eng-GB'], - 'alwaysAvailable' => false, - ] - ), - ]; - $spiUrlAliases3 = [ - new SPIUrlAlias( - [ - 'pathData' => $pathData3, - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - ] - ), - new SPIUrlAlias( - [ - 'pathData' => $pathData3, - 'languageCodes' => ['eng-GB'], - 'alwaysAvailable' => false, - ] - ), - new SPIUrlAlias( - [ - 'pathData' => $pathData3, - 'languageCodes' => ['ger-DE'], - 'alwaysAvailable' => false, - ] - ), - ]; - - return [ - [ - $spiUrlAliases1, - 'cro-HR', - ['cro-HR'], - [ - '/jedan', - ], - ], - [ - $spiUrlAliases1, - 'cro-HR', - ['eng-GB'], - [ - '/jedan', - ], - ], - [ - $spiUrlAliases2, - 'cro-HR', - ['cro-HR'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases2, - 'eng-GB', - ['eng-GB'], - [ - '/jedan/two', - ], - ], - [ - $spiUrlAliases2, - 'eng-GB', - ['cro-HR', 'eng-GB'], - [ - '/jedan/two', - ], - ], - [ - $spiUrlAliases2, - 'cro-HR', - ['cro-HR', 'ger-DE'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases2, - 'cro-HR', - ['eng-GB', 'cro-HR'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases2, - 'eng-GB', - ['eng-GB', 'ger-DE'], - [ - '/jedan/two', - ], - ], - [ - $spiUrlAliases2, - 'cro-HR', - ['ger-DE', 'cro-HR'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases2, - 'eng-GB', - ['ger-DE', 'eng-GB'], - [ - '/jedan/two', - ], - ], - [ - $spiUrlAliases2, - 'cro-HR', - ['cro-HR', 'eng-GB', 'ger-DE'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases2, - 'eng-GB', - ['cro-HR', 'ger-DE', 'eng-GB'], - [ - '/jedan/two', - ], - ], - [ - $spiUrlAliases2, - 'cro-HR', - ['eng-GB', 'cro-HR', 'ger-DE'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases2, - 'cro-HR', - ['eng-GB', 'ger-DE', 'cro-HR'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases2, - 'cro-HR', - ['ger-DE', 'cro-HR', 'eng-GB'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases2, - 'cro-HR', - ['ger-DE', 'eng-GB', 'cro-HR'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases3, - 'cro-HR', - ['cro-HR'], - [ - '/jedan/dva/tri', - ], - ], - [ - $spiUrlAliases3, - 'eng-GB', - ['eng-GB'], - [ - '/jedan/two/three', - ], - ], - [ - $spiUrlAliases3, - 'eng-GB', - ['cro-HR', 'eng-GB'], - [ - '/jedan/dva/three', - ], - ], - [ - $spiUrlAliases3, - 'ger-DE', - ['cro-HR', 'ger-DE'], - [ - '/jedan/dva/drei', - ], - ], - [ - $spiUrlAliases3, - 'cro-HR', - ['eng-GB', 'cro-HR'], - [ - '/jedan/two/tri', - ], - ], - [ - $spiUrlAliases3, - 'ger-DE', - ['eng-GB', 'ger-DE'], - [ - '/jedan/two/drei', - ], - ], - [ - $spiUrlAliases3, - 'eng-GB', - ['ger-DE', 'eng-GB'], - [ - '/jedan/two/three', - ], - ], - [ - $spiUrlAliases3, - 'ger-DE', - ['ger-DE', 'cro-HR'], - [ - '/jedan/dva/drei', - ], - ], - [ - $spiUrlAliases3, - 'ger-DE', - ['cro-HR', 'eng-GB', 'ger-DE'], - [ - '/jedan/dva/drei', - ], - ], - [ - $spiUrlAliases3, - 'ger-DE', - ['cro-HR', 'ger-DE', 'eng-GB'], - [ - '/jedan/dva/drei', - ], - ], - [ - $spiUrlAliases3, - 'ger-DE', - ['eng-GB', 'cro-HR', 'ger-DE'], - [ - '/jedan/two/drei', - ], - ], - [ - $spiUrlAliases3, - 'ger-DE', - ['eng-GB', 'ger-DE', 'cro-HR'], - [ - '/jedan/two/drei', - ], - ], - [ - $spiUrlAliases3, - 'eng-GB', - ['ger-DE', 'cro-HR', 'eng-GB'], - [ - '/jedan/dva/three', - ], - ], - [ - $spiUrlAliases3, - 'cro-HR', - ['ger-DE', 'eng-GB', 'cro-HR'], - [ - '/jedan/two/tri', - ], - ], - ]; - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodePath - */ - public function testListAutogeneratedLocationAliasesWithLanguageCodePath( - $spiUrlAliases, - $languageCode, - $prioritizedLanguageCodes, - $paths - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageCodes - ); - - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases($location, false, $languageCode); - - self::assertEquals( - \count($paths), - \count($urlAliases) - ); - - foreach ($urlAliases as $index => $urlAlias) { - self::assertEquals( - $paths[$index], - $urlAlias->path - ); - } - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodePath - */ - public function testListAutogeneratedLocationAliasesWithLanguageCodePathCustomConfiguration( - $spiUrlAliases, - $languageCode, - $prioritizedLanguageCodes, - $paths - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - [] - ); - - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases( - $location, - false, - $languageCode, - false, - $prioritizedLanguageCodes - ); - - self::assertEquals( - \count($paths), - \count($urlAliases) - ); - - foreach ($urlAliases as $index => $urlAlias) { - self::assertEquals( - $paths[$index], - $urlAlias->path - ); - } - } - - public function providerForTestListAutogeneratedLocationAliasesWithLanguageCodeEmpty() - { - $pathElement1 = [ - 'always-available' => true, - 'translations' => [ - 'cro-HR' => '/jedan', - ], - ]; - $pathElement2 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ]; - $pathElement3 = [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'tri', - 'eng-GB' => 'three', - 'ger-DE' => 'drei', - ], - ]; - $pathData1 = [$pathElement1]; - $pathData2 = [$pathElement1, $pathElement2]; - $pathData3 = [$pathElement1, $pathElement2, $pathElement3]; - $spiUrlAliases1 = [ - new SPIUrlAlias( - [ - 'pathData' => $pathData1, - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => true, - ] - ), - ]; - $spiUrlAliases2 = [ - new SPIUrlAlias( - [ - 'pathData' => $pathData2, - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - ] - ), - new SPIUrlAlias( - [ - 'pathData' => $pathData2, - 'languageCodes' => ['eng-GB'], - 'alwaysAvailable' => false, - ] - ), - ]; - $spiUrlAliases3 = [ - new SPIUrlAlias( - [ - 'pathData' => $pathData3, - 'languageCodes' => ['cro-HR'], - 'alwaysAvailable' => false, - ] - ), - new SPIUrlAlias( - [ - 'pathData' => $pathData3, - 'languageCodes' => ['eng-GB'], - 'alwaysAvailable' => false, - ] - ), - new SPIUrlAlias( - [ - 'pathData' => $pathData3, - 'languageCodes' => ['ger-DE'], - 'alwaysAvailable' => false, - ] - ), - ]; - - return [ - [ - $spiUrlAliases1, - 'eng-GB', - ['ger-DE'], - ], - [ - $spiUrlAliases1, - 'ger-DE', - ['cro-HR', 'eng-GB', 'ger-DE'], - ], - [ - $spiUrlAliases2, - 'eng-GB', - ['cro-HR'], - ], - [ - $spiUrlAliases2, - 'ger-DE', - ['cro-HR', 'eng-GB'], - ], - [ - $spiUrlAliases2, - 'ger-DE', - ['cro-HR', 'ger-DE'], - ], - [ - $spiUrlAliases2, - 'ger-DE', - ['eng-GB', 'ger-DE'], - ], - [ - $spiUrlAliases2, - 'ger-DE', - ['ger-DE', 'cro-HR'], - ], - [ - $spiUrlAliases2, - 'ger-DE', - ['ger-DE', 'eng-GB'], - ], - [ - $spiUrlAliases2, - 'ger-DE', - ['cro-HR', 'eng-GB', 'ger-DE'], - ], - [ - $spiUrlAliases2, - 'ger-DE', - ['cro-HR', 'ger-DE', 'eng-GB'], - ], - [ - $spiUrlAliases2, - 'ger-DE', - ['eng-GB', 'cro-HR', 'ger-DE'], - ], - [ - $spiUrlAliases2, - 'ger-DE', - ['eng-GB', 'ger-DE', 'cro-HR'], - ], - [ - $spiUrlAliases2, - 'ger-DE', - ['ger-DE', 'cro-HR', 'eng-GB'], - ], - [ - $spiUrlAliases2, - 'ger-DE', - ['ger-DE', 'eng-GB', 'cro-HR'], - ], - [ - $spiUrlAliases3, - 'ger-DE', - ['cro-HR'], - ], - [ - $spiUrlAliases3, - 'cro-HR', - ['eng-GB'], - ], - [ - $spiUrlAliases3, - 'ger-DE', - ['cro-HR', 'eng-GB'], - ], - [ - $spiUrlAliases3, - 'eng-GB', - ['cro-HR', 'ger-DE'], - ], - [ - $spiUrlAliases3, - 'ger-DE', - ['eng-GB', 'cro-HR'], - ], - [ - $spiUrlAliases3, - 'cro-HR', - ['eng-GB', 'ger-DE'], - ], - [ - $spiUrlAliases3, - 'cro-HR', - ['ger-DE', 'eng-GB'], - ], - [ - $spiUrlAliases3, - 'eng-GB', - ['ger-DE', 'cro-HR'], - ], - ]; - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeEmpty - */ - public function testListAutogeneratedLocationAliasesWithLanguageCodeEmpty( - $spiUrlAliases, - $languageCode, - $prioritizedLanguageCodes - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageCodes - ); - - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases($location, false, $languageCode); - - self::assertEmpty($urlAliases); - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeEmpty - */ - public function testListAutogeneratedLocationAliasesWithLanguageCodeEmptyCustomConfiguration( - $spiUrlAliases, - $languageCode, - $prioritizedLanguageCodes - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - [] - ); - - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases( - $location, - false, - $languageCode, - false, - $prioritizedLanguageCodes - ); - - self::assertEmpty($urlAliases); - } - - public function providerForTestListAutogeneratedLocationAliasesMultipleLanguagesPath() - { - $spiUrlAliases = [ - new SPIUrlAlias( - [ - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - 'eng-GB' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'dva', - 'ger-DE' => 'dva', - ], - ], - ], - 'languageCodes' => ['eng-GB', 'ger-DE'], - 'alwaysAvailable' => false, - ] - ), - ]; - - return [ - [ - $spiUrlAliases, - ['cro-HR', 'ger-DE'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases, - ['ger-DE', 'cro-HR'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases, - ['eng-GB'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases, - ['eng-GB', 'ger-DE', 'cro-HR'], - [ - '/jedan/dva', - ], - ], - ]; - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesMultipleLanguagesPath - */ - public function testListAutogeneratedLocationAliasesMultipleLanguagesPath($spiUrlAliases, $prioritizedLanguageCodes, $paths) - { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageCodes - ); - - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases($location, false, null); - - self::assertEquals( - \count($paths), - \count($urlAliases) - ); - - foreach ($urlAliases as $index => $urlAlias) { - self::assertEquals( - $paths[$index], - $urlAlias->path - ); - } - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesMultipleLanguagesPath - */ - public function testListAutogeneratedLocationAliasesMultipleLanguagesPathCustomConfiguration( - $spiUrlAliases, - $prioritizedLanguageCodes, - $paths - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - [] - ); - - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases( - $location, - false, - null, - false, - $prioritizedLanguageCodes - ); - - self::assertEquals( - \count($paths), - \count($urlAliases) - ); - - foreach ($urlAliases as $index => $urlAlias) { - self::assertEquals( - $paths[$index], - $urlAlias->path - ); - } - } - - public function providerForTestListAutogeneratedLocationAliasesMultipleLanguagesEmpty() - { - $spiUrlAliases = [ - new SPIUrlAlias( - [ - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => '/jedan', - 'eng-GB' => '/jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'dva', - 'ger-DE' => 'dva', - ], - ], - ], - 'languageCodes' => ['eng-GB', 'ger-DE'], - 'alwaysAvailable' => false, - ] - ), - ]; - - return [ - [ - $spiUrlAliases, - ['cro-HR'], - ], - [ - $spiUrlAliases, - ['ger-DE'], - ], - ]; - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesMultipleLanguagesEmpty - */ - public function testListAutogeneratedLocationAliasesMultipleLanguagesEmpty($spiUrlAliases, $prioritizedLanguageCodes) - { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageCodes - ); - - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases($location, false, null); - - self::assertEmpty($urlAliases); - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesMultipleLanguagesEmpty - */ - public function testListAutogeneratedLocationAliasesMultipleLanguagesEmptyCustomConfiguration( - $spiUrlAliases, - $prioritizedLanguageCodes - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - [] - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases( - $location, - false, - null, - false, - $prioritizedLanguageCodes - ); - - self::assertEmpty($urlAliases); - } - - public function providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath() - { - $spiUrlAliases = [ - new SPIUrlAlias( - [ - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - 'eng-GB' => 'jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'dva', - 'ger-DE' => 'dva', - ], - ], - ], - 'languageCodes' => ['eng-GB', 'ger-DE'], - 'alwaysAvailable' => false, - ] - ), - ]; - - return [ - [ - $spiUrlAliases, - 'ger-DE', - ['cro-HR', 'ger-DE'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases, - 'ger-DE', - ['ger-DE', 'cro-HR'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases, - 'eng-GB', - ['eng-GB'], - [ - '/jedan/dva', - ], - ], - [ - $spiUrlAliases, - 'eng-GB', - ['eng-GB', 'ger-DE', 'cro-HR'], - [ - '/jedan/dva', - ], - ], - ]; - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath - */ - public function testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath( - $spiUrlAliases, - $languageCode, - $prioritizedLanguageCodes, - $paths - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageCodes - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases($location, false, $languageCode); - - self::assertEquals( - \count($paths), - \count($urlAliases) - ); - - foreach ($urlAliases as $index => $urlAlias) { - self::assertEquals( - $paths[$index], - $urlAlias->path - ); - } - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath - */ - public function testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPathCustomConfiguration( - $spiUrlAliases, - $languageCode, - $prioritizedLanguageCodes, - $paths - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - [] - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases( - $location, - false, - $languageCode, - false, - $prioritizedLanguageCodes - ); - - self::assertEquals( - \count($paths), - \count($urlAliases) - ); - - foreach ($urlAliases as $index => $urlAlias) { - self::assertEquals( - $paths[$index], - $urlAlias->path - ); - } - } - - public function providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmpty() - { - $spiUrlAliases = [ - new SPIUrlAlias( - [ - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => '/jedan', - 'eng-GB' => '/jedan', - ], - ], - [ - 'always-available' => false, - 'translations' => [ - 'eng-GB' => 'dva', - 'ger-DE' => 'dva', - ], - ], - ], - 'languageCodes' => ['eng-GB', 'ger-DE'], - 'alwaysAvailable' => false, - ] - ), - ]; - - return [ - [ - $spiUrlAliases, - 'cro-HR', - ['cro-HR'], - ], - [ - $spiUrlAliases, - 'cro-HR', - ['cro-HR', 'eng-GB'], - ], - [ - $spiUrlAliases, - 'cro-HR', - ['ger-DE'], - ], - [ - $spiUrlAliases, - 'cro-HR', - ['cro-HR', 'eng-GB', 'ger-DE'], - ], - ]; - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmpty - */ - public function testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmpty( - $spiUrlAliases, - $languageCode, - $prioritizedLanguageCodes - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageCodes - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases($location, false, $languageCode); - - self::assertEmpty($urlAliases); - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmpty - */ - public function testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmptyCustomConfiguration( - $spiUrlAliases, - $languageCode, - $prioritizedLanguageCodes - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - [] - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases( - $location, - false, - $languageCode, - false, - $prioritizedLanguageCodes - ); - - self::assertEmpty($urlAliases); - } - - public function providerForTestListAutogeneratedLocationAliasesAlwaysAvailablePath() - { - $spiUrlAliases = [ - new SPIUrlAlias( - [ - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - 'eng-GB' => 'one', - ], - ], - [ - 'always-available' => true, - 'translations' => [ - 'ger-DE' => 'zwei', - ], - ], - ], - 'languageCodes' => ['ger-DE'], - 'alwaysAvailable' => true, - ] - ), - ]; - - return [ - [ - $spiUrlAliases, - ['cro-HR', 'ger-DE'], - [ - '/jedan/zwei', - ], - ], - [ - $spiUrlAliases, - ['ger-DE', 'cro-HR'], - [ - '/jedan/zwei', - ], - ], - [ - $spiUrlAliases, - ['eng-GB'], - [ - '/one/zwei', - ], - ], - [ - $spiUrlAliases, - ['cro-HR', 'eng-GB', 'ger-DE'], - [ - '/jedan/zwei', - ], - ], - [ - $spiUrlAliases, - ['eng-GB', 'ger-DE', 'cro-HR'], - [ - '/one/zwei', - ], - ], - ]; - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesAlwaysAvailablePath - */ - public function testListAutogeneratedLocationAliasesAlwaysAvailablePath( - $spiUrlAliases, - $prioritizedLanguageCodes, - $paths - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageCodes - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases($location, false, null); - - self::assertEquals( - \count($paths), - \count($urlAliases) - ); - - foreach ($urlAliases as $index => $urlAlias) { - self::assertEquals( - $paths[$index], - $urlAlias->path - ); - } - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesAlwaysAvailablePath - */ - public function testListAutogeneratedLocationAliasesAlwaysAvailablePathCustomConfiguration( - $spiUrlAliases, - $prioritizedLanguageCodes, - $paths - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - [] - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases( - $location, - false, - null, - false, - $prioritizedLanguageCodes - ); - - self::assertEquals( - \count($paths), - \count($urlAliases) - ); - - foreach ($urlAliases as $index => $urlAlias) { - self::assertEquals( - $paths[$index], - $urlAlias->path - ); - } - } - - public function providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath() - { - $spiUrlAliases = [ - new SPIUrlAlias( - [ - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - 'eng-GB' => 'one', - ], - ], - [ - 'always-available' => true, - 'translations' => [ - 'ger-DE' => 'zwei', - ], - ], - ], - 'languageCodes' => ['ger-DE'], - 'alwaysAvailable' => true, - ] - ), - ]; - - return [ - [ - $spiUrlAliases, - 'ger-DE', - ['cro-HR', 'ger-DE'], - [ - '/jedan/zwei', - ], - ], - [ - $spiUrlAliases, - 'ger-DE', - ['ger-DE', 'cro-HR'], - [ - '/jedan/zwei', - ], - ], - ]; - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath - */ - public function testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath( - $spiUrlAliases, - $languageCode, - $prioritizedLanguageCodes, - $paths - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageCodes - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases($location, false, $languageCode); - - self::assertEquals( - \count($paths), - \count($urlAliases) - ); - - foreach ($urlAliases as $index => $urlAlias) { - self::assertEquals( - $paths[$index], - $urlAlias->path - ); - } - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath - */ - public function testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePathCustomConfiguration( - $spiUrlAliases, - $languageCode, - $prioritizedLanguageCodes, - $paths - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - [] - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases( - $location, - false, - $languageCode, - false, - $prioritizedLanguageCodes - ); - - self::assertEquals( - \count($paths), - \count($urlAliases) - ); - - foreach ($urlAliases as $index => $urlAlias) { - self::assertEquals( - $paths[$index], - $urlAlias->path - ); - } - } - - public function providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmpty() - { - $spiUrlAliases = [ - new SPIUrlAlias( - [ - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'jedan', - 'eng-GB' => 'one', - ], - ], - [ - 'always-available' => true, - 'translations' => [ - 'ger-DE' => 'zwei', - ], - ], - ], - 'languageCodes' => ['ger-DE'], - 'alwaysAvailable' => true, - ] - ), - ]; - - return [ - [ - $spiUrlAliases, - 'eng-GB', - ['eng-GB'], - ], - [ - $spiUrlAliases, - 'eng-GB', - ['cro-HR', 'eng-GB', 'ger-DE'], - ], - [ - $spiUrlAliases, - 'eng-GB', - ['eng-GB', 'ger-DE', 'cro-HR'], - ], - ]; - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmpty - */ - public function testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmpty( - $spiUrlAliases, - $languageCode, - $prioritizedLanguageCodes - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageCodes - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases($location, false, $languageCode); - - self::assertEmpty($urlAliases); - } - - /** - * Test for the listLocationAliases() method. - * - * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmpty - */ - public function testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmptyCustomConfiguration( - $spiUrlAliases, - $languageCode, - $prioritizedLanguageCodes - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - [] - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAliases = $urlAliasService->listLocationAliases( - $location, - false, - $languageCode, - false, - $prioritizedLanguageCodes - ); - - self::assertEmpty($urlAliases); - } - - /** - * Test for the listGlobalAliases() method. - */ - public function testListGlobalAliases() - { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - ['ger-DE'], - true - ); - - $this->urlAliasHandler->expects( - $this->once() - )->method( - 'listGlobalURLAliases' - )->with( - $this->equalTo(null), - $this->equalTo(0), - $this->equalTo(-1) - )->willReturn( - [ - new SPIUrlAlias( - [ - 'pathData' => [ - [ - 'always-available' => true, - 'translations' => [ - 'ger-DE' => 'squirrel', - ], - ], - ], - 'languageCodes' => ['ger-DE'], - 'alwaysAvailable' => true, - ] - ), - ] - ); - - $urlAliases = $urlAliasService->listGlobalAliases(); - - self::assertCount(1, $urlAliases); - self::assertInstanceOf(URLAlias::class, $urlAliases[0]); - } - - /** - * Test for the listGlobalAliases() method. - */ - public function testListGlobalAliasesEmpty() - { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService(); - - $this->urlAliasHandler->expects( - $this->once() - )->method( - 'listGlobalURLAliases' - )->with( - $this->equalTo(null), - $this->equalTo(0), - $this->equalTo(-1) - )->willReturn( - [ - new SPIUrlAlias( - [ - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => [ - 'ger-DE' => 'squirrel', - ], - ], - ], - 'languageCodes' => ['ger-DE'], - 'alwaysAvailable' => false, - ] - ), - ] - ); - - $urlAliases = $urlAliasService->listGlobalAliases(); - - self::assertCount(0, $urlAliases); - } - - /** - * Test for the listGlobalAliases() method. - */ - public function testListGlobalAliasesWithParameters() - { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService(); - - $this->urlAliasHandler->expects( - $this->once() - )->method( - 'listGlobalURLAliases' - )->with( - $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), - $this->equalTo(self::EXAMPLE_OFFSET), - $this->equalTo(self::EXAMPLE_LIMIT) - )->willReturn( - [] - ); - - $urlAliases = $urlAliasService->listGlobalAliases( - self::EXAMPLE_LANGUAGE_CODE, - self::EXAMPLE_OFFSET, - self::EXAMPLE_LIMIT - ); - - self::assertEmpty($urlAliases); - } - - /** - * Test for the lookup() method. - */ - public function testLookupThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $urlAliasService = $this->getPartlyMockedURLAliasServiceService(); - - $this->urlAliasHandler->expects( - $this->once() - )->method( - 'lookup' - )->with( - $this->equalTo('url') - )->will( - $this->throwException(new NotFoundException('UrlAlias', 'url')) - ); - - $urlAliasService->lookup('url'); - } - - public function providerForTestLookupThrowsNotFoundExceptionPath() - { - return [ - // alias does not exist in requested language - ['ein/dva', ['cro-HR', 'ger-DE'], 'ger-DE'], - // alias exists in requested language but the language is not in prioritized languages list - ['ein/dva', ['ger-DE'], 'eng-GB'], - // alias path is not matched - ['jedan/dva', ['cro-HR', 'ger-DE'], 'cro-HR'], - // path is not loadable for prioritized languages list - ['ein/dva', ['cro-HR'], 'cro-HR'], - ]; - } - - /** - * Test for the lookup() method. - * - * @dataProvider providerForTestLookupThrowsNotFoundExceptionPath - */ - public function testLookupThrowsNotFoundExceptionPathNotMatchedOrNotLoadable($url, $prioritizedLanguageList, $languageCode) - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageList - ); - - $this->urlAliasHandler->expects( - $this->once() - )->method( - 'lookup' - )->with( - $this->equalTo($url) - )->willReturn( - new SPIUrlAlias( - [ - 'pathData' => [ - [ - 'always-available' => false, - 'translations' => ['ger-DE' => 'ein'], - ], - [ - 'always-available' => false, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ], - ], - 'languageCodes' => ['eng-GB', 'cro-HR'], - 'alwaysAvailable' => false, - ] - ) - ); - - $urlAliasService->lookup($url, $languageCode); - } - - public function providerForTestLookup() - { - return [ - // showAllTranslations setting is true - [['ger-DE'], true, false, null], - // alias is always available - [['ger-DE'], false, true, null], - // works with available language code - [['cro-HR'], false, false, 'eng-GB'], - ]; - } - - /** - * Test for the lookup() method. - * - * @dataProvider providerForTestLookup - */ - public function testLookup($prioritizedLanguageList, $showAllTranslations, $alwaysAvailable, $languageCode) - { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageList, - $showAllTranslations - ); - - $this->urlAliasHandler->expects( - $this->once() - )->method( - 'lookup' - )->with( - $this->equalTo('jedan/dva') - )->willReturn( - new SPIUrlAlias( - [ - 'pathData' => [ - [ - 'always-available' => $alwaysAvailable, - 'translations' => ['cro-HR' => 'jedan'], - ], - [ - 'always-available' => $alwaysAvailable, - 'translations' => [ - 'cro-HR' => 'dva', - 'eng-GB' => 'two', - ], - ], - ], - 'languageCodes' => ['eng-GB', 'cro-HR'], - 'alwaysAvailable' => $alwaysAvailable, - ] - ) - ); - - $urlAlias = $urlAliasService->lookup('jedan/dva', $languageCode); - - self::assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\Content\\URLAlias', - $urlAlias - ); - } - - public function providerForTestLookupWithSharedTranslation() - { - return [ - // showAllTranslations setting is true - [['ger-DE'], true, false, null], - // alias is always available - [['ger-DE'], false, true, null], - // works with available language codes - [['cro-HR'], false, false, 'eng-GB'], - [['eng-GB'], false, false, 'cro-HR'], - // works with cro-HR only - [['cro-HR'], false, false, null], - // works with eng-GB only - [['eng-GB'], false, false, null], - // works with cro-HR first - [['cro-HR', 'eng-GB'], false, false, null], - // works with eng-GB first - [['eng-GB', 'cro-HR'], false, false, null], - ]; - } - - /** - * Test for the lookup() method. - * - * @dataProvider providerForTestLookupWithSharedTranslation - */ - public function testLookupWithSharedTranslation( - $prioritizedLanguageList, - $showAllTranslations, - $alwaysAvailable, - $languageCode - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageList, - $showAllTranslations - ); - - $this->urlAliasHandler->expects( - $this->once() - )->method( - 'lookup' - )->with( - $this->equalTo('jedan/two') - )->willReturn( - new SPIUrlAlias( - [ - 'pathData' => [ - [ - 'always-available' => $alwaysAvailable, - 'translations' => [ - 'cro-HR' => 'jedan', - 'eng-GB' => 'jedan', - ], - ], - [ - 'always-available' => $alwaysAvailable, - 'translations' => [ - 'cro-HR' => 'two', - 'eng-GB' => 'two', - ], - ], - ], - 'languageCodes' => ['eng-GB', 'cro-HR'], - 'alwaysAvailable' => $alwaysAvailable, - ] - ) - ); - - $urlAlias = $urlAliasService->lookup('jedan/two', $languageCode); - - self::assertInstanceOf(URLAlias::class, $urlAlias); - } - - /** - * Test for the reverseLookup() method. - */ - public function testReverseLookupCustomConfiguration() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $mockedService = $this->getPartlyMockedURLAliasServiceService(['listLocationAliases']); - $location = $this->getLocationStub(); - $mockedService->expects( - $this->once() - )->method( - 'listLocationAliases' - )->with( - $this->equalTo($location), - $this->equalTo(false), - $this->equalTo(null), - $this->equalTo($showAllTranslations = true), - $this->equalTo($prioritizedLanguageList = ['LANGUAGES!']) - )->willReturn( - [] - ); - - $mockedService->reverseLookup($location, null, $showAllTranslations, $prioritizedLanguageList); - } - - /** - * Test for the reverseLookup() method. - */ - public function testReverseLookupThrowsNotFoundException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - ['listLocationAliases'], - ['ger-DE'] - ); - - $languageCode = 'eng-GB'; - $location = $this->getLocationStub(); - - $urlAliasService->expects( - $this->once() - )->method( - 'listLocationAliases' - )->with( - $this->equalTo($location), - $this->equalTo(false), - $this->equalTo($languageCode) - )->willReturn( - [ - new UrlAlias( - [ - 'languageCodes' => ['eng-GB'], - 'alwaysAvailable' => false, - ] - ), - ] - ); - - $urlAliasService->reverseLookup($location, $languageCode); - } - - public function providerForTestReverseLookup() - { - return $this->providerForTestListAutogeneratedLocationAliasesPath(); - } - - /** - * Test for the reverseLookup() method. - * - * @dataProvider providerForTestReverseLookup - */ - public function testReverseLookupPath($spiUrlAliases, $prioritizedLanguageCodes, $paths, $reverseLookupLanguageCode) - { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageCodes - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAlias = $urlAliasService->reverseLookup($location); - - self::assertEquals( - [$reverseLookupLanguageCode], - $urlAlias->languageCodes - ); - self::assertEquals( - $paths[$reverseLookupLanguageCode], - $urlAlias->path - ); - } - - public function providerForTestReverseLookupAlwaysAvailablePath() - { - return $this->providerForTestListAutogeneratedLocationAliasesAlwaysAvailablePath(); - } - - /** - * Test for the reverseLookup() method. - * - * @dataProvider providerForTestReverseLookupAlwaysAvailablePath - */ - public function testReverseLookupAlwaysAvailablePath( - $spiUrlAliases, - $prioritizedLanguageCodes, - $paths - ) { - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - $prioritizedLanguageCodes - ); - $this->configureListURLAliasesForLocation($spiUrlAliases); - - $location = $this->getLocationStub(); - $urlAlias = $urlAliasService->reverseLookup($location); - - self::assertEquals( - reset($paths), - $urlAlias->path - ); - } - - /** - * Test for the reverseLookup() method. - */ - public function testReverseLookupWithShowAllTranslations() - { - $spiUrlAlias = $this->getSpiUrlAlias(); - $urlAliasService = $this->getPartlyMockedURLAliasServiceService( - null, - ['fre-FR'], - true - ); - $this->configureListURLAliasesForLocation([$spiUrlAlias]); - - $location = $this->getLocationStub(); - $urlAlias = $urlAliasService->reverseLookup($location); - - self::assertEquals('/jedan/dva/tri', $urlAlias->path); - } - - /** - * Test for the createUrlAlias() method. - */ - public function testCreateUrlAlias() - { - $location = $this->getLocationStub(); - $this->permissionResolver - ->expects($this->once()) - ->method('canUser')->with( - $this->equalTo('content'), - $this->equalTo('urltranslator'), - $this->equalTo($location) - ) - ->willReturn(true); - - $repositoryMock = $this->getRepositoryMock(); - - $mockedService = $this->getPartlyMockedURLAliasServiceService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ - $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); - - $repositoryMock - ->expects($this->once()) - ->method('beginTransaction'); - $repositoryMock - ->expects($this->once()) - ->method('commit'); - - $urlAliasHandlerMock->expects( - $this->once() - )->method( - 'createCustomUrlAlias' - )->with( - $this->equalTo($location->id), - $this->equalTo(self::EXAMPLE_PATH), - $this->equalTo(true), - $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), - $this->equalTo(true) - )->willReturn( - new SPIUrlAlias() - ); - - $urlAlias = $mockedService->createUrlAlias( - $location, - self::EXAMPLE_PATH, - self::EXAMPLE_LANGUAGE_CODE, - true, - true - ); - - self::assertInstanceOf(URLAlias::class, $urlAlias); - } - - /** - * Test for the createUrlAlias() method. - */ - public function testCreateUrlAliasWithRollback() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Handler threw an exception'); - - $location = $this->getLocationStub(); - - $this->permissionResolver - ->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('urltranslator'), - $this->equalTo($location) - ) - ->willReturn(true); - - $repositoryMock = $this->getRepositoryMock(); - - $mockedService = $this->getPartlyMockedURLAliasServiceService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ - $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); - - $repositoryMock - ->expects($this->once()) - ->method('beginTransaction'); - $repositoryMock - ->expects($this->once()) - ->method('rollback'); - - $urlAliasHandlerMock->expects( - $this->once() - )->method( - 'createCustomUrlAlias' - )->with( - $this->equalTo($location->id), - $this->equalTo(self::EXAMPLE_PATH), - $this->equalTo(true), - $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), - $this->equalTo(true) - )->will( - $this->throwException(new Exception('Handler threw an exception')) - ); - - $mockedService->createUrlAlias( - $location, - self::EXAMPLE_PATH, - self::EXAMPLE_LANGUAGE_CODE, - true, - true - ); - } - - /** - * Test for the createUrlAlias() method. - */ - public function testCreateUrlAliasThrowsInvalidArgumentException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $location = $this->getLocationStub(); - - $mockedService = $this->getPartlyMockedURLAliasServiceService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */ - $handlerMock = $this->getPersistenceMock()->urlAliasHandler(); - - $this->permissionResolver - ->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('content'), - $this->equalTo('urltranslator'), - $this->equalTo($location) - ) - ->willReturn(true); - - $handlerMock->expects( - $this->once() - )->method( - 'createCustomUrlAlias' - )->with( - $this->equalTo($location->id), - $this->equalTo(self::EXAMPLE_PATH), - $this->equalTo(true), - $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), - $this->equalTo(true) - )->will( - $this->throwException(new ForbiddenException('Forbidden!')) - ); - - $mockedService->createUrlAlias( - $location, - self::EXAMPLE_PATH, - self::EXAMPLE_LANGUAGE_CODE, - true, - true - ); - } - - /** - * Test for the createGlobalUrlAlias() method. - */ - public function testCreateGlobalUrlAlias() - { - $resource = 'module:content/search'; - - $this->permissionResolver - ->expects($this->once()) - ->method('hasAccess') - ->with( - $this->equalTo('content'), - $this->equalTo('urltranslator') - ) - ->willReturn(true); - - $repositoryMock = $this->getRepositoryMock(); - - $mockedService = $this->getPartlyMockedURLAliasServiceService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ - $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); - - $repositoryMock - ->expects($this->once()) - ->method('beginTransaction'); - $repositoryMock - ->expects($this->once()) - ->method('commit'); - - $urlAliasHandlerMock->expects( - $this->once() - )->method( - 'createGlobalUrlAlias' - )->with( - $this->equalTo($resource), - $this->equalTo(self::EXAMPLE_PATH), - $this->equalTo(true), - $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), - $this->equalTo(true) - )->willReturn( - new SPIUrlAlias() - ); - - $urlAlias = $mockedService->createGlobalUrlAlias( - $resource, - self::EXAMPLE_PATH, - self::EXAMPLE_LANGUAGE_CODE, - true, - true - ); - - self::assertInstanceOf(URLAlias::class, $urlAlias); - } - - /** - * Test for the createGlobalUrlAlias() method. - */ - public function testCreateGlobalUrlAliasWithRollback() - { - $this->expectException(\Exception::class); - $this->expectExceptionMessage('Handler threw an exception'); - - $resource = 'module:content/search'; - - $this->permissionResolver - ->expects($this->once()) - ->method('hasAccess') - ->with( - $this->equalTo('content'), - $this->equalTo('urltranslator') - ) - ->willReturn(true); - - $repositoryMock = $this->getRepositoryMock(); - - $mockedService = $this->getPartlyMockedURLAliasServiceService(); - /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ - $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); - - $repositoryMock - ->expects($this->once()) - ->method('beginTransaction'); - $repositoryMock - ->expects($this->once()) - ->method('rollback'); - - $urlAliasHandlerMock->expects( - $this->once() - )->method( - 'createGlobalUrlAlias' - )->with( - $this->equalTo($resource), - $this->equalTo(self::EXAMPLE_PATH), - $this->equalTo(true), - $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), - $this->equalTo(true) - )->will( - $this->throwException(new Exception('Handler threw an exception')) - ); - - $mockedService->createGlobalUrlAlias( - $resource, - self::EXAMPLE_PATH, - self::EXAMPLE_LANGUAGE_CODE, - true, - true - ); - } - - /** - * Test for the createGlobalUrlAlias() method. - */ - public function testCreateGlobalUrlAliasThrowsInvalidArgumentExceptionResource() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $mockedService = $this->getPartlyMockedURLAliasServiceService(); - $this->permissionResolver - ->expects($this->once()) - ->method('hasAccess')->with( - $this->equalTo('content'), - $this->equalTo('urltranslator') - ) - ->willReturn(true); - - $mockedService->createGlobalUrlAlias( - 'invalid/resource', - self::EXAMPLE_PATH, - self::EXAMPLE_LANGUAGE_CODE, - true, - true - ); - } - - /** - * Test for the createGlobalUrlAlias() method. - */ - public function testCreateGlobalUrlAliasThrowsInvalidArgumentExceptionPath() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); - - $resource = 'module:content/search'; - $mockedService = $this->getPartlyMockedURLAliasServiceService(); - - $this->permissionResolver - ->expects($this->once()) - ->method('hasAccess') - ->with( - $this->equalTo('content'), - $this->equalTo('urltranslator') - ) - ->willReturn(true); - - $this->urlAliasHandler->expects( - $this->once() - )->method( - 'createGlobalUrlAlias' - )->with( - $this->equalTo($resource), - $this->equalTo(self::EXAMPLE_PATH), - $this->equalTo(true), - $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), - $this->equalTo(true) - )->will( - $this->throwException(new ForbiddenException('Forbidden!')) - ); - - $mockedService->createGlobalUrlAlias( - $resource, - self::EXAMPLE_PATH, - self::EXAMPLE_LANGUAGE_CODE, - true, - true - ); - } - - /** - * Test for the createGlobalUrlAlias() method. - * - * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\UrlAliasTest::testCreateUrlAlias - * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\UrlAliasTest::testCreateUrlAliasWithRollback - * @depends eZ\Publish\Core\Repository\Tests\Service\Mock\UrlAliasTest::testCreateUrlAliasThrowsInvalidArgumentException - */ - public function testCreateGlobalUrlAliasForLocation() - { - $repositoryMock = $this->getRepositoryMock(); - $mockedService = $this->getPartlyMockedURLAliasServiceService(['createUrlAlias']); - $location = $this->getLocationStub(); - $locationServiceMock = $this->createMock(LocationService::class); - - $locationServiceMock->expects( - $this->exactly(2) - )->method( - 'loadLocation' - )->with( - $this->equalTo(42) - )->willReturn( - $location - ); - - $repositoryMock->expects( - $this->exactly(2) - )->method( - 'getLocationService' - )->willReturn( - $locationServiceMock - ); - - $this->permissionResolver - ->expects($this->exactly(2)) - ->method('canUser')->with( - $this->equalTo('content'), - $this->equalTo('urltranslator'), - $this->equalTo($location) - ) - ->willReturn(true); - - $mockedService->expects( - $this->exactly(2) - )->method( - 'createUrlAlias' - )->with( - $this->equalTo($location), - $this->equalTo(self::EXAMPLE_PATH), - $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), - $this->equalTo(true), - $this->equalTo(true) - ); - - $mockedService->createGlobalUrlAlias( - 'eznode:42', - self::EXAMPLE_PATH, - self::EXAMPLE_LANGUAGE_CODE, - true, - true - ); - $mockedService->createGlobalUrlAlias( - 'module:content/view/full/42', - self::EXAMPLE_PATH, - self::EXAMPLE_LANGUAGE_CODE, - true, - true - ); - } - - /** - * @param int $id - * - * @return \eZ\Publish\Core\Repository\Values\Content\Location - */ - protected function getLocationStub($id = 42) - { - return new Location(['id' => $id]); - } - - /** - * @param object $urlAliasService - * @param array $configuration - */ - protected function setConfiguration($urlAliasService, array $configuration) - { - $refObject = new \ReflectionObject($urlAliasService); - $refProperty = $refObject->getProperty('settings'); - $refProperty->setAccessible(true); - $refProperty->setValue( - $urlAliasService, - $configuration - ); - } - - /** - * Returns the content service to test with $methods mocked. - * - * Injected Repository comes from {@see getRepositoryMock()} and persistence handler from {@see getPersistenceMock()} - * - * @param string[] $methods - * - * @return \eZ\Publish\Core\Repository\URLAliasService|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getPartlyMockedURLAliasServiceService( - array $methods = null, - array $prioritizedLanguages = ['eng-GB'], - bool $showAllTranslations = false - ) { - $languageResolverMock = $this->createMock(LanguageResolver::class); - - $languageResolverMock - ->method('getPrioritizedLanguages') - ->willReturn($prioritizedLanguages); - - $languageResolverMock - ->method('getShowAllTranslations') - ->willReturn($showAllTranslations); - - return $this->getMockBuilder(URLAliasService::class) - ->setMethods($methods) - ->setConstructorArgs( - [ - $this->getRepositoryMock(), - $this->getPersistenceMock()->urlAliasHandler(), - $this->getNameSchemaServiceMock(), - $this->permissionResolver, - $languageResolverMock, - ] - ) - ->getMock(); - } - - /** - * Test for the createUrlAlias() method. - * - * @covers \eZ\Publish\Core\Repository\URLAliasService::createUrlAlias - */ - public function testCreateUrlAliasThrowsUnauthorizedException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); - - $mockedService = $this->getPartlyMockedURLAliasServiceService(); - $location = $this->getLocationStub(); - $this->permissionResolver - ->expects($this->once()) - ->method('canUser')->with( - $this->equalTo('content'), - $this->equalTo('urltranslator'), - $this->equalTo($location) - ) - ->willReturn(false); - - $mockedService->createUrlAlias( - $location, - self::EXAMPLE_PATH, - self::EXAMPLE_LANGUAGE_CODE, - true - ); - } - - /** - * Test for the createGlobalUrlAlias() method. - * - * @covers \eZ\Publish\Core\Repository\URLAliasService::createGlobalUrlAlias - */ - public function testCreateGlobalUrlAliasThrowsUnauthorizedException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); - - $mockedService = $this->getPartlyMockedURLAliasServiceService(); - $this->permissionResolver - ->expects($this->once()) - ->method('hasAccess')->with( - $this->equalTo('content'), - $this->equalTo('urltranslator') - ) - ->willReturn(false); - - $mockedService->createGlobalUrlAlias( - 'eznode:42', - self::EXAMPLE_PATH, - self::EXAMPLE_LANGUAGE_CODE, - true, - true - ); - } - - /** - * Test for the removeAliases() method. - * - * @covers \eZ\Publish\Core\Repository\URLAliasService::removeAliases - */ - public function testRemoveAliasesThrowsUnauthorizedException() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); - - $aliasList = [new URLAlias(['isCustom' => true])]; - $mockedService = $this->getPartlyMockedURLAliasServiceService(); - $this->permissionResolver - ->expects($this->once()) - ->method('hasAccess')->with( - $this->equalTo('content'), - $this->equalTo('urltranslator') - ) - ->willReturn(false); - - $mockedService->removeAliases($aliasList); - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Helper\NameSchemaService - */ - protected function getNameSchemaServiceMock() - { - return $this->createMock(NameSchemaService::class); - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\UrlAlias[] $spiUrlAliases - */ - private function configureListURLAliasesForLocation(array $spiUrlAliases): void - { - $this->urlAliasHandler - ->expects($this->once()) - ->method('listURLAliasesForLocation') - ->with( - $this->equalTo(42), - $this->equalTo(false) - ) - ->willReturn($spiUrlAliases); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/UrlTest.php b/eZ/Publish/Core/Repository/Tests/Service/Mock/UrlTest.php deleted file mode 100644 index a536a6363d..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/UrlTest.php +++ /dev/null @@ -1,484 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; - -use DateTime; -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Query as ContentQuery; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion as ContentCriterion; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult as ContentSearchResults; -use eZ\Publish\API\Repository\Values\URL\SearchResult; -use eZ\Publish\API\Repository\Values\URL\URL; -use eZ\Publish\API\Repository\Values\URL\URLQuery; -use eZ\Publish\API\Repository\Values\URL\URLUpdateStruct; -use eZ\Publish\API\Repository\Values\URL\UsageSearchResult; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\Core\Repository\URLService; -use eZ\Publish\SPI\Persistence\URL\URL as SpiUrl; - -class UrlTest extends BaseServiceMockTest -{ - private const URL_ID = 12; - private const URL_EZ_NO = 'http://ez.no'; - private const URL_EZ_COM = 'http://ez.com'; - - /** @var \eZ\Publish\API\Repository\URLService|\PHPUnit\Framework\MockObject\MockObject */ - private $urlHandler; - - /** @var \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ - private $permissionResolver; - - protected function setUp(): void - { - parent::setUp(); - $this->urlHandler = $this->getPersistenceMockHandler('URL\\Handler'); - $this->permissionResolver = $this->getPermissionResolverMock(); - } - - public function testFindUrlsUnauthorized() - { - $this->configureUrlViewPermissionForHasAccess(false); - - $this->expectException(UnauthorizedException::class); - $this->createUrlService()->findUrls(new URLQuery()); - } - - public function testFindUrlsNonNumericOffset() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue::class); - - $query = new URLQuery(); - $query->offset = 'foo'; - - $this->createUrlService()->findUrls($query); - } - - public function testFindUrlsNonNumericLimit() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue::class); - - $query = new URLQuery(); - $query->limit = 'foo'; - - $this->createUrlService()->findUrls($query); - } - - public function testFindUrls() - { - $url = $this->getApiUrl(); - - $this->configureUrlViewPermissionForHasAccess(true); - - $query = new URLQuery(); - - $results = [ - 'count' => 1, - 'items' => [ - new SpiUrl(), - ], - ]; - - $expected = new SearchResult([ - 'totalCount' => 1, - 'items' => [$url], - ]); - - $this->urlHandler - ->expects($this->once()) - ->method('find') - ->with($query) - ->willReturn($results); - - $this->assertEquals($expected, $this->createUrlService()->findUrls($query)); - } - - public function testUpdateUrlUnauthorized() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class); - - $url = $this->getApiUrl(); - - $this->configureUrlUpdatePermission($url, false); - - $this->createUrlService()->updateUrl($url, new URLUpdateStruct()); - } - - public function testUpdateUrlNonUnique() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\InvalidArgumentException::class); - - $url = $this->getApiUrl(self::URL_ID, self::URL_EZ_NO); - - $this->configureUrlUpdatePermission($url, true); - - $struct = new URLUpdateStruct([ - 'url' => self::URL_EZ_COM, - ]); - - $urlService = $this->createUrlService(['isUnique']); - $urlService - ->expects($this->once()) - ->method('isUnique') - ->with($url->id, $struct->url) - ->willReturn(false); - - $urlService->updateUrl($url, $struct); - } - - public function testUpdateUrl() - { - $apiUrl = $this->getApiUrl(self::URL_ID, self::URL_EZ_NO); - $apiStruct = new URLUpdateStruct([ - 'url' => self::URL_EZ_COM, - 'isValid' => false, - 'lastChecked' => new DateTime(), - ]); - - $this->configurePermissions([ - ['url', 'update', $apiUrl, []], - ['url', 'view', $apiUrl, []], - ['url', 'view', new URL(['id' => self::URL_ID, 'url' => self::URL_EZ_COM, 'isValid' => true]), []], - ]); - - $urlService = $this->createUrlService(['isUnique']); - $urlService - ->expects($this->once()) - ->method('isUnique') - ->with($apiUrl->id, $apiStruct->url) - ->willReturn(true); - - $this->urlHandler - ->expects($this->once()) - ->method('updateUrl') - ->willReturnCallback(function ($id, $struct) use ($apiUrl, $apiStruct) { - $this->assertEquals($apiUrl->id, $id); - - $this->assertEquals($apiStruct->url, $struct->url); - $this->assertEquals(0, $struct->lastChecked); - $this->assertTrue($struct->isValid); - }); - - $this->urlHandler - ->method('loadById') - ->with($apiUrl->id) - ->willReturnOnConsecutiveCalls( - new SpiUrl([ - 'id' => $apiUrl->id, - 'url' => $apiUrl->url, - 'isValid' => $apiUrl->isValid, - 'lastChecked' => $apiUrl->lastChecked, - ]), - new SpiUrl([ - 'id' => $apiUrl->id, - 'url' => $apiStruct->url, - 'isValid' => true, - 'lastChecked' => 0, - ]) - ); - - $this->assertEquals(new URL([ - 'id' => $apiUrl->id, - 'url' => $apiStruct->url, - 'isValid' => true, - 'lastChecked' => null, - ]), $urlService->updateUrl($apiUrl, $apiStruct)); - } - - public function testUpdateUrlStatus() - { - $apiUrl = $this->getApiUrl(self::URL_ID, self::URL_EZ_NO); - $apiStruct = new URLUpdateStruct([ - 'isValid' => true, - 'lastChecked' => new DateTime('@' . time()), - ]); - - $urlAfterUpdate = new URL([ - 'id' => self::URL_ID, - 'url' => self::URL_EZ_NO, - 'isValid' => true, - 'lastChecked' => new DateTime('@' . time()), - ]); - - $this->configurePermissions([ - ['url', 'update', $apiUrl, []], - ['url', 'view', $apiUrl, []], - ['url', 'view', $urlAfterUpdate, []], - ]); - - $urlService = $this->createUrlService(['isUnique']); - - $this->urlHandler - ->expects($this->once()) - ->method('updateUrl') - ->willReturnCallback(function ($id, $struct) use ($apiUrl, $apiStruct) { - $this->assertEquals($apiUrl->id, $id); - - $this->assertEquals($apiUrl->url, $struct->url); - $this->assertEquals($apiStruct->lastChecked->getTimestamp(), $struct->lastChecked); - $this->assertTrue($apiStruct->isValid, $struct->isValid); - }); - - $this->urlHandler - ->method('loadById') - ->with($apiUrl->id) - ->willReturnOnConsecutiveCalls( - new SpiUrl([ - 'id' => $apiUrl->id, - 'url' => $apiUrl->url, - 'isValid' => $apiUrl->isValid, - 'lastChecked' => $apiUrl->lastChecked, - ]), - new SpiUrl([ - 'id' => $apiUrl->id, - 'url' => $apiUrl->url, - 'isValid' => $apiStruct->isValid, - 'lastChecked' => $apiStruct->lastChecked->getTimestamp(), - ]) - ); - - $this->assertEquals(new URL([ - 'id' => $apiUrl->id, - 'url' => $apiUrl->url, - 'isValid' => $apiStruct->isValid, - 'lastChecked' => $apiStruct->lastChecked, - ]), $urlService->updateUrl($apiUrl, $apiStruct)); - } - - public function testLoadByIdUnauthorized() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class); - - $this->configureUrlViewPermission( - new URL([ - 'id' => self::URL_ID, - ]), - false - ); - - $this->urlHandler - ->expects($this->once()) - ->method('loadById') - ->with(self::URL_ID) - ->willReturn(new SpiUrl([ - 'id' => self::URL_ID, - ])); - - $this->createUrlService()->loadById(self::URL_ID); - } - - public function testLoadById() - { - $url = new URL([ - 'id' => self::URL_ID, - ]); - - $this->configureUrlViewPermission($url, true); - - $this->urlHandler - ->expects($this->once()) - ->method('loadById') - ->with(self::URL_ID) - ->willReturn(new SpiUrl([ - 'id' => self::URL_ID, - ])); - - $this->assertEquals($url, $this->createUrlService()->loadById(self::URL_ID)); - } - - public function testLoadByUrlUnauthorized() - { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class); - - $url = self::URL_EZ_NO; - - $this->configureUrlViewPermission( - new URL([ - 'id' => self::URL_ID, - ]), - false - ); - - $this->urlHandler - ->expects($this->once()) - ->method('loadByUrl') - ->with($url) - ->willReturn(new SpiUrl([ - 'id' => self::URL_ID, - ])); - - $this->createUrlService()->loadByUrl(self::URL_EZ_NO); - } - - public function testLoadByUrl() - { - $url = self::URL_EZ_NO; - - $apiUrl = new URL([ - 'url' => $url, - ]); - - $this->configureUrlViewPermission($apiUrl, true); - - $this->urlHandler - ->expects($this->once()) - ->method('loadByUrl') - ->with($url) - ->willReturn(new SpiUrl([ - 'url' => $url, - ])); - - $this->assertEquals($apiUrl, $this->createUrlService()->loadByUrl($url)); - } - - /** - * @dataProvider dateProviderForFindUsages - */ - public function testFindUsages($offset, $limit, ContentQuery $expectedQuery, array $usages) - { - $url = $this->getApiUrl(self::URL_ID, self::URL_EZ_NO); - - if (!empty($usages)) { - $searchService = $this->createMock(SearchService::class); - $searchService - ->expects($this->once()) - ->method('findContentInfo') - ->willReturnCallback(function ($query) use ($expectedQuery, $usages) { - $this->assertEquals($expectedQuery, $query); - - return new ContentSearchResults([ - 'searchHits' => array_map(static function ($id) { - return new SearchHit([ - 'valueObject' => new ContentInfo([ - 'id' => $id, - ]), - ]); - }, $usages), - 'totalCount' => count($usages), - ]); - }); - - $this->getRepositoryMock() - ->expects($this->once()) - ->method('getSearchService') - ->willReturn($searchService); - } - - $this->urlHandler - ->expects($this->once()) - ->method('findUsages') - ->with($url->id) - ->willReturn($usages); - - $usageSearchResult = $this->createUrlService()->findUsages($url, $offset, $limit); - - $this->assertInstanceOf(UsageSearchResult::class, $usageSearchResult); - $this->assertEquals(count($usages), $usageSearchResult->totalCount); - foreach ($usageSearchResult as $contentInfo) { - $this->assertContains($contentInfo->id, $usages); - } - } - - public function dateProviderForFindUsages() - { - return [ - [ - 10, -1, new ContentQuery([ - 'filter' => new ContentCriterion\MatchNone(), - 'offset' => 10, - ]), [], - ], - [ - 10, -1, new ContentQuery([ - 'filter' => new ContentCriterion\LogicalAnd([ - new ContentCriterion\ContentId([1, 2, 3]), - new ContentCriterion\Visibility(ContentCriterion\Visibility::VISIBLE), - ]), - 'offset' => 10, - ]), [1, 2, 3], - ], - [ - 10, 10, new ContentQuery([ - 'filter' => new ContentCriterion\LogicalAnd([ - new ContentCriterion\ContentId([1, 2, 3]), - new ContentCriterion\Visibility(ContentCriterion\Visibility::VISIBLE), - ]), - 'offset' => 10, - 'limit' => 10, - ]), [1, 2, 3], - ], - ]; - } - - public function testCreateUpdateStruct() - { - $this->assertEquals(new URLUpdateStruct(), $this->createUrlService()->createUpdateStruct()); - } - - protected function configureUrlViewPermissionForHasAccess($hasAccess = false) - { - $this->permissionResolver - ->expects($this->once()) - ->method('hasAccess') - ->with('url', 'view') - ->willReturn($hasAccess); - } - - protected function configureUrlViewPermission($object, $hasAccess = false) - { - $this->permissionResolver - ->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('url'), - $this->equalTo('view'), - $this->equalTo($object) - ) - ->will($this->returnValue($hasAccess)); - } - - protected function configureUrlUpdatePermission($object, $hasAccess = false) - { - $this->permissionResolver - ->expects($this->once()) - ->method('canUser') - ->with( - $this->equalTo('url'), - $this->equalTo('update'), - $this->equalTo($object) - ) - ->will($this->returnValue($hasAccess)); - } - - protected function configurePermissions(array $permissions) - { - $this->permissionResolver - ->expects($this->exactly(count($permissions))) - ->method('canUser') - ->withConsecutive(...$permissions) - ->willReturn(true); - } - - /** - * @return \eZ\Publish\API\Repository\URLService|\PHPUnit\Framework\MockObject\MockObject - */ - private function createUrlService(array $methods = null) - { - return $this - ->getMockBuilder(URLService::class) - ->setConstructorArgs([$this->getRepositoryMock(), $this->urlHandler, $this->permissionResolver]) - ->setMethods($methods) - ->getMock(); - } - - private function getApiUrl($id = null, $url = null) - { - return new URL(['id' => $id, 'url' => $url]); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/UserPasswordValidatorTest.php b/eZ/Publish/Core/Repository/Tests/Service/Mock/UserPasswordValidatorTest.php deleted file mode 100644 index eefebe7886..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/UserPasswordValidatorTest.php +++ /dev/null @@ -1,196 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; - -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\Repository\Validator\UserPasswordValidator; -use eZ\Publish\Core\Search\Tests\TestCase; - -class UserPasswordValidatorTest extends TestCase -{ - /** - * @covers \eZ\Publish\Core\Repository\Validator\UserPasswordValidator::validate - * - * @dataProvider dateProviderForValidate - */ - public function testValidate(array $constraints, string $password, array $expectedErrors) - { - $validator = new UserPasswordValidator($constraints); - - $this->assertEqualsCanonicalizing($expectedErrors, $validator->validate($password), ''); - } - - public function dateProviderForValidate(): array - { - return [ - [ - [ - 'minLength' => -1, - 'requireAtLeastOneLowerCaseCharacter' => false, - 'requireAtLeastOneUpperCaseCharacter' => false, - 'requireAtLeastOneNumericCharacter' => false, - 'requireAtLeastOneNonAlphanumericCharacter' => false, - ], - 'pass', - [/* No errors */], - ], - [ - [ - 'minLength' => 6, - 'requireAtLeastOneLowerCaseCharacter' => false, - 'requireAtLeastOneUpperCaseCharacter' => false, - 'requireAtLeastOneNumericCharacter' => false, - 'requireAtLeastOneNonAlphanumericCharacter' => false, - ], - '123', - [ - new ValidationError('User password must be at least %length% characters long', null, [ - '%length%' => 6, - ], 'password'), - ], - ], - [ - [ - 'minLength' => 6, - 'requireAtLeastOneLowerCaseCharacter' => false, - 'requireAtLeastOneUpperCaseCharacter' => false, - 'requireAtLeastOneNumericCharacter' => false, - 'requireAtLeastOneNonAlphanumericCharacter' => false, - ], - '123456!', - [/* No errors */], - ], - [ - [ - 'minLength' => -1, - 'requireAtLeastOneLowerCaseCharacter' => true, - 'requireAtLeastOneUpperCaseCharacter' => false, - 'requireAtLeastOneNumericCharacter' => false, - 'requireAtLeastOneNonAlphanumericCharacter' => false, - ], - 'PASS', - [ - new ValidationError('User password must include at least one lower case letter', null, [], 'password'), - ], - ], - [ - [ - 'minLength' => -1, - 'requireAtLeastOneLowerCaseCharacter' => true, - 'requireAtLeastOneUpperCaseCharacter' => false, - 'requireAtLeastOneNumericCharacter' => false, - 'requireAtLeastOneNonAlphanumericCharacter' => false, - ], - 'PaSS', - [/* No errors */], - ], - [ - [ - 'minLength' => -1, - 'requireAtLeastOneLowerCaseCharacter' => false, - 'requireAtLeastOneUpperCaseCharacter' => true, - 'requireAtLeastOneNumericCharacter' => false, - 'requireAtLeastOneNonAlphanumericCharacter' => false, - ], - 'pass', - [ - new ValidationError('User password must include at least one upper case letter', null, [], 'password'), - ], - ], - [ - [ - 'minLength' => -1, - 'requireAtLeastOneLowerCaseCharacter' => false, - 'requireAtLeastOneUpperCaseCharacter' => true, - 'requireAtLeastOneNumericCharacter' => false, - 'requireAtLeastOneNonAlphanumericCharacter' => false, - ], - 'pAss', - [/* No errors */], - ], - [ - [ - 'minLength' => -1, - 'requireAtLeastOneLowerCaseCharacter' => false, - 'requireAtLeastOneUpperCaseCharacter' => false, - 'requireAtLeastOneNumericCharacter' => true, - 'requireAtLeastOneNonAlphanumericCharacter' => false, - ], - 'pass', - [ - new ValidationError('User password must include at least one number', null, [], 'password'), - ], - ], - [ - [ - 'minLength' => -1, - 'requireAtLeastOneLowerCaseCharacter' => false, - 'requireAtLeastOneUpperCaseCharacter' => false, - 'requireAtLeastOneNumericCharacter' => true, - 'requireAtLeastOneNonAlphanumericCharacter' => false, - ], - 'pass1', - [/* No errors */], - ], - [ - [ - 'minLength' => -1, - 'requireAtLeastOneLowerCaseCharacter' => false, - 'requireAtLeastOneUpperCaseCharacter' => false, - 'requireAtLeastOneNumericCharacter' => false, - 'requireAtLeastOneNonAlphanumericCharacter' => true, - ], - 'pass', - [ - new ValidationError('User password must include at least one special character', null, [], 'password'), - ], - ], - [ - [ - 'minLength' => -1, - 'requireAtLeastOneLowerCaseCharacter' => false, - 'requireAtLeastOneUpperCaseCharacter' => false, - 'requireAtLeastOneNumericCharacter' => false, - 'requireAtLeastOneNonAlphanumericCharacter' => true, - ], - 'pass!', - [/* No errors */], - ], - [ - [ - 'minLength' => 6, - 'requireAtLeastOneLowerCaseCharacter' => true, - 'requireAtLeastOneUpperCaseCharacter' => true, - 'requireAtLeastOneNumericCharacter' => true, - 'requireAtLeastOneNonAlphanumericCharacter' => true, - ], - 'asdf', - [ - new ValidationError('User password must be at least %length% characters long', null, [ - '%length%' => 6, - ], 'password'), - new ValidationError('User password must include at least one upper case letter', null, [], 'password'), - new ValidationError('User password must include at least one number', null, [], 'password'), - new ValidationError('User password must include at least one special character', null, [], 'password'), - ], - ], - [ - [ - 'minLength' => 6, - 'requireAtLeastOneLowerCaseCharacter' => true, - 'requireAtLeastOneUpperCaseCharacter' => true, - 'requireAtLeastOneNumericCharacter' => true, - 'requireAtLeastOneNonAlphanumericCharacter' => true, - ], - 'H@xxi0r!', - [/* No errors */], - ], - ]; - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/UserTest.php b/eZ/Publish/Core/Repository/Tests/Service/Mock/UserTest.php deleted file mode 100644 index 1230a0b5f9..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/UserTest.php +++ /dev/null @@ -1,145 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; - -use Exception; -use eZ\Publish\API\Repository\ContentService as APIContentService; -use eZ\Publish\API\Repository\PasswordHashService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\UserService as APIUserService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo as APIContentInfo; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\Core\Repository\User\PasswordValidatorInterface; -use eZ\Publish\Core\Repository\UserService; -use eZ\Publish\SPI\Persistence\User\Handler as PersistenceUserHandler; -use eZ\Publish\SPI\Persistence\User\RoleAssignment; - -/** - * @covers \eZ\Publish\Core\Repository\UserService - */ -class UserTest extends BaseServiceMockTest -{ - private const MOCKED_USER_ID = 42; - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testDeleteUser(): void - { - $repository = $this->getRepositoryMock(); - $userService = $this->getPartlyMockedUserService(['loadUser']); - $contentService = $this->createMock(APIContentService::class); - /* @var \PHPUnit\Framework\MockObject\MockObject $userHandler */ - $userHandler = $this->getPersistenceMock()->userHandler(); - - $user = $this->createMock(APIUser::class); - $contentInfo = $this->createMock(APIContentInfo::class); - $this->mockDeleteUserFlow($repository, $userService, $contentService, $user, $contentInfo, $userHandler); - - $contentService->expects(self::once())->method('deleteContent')->with($contentInfo); - $userHandler->expects(self::once())->method('delete')->with(self::MOCKED_USER_ID); - $repository->expects(self::once())->method('commit'); - - $userService->deleteUser($user); - } - - /** - * @covers \eZ\Publish\Core\Repository\UserService::deleteUser - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function testDeleteUserWithRollback(): void - { - $repository = $this->getRepositoryMock(); - $userService = $this->getPartlyMockedUserService(['loadUser']); - $contentService = $this->createMock(APIContentService::class); - /* @var \eZ\Publish\SPI\Persistence\User\Handler&\PHPUnit\Framework\MockObject\MockObject $userHandler */ - $userHandler = $this->getPersistenceMock()->userHandler(); - - $user = $this->createMock(APIUser::class); - $contentInfo = $this->createMock(APIContentInfo::class); - $this->mockDeleteUserFlow($repository, $userService, $contentService, $user, $contentInfo, $userHandler); - - $exception = new Exception(); - $contentService->expects(self::once()) - ->method('deleteContent') - ->with($contentInfo) - ->willThrowException($exception); - - $repository->expects(self::once())->method('rollback'); - - $this->expectExceptionObject($exception); - $userService->deleteUser($user); - } - - /** - * Returns the User service to test with $methods mocked. - * - * Injected Repository comes from {@see getRepositoryMock()} and persistence handler from {@see getPersistenceMock()} - * - * @param string[] $methods - * - * @return \eZ\Publish\API\Repository\UserService&\PHPUnit\Framework\MockObject\MockObject - */ - protected function getPartlyMockedUserService(array $methods = null): APIUserService - { - return $this->getMockBuilder(UserService::class) - ->onlyMethods($methods) - ->setConstructorArgs( - [ - $this->getRepositoryMock(), - $this->getPermissionResolverMock(), - $this->getPersistenceMock()->userHandler(), - $this->getPersistenceMock()->locationHandler(), - $this->createMock(PasswordHashService::class), - $this->createMock(PasswordValidatorInterface::class), - ] - ) - ->getMock(); - } - - /** - * @param \eZ\Publish\API\Repository\Repository&\PHPUnit\Framework\MockObject\MockObject $repository - * @param \eZ\Publish\API\Repository\UserService&\PHPUnit\Framework\MockObject\MockObject $userService - * @param \eZ\Publish\API\Repository\ContentService&\PHPUnit\Framework\MockObject\MockObject $contentService - * @param \eZ\Publish\API\Repository\Values\User\User&\PHPUnit\Framework\MockObject\MockObject $user - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo&\PHPUnit\Framework\MockObject\MockObject $contentInfo - * @param \eZ\Publish\SPI\Persistence\User\Handler&\PHPUnit\Framework\MockObject\MockObject $userHandler - */ - private function mockDeleteUserFlow( - Repository $repository, - APIUserService $userService, - APIContentService $contentService, - User $user, - APIContentInfo $contentInfo, - PersistenceUserHandler $userHandler - ): void { - $loadedUser = $this->createMock(APIUser::class); - $versionInfo = $this->createMock(APIVersionInfo::class); - - $user->method('__get')->with('id')->willReturn(self::MOCKED_USER_ID); - $versionInfo->method('getContentInfo')->willReturn($contentInfo); - $loadedUser->method('getVersionInfo')->willReturn($versionInfo); - $loadedUser->method('__get')->with('id')->willReturn(self::MOCKED_USER_ID); - - $userService->method('loadUser')->with(self::MOCKED_USER_ID)->willReturn($loadedUser); - - $userHandler - ->expects(self::once()) - ->method('loadRoleAssignmentsByGroupId') - ->with(self::MOCKED_USER_ID) - ->willReturn([new RoleAssignment(['id' => 1])]); - - $userHandler->method('removeRoleAssignment')->with(1); - - $repository->expects(self::once())->method('beginTransaction'); - $repository->expects(self::once())->method('getContentService')->willReturn($contentService); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/User/PasswordHashServiceTest.php b/eZ/Publish/Core/Repository/Tests/User/PasswordHashServiceTest.php deleted file mode 100644 index 5416abbd64..0000000000 --- a/eZ/Publish/Core/Repository/Tests/User/PasswordHashServiceTest.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Tests\User; - -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\Core\Repository\User\PasswordHashService; -use PHPUnit\Framework\TestCase; - -final class PasswordHashServiceTest extends TestCase -{ - private const NON_EXISTING_PASSWORD_HASH = PHP_INT_MAX; - - /** @var \eZ\Publish\Core\Repository\User\PasswordHashService */ - private $passwordHashService; - - protected function setUp(): void - { - $this->passwordHashService = new PasswordHashService(); - } - - public function testGetSupportedHashTypes(): void - { - $this->assertEquals( - [ - User::PASSWORD_HASH_BCRYPT, - User::PASSWORD_HASH_PHP_DEFAULT, - ], - $this->passwordHashService->getSupportedHashTypes() - ); - } - - public function testIsHashTypeSupported(): void - { - $this->assertTrue($this->passwordHashService->isHashTypeSupported(User::DEFAULT_PASSWORD_HASH)); - $this->assertFalse($this->passwordHashService->isHashTypeSupported(self::NON_EXISTING_PASSWORD_HASH)); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Values/Content/ContentInfoTest.php b/eZ/Publish/Core/Repository/Tests/Values/Content/ContentInfoTest.php deleted file mode 100644 index 6317a96f7e..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Values/Content/ContentInfoTest.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Values\Content; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use PHPUnit\Framework\TestCase; - -class ContentInfoTest extends TestCase -{ - /** - * @covers \eZ\Publish\API\Repository\Values\Content\ContentInfo::getProperties - */ - public function testObjectProperties() - { - $object = new ContentInfo(); - $properties = $object->attributes(); - self::assertNotContains('internalFields', $properties, 'Internal property found '); - self::assertContains('contentTypeId', $properties, 'Property not found'); - self::assertContains('id', $properties, 'Property not found'); - self::assertContains('name', $properties, 'Property not found'); - self::assertContains('sectionId', $properties, 'Property not found'); - self::assertContains('currentVersionNo', $properties, 'Property not found'); - self::assertContains('published', $properties, 'Property not found'); - self::assertContains('ownerId', $properties, 'Property not found'); - self::assertContains('modificationDate', $properties, 'Property not found'); - self::assertContains('publishedDate', $properties, 'Property not found'); - self::assertContains('alwaysAvailable', $properties, 'Property not found'); - self::assertContains('remoteId', $properties, 'Property not found'); - self::assertContains('mainLanguageCode', $properties, 'Property not found'); - self::assertContains('mainLocationId', $properties, 'Property not found'); - - // check for duplicates and double check existence of property - $propertiesHash = []; - foreach ($properties as $property) { - if (isset($propertiesHash[$property])) { - self::fail("Property '{$property}' exists several times in properties list"); - } elseif (!isset($object->$property)) { - self::fail("Property '{$property}' does not exist on object, even though it was hinted to be there"); - } - $propertiesHash[$property] = 1; - } - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Values/Content/ContentTest.php b/eZ/Publish/Core/Repository/Tests/Values/Content/ContentTest.php deleted file mode 100644 index 769b83ffac..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Values/Content/ContentTest.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Values\Content; - -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\Content\Content; -use PHPUnit\Framework\TestCase; - -class ContentTest extends TestCase -{ - /** - * @covers \eZ\Publish\Core\Repository\Values\Content\Content::getProperties - */ - public function testObjectProperties() - { - $object = new Content(['internalFields' => []]); - $properties = $object->attributes(); - self::assertNotContains('internalFields', $properties, 'Internal property found '); - self::assertContains('id', $properties, 'Property not found '); - self::assertContains('fields', $properties, 'Property not found '); - self::assertContains('versionInfo', $properties, 'Property not found '); - self::assertContains('contentInfo', $properties, 'Property not found '); - - // check for duplicates and double check existence of property - $propertiesHash = []; - foreach ($properties as $property) { - if (isset($propertiesHash[$property])) { - self::fail("Property '{$property}' exists several times in properties list"); - } elseif (!isset($object->$property)) { - self::fail("Property '{$property}' does not exist on object, even though it was hinted to be there"); - } - $propertiesHash[$property] = 1; - } - } - - /** - * Test getName method. - * - * @covers \eZ\Publish\Core\Repository\Values\Content\Content::getName - */ - public function testGetName() - { - $name = 'Translated name'; - $versionInfoMock = $this->createMock(VersionInfo::class); - $versionInfoMock->expects($this->once()) - ->method('getName') - ->willReturn($name); - - $object = new Content(['versionInfo' => $versionInfoMock]); - - $this->assertEquals($name, $object->getName()); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Values/Content/LocationTest.php b/eZ/Publish/Core/Repository/Tests/Values/Content/LocationTest.php deleted file mode 100644 index f43a2616e7..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Values/Content/LocationTest.php +++ /dev/null @@ -1,104 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Values\Content; - -use eZ\Publish\API\Repository\Tests\Values\ValueObjectTestTrait; -use eZ\Publish\Core\Repository\Values\Content\Location; -use PHPUnit\Framework\TestCase; - -/** - * Test internal integrity of @see \eZ\Publish\Core\Repository\Values\Content\Location ValueObject. - */ -class LocationTest extends TestCase -{ - use ValueObjectTestTrait; - - /** - * Test a new class and default values on properties. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Location::__construct - */ - public function testNewClass() - { - $location = new Location(); - - $this->assertPropertiesCorrect( - [ - 'id' => null, - 'contentInfo' => null, - 'priority' => null, - 'hidden' => null, - 'invisible' => null, - 'remoteId' => null, - 'parentLocationId' => null, - 'pathString' => null, - 'path' => [], - 'depth' => null, - 'sortField' => null, - 'sortOrder' => null, - ], - $location - ); - } - - /** - * Test retrieving missing property. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Location::__get - */ - public function testMissingProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); - - $location = new Location(); - $value = $location->notDefined; - self::fail('Succeeded getting non existing property'); - } - - /** - * Test setting read only property. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Location::__set - */ - public function testReadOnlyProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $location = new Location(); - $location->id = 42; - self::fail('Succeeded setting read only property'); - } - - /** - * Test if property exists. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Location::__isset - */ - public function testIsPropertySet() - { - $location = new Location(); - $value = isset($location->notDefined); - self::assertFalse($value); - - $value = isset($location->id); - self::assertTrue($value); - } - - /** - * Test unsetting a property. - * - * @covers \eZ\Publish\API\Repository\Values\Content\Location::__unset - */ - public function testUnsetProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $location = new Location(['id' => 2]); - unset($location->id); - self::fail('Unsetting read-only property succeeded'); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Values/Content/TrashItemTest.php b/eZ/Publish/Core/Repository/Tests/Values/Content/TrashItemTest.php deleted file mode 100644 index ac72d2172b..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Values/Content/TrashItemTest.php +++ /dev/null @@ -1,103 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Values\Content; - -use eZ\Publish\API\Repository\Tests\Values\ValueObjectTestTrait; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\Core\Repository\Values\Content\TrashItem; -use PHPUnit\Framework\TestCase; - -class TrashItemTest extends TestCase -{ - use ValueObjectTestTrait; - - /** - * @covers \eZ\Publish\Core\Repository\Values\Content\TrashItem::__construct - */ - public function testNewClass() - { - // create ContentInfo to be able to retrieve the contentId property via magic method - $contentInfo = new ContentInfo(); - $trashItem = new TrashItem(['contentInfo' => $contentInfo]); - - $this->assertPropertiesCorrect( - [ - 'contentInfo' => $contentInfo, - 'contentId' => null, - 'id' => null, - 'priority' => null, - 'hidden' => null, - 'invisible' => null, - 'remoteId' => null, - 'parentLocationId' => null, - 'pathString' => null, - 'path' => [], - 'depth' => null, - 'sortField' => null, - 'sortOrder' => null, - ], - $trashItem - ); - } - - /** - * Test retrieving missing property. - * - * @covers \eZ\Publish\API\Repository\Values\Content\TrashItem::__get - */ - public function testMissingProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); - - $trashItem = new TrashItem(); - $value = $trashItem->notDefined; - self::fail('Succeeded getting non existing property'); - } - - /** - * Test setting read only property. - * - * @covers \eZ\Publish\API\Repository\Values\Content\TrashItem::__set - */ - public function testReadOnlyProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $trashItem = new TrashItem(); - $trashItem->id = 42; - self::fail('Succeeded setting read only property'); - } - - /** - * Test if property exists. - * - * @covers \eZ\Publish\API\Repository\Values\Content\TrashItem::__isset - */ - public function testIsPropertySet() - { - $trashItem = new TrashItem(); - $value = isset($trashItem->notDefined); - self::assertFalse($value); - - $value = isset($trashItem->id); - self::assertTrue($value); - } - - /** - * Test unsetting a property. - * - * @covers \eZ\Publish\API\Repository\Values\Content\TrashItem::__unset - */ - public function testUnsetProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $trashItem = new TrashItem(['id' => 2]); - unset($trashItem->id); - self::fail('Unsetting read-only property succeeded'); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Values/Content/VersionInfoTest.php b/eZ/Publish/Core/Repository/Tests/Values/Content/VersionInfoTest.php deleted file mode 100644 index 95751716fb..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Values/Content/VersionInfoTest.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Values\Content; - -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use PHPUnit\Framework\TestCase; - -class VersionInfoTest extends TestCase -{ - public function testIsDraft() - { - $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_DRAFT); - self::assertTrue($versionInfo->isDraft()); - - $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_ARCHIVED); - self::assertFalse($versionInfo->isDraft()); - $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_PUBLISHED); - self::assertFalse($versionInfo->isDraft()); - } - - public function testIsPublished() - { - $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_PUBLISHED); - self::assertTrue($versionInfo->isPublished()); - - $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_DRAFT); - self::assertFalse($versionInfo->isPublished()); - $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_ARCHIVED); - self::assertFalse($versionInfo->isPublished()); - } - - public function testIsArchived() - { - $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_ARCHIVED); - self::assertTrue($versionInfo->isArchived()); - - $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_DRAFT); - self::assertFalse($versionInfo->isArchived()); - $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_PUBLISHED); - self::assertFalse($versionInfo->isArchived()); - } - - private function createVersionInfoWithStatus($status) - { - return new VersionInfo([ - 'status' => $status, - ]); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Values/ContentType/ContentTypeTest.php b/eZ/Publish/Core/Repository/Tests/Values/ContentType/ContentTypeTest.php deleted file mode 100644 index 315277e778..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Values/ContentType/ContentTypeTest.php +++ /dev/null @@ -1,174 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Tests\Values\ContentType; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCollection as APIFieldDefinitionCollection; -use eZ\Publish\Core\Repository\Values\ContentType\ContentType; -use PHPUnit\Framework\TestCase; - -class ContentTypeTest extends TestCase -{ - private const EXAMPLE_FIELD_DEFINITION_IDENTIFIER = 'example'; - private const EXAMPLE_FIELD_TYPE_IDENTIFIER = 'ezcustom'; - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\ContentType::getProperties - */ - public function testObjectProperties(): void - { - $object = new ContentType([ - 'fieldDefinitions' => $this->createMock(APIFieldDefinitionCollection::class), - ]); - - $properties = $object->attributes(); - - self::assertNotContains('internalFields', $properties, 'Internal property found '); - self::assertContains('contentTypeGroups', $properties, 'Property not found'); - self::assertContains('fieldDefinitions', $properties, 'Property not found'); - self::assertContains('id', $properties, 'Property not found'); - self::assertContains('status', $properties, 'Property not found'); - self::assertContains('identifier', $properties, 'Property not found'); - self::assertContains('creationDate', $properties, 'Property not found'); - self::assertContains('modificationDate', $properties, 'Property not found'); - self::assertContains('creatorId', $properties, 'Property not found'); - self::assertContains('modifierId', $properties, 'Property not found'); - self::assertContains('remoteId', $properties, 'Property not found'); - self::assertContains('urlAliasSchema', $properties, 'Property not found'); - self::assertContains('nameSchema', $properties, 'Property not found'); - self::assertContains('isContainer', $properties, 'Property not found'); - self::assertContains('mainLanguageCode', $properties, 'Property not found'); - self::assertContains('defaultAlwaysAvailable', $properties, 'Property not found'); - self::assertContains('defaultSortField', $properties, 'Property not found'); - self::assertContains('defaultSortOrder', $properties, 'Property not found'); - - // check for duplicates and double check existence of property - $propertiesHash = []; - foreach ($properties as $property) { - if (isset($propertiesHash[$property])) { - self::fail("Property '{$property}' exists several times in properties list"); - } elseif (!isset($object->$property)) { - self::fail("Property '{$property}' does not exist on object, even though it was hinted to be there"); - } - - $propertiesHash[$property] = 1; - } - } - - public function testGetFieldDefinition(): void - { - $fieldDefinition = $this->createMock(FieldDefinition::class); - - $fieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); - - $fieldDefinitionCollection - ->expects($this->once()) - ->method('has') - ->with(self::EXAMPLE_FIELD_DEFINITION_IDENTIFIER) - ->willReturn(true); - - $fieldDefinitionCollection - ->expects($this->once()) - ->method('get') - ->with(self::EXAMPLE_FIELD_DEFINITION_IDENTIFIER) - ->willReturn($fieldDefinition); - - $contentType = new ContentType([ - 'fieldDefinitions' => $fieldDefinitionCollection, - ]); - - $this->assertEquals( - $fieldDefinition, - $contentType->getFieldDefinition(self::EXAMPLE_FIELD_DEFINITION_IDENTIFIER) - ); - } - - public function testHasFieldDefinition(): void - { - $fieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); - $fieldDefinitionCollection - ->expects($this->once()) - ->method('has') - ->with(self::EXAMPLE_FIELD_DEFINITION_IDENTIFIER) - ->willReturn(true); - - $contentType = new ContentType([ - 'fieldDefinitions' => $fieldDefinitionCollection, - ]); - - $this->assertTrue( - $contentType->hasFieldDefinition(self::EXAMPLE_FIELD_DEFINITION_IDENTIFIER) - ); - } - - public function testHasFieldDefinitionOfType(): void - { - $fieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); - $fieldDefinitionCollection - ->expects($this->once()) - ->method('anyOfType') - ->with(self::EXAMPLE_FIELD_TYPE_IDENTIFIER) - ->willReturn(true); - - $contentType = new ContentType([ - 'fieldDefinitions' => $fieldDefinitionCollection, - ]); - - $this->assertTrue( - $contentType->hasFieldDefinitionOfType(self::EXAMPLE_FIELD_TYPE_IDENTIFIER) - ); - } - - public function testGetFieldDefinitionsOfType(): void - { - $expectedFieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); - - $fieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); - $fieldDefinitionCollection - ->expects($this->once()) - ->method('filterByType') - ->with(self::EXAMPLE_FIELD_TYPE_IDENTIFIER) - ->willReturn($expectedFieldDefinitionCollection); - - $contentType = new ContentType([ - 'fieldDefinitions' => $fieldDefinitionCollection, - ]); - - $this->assertEquals( - $expectedFieldDefinitionCollection, - $contentType->getFieldDefinitionsOfType(self::EXAMPLE_FIELD_TYPE_IDENTIFIER) - ); - } - - public function testGetFirstFieldDefinitionOfType(): void - { - $expectedFieldDefinition = $this->createMock(FieldDefinition::class); - - $filteredFieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); - $filteredFieldDefinitionCollection - ->method('first') - ->willReturn($expectedFieldDefinition); - - $fieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); - $fieldDefinitionCollection - ->expects($this->once()) - ->method('filterByType') - ->with(self::EXAMPLE_FIELD_TYPE_IDENTIFIER) - ->willReturn($filteredFieldDefinitionCollection); - - $contentType = new ContentType([ - 'fieldDefinitions' => $fieldDefinitionCollection, - ]); - - $this->assertEquals( - $expectedFieldDefinition, - $contentType->getFirstFieldDefinitionOfType(self::EXAMPLE_FIELD_TYPE_IDENTIFIER) - ); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Values/ContentType/FieldDefinitionCollectionTest.php b/eZ/Publish/Core/Repository/Tests/Values/ContentType/FieldDefinitionCollectionTest.php deleted file mode 100644 index bb543f316a..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Values/ContentType/FieldDefinitionCollectionTest.php +++ /dev/null @@ -1,396 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Tests\Values\ContentType; - -use Closure; -use eZ\Publish\API\Repository\Exceptions\OutOfBoundsException; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection; -use PHPUnit\Framework\TestCase; - -final class FieldDefinitionCollectionTest extends TestCase -{ - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::get - */ - public function testGet(): void - { - list($a, $b, $c) = $this->createFieldDefinitions('A', 'B', 'C'); - - $collection = new FieldDefinitionCollection([$a, $b, $c]); - - $this->assertEquals($a, $collection->get('A')); - $this->assertEquals($b, $collection->get('B')); - $this->assertEquals($c, $collection->get('C')); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::get - */ - public function testGetThrowsOutOfBoundsExceptionForNonExistingFieldDefinition(): void - { - $this->expectException(OutOfBoundsException::class); - $this->expectExceptionMessage("Field Definition Collection does not contain element with identifier 'Z'"); - - $collection = new FieldDefinitionCollection( - $this->createFieldDefinitions('A', 'B', 'C') - ); - - $collection->get('Z'); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::has - */ - public function testHasReturnTrueForExistingFieldDefinition(): void - { - $collection = new FieldDefinitionCollection( - $this->createFieldDefinitions('A', 'B', 'C') - ); - - $this->assertTrue($collection->has('A')); - $this->assertTrue($collection->has('B')); - $this->assertTrue($collection->has('C')); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::has - */ - public function testHasReturnFalseForNonExistingFieldDefinition(): void - { - $collection = new FieldDefinitionCollection( - $this->createFieldDefinitions('A', 'B', 'C') - ); - - $this->assertFalse($collection->has('Z')); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::isEmpty - */ - public function testIsEmptyReturnsTrueForEmptyCollection(): void - { - $collection = new FieldDefinitionCollection(); - - $this->assertTrue($collection->isEmpty()); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::isEmpty - */ - public function testIsEmptyReturnsFalseForNonEmptyCollection(): void - { - $collection = new FieldDefinitionCollection([ - $this->createFieldDefinition('Example'), - ]); - - $this->assertFalse($collection->isEmpty()); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::first - */ - public function testFirstThrowsOutOfBoundsExceptionForEmptyCollection(): void - { - $this->expectException(OutOfBoundsException::class); - $this->expectExceptionMessage('Field Definition Collection is empty'); - - $collection = new FieldDefinitionCollection(); - $collection->first(); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::first - */ - public function testFirstReturnsFieldDefinitionForNonEmptyCollection(): void - { - list($a, $b, $c) = $this->createFieldDefinitions('A', 'B', 'C'); - - $collection = new FieldDefinitionCollection([$a, $b, $c]); - - $this->assertEquals($a, $collection->first()); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::last - */ - public function testLastReturnsFieldDefinitionForNonEmptyCollection(): void - { - list($a, $b, $c) = $this->createFieldDefinitions('A', 'B', 'C'); - - $collection = new FieldDefinitionCollection([$a, $b, $c]); - - $this->assertEquals($c, $collection->last()); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::last - */ - public function testLastThrowsOutOfBoundsExceptionForEmptyCollection(): void - { - $this->expectException(OutOfBoundsException::class); - $this->expectExceptionMessage('Field Definition Collection is empty'); - - $collection = new FieldDefinitionCollection(); - $collection->last(); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::first - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::last - */ - public function testFirstAndLastAreEqualForCollectionWithOneElement(): void - { - $fieldDefinition = $this->createFieldDefinition('Example'); - - $collection = new FieldDefinitionCollection([$fieldDefinition]); - - $this->assertEquals($fieldDefinition, $collection->first()); - $this->assertEquals($fieldDefinition, $collection->last()); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::count - */ - public function testCountForNonEmptyCollection(): void - { - list($a, $b, $c) = $this->createFieldDefinitions('A', 'B', 'C'); - - $collection = new FieldDefinitionCollection([$a, $b, $c]); - - $this->assertEquals(3, $collection->count()); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::count - */ - public function testCountReturnsZeroForEmptyCollection(): void - { - $collection = new FieldDefinitionCollection(); - - $this->assertEquals(0, $collection->count()); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::map - */ - public function testMap(): void - { - $collection = new FieldDefinitionCollection($this->createFieldDefinitions('A', 'B', 'C')); - - $closure = static function (FieldDefinition $fieldDefinition): string { - return strtolower($fieldDefinition->identifier); - }; - - $this->assertEquals(['a', 'b', 'c'], $collection->map($closure)); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::filter - */ - public function testFilter(): void - { - list($a, $b, $c) = $this->createFieldDefinitions('A', 'B', 'C'); - - $collection = new FieldDefinitionCollection([$a, $b, $c]); - - $this->assertEquals( - new FieldDefinitionCollection([$a, $c]), - $collection->filter($this->getIdentifierIsEqualPredicate('A', 'C')) - ); - - $this->assertEquals( - new FieldDefinitionCollection(), - $collection->filter($this->getContraction()) - ); - - $this->assertEquals( - new FieldDefinitionCollection([$a, $b, $c]), - $collection->filter($this->getTautology()) - ); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::filterByType - */ - public function testFilterByType(): void - { - list($a, $b, $c) = $this->createFieldDefinitionsWith('fieldTypeIdentifier', ['ezstring', 'ezstring', 'ezimage']); - - $collection = new FieldDefinitionCollection([$a, $b, $c]); - - $this->assertEquals( - new FieldDefinitionCollection([$a, $b]), - $collection->filterByType('ezstring') - ); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::filterByGroup - */ - public function filterByGroup(): void - { - list($a, $b, $c) = $this->createFieldDefinitionsWith('fieldGroup', ['default', 'default', 'seo']); - - $collection = new FieldDefinitionCollection([$a, $b, $c]); - - $this->assertEquals( - new FieldDefinitionCollection([$c]), - $collection->filterByType('seo') - ); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::all - */ - public function testAll(): void - { - $collection = new FieldDefinitionCollection($this->createFieldDefinitions('A', 'B', 'C')); - - $this->assertTrue($collection->all($this->getIdentifierIsEqualPredicate('A', 'B', 'C'))); - $this->assertFalse($collection->all($this->getIdentifierIsEqualPredicate('A'))); - - $this->assertTrue($collection->all($this->getTautology())); - $this->assertFalse($collection->all($this->getContraction())); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::any - */ - public function testAny(): void - { - $collection = new FieldDefinitionCollection($this->createFieldDefinitions('A', 'B', 'C')); - - $this->assertTrue($collection->any($this->getIdentifierIsEqualPredicate('A'))); - $this->assertFalse($collection->any($this->getIdentifierIsEqualPredicate('Z'))); - - $this->assertTrue($collection->any($this->getTautology())); - $this->assertFalse($collection->any($this->getContraction())); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::anyOfType - */ - public function testAnyOfType(): void - { - $collection = new FieldDefinitionCollection( - $this->createFieldDefinitionsWith('fieldTypeIdentifier', ['ezstring', 'ezstring', 'ezimage']) - ); - - $this->assertTrue($collection->anyOfType('ezstring')); - $this->assertFalse($collection->anyOfType('ezrichtext')); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::anyInGroup - */ - public function testAnyInGroup(): void - { - $collection = new FieldDefinitionCollection( - $this->createFieldDefinitionsWith('fieldGroup', ['default', 'default', 'seo']) - ); - - $this->assertTrue($collection->anyInGroup('default')); - $this->assertFalse($collection->anyInGroup('comments')); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::partition - */ - public function testPartition(): void - { - list($a, $b, $c) = $this->createFieldDefinitions('A', 'B', 'C'); - - $collection = new FieldDefinitionCollection([$a, $b, $c]); - - $this->assertEquals( - [ - new FieldDefinitionCollection([$a, $c]), - new FieldDefinitionCollection([$b]), - ], - $collection->partition($this->getIdentifierIsEqualPredicate('A', 'C')) - ); - - $this->assertEquals( - [ - new FieldDefinitionCollection([$a, $b, $c]), - new FieldDefinitionCollection(), - ], - $collection->partition($this->getTautology()) - ); - - $this->assertEquals( - [ - new FieldDefinitionCollection(), - new FieldDefinitionCollection([$a, $b, $c]), - ], - $collection->partition($this->getContraction()) - ); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection::toArray - */ - public function testToArray(): void - { - $fieldDefinitions = $this->createFieldDefinitions('A', 'B', 'C'); - - $collection = new FieldDefinitionCollection($fieldDefinitions); - - $this->assertEquals($fieldDefinitions, $collection->toArray()); - } - - private function createFieldDefinitions(string ...$identifiers): array - { - return $this->createFieldDefinitionsWith('identifier', $identifiers); - } - - private function createFieldDefinitionsWith(string $property, array $values): array - { - return array_map(function (string $identifier) use ($property): APIFieldDefinition { - return $this->createFieldDefinition($identifier, $property); - }, $values); - } - - private function createFieldDefinition(string $identifier, string $property = 'identifier'): APIFieldDefinition - { - return new FieldDefinition([$property => $identifier]); - } - - /** - * Returns predicate which test if field definition identifier belongs to given set. - */ - private function getIdentifierIsEqualPredicate(string ...$identifiers): Closure - { - return static function (APIFieldDefinition $fieldDefinition) use ($identifiers): bool { - return in_array($fieldDefinition->identifier, $identifiers); - }; - } - - /** - * Returns a predicate which is always true. - */ - private function getTautology(): Closure - { - return static function (APIFieldDefinition $fieldDefinition): bool { - return true; - }; - } - - /** - * Returns a predicate which is always false. - */ - private function getContraction(): Closure - { - return static function (APIFieldDefinition $fieldDefinition): bool { - return false; - }; - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Values/ObjectState/ObjectStateGroupTest.php b/eZ/Publish/Core/Repository/Tests/Values/ObjectState/ObjectStateGroupTest.php deleted file mode 100644 index c545393660..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Values/ObjectState/ObjectStateGroupTest.php +++ /dev/null @@ -1,130 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Values\ObjectState; - -use eZ\Publish\API\Repository\Tests\Values\ValueObjectTestTrait; -use eZ\Publish\Core\Repository\Tests\Values\MultiLanguageTestTrait; -use eZ\Publish\Core\Repository\Values\ObjectState\ObjectStateGroup; -use PHPUnit\Framework\TestCase; - -/** - * Test internal integrity of @see \eZ\Publish\Core\Repository\Values\ObjectState\ObjectStateGroup ValueObject. - */ -class ObjectStateGroupTest extends TestCase -{ - use ValueObjectTestTrait; - use MultiLanguageTestTrait; - - /** - * Test a new class and default values on properties. - * - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup::__construct - */ - public function testNewClass() - { - $objectStateGroup = new ObjectStateGroup(); - - $this->assertPropertiesCorrect( - [ - 'id' => null, - 'identifier' => null, - 'mainLanguageCode' => null, - 'languageCodes' => null, - 'names' => [], - 'descriptions' => [], - ], - $objectStateGroup - ); - } - - /** - * Test a new class with unified multi language logic properties. - * - * @return \eZ\Publish\Core\Repository\Values\ObjectState\ObjectStateGroup - */ - public function testNewClassWithMultiLanguageProperties() - { - $properties = [ - 'names' => [ - 'eng-US' => 'Name', - 'pol-PL' => 'Nazwa', - ], - 'descriptions' => [ - 'eng-US' => 'Description', - 'pol-PL' => 'Opis', - ], - 'mainLanguageCode' => 'eng-US', - 'prioritizedLanguages' => ['pol-PL', 'eng-US'], - ]; - - $objectStateGroup = new ObjectStateGroup($properties); - $this->assertPropertiesCorrect($properties, $objectStateGroup); - - // BC test: - self::assertTrue(isset($objectStateGroup->defaultLanguageCode)); - self::assertSame('eng-US', $objectStateGroup->defaultLanguageCode); - - return $objectStateGroup; - } - - /** - * Test retrieving missing property. - * - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup::__get - */ - public function testMissingProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); - - $objectStateGroup = new ObjectStateGroup(); - $value = $objectStateGroup->notDefined; - $this->fail('Succeeded getting non existing property'); - } - - /** - * Test setting read only property. - * - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup::__set - */ - public function testReadOnlyProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $objectStateGroup = new ObjectStateGroup(); - $objectStateGroup->id = 42; - $this->fail('Succeeded setting read only property'); - } - - /** - * Test if property exists. - * - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup::__isset - */ - public function testIsPropertySet() - { - $objectStateGroup = new ObjectStateGroup(); - $value = isset($objectStateGroup->notDefined); - $this->assertFalse($value); - - $value = isset($objectStateGroup->id); - $this->assertTrue($value); - } - - /** - * Test unsetting a property. - * - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup::__unset - */ - public function testUnsetProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $objectStateGroup = new ObjectStateGroup(['id' => 2]); - unset($objectStateGroup->id); - $this->fail('Unsetting read-only property succeeded'); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Values/ObjectState/ObjectStateTest.php b/eZ/Publish/Core/Repository/Tests/Values/ObjectState/ObjectStateTest.php deleted file mode 100644 index de344f90df..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Values/ObjectState/ObjectStateTest.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Values\ObjectState; - -use eZ\Publish\API\Repository\Tests\Values\ValueObjectTestTrait; -use eZ\Publish\Core\Repository\Tests\Values\MultiLanguageTestTrait; -use eZ\Publish\Core\Repository\Values\ObjectState\ObjectState; -use PHPUnit\Framework\TestCase; - -/** - * Test internal integrity of @see \eZ\Publish\Core\Repository\Values\ObjectState\ObjectState ValueObject. - */ -class ObjectStateTest extends TestCase -{ - use ValueObjectTestTrait; - use MultiLanguageTestTrait; - - /** - * Test a new class and default values on properties. - * - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectState::__construct - */ - public function testNewClass() - { - $objectState = new ObjectState(); - - $this->assertPropertiesCorrect( - [ - 'id' => null, - 'identifier' => null, - 'priority' => null, - 'mainLanguageCode' => null, - 'languageCodes' => null, - 'names' => [], - 'descriptions' => [], - ], - $objectState - ); - } - - /** - * Test a new class with unified multi language logic properties. - * - * @return \eZ\Publish\Core\Repository\Values\ObjectState\ObjectState - */ - public function testNewClassWithMultiLanguageProperties() - { - $properties = [ - 'names' => [ - 'eng-US' => 'Name', - 'pol-PL' => 'Nazwa', - ], - 'descriptions' => [ - 'eng-US' => 'Description', - 'pol-PL' => 'Opis', - ], - 'mainLanguageCode' => 'eng-US', - 'prioritizedLanguages' => ['pol-PL', 'eng-US'], - ]; - - $objectState = new ObjectState($properties); - $this->assertPropertiesCorrect($properties, $objectState); - - // BC test: - self::assertTrue(isset($objectState->defaultLanguageCode)); - self::assertSame('eng-US', $objectState->defaultLanguageCode); - - return $objectState; - } - - /** - * Test retrieving missing property. - * - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectState::__get - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup::__get - */ - public function testMissingProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); - - $objectState = new ObjectState(); - $value = $objectState->notDefined; - $this->fail('Succeeded getting non existing property'); - } - - /** - * Test setting read only property. - * - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectState::__set - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup::__set - */ - public function testReadOnlyProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $objectState = new ObjectState(); - $objectState->id = 42; - $this->fail('Succeeded setting read only property'); - } - - /** - * Test if property exists. - * - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectState::__isset - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup::__isset - */ - public function testIsPropertySet() - { - $objectState = new ObjectState(); - $value = isset($objectState->notDefined); - $this->assertFalse($value); - - $value = isset($objectState->id); - $this->assertTrue($value); - } - - /** - * Test unsetting a property. - * - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectState::__unset - * @covers \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup::__unset - */ - public function testUnsetProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $objectState = new ObjectState(['id' => 2]); - unset($objectState->id); - $this->fail('Unsetting read-only property succeeded'); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Values/User/PolicyTest.php b/eZ/Publish/Core/Repository/Tests/Values/User/PolicyTest.php deleted file mode 100644 index aa708a1e38..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Values/User/PolicyTest.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Values\User; - -use eZ\Publish\API\Repository\Tests\Values\ValueObjectTestTrait; -use eZ\Publish\Core\Repository\Values\User\Policy; -use PHPUnit\Framework\TestCase; - -class PolicyTest extends TestCase -{ - use ValueObjectTestTrait; - - /** - * Test a new class and default values on properties. - * - * @covers \eZ\Publish\API\Repository\Values\User\Policy::__construct - */ - public function testNewClass() - { - $this->assertPropertiesCorrect( - [ - 'id' => null, - 'roleId' => null, - 'module' => null, - 'function' => null, - 'limitations' => [], - ], - new Policy() - ); - } - - /** - * Test retrieving missing property. - * - * @covers \eZ\Publish\API\Repository\Values\User\Policy::__get - */ - public function testMissingProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); - - $policy = new Policy(); - $value = $policy->notDefined; - self::fail('Succeeded getting non existing property'); - } - - /** - * Test setting read only property. - * - * @covers \eZ\Publish\API\Repository\Values\User\Policy::__set - */ - public function testReadOnlyProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $policy = new Policy(); - $policy->id = 42; - self::fail('Succeeded setting read only property'); - } - - /** - * Test if property exists. - * - * @covers \eZ\Publish\API\Repository\Values\User\Policy::__isset - */ - public function testIsPropertySet() - { - $policy = new Policy(); - $value = isset($policy->notDefined); - self::assertFalse($value); - - $value = isset($policy->id); - self::assertTrue($value); - } - - /** - * Test unsetting a property. - * - * @covers \eZ\Publish\API\Repository\Values\User\Policy::__unset - */ - public function testUnsetProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $policy = new Policy(['id' => 1]); - unset($policy->id); - self::fail('Unsetting read-only property succeeded'); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Values/User/RoleTest.php b/eZ/Publish/Core/Repository/Tests/Values/User/RoleTest.php deleted file mode 100644 index 70601cbf18..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Values/User/RoleTest.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Values\User; - -use eZ\Publish\API\Repository\Tests\Values\ValueObjectTestTrait; -use eZ\Publish\Core\Repository\Values\User\Role; -use PHPUnit\Framework\TestCase; - -class RoleTest extends TestCase -{ - use ValueObjectTestTrait; - - /** - * Test a new class and default values on properties. - * - * @covers \eZ\Publish\API\Repository\Values\User\Role::__construct - */ - public function testNewClass() - { - $this->assertPropertiesCorrect( - [ - 'id' => null, - 'identifier' => null, - 'policies' => [], - ], - new Role() - ); - } - - /** - * Test retrieving missing property. - * - * @covers \eZ\Publish\API\Repository\Values\User\Role::__get - */ - public function testMissingProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); - - $role = new Role(); - $value = $role->notDefined; - self::fail('Succeeded getting non existing property'); - } - - /** - * Test setting read only property. - * - * @covers \eZ\Publish\API\Repository\Values\User\Role::__set - */ - public function testReadOnlyProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $role = new Role(); - $role->id = 42; - self::fail('Succeeded setting read only property'); - } - - /** - * Test if property exists. - * - * @covers \eZ\Publish\API\Repository\Values\User\Role::__isset - */ - public function testIsPropertySet() - { - $role = new Role(); - $value = isset($role->notDefined); - self::assertFalse($value); - - $value = isset($role->id); - self::assertTrue($value); - } - - /** - * Test unsetting a property. - * - * @covers \eZ\Publish\API\Repository\Values\User\Role::__unset - */ - public function testUnsetProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $role = new Role(['id' => 1]); - unset($role->id); - self::fail('Unsetting read-only property succeeded'); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Values/User/UserGroupTest.php b/eZ/Publish/Core/Repository/Tests/Values/User/UserGroupTest.php deleted file mode 100644 index 3b7e7d6a36..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Values/User/UserGroupTest.php +++ /dev/null @@ -1,143 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Values\User; - -use eZ\Publish\API\Repository\Tests\Values\ValueObjectTestTrait; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\User\UserGroup; -use PHPUnit\Framework\TestCase; - -/** - * Test internal integrity of @see \eZ\Publish\Core\Repository\Values\User\UserGroup. - */ -class UserGroupTest extends TestCase -{ - use ValueObjectTestTrait; - - /** - * @covers \eZ\Publish\Core\Repository\Values\User\UserGroup::__construct - */ - public function testNewClass() - { - $group = new UserGroup(); - self::assertNull($group->parentId); - - $this->assertPropertiesCorrect( - [ - 'parentId' => null, - ], - $group - ); - } - - /** - * Test getName method. - * - * @covers \eZ\Publish\Core\Repository\Values\User\UserGroup::getName - */ - public function testGetName() - { - $name = 'Translated name'; - $contentMock = $this->createMock(Content::class); - $versionInfoMock = $this->createMock(VersionInfo::class); - - $contentMock->expects($this->once()) - ->method('getVersionInfo') - ->willReturn($versionInfoMock); - - $versionInfoMock->expects($this->once()) - ->method('getName') - ->willReturn($name); - - $object = new UserGroup(['content' => $contentMock]); - - $this->assertEquals($name, $object->getName()); - } - - /** - * Test retrieving missing property. - * - * @covers \eZ\Publish\API\Repository\Values\User\UserGroup::__get - */ - public function testMissingProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); - - $userGroup = new UserGroup(); - $value = $userGroup->notDefined; - self::fail('Succeeded getting non existing property'); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\User\UserGroup::getProperties - */ - public function testObjectProperties() - { - $object = new UserGroup(); - $properties = $object->attributes(); - self::assertNotContains('internalFields', $properties, 'Internal property found '); - self::assertContains('id', $properties, 'Property not found '); - self::assertContains('fields', $properties, 'Property not found '); - self::assertContains('versionInfo', $properties, 'Property not found '); - self::assertContains('contentInfo', $properties, 'Property not found '); - - // check for duplicates and double check existence of property - $propertiesHash = []; - foreach ($properties as $property) { - if (isset($propertiesHash[$property])) { - self::fail("Property '{$property}' exists several times in properties list"); - } elseif (!isset($object->$property)) { - self::fail("Property '{$property}' does not exist on object, even though it was hinted to be there"); - } - $propertiesHash[$property] = 1; - } - } - - /** - * Test setting read only property. - * - * @covers \eZ\Publish\API\Repository\Values\User\UserGroup::__set - */ - public function testReadOnlyProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $userGroup = new UserGroup(); - $userGroup->parentId = 42; - self::fail('Succeeded setting read only property'); - } - - /** - * Test if property exists. - * - * @covers \eZ\Publish\API\Repository\Values\User\UserGroup::__isset - */ - public function testIsPropertySet() - { - $userGroup = new UserGroup(); - $value = isset($userGroup->notDefined); - self::assertFalse($value); - - $value = isset($userGroup->parentId); - self::assertTrue($value); - } - - /** - * Test unsetting a property. - * - * @covers \eZ\Publish\API\Repository\Values\User\UserGroup::__unset - */ - public function testUnsetProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $userGroup = new UserGroup(['parentId' => 1]); - unset($userGroup->parentId); - self::fail('Unsetting read-only property succeeded'); - } -} diff --git a/eZ/Publish/Core/Repository/Tests/Values/User/UserTest.php b/eZ/Publish/Core/Repository/Tests/Values/User/UserTest.php deleted file mode 100644 index c9b1a844ff..0000000000 --- a/eZ/Publish/Core/Repository/Tests/Values/User/UserTest.php +++ /dev/null @@ -1,149 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Tests\Values\User; - -use eZ\Publish\API\Repository\Tests\Values\ValueObjectTestTrait; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\User\User; -use PHPUnit\Framework\TestCase; - -/** - * Test internal integrity of @see \eZ\Publish\Core\Repository\Values\User\User ValueObject. - */ -class UserTest extends TestCase -{ - use ValueObjectTestTrait; - - /** - * Test a new class and default values on properties. - * - * @covers \eZ\Publish\Core\Repository\Values\User\User::__construct - */ - public function testNewClass() - { - $user = new User(); - - $this->assertPropertiesCorrect( - [ - 'login' => null, - 'email' => null, - 'passwordHash' => null, - 'hashAlgorithm' => null, - 'maxLogin' => null, - 'enabled' => false, - ], - $user - ); - } - - /** - * Test getName method. - * - * @covers \eZ\Publish\Core\Repository\Values\User\User::getName - */ - public function testGetName() - { - $name = 'Translated name'; - $contentMock = $this->createMock(Content::class); - $versionInfoMock = $this->createMock(VersionInfo::class); - - $contentMock->expects($this->once()) - ->method('getVersionInfo') - ->willReturn($versionInfoMock); - - $versionInfoMock->expects($this->once()) - ->method('getName') - ->willReturn($name); - - $object = new User(['content' => $contentMock]); - - $this->assertEquals($name, $object->getName()); - } - - /** - * Test retrieving missing property. - * - * @covers \eZ\Publish\API\Repository\Values\User\User::__get - */ - public function testMissingProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); - - $user = new User(); - $value = $user->notDefined; - self::fail('Succeeded getting non existing property'); - } - - /** - * @covers \eZ\Publish\Core\Repository\Values\User\User::getProperties - */ - public function testObjectProperties() - { - $object = new User(); - $properties = $object->attributes(); - self::assertNotContains('internalFields', $properties, 'Internal property found '); - self::assertContains('id', $properties, 'Property not found '); - self::assertContains('fields', $properties, 'Property not found '); - self::assertContains('versionInfo', $properties, 'Property not found '); - self::assertContains('contentInfo', $properties, 'Property not found '); - - // check for duplicates and double check existence of property - $propertiesHash = []; - foreach ($properties as $property) { - if (isset($propertiesHash[$property])) { - self::fail("Property '{$property}' exists several times in properties list"); - } elseif (!isset($object->$property)) { - self::fail("Property '{$property}' does not exist on object, even though it was hinted to be there"); - } - $propertiesHash[$property] = 1; - } - } - - /** - * Test setting read only property. - * - * @covers \eZ\Publish\API\Repository\Values\User\User::__set - */ - public function testReadOnlyProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $user = new User(); - $user->login = 'user'; - self::fail('Succeeded setting read only property'); - } - - /** - * Test if property exists. - * - * @covers \eZ\Publish\API\Repository\Values\User\User::__isset - */ - public function testIsPropertySet() - { - $user = new User(); - $value = isset($user->notDefined); - self::assertFalse($value); - - $value = isset($user->login); - self::assertTrue($value); - } - - /** - * Test unsetting a property. - * - * @covers \eZ\Publish\API\Repository\Values\User\User::__unset - */ - public function testUnsetProperty() - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyReadOnlyException::class); - - $user = new User(['login' => 'admin']); - unset($user->login); - self::fail('Unsetting read-only property succeeded'); - } -} diff --git a/eZ/Publish/Core/Repository/TrashService.php b/eZ/Publish/Core/Repository/TrashService.php deleted file mode 100644 index 3e3e31de7f..0000000000 --- a/eZ/Publish/Core/Repository/TrashService.php +++ /dev/null @@ -1,441 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use DateTime; -use Exception; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException as APIUnauthorizedException; -use eZ\Publish\API\Repository\PermissionCriterionResolver; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\TrashService as TrashServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd as CriterionLogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot as CriterionLogicalNot; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree as CriterionSubtree; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Trash\SearchResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResultList; -use eZ\Publish\API\Repository\Values\Content\TrashItem as APITrashItem; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperInterface; -use eZ\Publish\Core\Repository\Values\Content\TrashItem; -use eZ\Publish\SPI\Limitation\Target; -use eZ\Publish\SPI\Persistence\Content\Location\Trashed; -use eZ\Publish\SPI\Persistence\Handler; - -/** - * Trash service, used for managing trashed content. - */ -class TrashService implements TrashServiceInterface -{ - /** @var \eZ\Publish\Core\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\SPI\Persistence\Handler */ - protected $persistenceHandler; - - /** @var array */ - protected $settings; - - /** @var \eZ\Publish\Core\Repository\Helper\NameSchemaService */ - protected $nameSchemaService; - - /** @var \eZ\Publish\API\Repository\PermissionCriterionResolver */ - private $permissionCriterionResolver; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionResolver; - - /** @var \eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperInterface */ - private $proxyDomainMapper; - - /** - * Setups service with reference to repository object that created it & corresponding handler. - * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\SPI\Persistence\Handler $handler - * @param \eZ\Publish\Core\Repository\Helper\NameSchemaService $nameSchemaService - * @param \eZ\Publish\API\Repository\PermissionCriterionResolver $permissionCriterionResolver - * @param \eZ\Publish\API\Repository\PermissionResolver $permissionResolver - * @param \eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperInterface $permissionResolver - * @param array $settings - */ - public function __construct( - RepositoryInterface $repository, - Handler $handler, - Helper\NameSchemaService $nameSchemaService, - PermissionCriterionResolver $permissionCriterionResolver, - PermissionResolver $permissionResolver, - ProxyDomainMapperInterface $proxyDomainMapper, - array $settings = [] - ) { - $this->repository = $repository; - $this->persistenceHandler = $handler; - $this->nameSchemaService = $nameSchemaService; - // Union makes sure default settings are ignored if provided in argument - $this->settings = $settings + [ - //'defaultSetting' => array(), - ]; - $this->permissionCriterionResolver = $permissionCriterionResolver; - $this->permissionResolver = $permissionResolver; - $this->proxyDomainMapper = $proxyDomainMapper; - } - - /** - * Loads a trashed location object from its $id. - * - * Note that $id is identical to original location, which has been previously trashed - * - * @param mixed $trashItemId - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the trashed location - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - if the location with the given id does not exist - * - * @return \eZ\Publish\API\Repository\Values\Content\TrashItem - */ - public function loadTrashItem(int $trashItemId): APITrashItem - { - $spiTrashItem = $this->persistenceHandler->trashHandler()->loadTrashItem($trashItemId); - $trash = $this->buildDomainTrashItemObject( - $spiTrashItem, - $this->repository->getContentService()->internalLoadContentById($spiTrashItem->contentId) - ); - if (!$this->permissionResolver->canUser('content', 'read', $trash->getContentInfo())) { - throw new UnauthorizedException('content', 'read'); - } - - if (!$this->permissionResolver->canUser('content', 'restore', $trash->getContentInfo())) { - throw new UnauthorizedException('content', 'restore'); - } - - return $trash; - } - - /** - * Sends $location and all its children to trash and returns the corresponding trash item. - * - * The current user may not have access to the returned trash item, check before using it. - * Content is left untouched. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to trash the given location - * - * @return \eZ\Publish\API\Repository\Values\Content\TrashItem|null null if location was deleted, otherwise TrashItem - */ - public function trash(Location $location): ?APITrashItem - { - if (empty($location->id)) { - throw new InvalidArgumentValue('id', $location->id, 'Location'); - } - - if (!$this->userHasPermissionsToRemove($location->getContentInfo(), $location)) { - throw new UnauthorizedException('content', 'remove'); - } - - $this->repository->beginTransaction(); - try { - $spiTrashItem = $this->persistenceHandler->trashHandler()->trashSubtree($location->id); - $this->persistenceHandler->urlAliasHandler()->locationDeleted($location->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - // Use internalLoadContent() as we want a trash item regardless of user access to the trash or not. - try { - return isset($spiTrashItem) - ? $this->buildDomainTrashItemObject( - $spiTrashItem, - $this->repository->getContentService()->internalLoadContentById($spiTrashItem->contentId) - ) - : null; - } catch (Exception $e) { - return null; - } - } - - /** - * Recovers the $trashedLocation at its original place if possible. - * - * @param \eZ\Publish\API\Repository\Values\Content\TrashItem $trashItem - * @param \eZ\Publish\API\Repository\Values\Content\Location $newParentLocation - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to recover the trash item at the parent location location - * - * @return \eZ\Publish\API\Repository\Values\Content\Location the newly created or recovered location - */ - public function recover(APITrashItem $trashItem, Location $newParentLocation = null): Location - { - if (!is_numeric($trashItem->id)) { - throw new InvalidArgumentValue('id', $trashItem->id, 'TrashItem'); - } - - if ($newParentLocation === null && !is_numeric($trashItem->parentLocationId)) { - throw new InvalidArgumentValue('parentLocationId', $trashItem->parentLocationId, 'TrashItem'); - } - - if ($newParentLocation !== null && !is_numeric($newParentLocation->id)) { - throw new InvalidArgumentValue('parentLocationId', $newParentLocation->id, 'Location'); - } - - if (!$this->permissionResolver->canUser( - 'content', - 'restore', - $trashItem->getContentInfo(), - [$newParentLocation ?: $trashItem] - )) { - throw new UnauthorizedException('content', 'restore'); - } - - $this->repository->beginTransaction(); - try { - $newParentLocationId = $newParentLocation ? $newParentLocation->id : $trashItem->parentLocationId; - $newLocationId = $this->persistenceHandler->trashHandler()->recover( - $trashItem->id, - $newParentLocationId - ); - - $content = $this->repository->getContentService()->loadContent($trashItem->contentId); - $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content); - - // Publish URL aliases for recovered location - foreach ($urlAliasNames as $languageCode => $name) { - $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation( - $newLocationId, - $newParentLocationId, - $name, - $languageCode, - $content->contentInfo->alwaysAvailable - ); - } - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->repository->getLocationService()->loadLocation($newLocationId); - } - - /** - * Empties trash. - * - * All locations contained in the trash will be removed. Content objects will be removed - * if all locations of the content are gone. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to empty the trash - * - * @return \eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResultList - */ - public function emptyTrash(): TrashItemDeleteResultList - { - if ($this->permissionResolver->hasAccess('content', 'cleantrash') === false) { - throw new UnauthorizedException('content', 'cleantrash'); - } - - $this->repository->beginTransaction(); - try { - // Persistence layer takes care of deleting content objects - $result = $this->persistenceHandler->trashHandler()->emptyTrash(); - $this->repository->commit(); - - return $result; - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Deletes a trash item. - * - * The corresponding content object will be removed - * - * @param \eZ\Publish\API\Repository\Values\Content\TrashItem $trashItem - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete this trash item - * - * @return \eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult - */ - public function deleteTrashItem(APITrashItem $trashItem): TrashItemDeleteResult - { - if (!$this->permissionResolver->canUser('content', 'cleantrash', $trashItem->getContentInfo())) { - throw new UnauthorizedException('content', 'cleantrash'); - } - - if (!is_numeric($trashItem->id)) { - throw new InvalidArgumentValue('id', $trashItem->id, 'TrashItem'); - } - - $this->repository->beginTransaction(); - try { - $trashItemDeleteResult = $this->persistenceHandler->trashHandler()->deleteTrashItem($trashItem->id); - $this->repository->commit(); - - return $trashItemDeleteResult; - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Returns a collection of Trashed locations contained in the trash, which are readable by the current user. - * - * $query allows to filter/sort the elements to be contained in the collection. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - * - * @return \eZ\Publish\API\Repository\Values\Content\Trash\SearchResult - */ - public function findTrashItems(Query $query): SearchResult - { - if ($query->filter !== null && !$query->filter instanceof Criterion) { - throw new InvalidArgumentValue('query->filter', $query->filter, 'Query'); - } - - if ($query->sortClauses !== null) { - if (!is_array($query->sortClauses)) { - throw new InvalidArgumentValue('query->sortClauses', $query->sortClauses, 'Query'); - } - - foreach ($query->sortClauses as $sortClause) { - if (!$sortClause instanceof SortClause) { - throw new InvalidArgumentValue('query->sortClauses', 'only instances of the SortClause class are allowed'); - } - } - } - - if ($query->offset !== null && !is_numeric($query->offset)) { - throw new InvalidArgumentValue('query->offset', $query->offset, 'Query'); - } - - if ($query->limit !== null && !is_numeric($query->limit)) { - throw new InvalidArgumentValue('query->limit', $query->limit, 'Query'); - } - - $spiTrashResult = $this->persistenceHandler->trashHandler()->findTrashItems( - $query->filter, - $query->offset !== null && $query->offset > 0 ? (int)$query->offset : 0, - $query->limit !== null && $query->limit >= 0 ? (int)$query->limit : null, - $query->sortClauses - ); - - $trashItems = $this->buildDomainTrashItems($spiTrashResult->items); - $searchResult = new SearchResult(['items' => $trashItems, 'totalCount' => $spiTrashResult->totalCount]); - - return $searchResult; - } - - protected function buildDomainTrashItems(array $spiTrashItems): array - { - $trashItems = []; - // TODO: load content in bulk once API allows for it - foreach ($spiTrashItems as $spiTrashItem) { - try { - $trashItems[] = $this->buildDomainTrashItemObject( - $spiTrashItem, - $this->repository->getContentService()->loadContent($spiTrashItem->contentId) - ); - } catch (APIUnauthorizedException $e) { - // Do nothing, thus exclude items the current user doesn't have read access to. - } - } - - return $trashItems; - } - - protected function buildDomainTrashItemObject(Trashed $spiTrashItem, Content $content): APITrashItem - { - return new TrashItem( - [ - 'content' => $content, - 'contentInfo' => $content->contentInfo, - 'id' => $spiTrashItem->id, - 'priority' => $spiTrashItem->priority, - 'hidden' => $spiTrashItem->hidden, - 'invisible' => $spiTrashItem->invisible, - 'remoteId' => $spiTrashItem->remoteId, - 'parentLocationId' => $spiTrashItem->parentId, - 'pathString' => $spiTrashItem->pathString, - 'depth' => $spiTrashItem->depth, - 'sortField' => $spiTrashItem->sortField, - 'sortOrder' => $spiTrashItem->sortOrder, - 'trashed' => isset($spiTrashItem->trashed) - ? new DateTime('@' . $spiTrashItem->trashed) - : new DateTime('@0'), - 'removedLocationContentIdMap' => $spiTrashItem->removedLocationContentIdMap, - 'parentLocation' => $this->proxyDomainMapper->createLocationProxy($spiTrashItem->parentId), - ] - ); - } - - /** - * @param int $timestamp - * - * @return \DateTime - */ - protected function getDateTime($timestamp) - { - $dateTime = new DateTime(); - $dateTime->setTimestamp($timestamp); - - return $dateTime; - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @return bool - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - private function userHasPermissionsToRemove(ContentInfo $contentInfo, Location $location) - { - $versionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo( - $contentInfo->id, - $contentInfo->currentVersionNo - ); - $target = (new Target\Version())->deleteTranslations($versionInfo->languageCodes); - - if (!$this->permissionResolver->canUser('content', 'remove', $contentInfo, [$location, $target])) { - return false; - } - $contentRemoveCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'remove'); - if (!$contentRemoveCriterion instanceof Criterion) { - return (bool)$contentRemoveCriterion; - } - $query = new Query( - [ - 'limit' => 0, - 'filter' => new CriterionLogicalAnd( - [ - new CriterionSubtree($location->pathString), - new CriterionLogicalNot($contentRemoveCriterion), - ] - ), - ] - ); - $result = $this->repository->getSearchService()->findContent($query, [], false); - - return $result->totalCount == 0; - } -} diff --git a/eZ/Publish/Core/Repository/URLAliasService.php b/eZ/Publish/Core/Repository/URLAliasService.php deleted file mode 100644 index 8a7b11ad53..0000000000 --- a/eZ/Publish/Core/Repository/URLAliasService.php +++ /dev/null @@ -1,839 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\ForbiddenException; -use eZ\Publish\API\Repository\LanguageResolver; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\URLAliasService as URLAliasServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\SPI\Persistence\Content\URLAlias as SPIURLAlias; -use eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler; - -/** - * @internal Type-hint \eZ\Publish\API\Repository\URLAliasService instead. - */ -class URLAliasService implements URLAliasServiceInterface -{ - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler */ - protected $urlAliasHandler; - - /** @var \eZ\Publish\Core\Repository\Helper\NameSchemaService */ - protected $nameSchemaService; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionResolver; - - /** @var \eZ\Publish\API\Repository\LanguageResolver */ - private $languageResolver; - - public function __construct( - RepositoryInterface $repository, - Handler $urlAliasHandler, - Helper\NameSchemaService $nameSchemaService, - PermissionResolver $permissionResolver, - LanguageResolver $languageResolver - ) { - $this->repository = $repository; - $this->urlAliasHandler = $urlAliasHandler; - $this->nameSchemaService = $nameSchemaService; - $this->permissionResolver = $permissionResolver; - $this->languageResolver = $languageResolver; - } - - /** - * Create a user chosen $alias pointing to $location in $languageCode. - * - * This method runs URL filters and transformers before storing them. - * Hence the path returned in the URLAlias Value may differ from the given. - * $alwaysAvailable makes the alias available in all languages. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param string $path - * @param string $languageCode the languageCode for which this alias is valid - * @param bool $forwarding if true a redirect is performed - * @param bool $alwaysAvailable - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create url alias - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the path already exists for the given context - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - public function createUrlAlias( - Location $location, - string $path, - string $languageCode, - bool $forwarding = false, - bool $alwaysAvailable = false - ): URLAlias { - if (!$this->permissionResolver->canUser('content', 'urltranslator', $location)) { - throw new UnauthorizedException('content', 'urltranslator'); - } - - $path = $this->cleanUrl($path); - - $this->repository->beginTransaction(); - try { - $spiUrlAlias = $this->urlAliasHandler->createCustomUrlAlias( - $location->id, - $path, - $forwarding, - $languageCode, - $alwaysAvailable - ); - $this->repository->commit(); - } catch (ForbiddenException $e) { - $this->repository->rollback(); - throw new InvalidArgumentException( - '$path', - $e->getMessage(), - $e - ); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->buildUrlAliasDomainObject($spiUrlAlias, $path); - } - - /** - * Create a user chosen $alias pointing to a resource in $languageCode. - * - * This method does not handle location resources - if a user enters a location target - * the createCustomUrlAlias method has to be used. - * This method runs URL filters and and transformers before storing them. - * Hence the path returned in the URLAlias Value may differ from the given. - * - * $alwaysAvailable makes the alias available in all languages. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create global - * url alias - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the path already exists for the given - * language or if resource is not valid - * - * @param string $resource - * @param string $path - * @param string $languageCode - * @param bool $forwarding - * @param bool $alwaysAvailable - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - public function createGlobalUrlAlias( - string $resource, - string $path, - string $languageCode, - bool $forwarding = false, - bool $alwaysAvailable = false - ): URLAlias { - if ($this->permissionResolver->hasAccess('content', 'urltranslator') === false) { - throw new UnauthorizedException('content', 'urltranslator'); - } - - if (!preg_match('#^([a-zA-Z0-9_]+):(.+)$#', $resource, $matches)) { - throw new InvalidArgumentException('$resource', 'argument is not valid'); - } - - $path = $this->cleanUrl($path); - - if ($matches[1] === 'eznode' || 0 === strpos($resource, 'module:content/view/full/')) { - if ($matches[1] === 'eznode') { - $locationId = $matches[2]; - } else { - $resourcePath = explode('/', $matches[2]); - $locationId = end($resourcePath); - } - - $location = $this->repository->getLocationService()->loadLocation((int)$locationId); - - if (!$this->permissionResolver->canUser('content', 'urltranslator', $location)) { - throw new UnauthorizedException('content', 'urltranslator'); - } - - return $this->createUrlAlias( - $location, - $path, - $languageCode, - $forwarding, - $alwaysAvailable - ); - } - - $this->repository->beginTransaction(); - try { - $spiUrlAlias = $this->urlAliasHandler->createGlobalUrlAlias( - $matches[1] . ':' . $this->cleanUrl($matches[2]), - $path, - $forwarding, - $languageCode, - $alwaysAvailable - ); - $this->repository->commit(); - } catch (ForbiddenException $e) { - $this->repository->rollback(); - throw new InvalidArgumentException('$path', $e->getMessage(), $e); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->buildUrlAliasDomainObject($spiUrlAlias, $path); - } - - /** - * List of url aliases pointing to $location, sorted by language priority. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param bool $custom if true the user generated aliases are listed otherwise the autogenerated - * @param string $languageCode filters those which are valid for the given language - * @param bool|null $showAllTranslations If enabled will include all alias as if they where always available. - * @param string[]|null $prioritizedLanguages If set used as prioritized language codes, returning first match. - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias[] - */ - public function listLocationAliases( - Location $location, - ?bool $custom = true, - ?string $languageCode = null, - ?bool $showAllTranslations = null, - ?array $prioritizedLanguages = null - ): iterable { - $spiUrlAliasList = $this->urlAliasHandler->listURLAliasesForLocation( - $location->id, - $custom - ); - if ($showAllTranslations === null) { - $showAllTranslations = $this->languageResolver->getShowAllTranslations(); - } - if ($prioritizedLanguages === null) { - $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages(); - } - $urlAliasList = []; - - foreach ($spiUrlAliasList as $spiUrlAlias) { - if ( - !$this->isUrlAliasLoadable( - $spiUrlAlias, - $languageCode, - $showAllTranslations, - $prioritizedLanguages - ) - ) { - continue; - } - - $path = $this->extractPath( - $spiUrlAlias, - $languageCode, - $showAllTranslations, - $prioritizedLanguages - ); - - if ($path === false) { - continue; - } - - $urlAliasList[$spiUrlAlias->id] = $this->buildUrlAliasDomainObject($spiUrlAlias, $path); - } - - $prioritizedAliasList = []; - foreach ($prioritizedLanguages as $prioritizedLanguageCode) { - foreach ($urlAliasList as $urlAlias) { - foreach ($urlAlias->languageCodes as $aliasLanguageCode) { - if ($aliasLanguageCode === $prioritizedLanguageCode) { - $prioritizedAliasList[$urlAlias->id] = $urlAlias; - break; - } - } - } - } - - // Add aliases not matched by prioritized language to the end of the list - return array_values($prioritizedAliasList + $urlAliasList); - } - - /** - * Determines alias language code. - * - * Method will return false if language code can't be matched against alias language codes or language settings. - * - * @param \eZ\Publish\SPI\Persistence\Content\URLAlias $spiUrlAlias - * @param string|null $languageCode - * @param bool $showAllTranslations - * @param string[] $prioritizedLanguageList - * - * @return string|bool - */ - protected function selectAliasLanguageCode( - SPIURLAlias $spiUrlAlias, - ?string $languageCode, - bool $showAllTranslations, - array $prioritizedLanguageList - ) { - if (isset($languageCode) && !in_array($languageCode, $spiUrlAlias->languageCodes)) { - return false; - } - - foreach ($prioritizedLanguageList as $prioritizedLanguageCode) { - if (in_array($prioritizedLanguageCode, $spiUrlAlias->languageCodes)) { - return $prioritizedLanguageCode; - } - } - - if ($spiUrlAlias->alwaysAvailable || $showAllTranslations) { - $lastLevelData = end($spiUrlAlias->pathData); - reset($lastLevelData['translations']); - - return key($lastLevelData['translations']); - } - - return false; - } - - /** - * Returns path extracted from normalized path data returned from persistence, using language settings. - * - * Will return false if path could not be determined. - * - * @param \eZ\Publish\SPI\Persistence\Content\URLAlias $spiUrlAlias - * @param string $languageCode - * @param bool $showAllTranslations - * @param string[] $prioritizedLanguageList - * - * @return string|bool - */ - protected function extractPath( - SPIURLAlias $spiUrlAlias, - $languageCode, - $showAllTranslations, - array $prioritizedLanguageList - ) { - $pathData = []; - $pathLevels = count($spiUrlAlias->pathData); - - foreach ($spiUrlAlias->pathData as $level => $levelEntries) { - if ($level === $pathLevels - 1) { - $prioritizedLanguageCode = $this->selectAliasLanguageCode( - $spiUrlAlias, - $languageCode, - $showAllTranslations, - $prioritizedLanguageList - ); - } else { - $prioritizedLanguageCode = $this->choosePrioritizedLanguageCode( - $levelEntries, - $showAllTranslations, - $prioritizedLanguageList - ); - } - - if ($prioritizedLanguageCode === false) { - return false; - } - - $pathData[$level] = $levelEntries['translations'][$prioritizedLanguageCode]; - } - - return implode('/', $pathData); - } - - /** - * Returns language code with highest priority. - * - * Will return false if language code could not be matched with language settings in place. - * - * @param array $entries - * @param bool $showAllTranslations - * @param string[] $prioritizedLanguageList - * - * @return string|bool - */ - protected function choosePrioritizedLanguageCode(array $entries, $showAllTranslations, array $prioritizedLanguageList) - { - foreach ($prioritizedLanguageList as $prioritizedLanguageCode) { - if (isset($entries['translations'][$prioritizedLanguageCode])) { - return $prioritizedLanguageCode; - } - } - - if ($entries['always-available'] || $showAllTranslations) { - reset($entries['translations']); - - return key($entries['translations']); - } - - return false; - } - - /** - * Matches path string with normalized path data returned from persistence. - * - * Returns matched path string (possibly case corrected) and array of corresponding language codes or false - * if path could not be matched. - * - * @param \eZ\Publish\SPI\Persistence\Content\URLAlias $spiUrlAlias - * @param string $path - * @param string $languageCode - * - * @return array - */ - protected function matchPath(SPIURLAlias $spiUrlAlias, $path, $languageCode) - { - $matchedPathElements = []; - $matchedPathLanguageCodes = []; - $pathElements = explode('/', $path); - $pathLevels = count($spiUrlAlias->pathData); - - foreach ($pathElements as $level => $pathElement) { - if ($level === $pathLevels - 1) { - $matchedLanguageCode = $this->selectAliasLanguageCode( - $spiUrlAlias, - $languageCode, - $this->languageResolver->getShowAllTranslations(), - $this->languageResolver->getPrioritizedLanguages() - ); - } else { - $matchedLanguageCode = $this->matchLanguageCode($spiUrlAlias->pathData[$level], $pathElement); - } - - if ($matchedLanguageCode === false) { - return [false, false]; - } - - $matchedPathLanguageCodes[] = $matchedLanguageCode; - $matchedPathElements[] = $spiUrlAlias->pathData[$level]['translations'][$matchedLanguageCode]; - } - - return [implode('/', $matchedPathElements), $matchedPathLanguageCodes]; - } - - /** - * @param array $pathElementData - * @param string $pathElement - * - * @return string|bool - */ - protected function matchLanguageCode(array $pathElementData, $pathElement) - { - foreach ($this->sortTranslationsByPrioritizedLanguages($pathElementData['translations']) as $translationData) { - if (strtolower($pathElement) === strtolower($translationData['text'])) { - return $translationData['lang']; - } - } - - return false; - } - - /** - * Needed when translations for the part of the alias are the same for multiple languages. - * In that case we need to ensure that more prioritized language is chosen. - * - * @param array $translations - * - * @return array - */ - private function sortTranslationsByPrioritizedLanguages(array $translations) - { - $sortedTranslations = []; - foreach ($this->languageResolver->getPrioritizedLanguages() as $languageCode) { - if (isset($translations[$languageCode])) { - $sortedTranslations[] = [ - 'lang' => $languageCode, - 'text' => $translations[$languageCode], - ]; - unset($translations[$languageCode]); - } - } - - foreach ($translations as $languageCode => $translation) { - $sortedTranslations[] = [ - 'lang' => $languageCode, - 'text' => $translation, - ]; - } - - return $sortedTranslations; - } - - /** - * Returns true or false depending if URL alias is loadable or not for language settings in place. - * - * @param \eZ\Publish\SPI\Persistence\Content\URLAlias $spiUrlAlias - * @param string|null $languageCode - * @param bool $showAllTranslations - * @param string[] $prioritizedLanguageList - * - * @return bool - */ - protected function isUrlAliasLoadable( - SPIURLAlias $spiUrlAlias, - ?string $languageCode, - bool $showAllTranslations, - array $prioritizedLanguageList - ) { - if (isset($languageCode) && !in_array($languageCode, $spiUrlAlias->languageCodes)) { - return false; - } - - if ($showAllTranslations) { - return true; - } - - foreach ($spiUrlAlias->pathData as $levelPathData) { - if ($levelPathData['always-available']) { - continue; - } - - foreach ($levelPathData['translations'] as $translationLanguageCode => $translation) { - if (in_array($translationLanguageCode, $prioritizedLanguageList)) { - continue 2; - } - } - - return false; - } - - return true; - } - - /** - * Returns true or false depending if URL alias is loadable or not for language settings in place. - * - * @param array $pathData - * @param array $languageCodes - * - * @return bool - */ - protected function isPathLoadable(array $pathData, array $languageCodes) - { - if ($this->languageResolver->getShowAllTranslations()) { - return true; - } - - foreach ($pathData as $level => $levelPathData) { - if ($levelPathData['always-available']) { - continue; - } - - if (in_array($languageCodes[$level], $this->languageResolver->getPrioritizedLanguages())) { - continue; - } - - return false; - } - - return true; - } - - /** - * List global aliases. - * - * @param string $languageCode filters those which are valid for the given language - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias[] - */ - public function listGlobalAliases(?string $languageCode = null, int $offset = 0, int $limit = -1): iterable - { - $urlAliasList = []; - $spiUrlAliasList = $this->urlAliasHandler->listGlobalURLAliases( - $languageCode, - $offset, - $limit - ); - - foreach ($spiUrlAliasList as $spiUrlAlias) { - $path = $this->extractPath( - $spiUrlAlias, - $languageCode, - $this->languageResolver->getShowAllTranslations(), - $this->languageResolver->getPrioritizedLanguages() - ); - - if ($path === false) { - continue; - } - - $urlAliasList[] = $this->buildUrlAliasDomainObject($spiUrlAlias, $path); - } - - return $urlAliasList; - } - - /** - * Removes urls aliases. - * - * This method does not remove autogenerated aliases for locations. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove url alias - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if alias list contains - * autogenerated alias - * - * @param \eZ\Publish\API\Repository\Values\Content\URLAlias[] $aliasList - */ - public function removeAliases(array $aliasList): void - { - if ($this->permissionResolver->hasAccess('content', 'urltranslator') === false) { - throw new UnauthorizedException('content', 'urltranslator'); - } - - $spiUrlAliasList = []; - foreach ($aliasList as $alias) { - if (!$alias->isCustom) { - throw new InvalidArgumentException( - '$aliasList', - 'The alias list contains an autogenerated alias' - ); - } - $spiUrlAliasList[] = $this->buildSPIUrlAlias($alias); - } - - $this->repository->beginTransaction(); - try { - $this->urlAliasHandler->removeURLAliases($spiUrlAliasList); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Builds persistence domain object. - * - * @param \eZ\Publish\API\Repository\Values\Content\URLAlias $urlAlias - * - * @return \eZ\Publish\SPI\Persistence\Content\URLAlias - */ - protected function buildSPIUrlAlias(URLAlias $urlAlias) - { - return new SPIURLAlias( - [ - 'id' => $urlAlias->id, - 'type' => $urlAlias->type, - 'destination' => $urlAlias->destination, - 'isCustom' => $urlAlias->isCustom, - ] - ); - } - - /** - * looks up the URLAlias for the given url. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the path does not exist or is not valid for the given language - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the path exceeded maximum depth level - * - * @param string $url - * @param string|null $languageCode - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - public function lookup(string $url, ?string $languageCode = null): URLAlias - { - $url = $this->cleanUrl($url); - - $spiUrlAlias = $this->urlAliasHandler->lookup($url); - - list($path, $languageCodes) = $this->matchPath($spiUrlAlias, $url, $languageCode); - if ($path === false || !$this->isPathLoadable($spiUrlAlias->pathData, $languageCodes)) { - throw new NotFoundException('URLAlias', $url); - } - - return $this->buildUrlAliasDomainObject($spiUrlAlias, $path); - } - - /** - * Returns the URL alias for the given location in the given language. - * - * If $languageCode is null the method returns the url alias in the most prioritized language. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if no url alias exist for the given language - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param string|null $languageCode - * @param bool|null $showAllTranslations - * @param string[]|null $prioritizedLanguageList - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - public function reverseLookup( - Location $location, - ?string $languageCode = null, - ?bool $showAllTranslations = null, - ?array $prioritizedLanguageList = null - ): URLAlias { - if ($showAllTranslations === null) { - $showAllTranslations = $this->languageResolver->getShowAllTranslations(); - } - if ($prioritizedLanguageList === null) { - $prioritizedLanguageList = $this->languageResolver->getPrioritizedLanguages(); - } - $urlAliases = $this->listLocationAliases( - $location, - false, - $languageCode, - $showAllTranslations, - $prioritizedLanguageList - ); - - foreach ($prioritizedLanguageList as $prioritizedLanguageCode) { - foreach ($urlAliases as $urlAlias) { - if (in_array($prioritizedLanguageCode, $urlAlias->languageCodes)) { - return $urlAlias; - } - } - } - - foreach ($urlAliases as $urlAlias) { - if ($urlAlias->alwaysAvailable) { - return $urlAlias; - } - } - - if (!empty($urlAliases) && $showAllTranslations) { - return reset($urlAliases); - } - - throw new NotFoundException('URLAlias', $location->id); - } - - /** - * Loads URL alias by given $id. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * - * @param string $id - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - public function load(string $id): URLAlias - { - $spiUrlAlias = $this->urlAliasHandler->loadUrlAlias($id); - $path = $this->extractPath( - $spiUrlAlias, - null, - $this->languageResolver->getShowAllTranslations(), - $this->languageResolver->getPrioritizedLanguages() - ); - - if ($path === false) { - throw new NotFoundException('URLAlias', $id); - } - - return $this->buildUrlAliasDomainObject($spiUrlAlias, $path); - } - - /** - * Refresh all system URL aliases for the given Location (and historize outdated if needed). - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \Exception any unexpected exception that might come from custom Field Type implementation - */ - public function refreshSystemUrlAliasesForLocation(Location $location): void - { - if (!$this->repository->getPermissionResolver()->canUser('content', 'urltranslator', $location)) { - throw new UnauthorizedException('content', 'urltranslator'); - } - - $this->repository->beginTransaction(); - try { - $content = $location->getContent(); - $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content); - foreach ($urlAliasNames as $languageCode => $name) { - $this->urlAliasHandler->publishUrlAliasForLocation( - $location->id, - $location->parentLocationId, - $name, - $languageCode, - $content->contentInfo->alwaysAvailable - ); - } - $this->urlAliasHandler->repairBrokenUrlAliasesForLocation($location->id); - - // handle URL aliases for missing Translations - $this->urlAliasHandler->archiveUrlAliasesForDeletedTranslations( - $location->id, - $location->parentLocationId, - $content->getVersionInfo()->languageCodes - ); - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Delete global, system or custom URL alias pointing to non-existent Locations. - * - * @return int Number of removed URL aliases - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function deleteCorruptedUrlAliases(): int - { - if ($this->repository->getPermissionResolver()->hasAccess('content', 'urltranslator') === false) { - throw new UnauthorizedException('content', 'urltranslator'); - } - - return $this->urlAliasHandler->deleteCorruptedUrlAliases(); - } - - /** - * @param string $url - * - * @return string - */ - protected function cleanUrl(string $url): string - { - return trim($url, '/ '); - } - - /** - * Builds API UrlAlias object from given SPI UrlAlias object. - * - * @param \eZ\Publish\SPI\Persistence\Content\URLAlias $spiUrlAlias - * @param string $path - * - * @return \eZ\Publish\API\Repository\Values\Content\URLAlias - */ - protected function buildUrlAliasDomainObject(SPIURLAlias $spiUrlAlias, string $path): URLAlias - { - return new URLAlias( - [ - 'id' => $spiUrlAlias->id, - 'type' => $spiUrlAlias->type, - 'destination' => $spiUrlAlias->destination, - 'languageCodes' => $spiUrlAlias->languageCodes, - 'alwaysAvailable' => $spiUrlAlias->alwaysAvailable, - 'path' => '/' . $path, - 'isHistory' => $spiUrlAlias->isHistory, - 'isCustom' => $spiUrlAlias->isCustom, - 'forward' => $spiUrlAlias->forward, - ] - ); - } -} diff --git a/eZ/Publish/Core/Repository/URLService.php b/eZ/Publish/Core/Repository/URLService.php deleted file mode 100644 index 524803e295..0000000000 --- a/eZ/Publish/Core/Repository/URLService.php +++ /dev/null @@ -1,271 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use DateTime; -use DateTimeInterface; -use Exception; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\URLService as URLServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion as ContentCriterion; -use eZ\Publish\API\Repository\Values\URL\SearchResult; -use eZ\Publish\API\Repository\Values\URL\URL; -use eZ\Publish\API\Repository\Values\URL\URLQuery; -use eZ\Publish\API\Repository\Values\URL\URLUpdateStruct; -use eZ\Publish\API\Repository\Values\URL\UsageSearchResult; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\SPI\Persistence\URL\Handler as URLHandler; -use eZ\Publish\SPI\Persistence\URL\URL as SPIUrl; -use eZ\Publish\SPI\Persistence\URL\URLUpdateStruct as SPIUrlUpdateStruct; - -class URLService implements URLServiceInterface -{ - /** @var \eZ\Publish\Core\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\SPI\Persistence\URL\Handler */ - protected $urlHandler; - - /** \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionResolver; - - /** - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\SPI\Persistence\URL\Handler $urlHandler - * @param \eZ\Publish\API\Repository\PermissionResolver $permissionResolver - */ - public function __construct( - RepositoryInterface $repository, - URLHandler $urlHandler, - PermissionResolver $permissionResolver - ) { - $this->repository = $repository; - $this->urlHandler = $urlHandler; - $this->permissionResolver = $permissionResolver; - } - - /** - * {@inheritdoc} - */ - public function findUrls(URLQuery $query): SearchResult - { - if ($this->permissionResolver->hasAccess('url', 'view') === false) { - throw new UnauthorizedException('url', 'view'); - } - - if ($query->offset !== null && !is_numeric($query->offset)) { - throw new InvalidArgumentValue('offset', $query->offset); - } - - if ($query->limit !== null && !is_numeric($query->limit)) { - throw new InvalidArgumentValue('limit', $query->limit); - } - - $results = $this->urlHandler->find($query); - - $items = []; - foreach ($results['items'] as $url) { - $items[] = $this->buildDomainObject($url); - } - - return new SearchResult([ - 'totalCount' => $results['count'], - 'items' => $items, - ]); - } - - /** - * {@inheritdoc} - */ - public function updateUrl(URL $url, URLUpdateStruct $struct): URL - { - if (!$this->permissionResolver->canUser('url', 'update', $url)) { - throw new UnauthorizedException('url', 'update'); - } - - if ($struct->url !== null && !$this->isUnique($url->id, $struct->url)) { - throw new InvalidArgumentException('struct', 'The URL already exists'); - } - - $updateStruct = $this->buildUpdateStruct($this->loadById($url->id), $struct); - - $this->repository->beginTransaction(); - try { - $this->urlHandler->updateUrl($url->id, $updateStruct); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadById($url->id); - } - - /** - * {@inheritdoc} - */ - public function loadById(int $id): URL - { - $url = $this->buildDomainObject( - $this->urlHandler->loadById($id) - ); - - if (!$this->permissionResolver->canUser('url', 'view', $url)) { - throw new UnauthorizedException('url', 'view'); - } - - return $url; - } - - /** - * {@inheritdoc} - */ - public function loadByUrl(string $url): URL - { - $apiUrl = $this->buildDomainObject( - $this->urlHandler->loadByUrl($url) - ); - - if (!$this->permissionResolver->canUser('url', 'view', $apiUrl)) { - throw new UnauthorizedException('url', 'view'); - } - - return $apiUrl; - } - - /** - * {@inheritdoc} - */ - public function createUpdateStruct(): URLUpdateStruct - { - return new URLUpdateStruct(); - } - - /** - * {@inheritdoc} - */ - public function findUsages(URL $url, int $offset = 0, int $limit = -1): UsageSearchResult - { - $contentIds = $this->urlHandler->findUsages($url->id); - if (empty($contentIds)) { - return new UsageSearchResult(); - } - - $query = new Query(); - $query->filter = new ContentCriterion\LogicalAnd([ - new ContentCriterion\ContentId($contentIds), - new ContentCriterion\Visibility(ContentCriterion\Visibility::VISIBLE), - ]); - - $query->offset = $offset; - if ($limit > -1) { - $query->limit = $limit; - } - - $searchResults = $this->repository->getSearchService()->findContentInfo($query); - - $usageResults = new UsageSearchResult(); - $usageResults->totalCount = $searchResults->totalCount; - foreach ($searchResults->searchHits as $hit) { - $usageResults->items[] = $hit->valueObject; - } - - return $usageResults; - } - - /** - * Builds domain object from ValueObject returned by Persistence API. - * - * @param \eZ\Publish\SPI\Persistence\URL\URL $data - * - * @return \eZ\Publish\API\Repository\Values\URL\URL - */ - protected function buildDomainObject(SPIUrl $data): URL - { - return new URL([ - 'id' => $data->id, - 'url' => $data->url, - 'isValid' => $data->isValid, - 'lastChecked' => $this->createDateTime($data->lastChecked), - 'created' => $this->createDateTime($data->created), - 'modified' => $this->createDateTime($data->modified), - ]); - } - - /** - * Builds SPI update structure used by Persistence API. - * - * @param \eZ\Publish\API\Repository\Values\URL\URL $url - * @param \eZ\Publish\API\Repository\Values\URL\URLUpdateStruct $data - * - * @return \eZ\Publish\SPI\Persistence\URL\URLUpdateStruct - */ - protected function buildUpdateStruct(URL $url, URLUpdateStruct $data): SPIUrlUpdateStruct - { - $updateStruct = new SPIUrlUpdateStruct(); - - if ($data->url !== null && $url->url !== $data->url) { - $updateStruct->url = $data->url; - // Reset URL validity - $updateStruct->lastChecked = 0; - $updateStruct->isValid = true; - } else { - $updateStruct->url = $url->url; - - if ($data->lastChecked !== null) { - $updateStruct->lastChecked = $data->lastChecked->getTimestamp(); - } elseif ($data->lastChecked !== null) { - $updateStruct->lastChecked = $url->lastChecked->getTimestamp(); - } else { - $updateStruct->lastChecked = 0; - } - - if ($data->isValid !== null) { - $updateStruct->isValid = $data->isValid; - } else { - $updateStruct->isValid = $url->isValid; - } - } - - return $updateStruct; - } - - /** - * Check if URL is unique. - * - * @param int $id - * @param string $url - * - * @return bool - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - protected function isUnique(int $id, string $url): bool - { - try { - return $this->loadByUrl($url)->id === $id; - } catch (NotFoundException $e) { - return true; - } - } - - private function createDateTime(?int $timestamp): ?DateTimeInterface - { - if ($timestamp > 0) { - return new DateTime("@{$timestamp}"); - } - - return null; - } -} diff --git a/eZ/Publish/Core/Repository/URLWildcardService.php b/eZ/Publish/Core/Repository/URLWildcardService.php deleted file mode 100644 index d22a423dcf..0000000000 --- a/eZ/Publish/Core/Repository/URLWildcardService.php +++ /dev/null @@ -1,328 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use Exception; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\URLWildcardService as URLWildcardServiceInterface; -use eZ\Publish\API\Repository\Values\Content\URLWildcard; -use eZ\Publish\API\Repository\Values\Content\URLWildcardTranslationResult; -use eZ\Publish\API\Repository\Values\Content\URLWildcardUpdateStruct; -use eZ\Publish\Core\Base\Exceptions\ContentValidationException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard as SPIUrlWildcard; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler; -use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\SearchResult; -use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\URLWildcardQuery; - -/** - * URLAlias service. - * - * @example Examples/urlalias.php - */ -class URLWildcardService implements URLWildcardServiceInterface -{ - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler */ - protected $urlWildcardHandler; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionResolver; - - /** @var array */ - protected $settings; - - /** - * Setups service with reference to repository object that created it & corresponding handler. - * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler $urlWildcardHandler - * @param \eZ\Publish\API\Repository\PermissionResolver $permissionResolver - * @param array $settings - */ - public function __construct( - RepositoryInterface $repository, - Handler $urlWildcardHandler, - PermissionResolver $permissionResolver, - array $settings = [] - ) { - $this->repository = $repository; - $this->urlWildcardHandler = $urlWildcardHandler; - $this->permissionResolver = $permissionResolver; - $this->settings = $settings; - } - - /** - * Creates a new url wildcard. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the $sourceUrl pattern already exists - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to create url wildcards - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if the number of "*" patterns in $sourceUrl and - * the numbers in {\d} placeholders in $destinationUrl does not match. - * - * @param string $sourceUrl - * @param string $destinationUrl - * @param bool $forward - * - * @return \eZ\Publish\API\Repository\Values\Content\UrlWildcard - */ - public function create(string $sourceUrl, string $destinationUrl, bool $forward = false): UrlWildcard - { - if (false === $this->permissionResolver->hasAccess('content', 'urltranslator')) { - throw new UnauthorizedException('content', 'urltranslator'); - } - - $sourceUrl = $this->cleanUrl($sourceUrl); - $destinationUrl = $this->cleanUrl($destinationUrl); - - if ($this->urlWildcardHandler->exactSourceUrlExists($this->cleanPath($sourceUrl))) { - throw new InvalidArgumentException( - '$sourceUrl', - 'Pattern already exists' - ); - } - - $this->validateUrls($sourceUrl, $destinationUrl); - - $this->repository->beginTransaction(); - try { - $spiUrlWildcard = $this->urlWildcardHandler->create( - $sourceUrl, - $destinationUrl, - $forward - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->buildUrlWildcardDomainObject($spiUrlWildcard); - } - - public function update( - URLWildcard $urlWildcard, - URLWildcardUpdateStruct $updateStruct - ): void { - if (false === $this->permissionResolver->canUser('content', 'urltranslator', $urlWildcard)) { - throw new UnauthorizedException('content', 'urltranslator'); - } - - $destinationUrl = $updateStruct->destinationUrl; - $sourceUrl = $updateStruct->sourceUrl; - - $this->validateUrls($sourceUrl, $destinationUrl); - - $this->repository->beginTransaction(); - - try { - $this->urlWildcardHandler->update( - $urlWildcard->id, - $sourceUrl, - $destinationUrl, - $updateStruct->forward - ); - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * @param \eZ\Publish\API\Repository\Values\Content\UrlWildcard $urlWildcard the url wildcard to remove - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove url wildcards - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - */ - public function remove(URLWildcard $urlWildcard): void - { - if (false === $this->permissionResolver->canUser('content', 'urltranslator', $urlWildcard)) { - throw new UnauthorizedException('content', 'urltranslator'); - } - - $this->repository->beginTransaction(); - try { - $this->urlWildcardHandler->remove( - $urlWildcard->id - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Loads a url wild card. - * - * @param int $id - * - * @return \eZ\Publish\API\Repository\Values\Content\UrlWildcard - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the url wild card was not found - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\Core\Base\Exceptions\UnauthorizedException - */ - public function load(int $id): UrlWildcard - { - return $this->buildUrlWildcardDomainObject( - $this->urlWildcardHandler->load($id) - ); - } - - /** - * Loads all url wild card (paged). - * - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\API\Repository\Values\Content\UrlWildcard[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\Core\Base\Exceptions\UnauthorizedException - */ - public function loadAll(int $offset = 0, int $limit = -1): iterable - { - $spiUrlWildcards = $this->urlWildcardHandler->loadAll( - $offset, - $limit - ); - - $urlWildcards = []; - foreach ($spiUrlWildcards as $spiUrlWildcard) { - $urlWildcards[] = $this->buildUrlWildcardDomainObject($spiUrlWildcard); - } - - return $urlWildcards; - } - - public function findUrlWildcards(URLWildcardQuery $query): SearchResult - { - if ($query->offset !== null && !is_numeric($query->offset)) { - throw new InvalidArgumentValue('offset', $query->offset); - } - - if ($query->limit !== null && !is_numeric($query->limit)) { - throw new InvalidArgumentValue('limit', $query->limit); - } - - $results = $this->urlWildcardHandler->find($query); - - $items = []; - foreach ($results['items'] as $urlWildcard) { - $items[] = $this->buildUrlWildcardDomainObject($urlWildcard); - } - - return new SearchResult([ - 'totalCount' => $results['count'], - 'items' => $items, - ]); - } - - public function countAll(): int - { - return $this->urlWildcardHandler->countAll(); - } - - /** - * Translates an url to an existing uri resource based on the - * source/destination patterns of the url wildcard. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the url could not be translated - * - * @param string $url - * - * @return \eZ\Publish\API\Repository\Values\Content\URLWildcardTranslationResult - */ - public function translate(string $url): URLWildcardTranslationResult - { - $spiWildcard = $this->urlWildcardHandler->translate($this->cleanPath($url)); - - return new URLWildcardTranslationResult( - [ - 'uri' => $spiWildcard->destinationUrl, - 'forward' => $spiWildcard->forward, - ] - ); - } - - /** - * Builds API UrlWildcard object from given SPI UrlWildcard object. - * - * @param \eZ\Publish\SPI\Persistence\Content\UrlWildcard $wildcard - * - * @return \eZ\Publish\API\Repository\Values\Content\URLWildcard - */ - private function buildUrlWildcardDomainObject(SPIUrlWildcard $wildcard): URLWildcard - { - return new URLWildcard( - [ - 'id' => $wildcard->id, - 'destinationUrl' => $wildcard->destinationUrl, - 'sourceUrl' => $wildcard->sourceUrl, - 'forward' => $wildcard->forward, - ] - ); - } - - /** - * Removes leading and trailing slashes and spaces. - * - * @param string $url - * - * @return string - */ - private function cleanUrl(string $url): string - { - return '/' . trim($url, '/ '); - } - - /** - * Removes leading slash from given path. - * - * @param string $path - * - * @return string - */ - private function cleanPath(string $path): string - { - return trim($path, '/ '); - } - - /** - * @param string $sourceUrl - * @param string $destinationUrl - * - * @throws \eZ\Publish\Core\Base\Exceptions\ContentValidationException - */ - private function validateUrls(string $sourceUrl, string $destinationUrl): void - { - preg_match_all('(\\*)', $sourceUrl, $patterns); - preg_match_all('({(\d+)})', $destinationUrl, $placeholders); - - if (empty($patterns) || empty($placeholders)) { - throw new ContentValidationException('Invalid URL wildcards provided.'); - } - - $patterns = array_map('intval', $patterns[0]); - $placeholders = array_map('intval', $placeholders[1]); - - if (!empty($placeholders) && max($placeholders) > count($patterns)) { - throw new ContentValidationException('Placeholders do not match the wildcards.'); - } - } -} diff --git a/eZ/Publish/Core/Repository/User/Exception/UnsupportedPasswordHashType.php b/eZ/Publish/Core/Repository/User/Exception/UnsupportedPasswordHashType.php deleted file mode 100644 index a6077857b6..0000000000 --- a/eZ/Publish/Core/Repository/User/Exception/UnsupportedPasswordHashType.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\User\Exception; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; - -class UnsupportedPasswordHashType extends InvalidArgumentException -{ - public function __construct(int $hashType) - { - parent::__construct('hashType', "Password hash type '$hashType' is not recognized"); - } -} diff --git a/eZ/Publish/Core/Repository/User/PasswordHashService.php b/eZ/Publish/Core/Repository/User/PasswordHashService.php deleted file mode 100644 index 450d4d2921..0000000000 --- a/eZ/Publish/Core/Repository/User/PasswordHashService.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\User; - -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\Core\Repository\User\Exception\UnsupportedPasswordHashType; - -/** - * @internal - */ -final class PasswordHashService implements PasswordHashServiceInterface -{ - /** @var int */ - private $defaultHashType; - - public function __construct(int $hashType = User::DEFAULT_PASSWORD_HASH) - { - $this->defaultHashType = $hashType; - } - - public function getSupportedHashTypes(): array - { - return User::SUPPORTED_PASSWORD_HASHES; - } - - public function isHashTypeSupported(int $hashType): bool - { - return in_array($hashType, $this->getSupportedHashTypes(), true); - } - - public function getDefaultHashType(): int - { - return $this->defaultHashType; - } - - /** - * @throws \eZ\Publish\Core\Repository\User\Exception\UnsupportedPasswordHashType - */ - public function createPasswordHash(string $password, ?int $hashType = null): string - { - $hashType = $hashType ?? $this->defaultHashType; - - switch ($hashType) { - case User::PASSWORD_HASH_BCRYPT: - return password_hash($password, PASSWORD_BCRYPT); - - case User::PASSWORD_HASH_PHP_DEFAULT: - return password_hash($password, PASSWORD_DEFAULT); - - default: - throw new UnsupportedPasswordHashType($hashType); - } - } - - public function isValidPassword(string $plainPassword, string $passwordHash, ?int $hashType = null): bool - { - if ($hashType === User::PASSWORD_HASH_BCRYPT || $hashType === User::PASSWORD_HASH_PHP_DEFAULT) { - // In case of bcrypt let php's password functionality do it's magic - return password_verify($plainPassword, $passwordHash); - } - - return $passwordHash === $this->createPasswordHash($plainPassword, $hashType); - } -} diff --git a/eZ/Publish/Core/Repository/User/PasswordHashServiceInterface.php b/eZ/Publish/Core/Repository/User/PasswordHashServiceInterface.php deleted file mode 100644 index d8d123d2c7..0000000000 --- a/eZ/Publish/Core/Repository/User/PasswordHashServiceInterface.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\User; - -use eZ\Publish\API\Repository\PasswordHashService; - -/** - * @deprecated since eZ Platform 3.3.0, to be removed in eZ Platform 4.0.0. Use - * \eZ\Publish\API\Repository\PasswordHashService directly instead. - */ -interface PasswordHashServiceInterface extends PasswordHashService -{ -} diff --git a/eZ/Publish/Core/Repository/User/PasswordValidatorInterface.php b/eZ/Publish/Core/Repository/User/PasswordValidatorInterface.php deleted file mode 100644 index 00aec48d25..0000000000 --- a/eZ/Publish/Core/Repository/User/PasswordValidatorInterface.php +++ /dev/null @@ -1,30 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\User; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\API\Repository\Values\User\PasswordInfo; -use eZ\Publish\API\Repository\Values\User\User; - -/** - * @internal - */ -interface PasswordValidatorInterface -{ - /** - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validatePassword( - string $password, - FieldDefinition $userFieldDefinition, - ?User $user = null - ): array; - - public function getPasswordInfo(User $user, FieldDefinition $fieldDefinition): PasswordInfo; -} diff --git a/eZ/Publish/Core/Repository/UserPreferenceService.php b/eZ/Publish/Core/Repository/UserPreferenceService.php deleted file mode 100644 index 621348e116..0000000000 --- a/eZ/Publish/Core/Repository/UserPreferenceService.php +++ /dev/null @@ -1,148 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use Exception; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\UserPreferenceService as UserPreferenceServiceInterface; -use eZ\Publish\API\Repository\Values\UserPreference\UserPreference as APIUserPreference; -use eZ\Publish\API\Repository\Values\UserPreference\UserPreferenceList; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\SPI\Persistence\UserPreference\Handler as UserPreferenceHandler; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreference; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreferenceSetStruct; - -class UserPreferenceService implements UserPreferenceServiceInterface -{ - /** @var \eZ\Publish\API\Repository\Repository */ - private $repository; - - /** @var \eZ\Publish\SPI\Persistence\UserPreference\Handler */ - private $userPreferenceHandler; - - /** - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\SPI\Persistence\UserPreference\Handler $userPreferenceHandler - */ - public function __construct(RepositoryInterface $repository, UserPreferenceHandler $userPreferenceHandler) - { - $this->repository = $repository; - $this->userPreferenceHandler = $userPreferenceHandler; - } - - /** - * {@inheritdoc} - */ - public function loadUserPreferences(int $offset = 0, int $limit = 25): UserPreferenceList - { - $currentUserId = $this->getCurrentUserId(); - - $list = new UserPreferenceList(); - - $list->totalCount = $this->userPreferenceHandler->countUserPreferences($currentUserId); - if ($list->totalCount > 0) { - $list->items = array_map(function (UserPreference $spiUserPreference) { - return $this->buildDomainObject($spiUserPreference); - }, $this->userPreferenceHandler->loadUserPreferences($currentUserId, $offset, $limit)); - } - - return $list; - } - - /** - * {@inheritdoc} - */ - public function setUserPreference(array $userPreferenceSetStructs): void - { - $spiSetStructs = []; - foreach ($userPreferenceSetStructs as $key => $userPreferenceSetStruct) { - if (empty($userPreferenceSetStruct->name)) { - throw new InvalidArgumentException('name', $userPreferenceSetStruct->name . ' at index ' . $key); - } - - $value = $userPreferenceSetStruct->value; - - if (is_object($value) && !method_exists($value, '__toString')) { - throw new InvalidArgumentException('value', 'Cannot convert value to string at index ' . $key); - } - - try { - $value = (string)$userPreferenceSetStruct->value; - } catch (\Exception $exception) { - throw new InvalidArgumentException('value', 'Cannot convert value to string at index ' . $key); - } - - $spiSetStruct = new UserPreferenceSetStruct(); - $spiSetStruct->userId = $this->getCurrentUserId(); - $spiSetStruct->name = $userPreferenceSetStruct->name; - $spiSetStruct->value = $value; - - $spiSetStructs[] = $spiSetStruct; - } - - $this->repository->beginTransaction(); - try { - foreach ($spiSetStructs as $spiSetStruct) { - $this->userPreferenceHandler->setUserPreference($spiSetStruct); - } - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * {@inheritdoc} - */ - public function getUserPreference(string $userPreferenceName): APIUserPreference - { - $currentUserId = $this->getCurrentUserId(); - - $userPreference = $this->userPreferenceHandler->getUserPreferenceByUserIdAndName( - $currentUserId, - $userPreferenceName - ); - - return $this->buildDomainObject($userPreference); - } - - /** - * {@inheritdoc} - */ - public function getUserPreferenceCount(): int - { - return $this->userPreferenceHandler->countUserPreferences( - $this->getCurrentUserId() - ); - } - - /** - * Builds UserPreference domain object from ValueObject returned by Persistence API. - * - * @param \eZ\Publish\SPI\Persistence\UserPreference\UserPreference $spiUserPreference - * - * @return \eZ\Publish\API\Repository\Values\UserPreference\UserPreference - */ - protected function buildDomainObject(UserPreference $spiUserPreference): APIUserPreference - { - return new APIUserPreference([ - 'name' => $spiUserPreference->name, - 'value' => $spiUserPreference->value, - ]); - } - - private function getCurrentUserId(): int - { - return $this->repository - ->getPermissionResolver() - ->getCurrentUserReference() - ->getUserId(); - } -} diff --git a/eZ/Publish/Core/Repository/UserService.php b/eZ/Publish/Core/Repository/UserService.php deleted file mode 100644 index 55f47c4c69..0000000000 --- a/eZ/Publish/Core/Repository/UserService.php +++ /dev/null @@ -1,1376 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository; - -use DateTime; -use DateTimeImmutable; -use DateTimeInterface; -use Exception; -use eZ\Publish\API\Repository\PasswordHashService; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\UserService as UserServiceInterface; -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeId as CriterionContentTypeId; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LocationId as CriterionLocationId; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd as CriterionLogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId as CriterionParentLocationId; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\API\Repository\Values\User\PasswordInfo; -use eZ\Publish\API\Repository\Values\User\PasswordValidationContext; -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\API\Repository\Values\User\UserCreateStruct as APIUserCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroup as APIUserGroup; -use eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct as APIUserGroupCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserTokenUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserUpdateStruct; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\Base\Exceptions\MissingUserFieldTypeException; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\Core\FieldType\User\Value as UserValue; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\Repository\User\Exception\UnsupportedPasswordHashType; -use eZ\Publish\Core\Repository\User\PasswordValidatorInterface; -use eZ\Publish\Core\Repository\Values\User\User; -use eZ\Publish\Core\Repository\Values\User\UserCreateStruct; -use eZ\Publish\Core\Repository\Values\User\UserGroup; -use eZ\Publish\Core\Repository\Values\User\UserGroupCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as LocationHandler; -use eZ\Publish\SPI\Persistence\User as SPIUser; -use eZ\Publish\SPI\Persistence\User\Handler; -use eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct as SPIUserTokenUpdateStruct; -use Psr\Log\LoggerInterface; - -/** - * This service provides methods for managing users and user groups. - */ -class UserService implements UserServiceInterface -{ - private const USER_FIELD_TYPE_NAME = 'ezuser'; - - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - /** @var \eZ\Publish\SPI\Persistence\User\Handler */ - protected $userHandler; - - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Handler */ - private $locationHandler; - - /** @var array */ - protected $settings; - - /** @var \Psr\Log\LoggerInterface|null */ - protected $logger; - - /** @var \eZ\Publish\API\Repository\PermissionResolver */ - private $permissionResolver; - - /** @var \eZ\Publish\API\Repository\PasswordHashService */ - private $passwordHashService; - - /** @var \eZ\Publish\Core\Repository\User\PasswordValidatorInterface */ - private $passwordValidator; - - public function setLogger(LoggerInterface $logger = null) - { - $this->logger = $logger; - } - - /** - * Setups service with reference to repository object that created it & corresponding handler. - */ - public function __construct( - RepositoryInterface $repository, - PermissionResolver $permissionResolver, - Handler $userHandler, - LocationHandler $locationHandler, - PasswordHashService $passwordHashGenerator, - PasswordValidatorInterface $passwordValidator, - array $settings = [] - ) { - $this->repository = $repository; - $this->permissionResolver = $permissionResolver; - $this->userHandler = $userHandler; - $this->locationHandler = $locationHandler; - // Union makes sure default settings are ignored if provided in argument - $this->settings = $settings + [ - 'defaultUserPlacement' => 12, - 'userClassID' => 4, // @todo Rename this settings to swap out "Class" for "Type" - 'userGroupClassID' => 3, - 'hashType' => $passwordHashGenerator->getDefaultHashType(), - 'siteName' => 'ez.no', - ]; - $this->passwordHashService = $passwordHashGenerator; - $this->passwordValidator = $passwordValidator; - } - - /** - * Creates a new user group using the data provided in the ContentCreateStruct parameter. - * - * In 4.x in the content type parameter in the profile is ignored - * - the content type is determined via configuration and can be set to null. - * The returned version is published. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct $userGroupCreateStruct a structure for setting all necessary data to create this user group - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $parentGroup - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the input structure has invalid data - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userGroupCreateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is missing or set to an empty value - */ - public function createUserGroup(APIUserGroupCreateStruct $userGroupCreateStruct, APIUserGroup $parentGroup): APIUserGroup - { - $contentService = $this->repository->getContentService(); - $locationService = $this->repository->getLocationService(); - $contentTypeService = $this->repository->getContentTypeService(); - - if ($userGroupCreateStruct->contentType === null) { - $userGroupContentType = $contentTypeService->loadContentType($this->settings['userGroupClassID']); - $userGroupCreateStruct->contentType = $userGroupContentType; - } - - $loadedParentGroup = $this->loadUserGroup($parentGroup->id); - - if ($loadedParentGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { - throw new InvalidArgumentException('parentGroup', 'parent User Group has no main Location'); - } - - $locationCreateStruct = $locationService->newLocationCreateStruct( - $loadedParentGroup->getVersionInfo()->getContentInfo()->mainLocationId - ); - - $this->repository->beginTransaction(); - try { - $contentDraft = $contentService->createContent($userGroupCreateStruct, [$locationCreateStruct]); - $publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo()); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->buildDomainUserGroupObject($publishedContent); - } - - /** - * Loads a user group for the given id. - * - * @param mixed $id - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the user group with the given id was not found - */ - public function loadUserGroup(int $id, array $prioritizedLanguages = []): APIUserGroup - { - $content = $this->repository->getContentService()->loadContent($id, $prioritizedLanguages); - - return $this->buildDomainUserGroupObject($content); - } - - public function loadUserGroupByRemoteId(string $remoteId, array $prioritizedLanguages = []): APIUserGroup - { - $content = $this->repository->getContentService()->loadContentByRemoteId($remoteId, $prioritizedLanguages); - - return $this->buildDomainUserGroupObject($content); - } - - /** - * Loads the sub groups of a user group. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * @param int $offset the start offset for paging - * @param int $limit the number of user groups returned - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read the user group - */ - public function loadSubUserGroups(APIUserGroup $userGroup, int $offset = 0, int $limit = 25, array $prioritizedLanguages = []): iterable - { - $locationService = $this->repository->getLocationService(); - - $loadedUserGroup = $this->loadUserGroup($userGroup->id); - if (!$this->permissionResolver->canUser('content', 'read', $loadedUserGroup)) { - throw new UnauthorizedException('content', 'read'); - } - - if ($loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { - return []; - } - - $mainGroupLocation = $locationService->loadLocation( - $loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId - ); - - $searchResult = $this->searchSubGroups($mainGroupLocation, $offset, $limit); - if ($searchResult->totalCount == 0) { - return []; - } - - $subUserGroups = []; - foreach ($searchResult->searchHits as $searchHit) { - $subUserGroups[] = $this->buildDomainUserGroupObject( - $this->repository->getContentService()->internalLoadContentById( - $searchHit->valueObject->contentInfo->id, - $prioritizedLanguages - ) - ); - } - - return $subUserGroups; - } - - /** - * Returns (searches) subgroups of a user group described by its main location. - * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult - */ - protected function searchSubGroups(Location $location, int $offset = 0, int $limit = 25): SearchResult - { - $searchQuery = new LocationQuery(); - - $searchQuery->offset = $offset; - $searchQuery->limit = $limit; - - $searchQuery->filter = new CriterionLogicalAnd([ - new CriterionContentTypeId($this->settings['userGroupClassID']), - new CriterionParentLocationId($location->id), - ]); - - $searchQuery->sortClauses = $location->getSortClauses(); - - return $this->repository->getSearchService()->findLocations($searchQuery, [], false); - } - - /** - * Removes a user group. - * - * the users which are not assigned to other groups will be deleted. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group - */ - public function deleteUserGroup(APIUserGroup $userGroup): iterable - { - $loadedUserGroup = $this->loadUserGroup($userGroup->id); - - $this->repository->beginTransaction(); - try { - foreach ($this->userHandler->loadRoleAssignmentsByGroupId($userGroup->id) as $roleAssignment) { - $this->userHandler->removeRoleAssignment($roleAssignment->id); - } - //@todo: what happens to sub user groups and users below sub user groups - $affectedLocationIds = $this->repository->getContentService()->deleteContent( - $loadedUserGroup->getVersionInfo()->getContentInfo() - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $affectedLocationIds; - } - - /** - * Moves the user group to another parent. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $newParent - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to move the user group - */ - public function moveUserGroup(APIUserGroup $userGroup, APIUserGroup $newParent): void - { - $loadedUserGroup = $this->loadUserGroup($userGroup->id); - $loadedNewParent = $this->loadUserGroup($newParent->id); - - $locationService = $this->repository->getLocationService(); - - if ($loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { - throw new BadStateException('userGroup', 'existing User Group is not stored and/or does not have any Location yet'); - } - - if ($loadedNewParent->getVersionInfo()->getContentInfo()->mainLocationId === null) { - throw new BadStateException('newParent', 'new User Group is not stored and/or does not have any Location yet'); - } - - $userGroupMainLocation = $locationService->loadLocation( - $loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId - ); - $newParentMainLocation = $locationService->loadLocation( - $loadedNewParent->getVersionInfo()->getContentInfo()->mainLocationId - ); - - $this->repository->beginTransaction(); - try { - $locationService->moveSubtree($userGroupMainLocation, $newParentMainLocation); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Updates the group profile with fields and meta data. - * - * 4.x: If the versionUpdateStruct is set in $userGroupUpdateStruct, this method internally creates a content draft, updates ts with the provided data - * and publishes the draft. If a draft is explicitly required, the user group can be updated via the content service methods. - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * @param \eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct $userGroupUpdateStruct - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update the user group - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userGroupUpdateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is set empty - */ - public function updateUserGroup(APIUserGroup $userGroup, UserGroupUpdateStruct $userGroupUpdateStruct): APIUserGroup - { - if ($userGroupUpdateStruct->contentUpdateStruct === null && - $userGroupUpdateStruct->contentMetadataUpdateStruct === null) { - // both update structs are empty, nothing to do - return $userGroup; - } - - $contentService = $this->repository->getContentService(); - - $loadedUserGroup = $this->loadUserGroup($userGroup->id); - - $this->repository->beginTransaction(); - try { - $publishedContent = $loadedUserGroup; - if ($userGroupUpdateStruct->contentUpdateStruct !== null) { - $contentDraft = $contentService->createContentDraft($loadedUserGroup->getVersionInfo()->getContentInfo()); - - $contentDraft = $contentService->updateContent( - $contentDraft->getVersionInfo(), - $userGroupUpdateStruct->contentUpdateStruct - ); - - $publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo()); - } - - if ($userGroupUpdateStruct->contentMetadataUpdateStruct !== null) { - $publishedContent = $contentService->updateContentMetadata( - $publishedContent->getVersionInfo()->getContentInfo(), - $userGroupUpdateStruct->contentMetadataUpdateStruct - ); - } - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->buildDomainUserGroupObject($publishedContent); - } - - /** - * Create a new user. The created user is published by this method. - * - * @param \eZ\Publish\API\Repository\Values\User\UserCreateStruct $userCreateStruct the data used for creating the user - * @param \eZ\Publish\API\Repository\Values\User\UserGroup[] $parentGroups the groups which are assigned to the user after creation - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to move the user group - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userCreateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is missing or set to an empty value - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if a user with provided login already exists - */ - public function createUser(APIUserCreateStruct $userCreateStruct, array $parentGroups): APIUser - { - $contentService = $this->repository->getContentService(); - $locationService = $this->repository->getLocationService(); - - $locationCreateStructs = []; - foreach ($parentGroups as $parentGroup) { - $parentGroup = $this->loadUserGroup($parentGroup->id); - if ($parentGroup->getVersionInfo()->getContentInfo()->mainLocationId !== null) { - $locationCreateStructs[] = $locationService->newLocationCreateStruct( - $parentGroup->getVersionInfo()->getContentInfo()->mainLocationId - ); - } - } - - // Search for the first ezuser field type in content type - $userFieldDefinition = $this->getUserFieldDefinition($userCreateStruct->contentType); - if ($userFieldDefinition === null) { - throw new MissingUserFieldTypeException($userCreateStruct->contentType, self::USER_FIELD_TYPE_NAME); - } - - $this->repository->beginTransaction(); - try { - $contentDraft = $contentService->createContent($userCreateStruct, $locationCreateStructs); - // There is no need to create user separately, just load it from SPI - $spiUser = $this->userHandler->load($contentDraft->id); - $publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo()); - - // User\Handler::create call is currently used to clear cache only - $this->userHandler->create( - new SPIUser( - [ - 'id' => $spiUser->id, - 'login' => $spiUser->login, - 'email' => $spiUser->email, - ] - ) - ); - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->buildDomainUserObject($spiUser, $publishedContent); - } - - /** - * Loads a user. - * - * @param int $userId - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given id was not found - */ - public function loadUser(int $userId, array $prioritizedLanguages = []): APIUser - { - /** @var \eZ\Publish\API\Repository\Values\Content\Content $content */ - $content = $this->repository->getContentService()->internalLoadContentById($userId, $prioritizedLanguages); - // Get spiUser value from Field Value - foreach ($content->getFields() as $field) { - if (!$field->value instanceof UserValue) { - continue; - } - - /** @var \eZ\Publish\Core\FieldType\User\Value $value */ - $value = $field->value; - $spiUser = new SPIUser(); - $spiUser->id = $value->contentId; - $spiUser->login = $value->login; - $spiUser->email = $value->email; - $spiUser->hashAlgorithm = $value->passwordHashType; - $spiUser->passwordHash = $value->passwordHash; - $spiUser->passwordUpdatedAt = $value->passwordUpdatedAt ? $value->passwordUpdatedAt->getTimestamp() : null; - $spiUser->isEnabled = $value->enabled; - $spiUser->maxLogin = $value->maxLogin; - break; - } - - // If for some reason not found, load it - if (!isset($spiUser)) { - $spiUser = $this->userHandler->load($userId); - } - - return $this->buildDomainUserObject($spiUser, $content); - } - - /** - * Checks if credentials are valid for provided User. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param string $credentials - * - * @return bool - */ - public function checkUserCredentials(APIUser $user, string $credentials): bool - { - return $this->comparePasswordHashForAPIUser($user, $credentials); - } - - /** - * Loads a user for the given login. - * - * {@inheritdoc} - * - * @param string $login - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if a user with the given credentials was not found - */ - public function loadUserByLogin(string $login, array $prioritizedLanguages = []): APIUser - { - if (empty($login)) { - throw new InvalidArgumentValue('login', $login); - } - - $spiUser = $this->userHandler->loadByLogin($login); - - return $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages); - } - - /** - * Loads a user for the given email. - * - * {@inheritdoc} - * - * @param string $email - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function loadUserByEmail(string $email, array $prioritizedLanguages = []): APIUser - { - if (empty($email)) { - throw new InvalidArgumentValue('email', $email); - } - - $spiUser = $this->userHandler->loadByEmail($email); - - return $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages); - } - - /** - * Loads a user for the given email. - * - * {@inheritdoc} - * - * @param string $email - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\User[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function loadUsersByEmail(string $email, array $prioritizedLanguages = []): iterable - { - if (empty($email)) { - throw new InvalidArgumentValue('email', $email); - } - - $users = []; - foreach ($this->userHandler->loadUsersByEmail($email) as $spiUser) { - $users[] = $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages); - } - - return $users; - } - - /** - * Loads a user for the given token. - * - * {@inheritdoc} - * - * @param string $hash - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\User - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue - */ - public function loadUserByToken(string $hash, array $prioritizedLanguages = []): APIUser - { - if (empty($hash)) { - throw new InvalidArgumentValue('hash', $hash); - } - - $spiUser = $this->userHandler->loadUserByToken($hash); - - return $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages); - } - - /** - * This method deletes a user. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete the user - */ - public function deleteUser(APIUser $user): iterable - { - $loadedUser = $this->loadUser($user->id); - - $this->repository->beginTransaction(); - try { - foreach ($this->userHandler->loadRoleAssignmentsByGroupId($user->id) as $roleAssignment) { - $this->userHandler->removeRoleAssignment($roleAssignment->id); - } - - $affectedLocationIds = $this->repository->getContentService()->deleteContent( - $loadedUser->getVersionInfo()->getContentInfo() - ); - - // User\Handler::delete call is currently used to clear cache only - $this->userHandler->delete($loadedUser->id); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $affectedLocationIds ?? []; - } - - /** - * Updates a user. - * - * 4.x: If the versionUpdateStruct is set in the user update structure, this method internally creates a content draft, updates ts with the provided data - * and publishes the draft. If a draft is explicitly required, the user group can be updated via the content service methods. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param \eZ\Publish\API\Repository\Values\User\UserUpdateStruct $userUpdateStruct - * - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException if a field in the $userUpdateStruct is not valid - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException if a required field is set empty - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update the user - */ - public function updateUser(APIUser $user, UserUpdateStruct $userUpdateStruct): APIUser - { - $loadedUser = $this->loadUser($user->id); - - $contentService = $this->repository->getContentService(); - - $canEditContent = $this->permissionResolver->canUser('content', 'edit', $loadedUser); - - if (!$canEditContent && $this->isUserProfileUpdateRequested($userUpdateStruct)) { - throw new UnauthorizedException('content', 'edit'); - } - - $userFieldDefinition = $this->getUserFieldDefinition($loadedUser->getContentType()); - if ($userFieldDefinition === null) { - throw new MissingUserFieldTypeException($loadedUser->getContentType(), self::USER_FIELD_TYPE_NAME); - } - - $userUpdateStruct->contentUpdateStruct = $userUpdateStruct->contentUpdateStruct ?? $contentService->newContentUpdateStruct(); - - $providedUserUpdateDataInField = false; - foreach ($userUpdateStruct->contentUpdateStruct->fields as $field) { - if ($field->value instanceof UserValue) { - $providedUserUpdateDataInField = true; - break; - } - } - - if (!$providedUserUpdateDataInField) { - $userUpdateStruct->contentUpdateStruct->setField( - $userFieldDefinition->identifier, - new UserValue([ - 'contentId' => $loadedUser->id, - 'hasStoredLogin' => true, - 'login' => $loadedUser->login, - 'email' => $userUpdateStruct->email ?? $loadedUser->email, - 'plainPassword' => $userUpdateStruct->password, - 'enabled' => $userUpdateStruct->enabled ?? $loadedUser->enabled, - 'maxLogin' => $userUpdateStruct->maxLogin ?? $loadedUser->maxLogin, - 'passwordHashType' => $user->hashAlgorithm, - 'passwordHash' => $user->passwordHash, - ]) - ); - } - - if (!empty($userUpdateStruct->password) && - !$canEditContent && - !$this->permissionResolver->canUser('user', 'password', $loadedUser, [$loadedUser]) - ) { - throw new UnauthorizedException('user', 'password'); - } - - $this->repository->beginTransaction(); - try { - $publishedContent = $loadedUser; - if ($userUpdateStruct->contentUpdateStruct !== null) { - $contentDraft = $contentService->createContentDraft($loadedUser->getVersionInfo()->getContentInfo()); - $contentDraft = $contentService->updateContent( - $contentDraft->getVersionInfo(), - $userUpdateStruct->contentUpdateStruct - ); - $publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo()); - } - - if ($userUpdateStruct->contentMetadataUpdateStruct !== null) { - $contentService->updateContentMetadata( - $publishedContent->getVersionInfo()->getContentInfo(), - $userUpdateStruct->contentMetadataUpdateStruct - ); - } - - // User\Handler::update call is currently used to clear cache only - $this->userHandler->update( - new SPIUser( - [ - 'id' => $loadedUser->id, - 'login' => $loadedUser->login, - 'email' => $userUpdateStruct->email ?: $loadedUser->email, - ] - ) - ); - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadUser($loadedUser->id); - } - - public function updateUserPassword(APIUser $user, string $newPassword): APIUser - { - $loadedUser = $this->loadUser($user->id); - - if (!$this->permissionResolver->canUser('content', 'edit', $loadedUser) - && !$this->permissionResolver->canUser('user', 'password', $loadedUser) - ) { - throw new UnauthorizedException('user', 'password'); - } - - $userFieldDefinition = $this->getUserFieldDefinition($loadedUser->getContentType()); - if ($userFieldDefinition === null) { - throw new MissingUserFieldTypeException($loadedUser->getContentType(), self::USER_FIELD_TYPE_NAME); - } - - $errors = $this->passwordValidator->validatePassword( - $newPassword, - $userFieldDefinition, - $user - ); - if (!empty($errors)) { - // Note: @deprecated this should rather throw a list wrapper of `ValidationError`s - throw ContentFieldValidationException::createNewWithMultiline( - // build errors array as expected by ContentFieldValidationException - [$userFieldDefinition->id => [$userFieldDefinition->mainLanguageCode => $errors]], - $loadedUser->getName() - ); - } - - $passwordHashAlgorithm = (int) $loadedUser->hashAlgorithm; - try { - $passwordHash = $this->passwordHashService->createPasswordHash($newPassword, $passwordHashAlgorithm); - } catch (UnsupportedPasswordHashType $e) { - $passwordHashAlgorithm = $this->passwordHashService->getDefaultHashType(); - $passwordHash = $this->passwordHashService->createPasswordHash($newPassword, $passwordHashAlgorithm); - } - - $this->repository->beginTransaction(); - try { - $this->userHandler->updatePassword( - new SPIUser( - [ - 'id' => $loadedUser->id, - 'login' => $loadedUser->login, - 'email' => $loadedUser->email, - 'passwordHash' => $passwordHash, - 'hashAlgorithm' => $passwordHashAlgorithm, - 'passwordUpdatedAt' => time(), - ] - ) - ); - - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadUser($loadedUser->id); - } - - /** - * Update the user token information specified by the user token struct. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param \eZ\Publish\API\Repository\Values\User\UserTokenUpdateStruct $userTokenUpdateStruct - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \RuntimeException - * @throws \Exception - * - * @return \eZ\Publish\API\Repository\Values\User\User - */ - public function updateUserToken(APIUser $user, UserTokenUpdateStruct $userTokenUpdateStruct): APIUser - { - $loadedUser = $this->loadUser($user->id); - - if ($userTokenUpdateStruct->hashKey !== null && (!is_string($userTokenUpdateStruct->hashKey) || empty($userTokenUpdateStruct->hashKey))) { - throw new InvalidArgumentValue('hashKey', $userTokenUpdateStruct->hashKey, 'UserTokenUpdateStruct'); - } - - if ($userTokenUpdateStruct->time === null) { - throw new InvalidArgumentValue('time', $userTokenUpdateStruct->time, 'UserTokenUpdateStruct'); - } - - $this->repository->beginTransaction(); - try { - $this->userHandler->updateUserToken( - new SPIUserTokenUpdateStruct( - [ - 'userId' => $loadedUser->id, - 'hashKey' => $userTokenUpdateStruct->hashKey, - 'time' => $userTokenUpdateStruct->time->getTimestamp(), - ] - ) - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - - return $this->loadUser($loadedUser->id); - } - - /** - * Expires user token with user hash. - * - * @param string $hash - */ - public function expireUserToken(string $hash): void - { - $this->repository->beginTransaction(); - try { - $this->userHandler->expireUserToken($hash); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Assigns a new user group to the user. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign the user group to the user - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the user is already in the given user group - */ - public function assignUserToUserGroup(APIUser $user, APIUserGroup $userGroup): void - { - $loadedUser = $this->loadUser($user->id); - $loadedGroup = $this->loadUserGroup($userGroup->id); - $locationService = $this->repository->getLocationService(); - - $existingGroupIds = []; - $userLocations = $locationService->loadLocations($loadedUser->getVersionInfo()->getContentInfo()); - foreach ($userLocations as $userLocation) { - $existingGroupIds[] = $userLocation->parentLocationId; - } - - if ($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { - throw new BadStateException('userGroup', 'User Group has no main Location or no Locations'); - } - - if (in_array($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId, $existingGroupIds)) { - // user is already assigned to the user group - throw new InvalidArgumentException('user', 'User is already in the given User Group'); - } - - $locationCreateStruct = $locationService->newLocationCreateStruct( - $loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId - ); - - $this->repository->beginTransaction(); - try { - $locationService->createLocation( - $loadedUser->getVersionInfo()->getContentInfo(), - $locationCreateStruct - ); - $this->repository->commit(); - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - - /** - * Removes a user group from the user. - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove the user group from the user - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the user is not in the given user group - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If $userGroup is the last assigned user group - */ - public function unAssignUserFromUserGroup(APIUser $user, APIUserGroup $userGroup): void - { - $loadedUser = $this->loadUser($user->id); - $loadedGroup = $this->loadUserGroup($userGroup->id); - $locationService = $this->repository->getLocationService(); - - $userLocations = $locationService->loadLocations($loadedUser->getVersionInfo()->getContentInfo()); - if (empty($userLocations)) { - throw new BadStateException('user', 'User has no Locations, cannot unassign from group'); - } - - if ($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { - throw new BadStateException('userGroup', 'User Group has no main Location or no Locations, cannot unassign'); - } - - foreach ($userLocations as $userLocation) { - if ($userLocation->parentLocationId == $loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId) { - // Throw this specific BadState when we know argument is valid - if (count($userLocations) === 1) { - throw new BadStateException('user', 'User only has one User Group, cannot unassign from last group'); - } - - $this->repository->beginTransaction(); - try { - $locationService->deleteLocation($userLocation); - $this->repository->commit(); - - return; - } catch (Exception $e) { - $this->repository->rollback(); - throw $e; - } - } - } - - throw new InvalidArgumentException('userGroup', 'User is not in the given User Group'); - } - - /** - * Loads the user groups the user belongs to. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed read the user or user group - * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param int $offset the start offset for paging - * @param int $limit the number of user groups returned - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup[] - */ - public function loadUserGroupsOfUser(APIUser $user, int $offset = 0, int $limit = 25, array $prioritizedLanguages = []): iterable - { - $locationService = $this->repository->getLocationService(); - - if (!$this->repository->getPermissionResolver()->canUser('content', 'read', $user)) { - throw new UnauthorizedException('content', 'read'); - } - - $userLocations = $locationService->loadLocations( - $user->getVersionInfo()->getContentInfo() - ); - - $parentLocationIds = []; - foreach ($userLocations as $userLocation) { - if ($userLocation->parentLocationId !== null) { - $parentLocationIds[] = $userLocation->parentLocationId; - } - } - - $searchQuery = new LocationQuery(); - - $searchQuery->offset = $offset; - $searchQuery->limit = $limit; - $searchQuery->performCount = false; - - $searchQuery->filter = new CriterionLogicalAnd( - [ - new CriterionContentTypeId($this->settings['userGroupClassID']), - new CriterionLocationId($parentLocationIds), - ] - ); - - $searchResult = $this->repository->getSearchService()->findLocations($searchQuery); - - $userGroups = []; - foreach ($searchResult->searchHits as $resultItem) { - $userGroups[] = $this->buildDomainUserGroupObject( - $this->repository->getContentService()->internalLoadContentById( - $resultItem->valueObject->contentInfo->id, - $prioritizedLanguages - ) - ); - } - - return $userGroups; - } - - /** - * Loads the users of a user group. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read the users or user group - * - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup - * @param int $offset the start offset for paging - * @param int $limit the number of users returned - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\User[] - */ - public function loadUsersOfUserGroup( - APIUserGroup $userGroup, - int $offset = 0, - int $limit = 25, - array $prioritizedLanguages = [] - ): iterable { - $loadedUserGroup = $this->loadUserGroup($userGroup->id); - - if ($loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { - return []; - } - - $mainGroupLocation = $this->repository->getLocationService()->loadLocation( - $loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId - ); - - $searchQuery = new LocationQuery(); - - $searchQuery->filter = new CriterionLogicalAnd( - [ - new CriterionContentTypeId($this->settings['userClassID']), - new CriterionParentLocationId($mainGroupLocation->id), - ] - ); - - $searchQuery->offset = $offset; - $searchQuery->limit = $limit; - $searchQuery->performCount = false; - $searchQuery->sortClauses = $mainGroupLocation->getSortClauses(); - - $searchResult = $this->repository->getSearchService()->findLocations($searchQuery); - - $users = []; - foreach ($searchResult->searchHits as $resultItem) { - $users[] = $this->buildDomainUserObject( - $this->userHandler->load($resultItem->valueObject->contentInfo->id), - $this->repository->getContentService()->internalLoadContentById( - $resultItem->valueObject->contentInfo->id, - $prioritizedLanguages - ) - ); - } - - return $users; - } - - /** - * {@inheritdoc} - */ - public function isUser(APIContent $content): bool - { - // First check against config for fast check - if ($this->settings['userClassID'] == $content->getVersionInfo()->getContentInfo()->contentTypeId) { - return true; - } - - // For users we ultimately need to look for ezuser type as content type id could be several for users. - // And config might be different from one SA to the next, which we don't care about here. - foreach ($content->getFields() as $field) { - if ($field->fieldTypeIdentifier === self::USER_FIELD_TYPE_NAME) { - return true; - } - } - - return false; - } - - /** - * {@inheritdoc} - */ - public function isUserGroup(APIContent $content): bool - { - return $this->settings['userGroupClassID'] == $content->getVersionInfo()->getContentInfo()->contentTypeId; - } - - /** - * Instantiate a user create class. - * - * @param string $login the login of the new user - * @param string $email the email of the new user - * @param string $password the plain password of the new user - * @param string $mainLanguageCode the main language for the underlying content object - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType 5.x the content type for the underlying content object. In 4.x it is ignored and taken from the configuration - * - * @return \eZ\Publish\API\Repository\Values\User\UserCreateStruct - */ - public function newUserCreateStruct(string $login, string $email, string $password, string $mainLanguageCode, ?ContentType $contentType = null): APIUserCreateStruct - { - if ($contentType === null) { - $contentType = $this->repository->getContentTypeService()->loadContentType( - $this->settings['userClassID'] - ); - } - - $fieldDefIdentifier = ''; - foreach ($contentType->fieldDefinitions as $fieldDefinition) { - if ($fieldDefinition->fieldTypeIdentifier === self::USER_FIELD_TYPE_NAME) { - $fieldDefIdentifier = $fieldDefinition->identifier; - break; - } - } - - return new UserCreateStruct( - [ - 'contentType' => $contentType, - 'mainLanguageCode' => $mainLanguageCode, - 'login' => $login, - 'email' => $email, - 'password' => $password, - 'enabled' => true, - 'fields' => [ - new Field([ - 'fieldDefIdentifier' => $fieldDefIdentifier, - 'languageCode' => $mainLanguageCode, - 'fieldTypeIdentifier' => self::USER_FIELD_TYPE_NAME, - 'value' => new UserValue([ - 'login' => $login, - 'email' => $email, - 'plainPassword' => $password, - 'enabled' => true, - 'passwordUpdatedAt' => new DateTime(), - ]), - ]), - ], - ] - ); - } - - /** - * Instantiate a user group create class. - * - * @param string $mainLanguageCode The main language for the underlying content object - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType|null $contentType 5.x the content type for the underlying content object. In 4.x it is ignored and taken from the configuration - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct - */ - public function newUserGroupCreateStruct(string $mainLanguageCode, ?ContentType $contentType = null): APIUserGroupCreateStruct - { - if ($contentType === null) { - $contentType = $this->repository->getContentTypeService()->loadContentType( - $this->settings['userGroupClassID'] - ); - } - - return new UserGroupCreateStruct( - [ - 'contentType' => $contentType, - 'mainLanguageCode' => $mainLanguageCode, - 'fields' => [], - ] - ); - } - - /** - * Instantiate a new user update struct. - * - * @return \eZ\Publish\API\Repository\Values\User\UserUpdateStruct - */ - public function newUserUpdateStruct(): UserUpdateStruct - { - return new UserUpdateStruct(); - } - - /** - * Instantiate a new user group update struct. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct - */ - public function newUserGroupUpdateStruct(): UserGroupUpdateStruct - { - return new UserGroupUpdateStruct(); - } - - /** - * {@inheritdoc} - */ - public function validatePassword(string $password, PasswordValidationContext $context = null): array - { - if ($context === null) { - $contentType = $this->repository->getContentTypeService()->loadContentType( - $this->settings['userClassID'] - ); - - $context = new PasswordValidationContext([ - 'contentType' => $contentType, - ]); - } - - // Search for the first ezuser field type in content type - $userFieldDefinition = $this->getUserFieldDefinition($context->contentType); - if ($userFieldDefinition === null) { - throw new MissingUserFieldTypeException($context->contentType, self::USER_FIELD_TYPE_NAME); - } - - return $this->passwordValidator->validatePassword( - $password, - $userFieldDefinition, - $context->user - ); - } - - /** - * Builds the domain UserGroup object from provided Content object. - * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - */ - protected function buildDomainUserGroupObject(APIContent $content): APIUserGroup - { - $locationService = $this->repository->getLocationService(); - - if ($content->getVersionInfo()->getContentInfo()->mainLocationId !== null) { - $mainLocation = $locationService->loadLocation( - $content->getVersionInfo()->getContentInfo()->mainLocationId - ); - $parentLocation = $this->locationHandler->load($mainLocation->parentLocationId); - } - - return new UserGroup( - [ - 'content' => $content, - 'parentId' => $parentLocation->contentId ?? null, - ] - ); - } - - /** - * Builds the domain user object from provided persistence user object. - * - * @param \eZ\Publish\SPI\Persistence\User $spiUser - * @param \eZ\Publish\API\Repository\Values\Content\Content|null $content - * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. - * - * @return \eZ\Publish\API\Repository\Values\User\User - */ - protected function buildDomainUserObject( - SPIUser $spiUser, - APIContent $content = null, - array $prioritizedLanguages = [] - ): APIUser { - if ($content === null) { - $content = $this->repository->getContentService()->internalLoadContentById( - $spiUser->id, - $prioritizedLanguages - ); - } - - return new User( - [ - 'content' => $content, - 'login' => $spiUser->login, - 'email' => $spiUser->email, - 'passwordHash' => $spiUser->passwordHash, - 'passwordUpdatedAt' => $this->getDateTime($spiUser->passwordUpdatedAt), - 'hashAlgorithm' => (int)$spiUser->hashAlgorithm, - 'enabled' => $spiUser->isEnabled, - 'maxLogin' => (int)$spiUser->maxLogin, - ] - ); - } - - public function getPasswordInfo(APIUser $user): PasswordInfo - { - $definition = $this->getUserFieldDefinition($user->getContentType()); - - return $this->passwordValidator->getPasswordInfo($user, $definition); - } - - private function getUserFieldDefinition(ContentType $contentType): ?FieldDefinition - { - return $contentType->getFirstFieldDefinitionOfType(self::USER_FIELD_TYPE_NAME); - } - - /** - * Verifies if the provided login and password are valid for eZ\Publish\SPI\Persistence\User. - * - * @return bool return true if the login and password are successfully validated and false, if not. - */ - protected function comparePasswordHashForSPIUser(SPIUser $user, string $password): bool - { - return $this->comparePasswordHashes($password, $user->passwordHash, $user->hashAlgorithm); - } - - /** - * Verifies if the provided login and password are valid for eZ\Publish\API\Repository\Values\User\User. - * - * @return bool return true if the login and password are successfully validated and false, if not. - */ - protected function comparePasswordHashForAPIUser(APIUser $user, string $password): bool - { - return $this->comparePasswordHashes($password, $user->passwordHash, $user->hashAlgorithm); - } - - /** - * Verifies if the provided login and password are valid against given password hash and hash type. - * - * @param string $plainPassword User password - * @param string $passwordHash User password hash - * @param int $hashAlgorithm Hash type - * - * @return bool return true if the login and password are successfully validated and false, if not. - */ - private function comparePasswordHashes( - string $plainPassword, - string $passwordHash, - int $hashAlgorithm - ): bool { - return $this->passwordHashService->isValidPassword($plainPassword, $passwordHash, $hashAlgorithm); - } - - /** - * Return true if any of the UserUpdateStruct properties refers to User Profile (Content) update. - * - * @param \eZ\Publish\API\Repository\Values\User\UserUpdateStruct $userUpdateStruct - * - * @return bool - */ - private function isUserProfileUpdateRequested(UserUpdateStruct $userUpdateStruct) - { - return - !empty($userUpdateStruct->contentUpdateStruct) || - !empty($userUpdateStruct->contentMetadataUpdateStruct) || - !empty($userUpdateStruct->email) || - !empty($userUpdateStruct->enabled) || - !empty($userUpdateStruct->maxLogin); - } - - private function getDateTime(?int $timestamp): ?DateTimeInterface - { - if ($timestamp !== null) { - // Instead of using DateTime(ts) we use setTimeStamp() so timezone does not get set to UTC - $dateTime = new DateTime(); - $dateTime->setTimestamp($timestamp); - - return DateTimeImmutable::createFromMutable($dateTime); - } - - return null; - } -} diff --git a/eZ/Publish/Core/Repository/Validator/UserPasswordValidator.php b/eZ/Publish/Core/Repository/Validator/UserPasswordValidator.php deleted file mode 100644 index 87d92818fb..0000000000 --- a/eZ/Publish/Core/Repository/Validator/UserPasswordValidator.php +++ /dev/null @@ -1,164 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Validator; - -use eZ\Publish\Core\FieldType\ValidationError; - -/** - * Internal service to user password validation against specified constraints. - * - * @internal Meant for internal use by Repository. - */ -class UserPasswordValidator -{ - private const AT_LEAST_ONE_LOWER_CASE_CHARACTER_REGEX = '/\p{Ll}/u'; - private const AT_LEAST_ONE_UPPER_CASE_CHARACTER_REGEX = '/\p{Lu}/u'; - private const AT_LEAST_ONE_NUMERIC_CHARACTER_REGEX = '/\pN/u'; - private const AT_LEAST_ONE_NON_ALPHANUMERIC_CHARACTER_REGEX = '/[^\p{Ll}\p{Lu}\pL\pN]/u'; - - /** @var array */ - private $constraints; - - /** - * @param array $constraints - */ - public function __construct(array $constraints) - { - $this->constraints = $constraints; - } - - /** - * Validates given $password. - * - * @param string $password - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(string $password): array - { - $errors = []; - - if (!$this->isLongEnough($password)) { - $errors[] = $this->createValidationError('User password must be at least %length% characters long', [ - '%length%' => $this->constraints['minLength'], - ]); - } - - if (!$this->containsAtLeastOneLowerCaseCharacter($password)) { - $errors[] = $this->createValidationError('User password must include at least one lower case letter'); - } - - if (!$this->containsAtLeastOneUpperCaseCharacter($password)) { - $errors[] = $this->createValidationError('User password must include at least one upper case letter'); - } - - if (!$this->containsAtLeastOneNumericCharacter($password)) { - $errors[] = $this->createValidationError('User password must include at least one number'); - } - - if (!$this->containsAtLeastOneNonAlphanumericCharacter($password)) { - $errors[] = $this->createValidationError('User password must include at least one special character'); - } - - return $errors; - } - - /** - * Checks if given $password satisfies length requirements. - * - * @param string $password - * - * @return bool - */ - private function isLongEnough(string $password): bool - { - if ((int) $this->constraints['minLength'] > 0) { - return mb_strlen($password) >= (int) $this->constraints['minLength']; - } - - return true; - } - - /** - * Checks if given $password contains at least one lower case character (if rule is applicable). - * - * @param string $password - * - * @return bool - */ - private function containsAtLeastOneLowerCaseCharacter(string $password): bool - { - if ($this->constraints['requireAtLeastOneLowerCaseCharacter']) { - return (bool)preg_match(self::AT_LEAST_ONE_LOWER_CASE_CHARACTER_REGEX, $password); - } - - return true; - } - - /** - * Checks if given $password contains at least one upper case character (if rule is applicable). - * - * @param string $password - * - * @return bool - */ - private function containsAtLeastOneUpperCaseCharacter(string $password): bool - { - if ($this->constraints['requireAtLeastOneUpperCaseCharacter']) { - return (bool)preg_match(self::AT_LEAST_ONE_UPPER_CASE_CHARACTER_REGEX, $password); - } - - return true; - } - - /** - * Checks if given $password contains at least one numeric character (if rule is applicable). - * - * @param string $password - * - * @return bool - */ - private function containsAtLeastOneNumericCharacter(string $password): bool - { - if ($this->constraints['requireAtLeastOneNumericCharacter']) { - return (bool)preg_match(self::AT_LEAST_ONE_NUMERIC_CHARACTER_REGEX, $password); - } - - return true; - } - - /** - * Checks if given $password contains at least one non alphanumeric character (if rule is applicable). - * - * @param string $password - * - * @return bool - */ - private function containsAtLeastOneNonAlphanumericCharacter(string $password): bool - { - if ($this->constraints['requireAtLeastOneNonAlphanumericCharacter']) { - return (bool)preg_match(self::AT_LEAST_ONE_NON_ALPHANUMERIC_CHARACTER_REGEX, $password); - } - - return true; - } - - /** - * Creates a validation error with given messages and placeholders. - * - * @param string $message - * @param array $values - * - * @return \eZ\Publish\Core\FieldType\ValidationError - */ - private function createValidationError(string $message, array $values = []): ValidationError - { - return new ValidationError($message, null, $values, 'password'); - } -} diff --git a/eZ/Publish/Core/Repository/Values/Content/Content.php b/eZ/Publish/Core/Repository/Values/Content/Content.php deleted file mode 100644 index 6edcced67b..0000000000 --- a/eZ/Publish/Core/Repository/Values/Content/Content.php +++ /dev/null @@ -1,213 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\SPI\FieldType\Value; - -/** - * this class represents a content object in a specific version. - * - * @property-read \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo convenience getter for $versionInfo->contentInfo - * @property-read \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType convenience getter for $versionInfo->contentInfo->contentType - * @property-read int $id convenience getter for retrieving the contentId: $versionInfo->content->id - * @property-read \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo calls getVersionInfo() - * @property-read \eZ\Publish\API\Repository\Values\Content\Field[] $fields Access fields, calls getFields() - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class Content extends APIContent -{ - /** @var \eZ\Publish\API\Repository\Values\Content\Thumbnail|null */ - protected $thumbnail; - - /** @var mixed[][] An array of array of field values like[$fieldDefIdentifier][$languageCode] */ - protected $fields; - - /** @var \eZ\Publish\API\Repository\Values\Content\VersionInfo */ - protected $versionInfo; - - /** @var \eZ\Publish\API\Repository\Values\ContentType\ContentType */ - protected $contentType; - - /** @var \eZ\Publish\API\Repository\Values\Content\Field[] An array of {@link Field} */ - private $internalFields; - - /** - * In-memory cache of Field Definition Identifier and Language Code mapped to a Field instance. - * - * <code>$fieldDefinitionTranslationMap[$fieldDefIdentifier][$languageCode] = $field</code> - * - * @var array<string, array<string, \eZ\Publish\API\Repository\Values\Content\Field>> - */ - private $fieldDefinitionTranslationMap = []; - - /** - * The first matched field language among user provided prioritized languages. - * - * The first matched language among user provided prioritized languages on object retrieval, or null if none - * provided (all languages) or on main fallback. - * - * @internal - * - * @var string|null - */ - protected $prioritizedFieldLanguageCode; - - public function __construct(array $data = []) - { - parent::__construct([]); - - $this->thumbnail = $data['thumbnail'] ?? null; - $this->versionInfo = $data['versionInfo'] ?? null; - $this->contentType = $data['contentType'] ?? null; - $this->internalFields = $data['internalFields'] ?? []; - $this->prioritizedFieldLanguageCode = $data['prioritizedFieldLanguageCode'] ?? null; - - foreach ($this->internalFields as $field) { - $this->fieldDefinitionTranslationMap[$field->fieldDefIdentifier][$field->languageCode] = $field; - // kept for BC due to property-read magic getter - $this->fields[$field->fieldDefIdentifier][$field->languageCode] = $field->value; - } - } - - public function getThumbnail(): ?Thumbnail - { - return $this->thumbnail; - } - - /** - * {@inheritdoc} - */ - public function getVersionInfo(): APIVersionInfo - { - return $this->versionInfo; - } - - /** - * {@inheritdoc} - */ - public function getContentType(): ContentType - { - return $this->contentType; - } - - /** - * {@inheritdoc} - */ - public function getFieldValue(string $fieldDefIdentifier, ?string $languageCode = null): ?Value - { - if (null === $languageCode) { - $languageCode = $this->getDefaultLanguageCode(); - } - - if (!isset($this->fieldDefinitionTranslationMap[$fieldDefIdentifier][$languageCode])) { - return null; - } - - return $this->fieldDefinitionTranslationMap[$fieldDefIdentifier][$languageCode]->getValue(); - } - - /** - * {@inheritdoc} - */ - public function getFields(): iterable - { - return $this->internalFields; - } - - /** - * {@inheritdoc} - */ - public function getFieldsByLanguage(?string $languageCode = null): iterable - { - $fields = []; - - if (null === $languageCode) { - $languageCode = $this->getDefaultLanguageCode(); - } - - $filteredFields = array_filter( - $this->internalFields, - static function (Field $field) use ($languageCode): bool { - return $field->languageCode === $languageCode; - } - ); - foreach ($filteredFields as $field) { - $fields[$field->fieldDefIdentifier] = $field; - } - - return $fields; - } - - /** - * {@inheritdoc} - */ - public function getField(string $fieldDefIdentifier, ?string $languageCode = null): ?Field - { - if (null === $languageCode) { - $languageCode = $this->getDefaultLanguageCode(); - } - - return $this->fieldDefinitionTranslationMap[$fieldDefIdentifier][$languageCode] ?? null; - } - - public function getDefaultLanguageCode(): string - { - return $this->prioritizedFieldLanguageCode - ?? $this->versionInfo->getContentInfo()->getMainLanguageCode(); - } - - /** - * {@inheritdoc} - */ - protected function getProperties($dynamicProperties = ['id', 'contentInfo']) - { - return parent::getProperties($dynamicProperties); - } - - /** - * {@inheritdoc} - */ - public function __get($property) - { - switch ($property) { - case 'id': - return $this->versionInfo->getContentInfo()->id; - - case 'contentInfo': - return $this->versionInfo->getContentInfo(); - - case 'thumbnail': - return $this->getThumbnail(); - } - - return parent::__get($property); - } - - /** - * {@inheritdoc} - */ - public function __isset($property) - { - if ($property === 'id') { - return true; - } - - if ($property === 'contentInfo') { - return true; - } - - return parent::__isset($property); - } -} diff --git a/eZ/Publish/Core/Repository/Values/Content/ContentCreateStruct.php b/eZ/Publish/Core/Repository/Values/Content/ContentCreateStruct.php deleted file mode 100644 index ff4a7dbd74..0000000000 --- a/eZ/Publish/Core/Repository/Values/Content/ContentCreateStruct.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct as APIContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Field; - -/** - * This class is used for creating a new content object. - * - * @property \eZ\Publish\API\Repository\Values\Content\Field[] $fields - * - * @internal Meant for internal use by Repository, type hint against API instead. - */ -class ContentCreateStruct extends APIContentCreateStruct -{ - /** - * Field collection. - * - * @var \eZ\Publish\API\Repository\Values\Content\Field[] - */ - public $fields = []; - - /** - * Adds a field to the field collection. - * - * This method could also be implemented by a magic setter so that - * $fields[$fieldDefIdentifier][$language] = $value or without language $fields[$fieldDefIdentifier] = $value - * is an equivalent call. - * - * @param string $fieldDefIdentifier the identifier of the field definition - * @param mixed $value Either a plain value which is understandable by the corresponding - * field type or an instance of a Value class provided by the field type - * @param string|null $language If not given on a translatable field the initial language is used - */ - public function setField(string $fieldDefIdentifier, $value, ?string $language = null): void - { - if (!isset($language)) { - $language = $this->mainLanguageCode; - } - - $this->fields[] = new Field( - [ - 'fieldDefIdentifier' => $fieldDefIdentifier, - 'value' => $value, - 'languageCode' => $language, - ] - ); - } -} diff --git a/eZ/Publish/Core/Repository/Values/Content/ContentUpdateStruct.php b/eZ/Publish/Core/Repository/Values/Content/ContentUpdateStruct.php deleted file mode 100644 index 9bf632a2e2..0000000000 --- a/eZ/Publish/Core/Repository/Values/Content/ContentUpdateStruct.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct as APIContentUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\Field; - -/** - * This class is used for updating the fields of a content object draft. - * - * @property \eZ\Publish\API\Repository\Values\Content\Field[] $fields - * - * @internal Meant for internal use by Repository, type hint against API instead. - */ -class ContentUpdateStruct extends APIContentUpdateStruct -{ - /** - * Field collection. - * - * @var \eZ\Publish\API\Repository\Values\Content\Field[] - */ - public $fields = []; - - /** - * Adds a field to the field collection. - * This method could also be implemented by ArrayAccess so that - * $fields[$fieldDefIdentifier][$language] = $value or without language $fields[$fieldDefIdentifier] = $value - * is an equivalent call. - * - * @param string $fieldDefIdentifier the identifier of the field definition - * @param mixed $value Either a plain value which is understandable by the field type or an instance of a Value class provided by the field type - * @param string|null $language If not given on a translatable field the initial language is used, - */ - public function setField(string $fieldDefIdentifier, $value, ?string $language = null): void - { - $this->fields[] = new Field( - [ - 'fieldDefIdentifier' => $fieldDefIdentifier, - 'value' => $value, - 'languageCode' => $language, - ] - ); - } -} diff --git a/eZ/Publish/Core/Repository/Values/Content/Location.php b/eZ/Publish/Core/Repository/Values/Content/Location.php deleted file mode 100644 index 68d30e1428..0000000000 --- a/eZ/Publish/Core/Repository/Values/Content/Location.php +++ /dev/null @@ -1,106 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo as APIContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; - -/** - * This class represents a location in the repository. - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class Location extends APILocation -{ - /** - * Content info of the content object of this location. - * - * @var \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - protected $contentInfo; - - /** @var array */ - protected $path; - - /** @var \eZ\Publish\API\Repository\Values\Content\Location|null */ - protected $parentLocation; - - /** - * Returns the content info of the content object of this location. - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - public function getContentInfo(): APIContentInfo - { - return $this->contentInfo; - } - - public function getParentLocation(): ?APILocation - { - return $this->parentLocation; - } - - /** - * Function where list of properties are returned. - * - * Override to add dynamic properties - * - * @uses \parent::getProperties() - * - * @param array $dynamicProperties - * - * @return array - */ - protected function getProperties($dynamicProperties = ['contentId']) - { - return parent::getProperties($dynamicProperties); - } - - /** - * Magic getter for retrieving convenience properties. - * - * @param string $property The name of the property to retrieve - * - * @return mixed - */ - public function __get($property) - { - switch ($property) { - case 'contentId': - return $this->contentInfo->id; - case 'path': - if ($this->path !== null) { - return $this->path; - } - if (isset($this->pathString[1]) && $this->pathString[0] === '/') { - return $this->path = explode('/', trim($this->pathString, '/')); - } - - return $this->path = []; - } - - return parent::__get($property); - } - - /** - * Magic isset for signaling existence of convenience properties. - * - * @param string $property - * - * @return bool - */ - public function __isset($property) - { - if ($property === 'contentId' || $property === 'path') { - return true; - } - - return parent::__isset($property); - } -} diff --git a/eZ/Publish/Core/Repository/Values/Content/Query/Criterion/PermissionSubtree.php b/eZ/Publish/Core/Repository/Values/Content/Query/Criterion/PermissionSubtree.php deleted file mode 100644 index 7bb06314b7..0000000000 --- a/eZ/Publish/Core/Repository/Values/Content/Query/Criterion/PermissionSubtree.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Values\Content\Query\Criterion; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree as APISubtreeCriterion; - -/** - * Criterion that matches content that belongs to a given (list of) Subtree(s). - * - * Content will be matched if it is part of at least one of the given subtree path strings - * - * This is a internal subtree criterion intended for use by permission system (SubtreeLimitationType) only! - * And will be applied by SQL based search engines on Content Search to avoid performance problems. - * - * @see https://jira.ez.no/browse/EZP-23037 - * - * @internal Meant for internal use by Repository. - */ -class PermissionSubtree extends APISubtreeCriterion -{ - /** - * @deprecated since 7.2, will be removed in 8.0. Use the constructor directly instead. - */ - public static function createFromQueryBuilder($target, $operator, $value) - { - @trigger_error('The ' . __METHOD__ . ' method is deprecated since version 7.2 and will be removed in 8.0.', E_USER_DEPRECATED); - - return new self($value); - } -} diff --git a/eZ/Publish/Core/Repository/Values/Content/Relation.php b/eZ/Publish/Core/Repository/Values/Content/Relation.php deleted file mode 100644 index 332861dbb1..0000000000 --- a/eZ/Publish/Core/Repository/Values/Content/Relation.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo as APIContentInfo; -use eZ\Publish\API\Repository\Values\Content\Relation as APIRelation; - -/** - * Class representing a relation between content. - * - * @property-read mixed $id the internal id of the relation - * @property-read string $sourceFieldDefinitionIdentifier the field definition identifier of the field where this relation is anchored if the relation is of type EMBED, LINK, or ATTRIBUTE - * @property-read \eZ\Publish\API\Repository\Values\Content\ContentInfo $sourceContentInfo - calls {@link getSourceContentInfo()} - * @property-read \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContentInfo - calls {@link getDestinationContentInfo()} - * @property-read int $type The relation type bitmask containing one or more of Relation::COMMON, Relation::EMBED, Relation::LINK, Relation::FIELD - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class Relation extends APIRelation -{ - /** - * the content of the source content of the relation. - * - * @var \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - protected $sourceContentInfo; - - /** - * the content of the destination content of the relation. - * - * @var \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - protected $destinationContentInfo; - - /** - * the content of the source content of the relation. - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - public function getSourceContentInfo(): APIContentInfo - { - return $this->sourceContentInfo; - } - - /** - * the content of the destination content of the relation. - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - public function getDestinationContentInfo(): APIContentInfo - { - return $this->destinationContentInfo; - } -} diff --git a/eZ/Publish/Core/Repository/Values/Content/TrashItem.php b/eZ/Publish/Core/Repository/Values/Content/TrashItem.php deleted file mode 100644 index b1e111915d..0000000000 --- a/eZ/Publish/Core/Repository/Values/Content/TrashItem.php +++ /dev/null @@ -1,107 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo as APIContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\TrashItem as APITrashItem; - -/** - * this class represents a trash item, which is actually a trashed location. - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class TrashItem extends APITrashItem -{ - /** - * Content info of the content object of this trash item. - * - * @var \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - protected $contentInfo; - - /** @var array */ - protected $path; - - /** @var \eZ\Publish\API\Repository\Values\Content\Location */ - protected $parentLocation; - - /** - * Returns the content info of the content object of this trash item. - * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo - */ - public function getContentInfo(): APIContentInfo - { - return $this->contentInfo; - } - - public function getParentLocation(): ?Location - { - return $this->parentLocation; - } - - /** - * Function where list of properties are returned. - * - * Override to add dynamic properties - * - * @uses \parent::getProperties() - * - * @param array $dynamicProperties - * - * @return array - */ - protected function getProperties($dynamicProperties = ['contentId', 'path']) - { - return parent::getProperties($dynamicProperties); - } - - /** - * Magic getter for retrieving convenience properties. - * - * @param string $property The name of the property to retrieve - * - * @return mixed - */ - public function __get($property) - { - switch ($property) { - case 'contentId': - return $this->contentInfo->id; - case 'path': - if ($this->path !== null) { - return $this->path; - } - if (isset($this->pathString[1]) && $this->pathString[0] === '/') { - return $this->path = explode('/', trim($this->pathString, '/')); - } - - return $this->path = []; - } - - return parent::__get($property); - } - - /** - * Magic isset for signaling existence of convenience properties. - * - * @param string $property - * - * @return bool - */ - public function __isset($property) - { - if ($property === 'contentId' || $property === 'path') { - return true; - } - - return parent::__isset($property); - } -} diff --git a/eZ/Publish/Core/Repository/Values/Content/VersionInfo.php b/eZ/Publish/Core/Repository/Values/Content/VersionInfo.php deleted file mode 100644 index c6bd09411c..0000000000 --- a/eZ/Publish/Core/Repository/Values/Content/VersionInfo.php +++ /dev/null @@ -1,118 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Values\Content; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\User\User; - -/** - * This class holds version information data. It also contains the corresponding {@link Content} to - * which the version belongs to. - * - * @property-read string[] $names returns an array with language code keys and name values - * @property-read \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo calls getContentInfo() - * @property-read int $id the internal id of the version - * @property-read int $versionNo the version number of this version (which only increments in scope of a single Content object) - * @property-read \DateTime $modifiedDate the last modified date of this version - * @property-read \DateTime $createdDate the creation date of this version - * @property-read int $creatorId the user id of the user which created this version - * @property-read int $status the status of this version. One of VersionInfo::STATUS_DRAFT, VersionInfo::STATUS_PUBLISHED, VersionInfo::STATUS_ARCHIVED - * @property-read string $initialLanguageCode the language code of the version. This value is used to flag a version as a translation to specific language - * @property-read string[] $languageCodes a collection of all languages which exist in this version. - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class VersionInfo extends APIVersionInfo -{ - /** @var string[] */ - protected $names; - - /** @var \eZ\Publish\API\Repository\Values\Content\ContentInfo */ - protected $contentInfo; - - /** @var \eZ\Publish\API\Repository\Values\User\User */ - protected $creator; - - /** @var \eZ\Publish\API\Repository\Values\Content\Language */ - protected $initialLanguage; - - /** @var \eZ\Publish\API\Repository\Values\Content\Language[] */ - protected $languages; - - /** - * The first matched name language among user provided prioritized languages. - * - * The first matched language among user provided prioritized languages on object retrieval, or null if none - * provided (all languages) or on main fallback. - * - * @internal - * - * @var string|null - */ - protected $prioritizedNameLanguageCode; - - /** - * {@inheritdoc} - */ - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - /** - * {@inheritdoc} - */ - public function getCreator(): User - { - return $this->creator; - } - - /** - * {@inheritdoc} - */ - public function getInitialLanguage(): Language - { - return $this->initialLanguage; - } - - /** - * {@inheritdoc} - */ - public function getLanguages(): iterable - { - return $this->languages; - } - - /** - * {@inheritdoc} - */ - public function getNames() - { - return $this->names; - } - - /** - * {@inheritdoc} - */ - public function getName($languageCode = null) - { - if ($languageCode) { - return isset($this->names[$languageCode]) ? $this->names[$languageCode] : null; - } - - if ($this->prioritizedNameLanguageCode) { - return $this->names[$this->prioritizedNameLanguageCode]; - } elseif (!empty($this->contentInfo->alwaysAvailable) && isset($this->names[$this->contentInfo->mainLanguageCode])) { - return $this->names[$this->contentInfo->mainLanguageCode]; - } - - // Versioned name should always exists in initial language for a version so we use that as final fallback - return $this->names[$this->initialLanguageCode]; - } -} diff --git a/eZ/Publish/Core/Repository/Values/ContentType/ContentType.php b/eZ/Publish/Core/Repository/Values/ContentType/ContentType.php deleted file mode 100644 index 7720e64f74..0000000000 --- a/eZ/Publish/Core/Repository/Values/ContentType/ContentType.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Values\ContentType; - -use eZ\Publish\API\Repository\Values\ContentType\ContentType as APIContentType; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCollection as APIFieldDefinitionCollection; -use eZ\Publish\Core\Repository\Values\MultiLanguageDescriptionTrait; -use eZ\Publish\Core\Repository\Values\MultiLanguageNameTrait; -use eZ\Publish\Core\Repository\Values\MultiLanguageTrait; - -/** - * this class represents a content type value. - * - * @property-read \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup[] $contentTypeGroups calls getContentTypeGroups - * @property-read \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCollection $fieldDefinitions calls getFieldDefinitions() or on access getFieldDefinition($fieldDefIdentifier) - * @property-read mixed $id the id of the content type - * @property-read int $status the status of the content type. One of ContentType::STATUS_DEFINED|ContentType::STATUS_DRAFT|ContentType::STATUS_MODIFIED - * @property-read string $identifier the identifier of the content type - * @property-read \DateTime $creationDate the date of the creation of this content type - * @property-read \DateTime $modificationDate the date of the last modification of this content type - * @property-read mixed $creatorId the user id of the creator of this content type - * @property-read mixed $modifierId the user id of the user which has last modified this content type - * @property-read string $remoteId a global unique id of the content object - * @property-read string $urlAliasSchema URL alias schema. If nothing is provided, $nameSchema will be used instead. - * @property-read string $nameSchema The name schema. - * @property-read bool $isContainer This flag hints to UIs if type may have children or not. - * @property-read string $mainLanguageCode the main language of the content type names and description used for fallback. - * @property-read bool $defaultAlwaysAvailable if an instance of a content type is created the always available flag is set by default this this value. - * @property-read int $defaultSortField Specifies which property the child locations should be sorted on by default when created. Valid values are found at {@link Location::SORT_FIELD_*} - * @property-read int $defaultSortOrder Specifies whether the sort order should be ascending or descending by default when created. Valid values are {@link Location::SORT_ORDER_*} - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class ContentType extends APIContentType -{ - use MultiLanguageTrait; - use MultiLanguageNameTrait; - use MultiLanguageDescriptionTrait; - - /** - * Holds the collection of contenttypegroups the contenttype is assigned to. - * - * @var \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup[] - */ - protected $contentTypeGroups = []; - - /** - * Contains the content type field definitions from this type. - * - * @var \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCollection - */ - protected $fieldDefinitions; - - public function __construct(array $data = []) - { - $this->fieldDefinitions = new FieldDefinitionCollection(); - - parent::__construct($data); - } - - /** - * This method returns the content type groups this content type is assigned to. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup[] - */ - public function getContentTypeGroups() - { - return $this->contentTypeGroups; - } - - /** - * This method returns the content type field definitions from this type. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCollection - */ - public function getFieldDefinitions(): APIFieldDefinitionCollection - { - return $this->fieldDefinitions; - } -} diff --git a/eZ/Publish/Core/Repository/Values/ContentType/ContentTypeCreateStruct.php b/eZ/Publish/Core/Repository/Values/ContentType/ContentTypeCreateStruct.php deleted file mode 100644 index f8d57db069..0000000000 --- a/eZ/Publish/Core/Repository/Values/ContentType/ContentTypeCreateStruct.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Values\ContentType; - -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeCreateStruct as APIContentTypeCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; - -/** - * this class is used for creating content types. - * - * @property \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct[] $fieldDefinitions the collection of field definitions - * - * @internal Meant for internal use by Repository, type hint against API instead. - */ -class ContentTypeCreateStruct extends APIContentTypeCreateStruct -{ - /** - * Holds the collection of field definitions. - * - * @var \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct[] - */ - public $fieldDefinitions = []; - - /** - * Adds a new field definition. - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct $fieldDef - */ - public function addFieldDefinition(FieldDefinitionCreateStruct $fieldDef): void - { - $this->fieldDefinitions[] = $fieldDef; - } -} diff --git a/eZ/Publish/Core/Repository/Values/ContentType/ContentTypeDraft.php b/eZ/Publish/Core/Repository/Values/ContentType/ContentTypeDraft.php deleted file mode 100644 index 48e0bdac69..0000000000 --- a/eZ/Publish/Core/Repository/Values/ContentType/ContentTypeDraft.php +++ /dev/null @@ -1,151 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Values\ContentType; - -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft as APIContentTypeDraft; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCollection as APIFieldDefinitionCollection; -use eZ\Publish\Core\Repository\Values\MultiLanguageTrait; - -/** - * This class represents a draft of a content type. - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class ContentTypeDraft extends APIContentTypeDraft -{ - use MultiLanguageTrait; - - /** - * Function where list of properties are returned. - * - * Override to add dynamic properties - * - * @uses \parent::getProperties() - * - * @param array $dynamicProperties - * - * @return array - */ - protected function getProperties($dynamicProperties = ['contentTypeGroups', 'fieldDefinitions']) - { - return parent::getProperties($dynamicProperties); - } - - /** - * Magic getter for routing get calls to innerContentType. - * - * @param string $property The name of the property to retrieve - * - * @return mixed - */ - public function __get($property) - { - return $this->innerContentType->$property; - } - - /** - * Magic set for routing set calls to innerContentType. - * - * @param string $property - * @param mixed $propertyValue - */ - public function __set($property, $propertyValue) - { - $this->innerContentType->$property = $propertyValue; - } - - /** - * Magic isset for routing isset calls to innerContentType. - * - * @param string $property - * - * @return bool - */ - public function __isset($property) - { - return $this->innerContentType->__isset($property); - } - - /** - * Holds internal content type object. - * - * @var \eZ\Publish\API\Repository\Values\ContentType\ContentType - * - * @todo document - */ - protected $innerContentType; - - /** - * {@inheritdoc} - */ - public function getNames() - { - return $this->innerContentType->getNames(); - } - - /** - * {@inheritdoc} - */ - public function getName($languageCode = null) - { - return $this->innerContentType->getName($languageCode); - } - - /** - * {@inheritdoc} - */ - public function getDescriptions() - { - return $this->innerContentType->getDescriptions(); - } - - /** - * {@inheritdoc} - */ - public function getDescription($languageCode = null) - { - return $this->innerContentType->getDescription($languageCode); - } - - /** - * This method returns the content type groups this content type is assigned to. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup[] - */ - public function getContentTypeGroups() - { - return $this->innerContentType->contentTypeGroups; - } - - /** - * This method returns the content type field definitions from this type. - * - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] - */ - public function getFieldDefinitions(): APIFieldDefinitionCollection - { - return $this->innerContentType->getFieldDefinitions(); - } - - /** - * This method returns the field definition for the given identifier. - * - * @param string $fieldDefinitionIdentifier - * - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition - */ - public function getFieldDefinition($fieldDefinitionIdentifier): ?FieldDefinition - { - return $this->innerContentType->getFieldDefinition($fieldDefinitionIdentifier); - } - - public function hasFieldDefinition(string $fieldDefinitionIdentifier): bool - { - return $this->innerContentType->hasFieldDefinition($fieldDefinitionIdentifier); - } -} diff --git a/eZ/Publish/Core/Repository/Values/ContentType/ContentTypeGroup.php b/eZ/Publish/Core/Repository/Values/ContentType/ContentTypeGroup.php deleted file mode 100644 index ebe08a3a49..0000000000 --- a/eZ/Publish/Core/Repository/Values/ContentType/ContentTypeGroup.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Values\ContentType; - -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup as APIContentTypeGroup; -use eZ\Publish\Core\Repository\Values\MultiLanguageDescriptionTrait; -use eZ\Publish\Core\Repository\Values\MultiLanguageNameTrait; -use eZ\Publish\Core\Repository\Values\MultiLanguageTrait; - -/** - * This class represents a content type group value. - * - * @property-read string[] $names calls getNames() or on access getName($language) - * @property-read string[] $descriptions calls getDescriptions() or on access getDescription($language) - * @property-read mixed $id the id of the content type group - * @property-read string $identifier the identifier of the content type group - * @property-read \DateTime $creationDate the date of the creation of this content type group - * @property-read \DateTime $modificationDate the date of the last modification of this content type group - * @property-read mixed $creatorId the user id of the creator of this content type group - * @property-read mixed $modifierId the user id of the user which has last modified this content type group - * @property-read string $mainLanguageCode 5.0, the main language of the content type group names and description used for fallback. - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class ContentTypeGroup extends APIContentTypeGroup -{ - use MultiLanguageTrait; - use MultiLanguageNameTrait; - use MultiLanguageDescriptionTrait; -} diff --git a/eZ/Publish/Core/Repository/Values/ContentType/FieldDefinition.php b/eZ/Publish/Core/Repository/Values/ContentType/FieldDefinition.php deleted file mode 100644 index e2ef599953..0000000000 --- a/eZ/Publish/Core/Repository/Values/ContentType/FieldDefinition.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\ContentType; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition; -use eZ\Publish\Core\Repository\Values\MultiLanguageDescriptionTrait; -use eZ\Publish\Core\Repository\Values\MultiLanguageNameTrait; -use eZ\Publish\Core\Repository\Values\MultiLanguageTrait; - -/** - * This class represents a field definition. - * - * @property-read string[] $names calls getNames() or on access getName($language) - * @property-read string[] $descriptions calls getDescriptions() or on access getDescription($language) - * @property-read array $fieldSettings calls getFieldSettings() - * @property-read array $validatorConfiguration calls getValidatorConfiguration() - * @property-read mixed $id the id of the field definition - * @property-read string $identifier the identifier of the field definition - * @property-read string $fieldGroup the field group name - * @property-read int $position the position of the field definition in the content type - * @property-read string $fieldTypeIdentifier String identifier of the field type - * @property-read bool $isTranslatable indicates if fields of this definition are translatable - * @property-read bool $isRequired indicates if this field is required in the content object - * @property-read bool $isSearchable indicates if the field is searchable - * @property-read bool $isInfoCollector indicates if this field is used for information collection - * @property-read mixed $defaultValue the default value of the field - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class FieldDefinition extends APIFieldDefinition -{ - use MultiLanguageTrait; - use MultiLanguageNameTrait; - use MultiLanguageDescriptionTrait; - - /** - * Holds collection of settings for the field definition supported by the field type. - * - * @var array - */ - protected $fieldSettings = []; - - /** - * Holds validator configuration of this field definition supported by the field type. - * - * @var array - */ - protected $validatorConfiguration = []; - - /** - * This method returns the validator configuration of this field definition supported by the field type. - * - * @return array - */ - public function getValidatorConfiguration(): array - { - return $this->validatorConfiguration; - } - - /** - * This method returns settings for the field definition supported by the field type. - * - * @return array - */ - public function getFieldSettings(): array - { - return $this->fieldSettings; - } -} diff --git a/eZ/Publish/Core/Repository/Values/ContentType/FieldDefinitionCollection.php b/eZ/Publish/Core/Repository/Values/ContentType/FieldDefinitionCollection.php deleted file mode 100644 index 89fd13994e..0000000000 --- a/eZ/Publish/Core/Repository/Values/ContentType/FieldDefinitionCollection.php +++ /dev/null @@ -1,195 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\ContentType; - -use ArrayIterator; -use BadMethodCallException; -use Closure; -use eZ\Publish\API\Repository\Exceptions\OutOfBoundsException; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCollection as FieldDefinitionCollectionInterface; -use Iterator; - -final class FieldDefinitionCollection implements FieldDefinitionCollectionInterface -{ - /** @var \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] */ - private $fieldDefinitions; - - /** @var \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] */ - private $fieldDefinitionsByIdentifier; - - /** - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition[] - */ - public function __construct(iterable $fieldDefinitions = []) - { - $this->fieldDefinitions = []; - $this->fieldDefinitionsByIdentifier = []; - - foreach ($fieldDefinitions as $fieldDefinition) { - $this->fieldDefinitions[] = $fieldDefinition; - $this->fieldDefinitionsByIdentifier[$fieldDefinition->identifier] = $fieldDefinition; - } - } - - public function get(string $fieldDefinitionIdentifier): FieldDefinition - { - if ($this->has($fieldDefinitionIdentifier)) { - return $this->fieldDefinitionsByIdentifier[$fieldDefinitionIdentifier]; - } - - throw new OutOfBoundsException( - sprintf("Field Definition Collection does not contain element with identifier '%s'", $fieldDefinitionIdentifier) - ); - } - - public function has(string $fieldDefinitionIdentifier): bool - { - return array_key_exists($fieldDefinitionIdentifier, $this->fieldDefinitionsByIdentifier); - } - - public function first(): FieldDefinition - { - if (($result = reset($this->fieldDefinitions)) !== false) { - return $result; - } - - throw new OutOfBoundsException('Field Definition Collection is empty'); - } - - public function last(): FieldDefinition - { - if (($result = end($this->fieldDefinitions)) !== false) { - return $result; - } - - throw new OutOfBoundsException('Field Definition Collection is empty'); - } - - public function isEmpty(): bool - { - return empty($this->fieldDefinitions); - } - - public function filter(Closure $predicate): FieldDefinitionCollectionInterface - { - return new self(array_filter($this->fieldDefinitions, $predicate)); - } - - public function filterByType(string $fieldTypeIdentifier): FieldDefinitionCollectionInterface - { - return $this->filter($this->getIsTypePredicate($fieldTypeIdentifier)); - } - - public function filterByGroup(string $fieldGroup): FieldDefinitionCollectionInterface - { - return $this->filter($this->getInGroupPredicate($fieldGroup)); - } - - public function map(Closure $predicate): array - { - return array_map($predicate, $this->fieldDefinitions); - } - - public function all(Closure $predicate): bool - { - foreach ($this->fieldDefinitions as $fieldDefinition) { - if (!$predicate($fieldDefinition)) { - return false; - } - } - - return true; - } - - public function any(Closure $predicate): bool - { - foreach ($this->fieldDefinitions as $fieldDefinition) { - if ($predicate($fieldDefinition)) { - return true; - } - } - - return false; - } - - public function anyOfType(string $fieldTypeIdentifier): bool - { - return $this->any($this->getIsTypePredicate($fieldTypeIdentifier)); - } - - public function anyInGroup(string $fieldGroup): bool - { - return $this->any($this->getInGroupPredicate($fieldGroup)); - } - - public function partition(Closure $predicate): array - { - $matches = $noMatches = []; - - foreach ($this->fieldDefinitions as $fieldDefinition) { - if ($predicate($fieldDefinition)) { - $matches[] = $fieldDefinition; - } else { - $noMatches[] = $fieldDefinition; - } - } - - return [new self($matches), new self($noMatches)]; - } - - public function count(): int - { - return count($this->fieldDefinitions); - } - - public function getIterator(): Iterator - { - return new ArrayIterator($this->fieldDefinitions); - } - - public function toArray(): array - { - return $this->fieldDefinitions; - } - - private function getIsTypePredicate(string $fieldTypeIdentifier): Closure - { - return static function (FieldDefinition $fieldDefinition) use ($fieldTypeIdentifier) { - return $fieldDefinition->fieldTypeIdentifier === $fieldTypeIdentifier; - }; - } - - private function getInGroupPredicate(string $fieldGroup): Closure - { - return static function (FieldDefinition $fieldDefinition) use ($fieldGroup) { - return $fieldDefinition->fieldGroup === $fieldGroup; - }; - } - - public function offsetExists($offset): bool - { - return isset($this->fieldDefinitions[$offset]); - } - - public function offsetGet($offset): FieldDefinition - { - return $this->fieldDefinitions[$offset]; - } - - public function offsetSet($offset, $value): void - { - throw new BadMethodCallException(self::class . ' is read-only!'); - } - - public function offsetUnset($offset): void - { - throw new BadMethodCallException(self::class . ' is read-only!'); - } -} diff --git a/eZ/Publish/Core/Repository/Values/ContentType/FieldType.php b/eZ/Publish/Core/Repository/Values/ContentType/FieldType.php deleted file mode 100644 index 5db32f460e..0000000000 --- a/eZ/Publish/Core/Repository/Values/ContentType/FieldType.php +++ /dev/null @@ -1,299 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\ContentType; - -use eZ\Publish\API\Repository\FieldType as FieldTypeInterface; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition; -use eZ\Publish\SPI\FieldType\FieldType as SPIFieldTypeInterface; -use eZ\Publish\SPI\FieldType\Value; - -/** - * This class represents a FieldType available to Public API users. - * - * @see \eZ\Publish\API\Repository\FieldType - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class FieldType implements FieldTypeInterface -{ - /** - * Holds internal FieldType object. - * - * @var \eZ\Publish\Core\FieldType\FieldType - */ - protected $internalFieldType; - - /** - * @param \eZ\Publish\SPI\FieldType\FieldType $fieldType - */ - public function __construct(SPIFieldTypeInterface $fieldType) - { - $this->internalFieldType = $fieldType; - } - - /** - * Returns the field type identifier for this field type. - * - * @return string - */ - public function getFieldTypeIdentifier(): string - { - return $this->internalFieldType->getFieldTypeIdentifier(); - } - - public function getName(Value $value, APIFieldDefinition $fieldDefinition, string $languageCode): string - { - return $this->internalFieldType->getName($value, $fieldDefinition, $languageCode); - } - - /** - * Returns a schema for the settings expected by the FieldType. - * - * Returns an arbitrary value, representing a schema for the settings of - * the FieldType. - * - * Explanation: There are no possible generic schemas for defining settings - * input, which is why no schema for the return value of this method is - * defined. It is up to the implementer to define and document a schema for - * the return value and document it. In addition, it is necessary that all - * consumers of this interface (e.g. Public API, REST API, GUIs, ...) - * provide plugin mechanisms to hook adapters for the specific FieldType - * into. These adapters then need to be either shipped with the FieldType - * or need to be implemented by a third party. If there is no adapter - * available for a specific FieldType, it will not be usable with the - * consumer. - * - * @return mixed - */ - public function getSettingsSchema() - { - return $this->internalFieldType->getSettingsSchema(); - } - - /** - * Returns a schema for the validator configuration expected by the FieldType. - * - * Returns an arbitrary value, representing a schema for the validator - * configuration of the FieldType. - * - * Explanation: There are no possible generic schemas for defining settings - * input, which is why no schema for the return value of this method is - * defined. It is up to the implementer to define and document a schema for - * the return value and document it. In addition, it is necessary that all - * consumers of this interface (e.g. Public API, REST API, GUIs, ...) - * provide plugin mechanisms to hook adapters for the specific FieldType - * into. These adapters then need to be either shipped with the FieldType - * or need to be implemented by a third party. If there is no adapter - * available for a specific FieldType, it will not be usable with the - * consumer. - * - * Best practice: - * - * It is considered best practice to return a hash map, which contains - * rudimentary settings structures, like e.g. for the "ezstring" FieldType - * - * <code> - * array( - * 'stringLength' => array( - * 'minStringLength' => array( - * 'type' => 'int', - * 'default' => 0, - * ), - * 'maxStringLength' => array( - * 'type' => 'int' - * 'default' => null, - * ) - * ), - * ); - * </code> - * - * @return mixed - */ - public function getValidatorConfigurationSchema() - { - return $this->internalFieldType->getValidatorConfigurationSchema(); - } - - /** - * Indicates if the field type supports indexing and sort keys for searching. - * - * @return bool - */ - public function isSearchable(): bool - { - return $this->internalFieldType->isSearchable(); - } - - /** - * Indicates if the field definition of this type can appear only once in the same ContentType. - * - * @return bool - */ - public function isSingular(): bool - { - return $this->internalFieldType->isSingular(); - } - - /** - * Indicates if the field definition of this type can be added to a ContentType with Content instances. - * - * @return bool - */ - public function onlyEmptyInstance(): bool - { - return $this->internalFieldType->onlyEmptyInstance(); - } - - /** - * Returns the fallback default value of field type when no such default - * value is provided in the field definition in content types. - * - * @return mixed - */ - public function getEmptyValue() - { - return $this->internalFieldType->getEmptyValue(); - } - - /** - * Returns if the given $value is considered empty by the field type. - * - * Usually, only the value returned by {@link getEmptyValue()} is - * considered empty but that is not always the case. - * - * Note: This function assumes that $value is valid so this function can only - * be used reliably on $values that came from the API, not from the user. - * - * @param mixed $value - * - * @return bool - */ - public function isEmptyValue($value): bool - { - return $this->internalFieldType->isEmptyValue($value); - } - - /** - * Converts an $hash to the Value defined by the field type. - * - * @param mixed $hash - * - * @return mixed - */ - public function fromHash($hash) - { - return $this->internalFieldType->fromHash($hash); - } - - /** - * Converts a Value to a hash. - * - * @param mixed $value - * - * @return mixed - */ - public function toHash($value) - { - return $this->internalFieldType->toHash($value); - } - - /** - * Converts the given $fieldSettings to a simple hash format. - * - * @param mixed $fieldSettings - * - * @return array|hash|scalar|null - */ - public function fieldSettingsToHash($fieldSettings) - { - return $this->internalFieldType->fieldSettingsToHash($fieldSettings); - } - - /** - * Converts the given $fieldSettingsHash to field settings of the type. - * - * This is the reverse operation of {@link fieldSettingsToHash()}. - * - * @param array|hash|scalar|null $fieldSettingsHash - * - * @return mixed - */ - public function fieldSettingsFromHash($fieldSettingsHash) - { - return $this->internalFieldType->fieldSettingsFromHash($fieldSettingsHash); - } - - /** - * Converts the given $validatorConfiguration to a simple hash format. - * - * @param mixed $validatorConfiguration - * - * @return array|hash|scalar|null - */ - public function validatorConfigurationToHash($validatorConfiguration) - { - return $this->internalFieldType->validatorConfigurationToHash($validatorConfiguration); - } - - /** - * Converts the given $validatorConfigurationHash to a validator - * configuration of the type. - * - * @param array|hash|scalar|null $validatorConfigurationHash - * - * @return mixed - */ - public function validatorConfigurationFromHash($validatorConfigurationHash) - { - return $this->internalFieldType->validatorConfigurationFromHash($validatorConfigurationHash); - } - - /** - * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * This methods determines if the given $validatorConfiguration is - * structurally correct and complies to the validator configuration schema as defined in FieldType. - * - * @param mixed $validatorConfiguration - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateValidatorConfiguration($validatorConfiguration): iterable - { - return $this->internalFieldType->validateValidatorConfiguration($validatorConfiguration); - } - - /** - * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * This methods determines if the given $fieldSettings are structurally - * correct and comply to the settings schema as defined in FieldType. - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateFieldSettings($fieldSettings): iterable - { - return $this->internalFieldType->validateFieldSettings($fieldSettings); - } - - /** - * Validates a field value based on the validator configuration in the field definition. - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDef The field definition of the field - * @param \eZ\Publish\SPI\FieldType\Value $value The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validateValue(APIFieldDefinition $fieldDef, Value $value): iterable - { - return $this->internalFieldType->validate($fieldDef, $value); - } -} diff --git a/eZ/Publish/Core/Repository/Values/ObjectState/ObjectState.php b/eZ/Publish/Core/Repository/Values/ObjectState/ObjectState.php deleted file mode 100644 index 118778e19b..0000000000 --- a/eZ/Publish/Core/Repository/Values/ObjectState/ObjectState.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\ObjectState; - -use eZ\Publish\API\Repository\Values\ObjectState\ObjectState as APIObjectState; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup as APIObjectStateGroup; -use eZ\Publish\Core\Repository\Values\MultiLanguageDescriptionTrait; -use eZ\Publish\Core\Repository\Values\MultiLanguageNameTrait; -use eZ\Publish\Core\Repository\Values\MultiLanguageTrait; - -/** - * This class represents a object state value. - * - * @property-read mixed $id the id of the content type group - * @property-read string $identifier the identifier of the content type group - * @property-read int $priority the priority in the group ordering - * @property-read string $mainLanguageCode the default language of the object state names and descriptions used for fallback. - * @property-read string $defaultLanguageCode deprecated, use $mainLanguageCode - * @property-read string[] $languageCodes the available languages - * @property-read string[] $prioritizedLanguages - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class ObjectState extends APIObjectState -{ - use MultiLanguageTrait; - use MultiLanguageNameTrait; - use MultiLanguageDescriptionTrait; - - /** @var \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup */ - protected $objectStateGroup; - - /** - * The object state group this object state belongs to. - * - * @return \eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup - */ - public function getObjectStateGroup(): APIObjectStateGroup - { - return $this->objectStateGroup; - } - - /** - * Magic getter for BC reasons. - * - * @param string $property - * - * @return mixed - */ - public function __get($property) - { - if ($property === 'defaultLanguageCode') { - @trigger_error( - __CLASS__ . '::$defaultLanguageCode is deprecated. Use mainLanguageCode', - E_USER_DEPRECATED - ); - - return $this->mainLanguageCode; - } - - return parent::__get($property); - } - - /** - * Magic isset for BC reasons. - * - * @param string $property - * - * @return bool - */ - public function __isset($property) - { - if ($property === 'defaultLanguageCode') { - @trigger_error( - __CLASS__ . '::$defaultLanguageCode is deprecated. Use mainLanguageCode' - ); - - return true; - } - - return parent::__isset($property); - } -} diff --git a/eZ/Publish/Core/Repository/Values/ObjectState/ObjectStateGroup.php b/eZ/Publish/Core/Repository/Values/ObjectState/ObjectStateGroup.php deleted file mode 100644 index ad07c2f5a4..0000000000 --- a/eZ/Publish/Core/Repository/Values/ObjectState/ObjectStateGroup.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Values\ObjectState; - -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup as APIObjectStateGroup; -use eZ\Publish\Core\Repository\Values\MultiLanguageDescriptionTrait; -use eZ\Publish\Core\Repository\Values\MultiLanguageNameTrait; -use eZ\Publish\Core\Repository\Values\MultiLanguageTrait; - -/** - * This class represents an object state group value. - * - * @property-read mixed $id the id of the content type group - * @property-read string $identifier the identifier of the content type group - * @property-read string $mainLanguageCode the default language of the object state group names and description used for fallback. - * @property-read string $defaultLanguageCode deprecated, use $mainLanguageCode - * @property-read string[] $languageCodes the available languages - * @property-read string[] $prioritizedLanguages - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class ObjectStateGroup extends APIObjectStateGroup -{ - use MultiLanguageTrait; - use MultiLanguageNameTrait; - use MultiLanguageDescriptionTrait; - - /** - * Magic getter for BC reasons. - * - * @param string $property - * - * @return mixed - */ - public function __get($property) - { - if ($property === 'defaultLanguageCode') { - @trigger_error( - __CLASS__ . '::$defaultLanguageCode is deprecated. Use mainLanguageCode', - E_USER_DEPRECATED - ); - - return $this->mainLanguageCode; - } - - return parent::__get($property); - } - - public function __isset($property) - { - if ($property === 'defaultLanguageCode') { - @trigger_error( - __CLASS__ . '::$defaultLanguageCode is deprecated. Use mainLanguageCode', - E_USER_DEPRECATED - ); - - return true; - } - - return parent::__isset($property); - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/Policy.php b/eZ/Publish/Core/Repository/Values/User/Policy.php deleted file mode 100644 index 728c022834..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/Policy.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\User\Policy as APIPolicy; - -/** - * This class represents a policy value. - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class Policy extends APIPolicy -{ - /** - * Limitations assigned to this policy. - * - * @var \eZ\Publish\API\Repository\Values\User\Limitation[] - */ - protected $limitations = []; - - /** - * Returns the list of limitations for this policy. - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation[] - */ - public function getLimitations(): iterable - { - return $this->limitations; - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/PolicyCreateStruct.php b/eZ/Publish/Core/Repository/Values/User/PolicyCreateStruct.php deleted file mode 100644 index 31d9bda111..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/PolicyCreateStruct.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\PolicyCreateStruct as APIPolicyCreateStruct; - -/** - * This class is used to create a policy. - * - * @internal Meant for internal use by Repository, type hint against API instead. - */ -class PolicyCreateStruct extends APIPolicyCreateStruct -{ - /** - * List of limitations added to policy. - * - * @var \eZ\Publish\API\Repository\Values\User\Limitation[] - */ - protected $limitations = []; - - /** - * Returns list of limitations added to policy. - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation[] - */ - public function getLimitations(): iterable - { - return $this->limitations; - } - - /** - * Adds a limitation with the given identifier and list of values. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - */ - public function addLimitation(Limitation $limitation): void - { - $limitationIdentifier = $limitation->getIdentifier(); - $this->limitations[$limitationIdentifier] = $limitation; - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/PolicyDraft.php b/eZ/Publish/Core/Repository/Values/User/PolicyDraft.php deleted file mode 100644 index c33270afc7..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/PolicyDraft.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\User\PolicyDraft as APIPolicyDraft; - -/** - * Class PolicyDraft. - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class PolicyDraft extends APIPolicyDraft -{ - /** @var \eZ\Publish\API\Repository\Values\User\Policy */ - protected $innerPolicy; - - /** - * Set of properties that are specific to PolicyDraft. - * - * @var array - */ - private $draftProperties = ['originalId' => true]; - - public function __get($property) - { - if (isset($this->draftProperties[$property])) { - return parent::__get($property); - } - - return $this->innerPolicy->$property; - } - - public function __set($property, $propertyValue) - { - if (isset($this->draftProperties[$property])) { - parent::__set($property, $propertyValue); - } - - $this->innerPolicy->$property = $propertyValue; - } - - public function __isset($property) - { - if (isset($this->draftProperties[$property])) { - return parent::__isset($property); - } - - return $this->innerPolicy->__isset($property); - } - - /** - * @return \eZ\Publish\API\Repository\Values\User\Limitation[] - */ - public function getLimitations(): iterable - { - return $this->innerPolicy->getLimitations(); - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/PolicyUpdateStruct.php b/eZ/Publish/Core/Repository/Values/User/PolicyUpdateStruct.php deleted file mode 100644 index 6c96fdb572..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/PolicyUpdateStruct.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct as APIPolicyUpdateStruct; - -/** - * This class is used for updating a policy. The limitations of the policy are replaced - * with those which are added in instances of this class. - * - * @internal Meant for internal use by Repository, type hint against API instead. - */ -class PolicyUpdateStruct extends APIPolicyUpdateStruct -{ - /** - * List of limitations added to policy. - * - * @var \eZ\Publish\API\Repository\Values\User\Limitation[] - */ - protected $limitations = []; - - /** - * Returns list of limitations added to policy. - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation[] - */ - public function getLimitations(): iterable - { - return $this->limitations; - } - - /** - * Adds a limitation to the policy - if a Limitation exists with the same identifier - * the existing limitation is replaced. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - */ - public function addLimitation(Limitation $limitation): void - { - $limitationIdentifier = $limitation->getIdentifier(); - $this->limitations[$limitationIdentifier] = $limitation; - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/Role.php b/eZ/Publish/Core/Repository/Values/User/Role.php deleted file mode 100644 index 349c561d3d..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/Role.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\User\Role as APIRole; - -/** - * This class represents a role. - * - * @property-read \eZ\Publish\API\Repository\Values\User\Policy[] $policies Policies assigned to this role - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class Role extends APIRole -{ - /** - * Policies assigned to this role. - * - * @var \eZ\Publish\API\Repository\Values\User\Policy[] - */ - protected $policies = []; - - /** - * Returns the list of policies of this role. - * - * @return \eZ\Publish\API\Repository\Values\User\Policy[] - */ - public function getPolicies(): iterable - { - return $this->policies; - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/RoleCopyStruct.php b/eZ/Publish/Core/Repository/Values/User/RoleCopyStruct.php deleted file mode 100644 index fdee6c6a52..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/RoleCopyStruct.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\User\PolicyCreateStruct as APIPolicyCreateStruct; -use eZ\Publish\API\Repository\Values\User\RoleCopyStruct as APIRoleCopyStruct; - -/** - * This class is used to create a new role. - * - * @internal Meant for internal use by Repository, type hint against API instead. - */ -class RoleCopyStruct extends APIRoleCopyStruct -{ - /** - * Policies associated with the role. - * - * @var \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct[] - */ - protected $policies = []; - - /** - * Returns policies associated with the role. - * - * @return \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct[] - */ - public function getPolicies(): iterable - { - return $this->policies; - } - - /** - * Adds a policy to this role. - * - * @param \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct $policyCreateStruct - */ - public function addPolicy(APIPolicyCreateStruct $policyCreateStruct): void - { - $this->policies[] = $policyCreateStruct; - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/RoleCreateStruct.php b/eZ/Publish/Core/Repository/Values/User/RoleCreateStruct.php deleted file mode 100644 index 324abec3b8..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/RoleCreateStruct.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\User\PolicyCreateStruct as APIPolicyCreateStruct; -use eZ\Publish\API\Repository\Values\User\RoleCreateStruct as APIRoleCreateStruct; - -/** - * This class is used to create a new role. - * - * @internal Meant for internal use by Repository, type hint against API instead. - */ -class RoleCreateStruct extends APIRoleCreateStruct -{ - /** - * Policies associated with the role. - * - * @var \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct[] - */ - protected $policies = []; - - /** - * Returns policies associated with the role. - * - * @return \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct[] - */ - public function getPolicies(): iterable - { - return $this->policies; - } - - /** - * Adds a policy to this role. - * - * @param \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct $policyCreateStruct - */ - public function addPolicy(APIPolicyCreateStruct $policyCreateStruct): void - { - $this->policies[] = $policyCreateStruct; - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/RoleDraft.php b/eZ/Publish/Core/Repository/Values/User/RoleDraft.php deleted file mode 100644 index 5cde79d5ec..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/RoleDraft.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\User\RoleDraft as APIRoleDraft; - -/** - * This class represents a draft of a role. - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class RoleDraft extends APIRoleDraft -{ - /** - * Holds internal role object. - * - * @var \eZ\Publish\API\Repository\Values\User\Role - * - * @todo document - */ - protected $innerRole; - - /** - * Magic getter for routing get calls to innerRole. - * - * @param string $property The name of the property to retrieve - * - * @return mixed - */ - public function __get($property) - { - return $this->innerRole->$property; - } - - /** - * Magic set for routing set calls to innerRole. - * - * @param string $property - * @param mixed $propertyValue - */ - public function __set($property, $propertyValue) - { - $this->innerRole->$property = $propertyValue; - } - - /** - * Magic isset for routing isset calls to innerRole. - * - * @param string $property - * - * @return bool - */ - public function __isset($property) - { - return $this->innerRole->__isset($property); - } - - /** - * Returns the list of policies of this role. - * - * @return \eZ\Publish\API\Repository\Values\User\PolicyDraft[] - */ - public function getPolicies(): iterable - { - return $this->innerRole->getPolicies(); - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/User.php b/eZ/Publish/Core/Repository/Values/User/User.php deleted file mode 100644 index 1c027f4a8a..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/User.php +++ /dev/null @@ -1,196 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\SPI\FieldType\Value; - -/** - * This class represents a user value. - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class User extends APIUser -{ - /** - * Internal content representation. - * - * @var \eZ\Publish\API\Repository\Values\Content\Content - */ - protected $content; - - /** - * Returns the VersionInfo for this version. - * - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo - */ - public function getVersionInfo(): APIVersionInfo - { - return $this->content->getVersionInfo(); - } - - public function getContentType(): ContentType - { - return $this->content->getContentType(); - } - - /** - * Returns a field value for the given value - * $version->fields[$fieldDefId][$languageCode] is an equivalent call - * if no language is given on a translatable field this method returns - * the value of the initial language of the version if present, otherwise null. - * On non translatable fields this method ignores the languageCode parameter. - * - * @param string $fieldDefIdentifier - * @param string $languageCode - * - * @return mixed a primitive type or a field type Value object depending on the field type. - */ - public function getFieldValue(string $fieldDefIdentifier, ?string $languageCode = null): ?Value - { - return $this->content->getFieldValue($fieldDefIdentifier, $languageCode); - } - - /** - * This method returns the complete fields collection. - * - * @return \eZ\Publish\API\Repository\Values\Content\Field[] - */ - public function getFields(): iterable - { - return $this->content->getFields(); - } - - /** - * This method returns the fields for a given language and non translatable fields. - * - * If note set the initialLanguage of the content version is used. - * - * @param string $languageCode - * - * @return \eZ\Publish\API\Repository\Values\Content\Field[] with field identifier as keys - */ - public function getFieldsByLanguage(?string $languageCode = null): iterable - { - return $this->content->getFieldsByLanguage($languageCode); - } - - /** - * This method returns the field for a given field definition identifier and language. - * - * If not set the initialLanguage of the content version is used. - * - * @param string $fieldDefIdentifier - * @param string|null $languageCode - * - * @return \eZ\Publish\API\Repository\Values\Content\Field|null A {@link Field} or null if nothing is found - */ - public function getField(string $fieldDefIdentifier, ?string $languageCode = null): ?Field - { - return $this->content->getField($fieldDefIdentifier, $languageCode); - } - - /** - * Function where list of properties are returned. - * - * Override to add dynamic properties - * - * @uses \parent::getProperties() - * - * @param array $dynamicProperties - * - * @return array - */ - protected function getProperties($dynamicProperties = ['id', 'contentInfo', 'versionInfo', 'fields']) - { - return parent::getProperties($dynamicProperties); - } - - /** - * Magic getter for retrieving convenience properties. - * - * @param string $property The name of the property to retrieve - * - * @return mixed - */ - public function __get($property) - { - switch ($property) { - case 'contentInfo': - return $this->getVersionInfo()->getContentInfo(); - - case 'id': - return $this->getVersionInfo()->getContentInfo()->id; - - case 'versionInfo': - return $this->getVersionInfo(); - - case 'fields': - return $this->getFields(); - - case 'thumbnail': - return $this->getThumbnail(); - - case 'content': - // trigger error for this, but for BC let it pass on to normal __get lookup for now - @trigger_error( - sprintf('%s is an internal property, usage is deprecated as of 6.10. User itself exposes everything needed.', $property), - E_USER_DEPRECATED - ); - } - - return parent::__get($property); - } - - /** - * Magic isset for signaling existence of convenience properties. - * - * @param string $property - * - * @return bool - */ - public function __isset($property) - { - if ($property === 'contentInfo') { - return true; - } - - if ($property === 'id') { - return true; - } - - if ($property === 'versionInfo') { - return true; - } - - if ($property === 'thumbnail') { - return true; - } - - if ($property === 'fields') { - return true; - } - - return parent::__isset($property); - } - - public function getThumbnail(): ?Thumbnail - { - return $this->content->getThumbnail(); - } - - public function getDefaultLanguageCode(): string - { - return $this->content->getDefaultLanguageCode(); - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/UserCreateStruct.php b/eZ/Publish/Core/Repository/Values/User/UserCreateStruct.php deleted file mode 100644 index 412e5f3919..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/UserCreateStruct.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\User\UserCreateStruct as APIUserCreateStruct; - -/** - * This class is used to create a new user in the repository. - * - * @internal Meant for internal use by Repository, type hint against API instead. - */ -class UserCreateStruct extends APIUserCreateStruct -{ - /** - * The list of fields added to the user. - * - * @var \eZ\Publish\API\Repository\Values\Content\Field[] - */ - public $fields = []; - - /** - * Adds a field to the field collection. - * - * This method could also be implemented by a magic setter so that - * $fields[$fieldDefIdentifier][$language] = $value or without language $fields[$fieldDefIdentifier] = $value - * is an equivalent call. - * - * @param string $fieldDefIdentifier the identifier of the field definition - * @param mixed $value Either a plain value which is understandable by the corresponding - * field type or an instance of a Value class provided by the field type - * @param string|null $language If not given on a translatable field the initial language is used - */ - public function setField(string $fieldDefIdentifier, $value, ?string $language = null): void - { - if (!isset($language)) { - $language = $this->mainLanguageCode; - } - - $this->fields[] = new Field( - [ - 'fieldDefIdentifier' => $fieldDefIdentifier, - 'value' => $value, - 'languageCode' => $language, - ] - ); - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/UserGroup.php b/eZ/Publish/Core/Repository/Values/User/UserGroup.php deleted file mode 100644 index 067d6edc3e..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/UserGroup.php +++ /dev/null @@ -1,195 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\User\UserGroup as APIUserGroup; -use eZ\Publish\SPI\FieldType\Value; - -/** - * This class represents a user group. - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class UserGroup extends APIUserGroup -{ - /** - * Internal content representation. - * - * @var \eZ\Publish\API\Repository\Values\Content\Content - */ - protected $content; - - /** - * Returns the VersionInfo for this version. - * - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo - */ - public function getVersionInfo(): \eZ\Publish\API\Repository\Values\Content\VersionInfo - { - return $this->content->getVersionInfo(); - } - - public function getContentType(): ContentType - { - return $this->content->getContentType(); - } - - /** - * Returns a field value for the given value - * $version->fields[$fieldDefId][$languageCode] is an equivalent call - * if no language is given on a translatable field this method returns - * the value of the initial language of the version if present, otherwise null. - * On non translatable fields this method ignores the languageCode parameter. - * - * @param string $fieldDefIdentifier - * @param string|null $languageCode - * - * @return mixed a primitive type or a field type Value object depending on the field type. - */ - public function getFieldValue(string $fieldDefIdentifier, ?string $languageCode = null): ?Value - { - return $this->content->getFieldValue($fieldDefIdentifier, $languageCode); - } - - /** - * This method returns the complete fields collection. - * - * @return \eZ\Publish\API\Repository\Values\Content\Field[] - */ - public function getFields(): iterable - { - return $this->content->getFields(); - } - - /** - * This method returns the fields for a given language and non translatable fields. - * - * If note set the initialLanguage of the content version is used. - * - * @param string $languageCode - * - * @return \eZ\Publish\API\Repository\Values\Content\Field[] with field identifier as keys - */ - public function getFieldsByLanguage(?string $languageCode = null): iterable - { - return $this->content->getFieldsByLanguage($languageCode); - } - - /** - * This method returns the field for a given field definition identifier and language. - * - * If not set the initialLanguage of the content version is used. - * - * @param string $fieldDefIdentifier - * @param string|null $languageCode - * - * @return \eZ\Publish\API\Repository\Values\Content\Field|null A {@link Field} or null if nothing is found - */ - public function getField(string $fieldDefIdentifier, ?string $languageCode = null): ?Field - { - return $this->content->getField($fieldDefIdentifier, $languageCode); - } - - /** - * Function where list of properties are returned. - * - * Override to add dynamic properties - * - * @uses \parent::getProperties() - * - * @param array $dynamicProperties - * - * @return array - */ - protected function getProperties($dynamicProperties = ['id', 'contentInfo', 'versionInfo', 'fields']) - { - return parent::getProperties($dynamicProperties); - } - - /** - * Magic getter for retrieving convenience properties. - * - * @param string $property The name of the property to retrieve - * - * @return mixed - */ - public function __get($property) - { - switch ($property) { - case 'contentInfo': - return $this->getVersionInfo()->getContentInfo(); - - case 'id': - return $this->getVersionInfo()->getContentInfo()->id; - - case 'versionInfo': - return $this->getVersionInfo(); - - case 'fields': - return $this->getFields(); - - case 'thumbnail': - return $this->getThumbnail(); - - case 'content': - // trigger error for this, but for BC let it pass on to normal __get lookup for now - @trigger_error( - sprintf('%s is an internal property, usage is deprecated as of 6.10. User Group itself exposes everything needed.', $property), - E_USER_DEPRECATED - ); - } - - return parent::__get($property); - } - - /** - * Magic isset for signaling existence of convenience properties. - * - * @param string $property - * - * @return bool - */ - public function __isset($property) - { - if ($property === 'contentInfo') { - return true; - } - - if ($property === 'id') { - return true; - } - - if ($property === 'versionInfo') { - return true; - } - - if ($property === 'fields') { - return true; - } - - if ($property === 'thumbnail') { - return true; - } - - return parent::__isset($property); - } - - public function getThumbnail(): ?Thumbnail - { - return $this->content->getThumbnail(); - } - - public function getDefaultLanguageCode(): string - { - return $this->content->getDefaultLanguageCode(); - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/UserGroupCreateStruct.php b/eZ/Publish/Core/Repository/Values/User/UserGroupCreateStruct.php deleted file mode 100644 index 036b3d97c8..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/UserGroupCreateStruct.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct as APIUserGroupCreateStruct; - -/** - * This class is used to create a new user group in the repository. - * - * @internal Meant for internal use by Repository, type hint against API instead. - */ -class UserGroupCreateStruct extends APIUserGroupCreateStruct -{ - /** - * The list of fields added to the user group. - * - * @var \eZ\Publish\API\Repository\Values\Content\Field[] - */ - public $fields = []; - - /** - * Adds a field to the field collection. - * - * This method could also be implemented by a magic setter so that - * $fields[$fieldDefIdentifier][$language] = $value or without language $fields[$fieldDefIdentifier] = $value - * is an equivalent call. - * - * @param string $fieldDefIdentifier the identifier of the field definition - * @param mixed $value Either a plain value which is understandable by the corresponding - * field type or an instance of a Value class provided by the field type - * @param string|null $language If not given on a translatable field the initial language is used - */ - public function setField(string $fieldDefIdentifier, $value, ?string $language = null): void - { - if (!isset($language)) { - $language = $this->mainLanguageCode; - } - - $this->fields[] = new Field( - [ - 'fieldDefIdentifier' => $fieldDefIdentifier, - 'value' => $value, - 'languageCode' => $language, - ] - ); - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/UserGroupRoleAssignment.php b/eZ/Publish/Core/Repository/Values/User/UserGroupRoleAssignment.php deleted file mode 100644 index 9490bb6069..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/UserGroupRoleAssignment.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation as APIRoleLimitation; -use eZ\Publish\API\Repository\Values\User\Role as APIRole; -use eZ\Publish\API\Repository\Values\User\UserGroup as APIUserGroup; -use eZ\Publish\API\Repository\Values\User\UserGroupRoleAssignment as APIUserGroupRoleAssignment; - -/** - * This class represents a user group to role assignment. - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class UserGroupRoleAssignment extends APIUserGroupRoleAssignment -{ - /** - * the limitation of this role assignment. - * - * @var \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation|null - */ - protected $limitation; - - /** - * the role which is assigned to the user group. - * - * @var \eZ\Publish\API\Repository\Values\User\Role - */ - protected $role; - - /** - * user group to which the role is assigned to. - * - * @var \eZ\Publish\API\Repository\Values\User\UserGroup - */ - protected $userGroup; - - /** - * Returns the limitation of the role assignment. - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation|null - */ - public function getRoleLimitation(): ?APIRoleLimitation - { - return $this->limitation; - } - - /** - * Returns the role to which the user group is assigned to. - * - * @return \eZ\Publish\API\Repository\Values\User\Role - */ - public function getRole(): APIRole - { - return $this->role; - } - - /** - * Returns the user group to which the role is assigned to. - * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup - */ - public function getUserGroup(): APIUserGroup - { - return $this->userGroup; - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/UserReference.php b/eZ/Publish/Core/Repository/Values/User/UserReference.php deleted file mode 100644 index fc4b27370e..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/UserReference.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; - -/** - * This class represents a user reference for use in sessions and Repository. - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class UserReference implements APIUserReference -{ - /** @var int */ - private $userId; - - public function __construct(int $userId) - { - $this->userId = $userId; - } - - /** - * The User id of the User this reference represent. - * - * @return int - */ - public function getUserId(): int - { - return $this->userId; - } -} diff --git a/eZ/Publish/Core/Repository/Values/User/UserRoleAssignment.php b/eZ/Publish/Core/Repository/Values/User/UserRoleAssignment.php deleted file mode 100644 index a929f3b986..0000000000 --- a/eZ/Publish/Core/Repository/Values/User/UserRoleAssignment.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Repository\Values\User; - -use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation as APIRoleLimitation; -use eZ\Publish\API\Repository\Values\User\Role as APIRole; -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\API\Repository\Values\User\UserRoleAssignment as APIUserRoleAssignment; - -/** - * This class represents a user to role assignment. - * - * @internal Meant for internal use by Repository, type hint against API object instead. - */ -class UserRoleAssignment extends APIUserRoleAssignment -{ - /** - * the limitation of this role assignment. - * - * @var \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation|null - */ - protected $limitation; - - /** - * the role which is assigned to the user. - * - * @var \eZ\Publish\API\Repository\Values\User\Role - */ - protected $role; - - /** - * user to which the role is assigned to. - * - * @var \eZ\Publish\API\Repository\Values\User\User - */ - protected $user; - - /** - * Returns the limitation of the user role assignment. - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation|null - */ - public function getRoleLimitation(): ?APIRoleLimitation - { - return $this->limitation; - } - - /** - * Returns the role to which the user is assigned to. - * - * @return \eZ\Publish\API\Repository\Values\User\Role - */ - public function getRole(): APIRole - { - return $this->role; - } - - /** - * Returns the user to which the role is assigned to. - * - * @return \eZ\Publish\API\Repository\Values\User\User - */ - public function getUser(): APIUser - { - return $this->user; - } -} diff --git a/eZ/Publish/Core/Search/Common/BackgroundIndexer/NullIndexer.php b/eZ/Publish/Core/Search/Common/BackgroundIndexer/NullIndexer.php deleted file mode 100644 index 301378d950..0000000000 --- a/eZ/Publish/Core/Search/Common/BackgroundIndexer/NullIndexer.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\BackgroundIndexer; - -use eZ\Publish\Core\Search\Common\BackgroundIndexer as BackgroundIndexerInterface; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\Location; - -/** - * Null indexer, does nothing, for default use when non has been configured. - */ -class NullIndexer implements BackgroundIndexerInterface -{ - public function registerContent(ContentInfo $contentInfo) - { - } - - public function registerLocation(Location $location) - { - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldRegistry.php b/eZ/Publish/Core/Search/Common/FieldRegistry.php deleted file mode 100644 index cdd8124183..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldRegistry.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common; - -use eZ\Publish\SPI\FieldType\Indexable; -use OutOfBoundsException; - -/** - * Registry for field type's Indexable interface implementations available to Search Engines. - */ -class FieldRegistry -{ - private const INDEXABLE_FIELD_TYPE_TAG = 'ezpublish.fieldType.indexable'; - - /** @var \eZ\Publish\SPI\FieldType\Indexable[] */ - protected $types = []; - - /** - * @param \eZ\Publish\SPI\FieldType\Indexable[] $types - */ - public function __construct(array $types = []) - { - foreach ($types as $name => $type) { - $this->registerType($name, $type); - } - } - - public function registerType(string $name, Indexable $type): void - { - $this->types[$name] = $type; - } - - public function getType(string $name): Indexable - { - if (!isset($this->types[$name])) { - throw new OutOfBoundsException( - sprintf( - 'Field Type "%s" is not indexable. Provide %s implementation and register it with the "%s" tag.', - $name, - Indexable::class, - self::INDEXABLE_FIELD_TYPE_TAG - ) - ); - } - - return $this->types[$name]; - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper.php b/eZ/Publish/Core/Search/Common/FieldValueMapper.php deleted file mode 100644 index d659822065..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common; - -use eZ\Publish\SPI\Search\Field; - -/** - * Maps raw field values to something search engine can understand. - * This is used when indexing Content and matching Content fields. - * Actual format of the returned value depends on the search engine - * implementation, meaning engines should override common implementation - * as needed, but the same input should be handled across engines. - * - * @see \eZ\Publish\SPI\Search\FieldType - */ -abstract class FieldValueMapper -{ - /** - * Check if field can be mapped. - * - * @param \eZ\Publish\SPI\Search\Field $field - * - * @return bool - */ - abstract public function canMap(Field $field); - - /** - * Map field value to a proper search engine representation. - * - * @param \eZ\Publish\SPI\Search\Field $field - * - * @return mixed|null Returns null on empty value - */ - abstract public function map(Field $field); -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/Aggregate.php b/eZ/Publish/Core/Search/Common/FieldValueMapper/Aggregate.php deleted file mode 100644 index d0c2d251af..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/Aggregate.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; - -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\Core\Search\Common\FieldValueMapper; -use eZ\Publish\SPI\Search\Field; - -/** - * Common aggregate mapper implementation. - */ -class Aggregate extends FieldValueMapper -{ - /** - * Array of available mappers. - * - * @var \eZ\Publish\Core\Search\Common\FieldValueMapper[] - */ - protected $mappers = []; - - /** - * Array of simple mappers mapping specific Field (by its FQCN). - * - * @var array<string, \eZ\Publish\Core\Search\Common\FieldValueMapper> - */ - protected $simpleMappers = []; - - /** - * Construct from optional mapper array. - * - * @param \eZ\Publish\Core\Search\Common\FieldValueMapper[] $mappers - */ - public function __construct(array $mappers = []) - { - foreach ($mappers as $mapper) { - $this->addMapper($mapper); - } - } - - /** - * @param class-string<\eZ\Publish\SPI\Search\Field>|null $searchTypeFQCN - */ - public function addMapper(FieldValueMapper $mapper, ?string $searchTypeFQCN = null): void - { - if (null !== $searchTypeFQCN) { - $this->simpleMappers[$searchTypeFQCN] = $mapper; - } else { - $this->mappers[] = $mapper; - } - } - - public function canMap(Field $field): bool - { - return true; - } - - /** - * Map field value to a proper search engine representation. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - * - * @param \eZ\Publish\SPI\Search\Field $field - * - * @return mixed - */ - public function map(Field $field) - { - $mapper = $this->simpleMappers[get_class($field->getType())] - ?? $this->findMapper($field); - - return $mapper->map($field); - } - - /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - private function findMapper(Field $field): FieldValueMapper - { - foreach ($this->mappers as $mapper) { - if ($mapper->canMap($field)) { - return $mapper; - } - } - - throw new NotImplementedException( - 'No mapper available for: ' . get_class($field->getType()) - ); - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/BooleanMapper.php b/eZ/Publish/Core/Search/Common/FieldValueMapper/BooleanMapper.php deleted file mode 100644 index e6c022bec6..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/BooleanMapper.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; - -use eZ\Publish\Core\Search\Common\FieldValueMapper; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\BooleanField; - -/** - * Common boolean field value mapper implementation. - */ -class BooleanMapper extends FieldValueMapper -{ - public function canMap(Field $field): bool - { - return $field->getType() instanceof BooleanField; - } - - public function map(Field $field) - { - return (bool)$field->getValue(); - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/DocumentMapper.php b/eZ/Publish/Core/Search/Common/FieldValueMapper/DocumentMapper.php deleted file mode 100644 index 2c0e488459..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/DocumentMapper.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; - -use eZ\Publish\Core\Search\Common\FieldValueMapper; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\DocumentField; - -/** - * Common document field value mapper implementation. - */ -class DocumentMapper extends FieldValueMapper -{ - public function canMap(Field $field): bool - { - return $field->getType() instanceof DocumentField; - } - - public function map(Field $field) - { - return $field->getValue(); - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/FloatMapper.php b/eZ/Publish/Core/Search/Common/FieldValueMapper/FloatMapper.php deleted file mode 100644 index 45a6e22d8b..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/FloatMapper.php +++ /dev/null @@ -1,27 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; - -use eZ\Publish\Core\Search\Common\FieldValueMapper; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\FloatField; - -/** - * Common float field value mapper implementation. - */ -class FloatMapper extends FieldValueMapper -{ - public function canMap(Field $field): bool - { - return $field->getType() instanceof FloatField; - } - - public function map(Field $field) - { - return sprintf('%F', (float)$field->getValue()); - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/GeoLocationMapper.php b/eZ/Publish/Core/Search/Common/FieldValueMapper/GeoLocationMapper.php deleted file mode 100644 index 0161746d20..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/GeoLocationMapper.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; - -use eZ\Publish\Core\Search\Common\FieldValueMapper; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\GeoLocationField; - -/** - * Common geo location field value mapper implementation. - */ -class GeoLocationMapper extends FieldValueMapper -{ - public function canMap(Field $field): bool - { - return $field->getType() instanceof GeoLocationField; - } - - public function map(Field $field) - { - $value = $field->getValue(); - if ($value['latitude'] === null || $value['longitude'] === null) { - return null; - } - - return sprintf('%F,%F', $value['latitude'], $value['longitude']); - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/IdentifierMapper.php b/eZ/Publish/Core/Search/Common/FieldValueMapper/IdentifierMapper.php deleted file mode 100644 index 6db35a6dbb..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/IdentifierMapper.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; - -use eZ\Publish\Core\Search\Common\FieldValueMapper; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\IdentifierField; - -/** - * Common identifier field value mapper implementation. - */ -class IdentifierMapper extends FieldValueMapper -{ - public function canMap(Field $field): bool - { - return $field->getType() instanceof IdentifierField; - } - - /** - * Map field value to a proper search engine representation. - * - * @param \eZ\Publish\SPI\Search\Field $field - * - * @return mixed - */ - public function map(Field $field) - { - if ($field->getType()->raw) { - return $field->getValue(); - } - - return $this->convert($field->getValue()); - } - - /** - * Convert to a proper search engine representation. - * - * @param mixed $value - */ - protected function convert($value): string - { - // Remove non-printable characters - return preg_replace('([^A-Za-z0-9/]+)', '', $value); - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/IntegerMapper.php b/eZ/Publish/Core/Search/Common/FieldValueMapper/IntegerMapper.php deleted file mode 100644 index b06a9b5487..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/IntegerMapper.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; - -use eZ\Publish\Core\Search\Common\FieldValueMapper; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\IntegerField; - -/** - * Common integer field value mapper implementation. - */ -class IntegerMapper extends FieldValueMapper -{ - public function canMap(Field $field): bool - { - return $field->getType() instanceof IntegerField; - } - - public function map(Field $field) - { - return $this->convert($field->getValue()); - } - - /** - * Convert to a proper search engine representation. - * - * @param mixed $value - */ - protected function convert($value): int - { - return (int)$value; - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleBooleanMapper.php b/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleBooleanMapper.php deleted file mode 100644 index 628d9a9f78..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleBooleanMapper.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; - -use eZ\Publish\Core\Search\Common\FieldValueMapper; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\MultipleBooleanField; - -/** - * Common multiple boolean field value mapper implementation. - */ -class MultipleBooleanMapper extends FieldValueMapper -{ - public function canMap(Field $field): bool - { - return $field->getType() instanceof MultipleBooleanField; - } - - public function map(Field $field) - { - $values = []; - - foreach ((array)$field->getValue() as $value) { - $values[] = (bool)$value; - } - - return $values; - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleIdentifierMapper.php b/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleIdentifierMapper.php deleted file mode 100644 index 1db733109d..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleIdentifierMapper.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; - -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\MultipleIdentifierField; - -/** - * Common multiple identifier field value mapper implementation. - */ -class MultipleIdentifierMapper extends IdentifierMapper -{ - public function canMap(Field $field): bool - { - return $field->getType() instanceof MultipleIdentifierField; - } - - /** - * Map field value to a proper search engine representation. - * - * @param \eZ\Publish\SPI\Search\Field $field - * - * @return mixed - */ - public function map(Field $field) - { - $values = []; - - $isRaw = $field->getType()->raw; - foreach ($field->getValue() as $value) { - if (!$isRaw) { - $value = $this->convert($value); - } - - $values[] = $value; - } - - return $values; - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleIntegerMapper.php b/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleIntegerMapper.php deleted file mode 100644 index 5c43b7bb84..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleIntegerMapper.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; - -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\MultipleIntegerField; - -/** - * Common multiple integer field value mapper implementation. - */ -class MultipleIntegerMapper extends IntegerMapper -{ - public function canMap(Field $field): bool - { - return $field->getType() instanceof MultipleIntegerField; - } - - public function map(Field $field) - { - $values = []; - - foreach ((array)$field->getValue() as $value) { - $values[] = $this->convert($value); - } - - return $values; - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleRemoteIdentifierMapper.php b/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleRemoteIdentifierMapper.php deleted file mode 100644 index 9b11ba824e..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleRemoteIdentifierMapper.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; - -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\MultipleRemoteIdentifierField; - -/** - * Common remote ID list field value mapper implementation. - */ -final class MultipleRemoteIdentifierMapper extends RemoteIdentifierMapper -{ - public function canMap(Field $field): bool - { - return $field->getType() instanceof MultipleRemoteIdentifierField; - } - - public function map(Field $field) - { - $values = []; - - foreach ($field->getValue() as $value) { - $values[] = $this->convert($value); - } - - return $values; - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/PriceMapper.php b/eZ/Publish/Core/Search/Common/FieldValueMapper/PriceMapper.php deleted file mode 100644 index b3b5394875..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/PriceMapper.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; - -use eZ\Publish\Core\Search\Common\FieldValueMapper; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\PriceField; - -/** - * Common price field value mapper implementation. - */ -class PriceMapper extends FieldValueMapper -{ - public function canMap(Field $field): bool - { - return $field->getType() instanceof PriceField; - } - - /** - * Map field value to a proper search engine representation. - * - * @param \eZ\Publish\SPI\Search\Field $field - * - * @return mixed - */ - public function map(Field $field) - { - return (float)$field->getValue(); - } -} diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/RemoteIdentifierMapper.php b/eZ/Publish/Core/Search/Common/FieldValueMapper/RemoteIdentifierMapper.php deleted file mode 100644 index 7942d416fe..0000000000 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/RemoteIdentifierMapper.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; - -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\RemoteIdentifierField; - -/** - * Common remote ID field value mapper. - * - * Currently behaves in the same way as StringMapper. - * - * @internal for internal use by Search engine field value mapper - */ -class RemoteIdentifierMapper extends StringMapper -{ - public function canMap(Field $field): bool - { - return $field->getType() instanceof RemoteIdentifierField; - } -} diff --git a/eZ/Publish/Core/Search/Common/Indexer.php b/eZ/Publish/Core/Search/Common/Indexer.php deleted file mode 100644 index 1d3398b25c..0000000000 --- a/eZ/Publish/Core/Search/Common/Indexer.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Common; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Driver\Statement; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Handler as PersistenceHandler; -use eZ\Publish\SPI\Search\Handler as SearchHandler; -use Psr\Log\LoggerInterface; -use Symfony\Component\Console\Output\OutputInterface; - -/** - * Base class for the Search Engine Indexer Service aimed to recreate Search Engine Index. - * Each Search Engine has to extend it on its own. - */ -abstract class Indexer -{ - /** @var \Psr\Log\LoggerInterface */ - protected $logger; - - /** @var \eZ\Publish\SPI\Persistence\Handler */ - protected $persistenceHandler; - - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - /** @var \eZ\Publish\SPI\Search\Handler */ - protected $searchHandler; - - public function __construct( - LoggerInterface $logger, - PersistenceHandler $persistenceHandler, - Connection $connection, - SearchHandler $searchHandler - ) { - $this->logger = $logger; - $this->persistenceHandler = $persistenceHandler; - $this->connection = $connection; - $this->searchHandler = $searchHandler; - } - - /** - * Create search engine index. - * - * @param \Symfony\Component\Console\Output\OutputInterface $output - * @param int $iterationCount - * @param bool $commit commit changes after each iteration - */ - abstract public function createSearchIndex(OutputInterface $output, $iterationCount, $commit); - - /** - * Get DB Statement to fetch metadata about content objects to be indexed. - * - * @param array $fields fields to select - */ - protected function getContentDbFieldsStmt(array $fields): Statement - { - $query = $this->connection->createQueryBuilder(); - $query - ->select($fields) - ->from(ContentGateway::CONTENT_ITEM_TABLE) - ->where($query->expr()->eq('status', ContentInfo::STATUS_PUBLISHED)); - - return $query->execute(); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriteriaConverter.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriteriaConverter.php deleted file mode 100644 index cd34330c6d..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriteriaConverter.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class CriteriaConverter -{ - /** - * Criterion handlers. - * - * @var \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler[] - */ - protected $handlers; - - /** - * Construct from an optional array of Criterion handlers. - * - * @param \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler[] $handlers - */ - public function __construct(array $handlers = []) - { - $this->handlers = $handlers; - } - - /** - * Adds handler. - * - * @param \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler $handler - */ - public function addHandler(CriterionHandler $handler) - { - $this->handlers[] = $handler; - } - - /** - * Generic converter of criteria into query fragments. - * - * @param array $languageSettings - * - * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|string - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function convertCriteria( - QueryBuilder $query, - Criterion $criterion, - array $languageSettings - ) { - foreach ($this->handlers as $handler) { - if ($handler->accept($criterion)) { - return $handler->handle($this, $query, $criterion, $languageSettings); - } - } - - throw new NotImplementedException( - 'No visitor available for: ' . get_class($criterion) . ' with operator ' . $criterion->operator - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler.php deleted file mode 100644 index 7dbf53381f..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; - -abstract class CriterionHandler -{ - /** - * Map of criterion operators to the respective function names in the zeta - * Database abstraction layer. - * - * @var array - */ - protected $comparatorMap = [ - Operator::EQ => 'eq', - Operator::GT => 'gt', - Operator::GTE => 'gte', - Operator::LT => 'lt', - Operator::LTE => 'lte', - Operator::LIKE => 'like', - ]; - - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform|null */ - protected $dbPlatform; - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct(Connection $connection) - { - $this->connection = $connection; - $this->dbPlatform = $connection->getDatabasePlatform(); - } - - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - abstract public function accept(Criterion $criterion); - - /** - * Generate query expression for a Criterion this handler accepts. - * - * accept() must be called before calling this method. - * - * @param array $languageSettings - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|string - */ - abstract public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ); - - protected function hasJoinedTableAs(QueryBuilder $queryBuilder, string $tableAlias): bool - { - // find table name in a structure: ['fromAlias' => [['joinTable' => '<table_name>'], ...]] - $joinedParts = $queryBuilder->getQueryPart('join'); - foreach ($joinedParts as $joinedTables) { - foreach ($joinedTables as $join) { - if ($join['joinAlias'] === $tableAlias) { - return true; - } - } - } - - return false; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/CompositeCriterion.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/CompositeCriterion.php deleted file mode 100644 index cf67b72ec6..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/CompositeCriterion.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -class CompositeCriterion extends CriterionHandler -{ - public function accept(Criterion $criterion): bool - { - return $criterion instanceof Criterion\CompositeCriterion; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - return $converter->convertCriteria($queryBuilder, $criterion->criteria, $languageSettings); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentId.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentId.php deleted file mode 100644 index f3c9043af4..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentId.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Content ID criterion handler. - */ -class ContentId extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\ContentId; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $value = (array)$criterion->value; - - return $queryBuilder->expr()->in( - 'c.id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeGroupId.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeGroupId.php deleted file mode 100644 index b461027fb7..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeGroupId.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Content type group criterion handler. - */ -class ContentTypeGroupId extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\ContentTypeGroupId; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $subSelect = $this->connection->createQueryBuilder(); - $subSelect - ->select( - 'contentclass_id' - )->from( - 'ezcontentclass_classgroup' - )->where( - $queryBuilder->expr()->in( - 'group_id', - $queryBuilder->createNamedParameter($criterion->value, Connection::PARAM_INT_ARRAY) - ) - ); - - return $queryBuilder->expr()->in( - 'c.contentclass_id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeId.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeId.php deleted file mode 100644 index d7013ffbba..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeId.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Content type criterion handler. - */ -class ContentTypeId extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\ContentTypeId; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - return $queryBuilder->expr()->in( - 'c.contentclass_id', - $queryBuilder->createNamedParameter($criterion->value, Connection::PARAM_INT_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeIdentifier.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeIdentifier.php deleted file mode 100644 index 5f27c03dfb..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeIdentifier.php +++ /dev/null @@ -1,95 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandler; -use Psr\Log\LoggerInterface; -use Psr\Log\NullLogger; - -/** - * Content type criterion handler. - */ -class ContentTypeIdentifier extends CriterionHandler -{ - /** - * Content Type handler. - * - * @var \eZ\Publish\SPI\Persistence\Content\Type\Handler - */ - protected $contentTypeHandler; - - /** - * @var \Psr\Log\LoggerInterface - */ - protected $logger; - - public function __construct( - Connection $connection, - ContentTypeHandler $contentTypeHandler, - LoggerInterface $logger = null - ) { - parent::__construct($connection); - - $this->contentTypeHandler = $contentTypeHandler; - $this->logger = $logger ?? new NullLogger(); - } - - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\ContentTypeIdentifier; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $idList = []; - $invalidIdentifiers = []; - - foreach ($criterion->value as $identifier) { - try { - $idList[] = $this->contentTypeHandler->loadByIdentifier($identifier)->id; - } catch (NotFoundException $e) { - // Skip non-existing content types, but track for code below - $invalidIdentifiers[] = $identifier; - } - } - - if (count($invalidIdentifiers) > 0) { - $this->logger->warning( - sprintf( - 'Invalid content type identifiers provided for ContentTypeIdentifier criterion: %s', - implode(', ', $invalidIdentifiers) - ) - ); - } - - if (count($idList) === 0) { - return '1 = 0'; - } - - return $queryBuilder->expr()->in( - 'c.contentclass_id', - $queryBuilder->createNamedParameter($idList, Connection::PARAM_INT_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/DateMetadata.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/DateMetadata.php deleted file mode 100644 index c153f74d46..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/DateMetadata.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; -use RuntimeException; - -/** - * Date metadata criterion handler. - */ -class DateMetadata extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\DateMetadata; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $column = $this->getColumnName($criterion); - - $value = (array)$criterion->value; - switch ($criterion->operator) { - case Criterion\Operator::IN: - return $queryBuilder->expr()->in( - $column, - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ); - - case Criterion\Operator::BETWEEN: - return $this->dbPlatform->getBetweenExpression( - $column, - $queryBuilder->createNamedParameter($value[0], ParameterType::INTEGER), - $queryBuilder->createNamedParameter($value[1], ParameterType::INTEGER) - ); - - case Criterion\Operator::EQ: - case Criterion\Operator::GT: - case Criterion\Operator::GTE: - case Criterion\Operator::LT: - case Criterion\Operator::LTE: - $operatorFunction = $this->comparatorMap[$criterion->operator]; - - return $queryBuilder->expr()->$operatorFunction( - $column, - $queryBuilder->createNamedParameter(reset($value), ParameterType::INTEGER) - ); - - default: - throw new RuntimeException( - "Unknown operator '{$criterion->operator}' for DateMetadata Criterion handler." - ); - } - } - - private function getColumnName(Criterion $criterion): string - { - switch ($criterion->target) { - case Criterion\DateMetadata::TRASHED: - return 't.' . Criterion\DateMetadata::TRASHED; - case Criterion\DateMetadata::MODIFIED: - return 'c.' . Criterion\DateMetadata::MODIFIED; - default: - return 'c.' . Criterion\DateMetadata::PUBLISHED; - } - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/Field.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/Field.php deleted file mode 100644 index 54656d972a..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/Field.php +++ /dev/null @@ -1,176 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry as Registry; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Converter as FieldValueConverter; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandler; - -/** - * Field criterion handler. - */ -class Field extends FieldBase -{ - /** - * Field converter registry. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry - */ - protected $fieldConverterRegistry; - - /** - * Field value converter. - * - * @var \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Converter - */ - protected $fieldValueConverter; - - /** - * Transformation processor. - * - * @var \eZ\Publish\Core\Persistence\TransformationProcessor - */ - protected $transformationProcessor; - - public function __construct( - Connection $connection, - ContentTypeHandler $contentTypeHandler, - LanguageHandler $languageHandler, - Registry $fieldConverterRegistry, - FieldValueConverter $fieldValueConverter, - TransformationProcessor $transformationProcessor - ) { - parent::__construct($connection, $contentTypeHandler, $languageHandler); - - $this->fieldConverterRegistry = $fieldConverterRegistry; - $this->fieldValueConverter = $fieldValueConverter; - $this->transformationProcessor = $transformationProcessor; - } - - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\Field; - } - - /** - * Returns relevant field information for the specified field. - * - * The returned information is returned as an array of the attribute - * identifier and the sort column, which should be used. - * - * @param string $fieldIdentifier - * - * @return array - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If no searchable fields are found for the given $fieldIdentifier. - * @throws \RuntimeException if no converter is found - * @throws \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound - */ - protected function getFieldsInformation($fieldIdentifier) - { - $fieldMapArray = []; - $fieldMap = $this->contentTypeHandler->getSearchableFieldMap(); - - foreach ($fieldMap as $fieldIdentifierMap) { - // First check if field exists in the current ContentType, there is nothing to do if it doesn't - if (!isset($fieldIdentifierMap[$fieldIdentifier])) { - continue; - } - - $fieldTypeIdentifier = $fieldIdentifierMap[$fieldIdentifier]['field_type_identifier']; - $fieldMapArray[$fieldTypeIdentifier]['ids'][] = $fieldIdentifierMap[$fieldIdentifier]['field_definition_id']; - if (!isset($fieldMapArray[$fieldTypeIdentifier]['column'])) { - $fieldMapArray[$fieldTypeIdentifier]['column'] = $this->fieldConverterRegistry->getConverter( - $fieldTypeIdentifier - )->getIndexColumn(); - } - } - - if (empty($fieldMapArray)) { - throw new InvalidArgumentException( - '$criterion->target', - "No searchable Fields found for the provided Criterion target '{$fieldIdentifier}'." - ); - } - - return $fieldMapArray; - } - - /** - * @param array $languageSettings - * - * @return string - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException If no searchable fields are found for the given criterion target. - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $fieldsInformation = $this->getFieldsInformation($criterion->target); - - $subSelect = $this->connection->createQueryBuilder(); - $subSelect - ->select('contentobject_id') - ->from(ContentGateway::CONTENT_FIELD_TABLE, 'f_def'); - - $whereExpressions = []; - - foreach ($fieldsInformation as $fieldTypeIdentifier => $fieldsInfo) { - if ($fieldsInfo['column'] === false) { - continue; - } - - $filter = $this->fieldValueConverter->convertCriteria( - $fieldTypeIdentifier, - $queryBuilder, - $subSelect, - $criterion, - $fieldsInfo['column'] - ); - - $whereExpressions[] = $subSelect->expr()->andX( - $subSelect->expr()->in( - 'contentclassattribute_id', - $queryBuilder->createNamedParameter( - $fieldsInfo['ids'], - Connection::PARAM_INT_ARRAY - ) - ), - $filter - ); - } - - return $this->getInExpressionWithFieldConditions( - $queryBuilder, - $subSelect, - $languageSettings, - $whereExpressions, - $fieldsInformation - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldRelation.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldRelation.php deleted file mode 100644 index bbb723c3a8..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldRelation.php +++ /dev/null @@ -1,200 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use RuntimeException; - -/** - * FieldRelation criterion handler. - */ -class FieldRelation extends FieldBase -{ - /** - * Field relation column, tied to chosen table alias. - * - * c_rel: ContentGateway::CONTENT_RELATION_TABLE - * - * @see \eZ\Publish\Core\Persistence\Legacy\Content\Gateway::CONTENT_RELATION_TABLE - */ - private const CONTENT_ITEM_REL_COLUMN = 'c_rel.to_contentobject_id'; - - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\FieldRelation; - } - - /** - * Returns a list of IDs of searchable FieldDefinitions for the given criterion target. - * - * @param string $fieldDefinitionIdentifier - * - * @return array - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException If no searchable fields are found for the given $fieldIdentifier. - */ - protected function getFieldDefinitionsIds($fieldDefinitionIdentifier) - { - $fieldDefinitionIdList = []; - $fieldMap = $this->contentTypeHandler->getSearchableFieldMap(); - - foreach ($fieldMap as $fieldIdentifierMap) { - // First check if field exists in the current ContentType, there is nothing to do if it doesn't - if (!isset($fieldIdentifierMap[$fieldDefinitionIdentifier])) { - continue; - } - - $fieldDefinitionIdList[] = $fieldIdentifierMap[$fieldDefinitionIdentifier]['field_definition_id']; - } - - if (empty($fieldDefinitionIdList)) { - throw new InvalidArgumentException( - '$criterion->target', - "No searchable Fields found for the provided Criterion target '{$fieldDefinitionIdentifier}'." - ); - } - - return $fieldDefinitionIdList; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $fieldDefinitionIds = $this->getFieldDefinitionsIds($criterion->target); - - $criterionValue = (array)$criterion->value; - switch ($criterion->operator) { - case Criterion\Operator::CONTAINS: - if (count($criterionValue) > 1) { - $subRequest = $this->buildQueryForContainsOperator( - $queryBuilder, - $criterionValue, - $fieldDefinitionIds - ); - - return $queryBuilder->expr()->andX(...$subRequest); - } - // Intentionally omitting break - - case Criterion\Operator::IN: - $subSelect = $this->buildQueryForInOperator( - $queryBuilder, - $criterionValue, - $fieldDefinitionIds - ); - - return $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - - default: - throw new RuntimeException( - "Unknown operator '{$criterion->operator}' for RelationList Criterion handler." - ); - } - } - - protected function buildQueryForContainsOperator( - QueryBuilder $queryBuilder, - array $criterionValue, - array $fieldDefinitionIds - ): array { - $subRequest = []; - - foreach ($criterionValue as $value) { - $subSelect = $this->connection->createQueryBuilder(); - $expr = $subSelect->expr(); - - $subSelect - ->select('from_contentobject_id') - ->from(ContentGateway::CONTENT_RELATION_TABLE, 'c_rel'); - - $subSelect->where( - $expr->andX( - $expr->eq( - 'c_rel.from_contentobject_version', - 'c.current_version' - ), - $expr->in( - 'c_rel.contentclassattribute_id', - $queryBuilder->createNamedParameter($fieldDefinitionIds, Connection::PARAM_INT_ARRAY) - ), - $expr->eq( - self::CONTENT_ITEM_REL_COLUMN, - $queryBuilder->createNamedParameter( - $value, - ParameterType::INTEGER - ) - ) - ) - ); - - $subRequest[] = $expr->in( - 'c.id', - $subSelect->getSQL() - ); - } - - return $subRequest; - } - - protected function buildQueryForInOperator( - QueryBuilder $queryBuilder, - array $criterionValue, - array $fieldDefinitionIds - ): QueryBuilder { - $subSelect = $this->connection->createQueryBuilder(); - $expr = $subSelect->expr(); - - $subSelect - ->select('from_contentobject_id') - ->from(ContentGateway::CONTENT_RELATION_TABLE, 'c_rel') - ->where( - $expr->eq( - 'c_rel.from_contentobject_version', - 'c.current_version' - ), - ) - ->andWhere( - $expr->in( - 'c_rel.contentclassattribute_id', - $queryBuilder->createNamedParameter( - $fieldDefinitionIds, - Connection::PARAM_INT_ARRAY - ) - ) - ) - ->andWhere( - $expr->in( - self::CONTENT_ITEM_REL_COLUMN, - $queryBuilder->createNamedParameter( - $criterionValue, - Connection::PARAM_INT_ARRAY - ) - ) - ); - - return $subSelect; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Converter.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Converter.php deleted file mode 100644 index fd9653b6e6..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Converter.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use RuntimeException; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class Converter -{ - /** - * Criterion field value handler registry. - * - * @var \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\HandlerRegistry - */ - protected $registry; - - /** - * Default Criterion field value handler. - * - * @var \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler - */ - protected $defaultHandler; - - /** - * Construct from an array of Criterion field value handlers. - * - * @param \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\HandlerRegistry $registry - * @param \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler|null $defaultHandler - */ - public function __construct(HandlerRegistry $registry, Handler $defaultHandler = null) - { - $this->registry = $registry; - $this->defaultHandler = $defaultHandler; - } - - /** - * Converts the criteria into query fragments. - * - * @param \Doctrine\DBAL\Query\QueryBuilder $outerQuery to be used only for parameter binding - * @param \Doctrine\DBAL\Query\QueryBuilder $subQuery to modify Field Value query constraints - * - * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|string - * - * @throws \RuntimeException if Criterion is not applicable to its target - */ - public function convertCriteria( - string $fieldTypeIdentifier, - QueryBuilder $outerQuery, - QueryBuilder $subQuery, - Criterion $criterion, - string $column - ) { - if ($this->registry->has($fieldTypeIdentifier)) { - return $this->registry->get($fieldTypeIdentifier)->handle( - $outerQuery, - $subQuery, - $criterion, - $column - ); - } - - if ($this->defaultHandler === null) { - throw new RuntimeException( - "No conversion for a Field Type '$fieldTypeIdentifier' found." - ); - } - - return $this->defaultHandler->handle($outerQuery, $subQuery, $criterion, $column); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php deleted file mode 100644 index 4f7af94470..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php +++ /dev/null @@ -1,228 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator as CriterionOperator; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Persistence\TransformationProcessor; -use RuntimeException; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -abstract class Handler -{ - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - /** - * Map of criterion operators to the respective function names - * in the DoctrineDatabase DBAL. - * - * @var array - */ - protected $comparatorMap = [ - CriterionOperator::EQ => 'eq', - CriterionOperator::GT => 'gt', - CriterionOperator::GTE => 'gte', - CriterionOperator::LT => 'lt', - CriterionOperator::LTE => 'lte', - ]; - - /** - * Transformation processor. - * - * @var \eZ\Publish\Core\Persistence\TransformationProcessor - */ - protected $transformationProcessor; - - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform|null */ - protected $dbPlatform; - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct(Connection $connection, TransformationProcessor $transformationProcessor) - { - $this->connection = $connection; - $this->dbPlatform = $connection->getDatabasePlatform(); - $this->transformationProcessor = $transformationProcessor; - } - - /** - * Generates query expression for operator and value of a Field Criterion. - * - * @param \Doctrine\DBAL\Query\QueryBuilder $outerQuery to be used only for parameter binding - * @param \Doctrine\DBAL\Query\QueryBuilder $subQuery to modify Field Value query constraints - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|string - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If passed more than 1 argument to unary operator. - * @throws \RuntimeException If operator is not handled. - */ - public function handle( - QueryBuilder $outerQuery, - QueryBuilder $subQuery, - Criterion $criterion, - string $column - ) { - if (is_array($criterion->value) && Criterion\Operator::isUnary($criterion->operator)) { - if (count($criterion->value) > 1) { - throw new InvalidArgumentException('$criterion->value', "Too many arguments for unary operator '$criterion->operator'"); - } - - $criterion->value = reset($criterion->value); - } - - switch ($criterion->operator) { - case Criterion\Operator::IN: - $values = array_map([$this, 'prepareParameter'], $criterion->value); - $filter = $subQuery->expr()->in( - $column, - $outerQuery->createNamedParameter( - $values, - $this->getParamArrayType($values) - ) - ); - break; - - case Criterion\Operator::BETWEEN: - $filter = $this->dbPlatform->getBetweenExpression( - $column, - $outerQuery->createNamedParameter($this->lowerCase($criterion->value[0])), - $outerQuery->createNamedParameter($this->lowerCase($criterion->value[1])) - ); - break; - - case Criterion\Operator::EQ: - case Criterion\Operator::GT: - case Criterion\Operator::GTE: - case Criterion\Operator::LT: - case Criterion\Operator::LTE: - $operatorFunction = $this->comparatorMap[$criterion->operator]; - $filter = $subQuery->expr()->{$operatorFunction}( - $column, - $this->createNamedParameter($outerQuery, $column, $criterion->value) - ); - break; - - case Criterion\Operator::LIKE: - $value = str_replace('*', '%', $this->prepareLikeString($criterion->value)); - - $filter = $subQuery->expr()->like( - $column, - $outerQuery->createNamedParameter($value) - ); - break; - - case Criterion\Operator::CONTAINS: - $filter = $subQuery->expr()->like( - $column, - $outerQuery->createNamedParameter( - '%' . $this->prepareLikeString($criterion->value) . '%' - ) - ); - break; - - default: - throw new RuntimeException( - "Unknown operator '{$criterion->operator}' for Field Criterion handler." - ); - } - - return $filter; - } - - /** - * Returns the given $string prepared for use in SQL LIKE clause. - * - * LIKE clause wildcards '%' and '_' contained in the given $string will be escaped. - */ - protected function prepareLikeString(string $string): string - { - return addcslashes($this->lowerCase($string), '%_'); - } - - /** - * Downcases a given string using string transformation processor. - */ - protected function lowerCase(string $string): string - { - return $this->transformationProcessor->transformByGroup($string, 'lowercase'); - } - - /** - * @param scalar|array<scalar> $value - * - * @return scalar|array<scalar> - */ - private function prepareParameter($value) - { - if (is_string($value)) { - return $this->lowerCase($value); - } - - if (is_array($value)) { - return array_map([$this, 'prepareParameter'], $value); - } - - return $value; - } - - private function createNamedParameter(QueryBuilder $outerQuery, string $column, $value): ?string - { - switch ($column) { - case 'sort_key_string': - $parameterValue = $this->prepareParameter($value); - $parameterType = ParameterType::STRING; - break; - case 'sort_key_int': - $parameterValue = (int)$value; - $parameterType = ParameterType::INTEGER; - break; - default: - $parameterValue = $value; - $parameterType = null; - } - - return $outerQuery->createNamedParameter( - $parameterValue, - $parameterType - ); - } - - /** - * @param array<int, scalar> $values - */ - private function getParamArrayType(array $values): int - { - if (empty($values)) { - throw new InvalidArgumentException('$values', 'Array cannot be empty'); - } - - $types = []; - foreach ($values as $value) { - if (is_bool($value) || ($value !== 0 && is_int($value))) { - // Ignore 0 as ambiguous (float vs int) - $types[] = Connection::PARAM_INT_ARRAY; - } else { - // Floats are considered strings - $types[] = Connection::PARAM_STR_ARRAY; - } - } - - $arrayValueTypes = array_unique($types); - - // Fallback to Connection::PARAM_STR_ARRAY - return $arrayValueTypes[0] ?? Connection::PARAM_STR_ARRAY; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Composite.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Composite.php deleted file mode 100644 index 6ad7a8f323..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Composite.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler; - -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - * - * Composite value handler is used for creating a filter on a value that can be partially matched. - * Eg. TextLine string, where it makes sense to match only a part of the sentence. - */ -class Composite extends Handler -{ -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/HandlerRegistry.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/HandlerRegistry.php deleted file mode 100644 index 2257a41b0c..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/HandlerRegistry.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue; - -use OutOfBoundsException; - -/** - * Registry for Criterion field value handlers. - */ -class HandlerRegistry -{ - /** - * Map of Criterion field value handlers where key is field type identifier - * and value is field value handler. - * - * @var \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler[] - */ - protected $map = []; - - /** - * Create field value handler registry with handler map. - * - * @param \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler[] $map - * Map of Criterion field value handlers where key is field type identifier and value field value handler - */ - public function __construct(array $map = []) - { - foreach ($map as $fieldTypeIdentifier => $handler) { - $this->register($fieldTypeIdentifier, $handler); - } - } - - /** - * Register $handler for $fieldTypeIdentifier. - * - * @param string $fieldTypeIdentifier - * @param \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler $handler - */ - public function register($fieldTypeIdentifier, $handler) - { - $this->map[$fieldTypeIdentifier] = $handler; - } - - /** - * Returns handler for given $fieldTypeIdentifier. - * - * @throws \OutOfBoundsException If handler is not registered for a given $fieldTypeIdentifier - * - * @param string $fieldTypeIdentifier - * - * @return \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler - */ - public function get($fieldTypeIdentifier) - { - if (!isset($this->map[$fieldTypeIdentifier])) { - throw new OutOfBoundsException("No handler registered for Field Type '{$fieldTypeIdentifier}'."); - } - - return $this->map[$fieldTypeIdentifier]; - } - - /** - * Checks if handler is registered for the given $fieldTypeIdentifier. - * - * @param string $fieldTypeIdentifier - * - * @return bool - */ - public function has($fieldTypeIdentifier) - { - return isset($this->map[$fieldTypeIdentifier]); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php deleted file mode 100644 index ecdfaa5a61..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php +++ /dev/null @@ -1,281 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator; -use eZ\Publish\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Full text criterion handler. - */ -class FullText extends CriterionHandler -{ - /** - * Full text search configuration options. - * - * @var array - */ - protected $configuration = [ - // @see getStopWordThresholdValue() - 'stopWordThresholdFactor' => 0.66, - 'enableWildcards' => true, - 'commands' => [ - 'apostrophe_normalize', - 'apostrophe_to_doublequote', - 'ascii_lowercase', - 'ascii_search_cleanup', - 'cyrillic_diacritical', - 'cyrillic_lowercase', - 'cyrillic_search_cleanup', - 'cyrillic_transliterate_ascii', - 'doublequote_normalize', - 'endline_search_normalize', - 'greek_diacritical', - 'greek_lowercase', - 'greek_normalize', - 'greek_transliterate_ascii', - 'hebrew_transliterate_ascii', - 'hyphen_normalize', - 'inverted_to_normal', - 'latin1_diacritical', - 'latin1_lowercase', - 'latin1_transliterate_ascii', - 'latin-exta_diacritical', - 'latin-exta_lowercase', - 'latin-exta_transliterate_ascii', - 'latin_lowercase', - 'latin_search_cleanup', - 'latin_search_decompose', - 'math_to_ascii', - 'punctuation_normalize', - 'space_normalize', - 'special_decompose', - 'specialwords_search_normalize', - 'tab_search_normalize', - ], - ]; - - /** - * @var int|null - * - * @see getStopWordThresholdValue() - */ - private $stopWordThresholdValue; - - /** - * Transformation processor to normalize search strings. - * - * @var \eZ\Publish\Core\Persistence\TransformationProcessor - */ - protected $processor; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator */ - private $languageMaskGenerator; - - /** - * @param array $configuration - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException On invalid $configuration values - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct( - Connection $connection, - TransformationProcessor $processor, - MaskGenerator $languageMaskGenerator, - array $configuration = [] - ) { - parent::__construct($connection); - - $this->configuration = $configuration + $this->configuration; - $this->processor = $processor; - - if ( - $this->configuration['stopWordThresholdFactor'] < 0 || - $this->configuration['stopWordThresholdFactor'] > 1 - ) { - throw new InvalidArgumentException( - "\$configuration['stopWordThresholdFactor']", - 'Stop Word Threshold Factor needs to be between 0 and 1, got: ' . $this->configuration['stopWordThresholdFactor'] - ); - } - $this->languageMaskGenerator = $languageMaskGenerator; - } - - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\FullText; - } - - /** - * Tokenize String. - * - * @param string $string - * - * @return array - */ - protected function tokenizeString($string) - { - return preg_split('/[^\w|*]/u', $string, -1, PREG_SPLIT_NO_EMPTY); - } - - /** - * Get single word query expression. - * - * Depending on the configuration of the full text search criterion - * converter wildcards are either transformed into the respective LIKE - * queries, or everything is just compared using equal. - */ - protected function getWordExpression(QueryBuilder $query, string $token): string - { - if ($this->configuration['enableWildcards'] && $token[0] === '*') { - return $query->expr()->like( - 'word', - $query->createNamedParameter('%' . substr($token, 1)) - ); - } - - if ($this->configuration['enableWildcards'] && $token[strlen($token) - 1] === '*') { - return $query->expr()->like( - 'word', - $query->createNamedParameter(substr($token, 0, -1) . '%') - ); - } - - return $query->expr()->eq('word', $query->createNamedParameter($token)); - } - - /** - * Get sub query to select relevant word IDs. - * - * @uses getStopWordThresholdValue To get threshold for words we would like to ignore in query. - */ - protected function getWordIdSubquery(QueryBuilder $query, string $string): string - { - $subQuery = $this->connection->createQueryBuilder(); - $tokens = $this->tokenizeString( - $this->processor->transform($string, $this->configuration['commands']) - ); - $wordExpressions = []; - foreach ($tokens as $token) { - $wordExpressions[] = $this->getWordExpression($query, $token); - } - - // Search for provided string itself as well - $wordExpressions[] = $this->getWordExpression($query, $string); - - $whereCondition = $subQuery->expr()->orX(...$wordExpressions); - - // If stop word threshold is below 100%, make it part of $whereCondition - if ($this->configuration['stopWordThresholdFactor'] < 1) { - $whereCondition = $subQuery->expr()->andX( - $whereCondition, - $subQuery->expr()->lt( - 'object_count', - $query->createNamedParameter($this->getStopWordThresholdValue(), ParameterType::STRING) - ) - ); - } - - $subQuery - ->select('id') - ->from('ezsearch_word') - ->where($whereCondition); - - return $subQuery->getSQL(); - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $subSelect = $this->connection->createQueryBuilder(); - $expr = $queryBuilder->expr(); - $subSelect - ->select( - 'contentobject_id' - )->from( - 'ezsearch_object_word_link' - )->where( - $expr->in( - 'word_id', - // pass main Query Builder to set query parameters - $this->getWordIdSubquery($queryBuilder, $criterion->value) - ) - ); - - if (!empty($languageSettings['languages'])) { - $languageMask = $this->languageMaskGenerator->generateLanguageMaskFromLanguageCodes( - $languageSettings['languages'], - $languageSettings['useAlwaysAvailable'] ?? true - ); - - $subSelect->andWhere( - $expr->gt( - $this->dbPlatform->getBitAndComparisonExpression( - 'ezsearch_object_word_link.language_mask', - $queryBuilder->createNamedParameter($languageMask, ParameterType::INTEGER) - ), - $queryBuilder->createNamedParameter(0, ParameterType::INTEGER) - ) - ); - } - - return $expr->in( - 'c.id', - $subSelect->getSQL() - ); - } - - /** - * Returns an exact content object count threshold to ignore common terms on. - * - * Common terms will be skipped if used in more then a given percentage of the total amount of content - * objects in the database. Percentage is defined by stopWordThresholdFactor configuration. - * - * Example: If stopWordThresholdFactor is 0.66 (66%), and a term like "the" exists in more then 66% of the content, it - * will ignore the phrase as it is assumed to not add any value ot the search. - * - * Caches the result for the instance used as we don't need this to be super accurate as it is based on percentage, - * set by stopWordThresholdFactor. - * - * @return int - */ - protected function getStopWordThresholdValue(): int - { - if ($this->stopWordThresholdValue !== null) { - return $this->stopWordThresholdValue; - } - - // Cached value does not exists, do a simple count query on ezcontentobject table - $query = $this->connection->createQueryBuilder(); - $query - ->select($this->dbPlatform->getCountExpression('id')) - ->from(ContentGateway::CONTENT_ITEM_TABLE); - - $count = (int)$query->execute()->fetchColumn(); - - // Calculate the int stopWordThresholdValue based on count (first column) * factor - return $this->stopWordThresholdValue = (int)($count * $this->configuration['stopWordThresholdFactor']); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserBased.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserBased.php deleted file mode 100644 index 29e13b9cd1..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserBased.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -class IsUserBased extends CriterionHandler -{ - public function accept(Criterion $criterion): bool - { - return $criterion instanceof Criterion\IsUserBased; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $isUserBased = (bool)reset($criterion->value); - - $subSelect = $this->connection->createQueryBuilder(); - $subSelect - ->select( - 'contentobject_id' - )->from( - 'ezuser' - ); - - $queryExpression = $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - - return $isUserBased - ? $queryExpression - : "NOT({$queryExpression})"; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserEnabled.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserEnabled.php deleted file mode 100644 index 22d343a0dd..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserEnabled.php +++ /dev/null @@ -1,52 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\FieldType\User\UserStorage\Gateway\DoctrineStorage as UserGateway; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -class IsUserEnabled extends CriterionHandler -{ - public function accept(Criterion $criterion): bool - { - return $criterion instanceof Criterion\IsUserEnabled; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $subSelect = $this->connection->createQueryBuilder(); - $subSelect - ->select('t1.contentobject_id') - ->from(UserGateway::USER_TABLE, 't1') - ->leftJoin( - 't1', - 'ezuser_setting', - 't2', - 't1.contentobject_id = t2.user_id' - ) - ->where( - $queryBuilder->expr()->eq( - 't2.is_enabled', - $queryBuilder->createNamedParameter((int)reset($criterion->value)) - ) - ); - - return $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php deleted file mode 100644 index 4d099876d1..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * LanguageCode criterion handler. - */ -class LanguageCode extends CriterionHandler -{ - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator */ - private $maskGenerator; - - public function __construct(Connection $connection, MaskGenerator $maskGenerator) - { - parent::__construct($connection); - - $this->maskGenerator = $maskGenerator; - } - - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\LanguageCode; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - /* @var $criterion \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LanguageCode */ - return $queryBuilder->expr()->gt( - $this->dbPlatform->getBitAndComparisonExpression( - 'c.language_mask', - $this->maskGenerator->generateLanguageMaskFromLanguageCodes( - $criterion->value, - $criterion->matchAlwaysAvailable - ) - ), - 0 - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalAnd.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalAnd.php deleted file mode 100644 index a5d6f9bed5..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalAnd.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Logical and criterion handler. - */ -class LogicalAnd extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\LogicalAnd; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $subexpressions = []; - foreach ($criterion->criteria as $subCriterion) { - $subexpressions[] = $converter->convertCriteria( - $queryBuilder, - $subCriterion, - $languageSettings - ); - } - - return $queryBuilder->expr()->andX(...$subexpressions); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalNot.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalNot.php deleted file mode 100644 index e7725b5730..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalNot.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Logical not criterion handler. - */ -class LogicalNot extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\LogicalNot; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - return sprintf( - 'NOT (%s)', - $converter->convertCriteria($queryBuilder, $criterion->criteria[0], $languageSettings) - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalOr.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalOr.php deleted file mode 100644 index e1d511183f..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalOr.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Logical or criterion handler. - */ -class LogicalOr extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\LogicalOr; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $subexpressions = []; - foreach ($criterion->criteria as $subCriterion) { - $subexpressions[] = $converter->convertCriteria( - $queryBuilder, - $subCriterion, - $languageSettings - ); - } - - return $queryBuilder->expr()->orX(...$subexpressions); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php deleted file mode 100644 index 28c732e33c..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php +++ /dev/null @@ -1,286 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Value\MapLocationValue; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use RuntimeException; - -/** - * MapLocationDistance criterion handler. - */ -class MapLocationDistance extends FieldBase -{ - /** - * Distance in kilometers of one degree longitude at the Equator. - */ - public const DEGREE_KM = 111.195; - - /** - * Radius of the planet in kilometers. - */ - public const EARTH_RADIUS = 6371.01; - - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\MapLocationDistance; - } - - /** - * Returns a list of IDs of searchable FieldDefinitions for the given criterion target. - * - * @param string $fieldIdentifier - * - * @return array - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException If no searchable fields are found for the given $fieldIdentifier. - */ - protected function getFieldDefinitionIds($fieldIdentifier) - { - $fieldDefinitionIdList = []; - $fieldMap = $this->contentTypeHandler->getSearchableFieldMap(); - - foreach ($fieldMap as $fieldIdentifierMap) { - // First check if field exists in the current ContentType, there is nothing to do if it doesn't - if ( - !( - isset($fieldIdentifierMap[$fieldIdentifier]) - && $fieldIdentifierMap[$fieldIdentifier]['field_type_identifier'] === 'ezgmaplocation' - ) - ) { - continue; - } - - $fieldDefinitionIdList[] = $fieldIdentifierMap[$fieldIdentifier]['field_definition_id']; - } - - if (empty($fieldDefinitionIdList)) { - throw new InvalidArgumentException( - '$criterion->target', - "No searchable Fields found for the provided Criterion target '{$fieldIdentifier}'." - ); - } - - return $fieldDefinitionIdList; - } - - protected function kilometersToDegrees($kilometers) - { - return $kilometers / self::DEGREE_KM; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $fieldDefinitionIds = $this->getFieldDefinitionIds($criterion->target); - $subSelect = $this->connection->createQueryBuilder(); - - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Value\MapLocationValue $location */ - $location = $criterion->valueData; - - // note: avoid using literal names for parameters to account for multiple visits of the same Criterion - $latitudePlaceholder = $queryBuilder->createNamedParameter($location->latitude); - $longitudePlaceholder = $queryBuilder->createNamedParameter($location->longitude); - $correctionPlaceholder = $queryBuilder->createNamedParameter( - cos(deg2rad($location->latitude)) ** 2 - ); - - // build: (latitude1 - latitude2)^2 + (longitude2 - longitude2)^2 * longitude_correction) - $latitudeSubstrExpr = "(map.latitude - {$latitudePlaceholder})"; - $longitudeSubstrExpr = "(map.longitude - {$longitudePlaceholder})"; - $latitudeExpr = "{$latitudeSubstrExpr} * {$latitudeSubstrExpr}"; - $longitudeExpr = "{$longitudeSubstrExpr} * {$longitudeSubstrExpr} * {$correctionPlaceholder}"; - $distanceExpression = "{$latitudeExpr} + {$longitudeExpr}"; - - $expr = $subSelect->expr(); - switch ($criterion->operator) { - case Criterion\Operator::IN: - case Criterion\Operator::EQ: - case Criterion\Operator::GT: - case Criterion\Operator::GTE: - case Criterion\Operator::LT: - case Criterion\Operator::LTE: - $operatorFunction = $this->comparatorMap[$criterion->operator]; - $distanceInDegrees = $this->kilometersToDegrees($criterion->value) ** 2; - $distanceFilter = $expr->$operatorFunction( - $distanceExpression, - $this->dbPlatform->getRoundExpression( - $queryBuilder->createNamedParameter($distanceInDegrees), - 10 - ) - ); - break; - - case Criterion\Operator::BETWEEN: - $distanceInDegrees1 = $this->kilometersToDegrees($criterion->value[0]) ** 2; - $distanceInDegrees2 = $this->kilometersToDegrees($criterion->value[1]) ** 2; - $distanceFilter = $this->dbPlatform->getBetweenExpression( - $distanceExpression, - $this->dbPlatform->getRoundExpression( - $queryBuilder->createNamedParameter($distanceInDegrees1), - 10 - ), - $this->dbPlatform->getRoundExpression( - $queryBuilder->createNamedParameter($distanceInDegrees2), - 10 - ), - ); - break; - - default: - throw new RuntimeException('Unknown operator.'); - } - - // Calculate bounding box if possible - // @todo consider covering operators EQ and IN as well - $boundingConstraints = []; - switch ($criterion->operator) { - case Criterion\Operator::LT: - case Criterion\Operator::LTE: - $distanceUpper = $criterion->value; - break; - case Criterion\Operator::BETWEEN: - $distanceUpper = $criterion->value[0] > $criterion->value[1] ? - $criterion->value[0] : - $criterion->value[1]; - break; - default: - // Skip other operators - break; - } - if (isset($distanceUpper)) { - $boundingConstraints = $this->getBoundingConstraints( - $queryBuilder, - $location, - $distanceUpper - ); - } - - $subSelect - ->select('contentobject_id') - ->from('ezcontentobject_attribute', 'f_def') - ->innerJoin( - 'f_def', - 'ezgmaplocation', - 'map', - $expr->andX( - 'map.contentobject_version = f_def.version', - 'map.contentobject_attribute_id = f_def.id', - ...$boundingConstraints - ) - ) - // note: joining by correlation on outer table - ->where('f_def.version = c.current_version') - ->andWhere( - $expr->in( - 'f_def.contentclassattribute_id', - $queryBuilder->createNamedParameter($fieldDefinitionIds, Connection::PARAM_INT_ARRAY) - ) - ) - ->andWhere($distanceFilter) - ->andWhere($this->getFieldCondition($queryBuilder, $languageSettings)) - ; - - return $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - } - - /** - * Credit for the formula goes to http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates. - */ - protected function getBoundingConstraints( - QueryBuilder $query, - MapLocationValue $location, - float $distance - ): array { - $boundingCoordinates = $this->getBoundingCoordinates($location, $distance); - - $expr = $query->expr(); - - return [ - $expr->gte( - 'map.latitude', - $query->createNamedParameter($boundingCoordinates['lowLatitude']) - ), - $expr->gte( - 'map.longitude', - $query->createNamedParameter($boundingCoordinates['lowLongitude']) - ), - $expr->lte( - 'map.latitude', - $query->createNamedParameter($boundingCoordinates['highLatitude']) - ), - $expr->lte( - 'map.longitude', - $query->createNamedParameter($boundingCoordinates['highLongitude']) - ), - ]; - } - - /** - * Calculates and returns bounding box coordinates. - * - * Credits: http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Value\MapLocationValue $location - * @param float $distance - * - * @return array - * - * @todo it should also be possible to calculate inner bounding box, which could be applied for the - * operators GT, GTE and lower distance of the BETWEEN operator. - */ - protected function getBoundingCoordinates(MapLocationValue $location, $distance) - { - $radiansLatitude = deg2rad($location->latitude); - $radiansLongitude = deg2rad($location->longitude); - $angularDistance = $distance / self::EARTH_RADIUS; - $deltaLongitude = asin(sin($angularDistance) / cos($radiansLatitude)); - - $lowLatitudeRadians = $radiansLatitude - $angularDistance; - $highLatitudeRadians = $radiansLatitude + $angularDistance; - - // Check that bounding box does not include poles. - if ($lowLatitudeRadians > -M_PI_2 && $highLatitudeRadians < M_PI_2) { - $boundingCoordinates = [ - 'lowLatitude' => rad2deg($lowLatitudeRadians), - 'lowLongitude' => rad2deg($radiansLongitude - $deltaLongitude), - 'highLatitude' => rad2deg($highLatitudeRadians), - 'highLongitude' => rad2deg($radiansLongitude + $deltaLongitude), - ]; - } else { - // Handle the pole(s) being inside a bounding box, in this case we MUST cover - // full circle of Earth's longitude and one or both poles. - // Note that calculation for distances over the polar regions with flat Earth formula - // will be VERY imprecise. - $boundingCoordinates = [ - 'lowLatitude' => rad2deg(max($lowLatitudeRadians, -M_PI_2)), - 'lowLongitude' => rad2deg(-M_PI), - 'highLatitude' => rad2deg(min($highLatitudeRadians, M_PI_2)), - 'highLongitude' => rad2deg(M_PI), - ]; - } - - return $boundingCoordinates; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/MatchAll.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/MatchAll.php deleted file mode 100644 index 2624745023..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/MatchAll.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * MatchAll criterion handler. - */ -class MatchAll extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\MatchAll; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - return '1 = 1'; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/MatchNone.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/MatchNone.php deleted file mode 100644 index b4e32172a7..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/MatchNone.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * MatchNone criterion handler. - */ -class MatchNone extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\MatchNone; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - return '1 = 0'; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ObjectStateId.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ObjectStateId.php deleted file mode 100644 index 674cfa3d2a..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ObjectStateId.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * ObjectState ID criterion handler. - */ -class ObjectStateId extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\ObjectStateId; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $subSelect = $this->connection->createQueryBuilder(); - $value = (array)$criterion->value; - $subSelect - ->select( - 'contentobject_id' - )->from( - 'ezcobj_state_link' - )->where( - $queryBuilder->expr()->in( - 'contentobject_state_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ) - ); - - return $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ObjectStateIdentifier.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ObjectStateIdentifier.php deleted file mode 100644 index e2b62686fe..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/ObjectStateIdentifier.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -class ObjectStateIdentifier extends CriterionHandler -{ - public function accept(Criterion $criterion): bool - { - return $criterion instanceof Criterion\ObjectStateIdentifier; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $value = (array)$criterion->value; - $matchStateIdentifier = $queryBuilder->expr()->in( - 't2.identifier', - $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) - ); - - if (null !== $criterion->target) { - $criterionTarget = (array)$criterion->target; - $constraints = $queryBuilder->expr()->andX( - $queryBuilder->expr()->in( - 't3.identifier', - $queryBuilder->createNamedParameter( - $criterionTarget, - Connection::PARAM_STR_ARRAY - ) - ), - $matchStateIdentifier - ); - } else { - $constraints = $matchStateIdentifier; - } - - $subSelect = $this->connection->createQueryBuilder(); - $subSelect - ->select('t1.contentobject_id') - ->from('ezcobj_state_link', 't1') - ->leftJoin( - 't1', - 'ezcobj_state', - 't2', - 't1.contentobject_state_id = t2.id', - ) - ->leftJoin( - 't2', - 'ezcobj_state_group', - 't3', - 't2.group_id = t3.id' - ) - ->where($constraints); - - return $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/RemoteId.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/RemoteId.php deleted file mode 100644 index 96f616364f..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/RemoteId.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Remote id criterion handler. - */ -class RemoteId extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\RemoteId; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $remoteIds = (array)$criterion->value; - - return $queryBuilder->expr()->in( - 'c.remote_id', - $queryBuilder->createNamedParameter($remoteIds, Connection::PARAM_STR_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionId.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionId.php deleted file mode 100644 index 869e9b00f0..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionId.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Section criterion handler. - */ -class SectionId extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\SectionId; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $sectionIds = (array)$criterion->value; - - return $queryBuilder->expr()->in( - 'c.section_id', - $queryBuilder->createNamedParameter($sectionIds, Connection::PARAM_INT_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionIdentifier.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionIdentifier.php deleted file mode 100644 index d80d6acf7a..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionIdentifier.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -class SectionIdentifier extends CriterionHandler -{ - public function accept(Criterion $criterion): bool - { - return $criterion instanceof Criterion\SectionIdentifier; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $subSelect = $this->connection->createQueryBuilder(); - $value = (array)$criterion->value; - $subSelect - ->select('t1.id') - ->from('ezcontentobject', 't1') - ->leftJoin( - 't1', - 'ezsection', - 't2', - 't1.section_id = t2.id' - ) - ->where( - $queryBuilder->expr()->in( - 't2.identifier', - $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) - ) - ); - - return $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserEmail.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserEmail.php deleted file mode 100644 index b856547cfb..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserEmail.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -class UserEmail extends CriterionHandler -{ - /** @var \eZ\Publish\Core\Persistence\TransformationProcessor */ - private $transformationProcessor; - - public function __construct( - Connection $connection, - TransformationProcessor $transformationProcessor - ) { - parent::__construct($connection); - - $this->transformationProcessor = $transformationProcessor; - } - - public function accept(Criterion $criterion): bool - { - return $criterion instanceof Criterion\UserEmail; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - if (Criterion\Operator::LIKE === $criterion->operator) { - $expression = $queryBuilder->expr()->like( - 't1.email', - $queryBuilder->createNamedParameter( - str_replace( - '*', - '%', - addcslashes( - $this->transformationProcessor->transformByGroup( - $criterion->value, - 'lowercase' - ), - '%_' - ) - ) - ) - ); - } else { - $value = (array)$criterion->value; - $expression = $queryBuilder->expr()->in( - 't1.email', - $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) - ); - } - - $subSelect = $this->connection->createQueryBuilder(); - $subSelect - ->select('t1.contentobject_id') - ->from('ezuser', 't1') - ->where($expression); - - return $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserId.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserId.php deleted file mode 100644 index 5b4d9206ff..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserId.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\FieldType\User\UserStorage\Gateway\DoctrineStorage as UserGateway; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -class UserId extends CriterionHandler -{ - public function accept(Criterion $criterion): bool - { - return $criterion instanceof Criterion\UserId; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $subSelect = $this->connection->createQueryBuilder(); - $value = (array)$criterion->value; - $subSelect - ->select('t1.contentobject_id') - ->from(UserGateway::USER_TABLE, 't1') - ->where( - $queryBuilder->expr()->in( - 't1.contentobject_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ) - ); - - return $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserLogin.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserLogin.php deleted file mode 100644 index adfd4160fd..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserLogin.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -class UserLogin extends CriterionHandler -{ - /** @var \eZ\Publish\Core\Persistence\TransformationProcessor */ - private $transformationProcessor; - - public function __construct( - Connection $connection, - TransformationProcessor $transformationProcessor - ) { - parent::__construct($connection); - - $this->transformationProcessor = $transformationProcessor; - } - - public function accept(Criterion $criterion): bool - { - return $criterion instanceof Criterion\UserLogin; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $expr = $queryBuilder->expr(); - if (Criterion\Operator::LIKE === $criterion->operator) { - $expression = $expr->like( - 't1.login', - $queryBuilder->createNamedParameter( - str_replace( - '*', - '%', - addcslashes( - $this->transformationProcessor->transformByGroup( - $criterion->value, - 'lowercase' - ), - '%_' - ) - ) - ) - ); - } else { - $value = (array)$criterion->value; - $expression = $expr->in( - 't1.login', - $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) - ); - } - - $subSelect = $this->connection->createQueryBuilder(); - $subSelect - ->select('t1.contentobject_id') - ->from('ezuser', 't1') - ->where($expression); - - return $expr->in( - 'c.id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserMetadata.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserMetadata.php deleted file mode 100644 index bc9c946e5c..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserMetadata.php +++ /dev/null @@ -1,91 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; -use RuntimeException; - -/** - * User metadata criterion handler. - */ -class UserMetadata extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\UserMetadata; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $value = (array)$criterion->value; - switch ($criterion->target) { - case Criterion\UserMetadata::MODIFIER: - return $queryBuilder->expr()->in( - 'v.creator_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ); - - case Criterion\UserMetadata::GROUP: - $subSelect = $this->connection->createQueryBuilder(); - $subSelect - ->select( - 't1.contentobject_id' - )->from( - LocationGateway::CONTENT_TREE_TABLE, - 't1' - )->innerJoin( - 't1', - LocationGateway::CONTENT_TREE_TABLE, - 't2', - $queryBuilder->expr()->like( - 't1.path_string', - $this->dbPlatform->getConcatExpression( - 't2.path_string', - $queryBuilder->createNamedParameter('%', ParameterType::STRING) - ) - ) - )->where( - $queryBuilder->expr()->in( - 't2.contentobject_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ) - ); - - return $queryBuilder->expr()->in( - 'c.owner_id', - $subSelect->getSQL() - ); - - case Criterion\UserMetadata::OWNER: - return $queryBuilder->expr()->in( - 'c.owner_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ); - default: - break; - } - - throw new RuntimeException("Invalid target Criterion: '" . $criterion->target . "'"); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/AbstractRandom.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/AbstractRandom.php deleted file mode 100644 index 81901ddd92..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/AbstractRandom.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -abstract class AbstractRandom extends SortClauseHandler -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\Random; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - '%s AS %s', - $this->getRandomFunctionName($sortClause->targetData->seed), - $column = $this->getSortColumnName($number) - ) - ); - - return [$column]; - } - - abstract public function getRandomFunctionName(?int $seed): string; - - abstract public function getDriverName(): string; -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentId.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentId.php deleted file mode 100644 index fb5b33652e..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentId.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class ContentId extends SortClauseHandler -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\ContentId; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - 'c.id AS %s', - $column = $this->getSortColumnName($number) - ) - ); - - return [$column]; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentName.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentName.php deleted file mode 100644 index b446323ad0..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentName.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class ContentName extends SortClauseHandler -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\ContentName; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - 'c.name AS %s', - $column = $this->getSortColumnName($number) - ) - ); - - return (array)$column; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DateModified.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DateModified.php deleted file mode 100644 index 1f084e14ee..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DateModified.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class DateModified extends SortClauseHandler -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\DateModified; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - 'c.modified AS %s', - $column = $this->getSortColumnName($number) - ) - ); - - return [$column]; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DatePublished.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DatePublished.php deleted file mode 100644 index 375df3d54b..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DatePublished.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class DatePublished extends SortClauseHandler -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\DatePublished; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - 'c.published AS %s', - $column = $this->getSortColumnName($number) - ) - ); - - return [$column]; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Factory/RandomSortClauseHandlerFactory.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Factory/RandomSortClauseHandlerFactory.php deleted file mode 100644 index 94c41061ec..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Factory/RandomSortClauseHandlerFactory.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Factory; - -use Doctrine\DBAL\Connection; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom; - -class RandomSortClauseHandlerFactory -{ - /** @var iterable|\eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom[] */ - private $randomSortClauseGateways = []; - - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - public function __construct(Connection $connection, iterable $randomSortClauseGateways) - { - $this->connection = $connection; - $this->randomSortClauseGateways = $randomSortClauseGateways; - } - - /** - * @throws \Doctrine\DBAL\DBALException - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - */ - public function getGateway(): AbstractRandom - { - $driverName = $this->connection->getDatabasePlatform()->getName(); - - foreach ($this->randomSortClauseGateways as $gateway) { - if ($gateway->getDriverName() === $driverName) { - return $gateway; - } - } - - throw new InvalidArgumentException('$this->randomSortClauseGateways', 'No RandomSortClauseHandler found for driver ' . $driverName); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Field.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Field.php deleted file mode 100644 index 824c0ce0ae..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Field.php +++ /dev/null @@ -1,239 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class Field extends SortClauseHandler -{ - /** - * Language handler. - * - * @var \eZ\Publish\SPI\Persistence\Content\Language\Handler - */ - protected $languageHandler; - - /** - * Content Type handler. - * - * @var \eZ\Publish\SPI\Persistence\Content\Type\Handler - */ - protected $contentTypeHandler; - - public function __construct( - Connection $connection, - LanguageHandler $languageHandler, - ContentTypeHandler $contentTypeHandler - ) { - parent::__construct($connection); - - $this->languageHandler = $languageHandler; - $this->contentTypeHandler = $contentTypeHandler; - } - - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\Field; - } - - /** - * Apply selects to the query. - * - * Returns the name of the (aliased) column, which information should be - * used for sorting. - * - * @param \Doctrine\DBAL\Query\QueryBuilder $query - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * @param int $number - * - * @return array - */ - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - '%s AS %s', - $query->expr()->isNotNull( - $this->getSortTableName($number) . '.sort_key_int' - ), - $column1 = $this->getSortColumnName($number . '_null') - ), - sprintf( - '%s AS %s', - $query->expr()->isNotNull( - $this->getSortTableName($number) . '.sort_key_string' - ), - $column2 = $this->getSortColumnName($number . '_bis_null') - ), - sprintf( - '%s AS %s', - $this->getSortTableName($number) . '.sort_key_int', - $column3 = $this->getSortColumnName($number) - ), - sprintf( - '%s AS %s', - $this->getSortTableName($number) . '.sort_key_string', - $column4 = $this->getSortColumnName($number . '_bis') - ) - ); - - return [$column1, $column2, $column3, $column4]; - } - - public function applyJoin( - QueryBuilder $query, - SortClause $sortClause, - int $number, - array $languageSettings - ): void { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target\FieldTarget $fieldTarget */ - $fieldTarget = $sortClause->targetData; - $fieldMap = $this->contentTypeHandler->getSearchableFieldMap(); - - if (!isset($fieldMap[$fieldTarget->typeIdentifier][$fieldTarget->fieldIdentifier]['field_definition_id'])) { - throw new InvalidArgumentException( - '$sortClause->targetData', - 'No searchable Fields found for the provided Sort Clause target ' . - "'{$fieldTarget->fieldIdentifier}' on '{$fieldTarget->typeIdentifier}'." - ); - } - - $fieldDefinitionId = $fieldMap[$fieldTarget->typeIdentifier][$fieldTarget->fieldIdentifier]['field_definition_id']; - $table = $this->getSortTableName($number); - - $tableAlias = $this->connection->quoteIdentifier($table); - $query - ->leftJoin( - 'c', - Gateway::CONTENT_FIELD_TABLE, - $tableAlias, - $query->expr()->andX( - $query->expr()->eq( - $query->createNamedParameter( - $fieldDefinitionId, - ParameterType::INTEGER - ), - $tableAlias . '.contentclassattribute_id' - ), - $query->expr()->eq( - $tableAlias . '.contentobject_id', - 'c.id' - ), - $query->expr()->eq( - $tableAlias . '.version', - 'c.current_version' - ), - $this->getFieldCondition($query, $languageSettings, $table) - ) - ); - } - - protected function getFieldCondition( - QueryBuilder $query, - array $languageSettings, - $fieldTableName - ) { - // 1. Use main language(s) by default - if (empty($languageSettings['languages'])) { - return $query->expr()->gt( - $this->dbPlatform->getBitAndComparisonExpression( - 'c.initial_language_id', - $fieldTableName . '.language_id' - ), - $query->createNamedParameter(0, ParameterType::INTEGER) - ); - } - - // 2. Otherwise use prioritized languages - $leftSide = $this->dbPlatform->getBitAndComparisonExpression( - sprintf( - 'c.language_mask - %s', - $this->dbPlatform->getBitAndComparisonExpression( - 'c.language_mask', - $fieldTableName . '.language_id' - ) - ), - $query->createNamedParameter(1, ParameterType::INTEGER) - ); - $rightSide = $this->dbPlatform->getBitAndComparisonExpression( - $fieldTableName . '.language_id', - $query->createNamedParameter(1, ParameterType::INTEGER) - ); - - for ($index = count( - $languageSettings['languages'] - ) - 1, $multiplier = 2; $index >= 0; $index--, $multiplier *= 2) { - $languageId = $this->languageHandler - ->loadByLanguageCode($languageSettings['languages'][$index])->id; - - $addToLeftSide = $this->dbPlatform->getBitAndComparisonExpression( - sprintf( - 'c.language_mask - %s', - $this->dbPlatform->getBitAndComparisonExpression( - 'c.language_mask', - $fieldTableName . '.language_id' - ) - ), - $query->createNamedParameter($languageId, ParameterType::INTEGER) - ); - $addToRightSide = $this->dbPlatform->getBitAndComparisonExpression( - $fieldTableName . '.language_id', - $query->createNamedParameter($languageId, ParameterType::INTEGER) - ); - - if ($multiplier > $languageId) { - $factor = $multiplier / $languageId; - for ($shift = 0; $factor > 1; $factor = $factor / 2, $shift++); - $factorTerm = ' << ' . $shift; - $addToLeftSide .= $factorTerm; - $addToRightSide .= $factorTerm; - } elseif ($multiplier < $languageId) { - $factor = $languageId / $multiplier; - for ($shift = 0; $factor > 1; $factor = $factor / 2, $shift++); - $factorTerm = ' >> ' . $shift; - $addToLeftSide .= $factorTerm; - $addToRightSide .= $factorTerm; - } - - $leftSide = "$leftSide + ($addToLeftSide)"; - $rightSide = "$rightSide + ($addToRightSide)"; - } - - return $query->expr()->andX( - $query->expr()->gt( - $this->dbPlatform->getBitAndComparisonExpression( - 'c.language_mask', - $fieldTableName . '.language_id' - ), - $query->createNamedParameter(0, ParameterType::INTEGER) - ), - $query->expr()->lt($leftSide, $rightSide) - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/MapLocationDistance.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/MapLocationDistance.php deleted file mode 100644 index 2687cf4d36..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/MapLocationDistance.php +++ /dev/null @@ -1,123 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class MapLocationDistance extends Field -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\MapLocationDistance; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target\MapLocationTarget $target */ - $target = $sortClause->targetData; - $externalTable = $this->getSortTableName($number, 'ezgmaplocation'); - - // note: avoid using literal names for parameters to account for multiple visits of the same Criterion - $latitudePlaceholder = $query->createNamedParameter($target->latitude); - $longitudePlaceholder = $query->createNamedParameter($target->longitude); - - // note: can have literal name for all visits of this Criterion because it's constant - $query->setParameter(':longitude_correction', cos(deg2rad($target->latitude)) ** 2); - - // build: (latitude1 - latitude2)^2 + (longitude2 - longitude2)^2 * longitude_correction) - $latitudeSubstrExpr = "({$externalTable}.latitude - {$latitudePlaceholder})"; - $longitudeSubstrExpr = "({$externalTable}.longitude - {$longitudePlaceholder})"; - $latitudeExpr = "{$latitudeSubstrExpr} * {$latitudeSubstrExpr}"; - $longitudeExpr = "{$longitudeSubstrExpr} * {$longitudeSubstrExpr} * :longitude_correction"; - $distanceExpression = "{$latitudeExpr} + {$longitudeExpr}"; - - $query->addSelect( - sprintf('%s AS %s', $distanceExpression, $column1 = $this->getSortColumnName($number)) - ); - - return [$column1]; - } - - public function applyJoin( - QueryBuilder $query, - SortClause $sortClause, - int $number, - array $languageSettings - ): void { - /** @var \eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target\FieldTarget $fieldTarget */ - $fieldTarget = $sortClause->targetData; - $fieldMap = $this->contentTypeHandler->getSearchableFieldMap(); - - if (!isset($fieldMap[$fieldTarget->typeIdentifier][$fieldTarget->fieldIdentifier]['field_definition_id'])) { - throw new InvalidArgumentException( - '$sortClause->targetData', - 'No searchable Fields found for the provided Sort Clause target ' . - "'{$fieldTarget->fieldIdentifier}' on '{$fieldTarget->typeIdentifier}'." - ); - } - - $fieldDefinitionId = $fieldMap[$fieldTarget->typeIdentifier][$fieldTarget->fieldIdentifier]['field_definition_id']; - $table = $this->getSortTableName($number); - $externalTable = $this->getSortTableName($number, 'ezgmaplocation'); - - $tableAlias = $this->connection->quoteIdentifier($table); - $externalTableAlias = $this->connection->quoteIdentifier($externalTable); - $query - ->leftJoin( - 'c', - ContentGateway::CONTENT_FIELD_TABLE, - $tableAlias, - $query->expr()->andX( - $query->expr()->eq( - $query->createNamedParameter($fieldDefinitionId, ParameterType::INTEGER), - $tableAlias . '.contentclassattribute_id' - ), - $query->expr()->eq( - $tableAlias . '.contentobject_id', - 'c.id' - ), - $query->expr()->eq( - $tableAlias . '.version', - 'c.current_version' - ), - $this->getFieldCondition($query, $languageSettings, $tableAlias) - ) - ) - ->leftJoin( - $tableAlias, - 'ezgmaplocation', - $externalTableAlias, - $query->expr()->andX( - $query->expr()->eq( - $externalTableAlias . '.contentobject_version', - $tableAlias . '.version' - ), - $query->expr()->eq( - $externalTableAlias . '.contentobject_attribute_id', - $tableAlias . '.id' - ) - ) - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/MySqlRandom.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/MySqlRandom.php deleted file mode 100644 index 76e08711ee..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/MySqlRandom.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Random; - -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom; - -class MySqlRandom extends AbstractRandom -{ - public function getDriverName(): string - { - return 'mysql'; - } - - public function getRandomFunctionName(?int $seed): string - { - return 'RAND(' . $seed . ')'; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/PgSqlRandom.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/PgSqlRandom.php deleted file mode 100644 index de480699ae..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/PgSqlRandom.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Random; - -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom; - -class PgSqlRandom extends AbstractRandom -{ - public function getDriverName(): string - { - return 'postgresql'; - } - - public function getRandomFunctionName(?int $seed): string - { - return 'random()'; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/SqlLiteRandom.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/SqlLiteRandom.php deleted file mode 100644 index 2583a356e3..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/SqlLiteRandom.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Random; - -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom; - -class SqlLiteRandom extends AbstractRandom -{ - public function getDriverName(): string - { - return 'sqlite'; - } - - public function getRandomFunctionName(?int $seed): string - { - return 'random()'; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/SectionIdentifier.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/SectionIdentifier.php deleted file mode 100644 index cc407994fc..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/SectionIdentifier.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class SectionIdentifier extends SortClauseHandler -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\SectionIdentifier; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - 'c.section_id AS %s', - $column = $this->getSortColumnName($number) - ) - ); - - return [$column]; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/SectionName.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/SectionName.php deleted file mode 100644 index 8e9a32a08e..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/SectionName.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class SectionName extends SortClauseHandler -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\SectionName; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - '%s AS %s', - $this->getSortTableName($number) . '.name', - $column = $this->getSortColumnName($number) - ) - ); - - return [$column]; - } - - public function applyJoin( - QueryBuilder $query, - SortClause $sortClause, - int $number, - array $languageSettings - ): void { - $table = $this->getSortTableName($number); - $query - ->leftJoin( - 'c', - 'ezsection', - $table, - "{$table}.id = c.section_id" - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/ContentTypeName.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/ContentTypeName.php deleted file mode 100644 index 662df33078..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/ContentTypeName.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Trash; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway as ContentTypeGateway; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * @internal - */ -final class ContentTypeName extends SortClauseHandler -{ - public function accept(SortClause $sortClause): bool - { - return $sortClause instanceof SortClause\Trash\ContentTypeName; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - 'ctn.name AS %s', - $column = $this->getSortColumnName($number) - ) - ); - - return [$column]; - } - - public function applyJoin( - QueryBuilder $query, - SortClause $sortClause, - int $number, - array $languageSettings - ): void { - $query->innerJoin( - 'c', - ContentTypeGateway::CONTENT_TYPE_TABLE, - 'ct', - 'c.contentclass_id = ct.id' - )->innerJoin( - 'ct', - ContentTypeGateway::CONTENT_TYPE_NAME_TABLE, - 'ctn', - 'ctn.contentclass_id = ct.id' - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/DateTrashed.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/DateTrashed.php deleted file mode 100644 index 11dcde73b5..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/DateTrashed.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Trash; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * @internal - */ -final class DateTrashed extends SortClauseHandler -{ - public function accept(SortClause $sortClause): bool - { - return $sortClause instanceof SortClause\Trash\DateTrashed; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - 't.trashed AS %s', - $column = $this->getSortColumnName($number) - ) - ); - - return (array)$column; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/UserLogin.php b/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/UserLogin.php deleted file mode 100644 index 38fe528ad3..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/UserLogin.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Trash; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Persistence\Legacy\User\Gateway as UserGateway; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * @internal - */ -final class UserLogin extends SortClauseHandler -{ - public function accept(SortClause $sortClause): bool - { - return $sortClause instanceof SortClause\Trash\UserLogin; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - 'u.login AS %s', - $column = $this->getSortColumnName($number) - ) - ); - - return (array)$column; - } - - public function applyJoin( - QueryBuilder $query, - SortClause $sortClause, - int $number, - array $languageSettings - ): void { - $query->leftJoin( - 'c', - UserGateway::USER_TABLE, - 'u', - 'c.owner_id = u.contentobject_id' - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Gateway.php b/eZ/Publish/Core/Search/Legacy/Content/Gateway.php deleted file mode 100644 index 2acf37f6b5..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Gateway.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -/** - * The Content Search Gateway provides the implementation for one database to - * retrieve the desired content objects. - */ -abstract class Gateway -{ - /** - * Returns a list of object satisfying the $criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * @param int $offset - * @param int $limit - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sort - * @param array $languageFilter - * @param bool $doCount - * - * @return mixed[][] - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if Criterion is not applicable to its target - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException if a given Criterion Handler or Sort Clause is not implemented - */ - abstract public function find( - Criterion $criterion, - $offset, - $limit, - array $sort = null, - array $languageFilter = [], - $doCount = true - ): array; -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/Ancestor.php b/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/Ancestor.php deleted file mode 100644 index 52c86b24da..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/Ancestor.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Visits the Ancestor criterion. - */ -class Ancestor extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\Ancestor; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $idSet = []; - foreach ($criterion->value as $value) { - foreach (explode('/', trim($value, '/')) as $id) { - $idSet[$id] = true; - } - } - - $subSelect = $this->connection->createQueryBuilder(); - $subSelect - ->select('contentobject_id') - ->from('ezcontentobject_tree') - ->where( - $queryBuilder->expr()->in( - 'node_id', - array_keys($idSet) - ) - ); - - return $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/LocationId.php b/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/LocationId.php deleted file mode 100644 index e89172a8d8..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/LocationId.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Location id criterion handler. - */ -class LocationId extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\LocationId; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $locationIds = (array)$criterion->value; - $subSelect = $this->connection->createQueryBuilder(); - $subSelect - ->select( - 'contentobject_id' - )->from( - 'ezcontentobject_tree' - )->where( - $queryBuilder->expr()->in( - 'node_id', - $queryBuilder->createNamedParameter($locationIds, Connection::PARAM_INT_ARRAY) - ) - ); - - return $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/LocationRemoteId.php b/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/LocationRemoteId.php deleted file mode 100644 index e30a6e6eac..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/LocationRemoteId.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Location remote id criterion handler. - */ -class LocationRemoteId extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\LocationRemoteId; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $subSelect = $this->connection->createQueryBuilder(); - $value = (array)$criterion->value; - $subSelect - ->select( - 'contentobject_id' - )->from( - 'ezcontentobject_tree', - 'subquery_location' - )->where( - $queryBuilder->expr()->in( - 'subquery_location.remote_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) - ) - ); - - return $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/ParentLocationId.php b/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/ParentLocationId.php deleted file mode 100644 index 777ed5acb0..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/ParentLocationId.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Parent location id criterion handler. - */ -class ParentLocationId extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\ParentLocationId; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $subSelect = $this->connection->createQueryBuilder(); - $expr = $queryBuilder->expr(); - $subSelect - ->select( - 'contentobject_id' - )->from( - 'ezcontentobject_tree' - )->where( - $expr->in( - 'parent_node_id', - $queryBuilder->createNamedParameter( - $criterion->value, - Connection::PARAM_INT_ARRAY - ) - ) - ); - - return $expr->in( - 'c.id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/PermissionSubtree.php b/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/PermissionSubtree.php deleted file mode 100644 index 52aecb1cb4..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/PermissionSubtree.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Repository\Values\Content\Query\Criterion\PermissionSubtree as PermissionSubtreeCriterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * PermissionSubtree criterion handler. - */ -class PermissionSubtree extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof PermissionSubtreeCriterion; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $table = 'permission_subtree'; - - $statements = []; - foreach ($criterion->value as $pattern) { - $statements[] = $queryBuilder->expr()->like( - "{$table}.path_string", - $queryBuilder->createNamedParameter($pattern . '%') - ); - } - - $locationTableAlias = $this->connection->quoteIdentifier($table); - if (!$this->hasJoinedTableAs($queryBuilder, $locationTableAlias)) { - $queryBuilder - ->leftJoin( - 'c', - LocationGateway::CONTENT_TREE_TABLE, - $locationTableAlias, - $queryBuilder->expr()->eq( - "{$locationTableAlias}.contentobject_id", - 'c.id' - ) - ); - } - - return $queryBuilder->expr()->orX(...$statements); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/Subtree.php b/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/Subtree.php deleted file mode 100644 index 2ac0bac60f..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/Subtree.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler; - -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Subtree criterion handler. - */ -class Subtree extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\Subtree; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $statements = []; - foreach ($criterion->value as $pattern) { - $statements[] = $queryBuilder->expr()->like( - 'path_string', - $queryBuilder->createNamedParameter($pattern . '%', ParameterType::STRING) - ); - } - - $subSelect = $this->connection->createQueryBuilder(); - $subSelect - ->select('contentobject_id') - ->from('ezcontentobject_tree') - ->where($queryBuilder->expr()->orX(...$statements)); - - return $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/Visibility.php b/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/Visibility.php deleted file mode 100644 index 895a826944..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Gateway/CriterionHandler/Visibility.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Visibility criterion handler. - */ -class Visibility extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\Visibility; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $subSelect = $this->connection->createQueryBuilder(); - - if ($criterion->value[0] === Criterion\Visibility::VISIBLE) { - $expression = $queryBuilder->expr()->andX( - $queryBuilder->expr()->eq( - 'subquery_location.is_hidden', - 0 - ), - $queryBuilder->expr()->eq( - 'subquery_location.is_invisible', - 0 - ) - ); - } else { - $expression = $queryBuilder->expr()->orX( - $queryBuilder->expr()->eq( - 'subquery_location.is_hidden', - 1 - ), - $queryBuilder->expr()->eq( - 'subquery_location.is_invisible', - 1 - ) - ); - } - - $subSelect - ->select('contentobject_id') - ->from(LocationGateway::CONTENT_TREE_TABLE, 'subquery_location') - ->where($expression); - - return $queryBuilder->expr()->in( - 'c.id', - $subSelect->getSQL() - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Search/Legacy/Content/Gateway/DoctrineDatabase.php deleted file mode 100644 index ea6aa209b2..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,264 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter; -use eZ\Publish\Core\Search\Legacy\Content\Gateway; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use RuntimeException; - -/** - * Content locator gateway implementation using the Doctrine database. - */ -final class DoctrineDatabase extends Gateway -{ - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */ - private $dbPlatform; - - /** - * Criteria converter. - * - * @var \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter - */ - private $criteriaConverter; - - /** - * Sort clause converter. - * - * @var \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter - */ - private $sortClauseConverter; - - /** - * Language handler. - * - * @var \eZ\Publish\SPI\Persistence\Content\Language\Handler - */ - private $languageHandler; - - /** - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct( - Connection $connection, - CriteriaConverter $criteriaConverter, - SortClauseConverter $sortClauseConverter, - LanguageHandler $languageHandler - ) { - $this->connection = $connection; - $this->dbPlatform = $connection->getDatabasePlatform(); - $this->criteriaConverter = $criteriaConverter; - $this->sortClauseConverter = $sortClauseConverter; - $this->languageHandler = $languageHandler; - } - - public function find( - Criterion $criterion, - $offset, - $limit, - array $sort = null, - array $languageFilter = [], - $doCount = true - ): array { - $count = $doCount ? $this->getResultCount($criterion, $languageFilter) : null; - - if (!$doCount && $limit === 0) { - throw new RuntimeException('Invalid query. Cannot disable count and request 0 items at the same time.'); - } - - if ($limit === 0 || ($count !== null && $count <= $offset)) { - return ['count' => $count, 'rows' => []]; - } - - $contentInfoList = $this->getContentInfoList($criterion, $sort, $offset, $limit, $languageFilter); - - return [ - 'count' => $count, - 'rows' => $contentInfoList, - ]; - } - - /** - * Generates a language mask from the given $languageSettings. - * - * @param array $languageSettings - * - * @return int - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - private function getLanguageMask(array $languageSettings) - { - $mask = 0; - if ($languageSettings['useAlwaysAvailable']) { - $mask |= 1; - } - - foreach ($languageSettings['languages'] as $languageCode) { - $mask |= $this->languageHandler->loadByLanguageCode($languageCode)->id; - } - - return $mask; - } - - /** - * @param array $languageFilter - * - * @return string - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - private function getQueryCondition( - Criterion $filter, - QueryBuilder $query, - array $languageFilter - ) { - $expr = $query->expr(); - $condition = $expr->andX( - $this->criteriaConverter->convertCriteria($query, $filter, $languageFilter), - $expr->eq( - 'c.status', - ContentInfo::STATUS_PUBLISHED - ), - $expr->eq( - 'v.status', - VersionInfo::STATUS_PUBLISHED - ) - ); - - // If not main-languages query - if (!empty($languageFilter['languages'])) { - $condition = $expr->andX( - $condition, - $expr->gt( - $this->dbPlatform->getBitAndComparisonExpression( - 'c.language_mask', - $query->createNamedParameter( - $this->getLanguageMask($languageFilter), - ParameterType::INTEGER, - ':language_mask' - ) - ), - $query->createNamedParameter(0, ParameterType::INTEGER, ':zero') - ) - ); - } - - return $condition; - } - - /** - * Get result count. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter - * @param array $languageFilter - * - * @return int - */ - private function getResultCount(Criterion $filter, array $languageFilter) - { - $query = $this->connection->createQueryBuilder(); - - $columnName = 'c.id'; - $query - ->select("COUNT( DISTINCT $columnName )") - ->from(ContentGateway::CONTENT_ITEM_TABLE, 'c') - ->innerJoin( - 'c', - ContentGateway::CONTENT_VERSION_TABLE, - 'v', - 'c.id = v.contentobject_id', - ); - - $query->where( - $this->getQueryCondition($filter, $query, $languageFilter) - ); - - $statement = $query->execute(); - - return (int)$statement->fetchColumn(); - } - - /** - * Get sorted arrays of content IDs, which should be returned. - * - * @param array $sort - * @param mixed $offset - * @param mixed $limit - * @param array $languageFilter - * - * @return int[] - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - private function getContentInfoList( - Criterion $filter, - ?array $sort, - ?int $offset, - ?int $limit, - array $languageFilter - ): array { - $query = $this->connection->createQueryBuilder(); - $query->select( - 'DISTINCT c.*, main_tree.main_node_id AS main_tree_main_node_id', - ); - - if ($sort !== null) { - $this->sortClauseConverter->applySelect($query, $sort); - } - - $query - ->from(ContentGateway::CONTENT_ITEM_TABLE, 'c') - ->innerJoin( - 'c', - ContentGateway::CONTENT_VERSION_TABLE, - 'v', - 'c.id = v.contentobject_id' - ) - ->leftJoin( - 'c', - LocationGateway::CONTENT_TREE_TABLE, - 'main_tree', - $query->expr()->andX( - 'main_tree.contentobject_id = c.id', - 'main_tree.main_node_id = main_tree.node_id' - ) - ); - - if ($sort !== null) { - $this->sortClauseConverter->applyJoin($query, $sort, $languageFilter); - } - - $query->where( - $this->getQueryCondition($filter, $query, $languageFilter) - ); - - if ($sort !== null) { - $this->sortClauseConverter->applyOrderBy($query); - } - - $query->setMaxResults($limit); - $query->setFirstResult($offset); - - $statement = $query->execute(); - - return $statement->fetchAll(FetchMode::ASSOCIATIVE); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Search/Legacy/Content/Gateway/ExceptionConversion.php deleted file mode 100644 index fdafefdece..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Search\Legacy\Content\Gateway; -use PDOException; - -/** - * The Content Search Gateway provides the implementation for one database to - * retrieve the desired content objects. - */ -class ExceptionConversion extends Gateway -{ - /** - * @var \eZ\Publish\Core\Search\Legacy\Content\Gateway - */ - protected $innerGateway; - - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function find( - Criterion $criterion, - $offset = 0, - $limit = null, - array $sort = null, - array $languageFilter = [], - $doCount = true - ): array { - try { - return $this->innerGateway->find($criterion, $offset, $limit, $sort, $languageFilter, $doCount); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Handler.php b/eZ/Publish/Core/Search/Legacy/Content/Handler.php deleted file mode 100644 index 3683279998..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Handler.php +++ /dev/null @@ -1,408 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content; - -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper as ContentMapper; -use eZ\Publish\Core\Search\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Search\Legacy\Content\Mapper\FullTextMapper; -use eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway as WordIndexerGateway; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Search\VersatileHandler as SearchHandlerInterface; - -/** - * The Content Search handler retrieves sets of of Content objects, based on a - * set of criteria. - * - * The basic idea of this class is to do the following: - * - * 1) The find methods retrieve a recursive set of filters, which define which - * content objects to retrieve from the database. Those may be combined using - * boolean operators. - * - * 2) This recursive criterion definition is visited into a query, which limits - * the content retrieved from the database. We might not be able to create - * sensible queries from all criterion definitions. - * - * 3) The query might be possible to optimize (remove empty statements), - * reduce singular and and or constructs… - * - * 4) Additionally we might need a post-query filtering step, which filters - * content objects based on criteria, which could not be converted in to - * database statements. - */ -class Handler implements SearchHandlerInterface -{ - /** - * Content locator gateway. - * - * @var \eZ\Publish\Core\Search\Legacy\Content\Gateway - */ - protected $gateway; - - /** - * Location locator gateway. - * - * @var \eZ\Publish\Core\Search\Legacy\Content\Location\Gateway - */ - protected $locationGateway; - - /** - * Word indexer gateway. - * - * @var \eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway - */ - protected $indexerGateway; - - /** - * Content mapper. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Mapper - */ - protected $contentMapper; - - /** - * Location locationMapper. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper - */ - protected $locationMapper; - - /** - * Language handler. - * - * @var \eZ\Publish\SPI\Persistence\Content\Language\Handler - */ - protected $languageHandler; - - /** - * FullText mapper. - * - * @var \eZ\Publish\Core\Search\Legacy\Content\Mapper\FullTextMapper - */ - protected $mapper; - - /** - * Creates a new content handler. - * - * @param \eZ\Publish\Core\Search\Legacy\Content\Gateway $gateway - * @param \eZ\Publish\Core\Search\Legacy\Content\Location\Gateway $locationGateway - * @param \eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway $indexerGateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Mapper $contentMapper - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper $locationMapper - * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $languageHandler - * @param \eZ\Publish\Core\Search\Legacy\Content\Mapper\FullTextMapper $mapper - */ - public function __construct( - Gateway $gateway, - LocationGateway $locationGateway, - WordIndexerGateway $indexerGateway, - ContentMapper $contentMapper, - LocationMapper $locationMapper, - LanguageHandler $languageHandler, - FullTextMapper $mapper - ) { - $this->gateway = $gateway; - $this->locationGateway = $locationGateway; - $this->indexerGateway = $indexerGateway; - $this->contentMapper = $contentMapper; - $this->locationMapper = $locationMapper; - $this->languageHandler = $languageHandler; - $this->mapper = $mapper; - } - - /** - * Finds content objects for the given query. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if Query criterion is not applicable to its target - * - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - * @param array $languageFilter - a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code> - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult - */ - public function findContent(Query $query, array $languageFilter = []) - { - if (!isset($languageFilter['languages'])) { - $languageFilter['languages'] = []; - } - - if (!isset($languageFilter['useAlwaysAvailable'])) { - $languageFilter['useAlwaysAvailable'] = true; - } - - $start = microtime(true); - $query->filter = $query->filter ?: new Criterion\MatchAll(); - $query->query = $query->query ?: new Criterion\MatchAll(); - - // The legacy search does not know about scores, so that we just - // combine the query with the filter - $filter = new Criterion\LogicalAnd([$query->query, $query->filter]); - - $data = $this->gateway->find( - $filter, - $query->offset, - $query->limit, - $query->sortClauses, - $languageFilter, - $query->performCount - ); - - $result = new SearchResult(); - $result->time = microtime(true) - $start; - $result->totalCount = $data['count']; - $contentInfoList = $this->contentMapper->extractContentInfoFromRows( - $data['rows'], - '', - 'main_tree_' - ); - - foreach ($contentInfoList as $index => $contentInfo) { - $searchHit = new SearchHit(); - $searchHit->valueObject = $contentInfo; - $searchHit->matchedTranslation = $this->extractMatchedLanguage( - $data['rows'][$index]['language_mask'], - $data['rows'][$index]['initial_language_id'], - $languageFilter - ); - - $result->searchHits[] = $searchHit; - } - - return $result; - } - - protected function extractMatchedLanguage($languageMask, $mainLanguageId, $languageSettings) - { - $languageList = !empty($languageSettings['languages']) ? - $this->languageHandler->loadListByLanguageCodes($languageSettings['languages']) : - []; - - foreach ($languageList as $language) { - if ($languageMask & $language->id) { - return $language->languageCode; - } - } - - if ($languageMask & 1 || empty($languageSettings['languages'])) { - return $this->languageHandler->load($mainLanguageId)->languageCode; - } - - return null; - } - - /** - * Performs a query for a single content object. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if Criterion is not applicable to its target - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter - * @param array $languageFilter - a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code> - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo - */ - public function findSingle(Criterion $filter, array $languageFilter = []) - { - if (!isset($languageFilter['languages'])) { - $languageFilter['languages'] = []; - } - - if (!isset($languageFilter['useAlwaysAvailable'])) { - $languageFilter['useAlwaysAvailable'] = true; - } - - $searchQuery = new Query(); - $searchQuery->filter = $filter; - $searchQuery->query = new Criterion\MatchAll(); - $searchQuery->offset = 0; - $searchQuery->limit = 2; // Because we optimize away the count query below - $searchQuery->performCount = true; - $searchQuery->sortClauses = null; - $result = $this->findContent($searchQuery, $languageFilter); - - if (empty($result->searchHits)) { - throw new NotFoundException('Content', 'findSingle() found no content for the given $criterion'); - } elseif (isset($result->searchHits[1])) { - throw new InvalidArgumentException('totalCount', 'findSingle() found more then one item for the given $criterion'); - } - - $first = reset($result->searchHits); - - return $first->valueObject; - } - - /** - * @see \eZ\Publish\SPI\Search\Handler::findLocations - */ - public function findLocations(LocationQuery $query, array $languageFilter = []) - { - if (!isset($languageFilter['languages'])) { - $languageFilter['languages'] = []; - } - - if (!isset($languageFilter['useAlwaysAvailable'])) { - $languageFilter['useAlwaysAvailable'] = true; - } - - $start = microtime(true); - $query->filter = $query->filter ?: new Criterion\MatchAll(); - $query->query = $query->query ?: new Criterion\MatchAll(); - - // The legacy search does not know about scores, so we just - // combine the query with the filter - $data = $this->locationGateway->find( - new Criterion\LogicalAnd([$query->query, $query->filter]), - $query->offset, - $query->limit, - $query->sortClauses, - $languageFilter, - $query->performCount - ); - - $result = new SearchResult(); - $result->time = microtime(true) - $start; - $result->totalCount = $data['count']; - $locationList = $this->locationMapper->createLocationsFromRows($data['rows']); - - foreach ($locationList as $index => $location) { - $searchHit = new SearchHit(); - $searchHit->valueObject = $location; - $searchHit->matchedTranslation = $this->extractMatchedLanguage( - $data['rows'][$index]['language_mask'], - $data['rows'][$index]['initial_language_id'], - $languageFilter - ); - - $result->searchHits[] = $searchHit; - } - - return $result; - } - - /** - * Suggests a list of values for the given prefix. - * - * @param string $prefix - * @param string[] $fieldPaths - * @param int $limit - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - */ - public function suggest($prefix, $fieldPaths = [], $limit = 10, Criterion $filter = null) - { - throw new NotImplementedException('Suggestions are not supported by Legacy search engine.'); - } - - /** - * Indexes a content object. - * - * @param \eZ\Publish\SPI\Persistence\Content $content - */ - public function indexContent(Content $content) - { - $fullTextValue = $this->mapper->mapContent($content); - - $this->indexerGateway->index($fullTextValue); - } - - /** - * Bulk index list of content objects. - * - * @param \eZ\Publish\SPI\Persistence\Content[] $contentList - * @param callable $errorCallback (Content $content, NotFoundException $e) - */ - public function bulkIndex(array $contentList, callable $errorCallback) - { - $fullTextBulkData = []; - foreach ($contentList as $content) { - try { - $fullTextBulkData[] = $this->mapper->mapContent($content); - } catch (NotFoundException $e) { - $errorCallback($content, $e); - } - } - - $this->indexerGateway->bulkIndex($fullTextBulkData); - } - - /** - * @param \eZ\Publish\SPI\Persistence\Content\Location $location - */ - public function indexLocation(Location $location) - { - // Not needed with Legacy Storage/Search Engine - } - - /** - * Deletes a content object from the index. - * - * @param int $contentId - * @param int|null $versionId - */ - public function deleteContent($contentId, $versionId = null) - { - $this->indexerGateway->remove($contentId, $versionId); - } - - public function deleteTranslation(int $contentId, string $languageCode): void - { - // Not needed with Legacy Storage/Search Engine - } - - /** - * Deletes a location from the index. - * - * @param mixed $locationId - * @param mixed $contentId - */ - public function deleteLocation($locationId, $contentId) - { - // Not needed with Legacy Storage/Search Engine - } - - /** - * Purges all contents from the index. - */ - public function purgeIndex() - { - $this->indexerGateway->purgeIndex(); - } - - /** - * Commits the data to the index, making it available for search. - * - * @param bool $flush - */ - public function commit($flush = false) - { - // Not needed with Legacy Storage/Search Engine - } - - public function supports(int $capabilityFlag): bool - { - return false; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Indexer.php b/eZ/Publish/Core/Search/Legacy/Content/Indexer.php deleted file mode 100644 index 6cc8a0cd0d..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Indexer.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content; - -use Doctrine\DBAL\Connection; -use Exception; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\Core\Search\Common\IncrementalIndexer; -use eZ\Publish\Core\Search\Legacy\Content\Handler as LegacySearchHandler; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Handler as PersistenceHandler; -use Psr\Log\LoggerInterface; - -class Indexer extends IncrementalIndexer -{ - public function __construct( - LoggerInterface $logger, - PersistenceHandler $persistenceHandler, - Connection $connection, - LegacySearchHandler $searchHandler - ) { - parent::__construct($logger, $persistenceHandler, $connection, $searchHandler); - } - - public function getName() - { - return 'eZ Platform Legacy (SQL) Search Engine'; - } - - public function updateSearchIndex(array $contentIds, $commit) - { - $contentHandler = $this->persistenceHandler->contentHandler(); - foreach ($contentIds as $contentId) { - try { - $info = $contentHandler->loadContentInfo($contentId); - if ($info->status === ContentInfo::STATUS_PUBLISHED) { - $this->searchHandler->indexContent( - $contentHandler->load($info->id, $info->currentVersionNo) - ); - } else { - $this->searchHandler->deleteContent($contentId); - } - } catch (NotFoundException $e) { - $this->searchHandler->deleteContent($contentId); - } catch (Exception $e) { - $context = [ - 'contentId' => $contentId, - 'error' => $e->getMessage(), - ]; - $this->logger->error('Unable to index the content', $context); - } - } - } - - public function purge() - { - $this->searchHandler->purgeIndex(); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/IndexerGateway.php b/eZ/Publish/Core/Search/Legacy/Content/IndexerGateway.php deleted file mode 100644 index 5e44cf975b..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/IndexerGateway.php +++ /dev/null @@ -1,147 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Legacy\Content; - -use DateTimeInterface; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Driver\ResultStatement; -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Search\Content\IndexerGateway as SPIIndexerGateway; -use Generator; - -/** - * @internal - */ -final class IndexerGateway implements SPIIndexerGateway -{ - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - public function __construct(Connection $connection) - { - $this->connection = $connection; - } - - public function getContentSince(DateTimeInterface $since, int $iterationCount): Generator - { - $query = $this->buildQueryForContentSince($since); - $query->orderBy('c.modified'); - - yield from $this->fetchIteration($query->execute(), $iterationCount); - } - - public function countContentSince(DateTimeInterface $since): int - { - $query = $this->buildCountingQuery( - $this->buildQueryForContentSince($since) - ); - - return (int)$query->execute()->fetchOne(); - } - - public function getContentInSubtree(string $locationPath, int $iterationCount): Generator - { - $query = $this->buildQueryForContentInSubtree($locationPath); - - yield from $this->fetchIteration($query->execute(), $iterationCount); - } - - public function countContentInSubtree(string $locationPath): int - { - $query = $this->buildCountingQuery( - $this->buildQueryForContentInSubtree($locationPath) - ); - - return (int)$query->execute()->fetchOne(); - } - - public function getAllContent(int $iterationCount): Generator - { - $query = $this->buildQueryForAllContent(); - - yield from $this->fetchIteration($query->execute(), $iterationCount); - } - - public function countAllContent(): int - { - $query = $this->buildCountingQuery( - $this->buildQueryForAllContent() - ); - - return (int)$query->execute()->fetchOne(); - } - - private function buildQueryForContentSince(DateTimeInterface $since): QueryBuilder - { - return $this->connection->createQueryBuilder() - ->select('c.id') - ->from('ezcontentobject', 'c') - ->where('c.status = :status')->andWhere('c.modified >= :since') - ->setParameter('status', ContentInfo::STATUS_PUBLISHED, ParameterType::INTEGER) - ->setParameter('since', $since->getTimestamp(), ParameterType::INTEGER); - } - - private function buildQueryForContentInSubtree(string $locationPath): QueryBuilder - { - return $this->connection->createQueryBuilder() - ->select('DISTINCT c.id') - ->from('ezcontentobject', 'c') - ->innerJoin('c', 'ezcontentobject_tree', 't', 't.contentobject_id = c.id') - ->where('c.status = :status') - ->andWhere('t.path_string LIKE :path') - ->setParameter('status', ContentInfo::STATUS_PUBLISHED, ParameterType::INTEGER) - ->setParameter('path', $locationPath . '%', ParameterType::STRING); - } - - private function buildQueryForAllContent(): QueryBuilder - { - return $this->connection->createQueryBuilder() - ->select('c.id') - ->from('ezcontentobject', 'c') - ->where('c.status = :status') - ->setParameter('status', ContentInfo::STATUS_PUBLISHED, ParameterType::INTEGER); - } - - /** - * @throws \Doctrine\DBAL\Exception - */ - private function buildCountingQuery(QueryBuilder $query): QueryBuilder - { - $databasePlatform = $this->connection->getDatabasePlatform(); - - // wrap existing select part in count expression - $query->select( - $databasePlatform->getCountExpression( - $query->getQueryPart('select')[0] - ) - ); - - return $query; - } - - private function fetchIteration(ResultStatement $statement, int $iterationCount): Generator - { - do { - $contentIds = []; - for ($i = 0; $i < $iterationCount; ++$i) { - if ($contentId = $statement->fetchOne()) { - $contentIds[] = $contentId; - } elseif (empty($contentIds)) { - return; - } else { - break; - } - } - - yield $contentIds; - } while (!empty($contentId)); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway.php deleted file mode 100644 index c67ecc87a7..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -/** - * Base class for location search gateways. - */ -abstract class Gateway -{ - /** - * Returns total count and data for all Locations satisfying the parameters. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * @param int $offset - * @param int $limit - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses - * @param array $languageFilter - * @param bool $doCount - * - * @return mixed[][] - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if Criterion is not applicable to its target - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException if a given Criterion Handler or Sort Clause is not implemented - */ - abstract public function find( - Criterion $criterion, - $offset, - $limit, - array $sortClauses = null, - array $languageFilter = [], - $doCount = true - ): array; -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Ancestor.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Ancestor.php deleted file mode 100644 index c87b5f5ff3..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Ancestor.php +++ /dev/null @@ -1,50 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Visits the Ancestor criterion. - */ -class Ancestor extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\Ancestor; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $idSet = []; - foreach ($criterion->value as $value) { - foreach (explode('/', trim($value, '/')) as $id) { - $idSet[$id] = true; - } - } - - return $queryBuilder->expr()->in( - 't.node_id', - $queryBuilder->createNamedParameter(array_keys($idSet), Connection::PARAM_INT_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Depth.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Depth.php deleted file mode 100644 index 5cf751dfce..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Depth.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location; - -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; -use RuntimeException; - -/** - * Location depth criterion handler. - */ -class Depth extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\Location\Depth; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $column = 't.depth'; - - switch ($criterion->operator) { - case Criterion\Operator::IN: - return $queryBuilder->expr()->in( - $column, - $criterion->value - ); - - case Criterion\Operator::BETWEEN: - return $this->dbPlatform->getBetweenExpression( - $column, - $queryBuilder->createNamedParameter($criterion->value[0], ParameterType::STRING), - $queryBuilder->createNamedParameter($criterion->value[1], ParameterType::STRING) - ); - - case Criterion\Operator::EQ: - case Criterion\Operator::GT: - case Criterion\Operator::GTE: - case Criterion\Operator::LT: - case Criterion\Operator::LTE: - $operatorFunction = $this->comparatorMap[$criterion->operator]; - - return $queryBuilder->expr()->$operatorFunction( - $column, - $queryBuilder->createNamedParameter(reset($criterion->value), ParameterType::STRING) - ); - - default: - throw new RuntimeException( - "Unknown operator '{$criterion->operator}' for Depth Criterion handler." - ); - } - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/IsMainLocation.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/IsMainLocation.php deleted file mode 100644 index 5707303fb9..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/IsMainLocation.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; -use RuntimeException; - -/** - * Location main status criterion handler. - */ -class IsMainLocation extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\Location\IsMainLocation; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $idColumn = 't.node_id'; - $mainIdColumn = 't.main_node_id'; - - switch ($criterion->value[0]) { - case Criterion\Location\IsMainLocation::MAIN: - return $queryBuilder->expr()->eq( - $idColumn, - $mainIdColumn - ); - - case Criterion\Location\IsMainLocation::NOT_MAIN: - return $queryBuilder->expr()->neq( - $idColumn, - $mainIdColumn - ); - - default: - throw new RuntimeException( - "Unknown value '{$criterion->value[0]}' for IsMainLocation Criterion handler." - ); - } - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Priority.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Priority.php deleted file mode 100644 index 407eae0b7d..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Priority.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location; - -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; -use RuntimeException; - -/** - * Location priority criterion handler. - */ -class Priority extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\Location\Priority; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $column = 'priority'; - - switch ($criterion->operator) { - case Criterion\Operator::BETWEEN: - return $this->dbPlatform->getBetweenExpression( - $column, - $queryBuilder->createNamedParameter($criterion->value[0], ParameterType::STRING), - $queryBuilder->createNamedParameter($criterion->value[1], ParameterType::STRING) - ); - - case Criterion\Operator::GT: - case Criterion\Operator::GTE: - case Criterion\Operator::LT: - case Criterion\Operator::LTE: - $operatorFunction = $this->comparatorMap[$criterion->operator]; - - return $queryBuilder->expr()->$operatorFunction( - $column, - $queryBuilder->createNamedParameter(reset($criterion->value), ParameterType::STRING) - ); - - default: - throw new RuntimeException( - "Unknown operator '{$criterion->operator}' for Priority Criterion handler." - ); - } - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationId.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationId.php deleted file mode 100644 index 0dacdee2e3..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationId.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Location id criterion handler. - */ -class LocationId extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\LocationId; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $value = (array)$criterion->value; - - return $queryBuilder->expr()->in( - 't.node_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationRemoteId.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationRemoteId.php deleted file mode 100644 index b08d732c1e..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationRemoteId.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Location remote id criterion handler. - */ -class LocationRemoteId extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\LocationRemoteId; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $value = (array)$criterion->value; - - return $queryBuilder->expr()->in( - 't.remote_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/ParentLocationId.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/ParentLocationId.php deleted file mode 100644 index 75e822f7ee..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/ParentLocationId.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Parent location id criterion handler. - */ -class ParentLocationId extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\ParentLocationId; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $value = (array)$criterion->value; - - return $queryBuilder->expr()->in( - 't.parent_node_id', - $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) - ); - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Subtree.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Subtree.php deleted file mode 100644 index d92e21dcf7..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Subtree.php +++ /dev/null @@ -1,48 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler; - -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; - -/** - * Location subtree criterion handler. - */ -class Subtree extends CriterionHandler -{ - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $statements = []; - foreach ($criterion->value as $pattern) { - $statements[] = $queryBuilder->expr()->like( - 't.path_string', - $queryBuilder->createNamedParameter($pattern . '%', ParameterType::STRING) - ); - } - - return $queryBuilder->expr()->orX(...$statements); - } - - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\Subtree; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Visibility.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Visibility.php deleted file mode 100644 index 3c4774673a..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/CriterionHandler/Visibility.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler; - -use Doctrine\DBAL\ParameterType; -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; -use RuntimeException; - -/** - * Location visibility criterion handler. - */ -class Visibility extends CriterionHandler -{ - /** - * Check if this criterion handler accepts to handle the given criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * - * @return bool - */ - public function accept(Criterion $criterion) - { - return $criterion instanceof Criterion\Visibility; - } - - public function handle( - CriteriaConverter $converter, - QueryBuilder $queryBuilder, - Criterion $criterion, - array $languageSettings - ) { - $column = 't.is_invisible'; - - switch ($criterion->value[0]) { - case Criterion\Visibility::VISIBLE: - return $queryBuilder->expr()->eq( - $column, - $queryBuilder->createNamedParameter(0, ParameterType::INTEGER) - ); - - case Criterion\Visibility::HIDDEN: - return $queryBuilder->expr()->eq( - $column, - $queryBuilder->createNamedParameter(1, ParameterType::INTEGER) - ); - - default: - throw new RuntimeException( - "Unknown value '{$criterion->value[0]}' for Visibility Criterion handler." - ); - } - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/DoctrineDatabase.php deleted file mode 100644 index 25d4e5b38d..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,260 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway; - -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; -use Doctrine\DBAL\ParameterType; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter; -use eZ\Publish\Core\Search\Legacy\Content\Location\Gateway; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use RuntimeException; - -/** - * Location gateway implementation using the Doctrine database. - */ -final class DoctrineDatabase extends Gateway -{ - /** - * 2^30, since PHP_INT_MAX can cause overflows in DB systems, if PHP is run - * on 64 bit systems. - */ - public const MAX_LIMIT = 1073741824; - - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** @var \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter */ - private $criteriaConverter; - - /** @var \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter */ - private $sortClauseConverter; - - /** - * Language handler. - * - * @var \eZ\Publish\SPI\Persistence\Content\Language\Handler - */ - private $languageHandler; - - /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */ - private $dbPlatform; - - /** - * Construct from database handler. - * - * @throws \Doctrine\DBAL\DBALException - */ - public function __construct( - Connection $connection, - CriteriaConverter $criteriaConverter, - SortClauseConverter $sortClauseConverter, - LanguageHandler $languageHandler - ) { - $this->connection = $connection; - $this->dbPlatform = $connection->getDatabasePlatform(); - $this->criteriaConverter = $criteriaConverter; - $this->sortClauseConverter = $sortClauseConverter; - $this->languageHandler = $languageHandler; - } - - public function find( - Criterion $criterion, - $offset, - $limit, - array $sortClauses = null, - array $languageFilter = [], - $doCount = true - ): array { - $count = $doCount ? $this->getTotalCount($criterion, $languageFilter) : null; - - if (!$doCount && $limit === 0) { - throw new RuntimeException('Invalid query. Cannot disable count and request 0 items at the same time.'); - } - - if ($limit === 0 || ($count !== null && $count <= $offset)) { - return ['count' => $count, 'rows' => []]; - } - - $selectQuery = $this->connection->createQueryBuilder(); - $selectQuery->select( - 't.*', - 'c.language_mask', - 'c.initial_language_id' - ); - - if ($sortClauses !== null) { - $this->sortClauseConverter->applySelect($selectQuery, $sortClauses); - } - - $selectQuery - ->from('ezcontentobject_tree', 't') - ->innerJoin( - 't', - 'ezcontentobject', - 'c', - 't.contentobject_id = c.id' - ) - ->innerJoin( - 'c', - 'ezcontentobject_version', - 'v', - 'c.id = v.contentobject_id', - ); - - if ($sortClauses !== null) { - $this->sortClauseConverter->applyJoin($selectQuery, $sortClauses, $languageFilter); - } - - $selectQuery->where( - $this->criteriaConverter->convertCriteria($selectQuery, $criterion, $languageFilter), - $selectQuery->expr()->eq( - 'c.status', - //ContentInfo::STATUS_PUBLISHED - $selectQuery->createNamedParameter(1, ParameterType::INTEGER) - ), - $selectQuery->expr()->eq( - 'v.status', - //VersionInfo::STATUS_PUBLISHED - $selectQuery->createNamedParameter(1, ParameterType::INTEGER) - ), - $selectQuery->expr()->neq( - 't.depth', - $selectQuery->createNamedParameter(0, ParameterType::INTEGER) - ) - ); - - // If not main-languages query - if (!empty($languageFilter['languages'])) { - $selectQuery->andWhere( - $selectQuery->expr()->gt( - $this->dbPlatform->getBitAndComparisonExpression( - 'c.language_mask', - $selectQuery->createNamedParameter( - $this->getLanguageMask($languageFilter), - ParameterType::INTEGER - ) - ), - $selectQuery->createNamedParameter(0, ParameterType::INTEGER) - ) - ); - } - - if ($sortClauses !== null) { - $this->sortClauseConverter->applyOrderBy($selectQuery); - } - - $selectQuery->setMaxResults($limit); - $selectQuery->setFirstResult($offset); - - $statement = $selectQuery->execute(); - - return [ - 'count' => $count, - 'rows' => $statement->fetchAll(FetchMode::ASSOCIATIVE), - ]; - } - - /** - * Returns total results count for $criterion and $sortClauses. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * @param array $languageFilter - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - private function getTotalCount(Criterion $criterion, array $languageFilter): int - { - $query = $this->connection->createQueryBuilder(); - $query - ->select($this->dbPlatform->getCountExpression('*')) - ->from('ezcontentobject_tree', 't') - ->innerJoin( - 't', - ContentGateway::CONTENT_ITEM_TABLE, - 'c', - 't.contentobject_id = c.id' - ) - ->innerJoin( - 'c', - ContentGateway::CONTENT_VERSION_TABLE, - 'v', - 'c.id = v.contentobject_id' - ); - - $query->where( - $this->criteriaConverter->convertCriteria($query, $criterion, $languageFilter), - $query->expr()->eq( - 'c.status', - //ContentInfo::STATUS_PUBLISHED - $query->createNamedParameter(1, ParameterType::INTEGER) - ), - $query->expr()->eq( - 'v.status', - //VersionInfo::STATUS_PUBLISHED - $query->createNamedParameter(1, ParameterType::INTEGER) - ), - $query->expr()->neq( - 't.depth', - $query->createNamedParameter(0, ParameterType::INTEGER) - ) - ); - - // If not main-languages query - if (!empty($languageFilter['languages'])) { - $query->andWhere( - $query->expr()->gt( - $this->dbPlatform->getBitAndComparisonExpression( - 'c.language_mask', - $query->createNamedParameter( - $this->getLanguageMask($languageFilter), - ParameterType::INTEGER - ) - ), - $query->createNamedParameter(0, ParameterType::INTEGER) - ) - ); - } - - $statement = $query->execute(); - - return (int)$statement->fetchColumn(); - } - - /** - * Generates a language mask from the given $languageFilter. - * - * @param array $languageFilter - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - private function getLanguageMask(array $languageFilter): int - { - if (!isset($languageFilter['languages'])) { - $languageFilter['languages'] = []; - } - - if (!isset($languageFilter['useAlwaysAvailable'])) { - $languageFilter['useAlwaysAvailable'] = true; - } - - $mask = 0; - if ($languageFilter['useAlwaysAvailable']) { - $mask |= 1; - } - - foreach ($languageFilter['languages'] as $languageCode) { - $mask |= $this->languageHandler->loadByLanguageCode($languageCode)->id; - } - - return $mask; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/ExceptionConversion.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/ExceptionConversion.php deleted file mode 100644 index 45e77303ea..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/ExceptionConversion.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway; - -use Doctrine\DBAL\DBALException; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Search\Legacy\Content\Location\Gateway; -use PDOException; - -/** - * Base class for location gateways. - */ -class ExceptionConversion extends Gateway -{ - /** - * The wrapped gateway. - * - * @var \eZ\Publish\Core\Search\Legacy\Content\Location\Gateway - */ - protected $innerGateway; - - /** - * Creates a new exception conversion gateway around $innerGateway. - * - * @param \eZ\Publish\Core\Search\Legacy\Content\Location\Gateway $innerGateway - */ - public function __construct(Gateway $innerGateway) - { - $this->innerGateway = $innerGateway; - } - - public function find( - Criterion $criterion, - $offset = 0, - $limit = null, - array $sortClauses = null, - array $languageFilter = [], - $doCount = true - ): array { - try { - return $this->innerGateway->find($criterion, $offset, $limit, $sortClauses, $languageFilter, $doCount); - } catch (DBALException | PDOException $e) { - throw DatabaseException::wrap($e); - } - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Depth.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Depth.php deleted file mode 100644 index e1f70b4515..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Depth.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class Depth extends SortClauseHandler -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\Location\Depth; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf('t.depth AS %s', $column = $this->getSortColumnName($number)) - ); - - return [$column]; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Id.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Id.php deleted file mode 100644 index b51c91d462..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Id.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class Id extends SortClauseHandler -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\Location\Id; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - 't.node_id AS %s', - $column = $this->getSortColumnName($number) - ) - ); - - return [$column]; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/IsMainLocation.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/IsMainLocation.php deleted file mode 100644 index 195915a51b..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/IsMainLocation.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class IsMainLocation extends SortClauseHandler -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\Location\IsMainLocation; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - '%s AS %s', - $query->expr()->eq( - 't.node_id', - 't.main_node_id' - ), - $column = $this->getSortColumnName($number) - ) - ); - - return [$column]; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Path.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Path.php deleted file mode 100644 index b47b63f4d5..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Path.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class Path extends SortClauseHandler -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\Location\Path; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - 't.path_string AS %s', - $column = $this->getSortColumnName($number) - ) - ); - - return [$column]; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Priority.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Priority.php deleted file mode 100644 index 9f1906b6f3..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Priority.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class Priority extends SortClauseHandler -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\Location\Priority; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - 't.priority AS %s', - $column = $this->getSortColumnName($number) - ) - ); - - return [$column]; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Visibility.php b/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Visibility.php deleted file mode 100644 index d64584f706..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Visibility.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location; - -use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler; - -/** - * Content locator gateway implementation using the DoctrineDatabase. - */ -class Visibility extends SortClauseHandler -{ - /** - * Check if this sort clause handler accepts to handle the given sort clause. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause - * - * @return bool - */ - public function accept(SortClause $sortClause) - { - return $sortClause instanceof SortClause\Location\Visibility; - } - - public function applySelect( - QueryBuilder $query, - SortClause $sortClause, - int $number - ): array { - $query - ->addSelect( - sprintf( - 't.is_invisible AS %s', - $column = $this->getSortColumnName($number) - ) - ); - - return [$column]; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/WordIndexer/Gateway.php b/eZ/Publish/Core/Search/Legacy/Content/WordIndexer/Gateway.php deleted file mode 100644 index 9d3ca79654..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/WordIndexer/Gateway.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\WordIndexer; - -use eZ\Publish\Core\Search\Legacy\Content\FullTextData; -use eZ\Publish\SPI\Persistence\Content; - -/** - * The WordIndexer Gateway abstracts indexing of content full text data. - */ -abstract class Gateway -{ - /** - * Index search engine FullTextData objects corresponding to content object field values. - * - * @param \eZ\Publish\Core\Search\Legacy\Content\FullTextData $fullTextValue - */ - abstract public function index(FullTextData $fullTextValue); - - /** - * Remove whole content or a specific version from index. - * - * @param mixed $contentId - * @param mixed|null $versionId - */ - abstract public function remove($contentId, $versionId = null); - - /** - * Indexes an array of FullTextData objects. - * - * @param \eZ\Publish\Core\Search\Legacy\Content\FullTextData[] $fullTextBulkData - */ - abstract public function bulkIndex(array $fullTextBulkData); - - /** - * Remove entire search index. - */ - abstract public function purgeIndex(); -} diff --git a/eZ/Publish/Core/Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php b/eZ/Publish/Core/Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php deleted file mode 100644 index 2889c281f6..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php +++ /dev/null @@ -1,344 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway; - -use Doctrine\DBAL\Connection; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator; -use eZ\Publish\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Search\Legacy\Content\FullTextData; -use eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway; -use eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Repository\SearchIndex; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as SPITypeHandler; - -/** - * WordIndexer gateway implementation using the Doctrine database. - */ -class DoctrineDatabase extends Gateway -{ - /** - * Max acceptable by any DBMS INT value. - * - * Note: 2^31-1 seems to be the most reasonable value that should work in any setup. - */ - public const DB_INT_MAX = 2147483647; - - /** @var \Doctrine\DBAL\Connection */ - protected $connection; - - /** - * SPI Content Type Handler. - * - * Need this for being able to pick fields that are searchable. - * - * @var \eZ\Publish\SPI\Persistence\Content\Type\Handler - */ - protected $typeHandler; - - /** - * Transformation processor. - * - * Need this for being able to transform text to searchable value - * - * @var \eZ\Publish\Core\Persistence\TransformationProcessor - */ - protected $transformationProcessor; - - /** - * LegacySearchService. - * - * Need this for queries on ezsearch* tables - * - * @var \eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Repository\SearchIndex - */ - protected $searchIndex; - - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator */ - private $languageMaskGenerator; - - /** - * Full text search configuration options. - * - * @var array - */ - protected $fullTextSearchConfiguration; - - public function __construct( - Connection $connection, - SPITypeHandler $typeHandler, - TransformationProcessor $transformationProcessor, - SearchIndex $searchIndex, - MaskGenerator $languageMaskGenerator, - array $fullTextSearchConfiguration - ) { - $this->connection = $connection; - $this->typeHandler = $typeHandler; - $this->transformationProcessor = $transformationProcessor; - $this->searchIndex = $searchIndex; - $this->fullTextSearchConfiguration = $fullTextSearchConfiguration; - $this->languageMaskGenerator = $languageMaskGenerator; - } - - /** - * Index search engine full text data corresponding to content object field values. - * - * Ported from the legacy code - * - * @see https://github.com/ezsystems/ezpublish-legacy/blob/master/kernel/search/plugins/ezsearchengine/ezsearchengine.php#L45 - * - * @param \eZ\Publish\Core\Search\Legacy\Content\FullTextData $fullTextData - */ - public function index(FullTextData $fullTextData) - { - $indexArray = []; - $indexArrayOnlyWords = []; - $wordCount = 0; - $placement = 0; - - $this->connection->beginTransaction(); - // Remove previously indexed content if exists to avoid keeping in index removed field values - $this->remove($fullTextData->id); - foreach ($fullTextData->values as $fullTextValue) { - /** @var \eZ\Publish\Core\Search\Legacy\Content\FullTextValue $fullTextValue */ - if (is_numeric(trim($fullTextValue->value))) { - $integerValue = (int)$fullTextValue->value; - if ($integerValue > self::DB_INT_MAX) { - $integerValue = 0; - } - } else { - $integerValue = 0; - } - $text = $this->transformationProcessor->transform( - $fullTextValue->value, - !empty($fullTextValue->transformationRules) - ? $fullTextValue->transformationRules - : $this->fullTextSearchConfiguration['commands'] - ); - - // split by non-words - $wordArray = $fullTextValue->splitFlag ? preg_split('/\W/u', $text, -1, PREG_SPLIT_NO_EMPTY) : [$text]; - foreach ($wordArray as $word) { - if (trim($word) === '') { - continue; - } - // words stored in search index are limited to 150 characters - if (mb_strlen($word) > 150) { - $word = mb_substr($word, 0, 150); - } - $indexArray[] = [ - 'Word' => $word, - 'ContentClassAttributeID' => $fullTextValue->fieldDefinitionId, - 'identifier' => $fullTextValue->fieldDefinitionIdentifier, - 'integer_value' => $integerValue, - 'language_code' => $fullTextValue->languageCode, - 'is_main_and_always_available' => $fullTextValue->isMainAndAlwaysAvailable, - ]; - $indexArrayOnlyWords[$word] = 1; - ++$wordCount; - // if we have "www." before word than - // treat it as url and add additional entry to the index - if (mb_strtolower(mb_substr($word, 0, 4)) === 'www.') { - $additionalUrlWord = substr($word, 4); - $indexArray[] = [ - 'Word' => $additionalUrlWord, - 'ContentClassAttributeID' => $fullTextValue->fieldDefinitionId, - 'identifier' => $fullTextValue->fieldDefinitionIdentifier, - 'integer_value' => $integerValue, - 'language_code' => $fullTextValue->languageCode, - 'is_main_and_always_available' => $fullTextValue->isMainAndAlwaysAvailable, - ]; - $indexArrayOnlyWords[$additionalUrlWord] = 1; - ++$wordCount; - } - } - } - - $wordIDArray = $this->buildWordIDArray(array_keys($indexArrayOnlyWords)); - for ($arrayCount = 0; $arrayCount < $wordCount; $arrayCount += 1000) { - $placement = $this->indexWords( - $fullTextData, - array_slice($indexArray, $arrayCount, 1000), - $wordIDArray, - $placement - ); - } - $this->connection->commit(); - } - - /** - * Indexes an array of FullTextData objects. - * - * Note: on large amounts of data make sure to iterate with several calls to this function with - * a limited set of FullTextData objects. Amount you have memory for depends on server, size - * of FullTextData objects & PHP version. - * - * @param \eZ\Publish\Core\Search\Legacy\Content\FullTextData[] $fullTextBulkData - */ - public function bulkIndex(array $fullTextBulkData) - { - foreach ($fullTextBulkData as $fullTextData) { - $this->index($fullTextData); - } - } - - /** - * Remove whole content or a specific version from index. - * - * Ported from the legacy code - * - * @see https://github.com/ezsystems/ezpublish-legacy/blob/master/kernel/search/plugins/ezsearchengine/ezsearchengine.php#L386 - * - * @param mixed $contentId - * @param mixed|null $versionId - * - * @return bool - */ - public function remove($contentId, $versionId = null) - { - $doDelete = false; - $this->connection->beginTransaction(); - // fetch all the words and decrease the object count on all the words - $wordIDList = $this->searchIndex->getContentObjectWords($contentId); - if (count($wordIDList) > 0) { - $this->searchIndex->decrementWordObjectCount($wordIDList); - $doDelete = true; - } - if ($doDelete) { - $this->searchIndex->deleteWordsWithoutObjects(); - $this->searchIndex->deleteObjectWordsLink($contentId); - } - $this->connection->commit(); - - return true; - } - - /** - * Remove entire search index. - */ - public function purgeIndex() - { - $this->searchIndex->purge(); - } - - /** - * Index wordIndex. - * - * Ported from the legacy code - * - * @see https://github.com/ezsystems/ezpublish-legacy/blob/master/kernel/search/plugins/ezsearchengine/ezsearchengine.php#L255 - * - * @param \eZ\Publish\Core\Search\Legacy\Content\FullTextData $fullTextData - * @param array $indexArray - * @param array $wordIDArray - * @param int $placement - * - * @return int last placement - */ - private function indexWords(FullTextData $fullTextData, array $indexArray, array $wordIDArray, $placement = 0) - { - $contentId = $fullTextData->id; - - $prevWordId = 0; - - for ($i = 0; $i < count($indexArray); ++$i) { - $indexWord = $indexArray[$i]['Word']; - $indexWord = $this->transformationProcessor->transformByGroup($indexWord, 'lowercase'); - $contentFieldId = $indexArray[$i]['ContentClassAttributeID']; - $identifier = $indexArray[$i]['identifier']; - $integerValue = $indexArray[$i]['integer_value']; - $languageCode = $indexArray[$i]['language_code']; - $wordId = $wordIDArray[$indexWord]; - $isMainAndAlwaysAvailable = $indexArray[$i]['is_main_and_always_available']; - $languageMask = $this->languageMaskGenerator->generateLanguageMaskFromLanguageCodes( - [$languageCode], - $isMainAndAlwaysAvailable - ); - - if (isset($indexArray[$i + 1])) { - $nextIndexWord = $indexArray[$i + 1]['Word']; - $nextIndexWord = $this->transformationProcessor->transformByGroup($nextIndexWord, 'lowercase'); - $nextWordId = $wordIDArray[$nextIndexWord]; - } else { - $nextWordId = 0; - } - $frequency = 0; - $this->searchIndex->addObjectWordLink( - $wordId, - $contentId, - $frequency, - $placement, - $nextWordId, - $prevWordId, - $fullTextData->contentTypeId, - $contentFieldId, - $fullTextData->published, - $fullTextData->sectionId, - $identifier, - $integerValue, - $languageMask - ); - $prevWordId = $wordId; - ++$placement; - } - - return $placement; - } - - /** - * Build WordIDArray and update ezsearch_word table. - * - * Ported from the legacy code - * - * @see https://github.com/ezsystems/ezpublish-legacy/blob/master/kernel/search/plugins/ezsearchengine/ezsearchengine.php#L155 - * - * @param array $indexArrayOnlyWords words for object to add - * - * @return array wordIDArray - */ - private function buildWordIDArray(array $indexArrayOnlyWords) - { - $wordCount = count($indexArrayOnlyWords); - $wordIDArray = []; - $wordArray = []; - - // store the words in the index and remember the ID - $this->connection->beginTransaction(); - for ($arrayCount = 0; $arrayCount < $wordCount; $arrayCount += 500) { - // Fetch already indexed words from database - $wordArrayChuck = array_slice($indexArrayOnlyWords, $arrayCount, 500); - $wordRes = $this->searchIndex->getWords($wordArrayChuck); - - // Build a has of the existing words - $wordResCount = count($wordRes); - $existingWordArray = []; - for ($i = 0; $i < $wordResCount; ++$i) { - $wordIDArray[] = $wordRes[$i]['id']; - $existingWordArray[] = $wordRes[$i]['word']; - $wordArray[$wordRes[$i]['word']] = $wordRes[$i]['id']; - } - - // Update the object count of existing words by one - if (count($wordIDArray) > 0) { - $this->searchIndex->incrementWordObjectCount($wordIDArray); - } - - // Insert if there is any news words - $newWordArray = array_diff($wordArrayChuck, $existingWordArray); - if (count($newWordArray) > 0) { - $this->searchIndex->addWords($newWordArray); - $newWordRes = $this->searchIndex->getWords($newWordArray); - $newWordCount = count($newWordRes); - for ($i = 0; $i < $newWordCount; ++$i) { - $wordLowercase = $this->transformationProcessor->transformByGroup($newWordRes[$i]['word'], 'lowercase'); - $wordArray[$wordLowercase] = $newWordRes[$i]['id']; - } - } - } - $this->connection->commit(); - - return $wordArray; - } -} diff --git a/eZ/Publish/Core/Search/Legacy/Tests/Content/AbstractTestCase.php b/eZ/Publish/Core/Search/Legacy/Tests/Content/AbstractTestCase.php deleted file mode 100644 index ab25ca43a7..0000000000 --- a/eZ/Publish/Core/Search/Legacy/Tests/Content/AbstractTestCase.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Legacy\Tests\Content; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase as ContentTypeGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler as ContentTypeHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper as ContentTypeMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler as ContentTypeUpdateHandler; -use eZ\Publish\Core\Persistence\Legacy\Tests\Content\LanguageAwareTestCase; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as SPIContentTypeHandler; -use Ibexa\Core\Persistence\Legacy\Content\Mapper\ResolveVirtualFieldSubscriber; -use Symfony\Component\EventDispatcher\EventDispatcher; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; - -/** - * Abstract test suite for legacy search. - */ -class AbstractTestCase extends LanguageAwareTestCase -{ - /** @var bool */ - private static $databaseInitialized = false; - - /** - * Field registry mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry - */ - private $converterRegistry; - - /** @var \eZ\Publish\SPI\Persistence\Content\Type\Handler */ - private $contentTypeHandler; - - /** - * Only set up once for these read only tests on a large fixture. - * - * Skipping the reset-up, since setting up for these tests takes quite some - * time, which is not required to spent, since we are only reading from the - * database anyways. - */ - protected function setUp(): void - { - if (!self::$databaseInitialized) { - parent::setUp(); - $this->insertDatabaseFixture(__DIR__ . '/../_fixtures/full_dump.php'); - self::$databaseInitialized = true; - } - } - - /** - * Assert that the elements are. - */ - protected function assertSearchResults($expectedIds, $searchResult) - { - $ids = $this->getIds($searchResult); - $this->assertEquals($expectedIds, $ids); - } - - protected function getIds($searchResult) - { - $ids = array_map( - static function ($hit) { - return $hit->valueObject->id; - }, - $searchResult->searchHits - ); - - sort($ids); - - return $ids; - } - - /** - * @throws \Doctrine\DBAL\DBALException - */ - protected function getContentTypeHandler(): SPIContentTypeHandler - { - if (!isset($this->contentTypeHandler)) { - $this->contentTypeHandler = new ContentTypeHandler( - new ContentTypeGateway( - $this->getDatabaseConnection(), - $this->getSharedGateway(), - $this->getLanguageMaskGenerator() - ), - new ContentTypeMapper($this->getConverterRegistry(), $this->getLanguageMaskGenerator()), - $this->createMock(ContentTypeUpdateHandler::class) - ); - } - - return $this->contentTypeHandler; - } - - protected function getConverterRegistry() - { - if (!isset($this->converterRegistry)) { - $this->converterRegistry = new ConverterRegistry( - [ - 'ezdatetime' => new Converter\DateAndTimeConverter(), - 'ezinteger' => new Converter\IntegerConverter(), - 'ezstring' => new Converter\TextLineConverter(), - 'ezfloat' => new Converter\FloatConverter(), - 'ezurl' => new Converter\UrlConverter(), - 'ezboolean' => new Converter\CheckboxConverter(), - 'ezkeyword' => new Converter\KeywordConverter(), - 'ezauthor' => new Converter\AuthorConverter(), - 'ezimage' => new Converter\NullConverter(), - 'ezmultioption' => new Converter\NullConverter(), - ] - ); - } - - return $this->converterRegistry; - } - - protected function getEventDispatcher(): EventDispatcherInterface - { - $eventDispatcher = new EventDispatcher(); - $eventDispatcher->addSubscriber( - new ResolveVirtualFieldSubscriber( - $this->getConverterRegistry(), - $this->createMock(StorageRegistry::class), - $this->createMock(Gateway::class) - ) - ); - - return $eventDispatcher; - } -} diff --git a/eZ/Publish/Core/Search/Tests/Common/LocationEventSubscriber/LocationEventSubscriberTest.php b/eZ/Publish/Core/Search/Tests/Common/LocationEventSubscriber/LocationEventSubscriberTest.php deleted file mode 100644 index a74a360642..0000000000 --- a/eZ/Publish/Core/Search/Tests/Common/LocationEventSubscriber/LocationEventSubscriberTest.php +++ /dev/null @@ -1,128 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\Core\Search\Tests\Common\LocationEventSubscriber; - -use eZ\Publish\API\Repository\Events\Location\CreateLocationEvent; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Search\Common\EventSubscriber\LocationEventSubscriber; -use eZ\Publish\Core\Search\Legacy\Content\Handler as SearchHandler; -use eZ\Publish\SPI\Persistence\Content as SPIContent; -use eZ\Publish\SPI\Persistence\Content\ContentInfo as SPIContentInfo; -use eZ\Publish\SPI\Persistence\Content\Handler as ContentHandler; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as LocationHandler; -use eZ\Publish\SPI\Persistence\Content\VersionInfo as SPIVersionInfo; -use eZ\Publish\SPI\Persistence\Handler as PersistenceHandler; -use PHPUnit\Framework\TestCase; - -final class LocationEventSubscriberTest extends TestCase -{ - private const EXAMPLE_LOCATION_ID = 54; - private const EXAMPLE_CONTENT_ID = 56; - private const EXAMPLE_VERSION_NO = 3; - - /** @var \eZ\Publish\Core\Search\Legacy\Content\Handler|\PHPUnit\Framework\MockObject\MockObject */ - private $searchHandler; - - /** @var \eZ\Publish\SPI\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject */ - private $persistenceHandler; - - /** @var \eZ\Publish\Core\Search\Common\EventSubscriber\LocationEventSubscriber */ - private $subscriber; - - protected function setUp(): void - { - $this->searchHandler = $this->createMock(SearchHandler::class); - $this->persistenceHandler = $this->createMock(PersistenceHandler::class); - - $this->subscriber = new LocationEventSubscriber( - $this->searchHandler, - $this->persistenceHandler - ); - } - - public function testOnCreateLocation(): void - { - $spiLocation = $this->getExampleSPILocation(); - $spiContent = $this->getExampleSPIContent(); - - $this->configurePersistenceHandler($spiContent, $spiLocation); - - $this->searchHandler->expects($this->atLeastOnce())->method('indexContent')->with($spiContent); - $this->searchHandler->expects($this->atLeastOnce())->method('indexLocation')->with($spiLocation); - - $this->subscriber->onCreateLocation( - new CreateLocationEvent( - $this->getExampleAPILocation(), - $this->getExampleAPIContentInfo(), - new LocationCreateStruct() - ) - ); - } - - private function configurePersistenceHandler(SPIContent $spiContent, SPILocation $spiLocation): void - { - $contentHandler = $this->createMock(ContentHandler::class); - $contentHandler - ->method('loadContentInfo') - ->with(self::EXAMPLE_CONTENT_ID) - ->willReturn($this->getExampleSPIContentInfo()); - - $contentHandler - ->method('load') - ->with(self::EXAMPLE_CONTENT_ID, self::EXAMPLE_VERSION_NO) - ->willReturn($spiContent); - - $locationHandler = $this->createMock(LocationHandler::class); - $locationHandler->method('load')->with(self::EXAMPLE_LOCATION_ID)->willReturn($spiLocation); - - $this->persistenceHandler->method('locationHandler')->willReturn($locationHandler); - $this->persistenceHandler->method('contentHandler')->willReturn($contentHandler); - } - - private function getExampleAPIContentInfo(): ContentInfo - { - return new ContentInfo([ - 'id' => self::EXAMPLE_CONTENT_ID, - 'currentVersionNo' => self::EXAMPLE_VERSION_NO, - ]); - } - - private function getExampleAPILocation(): Location - { - return new Location(['id' => self::EXAMPLE_LOCATION_ID]); - } - - private function getExampleSPILocation(): SPILocation - { - return new SPILocation([ - 'id' => self::EXAMPLE_LOCATION_ID, - ]); - } - - private function getExampleSPIContent(): SPIContent - { - return new SPIContent([ - 'versionInfo' => new SPIVersionInfo([ - 'id' => self::EXAMPLE_CONTENT_ID, - 'versionNo' => self::EXAMPLE_VERSION_NO, - ]), - ]); - } - - private function getExampleSPIContentInfo(): SPIContentInfo - { - return new SPIContentInfo([ - 'id' => self::EXAMPLE_CONTENT_ID, - 'currentVersionNo' => self::EXAMPLE_VERSION_NO, - ]); - } -} diff --git a/eZ/Publish/Core/Search/Tests/TestCase.php b/eZ/Publish/Core/Search/Tests/TestCase.php deleted file mode 100644 index 84e42b50c9..0000000000 --- a/eZ/Publish/Core/Search/Tests/TestCase.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\Core\Search\Tests; - -use PHPUnit\Framework\TestCase as BaseTestCase; - -/** - * Base test case for Search Engine related tests. - */ -abstract class TestCase extends BaseTestCase -{ -} diff --git a/eZ/Publish/Core/settings/containerBuilder.php b/eZ/Publish/Core/settings/containerBuilder.php deleted file mode 100644 index 5c57826223..0000000000 --- a/eZ/Publish/Core/settings/containerBuilder.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ConsoleCommandPass; -use eZ\Publish\API\Repository\Tests\Container\Compiler\SetAllServicesPublicPass; -use eZ\Publish\Core\Base\Container\Compiler; -use Symfony\Component\Cache\Adapter\RedisAdapter; -use Symfony\Component\Config\FileLocator; -use Symfony\Component\Config\Resource\FileResource; -use Symfony\Component\DependencyInjection\Compiler\PassConfig; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -use Symfony\Component\DependencyInjection\Reference; - -if (!isset($installDir)) { - throw new \RuntimeException('$installDir not provided to ' . __FILE__); -} - -$containerBuilder = new ContainerBuilder(); - -// Track current file for changes -$containerBuilder->addResource(new FileResource(__FILE__)); - -$settingsPath = $installDir . '/eZ/Publish/Core/settings/'; -$loader = new YamlFileLoader($containerBuilder, new FileLocator($settingsPath)); - -$loader->load('fieldtype_external_storages.yml'); -$loader->load('fieldtype_services.yml'); -$loader->load('fieldtypes.yml'); -$loader->load('indexable_fieldtypes.yml'); -$loader->load('io.yml'); -$loader->load('repository.yml'); -$loader->load('repository/inner.yml'); -$loader->load('repository/event.yml'); -$loader->load('repository/siteaccessaware.yml'); -$loader->load('repository/autowire.yml'); -$loader->load('roles.yml'); -$loader->load('storage_engines/common.yml'); -$loader->load('storage_engines/cache.yml'); -$loader->load('storage_engines/legacy.yml'); -$loader->load('storage_engines/shortcuts.yml'); -$loader->load('settings.yml'); -$loader->load('utils.yml'); -$loader->load('tests/common.yml'); -$loader->load('policies.yml'); -$loader->load('events.yml'); -$loader->load('thumbnails.yml'); -$loader->load('content_location_mapper.yml'); - -// Cache settings (takes same env variables as ezplatform does, only supports "singleredis" setup) -if (getenv('CUSTOM_CACHE_POOL') === 'singleredis') { - /* - * Symfony\Component\Cache\Adapter\RedisAdapter - * @param \Redis|\RedisArray|\RedisCluster|\Predis\Client $redisClient - * public function __construct($redisClient, $namespace = '', $defaultLifetime = 0) - * - * $redis = new \Redis(); - * $redis->connect('127.0.0.1', 6379, 2.5); - */ - $containerBuilder - ->register('ezpublish.cache_pool.driver.redis', 'Redis') - ->addMethodCall('connect', [(getenv('CACHE_HOST') ?: '127.0.0.1'), 6379, 2.5]); - - $containerBuilder - ->register('ezpublish.cache_pool.driver', RedisAdapter::class) - ->setArguments([new Reference('ezpublish.cache_pool.driver.redis'), '', 120]); -} - -$containerBuilder->setParameter('ezpublish.kernel.root_dir', $installDir); - -$containerBuilder->addCompilerPass(new Compiler\FieldTypeRegistryPass(), PassConfig::TYPE_OPTIMIZE); -$containerBuilder->addCompilerPass(new Compiler\Persistence\FieldTypeRegistryPass(), PassConfig::TYPE_OPTIMIZE); - -$containerBuilder->addCompilerPass(new Compiler\Storage\ExternalStorageRegistryPass()); -$containerBuilder->addCompilerPass(new Compiler\Storage\Legacy\FieldValueConverterRegistryPass()); -$containerBuilder->addCompilerPass(new Compiler\Storage\Legacy\RoleLimitationConverterPass()); - -$containerBuilder->addCompilerPass(new Compiler\Search\Legacy\CriteriaConverterPass()); -$containerBuilder->addCompilerPass(new Compiler\Search\Legacy\CriterionFieldValueHandlerRegistryPass()); -$containerBuilder->addCompilerPass(new Compiler\Search\Legacy\SortClauseConverterPass()); - -$containerBuilder->addCompilerPass(new ConsoleCommandPass()); - -// -// Symfony 4 makes services private by default. Test cases are not prepared for this. -// This is a simple workaround to override services as public. -// -$containerBuilder->addCompilerPass(new SetAllServicesPublicPass()); - -return $containerBuilder; diff --git a/eZ/Publish/Core/settings/events.yml b/eZ/Publish/Core/settings/events.yml deleted file mode 100644 index 89ec3dd9ab..0000000000 --- a/eZ/Publish/Core/settings/events.yml +++ /dev/null @@ -1,8 +0,0 @@ -services: - _defaults: - autowire: true - autoconfigure: true - public: false - - eZ\Publish\Core\Repository\EventSubscriber\: - resource: '../Repository/EventSubscriber/*' diff --git a/eZ/Publish/Core/settings/fieldtype_external_storages.yml b/eZ/Publish/Core/settings/fieldtype_external_storages.yml deleted file mode 100644 index 862a160adf..0000000000 --- a/eZ/Publish/Core/settings/fieldtype_external_storages.yml +++ /dev/null @@ -1,65 +0,0 @@ -services: - ezpublish.fieldType.ezbinaryfile.externalStorage: - class: eZ\Publish\Core\FieldType\BinaryFile\BinaryFileStorage - arguments: - $gateway: '@ezpublish.fieldType.ezbinaryfile.storage_gateway' - $ioService: '@ezpublish.fieldType.ezbinaryfile.io_service' - $pathGenerator: '@ezpublish.fieldType.ezbinaryfile.pathGenerator' - $mimeTypeDetector: '@ezpublish.core.io.mimeTypeDetector' - $fileExtensionBlackListValidator: '@ezpublish.fieldType.validator.black_list' - tags: - - {name: ezplatform.field_type.external_storage_handler, alias: ezbinaryfile} - - ezpublish.fieldType.ezimage.externalStorage: - class: eZ\Publish\Core\FieldType\Image\ImageStorage - arguments: - $gateway: '@ezpublish.fieldType.ezimage.storage_gateway' - $ioService: '@ezpublish.fieldType.ezimage.io_service' - $pathGenerator: '@ezpublish.fieldType.ezimage.pathGenerator' - $imageSizeMetadataHandler: '@ezpublish.fieldType.metadataHandler.imagesize' - $deprecationWarner: '@ezpublish.utils.deprecation_warner' - $aliasCleaner: '@eZ\Publish\Core\FieldType\Image\AliasCleanerInterface' - $filePathNormalizer: '@eZ\Publish\Core\IO\FilePathNormalizerInterface' - $fileExtensionBlackListValidator: '@ezpublish.fieldType.validator.black_list' - tags: - - {name: ezplatform.field_type.external_storage_handler, alias: ezimage} - - ezpublish.fieldType.ezkeyword.externalStorage: - class: eZ\Publish\Core\FieldType\Keyword\KeywordStorage - arguments: ["@ezpublish.fieldType.ezkeyword.storage_gateway"] - tags: - - {name: ezplatform.field_type.external_storage_handler, alias: ezkeyword} - - ezpublish.fieldType.ezmedia.externalStorage: - class: eZ\Publish\Core\FieldType\Media\MediaStorage - arguments: - $gateway: '@ezpublish.fieldType.ezmedia.storage_gateway' - $ioService: '@ezpublish.fieldType.ezbinaryfile.io_service' - $pathGenerator: '@ezpublish.fieldType.ezbinaryfile.pathGenerator' - $mimeTypeDetector: '@ezpublish.core.io.mimeTypeDetector' - $fileExtensionBlackListValidator: '@ezpublish.fieldType.validator.black_list' - tags: - - {name: ezplatform.field_type.external_storage_handler, alias: ezmedia} - - ezpublish.fieldType.ezurl.externalStorage: - class: eZ\Publish\Core\FieldType\Url\UrlStorage - arguments: - - "@ezpublish.fieldType.ezurl.storage_gateway" - - "@?logger" - tags: - - {name: ezplatform.field_type.external_storage_handler, alias: ezurl} - - ezpublish.fieldType.ezgmaplocation.externalStorage: - class: eZ\Publish\Core\FieldType\MapLocation\MapLocationStorage - arguments: ["@ezpublish.fieldType.externalStorageHandler.ezgmaplocation.gateway"] - tags: - - {name: ezplatform.field_type.external_storage_handler, alias: ezgmaplocation} - - ezpublish.fieldType.ezuser.externalStorage: - class: eZ\Publish\Core\FieldType\User\UserStorage - arguments: ["@ezpublish.fieldType.ezuser.storage_gateway"] - tags: - - {name: ezplatform.field_type.external_storage_handler, alias: ezuser} - - ezpublish.fieldType.metadataHandler.imagesize: - class: eZ\Publish\Core\IO\MetadataHandler\ImageSize diff --git a/eZ/Publish/Core/settings/fieldtype_services.yml b/eZ/Publish/Core/settings/fieldtype_services.yml deleted file mode 100644 index 2eccd48a68..0000000000 --- a/eZ/Publish/Core/settings/fieldtype_services.yml +++ /dev/null @@ -1,73 +0,0 @@ -services: - # Deprecated since 5.4. Use ezpublish.fieldType.ezimage.io_service. - ezpublish.fieldType.ezimage.io: - alias: ezpublish.fieldType.ezimage.io_service - - # Custom IOService with a proxy that handles the legacy prefix (images-versioned) - ezpublish.fieldType.ezimage.io_service: - class: eZ\Publish\Core\FieldType\Image\IO\Legacy - arguments: - - "@ezpublish.fieldType.ezimage.io_service.published" - - "@ezpublish.fieldType.ezimage.io_service.draft" - - "@ezpublish.fieldType.ezimage.io_service.options_provider" - - ezpublish.fieldType.ezimage.io_service.options_provider: - class: eZ\Publish\Core\FieldType\Image\IO\OptionsProvider - arguments: - $configResolver: '@ezpublish.config.resolver' - - ezpublish.fieldType.ezimage.io_service.published: - parent: ezpublish.core.io.service - calls: - - [ setPrefix, [ "%image_storage_prefix%" ] ] - - # Used to manipulate images with a legacy 'images-versioned' path - ezpublish.fieldType.ezimage.io_service.draft: - parent: ezpublish.core.io.service - calls: - - [ setPrefix, [ "%image_draft_storage_prefix%" ] ] - - ezpublish.fieldType.ezimage.pathGenerator: - class: eZ\Publish\Core\FieldType\Image\PathGenerator\LegacyPathGenerator - - eZ\Publish\Core\FieldType\Image\NullAliasCleaner: ~ - eZ\Publish\Core\FieldType\Image\AliasCleanerInterface: '@eZ\Publish\Core\FieldType\Image\NullAliasCleaner' - - # BinaryFile - ezpublish.fieldType.ezbinaryfile.io_service: - parent: ezpublish.core.io.service - calls: - - [ setPrefix, [ "%binaryfile_storage_prefix%" ] ] - - ezpublish.fieldType.ezbinaryfile.pathGenerator: - class: eZ\Publish\Core\FieldType\BinaryBase\PathGenerator\LegacyPathGenerator - - ezpublish.fieldType.validator.black_list: - class: eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator - arguments: - - '@ezpublish.config.resolver' - - ezpublish.fieldType.validator.image: - class: eZ\Publish\Core\FieldType\Validator\ImageValidator - - # Symfony 3.4+ service definitions: - eZ\Publish\Core\FieldType\ImageAsset\AssetMapper: - arguments: - $contentService: '@ezpublish.api.service.content' - $locationService: '@ezpublish.api.service.location' - $contentTypeService: '@ezpublish.api.service.content_type' - $configResolver: '@ezpublish.config.resolver' - - eZ\Publish\Core\FieldType\FieldTypeRegistry: ~ - - eZ\Publish\Core\Repository\User\PasswordHashService: ~ - - eZ\Publish\API\Repository\PasswordHashService: - alias: eZ\Publish\Core\Repository\User\PasswordHashService - - eZ\Publish\Core\Repository\User\PasswordValidator: - arguments: - $passwordHashService: '@eZ\Publish\Core\Repository\User\PasswordHashService' - - eZ\Publish\Core\Repository\User\PasswordValidatorInterface: - alias: eZ\Publish\Core\Repository\User\PasswordValidator diff --git a/eZ/Publish/Core/settings/fieldtypes.yml b/eZ/Publish/Core/settings/fieldtypes.yml deleted file mode 100644 index 552179d3c6..0000000000 --- a/eZ/Publish/Core/settings/fieldtypes.yml +++ /dev/null @@ -1,556 +0,0 @@ -parameters: - ezpublish.fieldType.ezcountry.data: - AF: {Name: "Afghanistan", Alpha2: "AF", Alpha3: "AFG", IDC: "93"} - AX: {Name: "Åland", Alpha2: "AX", Alpha3: "ALA", IDC: "358"} - AL: {Name: "Albania", Alpha2: "AL", Alpha3: "ALB", IDC: "355"} - DZ: {Name: "Algeria", Alpha2: "DZ", Alpha3: "DZA", IDC: "213"} - AS: {Name: "American Samoa", Alpha2: "AS", Alpha3: "ASM", IDC: "1684"} - AD: {Name: "Andorra", Alpha2: "AD", Alpha3: "AND", IDC: "376"} - AO: {Name: "Angola", Alpha2: "AO", Alpha3: "AGO", IDC: "244"} - AI: {Name: "Anguilla", Alpha2: "AI", Alpha3: "AIA", IDC: "1264"} - AQ: {Name: "Antarctica", Alpha2: "AQ", Alpha3: "ATA", IDC: "672"} - AG: {Name: "Antigua and Barbuda", Alpha2: "AG", Alpha3: "ATG", IDC: "1268"} - AR: {Name: "Argentina", Alpha2: "AR", Alpha3: "ARG", IDC: "54"} - AM: {Name: "Armenia", Alpha2: "AM", Alpha3: "ARM", IDC: "374"} - AW: {Name: "Aruba", Alpha2: "AW", Alpha3: "ABW", IDC: "297"} - AU: {Name: "Australia", Alpha2: "AU", Alpha3: "AUS", IDC: "61"} - AT: {Name: "Austria", Alpha2: "AT", Alpha3: "AUT", IDC: "43"} - AZ: {Name: "Azerbaijan", Alpha2: "AZ", Alpha3: "AZE", IDC: "994"} - BS: {Name: "Bahamas", Alpha2: "BS", Alpha3: "BHS", IDC: "1242"} - BH: {Name: "Bahrain", Alpha2: "BH", Alpha3: "BHR", IDC: "973"} - BD: {Name: "Bangladesh", Alpha2: "BD", Alpha3: "BGD", IDC: "880"} - BB: {Name: "Barbados", Alpha2: "BB", Alpha3: "BRB", IDC: "1246"} - BY: {Name: "Belarus", Alpha2: "BY", Alpha3: "BLR", IDC: "375"} - BE: {Name: "Belgium", Alpha2: "BE", Alpha3: "BEL", IDC: "32"} - BZ: {Name: "Belize", Alpha2: "BZ", Alpha3: "BLZ", IDC: "501"} - BJ: {Name: "Benin", Alpha2: "BJ", Alpha3: "BEN", IDC: "229"} - BM: {Name: "Bermuda", Alpha2: "BM", Alpha3: "BMU", IDC: "1441"} - BT: {Name: "Bhutan", Alpha2: "BT", Alpha3: "BTN", IDC: "975"} - BO: {Name: "Bolivia", Alpha2: "BO", Alpha3: "BOL", IDC: "591"} - BA: {Name: "Bosnia and Herzegovina", Alpha2: "BA", Alpha3: "BIH", IDC: "387"} - BW: {Name: "Botswana", Alpha2: "BW", Alpha3: "BWA", IDC: "267"} - BV: {Name: "Bouvet Island", Alpha2: "BV", Alpha3: "BVT", IDC: "47"} - BR: {Name: "Brazil", Alpha2: "BR", Alpha3: "BRA", IDC: "55"} - IO: {Name: "British Indian Ocean Territory", Alpha2: "IO", Alpha3: "IOT", IDC: "246"} - BN: {Name: "Brunei Darussalam", Alpha2: "BN", Alpha3: "BRN", IDC: "673"} - BG: {Name: "Bulgaria", Alpha2: "BG", Alpha3: "BGR", IDC: "359"} - BF: {Name: "Burkina Faso", Alpha2: "BF", Alpha3: "BFA", IDC: "226"} - BI: {Name: "Burundi", Alpha2: "BI", Alpha3: "BDI", IDC: "257"} - KH: {Name: "Cambodia", Alpha2: "KH", Alpha3: "KHM", IDC: "855"} - CM: {Name: "Cameroon", Alpha2: "CM", Alpha3: "CMR", IDC: "237"} - CA: {Name: "Canada", Alpha2: "CA", Alpha3: "CAN", IDC: "1"} - CV: {Name: "Cape Verde", Alpha2: "CV", Alpha3: "CPV", IDC: "238"} - KY: {Name: "Cayman Islands", Alpha2: "KY", Alpha3: "CYM", IDC: "1345"} - CF: {Name: "Central African Republic", Alpha2: "CF", Alpha3: "CAF", IDC: "236"} - TD: {Name: "Chad", Alpha2: "TD", Alpha3: "TCD", IDC: "235"} - CL: {Name: "Chile", Alpha2: "CL", Alpha3: "CHL", IDC: "56"} - CN: {Name: "China", Alpha2: "CN", Alpha3: "CHN", IDC: "86"} - CX: {Name: "Christmas Island", Alpha2: "CX", Alpha3: "CXR", IDC: "61"} - CC: {Name: "Cocos (Keeling) Islands", Alpha2: "CC", Alpha3: "CCK", IDC: "61"} - CO: {Name: "Colombia", Alpha2: "CO", Alpha3: "COL", IDC: "57"} - KM: {Name: "Comoros", Alpha2: "KM", Alpha3: "COM", IDC: "269"} - CG: {Name: "Congo", Alpha2: "CG", Alpha3: "COG", IDC: "242"} - CD: {Name: "Congo, The Democratic Republic Of The", Alpha2: "CD", Alpha3: "COD", IDC: "243"} - CK: {Name: "Cook Islands", Alpha2: "CK", Alpha3: "COK", IDC: "682"} - CR: {Name: "Costa Rica", Alpha2: "CR", Alpha3: "CRI", IDC: "506"} - CI: {Name: "Côte d'Ivoire", Alpha2: "CI", Alpha3: "CIV", IDC: "225"} - HR: {Name: "Croatia", Alpha2: "HR", Alpha3: "HRV", IDC: "385"} - CU: {Name: "Cuba", Alpha2: "CU", Alpha3: "CUB", IDC: "53"} - CY: {Name: "Cyprus", Alpha2: "CY", Alpha3: "CYP", IDC: "357"} - CZ: {Name: "Czech Republic", Alpha2: "CZ", Alpha3: "CZE", IDC: "420"} - DK: {Name: "Denmark", Alpha2: "DK", Alpha3: "DNK", IDC: "45"} - DJ: {Name: "Djibouti", Alpha2: "DJ", Alpha3: "DJI", IDC: "253"} - DM: {Name: "Dominica", Alpha2: "DM", Alpha3: "DMA", IDC: "1767"} - DO: {Name: "Dominican Republic", Alpha2: "DO", Alpha3: "DOM", IDC: "1809"} - EC: {Name: "Ecuador", Alpha2: "EC", Alpha3: "ECU", IDC: "593"} - EG: {Name: "Egypt", Alpha2: "EG", Alpha3: "EGY", IDC: "20"} - SV: {Name: "El Salvador", Alpha2: "SV", Alpha3: "SLV", IDC: "503"} - GQ: {Name: "Equatorial Guinea", Alpha2: "GQ", Alpha3: "GNQ", IDC: "240"} - ER: {Name: "Eritrea", Alpha2: "ER", Alpha3: "ERI", IDC: "291"} - EE: {Name: "Estonia", Alpha2: "EE", Alpha3: "EST", IDC: "372"} - ET: {Name: "Ethiopia", Alpha2: "ET", Alpha3: "ETH", IDC: "251"} - FK: {Name: "Falkland Islands (Malvinas)", Alpha2: "FK", Alpha3: "FLK", IDC: "500"} - FO: {Name: "Faroe Islands", Alpha2: "FO", Alpha3: "FRO", IDC: "298"} - FJ: {Name: "Fiji", Alpha2: "FJ", Alpha3: "FJI", IDC: "679"} - FI: {Name: "Finland", Alpha2: "FI", Alpha3: "FIN", IDC: "358"} - FR: {Name: "France", Alpha2: "FR", Alpha3: "FRA", IDC: "33"} - GF: {Name: "French Guiana", Alpha2: "GF", Alpha3: "GUF", IDC: "594"} - PF: {Name: "French Polynesia", Alpha2: "PF", Alpha3: "PYF", IDC: "689"} - TF: {Name: "French Southern Territories", Alpha2: "TF", Alpha3: "ATF", IDC: "0"} - GA: {Name: "Gabon", Alpha2: "GA", Alpha3: "GAB", IDC: "241"} - GM: {Name: "Gambia", Alpha2: "GM", Alpha3: "GMB", IDC: "220"} - GE: {Name: "Georgia", Alpha2: "GE", Alpha3: "GEO", IDC: "995"} - DE: {Name: "Germany", Alpha2: "DE", Alpha3: "DEU", IDC: "49"} - GH: {Name: "Ghana", Alpha2: "GH", Alpha3: "GHA", IDC: "233"} - GI: {Name: "Gibraltar", Alpha2: "GI", Alpha3: "GIB", IDC: "350"} - GR: {Name: "Greece", Alpha2: "GR", Alpha3: "GRC", IDC: "30"} - GL: {Name: "Greenland", Alpha2: "GL", Alpha3: "GRL", IDC: "299"} - GD: {Name: "Grenada", Alpha2: "GD", Alpha3: "GRD", IDC: "1473"} - GP: {Name: "Guadeloupe", Alpha2: "GP", Alpha3: "GLP", IDC: "590"} - GU: {Name: "Guam", Alpha2: "GU", Alpha3: "GUM", IDC: "1671"} - GT: {Name: "Guatemala", Alpha2: "GT", Alpha3: "GTM", IDC: "502"} - GG: {Name: "Guernsey", Alpha2: "GG", Alpha3: "GGY", IDC: "44"} - GN: {Name: "Guinea", Alpha2: "GN", Alpha3: "GIN", IDC: "224"} - GW: {Name: "Guinea-Bissau", Alpha2: "GW", Alpha3: "GNB", IDC: "245"} - GY: {Name: "Guyana", Alpha2: "GY", Alpha3: "GUY", IDC: "592"} - HT: {Name: "Haiti", Alpha2: "HT", Alpha3: "HTI", IDC: "509"} - HM: {Name: "Heard Island and McDonald Islands", Alpha2: "HM", Alpha3: "HMD", IDC: "672"} - HN: {Name: "Honduras", Alpha2: "HN", Alpha3: "HND", IDC: "504"} - HK: {Name: "Hong Kong", Alpha2: "HK", Alpha3: "HKG", IDC: "852"} - HU: {Name: "Hungary", Alpha2: "HU", Alpha3: "HUN", IDC: "36"} - IS: {Name: "Iceland", Alpha2: "IS", Alpha3: "ISL", IDC: "354"} - IN: {Name: "India", Alpha2: "IN", Alpha3: "IND", IDC: "91"} - ID: {Name: "Indonesia", Alpha2: "ID", Alpha3: "IDN", IDC: "62"} - IR: {Name: "Iran, Islamic Republic of", Alpha2: "IR", Alpha3: "IRN", IDC: "98"} - IQ: {Name: "Iraq", Alpha2: "IQ", Alpha3: "IRQ", IDC: "964"} - IE: {Name: "Ireland", Alpha2: "IE", Alpha3: "IRL", IDC: "353"} - IM: {Name: "Isle of Man", Alpha2: "IM", Alpha3: "IMN", IDC: "44"} - IL: {Name: "Israel", Alpha2: "IL", Alpha3: "ISR", IDC: "972"} - IT: {Name: "Italy", Alpha2: "IT", Alpha3: "ITA", IDC: "39"} - JM: {Name: "Jamaica", Alpha2: "JM", Alpha3: "JAM", IDC: "1876"} - JP: {Name: "Japan", Alpha2: "JP", Alpha3: "JPN", IDC: "81"} - JE: {Name: "Jersey", Alpha2: "JE", Alpha3: "JEY", IDC: "44"} - JO: {Name: "Jordan", Alpha2: "JO", Alpha3: "JOR", IDC: "962"} - KZ: {Name: "Kazakhstan", Alpha2: "KZ", Alpha3: "KAZ", IDC: "7"} - KE: {Name: "Kenya", Alpha2: "KE", Alpha3: "KEN", IDC: "254"} - KI: {Name: "Kiribati", Alpha2: "KI", Alpha3: "KIR", IDC: "686"} - KP: {Name: "Korea, Democratic People's Republic of", Alpha2: "KP", Alpha3: "PRK", IDC: "850"} - KR: {Name: "Korea, Republic of", Alpha2: "KR", Alpha3: "KOR", IDC: "82"} - XK: {Name: "Kosovo", Alpha2: "XK", Alpha3: "XXK", IDC: "383"} - KW: {Name: "Kuwait", Alpha2: "KW", Alpha3: "KWT", IDC: "965"} - KG: {Name: "Kyrgyzstan", Alpha2: "KG", Alpha3: "KGZ", IDC: "996"} - LA: {Name: "Lao People's Democratic Republic", Alpha2: "LA", Alpha3: "LAO", IDC: "856"} - LV: {Name: "Latvia", Alpha2: "LV", Alpha3: "LVA", IDC: "371"} - LB: {Name: "Lebanon", Alpha2: "LB", Alpha3: "LBN", IDC: "961"} - LS: {Name: "Lesotho", Alpha2: "LS", Alpha3: "LSO", IDC: "266"} - LR: {Name: "Liberia", Alpha2: "LR", Alpha3: "LBR", IDC: "231"} - LY: {Name: "Libyan Arab Jamahiriya", Alpha2: "LY", Alpha3: "LBY", IDC: "218"} - LI: {Name: "Liechtenstein", Alpha2: "LI", Alpha3: "LIE", IDC: "423"} - LT: {Name: "Lithuania", Alpha2: "LT", Alpha3: "LTU", IDC: "370"} - LU: {Name: "Luxembourg", Alpha2: "LU", Alpha3: "LUX", IDC: "352"} - MO: {Name: "Macau", Alpha2: "MO", Alpha3: "MAC", IDC: "853"} - MG: {Name: "Madagascar", Alpha2: "MG", Alpha3: "MDG", IDC: "261"} - MW: {Name: "Malawi", Alpha2: "MW", Alpha3: "MWI", IDC: "265"} - MY: {Name: "Malaysia", Alpha2: "MY", Alpha3: "MYS", IDC: "60"} - MV: {Name: "Maldives", Alpha2: "MV", Alpha3: "MDV", IDC: "960"} - ML: {Name: "Mali", Alpha2: "ML", Alpha3: "MLI", IDC: "223"} - MT: {Name: "Malta", Alpha2: "MT", Alpha3: "MLT", IDC: "356"} - MH: {Name: "Marshall Islands", Alpha2: "MH", Alpha3: "MHL", IDC: "692"} - MQ: {Name: "Martinique", Alpha2: "MQ", Alpha3: "MTQ", IDC: "596"} - MR: {Name: "Mauritania", Alpha2: "MR", Alpha3: "MRT", IDC: "222"} - MU: {Name: "Mauritius", Alpha2: "MU", Alpha3: "MUS", IDC: "230"} - YT: {Name: "Mayotte", Alpha2: "YT", Alpha3: "MYT", IDC: "262"} - MX: {Name: "Mexico", Alpha2: "MX", Alpha3: "MEX", IDC: "52"} - FM: {Name: "Micronesia, Federated States of", Alpha2: "FM", Alpha3: "FSM", IDC: "691"} - MD: {Name: "Moldova, Republic of", Alpha2: "MD", Alpha3: "MDA", IDC: "373"} - MC: {Name: "Monaco", Alpha2: "MC", Alpha3: "MCO", IDC: "377"} - MN: {Name: "Mongolia", Alpha2: "MN", Alpha3: "MNG", IDC: "976"} - ME: {Name: "Montenegro", Alpha2: "ME", Alpha3: "MNE", IDC: "382"} - MS: {Name: "Montserrat", Alpha2: "MS", Alpha3: "MSR", IDC: "1664"} - MA: {Name: "Morocco", Alpha2: "MA", Alpha3: "MAR", IDC: "212"} - MZ: {Name: "Mozambique", Alpha2: "MZ", Alpha3: "MOZ", IDC: "258"} - MM: {Name: "Myanmar", Alpha2: "MM", Alpha3: "MMR", IDC: "95"} - NA: {Name: "Namibia", Alpha2: "NA", Alpha3: "NAM", IDC: "264"} - NR: {Name: "Nauru", Alpha2: "NR", Alpha3: "NRU", IDC: "674"} - NP: {Name: "Nepal", Alpha2: "NP", Alpha3: "NPL", IDC: "977"} - NL: {Name: "Netherlands", Alpha2: "NL", Alpha3: "NLD", IDC: "31"} - AN: {Name: "Netherlands Antilles", Alpha2: "AN", Alpha3: "ANT", IDC: "599"} - NC: {Name: "New Caledonia", Alpha2: "NC", Alpha3: "NCL", IDC: "687"} - NZ: {Name: "New Zealand", Alpha2: "NZ", Alpha3: "NZL", IDC: "64"} - NI: {Name: "Nicaragua", Alpha2: "NI", Alpha3: "NIC", IDC: "505"} - NE: {Name: "Niger", Alpha2: "NE", Alpha3: "NER", IDC: "227"} - NG: {Name: "Nigeria", Alpha2: "NG", Alpha3: "NGA", IDC: "234"} - NU: {Name: "Niue", Alpha2: "NU", Alpha3: "NIU", IDC: "683"} - NF: {Name: "Norfolk Island", Alpha2: "NF", Alpha3: "NFK", IDC: "6723"} - MP: {Name: "Northern Mariana Islands", Alpha2: "MP", Alpha3: "MNP", IDC: "1670"} - MK: {Name: "North Macedonia, Republic of", Alpha2: "MK", Alpha3: "MKD", IDC: "389"} - NO: {Name: "Norway", Alpha2: "NO", Alpha3: "NOR", IDC: "47"} - OM: {Name: "Oman", Alpha2: "OM", Alpha3: "OMN", IDC: "968"} - PK: {Name: "Pakistan", Alpha2: "PK", Alpha3: "PAK", IDC: "92"} - PW: {Name: "Palau", Alpha2: "PW", Alpha3: "PLW", IDC: "680"} - PS: {Name: "Palestinian Territory, Occupied", Alpha2: "PS", Alpha3: "PSE", IDC: "970"} - PA: {Name: "Panama", Alpha2: "PA", Alpha3: "PAN", IDC: "507"} - PG: {Name: "Papua New Guinea", Alpha2: "PG", Alpha3: "PNG", IDC: "675"} - PY: {Name: "Paraguay", Alpha2: "PY", Alpha3: "PRY", IDC: "595"} - PE: {Name: "Peru", Alpha2: "PE", Alpha3: "PER", IDC: "51"} - PH: {Name: "Philippines", Alpha2: "PH", Alpha3: "PHL", IDC: "63"} - PN: {Name: "Pitcairn", Alpha2: "PN", Alpha3: "PCN", IDC: "64"} - PL: {Name: "Poland", Alpha2: "PL", Alpha3: "POL", IDC: "48"} - PT: {Name: "Portugal", Alpha2: "PT", Alpha3: "PRT", IDC: "351"} - PR: {Name: "Puerto Rico", Alpha2: "PR", Alpha3: "PRI", IDC: "1787"} - QA: {Name: "Qatar", Alpha2: "QA", Alpha3: "QAT", IDC: "974"} - RE: {Name: "Reunion", Alpha2: "RE", Alpha3: "REU", IDC: "262"} - RO: {Name: "Romania", Alpha2: "RO", Alpha3: "ROU", IDC: "40"} - RU: {Name: "Russian Federation", Alpha2: "RU", Alpha3: "RUS", IDC: "7"} - RW: {Name: "Rwanda", Alpha2: "RW", Alpha3: "RWA", IDC: "250"} - BL: {Name: "Saint Barthélemy", Alpha2: "BL", Alpha3: "BLM", IDC: "590"} - SH: {Name: "Saint Helena", Alpha2: "SH", Alpha3: "SHN", IDC: "290"} - KN: {Name: "Saint Kitts and Nevis", Alpha2: "KN", Alpha3: "KNA", IDC: "1869"} - LC: {Name: "Saint Lucia", Alpha2: "LC", Alpha3: "LCA", IDC: "1758"} - MF: {Name: "Saint Martin", Alpha2: "MF", Alpha3: "MAF", IDC: "590"} - PM: {Name: "Saint Pierre and Miquelon", Alpha2: "PM", Alpha3: "SPM", IDC: "508"} - VC: {Name: "Saint Vincent and The Grenadines", Alpha2: "VC", Alpha3: "VCT", IDC: "1784"} - WS: {Name: "Samoa", Alpha2: "WS", Alpha3: "WSM", IDC: "685"} - SM: {Name: "San Marino", Alpha2: "SM", Alpha3: "SMR", IDC: "378"} - ST: {Name: "Sao Tome and Principe", Alpha2: "ST", Alpha3: "STP", IDC: "239"} - SA: {Name: "Saudi Arabia", Alpha2: "SA", Alpha3: "SAU", IDC: "966"} - SN: {Name: "Senegal", Alpha2: "SN", Alpha3: "SEN", IDC: "221"} - RS: {Name: "Serbia", Alpha2: "RS", Alpha3: "SRB", IDC: "381"} - SC: {Name: "Seychelles", Alpha2: "SC", Alpha3: "SYC", IDC: "248"} - SL: {Name: "Sierra Leone", Alpha2: "SL", Alpha3: "SLE", IDC: "232"} - SG: {Name: "Singapore", Alpha2: "SG", Alpha3: "SGP", IDC: "65"} - SK: {Name: "Slovakia", Alpha2: "SK", Alpha3: "SVK", IDC: "421"} - SI: {Name: "Slovenia", Alpha2: "SI", Alpha3: "SVN", IDC: "386"} - SB: {Name: "Solomon Islands", Alpha2: "SB", Alpha3: "SLB", IDC: "677"} - SO: {Name: "Somalia", Alpha2: "SO", Alpha3: "SOM", IDC: "252"} - ZA: {Name: "South Africa", Alpha2: "ZA", Alpha3: "ZAF", IDC: "27"} - GS: {Name: "South Georgia and The South Sandwich Islands", Alpha2: "GS", Alpha3: "SGS", IDC: "500"} - SS: {Name: "South Sudan, Republic of", Alpha2: "SS", Alpha3: "SSD", IDC: "211"} - ES: {Name: "Spain", Alpha2: "ES", Alpha3: "ESP", IDC: "34"} - LK: {Name: "Sri Lanka", Alpha2: "LK", Alpha3: "LKA", IDC: "94"} - SD: {Name: "Sudan", Alpha2: "SD", Alpha3: "SDN", IDC: "249"} - SR: {Name: "Suriname", Alpha2: "SR", Alpha3: "SUR", IDC: "597"} - SJ: {Name: "Svalbard and Jan Mayen", Alpha2: "SJ", Alpha3: "SJM", IDC: "47"} - SZ: {Name: "Swaziland", Alpha2: "SZ", Alpha3: "SWZ", IDC: "268"} - SE: {Name: "Sweden", Alpha2: "SE", Alpha3: "SWE", IDC: "46"} - CH: {Name: "Switzerland", Alpha2: "CH", Alpha3: "CHE", IDC: "41"} - SY: {Name: "Syrian Arab Republic", Alpha2: "SY", Alpha3: "SYR", IDC: "963"} - TW: {Name: "Taiwan", Alpha2: "TW", Alpha3: "TWN", IDC: "886"} - TJ: {Name: "Tajikistan", Alpha2: "TJ", Alpha3: "TJK", IDC: "992"} - TZ: {Name: "Tanzania, United Republic of", Alpha2: "TZ", Alpha3: "TZA", IDC: "255"} - TH: {Name: "Thailand", Alpha2: "TH", Alpha3: "THA", IDC: "66"} - TL: {Name: "Timor-Leste", Alpha2: "TL", Alpha3: "TLS", IDC: "670"} - TG: {Name: "Togo", Alpha2: "TG", Alpha3: "TGO", IDC: "228"} - TK: {Name: "Tokelau", Alpha2: "TK", Alpha3: "TKL", IDC: "690"} - TO: {Name: "Tonga", Alpha2: "TO", Alpha3: "TON", IDC: "676"} - TT: {Name: "Trinidad and Tobago", Alpha2: "TT", Alpha3: "TTO", IDC: "1868"} - TN: {Name: "Tunisia", Alpha2: "TN", Alpha3: "TUN", IDC: "216"} - TR: {Name: "Turkey", Alpha2: "TR", Alpha3: "TUR", IDC: "90"} - TM: {Name: "Turkmenistan", Alpha2: "TM", Alpha3: "TKM", IDC: "993"} - TC: {Name: "Turks and Caicos Islands", Alpha2: "TC", Alpha3: "TCA", IDC: "1649"} - TV: {Name: "Tuvalu", Alpha2: "TV", Alpha3: "TUV", IDC: "688"} - UG: {Name: "Uganda", Alpha2: "UG", Alpha3: "UGA", IDC: "256"} - UA: {Name: "Ukraine", Alpha2: "UA", Alpha3: "UKR", IDC: "380"} - AE: {Name: "United Arab Emirates", Alpha2: "AE", Alpha3: "ARE", IDC: "971"} - GB: {Name: "United Kingdom", Alpha2: "GB", Alpha3: "GBR", IDC: "44"} - UM: {Name: "United States Minor Outlying Islands", Alpha2: "UM", Alpha3: "UMI", IDC: "1"} - US: {Name: "United States of America", Alpha2: "US", Alpha3: "USA", IDC: "1"} - UY: {Name: "Uruguay", Alpha2: "UY", Alpha3: "URY", IDC: "598"} - UZ: {Name: "Uzbekistan", Alpha2: "UZ", Alpha3: "UZB", IDC: "998"} - VU: {Name: "Vanuatu", Alpha2: "VU", Alpha3: "VUT", IDC: "678"} - VA: {Name: "Holy See (Vatican City State)", Alpha2: "VA", Alpha3: "VAT", IDC: "3906"} - VE: {Name: "Venezuela", Alpha2: "VE", Alpha3: "VEN", IDC: "58"} - VN: {Name: "Viet Nam", Alpha2: "VN", Alpha3: "VNM", IDC: "84"} - VG: {Name: "Virgin Islands, British", Alpha2: "VG", Alpha3: "VGB", IDC: "1284"} - VI: {Name: "Virgin Islands, U.S.", Alpha2: "VI", Alpha3: "VIR", IDC: "1340"} - WF: {Name: "Wallis and Futuna", Alpha2: "WF", Alpha3: "WLF", IDC: "681"} - EH: {Name: "Western Sahara", Alpha2: "EH", Alpha3: "ESH", IDC: "212"} - YE: {Name: "Yemen", Alpha2: "YE", Alpha3: "YEM", IDC: "967"} - ZM: {Name: "Zambia", Alpha2: "ZM", Alpha3: "ZMB", IDC: "260"} - ZW: {Name: "Zimbabwe", Alpha2: "ZW", Alpha3: "ZWE", IDC: "263"} - -services: - ezpublish.fieldType: - class: eZ\Publish\Core\FieldType\FieldType - calls: - - [setTransformationProcessor, ["@ezpublish.api.storage_engine.transformation_processor"]] - abstract: true - - ezpublish.fieldType.ezauthor: - class: eZ\Publish\Core\FieldType\Author\Type - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezauthor} - - ezpublish.fieldType.ezbinaryfile: - class: eZ\Publish\Core\FieldType\BinaryFile\Type - arguments: - - ['@ezpublish.fieldType.validator.black_list'] - - "@?ezpublish.fieldType.ezbinarybase.download_url_generator" - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezbinaryfile} - - ezpublish.fieldType.ezboolean: - class: eZ\Publish\Core\FieldType\Checkbox\Type - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezboolean} - - ezpublish.fieldType.ezcountry: - class: eZ\Publish\Core\FieldType\Country\Type - arguments: ["%ezpublish.fieldType.ezcountry.data%"] - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezcountry} - - ezpublish.fieldType.ezdate: - class: eZ\Publish\Core\FieldType\Date\Type - parent: ezpublish.fieldType - arguments: [ "ezdate" ] - tags: - - {name: ezplatform.field_type, alias: ezdate} - - ezpublish.fieldType.ezdatetime: - class: eZ\Publish\Core\FieldType\DateAndTime\Type - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezdatetime} - - ezpublish.fieldType.eztime: - class: eZ\Publish\Core\FieldType\Time\Type - parent: ezpublish.fieldType - arguments: [ "eztime" ] - tags: - - {name: ezplatform.field_type, alias: eztime} - - ezpublish.fieldType.ezemail: - class: eZ\Publish\Core\FieldType\EmailAddress\Type - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezemail} - - ezpublish.fieldType.ezfloat: - class: eZ\Publish\Core\FieldType\Float\Type - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezfloat} - - ezpublish.fieldType.ezinteger: - class: eZ\Publish\Core\FieldType\Integer\Type - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezinteger} - - ezpublish.fieldType.ezimage: - class: eZ\Publish\Core\FieldType\Image\Type - arguments: - - ['@ezpublish.fieldType.validator.black_list', '@ezpublish.fieldType.validator.image'] - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezimage} - - ezpublish.fieldType.ezisbn: - class: eZ\Publish\Core\FieldType\ISBN\Type - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezisbn} - - ezpublish.fieldType.ezkeyword: - class: eZ\Publish\Core\FieldType\Keyword\Type - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezkeyword} - - ezpublish.fieldType.ezmedia: - class: eZ\Publish\Core\FieldType\Media\Type - arguments: - - ['@ezpublish.fieldType.validator.black_list'] - - "@?ezpublish.fieldType.ezbinarybase.download_url_generator" - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezmedia} - - ezpublish.fieldType.ezobjectrelation: - class: eZ\Publish\Core\FieldType\Relation\Type - parent: ezpublish.fieldType - arguments: - $handler: '@ezpublish.spi.persistence.cache.contentHandler' - $targetContentValidator: '@Ibexa\Core\Repository\Validator\TargetContentValidatorInterface' - tags: - - {name: ezplatform.field_type, alias: ezobjectrelation} - - ezpublish.fieldType.ezselection: - class: eZ\Publish\Core\FieldType\Selection\Type - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezselection} - - ezpublish.fieldType.eztext: - class: eZ\Publish\Core\FieldType\TextBlock\Type - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: eztext} - - ezpublish.fieldType.ezstring: - class: eZ\Publish\Core\FieldType\TextLine\Type - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezstring} - - ezpublish.fieldType.ezurl: - class: eZ\Publish\Core\FieldType\Url\Type - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezurl} - - ezpublish.fieldType.ezgmaplocation: - class: eZ\Publish\Core\FieldType\MapLocation\Type - parent: ezpublish.fieldType - tags: - - {name: ezplatform.field_type, alias: ezgmaplocation} - - ezpublish.fieldType.ezobjectrelationlist: - class: eZ\Publish\Core\FieldType\RelationList\Type - parent: ezpublish.fieldType - arguments: - $handler: '@ezpublish.spi.persistence.cache.contentHandler' - $targetContentValidator: '@Ibexa\Core\Repository\Validator\TargetContentValidatorInterface' - tags: - - {name: ezplatform.field_type, alias: ezobjectrelationlist} - - ezpublish.fieldType.ezuser: - class: eZ\Publish\Core\FieldType\User\Type - parent: ezpublish.fieldType - arguments: - - "@ezpublish.spi.persistence.cache.userHandler" - - '@eZ\Publish\API\Repository\PasswordHashService' - - '@eZ\Publish\Core\Repository\User\PasswordValidatorInterface' - tags: - - {name: ezplatform.field_type, alias: ezuser} - - # Not implemented fieldtypes - # Configured to use the Null type to not throw exception - - ezpublish.fieldType.ezenum: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezenum" ] - tags: - - {name: ezplatform.field_type, alias: ezenum} - - ezpublish.fieldType.ezidentifier: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezidentifier" ] - tags: - - {name: ezplatform.field_type, alias: ezidentifier} - - ezpublish.fieldType.ezinisetting: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezinisetting" ] - tags: - - {name: ezplatform.field_type, alias: ezinisetting} - - ezpublish.fieldType.ezmatrix: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezmatrix" ] - tags: - - {name: ezplatform.field_type, alias: ezmatrix} - - ezpublish.fieldType.ezmultioption: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezmultioption" ] - tags: - - {name: ezplatform.field_type, alias: ezmultioption} - - ezpublish.fieldType.ezmultioption2: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezmultioption2" ] - tags: - - {name: ezplatform.field_type, alias: ezmultioption2} - - ezpublish.fieldType.ezmultiprice: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezmultiprice" ] - tags: - - {name: ezplatform.field_type, alias: ezmultiprice} - - ezpublish.fieldType.ezoption: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezoption" ] - tags: - - {name: ezplatform.field_type, alias: ezoption} - - ezpublish.fieldType.ezpackage: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezpackage" ] - tags: - - {name: ezplatform.field_type, alias: ezpackage} - - ezpublish.fieldType.ezproductcategory: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezproductcategory" ] - tags: - - {name: ezplatform.field_type, alias: ezproductcategory} - - ezpublish.fieldType.ezrangeoption: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezrangeoption" ] - tags: - - {name: ezplatform.field_type, alias: ezrangeoption} - - ezpublish.fieldType.ezsubtreesubscription: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezsubtreesubscription" ] - tags: - - {name: ezplatform.field_type, alias: ezsubtreesubscription} - - # not implemented fieldtypes from extensions - ezpublish.fieldType.ezcomcomments: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezcomcomments" ] - tags: - - {name: ezplatform.field_type, alias: ezcomcomments} - - ezpublish.fieldType.ezpaex: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezpaex" ] - tags: - - {name: ezplatform.field_type, alias: ezpaex} - - ezpublish.fieldType.ezsurvey: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezsurvey" ] - tags: - - {name: ezplatform.field_type, alias: ezsurvey} - - ezpublish.fieldType.eztags: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "eztags" ] - tags: - - {name: ezplatform.field_type, alias: eztags} - - ezpublish.fieldType.ezrecommendation: - class: eZ\Publish\Core\FieldType\Null\Type - parent: ezpublish.fieldType - arguments: [ "ezrecommendation" ] - tags: - - {name: ezplatform.field_type, alias: ezrecommendation} - - eZ\Publish\Core\FieldType\ImageAsset\Type: - parent: ezpublish.fieldType - arguments: - - '@ezpublish.api.service.content' - - '@ezpublish.api.service.content_type' - - '@eZ\Publish\Core\FieldType\ImageAsset\AssetMapper' - - '@ezpublish.spi.persistence.cache.contentHandler' - tags: - - {name: ezplatform.field_type, alias: ezimageasset} - - ezpublish.fieldType.ezimageasset: - alias: eZ\Publish\Core\FieldType\ImageAsset\Type - - eZ\Publish\Core\FieldType\ValueSerializer\SymfonySerializerAdapter: - arguments: - $normalizer: '@serializer' - $denormalizer: '@serializer' - $encoder: '@serializer' - $decoder: '@serializer' - - eZ\Publish\SPI\FieldType\ValueSerializerInterface: - alias: 'eZ\Publish\Core\FieldType\ValueSerializer\SymfonySerializerAdapter' diff --git a/eZ/Publish/Core/settings/indexable_fieldtypes.yml b/eZ/Publish/Core/settings/indexable_fieldtypes.yml deleted file mode 100644 index d297cb03af..0000000000 --- a/eZ/Publish/Core/settings/indexable_fieldtypes.yml +++ /dev/null @@ -1,138 +0,0 @@ -parameters: - # @todo drop once core dependencies stop relying on this parameter - ezpublish.fieldType.indexable.unindexed.class: eZ\Publish\Core\FieldType\Unindexed - -services: - ezpublish.fieldType.indexable.ezkeyword: - class: eZ\Publish\Core\FieldType\Keyword\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezkeyword} - - ezpublish.fieldType.indexable.ezauthor: - class: eZ\Publish\Core\FieldType\Author\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezauthor} - - ezpublish.fieldType.indexable.ezstring: - class: eZ\Publish\Core\FieldType\TextLine\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezstring} - - ezpublish.fieldType.indexable.ezgmaplocation: - class: eZ\Publish\Core\FieldType\MapLocation\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezgmaplocation} - - ezpublish.fieldType.indexable.ezcountry: - class: eZ\Publish\Core\FieldType\Country\SearchField - arguments: - - "%ezpublish.fieldType.ezcountry.data%" - tags: - - {name: ezplatform.field_type.indexable, alias: ezcountry} - - ezpublish.fieldType.indexable.ezdate: - class: eZ\Publish\Core\FieldType\Date\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezdate} - - ezpublish.fieldType.indexable.ezinteger: - class: eZ\Publish\Core\FieldType\Integer\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezinteger} - - ezpublish.fieldType.indexable.ezfloat: - class: eZ\Publish\Core\FieldType\Float\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezfloat} - - ezpublish.fieldType.indexable.ezemail: - class: eZ\Publish\Core\FieldType\EmailAddress\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezemail} - - ezpublish.fieldType.indexable.ezimage: - class: eZ\Publish\Core\FieldType\Image\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezimage} - - ezpublish.fieldType.indexable.ezmedia: - class: eZ\Publish\Core\FieldType\Media\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezmedia} - - ezpublish.fieldType.indexable.ezbinaryfile: - class: eZ\Publish\Core\FieldType\BinaryFile\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezbinaryfile} - - ezpublish.fieldType.indexable.eztime: - class: eZ\Publish\Core\FieldType\Time\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: eztime} - - ezpublish.fieldType.indexable.eztext: - class: eZ\Publish\Core\FieldType\TextBlock\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: eztext} - - ezpublish.fieldType.indexable.ezboolean: - class: eZ\Publish\Core\FieldType\Checkbox\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezboolean} - - ezpublish.fieldType.indexable.ezdatetime: - class: eZ\Publish\Core\FieldType\DateAndTime\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezdatetime} - - ezpublish.fieldType.indexable.ezisbn: - class: eZ\Publish\Core\FieldType\ISBN\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezisbn} - - ezpublish.fieldType.indexable.ezobjectrelation: - class: eZ\Publish\Core\FieldType\Relation\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezobjectrelation} - - ezpublish.fieldType.indexable.ezselection: - class: eZ\Publish\Core\FieldType\Selection\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezselection} - - ezpublish.fieldType.indexable.ezobjectrelationlist: - class: eZ\Publish\Core\FieldType\RelationList\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezobjectrelationlist} - - ezpublish.fieldType.indexable.ezurl: - class: eZ\Publish\Core\FieldType\Url\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezurl} - - ezpublish.fieldType.indexable.ezimageasset: - class: eZ\Publish\Core\FieldType\ImageAsset\SearchField - tags: - - {name: ezplatform.field_type.indexable, alias: ezimageasset} - - - ezpublish.fieldType.indexable.unindexed: - class: eZ\Publish\Core\FieldType\Unindexed - tags: - - {name: ezplatform.field_type.indexable, alias: ezuser} - - {name: ezplatform.field_type.indexable, alias: ezenum} - - {name: ezplatform.field_type.indexable, alias: ezidentifier} - - {name: ezplatform.field_type.indexable, alias: ezinisetting} - - {name: ezplatform.field_type.indexable, alias: ezmatrix} - - {name: ezplatform.field_type.indexable, alias: ezmultioption} - - {name: ezplatform.field_type.indexable, alias: ezmultioption2} - - {name: ezplatform.field_type.indexable, alias: ezmultiprice} - - {name: ezplatform.field_type.indexable, alias: ezoption} - - {name: ezplatform.field_type.indexable, alias: ezpackage} - - {name: ezplatform.field_type.indexable, alias: ezproductcategory} - - {name: ezplatform.field_type.indexable, alias: ezrangeoption} - - {name: ezplatform.field_type.indexable, alias: ezsubtreesubscription} - - {name: ezplatform.field_type.indexable, alias: ezcomcomments} - - {name: ezplatform.field_type.indexable, alias: ezsurvey} - - {name: ezplatform.field_type.indexable, alias: eztags} - - {name: ezplatform.field_type.indexable, alias: ezrecommendation} diff --git a/eZ/Publish/Core/settings/io.yml b/eZ/Publish/Core/settings/io.yml deleted file mode 100644 index 20b2b60e3b..0000000000 --- a/eZ/Publish/Core/settings/io.yml +++ /dev/null @@ -1,82 +0,0 @@ -services: - ezpublish.core.io.service: - class: eZ\Publish\Core\IO\TolerantIOService - lazy: true - arguments: - - "@ezpublish.core.io.metadata_handler" - - "@ezpublish.core.io.binarydata_handler" - - "@ezpublish.core.io.mimeTypeDetector" - calls: - - [ setLogger, ["@?logger" ] ] - - ezpublish.core.io.mimeTypeDetector.fileinfo: - class: eZ\Publish\Core\IO\MimeTypeDetector\FileInfo - - ezpublish.core.io.mimeTypeDetector: - alias: ezpublish.core.io.mimeTypeDetector.fileinfo - - # metadata handlers - ezpublish.core.io.metadata_handler: - alias: ezpublish.core.io.metadata_handler.flysystem - - ezpublish.core.io.metadata_handler.flysystem: - class: eZ\Publish\Core\IO\IOMetadataHandler\Flysystem - arguments: - - "@ezpublish.core.io.flysystem.default_filesystem" - - # binarydata handlers - ezpublish.core.io.binarydata_handler: - alias: ezpublish.core.io.binarydata_handler.flysystem - - ezpublish.core.io.binarydata_handler.flysystem: - class: eZ\Publish\Core\IO\IOBinarydataHandler\Flysystem - arguments: - - "@ezpublish.core.io.flysystem.default_filesystem" - - "@ezpublish.core.io.default_url_decorator" - - ezpublish.core.io.flysystem.base_filesystem: - class: League\Flysystem\Filesystem - abstract: true - - ezpublish.core.io.flysystem.default_filesystem: - parent: ezpublish.core.io.flysystem.base_filesystem - arguments: - - "@ezpublish.core.io.flysystem.default_adapter" - - ezpublish.core.io.flysystem.default_adapter: - class: League\Flysystem\Adapter\Local - arguments: - - "%io_root_dir%" - - ezpublish.core.io.default_url_decorator: - alias: ezpublish.core.io.prefix_url_decorator - - ezpublish.core.io.prefix_url_decorator: - class: eZ\Publish\Core\IO\UrlDecorator\AbsolutePrefix - arguments: - - '@eZ\Publish\Core\IO\IOConfigProvider' - - # used by legacy in Image Converter to decorate its own url - ezpublish.core.io.image_fieldtype.legacy_url_decorator: - class: eZ\Publish\Core\IO\UrlDecorator\Prefix - arguments: - - '@eZ\Publish\Core\IO\IOConfigProvider' - - ezpublish.core.io.image_fieldtype.legacy_url_redecorator: - class: eZ\Publish\Core\IO\UrlRedecorator - arguments: - - "@ezpublish.core.io.default_url_decorator" - - "@ezpublish.core.io.image_fieldtype.legacy_url_decorator" - - ezpublish.core.io.metadata_handler.legacy_dfs_cluster: - abstract: true - class: eZ\Publish\Core\IO\IOMetadataHandler\LegacyDFSCluster - arguments: - - ~ - - "@ezpublish.core.io.image_fieldtype.legacy_url_decorator" - - eZ\Publish\Core\IO\FilePathNormalizer\Flysystem: - arguments: - $slugConverter: '@ezpublish.persistence.slug_converter' - - eZ\Publish\Core\IO\FilePathNormalizerInterface: '@eZ\Publish\Core\IO\FilePathNormalizer\Flysystem' diff --git a/eZ/Publish/Core/settings/limitations/language.yml b/eZ/Publish/Core/settings/limitations/language.yml deleted file mode 100644 index ed1271b67b..0000000000 --- a/eZ/Publish/Core/settings/limitations/language.yml +++ /dev/null @@ -1,12 +0,0 @@ -services: - _defaults: - autowire: true - autoconfigure: true - public: false - _instanceof: - eZ\Publish\Core\Limitation\LanguageLimitation\VersionTargetEvaluator: - tags: - - { name: ezplatform.limitation.language.version_target_evaluator } - - eZ\Publish\Core\Limitation\LanguageLimitation\: - resource: '../../Limitation/LanguageLimitation/*' diff --git a/eZ/Publish/Core/settings/notification.yml b/eZ/Publish/Core/settings/notification.yml deleted file mode 100644 index 6f9157e41c..0000000000 --- a/eZ/Publish/Core/settings/notification.yml +++ /dev/null @@ -1,10 +0,0 @@ -services: - eZ\Publish\Core\Notification\Renderer\Registry: - autowire: true - autoconfigure: false - public: false - - notification.renderer.registry: '@eZ\Publish\Core\Notification\Renderer\Registry' - - eZ\Publish\API\Repository\NotificationService: - alias: eZ\Publish\Core\Event\NotificationService diff --git a/eZ/Publish/Core/settings/repository.yml b/eZ/Publish/Core/settings/repository.yml deleted file mode 100644 index 6b85580fd3..0000000000 --- a/eZ/Publish/Core/settings/repository.yml +++ /dev/null @@ -1,73 +0,0 @@ -services: - # API Aliases - ezpublish.api.repository: - alias: eZ\Publish\Core\Repository\SiteAccessAware\Repository - public: true - - ezpublish.api.service.bookmark: - alias: eZ\Publish\Core\Event\BookmarkService - public: true - - ezpublish.api.service.content: - alias: eZ\Publish\Core\Repository\SiteAccessAware\ContentService - public: true - - ezpublish.api.service.content_type: - alias: eZ\Publish\Core\Repository\SiteAccessAware\ContentTypeService - public: true - - ezpublish.api.service.field_type: - alias: eZ\Publish\Core\Event\FieldTypeService - public: true - - ezpublish.api.service.role: - alias: eZ\Publish\Core\Event\RoleService - public: true - - ezpublish.api.service.object_state: - alias: eZ\Publish\Core\Repository\SiteAccessAware\ObjectStateService - public: true - - ezpublish.api.service.url_wildcard: - alias: eZ\Publish\Core\Event\URLWildcardService - public: true - - ezpublish.api.service.url_alias: - alias: eZ\Publish\Core\Repository\SiteAccessAware\URLAliasService - public: true - - ezpublish.api.service.user: - alias: eZ\Publish\Core\Repository\SiteAccessAware\UserService - public: true - - ezpublish.api.service.search: - alias: eZ\Publish\Core\Repository\SiteAccessAware\SearchService - public: true - - ezpublish.api.service.section: - alias: eZ\Publish\Core\Repository\SiteAccessAware\SectionService - public: true - - ezpublish.api.service.trash: - alias: eZ\Publish\Core\Repository\SiteAccessAware\TrashService - public: true - - ezpublish.api.service.location: - alias: eZ\Publish\Core\Repository\SiteAccessAware\LocationService - public: true - - ezpublish.api.service.language: - alias: eZ\Publish\Core\Repository\SiteAccessAware\LanguageService - public: true - - ezpublish.api.service.url: - alias: eZ\Publish\Core\Event\URLService - public: true - - ezpublish.api.service.notification: - alias: eZ\Publish\Core\Repository\SiteAccessAware\NotificationService - public: true - - ezpublish.api.service.user_preference: - alias: eZ\Publish\Core\Event\UserPreferenceService - public: true diff --git a/eZ/Publish/Core/settings/repository/autowire.yml b/eZ/Publish/Core/settings/repository/autowire.yml deleted file mode 100644 index debfa28b25..0000000000 --- a/eZ/Publish/Core/settings/repository/autowire.yml +++ /dev/null @@ -1,30 +0,0 @@ -services: - eZ\Publish\API\Repository\Repository: '@ezpublish.api.repository' - - eZ\Publish\Core\MVC\ConfigResolverInterface: '@ezpublish.config.resolver' - eZ\Publish\Core\MVC\Symfony\SiteAccess: '@ezpublish.siteaccess' - - eZ\Publish\API\Repository\BookmarkService: '@ezpublish.api.service.bookmark' - eZ\Publish\API\Repository\ContentService: '@ezpublish.api.service.content' - eZ\Publish\API\Repository\ContentTypeService: '@ezpublish.api.service.content_type' - eZ\Publish\API\Repository\FieldTypeService: '@ezpublish.api.service.field_type' - eZ\Publish\API\Repository\LanguageService: '@ezpublish.api.service.language' - eZ\Publish\API\Repository\LocationService: '@ezpublish.api.service.location' - eZ\Publish\API\Repository\NotificationService: '@ezpublish.api.service.notification' - eZ\Publish\API\Repository\ObjectStateService: '@ezpublish.api.service.object_state' - eZ\Publish\API\Repository\RoleService: '@ezpublish.api.service.role' - eZ\Publish\API\Repository\SearchService: '@ezpublish.api.service.search' - eZ\Publish\API\Repository\SectionService: '@ezpublish.api.service.section' - eZ\Publish\API\Repository\UserPreferenceService: '@ezpublish.api.service.user_preference' - eZ\Publish\API\Repository\UserService: '@ezpublish.api.service.user' - eZ\Publish\API\Repository\URLService: '@ezpublish.api.service.url' - eZ\Publish\API\Repository\URLWildcardService: '@ezpublish.api.service.url_wildcard' - eZ\Publish\API\Repository\URLAliasService: '@ezpublish.api.service.url_alias' - eZ\Publish\API\Repository\TrashService: '@ezpublish.api.service.trash' - eZ\Publish\API\Repository\SettingService: '@eZ\Publish\Core\Event\SettingService' - - eZ\Publish\API\Repository\PermissionService: '@eZ\Publish\Core\Repository\Permission\CachedPermissionService' - eZ\Publish\API\Repository\PermissionResolver: '@eZ\Publish\API\Repository\PermissionService' - eZ\Publish\API\Repository\PermissionCriterionResolver: '@eZ\Publish\Core\Repository\Permission\PermissionCriterionResolver' - - eZ\Publish\Core\Helper\FieldsGroups\FieldsGroupsList: '@ezpublish.fields_groups.list' diff --git a/eZ/Publish/Core/settings/repository/event.yml b/eZ/Publish/Core/settings/repository/event.yml deleted file mode 100644 index 9ed8f371fd..0000000000 --- a/eZ/Publish/Core/settings/repository/event.yml +++ /dev/null @@ -1,98 +0,0 @@ -services: - _defaults: - autowire: true - autoconfigure: true - public: false - - eZ\Publish\Core\Event\Repository: - arguments: - $repository: '@ezpublish.api.inner_repository' - $bookmarkService: '@eZ\Publish\Core\Event\BookmarkService' - $contentService: '@eZ\Publish\Core\Event\ContentService' - $contentTypeService: '@eZ\Publish\Core\Event\ContentTypeService' - $fieldTypeService: '@eZ\Publish\Core\Event\FieldTypeService' - $languageService: '@eZ\Publish\Core\Event\LanguageService' - $locationService: '@eZ\Publish\Core\Event\LocationService' - $notificationService: '@eZ\Publish\Core\Event\NotificationService' - $objectStateService: '@eZ\Publish\Core\Event\ObjectStateService' - $roleService: '@eZ\Publish\Core\Event\RoleService' - $searchService: '@eZ\Publish\Core\Event\SearchService' - $sectionService: '@eZ\Publish\Core\Event\SectionService' - $trashService: '@eZ\Publish\Core\Event\TrashService' - $urlAliasService: '@eZ\Publish\Core\Event\URLAliasService' - $urlService: '@eZ\Publish\Core\Event\URLService' - $urlWildcardService: '@eZ\Publish\Core\Event\URLWildcardService' - $userPreferenceService: '@eZ\Publish\Core\Event\UserPreferenceService' - $userService: '@eZ\Publish\Core\Event\UserService' - - eZ\Publish\Core\Event\BookmarkService: - arguments: - $innerService: '@ezpublish.api.service.inner_bookmark' - - eZ\Publish\Core\Event\ContentService: - arguments: - $innerService: '@ezpublish.api.service.inner_content' - - eZ\Publish\Core\Event\ContentTypeService: - arguments: - $innerService: '@ezpublish.api.service.inner_content_type' - - eZ\Publish\Core\Event\FieldTypeService: - arguments: - $innerService: '@ezpublish.api.service.inner_field_type' - - eZ\Publish\Core\Event\LanguageService: - arguments: - $innerService: '@ezpublish.api.service.inner_language' - - eZ\Publish\Core\Event\LocationService: - arguments: - $innerService: '@ezpublish.api.service.inner_location' - - eZ\Publish\Core\Event\NotificationService: - arguments: - $innerService: '@ezpublish.api.service.inner_notification' - - eZ\Publish\Core\Event\ObjectStateService: - arguments: - $innerService: '@ezpublish.api.service.inner_object_state' - - eZ\Publish\Core\Event\RoleService: - arguments: - $innerService: '@ezpublish.api.service.inner_role' - - eZ\Publish\Core\Event\SearchService: - arguments: - $innerService: '@ezpublish.api.service.inner_search' - - eZ\Publish\Core\Event\SectionService: - arguments: - $innerService: '@ezpublish.api.service.inner_section' - - eZ\Publish\Core\Event\TrashService: - arguments: - $innerService: '@ezpublish.api.service.inner_trash' - - eZ\Publish\Core\Event\URLAliasService: - arguments: - $innerService: '@ezpublish.api.service.inner_url_alias' - - eZ\Publish\Core\Event\URLService: - arguments: - $innerService: '@ezpublish.api.service.inner_url' - - eZ\Publish\Core\Event\URLWildcardService: - arguments: - $innerService: '@ezpublish.api.service.inner_url_wildcard' - - eZ\Publish\Core\Event\UserPreferenceService: - arguments: - $innerService: '@ezpublish.api.service.inner_user_preference' - - eZ\Publish\Core\Event\UserService: - arguments: - $innerService: '@ezpublish.api.service.inner_user' - - eZ\Publish\Core\Event\SettingService: - arguments: - $innerService: '@eZ\Publish\Core\Repository\SettingService' diff --git a/eZ/Publish/Core/settings/repository/inner.yml b/eZ/Publish/Core/settings/repository/inner.yml deleted file mode 100644 index fe80ce2965..0000000000 --- a/eZ/Publish/Core/settings/repository/inner.yml +++ /dev/null @@ -1,266 +0,0 @@ -parameters: - ezplatform.kernel.proxy_cache_dir: 'var/cache/repository/proxy' - - # intentionally defined class parameter to be used by the Repository Factory - ezpublish.api.inner_repository.class: eZ\Publish\Core\Repository\Repository -services: - ezpublish.api.repository.factory: - class: eZ\Publish\Core\Base\Container\ApiLoader\RepositoryFactory - arguments: - - "%ezpublish.api.inner_repository.class%" - - "%ezpublish.api.role.policy_map%" - - '@eZ\Publish\API\Repository\LanguageResolver' - calls: - - [setContainer, ["@service_container"]] - - ezpublish.api.inner_repository: - class: eZ\Publish\Core\Repository\Repository - factory: ["@ezpublish.api.repository.factory", buildRepository] - arguments: - - "@ezpublish.api.persistence_handler" - - "@ezpublish.spi.search" - - '@ezpublish.search.background_indexer' - - '@ezpublish.repository.relation_processor' - - '@eZ\Publish\Core\FieldType\FieldTypeRegistry' - - '@eZ\Publish\Core\Repository\User\PasswordHashService' - - '@eZ\Publish\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy' - - '@eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperFactory' - - '@eZ\Publish\Core\Repository\Mapper\ContentDomainMapper' - - '@eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper' - - '@eZ\Publish\Core\Repository\Mapper\RoleDomainMapper' - - '@eZ\Publish\Core\Repository\Mapper\ContentMapper' - - '@eZ\Publish\SPI\Repository\Validator\ContentValidator' - - '@eZ\Publish\Core\Repository\Permission\LimitationService' - - '@eZ\Publish\API\Repository\PermissionService' - - '@eZ\Publish\SPI\Persistence\Filter\Content\Handler' - - '@eZ\Publish\SPI\Persistence\Filter\Location\Handler' - - '@eZ\Publish\Core\Repository\User\PasswordValidatorInterface' - - '%languages%' - - ezpublish.api.service.inner_content: - class: eZ\Publish\Core\Repository\ContentService - factory: ["@ezpublish.api.inner_repository", getContentService] - lazy: true - - ezpublish.api.service.inner_content_type: - class: eZ\Publish\Core\Repository\ContentTypeService - factory: ["@ezpublish.api.inner_repository", getContentTypeService] - lazy: true - - ezpublish.api.service.inner_field_type: - class: eZ\Publish\Core\Repository\FieldTypeService - factory: ["@ezpublish.api.inner_repository", getFieldTypeService] - lazy: true - - ezpublish.api.service.inner_role: - class: eZ\Publish\Core\Repository\RoleService - factory: ["@ezpublish.api.inner_repository", getRoleService] - lazy: true - - ezpublish.api.service.inner_object_state: - class: eZ\Publish\Core\Repository\ObjectStateService - factory: ["@ezpublish.api.inner_repository", getObjectStateService] - lazy: true - - ezpublish.api.service.inner_url_wildcard: - class: eZ\Publish\Core\Repository\URLWildcardService - factory: ["@ezpublish.api.inner_repository", getURLWildcardService] - lazy: true - - ezpublish.api.service.inner_url_alias: - class: eZ\Publish\Core\Repository\URLAliasService - factory: ["@ezpublish.api.inner_repository", getURLAliasService] - lazy: true - - ezpublish.api.service.inner_user: - class: eZ\Publish\Core\Repository\UserService - factory: ["@ezpublish.api.inner_repository", getUserService] - calls: - - [setLogger, ["@?logger"]] - lazy: true - - ezpublish.api.service.inner_search: - class: eZ\Publish\Core\Repository\SearchService - factory: ["@ezpublish.api.inner_repository", getSearchService] - lazy: true - - ezpublish.api.service.inner_section: - class: eZ\Publish\Core\Repository\SectionService - factory: ["@ezpublish.api.inner_repository", getSectionService] - lazy: true - - ezpublish.api.service.inner_trash: - class: eZ\Publish\Core\Repository\TrashService - factory: ["@ezpublish.api.inner_repository", getTrashService] - lazy: true - - ezpublish.api.service.inner_location: - class: eZ\Publish\Core\Repository\LocationService - factory: ["@ezpublish.api.inner_repository", getLocationService] - lazy: true - - ezpublish.api.service.inner_language: - class: eZ\Publish\Core\Repository\LanguageService - factory: ["@ezpublish.api.inner_repository", getContentLanguageService] - lazy: true - - ezpublish.api.service.inner_url: - class: eZ\Publish\Core\Repository\URLService - factory: ["@ezpublish.api.inner_repository", getUrlService] - lazy: true - - ezpublish.api.service.inner_bookmark: - class: eZ\Publish\Core\Repository\BookmarkService - factory: ["@ezpublish.api.inner_repository", getBookmarkService] - lazy: true - - ezpublish.api.service.inner_notification: - class: eZ\Publish\Core\Repository\NotificationService - factory: ["@ezpublish.api.inner_repository", getNotificationService] - lazy: true - - ezpublish.api.service.inner_user_preference: - class: eZ\Publish\Core\Repository\UserPreferenceService - factory: ["@ezpublish.api.inner_repository", getUserPreferenceService] - lazy: true - - eZ\Publish\Core\Repository\SettingService: - arguments: - $settingHandler: '@eZ\Publish\Core\Persistence\Cache\SettingHandler' - $permissionResolver: '@eZ\Publish\API\Repository\PermissionResolver' - - # Factories - ezpublish.search.background_indexer: - class: eZ\Publish\Core\Search\Common\BackgroundIndexer\NullIndexer - - ezpublish.repository.relation_processor: - class: eZ\Publish\Core\Repository\Helper\RelationProcessor - arguments: - - '@ezpublish.api.persistence_handler' - calls: - - ['setLogger', ['@?logger']] - - # Domain mappers and proxies - eZ\Publish\Core\Repository\ProxyFactory\ProxyGenerator: - arguments: - $proxyCacheDir: '%ezplatform.kernel.proxy_cache_dir%' - - eZ\Publish\Core\Repository\ProxyFactory\ProxyGeneratorInterface: - alias: 'eZ\Publish\Core\Repository\ProxyFactory\ProxyGenerator' - - eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperFactory: - arguments: - $proxyGenerator: '@eZ\Publish\Core\Repository\ProxyFactory\ProxyGeneratorInterface' - - eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapper: - factory: ['@eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperFactory', 'create'] - arguments: - $repository: '@ezpublish.api.inner_repository' - - eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperInterface: - alias: 'eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapper' - - # Mappers - eZ\Publish\Core\Repository\Mapper\ProxyAwareDomainMapper: - abstract: true - calls: - - method: setProxyFactory - arguments: - $proxyFactory: '@eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperInterface' - - eZ\Publish\Core\Repository\Mapper\ContentMapper: - arguments: - $contentLanguageHandler: '@ezpublish.spi.persistence.language_handler' - $fieldTypeRegistry: '@eZ\Publish\Core\FieldType\FieldTypeRegistry' - - eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper: - parent: eZ\Publish\Core\Repository\Mapper\ProxyAwareDomainMapper - arguments: - $contentTypeHandler: '@ezpublish.spi.persistence.content_type_handler' - $contentLanguageHandler: '@ezpublish.spi.persistence.language_handler' - $fieldTypeRegistry: '@eZ\Publish\Core\FieldType\FieldTypeRegistry' - - eZ\Publish\Core\Repository\Mapper\ContentDomainMapper: - parent: eZ\Publish\Core\Repository\Mapper\ProxyAwareDomainMapper - arguments: - $contentHandler: '@ezpublish.spi.persistence.content_handler' - $locationHandler: '@ezpublish.spi.persistence.location_handler' - $contentTypeHandler: '@ezpublish.spi.persistence.content_type_handler' - $contentTypeDomainMapper: '@eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper' - $contentLanguageHandler: '@ezpublish.spi.persistence.language_handler' - $fieldTypeRegistry: '@eZ\Publish\Core\FieldType\FieldTypeRegistry' - $thumbnailStrategy: '@eZ\Publish\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy' - calls: - - [setLogger, ['@?logger']] - tags: - - { name: 'monolog.logger', channel: 'ibexa.core' } - - eZ\Publish\Core\Repository\Mapper\RoleDomainMapper: - arguments: - $limitationService: '@eZ\Publish\Core\Repository\Permission\LimitationService' - - # Permission-related - eZ\Publish\Core\Repository\Permission\LimitationService: - arguments: - $limitationTypes: !tagged_iterator { tag: ezpublish.limitationType, index_by: alias } - - eZ\Publish\Core\Repository\Permission\PermissionResolver: - arguments: - $roleDomainMapper: '@eZ\Publish\Core\Repository\Mapper\RoleDomainMapper' - $limitationService: '@eZ\Publish\Core\Repository\Permission\LimitationService' - $userHandler: '@ezpublish.spi.persistence.user_handler' - $configResolver: '@ezpublish.config.resolver' - $policyMap: '%ezpublish.api.role.policy_map%' - - eZ\Publish\Core\Repository\Permission\PermissionCriterionResolver: - arguments: - $innerPermissionResolver: '@eZ\Publish\Core\Repository\Permission\PermissionResolver' - $limitationService: '@eZ\Publish\Core\Repository\Permission\LimitationService' - - eZ\Publish\Core\Repository\Permission\CachedPermissionService: - arguments: - $innerPermissionResolver: '@eZ\Publish\Core\Repository\Permission\PermissionResolver' - $permissionCriterionResolver: '@eZ\Publish\Core\Repository\Permission\PermissionCriterionResolver' - - eZ\Publish\Core\Repository\Strategy\ContentValidator\ContentValidatorStrategy: - arguments: - $contentValidators: !tagged_iterator ezplatform.spi.content.validator - - eZ\Publish\Core\Repository\Validator\ContentCreateStructValidator: - arguments: - $contentMapper: '@eZ\Publish\Core\Repository\Mapper\ContentMapper' - $fieldTypeRegistry: '@eZ\Publish\Core\FieldType\FieldTypeRegistry' - tags: - - ezplatform.spi.content.validator - - eZ\Publish\Core\Repository\Validator\ContentUpdateStructValidator: - arguments: - $contentMapper: '@eZ\Publish\Core\Repository\Mapper\ContentMapper' - $fieldTypeRegistry: '@eZ\Publish\Core\FieldType\FieldTypeRegistry' - $contentLanguageHandler: '@ezpublish.spi.persistence.language_handler' - tags: - - ezplatform.spi.content.validator - - eZ\Publish\Core\Repository\Validator\VersionValidator: - arguments: - $fieldTypeRegistry: '@eZ\Publish\Core\FieldType\FieldTypeRegistry' - tags: - - ezplatform.spi.content.validator - - eZ\Publish\SPI\Repository\Validator\ContentValidator: '@eZ\Publish\Core\Repository\Strategy\ContentValidator\ContentValidatorStrategy' - - # LocationResolver - eZ\Publish\Core\Repository\LocationResolver\PermissionAwareLocationResolver: - arguments: - - '@ezpublish.api.service.location' - - eZ\Publish\Core\Repository\LocationResolver\LocationResolver: - alias: eZ\Publish\Core\Repository\LocationResolver\PermissionAwareLocationResolver - - Ibexa\Core\Repository\Validator\TargetContentValidator: - arguments: - $contentHandler: '@ezpublish.spi.persistence.content_handler' - $contentTypeHandler: '@ezpublish.spi.persistence.content_type_handler' - - Ibexa\Core\Repository\Validator\TargetContentValidatorInterface: - alias: Ibexa\Core\Repository\Validator\TargetContentValidator diff --git a/eZ/Publish/Core/settings/repository/siteaccessaware.yml b/eZ/Publish/Core/settings/repository/siteaccessaware.yml deleted file mode 100644 index bbb7842ddc..0000000000 --- a/eZ/Publish/Core/settings/repository/siteaccessaware.yml +++ /dev/null @@ -1,115 +0,0 @@ -parameters: - languages: [] - storage_dir: var/ezdemo_site/storage - legacy_url_prefix: var/ezdemo_site/storage - url_prefix: var/ezdemo_site/storage - -services: - eZ\Publish\Core\Repository\SiteAccessAware\Repository: - arguments: - - '@eZ\Publish\Core\Event\Repository' - - '@eZ\Publish\Core\Repository\SiteAccessAware\ContentService' - - '@eZ\Publish\Core\Repository\SiteAccessAware\ContentTypeService' - - '@eZ\Publish\Core\Repository\SiteAccessAware\ObjectStateService' - - '@eZ\Publish\Core\Repository\SiteAccessAware\URLAliasService' - - '@eZ\Publish\Core\Repository\SiteAccessAware\UserService' - - '@eZ\Publish\Core\Repository\SiteAccessAware\SearchService' - - '@eZ\Publish\Core\Repository\SiteAccessAware\SectionService' - - '@eZ\Publish\Core\Repository\SiteAccessAware\TrashService' - - '@eZ\Publish\Core\Repository\SiteAccessAware\LocationService' - - '@eZ\Publish\Core\Repository\SiteAccessAware\LanguageService' - - '@eZ\Publish\Core\Repository\SiteAccessAware\NotificationService' - - eZ\Publish\Core\Repository\SiteAccessAware\ContentService: - arguments: - - '@eZ\Publish\Core\Event\ContentService' - - '@ezpublish.helper.language_resolver' - - eZ\Publish\Core\Repository\SiteAccessAware\ContentTypeService: - arguments: - - '@eZ\Publish\Core\Event\ContentTypeService' - - '@ezpublish.helper.language_resolver' - - eZ\Publish\Core\Repository\SiteAccessAware\ObjectStateService: - arguments: - - '@eZ\Publish\Core\Event\ObjectStateService' - - '@ezpublish.helper.language_resolver' - - eZ\Publish\Core\Repository\SiteAccessAware\URLAliasService: - arguments: - - '@eZ\Publish\Core\Event\URLAliasService' - - '@ezpublish.helper.language_resolver' - - eZ\Publish\Core\Repository\SiteAccessAware\UserService: - arguments: - - '@eZ\Publish\Core\Event\UserService' - - '@ezpublish.helper.language_resolver' - - eZ\Publish\Core\Repository\SiteAccessAware\SearchService: - arguments: - - '@eZ\Publish\Core\Event\SearchService' - - '@ezpublish.helper.language_resolver' - - eZ\Publish\Core\Repository\SiteAccessAware\SectionService: - arguments: - - '@eZ\Publish\Core\Event\SectionService' - - eZ\Publish\Core\Repository\SiteAccessAware\TrashService: - arguments: - - '@eZ\Publish\Core\Event\TrashService' - - eZ\Publish\Core\Repository\SiteAccessAware\LocationService: - arguments: - - '@eZ\Publish\Core\Event\LocationService' - - '@ezpublish.helper.language_resolver' - - eZ\Publish\Core\Repository\SiteAccessAware\LanguageService: - arguments: - - '@eZ\Publish\Core\Event\LanguageService' - - eZ\Publish\Core\Repository\SiteAccessAware\NotificationService: - arguments: - - '@eZ\Publish\Core\Event\NotificationService' - - eZ\Publish\Core\Repository\SiteAccessAware\SettingService: - arguments: - - '@eZ\Publish\Core\Event\SettingService' - - ezpublish.siteaccessaware.repository: '@eZ\Publish\Core\Repository\SiteAccessAware\Repository' - ezpublish.siteaccessaware.service.content: '@eZ\Publish\Core\Repository\SiteAccessAware\ContentService' - ezpublish.siteaccessaware.service.content_type: '@eZ\Publish\Core\Repository\SiteAccessAware\ContentTypeService' - ezpublish.siteaccessaware.service.object_state: '@eZ\Publish\Core\Repository\SiteAccessAware\ObjectStateService' - ezpublish.siteaccessaware.service.url_alias: '@eZ\Publish\Core\Repository\SiteAccessAware\URLAliasService' - ezpublish.siteaccessaware.service.user: '@eZ\Publish\Core\Repository\SiteAccessAware\UserService' - ezpublish.siteaccessaware.service.search: '@eZ\Publish\Core\Repository\SiteAccessAware\SearchService' - ezpublish.siteaccessaware.service.section: '@eZ\Publish\Core\Repository\SiteAccessAware\SectionService' - ezpublish.siteaccessaware.service.trash: '@eZ\Publish\Core\Repository\SiteAccessAware\TrashService' - ezpublish.siteaccessaware.service.location: '@eZ\Publish\Core\Repository\SiteAccessAware\LocationService' - ezpublish.siteaccessaware.service.language: '@eZ\Publish\Core\Repository\SiteAccessAware\LanguageService' - ezpublish.siteaccessaware.service.notification: '@eZ\Publish\Core\Repository\SiteAccessAware\NotificationService' - ezpublish.siteaccessaware.service.setting: '@eZ\Publish\Core\Repository\SiteAccessAware\SettingService' - - # Helpers - eZ\Publish\Core\Repository\SiteAccessAware\Language\AbstractLanguageResolver: - arguments: - $defaultUseAlwaysAvailable: true - $defaultShowAllTranslations: false - - eZ\Publish\Core\Repository\SiteAccessAware\Language\LanguageResolver: - parent: eZ\Publish\Core\Repository\SiteAccessAware\Language\AbstractLanguageResolver - arguments: ['%languages%'] - - eZ\Publish\API\Repository\LanguageResolver: - alias: eZ\Publish\Core\Repository\SiteAccessAware\Language\LanguageResolver - - ezpublish.helper.language_resolver: - alias: eZ\Publish\API\Repository\LanguageResolver - - eZ\Publish\Core\IO\IOConfigProvider: - alias: eZ\Publish\Core\Repository\SiteAccessAware\Config\IOConfigResolver - - eZ\Publish\Core\Repository\SiteAccessAware\Config\IOConfigResolver: - arguments: - - '%storage_dir%' - - '%legacy_url_prefix%' - - '%url_prefix%' diff --git a/eZ/Publish/Core/settings/roles.yml b/eZ/Publish/Core/settings/roles.yml deleted file mode 100644 index ee3a42b386..0000000000 --- a/eZ/Publish/Core/settings/roles.yml +++ /dev/null @@ -1,142 +0,0 @@ -imports: - - { resource: limitations/language.yml } - -parameters: - ezpublish.api.role.policy_map: {} - -services: - ## Implemented Limitations - ezpublish.api.role.limitation_type.content_type: - class: eZ\Publish\Core\Limitation\ContentTypeLimitationType - arguments: ["@ezpublish.api.persistence_handler"] - tags: - - {name: ezpublish.limitationType, alias: Class} - - ezpublish.api.role.limitation_type.language: - class: eZ\Publish\Core\Limitation\LanguageLimitationType - arguments: - $persistenceLanguageHandler: '@ezpublish.spi.persistence.language_handler' - $persistenceContentHandler: '@ezpublish.spi.persistence.content_handler' - $versionTargetEvaluators: !tagged ezplatform.limitation.language.version_target_evaluator - tags: - - {name: ezpublish.limitationType, alias: Language} - - ezpublish.api.role.limitation_type.location: - class: eZ\Publish\Core\Limitation\LocationLimitationType - arguments: ["@ezpublish.api.persistence_handler"] - tags: - - {name: ezpublish.limitationType, alias: Node} - - ezpublish.api.role.limitation_type.owner: - class: eZ\Publish\Core\Limitation\OwnerLimitationType - arguments: ["@ezpublish.api.persistence_handler"] - tags: - - {name: ezpublish.limitationType, alias: Owner} - - ezpublish.api.role.limitation_type.parent_content_type: - class: eZ\Publish\Core\Limitation\ParentContentTypeLimitationType - arguments: ["@ezpublish.api.persistence_handler"] - tags: - - {name: ezpublish.limitationType, alias: ParentClass} - - ezpublish.api.role.limitation_type.parent_depth: - class: eZ\Publish\Core\Limitation\ParentDepthLimitationType - arguments: ["@ezpublish.api.persistence_handler"] - tags: - - {name: ezpublish.limitationType, alias: ParentDepth} - - ezpublish.api.role.limitation_type.parent_owner: - class: eZ\Publish\Core\Limitation\ParentOwnerLimitationType - arguments: ["@ezpublish.api.persistence_handler"] - tags: - - {name: ezpublish.limitationType, alias: ParentOwner} - - ezpublish.api.role.limitation_type.parent_group: - class: eZ\Publish\Core\Limitation\ParentUserGroupLimitationType - arguments: ["@ezpublish.api.persistence_handler"] - tags: - - {name: ezpublish.limitationType, alias: ParentGroup} - - ezpublish.api.role.limitation_type.section: - class: eZ\Publish\Core\Limitation\SectionLimitationType - arguments: ["@ezpublish.api.persistence_handler"] - tags: - - {name: ezpublish.limitationType, alias: Section} - - ezpublish.api.role.limitation_type.new_section: - class: eZ\Publish\Core\Limitation\NewSectionLimitationType - arguments: ["@ezpublish.api.persistence_handler"] - tags: - - {name: ezpublish.limitationType, alias: NewSection} - - ezpublish.api.role.limitation_type.siteaccess: - class: eZ\Publish\Core\Limitation\SiteAccessLimitationType - arguments: ['@ezpublish.siteaccess_service'] - tags: - - {name: ezpublish.limitationType, alias: SiteAccess} - - ezpublish.api.role.limitation_type.state: - class: eZ\Publish\Core\Limitation\ObjectStateLimitationType - arguments: ["@ezpublish.api.persistence_handler"] - tags: - - {name: ezpublish.limitationType, alias: State} - - ezpublish.api.role.limitation_type.new_state: - class: eZ\Publish\Core\Limitation\NewObjectStateLimitationType - arguments: ["@ezpublish.api.persistence_handler"] - tags: - - {name: ezpublish.limitationType, alias: NewState} - - ezpublish.api.role.limitation_type.subtree: - class: eZ\Publish\Core\Limitation\SubtreeLimitationType - arguments: ["@ezpublish.api.persistence_handler"] - tags: - - {name: ezpublish.limitationType, alias: Subtree} - - ezpublish.api.role.limitation_type.user_group: - class: eZ\Publish\Core\Limitation\UserGroupLimitationType - arguments: ["@ezpublish.api.persistence_handler"] - tags: - - {name: ezpublish.limitationType, alias: Group} - - ezpublish.api.role.limitation_type.status: - class: eZ\Publish\Core\Limitation\StatusLimitationType - tags: - - {name: ezpublish.limitationType, alias: Status} - - ## Non implemented Limitations - # Configured to use "blocking" limitation (as they are not implemented) to avoid LimitationNotFoundException - - # ezjscore limitations, not applicable by API/Platform stack, users are adviced to use Symfony for ajax controllers - ezpublish.api.role.limitation_type.function_list: - class: eZ\Publish\Core\Limitation\BlockingLimitationType - arguments: ['FunctionList'] - tags: [{name: ezpublish.limitationType, alias: FunctionList}] - - # Misc limitations used by ezcomments, not applicable to Platform replacement: EzCommentsBundle - ezpublish.api.role.limitation_type.ezcomments.content_section: - class: eZ\Publish\Core\Limitation\BlockingLimitationType - arguments: ['ContentSection'] - tags: [{name: ezpublish.limitationType, alias: ContentSection}] - - ezpublish.api.role.limitation_type.ezcomments.comment_creator: - class: eZ\Publish\Core\Limitation\BlockingLimitationType - arguments: ['CommentCreator'] - tags: [{name: ezpublish.limitationType, alias: CommentCreator}] - - ezpublish.api.role.limitation_type.ezcomments.anti_spam: - class: eZ\Publish\Core\Limitation\BlockingLimitationType - arguments: ['AntiSpam'] - tags: [{name: ezpublish.limitationType, alias: AntiSpam}] - - Ibexa\Core\Limitation\MemberOfLimitationType: - arguments: - $persistence: '@ezpublish.api.persistence_handler' - tags: - - { name: ezpublish.limitationType, alias: MemberOf } - - Ibexa\Core\Limitation\RoleLimitationType: - arguments: - $persistence: '@ezpublish.api.persistence_handler' - tags: - - { name: ezpublish.limitationType, alias: Role } diff --git a/eZ/Publish/Core/settings/search_engines/common.yml b/eZ/Publish/Core/settings/search_engines/common.yml deleted file mode 100644 index 65e812a317..0000000000 --- a/eZ/Publish/Core/settings/search_engines/common.yml +++ /dev/null @@ -1,58 +0,0 @@ -imports: - - {resource: search_engines/field_value_mappers.yml} - -parameters: - ezpublish.search.common.field_name_generator.map: - ez_integer: 'i' - ez_minteger: 'mi' - ez_id: 'id' - ez_mid: 'mid' - ez_string: 's' - ez_mstring: 'ms' - ez_long: 'l' - ez_text: 't' - ez_html: 'h' - ez_boolean: 'b' - ez_mboolean: 'mb' - ez_float: 'f' - ez_double: 'd' - ez_date: 'dt' - ez_point: 'p' - ez_currency: 'c' - ez_geolocation: 'gl' - ez_document: 'doc' - ez_fulltext: 'fulltext' - -services: - # Note: services tagged with 'ezplatform.field_type.indexable' - # are registered to this one using compilation pass - ezpublish.search.common.field_registry: - class: eZ\Publish\Core\Search\Common\FieldRegistry - - # Mapping for our internal search field types - ezpublish.search.common.field_name_generator: - class: eZ\Publish\Core\Search\Common\FieldNameGenerator - arguments: - - "%ezpublish.search.common.field_name_generator.map%" - - ezpublish.search.common.field_name_resolver: - class: eZ\Publish\Core\Search\Common\FieldNameResolver - arguments: - - "@ezpublish.search.common.field_registry" - - "@ezpublish.spi.persistence.content_type_handler" - - "@ezpublish.search.common.field_name_generator" - - # Note: services tagged with 'ezpublish.search.common.field_value_mapper' - # are registered to this one using compilation pass - ezpublish.search.common.field_value_mapper.aggregate: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\Aggregate - - eZ\Publish\Core\Search\Common\EventSubscriber\: - resource: '../../Search/Common/EventSubscriber/*' - exclude: '../../Search/Common/EventSubscriber/{AbstractSearchEventSubscriber.php}' - autoconfigure: true - autowire: true - public: false - arguments: - $searchHandler: '@ezpublish.spi.search' - $persistenceHandler: '@ezpublish.api.persistence_handler' diff --git a/eZ/Publish/Core/settings/search_engines/field_value_mappers.yml b/eZ/Publish/Core/settings/search_engines/field_value_mappers.yml deleted file mode 100644 index e07d8529be..0000000000 --- a/eZ/Publish/Core/settings/search_engines/field_value_mappers.yml +++ /dev/null @@ -1,75 +0,0 @@ -services: - ezpublish.search.common.field_value_mapper.boolean: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\BooleanMapper - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\BooleanField } - - ezpublish.search.common.field_value_mapper.date: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\DateMapper - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\DateField } - - ezpublish.search.common.field_value_mapper.document: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\DocumentMapper - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\DocumentField } - - ezpublish.search.common.field_value_mapper.geo_location: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\GeoLocationMapper - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\GeoLocationField } - - ezpublish.search.common.field_value_mapper.float: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\FloatMapper - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\FloatField } - - ezpublish.search.common.field_value_mapper.identifier: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\IdentifierMapper - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\IdentifierField } - - ezpublish.search.common.field_value_mapper.multiple_integer: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\MultipleIntegerMapper - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\MultipleIntegerField } - - ezpublish.search.common.field_value_mapper.integer: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\IntegerMapper - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\IntegerField } - - ezpublish.search.common.field_value_mapper.multiple_boolean: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\MultipleBooleanMapper - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\MultipleBooleanField } - - ezpublish.search.common.field_value_mapper.multiple_identifier: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\MultipleIdentifierMapper - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\MultipleIdentifierField } - - ezpublish.search.common.field_value_mapper.multiple_string: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\MultipleStringMapper - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\MultipleStringField } - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\TextField } - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\FullTextField } - - ezpublish.search.common.field_value_mapper.price: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\PriceMapper - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\PriceField } - - ezpublish.search.common.field_value_mapper.string: - class: eZ\Publish\Core\Search\Common\FieldValueMapper\StringMapper - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\StringField } - - eZ\Publish\Core\Search\Common\FieldValueMapper\RemoteIdentifierMapper: - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\RemoteIdentifierField } - - eZ\Publish\Core\Search\Common\FieldValueMapper\MultipleRemoteIdentifierMapper: - tags: - - { name: ezpublish.search.common.field_value_mapper, maps: eZ\Publish\SPI\Search\FieldType\MultipleRemoteIdentifierField } diff --git a/eZ/Publish/Core/settings/search_engines/legacy.yml b/eZ/Publish/Core/settings/search_engines/legacy.yml deleted file mode 100644 index 18f74ab62a..0000000000 --- a/eZ/Publish/Core/settings/search_engines/legacy.yml +++ /dev/null @@ -1,87 +0,0 @@ -imports: - - {resource: search_engines/legacy/criterion_handlers_common.yml} - - {resource: search_engines/legacy/criterion_handlers_content.yml} - - {resource: search_engines/legacy/criterion_handlers_location.yml} - - {resource: search_engines/legacy/indexer.yml} - - {resource: search_engines/legacy/sort_clause_handlers_common.yml} - - {resource: search_engines/legacy/sort_clause_handlers_content.yml} - - {resource: search_engines/legacy/sort_clause_handlers_location.yml} - - {resource: search_engines/common.yml} - -services: - # Aliasing storage connection in order to support sqlite://:memory: - # Otherwise it should be possible to use a separate database and/or database connection - ezpublish.api.search_engine.legacy.connection: - alias: ezpublish.api.storage_engine.legacy.connection - - ezpublish.search.legacy.gateway.content.inner: - class: eZ\Publish\Core\Search\Legacy\Content\Gateway\DoctrineDatabase - arguments: - $connection: '@ezpublish.persistence.connection' - $criteriaConverter: '@ezpublish.search.legacy.gateway.criteria_converter.content' - $sortClauseConverter: '@ezpublish.search.legacy.gateway.sort_clause_converter.content' - $languageHandler: '@ezpublish.spi.persistence.language_handler' - - ezpublish.search.legacy.gateway.content.exception_conversion: - class: eZ\Publish\Core\Search\Legacy\Content\Gateway\ExceptionConversion - arguments: - - "@ezpublish.search.legacy.gateway.content.inner" - - # To disable exception conversion layer override this alias so that it points to inner gateway - ezpublish.search.legacy.gateway.content: - alias: ezpublish.search.legacy.gateway.content.exception_conversion - - ezpublish.search.legacy.gateway.location.inner: - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\DoctrineDatabase - arguments: - $connection: "@ezpublish.persistence.connection" - $criteriaConverter: "@ezpublish.search.legacy.gateway.criteria_converter.location" - $sortClauseConverter: "@ezpublish.search.legacy.gateway.sort_clause_converter.location" - $languageHandler: "@ezpublish.spi.persistence.language_handler" - - ezpublish.search.legacy.gateway.location.exception_conversion: - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\ExceptionConversion - arguments: - - "@ezpublish.search.legacy.gateway.location.inner" - - # To disable exception conversion layer override this alias so that it points to inner gateway - ezpublish.search.legacy.gateway.location: - alias: ezpublish.search.legacy.gateway.location.exception_conversion - - ezpublish.search.legacy.mapper.fulltext: - class: eZ\Publish\Core\Search\Legacy\Content\Mapper\FullTextMapper - arguments: - - "@ezpublish.search.common.field_registry" - - "@ezpublish.spi.persistence.content_type_handler" - - ezpublish.search.legacy.fulltext_mapper: - alias: ezpublish.search.legacy.mapper.fulltext - - ezpublish.spi.search.legacy: - class: eZ\Publish\Core\Search\Legacy\Content\Handler - arguments: - $gateway: '@ezpublish.search.legacy.gateway.content' - $locationGateway: '@ezpublish.search.legacy.gateway.location' - $indexerGateway: '@eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase' - $contentMapper: '@ezpublish.persistence.legacy.content.mapper' - $locationMapper: '@ezpublish.persistence.legacy.location.mapper' - $languageHandler: '@ezpublish.spi.persistence.language_handler' - $mapper: '@ezpublish.search.legacy.fulltext_mapper' - tags: - - {name: ezplatform.search_engine, alias: legacy} - lazy: true - - ezpublish.spi.search.legacy.indexer: - class: eZ\Publish\Core\Search\Legacy\Content\Indexer - arguments: - $logger: '@logger' - $persistenceHandler: '@ezpublish.api.storage_engine' - $connection: '@ezpublish.persistence.connection' - $searchHandler: "@ezpublish.spi.search.legacy" - tags: - - {name: ezplatform.search_engine.indexer, alias: legacy} - lazy: true - - eZ\Publish\Core\Search\Legacy\Content\IndexerGateway: - arguments: - $connection: '@ezpublish.persistence.connection' diff --git a/eZ/Publish/Core/settings/search_engines/legacy/criterion_handlers_common.yml b/eZ/Publish/Core/settings/search_engines/legacy/criterion_handlers_common.yml deleted file mode 100644 index f4d1bcb597..0000000000 --- a/eZ/Publish/Core/settings/search_engines/legacy/criterion_handlers_common.yml +++ /dev/null @@ -1,341 +0,0 @@ -parameters: - # Full text search configuration options. - ezpublish.search.legacy.criterion_handler.full_text.configuration: - stopWordThresholdFactor: 0.66 - enableWildcards: true - commands: - - "ascii_search_cleanup" - - "cyrillic_diacritical" - - "cyrillic_search_cleanup" - - "cyrillic_transliterate_ascii" - - "doublequote_normalize" - - "endline_search_normalize" - - "greek_diacritical" - - "greek_normalize" - - "greek_transliterate_ascii" - - "hebrew_transliterate_ascii" - - "hyphen_normalize" - - "inverted_to_normal" - - "latin1_diacritical" - - "latin1_transliterate_ascii" - - "latin-exta_diacritical" - - "latin-exta_transliterate_ascii" - - "latin_search_cleanup" - - "latin_search_decompose" - - "math_to_ascii" - - "punctuation_normalize" - - "space_normalize" - - "special_decompose" - - "specialwords_search_normalize" - - "tab_search_normalize" - - "latin-exta_lowercase" - - "latin1_lowercase" - - "ascii_lowercase" - - "latin_lowercase" - - "cyrillic_lowercase" - - "greek_lowercase" - -services: - eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler: - abstract: true - arguments: - $connection: '@ezpublish.persistence.connection' - - eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldBase: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - abstract: true - arguments: - $contentTypeHandler: '@ezpublish.spi.persistence.content_type_handler' - $languageHandler: '@ezpublish.spi.persistence.language_handler' - - eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler: - abstract: true - arguments: - $connection: '@ezpublish.persistence.connection' - $transformationProcessor: '@ezpublish.api.storage_engine.transformation_processor' - - # BC - ezpublish.search.legacy.gateway.criterion_handler.base: '@eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler' - ezpublish.search.legacy.gateway.criterion_handler.field_base: '@eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldBase' - ezpublish.search.legacy.gateway.criterion_field_value_handler.base: '@eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler' - - # Criterion handlers - eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\CompositeCriterion: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.content_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentId - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.content_type_group_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentTypeGroupId - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.content_type_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentTypeId - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.criterion_handler} - - ezpublish.search.legacy.gateway.criterion_handler.common.content_type_identifier: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentTypeIdentifier - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - arguments: - $contentTypeHandler: '@ezpublish.spi.persistence.content_type_handler' - $logger: '@?logger' - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.date_metadata: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\DateMetadata - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.criterion_handler} - - ezpublish.search.legacy.gateway.criterion_handler.common.field: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\Field - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldBase - arguments: - $fieldConverterRegistry: '@ezpublish.persistence.legacy.field_value_converter.registry' - $fieldValueConverter: '@ezpublish.search.legacy.gateway.criterion_field_value_converter' - $transformationProcessor: '@ezpublish.api.storage_engine.transformation_processor' - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.field_empty: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldEmpty - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldBase - arguments: - $fieldConverterRegistry: '@ezpublish.persistence.legacy.field_value_converter.registry' - $fieldTypeService: '@ezpublish.api.service.field_type' - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.full_text: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FullText - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - arguments: - $processor: '@ezpublish.api.storage_engine.transformation_processor' - $languageMaskGenerator: '@ezpublish.persistence.legacy.language.mask_generator' - $configuration: '%ezpublish.search.legacy.criterion_handler.full_text.configuration%' - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.language_code: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LanguageCode - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - arguments: - $maskGenerator: '@ezpublish.persistence.legacy.language.mask_generator' - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.logical_and: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LogicalAnd - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.criterion_handler} - - ezpublish.search.legacy.gateway.criterion_handler.common.logical_not: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LogicalNot - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.criterion_handler} - - ezpublish.search.legacy.gateway.criterion_handler.common.logical_or: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LogicalOr - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.criterion_handler} - - ezpublish.search.legacy.gateway.criterion_handler.common.map_location_distance: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldBase - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\MapLocationDistance - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.match_all: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\MatchAll - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.criterion_handler} - - ezpublish.search.legacy.gateway.criterion_handler.common.match_none: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\MatchNone - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.criterion_handler} - - ezpublish.search.legacy.gateway.criterion_handler.common.object_state_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ObjectStateId - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.object_state_identifier: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ObjectStateIdentifier - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.field_relation: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldBase - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldRelation - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.remote_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\RemoteId - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.section_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\SectionId - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.criterion_handler} - - ezpublish.search.legacy.gateway.criterion_handler.common.section_identifier: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\SectionIdentifier - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.user_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserId - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.user_email: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserEmail - arguments: - $transformationProcessor: '@ezpublish.api.storage_engine.transformation_processor' - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.user_login: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserLogin - arguments: - $transformationProcessor: '@ezpublish.api.storage_engine.transformation_processor' - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.is_user_enabled: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\IsUserEnabled - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.common.user_metadata: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserMetadata - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.criterion_handler} - - ezpublish.search.legacy.gateway.criterion_handler.common.is_user_based: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\IsUserBased - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - # Criterion field value handlers - - # Note: services tagged with 'ezpublish.search.legacy.gateway.criterion_field_value_handler' - # are registered to this one using compilation pass - ezpublish.search.legacy.gateway.criterion_field_value_handler.registry: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\HandlerRegistry - - ezpublish.search.legacy.gateway.criterion_field_value_converter: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Converter - arguments: - - "@ezpublish.search.legacy.gateway.criterion_field_value_handler.registry" - - "@ezpublish.search.legacy.gateway.criterion_field_value_handler.default" - - ezpublish.search.legacy.gateway.criterion_field_value_handler.collection.comma_separated: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Collection - arguments: - $separator: ',' - tags: - - {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezauthor} - - {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezcountry} - - {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezobjectrelationlist} - - ezpublish.search.legacy.gateway.criterion_field_value_handler.keyword: - parent: ezpublish.search.legacy.gateway.criterion_field_value_handler.collection.comma_separated - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Keyword - tags: - - {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezkeyword} - - ezpublish.search.legacy.gateway.criterion_field_value_handler.collection.hypen_separated: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Collection - arguments: - $separator: '-' - tags: - - {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezselection} - - ezpublish.search.legacy.gateway.criterion_field_value_handler.composite: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Composite - - ezpublish.search.legacy.gateway.criterion_field_value_handler.simple: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Simple - tags: - - {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezboolean} - - {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezdate} - - {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezdatetime} - - {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezemail} - - {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezinteger} - - {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: ezobjectrelation} - - {name: ezpublish.search.legacy.gateway.criterion_field_value_handler, alias: eztime} - - ezpublish.search.legacy.gateway.criterion_field_value_handler.default: - alias: ezpublish.search.legacy.gateway.criterion_field_value_handler.composite diff --git a/eZ/Publish/Core/settings/search_engines/legacy/criterion_handlers_content.yml b/eZ/Publish/Core/settings/search_engines/legacy/criterion_handlers_content.yml deleted file mode 100644 index d6e439e509..0000000000 --- a/eZ/Publish/Core/settings/search_engines/legacy/criterion_handlers_content.yml +++ /dev/null @@ -1,51 +0,0 @@ -services: - # Note: services tagged with: - # - ezpublish.search.legacy.gateway.criterion_handler.content - # are registered to this one using compilation pass - ezpublish.search.legacy.gateway.criteria_converter.content: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter - - ezpublish.search.legacy.gateway.criterion_handler.content.ancestor: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\Ancestor - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - ezpublish.search.legacy.gateway.criterion_handler.content.location_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\LocationId - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - ezpublish.search.legacy.gateway.criterion_handler.content.location_remote_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\LocationRemoteId - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - ezpublish.search.legacy.gateway.criterion_handler.content.parent_location_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\ParentLocationId - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - # Needs to be before subtree, as permission_subtree extends it. - # Only needed for Content Search on SQL engines where applying Permissions Subtree criterion on all possible - # locations leads to peformance issues: https://jira.ez.no/browse/EZP-23037 - ezpublish.search.legacy.gateway.criterion_handler.content.permission_subtree: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\PermissionSubtree - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - ezpublish.search.legacy.gateway.criterion_handler.content.subtree: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\Subtree - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} - - ezpublish.search.legacy.gateway.criterion_handler.content.visibility: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\Visibility - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.content} diff --git a/eZ/Publish/Core/settings/search_engines/legacy/criterion_handlers_location.yml b/eZ/Publish/Core/settings/search_engines/legacy/criterion_handlers_location.yml deleted file mode 100644 index c146e73740..0000000000 --- a/eZ/Publish/Core/settings/search_engines/legacy/criterion_handlers_location.yml +++ /dev/null @@ -1,60 +0,0 @@ -services: - # Note: services tagged with: - # - ezpublish.search.legacy.gateway.criterion_handler.location - # are registered to this one using compilation pass - ezpublish.search.legacy.gateway.criteria_converter.location: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter - - ezpublish.search.legacy.gateway.criterion_handler.location.ancestor: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Ancestor - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.location.depth: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location\Depth - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.location.location_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\LocationId - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.location.is_main_location: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location\IsMainLocation - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.location.parent_location_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\ParentLocationId - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.location.priority: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location\Priority - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.location.location_remote_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\LocationRemoteId - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.location.subtree: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Subtree - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} - - ezpublish.search.legacy.gateway.criterion_handler.location.visibility: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Visibility - tags: - - {name: ezpublish.search.legacy.gateway.criterion_handler.location} diff --git a/eZ/Publish/Core/settings/search_engines/legacy/indexer.yml b/eZ/Publish/Core/settings/search_engines/legacy/indexer.yml deleted file mode 100644 index 926de7e602..0000000000 --- a/eZ/Publish/Core/settings/search_engines/legacy/indexer.yml +++ /dev/null @@ -1,13 +0,0 @@ -services: - eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase: - arguments: - $connection: '@ezpublish.persistence.connection' - $typeHandler: '@ezpublish.spi.persistence.content_type_handler' - $transformationProcessor: '@ezpublish.api.storage_engine.transformation_processor' - $searchIndex: '@eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Repository\SearchIndex' - $languageMaskGenerator: '@ezpublish.persistence.legacy.language.mask_generator' - $fullTextSearchConfiguration: '%ezpublish.search.legacy.criterion_handler.full_text.configuration%' - - eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Repository\SearchIndex: - arguments: - $connection: '@ezpublish.persistence.connection' diff --git a/eZ/Publish/Core/settings/search_engines/legacy/sort_clause_handlers_common.yml b/eZ/Publish/Core/settings/search_engines/legacy/sort_clause_handlers_common.yml deleted file mode 100644 index 4760f92942..0000000000 --- a/eZ/Publish/Core/settings/search_engines/legacy/sort_clause_handlers_common.yml +++ /dev/null @@ -1,111 +0,0 @@ -services: - eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler: - abstract: true - arguments: - $connection: '@ezpublish.persistence.connection' - - ezpublish.search.legacy.gateway.sort_clause_handler.common.content_id: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\ContentId - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.content} - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - ezpublish.search.legacy.gateway.sort_clause_handler.common.content_name: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\ContentName - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.content} - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.sort_clause_handler} - - ezpublish.search.legacy.gateway.sort_clause_handler.common.date_modified: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\DateModified - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.content} - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - ezpublish.search.legacy.gateway.sort_clause_handler.common.date_published: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\DatePublished - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.content} - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Field: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler - arguments: - $languageHandler: '@ezpublish.spi.persistence.language_handler' - $contentTypeHandler: '@ezpublish.spi.persistence.content_type_handler' - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.content} - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - ezpublish.search.legacy.gateway.sort_clause_handler.common.map_location_distance: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\MapLocationDistance - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Field - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.content} - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - ezpublish.search.legacy.gateway.sort_clause_handler.common.section_identifier: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\SectionIdentifier - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.content} - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - ezpublish.search.legacy.gateway.sort_clause_handler.common.section_name: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\SectionName - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.content} - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.sort_clause_handler} - - eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Factory\RandomSortClauseHandlerFactory: - arguments: - - '@ezpublish.persistence.connection' - - !tagged ezpublish.search.legacy.gateway.sort_clause_handler.gateway.random - - ezpublish.search.legacy.gateway.sort_clause_handler.common.random: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom - factory: ['@eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Factory\RandomSortClauseHandlerFactory', 'getGateway'] - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.content} - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Random\MySqlRandom: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.gateway.random} - - eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Random\SqlLiteRandom: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.gateway.random} - - eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Random\PgSqlRandom: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.gateway.random} - - eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Trash\ContentTypeName: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler - tags: - - {name: ezplatform.trash.search.legacy.gateway.sort_clause_handler} - - eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Trash\UserLogin: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler - tags: - - {name: ezplatform.trash.search.legacy.gateway.sort_clause_handler} - - eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Trash\DateTrashed: - parent: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler - tags: - - {name: ezplatform.trash.search.legacy.gateway.sort_clause_handler} - - # BC - ezpublish.search.legacy.gateway.sort_clause_handler.base: '@eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler' - ezpublish.search.legacy.gateway.sort_clause_handler.common.field: '@eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Field' diff --git a/eZ/Publish/Core/settings/search_engines/legacy/sort_clause_handlers_content.yml b/eZ/Publish/Core/settings/search_engines/legacy/sort_clause_handlers_content.yml deleted file mode 100644 index 531044ee21..0000000000 --- a/eZ/Publish/Core/settings/search_engines/legacy/sort_clause_handlers_content.yml +++ /dev/null @@ -1,6 +0,0 @@ -services: - # Note: services tagged with: - # - ezpublish.search.legacy.gateway.sort_clause_handler.content - # are registered to this one using compilation pass - ezpublish.search.legacy.gateway.sort_clause_converter.content: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter diff --git a/eZ/Publish/Core/settings/search_engines/legacy/sort_clause_handlers_location.yml b/eZ/Publish/Core/settings/search_engines/legacy/sort_clause_handlers_location.yml deleted file mode 100644 index b1a992377d..0000000000 --- a/eZ/Publish/Core/settings/search_engines/legacy/sort_clause_handlers_location.yml +++ /dev/null @@ -1,45 +0,0 @@ -services: - # Note: services tagged with: - # - ezpublish.search.legacy.gateway.sort_clause_handler.location - # are registered to this one using compilation pass - ezpublish.search.legacy.gateway.sort_clause_converter.location: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter - - ezpublish.search.legacy.gateway.sort_clause_handler.location.id: - parent: ezpublish.search.legacy.gateway.sort_clause_handler.base - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Id - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - ezpublish.search.legacy.gateway.sort_clause_handler.location.depth: - parent: ezpublish.search.legacy.gateway.sort_clause_handler.base - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Depth - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.sort_clause_handler} - - ezpublish.search.legacy.gateway.sort_clause_handler.location.path: - parent: ezpublish.search.legacy.gateway.sort_clause_handler.base - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Path - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.sort_clause_handler} - - ezpublish.search.legacy.gateway.sort_clause_handler.location.is_main_location: - parent: ezpublish.search.legacy.gateway.sort_clause_handler.base - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\IsMainLocation - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - ezpublish.search.legacy.gateway.sort_clause_handler.location.priority: - parent: ezpublish.search.legacy.gateway.sort_clause_handler.base - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Priority - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} - - {name: ezplatform.trash.search.legacy.gateway.sort_clause_handler} - - ezpublish.search.legacy.gateway.sort_clause_handler.location.visibility: - parent: ezpublish.search.legacy.gateway.sort_clause_handler.base - class: eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Visibility - tags: - - {name: ezpublish.search.legacy.gateway.sort_clause_handler.location} diff --git a/eZ/Publish/Core/settings/settings.yml b/eZ/Publish/Core/settings/settings.yml deleted file mode 100644 index 398baa8bbb..0000000000 --- a/eZ/Publish/Core/settings/settings.yml +++ /dev/null @@ -1,26 +0,0 @@ -parameters: - legacy_dsn: sqlite://:memory: - anonymous_user_id: 10 - kernel.debug: false - languages: [] - image_storage_prefix: images - image_draft_storage_prefix: images-versioned - binaryfile_storage_prefix: original - storage_dir: var/ezdemo_site/storage - io_root_dir: "%storage_dir%" - ezpublish.siteaccess.list: [test] - ezsettings.default.anonymous_user_id: 10 - -services: - ezpublish.api.persistence_handler: - #To disable cache, switch alias to ezpublish.api.storage_engine - alias: ezpublish.spi.persistence.cache - - ezpublish.api.storage_engine: - alias: ezpublish.spi.persistence.legacy - - ezpublish.spi.search: - alias: ezpublish.spi.search_engine - - ezpublish.spi.search_engine: - alias: ezpublish.spi.search.legacy diff --git a/eZ/Publish/Core/settings/storage_engines/cache.yml b/eZ/Publish/Core/settings/storage_engines/cache.yml deleted file mode 100644 index ca5b329eb9..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/cache.yml +++ /dev/null @@ -1,373 +0,0 @@ -parameters: - # Make sure logging is only enabled for debug by default - ezpublish.spi.persistence.cache.persistenceLogger.enableCallLogging: "%kernel.debug%" - # Global in-memory settings, for meta data - ezpublish.spi.persistence.cache.inmemory.ttl: 3000 - ezpublish.spi.persistence.cache.inmemory.limit: 100 - ezpublish.spi.persistence.cache.inmemory.enable: true - # Global in-memory settings, for content in-memory cache - ## WARNING: TTL on purpose low to avoid getting outdated data in prod! For dev config you can safely increase by x3 - ezpublish.spi.persistence.cache.inmemory.content.ttl: 300 - ezpublish.spi.persistence.cache.inmemory.content.limit: 100 - ezpublish.spi.persistence.cache.inmemory.content.enable: true - ibexa.core.persistence.cache.tag_prefix: 'ibx-' - ibexa.core.persistence.cache.tag_patterns: - by_group: 'bg-%s' - on_content: 'oc-%s' - bookmark: 'b-%s' - content: 'c-%s' - content_fields_type: 'cft-%s' - content_info: 'ci-%s' - content_info_by_remote_id: 'cibri-%s' - content_locations: 'cl-%s' - content_version_info: 'cvi-%s' - content_version_list: 'c-%s-vl' - content_version: 'c-%%s-v-%%s' - content_type: 'ct-%s' - content_type_with_by_remote_suffix: 'ct-%s-br' - content_type_with_id_suffix: 'ct-%s-bi' - content_type_field_map: 'ctfm' - content_type_group: 'ctg-%s' - content_type_group_with_id_suffix: 'ctg-%s-bi' - content_type_group_list: 'ctgl-%s' - content_type_list_by_group: 'ctlbg-%s' - image_variation: 'ig' - image_variation_name: 'ign-%s' - image_variation_siteaccess: 'igs-%s' - image_variation_content: 'igc-%s' - image_variation_field: 'igf-%s' - language: 'la-%s' - language_code: 'lac-%s' - language_list: 'lal' - location: 'l-%s' - location_path: 'lp-%s' - location_remote_id: 'lri' - location_subtree: 'ls-%s' - content_locations_with_parent_for_draft_suffix: 'cl-%s-pfd' - notification: 'n-%s' - notification_count: 'nc-%s' - notification_pending_count: 'npc-%s' - policy: 'p-%s' - role: 'r-%s' - role_assignment: 'ra-%s' - role_assignment_group_list: 'ragl-%s' - role_assignment_role_list: 'rarl-%s' - role_with_by_id_suffix: 'r-%s-bi' - role_assignment_with_by_role_suffix: 'ra-%s-bro' - role_assignment_with_by_role_offset_limit_suffix: 'ra-%%s-bro-%%s-%%s' - role_assignment_with_by_group_inherited_suffix: 'ra-%s-bgi' - role_assignment_with_by_group_suffix: 'ra-%s-bg' - section: 'se-%s' - section_with_by_id: 'se-%s-bi' - setting: 'set-%%s-%%s' - state: 's-%s' - state_by_group: 'sbg-%s' - state_group: 'sg-%s' - state_group_with_id_suffix: 'sg-%s-bi' - state_group_all: 'sga' - state_identifier: 'si-%s' - state_identifier_with_by_group_suffix: 'si-%%s-bg-%%s' - state_list_by_group: 'slbg-%s' - state_by_group_on_content: 'sbg-%%s-oc-%%s' - user: 'u-%s' - user_with_by_login_suffix: 'u-%s-bl' - user_with_by_email_suffix: 'u-%s-be' - users_with_by_email_suffix: 'us-%s-be' - user_with_account_key_suffix: 'u-%s-ak' - user_with_by_account_key_suffix: 'u-%s-bak' - url: 'url-%s' - url_alias: 'urla-%s' - url_alias_with_hash: 'urla-%%s-%%s' - url_alias_custom: 'urlac-%s' - url_alias_location: 'urlal-%s' - url_alias_location_list: 'urlall-%s' - url_alias_location_list_custom: 'urlall-%s-c' - url_alias_location_path: 'urlalp-%s' - url_alias_not_found: 'urlanf' - url_alias_url: 'urlau-%s' - url_wildcard: 'urlw-%s' - url_wildcard_not_found: 'urlwnf' - url_wildcard_source: 'urlws-%s' - user_preference: 'up' - user_preference_with_suffix: 'up-%%s-%%s' - type: 't-%s' - type_without_value: 't' - type_group: 'tg-%s' - type_map: 'tm' - version: 'v-%s' - ibexa.core.persistence.cache.key_patterns: - by_identifier_suffix: '-bi' - by_remote_suffix: '-br' - parent_for_draft_suffix: '-pfd' - by_login_suffix: '-bl' - by_email_suffix: '-be' - by_account_key_suffix: '-bak' - account_key_suffix: '-ak' - by_role_suffix: '-bro' - by_group_inherited_suffix: '-bgi' - by_group_suffix: '-bg' - on_content_suffix: '-oc' - custom_suffix: '-c' - by_group: 'bg-%s' - on_content: 'oc-%s' - bookmark: 'b-%s' - content: 'c-%s' - content_fields_type: 'cft-%s' - content_info: 'ci-%s' - content_info_by_remote_id: 'cibri-%s' - content_locations: 'cl-%s' - content_version_info: 'cvi-%s' - content_version_list: 'c-%s-vl' - content_version: 'c-%%s-v-%%s' - content_type: 'ct-%s' - content_type_with_by_remote_suffix: 'ct-%s-br' - content_type_with_id_suffix: 'ct-%s-bi' - content_type_field_map: 'ctfm' - content_type_group: 'ctg-%s' - content_type_group_with_id_suffix: 'ctg-%s-bi' - content_type_group_list: 'ctgl-%s' - content_type_list_by_group: 'ctlbg-%s' - image_variation: 'ig' - image_variation_name: 'ign-%s' - image_variation_siteaccess: 'igs-%s' - image_variation_content: 'igc-%s' - image_variation_field: 'igf-%s' - language: 'la-%s' - language_code: 'lac-%s' - language_list: 'lal' - location: 'l-%s' - location_path: 'lp-%s' - location_remote_id: 'lri' - location_subtree: 'ls-%s' - content_locations_with_parent_for_draft_suffix: 'cl-%s-pfd' - notification: 'n-%s' - notification_count: 'nc-%s' - notification_pending_count: 'npc-%s' - policy: 'p-%s' - role: 'r-%s' - role_assignment: 'ra-%s' - role_assignment_group_list: 'ragl-%s' - role_assignment_role_list: 'rarl-%s' - role_with_by_id_suffix: 'r-%s-bi' - role_assignment_with_by_role_suffix: 'ra-%s-bro' - role_assignment_with_by_role_offset_limit_suffix: 'ra-%%s-bro-%%s-%%s' - role_assignment_with_by_group_inherited_suffix: 'ra-%s-bgi' - role_assignment_with_by_group_suffix: 'ra-%s-bg' - section: 'se-%s' - section_with_by_id: 'se-%s-bi' - setting: 'set-%%s-%%s' - state: 's-%s' - state_by_group: 'sbg-%s' - state_group: 'sg-%s' - state_group_with_id_suffix: 'sg-%s-bi' - state_group_all: 'sga' - state_identifier: 'si-%s' - state_identifier_with_by_group_suffix: 'si-%%s-bg-%%s' - state_list_by_group: 'slbg-%s' - state_by_group_on_content: 'sbg-%%s-oc-%%s' - user: 'u-%s' - user_with_by_login_suffix: 'u-%s-bl' - user_with_by_email_suffix: 'u-%s-be' - users_with_by_email_suffix: 'us-%s-be' - user_with_account_key_suffix: 'u-%s-ak' - user_with_by_account_key_suffix: 'u-%s-bak' - url: 'url-%s' - url_alias: 'urla-%s' - url_alias_with_hash: 'urla-%%s-%%s' - url_alias_custom: 'urlac-%s' - url_alias_location: 'urlal-%s' - url_alias_location_list: 'urlall-%s' - url_alias_location_list_custom: 'urlall-%s-c' - url_alias_location_path: 'urlalp-%s' - url_alias_not_found: 'urlanf' - url_alias_url: 'urlau-%s' - url_wildcard: 'urlw-%s' - url_wildcard_not_found: 'urlwnf' - url_wildcard_source: 'urlws-%s' - user_preference: 'up' - user_preference_with_suffix: 'up-%%s-%%s' - type: 't-%s' - type_without_value: 't' - type_group: 'tg-%s' - type_map: 'tm' - version: 'v-%s' - -services: - # Setup cache pool, with InMemoryCacheAdapter as decorator - eZ\Publish\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter: - decorates: ezpublish.cache_pool - lazy: true - arguments: - $sharedPool: '@eZ\Publish\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter.inner' - $inMemoryPools: !tagged ez.spi.persistence.cache.inmemory - - ezpublish.cache_pool: - public: true - class: Symfony\Component\Cache\Adapter\TagAwareAdapter - arguments: ["@ezpublish.cache_pool.driver"] - - # Note for tests: Default changed to in-memory Array cache in tests/common.yml by default, and opt in for redis - # testing is defined in containerBuilder.php - ezpublish.cache_pool.driver: - public: false - class: Symfony\Component\Cache\Adapter\FilesystemAdapter - arguments: ["", 120] - - # Logger which logs info when in dev for Symfony web toolbar - ezpublish.spi.persistence.cache.persistenceLogger: - class: eZ\Publish\Core\Persistence\Cache\PersistenceLogger - arguments: - - "%ezpublish.spi.persistence.cache.persistenceLogger.enableCallLogging%" - - # In-Memory cache pools - ezpublish.spi.persistence.cache.inmemory: - public: false - class: eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache - arguments: - - "%ezpublish.spi.persistence.cache.inmemory.ttl%" - - "%ezpublish.spi.persistence.cache.inmemory.limit%" - - "%ezpublish.spi.persistence.cache.inmemory.enable%" - tags: ['ez.spi.persistence.cache.inmemory'] - - ezpublish.spi.persistence.cache.inmemory.content: - public: false - class: eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache - arguments: - - "%ezpublish.spi.persistence.cache.inmemory.content.ttl%" - - "%ezpublish.spi.persistence.cache.inmemory.content.limit%" - - "%ezpublish.spi.persistence.cache.inmemory.content.enable%" - tags: ['ez.spi.persistence.cache.inmemory'] - - Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface: - alias: Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGenerator - - Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierSanitizer: ~ - Ibexa\Core\Persistence\Cache\LocationPathConverter: ~ - - Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGenerator: - arguments: - $prefix: '%ibexa.core.persistence.cache.tag_prefix%' - $tagPatterns: '%ibexa.core.persistence.cache.tag_patterns%' - $keyPatterns: '%ibexa.core.persistence.cache.key_patterns%' - - # SPI Persistence Cache Handlers, incl abstracts - ezpublish.spi.persistence.cache.abstractHandler: - class: eZ\Publish\Core\Persistence\Cache\AbstractHandler - abstract: true - arguments: - - "@ezpublish.cache_pool" - - "@ezpublish.api.storage_engine" - - "@ezpublish.spi.persistence.cache.persistenceLogger" - - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface' - - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierSanitizer' - - '@Ibexa\Core\Persistence\Cache\LocationPathConverter' - - ezpublish.spi.persistence.cache.abstractInMemoryHandler: - class: eZ\Publish\Core\Persistence\Cache\AbstractInMemoryHandler - abstract: true - arguments: - - "@ezpublish.cache_pool" - - "@ezpublish.spi.persistence.cache.persistenceLogger" - - "@ezpublish.spi.persistence.cache.inmemory" - - ezpublish.spi.persistence.cache.abstractInMemoryPersistenceHandler: - parent: ezpublish.spi.persistence.cache.abstractInMemoryHandler - class: eZ\Publish\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler - abstract: true - arguments: - - "@ezpublish.api.storage_engine" - - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface' - - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierSanitizer' - - '@Ibexa\Core\Persistence\Cache\LocationPathConverter' - - '@?Ibexa\Core\Persistence\Cache\CacheIndicesValidatorInterface' - - ezpublish.spi.persistence.cache.sectionHandler: - class: eZ\Publish\Core\Persistence\Cache\SectionHandler - parent: ezpublish.spi.persistence.cache.abstractHandler - - ezpublish.spi.persistence.cache.locationHandler: - class: eZ\Publish\Core\Persistence\Cache\LocationHandler - parent: ezpublish.spi.persistence.cache.abstractInMemoryPersistenceHandler - arguments: # Overload argument to use content in-memory service - index_2: '@ezpublish.spi.persistence.cache.inmemory.content' - - ezpublish.spi.persistence.cache.contentHandler: - class: eZ\Publish\Core\Persistence\Cache\ContentHandler - parent: ezpublish.spi.persistence.cache.abstractInMemoryPersistenceHandler - arguments: # Overload argument to use content in-memory service - index_2: '@ezpublish.spi.persistence.cache.inmemory.content' - - ezpublish.spi.persistence.cache.objectStateHandler: - class: eZ\Publish\Core\Persistence\Cache\ObjectStateHandler - parent: ezpublish.spi.persistence.cache.abstractInMemoryPersistenceHandler - - ezpublish.spi.persistence.cache.contentLanguageHandler: - class: eZ\Publish\Core\Persistence\Cache\ContentLanguageHandler - parent: ezpublish.spi.persistence.cache.abstractInMemoryPersistenceHandler - - ezpublish.spi.persistence.cache.contentTypeHandler: - class: eZ\Publish\Core\Persistence\Cache\ContentTypeHandler - parent: ezpublish.spi.persistence.cache.abstractInMemoryPersistenceHandler - - ezpublish.spi.persistence.cache.userHandler: - class: eZ\Publish\Core\Persistence\Cache\UserHandler - parent: ezpublish.spi.persistence.cache.abstractInMemoryPersistenceHandler - - ezpublish.spi.persistence.cache.transactionhandler: - class: eZ\Publish\Core\Persistence\Cache\TransactionHandler - parent: ezpublish.spi.persistence.cache.abstractInMemoryPersistenceHandler - - ezpublish.spi.persistence.cache.trashHandler: - class: eZ\Publish\Core\Persistence\Cache\TrashHandler - parent: ezpublish.spi.persistence.cache.abstractHandler - - ezpublish.spi.persistence.cache.urlAliasHandler: - class: eZ\Publish\Core\Persistence\Cache\UrlAliasHandler - parent: ezpublish.spi.persistence.cache.abstractInMemoryPersistenceHandler - arguments: # Overload argument to use content in-memory service - index_2: '@ezpublish.spi.persistence.cache.inmemory.content' - - ezpublish.spi.persistence.cache.urlHandler: - class: eZ\Publish\Core\Persistence\Cache\URLHandler - parent: ezpublish.spi.persistence.cache.abstractHandler - - ezpublish.spi.persistence.cache.bookmarkHandler: - class: eZ\Publish\Core\Persistence\Cache\BookmarkHandler - parent: ezpublish.spi.persistence.cache.abstractHandler - - ezpublish.spi.persistence.cache.notificationHandler: - class: eZ\Publish\Core\Persistence\Cache\NotificationHandler - parent: ezpublish.spi.persistence.cache.abstractHandler - - ezpublish.spi.persistence.cache.userPreferenceHandler: - class: eZ\Publish\Core\Persistence\Cache\UserPreferenceHandler - parent: ezpublish.spi.persistence.cache.abstractInMemoryPersistenceHandler - - ezpublish.spi.persistence.cache.urlWildcardHandler: - class: eZ\Publish\Core\Persistence\Cache\UrlWildcardHandler - parent: ezpublish.spi.persistence.cache.abstractHandler - - eZ\Publish\Core\Persistence\Cache\SettingHandler: - parent: ezpublish.spi.persistence.cache.abstractInMemoryPersistenceHandler - - ezpublish.spi.persistence.cache: - class: eZ\Publish\Core\Persistence\Cache\Handler - arguments: - - "@ezpublish.api.storage_engine" - - "@ezpublish.spi.persistence.cache.sectionHandler" - - "@ezpublish.spi.persistence.cache.locationHandler" - - "@ezpublish.spi.persistence.cache.contentHandler" - - "@ezpublish.spi.persistence.cache.contentLanguageHandler" - - "@ezpublish.spi.persistence.cache.contentTypeHandler" - - "@ezpublish.spi.persistence.cache.userHandler" - - "@ezpublish.spi.persistence.cache.transactionhandler" - - "@ezpublish.spi.persistence.cache.trashHandler" - - "@ezpublish.spi.persistence.cache.urlAliasHandler" - - "@ezpublish.spi.persistence.cache.objectStateHandler" - - "@ezpublish.spi.persistence.cache.urlHandler" - - "@ezpublish.spi.persistence.cache.bookmarkHandler" - - '@ezpublish.spi.persistence.cache.notificationHandler' - - '@ezpublish.spi.persistence.cache.userPreferenceHandler' - - "@ezpublish.spi.persistence.cache.urlWildcardHandler" - - '@eZ\Publish\Core\Persistence\Cache\SettingHandler' - - "@ezpublish.spi.persistence.cache.persistenceLogger" diff --git a/eZ/Publish/Core/settings/storage_engines/common.yml b/eZ/Publish/Core/settings/storage_engines/common.yml deleted file mode 100644 index badbd57a12..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/common.yml +++ /dev/null @@ -1,70 +0,0 @@ -parameters: - # Using definition files: - - # Transformation rules resources - ezpublish.api.storage_engine.transformation_rules.resources: - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/ascii.tr" - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/basic.tr" - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/cyrillic.tr" - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/greek.tr" - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/hebrew.tr" - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/latin.tr" - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/search.tr" - - # Using preprocessed files: - - ezpublish.api.storage_engine.preprocessed_transformation_rules.resources: - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/ascii.tr.result" - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/basic.tr.result" - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/cyrillic.tr.result" - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/greek.tr.result" - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/hebrew.tr.result" - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/latin.tr.result" - - "%ezpublish.kernel.root_dir%/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/search.tr.result" - -services: - ezpublish.persistence.field_type_registry: - class: eZ\Publish\Core\Persistence\FieldTypeRegistry - - ezpublish.persistence.external_storage_registry: - class: eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry - - ezpublish.persistence.slug_converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter - arguments: - - '@ezpublish.api.storage_engine.transformation_processor' - - [] - - ezpublish.api.storage_engine.transformation_parser: - class: eZ\Publish\Core\Persistence\TransformationProcessor\DefinitionBased\Parser - - ezpublish.api.storage_engine.pcre_compiler: - class: eZ\Publish\Core\Persistence\TransformationProcessor\PcreCompiler - arguments: ["@ezpublish.api.storage_engine.transformation_converter"] - - ezpublish.api.storage_engine.transformation_converter: - class: eZ\Publish\Core\Persistence\Utf8Converter - - ezpublish.api.storage_engine.transformation_processor: - class: eZ\Publish\Core\Persistence\TransformationProcessor\PreprocessedBased - arguments: - # Using definition files: - # - # - "@ezpublish.api.storage_engine.transformation_parser" - # - "@ezpublish.api.storage_engine.pcre_compiler" - # - "%ezpublish.api.storage_engine.transformation_rules.resources%" - - # Using preprocessed files: - - "@ezpublish.api.storage_engine.pcre_compiler" - - "%ezpublish.api.storage_engine.preprocessed_transformation_rules.resources%" - - ezpublish.persistence.connection: - public: true # @todo should be private - alias: ezpublish.api.storage_engine.legacy.connection - - ibexa.repository.transaction_handler: - alias: ezpublish.spi.persistence.cache.transactionhandler - - eZ\Publish\SPI\Persistence\TransactionHandler: - alias: ibexa.repository.transaction_handler - diff --git a/eZ/Publish/Core/settings/storage_engines/legacy.yml b/eZ/Publish/Core/settings/storage_engines/legacy.yml deleted file mode 100644 index 81c0cd3540..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy.yml +++ /dev/null @@ -1,56 +0,0 @@ -imports: - - {resource: storage_engines/legacy/bookmark.yml} - - {resource: storage_engines/legacy/content.yml} - - {resource: storage_engines/legacy/content_type.yml} - - {resource: storage_engines/legacy/external_storage_gateways.yml} - - {resource: storage_engines/legacy/field_value_converters.yml} - - {resource: storage_engines/legacy/language.yml} - - {resource: storage_engines/legacy/location.yml} - - {resource: storage_engines/legacy/object_state.yml} - - {resource: storage_engines/legacy/filter.yaml} - - {resource: storage_engines/legacy/section.yml} - - {resource: storage_engines/legacy/shared_gateway.yaml} - - {resource: storage_engines/legacy/trash.yml} - - {resource: storage_engines/legacy/url_alias.yml} - - {resource: storage_engines/legacy/url_wildcard.yml} - - {resource: storage_engines/legacy/url.yml} - - {resource: storage_engines/legacy/url_criterion_handlers.yml} - - {resource: storage_engines/legacy/url_wildcard_criterion_handlers.yml} - - {resource: storage_engines/legacy/user.yml} - - {resource: storage_engines/legacy/notification.yml} - - {resource: storage_engines/legacy/user_preference.yml} - - {resource: storage_engines/legacy/setting.yml} - -services: - ezpublish.spi.persistence.legacy: - class: eZ\Publish\Core\Persistence\Legacy\Handler - arguments: - - "@ezpublish.spi.persistence.legacy.content.handler" - - "@ezpublish.spi.persistence.legacy.content_type.handler" - - "@ezpublish.spi.persistence.legacy.language.handler" - - "@ezpublish.spi.persistence.legacy.location.handler" - - "@ezpublish.spi.persistence.legacy.object_state.handler" - - "@ezpublish.spi.persistence.legacy.section.handler" - - "@ezpublish.spi.persistence.legacy.transactionhandler" - - "@ezpublish.spi.persistence.legacy.trash.handler" - - "@ezpublish.spi.persistence.legacy.url_alias.handler" - - "@ezpublish.spi.persistence.legacy.url_wildcard.handler" - - "@ezpublish.spi.persistence.legacy.user.handler" - - "@ezpublish.spi.persistence.legacy.url.handler" - - "@ezpublish.spi.persistence.legacy.bookmark.handler" - - "@ezpublish.spi.persistence.legacy.notification.handler" - - "@ezpublish.spi.persistence.legacy.user_preference.handler" - - "@ezpublish.spi.persistence.legacy.setting.handler" - tags: - - {name: ezpublish.storageEngine, alias: legacy} - lazy: true - public: true # @todo should be private - - ezpublish.api.storage_engine.legacy.connection: '@ezpublish.persistence.connection' - - ezpublish.spi.persistence.legacy.transactionhandler: - class: eZ\Publish\Core\Persistence\Legacy\TransactionHandler - arguments: - $connection: '@ezpublish.api.storage_engine.legacy.connection' - $contentTypeHandler: '@ezpublish.spi.persistence.legacy.content_type.handler.caching' - $languageHandler: '@ezpublish.spi.persistence.legacy.language.handler.caching' diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/bookmark.yml b/eZ/Publish/Core/settings/storage_engines/legacy/bookmark.yml deleted file mode 100644 index 3cacc08bbb..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/bookmark.yml +++ /dev/null @@ -1,19 +0,0 @@ -services: - eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase: - arguments: - $connection: '@ezpublish.persistence.connection' - - eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\ExceptionConversion: - arguments: - $innerGateway: '@eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase' - - eZ\Publish\Core\Persistence\Legacy\Bookmark\Mapper: ~ - - eZ\Publish\Core\Persistence\Legacy\Bookmark\Handler: - arguments: - $gateway: '@eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\ExceptionConversion' - $mapper: '@eZ\Publish\Core\Persistence\Legacy\Bookmark\Mapper' - lazy: true - - ezpublish.spi.persistence.legacy.bookmark.handler: - alias: 'eZ\Publish\Core\Persistence\Legacy\Bookmark\Handler' diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/content_type.yml b/eZ/Publish/Core/settings/storage_engines/legacy/content_type.yml deleted file mode 100644 index 951cc8f3fb..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/content_type.yml +++ /dev/null @@ -1,60 +0,0 @@ -services: - ezpublish.persistence.legacy.content_type.gateway.inner: - class: eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase - arguments: - - '@ezpublish.api.storage_engine.legacy.connection' - - '@eZ\Publish\Core\Persistence\Legacy\SharedGateway\Gateway' - - '@ezpublish.persistence.legacy.language.mask_generator' - - ezpublish.persistence.legacy.content_type.gateway.exception_conversion: - class: eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\ExceptionConversion - arguments: - - "@ezpublish.persistence.legacy.content_type.gateway.inner" - - # To disable exception conversion layer override this alias so that it points to inner gateway - ezpublish.persistence.legacy.content_type.gateway: - alias: ezpublish.persistence.legacy.content_type.gateway.exception_conversion - - ezpublish.persistence.legacy.content_type.mapper: - class: eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper - arguments: - - "@ezpublish.persistence.legacy.field_value_converter.registry" - - '@ezpublish.persistence.legacy.language.mask_generator' - - ezpublish.persistence.legacy.content_type.content_updater: - class: eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater - arguments: - - "@ezpublish.persistence.legacy.content.gateway" - - "@ezpublish.persistence.legacy.field_value_converter.registry" - - "@ezpublish.persistence.legacy.external_storage_handler" - - "@ezpublish.persistence.legacy.content.mapper" - - ezpublish.persistence.legacy.content_type.update_handler.base: - abstract: true - class: eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler - - ezpublish.persistence.legacy.content_type.update_handler.basic: - parent: ezpublish.persistence.legacy.content_type.update_handler.base - class: eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase - arguments: - - "@ezpublish.persistence.legacy.content_type.gateway" - - ezpublish.persistence.legacy.content_type.update_handler: - alias: ezpublish.persistence.legacy.content_type.update_handler.basic - - ezpublish.spi.persistence.legacy.content_type.handler.inner: - class: eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler - arguments: - - "@ezpublish.persistence.legacy.content_type.gateway" - - "@ezpublish.persistence.legacy.content_type.mapper" - - "@ezpublish.persistence.legacy.content_type.update_handler" - - ezpublish.spi.persistence.legacy.content_type.handler.caching: - class: eZ\Publish\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler - lazy: true - arguments: - - "@ezpublish.spi.persistence.legacy.content_type.handler.inner" - - "@ezpublish.spi.persistence.cache.inmemory" - - ezpublish.spi.persistence.legacy.content_type.handler: - alias: ezpublish.spi.persistence.legacy.content_type.handler.caching diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/external_storage_gateways.yml b/eZ/Publish/Core/settings/storage_engines/legacy/external_storage_gateways.yml deleted file mode 100644 index 88f6445d7b..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/external_storage_gateways.yml +++ /dev/null @@ -1,38 +0,0 @@ -services: - ezpublish.persistence.legacy.external_storage_handler: - class: eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler - arguments: - - "@ezpublish.persistence.external_storage_registry" - - - identifier: "LegacyStorage" - - ezpublish.fieldType.ezbinaryfile.storage_gateway: - class: eZ\Publish\Core\FieldType\BinaryFile\BinaryFileStorage\Gateway\DoctrineStorage - arguments: ["@ezpublish.api.storage_engine.legacy.connection"] - - ezpublish.fieldType.ezkeyword.storage_gateway: - class: eZ\Publish\Core\FieldType\Keyword\KeywordStorage\Gateway\DoctrineStorage - arguments: ["@ezpublish.api.storage_engine.legacy.connection"] - - ezpublish.fieldType.ezmedia.storage_gateway: - class: eZ\Publish\Core\FieldType\Media\MediaStorage\Gateway\DoctrineStorage - arguments: ["@ezpublish.api.storage_engine.legacy.connection"] - - ezpublish.fieldType.ezurl.storage_gateway: - class: eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage - arguments: ["@ezpublish.api.storage_engine.legacy.connection"] - - ezpublish.fieldType.ezimage.storage_gateway: - class: eZ\Publish\Core\FieldType\Image\ImageStorage\Gateway\DoctrineStorage - arguments: - - "@ezpublish.core.io.image_fieldtype.legacy_url_redecorator" - - "@ezpublish.api.storage_engine.legacy.connection" - - ezpublish.fieldType.externalStorageHandler.ezgmaplocation.gateway: - class: eZ\Publish\Core\FieldType\MapLocation\MapLocationStorage\Gateway\DoctrineStorage - arguments: ["@ezpublish.api.storage_engine.legacy.connection"] - - ezpublish.fieldType.ezuser.storage_gateway: - class: eZ\Publish\Core\FieldType\User\UserStorage\Gateway\DoctrineStorage - arguments: - - '@ezpublish.api.storage_engine.legacy.connection' diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/field_value_converters.yml b/eZ/Publish/Core/settings/storage_engines/legacy/field_value_converters.yml deleted file mode 100644 index ed57d73117..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/field_value_converters.yml +++ /dev/null @@ -1,222 +0,0 @@ -services: - # Note: converter services tagged with 'ezpublish.storageEngine.legacy.converter' or - # 'ezplatform.field_type.legacy_storage.converter' are registered to this one using compilation pass and factory - ezpublish.persistence.legacy.field_value_converter.registry: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry - lazy: true - - eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SerializableConverter: - arguments: - $serializer: '@eZ\Publish\SPI\FieldType\ValueSerializerInterface' - - ezpublish.fieldType.ezauthor.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezauthor} - - ezpublish.fieldType.ezbinaryfile.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\BinaryFileConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezbinaryfile} - - ezpublish.fieldType.ezboolean.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezboolean} - - ezpublish.fieldType.ezcountry.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezcountry} - - ezpublish.fieldType.ezdatetime.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezdatetime} - - ezpublish.fieldType.ezfloat.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\FloatConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezfloat} - - ezpublish.fieldType.ezinteger.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\IntegerConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezinteger} - - ezpublish.fieldType.ezkeyword.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezkeyword} - - ezpublish.fieldType.ezmedia.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\MediaConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezmedia} - - ezpublish.fieldType.ezselection.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter - arguments: - - '@ezpublish.api.service.language' - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezselection} - - ezpublish.fieldType.ezstring.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezstring} - - ezpublish.fieldType.eztext.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: eztext} - - ezpublish.fieldType.ezurl.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezurl} - - ezpublish.fieldType.ezimage.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageConverter - arguments: - - "@ezpublish.fieldType.ezimage.io_service" - - "@ezpublish.core.io.image_fieldtype.legacy_url_redecorator" - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezimage} - - ezpublish.fieldType.ezisbn.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ISBNConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezisbn} - - ezpublish.fieldType.ezgmaplocation.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\MapLocationConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezgmaplocation} - - ezpublish.fieldType.ezemail.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\EmailAddressConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezemail} - - ezpublish.fieldType.ezobjectrelation.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezobjectrelation} - - ezpublish.fieldType.ezobjectrelationlist.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationListConverter - arguments: - $connection: '@ezpublish.persistence.connection' - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezobjectrelationlist} - - ezpublish.fieldType.ezuser.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\UserConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezuser} - - # Not implemented converters - # Configured to use the Null converter which does not nothing - ezpublish.fieldType.ezdate.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezdate} - - ezpublish.fieldType.ezenum.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezenum} - - ezpublish.fieldType.ezidentifier.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezidentifier} - - ezpublish.fieldType.ezinisetting.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezinisetting} - - ezpublish.fieldType.ezmatrix.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezmatrix} - - ezpublish.fieldType.ezmultioption.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezmultioption} - - ezpublish.fieldType.ezmultioption2.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezmultioption2} - - ezpublish.fieldType.ezmultiprice.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezmultiprice} - - ezpublish.fieldType.ezoption.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezoption} - - ezpublish.fieldType.ezpackage.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezpackage} - - ezpublish.fieldType.ezproductcategory.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezproductcategory} - - ezpublish.fieldType.ezrangeoption.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezrangeoption} - - ezpublish.fieldType.ezsubtreesubscription.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezsubtreesubscription} - - ezpublish.fieldType.eztime.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: eztime} - - # not implemented converters from extensions - ezpublish.fieldType.ezcomcomments.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezcomcomments} - - ezpublish.fieldType.ezpaex.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezpaex} - - ezpublish.fieldType.ezsurvey.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezsurvey} - - ezpublish.fieldType.eztags.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: eztags} - - ezpublish.fieldType.ezrecommendation.converter: - class: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezrecommendation} - - eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageAssetConverter: - tags: - - {name: ezplatform.field_type.legacy_storage.converter, alias: ezimageasset} - - ezpublish.fieldType.ezimageasset.converter: - alias: eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageAssetConverter diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/filter.yaml b/eZ/Publish/Core/settings/storage_engines/legacy/filter.yaml deleted file mode 100644 index 0de2ff758d..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/filter.yaml +++ /dev/null @@ -1,58 +0,0 @@ -imports: - - { resource: filter/query_builders.yaml } - -services: - _defaults: - autowire: true - autoconfigure: true - public: false - - # injectables: - eZ\Publish\SPI\Persistence\Filter\Content\Handler: - alias: eZ\Publish\Core\Persistence\Legacy\Filter\Handler\ContentFilteringHandler - - eZ\Publish\SPI\Persistence\Filter\Location\Handler: - alias: eZ\Publish\Core\Persistence\Legacy\Filter\Handler\LocationFilteringHandler - - eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Content\GatewayDataMapper: - alias: eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Content\Mapper\DoctrineGatewayDataMapper - - eZ\Publish\SPI\Persistence\Filter\CriterionVisitor: - alias: eZ\Publish\Core\Persistence\Legacy\Filter\CriterionVisitor - - eZ\Publish\SPI\Persistence\Filter\SortClauseVisitor: - alias: eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseVisitor - - # implementations: - eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Content\Mapper\DoctrineGatewayDataMapper: - arguments: - $languageHandler: '@ezpublish.spi.persistence.language_handler' - $languageMaskGenerator: '@ezpublish.persistence.legacy.language.mask_generator' - $contentTypeHandler: '@ezpublish.spi.persistence.content_type_handler' - $converterRegistry: '@ezpublish.persistence.legacy.field_value_converter.registry' - - eZ\Publish\Core\Persistence\Legacy\Filter\CriterionVisitor: - arguments: - $criterionQueryBuilders: !tagged_iterator ezplatform.filter.criterion.query_builder - - eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseVisitor: - arguments: - $sortClauseQueryBuilders: !tagged_iterator ezplatform.filter.sort_clause.query_builder - - eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Content\Doctrine\DoctrineGateway: - arguments: - $connection: '@ezpublish.persistence.connection' - - eZ\Publish\Core\Persistence\Legacy\Filter\Handler\ContentFilteringHandler: - arguments: - $gateway: '@eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Content\Doctrine\DoctrineGateway' - $fieldHandler: '@ezpublish.persistence.legacy.field_handler' - - eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Location\Doctrine\DoctrineGateway: - arguments: - $connection: '@ezpublish.persistence.connection' - - eZ\Publish\Core\Persistence\Legacy\Filter\Handler\LocationFilteringHandler: - arguments: - $gateway: '@eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Location\Doctrine\DoctrineGateway' - $locationMapper: '@ezpublish.persistence.legacy.location.mapper' diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/filter/query_builders.yaml b/eZ/Publish/Core/settings/storage_engines/legacy/filter/query_builders.yaml deleted file mode 100644 index 663da66fbf..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/filter/query_builders.yaml +++ /dev/null @@ -1,14 +0,0 @@ -services: - _defaults: - autowire: true - autoconfigure: true - public: false - bind: - $transformationProcessor: '@ezpublish.api.storage_engine.transformation_processor' - - eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\: - resource: '../../../../Persistence/Legacy/Filter/CriterionQueryBuilder/*' - - eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\: - resource: '../../../../Persistence/Legacy/Filter/SortClauseQueryBuilder/*' - diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/language.yml b/eZ/Publish/Core/settings/storage_engines/legacy/language.yml deleted file mode 100644 index 15a03e3182..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/language.yml +++ /dev/null @@ -1,38 +0,0 @@ -services: - ezpublish.persistence.legacy.language.gateway.inner: - class: eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase - arguments: - - "@ezpublish.api.storage_engine.legacy.connection" - - ezpublish.persistence.legacy.language.gateway.exception_conversion: - class: eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\ExceptionConversion - arguments: - - "@ezpublish.persistence.legacy.language.gateway.inner" - - # To disable exception conversion layer override this alias so that it points to inner gateway - ezpublish.persistence.legacy.language.gateway: - alias: ezpublish.persistence.legacy.language.gateway.exception_conversion - - ezpublish.persistence.legacy.language.mapper: - class: eZ\Publish\Core\Persistence\Legacy\Content\Language\Mapper - - ezpublish.spi.persistence.legacy.language.handler.inner: - class: eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler - arguments: - - "@ezpublish.persistence.legacy.language.gateway" - - "@ezpublish.persistence.legacy.language.mapper" - - ezpublish.spi.persistence.legacy.language.handler.caching: - class: eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler - lazy: true - arguments: - - "@ezpublish.spi.persistence.legacy.language.handler.inner" - - "@ezpublish.spi.persistence.cache.inmemory" - - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface' - - ezpublish.spi.persistence.legacy.language.handler: - alias: ezpublish.spi.persistence.legacy.language.handler.caching - - ezpublish.persistence.legacy.language.mask_generator: - class: eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator - arguments: ["@ezpublish.spi.persistence.legacy.language.handler"] diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/location.yml b/eZ/Publish/Core/settings/storage_engines/legacy/location.yml deleted file mode 100644 index 8112562d11..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/location.yml +++ /dev/null @@ -1,30 +0,0 @@ -services: - ezpublish.persistence.legacy.location.gateway.inner: - class: eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase - arguments: - - "@ezpublish.api.storage_engine.legacy.connection" - - "@ezpublish.persistence.legacy.language.mask_generator" - - '@ezplatform.trash.search.legacy.gateway.criteria_converter' - - '@ezplatform.trash.search.legacy.gateway.sort_clause_converter' - - ezpublish.persistence.legacy.location.gateway.exception_conversion: - class: eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\ExceptionConversion - arguments: - - "@ezpublish.persistence.legacy.location.gateway.inner" - - # To disable exception conversion layer override this alias so that it points to inner gateway - ezpublish.persistence.legacy.location.gateway: - alias: ezpublish.persistence.legacy.location.gateway.exception_conversion - - ezpublish.persistence.legacy.location.mapper: - class: eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper - - ezpublish.spi.persistence.legacy.location.handler: - class: eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler - arguments: - - "@ezpublish.persistence.legacy.location.gateway" - - "@ezpublish.persistence.legacy.location.mapper" - - "@ezpublish.spi.persistence.legacy.content.handler" - - "@ezpublish.spi.persistence.legacy.object_state.handler" - - "@ezpublish.persistence.legacy.tree_handler" - lazy: true diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/notification.yml b/eZ/Publish/Core/settings/storage_engines/legacy/notification.yml deleted file mode 100644 index aaf0c45ec5..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/notification.yml +++ /dev/null @@ -1,19 +0,0 @@ -services: - eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase: - arguments: - $connection: '@ezpublish.persistence.connection' - - eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\ExceptionConversion: - arguments: - $innerGateway: '@eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase' - - eZ\Publish\Core\Persistence\Legacy\Notification\Mapper: ~ - - eZ\Publish\Core\Persistence\Legacy\Notification\Handler: - arguments: - $gateway: '@eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\ExceptionConversion' - $mapper: '@eZ\Publish\Core\Persistence\Legacy\Notification\Mapper' - lazy: true - - ezpublish.spi.persistence.legacy.notification.handler: - alias: 'eZ\Publish\Core\Persistence\Legacy\Notification\Handler' diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/object_state.yml b/eZ/Publish/Core/settings/storage_engines/legacy/object_state.yml deleted file mode 100644 index 51555304b5..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/object_state.yml +++ /dev/null @@ -1,27 +0,0 @@ -services: - ezpublish.persistence.legacy.object_state.gateway.inner: - class: eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase - arguments: - - "@ezpublish.api.storage_engine.legacy.connection" - - "@ezpublish.persistence.legacy.language.mask_generator" - - ezpublish.persistence.legacy.object_state.gateway.exception_conversion: - class: eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\ExceptionConversion - arguments: - - "@ezpublish.persistence.legacy.object_state.gateway.inner" - - # To disable exception conversion layer override this alias so that it points to inner gateway - ezpublish.persistence.legacy.object_state.gateway: - alias: ezpublish.persistence.legacy.object_state.gateway.exception_conversion - - ezpublish.persistence.legacy.object_state.mapper: - class: eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper - arguments: - - "@ezpublish.spi.persistence.legacy.language.handler" - - ezpublish.spi.persistence.legacy.object_state.handler: - class: eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler - arguments: - - "@ezpublish.persistence.legacy.object_state.gateway" - - "@ezpublish.persistence.legacy.object_state.mapper" - lazy: true diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/section.yml b/eZ/Publish/Core/settings/storage_engines/legacy/section.yml deleted file mode 100644 index 231f801c76..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/section.yml +++ /dev/null @@ -1,20 +0,0 @@ -services: - ezpublish.persistence.legacy.section.gateway.inner: - class: eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase - arguments: - - "@ezpublish.api.storage_engine.legacy.connection" - - ezpublish.persistence.legacy.section.gateway.exception_conversion: - class: eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\ExceptionConversion - arguments: - - "@ezpublish.persistence.legacy.section.gateway.inner" - - # To disable exception conversion layer override this alias so that it points to inner gateway - ezpublish.persistence.legacy.section.gateway: - alias: ezpublish.persistence.legacy.section.gateway.exception_conversion - - ezpublish.spi.persistence.legacy.section.handler: - class: eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler - arguments: - - "@ezpublish.persistence.legacy.section.gateway" - lazy: true diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/setting.yml b/eZ/Publish/Core/settings/storage_engines/legacy/setting.yml deleted file mode 100644 index 3bc0630275..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/setting.yml +++ /dev/null @@ -1,20 +0,0 @@ -services: - ezpublish.persistence.legacy.setting.gateway.inner: - class: eZ\Publish\Core\Persistence\Legacy\Setting\Gateway\DoctrineDatabase - arguments: - - '@ezpublish.api.storage_engine.legacy.connection' - - ezpublish.persistence.legacy.setting.gateway.exception_conversion: - class: eZ\Publish\Core\Persistence\Legacy\Setting\Gateway\ExceptionConversion - arguments: - - '@ezpublish.persistence.legacy.setting.gateway.inner' - - # To disable exception conversion layer override this alias so that it points to inner gateway - ezpublish.persistence.legacy.setting.gateway: - alias: ezpublish.persistence.legacy.setting.gateway.exception_conversion - - ezpublish.spi.persistence.legacy.setting.handler: - class: eZ\Publish\Core\Persistence\Legacy\Setting\Handler - arguments: - - '@ezpublish.persistence.legacy.setting.gateway' - lazy: true diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/shared_gateway.yaml b/eZ/Publish/Core/settings/storage_engines/legacy/shared_gateway.yaml deleted file mode 100644 index 669b6d4832..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/shared_gateway.yaml +++ /dev/null @@ -1,21 +0,0 @@ -services: - _defaults: - autowire: true - autoconfigure: true - public: false - bind: - $connection: '@ezpublish.persistence.connection' - - eZ\Publish\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\FallbackGateway: ~ - - eZ\Publish\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\SqliteGateway: - tags: - - { name: ezplatform.persistence.legacy.gateway.shared, platform: sqlite } - - eZ\Publish\Core\Persistence\Legacy\SharedGateway\GatewayFactory: - arguments: - $fallbackGateway: '@eZ\Publish\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\FallbackGateway' - $gateways: !tagged_iterator { tag: ezplatform.persistence.legacy.gateway.shared, index_by: platform } - - eZ\Publish\Core\Persistence\Legacy\SharedGateway\Gateway: - factory: ['@eZ\Publish\Core\Persistence\Legacy\SharedGateway\GatewayFactory', 'buildSharedGateway'] diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/trash.yml b/eZ/Publish/Core/settings/storage_engines/legacy/trash.yml deleted file mode 100644 index b1eb9029cc..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/trash.yml +++ /dev/null @@ -1,18 +0,0 @@ -services: - ezpublish.spi.persistence.legacy.trash.handler: - class: eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler - arguments: - - "@ezpublish.spi.persistence.legacy.location.handler" - - "@ezpublish.persistence.legacy.location.gateway.exception_conversion" - - "@ezpublish.persistence.legacy.location.mapper" - - "@ezpublish.spi.persistence.legacy.content.handler" - lazy: true - - # reusing parts of LSE - ezplatform.trash.search.legacy.gateway.criteria_converter: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter - lazy: true - - ezplatform.trash.search.legacy.gateway.sort_clause_converter: - class: eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter - lazy: true diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/url.yml b/eZ/Publish/Core/settings/storage_engines/legacy/url.yml deleted file mode 100644 index 8fa820bb0c..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/url.yml +++ /dev/null @@ -1,30 +0,0 @@ -services: - ezpublish.persistence.legacy.url.gateway.inner: - class: eZ\Publish\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase - arguments: - - '@ezpublish.persistence.connection' - - '@ezpublish.spi.persistence.legacy.url.criterion_converter' - - ezpublish.persistence.legacy.url.gateway.exception_conversion: - class: eZ\Publish\Core\Persistence\Legacy\URL\Gateway\ExceptionConversion - arguments: - - '@ezpublish.persistence.legacy.url.gateway.inner' - - # To disable exception conversion layer override this alias so that it points to inner gateway - ezpublish.persistence.legacy.url.gateway: - alias: ezpublish.persistence.legacy.url.gateway.exception_conversion - - ezpublish.persistence.legacy.url.mapper: - class: eZ\Publish\Core\Persistence\Legacy\URL\Mapper - - ezpublish.spi.persistence.legacy.url.handler: - class: eZ\Publish\Core\Persistence\Legacy\URL\Handler - arguments: - - "@ezpublish.persistence.legacy.url.gateway" - - "@ezpublish.persistence.legacy.url.mapper" - lazy: true - - ezpublish.spi.persistence.legacy.url.criterion_converter: - class: eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter - - diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/url_alias.yml b/eZ/Publish/Core/settings/storage_engines/legacy/url_alias.yml deleted file mode 100644 index 56f6eccde6..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/url_alias.yml +++ /dev/null @@ -1,33 +0,0 @@ -services: - ezpublish.persistence.legacy.url_alias.gateway.inner: - class: eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase - arguments: - - "@ezpublish.api.storage_engine.legacy.connection" - - "@ezpublish.persistence.legacy.language.mask_generator" - - ezpublish.persistence.legacy.url_alias.gateway.exception_conversion: - class: eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\ExceptionConversion - arguments: - - "@ezpublish.persistence.legacy.url_alias.gateway.inner" - - # To disable exception conversion layer override this alias so that it points to inner gateway - ezpublish.persistence.legacy.url_alias.gateway: - alias: ezpublish.persistence.legacy.url_alias.gateway.exception_conversion - - ezpublish.persistence.legacy.url_alias.mapper: - class: eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Mapper - arguments: - - "@ezpublish.persistence.legacy.language.mask_generator" - - ezpublish.spi.persistence.legacy.url_alias.handler: - class: eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler - arguments: - - "@ezpublish.persistence.legacy.url_alias.gateway" - - "@ezpublish.persistence.legacy.url_alias.mapper" - - "@ezpublish.persistence.legacy.location.gateway" - - "@ezpublish.spi.persistence.legacy.language.handler" - - "@ezpublish.persistence.slug_converter" - - "@ezpublish.persistence.legacy.content.gateway" - - "@ezpublish.persistence.legacy.language.mask_generator" - - "@ezpublish.spi.persistence.legacy.transactionhandler" - lazy: true diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/url_criterion_handlers.yml b/eZ/Publish/Core/settings/storage_engines/legacy/url_criterion_handlers.yml deleted file mode 100644 index 2409b084f1..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/url_criterion_handlers.yml +++ /dev/null @@ -1,63 +0,0 @@ -services: - ezpublish.persistence.legacy.url.criterion_handler.base: - abstract: true - - ezpublish.persistence.legacy.url.criterion_handler.logical_and: - parent: ezpublish.persistence.legacy.url.criterion_handler.base - class: eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalAnd - tags: - - { name: ezpublish.persistence.legacy.url.criterion_handler } - - ezpublish.persistence.legacy.url.criterion_handler.logical_or: - parent: ezpublish.persistence.legacy.url.criterion_handler.base - class: eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalOr - tags: - - { name: ezpublish.persistence.legacy.url.criterion_handler } - - ezpublish.persistence.legacy.url.criterion_handler.logical_not: - parent: ezpublish.persistence.legacy.url.criterion_handler.base - class: eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalNot - tags: - - { name: ezpublish.persistence.legacy.url.criterion_handler } - - ezpublish.persistence.legacy.url.criterion_handler.match_all: - parent: ezpublish.persistence.legacy.url.criterion_handler.base - class: eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\MatchAll - tags: - - { name: ezpublish.persistence.legacy.url.criterion_handler } - - ezpublish.persistence.legacy.url.criterion_handler.match_none: - parent: ezpublish.persistence.legacy.url.criterion_handler.base - class: eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\MatchNone - tags: - - { name: ezpublish.persistence.legacy.url.criterion_handler } - - ezpublish.persistence.legacy.url.criterion_handler.validity: - parent: ezpublish.persistence.legacy.url.criterion_handler.base - class: eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\Validity - tags: - - { name: ezpublish.persistence.legacy.url.criterion_handler } - - ezpublish.persistence.legacy.url.criterion_handler.pattern: - parent: ezpublish.persistence.legacy.url.criterion_handler.base - class: eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\Pattern - tags: - - { name: ezpublish.persistence.legacy.url.criterion_handler } - - ezpublish.persistence.legacy.url.criterion_handler.visible_only: - parent: ezpublish.persistence.legacy.url.criterion_handler.base - class: eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\VisibleOnly - tags: - - { name: ezpublish.persistence.legacy.url.criterion_handler } - - ezpublish.persistence.legacy.url.criterion_handler.section_id: - parent: ezpublish.persistence.legacy.url.criterion_handler.base - class: eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\SectionId - tags: - - { name: ezpublish.persistence.legacy.url.criterion_handler } - - ezpublish.persistence.legacy.url.criterion_handler.section_identifier: - parent: ezpublish.persistence.legacy.url.criterion_handler.base - class: eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\SectionIdentifier - tags: - - { name: ezpublish.persistence.legacy.url.criterion_handler } diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/url_wildcard.yml b/eZ/Publish/Core/settings/storage_engines/legacy/url_wildcard.yml deleted file mode 100644 index 61671fb4dd..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/url_wildcard.yml +++ /dev/null @@ -1,29 +0,0 @@ -services: - ezpublish.persistence.legacy.url_wildcard.gateway.inner: - class: eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase - arguments: - - '@ezpublish.api.storage_engine.legacy.connection' - - '@Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriteriaConverter' - - ezpublish.persistence.legacy.url_wildcard.gateway.exception_conversion: - class: eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\ExceptionConversion - arguments: - - "@ezpublish.persistence.legacy.url_wildcard.gateway.inner" - - # To disable exception conversion layer override this alias so that it points to inner gateway - ezpublish.persistence.legacy.url_wildcard.gateway: - alias: ezpublish.persistence.legacy.url_wildcard.gateway.exception_conversion - - ezpublish.persistence.legacy.url_wildcard.mapper: - class: eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Mapper - - ezpublish.spi.persistence.legacy.url_wildcard.handler: - class: eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Handler - arguments: - - "@ezpublish.persistence.legacy.url_wildcard.gateway" - - "@ezpublish.persistence.legacy.url_wildcard.mapper" - lazy: true - - Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriteriaConverter: - arguments: - $handlers: !tagged_iterator ibexa.storage.legacy.url_wildcard.criterion.handler diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/user.yml b/eZ/Publish/Core/settings/storage_engines/legacy/user.yml deleted file mode 100644 index c23163bedb..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/user.yml +++ /dev/null @@ -1,57 +0,0 @@ -services: - ezpublish.persistence.legacy.user.gateway.inner: - class: eZ\Publish\Core\Persistence\Legacy\User\Gateway\DoctrineDatabase - arguments: - - '@ezpublish.api.storage_engine.legacy.connection' - - ezpublish.persistence.legacy.user.gateway.exception_conversion: - class: eZ\Publish\Core\Persistence\Legacy\User\Gateway\ExceptionConversion - arguments: - - "@ezpublish.persistence.legacy.user.gateway.inner" - - # To disable exception conversion layer override this alias so that it points to inner gateway - ezpublish.persistence.legacy.user.gateway: - alias: ezpublish.persistence.legacy.user.gateway.exception_conversion - - ezpublish.persistence.legacy.role.gateway.inner: - class: eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase - arguments: - - '@ezpublish.api.storage_engine.legacy.connection' - - ezpublish.persistence.legacy.role.gateway.exception_conversion: - class: eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway\ExceptionConversion - arguments: - - "@ezpublish.persistence.legacy.role.gateway.inner" - - # To disable exception conversion layer override this alias so that it points to inner gateway - ezpublish.persistence.legacy.role.gateway: - alias: ezpublish.persistence.legacy.role.gateway.exception_conversion - - ezpublish.persistence.legacy.user.mapper: - class: eZ\Publish\Core\Persistence\Legacy\User\Mapper - - ezpublish.persistence.legacy.role.limitation.handler: - abstract: true - class: eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationHandler - arguments: - $connection: '@ezpublish.api.storage_engine.legacy.connection' - - ezpublish.persistence.legacy.role.limitation.handler.object_state: - parent: ezpublish.persistence.legacy.role.limitation.handler - class: eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationHandler\ObjectStateHandler - tags: - - {name: ezpublish.persistence.legacy.role.limitation.handler} - - # Note: services tagged with 'ezpublish.persistence.legacy.role.limitation.handler' - # are registered to this one using compilation pass - ezpublish.persistence.legacy.role.limitation.converter: - class: eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter - - ezpublish.spi.persistence.legacy.user.handler: - class: eZ\Publish\Core\Persistence\Legacy\User\Handler - arguments: - - "@ezpublish.persistence.legacy.user.gateway" - - "@ezpublish.persistence.legacy.role.gateway" - - "@ezpublish.persistence.legacy.user.mapper" - - "@ezpublish.persistence.legacy.role.limitation.converter" - lazy: true diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/user_preference.yml b/eZ/Publish/Core/settings/storage_engines/legacy/user_preference.yml deleted file mode 100644 index dd5cf422e7..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/legacy/user_preference.yml +++ /dev/null @@ -1,19 +0,0 @@ -services: - eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway\DoctrineDatabase: - arguments: - $connection: '@ezpublish.persistence.connection' - - eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway\ExceptionConversion: - arguments: - $innerGateway: '@eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway\DoctrineDatabase' - - eZ\Publish\Core\Persistence\Legacy\UserPreference\Mapper: ~ - - eZ\Publish\Core\Persistence\Legacy\UserPreference\Handler: - arguments: - $gateway: '@eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway\ExceptionConversion' - $mapper: '@eZ\Publish\Core\Persistence\Legacy\UserPreference\Mapper' - lazy: true - - ezpublish.spi.persistence.legacy.user_preference.handler: - alias: 'eZ\Publish\Core\Persistence\Legacy\UserPreference\Handler' diff --git a/eZ/Publish/Core/settings/storage_engines/shortcuts.yml b/eZ/Publish/Core/settings/storage_engines/shortcuts.yml deleted file mode 100644 index f9314448c6..0000000000 --- a/eZ/Publish/Core/settings/storage_engines/shortcuts.yml +++ /dev/null @@ -1,48 +0,0 @@ -services: - ezpublish.spi.persistence.content_handler: - class: eZ\Publish\SPI\Persistence\Content\Handler - factory: ["@ezpublish.api.persistence_handler", contentHandler] - - ezpublish.spi.persistence.content_type_handler: - class: eZ\Publish\SPI\Persistence\Content\Type\Handler - factory: ["@ezpublish.api.persistence_handler", contentTypeHandler] - - ezpublish.spi.persistence.language_handler: - class: eZ\Publish\SPI\Persistence\Content\Language\Handler - factory: ["@ezpublish.api.persistence_handler", contentLanguageHandler] - - ezpublish.spi.persistence.location_handler: - class: eZ\Publish\SPI\Persistence\Content\Location\Handler - factory: ["@ezpublish.api.persistence_handler", locationHandler] - - ezpublish.spi.persistence.object_state_handler: - class: eZ\Publish\SPI\Persistence\Content\ObjectState\Handler - factory: ["@ezpublish.api.persistence_handler", objectStateHandler] - - ezpublish.spi.persistence.section_handler: - class: eZ\Publish\SPI\Persistence\Content\Section\Handler - factory: ["@ezpublish.api.persistence_handler", sectionHandler] - - ezpublish.spi.persistence.trash_handler: - class: eZ\Publish\SPI\Persistence\Content\Location\Trash\Handler - factory: ["@ezpublish.api.persistence_handler", trashHandler] - - ezpublish.spi.persistence.url_alias_handler: - class: eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler - factory: ["@ezpublish.api.persistence_handler", urlAliasHandler] - - ezpublish.spi.persistence.url_wildcard_handler: - class: eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler - factory: ["@ezpublish.api.persistence_handler", urlWildcardHandler] - - ezpublish.spi.persistence.user_handler: - class: eZ\Publish\SPI\Persistence\User\Handler - factory: ["@ezpublish.api.persistence_handler", userHandler] - - ezpublish.spi.persistence.bookmark_handler: - class: eZ\Publish\SPI\Persistence\Bookmark\Handler - factory: ["@ezpublish.api.persistence_handler", bookmarkHandler] - - ezpublish.spi.persistence.user_preference_handler: - class: eZ\Publish\SPI\Persistence\UserPreference\Handler - factory: ["@ezpublish.api.persistence_handler", userPreferenceHandler] diff --git a/eZ/Publish/Core/settings/tests/common.yml b/eZ/Publish/Core/settings/tests/common.yml deleted file mode 100644 index 762f328489..0000000000 --- a/eZ/Publish/Core/settings/tests/common.yml +++ /dev/null @@ -1,78 +0,0 @@ -parameters: - ezsettings.default.io.file_storage.file_type_blacklist: - - php - - php3 - - phar - - phpt - - pht - - phtml - - pgif - -services: - logger: - class: Psr\Log\NullLogger - - Symfony\Component\EventDispatcher\EventDispatcher: ~ - Symfony\Contracts\EventDispatcher\EventDispatcherInterface: '@Symfony\Component\EventDispatcher\EventDispatcher' - - # By default use in-memory cache for tests to avoid disk IO but still make sure we tests cache clearing works - ezpublish.cache_pool.driver: - class: Symfony\Component\Cache\Adapter\ArrayAdapter - arguments: [120, false] - - # Override Slug Converter service to expose mutating Service configuration - ezpublish.persistence.slug_converter: - class: eZ\Publish\API\Repository\Tests\Common\SlugConverter - arguments: - - '@ezpublish.api.storage_engine.transformation_processor' - - [] - - # Configure serializer required Generic Field Type - ezpublish.field_type.ezgeneric.value_serializer.symfony.normalizer: - class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer - - ezpublish.field_type.ezgeneric.value_serializer.symfony.encoder: - class: Symfony\Component\Serializer\Encoder\JsonEncoder - - ezpublish.field_type.ezgeneric.value_serializer.symfony.serializer: - class: Symfony\Component\Serializer\Serializer - arguments: - - [ '@ezpublish.field_type.ezgeneric.value_serializer.symfony.normalizer' ] - - [ '@ezpublish.field_type.ezgeneric.value_serializer.symfony.encoder' ] - - serializer: - alias: ezpublish.field_type.ezgeneric.value_serializer.symfony.serializer - - eZ\Publish\SPI\Tests\Variation\InMemoryVariationHandler: ~ - eZ\Publish\SPI\Variation\VariationHandler: '@eZ\Publish\SPI\Tests\Variation\InMemoryVariationHandler' - - ezpublish.config.resolver: - class: eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver - arguments: - - '@logger' - - [] - - 'ezsettings' - calls: - - [setSiteAccess, ['@ezpublish.siteaccess']] - - [setContainer, ['@service_container']] - - [setDefaultScope, ['default']] - - ezpublish.siteaccess: - class: eZ\Publish\Core\MVC\Symfony\SiteAccess - arguments: ['default', 'uninitialized'] - - ezpublish.siteaccess_service: - class: eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessService - arguments: - - '@ezpublish.siteaccess.provider' - - '@ezpublish.config.resolver' - calls: - - [setSiteAccess, ['@ezpublish.siteaccess']] - - ezpublish.siteaccess.provider.chain: - class: eZ\Publish\Core\MVC\Symfony\SiteAccess\Provider\ChainSiteAccessProvider - arguments: - $providers: !tagged ezplatform.siteaccess.provider - - ezpublish.siteaccess.provider: - alias: ezpublish.siteaccess.provider.chain diff --git a/eZ/Publish/Core/settings/tests/integration_legacy.yml b/eZ/Publish/Core/settings/tests/integration_legacy.yml deleted file mode 100644 index c2a96cec18..0000000000 --- a/eZ/Publish/Core/settings/tests/integration_legacy.yml +++ /dev/null @@ -1,59 +0,0 @@ -parameters: - languages: - - eng-US - - eng-GB - ignored_storage_files: - - - var/ezdemo_site/storage/images/design/plain-site/172-2-eng-US/eZ-Publish-Demo-Design-without-demo-content1.png - # Image Asset mappings - ezsettings.default.fieldtypes.ezimageasset.mappings: - content_type_identifier: image - content_field_identifier: image - name_field_identifier: name - parent_location_id: 51 - -services: - eZ\Publish\Core\FieldType\ImageAsset\AssetMapper: - arguments: - $contentService: '@ezpublish.api.service.content' - $locationService: '@ezpublish.api.service.location' - $contentTypeService: '@ezpublish.api.service.content_type' - $configResolver: '@ezpublish.config.resolver' - - # repeat part of DIC setup to avoid loading DoctrineSchemaBundle - _instanceof: - EzSystems\DoctrineSchema\Database\DbPlatform\DbPlatform: - tags: ['doctrine.dbplatform'] - - Symfony\Component\EventDispatcher\EventDispatcher: - calls: - - ['addSubscriber', ['@eZ\Publish\Core\Search\Common\EventSubscriber\ContentEventSubscriber']] - - ['addSubscriber', ['@eZ\Publish\Core\Search\Common\EventSubscriber\LocationEventSubscriber']] - - ['addSubscriber', ['@eZ\Publish\Core\Search\Common\EventSubscriber\ObjectStateEventSubscriber']] - - ['addSubscriber', ['@eZ\Publish\Core\Search\Common\EventSubscriber\SectionEventSubscriber']] - - ['addSubscriber', ['@eZ\Publish\Core\Search\Common\EventSubscriber\TrashEventSubscriber']] - - ['addSubscriber', ['@eZ\Publish\Core\Search\Common\EventSubscriber\UserEventSubscriber']] - - ['addSubscriber', ['@Ibexa\Core\Persistence\Legacy\Content\Mapper\ResolveVirtualFieldSubscriber']] - - Doctrine\Common\EventManager: ~ - - EzSystems\DoctrineSchema\Database\DbPlatform\SqliteDbPlatform: - autowire: true - - eZ\Publish\Core\Persistence\Tests\DatabaseConnectionFactory: - autowire: true - arguments: - $databasePlatforms: !tagged 'doctrine.dbplatform' - - # build ezpublish.api.storage_engine.legacy.connection for test purposes - ezpublish.api.storage_engine.legacy.connection: - class: Doctrine\DBAL\Connection - factory: ['@eZ\Publish\Core\Persistence\Tests\DatabaseConnectionFactory', 'createConnection'] - arguments: - $databaseURL: '%legacy_dsn%' - - eZ\Publish\API\Repository\SettingService: - public: true - alias: eZ\Publish\Core\Event\SettingService - - ezpublish.image_alias.imagine.cache.alias_generator_decorator: '@eZ\Publish\SPI\Tests\Variation\InMemoryVariationHandler' diff --git a/eZ/Publish/Core/settings/tests/integration_legacy_core.yml b/eZ/Publish/Core/settings/tests/integration_legacy_core.yml deleted file mode 100644 index a48b7d52e5..0000000000 --- a/eZ/Publish/Core/settings/tests/integration_legacy_core.yml +++ /dev/null @@ -1,4 +0,0 @@ -parameters: - ignored_storage_files: - - - var/ezdemo_site/storage/images/design/plain-site/172-2-eng-US/eZ-Publish-Demo-Design-without-demo-content1.png diff --git a/eZ/Publish/Core/settings/tests/override.yml b/eZ/Publish/Core/settings/tests/override.yml deleted file mode 100644 index 3a2c516e66..0000000000 --- a/eZ/Publish/Core/settings/tests/override.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Config loaded as the last one just before creating the test container. -# Effectively overrides all other settings - -parameters: - # for tests of features using prioritized languages list - languages: - - eng-US - - eng-GB - - ger-DE diff --git a/eZ/Publish/Core/settings/thumbnails.yml b/eZ/Publish/Core/settings/thumbnails.yml deleted file mode 100644 index 5ff36af849..0000000000 --- a/eZ/Publish/Core/settings/thumbnails.yml +++ /dev/null @@ -1,43 +0,0 @@ -services: - _defaults: - public: false - autoconfigure: true - autowire: true - - eZ\Publish\Core\FieldType\Image\ImageThumbnailStrategy: - arguments: - $fieldTypeIdentifier: 'ezimage' - $variationHandler: '@ezpublish.image_alias.imagine.cache.alias_generator_decorator' - $variationName: 'medium' - tags: - - { name: ezplatform.spi.field.thumbnail_strategy, priority: 0 } - - eZ\Publish\Core\FieldType\Image\ImageThumbnailProxyStrategy: - decorates: eZ\Publish\Core\FieldType\Image\ImageThumbnailStrategy - arguments: - $imageThumbnailStrategy: '@.inner' - $proxyGenerator: '@eZ\Publish\Core\Repository\ProxyFactory\ProxyGeneratorInterface' - - eZ\Publish\Core\FieldType\ImageAsset\ImageAssetThumbnailStrategy: - lazy: true - arguments: - $fieldTypeIdentifier: 'ezimageasset' - $thumbnailStrategy: '@eZ\Publish\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy' - $contentService: '@ezpublish.api.service.content' - tags: - - { name: ezplatform.spi.field.thumbnail_strategy, priority: 0 } - - eZ\Publish\Core\Repository\Strategy\ContentThumbnail\Field\ContentFieldStrategy: - arguments: - $strategies: !tagged_iterator ezplatform.spi.field.thumbnail_strategy - - eZ\Publish\Core\Repository\Strategy\ContentThumbnail\FirstMatchingFieldStrategy: - arguments: - $fieldTypeService: '@ezpublish.api.service.field_type' - $contentFieldStrategy: '@eZ\Publish\Core\Repository\Strategy\ContentThumbnail\Field\ContentFieldStrategy' - tags: - - { name: ezplatform.spi.content.thumbnail_strategy, priority: 0 } - - eZ\Publish\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy: - arguments: - $strategies: !tagged_iterator ezplatform.spi.content.thumbnail_strategy diff --git a/eZ/Publish/Core/settings/user_preference.yml b/eZ/Publish/Core/settings/user_preference.yml deleted file mode 100644 index 6774c7ef32..0000000000 --- a/eZ/Publish/Core/settings/user_preference.yml +++ /dev/null @@ -1,3 +0,0 @@ -services: - eZ\Publish\API\Repository\UserPreferenceService: - alias: eZ\Publish\Core\Event\UserPreferenceService diff --git a/eZ/Publish/Core/settings/utils.yml b/eZ/Publish/Core/settings/utils.yml deleted file mode 100644 index 1c632c9244..0000000000 --- a/eZ/Publish/Core/settings/utils.yml +++ /dev/null @@ -1,3 +0,0 @@ -services: - ezpublish.utils.deprecation_warner: - class: eZ\Publish\Core\Base\Utils\DeprecationWarner diff --git a/eZ/Publish/SPI/Exception/InvalidArgumentException.php b/eZ/Publish/SPI/Exception/InvalidArgumentException.php deleted file mode 100644 index bfb5882c57..0000000000 --- a/eZ/Publish/SPI/Exception/InvalidArgumentException.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Exception; - -use Exception; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException as APIInvalidArgumentException; - -/** - * Invalid Argument Type Exception implementation. - * - * Usage: throw new InvalidArgumentException( 'nodes', 'array' ); - */ -class InvalidArgumentException extends APIInvalidArgumentException -{ - /** - * Generates: "Argument '{$argumentName}' is invalid: {$whatIsWrong}". - * - * @param string $argumentName - * @param string $whatIsWrong - * @param \Exception|null $previous - */ - public function __construct(string $argumentName, string $whatIsWrong, Exception $previous = null) - { - $message = sprintf("Argument '%s' is invalid: %s", $argumentName, $whatIsWrong); - - parent::__construct($message, 0, $previous); - } -} diff --git a/eZ/Publish/SPI/Exception/InvalidArgumentType.php b/eZ/Publish/SPI/Exception/InvalidArgumentType.php deleted file mode 100644 index 94e5f9df70..0000000000 --- a/eZ/Publish/SPI/Exception/InvalidArgumentType.php +++ /dev/null @@ -1,39 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Exception; - -use Exception; - -/** - * Invalid Argument Type Exception implementation. - * - * Usage: throw new InvalidArgument( 'nodes', 'array' ); - */ -class InvalidArgumentType extends InvalidArgumentException -{ - /** - * Generates: "Argument '{$argumentName}' is invalid: expected value to be of type '{$expectedType}'[, got '{$value}']". - * - * @param string $argumentName - * @param string $expectedType - * @param mixed|null $value Optionally to output the type that was received - * @param \Exception|null $previous - */ - public function __construct(string $argumentName, string $expectedType, $value = null, Exception $previous = null) - { - if ($value !== null) { - $actualType = is_object($value) ? get_class($value) : gettype($value); - $whatIsWrong = sprintf("Received '%s' instead of expected value of type '%s'", $actualType, $expectedType); - } else { - $whatIsWrong = sprintf("Expected value is of type '%s'", $expectedType); - } - - parent::__construct($argumentName, $whatIsWrong, $previous); - } -} diff --git a/eZ/Publish/SPI/FieldType/BinaryBase/PathGenerator.php b/eZ/Publish/SPI/FieldType/BinaryBase/PathGenerator.php deleted file mode 100644 index 9d6e0d9d5f..0000000000 --- a/eZ/Publish/SPI/FieldType/BinaryBase/PathGenerator.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\FieldType\BinaryBase; - -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * @deprecated use \eZ\Publish\SPI\FieldType\BinaryBase\PathGeneratorInterface instead. - */ -abstract class PathGenerator implements PathGeneratorInterface -{ - abstract public function getStoragePathForField(Field $field, VersionInfo $versionInfo); -} diff --git a/eZ/Publish/SPI/FieldType/BinaryBase/PathGeneratorInterface.php b/eZ/Publish/SPI/FieldType/BinaryBase/PathGeneratorInterface.php deleted file mode 100644 index 26b92d9da2..0000000000 --- a/eZ/Publish/SPI/FieldType/BinaryBase/PathGeneratorInterface.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\FieldType\BinaryBase; - -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -interface PathGeneratorInterface -{ - public function getStoragePathForField(Field $field, VersionInfo $versionInfo); -} diff --git a/eZ/Publish/SPI/FieldType/BinaryBase/RouteAwarePathGenerator.php b/eZ/Publish/SPI/FieldType/BinaryBase/RouteAwarePathGenerator.php deleted file mode 100644 index 9b4306353f..0000000000 --- a/eZ/Publish/SPI/FieldType/BinaryBase/RouteAwarePathGenerator.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\FieldType\BinaryBase; - -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * A variant of PathGenerator that uses Symfony routes for generating URIs. - */ -interface RouteAwarePathGenerator extends PathGeneratorInterface -{ - public function getRoute(Field $field, VersionInfo $versionInfo): string; - - public function getParameters(Field $field, VersionInfo $versionInfo): array; - - public function generate(string $route, array $parameters = []): string; -} diff --git a/eZ/Publish/SPI/FieldType/Comparable.php b/eZ/Publish/SPI/FieldType/Comparable.php deleted file mode 100644 index df022091f9..0000000000 --- a/eZ/Publish/SPI/FieldType/Comparable.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\FieldType; - -interface Comparable -{ - public function valuesEqual(Value $value1, Value $value2): bool; -} diff --git a/eZ/Publish/SPI/FieldType/FieldStorage.php b/eZ/Publish/SPI/FieldType/FieldStorage.php deleted file mode 100644 index 81173b71b1..0000000000 --- a/eZ/Publish/SPI/FieldType/FieldStorage.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\FieldType; - -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Interface for setting field type data. - * - * Methods in this interface are called by storage engine. - * - * $context array passed to most methods is deprecated and will be dropped in the next major version. - */ -interface FieldStorage -{ - /** - * Allows custom field types to store data in an external source (e.g. another DB table). - * - * Stores value for $field in an external data source. - * The whole {@link eZ\Publish\SPI\Persistence\Content\Field} object is passed and its value - * is accessible through the {@link eZ\Publish\SPI\Persistence\Content\FieldValue} 'value' property. - * This value holds the data filled by the user as a {@link eZ\Publish\Core\FieldType\Value} based object, - * according to the field type (e.g. for TextLine, it will be a {@link eZ\Publish\Core\FieldType\TextLine\Value} object). - * - * $field->id = unique ID from the attribute tables (needs to be generated by - * database back end on create, before the external data source may be - * called from storing). - * - * The context array is deprecated and will be dropped in the next major version. - * - * This method might return true if $field needs to be updated after storage done here (to store a PK for instance). - * In any other case, this method must not return anything (null). - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context Deprecated. Rely on injected Connection instead. - * - * @return mixed null|true - */ - public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context); - - /** - * Populates $field value property based on the external data. - * $field->value is a {@link eZ\Publish\SPI\Persistence\Content\FieldValue} object. - * This value holds the data as a {@link eZ\Publish\Core\FieldType\Value} based object, - * according to the field type (e.g. for TextLine, it will be a {@link eZ\Publish\Core\FieldType\TextLine\Value} object). - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context Deprecated. Rely on injected Connection instead. - */ - public function getFieldData(VersionInfo $versionInfo, Field $field, array $context); - - /** - * Deletes field data for all $fieldIds in the version identified by - * $versionInfo. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param array $fieldIds Array of field IDs - * @param array $context Deprecated. Rely on injected Connection instead. - * - * @return bool - */ - public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context); - - /** - * Checks if field type has external data to deal with. - * - * @return bool - */ - public function hasFieldData(); - - /** - * Get index data for external data for search backend. - * - * @deprecated Use eZ\Publish\SPI\FieldType\Indexable - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param array $context Deprecated. Rely on injected Connection instead. - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(VersionInfo $versionInfo, Field $field, array $context); -} diff --git a/eZ/Publish/SPI/FieldType/FieldType.php b/eZ/Publish/SPI/FieldType/FieldType.php deleted file mode 100644 index 71733bc4a1..0000000000 --- a/eZ/Publish/SPI/FieldType/FieldType.php +++ /dev/null @@ -1,381 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\FieldType; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\FieldValue; - -/** - * The field type interface which all field types have to implement. - * - * - * Hashes: - * - * The {@link toHash()} method in this class is meant to generate a simple - * representation of a value of this field type. Hash does here not refer to - * MD5 or similar hashing algorithms, but rather to hash-map (associative array) - * type representation. This representation must be - * usable, to transfer the value over plain text encoding formats, like e.g. - * XML. As a result, the returned "hash" must either be a scalar value, a hash - * array (associative array) a pure numeric array or a nested combination of - * these. It must by no means contain objects, resources or cyclic references. - * The corresponding {@link fromHash()} method must convert such a - * representation back into a value, which is understood by the FieldType. - */ -abstract class FieldType -{ - /** - * Returns the field type identifier for this field type. - * - * This identifier should be globally unique and the implementer of a - * FieldType must take care for the uniqueness. It is therefore recommended - * to prefix the field-type identifier by a unique string that identifies - * the implementer. A good identifier could for example take your companies main - * domain name as a prefix in reverse order. - * - * @return string - */ - abstract public function getFieldTypeIdentifier(); - - /** - * Returns a human readable string representation from a given value. - * - * It will be used to generate content name and url alias if current field - * is designated to be used in the content name/urlAlias pattern. - * - * The used $value can be assumed to be already accepted by {@link FieldType::acceptValue()}. - * - * @param \eZ\Publish\SPI\FieldType\Value $value - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition - * @param string $languageCode - * - * @return string - */ - abstract public function getName(Value $value, FieldDefinition $fieldDefinition, string $languageCode): string; - - /** - * Returns a schema for the settings expected by the FieldType. - * - * Returns an arbitrary value, representing a schema for the settings of - * the FieldType. - * - * Explanation: There are no possible generic schemas for defining settings - * input, which is why no schema for the return value of this method is - * defined. It is up to the implementer to define and document a schema for - * the return value and document it. In addition, it is necessary that all - * consumers of this interface (e.g. Public API, REST API, GUIs, ...) - * provide plugin mechanisms to hook adapters for the specific FieldType - * into. These adapters then need to be either shipped with the FieldType - * or need to be implemented by a third party. If there is no adapter - * available for a specific FieldType, it will not be usable with the - * consumer. - * - * @return mixed - */ - abstract public function getSettingsSchema(); - - /** - * Returns a schema for the validator configuration expected by the FieldType. - * - * Returns an arbitrary value, representing a schema for the validator - * configuration of the FieldType. - * - * Explanation: There are no possible generic schemas for defining settings - * input, which is why no schema for the return value of this method is - * defined. It is up to the implementer to define and document a schema for - * the return value and document it. In addition, it is necessary that all - * consumers of this interface (e.g. Public API, REST API, GUIs, ...) - * provide plugin mechanisms to hook adapters for the specific FieldType - * into. These adapters then need to be either shipped with the FieldType - * or need to be implemented by a third party. If there is no adapter - * available for a specific FieldType, it will not be usable with the - * consumer. - * - * Best practice: - * - * It is considered best practice to return a hash map, which contains - * rudimentary settings structures, like e.g. for the "ezstring" FieldType - * - * <code> - * array( - * 'stringLength' => array( - * 'minStringLength' => array( - * 'type' => 'int', - * 'default' => 0, - * ), - * 'maxStringLength' => array( - * 'type' => 'int' - * 'default' => null, - * ) - * ), - * ); - * </code> - * - * @return mixed - */ - abstract public function getValidatorConfigurationSchema(); - - /** - * Validates a field based on the validator configuration in the field definition. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDef The field definition of the field - * @param \eZ\Publish\SPI\FieldType\Value $value The field value for which an action is performed - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - abstract public function validate(FieldDefinition $fieldDef, Value $value); - - /** - * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * This methods determines if the given $validatorConfiguration is - * structurally correct and complies to the validator configuration schema - * returned by {@link getValidatorConfigurationSchema()}. - * - * @param mixed $validatorConfiguration - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - abstract public function validateValidatorConfiguration($validatorConfiguration); - - /** - * Applies the default values to the given $validatorConfiguration of a FieldDefinitionCreateStruct. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param mixed $validatorConfiguration - */ - abstract public function applyDefaultValidatorConfiguration(&$validatorConfiguration); - - /** - * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. - * - * This methods determines if the given $fieldSettings are structurally - * correct and comply to the settings schema returned by {@link * getSettingsSchema()}. - * - * @param mixed $fieldSettings - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - abstract public function validateFieldSettings($fieldSettings); - - /** - * Applies the default values to the fieldSettings of a FieldDefinitionCreateStruct. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * - * @param mixed $fieldSettings - */ - abstract public function applyDefaultSettings(&$fieldSettings); - - /** - * Indicates if the field type supports indexing and sort keys for searching. - * - * @return bool - */ - abstract public function isSearchable(); - - /** - * Indicates if the field definition of this type can appear only once in the same ContentType. - * - * @return bool - */ - abstract public function isSingular(); - - /** - * Indicates if the field definition of this type can be added to a ContentType with Content instances. - * - * @return bool - */ - abstract public function onlyEmptyInstance(); - - /** - * Returns the empty value for this field type. - * - * This value will be used, if no value was provided for a field of this - * type and no default value was specified in the field definition. It is - * also used to determine that a user intentionally (or unintentionally) did not - * set a non-empty value. - * - * @return \eZ\Publish\SPI\FieldType\Value - */ - abstract public function getEmptyValue(); - - /** - * Returns if the given $value is considered empty by the field type. - * - * Usually, only the value returned by {@link getEmptyValue()} is - * considered empty. The given $value can be safely assumed to have already - * been processed by {@link acceptValue()}. - * - * @param \eZ\Publish\SPI\FieldType\Value $value - * - * @return bool - */ - abstract public function isEmptyValue(Value $value); - - /** - * Potentially builds and checks the type and structure of the $inputValue. - * - * This method first inspects $inputValue and convert it into a dedicated - * value object. - * - * After that, the value is checked for structural validity. - * Note that this does not include validation after the rules - * from validators, but only plausibility checks for the general data - * format. - * - * Note that this method must also cope with the empty value for the field - * type as e.g. returned by {@link getEmptyValue()}. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the parameter is not of the supported value sub type - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the value does not match the expected structure - * - * @param mixed $inputValue - * - * @return \eZ\Publish\SPI\FieldType\Value The potentially converted and structurally plausible value. - */ - abstract public function acceptValue($inputValue); - - /** - * Converts an $hash to the Value defined by the field type. - * - * This is the reverse operation to {@link toHash()}. At least the hash - * format generated by {@link toHash()} must be converted in reverse. - * Additional formats might be supported in the rare case that this is - * necessary. See the class description for more details on a hash format. - * - * @param mixed $hash - * - * @return \eZ\Publish\SPI\FieldType\Value - */ - abstract public function fromHash($hash); - - /** - * Converts the given $value into a plain hash format. - * - * Converts the given $value into a plain hash format, which can be used to - * transfer the value through plain text formats, e.g. XML, which do not - * support complex structures like objects. See the class level doc block - * for additional information. See the class description for more details on a hash format. - * - * @param \eZ\Publish\SPI\FieldType\Value $value - * - * @return mixed - */ - abstract public function toHash(Value $value); - - /** - * Converts the given $fieldSettings to a simple hash format. - * - * See the class description for more details on a hash format. - * - * @param mixed $fieldSettings - * - * @return array|hash|scalar|null - */ - abstract public function fieldSettingsToHash($fieldSettings); - - /** - * Converts the given $fieldSettingsHash to field settings of the type. - * - * This is the reverse operation of {@link fieldSettingsToHash()}. - * See the class description for more details on a hash format. - * - * @param array|hash|scalar|null $fieldSettingsHash - * - * @return mixed - */ - abstract public function fieldSettingsFromHash($fieldSettingsHash); - - /** - * Converts the given $validatorConfiguration to a simple hash format. - * - * See the class description for more details on a hash format. - * - * @param mixed $validatorConfiguration - * - * @return array|hash|scalar|null - */ - abstract public function validatorConfigurationToHash($validatorConfiguration); - - /** - * Converts the given $validatorConfigurationHash to a validator - * configuration of the type. - * - * See the class description for more details on a hash format. - * - * @param array|hash|scalar|null $validatorConfigurationHash - * - * @return mixed - */ - abstract public function validatorConfigurationFromHash($validatorConfigurationHash); - - /** - * Converts a $value to a persistence value. - * - * In this method the field type puts the data which is stored in the field of content in the repository - * into the property FieldValue::data. The format of $data is a primitive, an array (map) or an object, which - * is then canonically converted to e.g. json/xml structures by future storage engines without - * further conversions. For mapping the $data to the legacy database an appropriate Converter - * (implementing eZ\Publish\Core\Persistence\Legacy\FieldValue\Converter) has implemented for the field - * type. Note: $data should only hold data which is actually stored in the field. It must not - * hold data which is stored externally. - * - * The $externalData property in the FieldValue is used for storing data externally by the - * FieldStorage interface method storeFieldData. - * - * The FieldValuer::sortKey is build by the field type for using by sort operations. - * - * @see \eZ\Publish\SPI\Persistence\Content\FieldValue - * - * @param \eZ\Publish\SPI\FieldType\Value $value The value of the field type - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue the value processed by the storage engine - */ - abstract public function toPersistenceValue(Value $value); - - /** - * Converts a persistence $value to a Value. - * - * This method builds a field type value from the $data and $externalData properties. - * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue - * - * @return \eZ\Publish\SPI\FieldType\Value - */ - abstract public function fromPersistenceValue(FieldValue $fieldValue); - - /** - * Returns relation data extracted from value. - * - * Not intended for \eZ\Publish\API\Repository\Values\Content\Relation::COMMON type relations, - * there is an API for handling those. - * - * @param \eZ\Publish\SPI\FieldType\Value $value - * - * @return array Hash with relation type as key and array of destination content ids as value. - * - * Example: - * <code> - * array( - * \eZ\Publish\API\Repository\Values\Content\Relation::LINK => array( - * "contentIds" => array( 12, 13, 14 ), - * "locationIds" => array( 24 ) - * ), - * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED => array( - * "contentIds" => array( 12 ), - * "locationIds" => array( 24, 45 ) - * ), - * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD => array( 12 ) - * ) - * </code> - */ - abstract public function getRelations(Value $value); -} diff --git a/eZ/Publish/SPI/FieldType/GatewayBasedStorage.php b/eZ/Publish/SPI/FieldType/GatewayBasedStorage.php deleted file mode 100644 index d87fbb46c9..0000000000 --- a/eZ/Publish/SPI/FieldType/GatewayBasedStorage.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\FieldType; - -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; - -/** - * Field Type External Storage gateway base class. - * - * @template T of \eZ\Publish\SPI\FieldType\StorageGateway - */ -abstract class GatewayBasedStorage implements FieldStorage -{ - /** - * Field Type External Storage Gateway. - * - * @var \eZ\Publish\SPI\FieldType\StorageGateway - * @phpstan-var T - */ - protected $gateway; - - /** - * @param \eZ\Publish\SPI\FieldType\StorageGateway $gateway - * @phpstan-param T $gateway - */ - public function __construct(StorageGateway $gateway) - { - $this->gateway = $gateway; - } - - /** - * This method is used exclusively by Legacy Storage to copy external data of existing field in main language to - * the untranslatable field not passed in create or update struct, but created implicitly in storage layer. - * - * By default the method falls back to the {@link \eZ\Publish\SPI\FieldType\FieldStorage::storeFieldData()}. - * External storages implement this method as needed. - * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Field $originalField - * @param array $context - * - * @return bool|null Same as {@link \eZ\Publish\SPI\FieldType\FieldStorage::storeFieldData()}. - */ - public function copyLegacyField(VersionInfo $versionInfo, Field $field, Field $originalField, array $context) - { - return $this->storeFieldData($versionInfo, $field, $context); - } -} diff --git a/eZ/Publish/SPI/FieldType/Generic/Tests/Stubs/Type.php b/eZ/Publish/SPI/FieldType/Generic/Tests/Stubs/Type.php deleted file mode 100644 index b4cf6db0ed..0000000000 --- a/eZ/Publish/SPI/FieldType/Generic/Tests/Stubs/Type.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\FieldType\Generic\Tests\Stubs; - -use eZ\Publish\SPI\FieldType\Generic\Type as BaseType; - -final class Type extends BaseType -{ - public function getFieldTypeIdentifier(): string - { - return 'generic'; - } -} diff --git a/eZ/Publish/SPI/FieldType/Generic/Tests/Stubs/Value.php b/eZ/Publish/SPI/FieldType/Generic/Tests/Stubs/Value.php deleted file mode 100644 index 45591c3ccd..0000000000 --- a/eZ/Publish/SPI/FieldType/Generic/Tests/Stubs/Value.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\FieldType\Generic\Tests\Stubs; - -use eZ\Publish\SPI\FieldType\Value as ValueInterface; - -final class Value implements ValueInterface -{ - private $value; - - public function __construct($value = null) - { - $this->value = $value; - } - - public function getValue() - { - return $this->value; - } - - public function __toString() - { - return (string)$this->value; - } -} diff --git a/eZ/Publish/SPI/FieldType/Generic/Type.php b/eZ/Publish/SPI/FieldType/Generic/Type.php deleted file mode 100644 index f511c18e3a..0000000000 --- a/eZ/Publish/SPI/FieldType/Generic/Type.php +++ /dev/null @@ -1,361 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\FieldType\Generic; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\SPI\Exception\InvalidArgumentType; -use eZ\Publish\SPI\FieldType\FieldType; -use eZ\Publish\SPI\FieldType\Generic\ValidationError\ConstraintViolationAdapter; -use eZ\Publish\SPI\FieldType\ValidationError\NonConfigurableValidationError; -use eZ\Publish\SPI\FieldType\ValidationError\UnknownValidatorValidationError; -use eZ\Publish\SPI\FieldType\Value; -use eZ\Publish\SPI\FieldType\ValueSerializerInterface; -use eZ\Publish\SPI\Persistence\Content\FieldValue as PersistenceValue; -use Symfony\Component\Validator\Constraints as Assert; -use Symfony\Component\Validator\ConstraintViolationListInterface; -use Symfony\Component\Validator\Validator\ValidatorInterface; - -abstract class Type extends FieldType -{ - /** @var \eZ\Publish\SPI\FieldType\ValueSerializerInterface */ - protected $serializer; - - /** @var \Symfony\Component\Validator\Validator\ValidatorInterface */ - protected $validator; - - public function __construct(ValueSerializerInterface $serializer, ValidatorInterface $validator) - { - $this->serializer = $serializer; - $this->validator = $validator; - } - - public function getName(Value $value, FieldDefinition $fieldDefinition, string $languageCode): string - { - return (string)$value; - } - - public function getEmptyValue(): Value - { - $class = $this->getValueClass(); - - return new $class(); - } - - public function fromHash($hash): Value - { - if ($hash) { - return $this->serializer->denormalize($hash, $this->getValueClass()); - } - - return $this->getEmptyValue(); - } - - public function toHash(Value $value): ?array - { - if ($this->isEmptyValue($value)) { - return null; - } - - return $this->serializer->normalize($value); - } - - /** - * @see https://symfony.com/doc/current/validation/raw_values.html - */ - protected function getFieldSettingsConstraints(): ?Assert\Collection - { - return null; - } - - /** - * @see https://symfony.com/doc/current/validation/raw_values.html - */ - protected function getFieldValueConstraints(FieldDefinition $fieldDefinition): ?Assert\Collection - { - return null; - } - - protected function mapConstraintViolationList(ConstraintViolationListInterface $constraintViolationList): array - { - $errors = []; - - /** @var \Symfony\Component\Validator\ConstraintViolationInterface $constraintViolation */ - foreach ($constraintViolationList as $constraintViolation) { - $errors[] = new ConstraintViolationAdapter($constraintViolation); - } - - return $errors; - } - - public function getSettingsSchema(): array - { - return []; - } - - public function getValidatorConfigurationSchema(): array - { - return []; - } - - public function validate(FieldDefinition $fieldDefinition, Value $value): array - { - if ($this->isEmptyValue($value)) { - return []; - } - - return $this->mapConstraintViolationList( - $this->validator->validate($value, $this->getFieldValueConstraints($fieldDefinition)) - ); - } - - public function validateValidatorConfiguration($validatorConfiguration): array - { - $validationErrors = []; - - foreach ((array)$validatorConfiguration as $validatorIdentifier => $constraints) { - $validationErrors[] = new UnknownValidatorValidationError( - $validatorIdentifier, - "[$validatorIdentifier]" - ); - } - - return $validationErrors; - } - - public function applyDefaultValidatorConfiguration(&$validatorConfiguration): void - { - if ($validatorConfiguration !== null && !is_array($validatorConfiguration)) { - throw new InvalidArgumentType('$validatorConfiguration', 'array|null', $validatorConfiguration); - } - - foreach ($this->getValidatorConfigurationSchema() as $validatorName => $configurationSchema) { - // Set configuration of specific validator to empty array if it is not already provided - if (!isset($validatorConfiguration[$validatorName])) { - $validatorConfiguration[$validatorName] = []; - } - - foreach ($configurationSchema as $settingName => $settingConfiguration) { - // Check that a default entry exists in the configuration schema for the validator but that no value has been provided - if (!isset($validatorConfiguration[$validatorName][$settingName]) && array_key_exists('default', $settingConfiguration)) { - $validatorConfiguration[$validatorName][$settingName] = $settingConfiguration['default']; - } - } - } - } - - public function validateFieldSettings($fieldSettings): array - { - if (empty($this->getSettingsSchema()) && !empty($fieldSettings)) { - return [ - new NonConfigurableValidationError($this->getFieldTypeIdentifier(), 'fieldType'), - ]; - } - - if (empty($fieldSettings)) { - return []; - } - - return $this->mapConstraintViolationList( - $this->validator->validate($fieldSettings, $this->getFieldSettingsConstraints()) - ); - } - - public function applyDefaultSettings(&$fieldSettings): void - { - if ($fieldSettings !== null && !is_array($fieldSettings)) { - throw new InvalidArgumentType('$fieldSettings', 'array|null', $fieldSettings); - } - - foreach ($this->getSettingsSchema() as $settingName => $settingConfiguration) { - // Checking that a default entry exists in the settingsSchema but that no value has been provided - if (!array_key_exists($settingName, (array)$fieldSettings) && array_key_exists('default', $settingConfiguration)) { - $fieldSettings[$settingName] = $settingConfiguration['default']; - } - } - } - - /** - * Returns information for FieldValue->$sortKey relevant to the field type. - * - * Return value is mixed. It should be something which is sensible for - * sorting. - * - * It is up to the persistence implementation to handle those values. - * Common string and integer values are safe. - * - * For the legacy storage it is up to the field converters to set this - * value in either sort_key_string or sort_key_int. - * - * In case of multi value, values should be string and separated by "-" or ",". - * - * @param \eZ\Publish\Core\FieldType\Value $value - * - * @return mixed - */ - protected function getSortInfo(Value $value) - { - return null; - } - - public function toPersistenceValue(Value $value): PersistenceValue - { - return new PersistenceValue( - [ - 'data' => $this->toHash($value), - 'externalData' => null, - 'sortKey' => $this->getSortInfo($value), - ] - ); - } - - public function fromPersistenceValue(PersistenceValue $fieldValue) - { - return $this->fromHash($fieldValue->data); - } - - public function isSearchable(): bool - { - return false; - } - - public function isSingular(): bool - { - return false; - } - - public function onlyEmptyInstance(): bool - { - return false; - } - - public function isEmptyValue(Value $value): bool - { - return $value == $this->getEmptyValue(); - } - - final public function acceptValue($inputValue): Value - { - if ($inputValue === null) { - return $this->getEmptyValue(); - } - - $value = $this->createValueFromInput($inputValue); - - $this->checkValueType($value); - - if ($this->isEmptyValue($value)) { - return $this->getEmptyValue(); - } - - return $value; - } - - /** - * Inspects given $inputValue and potentially converts it into a dedicated value object. - * - * If given $inputValue could not be converted or is already an instance of dedicate value object, - * the method should simply return it. - * - * This is an operation method for {@see acceptValue()}. - * - * Example implementation: - * <code> - * protected function createValueFromInput( $inputValue ) - * { - * if ( is_array( $inputValue ) ) - * { - * $inputValue = \eZ\Publish\Core\FieldType\CookieJar\Value( $inputValue ); - * } - * - * return $inputValue; - * } - * </code> - * - * @param mixed $inputValue - * - * @return mixed The potentially converted input value. - */ - protected function createValueFromInput($inputValue) - { - if (is_string($inputValue)) { - $inputValue = $this->serializer->denormalize( - $this->serializer->decode($inputValue), - $this->getValueClass() - ); - } - - return $inputValue; - } - - /** - * Returns FQN of class representing Field Type Value. - * - * @return string - */ - protected function getValueClass(): string - { - return substr_replace(static::class, 'Value', strrpos(static::class, '\\') + 1); - } - - /** - * Throws an exception if the given $value is not an instance of the supported value subtype. - * - * This is an operation method for {@see acceptValue()}. - * - * Default implementation expects the value class to reside in the same namespace as its - * FieldType class and is named "Value". - * - * Example implementation: - * <code> - * protected function checkValueType($value): void - * { - * if ( !$inputValue instanceof \eZ\Publish\Core\FieldType\CookieJar\Value ) ) - * { - * throw new InvalidArgumentException( "Given value type is not supported." ); - * } - * } - * </code> - * - * @param mixed $value A value returned by {@see createValueFromInput()}. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the parameter is not an instance of the supported value subtype. - */ - protected function checkValueType($value): void - { - $valueClass = $this->getValueClass(); - if (!$value instanceof $valueClass) { - throw new InvalidArgumentType('$value', $valueClass, $value); - } - } - - public function fieldSettingsToHash($fieldSettings) - { - return $fieldSettings; - } - - public function fieldSettingsFromHash($fieldSettingsHash) - { - return $fieldSettingsHash; - } - - public function validatorConfigurationToHash($validatorConfiguration) - { - return $validatorConfiguration; - } - - public function validatorConfigurationFromHash($validatorConfiguration) - { - return $validatorConfiguration; - } - - public function getRelations(Value $value): array - { - return []; - } -} diff --git a/eZ/Publish/SPI/FieldType/Generic/ValidationError/ConstraintViolationAdapter.php b/eZ/Publish/SPI/FieldType/Generic/ValidationError/ConstraintViolationAdapter.php deleted file mode 100644 index d82c9a834f..0000000000 --- a/eZ/Publish/SPI/FieldType/Generic/ValidationError/ConstraintViolationAdapter.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\FieldType\Generic\ValidationError; - -use eZ\Publish\API\Repository\Values\Translation; -use eZ\Publish\API\Repository\Values\Translation\Message; -use eZ\Publish\SPI\FieldType\ValidationError as ValidationErrorInterface; -use Symfony\Component\Validator\ConstraintViolationInterface; - -/** - * \Symfony\Component\Validator\ConstraintViolationInterface to eZ\Publish\SPI\FieldType\ValidationError adapter. - */ -final class ConstraintViolationAdapter implements ValidationErrorInterface -{ - /** @var \Symfony\Component\Validator\ConstraintViolationInterface */ - private $violation; - - /** - * Element on which the error occurred - * e.g. property name or property path compatible with Symfony PropertyAccess component. - * - * Example: StringLengthValidator[minStringLength] - * - * @var string - */ - private $target; - - public function __construct(ConstraintViolationInterface $violation) - { - $this->violation = $violation; - $this->target = $violation->getPropertyPath(); - } - - public function getTranslatableMessage(): Translation - { - return new Message( - $this->violation->getMessageTemplate(), - $this->violation->getParameters() - ); - } - - public function setTarget($target): void - { - $this->target = $target; - } - - public function getTarget(): string - { - return $this->target; - } -} diff --git a/eZ/Publish/SPI/FieldType/Indexable.php b/eZ/Publish/SPI/FieldType/Indexable.php deleted file mode 100644 index 7b955143d7..0000000000 --- a/eZ/Publish/SPI/FieldType/Indexable.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\FieldType; - -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; - -/** - * The field type interface which all field types have to implement to be - * indexable by search backends. - */ -interface Indexable -{ - /** - * Get index data for field for search backend. - * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Search\Field[] - */ - public function getIndexData(Field $field, FieldDefinition $fieldDefinition); - - /** - * Get index field types for search backend. - * - * @return \eZ\Publish\SPI\Search\FieldType[] - */ - public function getIndexDefinition(); - - /** - * Get name of the default field to be used for matching. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for matching. Default field is typically used by Field criterion. - * - * @return string - */ - public function getDefaultMatchField(); - - /** - * Get name of the default field to be used for sorting. - * - * As field types can index multiple fields (see MapLocation field type's - * implementation of this interface), this method is used to define default - * field for sorting. Default field is typically used by Field sort clause. - * - * @return string - */ - public function getDefaultSortField(); -} diff --git a/eZ/Publish/SPI/FieldType/StorageGateway.php b/eZ/Publish/SPI/FieldType/StorageGateway.php deleted file mode 100644 index 6ecef92204..0000000000 --- a/eZ/Publish/SPI/FieldType/StorageGateway.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\FieldType; - -/** - * Base class for FieldType external storage gateways. - */ -abstract class StorageGateway -{ - /** - * Get sequence name bound to database table and column. - * - * @param string $table - * @param string $column - * - * @return string - */ - protected function getSequenceName($table, $column) - { - return sprintf('%s_%s_seq', $table, $column); - } -} diff --git a/eZ/Publish/SPI/FieldType/Tests/FieldTypeTest.php b/eZ/Publish/SPI/FieldType/Tests/FieldTypeTest.php deleted file mode 100644 index 2afd0873e7..0000000000 --- a/eZ/Publish/SPI/FieldType/Tests/FieldTypeTest.php +++ /dev/null @@ -1,955 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\FieldType\Tests; - -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition; -use eZ\Publish\SPI\FieldType\FieldType; -use eZ\Publish\SPI\FieldType\ValidationError; -use eZ\Publish\SPI\FieldType\Value as SPIValue; -use PHPUnit\Framework\TestCase; - -abstract class FieldTypeTest extends TestCase -{ - /** - * Generic cache for the getFieldTypeUnderTest() method. - * - * @var \eZ\Publish\SPI\FieldType\FieldType - */ - private $fieldTypeUnderTest; - - /** - * Returns the identifier of the field type under test. - * - * @return string - */ - abstract protected function provideFieldTypeIdentifier(); - - /** - * Returns the field type under test. - * - * This method is used by all test cases to retrieve the field type under - * test. Just create the FieldType instance using mocks from the provided - * get*Mock() methods and/or custom get*Mock() implementations. You MUST - * NOT take care for test case wide caching of the field type, just return - * a new instance from this method! - * - * @return \eZ\Publish\SPI\FieldType\FieldType - */ - abstract protected function createFieldTypeUnderTest(); - - /** - * Returns the validator configuration schema expected from the field type. - * - * @return array - */ - abstract protected function getValidatorConfigurationSchemaExpectation(); - - /** - * Returns the settings schema expected from the field type. - * - * @return array - */ - abstract protected function getSettingsSchemaExpectation(); - - /** - * Returns the empty value expected from the field type. - * - * @return mixed - */ - abstract protected function getEmptyValueExpectation(); - - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ - abstract public function provideInvalidInputForAcceptValue(); - - /** - * Data provider for valid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to acceptValue(), 2. The expected return value from acceptValue(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * __FILE__, - * new BinaryFileValue( array( - * 'path' => __FILE__, - * 'fileName' => basename( __FILE__ ), - * 'fileSize' => filesize( __FILE__ ), - * 'downloadCount' => 0, - * 'mimeType' => 'text/plain', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - abstract public function provideValidInputForAcceptValue(); - - /** - * Provide input for the toHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to toHash(), 2. The expected return value from toHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ), - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - abstract public function provideInputForToHash(); - - /** - * Provide input to fromHash() method. - * - * Returns an array of data provider sets with 2 arguments: 1. The valid - * input to fromHash(), 2. The expected return value from fromHash(). - * For example: - * - * <code> - * return array( - * array( - * null, - * null - * ), - * array( - * array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ), - * new BinaryFileValue( array( - * 'path' => 'some/file/here', - * 'fileName' => 'sindelfingen.jpg', - * 'fileSize' => 2342, - * 'downloadCount' => 0, - * 'mimeType' => 'image/jpeg', - * ) ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - abstract public function provideInputForFromHash(); - - /** - * Provides data for the getName() test. - * - * @return array - */ - abstract public function provideDataForGetName(): array; - - /** - * Provide data sets with field settings which are considered valid by the - * {@link validateFieldSettings()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten - * if a FieldType supports field settings! - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( 'rows' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidFieldSettings() - { - return [ - [ - [], - ], - ]; - } - - /** - * Provide data sets with field settings which are considered invalid by the - * {@link validateFieldSettings()} method. The method must return a - * non-empty array of validation error when receiving such field settings. - * - * ATTENTION: This is a default implementation, which must be overwritten - * if a FieldType supports field settings! - * - * Returns an array of data provider sets with a single argument: A valid - * set of field settings. - * For example: - * - * <code> - * return array( - * array( - * true, - * ), - * array( - * array( 'nonExistentKey' => 2 ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInValidFieldSettings() - { - return [ - [ - ['nonempty'], - ], - ]; - } - - /** - * Provide data sets with validator configurations which are considered - * valid by the {@link validateValidatorConfiguration()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten - * if a FieldType supports validators! - * - * Returns an array of data provider sets with a single argument: A valid - * set of validator configurations. - * - * For example: - * - * <code> - * return array( - * array( - * array(), - * ), - * array( - * array( - * 'IntegerValueValidator' => array( - * 'minIntegerValue' => 0, - * 'maxIntegerValue' => 23, - * ) - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidValidatorConfiguration() - { - return [ - [ - [], - ], - ]; - } - - /** - * Provide data sets with validator configurations which are considered - * invalid by the {@link validateValidatorConfiguration()} method. The - * method must return a non-empty array of valiation errors when receiving - * one of the provided values. - * - * ATTENTION: This is a default implementation, which must be overwritten - * if a FieldType supports validators! - * - * Returns an array of data provider sets with a single argument: A valid - * set of validator configurations. - * - * For example: - * - * <code> - * return array( - * array( - * array( - * 'NonExistentValidator' => array(), - * ), - * ), - * array( - * array( - * // Typos - * 'InTEgervALUeVALIdator' => array( - * 'minIntegerValue' => 0, - * 'maxIntegerValue' => 23, - * ) - * ) - * ), - * array( - * array( - * 'IntegerValueValidator' => array( - * // Incorrect value types - * 'minIntegerValue' => true, - * 'maxIntegerValue' => false, - * ) - * ) - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidValidatorConfiguration() - { - return [ - [ - [ - 'NonExistentValidator' => [], - ], - ], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings and - * field value which are considered valid by the {@link validate()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten if - * a FieldType supports validation! - * - * For example: - * - * <code> - * return array( - * array( - * array( - * "validatorConfiguration" => array( - * "StringLengthValidator" => array( - * "minStringLength" => 2, - * "maxStringLength" => 10, - * ), - * ), - * ), - * new TextLineValue( "lalalala" ), - * ), - * array( - * array( - * "fieldSettings" => array( - * 'isMultiple' => true - * ), - * ), - * new CountryValue( - * array( - * "BE" => array( - * "Name" => "Belgium", - * "Alpha2" => "BE", - * "Alpha3" => "BEL", - * "IDC" => 32, - * ), - * ), - * ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideValidDataForValidate() - { - return [ - [ - [], - $this->createMock(SPIValue::class), - ], - ]; - } - - /** - * Provides data sets with validator configuration and/or field settings, - * field value and corresponding validation errors returned by - * the {@link validate()} method. - * - * ATTENTION: This is a default implementation, which must be overwritten - * if a FieldType supports validation! - * - * For example: - * - * <code> - * return array( - * array( - * array( - * "validatorConfiguration" => array( - * "IntegerValueValidator" => array( - * "minIntegerValue" => 5, - * "maxIntegerValue" => 10 - * ), - * ), - * ), - * new IntegerValue( 3 ), - * array( - * new ValidationError( - * "The value can not be lower than %size%.", - * null, - * array( - * "%size%" => 5 - * ), - * ), - * ), - * ), - * array( - * array( - * "fieldSettings" => array( - * "isMultiple" => false - * ), - * ), - * new CountryValue( - * "BE" => array( - * "Name" => "Belgium", - * "Alpha2" => "BE", - * "Alpha3" => "BEL", - * "IDC" => 32, - * ), - * "FR" => array( - * "Name" => "France", - * "Alpha2" => "FR", - * "Alpha3" => "FRA", - * "IDC" => 33, - * ), - * ) - * ), - * array( - * new ValidationError( - * "Field definition does not allow multiple countries to be selected." - * ), - * ), - * // ... - * ); - * </code> - * - * @return array - */ - public function provideInvalidDataForValidate() - { - return [ - [ - [], - $this->createMock(SPIValue::class), - [], - ], - ]; - } - - /** - * Retrieves a test wide cached version of the field type under test. - * - * Uses {@link createFieldTypeUnderTest()} to create the instance - * initially. - * - * @return \eZ\Publish\SPI\FieldType\FieldType - */ - protected function getFieldTypeUnderTest() - { - if (!isset($this->fieldTypeUnderTest)) { - $this->fieldTypeUnderTest = $this->createFieldTypeUnderTest(); - } - - return $this->fieldTypeUnderTest; - } - - public function testGetFieldTypeIdentifier() - { - self::assertSame( - $this->provideFieldTypeIdentifier(), - $this->getFieldTypeUnderTest()->getFieldTypeIdentifier() - ); - } - - /** - * @dataProvider provideDataForGetName - */ - public function testGetName( - SPIValue $value, - string $expected, - array $fieldSettings = [], - string $languageCode = 'en_GB' - ): void { - $fieldDefinitionMock = $this->getFieldDefinitionMock($fieldSettings); - - self::assertSame( - $expected, - $this->getFieldTypeUnderTest()->getName($value, $fieldDefinitionMock, $languageCode) - ); - } - - public function testValidatorConfigurationSchema() - { - $fieldType = $this->getFieldTypeUnderTest(); - - self::assertSame( - $this->getValidatorConfigurationSchemaExpectation(), - $fieldType->getValidatorConfigurationSchema(), - 'Validator configuration schema not returned correctly.' - ); - } - - public function testSettingsSchema() - { - $fieldType = $this->getFieldTypeUnderTest(); - - self::assertSame( - $this->getSettingsSchemaExpectation(), - $fieldType->getSettingsSchema(), - 'Settings schema not returned correctly.' - ); - } - - public function testEmptyValue() - { - $fieldType = $this->getFieldTypeUnderTest(); - - $this->assertEquals( - $this->getEmptyValueExpectation(), - $fieldType->getEmptyValue() - ); - } - - /** - * @param mixed $inputValue - * @param mixed $expectedOutputValue - * - * @dataProvider provideValidInputForAcceptValue - */ - public function testAcceptValue($inputValue, $expectedOutputValue) - { - $fieldType = $this->getFieldTypeUnderTest(); - - $outputValue = $fieldType->acceptValue($inputValue); - - $this->assertEquals( - $expectedOutputValue, - $outputValue, - 'acceptValue() did not convert properly.' - ); - } - - /** - * Tests that default empty value is unchanged by acceptValue() method. - */ - public function testAcceptGetEmptyValue() - { - $fieldType = $this->getFieldTypeUnderTest(); - $emptyValue = $fieldType->getEmptyValue(); - - $acceptedEmptyValue = $fieldType->acceptValue($emptyValue); - - $this->assertEquals( - $emptyValue, - $acceptedEmptyValue, - 'acceptValue() did not convert properly.' - ); - } - - /** - * @param mixed $inputValue - * - * @dataProvider provideInvalidInputForAcceptValue - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function testAcceptValueFailsOnInvalidValues( - $inputValue, - string $expectedException - ): void { - $fieldType = $this->getFieldTypeUnderTest(); - - $this->expectException($expectedException); - $fieldType->acceptValue($inputValue); - } - - /** - * @param mixed $inputValue - * @param array $expectedResult - * - * @dataProvider provideInputForToHash - */ - public function testToHash($inputValue, $expectedResult) - { - $fieldType = $this->getFieldTypeUnderTest(); - - $actualResult = $fieldType->toHash($inputValue); - - $this->assertIsValidHashValue($actualResult); - - if (is_object($expectedResult) || is_array($expectedResult)) { - $this->assertEquals( - $expectedResult, - $actualResult, - 'toHash() method did not create expected result.' - ); - } else { - $this->assertSame( - $expectedResult, - $actualResult, - 'toHash() method did not create expected result.' - ); - } - } - - /** - * @param mixed $inputValue - * @param array $expectedResult - * - * @dataProvider provideInputForFromHash - */ - public function testFromHash($inputHash, $expectedResult) - { - $this->assertIsValidHashValue($inputHash); - - $fieldType = $this->getFieldTypeUnderTest(); - - $actualResult = $fieldType->fromHash($inputHash); - - if (is_object($expectedResult) || is_array($expectedResult)) { - $this->assertEquals( - $expectedResult, - $actualResult, - 'fromHash() method did not create expected result.' - ); - } else { - $this->assertSame( - $expectedResult, - $actualResult, - 'fromHash() method did not create expected result.' - ); - } - } - - public function testEmptyValueIsEmpty() - { - $fieldType = $this->getFieldTypeUnderTest(); - - $this->assertTrue( - $fieldType->isEmptyValue($fieldType->getEmptyValue()) - ); - } - - /** - * @param mixed $inputSettings - * - * @dataProvider provideValidFieldSettings - */ - public function testValidateFieldSettingsValid($inputSettings) - { - $fieldType = $this->getFieldTypeUnderTest(); - - $validationResult = $fieldType->validateFieldSettings($inputSettings); - - $this->assertIsArray( - $validationResult, - 'The method validateFieldSettings() must return an array.' - ); - $this->assertEquals( - [], - $validationResult, - 'validateFieldSettings() did not consider the input settings valid.' - ); - } - - /** - * @param mixed $inputSettings - * - * @dataProvider provideInvalidFieldSettings - */ - public function testValidateFieldSettingsInvalid($inputSettings) - { - $fieldType = $this->getFieldTypeUnderTest(); - - $validationResult = $fieldType->validateFieldSettings($inputSettings); - - $this->assertIsArray( - $validationResult, - 'The method validateFieldSettings() must return an array.' - ); - - $this->assertNotEquals( - [], - $validationResult, - 'validateFieldSettings() did consider the input settings valid, which should be invalid.' - ); - - foreach ($validationResult as $actualResultElement) { - $this->assertInstanceOf( - ValidationError::class, - $actualResultElement, - 'Validation result of incorrect type.' - ); - } - } - - /** - * @param mixed $inputConfiguration - * - * @dataProvider provideValidValidatorConfiguration - */ - public function testValidateValidatorConfigurationValid($inputConfiguration) - { - $fieldType = $this->getFieldTypeUnderTest(); - - $validationResult = $fieldType->validateValidatorConfiguration($inputConfiguration); - - $this->assertIsArray( - $validationResult, - 'The method validateValidatorConfiguration() must return an array.' - ); - $this->assertEquals( - [], - $validationResult, - 'validateValidatorConfiguration() did not consider the input configuration valid.' - ); - } - - /** - * @param mixed $inputConfiguration - * - * @dataProvider provideInvalidValidatorConfiguration - */ - public function testValidateValidatorConfigurationInvalid($inputConfiguration) - { - $fieldType = $this->getFieldTypeUnderTest(); - - $validationResult = $fieldType->validateValidatorConfiguration($inputConfiguration); - - $this->assertIsArray( - $validationResult, - 'The method validateValidatorConfiguration() must return an array.' - ); - - $this->assertNotEquals( - [], - $validationResult, - 'validateValidatorConfiguration() did consider the input settings valid, which should be invalid.' - ); - - foreach ($validationResult as $actualResultElement) { - $this->assertInstanceOf( - ValidationError::class, - $actualResultElement, - 'Validation result of incorrect type.' - ); - } - } - - /** - * @param mixed $inputConfiguration - * - * @dataProvider provideValidFieldSettings - */ - public function testFieldSettingsToHash($inputSettings) - { - $fieldType = $this->getFieldTypeUnderTest(); - - $hash = $fieldType->fieldSettingsToHash($inputSettings); - - $this->assertIsValidHashValue($hash); - } - - /** - * @param mixed $inputConfiguration - * - * @dataProvider provideValidValidatorConfiguration - */ - public function testValidatorConfigurationToHash($inputConfiguration) - { - $fieldType = $this->getFieldTypeUnderTest(); - - $hash = $fieldType->validatorConfigurationToHash($inputConfiguration); - - $this->assertIsValidHashValue($hash); - } - - /** - * @param mixed $inputConfiguration - * - * @dataProvider provideValidFieldSettings - */ - public function testFieldSettingsFromHash($inputSettings) - { - $fieldType = $this->getFieldTypeUnderTest(); - - $hash = $fieldType->fieldSettingsToHash($inputSettings); - $restoredSettings = $fieldType->fieldSettingsFromHash($hash); - - $this->assertEquals($inputSettings, $restoredSettings); - } - - /** - * @param mixed $inputConfiguration - * - * @dataProvider provideValidValidatorConfiguration - */ - public function testValidatorConfigurationFromHash($inputConfiguration) - { - $fieldType = $this->getFieldTypeUnderTest(); - - $hash = $fieldType->validatorConfigurationToHash($inputConfiguration); - $restoredConfiguration = $fieldType->validatorConfigurationFromHash($hash); - - $this->assertEquals($inputConfiguration, $restoredConfiguration); - } - - /** - * Asserts that the given $actualHash complies to the rules for hashes. - * - * @param mixed $actualHash - * @param array $keyChain - */ - protected function assertIsValidHashValue($actualHash, $keyChain = []) - { - switch ($actualHashType = gettype($actualHash)) { - case 'boolean': - case 'integer': - case 'double': - case 'string': - case 'NULL': - // All valid, just return - return; - - case 'array': - foreach ($actualHash as $key => $childHash) { - $this->assertIsValidHashValue( - $childHash, - array_merge($keyChain, [$key]) - ); - } - - return; - - case 'resource': - case 'object': - $this->fail( - sprintf( - 'Value for $hash[%s] is of invalid type "%s".', - implode('][', $keyChain), - $actualHashType - ) - ); - } - } - - /** - * @dataProvider provideValidDataForValidate - */ - public function testValidateValid($fieldDefinitionData, $value) - { - $validationErrors = $this->doValidate($fieldDefinitionData, $value); - - $this->assertIsArray($validationErrors); - $this->assertEmpty($validationErrors, "Got value:\n" . var_export($validationErrors, true)); - } - - /** - * @dataProvider provideInvalidDataForValidate - */ - public function testValidateInvalid($fieldDefinitionData, $value, $errors) - { - $validationErrors = $this->doValidate($fieldDefinitionData, $value); - - $this->assertIsArray($validationErrors); - $this->assertEquals($errors, $validationErrors); - } - - protected function doValidate($fieldDefinitionData, $value) - { - $fieldType = $this->getFieldTypeUnderTest(); - - /** @var \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition|\PHPUnit\Framework\MockObject\MockObject $fieldDefinitionMock */ - $fieldDefinitionMock = $this->createMock(APIFieldDefinition::class); - - foreach ($fieldDefinitionData as $method => $data) { - if ($method === 'validatorConfiguration') { - $fieldDefinitionMock - ->method('getValidatorConfiguration') - ->willReturn($data); - } - - if ($method === 'fieldSettings') { - $fieldDefinitionMock - ->method('getFieldSettings') - ->willReturn($data); - } - } - - return $fieldType->validate($fieldDefinitionMock, $value); - } - - /** - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition|\PHPUnit\Framework\MockObject\MockObject - */ - protected function getFieldDefinitionMock(array $fieldSettings) - { - /** @var |\PHPUnit\Framework\MockObject\MockObject $fieldDefinitionMock */ - $fieldDefinitionMock = $this->createMock(APIFieldDefinition::class); - $fieldDefinitionMock - ->method('getFieldSettings') - ->willReturn($fieldSettings); - - return $fieldDefinitionMock; - } - - // @todo: More test methods … -} diff --git a/eZ/Publish/SPI/FieldType/ValidationError.php b/eZ/Publish/SPI/FieldType/ValidationError.php deleted file mode 100644 index af832836a2..0000000000 --- a/eZ/Publish/SPI/FieldType/ValidationError.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\FieldType; - -use eZ\Publish\API\Repository\Translatable; - -/** - * Interface for validation errors. - * - * Enforces to return a translatable message, since it will be necessary to - * present validation errors to the user. Thus we need plural form handling and - * replacements of placeholders and so on. - */ -interface ValidationError extends Translatable -{ - /** - * Sets the target element on which the error occurred. - * - * E.g. Property of a Field value which didn't validate against validation. - * Can be a property path compatible with Symfony PropertyAccess component. - * - * Examples: - * - "[StringLengthValidator][minStringLength]" => Target is "minStringLength" key under "StringLengthValidator" key (fieldtype validator configuration) - * - "my_field_definition_identifier" - * - * @param string $target - */ - public function setTarget($target); - - /** - * Returns the target element on which the error occurred. - * - * @return string - */ - public function getTarget(); -} diff --git a/eZ/Publish/SPI/FieldType/Value.php b/eZ/Publish/SPI/FieldType/Value.php deleted file mode 100644 index 3d16034525..0000000000 --- a/eZ/Publish/SPI/FieldType/Value.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\FieldType; - -/** - * Interface for field value classes. - */ -interface Value -{ - /** - * Returns a string representation of the field value. - * - * @return string - */ - public function __toString(); -} diff --git a/eZ/Publish/SPI/IO/BinaryFile.php b/eZ/Publish/SPI/IO/BinaryFile.php deleted file mode 100644 index e26156a774..0000000000 --- a/eZ/Publish/SPI/IO/BinaryFile.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\IO; - -/** - * This class provides an abstract access to binary files. - * - * It allows reading & writing of files in a unified way - */ -class BinaryFile -{ - /** - * Unique persistence layer identifier for this file - * Ex: images/media/images/ez-logo/209-1-eng-GB/eZ-Logo.gif, - * or original/application/2b042138835bb5f48beb9c9df6e86de4.pdf. - * - * @var string - */ - public $id; - - /** - * File size, in bytes. - * - * @var int - */ - public $size; - - /** - * File modification time. - * - * @var \DateTime - */ - public $mtime; - - /** - * HTTP URI to the binary file. - * - * @var string - */ - public $uri; - - /** - * The file's mime type. - * - * Example: text/xml - * - * @deprecated Since 5.3.3, use IO\Handler::getMimeType() - * - * @var string - */ - public $mimeType; -} diff --git a/eZ/Publish/SPI/IO/BinaryFileCreateStruct.php b/eZ/Publish/SPI/IO/BinaryFileCreateStruct.php deleted file mode 100644 index f2d60fa200..0000000000 --- a/eZ/Publish/SPI/IO/BinaryFileCreateStruct.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\IO; - -/** - * Create struct for BinaryFile objects. - */ -class BinaryFileCreateStruct -{ - /** - * File size, in bytes. - * - * @var int - */ - public $size; - - /** - * File modification time. - * - * @var \DateTime - */ - public $mtime; - - /** - * The file's mime type - * If not provided, will be auto-detected by the IOService - * Example: text/xml. - * - * @var string - */ - public $mimeType; - - /** - * Unique identifier for this file - * Ex: images/media/images/ez-logo/209-1-eng-GB/eZ-Logo.gif, - * or original/application/2b042138835bb5f48beb9c9df6e86de4.pdf. - * - * @var mixed - */ - public $id; - - /** @var resource */ - private $inputStream; - - /** - * Returns the file's input resource. - * - * @return resource - */ - public function getInputStream() - { - return $this->inputStream; - } - - /** - * Sets the file's input resource. - * - * @param resource $inputStream - */ - public function setInputStream($inputStream) - { - $this->inputStream = $inputStream; - } -} diff --git a/eZ/Publish/SPI/Limitation/Target.php b/eZ/Publish/SPI/Limitation/Target.php deleted file mode 100644 index 69f9564094..0000000000 --- a/eZ/Publish/SPI/Limitation/Target.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Limitation; - -/** - * Marker interface for PermissionResolver::canUser $targets objects. - * - * It's aimed to provide Limitations with information about intent (result of an action) to evaluate. - * - * @see \eZ\Publish\API\Repository\PermissionResolver::canUser - */ -interface Target -{ -} diff --git a/eZ/Publish/SPI/Limitation/TargetAwareType.php b/eZ/Publish/SPI/Limitation/TargetAwareType.php deleted file mode 100644 index 310386f9e5..0000000000 --- a/eZ/Publish/SPI/Limitation/TargetAwareType.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject as APIValueObject; - -/** - * Represents Limitation type. - * Indicates that Limitation Type implementation properly supports $targets passed as instances of Target. - * - * @see \eZ\Publish\SPI\Limitation\Type - * @see \eZ\Publish\SPI\Limitation\Target - */ -interface TargetAwareType extends Type -{ - /** - * Evaluate ("Vote") against a main value object and targets for the context. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\SPI\Limitation\Target[]|null $targets $targets An array of location, parent or "assignment" - * objects, if null: none where provided by caller - * - * @return bool|null Returns one of ACCESS_* constants - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - */ - public function evaluate( - APILimitationValue $value, - APIUserReference $currentUser, - APIValueObject $object, - array $targets = null - ): ?bool; -} diff --git a/eZ/Publish/SPI/Limitation/Type.php b/eZ/Publish/SPI/Limitation/Type.php deleted file mode 100644 index 0cb601b8e5..0000000000 --- a/eZ/Publish/SPI/Limitation/Type.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Limitation; - -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject as APIValueObject; - -/** - * This interface represent the Limitation Type. - * - * A Limitation is a lot like a Symfony voter, telling the permission system if user has - * access or not. It consists of a Limitation Value which is persisted, and this Limitation - * Type which contains the business logic for evaluate ("vote"), as well as accepting and - * validating the Value object and to generate criteria for content/location searches. - */ -interface Type -{ - /** - * Constants for return value of {@see evaluate()}. - * - * Currently ACCESS_ABSTAIN must mean that evaluate does not support the provided $object or $targets, - * this is currently only supported by role limitations as policy limitations should not allow this. - * - * Note: In future version constant values might change to 1, 0 and -1 as used in Symfony. - * - * @since 5.3.2 - */ - public const ACCESS_GRANTED = true; - public const ACCESS_ABSTAIN = null; - public const ACCESS_DENIED = false; - - /** - * Constants for valueSchema() return values. - * - * Used in cases where a certain value is accepted but the options are to many to return as a hash of options. - * GUI should typically present option to browse content tree to select limitation value(s). - */ - public const VALUE_SCHEMA_LOCATION_ID = 1; - public const VALUE_SCHEMA_LOCATION_PATH = 2; - - /** - * Accepts a Limitation value and checks for structural validity. - * - * Makes sure LimitationValue object and ->limitationValues is of correct type. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - */ - public function acceptValue(APILimitationValue $limitationValue); - - /** - * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). - * - * Make sure {@link acceptValue()} is checked first! - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue - * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] - */ - public function validate(APILimitationValue $limitationValue); - - /** - * Create the Limitation Value. - * - * The is the method to create values as Limitation type needs value knowledge anyway in acceptValue, - * the reverse relation is provided by means of identifier lookup (Value has identifier, and so does RoleService). - * - * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation - */ - public function buildValue(array $limitationValues); - - /** - * Evaluate ("Vote") against a main value object and targets for the context. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid - * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * However if $object or $targets is unsupported by ROLE limitation, ACCESS_ABSTAIN should be returned! - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported - * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]|null $targets An array of location, parent or "assignment" - * objects, if null: none where provided by caller - * - * @return bool|null Returns one of ACCESS_* constants - */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, APIValueObject $object, array $targets = null); - - /** - * Returns Criterion for use in find() query. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException If the limitation does not support - * being used as a Criterion. - * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface|\eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOperator - */ - public function getCriterion(APILimitationValue $value, APIUserReference $currentUser); - - /** - * Returns info on valid $limitationValues. - * - * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name - * of that option, in case of int on of VALUE_SCHEMA_* constants. - * Note: The hash might be an instance of Traversable, and not a native php array. - */ - public function valueSchema(); -} diff --git a/eZ/Publish/SPI/MVC/EventSubscriber/ConfigScopeChangeSubscriber.php b/eZ/Publish/SPI/MVC/EventSubscriber/ConfigScopeChangeSubscriber.php deleted file mode 100644 index fd518ea3ea..0000000000 --- a/eZ/Publish/SPI/MVC/EventSubscriber/ConfigScopeChangeSubscriber.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\MVC\EventSubscriber; - -use eZ\Publish\Core\MVC\Symfony\Event\ScopeChangeEvent; - -/** - * Lets implementing class react to config scope changes. - */ -interface ConfigScopeChangeSubscriber -{ - public function onConfigScopeChange(ScopeChangeEvent $event): void; -} diff --git a/eZ/Publish/SPI/MVC/Templating/RenderStrategy.php b/eZ/Publish/SPI/MVC/Templating/RenderStrategy.php deleted file mode 100644 index 44a54e8bbd..0000000000 --- a/eZ/Publish/SPI/MVC/Templating/RenderStrategy.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\MVC\Templating; - -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\MVC\Symfony\Templating\RenderOptions; - -/** - * Strategy to decide, based on ValueObject descendant type, which - * renderer to pick (like RenderContentStrategy or else). To be used - * mainly by rendering abstraction Twig helpers, but may be used to - * inline rendering of Ibexa VOs anywhere. - */ -interface RenderStrategy -{ - public function supports(ValueObject $valueObject): bool; - - public function render(ValueObject $valueObject, RenderOptions $options): string; -} diff --git a/eZ/Publish/SPI/MVC/View/VariableProvider.php b/eZ/Publish/SPI/MVC/View/VariableProvider.php deleted file mode 100644 index 18e9820f44..0000000000 --- a/eZ/Publish/SPI/MVC/View/VariableProvider.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\MVC\View; - -use eZ\Publish\Core\MVC\Symfony\View\View; - -interface VariableProvider -{ - public function getIdentifier(): string; - - public function getTwigVariables(View $view, array $options = []): object; -} diff --git a/eZ/Publish/SPI/Persistence/Bookmark/CreateStruct.php b/eZ/Publish/SPI/Persistence/Bookmark/CreateStruct.php deleted file mode 100644 index 3920c94046..0000000000 --- a/eZ/Publish/SPI/Persistence/Bookmark/CreateStruct.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Bookmark; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class CreateStruct extends ValueObject -{ - /** - * Name of the bookmarked location. - * - * @deprecated Property is here purely for BC with 5.x. - * - * @var string - */ - public $name; - - /** - * ID of the bookmarked Location. - * - * @var int - */ - public $locationId; - - /** - * ID of bookmark owner. - * - * @var int - */ - public $userId; -} diff --git a/eZ/Publish/SPI/Persistence/Bookmark/Handler.php b/eZ/Publish/SPI/Persistence/Bookmark/Handler.php deleted file mode 100644 index c335f40297..0000000000 --- a/eZ/Publish/SPI/Persistence/Bookmark/Handler.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Bookmark; - -interface Handler -{ - /** - * Create a new bookmark. - * - * @param \eZ\Publish\SPI\Persistence\Bookmark\CreateStruct $createStruct - * - * @return \eZ\Publish\SPI\Persistence\Bookmark\Bookmark - */ - public function create(CreateStruct $createStruct): Bookmark; - - /** - * Delete a bookmark. - * - * @param int $bookmarkId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function delete(int $bookmarkId): void; - - /** - * Get bookmark by user id and location id. - * - * @param int $userId - * @param array $locationIds - * - * @return \eZ\Publish\SPI\Persistence\Bookmark\Bookmark[] - */ - public function loadByUserIdAndLocationId(int $userId, array $locationIds): array; - - /** - * Loads bookmarks owned by user. - * - * @param int $userId - * @param int $offset the start offset for paging - * @param int $limit the number of bookmarked locations returned - * - * @return \eZ\Publish\SPI\Persistence\Bookmark\Bookmark[] - */ - public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1): array; - - /** - * Count bookmarks owned by user. - * - * @param int $userId - * - * @return int - */ - public function countUserBookmarks(int $userId): int; - - /** - * Notifies the underlying engine that a location was swapped. - * - * This method triggers the change of the bookmarked locations. - * - * @param int $location1Id ID of first location - * @param int $location2Id ID of second location - */ - public function locationSwapped(int $location1Id, int $location2Id): void; -} diff --git a/eZ/Publish/SPI/Persistence/Content.php b/eZ/Publish/SPI/Persistence/Content.php deleted file mode 100644 index fa06704d8c..0000000000 --- a/eZ/Publish/SPI/Persistence/Content.php +++ /dev/null @@ -1,31 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence; - -/** - * Content value object, bound to a version. - * This object aggregates the following: - * - Version metadata - * - Content metadata - * - Fields. - */ -class Content extends ValueObject -{ - /** - * VersionInfo object for this content's version. - * - * @var \eZ\Publish\SPI\Persistence\Content\VersionInfo - */ - public $versionInfo; - - /** - * Field objects for this content. - * - * @var \eZ\Publish\SPI\Persistence\Content\Field[] - */ - public $fields; -} diff --git a/eZ/Publish/SPI/Persistence/Content/ContentInfo.php b/eZ/Publish/SPI/Persistence/Content/ContentInfo.php deleted file mode 100644 index 753cb3dae3..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/ContentInfo.php +++ /dev/null @@ -1,137 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * This class provides all version independent information of the content object. - * It is similar to {@link \eZ\Publish\API\Repository\Values\Content\ContentInfo}, but for the persistence layer. - * Thus it only contains raw data. - */ -class ContentInfo extends ValueObject -{ - public const STATUS_DRAFT = 0; - public const STATUS_PUBLISHED = 1; - public const STATUS_TRASHED = 2; - - /** @deprecated Use ContentInfo::STATUS_TRASHED */ - public const STATUS_ARCHIVED = self::STATUS_TRASHED; - - /** - * Content's unique ID. - * - * @var mixed - */ - public $id; - - /** - * Computed name (via name schema) in the main language. - * - * @var string - */ - public $name; - - /** - * Content type Id. - * - * @var int - */ - public $contentTypeId; - - /** - * Section id the content is assigned to. - * - * @var int - */ - public $sectionId; - - /** - * Version number of the current published version. - * If the content is not published yet (newly created draft), will be 1. - * - * @var int - */ - public $currentVersionNo; - - /** - * @deprecated Use SPI\ContentInfo::$status (with value ContentInfo::STATUS_PUBLISHED) - * - * Flag indicating if content is currently published. - * - * @var bool - */ - public $isPublished; - - /** - * Content owner's id. - * - * @var int - */ - public $ownerId; - - /** - * Content modification date, as a UNIX timestamp. - * - * @var int - */ - public $modificationDate; - - /** - * Content publication date, as a UNIX timestamp. - * - * @var int - */ - public $publicationDate; - - /** - * Indicates if the content is shown in the main language if its not present in an other requested language. - * - * @var bool - */ - public $alwaysAvailable; - - /** - * Remote identifier used as a custom identifier for the object. - * - * @var string - */ - public $remoteId; - - /** - * The main language code of the content. - * - * @var string - */ - public $mainLanguageCode; - - /** - * Identifier of the main location. - * - * If the content object has multiple locations, - * $mainLocationId will point to the main one. - * - * @var mixed - */ - public $mainLocationId; - - /** - * Status of the content. - * - * Replaces deprecated SPI\ContentInfo::$isPublished. - * - * @var int - */ - public $status; - - /** - * Flag indicating if content is currently hidden. - * - * @var bool - */ - public $isHidden = false; -} diff --git a/eZ/Publish/SPI/Persistence/Content/ContentItem.php b/eZ/Publish/SPI/Persistence/Content/ContentItem.php deleted file mode 100644 index d0f509a56b..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/ContentItem.php +++ /dev/null @@ -1,57 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * Content item Value Object - a composite of Content and Type instances. - * - * @property-read \eZ\Publish\SPI\Persistence\Content $content - * @property-read \eZ\Publish\SPI\Persistence\Content\ContentInfo $contentInfo - * @property-read \eZ\Publish\SPI\Persistence\Content\Type $type - */ -final class ContentItem extends ValueObject -{ - /** @var \eZ\Publish\SPI\Persistence\Content */ - protected $content; - - /** @var \eZ\Publish\SPI\Persistence\Content\ContentInfo */ - protected $contentInfo; - - /** @var \eZ\Publish\SPI\Persistence\Content\Type */ - protected $type; - - /** - * @internal for internal use by Repository Storage abstraction - */ - public function __construct(Content $content, ContentInfo $contentInfo, Type $type) - { - parent::__construct([]); - $this->content = $content; - $this->contentInfo = $contentInfo; - $this->type = $type; - } - - public function getContent(): Content - { - return $this->content; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } - - public function getType(): Type - { - return $this->type; - } -} diff --git a/eZ/Publish/SPI/Persistence/Content/CreateStruct.php b/eZ/Publish/SPI/Persistence/Content/CreateStruct.php deleted file mode 100644 index 3556e02422..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/CreateStruct.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class CreateStruct extends ValueObject -{ - /** @var string[] Eg. array( 'eng-GB' => "New Article" ) */ - public $name; - - /** @var int */ - public $typeId; - - /** @var int */ - public $sectionId; - - /** @var int */ - public $ownerId; - - /** - * ContentId, contentVersion and mainLocationId are allowed to be left empty - * when used on with this struct as these values are created by the create method. - * - * @var \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct[] - */ - public $locations = []; - - /** - * Contains *all* fields of the object to be created. - * - * This attribute should contain *all* fields (in all language) of the - * object to be created. If a field is not translatable, it may only occur - * once. The storage layer will automatically take care that such fields - * are assigned to each language version. - * - * @var Field[] - */ - public $fields = []; - - /** @var bool Always available flag */ - public $alwaysAvailable = false; - - /** @var string Remote identifier used as a custom identifier for the object */ - public $remoteId; - - /** - * Language id the content was initially created in. - * - * @var mixed - */ - public $initialLanguageId; - - /** - * Optional, main language of the content, if not set $initialLanguageId will be used instead. - * - * Typical use is copy operations where content main language and version initial language might differ. - * - * @var mixed|null - */ - public $mainLanguageId; - - /** - * Modification date. - * - * @var int - */ - public $modified; - - /** - * Is hidden flag. - * - * @var bool - */ - public $isHidden; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Field.php b/eZ/Publish/SPI/Persistence/Content/Field.php deleted file mode 100644 index b3a9e6531f..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Field.php +++ /dev/null @@ -1,66 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class Field extends ValueObject -{ - /** - * Field ID. - * - * @var int - */ - public $id; - - /** - * Corresponding field definition. - * - * @var int - */ - public $fieldDefinitionId; - - /** - * Data type name. - * - * @var string - */ - public $type; - - /** - * Value of the field. - * - * @var \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public $value; - - /** - * Language code of this Field. - * - * @var string - */ - public $languageCode; - - /** - * @var int|null Null if not created yet - * - * @todo Normally we would use a create struct here - */ - public $versionNo; - - /** - * Clone object properties. - * - * Note: `clone` keyword performs shallow copy of an object. - * For properties being objects this means that a reference - * is copied instead of the actual object. - */ - public function __clone() - { - $this->value = clone $this->value; - } -} diff --git a/eZ/Publish/SPI/Persistence/Content/Handler.php b/eZ/Publish/SPI/Persistence/Content/Handler.php deleted file mode 100644 index ff541f7406..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Handler.php +++ /dev/null @@ -1,377 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content; - -// @todo We must verify whether we want to type cast on the "Criterion" interface or abstract class -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; - -/** - * The Content Handler interface defines content operations on the storage engine. - * - * The basic operations which are performed on content objects are collected in - * this interface. Typically this interface would be used by a service managing - * business logic for content objects. - */ -interface Handler -{ - /** - * Creates a new Content entity in the storage engine. - * - * The values contained inside the $content will form the basis of stored - * entity. - * - * Will contain always a complete list of fields. - * - * @param \eZ\Publish\SPI\Persistence\Content\CreateStruct $content Content creation struct. - * - * @return \eZ\Publish\SPI\Persistence\Content Content value object - */ - public function create(CreateStruct $content); - - /** - * Creates a new draft version from $contentId in $srcVersion number. - * - * Copies all fields from $contentId in $srcVersion and creates a new - * version of the referred Content from it. - * - * @param mixed $contentId - * @param mixed $srcVersion - * @param mixed $userId - * @param string|null $languageCode - * - * @return \eZ\Publish\SPI\Persistence\Content - */ - public function createDraftFromVersion($contentId, $srcVersion, $userId, ?string $languageCode = null); - - /** - * Returns the raw data of a content object identified by $id, in a struct. - * - * If you want to load current version, $version number can be omitted to make sure - * you don't need to rely on search index (async) or having to load in two steps - * (first content info then content, risking changes in between to current version). - * - * Optionally a translation filter may be specified. If specified only the - * translations with the listed language codes will be retrieved. If not, - * all translations will be retrieved. - * - * @param int|string $id - * @param int|null $version - * @param string[]|null $translations - * - * @return \eZ\Publish\SPI\Persistence\Content Content value object - */ - public function load($id, $version = null, array $translations = null); - - /** - * Return list of unique Content, with content id as key. - * - * Missing items (NotFound) will be missing from the array and not cause an exception, it's up - * to calling logic to determine if this should cause exception or not. - * - * If items are missing but for other reasons then not being found, for instance exceptions during loading field - * data. Then the exception will be logged as warning or error depending on severity. - * The most common case of possible exceptions during loading of Content data is migration, - * where either custom Field Type configuration or implementation might not be aligned with new - * version of the system. - * - * NOTE!!: If you want to take always available flag into account, append main language - * to the list of languages(unless caller is asking for all languages). In some edge cases you'll end up with a - * bit more data returned, but upside is that storage engine is able to handle far larger datasets. - * - * @param int[] $contentIds - * @param string[]|null $translations - * - * @return \eZ\Publish\SPI\Persistence\Content[] - */ - public function loadContentList(array $contentIds, array $translations = null): iterable; - - /** - * Returns the metadata object for a content identified by $contentId. - * - * @param int|string $contentId - * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo - */ - public function loadContentInfo($contentId); - - /** - * Return list of unique Content Info, with content id as key. - * - * Missing items (NotFound) will be missing from the array and not cause an exception, it's up - * to calling logic to determine if this should cause exception or not. - * - * @param array $contentIds - * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo[] - */ - public function loadContentInfoList(array $contentIds); - - /** - * Returns the metadata object for a content identified by $remoteId. - * - * @param mixed $remoteId - * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo - */ - public function loadContentInfoByRemoteId($remoteId); - - /** - * Returns the version object for a content/version identified by $contentId and $versionNo. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If version is not found - * - * @param int|string $contentId - * @param int|null $versionNo Version number to load, loads current version if null. - * - * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo - */ - public function loadVersionInfo($contentId, $versionNo = null); - - /** - * Returns the number of versions with draft status created by the given $userId. - * - * @param int $userId - * - * @return int - */ - public function countDraftsForUser(int $userId): int; - - /** - * Returns all versions with draft status created by the given $userId. - * - * @param int $userId - * - * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo[] - */ - public function loadDraftsForUser($userId); - - /** - * Loads drafts for a user when content is not in the trash. The list is sorted by modification date. - * - * @param int $userId - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo[] - */ - public function loadDraftListForUser(int $userId, int $offset = 0, int $limit = -1): array; - - /** - * Sets the status of object identified by $contentId and $version to $status. - * - * The $status can be one of VersionInfo::STATUS_DRAFT, VersionInfo::STATUS_PUBLISHED, VersionInfo::STATUS_ARCHIVED - * When status is set to VersionInfo::STATUS_PUBLISHED content status is updated to ContentInfo::STATUS_PUBLISHED - * - * @param int $contentId - * @param int $status - * @param int $version - * - * @return bool - */ - public function setStatus($contentId, $status, $version); - - /** - * Updates a content object meta data, identified by $contentId. - * - * @param int $contentId - * @param \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct $content - * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo - */ - public function updateMetadata($contentId, MetadataUpdateStruct $content); - - /** - * Updates a content version, identified by $contentId and $versionNo. - * - * @param int $contentId - * @param int $versionNo - * @param \eZ\Publish\SPI\Persistence\Content\UpdateStruct $content - * - * @return \eZ\Publish\SPI\Persistence\Content - */ - public function updateContent($contentId, $versionNo, UpdateStruct $content); - - /** - * Deletes all versions and fields, all locations (subtree), and all relations. - * - * Removes the relations, but not the related objects. All subtrees of the - * assigned nodes of this content objects are removed (recursively). - * - * @param int $contentId - * - * @return bool - */ - public function deleteContent($contentId); - - /** - * Deletes given version, its fields, node assignment, relations and names. - * - * Removes the relations, but not the related objects. - * - * @param int $contentId - * @param int $versionNo - * - * @return bool - */ - public function deleteVersion($contentId, $versionNo); - - /** - * Returns the versions for $contentId. - * - * Result is returned with oldest version first (sorted by created, alternatively version number or id if auto increment). - * - * @param int $contentId - * @param mixed|null $status Optional argument to filter versions by status, like {@see VersionInfo::STATUS_ARCHIVED}. - * @param int $limit Limit for items returned, -1 means none. - * - * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo[] - */ - public function listVersions($contentId, $status = null, $limit = -1); - - /** - * Copy Content with Fields, Versions & Relations from $contentId in $version. - * - * Copies all fields and relations from $contentId in $version (or all versions if false) - * to a new object which is returned. Version numbers are maintained. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If content or version is not found - * - * @param mixed $contentId - * @param mixed|null $versionNo Copy all versions if left null - * @param int|null $newOwnerId By default owner is same content we copy, for other cases set owner here to change it. - * E.g. In order to give person copying access to edit (if owner limitation), use this to set copier as owner. - * - * @return \eZ\Publish\SPI\Persistence\Content - */ - public function copy($contentId, $versionNo = null, $newOwnerId = null); - - /** - * Creates a relation between $sourceContentId in $sourceContentVersionNo - * and $destinationContentId with a specific $type. - * - * @todo Should the existence verifications happen here or is this supposed to be handled at a higher level? - * - * @param \eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct $createStruct - * - * @return \eZ\Publish\SPI\Persistence\Content\Relation - */ - public function addRelation(RelationCreateStruct $createStruct); - - /** - * Removes a relation by relation Id. - * - * @todo Should the existence verifications happen here or is this supposed to be handled at a higher level? - * - * @param mixed $relationId - * @param int $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, - * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, - * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, - * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD} - */ - public function removeRelation($relationId, $type); - - /** - * Loads relations from $sourceContentId. Optionally, loads only those with $type and $sourceContentVersionNo. - * - * @param mixed $sourceContentId Source Content ID - * @param mixed|null $sourceContentVersionNo Source Content Version, null if not specified - * @param int|null $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, - * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, - * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, - * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD} - * - * @return \eZ\Publish\SPI\Persistence\Content\Relation[] - */ - public function loadRelations($sourceContentId, $sourceContentVersionNo = null, $type = null); - - /** - * Counts relations from $destinationContentId only against published versions. Optionally, count only those with $type. - * - * @param int $destinationContentId Destination Content ID - * @param int|null $type The relation type bitmask {@see \eZ\Publish\API\Repository\Values\Content\Relation} - * - * @return int - */ - public function countReverseRelations(int $destinationContentId, ?int $type = null): int; - - /** - * Loads relations from $contentId. Optionally, loads only those with $type. - * - * Only loads relations against published versions. - * - * @param mixed $destinationContentId Destination Content ID - * @param int|null $type {@see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, - * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, - * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, - * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD} - * - * @return \eZ\Publish\SPI\Persistence\Content\Relation[] - */ - public function loadReverseRelations($destinationContentId, $type = null); - - /** - * Loads paginated relations from $contentId. Optionally, loads only those with $type. - * - * Only loads relations against published versions. - * - * @param int $destinationContentId Destination Content ID - * @param int $offset - * @param int $limit - * @param int|null $type The relation type bitmask {@see \eZ\Publish\API\Repository\Values\Content\Relation} - * - * @return \eZ\Publish\SPI\Persistence\Content\Relation[] - */ - public function loadReverseRelationList( - int $destinationContentId, - int $offset = 0, - int $limit = -1, - ?int $type = null - ): array; - - /** - * Performs the publishing operations required to set the version identified by $updateStruct->versionNo and - * $updateStruct->id as the published one. - * - * @param int $contentId - * @param int $versionNo - * @param \eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct $metaDataUpdateStruct - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * - * @return \eZ\Publish\SPI\Persistence\Content The published Content - */ - public function publish($contentId, $versionNo, MetadataUpdateStruct $metaDataUpdateStruct); - - /** - * Delete the specified translation from all the Versions of a Content Object. - * - * @param int $contentId - * @param string $languageCode language code of the translation - */ - public function deleteTranslationFromContent($contentId, $languageCode); - - /** - * Remove the specified Translation from the given Version Draft of a Content Object. - * - * @param int $contentId - * @param int $versionNo - * @param string $languageCode - * - * @return \eZ\Publish\SPI\Persistence\Content The Content Draft w/o removed Translation - */ - public function deleteTranslationFromDraft($contentId, $versionNo, $languageCode); - - /** - * @param array<int> $contentIds - * - * @return array<\eZ\Publish\SPI\Persistence\Content\VersionInfo> - * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - */ - public function loadVersionInfoList(array $contentIds): array; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Language.php b/eZ/Publish/SPI/Persistence/Content/Language.php deleted file mode 100644 index 3b10bad24e..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Language.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * Struct containing accessible properties on Language entities. - */ -class Language extends ValueObject -{ - /** - * Language ID. - * - * @var int - */ - public $id; - - /** - * Language Code (eg: eng-GB). - * - * @var string - */ - public $languageCode; - - /** - * Human readable language name. - * - * @var string - */ - public $name; - - /** - * Indicates if language is enabled or not. - * - * @var bool - */ - public $isEnabled = true; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Language/CreateStruct.php b/eZ/Publish/SPI/Persistence/Content/Language/CreateStruct.php deleted file mode 100644 index e9daa6d494..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Language/CreateStruct.php +++ /dev/null @@ -1,36 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Language; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * Struct containing accessible properties when creating Language entities. - */ -class CreateStruct extends ValueObject -{ - /** - * Language Code (eg: eng-GB). - * - * @var string - */ - public $languageCode; - - /** - * Human readable language name. - * - * @var string - */ - public $name; - - /** - * Indicates if language is enabled or not. - * - * @var bool - */ - public $isEnabled = true; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Language/Handler.php b/eZ/Publish/SPI/Persistence/Content/Language/Handler.php deleted file mode 100644 index b1ba351591..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Language/Handler.php +++ /dev/null @@ -1,95 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Language; - -use eZ\Publish\SPI\Persistence\Content\Language; - -/** - * Language Handler interface. - */ -interface Handler -{ - /** - * Create a new language. - * - * @param \eZ\Publish\SPI\Persistence\Content\Language\CreateStruct $struct - * - * @return \eZ\Publish\SPI\Persistence\Content\Language - */ - public function create(CreateStruct $struct); - - /** - * Update language. - * - * @param \eZ\Publish\SPI\Persistence\Content\Language $struct - */ - public function update(Language $struct); - - /** - * Get language by id. - * - * @param mixed $id - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language could not be found by $id - * - * @return \eZ\Publish\SPI\Persistence\Content\Language - */ - public function load($id); - - /** - * Get list of languages by id. - * - * Missing items (NotFound) will be missing from the returned iterable and not cause an exception, it's up - * to calling logic to determine if this should cause exception or not. - * - * @param array $ids - * - * @return \eZ\Publish\SPI\Persistence\Content\Language[]|iterable - */ - public function loadList(array $ids): iterable; - - /** - * Get language by Language Code (eg: eng-GB). - * - * @param string $languageCode - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language could not be found by $languageCode - * - * @return \eZ\Publish\SPI\Persistence\Content\Language - */ - public function loadByLanguageCode($languageCode); - - /** - * Get list of languages by Language Code (eg: eng-GB). - * - * Missing items (NotFound) will be missing from the returned iterable and not cause an exception, it's up - * to calling logic to determine if this should cause exception or not. - * - * @param string[] $languageCodes - * - * @return \eZ\Publish\SPI\Persistence\Content\Language[]|iterable - */ - public function loadListByLanguageCodes(array $languageCodes): iterable; - - /** - * Get all languages. - * - * Return list of languages where key of hash is language code. - * - * @return \eZ\Publish\SPI\Persistence\Content\Language[] - */ - public function loadAll(); - - /** - * Delete a language. - * - * @throws \LogicException If language could not be deleted - * - * @param mixed $id - */ - public function delete($id); -} diff --git a/eZ/Publish/SPI/Persistence/Content/Location.php b/eZ/Publish/SPI/Persistence/Content/Location.php deleted file mode 100644 index 0c51886b49..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Location.php +++ /dev/null @@ -1,131 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * Struct containing accessible properties on Location entities. - */ -class Location extends ValueObject -{ - // Following constants borrowed from eZContentObjectTreeNode, for data compatibility. - // Actual names ought to be changed to better match current concepts. - public const SORT_FIELD_PATH = 1; - public const SORT_FIELD_PUBLISHED = 2; - public const SORT_FIELD_MODIFIED = 3; - public const SORT_FIELD_SECTION = 4; - public const SORT_FIELD_DEPTH = 5; - public const SORT_FIELD_CLASS_IDENTIFIER = 6; - public const SORT_FIELD_CLASS_NAME = 7; - public const SORT_FIELD_PRIORITY = 8; - public const SORT_FIELD_NAME = 9; - public const SORT_FIELD_MODIFIED_SUBNODE = 10; - public const SORT_FIELD_NODE_ID = 11; - public const SORT_FIELD_CONTENTOBJECT_ID = 12; - - public const SORT_ORDER_DESC = 0; - public const SORT_ORDER_ASC = 1; - - /** - * Location ID. - * - * @var mixed Location ID. - */ - public $id; - - /** - * Location priority. - * - * Position of the Location among its siblings when sorted using priority - * sort order. - * - * @var int - */ - public $priority; - - /** - * Indicates that the Location entity has been explicitly marked as hidden. - * - * @var bool - */ - public $hidden; - - /** - * Indicates that the Location is implicitly marked as hidden by a parent - * location. - * - * @var bool - */ - public $invisible; - - /** - * Remote ID. - * - * A universally unique identifier. - * - * @var mixed - */ - public $remoteId; - - /** - * ID of the corresponding {@link \eZ\Publish\SPI\Persistence\Content}. - * - * @var mixed Content ID. - */ - public $contentId; - - /** - * Parent ID. - * - * @var mixed Location ID. - */ - public $parentId; - - /** - * Legacy format of the url alias. - * - * This field might be removed in a later version. - * - * @deprecated Since 5.4, planned to be removed in 6.0 - * - * @var string - */ - public $pathIdentificationString; - - /** - * The materialized path of the location entry, eg: /1/2/. - * - * @var string - */ - public $pathString; - - /** - * Depth location has in the location tree. - * - * @var int - */ - public $depth; - - /** - * Specifies which property the child locations should be sorted on. - * - * Valid values are found at {@link Location::SORT_FIELD_*} - * - * @var mixed - */ - public $sortField; - - /** - * Specifies whether the sort order should be ascending or descending. - * - * Valid values are {@link Location::SORT_ORDER_*} - * - * @var mixed - */ - public $sortOrder; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Location/CreateStruct.php b/eZ/Publish/SPI/Persistence/Content/Location/CreateStruct.php deleted file mode 100644 index b0f95eeb94..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Location/CreateStruct.php +++ /dev/null @@ -1,110 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Location; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class CreateStruct extends ValueObject -{ - /** - * Location priority. - * - * Position of the Location among its siblings when sorted using priority - * sort order. - * - * @var int - */ - public $priority = 0; - - /** - * Indicates that the Location entity has been explicitly marked as hidden. - * - * @var bool - */ - public $hidden = false; - - /** - * Indicates that the Location is implicitly marked as hidden by a parent - * location. - * - * @var bool - */ - public $invisible = false; - - /** - * Remote ID. - * - * A universally unique identifier. - * - * @var mixed - */ - public $remoteId; - - /** - * ID of the corresponding {@link Content}. - * - * @var mixed Content ID. - */ - public $contentId; - - /** - * version of the corresponding {@link Content}. - * - * @todo Rename to $contentVersionNo? - * - * @var int Content version. - */ - public $contentVersion; - - /** - * Legacy format of the url alias. - * - * This field might be removed in a later version. - * - * @deprecated Since 5.4, planned to be removed in 6.0 - * - * @var string - */ - public $pathIdentificationString; - - /** - * Identifier of the main location. - * - * If the content object in this location has multiple locations, - * $mainLocationId will point to the main one. - * This is allowed to be set to true, this will mean this should become main location - * (@todo Find a better way to deal with being able to create the main location) - * - * @var mixed|true - */ - public $mainLocationId = true; - - /** - * Specifies which property the child locations should be sorted on. - * - * Valid values are found at {@link Location::SORT_FIELD_*} - * - * @var mixed - */ - public $sortField; - - /** - * Specifies whether the sort order should be ascending or descending. - * - * Valid values are {@link Location::SORT_ORDER_*} - * - * @var mixed - */ - public $sortOrder; - - /** - * Parent location's Id. - * - * @var int - */ - public $parentId; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Location/Handler.php b/eZ/Publish/SPI/Persistence/Content/Location/Handler.php deleted file mode 100644 index ff36c20df7..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Location/Handler.php +++ /dev/null @@ -1,260 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Location; - -use eZ\Publish\SPI\Persistence\Content\Location; - -/** - * The Location Handler interface defines operations on Location elements in the storage engine. - */ -interface Handler -{ - /** - * Loads the data for the location identified by $locationId. - * - * @param int $locationId - * @param string[]|null $translations If set, NotFound is thrown if content is not in given translation. - * @param bool $useAlwaysAvailable Respect always available flag on content, where main language is valid translation fallback. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * - * @return \eZ\Publish\SPI\Persistence\Content\Location - */ - public function load($locationId, array $translations = null, bool $useAlwaysAvailable = true); - - /** - * Return list of unique Locations, with location id as key. - * - * Missing items (NotFound) will be missing from the array and not cause an exception, it's up - * to calling logic to determine if this should cause exception or not. - * - * @param int[] $locationIds - * @param string[]|null $translations If set, only locations with content in given translations are returned. - * @param bool $useAlwaysAvailable Respect always available flag on content, where main language is valid translation fallback. - * - * @return \eZ\Publish\SPI\Persistence\Content\Location[]|iterable - */ - public function loadList(array $locationIds, array $translations = null, bool $useAlwaysAvailable = true): iterable; - - /** - * Loads the subtree ids of the location identified by $locationId. - * - * @param int $locationId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * - * @return array Location ids are in the index, Content ids in the value. - */ - public function loadSubtreeIds($locationId); - - /** - * Loads the data for the location identified by $remoteId. - * - * @param string $remoteId - * @param string[]|null $translations If set, NotFound is thrown if content is not in given translation. - * @param bool $useAlwaysAvailable Respect always available flag on content, where main language is valid translation fallback. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * - * @return \eZ\Publish\SPI\Persistence\Content\Location - */ - public function loadByRemoteId($remoteId, array $translations = null, bool $useAlwaysAvailable = true); - - /** - * Loads all locations for $contentId, optionally limited to a sub tree - * identified by $rootLocationId. - * - * @param int $contentId - * @param int $rootLocationId - * - * @return \eZ\Publish\SPI\Persistence\Content\Location[] - */ - public function loadLocationsByContent($contentId, $rootLocationId = null); - - /** - * Loads all locations for $contentId in trash, optionally limited to a sub tree - * identified by $rootLocationId. - * - * @return \eZ\Publish\SPI\Persistence\Content\Location[] - */ - public function loadLocationsByTrashContent(int $contentId, ?int $rootLocationId = null): array; - - /** - * Loads all parent Locations for unpublished Content by given $contentId. - * - * - * @param mixed $contentId - * - * @return \eZ\Publish\SPI\Persistence\Content\Location[] - */ - public function loadParentLocationsForDraftContent($contentId); - - /** - * Copy location object identified by $sourceId, into destination identified by $destinationParentId. - * - * Performs a deep copy of the location identified by $sourceId and all of - * its child locations, copying the most recent published content object - * for each location to a new content object without any additional version - * information. Relations for published version are copied. URLs are not touched at all. - * - * @param mixed $sourceId - * @param mixed $destinationParentId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If $sourceId or $destinationParentId are invalid - * - * @return \eZ\Publish\SPI\Persistence\Content\Location the newly created Location. - */ - public function copySubtree($sourceId, $destinationParentId); - - public function getSubtreeSize(string $path): int; - - /** - * Moves location identified by $sourceId into new parent identified by $destinationParentId. - * - * Performs a full move of the location identified by $sourceId to a new - * destination, identified by $destinationParentId. Relations do not need - * to be updated, since they refer to Content. URLs are not touched. - * - * @param mixed $sourceId - * @param mixed $destinationParentId - * - * @return bool - */ - public function move($sourceId, $destinationParentId); - - /** - * Marks the given nodes and all ancestors as modified. - * - * Optionally a time stamp with the modification date may be specified, - * otherwise the current time is used. - * - * @deprecated As of 6.8, not been used by repository since 5.x. - * - * @param int|string $locationId - * @param int $timestamp - */ - public function markSubtreeModified($locationId, $timestamp = null); - - /** - * Sets a location to be hidden, and it self + all children to invisible. - * - * @param mixed $id Location ID - */ - public function hide($id); - - /** - * Sets a location to be unhidden, and self + children to visible unless a parent is hiding the tree. - * If not make sure only children down to first hidden node is marked visible. - * - * @param mixed $id - */ - public function unHide($id); - - /** - * Sets a location + all children to invisible. - * - * @param int $id Location ID - */ - public function setInvisible(int $id): void; - - /** - * Sets a location + all children to visible. - * - * @param int $id Location ID - */ - public function setVisible(int $id): void; - - /** - * Swaps the content object being pointed to by a location object. - * - * Make the location identified by $locationId1 refer to the Content - * referred to by $locationId2 and vice versa. - * - * @param mixed $locationId1 - * @param mixed $locationId2 - * - * @return bool - */ - public function swap($locationId1, $locationId2); - - /** - * Updates an existing location. - * - * @param \eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct $location - * @param int $locationId - */ - public function update(UpdateStruct $location, $locationId); - - /** - * Creates a new location rooted at $location->parentId. - * - * @param \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct $location - * - * @return \eZ\Publish\SPI\Persistence\Content\Location - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if parent Location does not exist - */ - public function create(CreateStruct $location); - - /** - * Removes all Locations under and including $locationId. - * - * Performs a recursive delete on the location identified by $locationId, - * including all of its child locations. Content which is not referred to - * by any other location is automatically removed. Content which looses its - * main Location will get the first of its other Locations assigned as the - * new main Location. - * - * @param mixed $locationId - * - * @return bool - */ - public function removeSubtree($locationId); - - /** - * Set section on all content objects in the subtree. - * Only main locations will be updated. - * - * @todo This can be confusing (regarding permissions and main/multi location). - * So method is for the time being not in PublicAPI so people can instead - * write scripts using their own logic against the assignSectionToContent() api. - * - * @param mixed $locationId - * @param mixed $sectionId - */ - public function setSectionForSubtree($locationId, $sectionId); - - /** - * Changes main location of content identified by given $contentId to location identified by given $locationId. - * - * @param mixed $contentId - * @param mixed $locationId - */ - public function changeMainLocation($contentId, $locationId); - - /** - * Get the total number of all existing Locations. Can be combined with loadAllLocations. - * - * @return int - */ - public function countAllLocations(); - - /** - * Bulk-load all existing Locations, constrained by $limit and $offset to paginate results. - * - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\SPI\Persistence\Content\Location[] - */ - public function loadAllLocations($offset, $limit); - - /** - * Counts locations for a given content represented by its id. - */ - public function countLocationsByContent(int $contentId): int; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Location/Trash/Handler.php b/eZ/Publish/SPI/Persistence/Content/Location/Trash/Handler.php deleted file mode 100644 index 4eda8ce721..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Location/Trash/Handler.php +++ /dev/null @@ -1,93 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Location\Trash; - -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; - -/** - * The Trash Handler interface defines operations on Location elements in the storage engine. - */ -interface Handler -{ - /** - * Loads the data for the trashed location identified by $id. - * $id is the same as original location (which has been previously trashed). - * - * @param int $id - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * - * @return \eZ\Publish\SPI\Persistence\Content\Location\Trashed - */ - public function loadTrashItem($id); - - /** - * Sends a subtree starting to $locationId to the trash - * and returns a Trashed object corresponding to $locationId. - * - * Moves all locations in the subtree to the Trash. The associated content - * objects are left untouched. - * - * @param mixed $locationId - * - * @return \eZ\Publish\SPI\Persistence\Content\Location\Trashed|null null if location was deleted, otherwise Trashed object - */ - public function trashSubtree($locationId); - - /** - * Returns a trashed location to normal state. - * - * Recreates the originally trashed location in the new position. - * If this is not possible (because the old location does not exist any more), - * a ParentNotFound exception is thrown. - * - * Returns newly restored location Id. - * - * @param mixed $trashedId - * @param mixed $newParentId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If $newParentId is invalid - * - * @return int Newly restored location id - */ - public function recover($trashedId, $newParentId); - - /** - * Returns all trashed locations satisfying the $criterion (if provided), sorted with $sort (if any). - * - * If no criterion is provided (null), no filter is applied. - * - * TrashResult->totalCount will ignore limit and offset and representing the total amount of trashed items - * matching the criterion. - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion - * @param int $offset Offset to start listing from, 0 by default - * @param int $limit Limit for the listing. Null by default (no limit) - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sort - * - * @return \eZ\Publish\SPI\Persistence\Content\Location\Trashed[]|\eZ\Publish\SPI\Persistence\Content\Location\Trash\TrashResult - */ - public function findTrashItems(Criterion $criterion = null, $offset = 0, $limit = null, array $sort = null); - - /** - * Empties the trash - * Everything contained in the trash must be removed. - * - * @return \eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResultList - */ - public function emptyTrash(); - - /** - * Removes a trashed location identified by $trashedLocationId from trash - * Associated content has to be deleted. - * - * @param int $trashedId - * - * @return \eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult - */ - public function deleteTrashItem($trashedId); -} diff --git a/eZ/Publish/SPI/Persistence/Content/Location/Trash/TrashResult.php b/eZ/Publish/SPI/Persistence/Content/Location/Trash/TrashResult.php deleted file mode 100644 index 58b993645b..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Location/Trash/TrashResult.php +++ /dev/null @@ -1,35 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Location\Trash; - -use ArrayIterator; -use eZ\Publish\API\Repository\Values\ValueObject; - -class TrashResult extends ValueObject implements \IteratorAggregate -{ - /** - * The total number of Trash items matching criteria (ignores offset & limit arguments). - * - * @var int - */ - public $totalCount = 0; - - /** - * The value objects found for the query. - * - * @var \eZ\Publish\SPI\Persistence\Content\Location\Trashed[] - */ - public $items = []; - - /** - * {@inheritdoc} - */ - public function getIterator(): \Traversable - { - return new ArrayIterator($this->items); - } -} diff --git a/eZ/Publish/SPI/Persistence/Content/Location/Trashed.php b/eZ/Publish/SPI/Persistence/Content/Location/Trashed.php deleted file mode 100644 index 3cae5d9ac7..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Location/Trashed.php +++ /dev/null @@ -1,25 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Location; - -use eZ\Publish\SPI\Persistence\Content\Location; - -/** - * Struct containing accessible properties on TrashedLocation entities. - */ -class Trashed extends Location -{ - /** - * Trashed timestamp. - * - * @var mixed Trashed timestamp. - */ - public $trashed; - - /** @var array<int, int> Location ID to a Content ID map of removed items */ - public $removedLocationContentIdMap = []; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Location/UpdateStruct.php b/eZ/Publish/SPI/Persistence/Content/Location/UpdateStruct.php deleted file mode 100644 index cc31cb121c..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Location/UpdateStruct.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Location; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class UpdateStruct extends ValueObject -{ - /** - * Location priority. - * - * Position of the Location among its siblings when sorted using priority - * sort order. - * - * @var int - */ - public $priority = 0; - - /** - * Remote ID. - * - * A universally unique identifier. - * - * @var mixed - */ - public $remoteId; - - /** - * Specifies which property the child locations should be sorted on. - * - * Valid values are found at {@link Location::SORT_FIELD_*} - * - * @var mixed - */ - public $sortField; - - /** - * Specifies whether the sort order should be ascending or descending. - * - * Valid values are {@link Location::SORT_ORDER_*} - * - * @var mixed - */ - public $sortOrder; -} diff --git a/eZ/Publish/SPI/Persistence/Content/LocationWithContentInfo.php b/eZ/Publish/SPI/Persistence/Content/LocationWithContentInfo.php deleted file mode 100644 index 2e49339caa..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/LocationWithContentInfo.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * SPI\Persistence Location with Content Info Value Object. - * A composite of Location and ContentInfo instances. - * - * @property-read \eZ\Publish\SPI\Persistence\Content\Location $location - * @property-read \eZ\Publish\SPI\Persistence\Content\ContentInfo $contentInfo - */ -class LocationWithContentInfo extends ValueObject -{ - /** @var \eZ\Publish\SPI\Persistence\Content\Location */ - protected $location; - - /** @var \eZ\Publish\SPI\Persistence\Content\ContentInfo */ - protected $contentInfo; - - /** - * @internal for internal use by Repository Storage abstraction - */ - public function __construct(Location $location, ContentInfo $contentInfo) - { - parent::__construct([]); - $this->location = $location; - $this->contentInfo = $contentInfo; - } - - public function getLocation(): Location - { - return $this->location; - } - - public function getContentInfo(): ContentInfo - { - return $this->contentInfo; - } -} diff --git a/eZ/Publish/SPI/Persistence/Content/ObjectState.php b/eZ/Publish/SPI/Persistence/Content/ObjectState.php deleted file mode 100644 index 6a8d4a74b5..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/ObjectState.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * This class represents a persistent object state. - */ -class ObjectState extends ValueObject -{ - /** - * The id of the object state. - * - * @var mixed - */ - public $id; - - /** - * The identifier for the object state group. - * - * @var string - */ - public $identifier; - - /** - * The id of the group this object state belongs to. - * - * @var mixed - */ - public $groupId; - - /** - * The priority of the object state in the group. - * - * @var int - */ - public $priority; - - /** - * The default language code for. - * - * @var string - */ - public $defaultLanguage; - - /** - * The available language codes for names an descriptions. - * - * @var string[] - */ - public $languageCodes; - - /** - * Human readable name of the object state. - * - * The structure of this field is: - * <code> - * array( 'eng-US' => '<name_eng>', 'ger-DE' => '<name_de>' ); - * </code> - * - * @var string[] - */ - public $name; - - /** - * Human readable description of the object state. - * - * The structure of this field is: - * <code> - * array( 'eng-US' => '<description_eng>', 'ger-DE' => '<description_de>' ); - * </code> - * - * @var string[] - */ - public $description; -} diff --git a/eZ/Publish/SPI/Persistence/Content/ObjectState/Group.php b/eZ/Publish/SPI/Persistence/Content/ObjectState/Group.php deleted file mode 100644 index 2356fe790a..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/ObjectState/Group.php +++ /dev/null @@ -1,67 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\ObjectState; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * This class represents a persistent object state group. - */ -class Group extends ValueObject -{ - /** - * The id of the object state group. - * - * @var mixed - */ - public $id; - - /** - * The identifier for the object state group. - * - * @var string - */ - public $identifier; - - /** - * The default language code for. - * - * @var string - */ - public $defaultLanguage; - - /** - * The available language codes for names an descriptions. - * - * @var string[] - */ - public $languageCodes; - - /** - * Human readable name of the object state group. - * - * The structure of this field is: - * <code> - * array( 'eng-US' => '<name_eng>', 'ger-DE' => '<name_de>' ); - * </code> - * - * @var string[] - */ - public $name; - - /** - * Human readable description of the object state group. - * - * The structure of this field is: - * <code> - * array( 'eng-US' => '<description_eng>', 'ger-DE' => '<description_de>' ); - * </code> - * - * @var string[] - */ - public $description; -} diff --git a/eZ/Publish/SPI/Persistence/Content/ObjectState/Handler.php b/eZ/Publish/SPI/Persistence/Content/ObjectState/Handler.php deleted file mode 100644 index 8bbbee72f7..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/ObjectState/Handler.php +++ /dev/null @@ -1,174 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\ObjectState; - -/** - * The Object State Handler interface provides managing of object states and groups. - */ -interface Handler -{ - /** - * Creates a new object state group. - * - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct $input - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group - */ - public function createGroup(InputStruct $input); - - /** - * Loads a object state group. - * - * @param mixed $groupId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the group was not found - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group - */ - public function loadGroup($groupId); - - /** - * Loads a object state group by identifier. - * - * @param string $identifier - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the group was not found - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group - */ - public function loadGroupByIdentifier($identifier); - - /** - * Loads all object state groups. - * - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group[] - */ - public function loadAllGroups($offset = 0, $limit = -1); - - /** - * This method returns the ordered list of object states of a group. - * - * @param mixed $groupId - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState[] - */ - public function loadObjectStates($groupId); - - /** - * Updates an object state group. - * - * @param mixed $groupId - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct $input - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Group - */ - public function updateGroup($groupId, InputStruct $input); - - /** - * Deletes a object state group including all states and links to content. - * - * @param mixed $groupId - */ - public function deleteGroup($groupId); - - /** - * Creates a new object state in the given group. - * The new state gets the last priority. - * Note: in current kernel: If it is the first state all content objects will - * set to this state. - * - * @param mixed $groupId - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct $input - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - public function create($groupId, InputStruct $input); - - /** - * Loads an object state. - * - * @param mixed $stateId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the state was not found - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - public function load($stateId); - - /** - * Loads an object state by identifier and group it belongs to. - * - * @param string $identifier - * @param mixed $groupId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the state was not found - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - public function loadByIdentifier($identifier, $groupId); - - /** - * Updates an object state. - * - * @param mixed $stateId - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState\InputStruct $input - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - public function update($stateId, InputStruct $input); - - /** - * Changes the priority of the state. - * - * @param mixed $stateId - * @param int $priority - */ - public function setPriority($stateId, $priority); - - /** - * Deletes a object state. The state of the content objects is reset to the - * first object state in the group. - * - * @param mixed $stateId - */ - public function delete($stateId); - - /** - * Sets the object-state of a state group to $stateId for the given content. - * - * @param mixed $contentId - * @param mixed $groupId - * @param mixed $stateId - * - * @return bool - */ - public function setContentState($contentId, $groupId, $stateId); - - /** - * Gets the object-state of object identified by $contentId. - * - * The $state is the id of the state within one group. - * - * @param mixed $contentId - * @param mixed $stateGroupId - * - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState - */ - public function getContentState($contentId, $stateGroupId); - - /** - * Returns the number of objects which are in this state. - * - * @param mixed $stateId - * - * @return int - */ - public function getContentCount($stateId); -} diff --git a/eZ/Publish/SPI/Persistence/Content/Relation.php b/eZ/Publish/SPI/Persistence/Content/Relation.php deleted file mode 100644 index 593f1ff389..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Relation.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * Class representing a relation between content. - */ -class Relation extends ValueObject -{ - /** - * Id of the relation. - * - * @var mixed - */ - public $id; - - /** - * Source Content ID. - * - * @var mixed - */ - public $sourceContentId; - - /** - * Source Content Version. - * - * @var int - */ - public $sourceContentVersionNo; - - /** - * Source Content Type Field Definition Id. - * - * @var mixed - */ - public $sourceFieldDefinitionId; - - /** - * Destination Content ID. - * - * @var mixed - */ - public $destinationContentId; - - /** - * Type bitmask. - * - * @see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, - * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, - * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, - * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD - * \eZ\Publish\API\Repository\Values\Content\Relation::ASSET - * - * @var int - */ - public $type; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Relation/CreateStruct.php b/eZ/Publish/SPI/Persistence/Content/Relation/CreateStruct.php deleted file mode 100644 index 467bb26314..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Relation/CreateStruct.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Relation; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * CreateStruct representing a relation between content. - */ -class CreateStruct extends ValueObject -{ - /** - * Source Content ID. - * - * @var mixed - */ - public $sourceContentId; - - /** - * Source Content Version number. - * - * @var int - */ - public $sourceContentVersionNo; - - /** - * Source Content Type Field Definition Id. - * - * @var mixed - */ - public $sourceFieldDefinitionId; - - /** - * Destination Content ID. - * - * @var mixed - */ - public $destinationContentId; - - /** - * Type bitmask. - * - * @see \eZ\Publish\API\Repository\Values\Content\Relation::COMMON, - * \eZ\Publish\API\Repository\Values\Content\Relation::EMBED, - * \eZ\Publish\API\Repository\Values\Content\Relation::LINK, - * \eZ\Publish\API\Repository\Values\Content\Relation::FIELD - * - * @var int - */ - public $type; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Section.php b/eZ/Publish/SPI/Persistence/Content/Section.php deleted file mode 100644 index eeb4a07c12..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Section.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class Section extends ValueObject -{ - /** - * Id of the section. - * - * @var int - */ - public $id; - - /** - * Unique identifier of the section. - * - * @var string - */ - public $identifier; - - /** - * Name of the section. - * - * @var string - */ - public $name; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Section/Handler.php b/eZ/Publish/SPI/Persistence/Content/Section/Handler.php deleted file mode 100644 index cc5dacb6e9..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Section/Handler.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Section; - -interface Handler -{ - /** - * Create a new section. - * - * @param string $name - * @param string $identifier - * - * @return \eZ\Publish\SPI\Persistence\Content\Section - * - * @todo Should validate that $identifier is unique?? - * @todo What about translatable $name? - */ - public function create($name, $identifier); - - /** - * Update name and identifier of a section. - * - * @param mixed $id - * @param string $name - * @param string $identifier - * - * @return \eZ\Publish\SPI\Persistence\Content\Section - */ - public function update($id, $name, $identifier); - - /** - * Get section data. - * - * @param mixed $id - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If section is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Section - */ - public function load($id); - - /** - * Get all section data. - * - * @return \eZ\Publish\SPI\Persistence\Content\Section[] - */ - public function loadAll(); - - /** - * Get section data by identifier. - * - * @param string $identifier - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If section is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Section - */ - public function loadByIdentifier($identifier); - - /** - * Delete a section. - * - * Might throw an exception if the section is still associated with some - * content objects. Make sure that no content objects are associated with - * the section any more *before* calling this method. - * - * @param mixed $id - */ - public function delete($id); - - /** - * Assigns section to single content object. - * - * @param mixed $sectionId - * @param mixed $contentId - */ - public function assign($sectionId, $contentId); - - /** - * Number of content assignments a Section has. - * - * @param mixed $sectionId - * - * @return int - */ - public function assignmentsCount($sectionId); - - /** - * Number of role policies using a Section in limitations. - * - * @param mixed $sectionId - * - * @return int - */ - public function policiesCount($sectionId); - - /** - * Counts the number of role assignments using section with $sectionId in their limitations. - * - * @param int $sectionId - * - * @return int - */ - public function countRoleAssignmentsUsingSection($sectionId); -} diff --git a/eZ/Publish/SPI/Persistence/Content/Type.php b/eZ/Publish/SPI/Persistence/Content/Type.php deleted file mode 100644 index 6646e30209..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Type.php +++ /dev/null @@ -1,190 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * SPI Persistence Content\Type value object. - */ -class Type extends ValueObject -{ - /** @var int Status constant for defined (aka "published") Type */ - public const STATUS_DEFINED = 0; - - /** @var int Status constant for draft (aka "temporary") Type */ - public const STATUS_DRAFT = 1; - - /** @var int Status constant for modified (aka "deferred for publishing") Type */ - public const STATUS_MODIFIED = 2; - - /** - * Primary key: Content type ID. - * - * @var mixed - */ - public $id; - - /** - * Primary key: Status (legacy: "version"). - * - * @var int One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - */ - public $status = self::STATUS_DRAFT; - - /** - * Human readable name of the content type. - * - * The structure of this field is: - * <code> - * array( 'eng' => '<name_eng>', 'de' => '<name_de>' ); - * </code> - * - * @var string[] - */ - public $name; - - /** - * Human readable description of the content type. - * - * The structure of this field is: - * <code> - * array( 'eng' => '<description_eng>', 'de' => '<description_de>' ); - * </code> - * - * @var string[] - */ - public $description = []; - - /** - * String identifier of a type. - * - * @var string - */ - public $identifier; - - /** - * Creation date (timestamp). - * - * @var int - */ - public $created; - - /** - * Modification date (timestamp). - * - * @var int - */ - public $modified; - - /** - * Creator user id. - * - * @var mixed - */ - public $creatorId; - - /** - * Modifier user id. - * - * @var mixed - */ - public $modifierId; - - /** - * Unique remote ID. - * - * @var string - */ - public $remoteId; - - /** - * URL alias schema - * Same as {@link \eZ\Publish\SPI\Persistence\Content\Type::$nameSchema}. - * If nothing is provided, $nameSchema will be used instead. - * - * @var string - * - * @see \eZ\Publish\SPI\Persistence\Content\Type::$nameSchema - */ - public $urlAliasSchema; - - /** - * Name schema. - * Can be composed of FieldDefinition identifier place holders. - * These place holders must comply this pattern : <field_definition_identifier>. - * An OR condition can be used : - * <field_def|other_field_def> - * In this example, field_def will be used if available. If not, other_field_def will be used for content name generation. - * - * @var string - */ - public $nameSchema; - - /** - * Determines if the type is a container. - * - * @var bool - */ - public $isContainer; - - /** - * Initial language. - * - * @var mixed - */ - public $initialLanguageId; - - /** - * Specifies which property the child locations should be sorted on by default when created. - * - * Valid values are found at {@link Location::SORT_FIELD_*} - * - * @var mixed - */ - public $sortField = Location::SORT_FIELD_PUBLISHED; - - /** - * Specifies whether the sort order should be ascending or descending by default when created. - * - * Valid values are {@link Location::SORT_ORDER_*} - * - * @var mixed - */ - public $sortOrder = Location::SORT_ORDER_DESC; - - /** - * Contains an array of type group IDs. - * - * @var mixed[] - */ - public $groupIds = []; - - /** - * Definitions for Content fields in this type. - * - * @var \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition[] - */ - public $fieldDefinitions = []; - - /** - * Defines if content objects should have always available enabled or not by default. - * - * Always available (when enabled) means main language is always available, and works as a editorial fallback - * language on load operations when translation filter is provided but no match is found. - * - * @var bool - */ - public $defaultAlwaysAvailable = false; - - /** - * Translations language codes. - * - * @var array - */ - public $languageCodes = []; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Type/CreateStruct.php b/eZ/Publish/SPI/Persistence/Content/Type/CreateStruct.php deleted file mode 100644 index 8ac5415974..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Type/CreateStruct.php +++ /dev/null @@ -1,163 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Type; - -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\ValueObject; - -class CreateStruct extends ValueObject -{ - /** - * Human readable name of the content type. - * - * The structure of this field is: - * <code> - * array( 'eng' => '<name_eng>', 'de' => '<name_de>' ); - * </code> - * - * @var string[] - */ - public $name; - - /** - * Version (state) to create. - * - * @var int - */ - public $status; - - /** - * Human readable description of the content type. - * - * The structure of this field is: - * <code> - * array( 'eng' => '<description_eng>', 'de' => '<description_de>' ); - * </code> - * - * @var string[] - */ - public $description = []; - - /** - * String identifier of a type. - * - * @var string - */ - public $identifier; - - /** - * Creation date (timestamp). - * - * @var int - */ - public $created; - - /** - * Modification date (timestamp). - * - * @var int - */ - public $modified; - - /** - * Creator user id. - * - * @var mixed - */ - public $creatorId; - - /** - * Modifier user id. - * - * @var mixed - */ - public $modifierId; - - /** - * Unique remote ID. - * - * @var string - */ - public $remoteId; - - /** - * URL alias schema. - * - * @var string - */ - public $urlAliasSchema; - - /** - * Name schema. - * - * @var string - */ - public $nameSchema; - - /** - * Determines if the type is a container. - * - * @var bool - */ - public $isContainer; - - /** - * Initial language. - * - * @var mixed - */ - public $initialLanguageId; - - /** - * Specifies which property the child locations should be sorted on by default when created. - * - * Valid values are found at {@link Location::SORT_FIELD_*} - * - * @var mixed - */ - public $sortField = Location::SORT_FIELD_PUBLISHED; - - /** - * Specifies whether the sort order should be ascending or descending by default when created. - * - * Valid values are {@link Location::SORT_ORDER_*} - * - * @var mixed - */ - public $sortOrder = Location::SORT_ORDER_DESC; - - /** - * Contains an array of type group IDs. - * - * @var mixed[] - */ - public $groupIds = []; - - /** - * Content fields in this type. - * - * @var \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition[] - */ - public $fieldDefinitions = []; - - /** - * @todo: Document. - * - * @var bool - */ - public $defaultAlwaysAvailable = false; - - /** - * Performs a deep cloning. - */ - public function __clone() - { - foreach ($this->fieldDefinitions as $id => $fieldDef) { - $this->fieldDefinitions[$id] = clone $fieldDef; - } - } -} diff --git a/eZ/Publish/SPI/Persistence/Content/Type/DeleteByParamsStruct.php b/eZ/Publish/SPI/Persistence/Content/Type/DeleteByParamsStruct.php deleted file mode 100644 index 83cbdbb085..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Type/DeleteByParamsStruct.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Type; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class DeleteByParamsStruct extends ValueObject -{ - /** - * @var int - */ - public $modifierId; - - /** - * @var int - */ - public $status; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Type/FieldDefinition.php b/eZ/Publish/SPI/Persistence/Content/Type/FieldDefinition.php deleted file mode 100644 index 58c489cddf..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Type/FieldDefinition.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Type; - -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * @todo Do we need a FieldDefinitionCreateStruct? - * @todo What about the "serialized_data_text" field in legacy storage? - */ -class FieldDefinition extends ValueObject -{ - /** - * Primary key. - * - * @var mixed - */ - public $id; - - /** - * Name. - * - * @var string[] - */ - public $name; - - /** - * Description. - * - * @var string[] - */ - public $description = []; - - /** - * Readable string identifier of a field definition. - * - * @var string - */ - public $identifier; - - /** - * Field group name. - * - * @var string - */ - public $fieldGroup; - - /** - * Position. - * - * @var int - */ - public $position; - - /** - * String identifier of the field type. - * - * @var string - */ - public $fieldType; - - /** - * If the field type is translatable. - * - * @var bool - */ - public $isTranslatable; - - /** - * Is the field required. - * - * @var bool - */ - public $isRequired; - - /** - * If the field type can be a thumbnail. - * - * @var bool - */ - public $isThumbnail; - - /** - * Just a flag. - * - * @var bool - */ - public $isInfoCollector; - - /** - * A map of field type constraints. - * 2 constraints are available (as keys): - * - validators - * - fieldSettings. - * - * @var \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public $fieldTypeConstraints; - - /** - * Default value of the field. - * - * @var \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public $defaultValue; - - /** - * @todo: Document - * - * @var bool - */ - public $isSearchable; - - /** - * Based on mainLanguageCode of contentType. - * - * @var string - */ - public $mainLanguageCode; - - /** - * Constructor. - */ - public function __construct(array $properties = []) - { - $this->fieldTypeConstraints = new FieldTypeConstraints(); - $this->defaultValue = new FieldValue(); - parent::__construct($properties); - } -} diff --git a/eZ/Publish/SPI/Persistence/Content/Type/Group.php b/eZ/Publish/SPI/Persistence/Content/Type/Group.php deleted file mode 100644 index c6eff69218..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Type/Group.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Type; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class Group extends ValueObject -{ - /** - * Primary key. - * - * @var mixed - */ - public $id; - - /** - * Name. - * - * @since 5.0 - * - * @var string[] - */ - public $name = []; - - /** - * Description. - * - * @since 5.0 - * - * @var string[] - */ - public $description = []; - - /** - * Readable string identifier of a group. - * - * Legacy note: Maps to existing name property - * - * @var string - */ - public $identifier; - - /** - * Created date (timestamp). - * - * @var int - */ - public $created; - - /** - * Modified date (timestamp). - * - * @var int - */ - public $modified; - - /** - * Creator user id. - * - * @var mixed - */ - public $creatorId; - - /** - * Modifier user id. - * - * @var mixed - */ - public $modifierId; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Type/Group/CreateStruct.php b/eZ/Publish/SPI/Persistence/Content/Type/Group/CreateStruct.php deleted file mode 100644 index 20bb675f54..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Type/Group/CreateStruct.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Type\Group; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class CreateStruct extends ValueObject -{ - /** - * Name. - * - * @since 5.0 - * - * @var string[] - */ - public $name = []; - - /** - * Description. - * - * @since 5.0 - * - * @var string[] - */ - public $description = []; - - /** - * Readable string identifier of a group. - * - * @var string - */ - public $identifier; - - /** - * Created date (timestamp). - * - * @var int - */ - public $created; - - /** - * Modified date (timestamp). - * - * @var int - */ - public $modified; - - /** - * Creator user id. - * - * @var mixed - */ - public $creatorId; - - /** - * Modifier user id. - * - * @var mixed - */ - public $modifierId; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Type/Group/UpdateStruct.php b/eZ/Publish/SPI/Persistence/Content/Type/Group/UpdateStruct.php deleted file mode 100644 index d91807f9c5..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Type/Group/UpdateStruct.php +++ /dev/null @@ -1,58 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Type\Group; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class UpdateStruct extends ValueObject -{ - /** - * Primary key. - * - * @var mixed - */ - public $id; - - /** - * Name. - * - * @since 5.0 - * - * @var string[] - */ - public $name = []; - - /** - * Description. - * - * @since 5.0 - * - * @var string[] - */ - public $description = []; - - /** - * Readable string identifier of a group. - * - * @var string - */ - public $identifier; - - /** - * Modified date (timestamp). - * - * @var int - */ - public $modified; - - /** - * Modifier user id. - * - * @var mixed - */ - public $modifierId; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Type/Handler.php b/eZ/Publish/SPI/Persistence/Content/Type/Handler.php deleted file mode 100644 index 80ea1ed576..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Type/Handler.php +++ /dev/null @@ -1,337 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Type; - -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct as GroupCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct as GroupUpdateStruct; - -interface Handler -{ - /** - * @param \eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct $group - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group - */ - public function createGroup(GroupCreateStruct $group); - - /** - * @param \eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct $group - */ - public function updateGroup(GroupUpdateStruct $group); - - /** - * @param mixed $groupId - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If type group contains types - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If type group with id is not found - */ - public function deleteGroup($groupId); - - /** - * @param mixed $groupId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If type group with id is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group - */ - public function loadGroup($groupId); - - /** - * Return list of unique Content Type Groups, with group id as key. - * - * Missing items (NotFound) will be missing from the array and not cause an exception, it's up - * to calling logic to determine if this should cause exception or not. - * - * @param array $groupIds - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group[] - */ - public function loadGroups(array $groupIds); - - /** - * Loads Type Group by identifier. - * - * Legacy note: Uses name for identifier. - * - * @param string $identifier - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If type group with id is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group - */ - public function loadGroupByIdentifier($identifier); - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Type\Group[] - */ - public function loadAllGroups(); - - /** - * @param mixed $groupId - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * - * @return \eZ\Publish\SPI\Persistence\Content\Type[] - */ - public function loadContentTypes($groupId, $status = Type::STATUS_DEFINED); - - /** - * Return list of unique Content Types, with type id as key. - * - * Missing items (NotFound) will be missing from the array and not cause an exception, it's up - * to calling logic to determine if this should cause exception or not. - * - * @param array $contentTypeIds - * - * @return \eZ\Publish\SPI\Persistence\Content\Type[] - */ - public function loadContentTypeList(array $contentTypeIds): array; - - /** - * Loads a content type by id and status. - * - * Note: This method is responsible of having the Field Definitions of the loaded ContentType sorted by placement. - * - * @param mixed $contentTypeId - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If type with provided status is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function load($contentTypeId, $status = Type::STATUS_DEFINED); - - /** - * Loads a (defined) content type by identifier. - * - * Note: This method is responsible of having the Field Definitions of the loaded ContentType sorted by placement. - * - * @param string $identifier - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If defined type is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function loadByIdentifier($identifier); - - /** - * Loads a (defined) content type by remote id. - * - * Note: This method is responsible of having the Field Definitions of the loaded ContentType sorted by placement. - * - * @param mixed $remoteId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If defined type is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function loadByRemoteId($remoteId); - - /** - * @param \eZ\Publish\SPI\Persistence\Content\Type\CreateStruct $contentType - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function create(CreateStruct $contentType); - - /** - * @param mixed $contentTypeId - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * @param \eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct $contentType - */ - public function update($contentTypeId, $status, UpdateStruct $contentType); - - /** - * @param mixed $contentTypeId - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If type is defined and still has content - */ - public function delete($contentTypeId, $status); - - /** - * Creates a draft of existing defined content type. - * - * Updates modified date, sets $modifierId and status to Type::STATUS_DRAFT on the new returned draft. - * - * @param mixed $modifierId - * @param mixed $contentTypeId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If type with defined status is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function createDraft($modifierId, $contentTypeId); - - /** - * Copy a Type incl fields and group-relations from a given status to a new Type with status {@link Type::STATUS_DRAFT}. - * - * New Content Type will have $userId as creator / modifier, created / modified should be updated, new remoteId created - * and identifier should be 'copy_of_<originalBaseIdentifier>_<newTypeId>' or another unique string. - * - * @param mixed $userId - * @param mixed $contentTypeId - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user or type with provided status is not found - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function copy($userId, $contentTypeId, $status); - - /** - * Unlink a content type group from a content type. - * - * @param mixed $groupId - * @param mixed $contentTypeId - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If group or type with provided status is not found - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If $groupId is last group on $contentTypeId or - * not a group assigned to type - */ - public function unlink($groupId, $contentTypeId, $status); - - /** - * Link a content type group with a content type. - * - * @param mixed $groupId - * @param mixed $contentTypeId - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If group or type with provided status is not found - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If type is already part of group - */ - public function link($groupId, $contentTypeId, $status); - - /** - * Returns field definition for the given field definition id. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If field definition is not found - * - * @param mixed $id - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition - */ - public function getFieldDefinition($id, $status); - - /** - * Counts the number of Content instances of the ContentType identified by given $contentTypeId. - * - * @param mixed $contentTypeId - * - * @return int - */ - public function getContentCount($contentTypeId); - - /** - * Adds a new field definition to an existing Type. - * - * This method creates a new version of the Type with the $fieldDefinition - * added. It does not update existing content objects depending on the - * field (default) values. - * - * @param mixed $contentTypeId - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @return \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If type is not found - * - * @todo Add FieldDefinition\CreateStruct? - */ - public function addFieldDefinition($contentTypeId, $status, FieldDefinition $fieldDefinition); - - /** - * Removes a field definition from an existing Type. - * - * This method creates a new version of the Type with the field definition - * referred to by $fieldDefinitionId removed. It does not update existing - * content objects depending on the field (default) values. - * - * @param mixed $contentTypeId - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * @param mixed $fieldDefinitionId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If field is not found - */ - public function removeFieldDefinition($contentTypeId, $status, $fieldDefinitionId); - - /** - * This method updates the given $fieldDefinition on a Type. - * - * This method creates a new version of the Type with the updated - * $fieldDefinition. It does not update existing content objects depending - * on the - * field (default) values. - * - * @param mixed $contentTypeId - * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If field is not found - * - * @todo Add FieldDefinition\UpdateStruct? - */ - public function updateFieldDefinition($contentTypeId, $status, FieldDefinition $fieldDefinition); - - /** - * Update content objects. - * - * Updates content objects, depending on the changed field definitions. - * - * A content type has a state which tells if its content objects yet have - * been adapted. - * - * Flags the content type as updated. - * - * @param mixed $contentTypeId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If type with $contentTypeId and Type::STATUS_DRAFT is not found - */ - public function publish($contentTypeId); - - /** - * Returns content type, field definition and field type mapping information - * for search engine usage. Only searchable field definitions will be included - * in the returned data. - * - * Returns an array in the form: - * - * <code> - * array( - * "<ContentType identifier>" => array( - * "<FieldDefinition identifier>" => array( - * "field_definition_id" => "<FieldDefinition id>", - * "field_type_identifier" => "<FieldType identifier>", - * ), - * ... - * ), - * ... - * ) - * </code> - * - * @return array - */ - public function getSearchableFieldMap(); - - /** - * @param int $contentTypeId - * @param string $languageCode - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - public function removeContentTypeTranslation(int $contentTypeId, string $languageCode): Type; - - /** - * @param int $userId - * @param int $status - */ - public function deleteByUserAndStatus(int $userId, int $status): void; -} diff --git a/eZ/Publish/SPI/Persistence/Content/Type/UpdateStruct.php b/eZ/Publish/SPI/Persistence/Content/Type/UpdateStruct.php deleted file mode 100644 index 05c346f65a..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/Type/UpdateStruct.php +++ /dev/null @@ -1,117 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\Type; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class UpdateStruct extends ValueObject -{ - /** - * Human readable name of the content type. - * - * The structure of this field is: - * <code> - * array( 'eng' => '<name_eng>', 'de' => '<name_de>' ); - * </code> - * - * @var string[] - */ - public $name; - - /** - * Human readable description of the content type. - * - * The structure of this field is: - * <code> - * array( 'eng' => '<description_eng>', 'de' => '<description_de>' ); - * </code> - * - * @var string[] - */ - public $description = []; - - /** - * String identifier of a type. - * - * @var string - */ - public $identifier; - - /** - * Modification date (timestamp). - * - * @var int - */ - public $modified; - - /** - * Modifier user id. - * - * @var mixed - */ - public $modifierId; - - /** - * Unique remote ID. - * - * @var string - */ - public $remoteId; - - /** - * URL alias schema. - * - * @var string - */ - public $urlAliasSchema; - - /** - * Name schema. - * - * @var string - */ - public $nameSchema; - - /** - * Determines if the type is a container. - * - * @var bool - */ - public $isContainer; - - /** - * Initial language. - * - * @var mixed - */ - public $initialLanguageId; - - /** - * Specifies which property the child locations should be sorted on by default when created. - * - * Valid values are found at {@link Location::SORT_FIELD_*} - * - * @var mixed - */ - public $sortField; - - /** - * Specifies whether the sort order should be ascending or descending by default when created. - * - * Valid values are {@link Location::SORT_ORDER_*} - * - * @var mixed - */ - public $sortOrder; - - /** - * @todo: Document. - * - * @var bool - */ - public $defaultAlwaysAvailable; -} diff --git a/eZ/Publish/SPI/Persistence/Content/UpdateStruct.php b/eZ/Publish/SPI/Persistence/Content/UpdateStruct.php deleted file mode 100644 index cde771a6e3..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/UpdateStruct.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class UpdateStruct extends ValueObject -{ - /** @var string[] Eg. array( 'eng-GB' => "New Article" ) */ - public $name = []; - - /** - * Creator user ID for the version. - * - * @var int - */ - public $creatorId; - - /** - * Contains fields to be updated. - * - * @var \eZ\Publish\SPI\Persistence\Content\Field[] - */ - public $fields = []; - - /** - * Modification date for the version. - * Unix timestamp. - * - * @var int - */ - public $modificationDate; - - /** - * ID for initial (main) language for this version. - * - * @var mixed - */ - public $initialLanguageId = false; -} diff --git a/eZ/Publish/SPI/Persistence/Content/UrlAlias.php b/eZ/Publish/SPI/Persistence/Content/UrlAlias.php deleted file mode 100644 index 46b7432972..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/UrlAlias.php +++ /dev/null @@ -1,105 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * UrlAlias models one url alias path element separated by '/' in urls. - * - * This class models the legacy structure used for url aliases. - */ -class UrlAlias extends ValueObject -{ - public const LOCATION = 0; - public const RESOURCE = 1; - public const VIRTUAL = 2; - - /** - * A unique identifier for the alias - * (in legacy implementation this would be <parentid>-<md5text>). - * - * @var string - */ - public $id; - - /** - * The type of the URL Alias i.e. one of URLAlias::LOCATION, URLAlias::RESOURCE, URLAlias::VIRTUAL. - * - * @var int - */ - public $type; - - /** - * If type = URLAlias::LOCATION the locationId - * otherwise a string (e.g. /content/search). - * - * @var mixed - */ - public $destination; - - /** - * Holds normalized path data. - * - * Example: - * <code> - * array( - * array( - * "always-available" => true, - * "translations" => array( - * "cro-HR" => "jedan" - * ) - * ), - * array( - * "always-available" => false, - * "translations" => array( - * "cro-HR" => "dva", - * "eng-GB" => "two", - * ) - * ) - * ) - * </code> - * - * @var array - */ - public $pathData; - - /** - * Language code of url alias entry. - * - * @var string[] - */ - public $languageCodes; - - /** - * Fallback indicator for other languages. - * - * @var bool - */ - public $alwaysAvailable; - - /** - * Indicates that this alias was autogenerated for an in the meanwhile archived version of the content. - * - * @var bool - */ - public $isHistory; - - /** - * If false this alias was autogenerated otherwise manuel created. - * - * @var bool - */ - public $isCustom; - - /** - * Indicates if the url should be redirected. - * - * @var bool - */ - public $forward; -} diff --git a/eZ/Publish/SPI/Persistence/Content/UrlAlias/Handler.php b/eZ/Publish/SPI/Persistence/Content/UrlAlias/Handler.php deleted file mode 100644 index aa0492b9fe..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/UrlAlias/Handler.php +++ /dev/null @@ -1,210 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\UrlAlias; - -/** - * The UrlAlias Handler interface provides nice urls management. - * - * Its methods operate on a representation of the url alias data structure held - * inside a storage engine. - */ -interface Handler -{ - /** - * This method creates or updates an urlalias from a new or changed content name in a language - * (if published). It also can be used to create an alias for a new location of content. - * On update the old alias is linked to the new one (i.e. a history alias is generated). - * - * $alwaysAvailable controls whether the url alias is accessible in all - * languages. - * - * @param mixed $locationId - * @param mixed $parentLocationId - * @param string $name the new name computed by the name schema or url alias schema - * @param string $languageCode - * @param bool $alwaysAvailable - */ - public function publishUrlAliasForLocation( - $locationId, - $parentLocationId, - $name, - $languageCode, - $alwaysAvailable = false - ): string; - - /** - * Create a user chosen $alias pointing to $locationId in $languageCode. - * - * If $languageCode is null the $alias is created in the system's default - * language. $alwaysAvailable makes the alias available in all languages. - * - * @param mixed $locationId - * @param string $path - * @param bool $forwarding - * @param string|null $languageCode - * @param bool $alwaysAvailable - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias - */ - public function createCustomUrlAlias($locationId, $path, $forwarding = false, $languageCode = null, $alwaysAvailable = false); - - /** - * Create a user chosen $alias pointing to a resource in $languageCode. - * This method does not handle location resources - if a user enters a location target - * the createCustomUrlAlias method has to be used. - * - * If $languageCode is null the $alias is created in the system's default - * language. $alwaysAvailable makes the alias available in all languages. - * - * @param string $resource - * @param string $path - * @param bool $forwarding - * @param string|null $languageCode - * @param bool $alwaysAvailable - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias - */ - public function createGlobalUrlAlias($resource, $path, $forwarding = false, $languageCode = null, $alwaysAvailable = false); - - /** - * List global aliases. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if path for any of the global URL aliases is broken - * - * @param string|null $languageCode - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias[] - */ - public function listGlobalURLAliases($languageCode = null, $offset = 0, $limit = -1); - - /** - * List of url entries of $urlType, pointing to $locationId. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if any path for the Location is broken - * - * @param mixed $locationId - * @param bool $custom if true the user generated aliases are listed otherwise the autogenerated - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias[] - */ - public function listURLAliasesForLocation($locationId, $custom = false); - - /** - * Removes url aliases. - * - * Autogenerated aliases are not removed by this method. - * - * @param \eZ\Publish\SPI\Persistence\Content\UrlAlias[] $urlAliases - * - * @return bool - */ - public function removeURLAliases(array $urlAliases); - - /** - * Looks up a url alias for the given url. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if the stored path for the given URL is broken - * - * @param string $url - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias - */ - public function lookup($url); - - /** - * Loads URL alias by given $id. - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if path for the given URL alias is broken - * - * @param string $id unique identifier in the form of "<parentId>-<text_md5>" - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias - */ - public function loadUrlAlias($id); - - /** - * Notifies the underlying engine that a location has moved. - * - * This method triggers the change of the autogenerated aliases - * - * @param mixed $locationId - * @param mixed $oldParentId - * @param mixed $newParentId - */ - public function locationMoved($locationId, $oldParentId, $newParentId); - - /** - * Notifies the underlying engine that a location was copied. - * - * This method triggers the creation of the autogenerated aliases for the copied locations - * - * @param mixed $locationId - * @param mixed $newLocationId - * @param mixed $newParentId - */ - public function locationCopied($locationId, $newLocationId, $newParentId); - - /** - * Notifies the underlying engine that a location was swapped. - * - * This method triggers the change of the autogenerated aliases. - * - * @param string|int $location1Id - * @param string|int $location1ParentId - * @param string|int $location2Id - * @param string|int $location2ParentId - */ - public function locationSwapped($location1Id, $location1ParentId, $location2Id, $location2ParentId); - - /** - * Notifies the underlying engine that a location was deleted or moved to trash. - * - * @param mixed $locationId - * - * @return array - */ - public function locationDeleted($locationId): array; - - /** - * Notifies the underlying engine that Locations Content Translation was removed. - * - * @param int[] $locationIds all Locations of the Content that got Translation removed - * @param string $languageCode language code of the removed Translation - */ - public function translationRemoved(array $locationIds, $languageCode); - - /** - * Archive UrlAliases for Translations that were removed from the underlying published content. - * - * @param int $locationId Location of underlying published Content Object - * @param int $parentLocationId - * @param array $languageCodes Language codes of currently published Content Object Translations - */ - public function archiveUrlAliasesForDeletedTranslations($locationId, $parentLocationId, array $languageCodes); - - /** - * Delete corrupted URL aliases (global, custom and system). - * - * @return int Number of deleted URL aliases - */ - public function deleteCorruptedUrlAliases(); - - /** - * Attempt repairing auto-generated URL aliases for the given Location (including history). - * - * Note: it is assumed that at this point original, working, URL Alias for Location is published. - * - * @param int $locationId - * - * @throws \eZ\Publish\Core\Base\Exceptions\BadStateException - */ - public function repairBrokenUrlAliasesForLocation(int $locationId); -} diff --git a/eZ/Publish/SPI/Persistence/Content/UrlWildcard/Handler.php b/eZ/Publish/SPI/Persistence/Content/UrlWildcard/Handler.php deleted file mode 100644 index 8e24842a60..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/UrlWildcard/Handler.php +++ /dev/null @@ -1,99 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content\UrlWildcard; - -use eZ\Publish\SPI\Persistence\Content\UrlWildcard; -use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\URLWildcardQuery; - -/** - * The UrlWildcard Handler interface provides nice urls with wildcards management. - * - * Its methods operate on a representation of the url alias data structure held - * inside a storage engine. - */ -interface Handler -{ - /** - * Creates a new url wildcard. - * - * @param string $sourceUrl - * @param string $destinationUrl - * @param bool $forward - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard - */ - public function create($sourceUrl, $destinationUrl, $forward = false); - - public function update( - int $id, - string $sourceUrl, - string $destinationUrl, - bool $forward - ): UrlWildcard; - - /** - * removes an url wildcard. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the url wild card was not found - * - * @param mixed $id - */ - public function remove($id); - - /** - * Loads a url wild card. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the url wild card was not found - * - * @param mixed $id - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard - */ - public function load($id); - - /** - * Loads all url wild card (paged). - * - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard[] - */ - public function loadAll($offset = 0, $limit = -1); - - /** - * Find URLWildcards. - * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - */ - public function find(URLWildcardQuery $query): array; - - /** - * Performs lookup for given (source) URL. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the url wild card was not found - * - * @param string $sourceUrl - * - * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard - */ - public function translate(string $sourceUrl): UrlWildcard; - - /** - * Checks whether UrlWildcard with given source url exits. - * - * @param string $sourceUrl - * - * @return bool - */ - public function exactSourceUrlExists(string $sourceUrl): bool; - - /** - * Counts URL Wildcards. - */ - public function countAll(): int; -} diff --git a/eZ/Publish/SPI/Persistence/Content/VersionInfo.php b/eZ/Publish/SPI/Persistence/Content/VersionInfo.php deleted file mode 100644 index 304d07ca1e..0000000000 --- a/eZ/Publish/SPI/Persistence/Content/VersionInfo.php +++ /dev/null @@ -1,107 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\Content; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * This class holds version information data. - */ -class VersionInfo extends ValueObject -{ - /** - * Version status constants. - * - * @var int - */ - public const STATUS_DRAFT = 0; - public const STATUS_PUBLISHED = 1; - public const STATUS_PENDING = 2; - public const STATUS_ARCHIVED = 3; - public const STATUS_REJECTED = 4; - public const STATUS_INTERNAL_DRAFT = 5; - public const STATUS_REPEAT = 6; - public const STATUS_QUEUED = 7; - - /** - * Version ID. - * - * @var mixed - */ - public $id; - - /** - * Version number. - * - * In contrast to {@link $id}, this is the version number, which only - * increments in scope of a single Content object. - * - * @var int - */ - public $versionNo; - - /** - * ContentInfo of the content this VersionInfo belongs to. - * - * @var \eZ\Publish\SPI\Persistence\Content\ContentInfo - */ - public $contentInfo; - - /** - * Returns the names computed from the name schema in the available languages. - * Eg. array( 'eng-GB' => "New Article" ). - * - * @return string[] - */ - public $names; - - /** - * Creation date of this version, as a UNIX timestamp. - * - * @var int - */ - public $creationDate; - - /** - * Last modified date of this version, as a UNIX timestamp. - * - * @var int - */ - public $modificationDate; - - /** - * Creator user ID. - * - * Creator of the version, in the search API this is referred to as the modifier of the published content. - * - * @var int - */ - public $creatorId; - - /** - * One of VersionInfo::STATUS_DRAFT, VersionInfo::STATUS_PUBLISHED, VersionInfo::STATUS_ARCHIVED. - * - * @var int - */ - public $status; - - /** - * In 4.x this is the language code which is used for labeling a translation. - * - * @var string - */ - public $initialLanguageCode; - - /** - * List of languages in this version. - * - * Reflects which languages fields exists in for this version. - * - * @var string[] - */ - public $languageCodes = []; -} diff --git a/eZ/Publish/SPI/Persistence/FieldType.php b/eZ/Publish/SPI/Persistence/FieldType.php deleted file mode 100644 index 1d1e2131fe..0000000000 --- a/eZ/Publish/SPI/Persistence/FieldType.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence; - -/** - * The field type interface which field types available to storage engines have to implement. - * - * @see \eZ\Publish\SPI\FieldType\FieldType - */ -interface FieldType -{ - /** - * Returns the empty value for the field type that can be processed by the storage engine. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getEmptyValue(); -} diff --git a/eZ/Publish/SPI/Persistence/FieldType/IsEmptyValue.php b/eZ/Publish/SPI/Persistence/FieldType/IsEmptyValue.php deleted file mode 100644 index 2fad25b4e3..0000000000 --- a/eZ/Publish/SPI/Persistence/FieldType/IsEmptyValue.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\FieldType; - -use eZ\Publish\SPI\Persistence\Content\FieldValue; - -/** - * The field type interface which field types available to storage engines have to implement. - * - * @see \eZ\Publish\SPI\FieldType\FieldType - * @deprecated since 7.5.6. In 8.0 (for eZ Platform 3.0) it will be merged into the - * `\eZ\Publish\SPI\Persistence\FieldType` interface - */ -interface IsEmptyValue -{ - /** - * Returns the empty value for the field type that can be processed by the storage engine. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function isEmptyValue(FieldValue $fieldValue): bool; -} diff --git a/eZ/Publish/SPI/Persistence/Filter/Content/Handler.php b/eZ/Publish/SPI/Persistence/Filter/Content/Handler.php deleted file mode 100644 index 3acd26f354..0000000000 --- a/eZ/Publish/SPI/Persistence/Filter/Content/Handler.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Filter\Content; - -use eZ\Publish\API\Repository\Values\Filter\Filter; - -/** - * Content Filtering ContentHandler. - * - * @internal for internal use by Repository - */ -interface Handler -{ - /** - * @return \eZ\Publish\SPI\Persistence\Filter\Content\LazyContentItemListIterator - */ - public function find(Filter $filter): iterable; - - public function count(Filter $filter): int; -} diff --git a/eZ/Publish/SPI/Persistence/Filter/Content/LazyContentItemListIterator.php b/eZ/Publish/SPI/Persistence/Filter/Content/LazyContentItemListIterator.php deleted file mode 100644 index f88ab22b55..0000000000 --- a/eZ/Publish/SPI/Persistence/Filter/Content/LazyContentItemListIterator.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Filter\Content; - -use eZ\Publish\SPI\Persistence\Filter\LazyListIterator; - -/** - * SPI Persistence Content Item list iterator. - * - * @internal for internal use by Repository Filtering - * - * @see \eZ\Publish\SPI\Persistence\Content\ContentItem - */ -class LazyContentItemListIterator extends LazyListIterator -{ - /** - * @return \eZ\Publish\SPI\Persistence\Content\ContentItem[] - * - * @throws \Exception - */ - #[\ReturnTypeWillChange] - public function getIterator(): iterable - { - yield from parent::getIterator(); - } -} diff --git a/eZ/Publish/SPI/Persistence/Filter/CriterionVisitor.php b/eZ/Publish/SPI/Persistence/Filter/CriterionVisitor.php deleted file mode 100644 index b628099fdf..0000000000 --- a/eZ/Publish/SPI/Persistence/Filter/CriterionVisitor.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Filter; - -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; - -/** - * @internal for internal use by Repository Filtering. - * Visits instances of {@see \eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder}. - */ -interface CriterionVisitor -{ - public function visitCriteria( - FilteringQueryBuilder $queryBuilder, - FilteringCriterion $criterion - ): string; -} diff --git a/eZ/Publish/SPI/Persistence/Filter/Location/Handler.php b/eZ/Publish/SPI/Persistence/Filter/Location/Handler.php deleted file mode 100644 index 84ef9e397e..0000000000 --- a/eZ/Publish/SPI/Persistence/Filter/Location/Handler.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Filter\Location; - -use eZ\Publish\API\Repository\Values\Filter\Filter; - -/** - * Location Filtering ContentHandler. - * - * @internal for internal use by Repository - */ -interface Handler -{ - /** - * @return \eZ\Publish\SPI\Persistence\Filter\Location\LazyLocationListIterator - */ - public function find(Filter $filter): iterable; - - public function count(Filter $filter): int; -} diff --git a/eZ/Publish/SPI/Persistence/Filter/Location/LazyLocationListIterator.php b/eZ/Publish/SPI/Persistence/Filter/Location/LazyLocationListIterator.php deleted file mode 100644 index b08abcf31c..0000000000 --- a/eZ/Publish/SPI/Persistence/Filter/Location/LazyLocationListIterator.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Filter\Location; - -use eZ\Publish\SPI\Persistence\Filter\LazyListIterator; - -/** - * SPI Persistence Content Item list iterator. - * - * @internal for internal use by Repository Filtering - * - * @see \eZ\Publish\SPI\Persistence\Content\ContentItem - */ -class LazyLocationListIterator extends LazyListIterator -{ - /** - * @return \eZ\Publish\API\Repository\Values\Content\LocationList[] - * - * @throws \Exception - */ - #[\ReturnTypeWillChange] - public function getIterator(): iterable - { - yield from parent::getIterator(); - } -} diff --git a/eZ/Publish/SPI/Persistence/Filter/SortClauseVisitor.php b/eZ/Publish/SPI/Persistence/Filter/SortClauseVisitor.php deleted file mode 100644 index be85e6bbbc..0000000000 --- a/eZ/Publish/SPI/Persistence/Filter/SortClauseVisitor.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Filter; - -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; - -/** - * @internal for internal use by Repository Filtering. - * Visits instances of {@see \eZ\Publish\SPI\Repository\Values\Filter\SortClauseQueryBuilder}. - */ -interface SortClauseVisitor -{ - /** - * @param \eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause[] $sortClauses - */ - public function visitSortClauses(FilteringQueryBuilder $queryBuilder, array $sortClauses): void; -} diff --git a/eZ/Publish/SPI/Persistence/Handler.php b/eZ/Publish/SPI/Persistence/Handler.php deleted file mode 100644 index 3ca1242804..0000000000 --- a/eZ/Publish/SPI/Persistence/Handler.php +++ /dev/null @@ -1,126 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence; - -use eZ\Publish\SPI\Persistence\Setting\Handler as SettingHandler; - -/** - * The main handler for Storage Engine. - */ -interface Handler -{ - /** - * @return \eZ\Publish\SPI\Persistence\Content\Handler - */ - public function contentHandler(); - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Type\Handler - */ - public function contentTypeHandler(); - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Language\Handler - */ - public function contentLanguageHandler(); - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Location\Handler - */ - public function locationHandler(); - - /** - * @return \eZ\Publish\SPI\Persistence\Content\ObjectState\Handler - */ - public function objectStateHandler(); - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Location\Trash\Handler - */ - public function trashHandler(); - - /** - * @return \eZ\Publish\SPI\Persistence\User\Handler - */ - public function userHandler(); - - /** - * @return \eZ\Publish\SPI\Persistence\Content\Section\Handler - */ - public function sectionHandler(); - - /** - * @return \eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler - */ - public function urlAliasHandler(); - - /** - * @return \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler - */ - public function urlWildcardHandler(); - - /** - * @return \eZ\Publish\Core\Persistence\Legacy\URL\Handler - */ - public function urlHandler(); - - /** - * @return \eZ\Publish\SPI\Persistence\Bookmark\Handler - */ - public function bookmarkHandler(); - - /** - * @return \eZ\Publish\SPI\Persistence\Notification\Handler - */ - public function notificationHandler(); - - /** - * @return \eZ\Publish\SPI\Persistence\UserPreference\Handler - */ - public function userPreferenceHandler(); - - /** - * @return \eZ\Publish\SPI\Persistence\TransactionHandler - */ - public function transactionHandler(); - - public function settingHandler(): SettingHandler; - - /** - * Begin transaction. - * - * Begins an transaction, make sure you'll call commit or rollback when done, - * otherwise work will be lost. - * - * @deprecated Since 5.3 {@use transactionHandler()->beginTransaction()} - */ - public function beginTransaction(); - - /** - * Commit transaction. - * - * Commit transaction, or throw exceptions if no transactions has been started. - * - * @throws \RuntimeException If no transaction has been started - * - * @deprecated Since 5.3 {@use transactionHandler()->commit()} - */ - public function commit(); - - /** - * Rollback transaction. - * - * Rollback transaction, or throw exceptions if no transactions has been started. - * - * @throws \RuntimeException If no transaction has been started - * - * @deprecated Since 5.3 {@use transactionHandler()->rollback()} - */ - public function rollback(); -} diff --git a/eZ/Publish/SPI/Persistence/Notification/CreateStruct.php b/eZ/Publish/SPI/Persistence/Notification/CreateStruct.php deleted file mode 100644 index 4b657fac47..0000000000 --- a/eZ/Publish/SPI/Persistence/Notification/CreateStruct.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Notification; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class CreateStruct extends ValueObject -{ - /** @var int */ - public $ownerId; - - /** @var string */ - public $type; - - /** @var bool */ - public $isPending; - - /** @var array */ - public $data = []; - - /** @var int */ - public $created; -} diff --git a/eZ/Publish/SPI/Persistence/Notification/Handler.php b/eZ/Publish/SPI/Persistence/Notification/Handler.php deleted file mode 100644 index 2eecdbec44..0000000000 --- a/eZ/Publish/SPI/Persistence/Notification/Handler.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Notification; - -use eZ\Publish\API\Repository\Values\Notification\Notification as APINotification; - -interface Handler -{ - /** - * Store Notification ValueObject in persistent storage. - * - * @param \eZ\Publish\SPI\Persistence\Notification\CreateStruct $createStruct - * - * @return \eZ\Publish\SPI\Persistence\Notification\Notification - */ - public function createNotification(CreateStruct $createStruct): Notification; - - /** - * Update Notification ValueObject in persistent storage. - * There's no edit feature but it's essential to mark Notification as read. - * - * @param \eZ\Publish\API\Repository\Values\Notification\Notification $notification - * @param \eZ\Publish\SPI\Persistence\Notification\UpdateStruct $updateStruct - * - * @return \eZ\Publish\SPI\Persistence\Notification\Notification - */ - public function updateNotification(APINotification $notification, UpdateStruct $updateStruct): Notification; - - /** - * Count users unread Notifications. - * - * @param int $ownerId - * - * @return int - */ - public function countPendingNotifications(int $ownerId): int; - - /** - * Get Notification by its id. - * - * @param int $notificationId - * - * @return \eZ\Publish\SPI\Persistence\Notification\Notification - */ - public function getNotificationById(int $notificationId): Notification; - - /** - * @param int $userId - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\SPI\Persistence\Notification\Notification[] - */ - public function loadUserNotifications(int $userId, int $offset, int $limit): array; - - /** - * @param int $currentUserId - * - * @return int - */ - public function countNotifications(int $currentUserId): int; - - /** - * @param \eZ\Publish\API\Repository\Values\Notification\Notification $notification - */ - public function delete(APINotification $notification): void; -} diff --git a/eZ/Publish/SPI/Persistence/Notification/Notification.php b/eZ/Publish/SPI/Persistence/Notification/Notification.php deleted file mode 100644 index a2330529ff..0000000000 --- a/eZ/Publish/SPI/Persistence/Notification/Notification.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Notification; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class Notification extends ValueObject -{ - /** @var int */ - public $id; - - /** @var int */ - public $ownerId; - - /** @var bool */ - public $isPending; - - /** @var string */ - public $type; - - /** @var int */ - public $created; - - /** @var array */ - public $data = []; -} diff --git a/eZ/Publish/SPI/Persistence/Notification/UpdateStruct.php b/eZ/Publish/SPI/Persistence/Notification/UpdateStruct.php deleted file mode 100644 index b75b711d90..0000000000 --- a/eZ/Publish/SPI/Persistence/Notification/UpdateStruct.php +++ /dev/null @@ -1,17 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Notification; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class UpdateStruct extends ValueObject -{ - /** @var bool */ - public $isPending; -} diff --git a/eZ/Publish/SPI/Persistence/Setting/Handler.php b/eZ/Publish/SPI/Persistence/Setting/Handler.php deleted file mode 100644 index c8049a9ae5..0000000000 --- a/eZ/Publish/SPI/Persistence/Setting/Handler.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Setting; - -/** - * @internal - */ -interface Handler -{ - public function create( - string $group, - string $identifier, - string $serializedValue - ): Setting; - - public function update( - string $group, - string $identifier, - string $serializedValue - ): Setting; - - public function load( - string $group, - string $identifier - ): Setting; - - public function delete( - string $group, - string $identifier - ): void; -} diff --git a/eZ/Publish/SPI/Persistence/Setting/Setting.php b/eZ/Publish/SPI/Persistence/Setting/Setting.php deleted file mode 100644 index 46f0e19773..0000000000 --- a/eZ/Publish/SPI/Persistence/Setting/Setting.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\Setting; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * @property-read string $group - * @property-read string $identifier - * @property-read string $serializedValue - */ -class Setting extends ValueObject -{ - /** @var string */ - protected $group; - - /** @var string */ - protected $identifier; - - /** @var string */ - protected $serializedValue; -} diff --git a/eZ/Publish/SPI/Persistence/TransactionHandler.php b/eZ/Publish/SPI/Persistence/TransactionHandler.php deleted file mode 100644 index 866a01a06e..0000000000 --- a/eZ/Publish/SPI/Persistence/TransactionHandler.php +++ /dev/null @@ -1,41 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence; - -/** - * The Persistence Transaction handler for Storage Engine. - * - * @since 5.3 - */ -interface TransactionHandler -{ - /** - * Begin transaction. - * - * Begins an transaction, make sure you'll call commit or rollback when done, - * otherwise work will be lost. - */ - public function beginTransaction(); - - /** - * Commit transaction. - * - * Commit transaction, or throw exceptions if no transactions has been started. - * - * @throws \RuntimeException If no transaction has been started - */ - public function commit(); - - /** - * Rollback transaction. - * - * Rollback transaction, or throw exceptions if no transactions has been started. - * - * @throws \RuntimeException If no transaction has been started - */ - public function rollback(); -} diff --git a/eZ/Publish/SPI/Persistence/URL/Handler.php b/eZ/Publish/SPI/Persistence/URL/Handler.php deleted file mode 100644 index 0d7ec58eb9..0000000000 --- a/eZ/Publish/SPI/Persistence/URL/Handler.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\URL; - -use eZ\Publish\API\Repository\Values\URL\URLQuery; - -/** - * The URL Handler interface defines operations on URLs in the storage engine. - */ -interface Handler -{ - /** - * Updates a existing URL. - * - * @param int $id - * @param \eZ\Publish\SPI\Persistence\URL\URLUpdateStruct $urlUpdateStruct - * - * @return \eZ\Publish\SPI\Persistence\URL\URL - */ - public function updateUrl($id, URLUpdateStruct $urlUpdateStruct); - - /** - * Selects URLs data using $query. - * - * @param \eZ\Publish\API\Repository\Values\URL\URLQuery $query - * - * @return array - */ - public function find(URLQuery $query); - - /** - * Returns IDs of Content Objects using URL identified by $id. - * - * @param int $id - * - * @return array - */ - public function findUsages($id); - - /** - * Loads the data for the URL identified by $id. - * - * @param int $id - * - * @return \eZ\Publish\SPI\Persistence\URL\URL - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function loadById($id); - - /** - * Loads the data for the URL identified by $url. - * - * @param string $url - * - * @return \eZ\Publish\SPI\Persistence\URL\URL - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - */ - public function loadByUrl($url); -} diff --git a/eZ/Publish/SPI/Persistence/URL/URL.php b/eZ/Publish/SPI/Persistence/URL/URL.php deleted file mode 100644 index 343a652607..0000000000 --- a/eZ/Publish/SPI/Persistence/URL/URL.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\URL; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class URL extends ValueObject -{ - /** - * ID of the URL. - * - * @var int - */ - public $id; - - /** - * URL address. - * - * @var string - */ - public $url; - - /** - * MD5 checksum of original URL. - * - * @var string - */ - public $originalUrlMd5; - - /** - * Is URL valid? - * - * @var bool - */ - public $isValid; - - /** - * Date of last check (timestamp). - * - * @var int - */ - public $lastChecked; - - /** - * Creation date (timestamp). - * - * @var int - */ - public $created; - - /** - * Modified date (timestamp). - * - * @var int - */ - public $modified; -} diff --git a/eZ/Publish/SPI/Persistence/URL/URLUpdateStruct.php b/eZ/Publish/SPI/Persistence/URL/URLUpdateStruct.php deleted file mode 100644 index 4fa4703823..0000000000 --- a/eZ/Publish/SPI/Persistence/URL/URLUpdateStruct.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\URL; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class URLUpdateStruct extends ValueObject -{ - /** - * URL address. - * - * @var string - */ - public $url; - - /** - * Is URL valid? - * - * @var bool - */ - public $isValid; - - /** - * Date of last check (timestamp). - * - * @var int - */ - public $lastChecked; - - /** - * Modified date (timestamp). - * - * @var int - */ - public $modified; -} diff --git a/eZ/Publish/SPI/Persistence/User.php b/eZ/Publish/SPI/Persistence/User.php deleted file mode 100644 index 045818accb..0000000000 --- a/eZ/Publish/SPI/Persistence/User.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence; - -class User extends ValueObject -{ - /** - * User ID. - * - * @var mixed - */ - public $id; - - /** - * User login. - * - * @var string - */ - public $login; - - /** - * User E-Mail address. - * - * @var string - */ - public $email; - - /** - * User password hash. - * - * @var string - */ - public $passwordHash; - - /** - * Timestamp of last password update. - * - * @var int|null - */ - public $passwordUpdatedAt; - - /** - * Hash algorithm used to has the password. - * - * @var int - */ - public $hashAlgorithm; - - /** - * Flag to signal if user is enabled or not. - * - * User can not login if false - * - * @var bool - */ - public $isEnabled = false; - - /** - * Max number of time user is allowed to login. - * - * @todo: Not used in kernel, should probably be a number of login allowed before changing password. - * But new users gets 0 before they activate, admin has 10, and anonymous has 1000 in clean data. - * - * @var int - */ - public $maxLogin = 0; -} diff --git a/eZ/Publish/SPI/Persistence/User/Handler.php b/eZ/Publish/SPI/Persistence/User/Handler.php deleted file mode 100644 index 2a6892fdb1..0000000000 --- a/eZ/Publish/SPI/Persistence/User/Handler.php +++ /dev/null @@ -1,357 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\User; - -use eZ\Publish\SPI\Persistence\User; - -/** - * Storage Engine handler for user module. - */ -interface Handler -{ - /** - * Create a user. - * - * The User struct used to create the user will contain an ID which is used - * to reference the user. - * - * @param \eZ\Publish\SPI\Persistence\User $user - * - * @return \eZ\Publish\SPI\Persistence\User - */ - public function create(User $user); - - /** - * Loads user with user ID. - * - * @param mixed $userId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found - * - * @return \eZ\Publish\SPI\Persistence\User - */ - public function load($userId); - - /** - * Loads user with user login. - * - * Note: This method loads user by $login case in-sensitive on certain storage engines! - * - * @param string $login - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found - * - * @return \eZ\Publish\SPI\Persistence\User - */ - public function loadByLogin($login); - - /** - * Loads user with user email. - * - * Note: This method loads user by $email case in-sensitive on certain storage engines! - * - * @param string $email - * - * @return \eZ\Publish\SPI\Persistence\User - */ - public function loadByEmail(string $email): User; - - /** - * Loads user(s) with user email. - * - * As earlier eZ Publish versions supported several users having same email (ini config), - * this function may return several users. - * - * Note: This method loads user by $email case in-sensitive on certain storage engines! - * - * @param string $email - * - * @return \eZ\Publish\SPI\Persistence\User[] - */ - public function loadUsersByEmail(string $email): array; - - /** - * Loads user with user hash. - * - * @param string $hash - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If user is not found - * - * @return \eZ\Publish\SPI\Persistence\User - */ - public function loadUserByToken($hash); - - /** - * Update the user information specified by the user struct. - * - * @param \eZ\Publish\SPI\Persistence\User $user - */ - public function update(User $user); - - public function updatePassword(User $user): void; - - /** - * Update the user information specified by the user token struct. - * - * @param \eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct $userTokenUpdateStruct - */ - public function updateUserToken(UserTokenUpdateStruct $userTokenUpdateStruct); - - /** - * Expires user token with user hash. - * - * @param string $hash - */ - public function expireUserToken($hash); - - /** - * Delete user with the given ID. - * - * @param mixed $userId - * - * @todo Throw on missing user? - */ - public function delete($userId); - - /** - * Create new role. - * - * @param \eZ\Publish\SPI\Persistence\User\RoleCreateStruct $createStruct - * - * @return \eZ\Publish\SPI\Persistence\User\Role - */ - public function createRole(RoleCreateStruct $createStruct); - - /** - * Creates a draft of existing defined role. - * - * Sets status to Role::STATUS_DRAFT on the new returned draft. - * - * @param mixed $roleId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role with defined status is not found - * - * @return \eZ\Publish\SPI\Persistence\User\Role - */ - public function createRoleDraft($roleId); - - /** - * Copies an existing role. - */ - public function copyRole(RoleCopyStruct $copyStruct): Role; - - /** - * Loads a specified role (draft) by $roleId. - * - * @param mixed $roleId - * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role is not found - * - * @return \eZ\Publish\SPI\Persistence\User\Role - */ - public function loadRole($roleId, $status = Role::STATUS_DEFINED); - - /** - * Loads a specified role (draft) by $identifier. - * - * @param string $identifier - * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role is not found - * - * @return \eZ\Publish\SPI\Persistence\User\Role - */ - public function loadRoleByIdentifier($identifier, $status = Role::STATUS_DEFINED); - - /** - * Loads a role draft by the original role ID. - * - * @param mixed $roleId ID of the role the draft was created from. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role is not found - * - * @return \eZ\Publish\SPI\Persistence\User\Role - */ - public function loadRoleDraftByRoleId($roleId); - - /** - * Loads all roles. - * - * @return \eZ\Publish\SPI\Persistence\User\Role[] - */ - public function loadRoles(); - - /** - * Loads role assignment for specified assignment ID. - * - * @param mixed $roleAssignmentId - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If role assignment is not found - * - * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment - */ - public function loadRoleAssignment($roleAssignmentId); - - /** - * Loads roles assignments Role. - * - * Role Assignments with same roleId and limitationIdentifier will be merged together into one. - * - * @param mixed $roleId - * - * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment[] - */ - public function loadRoleAssignmentsByRoleId($roleId); - - /** - * Loads Role's assignments based on provided $offset and $limit arguments. - * - * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment[] - */ - public function loadRoleAssignmentsByRoleIdWithOffsetAndLimit(int $roleId, int $offset, ?int $limit): array; - - /** - * Counts Role's assignments taking into consideration related and existing user and user group objects. - */ - public function countRoleAssignments(int $roleId): int; - - /** - * Loads roles assignments to a user/group. - * - * Role Assignments with same roleId and limitationIdentifier will be merged together into one. - * - * @param mixed $groupId In legacy storage engine this is the content object id roles are assigned to in ezuser_role. - * By the nature of legacy this can currently also be used to get by $userId. - * @param bool $inherit If true also return inherited role assignments from user groups. - * - * @return \eZ\Publish\SPI\Persistence\User\RoleAssignment[] - */ - public function loadRoleAssignmentsByGroupId($groupId, $inherit = false); - - /** - * Update role (draft). - * - * @param \eZ\Publish\SPI\Persistence\User\RoleUpdateStruct $role - */ - public function updateRole(RoleUpdateStruct $role); - - /** - * Delete the specified role (draft). - * - * @param mixed $roleId - * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT - */ - public function deleteRole($roleId, $status = Role::STATUS_DEFINED); - - /** - * Publish the specified role draft. - * - * @param mixed $roleDraftId - */ - public function publishRoleDraft($roleDraftId); - - /** - * Adds a policy to a role draft. - * - * @param mixed $roleId - * @param \eZ\Publish\SPI\Persistence\User\Policy $policy - * - * @return \eZ\Publish\SPI\Persistence\User\Policy - * - * @todo Throw on invalid Role Id? - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If $policy->limitation is empty (null, empty string/array..) - */ - public function addPolicyByRoleDraft($roleId, Policy $policy); - - /** - * Adds a policy to a role. - * - * @param mixed $roleId - * @param \eZ\Publish\SPI\Persistence\User\Policy $policy - * - * @return \eZ\Publish\SPI\Persistence\User\Policy - * - * @todo Throw on invalid Role Id? - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If $policy->limitation is empty (null, empty string/array..) - */ - public function addPolicy($roleId, Policy $policy); - - /** - * Update a policy. - * - * Replaces limitations values with new values. - * - * @param \eZ\Publish\SPI\Persistence\User\Policy $policy - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If $policy->limitation is empty (null, empty string/array..) - */ - public function updatePolicy(Policy $policy); - - /** - * Removes a policy from a role. - * - * @param mixed $policyId - * @param mixed $roleId - * - * @todo Throw exception on missing role / policy? - */ - public function deletePolicy($policyId, $roleId); - - /** - * Returns the user policies associated with the user (including inherited policies from user groups). - * - * @deprecated Since 6.8, not currently in use as permission system needs to know about role assignment limitations. - * - * @param mixed $userId - * In legacy storage engine this is the content object id roles are assigned to in ezuser_role. - * - * @return \eZ\Publish\SPI\Persistence\User\Policy[] - */ - public function loadPoliciesByUserId($userId); - - /** - * Assigns role to a user or user group with given limitations. - * - * The limitation array looks like: - * <code> - * array( - * 'Subtree' => array( - * '/1/2/', - * '/1/4/', - * ), - * 'Foo' => array( 'Bar' ), - * … - * ) - * </code> - * - * Where the keys are the limitation identifiers, and the respective values - * are an array of limitation values. The limitation parameter is optional. - * - * @param mixed $contentId The groupId or userId to assign the role to. - * @param mixed $roleId - * @param array $limitation - */ - public function assignRole($contentId, $roleId, array $limitation = null); - - /** - * Un-assign a role. - * - * @param mixed $contentId The user or user group Id to un-assign the role from. - * @param mixed $roleId - */ - public function unassignRole($contentId, $roleId); - - /** - * Un-assign a role, by assignment ID. - * - * @param mixed $roleAssignmentId The assignment ID. - */ - public function removeRoleAssignment($roleAssignmentId); -} diff --git a/eZ/Publish/SPI/Persistence/User/Policy.php b/eZ/Publish/SPI/Persistence/User/Policy.php deleted file mode 100644 index f5d233542e..0000000000 --- a/eZ/Publish/SPI/Persistence/User/Policy.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\User; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class Policy extends ValueObject -{ - /** - * ID of the policy. - * - * @var mixed - */ - public $id; - - /** - * Foreign ID of the role. - * - * @var mixed - */ - public $roleId; - - /** - * Only used when the role's status, current policy belongs to, is Role::STATUS_DRAFT. - * Original policy ID the draft was created from. - * In other cases, will be null. - * - * @since 6.0 - * - * @var int|null - */ - public $originalId; - - /** - * Name of module, associated with the Policy. - * - * Eg: content - * - * @var string - */ - public $module; - - /** - * Name of the module function Or all functions with '*'. - * - * Eg: read - * - * @var string - */ - public $function; - - /** - * Array of policy limitations, which is just a random hash map. - * - * The limitation array may look like: - * <code> - * array( - * 'Subtree' => array( - * '/1/2/', - * '/1/4/', - * ), - * 'Foo' => array( 'Bar' ), - * … - * ) - * </code> - * - * Where the keys are the limitation identifiers, and the respective values - * are an array of limitation values - * - * @var array|string If string, then only the value '*' is allowed, meaning all limitations. - * Can not be a empty array as '*' should be used in this case. - */ - public $limitations; -} diff --git a/eZ/Publish/SPI/Persistence/User/Role.php b/eZ/Publish/SPI/Persistence/User/Role.php deleted file mode 100644 index 39f9b948c6..0000000000 --- a/eZ/Publish/SPI/Persistence/User/Role.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\User; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class Role extends ValueObject -{ - /** @var int Status constant for defined (aka "published") role */ - public const STATUS_DEFINED = 0; - - /** @var int Status constant for draft (aka "temporary") role */ - public const STATUS_DRAFT = 1; - - /** - * ID of the user rule. - * - * @var mixed - */ - public $id; - - /** - * Only used when the role's status, is Role::STATUS_DRAFT. - * Original role ID the draft was created from, or -1 if it's a new role. - * Will be null if role's status is Role::STATUS_DEFINED. - * - * @since 6.0 - * - * @var int|null - */ - public $originalId; - - /** - * Identifier of the role. - * - * Legacy note: Maps to name in 4.x. - * - * @var string - */ - public $identifier; - - /** - * Status of the role (legacy: "version"). - * - * @since 6.0 - * - * @var int One of Role::STATUS_DEFINED|Role::STATUS_DRAFT - */ - public $status; - - /** - * Name of the role. - * - * @since 5.0 - * - * @var string[] - */ - public $name; - - /** - * Name of the role. - * - * @since 5.0 - * - * @var string[] - */ - public $description = []; - - /** - * Policies associated with the role. - * - * @var \eZ\Publish\SPI\Persistence\User\Policy[] - */ - public $policies = []; -} diff --git a/eZ/Publish/SPI/Persistence/User/RoleAssignment.php b/eZ/Publish/SPI/Persistence/User/RoleAssignment.php deleted file mode 100644 index c750383a38..0000000000 --- a/eZ/Publish/SPI/Persistence/User/RoleAssignment.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\User; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class RoleAssignment extends ValueObject -{ - /** - * The role assignment id. - * - * @var mixed - */ - public $id; - - /** - * The Role connected to this assignment. - * - * @var mixed - */ - public $roleId; - - /** - * The user or user group id. - * - * @var mixed - */ - public $contentId; - - /** - * One of 'Subtree' or 'Section'. - * - * @var string|null - */ - public $limitationIdentifier; - - /** - * The subtree paths or section ids. - * - * @var mixed[]|null - */ - public $values; -} diff --git a/eZ/Publish/SPI/Persistence/User/RoleCopyStruct.php b/eZ/Publish/SPI/Persistence/User/RoleCopyStruct.php deleted file mode 100644 index 393cb64719..0000000000 --- a/eZ/Publish/SPI/Persistence/User/RoleCopyStruct.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\User; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class RoleCopyStruct extends ValueObject -{ - /** - * ID of user role being cloned. - * - * @var int - */ - public $clonedId; - - /** - * Identifier of new role. - * - * @var string - */ - public $newIdentifier; - - /** - * Status of new role. - * - * @var string - */ - public $status; - - /** - * Contains an array of role policies. - * - * @var \eZ\Publish\API\Repository\Values\User\PolicyCreateStruct[] - */ - public $policies = []; -} diff --git a/eZ/Publish/SPI/Persistence/User/RoleCreateStruct.php b/eZ/Publish/SPI/Persistence/User/RoleCreateStruct.php deleted file mode 100644 index e0f9dc44ae..0000000000 --- a/eZ/Publish/SPI/Persistence/User/RoleCreateStruct.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\User; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class RoleCreateStruct extends ValueObject -{ - /** - * Identifier of the role. - * - * Legacy note: Maps to name in 4.x. - * - * @var string - */ - public $identifier; - - /** - * Contains an array of role policies. - * - * @var mixed[] - */ - public $policies = []; -} diff --git a/eZ/Publish/SPI/Persistence/User/RoleUpdateStruct.php b/eZ/Publish/SPI/Persistence/User/RoleUpdateStruct.php deleted file mode 100644 index 27a7f6ced8..0000000000 --- a/eZ/Publish/SPI/Persistence/User/RoleUpdateStruct.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\User; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class RoleUpdateStruct extends ValueObject -{ - /** - * ID of the user rule. - * - * @var mixed - */ - public $id; - - /** - * Identifier of the role. - * - * Legacy note: Maps to name in 4.x. - * - * @var string - */ - public $identifier; - - /** - * Name of the role. - * - * @since 5.0 - * - * @var string[] - */ - public $name; - - /** - * Description of the role. - * - * @since 5.0 - * - * @var string[] - */ - public $description = []; -} diff --git a/eZ/Publish/SPI/Persistence/User/UserTokenUpdateStruct.php b/eZ/Publish/SPI/Persistence/User/UserTokenUpdateStruct.php deleted file mode 100644 index c58e5ef025..0000000000 --- a/eZ/Publish/SPI/Persistence/User/UserTokenUpdateStruct.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence\User; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * This update struct is used to update User Tokens (formerly known as User account keys). - */ -class UserTokenUpdateStruct extends ValueObject -{ - /** - * Hash key date for user account. - * - * @var string - */ - public $hashKey; - - /** - * Time to which the token is valid - * Unix timestamp. - * - * @var int - */ - public $time; - - /** - * The user to whom the token belongs. - * - * @var int - */ - public $userId; -} diff --git a/eZ/Publish/SPI/Persistence/UserPreference/Handler.php b/eZ/Publish/SPI/Persistence/UserPreference/Handler.php deleted file mode 100644 index 56ecfd037b..0000000000 --- a/eZ/Publish/SPI/Persistence/UserPreference/Handler.php +++ /dev/null @@ -1,49 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\UserPreference; - -interface Handler -{ - /** - * Store UserPreference ValueObject in persistent storage. - * - * @param \eZ\Publish\SPI\Persistence\UserPreference\UserPreferenceSetStruct $setStruct - * - * @return \eZ\Publish\SPI\Persistence\UserPreference\UserPreference - */ - public function setUserPreference(UserPreferenceSetStruct $setStruct): UserPreference; - - /** - * Get UserPreference by its user ID and name. - * - * @param int $userId - * @param string $name - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If no value is found for given preference name. - * - * @return \eZ\Publish\SPI\Persistence\UserPreference\UserPreference - */ - public function getUserPreferenceByUserIdAndName(int $userId, string $name): UserPreference; - - /** - * @param int $userId - * @param int $offset - * @param int $limit - * - * @return \eZ\Publish\SPI\Persistence\UserPreference\UserPreference[] - */ - public function loadUserPreferences(int $userId, int $offset, int $limit): array; - - /** - * @param int $userId - * - * @return int - */ - public function countUserPreferences(int $userId): int; -} diff --git a/eZ/Publish/SPI/Persistence/UserPreference/UserPreference.php b/eZ/Publish/SPI/Persistence/UserPreference/UserPreference.php deleted file mode 100644 index dbe5f601fe..0000000000 --- a/eZ/Publish/SPI/Persistence/UserPreference/UserPreference.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\UserPreference; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class UserPreference extends ValueObject -{ - /** - * ID of the user preference. - * - * @var int - */ - public $id; - - /** - * The ID of the user this user preference belongs to. - * - * @var int - */ - public $userId; - - /** - * Name of user preference. - * - * Eg: timezone - * - * @var string - */ - public $name; - - /** - * Value of user preference. - * - * Eg: America/New_York - * - * @var string - */ - public $value; -} diff --git a/eZ/Publish/SPI/Persistence/UserPreference/UserPreferenceSetStruct.php b/eZ/Publish/SPI/Persistence/UserPreference/UserPreferenceSetStruct.php deleted file mode 100644 index 5b5ee079bc..0000000000 --- a/eZ/Publish/SPI/Persistence/UserPreference/UserPreferenceSetStruct.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Persistence\UserPreference; - -use eZ\Publish\SPI\Persistence\ValueObject; - -class UserPreferenceSetStruct extends ValueObject -{ - /** @var int */ - public $userId; - - /** @var string */ - public $name; - - /** @var string */ - public $value; -} diff --git a/eZ/Publish/SPI/Persistence/ValueObject.php b/eZ/Publish/SPI/Persistence/ValueObject.php deleted file mode 100644 index 9577ee3824..0000000000 --- a/eZ/Publish/SPI/Persistence/ValueObject.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Persistence; - -use eZ\Publish\API\Repository\Values\ValueObject as APIValueObject; - -/** - * Base SPI Value object. - * - * All properties of SPI\ValueObject *must* be serializable for cache & NoSQL use. - */ -abstract class ValueObject extends APIValueObject -{ -} diff --git a/eZ/Publish/SPI/Repository/Decorator/BookmarkServiceDecorator.php b/eZ/Publish/SPI/Repository/Decorator/BookmarkServiceDecorator.php deleted file mode 100644 index b969d60910..0000000000 --- a/eZ/Publish/SPI/Repository/Decorator/BookmarkServiceDecorator.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Decorator; - -use eZ\Publish\API\Repository\BookmarkService; -use eZ\Publish\API\Repository\Values\Bookmark\BookmarkList; -use eZ\Publish\API\Repository\Values\Content\Location; - -abstract class BookmarkServiceDecorator implements BookmarkService -{ - /** @var \eZ\Publish\API\Repository\BookmarkService */ - protected $innerService; - - public function __construct(BookmarkService $innerService) - { - $this->innerService = $innerService; - } - - public function createBookmark(Location $location): void - { - $this->innerService->createBookmark($location); - } - - public function deleteBookmark(Location $location): void - { - $this->innerService->deleteBookmark($location); - } - - public function loadBookmarks( - int $offset = 0, - int $limit = 25 - ): BookmarkList { - return $this->innerService->loadBookmarks($offset, $limit); - } - - public function isBookmarked(Location $location): bool - { - return $this->innerService->isBookmarked($location); - } -} diff --git a/eZ/Publish/SPI/Repository/Decorator/FieldTypeServiceDecorator.php b/eZ/Publish/SPI/Repository/Decorator/FieldTypeServiceDecorator.php deleted file mode 100644 index 702c6582c1..0000000000 --- a/eZ/Publish/SPI/Repository/Decorator/FieldTypeServiceDecorator.php +++ /dev/null @@ -1,38 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Decorator; - -use eZ\Publish\API\Repository\FieldType; -use eZ\Publish\API\Repository\FieldTypeService; - -abstract class FieldTypeServiceDecorator implements FieldTypeService -{ - /** @var \eZ\Publish\API\Repository\FieldTypeService */ - protected $innerService; - - public function __construct(FieldTypeService $innerService) - { - $this->innerService = $innerService; - } - - public function getFieldTypes(): iterable - { - return $this->innerService->getFieldTypes(); - } - - public function getFieldType(string $identifier): FieldType - { - return $this->innerService->getFieldType($identifier); - } - - public function hasFieldType(string $identifier): bool - { - return $this->innerService->hasFieldType($identifier); - } -} diff --git a/eZ/Publish/SPI/Repository/Decorator/NotificationServiceDecorator.php b/eZ/Publish/SPI/Repository/Decorator/NotificationServiceDecorator.php deleted file mode 100644 index e350690826..0000000000 --- a/eZ/Publish/SPI/Repository/Decorator/NotificationServiceDecorator.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Decorator; - -use eZ\Publish\API\Repository\NotificationService; -use eZ\Publish\API\Repository\Values\Notification\CreateStruct; -use eZ\Publish\API\Repository\Values\Notification\Notification; -use eZ\Publish\API\Repository\Values\Notification\NotificationList; - -abstract class NotificationServiceDecorator implements NotificationService -{ - /** @var \eZ\Publish\API\Repository\NotificationService */ - protected $innerService; - - public function __construct(NotificationService $innerService) - { - $this->innerService = $innerService; - } - - public function loadNotifications( - int $offset, - int $limit - ): NotificationList { - return $this->innerService->loadNotifications($offset, $limit); - } - - public function getNotification(int $notificationId): Notification - { - return $this->innerService->getNotification($notificationId); - } - - public function markNotificationAsRead(Notification $notification): void - { - $this->innerService->markNotificationAsRead($notification); - } - - public function getPendingNotificationCount(): int - { - return $this->innerService->getPendingNotificationCount(); - } - - public function getNotificationCount(): int - { - return $this->innerService->getNotificationCount(); - } - - public function createNotification(CreateStruct $createStruct): Notification - { - return $this->innerService->createNotification($createStruct); - } - - public function deleteNotification(Notification $notification): void - { - $this->innerService->deleteNotification($notification); - } -} diff --git a/eZ/Publish/SPI/Repository/Decorator/SettingServiceDecorator.php b/eZ/Publish/SPI/Repository/Decorator/SettingServiceDecorator.php deleted file mode 100644 index 1cfe9122d7..0000000000 --- a/eZ/Publish/SPI/Repository/Decorator/SettingServiceDecorator.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Decorator; - -use eZ\Publish\API\Repository\SettingService; -use eZ\Publish\API\Repository\Values\Setting\Setting; -use eZ\Publish\API\Repository\Values\Setting\SettingCreateStruct; -use eZ\Publish\API\Repository\Values\Setting\SettingUpdateStruct; - -abstract class SettingServiceDecorator implements SettingService -{ - /** @var \eZ\Publish\API\Repository\SettingService */ - protected $innerService; - - public function __construct( - SettingService $innerService - ) { - $this->innerService = $innerService; - } - - public function loadSetting(string $group, string $identifier): Setting - { - return $this->innerService->loadSetting($group, $identifier); - } - - public function updateSetting(Setting $setting, SettingUpdateStruct $settingUpdateStruct): Setting - { - return $this->innerService->updateSetting($setting, $settingUpdateStruct); - } - - public function createSetting(SettingCreateStruct $settingCreateStruct): Setting - { - return $this->innerService->createSetting($settingCreateStruct); - } - - public function deleteSetting(Setting $setting): void - { - $this->innerService->deleteSetting($setting); - } - - public function newSettingCreateStruct(array $properties = []): SettingCreateStruct - { - return $this->innerService->newSettingCreateStruct($properties); - } - - public function newSettingUpdateStruct(array $properties = []): SettingUpdateStruct - { - return $this->innerService->newSettingUpdateStruct($properties); - } -} diff --git a/eZ/Publish/SPI/Repository/Decorator/TranslationServiceDecorator.php b/eZ/Publish/SPI/Repository/Decorator/TranslationServiceDecorator.php deleted file mode 100644 index 443344c64c..0000000000 --- a/eZ/Publish/SPI/Repository/Decorator/TranslationServiceDecorator.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Decorator; - -use eZ\Publish\API\Repository\TranslationService; -use eZ\Publish\API\Repository\Values\Translation; - -abstract class TranslationServiceDecorator implements TranslationService -{ - /** @var \eZ\Publish\API\Repository\TranslationService */ - protected $innerService; - - public function __construct(TranslationService $innerService) - { - $this->innerService = $innerService; - } - - public function translate( - Translation $translation, - $locale - ) { - return $this->innerService->translate($translation, $locale); - } - - public function translateString( - $translation, - $locale - ) { - return $this->innerService->translateString($translation, $locale); - } -} diff --git a/eZ/Publish/SPI/Repository/Decorator/TrashServiceDecorator.php b/eZ/Publish/SPI/Repository/Decorator/TrashServiceDecorator.php deleted file mode 100644 index ccd8daeb78..0000000000 --- a/eZ/Publish/SPI/Repository/Decorator/TrashServiceDecorator.php +++ /dev/null @@ -1,60 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Decorator; - -use eZ\Publish\API\Repository\TrashService; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Trash\SearchResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResultList; -use eZ\Publish\API\Repository\Values\Content\TrashItem; - -abstract class TrashServiceDecorator implements TrashService -{ - /** @var \eZ\Publish\API\Repository\TrashService */ - protected $innerService; - - public function __construct(TrashService $innerService) - { - $this->innerService = $innerService; - } - - public function loadTrashItem(int $trashItemId): TrashItem - { - return $this->innerService->loadTrashItem($trashItemId); - } - - public function trash(Location $location): ?TrashItem - { - return $this->innerService->trash($location); - } - - public function recover( - TrashItem $trashItem, - Location $newParentLocation = null - ): Location { - return $this->innerService->recover($trashItem, $newParentLocation); - } - - public function emptyTrash(): TrashItemDeleteResultList - { - return $this->innerService->emptyTrash(); - } - - public function deleteTrashItem(TrashItem $trashItem): TrashItemDeleteResult - { - return $this->innerService->deleteTrashItem($trashItem); - } - - public function findTrashItems(Query $query): SearchResult - { - return $this->innerService->findTrashItems($query); - } -} diff --git a/eZ/Publish/SPI/Repository/Decorator/URLServiceDecorator.php b/eZ/Publish/SPI/Repository/Decorator/URLServiceDecorator.php deleted file mode 100644 index 1aae45bc57..0000000000 --- a/eZ/Publish/SPI/Repository/Decorator/URLServiceDecorator.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Decorator; - -use eZ\Publish\API\Repository\URLService; -use eZ\Publish\API\Repository\Values\URL\SearchResult; -use eZ\Publish\API\Repository\Values\URL\URL; -use eZ\Publish\API\Repository\Values\URL\URLQuery; -use eZ\Publish\API\Repository\Values\URL\URLUpdateStruct; -use eZ\Publish\API\Repository\Values\URL\UsageSearchResult; - -abstract class URLServiceDecorator implements URLService -{ - /** @var \eZ\Publish\API\Repository\URLService */ - protected $innerService; - - public function __construct(URLService $innerService) - { - $this->innerService = $innerService; - } - - public function createUpdateStruct(): URLUpdateStruct - { - return $this->innerService->createUpdateStruct(); - } - - public function findUrls(URLQuery $query): SearchResult - { - return $this->innerService->findUrls($query); - } - - public function findUsages( - URL $url, - int $offset = 0, - int $limit = -1 - ): UsageSearchResult { - return $this->innerService->findUsages($url, $offset, $limit); - } - - public function loadById(int $id): URL - { - return $this->innerService->loadById($id); - } - - public function loadByUrl(string $url): URL - { - return $this->innerService->loadByUrl($url); - } - - public function updateUrl( - URL $url, - URLUpdateStruct $struct - ): URL { - return $this->innerService->updateUrl($url, $struct); - } -} diff --git a/eZ/Publish/SPI/Repository/Decorator/UserPreferenceServiceDecorator.php b/eZ/Publish/SPI/Repository/Decorator/UserPreferenceServiceDecorator.php deleted file mode 100644 index 66a7bd3cb9..0000000000 --- a/eZ/Publish/SPI/Repository/Decorator/UserPreferenceServiceDecorator.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Decorator; - -use eZ\Publish\API\Repository\UserPreferenceService; -use eZ\Publish\API\Repository\Values\UserPreference\UserPreference; -use eZ\Publish\API\Repository\Values\UserPreference\UserPreferenceList; - -abstract class UserPreferenceServiceDecorator implements UserPreferenceService -{ - /** @var \eZ\Publish\API\Repository\UserPreferenceService */ - protected $innerService; - - public function __construct(UserPreferenceService $innerService) - { - $this->innerService = $innerService; - } - - public function setUserPreference(array $userPreferenceSetStructs): void - { - $this->innerService->setUserPreference($userPreferenceSetStructs); - } - - public function getUserPreference(string $userPreferenceName): UserPreference - { - return $this->innerService->getUserPreference($userPreferenceName); - } - - public function loadUserPreferences( - int $offset = 0, - int $limit = 25 - ): UserPreferenceList { - return $this->innerService->loadUserPreferences($offset, $limit); - } - - public function getUserPreferenceCount(): int - { - return $this->innerService->getUserPreferenceCount(); - } -} diff --git a/eZ/Publish/SPI/Repository/Event/AfterEvent.php b/eZ/Publish/SPI/Repository/Event/AfterEvent.php deleted file mode 100644 index 043b855b8a..0000000000 --- a/eZ/Publish/SPI/Repository/Event/AfterEvent.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Event; - -use Symfony\Contracts\EventDispatcher\Event; - -/** - * Event emitted after action execution. - */ -abstract class AfterEvent extends Event -{ -} diff --git a/eZ/Publish/SPI/Repository/Event/BeforeEvent.php b/eZ/Publish/SPI/Repository/Event/BeforeEvent.php deleted file mode 100644 index 89368ff955..0000000000 --- a/eZ/Publish/SPI/Repository/Event/BeforeEvent.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Event; - -use Symfony\Contracts\EventDispatcher\Event; - -/** - * Event emitted before action execution. - */ -abstract class BeforeEvent extends Event -{ -} diff --git a/eZ/Publish/SPI/Repository/Strategy/ContentThumbnail/Field/FieldTypeBasedThumbnailStrategy.php b/eZ/Publish/SPI/Repository/Strategy/ContentThumbnail/Field/FieldTypeBasedThumbnailStrategy.php deleted file mode 100644 index 6b05835906..0000000000 --- a/eZ/Publish/SPI/Repository/Strategy/ContentThumbnail/Field/FieldTypeBasedThumbnailStrategy.php +++ /dev/null @@ -1,14 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field; - -interface FieldTypeBasedThumbnailStrategy extends ThumbnailStrategy -{ - public function getFieldTypeIdentifier(): string; -} diff --git a/eZ/Publish/SPI/Repository/Strategy/ContentThumbnail/Field/ThumbnailStrategy.php b/eZ/Publish/SPI/Repository/Strategy/ContentThumbnail/Field/ThumbnailStrategy.php deleted file mode 100644 index 8de33c5f0f..0000000000 --- a/eZ/Publish/SPI/Repository/Strategy/ContentThumbnail/Field/ThumbnailStrategy.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; - -interface ThumbnailStrategy -{ - public function getThumbnail(Field $field, ?VersionInfo $versionInfo = null): ?Thumbnail; -} diff --git a/eZ/Publish/SPI/Repository/Strategy/ContentThumbnail/ThumbnailStrategy.php b/eZ/Publish/SPI/Repository/Strategy/ContentThumbnail/ThumbnailStrategy.php deleted file mode 100644 index 5c4d3a9929..0000000000 --- a/eZ/Publish/SPI/Repository/Strategy/ContentThumbnail/ThumbnailStrategy.php +++ /dev/null @@ -1,18 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Strategy\ContentThumbnail; - -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; - -interface ThumbnailStrategy -{ - public function getThumbnail(ContentType $contentType, array $fields, ?VersionInfo $versionInfo = null): ?Thumbnail; -} diff --git a/eZ/Publish/SPI/Repository/Validator/ContentValidator.php b/eZ/Publish/SPI/Repository/Validator/ContentValidator.php deleted file mode 100644 index 10096981ac..0000000000 --- a/eZ/Publish/SPI/Repository/Validator/ContentValidator.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Validator; - -use eZ\Publish\API\Repository\Values\ValueObject; - -interface ContentValidator -{ - public function supports(ValueObject $object): bool; - - /** - * Validates given content related ValueObject returning field errors structure as a result. - * - * @param array $context Additional context parameters to be used by validators. - * @param string[]|null $fieldIdentifiers List of field identifiers for partial validation or null for - * case of full validation. Empty identifiers array is equal to no validation. - * - * @return array Grouped validation errors by field definition and language code, in format: - * $returnValue[string|int $fieldDefinitionId][string $languageCode] = $fieldErrors; - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - */ - public function validate( - ValueObject $object, - array $context = [], - ?array $fieldIdentifiers = null - ): array; -} diff --git a/eZ/Publish/SPI/Repository/Values/Filter/FilteringCriterion.php b/eZ/Publish/SPI/Repository/Values/Filter/FilteringCriterion.php deleted file mode 100644 index 5e2934f59a..0000000000 --- a/eZ/Publish/SPI/Repository/Values/Filter/FilteringCriterion.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Values\Filter; - -/** - * Marker for Content & Location filtering Criterion. - */ -interface FilteringCriterion -{ -} diff --git a/eZ/Publish/SPI/Repository/Values/Filter/FilteringSortClause.php b/eZ/Publish/SPI/Repository/Values/Filter/FilteringSortClause.php deleted file mode 100644 index 25e1e0c4da..0000000000 --- a/eZ/Publish/SPI/Repository/Values/Filter/FilteringSortClause.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Values\Filter; - -/** - * Marker for Content & Location filtering Sort Clause. - */ -interface FilteringSortClause -{ -} diff --git a/eZ/Publish/SPI/Repository/Values/Filter/SortClauseQueryBuilder.php b/eZ/Publish/SPI/Repository/Values/Filter/SortClauseQueryBuilder.php deleted file mode 100644 index 1e769fcd57..0000000000 --- a/eZ/Publish/SPI/Repository/Values/Filter/SortClauseQueryBuilder.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Values\Filter; - -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; - -interface SortClauseQueryBuilder -{ - public function accepts(FilteringSortClause $sortClause): bool; - - public function buildQuery( - FilteringQueryBuilder $queryBuilder, - FilteringSortClause $sortClause - ): void; -} diff --git a/eZ/Publish/SPI/Repository/Values/Trash/Query/Criterion.php b/eZ/Publish/SPI/Repository/Values/Trash/Query/Criterion.php deleted file mode 100644 index bca715ff77..0000000000 --- a/eZ/Publish/SPI/Repository/Values/Trash/Query/Criterion.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Values\Trash\Query; - -/** - * Marker for Content & Location trash Criterion. - */ -interface Criterion -{ -} diff --git a/eZ/Publish/SPI/Repository/Values/Trash/Query/SortClause.php b/eZ/Publish/SPI/Repository/Values/Trash/Query/SortClause.php deleted file mode 100644 index bb3c4642ef..0000000000 --- a/eZ/Publish/SPI/Repository/Values/Trash/Query/SortClause.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Repository\Values\Trash\Query; - -interface SortClause -{ -} diff --git a/eZ/Publish/SPI/Search/Capable.php b/eZ/Publish/SPI/Search/Capable.php deleted file mode 100644 index 2df09284f7..0000000000 --- a/eZ/Publish/SPI/Search/Capable.php +++ /dev/null @@ -1,26 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Search; - -/** - * Capability interface for search engines needed for {@see \eZ\Publish\API\Repository\SearchService::supports()}. - * - * @since 6.12 And ported to 6.7.6 for search engine forward compatibility. - */ -interface Capable -{ - /** - * Query for supported capability of currently configured search engine. - * - * @param int $capabilityFlag One of eZ\Publish\API\Repository\SearchService::CAPABILITY_* constants. - * - * @return bool - */ - public function supports(int $capabilityFlag): bool; -} diff --git a/eZ/Publish/SPI/Search/Content/IndexerGateway.php b/eZ/Publish/SPI/Search/Content/IndexerGateway.php deleted file mode 100644 index 8f14ee3259..0000000000 --- a/eZ/Publish/SPI/Search/Content/IndexerGateway.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Search\Content; - -use DateTimeInterface; -use Generator; - -/** - * @internal - */ -interface IndexerGateway -{ - /** - * @throws \Doctrine\DBAL\Exception - * - * @return \Generator list of Content IDs for each iteration - */ - public function getContentSince(DateTimeInterface $since, int $iterationCount): Generator; - - /** - * @throws \Doctrine\DBAL\Exception - */ - public function countContentSince(DateTimeInterface $since): int; - - /** - * @throws \Doctrine\DBAL\Exception - * - * @return \Generator list of Content IDs for each iteration - */ - public function getContentInSubtree(string $locationPath, int $iterationCount): Generator; - - /** - * @throws \Doctrine\DBAL\Exception - */ - public function countContentInSubtree(string $locationPath): int; - - /** - * @throws \Doctrine\DBAL\Exception - * - * @return \Generator list of Content IDs for each iteration - */ - public function getAllContent(int $iterationCount): Generator; - - /** - * @throws \Doctrine\DBAL\Exception - */ - public function countAllContent(): int; -} diff --git a/eZ/Publish/SPI/Search/Field.php b/eZ/Publish/SPI/Search/Field.php deleted file mode 100644 index 088fe7bc76..0000000000 --- a/eZ/Publish/SPI/Search/Field.php +++ /dev/null @@ -1,73 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * Base class for document fields. - * - * @property-read $name - * @property-read $value - * @property-read $type - */ -class Field extends ValueObject -{ - /** - * Name of the document field. Will be used to query this field. - * - * @var string - */ - protected $name; - - /** - * Value of the document field. - * - * Might be about anything depending on the type of the document field. - * - * @var mixed - */ - protected $value; - - /** - * Type of the search field. - * - * @var \eZ\Publish\SPI\Search\FieldType - */ - protected $type; - - /** - * @param string $name - * @param mixed $value - * @param \eZ\Publish\SPI\Search\FieldType $type - */ - public function __construct($name, $value, FieldType $type) - { - parent::__construct(); - $this->name = $name; - $this->value = $value; - $this->type = $type; - } - - public function getName(): string - { - return $this->name; - } - - /** - * @return mixed - */ - public function getValue() - { - return $this->value; - } - - public function getType(): FieldType - { - return $this->type; - } -} diff --git a/eZ/Publish/SPI/Search/FieldType.php b/eZ/Publish/SPI/Search/FieldType.php deleted file mode 100644 index 8c786c6d0e..0000000000 --- a/eZ/Publish/SPI/Search/FieldType.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search; - -use eZ\Publish\SPI\Persistence\ValueObject; - -/** - * Base class for document field definitions. - * - * @property-read string $type [deprecated] The type name of the facet, deprecated - use {@see \eZ\Publish\SPI\Search\FieldType::getType} instead. - */ -abstract class FieldType extends ValueObject -{ - /** - * Name of the document field. Will be used to query this field. - * - * @var string - */ - public $name; - - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type; - - /** - * Whether highlighting should be performed for this field on result documents. - * - * @var bool - */ - public $highlight = false; - - /** - * The importance of that field (boost factor). - * - * @var int - */ - public $boost = 1; - - /** - * Whether the field supports multiple values. - * - * @var bool - */ - public $multiValue = false; - - /** - * Whether the field should be a part of the resulting document. - * - * @var bool - */ - public $inResult = true; - - public function getType(): string - { - return $this->type; - } -} diff --git a/eZ/Publish/SPI/Search/FieldType/BooleanField.php b/eZ/Publish/SPI/Search/FieldType/BooleanField.php deleted file mode 100644 index 854c7b276f..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/BooleanField.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * Boolean document field. - */ -class BooleanField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_boolean'; -} diff --git a/eZ/Publish/SPI/Search/FieldType/CustomField.php b/eZ/Publish/SPI/Search/FieldType/CustomField.php deleted file mode 100644 index c7227e2b44..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/CustomField.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * Custom document field. - */ -class CustomField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - public $type; -} diff --git a/eZ/Publish/SPI/Search/FieldType/DateField.php b/eZ/Publish/SPI/Search/FieldType/DateField.php deleted file mode 100644 index 3717087748..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/DateField.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * Date document field. - */ -class DateField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_date'; -} diff --git a/eZ/Publish/SPI/Search/FieldType/DocumentField.php b/eZ/Publish/SPI/Search/FieldType/DocumentField.php deleted file mode 100644 index 4e88bb53f8..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/DocumentField.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * (Nested)Document document field. - */ -class DocumentField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_document'; -} diff --git a/eZ/Publish/SPI/Search/FieldType/FloatField.php b/eZ/Publish/SPI/Search/FieldType/FloatField.php deleted file mode 100644 index fd71182709..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/FloatField.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * Float document field. - */ -class FloatField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_float'; -} diff --git a/eZ/Publish/SPI/Search/FieldType/GeoLocationField.php b/eZ/Publish/SPI/Search/FieldType/GeoLocationField.php deleted file mode 100644 index 94182f1a87..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/GeoLocationField.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * GeoLocation document field. - */ -class GeoLocationField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_geolocation'; -} diff --git a/eZ/Publish/SPI/Search/FieldType/IdentifierField.php b/eZ/Publish/SPI/Search/FieldType/IdentifierField.php deleted file mode 100644 index 354c9291dd..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/IdentifierField.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * Identifier document field. - */ -class IdentifierField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_id'; - - /** - * Indicates that value will not be normalized. - * - * @var bool - */ - protected $raw = false; -} diff --git a/eZ/Publish/SPI/Search/FieldType/IntegerField.php b/eZ/Publish/SPI/Search/FieldType/IntegerField.php deleted file mode 100644 index 13477f4e99..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/IntegerField.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * Integer document field. - */ -class IntegerField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_integer'; -} diff --git a/eZ/Publish/SPI/Search/FieldType/MultipleBooleanField.php b/eZ/Publish/SPI/Search/FieldType/MultipleBooleanField.php deleted file mode 100644 index 7f2955c564..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/MultipleBooleanField.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * Boolean document field. - */ -class MultipleBooleanField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_mboolean'; -} diff --git a/eZ/Publish/SPI/Search/FieldType/MultipleIdentifierField.php b/eZ/Publish/SPI/Search/FieldType/MultipleIdentifierField.php deleted file mode 100644 index eaea44045e..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/MultipleIdentifierField.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * Identifier document field. - */ -class MultipleIdentifierField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_mid'; - - /** - * Indicates that values will not be normalized. - * - * @var bool - */ - protected $raw = false; -} diff --git a/eZ/Publish/SPI/Search/FieldType/MultipleIntegerField.php b/eZ/Publish/SPI/Search/FieldType/MultipleIntegerField.php deleted file mode 100644 index 864947b061..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/MultipleIntegerField.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * Multiple integer document field. - */ -class MultipleIntegerField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_minteger'; -} diff --git a/eZ/Publish/SPI/Search/FieldType/MultipleRemoteIdentifierField.php b/eZ/Publish/SPI/Search/FieldType/MultipleRemoteIdentifierField.php deleted file mode 100644 index 8cd02d0375..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/MultipleRemoteIdentifierField.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * Remote ID list document field. - */ -class MultipleRemoteIdentifierField extends FieldType -{ - /** - * Search engine field type corresponding to remote ID list. The same MultipleIdentifierField due to BC. - * - * @see \eZ\Publish\SPI\Search\FieldType\MultipleIdentifierField - * - * @var string - */ - protected $type = 'ez_mid'; -} diff --git a/eZ/Publish/SPI/Search/FieldType/MultipleStringField.php b/eZ/Publish/SPI/Search/FieldType/MultipleStringField.php deleted file mode 100644 index 7fb6f60615..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/MultipleStringField.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * String document field. - */ -class MultipleStringField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_mstring'; -} diff --git a/eZ/Publish/SPI/Search/FieldType/PriceField.php b/eZ/Publish/SPI/Search/FieldType/PriceField.php deleted file mode 100644 index f5b4cbf85c..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/PriceField.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * Price document field. - */ -class PriceField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_currency'; -} diff --git a/eZ/Publish/SPI/Search/FieldType/RemoteIdentifierField.php b/eZ/Publish/SPI/Search/FieldType/RemoteIdentifierField.php deleted file mode 100644 index bce332b946..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/RemoteIdentifierField.php +++ /dev/null @@ -1,24 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * Remote ID document field. - */ -final class RemoteIdentifierField extends FieldType -{ - /** - * Search engine field type corresponding to remote ID. The same as IdentifierField due to BC. - * - * @see \eZ\Publish\SPI\Search\FieldType\IdentifierField - * - * @var string - */ - protected $type = 'ez_id'; -} diff --git a/eZ/Publish/SPI/Search/FieldType/StringField.php b/eZ/Publish/SPI/Search/FieldType/StringField.php deleted file mode 100644 index 5c47549701..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/StringField.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * String document field. - */ -class StringField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_string'; -} diff --git a/eZ/Publish/SPI/Search/FieldType/TextField.php b/eZ/Publish/SPI/Search/FieldType/TextField.php deleted file mode 100644 index 0da26c5ff7..0000000000 --- a/eZ/Publish/SPI/Search/FieldType/TextField.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search\FieldType; - -use eZ\Publish\SPI\Search\FieldType; - -/** - * Text document field. - */ -class TextField extends FieldType -{ - /** - * The type name of the facet. Has to be handled by the solr schema. - * - * @var string - */ - protected $type = 'ez_text'; -} diff --git a/eZ/Publish/SPI/Search/Handler.php b/eZ/Publish/SPI/Search/Handler.php deleted file mode 100644 index a8d93dc0e0..0000000000 --- a/eZ/Publish/SPI/Search/Handler.php +++ /dev/null @@ -1,110 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Search; - -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Location; - -/** - * The Search handler retrieves sets of of Content objects, based on a - * set of criteria. - */ -interface Handler -{ - /** - * Finds content objects for the given query. - * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if Query criterion is not applicable to its target - * - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - * @param array $languageFilter - a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code> - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult With ContentInfo as SearchHit->valueObject - */ - public function findContent(Query $query, array $languageFilter = []); - - /** - * Performs a query for a single content object. - * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if Criterion is not applicable to its target - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions - * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter - * @param array $languageFilter - a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code> - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo - */ - public function findSingle(Criterion $filter, array $languageFilter = []); - - /** - * Finds locations for the given $query. - * - * @param \eZ\Publish\API\Repository\Values\Content\LocationQuery $query - * @param array $languageFilter - a map of language related filters specifying languages query will be performed on. - * Also used to define which field languages are loaded for the returned content. - * Currently supports: <code>array("languages" => array(<language1>,..), "useAlwaysAvailable" => bool)</code> - * useAlwaysAvailable defaults to true to avoid exceptions on missing translations - * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult With Location as SearchHit->valueObject - */ - public function findLocations(LocationQuery $query, array $languageFilter = []); - - /** - * Suggests a list of values for the given prefix. - * - * @param string $prefix - * @param string[] $fieldPaths - * @param int $limit - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $filter - */ - public function suggest($prefix, $fieldPaths = [], $limit = 10, Criterion $filter = null); - - /** - * Indexes a content object. - * - * @param \eZ\Publish\SPI\Persistence\Content $content - */ - public function indexContent(Content $content); - - /** - * Deletes a content object from the index. - * - * @param int $contentId - * @param int|null $versionId - */ - public function deleteContent($contentId, $versionId = null); - - /** - * Indexes a Location in the index storage. - * - * @param \eZ\Publish\SPI\Persistence\Content\Location $location - */ - public function indexLocation(Location $location); - - /** - * Deletes a location from the index. - * - * @param mixed $locationId - * @param mixed $contentId - */ - public function deleteLocation($locationId, $contentId); - - /** - * Purges all contents from the index. - */ - public function purgeIndex(); -} diff --git a/eZ/Publish/SPI/Specification/Content/ContentContainerSpecification.php b/eZ/Publish/SPI/Specification/Content/ContentContainerSpecification.php deleted file mode 100644 index 6fc44f0de5..0000000000 --- a/eZ/Publish/SPI/Specification/Content/ContentContainerSpecification.php +++ /dev/null @@ -1,19 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Specification\Content; - -use eZ\Publish\API\Repository\Values\Content\Content; - -final class ContentContainerSpecification implements ContentSpecification -{ - public function isSatisfiedBy(Content $content): bool - { - return $content->getContentType()->isContainer; - } -} diff --git a/eZ/Publish/SPI/Specification/Content/ContentSpecification.php b/eZ/Publish/SPI/Specification/Content/ContentSpecification.php deleted file mode 100644 index c3a5be3677..0000000000 --- a/eZ/Publish/SPI/Specification/Content/ContentSpecification.php +++ /dev/null @@ -1,16 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Specification\Content; - -use eZ\Publish\API\Repository\Values\Content\Content; - -interface ContentSpecification -{ - public function isSatisfiedBy(Content $content): bool; -} diff --git a/eZ/Publish/SPI/Specification/Content/ContentTypeSpecification.php b/eZ/Publish/SPI/Specification/Content/ContentTypeSpecification.php deleted file mode 100644 index e546d78f87..0000000000 --- a/eZ/Publish/SPI/Specification/Content/ContentTypeSpecification.php +++ /dev/null @@ -1,29 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Specification\Content; - -use eZ\Publish\API\Repository\Values\Content\Content; - -final class ContentTypeSpecification implements ContentSpecification -{ - /** - * @var string - */ - private $expectedType; - - public function __construct(string $expectedType) - { - $this->expectedType = $expectedType; - } - - public function isSatisfiedBy(Content $content): bool - { - return $content->getContentType()->identifier === $this->expectedType; - } -} diff --git a/eZ/Publish/SPI/Specification/Tests/Content/ContentContainerSpecificationTest.php b/eZ/Publish/SPI/Specification/Tests/Content/ContentContainerSpecificationTest.php deleted file mode 100644 index c2d286eccf..0000000000 --- a/eZ/Publish/SPI/Specification/Tests/Content/ContentContainerSpecificationTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Specification\Tests\Content; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\SPI\Specification\Content\ContentContainerSpecification; -use eZ\Publish\SPI\Specification\Content\ContentSpecification; -use PHPUnit\Framework\TestCase; - -/** - * @covers \eZ\Publish\SPI\Specification\Content\ContentContainerSpecification - */ -final class ContentContainerSpecificationTest extends TestCase -{ - public function testConstructor(): void - { - $contentTypeSpecification = new ContentContainerSpecification(); - - $this->assertInstanceOf(ContentSpecification::class, $contentTypeSpecification); - } - - /** - * @covers \eZ\Publish\SPI\Specification\Content\ContentContainerSpecification::isSatisfiedBy - * @dataProvider providerForIsSatisfiedBy - */ - public function testIsSatisfiedBy( - bool $isContainer, - bool $shouldBeSatisfied - ): void { - $contentContainerSpecification = new ContentContainerSpecification(); - - $contentTypeMock = $this->getMockBuilder(ContentType::class) - ->setConstructorArgs( - [['isContainer' => $isContainer]] - ) - ->getMockForAbstractClass(); - - $contentMock = $this->createMock(Content::class); - $contentMock->expects($this->once()) - ->method('getContentType') - ->willReturn($contentTypeMock); - - $this->assertEquals( - $contentContainerSpecification->isSatisfiedBy($contentMock), - $shouldBeSatisfied - ); - } - - public function providerForIsSatisfiedBy(): array - { - return [ - [true, true], - [false, false], - ]; - } -} diff --git a/eZ/Publish/SPI/Tests/BaseGatewayTest.php b/eZ/Publish/SPI/Tests/BaseGatewayTest.php deleted file mode 100644 index 98c74ff5ac..0000000000 --- a/eZ/Publish/SPI/Tests/BaseGatewayTest.php +++ /dev/null @@ -1,23 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Tests; - -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Tests\SetupFactory\Legacy; - -abstract class BaseGatewayTest extends BaseTest -{ - /** @var \eZ\Publish\API\Repository\Repository */ - protected $repository; - - protected function setUp(): void - { - $this->repository = (new Legacy())->getRepository(true); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/AuthorIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/AuthorIntegrationTest.php deleted file mode 100644 index ec7a0a4622..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/AuthorIntegrationTest.php +++ /dev/null @@ -1,152 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class AuthorIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezauthor'; - } - - /** - * Get handler with required custom field types registered. - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\Author\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezauthor', - $fieldType, - new Legacy\Content\FieldValue\Converter\AuthorConverter(), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezauthor field type does not have any special field definition - // properties - ['fieldType', 'ezauthor'], - [ - 'fieldTypeConstraints', - new Content\FieldTypeConstraints( - [ - 'fieldSettings' => new FieldType\FieldSettings( - [ - 'defaultAuthor' => 0, - ] - ), - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => [ - [ - 'id' => 14, - 'name' => 'Hans Mueller', - 'email' => 'hans@example.com', - ], - ], - 'externalData' => null, - 'sortKey' => null, - ] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => [ - [ - 'id' => 14, - 'name' => 'Hans Mueller', - 'email' => 'hans@example.com', - ], - [ - 'id' => 10, - 'name' => 'Lieschen Mueller', - 'email' => 'lieschen@example.com', - ], - ], - 'externalData' => null, - 'sortKey' => null, - ] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/BaseIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/BaseIntegrationTest.php deleted file mode 100644 index 396d0be03c..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/BaseIntegrationTest.php +++ /dev/null @@ -1,635 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\API\Repository\Tests\Container\Compiler\SetAllServicesPublicPass; -use eZ\Publish\Core\Persistence; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\Core\Persistence\TransformationProcessor\DefinitionBased; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\UpdateStruct; -use eZ\Publish\SPI\Tests\Persistence\FixtureImporter; -use eZ\Publish\SPI\Tests\Persistence\YamlFixture; -use Symfony\Component\Config\FileLocator; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; - -/** - * Integration test for the legacy storage. - * - * @group integration - */ -abstract class BaseIntegrationTest extends TestCase -{ - /** - * Property indicating whether the DB already has been set up. - * - * @var bool - */ - protected static $setUp = false; - - /** - * Id of test content type. - * - * @var string - */ - protected static $contentTypeId; - - /** - * Id of test content. - * - * @var string - */ - protected static $contentId; - - /** - * Current version of test content. - * - * @var string - */ - protected static $contentVersion; - - /** @var \Symfony\Component\DependencyInjection\ContainerBuilder */ - protected static $container; - - /** - * @return string - */ - protected static function getInstallationDir() - { - static $installDir = null; - if ($installDir === null) { - $config = require __DIR__ . '/../../../../../config.php'; - $installDir = $config['install_dir']; - } - - return $installDir; - } - - /** @var \eZ\Publish\Core\Persistence\TransformationProcessor */ - protected $transformationProcessor; - - /** - * @return \eZ\Publish\Core\Persistence\TransformationProcessor - */ - public function getTransformationProcessor() - { - if (!isset($this->transformationProcessor)) { - $this->transformationProcessor = new DefinitionBased( - new Persistence\TransformationProcessor\DefinitionBased\Parser(self::getInstallationDir()), - new Persistence\TransformationProcessor\PcreCompiler(new Persistence\Utf8Converter()), - glob(__DIR__ . '/../../../Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/*.tr') - ); - } - - return $this->transformationProcessor; - } - - /** - * Returns the identifier of the FieldType under test. - * - * @return string - */ - abstract public function getTypeName(); - - /** - * Returns the Handler with all necessary objects registered. - * - * Returns an instance of the Persistence Handler where the - * FieldType\Storage has been registered. - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - abstract public function getCustomHandler(); - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - abstract public function getTypeConstraints(); - - /** - * Returns the field definition data expected after loading the newly - * created field definition with the FieldType under test. - * - * This is a PHPUnit data provider - * - * @return array - */ - abstract public function getFieldDefinitionData(); - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - abstract public function getInitialValue(); - - /** - * Asserts that the loaded field data is correct. - * - * Performs assertions on the loaded field, mainly checking that the - * $field->value->externalData is loaded correctly. If the loading of - * external data manipulates other aspects of $field, their correctness - * also needs to be asserted. Make sure you implement this method agnostic - * to the used SPI\Persistence implementation! - */ - public function assertLoadedFieldDataCorrect(Field $field) - { - $this->assertEquals( - $this->getInitialValue(), - $field->value - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - abstract public function getUpdatedValue(); - - /** - * Asserts that the updated field data is loaded correct. - * - * Performs assertions on the loaded field after it has been updated, - * mainly checking that the $field->value->externalData is loaded - * correctly. If the loading of external data manipulates other aspects of - * $field, their correctness also needs to be asserted. Make sure you - * implement this method agnostic to the used SPI\Persistence - * implementation! - */ - public function assertUpdatedFieldDataCorrect(Field $field) - { - $this->assertEquals( - $this->getUpdatedValue(), - $field->value - ); - } - - /** - * Method called after content creation. - * - * Useful, if additional stuff should be executed (like creating the actual - * user). - * - * @param Legacy\Handler $handler - * @param \eZ\Publish\SPI\Persistence\Content $content - */ - public function postCreationHook(Legacy\Handler $handler, Content $content) - { - // Do nothing by default - } - - /** - * Can be overwritten to assert that additional data has been deleted. - * - * @param \eZ\Publish\SPI\Persistence\Content $content - */ - public function assertDeletedFieldDataCorrect(Content $content) - { - // Do nothing by default - } - - /** - * Only set up once for these read only tests on a large fixture. - * - * @throws \Doctrine\DBAL\DBALException - */ - protected function setUp(): void - { - if (!self::$setUp) { - self::$container = $this->getContainer(); - parent::setUp(); - $fixtureImporter = new FixtureImporter($this->getDatabaseConnection()); - $fixtureImporter->import( - new YamlFixture(__DIR__ . '/../../../API/Repository/Tests/_fixtures/Legacy/data/test_data.yaml') - ); - self::$setUp = true; - } - } - - public function testCreateContentType() - { - $contentType = $this->createContentType(); - - $this->assertNotNull($contentType->id); - self::$contentTypeId = $contentType->id; - - return $contentType; - } - - /** - * Performs the creation of the content type with a field of the field type - * under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - protected function createContentType() - { - $createStruct = new Content\Type\CreateStruct( - [ - 'name' => ['eng-GB' => 'Test'], - 'identifier' => 'test-' . $this->getTypeName(), - 'status' => 0, - 'creatorId' => 14, - 'created' => time(), - 'modifierId' => 14, - 'modified' => time(), - 'initialLanguageId' => 2, - 'remoteId' => 'abcdef', - ] - ); - - $createStruct->fieldDefinitions = [ - new Content\Type\FieldDefinition( - [ - 'name' => ['eng-GB' => 'Name'], - 'identifier' => 'name', - 'fieldGroup' => 'main', - 'position' => 1, - 'fieldType' => 'ezstring', - 'isTranslatable' => false, - 'isRequired' => true, - ] - ), - new Content\Type\FieldDefinition( - [ - 'name' => ['eng-GB' => 'Data'], - 'identifier' => 'data', - 'fieldGroup' => 'main', - 'position' => 2, - 'fieldType' => $this->getTypeName(), - 'isTranslatable' => false, - 'isRequired' => true, - 'fieldTypeConstraints' => $this->getTypeConstraints(), - ] - ), - ]; - - $handler = $this->getCustomHandler(); - $contentTypeHandler = $handler->contentTypeHandler(); - - return $contentTypeHandler->create($createStruct); - } - - /** - * @depends testCreateContentType - */ - public function testContentTypeField($contentType) - { - $this->assertSame( - $this->getTypeName(), - $contentType->fieldDefinitions[1]->fieldType - ); - } - - /** - * @depends testCreateContentType - */ - public function testLoadContentTypeField() - { - $handler = $this->getCustomHandler(); - $contentTypeHandler = $handler->contentTypeHandler(); - - return $contentTypeHandler->load(self::$contentTypeId); - } - - /** - * @depends testLoadContentTypeField - */ - public function testLoadContentTypeFieldType($contentType) - { - $this->assertSame( - $this->getTypeName(), - $contentType->fieldDefinitions[1]->fieldType - ); - - return $contentType->fieldDefinitions[1]; - } - - /** - * @depends testLoadContentTypeFieldType - * @dataProvider getFieldDefinitionData - */ - public function testLoadContentTypeFieldData($name, $value, $field) - { - $this->assertEquals( - $value, - $field->$name - ); - } - - /** - * @depends testLoadContentTypeField - */ - public function testCreateContent($contentType) - { - $handler = $this->getCustomHandler(); - - $content = $this->createContent($contentType, $this->getInitialValue()); - - self::$contentId = $content->versionInfo->contentInfo->id; - self::$contentVersion = $content->versionInfo->contentInfo->currentVersionNo; - - $this->postCreationHook($handler, $content); - - return $content; - } - - /** - * Creates content of the given $contentType with $fieldValue in - * $languageCode. - * - * @param \eZ\Publish\SPI\Persistence\Content\Type $contentType - * @param mixed $fieldValue - * @param string $languageCode - * - * @return \eZ\Publish\SPI\Persistence\Content - */ - protected function createContent(Type $contentType, $fieldValue, $languageCode = 'eng-GB') - { - $createStruct = new Content\CreateStruct( - [ - 'name' => [$languageCode => 'Test object'], - 'typeId' => $contentType->id, - 'sectionId' => 1, - 'ownerId' => 14, - 'locations' => [ - new Content\Location\CreateStruct( - [ - 'parentId' => 2, - 'remoteId' => 'sindelfingen', - ] - ), - ], - // Language with id=2 is eng-US - // This is probably a mistake, as the fields are given with eng-GB, but it has a nice - // side effect of testing creation with empty value. - // TODO: change to eng-GB (8) and/or find a more obvious way to test creation with empty value - 'initialLanguageId' => 2, - 'remoteId' => microtime(), - 'modified' => time(), - 'fields' => [ - new Content\Field( - [ - 'type' => 'ezstring', - 'languageCode' => $languageCode, - 'fieldDefinitionId' => $contentType->fieldDefinitions[0]->id, - 'value' => new Content\FieldValue( - [ - 'data' => 'This is just a test object', - 'sortKey' => 'this is just a test object', - ] - ), - ] - ), - new Content\Field( - [ - 'type' => $this->getTypeName(), - 'languageCode' => $languageCode, - 'fieldDefinitionId' => $contentType->fieldDefinitions[1]->id, - 'value' => $fieldValue, - ] - ), - ], - ] - ); - - $handler = $this->getCustomHandler(); - $contentHandler = $handler->contentHandler(); - - return $contentHandler->create($createStruct); - } - - /** - * @depends testCreateContent - */ - public function testCreatedFieldType($content) - { - $this->assertSame( - $this->getTypeName(), - $content->fields[1]->type - ); - - return $content->fields[1]; - } - - /** - * @depends testCreateContent - */ - public function testLoadField() - { - $handler = $this->getCustomHandler(); - - $contentHandler = $handler->contentHandler(); - - return $contentHandler->load(self::$contentId, self::$contentVersion); - } - - /** - * @depends testLoadField - */ - public function testLoadFieldType($content) - { - $this->assertSame( - $this->getTypeName(), - $content->fields[2]->type - ); - - return $content->fields[2]; - } - - /** - * @depends testLoadFieldType - */ - public function testLoadExternalData($field) - { - $this->assertLoadedFieldDataCorrect($field); - } - - /** - * @depends testLoadFieldType - */ - public function testUpdateField($field) - { - $field->value = $this->getUpdatedValue(); - - return $this->updateContent(self::$contentId, self::$contentVersion, $field); - } - - /** - * Performs an update on $contentId in $contentVersion setting $field. - * - * @param mixed $contentId - * @param mixed $contentVersion - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * - * @return \eZ\Publish\SPI\Persistence\Content - */ - protected function updateContent($contentId, $contentVersion, Field $field) - { - $handler = $this->getCustomHandler(); - - $field->value = $this->getUpdatedValue(); - $updateStruct = new UpdateStruct( - [ - 'creatorId' => 14, - 'modificationDate' => time(), - 'initialLanguageId' => 2, - 'fields' => [ - $field, - ], - ] - ); - - $contentHandler = $handler->contentHandler(); - - return $contentHandler->updateContent($contentId, $contentVersion, $updateStruct); - } - - /** - * @depends testUpdateField - */ - public function testUpdateFieldType($content) - { - $this->assertSame( - $this->getTypeName(), - $content->fields[2]->type - ); - - return $content->fields[2]; - } - - /** - * @depends testUpdateFieldType - */ - public function testUpdateExternalData($field) - { - $this->assertUpdatedFieldDataCorrect($field); - } - - /** - * @depends testUpdateField - */ - public function testDeleteField($content) - { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); - - $handler = $this->getCustomHandler(); - $contentHandler = $handler->contentHandler(); - - $this->deleteContent($content); - - $this->assertDeletedFieldDataCorrect($content); - - $contentHandler->load( - $content->versionInfo->contentInfo->id, - $content->versionInfo->versionNo - ); - } - - /** - * Deletes the given $content. - * - * @param \eZ\Publish\SPI\Persistence\Content $content - */ - protected function deleteContent(Content $content) - { - $handler = $this->getCustomHandler(); - $contentHandler = $handler->contentHandler(); - - $contentHandler->removeRawContent( - $content->versionInfo->contentInfo->id - ); - } - - protected function getContainer() - { - $config = include __DIR__ . '/../../../../../config.php'; - $installDir = $config['install_dir']; - - $containerBuilder = new ContainerBuilder(); - $settingsPath = $installDir . '/eZ/Publish/Core/settings/'; - $loader = new YamlFileLoader($containerBuilder, new FileLocator($settingsPath)); - - $loader->load('fieldtypes.yml'); - $loader->load('io.yml'); - $loader->load('repository.yml'); - $loader->load('repository/inner.yml'); - $loader->load('repository/event.yml'); - $loader->load('repository/siteaccessaware.yml'); - $loader->load('repository/autowire.yml'); - $loader->load('fieldtype_external_storages.yml'); - $loader->load('storage_engines/common.yml'); - $loader->load('storage_engines/shortcuts.yml'); - $loader->load('storage_engines/legacy.yml'); - $loader->load('search_engines/legacy.yml'); - $loader->load('storage_engines/cache.yml'); - $loader->load('settings.yml'); - $loader->load('fieldtype_services.yml'); - $loader->load('utils.yml'); - $loader->load('tests/common.yml'); - $loader->load('policies.yml'); - $loader->load('events.yml'); - $loader->load('tests/integration_legacy.yml'); - $loader->load('thumbnails.yml'); - - $containerBuilder->setParameter('ezpublish.kernel.root_dir', $installDir); - - $containerBuilder->setParameter( - 'legacy_dsn', - $this->getDsn() - ); - - $containerBuilder->addCompilerPass(new SetAllServicesPublicPass()); - - $containerBuilder->compile(); - - return $containerBuilder; - } - - /** - * Returns the Handler. - * - * @param string $identifier - * @param \eZ\Publish\SPI\FieldType\FieldType $fieldType - * @param \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter $fieldValueConverter - * @param \eZ\Publish\SPI\FieldType\FieldStorage $externalStorage - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - protected function getHandler($identifier, $fieldType, $fieldValueConverter, $externalStorage) - { - /** @var \eZ\Publish\Core\Persistence\FieldTypeRegistry $fieldTypeRegistry */ - $fieldTypeRegistry = self::$container->get('ezpublish.persistence.field_type_registry'); - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry $converterRegistry */ - $converterRegistry = self::$container->get('ezpublish.persistence.legacy.field_value_converter.registry'); - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry $storageRegistry */ - $storageRegistry = self::$container->get('ezpublish.persistence.external_storage_registry'); - - $textLineFieldType = new \eZ\Publish\Core\FieldType\TextLine\Type(); - $textLineFieldType->setTransformationProcessor($this->getTransformationProcessor()); - $textLineFieldValueConverter = new Legacy\Content\FieldValue\Converter\TextLineConverter(); - - $fieldTypeRegistry->register('ezstring', $textLineFieldType); - $converterRegistry->register('ezstring', $textLineFieldValueConverter); - - $fieldTypeRegistry->register($identifier, $fieldType); - $converterRegistry->register($identifier, $fieldValueConverter); - $storageRegistry->register($identifier, $externalStorage); - - return self::$container->get('ezpublish.spi.persistence.legacy'); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/BinaryFileIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/BinaryFileIntegrationTest.php deleted file mode 100644 index c49d9ba54d..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/BinaryFileIntegrationTest.php +++ /dev/null @@ -1,273 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator; -use eZ\Publish\Core\IO\MimeTypeDetector\FileInfo; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use FileSystemIterator; -use RecursiveDirectoryIterator; -use RecursiveIteratorIterator; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class BinaryFileIntegrationTest extends FileBaseIntegrationTest -{ - /** @var \eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator&\PHPUnit\Framework\MockObject\MockObject */ - private $fileExtensionBlackListValidator; - - /** - * Returns the storage identifier prefix used by the file service. - * - * @return string - */ - protected function getStoragePrefix() - { - return self::$container->getParameter('binaryfile_storage_prefix'); - } - - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezbinaryfile'; - } - - /** - * Get handler with required custom field types registered. - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\BinaryFile\Type([ - self::$container->get('ezpublish.fieldType.validator.black_list'), - ]); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - $this->ioService = self::$container->get('ezpublish.fieldType.ezbinaryfile.io_service'); - $this->fileExtensionBlackListValidator = $this->createMock(FileExtensionBlackListValidator::class); - - return $this->getHandler( - 'ezbinaryfile', - $fieldType, - new Legacy\Content\FieldValue\Converter\BinaryFileConverter(), - new FieldType\BinaryFile\BinaryFileStorage( - new FieldType\BinaryFile\BinaryFileStorage\Gateway\DoctrineStorage($this->getDatabaseConnection()), - $this->ioService, - new FieldType\BinaryBase\PathGenerator\LegacyPathGenerator(), - new FileInfo(), - $this->fileExtensionBlackListValidator - ) - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new FieldTypeConstraints( - [ - 'validators' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 2, // 2 MB - ], - ], - ] - ); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezint field type does not have any special field definition - // properties - ['fieldType', 'ezbinaryfile'], - [ - 'fieldTypeConstraints', - new FieldTypeConstraints( - [ - 'validators' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 2, // 2 MB - ], - ], - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => null, - 'externalData' => [ - 'id' => null, - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), - 'fileName' => 'Ice-Flower-Binary.jpg', - 'fileSize' => filesize($path), - 'mimeType' => 'image/jpeg', - 'downloadCount' => 0, - 'uri' => __DIR__ . '/_fixtures/image.jpg', - ], - 'sortKey' => '', - ] - ); - } - - /** - * Asserts that the loaded field data is correct. - * - * Performs assertions on the loaded field, mainly checking that the - * $field->value->externalData is loaded correctly. If the loading of - * external data manipulates other aspects of $field, their correctness - * also needs to be asserted. Make sure you implement this method agnostic - * to the used SPI\Persistence implementation! - */ - public function assertLoadedFieldDataCorrect(Field $field) - { - $this->assertNotNull($field->value->externalData); - - $this->assertIOIdExists($field->value->externalData['id']); - - $this->assertEquals('Ice-Flower-Binary.jpg', $field->value->externalData['fileName']); - $this->assertEquals($this->getFilesize($field->value->externalData['id']), $field->value->externalData['fileSize']); - $this->assertEquals('image/jpeg', $field->value->externalData['mimeType']); - $this->assertEquals(0, $field->value->externalData['downloadCount']); - - $this->assertNull($field->value->data); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => null, - 'externalData' => [ - // used to ensure that inputUri has precedence over 'id' - 'id' => 'some/value', - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.png'), - 'fileName' => 'Blueish-Blue-Binary.jpg', - 'fileSize' => filesize($path), - // on purpuse wrong, as it should be ignored by storage - 'mimeType' => 'foo/bar', - 'downloadCount' => 23, - 'uri' => __DIR__ . '/_fixtures/image.jpg', - ], - 'sortKey' => '', - ] - ); - } - - /** - * Asserts that the updated field data is loaded correct. - * - * Performs assertions on the loaded field after it has been updated, - * mainly checking that the $field->value->externalData is loaded - * correctly. If the loading of external data manipulates other aspects of - * $field, their correctness also needs to be asserted. Make sure you - * implement this method agnostic to the used SPI\Persistence - * implementation! - */ - public function assertUpdatedFieldDataCorrect(Field $field) - { - $this->assertNotNull($field->value->externalData); - - $this->assertIOIdExists($field->value->externalData['id']); - - $path = $this->getPathFromId($field->value->externalData['id']); - // Check old file removed before update - $this->assertCount( - 1, - glob(dirname($path) . '/*') - ); - - $this->assertEquals('Blueish-Blue-Binary.jpg', $field->value->externalData['fileName']); - $this->assertEquals(filesize($path), $field->value->externalData['fileSize']); - $this->assertEquals('image/png', $field->value->externalData['mimeType']); - $this->assertEquals(23, $field->value->externalData['downloadCount']); - - $this->assertNull($field->value->data); - } - - /** - * Can be overwritten to assert that additional data has been deleted. - * - * @param \eZ\Publish\SPI\Persistence\Content $content - */ - public function assertDeletedFieldDataCorrect(Content $content) - { - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator( - $this->getStorageDir(), - FileSystemIterator::KEY_AS_PATHNAME | FileSystemIterator::SKIP_DOTS | FileSystemIterator::CURRENT_AS_FILEINFO - ), - RecursiveIteratorIterator::CHILD_FIRST - ); - - foreach ($iterator as $path => $fileInfo) { - if ($fileInfo->isFile()) { - $this->fail( - sprintf( - 'Found undeleted file "%s"', - $path - ) - ); - } - } - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/CheckboxIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/CheckboxIntegrationTest.php deleted file mode 100644 index 4880880318..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/CheckboxIntegrationTest.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class CheckboxIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezboolean'; - } - - /** - * Get handler with required custom field types registered. - * - * @return Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\Checkbox\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezboolean', - $fieldType, - new Legacy\Content\FieldValue\Converter\CheckboxConverter(), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezbool field type does not have any special field definition - // properties - ['fieldType', 'ezboolean'], - ['fieldTypeConstraints', new Content\FieldTypeConstraints([])], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => true, - 'externalData' => null, - 'sortKey' => 1, - ] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => false, - 'externalData' => null, - 'sortKey' => 0, - ] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/CountryIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/CountryIntegrationTest.php deleted file mode 100644 index ac50ca6174..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/CountryIntegrationTest.php +++ /dev/null @@ -1,150 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class CountryIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezcountry'; - } - - /** - * Get handler with required custom field types registered. - * - * @return Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\Country\Type( - [ - 'BE' => [ - 'Name' => 'Belgium', - 'Alpha2' => 'BE', - 'Alpha3' => 'BEL', - 'IDC' => '32', - ], - 'FR' => [ - 'Name' => 'France', - 'Alpha2' => 'FR', - 'Alpha3' => 'FRA', - 'IDC' => '33', - ], - ] - ); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezcountry', - $fieldType, - new Legacy\Content\FieldValue\Converter\CountryConverter(), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezcountry field type does not have any special field definition - // properties - ['fieldType', 'ezcountry'], - [ - 'fieldTypeConstraints', - new Content\FieldTypeConstraints( - [ - 'fieldSettings' => new FieldType\FieldSettings( - [ - 'isMultiple' => false, - ] - ), - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => ['BE'], - 'externalData' => null, - 'sortKey' => 'Belgium', - ] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => ['FR'], - 'externalData' => null, - 'sortKey' => 'France', - ] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/DateAndTimeIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/DateAndTimeIntegrationTest.php deleted file mode 100644 index 5bda3768f3..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/DateAndTimeIntegrationTest.php +++ /dev/null @@ -1,143 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class DateAndTimeIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezdatetime'; - } - - /** - * Get handler with required custom field types registered. - * - * @return Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\DateAndTime\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezdatetime', - $fieldType, - new Legacy\Content\FieldValue\Converter\DateAndTimeConverter(), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezdatetime field type does not have any special field definition - // properties - ['fieldType', 'ezdatetime'], - [ - 'fieldTypeConstraints', - new Content\FieldTypeConstraints( - [ - 'fieldSettings' => new FieldType\FieldSettings( - [ - 'defaultType' => 0, - 'useSeconds' => false, - 'dateInterval' => null, - ] - ), - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => [ - 'timestamp' => 123456, - 'rfc850' => null, - ], - 'externalData' => null, - 'sortKey' => 42, - ] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => [ - 'timestamp' => 12345678, - 'rfc850' => null, - ], - 'externalData' => null, - 'sortKey' => 23, - ] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/DateIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/DateIntegrationTest.php deleted file mode 100644 index 65dff3db95..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/DateIntegrationTest.php +++ /dev/null @@ -1,141 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class DateIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezdate'; - } - - /** - * Get handler with required custom field types registered. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\Date\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezdate', - $fieldType, - new Legacy\Content\FieldValue\Converter\DateConverter(), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezdate field type does not have any special field definition - // properties - ['fieldType', 'ezdate'], - [ - 'fieldTypeConstraints', - new Content\FieldTypeConstraints( - [ - 'fieldSettings' => new FieldType\FieldSettings( - [ - 'defaultType' => 0, - ] - ), - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => [ - 'timestamp' => 123456, - 'rfc850' => null, - ], - 'externalData' => null, - 'sortKey' => 42, - ] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => [ - 'timestamp' => 12345678, - 'rfc850' => null, - ], - 'externalData' => null, - 'sortKey' => 23, - ] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/EmailAddressIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/EmailAddressIntegrationTest.php deleted file mode 100644 index 7bdcaa7d05..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/EmailAddressIntegrationTest.php +++ /dev/null @@ -1,133 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class EmailAddressIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezemail'; - } - - /** - * Get handler with required custom field types registered. - * - * @return Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\EmailAddress\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezemail', - $fieldType, - new Legacy\Content\FieldValue\Converter\EmailAddressConverter(), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezstring field type does not have any special field definition - // properties - ['fieldType', 'ezemail'], - [ - 'fieldTypeConstraints', - new Content\FieldTypeConstraints( - [ - 'validators' => [ - 'EmailAddressValidator' => [], - ], - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => 'admin@link.invalid', - 'externalData' => null, - 'sortKey' => 'admin@link.invalid', - ] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => 'no-spam@example.com', - 'externalData' => null, - 'sortKey' => 'no-spam@example.com', - ] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/FileBaseIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/FileBaseIntegrationTest.php deleted file mode 100644 index 4e2279f738..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/FileBaseIntegrationTest.php +++ /dev/null @@ -1,223 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\API\Repository\Tests\Container\Compiler\SetAllServicesPublicPass; -use FileSystemIterator; -use RecursiveDirectoryIterator; -use RecursiveIteratorIterator; -use Symfony\Component\Config\FileLocator; -use Symfony\Component\DependencyInjection\ContainerBuilder; -use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; -use Symfony\Component\Filesystem\Filesystem as FilesystemComponent; - -abstract class FileBaseIntegrationTest extends BaseIntegrationTest -{ - /** - * Temporary storage directory. - * - * @var string - */ - protected static $tmpDir; - - /** @var \eZ\Publish\Core\IO\IOServiceInterface */ - protected $ioService; - - /** - * @see EZP-23534 - */ - public function testLoadingContentWithMissingFileWorks() - { - $contentType = $this->createContentType(); - $content = $this->createContent($contentType, $this->getInitialValue()); - - // delete the binary file object - $this->deleteStoredFile($content); - - // try loading the content again. It should work even though the image isn't physically here - $this->getCustomHandler()->contentHandler()->load($content->versionInfo->contentInfo->id, 1); - } - - /** - * Deletes the binary file stored in the field. - * - * @param $content - * - * @return mixed - */ - protected function deleteStoredFile($content) - { - return $this->ioService->deleteBinaryFile( - $this->ioService->loadBinaryFile($content->fields[1]->value->externalData['id']) - ); - } - - /** - * Returns prefix used by the IOService. - * - * @return string - */ - abstract protected function getStoragePrefix(); - - /** - * Sets up a temporary directory and stores its path in self::$tmpDir. - */ - public static function setUpBeforeClass(): void - { - $calledClass = static::class; - - $tmpFile = tempnam( - sys_get_temp_dir(), - 'eZ_' . substr($calledClass, strrpos($calledClass, '\\') + 1) - ); - - // Convert file into directory - unlink($tmpFile); - mkdir($tmpFile); - - self::$tmpDir = $tmpFile; - - $storageDir = self::$tmpDir . '/var/ezdemo_site/storage'; - if (!file_exists($storageDir)) { - $fs = new FilesystemComponent(); - $fs->mkdir($storageDir); - } - - self::$setUp = false; - - parent::setUpBeforeClass(); - } - - /** - * Removes the temp dir. - */ - public static function tearDownAfterClass(): void - { - self::removeRecursive(self::$tmpDir); - parent::tearDownAfterClass(); - } - - /** - * Removes the given directory path recursively. - * - * @param string $dir - */ - protected static function removeRecursive($dir) - { - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator( - $dir, - FileSystemIterator::KEY_AS_PATHNAME | FileSystemIterator::SKIP_DOTS | FileSystemIterator::CURRENT_AS_FILEINFO - ), - RecursiveIteratorIterator::CHILD_FIRST - ); - - foreach ($iterator as $path => $fileInfo) { - if ($fileInfo->isDir()) { - rmdir($path); - } else { - unlink($path); - } - } - - rmdir($dir); - } - - protected function getContainer() - { - $config = include __DIR__ . '/../../../../../config.php'; - $installDir = $config['install_dir']; - - $containerBuilder = new ContainerBuilder(); - $settingsPath = $installDir . '/eZ/Publish/Core/settings/'; - $loader = new YamlFileLoader($containerBuilder, new FileLocator($settingsPath)); - - $loader->load('fieldtypes.yml'); - $loader->load('io.yml'); - $loader->load('repository.yml'); - $loader->load('repository/inner.yml'); - $loader->load('repository/event.yml'); - $loader->load('repository/siteaccessaware.yml'); - $loader->load('repository/autowire.yml'); - $loader->load('fieldtype_external_storages.yml'); - $loader->load('storage_engines/common.yml'); - $loader->load('storage_engines/shortcuts.yml'); - $loader->load('storage_engines/legacy.yml'); - $loader->load('search_engines/legacy.yml'); - $loader->load('storage_engines/cache.yml'); - $loader->load('settings.yml'); - $loader->load('fieldtype_services.yml'); - $loader->load('utils.yml'); - $loader->load('tests/common.yml'); - $loader->load('tests/integration_legacy.yml'); - $loader->load('policies.yml'); - $loader->load('events.yml'); - $loader->load('thumbnails.yml'); - - $containerBuilder->setParameter('ezpublish.kernel.root_dir', $installDir); - - $containerBuilder->setParameter( - 'legacy_dsn', - $this->getDsn() - ); - $containerBuilder->setParameter( - 'io_root_dir', - self::$tmpDir . '/var/ezdemo_site/storage' - ); - - $containerBuilder->addCompilerPass(new SetAllServicesPublicPass()); - - $containerBuilder->compile(); - - return $containerBuilder; - } - - /** - * Asserts that the IO File with uri $uri exists. - * - * @param string $uri - */ - protected function assertIOUriExists($uri) - { - $this->assertFileExists( - self::$tmpDir . '/' . $uri, - "Stored file uri $uri does not exist" - ); - } - - /** - * Asserts that the IO File with id $id exists. - * - * @param string $id - */ - protected function assertIOIdExists($id) - { - $path = $this->getPathFromId($id); - $this->assertFileExists( - $path, - "Stored file $path does not exists" - ); - } - - /** - * Returns the physical path to the file with id $id. - */ - protected function getPathFromId($id) - { - return $this->getStorageDir() . '/' . $this->getStoragePrefix() . '/' . $id; - } - - protected function getStorageDir() - { - return (self::$tmpDir ? self::$tmpDir . '/' : '') . self::$container->getParameter('storage_dir'); - } - - protected function getFilesize($binaryFileId) - { - return filesize($this->getPathFromId($binaryFileId)); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/FloatIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/FloatIntegrationTest.php deleted file mode 100644 index 184d9d58b9..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/FloatIntegrationTest.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class FloatIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezfloat'; - } - - /** - * Get handler with required custom field types registered. - * - * @return Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\Float\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezfloat', - $fieldType, - new Legacy\Content\FieldValue\Converter\FloatConverter(), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezfloat field type does not have any special field definition - // properties - ['fieldType', 'ezfloat'], - [ - 'fieldTypeConstraints', - new Content\FieldTypeConstraints( - [ - 'validators' => [ - 'FloatValueValidator' => [ - 'minFloatValue' => false, - 'maxFloatValue' => false, - ], - ], - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => 42.42, - 'externalData' => null, - 'sortKey' => 42, - ] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => 23.23, - 'externalData' => null, - 'sortKey' => 23, - ] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/ISBNIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/ISBNIntegrationTest.php deleted file mode 100644 index 88ccc43383..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/ISBNIntegrationTest.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - * @group ezisbn - */ -class ISBNIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezisbn'; - } - - /** - * Get handler with required custom field types registered. - * - * @return Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\ISBN\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezisbn', - $fieldType, - new Legacy\Content\FieldValue\Converter\ISBNConverter(), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezisbn field type does not have any special field definition - // properties - ['fieldType', 'ezisbn'], - [ - 'fieldTypeConstraints', - new Content\FieldTypeConstraints( - [ - 'fieldSettings' => new FieldType\FieldSettings( - [ - 'isISBN13' => true, - ] - ), - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => '978-972-25-1409-5', - 'externalData' => null, - 'sortKey' => '978-972-25-1409-5', - ] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => '978-972-25-1409-5', - 'externalData' => null, - 'sortKey' => '978-972-25-1409-5', - ] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/ImageAssetIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/ImageAssetIntegrationTest.php deleted file mode 100644 index 05ee354869..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/ImageAssetIntegrationTest.php +++ /dev/null @@ -1,132 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageAssetConverter; -use eZ\Publish\SPI\Persistence\Content; - -class ImageAssetIntegrationTest extends BaseIntegrationTest -{ - /** - * {@inheritdoc} - */ - public function getTypeName() - { - return FieldType\ImageAsset\Type::FIELD_TYPE_IDENTIFIER; - } - - /** - * {@inheritdoc} - */ - public function getCustomHandler() - { - $contentService = $this->createMock(ContentService::class); - $locationService = $this->createMock(LocationService::class); - $contentTypeService = $this->createMock(ContentTypeService::class); - $contentHandler = $this->createMock(Content\Handler::class); - $configResolver = $this->createMock(ConfigResolverInterface::class); - - $mapper = new FieldType\ImageAsset\AssetMapper( - $contentService, - $locationService, - $contentTypeService, - $configResolver - ); - - $fieldType = new FieldType\ImageAsset\Type( - $contentService, - $contentTypeService, - $mapper, - $contentHandler - ); - - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezimageasset', - $fieldType, - new ImageAssetConverter(), - new FieldType\NullStorage() - ); - } - - /** - * {@inheritdoc} - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * {@inheritdoc} - */ - public function getFieldDefinitionData() - { - return [ - ['fieldType', 'ezimageasset'], - ['fieldTypeConstraints', new Content\FieldTypeConstraints(['fieldSettings' => null])], - ]; - } - - /** - * {@inheritdoc} - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => [ - 'destinationContentId' => 1, - 'alternativeText' => null, - ], - 'externalData' => null, - 'sortKey' => null, - ], - [ - 'data' => [ - 'destinationContentId' => 1, - 'alternativeText' => 'The alternative text', - ], - 'externalData' => null, - 'sortKey' => null, - ] - ); - } - - /** - * {@inheritdoc} - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => [ - 'destinationContentId' => 2, - 'alternativeText' => null, - ], - 'externalData' => null, - 'sortKey' => null, - ], - [ - 'data' => [ - 'destinationContentId' => 2, - 'alternativeText' => 'The alternative text', - ], - 'externalData' => null, - 'sortKey' => null, - ] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/ImageIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/ImageIntegrationTest.php deleted file mode 100644 index d8c2438263..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/ImageIntegrationTest.php +++ /dev/null @@ -1,370 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\Base\Utils\DeprecationWarnerInterface; -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\FieldType\Image\AliasCleanerInterface; -use eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator; -use eZ\Publish\Core\IO; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; -use FileSystemIterator; -use PHPUnit\Framework\MockObject\MockObject; -use RecursiveDirectoryIterator; -use RecursiveIteratorIterator; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class ImageIntegrationTest extends FileBaseIntegrationTest -{ - private $deprecationWarnerMock; - - /** @var \PHPUnit\Framework\MockObject\MockObject */ - private $aliasCleanerMock; - - /** @var \eZ\Publish\Core\IO\FilePathNormalizer\Flysystem */ - private $filePathNormalizer; - - /** @var \eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator&\PHPUnit\Framework\MockObject\MockObject */ - private $fileExtensionBlackListValidator; - - /** - * Returns the storage identifier prefix used by the file service. - * - * @return string - */ - protected function getStoragePrefix() - { - return self::$container->getParameter('image_storage_prefix'); - } - - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezimage'; - } - - /** - * Get handler with required custom field types registered. - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\Image\Type([ - self::$container->get('ezpublish.fieldType.validator.black_list'), - self::$container->get('ezpublish.fieldType.validator.image'), - ]); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - $this->ioService = self::$container->get('ezpublish.fieldType.ezimage.io_service'); - /** @var \eZ\Publish\Core\IO\UrlRedecoratorInterface $urlRedecorator */ - $urlRedecorator = self::$container->get('ezpublish.core.io.image_fieldtype.legacy_url_redecorator'); - - return $this->getHandler( - 'ezimage', - $fieldType, - new Legacy\Content\FieldValue\Converter\ImageConverter($this->ioService, $urlRedecorator), - new FieldType\Image\ImageStorage( - new FieldType\Image\ImageStorage\Gateway\DoctrineStorage( - $urlRedecorator, - $this->getDatabaseConnection() - ), - $this->ioService, - new FieldType\Image\PathGenerator\LegacyPathGenerator(), - new IO\MetadataHandler\ImageSize(), - $this->getDeprecationWarnerMock(), - $this->getAliasCleanerMock(), - $this->getFilePathNormalizerMock(), - $this->getFileExtensionBlackListValidatorMock() - ) - ); - } - - /** - * @return \eZ\Publish\Core\Base\Utils\DeprecationWarnerInterface|\PHPUnit\Framework\MockObject\MockObject - */ - public function getDeprecationWarnerMock(): MockObject - { - if (!isset($this->deprecationWarnerMock)) { - $this->deprecationWarnerMock = $this->createMock(DeprecationWarnerInterface::class); - } - - return $this->deprecationWarnerMock; - } - - /** - * @return \eZ\Publish\Core\FieldType\Image\AliasCleanerInterface|\PHPUnit\Framework\MockObject\MockObject - */ - public function getAliasCleanerMock(): MockObject - { - if (!isset($this->aliasCleanerMock)) { - $this->aliasCleanerMock = $this->createMock(AliasCleanerInterface::class); - } - - return $this->aliasCleanerMock; - } - - private function getFilePathNormalizerMock(): IO\FilePathNormalizerInterface - { - if (!isset($this->filePathNormalizer)) { - $this->filePathNormalizer = $this->createMock(IO\FilePathNormalizerInterface::class); - $this->filePathNormalizer->method('normalizePath')->willReturnArgument(0); - } - - return $this->filePathNormalizer; - } - - private function getFileExtensionBlackListValidatorMock(): FileExtensionBlackListValidator - { - if (!isset($this->fileExtensionBlackListValidator)) { - $this->fileExtensionBlackListValidator = $this->createMock(FileExtensionBlackListValidator::class); - $this->fileExtensionBlackListValidator->expects(self::any())->method('validateFileExtension'); - } - - return $this->fileExtensionBlackListValidator; - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints( - [ - 'validators' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 2 * 1024 * 1024, // 2 MB - ], - 'AlternativeTextValidator' => [ - 'required' => true, - ], - ], - ] - ); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezint field type does not have any special field definition - // properties - ['fieldType', 'ezimage'], - [ - 'fieldTypeConstraints', - new Content\FieldTypeConstraints( - [ - 'validators' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 2 * 1024 * 1024, // 2 MB - ], - 'AlternativeTextValidator' => [ - 'required' => true, - ], - ], - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => null, - 'externalData' => [ - 'inputUri' => __DIR__ . '/_fixtures/image.jpg', - 'fileName' => 'Ice-Flower.jpg', - 'alternativeText' => 'An icy flower.', - ], - 'sortKey' => '', - ] - ); - } - - /** - * Asserts that the loaded field data is correct. - * - * Performs assertions on the loaded field, mainly checking that the - * $field->value->externalData is loaded correctly. If the loading of - * external data manipulates other aspects of $field, their correctness - * also needs to be asserted. Make sure you implement this method agnostic - * to the used SPI\Persistence implementation! - */ - public function assertLoadedFieldDataCorrect(Field $field) - { - $this->assertNotNull($field->value->data); - - $this->assertIOUriExists($field->value->data['uri']); - $this->assertEquals('Ice-Flower.jpg', $field->value->data['fileName']); - $this->assertEquals('An icy flower.', $field->value->data['alternativeText']); - $this->assertNull($field->value->externalData); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => null, - 'externalData' => [ - // should be ignored - 'id' => 'some/value', - 'inputUri' => __DIR__ . '/_fixtures/image.png', - 'fileName' => 'Blueish-Blue.jpg', - 'alternativeText' => 'This blue is so blueish.', - ], - 'sortKey' => '', - ] - ); - } - - /** - * Asserts that the updated field data is loaded correct. - * - * Performs assertions on the loaded field after it has been updated, - * mainly checking that the $field->value->externalData is loaded - * correctly. If the loading of external data manipulates other aspects of - * $field, their correctness also needs to be asserted. Make sure you - * implement this method agnostic to the used SPI\Persistence - * implementation! - */ - public function assertUpdatedFieldDataCorrect(Field $field) - { - $this->assertNotNull($field->value->data); - $this->assertIOUriExists($field->value->data['uri']); - - $this->assertEquals('Blueish-Blue.jpg', $field->value->data['fileName']); - $this->assertEquals('This blue is so blueish.', $field->value->data['alternativeText']); - $this->assertNull($field->value->externalData); - } - - /** - * Can be overwritten to assert that additional data has been deleted. - * - * @param \eZ\Publish\SPI\Persistence\Content $content - */ - public function assertDeletedFieldDataCorrect(Content $content) - { - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator( - $this->getStorageDir(), - FileSystemIterator::KEY_AS_PATHNAME | FileSystemIterator::SKIP_DOTS | FileSystemIterator::CURRENT_AS_FILEINFO - ), - RecursiveIteratorIterator::CHILD_FIRST - ); - - // @todo This will fail since updating content without publishing a new version isn't supposed to be supported - // we end up with two images in the attribute's folder, one of which isn't referenced anywhere - /*foreach ( $iterator as $path => $fileInfo ) - { - if ( $fileInfo->isFile() ) - - { - $this->fail( - sprintf( - 'Found undeleted file "%s"', - $path - ) - ); - } - }*/ - } - - public function testCreateContentUsingIdPropertyThrowsWarning() - { - $this->expectException(\eZ\Publish\Core\IO\Exception\InvalidBinaryFileIdException::class); - - $this->testCreateContentType(); - $contentType = $this->testLoadContentTypeField(); - $this->getDeprecationWarnerMock() - ->expects($this->never()) - ->method('log'); - - $this->createContent($contentType, $this->getDeprecatedIdPropertyValue()); - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getDeprecatedIdPropertyValue() - { - return new Content\FieldValue( - [ - 'data' => null, - 'externalData' => [ - 'id' => __DIR__ . '/_fixtures/image.jpg', - 'fileName' => 'Ice-Flower.jpg', - 'alternativeText' => 'An icy flower.', - ], - 'sortKey' => '', - ] - ); - } - - /** - * Overridden to take into account that image moves externaldata to data, unlike BinaryBase. - * - * @param $content - * - * @return mixed - */ - protected function deleteStoredFile($content) - { - return $this->ioService->deleteBinaryFile( - $this->ioService->loadBinaryFile($content->fields[1]->value->data['id']) - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/IntegerIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/IntegerIntegrationTest.php deleted file mode 100644 index d6e2bf6c96..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/IntegerIntegrationTest.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class IntegerIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezinteger'; - } - - /** - * Get handler with required custom field types registered. - * - * @return Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\Integer\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezinteger', - $fieldType, - new Legacy\Content\FieldValue\Converter\IntegerConverter(), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezint field type does not have any special field definition - // properties - ['fieldType', 'ezinteger'], - [ - 'fieldTypeConstraints', - new Content\FieldTypeConstraints( - [ - 'validators' => [ - 'IntegerValueValidator' => [ - 'minIntegerValue' => false, - 'maxIntegerValue' => false, - ], - ], - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => 42, - 'externalData' => null, - 'sortKey' => 42, - ] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => 23, - 'externalData' => null, - 'sortKey' => 23, - ] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/KeywordIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/KeywordIntegrationTest.php deleted file mode 100644 index 967ce307b8..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/KeywordIntegrationTest.php +++ /dev/null @@ -1,202 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class KeywordIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezkeyword'; - } - - /** - * Get handler with required custom field types registered. - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\Keyword\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezkeyword', - $fieldType, - new Legacy\Content\FieldValue\Converter\KeywordConverter(), - new FieldType\Keyword\KeywordStorage( - new FieldType\Keyword\KeywordStorage\Gateway\DoctrineStorage( - $this->getDatabaseConnection() - ) - ) - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezkeyword field type does not have any special field definition - // properties - ['fieldType', 'ezkeyword'], - ['fieldTypeConstraints', new Content\FieldTypeConstraints()], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => [], - 'externalData' => ['foo', 'bar', 'sindelfingen'], - 'sortKey' => 'foo,bar,sindelfingen', - ] - ); - } - - /** - * Asserts that the loaded field data is correct. - * - * Performs assertions on the loaded field, mainly checking that the - * $field->value->externalData is loaded correctly. If the loading of - * external data manipulates other aspects of $field, their correctness - * also needs to be asserted. Make sure you implement this method agnostic - * to the used SPI\Persistence implementation! - */ - public function assertLoadedFieldDataCorrect(Field $field) - { - $this->assertKeywordSetsEqual( - $this->getInitialValue()->externalData, - $field->value->externalData - ); - - $this->assertEquals([], $field->value->data); - $this->assertEquals('foo,bar,sindelfingen', $field->value->sortKey); - } - - /** - * Asserts that 2 keyword sets equal. - * - * @param array $expectedKeywords - * @param array $actualKeywords - */ - protected function assertKeywordSetsEqual($expectedKeywords, $actualKeywords) - { - // Assert all expected keywords are loaded - foreach ($expectedKeywords as $keyword) { - if (($index = array_search($keyword, $actualKeywords)) === false) { - $this->fail( - sprintf( - 'Keyword "%s" not loaded.', - $keyword - ) - ); - } - unset($actualKeywords[$index]); - } - - // Assert no additional keywords have been loaded - if (!empty($actualKeywords)) { - $this->fail( - sprintf( - 'Loaded unexpected keywords: "%s"', - implode('", "', $actualKeywords) - ) - ); - } - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => [], - 'externalData' => ['sindelfingen', 'baz'], - 'sortKey' => 'sindelfingen,baz', - ] - ); - } - - /** - * Asserts that the updated field data is loaded correct. - * - * Performs assertions on the loaded field after it has been updated, - * mainly checking that the $field->value->externalData is loaded - * correctly. If the loading of external data manipulates other aspects of - * $field, their correctness also needs to be asserted. Make sure you - * implement this method agnostic to the used SPI\Persistence - * implementation! - */ - public function assertUpdatedFieldDataCorrect(Field $field) - { - $this->assertKeywordSetsEqual( - $this->getUpdatedValue()->externalData, - $field->value->externalData - ); - - $this->assertEquals([], $field->value->data); - $this->assertEquals('sindelfingen,baz', $field->value->sortKey); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/MapLocationIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/MapLocationIntegrationTest.php deleted file mode 100644 index 7672454211..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/MapLocationIntegrationTest.php +++ /dev/null @@ -1,171 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class MapLocationIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezgmaplocation'; - } - - /** - * Get handler with required custom field types registered. - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\MapLocation\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezgmaplocation', - $fieldType, - new Legacy\Content\FieldValue\Converter\MapLocationConverter(), - new FieldType\MapLocation\MapLocationStorage( - new FieldType\MapLocation\MapLocationStorage\Gateway\DoctrineStorage( - $this->getDatabaseConnection() - ) - ) - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezgmaplocation field type does not have any special field definition - // properties - ['fieldType', 'ezgmaplocation'], - ['fieldTypeConstraints', new Content\FieldTypeConstraints()], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => null, - 'externalData' => [ - 'latitude' => 51.564479, - 'longitude' => 6.692219, - 'address' => 'Sindelfingen', - ], - 'sortKey' => 'Sindelfingen', - ] - ); - } - - /** - * Asserts that the loaded field data is correct. - * - * Performs assertions on the loaded field, mainly checking that the - * $field->value->externalData is loaded correctly. If the loading of - * external data manipulates other aspects of $field, their correctness - * also needs to be asserted. Make sure you implement this method agnostic - * to the used SPI\Persistence implementation! - */ - public function assertLoadedFieldDataCorrect(Field $field) - { - $this->assertEquals( - $this->getInitialValue()->externalData, - $field->value->externalData - ); - - $this->assertNull($field->value->data); - $this->assertNull($field->value->sortKey); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => null, - // Empty value - 'externalData' => null, - 'sortKey' => null, - ] - ); - } - - /** - * Asserts that the updated field data is loaded correct. - * - * Performs assertions on the loaded field after it has been updated, - * mainly checking that the $field->value->externalData is loaded - * correctly. If the loading of external data manipulates other aspects of - * $field, their correctness also needs to be asserted. Make sure you - * implement this method agnostic to the used SPI\Persistence - * implementation! - */ - public function assertUpdatedFieldDataCorrect(Field $field) - { - $this->assertNull($field->value->externalData); - $this->assertNull($field->value->data); - $this->assertNull($field->value->sortKey); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/MediaIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/MediaIntegrationTest.php deleted file mode 100644 index cbcb268d7a..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/MediaIntegrationTest.php +++ /dev/null @@ -1,299 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator; -use eZ\Publish\Core\IO\MimeTypeDetector\FileInfo; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use FileSystemIterator; -use RecursiveDirectoryIterator; -use RecursiveIteratorIterator; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class MediaIntegrationTest extends FileBaseIntegrationTest -{ - /** @var \eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator&\PHPUnit\Framework\MockObject\MockObject */ - private $fileExtensionBlackListValidator; - - /** - * Returns the storage identifier prefix used by the file service. - * - * @return string - */ - protected function getStoragePrefix() - { - return self::$container->getParameter('binaryfile_storage_prefix'); - } - - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezmedia'; - } - - /** - * Get handler with required custom field types registered. - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\Media\Type([ - self::$container->get('ezpublish.fieldType.validator.black_list'), - ]); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - $this->fileExtensionBlackListValidator = $this->createMock(FileExtensionBlackListValidator::class); - - return $this->getHandler( - 'ezmedia', - $fieldType, - new Legacy\Content\FieldValue\Converter\MediaConverter(), - new FieldType\Media\MediaStorage( - new FieldType\Media\MediaStorage\Gateway\DoctrineStorage($this->getDatabaseConnection()), - $this->ioService = self::$container->get('ezpublish.fieldType.ezbinaryfile.io_service'), - $legacyPathGenerator = new FieldType\BinaryBase\PathGenerator\LegacyPathGenerator(), - new FileInfo(), - $this->fileExtensionBlackListValidator - ) - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new FieldTypeConstraints( - [ - 'validators' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 2 * 1024 * 1024, // 2 MB - ], - ], - 'fieldSettings' => new FieldType\FieldSettings( - [ - 'mediaType' => FieldType\Media\Type::TYPE_SILVERLIGHT, - ] - ), - ] - ); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - ['fieldType', 'ezmedia'], - [ - 'fieldTypeConstraints', - new FieldTypeConstraints( - [ - 'validators' => [ - 'FileSizeValidator' => [ - 'maxFileSize' => 2 * 1024 * 1024, // 2 MB - ], - ], - 'fieldSettings' => new FieldType\FieldSettings( - [ - 'mediaType' => FieldType\Media\Type::TYPE_SILVERLIGHT, - ] - ), - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => null, - 'externalData' => [ - 'id' => null, - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), - 'fileName' => 'Ice-Flower-Media.jpg', - 'fileSize' => filesize($path), - 'mimeType' => 'image/jpeg', - 'hasController' => true, - 'autoplay' => true, - 'loop' => true, - 'width' => 23, - 'height' => 42, - 'uri' => $path, - ], - 'sortKey' => '', - ] - ); - } - - /** - * Asserts that the loaded field data is correct. - * - * Performs assertions on the loaded field, mainly checking that the - * $field->value->externalData is loaded correctly. If the loading of - * external data manipulates other aspects of $field, their correctness - * also needs to be asserted. Make sure you implement this method agnostic - * to the used SPI\Persistence implementation! - */ - public function assertLoadedFieldDataCorrect(Field $field) - { - $this->assertNotNull($field->value->externalData); - - $this->assertFileExists( - $path = $this->getStorageDir() . '/' . $this->getStoragePrefix() . '/' . $field->value->externalData['id'], - "Stored file $path does not exists" - ); - - $this->assertEquals('Ice-Flower-Media.jpg', $field->value->externalData['fileName']); - $this->assertEquals(filesize($path), $field->value->externalData['fileSize']); - $this->assertEquals('image/jpeg', $field->value->externalData['mimeType']); - $this->assertTrue($field->value->externalData['hasController']); - $this->assertTrue($field->value->externalData['autoplay']); - $this->assertTrue($field->value->externalData['loop']); - $this->assertEquals(23, $field->value->externalData['width']); - $this->assertEquals(42, $field->value->externalData['height']); - - $this->assertNull($field->value->data); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => null, - 'externalData' => [ - 'id' => null, - 'inputUri' => ($path = __DIR__ . '/_fixtures/image.png'), - 'fileName' => 'Blueish-Blue-Media.jpg', - 'fileSize' => filesize($path), - 'mimeType' => 'image/png', - 'hasController' => false, - 'autoplay' => false, - 'loop' => false, - 'width' => 0, - 'height' => 0, - 'uri' => $path, - ], - 'sortKey' => '', - ] - ); - } - - /** - * Asserts that the updated field data is loaded correct. - * - * Performs assertions on the loaded field after it has been updated, - * mainly checking that the $field->value->externalData is loaded - * correctly. If the loading of external data manipulates other aspects of - * $field, their correctness also needs to be asserted. Make sure you - * implement this method agnostic to the used SPI\Persistence - * implementation! - */ - public function assertUpdatedFieldDataCorrect(Field $field) - { - $this->assertNotNull($field->value->externalData); - - $this->assertFileExists( - $path = $this->getStorageDir() . '/' . $this->getStoragePrefix() . '/' . $field->value->externalData['id'], - "Stored file $path does not exists" - ); - - // Check old file removed before update - $this->assertCount( - 1, - glob(dirname($path) . '/*') - ); - - $this->assertEquals('Blueish-Blue-Media.jpg', $field->value->externalData['fileName']); - $this->assertEquals(filesize($path), $field->value->externalData['fileSize']); - $this->assertEquals('image/png', $field->value->externalData['mimeType']); - $this->assertFalse($field->value->externalData['hasController']); - $this->assertFalse($field->value->externalData['autoplay']); - $this->assertFalse($field->value->externalData['loop']); - $this->assertEquals(0, $field->value->externalData['width']); - $this->assertEquals(0, $field->value->externalData['height']); - - $this->assertNull($field->value->data); - } - - /** - * Can be overwritten to assert that additional data has been deleted. - * - * @param \eZ\Publish\SPI\Persistence\Content $content - */ - public function assertDeletedFieldDataCorrect(Content $content) - { - $iterator = new RecursiveIteratorIterator( - new RecursiveDirectoryIterator( - $this->getStorageDir(), - FileSystemIterator::KEY_AS_PATHNAME | FileSystemIterator::SKIP_DOTS | FileSystemIterator::CURRENT_AS_FILEINFO - ), - RecursiveIteratorIterator::CHILD_FIRST - ); - - foreach ($iterator as $path => $fileInfo) { - if ($fileInfo->isFile()) { - $this->fail( - sprintf( - 'Found undeleted file "%s"', - $path - ) - ); - } - } - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/RelationIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/RelationIntegrationTest.php deleted file mode 100644 index ff2f07d4e4..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/RelationIntegrationTest.php +++ /dev/null @@ -1,194 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; -use Ibexa\Core\Repository\Validator\TargetContentValidatorInterface; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class RelationIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezobjectrelation'; - } - - /** - * Get handler with required custom field types registered. - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - public function getCustomHandler() - { - $contentHandler = $this->createMock(Content\Handler::class); - $targetContentValidator = $this->createMock(TargetContentValidatorInterface::class); - - $fieldType = new FieldType\Relation\Type($contentHandler, $targetContentValidator); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezobjectrelation', - $fieldType, - new Legacy\Content\FieldValue\Converter\RelationConverter(), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints( - [ - 'fieldSettings' => [ - 'selectionMethod' => 0, - 'selectionRoot' => null, - 'selectionContentTypes' => [], - ], - ] - ); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - $fieldSettings = [ - 'selectionMethod' => 0, - 'selectionRoot' => null, - 'selectionContentTypes' => [], - ]; - - return [ - ['fieldType', 'ezobjectrelation'], - ['fieldTypeConstraints', new Content\FieldTypeConstraints(['fieldSettings' => $fieldSettings])], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => ['destinationContentId' => 1], - 'externalData' => null, - 'sortKey' => null, - ] - ); - } - - /** - * Asserts that the loaded field data is correct. - * - * Performs assertions on the loaded field, mainly checking that the - * $field->value->externalData is loaded correctly. If the loading of - * external data manipulates other aspects of $field, their correctness - * also needs to be asserted. Make sure you implement this method agnostic - * to the used SPI\Persistence implementation! - */ - public function assertLoadedFieldDataCorrect(Field $field) - { - $expected = $this->getInitialValue(); - $this->assertEquals( - $expected->externalData, - $field->value->externalData - ); - - $this->assertNotNull( - $field->value->data['destinationContentId'] - ); - $this->assertEquals( - $expected->data['destinationContentId'], - $field->value->data['destinationContentId'] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => ['destinationContentId' => 2], - 'externalData' => null, - 'sortKey' => null, - ] - ); - } - - /** - * Asserts that the updated field data is loaded correct. - * - * Performs assertions on the loaded field after it has been updated, - * mainly checking that the $field->value->externalData is loaded - * correctly. If the loading of external data manipulates other aspects of - * $field, their correctness also needs to be asserted. Make sure you - * implement this method agnostic to the used SPI\Persistence - * implementation! - */ - public function assertUpdatedFieldDataCorrect(Field $field) - { - $expected = $this->getUpdatedValue(); - $this->assertEquals( - $expected->externalData, - $field->value->externalData - ); - - $this->assertNotNull( - $field->value->data['destinationContentId'] - ); - $this->assertEquals( - $expected->data['destinationContentId'], - $field->value->data['destinationContentId'] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/RelationListIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/RelationListIntegrationTest.php deleted file mode 100644 index 5423c8a16c..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/RelationListIntegrationTest.php +++ /dev/null @@ -1,210 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; -use Ibexa\Core\Repository\Validator\TargetContentValidatorInterface; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class RelationListIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezobjectrelationlist'; - } - - /** - * Get handler with required custom field types registered. - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - public function getCustomHandler() - { - $contentHandler = $this->createMock(Content\Handler::class); - $targetContentValidator = $this->createMock(TargetContentValidatorInterface::class); - - $fieldType = new FieldType\RelationList\Type($contentHandler, $targetContentValidator); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezobjectrelationlist', - $fieldType, - new Legacy\Content\FieldValue\Converter\RelationListConverter( - $this->getDatabaseConnection() - ), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints( - [ - 'fieldSettings' => [ - 'selectionMethod' => 0, - 'selectionDefaultLocation' => '', - 'selectionContentTypes' => [], - ], - 'validators' => [ - 'RelationListValueValidator' => [ - 'selectionLimit' => 3, - ], - ], - ] - ); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - $fieldSettings = [ - 'selectionMethod' => 0, - 'selectionDefaultLocation' => '', - 'selectionContentTypes' => [], - ]; - - $validators = [ - 'RelationListValueValidator' => [ - 'selectionLimit' => 3, - ], - ]; - - return [ - ['fieldType', 'ezobjectrelationlist'], - ['fieldTypeConstraints', new Content\FieldTypeConstraints([ - 'fieldSettings' => $fieldSettings, - 'validators' => $validators, - ])], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => ['destinationContentIds' => [4]], - 'externalData' => null, - 'sortKey' => null, - ] - ); - } - - /** - * Asserts that the loaded field data is correct. - * - * Performs assertions on the loaded field, mainly checking that the - * $field->value->externalData is loaded correctly. If the loading of - * external data manipulates other aspects of $field, their correctness - * also needs to be asserted. Make sure you implement this method agnostic - * to the used SPI\Persistence implementation! - */ - public function assertLoadedFieldDataCorrect(Field $field) - { - $expected = $this->getInitialValue(); - $this->assertEquals( - $expected->externalData, - $field->value->externalData - ); - - $this->assertNotNull( - $field->value->data['destinationContentIds'] - ); - $this->assertEquals( - $expected->data['destinationContentIds'], - $field->value->data['destinationContentIds'] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => ['destinationContentIds' => [11]], - 'externalData' => null, - 'sortKey' => null, - ] - ); - } - - /** - * Asserts that the updated field data is loaded correct. - * - * Performs assertions on the loaded field after it has been updated, - * mainly checking that the $field->value->externalData is loaded - * correctly. If the loading of external data manipulates other aspects of - * $field, their correctness also needs to be asserted. Make sure you - * implement this method agnostic to the used SPI\Persistence - * implementation! - */ - public function assertUpdatedFieldDataCorrect(Field $field) - { - $expected = $this->getUpdatedValue(); - $this->assertEquals( - $expected->externalData, - $field->value->externalData - ); - - $this->assertNotNull( - $field->value->data['destinationContentIds'] - ); - $this->assertEquals( - $expected->data['destinationContentIds'], - $field->value->data['destinationContentIds'] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/SelectionIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/SelectionIntegrationTest.php deleted file mode 100644 index db4d77c707..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/SelectionIntegrationTest.php +++ /dev/null @@ -1,227 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; - -/** - * Integration test for legacy storage field types. - * - * @group integration - */ -class SelectionIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezselection'; - } - - /** - * Get handler with required custom field types registered. - * - * @return Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\Selection\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - $languageService = self::$container->get('ezpublish.api.service.language'); - - return $this->getHandler( - 'ezselection', - $fieldType, - new Legacy\Content\FieldValue\Converter\SelectionConverter($languageService), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints( - [ - 'validators' => null, - 'fieldSettings' => new FieldSettings( - [ - 'isMultiple' => true, - 'options' => [ - 1 => 'First', - 2 => 'Second', - 3 => 'Sindelfingen', - ], - 'multilingualOptions' => [ - 'ger-DE' => [ - 1 => 'Zuerst', - 2 => 'Zweite', - ], - 'eng-US' => [ - 1 => 'ML First', - 2 => 'ML Second', - 3 => 'ML Sindelfingen', - ], - ], - ] - ), - ] - ); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - ['fieldType', 'ezselection'], - [ - 'fieldTypeConstraints', - new Content\FieldTypeConstraints( - [ - 'validators' => null, - 'fieldSettings' => new FieldSettings( - [ - 'isMultiple' => true, - 'options' => [ - 1 => 'ML First', - 2 => 'ML Second', - 3 => 'ML Sindelfingen', - ], - 'multilingualOptions' => [ - 'ger-DE' => [ - 1 => 'Zuerst', - 2 => 'Zweite', - ], - 'eng-US' => [ - 1 => 'ML First', - 2 => 'ML Second', - 3 => 'ML Sindelfingen', - ], - ], - ] - ), - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => [1, 3], - 'externalData' => null, - 'sortKey' => '1-3', - ] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => [2], - 'externalData' => null, - 'sortKey' => '2', - ] - ); - } - - /** - * Performs the creation of the content type with a field of the field type - * under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\Type - */ - protected function createContentType() - { - $createStruct = new Content\Type\CreateStruct( - [ - 'name' => [ - 'eng-US' => 'Test', - 'ger-DE' => 'Test NOR', - ], - 'identifier' => 'test-' . $this->getTypeName(), - 'status' => 0, - 'creatorId' => 14, - 'created' => time(), - 'modifierId' => 14, - 'modified' => time(), - 'initialLanguageId' => 2, - 'remoteId' => 'abcdef', - ] - ); - - $createStruct->fieldDefinitions = [ - new Content\Type\FieldDefinition( - [ - 'name' => [ - 'eng-US' => 'Name', - 'ger-DE' => 'Name NOR', - ], - 'identifier' => 'name', - 'fieldGroup' => 'main', - 'position' => 1, - 'fieldType' => 'ezstring', - 'isTranslatable' => false, - 'isRequired' => true, - 'mainLanguageCode' => 'eng-US', - ] - ), - new Content\Type\FieldDefinition( - [ - 'name' => [ - 'eng-US' => 'Data', - 'ger-DE' => 'Data NOR', - ], - 'identifier' => 'data', - 'fieldGroup' => 'main', - 'position' => 2, - 'fieldType' => $this->getTypeName(), - 'isTranslatable' => false, - 'isRequired' => true, - 'fieldTypeConstraints' => $this->getTypeConstraints(), - 'mainLanguageCode' => 'eng-US', - ] - ), - ]; - - $handler = $this->getCustomHandler(); - $contentTypeHandler = $handler->contentTypeHandler(); - - return $contentTypeHandler->create($createStruct); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/TextBlockIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/TextBlockIntegrationTest.php deleted file mode 100644 index bac3ed35d4..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/TextBlockIntegrationTest.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class TextBlockIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'eztext'; - } - - /** - * Get handler with required custom field types registered. - * - * @return Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\TextBlock\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'eztext', - $fieldType, - new Legacy\Content\FieldValue\Converter\TextBlockConverter(), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The eztext field type does not have any special field definition - // properties - ['fieldType', 'eztext'], - [ - 'fieldTypeConstraints', - new Content\FieldTypeConstraints( - [ - 'fieldSettings' => new FieldType\FieldSettings( - [ - 'textRows' => 0, - ] - ), - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => 'Some longish text…', - 'externalData' => null, - 'sortKey' => 'some longish text', - ] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => 'A different longish text now…', - 'externalData' => null, - 'sortKey' => 'a different longish text now', - ] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/TextLineIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/TextLineIntegrationTest.php deleted file mode 100644 index d59ee1754a..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/TextLineIntegrationTest.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class TextLineIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezstring'; - } - - /** - * Get handler with required custom field types registered. - * - * @return Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\TextLine\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezstring', - $fieldType, - new Legacy\Content\FieldValue\Converter\TextLineConverter(), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezstring field type does not have any special field definition - // properties - ['fieldType', 'ezstring'], - [ - 'fieldTypeConstraints', - new Content\FieldTypeConstraints( - [ - 'validators' => [ - 'StringLengthValidator' => [ - 'minStringLength' => 0, - 'maxStringLength' => 0, - ], - ], - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => 'Some text…', - 'externalData' => null, - 'sortKey' => 'some text', - ] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => null, - 'externalData' => null, - 'sortKey' => '', - ] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/TimeIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/TimeIntegrationTest.php deleted file mode 100644 index a6769f5441..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/TimeIntegrationTest.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class TimeIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'eztime'; - } - - /** - * Get handler with required custom field types registered. - * - * @return \eZ\Publish\Core\Persistence\Legacy\Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\Time\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'eztime', - $fieldType, - new Legacy\Content\FieldValue\Converter\TimeConverter(), - new FieldType\NullStorage() - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The eztime field type does not have any special field definition - // properties - ['fieldType', 'eztime'], - [ - 'fieldTypeConstraints', - new Content\FieldTypeConstraints( - [ - 'fieldSettings' => new FieldType\FieldSettings( - [ - 'defaultType' => 0, - 'useSeconds' => false, - ] - ), - ] - ), - ], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => 3661, - 'externalData' => null, - 'sortKey' => 42, - ] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => 7322, - 'externalData' => null, - 'sortKey' => 23, - ] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/UrlIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/UrlIntegrationTest.php deleted file mode 100644 index e0f3883d1f..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/UrlIntegrationTest.php +++ /dev/null @@ -1,188 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class UrlIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezurl'; - } - - /** - * Get handler with required custom field types registered. - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - public function getCustomHandler() - { - $fieldType = new FieldType\Url\Type(); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezurl', - $fieldType, - new Legacy\Content\FieldValue\Converter\UrlConverter(), - new FieldType\Url\UrlStorage( - new FieldType\Url\UrlStorage\Gateway\DoctrineStorage( - $this->getDatabaseConnection() - ) - ) - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new Content\FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The ezurl field type does not have any special field definition - // properties - ['fieldType', 'ezurl'], - ['fieldTypeConstraints', new Content\FieldTypeConstraints()], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => [ - 'urlId' => null, - 'text' => 'Some awesome website', - ], - 'externalData' => 'http://example.com/sindelfingen', - 'sortKey' => null, - ] - ); - } - - /** - * Asserts that the loaded field data is correct. - * - * Performs assertions on the loaded field, mainly checking that the - * $field->value->externalData is loaded correctly. If the loading of - * external data manipulates other aspects of $field, their correctness - * also needs to be asserted. Make sure you implement this method agnostic - * to the used SPI\Persistence implementation! - */ - public function assertLoadedFieldDataCorrect(Field $field) - { - $expected = $this->getInitialValue(); - $this->assertEquals( - $expected->externalData, - $field->value->externalData - ); - - $this->assertNotNull( - $field->value->data['urlId'] - ); - $this->assertEquals( - $expected->data['text'], - $field->value->data['text'] - ); - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => [ - 'urlId' => null, - 'text' => 'An even more awesome website', - ], - 'externalData' => 'http://example.com/hubba', - 'sortKey' => null, - ] - ); - } - - /** - * Asserts that the updated field data is loaded correct. - * - * Performs assertions on the loaded field after it has been updated, - * mainly checking that the $field->value->externalData is loaded - * correctly. If the loading of external data manipulates other aspects of - * $field, their correctness also needs to be asserted. Make sure you - * implement this method agnostic to the used SPI\Persistence - * implementation! - */ - public function assertUpdatedFieldDataCorrect(Field $field) - { - $expected = $this->getUpdatedValue(); - $this->assertEquals( - $expected->externalData, - $field->value->externalData - ); - - $this->assertNotNull( - $field->value->data['urlId'] - ); - $this->assertEquals( - $expected->data['text'], - $field->value->data['text'] - ); - } -} diff --git a/eZ/Publish/SPI/Tests/FieldType/UserIntegrationTest.php b/eZ/Publish/SPI/Tests/FieldType/UserIntegrationTest.php deleted file mode 100644 index eac5def8ad..0000000000 --- a/eZ/Publish/SPI/Tests/FieldType/UserIntegrationTest.php +++ /dev/null @@ -1,217 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Tests\FieldType; - -use eZ\Publish\API\Repository\PasswordHashService; -use eZ\Publish\Core\FieldType; -use eZ\Publish\Core\Persistence\Legacy; -use eZ\Publish\Core\Repository\User\PasswordValidatorInterface; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\User; - -/** - * Integration test for legacy storage field types. - * - * This abstract base test case is supposed to be the base for field type - * integration tests. It basically calls all involved methods in the field type - * ``Converter`` and ``Storage`` implementations. Fo get it working implement - * the abstract methods in a sensible way. - * - * The following actions are performed by this test using the custom field - * type: - * - * - Create a new content type with the given field type - * - Load create content type - * - Create content object of new content type - * - Load created content - * - Copy created content - * - Remove copied content - * - * @group integration - */ -class UserIntegrationTest extends BaseIntegrationTest -{ - /** - * Get name of tested field type. - * - * @return string - */ - public function getTypeName() - { - return 'ezuser'; - } - - /** - * Get handler with required custom field types registered. - * - * @return \eZ\Publish\SPI\Persistence\Handler - */ - public function getCustomHandler() - { - $userHandler = $this->createMock(User\Handler::class); - $passwordHashGenerator = $this->createMock(PasswordHashService::class); - $passwordHashGenerator - ->method('getDefaultHashType') - ->willReturn(0); - $passwordValidator = $this->createMock(PasswordValidatorInterface::class); - - $fieldType = new FieldType\User\Type( - $userHandler, - $passwordHashGenerator, - $passwordValidator - ); - $fieldType->setTransformationProcessor($this->getTransformationProcessor()); - - return $this->getHandler( - 'ezuser', - $fieldType, - new Legacy\Content\FieldValue\Converter\NullConverter(), - new FieldType\User\UserStorage( - new FieldType\User\UserStorage\Gateway\DoctrineStorage( - $this->getDatabaseConnection() - ) - ) - ); - } - - /** - * Returns the FieldTypeConstraints to be used to create a field definition - * of the FieldType under test. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints - */ - public function getTypeConstraints() - { - return new FieldTypeConstraints(); - } - - /** - * Get field definition data values. - * - * This is a PHPUnit data provider - * - * @return array - */ - public function getFieldDefinitionData() - { - return [ - // The user field type does not have any special field definition - // properties - ['fieldType', 'ezuser'], - ['fieldTypeConstraints', new Content\FieldTypeConstraints()], - ]; - } - - /** - * Get initial field value. - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getInitialValue() - { - return new Content\FieldValue( - [ - 'data' => null, - 'externalData' => [ - 'login' => 'hans', - 'email' => 'hans@example.com', - 'passwordHash' => '*', - 'passwordHashType' => 0, - 'enabled' => true, - 'maxLogin' => 1000, - 'passwordUpdatedAt' => 1569229200, - ], - 'sortKey' => 'user', - ] - ); - } - - /** - * Asserts that the loaded field data is correct. - * - * Performs assertions on the loaded field, mainly checking that the - * $field->value->externalData is loaded correctly. If the loading of - * external data manipulates other aspects of $field, their correctness - * also needs to be asserted. Make sure you implement this method agnostic - * to the used SPI\Persistence implementation! - */ - public function assertLoadedFieldDataCorrect(Field $field) - { - $expectedValues = [ - 'hasStoredLogin' => true, - 'contentId' => self::$contentId, - 'login' => 'hans', - 'email' => 'hans@example.com', - 'passwordHash' => '*', - 'passwordHashType' => 0, - 'enabled' => true, - 'maxLogin' => 1000, - 'passwordUpdatedAt' => 1569229200, - ]; - - foreach ($expectedValues as $key => $value) { - $this->assertEquals($value, $field->value->externalData[$key]); - } - } - - /** - * Get update field value. - * - * Use to update the field - * - * @return \eZ\Publish\SPI\Persistence\Content\FieldValue - */ - public function getUpdatedValue() - { - return new Content\FieldValue( - [ - 'data' => null, - 'externalData' => [ - 'login' => 'changeLogin', - 'email' => 'changeEmail@ez.no', - 'passwordHash' => '*2', - 'passwordHashType' => 1, - 'enabled' => false, - 'maxLogin' => 1, - 'passwordUpdatedAt' => 1569229299, - ], - 'sortKey' => 'user', - ] - ); - } - - /** - * Asserts that the updated field data is loaded correct. - * - * Performs assertions on the loaded field after it has been updated, - * mainly checking that the $field->value->externalData is loaded - * correctly. If the loading of external data manipulates other aspects of - * $field, their correctness also needs to be asserted. Make sure you - * implement this method agnostic to the used SPI\Persistence - * implementation! - */ - public function assertUpdatedFieldDataCorrect(Field $field) - { - $expectedValues = [ - 'hasStoredLogin' => true, - 'contentId' => self::$contentId, - 'login' => 'changeLogin', - 'email' => 'changeEmail@ez.no', - 'passwordHash' => '*2', - 'passwordHashType' => 1, - 'enabled' => false, - 'maxLogin' => 1, - 'passwordUpdatedAt' => 1569229299, - ]; - - foreach ($expectedValues as $key => $value) { - $this->assertEquals($value, $field->value->externalData[$key]); - } - } -} diff --git a/eZ/Publish/SPI/Tests/Variation/InMemoryVariationHandler.php b/eZ/Publish/SPI/Tests/Variation/InMemoryVariationHandler.php deleted file mode 100644 index aff0e79244..0000000000 --- a/eZ/Publish/SPI/Tests/Variation/InMemoryVariationHandler.php +++ /dev/null @@ -1,28 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -declare(strict_types=1); - -namespace eZ\Publish\SPI\Tests\Variation; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\SPI\Variation\Values\Variation; -use eZ\Publish\SPI\Variation\VariationHandler; - -class InMemoryVariationHandler implements VariationHandler -{ - public function getVariation( - Field $field, - VersionInfo $versionInfo, - $variationName, - array $parameters = [] - ) { - return new Variation([ - 'uri' => $field->value . '-in-memory-test', - ]); - } -} diff --git a/eZ/Publish/SPI/User/IdentityAware.php b/eZ/Publish/SPI/User/IdentityAware.php deleted file mode 100644 index 582faf9177..0000000000 --- a/eZ/Publish/SPI/User/IdentityAware.php +++ /dev/null @@ -1,15 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\User; - -/** - * Interface for "user identity-aware" services. - */ -interface IdentityAware -{ - public function setIdentity(Identity $identity); -} diff --git a/eZ/Publish/SPI/Variation/Values/ImageVariation.php b/eZ/Publish/SPI/Variation/Values/ImageVariation.php deleted file mode 100644 index 5fae743161..0000000000 --- a/eZ/Publish/SPI/Variation/Values/ImageVariation.php +++ /dev/null @@ -1,53 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Variation\Values; - -/** - * @property-read int $width The width as number of pixels (for example "320") - * @property-read int $height The height as number of pixels (for example "256") - * @property-read string $name The name of the image alias (for example "original") - * @property-read mixed $info Extra information about the image, depending on the image type - * @property-read mixed $imageId - */ -class ImageVariation extends Variation -{ - /** - * The width as number of pixels (for example "320"). - * - * @var int - */ - protected $width; - - /** - * The height as number of pixels (for example "256"). - * - * @var int - */ - protected $height; - - /** - * The name of the image alias (for example "original"). - * - * @var string - */ - protected $name; - - /** - * Contains extra information about the image, depending on the image type. - * It will typically contain EXIF information from digital cameras or information about animated GIFs. - * If there is no information, the info will be a boolean FALSE. - * - * Beware: This information may contain e.g. HTML, JavaScript, or PHP code, and should be treated like any - * other user-submitted data. Make sure it is properly escaped before use. - * - * @var mixed - */ - protected $info; - - /** @var mixed */ - protected $imageId; -} diff --git a/eZ/Publish/SPI/Variation/VariationHandler.php b/eZ/Publish/SPI/Variation/VariationHandler.php deleted file mode 100644 index f14ce32660..0000000000 --- a/eZ/Publish/SPI/Variation/VariationHandler.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php - -/** - * @copyright Copyright (C) Ibexa AS. All rights reserved. - * @license For full copyright and license information view LICENSE file distributed with this source code. - */ -namespace eZ\Publish\SPI\Variation; - -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; - -/** - * Interface for Variation services. - * A variation service allows to generate variation from a given content field/version info - * (i.e. image aliases, variations of a document - doc, pdf...). - */ -interface VariationHandler -{ - /** - * Returns a Variation object for $field's $variationName. - * This method is responsible to create the variation if needed. - * Variations might be applicable for images (aliases), documents... - * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo - * @param string $variationName - * @param array $parameters Hash of arbitrary parameters useful to generate the variation - * - * @return \eZ\Publish\SPI\Variation\Values\Variation - */ - public function getVariation(Field $field, VersionInfo $versionInfo, $variationName, array $parameters = []); -} diff --git a/phpstan-baseline-7.4.neon b/phpstan-baseline-7.4.neon new file mode 100644 index 0000000000..77a0351af0 --- /dev/null +++ b/phpstan-baseline-7.4.neon @@ -0,0 +1,491 @@ +parameters: + ignoreErrors: + - + message: "#^Parameter \\#1 \\$input of function array_keys expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/StorageConnectionFactory.php + + - + message: "#^Parameter \\#1 \\$input of function array_filter expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\> given\\.$#" + count: 1 + path: src/bundle/Core/Command/CleanupVersionsCommand.php + + - + message: "#^Parameter \\#1 \\$input of function array_slice expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\> given\\.$#" + count: 1 + path: src/bundle/Core/Command/CleanupVersionsCommand.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, array\\|iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\> given\\.$#" + count: 1 + path: src/bundle/Core/Command/CleanupVersionsCommand.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\> given\\.$#" + count: 1 + path: src/bundle/Core/Command/CleanupVersionsCommand.php + + - + message: "#^Parameter \\#2 \\$str of function fwrite expects string, string\\|false given\\.$#" + count: 1 + path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php + + - + message: "#^Parameter \\#2 \\$parameters of function call_user_func_array expects array\\<int, mixed\\>, array\\<int\\|string, array\\<int, Symfony\\\\Component\\\\DependencyInjection\\\\Reference\\>\\> given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/ConsoleCacheWarmupPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\InjectEntityManagerMappingsPass\\:\\:getEntityMapForConfigurationService\\(\\) should return array but returns array\\|false\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Parameter \\#2 \\.\\.\\.\\$args of function array_merge expects array, Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ChainConfigResolver.php + + - + message: "#^Parameter \\#1 \\$str of function strtolower expects string, string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Parameter \\#1 \\$stack of function array_shift expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Languages.php + + - + message: "#^Parameter \\#1 \\$arr1 of function array_merge expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Parameter \\#1 \\$arr1 of function array_merge expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Parameter \\#3 \\$length of function substr expects int, int\\<0, max\\>\\|false given\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentPreviewContext.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\> given\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^PHPDoc tag @throws with type Psr\\\\Cache\\\\InvalidArgumentException is not subtype of Throwable$#" + count: 1 + path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php + + - + message: "#^Parameter \\#2 \\$str of function fwrite expects string, string\\|false given\\.$#" + count: 1 + path: src/bundle/Core/Imagine/IORepositoryResolver.php + + - + message: "#^Parameter \\#1 \\$fp of function fclose expects resource, resource\\|false given\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Parameter \\#2 \\$dest of function stream_copy_to_stream expects resource, resource\\|false given\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Parameter \\#3 \\$count of class LimitIterator constructor expects int, int\\|null given\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/BinaryFileLister.php + + - + message: "#^Parameter \\#3 \\$length of function substr expects int, int\\<0, max\\>\\|false given\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/FileRowReader/LegacyStorageFileRowReader.php + + - + message: "#^Parameter \\#3 \\$count of class LimitIterator constructor expects int, int\\|null given\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/ImageFileLister.php + + - + message: "#^Parameter \\#1 \\$input of function array_keys expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/LegacySearchEngine/ApiLoader/ConnectionFactory.php + + - + message: "#^Parameter \\#1 \\$input of function array_filter expects array, array\\<int, string\\>\\|false given\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php + + - + message: "#^Parameter \\#1 \\$object_or_string of function is_a expects object\\|string, string\\|null given\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/FieldTypeRegistryPass.php + + - + message: "#^Parameter \\#1 \\$object_or_string of function is_subclass_of expects object\\|string, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPass.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/Author/SearchField.php + + - + message: "#^Parameter \\#3 \\$length of function substr expects int, int\\<0, max\\>\\|false given\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Parameter \\#3 \\$length of function substr expects int, int\\<0, max\\>\\|false given\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/PathGenerator/LegacyPathGenerator.php + + - + message: "#^Parameter \\#1 \\$arr1 of function array_merge expects array, array\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Parameter \\#1 \\$input of function array_flip expects array\\<int\\|string\\>, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/Selection/SearchField.php + + - + message: "#^Parameter \\#1 \\$str of function mb_substr expects string, string\\|false given\\.$#" + count: 1 + path: src/lib/FieldType/TextBlock/SearchField.php + + - + message: "#^Parameter \\#1 \\$str of function mb_substr expects string, string\\|false given\\.$#" + count: 1 + path: src/lib/FieldType/TextBlock/Type.php + + - + message: "#^Parameter \\#1 \\$input of function array_keys expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\> given\\.$#" + count: 1 + path: src/lib/Limitation/LanguageLimitationType.php + + - + message: "#^Parameter \\#1 \\$input of function array_keys expects array, array\\|ArrayObject\\<\\(int\\|string\\), mixed\\>\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Component/Serializer/AbstractPropertyWhitelistNormalizer.php + + - + message: "#^Parameter \\#1 \\$input of function array_keys expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo\\> given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/RelationList/ParameterProvider.php + + - + message: "#^Parameter \\#1 \\$arr1 of function array_merge expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\> given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/UrlAlias.php + + - + message: "#^Parameter \\#2 \\.\\.\\.\\$args of function array_merge expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\> given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/UrlAlias.php + + - + message: "#^PHPDoc tag @throws with type Psr\\\\Cache\\\\InvalidArgumentException is not subtype of Throwable$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^PHPDoc tag @throws with type Psr\\\\Cache\\\\CacheException is not subtype of Throwable$#" + count: 1 + path: src/lib/Persistence/Cache/SettingHandler.php + + - + message: "#^PHPDoc tag @throws with type Psr\\\\Cache\\\\InvalidArgumentException is not subtype of Throwable$#" + count: 2 + path: src/lib/Persistence/Cache/SettingHandler.php + + - + message: "#^PHPDoc tag @throws with type Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\BadStateException\\|Psr\\\\Cache\\\\InvalidArgumentException is not subtype of Throwable$#" + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + + - + message: "#^PHPDoc tag @throws with type Psr\\\\Cache\\\\InvalidArgumentException is not subtype of Throwable$#" + count: 1 + path: src/lib/Persistence/Cache/UrlWildcardHandler.php + + - + message: "#^Parameter \\#2 \\$pieces of function implode expects array, array\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php + + - + message: "#^Parameter \\#2 \\$pieces of function implode expects array, array\\|float\\|int\\<min, \\-1\\>\\|int\\<1, max\\>\\|string\\|true given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php + + - + message: "#^Cannot access property \\$ownerDocument on DOMElement\\|false\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Parameter \\#1 \\$input of class Ibexa\\\\Core\\\\FieldType\\\\FieldSettings constructor expects array\\|object, array\\|null given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverter.php + + - + message: "#^Parameter \\#1 \\$input of function array_keys expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\> given\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/Language/MaskGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:filterOriginalAliases\\(\\) should return array but returns array\\|false\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Parameter \\#1 \\$string of function strlen expects string, string\\|false\\|null given\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php + + - + message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|false\\|null given\\.$#" + count: 2 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php + + - + message: "#^Parameter \\#2 \\$subject of function preg_match expects string, string\\|false\\|null given\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php + + - + message: "#^Parameter \\#1 \\$ascii of function chr expects int, float\\|int given\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Parameter \\#1 \\$hexadecimal_number of function hexdec expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Parameter \\#1 \\$str of function md5 expects string, string\\|false given\\.$#" + count: 4 + path: src/lib/Repository/Mapper/ContentMapper.php + + - + message: "#^Parameter \\#1 \\$autoload_function of function spl_autoload_register expects callable\\(string\\)\\: void, ProxyManager\\\\Autoloader\\\\AutoloaderInterface given\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyGenerator.php + + - + message: "#^Parameter \\#2 \\$arr2 of function array_diff expects array, array\\|null given\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\> given\\.$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^Parameter \\#1 \\$array of function reset expects array\\|object, null given\\.$#" + count: 3 + path: src/lib/Search/Common/FieldNameResolver.php + + - + message: "#^Parameter \\#1 \\$input of function array_keys expects array, string given\\.$#" + count: 3 + path: src/lib/Search/Common/FieldNameResolver.php + + - + message: "#^Parameter \\#1 \\$str of function trim expects string, bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Ancestor.php + + - + message: "#^Parameter \\#1 \\$str of function trim expects string, bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Ancestor.php + + - + message: "#^Parameter \\#1 \\$object of method ReflectionProperty\\:\\:getValue\\(\\) expects object, object\\|null given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPassTest.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Parameter \\#2 \\.\\.\\.\\$args of function array_merge_recursive expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Parameter \\#1 \\$str of function trim expects string, string\\|null given\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/BackwardCompatibleCommandListenerTest.php + + - + message: "#^Parameter \\#2 \\$start of function substr expects int, int\\<1, max\\>\\|false given\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Parameter \\#3 \\$length of function substr expects int, int\\<0, max\\>\\|false given\\.$#" + count: 1 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Parameter \\#1 \\$fp of function fclose expects resource, resource\\|false given\\.$#" + count: 1 + path: tests/integration/Core/IO/BinarydataHandler/FlysystemTest.php + + - + message: "#^Parameter \\#1 \\$str1 of function strcmp expects string, bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Parameter \\#2 \\$str2 of function strcmp expects string, bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\> given\\.$#" + count: 4 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Parameter \\#1 \\$array_arg of function usort expects TArray of array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\> given\\.$#" + count: 2 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$input of function array_keys expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$input of function array_values expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$str of function md5 expects string, float given\\.$#" + count: 3 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\> given\\.$#" + count: 2 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceMaximumSupportedLanguagesTest.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\> given\\.$#" + count: 2 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Parameter \\#1 \\$input of function array_filter expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#1 \\$input of function array_keys expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\> given\\.$#" + count: 3 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#1 \\$input of function array_filter expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21798Test.php + + - + message: "#^Parameter \\#1 \\$arr1 of function array_merge expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleAssignment\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupRoleAssignment\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserRoleAssignment\\> given\\.$#" + count: 3 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Parameter \\#1 \\$str of function md5 expects string, int\\<1, max\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/BaseLimitationTest.php + + - + message: "#^Parameter \\#1 \\$str of function md5 expects string, float given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/UserWrappedTest.php + + - + message: "#^Parameter \\#1 \\$function of function call_user_func_array expects callable\\(\\)\\: mixed, array\\{mixed, string\\} given\\.$#" + count: 3 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Parameter \\#1 \\$function of function call_user_func_array expects callable\\(\\)\\: mixed, array\\{mixed, string\\} given\\.$#" + count: 3 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Parameter \\#1 \\$function of function call_user_func_array expects callable\\(\\)\\: mixed, array\\{mixed, 'publish'\\} given\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentTypeHandlerTest.php + + - + message: "#^Parameter \\#1 \\$arr1 of function array_merge expects array, iterable given\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php + + - + message: "#^Parameter \\#2 \\.\\.\\.\\$args of function array_merge expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\CriterionQueryBuilder\\> given\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php + + - + message: "#^Parameter \\#1 \\$var of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\> given\\.$#" + count: 12 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Parameter \\#1 \\$array_arg of function key expects array\\|object, string given\\.$#" + count: 4 + path: tests/lib/Search/FieldNameResolverTest.php diff --git a/phpstan-baseline-gte-8.0.neon b/phpstan-baseline-gte-8.0.neon new file mode 100644 index 0000000000..c3cde9014c --- /dev/null +++ b/phpstan-baseline-gte-8.0.neon @@ -0,0 +1,461 @@ +parameters: + ignoreErrors: + - + message: "#^Parameter \\#1 \\$array of function array_keys expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/StorageConnectionFactory.php + + - + message: "#^Parameter \\#1 \\$array of function array_filter expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\> given\\.$#" + count: 1 + path: src/bundle/Core/Command/CleanupVersionsCommand.php + + - + message: "#^Parameter \\#1 \\$array of function array_slice expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\> given\\.$#" + count: 1 + path: src/bundle/Core/Command/CleanupVersionsCommand.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, array\\|iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\> given\\.$#" + count: 1 + path: src/bundle/Core/Command/CleanupVersionsCommand.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\> given\\.$#" + count: 1 + path: src/bundle/Core/Command/CleanupVersionsCommand.php + + - + message: "#^Parameter \\#2 \\$data of function fwrite expects string, string\\|false given\\.$#" + count: 1 + path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php + + - + message: "#^Parameter \\#2 \\.\\.\\.\\$arrays of function array_merge expects array, Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ChainConfigResolver.php + + - + message: "#^Parameter \\#1 \\$string of function strtolower expects string, string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Parameter \\#1 \\$array of function array_shift expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Languages.php + + - + message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Parameter \\#3 \\$length of function substr expects int\\|null, int\\<0, max\\>\\|false given\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentPreviewContext.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\> given\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Parameter \\#2 \\$data of function fwrite expects string, string\\|false given\\.$#" + count: 1 + path: src/bundle/Core/Imagine/IORepositoryResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\Handler\\\\HTTPHandler\\:\\:createCurlHandlerForUrl\\(\\) should return resource but returns CurlHandle\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/HTTPHandler.php + + - + message: "#^Parameter \\#1 \\$handle of function curl_getinfo expects CurlHandle, resource given\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/HTTPHandler.php + + - + message: "#^Parameter \\#2 \\$handle of function curl_multi_add_handle expects CurlHandle, resource given\\.$#" + count: 2 + path: src/bundle/Core/URLChecker/Handler/HTTPHandler.php + + - + message: "#^Parameter \\#1 \\$stream of function fclose expects resource, resource\\|false given\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Parameter \\#2 \\$to of function stream_copy_to_stream expects resource, resource\\|false given\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Parameter \\#3 \\$limit of class LimitIterator constructor expects int, int\\|null given\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/BinaryFileLister.php + + - + message: "#^Parameter \\#3 \\$length of function substr expects int\\|null, int\\<0, max\\>\\|false given\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/FileRowReader/LegacyStorageFileRowReader.php + + - + message: "#^Parameter \\#3 \\$limit of class LimitIterator constructor expects int, int\\|null given\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/ImageFileLister.php + + - + message: "#^Parameter \\#1 \\$array of function array_keys expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/LegacySearchEngine/ApiLoader/ConnectionFactory.php + + - + message: "#^Parameter \\#1 \\$array of function array_filter expects array, array\\<int, string\\>\\|false given\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php + + - + message: "#^Parameter \\#1 \\$object_or_class of function is_a expects object\\|string, string\\|null given\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/FieldTypeRegistryPass.php + + - + message: "#^Parameter \\#1 \\$object_or_class of function is_subclass_of expects object\\|string, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPass.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/Author/SearchField.php + + - + message: "#^Parameter \\#3 \\$length of function substr expects int\\|null, int\\<0, max\\>\\|false given\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Parameter \\#3 \\$length of function substr expects int\\|null, int\\<0, max\\>\\|false given\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/PathGenerator/LegacyPathGenerator.php + + - + message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, array\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Parameter \\#1 \\$array of function array_flip expects array\\<int\\|string\\>, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/Selection/SearchField.php + + - + message: "#^Parameter \\#1 \\$string of function mb_substr expects string, string\\|false given\\.$#" + count: 1 + path: src/lib/FieldType/TextBlock/SearchField.php + + - + message: "#^Parameter \\#1 \\$string of function mb_substr expects string, string\\|false given\\.$#" + count: 1 + path: src/lib/FieldType/TextBlock/Type.php + + - + message: "#^Parameter \\#1 \\$array of function array_keys expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\> given\\.$#" + count: 1 + path: src/lib/Limitation/LanguageLimitationType.php + + - + message: "#^Parameter \\#1 \\$array of function array_keys expects array, array\\|ArrayObject\\<\\(int\\|string\\), mixed\\>\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Component/Serializer/AbstractPropertyWhitelistNormalizer.php + + - + message: "#^Parameter \\#1 \\$array of function array_keys expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo\\> given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/RelationList/ParameterProvider.php + + - + message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\> given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/UrlAlias.php + + - + message: "#^Parameter \\#2 \\.\\.\\.\\$arrays of function array_merge expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\> given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/UrlAlias.php + + - + message: "#^Parameter \\#2 \\$array of function implode expects array\\|null, array\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php + + - + message: "#^Parameter \\#2 \\$array of function implode expects array\\|null, array\\|float\\|int\\<min, \\-1\\>\\|int\\<1, max\\>\\|string\\|true given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php + + - + message: "#^Parameter \\#1 \\$array of class Ibexa\\\\Core\\\\FieldType\\\\FieldSettings constructor expects array\\|object, array\\|null given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverter.php + + - + message: "#^Parameter \\#1 \\$array of function array_keys expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\> given\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/Language/MaskGenerator.php + + - + message: "#^Parameter \\#1 \\$string of function strlen expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php + + - + message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|null given\\.$#" + count: 2 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php + + - + message: "#^Parameter \\#2 \\$subject of function preg_match expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php + + - + message: "#^Parameter \\#1 \\$codepoint of function chr expects int, float\\|int given\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Parameter \\#1 \\$hex_string of function hexdec expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Parameter \\#1 \\$string of function md5 expects string, string\\|false given\\.$#" + count: 4 + path: src/lib/Repository/Mapper/ContentMapper.php + + - + message: "#^Parameter \\#1 \\$callback of function spl_autoload_register expects \\(callable\\(string\\)\\: void\\)\\|null, ProxyManager\\\\Autoloader\\\\AutoloaderInterface given\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyGenerator.php + + - + message: "#^Parameter \\#2 \\.\\.\\.\\$arrays of function array_diff expects array, array\\|null given\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\> given\\.$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^Parameter \\#1 \\$array of function array_keys expects array, string given\\.$#" + count: 3 + path: src/lib/Search/Common/FieldNameResolver.php + + - + message: "#^Parameter \\#1 \\$string of function trim expects string, bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Ancestor.php + + - + message: "#^Parameter \\#1 \\$string of function trim expects string, bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Ancestor.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Parameter \\#2 \\.\\.\\.\\$arrays of function array_merge_recursive expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Parameter \\#1 \\$string of function trim expects string, string\\|null given\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/BackwardCompatibleCommandListenerTest.php + + - + message: "#^Parameter \\#2 \\$offset of function substr expects int, int\\<1, max\\>\\|false given\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Parameter \\#3 \\$length of function substr expects int\\|null, int\\<0, max\\>\\|false given\\.$#" + count: 1 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Parameter \\#1 \\$stream of function fclose expects resource, resource\\|false given\\.$#" + count: 1 + path: tests/integration/Core/IO/BinarydataHandler/FlysystemTest.php + + - + message: "#^Parameter \\#1 \\$string1 of function strcmp expects string, bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Parameter \\#2 \\$string2 of function strcmp expects string, bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\> given\\.$#" + count: 4 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Parameter \\#1 \\$array of function array_keys expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$array of function array_values expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$array of function usort expects TArray of array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\> given\\.$#" + count: 2 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$string of function md5 expects string, float given\\.$#" + count: 3 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\> given\\.$#" + count: 2 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceMaximumSupportedLanguagesTest.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\> given\\.$#" + count: 2 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Parameter \\#1 \\$array of function array_filter expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#1 \\$array of function array_keys expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\> given\\.$#" + count: 3 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#1 \\$array of function array_filter expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21798Test.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleAssignment\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupRoleAssignment\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserRoleAssignment\\> given\\.$#" + count: 3 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Parameter \\#1 \\$class of class ReflectionProperty constructor expects object\\|string, class\\-string\\|false given\\.$#" + count: 4 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Parameter \\#1 \\$class of class ReflectionProperty constructor expects object\\|string, class\\-string\\|false given\\.$#" + count: 4 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Parameter \\#1 \\$string of function md5 expects string, int\\<1, max\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/BaseLimitationTest.php + + - + message: "#^Parameter \\#1 \\$string of function md5 expects string, float given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/UserWrappedTest.php + + - + message: "#^Parameter \\#1 \\$callback of function call_user_func_array expects callable\\(\\)\\: mixed, array\\{mixed, string\\} given\\.$#" + count: 3 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Parameter \\#1 \\$callback of function call_user_func_array expects callable\\(\\)\\: mixed, array\\{mixed, string\\} given\\.$#" + count: 3 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Parameter \\#1 \\$callback of function call_user_func_array expects callable\\(\\)\\: mixed, array\\{mixed, 'publish'\\} given\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentTypeHandlerTest.php + + - + message: "#^Parameter \\#1 \\.\\.\\.\\$arrays of function array_merge expects array, iterable given\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php + + - + message: "#^Parameter \\#2 \\.\\.\\.\\$arrays of function array_merge expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\CriterionQueryBuilder\\> given\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php + + - + message: "#^Parameter \\#1 \\$value of function count expects array\\|Countable, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\> given\\.$#" + count: 12 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Trying to invoke Closure\\|null but it might not be a callable\\.$#" + count: 2 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Parameter \\#1 \\$array of function key expects array\\|object, string given\\.$#" + count: 4 + path: tests/lib/Search/FieldNameResolverTest.php diff --git a/phpstan-baseline-lte-8.1.neon b/phpstan-baseline-lte-8.1.neon new file mode 100644 index 0000000000..dd1038f24c --- /dev/null +++ b/phpstan-baseline-lte-8.1.neon @@ -0,0 +1,6 @@ +parameters: + ignoreErrors: + - + message: "#^Class SensitiveParameterValue not found\\.$#" + count: 1 + path: tests/lib/Repository/User/PasswordHashServiceTest.php diff --git a/phpstan-baseline-lte-8.2.neon b/phpstan-baseline-lte-8.2.neon new file mode 100644 index 0000000000..af2834b3c0 --- /dev/null +++ b/phpstan-baseline-lte-8.2.neon @@ -0,0 +1,21 @@ +parameters: + ignoreErrors: + - + message: "#^Offset 'uri' does not exist on array\\{timed_out\\: bool, blocked\\: bool, eof\\: bool, unread_bytes\\: int, stream_type\\: string, wrapper_type\\: string, wrapper_data\\: mixed, mode\\: string, \\.\\.\\.\\}\\.$#" + count: 1 + path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php + + - + message: "#^Offset 'uri' does not exist on array\\{timed_out\\: bool, blocked\\: bool, eof\\: bool, unread_bytes\\: int, stream_type\\: string, wrapper_type\\: string, wrapper_data\\: mixed, mode\\: string, \\.\\.\\.\\}\\.$#" + count: 1 + path: src/bundle/Core/Imagine/IORepositoryResolver.php + + - + message: "#^Offset 'uri' does not exist on array\\{timed_out\\: bool, blocked\\: bool, eof\\: bool, unread_bytes\\: int, stream_type\\: string, wrapper_type\\: string, wrapper_data\\: mixed, mode\\: string, \\.\\.\\.\\}\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProvider/GenericProvider.php + + - + message: "#^Offset 'uri' does not exist on array\\{timed_out\\: bool, blocked\\: bool, eof\\: bool, unread_bytes\\: int, stream_type\\: string, wrapper_type\\: string, wrapper_data\\: mixed, mode\\: string, \\.\\.\\.\\}\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProvider/RemoteProvider.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 0803572c0d..43e60a3254 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1,512 +1,62666 @@ parameters: ignoreErrors: - - message: "#^Access to undefined constant static\\(eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\DependencyInjection\\\\Compiler\\\\ViewManagerPass\\)\\:\\:VIEW_PROVIDER_IDENTIFIER\\.$#" + message: "#^Cannot call method get\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/CacheFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryConfigurationProvider\\:\\:__construct\\(\\) has parameter \\$repositories with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/RepositoryConfigurationProvider.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryConfigurationProvider\\:\\:getDefaultRepositoryAlias\\(\\) should return string\\|null but returns int\\|string\\|null\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/RepositoryConfigurationProvider.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryConfigurationProvider\\:\\:getRepositoryConfig\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/RepositoryConfigurationProvider.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryConfigurationProvider\\:\\:\\$repositories type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/RepositoryConfigurationProvider.php + + - + message: "#^Cannot call method get\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/RepositoryFactory.php + + - + message: "#^Cannot call method getRepositoryConfig\\(\\) on object\\|null\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/RepositoryFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryFactory\\:\\:__construct\\(\\) has parameter \\$policyMap with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/RepositoryFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryFactory\\:\\:__construct\\(\\) has parameter \\$repositoryClass with no type specified\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/RepositoryFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryFactory\\:\\:buildRepository\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository but returns object\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/RepositoryFactory.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryFactory\\:\\:\\$policyMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/RepositoryFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\SearchEngineFactory\\:\\:registerSearchEngine\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/SearchEngineFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\SearchEngineIndexerFactory\\:\\:registerSearchEngineIndexer\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/SearchEngineIndexerFactory.php + + - + message: "#^Cannot call method get\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/StorageConnectionFactory.php + + - + message: "#^Cannot call method getParameter\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/StorageConnectionFactory.php + + - + message: "#^Cannot call method has\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/StorageConnectionFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageConnectionFactory\\:\\:getConnection\\(\\) should return Doctrine\\\\DBAL\\\\Connection but returns object\\|null\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/StorageConnectionFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageEngineFactory\\:\\:registerStorageEngine\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/ApiLoader/StorageEngineFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Cache\\\\Warmer\\\\ProxyCacheWarmer\\:\\:warmUp\\(\\) has parameter \\$cacheDir with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Cache/Warmer/ProxyCacheWarmer.php + + - + message: "#^Return type \\(void\\) of method Ibexa\\\\Bundle\\\\Core\\\\Cache\\\\Warmer\\\\ProxyCacheWarmer\\:\\:warmUp\\(\\) should be compatible with return type \\(array\\<string\\>\\) of method Symfony\\\\Component\\\\HttpKernel\\\\CacheWarmer\\\\WarmableInterface\\:\\:warmUp\\(\\)$#" + count: 2 + path: src/bundle/Core/Cache/Warmer/ProxyCacheWarmer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\CheckURLsCommand\\:\\:getTotalCount\\(\\) should return int but returns int\\|null\\.$#" + count: 1 + path: src/bundle/Core/Command/CheckURLsCommand.php + + - + message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\\\Connection\\:\\:createQueryBuilder\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Command/CleanupVersionsCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\CleanupVersionsCommand\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/CleanupVersionsCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\CleanupVersionsCommand\\:\\:getObjectsIds\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Command/CleanupVersionsCommand.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:ask\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Command/CopySubtreeCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\CopySubtreeCommand\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/CopySubtreeCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\CopySubtreeCommand\\:\\:getAllChildrenCount\\(\\) should return int but returns int\\|null\\.$#" + count: 1 + path: src/bundle/Core/Command/CopySubtreeCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\CopySubtreeCommand\\:\\:initialize\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/CopySubtreeCommand.php + + - + message: "#^PHPDoc tag @return with type int\\|null is not subtype of native type int\\.$#" + count: 1 + path: src/bundle/Core/Command/CopySubtreeCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\DebugConfigResolverCommand\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/DebugConfigResolverCommand.php + + - + message: "#^Parameter \\#1 \\$messages of method Symfony\\\\Component\\\\Console\\\\Output\\\\OutputInterface\\:\\:write\\(\\) expects iterable\\<string\\>\\|string, string\\|false given\\.$#" + count: 1 + path: src/bundle/Core/Command/DebugConfigResolverCommand.php + + - + message: "#^Parameter \\#1 \\$messages of method Symfony\\\\Component\\\\Console\\\\Output\\\\OutputInterface\\:\\:write\\(\\) expects iterable\\<string\\>\\|string, string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/Command/DebugConfigResolverCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\DeleteContentTranslationCommand\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/DeleteContentTranslationCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\DeleteContentTranslationCommand\\:\\:initialize\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/DeleteContentTranslationCommand.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Command\\\\DeleteContentTranslationCommand\\:\\:\\$questionHelper \\(Symfony\\\\Component\\\\Console\\\\Helper\\\\QuestionHelper\\) does not accept Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\.$#" + count: 1 + path: src/bundle/Core/Command/DeleteContentTranslationCommand.php + + - + message: "#^Ternary operator condition is always true\\.$#" + count: 1 + path: src/bundle/Core/Command/ExpireUserPasswordsCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\NormalizeImagesPathsCommand\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/NormalizeImagesPathsCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\NormalizeImagesPathsCommand\\:\\:getFinalNormalizedPath\\(\\) has parameter \\$imagePathsToNormalize with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Command/NormalizeImagesPathsCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\NormalizeImagesPathsCommand\\:\\:getImagePathsToNormalize\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Command/NormalizeImagesPathsCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\NormalizeImagesPathsCommand\\:\\:normalizeImagePaths\\(\\) has parameter \\$imagePathsToNormalize with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Command/NormalizeImagesPathsCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\NormalizeImagesPathsCommand\\:\\:normalizeImagePaths\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Command/NormalizeImagesPathsCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\NormalizeImagesPathsCommand\\:\\:updateImagePathsToNormalize\\(\\) has parameter \\$imageData with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/NormalizeImagesPathsCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\NormalizeImagesPathsCommand\\:\\:updateImagePathsToNormalize\\(\\) has parameter \\$imagePathsToNormalize with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Command/NormalizeImagesPathsCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\NormalizeImagesPathsCommand\\:\\:updateImagePathsToNormalize\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Command/NormalizeImagesPathsCommand.php + + - + message: "#^Parameter \\#3 \\$xml of method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\:\\:updateImageData\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: src/bundle/Core/Command/NormalizeImagesPathsCommand.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:ask\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Command/RegenerateUrlAliasesCommand.php + + - + message: "#^Cannot access offset int on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\>\\.$#" + count: 1 + path: src/bundle/Core/Command/RegenerateUrlAliasesCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\RegenerateUrlAliasesCommand\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/RegenerateUrlAliasesCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\RegenerateUrlAliasesCommand\\:\\:loadSpecificLocations\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\> but returns iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>\\.$#" + count: 1 + path: src/bundle/Core/Command/RegenerateUrlAliasesCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\RegenerateUrlAliasesCommand\\:\\:processLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/RegenerateUrlAliasesCommand.php + + - + message: "#^Parameter \\#2 \\$array of function array_map expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\> given\\.$#" + count: 1 + path: src/bundle/Core/Command/RegenerateUrlAliasesCommand.php + + - + message: "#^Parameter \\#3 \\$locationIds of method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\RegenerateUrlAliasesCommand\\:\\:regenerateSystemUrlAliases\\(\\) expects array\\<int\\>, array\\<int\\>\\|float\\|int\\|string\\|false\\|null given\\.$#" + count: 1 + path: src/bundle/Core/Command/RegenerateUrlAliasesCommand.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Search\\\\Common\\\\Indexer\\:\\:purge\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Command/ReindexCommand.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Search\\\\Common\\\\Indexer\\:\\:updateSearchIndex\\(\\)\\.$#" + count: 2 + path: src/bundle/Core/Command/ReindexCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\ReindexCommand\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/ReindexCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\ReindexCommand\\:\\:initialize\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/ReindexCommand.php + + - + message: "#^Parameter \\#1 \\$max of method Symfony\\\\Component\\\\Console\\\\Helper\\\\ProgressBar\\:\\:start\\(\\) expects int\\|null, float given\\.$#" + count: 1 + path: src/bundle/Core/Command/ReindexCommand.php + + - + message: "#^Parameter \\#2 \\$subject of function preg_match expects string, string\\|false given\\.$#" + count: 1 + path: src/bundle/Core/Command/ReindexCommand.php + + - + message: "#^Parameter \\#2 \\$subject of function preg_match_all expects string, string\\|false given\\.$#" + count: 1 + path: src/bundle/Core/Command/ReindexCommand.php + + - + message: "#^Parameter \\#3 \\$iterationCount of method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\ReindexCommand\\:\\:indexIncrementally\\(\\) expects int, float\\|int\\|string given\\.$#" + count: 1 + path: src/bundle/Core/Command/ReindexCommand.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Command\\\\ReindexCommand\\:\\:\\$phpPath \\(string\\) does not accept string\\|false\\.$#" + count: 1 + path: src/bundle/Core/Command/ReindexCommand.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Command\\\\ReindexCommand\\:\\:\\$phpPath \\(string\\) does not accept string\\|null\\.$#" + count: 1 + path: src/bundle/Core/Command/ReindexCommand.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$fields\\.$#" + count: 1 + path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:getVersionInfo\\(\\)\\.$#" + count: 2 + path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:ask\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\ResizeOriginalImagesCommand\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\ResizeOriginalImagesCommand\\:\\:initialize\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php + + - + message: "#^Parameter \\#1 \\$mimeType of method Symfony\\\\Component\\\\Mime\\\\MimeTypesInterface\\:\\:getExtensions\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php + + - + message: "#^Parameter \\#2 \\$mimeType of class Liip\\\\ImagineBundle\\\\Model\\\\Binary constructor expects string, string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/Command/ResizeOriginalImagesCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\SetSystemContentTypeGroupCommand\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/SetSystemContentTypeGroupCommand.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:ask\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\UpdateTimestampsToUTCCommand\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\UpdateTimestampsToUTCCommand\\:\\:dateStringToTimestamp\\(\\) has parameter \\$dateString with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\UpdateTimestampsToUTCCommand\\:\\:getTimestampBasedFields\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\UpdateTimestampsToUTCCommand\\:\\:processTimestamps\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\UpdateTimestampsToUTCCommand\\:\\:updateTimestampToUTC\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Command\\\\UpdateTimestampsToUTCCommand\\:\\:validateTimezone\\(\\) should return string but returns int\\.$#" + count: 1 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Parameter \\#1 \\$env of method Symfony\\\\Component\\\\Process\\\\Process\\:\\:setEnv\\(\\) expects array\\<string\\|Stringable\\>, array\\<string, int\\> given\\.$#" + count: 1 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Parameter \\#1 \\$messages of method Symfony\\\\Component\\\\Console\\\\Output\\\\OutputInterface\\:\\:writeln\\(\\) expects iterable\\<string\\>\\|string, int given\\.$#" + count: 1 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Parameter \\#2 \\$value of method Doctrine\\\\DBAL\\\\Query\\\\QueryBuilder\\:\\:set\\(\\) expects string, int given\\.$#" + count: 2 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Command\\\\UpdateTimestampsToUTCCommand\\:\\:\\$from \\(string\\) does not accept int\\.$#" + count: 1 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Command\\\\UpdateTimestampsToUTCCommand\\:\\:\\$phpPath \\(string\\) does not accept string\\|false\\.$#" + count: 1 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Command\\\\UpdateTimestampsToUTCCommand\\:\\:\\$to \\(string\\) does not accept int\\.$#" + count: 1 + path: src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Converter\\\\ContentParamConverter\\:\\:getSupportedClass\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Converter/ContentParamConverter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Converter\\\\ContentParamConverter\\:\\:loadValueObject\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Converter/ContentParamConverter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Converter\\\\LocationParamConverter\\:\\:getSupportedClass\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Converter/LocationParamConverter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Converter\\\\LocationParamConverter\\:\\:loadValueObject\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Converter/LocationParamConverter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Converter\\\\RepositoryParamConverter\\:\\:getSupportedClass\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Converter/RepositoryParamConverter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Converter\\\\RepositoryParamConverter\\:\\:loadValueObject\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Converter/RepositoryParamConverter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\BinaryContentDownloadPass\\:\\:addCall\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/BinaryContentDownloadPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\BinaryContentDownloadPass\\:\\:addCall\\(\\) has parameter \\$targetServiceName with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/BinaryContentDownloadPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\BinaryContentDownloadPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/BinaryContentDownloadPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ChainConfigResolverPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/ChainConfigResolverPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ChainRoutingPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/ChainRoutingPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ConsoleCacheWarmupPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/ConsoleCacheWarmupPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ConsoleCommandPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/ConsoleCommandPass.php + + - + message: "#^Argument of an invalid type array\\|bool\\|float\\|int\\|string\\|null supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\EntityManagerFactoryServiceLocatorPass\\:\\:getIbexaEntityManagers\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\FieldTypeParameterProviderRegistryPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\FragmentPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ImaginePass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/ImaginePass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ImaginePass\\:\\:processReduceNoiseFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/ImaginePass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ImaginePass\\:\\:processReduceNoiseFilter\\(\\) has parameter \\$driver with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/ImaginePass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ImaginePass\\:\\:processSwirlFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/ImaginePass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ImaginePass\\:\\:processSwirlFilter\\(\\) has parameter \\$driver with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/ImaginePass.php + + - + message: "#^Argument of an invalid type array\\|bool\\|float\\|int\\|string\\|null supplied for foreach, only iterables are supported\\.$#" + count: 2 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\InjectEntityManagerMappingsPass\\:\\:createMetadataDriverDefinition\\(\\) has parameter \\$driverPaths with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\InjectEntityManagerMappingsPass\\:\\:createMetadataDriverDefinition\\(\\) has parameter \\$driverType with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\InjectEntityManagerMappingsPass\\:\\:getEntityMapForConfigurationService\\(\\) has parameter \\$entityMappings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\InjectEntityManagerMappingsPass\\:\\:getEntityMapForConfigurationService\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\InjectEntityManagerMappingsPass\\:\\:getMappingDriverBundleConfigDefaults\\(\\) has parameter \\$bundle with generic class ReflectionClass but does not specify its types\\: T$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\InjectEntityManagerMappingsPass\\:\\:getMappingDriverBundleConfigDefaults\\(\\) has parameter \\$bundleConfig with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\InjectEntityManagerMappingsPass\\:\\:getMappingDriverBundleConfigDefaults\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\InjectEntityManagerMappingsPass\\:\\:prepareMappingDriverConfig\\(\\) has parameter \\$entityManagerConfig with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\InjectEntityManagerMappingsPass\\:\\:prepareMappingDriverConfig\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Parameter \\#1 \\$entityManagerConfig of method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\InjectEntityManagerMappingsPass\\:\\:prepareMappingDriverConfig\\(\\) expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Parameter \\#1 \\$entityMappings of method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\InjectEntityManagerMappingsPass\\:\\:getEntityMapForConfigurationService\\(\\) expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Parameter \\#1 \\$haystack of function strpos expects string, string\\|null given\\.$#" + count: 2 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Parameter \\#1 \\$path of function dirname expects string, string\\|false given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\NotificationRendererPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/NotificationRendererPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\PlaceholderProviderPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/PlaceholderProviderPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\RegisterSearchEngineIndexerPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/RegisterSearchEngineIndexerPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\RegisterSearchEnginePass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/RegisterSearchEnginePass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\RegisterStorageEnginePass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/RegisterStorageEnginePass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\RouterPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/RouterPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\SecurityPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/SecurityPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\SessionConfigurationPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/SessionConfigurationPass.php + + - + message: "#^Parameter \\#2 \\$id of method Symfony\\\\Component\\\\DependencyInjection\\\\ContainerBuilder\\:\\:setAlias\\(\\) expects string\\|Symfony\\\\Component\\\\DependencyInjection\\\\Alias, array\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/SessionConfigurationPass.php + + - + message: "#^Parameter \\#2 \\$subject of function preg_match expects string, array\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/SessionConfigurationPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\SlugConverterConfigurationPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\StorageConnectionPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/StorageConnectionPass.php + + - + message: "#^Constant Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\TranslationCollectorPass\\:\\:LOCALES_MAP type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\TranslationCollectorPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPass.php + + - + message: "#^Parameter \\#1 \\$kernelRootDir of class Ibexa\\\\Bundle\\\\Core\\\\Translation\\\\GlobCollector constructor expects string, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\URLHandlerPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/URLHandlerPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ViewProvidersPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Compiler/ViewProvidersPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\:\\:addHttpCacheSection\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\:\\:addImageMagickSection\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\:\\:addImagePlaceholderSection\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\:\\:addRepositoriesSection\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\:\\:addRouterSection\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\:\\:addSiteaccessSection\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\:\\:setSiteAccessConfigurationFilters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\:\\:setSiteAccessConfigurationFilters\\(\\) has parameter \\$filters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\:\\:\\$siteAccessConfigurationFilters \\(array\\<Ibexa\\\\Bundle\\\\Core\\\\SiteAccess\\\\SiteAccessConfigurationFilter\\>\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\:\\:\\$suggestionCollector is never read, only written\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\AbstractParser\\:\\:postMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/AbstractParser.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\AbstractParser\\:\\:preMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/AbstractParser.php + + - + message: "#^Cannot access an offset on array\\|Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ChainConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ChainConfigResolver\\:\\:addResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ChainConfigResolver.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ChainConfigResolver\\:\\:\\$resolvers \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface\\>\\) does not accept array\\<array\\<int, Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface\\>\\|Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface\\>\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ChainConfigResolver.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ChainConfigResolver\\:\\:\\$resolvers \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface\\>\\) does not accept array\\<array\\|Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface\\>\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ChainConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingParser\\:\\:matchDynamicSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParser.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingParser\\:\\:parseComplexSetting\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParser.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingParserInterface\\:\\:parseComplexSetting\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingValueResolver\\:\\:resolveSetting\\(\\) has parameter \\$argumentString with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolver.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$dynamicSettingName$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolver.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$dynamicSettingValue$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigBuilderInterface\\:\\:addConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigBuilderInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigBuilderInterface\\:\\:addConfig\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigBuilderInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigBuilderInterface\\:\\:addResource\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigBuilderInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParser\\:\\:__construct\\(\\) has parameter \\$configParsers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigParser.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParser\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigParser.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParser\\:\\:mapConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigParser.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParser\\:\\:mapConfig\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigParser.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParser\\:\\:postMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigParser.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParser\\:\\:preMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigParser.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParser\\:\\:setConfigParsers\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigParser.php + + - + message: "#^Cannot call method getParameter\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 7 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Cannot call method has\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Cannot call method hasParameter\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 8 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\:\\:__construct\\(\\) has parameter \\$groupsBySiteAccess with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\:\\:logTooEarlyLoadedListIfNeeded\\(\\) has parameter \\$paramName with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\:\\:logTooEarlyLoadedListIfNeeded\\(\\) should return string but empty return statement found\\.$#" + count: 4 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\:\\:logTooEarlyLoadedListIfNeeded\\(\\) should return string but return statement is missing\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\:\\:setUndefinedStrategy\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\:\\:warnAboutTooEarlyLoadedParams\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Offset 'function' on array\\{function\\: string, line\\?\\: int, file\\?\\: string, class\\?\\: class\\-string, type\\?\\: '\\-\\>'\\|'\\:\\:', args\\?\\: array, object\\?\\: object\\} in isset\\(\\) always exists and is not nullable\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Parameter \\#2 \\$haystack of function in_array expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\:\\:\\$groupsBySiteAccess type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\:\\:\\$tooEarlyLoadedList type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php + + - + message: "#^Cannot call method getParameter\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ContainerConfigResolver.php + + - + message: "#^Cannot call method hasParameter\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 2 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ContainerConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\ContainerConfigResolver\\:\\:resolveNamespaceAndScope\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ContainerConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\SiteAccessConfigResolver\\:\\:getParameterFromResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\SiteAccessConfigResolver\\:\\:resolveNamespaceAndScope\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\SiteAccessConfigResolver\\:\\:setDefaultNamespace\\(\\) has parameter \\$defaultNamespace with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessConfigResolver.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\SiteAccessConfigResolver\\:\\:\\$currentSiteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\SiteAccessGroupConfigResolver\\:\\:__construct\\(\\) has parameter \\$siteAccessGroups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\SiteAccessGroupConfigResolver\\:\\:getParameterFromResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\SiteAccessGroupConfigResolver\\:\\:getParameterFromResolverForGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\SiteAccessGroupConfigResolver\\:\\:isSiteAccessGroupScope\\(\\) has parameter \\$scope with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\StaticSiteAccessConfigResolver\\:\\:getParameterFromResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\FieldTypeParserInterface\\:\\:addFieldTypeSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/FieldTypeParserInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\AbstractFieldTypeParser\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/AbstractFieldTypeParser.php + + - + message: "#^Cannot access offset 'name' on array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Common\\:\\:addDatabaseConfigSuggestion\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Common\\:\\:addDatabaseConfigSuggestion\\(\\) has parameter \\$databaseConfig with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Common\\:\\:addDatabaseConfigSuggestion\\(\\) has parameter \\$sa with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Common\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Common\\:\\:mapConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Common\\:\\:mapConfig\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Common\\:\\:preMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Common\\:\\:setSuggestionCollector\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Content\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Content.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Content\\:\\:mapConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Content.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Content\\:\\:mapConfig\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Content.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\FieldType\\\\ImageAsset\\:\\:mapConfig\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/FieldType/ImageAsset.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\IO\\:\\:addComplexParametersDependencies\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\IO\\:\\:addComplexParametersDependencies\\(\\) has parameter \\$parameter with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\IO\\:\\:addComplexParametersDependencies\\(\\) has parameter \\$scope with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\IO\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\IO\\:\\:mapConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\IO\\:\\:mapConfig\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\IO\\:\\:postMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php + + - + message: "#^Parameter \\#1 \\$string of method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingParserInterface\\:\\:containsDynamicSettings\\(\\) expects string, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php + + - + message: "#^Parameter \\#1 \\$string of method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingParserInterface\\:\\:parseComplexSetting\\(\\) expects string, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php + + - + message: "#^Parameter \\#3 \\$container of method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\IO\\:\\:addComplexParametersDependencies\\(\\) expects Symfony\\\\Component\\\\DependencyInjection\\\\ContainerBuilder, Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface given\\.$#" + count: 3 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Image\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Image.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Image\\:\\:mapConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Image.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Image\\:\\:mapConfig\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Image.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Image\\:\\:preMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Image.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Image\\:\\:setSuggestionCollector\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Image.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Languages\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Languages.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Languages\\:\\:mapConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Languages.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Languages\\:\\:mapConfig\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Languages.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Languages\\:\\:postMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Languages.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Languages\\:\\:preMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Languages.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Languages\\:\\:\\$siteAccessesByLanguages has no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Languages.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\LocationView\\:\\:preMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/LocationView.php + + - + message: "#^Access to undefined constant static\\(Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Templates\\)\\:\\:INFO\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Templates.php + + - + message: "#^Access to undefined constant static\\(Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Templates\\)\\:\\:INFO_TEMPLATE_KEY\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Templates.php + + - + message: "#^Access to undefined constant static\\(Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Templates\\)\\:\\:NODE_KEY\\.$#" + count: 5 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Templates.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Templates\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Templates.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Templates\\:\\:mapConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Templates.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Templates\\:\\:mapConfig\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Templates.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Templates\\:\\:preMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/Templates.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\TwigVariablesParser\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/TwigVariablesParser.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\TwigVariablesParser\\:\\:mapConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/TwigVariablesParser.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\TwigVariablesParser\\:\\:mapConfig\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/TwigVariablesParser.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\UrlChecker\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/UrlChecker.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\UrlChecker\\:\\:mapConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/UrlChecker.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\UrlChecker\\:\\:mapConfig\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/UrlChecker.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\UserContentTypeIdentifier\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/UserContentTypeIdentifier.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\UserContentTypeIdentifier\\:\\:mapConfig\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/UserContentTypeIdentifier.php + + - + message: "#^Access to undefined constant static\\(Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\View\\)\\:\\:INFO\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/View.php + + - + message: "#^Access to undefined constant static\\(Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\View\\)\\:\\:NODE_KEY\\.$#" + count: 2 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/View.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\View\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/View.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\View\\:\\:mapConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/View.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\View\\:\\:mapConfig\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/View.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\View\\:\\:preMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Parser/View.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ParserInterface\\:\\:addSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/ParserInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationMapperInterface\\:\\:mapConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationMapperInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationMapperInterface\\:\\:mapConfig\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationMapperInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:__construct\\(\\) has parameter \\$namespace with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:__construct\\(\\) has parameter \\$siteAcccessNodeName with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:mapConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:mapConfig\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:mapConfigArray\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:mapConfigArray\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:mapSetting\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:mapSetting\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:setAvailableSiteAccessGroups\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:setAvailableSiteAccesses\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:setContextualizer\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:setGroupsBySiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:setGroupsBySiteAccess\\(\\) has parameter \\$groupsBySiteAccess with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:\\$availableSiteAccessGroups type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:\\$availableSiteAccesses type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:\\$groupsBySiteAccess type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:__construct\\(\\) has parameter \\$availableSiteAccessGroups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:__construct\\(\\) has parameter \\$availableSiteAccesses with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:__construct\\(\\) has parameter \\$groupsBySiteAccess with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:__construct\\(\\) has parameter \\$namespace with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:__construct\\(\\) has parameter \\$siteAccessNodeName with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:getAvailableSiteAccesses\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:getGroupsBySiteAccess\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:groupsArraySetting\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:groupsArraySetting\\(\\) has parameter \\$groups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:groupsArraySetting\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mapConfigArray\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mapConfigArray\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mapReservedScopeArray\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mapReservedScopeArray\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mapSetting\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mergeSettingsForSiteAccess\\(\\) has parameter \\$defaultSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mergeSettingsForSiteAccess\\(\\) has parameter \\$globalSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mergeSettingsForSiteAccess\\(\\) has parameter \\$groupsSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mergeSettingsForSiteAccess\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mergeSettingsForSiteAccess\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mergeSettingsForSiteAccessGroup\\(\\) has parameter \\$defaultSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mergeSettingsForSiteAccessGroup\\(\\) has parameter \\$globalSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mergeSettingsForSiteAccessGroup\\(\\) has parameter \\$scopeSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:mergeSettingsForSiteAccessGroup\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:setAvailableSiteAccesses\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:setAvailableSiteAccesses\\(\\) has parameter \\$availableSiteAccesses with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:setContainer\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:setContextualParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:setGroupsBySiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:setGroupsBySiteAccess\\(\\) has parameter \\$groupsBySiteAccess with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:setNamespace\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:setSiteAccessNodeName\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:\\$availableSiteAccessGroups type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:\\$availableSiteAccesses type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\Contextualizer\\:\\:\\$groupsBySiteAccess type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerInterface\\:\\:getAvailableSiteAccesses\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerInterface\\:\\:getGroupsBySiteAccess\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerInterface\\:\\:mapConfigArray\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerInterface\\:\\:mapConfigArray\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerInterface\\:\\:mapSetting\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerInterface\\:\\:setAvailableSiteAccesses\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerInterface\\:\\:setAvailableSiteAccesses\\(\\) has parameter \\$availableSiteAccesses with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerInterface\\:\\:setContainer\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerInterface\\:\\:setContextualParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerInterface\\:\\:setGroupsBySiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerInterface\\:\\:setGroupsBySiteAccess\\(\\) has parameter \\$groupsBySiteAccess with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerInterface\\:\\:setNamespace\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerInterface\\:\\:setSiteAccessNodeName\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\DynamicSettingParser\\:\\:parseDynamicSetting\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParser.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\DynamicSettingParserInterface\\:\\:parseDynamicSetting\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\HookableConfigurationMapperInterface\\:\\:postMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/HookableConfigurationMapperInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\HookableConfigurationMapperInterface\\:\\:preMap\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/HookableConfigurationMapperInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\Collector\\\\SuggestionCollector\\:\\:addSuggestion\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\Collector\\\\SuggestionCollectorAwareInterface\\:\\:setSuggestionCollector\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorAwareInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\Collector\\\\SuggestionCollectorInterface\\:\\:addSuggestion\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\ConfigSuggestion\\:\\:__construct\\(\\) has parameter \\$mandatory with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\ConfigSuggestion\\:\\:__construct\\(\\) has parameter \\$message with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\ConfigSuggestion\\:\\:__construct\\(\\) has parameter \\$suggestion with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\ConfigSuggestion\\:\\:getSuggestion\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\ConfigSuggestion\\:\\:setMandatory\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\ConfigSuggestion\\:\\:setMessage\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\ConfigSuggestion\\:\\:setSuggestion\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\ConfigSuggestion\\:\\:setSuggestion\\(\\) has parameter \\$suggestion with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\ConfigSuggestion\\:\\:\\$suggestion type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\Formatter\\\\SuggestionFormatterInterface\\:\\:format\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/Formatter/SuggestionFormatterInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\Formatter\\\\YamlSuggestionFormatter\\:\\:format\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Configuration/Suggestion/Formatter/YamlSuggestionFormatter.php + + - + message: "#^Binary operation \"\\.\" between array\\|bool\\|float\\|int\\|string\\|null and '/config/packages/dfs' results in an error\\.$#" + count: 2 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Binary operation \"\\.\" between array\\|bool\\|float\\|int\\|string\\|null and '/config/packages…' results in an error\\.$#" + count: 3 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Binary operation \"\\.\" between array\\|bool\\|float\\|int\\|string\\|null and '/vendor/ibexa/i18n…' results in an error\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Binary operation \"\\.\" between array\\|bool\\|float\\|int\\|string\\|null and non\\-falsy\\-string results in an error\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Expression on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:__construct\\(\\) has parameter \\$repositoryConfigParsers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:__construct\\(\\) has parameter \\$siteAccessConfigParsers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:addConfigParser\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:addDefaultSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:addDefaultSettings\\(\\) has parameter \\$files with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:addPolicyProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:addSiteAccessConfigurationFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:buildPolicyMap\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:getConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleCache\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleCache\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleHelpers\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleHelpers\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleImage\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleImage\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleLocale\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleLocale\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleRouting\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleRouting\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleSessionLoading\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleTemplating\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleUrlChecker\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleUrlChecker\\(\\) has parameter \\$config with no type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleUrlWildcards\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:handleUrlWildcards\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:load\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:prepend\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:prependTranslatorConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:registerImageMagickConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:registerImageMagickConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:registerOrmConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:registerRepositoriesConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:registerRepositoriesConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:registerSiteAccessConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:registerSiteAccessConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:registerUITranslationsConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:registerUrlAliasConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:registerUrlAliasConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:registerUrlWildcardsConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^PHPDoc tag @param for parameter \\$loader with type Symfony\\\\Component\\\\DependencyInjection\\\\Loader\\\\FileLoader is not subtype of native type Symfony\\\\Component\\\\DependencyInjection\\\\Loader\\\\YamlFileLoader\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Parameter \\#1 \\$search of function str_replace expects array\\|string, string\\|false\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Parameter \\#2 \\.\\.\\.\\$values of function sprintf expects bool\\|float\\|int\\|string\\|null, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:\\$defaultSettingsCollection type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtension\\:\\:\\$mainRepositoryConfigParser \\(Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\RepositoryConfigParser\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/IbexaCoreExtension.php + + - + message: "#^Cannot access offset \\(int\\|string\\) on array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilder.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\PoliciesConfigBuilder\\:\\:addConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilder.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\PoliciesConfigBuilder\\:\\:addConfig\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilder.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\PoliciesConfigBuilder\\:\\:addResource\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilder.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\PoliciesConfigBuilder\\:\\:policyExists\\(\\) has parameter \\$policyMap with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilder.php + + - + message: "#^Parameter \\#1 \\$policyMap of method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\PoliciesConfigBuilder\\:\\:policyExists\\(\\) expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilder.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\PolicyProviderInterface\\:\\:addPolicies\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Security/PolicyProvider/PolicyProviderInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\RepositoryPolicyProvider\\:\\:getFiles\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Security/PolicyProvider/RepositoryPolicyProvider.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\YamlPolicyProvider\\:\\:addPolicies\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Security/PolicyProvider/YamlPolicyProvider.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\YamlPolicyProvider\\:\\:getFiles\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Security/PolicyProvider/YamlPolicyProvider.php + + - + message: "#^Parameter \\#1 \\$input of static method Symfony\\\\Component\\\\Yaml\\\\Yaml\\:\\:parse\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: src/bundle/Core/DependencyInjection/Security/PolicyProvider/YamlPolicyProvider.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Entity\\\\EntityManagerFactory\\:\\:__construct\\(\\) has parameter \\$entityManagers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Entity/EntityManagerFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\BackgroundIndexingTerminateListener\\:\\:registerContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/BackgroundIndexingTerminateListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\BackgroundIndexingTerminateListener\\:\\:registerLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/BackgroundIndexingTerminateListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\BackgroundIndexingTerminateListener\\:\\:reindex\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/BackgroundIndexingTerminateListener.php + + - + message: "#^Cannot call method getName\\(\\) on Symfony\\\\Component\\\\Console\\\\Command\\\\Command\\|null\\.$#" + count: 1 + path: src/bundle/Core/EventListener/BackwardCompatibleCommandListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\CacheViewResponseListener\\:\\:configureCache\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/CacheViewResponseListener.php + + - + message: "#^Parameter \\#2 \\$values of method Symfony\\\\Component\\\\HttpFoundation\\\\ResponseHeaderBag\\:\\:set\\(\\) expects array\\<string\\>\\|string\\|null, int given\\.$#" + count: 1 + path: src/bundle/Core/EventListener/CacheViewResponseListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ConfigScopeListener\\:\\:__construct\\(\\) has parameter \\$configResolvers with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ConfigScopeListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ConfigScopeListener\\:\\:setViewProviders\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ConfigScopeListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ConfigScopeListener\\:\\:setViewProviders\\(\\) has parameter \\$viewProviders with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ConfigScopeListener.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ConfigScopeListener\\:\\:\\$configResolvers \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface\\>\\) does not accept iterable\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ConfigScopeListener.php + + - + message: "#^Cannot access property \\$matchingType on Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 2 + path: src/bundle/Core/EventListener/ConsoleCommandListener.php + + - + message: "#^Cannot access property \\$name on Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 3 + path: src/bundle/Core/EventListener/ConsoleCommandListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ConsoleCommandListener\\:\\:onConsoleCommand\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ConsoleCommandListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ConsoleCommandListener\\:\\:setDebug\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ConsoleCommandListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ConsoleCommandListener\\:\\:setDebug\\(\\) has parameter \\$debug with no type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ConsoleCommandListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ConsoleCommandListener\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ConsoleCommandListener.php + + - + message: "#^Parameter \\#1 \\$siteAccess of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\ScopeChangeEvent constructor expects Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess, Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null given\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ConsoleCommandListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ContentDownloadRouteReferenceListener\\:\\:configureOptions\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ContentDownloadRouteReferenceListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ContentDownloadRouteReferenceListener\\:\\:onRouteReferenceGeneration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ContentDownloadRouteReferenceListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ExceptionListener\\:\\:onKernelException\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ExceptionListener.php + + - + message: "#^Parameter \\#1 \\$exception of method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ExceptionListener\\:\\:getTranslatedMessage\\(\\) expects Exception, Ibexa\\\\Core\\\\Base\\\\Translatable&Throwable given\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ExceptionListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\IndexRequestListener\\:\\:onKernelRequestIndex\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/IndexRequestListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\OriginalRequestListener\\:\\:onKernelRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/OriginalRequestListener.php + + - + message: "#^Cannot access property \\$attributes on Symfony\\\\Component\\\\HttpFoundation\\\\Request\\|null\\.$#" + count: 1 + path: src/bundle/Core/EventListener/PreviewRequestListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\RejectExplicitFrontControllerRequestsListener\\:\\:onKernelRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/RejectExplicitFrontControllerRequestsListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListener\\:\\:__construct\\(\\) has parameter \\$defaultSiteAccess with no type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/RequestEventListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListener\\:\\:onKernelRequestForward\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/RequestEventListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListener\\:\\:onKernelRequestRedirect\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/RequestEventListener.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListener\\:\\:\\$configResolver is never read, only written\\.$#" + count: 1 + path: src/bundle/Core/EventListener/RequestEventListener.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListener\\:\\:\\$defaultSiteAccess is never read, only written\\.$#" + count: 1 + path: src/bundle/Core/EventListener/RequestEventListener.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListener\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) does not accept Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/bundle/Core/EventListener/RequestEventListener.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListener\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) in isset\\(\\) is not nullable\\.$#" + count: 2 + path: src/bundle/Core/EventListener/RequestEventListener.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListener\\:\\:\\$router is never read, only written\\.$#" + count: 1 + path: src/bundle/Core/EventListener/RequestEventListener.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:setExcludedUriPrefixes\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/EventListener/RoutingListener.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:setRootLocationId\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/EventListener/RoutingListener.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Routing\\\\RouterInterface\\:\\:setRootLocationId\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/EventListener/RoutingListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\RoutingListener\\:\\:onSiteAccessMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/RoutingListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\SessionInitByPostListener\\:\\:onSiteAccessMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/SessionInitByPostListener.php + + - + message: "#^Parameter \\#1 \\$id of method Symfony\\\\Component\\\\HttpFoundation\\\\Session\\\\SessionInterface\\:\\:setId\\(\\) expects string, bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/bundle/Core/EventListener/SessionInitByPostListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\SessionSetDynamicNameListener\\:\\:onSiteAccessMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/SessionSetDynamicNameListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListener\\:\\:generateViewParametersArray\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/EventListener/SiteAccessListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListener\\:\\:getViewParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/EventListener/SiteAccessListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ViewControllerListener\\:\\:getController\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ViewControllerListener.php + + - + message: "#^Parameter \\#1 \\$controller of method Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\ControllerEvent\\:\\:setController\\(\\) expects callable\\(\\)\\: mixed, \\(callable\\(\\)\\: mixed\\)\\|false given\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ViewControllerListener.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ViewControllerListener\\:\\:\\$eventDispatcher \\(Symfony\\\\Component\\\\EventDispatcher\\\\EventDispatcher\\) does not accept Symfony\\\\Component\\\\EventDispatcher\\\\EventDispatcherInterface\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ViewControllerListener.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ViewControllerListener\\:\\:\\$logger is never read, only written\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ViewControllerListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ViewRendererListener\\:\\:renderView\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventListener/ViewRendererListener.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\EventSubscriber\\\\CrowdinRequestLocaleSubscriber\\:\\:setInContextAcceptLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/EventSubscriber/CrowdinRequestLocaleSubscriber.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:createArticleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:createArticleDraft\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:createBasicArticle\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:createBasicArticle\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:createBasicFolder\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:createBasicFolder\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:createContent\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:createContentDraft\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:createContentDraft\\(\\) should return Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:createContentWithPath\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:getContentPath\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:getContentPath\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:getTitleFromPath\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:getTitleFromPath\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:mapContentPath\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:mapContentPath\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:publishDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\BasicContentContext\\:\\:\\$contentPaths has no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/BasicContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:getDefaultSiteaccessName\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:iExpectItToBeExecutedWithIt\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:iExpectItToBeExecutedWithTheDefaultOne\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:iExpectItToBeExecutedWithTheSiteaccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:iExpectItToBeExecutedWithTheSiteaccess\\(\\) has parameter \\$siteaccess with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:iRunAConsoleScript\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:iRunAConsoleScriptWithIt\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:iRunAConsoleScriptWithSiteaccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:iRunAConsoleScriptWithSiteaccess\\(\\) has parameter \\$siteaccessOption with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:iRunTheCommand\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:iRunTheCommand\\(\\) has parameter \\$command with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:iRunTheCommand\\(\\) has parameter \\$siteaccess with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:thereIsADefaultSiteaccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:thereIsADefaultSiteaccess\\(\\) has parameter \\$expectedSiteaccessName with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:thereIsASiteaccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:thereIsASiteaccess\\(\\) has parameter \\$default with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:thereIsASiteaccess\\(\\) has parameter \\$expectedSiteaccessName with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:thereIsASiteaccessThatIsNotTheDefaultOne\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:\\$it type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ConsoleContext\\:\\:\\$scriptOutput has no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ConsoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentContext\\:\\:createContentItem\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentContext\\:\\:createDraft\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentContext\\:\\:createDraftForContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentContext\\:\\:getCurrentDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentContext\\:\\:iCreateADraftOfAnExistingContentItem\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentContext\\:\\:iCreateAnFolderDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentContext\\:\\:updateDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentContext\\:\\:updateDraft\\(\\) has parameter \\$fields with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentContext.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentContext\\:\\:\\$currentDraft \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\) does not accept null\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentContext.php + + - + message: "#^Call to an undefined method Behat\\\\Testwork\\\\Environment\\\\Environment\\:\\:getContext\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentPreviewContext.php + + - + message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertTrue\\(\\) with false and non\\-falsy\\-string will always evaluate to false\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentPreviewContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentPreviewContext\\:\\:checkForExceptions\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentPreviewContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentPreviewContext\\:\\:gatherContexts\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentPreviewContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentPreviewContext\\:\\:iCreateDraftOfContentTypeWithCustomLocationController\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentPreviewContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentPreviewContext\\:\\:iModifyAFieldFromTheDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentPreviewContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentPreviewContext\\:\\:iPreviewThisDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentPreviewContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentPreviewContext\\:\\:iSeeAPreviewOfTheCurrentDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentPreviewContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentPreviewContext\\:\\:theOutputIsValid\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentPreviewContext.php + + - + message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentPreviewContext.php + + - + message: "#^Call to an undefined method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:processSettings\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Call to an undefined method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:processValidator\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeGroup\\:\\:getContentTypeGroups\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Dead catch \\- Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Exceptions\\\\NotFoundException is never thrown in the try block\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:assertContentTypeDoesntExistsByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:assertContentTypeDoesntExistsByIdentifier\\(\\) has parameter \\$identifier with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:assertContentTypeExistsByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:assertContentTypeExistsByIdentifier\\(\\) has parameter \\$identifier with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:assertContentTypeExistsByIdentifierOnGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:assertContentTypeExistsByIdentifierOnGroup\\(\\) has parameter \\$groupIdentifier with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:assertContentTypeExistsByIdentifierOnGroup\\(\\) has parameter \\$identifier with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:assignContentGroupTypeToContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:checkContentTypeExistenceByIdentifier\\(\\) has parameter \\$groupIdentifier with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:createContentType\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:ensureContentTypeDoesntExist\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:ensureContentTypeDoesntExist\\(\\) has parameter \\$identifier with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:ensureContentTypeWithIndentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:ensureContentTypeWithIndentifier\\(\\) has parameter \\$groupIdentifier with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:ensureContentTypeWithIndentifier\\(\\) has parameter \\$identifier with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:loadContentTypeByIdentifier\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeGroup\\|null but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\|null\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:removeContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Parameter \\#1 \\$contentType of method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:removeContentType\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeGroup given\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Parameter \\#3 \\$fields of method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:createContentType\\(\\) expects array, Behat\\\\Gherkin\\\\Node\\\\TableNode given\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ContentTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ExceptionContext\\:\\:anAccessDeniedExceptionIsThrown\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ExceptionContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ExceptionContext\\:\\:anAccessDeniedExceptionIsThrown\\(\\) has parameter \\$exceptionString with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ExceptionContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ExceptionContext\\:\\:anExceptionIsThrownDuringAnHTTPRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ExceptionContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ExceptionContext\\:\\:iAmLoggedIn\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ExceptionContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ExceptionContext\\:\\:iAmNotLoggedIn\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ExceptionContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ExceptionContext\\:\\:itIsConvertedToAnSymfonyComponentSecurityCoreExceptionAccessDeniedException\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ExceptionContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\ExceptionContext\\:\\:theLoginFormIsShown\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/ExceptionContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:addValueConstraint\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:associateFieldToContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createAndPublishContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createAndPublishContent\\(\\) has parameter \\$field with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createAndPublishContent\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContentOfThisType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContentOfThisType\\(\\) has parameter \\$field with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContentOfThisType\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContentOfThisTypeWithProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContentOfThisTypeWithProperties\\(\\) has parameter \\$fieldType with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContentOfThisTypeWithProperties\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContentTypeWithFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContentTypeWithFieldType\\(\\) has parameter \\$fieldType with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContentTypeWithFieldType\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContentTypeWithRequiredFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContentTypeWithRequiredFieldType\\(\\) has parameter \\$fieldType with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createContentTypeWithRequiredFieldType\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:createField\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:getActualFieldPosition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:getFieldContentState\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:publishContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:setFieldContentState\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^PHPDoc tag @param has invalid value \\(mixed The field value\\)\\: Unexpected token \"The\", expected variable at offset 173$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^PHPDoc tag @param has invalid value \\(string The field name\\)\\: Unexpected token \"The\", expected variable at offset 138$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Parameter \\#1 \\$parentLocationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:newLocationCreateStruct\\(\\) expects int, string given\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:\\$defaultValues type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:\\$fieldConstructionObject type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:\\$fieldTypeInternalIdentifier type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\FieldTypeContext\\:\\:\\$validatorMappings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/FieldTypeContext.php + + - + message: "#^Cannot call method getText\\(\\) on Behat\\\\Mink\\\\Element\\\\NodeElement\\|null\\.$#" + count: 2 + path: src/bundle/Core/Features/Context/QueryControllerContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\QueryControllerContext\\:\\:getVariableTypesFromTemplate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/QueryControllerContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\QueryControllerContext\\:\\:theQueryResultsAreAssignedToTheTwigVariable\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/QueryControllerContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\QueryControllerContext\\:\\:theQueryResultsAreAssignedToTheTwigVariable\\(\\) has parameter \\$twigVariableName with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/QueryControllerContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\QueryControllerContext\\:\\:theQueryResultsAssignedToTheTwigVariableIsAObject\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/QueryControllerContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\QueryControllerContext\\:\\:theQueryResultsAssignedToTheTwigVariableIsAObject\\(\\) has parameter \\$className with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/QueryControllerContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\QueryControllerContext\\:\\:theQueryResultsAssignedToTheTwigVariableIsAObject\\(\\) has parameter \\$twigVariableName with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/QueryControllerContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\QueryControllerContext\\:\\:theQueryResultsAssignedToTheTwigVariableIsAObjectAndHasLimitAndCountParams\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/QueryControllerContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\QueryControllerContext\\:\\:theQueryResultsAssignedToTheTwigVariableIsAObjectAndHasLimitAndCountParams\\(\\) has parameter \\$pageLimit with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/QueryControllerContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\QueryControllerContext\\:\\:theQueryResultsAssignedToTheTwigVariableIsAObjectAndHasLimitAndCountParams\\(\\) has parameter \\$pageValue with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/QueryControllerContext.php + + - + message: "#^Variable \\$valueNodes in PHPDoc tag @var does not match any variable in the foreach loop\\: \\$currentPage, \\$valueNode$#" + count: 1 + path: src/bundle/Core/Features/Context/QueryControllerContext.php + + - + message: "#^Interface Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService referenced with incorrect case\\: Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\roleService\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/RoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\RoleContext\\:\\:getRole\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Role but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Role\\|null\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/RoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\RoleContext\\:\\:iDontSeeRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/RoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\RoleContext\\:\\:iDontSeeRole\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/RoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\RoleContext\\:\\:iHaveRole\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/RoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\RoleContext\\:\\:iSeeRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/RoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\RoleContext\\:\\:iSeeRole\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/RoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\RoleContext\\:\\:noAssginedPolicies\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/RoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\RoleContext\\:\\:noAssginedPolicies\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/RoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\RoleContext\\:\\:noAssigneGroups\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/RoleContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\RoleContext\\:\\:noAssigneGroups\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/RoleContext.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" + count: 4 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Dead catch \\- Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Exceptions\\\\NotFoundException is never thrown in the try block\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameDoesntExist\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameDoesntExist\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameDoesntExistInGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameDoesntExistInGroup\\(\\) has parameter \\$parentGroup with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameDoesntExistInGroup\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameDoesntExistInGroups\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameDoesntExistInGroups\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameExists\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameExists\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameExistsInGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameExistsInGroup\\(\\) has parameter \\$parentGroup with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameExistsInGroup\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameExistsWithFields\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:assertUserWithNameExistsWithFields\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:createPasswordHash\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:createPasswordHash\\(\\) has parameter \\$login with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:createPasswordHash\\(\\) has parameter \\$password with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:createPasswordHash\\(\\) has parameter \\$type with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:createUser\\(\\) has parameter \\$fields with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:ensureUserDoesntExist\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:ensureUserExists\\(\\) invoked with 2 parameters, 3\\-4 required\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:findNonExistingUserEmail\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:findNonExistingUserName\\(\\) is unused\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iDontHaveUser\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iDontHaveUser\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iDontHaveUserInGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iDontHaveUserInGroup\\(\\) has parameter \\$parentGroup with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iDontHaveUserInGroup\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveTheFollowingUsers\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUser\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUser\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User but return statement is missing\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUserInGroup\\(\\) has parameter \\$parentGroupName with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUserInGroup\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUserInGroup\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User but return statement is missing\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUserWithUsernameEmailAndPassword\\(\\) has parameter \\$email with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUserWithUsernameEmailAndPassword\\(\\) has parameter \\$password with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUserWithUsernameEmailAndPassword\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUserWithUsernameEmailAndPassword\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User but return statement is missing\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUserWithUsernameEmailAndPasswordInGroup\\(\\) has parameter \\$email with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUserWithUsernameEmailAndPasswordInGroup\\(\\) has parameter \\$parentGroupName with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUserWithUsernameEmailAndPasswordInGroup\\(\\) has parameter \\$password with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUserWithUsernameEmailAndPasswordInGroup\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:searchUserByLogin\\(\\) has parameter \\$parentGroupId with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:searchUserByLogin\\(\\) should return Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\User but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\.$#" + count: 2 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:searchUserByLogin\\(\\) should return Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\User but returns null\\.$#" + count: 2 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$parentGroupLocationId$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Parameter \\#1 \\$value of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\ParentLocationId constructor expects array\\<int\\>\\|int, string given\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Parameter \\#2 \\$parentLocationId of method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\UserContext\\:\\:searchUserGroups\\(\\) expects string\\|null, int given\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/UserContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\YamlConfigurationContext\\:\\:addConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/YamlConfigurationContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\YamlConfigurationContext\\:\\:addConfiguration\\(\\) has parameter \\$configuration with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/YamlConfigurationContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\YamlConfigurationContext\\:\\:isSymfonyCacheClearRequired\\(\\) should return string but returns bool\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/YamlConfigurationContext.php + + - + message: "#^Offset 'message' does not exist on array\\{type\\: int, message\\: string, file\\: string, line\\: int\\}\\|null\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/YamlConfigurationContext.php + + - + message: "#^Parameter \\#1 \\$input of static method Symfony\\\\Component\\\\Yaml\\\\Yaml\\:\\:parse\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/YamlConfigurationContext.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Features\\\\Context\\\\YamlConfigurationContext\\:\\:\\$platformConfigurationFilePath has no type specified\\.$#" + count: 1 + path: src/bundle/Core/Features/Context/YamlConfigurationContext.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRenderer\\:\\:render\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Fragment/DecoratedFragmentRenderer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRenderer\\:\\:setFragmentPath\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Fragment/DecoratedFragmentRenderer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRenderer\\:\\:setFragmentPath\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Fragment/DecoratedFragmentRenderer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRenderer\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Fragment/DecoratedFragmentRenderer.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRenderer\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 1 + path: src/bundle/Core/Fragment/DecoratedFragmentRenderer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DirectFragmentRenderer\\:\\:getArguments\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Fragment/DirectFragmentRenderer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DirectFragmentRenderer\\:\\:getController\\(\\) should return callable\\(\\)\\: mixed but returns \\(callable\\(\\)\\: mixed\\)\\|false\\.$#" + count: 1 + path: src/bundle/Core/Fragment/DirectFragmentRenderer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DirectFragmentRenderer\\:\\:render\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Fragment/DirectFragmentRenderer.php + + - + message: "#^Parameter \\#1 \\$controller of method Symfony\\\\Component\\\\HttpKernel\\\\ControllerMetadata\\\\ArgumentMetadataFactoryInterface\\:\\:createArgumentMetadata\\(\\) expects array\\|object\\|string, callable given\\.$#" + count: 1 + path: src/bundle/Core/Fragment/DirectFragmentRenderer.php + + - + message: "#^Parameter \\#2 \\$controller of class Symfony\\\\Component\\\\HttpKernel\\\\Event\\\\ControllerEvent constructor expects callable\\(\\)\\: mixed, \\(callable\\(\\)\\: mixed\\)\\|false given\\.$#" + count: 1 + path: src/bundle/Core/Fragment/DirectFragmentRenderer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\FragmentListenerFactory\\:\\:buildFragmentListener\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Fragment/FragmentListenerFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\FragmentListenerFactory\\:\\:buildFragmentListener\\(\\) has parameter \\$fragmentListenerClass with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Fragment/FragmentListenerFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\FragmentListenerFactory\\:\\:buildFragmentListener\\(\\) has parameter \\$fragmentPath with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Fragment/FragmentListenerFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\FragmentListenerFactory\\:\\:setRequestStack\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Fragment/FragmentListenerFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRenderer\\:\\:render\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Fragment/InlineFragmentRenderer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRenderer\\:\\:setFragmentPath\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Fragment/InlineFragmentRenderer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRenderer\\:\\:setFragmentPath\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Fragment/InlineFragmentRenderer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRenderer\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Fragment/InlineFragmentRenderer.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRenderer\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 1 + path: src/bundle/Core/Fragment/InlineFragmentRenderer.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRenderer\\:\\:\\$siteAccess is never read, only written\\.$#" + count: 1 + path: src/bundle/Core/Fragment/InlineFragmentRenderer.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\DependencyInjection\\\\Extension\\\\ExtensionInterface\\:\\:addSecurityListenerFactory\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/IbexaCoreBundle.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\IbexaCoreBundle\\:\\:build\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/IbexaCoreBundle.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\AliasCleaner\\:\\:removeAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/AliasCleaner.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGenerator\\:\\:getVariation\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/AliasGenerator.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGenerator\\:\\:supportsValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/AliasGenerator.php + + - + message: "#^Parameter \\#1 \\$image of method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGenerator\\:\\:applyFilter\\(\\) expects Liip\\\\ImagineBundle\\\\Binary\\\\BinaryInterface, Liip\\\\ImagineBundle\\\\Binary\\\\BinaryInterface\\|string given\\.$#" + count: 1 + path: src/bundle/Core/Imagine/AliasGenerator.php + + - + message: "#^Parameter \\#1 \\$mimeType of method Symfony\\\\Component\\\\Mime\\\\MimeTypesInterface\\:\\:getExtensions\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/Imagine/BinaryLoader.php + + - + message: "#^Parameter \\#2 \\$mimeType of class Liip\\\\ImagineBundle\\\\Model\\\\Binary constructor expects string, string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/Imagine/BinaryLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Cache\\\\AliasGeneratorDecorator\\:\\:getTagsForVariation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Cache\\\\AliasGeneratorDecorator\\:\\:getVariation\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Cache\\\\AliasGeneratorDecorator\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Cache\\\\AliasGeneratorDecorator\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\:\\:\\$name \\(string\\) on left side of \\?\\? is not nullable\\.$#" + count: 2 + path: src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Cache\\\\Resolver\\\\RelativeResolver\\:\\:rewriteUrl\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Cache/Resolver/RelativeResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Cache\\\\Resolver\\\\RelativeResolver\\:\\:rewriteUrl\\(\\) should return string but returns string\\|false\\|null\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Cache/Resolver/RelativeResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Cache\\\\ResolverFactory\\:\\:createCacheResolver\\(\\) should return Liip\\\\ImagineBundle\\\\Imagine\\\\Cache\\\\Resolver\\\\ResolverInterface but returns object\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Cache/ResolverFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilter\\:\\:__construct\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/AbstractFilter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilter\\:\\:getOptions\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/AbstractFilter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilter\\:\\:setOption\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/AbstractFilter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilter\\:\\:setOptions\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/AbstractFilter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilter\\:\\:setOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/AbstractFilter.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilter\\:\\:\\$options type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/AbstractFilter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfiguration\\:\\:all\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/FilterConfiguration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfiguration\\:\\:get\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/FilterConfiguration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfiguration\\:\\:getVariationFilters\\(\\) has parameter \\$configuredVariations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/FilterConfiguration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfiguration\\:\\:getVariationFilters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/FilterConfiguration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfiguration\\:\\:getVariationPostProcessors\\(\\) has parameter \\$configuredVariations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/FilterConfiguration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfiguration\\:\\:getVariationPostProcessors\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/FilterConfiguration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfiguration\\:\\:setConfigResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/FilterConfiguration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterInterface\\:\\:getOptions\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/FilterInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterInterface\\:\\:setOption\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/FilterInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterInterface\\:\\:setOptions\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/FilterInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterInterface\\:\\:setOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/FilterInterface.php + + - + message: "#^Call to an undefined method Imagine\\\\Image\\\\ImageInterface\\:\\:getGmagick\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Gmagick/ReduceNoiseFilter.php + + - + message: "#^Call to an undefined method Imagine\\\\Image\\\\ImageInterface\\:\\:getGmagick\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Gmagick/SwirlFilter.php + + - + message: "#^Call to an undefined method Imagine\\\\Image\\\\ImageInterface\\:\\:getImagick\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Imagick/ReduceNoiseFilter.php + + - + message: "#^Call to an undefined method Imagine\\\\Image\\\\ImageInterface\\:\\:getImagick\\(\\)\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Imagick/SwirlFilter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\BorderFilterLoader\\:\\:load\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/BorderFilterLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\CropFilterLoader\\:\\:load\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/CropFilterLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\FilterLoaderWrapped\\:\\:setInnerLoader\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/FilterLoaderWrapped.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\GrayscaleFilterLoader\\:\\:load\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/GrayscaleFilterLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ReduceNoiseFilterLoader\\:\\:load\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/ReduceNoiseFilterLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleDownOnlyFilterLoader\\:\\:load\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/ScaleDownOnlyFilterLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleExactFilterLoader\\:\\:load\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/ScaleExactFilterLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleFilterLoader\\:\\:load\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/ScaleFilterLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleHeightDownOnlyFilterLoader\\:\\:load\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleHeightFilterLoader\\:\\:load\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/ScaleHeightFilterLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScalePercentFilterLoader\\:\\:load\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/ScalePercentFilterLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleWidthDownOnlyFilterLoader\\:\\:load\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleWidthFilterLoader\\:\\:load\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/ScaleWidthFilterLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\SwirlFilterLoader\\:\\:load\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Filter/Loader/SwirlFilterLoader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolver\\:\\:remove\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/IORepositoryResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolver\\:\\:store\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/IORepositoryResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\ImageAsset\\\\AliasGenerator\\:\\:getVariation\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/ImageAsset/AliasGenerator.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGenerator\\:\\:getVariation\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderAliasGenerator.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGenerator\\:\\:setPlaceholderProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderAliasGenerator.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGenerator\\:\\:setPlaceholderProvider\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderAliasGenerator.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGenerator\\:\\:\\$placeholderOptions type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderAliasGenerator.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorConfigurator\\:\\:__construct\\(\\) has parameter \\$providersConfig with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderAliasGeneratorConfigurator.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorConfigurator\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderAliasGeneratorConfigurator.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorConfigurator\\:\\:\\$providersConfig type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderAliasGeneratorConfigurator.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\:\\:getPlaceholder\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProvider.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\GenericProvider\\:\\:getPlaceholder\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProvider/GenericProvider.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\GenericProvider\\:\\:resolveOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProvider/GenericProvider.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\GenericProvider\\:\\:resolveOptions\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProvider/GenericProvider.php + + - + message: "#^Parameter \\#1 \\$width of class Imagine\\\\Image\\\\Box constructor expects int, int\\|null given\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProvider/GenericProvider.php + + - + message: "#^Parameter \\#1 \\$x of class Imagine\\\\Image\\\\Point constructor expects int, int\\|null given\\.$#" + count: 2 + path: src/bundle/Core/Imagine/PlaceholderProvider/GenericProvider.php + + - + message: "#^Parameter \\#2 \\$font of method Imagine\\\\Draw\\\\DrawerInterface\\:\\:text\\(\\) expects Imagine\\\\Image\\\\AbstractFont, Imagine\\\\Image\\\\FontInterface given\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProvider/GenericProvider.php + + - + message: "#^Parameter \\#2 \\$height of class Imagine\\\\Image\\\\Box constructor expects int, int\\|null given\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProvider/GenericProvider.php + + - + message: "#^Parameter \\#2 \\$y of class Imagine\\\\Image\\\\Point constructor expects int, int\\|null given\\.$#" + count: 2 + path: src/bundle/Core/Imagine/PlaceholderProvider/GenericProvider.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\RemoteProvider\\:\\:getPlaceholder\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProvider/RemoteProvider.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\RemoteProvider\\:\\:resolveOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProvider/RemoteProvider.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\RemoteProvider\\:\\:resolveOptions\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProvider/RemoteProvider.php + + - + message: "#^Cannot access offset string on Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\.$#" + count: 3 + path: src/bundle/Core/Imagine/PlaceholderProviderRegistry.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProviderRegistry\\:\\:__construct\\(\\) has parameter \\$providers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProviderRegistry.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProviderRegistry\\:\\:addProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProviderRegistry.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProviderRegistry\\:\\:\\$providers \\(Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\) does not accept array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/PlaceholderProviderRegistry.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Variation\\\\ImagineAwareAliasGenerator\\:\\:getVariation\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Variation/ImagineAwareAliasGenerator.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\Variation\\\\ImagineAwareAliasGenerator\\:\\:\\$configResolver is unused\\.$#" + count: 1 + path: src/bundle/Core/Imagine/Variation/ImagineAwareAliasGenerator.php + + - + message: "#^Offset 'dirname' does not exist on array\\{dirname\\?\\: string, basename\\: string, extension\\?\\: string, filename\\: string\\}\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPathGenerator/AliasDirectoryVariationPathGenerator.php + + - + message: "#^Offset 'dirname' does not exist on array\\{dirname\\?\\: string, basename\\: string, extension\\?\\: string, filename\\: string\\}\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGenerator.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\IOVariationPurger\\:\\:purge\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/IOVariationPurger.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\IOVariationPurger\\:\\:purge\\(\\) has parameter \\$aliasNames with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/IOVariationPurger.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\IOVariationPurger\\:\\:setLogger\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/IOVariationPurger.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\IOVariationPurger\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/IOVariationPurger.php + + - + message: "#^Interface Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileList extends generic interface Iterator but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/ImageFileList.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileRowReader\\:\\:init\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/ImageFileRowReader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileVariationPurger\\:\\:purge\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurger.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileVariationPurger\\:\\:purge\\(\\) has parameter \\$aliasNames with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurger.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileVariationPurger\\:\\:setLogger\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurger.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileVariationPurger\\:\\:\\$imageFileList \\(Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileList\\) does not accept Iterator\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurger.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileVariationPurger\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurger.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\LegacyStorageImageFileRowReader\\:\\:getCount\\(\\) should return int but returns int\\|string\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileRowReader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\LegacyStorageImageFileRowReader\\:\\:init\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileRowReader.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\LegacyStorageImageFileRowReader\\:\\:\\$statement \\(Doctrine\\\\DBAL\\\\Driver\\\\Statement\\) does not accept Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileRowReader.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\LegacyStorageImageFileRowReader\\:\\:\\$statement type has no value type specified in iterable type Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#" + count: 1 + path: src/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileRowReader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Matcher\\\\ServiceAwareMatcherFactory\\:\\:__construct\\(\\) has parameter \\$matchConfig with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Matcher/ServiceAwareMatcherFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:generate\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:generate\\(\\) has parameter \\$parameters with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:generate\\(\\) has parameter \\$referenceType with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:isSiteAccessAwareRoute\\(\\) has parameter \\$routeName with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:matchRequest\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:setConfigResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:setNonSiteAccessAwareRoutes\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:setNonSiteAccessAwareRoutes\\(\\) has parameter \\$routes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:setSiteAccessRouter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Parameter \\#1 \\$httpPort of method Symfony\\\\Component\\\\Routing\\\\RequestContext\\:\\:setHttpPort\\(\\) expects int, string given\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Parameter \\#1 \\$httpsPort of method Symfony\\\\Component\\\\Routing\\\\RequestContext\\:\\:setHttpsPort\\(\\) expects int, string given\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:\\$nonSiteAccessAwareRoutes has no type specified\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouter\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 1 + path: src/bundle/Core/Routing/DefaultRouter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouter\\:\\:matchRequest\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Routing/UrlAliasRouter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouter\\:\\:setConfigResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/Routing/UrlAliasRouter.php + + - + message: "#^Parameter \\#1 \\$rootLocationId of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:getPathPrefixByRootLocationId\\(\\) expects int, int\\|string given\\.$#" + count: 1 + path: src/bundle/Core/Routing/UrlAliasRouter.php + + - + message: "#^Cannot access property \\$name on Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 2 + path: src/bundle/Core/SiteAccess/Config/ComplexConfigProcessor.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\SiteAccess\\\\Matcher\\:\\:setMatchingConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/SiteAccess/Matcher.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\SiteAccess\\\\MatcherBuilder\\:\\:buildMatcher\\(\\) should return Ibexa\\\\Bundle\\\\Core\\\\SiteAccess\\\\Matcher but returns Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\.$#" + count: 1 + path: src/bundle/Core/SiteAccess/MatcherBuilder.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\SiteAccess\\\\SiteAccessConfigurationFilter\\:\\:filter\\(\\) has parameter \\$siteAccessConfiguration with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/SiteAccess/SiteAccessConfigurationFilter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\SiteAccess\\\\SiteAccessConfigurationFilter\\:\\:filter\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/SiteAccess/SiteAccessConfigurationFilter.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Translation\\\\Collector\\:\\:collect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Translation/Collector.php + + - + message: "#^Argument of an invalid type array\\<int, string\\>\\|false supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/bundle/Core/Translation/GlobCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Translation\\\\GlobCollector\\:\\:collect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Translation/GlobCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\Handler\\\\AbstractConfigResolverBasedURLHandler\\:\\:getOptions\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/AbstractConfigResolverBasedURLHandler.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URL\\:\\:\\$id\\.$#" + count: 2 + path: src/bundle/Core/URLChecker/Handler/AbstractURLHandler.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URL\\:\\:\\$url\\.$#" + count: 2 + path: src/bundle/Core/URLChecker/Handler/AbstractURLHandler.php + + - + message: "#^Cannot call method error\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/AbstractURLHandler.php + + - + message: "#^Cannot call method info\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/AbstractURLHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\Handler\\\\AbstractURLHandler\\:\\:getOptions\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/AbstractURLHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\Handler\\\\AbstractURLHandler\\:\\:setUrlStatus\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/AbstractURLHandler.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URL\\:\\:\\$url\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/HTTPHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\Handler\\\\HTTPHandler\\:\\:createCurlHandlerForUrl\\(\\) has parameter \\$handlers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/HTTPHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\Handler\\\\HTTPHandler\\:\\:doValidate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/HTTPHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\Handler\\\\HTTPHandler\\:\\:getOptions\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/HTTPHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\Handler\\\\HTTPHandler\\:\\:isSuccessful\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/HTTPHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\Handler\\\\HTTPHandler\\:\\:isSuccessful\\(\\) has parameter \\$statusCode with no type specified\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/HTTPHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\Handler\\\\HTTPHandler\\:\\:validate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/HTTPHandler.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URL\\:\\:\\$url\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/MailToHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\Handler\\\\MailToHandler\\:\\:validate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/Handler/MailToHandler.php + + - + message: "#^Cannot call method error\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/URLChecker.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\URLChecker\\:\\:check\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/URLChecker.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\URLChecker\\:\\:fetchUrls\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/URLChecker.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\URLChecker\\:\\:groupByScheme\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/URLChecker.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\URLCheckerInterface\\:\\:check\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/URLCheckerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\URLHandlerInterface\\:\\:validate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/URLHandlerInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\URLHandlerRegistry\\:\\:addHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/URLHandlerRegistry.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\URLChecker\\\\URLHandlerRegistryInterface\\:\\:addHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/URLChecker/URLHandlerRegistryInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Variation\\\\PathResolver\\:\\:resolve\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Variation/PathResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Variation\\\\PathResolver\\:\\:resolve\\(\\) has parameter \\$variation with no type specified\\.$#" + count: 1 + path: src/bundle/Core/Variation/PathResolver.php + + - + message: "#^Parameter \\#1 \\$path of method Ibexa\\\\Contracts\\\\Core\\\\Variation\\\\VariationPathGenerator\\:\\:getVariationPath\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/bundle/Core/Variation/PathResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\Variation\\\\VariationHandlerResolver\\:\\:getVariation\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Core/Variation/VariationHandlerResolver.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\View\\\\Manager\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/View/Manager.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Core\\\\View\\\\Provider\\\\Configured\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Core/View/Provider/Configured.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollector\\:\\:addCollector\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Debug/Collector/IbexaCoreCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollector\\:\\:addCollector\\(\\) has parameter \\$panelTemplate with no type specified\\.$#" + count: 1 + path: src/bundle/Debug/Collector/IbexaCoreCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollector\\:\\:addCollector\\(\\) has parameter \\$toolbarTemplate with no type specified\\.$#" + count: 1 + path: src/bundle/Debug/Collector/IbexaCoreCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollector\\:\\:collect\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Debug/Collector/IbexaCoreCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollector\\:\\:getPanelTemplate\\(\\) should return string but returns null\\.$#" + count: 1 + path: src/bundle/Debug/Collector/IbexaCoreCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollector\\:\\:getToolbarTemplate\\(\\) should return string but returns null\\.$#" + count: 1 + path: src/bundle/Debug/Collector/IbexaCoreCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollector\\:\\:reset\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Debug/Collector/IbexaCoreCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\PersistenceCacheCollector\\:\\:collect\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Debug/Collector/PersistenceCacheCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\PersistenceCacheCollector\\:\\:getCalls\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Debug/Collector/PersistenceCacheCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\PersistenceCacheCollector\\:\\:getHandlers\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/Debug/Collector/PersistenceCacheCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\Collector\\\\SiteAccessCollector\\:\\:collect\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Debug/Collector/SiteAccessCollector.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\DependencyInjection\\\\Compiler\\\\DataCollectorPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Debug/DependencyInjection/Compiler/DataCollectorPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\Debug\\\\IbexaDebugBundle\\:\\:build\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/Debug/IbexaDebugBundle.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\ApiLoader\\\\HandlerRegistry\\:\\:setHandlersMap\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/ApiLoader/HandlerRegistry.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\ApiLoader\\\\HandlerRegistry\\:\\:setHandlersMap\\(\\) has parameter \\$handlersMap with no type specified\\.$#" + count: 1 + path: src/bundle/IO/ApiLoader/HandlerRegistry.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\ApiLoader\\\\HandlerRegistry\\:\\:\\$handlersMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/IO/ApiLoader/HandlerRegistry.php + + - + message: "#^Cannot call method getTimestamp\\(\\) on int\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:__construct\\(\\) has parameter \\$headers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:getContent\\(\\) should return string\\|false but returns null\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:sendContent\\(\\) should return \\$this\\(Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\) but empty return statement found\\.$#" + count: 2 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:sendContent\\(\\) should return \\$this\\(Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\) but return statement is missing\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:setAutoLastModified\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:setContent\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:setContent\\(\\) should return \\$this\\(Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\) but return statement is missing\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:setFile\\(\\) should return Symfony\\\\Component\\\\HttpFoundation\\\\BinaryFileResponse but returns \\$this\\(Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\)\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$autoEtag$#" + count: 2 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Parameter \\#1 \\$date of method Symfony\\\\Component\\\\HttpFoundation\\\\Response\\:\\:setLastModified\\(\\) expects DateTimeInterface\\|null, DateTime\\|false given\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Parameter \\#1 \\$file of method Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:setFile\\(\\) expects SplFileInfo\\|string, Ibexa\\\\Core\\\\IO\\\\Values\\\\BinaryFile given\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|null given\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Parameter \\#2 \\$values of method Symfony\\\\Component\\\\HttpFoundation\\\\ResponseHeaderBag\\:\\:set\\(\\) expects array\\<string\\>\\|string\\|null, int given\\.$#" + count: 2 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:\\$file \\(Ibexa\\\\Core\\\\IO\\\\Values\\\\BinaryFile\\) does not accept SplFileInfo\\|string\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:\\$maxlen has no type specified\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:\\$offset has no type specified\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\BinaryStreamResponse\\:\\:\\$trustXSendfileTypeHeader has no type specified\\.$#" + count: 1 + path: src/bundle/IO/BinaryStreamResponse.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Console\\\\Helper\\\\HelperInterface\\:\\:ask\\(\\)\\.$#" + count: 1 + path: src/bundle/IO/Command/MigrateFilesCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Command\\\\MigrateFilesCommand\\:\\:__construct\\(\\) has parameter \\$configuredBinarydataHandlers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/IO/Command/MigrateFilesCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Command\\\\MigrateFilesCommand\\:\\:__construct\\(\\) has parameter \\$configuredMetadataHandlers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/IO/Command/MigrateFilesCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Command\\\\MigrateFilesCommand\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/Command/MigrateFilesCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Command\\\\MigrateFilesCommand\\:\\:migrateFiles\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/Command/MigrateFilesCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Command\\\\MigrateFilesCommand\\:\\:outputConfiguredHandlers\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/Command/MigrateFilesCommand.php + + - + message: "#^Parameter \\#2 \\$max of class Symfony\\\\Component\\\\Console\\\\Helper\\\\ProgressBar constructor expects int, int\\|null given\\.$#" + count: 1 + path: src/bundle/IO/Command/MigrateFilesCommand.php + + - + message: "#^Parameter \\#3 \\$toMetadataHandlerIdentifier of method Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandlerInterface\\:\\:setIODataHandlersByIdentifiers\\(\\) expects string, int\\|string given\\.$#" + count: 2 + path: src/bundle/IO/Command/MigrateFilesCommand.php + + - + message: "#^Parameter \\#4 \\$toBinarydataHandlerIdentifier of method Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandlerInterface\\:\\:setIODataHandlersByIdentifiers\\(\\) expects string, int\\|string given\\.$#" + count: 2 + path: src/bundle/IO/Command/MigrateFilesCommand.php + + - + message: "#^Strict comparison using \\=\\=\\= between \\(float\\|int\\) and null will always evaluate to false\\.$#" + count: 1 + path: src/bundle/IO/Command/MigrateFilesCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:__construct\\(\\) has parameter \\$binarydataHandlerFactories with generic class ArrayObject but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:__construct\\(\\) has parameter \\$metadataHandlerFactories with generic class ArrayObject but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:getFactory\\(\\) has parameter \\$factories with generic class ArrayObject but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:processHandlers\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:processHandlers\\(\\) has parameter \\$configuredHandlers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:processHandlers\\(\\) has parameter \\$factories with generic class ArrayObject but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php + + - + message: "#^Parameter \\#3 \\$configuredHandlers of method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:processHandlers\\(\\) expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 2 + path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:\\$binarydataHandlerFactories \\(ArrayObject&iterable\\<Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\>\\) does not accept ArrayObject\\|null\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:\\$binarydataHandlerFactories with generic class ArrayObject does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:\\$metadataHandlerFactories \\(ArrayObject&iterable\\<Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\>\\) does not accept ArrayObject\\|null\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPass\\:\\:\\$metadataHandlerFactories with generic class ArrayObject does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\MigrationFileListerPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Compiler/MigrationFileListerPass.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Config\\\\Definition\\\\Builder\\\\NodeDefinition\\:\\:children\\(\\)\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Configuration\\:\\:addHandlersSection\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Configuration\\:\\:addHandlersSection\\(\\) has parameter \\$factories with generic class ArrayObject but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Configuration\\:\\:setBinarydataHandlerFactories\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Configuration\\:\\:setBinarydataHandlerFactories\\(\\) has parameter \\$factories with generic class ArrayObject but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Configuration\\:\\:setMetadataHandlerFactories\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Configuration\\:\\:setMetadataHandlerFactories\\(\\) has parameter \\$factories with generic class ArrayObject but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Configuration.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Configuration\\:\\:\\$binarydataHandlerFactories \\(ArrayObject&iterable\\<Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\>\\) does not accept default value of type array\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Configuration.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Configuration\\:\\:\\$binarydataHandlerFactories with generic class ArrayObject does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Configuration.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Configuration\\:\\:\\$metadataHandlerFactories \\(ArrayObject&iterable\\<Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\>\\) does not accept default value of type array\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Configuration.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Configuration\\:\\:\\$metadataHandlerFactories with generic class ArrayObject does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/Configuration.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\:\\:addConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/ConfigurationFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\:\\:configureHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/ConfigurationFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\:\\:configureHandler\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/ConfigurationFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\Flysystem\\:\\:addConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/ConfigurationFactory/Flysystem.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\Flysystem\\:\\:configureHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/ConfigurationFactory/Flysystem.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\Flysystem\\:\\:configureHandler\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/ConfigurationFactory/Flysystem.php + + - + message: "#^Parameter \\#1 \\$container of method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\Flysystem\\:\\:createFilesystem\\(\\) expects Symfony\\\\Component\\\\DependencyInjection\\\\ContainerBuilder, Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null given\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/ConfigurationFactory/Flysystem.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\MetadataHandler\\\\LegacyDFSCluster\\:\\:addConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/ConfigurationFactory/MetadataHandler/LegacyDFSCluster.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\MetadataHandler\\\\LegacyDFSCluster\\:\\:configureHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/ConfigurationFactory/MetadataHandler/LegacyDFSCluster.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\MetadataHandler\\\\LegacyDFSCluster\\:\\:configureHandler\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/ConfigurationFactory/MetadataHandler/LegacyDFSCluster.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:addBinarydataHandlerFactory\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:addMetadataHandlerFactory\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:getBinarydataHandlerFactories\\(\\) return type with generic class ArrayObject does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:getConfiguration\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:getMetadataHandlerFactories\\(\\) return type with generic class ArrayObject does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:load\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:processHandlers\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:processHandlers\\(\\) has parameter \\$config with no type specified\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php + + - + message: "#^Parameter \\#1 \\$configuration of method Symfony\\\\Component\\\\DependencyInjection\\\\Extension\\\\Extension\\:\\:processConfiguration\\(\\) expects Symfony\\\\Component\\\\Config\\\\Definition\\\\ConfigurationInterface, Symfony\\\\Component\\\\Config\\\\Definition\\\\ConfigurationInterface\\|null given\\.$#" + count: 1 + path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:\\$binarydataHandlerFactories with generic class ArrayObject does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\:\\:\\$metadataHandlerFactories with generic class ArrayObject does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/DependencyInjection/IbexaIOExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListener\\:\\:onKernelRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/EventListener/StreamFileListener.php + + - + message: "#^Cannot call method getBinarydataHandlerFactories\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\Extension\\\\ExtensionInterface\\|null\\.$#" + count: 1 + path: src/bundle/IO/IbexaIOBundle.php + + - + message: "#^Cannot call method getMetadataHandlerFactories\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\Extension\\\\ExtensionInterface\\|null\\.$#" + count: 1 + path: src/bundle/IO/IbexaIOBundle.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\IbexaIOBundle\\:\\:build\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/IbexaIOBundle.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\IbexaIOBundle\\:\\:\\$extension \\(Ibexa\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtension\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/bundle/IO/IbexaIOBundle.php + + - + message: "#^Parameter \\#2 \\$offset of class LimitIterator constructor expects int, int\\|null given\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/BinaryFileLister.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\FileLister\\\\BinaryFileLister\\:\\:\\$fileList \\(Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\FileLister\\\\FileIteratorInterface\\) does not accept Iterator\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/BinaryFileLister.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\FileLister\\\\FileIterator\\\\LegacyStorageFileIterator\\:\\:fetchRow\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/FileIterator/LegacyStorageFileIterator.php + + - + message: "#^Interface Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\FileLister\\\\FileIteratorInterface extends generic interface Iterator but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/FileIteratorInterface.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\FileLister\\\\FileRowReader\\\\LegacyStorageFileRowReader\\:\\:getCount\\(\\) should return int but returns int\\|string\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/FileRowReader/LegacyStorageFileRowReader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\FileLister\\\\FileRowReader\\\\LegacyStorageFileRowReader\\:\\:init\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/FileRowReader/LegacyStorageFileRowReader.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\FileLister\\\\FileRowReader\\\\LegacyStorageFileRowReader\\:\\:\\$statement \\(Doctrine\\\\DBAL\\\\Driver\\\\Statement\\) does not accept Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/FileRowReader/LegacyStorageFileRowReader.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\FileLister\\\\FileRowReader\\\\LegacyStorageFileRowReader\\:\\:\\$statement type has no value type specified in iterable type Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/FileRowReader/LegacyStorageFileRowReader.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\FileLister\\\\FileRowReaderInterface\\:\\:init\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/FileRowReaderInterface.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\Ibexa\\\\Contracts\\\\Core\\\\Variation\\\\VariationPathGenerator\\)\\: Unexpected token \"\\\\n \\* \", expected variable at offset 324$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/ImageFileLister.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\Liip\\\\ImagineBundle\\\\Imagine\\\\Filter\\\\FilterConfiguration\\)\\: Unexpected token \"\\\\n \\* \", expected variable at offset 393$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/ImageFileLister.php + + - + message: "#^Parameter \\#2 \\$offset of class LimitIterator constructor expects int, int\\|null given\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/ImageFileLister.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\FileLister\\\\ImageFileLister\\:\\:\\$imageFileList \\(Ibexa\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileList\\) does not accept Iterator\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileLister/ImageFileLister.php + + - + message: "#^Cannot call method getMessage\\(\\) on Throwable\\|null\\.$#" + count: 1 + path: src/bundle/IO/Migration/FileMigrator/FileMigrator.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandler\\:\\:logError\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/Migration/MigrationHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandler\\:\\:logError\\(\\) has parameter \\$message with no type specified\\.$#" + count: 1 + path: src/bundle/IO/Migration/MigrationHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandler\\:\\:logInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/Migration/MigrationHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandler\\:\\:logInfo\\(\\) has parameter \\$message with no type specified\\.$#" + count: 1 + path: src/bundle/IO/Migration/MigrationHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandler\\:\\:logMissingFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/IO/Migration/MigrationHandler.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandler\\:\\:logMissingFile\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: src/bundle/IO/Migration/MigrationHandler.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandler\\:\\:\\$fromBinarydataHandler \\(Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\) does not accept object\\.$#" + count: 1 + path: src/bundle/IO/Migration/MigrationHandler.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandler\\:\\:\\$fromMetadataHandler \\(Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\) does not accept object\\.$#" + count: 1 + path: src/bundle/IO/Migration/MigrationHandler.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandler\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) does not accept Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/bundle/IO/Migration/MigrationHandler.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandler\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) in isset\\(\\) is not nullable\\.$#" + count: 2 + path: src/bundle/IO/Migration/MigrationHandler.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandler\\:\\:\\$toBinarydataHandler \\(Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\) does not accept object\\.$#" + count: 1 + path: src/bundle/IO/Migration/MigrationHandler.php + + - + message: "#^Property Ibexa\\\\Bundle\\\\IO\\\\Migration\\\\MigrationHandler\\:\\:\\$toMetadataHandler \\(Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\) does not accept object\\.$#" + count: 1 + path: src/bundle/IO/Migration/MigrationHandler.php + + - + message: "#^Cannot call method get\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 1 + path: src/bundle/LegacySearchEngine/ApiLoader/ConnectionFactory.php + + - + message: "#^Cannot call method getParameter\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 1 + path: src/bundle/LegacySearchEngine/ApiLoader/ConnectionFactory.php + + - + message: "#^Cannot call method has\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 1 + path: src/bundle/LegacySearchEngine/ApiLoader/ConnectionFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\LegacySearchEngine\\\\ApiLoader\\\\ConnectionFactory\\:\\:getConnection\\(\\) should return Doctrine\\\\DBAL\\\\Connection but returns object\\|null\\.$#" + count: 1 + path: src/bundle/LegacySearchEngine/ApiLoader/ConnectionFactory.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\LegacySearchEngine\\\\DependencyInjection\\\\IbexaLegacySearchEngineExtension\\:\\:load\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/LegacySearchEngine/DependencyInjection/IbexaLegacySearchEngineExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\LegacySearchEngine\\\\IbexaLegacySearchEngineBundle\\:\\:build\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/LegacySearchEngine/IbexaLegacySearchEngineBundle.php + + - + message: "#^Call to an undefined method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\Installer\\:\\:setOutput\\(\\)\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Command\\\\InstallPlatformCommand\\:\\:__construct\\(\\) has parameter \\$installers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Command\\\\InstallPlatformCommand\\:\\:cacheClear\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Command\\\\InstallPlatformCommand\\:\\:checkCreateDatabase\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Command\\\\InstallPlatformCommand\\:\\:checkParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Command\\\\InstallPlatformCommand\\:\\:checkPermissions\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Command\\\\InstallPlatformCommand\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Command\\\\InstallPlatformCommand\\:\\:executeCommand\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Command\\\\InstallPlatformCommand\\:\\:getInstaller\\(\\) has parameter \\$type with no type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Command\\\\InstallPlatformCommand\\:\\:getInstaller\\(\\) should return Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\Installer but returns false\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Command\\\\InstallPlatformCommand\\:\\:indexData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php + + - + message: "#^Strict comparison using \\=\\=\\= between bool and 1 will always evaluate to false\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Command\\\\ValidatePasswordHashesCommand\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Command/ValidatePasswordHashesCommand.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\DependencyInjection\\\\Compiler\\\\InstallerTagPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/DependencyInjection/Compiler/InstallerTagPass.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\DependencyInjection\\\\IbexaRepositoryInstallerExtension\\:\\:load\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/DependencyInjection/IbexaRepositoryInstallerExtension.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Event\\\\Subscriber\\\\BuildSchemaSubscriber\\:\\:getSubscribedEvents\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Event/Subscriber/BuildSchemaSubscriber.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\IbexaRepositoryInstallerBundle\\:\\:build\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundle.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\CoreInstaller\\:\\:createConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/CoreInstaller.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\CoreInstaller\\:\\:importBinaries\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/CoreInstaller.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\CoreInstaller\\:\\:importData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/CoreInstaller.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\CoreInstaller\\:\\:importSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/CoreInstaller.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\DbBasedInstaller\\:\\:copyConfigurationFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\DbBasedInstaller\\:\\:getKernelSQLFileForDBMS\\(\\) should return string but returns string\\|false\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\DbBasedInstaller\\:\\:runQueriesFromFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\DbBasedInstaller\\:\\:runQueriesFromFile\\(\\) has parameter \\$file with no type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\DbBasedInstaller\\:\\:setOutput\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php + + - + message: "#^Parameter \\#2 \\$subject of function preg_split expects string, string\\|false given\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\Installer\\:\\:createConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/Installer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\Installer\\:\\:importBinaries\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/Installer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\Installer\\:\\:importData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/Installer.php + + - + message: "#^Method Ibexa\\\\Bundle\\\\RepositoryInstaller\\\\Installer\\\\Installer\\:\\:importSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: src/bundle/RepositoryInstaller/Installer/Installer.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MutableArrayList\\:\\:createFrom\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MutableArrayList does not specify its types\\: TValue$#" + count: 1 + path: src/contracts/Collection/MutableArrayList.php + + - + message: "#^Parameter \\#2 \\$offset of function array_splice expects int, int\\|string given\\.$#" + count: 1 + path: src/contracts/Collection/MutableArrayList.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MutableArrayMap\\:\\:createFrom\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MutableArrayMap does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Collection/MutableArrayMap.php + + - + message: "#^Binary operation \"\\.\" between array\\|bool\\|float\\|int\\|string\\|null and '/' results in an error\\.$#" + count: 1 + path: src/contracts/Container/Encore/ConfigurationDumper.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Container\\\\Encore\\\\ConfigurationDumper\\:\\:createFinder\\(\\) has parameter \\$bundlesMetadata with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Container/Encore/ConfigurationDumper.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Container\\\\Encore\\\\ConfigurationDumper\\:\\:dumpConfigurationPaths\\(\\) has parameter \\$paths with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Container/Encore/ConfigurationDumper.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Container\\\\Encore\\\\ConfigurationDumper\\:\\:locateConfigurationFiles\\(\\) has parameter \\$bundlesMetadata with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Container/Encore/ConfigurationDumper.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Container\\\\Encore\\\\ConfigurationDumper\\:\\:locateConfigurationFiles\\(\\) has parameter \\$configFiles with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Container/Encore/ConfigurationDumper.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Container\\\\Encore\\\\ConfigurationDumper\\:\\:locateConfigurationFiles\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Container/Encore/ConfigurationDumper.php + + - + message: "#^Parameter \\#1 \\$bundlesMetadata of method Ibexa\\\\Contracts\\\\Core\\\\Container\\\\Encore\\\\ConfigurationDumper\\:\\:locateConfigurationFiles\\(\\) expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/contracts/Container/Encore/ConfigurationDumper.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\BinaryBase\\\\PathGenerator\\:\\:getStoragePathForField\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/FieldType/BinaryBase/PathGenerator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\BinaryBase\\\\PathGeneratorInterface\\:\\:getStoragePathForField\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/FieldType/BinaryBase/PathGeneratorInterface.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\BinaryBase\\\\RouteAwarePathGenerator\\:\\:generate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/BinaryBase/RouteAwarePathGenerator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\BinaryBase\\\\RouteAwarePathGenerator\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/BinaryBase/RouteAwarePathGenerator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldStorage\\:\\:deleteFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/FieldStorage.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldStorage\\:\\:deleteFieldData\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/FieldStorage.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldStorage\\:\\:getFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/FieldType/FieldStorage.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldStorage\\:\\:getFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/FieldStorage.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldStorage\\:\\:getIndexData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/FieldStorage.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldStorage\\:\\:storeFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/FieldStorage.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:applyDefaultSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:applyDefaultValidatorConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:fieldSettingsFromHash\\(\\) has parameter \\$fieldSettingsHash with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:fieldSettingsToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:validatorConfigurationFromHash\\(\\) has parameter \\$validatorConfigurationHash with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:validatorConfigurationToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\GatewayBasedStorage\\:\\:copyLegacyField\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/GatewayBasedStorage.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Generic\\\\Type\\:\\:fieldSettingsFromHash\\(\\) has parameter \\$fieldSettingsHash with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/Generic/Type.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Generic\\\\Type\\:\\:fieldSettingsToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/Generic/Type.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Generic\\\\Type\\:\\:getEmptyValue\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value but returns object\\.$#" + count: 1 + path: src/contracts/FieldType/Generic/Type.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Generic\\\\Type\\:\\:getRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/Generic/Type.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Generic\\\\Type\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/Generic/Type.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Generic\\\\Type\\:\\:getValidatorConfigurationSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/Generic/Type.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Generic\\\\Type\\:\\:mapConstraintViolationList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/Generic/Type.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Generic\\\\Type\\:\\:toHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/Generic/Type.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Generic\\\\Type\\:\\:validatorConfigurationFromHash\\(\\) has parameter \\$validatorConfiguration with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/Generic/Type.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Generic\\\\Type\\:\\:validatorConfigurationToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/Generic/Type.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValidationError\\:\\:setTarget\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/FieldType/ValidationError.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValidationError\\\\AbstractValidationError\\:\\:__construct\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/ValidationError/AbstractValidationError.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValidationError\\\\AbstractValidationError\\:\\:\\$parameters type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/ValidationError/AbstractValidationError.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValueSerializerInterface\\:\\:decode\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/ValueSerializerInterface.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValueSerializerInterface\\:\\:decode\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/ValueSerializerInterface.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValueSerializerInterface\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/ValueSerializerInterface.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValueSerializerInterface\\:\\:denormalize\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/ValueSerializerInterface.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValueSerializerInterface\\:\\:encode\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/ValueSerializerInterface.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValueSerializerInterface\\:\\:encode\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/ValueSerializerInterface.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValueSerializerInterface\\:\\:normalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/ValueSerializerInterface.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValueSerializerInterface\\:\\:normalize\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/FieldType/ValueSerializerInterface.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\IO\\\\BinaryFileCreateStruct\\:\\:setInputStream\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/IO/BinaryFileCreateStruct.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Limitation\\\\Target\\\\Builder\\\\VersionBuilder\\:\\:translateToAnyLanguageOf\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Limitation/Target/Builder/VersionBuilder.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Limitation\\\\Target\\\\Builder\\\\VersionBuilder\\:\\:\\$targetVersionProperties type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Limitation/Target/Builder/VersionBuilder.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Limitation\\\\Target\\\\DestinationLocation\\:\\:__construct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Limitation/Target/DestinationLocation.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Limitation\\\\Type\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Limitation/Type.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\MVC\\\\Templating\\\\BaseRenderStrategy\\:\\:__construct\\(\\) has parameter \\$fragmentRenderers with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/contracts/MVC/Templating/BaseRenderStrategy.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\MVC\\\\View\\\\VariableProvider\\:\\:getTwigVariables\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/MVC/View/VariableProvider.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Options\\\\OptionsBag\\:\\:all\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Options/OptionsBag.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Bookmark\\\\Handler\\:\\:loadByUserIdAndLocationId\\(\\) has parameter \\$locationIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Bookmark/Handler.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\FieldValue\\:\\:\\$data type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Content/FieldValue.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Handler\\:\\:deleteTranslationFromContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Handler\\:\\:loadContentInfoList\\(\\) has parameter \\$contentIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Handler\\:\\:removeRelation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Language/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\:\\:loadList\\(\\) has parameter \\$ids with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Language/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\:\\:update\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Language/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Handler\\:\\:changeMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Handler\\:\\:hide\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Handler\\:\\:loadSubtreeIds\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Handler\\:\\:markSubtreeModified\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Handler\\:\\:setSectionForSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Handler\\:\\:unHide\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Handler\\:\\:update\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Location/Handler.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Trash\\\\TrashResult implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Persistence/Content/Location/Trash/TrashResult.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ObjectState\\\\Handler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/ObjectState/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ObjectState\\\\Handler\\:\\:deleteGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/ObjectState/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ObjectState\\\\Handler\\:\\:setPriority\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/ObjectState/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Section\\\\Handler\\:\\:assign\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Section/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Section\\\\Handler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Section/Handler.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\:\\:\\$languageCodes type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\FieldDefinition\\:\\:__construct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type/FieldDefinition.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\:\\:deleteGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\:\\:getSearchableFieldMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\:\\:link\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\:\\:loadContentTypeList\\(\\) has parameter \\$contentTypeIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\:\\:loadGroups\\(\\) has parameter \\$groupIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\:\\:publish\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\:\\:unlink\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\:\\:update\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\:\\:updateFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\:\\:updateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type/Handler.php + + - + message: "#^PHPDoc tag @param for parameter \\$contentTypeId with type mixed is not subtype of native type int\\.$#" + count: 1 + path: src/contracts/Persistence/Content/Type/Handler.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$fieldDefinitionId$#" + count: 1 + path: src/contracts/Persistence/Content/Type/Handler.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\:\\:\\$pathData type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Content/UrlAlias.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\\\Handler\\:\\:archiveUrlAliasesForDeletedTranslations\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\\\Handler\\:\\:archiveUrlAliasesForDeletedTranslations\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\\\Handler\\:\\:locationCopied\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\\\Handler\\:\\:locationDeleted\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\\\Handler\\:\\:locationMoved\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\\\Handler\\:\\:locationSwapped\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\\\Handler\\:\\:repairBrokenUrlAliasesForLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\\\Handler\\:\\:translationRemoved\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlWildcard\\\\Handler\\:\\:find\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Content/UrlWildcard/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlWildcard\\\\Handler\\:\\:remove\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/UrlWildcard/Handler.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\VersionInfo\\:\\:\\$names has no type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Content/VersionInfo.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Filter\\\\Doctrine\\\\FilteringQueryBuilder\\:\\:buildOperatorBasedCriterionConstraint\\(\\) has parameter \\$criterionValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Filter/Doctrine/FilteringQueryBuilder.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Filter\\\\LazyListIterator implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Persistence/Filter/LazyListIterator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Filter\\\\LazyListIterator\\:\\:__construct\\(\\) has parameter \\$iterator with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/contracts/Persistence/Filter/LazyListIterator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Filter\\\\LazyListIterator\\:\\:prepareIterator\\(\\) has parameter \\$iterator with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/contracts/Persistence/Filter/LazyListIterator.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Filter\\\\LazyListIterator\\:\\:\\$iterator type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/contracts/Persistence/Filter/LazyListIterator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Handler\\:\\:beginTransaction\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Handler\\:\\:commit\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Handler\\:\\:rollback\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/Handler.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Notification\\\\CreateStruct\\:\\:\\$data type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Notification/CreateStruct.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Notification\\\\Notification\\:\\:\\$data type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/Notification/Notification.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\TransactionHandler\\:\\:beginTransaction\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/TransactionHandler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\TransactionHandler\\:\\:commit\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/TransactionHandler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\TransactionHandler\\:\\:rollback\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/TransactionHandler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\URL\\\\Handler\\:\\:find\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/URL/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\URL\\\\Handler\\:\\:findUsages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/URL/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:assignRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/User/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:assignRole\\(\\) has parameter \\$limitation with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/User/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/User/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:deletePolicy\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/User/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:deleteRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/User/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:expireUserToken\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/User/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:publishRoleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/User/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:removeRoleAssignment\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/User/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:unassignRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/User/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:update\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/User/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:updatePolicy\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/User/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:updateRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/User/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:updateUserToken\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Persistence/User/Handler.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Policy\\:\\:\\$limitations type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Persistence/User/Policy.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentByContentInfo\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:validate\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:validate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/ContentService.php + + - + message: "#^PHPDoc tag @param for parameter \\$contentId with type mixed is not subtype of native type int\\.$#" + count: 1 + path: src/contracts/Repository/ContentService.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\|null if not set the draft is created with the initialLanguage code of the source version or if not present with the main language\\.\\)\\: Unexpected token \"if\", expected variable at offset 870$#" + count: 1 + path: src/contracts/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecorator\\:\\:loadContentByContentInfo\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Decorator/ContentServiceDecorator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecorator\\:\\:validate\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Decorator/ContentServiceDecorator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecorator\\:\\:validate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Decorator/ContentServiceDecorator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecorator\\:\\:loadLocationList\\(\\) has parameter \\$locationIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Decorator/LocationServiceDecorator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Decorator/SearchServiceDecorator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:findContentInfo\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Decorator/SearchServiceDecorator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Decorator/SearchServiceDecorator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Decorator/SearchServiceDecorator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecorator\\:\\:suggest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Decorator/SearchServiceDecorator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SettingServiceDecorator\\:\\:newSettingCreateStruct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Decorator/SettingServiceDecorator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\SettingServiceDecorator\\:\\:newSettingUpdateStruct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Decorator/SettingServiceDecorator.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLWildcard referenced with incorrect case\\: Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\UrlWildcard\\.$#" + count: 4 + path: src/contracts/Repository/Decorator/URLWildcardServiceDecorator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecorator\\:\\:createUser\\(\\) has parameter \\$parentGroups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Decorator/UserServiceDecorator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeAddRelationEvent\\:\\:getRelation\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeAddRelationEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeCopyContentEvent\\:\\:getContent\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeCopyContentEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeCopyContentEvent\\:\\:\\$versionInfo \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeCopyContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeCreateContentDraftEvent\\:\\:getContentDraft\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeCreateContentDraftEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeCreateContentDraftEvent\\:\\:\\$creator \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeCreateContentDraftEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeCreateContentDraftEvent\\:\\:\\$versionInfo \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeCreateContentDraftEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeCreateContentEvent\\:\\:__construct\\(\\) has parameter \\$fieldIdentifiersToValidate with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeCreateContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeCreateContentEvent\\:\\:__construct\\(\\) has parameter \\$locationCreateStructs with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeCreateContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeCreateContentEvent\\:\\:getContent\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeCreateContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeCreateContentEvent\\:\\:getLocationCreateStructs\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeCreateContentEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeCreateContentEvent\\:\\:\\$locationCreateStructs type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeCreateContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeDeleteContentEvent\\:\\:getLocations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeDeleteContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeDeleteContentEvent\\:\\:getLocations\\(\\) should return array but returns array\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeDeleteContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeDeleteContentEvent\\:\\:setLocations\\(\\) has parameter \\$locations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeDeleteContentEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeDeleteContentEvent\\:\\:\\$locations type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeDeleteContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeDeleteTranslationEvent\\:\\:__construct\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeDeleteTranslationEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeDeleteTranslationEvent\\:\\:getLanguageCode\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeDeleteTranslationEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeDeleteTranslationEvent\\:\\:\\$languageCode has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeDeleteTranslationEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforePublishVersionEvent\\:\\:__construct\\(\\) has parameter \\$translations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforePublishVersionEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforePublishVersionEvent\\:\\:getContent\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforePublishVersionEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforePublishVersionEvent\\:\\:getTranslations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforePublishVersionEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeUpdateContentEvent\\:\\:__construct\\(\\) has parameter \\$fieldIdentifiersToValidate with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeUpdateContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeUpdateContentEvent\\:\\:getContent\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeUpdateContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\BeforeUpdateContentMetadataEvent\\:\\:getContent\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/BeforeUpdateContentMetadataEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\CopyContentEvent\\:\\:\\$versionInfo \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/CopyContentEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\CreateContentDraftEvent\\:\\:\\$creator \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/CreateContentDraftEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\CreateContentDraftEvent\\:\\:\\$versionInfo \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/CreateContentDraftEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\CreateContentEvent\\:\\:__construct\\(\\) has parameter \\$fieldIdentifiersToValidate with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/CreateContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\CreateContentEvent\\:\\:__construct\\(\\) has parameter \\$locationCreateStructs with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/CreateContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\CreateContentEvent\\:\\:getLocationCreateStructs\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/CreateContentEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\CreateContentEvent\\:\\:\\$locationCreateStructs type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/CreateContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\DeleteContentEvent\\:\\:__construct\\(\\) has parameter \\$locations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/DeleteContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\DeleteContentEvent\\:\\:getLocations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/DeleteContentEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\DeleteContentEvent\\:\\:\\$locations type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/DeleteContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\DeleteTranslationEvent\\:\\:__construct\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/DeleteTranslationEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\DeleteTranslationEvent\\:\\:getLanguageCode\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/DeleteTranslationEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\DeleteTranslationEvent\\:\\:\\$languageCode has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/DeleteTranslationEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\PublishVersionEvent\\:\\:__construct\\(\\) has parameter \\$translations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/PublishVersionEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\PublishVersionEvent\\:\\:getTranslations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/PublishVersionEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\UpdateContentEvent\\:\\:__construct\\(\\) has parameter \\$fieldIdentifiersToValidate with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/Content/UpdateContentEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ContentType\\\\BeforeCopyContentTypeEvent\\:\\:getContentTypeCopy\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/ContentType/BeforeCopyContentTypeEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ContentType\\\\BeforeCopyContentTypeEvent\\:\\:\\$creator \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/ContentType/BeforeCopyContentTypeEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ContentType\\\\BeforeCreateContentTypeDraftEvent\\:\\:getContentTypeDraft\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeDraft but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeDraft\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeDraftEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ContentType\\\\BeforeCreateContentTypeEvent\\:\\:__construct\\(\\) has parameter \\$contentTypeGroups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ContentType\\\\BeforeCreateContentTypeEvent\\:\\:getContentTypeDraft\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeDraft but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeDraft\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ContentType\\\\BeforeCreateContentTypeEvent\\:\\:getContentTypeGroups\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ContentType\\\\BeforeCreateContentTypeEvent\\:\\:\\$contentTypeGroups type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ContentType\\\\BeforeCreateContentTypeGroupEvent\\:\\:getContentTypeGroup\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeGroup but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeGroup\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeGroupEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ContentType\\\\BeforeRemoveContentTypeTranslationEvent\\:\\:getNewContentTypeDraft\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeDraft but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeDraft\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/ContentType/BeforeRemoveContentTypeTranslationEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ContentType\\\\CopyContentTypeEvent\\:\\:\\$creator \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/ContentType/CopyContentTypeEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ContentType\\\\CreateContentTypeEvent\\:\\:__construct\\(\\) has parameter \\$contentTypeGroups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/ContentType/CreateContentTypeEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ContentType\\\\CreateContentTypeEvent\\:\\:getContentTypeGroups\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/ContentType/CreateContentTypeEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ContentType\\\\CreateContentTypeEvent\\:\\:\\$contentTypeGroups type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/ContentType/CreateContentTypeEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Language\\\\BeforeCreateLanguageEvent\\:\\:getLanguage\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Language/BeforeCreateLanguageEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Language\\\\BeforeDisableLanguageEvent\\:\\:getDisabledLanguage\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Language/BeforeDisableLanguageEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Language\\\\BeforeEnableLanguageEvent\\:\\:getEnabledLanguage\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Language/BeforeEnableLanguageEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Language\\\\BeforeUpdateLanguageNameEvent\\:\\:getUpdatedLanguage\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Language/BeforeUpdateLanguageNameEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Location\\\\BeforeCopySubtreeEvent\\:\\:getLocation\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Location/BeforeCopySubtreeEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Location\\\\BeforeCreateLocationEvent\\:\\:getLocation\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Location/BeforeCreateLocationEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Location\\\\BeforeHideLocationEvent\\:\\:getHiddenLocation\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Location/BeforeHideLocationEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Location\\\\BeforeUnhideLocationEvent\\:\\:getRevealedLocation\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Location/BeforeUnhideLocationEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Location\\\\BeforeUpdateLocationEvent\\:\\:getUpdatedLocation\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Location/BeforeUpdateLocationEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Notification\\\\BeforeCreateNotificationEvent\\:\\:getNotification\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Notification\\\\Notification but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Notification\\\\Notification\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Notification/BeforeCreateNotificationEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ObjectState\\\\BeforeCreateObjectStateEvent\\:\\:getObjectState\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectState but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectState\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/ObjectState/BeforeCreateObjectStateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ObjectState\\\\BeforeCreateObjectStateGroupEvent\\:\\:getObjectStateGroup\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroup but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroup\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/ObjectState/BeforeCreateObjectStateGroupEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ObjectState\\\\BeforeSetPriorityOfObjectStateEvent\\:\\:__construct\\(\\) has parameter \\$priority with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/ObjectState/BeforeSetPriorityOfObjectStateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ObjectState\\\\BeforeSetPriorityOfObjectStateEvent\\:\\:getPriority\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/ObjectState/BeforeSetPriorityOfObjectStateEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ObjectState\\\\BeforeSetPriorityOfObjectStateEvent\\:\\:\\$priority has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/ObjectState/BeforeSetPriorityOfObjectStateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ObjectState\\\\BeforeUpdateObjectStateEvent\\:\\:getUpdatedObjectState\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectState but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectState\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/ObjectState/BeforeUpdateObjectStateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ObjectState\\\\BeforeUpdateObjectStateGroupEvent\\:\\:getUpdatedObjectStateGroup\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroup but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroup\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/ObjectState/BeforeUpdateObjectStateGroupEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ObjectState\\\\SetPriorityOfObjectStateEvent\\:\\:__construct\\(\\) has parameter \\$priority with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/ObjectState/SetPriorityOfObjectStateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ObjectState\\\\SetPriorityOfObjectStateEvent\\:\\:getPriority\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/ObjectState/SetPriorityOfObjectStateEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\ObjectState\\\\SetPriorityOfObjectStateEvent\\:\\:\\$priority has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/ObjectState/SetPriorityOfObjectStateEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Role\\\\AddPolicyByRoleDraftEvent\\:\\:\\$updatedRoleDraft has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/Role/AddPolicyByRoleDraftEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Role\\\\AssignRoleToUserEvent\\:\\:\\$roleLimitation \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\RoleLimitation\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\RoleLimitation\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Role/AssignRoleToUserEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Role\\\\AssignRoleToUserGroupEvent\\:\\:\\$roleLimitation \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\RoleLimitation\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\RoleLimitation\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Role/AssignRoleToUserGroupEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Role\\\\BeforeAddPolicyByRoleDraftEvent\\:\\:getUpdatedRoleDraft\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleDraft but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleDraft\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Role/BeforeAddPolicyByRoleDraftEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Role\\\\BeforeAssignRoleToUserEvent\\:\\:\\$roleLimitation \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\RoleLimitation\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\RoleLimitation\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Role/BeforeAssignRoleToUserEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Role\\\\BeforeAssignRoleToUserGroupEvent\\:\\:\\$roleLimitation \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\RoleLimitation\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\RoleLimitation\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Role/BeforeAssignRoleToUserGroupEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Role\\\\BeforeCopyRoleEvent\\:\\:getCopiedRole\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Role but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Role\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Role/BeforeCopyRoleEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Role\\\\BeforeCreateRoleDraftEvent\\:\\:getRoleDraft\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleDraft but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleDraft\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Role/BeforeCreateRoleDraftEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Role\\\\BeforeCreateRoleEvent\\:\\:getRoleDraft\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleDraft but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleDraft\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Role/BeforeCreateRoleEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Role\\\\BeforeRemovePolicyByRoleDraftEvent\\:\\:getUpdatedRoleDraft\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleDraft but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleDraft\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Role/BeforeRemovePolicyByRoleDraftEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Role\\\\BeforeUpdatePolicyByRoleDraftEvent\\:\\:getUpdatedPolicyDraft\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Role/BeforeUpdatePolicyByRoleDraftEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Role\\\\BeforeUpdateRoleDraftEvent\\:\\:getUpdatedRoleDraft\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleDraft but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleDraft\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Role/BeforeUpdateRoleDraftEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Section\\\\BeforeCreateSectionEvent\\:\\:getSection\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Section but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Section\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Section/BeforeCreateSectionEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Section\\\\BeforeUpdateSectionEvent\\:\\:getUpdatedSection\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Section but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Section\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Section/BeforeUpdateSectionEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Setting\\\\BeforeCreateSettingEvent\\:\\:getSetting\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Setting\\\\Setting but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Setting\\\\Setting\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Setting/BeforeCreateSettingEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Setting\\\\BeforeUpdateSettingEvent\\:\\:getUpdatedSetting\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Setting\\\\Setting but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Setting\\\\Setting\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Setting/BeforeUpdateSettingEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Token\\\\BeforeCheckTokenEvent\\:\\:getResult\\(\\) should return bool but returns bool\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Token\\\\BeforeGenerateTokenEvent\\:\\:getToken\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Token\\\\Token but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Token\\\\Token\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Token\\\\BeforeGetTokenEvent\\:\\:getResult\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Token\\\\Token but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Token\\\\Token\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Trash\\\\BeforeDeleteTrashItemEvent\\:\\:getResult\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Trash\\\\TrashItemDeleteResult but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Trash\\\\TrashItemDeleteResult\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Trash/BeforeDeleteTrashItemEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Trash\\\\BeforeEmptyTrashEvent\\:\\:getResultList\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Trash\\\\TrashItemDeleteResultList but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Trash\\\\TrashItemDeleteResultList\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Trash/BeforeEmptyTrashEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Trash\\\\BeforeRecoverEvent\\:\\:getLocation\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Trash/BeforeRecoverEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Trash\\\\BeforeRecoverEvent\\:\\:\\$newParentLocation \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Trash/BeforeRecoverEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Trash\\\\RecoverEvent\\:\\:\\$newParentLocation \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/Trash/RecoverEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URL\\\\BeforeUpdateUrlEvent\\:\\:getUpdatedUrl\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URL but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URL\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/URL/BeforeUpdateUrlEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$forwarding with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$resource with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:getAlwaysAvailable\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:getForwarding\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:getLanguageCode\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:getPath\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:getResource\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:getUrlAlias\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:\\$alwaysAvailable has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:\\$forwarding has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:\\$languageCode has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:\\$path has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateGlobalUrlAliasEvent\\:\\:\\$resource has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$forwarding with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateUrlAliasEvent\\:\\:getAlwaysAvailable\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateUrlAliasEvent\\:\\:getForwarding\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateUrlAliasEvent\\:\\:getLanguageCode\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateUrlAliasEvent\\:\\:getPath\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateUrlAliasEvent\\:\\:getUrlAlias\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateUrlAliasEvent\\:\\:\\$alwaysAvailable has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateUrlAliasEvent\\:\\:\\$forwarding has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateUrlAliasEvent\\:\\:\\$languageCode has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeCreateUrlAliasEvent\\:\\:\\$path has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeRemoveAliasesEvent\\:\\:__construct\\(\\) has parameter \\$aliasList with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeRemoveAliasesEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeRemoveAliasesEvent\\:\\:getAliasList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeRemoveAliasesEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\BeforeRemoveAliasesEvent\\:\\:\\$aliasList type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/BeforeRemoveAliasesEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$forwarding with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$resource with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:getAlwaysAvailable\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:getForwarding\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:getLanguageCode\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:getPath\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:getResource\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:\\$alwaysAvailable has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:\\$forwarding has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:\\$languageCode has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:\\$path has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateGlobalUrlAliasEvent\\:\\:\\$resource has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$forwarding with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateUrlAliasEvent\\:\\:__construct\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateUrlAliasEvent\\:\\:getAlwaysAvailable\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateUrlAliasEvent\\:\\:getForwarding\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateUrlAliasEvent\\:\\:getLanguageCode\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateUrlAliasEvent\\:\\:getPath\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateUrlAliasEvent\\:\\:\\$alwaysAvailable has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateUrlAliasEvent\\:\\:\\$forwarding has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateUrlAliasEvent\\:\\:\\$languageCode has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\CreateUrlAliasEvent\\:\\:\\$path has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\RemoveAliasesEvent\\:\\:__construct\\(\\) has parameter \\$aliasList with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/RemoveAliasesEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\RemoveAliasesEvent\\:\\:getAliasList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/RemoveAliasesEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLAlias\\\\RemoveAliasesEvent\\:\\:\\$aliasList type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLAlias/RemoveAliasesEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeCreateEvent\\:\\:__construct\\(\\) has parameter \\$destinationUrl with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeCreateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeCreateEvent\\:\\:__construct\\(\\) has parameter \\$forward with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeCreateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeCreateEvent\\:\\:__construct\\(\\) has parameter \\$sourceUrl with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeCreateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeCreateEvent\\:\\:getDestinationUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeCreateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeCreateEvent\\:\\:getForward\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeCreateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeCreateEvent\\:\\:getSourceUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeCreateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeCreateEvent\\:\\:getUrlWildcard\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLWildcard but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLWildcard\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeCreateEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeCreateEvent\\:\\:\\$destinationUrl has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeCreateEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeCreateEvent\\:\\:\\$forward has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeCreateEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeCreateEvent\\:\\:\\$sourceUrl has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeCreateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeTranslateEvent\\:\\:__construct\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeTranslateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeTranslateEvent\\:\\:getResult\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLWildcardTranslationResult but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLWildcardTranslationResult\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeTranslateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeTranslateEvent\\:\\:getUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeTranslateEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\BeforeTranslateEvent\\:\\:\\$url has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/BeforeTranslateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\CreateEvent\\:\\:__construct\\(\\) has parameter \\$destinationUrl with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/CreateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\CreateEvent\\:\\:__construct\\(\\) has parameter \\$forward with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/CreateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\CreateEvent\\:\\:__construct\\(\\) has parameter \\$sourceUrl with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/CreateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\CreateEvent\\:\\:getDestinationUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/CreateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\CreateEvent\\:\\:getForward\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/CreateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\CreateEvent\\:\\:getSourceUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/CreateEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\CreateEvent\\:\\:\\$destinationUrl has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/CreateEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\CreateEvent\\:\\:\\$forward has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/CreateEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\CreateEvent\\:\\:\\$sourceUrl has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/CreateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\TranslateEvent\\:\\:__construct\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/TranslateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\TranslateEvent\\:\\:getUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/TranslateEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\URLWildcard\\\\TranslateEvent\\:\\:\\$url has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/URLWildcard/TranslateEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeCreateUserEvent\\:\\:__construct\\(\\) has parameter \\$parentGroups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeCreateUserEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeCreateUserEvent\\:\\:getParentGroups\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeCreateUserEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeCreateUserEvent\\:\\:getUser\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeCreateUserEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeCreateUserEvent\\:\\:\\$parentGroups type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeCreateUserEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeCreateUserGroupEvent\\:\\:getUserGroup\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroup but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroup\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeCreateUserGroupEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeDeleteUserEvent\\:\\:getLocations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeDeleteUserEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeDeleteUserEvent\\:\\:getLocations\\(\\) should return array but returns array\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeDeleteUserEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeDeleteUserEvent\\:\\:setLocations\\(\\) has parameter \\$locations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeDeleteUserEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeDeleteUserEvent\\:\\:\\$locations type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeDeleteUserEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeDeleteUserGroupEvent\\:\\:getLocations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeDeleteUserGroupEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeDeleteUserGroupEvent\\:\\:getLocations\\(\\) should return array but returns array\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeDeleteUserGroupEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeDeleteUserGroupEvent\\:\\:setLocations\\(\\) has parameter \\$locations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeDeleteUserGroupEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeDeleteUserGroupEvent\\:\\:\\$locations type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeDeleteUserGroupEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeUpdateUserEvent\\:\\:getUpdatedUser\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeUpdateUserEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeUpdateUserGroupEvent\\:\\:getUpdatedUserGroup\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroup but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroup\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeUpdateUserGroupEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeUpdateUserPasswordEvent\\:\\:getUpdatedUser\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeUpdateUserPasswordEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\BeforeUpdateUserTokenEvent\\:\\:getUpdatedUser\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/BeforeUpdateUserTokenEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\CreateUserEvent\\:\\:__construct\\(\\) has parameter \\$parentGroups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/CreateUserEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\CreateUserEvent\\:\\:getParentGroups\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/CreateUserEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\CreateUserEvent\\:\\:\\$parentGroups type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/CreateUserEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\DeleteUserEvent\\:\\:__construct\\(\\) has parameter \\$locations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/DeleteUserEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\DeleteUserEvent\\:\\:getLocations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/DeleteUserEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\DeleteUserEvent\\:\\:\\$locations type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/DeleteUserEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\DeleteUserGroupEvent\\:\\:__construct\\(\\) has parameter \\$locations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/DeleteUserGroupEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\DeleteUserGroupEvent\\:\\:getLocations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/DeleteUserGroupEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\DeleteUserGroupEvent\\:\\:\\$locations type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/DeleteUserGroupEvent.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\UpdateUserGroupEvent\\:\\:\\$updatedUserGroup has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Events/User/UpdateUserGroupEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\UserPreference\\\\BeforeSetUserPreferenceEvent\\:\\:__construct\\(\\) has parameter \\$userPreferenceSetStructs with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/UserPreference/BeforeSetUserPreferenceEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\UserPreference\\\\BeforeSetUserPreferenceEvent\\:\\:getUserPreferenceSetStructs\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/UserPreference/BeforeSetUserPreferenceEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\UserPreference\\\\SetUserPreferenceEvent\\:\\:__construct\\(\\) has parameter \\$userPreferenceSetStructs with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/UserPreference/SetUserPreferenceEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\UserPreference\\\\SetUserPreferenceEvent\\:\\:getUserPreferenceSetStructs\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Events/UserPreference/SetUserPreferenceEvent.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Exceptions\\\\ContentFieldValidationException\\:\\:getFieldErrors\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Exceptions/ContentFieldValidationException.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Exceptions\\\\ContentTypeFieldDefinitionValidationException\\:\\:getFieldErrors\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Exceptions/ContentTypeFieldDefinitionValidationException.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Exceptions\\\\InvalidCriterionArgumentException\\:\\:__construct\\(\\) has parameter \\$criterion with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Exceptions/InvalidCriterionArgumentException.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Exceptions\\\\InvalidCriterionArgumentException\\:\\:__construct\\(\\) has parameter \\$key with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Exceptions/InvalidCriterionArgumentException.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Exceptions\\\\InvalidVariationException\\:\\:__construct\\(\\) has parameter \\$code with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Exceptions/InvalidVariationException.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Exceptions\\\\InvalidVariationException\\:\\:__construct\\(\\) has parameter \\$variationName with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Exceptions/InvalidVariationException.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Exceptions\\\\InvalidVariationException\\:\\:__construct\\(\\) has parameter \\$variationType with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Exceptions/InvalidVariationException.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Exceptions\\\\LimitationValidationException\\:\\:getLimitationErrors\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Exceptions/LimitationValidationException.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\FieldType\\:\\:fieldSettingsFromHash\\(\\) has parameter \\$fieldSettingsHash with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/FieldType.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\FieldType\\:\\:fieldSettingsToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/FieldType.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\FieldType\\:\\:validatorConfigurationFromHash\\(\\) has parameter \\$validatorConfigurationHash with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/FieldType.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\FieldType\\:\\:validatorConfigurationToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/FieldType.php + + - + message: "#^Cannot call method current\\(\\) on Iterator\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Iterator/BatchIterator.php + + - + message: "#^Cannot call method next\\(\\) on Iterator\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Iterator/BatchIterator.php + + - + message: "#^Cannot call method valid\\(\\) on Iterator\\|null\\.$#" + count: 2 + path: src/contracts/Repository/Iterator/BatchIterator.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIterator implements generic interface Iterator but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Iterator/BatchIterator.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\AbstractSearchAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapter.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\ContentFilteringAdapter\\:\\:__construct\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentFilteringAdapter.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\LocationFilteringAdapter\\:\\:__construct\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationFilteringAdapter.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\LocationFilteringAdapter\\:\\:fetch\\(\\) should return Iterator but returns iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>&Traversable\\.$#" + count: 1 + path: src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationFilteringAdapter.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\LocationSearchAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php + + - + message: "#^Parameter \\#1 \\$query of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findLocations\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LocationQuery, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query given\\.$#" + count: 1 + path: src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LanguageResolver\\:\\:getPrioritizedLanguages\\(\\) has parameter \\$forcedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/LanguageResolver.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Lists\\\\UnauthorizedListItem\\:\\:__construct\\(\\) has parameter \\$payload with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Lists/UnauthorizedListItem.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Lists\\\\UnauthorizedListItem\\:\\:getPayload\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Lists/UnauthorizedListItem.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Lists\\\\UnauthorizedListItem\\:\\:\\$payload type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Lists/UnauthorizedListItem.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocationList\\(\\) has parameter \\$locationIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/LocationService.php + + - + message: "#^PHPDoc tag @param for parameter \\$locationId with type mixed is not subtype of native type int\\.$#" + count: 1 + path: src/contracts/Repository/LocationService.php + + - + message: "#^PHPDoc tag @param for parameter \\$parentLocationId with type mixed is not subtype of native type int\\.$#" + count: 1 + path: src/contracts/Repository/LocationService.php + + - + message: "#^PHPDoc tag @param for parameter \\$objectStateGroupId with type mixed is not subtype of native type int\\.$#" + count: 1 + path: src/contracts/Repository/ObjectStateService.php + + - + message: "#^PHPDoc tag @param for parameter \\$stateId with type mixed is not subtype of native type int\\.$#" + count: 1 + path: src/contracts/Repository/ObjectStateService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\PermissionCriterionResolver\\:\\:getPermissionsCriterion\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/PermissionCriterionResolver.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findContentInfo\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService\\:\\:suggest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SettingService\\:\\:newSettingCreateStruct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/SettingService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SettingService\\:\\:newSettingUpdateStruct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/SettingService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\ThumbnailStrategy\\:\\:getThumbnail\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Strategy/ContentThumbnail/ThumbnailStrategy.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLWildcard referenced with incorrect case\\: Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\UrlWildcard\\.$#" + count: 4 + path: src/contracts/Repository/URLWildcardService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserService\\:\\:createUser\\(\\) has parameter \\$parentGroups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/UserService.php + + - + message: "#^PHPDoc tag @param for parameter \\$userId with type mixed is not subtype of native type int\\.$#" + count: 1 + path: src/contracts/Repository/UserService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Validator\\\\ContentValidator\\:\\:validate\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Validator/ContentValidator.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Bookmark\\\\BookmarkList implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Bookmark/BookmarkList.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentDraftList implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Content/ContentDraftList.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Content/ContentList.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\DraftList\\\\Item\\\\UnauthorizedContentDraftListItem\\:\\:__construct\\(\\) has parameter \\$payload with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/DraftList/Item/UnauthorizedContentDraftListItem.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\DraftList\\\\Item\\\\UnauthorizedContentDraftListItem\\:\\:getPayload\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/DraftList/Item/UnauthorizedContentDraftListItem.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\DraftList\\\\Item\\\\UnauthorizedContentDraftListItem\\:\\:\\$payload type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/DraftList/Item/UnauthorizedContentDraftListItem.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LocationList implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Content/LocationList.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Aggregation\\\\AbstractRangeAggregation\\:\\:__construct\\(\\) has parameter \\$ranges with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Aggregation/AbstractRangeAggregation.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Aggregation\\\\AbstractRangeAggregation\\:\\:getRanges\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Aggregation/AbstractRangeAggregation.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Aggregation\\\\DateMetadataRangeAggregation\\:\\:__construct\\(\\) has parameter \\$ranges with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Aggregation/DateMetadataRangeAggregation.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Aggregation\\\\Field\\\\AbstractFieldRangeAggregation\\:\\:__construct\\(\\) has parameter \\$ranges with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldRangeAggregation.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Aggregation\\\\Range\\:\\:__construct\\(\\) has parameter \\$from with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Aggregation/Range.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Aggregation\\\\Range\\:\\:__construct\\(\\) has parameter \\$to with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Aggregation/Range.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Aggregation\\\\Range\\:\\:getFrom\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Aggregation/Range.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Aggregation\\\\Range\\:\\:getRangeValueAsString\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Aggregation/Range.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Aggregation\\\\Range\\:\\:getTo\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Aggregation/Range.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Aggregation\\\\RawRangeAggregation\\:\\:__construct\\(\\) has parameter \\$ranges with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Aggregation/RawRangeAggregation.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\:\\:\\$target \\(string\\) does not accept string\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\:\\:\\$valueData \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Value\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Value\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion.php + + - + message: "#^Right side of \\|\\| is always false\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Field\\:\\:\\$customFields type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/Field.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\FullText\\:\\:__construct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/FullText.php + + - + message: "#^PHPDoc tag @return with type mixed is not subtype of native type string\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/FullText.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\FullText\\:\\:\\$boost type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/FullText.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\FullText\\:\\:\\$customFields type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/FullText.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Location\\\\IsMainLocation\\:\\:createFromQueryBuilder\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/Location/IsMainLocation.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Location\\\\IsMainLocation\\:\\:createFromQueryBuilder\\(\\) has parameter \\$operator with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/Location/IsMainLocation.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Location\\\\IsMainLocation\\:\\:createFromQueryBuilder\\(\\) has parameter \\$target with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/Location/IsMainLocation.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Location\\\\IsMainLocation\\:\\:createFromQueryBuilder\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/Location/IsMainLocation.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Location\\\\Priority\\:\\:createFromQueryBuilder\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/Location/Priority.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Location\\\\Priority\\:\\:createFromQueryBuilder\\(\\) has parameter \\$operator with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/Location/Priority.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Location\\\\Priority\\:\\:createFromQueryBuilder\\(\\) has parameter \\$target with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/Location/Priority.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Location\\\\Priority\\:\\:createFromQueryBuilder\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/Location/Priority.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$criteria$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/LogicalNot.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\LogicalOperator\\:\\:getSpecifications\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/LogicalOperator.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\MapLocationDistance\\:\\:\\$boost type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/MapLocationDistance.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\MapLocationDistance\\:\\:\\$customFields type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/MapLocationDistance.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Operator\\\\Specifications\\:\\:\\$operator has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/Operator/Specifications.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Operator\\\\Specifications\\:\\:\\$valueCount \\(int\\) does not accept int\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/Operator/Specifications.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\Operator\\\\Specifications\\:\\:\\$valueTypes \\(int\\) does not accept int\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/Criterion/Operator/Specifications.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\FacetBuilder\\\\DateRangeFacetBuilder\\:\\:addRange\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/FacetBuilder/DateRangeFacetBuilder.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\FacetBuilder\\\\DateRangeFacetBuilder\\:\\:addUnboundedFrom\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/FacetBuilder/DateRangeFacetBuilder.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\FacetBuilder\\\\DateRangeFacetBuilder\\:\\:addUnboundedTo\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/FacetBuilder/DateRangeFacetBuilder.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\FacetBuilder\\\\DateRangeFacetBuilder\\:\\:\\$type has no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/FacetBuilder/DateRangeFacetBuilder.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\FacetBuilder\\\\FieldRangeFacetBuilder\\:\\:addRange\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/FacetBuilder/FieldRangeFacetBuilder.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\FacetBuilder\\\\FieldRangeFacetBuilder\\:\\:addUnboundedFrom\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/FacetBuilder/FieldRangeFacetBuilder.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\FacetBuilder\\\\FieldRangeFacetBuilder\\:\\:addUnboundedTo\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/FacetBuilder/FieldRangeFacetBuilder.php + + - + message: "#^PHPDoc tag @return with type mixed is not subtype of native type string\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/SortClause/Field.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\SortClause\\\\Field\\:\\:\\$customFields type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/SortClause/Field.php + + - + message: "#^PHPDoc tag @return with type mixed is not subtype of native type string\\|null\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/SortClause/MapLocationDistance.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\SortClause\\\\MapLocationDistance\\:\\:\\$customFields type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Query/SortClause/MapLocationDistance.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\RelationList implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Content/RelationList.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResult\\\\RangeAggregationResult implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/AggregationResult/RangeAggregationResult.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResult\\\\RangeAggregationResult\\:\\:\\$entries \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResult\\\\RangeAggregationResultEntry\\>\\) does not accept iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResult\\\\RangeAggregationResultEntry\\>\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/AggregationResult/RangeAggregationResult.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResult\\\\TermAggregationResult implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/AggregationResult/TermAggregationResult.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResult\\\\TermAggregationResult\\:\\:__construct\\(\\) has parameter \\$entries with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/AggregationResult/TermAggregationResult.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResult\\\\TermAggregationResult\\:\\:createForAggregation\\(\\) has parameter \\$entries with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/AggregationResult/TermAggregationResult.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResult\\\\TermAggregationResult\\:\\:\\$entries \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResult\\\\TermAggregationResultEntry\\>\\) does not accept iterable\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/AggregationResult/TermAggregationResult.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResult\\\\TermAggregationResultEntry\\:\\:__construct\\(\\) has parameter \\$key with no type specified\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/AggregationResult/TermAggregationResultEntry.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResultCollection implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/AggregationResultCollection.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResultCollection\\:\\:toArray\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\> but returns array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResult\\>\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/AggregationResultCollection.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\Facet\\\\ContentTypeFacet\\:\\:\\$entries type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/Facet/ContentTypeFacet.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\Facet\\\\FieldFacet\\:\\:\\$entries type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/Facet/FieldFacet.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\Facet\\\\LocationFacet\\:\\:\\$entries type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/Facet/LocationFacet.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\Facet\\\\SectionFacet\\:\\:\\$entries type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/Facet/SectionFacet.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\Facet\\\\TermFacet\\:\\:\\$entries type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/Facet/TermFacet.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\Facet\\\\UserFacet\\:\\:\\$entries type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/Facet/UserFacet.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/SearchResult.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\:\\:__construct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Search/SearchResult.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Trash\\\\SearchResult implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Content/Trash/SearchResult.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Trash\\\\SearchResult\\:\\:__construct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Content/Trash/SearchResult.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Trash\\\\TrashItemDeleteResultList implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Content/Trash/TrashItemDeleteResultList.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Trash\\\\TrashItemDeleteResultList\\:\\:getIterator\\(\\) return type with generic class ArrayIterator does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Content/Trash/TrashItemDeleteResultList.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLWildcard\\\\SearchResult implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Content/URLWildcard/SearchResult.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeCreateStruct\\:\\:\\$descriptions type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ContentType/ContentTypeCreateStruct.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeCreateStruct\\:\\:\\$names type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ContentType/ContentTypeCreateStruct.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeUpdateStruct\\:\\:\\$descriptions type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ContentType/ContentTypeUpdateStruct.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeUpdateStruct\\:\\:\\$names type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ContentType/ContentTypeUpdateStruct.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\:\\:getFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ContentType/FieldDefinition.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\:\\:getValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ContentType/FieldDefinition.php + + - + message: "#^Interface Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionCollection extends generic interface ArrayAccess but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/ContentType/FieldDefinitionCollection.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionCollection\\:\\:map\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ContentType/FieldDefinitionCollection.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionCreateStruct\\:\\:\\$descriptions type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ContentType/FieldDefinitionCreateStruct.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionCreateStruct\\:\\:\\$names type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ContentType/FieldDefinitionCreateStruct.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionUpdateStruct\\:\\:\\$descriptions type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ContentType/FieldDefinitionUpdateStruct.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionUpdateStruct\\:\\:\\$names type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ContentType/FieldDefinitionUpdateStruct.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\Filter\\:\\:__construct\\(\\) has parameter \\$sortClauses with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Filter/Filter.php + + - + message: "#^Parameter \\#1 \\$criteria of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\LogicalAnd constructor expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\>, array\\<int, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion\\> given\\.$#" + count: 1 + path: src/contracts/Repository/Values/Filter/Filter.php + + - + message: "#^Parameter \\#1 \\$criteria of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\LogicalOr constructor expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\>, array\\<int, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion\\> given\\.$#" + count: 1 + path: src/contracts/Repository/Values/Filter/Filter.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\LogicalOperator\\:\\:\\$criteria \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\>\\) does not accept array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion\\>\\.$#" + count: 2 + path: src/contracts/Repository/Values/Filter/Filter.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Notification\\\\CreateStruct\\:\\:\\$data type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Notification/CreateStruct.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Notification\\\\Notification\\:\\:\\$data type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/Notification/Notification.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Notification\\\\NotificationList implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/Notification/NotificationList.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateCreateStruct\\:\\:\\$priority \\(int\\) does not accept default value of type false\\.$#" + count: 1 + path: src/contracts/Repository/Values/ObjectState/ObjectStateCreateStruct.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\SearchResult implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/URL/SearchResult.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\UsageSearchResult implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/URL/UsageSearchResult.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\BlockingLimitation\\:\\:__construct\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/User/Limitation/BlockingLimitation.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\UserPreference\\\\UserPreferenceList implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/contracts/Repository/Values/UserPreference/UserPreferenceList.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:__construct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ValueObject.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:attributes\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ValueObject.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:getProperties\\(\\) has parameter \\$dynamicProperties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ValueObject.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:getProperties\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Repository/Values/ValueObject.php + + - + message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:__set\\(\\) expects string, null given\\.$#" + count: 1 + path: src/contracts/Repository/Values/ValueObject.php + + - + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 1 + path: src/contracts/Repository/Values/ValueObject.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\FieldType\\\\FullTextField\\:\\:__construct\\(\\) has parameter \\$transformationRules with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Search/FieldType/FullTextField.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Search\\\\FieldType\\\\FullTextField\\:\\:\\$transformationRules type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Search/FieldType/FullTextField.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:deleteContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Search/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:deleteLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Search/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Search/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Search/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Search/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:indexContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Search/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:indexLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Search/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:purgeIndex\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Search/Handler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:suggest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Search/Handler.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\HttpKernel\\\\KernelInterface\\:\\:getFixtures\\(\\)\\.$#" + count: 1 + path: src/contracts/Test/IbexaKernelTestCase.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\HttpKernel\\\\KernelInterface\\:\\:getSchemaFiles\\(\\)\\.$#" + count: 1 + path: src/contracts/Test/IbexaKernelTestCase.php + + - + message: "#^Cannot call method import\\(\\) on object\\|null\\.$#" + count: 1 + path: src/contracts/Test/IbexaKernelTestCase.php + + - + message: "#^Cannot call method importSchema\\(\\) on object\\|null\\.$#" + count: 1 + path: src/contracts/Test/IbexaKernelTestCase.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Persistence\\\\Fixture\\:\\:load\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Test/Persistence/Fixture.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Persistence\\\\Fixture\\\\BaseInMemoryCachedFileFixture\\:\\:load\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Test/Persistence/Fixture/BaseInMemoryCachedFileFixture.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Persistence\\\\Fixture\\\\BaseInMemoryCachedFileFixture\\:\\:loadFixture\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Test/Persistence/Fixture/BaseInMemoryCachedFileFixture.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Persistence\\\\Fixture\\\\BaseInMemoryCachedFileFixture\\:\\:\\$filePath \\(string\\) does not accept string\\|false\\.$#" + count: 1 + path: src/contracts/Test/Persistence/Fixture/BaseInMemoryCachedFileFixture.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Persistence\\\\Fixture\\\\BaseInMemoryCachedFileFixture\\:\\:\\$inMemoryCachedLoadedData type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Test/Persistence/Fixture/BaseInMemoryCachedFileFixture.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Persistence\\\\Fixture\\\\PhpArrayFileFixture\\:\\:loadFixture\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Test/Persistence/Fixture/PhpArrayFileFixture.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Persistence\\\\Fixture\\\\YamlFixture\\:\\:loadFixture\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Test/Persistence/Fixture/YamlFixture.php + + - + message: "#^Binary operation \"\\.\" between non\\-falsy\\-string and array\\|bool\\|float\\|int\\|string\\|null results in an error\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Call to an undefined method object\\:\\:clear\\(\\)\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Call to an undefined method object\\:\\:contentLanguageHandler\\(\\)\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Call to an undefined method object\\:\\:contentTypeHandler\\(\\)\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:cleanupVarDir\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:cleanupVarDir\\(\\) has parameter \\$sourceDir with no type specified\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:clearInternalCaches\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:externalBuildContainer\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:getDatabaseConnection\\(\\) should return Doctrine\\\\DBAL\\\\Connection but returns object\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:getInstallationDir\\(\\) should return string but returns string\\|false\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$cachePool \\\\Psr\\\\Cache\\\\CacheItemPoolInterface\\)\\: Unexpected token \"\\$cachePool\", expected type at offset 9$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$handler \\\\Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Handler\\)\\: Unexpected token \"\\$handler\", expected type at offset 9$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:\\$connection \\(Doctrine\\\\DBAL\\\\Connection\\) does not accept object\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:\\$repositoryReference has no type specified\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Static property Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:\\$db \\(string\\) does not accept string\\|null\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Static property Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:\\$dsn \\(string\\) does not accept string\\|false\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Static property Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:\\$initialDataFixture \\(Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Persistence\\\\Fixture\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Static property Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:\\$ioRootDir \\(string\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Static property Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:\\$postInsertStatements is unused\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Static property Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\\\Legacy\\:\\:\\$serviceContainer \\(Ibexa\\\\Core\\\\Base\\\\ServiceContainer\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/contracts/Test/Repository/SetupFactory/Legacy.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\User\\\\Identity\\:\\:addInformation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/User/Identity.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\User\\\\Identity\\:\\:addInformation\\(\\) has parameter \\$information with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/User/Identity.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\User\\\\Identity\\:\\:getInformation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/User/Identity.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\User\\\\Identity\\:\\:replaceInformation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/User/Identity.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\User\\\\Identity\\:\\:replaceInformation\\(\\) has parameter \\$information with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/User/Identity.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\User\\\\Identity\\:\\:setInformation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/User/Identity.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\User\\\\IdentityAware\\:\\:setIdentity\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/User/IdentityAware.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Variation\\\\VariationHandler\\:\\:getVariation\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Variation/VariationHandler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Variation\\\\VariationPurger\\:\\:purge\\(\\) has no return type specified\\.$#" + count: 1 + path: src/contracts/Variation/VariationPurger.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Variation\\\\VariationPurger\\:\\:purge\\(\\) has parameter \\$aliasNames with no value type specified in iterable type array\\.$#" + count: 1 + path: src/contracts/Variation/VariationPurger.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\ApiLoader\\\\RepositoryFactory\\:\\:__construct\\(\\) has parameter \\$policyMap with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Container/ApiLoader/RepositoryFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\ApiLoader\\\\RepositoryFactory\\:\\:__construct\\(\\) has parameter \\$repositoryClass with no type specified\\.$#" + count: 1 + path: src/lib/Base/Container/ApiLoader/RepositoryFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\ApiLoader\\\\RepositoryFactory\\:\\:buildRepository\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository but returns object\\.$#" + count: 1 + path: src/lib/Base/Container/ApiLoader/RepositoryFactory.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Container\\\\ApiLoader\\\\RepositoryFactory\\:\\:\\$policyMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Container/ApiLoader/RepositoryFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\AbstractFieldTypeBasedPass\\:\\:getFieldTypeServiceIds\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/AbstractFieldTypeBasedPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\AbstractFieldTypeBasedPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/AbstractFieldTypeBasedPass.php + + - + message: "#^Cannot call method isSubclassOf\\(\\) on ReflectionClass\\|null\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/GenericFieldTypeConverterPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\GenericFieldTypeConverterPass\\:\\:findTaggedServiceIds\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/GenericFieldTypeConverterPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\GenericFieldTypeConverterPass\\:\\:getGenericFieldTypeForAutoRegisterConverter\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/GenericFieldTypeConverterPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Persistence\\\\FieldTypeRegistryPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/Persistence/FieldTypeRegistryPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\FieldRegistryPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/Search/FieldRegistryPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\CriteriaConverterPass\\:\\:addHandlers\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/Search/Legacy/CriteriaConverterPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\CriteriaConverterPass\\:\\:addHandlers\\(\\) has parameter \\$handlers with no type specified\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/Search/Legacy/CriteriaConverterPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\CriteriaConverterPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/Search/Legacy/CriteriaConverterPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\CriterionFieldValueHandlerRegistryPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\SortClauseConverterPass\\:\\:addHandlers\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\SortClauseConverterPass\\:\\:addHandlers\\(\\) has parameter \\$handlers with no type specified\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\SortClauseConverterPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\ExternalStorageRegistryPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\Legacy\\\\RoleLimitationConverterPass\\:\\:process\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Container/Compiler/Storage/Legacy/RoleLimitationConverterPass.php + + - + message: "#^Class Ibexa\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\TaggedServiceIdsIterator\\\\BackwardCompatibleIterator implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/lib/Base/Container/Compiler/TaggedServiceIdsIterator/BackwardCompatibleIterator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\BadStateException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/BadStateException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\BadStateException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/BadStateException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\BadStateException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/BadStateException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\BadStateException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/BadStateException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\BadStateException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/BadStateException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\BadStateException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/BadStateException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\BadStateException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/BadStateException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\BadStateException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/BadStateException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\BadStateException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/BadStateException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentFieldValidationException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentFieldValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentFieldValidationException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentFieldValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentFieldValidationException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentFieldValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentFieldValidationException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentFieldValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentFieldValidationException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentFieldValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentFieldValidationException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentFieldValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentFieldValidationException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentFieldValidationException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentFieldValidationException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentFieldValidationException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentFieldValidationException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentFieldValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeFieldDefinitionValidationException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeFieldDefinitionValidationException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeFieldDefinitionValidationException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeFieldDefinitionValidationException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeFieldDefinitionValidationException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeFieldDefinitionValidationException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeFieldDefinitionValidationException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeFieldDefinitionValidationException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeFieldDefinitionValidationException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeValidationException\\:\\:__construct\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeValidationException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeValidationException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeValidationException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeValidationException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeValidationException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeValidationException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeValidationException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeValidationException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeValidationException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeValidationException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeValidationException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentTypeValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentValidationException\\:\\:__construct\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentValidationException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentValidationException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentValidationException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentValidationException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentValidationException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentValidationException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentValidationException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentValidationException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentValidationException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentValidationException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentValidationException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ContentValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ForbiddenException\\:\\:__construct\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ForbiddenException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ForbiddenException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ForbiddenException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ForbiddenException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ForbiddenException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ForbiddenException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ForbiddenException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ForbiddenException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ForbiddenException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ForbiddenException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ForbiddenException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ForbiddenException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ForbiddenException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ForbiddenException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ForbiddenException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ForbiddenException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ForbiddenException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ForbiddenException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/ForbiddenException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\InvalidArgumentException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/InvalidArgumentException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\InvalidArgumentException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/InvalidArgumentException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\InvalidArgumentException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/InvalidArgumentException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\InvalidArgumentException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/InvalidArgumentException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\InvalidArgumentException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/InvalidArgumentException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\InvalidArgumentException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/InvalidArgumentException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\InvalidArgumentException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/InvalidArgumentException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\InvalidArgumentException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/InvalidArgumentException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\InvalidArgumentException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/InvalidArgumentException.php + + - + message: "#^Constructor of class Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\InvalidArgumentType has an unused parameter \\$previous\\.$#" + count: 1 + path: src/lib/Base/Exceptions/InvalidArgumentType.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException\\:\\:\\$validationErrors\\.$#" + count: 1 + path: src/lib/Base/Exceptions/LimitationValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/LimitationValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/LimitationValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/LimitationValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/LimitationValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/LimitationValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/LimitationValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/LimitationValidationException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/LimitationValidationException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/LimitationValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\MissingClass\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/MissingClass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\MissingClass\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/MissingClass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\MissingClass\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/MissingClass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\MissingClass\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/MissingClass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\MissingClass\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/MissingClass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\MissingClass\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/MissingClass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\MissingClass\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/MissingClass.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\MissingClass\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/MissingClass.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\MissingClass\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/MissingClass.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\FieldTypeNotFoundException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/FieldTypeNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\FieldTypeNotFoundException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/FieldTypeNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\FieldTypeNotFoundException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/FieldTypeNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\FieldTypeNotFoundException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/FieldTypeNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\FieldTypeNotFoundException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/FieldTypeNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\FieldTypeNotFoundException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/FieldTypeNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\FieldTypeNotFoundException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/FieldTypeNotFoundException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\FieldTypeNotFoundException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/FieldTypeNotFoundException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\FieldTypeNotFoundException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/FieldTypeNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\LimitationNotFoundException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/LimitationNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\LimitationNotFoundException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/LimitationNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\LimitationNotFoundException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/LimitationNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\LimitationNotFoundException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/LimitationNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\LimitationNotFoundException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/LimitationNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\LimitationNotFoundException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/LimitationNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\LimitationNotFoundException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/LimitationNotFoundException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\LimitationNotFoundException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/LimitationNotFoundException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFound\\\\LimitationNotFoundException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFound/LimitationNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFoundException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFoundException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFoundException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFoundException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFoundException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFoundException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFoundException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFoundException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFoundException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFoundException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\NotFoundException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/NotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\TokenExpiredException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/TokenExpiredException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\TokenExpiredException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/TokenExpiredException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\TokenExpiredException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/TokenExpiredException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\TokenExpiredException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/TokenExpiredException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\TokenExpiredException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/TokenExpiredException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\TokenExpiredException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/TokenExpiredException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\TokenExpiredException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/TokenExpiredException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\TokenExpiredException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/TokenExpiredException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\TokenExpiredException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/TokenExpiredException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UnauthorizedException\\:\\:__construct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/UnauthorizedException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UnauthorizedException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/UnauthorizedException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UnauthorizedException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/UnauthorizedException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UnauthorizedException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/UnauthorizedException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UnauthorizedException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/UnauthorizedException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UnauthorizedException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/UnauthorizedException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UnauthorizedException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/UnauthorizedException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UnauthorizedException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/UnauthorizedException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UnauthorizedException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/UnauthorizedException.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UnauthorizedException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/Base/Exceptions/UnauthorizedException.php + + - + message: "#^Cannot cast Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Translation to string\\.$#" + count: 1 + path: src/lib/Base/Exceptions/UserPasswordValidationException.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UserPasswordValidationException\\:\\:__construct\\(\\) has parameter \\$errors with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Exceptions/UserPasswordValidationException.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\DependencyInjection\\\\Container\\:\\:getResources\\(\\)\\.$#" + count: 1 + path: src/lib/Base/ServiceContainer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\ServiceContainer\\:\\:dumpContainer\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/ServiceContainer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\ServiceContainer\\:\\:get\\(\\) should return object but returns object\\|null\\.$#" + count: 1 + path: src/lib/Base/ServiceContainer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\ServiceContainer\\:\\:getContainer\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/ServiceContainer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\ServiceContainer\\:\\:getInnerContainer\\(\\) should return Symfony\\\\Component\\\\DependencyInjection\\\\ContainerBuilder but returns Symfony\\\\Component\\\\DependencyInjection\\\\Container\\.$#" + count: 1 + path: src/lib/Base/ServiceContainer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\ServiceContainer\\:\\:getRepository\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository but returns object\\|null\\.$#" + count: 1 + path: src/lib/Base/ServiceContainer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\ServiceContainer\\:\\:initializeContainer\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/ServiceContainer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\ServiceContainer\\:\\:prepareDirectory\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/ServiceContainer.php + + - + message: "#^Parameter \\#1 \\$container of class Symfony\\\\Component\\\\DependencyInjection\\\\Dumper\\\\PhpDumper constructor expects Symfony\\\\Component\\\\DependencyInjection\\\\ContainerBuilder, Symfony\\\\Component\\\\DependencyInjection\\\\Container given\\.$#" + count: 1 + path: src/lib/Base/ServiceContainer.php + + - + message: "#^Parameter \\#1 \\$content of method Symfony\\\\Component\\\\Config\\\\ResourceCheckerConfigCache\\:\\:write\\(\\) expects string, array\\|string given\\.$#" + count: 1 + path: src/lib/Base/ServiceContainer.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\ServiceContainer\\:\\:\\$innerContainer \\(Symfony\\\\Component\\\\DependencyInjection\\\\Container\\) does not accept object\\.$#" + count: 1 + path: src/lib/Base/ServiceContainer.php + + - + message: "#^Property Ibexa\\\\Core\\\\Base\\\\ServiceContainer\\:\\:\\$innerContainer \\(Symfony\\\\Component\\\\DependencyInjection\\\\Container\\) does not accept string\\|Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\.$#" + count: 1 + path: src/lib/Base/ServiceContainer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Translatable\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Translatable.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Translatable\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Translatable.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Translatable\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Translatable.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Translatable\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Translatable.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Translatable\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Translatable.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Translatable\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Translatable.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Translatable\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Base/Translatable.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Utils\\\\DeprecationWarner\\:\\:log\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Utils/DeprecationWarner.php + + - + message: "#^Method Ibexa\\\\Core\\\\Base\\\\Utils\\\\DeprecationWarnerInterface\\:\\:log\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Base/Utils/DeprecationWarnerInterface.php + + - + message: "#^Parameter \\#1 \\$locations of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Content\\\\DeleteContentEvent constructor expects array, array\\|iterable\\<int\\> given\\.$#" + count: 1 + path: src/lib/Event/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Event\\\\TokenService\\:\\:getToken\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Token\\\\Token but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Token\\\\Token\\|string\\.$#" + count: 1 + path: src/lib/Event/TokenService.php + + - + message: "#^Parameter \\#1 \\$result of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\Token\\\\GetTokenEvent constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Token\\\\Token, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Token\\\\Token\\|string given\\.$#" + count: 1 + path: src/lib/Event/TokenService.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLWildcard referenced with incorrect case\\: Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\UrlWildcard\\.$#" + count: 2 + path: src/lib/Event/URLWildcardService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Event\\\\UserService\\:\\:createUser\\(\\) has parameter \\$parentGroups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Event/UserService.php + + - + message: "#^Parameter \\#1 \\$locations of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\DeleteUserEvent constructor expects array, array\\|iterable\\<int\\> given\\.$#" + count: 1 + path: src/lib/Event/UserService.php + + - + message: "#^Parameter \\#1 \\$locations of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Events\\\\User\\\\DeleteUserGroupEvent constructor expects array, array\\|iterable\\<int\\> given\\.$#" + count: 1 + path: src/lib/Event/UserService.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Author\\\\AuthorCollection\\:\\:removeAuthorsById\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Author/AuthorCollection.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Author\\\\AuthorCollection\\:\\:removeAuthorsById\\(\\) has parameter \\$authorIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Author/AuthorCollection.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Author\\\\Author\\:\\:\\$id \\(int\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/FieldType/Author/AuthorCollection.php + + - + message: "#^Argument of an invalid type array\\|bool\\|float\\|int\\|string\\|null supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/FieldType/Author/SearchField.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Author\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Author/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Author\\\\Type\\:\\:createValueFromInput\\(\\) has parameter \\$inputValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Author/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Author\\\\Type\\:\\:validateFieldSettings\\(\\) has parameter \\$fieldSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Author/Type.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Author\\\\Value\\:\\:\\$authors \\(Ibexa\\\\Core\\\\FieldType\\\\Author\\\\AuthorCollection\\) in empty\\(\\) is not falsy\\.$#" + count: 1 + path: src/lib/FieldType/Author/Value.php + + - + message: "#^Class Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage extends generic class Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\GatewayBasedStorage but does not specify its types\\: T$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:copyLegacyField\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:deleteFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:deleteFieldData\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:deleteFieldData\\(\\) should return bool but empty return statement found\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:deleteFieldData\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:getFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:getFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:getIndexData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:getIndexData\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Field\\> but return statement is missing\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:removeOldFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:removeOldFile\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:setDownloadUrlGenerator\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:storeFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Parameter \\#2 \\$versionNo of method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:removeOldFile\\(\\) expects string, int given\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Parameter \\#2 \\$versionNo of method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\:\\:getFileReferenceData\\(\\) expects int, string given\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Parameter \\#2 \\$versionNo of method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\:\\:removeFileReference\\(\\) expects int, string given\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:\\$downloadUrlGenerator \\(Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\BinaryBase\\\\PathGenerator\\) in isset\\(\\) is not nullable\\.$#" + count: 2 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\:\\:countFileReferences\\(\\) has parameter \\$files with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\:\\:countFileReferences\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\:\\:getFileReferenceData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\:\\:getReferencedFiles\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\:\\:getReferencedFiles\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\:\\:removeFileReference\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\:\\:removeFileReferences\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\:\\:removeFileReferences\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\:\\:storeFileReference\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 3 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:countFileReferences\\(\\) has parameter \\$files with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:countFileReferences\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getFileReferenceData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getPropertyMapping\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getReferencedFiles\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getReferencedFiles\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:removeFileReference\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:removeFileReferences\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:removeFileReferences\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:setFetchColumns\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:setInsertColumns\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:setUpdateColumns\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:storeNewFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\\\DoctrineStorage\\:\\:updateFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\PathGenerator\\\\LegacyPathGenerator\\:\\:getStoragePathForField\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/PathGenerator/LegacyPathGenerator.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$fileName\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$fileName\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$fileSize\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Type\\:\\:completeValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Type\\:\\:createValue\\(\\) has parameter \\$inputValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Type\\:\\:createValueFromInput\\(\\) has parameter \\$inputValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Type\\:\\:fromHash\\(\\) should return Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Value but returns Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Type\\:\\:regenerateUri\\(\\) has parameter \\$inputValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Type\\:\\:regenerateUri\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/Type.php + + - + message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Type\\:\\:getSortInfo\\(\\) expects Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Value, Ibexa\\\\Core\\\\FieldType\\\\Value given\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/Type.php + + - + message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Type\\:\\:toHash\\(\\) expects Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Value, Ibexa\\\\Core\\\\FieldType\\\\Value given\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Value\\:\\:__construct\\(\\) has parameter \\$fileData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryBase/Value.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryFile\\\\BinaryFileStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getPropertyMapping\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryFile/BinaryFileStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryFile\\\\BinaryFileStorage\\\\Gateway\\\\DoctrineStorage\\:\\:setFetchColumns\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryFile/BinaryFileStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryFile\\\\BinaryFileStorage\\\\Gateway\\\\DoctrineStorage\\:\\:setInsertColumns\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryFile/BinaryFileStorage/Gateway/DoctrineStorage.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Value\\:\\:\\$downloadCount\\.$#" + count: 1 + path: src/lib/FieldType/BinaryFile/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryFile\\\\Type\\:\\:completeValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/BinaryFile/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryFile\\\\Type\\:\\:createValue\\(\\) has parameter \\$inputValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/BinaryFile/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\BinaryFile\\\\Type\\:\\:fromPersistenceValue\\(\\) should return Ibexa\\\\Core\\\\FieldType\\\\BinaryFile\\\\Value but returns Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/BinaryFile/Type.php + + - + message: "#^Return type \\(Ibexa\\\\Core\\\\FieldType\\\\BinaryFile\\\\Value\\) of method Ibexa\\\\Core\\\\FieldType\\\\BinaryFile\\\\Type\\:\\:createValue\\(\\) should be compatible with return type \\(Ibexa\\\\Core\\\\FieldType\\\\Media\\\\Value\\) of method Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Type\\:\\:createValue\\(\\)$#" + count: 1 + path: src/lib/FieldType/BinaryFile/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$bool\\.$#" + count: 1 + path: src/lib/FieldType/Checkbox/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Checkbox\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Checkbox/Type.php + + - + message: "#^Argument of an invalid type array\\|float\\|int\\<min, \\-1\\>\\|int\\<1, max\\>\\|string\\|true supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/FieldType/Country/SearchField.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Country\\\\SearchField\\:\\:__construct\\(\\) has parameter \\$countriesInfo with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Country/SearchField.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Country\\\\SearchField\\:\\:\\$countriesInfo type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Country/SearchField.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$countries\\.$#" + count: 1 + path: src/lib/FieldType/Country/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Country\\\\Type\\:\\:__construct\\(\\) has parameter \\$countriesInfo with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Country/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Country\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Country/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Country\\\\Type\\:\\:createValueFromInput\\(\\) has parameter \\$inputValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Country/Type.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Country\\\\Type\\:\\:\\$countriesInfo type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Country/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Country\\\\Value\\:\\:__construct\\(\\) has parameter \\$countries with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Country/Value.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Country\\\\Value\\:\\:\\$countries type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Country/Value.php + + - + message: "#^Cannot access offset 'timestamp' on array\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/FieldType/Date/SearchField.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$date\\.$#" + count: 1 + path: src/lib/FieldType/Date/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Date\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Date/Type.php + + - + message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:isEmptyValue\\(\\) expects Ibexa\\\\Core\\\\FieldType\\\\Value, Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value given\\.$#" + count: 1 + path: src/lib/FieldType/Date/Type.php + + - + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 2 + path: src/lib/FieldType/Date/Value.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$value\\.$#" + count: 1 + path: src/lib/FieldType/DateAndTime/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\DateAndTime\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/DateAndTime/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\DateAndTime\\\\Type\\:\\:fieldSettingsFromHash\\(\\) has parameter \\$fieldSettingsHash with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/DateAndTime/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\DateAndTime\\\\Type\\:\\:fieldSettingsToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/DateAndTime/Type.php + + - + message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:isEmptyValue\\(\\) expects Ibexa\\\\Core\\\\FieldType\\\\Value, Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value given\\.$#" + count: 1 + path: src/lib/FieldType/DateAndTime/Type.php + + - + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 2 + path: src/lib/FieldType/DateAndTime/Value.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\EmailAddress\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/EmailAddress/Type.php + + - + message: "#^Class Ibexa\\\\Core\\\\FieldType\\\\FieldSettings extends generic class ArrayObject but does not specify its types\\: TKey, TValue$#" + count: 1 + path: src/lib/FieldType/FieldSettings.php + + - + message: "#^Parameter \\#1 \\$propertyName of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Exceptions\\\\PropertyReadOnlyException constructor expects string, int\\|string given\\.$#" + count: 1 + path: src/lib/FieldType/FieldSettings.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:acceptValue\\(\\) should return Ibexa\\\\Core\\\\FieldType\\\\Value but returns Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\.$#" + count: 2 + path: src/lib/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:applyDefaultSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:applyDefaultValidatorConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:checkValueType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:fieldSettingsFromHash\\(\\) has parameter \\$fieldSettingsHash with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:fieldSettingsToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:fromPersistenceValue\\(\\) should return Ibexa\\\\Core\\\\FieldType\\\\Value but returns Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:getRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:setTransformationProcessor\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:validatorConfigurationFromHash\\(\\) has parameter \\$validatorConfigurationHash with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:validatorConfigurationToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/FieldType.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$value\\.$#" + count: 1 + path: src/lib/FieldType/Float/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Float\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Float/Type.php + + - + message: "#^PHPDoc tag @param for parameter \\$value with type mixed is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/Float/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\GatewayBasedStorage\\:\\:addGateway\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/GatewayBasedStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\GatewayBasedStorage\\:\\:copyLegacyField\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/GatewayBasedStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\GatewayBasedStorage\\:\\:getGateway\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/GatewayBasedStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Handler\\:\\:initWithFieldTypeValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Handler.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$isbn\\.$#" + count: 2 + path: src/lib/FieldType/ISBN/Type.php + + - + message: "#^Binary operation \"\\*\" between int\\<\\-3, 3\\> and string results in an error\\.$#" + count: 1 + path: src/lib/FieldType/ISBN/Type.php + + - + message: "#^Binary operation \"\\*\" between int\\<min, 3\\> and string results in an error\\.$#" + count: 1 + path: src/lib/FieldType/ISBN/Type.php + + - + message: "#^Binary operation \"\\*\" between string and int\\<1, 10\\> results in an error\\.$#" + count: 1 + path: src/lib/FieldType/ISBN/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ISBN\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/ISBN/Type.php + + - + message: "#^PHPDoc tag @param for parameter \\$value with type mixed is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/ISBN/Type.php + + - + message: "#^Parameter \\#1 \\$isbnNr of method Ibexa\\\\Core\\\\FieldType\\\\ISBN\\\\Type\\:\\:validateISBN13Checksum\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/ISBN/Type.php + + - + message: "#^Parameter \\#1 \\$isbnNr of method Ibexa\\\\Core\\\\FieldType\\\\ISBN\\\\Type\\:\\:validateISBNChecksum\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/ISBN/Type.php + + - + message: "#^Parameter \\#1 \\$string of function strlen expects string, string\\|null given\\.$#" + count: 2 + path: src/lib/FieldType/ISBN/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\AliasCleanerInterface\\:\\:removeAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/AliasCleanerInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\Legacy\\:\\:deleteBinaryFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/IO/Legacy.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\Legacy\\:\\:deleteDirectory\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/IO/Legacy.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\Legacy\\:\\:newBinaryCreateStructFromUploadedFile\\(\\) has parameter \\$uploadedFile with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/IO/Legacy.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\Legacy\\:\\:setPrefix\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/IO/Legacy.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\Legacy\\:\\:setPrefixes\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/IO/Legacy.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\OptionsProvider\\:\\:getDraftImagesDir\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/IO/OptionsProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\OptionsProvider\\:\\:getPublishedImagesDir\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/IO/OptionsProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\OptionsProvider\\:\\:getStorageDir\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/IO/OptionsProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\OptionsProvider\\:\\:getVarDir\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/IO/OptionsProvider.php + + - + message: "#^Cannot access offset 'id' on array\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Cannot access offset 'imageId' on array\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Cannot access offset 0 on array\\{0\\: int\\<0, max\\>, 1\\: int\\<0, max\\>, 2\\: int, 3\\: string, mime\\: string, channels\\?\\: int, bits\\?\\: int\\}\\|false\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Cannot access offset 1 on array\\{0\\: int\\<0, max\\>, 1\\: int\\<0, max\\>, 2\\: int, 3\\: string, mime\\: string, channels\\?\\: int, bits\\?\\: int\\}\\|false\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Class Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage extends generic class Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\GatewayBasedStorage but does not specify its types\\: T$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\:\\:deleteFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\:\\:deleteFieldData\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\:\\:deleteFieldData\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\:\\:getFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\:\\:getFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\:\\:getIndexData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\:\\:getIndexData\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Field\\> but returns null\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\:\\:storeFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\:\\:extractFilesFromXml\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\:\\:extractFilesFromXml\\(\\) has parameter \\$xml with no type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\:\\:getAllVersionsImageXmlForFieldId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\:\\:getImagesData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\:\\:getXmlForImages\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\:\\:getXmlForImages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\:\\:removeImageReferences\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\:\\:storeImageReference\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway.php + + - + message: "#^Cannot access property \\$childNodes on DOMElement\\|null\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 2 + path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php + + - + message: "#^Cannot call method getAttribute\\(\\) on DOMElement\\|null\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php + + - + message: "#^Cannot call method hasAttribute\\(\\) on DOMElement\\|null\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\\\DoctrineStorage\\:\\:extractFilesFromXml\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getAllVersionsImageXmlForFieldId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getImagesData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getXmlForImages\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getXmlForImages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\\\DoctrineStorage\\:\\:storeImageReference\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php + + - + message: "#^Parameter \\#1 \\$select of method Doctrine\\\\DBAL\\\\Query\\\\QueryBuilder\\:\\:select\\(\\) expects array\\<string\\>\\|string\\|null, int given\\.$#" + count: 2 + path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\\\Gateway\\\\DoctrineStorage\\:\\:\\$fieldNameMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/ImageStorage/Gateway/DoctrineStorage.php + + - + message: "#^Cannot call method warning\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 2 + path: src/lib/FieldType/Image/ImageThumbnailStrategy.php + + - + message: "#^Parameter \\#3 \\$pad_string of function str_pad expects string, int given\\.$#" + count: 1 + path: src/lib/FieldType/Image/PathGenerator/LegacyPathGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Image/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\Type\\:\\:createValueFromInput\\(\\) has parameter \\$inputValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/Type.php + + - + message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\Type\\:\\:toHash\\(\\) expects Ibexa\\\\Core\\\\FieldType\\\\Image\\\\Value, Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value given\\.$#" + count: 2 + path: src/lib/FieldType/Image/Type.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Image\\\\Value\\:\\:\\$additionalData \\(array\\<string\\>\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/FieldType/Image/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\Value\\:\\:__construct\\(\\) has parameter \\$imageData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Image/Value.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\Value\\:\\:getFileSize\\(\\) should return int but returns int\\|null\\.$#" + count: 1 + path: src/lib/FieldType/Image/Value.php + + - + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 1 + path: src/lib/FieldType/Image/Value.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ImageAsset\\\\AssetMapper\\:\\:getAssetField\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 1 + path: src/lib/FieldType/ImageAsset/AssetMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ImageAsset\\\\AssetMapper\\:\\:getAssetFieldDefinition\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 1 + path: src/lib/FieldType/ImageAsset/AssetMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ImageAsset\\\\AssetMapper\\:\\:getAssetValue\\(\\) should return Ibexa\\\\Core\\\\FieldType\\\\Image\\\\Value but returns Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: src/lib/FieldType/ImageAsset/AssetMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ImageAsset\\\\AssetMapper\\:\\:getMappings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/ImageAsset/AssetMapper.php + + - + message: "#^Parameter \\#2 \\$fields of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\ThumbnailStrategy\\:\\:getThumbnail\\(\\) expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\> given\\.$#" + count: 1 + path: src/lib/FieldType/ImageAsset/ImageAssetThumbnailStrategy.php + + - + message: "#^Parameter \\#1 \\$array of function reset expects array\\|object, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/ImageAsset/SearchField.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$destinationContentId\\.$#" + count: 1 + path: src/lib/FieldType/ImageAsset/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ImageAsset\\\\Type\\:\\:createValueFromInput\\(\\) should return Ibexa\\\\Core\\\\FieldType\\\\ImageAsset\\\\Value but returns Ibexa\\\\Core\\\\FieldType\\\\ImageAsset\\\\Value\\|Ibexa\\\\Core\\\\FieldType\\\\Relation\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/ImageAsset/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ImageAsset\\\\Type\\:\\:getRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/ImageAsset/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ImageAsset\\\\Type\\:\\:toHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/ImageAsset/Type.php + + - + message: "#^PHPDoc tag @param for parameter \\$value with type mixed is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/ImageAsset/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$value\\.$#" + count: 1 + path: src/lib/FieldType/Integer/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$value\\.$#" + count: 1 + path: src/lib/FieldType/Integer/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Integer\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Integer/Type.php + + - + message: "#^PHPDoc tag @param for parameter \\$value with type mixed is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/Integer/Type.php + + - + message: "#^Class Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage extends generic class Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\GatewayBasedStorage but does not specify its types\\: T$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\:\\:deleteFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\:\\:deleteFieldData\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\:\\:getFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\:\\:getFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\:\\:getIndexData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\:\\:storeFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\:\\:deleteFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\:\\:deleteFieldData\\(\\) has parameter \\$fieldId with no type specified\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\:\\:deleteFieldData\\(\\) has parameter \\$versionNo with no type specified\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\:\\:getFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\:\\:storeFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\:\\:storeFieldData\\(\\) has parameter \\$contentTypeId with no type specified\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway.php + + - + message: "#^Cannot call method fetch\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 2 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Cannot call method fetchFirstColumn\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\\\DoctrineStorage\\:\\:assignKeywords\\(\\) has parameter \\$keywordMap with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\\\DoctrineStorage\\:\\:deleteFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\\\DoctrineStorage\\:\\:deleteOrphanedKeywords\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getAssignedKeywords\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\\\DoctrineStorage\\:\\:storeFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\)\\: Unexpected token \"\\\\n \\* \", expected variable at offset 137$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Parameter \\#1 \\$keywordsToInsert of method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\\\DoctrineStorage\\:\\:insertKeywords\\(\\) expects array\\<string\\>, array\\<true\\> given\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Parameter \\#2 \\$versionNo of method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\\\DoctrineStorage\\:\\:deleteFieldData\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Parameter \\#2 \\$versionNo of method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\\\DoctrineStorage\\:\\:deleteOldKeywordAssignments\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Parameter \\#2 \\$versionNo of method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getAssignedKeywords\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Parameter \\#3 \\$versionNo of method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\KeywordStorage\\\\Gateway\\\\DoctrineStorage\\:\\:assignKeywords\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$values\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$values\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Keyword/Type.php + + - + message: "#^Class Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage extends generic class Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\GatewayBasedStorage but does not specify its types\\: T$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\:\\:deleteFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\:\\:deleteFieldData\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\:\\:deleteFieldData\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\:\\:getFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\:\\:getFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\:\\:getIndexData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\:\\:storeFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\\\Gateway\\:\\:deleteFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\\\Gateway\\:\\:deleteFieldData\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\\\Gateway\\:\\:getFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage/Gateway.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\\\Gateway\\\\DoctrineStorage\\:\\:deleteFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getFieldData\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\\\Gateway\\\\DoctrineStorage\\:\\:loadFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\\\Gateway\\\\DoctrineStorage\\:\\:storeNewFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\\\Gateway\\\\DoctrineStorage\\:\\:updateFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$address\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$latitude\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$longitude\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\Type\\:\\:createValueFromInput\\(\\) has parameter \\$inputValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/Type.php + + - + message: "#^PHPDoc tag @param for parameter \\$value with type mixed is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\Value\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/Value.php + + - + message: "#^PHPDoc tag @param for parameter \\$values with type array\\<string\\>\\|string is not subtype of native type array\\|null\\.$#" + count: 1 + path: src/lib/FieldType/MapLocation/Value.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Media\\\\MediaStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getPropertyMapping\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Media/MediaStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Media\\\\MediaStorage\\\\Gateway\\\\DoctrineStorage\\:\\:setFetchColumns\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Media/MediaStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Media\\\\MediaStorage\\\\Gateway\\\\DoctrineStorage\\:\\:setInsertColumns\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Media/MediaStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Media\\\\MediaStorage\\\\Gateway\\\\DoctrineStorage\\:\\:setUpdateColumns\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Media/MediaStorage/Gateway/DoctrineStorage.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Value\\:\\:\\$autoplay\\.$#" + count: 1 + path: src/lib/FieldType/Media/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Value\\:\\:\\$hasController\\.$#" + count: 1 + path: src/lib/FieldType/Media/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Value\\:\\:\\$height\\.$#" + count: 1 + path: src/lib/FieldType/Media/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Value\\:\\:\\$loop\\.$#" + count: 1 + path: src/lib/FieldType/Media/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Value\\:\\:\\$width\\.$#" + count: 1 + path: src/lib/FieldType/Media/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Media\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Media/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Media\\\\Type\\:\\:completeValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Media/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Media\\\\Type\\:\\:createValue\\(\\) has parameter \\$inputValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Media/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Media\\\\Type\\:\\:fromPersistenceValue\\(\\) should return Ibexa\\\\Core\\\\FieldType\\\\Media\\\\Value but returns Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/Media/Type.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Media\\\\Type\\:\\:\\$availableTypes has no type specified\\.$#" + count: 1 + path: src/lib/FieldType/Media/Type.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Media\\\\Type\\:\\:\\$settingsSchema type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Media/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$value\\.$#" + count: 1 + path: src/lib/FieldType/Null/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Null\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Null/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\NullStorage\\:\\:copyLegacyField\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/NullStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\NullStorage\\:\\:copyLegacyField\\(\\) should return bool\\|null but empty return statement found\\.$#" + count: 1 + path: src/lib/FieldType/NullStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\NullStorage\\:\\:deleteFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/NullStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\NullStorage\\:\\:deleteFieldData\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/NullStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\NullStorage\\:\\:getFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/NullStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\NullStorage\\:\\:getFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/NullStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\NullStorage\\:\\:getIndexData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/NullStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\NullStorage\\:\\:getIndexData\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Field\\> but returns false\\.$#" + count: 1 + path: src/lib/FieldType/NullStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\NullStorage\\:\\:storeFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/NullStorage.php + + - + message: "#^Parameter \\#1 \\$array of function reset expects array\\|object, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/Relation/SearchField.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$destinationContentId\\.$#" + count: 1 + path: src/lib/FieldType/Relation/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$destinationContentId\\.$#" + count: 1 + path: src/lib/FieldType/Relation/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Relation\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Relation/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Relation\\\\Type\\:\\:getRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Relation/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Relation\\\\Type\\:\\:validate\\(\\) should return array\\<Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\> but returns array\\<int, Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValidationError\\>\\.$#" + count: 1 + path: src/lib/FieldType/Relation/Type.php + + - + message: "#^PHPDoc tag @param for parameter \\$value with type mixed is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/Relation/Type.php + + - + message: "#^Cannot access offset 'destinationContentI…' on array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 2 + path: src/lib/FieldType/RelationList/SearchField.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$destinationContentIds\\.$#" + count: 2 + path: src/lib/FieldType/RelationList/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\RelationList\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/RelationList/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\RelationList\\\\Type\\:\\:createValueFromInput\\(\\) has parameter \\$inputValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/RelationList/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\RelationList\\\\Type\\:\\:getRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/RelationList/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Selection\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Selection/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Selection\\\\Type\\:\\:createValueFromInput\\(\\) has parameter \\$inputValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Selection/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\StorageGateway\\:\\:setConnection\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/StorageGateway.php + + - + message: "#^Parameter \\#1 \\$string of method Ibexa\\\\Core\\\\FieldType\\\\TextBlock\\\\SearchField\\:\\:extractShortText\\(\\) expects string, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/TextBlock/SearchField.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$text\\.$#" + count: 2 + path: src/lib/FieldType/TextBlock/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\TextBlock\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/TextBlock/Type.php + + - + message: "#^PHPDoc tag @param for parameter \\$value with type mixed is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/TextBlock/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$text\\.$#" + count: 2 + path: src/lib/FieldType/TextLine/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\TextLine\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/TextLine/Type.php + + - + message: "#^PHPDoc tag @param for parameter \\$value with type mixed is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/TextLine/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$time\\.$#" + count: 1 + path: src/lib/FieldType/Time/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$time\\.$#" + count: 1 + path: src/lib/FieldType/Time/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Time\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Time/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Time\\\\Type\\:\\:getSortInfo\\(\\) should return int but returns int\\|null\\.$#" + count: 1 + path: src/lib/FieldType/Time/Type.php + + - + message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Core\\\\FieldType\\\\Time\\\\Type\\:\\:isEmptyValue\\(\\) expects Ibexa\\\\Core\\\\FieldType\\\\Value, Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value given\\.$#" + count: 1 + path: src/lib/FieldType/Time/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Time\\\\Value\\:\\:fromTimestamp\\(\\) should return static\\(Ibexa\\\\Core\\\\FieldType\\\\Time\\\\Value\\) but returns Ibexa\\\\Core\\\\FieldType\\\\Time\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/Time/Value.php + + - + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 1 + path: src/lib/FieldType/Time/Value.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$text\\.$#" + count: 1 + path: src/lib/FieldType/Url/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$link\\.$#" + count: 1 + path: src/lib/FieldType/Url/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$text\\.$#" + count: 1 + path: src/lib/FieldType/Url/Type.php + + - + message: "#^Cannot access offset 'text' on array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/FieldType/Url/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Url/Type.php + + - + message: "#^Strict comparison using \\=\\=\\= between Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value and null will always evaluate to false\\.$#" + count: 1 + path: src/lib/FieldType/Url/Type.php + + - + message: "#^Cannot access offset 'urlId' on array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 2 + path: src/lib/FieldType/Url/UrlStorage.php + + - + message: "#^Class Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage extends generic class Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\GatewayBasedStorage but does not specify its types\\: T$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\:\\:deleteFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\:\\:deleteFieldData\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\:\\:deleteFieldData\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\:\\:getFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\:\\:getFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\:\\:getIndexData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\:\\:getIndexData\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Field\\> but return statement is missing\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\:\\:storeFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) does not accept Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\\\Gateway\\:\\:getIdUrlMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\\\Gateway\\:\\:getUrlIdMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\\\Gateway\\:\\:linkUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\\\Gateway\\:\\:unlinkUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage/Gateway.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 3 + path: src/lib/FieldType/Url/UrlStorage/Gateway/DoctrineStorage.php + + - + message: "#^Cannot call method fetchFirstColumn\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getIdUrlMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getUrlIdMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\\\Gateway\\\\DoctrineStorage\\:\\:insertUrl\\(\\) should return string but returns int\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\\\Gateway\\\\DoctrineStorage\\:\\:linkUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Url/UrlStorage/Gateway/DoctrineStorage.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$login\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$passwordHashType\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$passwordHash\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$passwordHashType\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$passwordUpdatedAt\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$plainPassword\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\Type\\:\\:checkValueStructure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\Type\\:\\:createValueFromInput\\(\\) has parameter \\$inputValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\Type\\:\\:fromPersistenceValue\\(\\) should return Ibexa\\\\Core\\\\FieldType\\\\User\\\\Value but returns Ibexa\\\\Core\\\\FieldType\\\\Value\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\Type\\:\\:validatePasswordTTLSetting\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\Type\\:\\:validatePasswordTTLWarningSetting\\(\\) has parameter \\$fieldSettings with no type specified\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\Type\\:\\:validatePasswordTTLWarningSetting\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Core\\\\FieldType\\\\User\\\\Type\\:\\:toHash\\(\\) expects Ibexa\\\\Core\\\\FieldType\\\\User\\\\Value, Ibexa\\\\Core\\\\FieldType\\\\Value given\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\User\\\\Type\\:\\:\\$settingsSchema type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\User\\\\Type\\:\\:\\$validatorConfigurationSchema type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\User\\\\Value\\:\\:\\$login \\(string\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/FieldType/User/Type.php + + - + message: "#^Class Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage extends generic class Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\GatewayBasedStorage but does not specify its types\\: T$#" + count: 1 + path: src/lib/FieldType/User/UserStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\:\\:deleteFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\:\\:getFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\:\\:getFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\:\\:getIndexData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\:\\:getIndexData\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Field\\> but return statement is missing\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\:\\:storeFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\\\Gateway\\:\\:getFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage/Gateway.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 2 + path: src/lib/FieldType/User/UserStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\\\Gateway\\\\DoctrineStorage\\:\\:convertColumnsToProperties\\(\\) has parameter \\$databaseValues with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\\\Gateway\\\\DoctrineStorage\\:\\:convertColumnsToProperties\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\\\Gateway\\\\DoctrineStorage\\:\\:fetchUserData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\\\Gateway\\\\DoctrineStorage\\:\\:fetchUserSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getPropertyMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage/Gateway/DoctrineStorage.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\\\Gateway\\\\DoctrineStorage\\:\\:\\$defaultValues type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/User/UserStorage/Gateway/DoctrineStorage.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\:\\:__construct\\(\\) has parameter \\$target with no type specified\\.$#" + count: 1 + path: src/lib/FieldType/ValidationError.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\:\\:__construct\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/ValidationError.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\:\\:setTarget\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/ValidationError.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\:\\:\\$plural \\(string\\) does not accept string\\|null\\.$#" + count: 1 + path: src/lib/FieldType/ValidationError.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\:\\:\\$plural \\(string\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/FieldType/ValidationError.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\:\\:\\$values type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/ValidationError.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Validator\\:\\:getConstraintsSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Validator.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Validator\\:\\:initializeWithConstraints\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/FieldType/Validator.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Validator\\:\\:initializeWithConstraints\\(\\) has parameter \\$constraints with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Validator.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Validator\\:\\:\\$constraints type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Validator.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$email\\.$#" + count: 1 + path: src/lib/FieldType/Validator/EmailAddressValidator.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\EmailAddressValidator\\:\\:\\$constraints type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Validator/EmailAddressValidator.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Value\\:\\:\\$fileName\\.$#" + count: 1 + path: src/lib/FieldType/Validator/FileExtensionBlackListValidator.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\FileExtensionBlackListValidator\\:\\:\\$constraints type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Validator/FileExtensionBlackListValidator.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\BinaryFile\\\\Value\\:\\:\\$file\\.$#" + count: 1 + path: src/lib/FieldType/Validator/FileSizeValidator.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\FileSizeValidator\\:\\:\\$constraints type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Validator/FileSizeValidator.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\FloatValueValidator\\:\\:\\$constraints type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Validator/FloatValueValidator.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\ImageValidator\\:\\:innerValidate\\(\\) has parameter \\$filePath with no type specified\\.$#" + count: 1 + path: src/lib/FieldType/Validator/ImageValidator.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\IntegerValueValidator\\:\\:\\$constraints type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Validator/IntegerValueValidator.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\StringLengthValidator\\:\\:validateConstraintsOrder\\(\\) has parameter \\$constraints with no type specified\\.$#" + count: 1 + path: src/lib/FieldType/Validator/StringLengthValidator.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\StringLengthValidator\\:\\:\\$constraints type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/Validator/StringLengthValidator.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ValueSerializer\\\\SymfonySerializerAdapter\\:\\:decode\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/ValueSerializer/SymfonySerializerAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ValueSerializer\\\\SymfonySerializerAdapter\\:\\:decode\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/ValueSerializer/SymfonySerializerAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ValueSerializer\\\\SymfonySerializerAdapter\\:\\:denormalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/ValueSerializer/SymfonySerializerAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ValueSerializer\\\\SymfonySerializerAdapter\\:\\:denormalize\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/ValueSerializer/SymfonySerializerAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ValueSerializer\\\\SymfonySerializerAdapter\\:\\:encode\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/ValueSerializer/SymfonySerializerAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ValueSerializer\\\\SymfonySerializerAdapter\\:\\:encode\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/ValueSerializer/SymfonySerializerAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ValueSerializer\\\\SymfonySerializerAdapter\\:\\:normalize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/ValueSerializer/SymfonySerializerAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ValueSerializer\\\\SymfonySerializerAdapter\\:\\:normalize\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/FieldType/ValueSerializer/SymfonySerializerAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\FieldType\\\\ValueSerializer\\\\SymfonySerializerAdapter\\:\\:normalize\\(\\) should return array\\|null but returns array\\|ArrayObject\\<\\(int\\|string\\), mixed\\>\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/FieldType/ValueSerializer/SymfonySerializerAdapter.php + + - + message: "#^Parameter \\#1 \\$data of method Symfony\\\\Component\\\\Serializer\\\\Encoder\\\\DecoderInterface\\:\\:decode\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/FieldType/ValueSerializer/SymfonySerializerAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\ContentPreviewHelper\\:\\:setPreviewActive\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Helper/ContentPreviewHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\ContentPreviewHelper\\:\\:setPreviewedContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Helper/ContentPreviewHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\ContentPreviewHelper\\:\\:setPreviewedLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Helper/ContentPreviewHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\ContentPreviewHelper\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Helper/ContentPreviewHelper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Helper\\\\ContentPreviewHelper\\:\\:\\$originalSiteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 1 + path: src/lib/Helper/ContentPreviewHelper.php + + - + message: "#^Cannot access property \\$fieldTypeIdentifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 1 + path: src/lib/Helper/FieldHelper.php + + - + message: "#^Cannot access property \\$value on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 1 + path: src/lib/Helper/FieldHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\FieldHelper\\:\\:getFieldDefinition\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 1 + path: src/lib/Helper/FieldHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\FieldsGroups\\\\ArrayTranslatorFieldsGroupsList\\:\\:__construct\\(\\) has parameter \\$groups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsList.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\FieldsGroups\\\\ArrayTranslatorFieldsGroupsList\\:\\:getGroups\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsList.php + + - + message: "#^Property Ibexa\\\\Core\\\\Helper\\\\FieldsGroups\\\\ArrayTranslatorFieldsGroupsList\\:\\:\\$groups type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsList.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\FieldsGroups\\\\FieldsGroupsList\\:\\:getGroups\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Helper/FieldsGroups/FieldsGroupsList.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\FieldsGroups\\\\RepositoryConfigFieldsGroupsListFactory\\:\\:build\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Helper/FieldsGroups/RepositoryConfigFieldsGroupsListFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\TranslationHelper\\:\\:__construct\\(\\) has parameter \\$siteAccessesByLanguage with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Helper/TranslationHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\TranslationHelper\\:\\:getAvailableLanguages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Helper/TranslationHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\TranslationHelper\\:\\:getTranslatedByMethod\\(\\) should return string\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/Helper/TranslationHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\TranslationHelper\\:\\:getTranslatedByProperty\\(\\) should return string\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/Helper/TranslationHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\TranslationHelper\\:\\:getTranslatedField\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/Helper/TranslationHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Helper\\\\TranslationHelper\\:\\:getTranslatedFieldDefinitionProperty\\(\\) should return string\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/Helper/TranslationHelper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Helper\\\\TranslationHelper\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) does not accept Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/lib/Helper/TranslationHelper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Helper\\\\TranslationHelper\\:\\:\\$siteAccessesByLanguage type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Helper/TranslationHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\ConfigScopeChangeAwareIOService\\:\\:newBinaryCreateStructFromUploadedFile\\(\\) has parameter \\$uploadedFile with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/IO/ConfigScopeChangeAwareIOService.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\Exception\\\\BinaryFileNotFoundException\\:\\:__construct\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/lib/IO/Exception/BinaryFileNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\Exception\\\\IOException\\:\\:__construct\\(\\) has parameter \\$message with no type specified\\.$#" + count: 1 + path: src/lib/IO/Exception/IOException.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\Exception\\\\InvalidBinaryAbsolutePathException\\:\\:__construct\\(\\) has parameter \\$code with no type specified\\.$#" + count: 1 + path: src/lib/IO/Exception/InvalidBinaryAbsolutePathException.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\Exception\\\\InvalidBinaryAbsolutePathException\\:\\:__construct\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: src/lib/IO/Exception/InvalidBinaryAbsolutePathException.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\Exception\\\\InvalidBinaryFileIdException\\:\\:__construct\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: src/lib/IO/Exception/InvalidBinaryFileIdException.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\Exception\\\\InvalidBinaryPrefixException\\:\\:__construct\\(\\) has parameter \\$code with no type specified\\.$#" + count: 1 + path: src/lib/IO/Exception/InvalidBinaryPrefixException.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\Exception\\\\InvalidBinaryPrefixException\\:\\:__construct\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: src/lib/IO/Exception/InvalidBinaryPrefixException.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\Exception\\\\InvalidBinaryPrefixException\\:\\:__construct\\(\\) has parameter \\$prefix with no type specified\\.$#" + count: 1 + path: src/lib/IO/Exception/InvalidBinaryPrefixException.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\:\\:create\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\:\\:deleteDirectory\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\:\\:getContents\\(\\) has parameter \\$spiBinaryFileId with no type specified\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\Flysystem\\:\\:getContents\\(\\) has parameter \\$spiBinaryFileId with no type specified\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler/Flysystem.php + + - + message: "#^Parameter \\#2 \\$e of class Ibexa\\\\Core\\\\IO\\\\Exception\\\\IOException constructor expects Exception\\|null, League\\\\Flysystem\\\\FilesystemException given\\.$#" + count: 2 + path: src/lib/IO/IOBinarydataHandler/Flysystem.php + + - + message: "#^Parameter \\#2 \\$previous of class Ibexa\\\\Core\\\\IO\\\\Exception\\\\BinaryFileNotFoundException constructor expects Exception\\|null, League\\\\Flysystem\\\\FilesystemException given\\.$#" + count: 3 + path: src/lib/IO/IOBinarydataHandler/Flysystem.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\SiteAccessDependentBinaryDataHandler\\:\\:create\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler/SiteAccessDependentBinaryDataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\SiteAccessDependentBinaryDataHandler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler/SiteAccessDependentBinaryDataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\SiteAccessDependentBinaryDataHandler\\:\\:deleteDirectory\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler/SiteAccessDependentBinaryDataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\SiteAccessDependentBinaryDataHandler\\:\\:getContents\\(\\) has parameter \\$spiBinaryFileId with no type specified\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler/SiteAccessDependentBinaryDataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\SiteAccessDependentBinaryDataHandler\\:\\:getHandler\\(\\) should return Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler but returns object\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler/SiteAccessDependentBinaryDataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\SiteAccessDependentMetadataHandler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler/SiteAccessDependentMetadataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\SiteAccessDependentMetadataHandler\\:\\:deleteDirectory\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler/SiteAccessDependentMetadataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\SiteAccessDependentMetadataHandler\\:\\:deleteDirectory\\(\\) has parameter \\$spiPath with no type specified\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler/SiteAccessDependentMetadataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\SiteAccessDependentMetadataHandler\\:\\:getHandler\\(\\) should return Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler but returns object\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler/SiteAccessDependentMetadataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\SiteAccessDependentMetadataHandler\\:\\:getMimeType\\(\\) has parameter \\$spiBinaryFileId with no type specified\\.$#" + count: 1 + path: src/lib/IO/IOBinarydataHandler/SiteAccessDependentMetadataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\:\\:deleteDirectory\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\:\\:deleteDirectory\\(\\) has parameter \\$spiPath with no type specified\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\:\\:getMimeType\\(\\) has parameter \\$spiBinaryFileId with no type specified\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\Flysystem\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler/Flysystem.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\Flysystem\\:\\:deleteDirectory\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler/Flysystem.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\Flysystem\\:\\:deleteDirectory\\(\\) has parameter \\$spiPath with no type specified\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler/Flysystem.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\Flysystem\\:\\:getMimeType\\(\\) has parameter \\$spiBinaryFileId with no type specified\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler/Flysystem.php + + - + message: "#^Parameter \\#2 \\$e of class Ibexa\\\\Core\\\\IO\\\\Exception\\\\IOException constructor expects Exception\\|null, League\\\\Flysystem\\\\FilesystemException given\\.$#" + count: 2 + path: src/lib/IO/IOMetadataHandler/Flysystem.php + + - + message: "#^Cannot access offset 'datatype' on array\\<string, mixed\\>\\|false\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler/LegacyDFSCluster.php + + - + message: "#^Cannot call method rowCount\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 3 + path: src/lib/IO/IOMetadataHandler/LegacyDFSCluster.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\LegacyDFSCluster\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler/LegacyDFSCluster.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\LegacyDFSCluster\\:\\:deleteDirectory\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler/LegacyDFSCluster.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\LegacyDFSCluster\\:\\:getMimeType\\(\\) has parameter \\$spiBinaryFileId with no type specified\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler/LegacyDFSCluster.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\LegacyDFSCluster\\:\\:mapArrayToSPIBinaryFile\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler/LegacyDFSCluster.php + + - + message: "#^Property Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\LegacyDFSCluster\\:\\:\\$urlDecorator \\(Ibexa\\\\Core\\\\IO\\\\UrlDecorator\\) does not accept Ibexa\\\\Core\\\\IO\\\\UrlDecorator\\|null\\.$#" + count: 1 + path: src/lib/IO/IOMetadataHandler/LegacyDFSCluster.php + + - + message: "#^Property Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\LegacyDFSCluster\\:\\:\\$urlDecorator \\(Ibexa\\\\Core\\\\IO\\\\UrlDecorator\\) in isset\\(\\) is not nullable\\.$#" + count: 2 + path: src/lib/IO/IOMetadataHandler/LegacyDFSCluster.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOService\\:\\:__construct\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/IO/IOService.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOService\\:\\:checkBinaryFileId\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOService.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOService\\:\\:deleteBinaryFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOService.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOService\\:\\:deleteDirectory\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOService.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOService\\:\\:loadBinaryFile\\(\\) should return Ibexa\\\\Core\\\\IO\\\\Values\\\\BinaryFile but returns false\\.$#" + count: 1 + path: src/lib/IO/IOService.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOService\\:\\:newBinaryCreateStructFromUploadedFile\\(\\) has parameter \\$uploadedFile with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/IO/IOService.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOService\\:\\:setPrefix\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOService.php + + - + message: "#^Parameter \\#1 \\$buffer of method Ibexa\\\\Contracts\\\\Core\\\\IO\\\\MimeTypeDetector\\:\\:getFromBuffer\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: src/lib/IO/IOService.php + + - + message: "#^Parameter \\#2 \\$length of function fread expects int\\<1, max\\>, int\\<0, max\\> given\\.$#" + count: 1 + path: src/lib/IO/IOService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\IO\\\\BinaryFile\\:\\:\\$uri \\(string\\) in isset\\(\\) is not nullable\\.$#" + count: 2 + path: src/lib/IO/IOService.php + + - + message: "#^Property Ibexa\\\\Core\\\\IO\\\\IOService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/IO/IOService.php + + - + message: "#^Property Ibexa\\\\Core\\\\IO\\\\Values\\\\BinaryFileCreateStruct\\:\\:\\$mimeType \\(string\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/IO/IOService.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOServiceInterface\\:\\:deleteBinaryFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOServiceInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOServiceInterface\\:\\:deleteDirectory\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOServiceInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOServiceInterface\\:\\:newBinaryCreateStructFromUploadedFile\\(\\) has parameter \\$uploadedFile with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/IO/IOServiceInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\IOServiceInterface\\:\\:setPrefix\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/IOServiceInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\MetadataHandler\\:\\:extract\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/IO/MetadataHandler.php + + - + message: "#^Cannot access offset 'mime' on array\\{0\\: int\\<0, max\\>, 1\\: int\\<0, max\\>, 2\\: int, 3\\: string, mime\\: string, channels\\?\\: int, bits\\?\\: int\\}\\|false\\.$#" + count: 1 + path: src/lib/IO/MetadataHandler/ImageSize.php + + - + message: "#^Cannot access offset 0 on array\\{0\\: int\\<0, max\\>, 1\\: int\\<0, max\\>, 2\\: int, 3\\: string, mime\\: string, channels\\?\\: int, bits\\?\\: int\\}\\|false\\.$#" + count: 1 + path: src/lib/IO/MetadataHandler/ImageSize.php + + - + message: "#^Cannot access offset 1 on array\\{0\\: int\\<0, max\\>, 1\\: int\\<0, max\\>, 2\\: int, 3\\: string, mime\\: string, channels\\?\\: int, bits\\?\\: int\\}\\|false\\.$#" + count: 1 + path: src/lib/IO/MetadataHandler/ImageSize.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\MetadataHandler\\\\ImageSize\\:\\:extract\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/IO/MetadataHandler/ImageSize.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\MimeTypeDetector\\\\FileInfo\\:\\:getFromBuffer\\(\\) should return string but returns string\\|false\\.$#" + count: 1 + path: src/lib/IO/MimeTypeDetector/FileInfo.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\MimeTypeDetector\\\\FileInfo\\:\\:getFromPath\\(\\) should return string but returns string\\|false\\.$#" + count: 1 + path: src/lib/IO/MimeTypeDetector/FileInfo.php + + - + message: "#^Property Ibexa\\\\Core\\\\IO\\\\MimeTypeDetector\\\\FileInfo\\:\\:\\$fileInfo \\(finfo\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/IO/MimeTypeDetector/FileInfo.php + + - + message: "#^Dead catch \\- Ibexa\\\\Core\\\\IO\\\\Exception\\\\BinaryFileNotFoundException is never thrown in the try block\\.$#" + count: 1 + path: src/lib/IO/TolerantIOService.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\TolerantIOService\\:\\:deleteBinaryFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/TolerantIOService.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\TolerantIOService\\:\\:logMissingFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/TolerantIOService.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\TolerantIOService\\:\\:logMissingFile\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: src/lib/IO/TolerantIOService.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\TolerantIOService\\:\\:setLogger\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/IO/TolerantIOService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\IO\\\\BinaryFile\\:\\:\\$uri \\(string\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/IO/TolerantIOService.php + + - + message: "#^Property Ibexa\\\\Core\\\\IO\\\\TolerantIOService\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) does not accept Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/lib/IO/TolerantIOService.php + + - + message: "#^Property Ibexa\\\\Core\\\\IO\\\\TolerantIOService\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/IO/TolerantIOService.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\UrlDecorator\\:\\:undecorate\\(\\) has parameter \\$uri with no type specified\\.$#" + count: 1 + path: src/lib/IO/UrlDecorator.php + + - + message: "#^Method Ibexa\\\\Core\\\\IO\\\\UrlDecorator\\\\Prefix\\:\\:undecorate\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: src/lib/IO/UrlDecorator/Prefix.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\BlockingLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/BlockingLimitationType.php + + - + message: "#^Return type \\(void\\) of method Ibexa\\\\Core\\\\Limitation\\\\ChangeOwnerLimitationType\\:\\:valueSchema\\(\\) should be compatible with return type \\(array\\|int\\) of method Ibexa\\\\Contracts\\\\Core\\\\Limitation\\\\Type\\:\\:valueSchema\\(\\)$#" + count: 1 + path: src/lib/Limitation/ChangeOwnerLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\ContentTypeLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/ContentTypeLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\LanguageLimitationType\\:\\:buildValue\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Limitation/LanguageLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\LanguageLimitationType\\:\\:evaluateContentCreateStruct\\(\\) never returns null so it can be removed from the return type\\.$#" + count: 1 + path: src/lib/Limitation/LanguageLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\LanguageLimitationType\\:\\:evaluateLocationTarget\\(\\) never returns null so it can be removed from the return type\\.$#" + count: 1 + path: src/lib/Limitation/LanguageLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\LanguageLimitationType\\:\\:valueSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Limitation/LanguageLimitationType.php + + - + message: "#^Property Ibexa\\\\Core\\\\Limitation\\\\LanguageLimitationType\\:\\:\\$versionTargetEvaluators \\(array\\<Ibexa\\\\Core\\\\Limitation\\\\LanguageLimitation\\\\VersionTargetEvaluator\\>\\) does not accept iterable\\<Ibexa\\\\Core\\\\Limitation\\\\LanguageLimitation\\\\VersionTargetEvaluator\\>\\.$#" + count: 1 + path: src/lib/Limitation/LanguageLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\LocationLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/LocationLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\LocationLimitationType\\:\\:evaluateForContentCreateStruct\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Limitation/LocationLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\MemberOfLimitationType\\:\\:getCurrentUserGroupsIdList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Limitation/MemberOfLimitationType.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: src/lib/Limitation/MemberOfLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\NewObjectStateLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/NewObjectStateLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\NewSectionLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/NewSectionLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\NewSectionLimitationType\\:\\:doEvaluate\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Limitation/NewSectionLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\NewSectionLimitationType\\:\\:getCriterionByTarget\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Limitation/NewSectionLimitationType.php + + - + message: "#^PHPDoc tag @param for parameter \\$targets with type array\\|null is not subtype of native type array\\.$#" + count: 1 + path: src/lib/Limitation/NewSectionLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\ObjectStateLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/ObjectStateLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\ObjectStateLimitationType\\:\\:getObjectStateIdsToVerify\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Limitation/ObjectStateLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\ObjectStateLimitationType\\:\\:getObjectStateIdsToVerify\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Limitation/ObjectStateLimitationType.php + + - + message: "#^Parameter \\#2 \\$limitationValues of method Ibexa\\\\Core\\\\Limitation\\\\ObjectStateLimitationType\\:\\:areObjectStatesMatchingTheLimitation\\(\\) expects array\\<string\\>, array\\<int\\> given\\.$#" + count: 1 + path: src/lib/Limitation/ObjectStateLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\OwnerLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/OwnerLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/ParentContentTypeLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationType\\:\\:evaluateForContentCreateStruct\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Limitation/ParentContentTypeLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\ParentDepthLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/ParentDepthLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\ParentDepthLimitationType\\:\\:evaluateForContentCreateStruct\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Limitation/ParentDepthLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\ParentOwnerLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/ParentOwnerLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\ParentUserGroupLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/ParentUserGroupLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\SectionLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/SectionLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\SectionLimitationType\\:\\:evaluate\\(\\) should return bool but returns null\\.$#" + count: 1 + path: src/lib/Limitation/SectionLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\SiteAccessLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/SiteAccessLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\StatusLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/StatusLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\SubtreeLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/SubtreeLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\SubtreeLimitationType\\:\\:evaluate\\(\\) should return bool but returns null\\.$#" + count: 2 + path: src/lib/Limitation/SubtreeLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\SubtreeLimitationType\\:\\:evaluateForContentCreateStruct\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Limitation/SubtreeLimitationType.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Handler\\:\\:load\\(\\) expects int, string given\\.$#" + count: 1 + path: src/lib/Limitation/SubtreeLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\TargetOnlyLimitationType\\:\\:getCriterionByTarget\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Limitation/TargetOnlyLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Limitation\\\\UserGroupLimitationType\\:\\:acceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Limitation/UserGroupLimitationType.php + + - + message: "#^PHPDoc tag @var does not specify variable name\\.$#" + count: 1 + path: src/lib/Limitation/UserGroupLimitationType.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Exception\\\\NoViewTemplateException\\:\\:getView\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Exception/NoViewTemplateException.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Exception\\\\ParameterNotFoundException\\:\\:__construct\\(\\) has parameter \\$namespace with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Exception/ParameterNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Exception\\\\ParameterNotFoundException\\:\\:__construct\\(\\) has parameter \\$paramName with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Exception/ParameterNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Exception\\\\ParameterNotFoundException\\:\\:__construct\\(\\) has parameter \\$triedScopes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Exception/ParameterNotFoundException.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\RepositoryAware\\:\\:setRepository\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/RepositoryAware.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\RepositoryAwareInterface\\:\\:setRepository\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/RepositoryAwareInterface.php + + - + message: "#^Cannot access offset 'config' on array\\|ArrayObject\\<\\(int\\|string\\), mixed\\>\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Component/Serializer/CompoundMatcherNormalizer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\SimplifiedRequestNormalizer\\:\\:supportsNormalization\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Component/Serializer/SimplifiedRequestNormalizer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\ConfigDumperInterface\\:\\:dump\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/ConfigDumperInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\ConfigDumperInterface\\:\\:dump\\(\\) has parameter \\$configArray with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/ConfigDumperInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\QueryController\\:\\:runPagingQuery\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Content/QueryController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\QueryController\\:\\:runQuery\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Content/QueryController.php + + - + message: "#^Parameter \\#1 \\$adapter of class Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\Pagerfanta constructor expects Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\SearchResultAdapter, Pagerfanta\\\\Adapter\\\\AdapterInterface given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Content/QueryController.php + + - + message: "#^Parameter \\#1 \\$query of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\QueryController\\:\\:getAdapter\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query, Ibexa\\\\Core\\\\QueryType\\\\QueryType given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Content/QueryController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\ViewController\\:\\:handleViewException\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Content/ViewController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\ViewController\\:\\:handleViewException\\(\\) has parameter \\$contentId with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Content/ViewController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\ViewController\\:\\:handleViewException\\(\\) has parameter \\$locationId with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Content/ViewController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\ViewController\\:\\:handleViewException\\(\\) has parameter \\$params with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Content/ViewController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\ViewController\\:\\:handleViewException\\(\\) has parameter \\$viewType with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Content/ViewController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\ViewController\\:\\:performAccessChecks\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Content/ViewController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\ViewController\\:\\:renderContent\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Content/ViewController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\ViewController\\:\\:renderLocation\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Content/ViewController.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Content\\\\ViewController\\:\\:\\$authorizationChecker is never read, only written\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Content/ViewController.php + + - + message: "#^Cannot call method get\\(\\) on Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null\\.$#" + count: 7 + path: src/lib/MVC/Symfony/Controller/Controller.php + + - + message: "#^Cannot call method getCurrentRequest\\(\\) on object\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Controller.php + + - + message: "#^Cannot call method isGranted\\(\\) on object\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Controller.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Controller\\:\\:getConfigResolver\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface but returns object\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Controller.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Controller\\:\\:getEventDispatcher\\(\\) should return Symfony\\\\Component\\\\EventDispatcher\\\\EventDispatcherInterface but returns object\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Controller.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Controller\\:\\:getLogger\\(\\) should return Psr\\\\Log\\\\LoggerInterface\\|null but returns object\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Controller.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Controller\\:\\:getRepository\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository but returns object\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Controller.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Controller\\:\\:getTemplateEngine\\(\\) should return Symfony\\\\Component\\\\Templating\\\\EngineInterface but returns object\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Controller.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\Controller\\:\\:render\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/Controller.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:createQueryView\\(\\) has parameter \\$results with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/QueryRenderController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:getAdapter\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/QueryRenderController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:getAdapter\\(\\) should return Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\SearchResultAdapter but returns Pagerfanta\\\\Adapter\\\\AdapterInterface\\.$#" + count: 2 + path: src/lib/MVC/Symfony/Controller/QueryRenderController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:renderQuery\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/QueryRenderController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:resolveOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/QueryRenderController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderController\\:\\:resolveOptions\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/QueryRenderController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\SecurityController\\:\\:loginAction\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Controller/SecurityController.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\APIContentExceptionEvent\\:\\:__construct\\(\\) has parameter \\$contentMeta with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Event/APIContentExceptionEvent.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\APIContentExceptionEvent\\:\\:getContentMeta\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Event/APIContentExceptionEvent.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\APIContentExceptionEvent\\:\\:setContentView\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Event/APIContentExceptionEvent.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\APIContentExceptionEvent\\:\\:\\$contentMeta type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Event/APIContentExceptionEvent.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\APIContentExceptionEvent\\:\\:\\$contentView \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Event/APIContentExceptionEvent.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\ContentCacheClearEvent\\:\\:addLocationToClear\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Event/ContentCacheClearEvent.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\ContentCacheClearEvent\\:\\:setLocationsToClear\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Event/ContentCacheClearEvent.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\InteractiveLoginEvent\\:\\:\\$apiUser \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Event/InteractiveLoginEvent.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\PostSiteAccessMatchEvent\\:\\:__construct\\(\\) has parameter \\$requestType with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Event/PostSiteAccessMatchEvent.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\RouteReferenceGenerationEvent\\:\\:setRouteReference\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Event/RouteReferenceGenerationEvent.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:getContent\\(\\)\\.$#" + count: 1 + path: src/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriber.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:getLocation\\(\\)\\.$#" + count: 1 + path: src/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\ContentViewTwigVariablesSubscriber\\:\\:isExpressionParameter\\(\\) has parameter \\$twigVariable with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\ContentViewTwigVariablesSubscriber\\:\\:processParameterRecursive\\(\\) has parameter \\$twigVariable with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\LanguageSwitchListener\\:\\:onRouteReferenceGeneration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/EventListener/LanguageSwitchListener.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\:\\:setSubMatchers\\(\\)\\.$#" + count: 1 + path: src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\SiteAccessMatchListener\\:\\:deserializeMatcher\\(\\) has parameter \\$serializedSubMatchers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php + + - + message: "#^Parameter \\#1 \\$serializedGroups of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\SiteAccessMatchListener\\:\\:buildGroups\\(\\) expects array\\<array\\{name\\: string\\}\\>, array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccessGroup\\> given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php + + - + message: "#^Parameter \\#2 \\$haystack of function in_array expects array, array\\<string, class\\-string\\>\\|false given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php + + - + message: "#^Parameter \\#2 \\$matcherFQCN of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\SiteAccessMatchListener\\:\\:deserializeMatcher\\(\\) expects string, Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\ExpressionLanguage\\\\TwigVariableProviderExtension\\:\\:hasParameterProvider\\(\\) has parameter \\$variables with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/ExpressionLanguage/TwigVariableProviderExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\BinaryBase\\\\ContentDownloadUrlGenerator\\:\\:generate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/BinaryBase/ContentDownloadUrlGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\BinaryBase\\\\ContentDownloadUrlGenerator\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/BinaryBase/ContentDownloadUrlGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\BinaryBase\\\\ContentDownloadUrlGenerator\\:\\:getStoragePathForField\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/BinaryBase/ContentDownloadUrlGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\ImageAsset\\\\ParameterProvider\\:\\:getViewParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/ImageAsset/ParameterProvider.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\ImageAsset\\\\ParameterProvider\\:\\:\\$fieldTypeService \\(Ibexa\\\\Core\\\\Repository\\\\FieldTypeService\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\FieldTypeService\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/ImageAsset/ParameterProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\Relation\\\\ParameterProvider\\:\\:getViewParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/Relation/ParameterProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\RelationList\\\\ParameterProvider\\:\\:getViewParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/RelationList/ParameterProvider.php + + - + message: "#^Cannot call method diff\\(\\) on DateTimeImmutable\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/User/ParameterProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\User\\\\ParameterProvider\\:\\:getViewParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/User/ParameterProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProvider\\\\LocaleParameterProvider\\:\\:getViewParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProvider\\\\LocaleParameterProvider\\:\\:setRequestStack\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProviderInterface\\:\\:getViewParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/View/ParameterProviderInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProviderRegistry\\:\\:setParameterProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/View/ParameterProviderRegistry.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProviderRegistry\\:\\:\\$providers has no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/View/ParameterProviderRegistry.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProviderRegistryInterface\\:\\:setParameterProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/FieldType/View/ParameterProviderRegistryInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\LocaleConverter\\:\\:__construct\\(\\) has parameter \\$conversionMap with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Locale/LocaleConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\LocaleConverter\\:\\:convertToEz\\(\\) should return string\\|null but empty return statement found\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Locale/LocaleConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\LocaleConverter\\:\\:convertToPOSIX\\(\\) should return string\\|null but empty return statement found\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Locale/LocaleConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\LocaleConverter\\:\\:\\$conversionMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Locale/LocaleConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\LocaleConverter\\:\\:\\$reverseConversionMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Locale/LocaleConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\UserLanguagePreferenceProvider\\:\\:__construct\\(\\) has parameter \\$languageCodesMap with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Locale/UserLanguagePreferenceProvider.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\UserLanguagePreferenceProvider\\:\\:\\$languageCodesMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Locale/UserLanguagePreferenceProvider.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\Symfony\\\\Component\\\\HttpFoundation\\\\Request request to retrieve information from, use current if null\\)\\: Unexpected token \"request\", expected variable at offset 162$#" + count: 1 + path: src/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ClassNameMatcherFactory\\:\\:__construct\\(\\) has parameter \\$matchConfig with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ClassNameMatcherFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ClassNameMatcherFactory\\:\\:getMatcher\\(\\) should return Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ViewMatcherInterface but returns object\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ClassNameMatcherFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ClassNameMatcherFactory\\:\\:match\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ClassNameMatcherFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ClassNameMatcherFactory\\:\\:setMatchConfig\\(\\) has parameter \\$matchConfig with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ClassNameMatcherFactory.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ClassNameMatcherFactory\\:\\:\\$alreadyMatched with generic class SplObjectStorage does not specify its types\\: TObject, TData$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ClassNameMatcherFactory.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ClassNameMatcherFactory\\:\\:\\$matchConfig type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ClassNameMatcherFactory.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ClassNameMatcherFactory\\:\\:\\$matchers \\(array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ViewMatcherInterface\\>\\) does not accept array\\<object\\>\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ClassNameMatcherFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ConfigurableMatcherFactoryInterface\\:\\:setMatchConfig\\(\\) has parameter \\$matchConfig with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ConfigurableMatcherFactoryInterface.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/Depth.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTypeGroup\\:\\:matchContentTypeId\\(\\) has parameter \\$contentTypeId with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroup.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\LocationRemote\\:\\:matchContentInfo\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/Id/LocationRemote.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\LocationRemote\\:\\:matchLocation\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/Id/LocationRemote.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\Location\\:\\:getContentInfo\\(\\)\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentType.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ParentContentType\\:\\:loadParentLocation\\(\\) has parameter \\$locationId with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentType.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ParentContentType\\:\\:loadParentLocation\\(\\) should return Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\Location but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentType.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentType.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentLocation.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentType.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\MultipleValued\\:\\:getValues\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/MultipleValued.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\MultipleValued\\:\\:setMatchingConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/MultipleValued.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\MultipleValued\\:\\:\\$values type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/MultipleValued.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\UrlAlias\\:\\:setMatchingConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ContentBased/UrlAlias.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\MatcherFactoryInterface\\:\\:setMatchConfig\\(\\)\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecorator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\DynamicallyConfiguredMatcherFactoryDecorator\\:\\:match\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecorator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\MatcherFactoryInterface\\:\\:match\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/MatcherFactoryInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ViewMatcherInterface\\:\\:setMatchingConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Matcher/ViewMatcherInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:doGenerate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:generate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:setLogger\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:setRequestContext\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:setSiteAccessRouter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator.php + + - + message: "#^Parameter \\#1 \\$httpPort of method Symfony\\\\Component\\\\Routing\\\\RequestContext\\:\\:setHttpPort\\(\\) expects int, string given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator.php + + - + message: "#^Parameter \\#1 \\$httpsPort of method Symfony\\\\Component\\\\Routing\\\\RequestContext\\:\\:setHttpsPort\\(\\) expects int, string given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) does not accept Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator.php + + - + message: "#^Cannot access property \\$attributes on Symfony\\\\Component\\\\HttpFoundation\\\\Request\\|null\\.$#" + count: 2 + path: src/lib/MVC/Symfony/Routing/Generator/RouteReferenceGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\RouteReferenceGenerator\\:\\:generate\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/RouteReferenceGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\RouteReferenceGenerator\\:\\:setRequestStack\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/RouteReferenceGenerator.php + + - + message: "#^Parameter \\#2 \\$request of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\RouteReferenceGenerationEvent constructor expects Symfony\\\\Component\\\\HttpFoundation\\\\Request, Symfony\\\\Component\\\\HttpFoundation\\\\Request\\|null given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/RouteReferenceGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\RouteReferenceGeneratorInterface\\:\\:generate\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/RouteReferenceGeneratorInterface.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\>\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:__construct\\(\\) has parameter \\$unsafeCharMap with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:createQueryString\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:doGenerate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:loadLocation\\(\\) should return Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:setExcludedUriPrefixes\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:setExcludedUriPrefixes\\(\\) has parameter \\$excludedUriPrefixes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:setRootLocationId\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:\\$excludedUriPrefixes type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:\\$pathPrefixMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:\\$repository \\(Ibexa\\\\Core\\\\Repository\\\\Repository\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:\\$unsafeCharMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReference\\:\\:__construct\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/RouteReference.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReference\\:\\:__construct\\(\\) has parameter \\$route with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/RouteReference.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReference\\:\\:getParams\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/RouteReference.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReference\\:\\:has\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/RouteReference.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReference\\:\\:has\\(\\) has parameter \\$parameterName with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/RouteReference.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReference\\:\\:remove\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/RouteReference.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReference\\:\\:set\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/RouteReference.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReference\\:\\:setRoute\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/RouteReference.php + + - + message: "#^Method Symfony\\\\Component\\\\HttpFoundation\\\\ParameterBag\\:\\:get\\(\\) invoked with 3 parameters, 1\\-2 required\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/RouteReference.php + + - + message: "#^Cannot access offset 'pathinfo' on array\\{scheme\\?\\: string, host\\?\\: string, port\\?\\: int\\<0, 65535\\>, user\\?\\: string, pass\\?\\: string, path\\?\\: string, query\\?\\: string, fragment\\?\\: string\\}\\|false\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\:\\:setHeaders\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\:\\:setHeaders\\(\\) has parameter \\$headers with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\:\\:setHost\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\:\\:setLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\:\\:setLanguages\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\:\\:setPathinfo\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\:\\:setPort\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\:\\:setQueryParams\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\:\\:setQueryParams\\(\\) has parameter \\$queryParams with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\:\\:setScheme\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\:\\:\\$headers type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\:\\:\\$languages type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\:\\:\\$queryParams type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Unsafe usage of new static\\(\\)\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/SimplifiedRequest.php + + - + message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:generate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:getRouteDebugMessage\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:getUrlAlias\\(\\) has parameter \\$pathinfo with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:match\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:matchRequest\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:setContext\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:setRootLocationId\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:supportsObject\\(\\) has parameter \\$object with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php + + - + message: "#^Parameter \\#1 \\$rootLocationId of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\UrlAliasGenerator\\:\\:getPathPrefixByRootLocationId\\(\\) expects int, int\\|string given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) does not accept Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlAliasRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlWildcardRouter\\:\\:generate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlWildcardRouter\\:\\:getRouteDebugMessage\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlWildcardRouter\\:\\:match\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlWildcardRouter\\:\\:matchRequest\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlWildcardRouter\\:\\:setLogger\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\AnonymousAuthenticationProvider\\:\\:setConfigResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authentication/AnonymousAuthenticationProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\AnonymousAuthenticationProvider\\:\\:setPermissionResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authentication/AnonymousAuthenticationProvider.php + + - + message: "#^Parameter \\#3 \\$firewallName of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\DetermineTargetUrlEvent constructor expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authentication/DefaultAuthenticationSuccessHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RememberMeRepositoryAuthenticationProvider\\:\\:setPermissionResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProvider.php + + - + message: "#^Dead catch \\- Ibexa\\\\Core\\\\Repository\\\\User\\\\Exception\\\\UnsupportedPasswordHashType is never thrown in the try block\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RepositoryAuthenticationProvider\\:\\:checkAuthentication\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RepositoryAuthenticationProvider\\:\\:setConstantAuthTime\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RepositoryAuthenticationProvider\\:\\:setPermissionResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RepositoryAuthenticationProvider\\:\\:setUserService\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RepositoryAuthenticationProvider\\:\\:startConstantTimer\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authorization\\\\Attribute\\:\\:__construct\\(\\) has parameter \\$function with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authorization/Attribute.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authorization\\\\Attribute\\:\\:__construct\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authorization/Attribute.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authorization\\\\Attribute\\:\\:__construct\\(\\) has parameter \\$module with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authorization/Attribute.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authorization\\\\Attribute\\:\\:\\$limitations type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authorization/Attribute.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authorization\\\\Voter\\\\CoreVoter\\:\\:vote\\(\\) has parameter \\$attributes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authorization/Voter/CoreVoter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authorization\\\\Voter\\\\ValueObjectVoter\\:\\:supportsAttribute\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authorization/Voter/ValueObjectVoter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authorization\\\\Voter\\\\ValueObjectVoter\\:\\:supportsAttribute\\(\\) has parameter \\$attribute with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authorization/Voter/ValueObjectVoter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authorization\\\\Voter\\\\ValueObjectVoter\\:\\:supportsClass\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authorization/Voter/ValueObjectVoter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authorization\\\\Voter\\\\ValueObjectVoter\\:\\:supportsClass\\(\\) has parameter \\$class with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authorization/Voter/ValueObjectVoter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authorization\\\\Voter\\\\ValueObjectVoter\\:\\:vote\\(\\) has parameter \\$attributes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Authorization/Voter/ValueObjectVoter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListener\\:\\:__construct\\(\\) has parameter \\$fragmentPath with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/EventListener/SecurityListener.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListener\\:\\:checkSiteAccessPermission\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/EventListener/SecurityListener.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListener\\:\\:isMasterRequest\\(\\) has parameter \\$requestType with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/EventListener/SecurityListener.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListener\\:\\:onInteractiveLogin\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/EventListener/SecurityListener.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListener\\:\\:onKernelRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/EventListener/SecurityListener.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Exception\\\\UnauthorizedSiteAccessException\\:\\:__construct\\(\\) has parameter \\$username with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/Exception/UnauthorizedSiteAccessException.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtils\\:\\:analyzeLink\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/HttpUtils.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtils\\:\\:analyzeLink\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/HttpUtils.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtils\\:\\:generateUri\\(\\) has parameter \\$request with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/HttpUtils.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtils\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/HttpUtils.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtils\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/HttpUtils.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\InteractiveLoginToken\\:\\:__construct\\(\\) has parameter \\$credentials with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/InteractiveLoginToken.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\InteractiveLoginToken\\:\\:__construct\\(\\) has parameter \\$originalTokenType with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/InteractiveLoginToken.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\InteractiveLoginToken\\:\\:__construct\\(\\) has parameter \\$providerKey with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/InteractiveLoginToken.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\:\\:__construct\\(\\) has parameter \\$roles with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/User.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\:\\:eraseCredentials\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/User.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\:\\:getSalt\\(\\) should return string but returns null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/User.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\:\\:setAPIUser\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/User.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\EmailProvider\\:\\:loadUserByUsername\\(\\) has parameter \\$user with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/User/EmailProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\UsernameProvider\\:\\:loadUserByUsername\\(\\) has parameter \\$user with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/User/UsernameProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserInterface\\:\\:setAPIUser\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/UserInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrapped\\:\\:eraseCredentials\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/UserWrapped.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrapped\\:\\:setAPIUser\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/UserWrapped.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrapped\\:\\:setWrappedUser\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Security/UserWrapped.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\:\\:__construct\\(\\) has parameter \\$groups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\:\\:__construct\\(\\) has parameter \\$matcher with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\:\\:jsonSerialize\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher.php + + - + message: "#^Access to undefined constant static\\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Compound\\)\\:\\:NAME\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Compound.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Compound\\:\\:__construct\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Compound.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Compound\\:\\:setMatcherBuilder\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Compound.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Compound\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Compound.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Compound\\:\\:setSubMatchers\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Compound.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Compound\\:\\:\\$config type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Compound.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Compound\\:\\:\\$matchersMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Compound.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Compound\\\\LogicalAnd\\:\\:reverseMatch\\(\\) should return Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\VersatileMatcher\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Compound/LogicalAnd.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Compound\\\\LogicalOr\\:\\:reverseMatch\\(\\) should return Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\VersatileMatcher\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Compound/LogicalOr.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\CompoundInterface\\:\\:setMatcherBuilder\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/CompoundInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\CompoundInterface\\:\\:setSubMatchers\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/CompoundInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostElement\\:\\:__construct\\(\\) has parameter \\$elementNumber with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostElement.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostElement\\:\\:getHostElements\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostElement.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostElement\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostElement.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostElement\\:\\:\\$hostElements \\(array\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostElement.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostElement\\:\\:\\$hostElements is never written, only read\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostElement.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostElement\\:\\:\\$hostElements type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostElement.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostElement\\:\\:\\$request \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostElement.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostElement.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostText\\:\\:__construct\\(\\) has parameter \\$siteAccessesConfiguration with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostText.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostText\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostText.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostText\\:\\:\\$prefix has no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostText.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostText\\:\\:\\$siteAccessesConfiguration is never read, only written\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostText.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostText\\:\\:\\$siteAccessesConfiguration type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostText.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostText\\:\\:\\$suffix has no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/HostText.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Map\\:\\:__construct\\(\\) has parameter \\$map with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Map.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Map\\:\\:getReverseMap\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Map.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Map\\:\\:getReverseMap\\(\\) has parameter \\$defaultSiteAccess with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Map.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Map\\:\\:setMapKey\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Map.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Map\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Map.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Map\\:\\:\\$map type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Map.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Map\\:\\:\\$reverseMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Map.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Map\\\\Host\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Map/Host.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Map\\\\Port\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Map/Port.php + + - + message: "#^Parameter \\#1 \\$key of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Map\\:\\:setMapKey\\(\\) expects string, int\\|string given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Map/Port.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Map\\\\URI\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Map/URI.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\:\\:match\\(\\) should return string\\|false but returns bool\\|string\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\:\\:setMatchElement\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\:\\:\\$itemNumber \\(string\\) does not accept int\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\:\\:\\$matchedSiteAccess \\(string\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\\\Host\\:\\:__construct\\(\\) has parameter \\$siteAccessesConfiguration with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/Host.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\\\Host\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/Host.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\\\Host\\:\\:\\$siteAccessesConfiguration is never read, only written\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/Host.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\\\Host\\:\\:\\$siteAccessesConfiguration type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/Host.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\\\URI\\:\\:__construct\\(\\) has parameter \\$siteAccessesConfiguration with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/URI.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\\\URI\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/URI.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\\\URI\\:\\:\\$siteAccessesConfiguration is never read, only written\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/URI.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\\\URI\\:\\:\\$siteAccessesConfiguration type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/URI.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\URIElement\\:\\:__construct\\(\\) has parameter \\$elementNumber with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/URIElement.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\URIElement\\:\\:getURIElements\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/URIElement.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\URIElement\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/URIElement.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\URIElement\\:\\:\\$request \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequest\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/URIElement.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\URIElement\\:\\:\\$uriElements \\(array\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/URIElement.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\URIElement\\:\\:\\$uriElements is never written, only read\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/URIElement.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\URIElement\\:\\:\\$uriElements type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/URIElement.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/URIElement.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\URIText\\:\\:__construct\\(\\) has parameter \\$siteAccessesConfiguration with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/URIText.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\URIText\\:\\:analyseURI\\(\\) should return string but returns string\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/URIText.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\URIText\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/URIText.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\URIText\\:\\:\\$siteAccessesConfiguration is never read, only written\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/URIText.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\URIText\\:\\:\\$siteAccessesConfiguration type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Matcher/URIText.php + + - + message: "#^Call to an undefined method object\\:\\:setRequest\\(\\)\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/MatcherBuilder.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\MatcherBuilder\\:\\:buildMatcher\\(\\) should return Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher but returns object\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/MatcherBuilder.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$matcher \\\\Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\)\\: Unexpected token \"\\$matcher\", expected type at offset 9$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/MatcherBuilder.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Provider\\\\ChainSiteAccessProvider\\:\\:getSiteAccesses\\(\\) return type has no value type specified in iterable type Traversable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Provider/ChainSiteAccessProvider.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Provider\\\\ChainSiteAccessProvider\\:\\:\\$providers \\(array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\SiteAccessProviderInterface\\>\\) does not accept iterable\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\SiteAccessProviderInterface\\>\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Provider/ChainSiteAccessProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Provider\\\\StaticSiteAccessProvider\\:\\:getSiteAccesses\\(\\) return type has no value type specified in iterable type Traversable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Provider/StaticSiteAccessProvider.php + + - + message: "#^Parameter \\#2 \\$array of function array_map expects array, array\\|string given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Provider/StaticSiteAccessProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Router\\:\\:__construct\\(\\) has parameter \\$siteAccessesConfiguration with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Router.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Router\\:\\:doMatch\\(\\) is unused\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Router.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Router\\:\\:matchByName\\(\\) should return Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null but returns object\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Router.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Router\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Router.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Router\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Router.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Router\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Router.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Router\\:\\:\\$siteAccessesConfiguration type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Router.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/Router.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\SiteAccessAware\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/SiteAccessAware.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\SiteAccessProviderInterface\\:\\:getSiteAccesses\\(\\) return type has no value type specified in iterable type Traversable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/SiteAccessProviderInterface.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\SiteAccessService\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/SiteAccessService.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\SiteAccessService\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\) on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/SiteAccess/SiteAccessService.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Exception\\\\InvalidResponseException\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Exception/InvalidResponseException.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Exception\\\\InvalidResponseException\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Exception/InvalidResponseException.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Exception\\\\InvalidResponseException\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Exception/InvalidResponseException.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Exception\\\\InvalidResponseException\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Exception/InvalidResponseException.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Exception\\\\InvalidResponseException\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Exception/InvalidResponseException.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Exception\\\\InvalidResponseException\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Exception/InvalidResponseException.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Exception\\\\InvalidResponseException\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Exception/InvalidResponseException.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Exception\\\\InvalidResponseException\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Exception/InvalidResponseException.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Exception\\\\InvalidResponseException\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Exception/InvalidResponseException.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\FieldBlockRendererInterface\\:\\:renderContentFieldEdit\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/FieldBlockRendererInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\FieldBlockRendererInterface\\:\\:renderContentFieldView\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/FieldBlockRendererInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\FieldBlockRendererInterface\\:\\:renderFieldDefinitionEdit\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/FieldBlockRendererInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\FieldBlockRendererInterface\\:\\:renderFieldDefinitionView\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/FieldBlockRendererInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelper\\:\\:getAvailableLanguages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/GlobalHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelper\\:\\:getRequestedUriString\\(\\) should return string but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/GlobalHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelper\\:\\:getSiteaccess\\(\\) should return Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/GlobalHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelper\\:\\:getSystemUriString\\(\\) should return string\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/GlobalHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelper\\:\\:getViewParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/GlobalHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelper\\:\\:getViewParameters\\(\\) should return array\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/GlobalHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelper\\:\\:getViewParametersString\\(\\) should return string but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/GlobalHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelper\\:\\:setRequestStack\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/GlobalHelper.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\RenderContentStrategy\\:\\:render\\(\\) should return string but returns string\\|false\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/RenderContentStrategy.php + + - + message: "#^Parameter \\#2 \\$request of method Symfony\\\\Component\\\\HttpKernel\\\\Fragment\\\\FragmentRendererInterface\\:\\:render\\(\\) expects Symfony\\\\Component\\\\HttpFoundation\\\\Request, Symfony\\\\Component\\\\HttpFoundation\\\\Request\\|null given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/RenderContentStrategy.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\RenderLocationStrategy\\:\\:render\\(\\) should return string but returns string\\|false\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/RenderLocationStrategy.php + + - + message: "#^Parameter \\#2 \\$request of method Symfony\\\\Component\\\\HttpKernel\\\\Fragment\\\\FragmentRendererInterface\\:\\:render\\(\\) expects Symfony\\\\Component\\\\HttpFoundation\\\\Request, Symfony\\\\Component\\\\HttpFoundation\\\\Request\\|null given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/RenderLocationStrategy.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\RenderOptions\\:\\:__construct\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/RenderOptions.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\RenderOptions\\:\\:all\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/RenderOptions.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\RenderOptions\\:\\:\\$options type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/RenderOptions.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\RenderStrategy\\:\\:__construct\\(\\) has parameter \\$strategies with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/RenderStrategy.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\RenderStrategy\\:\\:\\$strategies \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\MVC\\\\Templating\\\\RenderStrategy\\>\\) does not accept iterable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/RenderStrategy.php + + - + message: "#^Cannot access property \\$fieldTypeIdentifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php + + - + message: "#^Cannot access property \\$value on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtension\\:\\:getContentType\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtension\\:\\:getFirstFilledImageFieldIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtension\\:\\:getFunctions\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtension\\:\\:getTranslatedField\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php + + - + message: "#^Parameter \\#1 \\$content of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtension\\:\\:getContentType\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject given\\.$#" + count: 2 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtension\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) does not accept Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\CoreExtension\\:\\:getGlobals\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/CoreExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\DataAttributesExtension\\:\\:getFilters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/DataAttributesExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\DataAttributesExtension\\:\\:serializeDataAttributes\\(\\) has parameter \\$dataAttributes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/DataAttributesExtension.php + + - + message: "#^Parameter \\#1 \\$string of function htmlspecialchars expects string, string\\|false given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/DataAttributesExtension.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\FieldBlockRendererInterface\\:\\:setTwig\\(\\)\\.$#" + count: 2 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php + + - + message: "#^Cannot access property \\$fieldTypeIdentifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 3 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php + + - + message: "#^Cannot call method getFieldSettings\\(\\) on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtension\\:\\:getRenderFieldBlockParameters\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtension\\:\\:getRenderFieldBlockParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtension\\:\\:renderField\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtension\\:\\:renderFieldDefinitionSettings\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtension\\:\\:\\$fieldTypeIdentifiers type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtension\\:\\:__construct\\(\\) has parameter \\$suffixes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtension\\:\\:getFilters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtension\\:\\:getLocale\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtension\\:\\:sizeFilter\\(\\) should return string but returns string\\|false\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtension\\:\\:\\$configResolver has no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtension\\:\\:\\$localeConverter has no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtension\\:\\:\\$suffixes has no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtension\\:\\:\\$translator has no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ImageExtension\\:\\:getImageVariation\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Variation\\\\Values\\\\Variation\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/ImageExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\QueryRenderingExtension\\:\\:createControllerReference\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/QueryRenderingExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\RenderContentExtension\\:\\:renderContent\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/RenderContentExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\RenderExtension\\:\\:render\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/RenderExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\RenderLocationExtension\\:\\:renderLocation\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/RenderLocationExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\RoutingExtension\\:\\:generateUrlForObject\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\RoutingExtension\\:\\:getPath\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\RoutingExtension\\:\\:getRouteReference\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\RoutingExtension\\:\\:getUrl\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\RoutingExtension\\:\\:isUrlGenerationSafe\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtension.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:__construct\\(\\) has parameter \\$blocks with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:getBlockByName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:getBlocksByField\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:getBlocksByFieldDefinition\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:renderContentField\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:renderContentFieldEdit\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:renderContentFieldView\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:renderFieldDefinition\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:renderFieldDefinitionEdit\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:renderFieldDefinitionView\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:searchBlock\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:setTwig\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:sortResources\\(\\) has parameter \\$resources with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:sortResources\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\FieldBlockRenderer\\:\\:\\$blocks type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\CatalogueMapperFileWriter\\:\\:getEnglishFilePath\\(\\) has parameter \\$filePath with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/CatalogueMapperFileWriter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\CatalogueMapperFileWriter\\:\\:hasEnglishCatalogue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/CatalogueMapperFileWriter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\CatalogueMapperFileWriter\\:\\:hasEnglishCatalogue\\(\\) has parameter \\$foreignFilePath with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/CatalogueMapperFileWriter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\CatalogueMapperFileWriter\\:\\:loadEnglishCatalogue\\(\\) has parameter \\$domain with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/CatalogueMapperFileWriter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\CatalogueMapperFileWriter\\:\\:loadEnglishCatalogue\\(\\) has parameter \\$foreignFilePath with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/CatalogueMapperFileWriter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\CatalogueMapperFileWriter\\:\\:loadEnglishCatalogue\\(\\) has parameter \\$format with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/CatalogueMapperFileWriter.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\CatalogueMapperFileWriter\\:\\:write\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/CatalogueMapperFileWriter.php + + - + message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$args\\.$#" + count: 5 + path: src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php + + - + message: "#^Cannot call method error\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ExceptionMessageTemplateFileVisitor\\:\\:isIgnore\\(\\) has parameter \\$node with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ExceptionMessageTemplateFileVisitor\\:\\:visitPhpFile\\(\\) has parameter \\$ast with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php + + - + message: "#^Return type \\(void\\) of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ExceptionMessageTemplateFileVisitor\\:\\:enterNode\\(\\) should be compatible with return type \\(int\\|PhpParser\\\\Node\\|null\\) of method PhpParser\\\\NodeVisitor\\:\\:enterNode\\(\\)$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\FieldTypesTranslationExtractor\\:\\:extract\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/FieldTypesTranslationExtractor.php + + - + message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$args\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Access to an undefined property PhpParser\\\\Node\\\\Arg\\|PhpParser\\\\Node\\\\VariadicPlaceholder\\:\\:\\$value\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Access to an undefined property PhpParser\\\\Node\\\\Expr\\|PhpParser\\\\Node\\\\Name\\|PhpParser\\\\Node\\\\Stmt\\\\Class_\\:\\:\\$parts\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:afterTraverse\\(\\) has parameter \\$nodes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:afterTraverse\\(\\) should return array\\<PhpParser\\\\Node\\>\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:beforeTraverse\\(\\) has parameter \\$nodes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:beforeTraverse\\(\\) should return array\\<PhpParser\\\\Node\\>\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:enterNode\\(\\) should return int\\|PhpParser\\\\Node\\|null but empty return statement found\\.$#" + count: 4 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:enterNode\\(\\) should return int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:leaveNode\\(\\) should return array\\<PhpParser\\\\Node\\>\\|int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:setLogger\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:visitFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:visitPhpFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:visitPhpFile\\(\\) has parameter \\$ast with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:visitTwigFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Parameter \\#1 \\$desc of method JMS\\\\TranslationBundle\\\\Model\\\\Message\\:\\:setDesc\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Parameter \\#1 \\$meaning of method JMS\\\\TranslationBundle\\\\Model\\\\Message\\:\\:setMeaning\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\TranslatableExceptionsFileVisitor\\:\\:\\$exceptionsToExtractFrom type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php + + - + message: "#^Access to an undefined property PhpParser\\\\Node\\:\\:\\$args\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Access to an undefined property PhpParser\\\\Node\\\\Arg\\|PhpParser\\\\Node\\\\VariadicPlaceholder\\:\\:\\$value\\.$#" + count: 2 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:afterTraverse\\(\\) has parameter \\$nodes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:afterTraverse\\(\\) should return array\\<PhpParser\\\\Node\\>\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:beforeTraverse\\(\\) has parameter \\$nodes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:beforeTraverse\\(\\) should return array\\<PhpParser\\\\Node\\>\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:enterNode\\(\\) should return int\\|PhpParser\\\\Node\\|null but empty return statement found\\.$#" + count: 3 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:enterNode\\(\\) should return int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:leaveNode\\(\\) should return array\\<PhpParser\\\\Node\\>\\|int\\|PhpParser\\\\Node\\|null but return statement is missing\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:setLogger\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:visitFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:visitPhpFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:visitPhpFile\\(\\) has parameter \\$ast with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:visitTwigFile\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Parameter \\#1 \\$desc of method JMS\\\\TranslationBundle\\\\Model\\\\Message\\:\\:setDesc\\(\\) expects string, string\\|null given\\.$#" + count: 2 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Parameter \\#1 \\$meaning of method JMS\\\\TranslationBundle\\\\Model\\\\Message\\:\\:setMeaning\\(\\) expects string, string\\|null given\\.$#" + count: 2 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ValidationErrorFileVisitor\\:\\:\\$classToExtractFrom type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:__construct\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:getConfigHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:getInternalParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:getViewType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:isCacheEnabled\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:setCacheEnabled\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:setCacheEnabled\\(\\) has parameter \\$cacheEnabled with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:setConfigHash\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:setConfigHash\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:setControllerReference\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:setResponse\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:setTemplateIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:setViewType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:setViewType\\(\\) has parameter \\$viewType with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:\\$configHash type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\BaseView\\:\\:\\$parameters type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/BaseView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\ContentViewBuilder\\:\\:buildView\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Builder/ContentViewBuilder.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\ContentViewBuilder\\:\\:isEmbed\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Builder/ContentViewBuilder.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\ContentViewBuilder\\:\\:loadLocation\\(\\) has parameter \\$locationId with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Builder/ContentViewBuilder.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\ContentViewBuilder\\:\\:\\$defaultTemplates is unused\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Builder/ContentViewBuilder.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\ContentViewBuilder\\:\\:\\$defaultTemplates type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Builder/ContentViewBuilder.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\ContentViewBuilder\\:\\:\\$locationLoader \\(Ibexa\\\\Core\\\\Helper\\\\ContentInfoLocationLoader\\) does not accept Ibexa\\\\Core\\\\Helper\\\\ContentInfoLocationLoader\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Builder/ContentViewBuilder.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\ContentViewBuilder\\:\\:\\$locationLoader \\(Ibexa\\\\Core\\\\Helper\\\\ContentInfoLocationLoader\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Builder/ContentViewBuilder.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\ParametersFilter\\\\RequestAttributes\\:\\:addRequestAttributes\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Builder/ParametersFilter/RequestAttributes.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\Registry\\\\ControllerMatch\\:\\:__construct\\(\\) has parameter \\$viewBuilders with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Builder/Registry/ControllerMatch.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\Registry\\\\ControllerMatch\\:\\:addToRegistry\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Builder/Registry/ControllerMatch.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\ViewBuilder\\:\\:buildView\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Builder/ViewBuilder.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\ViewBuilderRegistry\\:\\:addToRegistry\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Builder/ViewBuilderRegistry.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\CachableView\\:\\:setCacheEnabled\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/CachableView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Configurator\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Configurator.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Configurator\\\\ViewProvider\\:\\:configure\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Configurator/ViewProvider.php + + - + message: "#^Parameter \\#1 \\$config of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:setConfigHash\\(\\) expects array, array\\|null given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Configurator/ViewProvider.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentView\\:\\:getInternalParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ContentView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentView\\:\\:setContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ContentView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentView\\:\\:setIsEmbed\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ContentView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentView\\:\\:setLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ContentView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\CustomLocationControllerChecker\\:\\:addViewProviders\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/CustomLocationControllerChecker.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\CustomLocationControllerChecker\\:\\:usesCustomController\\(\\) has parameter \\$viewMode with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/CustomLocationControllerChecker.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\EmbedView\\:\\:setIsEmbed\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/EmbedView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Event\\\\FilterViewParametersEvent\\:\\:__construct\\(\\) has parameter \\$builderParameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Event/FilterViewParametersEvent.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Event\\\\FilterViewParametersEvent\\:\\:getBuilderParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Event/FilterViewParametersEvent.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Event\\\\FilterViewParametersEvent\\:\\:getViewParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Event/FilterViewParametersEvent.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Event\\\\FilterViewParametersEvent\\:\\:\\$builderParameters type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Event/FilterViewParametersEvent.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\GenericVariableProviderRegistry\\:\\:__construct\\(\\) has parameter \\$twigVariableProviders with no value type specified in iterable type Traversable\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/GenericVariableProviderRegistry.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\LoginFormView\\:\\:getInternalParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/LoginFormView.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\LoginFormView\\:\\:\\$lastUsername \\(string\\) does not accept string\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/LoginFormView.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:__construct\\(\\) has parameter \\$viewBaseLayout with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:__construct\\(\\) has parameter \\$viewConfigurator with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:addContentViewProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:addLocationViewProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:addViewProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:addViewProvider\\(\\) has parameter \\$property with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:getAllContentViewProviders\\(\\) should return array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewProvider\\> but returns array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Content\\|Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Location\\>\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:getAllLocationViewProviders\\(\\) should return array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewProvider\\> but returns array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Content\\|Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Location\\>\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:renderContent\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:renderContentView\\(\\) has parameter \\$defaultParams with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:renderLocation\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:sortViewProviders\\(\\) has parameter \\$property with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:\\$contentViewProviders type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:\\$locationViewProviders type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:\\$logger \\(Psr\\\\Log\\\\LoggerInterface\\) does not accept Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:\\$sortedContentViewProviders \\(array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Content\\>\\) does not accept array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Content\\|Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Location\\>\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Manager\\:\\:\\$sortedLocationViewProviders \\(array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Location\\>\\) does not accept array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Content\\|Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Location\\>\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Manager.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ParametersInjector\\:\\:injectViewParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ParametersInjector.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ParametersInjector\\:\\:injectViewParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ParametersInjector.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ParametersInjector\\\\CustomParameters\\:\\:injectCustomParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ParametersInjector/CustomParameters.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ParametersInjector\\\\EmbedObjectParameters\\:\\:injectEmbedObjectParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ParametersInjector/EmbedObjectParameters.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ParametersInjector\\\\EventDispatcherInjector\\:\\:injectViewParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ParametersInjector/EventDispatcherInjector.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ParametersInjector\\\\EventDispatcherInjector\\:\\:injectViewParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ParametersInjector/EventDispatcherInjector.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ParametersInjector\\\\NoLayout\\:\\:injectCustomParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ParametersInjector/NoLayout.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ParametersInjector\\\\ValueObjectsIds\\:\\:injectValueObjectsIds\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ParametersInjector/ValueObjectsIds.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ParametersInjector\\\\ViewbaseLayout\\:\\:__construct\\(\\) has parameter \\$viewbaseLayout with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ParametersInjector/ViewbaseLayout.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ParametersInjector\\\\ViewbaseLayout\\:\\:injectViewbaseLayout\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ParametersInjector/ViewbaseLayout.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Configured\\:\\:buildContentView\\(\\) has parameter \\$viewConfig with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Provider/Configured.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Configured\\:\\:getView\\(\\) should return Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View but returns null\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Provider/Configured.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Registry\\:\\:setViewProviders\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Provider/Registry.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Registry\\:\\:setViewProviders\\(\\) has parameter \\$viewProviders with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Provider/Registry.php + + - + message: "#^Parameter \\#1 \\$name of method Twig\\\\Environment\\:\\:render\\(\\) expects string\\|Twig\\\\TemplateWrapper, \\(Closure\\)\\|string given\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/Renderer/TemplateRenderer.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:getConfigHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:getViewType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:setConfigHash\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:setConfigHash\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:setControllerReference\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:setResponse\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:setTemplateIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:setViewType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:setViewType\\(\\) has parameter \\$viewType with no type specified\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/View.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerInterface\\:\\:renderContent\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ViewManagerInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerInterface\\:\\:renderContentView\\(\\) has parameter \\$defaultParams with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ViewManagerInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerInterface\\:\\:renderLocation\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/MVC/Symfony/View/ViewManagerInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:executeQuery\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:getNbResults\\(\\) should return int but returns int\\|null\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:\\$languageFilter type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactory\\:\\:createAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactory\\:\\:createFixedAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactoryInterface\\:\\:createAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AdapterFactory\\\\SearchHitAdapterFactoryInterface\\:\\:createFixedAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentFilteringAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentFilteringAdapter\\:\\:\\$languageFilter type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/ContentFilteringAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentSearchAdapter\\:\\:getSlice\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\> but returns array\\<int\\<0, max\\>, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\>\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php + + - + message: "#^Return type \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\>\\) of method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentSearchAdapter\\:\\:getSlice\\(\\) should be compatible with return type \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\>\\) of method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:getSlice\\(\\)$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\ContentSearchHitAdapter\\:\\:executeQuery\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\FixedSearchResultHitAdapter\\:\\:getAggregations\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResultCollection but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResultCollection\\|null\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationFilteringAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationFilteringAdapter\\:\\:\\$languageFilter type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationSearchAdapter\\:\\:getSlice\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\> but returns array\\<int\\<0, max\\>, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\>\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php + + - + message: "#^Return type \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>\\) of method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationSearchAdapter\\:\\:getSlice\\(\\) should be compatible with return type \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\>\\) of method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\AbstractSearchResultAdapter\\:\\:getSlice\\(\\)$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationSearchHitAdapter\\:\\:__construct\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\LocationSearchHitAdapter\\:\\:executeQuery\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php + + - + message: "#^Call to an undefined method Pagerfanta\\\\Adapter\\\\AdapterInterface\\:\\:getAggregations\\(\\)\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/Pagerfanta.php + + - + message: "#^Call to an undefined method Pagerfanta\\\\Adapter\\\\AdapterInterface\\:\\:getMaxScore\\(\\)\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/Pagerfanta.php + + - + message: "#^Call to an undefined method Pagerfanta\\\\Adapter\\\\AdapterInterface\\:\\:getTime\\(\\)\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/Pagerfanta.php + + - + message: "#^Call to an undefined method Pagerfanta\\\\Adapter\\\\AdapterInterface\\:\\:getTimedOut\\(\\)\\.$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/Pagerfanta.php + + - + message: "#^Class Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\Pagerfanta extends generic class Pagerfanta\\\\Pagerfanta but does not specify its types\\: T$#" + count: 1 + path: src/lib/Pagination/Pagerfanta/Pagerfanta.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\AbstractHandler\\:\\:getMultipleCacheItems\\(\\) has parameter \\$ids with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/AbstractHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\AbstractHandler\\:\\:getMultipleCacheItems\\(\\) has parameter \\$keySuffixes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/AbstractHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\AbstractHandler\\:\\:getMultipleCacheItems\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/AbstractHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryHandler\\:\\:getCacheValue\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/AbstractInMemoryHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryHandler\\:\\:getListCacheValue\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/AbstractInMemoryHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryHandler\\:\\:getListCacheValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/AbstractInMemoryHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryHandler\\:\\:getListCacheValue\\(\\) should return array but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/AbstractInMemoryHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryHandler\\:\\:getMultipleCacheValues\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/AbstractInMemoryHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryHandler\\:\\:getMultipleCacheValues\\(\\) has parameter \\$ids with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/AbstractInMemoryHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryHandler\\:\\:getMultipleCacheValues\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/AbstractInMemoryHandler.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryHandler\\:\\:\\$cacheIndicesValidator \\(Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\CacheIndicesValidatorInterface\\) does not accept Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\CacheIndicesValidatorInterface\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Cache/AbstractInMemoryHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:__construct\\(\\) has parameter \\$deferredItemsDeletion with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:__construct\\(\\) has parameter \\$deferredTagsInvalidation with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:getItems\\(\\) should return Traversable\\<string, Symfony\\\\Component\\\\Cache\\\\CacheItem\\> but returns array\\<Symfony\\\\Component\\\\Cache\\\\CacheItem\\>\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:markItemsAsDeferredMissIfNeeded\\(\\) should return array\\<Symfony\\\\Component\\\\Cache\\\\CacheItem\\> but returns iterable\\<Symfony\\\\Component\\\\Cache\\\\CacheItem\\>\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:\\$deferredItemsDeletion type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:\\$deferredTagsInvalidation type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\TransactionalInMemoryCacheAdapter\\:\\:\\$inMemoryPools \\(array\\<Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\InMemory\\\\InMemoryCache\\>\\) does not accept iterable\\<Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\InMemory\\\\InMemoryCache\\>\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\BookmarkHandler\\:\\:loadByUserIdAndLocationId\\(\\) has parameter \\$locationIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/BookmarkHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentHandler\\:\\:deleteTranslationFromContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentHandler\\:\\:getCacheTagsForVersion\\(\\) has parameter \\$tags with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentHandler\\:\\:getCacheTagsForVersion\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentHandler\\:\\:load\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentHandler\\:\\:loadContentInfo\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ContentInfo but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentHandler\\:\\:loadContentInfoByRemoteId\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ContentInfo but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentHandler\\:\\:loadContentInfoList\\(\\) has parameter \\$contentIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentLanguageHandler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentLanguageHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentLanguageHandler\\:\\:load\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentLanguageHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentLanguageHandler\\:\\:loadByLanguageCode\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentLanguageHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentLanguageHandler\\:\\:loadList\\(\\) has parameter \\$ids with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentLanguageHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentLanguageHandler\\:\\:update\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentLanguageHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:deleteGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:getSearchableFieldMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:link\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:load\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:loadByIdentifier\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:loadByRemoteId\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:loadContentTypeList\\(\\) has parameter \\$contentTypeIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:loadGroup\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Group but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:loadGroupByIdentifier\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Group but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:loadGroups\\(\\) has parameter \\$groupIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:publish\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:unlink\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:update\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:updateFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandler\\:\\:updateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ContentTypeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Handler\\:\\:beginTransaction\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Handler\\:\\:commit\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Handler\\:\\:rollback\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Handler.php + + - + message: "#^Return type \\(Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\URLHandler\\) of method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Handler\\:\\:urlHandler\\(\\) should be compatible with return type \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Handler\\) of method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Handler\\:\\:urlHandler\\(\\)$#" + count: 1 + path: src/lib/Persistence/Cache/Handler.php + + - + message: "#^Cannot call method debug\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Identifier/CacheIdentifierGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGenerator\\:\\:__construct\\(\\) has parameter \\$keyPatterns with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Identifier/CacheIdentifierGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGenerator\\:\\:__construct\\(\\) has parameter \\$tagPatterns with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Identifier/CacheIdentifierGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGenerator\\:\\:generate\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Identifier/CacheIdentifierGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGenerator\\:\\:generateKey\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Identifier/CacheIdentifierGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGenerator\\:\\:generateTag\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Identifier/CacheIdentifierGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGeneratorInterface\\:\\:generateKey\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGeneratorInterface\\:\\:generateTag\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorInterface.php + + - + message: "#^Binary operation \"\\+\" between float\\|string and float results in an error\\.$#" + count: 1 + path: src/lib/Persistence/Cache/InMemory/InMemoryCache.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\InMemory\\\\InMemoryCache\\:\\:\\$cache \\(array\\<object\\>\\) does not accept array\\<array\\<object\\>\\|object\\>\\.$#" + count: 1 + path: src/lib/Persistence/Cache/InMemory/InMemoryCache.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Handler\\:\\:copySubtree\\(\\) invoked with 3 parameters, 2 required\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:changeMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:copySubtree\\(\\) has parameter \\$newOwnerId with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:getCacheTags\\(\\) has parameter \\$tags with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:getCacheTags\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:getCacheTranslationKey\\(\\) has parameter \\$translations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:getCacheTranslationKey\\(\\) should return string but returns int\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:hide\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:load\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:loadByRemoteId\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:loadLocationsByContent\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\> but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:loadParentLocationsForDraftContent\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\> but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:loadSubtreeIds\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:loadSubtreeIds\\(\\) should return array but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:markSubtreeModified\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:setSectionForSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:unHide\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandler\\:\\:update\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/LocationHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ObjectStateHandler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ObjectStateHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ObjectStateHandler\\:\\:deleteGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ObjectStateHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ObjectStateHandler\\:\\:getContentState\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ObjectState but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ObjectStateHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ObjectStateHandler\\:\\:load\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ObjectState but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ObjectStateHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ObjectStateHandler\\:\\:loadByIdentifier\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ObjectState but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ObjectStateHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ObjectStateHandler\\:\\:loadGroup\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ObjectState\\\\Group but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ObjectStateHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ObjectStateHandler\\:\\:loadGroupByIdentifier\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ObjectState\\\\Group but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ObjectStateHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ObjectStateHandler\\:\\:loadObjectStates\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ObjectState\\> but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ObjectStateHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\ObjectStateHandler\\:\\:setPriority\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/ObjectStateHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLogger\\:\\:collectCacheCallData\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/PersistenceLogger.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLogger\\:\\:collectCacheCallData\\(\\) has parameter \\$method with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/PersistenceLogger.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLogger\\:\\:collectCacheCallData\\(\\) has parameter \\$trimmedBacktrace with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/PersistenceLogger.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLogger\\:\\:getCalls\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/PersistenceLogger.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLogger\\:\\:getLoadedUnCachedHandlers\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/PersistenceLogger.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLogger\\:\\:getSimpleCallTrace\\(\\) has parameter \\$backtrace with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/PersistenceLogger.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLogger\\:\\:getStats\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/PersistenceLogger.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLogger\\:\\:logCacheHit\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/PersistenceLogger.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLogger\\:\\:logCacheMiss\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/PersistenceLogger.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLogger\\:\\:logCall\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/PersistenceLogger.php + + - + message: "#^Offset 'class' does not exist on array\\{function\\: string, line\\?\\: int, file\\?\\: string, class\\?\\: class\\-string, type\\?\\: '\\-\\>'\\|'\\:\\:', args\\?\\: array, object\\?\\: object\\}\\.$#" + count: 2 + path: src/lib/Persistence/Cache/PersistenceLogger.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLogger\\:\\:\\$calls type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/PersistenceLogger.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLogger\\:\\:\\$unCachedHandlers type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/PersistenceLogger.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\SectionHandler\\:\\:assign\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/SectionHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\SectionHandler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/SectionHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\TransactionHandler\\:\\:beginTransaction\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/TransactionHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\TransactionHandler\\:\\:commit\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/TransactionHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\TransactionHandler\\:\\:rollback\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/TransactionHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\URLHandler\\:\\:find\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/URLHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\URLHandler\\:\\:findUsages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/URLHandler.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\\\Handler\\:\\:publishUrlAliasForLocation\\(\\) invoked with 6 parameters, 4\\-5 required\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandler\\:\\:archiveUrlAliasesForDeletedTranslations\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandler\\:\\:archiveUrlAliasesForDeletedTranslations\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandler\\:\\:getCacheTags\\(\\) has parameter \\$tags with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandler\\:\\:getCacheTags\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandler\\:\\:locationCopied\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandler\\:\\:locationDeleted\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandler\\:\\:locationMoved\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandler\\:\\:locationSwapped\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandler\\:\\:publishUrlAliasForLocation\\(\\) has parameter \\$updatePathIdentificationString with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandler\\:\\:repairBrokenUrlAliasesForLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandler\\:\\:translationRemoved\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlAliasHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UrlWildcardHandler\\:\\:find\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlWildcardHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UrlWildcardHandler\\:\\:remove\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UrlWildcardHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:assignRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:assignRole\\(\\) has parameter \\$limitation with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:deletePolicy\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:deleteRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:expireUserToken\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:load\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:loadByLogin\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:loadRole\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Role but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:loadRoleAssignment\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\RoleAssignment but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:loadRoleByIdentifier\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Role but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:loadUserByToken\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:publishRoleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:unassignRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:update\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:updatePolicy\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:updateRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserHandler\\:\\:updateUserToken\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\UserPreferenceHandler\\:\\:getUserPreferenceByUserIdAndName\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\UserPreference\\\\UserPreference but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Cache/UserPreferenceHandler.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\FieldTypeRegistry\\:\\:\\$coreFieldTypes \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\>\\) does not accept array\\<Ibexa\\\\Core\\\\Persistence\\\\FieldType\\>\\.$#" + count: 1 + path: src/lib/Persistence/FieldTypeRegistry.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\FieldTypeRegistry\\:\\:\\$fieldTypes \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\FieldType\\>\\) does not accept array\\<Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\>\\.$#" + count: 1 + path: src/lib/Persistence/FieldTypeRegistry.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\:\\:loadBookmarkDataByUserIdAndLocationId\\(\\) has parameter \\$locationIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\:\\:loadBookmarkDataByUserIdAndLocationId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\:\\:loadUserBookmarks\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Gateway.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 3 + path: src/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabase\\:\\:getColumns\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabase\\:\\:loadBookmarkDataById\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabase\\:\\:loadBookmarkDataByUserIdAndLocationId\\(\\) has parameter \\$locationIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabase\\:\\:loadBookmarkDataByUserIdAndLocationId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabase\\:\\:loadUserBookmarks\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\ExceptionConversion\\:\\:loadBookmarkDataByUserIdAndLocationId\\(\\) has parameter \\$locationId with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\ExceptionConversion\\:\\:loadBookmarkDataByUserIdAndLocationId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\ExceptionConversion\\:\\:loadUserBookmarks\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Handler\\:\\:loadByUserIdAndLocationId\\(\\) has parameter \\$locationIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Mapper\\:\\:extractBookmarkFromRow\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Mapper\\:\\:extractBookmarksFromRows\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Bookmark/Mapper.php + + - + message: "#^Access to an undefined property object\\:\\:\\$languageCode\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Cannot access offset int\\|string on Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\.$#" + count: 3 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Cannot access offset string on Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\.$#" + count: 5 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:copyField\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:copyFields\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:copyFields\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:createExistingFieldInNewVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:createNewField\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:createNewFields\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:deleteFields\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:deleteTranslationFromContentFields\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:deleteTranslationFromVersionFields\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:getFieldMap\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:getFieldMap\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\> but returns array\\<int, array\\<string, Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\>\\>\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:loadExternalFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:updateCopiedField\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:updateField\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:updateFields\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Parameter \\#1 \\$field of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:updateField\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field, object given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Parameter \\#2 \\$languageCode of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:getEmptyField\\(\\) expects string, int\\|string given\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Parameter \\#3 \\$originalField of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:updateCopiedField\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field, object given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:\\$fieldTypes type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:\\$languageHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\AuthorConverter\\:\\:generateXmlString\\(\\) has parameter \\$authorValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\AuthorConverter\\:\\:generateXmlString\\(\\) should return string but returns string\\|false\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\AuthorConverter\\:\\:restoreValueFromXmlString\\(\\) should return array\\<Ibexa\\\\Core\\\\FieldType\\\\Author\\\\Value\\> but returns array\\<int\\<0, max\\>, array\\<string, string\\>\\>\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\AuthorConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\AuthorConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\AuthorConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\AuthorConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php + + - + message: "#^Parameter \\#1 \\$authorValue of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\AuthorConverter\\:\\:generateXmlString\\(\\) expects array, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataInt1 \\(int\\) on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$sortKeyString \\(string\\) does not accept bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\BinaryFileConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/BinaryFileConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\BinaryFileConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/BinaryFileConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\BinaryFileConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/BinaryFileConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\BinaryFileConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/BinaryFileConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CheckboxConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CheckboxConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CheckboxConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CheckboxConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$sortKeyString \\(string\\) does not accept bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php + + - + message: "#^Cannot access offset 'timestamp' on array\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeConverter\\:\\:generateDateIntervalXML\\(\\) should return string but returns string\\|false\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeConverter\\:\\:getDateIntervalFromXML\\(\\) should return DateInterval but empty return statement found\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeConverter\\:\\:getDateIntervalFromXML\\(\\) should return DateInterval but returns DateInterval\\|false\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php + + - + message: "#^Cannot access offset 'timestamp' on array\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\EmailAddressConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\EmailAddressConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\EmailAddressConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\EmailAddressConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataText1 \\(string\\) does not accept array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataText1 \\(string\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataText \\(string\\) does not accept array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$sortKeyString \\(string\\) does not accept bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\FloatConverter\\:\\:create\\(\\) should return static\\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\FloatConverter\\) but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\FloatConverter\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/FloatConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\FloatConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/FloatConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\FloatConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/FloatConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\FloatConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/FloatConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\FloatConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/FloatConverter.php + + - + message: "#^Parameter \\#1 \\$minValue of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\FloatConverter\\:\\:getStorageDefValidatorState\\(\\) expects int\\|null, float given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/FloatConverter.php + + - + message: "#^Parameter \\#2 \\$maxValue of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\FloatConverter\\:\\:getStorageDefValidatorState\\(\\) expects int\\|null, float given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/FloatConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataFloat3 \\(float\\) does not accept array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/FloatConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataFloat \\(float\\) does not accept array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/FloatConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$sortKeyString \\(string\\) does not accept bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/FloatConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ISBNConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ISBNConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ISBNConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ISBNConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataText1 \\(string\\) does not accept array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataText \\(string\\) does not accept array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$sortKeyString \\(string\\) does not accept bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageAssetConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageAssetConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageAssetConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageAssetConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageAssetConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageAssetConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageAssetConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageAssetConverter.php + + - + message: "#^Cannot access property \\$documentElement on DOMDocument\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Cannot access property \\$nodeValue on DOMNode\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Cannot call method getAttribute\\(\\) on DOMElement\\|null\\.$#" + count: 6 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Cannot call method hasAttribute\\(\\) on DOMElement\\|null\\.$#" + count: 3 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Cannot call method saveXML\\(\\) on DOMDocument\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageConverter\\:\\:buildAdditionalDataTag\\(\\) has parameter \\$imageEditorData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageConverter\\:\\:buildAdditionalDataTag\\(\\) should return string but returns string\\|false\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageConverter\\:\\:createEmptyLegacyXml\\(\\) has parameter \\$contentMetaData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageConverter\\:\\:createLegacyXml\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageConverter\\:\\:fillXml\\(\\) has parameter \\$imageData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageConverter\\:\\:fillXml\\(\\) has parameter \\$pathInfo with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageConverter\\:\\:parseLegacyXml\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageConverter\\:\\:parseLegacyXml\\(\\) should return array but returns null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ImageConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Parameter \\#1 \\$string of function htmlspecialchars expects string, int given\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\IntegerConverter\\:\\:create\\(\\) should return static\\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\IntegerConverter\\) but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\IntegerConverter\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/IntegerConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\IntegerConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/IntegerConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\IntegerConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/IntegerConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\IntegerConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/IntegerConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\IntegerConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/IntegerConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataInt3 \\(int\\) does not accept array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/IntegerConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataInt \\(int\\) does not accept array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/IntegerConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\KeywordConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\KeywordConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\KeywordConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\KeywordConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$sortKeyString \\(string\\) does not accept bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\MapLocationConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/MapLocationConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\MapLocationConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/MapLocationConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\MapLocationConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/MapLocationConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\MapLocationConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/MapLocationConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\MediaConverter\\:\\:getIndexColumn\\(\\) should return string but returns false\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/MediaConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\MediaConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/MediaConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\MediaConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/MediaConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\NullConverter\\:\\:getIndexColumn\\(\\) should return string but returns false\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/NullConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\NullConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/NullConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\NullConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/NullConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\NullConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/NullConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\NullConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/NullConverter.php + + - + message: "#^Cannot call method getElementsByTagName\\(\\) on DOMElement\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationConverter.php + + - + message: "#^Parameter \\#2 \\$value of method DOMElement\\:\\:setAttribute\\(\\) expects string, bool given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationConverter.php + + - + message: "#^Parameter \\#2 \\$value of method DOMElement\\:\\:setAttribute\\(\\) expects string, int given\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataText5 \\(string\\) does not accept string\\|false\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationConverter.php + + - + message: "#^Cannot access offset 'destinationContentI…' on array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Cannot call method getElementsByTagName\\(\\) on DOMElement\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListConverter\\:\\:dbAttributeMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListConverter\\:\\:getRelationXmlHashFromDB\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListConverter\\:\\:groupResultSetById\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListConverter\\:\\:groupResultSetById\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Parameter \\#2 \\$value of method DOMElement\\:\\:setAttribute\\(\\) expects string, bool given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Parameter \\#2 \\$value of method DOMElement\\:\\:setAttribute\\(\\) expects string, int given\\.$#" + count: 6 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataText5 \\(string\\) does not accept string\\|false\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataText \\(string\\) does not accept string\\|false\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$sortKeyString \\(string\\) does not accept bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\SelectionConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\SelectionConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\SelectionConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\SelectionConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\FieldDefinition\\:\\:\\$mainLanguageCode \\(string\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MultilingualStorageFieldDefinition\\:\\:\\$dataText \\(string\\) does not accept string\\|false\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataText5 \\(string\\) does not accept string\\|false\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataText5 \\(string\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataText \\(string\\) does not accept bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$sortKeyString \\(string\\) does not accept bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php + + - + message: "#^Parameter \\#1 \\$data of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValueSerializerInterface\\:\\:encode\\(\\) expects array\\|null, array\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataText5 \\(string\\) does not accept string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataText \\(string\\) does not accept string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextBlockConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextBlockConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextBlockConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextBlockConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataText \\(string\\) does not accept array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$sortKeyString \\(string\\) does not accept bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextLineConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextLineConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextLineConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextLineConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataInt1 \\(int\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataInt2 \\(int\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataText1 \\(string\\) does not accept array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataText \\(string\\) does not accept array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$sortKeyString \\(string\\) does not accept bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TimeConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TimeConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TimeConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TimeConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataInt \\(int\\) does not accept array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\UrlConverter\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\UrlConverter\\:\\:toFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\UrlConverter\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\UrlConverter\\:\\:toStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataInt2 \\(int\\) does not accept null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/Converter/UserConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\ConverterRegistry\\:\\:__construct\\(\\) has parameter \\$converterMap with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/ConverterRegistry.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\ConverterRegistry\\:\\:register\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/ConverterRegistry.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\ConverterRegistry\\:\\:\\$converterMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/FieldValue/ConverterRegistry.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:listReverseRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:load\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadContentInfo\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadContentInfoByLocationId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadContentInfoByRemoteId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadContentInfoList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadContentList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadRelation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadReverseRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadVersionInfo\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadVersionInfoList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadVersionedNameData\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadVersionedNameData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadVersionsForUser\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway.php + + - + message: "#^Argument of an invalid type DOMNodeList\\<DOMNode\\>\\|false supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Cannot call method fetch\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 16 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Cannot call method removeChild\\(\\) on DOMNode\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:deleteTranslationFromContentNames\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:deleteTranslationFromContentObject\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:deleteTranslationFromContentVersions\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:internalLoadContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:listReverseRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:load\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:loadContentInfo\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:loadContentInfoByLocationId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:loadContentInfoByRemoteId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:loadContentInfoList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:loadContentList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:loadRelation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:loadRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:loadReverseRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:loadVersionInfo\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:loadVersionInfoList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:loadVersionedNameData\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:loadVersionedNameData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:loadVersionsForUser\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:removeRelationFromRelationField\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:removeRelationFromRelationListField\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Parameter \\#2 \\$value2 of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getBitAndComparisonExpression\\(\\) expects string, int given\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\MetadataUpdateStruct\\:\\:\\$alwaysAvailable \\(bool\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\MetadataUpdateStruct\\:\\:\\$alwaysAvailable \\(bool\\) on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\MetadataUpdateStruct\\:\\:\\$mainLanguageId \\(int\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:\\$languageHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Result of \\|\\| is always true\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Ternary operator condition is always true\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Dead catch \\- Doctrine\\\\DBAL\\\\DBALException\\|PDOException is never thrown in the try block\\.$#" + count: 14 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:getContentIdsByContentTypeId\\(\\) has parameter \\$contentTypeId with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:listReverseRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:load\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:loadContentInfo\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:loadContentInfoByLocationId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:loadContentInfoByRemoteId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:loadContentInfoList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:loadContentList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:loadRelation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:loadRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:loadReverseRelations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:loadVersionInfo\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:loadVersionInfoList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:loadVersionedNameData\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:loadVersionedNameData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:loadVersionsForUser\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Handler\\:\\:deleteContent\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Handler\\:\\:deleteTranslationFromContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Handler\\:\\:deleteVersion\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Handler\\:\\:loadContentInfoList\\(\\) has parameter \\$contentIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Handler\\:\\:loadVersionInfo\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\VersionInfo but returns Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\VersionInfo\\|false\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Handler\\:\\:removeRawContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Handler\\:\\:updatePathIdentificationString\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Handler.php + + - + message: "#^Parameter \\#1 \\$contentId of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:load\\(\\) expects int, int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Handler.php + + - + message: "#^Parameter \\#1 \\$contentId of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadVersionInfo\\(\\) expects int, int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Handler.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\MetadataUpdateStruct\\:\\:\\$mainLanguageId \\(int\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/CachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\:\\:load\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/CachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\:\\:loadAll\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\> but returns array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\>\\|object\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/CachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\:\\:loadByLanguageCode\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/CachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\:\\:loadList\\(\\) has parameter \\$ids with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/CachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\:\\:storeCache\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/CachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\:\\:update\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/CachingHandler.php + + - + message: "#^Parameter \\#1 \\$languages of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\:\\:storeCache\\(\\) expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\> given\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/Language/CachingHandler.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\:\\:\\$innerHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/CachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Gateway\\:\\:loadAllLanguagesData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/Gateway.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 3 + path: src/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Gateway\\\\DoctrineDatabase\\:\\:loadAllLanguagesData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Gateway\\\\ExceptionConversion\\:\\:loadAllLanguagesData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\:\\:loadList\\(\\) has parameter \\$ids with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\:\\:update\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/Handler.php + + - + message: "#^Parameter \\#1 \\$rows of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Mapper\\:\\:extractLanguagesFromRows\\(\\) expects array, iterable\\<array\\<string\\>\\> given\\.$#" + count: 4 + path: src/lib/Persistence/Legacy/Content/Language/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Mapper\\:\\:extractLanguagesFromRows\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGenerator\\:\\:extractLanguageCodesFromMask\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/MaskGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGenerator\\:\\:extractLanguageIdsFromMask\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/MaskGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGenerator\\:\\:generateLanguageMask\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/MaskGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGenerator\\:\\:isLanguageAlwaysAvailable\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/MaskGenerator.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGenerator\\:\\:\\$languageHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Language/MaskGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:create\\(\\) has parameter \\$parentNode with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:getBasicNodeData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:getBasicNodeDataByRemoteId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:getChildren\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:getFallbackMainNodeData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:getNodeDataList\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:getSubtreeContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:listTrashed\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:loadAllLocationsData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:loadLocationDataByContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:loadParentLocationsDataForDraftContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:loadTrashByLocation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:moveSubtreeNodes\\(\\) has parameter \\$fromPathString with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:moveSubtreeNodes\\(\\) has parameter \\$toPathString with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway.php + + - + message: "#^Cannot call method fetch\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 6 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 12 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Cannot call method fetchFirstColumn\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:addSort\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:addSort\\(\\) has parameter \\$sort with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:appendContentItemTranslationsConstraint\\(\\) has parameter \\$translations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:create\\(\\) has parameter \\$parentNode with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:createNodeQueryBuilder\\(\\) has parameter \\$columns with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:createNodeQueryBuilder\\(\\) has parameter \\$translations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:getBasicNodeData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:getBasicNodeDataByRemoteId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:getChildren\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:getFallbackMainNodeData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:getNodeDataList\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:getSubtreeContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:getSubtreeNodesData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:insertLocationIntoContentTree\\(\\) has parameter \\$parentNode with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:listTrashed\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:loadAllLocationsData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:loadHiddenSubtreesByPath\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:loadLocationDataByContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:loadParentLocationsDataForDraftContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:loadTrashByLocation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:moveSingleSubtreeNode\\(\\) has parameter \\$destinationNodeData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:moveSingleSubtreeNode\\(\\) has parameter \\$sourceNodeData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:moveSubtreeNodes\\(\\) has parameter \\$destinationNodeData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:moveSubtreeNodes\\(\\) has parameter \\$sourceNodeData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:updatePathIdentificationString\\(\\) has parameter \\$locationId with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:updatePathIdentificationString\\(\\) has parameter \\$parentLocationId with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:updatePathIdentificationString\\(\\) has parameter \\$text with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Parameter \\#2 \\$value2 of method Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\:\\:getBitAndComparisonExpression\\(\\) expects string, int given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Parameter \\#2 \\$y of method Doctrine\\\\DBAL\\\\Query\\\\Expression\\\\ExpressionBuilder\\:\\:in\\(\\) expects array\\<string\\>\\|string, array\\<int\\> given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Dead catch \\- Doctrine\\\\DBAL\\\\DBALException\\|PDOException is never thrown in the try block\\.$#" + count: 8 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:create\\(\\) has parameter \\$parentNode with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:getBasicNodeData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:getBasicNodeDataByRemoteId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:getChildren\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:getFallbackMainNodeData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:getNodeDataList\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:getSubtreeContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:listTrashed\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:loadAllLocationsData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:loadLocationDataByContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:loadParentLocationsDataForDraftContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:loadTrashByLocation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:moveSubtreeNodes\\(\\) has parameter \\$fromPathString with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:moveSubtreeNodes\\(\\) has parameter \\$toPathString with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:changeMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:hide\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:loadSubtreeIds\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:markSubtreeModified\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:move\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:removeSubtree\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:setContentStates\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:setSectionForSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:swap\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:unHide\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:update\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:updateSubtreeSectionIfNecessary\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Parameter \\#1 \\$nodeId of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:getBasicNodeData\\(\\) expects int, int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\CreateStruct\\:\\:\\$pathIdentificationString \\(string\\) does not accept string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Variable \\$copiedSubtreeRootLocation might not be defined\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/Location/Handler.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\:\\:\\$trashed\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Mapper\\:\\:createLocationFromRow\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Mapper\\:\\:createLocationsFromRows\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Mapper\\:\\:getLocationCreateStruct\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:changeMainLocation\\(\\) invoked with 4 parameters, 2 required\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Trash/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Trash\\\\Handler\\:\\:loadTrashItem\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Trashed but returns Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Location/Trash/Handler.php + + - + message: "#^Parameter \\#2 \\$prefix of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Mapper\\:\\:createLocationFromRow\\(\\) expects string, null given\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/Location/Trash/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\:\\:extractContentInfoFromRows\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\:\\:extractFieldFromRow\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\:\\:extractFieldValueFromRow\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\:\\:extractRelationFromRow\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\:\\:extractRelationsFromRows\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\:\\:extractRelationsFromRows\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\:\\:extractVersionInfoFromRow\\(\\) has parameter \\$names with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\:\\:extractVersionInfoFromRow\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Mapper.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\:\\:\\$id \\(int\\) does not accept null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Mapper.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\VersionInfo\\:\\:\\$contentInfo \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ContentInfo\\) does not accept null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Mapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataFloat \\(float\\) does not accept float\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Mapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataInt \\(int\\) does not accept int\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\:\\:loadObjectStateData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\:\\:loadObjectStateDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\:\\:loadObjectStateDataForContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\:\\:loadObjectStateGroupData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\:\\:loadObjectStateGroupDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\:\\:loadObjectStateGroupListData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\:\\:loadObjectStateListData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway.php + + - + message: "#^Cannot call method fetch\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 3 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 5 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabase\\:\\:loadObjectStateData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabase\\:\\:loadObjectStateDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabase\\:\\:loadObjectStateDataForContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabase\\:\\:loadObjectStateGroupData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabase\\:\\:loadObjectStateGroupDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabase\\:\\:loadObjectStateGroupListData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabase\\:\\:loadObjectStateListData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php + + - + message: "#^PHPDoc tag @param for parameter \\$stateId with type mixed is not subtype of native type int\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabase.php + + - + message: "#^Dead catch \\- Doctrine\\\\DBAL\\\\DBALException\\|PDOException is never thrown in the try block\\.$#" + count: 4 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\ExceptionConversion\\:\\:loadObjectStateData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\ExceptionConversion\\:\\:loadObjectStateDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\ExceptionConversion\\:\\:loadObjectStateDataForContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\ExceptionConversion\\:\\:loadObjectStateGroupData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\ExceptionConversion\\:\\:loadObjectStateGroupDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\ExceptionConversion\\:\\:loadObjectStateGroupListData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\ExceptionConversion\\:\\:loadObjectStateListData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php + + - + message: "#^Cannot access property \\$id on Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ObjectState\\|false\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Handler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Handler\\:\\:deleteGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Handler\\:\\:setPriority\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Handler.php + + - + message: "#^Parameter \\#1 \\$stateId of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\:\\:updateObjectStatePriority\\(\\) expects int, int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Handler.php + + - + message: "#^Cannot access offset int on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\>\\.$#" + count: 4 + path: src/lib/Persistence/Legacy/Content/ObjectState/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Mapper\\:\\:createObjectStateFromData\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Mapper\\:\\:createObjectStateGroupFromData\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Mapper\\:\\:createObjectStateGroupListFromData\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Mapper\\:\\:createObjectStateListFromData\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Mapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Mapper\\:\\:\\$languageHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/ObjectState/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\:\\:loadAllSectionData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Section/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\:\\:loadSectionData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Section/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\:\\:loadSectionDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Section/Gateway.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 3 + path: src/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabase\\:\\:loadAllSectionData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabase\\:\\:loadSectionData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabase\\:\\:loadSectionDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\ExceptionConversion\\:\\:loadAllSectionData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Section/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\ExceptionConversion\\:\\:loadSectionData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Section/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\ExceptionConversion\\:\\:loadSectionDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Section/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Handler\\:\\:assign\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Section/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Handler\\:\\:createSectionFromArray\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Section/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Handler\\:\\:createSectionsFromArray\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Section/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Handler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Section/Handler.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldStorage\\:\\:copyLegacyField\\(\\)\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/StorageHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler\\:\\:__construct\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/StorageHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler\\:\\:copyFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/StorageHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler\\:\\:deleteFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/StorageHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler\\:\\:getFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/StorageHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler\\:\\:storeFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/StorageHandler.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler\\:\\:\\$context type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/StorageHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageRegistry\\:\\:__construct\\(\\) has parameter \\$storageMap with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/StorageRegistry.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandler\\:\\:changeMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/TreeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandler\\:\\:removeRawContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/TreeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandler\\:\\:removeSubtree\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/TreeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandler\\:\\:setSectionForSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/TreeHandler.php + + - + message: "#^Parameter \\#1 \\$contentId of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:loadContentInfo\\(\\) expects int, int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/TreeHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\:\\:applyUpdates\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/ContentUpdater.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\:\\:apply\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddField\\:\\:apply\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddField.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\:\\:\\$id \\(int\\) does not accept int\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddField.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\:\\:\\$id \\(int\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddField.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\RemoveField\\:\\:apply\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveField.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\:\\:getSearchableFieldMapData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\:\\:loadAllGroupsData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\:\\:loadFieldDefinition\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\:\\:loadGroupData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\:\\:loadGroupDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\:\\:loadTypeData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\:\\:loadTypeDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\:\\:loadTypeDataByRemoteId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\:\\:loadTypesDataForGroup\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\:\\:loadTypesListData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 10 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Method Doctrine\\\\DBAL\\\\Query\\\\QueryBuilder\\:\\:execute\\(\\) invoked with 1 parameter, 0 required\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:getSearchableFieldMapData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:loadAllGroupsData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:loadFieldDefinition\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:loadGroupData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:loadGroupDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:loadTypeData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:loadTypeDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:loadTypeDataByRemoteId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:loadTypesDataForGroup\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:loadTypesListData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:mapCommonContentTypeColumnsToQueryValuesAndTypes\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:mapCommonFieldDefinitionColumnsToQueryValuesAndTypes\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Parameter \\#4 \\$condition of method Doctrine\\\\DBAL\\\\Query\\\\QueryBuilder\\:\\:leftJoin\\(\\) expects string\\|null, Doctrine\\\\DBAL\\\\Query\\\\Expression\\\\CompositeExpression given\\.$#" + count: 5 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Group\\:\\:\\$isSystem \\(bool\\) on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:\\$columns type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php + + - + message: "#^Dead catch \\- Doctrine\\\\DBAL\\\\DBALException\\|PDOException is never thrown in the try block\\.$#" + count: 4 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\ExceptionConversion\\:\\:getSearchableFieldMapData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\ExceptionConversion\\:\\:loadAllGroupsData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\ExceptionConversion\\:\\:loadFieldDefinition\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\ExceptionConversion\\:\\:loadGroupData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\ExceptionConversion\\:\\:loadGroupDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\ExceptionConversion\\:\\:loadTypeData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\ExceptionConversion\\:\\:loadTypeDataByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\ExceptionConversion\\:\\:loadTypeDataByRemoteId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\ExceptionConversion\\:\\:loadTypesDataForGroup\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\ExceptionConversion\\:\\:loadTypesListData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\:\\:addFieldDefinition\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\FieldDefinition but return statement is missing\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\:\\:deleteGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\:\\:getSearchableFieldMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\:\\:link\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\:\\:loadContentTypeList\\(\\) has parameter \\$contentTypeIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\:\\:loadFromRows\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\:\\:loadGroups\\(\\) has parameter \\$groupIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\:\\:publish\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\:\\:unlink\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\:\\:updateFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Handler.php + + - + message: "#^PHPDoc tag @param for parameter \\$contentTypeId with type mixed is not subtype of native type int\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Handler.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$fieldDefinitionId$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Handler.php + + - + message: "#^PHPDoc tag @return with type bool is incompatible with native type void\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:extractFieldFromRow\\(\\) has parameter \\$multilingualData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:extractFieldFromRow\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:extractGroupsFromRows\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:extractMultilingualData\\(\\) has parameter \\$fieldDefinitionRows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:extractMultilingualData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:extractMultilingualDataFromRows\\(\\) has parameter \\$mlFieldDefinitionsRows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:extractMultilingualDataFromRows\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:extractStorageFieldFromRow\\(\\) has parameter \\$multilingualDataRow with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:extractStorageFieldFromRow\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:extractTypeFromRow\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:extractTypesFromRows\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:extractTypesFromRows\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:toFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:toStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MultilingualStorageFieldDefinition\\:\\:\\$description \\(string\\) does not accept string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataFloat1 \\(float\\) does not accept float\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataFloat2 \\(float\\) does not accept float\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataFloat3 \\(float\\) does not accept float\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataFloat4 \\(float\\) does not accept float\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataInt1 \\(int\\) does not accept int\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataInt2 \\(int\\) does not accept int\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataInt3 \\(int\\) does not accept int\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldDefinition\\:\\:\\$dataInt4 \\(int\\) does not accept int\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:getSearchableFieldMap\\(\\) should return array but returns array\\|object\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:load\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:loadAllGroups\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Group\\> but returns array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Group\\>\\|object\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:loadByIdentifier\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:loadByRemoteId\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:loadContentTypeList\\(\\) has parameter \\$contentTypeIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:loadContentTypeList\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\> but returns array\\<int\\|string, object\\>\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:loadContentTypes\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\> but returns array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\>\\|object\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:loadContentTypesByFieldDefinitionIdentifier\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\> but returns array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\>\\|object\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:loadGroup\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Group but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:loadGroupByIdentifier\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Group but returns object\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:loadGroups\\(\\) has parameter \\$groupIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:loadGroups\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Group\\> but returns array\\<int\\|string, object\\>\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:storeGroupCache\\(\\) has parameter \\$groups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\:\\:storeTypeCache\\(\\) has parameter \\$types with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\StorageRegistry\\:\\:__construct\\(\\) has parameter \\$storages with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/Type/StorageRegistry.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\DTO\\\\SwappedLocationProperties\\:\\:__construct\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/DTO/SwappedLocationProperties.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\DTO\\\\SwappedLocationProperties\\:\\:__construct\\(\\) has parameter \\$parentId with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/DTO/SwappedLocationProperties.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\DTO\\\\SwappedLocationProperties\\:\\:\\$entries type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/DTO/SwappedLocationProperties.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\DTO\\\\UrlAliasForSwappedLocation\\:\\:__construct\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/DTO/UrlAliasForSwappedLocation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\DTO\\\\UrlAliasForSwappedLocation\\:\\:__construct\\(\\) has parameter \\$isAlwaysAvailable with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/DTO/UrlAliasForSwappedLocation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\DTO\\\\UrlAliasForSwappedLocation\\:\\:__construct\\(\\) has parameter \\$isPathIdentificationStringModified with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/DTO/UrlAliasForSwappedLocation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\DTO\\\\UrlAliasForSwappedLocation\\:\\:__construct\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/DTO/UrlAliasForSwappedLocation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\DTO\\\\UrlAliasForSwappedLocation\\:\\:__construct\\(\\) has parameter \\$newId with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/DTO/UrlAliasForSwappedLocation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\DTO\\\\UrlAliasForSwappedLocation\\:\\:__construct\\(\\) has parameter \\$parentId with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/DTO/UrlAliasForSwappedLocation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:getAllChildrenAliases\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:insertRow\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:listGlobalEntries\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:loadAllLocationEntries\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:loadAutogeneratedEntries\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:loadAutogeneratedEntry\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:loadLocationEntries\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:loadPathData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:loadPathDataByHierarchy\\(\\) has parameter \\$hierarchyData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:loadPathDataByHierarchy\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:loadRow\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:loadUrlAliasData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:updateRow\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php + + - + message: "#^Cannot call method fetch\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 5 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 10 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Cannot cast Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string to int\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:deleteRow\\(\\) should return int but returns Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:deleteUrlAliasesWithoutLocation\\(\\) should return int but returns Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:deleteUrlAliasesWithoutParent\\(\\) should return int but returns Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:deleteUrlNopAliasesWithoutChildren\\(\\) should return int but returns Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:filterOriginalAliases\\(\\) has parameter \\$urlAliasesData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:filterOriginalAliases\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:getAllChildrenAliases\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:getUrlAliasesForLocation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:insertRow\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:listGlobalEntries\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:loadAllLocationEntries\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:loadAutogeneratedEntries\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:loadAutogeneratedEntry\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:loadLocationEntries\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:loadLocationEntriesMatchingMultipleLanguages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:loadPathData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:loadPathDataByHierarchy\\(\\) has parameter \\$hierarchyData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:loadPathDataByHierarchy\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:loadRow\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:loadUrlAliasData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase\\:\\:updateRow\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Parameter \\#2 \\$callback of function array_filter expects \\(callable\\(int\\)\\: bool\\)\\|null, Closure\\(mixed\\)\\: int given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Variable \\$connection in PHPDoc tag @var does not match assigned variable \\$query\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabase.php + + - + message: "#^Dead catch \\- Doctrine\\\\DBAL\\\\DBALException\\|PDOException is never thrown in the try block\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\ExceptionConversion\\:\\:getAllChildrenAliases\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\ExceptionConversion\\:\\:insertRow\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\ExceptionConversion\\:\\:listGlobalEntries\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\ExceptionConversion\\:\\:loadAllLocationEntries\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\ExceptionConversion\\:\\:loadAutogeneratedEntries\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\ExceptionConversion\\:\\:loadAutogeneratedEntry\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\ExceptionConversion\\:\\:loadLocationEntries\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\ExceptionConversion\\:\\:loadPathData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\ExceptionConversion\\:\\:loadPathDataByHierarchy\\(\\) has parameter \\$hierarchyData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\ExceptionConversion\\:\\:loadPathDataByHierarchy\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\ExceptionConversion\\:\\:loadRow\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\ExceptionConversion\\:\\:loadUrlAliasData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\ExceptionConversion\\:\\:updateRow\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:archiveUrlAliasesForDeletedTranslations\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:archiveUrlAliasesForDeletedTranslations\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:copySubtree\\(\\) has parameter \\$actionMap with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:copySubtree\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:getCopiedLocationsMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:getLocationEntryInLanguage\\(\\) has parameter \\$locationEntries with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:getLocationEntryInLanguage\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:getNamesForAllLanguages\\(\\) has parameter \\$contentInfo with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:getNamesForAllLanguages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:historizeBeforeSwap\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:historizeBeforeSwap\\(\\) has parameter \\$location1Entries with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:historizeBeforeSwap\\(\\) has parameter \\$location2Entries with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:insertAliasEntryAsNop\\(\\) has parameter \\$aliasEntry with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:internalPublishCustomUrlAliasForLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:locationCopied\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:locationDeleted\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:locationMoved\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:locationSwapped\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:publishUrlAliasForLocation\\(\\) has parameter \\$updatePathIdentificationString with no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:removeSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:repairBrokenUrlAliasesForLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:shouldUrlAliasForSecondLocationBePublishedFirst\\(\\) has parameter \\$location1Entries with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:shouldUrlAliasForSecondLocationBePublishedFirst\\(\\) has parameter \\$location2Entries with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:translationRemoved\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Parameter \\#1 \\$languageCode of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\:\\:loadByLanguageCode\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Parameter \\#1 \\$parentId of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:removeCustomAlias\\(\\) expects int, string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\DTO\\\\SwappedLocationProperties\\:\\:\\$name \\(string\\) in isset\\(\\) is not nullable\\.$#" + count: 3 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:\\$languageHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Mapper\\:\\:extractLanguageCodesFromData\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Mapper\\:\\:extractUrlAliasListFromData\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Mapper\\:\\:matchTypeAndDestination\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Mapper\\:\\:normalizePathData\\(\\) has parameter \\$pathData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Mapper\\:\\:normalizePathData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Mapper\\:\\:normalizePathDataRow\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Mapper\\:\\:normalizePathDataRow\\(\\) has parameter \\$pathElementData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Mapper\\:\\:normalizePathDataRow\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverter\\:\\:__construct\\(\\) has parameter \\$configuration with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/SlugConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverter\\:\\:cleanupText\\(\\) should return string but returns string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/SlugConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverter\\:\\:\\$configuration type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlAlias/SlugConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\:\\:find\\(\\) has parameter \\$sortClauses with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\:\\:loadUrlWildcardBySourceUrl\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\:\\:loadUrlWildcardData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\:\\:loadUrlWildcardsData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway.php + + - + message: "#^Cannot call method fetch\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabase.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\DoctrineDatabase\\:\\:find\\(\\) has parameter \\$sortClauses with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\DoctrineDatabase\\:\\:loadUrlWildcardBySourceUrl\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\DoctrineDatabase\\:\\:loadUrlWildcardData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\DoctrineDatabase\\:\\:loadUrlWildcardsData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabase.php + + - + message: "#^Dead catch \\- Doctrine\\\\DBAL\\\\DBALException\\|PDOException is never thrown in the try block\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\ExceptionConversion\\:\\:find\\(\\) has parameter \\$sortClauses with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\ExceptionConversion\\:\\:loadUrlWildcardBySourceUrl\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\ExceptionConversion\\:\\:loadUrlWildcardData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\ExceptionConversion\\:\\:loadUrlWildcardsData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Handler\\:\\:find\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Handler\\:\\:match\\(\\) has parameter \\$wildcard with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Handler\\:\\:remove\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Handler\\:\\:substitute\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Mapper\\:\\:extractUrlWildcardFromRow\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Mapper\\:\\:extractUrlWildcardsFromRows\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Mapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Query\\\\CriteriaConverter\\:\\:\\$handlers \\(array\\<Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Query\\\\CriterionHandler\\>\\) does not accept iterable\\<Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Query\\\\CriterionHandler\\>\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriteriaConverter.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$status$#" + count: 1 + path: src/lib/Persistence/Legacy/Exception/TypeGroupNotFound.php + + - + message: "#^Parameter \\#2 \\$array of function array_map expects array, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ContentIdQueryBuilder.php + + - + message: "#^Parameter \\#2 \\$array of function array_map expects array, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Section/IdQueryBuilder.php + + - + message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(bool\\|float\\|int\\|string\\)\\: mixed\\)\\|null, Closure\\(string\\)\\: array\\<int, int\\> given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilder.php + + - + message: "#^Parameter \\#2 \\$array of function array_map expects array, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilder.php + + - + message: "#^Parameter \\#2 \\$criterionValue of method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Filter\\\\Doctrine\\\\FilteringQueryBuilder\\:\\:buildOperatorBasedCriterionConstraint\\(\\) expects array, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/DepthQueryBuilder.php + + - + message: "#^Cannot access offset 0 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/IsMainLocationQueryBuilder.php + + - + message: "#^Parameter \\#2 \\$criterionValue of method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Filter\\\\Doctrine\\\\FilteringQueryBuilder\\:\\:buildOperatorBasedCriterionConstraint\\(\\) expects array, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/PriorityQueryBuilder.php + + - + message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(bool\\|float\\|int\\|string\\)\\: mixed\\)\\|null, Closure\\(string\\)\\: string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/SubtreeQueryBuilder.php + + - + message: "#^Parameter \\#2 \\$array of function array_map expects array, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/SubtreeQueryBuilder.php + + - + message: "#^Cannot access offset 0 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/VisibilityQueryBuilder.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionQueryBuilder\\\\Location\\\\VisibilityQueryBuilder\\:\\:getVisibilityColumnsExpressions\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/VisibilityQueryBuilder.php + + - + message: "#^Parameter \\#2 \\$visibleFlag of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionQueryBuilder\\\\Location\\\\VisibilityQueryBuilder\\:\\:getVisibilityColumnsExpressions\\(\\) expects int, bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/VisibilityQueryBuilder.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion\\:\\:\\$criteria\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalAndQueryBuilder.php + + - + message: "#^Variable \\$criterion in PHPDoc tag @var does not match any variable in the foreach loop\\: \\$_criterion$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalAndQueryBuilder.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion\\:\\:\\$criteria\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalOrQueryBuilder.php + + - + message: "#^Variable \\$criterion in PHPDoc tag @var does not match any variable in the foreach loop\\: \\$_criterion$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalOrQueryBuilder.php + + - + message: "#^Parameter \\#1 \\$array of function reset expects array\\|object, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserBasedQueryBuilder.php + + - + message: "#^Parameter \\#1 \\$array of function reset expects array\\|object, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserEnabledQueryBuilder.php + + - + message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionQueryBuilder\\\\User\\\\BaseUserCriterionQueryBuilder\\:\\:transformCriterionValueForLikeExpression\\(\\) expects string, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserEmailQueryBuilder.php + + - + message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionQueryBuilder\\\\User\\\\BaseUserCriterionQueryBuilder\\:\\:transformCriterionValueForLikeExpression\\(\\) expects string, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserLoginQueryBuilder.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionVisitor\\:\\:__construct\\(\\) has parameter \\$criterionQueryBuilders with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionVisitor\\:\\:setCriterionQueryBuilders\\(\\) has parameter \\$criterionQueryBuilders with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionVisitor\\:\\:visitCriteria\\(\\) should return string but returns string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionVisitor.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionVisitor\\:\\:\\$criterionQueryBuilders \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\CriterionQueryBuilder\\>\\) does not accept iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/CriterionVisitor.php + + - + message: "#^Cannot call method fetch\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Doctrine\\\\DoctrineGateway\\:\\:buildQuery\\(\\) has parameter \\$columns with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Doctrine\\\\DoctrineGateway\\:\\:bulkFetchFieldValues\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Doctrine\\\\DoctrineGateway\\:\\:bulkFetchVersionNames\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Doctrine\\\\DoctrineGateway\\:\\:extractFieldValues\\(\\) has parameter \\$fieldValues with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Doctrine\\\\DoctrineGateway\\:\\:extractFieldValues\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Doctrine\\\\DoctrineGateway\\:\\:extractVersionData\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Doctrine\\\\DoctrineGateway\\:\\:extractVersionData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Doctrine\\\\DoctrineGateway\\:\\:extractVersionNames\\(\\) has parameter \\$names with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Doctrine\\\\DoctrineGateway\\:\\:extractVersionNames\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Doctrine\\\\DoctrineGateway\\:\\:find\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Doctrine\\\\DoctrineGateway\\:\\:getColumns\\(\\) return type has no value type specified in iterable type Traversable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Parameter \\#1 \\$firstResult of method Doctrine\\\\DBAL\\\\Query\\\\QueryBuilder\\:\\:setFirstResult\\(\\) expects int, null given\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Parameter \\#4 \\$condition of method Doctrine\\\\DBAL\\\\Query\\\\QueryBuilder\\:\\:leftJoin\\(\\) expects string\\|null, Doctrine\\\\DBAL\\\\Query\\\\Expression\\\\CompositeExpression given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\GatewayDataMapper\\:\\:mapContentMetadataToPersistenceContentInfo\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/GatewayDataMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\GatewayDataMapper\\:\\:mapRawDataToPersistenceContentItem\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/GatewayDataMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Mapper\\\\DoctrineGatewayDataMapper\\:\\:mapContentDataToPersistenceContent\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Mapper/DoctrineGatewayDataMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Mapper\\\\DoctrineGatewayDataMapper\\:\\:mapContentMetadataToPersistenceContentInfo\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Mapper/DoctrineGatewayDataMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Mapper\\\\DoctrineGatewayDataMapper\\:\\:mapFieldDataToPersistenceFieldList\\(\\) has parameter \\$rawVersionFields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Mapper/DoctrineGatewayDataMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Mapper\\\\DoctrineGatewayDataMapper\\:\\:mapFieldValueDataToStorageFieldValue\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Mapper/DoctrineGatewayDataMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Mapper\\\\DoctrineGatewayDataMapper\\:\\:mapRawDataToPersistenceContentItem\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Mapper/DoctrineGatewayDataMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Content\\\\Mapper\\\\DoctrineGatewayDataMapper\\:\\:mapVersionDataToPersistenceVersionInfo\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Mapper/DoctrineGatewayDataMapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataFloat \\(float\\) does not accept float\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Mapper/DoctrineGatewayDataMapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$dataInt \\(int\\) does not accept int\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Content/Mapper/DoctrineGatewayDataMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Gateway\\:\\:find\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Gateway.php + + - + message: "#^Cannot call method fetch\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Location\\\\Doctrine\\\\DoctrineGateway\\:\\:find\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Gateway/Location/Doctrine/DoctrineGateway.php + + - + message: "#^Parameter \\#1 \\$criterion of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Gateway\\:\\:count\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion\\|null given\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Filter/Handler/ContentFilteringHandler.php + + - + message: "#^Parameter \\#1 \\$criterion of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Gateway\\:\\:find\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion\\|null given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Handler/ContentFilteringHandler.php + + - + message: "#^Parameter \\#1 \\$criterion of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Gateway\\:\\:count\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion\\|null given\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Filter/Handler/LocationFilteringHandler.php + + - + message: "#^Parameter \\#1 \\$criterion of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\Gateway\\\\Gateway\\:\\:find\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion\\|null given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/Handler/LocationFilteringHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\SortClauseVisitor\\:\\:__construct\\(\\) has parameter \\$sortClauseQueryBuilders with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/SortClauseVisitor.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\SortClauseVisitor\\:\\:\\$sortClauseQueryBuilders \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\SortClauseQueryBuilder\\>\\) does not accept iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Filter/SortClauseVisitor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Handler\\:\\:beginTransaction\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Handler\\:\\:commit\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Handler\\:\\:rollback\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\:\\:getNotificationById\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Notification/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\:\\:loadUserNotifications\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Notification/Gateway.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\\\DoctrineDatabase\\:\\:getColumns\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\\\DoctrineDatabase\\:\\:getNotificationById\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\\\DoctrineDatabase\\:\\:loadUserNotifications\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Notification\\\\Notification\\:\\:\\$id \\(int\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabase.php + + - + message: "#^Dead catch \\- Doctrine\\\\DBAL\\\\DBALException\\|PDOException is never thrown in the try block\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\\\ExceptionConversion\\:\\:getNotificationById\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\\\ExceptionConversion\\:\\:loadUserNotifications\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Mapper\\:\\:extractNotificationFromRow\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Notification/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Mapper\\:\\:extractNotificationsFromRows\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Notification/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Setting\\\\Gateway\\:\\:loadSetting\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Setting/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Setting\\\\Gateway\\:\\:loadSettingById\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Setting/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Setting\\\\Gateway\\\\DoctrineDatabase\\:\\:loadSetting\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Setting/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Setting\\\\Gateway\\\\DoctrineDatabase\\:\\:loadSettingById\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Setting/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Setting\\\\Gateway\\\\ExceptionConversion\\:\\:loadSetting\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Setting/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Setting\\\\Gateway\\\\ExceptionConversion\\:\\:loadSettingById\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Setting/Gateway/ExceptionConversion.php + + - + message: "#^Argument of an invalid type array\\<Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\SharedGateway\\\\Gateway\\>\\|iterable supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/SharedGateway/GatewayFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\SharedGateway\\\\GatewayFactory\\:\\:__construct\\(\\) has parameter \\$gateways with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/SharedGateway/GatewayFactory.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\SharedGateway\\\\GatewayFactory\\:\\:\\$gateways \\(array\\<Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\SharedGateway\\\\Gateway\\>\\|iterable\\) does not accept iterable\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/SharedGateway/GatewayFactory.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\SharedGateway\\\\GatewayFactory\\:\\:\\$gateways has unknown class iterable as its type\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/SharedGateway/GatewayFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\AbstractGateway\\:\\:getAliasedColumns\\(\\) has parameter \\$columns with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/AbstractGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\AbstractGateway\\:\\:getAliasedColumns\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/AbstractGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\Gateway\\\\Token\\\\Doctrine\\\\DoctrineGateway\\:\\:getColumns\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\Gateway\\\\Token\\\\Doctrine\\\\DoctrineGateway\\:\\:getToken\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\Gateway\\\\Token\\\\Doctrine\\\\DoctrineGateway\\:\\:getTokenById\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\Gateway\\\\Token\\\\Gateway\\:\\:getToken\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/Gateway/Token/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\Gateway\\\\Token\\\\Gateway\\:\\:getTokenById\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/Gateway/Token/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\Gateway\\\\TokenType\\\\Doctrine\\\\DoctrineGateway\\:\\:getColumns\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\Gateway\\\\TokenType\\\\Doctrine\\\\DoctrineGateway\\:\\:getTypeById\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\Gateway\\\\TokenType\\\\Doctrine\\\\DoctrineGateway\\:\\:getTypeByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/Gateway/TokenType/Doctrine/DoctrineGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\Gateway\\\\TokenType\\\\Gateway\\:\\:getTypeById\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/Gateway/TokenType/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\Gateway\\\\TokenType\\\\Gateway\\:\\:getTypeByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/Gateway/TokenType/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\Mapper\\:\\:mapToken\\(\\) has parameter \\$tokenRow with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Token\\\\Mapper\\:\\:mapTokenType\\(\\) has parameter \\$tokenTypeRow with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/Token/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandler\\:\\:beginTransaction\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/TransactionHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandler\\:\\:commit\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/TransactionHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandler\\:\\:rollback\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/TransactionHandler.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandler\\:\\:\\$contentTypeHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\) does not accept Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/TransactionHandler.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandler\\:\\:\\$languageHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\) does not accept Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\|null\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/TransactionHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\:\\:findUsages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\:\\:loadUrlData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\:\\:loadUrlDataByUrl\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\:\\:updateUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 3 + path: src/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\\\DoctrineDatabase\\:\\:findUsages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\\\DoctrineDatabase\\:\\:getSelectColumns\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\\\DoctrineDatabase\\:\\:loadUrlData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\\\DoctrineDatabase\\:\\:loadUrlDataByUrl\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\\\DoctrineDatabase\\:\\:updateUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabase.php + + - + message: "#^Dead catch \\- Doctrine\\\\DBAL\\\\DBALException\\|PDOException is never thrown in the try block\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\\\ExceptionConversion\\:\\:findUsages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\\\ExceptionConversion\\:\\:loadUrlData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\\\ExceptionConversion\\:\\:loadUrlDataByUrl\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\\\ExceptionConversion\\:\\:updateUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Handler\\:\\:find\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Handler\\:\\:findUsages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Handler.php + + - + message: "#^Parameter \\#1 \\$url of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\:\\:loadUrlDataByUrl\\(\\) expects int, string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Mapper\\:\\:extractURLsFromRows\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriteriaConverter\\:\\:addHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Query/CriteriaConverter.php + + - + message: "#^Parameter \\#4 \\$condition of method Doctrine\\\\DBAL\\\\Query\\\\QueryBuilder\\:\\:innerJoin\\(\\) expects string\\|null, Doctrine\\\\DBAL\\\\Query\\\\Expression\\\\CompositeExpression given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Query/CriterionHandler/Base.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\Query\\\\Criterion\\:\\:\\$criteria\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalAnd.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\Query\\\\Criterion\\:\\:\\$criteria\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalNot.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\Query\\\\Criterion\\:\\:\\$criteria\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalOr.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\Query\\\\Criterion\\:\\:\\$sectionIds\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Query/CriterionHandler/SectionId.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\Query\\\\Criterion\\:\\:\\$sectionIdentifiers\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/URL/Query/CriterionHandler/SectionIdentifier.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\:\\:assignRole\\(\\) has parameter \\$limitation with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\:\\:load\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\:\\:loadByEmail\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\:\\:loadByLogin\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\:\\:loadUserByToken\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway.php + + - + message: "#^Cannot call method fetch\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway/DoctrineDatabase.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 4 + path: src/lib/Persistence/Legacy/User/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\\\DoctrineDatabase\\:\\:assignRole\\(\\) has parameter \\$limitation with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\\\DoctrineDatabase\\:\\:load\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\\\DoctrineDatabase\\:\\:loadByEmail\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\\\DoctrineDatabase\\:\\:loadByLogin\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\\\DoctrineDatabase\\:\\:loadUserByToken\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\\\ExceptionConversion\\:\\:assignRole\\(\\) has parameter \\$limitation with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\\\ExceptionConversion\\:\\:load\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\\\ExceptionConversion\\:\\:loadByEmail\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\\\ExceptionConversion\\:\\:loadByLogin\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\\\ExceptionConversion\\:\\:loadUserByToken\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler\\:\\:assignRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler\\:\\:assignRole\\(\\) has parameter \\$limitation with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler\\:\\:deletePolicy\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler\\:\\:deleteRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler\\:\\:expireUserToken\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler\\:\\:publishRoleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler\\:\\:removeRoleAssignment\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler\\:\\:unassignRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler\\:\\:update\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler\\:\\:updatePolicy\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler\\:\\:updateRole\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler\\:\\:updateUserToken\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Parameter \\#2 \\$limitations of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\:\\:addPolicyLimitations\\(\\) expects array, array\\|string given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Handler.php + + - + message: "#^Cannot access offset mixed on non\\-empty\\-array\\<int\\|string, mixed\\>\\|Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\RoleAssignment\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Mapper\\:\\:mapPolicies\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Mapper\\:\\:mapRole\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Mapper\\:\\:mapRoleAssignments\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Mapper\\:\\:mapRoles\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Mapper\\:\\:mapUser\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Mapper\\:\\:mapUsers\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Mapper.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Role\\:\\:\\$policies \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Policy\\>\\) does not accept array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyCreateStruct\\>\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Mapper.php + + - + message: "#^array\\|string does not accept array\\<int, mixed\\>\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\:\\:addPolicyLimitations\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\:\\:loadPoliciesByUserId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\:\\:loadRole\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\:\\:loadRoleAssignment\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\:\\:loadRoleAssignmentsByGroupId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\:\\:loadRoleAssignmentsByRoleId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\:\\:loadRoleAssignmentsByRoleIdWithOffsetAndLimit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\:\\:loadRoleByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\:\\:loadRoleDraftByRoleId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\:\\:loadRoles\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\:\\:loadRolesForContentObjects\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 9 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:addPolicyLimitations\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:fetchUserGroups\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:loadPoliciesByUserId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:loadPolicyLimitationValues\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:loadRole\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:loadRoleAssignment\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:loadRoleAssignmentsByGroupId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:loadRoleAssignmentsByRoleId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:loadRoleAssignmentsByRoleIdWithOffsetAndLimit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:loadRoleByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:loadRoleDraftByRoleId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:loadRoles\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:loadRolesForContentObjects\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Parameter \\#2 \\$y of method Doctrine\\\\DBAL\\\\Query\\\\Expression\\\\ExpressionBuilder\\:\\:in\\(\\) expects array\\<string\\>\\|string, array\\<int\\> given\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\:\\:\\$dbPlatform is never read, only written\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\ExceptionConversion\\:\\:addPolicyLimitations\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\ExceptionConversion\\:\\:loadPoliciesByUserId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\ExceptionConversion\\:\\:loadRole\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\ExceptionConversion\\:\\:loadRoleAssignment\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\ExceptionConversion\\:\\:loadRoleAssignmentsByGroupId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\ExceptionConversion\\:\\:loadRoleAssignmentsByRoleId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\ExceptionConversion\\:\\:loadRoleAssignmentsByRoleIdWithOffsetAndLimit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\ExceptionConversion\\:\\:loadRoleByIdentifier\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\ExceptionConversion\\:\\:loadRoleDraftByRoleId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\ExceptionConversion\\:\\:loadRoles\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\ExceptionConversion\\:\\:loadRolesForContentObjects\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\LimitationConverter\\:\\:addHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/LimitationConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\LimitationConverter\\:\\:toLegacy\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/LimitationConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\LimitationConverter\\:\\:toSPI\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/LimitationConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\LimitationHandler\\:\\:\\$connection has no type specified\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/LimitationHandler.php + + - + message: "#^Argument of an invalid type array\\|string supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/LimitationHandler/ObjectStateHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\LimitationHandler\\\\ObjectStateHandler\\:\\:getGroupMap\\(\\) has parameter \\$limitIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/LimitationHandler/ObjectStateHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\LimitationHandler\\\\ObjectStateHandler\\:\\:getGroupMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/User/Role/LimitationHandler/ObjectStateHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Gateway\\:\\:getUserPreferenceByUserIdAndName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/UserPreference/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Gateway\\:\\:loadUserPreferences\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/UserPreference/Gateway.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 2 + path: src/lib/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Gateway\\\\DoctrineDatabase\\:\\:getColumns\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Gateway\\\\DoctrineDatabase\\:\\:getUserPreferenceByUserIdAndName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Gateway\\\\DoctrineDatabase\\:\\:loadUserPreferences\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Gateway\\\\ExceptionConversion\\:\\:getUserPreferenceByUserIdAndName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/UserPreference/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Gateway\\\\ExceptionConversion\\:\\:loadUserPreferences\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/UserPreference/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Mapper\\:\\:extractUserPreferenceFromRow\\(\\) has parameter \\$row with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/UserPreference/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Mapper\\:\\:extractUserPreferencesFromRows\\(\\) has parameter \\$rows with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/Legacy/UserPreference/Mapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\:\\:__construct\\(\\) has parameter \\$ruleFiles with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\:\\:getRules\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\:\\:transform\\(\\) has parameter \\$ruleNames with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\:\\:transform\\(\\) should return string but returns string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\:\\:transformByGroup\\(\\) should return string but returns string\\|null\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor.php + + - + message: "#^Parameter \\#3 \\$subject of function preg_replace_callback expects array\\|string, string\\|null given\\.$#" + count: 2 + path: src/lib/Persistence/TransformationProcessor.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\:\\:\\$compiledRules type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\:\\:\\$ruleFiles type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased\\:\\:__construct\\(\\) has parameter \\$ruleFiles with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased\\:\\:getRules\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased\\\\Parser\\:\\:filterValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased\\\\Parser\\:\\:filterValues\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased\\\\Parser\\:\\:parse\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased\\\\Parser\\:\\:parseString\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased\\\\Parser\\:\\:tokenize\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php + + - + message: "#^Parameter \\#1 \\$string of method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased\\\\Parser\\:\\:parseString\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased\\\\Parser\\:\\:\\$tokenSpecifications type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PcreCompiler\\:\\:compile\\(\\) has parameter \\$ast with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PcreCompiler\\:\\:compile\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PcreCompiler\\:\\:compileMap\\(\\) has parameter \\$rule with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PcreCompiler\\:\\:compileMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PcreCompiler\\:\\:compileReplace\\(\\) has parameter \\$rule with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PcreCompiler\\:\\:compileReplace\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PcreCompiler\\:\\:compileRule\\(\\) has parameter \\$rule with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PcreCompiler\\:\\:compileRule\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PcreCompiler\\:\\:compileTranspose\\(\\) has parameter \\$rule with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PcreCompiler\\:\\:compileTranspose\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PcreCompiler\\:\\:compileTransposeModulo\\(\\) has parameter \\$rule with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PcreCompiler\\:\\:compileTransposeModulo\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PcreCompiler\\:\\:hexdec\\(\\) should return int but returns float\\|int\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Parameter \\#1 \\$charCode of method Ibexa\\\\Core\\\\Persistence\\\\Utf8Converter\\:\\:toUTF8Character\\(\\) expects int, float\\|int given\\.$#" + count: 2 + path: src/lib/Persistence/TransformationProcessor/PcreCompiler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PreprocessedBased\\:\\:__construct\\(\\) has parameter \\$ruleFiles with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PreprocessedBased.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PreprocessedBased\\:\\:getRules\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PreprocessedBased.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$installDir$#" + count: 1 + path: src/lib/Persistence/TransformationProcessor/PreprocessedBased.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Utf8Converter\\:\\:toUnicodeCodepoint\\(\\) should return int but returns false\\.$#" + count: 5 + path: src/lib/Persistence/Utf8Converter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Utf8Converter\\:\\:toUnicodeCodepoint\\(\\) should return int but returns int\\<0, 2147483647\\>\\|false\\.$#" + count: 1 + path: src/lib/Persistence/Utf8Converter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Query\\\\QueryFactory\\:\\:create\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Query/QueryFactory.php + + - + message: "#^Method Ibexa\\\\Core\\\\Query\\\\QueryFactoryInterface\\:\\:create\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Query/QueryFactoryInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\ArrayQueryTypeRegistry\\:\\:addQueryType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/QueryType/ArrayQueryTypeRegistry.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\ArrayQueryTypeRegistry\\:\\:addQueryTypes\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/QueryType/ArrayQueryTypeRegistry.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\AbstractLocationQueryType\\:\\:resolveLocation\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/AbstractLocationQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\AbstractQueryType\\:\\:buildFilters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/AbstractQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\AbstractQueryType\\:\\:doGetQuery\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/AbstractQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\AbstractQueryType\\:\\:getQueryFilter\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/AbstractQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\AncestorsQueryType\\:\\:getQueryFilter\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/AncestorsQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\ChildrenQueryType\\:\\:getQueryFilter\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/ChildrenQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\GeoLocationQueryType\\:\\:getQueryFilter\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/GeoLocationQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\RelatedToContentQueryType\\:\\:getQueryFilter\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/RelatedToContentQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SiblingsQueryType\\:\\:getQueryFilter\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SiblingsQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\Exception\\\\SyntaxErrorException\\:\\:fromUnexpectedToken\\(\\) has parameter \\$expectedTypes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SortSpec/Exception/SyntaxErrorException.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\Exception\\\\UnsupportedSortClauseException\\:\\:__construct\\(\\) has parameter \\$code with no type specified\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SortSpec/Exception/UnsupportedSortClauseException.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortClauseParser\\\\DefaultSortClauseParser\\:\\:__construct\\(\\) has parameter \\$valueObjectClassMap with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/DefaultSortClauseParser.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortClauseParser\\\\DefaultSortClauseParser\\:\\:parse\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\SortClause but returns object\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/DefaultSortClauseParser.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortClauseParserDispatcher\\:\\:__construct\\(\\) has parameter \\$parsers with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SortSpec/SortClauseParserDispatcher.php + + - + message: "#^Property Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortClauseParserDispatcher\\:\\:\\$parsers \\(array\\<Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortClauseParserInterface\\>\\) does not accept iterable\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SortSpec/SortClauseParserDispatcher.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortSpecLexer\\:\\:consume\\(\\) should return Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\Token but returns Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\Token\\|null\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SortSpec/SortSpecLexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortSpecLexer\\:\\:getAll\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SortSpec/SortSpecLexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortSpecLexer\\:\\:split\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SortSpec/SortSpecLexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortSpecLexer\\:\\:split\\(\\) should return array but returns array\\<int, array\\<int, int\\<0, max\\>\\|string\\>\\>\\|false\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SortSpec/SortSpecLexer.php + + - + message: "#^Parameter \\#2 \\$token of static method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\Exception\\\\SyntaxErrorException\\:\\:fromUnexpectedToken\\(\\) expects Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\Token, Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\Token\\|null given\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SortSpec/SortSpecParser.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortSpecParserInterface\\:\\:parseSortClausesList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SortSpec/SortSpecParserInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\BuiltIn\\\\SubtreeQueryType\\:\\:getQueryFilter\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/BuiltIn/SubtreeQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\OptionsResolverBasedQueryType\\:\\:configureOptions\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/QueryType/OptionsResolverBasedQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\OptionsResolverBasedQueryType\\:\\:doGetQuery\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/OptionsResolverBasedQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\OptionsResolverBasedQueryType\\:\\:getQuery\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/OptionsResolverBasedQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\OptionsResolverBasedQueryType\\:\\:getSupportedParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/OptionsResolverBasedQueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\QueryParameterContentViewQueryTypeMapper\\:\\:extractParameters\\(\\) has parameter \\$queryParameterValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\QueryParameterContentViewQueryTypeMapper\\:\\:extractParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\QueryParameterContentViewQueryTypeMapper\\:\\:extractParametersFromContentView\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\QueryParameterContentViewQueryTypeMapper\\:\\:map\\(\\) should return Ibexa\\\\Core\\\\QueryType\\\\QueryType but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\.$#" + count: 1 + path: src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php + + - + message: "#^Parameter \\#2 \\$code of class InvalidArgumentException constructor expects int, string given\\.$#" + count: 1 + path: src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\QueryType\\:\\:getQuery\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/QueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\QueryType\\:\\:getSupportedParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/QueryType/QueryType.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\QueryTypeRegistry\\:\\:addQueryType\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/QueryType/QueryTypeRegistry.php + + - + message: "#^Method Ibexa\\\\Core\\\\QueryType\\\\QueryTypeRegistry\\:\\:addQueryTypes\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/QueryType/QueryTypeRegistry.php + + - + message: "#^Argument of an invalid type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentMetadataUpdateStruct supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\PermissionResolver\\:\\:getQueryPermissionsCriterion\\(\\)\\.$#" + count: 2 + path: src/lib/Repository/ContentService.php + + - + message: "#^Cannot access offset int on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\>\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Cannot access offset string on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\>\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Cannot access property \\$defaultValue on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 2 + path: src/lib/Repository/ContentService.php + + - + message: "#^Cannot access property \\$fieldTypeIdentifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 3 + path: src/lib/Repository/ContentService.php + + - + message: "#^Cannot access property \\$id on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Cannot access property \\$id on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Cannot access property \\$identifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 3 + path: src/lib/Repository/ContentService.php + + - + message: "#^Cannot access property \\$isRequired on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Cannot access property \\$isTranslatable on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 2 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\\\Handler\\:\\:publishUrlAliasForLocation\\(\\) invoked with 6 parameters, 4\\-5 required\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:__construct\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:copyTranslationsFromPublishedVersion\\(\\) has parameter \\$translations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:internalLoadContentById\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:internalLoadContentByRemoteId\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:internalLoadContentBySPIContentInfo\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:internalUpdateContent\\(\\) has parameter \\$fieldIdentifiersToValidate with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentByContentInfo\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentByRemoteId\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:validate\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:validate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\|null if not set the draft is created with the initialLanguage code of the source version or if not present with the main language\\.\\)\\: Unexpected token \"if\", expected variable at offset 565$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$content \\\\Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\)\\: Unexpected token \"\\$content\", expected type at offset 9$#" + count: 2 + path: src/lib/Repository/ContentService.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$fieldType \\\\Ibexa\\\\Core\\\\FieldType\\\\FieldType\\)\\: Unexpected token \"\\$fieldType\", expected type at offset 9$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$relations \\\\Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\[\\]\\)\\: Unexpected token \"\\$relations\", expected type at offset 9$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Parameter \\#1 \\$callback of function array_map expects \\(callable\\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\)\\: mixed\\)\\|null, Closure\\(Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\)\\: int given\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Parameter \\#1 \\$locationId of class Ibexa\\\\Contracts\\\\Core\\\\Limitation\\\\Target\\\\DestinationLocation constructor expects int, int\\|null given\\.$#" + count: 2 + path: src/lib/Repository/ContentService.php + + - + message: "#^Parameter \\#2 \\$array of function array_map expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\> given\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Parameter \\#3 \\$prioritizedLanguages of method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildContentDomainObjectFromPersistence\\(\\) expects array\\<string\\>, array\\<string\\>\\|null given\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Parameter \\#5 \\$fieldDefinitionId of method Ibexa\\\\Core\\\\Repository\\\\Helper\\\\RelationProcessor\\:\\:appendFieldRelations\\(\\) expects string, int given\\.$#" + count: 2 + path: src/lib/Repository/ContentService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\MetadataUpdateStruct\\:\\:\\$publicationDate \\(int\\) does not accept int\\|null\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentCreateStruct\\:\\:\\$modificationDate \\(DateTime\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:\\$repository \\(Ibexa\\\\Core\\\\Repository\\\\Repository\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\ContentService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:__construct\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ContentTypeService.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$fieldType \\\\Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\)\\: Unexpected token \"\\$fieldType\", expected type at offset 9$#" + count: 2 + path: src/lib/Repository/ContentTypeService.php + + - + message: "#^Parameter \\#1 \\$errors of class Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeFieldDefinitionValidationException constructor expects array\\<Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\>, array\\<string, array\\<Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValidationError\\>\\> given\\.$#" + count: 2 + path: src/lib/Repository/ContentTypeService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeCreateStruct\\:\\:\\$descriptions \\(array\\) on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: src/lib/Repository/ContentTypeService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeCreateStruct\\:\\:\\$isContainer \\(bool\\) on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: src/lib/Repository/ContentTypeService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeCreateStruct\\:\\:\\$nameSchema \\(string\\) on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: src/lib/Repository/ContentTypeService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeCreateStruct\\:\\:\\$urlAliasSchema \\(string\\) on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: src/lib/Repository/ContentTypeService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeUpdateStruct\\:\\:\\$descriptions \\(array\\) on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: src/lib/Repository/ContentTypeService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeUpdateStruct\\:\\:\\$names \\(array\\) on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: src/lib/Repository/ContentTypeService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ContentTypeService.php + + - + message: "#^Cannot access property \\$fieldTypeIdentifier on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 1 + path: src/lib/Repository/Helper/NameSchemaService.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:getName\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: src/lib/Repository/Helper/NameSchemaService.php + + - + message: "#^Argument of an invalid type array\\<int, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\>\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Repository/Helper/RelationProcessor.php + + - + message: "#^Cannot access property \\$destinationContentInfo on array\\<int, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\>\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\.$#" + count: 1 + path: src/lib/Repository/Helper/RelationProcessor.php + + - + message: "#^Cannot access property \\$id on array\\<int, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\>\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\.$#" + count: 1 + path: src/lib/Repository/Helper/RelationProcessor.php + + - + message: "#^Cannot call method error\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/lib/Repository/Helper/RelationProcessor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Helper\\\\RelationProcessor\\:\\:appendFieldRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Repository/Helper/RelationProcessor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Helper\\\\RelationProcessor\\:\\:appendFieldRelations\\(\\) has parameter \\$locationIdToContentIdMapping with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Helper/RelationProcessor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Helper\\\\RelationProcessor\\:\\:appendFieldRelations\\(\\) has parameter \\$relations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Helper/RelationProcessor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Helper\\\\RelationProcessor\\:\\:processFieldRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Repository/Helper/RelationProcessor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Helper\\\\RelationProcessor\\:\\:processFieldRelations\\(\\) has parameter \\$inputRelations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Helper/RelationProcessor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\LanguageService\\:\\:__construct\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/LanguageService.php + + - + message: "#^PHPDoc tag @param for parameter \\$languageId with type mixed is not subtype of native type int\\.$#" + count: 1 + path: src/lib/Repository/LanguageService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\LanguageService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/LanguageService.php + + - + message: "#^Variable \\$location might not be defined\\.$#" + count: 1 + path: src/lib/Repository/LocationResolver/PermissionAwareLocationResolver.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LocationList\\:\\:getContentInfo\\(\\)\\.$#" + count: 1 + path: src/lib/Repository/LocationService.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LocationList\\:\\:getLocation\\(\\)\\.$#" + count: 1 + path: src/lib/Repository/LocationService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Handler\\:\\:copySubtree\\(\\) invoked with 3 parameters, 2 required\\.$#" + count: 1 + path: src/lib/Repository/LocationService.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\\\Handler\\:\\:publishUrlAliasForLocation\\(\\) invoked with 6 parameters, 4\\-5 required\\.$#" + count: 1 + path: src/lib/Repository/LocationService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\LocationService\\:\\:__construct\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/LocationService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocationList\\(\\) has parameter \\$locationIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/LocationService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\LocationService\\:\\:\\$repository \\(Ibexa\\\\Core\\\\Repository\\\\Repository\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository\\.$#" + count: 1 + path: src/lib/Repository/LocationService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\LocationService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/LocationService.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentId\\.$#" + count: 4 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentTypeId\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" + count: 2 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$mainLanguageCode\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Cannot access offset mixed on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\>\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Cannot call method error\\(\\) on Psr\\\\Log\\\\LoggerInterface\\|null\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildContentDomainObject\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildContentDomainObjectsOnSearchResult\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildContentDomainObjectsOnSearchResult\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ContentInfo\\> but returns array\\<int\\<0, max\\>, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\>\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildContentProxy\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildLocation\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildLocationDomainObject\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildLocationDomainObjectsOnSearchResult\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildLocationDomainObjectsOnSearchResult\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\> but returns array\\<int\\<0, max\\>, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\>\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildVersionInfoDomainObject\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:validateTranslatedList\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Parameter \\#1 \\$spiLocation of method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildLocationWithContent\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject given\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentLocationMapper\\\\DecoratedLocationService\\:\\:loadLocationList\\(\\) has parameter \\$locationIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationService.php + + - + message: "#^Cannot access property \\$value on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 2 + path: src/lib/Repository/Mapper/ContentMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentMapper\\:\\:cloneField\\(\\) has parameter \\$overrides with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentMapper\\:\\:getFieldsForCreate\\(\\) has parameter \\$createdFields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentMapper\\:\\:getFieldsForCreate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentMapper\\:\\:mapFieldsForCreate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentMapper\\:\\:mapFieldsForUpdate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentMapper.php + + - + message: "#^Parameter \\#3 \\$fieldDefinition of method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentMapper\\:\\:getFieldValueForUpdate\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentMapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentMapper\\:\\:\\$contentLanguageHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentMapper.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeDraft\\:\\:\\$descriptions\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentTypeDomainMapper.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeDraft\\:\\:\\$names\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentTypeDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentTypeDomainMapper\\:\\:buildContentTypeDomainObject\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentTypeDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentTypeDomainMapper\\:\\:buildContentTypeGroupDomainObject\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ContentTypeDomainMapper.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$fieldType \\\\Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\)\\: Unexpected token \"\\$fieldType\", expected type at offset 9$#" + count: 3 + path: src/lib/Repository/Mapper/ContentTypeDomainMapper.php + + - + message: "#^Parameter \\#1 \\$errors of class Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\ContentTypeFieldDefinitionValidationException constructor expects array\\<Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\>, array\\<Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValidationError\\> given\\.$#" + count: 2 + path: src/lib/Repository/Mapper/ContentTypeDomainMapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ProxyAwareDomainMapper\\:\\:\\$proxyFactory \\(Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperInterface\\) does not accept Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperInterface\\|null\\.$#" + count: 1 + path: src/lib/Repository/Mapper/ProxyAwareDomainMapper.php + + - + message: "#^Argument of an invalid type array\\|string supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Repository/Mapper/RoleDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\RoleDomainMapper\\:\\:fillRoleStructWithPolicies\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Mapper/RoleDomainMapper.php + + - + message: "#^Parameter \\#1 \\$limitationValues of method Ibexa\\\\Contracts\\\\Core\\\\Limitation\\\\Type\\:\\:buildValue\\(\\) expects array, array\\|null given\\.$#" + count: 2 + path: src/lib/Repository/Mapper/RoleDomainMapper.php + + - + message: "#^Parameter \\#3 \\$limitations of method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\RoleDomainMapper\\:\\:buildPersistencePolicyObject\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\>, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\> given\\.$#" + count: 1 + path: src/lib/Repository/Mapper/RoleDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:extractTokens\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/NameSchema/NameSchemaService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:getIdentifiers\\(\\) should return array\\<string\\> but returns array\\<int\\<0, max\\>, string\\|false\\>\\.$#" + count: 1 + path: src/lib/Repository/NameSchema/NameSchemaService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:resolveToken\\(\\) has parameter \\$groupLookupTable with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/NameSchema/NameSchemaService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:resolveToken\\(\\) has parameter \\$titles with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/NameSchema/NameSchemaService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:tokenParts\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/NameSchema/NameSchemaService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:tokenParts\\(\\) should return array but returns array\\<int, string\\>\\|false\\.$#" + count: 1 + path: src/lib/Repository/NameSchema/NameSchemaService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/NameSchema/NameSchemaService.php + + - + message: "#^Parameter \\#1 \\$module of class Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\UnauthorizedException constructor expects string, int\\<min, \\-1\\>\\|int\\<1, max\\> given\\.$#" + count: 1 + path: src/lib/Repository/NotificationService.php + + - + message: "#^Parameter \\#2 \\$whatIsWrong of class Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\InvalidArgumentException constructor expects string, int given\\.$#" + count: 1 + path: src/lib/Repository/NotificationService.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectState\\:\\:\\$defaultLanguageCode\\.$#" + count: 1 + path: src/lib/Repository/ObjectStateService.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectState\\:\\:\\$prioritizedLanguages\\.$#" + count: 1 + path: src/lib/Repository/ObjectStateService.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroup\\:\\:\\$prioritizedLanguages\\.$#" + count: 1 + path: src/lib/Repository/ObjectStateService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ObjectStateService\\:\\:__construct\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ObjectStateService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ObjectStateService\\:\\:buildDomainObjectStateGroupObject\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ObjectStateService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\ObjectStateService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ObjectStateService.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\PermissionResolver\\:\\:sudo\\(\\)\\.$#" + count: 1 + path: src/lib/Repository/Permission/CachedPermissionService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionService\\:\\:getPermissionsCriterion\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Permission/CachedPermissionService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionService\\:\\:sudo\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Repository/Permission/CachedPermissionService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionService\\:\\:\\$permissionCriterion \\(bool\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\) does not accept null\\.$#" + count: 1 + path: src/lib/Repository/Permission/CachedPermissionService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Permission\\\\LimitationService\\:\\:__construct\\(\\) has parameter \\$limitationTypes with no value type specified in iterable type Traversable\\.$#" + count: 1 + path: src/lib/Repository/Permission/LimitationService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Permission\\\\LimitationService\\:\\:validateLimitations\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValidationError\\> but returns array\\<string, array\\<Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValidationError\\>\\>\\.$#" + count: 1 + path: src/lib/Repository/Permission/LimitationService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolver\\:\\:getCriterionForLimitation\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionCriterionResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolver\\:\\:getPermissionsCriterion\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionCriterionResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolver\\:\\:getPermissionsCriterion\\(\\) should return bool\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\CriterionInterface\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionCriterionResolver.php + + - + message: "#^PHPDoc tag @var does not specify variable name\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionCriterionResolver.php + + - + message: "#^Parameter \\#1 \\$criteria of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\LogicalAnd constructor expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\>, array\\<int, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\CriterionInterface\\> given\\.$#" + count: 2 + path: src/lib/Repository/Permission/PermissionCriterionResolver.php + + - + message: "#^Parameter \\#1 \\$criteria of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\LogicalOr constructor expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\>, array\\<int, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\CriterionInterface\\> given\\.$#" + count: 2 + path: src/lib/Repository/Permission/PermissionCriterionResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Permission\\\\PermissionResolver\\:\\:__construct\\(\\) has parameter \\$policyMap with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Permission\\\\PermissionResolver\\:\\:isDeniedByRoleLimitation\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Permission\\\\PermissionResolver\\:\\:isGrantedByLimitation\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Permission\\\\PermissionResolver\\:\\:prepareTargetsForType\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Permission\\\\PermissionResolver\\:\\:prepareTargetsForType\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionResolver.php + + - + message: "#^PHPDoc tag @param for parameter \\$callback contains unresolvable type\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionResolver.php + + - + message: "#^PHPDoc tag @var does not specify variable name\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionResolver.php + + - + message: "#^Parameter \\#1 \\$limitationValues of method Ibexa\\\\Contracts\\\\Core\\\\Limitation\\\\Type\\:\\:buildValue\\(\\) expects array, array\\|null given\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionResolver.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Permission\\\\PermissionResolver\\:\\:\\$currentUserRef \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserReference\\) in empty\\(\\) is not falsy\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionResolver.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Permission\\\\PermissionResolver\\:\\:\\$policyMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Permission/PermissionResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapper\\:\\:createContentProxy\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapper\\:\\:createContentTypeGroupProxy\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapper\\:\\:createContentTypeGroupProxyList\\(\\) has parameter \\$contentTypeGroupIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapper\\:\\:createContentTypeGroupProxyList\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapper\\:\\:createContentTypeGroupProxyList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapper\\:\\:createContentTypeProxy\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapper\\:\\:createLanguageProxyList\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapper\\:\\:createLanguageProxyList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapper\\:\\:createLocationProxy\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapper\\:\\:createUserProxy\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Parameter \\#2 \\$initializer of method ProxyManager\\\\Factory\\\\LazyLoadingValueHolderFactory\\:\\:createProxy\\(\\) expects Closure\\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeGroup\\|null\\=, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeGroup&ProxyManager\\\\Proxy\\\\ValueHolderInterface\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeGroup\\>&ProxyManager\\\\Proxy\\\\VirtualProxyInterface\\=, string\\=, array\\<string, mixed\\>\\=, Closure\\|null\\=\\)\\: bool, Closure\\(mixed, ProxyManager\\\\Proxy\\\\LazyLoadingInterface, mixed, array, mixed\\)\\: true given\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Parameter \\#2 \\$initializer of method ProxyManager\\\\Factory\\\\LazyLoadingValueHolderFactory\\:\\:createProxy\\(\\) expects Closure\\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\|null\\=, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType&ProxyManager\\\\Proxy\\\\ValueHolderInterface\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\>&ProxyManager\\\\Proxy\\\\VirtualProxyInterface\\=, string\\=, array\\<string, mixed\\>\\=, Closure\\|null\\=\\)\\: bool, Closure\\(mixed, ProxyManager\\\\Proxy\\\\LazyLoadingInterface, mixed, array, mixed\\)\\: true given\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Parameter \\#2 \\$initializer of method ProxyManager\\\\Factory\\\\LazyLoadingValueHolderFactory\\:\\:createProxy\\(\\) expects Closure\\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo\\|null\\=, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo&ProxyManager\\\\Proxy\\\\ValueHolderInterface\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo\\>&ProxyManager\\\\Proxy\\\\VirtualProxyInterface\\=, string\\=, array\\<string, mixed\\>\\=, Closure\\|null\\=\\)\\: bool, Closure\\(mixed, ProxyManager\\\\Proxy\\\\LazyLoadingInterface, mixed, array, mixed\\)\\: true given\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Parameter \\#2 \\$initializer of method ProxyManager\\\\Factory\\\\LazyLoadingValueHolderFactory\\:\\:createProxy\\(\\) expects Closure\\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\|null\\=, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content&ProxyManager\\\\Proxy\\\\ValueHolderInterface\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\>&ProxyManager\\\\Proxy\\\\VirtualProxyInterface\\=, string\\=, array\\<string, mixed\\>\\=, Closure\\|null\\=\\)\\: bool, Closure\\(mixed, ProxyManager\\\\Proxy\\\\LazyLoadingInterface, mixed, array, mixed\\)\\: true given\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Parameter \\#2 \\$initializer of method ProxyManager\\\\Factory\\\\LazyLoadingValueHolderFactory\\:\\:createProxy\\(\\) expects Closure\\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\|null\\=, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language&ProxyManager\\\\Proxy\\\\ValueHolderInterface\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\>&ProxyManager\\\\Proxy\\\\VirtualProxyInterface\\=, string\\=, array\\<string, mixed\\>\\=, Closure\\|null\\=\\)\\: bool, Closure\\(mixed, ProxyManager\\\\Proxy\\\\LazyLoadingInterface, mixed, array, mixed\\)\\: true given\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Parameter \\#2 \\$initializer of method ProxyManager\\\\Factory\\\\LazyLoadingValueHolderFactory\\:\\:createProxy\\(\\) expects Closure\\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\=, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location&ProxyManager\\\\Proxy\\\\ValueHolderInterface\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>&ProxyManager\\\\Proxy\\\\VirtualProxyInterface\\=, string\\=, array\\<string, mixed\\>\\=, Closure\\|null\\=\\)\\: bool, Closure\\(mixed, ProxyManager\\\\Proxy\\\\LazyLoadingInterface, mixed, array, mixed\\)\\: true given\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Parameter \\#2 \\$initializer of method ProxyManager\\\\Factory\\\\LazyLoadingValueHolderFactory\\:\\:createProxy\\(\\) expects Closure\\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Section\\|null\\=, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Section&ProxyManager\\\\Proxy\\\\ValueHolderInterface\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Section\\>&ProxyManager\\\\Proxy\\\\VirtualProxyInterface\\=, string\\=, array\\<string, mixed\\>\\=, Closure\\|null\\=\\)\\: bool, Closure\\(mixed, ProxyManager\\\\Proxy\\\\LazyLoadingInterface, mixed, array, mixed\\)\\: true given\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Parameter \\#2 \\$initializer of method ProxyManager\\\\Factory\\\\LazyLoadingValueHolderFactory\\:\\:createProxy\\(\\) expects Closure\\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\|null\\=, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User&ProxyManager\\\\Proxy\\\\ValueHolderInterface\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\>&ProxyManager\\\\Proxy\\\\VirtualProxyInterface\\=, string\\=, array\\<string, mixed\\>\\=, Closure\\|null\\=\\)\\: bool, Closure\\(mixed, ProxyManager\\\\Proxy\\\\LazyLoadingInterface, mixed, array, mixed\\)\\: true given\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapper\\:\\:\\$proxyGenerator \\(ProxyManager\\\\Factory\\\\LazyLoadingValueHolderFactory\\) does not accept Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyGeneratorInterface\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperInterface\\:\\:createContentProxy\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapperInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperInterface\\:\\:createContentTypeGroupProxy\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapperInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperInterface\\:\\:createContentTypeGroupProxyList\\(\\) has parameter \\$contentTypeGroupIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapperInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperInterface\\:\\:createContentTypeGroupProxyList\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapperInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperInterface\\:\\:createContentTypeGroupProxyList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapperInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperInterface\\:\\:createContentTypeProxy\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapperInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperInterface\\:\\:createLanguageProxyList\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapperInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperInterface\\:\\:createLanguageProxyList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapperInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperInterface\\:\\:createLocationProxy\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapperInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperInterface\\:\\:createUserProxy\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyDomainMapperInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyGenerator\\:\\:createProxy\\(\\) should return ProxyManager\\\\Proxy\\\\VirtualProxyInterface&T but returns ProxyManager\\\\Proxy\\\\VirtualProxyInterface\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyGenerator\\:\\:warmUp\\(\\) has parameter \\$classes with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyGenerator.php + + - + message: "#^Offset 'message' does not exist on array\\{type\\: int, message\\: string, file\\: string, line\\: int\\}\\|null\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyGenerator.php + + - + message: "#^Unable to resolve the template type RealObjectType in call to method ProxyManager\\\\Factory\\\\LazyLoadingValueHolderFactory\\:\\:createProxy\\(\\)$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyGeneratorInterface\\:\\:warmUp\\(\\) has parameter \\$classes with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Repository/ProxyFactory/ProxyGeneratorInterface.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\PermissionResolver\\:\\:sudo\\(\\)\\.$#" + count: 1 + path: src/lib/Repository/Repository.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Repository\\:\\:__construct\\(\\) has parameter \\$serviceSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Repository.php + + - + message: "#^Parameter \\#4 \\$nameSchemaService of class Ibexa\\\\Core\\\\Repository\\\\LocationService constructor expects Ibexa\\\\Core\\\\Repository\\\\Helper\\\\NameSchemaService, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceInterface given\\.$#" + count: 1 + path: src/lib/Repository/Repository.php + + - + message: "#^Parameter \\#5 \\$backgroundIndexer of class Ibexa\\\\Core\\\\Repository\\\\SearchService constructor expects Ibexa\\\\Core\\\\Search\\\\Common\\\\BackgroundIndexer, Ibexa\\\\Core\\\\Search\\\\Common\\\\BackgroundIndexer\\|null given\\.$#" + count: 1 + path: src/lib/Repository/Repository.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Repository\\:\\:\\$proxyDomainMapperFactory \\(Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperFactory\\) does not accept Ibexa\\\\Core\\\\Repository\\\\ProxyFactory\\\\ProxyDomainMapperFactoryInterface\\.$#" + count: 1 + path: src/lib/Repository/Repository.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Repository\\:\\:\\$serviceSettings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Repository.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Repository\\:\\:\\$thumbnailStrategy is never read, only written\\.$#" + count: 1 + path: src/lib/Repository/Repository.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleAssignment\\:\\:\\$id\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\RoleService\\:\\:__construct\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\RoleService\\:\\:buildRoleAssignmentsFromPersistence\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\RoleService\\:\\:checkAssignmentAndFilterLimitationValues\\(\\) has parameter \\$limitation with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\RoleService\\:\\:checkAssignmentAndFilterLimitationValues\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\RoleService\\:\\:getRoleAssignmentsForUser\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\RoleService\\:\\:loadRole\\(\\) should return Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\Role but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Role\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\RoleService\\:\\:loadRoleAssignment\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleAssignment but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupRoleAssignment\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserRoleAssignment\\|null\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\RoleService\\:\\:updatePolicyByRoleDraft\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\RoleService\\:\\:validatePolicy\\(\\) should return iterable\\<array\\<Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\>\\> but returns array\\<Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValidationError\\>\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\RoleService\\:\\:validateRoleCreateStruct\\(\\) should return iterable\\<array\\<array\\<Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\>\\>\\> but returns array\\<int\\|string, iterable\\<array\\<Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\>\\>\\>\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Parameter \\#1 \\$errors of class Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException constructor expects array\\<Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\>, array\\<Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValidationError\\> given\\.$#" + count: 2 + path: src/lib/Repository/RoleService.php + + - + message: "#^Parameter \\#1 \\$errors of class Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException constructor expects array\\<Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\>, iterable\\<array\\<Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\>\\> given\\.$#" + count: 2 + path: src/lib/Repository/RoleService.php + + - + message: "#^Parameter \\#1 \\$errors of class Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException constructor expects array\\<Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\>, iterable\\<array\\<array\\<Ibexa\\\\Core\\\\FieldType\\\\ValidationError\\>\\>\\> given\\.$#" + count: 2 + path: src/lib/Repository/RoleService.php + + - + message: "#^Parameter \\#1 \\$identifier of method Ibexa\\\\Core\\\\Repository\\\\Permission\\\\LimitationService\\:\\:getLimitationType\\(\\) expects string, int\\|string given\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Parameter \\#3 \\$limitations of method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\RoleDomainMapper\\:\\:buildPersistencePolicyObject\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\>, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\> given\\.$#" + count: 2 + path: src/lib/Repository/RoleService.php + + - + message: "#^Parameter \\#3 \\$limitations of method Ibexa\\\\Core\\\\Repository\\\\RoleService\\:\\:validatePolicy\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\>, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\> given\\.$#" + count: 3 + path: src/lib/Repository/RoleService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\RoleService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/RoleService.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:internalLoadContentById\\(\\)\\.$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:__construct\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:findContentInfo\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:internalFindContentInfo\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:suggest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:validateContentCriteria\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:validateContentSortClauses\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Parameter \\#1 \\$spiContentInfo of method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper\\:\\:buildContentInfoDomainObject\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ContentInfo, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject given\\.$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:\\$repository \\(Ibexa\\\\Core\\\\Repository\\\\Repository\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository\\.$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\SearchService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SectionService\\:\\:__construct\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SectionService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\SectionService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SectionService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SettingService\\:\\:newSettingCreateStruct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SettingService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SettingService\\:\\:newSettingUpdateStruct\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SettingService.php + + - + message: "#^Parameter \\#3 \\$serializedValue of method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Setting\\\\Handler\\:\\:create\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: src/lib/Repository/SettingService.php + + - + message: "#^Parameter \\#3 \\$serializedValue of method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Setting\\\\Handler\\:\\:update\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: src/lib/Repository/SettingService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ContentService\\:\\:loadContentByContentInfo\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ContentService\\:\\:validate\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ContentService\\:\\:validate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/ContentService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\Language\\\\AbstractLanguageResolver\\:\\:getPrioritizedLanguages\\(\\) has parameter \\$forcedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/Language/AbstractLanguageResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\Language\\\\AbstractLanguageResolver\\:\\:getPrioritizedLanguages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/Language/AbstractLanguageResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\Language\\\\LanguageResolver\\:\\:__construct\\(\\) has parameter \\$configLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/Language/LanguageResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\LocationService\\:\\:loadLocationList\\(\\) has parameter \\$locationIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/LocationService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\Repository\\:\\:\\$notificationService \\(Ibexa\\\\Core\\\\Repository\\\\NotificationService\\) does not accept Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\NotificationService\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/Repository.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:findContentInfo\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchService\\:\\:suggest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/SearchService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UserService\\:\\:createUser\\(\\) has parameter \\$parentGroups with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/UserService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UserService\\:\\:loadUsersByEmail\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\> but returns iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\>\\.$#" + count: 1 + path: src/lib/Repository/SiteAccessAware/UserService.php + + - + message: "#^Argument of an invalid type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\Field\\\\FieldTypeBasedThumbnailStrategy supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentThumbnail/Field/ContentFieldStrategy.php + + - + message: "#^Cannot access an offset on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\Field\\\\FieldTypeBasedThumbnailStrategy\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentThumbnail/Field/ContentFieldStrategy.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\Field\\\\ContentFieldStrategy\\:\\:setStrategies\\(\\) has parameter \\$thumbnailStrategies with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentThumbnail/Field/ContentFieldStrategy.php + + - + message: "#^PHPDoc tag @param for parameter \\$thumbnailStrategies with type iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\Field\\\\FieldTypeBasedThumbnailStrategy\\>&Traversable is incompatible with native type array\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentThumbnail/Field/ContentFieldStrategy.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\Field\\\\ThumbnailStrategy\\:\\:hasStrategy\\(\\)\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentThumbnail/FirstMatchingFieldStrategy.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\FirstMatchingFieldStrategy\\:\\:getFieldByIdentifier\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentThumbnail/FirstMatchingFieldStrategy.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\FirstMatchingFieldStrategy\\:\\:getThumbnail\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentThumbnail/FirstMatchingFieldStrategy.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\StaticStrategy\\:\\:getThumbnail\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentThumbnail/StaticStrategy.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\ThumbnailChainStrategy\\:\\:getThumbnail\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentThumbnail/ThumbnailChainStrategy.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\ThumbnailChainStrategy\\:\\:\\$strategies \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\ThumbnailStrategy\\>\\) does not accept iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\ThumbnailStrategy\\>\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentThumbnail/ThumbnailChainStrategy.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Strategy\\\\ContentValidator\\\\ContentValidatorStrategy\\:\\:__construct\\(\\) has parameter \\$contentValidators with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentValidator/ContentValidatorStrategy.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Strategy\\\\ContentValidator\\\\ContentValidatorStrategy\\:\\:mergeErrors\\(\\) has parameter \\$fieldErrors with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentValidator/ContentValidatorStrategy.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Strategy\\\\ContentValidator\\\\ContentValidatorStrategy\\:\\:mergeErrors\\(\\) has parameter \\$foundErrors with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentValidator/ContentValidatorStrategy.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Strategy\\\\ContentValidator\\\\ContentValidatorStrategy\\:\\:mergeErrors\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentValidator/ContentValidatorStrategy.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Strategy\\\\ContentValidator\\\\ContentValidatorStrategy\\:\\:validate\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentValidator/ContentValidatorStrategy.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Strategy\\\\ContentValidator\\\\ContentValidatorStrategy\\:\\:\\$contentValidators \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Validator\\\\ContentValidator\\>\\) does not accept iterable\\.$#" + count: 1 + path: src/lib/Repository/Strategy/ContentValidator/ContentValidatorStrategy.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:internalLoadContentById\\(\\)\\.$#" + count: 2 + path: src/lib/Repository/TrashService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\TrashService\\:\\:__construct\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/TrashService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\TrashService\\:\\:buildDomainTrashItems\\(\\) has parameter \\$spiTrashItems with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/TrashService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\TrashService\\:\\:buildDomainTrashItems\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/TrashService.php + + - + message: "#^PHPDoc tag @param for parameter \\$trashItemId with type mixed is not subtype of native type int\\.$#" + count: 1 + path: src/lib/Repository/TrashService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\TrashService\\:\\:\\$repository \\(Ibexa\\\\Core\\\\Repository\\\\Repository\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository\\.$#" + count: 1 + path: src/lib/Repository/TrashService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\TrashService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/TrashService.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias referenced with incorrect case\\: Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\URLAlias\\.$#" + count: 7 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\URLAliasService\\:\\:choosePrioritizedLanguageCode\\(\\) has parameter \\$entries with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\URLAliasService\\:\\:isPathLoadable\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\URLAliasService\\:\\:isPathLoadable\\(\\) has parameter \\$pathData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\URLAliasService\\:\\:matchLanguageCode\\(\\) has parameter \\$pathElementData with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\URLAliasService\\:\\:matchPath\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\URLAliasService\\:\\:reverseLookup\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\|false\\.$#" + count: 1 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\URLAliasService\\:\\:sortTranslationsByPrioritizedLanguages\\(\\) has parameter \\$translations with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\URLAliasService\\:\\:sortTranslationsByPrioritizedLanguages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Parameter \\#2 \\$custom of method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\\\Handler\\:\\:listURLAliasesForLocation\\(\\) expects bool, bool\\|null given\\.$#" + count: 1 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Parameter \\#2 \\$languageCode of method Ibexa\\\\Core\\\\Repository\\\\URLAliasService\\:\\:extractPath\\(\\) expects string, null given\\.$#" + count: 1 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Parameter \\#2 \\$languageCode of method Ibexa\\\\Core\\\\Repository\\\\URLAliasService\\:\\:extractPath\\(\\) expects string, string\\|null given\\.$#" + count: 2 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Parameter \\#2 \\$path of method Ibexa\\\\Core\\\\Repository\\\\URLAliasService\\:\\:buildUrlAliasDomainObject\\(\\) expects string, string\\|true given\\.$#" + count: 3 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Parameter \\#3 \\$languageCode of method Ibexa\\\\Core\\\\Repository\\\\URLAliasService\\:\\:matchPath\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: src/lib/Repository/URLAliasService.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URL\\:\\:\\$id\\.$#" + count: 6 + path: src/lib/Repository/URLService.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URL\\:\\:\\$isValid\\.$#" + count: 1 + path: src/lib/Repository/URLService.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URL\\:\\:\\$lastChecked\\.$#" + count: 1 + path: src/lib/Repository/URLService.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URL\\:\\:\\$url\\.$#" + count: 2 + path: src/lib/Repository/URLService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\UsageSearchResult\\:\\:\\$items \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo\\>\\) does not accept array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\>\\.$#" + count: 1 + path: src/lib/Repository/URLService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\UsageSearchResult\\:\\:\\$totalCount \\(int\\) does not accept int\\|null\\.$#" + count: 1 + path: src/lib/Repository/URLService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\URLService\\:\\:\\$repository \\(Ibexa\\\\Core\\\\Repository\\\\Repository\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository\\.$#" + count: 1 + path: src/lib/Repository/URLService.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLWildcard referenced with incorrect case\\: Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\UrlWildcard\\.$#" + count: 4 + path: src/lib/Repository/URLWildcardService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\URLWildcardService\\:\\:__construct\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/URLWildcardService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\URLWildcardService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/URLWildcardService.php + + - + message: "#^Parameter \\#3 \\$hashType of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\PasswordHashService\\:\\:isValidPassword\\(\\) expects int\\|null, string given\\.$#" + count: 1 + path: src/lib/Repository/User/PasswordValidator.php + + - + message: "#^Dead catch \\- Exception is never thrown in the try block\\.$#" + count: 1 + path: src/lib/Repository/UserPreferenceService.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" + count: 4 + path: src/lib/Repository/UserService.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:internalLoadContentById\\(\\)\\.$#" + count: 5 + path: src/lib/Repository/UserService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\UserService\\:\\:__construct\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\UserService\\:\\:setLogger\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^PHPDoc tag @param for parameter \\$id with type mixed is not subtype of native type int\\.$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^Parameter \\#1 \\$contentType of class Ibexa\\\\Core\\\\Base\\\\Exceptions\\\\MissingUserFieldTypeException constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\|null given\\.$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^Parameter \\#1 \\$contentType of method Ibexa\\\\Core\\\\Repository\\\\UserService\\:\\:getUserFieldDefinition\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\|null given\\.$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^Parameter \\#1 \\$identifier of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:loadContentTypeByIdentifier\\(\\) expects string, string\\|false given\\.$#" + count: 2 + path: src/lib/Repository/UserService.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Core\\\\Repository\\\\User\\\\PasswordValidatorInterface\\:\\:getPasswordInfo\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^Parameter \\#3 \\$hashAlgorithm of method Ibexa\\\\Core\\\\Repository\\\\UserService\\:\\:comparePasswordHashes\\(\\) expects int, string given\\.$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserUpdateStruct\\:\\:\\$contentMetadataUpdateStruct \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentMetadataUpdateStruct\\) in empty\\(\\) is not falsy\\.$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserUpdateStruct\\:\\:\\$contentUpdateStruct \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentUpdateStruct\\) in empty\\(\\) is not falsy\\.$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserUpdateStruct\\:\\:\\$contentUpdateStruct \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentUpdateStruct\\) on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\UserService\\:\\:\\$settings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/UserService.php + + - + message: "#^Result of \\|\\| is always true\\.$#" + count: 4 + path: src/lib/Repository/UserService.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Validator\\\\ContentCreateStructValidator\\:\\:validate\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Validator/ContentCreateStructValidator.php + + - + message: "#^Cannot access property \\$value on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 2 + path: src/lib/Repository/Validator/ContentUpdateStructValidator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Validator\\\\ContentUpdateStructValidator\\:\\:validate\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Validator/ContentUpdateStructValidator.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Validator\\\\ContentUpdateStructValidator\\:\\:\\$contentLanguageHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\.$#" + count: 1 + path: src/lib/Repository/Validator/ContentUpdateStructValidator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Validator\\\\TargetContentValidator\\:\\:validate\\(\\) has parameter \\$allowedContentTypes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Validator/TargetContentValidator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Validator\\\\TargetContentValidatorInterface\\:\\:validate\\(\\) has parameter \\$allowedContentTypes with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Validator/TargetContentValidatorInterface.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Validator\\\\UserPasswordValidator\\:\\:__construct\\(\\) has parameter \\$constraints with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Validator/UserPasswordValidator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Validator\\\\UserPasswordValidator\\:\\:createValidationError\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Validator/UserPasswordValidator.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Validator\\\\UserPasswordValidator\\:\\:\\$constraints type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Validator/UserPasswordValidator.php + + - + message: "#^Call to method getValue\\(\\) on an unknown class eZ\\\\Publish\\\\API\\\\Repository\\\\Values\\\\Content\\\\Field\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Content.php + + - + message: "#^Cannot access offset mixed on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Content.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\:\\:__construct\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Content.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\:\\:getField\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null but returns eZ\\\\Publish\\\\API\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Content.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\:\\:getProperties\\(\\) has parameter \\$dynamicProperties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Content.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\:\\:getProperties\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Content.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\:\\:\\$fieldDefinitionTranslationMap \\(array\\<string, array\\<string, eZ\\\\Publish\\\\API\\\\Repository\\\\Values\\\\Content\\\\Field\\>\\>\\) does not accept array\\<int\\|string, array\\<int\\|string, mixed\\>\\>\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Content.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\:\\:\\$fieldDefinitionTranslationMap has unknown class eZ\\\\Publish\\\\API\\\\Repository\\\\Values\\\\Content\\\\Field as its type\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Content.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\:\\:getProperties\\(\\) has parameter \\$dynamicProperties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Location.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\:\\:getProperties\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Location.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\PermissionSubtree\\:\\:createFromQueryBuilder\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Query/Criterion/PermissionSubtree.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\PermissionSubtree\\:\\:createFromQueryBuilder\\(\\) has parameter \\$operator with no type specified\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Query/Criterion/PermissionSubtree.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\PermissionSubtree\\:\\:createFromQueryBuilder\\(\\) has parameter \\$target with no type specified\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Query/Criterion/PermissionSubtree.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\PermissionSubtree\\:\\:createFromQueryBuilder\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/Query/Criterion/PermissionSubtree.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItem\\:\\:getProperties\\(\\) has parameter \\$dynamicProperties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/TrashItem.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItem\\:\\:getProperties\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/Content/TrashItem.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\:\\:__construct\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/ContentType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\:\\:getDescription\\(\\) should return string\\|null but returns string\\|false\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/ContentType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\:\\:getName\\(\\) should return string\\|null but returns string\\|false\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/ContentType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeDraft\\:\\:getProperties\\(\\) has parameter \\$dynamicProperties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/ContentTypeDraft.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeDraft\\:\\:getProperties\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/ContentTypeDraft.php + + - + message: "#^PHPDoc tag @return with type array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\> is incompatible with native type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionCollection\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/ContentTypeDraft.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeGroup\\:\\:getDescription\\(\\) should return string\\|null but returns string\\|false\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/ContentTypeGroup.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeGroup\\:\\:getName\\(\\) should return string\\|null but returns string\\|false\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/ContentTypeGroup.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\:\\:getDescription\\(\\) should return string\\|null but returns string\\|false\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldDefinition.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\:\\:getFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldDefinition.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\:\\:getName\\(\\) should return string\\|null but returns string\\|false\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldDefinition.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\:\\:getValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldDefinition.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\:\\:\\$fieldSettings type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldDefinition.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\:\\:\\$validatorConfiguration type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldDefinition.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionCollection\\:\\:__construct\\(\\) has parameter \\$fieldDefinitions with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldDefinitionCollection.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionCollection\\:\\:map\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldDefinitionCollection.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\[\\]\\)\\: Unexpected token \"\\\\n \", expected variable at offset 87$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldDefinitionCollection.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldType\\:\\:fieldSettingsFromHash\\(\\) has parameter \\$fieldSettingsHash with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldType\\:\\:fieldSettingsToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldType\\:\\:validatorConfigurationFromHash\\(\\) has parameter \\$validatorConfigurationHash with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldType\\:\\:validatorConfigurationToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldType.php + + - + message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Core\\\\FieldType\\\\FieldType\\:\\:validate\\(\\) expects Ibexa\\\\Core\\\\FieldType\\\\Value, Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value given\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldType.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldType\\:\\:\\$internalFieldType \\(Ibexa\\\\Core\\\\FieldType\\\\FieldType\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\.$#" + count: 1 + path: src/lib/Repository/Values/ContentType/FieldType.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectState\\:\\:getDescription\\(\\) should return string\\|null but returns string\\|false\\.$#" + count: 1 + path: src/lib/Repository/Values/ObjectState/ObjectState.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectState\\:\\:getName\\(\\) should return string\\|null but returns string\\|false\\.$#" + count: 1 + path: src/lib/Repository/Values/ObjectState/ObjectState.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroup\\:\\:getDescription\\(\\) should return string\\|null but returns string\\|false\\.$#" + count: 1 + path: src/lib/Repository/Values/ObjectState/ObjectStateGroup.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroup\\:\\:getName\\(\\) should return string\\|null but returns string\\|false\\.$#" + count: 1 + path: src/lib/Repository/Values/ObjectState/ObjectStateGroup.php + + - + message: "#^Property Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft\\:\\:\\$draftProperties type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/User/PolicyDraft.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleDraft\\:\\:getPolicies\\(\\) should return iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft\\> but returns iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\>\\.$#" + count: 1 + path: src/lib/Repository/Values/User/RoleDraft.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\:\\:getProperties\\(\\) has parameter \\$dynamicProperties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/User/User.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\:\\:getProperties\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/User/User.php + + - + message: "#^PHPDoc tag @return with type mixed is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: src/lib/Repository/Values/User/User.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroup\\:\\:getProperties\\(\\) has parameter \\$dynamicProperties with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/User/UserGroup.php + + - + message: "#^Method Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroup\\:\\:getProperties\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Repository/Values/User/UserGroup.php + + - + message: "#^PHPDoc tag @return with type mixed is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: src/lib/Repository/Values/User/UserGroup.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\BackgroundIndexer\\:\\:registerContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/BackgroundIndexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\BackgroundIndexer\\:\\:registerLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/BackgroundIndexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\BackgroundIndexer\\\\NullIndexer\\:\\:registerContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/BackgroundIndexer/NullIndexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\BackgroundIndexer\\\\NullIndexer\\:\\:registerLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/BackgroundIndexer/NullIndexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\ContentEventSubscriber\\:\\:onCopyContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/ContentEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\ContentEventSubscriber\\:\\:onDeleteContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/ContentEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\ContentEventSubscriber\\:\\:onDeleteTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/ContentEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\ContentEventSubscriber\\:\\:onHideContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/ContentEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\ContentEventSubscriber\\:\\:onPublishVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/ContentEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\ContentEventSubscriber\\:\\:onRevealContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/ContentEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\ContentEventSubscriber\\:\\:onUpdateContentMetadata\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/ContentEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\LocationEventSubscriber\\:\\:onCopySubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/LocationEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\LocationEventSubscriber\\:\\:onCreateLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/LocationEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\LocationEventSubscriber\\:\\:onDeleteLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/LocationEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\LocationEventSubscriber\\:\\:onHideLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/LocationEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\LocationEventSubscriber\\:\\:onMoveSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/LocationEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\LocationEventSubscriber\\:\\:onSwapLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/LocationEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\LocationEventSubscriber\\:\\:onUnhideLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/LocationEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\LocationEventSubscriber\\:\\:onUpdateLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/LocationEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\ObjectStateEventSubscriber\\:\\:onSetContentState\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/ObjectStateEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\SectionEventSubscriber\\:\\:onAssignSection\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/SectionEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\TrashEventSubscriber\\:\\:onRecover\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/TrashEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\TrashEventSubscriber\\:\\:onTrash\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/TrashEventSubscriber.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Event\\\\AfterEvent\\:\\:getUser\\(\\)\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/UserEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\UserEventSubscriber\\:\\:onCreateUser\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/UserEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\UserEventSubscriber\\:\\:onCreateUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/UserEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\UserEventSubscriber\\:\\:onDeleteUser\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/UserEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\UserEventSubscriber\\:\\:onDeleteUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/UserEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\UserEventSubscriber\\:\\:onMoveUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/UserEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\UserEventSubscriber\\:\\:onUpdateUser\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/UserEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\EventSubscriber\\\\UserEventSubscriber\\:\\:onUpdateUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/EventSubscriber/UserEventSubscriber.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldNameGenerator\\:\\:__construct\\(\\) has parameter \\$fieldNameMapping with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Common/FieldNameGenerator.php + + - + message: "#^Property Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldNameGenerator\\:\\:\\$fieldNameMapping type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Common/FieldNameGenerator.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldNameResolver\\:\\:getAggregationFieldName\\(\\) should return string\\|null but returns false\\.$#" + count: 1 + path: src/lib/Search/Common/FieldNameResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldNameResolver\\:\\:getFieldTypes\\(\\) should return array\\<string, Ibexa\\\\Contracts\\\\Core\\\\Search\\\\FieldType\\> but returns array\\<int, mixed\\>\\.$#" + count: 1 + path: src/lib/Search/Common/FieldNameResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldNameResolver\\:\\:getIndexFieldName\\(\\) should return string but returns array\\<string, Ibexa\\\\Contracts\\\\Core\\\\Search\\\\FieldType\\>\\.$#" + count: 1 + path: src/lib/Search/Common/FieldNameResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldNameResolver\\:\\:getIndexFieldName\\(\\) should return string but returns array\\<string, null\\>\\.$#" + count: 1 + path: src/lib/Search/Common/FieldNameResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldNameResolver\\:\\:getSearchableFieldMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Common/FieldNameResolver.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldNameResolver\\:\\:getSortFieldName\\(\\) should return string\\|null but returns false\\.$#" + count: 1 + path: src/lib/Search/Common/FieldNameResolver.php + + - + message: "#^Offset false does not exist on string\\.$#" + count: 1 + path: src/lib/Search/Common/FieldNameResolver.php + + - + message: "#^Parameter \\#1 \\$criterionOrSortClause of method Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldNameResolver\\:\\:getIndexFieldName\\(\\) expects object, null given\\.$#" + count: 1 + path: src/lib/Search/Common/FieldNameResolver.php + + - + message: "#^Parameter \\#5 \\$name of method Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldNameResolver\\:\\:getIndexFieldName\\(\\) expects string, string\\|null given\\.$#" + count: 2 + path: src/lib/Search/Common/FieldNameResolver.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Search\\\\FieldType\\:\\:\\$raw\\.$#" + count: 1 + path: src/lib/Search/Common/FieldValueMapper/IdentifierMapper.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Search\\\\FieldType\\:\\:\\$raw\\.$#" + count: 1 + path: src/lib/Search/Common/FieldValueMapper/MultipleIdentifierMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldValueMapper\\\\MultipleStringMapper\\:\\:map\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Common/FieldValueMapper/MultipleStringMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldValueMapper\\\\StringMapper\\:\\:convert\\(\\) should return string but returns string\\|null\\.$#" + count: 1 + path: src/lib/Search/Common/FieldValueMapper/StringMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\IncrementalIndexer\\:\\:createSearchIndex\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/IncrementalIndexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\IncrementalIndexer\\:\\:purge\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/IncrementalIndexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\IncrementalIndexer\\:\\:updateSearchIndex\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/IncrementalIndexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\Indexer\\:\\:createSearchIndex\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Common/Indexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\Indexer\\:\\:getContentDbFieldsStmt\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Common/Indexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\Indexer\\:\\:getContentDbFieldsStmt\\(\\) return type has no value type specified in iterable type Doctrine\\\\DBAL\\\\Driver\\\\Statement\\.$#" + count: 1 + path: src/lib/Search/Common/Indexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Common\\\\Indexer\\:\\:getContentDbFieldsStmt\\(\\) should return Doctrine\\\\DBAL\\\\Driver\\\\Statement but returns Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/Search/Common/Indexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriteriaConverter\\:\\:addHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriteriaConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriteriaConverter\\:\\:convertCriteria\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriteriaConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler.php + + - + message: "#^Property Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\:\\:\\$comparatorMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\:\\:\\$criteria\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/CompositeCriterion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\CompositeCriterion\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/CompositeCriterion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\ContentId\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentId.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\ContentTypeGroupId\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeGroupId.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\ContentTypeId\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeId.php + + - + message: "#^Argument of an invalid type array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeIdentifier.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\ContentTypeIdentifier\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeIdentifier.php + + - + message: "#^Parameter \\#1 \\$identifier of method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\:\\:loadByIdentifier\\(\\) expects string, bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeIdentifier.php + + - + message: "#^Cannot call method getBetweenExpression\\(\\) on Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\|null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/DateMetadata.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\DateMetadata\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/DateMetadata.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\Field\\:\\:getFieldsInformation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/Field.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\Field\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/Field.php + + - + message: "#^Cannot call method getBitAndComparisonExpression\\(\\) on Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\|null\\.$#" + count: 8 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldBase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldBase\\:\\:getFieldCondition\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldBase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldBase\\:\\:getInExpressionWithFieldConditions\\(\\) has parameter \\$fieldWhereExpressions with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldBase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldBase\\:\\:getInExpressionWithFieldConditions\\(\\) has parameter \\$fieldsInformation with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldBase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldBase\\:\\:getInExpressionWithFieldConditions\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldBase.php + + - + message: "#^Cannot access offset 0 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldEmpty.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldEmpty\\:\\:getFieldsInformation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldEmpty.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldEmpty\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldEmpty.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldRelation\\:\\:buildQueryForContainsOperator\\(\\) has parameter \\$criterionValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldRelation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldRelation\\:\\:buildQueryForContainsOperator\\(\\) has parameter \\$fieldDefinitionIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldRelation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldRelation\\:\\:buildQueryForContainsOperator\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldRelation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldRelation\\:\\:buildQueryForInOperator\\(\\) has parameter \\$criterionValue with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldRelation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldRelation\\:\\:buildQueryForInOperator\\(\\) has parameter \\$fieldDefinitionIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldRelation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldRelation\\:\\:getFieldDefinitionsIds\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldRelation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldRelation\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldRelation.php + + - + message: "#^Property Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldValue\\\\Converter\\:\\:\\$defaultHandler \\(Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldValue\\\\Handler\\) does not accept Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldValue\\\\Handler\\|null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Converter.php + + - + message: "#^Cannot access offset 0 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php + + - + message: "#^Cannot access offset 1 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php + + - + message: "#^Cannot call method getBetweenExpression\\(\\) on Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\|null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldValue\\\\Handler\\:\\:createNamedParameter\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldValue\\\\Handler\\:\\:createNamedParameter\\(\\) never returns null so it can be removed from the return type\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldValue\\\\Handler\\:\\:prepareParameter\\(\\) should return array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string but returns array\\<array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\>\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php + + - + message: "#^Parameter \\#1 \\$string of method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldValue\\\\Handler\\:\\:lowerCase\\(\\) expects string, bool\\|float\\|int\\|string given\\.$#" + count: 2 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php + + - + message: "#^Parameter \\#1 \\$string of method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldValue\\\\Handler\\:\\:prepareLikeString\\(\\) expects string, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 2 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php + + - + message: "#^Parameter \\#1 \\$values of method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldValue\\\\Handler\\:\\:getParamArrayType\\(\\) expects array\\<int, bool\\|float\\|int\\|string\\>, array\\<array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\> given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php + + - + message: "#^Parameter \\#2 \\$array of function array_map expects array, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php + + - + message: "#^Property Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldValue\\\\Handler\\:\\:\\$comparatorMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php + + - + message: "#^Parameter \\#1 \\$string of method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldValue\\\\Handler\\:\\:prepareLikeString\\(\\) expects string, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 2 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Collection.php + + - + message: "#^Parameter \\#1 \\$string of method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldValue\\\\Handler\\:\\:lowerCase\\(\\) expects string, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Simple.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FieldValue\\\\HandlerRegistry\\:\\:register\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/HandlerRegistry.php + + - + message: "#^Cannot call method getBitAndComparisonExpression\\(\\) on Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\|null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php + + - + message: "#^Cannot call method getCountExpression\\(\\) on Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\|null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FullText\\:\\:__construct\\(\\) has parameter \\$configuration with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FullText\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FullText\\:\\:tokenizeString\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FullText\\:\\:tokenizeString\\(\\) should return array but returns array\\<int, string\\>\\|false\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php + + - + message: "#^Parameter \\#2 \\$string of method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FullText\\:\\:getWordIdSubquery\\(\\) expects string, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php + + - + message: "#^Property Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\FullText\\:\\:\\$configuration type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\IsUserBased\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserBased.php + + - + message: "#^Parameter \\#1 \\$array of function reset expects array\\|object, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserBased.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\IsUserEnabled\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserEnabled.php + + - + message: "#^Parameter \\#1 \\$array of function reset expects array\\|object, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserEnabled.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\:\\:\\$matchAlwaysAvailable\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php + + - + message: "#^Cannot call method getBitAndComparisonExpression\\(\\) on Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\|null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\LanguageCode\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php + + - + message: "#^Parameter \\#1 \\$languageCodes of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGenerator\\:\\:generateLanguageMaskFromLanguageCodes\\(\\) expects array\\<string\\>, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\:\\:\\$criteria\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalAnd.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\LogicalAnd\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalAnd.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\:\\:\\$criteria\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalNot.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\LogicalNot\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalNot.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\:\\:\\$criteria\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalOr.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\LogicalOr\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalOr.php + + - + message: "#^Cannot access offset 0 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 2 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php + + - + message: "#^Cannot access offset 1 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 2 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php + + - + message: "#^Cannot call method getBetweenExpression\\(\\) on Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\|null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php + + - + message: "#^Cannot call method getRoundExpression\\(\\) on Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\|null\\.$#" + count: 3 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\MapLocationDistance\\:\\:getBoundingConstraints\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\MapLocationDistance\\:\\:getBoundingCoordinates\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\MapLocationDistance\\:\\:getFieldDefinitionIds\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\MapLocationDistance\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\MapLocationDistance\\:\\:kilometersToDegrees\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\MapLocationDistance\\:\\:kilometersToDegrees\\(\\) has parameter \\$kilometers with no type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php + + - + message: "#^Parameter \\#3 \\$distance of method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\MapLocationDistance\\:\\:getBoundingConstraints\\(\\) expects float, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\MatchAll\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MatchAll.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\MatchNone\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MatchNone.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\ObjectStateId\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ObjectStateId.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\ObjectStateIdentifier\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ObjectStateIdentifier.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\RemoteId\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/RemoteId.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\SectionId\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionId.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\SectionIdentifier\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionIdentifier.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\UserEmail\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserEmail.php + + - + message: "#^Parameter \\#1 \\$string of method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\:\\:transformByGroup\\(\\) expects string, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserEmail.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\UserId\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserId.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\UserLogin\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserLogin.php + + - + message: "#^Parameter \\#1 \\$string of method Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\:\\:transformByGroup\\(\\) expects string, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserLogin.php + + - + message: "#^Cannot call method getConcatExpression\\(\\) on Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\|null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserMetadata.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\CriterionHandler\\\\UserMetadata\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserMetadata.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseConverter\\:\\:addHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseConverter\\:\\:applyJoin\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseConverter.php + + - + message: "#^Property Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseConverter\\:\\:\\$sortColumns type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseConverter.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\:\\:applyJoin\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler.php + + - + message: "#^Cannot access property \\$seed on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\SortClause\\\\Target\\|null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/AbstractRandom.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\AbstractRandom\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/AbstractRandom.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\ContentId\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentId.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\ContentName\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentName.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\DateModified\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DateModified.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\DatePublished\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DatePublished.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\Factory\\\\RandomSortClauseHandlerFactory\\:\\:__construct\\(\\) has parameter \\$randomSortClauseGateways with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Factory/RandomSortClauseHandlerFactory.php + + - + message: "#^Cannot call method getBitAndComparisonExpression\\(\\) on Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\|null\\.$#" + count: 8 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Field.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\Field\\:\\:applyJoin\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Field.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\Field\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Field.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\Field\\:\\:getFieldCondition\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Field.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\Field\\:\\:getFieldCondition\\(\\) has parameter \\$fieldTableName with no type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Field.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\Field\\:\\:getFieldCondition\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Field.php + + - + message: "#^Parameter \\#1 \\$number of method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\:\\:getSortColumnName\\(\\) expects int, string given\\.$#" + count: 3 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Field.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\MapLocationDistance\\:\\:applyJoin\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/MapLocationDistance.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\MapLocationDistance\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/MapLocationDistance.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\SectionIdentifier\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/SectionIdentifier.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\SectionName\\:\\:applyJoin\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/SectionName.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\SectionName\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/SectionName.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\Trash\\\\ContentTypeName\\:\\:applyJoin\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/ContentTypeName.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\Trash\\\\ContentTypeName\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/ContentTypeName.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\Trash\\\\DateTrashed\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/DateTrashed.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\Trash\\\\UserLogin\\:\\:applyJoin\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/UserLogin.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Common\\\\Gateway\\\\SortClauseHandler\\\\Trash\\\\UserLogin\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/UserLogin.php + + - + message: "#^Property Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\FullTextValue\\:\\:\\$transformationRules type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/FullTextValue.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\:\\:find\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway.php + + - + message: "#^Argument of an invalid type array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Ancestor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\CriterionHandler\\\\Ancestor\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Ancestor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\CriterionHandler\\\\LocationId\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/CriterionHandler/LocationId.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\CriterionHandler\\\\LocationRemoteId\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/CriterionHandler/LocationRemoteId.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\CriterionHandler\\\\ParentLocationId\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/CriterionHandler/ParentLocationId.php + + - + message: "#^Argument of an invalid type array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/CriterionHandler/PermissionSubtree.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\CriterionHandler\\\\PermissionSubtree\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/CriterionHandler/PermissionSubtree.php + + - + message: "#^Argument of an invalid type array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Subtree.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\CriterionHandler\\\\Subtree\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Subtree.php + + - + message: "#^Cannot access offset 0 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Visibility.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\CriterionHandler\\\\Visibility\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Visibility.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:find\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:find\\(\\) should return array\\<array\\> but returns array\\<string, array\\<int\\>\\|int\\|null\\>\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:find\\(\\) should return array\\<array\\> but returns array\\<string, array\\|int\\|null\\>\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:getContentInfoList\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:getContentInfoList\\(\\) has parameter \\$sort with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:getLanguageMask\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:getQueryCondition\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\:\\:getResultCount\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^PHPDoc tag @param for parameter \\$limit with type mixed is not subtype of native type int\\|null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^PHPDoc tag @param for parameter \\$offset with type mixed is not subtype of native type int\\|null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Parameter \\#1 \\$firstResult of method Doctrine\\\\DBAL\\\\Query\\\\QueryBuilder\\:\\:setFirstResult\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php + + - + message: "#^Dead catch \\- Doctrine\\\\DBAL\\\\DBALException\\|PDOException is never thrown in the try block\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\\\ExceptionConversion\\:\\:find\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Parameter \\#3 \\$limit of method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Gateway\\:\\:find\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:bulkIndex\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:commit\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:deleteContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:deleteLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:extractMatchedLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:extractMatchedLanguage\\(\\) has parameter \\$languageMask with no type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:extractMatchedLanguage\\(\\) has parameter \\$languageSettings with no type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:extractMatchedLanguage\\(\\) has parameter \\$mainLanguageId with no type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findContent\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findLocations\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findSingle\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:findSingle\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\ContentInfo but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:indexContent\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:indexLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:purgeIndex\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Handler\\:\\:suggest\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\:\\:\\$sortClauses \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\SortClause\\>\\) does not accept null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\:\\:\\$time \\(int\\) does not accept float\\.$#" + count: 2 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchResult\\:\\:\\$totalCount \\(int\\|null\\) does not accept array\\.$#" + count: 2 + path: src/lib/Search/Legacy/Content/Handler.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Indexer\\:\\:purge\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Indexer.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Indexer\\:\\:updateSearchIndex\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Indexer.php + + - + message: "#^Call to an undefined method Doctrine\\\\DBAL\\\\Driver\\\\ResultStatement\\:\\:fetchOne\\(\\)\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/IndexerGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\IndexerGateway\\:\\:fetchIteration\\(\\) has parameter \\$statement with no value type specified in iterable type Doctrine\\\\DBAL\\\\Driver\\\\ResultStatement\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/IndexerGateway.php + + - + message: "#^Parameter \\#1 \\$statement of method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\IndexerGateway\\:\\:fetchIteration\\(\\) expects Doctrine\\\\DBAL\\\\Driver\\\\ResultStatement, Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string given\\.$#" + count: 3 + path: src/lib/Search/Legacy/Content/IndexerGateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:find\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway.php + + - + message: "#^Argument of an invalid type array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Ancestor.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\CriterionHandler\\\\Ancestor\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Ancestor.php + + - + message: "#^Cannot access offset 0 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Depth.php + + - + message: "#^Cannot access offset 1 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Depth.php + + - + message: "#^Cannot call method getBetweenExpression\\(\\) on Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\|null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Depth.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\CriterionHandler\\\\Location\\\\Depth\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Depth.php + + - + message: "#^Parameter \\#1 \\$array of function reset expects array\\|object, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Depth.php + + - + message: "#^Parameter \\#2 \\$y of method Doctrine\\\\DBAL\\\\Query\\\\Expression\\\\ExpressionBuilder\\:\\:in\\(\\) expects array\\<string\\>\\|string, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Depth.php + + - + message: "#^Cannot access offset 0 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 2 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/IsMainLocation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\CriterionHandler\\\\Location\\\\IsMainLocation\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/IsMainLocation.php + + - + message: "#^Cannot access offset 0 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Priority.php + + - + message: "#^Cannot access offset 1 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Priority.php + + - + message: "#^Cannot call method getBetweenExpression\\(\\) on Doctrine\\\\DBAL\\\\Platforms\\\\AbstractPlatform\\|null\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Priority.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\CriterionHandler\\\\Location\\\\Priority\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Priority.php + + - + message: "#^Parameter \\#1 \\$array of function reset expects array\\|object, array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Priority.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\CriterionHandler\\\\LocationId\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationId.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\CriterionHandler\\\\LocationRemoteId\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationRemoteId.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\CriterionHandler\\\\ParentLocationId\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/ParentLocationId.php + + - + message: "#^Argument of an invalid type array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Subtree.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\CriterionHandler\\\\Subtree\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Subtree.php + + - + message: "#^Cannot access offset 0 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 2 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Visibility.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\CriterionHandler\\\\Visibility\\:\\:handle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Visibility.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:find\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:find\\(\\) should return array\\<array\\> but returns array\\<string, array\\|int\\|null\\>\\.$#" + count: 2 + path: src/lib/Search/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:getLanguageMask\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabase\\:\\:getTotalCount\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/DoctrineDatabase.php + + - + message: "#^Dead catch \\- Doctrine\\\\DBAL\\\\DBALException\\|PDOException is never thrown in the try block\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\ExceptionConversion\\:\\:find\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Parameter \\#3 \\$limit of method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:find\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/ExceptionConversion.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\SortClauseHandler\\\\Location\\\\Depth\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Depth.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\SortClauseHandler\\\\Location\\\\Id\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Id.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\SortClauseHandler\\\\Location\\\\IsMainLocation\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/IsMainLocation.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\SortClauseHandler\\\\Location\\\\Path\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Path.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\SortClauseHandler\\\\Location\\\\Priority\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Priority.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\SortClauseHandler\\\\Location\\\\Visibility\\:\\:applySelect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Visibility.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Search\\\\FieldType\\:\\:\\$splitFlag\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Mapper/FullTextMapper.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Search\\\\FieldType\\:\\:\\$transformationRules\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/Mapper/FullTextMapper.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Gateway\\:\\:bulkIndex\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Gateway\\:\\:index\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Gateway\\:\\:purgeIndex\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Gateway\\:\\:remove\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway.php + + - + message: "#^Argument of an invalid type array\\<int, string\\>\\|false supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Gateway\\\\DoctrineDatabase\\:\\:__construct\\(\\) has parameter \\$fullTextSearchConfiguration with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Gateway\\\\DoctrineDatabase\\:\\:buildWordIDArray\\(\\) has parameter \\$indexArrayOnlyWords with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Gateway\\\\DoctrineDatabase\\:\\:buildWordIDArray\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Gateway\\\\DoctrineDatabase\\:\\:bulkIndex\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Gateway\\\\DoctrineDatabase\\:\\:index\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Gateway\\\\DoctrineDatabase\\:\\:indexWords\\(\\) has parameter \\$indexArray with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Gateway\\\\DoctrineDatabase\\:\\:indexWords\\(\\) has parameter \\$wordIDArray with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Gateway\\\\DoctrineDatabase\\:\\:purgeIndex\\(\\) has no return type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php + + - + message: "#^Property Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Gateway\\\\DoctrineDatabase\\:\\:\\$fullTextSearchConfiguration type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Gateway/DoctrineDatabase.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Repository\\\\SearchIndex\\:\\:getContentObjectWords\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Repository/SearchIndex.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Repository\\\\SearchIndex\\:\\:getWordUpdateQuery\\(\\) has parameter \\$wordIds with no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Repository/SearchIndex.php + + - + message: "#^Method Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Repository\\\\SearchIndex\\:\\:getWords\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Repository/SearchIndex.php + + - + message: "#^Property Ibexa\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\WordIndexer\\\\Repository\\\\SearchIndex\\:\\:\\$connection has no type specified\\.$#" + count: 1 + path: src/lib/Search/Legacy/Content/WordIndexer/Repository/SearchIndex.php + + - + message: "#^Parameter \\#1 \\$length of function random_bytes expects int\\<1, max\\>, int given\\.$#" + count: 1 + path: src/lib/Token/RandomBytesGenerator.php + + - + message: "#^Cannot access offset string on iterable\\<string, Ibexa\\\\Contracts\\\\Core\\\\Variation\\\\VariationHandler\\>\\.$#" + count: 3 + path: src/lib/Variation/VariationHandlerRegistry.php + + - + message: "#^Method Ibexa\\\\Core\\\\Variation\\\\VariationHandlerRegistry\\:\\:__construct\\(\\) has parameter \\$variationHandlers with no value type specified in iterable type iterable\\.$#" + count: 1 + path: src/lib/Variation/VariationHandlerRegistry.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\CacheFactoryTest\\:\\:providerGetService\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/CacheFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\CacheFactoryTest\\:\\:testGetService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/CacheFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\CacheFactoryTest\\:\\:testGetService\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/CacheFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\CacheFactoryTest\\:\\:testGetService\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/CacheFactoryTest.php + + - + message: "#^Parameter \\#1 \\$configResolver of method Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\CacheFactory\\:\\:getCachePool\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/CacheFactoryTest.php + + - + message: "#^Parameter \\#1 \\$container of method Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\CacheFactory\\:\\:setContainer\\(\\) expects Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/CacheFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryConfigurationProviderTest\\:\\:providerForRepositories\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/RepositoryConfigurationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryConfigurationProviderTest\\:\\:testGetCurrentRepositoryAlias\\(\\) has parameter \\$repositories with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/RepositoryConfigurationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryConfigurationProviderTest\\:\\:testGetDefaultRepositoryAlias\\(\\) has parameter \\$repositories with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/RepositoryConfigurationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryConfigurationProviderTest\\:\\:testGetRepositoryConfigNotSpecifiedRepository\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/RepositoryConfigurationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryConfigurationProviderTest\\:\\:testGetRepositoryConfigSpecifiedRepository\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/RepositoryConfigurationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryConfigurationProviderTest\\:\\:testGetRepositoryConfigUndefinedRepository\\(\\) has parameter \\$repositories with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/RepositoryConfigurationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageConnectionFactoryTest\\:\\:getConfigResolverMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/StorageConnectionFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageConnectionFactoryTest\\:\\:getConnectionProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/StorageConnectionFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageConnectionFactoryTest\\:\\:getContainerMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/StorageConnectionFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageConnectionFactoryTest\\:\\:testGetConnection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/StorageConnectionFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageConnectionFactoryTest\\:\\:testGetConnection\\(\\) has parameter \\$doctrineConnection with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/StorageConnectionFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageConnectionFactoryTest\\:\\:testGetConnection\\(\\) has parameter \\$repositoryAlias with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/StorageConnectionFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageConnectionFactoryTest\\:\\:testGetConnectionInvalidConnection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/StorageConnectionFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageConnectionFactoryTest\\:\\:testGetConnectionInvalidRepository\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/StorageConnectionFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageEngineFactoryTest\\:\\:getPersistenceHandlerMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/StorageEngineFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageEngineFactoryTest\\:\\:testBuildInvalidStorageEngine\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/StorageEngineFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageEngineFactoryTest\\:\\:testBuildStorageEngine\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/StorageEngineFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ApiLoader\\\\StorageEngineFactoryTest\\:\\:testRegisterStorageEngine\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ApiLoader/StorageEngineFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ChainConfigResolverTest\\:\\:buildMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ChainConfigResolverTest\\:\\:buildMock\\(\\) has parameter \\$class with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ChainConfigResolverTest\\:\\:buildMock\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ChainConfigResolverTest\\:\\:getParameterProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ChainConfigResolverTest\\:\\:testGetDefaultNamespace\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ChainConfigResolverTest\\:\\:testGetParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ChainConfigResolverTest\\:\\:testGetParameterInvalid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ChainConfigResolverTest\\:\\:testHasParameterFalse\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ChainConfigResolverTest\\:\\:testHasParameterTrue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ChainConfigResolverTest\\:\\:testPriority\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ChainConfigResolverTest\\:\\:testReSortResolvers\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ChainConfigResolverTest\\:\\:testSetDefaultNamespace\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ChainConfigResolverTest\\:\\:testSortResolvers\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Parameter \\#1 \\$resolver of method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ChainConfigResolver\\:\\:addResolver\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 4 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Unable to resolve the template type T in call to method PHPUnit\\\\Framework\\\\TestCase\\:\\:getMockBuilder\\(\\)$#" + count: 1 + path: tests/bundle/Core/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:getResolver\\(\\) has parameter \\$groupsBySiteAccess with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:hasParameterProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:parameterProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterDefaultScope\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterDefaultScope\\(\\) has parameter \\$expectedValue with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterDefaultScope\\(\\) has parameter \\$paramName with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterFailedNull\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterFailedWithException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterGlobalScope\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterGlobalScope\\(\\) has parameter \\$expectedValue with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterGlobalScope\\(\\) has parameter \\$paramName with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterRelativeScope\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterRelativeScope\\(\\) has parameter \\$expectedValue with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterRelativeScope\\(\\) has parameter \\$paramName with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterSpecificScope\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterSpecificScope\\(\\) has parameter \\$expectedValue with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetParameterSpecificScope\\(\\) has parameter \\$paramName with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetSetDefaultScope\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testGetSetUndefinedStrategy\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testHasParameterNoNamespace\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testHasParameterNoNamespace\\(\\) has parameter \\$defaultMatch with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testHasParameterNoNamespace\\(\\) has parameter \\$expectedResult with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testHasParameterNoNamespace\\(\\) has parameter \\$globalMatch with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testHasParameterNoNamespace\\(\\) has parameter \\$groupMatch with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testHasParameterNoNamespace\\(\\) has parameter \\$scopeMatch with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testHasParameterWithNamespaceAndScope\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testHasParameterWithNamespaceAndScope\\(\\) has parameter \\$defaultMatch with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testHasParameterWithNamespaceAndScope\\(\\) has parameter \\$expectedResult with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testHasParameterWithNamespaceAndScope\\(\\) has parameter \\$globalMatch with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testHasParameterWithNamespaceAndScope\\(\\) has parameter \\$groupMatch with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\ConfigResolverTest\\:\\:testHasParameterWithNamespaceAndScope\\(\\) has parameter \\$scopeMatch with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Parameter \\#1 \\$container of method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\:\\:setContainer\\(\\) expects Symfony\\\\Component\\\\DependencyInjection\\\\ContainerInterface\\|null, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/bundle/Core/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Converter\\\\AbstractParamConverterTest\\:\\:createConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Converter/AbstractParamConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Converter\\\\AbstractParamConverterTest\\:\\:createConfiguration\\(\\) has parameter \\$class with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Converter/AbstractParamConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Converter\\\\AbstractParamConverterTest\\:\\:createConfiguration\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Converter/AbstractParamConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Converter\\\\ContentParamConverterTest\\:\\:testApplyContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Converter/ContentParamConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Converter\\\\ContentParamConverterTest\\:\\:testApplyContentOptionalWithEmptyAttribute\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Converter/ContentParamConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Converter\\\\ContentParamConverterTest\\:\\:testSupports\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Converter/ContentParamConverterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Converter\\\\ContentParamConverterTest\\:\\:\\$contentServiceMock has no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Converter/ContentParamConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Converter\\\\LocationParamConverterTest\\:\\:testApplyLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Converter/LocationParamConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Converter\\\\LocationParamConverterTest\\:\\:testApplyLocationOptionalWithEmptyAttribute\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Converter/LocationParamConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Converter\\\\LocationParamConverterTest\\:\\:testSupports\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Converter/LocationParamConverterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Converter\\\\LocationParamConverterTest\\:\\:\\$locationServiceMock has no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Converter/LocationParamConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ChainConfigResolverPassTest\\:\\:addResolverProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/ChainConfigResolverPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ChainConfigResolverPassTest\\:\\:testAddResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/ChainConfigResolverPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ChainRoutingPassTest\\:\\:addRouterProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/ChainRoutingPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ChainRoutingPassTest\\:\\:testAddRouter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/ChainRoutingPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ChainRoutingPassTest\\:\\:testAddRouterWithDefaultRouter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/ChainRoutingPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\FieldTypeParameterProviderRegistryPassTest\\:\\:tagsProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\FieldTypeParameterProviderRegistryPassTest\\:\\:testRegisterFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\FieldTypeParameterProviderRegistryPassTest\\:\\:testRegisterFieldTypeNoAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\FragmentPassTest\\:\\:testProcess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/FragmentPassTest.php + + - + message: "#^Offset 0 does not exist on array\\|string\\|null\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/FragmentPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\NotificationRendererPassTest\\:\\:testAddRenderer\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/NotificationRendererPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\NotificationRendererPassTest\\:\\:testAddRendererWithoutAliasThrowsLogicException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/NotificationRendererPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\PlaceholderProviderPassTest\\:\\:testAddProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/PlaceholderProviderPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\PlaceholderProviderPassTest\\:\\:testAddProviderWithoutType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/PlaceholderProviderPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\QueryTypePassTest\\:\\:tagsProvider\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/QueryTypePassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\RegisterSearchEngineIndexerPassTest\\:\\:tagsProvider\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/RegisterSearchEngineIndexerPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\RegisterSearchEnginePassTest\\:\\:tagsProvider\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/RegisterSearchEnginePassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\RegisterStorageEnginePassTest\\:\\:testRegisterDefaultStorageEngine\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/RegisterStorageEnginePassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\RegisterStorageEnginePassTest\\:\\:testRegisterStorageEngine\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/RegisterStorageEnginePassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\RegisterStorageEnginePassTest\\:\\:testRegisterStorageEngineNoAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/RegisterStorageEnginePassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\SecurityPassTest\\:\\:testAlteredDaoAuthenticationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/SecurityPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\SlugConverterConfigurationPassTest\\:\\:configurationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\SlugConverterConfigurationPassTest\\:\\:testMergeConfigurations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\SlugConverterConfigurationPassTest\\:\\:testMergeConfigurations\\(\\) has parameter \\$commandsToAdd with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\SlugConverterConfigurationPassTest\\:\\:testMergeConfigurations\\(\\) has parameter \\$existingOldParameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\SlugConverterConfigurationPassTest\\:\\:testMergeConfigurations\\(\\) has parameter \\$expectedCommands with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPassTest.php + + - + message: "#^Variable \\$slugConverter in PHPDoc tag @var does not match assigned variable \\$slugConverterRef\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\TranslationCollectorPassTest\\:\\:normalizePath\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\TranslationCollectorPassTest\\:\\:testTranslationCollector\\(\\) has parameter \\$availableTranslations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\URLHandlerPassTest\\:\\:testRegisterURLHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/URLHandlerPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\URLHandlerPassTest\\:\\:testRegisterURLHandlerNoScheme\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/URLHandlerPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ViewProvidersPassTest\\:\\:addViewProviderProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/ViewProvidersPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ViewProvidersPassTest\\:\\:testAddViewProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/ViewProvidersPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ViewProvidersPassTest\\:\\:testAddViewProvider\\(\\) has parameter \\$declaredPriority with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/ViewProvidersPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Compiler\\\\ViewProvidersPassTest\\:\\:testAddViewProvider\\(\\) has parameter \\$expectedPriority with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Compiler/ViewProvidersPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingParserTest\\:\\:provideSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingParserTest\\:\\:testContainsDynamicSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingParserTest\\:\\:testContainsDynamicSettings\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingParserTest\\:\\:testContainsDynamicSettings\\(\\) has parameter \\$setting with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingParserTest\\:\\:testParseComplexSetting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingParserTest\\:\\:testParseComplexSetting\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingParserTest\\:\\:testParseComplexSetting\\(\\) has parameter \\$setting with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ComplexSettings\\\\ComplexSettingValueResolverTest\\:\\:testGetArgumentValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParserTest\\:\\:getConfigurationParserMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParserTest\\:\\:testAddSemanticConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParserTest\\:\\:testConstruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParserTest\\:\\:testConstructWrongInnerParser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParserTest\\:\\:testGetSetInnerParsers\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParserTest\\:\\:testMapConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigParserTest\\:\\:testPrePostMap\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\ChainConfigResolverTest\\:\\:hasParameterProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\ChainConfigResolverTest\\:\\:parameterProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\ChainConfigResolverTest\\:\\:testGetParameterDefaultScope\\(\\) has parameter \\$expectedValue with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\ChainConfigResolverTest\\:\\:testGetParameterGlobalScope\\(\\) has parameter \\$expectedValue with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\ChainConfigResolverTest\\:\\:testGetParameterRelativeScope\\(\\) has parameter \\$expectedValue with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\ChainConfigResolverTest\\:\\:testGetParameterSpecificScope\\(\\) has parameter \\$expectedValue with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php + + - + message: "#^Parameter \\#2 \\$groupsBySiteAccess of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Provider\\\\StaticSiteAccessProvider constructor expects array\\<string\\>, array\\<string, array\\<int, string\\>\\> given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\ChainConfigResolverTest\\:\\:\\$siteAccess \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\ConfigResolverTest\\:\\:parameterProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\\\ConfigResolverTest\\:\\:testGetParameterGlobalScope\\(\\) has parameter \\$expectedValue with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ConfigResolverTest.php + + - + message: "#^Parameter \\#2 \\$groupsBySiteAccess of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Provider\\\\StaticSiteAccessProvider constructor expects array\\<string\\>, array\\<string, array\\<int, string\\>\\> given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolverTest.php + + - + message: "#^Parameter \\#2 \\$groupsBySiteAccess of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Provider\\\\StaticSiteAccessProvider constructor expects array\\<string\\>, array\\<string, array\\<int, string\\>\\> given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\AbstractParserTestCase\\:\\:assertConfigResolverParameterValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/AbstractParserTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:getMinimalConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:sessionSettingsProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:testApiKeysSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:testDatabaseSingleSiteaccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:testDatabaseSiteaccessGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:testDefaultPage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:testIndexPage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:testMiscSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:testNoUserSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:testNonExistentSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:testSessionSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:testSessionSettings\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:testSessionSettings\\(\\) has parameter \\$inputParams with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:testUserSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Parameter \\#1 \\$input of static method Symfony\\\\Component\\\\Yaml\\\\Yaml\\:\\:parse\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:\\$minimalConfig has no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:\\$minimalConfig is never read, only written\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\CommonTest\\:\\:\\$suggestionCollector is never read, only written\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ContentTest\\:\\:contentSettingsProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ContentTest\\:\\:getMinimalConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ContentTest\\:\\:testContentSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ContentTest\\:\\:testContentSettings\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ContentTest\\:\\:testContentSettings\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ContentTest\\:\\:testDefaultContentSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ContentTest.php + + - + message: "#^Parameter \\#1 \\$input of static method Symfony\\\\Component\\\\Yaml\\\\Yaml\\:\\:parse\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\FieldType\\\\ImageAssetTest\\:\\:imageAssetSettingsProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\FieldType\\\\ImageAssetTest\\:\\:testDefaultImageAssetSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\FieldType\\\\ImageAssetTest\\:\\:testImageAssetSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\FieldType\\\\ImageAssetTest\\:\\:testImageAssetSettings\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\FieldType\\\\ImageAssetTest\\:\\:testImageAssetSettings\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\IOTest\\:\\:getMinimalConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/IOTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\IOTest\\:\\:testHandlersConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/IOTest.php + + - + message: "#^Parameter \\#1 \\$input of static method Symfony\\\\Component\\\\Yaml\\\\Yaml\\:\\:parse\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/IOTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\IOTest\\:\\:\\$minimalConfig has no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/IOTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\IOTest\\:\\:\\$minimalConfig is never read, only written\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/IOTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ImageTest\\:\\:getMinimalConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ImageTest\\:\\:testPrePostParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ImageTest\\:\\:testVariations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ImageTest.php + + - + message: "#^Parameter \\#1 \\$input of static method Symfony\\\\Component\\\\Yaml\\\\Yaml\\:\\:parse\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ImageTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ImageTest\\:\\:\\$config has no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ImageTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\LanguagesTest\\:\\:\\$minimalConfig\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/LanguagesTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\LanguagesTest\\:\\:getMinimalConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/LanguagesTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\LanguagesTest\\:\\:testLanguagesSingleSiteaccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/LanguagesTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\LanguagesTest\\:\\:testLanguagesSiteaccessGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/LanguagesTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\LanguagesTest\\:\\:testTranslationSiteAccesses\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/LanguagesTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\LanguagesTest\\:\\:testTranslationSiteAccessesWithGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/LanguagesTest.php + + - + message: "#^Parameter \\#1 \\$input of static method Symfony\\\\Component\\\\Yaml\\\\Yaml\\:\\:parse\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/LanguagesTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\TemplatesTest\\:\\:getExpectedConfigFieldDefinitionSettingsTemplates\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/TemplatesTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\TemplatesTest\\:\\:getExpectedConfigFieldDefinitionSettingsTemplates\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/TemplatesTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\TemplatesTest\\:\\:getExpectedConfigFieldTemplates\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/TemplatesTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\TemplatesTest\\:\\:getExpectedConfigFieldTemplates\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/TemplatesTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\TemplatesTest\\:\\:getMinimalConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/TemplatesTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\TemplatesTest\\:\\:testFieldDefinitionSettingsTemplates\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/TemplatesTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\TemplatesTest\\:\\:testFieldTemplates\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/TemplatesTest.php + + - + message: "#^Parameter \\#1 \\$input of static method Symfony\\\\Component\\\\Yaml\\\\Yaml\\:\\:parse\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/TemplatesTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\TemplatesTest\\:\\:\\$config has no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/TemplatesTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ViewTest\\:\\:getMinimalConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ViewTest\\:\\:testContentView\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ViewTest\\:\\:testLocationView\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ViewTest.php + + - + message: "#^Parameter \\#1 \\$input of static method Symfony\\\\Component\\\\Yaml\\\\Yaml\\:\\:parse\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ViewTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\ViewTest\\:\\:\\$config has no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Parser/ViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessorTest\\:\\:getContainerMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessorTest\\:\\:getContextualizerMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessorTest\\:\\:testConstruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessorTest\\:\\:testGetSetContextualizer\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessorTest\\:\\:testMapConfigArray\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessorTest\\:\\:testMapConfigClosure\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessorTest\\:\\:testMapConfigHookableMapperObject\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessorTest\\:\\:testMapConfigMapperObject\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessorTest\\:\\:testMapConfigWrongMapper\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessorTest\\:\\:testMapSetting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php + + - + message: "#^Parameter \\#2 \\$mapper of method Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationProcessor\\:\\:mapConfig\\(\\) expects \\(callable\\(\\)\\: mixed\\)\\|Ibexa\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ConfigurationMapperInterface, stdClass given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:fullMapConfigArrayProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:setContextualParameterProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testFullMapConfigArray\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testFullMapConfigArray\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testFullMapConfigArray\\(\\) has parameter \\$customSANodeKey with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testFullMapConfigArray\\(\\) has parameter \\$defaultValue with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testFullMapConfigArray\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testFullMapConfigArray\\(\\) has parameter \\$globalValue with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testFullMapConfigArray\\(\\) has parameter \\$groups with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testFullMapConfigArray\\(\\) has parameter \\$options with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testFullMapConfigArray\\(\\) has parameter \\$siteaccess with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testFullMapConfigArray\\(\\) has parameter \\$testId with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testGetSetAvailableSiteAccesses\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testGetSetContainer\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testGetSetGroupsBySA\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testGetSetNamespace\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testGetSetSANodeName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testMapConfigArray\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testMapConfigArraySecondLevel\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testMapConfigArrayUnique\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testMapSetting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testSetContextualParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testSetContextualParameter\\(\\) has parameter \\$parameterName with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testSetContextualParameter\\(\\) has parameter \\$scope with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:testSetContextualParameter\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:\\$availableSAs type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:\\$availableSiteAccessGroups type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\ContextualizerTest\\:\\:\\$groupsBySA type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\DynamicSettingParserTest\\:\\:isDynamicSettingProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\DynamicSettingParserTest\\:\\:parseDynamicSettingProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\DynamicSettingParserTest\\:\\:testIsDynamicSetting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\DynamicSettingParserTest\\:\\:testIsDynamicSetting\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\DynamicSettingParserTest\\:\\:testIsDynamicSetting\\(\\) has parameter \\$setting with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\DynamicSettingParserTest\\:\\:testParseDynamicSetting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\DynamicSettingParserTest\\:\\:testParseDynamicSetting\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\DynamicSettingParserTest\\:\\:testParseDynamicSetting\\(\\) has parameter \\$setting with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\SiteAccessAware\\\\DynamicSettingParserTest\\:\\:testParseDynamicSettingFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\Collector\\\\SuggestionCollectorTest\\:\\:testAddHasGetSuggestions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\ConfigSuggestionTest\\:\\:testConfigSuggestion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\ConfigSuggestionTest\\:\\:testEmptyConstructor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\Formatter\\\\YamlSuggestionFormatterTest\\:\\:testFormat\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Suggestion/Formatter/YamlSuggestionFormatterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Configuration\\\\Suggestion\\\\Formatter\\\\YamlSuggestionFormatterTest\\:\\:testFormatNoSuggestion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Configuration/Suggestion/Formatter/YamlSuggestionFormatterTest.php + + - + message: "#^Argument of an invalid type array\\|bool\\|float\\|int\\|string\\|null supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Generator expects value type array\\{bool, array\\{string\\}\\}, array\\{false, false\\} given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Generator expects value type array\\{bool, array\\{string\\}\\}, array\\{true, true\\} given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:cacheConfigurationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:getFilteringQueryBuilderData\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:getMinimalConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:repositoriesConfigurationFieldGroupsProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testCacheConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testCacheConfiguration\\(\\) has parameter \\$customCacheConfig with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testCacheConfigurationCustomPurgeService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testImageMagickConfigurationBasic\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testImageMagickConfigurationFilters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testImagePlaceholderConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testLocaleConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testRegisteredPolicies\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testRepositoriesConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testRepositoriesConfigurationCompatibility\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testRepositoriesConfigurationCompatibility2\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testRepositoriesConfigurationEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testRepositoriesConfigurationFieldGroups\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testRepositoriesConfigurationFieldGroups\\(\\) has parameter \\$expectedRepositories with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testRepositoriesConfigurationFieldGroups\\(\\) has parameter \\$repositories with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testRepositoriesConfigurationSearchEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testRepositoriesConfigurationStorageEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testRoutingConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testSiteAccessConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testSiteAccessNoConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:testUrlAliasConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Parameter \\#1 \\$input of static method Symfony\\\\Component\\\\Yaml\\\\Yaml\\:\\:parse\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Parameter \\#2 \\$array of method PHPUnit\\\\Framework\\\\Assert\\:\\:assertArrayHasKey\\(\\) expects array\\|ArrayAccess, array\\|bool\\|float\\|int\\|string\\|null given\\.$#" + count: 2 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:\\$minimalConfig has no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:\\$minimalConfig is never read, only written\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\IbexaCoreExtensionTest\\:\\:\\$siteaccessConfig has no type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\PoliciesConfigBuilderTest\\:\\:policiesConfigProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\PoliciesConfigBuilderTest\\:\\:testAddConfig\\(\\) has parameter \\$configOne with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\PoliciesConfigBuilderTest\\:\\:testAddConfig\\(\\) has parameter \\$configTwo with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\PoliciesConfigBuilderTest\\:\\:testAddConfig\\(\\) has parameter \\$expectedConfig with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\PoliciesConfigBuilderTest\\:\\:testAddResource\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\YamlPolicyProviderTest\\:\\:testMultipleYaml\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Security/PolicyProvider/YamlPolicyProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Security\\\\PolicyProvider\\\\YamlPolicyProviderTest\\:\\:testSingleYaml\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Security/PolicyProvider/YamlPolicyProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Stub\\\\QueryTypeBundle\\\\QueryType\\\\TestQueryType\\:\\:getQuery\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Stub/QueryTypeBundle/QueryType/TestQueryType.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Stub\\\\QueryTypeBundle\\\\QueryType\\\\TestQueryType\\:\\:getQuery\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query but return statement is missing\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Stub/QueryTypeBundle/QueryType/TestQueryType.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Stub\\\\QueryTypeBundle\\\\QueryType\\\\TestQueryType\\:\\:getSupportedParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Stub/QueryTypeBundle/QueryType/TestQueryType.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Stub\\\\QueryTypeBundle\\\\QueryType\\\\TestQueryType\\:\\:getSupportedParameters\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Stub/QueryTypeBundle/QueryType/TestQueryType.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Stub\\\\StubPolicyProvider\\:\\:__construct\\(\\) has parameter \\$policies with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Stub/StubPolicyProvider.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Stub\\\\StubPolicyProvider\\:\\:addPolicies\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Stub/StubPolicyProvider.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Stub\\\\StubPolicyProvider\\:\\:\\$policies type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Stub/StubPolicyProvider.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Stub\\\\StubYamlPolicyProvider\\:\\:__construct\\(\\) has parameter \\$files with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Stub/StubYamlPolicyProvider.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Stub\\\\StubYamlPolicyProvider\\:\\:getFiles\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Stub/StubYamlPolicyProvider.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\DependencyInjection\\\\Stub\\\\StubYamlPolicyProvider\\:\\:\\$files type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/DependencyInjection/Stub/StubYamlPolicyProvider.php + + - + message: "#^Call to an undefined method Ibexa\\\\Bundle\\\\Core\\\\ApiLoader\\\\RepositoryConfigurationProvider\\:\\:method\\(\\)\\.$#" + count: 3 + path: tests/bundle/Core/Entity/EntityManagerFactoryTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Entity\\\\EntityManagerFactoryTest\\:\\:\\$serviceLocator is never read, only written\\.$#" + count: 1 + path: tests/bundle/Core/Entity/EntityManagerFactoryTest.php + + - + message: "#^Call to an undefined method PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\|Psr\\\\Log\\\\LoggerInterface\\:\\:expects\\(\\)\\.$#" + count: 2 + path: tests/bundle/Core/EventListener/BackgroundIndexingTerminateListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\BackgroundIndexingTerminateListenerTest\\:\\:indexDeleteProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/BackgroundIndexingTerminateListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\BackgroundIndexingTerminateListenerTest\\:\\:indexingProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/BackgroundIndexingTerminateListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\BackgroundIndexingTerminateListenerTest\\:\\:testGetSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/BackgroundIndexingTerminateListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\BackgroundIndexingTerminateListenerTest\\:\\:testIndexDelete\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/BackgroundIndexingTerminateListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\BackgroundIndexingTerminateListenerTest\\:\\:testIndexing\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/BackgroundIndexingTerminateListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\BackgroundIndexingTerminateListenerTest\\:\\:testIndexing\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/BackgroundIndexingTerminateListenerTest.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$value$#" + count: 1 + path: tests/bundle/Core/EventListener/BackgroundIndexingTerminateListenerTest.php + + - + message: "#^Parameter \\#1 \\$logger of method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\BackgroundIndexingTerminateListener\\:\\:setLogger\\(\\) expects Psr\\\\Log\\\\LoggerInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\|Psr\\\\Log\\\\LoggerInterface given\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/BackgroundIndexingTerminateListenerTest.php + + - + message: "#^Parameter \\#1 \\$stub of method PHPUnit\\\\Framework\\\\MockObject\\\\Builder\\\\InvocationMocker\\<Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Handler\\>\\:\\:will\\(\\) expects PHPUnit\\\\Framework\\\\MockObject\\\\Stub\\\\Stub, PHPUnit\\\\Framework\\\\MockObject\\\\Stub given\\.$#" + count: 2 + path: tests/bundle/Core/EventListener/BackgroundIndexingTerminateListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\BackwardCompatibleCommandListenerTest\\:\\:createBackwardCompatibleCommand\\(\\) has parameter \\$aliases with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/BackwardCompatibleCommandListenerTest.php + + - + message: "#^Method class@anonymous/tests/bundle/Core/EventListener/BackwardCompatibleCommandListenerTest\\.php\\:116\\:\\:__construct\\(\\) has parameter \\$deprecatedAliases with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/BackwardCompatibleCommandListenerTest.php + + - + message: "#^PHPDoc tag @return with type Ibexa\\\\Bundle\\\\Core\\\\Command\\\\BackwardCompatibleCommand\\|Symfony\\\\Component\\\\Console\\\\Command\\\\Command is not subtype of native type Symfony\\\\Component\\\\Console\\\\Command\\\\Command\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/BackwardCompatibleCommandListenerTest.php + + - + message: "#^Argument of an invalid type PHPUnit\\\\Framework\\\\MockObject\\\\MockObject supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ConfigScopeListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ConfigScopeListenerTest\\:\\:testGetSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ConfigScopeListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ConfigScopeListenerTest\\:\\:testOnConfigScopeChange\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ConfigScopeListenerTest.php + + - + message: "#^Parameter \\#1 \\$viewProviders of method Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ConfigScopeListener\\:\\:setViewProviders\\(\\) expects array, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ConfigScopeListenerTest.php + + - + message: "#^Parameter \\#2 \\$viewManager of class Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\ConfigScopeListener constructor expects Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ConfigScopeListenerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ConfigScopeListenerTest\\:\\:\\$viewProviders \\(PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept array\\<int, Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\ViewProvider&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\>\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ConfigScopeListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ConsoleCommandListenerTest\\:\\:testDefaultSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ConsoleCommandListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ConsoleCommandListenerTest\\:\\:testGetSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ConsoleCommandListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ConsoleCommandListenerTest\\:\\:testInvalidSiteAccessDev\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ConsoleCommandListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ConsoleCommandListenerTest\\:\\:testInvalidSiteAccessProd\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ConsoleCommandListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ConsoleCommandListenerTest\\:\\:testValidSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ConsoleCommandListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ContentDownloadRouteReferenceListenerTest\\:\\:getListener\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ContentDownloadRouteReferenceListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ContentDownloadRouteReferenceListenerTest\\:\\:testDownloadNameOverrideWorks\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ContentDownloadRouteReferenceListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ContentDownloadRouteReferenceListenerTest\\:\\:testGeneratesCorrectRouteReference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ContentDownloadRouteReferenceListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ContentDownloadRouteReferenceListenerTest\\:\\:testIgnoresOtherRoutes\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ContentDownloadRouteReferenceListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ContentDownloadRouteReferenceListenerTest\\:\\:testThrowsExceptionOnBadContentParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ContentDownloadRouteReferenceListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ContentDownloadRouteReferenceListenerTest\\:\\:testThrowsExceptionOnBadFieldIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ContentDownloadRouteReferenceListenerTest.php + + - + message: "#^Call to an undefined method Exception\\:\\:setMessageTemplate\\(\\)\\.$#" + count: 2 + path: tests/bundle/Core/EventListener/ExceptionListenerTest.php + + - + message: "#^Call to an undefined method Exception\\:\\:setParameters\\(\\)\\.$#" + count: 2 + path: tests/bundle/Core/EventListener/ExceptionListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ExceptionListenerTest\\:\\:badRequestExceptionProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ExceptionListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ExceptionListenerTest\\:\\:otherExceptionProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ExceptionListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ExceptionListenerTest\\:\\:testBadRequestException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ExceptionListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ExceptionListenerTest\\:\\:testGetSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ExceptionListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ExceptionListenerTest\\:\\:testNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ExceptionListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ExceptionListenerTest\\:\\:testOtherRepositoryException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ExceptionListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ExceptionListenerTest\\:\\:testUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ExceptionListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ExceptionListenerTest\\:\\:testUntouchedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ExceptionListenerTest.php + + - + message: "#^PHPDoc tag @param for parameter \\$exception with type Exception\\|Ibexa\\\\Core\\\\Base\\\\Translatable is not subtype of native type Exception\\.$#" + count: 2 + path: tests/bundle/Core/EventListener/ExceptionListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\IndexRequestListenerTest\\:\\:indexPageProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/IndexRequestListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\IndexRequestListenerTest\\:\\:testOnKernelRequestIndexNotOnIndexPage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/IndexRequestListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\IndexRequestListenerTest\\:\\:testOnKernelRequestIndexOnIndexPage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/IndexRequestListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\IndexRequestListenerTest\\:\\:testOnKernelRequestIndexOnIndexPage\\(\\) has parameter \\$configuredIndexPath with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/IndexRequestListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\IndexRequestListenerTest\\:\\:testOnKernelRequestIndexOnIndexPage\\(\\) has parameter \\$expectedIndexPath with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/IndexRequestListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\IndexRequestListenerTest\\:\\:testOnKernelRequestIndexOnIndexPage\\(\\) has parameter \\$requestPath with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/IndexRequestListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\IndexRequestListenerTest\\:\\:testSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/IndexRequestListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\LocaleListenerTest\\:\\:onKernelRequestProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/LocaleListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\LocaleListenerTest\\:\\:testOnKernelRequest\\(\\) has parameter \\$configuredLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/LocaleListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\LocaleListenerTest\\:\\:testOnKernelRequest\\(\\) has parameter \\$convertedLocalesValueMap with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/LocaleListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\LocaleListenerTest\\:\\:testOnKernelRequest\\(\\) has parameter \\$expectedLocale with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/LocaleListenerTest.php + + - + message: "#^Parameter \\#1 \\$requestStack of class Symfony\\\\Component\\\\HttpKernel\\\\EventListener\\\\LocaleListener constructor expects Symfony\\\\Component\\\\HttpFoundation\\\\RequestStack, Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/LocaleListenerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\LocaleListenerTest\\:\\:\\$requestStack \\(Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept Symfony\\\\Component\\\\HttpFoundation\\\\RequestStack\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/LocaleListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\OriginalRequestListenerTest\\:\\:testGetSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/OriginalRequestListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\OriginalRequestListenerTest\\:\\:testOnKernelRequestNoOriginalRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/OriginalRequestListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\OriginalRequestListenerTest\\:\\:testOnKernelRequestNotMaster\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/OriginalRequestListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\OriginalRequestListenerTest\\:\\:testOnKernelRequestWithOriginalRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/OriginalRequestListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\RejectExplicitFrontControllerRequestsListenerTest\\:\\:prohibitedRequestDataProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/RejectExplicitFrontControllerRequestsListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\RejectExplicitFrontControllerRequestsListenerTest\\:\\:validRequestDataProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/RejectExplicitFrontControllerRequestsListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListenerTest\\:\\:testOnKernelRequestForward\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/RequestEventListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListenerTest\\:\\:testOnKernelRequestForwardSubRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/RequestEventListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListenerTest\\:\\:testOnKernelRequestRedirect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/RequestEventListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListenerTest\\:\\:testOnKernelRequestRedirectPrependSiteaccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/RequestEventListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListenerTest\\:\\:testOnKernelRequestRedirectSubRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/RequestEventListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListenerTest\\:\\:testOnKernelRequestRedirectWithLocationId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/RequestEventListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListenerTest\\:\\:testSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/RequestEventListenerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\RequestEventListenerTest\\:\\:\\$event is never read, only written\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/RequestEventListenerTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\RoutingListenerTest\\:\\:\\$container\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/RoutingListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\RoutingListenerTest\\:\\:testGetSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/RoutingListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\RoutingListenerTest\\:\\:testOnSiteAccessMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/RoutingListenerTest.php + + - + message: "#^Parameter \\#1 \\$configResolver of class Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\RoutingListener constructor expects Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 2 + path: tests/bundle/Core/EventListener/RoutingListenerTest.php + + - + message: "#^Parameter \\#2 \\$urlAliasRouter of class Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\RoutingListener constructor expects Symfony\\\\Component\\\\Routing\\\\RouterInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 2 + path: tests/bundle/Core/EventListener/RoutingListenerTest.php + + - + message: "#^Parameter \\#3 \\$urlAliasGenerator of class Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\RoutingListener constructor expects Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 2 + path: tests/bundle/Core/EventListener/RoutingListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionInitByPostListenerTest\\:\\:testGetSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionInitByPostListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionInitByPostListenerTest\\:\\:testOnSiteAccessMatchNewSessionName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionInitByPostListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionInitByPostListenerTest\\:\\:testOnSiteAccessMatchNoSessionService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionInitByPostListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionInitByPostListenerTest\\:\\:testOnSiteAccessMatchRequestNoSessionName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionInitByPostListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionInitByPostListenerTest\\:\\:testOnSiteAccessMatchSubRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionInitByPostListenerTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionSetDynamicNameListenerTest\\:\\:\\$session\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionSetDynamicNameListenerTest\\:\\:onSiteAccessMatchProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionSetDynamicNameListenerTest\\:\\:testGetSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionSetDynamicNameListenerTest\\:\\:testOnSiteAccessMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionSetDynamicNameListenerTest\\:\\:testOnSiteAccessMatch\\(\\) has parameter \\$configuredSessionStorageOptions with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionSetDynamicNameListenerTest\\:\\:testOnSiteAccessMatch\\(\\) has parameter \\$expectedSessionStorageOptions with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionSetDynamicNameListenerTest\\:\\:testOnSiteAccessMatchNoConfiguredSessionName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionSetDynamicNameListenerTest\\:\\:testOnSiteAccessMatchNoSession\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionSetDynamicNameListenerTest\\:\\:testOnSiteAccessMatchNonNativeSessionStorage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SessionSetDynamicNameListenerTest\\:\\:testOnSiteAccessMatchSubRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php + + - + message: "#^Parameter \\#1 \\$configResolver of class Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\SessionSetDynamicNameListener constructor expects Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 6 + path: tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php + + - + message: "#^Parameter \\#2 \\$sessionStorageFactory of class Ibexa\\\\Bundle\\\\Core\\\\EventListener\\\\SessionSetDynamicNameListener constructor expects Symfony\\\\Component\\\\HttpFoundation\\\\Session\\\\Storage\\\\SessionStorageFactoryInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 5 + path: tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListenerTest\\:\\:siteAccessMatchProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListenerTest\\:\\:testGetSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListenerTest\\:\\:testOnSiteAccessMatchMasterRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListenerTest\\:\\:testOnSiteAccessMatchMasterRequest\\(\\) has parameter \\$expectedSemanticPathinfo with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListenerTest\\:\\:testOnSiteAccessMatchMasterRequest\\(\\) has parameter \\$expectedVPArray with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListenerTest\\:\\:testOnSiteAccessMatchMasterRequest\\(\\) has parameter \\$expectedVPString with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListenerTest\\:\\:testOnSiteAccessMatchMasterRequest\\(\\) has parameter \\$uri with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListenerTest\\:\\:testOnSiteAccessMatchSubRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListenerTest\\:\\:testOnSiteAccessMatchSubRequest\\(\\) has parameter \\$expectedViewParameters with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListenerTest\\:\\:testOnSiteAccessMatchSubRequest\\(\\) has parameter \\$semanticPathinfo with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListenerTest\\:\\:testOnSiteAccessMatchSubRequest\\(\\) has parameter \\$uri with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\SiteAccessListenerTest\\:\\:testOnSiteAccessMatchSubRequest\\(\\) has parameter \\$vpString with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/SiteAccessListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\FooServiceInterface\\:\\:someMethod\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/FooServiceInterface.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\FooServiceInterface\\:\\:someMethod\\(\\) has parameter \\$arg with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/FooServiceInterface.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\TestOutput\\:\\:clear\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/TestOutput.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\TestOutput\\:\\:doWrite\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/TestOutput.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\TestOutput\\:\\:doWrite\\(\\) has parameter \\$message with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/TestOutput.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\TestOutput\\:\\:doWrite\\(\\) has parameter \\$newline with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/TestOutput.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\TestOutput\\:\\:\\$output has no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/TestOutput.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\ViewManager\\:\\:renderContent\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/ViewManager.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\ViewManager\\:\\:renderContent\\(\\) should return string but return statement is missing\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/ViewManager.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\ViewManager\\:\\:renderContentView\\(\\) has parameter \\$defaultParams with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/ViewManager.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\ViewManager\\:\\:renderContentView\\(\\) should return string but return statement is missing\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/ViewManager.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\ViewManager\\:\\:renderLocation\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/ViewManager.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\ViewManager\\:\\:renderLocation\\(\\) should return string but return statement is missing\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/ViewManager.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\ViewManager\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/ViewManager.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\ViewProvider\\:\\:getView\\(\\) should return Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View but return statement is missing\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/ViewProvider.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\Stubs\\\\ViewProvider\\:\\:setSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/Stubs/ViewProvider.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ViewControllerListenerTest\\:\\:testGetControllerMatchedView\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ViewControllerListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ViewControllerListenerTest\\:\\:testGetControllerNoBuilder\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ViewControllerListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ViewControllerListenerTest\\:\\:testGetControllerWithClosure\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ViewControllerListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ViewControllerListenerTest\\:\\:testGetSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ViewControllerListenerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ViewControllerListenerTest\\:\\:\\$controllerResolver \\(PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&Symfony\\\\Component\\\\HttpKernel\\\\Controller\\\\ControllerResolver\\) does not accept PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&Symfony\\\\Component\\\\HttpKernel\\\\Controller\\\\ControllerResolverInterface\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ViewControllerListenerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventListener\\\\ViewControllerListenerTest\\:\\:\\$viewConfigurator is unused\\.$#" + count: 1 + path: tests/bundle/Core/EventListener/ViewControllerListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventSubscriber\\\\CrowdinRequestLocaleSubscriberTest\\:\\:testSetLocale\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventSubscriber/CrowdinRequestLocaleSubscriberTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventSubscriber\\\\CrowdinRequestLocaleSubscriberTest\\:\\:testSetLocale\\(\\) has parameter \\$shouldHaveCustomLocale with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventSubscriber/CrowdinRequestLocaleSubscriberTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventSubscriber\\\\CrowdinRequestLocaleSubscriberTest\\:\\:testSetRequestsProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/EventSubscriber/CrowdinRequestLocaleSubscriberTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventSubscriber\\\\TrustedHeaderClientIpEventSubscriberTest\\:\\:getTrustedHeaderEventSubscriberTestData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventSubscriber/TrustedHeaderClientIpEventSubscriberTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventSubscriber\\\\TrustedHeaderClientIpEventSubscriberTest\\:\\:testTrustedHeaderEventSubscriberWithTrustedProxy\\(\\) has parameter \\$headers with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventSubscriber/TrustedHeaderClientIpEventSubscriberTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\EventSubscriber\\\\TrustedHeaderClientIpEventSubscriberTest\\:\\:testTrustedHeaderEventSubscriberWithTrustedProxy\\(\\) has parameter \\$server with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/EventSubscriber/TrustedHeaderClientIpEventSubscriberTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRendererTest\\:\\:testGetName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRendererTest\\:\\:testRendererAbsoluteUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRendererTest\\:\\:testRendererControllerReference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRendererTest\\:\\:testSetFragmentPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRendererTest\\:\\:testSetFragmentPathNotRoutableRenderer\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php + + - + message: "#^Parameter \\#1 \\$innerRenderer of class Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\DecoratedFragmentRenderer constructor expects Symfony\\\\Component\\\\HttpKernel\\\\Fragment\\\\FragmentRendererInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 5 + path: tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php + + - + message: "#^Binary operation \"\\.\" between 'rendered_' and \\(Closure\\(array\\<string, mixed\\>\\)\\: string\\)\\|string results in an error\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/DirectFragmentRendererTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\FragmentListenerFactoryTest\\:\\:buildFragmentListenerProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/FragmentListenerFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\FragmentListenerFactoryTest\\:\\:testBuildFragmentListener\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/FragmentListenerFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\FragmentListenerFactoryTest\\:\\:testBuildFragmentListener\\(\\) has parameter \\$isFragmentCandidate with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/FragmentListenerFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\FragmentListenerFactoryTest\\:\\:testBuildFragmentListener\\(\\) has parameter \\$requestUri with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/FragmentListenerFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\FragmentListenerFactoryTest\\:\\:testBuildFragmentListenerNoRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/FragmentListenerFactoryTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\FragmentRendererBaseTest\\:\\:\\$innerRenderer\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/FragmentRendererBaseTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\:\\:getSubMatchers\\(\\)\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/FragmentRendererBaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRendererTest\\:\\:testRendererControllerReference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Fragment/InlineFragmentRendererTest.php + + - + message: "#^Parameter \\#1 \\$innerRenderer of class Ibexa\\\\Bundle\\\\Core\\\\Fragment\\\\InlineFragmentRenderer constructor expects Symfony\\\\Component\\\\HttpKernel\\\\Fragment\\\\FragmentRendererInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 2 + path: tests/bundle/Core/Fragment/InlineFragmentRendererTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasCleanerTest\\:\\:testRemoveAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/AliasCleanerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGeneratorTest\\:\\:assertImageVariationIsCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/AliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGeneratorTest\\:\\:supportsValueProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/AliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGeneratorTest\\:\\:testGetVariationAlreadyStored\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/AliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGeneratorTest\\:\\:testGetVariationInvalidVariation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/AliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGeneratorTest\\:\\:testGetVariationNotStored\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/AliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGeneratorTest\\:\\:testGetVariationNotStoredHavingReferences\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/AliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGeneratorTest\\:\\:testGetVariationOriginal\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/AliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGeneratorTest\\:\\:testGetVariationOriginalNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/AliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGeneratorTest\\:\\:testGetVariationWrongValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/AliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\AliasGeneratorTest\\:\\:testSupportsValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/AliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\BinaryLoaderTest\\:\\:testFindBadPathRoot\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/BinaryLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\BinaryLoaderTest\\:\\:testFindMissing\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/BinaryLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\BinaryLoaderTest\\:\\:testFindNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/BinaryLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Cache\\\\Resolver\\\\ProxyResolverTest\\:\\:testResolveAndRemovePortUsingProxyHost\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Cache/Resolver/ProxyResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Cache\\\\Resolver\\\\ProxyResolverTest\\:\\:testResolveAndRemovePortUsingProxyHostWithTrailingSlash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Cache/Resolver/ProxyResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Cache\\\\Resolver\\\\ProxyResolverTest\\:\\:testResolveUsingProxyHostWithTrailingSlash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Cache/Resolver/ProxyResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Cache\\\\Resolver\\\\RelativeResolverTest\\:\\:testResolve\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Cache/Resolver/RelativeResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Cache\\\\ResolverFactoryTest\\:\\:testCreateProxyCacheResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Cache/ResolverFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Cache\\\\ResolverFactoryTest\\:\\:testCreateRelativeCacheResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Cache/ResolverFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilterTest\\:\\:getFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilterTest\\:\\:getSetOptionNoDefaulValueProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilterTest\\:\\:getSetOptionWithDefaulValueProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilterTest\\:\\:testGetSetOptionNoDefaultValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilterTest\\:\\:testGetSetOptionNoDefaultValue\\(\\) has parameter \\$optionName with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilterTest\\:\\:testGetSetOptionNoDefaultValue\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilterTest\\:\\:testGetSetOptionWithDefaultValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilterTest\\:\\:testGetSetOptionWithDefaultValue\\(\\) has parameter \\$defaultValue with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilterTest\\:\\:testGetSetOptionWithDefaultValue\\(\\) has parameter \\$optionName with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilterTest\\:\\:testGetSetOptionWithDefaultValue\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\AbstractFilterTest\\:\\:testGetSetOptions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfigurationTest\\:\\:testAll\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/FilterConfigurationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfigurationTest\\:\\:testGetEzVariationImagineFilters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/FilterConfigurationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfigurationTest\\:\\:testGetEzVariationImagineOptions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/FilterConfigurationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfigurationTest\\:\\:testGetEzVariationNoReference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/FilterConfigurationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfigurationTest\\:\\:testGetEzVariationWithReference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/FilterConfigurationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfigurationTest\\:\\:testGetNoEzVariationInvalidImagineFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/FilterConfigurationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\FilterConfigurationTest\\:\\:testGetOnlyImagineFilters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/FilterConfigurationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\BorderFilterLoaderTest\\:\\:loadInvalidProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/BorderFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\BorderFilterLoaderTest\\:\\:loadProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/BorderFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\BorderFilterLoaderTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/BorderFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\BorderFilterLoaderTest\\:\\:testLoad\\(\\) has parameter \\$color with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/BorderFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\BorderFilterLoaderTest\\:\\:testLoad\\(\\) has parameter \\$thickX with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/BorderFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\BorderFilterLoaderTest\\:\\:testLoad\\(\\) has parameter \\$thickY with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/BorderFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\BorderFilterLoaderTest\\:\\:testLoadDefaultColor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/BorderFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\BorderFilterLoaderTest\\:\\:testLoadInvalidOptions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/BorderFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\BorderFilterLoaderTest\\:\\:testLoadInvalidOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/BorderFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\CropFilterLoaderTest\\:\\:loadInvalidProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/CropFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\CropFilterLoaderTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/CropFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\CropFilterLoaderTest\\:\\:testLoadInvalidOptions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/CropFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\CropFilterLoaderTest\\:\\:testLoadInvalidOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/CropFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\GrayscaleFilterLoaderTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/GrayscaleFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ReduceNoiseFilterLoaderTest\\:\\:testLoadInvalidDriver\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ReduceNoiseFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleDownOnlyFilterLoaderTest\\:\\:loadInvalidProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleDownOnlyFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleDownOnlyFilterLoaderTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleDownOnlyFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleDownOnlyFilterLoaderTest\\:\\:testLoadInvalidOptions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleDownOnlyFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleDownOnlyFilterLoaderTest\\:\\:testLoadInvalidOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleDownOnlyFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleExactFilterLoaderTest\\:\\:loadInvalidProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleExactFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleExactFilterLoaderTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleExactFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleExactFilterLoaderTest\\:\\:testLoadInvalidOptions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleExactFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleExactFilterLoaderTest\\:\\:testLoadInvalidOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleExactFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleFilterLoaderTest\\:\\:loadInvalidProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleFilterLoaderTest\\:\\:testLoadHeighten\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleFilterLoaderTest\\:\\:testLoadInvalidOptions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleFilterLoaderTest\\:\\:testLoadInvalidOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleFilterLoaderTest\\:\\:testLoadWiden\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleHeightDownOnlyFilterLoaderTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleHeightDownOnlyFilterLoaderTest\\:\\:testLoadInvalid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleHeightFilterLoaderTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleHeightFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleHeightFilterLoaderTest\\:\\:testLoadFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleHeightFilterLoaderTest.php + + - + message: "#^Method PHPUnit\\\\Framework\\\\TestCase\\:\\:createMock\\(\\) invoked with 2 parameters, 1 required\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleHeightFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScalePercentFilterLoaderTest\\:\\:loadInvalidProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScalePercentFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScalePercentFilterLoaderTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScalePercentFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScalePercentFilterLoaderTest\\:\\:testLoadInvalidOptions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScalePercentFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScalePercentFilterLoaderTest\\:\\:testLoadInvalidOptions\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScalePercentFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleWidthDownOnlyFilterLoaderTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleWidthDownOnlyFilterLoaderTest\\:\\:testLoadInvalid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleWidthFilterLoaderTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleWidthFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\ScaleWidthFilterLoaderTest\\:\\:testLoadFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleWidthFilterLoaderTest.php + + - + message: "#^Method PHPUnit\\\\Framework\\\\TestCase\\:\\:createMock\\(\\) invoked with 2 parameters, 1 required\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/ScaleWidthFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\SwirlFilterLoaderTest\\:\\:loadWithOptionProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/SwirlFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\SwirlFilterLoaderTest\\:\\:testLoadNoOption\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/SwirlFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\SwirlFilterLoaderTest\\:\\:testLoadWithOption\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/SwirlFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\Loader\\\\SwirlFilterLoaderTest\\:\\:testLoadWithOption\\(\\) has parameter \\$degrees with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/Loader/SwirlFilterLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\Filter\\\\UnsupportedFilterTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/Filter/UnsupportedFilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:getFilePathProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:resolveProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testGetFilePath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testGetFilePath\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testGetFilePath\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testGetFilePath\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testIsStoredImageDoesntExist\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testIsStoredImageExists\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testRemoveEmptyFilters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testRemoveWithFilters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testResolve\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testResolve\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testResolve\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testResolve\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testResolve\\(\\) has parameter \\$requestUrl with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testResolve\\(\\) has parameter \\$variationPath with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testResolveMissing\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testResolveNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\IORepositoryResolverTest\\:\\:testStore\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/IORepositoryResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\ImageAsset\\\\AliasGeneratorTest\\:\\:testGetVariationOfImageAsset\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/ImageAsset/AliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\ImageAsset\\\\AliasGeneratorTest\\:\\:testGetVariationOfNonImageAsset\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/ImageAsset/AliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\ImageAsset\\\\AliasGeneratorTest\\:\\:testSupport\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/ImageAsset/AliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorConfiguratorTest\\:\\:testConfigure\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderAliasGeneratorConfiguratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorTest\\:\\:getVariationProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorTest\\:\\:supportsValueProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorTest\\:\\:testGetVariationOriginalFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorTest\\:\\:testGetVariationOriginalFound\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorTest\\:\\:testGetVariationOriginalNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorTest\\:\\:testGetVariationOriginalNotFound\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorTest\\:\\:testGetVariationReturnsPlaceholderIfBinaryDataIsNotAvailable\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorTest\\:\\:testGetVariationSkipsPlaceholderGeneration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorTest\\:\\:testGetVariationSkipsPlaceholderGeneration\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorTest\\:\\:testGetVariationWrongValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorTest\\:\\:testSupportsValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderAliasGeneratorTest\\:\\:\\$placeholderOptions type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\GenericProviderTest\\:\\:assertColorEquals\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderProvider/GenericProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\GenericProviderTest\\:\\:assertColorEquals\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderProvider/GenericProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\GenericProviderTest\\:\\:assertSizeEquals\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderProvider/GenericProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\GenericProviderTest\\:\\:assertSizeEquals\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderProvider/GenericProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\GenericProviderTest\\:\\:getPlaceholderDataProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderProvider/GenericProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\GenericProviderTest\\:\\:testGetPlaceholder\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderProvider/GenericProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\GenericProviderTest\\:\\:testGetPlaceholder\\(\\) has parameter \\$expectedText with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderProvider/GenericProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProvider\\\\GenericProviderTest\\:\\:testGetPlaceholder\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderProvider/GenericProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProviderRegistryTest\\:\\:testConstructor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderProviderRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProviderRegistryTest\\:\\:testGetProviderKnown\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderProviderRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProviderRegistryTest\\:\\:testGetProviderUnknown\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderProviderRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\PlaceholderProviderRegistryTest\\:\\:testSupports\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/PlaceholderProviderRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPathGenerator\\\\AliasDirectoryVariationPathGeneratorTest\\:\\:testGetVariationPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/VariationPathGenerator/AliasDirectoryVariationPathGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPathGenerator\\\\OriginalDirectoryVariationPathGeneratorTest\\:\\:testGetVariationPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileVariationPurgerTest\\:\\:createPurger\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurgerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileVariationPurgerTest\\:\\:createPurger\\(\\) has parameter \\$fileList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurgerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileVariationPurgerTest\\:\\:testDoesNotPurgeNotExistingItem\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurgerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileVariationPurgerTest\\:\\:testIteratesOverItems\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurgerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\ImageFileVariationPurgerTest\\:\\:testPurgesExistingItem\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurgerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\LegacyStorageImageFileListTest\\:\\:configureRowReaderMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\LegacyStorageImageFileListTest\\:\\:configureRowReaderMock\\(\\) has parameter \\$fileList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\LegacyStorageImageFileListTest\\:\\:testImageIdTransformation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Imagine\\\\VariationPurger\\\\LegacyStorageImageFileListTest\\:\\:testIterator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileListTest.php + + - + message: "#^Cannot access offset 'path' on array\\{scheme\\?\\: string, host\\?\\: string, port\\?\\: int\\<0, 65535\\>, user\\?\\: string, pass\\?\\: string, path\\?\\: string, query\\?\\: string, fragment\\?\\: string\\}\\|false\\.$#" + count: 2 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouterTest\\:\\:getExpectedRequestContext\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouterTest\\:\\:getExpectedRequestContext\\(\\) has parameter \\$uri with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouterTest\\:\\:providerGenerateNoSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouterTest\\:\\:providerGenerateWithSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouterTest\\:\\:testGenerateNoSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouterTest\\:\\:testGenerateNoSiteAccess\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouterTest\\:\\:testGenerateReverseSiteAccessMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouterTest\\:\\:testGenerateWithSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouterTest\\:\\:testGetContextBySimplifiedRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouterTest\\:\\:testMatchRequestRegularPathinfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\DefaultRouterTest\\:\\:testMatchRequestWithSemanticPathinfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Offset 'scheme' does not exist on array\\{scheme\\?\\: string, host\\: string, port\\: int\\<0, 65535\\>, user\\?\\: string, pass\\?\\: string, path\\?\\: string, query\\?\\: string, fragment\\?\\: string\\}\\.$#" + count: 4 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Offset 'scheme' does not exist on array\\{scheme\\?\\: string, host\\: string, port\\?\\: int\\<0, 65535\\>, user\\?\\: string, pass\\?\\: string, path\\?\\: string, query\\?\\: string, fragment\\?\\: string\\}\\.$#" + count: 2 + path: tests/bundle/Core/Routing/DefaultRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\JsRouting\\\\ExposedRoutesExtractorTest\\:\\:getDataForTestGetBaseUrl\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/bundle/Core/Routing/JsRouting/ExposedRoutesExtractorTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouterTest\\:\\:\\$container\\.$#" + count: 1 + path: tests/bundle/Core/Routing/UrlAliasRouterTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouter\\:\\:setConfigResolver\\(\\)\\.$#" + count: 1 + path: tests/bundle/Core/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouterTest\\:\\:resetConfigResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestDeactivatedUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestLocationCaseRedirectWithRootLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestLocationCaseRedirectWithRootRootLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestResourceCaseRedirectWithRootLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestVirtualCaseRedirectWithRootLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestWithRootLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestWithRootLocationAndExclusion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/Routing/UrlAliasRouterTest.php + + - + message: "#^Parameter \\#1 \\$configResolver of method Ibexa\\\\Bundle\\\\Core\\\\Routing\\\\UrlAliasRouter\\:\\:setConfigResolver\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/bundle/Core/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\SiteAccess\\\\MatcherBuilderTest\\:\\:testBuildMatcherNoService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/SiteAccess/MatcherBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\SiteAccess\\\\MatcherBuilderTest\\:\\:testBuildMatcherService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/SiteAccess/MatcherBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\SiteAccess\\\\MatcherBuilderTest\\:\\:testBuildMatcherServiceWrongInterface\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/SiteAccess/MatcherBuilderTest.php + + - + message: "#^Parameter \\#1 \\$siteAccessMatcherRegistry of class Ibexa\\\\Bundle\\\\Core\\\\SiteAccess\\\\MatcherBuilder constructor expects Ibexa\\\\Bundle\\\\Core\\\\SiteAccess\\\\SiteAccessMatcherRegistryInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 3 + path: tests/bundle/Core/SiteAccess/MatcherBuilderTest.php + + - + message: "#^Parameter \\#1 \\$matchers of class Ibexa\\\\Bundle\\\\Core\\\\SiteAccess\\\\SiteAccessMatcherRegistry constructor expects array\\<Ibexa\\\\Bundle\\\\Core\\\\SiteAccess\\\\Matcher\\>, array\\<int, Ibexa\\\\Bundle\\\\Core\\\\SiteAccess\\\\Matcher\\|string\\> given\\.$#" + count: 1 + path: tests/bundle/Core/SiteAccess/SiteAccessMatcherRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\URLChecker\\\\URLCheckerTest\\:\\:configureUrlHandlerRegistry\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/URLChecker/URLCheckerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\URLChecker\\\\URLCheckerTest\\:\\:configureUrlHandlerRegistry\\(\\) has parameter \\$schemes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/URLChecker/URLCheckerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\URLChecker\\\\URLCheckerTest\\:\\:createGroupedUrls\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/URLChecker/URLCheckerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\URLChecker\\\\URLCheckerTest\\:\\:createGroupedUrls\\(\\) has parameter \\$n with no type specified\\.$#" + count: 1 + path: tests/bundle/Core/URLChecker/URLCheckerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\URLChecker\\\\URLCheckerTest\\:\\:createGroupedUrls\\(\\) has parameter \\$schemes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/URLChecker/URLCheckerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\URLChecker\\\\URLCheckerTest\\:\\:createSearchResults\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/URLChecker/URLCheckerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\URLChecker\\\\URLCheckerTest\\:\\:createSearchResults\\(\\) has parameter \\$urls with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/bundle/Core/URLChecker/URLCheckerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\URLChecker\\\\URLCheckerTest\\:\\:testCheck\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/URLChecker/URLCheckerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Core\\\\URLChecker\\\\URLCheckerTest\\:\\:testCheckUnsupported\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Core/URLChecker/URLCheckerTest.php + + - + message: "#^Argument of an invalid type PHPUnit\\\\Framework\\\\MockObject\\\\MockObject supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollectorTest\\:\\:getDataCollectorMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollectorTest\\:\\:testAddGetCollector\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollectorTest\\:\\:testCollect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollectorTest\\:\\:testGetAllCollectors\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollectorTest\\:\\:testGetInvalidCollector\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollectorTest\\:\\:testGetPanelTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollectorTest\\:\\:testGetPanelTemplateNothing\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollectorTest\\:\\:testGetToolbarTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Debug\\\\Collector\\\\IbexaCoreCollectorTest\\:\\:testGetToolbarTemplateNothing\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php + + - + message: "#^PHPDoc tag @var above foreach loop does not specify variable name\\.$#" + count: 1 + path: tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\Debug\\\\DependencyInjection\\\\Compiler\\\\DataCollectorPassTest\\:\\:testAddCollector\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/Debug/DependencyInjection/Compiler/DataCollectorPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPassTest\\:\\:testBinarydataHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/Compiler/IOConfigurationPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPassTest\\:\\:testDefaultHandlers\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/Compiler/IOConfigurationPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPassTest\\:\\:testMetadataHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/Compiler/IOConfigurationPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPassTest\\:\\:testUnknownBinarydataHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/Compiler/IOConfigurationPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\Compiler\\\\IOConfigurationPassTest\\:\\:testUnknownMetadataHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/Compiler/IOConfigurationPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\BaseFlysystemTest\\:\\:provideHandlerConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactory/BaseFlysystemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\BaseFlysystemTest\\:\\:validateConfiguredContainer\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactory/BaseFlysystemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\BaseFlysystemTest\\:\\:validateConfiguredHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactory/BaseFlysystemTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\BaseFlysystemTest\\:\\:\\$filesystemServiceId has no type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactory/BaseFlysystemTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\BaseFlysystemTest\\:\\:\\$flysystemAdapterServiceId has no type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactory/BaseFlysystemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\BinarydataHandler\\\\FlysystemTest\\:\\:provideExpectedParentServiceId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactory/BinarydataHandler/FlysystemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\MetadataHandler\\\\FlysystemTest\\:\\:provideExpectedParentServiceId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactory/MetadataHandler/FlysystemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\MetadataHandler\\\\LegacyDFSClusterTest\\:\\:provideExpectedParentServiceId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactory/MetadataHandler/LegacyDFSClusterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\MetadataHandler\\\\LegacyDFSClusterTest\\:\\:provideHandlerConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactory/MetadataHandler/LegacyDFSClusterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactory\\\\MetadataHandler\\\\LegacyDFSClusterTest\\:\\:validateConfiguredHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactory/MetadataHandler/LegacyDFSClusterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactoryTest\\:\\:provideExpectedParentServiceId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactoryTest\\:\\:provideHandlerConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactoryTest\\:\\:provideHandlerConfiguration\\(\\) invoked with 1 parameter, 0 required\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactoryTest\\:\\:registerHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactoryTest\\:\\:registerHandler\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactoryTest\\:\\:testAddConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactoryTest\\:\\:testConfigureHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactoryTest\\:\\:testGetParentServiceId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactoryTest\\:\\:validateConfiguredContainer\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\ConfigurationFactoryTest\\:\\:validateConfiguredHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$container$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php + + - + message: "#^Cannot call method decorate\\(\\) on object\\|null\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/IbexaIOExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtensionTest\\:\\:testParametersWithBinarydataHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/IbexaIOExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtensionTest\\:\\:testParametersWithMetadataHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/IbexaIOExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\DependencyInjection\\\\IbexaIOExtensionTest\\:\\:testParametersWithoutConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/DependencyInjection/IbexaIOExtensionTest.php + + - + message: "#^Cannot call method getDate\\(\\) on Symfony\\\\Component\\\\HttpFoundation\\\\Response\\|null\\.$#" + count: 2 + path: tests/bundle/IO/EventListener/StreamFileListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:configureIoUrlPrefix\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/EventListener/StreamFileListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:configureIoUrlPrefix\\(\\) has parameter \\$urlPrefix with no type specified\\.$#" + count: 1 + path: tests/bundle/IO/EventListener/StreamFileListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:createEvent\\(\\) has parameter \\$request with no type specified\\.$#" + count: 1 + path: tests/bundle/IO/EventListener/StreamFileListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:createRequest\\(\\) has parameter \\$host with no type specified\\.$#" + count: 1 + path: tests/bundle/IO/EventListener/StreamFileListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:createRequest\\(\\) has parameter \\$semanticPath with no type specified\\.$#" + count: 1 + path: tests/bundle/IO/EventListener/StreamFileListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:testDoesNotRespondToNoIoRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/EventListener/StreamFileListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:testDoesNotRespondToNonIoUri\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/EventListener/StreamFileListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:testRespondsToIoRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/EventListener/StreamFileListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\EventListener\\\\StreamFileListenerTest\\:\\:testRespondsToIoUri\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/bundle/IO/EventListener/StreamFileListenerTest.php + + - + message: "#^Parameter \\#1 \\$date of method Symfony\\\\Component\\\\HttpFoundation\\\\Response\\:\\:setDate\\(\\) expects DateTimeInterface, DateTimeInterface\\|null given\\.$#" + count: 2 + path: tests/bundle/IO/EventListener/StreamFileListenerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\Flysystem\\:\\:expects\\(\\)\\.$#" + count: 2 + path: tests/bundle/IO/Migration/FileMigratorTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\Flysystem\\:\\:expects\\(\\)\\.$#" + count: 1 + path: tests/bundle/IO/Migration/FileMigratorTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\LegacyDFSCluster\\:\\:expects\\(\\)\\.$#" + count: 1 + path: tests/bundle/IO/Migration/FileMigratorTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\Migration\\\\FileMigratorTest\\:\\:\\$binaryFlysystemFrom \\(Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\Flysystem\\) does not accept Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/bundle/IO/Migration/FileMigratorTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Bundle\\\\IO\\\\Migration\\\\FileMigratorTest\\:\\:\\$binaryFlysystemTo \\(Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\Flysystem\\) does not accept Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/bundle/IO/Migration/FileMigratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\BaseCoreFieldTypeIntegrationTest\\:\\:getDatabaseConnection\\(\\) should return Doctrine\\\\DBAL\\\\Connection but returns object\\.$#" + count: 1 + path: tests/integration/Core/BaseCoreFieldTypeIntegrationTest.php + + - + message: "#^PHPDoc tag @return with type object is not subtype of native type Doctrine\\\\DBAL\\\\Connection\\.$#" + count: 1 + path: tests/integration/Core/BaseCoreFieldTypeIntegrationTest.php + + - + message: "#^Cannot call method match\\(\\) on object\\|null\\.$#" + count: 1 + path: tests/integration/Core/BasicKernelTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\BinaryBase\\\\BinaryBaseStorage\\\\BinaryBaseStorageTest\\:\\:getContext\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/BinaryBase/BinaryBaseStorage/BinaryBaseStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\BinaryBase\\\\BinaryBaseStorage\\\\BinaryBaseStorageTest\\:\\:providerOfFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/BinaryBase/BinaryBaseStorage/BinaryBaseStorageTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\BinaryBase\\\\BinaryBaseStorage\\\\BinaryBaseStorageTest\\:\\:\\$gateway \\(Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept Ibexa\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\\\Gateway\\.$#" + count: 1 + path: tests/integration/Core/BinaryBase/BinaryBaseStorage/BinaryBaseStorageTest.php + + - + message: "#^Cannot access property \\$id on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 4 + path: tests/integration/Core/FieldType/FieldConstraintsStorage/FieldConstraintsStorageTest.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:updateFieldDefinition\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: tests/integration/Core/FieldType/FieldConstraintsStorage/FieldConstraintsStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\FieldType\\\\FieldConstraintsStorage\\\\Stub\\\\ExampleFieldConstraintsStorage\\:\\:__construct\\(\\) has parameter \\$fieldConstraints with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/FieldType/FieldConstraintsStorage/Stub/ExampleFieldConstraintsStorage.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\FieldTypeConstraints\\[\\]\\)\\: Unexpected token \"\\\\n \", expected variable at offset 82$#" + count: 1 + path: tests/integration/Core/FieldType/FieldConstraintsStorage/Stub/ExampleFieldConstraintsStorage.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\IO\\\\BinarydataHandler\\\\FlysystemTest\\:\\:getBinaryDataHandler\\(\\) should return Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler but returns object\\|null\\.$#" + count: 1 + path: tests/integration/Core/IO/BinarydataHandler/FlysystemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\IO\\\\BinarydataHandler\\\\FlysystemTest\\:\\:getFlysystemFilesystem\\(\\) should return League\\\\Flysystem\\\\FilesystemOperator but returns object\\|null\\.$#" + count: 1 + path: tests/integration/Core/IO/BinarydataHandler/FlysystemTest.php + + - + message: "#^Parameter \\#1 \\$inputStream of method Ibexa\\\\Contracts\\\\Core\\\\IO\\\\BinaryFileCreateStruct\\:\\:setInputStream\\(\\) expects resource, resource\\|false given\\.$#" + count: 1 + path: tests/integration/Core/IO/BinarydataHandler/FlysystemTest.php + + - + message: "#^Generator expects value type array\\{Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Image\\\\ImageStorage\\\\Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\VersionInfo, Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Image\\\\ImageStorage\\\\Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\}, array\\{Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\VersionInfo, Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\} given\\.$#" + count: 1 + path: tests/integration/Core/Image/ImageStorage/ImageStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Image\\\\ImageStorage\\\\ImageStorageTest\\:\\:providerOfFieldData\\(\\) has invalid return type Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Image\\\\ImageStorage\\\\Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\.$#" + count: 1 + path: tests/integration/Core/Image/ImageStorage/ImageStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Image\\\\ImageStorage\\\\ImageStorageTest\\:\\:providerOfFieldData\\(\\) has invalid return type Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Image\\\\ImageStorage\\\\Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\VersionInfo\\.$#" + count: 1 + path: tests/integration/Core/Image/ImageStorage/ImageStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Limitation\\\\MemberOfLimitationTest\\:\\:testCanUserAssignRoleToUser\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Limitation/MemberOfLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Limitation\\\\MemberOfLimitationTest\\:\\:userPermissionLimitationProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Limitation/MemberOfLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Limitation\\\\PermissionResolver\\\\LanguageLimitationIntegrationTest\\:\\:providerForCanUserCreateContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Limitation\\\\PermissionResolver\\\\LanguageLimitationIntegrationTest\\:\\:providerForCanUserDeleteContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Limitation\\\\PermissionResolver\\\\LanguageLimitationIntegrationTest\\:\\:providerForCanUserDeleteContentTranslation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Limitation\\\\PermissionResolver\\\\LanguageLimitationIntegrationTest\\:\\:providerForCanUserEditOrPublishContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Limitation\\\\PermissionResolver\\\\LanguageLimitationIntegrationTest\\:\\:testCanUserCreateContent\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Limitation\\\\PermissionResolver\\\\LanguageLimitationIntegrationTest\\:\\:testCanUserEditContent\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Limitation\\\\PermissionResolver\\\\LanguageLimitationIntegrationTest\\:\\:testCanUserPublishContent\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Limitation\\\\RoleLimitationTest\\:\\:testCanUserAssignRole\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Limitation/RoleLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Limitation\\\\RoleLimitationTest\\:\\:userPermissionLimitationProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Limitation/RoleLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Persistence\\\\Search\\\\Content\\\\IndexerGatewayTest\\:\\:getDataForContentInSubtree\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Persistence/Search/Content/IndexerGatewayTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Persistence\\\\Search\\\\Content\\\\IndexerGatewayTest\\:\\:getDataForContentSince\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Persistence/Search/Content/IndexerGatewayTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Persistence\\\\Variation\\\\InMemoryVariationHandler\\:\\:getVariation\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Persistence/Variation/InMemoryVariationHandler.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseContentServiceTest\\:\\:createContentDraft\\(\\) has parameter \\$fieldValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseContentServiceTest\\:\\:createContentDraftVersion1\\(\\) has parameter \\$contentFieldNameIdentifier with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseContentServiceTest\\:\\:createContentDraftVersion1\\(\\) has parameter \\$contentTypeIdentifier with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseContentServiceTest\\:\\:createContentDraftVersion1\\(\\) has parameter \\$locationId with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseContentServiceTest\\:\\:createMultilingualContentDraft\\(\\) has parameter \\$multilingualFieldValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseContentServiceTest\\:\\:createUpdatedDraftVersion2NotAdmin\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$id of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserService\\:\\:loadUserGroup\\(\\) expects int, string given\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseNonRedundantFieldSetTest\\:\\:createMultilingualTestContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseNonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseNonRedundantFieldSetTest\\:\\:createTestContent\\(\\) has parameter \\$fieldValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseNonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseNonRedundantFieldSetTest\\:\\:createTestContentForUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseNonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseNonRedundantFieldSetTest\\:\\:updateTestContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseNonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseNonRedundantFieldSetTest\\:\\:updateTestContent\\(\\) has parameter \\$fieldValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseNonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseNonRedundantFieldSetTest\\:\\:updateTestContent\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseNonRedundantFieldSetTest.php + + - + message: "#^Argument of an invalid type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Call to method commit\\(\\) on an unknown class Ibexa\\\\Solr\\\\Handler\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:assertPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:assertPropertiesCorrectUnsorted\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:assertPropertiesEqual\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:assertPropertiesEqual\\(\\) has parameter \\$actualValue with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:assertPropertiesEqual\\(\\) has parameter \\$expectedValue with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:assertPropertiesEqual\\(\\) has parameter \\$propertyName with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:assertPropertiesEqual\\(\\) has parameter \\$sortArray with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:assertStructPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:assertStructPropertiesCorrect\\(\\) has parameter \\$additionalProperties with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:createFolder\\(\\) has parameter \\$names with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:createRoleWithPolicies\\(\\) has parameter \\$policiesData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:createRoleWithPolicies\\(\\) has parameter \\$roleName with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:createSimpleContentType\\(\\) has parameter \\$fieldsToDefine with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:createUserWithPolicies\\(\\) has parameter \\$policiesData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:getSetupFactory\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory but returns object\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:refreshSearch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:sortItems\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:sortItems\\(\\) has parameter \\$items with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:updateFolder\\(\\) has parameter \\$names with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$propertyNames$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^PHPDoc tag @var for variable \\$searchHandler contains unknown class Ibexa\\\\Solr\\\\Handler\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentCreateStruct\\:\\:\\$remoteId \\(string\\) does not accept string\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:\\$repository \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository\\) does not accept null\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:\\$setupFactory \\(Ibexa\\\\Contracts\\\\Core\\\\Test\\\\Repository\\\\SetupFactory\\) does not accept object\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTrashServiceTest\\:\\:createTrashItem\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItem but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItem\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseTrashServiceTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URL\\:\\:\\$url\\.$#" + count: 5 + path: tests/integration/Core/Repository/BaseURLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseURLServiceTest\\:\\:assertSearchResultItems\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseURLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseURLServiceTest\\:\\:assertSearchResultItems\\(\\) has parameter \\$expectedUrls with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseURLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseURLServiceTest\\:\\:assertSearchResultItems\\(\\) has parameter \\$ignoreOrder with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseURLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseURLServiceTest\\:\\:assertUsagesSearchResultItems\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseURLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseURLServiceTest\\:\\:assertUsagesSearchResultItems\\(\\) has parameter \\$expectedContentInfoIds with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseURLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseURLServiceTest\\:\\:doTestFindUrls\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseURLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseURLServiceTest\\:\\:doTestFindUrls\\(\\) has parameter \\$expectedUrls with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/BaseURLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BookmarkServiceTest\\:\\:testCreateBookmark\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BookmarkServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BookmarkServiceTest\\:\\:testCreateBookmarkThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BookmarkServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BookmarkServiceTest\\:\\:testDeleteBookmark\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BookmarkServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BookmarkServiceTest\\:\\:testDeleteBookmarkThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BookmarkServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BookmarkServiceTest\\:\\:testIsBookmarked\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BookmarkServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BookmarkServiceTest\\:\\:testIsNotBookmarked\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BookmarkServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BookmarkServiceTest\\:\\:testLoadBookmarks\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/BookmarkServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Common\\\\SlugConverter\\:\\:setConfigurationValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Common/SlugConverter.php + + - + message: "#^Variable \\$definition might not be defined\\.$#" + count: 1 + path: tests/integration/Core/Repository/Container/Compiler/SetAllServicesPublicPass.php + + - + message: "#^Cannot call method getValue\\(\\) on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentService/CopyNonTranslatableFieldsFromPublishedVersionTest.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:updateFieldDefinition\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentService/VersionValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testAddRelationThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testCopyContentThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testCopyContentThrowsUnauthorizedExceptionWithGivenVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testCopyContentToAuthorizedLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testCopyContentToAuthorizedLocationWithSubtreeLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testCountContentDraftsReturnZero\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testCreateContentDraftThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testCreateContentDraftThrowsUnauthorizedExceptionWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testCreateContentThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testCreateContentThrowsUnauthorizedExceptionWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testDeleteContentThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testDeleteRelationThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testDeleteVersionThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentByContentInfoThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentByContentInfoThrowsUnauthorizedExceptionWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentByContentInfoThrowsUnauthorizedExceptionWithThirdParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentByRemoteIdThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentByRemoteIdThrowsUnauthorizedExceptionWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentByRemoteIdThrowsUnauthorizedExceptionWithThirdParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentByVersionInfoThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentByVersionInfoThrowsUnauthorizedExceptionWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentDraftsThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentDraftsThrowsUnauthorizedExceptionWithUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentInfoByRemoteIdThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentInfoListSkipsUnauthorizedItems\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentInfoThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentThrowsUnauthorizedExceptionOnDrafts\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentThrowsUnauthorizedExceptionWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentThrowsUnauthorizedExceptionWithThirdParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadContentThrowsUnauthorizedExceptionsOnArchives\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadRelationsForDraftVersionThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadRelationsThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadRelationsWithUnauthorizedRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadReverseRelationsThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadVersionInfoByIdThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadVersionInfoByIdThrowsUnauthorizedExceptionForFirstDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadVersionInfoByIdThrowsUnauthorizedExceptionWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadVersionInfoThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadVersionInfoThrowsUnauthorizedExceptionWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testLoadVersionsThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testPublishVersionThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testSudo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testUpdateContentMetadataThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceAuthorizationTest\\:\\:testUpdateContentThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Parameter \\#1 \\$parentLocationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:newLocationCreateStruct\\(\\) expects int, int\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/ContentServiceAuthorizationTest.php + + - + message: "#^Access to an undefined property object\\:\\:\\$content\\.$#" + count: 6 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Access to an undefined property object\\:\\:\\$reverseRelations\\.$#" + count: 9 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Argument of an invalid type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentMetadataUpdateStruct supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>\\.$#" + count: 5 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\>\\.$#" + count: 12 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\>\\.$#" + count: 11 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset 1 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\>\\.$#" + count: 4 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset 1 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\>\\.$#" + count: 3 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset 2 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset 3 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset 4 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset 5 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\>\\.$#" + count: 2 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset mixed on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access offset mixed on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access property \\$sortField on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access property \\$sortOrder on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Cannot access property \\$sourceContentInfo on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\|null\\.$#" + count: 9 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:assertAliasesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:assertAliasesCorrect\\(\\) has parameter \\$actualAliases with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:assertAliasesCorrect\\(\\) has parameter \\$expectedAliasProperties with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:assertAllFieldsEquals\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:assertDefaultContentStates\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:assertExpectedRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:assertLocaleFieldsEquals\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:assertTranslationDoesNotExist\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:contentRemoteIdVersionLanguageProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:createUserWithVersionReadLimitations\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:filterHiddenLocations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:getExpectedMediaContentInfoProperties\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:getPrioritizedLanguageList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:providerForDeleteTranslationFromDraftRemovesUrlAliasOnPublishing\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testAddRelation\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\> but returns iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testAddRelationAddsRelationToContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testAddRelationSetsExpectedRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testAddRelationThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testChangeContentName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCopyContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCopyContentInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCopyContentInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCopyContentWithGivenVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCopyContentWithNewOwner\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCopyTranslationsFromInvalidPublishedContentToDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCopyTranslationsFromPublishedToDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftAndLoadAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftInOtherLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftLoadContentByContentInfoStillLoadsPublishedVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftLoadContentByRemoteIdStillLoadsPublishedVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftLoadContentStillLoadsPublishedVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftLoadVersionInfoStillLoadsPublishedVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftSetsContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftSetsExpectedProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftSetsVersionInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftWithRelations\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\> but returns iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftWithRelationsCreatesExpectedRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentDraftWithThirdParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentRequiredFieldMissing\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentSetsExpectedContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentSetsExpectedContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentSetsExpectedVersionInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentThrowsContentFieldValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentThrowsInvalidArgumentExceptionOnFieldTypeNotAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentThrowsInvalidArgumentExceptionWithLocationCreateParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentWithLocationCreateParameterCreatesExpectedLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentWithLocationCreateParameterCreatesExpectedLocation\\(\\) has parameter \\$testData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentWithLocationCreateParameterInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentWithLocationCreateParameterInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCreateContentWithRomanianSpecialCharsInTitle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testCustomURLAliasesNotHistorizedOnUpdatedContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteContentInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteContentInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteContentWithEmptyBinaryField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteRelation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteRelationThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteRelationThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationDeletesSingleTranslationVersions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationFromDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationFromDraftArchivesUrlAliasOnPublishing\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationFromDraftRemovesUrlAliasOnPublishing\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationFromDraftThrowsBadStateExceptionOnMainTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationFromDraftThrowsBadStateExceptionOnPublishedVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationFromDraftThrowsBadStateExceptionOnSingleTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationFromDraftThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationFromDraftThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationMainLanguageThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationUpdatesInitialLanguageCodeVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteTranslationUpdatesUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteVersionInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteVersionInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteVersionThrowsBadStateExceptionOnPublishedVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testDeleteVersionWorksIfOnlyVersionIsDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testHideContentWithParentLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadAllContentDrafts\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentByContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentByContentInfoThrowsNotFoundExceptionWithVersionNumberParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentByContentInfoWithLanguageParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentByContentInfoWithVersionNumberParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentByRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentByRemoteId\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentByRemoteIdThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentByRemoteIdThrowsNotFoundExceptionWithThirdParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentByRemoteIdWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentByRemoteIdWithThirdParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentByVersionInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentByVersionInfoWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentDraftListWithForUserWithLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentDraftListWithPaginationParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentDrafts\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentDraftsReturnsEmptyArrayByDefault\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentDraftsWithFirstParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentInfoByRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentInfoByRemoteIdSetsExpectedContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentInfoByRemoteIdThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentInfoList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentInfoListSkipsNotFoundItems\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentInfoSetsExpectedContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentInfoThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentListByContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentThrowsNotFoundExceptionWithThirdParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentWithPrioritizedLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentWithPrioritizedLanguagesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentWithPrioritizedLanguagesThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadContentWithThirdParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadRelationsSkipsArchivedContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadRelationsSkipsDraftContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadReverseRelationListSkipsDraftContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadReverseRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadReverseRelationsSkipsArchivedContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadReverseRelationsSkipsDraftContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionInfoById\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionInfoByIdSetsExpectedVersionInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionInfoByIdThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionInfoByIdThrowsNotFoundExceptionWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionInfoByIdWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionInfoByIdWithSecondParameterSetsExpectedVersionInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionInfoByIdWithSecondParameterSetsExpectedVersionInfo\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionInfoThrowsNotFoundExceptionWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionInfoWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersions\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\> but returns iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionsAfterDeletingTwoDrafts\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionsOfStatusArchived\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionsOfStatusDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testLoadVersionsSetsExpectedVersionInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testNewContentCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testNewContentMetadataUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testNewContentUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishContentWithoutPublishPolicyThrowsException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionCreatesLocationsDefinedOnCreate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionDoesNotChangePublishedDate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionFromContentDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionFromContentDraftArchivesOldVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionFromContentDraftUpdatesContentInfoCurrentVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionFromOldContentDraftArchivesNewerVersionNo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionNotCreatingUnlimitedArchives\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionSetsExpectedContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionSetsExpectedContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionSetsExpectedVersionInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishVersionWithSelectedLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testPublishWorkflow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testRevealContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testRevealContentWithHiddenChildren\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testRevealContentWithHiddenParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testURLAliasesCreatedForNewContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testURLAliasesCreatedForUpdatedContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentMetadataCheckWithinTransaction\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentMetadataInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentMetadataInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentMetadataNotUpdatesContentVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentMetadataSetsExpectedProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentMetadataThrowsInvalidArgumentExceptionOnDuplicateRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentMetadataThrowsInvalidArgumentExceptionOnNoMetadataPropertiesSet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentSetsExpectedFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentThrowsContentFieldValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentThrowsInvalidArgumentExceptionWhenFieldTypeDoesNotAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentValidatorIgnoresRequiredFieldsOfNotUpdatedLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentWhenMandatoryFieldIsEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdateContentWithNotUpdatingMandatoryField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:testUpdatingDraftDoesNotUpdateOldVersions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^PHPDoc tag @var for variable \\$field has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$fields of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:assertAllFieldsEquals\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\>, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$fields of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:assertLocaleFieldsEquals\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\>, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\> given\\.$#" + count: 2 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$fields of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:normalizeFields\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\>, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\> given\\.$#" + count: 4 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 12 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#1 \\$locations of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:filterHiddenLocations\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\> given\\.$#" + count: 5 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#2 \\$actualAliases of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentServiceTest\\:\\:assertAliasesCorrect\\(\\) expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\> given\\.$#" + count: 4 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#2 \\$code of class InvalidArgumentException constructor expects int, string given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:updateFieldDefinition\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testAddFieldDefinitionThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testAssignContentTypeGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testCopyContentTypeThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testCreateContentTypeDraftThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testCreateContentTypeGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testCreateContentTypeThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testDeleteContentTypeGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testDeleteContentTypeThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testPublishContentTypeDraftThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testRemoveFieldDefinitionThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testUnassignContentTypeGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testUpdateContentTypeDraftThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testUpdateContentTypeGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceAuthorizationTest\\:\\:testUpdateFieldDefinitionThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:removeFieldDefinition\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:updateFieldDefinition\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php + + - + message: "#^Argument of an invalid type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionCreateStruct supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Argument of an invalid type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionUpdateStruct supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Cannot access property \\$fieldSettings on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 2 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Cannot access property \\$id on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Cannot access property \\$id on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 4 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Cannot call method getDescriptions\\(\\) on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Cannot call method getNames\\(\\) on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null\\.$#" + count: 2 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:assertContentTypeGroupsCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:assertCopyContentTypeValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:assertCopyContentTypeValues\\(\\) has parameter \\$excludedProperties with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:getPrioritizedLanguageList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testAddFieldDefinition\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testAddFieldDefinitionAddsFieldToContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testAddFieldDefinitionAddsFieldToContentAdded\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testAddFieldDefinitionStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testAddFieldDefinitionStructValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testAddFieldDefinitionThrowsBadStateExceptionContentInstances\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testAddFieldDefinitionThrowsBadStateExceptionNonRepeatableField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testAddFieldDefinitionThrowsContentTypeFieldDefinitionValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testAddFieldDefinitionThrowsInvalidArgumentExceptionDuplicateFieldIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testAssignContentTypeGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testAssignContentTypeGroupInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testAssignContentTypeGroupInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testAssignContentTypeGroupThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCanLoadContentTypeDraftEvenIfDiffrentOwner\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCopyContentType\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCopyContentTypeInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCopyContentTypeInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCopyContentTypeStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCopyContentTypeStructValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCopyContentTypeWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentThrowsContentTypeValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeDraftStructLanguageDependentValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeDraftStructLanguageDependentValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeDraftStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeDraftStructValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeDraftThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeGroupInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeGroupInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeGroupStructLanguageDependentValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeGroupStructLanguageDependentValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeGroupStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeGroupStructValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeGroupThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeStructValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeThrowsContentTypeFieldDefinitionValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeThrowsInvalidArgumentExceptionDuplicateContentTypeIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeThrowsInvalidArgumentExceptionDuplicateFieldIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeThrowsInvalidArgumentExceptionDuplicateIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeThrowsInvalidArgumentExceptionDuplicateRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeThrowsInvalidArgumentExceptionGroupsEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testDeleteContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testDeleteContentTypeGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testDeleteContentTypeGroupThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testDeleteContentTypeGroupWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testDeleteContentTypeGroupWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testDeleteContentTypeInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testDeleteContentTypeInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testDeleteContentTypeThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testDeleteUserDrafts\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testIsContentTypeUsed\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeByIdentifierReturnsCorrectInstance\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeByIdentifierThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeByRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeByRemoteIdReturnsCorrectInstance\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeByRemoteIdReturnsCorrectInstance\\(\\) has parameter \\$contentType with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeByRemoteIdThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeDraftThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeDraftThrowsNotFoundExceptionIfDiffrentOwner\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeFieldDefinitions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeGroupByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeGroupByIdentifierStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeGroupByIdentifierThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeGroupStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeGroupThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeGroups\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeGroupsIdentifiers\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeGroupsIdentifiers\\(\\) has parameter \\$groups with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeStructValues\\(\\) has parameter \\$userGroupType with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypeWithPrioritizedLanguagesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypes\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypesContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testLoadContentTypesContent\\(\\) has parameter \\$types with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewContentTypeCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewContentTypeCreateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewContentTypeCreateStructValues\\(\\) has parameter \\$createStruct with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewContentTypeGroupCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewContentTypeGroupCreateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewContentTypeGroupCreateStructValues\\(\\) has parameter \\$createStruct with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewContentTypeGroupUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewContentTypeUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewContentTypeUpdateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewContentTypeUpdateStructValues\\(\\) has parameter \\$typeUpdate with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewFieldDefinitionCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewFieldDefinitionCreateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewFieldDefinitionCreateStructValues\\(\\) has parameter \\$createStruct with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewFieldDefinitionUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testNewFieldDefinitionUpdateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testPublishContentTypeDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testPublishContentTypeDraftRefreshesContentTypesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testPublishContentTypeDraftSetsNameSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testPublishContentTypeDraftThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testPublishContentTypeDraftThrowsInvalidArgumentExceptionWithoutFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testRemoveContentTypeTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testRemoveContentTypeTranslationWithMultilingualData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testRemoveFieldDefinition\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testRemoveFieldDefinitionRemoved\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testRemoveFieldDefinitionRemoved\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testRemoveFieldDefinitionRemovesFieldFromContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testRemoveFieldDefinitionRemovesFieldFromContentRemoved\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testRemoveFieldDefinitionThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testRemoveFieldDefinitionThrowsInvalidArgumentExceptionOnWrongDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUnassignContentTypeGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUnassignContentTypeGroupThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUnassignContentTypeGroupThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeDraftStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeDraftStructValues\\(\\) has parameter \\$data with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeDraftThrowsInvalidArgumentExceptionDuplicateIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeDraftThrowsInvalidArgumentExceptionDuplicateRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeDraftThrowsInvalidArgumentExceptionNoDraftForAuthenticatedUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeDraftWithNewTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeDraftWithNewTranslationWithMultilingualData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeGroupInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeGroupInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeGroupStructLanguageDependentValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeGroupStructLanguageDependentValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeGroupStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeGroupStructValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeGroupThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateFieldDefinition\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateFieldDefinitionStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateFieldDefinitionStructValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateFieldDefinitionThrowsInvalidArgumentExceptionFieldIdentifierExists\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateFieldDefinitionThrowsInvalidArgumentExceptionForUndefinedField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateFieldDefinitionWithEmptyStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ContentTypeServiceTest\\:\\:testUpdateFieldDefinitionWithNewTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Parameter \\#2 \\$actualObject of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:assertStructPropertiesCorrect\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Parameter \\#2 \\$array of function array_map expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\> given\\.$#" + count: 3 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Parameter \\#2 \\$array of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertArrayHasKey\\(\\) expects array\\|ArrayAccess, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Parameter \\#2 \\$array of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertArrayNotHasKey\\(\\) expects array\\|ArrayAccess, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:removeFieldDefinition\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 6 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:updateFieldDefinition\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 6 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Variable \\$nameValue in PHPDoc tag @var does not exist\\.$#" + count: 1 + path: tests/integration/Core/Repository/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:getAdditionallyIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:getAdditionallyIndexedMultivaluedFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:getFullTextIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\AuthorIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php + + - + message: "#^Cannot access property \\$value on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:addFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:createContent\\(\\) has parameter \\$contentType with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:createContentType\\(\\) has parameter \\$fieldCreateOverride with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:createContentType\\(\\) has parameter \\$typeCreateOverride with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:createMultilingualContent\\(\\) has parameter \\$fieldData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:createMultilingualContent\\(\\) has parameter \\$names with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:getOverride\\(\\) has parameter \\$overrideValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:getValidContentTypeConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:getValidFieldConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:getValidMultilingualFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:postCreationHook\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:removeFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testAddFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testContentTypeField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testContentTypeField\\(\\) has parameter \\$contentType with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCopiedExternalData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCopiedFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCopiedFieldType\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCopyField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCopyField\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCreateContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCreateContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCreateContentTypeFailsWithInvalidFieldSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCreateContentTypeFailsWithInvalidValidatorConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCreateContentWithEmptyFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCreatedEmptyFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCreatedEmptyFieldValue\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCreatedFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testCreatedFieldType\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testDeleteContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testDeleteContent\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testDeleteDraftOfPublishedContentDoesNotDeleteData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testDeleteTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testExceededVersionArchiveLimitHasNoEffectOnContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testFromHash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testFromHash\\(\\) has parameter \\$expectedValue with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testFromHash\\(\\) has parameter \\$hash with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testIsEmptyValue\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testIsNotEmptyValue\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testLoadContentTypeField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testLoadContentTypeFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testLoadContentTypeFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testLoadContentTypeFieldType\\(\\) has parameter \\$contentType with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testLoadEmptyFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testLoadEmptyFieldValueData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testLoadEmptyFieldValueData\\(\\) has parameter \\$field with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testLoadEmptyFieldValueType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testLoadEmptyFieldValueType\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testLoadExternalData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testLoadField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testLoadFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testPublishContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testPublishContentWithEmptyFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testPublishedFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testPublishedFieldType\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testPublishedName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testRemoveFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testSettingsSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testToHash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testToHash\\(\\) has parameter \\$expectedHash with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testToHash\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testUpdateContentFails\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testUpdateContentWithNewTranslationOnEmptyField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testUpdateField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testUpdateFieldNoNewContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testUpdateNoNewContentTypeFieldStillAvailable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testUpdateNoNewContentTypeFieldStillAvailable\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testUpdateTypeFieldStillAvailable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testUpdateTypeFieldStillAvailable\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testUpdatedDataCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testUpdatedNoNewContentDataCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:testValidatorSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^PHPDoc tag @param for parameter \\$repository with type Ibexa\\\\Contracts\\\\Core\\\\Repository is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$exception of method PHPUnit\\\\Framework\\\\TestCase\\:\\:expectException\\(\\) expects class\\-string\\<Throwable\\>, string given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$exception of method PHPUnit\\\\Framework\\\\TestCase\\:\\:expectException\\(\\) expects class\\-string\\<Throwable\\>, string\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$field of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:removeFieldDefinition\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Parameter \\$repository of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BaseIntegrationTest\\:\\:postCreationHook\\(\\) has invalid type Ibexa\\\\Contracts\\\\Core\\\\Repository\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:checkSearchEngineSupport\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:getAdditionallyIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:getFixtureData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:getStoragePrefix\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\BinaryFile\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:\\$loadedBinaryFilePath has no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php + + - + message: "#^Cannot access property \\$value on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:getDataForTestFindContentFieldCriterion\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\Checkbox\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$boolValue of class Ibexa\\\\Core\\\\FieldType\\\\Checkbox\\\\Value constructor expects bool, null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$boolValue of class Ibexa\\\\Core\\\\FieldType\\\\Checkbox\\\\Value constructor expects bool, stdClass given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:getAdditionallyIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:getAdditionallyIndexedMultivaluedFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:getFullTextIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\Country\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\CountryIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\DateAndTime\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:testUpdateContentFails\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\DateIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/DateIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:getFullTextIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\EmailAddress\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$email of class Ibexa\\\\Core\\\\FieldType\\\\EmailAddress\\\\Value constructor expects string, null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FileSearchBaseIntegrationTest\\:\\:cleanupStorageDir\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FileSearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FileSearchBaseIntegrationTest\\:\\:setUpIgnoredPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FileSearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FileSearchBaseIntegrationTest\\:\\:setUpIgnoredPath\\(\\) has parameter \\$ignoredFiles with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FileSearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FileSearchBaseIntegrationTest\\:\\:testUpdateWithRemove\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FileSearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FileSearchBaseIntegrationTest\\:\\:uriExistsOnIO\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FileSearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FileSearchBaseIntegrationTest\\:\\:uriExistsOnIO\\(\\) has parameter \\$uri with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FileSearchBaseIntegrationTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FileSearchBaseIntegrationTest\\:\\:\\$ignoredPathList type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FileSearchBaseIntegrationTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FileSearchBaseIntegrationTest\\:\\:\\$storageDirConfigKey has no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FileSearchBaseIntegrationTest.php + + - + message: "#^Static property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FileSearchBaseIntegrationTest\\:\\:\\$installDir \\(string\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FileSearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FloatIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FloatIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FloatIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FloatIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FloatIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FloatIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FloatIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FloatIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FloatIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FloatIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FloatIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\FloatIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:getFullTextIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\ISBN\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ISBNIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$isbn of class Ibexa\\\\Core\\\\FieldType\\\\ISBN\\\\Value constructor expects string, null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php + + - + message: "#^Cannot access property \\$alternativeText on Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php + + - + message: "#^Cannot access property \\$uri on Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:getAdditionallyIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:getFixtureData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:getStoragePrefix\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:publishNewImage\\(\\) has parameter \\$parentLocationIDs with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\ImageIntegrationTest\\:\\:\\$loadedImagePath has no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:getFullTextIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\Integer\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\IntegerIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$value of class Ibexa\\\\Core\\\\FieldType\\\\Integer\\\\Value constructor expects int\\|null, float given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php + + - + message: "#^Cannot access property \\$value on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 5 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:assertContentFieldHasCorrectData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:checkFullTextSupport\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:createContent\\(\\) has parameter \\$contentType with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:getFullTextIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:getValidMultilingualFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\Keyword\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:providerForTestTruncateField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:testFindContentFieldCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:testKeywordsAreCaseSensitive\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:testTruncateField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\KeywordIntegrationTest\\:\\:testUpdateContentKeywords\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\MapLocation\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:getAdditionallyIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:getFixtureData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:getStoragePrefix\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\Media\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\MediaIntegrationTest\\:\\:\\$loadedMediaPath has no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\Relation\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:testCopyContentCopiesFieldRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:testCreateContentRelationsProcessedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:testSubtreeCopyContentCopiesFieldRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:testUpdateContentRelationsProcessedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$relations of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:normalizeRelations\\(\\) expects array\\<Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\>, array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\> given\\.$#" + count: 5 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$relations of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationIntegrationTest\\:\\:normalizeRelations\\(\\) expects array\\<Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\>, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\> given\\.$#" + count: 5 + path: tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\RelationList\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:testCopyContentCopiesFieldRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:testCreateContentRelationsProcessedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:testSubtreeCopyContentCopiesFieldRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:testUpdateContentRelationsProcessedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$relations of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:normalizeRelations\\(\\) expects array\\<Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\>, array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\> given\\.$#" + count: 5 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$relations of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\RelationListIntegrationTest\\:\\:normalizeRelations\\(\\) expects array\\<Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\>, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Relation\\> given\\.$#" + count: 5 + path: tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:assertFindResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:assertFindResult\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:assertSortResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:checkCustomFieldsSupport\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:checkFullTextSupport\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:checkSearchEngineSupport\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:doModifyField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:findProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:fullTextFindProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:getAdditionallyIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:getFullTextIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:getResultContentIdList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:modifyFieldCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:modifyFieldSortClause\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:sortProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testCreateTestContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenOneTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenOneTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenOneTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenOneTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenOneTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenOneTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenOneTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenTwoOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenTwoOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenTwoOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenTwoOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenTwoOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenTwoOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindBetweenTwoOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindContainsTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindEqualsTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanOrEqualTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindGreaterThanTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOneTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOneTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOneTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOneTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOneTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOneTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInOneTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindInTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLikeTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanOrEqualTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindLowerThanTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenOneTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenOneTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenOneTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenOneTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenOneTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenOneTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenOneTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenTwoOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenTwoOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenTwoOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenTwoOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenTwoOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenTwoOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotBetweenTwoOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotContainsTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotEqualsTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqual\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqual\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqual\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqual\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqual\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqual\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqual\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqualTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqualTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqualTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqualTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqualTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqualTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanOrEqualTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotGreaterThanTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOneTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOneTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOneTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOneTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOneTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOneTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInOneTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotInTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLikeTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanOrEqualTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFindNotLowerThanTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFullTextFindOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFullTextFindOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFullTextFindOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFullTextFindOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFullTextFindOne\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFullTextFindOne\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFullTextFindTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFullTextFindTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFullTextFindTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFullTextFindTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFullTextFindTwo\\(\\) has parameter \\$valueOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testFullTextFindTwo\\(\\) has parameter \\$valueTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testSort\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testSort\\(\\) has parameter \\$ascending with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testSort\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testSort\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:testSort\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Parameter \\#2 \\$fieldName of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:doModifyField\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Parameter \\#2 \\$fieldName of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchBaseIntegrationTest\\:\\:doModifyField\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:checkOperatorSupport\\(\\) has parameter \\$operator with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:findMultivaluedProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:getAdditionallyIndexedMultivaluedFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testCreateMultivaluedTestContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenOneTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenOneTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenOneTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenOneTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenOneTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenOneTwo\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenOneTwo\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenTwoOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenTwoOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenTwoOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenTwoOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenTwoOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenTwoOne\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedBetweenTwoOne\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedContainsOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedContainsOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedContainsOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedContainsOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedContainsOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedContainsOne\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedContainsOne\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedEqualsOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedEqualsOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedEqualsOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedEqualsOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedEqualsOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedEqualsOne\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedEqualsOne\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsOneTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsOneTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsOneTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsOneTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsOneTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsOneTwo\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsOneTwo\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsTwo\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOneFindsTwo\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOrEqualOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOrEqualOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOrEqualOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOrEqualOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOrEqualOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOrEqualOne\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedGreaterThanOrEqualOne\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOne\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOne\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOneTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOneTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOneTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOneTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOneTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOneTwo\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedInOneTwo\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneEmpty\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneEmpty\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneEmpty\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneEmpty\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneEmpty\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneEmpty\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneFindsOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneFindsOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneFindsOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneFindsOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneFindsOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneFindsOne\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOneFindsOne\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOrEqualOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOrEqualOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOrEqualOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOrEqualOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOrEqualOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOrEqualOne\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedLowerThanOrEqualOne\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenOneTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenOneTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenOneTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenOneTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenOneTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenOneTwo\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenOneTwo\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenTwoOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenTwoOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenTwoOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenTwoOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenTwoOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenTwoOne\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotBetweenTwoOne\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotContainsOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotContainsOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotContainsOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotContainsOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotContainsOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotContainsOne\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotContainsOne\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotEqualsOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotEqualsOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotEqualsOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotEqualsOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotEqualsOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotEqualsOne\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotEqualsOne\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsOneTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsOneTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsOneTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsOneTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsOneTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsOneTwo\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsOneTwo\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsTwo\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOneFindsTwo\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOrEqual\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOrEqual\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOrEqual\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOrEqual\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOrEqual\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOrEqual\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotGreaterThanOrEqual\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOne\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOne\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOneTwo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOneTwo\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOneTwo\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOneTwo\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOneTwo\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOneTwo\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotInOneTwo\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneEmpty\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneEmpty\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneEmpty\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneEmpty\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneEmpty\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneEmpty\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneFindsOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneFindsOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneFindsOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneFindsOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneFindsOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneFindsOne\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOneFindsOne\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOrEqualOne\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOrEqualOne\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOrEqualOne\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOrEqualOne\\(\\) has parameter \\$filter with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOrEqualOne\\(\\) has parameter \\$modifyField with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOrEqualOne\\(\\) has parameter \\$valuesOne with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:testFindMultivaluedNotLowerThanOrEqualOne\\(\\) has parameter \\$valuesTwo with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SearchMultivaluedBaseIntegrationTest\\:\\:\\$legacyUnsupportedOperators type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:getAdditionallyIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:getAdditionallyIndexedMultivaluedFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:getFullTextIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\Selection\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionMultilingualIntegrationTest\\:\\:getAdditionallyIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionMultilingualIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionMultilingualIntegrationTest\\:\\:getAdditionallyIndexedMultivaluedFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionMultilingualIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionMultilingualIntegrationTest\\:\\:getFullTextIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionMultilingualIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionMultilingualIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionMultilingualIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\SelectionMultilingualIntegrationTest\\:\\:getValidFieldConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/SelectionMultilingualIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:getFullTextIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\TextBlock\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$text of class Ibexa\\\\Core\\\\FieldType\\\\TextBlock\\\\Value constructor expects string, int given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$text of class Ibexa\\\\Core\\\\FieldType\\\\TextBlock\\\\Value constructor expects string, null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:getFullTextIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\TextLine\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TextLineIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$text of class Ibexa\\\\Core\\\\FieldType\\\\TextLine\\\\Value constructor expects string, int given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$text of class Ibexa\\\\Core\\\\FieldType\\\\TextLine\\\\Value constructor expects string, null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TimeIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TimeIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TimeIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TimeIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TimeIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TimeIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TimeIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TimeIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TimeIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TimeIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TimeIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TimeIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\TimeIntegrationTest\\:\\:testUpdateContentFails\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:getAdditionallyIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:getFullTextIndexedFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:getValidUpdateFieldData\\(\\) should return array but returns Ibexa\\\\Core\\\\FieldType\\\\Url\\\\Value\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UrlIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$link of class Ibexa\\\\Core\\\\FieldType\\\\Url\\\\Value constructor expects string\\|null, int given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Parameter \\#2 \\$text of class Ibexa\\\\Core\\\\FieldType\\\\Url\\\\Value constructor expects string\\|null, int given\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\:\\:\\$content\\.$#" + count: 2 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:assertCopiedFieldDataLoadedCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:assertFieldDataLoadedCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:createContent\\(\\) has parameter \\$contentType with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:getSettingsSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:getValidatorSchema\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:provideFromHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:provideInvalidCreationFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:provideInvalidUpdateFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:provideToHashData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:providerForTestIsEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:providerForTestIsNotEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:testAddFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:testCopiedExternalData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:testCopiedFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:testCopiedFieldType\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:testCopyField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:testCopyField\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:testCreateContentWithEmptyFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:testRemoveFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldType\\\\UserIntegrationTest\\:\\:testUpdateFieldDefinitionWithIncompleteSettingsSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldType/UserIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldTypeServiceTest\\:\\:testGetFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldTypeServiceTest\\:\\:testGetFieldTypeThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldTypeServiceTest\\:\\:testGetFieldTypes\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldTypeServiceTest\\:\\:testHasFieldTypeReturnsFalse\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\FieldTypeServiceTest\\:\\:testHasFieldTypeReturnsTrue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/FieldTypeServiceTest.php + + - + message: "#^Cannot access property \\$pathString on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/BaseRepositoryFilteringTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\BaseRepositoryFilteringTestCase\\:\\:assertFoundContentItemsByRemoteIds\\(\\) has parameter \\$expectedContentRemoteIds with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/BaseRepositoryFilteringTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\BaseRepositoryFilteringTestCase\\:\\:assertFoundContentItemsByRemoteIds\\(\\) has parameter \\$list with no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/BaseRepositoryFilteringTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\BaseRepositoryFilteringTestCase\\:\\:find\\(\\) has parameter \\$contextLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/BaseRepositoryFilteringTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\BaseRepositoryFilteringTestCase\\:\\:find\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/BaseRepositoryFilteringTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\BaseRepositoryFilteringTestCase\\:\\:getFilterFactories\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/BaseRepositoryFilteringTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\BaseRepositoryFilteringTestCase\\:\\:getUserLimitationData\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/BaseRepositoryFilteringTestCase.php + + - + message: "#^Parameter \\#1 \\$value of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\ParentLocationId constructor expects array\\<int\\>\\|int, int\\|null given\\.$#" + count: 5 + path: tests/integration/Core/Repository/Filtering/BaseRepositoryFilteringTestCase.php + + - + message: "#^Call to an undefined method Traversable\\<mixed, mixed\\>\\:\\:offsetGet\\(\\)\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Cannot use array destructuring on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\ContentFilteringTest\\:\\:assertFoundContentItemsByRemoteIds\\(\\) has parameter \\$expectedContentRemoteIds with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\ContentFilteringTest\\:\\:assertFoundContentItemsByRemoteIds\\(\\) has parameter \\$list with no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\ContentFilteringTest\\:\\:createMultiplePagesOfContentItems\\(\\) should return int but returns int\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\ContentFilteringTest\\:\\:find\\(\\) has parameter \\$contextLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\ContentFilteringTest\\:\\:getDataForTestFindContentWithLocationCriterion\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\ContentFilteringTest\\:\\:getFilterFactories\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\ContentFilteringTest\\:\\:getListOfSupportedSortClauses\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Parameter \\#1 \\$sortClause of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\ContentFilteringTest\\:\\:performAndAssertSimpleSortClauseQuery\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringSortClause, object given\\.$#" + count: 2 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Parameter \\#1 \\$totalCount of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentList constructor expects int, int\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Parameter \\#1 \\$value of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\ParentLocationId constructor expects array\\<int\\>\\|int, int\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/ContentFilteringTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\LocationFilteringTest\\:\\:assertFoundContentItemsByRemoteIds\\(\\) has parameter \\$expectedContentRemoteIds with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/LocationFilteringTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\LocationFilteringTest\\:\\:assertFoundContentItemsByRemoteIds\\(\\) has parameter \\$list with no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/LocationFilteringTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Filtering\\\\LocationFilteringTest\\:\\:find\\(\\) has parameter \\$contextLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Filtering/LocationFilteringTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceAuthorizationTest\\:\\:testCreateLanguageThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceAuthorizationTest\\:\\:testDeleteLanguageThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceAuthorizationTest\\:\\:testDisableLanguageThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceAuthorizationTest\\:\\:testEnableLanguageThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceAuthorizationTest\\:\\:testUpdateLanguageNameThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceMaximumSupportedLanguagesTest\\:\\:testCreateMaximumLanguageLimit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceMaximumSupportedLanguagesTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceMaximumSupportedLanguagesTest\\:\\:\\$createdLanguages type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceMaximumSupportedLanguagesTest.php + + - + message: "#^Cannot access offset 'eng\\-NZ' on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Cannot access offset mixed on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:loadLanguagesReturnsAnEmptyArrayByDefault\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testCreateLanguageInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testCreateLanguageInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testCreateLanguageSetsExpectedProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testCreateLanguageSetsIdPropertyOnReturnedLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testCreateLanguageThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testDeleteLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testDeleteLanguageThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testDisableLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testEnableLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testGetDefaultLanguageCode\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testLoadLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testLoadLanguageById\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testLoadLanguageByIdThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testLoadLanguageThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testLoadLanguageThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testLoadLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testNewLanguageCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testUpdateLanguageName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testUpdateLanguageNameInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testUpdateLanguageNameInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LanguageServiceTest\\:\\:testUpdateLanguageNameThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Limitation\\\\PermissionResolver\\\\BaseLimitationIntegrationTest\\:\\:assertCanUser\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Limitation/PermissionResolver/BaseLimitationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Limitation\\\\PermissionResolver\\\\BaseLimitationIntegrationTest\\:\\:assertCanUser\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Limitation/PermissionResolver/BaseLimitationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Limitation\\\\PermissionResolver\\\\BaseLimitationIntegrationTest\\:\\:loginAsEditorUserWithLimitations\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Limitation/PermissionResolver/BaseLimitationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Limitation\\\\PermissionResolver\\\\ContentLimitationsMixIntegrationTest\\:\\:providerForCanUser\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Limitation/PermissionResolver/ContentLimitationsMixIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Limitation\\\\PermissionResolver\\\\ContentLimitationsMixIntegrationTest\\:\\:testCanUser\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Limitation/PermissionResolver/ContentLimitationsMixIntegrationTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Limitation/PermissionResolver/ContentLimitationsMixIntegrationTest.php + + - + message: "#^Cannot access property \\$contentInfo on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItem\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/Limitation/PermissionResolver/LocationLimitationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Limitation\\\\PermissionResolver\\\\LocationLimitationIntegrationTest\\:\\:providerForCanUserEditOrPublishContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Limitation/PermissionResolver/LocationLimitationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Limitation\\\\PermissionResolver\\\\LocationLimitationIntegrationTest\\:\\:testCanUserEditContent\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Limitation/PermissionResolver/LocationLimitationIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Limitation\\\\PermissionResolver\\\\LocationLimitationIntegrationTest\\:\\:testCanUserReadTrashedContent\\(\\) has parameter \\$limitations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Limitation/PermissionResolver/LocationLimitationIntegrationTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceAuthorizationTest\\:\\:testCopySubtreeThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceAuthorizationTest\\:\\:testCreateLocationThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceAuthorizationTest\\:\\:testCreateLocationThrowsUnauthorizedExceptionDueToLackOfContentManageLocationsPolicy\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceAuthorizationTest\\:\\:testDeleteLocationThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceAuthorizationTest\\:\\:testDeleteLocationWithSubtreeThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceAuthorizationTest\\:\\:testHideLocationThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceAuthorizationTest\\:\\:testLoadLocationByRemoteIdThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceAuthorizationTest\\:\\:testLoadLocationThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceAuthorizationTest\\:\\:testLoadLocationsNoAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceAuthorizationTest\\:\\:testMoveSubtreeThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceAuthorizationTest\\:\\:testSwapLocationThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceAuthorizationTest\\:\\:testUnhideLocationThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceAuthorizationTest\\:\\:testUpdateLocationThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Parameter \\#1 \\$parentLocationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:newLocationCreateStruct\\(\\) expects int, string given\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceAuthorizationTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Cannot access offset 1 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Cannot access offset 5 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>\\.$#" + count: 2 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Cannot access offset int\\|false on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Cannot access property \\$id on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 11 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Cannot call method getContentInfo\\(\\) on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 2 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:assertAliasesBeforeCopy\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:assertAliasesBeforeCopy\\(\\) has parameter \\$expectedSubItemAliases with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:assertDefaultContentStates\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:assertGeneratedAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:assertGeneratedAliases\\(\\) has parameter \\$expectedAliases with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:assertSubtreeProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:assertSubtreeProperties\\(\\) has parameter \\$expectedValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:assertSubtreeProperties\\(\\) has parameter \\$stopId with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:dataProviderForOutOfRangeLocationPriority\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:loadLocationProperties\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:loadSubtreeProperties\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:loadSubtreeProperties\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:providerForLoadLocationChildrenRespectsParentSortingClauses\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testBookmarksAreSwappedAfterSwapLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCopySubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCopySubtreeIncrementsChildCountOfNewParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCopySubtreeThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCopySubtreeUpdatesSubtreeProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCopySubtreeWithAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCreateLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCreateLocationInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCreateLocationStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCreateLocationStructValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCreateLocationThrowsInvalidArgumentExceptionContentAlreadyBelowParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCreateLocationThrowsInvalidArgumentExceptionParentIsSubLocationOfContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCreateLocationThrowsInvalidArgumentExceptionPriorityIsOutOfRange\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCreateLocationThrowsInvalidArgumentExceptionPriorityIsOutOfRange\\(\\) has parameter \\$priority with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testCreateLocationThrowsInvalidArgumentExceptionRemoteIdExists\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testDeleteContentObjectLastLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testDeleteLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testDeleteLocationDecrementsChildCountOnParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testDeleteLocationDeletesRelatedBookmarks\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testGetLocationChildCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testGetSubtreeSize\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testHideLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationByRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationByRemoteIdThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationChildren\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationChildrenData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationChildrenRespectsParentSortingClauses\\(\\) has parameter \\$expectedChildrenNames with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationListInCorrectOrder\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationListWithRootLocationId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationPrioritizedLanguagesFallback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationRootStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationsContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationsContent\\(\\) has parameter \\$locations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationsLimitedSubtreeContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationsThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadLocationsThrowsBadStateExceptionLimitedSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadParentLocationsForDraftContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testLoadParentLocationsForDraftContentThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testMoveInvisibleSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testMoveSubtreeDecrementsChildCountOfOldParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testMoveSubtreeIncrementsChildCountOfNewParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testMoveSubtreeUpdatesSubtreeProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testMoveSubtreeUpdatesSubtreePropertiesHidden\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testNewLocationCreateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testNewLocationUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testSwapLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testSwapLocationDoesNotCorruptSearchResults\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testSwapLocationForMainAndSecondaryLocation\\(\\) should return array\\<int\\> but returns array\\<int, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testSwapLocationUpdatesMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testUnhideLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testUnhideLocationNotUnhidesHiddenSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testUpdateLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testUpdateLocationStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testUpdateLocationStructValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testUpdateLocationThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testUpdateLocationThrowsInvalidArgumentExceptionPriorityIsOutOfRange\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testUpdateLocationThrowsInvalidArgumentExceptionPriorityIsOutOfRange\\(\\) has parameter \\$priority with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testUpdateLocationTwice\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:testUpdateLocationWithSameRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#1 \\$array of function array_column expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#1 \\$array of function array_column expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#1 \\$expectedLocations of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:assertContentHasExpectedLocations\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>, array\\<int, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#1 \\$location of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:getSubtreeSize\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#1 \\$location of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:moveSubtree\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null given\\.$#" + count: 3 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 23 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#1 \\$parentLocationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:newLocationCreateStruct\\(\\) expects int, int\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#2 \\$actualObject of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\BaseTest\\:\\:assertPropertiesCorrect\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#2 \\$array of function array_map expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\> given\\.$#" + count: 3 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#2 \\$location2 of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:swapLocation\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#2 \\$newParentLocation of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:moveSubtree\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#2 \\$parentLocationId of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\LocationServiceTest\\:\\:publishContentWithParentLocation\\(\\) expects int, int\\|null given\\.$#" + count: 8 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Parameter \\#2 \\$targetParentLocation of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:copySubtree\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|false given\\.$#" + count: 1 + path: tests/integration/Core/Repository/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:assertFieldIds\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:mapFields\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testCreateContentDefaultValuesFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testCreateContentDefaultValuesNoStructFieldsFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testCreateContentDraftFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testCreateContentDraftFieldsRetainsIds\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testCreateContentEmptyValuesFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testCreateContentEmptyValuesTranslationNotStoredFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testCreateContentTwoLanguagesMainTranslationStoredFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testCreateContentTwoLanguagesNoValuesForMainLanguageFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testCreateContentTwoLanguagesSecondTranslationNotStoredFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testUpdateContentUpdatingNonTranslatableFieldUpdatesFieldCopyFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testUpdateContentWithNewLanguageFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testUpdateContentWithNewLanguageNoValuesFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testUpdateContentWithNewLanguageVariantFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:testUpdateContentWithTwoLanguagesInitialLanguageTranslationNotCreatedFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Parameter \\#1 \\$fields of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NonRedundantFieldSetTest\\:\\:mapFields\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\>, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\> given\\.$#" + count: 2 + path: tests/integration/Core/Repository/NonRedundantFieldSetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NotificationServiceTest\\:\\:testCreateNotification\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NotificationServiceTest\\:\\:testCreateNotificationThrowsInvalidArgumentExceptionOnMissingOwner\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NotificationServiceTest\\:\\:testCreateNotificationThrowsInvalidArgumentExceptionOnMissingType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NotificationServiceTest\\:\\:testDeleteNotification\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NotificationServiceTest\\:\\:testGetNotification\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NotificationServiceTest\\:\\:testGetNotificationCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NotificationServiceTest\\:\\:testGetPendingNotificationCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NotificationServiceTest\\:\\:testLoadNotifications\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\NotificationServiceTest\\:\\:testMarkNotificationAsRead\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceAuthorizationTest\\:\\:testCreateObjectStateGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceAuthorizationTest\\:\\:testCreateObjectStateThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceAuthorizationTest\\:\\:testDeleteObjectStateGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceAuthorizationTest\\:\\:testDeleteObjectStateThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceAuthorizationTest\\:\\:testSetContentStateThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceAuthorizationTest\\:\\:testSetPriorityOfObjectStateThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceAuthorizationTest\\:\\:testUpdateObjectStateGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceAuthorizationTest\\:\\:testUpdateObjectStateThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceAuthorizationTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectState\\:\\:\\$defaultLanguageCode\\.$#" + count: 2 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectState\\:\\:\\$names\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Cannot access property \\$id on bool\\.$#" + count: 2 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:assertObjectsLoadedByIdentifiers\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:assertObjectsLoadedByIdentifiers\\(\\) has parameter \\$expectedIdentifiers with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:assertObjectsLoadedByIdentifiers\\(\\) has parameter \\$loadedObjects with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:createObjectState\\(\\) has parameter \\$descriptions with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:createObjectState\\(\\) has parameter \\$names with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:createObjectStateGroups\\(\\) should return array\\<bool\\> but returns array\\<int, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroup\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:deleteExistingObjectStateGroups\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:getGroupIdentifierMap\\(\\) has parameter \\$groups with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:getGroupIdentifierMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:getPrioritizedLanguagesList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testCreateObjectState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testCreateObjectStateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testCreateObjectStateGroupStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testCreateObjectStateGroupThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testCreateObjectStateInEmptyGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testCreateObjectStateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testCreateObjectStateStructValues\\(\\) has parameter \\$testData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testCreateObjectStateThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testDeleteObjectState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testDeleteObjectStateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testGetContentCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testGetContentState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testGetInitialObjectState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testLoadObjectState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testLoadObjectStateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testLoadObjectStateGroupThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testLoadObjectStateGroupWithPrioritizedLanguagesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testLoadObjectStateGroups\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testLoadObjectStateGroupsWithOffset\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testLoadObjectStateGroupsWithOffsetAndLimit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testLoadObjectStateGroupsWithPrioritizedLanguagesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testLoadObjectStateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testLoadObjectStateThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testLoadObjectStateWithPrioritizedLanguagesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testLoadObjectStates\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testLoadObjectStatesWithPrioritizedLanguagesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testNewObjectStateCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testNewObjectStateCreateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testNewObjectStateGroupCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testNewObjectStateGroupCreateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testNewObjectStateGroupUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testNewObjectStateGroupUpdateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testNewObjectStateUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testNewObjectStateUpdateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testSetContentState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testSetContentStateThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testSetPriorityOfObjectState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testUpdateObjectState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testUpdateObjectStateChosenFieldsOnly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testUpdateObjectStateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testUpdateObjectStateGroupChosenFieldsOnly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testUpdateObjectStateGroupStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testUpdateObjectStateGroupStructValues\\(\\) has parameter \\$testData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testUpdateObjectStateGroupThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testUpdateObjectStateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testUpdateObjectStateStructValues\\(\\) has parameter \\$testData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:testUpdateObjectStateThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Parameter \\#1 \\$groups of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\ObjectStateServiceTest\\:\\:getGroupIdentifierMap\\(\\) expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroup\\> given\\.$#" + count: 2 + path: tests/integration/Core/Repository/ObjectStateServiceTest.php + + - + message: "#^Parameter \\#1 \\$execution of class Jenner\\\\SimpleFork\\\\Process constructor expects string\\|null, Closure given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Parallel/BaseParallelTestCase.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\LookupLimitationResult\\:\\:\\$hasAccess\\.$#" + count: 3 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\LookupLimitationResult\\:\\:\\$lookupPolicyLimitations\\.$#" + count: 8 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\LookupLimitationResult\\:\\:\\$roleLimitations\\.$#" + count: 3 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\LookupPolicyLimitations\\:\\:\\$policy\\.$#" + count: 3 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Cannot access offset 1 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\>\\.$#" + count: 3 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Cannot access property \\$text on Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 2 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:getDataForTestCanUserWithLimitations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testCanUserForAdministratorUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testCanUserForAnonymousUserNo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testCanUserThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testCanUserThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testCanUserWithLimitationNo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testCanUserWithLimitationYes\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testCanUserWithLimitations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testCanUserWithLimitations\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testCanUserWithMultipleTargetsNo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testCanUserWithMultipleTargetsYes\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testCanUserWithTargetNo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testCanUserWithTargetThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testCanUserWithTargetYes\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testGetCurrentUserReferenceReturnsAnonymousUserReference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testHasAccessForCurrentUserNo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testHasAccessForCurrentUserYes\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testHasAccessLimited\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testHasAccessWithAdministratorUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testHasAccessWithAnonymousUserNo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\PermissionResolverTest\\:\\:testSetCurrentUserReference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Parameter \\#1 \\$policy of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\LookupPolicyLimitations constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\|false given\\.$#" + count: 1 + path: tests/integration/Core/Repository/PermissionResolverTest.php + + - + message: "#^Class Ibexa\\\\Tests\\\\Solr\\\\SetupFactory\\\\LegacySetupFactory not found\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP20018LanguageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP20018LanguageTest\\:\\:testSearchOnNotExistingLanguageGivesException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP20018LanguageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP20018LanguageTest\\:\\:testSearchOnNotUsedInstalledLanguageGivesNoResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP20018LanguageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP20018LanguageTest\\:\\:testSearchOnStandardLanguageGivesManyResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP20018LanguageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP20018LanguageTest\\:\\:testSearchOnUsedLanguageGivesOneResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP20018LanguageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP20018ObjectStateTest\\:\\:testSearchForNonUsedObjectState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP20018ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP20018ObjectStateTest\\:\\:testSearchForUsedObjectState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP20018ObjectStateTest.php + + - + message: "#^Parameter \\#1 \\$expectedCount of method PHPUnit\\\\Framework\\\\Assert\\:\\:assertCount\\(\\) expects int, int\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/Regression/EZP20018ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP20018VisibilityTest\\:\\:testSearchForHiddenContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP20018VisibilityTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP20018VisibilityTest\\:\\:testSearchForVisibleContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP20018VisibilityTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21069Test\\:\\:testSearchOnCurrentAttributeContentGivesOnesResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21069Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21069Test\\:\\:testSearchOnDraftAttributeContentGivesNoResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21069Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21069Test\\:\\:testSearchOnPreviousAttributeContentGivesNoResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21069Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21089Test\\:\\:testCreateContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21089Test.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21089Test\\:\\:\\$contentType \\(Ibexa\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentType\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21089Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21109EzIntegerTest\\:\\:deleteTestContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21109EzIntegerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21109EzIntegerTest\\:\\:testEzIntegerWithDefaultValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21109EzIntegerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21109EzIntegerTest\\:\\:testEzIntegerWithDefaultValues\\(\\) has parameter \\$integerValue with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21109EzIntegerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21109EzIntegerTest\\:\\:validIntegerValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21109EzIntegerTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\VersionInfo\\:\\:\\$names\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21771EzStringTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21771EzStringTest\\:\\:test11NumbersOnEzString\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21771EzStringTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21798Test\\:\\:testRoleChanges\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21798Test.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>\\.$#" + count: 2 + path: tests/integration/Core/Repository/Regression/EZP21906SearchOneContentMultipleLocationsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21906SearchOneContentMultipleLocationsTest\\:\\:searchContentQueryProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21906SearchOneContentMultipleLocationsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21906SearchOneContentMultipleLocationsTest\\:\\:testSearchContentMultipleLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21906SearchOneContentMultipleLocationsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP21906SearchOneContentMultipleLocationsTest\\:\\:testSearchContentMultipleLocations\\(\\) has parameter \\$expectedResultCount with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP21906SearchOneContentMultipleLocationsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22408DeleteRelatedObjectTest\\:\\:createReferenceObject\\(\\) has parameter \\$relationListTarget with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22408DeleteRelatedObjectTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22408DeleteRelatedObjectTest\\:\\:createTestContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22408DeleteRelatedObjectTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22408DeleteRelatedObjectTest\\:\\:getMainLanguageCode\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22408DeleteRelatedObjectTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22408DeleteRelatedObjectTest\\:\\:testRelationListIsUpdatedWhenRelatedObjectIsDeleted\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22408DeleteRelatedObjectTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22408DeleteRelatedObjectTest\\:\\:testSingleRelationIsUpdatedWhenRelatedObjectIsDeleted\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22408DeleteRelatedObjectTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22409RelationListTypeStateTest\\:\\:createContentWithRelationList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22409RelationListTypeStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22409RelationListTypeStateTest\\:\\:testCreateObjectWithRelationToContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22409RelationListTypeStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22409RelationListTypeStateTest\\:\\:testCreateObjectWithRelationToContentTypeWithExistingDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22409RelationListTypeStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22612URLAliasTranslations\\:\\:getFolderCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22612URLAliasTranslations.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22612URLAliasTranslations\\:\\:getFolderCreateStruct\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22612URLAliasTranslations.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22612URLAliasTranslations\\:\\:testURLAliasLoadedInRightLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22612URLAliasTranslations.php + + - + message: "#^Parameter \\#1 \\$parentLocationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:newLocationCreateStruct\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22612URLAliasTranslations.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22840RoleLimitations\\:\\:testSectionRoleAssignLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22840RoleLimitations.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22840RoleLimitations\\:\\:testSubtreeRoleAssignLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22840RoleLimitations.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22958SearchSubtreePathstringFormatTest\\:\\:searchContentQueryProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22958SearchSubtreePathstringFormatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22958SearchSubtreePathstringFormatTest\\:\\:searchContentQueryWithInvalidDataProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22958SearchSubtreePathstringFormatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22958SearchSubtreePathstringFormatTest\\:\\:testSearchContentSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22958SearchSubtreePathstringFormatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22958SearchSubtreePathstringFormatTest\\:\\:testSearchContentSubtree\\(\\) has parameter \\$pathString with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22958SearchSubtreePathstringFormatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22958SearchSubtreePathstringFormatTest\\:\\:testSearchContentSubtreeShouldThrowException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22958SearchSubtreePathstringFormatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP22958SearchSubtreePathstringFormatTest\\:\\:testSearchContentSubtreeShouldThrowException\\(\\) has parameter \\$pathString with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP22958SearchSubtreePathstringFormatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP26327UrlAliasHistorizationTest\\:\\:testHistorization\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP26327UrlAliasHistorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP26367UrlAliasHistoryRedirectLoopTest\\:\\:testLookupHistoryUrlReturnsActiveAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP26367UrlAliasHistoryRedirectLoopTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP26367UrlAliasHistoryRedirectLoopTest\\:\\:testReverseLookupReturnsHistoryAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP26367UrlAliasHistoryRedirectLoopTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 4 + path: tests/integration/Core/Repository/Regression/EZP26367UrlAliasHistoryRedirectLoopTest.php + + - + message: "#^Parameter \\#1 \\$parentLocationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:newLocationCreateStruct\\(\\) expects int, int\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/Regression/EZP26367UrlAliasHistoryRedirectLoopTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP26551DeleteContentTypeDraftTest\\:\\:testDeleteContentTypeGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP26551DeleteContentTypeDraftTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP28799SubtreeSearchTest\\:\\:testConflictingConditions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP28799SubtreeSearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EZP28799SubtreeSearchTest\\:\\:testNegativeSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EZP28799SubtreeSearchTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/Regression/EZP28799SubtreeSearchTest.php + + - + message: "#^Parameter \\#1 \\$parentLocationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:newLocationCreateStruct\\(\\) expects int, int\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/Regression/EZP28799SubtreeSearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\EnvTest\\:\\:testVerifyCacheDriver\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/EnvTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\PureNegativeQueryTest\\:\\:providerForTestMatchAll\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/PureNegativeQueryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\PureNegativeQueryTest\\:\\:testMatchAllContentInfoFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/PureNegativeQueryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\PureNegativeQueryTest\\:\\:testMatchAllContentInfoQuery\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/PureNegativeQueryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\PureNegativeQueryTest\\:\\:testMatchAllLocationFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/PureNegativeQueryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Regression\\\\PureNegativeQueryTest\\:\\:testMatchAllLocationQuery\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Regression/PureNegativeQueryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testCommitThrowsRuntimeException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetContentLanguageService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetContentService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetContentTypeService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetFieldTypeService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetLocationService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetNotificationService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetObjectStateService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetPermissionResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetRepository\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetRoleService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetSearchService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetSectionService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetTrashService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetURLAliasService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetURLWildcardService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testGetUserService\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RepositoryTest\\:\\:testRollbackThrowsRuntimeException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RepositoryTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupRoleAssignment\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserRoleAssignment\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testAssignRoleToUserGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testAssignRoleToUserGroupThrowsUnauthorizedExceptionWithRoleLimitationParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testAssignRoleToUserThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testAssignRoleToUserThrowsUnauthorizedExceptionWithRoleLimitationParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testCreateRoleThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testDeletePolicyThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testDeleteRoleThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testGetRoleAssignmentsForUserGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testGetRoleAssignmentsForUserLoadsEmptyListForAnonymousUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testGetRoleAssignmentsForUserWithSubtreeLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testGetRoleAssignmentsThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testLoadRoleByIdentifierThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testLoadRoleThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testLoadRolesForUserWithSubtreeLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testLoadRolesLoadsEmptyListForAnonymousUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testRemovePolicyThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testRemoveRoleAssignmentFromUserGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testRemoveRoleAssignmentThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceAuthorizationTest\\:\\:testUpdatePolicyByRoleDraftThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Parameter \\#2 \\$policy of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService\\:\\:updatePolicyByRoleDraft\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\|false given\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Parameter \\#2 \\$policyDraft of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService\\:\\:removePolicyByRoleDraft\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\|false given\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceAuthorizationTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupRoleAssignment\\:\\:\\$id\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleAssignment\\>\\.$#" + count: 4 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupRoleAssignment\\>\\.$#" + count: 4 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupRoleAssignment\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserRoleAssignment\\>\\.$#" + count: 2 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Cannot access property \\$limitation on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleAssignment\\|false\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Cannot call method getIdentifier\\(\\) on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\RoleLimitation\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService\\:\\:newRoleUpdateStruct\\(\\) invoked with 1 parameter, 0 required\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:providerForCopyRoleTests\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAddPolicyByRoleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAddPolicyByRoleDraftSetsPolicyProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAddPolicyByRoleDraftSetsPolicyProperties\\(\\) has parameter \\$roleAndPolicy with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAddPolicyByRoleDraftThrowsLimitationValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAddPolicyByRoleDraftUpdatesRole\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAddPolicyWithRoleAssignment\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAssignRoleToUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAssignRoleToUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAssignRoleToUserGroupAffectsRoleAssignmentsForUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAssignRoleToUserGroupThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAssignRoleToUserGroupWithRoleLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAssignRoleToUserGroupWithRoleLimitationThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAssignRoleToUserGroupWithRoleLimitationThrowsLimitationValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAssignRoleToUserThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAssignRoleToUserWithRoleLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAssignRoleToUserWithRoleLimitationThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testAssignRoleToUserWithRoleLimitationThrowsLimitationValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testCreateRole\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testCreateRoleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testCreateRoleDraftInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testCreateRoleDraftThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testCreateRoleDraftWithAddPolicy\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testCreateRoleInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testCreateRoleThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testCreateRoleThrowsLimitationValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testCreateRoleWithAddPolicy\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testCreateRoleWithMultiplePolicies\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testCreateRoleWithPolicy\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testDeleteRole\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testDeleteRoleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testGetRoleAssignments\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleAssignment\\> but returns array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleAssignment\\>\\|\\(ArrayAccess&iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleAssignment\\>\\)\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testGetRoleAssignmentsContainExpectedLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testGetRoleAssignmentsForUserDirect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testGetRoleAssignmentsForUserEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testGetRoleAssignmentsForUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testGetRoleAssignmentsForUserInherited\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testLoadPoliciesByUserId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testLoadRole\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testLoadRoleByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testLoadRoleByIdentifierThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testLoadRoleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testLoadRoleDraftByRoleId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testLoadRoleDraftByRoleIdThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testLoadRoleDraftThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testLoadRoleThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testLoadRoles\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testLoadRolesReturnsExpectedSetOfDefaultRoles\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testNewPolicyCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testNewPolicyCreateStructSetsStructProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testNewPolicyUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testNewRoleCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testNewRoleCreateStructSetsNamePropertyOnStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testNewRoleUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testPublishRoleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testPublishRoleDraftAddPolicies\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testRemovePolicyByRoleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testRemoveRoleAssignment\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testRemoveRoleAssignmentFromUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testRoleCreateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testRoleCreateStructValues\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testRoleCreateStructValuesWithPolicy\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testRoleCreateStructValuesWithPolicy\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testUnassignRoleByAssignment\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testUnassignRoleByAssignmentThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testUnassignRoleByAssignmentThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testUpdatePolicyByRoleDraft\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testUpdatePolicyByRoleDraftNoLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testUpdatePolicyByRoleDraftThrowsLimitationValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testUpdatePolicyUpdatesLimitations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testUpdatePolicyUpdatesLimitations\\(\\) has parameter \\$roleAndPolicy with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testUpdatePolicyUpdatesRole\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testUpdateRoleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\RoleServiceTest\\:\\:testUpdateRoleDraftThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Parameter \\#2 \\$policy of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService\\:\\:updatePolicyByRoleDraft\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft\\|null given\\.$#" + count: 3 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Parameter \\#2 \\$policyDraft of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService\\:\\:removePolicyByRoleDraft\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy given\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Variable \\$role might not be defined\\.$#" + count: 1 + path: tests/integration/Core/Repository/RoleServiceTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" + count: 4 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$hidden\\.$#" + count: 2 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" + count: 17 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$invisible\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$versionInfo\\.$#" + count: 2 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\:\\:\\$mainLocationId\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\:\\:\\$publishedDate\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\:\\:\\$remoteId\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:assertContentIdSearch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:assertSubtreeInvisibleProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:createContent\\(\\) has parameter \\$parentLocationIdList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:createContentWithNameAndDescription\\(\\) has parameter \\$contentDescription with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:createContentWithNameAndDescription\\(\\) has parameter \\$parentLocationIdList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:getEmailAddressesCases\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:getSpecialFullTextCases\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testAssignSection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testCopyContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testCopySubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testCreateLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testCreateUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testCreateUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testDeleteContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testDeleteLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testDeleteTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testDeleteUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testDeleteUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testDeleteVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testFindContentInfoFullTextIsNotSearchable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testFindContentInfoFullTextIsSearchable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testFindLocationsFullTextIsNotSearchable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testFindLocationsFullTextIsSearchable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testHideSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testIndexContentWithNullField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testIndexingSpecialFullTextCases\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testMoveSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testPublishVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testRecoverLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testRemovedContentFieldValueIsNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testRevealSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testSetContentState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testSwapLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testUpdateContentDraftMetadataIsNotIndexed\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testUpdateContentMetadata\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testUpdateLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testUpdateUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:testUserFullTextSearch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$ignoreForSetupFactories$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 4 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Parameter \\#1 \\$value of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\LocationId constructor expects array\\<int\\>\\|int, int\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Parameter \\#1 \\$value of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\ParentLocationId constructor expects array\\<int\\>\\|int, int\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Parameter \\#2 \\$fieldDefinition of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:updateFieldDefinition\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinition\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Parameter \\#2 \\$parentLocationIdList of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchEngineIndexingTest\\:\\:createContentWithName\\(\\) expects array\\<int\\>, array\\<int, int\\|null\\> given\\.$#" + count: 2 + path: tests/integration/Core/Repository/SearchEngineIndexingTest.php + + - + message: "#^Cannot call method first\\(\\) on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\AggregationResultCollection\\|null\\.$#" + count: 2 + path: tests/integration/Core/Repository/SearchService/Aggregation/AbstractAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\AbstractAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/AbstractAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\AbstractAggregationTest\\:\\:dataProviderForTestFindLocationWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/AbstractAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\ContentTypeGroupTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/ContentTypeGroupTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\ContentTypeTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/ContentTypeTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\DataSetBuilder\\\\TermAggregationDataSetBuilder\\:\\:build\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/DataSetBuilder/TermAggregationDataSetBuilder.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\DataSetBuilder\\\\TermAggregationDataSetBuilder\\:\\:setExpectedEntries\\(\\) has parameter \\$entries with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/DataSetBuilder/TermAggregationDataSetBuilder.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\DataSetBuilder\\\\TermAggregationDataSetBuilder\\:\\:\\$entries type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/DataSetBuilder/TermAggregationDataSetBuilder.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\DateMetadataRangeAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/DateMetadataRangeAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\Field\\\\AuthorTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/Field/AuthorTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\Field\\\\CheckboxTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/Field/CheckboxTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\Field\\\\CountryTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/Field/CountryTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\Field\\\\DateRangeAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/Field/DateRangeAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\Field\\\\DateTimeRangeAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/Field/DateTimeRangeAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\Field\\\\FloatRangeAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/Field/FloatRangeAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\Field\\\\FloatStatsAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/Field/FloatStatsAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\Field\\\\IntegerRangeAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/Field/IntegerRangeAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\Field\\\\IntegerStatsAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/Field/IntegerStatsAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\Field\\\\KeywordTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/Field/KeywordTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\Field\\\\SelectionTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/Field/SelectionTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\Field\\\\TimeRangeAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/Field/TimeRangeAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\FixtureGenerator\\\\FieldAggregationFixtureGenerator\\:\\:createFieldAggregationFixtures\\(\\) has parameter \\$values with no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\FixtureGenerator\\\\FieldAggregationFixtureGenerator\\:\\:setValues\\(\\) has parameter \\$values with no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php + + - + message: "#^Parameter \\#1 \\$contentTypeIdentifier of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\FixtureGenerator\\\\FieldAggregationFixtureGenerator\\:\\:createContentTypeForFieldAggregation\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php + + - + message: "#^Parameter \\#2 \\$fieldDefinitionIdentifier of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\FixtureGenerator\\\\FieldAggregationFixtureGenerator\\:\\:createContentTypeForFieldAggregation\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php + + - + message: "#^Parameter \\#2 \\$fieldDefinitionIdentifier of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\FixtureGenerator\\\\FieldAggregationFixtureGenerator\\:\\:createFieldAggregationFixtures\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php + + - + message: "#^Parameter \\#3 \\$fieldTypeIdentifier of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\FixtureGenerator\\\\FieldAggregationFixtureGenerator\\:\\:createContentTypeForFieldAggregation\\(\\) expects string, string\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php + + - + message: "#^Parameter \\#3 \\$values of method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\FixtureGenerator\\\\FieldAggregationFixtureGenerator\\:\\:createFieldAggregationFixtures\\(\\) expects iterable, iterable\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\FixtureGenerator\\\\FieldAggregationFixtureGenerator\\:\\:\\$values type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\LanguageTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/LanguageTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\LocationChildrenTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/LocationChildrenTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\ObjectStateTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/ObjectStateTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\RawRangeAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/RawRangeAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\RawStatsAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/RawStatsAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\RawTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/RawTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\SectionTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/SectionTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\SubtreeTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/SubtreeTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\UserMetadataTermAggregationTest\\:\\:createGroupTermAggregationDataSet\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/UserMetadataTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\UserMetadataTermAggregationTest\\:\\:createModifierTermAggregationDataSet\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/UserMetadataTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\UserMetadataTermAggregationTest\\:\\:createOwnerTermAggregationDataSet\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/UserMetadataTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\UserMetadataTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/UserMetadataTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\UserMetadataTermAggregationTest\\:\\:dataProviderForTestFindLocationWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/UserMetadataTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\Aggregation\\\\VisibilityTermAggregationTest\\:\\:dataProviderForTestFindContentWithAggregation\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/Aggregation/VisibilityTermAggregationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\DeleteTranslationTest\\:\\:createTestContentWithLanguages\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/DeleteTranslationTest.php + + - + message: "#^Parameter \\#1 \\$array of function array_column expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/MultilingualContentSearchIndexingTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/RemoteIdIndexingTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/RemoteIdIndexingTest.php + + - + message: "#^Cannot access offset 0 on array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/RemoteIdIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\RemoteIdIndexingTest\\:\\:getRemoteIDs\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/RemoteIdIndexingTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\RemoteIdIndexingTest\\:\\:providerForTestIndexingRemoteId\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/RemoteIdIndexingTest.php + + - + message: "#^Part \\$criterion\\-\\>value \\(array\\<bool\\|float\\|int\\|string\\>\\|bool\\|float\\|int\\|string\\) of encapsed string cannot be cast to string\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/RemoteIdIndexingTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$remoteId\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/AbstractSortClauseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\AbstractSortClauseTest\\:\\:assertSearchResultOrderByRemoteId\\(\\) has parameter \\$expectedOrderedIds with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/AbstractSortClauseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\ContentTranslatedNameTest\\:\\:createContentForContentTranslatedNameTesting\\(\\) has parameter \\$values with no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/ContentTranslatedNameTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\ContentTranslatedNameTest\\:\\:dataProviderForTestSortingByContentTranslatedName\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/ContentTranslatedNameTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\ContentTranslatedNameTest\\:\\:testContentSortingByContentTranslatedName\\(\\) has parameter \\$expectedOrderedRemoteIds with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/ContentTranslatedNameTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\ContentTranslatedNameTest\\:\\:testContentSortingByContentTranslatedName\\(\\) has parameter \\$inputValues with no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/ContentTranslatedNameTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\ContentTranslatedNameTest\\:\\:testContentSortingByContentTranslatedName\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/ContentTranslatedNameTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\ContentTranslatedNameTest\\:\\:testLocationSortingByContentTranslatedName\\(\\) has parameter \\$expectedOrderedRemoteIds with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/ContentTranslatedNameTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\ContentTranslatedNameTest\\:\\:testLocationSortingByContentTranslatedName\\(\\) has parameter \\$inputValues with no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/ContentTranslatedNameTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\ContentTranslatedNameTest\\:\\:testLocationSortingByContentTranslatedName\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/ContentTranslatedNameTest.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$values$#" + count: 2 + path: tests/integration/Core/Repository/SearchService/SortClause/ContentTranslatedNameTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\ScoreTest\\:\\:dataProviderForTestSortingByScore\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/ScoreTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\ScoreTest\\:\\:testSortingByScore\\(\\) has parameter \\$expectedOrderedIds with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/ScoreTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchService\\\\SortClause\\\\ScoreTest\\:\\:testSortingByScore\\(\\) has parameter \\$inputValues with no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/ScoreTest.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$values$#" + count: 1 + path: tests/integration/Core/Repository/SearchService/SortClause/ScoreTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentInfo\\.$#" + count: 2 + path: tests/integration/Core/Repository/SearchServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceAuthorizationTest\\:\\:testFindContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceAuthorizationTest\\:\\:testFindContentEmptyResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceAuthorizationTest\\:\\:testFindContentWithUserPermissionFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceAuthorizationTest\\:\\:testFindSingleThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceAuthorizationTest\\:\\:testFindSingleThrowsNotFoundExceptionWithUserPermissionFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceAuthorizationTest\\:\\:testFindSingleWithUserPermissionFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:assertFulltextSearch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:assertFulltextSearch\\(\\) has parameter \\$expectedKeys with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:assertFulltextSearch\\(\\) has parameter \\$idMap with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:doTestFulltextContentSearch\\(\\) has parameter \\$expectedKeys with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:doTestFulltextContentSearch\\(\\) has parameter \\$idMap with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:doTestFulltextLocationSearch\\(\\) has parameter \\$expectedKeys with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:doTestFulltextLocationSearch\\(\\) has parameter \\$idMap with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:doTestFulltextLocationSearch\\(\\) has parameter \\$searchString with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:mapKeysToIds\\(\\) has parameter \\$expectedKeys with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:mapKeysToIds\\(\\) has parameter \\$idMap with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:mapKeysToIds\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:mapSearchResultToIds\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:providerForTestFulltextSearchSolr7\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:testFulltextContentSearchSolr7\\(\\) has parameter \\$expectedKeys with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:testFulltextContentSearchSolr7\\(\\) has parameter \\$idMap with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:testFulltextLocationSearchSolr7\\(\\) has parameter \\$expectedKeys with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:testFulltextLocationSearchSolr7\\(\\) has parameter \\$idMap with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:testFulltextLocationSearchSolr7\\(\\) has parameter \\$searchString with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceFulltextTest\\:\\:testPrepareContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^PHPDoc tag @var for variable \\$keyGroup has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceFulltextTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" + count: 19 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\:\\:setCustomField\\(\\)\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Cannot access offset 'id' on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Cannot access offset 'title' on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Class Ibexa\\\\Tests\\\\Solr\\\\SetupFactory\\\\LegacySetupFactory not found\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:assertQueryFixture\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:assertQueryFixture\\(\\) has parameter \\$ignoreScore with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:getFacetedSearches\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:mapResultLocationIds\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:simplifySearchResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testFieldCollectionContains\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testFieldCollectionContainsNoMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testFieldIsEmptyInLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testFieldIsNotEmptyInLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testFindFacetedLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testFindFacetedLocation\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testFindLocationsWithNonSearchableField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testInvalidFieldIdentifierIn\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testInvalidFieldIdentifierRange\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testMapLocationDistanceBetween\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testMapLocationDistanceGreaterThanOrEqual\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testMapLocationDistanceLessThanOrEqual\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testMapLocationDistanceSortAscending\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testMapLocationDistanceSortDescending\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testMapLocationDistanceWithCustomField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testMapLocationDistanceWithCustomFieldSort\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testQueryCustomField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testQueryModifiedField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceLocationTest\\:\\:testVisibilityCriterionWithHiddenContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Parameter \\#1 \\$parentLocationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:newLocationCreateStruct\\(\\) expects int, int\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\:\\:\\$valueObject \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\) does not accept array\\<string, int\\|string\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceLocationTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$contentId\\.$#" + count: 4 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" + count: 36 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\:\\:setCustomField\\(\\)\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Cannot access offset 'id' on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\.$#" + count: 3 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Cannot access offset 'title' on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Cannot access property \\$valueObject on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\|null\\.$#" + count: 3 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\ContentTypeIdentifier constructor invoked with 2 parameters, 1 required\\.$#" + count: 2 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Class Ibexa\\\\Tests\\\\Solr\\\\SetupFactory\\\\LegacySetupFactory not found\\.$#" + count: 5 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Else branch is unreachable because previous condition is always true\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:assertMultilingualFieldFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:assertMultilingualFieldFilter\\(\\) has parameter \\$contentDataList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:assertMultilingualFieldFilter\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:assertMultilingualFieldFilter\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:assertMultilingualFieldSort\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:assertMultilingualFieldSort\\(\\) has parameter \\$contentDataList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:assertMultilingualFieldSort\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:assertMultilingualFieldSort\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:createContentForTestUserMetadataGroupHorizontal\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:createContentWithFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:find\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:getCaseInsensitiveSearches\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:getContentQuerySearches\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:getFacetedSearches\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:getFilterContentSearches\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:getLocationQuerySearches\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:getRelationFieldFilterSearches\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:getSeedsForRandomSortClause\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:getSortedContentSearches\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:getSortedLocationSearches\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:mapResultContentIds\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:providerForTestMultilingualFieldFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:providerForTestMultilingualFieldSort\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:providerForTestSortingByNumericFieldsWithValuesOfDifferentLength\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:simplifySearchResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:skipIfSeedNotImplemented\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:sortSearchHitsById\\(\\) has parameter \\$searchHits with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testContentWithMultipleLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testDeprecatedCriteriaProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFieldCollectionContains\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFieldCollectionContainsNoMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFieldCriterionForContentsWithIdenticalFieldIdentifiers\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFieldIsNotEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortContent\\(\\) has parameter \\$closure with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortContent\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortContent\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortContentInfo\\(\\) has parameter \\$closure with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortContentInfo\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortContentInfo\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortContentLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortContentLocations\\(\\) has parameter \\$closure with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortContentLocations\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortContentLocations\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortLocations\\(\\) has parameter \\$closure with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortLocations\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindAndSortLocations\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindContentFieldFiltersCaseSensitivity\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindContentFieldFiltersCaseSensitivity\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindContentFiltered\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindContentFiltered\\(\\) has parameter \\$closure with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindContentFiltered\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindContentFiltered\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindContentInfoFiltered\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindContentInfoFiltered\\(\\) has parameter \\$closure with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindContentInfoFiltered\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindContentInfoFiltered\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindContentWithLanguageFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindContentWithNonSearchableField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindFacetedContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindFacetedContent\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindFacetedContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindFacetedContentInfo\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindLocationsContentFiltered\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindLocationsContentFiltered\\(\\) has parameter \\$closure with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindLocationsContentFiltered\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindLocationsContentFiltered\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindLocationsFieldFiltersCaseSensitivity\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindLocationsFieldFiltersCaseSensitivity\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindLocationsNoPerformCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindLocationsNoPerformCountException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindNoPerformCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindNoPerformCountException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindNonMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindRelationFieldContentInfoFiltered\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindRelationFieldContentInfoFiltered\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindRelationFieldContentInfoFiltered\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindRelationFieldLocationsFiltered\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindRelationFieldLocationsFiltered\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindRelationFieldLocationsFiltered\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindSingle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindSingleFailMultiple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFindSingleWithNonSearchableField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFullTextOnNewContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFulltextComplex\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFulltextContentSearchComplex\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFulltextContentSearchComplex\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFulltextContentTranslationSearch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFulltextContentTranslationSearch\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFulltextLocationSearchComplex\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFulltextLocationSearchComplex\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testFulltextLocationTranslationSearch\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testInvalidFieldIdentifierIn\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testInvalidFieldIdentifierRange\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testLanguageAnalysisSameContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testLanguageAnalysisSameContentNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testLanguageAnalysisSeparateContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMapLocationDistanceBetween\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMapLocationDistanceBetweenPolar\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMapLocationDistanceGreaterThanOrEqual\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMapLocationDistanceLessThanOrEqual\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMapLocationDistanceSortAscending\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMapLocationDistanceSortDescending\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMapLocationDistanceWithCustomField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMapLocationDistanceWithCustomFieldSort\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldFilterContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldFilterContent\\(\\) has parameter \\$contentDataList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldFilterContent\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldFilterContent\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldFilterLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldFilterLocation\\(\\) has parameter \\$contentDataList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldFilterLocation\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldFilterLocation\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldSortContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldSortContent\\(\\) has parameter \\$contentDataList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldSortContent\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldSortContent\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldSortLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldSortLocation\\(\\) has parameter \\$contentDataList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldSortLocation\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testMultilingualFieldSortLocation\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryContent\\(\\) has parameter \\$closure with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryContent\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryContent\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryContentInfo\\(\\) has parameter \\$closure with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryContentInfo\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryContentInfo\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryContentLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryContentLocations\\(\\) has parameter \\$closure with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryContentLocations\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryContentLocations\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryCustomField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryLocations\\(\\) has parameter \\$closure with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryLocations\\(\\) has parameter \\$fixture with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryLocations\\(\\) has parameter \\$queryData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testQueryModifiedField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testRandomSortContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testRandomSortLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testRelationContentCreation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testSortFieldWithNonSearchableField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testSortMainLocationAscending\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testSortMainLocationDescending\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testSortMapLocationDistanceWithNonSearchableField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testSortModifiedField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testUserMetadataGroupHorizontalFilterContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testUserMetadataGroupHorizontalFilterContent\\(\\) has parameter \\$queryType with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testUserMetadataGroupHorizontalFilterLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testUserMetadataGroupHorizontalFilterLocation\\(\\) has parameter \\$queryType with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testUserMetadataGroupHorizontalQueryContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTest\\:\\:testUserMetadataGroupHorizontalQueryLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Parameter \\#1 \\$expectedCount of method PHPUnit\\\\Framework\\\\Assert\\:\\:assertCount\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Parameter \\#1 \\$parentLocationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:newLocationCreateStruct\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Search\\\\SearchHit\\:\\:\\$valueObject \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\) does not accept array\\<string, int\\|string\\>\\.$#" + count: 2 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 3 + path: tests/integration/Core/Repository/SearchServiceTest.php + + - + message: "#^Class Ibexa\\\\Tests\\\\Solr\\\\SetupFactory\\\\LegacySetupFactory not found\\.$#" + count: 3 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:assertIndexName\\(\\) has parameter \\$indexMap with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:createContent\\(\\) has parameter \\$parentLocationIds with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:createContent\\(\\) has parameter \\$searchValuesMap with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:createTestContent\\(\\) has parameter \\$parentLocationIds with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:createTestContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:getIndexName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:getIndexName\\(\\) has parameter \\$indexMap with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:getIndexesToMatchData\\(\\) has parameter \\$inputContentData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:getIndexesToMatchData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:getSetupType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:providerForTestFind\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:testCreateTestContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:testFindContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:testFindContent\\(\\) has parameter \\$contentDataList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:testFindContent\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:testFindContent\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:testFindLocationsMultiple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:testFindLocationsMultiple\\(\\) has parameter \\$contentDataList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:testFindLocationsMultiple\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:testFindLocationsMultiple\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:testFindLocationsSingle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:testFindLocationsSingle\\(\\) has parameter \\$contentDataList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:testFindLocationsSingle\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SearchServiceTranslationLanguageFallbackTest\\:\\:testFindLocationsSingle\\(\\) has parameter \\$languageSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceAuthorizationTest\\:\\:testAssignSectionThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceAuthorizationTest\\:\\:testCreateSectionThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceAuthorizationTest\\:\\:testDeleteSectionThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceAuthorizationTest\\:\\:testLoadSectionByIdentifierThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceAuthorizationTest\\:\\:testLoadSectionFiltersSections\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceAuthorizationTest\\:\\:testLoadSectionThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceAuthorizationTest\\:\\:testLoadSectionsLoadsEmptyListForAnonymousUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceAuthorizationTest\\:\\:testUpdateSectionThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testAssignSection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testAssignSectionToSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testCountAssignedContents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testCountAssignedContentsReturnsZeroByDefault\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testCreateSection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testCreateSectionForUserWithSectionLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testCreateSectionInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testCreateSectionInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testCreateSectionThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testDeleteSection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testDeleteSectionThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testDeleteSectionThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testIsSectionUsed\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testIsSectionUsedReturnsZeroByDefault\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testLoadSection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testLoadSectionByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testLoadSectionByIdentifierThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testLoadSectionThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testLoadSections\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testLoadSectionsReturnsDefaultSectionsByDefault\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testNewSectionCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testNewSectionUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testUpdateSection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testUpdateSectionForUserWithSectionLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testUpdateSectionInTransactionWithCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testUpdateSectionInTransactionWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testUpdateSectionKeepsSectionIdentifierOnNameUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testUpdateSectionKeepsSectionNameOnIdentifierUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testUpdateSectionThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SectionServiceTest\\:\\:testUpdateSectionWithSectionIdentifierOnNameUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SettingServiceTest\\:\\:dataProviderForCreateSetting\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/SettingServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\SettingServiceTest\\:\\:testCreateSetting\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/SettingServiceTest.php + + - + message: "#^Generator expects value type array\\{string, int, string\\|null, int\\|null\\}, array\\{'foo', 100, null\\} given\\.$#" + count: 1 + path: tests/integration/Core/Repository/TokenServiceTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceAuthorizationTest\\:\\:testDeleteTrashItemThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceAuthorizationTest\\:\\:testEmptyTrashThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceAuthorizationTest\\:\\:testLoadTrashItemThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceAuthorizationTest\\:\\:testRecoverThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceAuthorizationTest\\:\\:testRecoverThrowsUnauthorizedExceptionWithNewParentLocationParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceAuthorizationTest\\:\\:testTrashRequiresContentRemovePolicy\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceAuthorizationTest\\:\\:testTrashRequiresPremissionsToRemoveAllSubitems\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceAuthorizationTest\\:\\:testTrashThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceAuthorizationTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItem\\:\\:\\$trashed\\.$#" + count: 4 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Cannot access property \\$id on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItem\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Cannot access property \\$remoteId on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItem\\|null\\.$#" + count: 4 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Cannot call method getRemovedLocationContentIdMap\\(\\) on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItem\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:assertAliasNotExists\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testAliasesForRemovedItems\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testDeleteThrowsNotFoundExceptionForNonExistingTrashItem\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testDeleteTrashItem\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testEmptyTrash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testEmptyTrashForUserWithSubtreeLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testFindTrashItems\\(\\) has parameter \\$filters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testFindTrashItemsLimitedAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testFindTrashItemsLimits\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testFindTrashItemsSort\\(\\) has parameter \\$sortClausesClasses with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testFindTrashItemsSubtreeLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testLoadTrashItem\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testLoadTrashItemThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testNotFoundAliasAfterRemoveIt\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testRecover\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testRecoverDoesNotRestoreChildLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testRecoverIncrementsChildCountOnOriginalParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testRecoverThrowsNotFoundExceptionForNonExistingTrashItem\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testRecoverToNonExistingLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testRecoverWithLocationCreateStructParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testRecoverWithLocationCreateStructParameterIncrementsChildCountOnNewParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testTrash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testTrashDecrementsChildCountOnParentLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testTrashRemovesChildLocationsFromMainStorage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testTrashRemovesLocationFromMainStorage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testTrashReturnsNull\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testTrashSetsExpectedTrashItemProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:testTrashUpdatesMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:trashFiltersProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\TrashServiceTest\\:\\:trashSortClausesProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 6 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Parameter \\#1 \\$trashItem of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\TrashService\\:\\:recover\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItem, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItem\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\:\\:\\$sortClauses \\(array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\SortClause\\>\\) does not accept array\\<object\\>\\.$#" + count: 2 + path: tests/integration/Core/Repository/TrashServiceTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasService/CustomUrlAliasForMultilingualContentTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasService/UrlAliasLookupTest.php + + - + message: "#^Cannot access property \\$id on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasService/UrlAliasLookupTest.php + + - + message: "#^Parameter \\#1 \\$location of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\URLAliasService\\:\\:listLocationAliases\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasService/UrlAliasLookupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceAuthorizationTest\\:\\:testCreateGlobalUrlAliasThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceAuthorizationTest\\:\\:testCreateUrlAliasThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceAuthorizationTest\\:\\:testRemoveAliasesThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceAuthorizationTest.php + + - + message: "#^Parameter \\#1 \\$aliasList of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\URLAliasService\\:\\:removeAliases\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\>, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceAuthorizationTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceAuthorizationTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\>\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 3 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:assertLookupHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:assertUrlAliasPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:assertUrlAliasPropertiesCorrect\\(\\) has parameter \\$expectedIsHistory with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:assertUrlAliasPropertiesCorrect\\(\\) has parameter \\$expectedLanguageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:assertUrlAliasPropertiesCorrect\\(\\) has parameter \\$expectedPath with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:assertUrlAliasPropertiesSame\\(\\) has parameter \\$expectedValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:assertUrlIsCurrent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:assertUrlIsHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:changeContentTypeUrlAliasSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:changeSlugConverterConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:createGlobalAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasForLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasForLocationPropertyValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasForLocationPropertyValues\\(\\) has parameter \\$testData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasForLocationVariation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasForLocationVariationPropertyValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasForLocationVariationPropertyValues\\(\\) has parameter \\$testData with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasPropertyValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasWithAlwaysAvailable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasWithAlwaysAvailablePropertyValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasWithForward\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasWithForwardPropertyValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateNonLatinNonEmptyUniqueAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateSameAliasForDifferentLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateUrlAliasPropertyValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateUrlAliasPropertyValues\\(\\) has parameter \\$testData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateUrlAliasPropertyValuesWithAlwaysAvailable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateUrlAliasPropertyValuesWithAlwaysAvailable\\(\\) has parameter \\$testData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateUrlAliasPropertyValuesWithForwarding\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateUrlAliasPropertyValuesWithForwarding\\(\\) has parameter \\$testData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateUrlAliasThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateUrlAliasWithAlwaysAvailable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testCreateUrlAliasWithForwarding\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testDeleteCorruptedUrlAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testListGlobalAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testListGlobalAliasesWithLanguageFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testListGlobalAliasesWithLimit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testListGlobalAliasesWithOffset\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testListLocationAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testListLocationAliasesLoadsCorrectly\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testListLocationAliasesLoadsCorrectly\\(\\) has parameter \\$testData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testListLocationAliasesWithCustomFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testListLocationAliasesWithLanguageCodeFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testLookUp\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testLookUpThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testLookUpThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testLookUpThrowsNotFoundExceptionWithLanguageFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testLookUpWithLanguageFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testLookupOnMultilingualNestedLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testLookupOnRenamedParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testRefreshSystemUrlAliasesForContentsWithUpdatedContentTypes\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testRefreshSystemUrlAliasesForLocationWithChangedSlugConverterConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testRefreshSystemUrlAliasesForMissingUrlWithHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testRefreshSystemUrlAliasesForMovedLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testRemoveAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:testRemoveAliasesThrowsInvalidArgumentExceptionIfAutogeneratedAliasesAreToBeRemoved\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:updateContentField\\(\\) has parameter \\$fieldDefinitionIdentifier with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLAliasServiceTest\\:\\:updateContentField\\(\\) has parameter \\$fieldValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\)\\: Unexpected token \"\\\\n \\*\", expected variable at offset 74$#" + count: 5 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Parameter \\#1 \\$aliasList of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\URLAliasService\\:\\:removeAliases\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\>, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\> given\\.$#" + count: 2 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 11 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Parameter \\#1 \\$object of function get_class expects object, object\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceAuthorizationTest\\:\\:testFindUrlsThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceAuthorizationTest\\:\\:testLoadByIdThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceAuthorizationTest\\:\\:testLoadByUrlThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceAuthorizationTest\\:\\:testUpdateUrlThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceAuthorizationTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URL\\:\\:\\$modified\\.$#" + count: 4 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:dataProviderForFindUrlsWithSorting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:dataProviderForFindUsages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testCreateUpdateStructValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrls\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrlsUsingMatchNone\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrlsUsingPatternCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrlsUsingValidityCriterionInvalid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrlsUsingValidityCriterionValid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrlsUsingVisibleOnlyCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrlsWithInvalidLimitThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrlsWithInvalidOffsetThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrlsWithLimitZero\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrlsWithOffset\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrlsWithOffsetAndLimit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrlsWithSorting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrlsWithSorting\\(\\) has parameter \\$expectedUrls with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUrlsWithoutCounting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUsages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUsages\\(\\) has parameter \\$expectedContentInfos with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUsages\\(\\) has parameter \\$expectedTotalCount with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUsages\\(\\) has parameter \\$limit with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUsages\\(\\) has parameter \\$offset with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUsages\\(\\) has parameter \\$urlId with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testFindUsagesReturnsEmptySearchResults\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testLoadById\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testLoadByIdThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testLoadByUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testLoadByUrlThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testUpdateUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testUpdateUrlStatus\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLServiceTest\\:\\:testUpdateUrlWithNonUniqueUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URLQuery\\:\\:\\$limit \\(int\\) does not accept string\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URLQuery\\:\\:\\$offset \\(int\\) does not accept string\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardService\\\\CriterionTest\\:\\:getUrlWildcards\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardService/CriterionTest.php + + - + message: "#^Parameter \\#1 \\$criteria of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLWildcard\\\\Query\\\\Criterion\\\\LogicalAnd constructor expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLWildcard\\\\Query\\\\Criterion\\>, array\\<int, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLWildcard\\\\Query\\\\Criterion\\\\SourceUrl\\|Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\Query\\\\Criterion\\\\VisibleOnly\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardService/CriterionTest.php + + - + message: "#^Parameter \\#1 \\$expectedCount of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertCount\\(\\) expects int, int\\|null given\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardService/CriterionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceAuthorizationTest\\:\\:testRemoveThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testCreateSetsIdPropertyOnURLWildcard\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testCreateSetsPropertiesOnURLWildcard\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testCreateThrowsContentValidationExceptionWhenPatternsAndPlaceholdersNotMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testCreateThrowsContentValidationExceptionWhenPlaceholdersNotValidNumberSequence\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testCreateThrowsInvalidArgumentExceptionOnDuplicateSourceUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testCreateWithOptionalForwardParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testLoadAll\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testLoadAllReturnsEmptyArrayByDefault\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testLoadAllWithOffsetAndLimitParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testLoadAllWithOffsetParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testLoadSetsPropertiesOnURLWildcard\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testLoadThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testRemove\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testTranslateReturnsLongestMatchingWildcard\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testTranslateSetsPropertiesOnTranslationResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testTranslateThrowsNotFoundExceptionWhenNotAliasOrWildcardMatches\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\URLWildcardServiceTest\\:\\:testTranslateWithForwardSetToTrue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserPreferenceServiceTest\\:\\:testGetUserPreference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserPreferenceServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserPreferenceServiceTest\\:\\:testGetUserPreferenceCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserPreferenceServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserPreferenceServiceTest\\:\\:testLoadUserPreferences\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserPreferenceServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserPreferenceServiceTest\\:\\:testSetUserPreference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserPreferenceServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserPreferenceServiceTest\\:\\:testSetUserPreferenceThrowsInvalidArgumentExceptionOnEmptyName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserPreferenceServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserPreferenceServiceTest\\:\\:testSetUserPreferenceThrowsInvalidArgumentExceptionOnInvalidValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserPreferenceServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceAuthorizationTest\\:\\:testAssignUserToUserGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceAuthorizationTest\\:\\:testCreateUserGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceAuthorizationTest\\:\\:testCreateUserThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceAuthorizationTest\\:\\:testDeleteUserGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceAuthorizationTest\\:\\:testDeleteUserThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceAuthorizationTest\\:\\:testLoadSubUserGroupsThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceAuthorizationTest\\:\\:testLoadUserGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceAuthorizationTest\\:\\:testLoadUserGroupsOfUserThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceAuthorizationTest\\:\\:testLoadUsersOfUserGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceAuthorizationTest\\:\\:testMoveUserGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceAuthorizationTest\\:\\:testUnAssignUserFromUserGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceAuthorizationTest\\:\\:testUpdateUserGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceAuthorizationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceAuthorizationTest\\:\\:testUpdateUserThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceAuthorizationTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\>\\.$#" + count: 2 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Cannot call method add\\(\\) on DateTimeImmutable\\|false\\|null\\.$#" + count: 3 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:createUserContentTypeWithAccountSettings\\(\\) has parameter \\$fieldSetting with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:createUserContentTypeWithAccountSettings\\(\\) has parameter \\$validatorConfiguration with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:dataProviderForValidatePassword\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:getDataForTestPasswordUpdateRespectsAllValidationSettings\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:getPrioritizedLanguageList\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testAssignUserToUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testAssignUserToUserGroupThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testCreateUserGroupSetsExpectedProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testCreateUserGroupThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testCreateUserGroupThrowsInvalidArgumentExceptionFieldTypeNotAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testCreateUserGroupWhenMissingField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testCreateUserSetsExpectedProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testCreateUserThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testCreateUserThrowsInvalidArgumentExceptionOnFieldTypeNotAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testCreateUserThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testCreateUserWhenMissingField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testCreateUserWithStrongPassword\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testCreateUserWithWeakPasswordThrowsUserPasswordValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testDeleteUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testDeleteUserDeletesRelatedBookmarks\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testDeleteUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testDeleteUserGroupThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testExpireUserToken\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadSubUserGroups\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadSubUserGroupsThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadSubUserGroupsWithPrioritizedLanguagesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserByEmailReturnsEmptyInUnknownEmail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserByLogin\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserByLoginThrowsNotFoundExceptionForUnknownLogin\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserByLoginThrowsNotFoundExceptionForUnknownLoginByEmail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserByLoginWithPrioritizedLanguagesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserByLoginWorksForLoginWithWrongCase\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserGroupThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserGroupWithNoAccessToParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserGroupWithPrioritizedLanguagesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserGroupWithPrioritizedLanguagesListAfterMainLanguageUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserGroupsOfUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserGroupsOfUserWithPrioritizedLanguagesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserWithPrioritizedLanguagesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUserWithPrioritizedLanguagesListAfterMainLanguageUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUsersByEmailWithPrioritizedLanguagesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUsersOfUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testLoadUsersOfUserGroupWithPrioritizedLanguagesList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testMoveUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testMoveUserGroupThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testNewUserCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testNewUserCreateStructSetsExpectedProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testNewUserCreateStructWithFifthParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testNewUserGroupCreateStructSetsContentType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testNewUserGroupCreateStructSetsMainLanguageCode\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testNewUserGroupCreateStructWithSecondParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testNewUserGroupUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testNewUserUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUnAssignUserFromUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUnAssignUserFromUserGroupThrowsBadStateArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUnAssignUserFromUserGroupThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserGroupThrowsContentFieldValidationExceptionOnRequiredFieldEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserGroupThrowsInvalidArgumentExceptionOnFieldTypeNotAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserGroupWithSubContentMetadataUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserGroupWithSubContentUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserReturnsPublishedVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserThrowsInvalidArgumentExceptionOnFieldTypeNotAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserToken\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserUpdatesExpectedProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserWhenMissingField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserWithContentMetadataUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserWithContentUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserWithStrongPassword\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testUpdateUserWithWeakPasswordThrowsUserPasswordValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testValidatePassword\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testValidatePassword\\(\\) has parameter \\$expectedErrors with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\UserServiceTest\\:\\:testValidatePasswordWithDefaultContext\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 2 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Parameter \\#2 \\$array of function array_map expects array, iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroup\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ChangeOwnerLimitationTest\\:\\:getContentCreatePolicyDraft\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft but returns Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ChangeOwnerLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ChangeOwnerLimitationTest\\:\\:getDataForDeniedAccess\\(\\) should return array\\<array\\{Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User, int, array\\<int\\>\\}\\> but returns array\\{array\\{123, array\\{\\-1\\}\\}, array\\{123, array\\{456, 789\\}\\}\\}\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ChangeOwnerLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ChangeOwnerLimitationTest\\:\\:getDataForGrantedAccess\\(\\) should return array\\<array\\{Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User, int, array\\<int\\>\\}\\> but returns array\\{array\\{null, array\\{\\}\\}, array\\{null, array\\{\\-1\\}\\}, array\\{null, array\\{123, 456, \\-1\\}\\}, array\\{123, array\\{123, 456, \\-1\\}\\}, array\\{123, array\\{\\}\\}\\}\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ChangeOwnerLimitationTest.php + + - + message: "#^Cannot access property \\$text on Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ContentTypeLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ContentTypeLimitationTest\\:\\:testContentTypeLimitationAllow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ContentTypeLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ContentTypeLimitationTest\\:\\:testContentTypeLimitationForbid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ContentTypeLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ContentTypeLimitationTest\\:\\:testContentTypeLimitationForbidVariant\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ContentTypeLimitationTest.php + + - + message: "#^Parameter \\#2 \\$policy of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService\\:\\:updatePolicyByRoleDraft\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ContentTypeLimitationTest.php + + - + message: "#^Cannot access property \\$value on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\|null\\.$#" + count: 2 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:createEditorUserWithLanguageLimitation\\(\\) has parameter \\$allowedTranslationsList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:providerForCanUserWithLimitationTargets\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:providerForCreateAndPublishContent\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:providerForPrepareDataForTestsWithLanguageLimitationAndDifferentContentTranslations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:providerForPublishVersionWithLanguageLimitation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testCanUserWithLimitationTargets\\(\\) has parameter \\$allowedTranslationsList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testCanUserWithLimitationTargets\\(\\) has parameter \\$folderNames with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testCopyContentWithLanguageLimitationAndDifferentContentTranslations\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testCopySubtreeWithLanguageLimitationAndDifferentContentTranslations\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testCreateAndPublishContent\\(\\) has parameter \\$allowedTranslationsList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testCreateAndPublishContent\\(\\) has parameter \\$names with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testHideContentWithLanguageLimitationAndDifferentContentTranslations\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testHideLocationWithLanguageLimitationAndDifferentContentTranslations\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testMoveSubtreeWithLanguageLimitationAndDifferentContentTranslations\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testPrepareDataForTestsWithLanguageLimitationAndDifferentContentTranslations\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testPublishVersion\\(\\) has parameter \\$allowedTranslationsList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testPublishVersion\\(\\) has parameter \\$names with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testPublishVersion\\(\\) has parameter \\$namesToUpdate with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testPublishVersionIsNotAllowedIfModifiedOtherTranslations\\(\\) has parameter \\$names with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testRevealContentWithLanguageLimitationAndDifferentContentTranslations\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testSwapLocationWithLanguageLimitationAndDifferentContentTranslations\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testUnhideLocationWithLanguageLimitationAndDifferentContentTranslations\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LanguageLimitationTest\\:\\:testUpdateLocationWithLanguageLimitationAndDifferentContentTranslations\\(\\) has parameter \\$limitationValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^PHPDoc tag @return with type iterable\\<array\\<int, array\\<string\\>\\|bool\\>\\> is not subtype of native type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Parameter \\#1 \\$locationId of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:loadLocation\\(\\) expects int, int\\|null given\\.$#" + count: 7 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Parameter \\#4 \\$targets of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\PermissionResolver\\:\\:canUser\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\>, array\\<Ibexa\\\\Contracts\\\\Core\\\\Limitation\\\\Target\\> given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php + + - + message: "#^Cannot access property \\$text on Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LocationLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LocationLimitationTest\\:\\:testLocationLimitationAllow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LocationLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\LocationLimitationTest\\:\\:testLocationLimitationForbid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/LocationLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\NewObjectStateLimitationTest\\:\\:testNewObjectStateLimitationAllow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/NewObjectStateLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\NewObjectStateLimitationTest\\:\\:testNewObjectStateLimitationForbid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/NewObjectStateLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\NewSectionLimitationTest\\:\\:testNewSectionLimitationAllow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/NewSectionLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\NewSectionLimitationTest\\:\\:testNewSectionLimitationForbid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/NewSectionLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ObjectStateLimitationTest\\:\\:createUserWithObjectStateLimitation\\(\\) has parameter \\$objectStateIDs with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ObjectStateLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ObjectStateLimitationTest\\:\\:createUserWithObjectStateLimitationOnContentRead\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ObjectStateLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\OwnerLimitationTest\\:\\:testOwnerLimitationAllow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/OwnerLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\OwnerLimitationTest\\:\\:testOwnerLimitationForbid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/OwnerLimitationTest.php + + - + message: "#^Parameter \\#2 \\$policy of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService\\:\\:updatePolicyByRoleDraft\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy given\\.$#" + count: 2 + path: tests/integration/Core/Repository/Values/User/Limitation/OwnerLimitationTest.php + + - + message: "#^Variable \\$policy in PHPDoc tag @var does not match assigned variable \\$removePolicy\\.$#" + count: 2 + path: tests/integration/Core/Repository/Values/User/Limitation/OwnerLimitationTest.php + + - + message: "#^Cannot access property \\$text on Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ParentContentTypeLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ParentContentTypeLimitationTest\\:\\:testParentContentTypeLimitationAllow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ParentContentTypeLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ParentContentTypeLimitationTest\\:\\:testParentContentTypeLimitationForbid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ParentContentTypeLimitationTest.php + + - + message: "#^Cannot access property \\$text on Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ParentDepthLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ParentDepthLimitationTest\\:\\:testParentDepthLimitationAllow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ParentDepthLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ParentDepthLimitationTest\\:\\:testParentDepthLimitationAllowPublish\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ParentDepthLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ParentDepthLimitationTest\\:\\:testParentDepthLimitationForbid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ParentDepthLimitationTest.php + + - + message: "#^Cannot access property \\$text on Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ParentOwnerLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ParentOwnerLimitationTest\\:\\:testParentOwnerLimitationAllow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ParentOwnerLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ParentOwnerLimitationTest\\:\\:testParentOwnerLimitationForbid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ParentOwnerLimitationTest.php + + - + message: "#^Cannot access property \\$text on Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ParentUserGroupLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ParentUserGroupLimitationTest\\:\\:testParentUserGroupLimitationAllow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ParentUserGroupLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\ParentUserGroupLimitationTest\\:\\:testParentUserGroupLimitationForbid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/ParentUserGroupLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\RolePolicyLimitationTest\\:\\:addPolicyToNewRole\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/RolePolicyLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\RolePolicyLimitationTest\\:\\:getSubtreeLocationsCount\\(\\) has parameter \\$subtreePathString with no type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/RolePolicyLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\RolePolicyLimitationTest\\:\\:providerForTestRolePoliciesWithOverlappingLimitations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/RolePolicyLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\RolePolicyLimitationTest\\:\\:testRolePoliciesWithOverlappingLimitations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/RolePolicyLimitationTest.php + + - + message: "#^Cannot access property \\$text on Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/SectionLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\SectionLimitationTest\\:\\:testSectionLimitationAllow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/SectionLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\SectionLimitationTest\\:\\:testSectionLimitationForbid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/SectionLimitationTest.php + + - + message: "#^Parameter \\#2 \\$policy of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService\\:\\:updatePolicyByRoleDraft\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy given\\.$#" + count: 2 + path: tests/integration/Core/Repository/Values/User/Limitation/SectionLimitationTest.php + + - + message: "#^Variable \\$policy in PHPDoc tag @var does not match assigned variable \\$readPolicy\\.$#" + count: 2 + path: tests/integration/Core/Repository/Values/User/Limitation/SectionLimitationTest.php + + - + message: "#^Cannot access property \\$text on Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/StatusLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\StatusLimitationTest\\:\\:testStatusLimitationAllow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/StatusLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\StatusLimitationTest\\:\\:testStatusLimitationForbid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/StatusLimitationTest.php + + - + message: "#^Cannot access property \\$text on Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/SubtreeLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\SubtreeLimitationTest\\:\\:prepareLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/SubtreeLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\SubtreeLimitationTest\\:\\:testSubtreeLimitationAllow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/SubtreeLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\SubtreeLimitationTest\\:\\:testSubtreeLimitationForbid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/SubtreeLimitationTest.php + + - + message: "#^Parameter \\#2 \\$policy of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService\\:\\:updatePolicyByRoleDraft\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/SubtreeLimitationTest.php + + - + message: "#^Variable \\$policy in PHPDoc tag @var does not match assigned variable \\$editPolicy\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/SubtreeLimitationTest.php + + - + message: "#^Cannot access property \\$text on Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\|null\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/UserGroupLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\UserGroupLimitationTest\\:\\:testUserGroupLimitationAllow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/UserGroupLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\Repository\\\\Values\\\\User\\\\Limitation\\\\UserGroupLimitationTest\\:\\:testUserGroupLimitationForbid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/UserGroupLimitationTest.php + + - + message: "#^Parameter \\#2 \\$policy of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService\\:\\:updatePolicyByRoleDraft\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyDraft, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy given\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/UserGroupLimitationTest.php + + - + message: "#^Variable \\$policy in PHPDoc tag @var does not match assigned variable \\$editPolicy\\.$#" + count: 1 + path: tests/integration/Core/Repository/Values/User/Limitation/UserGroupLimitationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\User\\\\UserStorage\\\\UserStorageGatewayTest\\:\\:getDataForTestCountUsersWithUnsupportedHashType\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/integration/Core/User/UserStorage/UserStorageGatewayTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\User\\\\UserStorage\\\\UserStorageGatewayTest\\:\\:providerForGetFieldData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/User/UserStorage/UserStorageGatewayTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Integration\\\\Core\\\\User\\\\UserStorage\\\\UserStorageGatewayTest\\:\\:testGetFieldData\\(\\) has parameter \\$expectedUserData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/integration/Core/User/UserStorage/UserStorageGatewayTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Integration\\\\RepositoryInstaller\\\\TestCase\\:\\:\\$class has no type specified\\.$#" + count: 1 + path: tests/integration/RepositoryInstaller/TestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\FieldTypeRegistryPassTest\\:\\:tagsProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/FieldTypeRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\FieldTypeRegistryPassTest\\:\\:testRegisterFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/FieldTypeRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\FieldTypeRegistryPassTest\\:\\:testRegisterFieldTypeNoAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/FieldTypeRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\GenericFieldTypeConverterPassTest\\:\\:assertContainerBuilderHasNoServiceDefinitionWithMethodCall\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/GenericFieldTypeConverterPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\GenericFieldTypeConverterPassTest\\:\\:assertContainerBuilderHasNoServiceDefinitionWithMethodCall\\(\\) has parameter \\$index with no type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/GenericFieldTypeConverterPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\FieldTypeRegistryPassTest\\:\\:testRegisterFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Search/FieldTypeRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\FieldTypeRegistryPassTest\\:\\:testRegisterFieldTypeNoAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Search/FieldTypeRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\CriteriaConverterPassTest\\:\\:testAddContentHandlers\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Search/Legacy/CriteriaConverterPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\CriteriaConverterPassTest\\:\\:testAddLocationHandlers\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Search/Legacy/CriteriaConverterPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\CriterionFieldValueHandlerRegistryPassTest\\:\\:testRegisterValueHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\CriterionFieldValueHandlerRegistryPassTest\\:\\:testRegisterValueHandlerNoAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\SortClauseConverterPassTest\\:\\:testAddContentHandlers\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\SortClauseConverterPassTest\\:\\:testAddLocationAndContentHandlers\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Search\\\\Legacy\\\\SortClauseConverterPassTest\\:\\:testAddLocationHandlers\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\ExternalStorageRegistryPassTest\\:\\:externalStorageHandlerGatewayTagsProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\ExternalStorageRegistryPassTest\\:\\:externalStorageHandlerTagsProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\ExternalStorageRegistryPassTest\\:\\:testRegisterExternalStorageHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\ExternalStorageRegistryPassTest\\:\\:testRegisterExternalStorageHandlerNoAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\ExternalStorageRegistryPassTest\\:\\:testRegisterExternalStorageHandlerWithGateway\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\ExternalStorageRegistryPassTest\\:\\:testRegisterExternalStorageHandlerWithGatewayNoAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\ExternalStorageRegistryPassTest\\:\\:testRegisterExternalStorageHandlerWithGatewayNoIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\ExternalStorageRegistryPassTest\\:\\:testRegisterExternalStorageHandlerWithoutRegisteredGateway\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\Legacy\\\\FieldValueConverterRegistryPassTest\\:\\:testRegisterConverterNoLazy\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Storage/Legacy/FieldValueConverterRegistryPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Storage\\\\Legacy\\\\RoleLimitationConverterPassTest\\:\\:testRegisterRoleLimitationConverter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Storage/Legacy/RoleLimitationConverterPassTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Stubs\\\\GatewayBasedStorageHandler\\:\\:deleteFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Stubs/GatewayBasedStorageHandler.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Stubs\\\\GatewayBasedStorageHandler\\:\\:deleteFieldData\\(\\) has parameter \\$fieldIds with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Stubs/GatewayBasedStorageHandler.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Stubs\\\\GatewayBasedStorageHandler\\:\\:deleteFieldData\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Stubs/GatewayBasedStorageHandler.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Stubs\\\\GatewayBasedStorageHandler\\:\\:getFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Stubs/GatewayBasedStorageHandler.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Stubs\\\\GatewayBasedStorageHandler\\:\\:getFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Stubs/GatewayBasedStorageHandler.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Stubs\\\\GatewayBasedStorageHandler\\:\\:getIndexData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Stubs/GatewayBasedStorageHandler.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Stubs\\\\GatewayBasedStorageHandler\\:\\:getIndexData\\(\\) should return array\\<Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Field\\> but return statement is missing\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Stubs/GatewayBasedStorageHandler.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Stubs\\\\GatewayBasedStorageHandler\\:\\:hasFieldData\\(\\) should return bool but return statement is missing\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Stubs/GatewayBasedStorageHandler.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\Stubs\\\\GatewayBasedStorageHandler\\:\\:storeFieldData\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/Stubs/GatewayBasedStorageHandler.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\TaggedServiceIdsIterator\\\\DeprecationErrorCollector\\:\\:getErrors\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/TaggedServiceIdsIterator/DeprecationErrorCollector.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Base\\\\Container\\\\Compiler\\\\TaggedServiceIdsIterator\\\\DeprecationErrorCollector\\:\\:\\$errors type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Base/Container/Compiler/TaggedServiceIdsIterator/DeprecationErrorCollector.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\CollectionInterface\\:\\:filter\\(\\)\\.$#" + count: 2 + path: tests/lib/Collection/AbstractCollectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\AbstractCollectionTest\\:\\:createCollection\\(\\) has invalid return type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Collection\\\\CollectionInterfaces\\.$#" + count: 1 + path: tests/lib/Collection/AbstractCollectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\AbstractCollectionTest\\:\\:createCollection\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Collection/AbstractCollectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\AbstractCollectionTest\\:\\:createCollection\\(\\) return type with generic interface Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\CollectionInterface does not specify its types\\: TValue$#" + count: 1 + path: tests/lib/Collection/AbstractCollectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\AbstractCollectionTest\\:\\:createCollectionWithExampleData\\(\\) has invalid return type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Collection\\\\CollectionInterfaces\\.$#" + count: 1 + path: tests/lib/Collection/AbstractCollectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\AbstractCollectionTest\\:\\:createCollectionWithExampleData\\(\\) return type with generic interface Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\CollectionInterface does not specify its types\\: TValue$#" + count: 1 + path: tests/lib/Collection/AbstractCollectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\AbstractCollectionTest\\:\\:createEmptyCollection\\(\\) has invalid return type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Collection\\\\CollectionInterfaces\\.$#" + count: 1 + path: tests/lib/Collection/AbstractCollectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\AbstractCollectionTest\\:\\:createEmptyCollection\\(\\) return type with generic interface Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\CollectionInterface does not specify its types\\: TValue$#" + count: 1 + path: tests/lib/Collection/AbstractCollectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\AbstractCollectionTest\\:\\:getExampleData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Collection/AbstractCollectionTest.php + + - + message: "#^PHPDoc tag @return with type TCollection of Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Collection\\\\CollectionInterfaces is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\CollectionInterface\\.$#" + count: 3 + path: tests/lib/Collection/AbstractCollectionTest.php + + - + message: "#^PHPDoc tag @template TCollection for class Ibexa\\\\Tests\\\\Core\\\\Collection\\\\AbstractCollectionTest has invalid bound type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Collection\\\\CollectionInterfaces\\.$#" + count: 1 + path: tests/lib/Collection/AbstractCollectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\ArrayListTest\\:\\:createCollection\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Collection/ArrayListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\ArrayListTest\\:\\:createCollection\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\ArrayList does not specify its types\\: TValue$#" + count: 1 + path: tests/lib/Collection/ArrayListTest.php + + - + message: "#^PHPDoc tag @var for variable \\$list contains generic class Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\ArrayList but does not specify its types\\: TValue$#" + count: 2 + path: tests/lib/Collection/ArrayListTest.php + + - + message: "#^Type Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\ArrayList in generic type Ibexa\\\\Tests\\\\Core\\\\Collection\\\\AbstractCollectionTest\\<Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\ArrayList\\> in PHPDoc tag @extends is not subtype of template type TCollection of Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Collection\\\\CollectionInterfaces of class Ibexa\\\\Tests\\\\Core\\\\Collection\\\\AbstractCollectionTest\\.$#" + count: 1 + path: tests/lib/Collection/ArrayListTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MapInterface\\:\\:exists\\(\\)\\.$#" + count: 2 + path: tests/lib/Collection/ArrayMapTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MapInterface\\:\\:filter\\(\\)\\.$#" + count: 1 + path: tests/lib/Collection/ArrayMapTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MapInterface\\:\\:forAll\\(\\)\\.$#" + count: 2 + path: tests/lib/Collection/ArrayMapTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MapInterface\\:\\:map\\(\\)\\.$#" + count: 1 + path: tests/lib/Collection/ArrayMapTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\ArrayMapTest\\:\\:createCollection\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Collection/ArrayMapTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\ArrayMapTest\\:\\:createCollection\\(\\) return type with generic interface Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MapInterface does not specify its types\\: TKey, TValue$#" + count: 1 + path: tests/lib/Collection/ArrayMapTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\ArrayMapTest\\:\\:getExampleData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Collection/ArrayMapTest.php + + - + message: "#^PHPDoc tag @return with type Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MapInterface\\|Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\StreamableInterface is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MapInterface\\.$#" + count: 1 + path: tests/lib/Collection/ArrayMapTest.php + + - + message: "#^PHPDoc tag @var for variable \\$map contains generic class Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\ArrayMap but does not specify its types\\: TKey, TValue$#" + count: 1 + path: tests/lib/Collection/ArrayMapTest.php + + - + message: "#^Type Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\ArrayMap in generic type Ibexa\\\\Tests\\\\Core\\\\Collection\\\\AbstractCollectionTest\\<Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\ArrayMap\\> in PHPDoc tag @extends is not subtype of template type TCollection of Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Collection\\\\CollectionInterfaces of class Ibexa\\\\Tests\\\\Core\\\\Collection\\\\AbstractCollectionTest\\.$#" + count: 1 + path: tests/lib/Collection/ArrayMapTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\MutableArrayListTest\\:\\:createCollection\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Collection/MutableArrayListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\MutableArrayListTest\\:\\:createCollection\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MutableArrayList does not specify its types\\: TValue$#" + count: 1 + path: tests/lib/Collection/MutableArrayListTest.php + + - + message: "#^PHPDoc tag @var for variable \\$list contains generic class Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MutableArrayList but does not specify its types\\: TValue$#" + count: 4 + path: tests/lib/Collection/MutableArrayListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\MutableArrayMapTest\\:\\:createCollection\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Collection/MutableArrayMapTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Collection\\\\MutableArrayMapTest\\:\\:createCollection\\(\\) return type with generic class Ibexa\\\\Contracts\\\\Core\\\\Collection\\\\MutableArrayMap does not specify its types\\: TKey, TValue$#" + count: 1 + path: tests/lib/Collection/MutableArrayMapTest.php + + - + message: "#^Parameter \\#2 \\$string of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertRegExp\\(\\) expects string, string\\|false given\\.$#" + count: 2 + path: tests/lib/Container/Encore/ConfigurationDumperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\AbstractServiceTest\\:\\:getListenersStack\\(\\) has parameter \\$listeners with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Event/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\AbstractServiceTest\\:\\:getListenersStack\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Event/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\BookmarkServiceTest\\:\\:testCreateBookmarkEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/BookmarkServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\BookmarkServiceTest\\:\\:testCreateBookmarkStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/BookmarkServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\BookmarkServiceTest\\:\\:testDeleteBookmarkEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/BookmarkServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\BookmarkServiceTest\\:\\:testDeleteBookmarkStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/BookmarkServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testAddRelationEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testAddRelationStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testCopyContentEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testCopyContentStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testCreateContentDraftEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testCreateContentDraftStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testCreateContentEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testCreateContentStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testDeleteContentEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testDeleteContentStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testDeleteRelationEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testDeleteRelationStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testDeleteTranslationEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testDeleteTranslationStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testDeleteVersionEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testDeleteVersionStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testHideContentEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testHideContentStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testPublishVersionEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testPublishVersionStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testReturnAddRelationResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testReturnCopyContentResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testReturnCreateContentDraftResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testReturnCreateContentResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testReturnDeleteContentResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testReturnPublishVersionResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testReturnUpdateContentMetadataResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testReturnUpdateContentResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testRevealContentEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testRevealContentStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testUpdateContentEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testUpdateContentMetadataEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testUpdateContentMetadataStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentServiceTest\\:\\:testUpdateContentStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testAddFieldDefinitionEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testAddFieldDefinitionStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testAssignContentTypeGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testAssignContentTypeGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testCopyContentTypeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testCopyContentTypeStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeDraftEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeDraftStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testCreateContentTypeStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testDeleteContentTypeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testDeleteContentTypeGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testDeleteContentTypeGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testDeleteContentTypeStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testPublishContentTypeDraftEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testPublishContentTypeDraftStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testRemoveContentTypeTranslationEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testRemoveContentTypeTranslationStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testRemoveFieldDefinitionEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testRemoveFieldDefinitionStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testReturnCopyContentTypeResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testReturnCreateContentTypeDraftResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testReturnCreateContentTypeGroupResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testReturnCreateContentTypeResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testReturnRemoveContentTypeTranslationResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testUnassignContentTypeGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testUnassignContentTypeGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeDraftEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeDraftStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testUpdateContentTypeGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testUpdateFieldDefinitionEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ContentTypeServiceTest\\:\\:testUpdateFieldDefinitionStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testCreateLanguageEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testCreateLanguageStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testDeleteLanguageEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testDeleteLanguageStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testDisableLanguageEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testDisableLanguageStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testEnableLanguageEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testEnableLanguageStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testReturnCreateLanguageResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testReturnDisableLanguageResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testReturnEnableLanguageResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testReturnUpdateLanguageNameResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testUpdateLanguageNameEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LanguageServiceTest\\:\\:testUpdateLanguageNameStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testCopySubtreeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testCopySubtreeStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testCreateLocationEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testCreateLocationStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testDeleteLocationEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testDeleteLocationStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testHideLocationEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testHideLocationStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testMoveSubtreeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testMoveSubtreeStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testReturnCopySubtreeResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testReturnCreateLocationResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testReturnHideLocationResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testReturnUnhideLocationResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testReturnUpdateLocationResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testSwapLocationEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testSwapLocationStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testUnhideLocationEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testUnhideLocationStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testUpdateLocationEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\LocationServiceTest\\:\\:testUpdateLocationStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\NotificationServiceTest\\:\\:testCreateNotificationEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\NotificationServiceTest\\:\\:testCreateNotificationStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\NotificationServiceTest\\:\\:testDeleteNotificationEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\NotificationServiceTest\\:\\:testDeleteNotificationStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\NotificationServiceTest\\:\\:testMarkNotificationAsReadEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\NotificationServiceTest\\:\\:testMarkNotificationAsReadStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\NotificationServiceTest\\:\\:testReturnCreateNotificationResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/NotificationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testCreateObjectStateEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testCreateObjectStateGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testCreateObjectStateGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testCreateObjectStateStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testDeleteObjectStateEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testDeleteObjectStateGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testDeleteObjectStateGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testDeleteObjectStateStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testReturnCreateObjectStateGroupResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testReturnCreateObjectStateResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testReturnUpdateObjectStateGroupResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testReturnUpdateObjectStateResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testSetContentStateEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testSetContentStateStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testSetPriorityOfObjectStateEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testSetPriorityOfObjectStateStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testUpdateObjectStateEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testUpdateObjectStateGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testUpdateObjectStateGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\ObjectStateServiceTest\\:\\:testUpdateObjectStateStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testAddPolicyByRoleDraftEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testAddPolicyByRoleDraftStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testAssignRoleToUserEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testAssignRoleToUserGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testAssignRoleToUserGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testAssignRoleToUserStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testCreateRoleDraftEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testCreateRoleDraftStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testCreateRoleEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testCreateRoleStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testDeleteRoleDraftEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testDeleteRoleDraftStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testDeleteRoleEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testDeleteRoleStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testPublishRoleDraftEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testPublishRoleDraftStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testRemovePolicyByRoleDraftEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testRemovePolicyByRoleDraftStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testRemoveRoleAssignmentEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testRemoveRoleAssignmentStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testReturnAddPolicyByRoleDraftResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testReturnCreateRoleDraftResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testReturnCreateRoleResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testReturnRemovePolicyByRoleDraftResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testReturnUpdatePolicyByRoleDraftResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testReturnUpdateRoleDraftResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testUpdatePolicyByRoleDraftEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testUpdatePolicyByRoleDraftStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testUpdateRoleDraftEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\RoleServiceTest\\:\\:testUpdateRoleDraftStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/RoleServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\SectionServiceTest\\:\\:testAssignSectionEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\SectionServiceTest\\:\\:testAssignSectionStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\SectionServiceTest\\:\\:testAssignSectionToSubtreeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\SectionServiceTest\\:\\:testAssignSectionToSubtreeStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\SectionServiceTest\\:\\:testCreateSectionEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\SectionServiceTest\\:\\:testCreateSectionStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\SectionServiceTest\\:\\:testDeleteSectionEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\SectionServiceTest\\:\\:testDeleteSectionStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\SectionServiceTest\\:\\:testReturnCreateSectionResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\SectionServiceTest\\:\\:testReturnUpdateSectionResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\SectionServiceTest\\:\\:testUpdateSectionEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\SectionServiceTest\\:\\:testUpdateSectionStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/SectionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\TrashServiceTest\\:\\:testDeleteTrashItemEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\TrashServiceTest\\:\\:testDeleteTrashItemStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\TrashServiceTest\\:\\:testEmptyTrashEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\TrashServiceTest\\:\\:testEmptyTrashStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\TrashServiceTest\\:\\:testRecoverEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\TrashServiceTest\\:\\:testRecoverStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\TrashServiceTest\\:\\:testReturnDeleteTrashItemResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\TrashServiceTest\\:\\:testReturnEmptyTrashResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\TrashServiceTest\\:\\:testReturnRecoverResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\TrashServiceTest\\:\\:testReturnTrashResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\TrashServiceTest\\:\\:testTrashEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\TrashServiceTest\\:\\:testTrashStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLAliasServiceTest\\:\\:testCreateGlobalUrlAliasStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLAliasServiceTest\\:\\:testCreateUrlAliasEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLAliasServiceTest\\:\\:testCreateUrlAliasStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLAliasServiceTest\\:\\:testRefreshSystemUrlAliasesForLocationEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLAliasServiceTest\\:\\:testRefreshSystemUrlAliasesForLocationStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLAliasServiceTest\\:\\:testRemoveAliasesEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLAliasServiceTest\\:\\:testRemoveAliasesStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLAliasServiceTest\\:\\:testReturnCreateGlobalUrlAliasResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLAliasServiceTest\\:\\:testReturnCreateUrlAliasResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Parameter \\#4 \\$forwarding of method Ibexa\\\\Core\\\\Event\\\\URLAliasService\\:\\:createGlobalUrlAlias\\(\\) expects bool, string given\\.$#" + count: 3 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Parameter \\#4 \\$forwarding of method Ibexa\\\\Core\\\\Event\\\\URLAliasService\\:\\:createUrlAlias\\(\\) expects bool, string given\\.$#" + count: 3 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Parameter \\#5 \\$alwaysAvailable of method Ibexa\\\\Core\\\\Event\\\\URLAliasService\\:\\:createGlobalUrlAlias\\(\\) expects bool, string given\\.$#" + count: 3 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Parameter \\#5 \\$alwaysAvailable of method Ibexa\\\\Core\\\\Event\\\\URLAliasService\\:\\:createUrlAlias\\(\\) expects bool, string given\\.$#" + count: 3 + path: tests/lib/Event/URLAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLServiceTest\\:\\:testReturnUpdateUrlResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLServiceTest\\:\\:testUpdateUrlEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLServiceTest\\:\\:testUpdateUrlStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLWildcardServiceTest\\:\\:testCreateEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLWildcardServiceTest\\:\\:testCreateStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLWildcardServiceTest\\:\\:testRemoveEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLWildcardServiceTest\\:\\:testRemoveStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLWildcardServiceTest\\:\\:testReturnCreateResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLWildcardServiceTest\\:\\:testReturnTranslateResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLWildcardServiceTest\\:\\:testTranslateEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\URLWildcardServiceTest\\:\\:testTranslateStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/URLWildcardServiceTest.php + + - + message: "#^Parameter \\#3 \\$forward of method Ibexa\\\\Core\\\\Event\\\\URLWildcardService\\:\\:create\\(\\) expects bool, string given\\.$#" + count: 3 + path: tests/lib/Event/URLWildcardServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserPreferenceServiceTest\\:\\:testSetUserPreferenceEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserPreferenceServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserPreferenceServiceTest\\:\\:testSetUserPreferenceStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserPreferenceServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testAssignUserToUserGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testAssignUserToUserGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testCreateUserEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testCreateUserGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testCreateUserGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testCreateUserStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testDeleteUserEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testDeleteUserGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testDeleteUserGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testDeleteUserStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testMoveUserGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testMoveUserGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testReturnCreateUserGroupResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testReturnCreateUserResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testReturnDeleteUserGroupResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testReturnDeleteUserResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testReturnUpdateUserGroupResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testReturnUpdateUserResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testReturnUpdateUserTokenResultInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testUnAssignUserFromUserGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testUnAssignUserFromUserGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testUpdateUserEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testUpdateUserGroupEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testUpdateUserGroupStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testUpdateUserStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testUpdateUserTokenEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Event\\\\UserServiceTest\\:\\:testUpdateUserTokenStopPropagationInBeforeEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Event/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\APIFieldTypeTest\\:\\:testValidateFieldSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/APIFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\APIFieldTypeTest\\:\\:testValidateFieldSettingsNoError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/APIFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\APIFieldTypeTest\\:\\:testValidateValidatorConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/APIFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\APIFieldTypeTest\\:\\:testValidateValidatorConfigurationNoError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/APIFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\APIFieldTypeTest\\:\\:testValidateValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/APIFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\APIFieldTypeTest\\:\\:testValidateValueNoError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/APIFieldTypeTest.php + + - + message: "#^Cannot assign new offset to Ibexa\\\\Core\\\\FieldType\\\\Author\\\\AuthorCollection\\.$#" + count: 3 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:provideInValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:provideValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:testAcceptValueInvalidFormat\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:testAcceptValueInvalidType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:testAcceptValueValidFormat\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:testAddAuthor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:testBuildFieldValueWithParam\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:testBuildFieldValueWithoutParam\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:testFieldValueToString\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:testRemoveAuthors\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\AuthorTest\\:\\:testValidatorConfigurationSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Property Ibexa\\\\Core\\\\FieldType\\\\Author\\\\Value\\:\\:\\$authors \\(Ibexa\\\\Core\\\\FieldType\\\\Author\\\\AuthorCollection\\) does not accept string\\.$#" + count: 1 + path: tests/lib/FieldType/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:assertIsValidHashValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:assertIsValidHashValue\\(\\) has parameter \\$keyChain with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:doValidate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:doValidate\\(\\) has parameter \\$fieldDefinitionData with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:doValidate\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:getFieldDefinitionMock\\(\\) has parameter \\$fieldSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:provideInValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:provideInvalidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:provideValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:provideValidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testAcceptGetEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testAcceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testEmptyValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testEmptyValueIsEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testFieldSettingsFromHash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testFieldSettingsFromHash\\(\\) has parameter \\$inputSettings with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testFieldSettingsToHash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testFieldSettingsToHash\\(\\) has parameter \\$inputSettings with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testFromHash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testFromHash\\(\\) has parameter \\$expectedResult with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testFromHash\\(\\) has parameter \\$inputHash with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testGetFieldTypeIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testGetName\\(\\) has parameter \\$fieldSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testSettingsSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testToHash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testToHash\\(\\) has parameter \\$expectedResult with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidateFieldSettingsInvalid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidateFieldSettingsValid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidateInvalid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidateInvalid\\(\\) has parameter \\$errors with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidateInvalid\\(\\) has parameter \\$fieldDefinitionData with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidateInvalid\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidateValid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidateValid\\(\\) has parameter \\$fieldDefinitionData with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidateValid\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidateValidatorConfigurationInvalid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidateValidatorConfigurationValid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidatorConfigurationFromHash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidatorConfigurationSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:testValidatorConfigurationToHash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$inputConfiguration$#" + count: 2 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$inputValue$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\|\\\\PHPUnit\\\\Framework\\\\MockObject\\\\MockObject \\$fieldDefinitionMock\\)\\: Unexpected token \"\\|\", expected type at offset 9$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Parameter \\#1 \\$exception of method PHPUnit\\\\Framework\\\\TestCase\\:\\:expectException\\(\\) expects class\\-string\\<Throwable\\>, string given\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BaseFieldTypeTest\\:\\:\\$fieldTypeUnderTest \\(Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/FieldType/BaseFieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryBaseTest\\:\\:getBlackListValidatorMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryBaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryBaseTest\\:\\:getConfigResolverMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryBaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryBaseTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryBaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryBaseTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryBaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryBaseTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryBaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryBaseTest\\:\\:provideInvalidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryBaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryBaseTest\\:\\:provideValidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryBaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryBaseTest\\:\\:\\$blackListedExtensions has no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryBaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryFileTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryFileTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryFileTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryFileTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryFileTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryFileTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryFileTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryFileTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryFileTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryFileTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryFileTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryFileTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\BinaryFileTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/BinaryFileTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CheckboxTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CheckboxTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CheckboxTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CheckboxTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CheckboxTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CheckboxTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CheckboxTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CheckboxTest\\:\\:testBuildFieldValueWithParam\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CheckboxTest\\:\\:testBuildFieldValueWithoutParam\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CheckboxTest\\:\\:testFieldValueToString\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CheckboxTest\\:\\:testToPersistenceValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/CheckboxTest.php + + - + message: "#^Parameter \\#1 \\$boolValue of class Ibexa\\\\Core\\\\FieldType\\\\Checkbox\\\\Value constructor expects bool, int given\\.$#" + count: 1 + path: tests/lib/FieldType/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CountryTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CountryTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CountryTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CountryTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CountryTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CountryTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CountryTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CountryTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\CountryTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/CountryTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$value\\.$#" + count: 2 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Cannot access property \\$value on array\\.$#" + count: 2 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Cannot call method add\\(\\) on DateTime\\|null\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:provideInValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:provideInputForTimeStringFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:provideValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:testFromHash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:testFromHash\\(\\) has parameter \\$expectedResult with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:testFromHash\\(\\) has parameter \\$inputHash with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:testTimeStringFromHash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateAndTimeTest\\:\\:testTimeStringFromHash\\(\\) has parameter \\$inputHash with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^PHPDoc tag @param references unknown parameter\\: \\$inputValue$#" + count: 2 + path: tests/lib/FieldType/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateTest\\:\\:provideInValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateTest\\:\\:provideValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\DateTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressTest\\:\\:provideInvalidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressTest\\:\\:provideValidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressTest.php + + - + message: "#^Parameter \\#1 \\$email of class Ibexa\\\\Core\\\\FieldType\\\\EmailAddress\\\\Value constructor expects string, int given\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\EmailAddressValidator\\:\\:\\$Extent\\.$#" + count: 4 + path: tests/lib/FieldType/EmailAddressValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressValidatorTest\\:\\:testConstraintsInitializeGet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressValidatorTest\\:\\:testConstraintsSetGet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressValidatorTest\\:\\:testConstructor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressValidatorTest\\:\\:testGetConstraintsSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressValidatorTest\\:\\:testValidateCorrectEmailAddresses\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\EmailAddressValidatorTest\\:\\:testValidateWrongEmailAddresses\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/EmailAddressValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FieldTypeMockTest\\:\\:providerForTestApplyDefaultSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FieldTypeMockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FieldTypeMockTest\\:\\:providerForTestApplyDefaultValidatorConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FieldTypeMockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FieldTypeMockTest\\:\\:testApplyDefaultSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FieldTypeMockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FieldTypeMockTest\\:\\:testApplyDefaultSettings\\(\\) has parameter \\$expectedSettings with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FieldTypeMockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FieldTypeMockTest\\:\\:testApplyDefaultSettings\\(\\) has parameter \\$initialSettings with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FieldTypeMockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FieldTypeMockTest\\:\\:testApplyDefaultSettingsThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FieldTypeMockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FieldTypeMockTest\\:\\:testApplyDefaultValidatorConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FieldTypeMockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FieldTypeMockTest\\:\\:testApplyDefaultValidatorConfiguration\\(\\) has parameter \\$expectedConfiguration with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FieldTypeMockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FieldTypeMockTest\\:\\:testApplyDefaultValidatorConfiguration\\(\\) has parameter \\$initialConfiguration with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FieldTypeMockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FieldTypeMockTest\\:\\:testApplyDefaultValidatorConfigurationEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FieldTypeMockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FieldTypeMockTest\\:\\:testApplyDefaultValidatorConfigurationEmptyThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FieldTypeMockTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\:\\:valuesEqual\\(\\)\\.$#" + count: 1 + path: tests/lib/FieldType/FieldTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FieldTypeTest\\:\\:provideInputForValuesEqual\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/FieldTypeTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\FileSizeValidator\\:\\:\\$unexisting\\.$#" + count: 2 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Translation\\\\Message\\:\\:\\$message\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Translation\\\\Message\\:\\:\\$values\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:providerForValidateConstraintsKO\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:providerForValidateConstraintsOK\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:providerForValidateKO\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:providerForValidateOK\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testConstraintsInitializeGet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testConstraintsSetGet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testConstructor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testGetBadConstraint\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testGetConstraintsSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testInitializeBadConstraint\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testSetBadConstraint\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testValidateConstraintsCorrectValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testValidateConstraintsCorrectValues\\(\\) has parameter \\$constraints with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has parameter \\$constraints with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has parameter \\$expectedMessages with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has parameter \\$values with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testValidateCorrectValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testValidateWrongValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testValidateWrongValues\\(\\) has parameter \\$message with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testValidateWrongValues\\(\\) has parameter \\$size with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FileSizeValidatorTest\\:\\:testValidateWrongValues\\(\\) has parameter \\$values with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 3 + path: tests/lib/FieldType/FileSizeValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/FloatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/FloatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/FloatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/FloatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/FloatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/FloatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/FloatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatTest\\:\\:provideInvalidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/FloatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/FloatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/FloatTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatTest\\:\\:provideValidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/FloatTest.php + + - + message: "#^Parameter \\#1 \\$value of class Ibexa\\\\Core\\\\FieldType\\\\Float\\\\Value constructor expects float\\|null, string given\\.$#" + count: 1 + path: tests/lib/FieldType/FloatTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\FloatValueValidator\\:\\:\\$unexisting\\.$#" + count: 2 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Translation\\\\Message\\:\\:\\$message\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Translation\\\\Message\\:\\:\\$values\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:providerForValidateConstraintsKO\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:providerForValidateConstraintsOK\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:providerForValidateKO\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:providerForValidateOK\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testConstraintsInitializeGet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testConstraintsSetGet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testConstructor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testGetBadConstraint\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testGetConstraintsSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testInitializeBadConstraint\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testSetBadConstraint\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testValidateConstraintsCorrectValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testValidateConstraintsCorrectValues\\(\\) has parameter \\$constraints with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has parameter \\$constraints with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has parameter \\$expectedMessages with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has parameter \\$values with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testValidateCorrectValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testValidateCorrectValues\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testValidateWrongValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testValidateWrongValues\\(\\) has parameter \\$message with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testValidateWrongValues\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\FloatValueValidatorTest\\:\\:testValidateWrongValues\\(\\) has parameter \\$values with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/FloatValueValidatorTest.php + + - + message: "#^Cannot cast Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Translation to string\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Class Symfony\\\\Component\\\\Validator\\\\ConstraintViolation constructor invoked with 1 parameter, 6\\-10 required\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:createFieldTypeUnderTest\\(\\) should return Ibexa\\\\Core\\\\FieldType\\\\FieldType but returns Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\Stubs\\\\Type\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:testValidateInvalid\\(\\) has parameter \\$errors with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:testValidateInvalid\\(\\) has parameter \\$fieldDefinitionData with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:testValidateInvalid\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:testValidateValid\\(\\) has parameter \\$fieldDefinitionData with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:testValidateValid\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\GenericTest\\:\\:\\$serializer \\(Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValueSerializerInterface&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValueSerializerInterface\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/GenericTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\Stubs\\\\Value\\:\\:__construct\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/Stubs/Value.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\Stubs\\\\Value\\:\\:getValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/Stubs/Value.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Generic\\\\Stubs\\\\Value\\:\\:\\$value has no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Generic/Stubs/Value.php + + - + message: "#^Class Ibexa\\\\Core\\\\FieldType\\\\ISBN\\\\Type does not have a constructor and must be instantiated without any parameters\\.$#" + count: 1 + path: tests/lib/FieldType/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ISBNTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ISBNTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ISBNTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ISBNTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ISBNTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ISBNTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ISBNTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ISBNTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ISBNTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testCreateBinaryFile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testDeleteBinaryFile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testExists\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testGetExternalPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testGetFileContents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testGetFileContentsOfDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testGetFileInputStream\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testGetInternalPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testGetMimeType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testGetMimeTypeOfDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testGetUri\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testLoadBinaryFile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testLoadBinaryFileByUriWithDraftFile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testLoadBinaryFileByUriWithPublishedFile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testLoadBinaryFileDraftExternalPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testLoadBinaryFileDraftInternalPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testLoadBinaryFilePublishedInternalPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testNewBinaryCreateStructFromLocalFile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\LegacyTest\\:\\:testNewBinaryCreateStructFromUploadedFile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Parameter \\#1 \\$binaryFileId of method Ibexa\\\\Core\\\\FieldType\\\\Image\\\\IO\\\\Legacy\\:\\:getUri\\(\\) expects string, Ibexa\\\\Core\\\\IO\\\\Values\\\\BinaryFile given\\.$#" + count: 1 + path: tests/lib/FieldType/Image/IO/LegacyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\PathGenerator\\\\LegacyPathGeneratorTest\\:\\:provideStoragePathForFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/PathGenerator/LegacyPathGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Image\\\\PathGenerator\\\\LegacyPathGeneratorTest\\:\\:testGetStoragePathForField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Image/PathGenerator/LegacyPathGeneratorTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\FieldType\\\\ImageAsset\\\\AssetMapper\\:\\:expects\\(\\)\\.$#" + count: 4 + path: tests/lib/FieldType/ImageAsset/AssetMapperTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Content\\:\\:expects\\(\\)\\.$#" + count: 2 + path: tests/lib/FieldType/ImageAsset/AssetMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAsset\\\\AssetMapperTest\\:\\:createPartialMapper\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAsset/AssetMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAsset\\\\AssetMapperTest\\:\\:dataProviderForIsAsset\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAsset/AssetMapperTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAsset\\\\AssetMapperTest\\:\\:\\$mappings type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAsset/AssetMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAssetTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAssetTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAssetTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAssetTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAssetTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAssetTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAssetTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAssetTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAssetTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAssetTest\\:\\:testGetName\\(\\) has parameter \\$fieldSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAssetTest\\:\\:testGetRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAssetTest\\:\\:testIsSearchable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAssetTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageAssetTest\\:\\:testValidateNonAsset\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/ImageAssetTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:\\$mimeTypeDetectorMock\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:getBlackListValidatorMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:getConfigResolverMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:getImageInputPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:getImageValidatorMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:provideInputForValuesEqual\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\ImageTest\\:\\:\\$blackListedExtensions has no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/ImageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerTest\\:\\:provideInvalidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerTest\\:\\:provideValidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerTest.php + + - + message: "#^Parameter \\#1 \\$value of class Ibexa\\\\Core\\\\FieldType\\\\Integer\\\\Value constructor expects int\\|null, string given\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\IntegerValueValidator\\:\\:\\$unexisting\\.$#" + count: 2 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Translation\\\\Message\\:\\:\\$message\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Translation\\\\Message\\:\\:\\$values\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:providerForValidateConstraintsKO\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:providerForValidateConstraintsOK\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:providerForValidateKO\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:providerForValidateOK\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testConstraintsInitializeGet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testConstraintsSetGet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testConstructor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testGetBadConstraint\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testGetConstraintsSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testInitializeBadConstraint\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testSetBadConstraint\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testValidateConstraintsCorrectValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testValidateConstraintsCorrectValues\\(\\) has parameter \\$constraints with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has parameter \\$constraints with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has parameter \\$expectedMessages with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has parameter \\$values with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testValidateCorrectValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testValidateCorrectValues\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testValidateWrongValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testValidateWrongValues\\(\\) has parameter \\$message with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testValidateWrongValues\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\IntegerValueValidatorTest\\:\\:testValidateWrongValues\\(\\) has parameter \\$values with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/IntegerValueValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\KeywordTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/KeywordTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\KeywordTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/KeywordTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\KeywordTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/KeywordTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\KeywordTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/KeywordTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\KeywordTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/KeywordTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\KeywordTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/KeywordTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\KeywordTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/KeywordTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MapLocationTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MapLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MapLocationTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MapLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MapLocationTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MapLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MapLocationTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MapLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MapLocationTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MapLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MapLocationTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MapLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MapLocationTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MapLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MediaTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MediaTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MediaTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MediaTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MediaTest\\:\\:provideInValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MediaTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MediaTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MediaTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MediaTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MediaTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MediaTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MediaTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MediaTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MediaTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MediaTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MediaTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MediaTest\\:\\:provideValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MediaTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\MediaTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/MediaTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:provideInValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:provideInvalidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:provideValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:provideValidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:testGetName\\(\\) has parameter \\$fieldSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:testGetRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Parameter \\#1 \\$contentId of method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationListTest\\:\\:generateValidationError\\(\\) expects string, int given\\.$#" + count: 2 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Parameter \\#1 \\$value of method Ibexa\\\\Core\\\\FieldType\\\\RelationList\\\\Type\\:\\:getRelations\\(\\) expects Ibexa\\\\Core\\\\FieldType\\\\RelationList\\\\Value, Ibexa\\\\Core\\\\FieldType\\\\Value given\\.$#" + count: 1 + path: tests/lib/FieldType/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:provideInValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:provideValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:testGetName\\(\\) has parameter \\$fieldSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:testGetRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Parameter \\#1 \\$fieldValue of method Ibexa\\\\Core\\\\FieldType\\\\Relation\\\\Type\\:\\:getRelations\\(\\) expects Ibexa\\\\Core\\\\FieldType\\\\Relation\\\\Value, Ibexa\\\\Core\\\\FieldType\\\\Value given\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\RelationTest\\:\\:\\$contentHandler has no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\SelectionTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\SelectionTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\SelectionTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\SelectionTest\\:\\:provideInValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\SelectionTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\SelectionTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\SelectionTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\SelectionTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\SelectionTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\SelectionTest\\:\\:provideValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\SelectionTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\SelectionTest\\:\\:testGetName\\(\\) has parameter \\$fieldSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/SelectionTest.php + + - + message: "#^Parameter \\#1 \\$selection of class Ibexa\\\\Core\\\\FieldType\\\\Selection\\\\Value constructor expects array\\<int\\>, array\\<int, string\\> given\\.$#" + count: 4 + path: tests/lib/FieldType/SelectionTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\FieldType\\\\Validator\\\\StringLengthValidator\\:\\:\\$unexisting\\.$#" + count: 2 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Translation\\\\Plural\\:\\:\\$plural\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Translation\\\\Plural\\:\\:\\$singular\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Access to protected property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Translation\\\\Plural\\:\\:\\$values\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:providerForValidateConstraintsKO\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:providerForValidateConstraintsOK\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:providerForValidateKO\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:providerForValidateOK\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testConstraintsInitializeGet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testConstraintsSetGet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testConstructor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testGetBadConstraint\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testGetConstraintsSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testInitializeBadConstraint\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testSetBadConstraint\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testValidateConstraintsCorrectValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testValidateConstraintsCorrectValues\\(\\) has parameter \\$constraints with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has parameter \\$constraints with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has parameter \\$expectedMessages with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testValidateConstraintsWrongValues\\(\\) has parameter \\$values with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testValidateCorrectValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testValidateCorrectValues\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testValidateWrongValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testValidateWrongValues\\(\\) has parameter \\$messagePlural with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testValidateWrongValues\\(\\) has parameter \\$messageSingular with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testValidateWrongValues\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\StringLengthValidatorTest\\:\\:testValidateWrongValues\\(\\) has parameter \\$values with no type specified\\.$#" + count: 1 + path: tests/lib/FieldType/StringLengthValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextBlockTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextBlockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextBlockTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextBlockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextBlockTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextBlockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextBlockTest\\:\\:provideInValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextBlockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextBlockTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextBlockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextBlockTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextBlockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextBlockTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextBlockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextBlockTest\\:\\:provideValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextBlockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextBlockTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextBlockTest.php + + - + message: "#^Parameter \\#1 \\$text of class Ibexa\\\\Core\\\\FieldType\\\\TextBlock\\\\Value constructor expects string, int given\\.$#" + count: 1 + path: tests/lib/FieldType/TextBlockTest.php + + - + message: "#^Parameter \\#1 \\$text of class Ibexa\\\\Core\\\\FieldType\\\\TextBlock\\\\Value constructor expects string, null given\\.$#" + count: 1 + path: tests/lib/FieldType/TextBlockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextLineTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextLineTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextLineTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextLineTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextLineTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextLineTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextLineTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextLineTest\\:\\:provideInvalidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextLineTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextLineTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TextLineTest\\:\\:provideValidValidatorConfiguration\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TextLineTest.php + + - + message: "#^Parameter \\#1 \\$text of class Ibexa\\\\Core\\\\FieldType\\\\TextLine\\\\Value constructor expects string, int given\\.$#" + count: 1 + path: tests/lib/FieldType/TextLineTest.php + + - + message: "#^Parameter \\#1 \\$text of class Ibexa\\\\Core\\\\FieldType\\\\TextLine\\\\Value constructor expects string, null given\\.$#" + count: 1 + path: tests/lib/FieldType/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TimeTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TimeTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TimeTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TimeTest\\:\\:provideInValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TimeTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TimeTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TimeTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TimeTest\\:\\:provideValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\TimeTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/TimeTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\Gateway\\\\DoctrineStorageTest\\:\\:\\$storageGateway\\.$#" + count: 1 + path: tests/lib/FieldType/Url/Gateway/DoctrineStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\Gateway\\\\DoctrineStorageTest\\:\\:testGetIdUrlMap\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Url/Gateway/DoctrineStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\Gateway\\\\DoctrineStorageTest\\:\\:testGetUrlIdMap\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Url/Gateway/DoctrineStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\Gateway\\\\DoctrineStorageTest\\:\\:testInsertUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Url/Gateway/DoctrineStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\Gateway\\\\DoctrineStorageTest\\:\\:testLinkUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Url/Gateway/DoctrineStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\Gateway\\\\DoctrineStorageTest\\:\\:testUnlinkUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Url/Gateway/DoctrineStorageTest.php + + - + message: "#^Cannot access offset 'urlId' on array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 2 + path: tests/lib/FieldType/Url/UrlStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\UrlStorageTest\\:\\:getContext\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/Url/UrlStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\UrlStorageTest\\:\\:testDeleteFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Url/UrlStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\UrlStorageTest\\:\\:testGetFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Url/UrlStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\UrlStorageTest\\:\\:testGetFieldDataNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Url/UrlStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\UrlStorageTest\\:\\:testGetFieldDataWithEmptyUrlId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Url/UrlStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\UrlStorageTest\\:\\:testHasFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Url/UrlStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\UrlStorageTest\\:\\:testStoreFieldDataWithEmptyUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Url/UrlStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\UrlStorageTest\\:\\:testStoreFieldDataWithExistingUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Url/UrlStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\UrlStorageTest\\:\\:testStoreFieldDataWithNewUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/FieldType/Url/UrlStorageTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\UrlStorageTest\\:\\:\\$gatewayMock \\(Ibexa\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\\\Gateway&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/FieldType/Url/UrlStorageTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\Url\\\\UrlStorageTest\\:\\:\\$loggerMock \\(PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&Psr\\\\Log\\\\LoggerInterface\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/FieldType/Url/UrlStorageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UrlTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UrlTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UrlTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UrlTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UrlTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UrlTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UrlTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UrlTest.php + + - + message: "#^Parameter \\#1 \\$link of class Ibexa\\\\Core\\\\FieldType\\\\Url\\\\Value constructor expects string\\|null, int given\\.$#" + count: 1 + path: tests/lib/FieldType/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:getSettingsSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:getValidatorConfigurationSchemaExpectation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:provideDataForGetName\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:provideInValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:provideInputForFromHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:provideInputForToHash\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:provideInvalidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:provideInvalidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:provideValidDataForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:provideValidFieldSettings\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:provideValidInputForAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:providerForTestCreatePersistenceValue\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:providerForTestValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:testCreatePersistenceValue\\(\\) has parameter \\$expectedFieldValueExternalData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:testCreatePersistenceValue\\(\\) has parameter \\$userValueDate with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\FieldType\\\\UserTest\\:\\:testValidate\\(\\) has parameter \\$expectedValidationErrors with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/FieldType/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\ContentInfoLocationLoader\\\\SudoMainLocationLoaderTest\\:\\:testLoadLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/ContentInfoLocationLoader/SudoMainLocationLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\ContentInfoLocationLoader\\\\SudoMainLocationLoaderTest\\:\\:testLoadLocationError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/ContentInfoLocationLoader/SudoMainLocationLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\ContentInfoLocationLoader\\\\SudoMainLocationLoaderTest\\:\\:testLoadLocationNoMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/ContentInfoLocationLoader/SudoMainLocationLoaderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\ContentPreviewHelperTest\\:\\:testChangeConfigScope\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/ContentPreviewHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\ContentPreviewHelperTest\\:\\:testPreviewActive\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/ContentPreviewHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\ContentPreviewHelperTest\\:\\:testPreviewedContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/ContentPreviewHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\ContentPreviewHelperTest\\:\\:testPreviewedLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/ContentPreviewHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\ContentPreviewHelperTest\\:\\:testRestoreConfigScope\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/ContentPreviewHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\FieldHelperTest\\:\\:testIsFieldEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/FieldHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\FieldHelperTest\\:\\:testIsFieldNotEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/FieldHelperTest.php + + - + message: "#^Constant Ibexa\\\\Tests\\\\Core\\\\Helper\\\\FieldsGroups\\\\ArrayTranslatorFieldsGroupsListTest\\:\\:DEFAULT_GROUP_NAME is unused\\.$#" + count: 1 + path: tests/lib/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\FieldsGroups\\\\ArrayTranslatorFieldsGroupsListTest\\:\\:getArrayTranslatorFieldsGroupsList\\(\\) has parameter \\$groups with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\FieldsGroups\\\\ArrayTranslatorFieldsGroupsListTest\\:\\:getFieldDefinitionMock\\(\\) has parameter \\$constructorArgs with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsListTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Helper\\\\FieldsGroups\\\\ArrayTranslatorFieldsGroupsListTest\\:\\:\\$translatorMock has no type specified\\.$#" + count: 1 + path: tests/lib/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\FieldsGroups\\\\RepositoryConfigFieldsGroupsListFactoryTest\\:\\:testBuild\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/FieldsGroups/RepositoryConfigFieldsGroupsListFactoryTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Helper\\\\FieldsGroups\\\\RepositoryConfigFieldsGroupsListFactoryTest\\:\\:\\$repositoryConfigMock has no type specified\\.$#" + count: 1 + path: tests/lib/Helper/FieldsGroups/RepositoryConfigFieldsGroupsListFactoryTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Helper\\\\FieldsGroups\\\\RepositoryConfigFieldsGroupsListFactoryTest\\:\\:\\$translatorMock has no type specified\\.$#" + count: 1 + path: tests/lib/Helper/FieldsGroups/RepositoryConfigFieldsGroupsListFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\PreviewLocationProviderTest\\:\\:testGetPreviewLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/PreviewLocationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\PreviewLocationProviderTest\\:\\:testGetPreviewLocationDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/PreviewLocationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\PreviewLocationProviderTest\\:\\:testGetPreviewLocationNoLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/PreviewLocationProviderTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Helper\\\\PreviewLocationProviderTest\\:\\:\\$contentService \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Helper/PreviewLocationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:getTranslatedField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:getTranslatedField\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:getTranslatedFieldProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:getTranslatedNameProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:getTranslationSiteAccessProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetAvailableLanguagesWithTranslationSiteAccesses\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetAvailableLanguagesWithoutTranslationSiteAccesses\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetTranslatedName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetTranslatedName\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetTranslatedNameByContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetTranslatedNameByContentInfo\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetTranslatedNameByContentInfoForcedLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetTranslatedNameByContentInfoForcedLanguageMainLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetTranslatedNameForcedLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetTranslationSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetTranslationSiteAccess\\(\\) has parameter \\$expectedResult with no type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetTranslationSiteAccess\\(\\) has parameter \\$language with no type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetTranslationSiteAccess\\(\\) has parameter \\$relatedSiteAccesses with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetTranslationSiteAccess\\(\\) has parameter \\$translationSiteAccesses with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:testGetTranslationSiteAccessUnkownLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Helper\\\\TranslationHelperTest\\:\\:\\$siteAccessByLanguages has no type specified\\.$#" + count: 1 + path: tests/lib/Helper/TranslationHelperTest.php + + - + message: "#^Parameter \\#1 \\$internalId of method Ibexa\\\\Core\\\\IO\\\\ConfigScopeChangeAwareIOService\\:\\:getExternalPath\\(\\) expects string, int given\\.$#" + count: 1 + path: tests/lib/IO/ConfigScopeChangeAwareIOServiceTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\IO\\\\ConfigScopeChangeAwareIOServiceTest\\:\\:\\$innerIOService \\(Ibexa\\\\Core\\\\IO\\\\ConfigScopeChangeAwareIOService&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept Ibexa\\\\Core\\\\IO\\\\IOServiceInterface&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/IO/ConfigScopeChangeAwareIOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\FilePathNormalizer\\\\FlysystemTest\\:\\:providerForTestNormalizePath\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/IO/FilePathNormalizer/FlysystemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\Flysystem\\\\VisibilityConverter\\\\BaseVisibilityConverterTest\\:\\:getDataForTestForDirectory\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/IO/Flysystem/VisibilityConverter/BaseVisibilityConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\Flysystem\\\\VisibilityConverter\\\\BaseVisibilityConverterTest\\:\\:getDataForTestForFile\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/IO/Flysystem/VisibilityConverter/BaseVisibilityConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\Flysystem\\\\VisibilityConverter\\\\BaseVisibilityConverterTest\\:\\:getDataForTestInverseForDirectory\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/IO/Flysystem/VisibilityConverter/BaseVisibilityConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\Flysystem\\\\VisibilityConverter\\\\BaseVisibilityConverterTest\\:\\:getDataForTestInverseForFile\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/IO/Flysystem/VisibilityConverter/BaseVisibilityConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\Flysystem\\\\VisibilityConverter\\\\DFSVisibilityConverterTest\\:\\:getDataForTestForDirectory\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/IO/Flysystem/VisibilityConverter/DFSVisibilityConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\Flysystem\\\\VisibilityConverter\\\\DFSVisibilityConverterTest\\:\\:getDataForTestForFile\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/IO/Flysystem/VisibilityConverter/DFSVisibilityConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\Flysystem\\\\VisibilityConverter\\\\DFSVisibilityConverterTest\\:\\:getDataForTestInverseForDirectory\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/IO/Flysystem/VisibilityConverter/DFSVisibilityConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\Flysystem\\\\VisibilityConverter\\\\DFSVisibilityConverterTest\\:\\:getDataForTestInverseForFile\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/IO/Flysystem/VisibilityConverter/DFSVisibilityConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\Flysystem\\\\VisibilityConverter\\\\SiteAccessAwareVisibilityConverterTest\\:\\:getDataForTestForDirectory\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\Flysystem\\\\VisibilityConverter\\\\SiteAccessAwareVisibilityConverterTest\\:\\:getDataForTestForFile\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\Flysystem\\\\VisibilityConverter\\\\SiteAccessAwareVisibilityConverterTest\\:\\:getDataForTestInverseForDirectory\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\Flysystem\\\\VisibilityConverter\\\\SiteAccessAwareVisibilityConverterTest\\:\\:getDataForTestInverseForFile\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\FlysystemTest\\:\\:testDelete\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOBinarydataHandler/FlysystemTest.php + + - + message: "#^Parameter \\#1 \\$inputStream of method Ibexa\\\\Contracts\\\\Core\\\\IO\\\\BinaryFileCreateStruct\\:\\:setInputStream\\(\\) expects resource, resource\\|false given\\.$#" + count: 1 + path: tests/lib/IO/IOBinarydataHandler/FlysystemTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\IO\\\\BinaryFileCreateStruct\\:\\:\\$mtime \\(DateTime\\) does not accept int\\.$#" + count: 1 + path: tests/lib/IO/IOBinarydataHandler/FlysystemTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\FlysystemTest\\:\\:\\$handler \\(Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept Ibexa\\\\Core\\\\IO\\\\IOBinarydataHandler\\\\Flysystem\\.$#" + count: 1 + path: tests/lib/IO/IOBinarydataHandler/FlysystemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOMetadataHandler\\\\FlysystemTest\\:\\:getDataForFileExists\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/IO/IOMetadataHandler/FlysystemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOMetadataHandler\\\\FlysystemTest\\:\\:testDelete\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOMetadataHandler/FlysystemTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOMetadataHandler\\\\FlysystemTest\\:\\:\\$handler \\(Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\Flysystem\\.$#" + count: 1 + path: tests/lib/IO/IOMetadataHandler/FlysystemTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\IO\\\\BinaryFileCreateStruct\\:\\:\\$mtime \\(DateTime\\) does not accept int\\.$#" + count: 1 + path: tests/lib/IO/IOMetadataHandler/LegacyDFSClusterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOMetadataHandler\\\\LegacyDFSClusterTest\\:\\:\\$handler \\(Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept Ibexa\\\\Core\\\\IO\\\\IOMetadataHandler\\\\LegacyDFSCluster\\.$#" + count: 1 + path: tests/lib/IO/IOMetadataHandler/LegacyDFSClusterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:getPrefixedUri\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:getPrefixedUri\\(\\) has parameter \\$uri with no type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testCreateBinaryFile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testDeleteBinaryFile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testDeleteDirectory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testExists\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testExistsNot\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testGetFileContents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testGetFileInputStream\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testGetMimeType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testLoadBinaryFile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testLoadBinaryFileByUri\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testLoadBinaryFileNoMetadataUri\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testNewBinaryCreateStructFromLocalFile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\IOServiceTest\\:\\:testNewBinaryCreateStructFromUploadedFile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\IO\\\\BinaryFile\\:\\:\\$size \\(int\\) does not accept int\\<0, max\\>\\|false\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Unreachable statement \\- code above always terminates\\.$#" + count: 1 + path: tests/lib/IO/IOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\MetadataHandler\\\\ImageSizeTest\\:\\:testExtract\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/MetadataHandler/ImageSizeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\MimeTypeDetector\\\\FileInfoTest\\:\\:getFixture\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/MimeTypeDetector/FileInfoTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\MimeTypeDetector\\\\FileInfoTest\\:\\:testGetFromBuffer\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/MimeTypeDetector/FileInfoTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\MimeTypeDetector\\\\FileInfoTest\\:\\:testGetFromPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/MimeTypeDetector/FileInfoTest.php + + - + message: "#^Parameter \\#1 \\$path of method Ibexa\\\\Core\\\\IO\\\\MimeTypeDetector\\\\FileInfo\\:\\:getFromBuffer\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: tests/lib/IO/MimeTypeDetector/FileInfoTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\TolerantIOServiceTest\\:\\:testCreateMissingBinaryFile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/TolerantIOServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlDecorator\\\\AbsolutePrefixTest\\:\\:provideData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/IO/UrlDecorator/AbsolutePrefixTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlDecorator\\\\PrefixTest\\:\\:provideData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/UrlDecorator/PrefixTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlDecorator\\\\PrefixTest\\:\\:testDecorate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/UrlDecorator/PrefixTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlDecorator\\\\PrefixTest\\:\\:testDecorate\\(\\) has parameter \\$decoratedUrl with no type specified\\.$#" + count: 1 + path: tests/lib/IO/UrlDecorator/PrefixTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlDecorator\\\\PrefixTest\\:\\:testDecorate\\(\\) has parameter \\$prefix with no type specified\\.$#" + count: 1 + path: tests/lib/IO/UrlDecorator/PrefixTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlDecorator\\\\PrefixTest\\:\\:testDecorate\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/IO/UrlDecorator/PrefixTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlDecorator\\\\PrefixTest\\:\\:testUndecorate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/UrlDecorator/PrefixTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlDecorator\\\\PrefixTest\\:\\:testUndecorate\\(\\) has parameter \\$decoratedUrl with no type specified\\.$#" + count: 1 + path: tests/lib/IO/UrlDecorator/PrefixTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlDecorator\\\\PrefixTest\\:\\:testUndecorate\\(\\) has parameter \\$prefix with no type specified\\.$#" + count: 1 + path: tests/lib/IO/UrlDecorator/PrefixTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlDecorator\\\\PrefixTest\\:\\:testUndecorate\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/IO/UrlDecorator/PrefixTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlRedecoratorTest\\:\\:testRedecorateFromSource\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/UrlRedecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlRedecoratorTest\\:\\:testRedecorateFromTarget\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/IO/UrlRedecoratorTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\IO\\\\UrlRedecoratorTest\\:\\:\\$redecorator \\(Ibexa\\\\Core\\\\IO\\\\UrlRedecorator&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept Ibexa\\\\Core\\\\IO\\\\UrlRedecorator\\.$#" + count: 1 + path: tests/lib/IO/UrlRedecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\Base\\:\\:getPersistenceMock\\(\\) has parameter \\$mockMethods with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/Base.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\Base\\:\\:getUserMock\\(\\) has parameter \\$mockMethods with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/Base.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:providerForTestAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:providerForTestAcceptValueException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:providerForTestEvaluate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:providerForTestEvaluateInvalidArgument\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:providerForTestValidateError\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:providerForTestValidatePass\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:testAcceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:testAcceptValueException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:testBuildValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:testEvaluate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:testGetCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:testValidateError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:testValidatePass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\BlockingLimitationTypeTest\\:\\:testValueSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/BlockingLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:providerForTestAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:providerForTestAcceptValueException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:providerForTestEvaluate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:providerForTestEvaluateInvalidArgument\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:providerForTestValidateError\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:providerForTestValidatePass\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testAcceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testAcceptValueException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testBuildValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testEvaluate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testGetCriterionInvalidValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testGetCriterionMultipleValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testGetCriterionSingleValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testValidateError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testValidatePass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ContentTypeLimitationTypeTest\\:\\:testValueSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LanguageLimitation\\\\ContentDeleteEvaluatorTest\\:\\:dataProviderForAccept\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Limitation/LanguageLimitation/ContentDeleteEvaluatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LanguageLimitation\\\\ContentDeleteEvaluatorTest\\:\\:dataProviderForEvaluate\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Limitation/LanguageLimitation/ContentDeleteEvaluatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LanguageLimitation\\\\ContentDeleteEvaluatorTest\\:\\:getLanguageLimitation\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/LanguageLimitation/ContentDeleteEvaluatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LanguageLimitation\\\\ContentDeleteEvaluatorTest\\:\\:getTergetVersion\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/LanguageLimitation/ContentDeleteEvaluatorTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:providerForTestAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:providerForTestAcceptValueException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:providerForTestEvaluate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:providerForTestEvaluateInvalidArgument\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:providerForTestValidateError\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:providerForTestValidatePass\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testAcceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testAcceptValueException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testBuildValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testEvaluate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$persistenceLocations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$targets with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has parameter \\$persistenceLocations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has parameter \\$targets with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testGetCriterionInvalidValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testGetCriterionMultipleValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testGetCriterionSingleValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testValidateError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testValidatePass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\LocationLimitationTypeTest\\:\\:testValueSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/LocationLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\MemberOfLimitationTypeTest\\:\\:providerForTestAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/MemberOfLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\MemberOfLimitationTypeTest\\:\\:providerForTestAcceptValueException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/MemberOfLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\MemberOfLimitationTypeTest\\:\\:providerForTestEvaluate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/MemberOfLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\MemberOfLimitationTypeTest\\:\\:providerForTestEvaluateSelfGroup\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/MemberOfLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\MemberOfLimitationTypeTest\\:\\:providerForTestValidateError\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/MemberOfLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\MemberOfLimitationTypeTest\\:\\:testEvaluateSelfGroup\\(\\) has parameter \\$currentUserGroupLocations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/MemberOfLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:providerForTestAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:providerForTestAcceptValueException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:providerForTestEvaluate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:providerForTestEvaluateInvalidArgument\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:providerForTestValidateError\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:providerForTestValidatePass\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:testAcceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:testAcceptValueException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:testBuildValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:testEvaluate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:testGetCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:testValidateError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:testValidatePass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\NewObjectStateLimitationTypeTest\\:\\:testValueSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/NewObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ObjectStateLimitationTypeTest\\:\\:providerForTestEvaluate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ObjectStateLimitationTypeTest\\:\\:testEvaluate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ObjectStateLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ObjectStateLimitationTypeTest\\:\\:testGetCriterionInvalidValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ObjectStateLimitationTypeTest\\:\\:testGetCriterionMultipleValuesFromMultipleGroups\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ObjectStateLimitationTypeTest\\:\\:testGetCriterionMultipleValuesFromSingleGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ObjectStateLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ObjectStateLimitationTypeTest\\:\\:testGetCriterionSingleValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ObjectStateLimitationTypeTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ObjectStateLimitationTypeTest\\:\\:\\$loadObjectStatesMap type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ObjectStateLimitationTypeTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:assertContentHandlerExpectations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:assertContentHandlerExpectations\\(\\) has parameter \\$callNo with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:assertContentHandlerExpectations\\(\\) has parameter \\$contentId with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:assertContentHandlerExpectations\\(\\) has parameter \\$contentInfo with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:assertContentHandlerExpectations\\(\\) has parameter \\$persistenceCalled with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:getTestEvaluateContentMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:getTestEvaluateVersionInfoMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:providerForTestAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:providerForTestAcceptValueException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:providerForTestEvaluate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:providerForTestEvaluateInvalidArgument\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:providerForTestValidateError\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:providerForTestValidatePass\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:testAcceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:testAcceptValueException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:testBuildValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:testEvaluate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$persistence with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$targets with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has parameter \\$targets with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:testGetCriterionInvalidValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:testValidateError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:testValidatePass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentContentTypeLimitationTypeTest\\:\\:testValueSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Method PHPUnit\\\\Framework\\\\TestCase\\:\\:once\\(\\) invoked with 1 parameter, 0 required\\.$#" + count: 1 + path: tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:providerForTestAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:providerForTestAcceptValueException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:providerForTestEvaluate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:providerForTestEvaluateInvalidArgument\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:providerForTestValidatePass\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:testAcceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:testAcceptValueException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:testBuildValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:testEvaluate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$persistenceLocations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$targets with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has parameter \\$persistenceLocations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has parameter \\$targets with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\ParentDepthLimitationTypeTest\\:\\:testValidatePass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/ParentDepthLimitationTypeTest.php + + - + message: "#^Instanceof between Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserRoleAssignment and Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\User will always evaluate to false\\.$#" + count: 1 + path: tests/lib/Limitation/RoleLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\RoleLimitationTypeTest\\:\\:providerForTestAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/RoleLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\RoleLimitationTypeTest\\:\\:providerForTestAcceptValueException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/RoleLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\RoleLimitationTypeTest\\:\\:providerForTestEvaluate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/RoleLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\RoleLimitationTypeTest\\:\\:providerForTestValidateError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/RoleLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\RoleLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$targets with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/RoleLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:providerForTestAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:providerForTestAcceptValueException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:providerForTestEvaluate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:providerForTestEvaluateInvalidArgument\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:providerForTestValidateError\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:providerForTestValidatePass\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testAcceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testAcceptValueException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testBuildValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testEvaluate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$targets with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has parameter \\$targets with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testGetCriterionInvalidValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testGetCriterionMultipleValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testGetCriterionSingleValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testValidateError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testValidatePass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SectionLimitationTypeTest\\:\\:testValueSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SectionLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:providerForTestAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:providerForTestAcceptValueException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:providerForTestEvaluate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:providerForTestEvaluateInvalidArgument\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:providerForTestValidateError\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:testAcceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:testAcceptValueException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:testBuildValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:testEvaluate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:testGetCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:testValidateError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SiteAccessLimitationTypeTest\\:\\:testValueSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SiteAccessLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:getContentMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:getVersionInfoMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:getVersionInfoMock\\(\\) has parameter \\$shouldBeCalled with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:providerForTestAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:providerForTestAcceptValueException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:providerForTestEvaluate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:providerForTestEvaluateInvalidArgument\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:providerForTestValidateError\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:testAcceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:testAcceptValueException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:testBuildValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:testEvaluate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:testGetCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:testValidateError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\StatusLimitationTypeTest\\:\\:testValueSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/StatusLimitationTypeTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:providerForTestAcceptValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:providerForTestAcceptValueException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:providerForTestEvaluate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:providerForTestEvaluateInvalidArgument\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:providerForTestValidateError\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:providerForTestValidatePass\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testAcceptValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testAcceptValueException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testBuildValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testEvaluate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$persistenceLocations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testEvaluate\\(\\) has parameter \\$targets with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has parameter \\$persistenceLocations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testEvaluateInvalidArgument\\(\\) has parameter \\$targets with no type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testGetCriterionInvalidValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testGetCriterionMultipleValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testGetCriterionSingleValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testValidateError\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testValidateErrorWrongPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testValidatePass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Limitation\\\\SubtreeLimitationTypeTest\\:\\:testValueSchema\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Limitation/SubtreeLimitationTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\SimplifiedRequestNormalizerTest\\:\\:testNormalize\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Component/Serializer/SimplifiedRequestNormalizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\SimplifiedRequestNormalizerTest\\:\\:testSupportsNormalization\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Component/Serializer/SimplifiedRequestNormalizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\Stubs\\\\CompoundStub\\:\\:__construct\\(\\) has parameter \\$subMatchers with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Component/Serializer/Stubs/CompoundStub.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\Stubs\\\\MatcherStub\\:\\:__construct\\(\\) has parameter \\$data with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Component/Serializer/Stubs/MatcherStub.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\Stubs\\\\MatcherStub\\:\\:getData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Component/Serializer/Stubs/MatcherStub.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\Stubs\\\\MatcherStub\\:\\:setRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Component/Serializer/Stubs/MatcherStub.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\Stubs\\\\SerializerStub\\:\\:deserialize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\Stubs\\\\SerializerStub\\:\\:deserialize\\(\\) has parameter \\$format with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\Stubs\\\\SerializerStub\\:\\:deserialize\\(\\) has parameter \\$type with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Component\\\\Serializer\\\\Stubs\\\\SerializerStub\\:\\:serialize\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\ControllerTest\\:\\:testRender\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Controller/ControllerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\ControllerTest\\:\\:testRenderWithResponse\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Controller/ControllerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderControllerTest\\:\\:assertRenderQueryResult\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Controller\\\\QueryRenderControllerTest\\:\\:configureMocks\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php + + - + message: "#^Parameter \\#1 \\$adapter of class Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\Pagerfanta constructor expects Ibexa\\\\Core\\\\Pagination\\\\Pagerfanta\\\\SearchResultAdapter, Pagerfanta\\\\Adapter\\\\AdapterInterface given\\.$#" + count: 2 + path: tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\ContentCacheClearEventTest\\:\\:setLocationsToClear\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Event/ContentCacheClearEventTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\ContentCacheClearEventTest\\:\\:testAddLocationsToClear\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Event/ContentCacheClearEventTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\ContentCacheClearEventTest\\:\\:testConstruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Event/ContentCacheClearEventTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\InteractiveLoginEventTest\\:\\:testGetSetAPIUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Event/InteractiveLoginEventTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\RouteReferenceGenerationEventTest\\:\\:testConstruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Event/RouteReferenceGenerationEventTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\RouteReferenceGenerationEventTest\\:\\:testGetSet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Event/RouteReferenceGenerationEventTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Event\\\\ScopeChangeEventTest\\:\\:testGetSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Event/ScopeChangeEventTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\ContentViewTwigVariablesSubscriberTest\\:\\:getRegistry\\(\\) has parameter \\$providers with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriberTest.php + + - + message: "#^Method class@anonymous/tests/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriberTest\\.php\\:47\\:\\:getTwigVariables\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriberTest.php + + - + message: "#^Property class@anonymous/tests/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriberTest\\.php\\:47\\:\\:\\$identifier has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriberTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\LanguageSwitchListenerTest\\:\\:testGetSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/EventListener/LanguageSwitchListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\LanguageSwitchListenerTest\\:\\:testOnRouteReferenceGeneration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/EventListener/LanguageSwitchListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\LanguageSwitchListenerTest\\:\\:testOnRouteReferenceGenerationNoLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/EventListener/LanguageSwitchListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\LanguageSwitchListenerTest\\:\\:testOnRouteReferenceGenerationNoTranslationSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/EventListener/LanguageSwitchListenerTest.php + + - + message: "#^Parameter \\#1 \\$translationHelper of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\LanguageSwitchListener constructor expects Ibexa\\\\Core\\\\Helper\\\\TranslationHelper, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 3 + path: tests/lib/MVC/Symfony/EventListener/LanguageSwitchListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\SiteAccessMatchListenerTest\\:\\:testGetSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\SiteAccessMatchListenerTest\\:\\:testOnKernelRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\SiteAccessMatchListenerTest\\:\\:testOnKernelRequestSerializedSA\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\SiteAccessMatchListenerTest\\:\\:testOnKernelRequestSerializedSAWithCompoundMatcher\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\SiteAccessMatchListenerTest\\:\\:testOnKernelRequestSiteAccessPresent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\EventListener\\\\SiteAccessMatchListenerTest\\:\\:testOnKernelRequestUserHashWithOriginalRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\ImageAsset\\\\ParameterProviderTest\\:\\:dataProviderForTestGetViewParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/ImageAsset/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\ImageAsset\\\\ParameterProviderTest\\:\\:testGetViewParameters\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/ImageAsset/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\ImageAsset\\\\ParameterProviderTest\\:\\:testGetViewParameters\\(\\) has parameter \\$status with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/ImageAsset/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\Relation\\\\ParameterProviderTest\\:\\:providerForTestGetViewParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/Relation/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\Relation\\\\ParameterProviderTest\\:\\:testGetViewParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/Relation/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\Relation\\\\ParameterProviderTest\\:\\:testGetViewParameters\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/Relation/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\Relation\\\\ParameterProviderTest\\:\\:testGetViewParameters\\(\\) has parameter \\$status with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/Relation/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\Relation\\\\ParameterProviderTest\\:\\:testNotFoundGetViewParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/Relation/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\Relation\\\\ParameterProviderTest\\:\\:testUnauthorizedGetViewParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/Relation/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\RelationList\\\\ParameterProviderTest\\:\\:providerForTestGetViewParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/RelationList/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\RelationList\\\\ParameterProviderTest\\:\\:testGetViewParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/RelationList/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\RelationList\\\\ParameterProviderTest\\:\\:testGetViewParameters\\(\\) has parameter \\$desinationContentIds with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/RelationList/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\RelationList\\\\ParameterProviderTest\\:\\:testGetViewParameters\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/RelationList/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\RelationList\\\\ParameterProviderTest\\:\\:testNotFoundGetViewParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/RelationList/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\RelationList\\\\ParameterProviderTest\\:\\:testUnauthorizedGetViewParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/RelationList/ParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProvider\\\\LocaleParameterProviderTest\\:\\:getLocaleConverterMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProvider\\\\LocaleParameterProviderTest\\:\\:getRequestStackMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProvider\\\\LocaleParameterProviderTest\\:\\:getRequestStackMock\\(\\) has parameter \\$hasLocale with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProvider\\\\LocaleParameterProviderTest\\:\\:providerForTestGetViewParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProvider\\\\LocaleParameterProviderTest\\:\\:testGetViewParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProvider\\\\LocaleParameterProviderTest\\:\\:testGetViewParameters\\(\\) has parameter \\$expectedLocale with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProvider\\\\LocaleParameterProviderTest\\:\\:testGetViewParameters\\(\\) has parameter \\$hasRequestLocale with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProviderRegistryTest\\:\\:testGetParameterProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/View/ParameterProviderRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProviderRegistryTest\\:\\:testGetParameterProviderFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/View/ParameterProviderRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\FieldType\\\\View\\\\ParameterProviderRegistryTest\\:\\:testSetHasParameterProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/FieldType/View/ParameterProviderRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\LocaleConverterTest\\:\\:convertToPOSIXProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Locale/LocaleConverterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\LocaleConverterTest\\:\\:\\$conversionMap \\(array\\{array\\{string, string\\|null\\}\\}\\) does not accept array\\{eng\\-GB\\: 'en_GB', eng\\-US\\: 'en_US', fre\\-FR\\: 'fr_FR', ger\\-DE\\: 'de_DE', nor\\-NO\\: 'no_NO', cro\\-HR\\: 'hr_HR'\\}\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Locale/LocaleConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\UserLanguagePreferenceProviderTest\\:\\:getLanguageCodesMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\UserLanguagePreferenceProviderTest\\:\\:providerForTestGetPreferredLanguages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\UserLanguagePreferenceProviderTest\\:\\:providerForTestGetPreferredLanguagesWithUserPreferredLanguage\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\UserLanguagePreferenceProviderTest\\:\\:testGetPreferredLanguagesWithUserPreferredLanguage\\(\\) has parameter \\$expectedEzLanguageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\UserLanguagePreferenceProviderTest\\:\\:testGetPreferredLanguagesWithUserPreferredLanguage\\(\\) has parameter \\$userLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\UserLanguagePreferenceProviderTest\\:\\:testGetPreferredLanguagesWithoutUserLanguage\\(\\) has parameter \\$expectedEzLanguageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\UserLanguagePreferenceProviderTest\\:\\:testGetPreferredLanguagesWithoutUserLanguage\\(\\) has parameter \\$userLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderTest.php + + - + message: "#^Parameter \\#1 \\$filename of static method Symfony\\\\Component\\\\Yaml\\\\Yaml\\:\\:parseFile\\(\\) expects string, string\\|false given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\UserLanguagePreferenceProviderTest\\:\\:\\$userLanguagePreferenceProvider is never read, only written\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\BaseTest\\:\\:getContentInfoMock\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\BaseTest\\:\\:getLocationMock\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\BaseTest\\:\\:getPartiallyMockedViewProvider\\(\\) has parameter \\$matchingConfig with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\BaseTest\\:\\:getPermissionResolverMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/BaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\DepthTest\\:\\:matchContentInfoProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/DepthTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\DepthTest\\:\\:matchLocationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/DepthTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\DepthTest\\:\\:testMatchContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/DepthTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\DepthTest\\:\\:testMatchLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/DepthTest.php + + - + message: "#^Parameter \\#1 \\$contentInfo of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Depth\\:\\:matchContentInfo\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/DepthTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTest\\:\\:matchContentInfoProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTest\\:\\:matchLocationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTest\\:\\:testMatchContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTest\\:\\:testMatchLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTypeGroupTest\\:\\:matchContentInfoProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTypeGroupTest\\:\\:matchLocationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTypeGroupTest\\:\\:testMatchContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTypeGroupTest\\:\\:testMatchLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroupTest.php + + - + message: "#^Parameter \\#1 \\$contentInfo of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTypeGroup\\:\\:matchContentInfo\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroupTest.php + + - + message: "#^Parameter \\#1 \\$location of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTypeGroup\\:\\:matchLocation\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTypeTest\\:\\:matchContentInfoProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTypeTest\\:\\:matchLocationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTypeTest\\:\\:testMatchContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ContentTypeTest\\:\\:testMatchLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\LocationTest\\:\\:matchContentInfoProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/LocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\LocationTest\\:\\:matchLocationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/LocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\LocationTest\\:\\:testMatchContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/LocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\LocationTest\\:\\:testMatchLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/LocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ParentContentTypeTest\\:\\:matchLocationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ParentContentTypeTest\\:\\:testMatchContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ParentContentTypeTest\\:\\:testMatchLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentTypeTest.php + + - + message: "#^Parameter \\#1 \\$contentInfo of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ParentContentType\\:\\:matchContentInfo\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentTypeTest.php + + - + message: "#^Parameter \\#1 \\$location of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ParentContentType\\:\\:matchLocation\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ParentLocationTest\\:\\:matchContentInfoProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ParentLocationTest\\:\\:matchLocationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ParentLocationTest\\:\\:testMatchContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ParentLocationTest\\:\\:testMatchLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentLocationTest.php + + - + message: "#^Parameter \\#1 \\$contentInfo of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\ParentLocation\\:\\:matchContentInfo\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\RemoteTest\\:\\:matchContentInfoProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/RemoteTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\RemoteTest\\:\\:matchLocationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/RemoteTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\RemoteTest\\:\\:testMatchContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/RemoteTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\RemoteTest\\:\\:testMatchLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/RemoteTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\SectionTest\\:\\:matchContentInfoProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/SectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\SectionTest\\:\\:matchLocationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/SectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\SectionTest\\:\\:testMatchContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/SectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\SectionTest\\:\\:testMatchLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Id/SectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\ContentTypeTest\\:\\:matchContentInfoProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\ContentTypeTest\\:\\:matchLocationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\ContentTypeTest\\:\\:testMatchContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\ContentTypeTest\\:\\:testMatchLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ContentTypeTest.php + + - + message: "#^Parameter \\#1 \\$contentInfo of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\ContentType\\:\\:matchContentInfo\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ContentTypeTest.php + + - + message: "#^Parameter \\#1 \\$contentTypeIdentifier of method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\ContentTypeTest\\:\\:generateRepositoryMockForContentTypeIdentifier\\(\\) expects int, string given\\.$#" + count: 8 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ContentTypeTest.php + + - + message: "#^Parameter \\#1 \\$location of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\ContentType\\:\\:matchLocation\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\ParentContentTypeTest\\:\\:matchLocationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\ParentContentTypeTest\\:\\:testMatchContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\ParentContentTypeTest\\:\\:testMatchLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentTypeTest.php + + - + message: "#^Parameter \\#1 \\$contentInfo of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\ParentContentType\\:\\:matchContentInfo\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentTypeTest.php + + - + message: "#^Parameter \\#1 \\$location of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\ParentContentType\\:\\:matchLocation\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\SectionTest\\:\\:matchSectionProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/SectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\SectionTest\\:\\:testMatchContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/SectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\SectionTest\\:\\:testMatchLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/SectionTest.php + + - + message: "#^Parameter \\#1 \\$contentInfo of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\Section\\:\\:matchContentInfo\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/SectionTest.php + + - + message: "#^Parameter \\#1 \\$location of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Identifier\\\\Section\\:\\:matchLocation\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/SectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\MultipleValuedTest\\:\\:getMultipleValuedMatcherMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/MultipleValuedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\MultipleValuedTest\\:\\:matchingConfigProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/MultipleValuedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\MultipleValuedTest\\:\\:testInjectRepository\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/MultipleValuedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\MultipleValuedTest\\:\\:testSetMatchingConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/MultipleValuedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\MultipleValuedTest\\:\\:testSetMatchingConfig\\(\\) has parameter \\$matchingConfig with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/MultipleValuedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\UrlAliasTest\\:\\:matchLocationProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\UrlAliasTest\\:\\:setMatchingConfigProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\UrlAliasTest\\:\\:testMatchContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\UrlAliasTest\\:\\:testMatchLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\UrlAliasTest\\:\\:testSetMatchingConfig\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/UrlAliasTest.php + + - + message: "#^Parameter \\#1 \\$contentInfo of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\UrlAlias\\:\\:matchContentInfo\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\ContentInfo, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/UrlAliasTest.php + + - + message: "#^Parameter \\#1 \\$location of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\UrlAlias\\:\\:matchLocation\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/ContentBased/UrlAliasTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface\\:\\:expects\\(\\)\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecoratorTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ConfigurableMatcherFactoryInterface\\:\\:expects\\(\\)\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\DynamicallyConfiguredMatcherFactoryDecoratorTest\\:\\:matchConfigProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\DynamicallyConfiguredMatcherFactoryDecoratorTest\\:\\:testMatch\\(\\) has parameter \\$matchedConfig with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\DynamicallyConfiguredMatcherFactoryDecoratorTest\\:\\:testMatch\\(\\) has parameter \\$namespace with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\DynamicallyConfiguredMatcherFactoryDecoratorTest\\:\\:testMatch\\(\\) has parameter \\$parameterName with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\DynamicallyConfiguredMatcherFactoryDecoratorTest\\:\\:testMatch\\(\\) has parameter \\$scope with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\DynamicallyConfiguredMatcherFactoryDecoratorTest\\:\\:testMatch\\(\\) has parameter \\$viewsConfiguration with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\GeneratorTest\\:\\:generateProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/GeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\GeneratorTest\\:\\:testGenerateWithSiteAccessNoReverseMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/GeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\GeneratorTest\\:\\:testGenerateWithSiteAccessNoReverseMatch\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/GeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\GeneratorTest\\:\\:testGenerateWithSiteAccessNoReverseMatch\\(\\) has parameter \\$referenceType with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/GeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\GeneratorTest\\:\\:testGenerateWithSiteAccessNoReverseMatch\\(\\) has parameter \\$urlResource with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/GeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\GeneratorTest\\:\\:testSimpleGenerate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/GeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\GeneratorTest\\:\\:testSimpleGenerate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/GeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\GeneratorTest\\:\\:testSimpleGenerate\\(\\) has parameter \\$referenceType with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/GeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\GeneratorTest\\:\\:testSimpleGenerate\\(\\) has parameter \\$urlResource with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/GeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReferenceGeneratorTest\\:\\:generateGenerator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/RouteReferenceGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReferenceGeneratorTest\\:\\:testGenerate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/RouteReferenceGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReferenceGeneratorTest\\:\\:testGenerate\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/RouteReferenceGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReferenceGeneratorTest\\:\\:testGenerate\\(\\) has parameter \\$resource with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/RouteReferenceGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReferenceGeneratorTest\\:\\:testGenerateNullResource\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/RouteReferenceGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReferenceGeneratorTest\\:\\:testGenerateNullResourceAndPassedParams\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/RouteReferenceGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReferenceGeneratorTest\\:\\:testGenerateNullResourceWithoutRoute\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/RouteReferenceGeneratorTest.php + + - + message: "#^Parameter \\#1 \\$dispatcher of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\Generator\\\\RouteReferenceGenerator constructor expects Symfony\\\\Component\\\\EventDispatcher\\\\EventDispatcherInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 4 + path: tests/lib/MVC/Symfony/Routing/RouteReferenceGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReferenceTest\\:\\:testConstruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/RouteReferenceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReferenceTest\\:\\:testGetSetParams\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/RouteReferenceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReferenceTest\\:\\:testGetSetRoute\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/RouteReferenceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\RouteReferenceTest\\:\\:testRemoveParam\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/RouteReferenceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequestTest\\:\\:fromUrlProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/SimplifiedRequestTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\SimplifiedRequestTest\\:\\:testFromUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/SimplifiedRequestTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:getPermissionResolverMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:providerTestDoGenerate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:providerTestDoGenerateRootLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:providerTestDoGenerateWithSiteaccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:providerTestIsPrefixExcluded\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testDoGenerate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testDoGenerate\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testDoGenerate\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testDoGenerateNoUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testDoGenerateRootLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testDoGenerateRootLocation\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testDoGenerateRootLocation\\(\\) has parameter \\$isOutsideAndNotExcluded with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testDoGenerateRootLocation\\(\\) has parameter \\$pathPrefix with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testDoGenerateWithSiteAccessParam\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testDoGenerateWithSiteAccessParam\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testGetPathPrefixByRootLocationId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testIsPrefixExcluded\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testIsPrefixExcluded\\(\\) has parameter \\$expectedIsExcluded with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testIsPrefixExcluded\\(\\) has parameter \\$uri with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasGeneratorTest\\:\\:testLoadLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:getRequestByPathInfo\\(\\) has parameter \\$pathInfo with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:providerTestSupports\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testGenerateFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testGenerateInvalidLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testGenerateNoLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testGenerateWithContentId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testGenerateWithContentIdWithMissingMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testGenerateWithLocationAsParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testGenerateWithLocationId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testGetRouteCollection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestLocationCorrectCaseUriPrefixExcluded\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestLocationCustom\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestLocationCustomForward\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestLocationHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestLocationWithCaseRedirect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestLocationWrongCaseUriPrefixExcluded\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestResource\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestResourceCaseIncorrectWithForwardRedirect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestResourceWithCaseRedirect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestResourceWithRedirect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestVirtual\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testMatchRequestVirtualWithCaseRedirect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testRequestContext\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testSupports\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testSupports\\(\\) has parameter \\$isSupported with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:testSupports\\(\\) has parameter \\$routeReference with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Routing\\\\UrlAliasRouterTest\\:\\:\\$requestContext has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\AnonymousAuthenticationProviderTest\\:\\:testAuthenticate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/AnonymousAuthenticationProviderTest.php + + - + message: "#^Parameter \\#1 \\$configResolver of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\AnonymousAuthenticationProvider\\:\\:setConfigResolver\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/AnonymousAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\DefaultAuthenticationSuccessHandlerTest\\:\\:testSetConfigResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/DefaultAuthenticationSuccessHandlerTest.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Security\\\\Core\\\\Authentication\\\\Token\\\\TokenInterface\\:\\:getProviderKey\\(\\)\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/GuardRepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\GuardRepositoryAuthenticationProviderTest\\:\\:testAuthenticate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/GuardRepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\GuardRepositoryAuthenticationProviderTest\\:\\:testAuthenticateUnsupportedToken\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/GuardRepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\GuardRepositoryAuthenticationProviderTest\\:\\:testAuthenticateWrongGuardProviderKey\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/GuardRepositoryAuthenticationProviderTest.php + + - + message: "#^Parameter \\#1 \\$token of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\GuardRepositoryAuthenticationProvider\\:\\:authenticate\\(\\) expects Symfony\\\\Component\\\\Security\\\\Guard\\\\Token\\\\GuardTokenInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&Symfony\\\\Component\\\\Security\\\\Core\\\\Authentication\\\\Token\\\\AnonymousToken given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/GuardRepositoryAuthenticationProviderTest.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Security\\\\Core\\\\Authentication\\\\Token\\\\TokenInterface\\:\\:getProviderKey\\(\\)\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProviderTest.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Security\\\\Core\\\\Authentication\\\\Token\\\\TokenInterface\\:\\:getSecret\\(\\)\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RememberMeRepositoryAuthenticationProviderTest\\:\\:testAuthenticate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RememberMeRepositoryAuthenticationProviderTest\\:\\:testAuthenticateUnsupportedToken\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RememberMeRepositoryAuthenticationProviderTest\\:\\:testAuthenticateWrongProviderKey\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RememberMeRepositoryAuthenticationProviderTest\\:\\:testAuthenticateWrongSecret\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProviderTest.php + + - + message: "#^Call to an undefined method Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserProviderInterface\\:\\:method\\(\\)\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RepositoryAuthenticationProviderTest\\:\\:testAuthenticationNotEzUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RepositoryAuthenticationProviderTest\\:\\:testCheckAuthentication\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RepositoryAuthenticationProviderTest\\:\\:testCheckAuthenticationAlreadyLoggedIn\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RepositoryAuthenticationProviderTest\\:\\:testCheckAuthenticationCredentialsChanged\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RepositoryAuthenticationProviderTest\\:\\:testCheckAuthenticationFailed\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RepositoryAuthenticationProviderTest\\:\\:testCheckAuthenticationFailedWhenPasswordInUnsupportedFormat\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProviderTest.php + + - + message: "#^Parameter \\#1 \\$user of class Symfony\\\\Component\\\\Security\\\\Core\\\\Authentication\\\\Token\\\\UsernamePasswordToken constructor expects Symfony\\\\Component\\\\Security\\\\Core\\\\User\\\\UserInterface, string given\\.$#" + count: 6 + path: tests/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProviderTest.php + + - + message: "#^Parameter \\#3 \\$roles of class Symfony\\\\Component\\\\Security\\\\Core\\\\Authentication\\\\Token\\\\UsernamePasswordToken constructor expects array\\<string\\>, string given\\.$#" + count: 9 + path: tests/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProviderTest.php + + - + message: "#^Parameter \\#4 \\$hasherFactory of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Authentication\\\\RepositoryAuthenticationProvider constructor expects Symfony\\\\Component\\\\PasswordHasher\\\\Hasher\\\\PasswordHasherFactoryInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&Symfony\\\\Component\\\\Security\\\\Core\\\\Encoder\\\\EncoderFactoryInterface given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:generateListener\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testCheckSiteAccessNoSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testCheckSiteAccessNotEzUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testCheckSiteAccessPermissionDenied\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testCheckSiteAccessPermissionGranted\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testGetSubscribedEvents\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testOnInteractiveLogin\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testOnInteractiveLoginAlreadyEzUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testOnInteractiveLoginNotUserObject\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testOnKernelRequestAccessDenied\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testOnKernelRequestAccessGranted\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testOnKernelRequestLoginRoute\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testOnKernelRequestNoSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testOnKernelRequestNullToken\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testOnKernelRequestSubRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:testOnKernelRequestSubRequestFragment\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Parameter \\#3 \\$configResolver of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListener constructor expects Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Parameter \\#4 \\$eventDispatcher of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListener constructor expects Symfony\\\\Component\\\\EventDispatcher\\\\EventDispatcherInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Parameter \\#5 \\$tokenStorage of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListener constructor expects Symfony\\\\Component\\\\Security\\\\Core\\\\Authentication\\\\Token\\\\Storage\\\\TokenStorageInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&Symfony\\\\Component\\\\Security\\\\Core\\\\Authorization\\\\AuthorizationCheckerInterface given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Parameter \\#6 \\$authorizationChecker of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListener constructor expects Symfony\\\\Component\\\\Security\\\\Core\\\\Authorization\\\\AuthorizationCheckerInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\EventListener\\\\SecurityListenerTest\\:\\:\\$tokenStorage \\(PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&Symfony\\\\Component\\\\Security\\\\Core\\\\Authorization\\\\AuthorizationCheckerInterface\\) does not accept PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&Symfony\\\\Component\\\\Security\\\\Core\\\\Authentication\\\\Token\\\\Storage\\\\TokenStorageInterface\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:checkRequestPathProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:generateUriProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:generateUriStandardProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testCheckRequestPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testCheckRequestPath\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testCheckRequestPath\\(\\) has parameter \\$path with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testCheckRequestPath\\(\\) has parameter \\$requestUri with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testCheckRequestPath\\(\\) has parameter \\$siteAccessUri with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testCheckRequestPathStandard\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testGenerateUri\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testGenerateUri\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testGenerateUri\\(\\) has parameter \\$isUriRouteName with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testGenerateUri\\(\\) has parameter \\$siteAccessUri with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testGenerateUri\\(\\) has parameter \\$uri with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testGenerateUriStandard\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testGenerateUriStandard\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testGenerateUriStandard\\(\\) has parameter \\$isUriRouteName with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\HttpUtilsTest\\:\\:testGenerateUriStandard\\(\\) has parameter \\$uri with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\:\\:\\$matcher \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\URILexer&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 2 + path: tests/lib/MVC/Symfony/Security/HttpUtilsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\InteractiveLoginTokenTest\\:\\:testConstruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/InteractiveLoginTokenTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\InteractiveLoginTokenTest\\:\\:testSerialize\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/InteractiveLoginTokenTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\EmailProviderTest\\:\\:supportsClassProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\EmailProviderTest\\:\\:testLoadUserByAPIUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\EmailProviderTest\\:\\:testLoadUserByUsername\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\EmailProviderTest\\:\\:testLoadUserByUsernameAlreadyUserObject\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\EmailProviderTest\\:\\:testLoadUserByUsernameUserNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\EmailProviderTest\\:\\:testRefreshUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\EmailProviderTest\\:\\:testRefreshUserNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\EmailProviderTest\\:\\:testRefreshUserNotSupported\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\EmailProviderTest\\:\\:testSupportsClass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\EmailProviderTest\\:\\:testSupportsClass\\(\\) has parameter \\$class with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\EmailProviderTest\\:\\:testSupportsClass\\(\\) has parameter \\$supports with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\UsernameProviderTest\\:\\:supportsClassProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\UsernameProviderTest\\:\\:testLoadUserByAPIUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\UsernameProviderTest\\:\\:testLoadUserByUsername\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\UsernameProviderTest\\:\\:testLoadUserByUsernameAlreadyUserObject\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\UsernameProviderTest\\:\\:testLoadUserByUsernameUserNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\UsernameProviderTest\\:\\:testRefreshUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\UsernameProviderTest\\:\\:testRefreshUserNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\UsernameProviderTest\\:\\:testRefreshUserNotSupported\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\UsernameProviderTest\\:\\:testSupportsClass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\UsernameProviderTest\\:\\:testSupportsClass\\(\\) has parameter \\$class with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\User\\\\UsernameProviderTest\\:\\:testSupportsClass\\(\\) has parameter \\$supports with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php + + - + message: "#^Parameter \\#1 \\$expirationDate of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\PasswordInfo constructor expects DateTimeImmutable\\|null, DateTimeImmutable\\|false given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/UserCheckerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserTest\\:\\:testConstruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserTest\\:\\:testIsEqualTo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserTest\\:\\:testIsEqualToNotSameUserType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserTest\\:\\:testIsNotEqualTo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserTest\\:\\:testSetAPIUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrappedTest\\:\\:testGetSetAPIUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/UserWrappedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrappedTest\\:\\:testGetSetWrappedUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/UserWrappedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrappedTest\\:\\:testIsEqualTo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/UserWrappedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrappedTest\\:\\:testRegularUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/UserWrappedTest.php + + - + message: "#^Parameter \\#2 \\$apiUser of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\UserWrapped constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\User, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 5 + path: tests/lib/MVC/Symfony/Security/UserWrappedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:supportsAttributeProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:supportsClassProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:testSupportsAttribute\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:testSupportsAttribute\\(\\) has parameter \\$attribute with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:testSupportsAttribute\\(\\) has parameter \\$expectedResult with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:testSupportsClass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:testSupportsClass\\(\\) has parameter \\$class with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:testVote\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:testVote\\(\\) has parameter \\$expectedResult with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:testVote\\(\\) has parameter \\$repositoryCanUser with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:testVoteInvalidAttribute\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:testVoteInvalidAttribute\\(\\) has parameter \\$attributes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:voteInvalidAttributeProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\CoreVoterTest\\:\\:voteProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:supportsAttributeProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:supportsClassProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:testSupportsAttribute\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:testSupportsAttribute\\(\\) has parameter \\$attribute with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:testSupportsAttribute\\(\\) has parameter \\$expectedResult with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:testSupportsClass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:testSupportsClass\\(\\) has parameter \\$class with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:testVote\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:testVote\\(\\) has parameter \\$expectedResult with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:testVote\\(\\) has parameter \\$repositoryCanUser with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:testVoteInvalidAttribute\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:testVoteInvalidAttribute\\(\\) has parameter \\$attributes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:voteInvalidAttributeProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Security\\\\Voter\\\\ValueObjectVoterTest\\:\\:voteProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundAndTest\\:\\:matchProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundAndTest\\:\\:testMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundAndTest\\:\\:testMatch\\(\\) has parameter \\$expectedMatch with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundAndTest\\:\\:testReverseMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundAndTest\\:\\:testReverseMatchFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundAndTest\\:\\:testReverseMatchNotVersatile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundAndTest\\:\\:testReverseMatchSiteAccessNotConfigured\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundAndTest\\:\\:testSerialize\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundAndTest\\:\\:testSetMatcherBuilder\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundAndTest\\:\\:testSetRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php + + - + message: "#^Parameter \\#1 \\$matcherBuilder of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Compound\\:\\:setMatcherBuilder\\(\\) expects Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\MatcherBuilderInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 6 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundOrTest\\:\\:matchProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundOrTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundOrTest\\:\\:testMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundOrTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundOrTest\\:\\:testReverseMatch1\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundOrTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundOrTest\\:\\:testReverseMatch2\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundOrTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundOrTest\\:\\:testReverseMatchFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundOrTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundOrTest\\:\\:testReverseMatchNotVersatile\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundOrTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundOrTest\\:\\:testReverseMatchSiteAccessNotConfigured\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundOrTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundOrTest\\:\\:testSerialize\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundOrTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Compound\\\\CompoundOrTest\\:\\:testSetMatcherBuilder\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundOrTest.php + + - + message: "#^Parameter \\#1 \\$matcherBuilder of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Compound\\:\\:setMatcherBuilder\\(\\) expects Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\MatcherBuilderInterface, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 6 + path: tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundOrTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\MatcherSerializationTest\\:\\:getMapHostMatcherTestCase\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/MatcherSerializationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\MatcherSerializationTest\\:\\:getMapPortMatcherTestCase\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/MatcherSerializationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\MatcherSerializationTest\\:\\:getMapURIMatcherTestCase\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/MatcherSerializationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\MatcherSerializationTest\\:\\:matcherProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/MatcherSerializationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\MatcherSerializationTest\\:\\:testDeserialize\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/MatcherSerializationTest.php + + - + message: "#^Parameter \\#1 \\$subMatchers of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Compound\\:\\:setSubMatchers\\(\\) expects array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\>, array\\<string, array\\<string, array\\<string, string\\>\\>\\> given\\.$#" + count: 4 + path: tests/lib/MVC/Symfony/SiteAccess/MatcherSerializationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Provider\\\\ChainSiteAccessProviderTest\\:\\:getExistingSiteProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Provider/ChainSiteAccessProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Provider\\\\ChainSiteAccessProviderTest\\:\\:siteAccessNamesProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Provider/ChainSiteAccessProviderTest.php + + - + message: "#^Parameter \\#2 \\$groupsBySiteAccess of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Provider\\\\StaticSiteAccessProvider constructor expects array\\<string\\>, array\\<string, array\\<int, string\\>\\> given\\.$#" + count: 2 + path: tests/lib/MVC/Symfony/SiteAccess/Provider/ChainSiteAccessProviderTest.php + + - + message: "#^Property Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\:\\:\\$groups \\(array\\<Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccessGroup\\>\\) does not accept array\\<string\\>\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Provider/ChainSiteAccessProviderTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Provider\\\\ChainSiteAccessProviderTest\\:\\:\\$groupsBySiteAccess type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/Provider/ChainSiteAccessProviderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterBaseTest\\:\\:matchProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterBaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterBaseTest\\:\\:testMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterBaseTest.php + + - + message: "#^Class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Map\\\\Host constructor invoked with 2 parameters, 1 required\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostElementTest\\:\\:matchProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostElementTest\\:\\:reverseMatchProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostElementTest\\:\\:testGetName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostElementTest\\:\\:testReverseMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostElementTest\\:\\:testReverseMatch\\(\\) has parameter \\$elementNumber with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostElementTest\\:\\:testReverseMatch\\(\\) has parameter \\$expectedHost with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostElementTest\\:\\:testReverseMatch\\(\\) has parameter \\$siteAccessName with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostElementTest\\:\\:testReverseMatchFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostElementTest\\:\\:testSerialize\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostPortURITest\\:\\:matchProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostPortURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostPortURITest\\:\\:testReverseHostMatchFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostPortURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostPortURITest\\:\\:testReverseMatchHost\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostPortURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostPortURITest\\:\\:testReverseMatchPort\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostPortURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostPortURITest\\:\\:testReversePortMatchFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostPortURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostPortURITest\\:\\:testSetGetRequestMapHost\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostPortURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostPortURITest\\:\\:testSetGetRequestMapPort\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostPortURITest.php + + - + message: "#^Class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\\\Host constructor invoked with 2 parameters, 1 required\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostRegexTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostRegexTest\\:\\:matchProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostRegexTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostRegexTest\\:\\:testGetName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostRegexTest.php + + - + message: "#^Class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\HostText constructor invoked with 2 parameters, 1 required\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostTextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostTextTest\\:\\:matchProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostTextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostTextTest\\:\\:testGetName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostTextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterHostTextTest\\:\\:testReverseMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterHostTextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterMapURITest\\:\\:fixupURIProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterMapURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterMapURITest\\:\\:setRequestProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterMapURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterMapURITest\\:\\:testAnalyseLink\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterMapURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterMapURITest\\:\\:testAnalyseURI\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterMapURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterMapURITest\\:\\:testReverseMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterMapURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterMapURITest\\:\\:testReverseMatchFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterMapURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterMapURITest\\:\\:testSetGetRequest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterMapURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterMapURITest\\:\\:testSetGetRequest\\(\\) has parameter \\$config with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterMapURITest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterPortHostURITest\\:\\:matchProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterPortHostURITest.php + + - + message: "#^Class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Map\\\\Port constructor invoked with 2 parameters, 1 required\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterSpecialPortsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterSpecialPortsTest\\:\\:matchProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterSpecialPortsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterSpecialPortsTest\\:\\:testGetName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterSpecialPortsTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterTest\\:\\:createRouter\\(\\) has parameter \\$debug with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterTest\\:\\:matchProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterTest\\:\\:testConstructDebug\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterTest\\:\\:testMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterTest\\:\\:testMatch\\(\\) has parameter \\$siteAccess with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterTest\\:\\:testMatchByName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterTest\\:\\:testMatchByNameInvalidSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterTest\\:\\:testMatchByNameNoVersatileMatcher\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterTest\\:\\:testMatchWithDevEnvFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterTest\\:\\:testMatchWithEnv\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterTest\\:\\:testMatchWithProdEnvFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElement2Test\\:\\:analyseProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElement2Test\\:\\:matchProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElement2Test\\:\\:reverseMatchProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElement2Test\\:\\:testAnalyseLink\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElement2Test\\:\\:testAnalyseURI\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElement2Test\\:\\:testAnalyseURILevelAsInt\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElement2Test\\:\\:testReverseMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElement2Test\\:\\:testReverseMatch\\(\\) has parameter \\$originalPathinfo with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElement2Test\\:\\:testReverseMatch\\(\\) has parameter \\$siteAccessName with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElement2Test\\:\\:testReverseMatchFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElement2Test\\:\\:testSerialize\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElementTest\\:\\:analyseProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElementTest\\:\\:matchProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElementTest\\:\\:reverseMatchProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElementTest\\:\\:testAnalyseLink\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElementTest\\:\\:testAnalyseURI\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElementTest\\:\\:testGetName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElementTest\\:\\:testReverseMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElementTest\\:\\:testReverseMatch\\(\\) has parameter \\$originalPathinfo with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElementTest\\:\\:testReverseMatch\\(\\) has parameter \\$siteAccessName with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElementTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIElementTest\\:\\:testSerialize\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIElementTest.php + + - + message: "#^Class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Regex\\\\URI constructor invoked with 2 parameters, 1 required\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIRegexTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIRegexTest\\:\\:matchProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIRegexTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIRegexTest\\:\\:testGetName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIRegexTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURIRegexTest\\:\\:testSerialize\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURIRegexTest.php + + - + message: "#^Class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\URIText constructor invoked with 2 parameters, 1 required\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURITextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURITextTest\\:\\:matchProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURITextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURITextTest\\:\\:testAnalyseLink\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURITextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURITextTest\\:\\:testAnalyseURI\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURITextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURITextTest\\:\\:testGetName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURITextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\RouterURITextTest\\:\\:testReverseMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/RouterURITextTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\SiteAccessServiceTest\\:\\:getAvailableSitAccesses\\(\\) return type with generic class ArrayIterator does not specify its types\\: TKey, TValue$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\SiteAccessServiceTest\\:\\:getConfigResolverParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php + + - + message: "#^Parameter \\#2 \\$groupsBySiteAccess of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Provider\\\\StaticSiteAccessProvider constructor expects array\\<string\\>, array\\<string, array\\<int, string\\>\\> given\\.$#" + count: 2 + path: tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\SiteAccessServiceTest\\:\\:\\$availableSiteAccesses with generic class ArrayIterator does not specify its types\\: TKey, TValue$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\SiteAccessServiceTest\\:\\:\\$configResolverParameters type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\BaseRenderStrategyTest\\:\\:createRenderStrategy\\(\\) has parameter \\$fragmentRenderers with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\BaseRenderStrategyTest\\:\\:createRenderStrategy\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\MVC\\\\Templating\\\\RenderStrategy but returns object\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php + + - + message: "#^Method class@anonymous/tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest\\.php\\:51\\:\\:render\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php + + - + message: "#^Property class@anonymous/tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest\\.php\\:51\\:\\:\\$rendered \\(string\\) does not accept string\\|null\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php + + - + message: "#^Property class@anonymous/tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest\\.php\\:51\\:\\:\\$rendered \\(string\\) on left side of \\?\\? is not nullable\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelperTest\\:\\:testGetAvailableLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/GlobalHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelperTest\\:\\:testGetConfigResolver\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/GlobalHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelperTest\\:\\:testGetRequestedUriString\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/GlobalHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelperTest\\:\\:testGetRootLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/GlobalHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelperTest\\:\\:testGetSiteaccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/GlobalHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelperTest\\:\\:testGetSystemUriString\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/GlobalHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelperTest\\:\\:testGetSystemUriStringNoUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/GlobalHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelperTest\\:\\:testGetTranslationSiteAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/GlobalHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelperTest\\:\\:testGetViewParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/GlobalHelperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelperTest\\:\\:testGetViewParametersString\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/GlobalHelperTest.php + + - + message: "#^Anonymous function has an unused use \\$content\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/RenderContentStrategyTest.php + + - + message: "#^Anonymous function has an unused use \\$siteAccess\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/RenderContentStrategyTest.php + + - + message: "#^Anonymous function has an unused use \\$content\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/RenderLocationStrategyTest.php + + - + message: "#^Anonymous function has an unused use \\$siteAccess\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/RenderLocationStrategyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtensionTest\\:\\:getConfigResolverMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtensionTest\\:\\:getContent\\(\\) has parameter \\$fieldsData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtensionTest\\:\\:getContent\\(\\) has parameter \\$namesData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtensionTest\\:\\:getField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtensionTest\\:\\:getField\\(\\) has parameter \\$isEmpty with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php + + - + message: "#^Parameter \\#1 \\$repository of class Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtension constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\ContentExtensionTest\\:\\:\\$fieldHelperMock \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept Ibexa\\\\Core\\\\Helper\\\\FieldHelper&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtensionIntegrationTest\\:\\:getConfigResolverMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtensionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtensionIntegrationTest\\:\\:getContent\\(\\) has parameter \\$fieldsData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtensionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtensionIntegrationTest\\:\\:getContent\\(\\) has parameter \\$namesData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtensionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtensionIntegrationTest\\:\\:getFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtensionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtensionIntegrationTest\\:\\:getFieldDefinition\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtensionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtensionIntegrationTest\\:\\:getFieldDefinition\\(\\) has parameter \\$settings with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtensionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtensionIntegrationTest\\:\\:getFieldDefinition\\(\\) has parameter \\$typeIdentifier with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtensionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtensionIntegrationTest\\:\\:getTemplatePath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtensionIntegrationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtensionIntegrationTest\\:\\:getTemplatePath\\(\\) has parameter \\$tpl with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtensionIntegrationTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FieldRenderingExtensionIntegrationTest\\:\\:\\$fieldDefinitions has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtensionIntegrationTest.php + + - + message: "#^Argument of an invalid type string supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtensionTest\\:\\:getExtensions\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtensionTest\\:\\:getLocale\\(\\) should return string but returns array\\<int, mixed\\>\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtensionTest\\:\\:setConfigurationLocale\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\Ibexa\\\\Contracts\\\\Core\\\\SiteAccess\\\\ConfigResolverInterface\\|\\\\PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\)\\: Unexpected token \"\\\\n \", expected variable at offset 115$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Locale\\\\LocaleConverterInterface\\|\\\\PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\)\\: Unexpected token \"\\\\n \", expected variable at offset 114$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php + + - + message: "#^PHPDoc tag @param has invalid value \\(\\\\Symfony\\\\Contracts\\\\Translation\\\\TranslatorInterface\\|\\\\PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\)\\: Unexpected token \"\\\\n \", expected variable at offset 109$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtensionTest\\:\\:\\$configResolverInterfaceMock has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtensionTest\\:\\:\\$locale has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtensionTest\\:\\:\\$localeConverterInterfaceMock has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtensionTest\\:\\:\\$suffixes has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSizeExtensionTest\\:\\:\\$translatorMock has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php + + - + message: "#^Call to an undefined method Twig\\\\Error\\\\SyntaxError\\:\\:setTemplateFile\\(\\)\\.$#" + count: 2 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSystemTwigIntegrationTestCase\\:\\:doIntegrationTest\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSystemTwigIntegrationTestCase\\:\\:doIntegrationTest\\(\\) has parameter \\$condition with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSystemTwigIntegrationTestCase\\:\\:doIntegrationTest\\(\\) has parameter \\$deprecation with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSystemTwigIntegrationTestCase\\:\\:doIntegrationTest\\(\\) has parameter \\$exception with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSystemTwigIntegrationTestCase\\:\\:doIntegrationTest\\(\\) has parameter \\$file with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSystemTwigIntegrationTestCase\\:\\:doIntegrationTest\\(\\) has parameter \\$message with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSystemTwigIntegrationTestCase\\:\\:doIntegrationTest\\(\\) has parameter \\$outputs with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\FileSystemTwigIntegrationTestCase\\:\\:doIntegrationTest\\(\\) has parameter \\$templates with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php + + - + message: "#^Parameter \\#1 \\$name of method Twig\\\\Loader\\\\ChainLoader\\:\\:getSourceContext\\(\\) expects string, int\\|string given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php + + - + message: "#^Undefined variable\\: \\$ret$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php + + - + message: "#^Anonymous function should return non\\-empty\\-string but returns non\\-empty\\-string\\|false\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\RoutingExtensionTest\\:\\:getExampleRouteReference\\(\\) has parameter \\$name with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtensionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\Twig\\\\Extension\\\\RoutingExtensionTest\\:\\:getExampleRouteReference\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtensionTest.php + + - + message: "#^Parameter \\#1 \\$code of method PhpParser\\\\Parser\\:\\:parse\\(\\) expects string, string\\|false given\\.$#" + count: 3 + path: tests/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitorTest.php + + - + message: "#^Parameter \\#3 \\$ast of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\ExceptionMessageTemplateFileVisitor\\:\\:visitPhpFile\\(\\) expects array, array\\<PhpParser\\\\Node\\\\Stmt\\>\\|null given\\.$#" + count: 3 + path: tests/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\NoTranslationToExtract\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/NoTranslationToExtract.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\NoTranslationToExtract\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/NoTranslationToExtract.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\NoTranslationToExtract\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/NoTranslationToExtract.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\NoTranslationToExtract\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/NoTranslationToExtract.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\NoTranslationToExtract\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/NoTranslationToExtract.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\NoTranslationToExtract\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/NoTranslationToExtract.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\NoTranslationToExtract\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/NoTranslationToExtract.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\NoTranslationToExtract\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/NoTranslationToExtract.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\NoTranslationToExtract\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/NoTranslationToExtract.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\SetMessageTemplate\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/SetMessageTemplate.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\SetMessageTemplate\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/SetMessageTemplate.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\SetMessageTemplate\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/SetMessageTemplate.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\SetMessageTemplate\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/SetMessageTemplate.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\SetMessageTemplate\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/SetMessageTemplate.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\SetMessageTemplate\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/SetMessageTemplate.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\SetMessageTemplate\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/SetMessageTemplate.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\SetMessageTemplate\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/SetMessageTemplate.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\SetMessageTemplate\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/SetMessageTemplate.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\WrongTranslationId\\:\\:addParameter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/WrongTranslationId.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\WrongTranslationId\\:\\:addParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/WrongTranslationId.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\WrongTranslationId\\:\\:addParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/WrongTranslationId.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\WrongTranslationId\\:\\:getParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/WrongTranslationId.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\WrongTranslationId\\:\\:setMessageTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/WrongTranslationId.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\WrongTranslationId\\:\\:setParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/WrongTranslationId.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\WrongTranslationId\\:\\:setParameters\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/WrongTranslationId.php + + - + message: "#^Parameter \\#1 \\$messageTemplate of method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\WrongTranslationId\\:\\:setMessageTemplate\\(\\) expects string, array\\<int, string\\> given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/WrongTranslationId.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\WrongTranslationId\\:\\:\\$messageTemplate has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/WrongTranslationId.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\Translation\\\\fixtures\\\\WrongTranslationId\\:\\:\\$parameters has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/Translation/fixtures/WrongTranslationId.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\AbstractViewTest\\:\\:badTemplateIdentifierProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/AbstractViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\AbstractViewTest\\:\\:createViewUnderTest\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/AbstractViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\AbstractViewTest\\:\\:createViewUnderTest\\(\\) has parameter \\$template with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/AbstractViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\AbstractViewTest\\:\\:createViewUnderTest\\(\\) has parameter \\$viewType with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/AbstractViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\AbstractViewTest\\:\\:getAlwaysAvailableParams\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/AbstractViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\AbstractViewTest\\:\\:goodTemplateIdentifierProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/AbstractViewTest.php + + - + message: "#^Parameter \\#1 \\$templateIdentifier of method Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View\\:\\:setTemplateIdentifier\\(\\) expects \\(Closure\\(array\\<string, mixed\\>\\)\\: string\\)\\|string, \\(callable\\(\\)\\: mixed\\)\\|string given\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/AbstractViewTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\ContentViewBuilderTest\\:\\:\\$contentViewBuilder \\(Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\ContentViewBuilder&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept Ibexa\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Builder\\\\ContentViewBuilder\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/Builder/ContentViewBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentViewTest\\:\\:constructFailProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ContentViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentViewTest\\:\\:constructProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ContentViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentViewTest\\:\\:createViewUnderTest\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ContentViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentViewTest\\:\\:createViewUnderTest\\(\\) has parameter \\$template with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ContentViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentViewTest\\:\\:createViewUnderTest\\(\\) has parameter \\$viewType with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ContentViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentViewTest\\:\\:getAlwaysAvailableParams\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ContentViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentViewTest\\:\\:testConstruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ContentViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentViewTest\\:\\:testConstruct\\(\\) has parameter \\$params with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ContentViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentViewTest\\:\\:testConstruct\\(\\) has parameter \\$templateIdentifier with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ContentViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentViewTest\\:\\:testConstructFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ContentViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentViewTest\\:\\:testConstructFail\\(\\) has parameter \\$templateIdentifier with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ContentViewTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ContentViewTest\\:\\:\\$valueParams type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ContentViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\LoginFormViewTest\\:\\:createViewUnderTest\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/LoginFormViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\LoginFormViewTest\\:\\:createViewUnderTest\\(\\) has parameter \\$template with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/LoginFormViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\LoginFormViewTest\\:\\:createViewUnderTest\\(\\) has parameter \\$viewType with no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/LoginFormViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\LoginFormViewTest\\:\\:getAlwaysAvailableParams\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/LoginFormViewTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Renderer\\\\TemplateRendererTest\\:\\:testRender\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/Renderer/TemplateRendererTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Renderer\\\\TemplateRendererTest\\:\\:testRenderNoViewTemplate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/Renderer/TemplateRendererTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\VariableProviderRegistryTest\\:\\:getRegistry\\(\\) has parameter \\$providers with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/VariableProviderRegistryTest.php + + - + message: "#^Method class@anonymous/tests/lib/MVC/Symfony/View/VariableProviderRegistryTest\\.php\\:29\\:\\:getTwigVariables\\(\\) has parameter \\$options with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/VariableProviderRegistryTest.php + + - + message: "#^Property class@anonymous/tests/lib/MVC/Symfony/View/VariableProviderRegistryTest\\.php\\:29\\:\\:\\$identifier has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/VariableProviderRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerTest\\:\\:createContentViewProviderMocks\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ViewManagerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerTest\\:\\:createLocationViewProviderMocks\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ViewManagerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerTest\\:\\:testAddContentViewProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ViewManagerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerTest\\:\\:testAddLocationViewProvider\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ViewManagerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerTest\\:\\:testContentViewProvidersPriority\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ViewManagerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerTest\\:\\:testLocationViewProvidersPriority\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ViewManagerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerTest\\:\\:testRenderContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ViewManagerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerTest\\:\\:testRenderContentWithClosure\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ViewManagerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerTest\\:\\:testRenderLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ViewManagerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerTest\\:\\:testRenderLocationWithClosure\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ViewManagerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerTest\\:\\:testRenderLocationWithContentPassed\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ViewManagerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\ViewManagerTest\\:\\:\\$viewBaseLayout has no type specified\\.$#" + count: 1 + path: tests/lib/MVC/Symfony/View/ViewManagerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\AdapterFactory\\\\SearchHitAdapterFactoryTest\\:\\:dataProviderForCreateFixedAdapter\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Pagination/AdapterFactory/SearchHitAdapterFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchAdapterTest\\:\\:getAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Pagination/ContentSearchAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchAdapterTest\\:\\:getExpectedFinalResultFromHits\\(\\) has parameter \\$hits with no type specified\\.$#" + count: 1 + path: tests/lib/Pagination/ContentSearchAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchHitAdapterTest\\:\\:getAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Pagination/ContentSearchHitAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchHitAdapterTest\\:\\:getExpectedFinalResultFromHits\\(\\) has parameter \\$hits with no type specified\\.$#" + count: 1 + path: tests/lib/Pagination/ContentSearchHitAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchHitAdapterTest\\:\\:testGetNbResults\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Pagination/ContentSearchHitAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\ContentSearchHitAdapterTest\\:\\:testGetSlice\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Pagination/ContentSearchHitAdapterTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\:\\:\\$query \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\CriterionInterface&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Pagination/ContentSearchHitAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchAdapterTest\\:\\:getAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Pagination/LocationSearchAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchAdapterTest\\:\\:getExpectedFinalResultFromHits\\(\\) has parameter \\$hits with no type specified\\.$#" + count: 1 + path: tests/lib/Pagination/LocationSearchAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchHitAdapterTest\\:\\:getAdapter\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Pagination/LocationSearchHitAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchHitAdapterTest\\:\\:getExpectedFinalResultFromHits\\(\\) has parameter \\$hits with no type specified\\.$#" + count: 1 + path: tests/lib/Pagination/LocationSearchHitAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchHitAdapterTest\\:\\:testGetNbResults\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Pagination/LocationSearchHitAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Pagination\\\\LocationSearchHitAdapterTest\\:\\:testGetSlice\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Pagination/LocationSearchHitAdapterTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\:\\:\\$query \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\CriterionInterface&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Pagination/LocationSearchHitAdapterTest.php + + - + message: "#^Access to an undefined property Symfony\\\\Component\\\\Cache\\\\CacheItem\\:\\:\\$defaultLifetime\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractBaseHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractBaseHandlerTest\\:\\:getCacheItem\\(\\) has parameter \\$key with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractBaseHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractBaseHandlerTest\\:\\:provideAbstractCacheHandlerArguments\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractBaseHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractBaseHandlerTest\\:\\:provideInMemoryCacheHandlerArguments\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractBaseHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has parameter \\$additionalCalls with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has parameter \\$keyGeneratingArguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has parameter \\$keyGeneratingResults with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has parameter \\$tagGeneratingArguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has parameter \\$tagGeneratingResults with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has parameter \\$additionalCalls with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has parameter \\$keyGeneratingArguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has parameter \\$keyGeneratingResults with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has parameter \\$tagGeneratingArguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has parameter \\$tagGeneratingResults with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testUnCachedMethods\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testUnCachedMethods\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testUnCachedMethods\\(\\) has parameter \\$key with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testUnCachedMethods\\(\\) has parameter \\$keyGeneratingArguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testUnCachedMethods\\(\\) has parameter \\$tagGeneratingArguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractCacheHandlerTest\\:\\:testUnCachedMethods\\(\\) has parameter \\$tags with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Only iterables can be unpacked, array\\|null given in argument \\#1\\.$#" + count: 5 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Parameter \\#1 \\$originalClassName of method PHPUnit\\\\Framework\\\\TestCase\\:\\:createMock\\(\\) expects class\\-string\\<mixed\\>, string given\\.$#" + count: 2 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Unable to resolve the template type T in call to method PHPUnit\\\\Framework\\\\TestCase\\:\\:createMock\\(\\)$#" + count: 3 + path: tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has parameter \\$additionalCalls with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has parameter \\$keyGeneratingArguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has parameter \\$keyGeneratingResults with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has parameter \\$tagGeneratingArguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheHit\\(\\) has parameter \\$tagGeneratingResults with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has parameter \\$additionalCalls with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has parameter \\$keyGeneratingArguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has parameter \\$keyGeneratingResults with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has parameter \\$tagGeneratingArguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testLoadMethodsCacheMiss\\(\\) has parameter \\$tagGeneratingResults with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testUnCachedMethods\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testUnCachedMethods\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testUnCachedMethods\\(\\) has parameter \\$key with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testUnCachedMethods\\(\\) has parameter \\$keyGeneratingArguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testUnCachedMethods\\(\\) has parameter \\$tagGeneratingArguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractInMemoryCacheHandlerTest\\:\\:testUnCachedMethods\\(\\) has parameter \\$tags with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Only iterables can be unpacked, array\\|null given in argument \\#1\\.$#" + count: 5 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Parameter \\#1 \\$originalClassName of method PHPUnit\\\\Framework\\\\TestCase\\:\\:createMock\\(\\) expects class\\-string\\<mixed\\>, string given\\.$#" + count: 2 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Unable to resolve the template type T in call to method PHPUnit\\\\Framework\\\\TestCase\\:\\:createMock\\(\\)$#" + count: 3 + path: tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php + + - + message: "#^Access to an undefined property Symfony\\\\Component\\\\Cache\\\\CacheItem\\:\\:\\$defaultLifetime\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Access to an undefined property Symfony\\\\Component\\\\Cache\\\\CacheItem\\:\\:\\$prevTags\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\InMemoryClearingProxyAdapterTest\\:\\:arrayAsGenerator\\(\\) has parameter \\$array with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\InMemoryClearingProxyAdapterTest\\:\\:createCacheItem\\(\\) has parameter \\$tags with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\InMemoryClearingProxyAdapterTest\\:\\:providerForClearAndInvalidation\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\InMemoryClearingProxyAdapterTest\\:\\:providerForDelete\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\InMemoryClearingProxyAdapterTest\\:\\:testClearAndInvalidation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\InMemoryClearingProxyAdapterTest\\:\\:testClearAndInvalidation\\(\\) has parameter \\$argument with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\InMemoryClearingProxyAdapterTest\\:\\:testDelete\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\InMemoryClearingProxyAdapterTest\\:\\:testDelete\\(\\) has parameter \\$argument with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\InMemoryClearingProxyAdapterTest\\:\\:testGetItem\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\InMemoryClearingProxyAdapterTest\\:\\:testGetItems\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\InMemoryClearingProxyAdapterTest\\:\\:testGetItemsWithGenerator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Adapter\\\\InMemoryClearingProxyAdapterTest\\:\\:testHasItem\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\BookmarkHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/BookmarkHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\BookmarkHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/BookmarkHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\BookmarkHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/BookmarkHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ContentHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ContentHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ContentHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ContentHandlerTest\\:\\:testDeleteContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ContentLanguageHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ContentLanguageHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ContentLanguageHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ContentTypeHandlerTest\\:\\:testPublish\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentTypeHandlerTest.php + + - + message: "#^Parameter \\#1 \\$originalClassName of method PHPUnit\\\\Framework\\\\TestCase\\:\\:createMock\\(\\) expects class\\-string\\<mixed\\>, string given\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentTypeHandlerTest.php + + - + message: "#^Parameter \\#2 \\$value of method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\AbstractBaseHandlerTest\\:\\:getCacheItem\\(\\) expects null, Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type given\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentTypeHandlerTest.php + + - + message: "#^Unable to resolve the template type T in call to method PHPUnit\\\\Framework\\\\TestCase\\:\\:createMock\\(\\)$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentTypeHandlerTest.php + + - + message: "#^Variable \\$tags in empty\\(\\) always exists and is not falsy\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGeneratorTest\\:\\:providerForTestGenerateKey\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGeneratorTest\\:\\:providerForTestGenerateKeyThrowsInvalidArgumentException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGeneratorTest\\:\\:providerForTestGenerateTag\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGeneratorTest\\:\\:providerForTestGenerateTagThrowsInvalidArgumentException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGeneratorTest\\:\\:testGenerateKey\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGeneratorTest\\:\\:testGenerateKeyThrowsInvalidArgumentException\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGeneratorTest\\:\\:testGenerateTag\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGeneratorTest\\:\\:testGenerateTagThrowsInvalidArgumentException\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierSanitizerTest\\:\\:providerForTestEscapeCacheKey\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Identifier/CacheIdentifierSanitizerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierSanitizerTest\\:\\:testEscapeCacheKey\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/Identifier/CacheIdentifierSanitizerTest.php + + - + message: "#^Function Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\InMemory\\\\microtime\\(\\) has parameter \\$asFloat with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/InMemory/InMemoryCacheTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\LocationHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\LocationPathConverterTest\\:\\:providerForTestConvertToPathIds\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/LocationPathConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\LocationPathConverterTest\\:\\:testConvertToPathIds\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/LocationPathConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\LocationPathConverterTest\\:\\:testConvertToPathIds\\(\\) has parameter \\$resultArray with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/LocationPathConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\NotificationHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/NotificationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\NotificationHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/NotificationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\NotificationHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/NotificationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ObjectStateHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ObjectStateHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\ObjectStateHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceHandlerTest\\:\\:testContentHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceHandlerTest\\:\\:testContentLocationHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceHandlerTest\\:\\:testContentTypeHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceHandlerTest\\:\\:testHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceHandlerTest\\:\\:testLanguageHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceHandlerTest\\:\\:testObjectStateHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceHandlerTest\\:\\:testSectionHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceHandlerTest\\:\\:testTransactionHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceHandlerTest\\:\\:testTrashHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceHandlerTest\\:\\:testUrlAliasHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceHandlerTest\\:\\:testUrlWildcardHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceHandlerTest\\:\\:testUserHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLoggerTest\\:\\:testGetCallValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceLoggerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLoggerTest\\:\\:testGetCalls\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceLoggerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLoggerTest\\:\\:testGetCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceLoggerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLoggerTest\\:\\:testGetCountValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceLoggerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLoggerTest\\:\\:testGetName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceLoggerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLoggerTest\\:\\:testLogCall\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceLoggerTest.php + + - + message: "#^Result of method Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\PersistenceLogger\\:\\:logCall\\(\\) \\(void\\) is used\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/PersistenceLoggerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\SectionHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\SectionHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\SectionHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\SettingHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/SettingHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\SettingHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/SettingHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\SettingHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/SettingHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\TransactionHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/TransactionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\TransactionHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/TransactionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\TransactionHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/TransactionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\TransactionHandlerTest\\:\\:testBeginTransactionStartsCacheTransaction\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/TransactionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\TransactionHandlerTest\\:\\:testCommitStopsCacheTransaction\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/TransactionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\TransactionHandlerTest\\:\\:testRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/TransactionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\TrashHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\TrashHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\TrashHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\TrashHandlerTest\\:\\:testDeleteTrashItem\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\TrashHandlerTest\\:\\:testEmptyTrash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\TrashHandlerTest\\:\\:testRecover\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\TrashHandlerTest\\:\\:testTrashSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/TrashHandlerTest.php + + - + message: "#^Parameter \\#1 \\$originalClassName of method PHPUnit\\\\Framework\\\\TestCase\\:\\:createMock\\(\\) expects class\\-string\\<mixed\\>, string given\\.$#" + count: 4 + path: tests/lib/Persistence/Cache/TrashHandlerTest.php + + - + message: "#^Unable to resolve the template type T in call to method PHPUnit\\\\Framework\\\\TestCase\\:\\:createMock\\(\\)$#" + count: 4 + path: tests/lib/Persistence/Cache/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\URLHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/URLHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\URLHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/URLHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\URLHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/URLHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\URLHandlerTest\\:\\:testUpdateUrlStatusIsUpdated\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/URLHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UrlAliasHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UrlWildcardHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UrlWildcardHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UrlWildcardHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UrlWildcardHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UrlWildcardHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UrlWildcardHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UserHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UserHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UserHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UserHandlerTest\\:\\:testAssignRole\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UserHandlerTest\\:\\:testPublishNewRoleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UserHandlerTest\\:\\:testPublishRoleDraftFromExistingRole\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UserPreferenceHandlerTest\\:\\:providerForCachedLoadMethodsHit\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UserPreferenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UserPreferenceHandlerTest\\:\\:providerForCachedLoadMethodsMiss\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UserPreferenceHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Cache\\\\UserPreferenceHandlerTest\\:\\:providerForUnCachedMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Cache/UserPreferenceHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\DatabaseConnectionFactory\\:\\:\\$databasePlatforms type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/DatabaseConnectionFactory.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\FieldTypeRegistryTest\\:\\:getFieldTypeMock\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\FieldType but returns Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Persistence/FieldTypeRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\FieldTypeRegistryTest\\:\\:testGetFieldTypeInstance\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/FieldTypeRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\FieldTypeRegistryTest\\:\\:testGetNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/FieldTypeRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\FieldTypeRegistryTest\\:\\:testGetNotFoundBCException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/FieldTypeRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\FieldTypeRegistryTest\\:\\:testGetNotInstance\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/FieldTypeRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\FieldTypeRegistryTest\\:\\:testRegister\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/FieldTypeRegistryTest.php + + - + message: "#^Parameter \\#1 \\$coreFieldTypes of class Ibexa\\\\Core\\\\Persistence\\\\FieldTypeRegistry constructor expects array\\<Ibexa\\\\Core\\\\Persistence\\\\FieldType\\>, array\\<string, DateTime\\> given\\.$#" + count: 1 + path: tests/lib/Persistence/FieldTypeRegistryTest.php + + - + message: "#^Parameter \\#1 \\$coreFieldTypes of class Ibexa\\\\Core\\\\Persistence\\\\FieldTypeRegistry constructor expects array\\<Ibexa\\\\Core\\\\Persistence\\\\FieldType\\>, array\\<string, Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\FieldType\\> given\\.$#" + count: 2 + path: tests/lib/Persistence/FieldTypeRegistryTest.php + + - + message: "#^Parameter \\#2 \\$fieldType of method Ibexa\\\\Core\\\\Persistence\\\\FieldTypeRegistry\\:\\:register\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType, Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\FieldType given\\.$#" + count: 1 + path: tests/lib/Persistence/FieldTypeRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\FieldValue\\\\Converter\\\\ImageConverterTest\\:\\:dataProviderForTestToFieldDefinition\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Persistence/FieldValue/Converter/ImageConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\FieldValue\\\\Converter\\\\ImageConverterTest\\:\\:dataProviderForTestToStorageFieldDefinition\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Persistence/FieldValue/Converter/ImageConverterTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\:\\:loadBookmarkDataById\\(\\)\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:dataProviderForLoadUserBookmarks\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:loadBookmark\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCountUserBookmarks\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCountUserBookmarks\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteBookmark\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertBookmark\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadBookmarkDataById\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadBookmarkDataByUserIdAndLocationId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadUserBookmarks\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadUserBookmarks\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLocationSwapped\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\HandlerTest\\:\\:testCreate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\HandlerTest\\:\\:testDelete\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\HandlerTest\\:\\:testLoadByUserIdAndLocationIdExistingBookmark\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\HandlerTest\\:\\:testLoadByUserIdAndLocationIdNonExistingBookmark\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\HandlerTest\\:\\:testLoadUserBookmarks\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\HandlerTest\\:\\:testLocationSwapped\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\MapperTest\\:\\:testCreateBookmarkFromCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Bookmark\\\\MapperTest\\:\\:testExtractBookmarksFromRows\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Bookmark/MapperTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\:\\:expects\\(\\)\\.$#" + count: 9 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Handler\\:\\:expects\\(\\)\\.$#" + count: 17 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:expects\\(\\)\\.$#" + count: 8 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\:\\:expects\\(\\)\\.$#" + count: 17 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\:\\:expects\\(\\)\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverter\\:\\:expects\\(\\)\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:getContentTypeHandlerMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:getGatewayMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:getRelationFixture\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:getTreeHandlerMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandler\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testAddRelation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testCopyAllVersions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testCopySingleVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testCopyThrowsNotFoundExceptionContentNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testCopyThrowsNotFoundExceptionVersionNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testCreate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testCreateDraftFromVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testDeleteContentWithLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testDeleteContentWithoutLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testDeleteVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testListVersions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testLoadContentInfoByRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testLoadContentList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testLoadDraftsForUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testLoadErrorNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testLoadRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testLoadReverseRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testPublish\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testPublishFirstVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testRemoveRawContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testRemoveRelation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testSetStatus\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testUpdateContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testUpdateMetadata\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:testUpdateMetadataUpdatesPathIdentificationString\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:\\$contentHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:\\$contentTypeHandlerMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:\\$fieldHandlerMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:\\$gatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:\\$locationGatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:\\$mapperMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:\\$slugConverterMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverter\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:\\$treeHandlerMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:\\$typeGatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ContentHandlerTest\\:\\:\\$urlAliasGatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:assertCreateExistingFieldsInNewVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:assertCreateNewFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:assertCreateNewFieldsForMainLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:assertUpdateFieldsExistingLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:assertUpdateFieldsForInitialLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:assertUpdateFieldsWithNewLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:getContentGatewayMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:getFieldTypeMock\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\FieldType&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:getFieldTypeRegistryMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\FieldTypeRegistry&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\FieldTypeRegistry\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:getMapperMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:getStorageHandlerMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testCreateExistingFieldsInNewVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testCreateExistingFieldsInNewVersionUpdatingStorageHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testCreateNewFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testCreateNewFieldsForMainLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testCreateNewFieldsForMainLanguageUpdatingStorageHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testCreateNewFieldsUpdatingStorageHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testDeleteFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testLoadExternalFieldData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testUpdateFieldsExistingLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testUpdateFieldsExistingLanguagesUpdatingStorageHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testUpdateFieldsForInitialLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testUpdateFieldsForInitialLanguageUpdatingStorageHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testUpdateFieldsWithNewLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:testUpdateFieldsWithNewLanguageUpdatingStorageHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\:\\:\\$value \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\FieldValue\\) does not accept null\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:\\$contentGatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:\\$fieldTypeMock \\(Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\FieldType&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:\\$fieldTypeMock \\(Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:\\$fieldTypeRegistryMock \\(Ibexa\\\\Core\\\\Persistence\\\\FieldTypeRegistry\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:\\$mapperMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandlerTest\\:\\:\\$storageHandlerMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Variable \\$copyField might not be defined\\.$#" + count: 2 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Variable \\$originalField might not be defined\\.$#" + count: 2 + path: tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php + + - + message: "#^Cannot access offset 'email' on Ibexa\\\\Core\\\\FieldType\\\\Author\\\\Author\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorTest.php + + - + message: "#^Cannot access offset 'id' on Ibexa\\\\Core\\\\FieldType\\\\Author\\\\Author\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorTest.php + + - + message: "#^Cannot access offset 'name' on Ibexa\\\\Core\\\\FieldType\\\\Author\\\\Author\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\AuthorTest\\:\\:testToFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\AuthorTest\\:\\:testToStorageFieldDefinitionDefaultCurrentUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\AuthorTest\\:\\:testToStorageFieldDefinitionDefaultEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\AuthorTest\\:\\:testToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\AuthorTest\\:\\:\\$authors \\(array\\<Ibexa\\\\Core\\\\FieldType\\\\Author\\\\Author\\>\\) does not accept array\\<int, array\\<string, int\\|string\\>\\>\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CheckboxTest\\:\\:testToFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CheckboxTest\\:\\:testToFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CheckboxTest\\:\\:testToStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CheckboxTest\\:\\:testToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:providerForTestToFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:providerForTestToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:testToFieldDefinitionMultiple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:testToFieldDefinitionSingle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:testToFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:testToFieldValue\\(\\) has parameter \\$data with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:testToFieldValue\\(\\) has parameter \\$dataText with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:testToFieldValue\\(\\) has parameter \\$sortKeyString with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:testToStorageFieldDefinitionMultiple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:testToStorageFieldDefinitionSingle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:testToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:testToStorageValue\\(\\) has parameter \\$data with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:testToStorageValue\\(\\) has parameter \\$dataText with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:testToStorageValue\\(\\) has parameter \\$sortKey with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\CountryTest\\:\\:testToStorageValue\\(\\) has parameter \\$sortKeyString with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php + + - + message: "#^Cannot access offset 'timestring' on array\\|bool\\|float\\|int\\|string\\|null\\.$#" + count: 3 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeTest\\:\\:getXMLToDateIntervalMap\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeTest\\:\\:testGenerateDateIntervalXML\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeTest\\:\\:testGetDateIntervalFromXML\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeTest\\:\\:testToFieldDefinitionCurrentDate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeTest\\:\\:testToFieldDefinitionNoDefault\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeTest\\:\\:testToFieldDefinitionWithAdjustmentAndSeconds\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeTest\\:\\:testToFieldDefinitionWithAdjustmentNoSeconds\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeTest\\:\\:testToFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeTest\\:\\:testToStorageFieldDefinitionCurrentDate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeTest\\:\\:testToStorageFieldDefinitionNoDefault\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeTest\\:\\:testToStorageFieldDefinitionWithAdjustment\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateAndTimeTest\\:\\:testToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateTest\\:\\:testToFieldDefinitionDefaultCurrentDate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateTest\\:\\:testToFieldDefinitionDefaultEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateTest\\:\\:testToFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateTest\\:\\:testToStorageFieldDefinitionDefaultCurrentDate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateTest\\:\\:testToStorageFieldDefinitionDefaultEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\DateTest\\:\\:testToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ISBNTest\\:\\:providerForTestToFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ISBNTest\\:\\:testToFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ISBNTest\\:\\:testToFieldDefinition\\(\\) has parameter \\$dataInt with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\ISBNTest\\:\\:testToFieldDefinition\\(\\) has parameter \\$excpectedIsbn13Value with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\KeywordTest\\:\\:testToFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\KeywordTest\\:\\:testToFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\KeywordTest\\:\\:testToStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\KeywordTest\\:\\:testToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\MediaTest\\:\\:testToFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/MediaTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\MediaTest\\:\\:testToStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/MediaTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\MediaTest\\:\\:\\$converter has no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/MediaTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListTest\\:\\:testToFieldDefinitionMultiple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListTest\\:\\:testToFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListTest\\:\\:testToFieldValueEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListTest\\:\\:testToStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListTest\\:\\:testToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationListTest\\:\\:testToStorageValueEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationTest\\:\\:testToStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationTest\\:\\:\\$converter \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationConverter&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\RelationConverter\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\SelectionTest\\:\\:testToFieldDefinitionMultiple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\SelectionTest\\:\\:testToFieldDefinitionSingleEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\SelectionTest\\:\\:testToFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\SelectionTest\\:\\:testToFieldValueEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\SelectionTest\\:\\:testToStorageFieldDefinitionMultiple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\SelectionTest\\:\\:testToStorageFieldDefinitionSingle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\SelectionTest\\:\\:testToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\SelectionTest\\:\\:testToStorageValueEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextBlockTest\\:\\:testToFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextBlockTest\\:\\:testToFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextBlockTest\\:\\:testToStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextBlockTest\\:\\:testToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextBlockTest\\:\\:\\$longText has no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextLineTest\\:\\:testToFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextLineTest\\:\\:testToFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextLineTest\\:\\:testToStorageFieldDefinitionNoValidator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextLineTest\\:\\:testToStorageFieldDefinitionWithValidator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TextLineTest\\:\\:testToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TimeTest\\:\\:testToFieldDefinitionDefaultCurrentTime\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TimeTest\\:\\:testToFieldDefinitionDefaultEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TimeTest\\:\\:testToFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TimeTest\\:\\:testToStorageFieldDefinitionDefaultCurrentTime\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TimeTest\\:\\:testToStorageFieldDefinitionDefaultEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\TimeTest\\:\\:testToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\UrlTest\\:\\:testToFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\UrlTest\\:\\:testToFieldValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\UrlTest\\:\\:testToStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\UrlTest\\:\\:testToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlTest.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$sortKeyInt \\(int\\) does not accept false\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlTest.php + + - + message: "#^Property Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageFieldValue\\:\\:\\$sortKeyString \\(string\\) does not accept false\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValueConverterRegistryTest\\:\\:testGetNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValueConverterRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValueConverterRegistryTest\\:\\:testGetStorage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValueConverterRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValueConverterRegistryTest\\:\\:testRegister\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/FieldValueConverterRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:assertContentVersionAttributesLanguages\\(\\) has parameter \\$expectation with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:assertValuesInRows\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:insertRelationFixture\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:storeFixture\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCreateFixtureForMapperExtractContentFromRows\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCreateFixtureForMapperExtractContentFromRowsMultipleVersions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteFieldsWithSecondArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteNames\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteNamesWithSecondArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteRelation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteRelationsFrom\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteRelationsTo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteRelationsWithSecondArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteVersions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteVersionsWithSecondArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testGetAllLocationIds\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testGetFieldIdsByType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testGetFieldIdsByTypeWithSecondArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testGetLastVersionNumber\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertContentObject\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertNewAlwaysAvailableField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertNewField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertRelation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testListVersionNumbers\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testListVersionsForUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadNonExistentTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadRelationsByType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadRelationsByVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadRelationsNoResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadReverseRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadReverseRelationsWithType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadWithAllTranslations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadWithSingleTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testSetName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testSetStatus\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testSetStatusPublished\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testSetStatusUnknownVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateNonTranslatableField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Parameter \\#2 \\$expectedValues of method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:assertValuesInRows\\(\\) expects array\\<string\\>, array\\<int, int\\> given\\.$#" + count: 9 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:\\$databaseGateway \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\DoctrineDatabase\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\RandomSortClauseHandlerFactoryTest\\:\\:getGateways\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/RandomSortClauseHandlerFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\RandomSortClauseHandlerFactoryTest\\:\\:testGetGateway\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/RandomSortClauseHandlerFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\\\RandomSortClauseHandlerFactoryTest\\:\\:testGetGatewayNotImplemented\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Gateway/RandomSortClauseHandlerFactoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:getCacheIdentifierGeneratorMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGeneratorInterface&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGeneratorInterface\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:getInnerLanguageHandlerMock\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:getLanguageCacheMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\InMemory\\\\InMemoryCache&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\InMemory\\\\InMemoryCache\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:getLanguageHandler\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:testCreate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:testDelete\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:testLoadAll\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:testLoadByLanguageCode\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:testLoadByLanguageCodeFailure\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:testLoadFailure\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:testUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:\\$cacheIdentifierGeneratorMock \\(Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\Identifier\\\\CacheIdentifierGeneratorInterface\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:\\$innerHandlerMock \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:\\$languageCacheMock \\(Ibexa\\\\Core\\\\Persistence\\\\Cache\\\\InMemory\\\\InMemoryCache\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:\\$languageHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\) does not accept Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingLanguageHandlerTest\\:\\:\\$languageHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadAllLanguagesData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadLanguageListData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:\\$databaseGateway \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Gateway\\\\DoctrineDatabase\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Result of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Gateway\\\\DoctrineDatabase\\:\\:deleteLanguage\\(\\) \\(void\\) is used\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Gateway\\:\\:expects\\(\\)\\.$#" + count: 11 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Mapper\\:\\:expects\\(\\)\\.$#" + count: 6 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\LanguageHandlerTest\\:\\:testCreate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\LanguageHandlerTest\\:\\:testDeleteFail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\LanguageHandlerTest\\:\\:testDeleteSuccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\LanguageHandlerTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\LanguageHandlerTest\\:\\:testLoadAll\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\LanguageHandlerTest\\:\\:testLoadByLanguageCode\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\LanguageHandlerTest\\:\\:testLoadByLanguageCodeFailure\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\LanguageHandlerTest\\:\\:testLoadFailure\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\LanguageHandlerTest\\:\\:testUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\LanguageHandlerTest\\:\\:\\$gatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\LanguageHandlerTest\\:\\:\\$languageHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\LanguageHandlerTest\\:\\:\\$mapperMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Mapper\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MapperTest\\:\\:testCreateLanguageFromCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MapperTest\\:\\:testExtractLanguagesFromRows\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:getLanguageHandler\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler but returns Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:getLanguageIndicatorData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:getLanguageMaskData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:isAlwaysAvailableProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:languageIdsFromMaskProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:removeAlwaysAvailableFlagProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testExtractLanguageIdsFromMask\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testExtractLanguageIdsFromMask\\(\\) has parameter \\$expectedResult with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testGenerateLanguageIndicator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testGenerateLanguageMask\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testGenerateLanguageMask\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testGenerateLanguageMaskFromLanguagesCodes\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testGenerateLanguageMaskFromLanguagesCodes\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testIsAlwaysAvailable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testIsLanguageAlwaysAvailable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testIsLanguageAlwaysAvailableNoDefault\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testIsLanguageAlwaysAvailableOtherLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testRemoveAlwaysAvailableFlag\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testRemoveAlwaysAvailableFlag\\(\\) has parameter \\$expectedResult with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGeneratorTest\\:\\:testRemoveAlwaysAvailableFlag\\(\\) has parameter \\$langMask with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Offset string does not exist on array\\{\\}\\|array\\{eng\\-US\\?\\: Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language, eng\\-GB\\?\\: Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\}\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageAwareTestCase\\:\\:\\$languageHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageAwareTestCase\\:\\:getFullTextSearchConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LanguageAwareTestCase.php + + - + message: "#^Parameter \\#3 \\$ruleFiles of class Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased constructor expects array, array\\<int, string\\>\\|false given\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LanguageAwareTestCase.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageAwareTestCase\\:\\:\\$fieldNameGeneratorMock \\(Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldNameGenerator&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LanguageAwareTestCase.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageAwareTestCase\\:\\:\\$languageHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LanguageAwareTestCase.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageAwareTestCase\\:\\:\\$languageMaskGenerator \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGenerator\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LanguageAwareTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageHandlerMock\\:\\:delete\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LanguageHandlerMock.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageHandlerMock\\:\\:loadList\\(\\) has parameter \\$ids with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LanguageHandlerMock.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageHandlerMock\\:\\:update\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LanguageHandlerMock.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageHandlerMock\\:\\:\\$languages has no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LanguageHandlerMock.php + + - + message: "#^Cannot call method fetch\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:assertLoadLocationProperties\\(\\) has parameter \\$locationData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:buildContentTreeSelectContentWithParentQuery\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:buildGenericNodeSelectContentWithParentQuery\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:buildNodeAssignmentSelectContentWithParentQuery\\(\\) has parameter \\$fields with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:getConvertNodeAssignmentsLocationValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:getCreateLocationReturnValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:getCreateLocationValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:getLoadLocationValues\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:getLocationGateway\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:getNodeAssignmentValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:getUpdateLocationData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:providerForTestUpdatePathIdentificationString\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testChangeMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testConvertNodeAssignments\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testConvertNodeAssignments\\(\\) has parameter \\$field with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testConvertNodeAssignments\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testConvertNodeAssignmentsMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testConvertNodeAssignmentsParentHidden\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testConvertNodeAssignmentsParentInvisible\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testConvertNodeAssignmentsUpdateAssignment\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCreateLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCreateLocationNodeAssignmentCreation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCreateLocationNodeAssignmentCreation\\(\\) has parameter \\$expectedResult with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCreateLocationNodeAssignmentCreationMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCreateLocationReturnValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCreateLocationReturnValues\\(\\) has parameter \\$field with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCreateLocationReturnValues\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCreateLocationValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCreateLocationValues\\(\\) has parameter \\$field with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCreateLocationValues\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteNodeAssignment\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteNodeAssignmentWithSecondArgument\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testGetChildren\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testGetMainNodeId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testHideUnhideParentTree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testHideUnhidePartialSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testHideUnhideUpdateHidden\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testHideUpdateHidden\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadInvalidLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadLocationByRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadLocationDataByContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadLocationDataByContentLimitSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadLocationList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadParentLocationDataForDraftContentAll\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testMoveHiddenDestinationUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testMoveHiddenSourceChildUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testMoveHiddenSourceUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testMoveSubtreeAssignmentUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testMoveSubtreePathUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testRemoveLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testSetSectionForSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testSwapLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateLocation\\(\\) has parameter \\$field with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateLocation\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateLocationsContentVersionNo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdatePathIdentificationString\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdatePathIdentificationString\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdatePathIdentificationString\\(\\) has parameter \\$locationId with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdatePathIdentificationString\\(\\) has parameter \\$parentLocationId with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdatePathIdentificationString\\(\\) has parameter \\$text with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateSubtreeModificationTime\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Parameter \\#2 \\$y of method Doctrine\\\\DBAL\\\\Query\\\\Expression\\\\ExpressionBuilder\\:\\:in\\(\\) expects array\\<string\\>\\|string, array\\<int, int\\> given\\.$#" + count: 11 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:getLoadTrashValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:getLocationGateway\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:getTrashValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:getUntrashedLocationValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testCleanupTrash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testCountLocationsByContentId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testCountTrashed\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testListEmptyTrash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testListFullTrash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testListTrashItem\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testListTrashItem\\(\\) has parameter \\$key with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testListTrashItem\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testListTrashLimited\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testListTrashSortedDepth\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testListTrashSortedPathStringDesc\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testLoadTrashByLocationId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testLoadTrashByLocationId\\(\\) has parameter \\$field with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testLoadTrashByLocationId\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testRemoveElementFromTrash\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testTrashLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testTrashLocationUpdateTrashTable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testUntrashInvalidLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testUntrashLocationDefault\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testUntrashLocationDefault\\(\\) has parameter \\$property with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testUntrashLocationDefault\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testUntrashLocationInvalidOldParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testUntrashLocationInvalidParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:testUntrashLocationNewParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\\\DoctrineDatabaseTrashTest\\:\\:trashSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Parameter \\#2 \\$y of method Doctrine\\\\DBAL\\\\Query\\\\Expression\\\\ExpressionBuilder\\:\\:in\\(\\) expects array\\<string\\>\\|string, array\\<int, int\\> given\\.$#" + count: 3 + path: tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\MapperTest\\:\\:testCreateLocationFromRow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\MapperTest\\:\\:testCreateLocationFromRowWithPrefix\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\MapperTest\\:\\:testCreateLocationsFromRows\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\MapperTest\\:\\:testCreateTrashedFromRow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\MapperTest\\:\\:testGetLocationCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/MapperTest.php + + - + message: "#^Parameter \\#2 \\$prefix of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Mapper\\:\\:createLocationFromRow\\(\\) expects string, null given\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/MapperTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\MapperTest\\:\\:\\$locationCreateStructValues type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/MapperTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\MapperTest\\:\\:\\$locationRow type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/MapperTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\MapperTest\\:\\:\\$locationValues type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/MapperTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:expects\\(\\)\\.$#" + count: 34 + path: tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:expects\\(\\)\\.$#" + count: 4 + path: tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Mapper\\:\\:expects\\(\\)\\.$#" + count: 8 + path: tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\TrashHandlerTest\\:\\:getTrashHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\TrashHandlerTest\\:\\:testDeleteTrashItemStillHaveLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\TrashHandlerTest\\:\\:testFindTrashItemsWhenEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\TrashHandlerTest\\:\\:testFindTrashItemsWithLimits\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\TrashHandlerTest\\:\\:testLoadTrashItem\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\TrashHandlerTest\\:\\:testRecover\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\TrashHandlerTest\\:\\:testTrashSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\TrashHandlerTest\\:\\:testTrashSubtreeReturnsNull\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\TrashHandlerTest\\:\\:testTrashSubtreeUpdatesMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Handler\\:\\:expects\\(\\)\\.$#" + count: 6 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\:\\:expects\\(\\)\\.$#" + count: 22 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:expects\\(\\)\\.$#" + count: 4 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Mapper\\:\\:expects\\(\\)\\.$#" + count: 4 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:getLocationHandler\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:loadParentLocationsForDraftContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testChangeMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testCopySubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testCreateLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testHideUnhideUpdateHidden\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testHideUpdateHidden\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testLoadLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testLoadLocationByRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testLoadLocationSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testLoadLocationsByContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testMarkSubtreeModified\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testMoveSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testRemoveSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testSetSectionForSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testSwapLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LocationHandlerTest\\:\\:testUpdateLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php + + - + message: "#^Argument of an invalid type Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Relation\\\\CreateStruct supplied for foreach, only iterables are supported\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\:\\:createCreateStructFromContent\\(\\) invoked with 2 parameters, 1 required\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:extractContentInfoFromRowProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:extractVersionInfoFromRowProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:getContentExtractFixture\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:getLanguageHandler\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler but returns Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:getMapper\\(\\) has parameter \\$valueConverter with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:getMultipleVersionsNamesExtractFixture\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:getNamesExtractFixture\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:getRelationExtractFixture\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testConvertToStorageValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testCreateCreateStructFromContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testCreateCreateStructFromContentBasicProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testCreateCreateStructFromContentBasicProperties\\(\\) has parameter \\$data with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testCreateCreateStructFromContentFieldCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testCreateCreateStructFromContentFieldCount\\(\\) has parameter \\$data with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testCreateCreateStructFromContentFieldsNoId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testCreateCreateStructFromContentFieldsNoId\\(\\) has parameter \\$data with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testCreateCreateStructFromContentParentLocationsEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testCreateCreateStructFromContentParentLocationsEmpty\\(\\) has parameter \\$data with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testCreateCreateStructFromContentWithPreserveOriginalLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testCreateRelationFromCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testCreateVersionInfoForContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testExtractContentFromRows\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testExtractContentFromRowsMultipleVersions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testExtractContentInfoFromRow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testExtractContentInfoFromRow\\(\\) has parameter \\$fixtures with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:testExtractRelationsFromRows\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Possibly invalid array key type \\(array\\<int, string\\>\\|string\\)\\.$#" + count: 2 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\CreateStruct\\:\\:\\$name \\(array\\<string\\>\\) does not accept string\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageAwareTestCase\\:\\:\\$languageHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\MapperTest\\:\\:\\$valueConverterRegistryMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\ConverterRegistry\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteObjectState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteObjectStateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteObjectStateLinks\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testGetContentCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertObjectState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertObjectStateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertObjectStateInEmptyGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadObjectStateData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadObjectStateDataByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadObjectStateDataForContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadObjectStateGroupData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadObjectStateGroupDataByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadObjectStateGroupListData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadObjectStateListData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testSetContentState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateObjectState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateObjectStateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateObjectStateLinks\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateObjectStatePriority\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:\\$databaseGateway \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\\\DoctrineDatabase\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\MapperTest\\:\\:getObjectStateGroupRowsFixture\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\MapperTest\\:\\:getObjectStateGroupRowsFixture\\(\\) should return array\\<array\\<array\\>\\> but returns array\\<int, array\\<string, int\\|string\\>\\>\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\MapperTest\\:\\:getObjectStateRowsFixture\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\MapperTest\\:\\:getObjectStateRowsFixture\\(\\) should return array\\<array\\<array\\>\\> but returns array\\<int, array\\<string, int\\|string\\>\\>\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\MapperTest\\:\\:testCreateObjectStateFromData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\MapperTest\\:\\:testCreateObjectStateFromInputStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\MapperTest\\:\\:testCreateObjectStateGroupFromData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\MapperTest\\:\\:testCreateObjectStateGroupFromInputStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\MapperTest\\:\\:testCreateObjectStateGroupListFromData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\MapperTest\\:\\:testCreateObjectStateListFromData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/MapperTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\:\\:expects\\(\\)\\.$#" + count: 39 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Mapper\\:\\:expects\\(\\)\\.$#" + count: 18 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testCreate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testCreateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testDelete\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testDeleteGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testDeleteThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testGetContentCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testGetContentState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testLoadAllGroups\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testLoadByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testLoadByIdentifierThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testLoadGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testLoadGroupByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testLoadGroupByIdentifierThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testLoadGroupThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testLoadObjectStates\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testLoadThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testSetContentState\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testSetPriority\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:testUpdateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:\\$gatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:\\$mapperMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Mapper\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\ObjectStateHandlerTest\\:\\:\\$objectStateHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\ObjectState\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testAssignSectionToContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCountContentObjectsInSection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCountRoleAssignmentsUsingSection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteSection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertSection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadAllSectionData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadSectionData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadSectionDataByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateSection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:\\$databaseGateway \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabase\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Result of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\\\DoctrineDatabase\\:\\:assignSectionToContent\\(\\) \\(void\\) is used\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\:\\:expects\\(\\)\\.$#" + count: 12 + path: tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\SectionHandlerTest\\:\\:testAssign\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\SectionHandlerTest\\:\\:testCountRoleAssignmentsUsingSection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\SectionHandlerTest\\:\\:testCreate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\SectionHandlerTest\\:\\:testDelete\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\SectionHandlerTest\\:\\:testDeleteFailure\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\SectionHandlerTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\SectionHandlerTest\\:\\:testLoadAll\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\SectionHandlerTest\\:\\:testLoadByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\SectionHandlerTest\\:\\:testPoliciesCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\SectionHandlerTest\\:\\:testUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\SectionHandlerTest\\:\\:\\$gatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\SectionHandlerTest\\:\\:\\$sectionHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Section\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageRegistryTest\\:\\:testGetNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/StorageRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageRegistryTest\\:\\:testGetStorage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/StorageRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:getPartlyMockedTreeHandler\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:testChangeMainLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:testChangeMainLocationToLocationWithoutContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:testListVersions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:testLoadContentInfoByRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:testLoadLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:testRemoveRawContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:testRemoveSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:testSetSectionForSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:\\$contentGatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:\\$contentMapper \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:\\$fieldHandlerMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldHandler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:\\$locationGatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandlerTest\\:\\:\\$locationMapperMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Mapper&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\:\\:expects\\(\\)\\.$#" + count: 35 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\:\\:expects\\(\\)\\.$#" + count: 3 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\:\\:expects\\(\\)\\.$#" + count: 21 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Update\\\\Handler\\:\\:expects\\(\\)\\.$#" + count: 6 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:getMapperMock\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:getPartlyMockedHandler\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testAddFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testCopy\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testCreate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testCreateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testCreateVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testDeleteGroupFailure\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testDeleteGroupSuccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testDeleteSuccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testDeleteThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testGetContentCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testGetFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testLink\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testLoadAllGroups\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testLoadByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testLoadByRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testLoadContentTypes\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testLoadDefaultVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testLoadGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testLoadGroupByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testLoadNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testPublish\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testPublishNoOldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testRemoveContentTypeTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testRemoveFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testUnlinkFailure\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testUnlinkSuccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testUpdate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testUpdateFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:testUpdateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:\\$gatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:\\$mapperMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Mapper\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:\\$storageDispatcherMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\StorageDispatcherInterface&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentTypeHandlerTest\\:\\:\\$updateHandlerMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Update\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:getContentFixture\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:getContentGatewayMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:getContentMapperMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:getContentStorageHandlerMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:getFieldValueConverterMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:getMockedAction\\(\\) has parameter \\$methods with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:testApplyMultipleVersionsMultipleTranslations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:testApplyMultipleVersionsSingleTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:testApplySingleVersionMultipleTranslations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:testApplySingleVersionSingleTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:testConstructor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:testInsertExistingField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:testInsertExistingFieldUpdating\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:testInsertNewField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:testInsertNewFieldUpdating\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Parameter \\#1 \\$id of method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:getFieldReference\\(\\) expects int, null given\\.$#" + count: 6 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Parameter \\#1 \\$id of method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:getFieldReference\\(\\) expects int, string given\\.$#" + count: 3 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Field\\:\\:\\$id \\(int\\) does not accept null\\.$#" + count: 2 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:\\$contentGatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:\\$contentMapperMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:\\$contentStorageHandlerMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\AddFieldTest\\:\\:\\$fieldValueConverterMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\RemoveFieldTest\\:\\:getContentFixture\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\RemoveFieldTest\\:\\:getContentGatewayMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\RemoveFieldTest\\:\\:getContentMapperMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\RemoveFieldTest\\:\\:getContentStorageHandlerMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\RemoveFieldTest\\:\\:testApplyMultipleVersionsMultipleTranslations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\RemoveFieldTest\\:\\:testApplyMultipleVersionsSingleTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\RemoveFieldTest\\:\\:testApplySingleVersionSingleTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\RemoveFieldTest\\:\\:\\$contentGatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\RemoveFieldTest\\:\\:\\$contentMapperMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\RemoveFieldTest\\:\\:\\$contentStorageHandlerMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\RemoveFieldTest\\:\\:\\$removeFieldAction \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\\\Action\\\\RemoveField\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\ConverterRegistry\\:\\:expects\\(\\)\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdaterTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\:\\:expects\\(\\)\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdaterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdaterTest\\:\\:testApplyUpdates\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdaterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdaterTest\\:\\:testDetermineActions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdaterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdaterTest\\:\\:\\$contentGatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdaterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdaterTest\\:\\:\\$contentMapperMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Mapper\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdaterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdaterTest\\:\\:\\$contentStorageHandlerMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\StorageHandler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdaterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdaterTest\\:\\:\\$contentUpdater \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdaterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdaterTest\\:\\:\\$converterRegistryMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\ConverterRegistry\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/ContentUpdaterTest.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:countInstancesOfType\\(\\) invoked with 2 parameters, 1 required\\.$#" + count: 2 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:getTypeCreationContentClassNameExpectations\\(\\) should return array\\<array\\<string\\>\\> but returns array\\<int, array\\<int, array\\<int, int\\|string\\>\\|string\\>\\>\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:getTypeCreationExpectations\\(\\) should return array\\<array\\<string\\>\\> but returns array\\<int, array\\<int, int\\|string\\>\\>\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCountGroupsForType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCountInstancesOfTypeExist\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCountInstancesOfTypeNotExist\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCountTypesInGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteFieldDefinitionsForTypeExisting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteFieldDefinitionsForTypeNotExisting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteGroupAssignment\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteGroupAssignmentsForTypeExisting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteGroupAssignmentsForTypeNotExisting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteTypeExisting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteTypeNotExisting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertGroupAssignment\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertType\\(\\) has parameter \\$column with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertType\\(\\) has parameter \\$expectation with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertTypeContentClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertTypeContentClassName\\(\\) has parameter \\$column with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertTypeContentClassName\\(\\) has parameter \\$expectation with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadAllGroupsData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadGroupData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadGroupDataByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadTypeData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadTypeDataByIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadTypeDataByRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadTypesDataForGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testPublishTypeAndFields\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateType\\(\\) has parameter \\$expectedValue with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateType\\(\\) has parameter \\$fieldName with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateTypeName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\:\\:\\$description \\(array\\<string\\>\\) does not accept array\\<int\\|string, string\\|false\\>\\.$#" + count: 2 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\FieldDefinition\\:\\:\\$defaultValue \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\FieldValue\\) does not accept array\\<int\\|string, string\\|false\\>\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:\\$gateway \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Result of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\\\DoctrineDatabase\\:\\:updateGroup\\(\\) \\(void\\) is used\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Argument of an invalid type Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\CreateStruct supplied for foreach, only iterables are supported\\.$#" + count: 2 + path: tests/lib/Persistence/Legacy/Content/Type/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MapperTest\\:\\:getLoadGroupFixture\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MapperTest\\:\\:getLoadTypeFixture\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MapperTest\\:\\:getMaskGeneratorMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MapperTest\\:\\:testCreateGroupFromCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MapperTest\\:\\:testCreateStructFromType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MapperTest\\:\\:testExtractGroupsFromRows\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MapperTest\\:\\:testExtractTypesFromRowsSingle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MapperTest\\:\\:testToFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MapperTest\\:\\:testToStorageFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MapperTest\\:\\:testTypeFromCreateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MapperTest\\:\\:testTypeFromUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/MapperTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\:\\:expects\\(\\)\\.$#" + count: 2 + path: tests/lib/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabaseTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\:\\:expects\\(\\)\\.$#" + count: 2 + path: tests/lib/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Update\\\\Handler\\\\DoctrineDatabase\\:\\:deleteOldType\\(\\) invoked with 2 parameters, 1 required\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Update\\\\Handler\\\\DoctrineDatabaseTest\\:\\:testDeleteOldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Update\\\\Handler\\\\DoctrineDatabaseTest\\:\\:testPublishNewType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Update\\\\Handler\\\\DoctrineDatabaseTest\\:\\:testUpdateContentObjects\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Update\\\\Handler\\\\DoctrineDatabaseTest\\:\\:\\$contentUpdaterMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\ContentUpdater\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Update\\\\Handler\\\\DoctrineDatabaseTest\\:\\:\\$gatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:getGateway\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabase but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:providerForTestArchiveUrlAliasesForDeletedTranslations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:providerForTestCleanupAfterPublishHistorize\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:providerForTestCleanupAfterPublishRemovesLanguage\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:providerForTestLoadPathData\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:providerForTestLoadPathDataMultipleLanguages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testArchiveUrlAliasesForDeletedTranslations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCleanupAfterPublishHistorize\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCleanupAfterPublishHistorize\\(\\) has parameter \\$action with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCleanupAfterPublishHistorize\\(\\) has parameter \\$languageId with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCleanupAfterPublishHistorize\\(\\) has parameter \\$parentId with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCleanupAfterPublishHistorize\\(\\) has parameter \\$textMD5 with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCleanupAfterPublishRemovesLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCleanupAfterPublishRemovesLanguage\\(\\) has parameter \\$action with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCleanupAfterPublishRemovesLanguage\\(\\) has parameter \\$languageId with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCleanupAfterPublishRemovesLanguage\\(\\) has parameter \\$parentId with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCleanupAfterPublishRemovesLanguage\\(\\) has parameter \\$textMD5 with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testGetNextId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadPathData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadPathData\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadPathData\\(\\) has parameter \\$pathData with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadPathDataMultipleLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadPathDataMultipleLanguages\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadPathDataMultipleLanguages\\(\\) has parameter \\$pathData with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadUrlaliasData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadUrlaliasDataMultipleLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadUrlaliasDataNonExistent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testRemove\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testRemoveCustomAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testRemoveCustomAliasFails\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testRemoveWithId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testReparent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:\\$gateway \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Unary operation \"~\" on mixed results in an error\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:cleanupTextData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:convertData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:getSlugConverterMock\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:getSlugConverterMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverter&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:providerForTestGetUniqueCounterValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testCleanupText\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testCleanupText\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testCleanupText\\(\\) has parameter \\$method with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testCleanupText\\(\\) has parameter \\$text with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testConvert\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testConvertNoMocking\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testConvertNoMocking\\(\\) has parameter \\$defaultText with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testConvertNoMocking\\(\\) has parameter \\$expected with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testConvertNoMocking\\(\\) has parameter \\$text with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testConvertNoMocking\\(\\) has parameter \\$transformation with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testConvertWithDefaultTextFallback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testConvertWithGivenTransformation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testGetUniqueCounterValue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testGetUniqueCounterValue\\(\\) has parameter \\$isRootLevel with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testGetUniqueCounterValue\\(\\) has parameter \\$returnValue with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:testGetUniqueCounterValue\\(\\) has parameter \\$text with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Parameter \\#1 \\$transformationProcessor of class Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverter constructor expects Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:\\$configuration type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:\\$slugConverter \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverter\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:\\$slugConverterMock \\(PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\SlugConverterTest\\:\\:\\$transformationProcessorMock \\(PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php + + - + message: "#^Argument of an invalid type Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias supplied for foreach, only iterables are supported\\.$#" + count: 4 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:assertVirtualUrlAliasValid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:assertVirtualUrlAliasValid\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:getHistoryAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:getPartlyMockedHandler\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:providerForArchiveUrlAliasesForDeletedTranslations\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:providerForTestLookupCustomLocationUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:providerForTestLookupLocationMultipleLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:providerForTestLookupLocationUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:providerForTestLookupResourceUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:providerForTestLookupVirtualUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:providerForTestPublishUrlAliasForLocationComplex\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:providerForTestPublishUrlAliasForLocationSkipsReservedWord\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testArchiveUrlAliasesForDeletedTranslations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testCreateCustomUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testCreateCustomUrlAliasBehaviour\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testCreateCustomUrlAliasReusesHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testCreateCustomUrlAliasReusesHistoryOfDifferentLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testCreateCustomUrlAliasReusesLocationElement\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testCreateCustomUrlAliasReusesNopElement\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testCreateCustomUrlAliasWithNonameParts\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testCreateGlobalUrlAliasBehaviour\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testCreatedCustomUrlAliasIsLoadable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testListGlobalURLAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testListGlobalURLAliasesWithLanguageCode\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testListGlobalURLAliasesWithOffset\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testListGlobalURLAliasesWithOffsetAndLimit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testListURLAliasesForLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadAutogeneratedUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadAutogeneratedUrlAlias\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadAutogeneratedUrlAlias\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadAutogeneratedUrlAlias\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadAutogeneratedUrlAlias\\(\\) has parameter \\$locationId with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadAutogeneratedUrlAlias\\(\\) has parameter \\$pathData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadAutogeneratedUrlAlias\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadHistoryUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadResourceUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadResourceUrlAlias\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadResourceUrlAlias\\(\\) has parameter \\$destination with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadResourceUrlAlias\\(\\) has parameter \\$forward with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadResourceUrlAlias\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadResourceUrlAlias\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadResourceUrlAlias\\(\\) has parameter \\$pathData with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadResourceUrlAlias\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadUrlAliasThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadVirtualUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadVirtualUrlAlias\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLoadVirtualUrlAlias\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationCopiedCopiedLocationAliasIsValid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationCopiedCopiedSubtreeIsValid\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationCopiedHistoryNotCopied\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationCopiedSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationCopiedSubtreeHistoryNotCopied\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationDeleted\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationMovedHistorize\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationMovedHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationMovedHistorySubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationMovedReparent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationMovedReparentHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationMovedReparentSubtree\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationMovedReparentSubtreeHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedMultipleLanguagesDifferentLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedMultipleLanguagesDifferentLanguagesSimple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedMultipleLanguagesSimple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedMultipleLanguagesUpdatesLocationPathIdentificationString\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedMultipleLanguagesWithCompositeHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedSiblingsSameName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedSiblingsSameNameMultipleLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedSiblingsSameNameReverse\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedSiblingsSimple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedSiblingsSimpleReverse\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedSiblingsSimpleWithHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedSiblingsSimpleWithHistoryReverse\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedSimple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedSimpleWithConflict\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedSimpleWithHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedUpdatesLocationPathIdentificationString\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedWithReusingExternalHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedWithReusingNopEntry\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLocationSwappedWithReusingNopEntryCustomAliasIsDestroyed\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAlias\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAlias\\(\\) has parameter \\$destination with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAlias\\(\\) has parameter \\$forward with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAlias\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAlias\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAlias\\(\\) has parameter \\$pathData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAlias\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAliasCaseCorrection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAliasCaseCorrection\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAliasCaseCorrection\\(\\) has parameter \\$destination with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAliasCaseCorrection\\(\\) has parameter \\$forward with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAliasCaseCorrection\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAliasCaseCorrection\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAliasCaseCorrection\\(\\) has parameter \\$pathData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupCustomLocationUrlAliasCaseCorrection\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationCaseCorrection\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationCaseCorrection\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationCaseCorrection\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationCaseCorrection\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationCaseCorrection\\(\\) has parameter \\$locationId with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationCaseCorrection\\(\\) has parameter \\$pathData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationCaseCorrection\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationHistoryUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationMultipleLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationMultipleLanguages\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationMultipleLanguages\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationMultipleLanguages\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationMultipleLanguages\\(\\) has parameter \\$locationId with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationMultipleLanguages\\(\\) has parameter \\$pathData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationMultipleLanguages\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationUrlAlias\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationUrlAlias\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationUrlAlias\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationUrlAlias\\(\\) has parameter \\$locationId with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationUrlAlias\\(\\) has parameter \\$pathData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupLocationUrlAlias\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAlias\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAlias\\(\\) has parameter \\$destination with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAlias\\(\\) has parameter \\$forward with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAlias\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAlias\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAlias\\(\\) has parameter \\$pathData with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAlias\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAliasCaseInsensitive\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAliasCaseInsensitive\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAliasCaseInsensitive\\(\\) has parameter \\$destination with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAliasCaseInsensitive\\(\\) has parameter \\$forward with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAliasCaseInsensitive\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAliasCaseInsensitive\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAliasCaseInsensitive\\(\\) has parameter \\$pathData with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupResourceUrlAliasCaseInsensitive\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupUppercaseIri\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupVirtualUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupVirtualUrlAlias\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testLookupVirtualUrlAlias\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasCreatesUniqueAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationComplex\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationComplex\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationComplex\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationComplex\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationComplex\\(\\) has parameter \\$locationId with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationComplex\\(\\) has parameter \\$pathData with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationComplex\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationDowngradesOldEntryRemovesLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationDowngradesOldEntryToHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationRepublish\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationReusesCustomAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationReusesHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationReusesHistoryOfDifferentLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationReusingNopElement\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationReusingNopElementChangesCustomPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationReusingNopElementChangesCustomPathAndCreatesHistory\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationSameAliasForMultipleLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationSkipsReservedWord\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationSkipsReservedWord\\(\\) has parameter \\$alias with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationSkipsReservedWord\\(\\) has parameter \\$text with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasForLocationUpdatesLocationPathIdentificationString\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasReuseAutogeneratedCleanup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasReuseHistoryCleanup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:testPublishUrlAliasReuseNopCleanupCustomAliasIsDestroyed\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Parameter \\#1 \\$url of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\Handler\\:\\:lookup\\(\\) expects string, int\\|string given\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Parameter \\#3 \\$ruleFiles of class Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased constructor expects array, array\\<int, string\\>\\|false given\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:\\$languageHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:\\$languageMaskGenerator \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\MaskGenerator\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasHandlerTest\\:\\:\\$locationGateway \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasMapperTest\\:\\:getExpectation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasMapperTest\\:\\:providerForTestExtractUrlAliasFromData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasMapperTest\\:\\:testExtractLanguageCodesFromData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasMapperTest\\:\\:testExtractUrlAliasFromData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasMapperTest\\:\\:testExtractUrlAliasFromData\\(\\) has parameter \\$index with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasMapperTest\\:\\:testExtractUrlAliasListFromData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasMapperTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlAlias\\\\UrlAliasMapperTest\\:\\:\\$fixture has no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDeleteUrlWildcard\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsertUrlWildcard\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadUrlWildcardData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadUrlWildcardsData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadUrlWildcardsDataWithOffset\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadUrlWildcardsDataWithOffsetAndLimit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:\\$fixtureData has no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:\\$gateway \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\Gateway\\\\DoctrineDatabase\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\UrlWildcardHandlerTest\\:\\:testCreate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\UrlWildcardHandlerTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\UrlWildcardHandlerTest\\:\\:testLoadAll\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\UrlWildcardHandlerTest\\:\\:testLoadAllWithOffset\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\UrlWildcardHandlerTest\\:\\:testLoadAllWithOffsetAndLimit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\UrlWildcardHandlerTest\\:\\:testLoadThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\UrlWildcardHandlerTest\\:\\:testRemove\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\UrlWildcardHandlerTest\\:\\:\\$fixtureData has no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\UrlWildcardHandlerTest\\:\\:\\$urlWildcardHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlWildcard\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\UrlWildcardMapperTest\\:\\:testCreateUrlWildcard\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\UrlWildcardMapperTest\\:\\:testExtractUrlWildcardFromRow\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\UrlWildcard\\\\UrlWildcardMapperTest\\:\\:testExtractUrlWildcardsFromRows\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\FieldValue\\\\Converter\\\\ImageConverterTest\\:\\:fieldValueToXmlProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/FieldValue/Converter/ImageConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\FieldValue\\\\Converter\\\\ImageConverterTest\\:\\:xmlToFieldValueProvider\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/FieldValue/Converter/ImageConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\BaseCriterionVisitorQueryBuilderTestCase\\:\\:getBaseCriterionQueryBuilders\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\BaseCriterionVisitorQueryBuilderTestCase\\:\\:getFilteringCriteriaQueryData\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\BaseCriterionVisitorQueryBuilderTestCase\\:\\:testVisitCriteriaProducesQuery\\(\\) has parameter \\$expectedParameterValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionQueryBuilder\\\\Content\\\\LanguageCodeQueryBuilderQueryBuilderTest\\:\\:getFilteringCriteriaQueryData\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/LanguageCodeQueryBuilderQueryBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionQueryBuilder\\\\Content\\\\Type\\\\ContentTypeGroupIdQueryBuilderTest\\:\\:getFilteringCriteriaQueryData\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/ContentTypeGroupIdQueryBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionQueryBuilder\\\\Content\\\\Type\\\\ContentTypeQueryBuildersTest\\:\\:getFilteringCriteriaQueryData\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/ContentTypeQueryBuildersTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionQueryBuilder\\\\Location\\\\AncestorQueryBuilderTest\\:\\:getFilteringCriteriaQueryData\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionQueryBuilder\\\\Location\\\\LocationIdQueryBuilderTest\\:\\:getFilteringCriteriaQueryData\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/LocationIdQueryBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionQueryBuilder\\\\Location\\\\ParentLocationQueryBuilderTest\\:\\:getFilteringCriteriaQueryData\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/ParentLocationQueryBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Filter\\\\CriterionQueryBuilder\\\\LogicalOperatorQueryBuilderQueryBuilderTest\\:\\:getFilteringCriteriaQueryData\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalOperatorQueryBuilderQueryBuilderTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Container\\:\\:get\\(\\)\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:loadNotification\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCountUserNotifications\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCountUserPendingNotifications\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testDelete\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testGetNotificationById\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsert\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadUserNotifications\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateNotification\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\HandlerTest\\:\\:testCountNotifications\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\HandlerTest\\:\\:testCountPendingNotifications\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\HandlerTest\\:\\:testCreateNotification\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\HandlerTest\\:\\:testDelete\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\HandlerTest\\:\\:testGetNotificationById\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\HandlerTest\\:\\:testLoadUserNotifications\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\HandlerTest\\:\\:testUpdateNotification\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\MapperTest\\:\\:testCreateNotificationFromUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\MapperTest\\:\\:testExtractNotificationsFromRows\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Notification\\\\MapperTest\\:\\:testExtractNotificationsFromRowsThrowsRuntimeException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Notification/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Setting\\\\SettingHandlerTest\\:\\:getGatewayMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Setting\\\\Gateway&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Setting\\\\Gateway\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Setting/SettingHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Setting\\\\SettingHandlerTest\\:\\:\\$gatewayMock \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Setting\\\\Gateway\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Setting/SettingHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Setting\\\\SettingHandlerTest\\:\\:\\$settingHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Setting\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/Setting/SettingHandlerTest.php + + - + message: "#^Generator expects value type Doctrine\\\\DBAL\\\\Connection\\|PHPUnit\\\\Framework\\\\MockObject\\\\MockObject, array\\<int, \\(Doctrine\\\\DBAL\\\\Connection&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\)\\|string\\> given\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/SharedGateway/GatewayFactoryTest.php + + - + message: "#^Parameter \\#1 \\$expected of static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertInstanceOf\\(\\) expects class\\-string\\<object\\>, string given\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/SharedGateway/GatewayFactoryTest.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TestCase\\:\\:assertPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TestCase\\:\\:assertPropertiesCorrect\\(\\) has parameter \\$properties with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TestCase\\:\\:assertQueryResult\\(\\) has parameter \\$expectation with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TestCase\\:\\:assertStructsEqual\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TestCase\\:\\:assertStructsEqual\\(\\) has parameter \\$propertyNames with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TestCase\\:\\:getPublicPropertyNames\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TestCase\\:\\:getResultTextRepresentation\\(\\) has parameter \\$result with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TestCase.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TestCase\\:\\:\\$db \\(string\\) does not accept string\\|null\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TestCase.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TestCase\\:\\:\\$dsn \\(string\\) does not accept string\\|false\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandlerTest\\:\\:getContentTypeHandlerMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\MemoryCachingHandler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TransactionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandlerTest\\:\\:getLanguageHandlerMock\\(\\) should return Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Language\\\\CachingHandler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TransactionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandlerTest\\:\\:testBeginTransaction\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TransactionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandlerTest\\:\\:testCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TransactionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandlerTest\\:\\:testCommitException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TransactionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandlerTest\\:\\:testRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TransactionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandlerTest\\:\\:testRollbackException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TransactionHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandlerTest\\:\\:\\$connectionMock \\(Doctrine\\\\DBAL\\\\Connection&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TransactionHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandlerTest\\:\\:\\$contentTypeHandlerMock \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TransactionHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandlerTest\\:\\:\\$languageHandlerMock \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TransactionHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandlerTest\\:\\:\\$transactionHandler \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\TransactionHandler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/TransactionHandlerTest.php + + - + message: "#^Parameter \\#1 \\$url of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\\\DoctrineDatabase\\:\\:loadUrlDataByUrl\\(\\) expects int, string given\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:\\$fixtureData type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:\\$gateway \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Gateway\\\\DoctrineDatabase\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\HandlerTest\\:\\:getUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\HandlerTest\\:\\:getUrl\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\HandlerTest\\:\\:getUrl\\(\\) has parameter \\$urlAddr with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\HandlerTest\\:\\:testFind\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\HandlerTest\\:\\:testFindUsages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\HandlerTest\\:\\:testLoadByIdWithUrlData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\HandlerTest\\:\\:testLoadByIdWithoutUrlData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\HandlerTest\\:\\:testLoadByUrlWithUrlData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\HandlerTest\\:\\:testLoadByUrlWithoutUrlData\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\HandlerTest\\:\\:testUpdateUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/HandlerTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\URL\\\\URL\\:\\:\\$url \\(string\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\URL\\\\URL\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\MapperTest\\:\\:testCreateURLFromUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\MapperTest\\:\\:testExtractURLsFromRows\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\CriterionHandlerTest\\:\\:assertHandlerAcceptsCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\CriterionHandlerTest\\:\\:assertHandlerRejectsCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\CriterionHandlerTest\\:\\:testAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\CriterionHandlerTest\\:\\:testHandle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTest.php + + - + message: "#^Parameter \\#1 \\$criterion of method Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\:\\:accept\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\Query\\\\Criterion, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 2 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTest.php + + - + message: "#^Parameter \\#1 \\$originalClassName of method PHPUnit\\\\Framework\\\\TestCase\\:\\:createMock\\(\\) expects class\\-string\\<mixed\\>, string given\\.$#" + count: 2 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTest.php + + - + message: "#^Unable to resolve the template type T in call to method PHPUnit\\\\Framework\\\\TestCase\\:\\:createMock\\(\\)$#" + count: 2 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\LogicalAndTest\\:\\:testAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalAndTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\LogicalNotTest\\:\\:testAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalNotTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\LogicalOrTest\\:\\:testAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalOrTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\MatchAllTest\\:\\:testAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/MatchAllTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\MatchAllTest\\:\\:testHandle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/MatchAllTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\MatchNoneTest\\:\\:testAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/MatchNoneTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\MatchNoneTest\\:\\:testHandle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/MatchNoneTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\PatternTest\\:\\:testAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/PatternTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\PatternTest\\:\\:testHandle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/PatternTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\ValidityTest\\:\\:testAccept\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/ValidityTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\URL\\\\Query\\\\CriterionHandler\\\\ValidityTest\\:\\:testHandle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/ValidityTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:\\$databaseGateway \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Gateway\\\\DoctrineDatabase\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:\\$databaseGateway \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\Gateway\\\\DoctrineDatabase\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\LimitationConverterTest\\:\\:getLimitationConverter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/Role/LimitationConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\LimitationConverterTest\\:\\:testObjectStateToLegacy\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/Role/LimitationConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Role\\\\LimitationConverterTest\\:\\:testObjectStateToSPI\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/Role/LimitationConverterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:createRole\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:getDummyUser\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:getGatewayReturnValue\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:getValidUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:getValidUserToken\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:getValidUserToken\\(\\) has parameter \\$time with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testAddPolicyLimitationValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testAddPolicyLimitations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testAddPolicyPolicyId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testAddPolicyToRoleLimitations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testAddRoleToUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testAddRoleToUserWithComplexLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testAddRoleToUserWithLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testCreateNewRoleRoleId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testCreateNewRoleWithoutPolicies\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testCreateRoleDraftWithoutPolicies\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testCreateUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testDeleteNonExistingUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testDeletePolicy\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testDeletePolicyLimitationValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testDeletePolicyLimitations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testDeleteRole\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testDeleteRoleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testExpireUserToken\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testImplicitlyCreatePolicies\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadComplexRoleAssignments\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadMultipleUsersByEmail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadMultipleUsersByLogin\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadPoliciesForUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadRole\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadRoleAssignmentsByGroupId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadRoleAssignmentsByGroupIdInherited\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadRoleDraftByRoleId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadRoleWithPolicies\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadRoleWithPoliciesAndGroups\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadRoleWithPolicyLimitations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadRoles\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadUnknownUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadUserByEmail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadUserByEmailNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadUserByLogin\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadUserByToken\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadUserByTokenNotFound\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testLoadUsersByEmail\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testRemoveUserRoleAssociation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testRoleDraftOnlyHavePolicyDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testUpdatePolicies\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testUpdateRole\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testUpdateUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testUpdateUserSettings\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:testUpdateUserToken\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Parameter \\#1 \\$handler of method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\UserHandlerTest\\:\\:createTestRole\\(\\) expects Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\User\\\\Handler, Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler given\\.$#" + count: 5 + path: tests/lib/Persistence/Legacy/User/UserHandlerTest.php + + - + message: "#^Cannot call method fetchAll\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:loadUserPreference\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testCountUserPreferences\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testInsert\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testLoadUserPreferences\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\Gateway\\\\DoctrineDatabaseTest\\:\\:testUpdateUserPreference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabaseTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\HandlerTest\\:\\:testCountUserPreferences\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/UserPreference/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\HandlerTest\\:\\:testLoadUserPreferences\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/UserPreference/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\HandlerTest\\:\\:testSetUserPreference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/UserPreference/HandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\UserPreference\\\\MapperTest\\:\\:testExtractUserPreferencesFromRows\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/Legacy/UserPreference/MapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Limitation\\\\Target\\\\Builder\\\\VersionBuilderTest\\:\\:providerForTestBuild\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/Limitation/Target/Builder/VersionBuilderTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorDefinitionBasedParserTest\\:\\:getTestFiles\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorDefinitionBasedParserTest\\:\\:testParse\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorDefinitionBasedParserTest\\:\\:testParse\\(\\) has parameter \\$file with no type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedParserTest.php + + - + message: "#^Parameter \\#2 \\$array of function array_map expects array, array\\<int, string\\>\\|false given\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorDefinitionBasedTest\\:\\:getProcessor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorDefinitionBasedTest\\:\\:testAllNormalizations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorDefinitionBasedTest\\:\\:testApplyAllLowercaseNormalizations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorDefinitionBasedTest\\:\\:testSimpleNormalizationLowercase\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorDefinitionBasedTest\\:\\:testSimpleNormalizationUppercase\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedTest.php + + - + message: "#^Parameter \\#3 \\$ruleFiles of class Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased constructor expects array, array\\<int, string\\>\\|false given\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedTest.php + + - + message: "#^Class Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased\\\\Parser constructor invoked with 1 parameter, 0 required\\.$#" + count: 10 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPcreCompilerTest\\:\\:applyTransformations\\(\\) has parameter \\$transformations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPcreCompilerTest\\:\\:applyTransformations\\(\\) should return string but returns string\\|null\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPcreCompilerTest\\:\\:testCompileMap\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPcreCompilerTest\\:\\:testCompileMapAscii\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPcreCompilerTest\\:\\:testCompileMapKeep\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPcreCompilerTest\\:\\:testCompileMapRemove\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPcreCompilerTest\\:\\:testCompileMapUnicode\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPcreCompilerTest\\:\\:testCompileModuloTranspose\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPcreCompilerTest\\:\\:testCompileReplace\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPcreCompilerTest\\:\\:testCompileTranspose\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPcreCompilerTest\\:\\:testCompileTransposeAsciiLowercase\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPcreCompilerTest\\:\\:testCompileTransposePlus\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Parameter \\#3 \\$subject of function preg_replace_callback expects array\\|string, string\\|null given\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPreprocessedBasedTest\\:\\:getProcessor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPreprocessedBasedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPreprocessedBasedTest\\:\\:testAllNormalizations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPreprocessedBasedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPreprocessedBasedTest\\:\\:testSimpleNormalizationLowercase\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPreprocessedBasedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\TransformationProcessorPreprocessedBasedTest\\:\\:testSimpleNormalizationUppercase\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPreprocessedBasedTest.php + + - + message: "#^Parameter \\#2 \\$ruleFiles of class Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\PreprocessedBased constructor expects array, array\\<int, string\\>\\|false given\\.$#" + count: 1 + path: tests/lib/Persistence/TransformationProcessor/TransformationProcessorPreprocessedBasedTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\AbstractQueryTypeTest\\:\\:dataProviderForGetQuery\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/AbstractQueryTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\AbstractQueryTypeTest\\:\\:getExpectedSupportedParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/AbstractQueryTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\AbstractQueryTypeTest\\:\\:testGetQuery\\(\\) has parameter \\$parameters with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/AbstractQueryTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\AncestorsQueryTypeTest\\:\\:dataProviderForGetQuery\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/AncestorsQueryTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\AncestorsQueryTypeTest\\:\\:getExpectedSupportedParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/AncestorsQueryTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\ChildrenQueryTypeTest\\:\\:dataProviderForGetQuery\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/ChildrenQueryTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\ChildrenQueryTypeTest\\:\\:getExpectedSupportedParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/ChildrenQueryTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\GeoLocationQueryTypeTest\\:\\:dataProviderForGetQuery\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/GeoLocationQueryTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\GeoLocationQueryTypeTest\\:\\:getExpectedSupportedParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/GeoLocationQueryTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\RelatedToContentQueryTypeTest\\:\\:dataProviderForGetQuery\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/RelatedToContentQueryTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\RelatedToContentQueryTypeTest\\:\\:getExpectedSupportedParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/RelatedToContentQueryTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\SiblingsQueryTypeTest\\:\\:dataProviderForGetQuery\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/SiblingsQueryTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\SiblingsQueryTypeTest\\:\\:getExpectedSupportedParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/SiblingsQueryTypeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortSpecLexerStub\\:\\:__construct\\(\\) has parameter \\$tokens with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/SortSpec/SortSpecLexerStub.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortSpecLexerTest\\:\\:dataProviderForTokenize\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/SortSpec/SortSpecLexerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortSpecLexerTest\\:\\:testTokenize\\(\\) has parameter \\$expectedTokens with no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/SortSpec/SortSpecLexerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortSpecParserTest\\:\\:dataProviderForParseSortDirection\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/SortSpec/SortSpecParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\SortSpec\\\\SortSpecParserTest\\:\\:testParseSortDirection\\(\\) has parameter \\$input with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/SortSpec/SortSpecParserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\SubtreeQueryTest\\:\\:dataProviderForGetQuery\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/SubtreeQueryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\QueryType\\\\BuiltIn\\\\SubtreeQueryTest\\:\\:getExpectedSupportedParameters\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/QueryType/BuiltIn/SubtreeQueryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\ContentThumbnail\\\\StaticStrategyTest\\:\\:testStaticStrategy\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/ContentThumbnail/StaticStrategyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\ContentValidator\\\\ContentValidatorStrategyTest\\:\\:buildContentValidator\\(\\) has parameter \\$validationReturn with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/ContentValidator/ContentValidatorStrategyTest.php + + - + message: "#^Method class@anonymous/tests/lib/Repository/ContentValidator/ContentValidatorStrategyTest\\.php\\:87\\:\\:__construct\\(\\) has parameter \\$validationReturn with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/ContentValidator/ContentValidatorStrategyTest.php + + - + message: "#^Method class@anonymous/tests/lib/Repository/ContentValidator/ContentValidatorStrategyTest\\.php\\:87\\:\\:validate\\(\\) has parameter \\$context with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/ContentValidator/ContentValidatorStrategyTest.php + + - + message: "#^Property class@anonymous/tests/lib/Repository/ContentValidator/ContentValidatorStrategyTest\\.php\\:87\\:\\:\\$validationReturn type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/ContentValidator/ContentValidatorStrategyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\BookmarkServiceDecoratorTest\\:\\:testCreateBookmarkDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/BookmarkServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\BookmarkServiceDecoratorTest\\:\\:testDeleteBookmarkDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/BookmarkServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\BookmarkServiceDecoratorTest\\:\\:testIsBookmarkedDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/BookmarkServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\BookmarkServiceDecoratorTest\\:\\:testLoadBookmarksDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/BookmarkServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/BookmarkServiceDecoratorTest\\.php\\:21 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\BookmarkService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/BookmarkServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testAddRelationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testCopyContentDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testCreateContentDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testCreateContentDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testDeleteContentDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testDeleteRelationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testDeleteTranslationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testDeleteTranslationFromDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testDeleteVersionDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testHideContentDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadContentByContentInfoDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadContentByRemoteIdDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadContentByVersionInfoDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadContentDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadContentDraftsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadContentInfoByRemoteIdDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadContentInfoDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadContentInfoListDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadContentListByContentInfoDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadRelationsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadReverseRelationsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadVersionInfoByIdDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadVersionInfoDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testLoadVersionsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testNewContentCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testNewContentMetadataUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testNewContentUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testPublishVersionDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testRevealContentDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testUpdateContentDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentServiceDecoratorTest\\:\\:testUpdateContentMetadataDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$contentIds of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:loadContentInfoList\\(\\) expects array\\<int\\>, array\\<int, string\\> given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/ContentServiceDecoratorTest\\.php\\:35 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Parameter \\#2 \\$locationCreateStructs of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:createContent\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LocationCreateStruct\\>, array\\<int, string\\> given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testAddFieldDefinitionDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testAssignContentTypeGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testCopyContentTypeDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testCreateContentTypeDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testCreateContentTypeDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testCreateContentTypeGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testDeleteContentTypeDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testDeleteContentTypeGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testIsContentTypeUsedDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testLoadContentTypeByIdentifierDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testLoadContentTypeByRemoteIdDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testLoadContentTypeDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testLoadContentTypeDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testLoadContentTypeGroupByIdentifierDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testLoadContentTypeGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testLoadContentTypeGroupsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testLoadContentTypeListDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testLoadContentTypesDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testNewContentTypeCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testNewContentTypeGroupCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testNewContentTypeGroupUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testNewContentTypeUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testNewFieldDefinitionCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testNewFieldDefinitionUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testPublishContentTypeDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testRemoveContentTypeTranslationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testRemoveFieldDefinitionDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testUnassignContentTypeGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testUpdateContentTypeDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testUpdateContentTypeGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ContentTypeServiceDecoratorTest\\:\\:testUpdateFieldDefinitionDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$contentTypeIds of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:loadContentTypeList\\(\\) expects array\\<int\\>, array\\<int, string\\> given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest\\.php\\:31 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Parameter \\#2 \\$contentTypeGroups of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentTypeService\\:\\:createContentType\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeGroup\\>, array\\<int, string\\> given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\FieldTypeServiceDecoratorTest\\:\\:testGetFieldTypeDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/FieldTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\FieldTypeServiceDecoratorTest\\:\\:testGetFieldTypesDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/FieldTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\FieldTypeServiceDecoratorTest\\:\\:testHasFieldTypeDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/FieldTypeServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/FieldTypeServiceDecoratorTest\\.php\\:20 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\FieldTypeService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/FieldTypeServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LanguageServiceDecoratorTest\\:\\:testCreateLanguageDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LanguageServiceDecoratorTest\\:\\:testDeleteLanguageDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LanguageServiceDecoratorTest\\:\\:testDisableLanguageDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LanguageServiceDecoratorTest\\:\\:testEnableLanguageDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LanguageServiceDecoratorTest\\:\\:testGetDefaultLanguageCodeDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LanguageServiceDecoratorTest\\:\\:testLoadLanguageByIdDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LanguageServiceDecoratorTest\\:\\:testLoadLanguageDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LanguageServiceDecoratorTest\\:\\:testLoadLanguageListByCodeDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LanguageServiceDecoratorTest\\:\\:testLoadLanguageListByIdDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LanguageServiceDecoratorTest\\:\\:testLoadLanguagesDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LanguageServiceDecoratorTest\\:\\:testNewLanguageCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LanguageServiceDecoratorTest\\:\\:testUpdateLanguageNameDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/LanguageServiceDecoratorTest\\.php\\:22 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LanguageService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$languageIds of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LanguageService\\:\\:loadLanguageListById\\(\\) expects array\\<int\\>, array\\<int, string\\> given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testCopySubtreeDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testCreateLocationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testDeleteLocationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testGetAllLocationsCountDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testGetLocationChildCountDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testHideLocationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testLoadAllLocationsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testLoadLocationByRemoteIdDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testLoadLocationChildrenDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testLoadLocationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testLoadLocationListDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testLoadLocationsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testLoadParentLocationsForDraftContentDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testMoveSubtreeDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testNewLocationCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testNewLocationUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testSwapLocationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testUnhideLocationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\LocationServiceDecoratorTest\\:\\:testUpdateLocationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/LocationServiceDecoratorTest\\.php\\:29 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\NotificationServiceDecoratorTest\\:\\:testCreateNotificationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\NotificationServiceDecoratorTest\\:\\:testDeleteNotificationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\NotificationServiceDecoratorTest\\:\\:testGetNotificationCountDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\NotificationServiceDecoratorTest\\:\\:testGetNotificationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\NotificationServiceDecoratorTest\\:\\:testGetPendingNotificationCountDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\NotificationServiceDecoratorTest\\:\\:testLoadNotificationsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\NotificationServiceDecoratorTest\\:\\:testMarkNotificationAsReadDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/NotificationServiceDecoratorTest\\.php\\:22 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\NotificationService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testCreateObjectStateDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testCreateObjectStateGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testDeleteObjectStateDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testDeleteObjectStateGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testGetContentCountDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testGetContentStateDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testLoadObjectStateDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testLoadObjectStateGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testLoadObjectStateGroupsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testLoadObjectStatesDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testNewObjectStateCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testNewObjectStateGroupCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testNewObjectStateGroupUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testNewObjectStateUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testSetContentStateDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testSetPriorityOfObjectStateDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testUpdateObjectStateDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\ObjectStateServiceDecoratorTest\\:\\:testUpdateObjectStateGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest\\.php\\:27 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ObjectStateService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testAddPolicyByRoleDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testAssignRoleToUserDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testAssignRoleToUserGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testCreateRoleDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testCreateRoleDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testDeleteRoleDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testDeleteRoleDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testGetLimitationTypeDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testGetLimitationTypesByModuleFunctionDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testGetRoleAssignmentsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testGetRoleAssignmentsForUserDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testGetRoleAssignmentsForUserGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testLoadRoleAssignmentDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testLoadRoleByIdentifierDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testLoadRoleDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testLoadRoleDraftByRoleIdDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testLoadRoleDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testLoadRolesDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testNewPolicyCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testNewPolicyUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testNewRoleCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testNewRoleUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testPublishRoleDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testRemovePolicyByRoleDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testRemoveRoleAssignmentDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testUpdatePolicyByRoleDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\RoleServiceDecoratorTest\\:\\:testUpdateRoleDraftDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/RoleServiceDecoratorTest\\.php\\:34 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\RoleService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecoratorTest\\:\\:getSearchEngineCapabilities\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SearchServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecoratorTest\\:\\:testFindContentDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SearchServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecoratorTest\\:\\:testFindContentInfoDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SearchServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecoratorTest\\:\\:testFindLocationsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SearchServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecoratorTest\\:\\:testFindSingleDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SearchServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SearchServiceDecoratorTest\\:\\:testSuggestDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SearchServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/SearchServiceDecoratorTest\\.php\\:23 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SearchService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SearchServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SectionServiceDecoratorTest\\:\\:testAssignSectionDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SectionServiceDecoratorTest\\:\\:testAssignSectionToSubtreeDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SectionServiceDecoratorTest\\:\\:testCountAssignedContentsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SectionServiceDecoratorTest\\:\\:testCreateSectionDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SectionServiceDecoratorTest\\:\\:testDeleteSectionDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SectionServiceDecoratorTest\\:\\:testIsSectionUsedDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SectionServiceDecoratorTest\\:\\:testLoadSectionByIdentifierDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SectionServiceDecoratorTest\\:\\:testLoadSectionDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SectionServiceDecoratorTest\\:\\:testLoadSectionsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SectionServiceDecoratorTest\\:\\:testNewSectionCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SectionServiceDecoratorTest\\:\\:testNewSectionUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SectionServiceDecoratorTest\\:\\:testUpdateSectionDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/SectionServiceDecoratorTest\\.php\\:27 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SectionService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SettingServiceDecoratorTest\\:\\:testCreateSettingDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SettingServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SettingServiceDecoratorTest\\:\\:testDeleteSettingDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SettingServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SettingServiceDecoratorTest\\:\\:testLoadSettingDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SettingServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SettingServiceDecoratorTest\\:\\:testNewSettingCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SettingServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SettingServiceDecoratorTest\\:\\:testNewSettingUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SettingServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\SettingServiceDecoratorTest\\:\\:testUpdateSettingDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SettingServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/SettingServiceDecoratorTest\\.php\\:26 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\SettingService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/SettingServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\TranslationServiceDecoratorTest\\:\\:testTranslateDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/TranslationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\TranslationServiceDecoratorTest\\:\\:testTranslateStringDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/TranslationServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/TranslationServiceDecoratorTest\\.php\\:21 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\TranslationService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/TranslationServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\TrashServiceDecoratorTest\\:\\:testDeleteTrashItemDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/TrashServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\TrashServiceDecoratorTest\\:\\:testEmptyTrashDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/TrashServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\TrashServiceDecoratorTest\\:\\:testFindTrashItemsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/TrashServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\TrashServiceDecoratorTest\\:\\:testLoadTrashItemDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/TrashServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\TrashServiceDecoratorTest\\:\\:testRecoverDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/TrashServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\TrashServiceDecoratorTest\\:\\:testTrashDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/TrashServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/TrashServiceDecoratorTest\\.php\\:23 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\TrashService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/TrashServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLAliasServiceDecoratorTest\\:\\:testCreateGlobalUrlAliasDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLAliasServiceDecoratorTest\\:\\:testCreateUrlAliasDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLAliasServiceDecoratorTest\\:\\:testDeleteCorruptedUrlAliasesDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLAliasServiceDecoratorTest\\:\\:testListGlobalAliasesDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLAliasServiceDecoratorTest\\:\\:testListLocationAliasesDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLAliasServiceDecoratorTest\\:\\:testLoadDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLAliasServiceDecoratorTest\\:\\:testLookupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLAliasServiceDecoratorTest\\:\\:testRefreshSystemUrlAliasesForLocationDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLAliasServiceDecoratorTest\\:\\:testRemoveAliasesDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLAliasServiceDecoratorTest\\:\\:testReverseLookupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$aliasList of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\URLAliasService\\:\\:removeAliases\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\>, array\\<int, string\\> given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest\\.php\\:24 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\URLAliasService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLServiceDecoratorTest\\:\\:testCreateUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLServiceDecoratorTest\\:\\:testFindUrlsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLServiceDecoratorTest\\:\\:testFindUsagesDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLServiceDecoratorTest\\:\\:testLoadByIdDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLServiceDecoratorTest\\:\\:testLoadByUrlDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLServiceDecoratorTest\\:\\:testUpdateUrlDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/URLServiceDecoratorTest\\.php\\:23 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\URLService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLWildcardServiceDecoratorTest\\:\\:testCreateDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLWildcardServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLWildcardServiceDecoratorTest\\:\\:testLoadAllDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLWildcardServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLWildcardServiceDecoratorTest\\:\\:testLoadDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLWildcardServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLWildcardServiceDecoratorTest\\:\\:testRemoveDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLWildcardServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\URLWildcardServiceDecoratorTest\\:\\:testTranslateDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLWildcardServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/URLWildcardServiceDecoratorTest\\.php\\:21 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\URLWildcardService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/URLWildcardServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserPreferenceServiceDecoratorTest\\:\\:testGetUserPreferenceCountDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserPreferenceServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserPreferenceServiceDecoratorTest\\:\\:testGetUserPreferenceDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserPreferenceServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserPreferenceServiceDecoratorTest\\:\\:testLoadUserPreferencesDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserPreferenceServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserPreferenceServiceDecoratorTest\\:\\:testSetUserPreferenceDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserPreferenceServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/UserPreferenceServiceDecoratorTest\\.php\\:20 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserPreferenceService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserPreferenceServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$userPreferenceSetStructs of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserPreferenceService\\:\\:setUserPreference\\(\\) expects array\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\UserPreference\\\\UserPreferenceSetStruct\\>, array\\<int, string\\> given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserPreferenceServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testAssignUserToUserGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testCheckUserCredentialsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testCreateUserDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testCreateUserGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testDeleteUserDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testDeleteUserGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testExpireUserTokenDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testIsUserDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testIsUserGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testLoadSubUserGroupsDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testLoadUserByLoginDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testLoadUserByTokenDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testLoadUserDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testLoadUserGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testLoadUserGroupsOfUserDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testLoadUsersByEmailDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testLoadUsersOfUserGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testMoveUserGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testNewUserCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testNewUserGroupCreateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testNewUserGroupUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testNewUserUpdateStructDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testUnAssignUserFromUserGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testUpdateUserDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testUpdateUserGroupDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testUpdateUserTokenDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Decorator\\\\UserServiceDecoratorTest\\:\\:testValidatePasswordDecorator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Parameter \\#1 \\$innerService of class class@anonymous/tests/lib/Repository/Decorator/UserServiceDecoratorTest\\.php\\:37 constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\UserService, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Decorator/UserServiceDecoratorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Filtering\\\\TestContentProvider\\:\\:createContentDraft\\(\\) has parameter \\$multilingualFields with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Filtering/TestContentProvider.php + + - + message: "#^Parameter \\#2 \\$mainLanguageCode of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\ContentService\\:\\:newContentCreateStruct\\(\\) expects string, int\\|string given\\.$#" + count: 1 + path: tests/lib/Repository/Filtering/TestContentProvider.php + + - + message: "#^Parameter \\#2 \\$parentLocationId of method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Filtering\\\\TestContentProvider\\:\\:createArticle\\(\\) expects int, int\\|null given\\.$#" + count: 3 + path: tests/lib/Repository/Filtering/TestContentProvider.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Helper\\\\FieldTypeRegistryTest\\:\\:getFieldTypeMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Helper/FieldTypeRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Helper\\\\FieldTypeRegistryTest\\:\\:testConstructor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Helper/FieldTypeRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Helper\\\\FieldTypeRegistryTest\\:\\:testGetFieldType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Helper/FieldTypeRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Helper\\\\FieldTypeRegistryTest\\:\\:testGetFieldTypeThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Helper/FieldTypeRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Helper\\\\FieldTypeRegistryTest\\:\\:testGetFieldTypeThrowsRuntimeExceptionIncorrectType\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Helper/FieldTypeRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Helper\\\\FieldTypeRegistryTest\\:\\:testGetFieldTypes\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Helper/FieldTypeRegistryTest.php + + - + message: "#^Parameter \\#1 \\$fieldTypes of class Ibexa\\\\Core\\\\FieldType\\\\FieldTypeRegistry constructor expects array\\<Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\FieldType\\>, array\\<string, string\\> given\\.$#" + count: 1 + path: tests/lib/Repository/Helper/FieldTypeRegistryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\AbstractSearchAdapterTest\\:\\:createAdapterUnderTest\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\ContentInfoSearchAdapterTest\\:\\:createAdapterUnderTest\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\ContentSearchAdapterTest\\:\\:createAdapterUnderTest\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\LocationSearchAdapterTest\\:\\:createAdapterUnderTest\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php + + - + message: "#^Parameter \\#2 \\$query of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorAdapter\\\\LocationSearchAdapter constructor expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LocationQuery, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query given\\.$#" + count: 1 + path: tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorTestAdapter\\:\\:__construct\\(\\) has parameter \\$data with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Iterator/BatchIteratorTestAdapter.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Iterator\\\\BatchIteratorTestAdapter\\:\\:\\$data type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Iterator/BatchIteratorTestAdapter.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:method\\(\\)\\.$#" + count: 7 + path: tests/lib/Repository/LocationResolver/PermissionAwareLocationResolverTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\LocationService\\:\\:method\\(\\)\\.$#" + count: 5 + path: tests/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationServiceTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentLocationMapper\\\\ContentLocationMapper\\:\\:expects\\(\\)\\.$#" + count: 5 + path: tests/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationServiceTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>\\.$#" + count: 1 + path: tests/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationServiceTest.php + + - + message: "#^Cannot access offset 1 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>\\.$#" + count: 1 + path: tests/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationServiceTest.php + + - + message: "#^Cannot access property \\$locations on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\>\\.$#" + count: 2 + path: tests/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\PHPUnitConstraint\\\\AllValidationErrorsOccur\\:\\:extractTranslatable\\(\\) has parameter \\$fieldErrors with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/PHPUnitConstraint/AllValidationErrorsOccur.php + + - + message: "#^PHPDoc tag @param has invalid value \\(array\\<int, \\<string, array\\<\\\\Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\ValidationError\\>\\>\\> \\$fieldErrors\\)\\: Unexpected token \"\\<\", expected type at offset 29$#" + count: 1 + path: tests/lib/Repository/PHPUnitConstraint/AllValidationErrorsOccur.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\PHPUnitConstraint\\\\ContentItemEquals\\:\\:compareArrays\\(\\) has parameter \\$actual with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/PHPUnitConstraint/ContentItemEquals.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\PHPUnitConstraint\\\\ContentItemEquals\\:\\:compareArrays\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/PHPUnitConstraint/ContentItemEquals.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\PHPUnitConstraint\\\\ContentItemEquals\\:\\:evaluate\\(\\) has parameter \\$content with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/PHPUnitConstraint/ContentItemEquals.php + + - + message: "#^Class Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Parallel\\\\ParallelProcessList implements generic interface IteratorAggregate but does not specify its types\\: TKey, TValue$#" + count: 1 + path: tests/lib/Repository/Parallel/ParallelProcessList.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionServiceTest\\:\\:getPermissionCriterionResolverMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/CachedPermissionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionServiceTest\\:\\:getPermissionCriterionResolverMock\\(\\) has parameter \\$methods with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/CachedPermissionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionServiceTest\\:\\:getPermissionResolverMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/CachedPermissionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionServiceTest\\:\\:getPermissionResolverMock\\(\\) has parameter \\$methods with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/CachedPermissionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionServiceTest\\:\\:providerForTestPermissionResolverPassTrough\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/CachedPermissionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionServiceTest\\:\\:testGetPermissionsCriterionCaching\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/CachedPermissionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionServiceTest\\:\\:testGetPermissionsCriterionPassTrough\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/CachedPermissionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionServiceTest\\:\\:testPermissionResolverPassTrough\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/CachedPermissionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionServiceTest\\:\\:testPermissionResolverPassTrough\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Permission/CachedPermissionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionServiceTest\\:\\:testPermissionResolverPassTrough\\(\\) has parameter \\$expectedReturn with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/CachedPermissionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionServiceTest\\:\\:testPermissionResolverPassTrough\\(\\) has parameter \\$method with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/CachedPermissionServiceTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionServiceTest\\:\\:\\$permissionCriterionResolverMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/CachedPermissionServiceTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\CachedPermissionServiceTest\\:\\:\\$permissionResolverMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/CachedPermissionServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:getLimitationServiceMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:getLimitationServiceMock\\(\\) has parameter \\$methods with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:getPermissionResolverMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:getPermissionResolverMock\\(\\) has parameter \\$methods with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:mockServices\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:mockServices\\(\\) has parameter \\$criterionMock with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:mockServices\\(\\) has parameter \\$limitationCount with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:mockServices\\(\\) has parameter \\$permissionSets with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:providerForTestGetPermissionsCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:providerForTestGetPermissionsCriterionBooleanPermissionSets\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:testGetPermissionsCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:testGetPermissionsCriterion\\(\\) has parameter \\$criterionMock with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:testGetPermissionsCriterion\\(\\) has parameter \\$expectedCriterion with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:testGetPermissionsCriterion\\(\\) has parameter \\$limitationCount with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:testGetPermissionsCriterion\\(\\) has parameter \\$permissionSets with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:testGetPermissionsCriterionBooleanPermissionSets\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:testGetPermissionsCriterionBooleanPermissionSets\\(\\) has parameter \\$permissionSets with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:\\$limitationServiceMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Permission\\\\PermissionCriterionResolverTest\\:\\:\\$permissionResolverMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Permission/PermissionCriterionResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\Base\\:\\:getEventDispatcher\\(\\) should return PHPUnit\\\\Framework\\\\MockObject\\\\MockObject&Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface but returns Symfony\\\\Contracts\\\\EventDispatcher\\\\EventDispatcherInterface\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\Base\\:\\:getRelationProcessorMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\Base\\:\\:getRepository\\(\\) has parameter \\$serviceSettings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Parameter \\#1 \\$className of method PHPUnit\\\\Framework\\\\TestCase\\:\\:getMockBuilder\\(\\) expects class\\-string\\<mixed\\>, string given\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Parameter \\#2 \\$searchHandler of class Ibexa\\\\Core\\\\Repository\\\\Repository constructor expects Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\Base\\:\\:\\$contentDomainMapperMock \\(Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\Base\\:\\:\\$contentTypeDomainMapperMock \\(Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentTypeDomainMapper&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\Base\\:\\:\\$fieldTypeRegistryMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\Base\\:\\:\\$fieldTypeServiceMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\Base\\:\\:\\$permissionServiceMock \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\PermissionService&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\Base\\:\\:\\$persistenceMock \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\Base\\:\\:\\$repositoryMock \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Repository&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\Base\\:\\:\\$thumbnailStrategyMock \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Strategy\\\\ContentThumbnail\\\\ThumbnailStrategy&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Unable to resolve the template type T in call to method PHPUnit\\\\Framework\\\\TestCase\\:\\:getMockBuilder\\(\\)$#" + count: 1 + path: tests/lib/Repository/Service/Mock/Base.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\BookmarkTest\\:\\:createBookmarkService\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/BookmarkTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\BookmarkTest\\:\\:testCreateBookmark\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/BookmarkTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\BookmarkTest\\:\\:testCreateBookmarkThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/BookmarkTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\BookmarkTest\\:\\:testCreateBookmarkWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/BookmarkTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\BookmarkTest\\:\\:testDeleteBookmarkExisting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/BookmarkTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\BookmarkTest\\:\\:testDeleteBookmarkNonExisting\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/BookmarkTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\BookmarkTest\\:\\:testDeleteBookmarkWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/BookmarkTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\BookmarkTest\\:\\:testLoadBookmarks\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/BookmarkTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\BookmarkTest\\:\\:testLoadBookmarksEmptyList\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/BookmarkTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\BookmarkTest\\:\\:testLocationShouldBeBookmarked\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/BookmarkTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\BookmarkTest\\:\\:testLocationShouldNotBeBookmarked\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/BookmarkTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\BookmarkTest\\:\\:\\$bookmarkHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Bookmark\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/BookmarkTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:getContentHandlerMock\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:getLanguageHandlerMock\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:getTypeHandlerMock\\(\\) should return Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:providerForBuildLocationDomainObjectsOnSearchResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:providerForBuildVersionInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:testBuildLocationDomainObjectsOnSearchResult\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:testBuildLocationDomainObjectsOnSearchResult\\(\\) has parameter \\$contentIds with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:testBuildLocationDomainObjectsOnSearchResult\\(\\) has parameter \\$contentInfoList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:testBuildLocationDomainObjectsOnSearchResult\\(\\) has parameter \\$languageFilter with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:testBuildLocationDomainObjectsOnSearchResult\\(\\) has parameter \\$locationHits with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:testBuildLocationWithContentForRootLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:testBuildLocationWithContentIsAlignedWithBuildLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:testBuildLocationWithContentThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentDomainMapperTest\\:\\:testBuildVersionInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Parameter \\#2 \\$locationHandler of class Ibexa\\\\Core\\\\Repository\\\\Mapper\\\\ContentDomainMapper constructor expects Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Handler, PHPUnit\\\\Framework\\\\MockObject\\\\MockObject given\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\FieldType\\\\Value\\:\\:\\$value\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Handler\\:\\:expects\\(\\)\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceInterface\\:\\:expects\\(\\)\\.$#" + count: 3 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Cannot access offset mixed on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Cannot access offset string on Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Field\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForCreateContentContentValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForCreateContentContentValidationException\\(\\) has parameter \\$fieldDefinitions with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForCreateContentContentValidationException\\(\\) has parameter \\$mainLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForCreateContentContentValidationException\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForTestCreateContentThrowsContentFieldValidationException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForTestUpdateContentRequiredField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForTestUpdateContentRequiredField\\(\\) has parameter \\$existingFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForTestUpdateContentRequiredField\\(\\) has parameter \\$fieldDefinitions with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForTestUpdateContentRequiredField\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForTestUpdateContentRequiredField\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForTestUpdateContentThrowsContentFieldValidationException\\(\\) has parameter \\$allFieldErrors with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForTestUpdateContentThrowsContentFieldValidationException\\(\\) has parameter \\$existingFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForTestUpdateContentThrowsContentFieldValidationException\\(\\) has parameter \\$fieldDefinitions with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForTestUpdateContentThrowsContentFieldValidationException\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForTestUpdateContentThrowsContentFieldValidationException\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForTestUpdateContentThrowsContentFieldValidationException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForUpdateContentContentValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForUpdateContentContentValidationException\\(\\) has parameter \\$fieldDefinitions with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForUpdateContentContentValidationException\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:assertForUpdateContentContentValidationException\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:determineValuesForCreate\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:determineValuesForCreate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:determineValuesForUpdate\\(\\) has parameter \\$languageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:determineValuesForUpdate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:fixturesForTestCreateContentNonRedundantFieldSetComplex\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:fixturesForTestUpdateContentNonRedundantFieldSetComplex\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:getPartlyMockedContentService\\(\\) invoked with 2 parameters, 0\\-1 required\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:getPartlyMockedContentService\\(\\) should return Ibexa\\\\Core\\\\Repository\\\\ContentService&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Repository\\\\ContentService\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:internalLoadContentProviderById\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:internalLoadContentProviderByRemoteId\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:mapStructFieldsForCreate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:mapStructFieldsForUpdate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:mockBuildContentDomainObject\\(\\) has parameter \\$translations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:mockGetDefaultObjectStates\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:mockPublishUrlAliasesForContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:mockSetDefaultObjectStates\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:prepareContentForTestCreateAndUpdateContent\\(\\) has parameter \\$existingFields with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:prepareContentForTestCreateAndUpdateContent\\(\\) has parameter \\$existingLanguageCodes with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:prepareContentForTestCreateAndUpdateContent\\(\\) has parameter \\$fieldDefinitions with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:provideCommonCreateContentObjects\\(\\) has parameter \\$fieldDefinitions with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:provideCommonCreateContentObjects\\(\\) has parameter \\$mainLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:provideCommonCreateContentObjects\\(\\) has parameter \\$structFields with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:provideCommonCreateContentObjects\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestCreateContentNonRedundantFieldSet1\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestCreateContentNonRedundantFieldSet2\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestCreateContentNonRedundantFieldSetComplex\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestCreateContentThrowsContentFieldValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestCreateContentThrowsContentValidationExceptionFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestCreateContentThrowsContentValidationExceptionRequiredField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestCreateContentThrowsContentValidationExceptionTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestCreateContentWithInvalidLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestUpdateContentNonRedundantFieldSet1\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestUpdateContentNonRedundantFieldSet2\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestUpdateContentNonRedundantFieldSet3\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestUpdateContentNonRedundantFieldSet4\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestUpdateContentNonRedundantFieldSetComplex\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestUpdateContentRequiredField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestUpdateContentThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestUpdateContentThrowsContentFieldValidationException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestUpdateContentThrowsContentValidationExceptionFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestUpdateContentThrowsContentValidationExceptionTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:providerForTestUpdateContentWithInvalidLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:stubValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:stubValues\\(\\) has parameter \\$fieldValues with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCopyContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCopyContentThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCopyContentWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCopyContentWithVersionInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentNonRedundantFieldSet1\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentNonRedundantFieldSet1\\(\\) has parameter \\$mainLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentNonRedundantFieldSet1\\(\\) has parameter \\$spiFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentNonRedundantFieldSet1\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentNonRedundantFieldSet2\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentNonRedundantFieldSet2\\(\\) has parameter \\$mainLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentNonRedundantFieldSet2\\(\\) has parameter \\$spiFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentNonRedundantFieldSet2\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentNonRedundantFieldSetComplex\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentNonRedundantFieldSetComplex\\(\\) has parameter \\$mainLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentNonRedundantFieldSetComplex\\(\\) has parameter \\$spiFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentNonRedundantFieldSetComplex\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentObjectStates\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentRequiredField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentRequiredField\\(\\) has parameter \\$identifier with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentRequiredField\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentRequiredField\\(\\) has parameter \\$mainLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentRequiredField\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentThrowsContentFieldValidationException\\(\\) has parameter \\$mainLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentThrowsContentFieldValidationException\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentThrowsContentValidationExceptionFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentThrowsContentValidationExceptionFieldDefinition\\(\\) has parameter \\$mainLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentThrowsContentValidationExceptionFieldDefinition\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentThrowsContentValidationExceptionTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentThrowsContentValidationExceptionTranslation\\(\\) has parameter \\$mainLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentThrowsContentValidationExceptionTranslation\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentThrowsInvalidArgumentExceptionContentTypeNotSet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentThrowsInvalidArgumentExceptionDuplicateRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentThrowsInvalidArgumentExceptionMainLanguageCodeNotSet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentWithInvalidLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentWithInvalidLanguage\\(\\) has parameter \\$mainLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentWithInvalidLanguage\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentWithLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentWithLocationsDuplicateUnderParent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testCreateContentWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testDeleteContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testDeleteContentThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testDeleteContentWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testDeleteVersionThrowsBadStateExceptionLastVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testInternalLoadContentById\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testInternalLoadContentByRemoteId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testInternalLoadContentByRemoteId\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testLoadContent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testLoadContentByContentInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testLoadContentByVersionInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testLoadContentNonPublished\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testLoadContentNotPublishedStatusUnauthorized\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testLoadContentUnauthorized\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testLoadVersionInfo\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testLoadVersionInfoById\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testLoadVersionInfoByIdAndVersionNumber\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testLoadVersionInfoByIdNonPublishedVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testLoadVersionInfoByIdPublishedVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testLoadVersionInfoByIdThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testLoadVersionInfoByIdThrowsUnauthorizedExceptionNonPublishedVersion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet1\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet1\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet1\\(\\) has parameter \\$spiFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet1\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet2\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet2\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet2\\(\\) has parameter \\$spiFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet2\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet3\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet3\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet3\\(\\) has parameter \\$spiFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet3\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet4\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet4\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet4\\(\\) has parameter \\$spiFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSet4\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSetComplex\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSetComplex\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSetComplex\\(\\) has parameter \\$spiFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentNonRedundantFieldSetComplex\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentRequiredField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentRequiredField\\(\\) has parameter \\$identifier with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentRequiredField\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentRequiredField\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentRequiredField\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentThrowsBadStateException\\(\\) has parameter \\$status with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentThrowsContentFieldValidationException\\(\\) has parameter \\$allFieldErrors with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentThrowsContentFieldValidationException\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentThrowsContentFieldValidationException\\(\\) has parameter \\$spiField with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentThrowsContentFieldValidationException\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentThrowsContentValidationExceptionFieldDefinition\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentThrowsContentValidationExceptionFieldDefinition\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentThrowsContentValidationExceptionFieldDefinition\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentThrowsContentValidationExceptionTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentThrowsContentValidationExceptionTranslation\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentThrowsContentValidationExceptionTranslation\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentTransactionRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentWithInvalidLanguage\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentWithInvalidLanguage\\(\\) has parameter \\$initialLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:testUpdateContentWithInvalidLanguage\\(\\) has parameter \\$structFields with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^PHPDoc tag @return with type PHPUnit\\\\Framework\\\\MockObject\\\\MockObject is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceInterface\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^PHPDoc tag @var for property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:\\$nameSchemaServiceMock with type PHPUnit\\\\Framework\\\\MockObject\\\\MockObject is not subtype of native type Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\NameSchema\\\\NameSchemaServiceInterface\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Parameter \\#1 \\$languageHandlerMock of method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:loadByLanguageCodeMock\\(\\) expects PHPUnit\\\\Framework\\\\MockObject\\\\MockObject, Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Language\\\\Handler given\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Parameter \\#1 \\$singular of class Ibexa\\\\Core\\\\FieldType\\\\ValidationError constructor expects string, int given\\.$#" + count: 2 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\MetadataUpdateStruct\\:\\:\\$publicationDate \\(int\\) does not accept int\\|null\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:\\$contentTypeServiceMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:\\$locationServiceMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:\\$partlyMockedContentService \\(Ibexa\\\\Core\\\\Repository\\\\ContentService\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\ContentTest\\:\\:\\$relationProcessorMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/ContentTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:expects\\(\\)\\.$#" + count: 11 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\PermissionResolver&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\:\\:sudo\\(\\)\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:createRole\\(\\) has parameter \\$policiesData with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:getPermissionResolverMock\\(\\) has parameter \\$methods with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:getPermissionSetsMock\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:getUserReferenceMock\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:providerForTestCanUserComplex\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:providerForTestCanUserSimple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:providerForTestHasAccessReturnsFalse\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:providerForTestHasAccessReturnsInvalidArgumentValueException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:providerForTestHasAccessReturnsLimitationNotFoundException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:providerForTestHasAccessReturnsPermissionSets\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:providerForTestHasAccessReturnsPermissionSetsWithRoleLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:providerForTestHasAccessReturnsTrue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testCanUserComplex\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testCanUserComplex\\(\\) has parameter \\$policyLimitationEvaluations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testCanUserComplex\\(\\) has parameter \\$roleLimitationEvaluations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testCanUserComplex\\(\\) has parameter \\$userCan with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testCanUserSimple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testCanUserSimple\\(\\) has parameter \\$permissionSets with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testCanUserSimple\\(\\) has parameter \\$result with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testCanUserWithoutLimitations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testGetCurrentUserReferenceReturnsAnonymousUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsFalse\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsFalse\\(\\) has parameter \\$roleAssignments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsFalse\\(\\) has parameter \\$roles with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsFalseButSudoSoTrue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsInvalidArgumentValueException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsInvalidArgumentValueException\\(\\) has parameter \\$roleAssignments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsInvalidArgumentValueException\\(\\) has parameter \\$roles with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsLimitationNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsLimitationNotFoundException\\(\\) has parameter \\$roleAssignments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsLimitationNotFoundException\\(\\) has parameter \\$roles with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsPermissionSets\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsPermissionSets\\(\\) has parameter \\$roleAssignments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsPermissionSets\\(\\) has parameter \\$roles with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsPermissionSetsWithRoleLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsPermissionSetsWithRoleLimitation\\(\\) has parameter \\$roleAssignments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsPermissionSetsWithRoleLimitation\\(\\) has parameter \\$roles with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsTrue\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsTrue\\(\\) has parameter \\$roleAssignments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testHasAccessReturnsTrue\\(\\) has parameter \\$roles with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:testSetAndGetCurrentUserReference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$policy \\\\Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Policy\\)\\: Unexpected token \"\\$policy\", expected type at offset 9$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$policy \\\\Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\)\\: Unexpected token \"\\$policy\", expected type at offset 9$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$role \\\\Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Role\\)\\: Unexpected token \"\\$role\", expected type at offset 9$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$roleAssignments \\\\Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\RoleAssignment\\[\\]\\)\\: Unexpected token \"\\$roleAssignments\", expected type at offset 9$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$userHandlerMock \\\\PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\)\\: Unexpected token \"\\$userHandlerMock\", expected type at offset 9$#" + count: 6 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^PHPDoc tag @var has invalid value \\(\\$valueObject \\\\Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\)\\: Unexpected token \"\\$valueObject\", expected type at offset 9$#" + count: 3 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:\\$permissionResolverMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:\\$repositoryMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionTest\\:\\:\\$userReferenceMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Variable \\$limitationsPass might not be defined\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:getPermissionResolverMock\\(\\) has parameter \\$methods with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:mockServices\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:mockServices\\(\\) has parameter \\$criterionMock with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:mockServices\\(\\) has parameter \\$limitationCount with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:mockServices\\(\\) has parameter \\$permissionSets with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:providerForTestAddPermissionsCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:providerForTestAddPermissionsCriterionWithBooleanPermission\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:providerForTestGetPermissionsCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:providerForTestGetPermissionsCriterionBooleanPermissionSets\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:testAddPermissionsCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:testAddPermissionsCriterion\\(\\) has parameter \\$expectedCriterion with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:testAddPermissionsCriterion\\(\\) has parameter \\$givenCriterion with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:testAddPermissionsCriterion\\(\\) has parameter \\$permissionsCriterionMock with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:testAddPermissionsCriterionWithBooleanPermission\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:testAddPermissionsCriterionWithBooleanPermission\\(\\) has parameter \\$permissionsCriterion with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:testGetPermissionsCriterion\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:testGetPermissionsCriterion\\(\\) has parameter \\$criterionMock with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:testGetPermissionsCriterion\\(\\) has parameter \\$expectedCriterion with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:testGetPermissionsCriterion\\(\\) has parameter \\$limitationCount with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:testGetPermissionsCriterion\\(\\) has parameter \\$permissionSets with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:testGetPermissionsCriterionBooleanPermissionSets\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:testGetPermissionsCriterionBooleanPermissionSets\\(\\) has parameter \\$permissionSets with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\PermissionsCriterionHandlerTest\\:\\:\\$permissionResolverMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Location\\\\Handler\\:\\:expects\\(\\)\\.$#" + count: 2 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:assertLocationHandlerExpectation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:assertLocationHandlerExpectation\\(\\) has parameter \\$callCounter with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:assertLocationHandlerExpectation\\(\\) has parameter \\$fieldRelations with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:assertLocationHandlerExpectation\\(\\) has parameter \\$locationHandlerMock with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:assertLocationHandlerExpectation\\(\\) has parameter \\$type with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:getStubbedRelation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:getStubbedRelation\\(\\) has parameter \\$contentId with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:getStubbedRelation\\(\\) has parameter \\$fieldDefinitionId with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:getStubbedRelation\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:getStubbedRelation\\(\\) has parameter \\$type with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:providerForTestAppendRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:testAppendFieldRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:testAppendFieldRelations\\(\\) has parameter \\$expected with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:testAppendFieldRelations\\(\\) has parameter \\$fieldRelations with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:testAppendFieldRelationsLocationMappingWorks\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:testAppendFieldRelationsLogsMissingLocations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:testProcessFieldRelationsAddsRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:testProcessFieldRelationsNoChanges\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:testProcessFieldRelationsRemovesRelations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RelationProcessorTest\\:\\:testProcessFieldRelationsWhenRelationFieldNoLongerExists\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Parameter \\#5 \\$fieldDefinitionId of method Ibexa\\\\Core\\\\Repository\\\\Helper\\\\RelationProcessor\\:\\:appendFieldRelations\\(\\) expects string, int given\\.$#" + count: 3 + path: tests/lib/Repository/Service/Mock/RelationProcessorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RepositoryTest\\:\\:testBeginTransaction\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RepositoryTest\\:\\:testCommit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RepositoryTest\\:\\:testCommitThrowsRuntimeException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RepositoryTest\\:\\:testRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RepositoryTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RepositoryTest\\:\\:testRollbackThrowsRuntimeException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RepositoryTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleCreateStruct\\:\\:expects\\(\\)\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:getPartlyMockedRoleService\\(\\) has parameter \\$settings with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:getPartlyMockedRoleService\\(\\) should return Ibexa\\\\Core\\\\Repository\\\\RoleService&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject but returns Ibexa\\\\Core\\\\Repository\\\\RoleService\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testAddPolicyThrowsLimitationValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testAssignRoleGroupToUserThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testAssignRoleToUser\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testAssignRoleToUserGroup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testAssignRoleToUserGroupThrowsLimitationValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testAssignRoleToUserGroupThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testAssignRoleToUserGroupWithNullLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testAssignRoleToUserGroupWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testAssignRoleToUserThrowsBadStateException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testAssignRoleToUserThrowsLimitationValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testAssignRoleToUserThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testAssignRoleToUserWithNullLimitation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testAssignRoleToUserWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testCreateRoleThrowsLimitationValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testRemovePolicyByRoleDraft\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testRemovePolicyByRoleDraftThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testRemovePolicyByRoleDraftWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:testUpdatePolicyThrowsLimitationValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\RoleTest\\:\\:\\$partlyMockedRoleService \\(Ibexa\\\\Core\\\\Repository\\\\RoleService\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/RoleTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Search\\\\Handler\\:\\:expects\\(\\)\\.$#" + count: 8 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:providerForFindContentValidatesLocationCriteriaAndSortClauses\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:providerForFindSingleValidatesLocationCriteria\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testConstructor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindContentNoPermissionsFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindContentThrowsHandlerException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindContentValidatesLocationCriteriaAndSortClauses\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindContentValidatesLocationCriteriaAndSortClauses\\(\\) has parameter \\$exceptionMessage with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindContentValidatesLocationCriteriaAndSortClauses\\(\\) has parameter \\$query with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindContentWhenDomainMapperThrowsException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindContentWithDefaultQueryValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindContentWithNoPermission\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindContentWithPermission\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindLocationsBackgroundIndexerWhenDomainMapperThrowsException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindLocationsThrowsHandlerException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindLocationsWithDefaultQueryValues\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindLocationsWithNoPermissionsFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindLocationsWithPermission\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindSingle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindSingleThrowsHandlerException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindSingleThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindSingleValidatesLocationCriteria\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindSingleValidatesLocationCriteria\\(\\) has parameter \\$criterion with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:testFindSingleValidatesLocationCriteria\\(\\) has parameter \\$exceptionMessage with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:\\$contentDomainMapperMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:\\$permissionsCriterionResolverMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\SearchTest\\:\\:\\$repositoryMock has no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/SearchTest.php + + - + message: "#^Cannot access offset 0 on iterable\\<Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\URLAlias\\>\\.$#" + count: 3 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:getPartlyMockedURLAliasServiceService\\(\\) has parameter \\$prioritizedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:getSpiUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestListAutogeneratedLocationAliasesAlwaysAvailablePath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestListAutogeneratedLocationAliasesEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestListAutogeneratedLocationAliasesMultipleLanguagesEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestListAutogeneratedLocationAliasesMultipleLanguagesPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestListAutogeneratedLocationAliasesPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestListAutogeneratedLocationAliasesWithLanguageCodeEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestListAutogeneratedLocationAliasesWithLanguageCodePath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestLookup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestLookupThrowsNotFoundExceptionPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestLookupWithSharedTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestReverseLookup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:providerForTestReverseLookupAlwaysAvailablePath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:setConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:setConfiguration\\(\\) has parameter \\$configuration with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testConstructor\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testCreateGlobalUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testCreateGlobalUrlAliasForLocation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testCreateGlobalUrlAliasThrowsInvalidArgumentExceptionPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testCreateGlobalUrlAliasThrowsInvalidArgumentExceptionResource\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testCreateGlobalUrlAliasThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testCreateGlobalUrlAliasWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testCreateUrlAlias\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testCreateUrlAliasThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testCreateUrlAliasThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testCreateUrlAliasWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesAlwaysAvailablePath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesAlwaysAvailablePath\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesAlwaysAvailablePath\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesAlwaysAvailablePath\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesAlwaysAvailablePathCustomConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesAlwaysAvailablePathCustomConfiguration\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesAlwaysAvailablePathCustomConfiguration\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesAlwaysAvailablePathCustomConfiguration\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesEmpty\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesEmpty\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesEmptyCustomConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesEmptyCustomConfiguration\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesEmptyCustomConfiguration\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesEmpty\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesEmpty\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesEmptyCustomConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesEmptyCustomConfiguration\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesEmptyCustomConfiguration\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesPath\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesPath\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesPath\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesPathCustomConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesPathCustomConfiguration\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesPathCustomConfiguration\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesMultipleLanguagesPathCustomConfiguration\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesPath\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesPath\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesPath\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesPathCustomConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesPathCustomConfiguration\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesPathCustomConfiguration\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesPathCustomConfiguration\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmpty\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmpty\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmpty\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmptyCustomConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmptyCustomConfiguration\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmptyCustomConfiguration\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmptyCustomConfiguration\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePathCustomConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePathCustomConfiguration\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePathCustomConfiguration\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePathCustomConfiguration\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePathCustomConfiguration\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeEmpty\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeEmpty\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeEmpty\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeEmptyCustomConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeEmptyCustomConfiguration\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeEmptyCustomConfiguration\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeEmptyCustomConfiguration\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmpty\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmpty\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmpty\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmptyCustomConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmptyCustomConfiguration\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmptyCustomConfiguration\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmptyCustomConfiguration\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPathCustomConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPathCustomConfiguration\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPathCustomConfiguration\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPathCustomConfiguration\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPathCustomConfiguration\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodePath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodePath\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodePath\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodePath\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodePath\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodePathCustomConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodePathCustomConfiguration\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodePathCustomConfiguration\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodePathCustomConfiguration\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListAutogeneratedLocationAliasesWithLanguageCodePathCustomConfiguration\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListGlobalAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListGlobalAliasesEmpty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListGlobalAliasesWithParameters\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListLocationAliasesWithShowAllTranslations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testListLocationAliasesWithShowAllTranslationsCustomConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLoadThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLoadThrowsNotFoundExceptionPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookup\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookup\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookup\\(\\) has parameter \\$prioritizedLanguageList with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookup\\(\\) has parameter \\$showAllTranslations with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookupThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookupThrowsNotFoundExceptionPathNotMatchedOrNotLoadable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookupThrowsNotFoundExceptionPathNotMatchedOrNotLoadable\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookupThrowsNotFoundExceptionPathNotMatchedOrNotLoadable\\(\\) has parameter \\$prioritizedLanguageList with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookupThrowsNotFoundExceptionPathNotMatchedOrNotLoadable\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookupWithSharedTranslation\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookupWithSharedTranslation\\(\\) has parameter \\$alwaysAvailable with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookupWithSharedTranslation\\(\\) has parameter \\$languageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookupWithSharedTranslation\\(\\) has parameter \\$prioritizedLanguageList with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testLookupWithSharedTranslation\\(\\) has parameter \\$showAllTranslations with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testRemoveAliases\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testRemoveAliasesThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testRemoveAliasesThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testRemoveAliasesWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testReverseLookupAlwaysAvailablePath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testReverseLookupAlwaysAvailablePath\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testReverseLookupAlwaysAvailablePath\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testReverseLookupAlwaysAvailablePath\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testReverseLookupCustomConfiguration\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testReverseLookupPath\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testReverseLookupPath\\(\\) has parameter \\$paths with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testReverseLookupPath\\(\\) has parameter \\$prioritizedLanguageCodes with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testReverseLookupPath\\(\\) has parameter \\$reverseLookupLanguageCode with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testReverseLookupPath\\(\\) has parameter \\$spiUrlAliases with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testReverseLookupThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:testReverseLookupWithShowAllTranslations\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlAliasTest\\:\\:\\$urlAliasHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlAlias\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlAliasTest.php + + - + message: "#^Cannot call method getTimestamp\\(\\) on DateTimeInterface\\|null\\.$#" + count: 2 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:configurePermissions\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:configurePermissions\\(\\) has parameter \\$permissions with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:configureUrlUpdatePermission\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:configureUrlUpdatePermission\\(\\) has parameter \\$hasAccess with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:configureUrlUpdatePermission\\(\\) has parameter \\$object with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:configureUrlViewPermission\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:configureUrlViewPermission\\(\\) has parameter \\$hasAccess with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:configureUrlViewPermission\\(\\) has parameter \\$object with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:configureUrlViewPermissionForHasAccess\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:configureUrlViewPermissionForHasAccess\\(\\) has parameter \\$hasAccess with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:createUrlService\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:dateProviderForFindUsages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:getApiUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:getApiUrl\\(\\) has parameter \\$id with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:getApiUrl\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testCreateUpdateStruct\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testFindUrls\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testFindUrlsNonNumericLimit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testFindUrlsNonNumericOffset\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testFindUrlsUnauthorized\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testFindUsages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testFindUsages\\(\\) has parameter \\$limit with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testFindUsages\\(\\) has parameter \\$offset with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testFindUsages\\(\\) has parameter \\$usages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testLoadById\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testLoadByIdUnauthorized\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testLoadByUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testLoadByUrlUnauthorized\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testUpdateUrl\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testUpdateUrlNonUnique\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testUpdateUrlStatus\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:testUpdateUrlUnauthorized\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URLQuery\\:\\:\\$limit \\(int\\) does not accept string\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\URL\\\\URLQuery\\:\\:\\$offset \\(int\\) does not accept string\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlTest\\:\\:\\$urlHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\URLService&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:providerForTestCreate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:providerForTestCreateThrowsContentValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:providerForTestTranslate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:providerForTestTranslateThrowsNotFoundException\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testCreate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testCreate\\(\\) has parameter \\$destinationUrl with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testCreate\\(\\) has parameter \\$forward with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testCreate\\(\\) has parameter \\$sourceUrl with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testCreateThrowsContentValidationException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testCreateThrowsContentValidationException\\(\\) has parameter \\$destinationUrl with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testCreateThrowsContentValidationException\\(\\) has parameter \\$forward with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testCreateThrowsContentValidationException\\(\\) has parameter \\$sourceUrl with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testCreateThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testCreateThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testCreateWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testLoad\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testLoadAll\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testLoadAllWithLimitAndOffset\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testLoadThrowsException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testRemove\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testRemoveThrowsUnauthorizedException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testRemoveWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testTranslate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testTranslate\\(\\) has parameter \\$createArray with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testTranslate\\(\\) has parameter \\$uri with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testTranslate\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testTranslateThrowsNotFoundException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testTranslateThrowsNotFoundException\\(\\) has parameter \\$createArray with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testTranslateThrowsNotFoundException\\(\\) has parameter \\$url with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:testTranslateUsesLongestMatchingWildcard\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UrlWildcardTest\\:\\:\\$urlWildcardHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\UrlWildcard\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UrlWildcardTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UserPasswordValidatorTest\\:\\:dateProviderForValidate\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserPasswordValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UserPasswordValidatorTest\\:\\:testValidate\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserPasswordValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UserPasswordValidatorTest\\:\\:testValidate\\(\\) has parameter \\$constraints with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserPasswordValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UserPasswordValidatorTest\\:\\:testValidate\\(\\) has parameter \\$expectedErrors with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserPasswordValidatorTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UserPreferenceTest\\:\\:createAPIUserPreferenceService\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserPreferenceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UserPreferenceTest\\:\\:testGetUserPreference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserPreferenceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UserPreferenceTest\\:\\:testGetUserPreferenceCount\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserPreferenceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UserPreferenceTest\\:\\:testLoadUserPreferences\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserPreferenceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UserPreferenceTest\\:\\:testSetUserPreference\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserPreferenceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UserPreferenceTest\\:\\:testSetUserPreferenceThrowsInvalidArgumentException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserPreferenceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UserPreferenceTest\\:\\:testSetUserPreferenceWithRollback\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserPreferenceTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UserPreferenceTest\\:\\:\\$userSPIPreferenceHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\UserPreference\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserPreferenceTest.php + + - + message: "#^Call to an undefined method Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler\\:\\:expects\\(\\)\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserTest.php + + - + message: "#^Parameter \\#1 \\$methods of method PHPUnit\\\\Framework\\\\MockObject\\\\MockBuilder\\<Ibexa\\\\Core\\\\Repository\\\\UserService\\>\\:\\:onlyMethods\\(\\) expects array\\<string\\>, array\\<string\\>\\|null given\\.$#" + count: 1 + path: tests/lib/Repository/Service/Mock/UserTest.php + + - + message: "#^Parameter \\#6 \\$userHandler of method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Service\\\\Mock\\\\UserTest\\:\\:mockDeleteUserFlow\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject, Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\User\\\\Handler given\\.$#" + count: 2 + path: tests/lib/Repository/Service/Mock/UserTest.php + + - + message: "#^Call to method expects\\(\\) on an unknown class object\\.$#" + count: 4 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:getAPIServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:getSiteAccessAwareServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:providerForLanguagesLookupMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:providerForPassTroughMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:setLanguagesLookupArguments\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:setLanguagesLookupArguments\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:setLanguagesLookupExpectedArguments\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:setLanguagesLookupExpectedArguments\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:setLanguagesLookupExpectedArguments\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:setLanguagesPassTroughArguments\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:setLanguagesPassTroughArguments\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:setLanguagesPassTroughArguments\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:testForLanguagesLookup\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:testForLanguagesLookup\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:testForLanguagesPassTrough\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:testForLanguagesPassTrough\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:testForPassTrough\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:testForPassTrough\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:\\$innerApiServiceMock \\(object&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) does not accept PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\AbstractServiceTest\\:\\:\\$innerApiServiceMock has unknown class object as its type\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Unable to resolve the template type T in call to method PHPUnit\\\\Framework\\\\TestCase\\:\\:getMockBuilder\\(\\)$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ContentServiceTest\\:\\:getAPIServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ContentServiceTest\\:\\:getSiteAccessAwareServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ContentServiceTest\\:\\:providerForLanguagesLookupMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ContentServiceTest\\:\\:providerForPassTroughMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/ContentServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ContentTypeServiceTest\\:\\:getAPIServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ContentTypeServiceTest\\:\\:getSiteAccessAwareServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ContentTypeServiceTest\\:\\:providerForLanguagesLookupMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ContentTypeServiceTest\\:\\:providerForPassTroughMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/ContentTypeServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\Language\\\\LanguageResolverTest\\:\\:getDataForTestGetPrioritizedLanguages\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/Language/LanguageResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\Language\\\\LanguageResolverTest\\:\\:testGetPrioritizedLanguages\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/Language/LanguageResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\Language\\\\LanguageResolverTest\\:\\:testGetPrioritizedLanguages\\(\\) has parameter \\$configLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/Language/LanguageResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\Language\\\\LanguageResolverTest\\:\\:testGetPrioritizedLanguages\\(\\) has parameter \\$expectedPrioritizedLanguagesList with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/Language/LanguageResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\Language\\\\LanguageResolverTest\\:\\:testGetPrioritizedLanguages\\(\\) has parameter \\$forcedLanguages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/Language/LanguageResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\LanguageServiceTest\\:\\:getAPIServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\LanguageServiceTest\\:\\:getSiteAccessAwareServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\LanguageServiceTest\\:\\:providerForLanguagesLookupMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\LanguageServiceTest\\:\\:providerForPassTroughMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/LanguageServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\LocationServiceTest\\:\\:getAPIServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\LocationServiceTest\\:\\:getSiteAccessAwareServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\LocationServiceTest\\:\\:providerForLanguagesLookupMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\LocationServiceTest\\:\\:providerForPassTroughMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/LocationServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ObjectStateServiceTest\\:\\:getAPIServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ObjectStateServiceTest\\:\\:getSiteAccessAwareServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ObjectStateServiceTest\\:\\:providerForLanguagesLookupMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\ObjectStateServiceTest\\:\\:providerForPassTroughMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/ObjectStateServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchServiceTest\\:\\:getAPIServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchServiceTest\\:\\:getSiteAccessAwareServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchServiceTest\\:\\:providerForLanguagesLookupMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchServiceTest\\:\\:providerForPassTroughMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchServiceTest\\:\\:setLanguagesLookupArguments\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchServiceTest\\:\\:setLanguagesLookupArguments\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchServiceTest\\:\\:setLanguagesLookupExpectedArguments\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchServiceTest\\:\\:setLanguagesLookupExpectedArguments\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchServiceTest\\:\\:setLanguagesLookupExpectedArguments\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchServiceTest\\:\\:setLanguagesPassTroughArguments\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchServiceTest\\:\\:setLanguagesPassTroughArguments\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\SearchServiceTest\\:\\:setLanguagesPassTroughArguments\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/SearchServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\TrashServiceTest\\:\\:getAPIServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\TrashServiceTest\\:\\:getSiteAccessAwareServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\TrashServiceTest\\:\\:providerForLanguagesLookupMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\TrashServiceTest\\:\\:providerForPassTroughMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/TrashServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UrlAliasServiceTest\\:\\:getAPIServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UrlAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UrlAliasServiceTest\\:\\:getSiteAccessAwareServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UrlAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UrlAliasServiceTest\\:\\:providerForLanguagesLookupMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UrlAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UrlAliasServiceTest\\:\\:providerForPassTroughMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UrlAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UrlAliasServiceTest\\:\\:setLanguagesLookupExpectedArguments\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UrlAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UrlAliasServiceTest\\:\\:setLanguagesLookupExpectedArguments\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UrlAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UrlAliasServiceTest\\:\\:setLanguagesLookupExpectedArguments\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UrlAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UrlAliasServiceTest\\:\\:setLanguagesPassTroughArguments\\(\\) has parameter \\$arguments with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UrlAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UrlAliasServiceTest\\:\\:setLanguagesPassTroughArguments\\(\\) has parameter \\$languages with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UrlAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UrlAliasServiceTest\\:\\:setLanguagesPassTroughArguments\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UrlAliasServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UserServiceTest\\:\\:getAPIServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UserServiceTest\\:\\:getSiteAccessAwareServiceClassName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UserServiceTest\\:\\:providerForLanguagesLookupMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UserServiceTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\SiteAccessAware\\\\UserServiceTest\\:\\:providerForPassTroughMethods\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/SiteAccessAware/UserServiceTest.php + + - + message: "#^Call to method expects\\(\\) on an unknown class PHPUnit_Framework_MockObject_MockObject\\.$#" + count: 2 + path: tests/lib/Repository/Validator/TargetContentValidatorTest.php + + - + message: "#^Call to method method\\(\\) on an unknown class PHPUnit_Framework_MockObject_MockObject\\.$#" + count: 1 + path: tests/lib/Repository/Validator/TargetContentValidatorTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Validator\\\\TargetContentValidatorTest\\:\\:\\$contentHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Handler&PHPUnit_Framework_MockObject_MockObject\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Repository/Validator/TargetContentValidatorTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Validator\\\\TargetContentValidatorTest\\:\\:\\$contentHandler has unknown class PHPUnit_Framework_MockObject_MockObject as its type\\.$#" + count: 1 + path: tests/lib/Repository/Validator/TargetContentValidatorTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Validator\\\\TargetContentValidatorTest\\:\\:\\$contentTypeHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler&PHPUnit_Framework_MockObject_MockObject\\) does not accept Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\.$#" + count: 1 + path: tests/lib/Repository/Validator/TargetContentValidatorTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Validator\\\\TargetContentValidatorTest\\:\\:\\$contentTypeHandler has unknown class PHPUnit_Framework_MockObject_MockObject as its type\\.$#" + count: 1 + path: tests/lib/Repository/Validator/TargetContentValidatorTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Language\\:\\:\\$notDefined\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/LanguageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LanguageTest\\:\\:assertPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/LanguageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LanguageTest\\:\\:testIsPropertySet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/LanguageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LanguageTest\\:\\:testMissingProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/LanguageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LanguageTest\\:\\:testNewClass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/LanguageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LanguageTest\\:\\:testReadOnlyProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/LanguageTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LanguageTest\\:\\:testUnsetProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/LanguageTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Location\\:\\:\\$notDefined\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/LocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\LocationTest\\:\\:assertPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/LocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Aggregation\\\\RangeTest\\:\\:dataProviderForTestToString\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/Query/Aggregation/RangeTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\DateMetadataTest\\:\\:testConstruction\\(\\) has parameter \\$value with no type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/Query/Criterion/DateMetadataTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Section\\:\\:\\$notDefined\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/SectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\SectionTest\\:\\:assertPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/SectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\SectionTest\\:\\:testIsPropertySet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/SectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\SectionTest\\:\\:testMissingProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/SectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\SectionTest\\:\\:testNewClass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/SectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\SectionTest\\:\\:testReadOnlyProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/SectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\SectionTest\\:\\:testUnsetProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/SectionTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItem\\:\\:\\$notDefined\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/TrashItemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItemTest\\:\\:assertPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/TrashItemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItemTest\\:\\:testIsPropertySet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/TrashItemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItemTest\\:\\:testMissingProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/TrashItemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItemTest\\:\\:testNewClass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/TrashItemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItemTest\\:\\:testReadOnlyProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/TrashItemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Content\\\\TrashItemTest\\:\\:testUnsetProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/Content/TrashItemTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\ContentTypeDraftTest\\:\\:testObjectProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ContentType/ContentTypeDraftTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionCollectionTest\\:\\:createFieldDefinitions\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Values/ContentType/FieldDefinitionCollectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionCollectionTest\\:\\:createFieldDefinitionsWith\\(\\) has parameter \\$values with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Values/ContentType/FieldDefinitionCollectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ContentType\\\\FieldDefinitionCollectionTest\\:\\:createFieldDefinitionsWith\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Values/ContentType/FieldDefinitionCollectionTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilterTest\\:\\:getComplexFilterTestData\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Repository/Values/Filter/FilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilterTest\\:\\:getFilters\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Repository/Values/Filter/FilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilterTest\\:\\:getFiltersWithInvalidSliceData\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Repository/Values/Filter/FilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilterTest\\:\\:getInvalidSortClausesData\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Repository/Values/Filter/FilterTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilterTest\\:\\:testConstructorThrowsBadStateException\\(\\) has parameter \\$sortClauses with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Repository/Values/Filter/FilterTest.php + + - + message: "#^Parameter \\#1 \\$criterion of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\Filter\\:\\:orWithCriterion\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion given\\.$#" + count: 1 + path: tests/lib/Repository/Values/Filter/FilterTest.php + + - + message: "#^Parameter \\#1 \\$criterion of method Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\Filter\\:\\:withCriterion\\(\\) expects Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Filter\\\\FilteringCriterion, Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion given\\.$#" + count: 1 + path: tests/lib/Repository/Values/Filter/FilterTest.php + + - + message: "#^Parameter \\#1 \\$value of class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\Criterion\\\\RemoteId constructor expects array\\<string\\>\\|string, int given\\.$#" + count: 1 + path: tests/lib/Repository/Values/Filter/FilterTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroup\\:\\:\\$notDefined\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroupTest\\:\\:assertPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroupTest\\:\\:testGetMultiLanguageDefaultDescription\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroupTest\\:\\:testGetMultiLanguageDefaultName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroupTest\\:\\:testGetMultiLanguagePrioritizedDescription\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroupTest\\:\\:testGetMultiLanguagePrioritizedName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroupTest\\:\\:testIsPropertySet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroupTest\\:\\:testMissingProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroupTest\\:\\:testNewClass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroupTest\\:\\:testReadOnlyProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateGroupTest\\:\\:testUnsetProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectState\\:\\:\\$notDefined\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateTest\\:\\:assertPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateTest\\:\\:testGetMultiLanguageDefaultDescription\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateTest\\:\\:testGetMultiLanguageDefaultName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateTest\\:\\:testGetMultiLanguagePrioritizedDescription\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateTest\\:\\:testGetMultiLanguagePrioritizedName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateTest\\:\\:testIsPropertySet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateTest\\:\\:testMissingProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateTest\\:\\:testNewClass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateTest\\:\\:testReadOnlyProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\ObjectState\\\\ObjectStateTest\\:\\:testUnsetProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/ObjectState/ObjectStateTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\Policy\\:\\:\\$notDefined\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/PolicyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyTest\\:\\:assertPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/PolicyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyTest\\:\\:testIsPropertySet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/PolicyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyTest\\:\\:testMissingProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/PolicyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyTest\\:\\:testNewClass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/PolicyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyTest\\:\\:testReadOnlyProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/PolicyTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\PolicyTest\\:\\:testUnsetProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/PolicyTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\Role\\:\\:\\$notDefined\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleTest\\:\\:assertPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleTest\\:\\:testIsPropertySet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleTest\\:\\:testMissingProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleTest\\:\\:testNewClass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleTest\\:\\:testReadOnlyProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/RoleTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\RoleTest\\:\\:testUnsetProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/RoleTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroup\\:\\:\\$notDefined\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupTest\\:\\:assertPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupTest\\:\\:testGetName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupTest\\:\\:testIsPropertySet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupTest\\:\\:testMissingProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupTest\\:\\:testNewClass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupTest\\:\\:testObjectProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupTest\\:\\:testReadOnlyProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserGroupTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserGroupTest\\:\\:testUnsetProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserGroupTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Core\\\\Repository\\\\Values\\\\User\\\\User\\:\\:\\$notDefined\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserTest\\:\\:assertPropertiesCorrect\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserTest\\:\\:testGetName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserTest\\:\\:testIsPropertySet\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserTest\\:\\:testMissingProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserTest\\:\\:testNewClass\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserTest\\:\\:testObjectProperties\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserTest\\:\\:testReadOnlyProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Repository\\\\Values\\\\User\\\\UserTest\\:\\:testUnsetProperty\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Repository/Values/User/UserTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Common\\\\FieldValueMapper\\\\RemoteIdentifierMapperTest\\:\\:getDataForTestCanMap\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Search/Common/FieldValueMapper/RemoteIdentifierMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Common\\\\FieldValueMapper\\\\RemoteIdentifierMapperTest\\:\\:getDataForTestMap\\(\\) return type has no value type specified in iterable type iterable\\.$#" + count: 1 + path: tests/lib/Search/Common/FieldValueMapper/RemoteIdentifierMapperTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:getMockedFieldNameResolver\\(\\) has parameter \\$methods with no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetFieldNames\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetFieldNamesReturnsEmptyArray\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetFieldNamesWithNamedField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetFieldNamesWithTypedAndNamedField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetFieldNamesWithTypedField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetIndexFieldNameCustomField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetIndexFieldNameDefaultMatchField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetIndexFieldNameDefaultMatchFieldThrowsRuntimeException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetIndexFieldNameDefaultSortField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetIndexFieldNameDefaultSortFieldThrowsRuntimeException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetIndexFieldNameNamedField\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetIndexFieldNameNamedFieldThrowsRuntimeException\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetSortFieldName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:testGetSortFieldNameReturnsNull\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Parameter \\#5 \\$name of method Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldNameResolver\\:\\:getIndexFieldName\\(\\) expects string, null given\\.$#" + count: 4 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:\\$contentTypeHandlerMock \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:\\$fieldNameGeneratorMock \\(Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldNameGenerator&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Search\\\\FieldNameResolverTest\\:\\:\\$fieldRegistryMock \\(Ibexa\\\\Core\\\\Search\\\\Common\\\\FieldRegistry&PHPUnit\\\\Framework\\\\MockObject\\\\MockObject\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Search/FieldNameResolverTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\AbstractTestCase\\:\\:assertSearchResults\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/AbstractTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\AbstractTestCase\\:\\:assertSearchResults\\(\\) has parameter \\$expectedIds with no type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/AbstractTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\AbstractTestCase\\:\\:assertSearchResults\\(\\) has parameter \\$searchResult with no type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/AbstractTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\AbstractTestCase\\:\\:getConverterRegistry\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/AbstractTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\AbstractTestCase\\:\\:getIds\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/AbstractTestCase.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\AbstractTestCase\\:\\:getIds\\(\\) has parameter \\$searchResult with no type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/AbstractTestCase.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\AbstractTestCase\\:\\:\\$contentTypeHandler \\(Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/AbstractTestCase.php + + - + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\AbstractTestCase\\:\\:\\$converterRegistry \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\ConverterRegistry\\) in isset\\(\\) is not nullable\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/AbstractTestCase.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" + count: 8 + path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\SortClause\\\\Field constructor invoked with 4 parameters, 2\\-3 required\\.$#" count: 2 - path: eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ViewManagerPass.php + path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php - - message: "#^Access to undefined constant static\\(eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\DependencyInjection\\\\Compiler\\\\ViewManagerPass\\)\\:\\:VIEW_TYPE\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentSortTest\\:\\:getContentSearchHandler\\(\\) has parameter \\$fullTextSearchConfiguration with no value type specified in iterable type array\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ViewManagerPass.php + path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php - - message: "#^Method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\DependencyInjection\\\\Configuration\\\\ConfigResolver\\:\\:logTooEarlyLoadedListIfNeeded\\(\\) should return string but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentSortTest\\:\\:testNoSorting\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver.php + path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php - - message: "#^Access to undefined constant static\\(eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Templates\\)\\:\\:INFO\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentSortTest\\:\\:testSortContentName\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Templates.php + path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php - - message: "#^Access to undefined constant static\\(eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Templates\\)\\:\\:INFO_TEMPLATE_KEY\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentSortTest\\:\\:testSortDateModified\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Templates.php + path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php - - message: "#^Access to undefined constant static\\(eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\Templates\\)\\:\\:NODE_KEY\\.$#" - count: 5 - path: eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Templates.php + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentSortTest\\:\\:testSortDatePublished\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentSortTest\\:\\:testSortFieldNumeric\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentSortTest\\:\\:testSortFieldText\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentSortTest\\:\\:testSortSectionIdentifier\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentSortTest\\:\\:testSortSectionName\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php + + - + message: "#^Parameter \\#1 \\$contentTypeHandler of method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageAwareTestCase\\:\\:getFullTextMapper\\(\\) expects Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler, Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler given\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php - - message: "#^Access to undefined constant static\\(eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\View\\)\\:\\:INFO\\.$#" + message: "#^Property Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentSortTest\\:\\:\\$fieldRegistry \\(Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\ConverterRegistry\\) in isset\\(\\) is not nullable\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/View.php + path: tests/lib/Search/Legacy/Content/HandlerContentSortTest.php - - message: "#^Access to undefined constant static\\(eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\View\\)\\:\\:NODE_KEY\\.$#" + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" count: 2 - path: eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/View.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Parameter \\$content of method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Features\\\\Context\\\\BasicContentContext\\:\\:publishDraft\\(\\) has invalid type eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Features\\\\Context\\\\Content\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:getContentSearchHandler\\(\\) has parameter \\$fullTextSearchConfiguration with no value type specified in iterable type array\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Features/Context/BasicContentContext.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Call to an undefined method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:processSettings\\(\\)\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testContentAndCombinatorFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Features/Context/ContentTypeContext.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Call to an undefined method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Features\\\\Context\\\\ContentTypeContext\\:\\:processValidator\\(\\)\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testContentIdFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Features/Context/ContentTypeContext.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Features\\\\Context\\\\UserContext\\:\\:ensureUserExists\\(\\) invoked with 2 parameters, 3\\-4 required\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testContentIdFilterCount\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Features/Context/UserContext.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUser\\(\\) should return eZ\\\\Publish\\\\API\\\\Repository\\\\Values\\\\User\\\\User but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testContentNotCombinatorFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Features/Context/UserContext.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUserInGroup\\(\\) should return eZ\\\\Publish\\\\API\\\\Repository\\\\Values\\\\User\\\\User but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testContentOrCombinatorFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Features/Context/UserContext.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Features\\\\Context\\\\UserContext\\:\\:iHaveUserWithUsernameEmailAndPassword\\(\\) should return eZ\\\\Publish\\\\API\\\\Repository\\\\Values\\\\User\\\\User but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testContentSubtreeFilterEq\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Features/Context/UserContext.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Access to an undefined property eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Tests\\\\DependencyInjection\\\\Configuration\\\\Parser\\\\LanguagesTest\\:\\:\\$minimalConfig\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testContentSubtreeFilterIn\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/LanguagesTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Tests\\\\DependencyInjection\\\\Stub\\\\QueryTypeBundle\\\\QueryType\\\\TestQueryType\\:\\:getQuery\\(\\) should return eZ\\\\Publish\\\\API\\\\Repository\\\\Values\\\\Content\\\\Query but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testContentTypeGroupFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Stub/QueryTypeBundle/QueryType/TestQueryType.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Tests\\\\DependencyInjection\\\\Stub\\\\QueryTypeBundle\\\\QueryType\\\\TestQueryType\\:\\:getSupportedParameters\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testContentTypeIdFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Stub/QueryTypeBundle/QueryType/TestQueryType.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Access to an undefined property eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Tests\\\\EventListener\\\\RoutingListenerTest\\:\\:\\$container\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testContentTypeIdentifierFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RoutingListenerTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Access to an undefined property eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Tests\\\\EventListener\\\\SessionSetDynamicNameListenerTest\\:\\:\\$session\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testDateMetadataFilterCreatedBetween\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/SessionSetDynamicNameListenerTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Tests\\\\EventListener\\\\Stubs\\\\ViewManager\\:\\:renderContent\\(\\) should return string but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testDateMetadataFilterModifiedBetween\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/Stubs/ViewManager.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Tests\\\\EventListener\\\\Stubs\\\\ViewManager\\:\\:renderContentView\\(\\) should return string but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testDateMetadataFilterModifiedGreater\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/Stubs/ViewManager.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Tests\\\\EventListener\\\\Stubs\\\\ViewManager\\:\\:renderLocation\\(\\) should return string but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testDateMetadataFilterModifiedGreaterOrEqual\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/Stubs/ViewManager.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Tests\\\\EventListener\\\\Stubs\\\\ViewProvider\\:\\:getView\\(\\) should return eZ\\\\Publish\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\View but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testDateMetadataFilterModifiedIn\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/Stubs/ViewProvider.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Access to an undefined property eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Tests\\\\Fragment\\\\FragmentRendererBaseTest\\:\\:\\$innerRenderer\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFieldFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/FragmentRendererBaseTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Access to an undefined property eZ\\\\Bundle\\\\EzPublishCoreBundle\\\\Tests\\\\Routing\\\\UrlAliasRouterTest\\:\\:\\$container\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFieldFilterBetween\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishCoreBundle/Tests/Routing/UrlAliasRouterTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Bundle\\\\EzPublishIOBundle\\\\BinaryStreamResponse\\:\\:sendContent\\(\\) should return \\$this\\(eZ\\\\Bundle\\\\EzPublishIOBundle\\\\BinaryStreamResponse\\) but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFieldFilterContainsPartial\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishIOBundle/BinaryStreamResponse.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Bundle\\\\EzPublishIOBundle\\\\BinaryStreamResponse\\:\\:setContent\\(\\) should return \\$this\\(eZ\\\\Bundle\\\\EzPublishIOBundle\\\\BinaryStreamResponse\\) but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFieldFilterContainsSimple\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishIOBundle/BinaryStreamResponse.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Instantiated class eZ\\\\Bundle\\\\EzPublishIOBundle\\\\Tests\\\\DependencyInjection\\\\ConfigurationFactory\\\\MetadataHandler\\\\Flysystem not found\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFieldFilterContainsSimpleNoMatch\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Bundle/EzPublishIOBundle/Tests/DependencyInjection/ConfigurationFactory/MetadataHandler/FlysystemTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\ContentServiceTest\\:\\:testCreateContentAndPublishWithPrivilegedAnonymousUser\\(\\) should return eZ\\\\Publish\\\\API\\\\Repository\\\\Values\\\\Content\\\\Content but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFieldFilterIn\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/ContentServiceTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\AuthorIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFieldFilterOr\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/AuthorIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\BinaryFileIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFieldRelationFilterContainsArray\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/BinaryFileIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\CheckboxIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFieldRelationFilterContainsArrayNotMatch\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/CheckboxIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\CountryIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFieldRelationFilterContainsSingle\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/CountryIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\DateAndTimeIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFieldRelationFilterContainsSingleNoMatch\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/DateAndTimeIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\EmailAddressIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFieldRelationFilterInArray\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/EmailAddressIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\FloatIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFieldRelationFilterInArrayNotMatch\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/FloatIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\ISBNIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFindContentWithNonSearchableField\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/ISBNIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\ImageIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFindSingle\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/ImageIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\IntegerIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFindSingleTooMany\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/IntegerIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\KeywordIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFindSingleWithNonSearchableField\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/KeywordIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\MapLocationIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFindSingleZero\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/MapLocationIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\MediaIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFindWithNullLimit\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/MediaIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\RelationIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFindWithOffsetToNonexistent\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/RelationIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\RelationListIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFindWithZeroLimit\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/RelationListIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\SelectionIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFindWithoutOffsetLimit\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/SelectionIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\TextBlockIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFullTextDisabledWildcardFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/TextBlockIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\TextLineIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFullTextFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/TextLineIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\TimeIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFullTextFilterInvalidStopwordThreshold\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/TimeIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\UrlIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFullTextFilterNoStopwordRemoval\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/UrlIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\FieldType\\\\UserIntegrationTest\\:\\:assertUpdatedFieldDataLoadedCorrect\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFullTextFilterStopwordRemoval\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/FieldType/UserIntegrationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Class EzSystems\\\\EzPlatformSolrSearchEngine\\\\Tests\\\\SetupFactory\\\\LegacySetupFactory not found\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testFullTextWildcardFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/Regression/EZP20018LanguageTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Instantiated class eZ\\\\Publish\\\\Core\\\\FieldType\\\\RichText\\\\Value not found\\.$#" - count: 3 - path: eZ/Publish/API/Repository/Tests/SearchService/SearchServiceFullTextEmbedTest.php + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testLanguageCodeFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Class EzSystems\\\\EzPlatformSolrSearchEngine\\\\Tests\\\\SetupFactory\\\\LegacySetupFactory not found\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testLanguageCodeFilterIn\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/SearchServiceLocationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Class EzSystems\\\\EzPlatformSolrSearchEngine\\\\Tests\\\\SetupFactory\\\\LegacySetupFactory not found\\.$#" - count: 5 - path: eZ/Publish/API/Repository/Tests/SearchServiceTest.php + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testLanguageCodeFilterWithAlwaysAvailable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Class EzSystems\\\\EzPlatformSolrSearchEngine\\\\Tests\\\\SetupFactory\\\\LegacySetupFactory not found\\.$#" - count: 3 - path: eZ/Publish/API/Repository/Tests/SearchServiceTranslationLanguageFallbackTest.php + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testLocationIdFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\URLWildcardServiceAuthorizationTest\\:\\:testCreateThrowsUnauthorizedException\\(\\) should return eZ\\\\Publish\\\\API\\\\Repository\\\\Values\\\\Content\\\\URLWildcard but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testLocationRemoteIdFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/URLWildcardServiceAuthorizationTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\URLWildcardServiceTest\\:\\:testTranslateThrowsNotFoundExceptionWhenNotAliasOrWildcardMatches\\(\\) should return eZ\\\\Publish\\\\API\\\\Repository\\\\Values\\\\Content\\\\URLAlias but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testObjectStateIdFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/URLWildcardServiceTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\UserServiceTest\\:\\:testUpdateUserNoPassword\\(\\) should return eZ\\\\Publish\\\\API\\\\Repository\\\\Values\\\\User\\\\User but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testObjectStateIdFilterIn\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/UserServiceTest.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Instantiated class eZ\\\\Publish\\\\API\\\\Repository\\\\Tests\\\\RuntimeException not found\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testParentLocationIdFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Tests/_fixtures/generate-fixtures.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Unsafe usage of new static\\(\\)\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testRemoteIdFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/API/Repository/Values/ValueObject.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Access to an undefined property eZ\\\\Publish\\\\Core\\\\Base\\\\Exceptions\\\\LimitationValidationException\\:\\:\\$validationErrors\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testSectionFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/Base/Exceptions/LimitationValidationException.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\Base\\\\Tests\\\\Container\\\\Compiler\\\\Stubs\\\\GatewayBasedStorageHandler\\:\\:deleteFieldData\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testStatusFilter\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/Base/Tests/Container/Compiler/Stubs/GatewayBasedStorageHandler.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\Base\\\\Tests\\\\Container\\\\Compiler\\\\Stubs\\\\GatewayBasedStorageHandler\\:\\:getIndexData\\(\\) should return array\\<eZ\\\\Publish\\\\SPI\\\\Search\\\\Field\\> but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testUserMetadataFilterCreatorEqAMember\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/Base/Tests/Container/Compiler/Stubs/GatewayBasedStorageHandler.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\Base\\\\Tests\\\\Container\\\\Compiler\\\\Stubs\\\\GatewayBasedStorageHandler\\:\\:hasFieldData\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testUserMetadataFilterCreatorInAMember\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/Base/Tests/Container/Compiler/Stubs/GatewayBasedStorageHandler.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:deleteFieldData\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testUserMetadataFilterEqGroupMember\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/BinaryBase/BinaryBaseStorage.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\FieldType\\\\BinaryBase\\\\BinaryBaseStorage\\:\\:getIndexData\\(\\) should return array\\<eZ\\\\Publish\\\\SPI\\\\Search\\\\Field\\> but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testUserMetadataFilterEqGroupMemberNoMatch\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/BinaryBase/BinaryBaseStorage.php + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Unsafe usage of new static\\(\\)\\.$#" - count: 2 - path: eZ/Publish/Core/FieldType/Date/Value.php + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testUserMetadataFilterInGroupMember\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php - - message: "#^Unsafe usage of new static\\(\\)\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testUserMetadataFilterInGroupMemberNoMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testUserMetadataFilterOwnerAdministrator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testUserMetadataFilterOwnerEqAMember\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testUserMetadataFilterOwnerInAMember\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testUserMetadataFilterOwnerWrongUserId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerContentTest\\:\\:testVisibilityFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php + + - + message: "#^Parameter \\#1 \\$contentTypeHandler of method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageAwareTestCase\\:\\:getFullTextMapper\\(\\) expects Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler, Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler given\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php + + - + message: "#^Parameter \\#3 \\$ruleFiles of class Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased constructor expects array, array\\<int, string\\>\\|false given\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerContentTest.php + + - + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" + count: 3 + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php + + - + message: "#^Class Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\Content\\\\Query\\\\SortClause\\\\Field constructor invoked with 4 parameters, 2\\-3 required\\.$#" count: 2 - path: eZ/Publish/Core/FieldType/DateAndTime/Value.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\FieldType\\\\Image\\\\ImageStorage\\:\\:deleteFieldData\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:getIds\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/Image/ImageStorage.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Unsafe usage of new static\\(\\)\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:getIds\\(\\) has parameter \\$searchResult with no type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/Image/Value.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\:\\:deleteFieldData\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testNoSorting\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/MapLocation/MapLocationStorage.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\\\Gateway\\\\DoctrineStorage\\:\\:getFieldData\\(\\) should return array but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortContentId\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\FieldType\\\\MapLocation\\\\MapLocationStorage\\\\Gateway\\\\DoctrineStorage\\:\\:updateFieldData\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortContentName\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Class eZ\\\\Publish\\\\Core\\\\FieldType\\\\ISBN\\\\Type does not have a constructor and must be instantiated without any parameters\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortDateModified\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/Tests/ISBNTest.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Access to an undefined property eZ\\\\Publish\\\\Core\\\\FieldType\\\\Tests\\\\ImageTest\\:\\:\\$mimeTypeDetectorMock\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortDatePublished\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/Tests/ImageTest.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Class eZ\\\\Publish\\\\SPI\\\\FieldType\\\\BinaryBase\\\\MimeTypeDetector not found\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortFieldNumeric\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/Tests/ImageTest.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Access to an undefined property eZ\\\\Publish\\\\Core\\\\FieldType\\\\Tests\\\\Url\\\\Gateway\\\\DoctrineStorageTest\\:\\:\\$storageGateway\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortFieldText\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/Tests/Url/Gateway/DoctrineStorageTest.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Unsafe usage of new static\\(\\)\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortIsMainLocationAscending\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/Time/Value.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\:\\:deleteFieldData\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortIsMainLocationDescending\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/Url/UrlStorage.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\FieldType\\\\Url\\\\UrlStorage\\:\\:getIndexData\\(\\) should return array\\<eZ\\\\Publish\\\\SPI\\\\Search\\\\Field\\> but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortLocationDepth\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/Url/UrlStorage.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\FieldType\\\\User\\\\UserStorage\\:\\:getIndexData\\(\\) should return array\\<eZ\\\\Publish\\\\SPI\\\\Search\\\\Field\\> but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortLocationDepthAndPath\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/FieldType/User/UserStorage.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\LocationRemote\\:\\:matchContentInfo\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortLocationId\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/LocationRemote.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\ContentBased\\\\Id\\\\LocationRemote\\:\\:matchLocation\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortLocationPath\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/MVC/Symfony/Matcher/ContentBased/Id/LocationRemote.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Access to an undefined property eZ\\\\Publish\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\Tests\\\\AbstractMatcherFactoryTest\\:\\:\\$matcherFactoryClass\\.$#" - count: 5 - path: eZ/Publish/Core/MVC/Symfony/Matcher/Tests/AbstractMatcherFactoryTest.php + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortLocationPriority\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Class eZ\\\\Publish\\\\Core\\\\MVC\\\\Symfony\\\\View\\\\Provider\\\\Location\\\\Configured not found\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortLocationVisibilityAscending\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/BaseTest.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Access to an undefined property eZ\\\\Publish\\\\Core\\\\MVC\\\\Symfony\\\\Matcher\\\\Tests\\\\ContentBasedMatcherFactoryTest\\:\\:\\$matcherFactoryClass\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortLocationVisibilityDescending\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBasedMatcherFactoryTest.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Unsafe usage of new static\\(\\)\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortSectionIdentifier\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/MVC/Symfony/Routing/SimplifiedRequest.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Access to undefined constant static\\(eZ\\\\Publish\\\\Core\\\\MVC\\\\Symfony\\\\SiteAccess\\\\Matcher\\\\Compound\\)\\:\\:NAME\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationSortTest\\:\\:testSortSectionName\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Compound.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelper\\:\\:getRequestedUriString\\(\\) should return string but return statement is missing\\.$#" + message: "#^Parameter \\#1 \\$contentTypeHandler of method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageAwareTestCase\\:\\:getFullTextMapper\\(\\) expects Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler, Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler given\\.$#" count: 1 - path: eZ/Publish/Core/MVC/Symfony/Templating/GlobalHelper.php + path: tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\MVC\\\\Symfony\\\\Templating\\\\GlobalHelper\\:\\:getViewParametersString\\(\\) should return string but return statement is missing\\.$#" + message: "#^Access to an undefined property Ibexa\\\\Contracts\\\\Core\\\\Repository\\\\Values\\\\ValueObject\\:\\:\\$id\\.$#" count: 1 - path: eZ/Publish/Core/MVC/Symfony/Templating/GlobalHelper.php + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php - - message: "#^Undefined variable\\: \\$ret$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:getContentSearchHandler\\(\\) has parameter \\$fullTextSearchConfiguration with no value type specified in iterable type array\\.$#" count: 1 - path: eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/FileSystemTwigIntegrationTestCase.php + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php - - message: "#^Class eZ\\\\Publish\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\FieldValue\\\\Converter\\\\SelectionConverter constructor invoked with 0 parameters, 1 required\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testContentDepthFilterBetween\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Handler\\:\\:deleteContent\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testContentDepthFilterEq\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/Persistence/Legacy/Content/Handler.php + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Handler\\:\\:deleteVersion\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testContentDepthFilterGreaterThan\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/Persistence/Legacy/Content/Handler.php + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:move\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testContentDepthFilterGreaterThanOrEqual\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/Persistence/Legacy/Content/Location/Handler.php + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:removeSubtree\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testContentDepthFilterIn\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/Persistence/Legacy/Content/Location/Handler.php + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Location\\\\Handler\\:\\:swap\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testContentDepthFilterLessThan\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/Persistence/Legacy/Content/Location/Handler.php + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\TreeHandler\\:\\:removeSubtree\\(\\) should return bool but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testContentDepthFilterLessThanOrEqual\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/Persistence/Legacy/Content/TreeHandler.php + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php - - message: "#^Method eZ\\\\Publish\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler\\:\\:addFieldDefinition\\(\\) should return eZ\\\\Publish\\\\SPI\\\\Persistence\\\\Content\\\\Type\\\\FieldDefinition but return statement is missing\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testContentIdFilterEquals\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/Core/Persistence/Legacy/Content/Type/Handler.php + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php - - message: "#^Class eZ\\\\Publish\\\\SPI\\\\Persistence\\\\Content\\\\UrlAlias referenced with incorrect case\\: eZ\\\\Publish\\\\SPI\\\\Persistence\\\\Content\\\\URLAlias\\.$#" - count: 2 - path: eZ/Publish/Core/Repository/URLAliasService.php + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testContentIdFilterIn\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php - - message: "#^Class Symfony\\\\Component\\\\Validator\\\\ConstraintViolation constructor invoked with 1 parameter, 6\\-10 required\\.$#" + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testContentTypeGroupFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testContentTypeIdFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testContentTypeIdentifierFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testDateMetadataFilterCreatedBetween\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testDateMetadataFilterModifiedBetween\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testDateMetadataFilterModifiedGreater\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testDateMetadataFilterModifiedGreaterOrEqual\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testDateMetadataFilterModifiedIn\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFieldFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFieldFilterBetween\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFieldFilterContainsPartial\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFieldFilterContainsSimple\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFieldFilterContainsSimpleNoMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFieldFilterIn\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFieldFilterOr\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFieldRelationFilterContainsArray\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFieldRelationFilterContainsArrayNotMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFieldRelationFilterContainsSingle\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFieldRelationFilterContainsSingleNoMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFieldRelationFilterInArray\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFieldRelationFilterInArrayNotMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFindWithNullLimit\\(\\) has no return type specified\\.$#" count: 1 - path: eZ/Publish/SPI/FieldType/Generic/Tests/GenericTest.php + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFindWithOffsetToNonexistent\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFindWithZeroLimit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFindWithoutOffsetLimit\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFullTextDisabledWildcardFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFullTextFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFullTextFilterInvalidStopwordThreshold\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFullTextFilterNoStopwordRemoval\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFullTextFilterStopwordRemoval\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testFullTextWildcardFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testIsMainLocationFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testIsNotMainLocationFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testLanguageCodeFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testLanguageCodeFilterIn\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testLanguageCodeFilterWithAlwaysAvailable\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testLocationIdAndCombinatorFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testLocationIdFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testLocationIdParentLocationIdAndCombinatorFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testLocationNotCombinatorFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testLocationOrCombinatorFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testLocationPriorityFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testLocationRemoteIdFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testMatchAllFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testObjectStateIdFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testObjectStateIdFilterIn\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testParentLocationIdFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testRemoteIdFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testSectionFilter\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testUserMetadataFilterCreatorEqAMember\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testUserMetadataFilterCreatorInAMember\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testUserMetadataFilterEqGroupMember\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testUserMetadataFilterEqGroupMemberNoMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testUserMetadataFilterInGroupMember\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testUserMetadataFilterInGroupMemberNoMatch\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testUserMetadataFilterOwnerAdministrator\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testUserMetadataFilterOwnerEqAMember\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testUserMetadataFilterOwnerInAMember\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testUserMetadataFilterOwnerWrongUserId\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testVisibilityFilterHidden\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Search\\\\Legacy\\\\Content\\\\HandlerLocationTest\\:\\:testVisibilityFilterVisible\\(\\) has no return type specified\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Parameter \\#1 \\$contentTypeHandler of method Ibexa\\\\Tests\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\LanguageAwareTestCase\\:\\:getFullTextMapper\\(\\) expects Ibexa\\\\Core\\\\Persistence\\\\Legacy\\\\Content\\\\Type\\\\Handler, Ibexa\\\\Contracts\\\\Core\\\\Persistence\\\\Content\\\\Type\\\\Handler given\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Parameter \\#3 \\$ruleFiles of class Ibexa\\\\Core\\\\Persistence\\\\TransformationProcessor\\\\DefinitionBased constructor expects array, array\\<int, string\\>\\|false given\\.$#" + count: 1 + path: tests/lib/Search/Legacy/Content/HandlerLocationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Specification\\\\Content\\\\ContentContainerSpecificationTest\\:\\:providerForIsSatisfiedBy\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Specification/Content/ContentContainerSpecificationTest.php + + - + message: "#^Method Ibexa\\\\Tests\\\\Core\\\\Specification\\\\Content\\\\ContentTypeSpecificationTest\\:\\:providerForIsSatisfiedBy\\(\\) return type has no value type specified in iterable type array\\.$#" + count: 1 + path: tests/lib/Specification/Content/ContentTypeSpecificationTest.php diff --git a/phpstan-baseline.neon.php b/phpstan-baseline.neon.php new file mode 100644 index 0000000000..662ea4edc3 --- /dev/null +++ b/phpstan-baseline.neon.php @@ -0,0 +1,27 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +$includes = []; +if (PHP_VERSION_ID < 80000) { + $includes[] = __DIR__ . '/phpstan-baseline-7.4.neon'; +} else { + $includes[] = __DIR__ . '/phpstan-baseline-gte-8.0.neon'; +} + +if (PHP_VERSION_ID < 80200) { + $includes[] = __DIR__ . '/phpstan-baseline-lte-8.1.neon'; +} + +if (PHP_VERSION_ID < 80300) { + $includes[] = __DIR__ . '/phpstan-baseline-lte-8.2.neon'; +} + +$config = []; +$config['includes'] = $includes; + +return $config; diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 5dccb39647..07ecb2f8d9 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,13 +1,22 @@ includes: + - vendor/phpstan/phpstan-phpunit/extension.neon + - vendor/phpstan/phpstan-symfony/extension.neon + - phpstan-baseline.neon.php - phpstan-baseline.neon parameters: - level: 0 + level: 8 + treatPhpDocTypesAsCertain: false + ignoreErrors: + - + message: "#^Cannot call method (fetchOne|fetchColumn|fetchAllAssociative|fetchAssociative|fetchAllKeyValue)\\(\\) on Doctrine\\\\DBAL\\\\ForwardCompatibility\\\\Result\\|int\\|string\\.$#" + paths: + - src/* + - tests/* paths: - - eZ - src - tests excludePaths: analyse: - tests/integration/Core/Repository/var - - eZ/Publish/Core/Search/Legacy/Tests/_fixtures/full_dump.php + - */_fixtures/*.php diff --git a/phpunit-integration-legacy-solr.xml b/phpunit-integration-legacy-solr.xml index 617d713820..2008a3ff12 100644 --- a/phpunit-integration-legacy-solr.xml +++ b/phpunit-integration-legacy-solr.xml @@ -11,72 +11,28 @@ failOnWarning="true" > <php> - <env name="setupFactory" value="EzSystems\EzPlatformSolrSearchEngine\Tests\SetupFactory\LegacySetupFactory" /> + <env name="setupFactory" value="Ibexa\Tests\Solr\SetupFactory\LegacySetupFactory" /> <env name="backendVersion" value="5" /> <env name="fixtureDir" value="Solr" /> <env name="solrServer" value="http://localhost:8983/" /> <env name="CORES_SETUP" value="dedicated" /> <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled"/> <ini name="error_reporting" value="-1" /> - <env name="KERNEL_CLASS" value="Ibexa\Contracts\Core\Test\IbexaTestKernel"/> + <env name="KERNEL_CLASS" value="Ibexa\Contracts\Solr\Test\IbexaSolrTestKernel"/> + <env name="SEARCH_ENGINE" value="solr"/> + <env name="DATABASE_URL" value="sqlite://:memory:" /> + <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled"/> </php> <testsuites> - <testsuite name="eZ\Publish\API\Repository"> - <!-- Only the Solr Search tests differ from the other tests, so we - are skipping everything else - - @todo: Search service is used all over the place, so we must test - all services here. - --> - <directory>eZ/Publish/API/Repository/Tests/Values/User/Limitation</directory> - <directory>eZ/Publish/API/Repository/Tests/FieldType/</directory> - <directory>eZ/Publish/API/Repository/Tests/SearchService/</directory> - <file>eZ/Publish/API/Repository/Tests/RepositoryTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SectionServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/LanguageServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/LanguageServiceMaximumSupportedLanguagesTest.php</file> - <file>eZ/Publish/API/Repository/Tests/ContentTypeServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/ContentServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/NonRedundantFieldSetTest.php</file> - <file>eZ/Publish/API/Repository/Tests/LocationServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/UserServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/RoleServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/TrashServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/URLAliasServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/URLWildcardServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/ObjectStateServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SectionServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/LanguageServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/ContentTypeServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/ContentServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/LocationServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/UserServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/RoleServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/TrashServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/URLWildcardServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/ObjectStateServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SearchServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SearchServiceFulltextTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SearchServiceTranslationLanguageFallbackTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SearchServiceLocationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SearchServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SearchEngineIndexingTest.php</file> - <file>eZ/Publish/API/Repository/Tests/FieldTypeServiceTest.php</file> - </testsuite> - <testsuite name="eZ\Publish\API\Repository\Tests\Regression"> - <directory>eZ/Publish/API/Repository/Tests/Regression/</directory> - </testsuite> - <testsuite name="ibexa-core-integration-tests"> + <!-- Search service is used all over the place, so we must run entire integration test suite --> + <testsuite name="integration_repository"> <directory>tests/integration/Core</directory> + <exclude>tests/integration/Core/Repository/Filtering</exclude> </testsuite> </testsuites> <filter> <whitelist> - <directory suffix=".php">eZ/Publish/API/Repository</directory> - <exclude> - <directory>eZ/Publish/API/Repository/Tests</directory> - <directory>eZ/Publish/API/Repository/Tutorials</directory> - </exclude> + <directory suffix=".php">tests/integration</directory> </whitelist> </filter> </phpunit> diff --git a/phpunit-integration-legacy.xml b/phpunit-integration-legacy.xml index 70b551314b..5a608b168f 100644 --- a/phpunit-integration-legacy.xml +++ b/phpunit-integration-legacy.xml @@ -11,84 +11,36 @@ failOnWarning="true" > <php> - <env name="setupFactory" value="eZ\Publish\API\Repository\Tests\SetupFactory\Legacy" /> + <env name="setupFactory" value="\Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy" /> <env name="backendVersion" value="5" /> <env name="fixtureDir" value="Legacy" /> <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled"/> <ini name="error_reporting" value="-1" /> <env name="DATABASE_URL" value="sqlite://:memory:" /> <env name="KERNEL_CLASS" value="Ibexa\Contracts\Core\Test\IbexaTestKernel"/> + <env name="SEARCH_ENGINE" value="legacy"/> + <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled"/> </php> <testsuites> - <testsuite name="eZ\Publish\API\Repository"> - <directory>eZ/Publish/API/Repository/Tests/Values/Content</directory> - <directory>eZ/Publish/API/Repository/Tests/Values/User/Limitation</directory> - <directory>eZ/Publish/API/Repository/Tests/FieldType/</directory> - <directory>eZ/Publish/API/Repository/Tests/Limitation</directory> - <directory>eZ/Publish/API/Repository/Tests/SearchService</directory> - <directory>eZ/Publish/API/Repository/Tests/Filtering</directory> - <directory>eZ/Publish/API/Repository/Tests/URLAliasService</directory> - <file>eZ/Publish/API/Repository/Tests/RepositoryTest.php</file> - <file>eZ/Publish/API/Repository/Tests/PermissionResolverTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SectionServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/LanguageServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/LanguageServiceMaximumSupportedLanguagesTest.php</file> - <file>eZ/Publish/API/Repository/Tests/ContentTypeServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/ContentServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/Parallel/ContentServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/NonRedundantFieldSetTest.php</file> - <file>eZ/Publish/API/Repository/Tests/LocationServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/UserServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/RoleServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/TrashServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/URLServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/URLAliasServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/URLWildcardServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/ObjectStateServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SectionServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/LanguageServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/ContentTypeServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/ContentServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/LocationServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/UserServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/RoleServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/TrashServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/URLServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/URLAliasServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/URLWildcardServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/ObjectStateServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SearchServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SearchServiceTranslationLanguageFallbackTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SearchServiceLocationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SearchServiceAuthorizationTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SearchEngineIndexingTest.php</file> - <file>eZ/Publish/API/Repository/Tests/FieldTypeServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/BookmarkServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/NotificationServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/UserPreferenceServiceTest.php</file> - <file>eZ/Publish/API/Repository/Tests/SettingServiceTest.php</file> + <testsuite name="integration_core"> + <directory>tests/integration/Core</directory> </testsuite> - <testsuite name="eZ\Publish\API\Repository\Tests\Regression"> - <directory>eZ/Publish/API/Repository/Tests/Regression/</directory> + <testsuite name="integration_debug"> + <directory>tests/integration/Debug</directory> </testsuite> - <testsuite name="eZ\Publish\Core\FieldType\Tests\Integration"> - <directory>eZ/Publish/Core/FieldType/Tests/Integration</directory> + <testsuite name="integration_io"> + <directory>tests/integration/IO</directory> </testsuite> - <testsuite name="gateway-integration"> - <file>eZ/Publish/SPI/Tests/Search/Content/IndexerGatewayTest.php</file> + <testsuite name="integration_legacy_search_engine"> + <directory>tests/integration/LegacySearchEngine</directory> </testsuite> - <testsuite name="kernel integration tests"> - <directory>tests/integration</directory> + <testsuite name="integration_repository_installer"> + <directory>tests/integration/RepositoryInstaller</directory> </testsuite> </testsuites> <filter> <whitelist> <directory suffix=".php">tests/integration</directory> - <directory suffix=".php">eZ/Publish/API/Repository</directory> - <exclude> - <directory>eZ/Publish/API/Repository/Tests</directory> - <directory>eZ/Publish/API/Repository/Tutorials</directory> - </exclude> </whitelist> </filter> </phpunit> diff --git a/phpunit.xml b/phpunit.xml index d8a4988387..b55dae9d52 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -11,92 +11,29 @@ > <php> <ini name="error_reporting" value="-1" /> - <env name="SYMFONY_DEPRECATIONS_HELPER" value="disabled"/> + <env name="SYMFONY_DEPRECATIONS_HELPER" value="max[total]=576&verbose=0"/> </php> <testsuites> - <testsuite name="eZ\Publish\Core\Base"> - <directory>eZ/Publish/Core/Base/Tests</directory> + <testsuite name="unit_core"> + <directory>tests/lib</directory> </testsuite> - <testsuite name="eZ\Publish\Core\Repository"> - <directory>eZ/Publish/Core/Repository/Tests</directory> + <testsuite name="bundle_core"> + <directory>tests/bundle/Core</directory> </testsuite> - <testsuite name="eZ\Publish\Core\FieldType"> - <directory>eZ/Publish/Core/FieldType/Tests</directory> - <exclude>eZ/Publish/Core/FieldType/Tests/Integration</exclude> + <testsuite name="bundle_debug"> + <directory>tests/bundle/Debug</directory> </testsuite> - <testsuite name="eZ\Publish\Core\Limitation"> - <directory>eZ/Publish/Core/Limitation/Tests</directory> + <testsuite name="bundle_installer"> + <directory>tests/bundle/Installer</directory> </testsuite> - <testsuite name="eZ\Publish\Core\Persistence"> - <directory>eZ/Publish/Core/Persistence/Tests</directory> + <testsuite name="bundle_io"> + <directory>tests/bundle/IO</directory> </testsuite> - <testsuite name="eZ\Publish\Core\Persistence\Cache"> - <directory>eZ/Publish/Core/Persistence/Cache/Tests</directory> - </testsuite> - <testsuite name="eZ\Publish\Core\Persistence\Legacy"> - <directory>eZ/Publish/Core/Persistence/Legacy/Tests</directory> - </testsuite> - <testsuite name="eZ\Publish\Core\QueryType\BuiltIn"> - <directory>eZ/Publish/Core/QueryType/BuiltIn/Tests</directory> - </testsuite> - <testsuite name="eZ\Publish\Core\QueryType\BuiltIn\SortSpec"> - <directory>eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests</directory> - </testsuite> - <testsuite name="eZ\Publish\Core\Search\Legacy"> - <directory>eZ/Publish/Core/Search/Legacy/Tests</directory> - </testsuite> - <testsuite name="eZ\Publish\Core\Search"> - <directory>eZ/Publish/Core/Search/Tests</directory> - </testsuite> - <testsuite name="eZ\Publish\Core\IO"> - <directory>eZ/Publish/Core/IO/Tests</directory> - </testsuite> - <testsuite name="eZ Publish HMVC test suite"> - <directory>eZ/Publish/Core/MVC/Symfony</directory> - <directory>eZ/Bundle</directory> - </testsuite> - <testsuite name="eZ Publish SPI test suite"> - <directory>eZ/Publish/SPI</directory> - </testsuite> - <testsuite name="eZ Publish Event Repository test suite"> - <directory>eZ/Publish/Core/Event</directory> - </testsuite> - <testsuite name="eZ\Publish\Core\Repository\SiteAccessAware"> - <directory>eZ/Publish/Core/Repository/SiteAccessAware</directory> - </testsuite> - <testsuite name="eZ\Publish\Core\Pagination"> - <directory>eZ/Publish/Core/Pagination</directory> - </testsuite> - <testsuite name="eZ\Publish\Core\Helper"> - <directory>eZ/Publish/Core/Helper</directory> - </testsuite> - <testsuite name="eZ\Publish\API\Repository\Values\Content"> - <directory>eZ/Publish/API/Repository/Tests/Values</directory> - </testsuite> - <testsuite name="eZ\Publish\API\Repository\Iterator"> - <directory>eZ/Publish/API/Repository/Tests/Iterator</directory> - </testsuite> - <testsuite name="Ibexa\Tests\Core\"> - <directory>tests/lib</directory> + <testsuite name="bundle_lse"> + <directory>tests/bundle/LegacySearchEngineBundle</directory> </testsuite> </testsuites> - <filter> - <whitelist> - <directory suffix=".php">eZ</directory> - <exclude> - <directory>eZ/Publish/*/Tests</directory> - <directory>eZ/Publish/*/*/Tests</directory> - <directory>eZ/Publish/*/*/*/Tests</directory> - <directory>eZ/Publish/*/*/*/*/Tests</directory> - <directory>eZ/Bundle/*/Tests</directory> - <directory>eZ/Bundle/*/_fixtures</directory> - <directory>eZ/Publish/Core/Persistence/Legacy/docs</directory> - <directory>doc</directory> - <directory>design</directory> - <directory>vendor</directory> - <directory>eZ/Publish/API/Repository/Examples</directory> - <directory>eZ/Publish/API/Repository/Tutorials</directory> - </exclude> - </whitelist> - </filter> + <listeners> + <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener"/> + </listeners> </phpunit> diff --git a/src/bundle/Core/ApiLoader/CacheFactory.php b/src/bundle/Core/ApiLoader/CacheFactory.php new file mode 100644 index 0000000000..a01c686542 --- /dev/null +++ b/src/bundle/Core/ApiLoader/CacheFactory.php @@ -0,0 +1,45 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\ApiLoader; + +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Symfony\Component\Cache\Adapter\TagAwareAdapter; +use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; + +/** + * Class CacheFactory. + * + * Service "ibexa.cache_pool", selects a Symfony cache service based on siteaccess[-group] setting "cache_service_name" + */ +class CacheFactory implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + /** + * @param \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface $configResolver + * + * @return \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface + */ + public function getCachePool(ConfigResolverInterface $configResolver) + { + /** @var \Symfony\Component\Cache\Adapter\AdapterInterface $cacheService */ + $cacheService = $this->container->get($configResolver->getParameter('cache_service_name')); + + // If cache service is already implementing TagAwareAdapterInterface, return as-is + if ($cacheService instanceof TagAwareAdapterInterface) { + return $cacheService; + } + + return new TagAwareAdapter( + $cacheService + ); + } +} + +class_alias(CacheFactory::class, 'eZ\Bundle\EzPublishCoreBundle\ApiLoader\CacheFactory'); diff --git a/src/bundle/Core/ApiLoader/Exception/InvalidRepositoryException.php b/src/bundle/Core/ApiLoader/Exception/InvalidRepositoryException.php new file mode 100644 index 0000000000..3b4941d068 --- /dev/null +++ b/src/bundle/Core/ApiLoader/Exception/InvalidRepositoryException.php @@ -0,0 +1,15 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\ApiLoader\Exception; + +use InvalidArgumentException; + +class InvalidRepositoryException extends InvalidArgumentException +{ +} + +class_alias(InvalidRepositoryException::class, 'eZ\Bundle\EzPublishCoreBundle\ApiLoader\Exception\InvalidRepositoryException'); diff --git a/src/bundle/Core/ApiLoader/Exception/InvalidSearchEngine.php b/src/bundle/Core/ApiLoader/Exception/InvalidSearchEngine.php new file mode 100644 index 0000000000..9ef5d897e5 --- /dev/null +++ b/src/bundle/Core/ApiLoader/Exception/InvalidSearchEngine.php @@ -0,0 +1,15 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\ApiLoader\Exception; + +use InvalidArgumentException; + +class InvalidSearchEngine extends InvalidArgumentException +{ +} + +class_alias(InvalidSearchEngine::class, 'eZ\Bundle\EzPublishCoreBundle\ApiLoader\Exception\InvalidSearchEngine'); diff --git a/src/bundle/Core/ApiLoader/Exception/InvalidSearchEngineIndexer.php b/src/bundle/Core/ApiLoader/Exception/InvalidSearchEngineIndexer.php new file mode 100644 index 0000000000..2f4c23bdf6 --- /dev/null +++ b/src/bundle/Core/ApiLoader/Exception/InvalidSearchEngineIndexer.php @@ -0,0 +1,15 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\ApiLoader\Exception; + +use InvalidArgumentException; + +class InvalidSearchEngineIndexer extends InvalidArgumentException +{ +} + +class_alias(InvalidSearchEngineIndexer::class, 'eZ\Bundle\EzPublishCoreBundle\ApiLoader\Exception\InvalidSearchEngineIndexer'); diff --git a/src/bundle/Core/ApiLoader/Exception/InvalidStorageEngine.php b/src/bundle/Core/ApiLoader/Exception/InvalidStorageEngine.php new file mode 100644 index 0000000000..da452291ee --- /dev/null +++ b/src/bundle/Core/ApiLoader/Exception/InvalidStorageEngine.php @@ -0,0 +1,15 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\ApiLoader\Exception; + +use InvalidArgumentException; + +class InvalidStorageEngine extends InvalidArgumentException +{ +} + +class_alias(InvalidStorageEngine::class, 'eZ\Bundle\EzPublishCoreBundle\ApiLoader\Exception\InvalidStorageEngine'); diff --git a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/RepositoryConfigurationProvider.php b/src/bundle/Core/ApiLoader/RepositoryConfigurationProvider.php similarity index 80% rename from eZ/Bundle/EzPublishCoreBundle/ApiLoader/RepositoryConfigurationProvider.php rename to src/bundle/Core/ApiLoader/RepositoryConfigurationProvider.php index 55fcf121a9..ed7b577b8f 100644 --- a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/RepositoryConfigurationProvider.php +++ b/src/bundle/Core/ApiLoader/RepositoryConfigurationProvider.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\ApiLoader; +namespace Ibexa\Bundle\Core\ApiLoader; -use eZ\Bundle\EzPublishCoreBundle\ApiLoader\Exception\InvalidRepositoryException; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Bundle\Core\ApiLoader\Exception\InvalidRepositoryException; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; /** * The repository configuration provider. @@ -18,7 +18,7 @@ class RepositoryConfigurationProvider private const REPOSITORY_CONNECTION = 'connection'; private const DEFAULT_CONNECTION_NAME = 'default'; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; /** @var array */ @@ -33,7 +33,7 @@ public function __construct(ConfigResolverInterface $configResolver, array $repo /** * @return array * - * @throws \eZ\Bundle\EzPublishCoreBundle\ApiLoader\Exception\InvalidRepositoryException + * @throws \Ibexa\Bundle\Core\ApiLoader\Exception\InvalidRepositoryException */ public function getRepositoryConfig() { @@ -44,7 +44,7 @@ public function getRepositoryConfig() if (empty($repositoryAlias) || !isset($this->repositories[$repositoryAlias])) { throw new InvalidRepositoryException( - "Undefined Repository '$repositoryAlias'. Check if the Repository is configured in ezpublish_*.yml." + "Undefined Repository '$repositoryAlias'. Check if the Repository is configured in your project's ibexa.yaml." ); } @@ -72,3 +72,5 @@ public function getStorageConnectionName(): string : self::DEFAULT_CONNECTION_NAME; } } + +class_alias(RepositoryConfigurationProvider::class, 'eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider'); diff --git a/src/bundle/Core/ApiLoader/RepositoryFactory.php b/src/bundle/Core/ApiLoader/RepositoryFactory.php new file mode 100644 index 0000000000..2d2ac159fd --- /dev/null +++ b/src/bundle/Core/ApiLoader/RepositoryFactory.php @@ -0,0 +1,137 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\ApiLoader; + +use Ibexa\Contracts\Core\Persistence\Filter\Content\Handler as ContentFilteringHandler; +use Ibexa\Contracts\Core\Persistence\Filter\Location\Handler as LocationFilteringHandler; +use Ibexa\Contracts\Core\Persistence\Handler as PersistenceHandler; +use Ibexa\Contracts\Core\Repository\LanguageResolver; +use Ibexa\Contracts\Core\Repository\NameSchema\NameSchemaServiceInterface; +use Ibexa\Contracts\Core\Repository\PasswordHashService; +use Ibexa\Contracts\Core\Repository\PermissionService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Strategy\ContentThumbnail\ThumbnailStrategy; +use Ibexa\Contracts\Core\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Search\Handler as SearchHandler; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\FieldType\FieldTypeRegistry; +use Ibexa\Core\Repository\Helper\RelationProcessor; +use Ibexa\Core\Repository\Mapper; +use Ibexa\Core\Repository\Permission\LimitationService; +use Ibexa\Core\Repository\ProxyFactory\ProxyDomainMapperFactoryInterface; +use Ibexa\Core\Repository\User\PasswordValidatorInterface; +use Ibexa\Core\Search\Common\BackgroundIndexer; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use Symfony\Component\DependencyInjection\ContainerAwareInterface; +use Symfony\Component\DependencyInjection\ContainerAwareTrait; + +class RepositoryFactory implements ContainerAwareInterface +{ + use ContainerAwareTrait; + + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ + private $configResolver; + + /** @var string */ + private $repositoryClass; + + /** + * Map of system configured policies. + * + * @var array + */ + private $policyMap; + + /** @var \Psr\Log\LoggerInterface */ + private $logger; + + /** @var \Ibexa\Contracts\Core\Repository\LanguageResolver */ + private $languageResolver; + + public function __construct( + ConfigResolverInterface $configResolver, + $repositoryClass, + array $policyMap, + LanguageResolver $languageResolver, + LoggerInterface $logger = null + ) { + $this->configResolver = $configResolver; + $this->repositoryClass = $repositoryClass; + $this->policyMap = $policyMap; + $this->languageResolver = $languageResolver; + $this->logger = $logger ?? new NullLogger(); + } + + /** + * Builds the main repository, heart of Ibexa API. + * + * This always returns the true inner Repository, please depend on ibexa.api.repository and not this method + * directly to make sure you get an instance wrapped inside Event / Cache / * functionality. + */ + public function buildRepository( + PersistenceHandler $persistenceHandler, + SearchHandler $searchHandler, + BackgroundIndexer $backgroundIndexer, + RelationProcessor $relationProcessor, + FieldTypeRegistry $fieldTypeRegistry, + PasswordHashService $passwordHashService, + ThumbnailStrategy $thumbnailStrategy, + ProxyDomainMapperFactoryInterface $proxyDomainMapperFactory, + Mapper\ContentDomainMapper $contentDomainMapper, + Mapper\ContentTypeDomainMapper $contentTypeDomainMapper, + Mapper\RoleDomainMapper $roleDomainMapper, + Mapper\ContentMapper $contentMapper, + ContentValidator $contentValidator, + LimitationService $limitationService, + PermissionService $permissionService, + ContentFilteringHandler $contentFilteringHandler, + LocationFilteringHandler $locationFilteringHandler, + PasswordValidatorInterface $passwordValidator, + ConfigResolverInterface $configResolver, + NameSchemaServiceInterface $nameSchemaService + ): Repository { + $config = $this->container->get(RepositoryConfigurationProvider::class)->getRepositoryConfig(); + + return new $this->repositoryClass( + $persistenceHandler, + $searchHandler, + $backgroundIndexer, + $relationProcessor, + $fieldTypeRegistry, + $passwordHashService, + $thumbnailStrategy, + $proxyDomainMapperFactory, + $contentDomainMapper, + $contentTypeDomainMapper, + $roleDomainMapper, + $contentMapper, + $contentValidator, + $limitationService, + $this->languageResolver, + $permissionService, + $contentFilteringHandler, + $locationFilteringHandler, + $passwordValidator, + $configResolver, + $nameSchemaService, + [ + 'role' => [ + 'policyMap' => $this->policyMap, + ], + 'languages' => $this->configResolver->getParameter('languages'), + 'content' => [ + 'default_version_archive_limit' => $config['options']['default_version_archive_limit'], + 'remove_archived_versions_on_publish' => $config['options']['remove_archived_versions_on_publish'], + ], + ], + $this->logger + ); + } +} + +class_alias(RepositoryFactory::class, 'eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryFactory'); diff --git a/src/bundle/Core/ApiLoader/SearchEngineFactory.php b/src/bundle/Core/ApiLoader/SearchEngineFactory.php new file mode 100644 index 0000000000..75d29fe433 --- /dev/null +++ b/src/bundle/Core/ApiLoader/SearchEngineFactory.php @@ -0,0 +1,88 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\ApiLoader; + +use Ibexa\Bundle\Core\ApiLoader\Exception\InvalidSearchEngine; +use Ibexa\Contracts\Core\Search\Handler as SearchHandler; + +/** + * The search engine factory. + */ +class SearchEngineFactory +{ + /** @var \Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider */ + private $repositoryConfigurationProvider; + + /** + * Hash of registered search engines. + * Key is the search engine identifier, value search handler itself. + * + * @var \Ibexa\Contracts\Core\Search\Handler[] + */ + protected $searchEngines = []; + + public function __construct(RepositoryConfigurationProvider $repositoryConfigurationProvider) + { + $this->repositoryConfigurationProvider = $repositoryConfigurationProvider; + } + + /** + * Registers $searchHandler as a valid search engine with identifier $searchEngineIdentifier. + * + * Note It is strongly recommended to register a lazy persistent handler. + * + * @param \Ibexa\Contracts\Core\Search\Handler $searchHandler + * @param string $searchEngineIdentifier + */ + public function registerSearchEngine(SearchHandler $searchHandler, $searchEngineIdentifier) + { + $this->searchEngines[$searchEngineIdentifier] = $searchHandler; + } + + /** + * Returns registered search engines. + * + * @return \Ibexa\Contracts\Core\Search\Handler[] + */ + public function getSearchEngines() + { + return $this->searchEngines; + } + + /** + * Builds search engine identified by its identifier (the "alias" attribute in the service tag), + * resolved for current SiteAccess. + * + * @throws \Ibexa\Bundle\Core\ApiLoader\Exception\InvalidSearchEngine + */ + public function buildSearchEngine(): SearchHandler + { + $repositoryConfig = $this->repositoryConfigurationProvider->getRepositoryConfig(); + + $searchEngineAlias = $repositoryConfig['search']['engine'] ?? null; + if (null === $searchEngineAlias) { + throw new InvalidSearchEngine( + sprintf( + 'Ibexa "%s" Repository has no Search Engine configured', + $this->repositoryConfigurationProvider->getCurrentRepositoryAlias() + ) + ); + } + + if (!isset($this->searchEngines[$searchEngineAlias])) { + throw new InvalidSearchEngine( + "Invalid search engine '{$searchEngineAlias}'. " . + "Could not find any service tagged with 'ibexa.search.engine' " . + "with alias '{$searchEngineAlias}'." + ); + } + + return $this->searchEngines[$searchEngineAlias]; + } +} + +class_alias(SearchEngineFactory::class, 'eZ\Bundle\EzPublishCoreBundle\ApiLoader\SearchEngineFactory'); diff --git a/src/bundle/Core/ApiLoader/SearchEngineIndexerFactory.php b/src/bundle/Core/ApiLoader/SearchEngineIndexerFactory.php new file mode 100644 index 0000000000..a0190e3e02 --- /dev/null +++ b/src/bundle/Core/ApiLoader/SearchEngineIndexerFactory.php @@ -0,0 +1,91 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\ApiLoader; + +use Ibexa\Bundle\Core\ApiLoader\Exception\InvalidSearchEngine; +use Ibexa\Bundle\Core\ApiLoader\Exception\InvalidSearchEngineIndexer; +use Ibexa\Core\Search\Common\Indexer as SearchEngineIndexer; + +/** + * The search engine indexer factory. + */ +class SearchEngineIndexerFactory +{ + /** @var \Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider */ + private $repositoryConfigurationProvider; + + /** + * Hash of registered search engine indexers. + * Key is the search engine identifier, value indexer itself. + * + * @var \Ibexa\Core\Search\Common\Indexer[] + */ + protected $searchEngineIndexers = []; + + public function __construct(RepositoryConfigurationProvider $repositoryConfigurationProvider) + { + $this->repositoryConfigurationProvider = $repositoryConfigurationProvider; + } + + /** + * Registers $searchEngineIndexer as a valid search engine indexer with identifier $searchEngineIdentifier. + * + * note: It is strongly recommended to register indexer as a lazy service. + * + * @param \Ibexa\Core\Search\Common\Indexer $searchEngineIndexer + * @param string $searchEngineIdentifier + */ + public function registerSearchEngineIndexer(SearchEngineIndexer $searchEngineIndexer, $searchEngineIdentifier) + { + $this->searchEngineIndexers[$searchEngineIdentifier] = $searchEngineIndexer; + } + + /** + * Returns registered search engine indexers. + * + * @return \Ibexa\Core\Search\Common\Indexer[] + */ + public function getSearchEngineIndexers() + { + return $this->searchEngineIndexers; + } + + /** + * Build search engine indexer identified by its identifier (the "alias" attribute in the service tag), + * resolved for current SiteAccess. + * + * @throws \Ibexa\Bundle\Core\ApiLoader\Exception\InvalidSearchEngineIndexer + * + * @return \Ibexa\Core\Search\Common\Indexer + */ + public function buildSearchEngineIndexer(): SearchEngineIndexer + { + $repositoryConfig = $this->repositoryConfigurationProvider->getRepositoryConfig(); + + $searchEngineAlias = $repositoryConfig['search']['engine'] ?? null; + if (null === $searchEngineAlias) { + throw new InvalidSearchEngine( + sprintf( + 'Ibexa "%s" Repository has no Search Engine configured', + $this->repositoryConfigurationProvider->getCurrentRepositoryAlias() + ) + ); + } + + if (!isset($this->searchEngineIndexers[$searchEngineAlias])) { + throw new InvalidSearchEngineIndexer( + "Invalid search engine '{$searchEngineAlias}'. " . + "Could not find any service tagged with 'ibexa.search.engine.indexer' " . + "with alias '{$searchEngineAlias}'." + ); + } + + return $this->searchEngineIndexers[$searchEngineAlias]; + } +} + +class_alias(SearchEngineIndexerFactory::class, 'eZ\Bundle\EzPublishCoreBundle\ApiLoader\SearchEngineIndexerFactory'); diff --git a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/StorageConnectionFactory.php b/src/bundle/Core/ApiLoader/StorageConnectionFactory.php similarity index 89% rename from eZ/Bundle/EzPublishCoreBundle/ApiLoader/StorageConnectionFactory.php rename to src/bundle/Core/ApiLoader/StorageConnectionFactory.php index be8434b63d..365ab589b9 100644 --- a/eZ/Bundle/EzPublishCoreBundle/ApiLoader/StorageConnectionFactory.php +++ b/src/bundle/Core/ApiLoader/StorageConnectionFactory.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\ApiLoader; +namespace Ibexa\Bundle\Core\ApiLoader; use InvalidArgumentException; use Symfony\Component\DependencyInjection\ContainerAwareInterface; @@ -14,7 +14,7 @@ class StorageConnectionFactory implements ContainerAwareInterface { use ContainerAwareTrait; - /** @var \eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider */ + /** @var \Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider */ protected $repositoryConfigurationProvider; public function __construct(RepositoryConfigurationProvider $repositoryConfigurationProvider) @@ -52,3 +52,5 @@ public function getConnection() return $this->container->get($doctrineConnectionId); } } + +class_alias(StorageConnectionFactory::class, 'eZ\Bundle\EzPublishCoreBundle\ApiLoader\StorageConnectionFactory'); diff --git a/src/bundle/Core/ApiLoader/StorageEngineFactory.php b/src/bundle/Core/ApiLoader/StorageEngineFactory.php new file mode 100644 index 0000000000..b856747531 --- /dev/null +++ b/src/bundle/Core/ApiLoader/StorageEngineFactory.php @@ -0,0 +1,85 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\ApiLoader; + +use Ibexa\Bundle\Core\ApiLoader\Exception\InvalidStorageEngine; +use Ibexa\Contracts\Core\Persistence\Handler as PersistenceHandler; + +/** + * The storage engine factory. + */ +class StorageEngineFactory +{ + /** @var \Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider */ + private $repositoryConfigurationProvider; + + /** + * Hash of registered storage engines. + * Key is the storage engine identifier, value persistence handler itself. + * + * @var \Ibexa\Contracts\Core\Persistence\Handler[] + */ + protected $storageEngines = []; + + public function __construct(RepositoryConfigurationProvider $repositoryConfigurationProvider) + { + $this->repositoryConfigurationProvider = $repositoryConfigurationProvider; + } + + /** + * Registers $persistenceHandler as a valid storage engine, with identifier $storageEngineIdentifier. + * + * Note: It is strongly recommenced to register a lazy persistent handler. + * + * @param \Ibexa\Contracts\Core\Persistence\Handler $persistenceHandler + * @param string $storageEngineIdentifier + */ + public function registerStorageEngine(PersistenceHandler $persistenceHandler, $storageEngineIdentifier) + { + $this->storageEngines[$storageEngineIdentifier] = $persistenceHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Handler[] + */ + public function getStorageEngines() + { + return $this->storageEngines; + } + + /** + * Builds storage engine identified by $storageEngineIdentifier (the "alias" attribute in the service tag). + * + * @throws \Ibexa\Bundle\Core\ApiLoader\Exception\InvalidStorageEngine + */ + public function buildStorageEngine(): PersistenceHandler + { + $repositoryConfig = $this->repositoryConfigurationProvider->getRepositoryConfig(); + + $storageEngineAlias = $repositoryConfig['storage']['engine'] ?? null; + if (null === $storageEngineAlias) { + throw new InvalidStorageEngine( + sprintf( + 'Ibexa "%s" Repository has no Storage Engine configured', + $this->repositoryConfigurationProvider->getCurrentRepositoryAlias() + ) + ); + } + + if (!isset($this->storageEngines[$storageEngineAlias])) { + throw new InvalidStorageEngine( + "Invalid storage engine '{$storageEngineAlias}'. " . + 'Could not find any service tagged with ibexa.storage ' . + "with alias {$storageEngineAlias}." + ); + } + + return $this->storageEngines[$repositoryConfig['storage']['engine']]; + } +} + +class_alias(StorageEngineFactory::class, 'eZ\Bundle\EzPublishCoreBundle\ApiLoader\StorageEngineFactory'); diff --git a/src/bundle/Core/Cache/Warmer/ProxyCacheWarmer.php b/src/bundle/Core/Cache/Warmer/ProxyCacheWarmer.php new file mode 100644 index 0000000000..bb1e498886 --- /dev/null +++ b/src/bundle/Core/Cache/Warmer/ProxyCacheWarmer.php @@ -0,0 +1,56 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\Cache\Warmer; + +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\Section; +use Ibexa\Contracts\Core\Repository\Values\Content\Thumbnail; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Core\Repository\ProxyFactory\ProxyGeneratorInterface; +use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerInterface; + +final class ProxyCacheWarmer implements CacheWarmerInterface +{ + public const PROXY_CLASSES = [ + Content::class, + ContentInfo::class, + ContentType::class, + ContentTypeGroup::class, + Language::class, + Location::class, + Section::class, + User::class, + Thumbnail::class, + ]; + + /** @var \Ibexa\Core\Repository\ProxyFactory\ProxyGeneratorInterface */ + private $proxyGenerator; + + public function __construct(ProxyGeneratorInterface $proxyGenerator) + { + $this->proxyGenerator = $proxyGenerator; + } + + public function isOptional(): bool + { + return false; + } + + public function warmUp($cacheDir): void + { + $this->proxyGenerator->warmUp(self::PROXY_CLASSES); + } +} + +class_alias(ProxyCacheWarmer::class, 'eZ\Bundle\EzPublishCoreBundle\Cache\Warmer\ProxyCacheWarmer'); diff --git a/src/bundle/Core/Command/BackwardCompatibleCommand.php b/src/bundle/Core/Command/BackwardCompatibleCommand.php new file mode 100644 index 0000000000..3adee22ade --- /dev/null +++ b/src/bundle/Core/Command/BackwardCompatibleCommand.php @@ -0,0 +1,21 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\Command; + +interface BackwardCompatibleCommand +{ + /** + * Returns deprecated command aliases. + * + * @return string[] + */ + public function getDeprecatedAliases(): array; +} + +class_alias(BackwardCompatibleCommand::class, 'eZ\Bundle\EzPublishCoreBundle\Command\BackwardCompatibleCommand'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Command/CheckURLsCommand.php b/src/bundle/Core/Command/CheckURLsCommand.php similarity index 78% rename from eZ/Bundle/EzPublishCoreBundle/Command/CheckURLsCommand.php rename to src/bundle/Core/Command/CheckURLsCommand.php index 8b9069067b..7716722b69 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Command/CheckURLsCommand.php +++ b/src/bundle/Core/Command/CheckURLsCommand.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Command; - -use eZ\Bundle\EzPublishCoreBundle\URLChecker\URLCheckerInterface; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\URLService; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\API\Repository\Values\URL\Query\SortClause; -use eZ\Publish\API\Repository\Values\URL\URLQuery; +namespace Ibexa\Bundle\Core\Command; + +use Ibexa\Bundle\Core\URLChecker\URLCheckerInterface; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\URLService; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\URL\URLQuery; use RuntimeException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\ProgressBar; @@ -27,16 +27,16 @@ class CheckURLsCommand extends Command implements BackwardCompatibleCommand private const DEFAULT_ITERATION_COUNT = 50; private const DEFAULT_REPOSITORY_USER = 'admin'; - /** @var \eZ\Publish\API\Repository\UserService */ + /** @var \Ibexa\Contracts\Core\Repository\UserService */ private $userService; - /** @var \eZ\Publish\API\Repository\PermissionResolver */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ private $permissionResolver; - /** @var \eZ\Publish\API\Repository\URLService */ + /** @var \Ibexa\Contracts\Core\Repository\URLService */ private $urlService; - /** @var \eZ\Bundle\EzPublishCoreBundle\URLChecker\URLCheckerInterface */ + /** @var \Ibexa\Bundle\Core\URLChecker\URLCheckerInterface */ private $urlChecker; public function __construct( @@ -69,7 +69,7 @@ public function configure(): void 'user', 'u', InputOption::VALUE_OPTIONAL, - 'eZ Platform username (with Role containing at least content Policies: read, versionread, edit, remove, versionremove)', + 'Ibexa username (with Role containing at least content Policies: read, versionread, edit, remove, versionremove)', self::DEFAULT_REPOSITORY_USER ); } @@ -124,3 +124,5 @@ public function getDeprecatedAliases(): array return ['ezplatform:check-urls']; } } + +class_alias(CheckURLsCommand::class, 'eZ\Bundle\EzPublishCoreBundle\Command\CheckURLsCommand'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Command/CleanupVersionsCommand.php b/src/bundle/Core/Command/CleanupVersionsCommand.php similarity index 91% rename from eZ/Bundle/EzPublishCoreBundle/Command/CleanupVersionsCommand.php rename to src/bundle/Core/Command/CleanupVersionsCommand.php index c59e6ad338..82d3bb0a33 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Command/CleanupVersionsCommand.php +++ b/src/bundle/Core/Command/CleanupVersionsCommand.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Command; +namespace Ibexa\Bundle\Core\Command; use Doctrine\DBAL\Connection; use Exception; -use eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; use PDO; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\ProgressBar; @@ -43,10 +43,10 @@ class CleanupVersionsCommand extends Command implements BackwardCompatibleComman self::VERSION_PUBLISHED => VersionInfo::STATUS_PUBLISHED, ]; - /** @var \eZ\Publish\API\Repository\Repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository */ private $repository; - /** @var \eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider */ + /** @var \Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider */ private $repositoryConfigurationProvider; /** @var \Doctrine\DBAL\Driver\Connection */ @@ -94,14 +94,14 @@ protected function configure() 'user', 'u', InputOption::VALUE_OPTIONAL, - 'eZ Platform username (with Role containing at least content policies: remove, read, versionread)', + 'Ibexa username (with Role containing at least content policies: remove, read, versionread)', self::DEFAULT_REPOSITORY_USER ) ->addOption( 'excluded-content-types', null, InputOption::VALUE_OPTIONAL, - 'Comma-separated list of Content Type identifiers whose versions should not be removed, for instance `article`.', + 'Comma-separated list of content type identifiers whose versions should not be removed, for instance `article`.', self::DEFAULT_EXCLUDED_CONTENT_TYPES )->setHelp( <<<EOT @@ -204,7 +204,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int (int) $contentId ), OutputInterface::VERBOSITY_VERBOSE); - /** @var \eZ\Publish\API\Repository\Values\Content\VersionInfo $version */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $version */ foreach ($versions as $version) { $contentService->deleteVersion($version); ++$removedVersionsCounter; @@ -242,7 +242,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int * * @return array * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException */ protected function getObjectsIds($keep, $status, $excludedContentTypes = []) { @@ -284,7 +284,7 @@ protected function getObjectsIds($keep, $status, $excludedContentTypes = []) * * @return int * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException */ private function mapStatusToVersionInfoStatus($status) { @@ -306,3 +306,5 @@ public function getDeprecatedAliases(): array return ['ezplatform:content:cleanup-versions']; } } + +class_alias(CleanupVersionsCommand::class, 'eZ\Bundle\EzPublishCoreBundle\Command\CleanupVersionsCommand'); diff --git a/src/bundle/Core/Command/CopySubtreeCommand.php b/src/bundle/Core/Command/CopySubtreeCommand.php new file mode 100644 index 0000000000..5e65b57873 --- /dev/null +++ b/src/bundle/Core/Command/CopySubtreeCommand.php @@ -0,0 +1,192 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Command; + +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; + +/** + * Console command for deep copying subtree from one location to another. + */ +class CopySubtreeCommand extends Command implements BackwardCompatibleCommand +{ + /** @var \Ibexa\Contracts\Core\Repository\LocationService */ + private $locationService; + + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ + private $permissionResolver; + + /** @var \Ibexa\Contracts\Core\Repository\UserService */ + private $userService; + + /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */ + private $contentTypeService; + + /** @var \Ibexa\Contracts\Core\Repository\SearchService */ + private $searchService; + + /** + * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService + * @param \Ibexa\Contracts\Core\Repository\PermissionResolver $permissionResolver + * @param \Ibexa\Contracts\Core\Repository\UserService $userService + * @param \Ibexa\Contracts\Core\Repository\ContentTypeService $contentTypeService + * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService + */ + public function __construct( + LocationService $locationService, + PermissionResolver $permissionResolver, + UserService $userService, + ContentTypeService $contentTypeService, + SearchService $searchService + ) { + parent::__construct(); + $this->locationService = $locationService; + $this->permissionResolver = $permissionResolver; + $this->userService = $userService; + $this->contentTypeService = $contentTypeService; + $this->searchService = $searchService; + } + + protected function configure() + { + $this + ->setName('ibexa:copy-subtree') + ->setAliases($this->getDeprecatedAliases()) + ->addArgument( + 'source-location-id', + InputArgument::REQUIRED, + 'ID of source Location' + ) + ->addArgument( + 'target-location-id', + InputArgument::REQUIRED, + 'ID of target Location' + ) + ->addOption( + 'user', + 'u', + InputOption::VALUE_OPTIONAL, + 'Ibexa username (with Role containing at least content policies: create, read)', + 'admin' + ) + ->setDescription('Copies a subtree from one Location to another'); + } + + /** + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + protected function initialize(InputInterface $input, OutputInterface $output) + { + parent::initialize($input, $output); + $this->permissionResolver->setCurrentUserReference( + $this->userService->loadUserByLogin($input->getOption('user')) + ); + } + + /** + * @param \Symfony\Component\Console\Input\InputInterface $input + * @param \Symfony\Component\Console\Output\OutputInterface $output + * + * @return int|null + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + protected function execute(InputInterface $input, OutputInterface $output): int + { + $sourceLocationId = $input->getArgument('source-location-id'); + $targetLocationId = $input->getArgument('target-location-id'); + + $sourceLocation = $this->locationService->loadLocation($sourceLocationId); + $targetLocation = $this->locationService->loadLocation($targetLocationId); + + if (stripos($targetLocation->pathString, $sourceLocation->pathString) !== false) { + throw new InvalidArgumentException( + 'target-location-id', + 'Cannot copy subtree to its own descendant Location' + ); + } + + $targetContentType = $this->contentTypeService->loadContentType( + $targetLocation->getContentInfo()->contentTypeId + ); + + if (!$targetContentType->isContainer) { + throw new InvalidArgumentException( + 'target-location-id', + 'The selected Location cannot contain children' + ); + } + $questionHelper = $this->getHelper('question'); + $question = new ConfirmationQuestion( + sprintf( + 'Are you sure you want to copy `%s` subtree (no. of children: %d) into `%s`? This may take a while for a big number of nested children [Y/n]? ', + $sourceLocation->contentInfo->name, + $this->getAllChildrenCount($sourceLocation), + $targetLocation->contentInfo->name + ) + ); + + if (!$input->getOption('no-interaction') && !$questionHelper->ask($input, $output, $question)) { + return 0; + } + + $this->locationService->copySubtree( + $sourceLocation, + $targetLocation + ); + + $output->writeln( + '<info>Finished</info>' + ); + + return 0; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * + * @return int + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + protected function getAllChildrenCount(Location $location): int + { + $query = new LocationQuery([ + 'filter' => new Criterion\Subtree($location->pathString), + ]); + + $searchResults = $this->searchService->findLocations($query); + + return $searchResults->totalCount; + } + + public function getDeprecatedAliases(): array + { + return ['ezplatform:copy-subtree']; + } +} + +class_alias(CopySubtreeCommand::class, 'eZ\Bundle\EzPublishCoreBundle\Command\CopySubtreeCommand'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Command/DebugConfigResolverCommand.php b/src/bundle/Core/Command/DebugConfigResolverCommand.php similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/Command/DebugConfigResolverCommand.php rename to src/bundle/Core/Command/DebugConfigResolverCommand.php index 0d7d5d8f2f..42f8d11dae 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Command/DebugConfigResolverCommand.php +++ b/src/bundle/Core/Command/DebugConfigResolverCommand.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Command; +namespace Ibexa\Bundle\Core\Command; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -18,10 +18,10 @@ class DebugConfigResolverCommand extends Command implements BackwardCompatibleCommand { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ private $siteAccess; public function __construct( @@ -63,7 +63,7 @@ public function configure() 'namespace', null, InputOption::VALUE_REQUIRED, - 'Set a different namespace than the default "ezsettings" used by SiteAccess settings.' + 'Set a different namespace than the default "ibexa.site_access.config" used by SiteAccess settings.' ); $this->setHelp( <<<EOM @@ -72,10 +72,10 @@ public function configure() By default it will give value depending on the global <comment>--siteaccess[=SITEACCESS]</comment> (default SiteAccess is used if not set). However, you can also manually set <comment>--scope[=NAME]</comment> yourself if you don't want to affect the SiteAccess -set by the system. You can also override the namespace to get something other than the default "ezsettings" namespace by using +set by the system. You can also override the namespace to get something other than the default "ibexa.site_access.config" namespace by using the <comment>--namespace[=NS]</comment> option. -NOTE: To see *all* compiled SiteAccess settings, use: <comment>debug:config ezpublish [system.default]</comment> +NOTE: To see *all* compiled SiteAccess settings, use: <comment>debug:config ibexa [system.default]</comment> EOM ); @@ -118,3 +118,5 @@ public function getDeprecatedAliases(): array return ['ezplatform:debug:config-resolver', 'ezplatform:debug:config']; } } + +class_alias(DebugConfigResolverCommand::class, 'eZ\Bundle\EzPublishCoreBundle\Command\DebugConfigResolverCommand'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Command/DeleteContentTranslationCommand.php b/src/bundle/Core/Command/DeleteContentTranslationCommand.php similarity index 89% rename from eZ/Bundle/EzPublishCoreBundle/Command/DeleteContentTranslationCommand.php rename to src/bundle/Core/Command/DeleteContentTranslationCommand.php index 6fcab7bf4c..a1bd2bac3b 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Command/DeleteContentTranslationCommand.php +++ b/src/bundle/Core/Command/DeleteContentTranslationCommand.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Command; +namespace Ibexa\Bundle\Core\Command; use Exception; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -23,10 +23,10 @@ */ class DeleteContentTranslationCommand extends Command implements BackwardCompatibleCommand { - /** @var \eZ\Publish\API\Repository\Repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository */ private $repository; - /** @var \eZ\Publish\API\Repository\ContentService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentService */ private $contentService; /** @var \Symfony\Component\Console\Input\InputInterface */ @@ -62,7 +62,7 @@ protected function configure() 'user', 'u', InputOption::VALUE_OPTIONAL, - 'eZ Platform username (with Role containing at least content Policies: read, versionread, edit, remove, versionremove)', + 'Ibexa username (with Role containing at least content Policies: read, versionread, edit, remove, versionremove)', 'admin' ) ->setDescription('Deletes a translation from all versions of a Content item'); @@ -97,7 +97,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $this->output->writeln( - '<comment>**NOTE**: Make sure to run this command using the same SYMFONY_ENV setting as your eZ Platform installation</comment>' + '<comment>**NOTE**: Make sure to run this command using the same SYMFONY_ENV setting as your Ibexa installation</comment>' ); $versionInfo = $this->contentService->loadVersionInfoById($contentId); @@ -148,11 +148,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** * Interact with user to update main Language of a Content Object. * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo * @param string $languageCode language code of the Translation to be deleted * @param string[] $lastVersionLanguageCodes all Translations last Version has. * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo */ private function promptUserForMainLanguageChange( ContentInfo $contentInfo, @@ -201,3 +201,5 @@ public function getDeprecatedAliases(): array return ['ezplatform:delete-content-translation']; } } + +class_alias(DeleteContentTranslationCommand::class, 'eZ\Bundle\EzPublishCoreBundle\Command\DeleteContentTranslationCommand'); diff --git a/src/bundle/Core/Command/ExpireUserPasswordsCommand.php b/src/bundle/Core/Command/ExpireUserPasswordsCommand.php index efab27711d..b6062f6165 100644 --- a/src/bundle/Core/Command/ExpireUserPasswordsCommand.php +++ b/src/bundle/Core/Command/ExpireUserPasswordsCommand.php @@ -9,15 +9,15 @@ namespace Ibexa\Bundle\Core\Command; use Exception; -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\ContentList; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\SPI\Persistence\User\Handler; +use Ibexa\Contracts\Core\Persistence\User\Handler; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentList; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; use InvalidArgumentException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\ProgressBar; @@ -45,17 +45,13 @@ final class ExpireUserPasswordsCommand extends Command - Run this command in production environment using <info>--env=prod</info> EOT; - /** @var \eZ\Publish\API\Repository\Repository */ - private $repository; + private Repository $repository; - /** @var \eZ\Publish\API\Repository\ContentService */ - private $contentService; + private ContentService $contentService; - /** @var \eZ\Publish\API\Repository\ContentTypeService */ - private $contentTypeService; + private ContentTypeService $contentTypeService; - /** @var \eZ\Publish\SPI\Persistence\User\Handler */ - private $userHandler; + private Handler $userHandler; public function __construct( Repository $repository, @@ -92,7 +88,7 @@ protected function configure(): void 'user-content-type-identifier', 'ct', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, - 'Expire passwords of all users based on specific Content Type' + 'Expire passwords of all users based on specific content type' ) ->addOption( 'force', @@ -111,7 +107,7 @@ protected function configure(): void 'password-ttl', 't', InputOption::VALUE_REQUIRED, - 'After how many days passwords expire. Set when Content Type needs to be updated.', + 'After how many days passwords expire. Set when content type needs to be updated.', self::DEFAULT_PASSWORD_TTL ) ->setHelp( @@ -275,7 +271,7 @@ private function supplySearchCriteria( if (!empty($userContentTypeIdentifiers)) { $output->writeln( sprintf( - "<info>\tUser Content Type Identifier: %s</info>", + "<info>\tUser content type Identifier: %s</info>", implode(', ', $userContentTypeIdentifiers) ) ); @@ -341,7 +337,7 @@ private function updateContentType( $fieldSettings = $userFieldDefinition->getFieldSettings(); $output->writeln(sprintf( - '<info>Content Type "%s" needs to be updated:</info>', + '<info>Content type "%s" needs to be updated:</info>', $contentType->identifier )); @@ -429,7 +425,7 @@ private function doesContentTypeNeedUpdate(ContentType $contentType, array $proc if ($count !== 1) { throw new InvalidArgumentException(sprintf( - 'Expected exactly 1 "%s" field type in "%s" Content Type, found %d', + 'Expected exactly 1 "%s" field type in "%s" content type, found %d', self::USER_FIELDTYPE_IDENTIFIER, $contentType->identifier, $count @@ -441,9 +437,7 @@ private function doesContentTypeNeedUpdate(ContentType $contentType, array $proc $validatorConfiguration = $fieldDefinition->getValidatorConfiguration(); $fieldSettings = $fieldDefinition->getFieldSettings(); - $isUpdateNeeded = !$validatorConfiguration['PasswordValueValidator']['requireNewPassword'] + return !$validatorConfiguration['PasswordValueValidator']['requireNewPassword'] || 0 === $fieldSettings['PasswordTTL']; - - return $isUpdateNeeded; } } diff --git a/src/bundle/Core/Command/Indexer/ContentIdList/ContentTypeInputGeneratorStrategy.php b/src/bundle/Core/Command/Indexer/ContentIdList/ContentTypeInputGeneratorStrategy.php new file mode 100644 index 0000000000..8b6faa8ccf --- /dev/null +++ b/src/bundle/Core/Command/Indexer/ContentIdList/ContentTypeInputGeneratorStrategy.php @@ -0,0 +1,79 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\Command\Indexer\ContentIdList; + +use Generator; +use Ibexa\Bundle\Core\Command\Indexer\ContentIdListGeneratorStrategyInterface; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentList; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use Ibexa\Core\Search\Indexer\ContentIdBatchList; +use Symfony\Component\Console\Input\InputInterface; + +/** + * @internal + */ +final class ContentTypeInputGeneratorStrategy implements ContentIdListGeneratorStrategyInterface +{ + private ContentService $contentService; + + public function __construct(ContentService $contentService) + { + $this->contentService = $contentService; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + public function getBatchList(InputInterface $input, int $batchSize): ContentIdBatchList + { + $contentList = $this->getContentList($input->getOption('content-type')); + + return new ContentIdBatchList( + $this->buildGenerator($contentList, $batchSize), + $contentList->getTotalCount(), + ); + } + + private function buildGenerator(ContentList $contentList, int $batchSize): Generator + { + $contentIds = []; + foreach ($contentList as $content) { + $contentIds[] = $content->getVersionInfo()->getContentInfo()->getId(); + if (count($contentIds) >= $batchSize) { + yield $contentIds; + $contentIds = []; + } + } + if (!empty($contentIds)) { + yield $contentIds; + } + } + + public function shouldPurgeIndex(InputInterface $input): bool + { + return false; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + private function getContentList(string $contentTypeIdentifier): ContentList + { + $filter = new Filter(); + $filter + ->withCriterion( + new Query\Criterion\ContentTypeIdentifier($contentTypeIdentifier) + ) + ; + + return $this->contentService->find($filter); + } +} diff --git a/src/bundle/Core/Command/Indexer/ContentIdListGeneratorStrategyInterface.php b/src/bundle/Core/Command/Indexer/ContentIdListGeneratorStrategyInterface.php new file mode 100644 index 0000000000..2cbbb02e8e --- /dev/null +++ b/src/bundle/Core/Command/Indexer/ContentIdListGeneratorStrategyInterface.php @@ -0,0 +1,22 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\Command\Indexer; + +use Ibexa\Core\Search\Indexer\ContentIdBatchList; +use Symfony\Component\Console\Input\InputInterface; + +/** + * @internal + */ +interface ContentIdListGeneratorStrategyInterface +{ + public function shouldPurgeIndex(InputInterface $input): bool; + + public function getBatchList(InputInterface $input, int $batchSize): ContentIdBatchList; +} diff --git a/eZ/Bundle/EzPublishCoreBundle/Command/NormalizeImagesPathsCommand.php b/src/bundle/Core/Command/NormalizeImagesPathsCommand.php similarity index 92% rename from eZ/Bundle/EzPublishCoreBundle/Command/NormalizeImagesPathsCommand.php rename to src/bundle/Core/Command/NormalizeImagesPathsCommand.php index 759fff0024..f224c012eb 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Command/NormalizeImagesPathsCommand.php +++ b/src/bundle/Core/Command/NormalizeImagesPathsCommand.php @@ -6,16 +6,16 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Command; +namespace Ibexa\Bundle\Core\Command; use Doctrine\DBAL\Driver\Connection; -use eZ\Publish\Core\FieldType\Image\ImageStorage\Gateway as ImageStorageGateway; -use eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException; -use eZ\Publish\Core\IO\FilePathNormalizerInterface; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\IO\Values\BinaryFileCreateStruct; -use eZ\Publish\Core\IO\Values\MissingBinaryFile; +use Ibexa\Core\FieldType\Image\ImageStorage\Gateway as ImageStorageGateway; +use Ibexa\Core\IO\Exception\BinaryFileNotFoundException; +use Ibexa\Core\IO\FilePathNormalizerInterface; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\BinaryFile; +use Ibexa\Core\IO\Values\BinaryFileCreateStruct; +use Ibexa\Core\IO\Values\MissingBinaryFile; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -39,16 +39,16 @@ final class NormalizeImagesPathsCommand extends Command protected static $defaultName = 'ibexa:images:normalize-paths'; - /** @var \eZ\Publish\Core\FieldType\Image\ImageStorage\Gateway */ + /** @var \Ibexa\Core\FieldType\Image\ImageStorage\Gateway */ private $imageGateway; - /** @var \eZ\Publish\Core\IO\FilePathNormalizerInterface */ + /** @var \Ibexa\Core\IO\FilePathNormalizerInterface */ private $filePathNormalizer; /** @var \Doctrine\DBAL\Driver\Connection */ private $connection; - /** @var \eZ\Publish\Core\IO\IOServiceInterface */ + /** @var \Ibexa\Core\IO\IOServiceInterface */ private $ioService; /** @var bool */ @@ -141,8 +141,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** * @param resource $inputStream * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ private function updateImagePath( int $fieldId, @@ -309,3 +309,5 @@ private function normalizeImagePaths(array $imagePathsToNormalize, SymfonyStyle return $oldBinaryFilesToDelete; } } + +class_alias(NormalizeImagesPathsCommand::class, 'eZ\Bundle\EzPublishCoreBundle\Command\NormalizeImagesPathsCommand'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php b/src/bundle/Core/Command/RegenerateUrlAliasesCommand.php similarity index 94% rename from eZ/Bundle/EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php rename to src/bundle/Core/Command/RegenerateUrlAliasesCommand.php index 1f2e1e3f7c..b65cef4e45 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Command/RegenerateUrlAliasesCommand.php +++ b/src/bundle/Core/Command/RegenerateUrlAliasesCommand.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Command; +namespace Ibexa\Bundle\Core\Command; use Exception; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Symfony\Component\Console\Command\Command; @@ -36,14 +36,14 @@ class RegenerateUrlAliasesCommand extends Command implements BackwardCompatibleC - Manually clear HTTP cache after running this command. EOT; - /** @var \eZ\Publish\API\Repository\Repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository */ private $repository; /** @var \Psr\Log\LoggerInterface */ private $logger; /** - * @param \eZ\Publish\API\Repository\Repository $repository + * @param \Ibexa\Contracts\Core\Repository\Repository $repository * @param \Psr\Log\LoggerInterface $logger */ public function __construct(Repository $repository, LoggerInterface $logger = null) @@ -181,7 +181,7 @@ protected function getProgressBar($maxSteps, OutputInterface $output) /** * Process single results page of fetched Locations. * - * @param \eZ\Publish\API\Repository\Values\Content\Location[] $locations + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location[] $locations * @param \Symfony\Component\Console\Helper\ProgressBar $progressBar */ private function processLocations(array $locations, ProgressBar $progressBar) @@ -240,7 +240,7 @@ static function (Repository $repository) use ($location) { * @param int $offset * @param int $iterationCount * - * @return \eZ\Publish\API\Repository\Values\Content\Location[] + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location[] * * @throws \Exception */ @@ -258,7 +258,7 @@ static function (Repository $repository) use ($offset, $iterationCount) { * @param int $offset * @param int $iterationCount * - * @return \eZ\Publish\API\Repository\Values\Content\Location[] + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location[] * * @throws \Exception */ @@ -335,3 +335,5 @@ public function getDeprecatedAliases(): array return ['ezplatform:urls:regenerate-aliases']; } } + +class_alias(RegenerateUrlAliasesCommand::class, 'eZ\Bundle\EzPublishCoreBundle\Command\RegenerateUrlAliasesCommand'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Command/ReindexCommand.php b/src/bundle/Core/Command/ReindexCommand.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/Command/ReindexCommand.php rename to src/bundle/Core/Command/ReindexCommand.php index ca3dc61c30..bd8d6cc246 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Command/ReindexCommand.php +++ b/src/bundle/Core/Command/ReindexCommand.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Command; +namespace Ibexa\Bundle\Core\Command; use function count; use DateTime; use const DIRECTORY_SEPARATOR; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Search\Common\IncrementalIndexer; -use eZ\Publish\Core\Search\Common\Indexer; -use eZ\Publish\SPI\Persistence\Content\Location\Handler; -use eZ\Publish\SPI\Search\Content\IndexerGateway; -use Generator; +use Ibexa\Bundle\Core\Command\Indexer\ContentIdListGeneratorStrategyInterface; +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler; +use Ibexa\Contracts\Core\Search\Content\IndexerGateway; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Search\Common\IncrementalIndexer; +use Ibexa\Core\Search\Common\Indexer; use Psr\Log\LoggerInterface; use RuntimeException; use Symfony\Component\Console\Command\Command; @@ -28,7 +28,7 @@ class ReindexCommand extends Command implements BackwardCompatibleCommand { - /** @var \eZ\Publish\Core\Search\Common\Indexer|\eZ\Publish\Core\Search\Common\IncrementalIndexer */ + /** @var \Ibexa\Core\Search\Common\Indexer|\Ibexa\Core\Search\Common\IncrementalIndexer */ private $searchIndexer; /** @var string */ @@ -49,14 +49,16 @@ class ReindexCommand extends Command implements BackwardCompatibleCommand /** @var string */ private $projectDir; - /** @var \eZ\Publish\SPI\Search\Content\IndexerGateway */ + /** @var \Ibexa\Contracts\Core\Search\Content\IndexerGateway */ private $gateway; - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Handler */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Location\Handler */ private $locationHandler; + private ContentIdListGeneratorStrategyInterface $contentIdListGeneratorStrategy; + public function __construct( - $searchIndexer, + Indexer $searchIndexer, Handler $locationHandler, IndexerGateway $gateway, LoggerInterface $logger, @@ -64,6 +66,7 @@ public function __construct( string $env, bool $isDebug, string $projectDir, + ContentIdListGeneratorStrategyInterface $contentIdListGeneratorStrategy, string $phpPath = null ) { $this->gateway = $gateway; @@ -75,7 +78,7 @@ public function __construct( $this->env = $env; $this->isDebug = $isDebug; $this->projectDir = $projectDir; - $this->phpPath = $phpPath; + $this->contentIdListGeneratorStrategy = $contentIdListGeneratorStrategy; parent::__construct(); } @@ -128,23 +131,28 @@ protected function configure() 'since', null, InputOption::VALUE_OPTIONAL, - 'Refresh changes since a time provided in any format understood by DateTime. Implies "no-purge", cannot be combined with "content-ids" or "subtree"' + 'Refresh changes since a time provided in any format understood by DateTime. Implies "no-purge", cannot be combined with "content-ids", "subtree", or "content-type"' )->addOption( 'content-ids', null, InputOption::VALUE_OPTIONAL, - 'Comma-separated list of content ID\'s to refresh (deleted/updated/added). Implies "no-purge", cannot be combined with "since" or "subtree"' + 'Comma-separated list of content ID\'s to refresh (deleted/updated/added). Implies "no-purge", cannot be combined with "since", "subtree", or "content-type"' )->addOption( 'subtree', null, InputOption::VALUE_OPTIONAL, - 'Location ID whose subtree will be indexed (including the Location itself). Implies "no-purge", cannot be combined with "since" or "content-ids"' + 'Location ID whose subtree will be indexed (including the Location itself). Implies "no-purge", cannot be combined with "since", "content-ids", or "content-type"' )->addOption( 'processes', null, InputOption::VALUE_OPTIONAL, 'Number of child processes to run in parallel for iterations, if set to "auto" it will set to number of CPU cores -1, set to "1" or "0" to disable', 'auto' + )->addOption( + 'content-type', + null, + InputOption::VALUE_REQUIRED, + 'Content type identifier to refresh (deleted/updated/added). Implies "no-purge", cannot be combined with "since", "subtree", or "content-ids"' )->setHelp( <<<EOT The command <info>%command.name%</info> indexes the current configured database in the configured search engine index. @@ -248,17 +256,20 @@ protected function indexIncrementally( if ($since = $input->getOption('since')) { $count = $this->gateway->countContentSince(new DateTime($since)); - $generator = $this->gateway->getContentSince(new DateTime($since), $iterationCount); + $batchList = $this->gateway->getContentSince(new DateTime($since), $iterationCount); $purge = false; } elseif ($locationId = (int) $input->getOption('subtree')) { - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Handler */ $location = $this->locationHandler->load($locationId); $count = $this->gateway->countContentInSubtree($location->pathString); - $generator = $this->gateway->getContentInSubtree($location->pathString, $iterationCount); + $batchList = $this->gateway->getContentInSubtree($location->pathString, $iterationCount); $purge = false; + } elseif (!empty($input->getOption('content-type'))) { + $batchList = $this->contentIdListGeneratorStrategy->getBatchList($input, $iterationCount); + $count = $batchList->getCount(); + $purge = $this->contentIdListGeneratorStrategy->shouldPurgeIndex($input); } else { $count = $this->gateway->countAllContent(); - $generator = $this->gateway->getAllContent($iterationCount); + $batchList = $this->gateway->getAllContent($iterationCount); $purge = !$input->getOption('no-purge'); } @@ -294,13 +305,13 @@ protected function indexIncrementally( if ($processCount > 1) { $this->runParallelProcess( $progress, - $generator, + $batchList, (int)$processCount, $commit ); } else { // if we only have one process, or less iterations to warrant running several, we index it all inline - foreach ($generator as $contentIds) { + foreach ($batchList as $contentIds) { $this->searchIndexer->updateSearchIndex($contentIds, $commit); $progress->advance(1); } @@ -317,15 +328,28 @@ protected function indexIncrementally( } /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @param iterable<int, array<int>> $results + * + * @return \Generator<int, array<int>> + */ + private function buildGenerator(iterable $results): \Generator + { + yield from $results; + } + + /** + * @param iterable<int, array<int>> $batchList + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ private function runParallelProcess( ProgressBar $progress, - Generator $generator, + iterable $batchList, int $processCount, bool $commit ): void { - /** @var \Symfony\Component\Process\Process[]|null[] */ + $generator = $this->buildGenerator($batchList); + /** @var \Symfony\Component\Process\Process[]|null[] $processes */ $processes = array_fill(0, $processCount, null); do { /** @var \Symfony\Component\Process\Process $process */ @@ -366,9 +390,9 @@ private function runParallelProcess( } /** - * @param array $contentIds + * @param int[] $contentIds * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ private function getPhpProcess(array $contentIds, bool $commit): Process { @@ -455,3 +479,5 @@ public function getDeprecatedAliases(): array return ['ezplatform:reindex']; } } + +class_alias(ReindexCommand::class, 'eZ\Bundle\EzPublishCoreBundle\Command\ReindexCommand'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Command/ResizeOriginalImagesCommand.php b/src/bundle/Core/Command/ResizeOriginalImagesCommand.php similarity index 83% rename from eZ/Bundle/EzPublishCoreBundle/Command/ResizeOriginalImagesCommand.php rename to src/bundle/Core/Command/ResizeOriginalImagesCommand.php index 78f06f4805..52f5f5bc4d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Command/ResizeOriginalImagesCommand.php +++ b/src/bundle/Core/Command/ResizeOriginalImagesCommand.php @@ -6,19 +6,19 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Command; +namespace Ibexa\Bundle\Core\Command; use Exception; -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\Core\FieldType\Image\Value; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\BinaryFile; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Core\FieldType\Image\Value; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\BinaryFile; use Imagine\Image\ImagineInterface; use Liip\ImagineBundle\Binary\BinaryInterface; use Liip\ImagineBundle\Exception\Imagine\Filter\NonExistingFilterException; @@ -41,25 +41,25 @@ class ResizeOriginalImagesCommand extends Command implements BackwardCompatibleC public const DEFAULT_ITERATION_COUNT = 25; public const DEFAULT_REPOSITORY_USER = 'admin'; - /** @var \eZ\Publish\API\Repository\PermissionResolver */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ private $permissionResolver; - /** @var \eZ\Publish\API\Repository\UserService */ + /** @var \Ibexa\Contracts\Core\Repository\UserService */ private $userService; - /** @var \eZ\Publish\API\Repository\ContentTypeService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */ private $contentTypeService; - /** @var \eZ\Publish\API\Repository\ContentService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentService */ private $contentService; - /** @var \eZ\Publish\API\Repository\SearchService */ + /** @var \Ibexa\Contracts\Core\Repository\SearchService */ private $searchService; /** @var \Liip\ImagineBundle\Imagine\Filter\FilterManager */ private $filterManager; - /** @var \eZ\Publish\Core\IO\IOServiceInterface */ + /** @var \Ibexa\Core\IO\IOServiceInterface */ private $ioService; /** @var \Symfony\Component\Mime\MimeTypesInterface */ @@ -114,7 +114,7 @@ protected function configure() ->addArgument( 'contentTypeIdentifier', InputArgument::REQUIRED, - 'Identifier of a Content Type which has an ezimage Field Type.' + 'Identifier of a content type which has an ezimage Field Type.' ) ->addOption( 'filter', @@ -133,7 +133,7 @@ protected function configure() 'user', 'u', InputOption::VALUE_OPTIONAL, - 'eZ Platform username (with Role containing at least content Policies: read, versionread, edit, publish)', + 'Ibexa username (with Role containing at least content Policies: read, versionread, edit, publish)', self::DEFAULT_REPOSITORY_USER ); } @@ -150,7 +150,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int if (!$fieldType || $fieldType->fieldTypeIdentifier !== 'ezimage') { $output->writeln( sprintf( - "<error>Field Type with identifier '%s' in Content Type '%s' must be 'ezimage', you provided '%s'.</error>", + "<error>Field Type with identifier '%s' in content type '%s' must be 'ezimage', you provided '%s'.</error>", $imageFieldIdentifier, $contentType->identifier, $fieldType ? $fieldType->fieldTypeIdentifier : '' @@ -210,7 +210,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int while ($query->offset <= $totalCount) { $results = $this->searchService->findContent($query); - /** @var \eZ\Publish\API\Repository\Values\Content\Search\SearchHit $hit */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit $hit */ foreach ($results->searchHits as $hit) { $this->resize($output, $hit, $imageFieldIdentifier, $filter); $progressBar->advance(); @@ -233,14 +233,14 @@ protected function execute(InputInterface $input, OutputInterface $output): int /** * @param \Symfony\Component\Console\Output\OutputInterface $output - * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchHit $hit + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit $hit * @param string $imageFieldIdentifier * @param string $filter */ private function resize(OutputInterface $output, SearchHit $hit, string $imageFieldIdentifier, string $filter): void { try { - /** @var \eZ\Publish\Core\FieldType\Image\Value $field */ + /** @var \Ibexa\Core\FieldType\Image\Value $field */ foreach ($hit->valueObject->fields[$imageFieldIdentifier] as $language => $field) { if (null === $field->id) { continue; @@ -285,17 +285,17 @@ private function resize(OutputInterface $output, SearchHit $hit, string $imageFi } /** - * Copy of eZ\Bundle\EzPublishCoreBundle\Imagine\IORepositoryResolver::store() - * Original one cannot be used since original method uses eZ\Bundle\EzPublishCoreBundle\Imagine\IORepositoryResolver::getFilePath() + * Copy of {@see \Ibexa\Bundle\Core\Imagine\IORepositoryResolver::store} + * Original one cannot be used since original method uses {@see \Ibexa\Bundle\Core\Imagine\IORepositoryResolver::getFilePath} * so ends-up with image stored in _aliases instead of overwritten original image. * * @param \Liip\ImagineBundle\Binary\BinaryInterface $binary - * @param \eZ\Publish\Core\FieldType\Image\Value $image + * @param \Ibexa\Core\FieldType\Image\Value $image * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentValue * - * @return \eZ\Publish\Core\IO\Values\BinaryFile + * @return \Ibexa\Core\IO\Values\BinaryFile */ private function store(BinaryInterface $binary, Value $image): BinaryFile { @@ -315,3 +315,5 @@ public function getDeprecatedAliases(): array return ['ezplatform:images:resize-original']; } } + +class_alias(ResizeOriginalImagesCommand::class, 'eZ\Bundle\EzPublishCoreBundle\Command\ResizeOriginalImagesCommand'); diff --git a/src/bundle/Core/Command/SetSystemContentTypeGroupCommand.php b/src/bundle/Core/Command/SetSystemContentTypeGroupCommand.php new file mode 100644 index 0000000000..4e5d901898 --- /dev/null +++ b/src/bundle/Core/Command/SetSystemContentTypeGroupCommand.php @@ -0,0 +1,121 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\Command; + +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\UserService; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; + +/** + * @internal + */ +final class SetSystemContentTypeGroupCommand extends Command +{ + private const DEFAULT_REPOSITORY_USER = 'admin'; + + protected static $defaultName = 'ibexa:content-type-group:set-system'; + + /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */ + private $contentTypeService; + + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ + private $permissionResolver; + + /** @var \Ibexa\Contracts\Core\Repository\UserService */ + private $userService; + + public function __construct( + ContentTypeService $contentTypeService, + PermissionResolver $permissionResolver, + UserService $userService + ) { + parent::__construct(); + + $this->contentTypeService = $contentTypeService; + $this->permissionResolver = $permissionResolver; + $this->userService = $userService; + } + + protected function configure() + { + $this + ->addArgument('content-type-group-identifier', InputArgument::REQUIRED, 'ContentTypGroup identifier') + ->addOption( + 'system', + null, + InputOption::VALUE_NEGATABLE, + 'Sets ContentTypeGroup as a system group.' + ) + ->addOption( + 'user', + 'u', + InputOption::VALUE_OPTIONAL, + 'Ibexa username (with Role containing at least content policies: remove, read, versionread)', + self::DEFAULT_REPOSITORY_USER + ) + ->setDescription('Sets information if ContentTypeGroup is a system group') + ->setHelp( + <<<EOT +The command <info>%command.name%</info> sets `is_system` flag for ContentTypeGroup which determines if ContentTypeGroup is a system group. +EOT + ); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $io = new SymfonyStyle($input, $output); + + if ($input->getOption('system') === null) { + $io->error('Please provide `system` option to determine if ContentTypeGroup should be system group or not.'); + + return self::SUCCESS; + } + + $this->permissionResolver->setCurrentUserReference( + $this->userService->loadUserByLogin($input->getOption('user')) + ); + + $io->title('Sets ContentTypeGroup as a system group or not.'); + $io->writeln([ + 'This setting determines if ContentTypeGroup is visible on the list of ContentTypeGroups.', + ]); + + $identifier = $input->getArgument('content-type-group-identifier'); + try { + $contentTypeGroup = $this->contentTypeService->loadContentTypeGroupByIdentifier($identifier); + } catch (NotFoundException $e) { + $io->error(sprintf('Can\'t find ContentTypeGroup with identifier: %s', $identifier)); + + return self::INVALID; + } + $isSystem = $input->getOption('system'); + $isSystemText = $isSystem ? 'system' : 'no system'; + $io->note(sprintf('ContentTypeGroup with identifier `%s` will be set as a %s group.', $identifier, $isSystemText)); + + if (!$io->confirm('Do you want to continue?')) { + return self::SUCCESS; + } + + $updateStruct = $this->contentTypeService->newContentTypeGroupUpdateStruct(); + $updateStruct->isSystem = $isSystem; + + $this->contentTypeService->updateContentTypeGroup($contentTypeGroup, $updateStruct); + + $io->success(sprintf('Done! ContentTypeGroup is set as a %s group.', $isSystemText)); + + return self::SUCCESS; + } +} diff --git a/eZ/Bundle/EzPublishCoreBundle/Command/UpdateTimestampsToUTCCommand.php b/src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php similarity index 98% rename from eZ/Bundle/EzPublishCoreBundle/Command/UpdateTimestampsToUTCCommand.php rename to src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php index 2cb4328e0e..3a7fc01c08 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Command/UpdateTimestampsToUTCCommand.php +++ b/src/bundle/Core/Command/UpdateTimestampsToUTCCommand.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Command; +namespace Ibexa\Bundle\Core\Command; use DateTime; use DateTimeZone; @@ -121,9 +121,7 @@ protected function configure() The command <info>%command.name%</info> updates field data_int in configured Legacy Storage database for a given Field Type. -This is to be used either when migrating from eZ Publish to eZ Platform -(when using platform backend instead of legacy), or when upgrading legacy -to v2019.03 which has been adapted to use UTC. +This is to be used when upgrading from a legacy version which was not adapted to use UTC. <warning>The database should not be modified while the script is being executed. @@ -511,3 +509,5 @@ public function getDeprecatedAliases(): array return ['ezplatform:timestamps:to-utc']; } } + +class_alias(UpdateTimestampsToUTCCommand::class, 'eZ\Bundle\EzPublishCoreBundle\Command\UpdateTimestampsToUTCCommand'); diff --git a/src/bundle/Core/Controller.php b/src/bundle/Core/Controller.php new file mode 100644 index 0000000000..a890a553e9 --- /dev/null +++ b/src/bundle/Core/Controller.php @@ -0,0 +1,57 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core; + +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Templating\GlobalHelper; +use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; + +class Controller extends AbstractController +{ + public function getRepository(): Repository + { + return $this->container->get('ibexa.api.repository'); + } + + protected function getConfigResolver(): ConfigResolverInterface + { + return $this->container->get('ibexa.config.resolver'); + } + + public function getGlobalHelper(): GlobalHelper + { + return $this->container->get('ibexa.templating.global_helper'); + } + + /** + * Returns the root location object for current siteaccess configuration. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location + */ + public function getRootLocation(): Location + { + return $this->getGlobalHelper()->getRootLocation(); + } + + public static function getSubscribedServices(): array + { + return array_merge( + parent::getSubscribedServices(), + [ + 'ibexa.api.repository' => Repository::class, + 'ibexa.config.resolver' => ConfigResolverInterface::class, + 'ibexa.templating.global_helper' => GlobalHelper::class, + ] + ); + } +} + +class_alias(Controller::class, 'eZ\Bundle\EzPublishCoreBundle\Controller'); diff --git a/src/bundle/Core/ControllerArgumentResolver/LocationArgumentResolver.php b/src/bundle/Core/ControllerArgumentResolver/LocationArgumentResolver.php new file mode 100644 index 0000000000..fe4dd8d226 --- /dev/null +++ b/src/bundle/Core/ControllerArgumentResolver/LocationArgumentResolver.php @@ -0,0 +1,59 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\ControllerArgumentResolver; + +use Ibexa\Contracts\Core\Exception\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; +use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata; + +/** + * @internal + */ +final class LocationArgumentResolver implements ArgumentValueResolverInterface +{ + private LocationService $locationService; + + private const PARAMETER_LOCATION_ID = 'locationId'; + + public function __construct(LocationService $locationService) + { + $this->locationService = $locationService; + } + + public function supports(Request $request, ArgumentMetadata $argument): bool + { + return + Location::class === $argument->getType() + && !$request->attributes->has(self::PARAMETER_LOCATION_ID) + && $request->query->has(self::PARAMETER_LOCATION_ID); + } + + /** + * @return iterable<\Ibexa\Contracts\Core\Repository\Values\Content\Location> + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function resolve(Request $request, ArgumentMetadata $argument): iterable + { + $locationId = $request->query->get(self::PARAMETER_LOCATION_ID); + if (!is_numeric($locationId)) { + throw new InvalidArgumentException( + 'locationId', + 'Expected numeric type, ' . get_debug_type($locationId) . ' given.' + ); + } + + yield $this->locationService->loadLocation((int)$locationId); + } +} diff --git a/src/bundle/Core/Converter/ContentParamConverter.php b/src/bundle/Core/Converter/ContentParamConverter.php new file mode 100644 index 0000000000..3742773eca --- /dev/null +++ b/src/bundle/Core/Converter/ContentParamConverter.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Converter; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; + +class ContentParamConverter extends RepositoryParamConverter +{ + /** @var \Ibexa\Contracts\Core\Repository\ContentService */ + private $contentService; + + public function __construct(ContentService $contentService) + { + $this->contentService = $contentService; + } + + protected function getSupportedClass() + { + return Content::class; + } + + protected function getPropertyName() + { + return 'contentId'; + } + + protected function loadValueObject($id) + { + return $this->contentService->loadContent($id); + } +} + +class_alias(ContentParamConverter::class, 'eZ\Bundle\EzPublishCoreBundle\Converter\ContentParamConverter'); diff --git a/src/bundle/Core/Converter/LocationParamConverter.php b/src/bundle/Core/Converter/LocationParamConverter.php new file mode 100644 index 0000000000..8bcbc9e053 --- /dev/null +++ b/src/bundle/Core/Converter/LocationParamConverter.php @@ -0,0 +1,51 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Converter; + +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Core\Helper\ContentPreviewHelper; + +class LocationParamConverter extends RepositoryParamConverter +{ + /** @var \Ibexa\Contracts\Core\Repository\LocationService */ + private $locationService; + + private ContentPreviewHelper $contentPreviewHelper; + + public function __construct( + LocationService $locationService, + ContentPreviewHelper $contentPreviewHelper + ) { + $this->locationService = $locationService; + $this->contentPreviewHelper = $contentPreviewHelper; + } + + protected function getSupportedClass() + { + return Location::class; + } + + protected function getPropertyName() + { + return 'locationId'; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + protected function loadValueObject($id): Location + { + $prioritizedLanguages = $this->contentPreviewHelper->isPreviewActive() ? Language::ALL : null; + + return $this->locationService->loadLocation($id, $prioritizedLanguages); + } +} + +class_alias(LocationParamConverter::class, 'eZ\Bundle\EzPublishCoreBundle\Converter\LocationParamConverter'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Converter/RepositoryParamConverter.php b/src/bundle/Core/Converter/RepositoryParamConverter.php similarity index 87% rename from eZ/Bundle/EzPublishCoreBundle/Converter/RepositoryParamConverter.php rename to src/bundle/Core/Converter/RepositoryParamConverter.php index ade4739bb4..a18e341bff 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Converter/RepositoryParamConverter.php +++ b/src/bundle/Core/Converter/RepositoryParamConverter.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Converter; +namespace Ibexa\Bundle\Core\Converter; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Sensio\Bundle\FrameworkExtraBundle\Request\ParamConverter\ParamConverterInterface; @@ -25,7 +25,7 @@ abstract protected function getSupportedClass(); abstract protected function getPropertyName(); /** - * @return \eZ\Publish\API\Repository\Values\ValueObject + * @return \Ibexa\Contracts\Core\Repository\Values\ValueObject */ abstract protected function loadValueObject($id); @@ -51,3 +51,5 @@ public function apply(Request $request, ParamConverter $configuration) return true; } } + +class_alias(RepositoryParamConverter::class, 'eZ\Bundle\EzPublishCoreBundle\Converter\RepositoryParamConverter'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/BinaryContentDownloadPass.php b/src/bundle/Core/DependencyInjection/Compiler/BinaryContentDownloadPass.php new file mode 100644 index 0000000000..2ca0180814 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Compiler/BinaryContentDownloadPass.php @@ -0,0 +1,44 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; + +use Ibexa\Core\FieldType\BinaryFile\BinaryFileStorage; +use Ibexa\Core\FieldType\Media\MediaStorage; +use Ibexa\Core\MVC\Symfony\FieldType\BinaryBase\ContentDownloadUrlGenerator; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Injects the downloadUrlGenerator into the binary fieldtype external storage services. + */ +class BinaryContentDownloadPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->has(ContentDownloadUrlGenerator::class)) { + return; + } + + $downloadUrlReference = new Reference(ContentDownloadUrlGenerator::class); + + $this->addCall($container, $downloadUrlReference, MediaStorage::class); + $this->addCall($container, $downloadUrlReference, BinaryFileStorage::class); + } + + private function addCall(ContainerBuilder $container, Reference $reference, $targetServiceName) + { + if (!$container->has($targetServiceName)) { + return; + } + + $definition = $container->findDefinition($targetServiceName); + $definition->addMethodCall('setDownloadUrlGenerator', [$reference]); + } +} + +class_alias(BinaryContentDownloadPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\BinaryContentDownloadPass'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ChainConfigResolverPass.php b/src/bundle/Core/DependencyInjection/Compiler/ChainConfigResolverPass.php similarity index 76% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ChainConfigResolverPass.php rename to src/bundle/Core/DependencyInjection/Compiler/ChainConfigResolverPass.php index a5423686fb..244965652e 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ChainConfigResolverPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/ChainConfigResolverPass.php @@ -4,15 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ChainConfigResolver; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; /** - * The ChainConfigResolverPass will register all services tagged as "ezpublish.config.resolver" to the chain config resolver. + * The ChainConfigResolverPass will register all services tagged as "ibexa.site.config.resolver" + * to the chain config resolver. */ class ChainConfigResolverPass implements CompilerPassInterface { @@ -27,7 +28,7 @@ public function process(ContainerBuilder $container) $chainResolver = $container->getDefinition(ChainConfigResolver::class); - foreach ($container->findTaggedServiceIds('ezpublish.config.resolver') as $id => $attributes) { + foreach ($container->findTaggedServiceIds('ibexa.site.config.resolver') as $id => $attributes) { $priority = isset($attributes[0]['priority']) ? (int)$attributes[0]['priority'] : 0; // Priority range is between -255 (the lowest) and 255 (the highest) if ($priority > 255) { @@ -47,3 +48,5 @@ public function process(ContainerBuilder $container) } } } + +class_alias(ChainConfigResolverPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ChainConfigResolverPass'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ChainRoutingPass.php b/src/bundle/Core/DependencyInjection/Compiler/ChainRoutingPass.php similarity index 77% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ChainRoutingPass.php rename to src/bundle/Core/DependencyInjection/Compiler/ChainRoutingPass.php index ca851491e9..a92d9b9d4e 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ChainRoutingPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/ChainRoutingPass.php @@ -4,8 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; +use Ibexa\Core\MVC\Symfony\Routing\ChainRouter; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; @@ -20,25 +23,25 @@ class ChainRoutingPass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { - if (!$container->hasDefinition('ezpublish.chain_router')) { + if (!$container->hasDefinition(ChainRouter::class)) { return; } - $chainRouter = $container->getDefinition('ezpublish.chain_router'); + $chainRouter = $container->getDefinition(ChainRouter::class); // Enforce default router to be part of the routing chain // The default router will be given the highest priority so that it will be used by default if ($container->hasDefinition('router.default')) { $defaultRouter = $container->getDefinition('router.default'); - $defaultRouter->addMethodCall('setSiteAccess', [new Reference('ezpublish.siteaccess')]); - $defaultRouter->addMethodCall('setConfigResolver', [new Reference('ezpublish.config.resolver')]); + $defaultRouter->addMethodCall('setSiteAccess', [new Reference(SiteAccess::class)]); + $defaultRouter->addMethodCall('setConfigResolver', [new Reference('ibexa.config.resolver')]); $defaultRouter->addMethodCall( 'setNonSiteAccessAwareRoutes', - ['%ezpublish.default_router.non_siteaccess_aware_routes%'] + ['%ibexa.default_router.non_site_access_aware_routes%'] ); $defaultRouter->addMethodCall( 'setSiteAccessRouter', - [new Reference('ezpublish.siteaccess_router')] + [new Reference(Router::class)] ); if (!$defaultRouter->hasTag('router')) { $defaultRouter->addTag( @@ -68,3 +71,5 @@ public function process(ContainerBuilder $container) } } } + +class_alias(ChainRoutingPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ChainRoutingPass'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ConsoleCacheWarmupPass.php b/src/bundle/Core/DependencyInjection/Compiler/ConsoleCacheWarmupPass.php similarity index 88% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ConsoleCacheWarmupPass.php rename to src/bundle/Core/DependencyInjection/Compiler/ConsoleCacheWarmupPass.php index 1b09292637..28c53a6532 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ConsoleCacheWarmupPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/ConsoleCacheWarmupPass.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -46,3 +46,5 @@ public function process(ContainerBuilder $container) $container->getDefinition('cache_warmer')->replaceArgument(0, $warmers); } } + +class_alias(ConsoleCacheWarmupPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ConsoleCacheWarmupPass'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ConsoleCommandPass.php b/src/bundle/Core/DependencyInjection/Compiler/ConsoleCommandPass.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ConsoleCommandPass.php rename to src/bundle/Core/DependencyInjection/Compiler/ConsoleCommandPass.php index 26a91d332b..bc4849a7d8 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/ConsoleCommandPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/ConsoleCommandPass.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -28,3 +28,5 @@ public function process(ContainerBuilder $container) } } } + +class_alias(ConsoleCommandPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ConsoleCommandPass'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPass.php b/src/bundle/Core/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPass.php similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPass.php rename to src/bundle/Core/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPass.php index 6a47d162f4..7699bafacd 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPass.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass; @@ -40,3 +40,5 @@ private function getIbexaEntityManagers(ContainerBuilder $container): array return $entityManagers; } } + +class_alias(EntityManagerFactoryServiceLocatorPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\EntityManagerFactoryServiceLocatorPass'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPass.php b/src/bundle/Core/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPass.php new file mode 100644 index 0000000000..d43ac8fdb3 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPass.php @@ -0,0 +1,62 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; + +use Ibexa\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * This compiler pass will register Ibexa field type parameter providers. + */ +class FieldTypeParameterProviderRegistryPass implements CompilerPassInterface +{ + public const FIELD_TYPE_PARAMETER_PROVIDER_SERVICE_TAG = 'ibexa.field_type.view.parameter.provider'; + + /** + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @throws \LogicException + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition(ParameterProviderRegistry::class)) { + return; + } + + $parameterProviderRegistryDef = $container->getDefinition(ParameterProviderRegistry::class); + + $serviceTags = $container->findTaggedServiceIds( + self::FIELD_TYPE_PARAMETER_PROVIDER_SERVICE_TAG + ); + foreach ($serviceTags as $serviceId => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['alias'])) { + throw new \LogicException( + sprintf( + 'Service "%s" tagged with "%s" service tag needs an "alias" attribute to identify the Field Type.', + $serviceId, + self::FIELD_TYPE_PARAMETER_PROVIDER_SERVICE_TAG + ) + ); + } + + $parameterProviderRegistryDef->addMethodCall( + 'setParameterProvider', + [ + // Only pass the service Id since field types will be lazy loaded via the service container + new Reference($serviceId), + $attribute['alias'], + ] + ); + } + } + } +} + +class_alias(FieldTypeParameterProviderRegistryPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\FieldTypeParameterProviderRegistryPass'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/FragmentPass.php b/src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php similarity index 77% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/FragmentPass.php rename to src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php index 6a4ebb412e..286d5d23b0 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/FragmentPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/FragmentPass.php @@ -4,9 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\Fragment\InlineFragmentRenderer; +use Ibexa\Bundle\Core\Fragment\DecoratedFragmentRenderer; +use Ibexa\Bundle\Core\Fragment\FragmentListenerFactory; +use Ibexa\Bundle\Core\Fragment\InlineFragmentRenderer; use Symfony\Component\DependencyInjection\ChildDefinition; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -23,7 +25,7 @@ public function process(ContainerBuilder $container) if ( !( $container->hasDefinition('fragment.listener') - && $container->hasDefinition('ezpublish.decorated_fragment_renderer') + && $container->hasDefinition(DecoratedFragmentRenderer::class) ) ) { return null; @@ -31,7 +33,7 @@ public function process(ContainerBuilder $container) $fragmentListenerDef = $container->findDefinition('fragment.listener'); $fragmentListenerDef - ->setFactory([new Reference('ezpublish.fragment_listener.factory'), 'buildFragmentListener']) + ->setFactory([new Reference(FragmentListenerFactory::class), 'buildFragmentListener']) ->addArgument(FragmentListener::class); // Looping over all fragment renderers to decorate them @@ -44,7 +46,7 @@ public function process(ContainerBuilder $container) $definition->setPublic(false); $container->setDefinition($renamedId, $definition); - $decoratedDef = new ChildDefinition('ezpublish.decorated_fragment_renderer'); + $decoratedDef = new ChildDefinition(DecoratedFragmentRenderer::class); $decoratedDef->setArguments([new Reference($renamedId)]); $decoratedDef->setPublic($public); $decoratedDef->setTags($tags); @@ -57,3 +59,5 @@ public function process(ContainerBuilder $container) } } } + +class_alias(FragmentPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\FragmentPass'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/ImaginePass.php b/src/bundle/Core/DependencyInjection/Compiler/ImaginePass.php new file mode 100644 index 0000000000..07902794cb --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Compiler/ImaginePass.php @@ -0,0 +1,59 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; + +use Ibexa\Bundle\Core\Imagine\Filter\FilterConfiguration; +use Ibexa\Bundle\Core\Imagine\Filter\Gmagick\ReduceNoiseFilter as GmagickReduceNoiseFilter; +use Ibexa\Bundle\Core\Imagine\Filter\Gmagick\SwirlFilter as GmagickSwirlFilter; +use Ibexa\Bundle\Core\Imagine\Filter\Imagick\ReduceNoiseFilter as ImagickReduceNoiseFilter; +use Ibexa\Bundle\Core\Imagine\Filter\Imagick\SwirlFilter as ImagickSwirlFilter; +use Symfony\Component\DependencyInjection\Alias; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +class ImaginePass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('liip_imagine.filter.configuration')) { + return; + } + + $filterConfigDef = $container->findDefinition('liip_imagine.filter.configuration'); + $filterConfigDef->setClass(FilterConfiguration::class); + $filterConfigDef->addMethodCall('setConfigResolver', [new Reference('ibexa.config.resolver')]); + + if ($container->hasAlias('liip_imagine')) { + $imagineAlias = (string)$container->getAlias('liip_imagine'); + $driver = substr($imagineAlias, strrpos($imagineAlias, '.') + 1); + + $this->processReduceNoiseFilter($container, $driver); + $this->processSwirlFilter($container, $driver); + } + } + + private function processReduceNoiseFilter(ContainerBuilder $container, $driver) + { + if ($driver === 'imagick') { + $container->setAlias('ibexa.image_alias.imagine.filter.reduce_noise', new Alias(ImagickReduceNoiseFilter::class)); + } elseif ($driver === 'gmagick') { + $container->setAlias('ibexa.image_alias.imagine.filter.reduce_noise', new Alias(GmagickReduceNoiseFilter::class)); + } + } + + private function processSwirlFilter(ContainerBuilder $container, $driver) + { + if ($driver === 'imagick') { + $container->setAlias('ibexa.image_alias.imagine.filter.swirl', new Alias(ImagickSwirlFilter::class)); + } elseif ($driver === 'gmagick') { + $container->setAlias('ibexa.image_alias.imagine.filter.swirl', new Alias(GmagickSwirlFilter::class)); + } + } +} + +class_alias(ImaginePass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ImaginePass'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php b/src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php similarity index 96% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php rename to src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php index 6c03a78b6c..8d5f502ecc 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingsPass.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -156,3 +156,5 @@ private function getEntityMapForConfigurationService(array $entityMappings): arr ); } } + +class_alias(InjectEntityManagerMappingsPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\InjectEntityManagerMappingsPass'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/LazyDoctrineRepositoriesPass.php b/src/bundle/Core/DependencyInjection/Compiler/LazyDoctrineRepositoriesPass.php similarity index 89% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/LazyDoctrineRepositoriesPass.php rename to src/bundle/Core/DependencyInjection/Compiler/LazyDoctrineRepositoriesPass.php index 7c6adbe81b..adf5975f4f 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/LazyDoctrineRepositoriesPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/LazyDoctrineRepositoriesPass.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; use RuntimeException; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -55,3 +55,5 @@ public function process(ContainerBuilder $container): void ); } } + +class_alias(LazyDoctrineRepositoriesPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\LazyDoctrineRepositoriesPass'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/NotificationRendererPass.php b/src/bundle/Core/DependencyInjection/Compiler/NotificationRendererPass.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/NotificationRendererPass.php rename to src/bundle/Core/DependencyInjection/Compiler/NotificationRendererPass.php index 4d7cf04afa..f6091c19cd 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/NotificationRendererPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/NotificationRendererPass.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; use LogicException; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; @@ -15,7 +15,7 @@ class NotificationRendererPass implements CompilerPassInterface { - public const TAG_NAME = 'ezpublish.notification.renderer'; + public const TAG_NAME = 'ibexa.notification.renderer'; public const REGISTRY_DEFINITION_ID = 'notification.renderer.registry'; public function process(ContainerBuilder $container) @@ -40,3 +40,5 @@ public function process(ContainerBuilder $container) } } } + +class_alias(NotificationRendererPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\NotificationRendererPass'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/PlaceholderProviderPass.php b/src/bundle/Core/DependencyInjection/Compiler/PlaceholderProviderPass.php similarity index 75% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/PlaceholderProviderPass.php rename to src/bundle/Core/DependencyInjection/Compiler/PlaceholderProviderPass.php index 454f3a5cf6..20fbe8aeab 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/PlaceholderProviderPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/PlaceholderProviderPass.php @@ -4,8 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; +use Ibexa\Bundle\Core\Imagine\PlaceholderProviderRegistry; use LogicException; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -13,8 +14,8 @@ class PlaceholderProviderPass implements CompilerPassInterface { - public const TAG_NAME = 'ezpublish.placeholder_provider'; - public const REGISTRY_DEFINITION_ID = 'ezpublish.image_alias.imagine.placeholder_provider.registry'; + public const TAG_NAME = 'ibexa.media.images.placeholder.provider'; + public const REGISTRY_DEFINITION_ID = PlaceholderProviderRegistry::class; public function process(ContainerBuilder $container) { @@ -37,3 +38,5 @@ public function process(ContainerBuilder $container) } } } + +class_alias(PlaceholderProviderPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\PlaceholderProviderPass'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/QueryTypePass.php b/src/bundle/Core/DependencyInjection/Compiler/QueryTypePass.php new file mode 100644 index 0000000000..61dd867c3c --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Compiler/QueryTypePass.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; + +use Ibexa\Core\QueryType\ArrayQueryTypeRegistry; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Processes services tagged as ibexa.query_type, and registers them with ezpublish.query_type.registry. + */ +final class QueryTypePass implements CompilerPassInterface +{ + public const QUERY_TYPE_SERVICE_TAG = 'ibexa.query_type'; + + public function process(ContainerBuilder $container): void + { + if (!$container->hasDefinition(ArrayQueryTypeRegistry::class)) { + return; + } + + $queryTypes = []; + + $serviceTags = $container->findTaggedServiceIds(self::QUERY_TYPE_SERVICE_TAG); + foreach ($serviceTags as $taggedServiceId => $tags) { + $queryTypeDefinition = $container->getDefinition($taggedServiceId); + $queryTypeClass = $container->getParameterBag()->resolveValue($queryTypeDefinition->getClass()); + + foreach ($tags as $attributes) { + $name = $attributes['alias'] ?? $queryTypeClass::getName(); + $queryTypes[$name] = new Reference($taggedServiceId); + } + } + + $aggregatorDefinition = $container->getDefinition(ArrayQueryTypeRegistry::class); + $aggregatorDefinition->addMethodCall('addQueryTypes', [$queryTypes]); + } +} + +class_alias(QueryTypePass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\QueryTypePass'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/RegisterSearchEngineIndexerPass.php b/src/bundle/Core/DependencyInjection/Compiler/RegisterSearchEngineIndexerPass.php new file mode 100644 index 0000000000..d6a3235ac7 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Compiler/RegisterSearchEngineIndexerPass.php @@ -0,0 +1,72 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; + +use Ibexa\Bundle\Core\ApiLoader\SearchEngineIndexerFactory; +use LogicException; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * This compiler pass registers Ibexa search engines indexers. + */ +class RegisterSearchEngineIndexerPass implements CompilerPassInterface +{ + public const SEARCH_ENGINE_INDEXER_SERVICE_TAG = 'ibexa.search.engine.indexer'; + + /** + * Container service id of the SearchEngineIndexerFactory. + * + * @see \Ibexa\Bundle\Core\ApiLoader\SearchEngineIndexerFactory + * + * @var string + */ + protected $factoryId = SearchEngineIndexerFactory::class; + + /** + * Register all found search engine indexers to the SearchEngineIndexerFactory. + * + * @throws \LogicException + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->factoryId)) { + return; + } + + $searchEngineIndexerFactoryDefinition = $container->getDefinition($this->factoryId); + + $serviceTags = $container->findTaggedServiceIds(self::SEARCH_ENGINE_INDEXER_SERVICE_TAG); + foreach ($serviceTags as $serviceId => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['alias'])) { + throw new LogicException( + sprintf( + 'Service "%s" tagged with "%s" needs an "alias" attribute to identify the search engine', + $serviceId, + self::SEARCH_ENGINE_INDEXER_SERVICE_TAG + ) + ); + } + + // Register the search engine with the search engine factory + $searchEngineIndexerFactoryDefinition->addMethodCall( + 'registerSearchEngineIndexer', + [ + new Reference($serviceId), + $attribute['alias'], + ] + ); + } + } + } +} + +class_alias(RegisterSearchEngineIndexerPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\RegisterSearchEngineIndexerPass'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/RegisterSearchEnginePass.php b/src/bundle/Core/DependencyInjection/Compiler/RegisterSearchEnginePass.php new file mode 100644 index 0000000000..2e3df7eb34 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Compiler/RegisterSearchEnginePass.php @@ -0,0 +1,72 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; + +use Ibexa\Bundle\Core\ApiLoader\SearchEngineFactory; +use LogicException; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * This compiler pass will register Ibexa search engines. + */ +class RegisterSearchEnginePass implements CompilerPassInterface +{ + public const SEARCH_ENGINE_SERVICE_TAG = 'ibexa.search.engine'; + + /** + * Container service id of the SearchEngineFactory. + * + * @see \Ibexa\Bundle\Core\ApiLoader\SearchEngineFactory + * + * @var string + */ + protected $factoryId = SearchEngineFactory::class; + + /** + * Registers all found search engines to the SearchEngineFactory. + * + * @throws \LogicException + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition($this->factoryId)) { + return; + } + + $searchEngineFactoryDefinition = $container->getDefinition($this->factoryId); + + $serviceTags = $container->findTaggedServiceIds(self::SEARCH_ENGINE_SERVICE_TAG); + foreach ($serviceTags as $serviceId => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['alias'])) { + throw new LogicException( + sprintf( + 'Service "%s" tagged with "%s" needs an "alias" attribute to identify the search engine', + $serviceId, + self::SEARCH_ENGINE_SERVICE_TAG + ) + ); + } + + // Register the search engine with the search engine factory + $searchEngineFactoryDefinition->addMethodCall( + 'registerSearchEngine', + [ + new Reference($serviceId), + $attribute['alias'], + ] + ); + } + } + } +} + +class_alias(RegisterSearchEnginePass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\RegisterSearchEnginePass'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/RegisterStorageEnginePass.php b/src/bundle/Core/DependencyInjection/Compiler/RegisterStorageEnginePass.php new file mode 100644 index 0000000000..5daf4d41ae --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Compiler/RegisterStorageEnginePass.php @@ -0,0 +1,65 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; + +use Ibexa\Bundle\Core\ApiLoader\StorageEngineFactory; +use LogicException; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * This compiler pass will register Ibexa storage engines. + */ +class RegisterStorageEnginePass implements CompilerPassInterface +{ + public const STORAGE_ENGINE_TAG = 'ibexa.storage'; + + /** + * Performs compiler passes for persistence factories. + * + * Does: + * - Registers all storage engines to ezpublish.api.storage_engine.factory + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @throws \LogicException + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition(StorageEngineFactory::class)) { + return; + } + + $storageEngineFactoryDef = $container->getDefinition(StorageEngineFactory::class); + foreach ($container->findTaggedServiceIds(self::STORAGE_ENGINE_TAG) as $serviceId => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['alias'])) { + throw new LogicException( + sprintf( + 'Service "%s" tagged with "%s" service tag needs an "alias" ' . + 'attribute to identify the storage engine.', + $serviceId, + self::STORAGE_ENGINE_TAG + ) + ); + } + + // Register the storage engine on the main storage engine factory + $storageEngineFactoryDef->addMethodCall( + 'registerStorageEngine', + [ + new Reference($serviceId), + $attribute['alias'], + ] + ); + } + } + } +} + +class_alias(RegisterStorageEnginePass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\RegisterStorageEnginePass'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/RouterPass.php b/src/bundle/Core/DependencyInjection/Compiler/RouterPass.php similarity index 79% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/RouterPass.php rename to src/bundle/Core/DependencyInjection/Compiler/RouterPass.php index 3fee7f81bc..d18fba635a 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/RouterPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/RouterPass.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\Routing\DefaultRouter; +use Ibexa\Bundle\Core\Routing\DefaultRouter; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -29,3 +29,5 @@ public function process(ContainerBuilder $container) ->setClass(DefaultRouter::class); } } + +class_alias(RouterPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\RouterPass'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/SecurityPass.php b/src/bundle/Core/DependencyInjection/Compiler/SecurityPass.php new file mode 100644 index 0000000000..28aa5b0f6b --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Compiler/SecurityPass.php @@ -0,0 +1,128 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; + +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Core\MVC\Symfony\Security\Authentication\AnonymousAuthenticationProvider; +use Ibexa\Core\MVC\Symfony\Security\Authentication\DefaultAuthenticationSuccessHandler; +use Ibexa\Core\MVC\Symfony\Security\Authentication\GuardRepositoryAuthenticationProvider; +use Ibexa\Core\MVC\Symfony\Security\Authentication\RememberMeRepositoryAuthenticationProvider; +use Ibexa\Core\MVC\Symfony\Security\Authentication\RepositoryAuthenticationProvider; +use Ibexa\Core\MVC\Symfony\Security\HttpUtils; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Security related compiler pass. + * Manipulates Symfony core security services to adapt them to eZ security needs. + */ +class SecurityPass implements CompilerPassInterface +{ + /** + * @deprecated 4.6.7 CONSTANT_AUTH_TIME_SETTING constant is deprecated, will be removed in 5.0. + */ + public const CONSTANT_AUTH_TIME_SETTING = 'ibexa.security.authentication.constant_auth_time'; + + public const CONSTANT_AUTH_TIME_DEFAULT = 1.0; + + public function process(ContainerBuilder $container) + { + if (!($container->hasDefinition('security.authentication.provider.dao') && + $container->hasDefinition('security.authentication.provider.rememberme') && + $container->hasDefinition('security.authentication.provider.guard') && + $container->hasDefinition('security.authentication.provider.anonymous'))) { + return; + } + + $configResolverRef = new Reference('ibexa.config.resolver'); + $permissionResolverRef = new Reference(PermissionResolver::class); + $userServiceRef = new Reference(UserService::class); + $loggerRef = new Reference('logger'); + + // Override and inject the Repository in the authentication provider. + // We need it for checking user credentials + $daoAuthenticationProviderDef = $container->findDefinition('security.authentication.provider.dao'); + $daoAuthenticationProviderDef->setClass(RepositoryAuthenticationProvider::class); + $daoAuthenticationProviderDef->addMethodCall( + 'setPermissionResolver', + [$permissionResolverRef] + ); + $daoAuthenticationProviderDef->addMethodCall( + 'setUserService', + [$userServiceRef] + ); + $daoAuthenticationProviderDef->addMethodCall( + 'setConstantAuthTime', + [ + $container->hasParameter(self::CONSTANT_AUTH_TIME_SETTING) ? + (float)$container->getParameter(self::CONSTANT_AUTH_TIME_SETTING) : + self::CONSTANT_AUTH_TIME_DEFAULT, + ] + ); + $daoAuthenticationProviderDef->addMethodCall( + 'setLogger', + [$loggerRef] + ); + + $rememberMeAuthenticationProviderDef = $container->findDefinition('security.authentication.provider.rememberme'); + $rememberMeAuthenticationProviderDef->setClass(RememberMeRepositoryAuthenticationProvider::class); + $rememberMeAuthenticationProviderDef->addMethodCall( + 'setPermissionResolver', + [$permissionResolverRef] + ); + + $guardAuthenticationProviderDef = $container->findDefinition('security.authentication.provider.guard'); + $guardAuthenticationProviderDef->setClass(GuardRepositoryAuthenticationProvider::class); + $guardAuthenticationProviderDef->addMethodCall( + 'setPermissionResolver', + [$permissionResolverRef] + ); + + $anonymousAuthenticationProviderDef = $container->findDefinition('security.authentication.provider.anonymous'); + $anonymousAuthenticationProviderDef->setClass(AnonymousAuthenticationProvider::class); + $anonymousAuthenticationProviderDef->addMethodCall( + 'setPermissionResolver', + [$permissionResolverRef] + ); + + $anonymousAuthenticationProviderDef->addMethodCall( + 'setConfigResolver', + [$configResolverRef] + ); + + if (!$container->hasDefinition('security.http_utils')) { + return; + } + + $httpUtilsDef = $container->findDefinition('security.http_utils'); + $httpUtilsDef->setClass(HttpUtils::class); + $httpUtilsDef->addMethodCall( + 'setSiteAccess', + [new Reference(SiteAccess::class)] + ); + + if (!$container->hasDefinition('security.authentication.success_handler')) { + return; + } + + $successHandlerDef = $container->getDefinition('security.authentication.success_handler'); + $successHandlerDef->setClass(DefaultAuthenticationSuccessHandler::class); + $successHandlerDef->addMethodCall( + 'setConfigResolver', + [$configResolverRef] + ); + $successHandlerDef->addMethodCall( + 'setEventDispatcher', + [new Reference('event_dispatcher')] + ); + } +} + +class_alias(SecurityPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\SecurityPass'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/SessionConfigurationPass.php b/src/bundle/Core/DependencyInjection/Compiler/SessionConfigurationPass.php new file mode 100644 index 0000000000..6fde946cac --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Compiler/SessionConfigurationPass.php @@ -0,0 +1,88 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Configures session handler based on `ibexa.session.handler_id` + * and `ibexa.session.save_path`. + * + * This ensures parameters have the highest priority and the configuration + * will be respected with default framework.yaml file. + * + * @internal + */ +final class SessionConfigurationPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $handlerId = $container->hasParameter('ibexa.session.handler_id') + ? $container->getParameter('ibexa.session.handler_id') + : null; + + $savePath = $container->hasParameter('ibexa.session.save_path') + ? $container->getParameter('ibexa.session.save_path') + : null; + + if (null !== $handlerId) { + $usedEnvs = []; + $container->resolveEnvPlaceholders($handlerId, null, $usedEnvs); + + // code below follows FrameworkExtension from symfony/framework-bundle + if ($usedEnvs || preg_match('#^[a-z]++://#', $handlerId)) { + $id = '.cache_connection.' . ContainerBuilder::hash($handlerId); + + $container->getDefinition('session.abstract_handler') + ->replaceArgument( + 0, + $container->has($id) + ? new Reference($id) + : $handlerId + ); + + $container->setAlias('session.handler', 'session.abstract_handler'); + } else { + $container->setAlias('session.handler', $handlerId); + } + + if ($container->hasDefinition('session.storage.native')) { + $container + ->getDefinition('session.storage.native') + ->replaceArgument(1, new Reference('session.handler')); + } + + if ($container->hasDefinition('session.storage.php_bridge')) { + $container + ->getDefinition('session.storage.php_bridge') + ->replaceArgument(0, new Reference('session.handler')); + } + + if ($container->hasDefinition('session.storage.factory.native')) { + $container + ->getDefinition('session.storage.factory.native') + ->replaceArgument(1, new Reference('session.handler')); + } + + if ($container->hasDefinition('session.storage.factory.php_bridge')) { + $container + ->getDefinition('session.storage.factory.php_bridge') + ->replaceArgument(0, new Reference('session.handler')); + } + } + + if (null !== $savePath) { + $container->setParameter('session.save_path', $savePath); + } + } +} + +class_alias(SessionConfigurationPass::class, 'EzSystems\EzPlatformCoreBundle\DependencyInjection\Compiler'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/SiteAccessMatcherRegistryPass.php b/src/bundle/Core/DependencyInjection/Compiler/SiteAccessMatcherRegistryPass.php similarity index 76% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/SiteAccessMatcherRegistryPass.php rename to src/bundle/Core/DependencyInjection/Compiler/SiteAccessMatcherRegistryPass.php index 57108a362d..102e4e2fab 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/SiteAccessMatcherRegistryPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/SiteAccessMatcherRegistryPass.php @@ -6,19 +6,19 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessMatcherRegistry; +use Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistry; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; /** - * The SiteAccessMatcherRegistryPass will register all services tagged as "ezplatform.siteaccess.matcher" to the registry. + * The SiteAccessMatcherRegistryPass will register all services tagged as "ibexa.site_access.matcher" to the registry. */ final class SiteAccessMatcherRegistryPass implements CompilerPassInterface { - public const MATCHER_TAG = 'ezplatform.siteaccess.matcher'; + public const MATCHER_TAG = 'ibexa.site_access.matcher'; /** * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container @@ -42,3 +42,5 @@ public function process(ContainerBuilder $container): void } } } + +class_alias(SiteAccessMatcherRegistryPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\SiteAccessMatcherRegistryPass'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/SlugConverterConfigurationPass.php b/src/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPass.php similarity index 83% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/SlugConverterConfigurationPass.php rename to src/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPass.php index e8b6b66a44..de299def82 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Compiler/SlugConverterConfigurationPass.php +++ b/src/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPass.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -21,13 +21,13 @@ class SlugConverterConfigurationPass implements CompilerPassInterface */ public function process(ContainerBuilder $container) { - if (!$container->has('ezpublish.persistence.slug_converter')) { + if (!$container->has(\Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter::class)) { return; } - $slugConverterDefinition = $container->getDefinition('ezpublish.persistence.slug_converter'); + $slugConverterDefinition = $container->getDefinition(\Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter::class); $parameterConfiguration = $slugConverterDefinition->getArgument(1); - $semanticConfiguration = $container->getParameter('ezpublish.url_alias.slug_converter'); + $semanticConfiguration = $container->getParameter('ibexa.url_alias.slug_converter'); $mergedConfiguration = $parameterConfiguration; @@ -75,3 +75,5 @@ public function process(ContainerBuilder $container) $slugConverterDefinition->setArgument(1, $mergedConfiguration); } } + +class_alias(SlugConverterConfigurationPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\SlugConverterConfigurationPass'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/StorageConnectionPass.php b/src/bundle/Core/DependencyInjection/Compiler/StorageConnectionPass.php new file mode 100644 index 0000000000..58f655155d --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Compiler/StorageConnectionPass.php @@ -0,0 +1,48 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; + +use LogicException; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * This compiler pass will create aliases for storage engine database handler connections + * to the storage connection factory. + */ +class StorageConnectionPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container) + { + $taggedServiceIds = $container->findTaggedServiceIds( + RegisterStorageEnginePass::STORAGE_ENGINE_TAG + ); + foreach ($taggedServiceIds as $serviceId => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['alias'])) { + throw new LogicException( + sprintf( + 'Service "%s" tagged with "%s" service tag needs an "alias" ' . + 'attribute to identify the storage engine.', + $serviceId, + RegisterStorageEnginePass::STORAGE_ENGINE_TAG + ) + ); + } + + $alias = $attribute['alias']; + + $container->setAlias( + "ezpublish.api.storage_engine.{$alias}.connection", + 'ibexa.persistence.connection' + ); + } + } + } +} + +class_alias(StorageConnectionPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\StorageConnectionPass'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPass.php b/src/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPass.php new file mode 100644 index 0000000000..5274e55c11 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPass.php @@ -0,0 +1,62 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; + +use Ibexa\Bundle\Core\Translation\GlobCollector; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * This compilation pass loads every ezplatform available translations into symfony translator. + */ +class TranslationCollectorPass implements CompilerPassInterface +{ + public const ORIGINAL_TRANSLATION = 'en'; + + /** @var array */ + public const LOCALES_MAP = [ + 'de_DE' => 'de', + 'el_GR' => 'el', + 'es_ES' => 'es', + 'fi_FI' => 'fi', + 'fr_FR' => 'fr', + 'hi_IN' => 'hi', + 'hr_HR' => 'hr', + 'hu_HU' => 'hu', + 'it_IT' => 'it', + 'ja_JP' => 'ja', + 'nb_NO' => 'nb', + 'pl_PL' => 'pl', + 'pt_PT' => 'pt', + 'ru_RU' => 'ru', + ]; + + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition('translator.default')) { + return; + } + + $collector = new GlobCollector($container->getParameterBag()->get('kernel.project_dir')); + + $availableTranslations = [self::ORIGINAL_TRANSLATION]; + + if ($container->getParameter('ibexa.ui.translations.enabled')) { + foreach ($collector->collect() as $file) { + /* TODO - to remove when translation files will have proper names. */ + if (isset(self::LOCALES_MAP[$file['locale']])) { + $file['locale'] = self::LOCALES_MAP[$file['locale']]; + } + $availableTranslations[] = $file['locale']; + } + } + + $container->setParameter('available_translations', array_values(array_unique($availableTranslations))); + } +} + +class_alias(TranslationCollectorPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\TranslationCollectorPass'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/URLHandlerPass.php b/src/bundle/Core/DependencyInjection/Compiler/URLHandlerPass.php new file mode 100644 index 0000000000..5e54a9cd27 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Compiler/URLHandlerPass.php @@ -0,0 +1,48 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; + +use Ibexa\Bundle\Core\URLChecker\URLHandlerRegistry; +use LogicException; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * This compiler pass will register URL handlers. + */ +class URLHandlerPass implements CompilerPassInterface +{ + /** + * {@inheritdoc} + */ + public function process(ContainerBuilder $container) + { + if (!$container->hasDefinition(URLHandlerRegistry::class)) { + return; + } + + $definition = $container->findDefinition(URLHandlerRegistry::class); + foreach ($container->findTaggedServiceIds('ibexa.url_checker.handler') as $id => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['scheme'])) { + throw new LogicException(sprintf( + '%s service tag needs a "scheme" attribute to identify which scheme is supported by the handler.', + 'ibexa.url_checker.handler' + )); + } + + $definition->addMethodCall('addHandler', [ + $attribute['scheme'], + new Reference($id), + ]); + } + } + } +} + +class_alias(URLHandlerPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\URLHandlerPass'); diff --git a/src/bundle/Core/DependencyInjection/Compiler/ViewProvidersPass.php b/src/bundle/Core/DependencyInjection/Compiler/ViewProvidersPass.php new file mode 100644 index 0000000000..99a9c8b557 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Compiler/ViewProvidersPass.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Compiler; + +use Ibexa\Bundle\Core\EventListener\ConfigScopeListener; +use Ibexa\Core\MVC\Symfony\View\CustomLocationControllerChecker; +use Ibexa\Core\MVC\Symfony\View\Provider\Registry; +use LogicException; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +/** + * Registers services tagged as "ibexa.view.provider" into the view_provider registry. + */ +class ViewProvidersPass implements CompilerPassInterface +{ + private const VIEW_PROVIDER_TAG = 'ibexa.view.provider'; + + /** + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + public function process(ContainerBuilder $container) + { + $rawViewProviders = []; + foreach ($container->findTaggedServiceIds(self::VIEW_PROVIDER_TAG) as $serviceId => $tags) { + foreach ($tags as $attributes) { + // Priority range is between -255 (the lowest) and 255 (the highest) + $priority = isset($attributes['priority']) ? max(min((int)$attributes['priority'], 255), -255) : 0; + + if (!isset($attributes['type'])) { + throw new LogicException( + sprintf( + 'Service "%s" tagged with "%s" service tag needs a "type" attribute', + $serviceId, + self::VIEW_PROVIDER_TAG + ) + ); + } + $type = $attributes['type']; + + $rawViewProviders[$type][$priority][] = new Reference($serviceId); + } + } + + $viewProviders = []; + foreach ($rawViewProviders as $type => $viewProvidersPerPriority) { + krsort($viewProvidersPerPriority); + foreach ($viewProvidersPerPriority as $priorityViewProviders) { + if (!isset($viewProviders[$type])) { + $viewProviders[$type] = []; + } + $viewProviders[$type] = array_merge($viewProviders[$type], $priorityViewProviders); + } + } + + if ($container->hasDefinition(Registry::class)) { + $container->getDefinition(Registry::class)->addMethodCall( + 'setViewProviders', + [$viewProviders] + ); + } + + $flattenedViewProviders = []; + foreach ($viewProviders as $type => $typeViewProviders) { + foreach ($typeViewProviders as $typeViewProvider) { + $flattenedViewProviders[] = $typeViewProvider; + } + } + + if ($container->hasDefinition(ConfigScopeListener::class)) { + $container->getDefinition(ConfigScopeListener::class)->addMethodCall( + 'setViewProviders', + [$flattenedViewProviders] + ); + } + + // 5.4.5 BC service after location view deprecation + if ($container->hasDefinition(CustomLocationControllerChecker::class)) { + $container->getDefinition(CustomLocationControllerChecker::class)->addMethodCall( + 'addViewProviders', + [$viewProviders['Ibexa\Core\MVC\Symfony\View\ContentView']] + ); + } + } +} + +class_alias(ViewProvidersPass::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ViewProvidersPass'); diff --git a/src/bundle/Core/DependencyInjection/Configuration.php b/src/bundle/Core/DependencyInjection/Configuration.php new file mode 100644 index 0000000000..4b859f673b --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration.php @@ -0,0 +1,568 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ParserInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\RepositoryConfigParserInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\Configuration as SiteAccessConfiguration; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface; +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; + +class Configuration extends SiteAccessConfiguration +{ + public const CUSTOM_TAG_ATTRIBUTE_TYPES = ['number', 'string', 'boolean', 'choice']; + + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\ParserInterface */ + private $mainSiteAccessConfigParser; + + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\RepositoryConfigParserInterface */ + private $mainRepositoryConfigParser; + + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface */ + private $suggestionCollector; + + /** @var \Ibexa\Bundle\Core\SiteAccess\SiteAccessConfigurationFilter[] */ + private $siteAccessConfigurationFilters; + + public function __construct( + ParserInterface $mainConfigParser, + RepositoryConfigParserInterface $mainRepositoryConfigParser, + SuggestionCollectorInterface $suggestionCollector + ) { + $this->mainSiteAccessConfigParser = $mainConfigParser; + $this->mainRepositoryConfigParser = $mainRepositoryConfigParser; + $this->suggestionCollector = $suggestionCollector; + } + + public function setSiteAccessConfigurationFilters(array $filters) + { + $this->siteAccessConfigurationFilters = $filters; + } + + /** + * Generates the configuration tree builder. + * + * @return \Symfony\Component\Config\Definition\Builder\TreeBuilder The tree builder + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder('ibexa'); + + $rootNode = $treeBuilder->getRootNode(); + + $this->addRepositoriesSection($rootNode); + $this->addSiteaccessSection($rootNode); + $this->addImageMagickSection($rootNode); + $this->addHttpCacheSection($rootNode); + $this->addRouterSection($rootNode); + $this->addUrlAliasSection($rootNode); + $this->addImagePlaceholderSection($rootNode); + $this->addUrlWildcardsSection($rootNode); + $this->addOrmSection($rootNode); + $this->addUITranslationsSection($rootNode); + + // Delegate SiteAccess config to configuration parsers + $this->mainSiteAccessConfigParser->addSemanticConfig($this->generateScopeBaseNode($rootNode)); + + return $treeBuilder; + } + + public function addRepositoriesSection(ArrayNodeDefinition $rootNode) + { + $repositoriesNode = $rootNode + ->children() + ->arrayNode('repositories') + ->info('Content repositories configuration') + ->example( + [ + 'main' => [ + 'storage' => [ + 'engine' => 'legacy', + 'connection' => 'my_doctrine_connection_name', + ], + ], + ] + ) + ->useAttributeAsKey('alias') + ->prototype('array') + ->beforeNormalization() + ->always( + // Handling deprecated structure by mapping it to new one + static function ($v) { + if (isset($v['storage'])) { + return $v; + } + + if (isset($v['engine'])) { + $v['storage']['engine'] = $v['engine']; + unset($v['engine']); + } + + if (isset($v['connection'])) { + $v['storage']['connection'] = $v['connection']; + unset($v['connection']); + } + + if (isset($v['config'])) { + $v['storage']['config'] = $v['config']; + unset($v['config']); + } + + return $v; + } + ) + ->end() + ->beforeNormalization() + ->always( + // Setting default values + static function ($v) { + if ($v === null) { + $v = []; + } + + if (!isset($v['storage'])) { + $v['storage'] = []; + } + + if (!isset($v['search'])) { + $v['search'] = []; + } + + if (!isset($v['fields_groups']['list'])) { + $v['fields_groups']['list'] = []; + } + + if (!isset($v['options'])) { + $v['options'] = []; + } + + return $v; + } + ) + ->end() + ->children(); + + $this->mainRepositoryConfigParser->addSemanticConfig( + $repositoriesNode + ); + } + + public function addSiteaccessSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('siteaccess') + ->info('SiteAccess configuration') + ->children() + ->arrayNode('list') + ->info('Available SiteAccess list') + ->example(['ibexa_demo_site', 'ibexa_demo_site_admin']) + ->isRequired() + ->requiresAtLeastOneElement() + ->prototype('scalar')->end() + ->end() + ->arrayNode('groups') + ->useAttributeAsKey('key') + ->info('SiteAccess groups. Useful to share settings between Siteaccess') + ->example(['ibexa_demo_group' => ['ibexa_demo_site', 'ibexa_demo_site_admin']]) + ->prototype('array') + ->prototype('scalar')->end() + ->end() + ->end() + ->scalarNode('default_siteaccess')->isRequired()->info('Name of the default siteaccess')->end() + ->arrayNode('match') + ->info('Siteaccess match configuration. First key is the matcher class, value is passed to the matcher. Key can be a service identifier (prepended by "@"), or a FQ class name (prepended by "\\")') + ->example( + [ + 'Map\\URI' => [ + 'foo' => 'ibexa_demo_site', + 'ibexa_demo_site' => 'ibexa_demo_site', + 'ibexa_demo_site_admin' => 'ibexa_demo_site_admin', + ], + 'Map\\Host' => [ + 'ezpublish.dev' => 'ibexa_demo_site', + 'admin.ezpublish.dev' => 'ibexa_demo_site_admin', + ], + '\\My\\Custom\\Matcher' => [ + 'some' => 'configuration', + ], + '@my.custom.matcher' => [ + 'some' => 'other_configuration', + ], + ] + ) + ->isRequired() + ->useAttributeAsKey('key') + ->normalizeKeys(false) + ->prototype('array') + ->useAttributeAsKey('key') + ->beforeNormalization() + ->always( + static function ($v) { + // Value passed to the matcher should always be an array. + // If value is not an array, we transform it to a hash, with 'value' as key. + if (!is_array($v)) { + return ['value' => $v]; + } + + // If passed value is a numerically indexed array, we must convert it into a hash. + // See https://issues.ibexa.co/browse/EZP-21876 + if (array_keys($v) === range(0, count($v) - 1)) { + $final = []; + foreach ($v as $i => $val) { + $final["i$i"] = $val; + } + + return $final; + } + + return $v; + } + ) + ->end() + ->normalizeKeys(false) + ->prototype('variable')->end() + ->end() + ->end() + ->end() + ->beforeNormalization() + ->always()->then(function ($v) { + if (isset($this->siteAccessConfigurationFilters)) { + foreach ($this->siteAccessConfigurationFilters as $filter) { + $v = $filter->filter($v); + } + } + + return $v; + }) + ->end() + ->end() + ->arrayNode('locale_conversion') + ->info('Locale conversion map between Ibexa format (i.e. fre-FR) to POSIX (i.e. fr_FR). The key is the Ibexa locale. Check locale.yml in IbexaCoreBundle to see natively supported locales.') + ->example(['fre-FR' => 'fr_FR']) + ->useAttributeAsKey('key') + ->normalizeKeys(false) + ->prototype('scalar')->end() + ->end() + ->end(); + } + + private function addImageMagickSection(ArrayNodeDefinition $rootNode) + { + $filtersInfo = +<<<EOT +DEPRECATED. +This is only used for legacy injection. +You may use imagick/gmagick liip_imagine bundle drivers. + +Hash of filters to be used for your image variations config. +# Key is the filter name, value is an argument passed to "convert" binary. +# You can use numbered placeholders (aka input variables) that will be replaced by defined parameters in your image variations config +EOT; + + $rootNode + ->children() + ->arrayNode('imagemagick') + ->info('ImageMagick configuration') + ->children() + ->booleanNode('enabled')->defaultTrue()->end() + ->scalarNode('path') + ->info('Absolute path of ImageMagick / GraphicsMagick "convert" binary.') + ->beforeNormalization() + ->ifTrue( + static function ($v) { + $basename = basename($v); + // If there is a space in the basename, just drop it and everything after it. + if (($wsPos = strpos($basename, ' ')) !== false) { + $basename = substr($basename, 0, $wsPos); + } + + return !is_executable(dirname($v) . \DIRECTORY_SEPARATOR . $basename); + } + ) + ->thenInvalid('Please provide full path to ImageMagick / GraphicsMagick "convert" binary. Please also check that it is executable.') + ->end() + ->end() + ->arrayNode('filters') + ->info($filtersInfo) + ->example(['geometry/scaledownonly' => '"-geometry {1}x{2}>"']) + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end(); + } + + private function addHttpCacheSection(ArrayNodeDefinition $rootNode) + { + $purgeTypeInfo = <<<EOT +Http cache purge type. + +Cache purge for content/locations is triggered when needed (e.g. on publish) and will result in one or several Http PURGE requests. +Can be "local", "http" or a valid symfony service id: +- If "local" is used, an Http PURGE request will be emulated when needed (e.g. when using Symfony internal reverse proxy). +- If "http" is used, a full HTTP PURGE/BAN is done to a real reverse proxy (Varnish, ..) depending on your config +- If custom symfony service id is used, then check documentation on that service for how it behaves and how you need to configure your system for it. + +If ezplatform-http-cache package is enabled (default as of 1.12 and up), then go to documentation on this package for further +info on how it supports multiple response tagging, purges and allow plugins for custom purge types. + +If that is not enabled, then the (deprecated as of 1.8) default BAN based system will be used instead. +Where ressponses can be tagged by a single X-Location-Id header, and for purges a single Http BAN request will be sent, +where X-Location-Id header consists of a Regexp containing locationIds to ban. + BAN Examples: + - (123|456|789) => Purge locations #123, #456, #789. + - .* => Purge all locations. +EOT; + + $rootNode + ->children() + ->arrayNode('http_cache') + ->children() + ->scalarNode('purge_type') + ->info($purgeTypeInfo) + ->defaultValue('local') + ->beforeNormalization() + ->ifTrue( + static function ($v) { + $http = ['multiple_http' => true, 'single_http' => true]; + + return isset($http[$v]); + } + ) + ->then( + static function () { + return 'http'; + } + ) + ->end() + ->end() + ->scalarNode('timeout')->info('DEPRECATED')->end() + ->end() + ->end() + ->end(); + } + + private function addRouterSection(ArrayNodeDefinition $rootNode) + { + $nonSAAwareInfo = <<<EOT +Route names that are not supposed to be SiteAccess aware, i.e. Routes pointing to asset generation (like assetic). +Note that you can just specify a prefix to match a selection of routes. +e.g. "_assetic_" will match "_assetic_*" +Defaults to ['_assetic_', '_wdt', '_profiler', '_configurator_'] +EOT; + $rootNode + ->children() + ->arrayNode('router') + ->children() + ->arrayNode('default_router') + ->children() + ->arrayNode('non_siteaccess_aware_routes') + ->prototype('scalar')->end() + ->info($nonSAAwareInfo) + ->example(['my_route_name', 'some_prefix_']) + ->end() + ->end() + ->end() + ->end() + ->info('Router related settings') + ->end() + ->end(); + } + + /** + * Defines configuration the images placeholder generation. + * + * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $rootNode + */ + private function addImagePlaceholderSection(ArrayNodeDefinition $rootNode) + { + $rootNode + ->children() + ->arrayNode('image_placeholder') + ->info('Configuration for strategy of replacing missing images') + ->useAttributeAsKey('name') + ->arrayPrototype() + ->children() + ->scalarNode('provider') + ->end() + ->variableNode('options') + ->defaultValue([]) + ->end() + ->booleanNode('verify_binary_data_availability') + ->info('Enable additional binary data availability check for source image. Will cause additional IO operation.') + ->defaultFalse() + ->end() + ->end() + ->end() + ->end() + ->end(); + } + + /** + * Define Url Alias Slug converter Semantic Configuration. + * + * The configuration is available at: + * <code> + * ibexa: + * url_alias: + * slug_converter: + * transformation: name_of_transformation_group_to_use + * separator: name_of_separator_to_use + * transformation_groups: + * transformation_group_name: name of existing or new transformation group + * commands : [] array of commands which will be added to group + * cleanup_method: name_of_cleanup_method + * </code> + * + * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $rootNode + * + * @return \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition + */ + private function addUrlAliasSection(ArrayNodeDefinition $rootNode) + { + return $rootNode + ->children() + ->arrayNode('url_alias') + ->children() + ->arrayNode('slug_converter') + ->children() + ->scalarNode('transformation')->end() + ->scalarNode('separator')->end() + ->arrayNode('transformation_groups') + ->arrayPrototype() + ->children() + ->arrayNode('commands') + ->scalarPrototype()->end() + ->end() + ->scalarNode('cleanup_method')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end(); + } + + /** + * Defines configuration for Url Wildcards. + * + * The configuration is available at: + * <code> + * ibexa: + * url_wildcards: + * enabled: true + * </code> + * + * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $rootNode + * + * @return \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition + */ + private function addUrlWildcardsSection($rootNode): ArrayNodeDefinition + { + return $rootNode + ->children() + ->arrayNode('url_wildcards') + ->children() + ->booleanNode('enabled') + ->info('Enable UrlWildcards support') + ->defaultFalse() + ->end() + ->end() + ->end() + ->end(); + } + + /** + * Defines configuration for Doctrine ORM. + * + * The configuration is available at: + * <code> + * ibexa: + * orm: + * entity_mappings: + * IbexaCoreBundle: + * is_bundle: true + * type: annotation + * dir: Entity + * prefix: Ibexa\Bundle\Core\Entity + * + * </code> + * + * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $rootNode + * + * @return \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition + */ + private function addOrmSection($rootNode): ArrayNodeDefinition + { + return $rootNode + ->children() + ->arrayNode('orm') + ->children() + ->arrayNode('entity_mappings') + ->info('Entity Mapping configuration compatible with Doctrine ORM bundle') + ->useAttributeAsKey('name') + ->defaultValue([]) + ->arrayPrototype() + ->children() + ->booleanNode('is_bundle')->defaultTrue()->end() + ->booleanNode('mapping')->defaultTrue()->end() + ->scalarNode('type') + ->isRequired() + ->end() + ->scalarNode('dir') + ->isRequired() + ->end() + ->scalarNode('prefix') + ->isRequired() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->end(); + } + + /** + * Defines configuration for UI Translations. + * + * The configuration is available at: + * <code> + * ibexa: + * ui: + * translations: + * enabled: true + * + * </code> + * + * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $rootNode + */ + private function addUITranslationsSection($rootNode): ArrayNodeDefinition + { + return $rootNode + ->children() + ->arrayNode('ui') + ->children() + ->arrayNode('translations') + ->children() + ->booleanNode('enabled') + ->defaultFalse() + ->info('When enabled UI will be translated based on translations from i18n package') + ->end() + ->end() + ->end() + ->end() + ->end() + ->end(); + } +} + +class_alias(Configuration::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/AbstractParser.php b/src/bundle/Core/DependencyInjection/Configuration/AbstractParser.php new file mode 100644 index 0000000000..33d0210a1e --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/AbstractParser.php @@ -0,0 +1,46 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; + +abstract class AbstractParser implements ParserInterface +{ + /** + * This method is called by the ConfigurationProcessor before looping over available scopes. + * You may here use $contextualizer->mapConfigArray(). + * + * @see ConfigurationProcessor::mapConfig() + * @see \Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface::mapConfigArray() + * + * @param array $config Complete parsed semantic configuration + * @param \Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface $contextualizer + * + * @return mixed + */ + public function preMap(array $config, ContextualizerInterface $contextualizer) + { + } + + /** + * This method is called by the ConfigurationProcessor after looping over available scopes. + * You may here use $contextualizer->mapConfigArray(). + * + * @see ConfigurationProcessor::mapConfig() + * @see \Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface::mapConfigArray() + * + * @param array $config Complete parsed semantic configuration + * @param \Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface $contextualizer + * + * @return mixed + */ + public function postMap(array $config, ContextualizerInterface $contextualizer) + { + } +} + +class_alias(AbstractParser::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\AbstractParser'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ChainConfigResolver.php b/src/bundle/Core/DependencyInjection/Configuration/ChainConfigResolver.php similarity index 80% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ChainConfigResolver.php rename to src/bundle/Core/DependencyInjection/Configuration/ChainConfigResolver.php index 6e68a750b4..fe6175e4b0 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ChainConfigResolver.php +++ b/src/bundle/Core/DependencyInjection/Configuration/ChainConfigResolver.php @@ -4,24 +4,24 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Exception\ParameterNotFoundException; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Exception\ParameterNotFoundException; class ChainConfigResolver implements ConfigResolverInterface { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface[] */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface[] */ protected $resolvers = []; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface[] */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface[] */ protected $sortedResolvers; /** * Registers $mapper as a valid mapper to be used in the configuration mapping chain. * When this mapper will be called in the chain depends on $priority. The highest $priority is, the earliest the router will be called. * - * @param \eZ\Publish\Core\MVC\ConfigResolverInterface $resolver + * @param \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface $resolver * @param int $priority */ public function addResolver(ConfigResolverInterface $resolver, $priority = 0) @@ -36,7 +36,7 @@ public function addResolver(ConfigResolverInterface $resolver, $priority = 0) } /** - * @return \eZ\Publish\Core\MVC\ConfigResolverInterface[] + * @return \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface[] */ public function getAllResolvers() { @@ -51,7 +51,7 @@ public function getAllResolvers() * Sort the registered mappers by priority. * The highest priority number is the highest priority (reverse sorting). * - * @return \eZ\Publish\Core\MVC\ConfigResolverInterface[] + * @return \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface[] */ protected function sortResolvers() { @@ -68,7 +68,7 @@ protected function sortResolvers() /** * @return mixed * - * @throws \eZ\Publish\Core\MVC\Exception\ParameterNotFoundException + * @throws \Ibexa\Core\MVC\Exception\ParameterNotFoundException */ public function getParameter(string $paramName, ?string $namespace = null, ?string $scope = null) { @@ -113,3 +113,5 @@ public function getDefaultNamespace(): string throw new \LogicException('getDefaultNamespace() is not supported by the ChainConfigResolver'); } } + +class_alias(ChainConfigResolver::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ChainConfigResolver'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParser.php b/src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParser.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParser.php rename to src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParser.php index d96856a6f2..e58d33375a 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParser.php +++ b/src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParser.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ComplexSettings; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\DynamicSettingParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\DynamicSettingParser; class ComplexSettingParser extends DynamicSettingParser implements ComplexSettingParserInterface { @@ -69,3 +69,5 @@ public function parseComplexSetting($string) return $this->matchDynamicSettings($string); } } + +class_alias(ComplexSettingParser::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParser'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserInterface.php b/src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserInterface.php new file mode 100644 index 0000000000..93425374c0 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserInterface.php @@ -0,0 +1,37 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\DynamicSettingParserInterface; + +/** + * Parses a string that contains dynamic settings ($foo;eng;bar$). + * + * Example: "$var_dir$/$storage_dir$" + */ +interface ComplexSettingParserInterface extends DynamicSettingParserInterface +{ + /** + * Tests if $string contains dynamic settings. + * + * @param string $string + * + * @return bool + */ + public function containsDynamicSettings($string); + + /** + * Parses dynamic settings. + * + * @param string $string + * + * @return array key: original string, value: dynamic settings + */ + public function parseComplexSetting($string); +} + +class_alias(ComplexSettingParserInterface::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParserInterface'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolver.php b/src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolver.php similarity index 88% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolver.php rename to src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolver.php index 3cfd90cbfb..2a3b3f57c1 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolver.php +++ b/src/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolver.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ComplexSettings; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings; /** * Factory for complex dynamic settings resolution. @@ -56,3 +56,5 @@ public function resolveSetting($argumentString) return $value; } } + +class_alias(ComplexSettingValueResolver::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ComplexSettings\ComplexSettingValueResolver'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigBuilderInterface.php b/src/bundle/Core/DependencyInjection/Configuration/ConfigBuilderInterface.php similarity index 79% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigBuilderInterface.php rename to src/bundle/Core/DependencyInjection/Configuration/ConfigBuilderInterface.php index 40babc6b9e..01231b0ec3 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigBuilderInterface.php +++ b/src/bundle/Core/DependencyInjection/Configuration/ConfigBuilderInterface.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration; use Symfony\Component\Config\Resource\ResourceInterface; @@ -28,3 +28,5 @@ public function addConfig(array $config); */ public function addResource(ResourceInterface $resource); } + +class_alias(ConfigBuilderInterface::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigBuilderInterface'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigParser.php b/src/bundle/Core/DependencyInjection/Configuration/ConfigParser.php similarity index 77% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigParser.php rename to src/bundle/Core/DependencyInjection/Configuration/ConfigParser.php index 964c0a62d7..d963217279 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigParser.php +++ b/src/bundle/Core/DependencyInjection/Configuration/ConfigParser.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; use Symfony\Component\Config\Definition\Builder\NodeBuilder; /** @@ -16,7 +16,7 @@ */ class ConfigParser implements ParserInterface { - /** @var \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ParserInterface[] */ + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\ParserInterface[] */ private $configParsers; public function __construct(array $configParsers = []) @@ -25,7 +25,7 @@ public function __construct(array $configParsers = []) if (!$parser instanceof ParserInterface) { throw new InvalidArgumentType( 'Inner config parser', - 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ParserInterface', + ParserInterface::class, $parser ); } @@ -35,7 +35,7 @@ public function __construct(array $configParsers = []) } /** - * @param \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ParserInterface[] $configParsers + * @param \Ibexa\Bundle\Core\DependencyInjection\Configuration\ParserInterface[] $configParsers */ public function setConfigParsers($configParsers) { @@ -43,7 +43,7 @@ public function setConfigParsers($configParsers) } /** - * @return \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ParserInterface[] + * @return \Ibexa\Bundle\Core\DependencyInjection\Configuration\ParserInterface[] */ public function getConfigParsers() { @@ -87,3 +87,5 @@ public function addSemanticConfig(NodeBuilder $nodeBuilder) } } } + +class_alias(ConfigParser::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigParser'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver.php b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php similarity index 94% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver.php rename to src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php index 044daaa0ec..09e6c64263 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver.php +++ b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration; -use eZ\Publish\Core\MVC\Exception\ParameterNotFoundException; -use eZ\Publish\Core\MVC\Symfony\Configuration\VersatileScopeInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessAware; +use Ibexa\Core\MVC\Exception\ParameterNotFoundException; +use Ibexa\Core\MVC\Symfony\Configuration\VersatileScopeInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Symfony\Component\DependencyInjection\ContainerAwareInterface; @@ -23,7 +23,7 @@ * It will check the different scopes available for a given namespace to find the appropriate parameter. * To work, the dynamic setting must comply internally to the following name format : "<namespace>.<scope>.parameter.name". * - * - <namespace> is the namespace for your dynamic setting. Defaults to "ezsettings", but can be anything. + * - <namespace> is the namespace for your dynamic setting. Defaults to "ibexa.site_access.config", but can be anything. * - <scope> is basically the siteaccess name you want your parameter value to apply to. * Can also be "global" for a global override. * Another scope is used internally: "default". This is the generic fallback. @@ -43,7 +43,7 @@ class ConfigResolver implements VersatileScopeInterface, SiteAccessAware, Contai public const UNDEFINED_STRATEGY_EXCEPTION = 1; public const UNDEFINED_STRATEGY_NULL = 2; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ protected $siteAccess; /** @var \Psr\Log\LoggerInterface */ @@ -144,7 +144,7 @@ public function hasParameter(string $paramName, ?string $namespace = null, ?stri /** * @return mixed * - * @throws \eZ\Publish\Core\MVC\Exception\ParameterNotFoundException + * @throws \Ibexa\Core\MVC\Exception\ParameterNotFoundException */ public function getParameter(string $paramName, ?string $namespace = null, ?string $scope = null) { @@ -273,8 +273,8 @@ private function logTooEarlyLoadedListIfNeeded($paramName) $serviceName = '??'; $firstService = '??'; $commandName = null; - $resettableServiceIds = $container->getParameter('ezpublish.config_resolver.resettable_services'); - $updatableServices = $container->getParameter('ezpublish.config_resolver.updateable_services'); + $resettableServiceIds = $container->getParameter('ibexa.config_resolver.resettable_services'); + $updatableServices = $container->getParameter('ibexa.config_resolver.updateable_services'); // Lookup trace to find last service being loaded as possible blame for eager loading // Abort if one of the earlier services is detected to be "safe", aka updatable @@ -333,3 +333,5 @@ private function logTooEarlyLoadedListIfNeeded($paramName) $this->tooEarlyLoadedList[$blame][] = $paramName; } } + +class_alias(ConfigResolver::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/ContainerConfigResolver.php b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ContainerConfigResolver.php similarity index 87% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/ContainerConfigResolver.php rename to src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ContainerConfigResolver.php index c0a35909ef..2018b5119b 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/ContainerConfigResolver.php +++ b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ContainerConfigResolver.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Exception\ParameterNotFoundException; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Exception\ParameterNotFoundException; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; @@ -70,3 +70,5 @@ private function getScopeRelativeParamName(string $paramName, string $namespace, return "$namespace.$scope.$paramName"; } } + +class_alias(ContainerConfigResolver::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver\ContainerConfigResolver'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/DefaultScopeConfigResolver.php b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/DefaultScopeConfigResolver.php similarity index 78% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/DefaultScopeConfigResolver.php rename to src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/DefaultScopeConfigResolver.php index 45dd404be7..328f69687d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/DefaultScopeConfigResolver.php +++ b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/DefaultScopeConfigResolver.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver; /** * @internal @@ -30,3 +30,5 @@ public function getParameter(string $paramName, ?string $namespace = null, ?stri return parent::getParameter($paramName, $namespace, self::SCOPE_NAME); } } + +class_alias(DefaultScopeConfigResolver::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver\DefaultScopeConfigResolver'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/GlobalScopeConfigResolver.php b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/GlobalScopeConfigResolver.php similarity index 78% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/GlobalScopeConfigResolver.php rename to src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/GlobalScopeConfigResolver.php index 9d9c1890d5..d0ed41a515 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/GlobalScopeConfigResolver.php +++ b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/GlobalScopeConfigResolver.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver; /** * @internal @@ -30,3 +30,5 @@ public function getParameter(string $paramName, ?string $namespace = null, ?stri return parent::getParameter($paramName, $namespace, self::SCOPE_NAME); } } + +class_alias(GlobalScopeConfigResolver::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver\GlobalScopeConfigResolver'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/SiteAccessConfigResolver.php b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessConfigResolver.php similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/SiteAccessConfigResolver.php rename to src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessConfigResolver.php index ee0a9c303a..b1bae07ffa 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/SiteAccessConfigResolver.php +++ b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessConfigResolver.php @@ -6,19 +6,19 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver; -use eZ\Publish\Core\MVC\Exception\ParameterNotFoundException; -use eZ\Publish\Core\MVC\Symfony\Configuration\VersatileScopeInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessAware; +use Ibexa\Core\MVC\Exception\ParameterNotFoundException; +use Ibexa\Core\MVC\Symfony\Configuration\VersatileScopeInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware; abstract class SiteAccessConfigResolver implements VersatileScopeInterface, SiteAccessAware { - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface */ protected $siteAccessProvider; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ protected $currentSiteAccess; /** @var string */ @@ -123,3 +123,5 @@ abstract protected function resolverHasParameter(SiteAccess $siteAccess, string abstract protected function getParameterFromResolver(SiteAccess $siteAccess, string $paramName, string $namespace); } + +class_alias(SiteAccessConfigResolver::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver\SiteAccessConfigResolver'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolver.php b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolver.php similarity index 90% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolver.php rename to src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolver.php index 11b7d54721..f763914a19 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolver.php +++ b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolver.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver; -use eZ\Publish\Core\MVC\Exception\ParameterNotFoundException; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccessGroup; +use Ibexa\Core\MVC\Exception\ParameterNotFoundException; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccessGroup; use Symfony\Component\DependencyInjection\ContainerAwareTrait; /** @@ -107,3 +107,5 @@ private function isSiteAccessGroupScope($scope): bool return array_key_exists($scope, $this->siteAccessGroups); } } + +class_alias(SiteAccessGroupConfigResolver::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver\SiteAccessGroupConfigResolver'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolver.php b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolver.php similarity index 77% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolver.php rename to src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolver.php index ee4d7e25fe..ea39515a54 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolver.php +++ b/src/bundle/Core/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolver.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver; -use eZ\Publish\Core\MVC\Exception\ParameterNotFoundException; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Provider\StaticSiteAccessProvider; +use Ibexa\Core\MVC\Exception\ParameterNotFoundException; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Provider\StaticSiteAccessProvider; use Symfony\Component\DependencyInjection\ContainerAwareTrait; /** @@ -44,3 +44,5 @@ protected function isSiteAccessSupported(SiteAccess $siteAccess): bool return StaticSiteAccessProvider::class === $siteAccess->provider; } } + +class_alias(StaticSiteAccessConfigResolver::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver\StaticSiteAccessConfigResolver'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/ContainerConfigBuilder.php b/src/bundle/Core/DependencyInjection/Configuration/ContainerConfigBuilder.php new file mode 100644 index 0000000000..6c0d0e7b90 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/ContainerConfigBuilder.php @@ -0,0 +1,22 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration; + +use Symfony\Component\DependencyInjection\ContainerBuilder; + +abstract class ContainerConfigBuilder implements ConfigBuilderInterface +{ + /** @var \Symfony\Component\DependencyInjection\ContainerBuilder */ + protected $containerBuilder; + + public function __construct(ContainerBuilder $containerBuilder) + { + $this->containerBuilder = $containerBuilder; + } +} + +class_alias(ContainerConfigBuilder::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ContainerConfigBuilder'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/FieldTypeParserInterface.php b/src/bundle/Core/DependencyInjection/Configuration/FieldTypeParserInterface.php similarity index 80% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/FieldTypeParserInterface.php rename to src/bundle/Core/DependencyInjection/Configuration/FieldTypeParserInterface.php index 98d41aa2a6..6c9df96c8d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/FieldTypeParserInterface.php +++ b/src/bundle/Core/DependencyInjection/Configuration/FieldTypeParserInterface.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration; use Symfony\Component\Config\Definition\Builder\NodeBuilder; @@ -25,3 +25,5 @@ public function getFieldTypeIdentifier(); */ public function addFieldTypeSemanticConfig(NodeBuilder $nodeBuilder); } + +class_alias(FieldTypeParserInterface::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\FieldTypeParserInterface'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/AbstractFieldTypeParser.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/AbstractFieldTypeParser.php new file mode 100644 index 0000000000..c7d80ed798 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/AbstractFieldTypeParser.php @@ -0,0 +1,32 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\AbstractParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\FieldTypeParserInterface; +use Symfony\Component\Config\Definition\Builder\NodeBuilder; + +/** + * Abstract parser class that field type parsers need to extend in order + * to receive NodeBuilder at Node just under ezpublish.<system>.<siteaccess>.fieldtypes.<identifier>. + */ +abstract class AbstractFieldTypeParser extends AbstractParser implements FieldTypeParserInterface +{ + /** + * Adds semantic configuration definition. + * + * @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $nodeBuilder Node just under ezpublish.<system>.<siteaccess> + */ + public function addSemanticConfig(NodeBuilder $nodeBuilder) + { + $fieldTypeNodeBuilder = $nodeBuilder->arrayNode($this->getFieldTypeIdentifier())->children(); + + $this->addFieldTypeSemanticConfig($fieldTypeNodeBuilder); + } +} + +class_alias(AbstractFieldTypeParser::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\AbstractFieldTypeParser'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Common.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php similarity index 88% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Common.php rename to src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php index 6a393bf16b..803c2a5ba7 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Common.php +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/Common.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\AbstractParser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorAwareInterface; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\ConfigSuggestion; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\AbstractParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorAwareInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\ConfigSuggestion; use Symfony\Component\Config\Definition\Builder\NodeBuilder; /** @@ -18,22 +18,22 @@ */ class Common extends AbstractParser implements SuggestionCollectorAwareInterface { - /** @var \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface */ + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface */ private $suggestionCollector; /** * Adds semantic configuration definition. * - * @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $nodeBuilder Node just under ezpublish.system.<siteaccess> + * @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $nodeBuilder Node just under ibexa.system.<siteaccess> */ public function addSemanticConfig(NodeBuilder $nodeBuilder) { $nodeBuilder - ->scalarNode('repository')->info('The repository to use. Choose among ezpublish.repositories.')->end() + ->scalarNode('repository')->info('The repository to use. Choose among ibexa.repositories.')->end() // @deprecated - // Use ezpublish.repositories / repository settings instead. + // Use ibexa.repositories / repository settings instead. ->arrayNode('database') - ->info('DEPRECATED. Use ezpublish.repositories / repository settings instead.') + ->info('DEPRECATED. Use ibexa.repositories / repository settings instead.') ->children() ->enumNode('type')->values(['mysql', 'pgsql', 'sqlite'])->info('The database driver. Can be mysql, pgsql or sqlite.')->end() ->scalarNode('server')->end() @@ -58,7 +58,7 @@ public function addSemanticConfig(NodeBuilder $nodeBuilder) ->end() ->scalarNode('var_dir') ->cannotBeEmpty() - ->example('var/ezdemo_site') + ->example('var/ibexa_demo_site') ->info('The directory relative to web/ where files are stored. Default value is "var"') ->end() ->arrayNode('api_keys') @@ -118,7 +118,7 @@ public function addSemanticConfig(NodeBuilder $nodeBuilder) ->info('Settings related to Http cache') ->children() ->arrayNode('purge_servers') - ->info('Servers to use for Http PURGE (will NOT be used if ezpublish.http_cache.purge_type is "local").') + ->info('Servers to use for Http PURGE (will NOT be used if ibexa.http_cache.purge_type is "local").') ->example(['http://localhost/', 'http://another.server/']) ->requiresAtLeastOneElement() ->prototype('scalar')->end() @@ -137,7 +137,7 @@ public function addSemanticConfig(NodeBuilder $nodeBuilder) ->example('pagelayout.html.twig') ->end() ->scalarNode('login_template') - ->info('Template to use for login form. Defaults to EzPublishCoreBundle:security:login.html.twig') + ->info('Template to use for login form. Defaults to @IbexaCore/Security/login.html.twig') ->example('login.html.twig') ->end() ->end() @@ -177,7 +177,7 @@ public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerIn // session_name setting is deprecated in favor of session.name $container = $contextualizer->getContainer(); - $sessionOptions = $container->hasParameter("ezsettings.$currentScope.session") ? $container->getParameter("ezsettings.$currentScope.session") : []; + $sessionOptions = $container->hasParameter("ibexa.site_access.config.$currentScope.session") ? $container->getParameter("ibexa.site_access.config.$currentScope.session") : []; if (isset($sessionOptions['name'])) { $contextualizer->setContextualParameter('session_name', $currentScope, $sessionOptions['name']); } @@ -221,7 +221,7 @@ public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerIn /** * Injects SuggestionCollector. * - * @param \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface $suggestionCollector + * @param \Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface $suggestionCollector */ public function setSuggestionCollector(SuggestionCollectorInterface $suggestionCollector) { @@ -232,11 +232,11 @@ private function addDatabaseConfigSuggestion($sa, array $databaseConfig) { $suggestion = new ConfigSuggestion( <<<EOT -Database configuration has changed for eZ Content repository. +Database configuration has changed for Ibexa Content repository. Please define: - - An entry in ezpublish.repositories + - An entry in ibexa.repositories - A Doctrine connection (You may check configuration reference for Doctrine "config:dump-reference doctrine" console command.) - - A reference to configured repository in ezpublish.system.$sa.repository + - A reference to configured repository in ibexa.system.$sa.repository EOT ); $suggestion->setMandatory(true); @@ -278,7 +278,7 @@ private function addDatabaseConfigSuggestion($sa, array $databaseConfig) ], ], ], - 'ezpublish' => [ + 'ibexa' => [ 'repositories' => [ 'my_repository' => [ 'storage' => [ @@ -303,3 +303,5 @@ private function addDatabaseConfigSuggestion($sa, array $databaseConfig) $this->suggestionCollector->addSuggestion($suggestion); } } + +class_alias(Common::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\Common'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/Content.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/Content.php new file mode 100644 index 0000000000..64ee5b8773 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/Content.php @@ -0,0 +1,83 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\AbstractParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Symfony\Component\Config\Definition\Builder\NodeBuilder; + +/** + * Configuration parser handling content related config. + */ +class Content extends AbstractParser +{ + /** + * Adds semantic configuration definition. + * + * @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $nodeBuilder Node just under ezpublish.system.<siteaccess> + */ + public function addSemanticConfig(NodeBuilder $nodeBuilder) + { + $nodeBuilder + ->arrayNode('content') + ->info('Content related configuration') + ->children() + ->booleanNode('view_cache')->end() + ->booleanNode('ttl_cache')->end() + ->scalarNode('default_ttl')->info('Default value for TTL cache, in seconds')->end() + ->arrayNode('tree_root') + ->canBeUnset() + ->children() + ->integerNode('location_id') + ->info("Root locationId for routing and link generation.\nUseful for multisite apps with one repository.") + ->isRequired() + ->end() + ->arrayNode('excluded_uri_prefixes') + ->info("URI prefixes that are allowed to be outside the content tree\n(useful for content sharing between multiple sites).\nPrefixes are not case sensitive") + ->example(['/media/images', '/products']) + ->prototype('scalar')->end() + ->end() + ->end() + ->end() + ->end() + ->end(); + } + + public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerInterface $contextualizer) + { + if (!empty($scopeSettings['content'])) { + if (isset($scopeSettings['content']['view_cache'])) { + $contextualizer->setContextualParameter('content.view_cache', $currentScope, $scopeSettings['content']['view_cache']); + } + + if (isset($scopeSettings['content']['ttl_cache'])) { + $contextualizer->setContextualParameter('content.ttl_cache', $currentScope, $scopeSettings['content']['ttl_cache']); + } + + if (isset($scopeSettings['content']['default_ttl'])) { + $contextualizer->setContextualParameter('content.default_ttl', $currentScope, $scopeSettings['content']['default_ttl']); + } + + if (isset($scopeSettings['content']['tree_root'])) { + $contextualizer->setContextualParameter( + 'content.tree_root.location_id', + $currentScope, + $scopeSettings['content']['tree_root']['location_id'] + ); + if (isset($scopeSettings['content']['tree_root']['excluded_uri_prefixes'])) { + $contextualizer->setContextualParameter( + 'content.tree_root.excluded_uri_prefixes', + $currentScope, + $scopeSettings['content']['tree_root']['excluded_uri_prefixes'] + ); + } + } + } + } +} + +class_alias(Content::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\Content'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/ContentView.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/ContentView.php new file mode 100644 index 0000000000..365ee5164c --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/ContentView.php @@ -0,0 +1,15 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; + +class ContentView extends View +{ + public const NODE_KEY = 'content_view'; + public const INFO = 'Template selection settings when displaying a content'; +} + +class_alias(ContentView::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\ContentView'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldDefinitionEditTemplates.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldDefinitionEditTemplates.php new file mode 100644 index 0000000000..103e502e20 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldDefinitionEditTemplates.php @@ -0,0 +1,16 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; + +class FieldDefinitionEditTemplates extends Templates +{ + public const NODE_KEY = 'fielddefinition_edit_templates'; + public const INFO = 'Settings for field definition templates'; + public const INFO_TEMPLATE_KEY = 'Template file where to find block definition to display field definition settings'; +} + +class_alias(FieldDefinitionEditTemplates::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\FieldDefinitionEditTemplates'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldDefinitionSettingsTemplates.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldDefinitionSettingsTemplates.php new file mode 100644 index 0000000000..e8d5932a82 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldDefinitionSettingsTemplates.php @@ -0,0 +1,16 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; + +class FieldDefinitionSettingsTemplates extends Templates +{ + public const NODE_KEY = 'fielddefinition_settings_templates'; + public const INFO = 'Template settings for field definition settings rendered by the ibexa_render_field_definition_settings() Twig function'; + public const INFO_TEMPLATE_KEY = 'Template file where to find block definition to display field definition settings'; +} + +class_alias(FieldDefinitionSettingsTemplates::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\FieldDefinitionSettingsTemplates'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldEditTemplates.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldEditTemplates.php new file mode 100644 index 0000000000..1b82c4dc94 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldEditTemplates.php @@ -0,0 +1,16 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; + +class FieldEditTemplates extends Templates +{ + public const NODE_KEY = 'field_edit_templates'; + public const INFO = 'Settings for field edit templates'; + public const INFO_TEMPLATE_KEY = 'Template file where to find block definition to display fields'; +} + +class_alias(FieldEditTemplates::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\FieldEditTemplates'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldTemplates.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldTemplates.php new file mode 100644 index 0000000000..8c796d684b --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldTemplates.php @@ -0,0 +1,16 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; + +class FieldTemplates extends Templates +{ + public const NODE_KEY = 'field_templates'; + public const INFO = 'Template settings for fields rendered by the ibexa_render_field() Twig function'; + public const INFO_TEMPLATE_KEY = 'Template file where to find block definition to display fields'; +} + +class_alias(FieldTemplates::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\FieldTemplates'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/FieldType/ImageAsset.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldType/ImageAsset.php similarity index 78% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/FieldType/ImageAsset.php rename to src/bundle/Core/DependencyInjection/Configuration/Parser/FieldType/ImageAsset.php index 05e5cca93b..c6782f86f6 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/FieldType/ImageAsset.php +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/FieldType/ImageAsset.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\FieldType; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser\FieldType; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\AbstractFieldTypeParser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; -use eZ\Publish\Core\FieldType\ImageAsset\Type as ImageAssetFieldType; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser\AbstractFieldTypeParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Core\FieldType\ImageAsset\Type as ImageAssetFieldType; use Symfony\Component\Config\Definition\Builder\NodeBuilder; class ImageAsset extends AbstractFieldTypeParser @@ -63,3 +63,5 @@ public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerIn } } } + +class_alias(ImageAsset::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\FieldType\ImageAsset'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/IO.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/IO.php rename to src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php index 29bcc9bdf8..1717c667a5 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/IO.php +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/IO.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\AbstractParser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParserInterface; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\AbstractParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParserInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; use Symfony\Component\Config\Definition\Builder\NodeBuilder; use Symfony\Component\DependencyInjection\ContainerBuilder; class IO extends AbstractParser { - /** @var \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParserInterface */ + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParserInterface */ private $complexSettingParser; public function __construct(ComplexSettingParserInterface $complexSettingParser) @@ -102,10 +102,10 @@ public function postMap(array $config, ContextualizerInterface $contextualizer) private function addComplexParametersDependencies($parameter, $scope, ContainerBuilder $container) { // The complex setting exists in this scope, we don't need to do anything - if ($container->hasParameter("ezsettings.$scope.$parameter")) { + if ($container->hasParameter("ibexa.site_access.config.$scope.$parameter")) { return; } - $parameterValue = $container->getParameter("ezsettings.default.$parameter"); + $parameterValue = $container->getParameter("ibexa.site_access.config.default.$parameter"); // not complex in this scope if (!$this->complexSettingParser->containsDynamicSettings($parameterValue)) { @@ -121,14 +121,16 @@ private function addComplexParametersDependencies($parameter, $scope, ContainerB } $dynamicParameterId = sprintf( '%s.%s.%s', - $dynamicParameterParts['namespace'] ?: 'ezsettings', + $dynamicParameterParts['namespace'] ?: 'ibexa.site_access.config', $scope, $dynamicParameterParts['param'] ); if ($container->hasParameter($dynamicParameterId)) { - $container->setParameter("ezsettings.$scope.$parameter", $parameterValue); + $container->setParameter("ibexa.site_access.config.$scope.$parameter", $parameterValue); break; } } } } + +class_alias(IO::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\IO'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/Image.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/Image.php new file mode 100644 index 0000000000..26972e3829 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/Image.php @@ -0,0 +1,143 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\AbstractParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorAwareInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\ConfigSuggestion; +use Symfony\Component\Config\Definition\Builder\NodeBuilder; + +/** + * Configuration parser handling all basic configuration (aka "Image"). + */ +class Image extends AbstractParser implements SuggestionCollectorAwareInterface +{ + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface */ + private $suggestionCollector; + + public function setSuggestionCollector(SuggestionCollectorInterface $suggestionCollector) + { + $this->suggestionCollector = $suggestionCollector; + } + + /** + * Adds semantic configuration definition. + * + * @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $nodeBuilder Node just under ezpublish.system.<siteaccess> + */ + public function addSemanticConfig(NodeBuilder $nodeBuilder) + { + $nodeBuilder + ->arrayNode('imagemagick') + ->info('DEPRECATED.') + ->children() + ->scalarNode('pre_parameters')->info('Parameters that must be run BEFORE the filenames and filters')->end() + ->scalarNode('post_parameters')->info('Parameters that must be run AFTER the filenames and filters')->end() + ->end() + ->end() + ->arrayNode('image_variations') + ->info('Configuration for your image variations (aka "image aliases")') + ->example( + [ + 'my_image_variation' => [ + 'reference' => '~', + 'filters' => [ + [ + 'name' => 'geometry/scaledownonly', + 'params' => [400, 350], + ], + ], + ], + 'my_cropped_variation' => [ + 'reference' => 'my_image_variation', + 'filters' => [ + [ + 'name' => 'geometry/scalewidthdownonly', + 'params' => [300], + ], + [ + 'name' => 'geometry/crop', + 'params' => [300, 300, 0, 0], + ], + ], + ], + ] + ) + ->useAttributeAsKey('variation_name') + ->normalizeKeys(false) + ->prototype('array') + ->children() + ->scalarNode('reference') + ->info('Tells the system which original variation to use as reference image. Defaults to original') + ->example('large') + ->end() + ->arrayNode('filters') + ->info('A list of filters to run, each filter must be supported by the active image converters') + ->useAttributeAsKey('name') + ->normalizeKeys(false) + ->prototype('array') + ->info('Array/Hash of parameters to pass to the filter') + ->useAttributeAsKey('options') + ->beforeNormalization() + ->ifTrue( + static function ($v) { + // Check if passed array only contains a "params" key (BC with <=5.3). + return is_array($v) && count($v) === 1 && isset($v['params']); + } + ) + ->then( + static function ($v) { + // If we have the "params" key, just use the value. + return $v['params']; + } + ) + ->end() + ->prototype('variable')->end() + ->end() + ->end() + ->arrayNode('post_processors') + ->info('Post processors as defined in LiipImagineBundle. See https://github.com/liip/LiipImagineBundle/blob/master/Resources/doc/filters.md#post-processors') + ->useAttributeAsKey('name') + ->prototype('array') + ->useAttributeAsKey('name') + ->prototype('variable')->end() + ->end() + ->end() + ->end() + ->end() + ->end() + ->scalarNode('variation_handler_identifier') + ->info('Variation handler to be used. Defaults to built-in alias variations.') + ->example('alias') + ->end() + ->scalarNode('image_host') + ->info('Images host. All system images URLs are prefixed with given host if configured.') + ->example('https://ezplatform.com') + ->end(); + } + + public function preMap(array $config, ContextualizerInterface $contextualizer) + { + $contextualizer->mapConfigArray('image_variations', $config); + $contextualizer->mapSetting('image_host', $config); + $contextualizer->mapSetting('variation_handler_identifier', $config); + } + + public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerInterface $contextualizer) + { + if (isset($scopeSettings['imagemagick'])) { + $suggestion = new ConfigSuggestion( + '"imagemagick" settings are deprecated. Just remove them from your configuration file.' + ); + $this->suggestionCollector->addSuggestion($suggestion); + } + } +} + +class_alias(Image::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\Image'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Languages.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/Languages.php similarity index 75% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Languages.php rename to src/bundle/Core/DependencyInjection/Configuration/Parser/Languages.php index 8268c1213c..7d87316b26 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Languages.php +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/Languages.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\AbstractParser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\AbstractParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; use Symfony\Component\Config\Definition\Builder\NodeBuilder; class Languages extends AbstractParser @@ -41,16 +41,16 @@ public function preMap(array $config, ContextualizerInterface $contextualizer) $contextualizer->mapConfigArray('translation_siteaccesses', $config, ContextualizerInterface::UNIQUE); $container = $contextualizer->getContainer(); - if ($container->hasParameter('ezpublish.siteaccesses_by_language')) { - $this->siteAccessesByLanguages = $container->getParameter('ezpublish.siteaccesses_by_language'); + if ($container->hasParameter('ibexa.site_access.by_language')) { + $this->siteAccessesByLanguages = $container->getParameter('ibexa.site_access.by_language'); } } public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerInterface $contextualizer) { $container = $contextualizer->getContainer(); - if ($container->hasParameter("ezsettings.$currentScope.languages")) { - $languages = $container->getParameter("ezsettings.$currentScope.languages"); + if ($container->hasParameter("ibexa.site_access.config.$currentScope.languages")) { + $languages = $container->getParameter("ibexa.site_access.config.$currentScope.languages"); $mainLanguage = array_shift($languages); if ($mainLanguage) { $this->siteAccessesByLanguages[$mainLanguage][] = $currentScope; @@ -61,8 +61,10 @@ public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerIn public function postMap(array $config, ContextualizerInterface $contextualizer) { $contextualizer->getContainer()->setParameter( - 'ezpublish.siteaccesses_by_language', + 'ibexa.site_access.by_language', $this->siteAccessesByLanguages ); } } + +class_alias(Languages::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\Languages'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/LocationView.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/LocationView.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/LocationView.php rename to src/bundle/Core/DependencyInjection/Configuration/Parser/LocationView.php index c9487326a0..6d8a1ea37d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/LocationView.php +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/LocationView.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; class LocationView extends View { @@ -50,3 +50,5 @@ public function preMap(array $config, ContextualizerInterface $contextualizer) $contextualizer->mapConfigArray(ContentView::NODE_KEY, $config, ContextualizerInterface::MERGE_FROM_SECOND_LEVEL); } } + +class_alias(LocationView::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\LocationView'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/FieldGroups.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/FieldGroups.php new file mode 100644 index 0000000000..0780b4caf8 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/FieldGroups.php @@ -0,0 +1,32 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser\Repository; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\RepositoryConfigParserInterface; +use Symfony\Component\Config\Definition\Builder\NodeBuilder; + +final class FieldGroups implements RepositoryConfigParserInterface +{ + public function addSemanticConfig(NodeBuilder $nodeBuilder): void + { + $nodeBuilder + ->arrayNode('fields_groups') + ->info('Definitions of fields groups.') + ->children() + ->arrayNode('list') + ->prototype('scalar') + ->end() + ->end() + ->scalarNode('default') + ->defaultValue('%ibexa.site_access.config.default.content.field_groups.default%') + ->end() + ->end() + ->end(); + } +} diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/Options.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/Options.php new file mode 100644 index 0000000000..19d26ace5b --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/Options.php @@ -0,0 +1,33 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser\Repository; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\RepositoryConfigParserInterface; +use Symfony\Component\Config\Definition\Builder\NodeBuilder; + +final class Options implements RepositoryConfigParserInterface +{ + public function addSemanticConfig(NodeBuilder $nodeBuilder): void + { + $nodeBuilder + ->arrayNode('options') + ->info('Options for repository.') + ->children() + ->scalarNode('default_version_archive_limit') + ->defaultValue(5) + ->info('Default version archive limit (0-50), only enforced on publish, not on un-publish.') + ->end() + ->booleanNode('remove_archived_versions_on_publish') + ->defaultTrue() + ->info('Enables automatic removal of archived versions when publishing, at the cost of performance. "ezplatform:content:cleanup-versions" command should be used to perform this task instead if this option is set to false.') + ->end() + ->end() + ->end(); + } +} diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/Search.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/Search.php new file mode 100644 index 0000000000..169a84f849 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/Search.php @@ -0,0 +1,37 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser\Repository; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\RepositoryConfigParserInterface; +use Symfony\Component\Config\Definition\Builder\NodeBuilder; + +final class Search implements RepositoryConfigParserInterface +{ + public function addSemanticConfig(NodeBuilder $nodeBuilder): void + { + $nodeBuilder + ->arrayNode('search') + ->children() + ->scalarNode('engine') + ->defaultValue('%ibexa.api.search_engine.default%') + ->info('The search engine to use') + ->end() + ->scalarNode('connection') + ->defaultNull() + ->info('The connection name, if applicable (e.g. Doctrine connection name). If not set, the default connection will be used.') + ->end() + ->arrayNode('config') + ->info('Arbitrary configuration options, supported by your search engine') + ->useAttributeAsKey('key') + ->prototype('variable')->end() + ->end() + ->end() + ->end(); + } +} diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/Storage.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/Storage.php new file mode 100644 index 0000000000..bf9a1fb939 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/Repository/Storage.php @@ -0,0 +1,37 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser\Repository; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\RepositoryConfigParserInterface; +use Symfony\Component\Config\Definition\Builder\NodeBuilder; + +final class Storage implements RepositoryConfigParserInterface +{ + public function addSemanticConfig(NodeBuilder $nodeBuilder): void + { + $nodeBuilder + ->arrayNode('storage') + ->children() + ->scalarNode('engine') + ->defaultValue('%ibexa.api.storage_engine.default%') + ->info('The storage engine to use') + ->end() + ->scalarNode('connection') + ->defaultNull() + ->info('The connection name, if applicable (e.g. Doctrine connection name). If not set, the default connection will be used.') + ->end() + ->arrayNode('config') + ->info('Arbitrary configuration options, supported by your storage engine') + ->useAttributeAsKey('key') + ->prototype('variable')->end() + ->end() + ->end() + ->end(); + } +} diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Templates.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/Templates.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Templates.php rename to src/bundle/Core/DependencyInjection/Configuration/Parser/Templates.php index 29d0995d57..6d5274d8ee 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/Templates.php +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/Templates.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\AbstractParser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\AbstractParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; use Symfony\Component\Config\Definition\Builder\NodeBuilder; class Templates extends AbstractParser @@ -56,3 +56,5 @@ public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerIn // Nothing to do here. } } + +class_alias(Templates::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\Templates'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/TwigVariablesParser.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/TwigVariablesParser.php similarity index 78% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/TwigVariablesParser.php rename to src/bundle/Core/DependencyInjection/Configuration/Parser/TwigVariablesParser.php index ce5276e3a6..2ba35a41a6 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/TwigVariablesParser.php +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/TwigVariablesParser.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\AbstractParser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\AbstractParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; use Symfony\Component\Config\Definition\Builder\NodeBuilder; final class TwigVariablesParser extends AbstractParser @@ -48,3 +48,5 @@ public function mapConfig( } } } + +class_alias(TwigVariablesParser::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\TwigVariablesParser'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/UrlChecker.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/UrlChecker.php similarity index 76% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/UrlChecker.php rename to src/bundle/Core/DependencyInjection/Configuration/Parser/UrlChecker.php index 4abeab5d95..99836f93df 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Parser/UrlChecker.php +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/UrlChecker.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\AbstractParser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\AbstractParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; use Symfony\Component\Config\Definition\Builder\NodeBuilder; class UrlChecker extends AbstractParser @@ -35,3 +35,5 @@ public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerIn } } } + +class_alias(UrlChecker::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\UrlChecker'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/UserContentTypeIdentifier.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/UserContentTypeIdentifier.php new file mode 100644 index 0000000000..21005138b3 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/UserContentTypeIdentifier.php @@ -0,0 +1,61 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\AbstractParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Symfony\Component\Config\Definition\Builder\NodeBuilder; + +/** + * @internal + * + * Configuration parser for user identifier configuration. + * + * Example configuration: + * ```yaml + * ibexa: + * system: + * default: # configuration per siteaccess or siteaccess group + * user_content_type_identifier: ['user', 'my_custom_user_identifier'] + * ``` + */ +final class UserContentTypeIdentifier extends AbstractParser +{ + /** + * Adds semantic configuration definition. + * + * @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $nodeBuilder Node just under ezpublish.system.<siteaccess> + */ + public function addSemanticConfig(NodeBuilder $nodeBuilder) + { + $nodeBuilder + ->arrayNode('user_content_type_identifier') + ->info('User content type identifier configuration.') + ->example(['user', 'my_custom_user_identifier']) + ->requiresAtLeastOneElement() + ->prototype('scalar')->end() + ->end(); + } + + public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerInterface $contextualizer): void + { + if (empty($scopeSettings['user_content_type_identifier'])) { + return; + } + + $contextualizer->setContextualParameter( + 'user_content_type_identifier', + $currentScope, + $scopeSettings['user_content_type_identifier'] + ); + } +} + +class_alias(UserContentTypeIdentifier::class, 'EzSystems\EzPlatformAdminUiBundle\DependencyInjection\Configuration\Parser\UserIdentifier'); +class_alias(UserContentTypeIdentifier::class, 'Ibexa\Bundle\AdminUi\DependencyInjection\Configuration\Parser\UserIdentifier'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Parser/View.php b/src/bundle/Core/DependencyInjection/Configuration/Parser/View.php new file mode 100644 index 0000000000..6cb2f70dd1 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Parser/View.php @@ -0,0 +1,82 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\AbstractParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Symfony\Component\Config\Definition\Builder\NodeBuilder; + +class View extends AbstractParser +{ + /** + * Adds semantic configuration definition. + * + * @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $nodeBuilder Node just under ezpublish.system.<siteaccess> + */ + public function addSemanticConfig(NodeBuilder $nodeBuilder) + { + $nodeBuilder + ->arrayNode(static::NODE_KEY) + ->info(static::INFO) + ->useAttributeAsKey('key') + ->normalizeKeys(false) + ->prototype('array') + ->useAttributeAsKey('key') + ->normalizeKeys(false) + ->info("View selection rulesets, grouped by view type. Key is the view type (e.g. 'full', 'line', ...)") + ->prototype('array') + ->children() + ->scalarNode('template')->info('Your template path, as MyBundle:subdir:my_template.html.twig')->end() + ->scalarNode('controller') + ->info( + <<<EOT +Use custom controller instead of the default one to display a content matching your rules. +You can use the controller reference notation supported by Symfony. +EOT + ) + ->example('MyBundle:MyControllerClass:view') + ->end() + ->arrayNode('match') + ->info('Condition matchers configuration') + ->isRequired() + ->useAttributeAsKey('key') + ->prototype('variable')->end() + ->end() + ->arrayNode('params') + ->info( + <<<EOT +Arbitrary params that will be passed in the ContentView object, manageable by ViewProviders. +Those params will NOT be passed to the resulting view template by default. +EOT + ) + ->example( + [ + 'foo' => '%some.parameter.reference%', + 'osTypes' => ['osx', 'linux', 'windows'], + ] + ) + ->useAttributeAsKey('key') + ->prototype('variable')->end() + ->end() + ->end() + ->end() + ->end() + ->end(); + } + + public function preMap(array $config, ContextualizerInterface $contextualizer) + { + $contextualizer->mapConfigArray(static::NODE_KEY, $config, ContextualizerInterface::MERGE_FROM_SECOND_LEVEL); + } + + public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerInterface $contextualizer) + { + // Nothing to do here. + } +} + +class_alias(View::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\View'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/ParserInterface.php b/src/bundle/Core/DependencyInjection/Configuration/ParserInterface.php new file mode 100644 index 0000000000..96cccb2e60 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/ParserInterface.php @@ -0,0 +1,22 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\HookableConfigurationMapperInterface; +use Symfony\Component\Config\Definition\Builder\NodeBuilder; + +interface ParserInterface extends HookableConfigurationMapperInterface +{ + /** + * Adds semantic configuration definition. + * + * @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $nodeBuilder Node just under ezpublish.system.<siteaccess> + */ + public function addSemanticConfig(NodeBuilder $nodeBuilder); +} + +class_alias(ParserInterface::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ParserInterface'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/RepositoryConfigParser.php b/src/bundle/Core/DependencyInjection/Configuration/RepositoryConfigParser.php new file mode 100644 index 0000000000..d0954f0a86 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/RepositoryConfigParser.php @@ -0,0 +1,39 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration; + +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Symfony\Component\Config\Definition\Builder\NodeBuilder; + +final class RepositoryConfigParser implements RepositoryConfigParserInterface +{ + /** @var iterable<\Ibexa\Bundle\Core\DependencyInjection\Configuration\RepositoryConfigParserInterface> */ + private $configParsers; + + /** + * @param \Ibexa\Bundle\Core\DependencyInjection\Configuration\RepositoryConfigParserInterface[] $configParsers + */ + public function __construct(iterable $configParsers = []) + { + foreach ($configParsers as $parser) { + if (!$parser instanceof RepositoryConfigParserInterface) { + throw new InvalidArgumentType('Inner repository config parser', RepositoryConfigParserInterface::class, $parser); + } + } + + $this->configParsers = $configParsers; + } + + public function addSemanticConfig(NodeBuilder $nodeBuilder): void + { + foreach ($this->configParsers as $parser) { + $parser->addSemanticConfig($nodeBuilder); + } + } +} diff --git a/src/bundle/Core/DependencyInjection/Configuration/RepositoryConfigParserInterface.php b/src/bundle/Core/DependencyInjection/Configuration/RepositoryConfigParserInterface.php new file mode 100644 index 0000000000..5ae512d4b9 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/RepositoryConfigParserInterface.php @@ -0,0 +1,16 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration; + +use Symfony\Component\Config\Definition\Builder\NodeBuilder; + +interface RepositoryConfigParserInterface +{ + public function addSemanticConfig(NodeBuilder $nodeBuilder): void; +} diff --git a/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Configuration.php b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Configuration.php new file mode 100644 index 0000000000..ede48e5bc3 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Configuration.php @@ -0,0 +1,69 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * Base class to build scope based semantic configuration tree (aka SiteAccess aware configuration). + * This is very helpful if you need to define specific configuration blocks which need to be repeated by scope/contexts. + * + * Example of scope (aka SiteAccesses) usage, "system" being the node under which scope based configuration take place. + * Key is the context name. + * + * <code> + * ibexa: + * system: + * eng: + * languages: + * - eng-GB + * + * fre: + * languages: + * - fre-FR + * - eng-GB + * </code> + */ +abstract class Configuration implements ConfigurationInterface +{ + /** + * Generates the context node under which context based configuration will be defined. + * + * @param \Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition $rootNode Node under which the generated node will be placed. + * @param string $scopeNodeName + * + * @return \Symfony\Component\Config\Definition\Builder\NodeBuilder + */ + public function generateScopeBaseNode(ArrayNodeDefinition $rootNode, $scopeNodeName = 'system') + { + $contextNode = $rootNode + ->children() + ->arrayNode($scopeNodeName) + ->info('System configuration. First key is always a siteaccess or siteaccess group name') + ->example( + [ + 'my_siteaccess' => [ + 'preferred_quote' => 'Let there be Light!', + 'j_aime' => ['le_saucisson'], + ], + 'my_siteaccess_group' => [ + 'j_aime' => ['la_truite_a_la_vapeur'], + ], + ] + ) + ->useAttributeAsKey('siteaccess_name') + ->requiresAtLeastOneElement() + ->normalizeKeys(false) + ->prototype('array') + ->children(); + + return $contextNode; + } +} + +class_alias(Configuration::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\Configuration'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/ConfigurationMapperInterface.php b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationMapperInterface.php similarity index 83% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/ConfigurationMapperInterface.php rename to src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationMapperInterface.php index 3c2e72a0d2..7196a1c948 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/ConfigurationMapperInterface.php +++ b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationMapperInterface.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware; /** * ConfigurationMapper purpose is to map parsed semantic configuration for given scope @@ -28,3 +28,5 @@ interface ConfigurationMapperInterface */ public function mapConfig(array &$scopeSettings, $currentScope, ContextualizerInterface $contextualizer); } + +class_alias(ConfigurationMapperInterface::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ConfigurationMapperInterface'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php similarity index 90% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php rename to src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php index ec50c3864d..64b0bae3a6 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php +++ b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessor.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware; use InvalidArgumentException; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -44,7 +44,7 @@ class ConfigurationProcessor */ protected $scopeNodeName; - /** @var \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface */ + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface */ protected $contextualizer; public function __construct(ContainerInterface $containerBuilder, $namespace, $siteAcccessNodeName = 'system') @@ -162,7 +162,7 @@ public function mapConfigArray($id, array $config, $options = 0) * @param string $namespace * @param string $siteAccessNodeName * - * @return \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface + * @return \Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface */ protected function buildContextualizer(ContainerInterface $containerBuilder, $namespace, $siteAccessNodeName) { @@ -177,7 +177,7 @@ protected function buildContextualizer(ContainerInterface $containerBuilder, $na } /** - * @param \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface $contextualizer + * @param \Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface $contextualizer */ public function setContextualizer(ContextualizerInterface $contextualizer) { @@ -185,10 +185,12 @@ public function setContextualizer(ContextualizerInterface $contextualizer) } /** - * @return \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface + * @return \Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface */ public function getContextualizer() { return $this->contextualizer; } } + +class_alias(ConfigurationProcessor::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ConfigurationProcessor'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php similarity index 97% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php rename to src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php index af1616d663..2aa708bced 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php +++ b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/Contextualizer.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver; use Symfony\Component\DependencyInjection\ContainerInterface; class Contextualizer implements ContextualizerInterface @@ -385,3 +385,5 @@ private function mergeSettingsForSiteAccess( return $mergedSettings; } } + +class_alias(Contextualizer::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\Contextualizer'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php similarity index 94% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php rename to src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php index b907124dac..901799759b 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php +++ b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerInterface.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -35,7 +35,7 @@ interface ContextualizerInterface * use Symfony\Component\HttpKernel\DependencyInjection\Extension; * use Symfony\Component\DependencyInjection\ContainerBuilder; * use Symfony\Component\DependencyInjection\Loader; - * use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware; + * use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware; * * class AcmeDemoExtension extends Extension * { @@ -122,7 +122,7 @@ public function mapSetting($id, array $config); * use Symfony\Component\HttpKernel\DependencyInjection\Extension; * use Symfony\Component\DependencyInjection\ContainerBuilder; * use Symfony\Component\DependencyInjection\Loader; - * use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware; + * use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware; * * class AcmeDemoExtension extends Extension * { @@ -186,7 +186,7 @@ public function setContainer(ContainerInterface $container); /** * Injects namespace for internal settings. * Registered internal settings always have the format <namespace>.<scope>.<parameter_name> - * e.g. ezsettings.default.session. + * e.g. ibexa.site_access.config.default.session. * * @param string $namespace */ @@ -234,3 +234,5 @@ public function setGroupsBySiteAccess(array $groupsBySiteAccess); */ public function getGroupsBySiteAccess(); } + +class_alias(ContextualizerInterface::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParser.php b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParser.php similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParser.php rename to src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParser.php index 2fead88cdf..4c26f6048c 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParser.php +++ b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParser.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware; use OutOfBoundsException; @@ -45,3 +45,5 @@ private function removeBoundaryDelimiter($setting) return substr($setting, 1, -1); } } + +class_alias(DynamicSettingParser::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\DynamicSettingParser'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserInterface.php b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserInterface.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserInterface.php rename to src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserInterface.php index afa9aba128..cc5d4e8f98 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserInterface.php +++ b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserInterface.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware; /** * Interface for dynamic setting parsers. @@ -14,7 +14,7 @@ * Supported syntax for dynamic settings: $<paramName>[;<namespace>[;<scope>]]$ * * The following will work : - * $my_param$ (using default namespace, e.g. ezsettings, with current scope). + * $my_param$ (using default namespace, e.g. ibexa.site_access.config, with current scope). * $my_param;foo$ (using "foo" as namespace, in current scope). * $my_param;foo;some_siteaccess$ (using "foo" as namespace, forcing "some_siteaccess scope"). * @@ -51,3 +51,5 @@ public function isDynamicSetting($setting); */ public function parseDynamicSetting($setting); } + +class_alias(DynamicSettingParserInterface::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\DynamicSettingParserInterface'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/HookableConfigurationMapperInterface.php b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/HookableConfigurationMapperInterface.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/HookableConfigurationMapperInterface.php rename to src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/HookableConfigurationMapperInterface.php index 5fcfbc8fee..9f6ffd935f 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/SiteAccessAware/HookableConfigurationMapperInterface.php +++ b/src/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/HookableConfigurationMapperInterface.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware; /** * Interface of ConfigurationMapper objects that need to trigger actions before and/or after looping over @@ -40,3 +40,5 @@ public function preMap(array $config, ContextualizerInterface $contextualizer); */ public function postMap(array $config, ContextualizerInterface $contextualizer); } + +class_alias(HookableConfigurationMapperInterface::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\HookableConfigurationMapperInterface'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollector.php b/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollector.php new file mode 100644 index 0000000000..4752c522d8 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollector.php @@ -0,0 +1,45 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\ConfigSuggestion; + +class SuggestionCollector implements SuggestionCollectorInterface +{ + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\ConfigSuggestion[] */ + private $suggestions = []; + + /** + * Adds a config suggestion to the list. + * + * @param \Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\ConfigSuggestion $suggestion + */ + public function addSuggestion(ConfigSuggestion $suggestion) + { + $this->suggestions[] = $suggestion; + } + + /** + * Returns all config suggestions. + * + * @return \Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\ConfigSuggestion[] + */ + public function getSuggestions() + { + return $this->suggestions; + } + + /** + * @return bool + */ + public function hasSuggestions() + { + return !empty($this->suggestions); + } +} + +class_alias(SuggestionCollector::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollector'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorAwareInterface.php b/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorAwareInterface.php new file mode 100644 index 0000000000..63ed004eb2 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorAwareInterface.php @@ -0,0 +1,19 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector; + +interface SuggestionCollectorAwareInterface +{ + /** + * Injects SuggestionCollector. + * + * @param SuggestionCollectorInterface $suggestionCollector + */ + public function setSuggestionCollector(SuggestionCollectorInterface $suggestionCollector); +} + +class_alias(SuggestionCollectorAwareInterface::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorAwareInterface'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorInterface.php b/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorInterface.php new file mode 100644 index 0000000000..c278e123fc --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorInterface.php @@ -0,0 +1,36 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\ConfigSuggestion; + +/** + * Interface for configuration suggestion collectors. + */ +interface SuggestionCollectorInterface +{ + /** + * Adds a config suggestion to the list. + * + * @param \Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\ConfigSuggestion $suggestion + */ + public function addSuggestion(ConfigSuggestion $suggestion); + + /** + * Returns all config suggestions. + * + * @return \Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\ConfigSuggestion[] + */ + public function getSuggestions(); + + /** + * @return bool + */ + public function hasSuggestions(); +} + +class_alias(SuggestionCollectorInterface::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php b/src/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php similarity index 89% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php rename to src/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php index 8c1e0a7cfa..0ed806dca0 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php +++ b/src/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestion.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion; +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion; /** * ConfigSuggestion is a value object holding a suggestion change for semantic configuration. @@ -86,3 +86,5 @@ public function isMandatory() return $this->mandatory; } } + +class_alias(ConfigSuggestion::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\ConfigSuggestion'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Formatter/SuggestionFormatterInterface.php b/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Formatter/SuggestionFormatterInterface.php new file mode 100644 index 0000000000..eb3f1ef795 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Formatter/SuggestionFormatterInterface.php @@ -0,0 +1,22 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Formatter; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\ConfigSuggestion; + +/** + * Interface for ConfigSuggestion formatters. + * + * A SuggestionFormatter will convert a ConfigSuggestion value object to a human readable format + * (e.g. YAML, XML, JSON...). + */ +interface SuggestionFormatterInterface +{ + public function format(ConfigSuggestion $configSuggestion); +} + +class_alias(SuggestionFormatterInterface::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Formatter\SuggestionFormatterInterface'); diff --git a/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Formatter/YamlSuggestionFormatter.php b/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Formatter/YamlSuggestionFormatter.php new file mode 100644 index 0000000000..7b007bc6b5 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Configuration/Suggestion/Formatter/YamlSuggestionFormatter.php @@ -0,0 +1,39 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Formatter; + +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\ConfigSuggestion; +use Symfony\Component\Yaml\Yaml; + +class YamlSuggestionFormatter implements SuggestionFormatterInterface +{ + public function format(ConfigSuggestion $configSuggestion) + { + $message = $configSuggestion->getMessage(); + $suggestion = $configSuggestion->getSuggestion(); + if ($suggestion) { + $yamlConfig = Yaml::dump($suggestion, 8); + if (\PHP_SAPI !== 'cli') { + $yamlConfig = "<pre>$yamlConfig</pre>"; + } + + return <<<EOT +{$message} + + +Example: +======== + +$yamlConfig +EOT; + } + + return $message; + } +} + +class_alias(YamlSuggestionFormatter::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Formatter\YamlSuggestionFormatter'); diff --git a/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php b/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php new file mode 100644 index 0000000000..8b99223fde --- /dev/null +++ b/src/bundle/Core/DependencyInjection/IbexaCoreExtension.php @@ -0,0 +1,1031 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\DependencyInjection; + +use Ibexa\Bundle\Core\DependencyInjection\Compiler\QueryTypePass; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ParserInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\RepositoryConfigParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\RepositoryConfigParserInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ConfigurationProcessor; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollector; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorAwareInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Formatter\YamlSuggestionFormatter; +use Ibexa\Bundle\Core\DependencyInjection\Security\PolicyProvider\PoliciesConfigBuilder; +use Ibexa\Bundle\Core\DependencyInjection\Security\PolicyProvider\PolicyProviderInterface; +use Ibexa\Bundle\Core\Session\Handler\NativeSessionHandler; +use Ibexa\Bundle\Core\SiteAccess\SiteAccessConfigurationFilter; +use Ibexa\Contracts\Core\MVC\EventSubscriber\ConfigScopeChangeSubscriber; +use Ibexa\Contracts\Core\Repository\Values\Filter\CriterionQueryBuilder as FilteringCriterionQueryBuilder; +use Ibexa\Contracts\Core\Repository\Values\Filter\SortClauseQueryBuilder as FilteringSortClauseQueryBuilder; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\Routing\ChainRouter; +use Ibexa\Core\QueryType\QueryType; +use InvalidArgumentException; +use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; +use Symfony\Component\DependencyInjection\Loader; +use Symfony\Component\DependencyInjection\Loader\FileLoader; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; + +class IbexaCoreExtension extends Extension implements PrependExtensionInterface +{ + public const EXTENSION_NAME = 'ibexa'; + private const ENTITY_MANAGER_TEMPLATE = [ + 'connection' => null, + 'mappings' => [], + ]; + + private const TRANSLATIONS_DIRECTORY = '/vendor/ibexa/i18n/translations'; + + private const DEBUG_PARAM = 'kernel.debug'; + + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollector */ + private $suggestionCollector; + + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\ParserInterface */ + private $mainConfigParser; + + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\RepositoryConfigParser */ + private $mainRepositoryConfigParser; + + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\ParserInterface[] */ + private $siteAccessConfigParsers; + + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\RepositoryConfigParserInterface[] */ + private $repositoryConfigParsers = []; + + /** @var \Ibexa\Bundle\Core\DependencyInjection\Security\PolicyProvider\PolicyProviderInterface[] */ + private $policyProviders = []; + + /** + * Holds a collection of YAML files, as an array with directory path as a + * key to the array of contained file names. + * + * @var array + */ + private $defaultSettingsCollection = []; + + /** @var \Ibexa\Bundle\Core\SiteAccess\SiteAccessConfigurationFilter[] */ + private $siteaccessConfigurationFilters = []; + + public function __construct(array $siteAccessConfigParsers = [], array $repositoryConfigParsers = []) + { + $this->siteAccessConfigParsers = $siteAccessConfigParsers; + $this->repositoryConfigParsers = $repositoryConfigParsers; + $this->suggestionCollector = new SuggestionCollector(); + } + + public function getAlias() + { + return self::EXTENSION_NAME; + } + + /** + * Loads a specific configuration. + * + * @param mixed[] $configs An array of configuration values + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container A ContainerBuilder instance + * + * @throws \InvalidArgumentException When provided tag is not defined in this extension + * + * @api + */ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new Loader\YamlFileLoader( + $container, + new FileLocator(__DIR__ . '/../Resources/config') + ); + + $configuration = $this->getConfiguration($configs, $container); + + if ($this->shouldLoadTestBehatServices($container)) { + $loader->load('feature_contexts.yml'); + } + + // Note: this is where the transformation occurs + $config = $this->processConfiguration($configuration, $configs); + + // Base services and services overrides + $loader->load('services.yml'); + // Security services + $loader->load('security.yml'); + // HTTP Kernel + $loader->load('http_kernel.yml'); + + if (interface_exists('FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractorInterface')) { + $loader->load('routing/js_routing.yml'); + } + + $this->registerRepositoriesConfiguration($config, $container); + $this->registerSiteAccessConfiguration($config, $container); + $this->registerImageMagickConfiguration($config, $container); + $this->registerUrlAliasConfiguration($config, $container); + $this->registerUrlWildcardsConfiguration($config, $container); + $this->registerOrmConfiguration($config, $container); + $this->registerUITranslationsConfiguration($config, $container); + + // Routing + $this->handleRouting($config, $container, $loader); + // Public API loading + $this->handleApiLoading($container, $loader); + $this->handleTemplating($container, $loader); + $this->handleSessionLoading($container, $loader); + $this->handleCache($config, $container, $loader); + $this->handleLocale($config, $container, $loader); + $this->handleHelpers($config, $container, $loader); + $this->handleImage($config, $container, $loader); + $this->handleUrlChecker($config, $container, $loader); + $this->handleUrlWildcards($config, $container, $loader); + + // Map settings + $processor = new ConfigurationProcessor($container, 'ibexa.site_access.config'); + $processor->mapConfig($config, $this->getMainConfigParser()); + + if ($this->suggestionCollector->hasSuggestions()) { + $message = ''; + $suggestionFormatter = new YamlSuggestionFormatter(); + foreach ($this->suggestionCollector->getSuggestions() as $suggestion) { + $message .= $suggestionFormatter->format($suggestion) . "\n\n"; + } + + throw new InvalidArgumentException($message); + } + + $this->buildPolicyMap($container); + + $this->registerForAutoConfiguration($container); + } + + /** + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @return \Ibexa\Bundle\Core\DependencyInjection\Configuration + */ + public function getConfiguration(array $config, ContainerBuilder $container) + { + $configuration = new Configuration( + $this->getMainConfigParser(), + $this->getMainRepositoryConfigParser(), + $this->suggestionCollector + ); + + $configuration->setSiteAccessConfigurationFilters($this->siteaccessConfigurationFilters); + + return $configuration; + } + + /** + * {@inheritdoc} + */ + public function prepend(ContainerBuilder $container) + { + $this->prependTranslatorConfiguration($container); + $this->prependDoctrineConfiguration($container); + $this->prependJMSTranslation($container); + + // Default settings + $this->handleDefaultSettingsLoading($container); + + $this->configureGenericSetup($container); + $this->configurePlatformShSetup($container); + } + + /** + * @return \Ibexa\Bundle\Core\DependencyInjection\Configuration\ParserInterface + */ + private function getMainConfigParser() + { + if ($this->mainConfigParser === null) { + foreach ($this->siteAccessConfigParsers as $parser) { + if ($parser instanceof SuggestionCollectorAwareInterface) { + $parser->setSuggestionCollector($this->suggestionCollector); + } + } + + $this->mainConfigParser = new ConfigParser($this->siteAccessConfigParsers); + } + + return $this->mainConfigParser; + } + + private function getMainRepositoryConfigParser(): RepositoryConfigParserInterface + { + if (!isset($this->mainRepositoryConfigParser)) { + foreach ($this->repositoryConfigParsers as $parser) { + if ($parser instanceof SuggestionCollectorAwareInterface) { + $parser->setSuggestionCollector($this->suggestionCollector); + } + } + + $this->mainRepositoryConfigParser = new RepositoryConfigParser($this->repositoryConfigParsers); + } + + return $this->mainRepositoryConfigParser; + } + + /** + * @throws \Exception + */ + private function handleDefaultSettingsLoading(ContainerBuilder $container): void + { + $loader = new Loader\YamlFileLoader( + $container, + new FileLocator(__DIR__ . '/../Resources/config') + ); + $loader->load('default_settings.yml'); + + foreach ($this->defaultSettingsCollection as $fileLocation => $files) { + $externalLoader = new Loader\YamlFileLoader($container, new FileLocator($fileLocation)); + foreach ($files as $file) { + $externalLoader->load($file); + } + } + } + + private function registerRepositoriesConfiguration(array $config, ContainerBuilder $container) + { + if (!isset($config['repositories'])) { + $config['repositories'] = []; + } + + foreach ($config['repositories'] as $name => &$repository) { + if (empty($repository['fields_groups']['list'])) { + $repository['fields_groups']['list'] = $container->getParameter('ibexa.site_access.config.default.content.field_groups.list'); + } + } + + $container->setParameter('ibexa.repositories', $config['repositories']); + } + + private function registerSiteAccessConfiguration(array $config, ContainerBuilder $container) + { + if (!isset($config['siteaccess'])) { + $config['siteaccess'] = []; + $config['siteaccess']['list'] = ['setup']; + $config['siteaccess']['default_siteaccess'] = 'setup'; + $config['siteaccess']['groups'] = []; + $config['siteaccess']['match'] = null; + } + + $container->setParameter('ibexa.site_access.list', $config['siteaccess']['list']); + ConfigurationProcessor::setAvailableSiteAccesses($config['siteaccess']['list']); + $container->setParameter('ibexa.site_access.default', $config['siteaccess']['default_siteaccess']); + $container->setParameter('ibexa.site_access.match_config', $config['siteaccess']['match']); + + // Register siteaccess groups + reverse + $container->setParameter('ibexa.site_access.groups', $config['siteaccess']['groups']); + ConfigurationProcessor::setAvailableSiteAccessGroups($config['siteaccess']['groups']); + $groupsBySiteaccess = []; + foreach ($config['siteaccess']['groups'] as $groupName => $groupMembers) { + foreach ($groupMembers as $member) { + if (!isset($groupsBySiteaccess[$member])) { + $groupsBySiteaccess[$member] = []; + } + + $groupsBySiteaccess[$member][] = $groupName; + } + } + $container->setParameter('ibexa.site_access.groups_by_site_access', $groupsBySiteaccess); + ConfigurationProcessor::setGroupsBySiteAccess($groupsBySiteaccess); + } + + private function registerImageMagickConfiguration(array $config, ContainerBuilder $container) + { + if (isset($config['imagemagick'])) { + $container->setParameter('ibexa.image.imagemagick.enabled', $config['imagemagick']['enabled']); + if ($config['imagemagick']['enabled']) { + $container->setParameter('ibexa.image.imagemagick.executable_path', dirname($config['imagemagick']['path'])); + $container->setParameter('ibexa.image.imagemagick.executable', basename($config['imagemagick']['path'])); + } + } + + $filters = isset($config['imagemagick']['filters']) ? $config['imagemagick']['filters'] : []; + $filters = $filters + $container->getParameter('ibexa.image.imagemagick.filters'); + $container->setParameter('ibexa.image.imagemagick.filters', $filters); + } + + private function registerOrmConfiguration(array $config, ContainerBuilder $container): void + { + if (!isset($config['orm']['entity_mappings'])) { + return; + } + + $entityMappings = $config['orm']['entity_mappings']; + $container->setParameter('ibexa.orm.entity_mappings', $entityMappings); + } + + private function registerUITranslationsConfiguration(array $config, ContainerBuilder $container): void + { + $container->setParameter('ibexa.ui.translations.enabled', $config['ui']['translations']['enabled'] ?? false); + } + + /** + * Handle routing parameters. + * + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader + */ + private function handleRouting(array $config, ContainerBuilder $container, FileLoader $loader) + { + $loader->load('routing.yml'); + $container->setAlias('router', ChainRouter::class); + $container->getAlias('router')->setPublic(true); + + if (isset($config['router']['default_router']['non_siteaccess_aware_routes'])) { + $container->setParameter( + 'ibexa.default_router.non_site_access_aware_routes', + array_merge( + $container->getParameter('ibexa.default_router.non_site_access_aware_routes'), + $config['router']['default_router']['non_siteaccess_aware_routes'] + ) + ); + } + } + + /** + * Handle public API loading. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader + * + * @throws \Exception + */ + private function handleApiLoading(ContainerBuilder $container, FileLoader $loader): void + { + // @todo move settings to Core Bundle Resources + // Loading configuration from ./src/lib/Resources/settings + $coreLoader = new Loader\YamlFileLoader( + $container, + new FileLocator(__DIR__ . '/../../../lib/Resources/settings') + ); + $coreLoader->load('repository.yml'); + $coreLoader->load('repository/inner.yml'); + $coreLoader->load('repository/event.yml'); + $coreLoader->load('repository/siteaccessaware.yml'); + $coreLoader->load('repository/autowire.yml'); + $coreLoader->load('fieldtype_external_storages.yml'); + $coreLoader->load('fieldtypes.yml'); + $coreLoader->load('indexable_fieldtypes.yml'); + $coreLoader->load('fieldtype_services.yml'); + $coreLoader->load('roles.yml'); + $coreLoader->load('storage_engines/common.yml'); + $coreLoader->load('storage_engines/cache.yml'); + $coreLoader->load('storage_engines/legacy.yml'); + $coreLoader->load('storage_engines/shortcuts.yml'); + $coreLoader->load('search_engines/common.yml'); + $coreLoader->load('utils.yml'); + $coreLoader->load('io.yml'); + $coreLoader->load('policies.yml'); + $coreLoader->load('notification.yml'); + $coreLoader->load('user_preference.yml'); + $coreLoader->load('events.yml'); + $coreLoader->load('thumbnails.yml'); + $coreLoader->load('tokens.yml'); + $coreLoader->load('content_location_mapper.yml'); + + // Public API services + $loader->load('papi.yml'); + + // Built-in field types + $loader->load('fieldtype_services.yml'); + + // Storage engine + $loader->load('storage_engines.yml'); + + $loader->load('query_types.yml'); + $loader->load('sort_spec.yml'); + } + + /** + * Handle templating parameters. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader + */ + private function handleTemplating(ContainerBuilder $container, FileLoader $loader) + { + $loader->load('templating.yml'); + } + + /** + * Handle session parameters. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader + */ + private function handleSessionLoading(ContainerBuilder $container, FileLoader $loader) + { + $loader->load('session.yml'); + } + + /** + * Handle cache parameters. + * + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader + * + * @throws \InvalidArgumentException + */ + private function handleCache(array $config, ContainerBuilder $container, FileLoader $loader) + { + $loader->load('cache.yml'); + + if (isset($config['http_cache']['purge_type'])) { + // resolves ENV variable at compile time, needed by ezplatform-http-cache to setup purge driver + $purgeType = $container->resolveEnvPlaceholders($config['http_cache']['purge_type'], true); + + $container->setParameter('ibexa.http_cache.purge_type', $purgeType); + } + + if ( + $container->hasParameter(self::DEBUG_PARAM) + && $container->getParameter(self::DEBUG_PARAM) === true + ) { + $loader->load('debug/cache_validator.yaml'); + } + } + + /** + * Handle locale parameters. + * + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader + */ + private function handleLocale(array $config, ContainerBuilder $container, FileLoader $loader) + { + $loader->load('locale.yml'); + $container->setParameter( + 'ibexa.locale.conversion_map', + $config['locale_conversion'] + $container->getParameter('ibexa.locale.conversion_map') + ); + } + + /** + * Handle helpers. + * + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader + */ + private function handleHelpers(array $config, ContainerBuilder $container, FileLoader $loader) + { + $loader->load('helpers.yml'); + } + + /** + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader + */ + private function handleImage(array $config, ContainerBuilder $container, FileLoader $loader) + { + $loader->load('image.yml'); + + $providers = []; + if (isset($config['image_placeholder'])) { + foreach ($config['image_placeholder'] as $name => $value) { + if (isset($providers[$name])) { + throw new InvalidConfigurationException("An image_placeholder called $name already exists"); + } + + $providers[$name] = $value; + } + } + + $container->setParameter('ibexa.io.images.alias.placeholder_provider', $providers); + } + + private function handleUrlChecker($config, ContainerBuilder $container, FileLoader $loader) + { + $loader->load('url_checker.yml'); + } + + private function buildPolicyMap(ContainerBuilder $container) + { + $policiesBuilder = new PoliciesConfigBuilder($container); + foreach ($this->policyProviders as $provider) { + $provider->addPolicies($policiesBuilder); + } + } + + /** + * Adds a new policy provider to the internal collection. + * One can call this method from a bundle `build()` method. + * + * ```php + * public function build(ContainerBuilder $container) + * { + * $ibexaExtension = $container->getExtension('ibexa'); + * $ibexaExtension->addPolicyProvider($myPolicyProvider); + * } + * ``` + * + * @since 6.0 + * + * @param \Ibexa\Bundle\Core\DependencyInjection\Security\PolicyProvider\PolicyProviderInterface $policyProvider + */ + public function addPolicyProvider(PolicyProviderInterface $policyProvider) + { + $this->policyProviders[] = $policyProvider; + } + + /** + * Adds a new config parser to the internal collection. + * One can call this method from a bundle `build()` method. + * + * ```php + * public function build(ContainerBuilder $container) + * { + * $ibexaExtension = $container->getExtension('ibexa'); + * $ibexaExtension->addConfigParser($myConfigParser); + * } + * ``` + * + * @since 6.0 + * + * @param \Ibexa\Bundle\Core\DependencyInjection\Configuration\ParserInterface $configParser + */ + public function addConfigParser(ParserInterface $configParser) + { + $this->siteAccessConfigParsers[] = $configParser; + } + + public function addRepositoryConfigParser(RepositoryConfigParserInterface $configParser): void + { + $this->repositoryConfigParsers[] = $configParser; + } + + /** + * Adds new default settings to the internal collection. + * One can call this method from a bundle `build()` method. + * + * ```php + * public function build(ContainerBuilder $container) + * { + * $ibexaExtension = $container->getExtension('ibexa'); + * $ibexaExtension->addDefaultSettings( + * __DIR__ . '/Resources/config', + * ['default_settings.yml'] + * ); + * } + * ``` + * + * @since 6.0 + * + * @param string $fileLocation + * @param array $files + */ + public function addDefaultSettings($fileLocation, array $files) + { + $this->defaultSettingsCollection[$fileLocation] = $files; + } + + public function addSiteAccessConfigurationFilter(SiteAccessConfigurationFilter $filter) + { + $this->siteaccessConfigurationFilters[] = $filter; + } + + /** + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + private function registerUrlAliasConfiguration(array $config, ContainerBuilder $container) + { + if (!isset($config['url_alias'])) { + $config['url_alias'] = ['slug_converter' => []]; + } + + $container->setParameter('ibexa.url_alias.slug_converter', $config['url_alias']['slug_converter']); + } + + /** + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + private function prependTranslatorConfiguration(ContainerBuilder $container) + { + if (!$container->hasExtension('framework')) { + return; + } + + $fileSystem = new Filesystem(); + $translationsPath = $container->getParameterBag()->get('kernel.project_dir') . self::TRANSLATIONS_DIRECTORY; + + if ($fileSystem->exists($translationsPath)) { + $container->prependExtensionConfig('framework', ['translator' => ['paths' => [$translationsPath]]]); + } + } + + private function prependDoctrineConfiguration(ContainerBuilder $container): void + { + if (!$container->hasExtension('doctrine')) { + return; + } + + $kernelConfigs = $container->getExtensionConfig('ibexa'); + $entityMappings = []; + + $repositoryConnections = []; + foreach ($kernelConfigs as $config) { + if (isset($config['orm']['entity_mappings'])) { + $entityMappings[] = $config['orm']['entity_mappings']; + } + + if (isset($config['repositories'])) { + $repositoryConnections[] = array_map( + static function (array $repository): string { + return $repository['storage']['connection'] + ?? 'default'; + }, + $config['repositories'] + ); + } + } + + // compose clean array with all connection identifiers + $connections = array_values( + array_filter( + array_unique( + array_merge(...$repositoryConnections) ?? [] + ) + ) + ); + + $doctrineConfig = [ + 'orm' => [ + 'entity_managers' => [], + ], + ]; + + $entityMappingConfig = !empty($entityMappings) ? array_merge_recursive(...$entityMappings) : []; + + foreach ($connections as $connection) { + $doctrineConfig['orm']['entity_managers'][sprintf('ibexa_%s', $connection)] = array_merge( + self::ENTITY_MANAGER_TEMPLATE, + ['connection' => $connection, 'mappings' => $entityMappingConfig] + ); + } + + $container->prependExtensionConfig('doctrine', $doctrineConfig); + } + + /** + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + */ + private function registerUrlWildcardsConfiguration(array $config, ContainerBuilder $container): void + { + $container->setParameter('ibexa.url_wildcards.enabled', $config['url_wildcards']['enabled'] ?? false); + } + + /** + * Loads configuration for UrlWildcardsRouter service if enabled. + * + * @param array $config + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\Loader\FileLoader $loader + */ + private function handleUrlWildcards(array $config, ContainerBuilder $container, Loader\YamlFileLoader $loader) + { + if ($container->getParameter('ibexa.url_wildcards.enabled')) { + $loader->load('url_wildcard.yml'); + } + } + + private function registerForAutoConfiguration(ContainerBuilder $container): void + { + $container->registerForAutoconfiguration(QueryType::class) + ->addTag(QueryTypePass::QUERY_TYPE_SERVICE_TAG); + + $container->registerForAutoconfiguration(ConfigScopeChangeSubscriber::class) + ->addTag( + 'kernel.event_listener', + ['method' => 'onConfigScopeChange', 'event' => MVCEvents::CONFIG_SCOPE_CHANGE] + ) + ->addTag( + 'kernel.event_listener', + ['method' => 'onConfigScopeChange', 'event' => MVCEvents::CONFIG_SCOPE_RESTORE] + ); + + $container->registerForAutoconfiguration(FilteringCriterionQueryBuilder::class) + ->addTag(ServiceTags::FILTERING_CRITERION_QUERY_BUILDER); + + $container->registerForAutoconfiguration(FilteringSortClauseQueryBuilder::class) + ->addTag(ServiceTags::FILTERING_SORT_CLAUSE_QUERY_BUILDER); + } + + /** + * @throws \Exception + */ + private function configureGenericSetup(ContainerBuilder $container): void + { + // One of `legacy` (default) or `solr` + $container->setParameter('search_engine', '%env(SEARCH_ENGINE)%'); + + // Session save path as used by symfony session handlers (eg. used for dsn with redis) + $container->setParameter('ibexa.session.save_path', '%kernel.project_dir%/var/sessions/%kernel.environment%'); + + // Predefined pools are located in config/packages/cache_pool/ + // You can add your own cache pool to the folder mentioned above. + // In order to change the default cache_pool use environmental variable export. + // The line below must not be altered as required cache service files are resolved based on environmental config. + $container->setParameter('cache_pool', '%env(CACHE_POOL)%'); + + // By default cache ttl is set to 24h, when using Varnish you can set a much higher value. High values depends on + // using IbexaHttpCacheBundle (default as of v1.12) which by design expires affected cache on changes + $container->setParameter('httpcache_default_ttl', '%env(HTTPCACHE_DEFAULT_TTL)%'); + + // Settings for HttpCache + $container->setParameter('purge_server', '%env(HTTPCACHE_PURGE_SERVER)%'); + + // Identifier used to generate the CSRF token. Commenting this line will result in authentication + // issues both in AdminUI and REST calls + $container->setParameter('ibexa.rest.csrf_token_intention', 'authenticate'); + + // Varnish invalidation/purge token (for use on platform.sh, Ibexa Cloud and other places you can't use IP for ACL) + $container->setParameter('varnish_invalidate_token', '%env(resolve:default::HTTPCACHE_VARNISH_INVALIDATE_TOKEN)%'); + + // Compile time handlers + // These are defined at compile time, and hence can't be set at runtime using env() + // config/env/generic.php takes care about letting you set them by env variables + + // Session handler, by default set to file based (instead of ~) in order to be able to use %ibexa.session.save_path% + $container->setParameter('ibexa.session.handler_id', 'session.handler.native_file'); + + // Purge type used by HttpCache system ("local", "varnish"/"http", and on ee also "fastly") + $container->setParameter('purge_type', '%env(HTTPCACHE_PURGE_TYPE)%'); + + $container->setParameter('solr_dsn', '%env(SOLR_DSN)%'); + $container->setParameter('solr_core', '%env(SOLR_CORE)%'); + + $projectDir = $container->getParameter('kernel.project_dir'); + + if ($dfsNfsPath = $_SERVER['DFS_NFS_PATH'] ?? false) { + $container->setParameter('dfs_nfs_path', $dfsNfsPath); + + $parameterMap = [ + 'dfs_database_charset' => 'database_charset', + 'dfs_database_driver' => 'database_driver', + 'dfs_database_collation' => 'database_collation', + ]; + + foreach ($parameterMap as $dfsParameter => $platformParameter) { + $container->setParameter( + $dfsParameter, + $_SERVER[strtoupper($dfsParameter)] ?? $container->getParameter($platformParameter) + ); + } + + $loader = new Loader\YamlFileLoader($container, new FileLocator($projectDir . '/config/packages/dfs')); + $loader->load('dfs.yaml'); + } + + // Cache settings + // If CACHE_POOL env variable is set, check if there is a yml file that needs to be loaded for it + if (($pool = $_SERVER['CACHE_POOL'] ?? false) && file_exists($projectDir . "/config/packages/cache_pool/$pool.yaml")) { + $loader = new Loader\YamlFileLoader($container, new FileLocator($projectDir . '/config/packages/cache_pool')); + $loader->load($pool . '.yaml'); + } + + if ($purgeType = $_SERVER['HTTPCACHE_PURGE_TYPE'] ?? false) { + $container->setParameter('purge_type', $purgeType); + $container->setParameter('ibexa.http_cache.purge_type', $purgeType); + } + + if ($value = $_SERVER['MAILER_TRANSPORT'] ?? false) { + $container->setParameter('mailer_transport', $value); + } + + if ($value = $_SERVER['LOG_TYPE'] ?? false) { + $container->setParameter('log_type', $value); + } + + if ($value = $_SERVER['SESSION_HANDLER_ID'] ?? false) { + $container->setParameter('ibexa.session.handler_id', $value); + } + + if ($value = $_SERVER['SESSION_SAVE_PATH'] ?? false) { + $container->setParameter('ibexa.session.save_path', $value); + } + } + + /** + * @throws \Exception + */ + private function configurePlatformShSetup(ContainerBuilder $container): void + { + $projectDir = $container->getParameter('kernel.project_dir'); + + // Will not be executed on build step + $relationships = $_SERVER['PLATFORM_RELATIONSHIPS'] ?? false; + if (!$relationships) { + return; + } + $routes = $_SERVER['PLATFORM_ROUTES']; + + $relationships = json_decode(base64_decode($relationships), true); + $routes = json_decode(base64_decode($routes), true); + + // PLATFORMSH_DFS_NFS_PATH is different compared to DFS_NFS_PATH in the sense that it is relative to ezplatform dir + // DFS_NFS_PATH is an absolute path + if ($dfsNfsPath = $_SERVER['PLATFORMSH_DFS_NFS_PATH'] ?? false) { + $container->setParameter('dfs_nfs_path', sprintf('%s/%s', $projectDir, $dfsNfsPath)); + + // Map common parameters + $container->setParameter('dfs_database_charset', $container->getParameter('database_charset')); + $container->setParameter( + 'dfs_database_collation', + $container->getParameter('database_collation') + ); + if (\array_key_exists('dfs_database', $relationships)) { + // process dedicated P.sh dedicated config + foreach ($relationships['dfs_database'] as $endpoint) { + if (empty($endpoint['query']['is_master'])) { + continue; + } + $container->setParameter('dfs_database_driver', 'pdo_' . $endpoint['scheme']); + $container->setParameter( + 'dfs_database_url', + sprintf( + '%s://%s:%s:%d@%s/%s', + $endpoint['scheme'], + $endpoint['username'], + $endpoint['password'], + $endpoint['port'], + $endpoint['host'], + $endpoint['path'] + ) + ); + } + } else { + // or set fallback from the Repository database, if not configured + $container->setParameter('dfs_database_driver', $container->getParameter('database_driver')); + } + + $loader = new Loader\YamlFileLoader($container, new FileLocator($projectDir . '/config/packages/dfs')); + $loader->load('dfs.yaml'); + } + + // Use Redis-based caching if possible. + if (isset($relationships['rediscache'])) { + foreach ($relationships['rediscache'] as $endpoint) { + if ($endpoint['scheme'] !== 'redis') { + continue; + } + + $loader = new Loader\YamlFileLoader($container, new FileLocator($projectDir . '/config/packages/cache_pool')); + $loader->load('cache.redis.yaml'); + + $container->setParameter('cache_pool', 'cache.redis'); + $container->setParameter('cache_dsn', sprintf('%s:%d', $endpoint['host'], $endpoint['port']) . '?retry_interval=3'); + } + } elseif (isset($relationships['cache'])) { + // Fallback to memcached if here (deprecated, we will only handle redis here in the future) + foreach ($relationships['cache'] as $endpoint) { + if ($endpoint['scheme'] !== 'memcached') { + continue; + } + + @trigger_error('Usage of Memcached is deprecated, redis is recommended', E_USER_DEPRECATED); + + $container->setParameter('cache_pool', 'cache.memcached'); + $container->setParameter('cache_dsn', sprintf('%s:%d', $endpoint['host'], $endpoint['port'])); + + $loader = new Loader\YamlFileLoader($container, new FileLocator($projectDir . '/config/packages/cache_pool')); + $loader->load('cache.memcached.yaml'); + } + } + + // Use Redis-based sessions if possible. If a separate Redis instance + // is available, use that. If not, share a Redis instance with the + // Cache. (That should be safe to do except on especially high-traffic sites.) + if (isset($relationships['redissession'])) { + foreach ($relationships['redissession'] as $endpoint) { + if ($endpoint['scheme'] !== 'redis') { + continue; + } + + $container->setParameter('ibexa.session.handler_id', NativeSessionHandler::class); + $container->setParameter('ibexa.session.save_path', sprintf('%s:%d', $endpoint['host'], $endpoint['port'])); + } + } elseif (isset($relationships['rediscache'])) { + foreach ($relationships['rediscache'] as $endpoint) { + if ($endpoint['scheme'] !== 'redis') { + continue; + } + + $container->setParameter('ibexa.session.handler_id', NativeSessionHandler::class); + $container->setParameter('ibexa.session.save_path', sprintf('%s:%d', $endpoint['host'], $endpoint['port'])); + } + } + + if (isset($relationships['solr'])) { + foreach ($relationships['solr'] as $endpoint) { + if ($endpoint['scheme'] !== 'solr') { + continue; + } + + $container->setParameter('search_engine', 'solr'); + + $container->setParameter('solr_dsn', sprintf('http://%s:%d/%s', $endpoint['host'], $endpoint['port'], 'solr')); + // To set solr_core parameter we assume path is in form like: "solr/collection1" + $container->setParameter('solr_core', substr($endpoint['path'], 5)); + } + } + + if (isset($relationships['elasticsearch'])) { + foreach ($relationships['elasticsearch'] as $endpoint) { + $dsn = sprintf('%s:%d', $endpoint['host'], $endpoint['port']); + + if ($endpoint['username'] !== null && $endpoint['password'] !== null) { + $dsn = $endpoint['username'] . ':' . $endpoint['password'] . '@' . $dsn; + } + + if ($endpoint['path'] !== null) { + $dsn .= '/' . $endpoint['path']; + } + + $dsn = $endpoint['scheme'] . '://' . $dsn; + + $container->setParameter('search_engine', 'elasticsearch'); + $container->setParameter('elasticsearch_dsn', $dsn); + } + } + + // We will pick a varnish route by the following prioritization: + // - The first route found that has upstream: varnish + // - if primary route has upstream: varnish, that route will be prioritised + // If no route is found with upstream: varnish, then purge_server will not be set + $route = null; + foreach ($routes as $host => $info) { + if ($route === null && $info['type'] === 'upstream' && $info['upstream'] === 'varnish') { + $route = $host; + } + if ($info['type'] === 'upstream' && $info['upstream'] === 'varnish' && $info['primary'] === true) { + $route = $host; + break; + } + } + + if ($route !== null && !($_SERVER['SKIP_HTTPCACHE_PURGE'] ?? false)) { + $purgeServer = rtrim($route, '/'); + if (($_SERVER['HTTPCACHE_USERNAME'] ?? false) && ($_SERVER['HTTPCACHE_PASSWORD'] ?? false)) { + $domain = parse_url($purgeServer, PHP_URL_HOST); + $credentials = urlencode($_SERVER['HTTPCACHE_USERNAME']) . ':' . urlencode($_SERVER['HTTPCACHE_PASSWORD']); + $purgeServer = str_replace($domain, $credentials . '@' . $domain, $purgeServer); + } + + $container->setParameter('ibexa.http_cache.purge_type', 'varnish'); + $container->setParameter('purge_type', 'varnish'); + $container->setParameter('purge_server', $purgeServer); + } + // Setting default value for HTTPCACHE_VARNISH_INVALIDATE_TOKEN if it is not explicitly set + if (!($_SERVER['HTTPCACHE_VARNISH_INVALIDATE_TOKEN'] ?? false)) { + $container->setParameter('varnish_invalidate_token', $_SERVER['PLATFORM_PROJECT_ENTROPY']); + } + + // Adapt config based on enabled PHP extensions + // Get imagine to use imagick if enabled, to avoid using php memory for image conversions + if (\extension_loaded('imagick')) { + $container->setParameter('liip_imagine_driver', 'imagick'); + } + } + + private function shouldLoadTestBehatServices(ContainerBuilder $container): bool + { + return $container->hasParameter('ibexa.behat.browser.enabled') + && $container->getParameter('ibexa.behat.browser.enabled'); + } + + private function prependJMSTranslation(ContainerBuilder $container): void + { + $container->prependExtensionConfig('jms_translation', [ + 'configs' => [ + 'ibexa_core' => [ + 'dirs' => [ + __DIR__ . '/../../../', + ], + 'output_dir' => __DIR__ . '/../Resources/translations/', + 'output_format' => 'xlf', + 'excluded_dirs' => ['Test', 'Features'], + ], + ], + ]); + } +} + +class_alias(IbexaCoreExtension::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\EzPublishCoreExtension'); diff --git a/src/bundle/Core/DependencyInjection/Security/HttpBasicFactory.php b/src/bundle/Core/DependencyInjection/Security/HttpBasicFactory.php new file mode 100644 index 0000000000..ee4170d6ce --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Security/HttpBasicFactory.php @@ -0,0 +1,24 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Security; + +use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\HttpBasicFactory as BaseHttpBasicFactory; + +/** + * Basic auth based authentication provider, working with Ibexa repository. + * + * @deprecated Use http_basic in security.yml instead of ezpublish_http_basic + */ +class HttpBasicFactory extends BaseHttpBasicFactory +{ + public function getKey(): string + { + return 'ezpublish_http_basic'; + } +} + +class_alias(HttpBasicFactory::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\HttpBasicFactory'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilder.php b/src/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilder.php similarity index 79% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilder.php rename to src/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilder.php index bf53326cb4..a81e201004 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilder.php +++ b/src/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilder.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider; +namespace Ibexa\Bundle\Core\DependencyInjection\Security\PolicyProvider; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ContainerConfigBuilder; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ContainerConfigBuilder; use Symfony\Component\Config\Resource\ResourceInterface; class PoliciesConfigBuilder extends ContainerConfigBuilder @@ -15,8 +15,8 @@ public function addConfig(array $config) { $previousPolicyMap = []; - if ($this->containerBuilder->hasParameter('ezpublish.api.role.policy_map')) { - $previousPolicyMap = $this->containerBuilder->getParameter('ezpublish.api.role.policy_map'); + if ($this->containerBuilder->hasParameter('ibexa.api.role.policy_map')) { + $previousPolicyMap = $this->containerBuilder->getParameter('ibexa.api.role.policy_map'); } // We receive limitations as values, but we want them as keys to be used by isset(). @@ -33,7 +33,7 @@ public function addConfig(array $config) } $this->containerBuilder->setParameter( - 'ezpublish.api.role.policy_map', + 'ibexa.api.role.policy_map', $previousPolicyMap ); } @@ -57,3 +57,5 @@ private function policyExists(array $policyMap, $module, $function) return array_key_exists($module, $policyMap) && array_key_exists($function, $policyMap[$module]); } } + +class_alias(PoliciesConfigBuilder::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\PoliciesConfigBuilder'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Security/PolicyProvider/PolicyProviderInterface.php b/src/bundle/Core/DependencyInjection/Security/PolicyProvider/PolicyProviderInterface.php similarity index 79% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Security/PolicyProvider/PolicyProviderInterface.php rename to src/bundle/Core/DependencyInjection/Security/PolicyProvider/PolicyProviderInterface.php index 5775b52b1d..1307863012 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Security/PolicyProvider/PolicyProviderInterface.php +++ b/src/bundle/Core/DependencyInjection/Security/PolicyProvider/PolicyProviderInterface.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider; +namespace Ibexa\Bundle\Core\DependencyInjection\Security\PolicyProvider; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigBuilderInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigBuilderInterface; /** * Interface for DIC extensions which provide policies to be exposed for permissions in the Repository. @@ -16,8 +16,8 @@ * E.g. "content/read": "content" is the module, "read" is the function. * * Each function can provide a collection of limitations. - * These need to be implemented as "limitation types" and declared as services with "ezpublish.limitationType" service tag. - * Limitation types also provide value objects based on \eZ\Publish\API\Repository\Values\User\Limitation abstract class. + * These need to be implemented as "limitation types" and declared as services with "ibexa.permissions.limitation_type" service tag. + * Limitation types also provide value objects based on {@see \Ibexa\Contracts\Core\Repository\Values\User\Limitation} abstract class. * * @since 6.0 */ @@ -61,3 +61,5 @@ interface PolicyProviderInterface */ public function addPolicies(ConfigBuilderInterface $configBuilder); } + +class_alias(PolicyProviderInterface::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\PolicyProviderInterface'); diff --git a/src/bundle/Core/DependencyInjection/Security/PolicyProvider/RepositoryPolicyProvider.php b/src/bundle/Core/DependencyInjection/Security/PolicyProvider/RepositoryPolicyProvider.php new file mode 100644 index 0000000000..ede1ed3f7a --- /dev/null +++ b/src/bundle/Core/DependencyInjection/Security/PolicyProvider/RepositoryPolicyProvider.php @@ -0,0 +1,20 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\DependencyInjection\Security\PolicyProvider; + +/** + * @deprecated Deprecated since 7.1. No longer used. System policies configuration was moved to src/lib/Resources/settings/policies.yml. + */ +class RepositoryPolicyProvider extends YamlPolicyProvider +{ + public function getFiles() + { + return []; + } +} + +class_alias(RepositoryPolicyProvider::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\RepositoryPolicyProvider'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Security/PolicyProvider/YamlPolicyProvider.php b/src/bundle/Core/DependencyInjection/Security/PolicyProvider/YamlPolicyProvider.php similarity index 79% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Security/PolicyProvider/YamlPolicyProvider.php rename to src/bundle/Core/DependencyInjection/Security/PolicyProvider/YamlPolicyProvider.php index 018ef35d34..f9caa6d375 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Security/PolicyProvider/YamlPolicyProvider.php +++ b/src/bundle/Core/DependencyInjection/Security/PolicyProvider/YamlPolicyProvider.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider; +namespace Ibexa\Bundle\Core\DependencyInjection\Security\PolicyProvider; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigBuilderInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigBuilderInterface; use Symfony\Component\Config\Resource\FileResource; use Symfony\Component\Yaml\Yaml; @@ -40,3 +40,5 @@ public function addPolicies(ConfigBuilderInterface $configBuilder) */ abstract protected function getFiles(); } + +class_alias(YamlPolicyProvider::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\YamlPolicyProvider'); diff --git a/src/bundle/Core/DependencyInjection/ServiceTags.php b/src/bundle/Core/DependencyInjection/ServiceTags.php new file mode 100644 index 0000000000..15e7210492 --- /dev/null +++ b/src/bundle/Core/DependencyInjection/ServiceTags.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\DependencyInjection; + +/** + * "Enum" for the Symfony Service tag names provided by the Extension. + */ +class ServiceTags +{ + /** + * Auto-configured tag name for + * {@see \Ibexa\Contracts\Core\Repository\Values\Filter\CriterionQueryBuilder}. + */ + public const FILTERING_CRITERION_QUERY_BUILDER = 'ibexa.filter.criterion.query.builder'; + + /** + * Auto-configured tag name for + * {@see \Ibexa\Contracts\Core\Repository\Values\Filter\SortClauseQueryBuilder}. + */ + public const FILTERING_SORT_CLAUSE_QUERY_BUILDER = 'ibexa.filter.sort_clause.query.builder'; +} + +class_alias(ServiceTags::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\ServiceTags'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Entity/EntityManagerFactory.php b/src/bundle/Core/Entity/EntityManagerFactory.php similarity index 89% rename from eZ/Bundle/EzPublishCoreBundle/Entity/EntityManagerFactory.php rename to src/bundle/Core/Entity/EntityManagerFactory.php index cb227ecd36..574c15371a 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Entity/EntityManagerFactory.php +++ b/src/bundle/Core/Entity/EntityManagerFactory.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Entity; +namespace Ibexa\Bundle\Core\Entity; use Doctrine\ORM\EntityManagerInterface; -use eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider; +use Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider; use Symfony\Component\DependencyInjection\ServiceLocator; /** @@ -17,7 +17,7 @@ */ class EntityManagerFactory { - /** @var \eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider */ + /** @var \Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider */ private $repositoryConfigurationProvider; /** @var \Symfony\Component\DependencyInjection\ServiceLocator */ @@ -69,3 +69,5 @@ protected function getEntityManagerServiceId(string $connection): string return sprintf('doctrine.orm.ibexa_%s_entity_manager', $connection); } } + +class_alias(EntityManagerFactory::class, 'eZ\Bundle\EzPublishCoreBundle\Entity\EntityManagerFactory'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/BackgroundIndexingTerminateListener.php b/src/bundle/Core/EventListener/BackgroundIndexingTerminateListener.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/BackgroundIndexingTerminateListener.php rename to src/bundle/Core/EventListener/BackgroundIndexingTerminateListener.php index 30835b420d..1cefdf70fb 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/BackgroundIndexingTerminateListener.php +++ b/src/bundle/Core/EventListener/BackgroundIndexingTerminateListener.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\Core\Search\Common\BackgroundIndexer as BackgroundIndexerInterface; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Handler as PersistenceHandler; -use eZ\Publish\SPI\Search\Handler as SearchHandler; +namespace Ibexa\Bundle\Core\EventListener; + +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Handler as PersistenceHandler; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Search\Handler as SearchHandler; +use Ibexa\Core\Search\Common\BackgroundIndexer as BackgroundIndexerInterface; use Psr\Log\LoggerAwareTrait; use Psr\Log\LoggerInterface; use Symfony\Component\Console\ConsoleEvents; @@ -25,16 +25,16 @@ class BackgroundIndexingTerminateListener implements BackgroundIndexerInterface, { use LoggerAwareTrait; - /** @var \eZ\Publish\SPI\Persistence\Handler */ + /** @var \Ibexa\Contracts\Core\Persistence\Handler */ protected $persistenceHandler; - /** @var \eZ\Publish\SPI\Search\Handler */ + /** @var \Ibexa\Contracts\Core\Search\Handler */ protected $searchHandler; - /** @var \eZ\Publish\SPI\Persistence\Content\ContentInfo[] */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\ContentInfo[] */ protected $contentInfo = []; - /** @var \eZ\Publish\SPI\Persistence\Content\Location[] */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Location[] */ protected $locations = []; public function __construct(PersistenceHandler $persistenceHandler, SearchHandler $searchHandler) @@ -136,3 +136,5 @@ public function reindex() } } } + +class_alias(BackgroundIndexingTerminateListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\BackgroundIndexingTerminateListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/BackwardCompatibleCommandListener.php b/src/bundle/Core/EventListener/BackwardCompatibleCommandListener.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/BackwardCompatibleCommandListener.php rename to src/bundle/Core/EventListener/BackwardCompatibleCommandListener.php index ce5d97e8f1..615c3f1fc7 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/BackwardCompatibleCommandListener.php +++ b/src/bundle/Core/EventListener/BackwardCompatibleCommandListener.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Bundle\EzPublishCoreBundle\Command\BackwardCompatibleCommand; +use Ibexa\Bundle\Core\Command\BackwardCompatibleCommand; use Symfony\Component\Console\ConsoleEvents; use Symfony\Component\Console\Event\ConsoleCommandEvent; use Symfony\Component\Console\Style\SymfonyStyle; @@ -46,3 +46,5 @@ public function onConsoleCommand(ConsoleCommandEvent $event): void } } } + +class_alias(BackwardCompatibleCommandListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\BackwardCompatibleCommandListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/CacheViewResponseListener.php b/src/bundle/Core/EventListener/CacheViewResponseListener.php similarity index 78% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/CacheViewResponseListener.php rename to src/bundle/Core/EventListener/CacheViewResponseListener.php index b38a67f304..6cd55df609 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/CacheViewResponseListener.php +++ b/src/bundle/Core/EventListener/CacheViewResponseListener.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\View\CachableView; -use eZ\Publish\Core\MVC\Symfony\View\LocationValueView; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\View\CachableView; +use Ibexa\Core\MVC\Symfony\View\LocationValueView; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; @@ -19,7 +19,7 @@ */ class CacheViewResponseListener implements EventSubscriberInterface { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; public function __construct(ConfigResolverInterface $configResolver) @@ -57,3 +57,5 @@ public function configureCache(ResponseEvent $event) } } } + +class_alias(CacheViewResponseListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\CacheViewResponseListener'); diff --git a/src/bundle/Core/EventListener/ConfigScopeListener.php b/src/bundle/Core/EventListener/ConfigScopeListener.php new file mode 100644 index 0000000000..b9fab21cd1 --- /dev/null +++ b/src/bundle/Core/EventListener/ConfigScopeListener.php @@ -0,0 +1,74 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\EventListener; + +use Ibexa\Contracts\Core\MVC\EventSubscriber\ConfigScopeChangeSubscriber; +use Ibexa\Core\MVC\Symfony\Configuration\VersatileScopeInterface; +use Ibexa\Core\MVC\Symfony\Event\ScopeChangeEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware; +use Ibexa\Core\MVC\Symfony\View\ViewManagerInterface; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class ConfigScopeListener implements EventSubscriberInterface, ConfigScopeChangeSubscriber +{ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface[] */ + private $configResolvers; + + /** @var \Ibexa\Core\MVC\Symfony\View\ViewManagerInterface|\Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware */ + private $viewManager; + + /** @var \Ibexa\Core\MVC\Symfony\View\ViewProvider[]|\Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware[] */ + private $viewProviders; + + public function __construct( + iterable $configResolvers, + ViewManagerInterface $viewManager + ) { + $this->configResolvers = $configResolvers; + $this->viewManager = $viewManager; + } + + public static function getSubscribedEvents() + { + return [ + MVCEvents::CONFIG_SCOPE_CHANGE => ['onConfigScopeChange', 100], + MVCEvents::CONFIG_SCOPE_RESTORE => ['onConfigScopeChange', 100], + ]; + } + + public function onConfigScopeChange(ScopeChangeEvent $event): void + { + $siteAccess = $event->getSiteAccess(); + + foreach ($this->configResolvers as $configResolver) { + if ($configResolver instanceof VersatileScopeInterface) { + $configResolver->setDefaultScope($siteAccess->name); + } + } + + if ($this->viewManager instanceof SiteAccessAware) { + $this->viewManager->setSiteAccess($siteAccess); + } + + foreach ($this->viewProviders as $viewProvider) { + if ($viewProvider instanceof SiteAccessAware) { + $viewProvider->setSiteAccess($siteAccess); + } + } + } + + /** + * Sets the complete list of view providers. + */ + public function setViewProviders(array $viewProviders) + { + $this->viewProviders = $viewProviders; + } +} + +class_alias(ConfigScopeListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\ConfigScopeListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/ConsoleCommandListener.php b/src/bundle/Core/EventListener/ConsoleCommandListener.php similarity index 81% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/ConsoleCommandListener.php rename to src/bundle/Core/EventListener/ConsoleCommandListener.php index 382aab40ba..86454f7ac8 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/ConsoleCommandListener.php +++ b/src/bundle/Core/EventListener/ConsoleCommandListener.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Publish\Core\MVC\Exception\InvalidSiteAccessException; -use eZ\Publish\Core\MVC\Symfony\Event\ScopeChangeEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessAware; +use Ibexa\Core\MVC\Exception\InvalidSiteAccessException; +use Ibexa\Core\MVC\Symfony\Event\ScopeChangeEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware; use Symfony\Component\Console\ConsoleEvents; use Symfony\Component\Console\Event\ConsoleCommandEvent; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -21,13 +21,13 @@ class ConsoleCommandListener implements EventSubscriberInterface, SiteAccessAwar /** @var string */ private $defaultSiteAccessName; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface */ private $siteAccessProvider; /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ private $eventDispatcher; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess|null */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess|null */ private $siteAccess; /** @var bool */ @@ -81,3 +81,5 @@ public function setDebug($debug = false) $this->debug = $debug; } } + +class_alias(ConsoleCommandListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\ConsoleCommandListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/ContentDownloadRouteReferenceListener.php b/src/bundle/Core/EventListener/ContentDownloadRouteReferenceListener.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/ContentDownloadRouteReferenceListener.php rename to src/bundle/Core/EventListener/ContentDownloadRouteReferenceListener.php index c5e52a0adb..1f9a4d3ddb 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/ContentDownloadRouteReferenceListener.php +++ b/src/bundle/Core/EventListener/ContentDownloadRouteReferenceListener.php @@ -4,12 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\Helper\TranslationHelper; -use eZ\Publish\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\Helper\TranslationHelper; +use Ibexa\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; use InvalidArgumentException; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\OptionsResolver\Options; @@ -17,7 +18,7 @@ class ContentDownloadRouteReferenceListener implements EventSubscriberInterface { - public const ROUTE_NAME = 'ez_content_download'; + public const ROUTE_NAME = 'ibexa.content.download'; public const OPT_FIELD_IDENTIFIER = 'fieldIdentifier'; public const OPT_CONTENT = 'content'; public const OPT_CONTENT_ID = 'contentId'; @@ -27,7 +28,7 @@ class ContentDownloadRouteReferenceListener implements EventSubscriberInterface public const OPT_SITEACCESS = 'siteaccess'; public const OPT_VERSION = 'version'; - /** @var \eZ\Publish\Core\Helper\TranslationHelper */ + /** @var \Ibexa\Core\Helper\TranslationHelper */ private $translationHelper; public function __construct(TranslationHelper $translationHelper) @@ -87,7 +88,7 @@ protected function configureOptions(OptionsResolver $resolver) ] ); - $resolver->setAllowedTypes(self::OPT_CONTENT, 'eZ\Publish\API\Repository\Values\Content\Content'); + $resolver->setAllowedTypes(self::OPT_CONTENT, Content::class); $resolver->setAllowedTypes(self::OPT_FIELD_IDENTIFIER, 'string'); $resolver->setDefault( @@ -119,3 +120,5 @@ function (Options $options) { ); } } + +class_alias(ContentDownloadRouteReferenceListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\ContentDownloadRouteReferenceListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/ExceptionListener.php b/src/bundle/Core/EventListener/ExceptionListener.php similarity index 77% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/ExceptionListener.php rename to src/bundle/Core/EventListener/ExceptionListener.php index f1fcc41b76..78fb4b00d8 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/ExceptionListener.php +++ b/src/bundle/Core/EventListener/ExceptionListener.php @@ -4,14 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; use Exception; -use eZ\Publish\API\Repository\Exceptions\BadStateException; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\Core\Base\Translatable; +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Core\Base\Translatable; +use JMS\TranslationBundle\Annotation\Ignore; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\ExceptionEvent; @@ -71,9 +72,16 @@ private function getTranslatedMessage(Exception $exception) { $message = $exception->getMessage(); if ($exception instanceof Translatable) { - $message = $this->translator->trans($exception->getMessageTemplate(), $exception->getParameters(), 'repository_exceptions'); + $message = $this->translator->trans( + /** @Ignore */ + $exception->getMessageTemplate(), + $exception->getParameters(), + 'ibexa_repository_exceptions' + ); } return $message; } } + +class_alias(ExceptionListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\ExceptionListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/IndexRequestListener.php b/src/bundle/Core/EventListener/IndexRequestListener.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/IndexRequestListener.php rename to src/bundle/Core/EventListener/IndexRequestListener.php index 468c0d5374..90b9a55587 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/IndexRequestListener.php +++ b/src/bundle/Core/EventListener/IndexRequestListener.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; @@ -14,7 +14,7 @@ class IndexRequestListener implements EventSubscriberInterface { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ protected $configResolver; public function __construct(ConfigResolverInterface $configResolver) @@ -54,3 +54,5 @@ public function onKernelRequestIndex(RequestEvent $event) } } } + +class_alias(IndexRequestListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\IndexRequestListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/LocaleListener.php b/src/bundle/Core/EventListener/LocaleListener.php similarity index 83% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/LocaleListener.php rename to src/bundle/Core/EventListener/LocaleListener.php index ff9781290e..ee5c62b397 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/LocaleListener.php +++ b/src/bundle/Core/EventListener/LocaleListener.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Locale\LocaleConverterInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\FinishRequestEvent; use Symfony\Component\HttpKernel\Event\KernelEvent; @@ -15,17 +15,17 @@ use Symfony\Component\HttpKernel\EventListener\LocaleListener as BaseLocaleListener; /** - * Enhanced LocaleListener, injecting the converted locale extracted from eZ Publish configuration. + * Enhanced LocaleListener, injecting the converted locale extracted from Ibexa configuration. */ class LocaleListener implements EventSubscriberInterface { /** @var \Symfony\Component\HttpKernel\EventListener\LocaleListener */ private $innerListener; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; - /** @var \eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface */ + /** @var \Ibexa\Core\MVC\Symfony\Locale\LocaleConverterInterface */ private $localeConverter; public function __construct(BaseLocaleListener $innerListener, ConfigResolverInterface $configResolver, LocaleConverterInterface $localeConverter) @@ -67,3 +67,5 @@ public function setDefaultLocale(KernelEvent $event): void $this->innerListener->setDefaultLocale($event); } } + +class_alias(LocaleListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\LocaleListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/OriginalRequestListener.php b/src/bundle/Core/EventListener/OriginalRequestListener.php similarity index 91% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/OriginalRequestListener.php rename to src/bundle/Core/EventListener/OriginalRequestListener.php index e437ca37d0..82843293c1 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/OriginalRequestListener.php +++ b/src/bundle/Core/EventListener/OriginalRequestListener.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; @@ -49,3 +49,5 @@ public function onKernelRequest(RequestEvent $event) $request->attributes->set('_ez_original_request', $originalRequest); } } + +class_alias(OriginalRequestListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\OriginalRequestListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/PreviewRequestListener.php b/src/bundle/Core/EventListener/PreviewRequestListener.php similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/PreviewRequestListener.php rename to src/bundle/Core/EventListener/PreviewRequestListener.php index 44a1ba952b..2b095fef55 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/PreviewRequestListener.php +++ b/src/bundle/Core/EventListener/PreviewRequestListener.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Publish\Core\MVC\Symfony\Controller\Content\PreviewController; +use Ibexa\Core\MVC\Symfony\Controller\Content\PreviewController; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -45,3 +45,5 @@ public function onKernelRequest(RequestEvent $event): void } } } + +class_alias(PreviewRequestListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\PreviewRequestListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/RejectExplicitFrontControllerRequestsListener.php b/src/bundle/Core/EventListener/RejectExplicitFrontControllerRequestsListener.php similarity index 82% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/RejectExplicitFrontControllerRequestsListener.php rename to src/bundle/Core/EventListener/RejectExplicitFrontControllerRequestsListener.php index 5326895230..c58754c543 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/RejectExplicitFrontControllerRequestsListener.php +++ b/src/bundle/Core/EventListener/RejectExplicitFrontControllerRequestsListener.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -35,6 +35,12 @@ public function onKernelRequest(RequestEvent $event) } $request = $event->getRequest(); + + // Not every symfony runtime provides SCRIPT_FILENAME + if (!$request->server->has('SCRIPT_FILENAME')) { + return; + } + $scriptFileName = preg_quote(basename($request->server->get('SCRIPT_FILENAME')), '\\'); // This pattern has to match with vhost.template files in meta repository $pattern = sprintf('<^/([^/]+/)?%s([/?#]|$)>', $scriptFileName); @@ -45,3 +51,5 @@ public function onKernelRequest(RequestEvent $event) } } } + +class_alias(RejectExplicitFrontControllerRequestsListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\RejectExplicitFrontControllerRequestsListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/RequestEventListener.php b/src/bundle/Core/EventListener/RequestEventListener.php similarity index 92% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/RequestEventListener.php rename to src/bundle/Core/EventListener/RequestEventListener.php index 179142f7cb..1e49692d2c 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/RequestEventListener.php +++ b/src/bundle/Core/EventListener/RequestEventListener.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\URILexer; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\URILexer; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -23,7 +23,7 @@ class RequestEventListener implements EventSubscriberInterface /** @var \Psr\Log\LoggerInterface */ private $logger; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; /** @var string */ @@ -102,7 +102,7 @@ public function onKernelRequestForward(RequestEvent $event) * * @param \Symfony\Component\HttpKernel\Event\RequestEvent $event * - * @see \eZ\Publish\Core\MVC\Symfony\Routing\UrlAliasRouter + * @see \Ibexa\Core\MVC\Symfony\Routing\UrlAliasRouter */ public function onKernelRequestRedirect(RequestEvent $event) { @@ -143,3 +143,5 @@ public function onKernelRequestRedirect(RequestEvent $event) } } } + +class_alias(RequestEventListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\RequestEventListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/RoutingListener.php b/src/bundle/Core/EventListener/RoutingListener.php similarity index 75% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/RoutingListener.php rename to src/bundle/Core/EventListener/RoutingListener.php index daac40f292..b9a5774fad 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/RoutingListener.php +++ b/src/bundle/Core/EventListener/RoutingListener.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\Routing\Generator; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\Routing\RouterInterface; @@ -18,13 +18,13 @@ */ class RoutingListener implements EventSubscriberInterface { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; /** @var \Symfony\Component\Routing\RouterInterface */ private $urlAliasRouter; - /** @var \eZ\Publish\Core\MVC\Symfony\Routing\Generator */ + /** @var \Ibexa\Core\MVC\Symfony\Routing\Generator */ private $urlAliasGenerator; public function __construct(ConfigResolverInterface $configResolver, RouterInterface $urlAliasRouter, Generator $urlAliasGenerator) @@ -49,3 +49,5 @@ public function onSiteAccessMatch(PostSiteAccessMatchEvent $event) $this->urlAliasGenerator->setExcludedUriPrefixes($this->configResolver->getParameter('content.tree_root.excluded_uri_prefixes')); } } + +class_alias(RoutingListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\RoutingListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/SessionInitByPostListener.php b/src/bundle/Core/EventListener/SessionInitByPostListener.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/SessionInitByPostListener.php rename to src/bundle/Core/EventListener/SessionInitByPostListener.php index 5048b939de..f4f9e92a08 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/SessionInitByPostListener.php +++ b/src/bundle/Core/EventListener/SessionInitByPostListener.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Publish\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\HttpKernelInterface; @@ -49,3 +49,5 @@ public function onSiteAccessMatch(PostSiteAccessMatchEvent $event) } } } + +class_alias(SessionInitByPostListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\SessionInitByPostListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/SessionSetDynamicNameListener.php b/src/bundle/Core/EventListener/SessionSetDynamicNameListener.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/SessionSetDynamicNameListener.php rename to src/bundle/Core/EventListener/SessionSetDynamicNameListener.php index dc29b627e8..bda86b016f 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/SessionSetDynamicNameListener.php +++ b/src/bundle/Core/EventListener/SessionSetDynamicNameListener.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\SiteAccess; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage; use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageFactoryInterface; @@ -29,7 +29,7 @@ class SessionSetDynamicNameListener implements EventSubscriberInterface */ public const SESSION_NAME_PREFIX = 'eZSESSID'; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; /** @var \Symfony\Component\HttpFoundation\Session\Storage\SessionStorageFactoryInterface */ @@ -75,7 +75,7 @@ public function onSiteAccessMatch(PostSiteAccessMatchEvent $event) /** * @param string $sessionName - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess $siteAccess + * @param \Ibexa\Core\MVC\Symfony\SiteAccess $siteAccess * * @return string */ @@ -94,3 +94,5 @@ private function getSessionName($sessionName, SiteAccess $siteAccess) return $sessionName; } } + +class_alias(SessionSetDynamicNameListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\SessionSetDynamicNameListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/SiteAccessListener.php b/src/bundle/Core/EventListener/SiteAccessListener.php similarity index 93% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/SiteAccessListener.php rename to src/bundle/Core/EventListener/SiteAccessListener.php index 99c37f81b5..e35ca05a29 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/SiteAccessListener.php +++ b/src/bundle/Core/EventListener/SiteAccessListener.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Publish\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\URILexer; +use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\URILexer; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -17,7 +17,7 @@ */ class SiteAccessListener implements EventSubscriberInterface { - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ private $siteaccess; public function __construct( @@ -147,3 +147,5 @@ private function generateViewParametersArray(string $vpString): array return $viewParameters; } } + +class_alias(SiteAccessListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\SiteAccessListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/ViewControllerListener.php b/src/bundle/Core/EventListener/ViewControllerListener.php similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/ViewControllerListener.php rename to src/bundle/Core/EventListener/ViewControllerListener.php index e734fcada4..2d4169263d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/ViewControllerListener.php +++ b/src/bundle/Core/EventListener/ViewControllerListener.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Publish\Core\MVC\Symfony\View\Builder\ViewBuilderRegistry; -use eZ\Publish\Core\MVC\Symfony\View\Event\FilterViewBuilderParametersEvent; -use eZ\Publish\Core\MVC\Symfony\View\ViewEvents; use Ibexa\Contracts\Core\Event\View\PostBuildViewEvent; +use Ibexa\Core\MVC\Symfony\View\Builder\ViewBuilderRegistry; +use Ibexa\Core\MVC\Symfony\View\Event\FilterViewBuilderParametersEvent; +use Ibexa\Core\MVC\Symfony\View\ViewEvents; use Psr\Log\LoggerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -26,7 +26,7 @@ class ViewControllerListener implements EventSubscriberInterface /** @var \Psr\Log\LoggerInterface */ private $logger; - /** @var \eZ\Publish\Core\MVC\Symfony\View\Builder\ViewBuilderRegistry */ + /** @var \Ibexa\Core\MVC\Symfony\View\Builder\ViewBuilderRegistry */ private $viewBuilderRegistry; /** @var \Symfony\Component\EventDispatcher\EventDispatcher */ @@ -50,7 +50,7 @@ public static function getSubscribedEvents() } /** - * Configures the View for eZ View controllers. + * Configures the View for Ibexa View controllers. * * @param \Symfony\Component\HttpKernel\Event\ControllerEvent $event * @@ -79,3 +79,5 @@ public function getController(ControllerEvent $event) } } } + +class_alias(ViewControllerListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\ViewControllerListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventListener/ViewRendererListener.php b/src/bundle/Core/EventListener/ViewRendererListener.php similarity index 78% rename from eZ/Bundle/EzPublishCoreBundle/EventListener/ViewRendererListener.php rename to src/bundle/Core/EventListener/ViewRendererListener.php index 140fb69253..27cafda9cb 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventListener/ViewRendererListener.php +++ b/src/bundle/Core/EventListener/ViewRendererListener.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventListener; +namespace Ibexa\Bundle\Core\EventListener; -use eZ\Publish\Core\MVC\Symfony\View\Renderer as ViewRenderer; -use eZ\Publish\Core\MVC\Symfony\View\View; +use Ibexa\Core\MVC\Symfony\View\Renderer as ViewRenderer; +use Ibexa\Core\MVC\Symfony\View\View; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\ViewEvent; @@ -15,7 +15,7 @@ class ViewRendererListener implements EventSubscriberInterface { - /** @var \eZ\Publish\Core\MVC\Symfony\View\Renderer */ + /** @var \Ibexa\Core\MVC\Symfony\View\Renderer */ private $viewRenderer; public function __construct(ViewRenderer $viewRenderer) @@ -43,3 +43,5 @@ public function renderView(ViewEvent $event) $event->setResponse($response); } } + +class_alias(ViewRendererListener::class, 'eZ\Bundle\EzPublishCoreBundle\EventListener\ViewRendererListener'); diff --git a/eZ/Bundle/EzPublishCoreBundle/EventSubscriber/CrowdinRequestLocaleSubscriber.php b/src/bundle/Core/EventSubscriber/CrowdinRequestLocaleSubscriber.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/EventSubscriber/CrowdinRequestLocaleSubscriber.php rename to src/bundle/Core/EventSubscriber/CrowdinRequestLocaleSubscriber.php index 2ca604fc7e..c6e6e39ea6 100644 --- a/eZ/Bundle/EzPublishCoreBundle/EventSubscriber/CrowdinRequestLocaleSubscriber.php +++ b/src/bundle/Core/EventSubscriber/CrowdinRequestLocaleSubscriber.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\EventSubscriber; +namespace Ibexa\Bundle\Core\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -34,3 +34,5 @@ public function setInContextAcceptLanguage(RequestEvent $e) $e->getRequest()->headers->set('accept-language', 'ach-UG'); } } + +class_alias(CrowdinRequestLocaleSubscriber::class, 'eZ\Bundle\EzPublishCoreBundle\EventSubscriber\CrowdinRequestLocaleSubscriber'); diff --git a/src/bundle/Core/EventSubscriber/TrustedHeaderClientIpEventSubscriber.php b/src/bundle/Core/EventSubscriber/TrustedHeaderClientIpEventSubscriber.php new file mode 100644 index 0000000000..cff112d523 --- /dev/null +++ b/src/bundle/Core/EventSubscriber/TrustedHeaderClientIpEventSubscriber.php @@ -0,0 +1,67 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\EventSubscriber; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpKernel\Event\RequestEvent; +use Symfony\Component\HttpKernel\KernelEvents; + +final class TrustedHeaderClientIpEventSubscriber implements EventSubscriberInterface +{ + private const PLATFORM_SH_TRUSTED_HEADER_CLIENT_IP = 'X-Client-IP'; + + private ?string $trustedHeaderName; + + public function __construct( + ?string $trustedHeaderName + ) { + $this->trustedHeaderName = $trustedHeaderName; + } + + public static function getSubscribedEvents(): array + { + return [ + KernelEvents::REQUEST => ['onKernelRequest', PHP_INT_MAX], + ]; + } + + public function onKernelRequest(RequestEvent $event): void + { + $request = $event->getRequest(); + + $trustedProxies = Request::getTrustedProxies(); + $trustedHeaderSet = Request::getTrustedHeaderSet(); + + $trustedHeaderName = $this->trustedHeaderName; + if (null === $trustedHeaderName && $this->isPlatformShProxy($request)) { + $trustedHeaderName = self::PLATFORM_SH_TRUSTED_HEADER_CLIENT_IP; + } + + if (null === $trustedHeaderName) { + return; + } + + $trustedClientIp = $request->headers->get($trustedHeaderName); + + if (null !== $trustedClientIp) { + if ($trustedHeaderSet !== -1) { + $trustedHeaderSet |= Request::HEADER_X_FORWARDED_FOR; + } + $request->headers->set('X_FORWARDED_FOR', $trustedClientIp); + } + + Request::setTrustedProxies($trustedProxies, $trustedHeaderSet); + } + + private function isPlatformShProxy(Request $request): bool + { + return null !== $request->server->get('PLATFORM_RELATIONSHIPS'); + } +} diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Console/console.feature b/src/bundle/Core/Features/Console/console.feature similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Features/Console/console.feature rename to src/bundle/Core/Features/Console/console.feature diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Content/content_preview.feature b/src/bundle/Core/Features/Content/content_preview.feature similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Features/Content/content_preview.feature rename to src/bundle/Core/Features/Content/content_preview.feature diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Context/BasicContentContext.php b/src/bundle/Core/Features/Context/BasicContentContext.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/Features/Context/BasicContentContext.php rename to src/bundle/Core/Features/Context/BasicContentContext.php index ac78b6b2f5..46100998a6 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/Context/BasicContentContext.php +++ b/src/bundle/Core/Features/Context/BasicContentContext.php @@ -4,12 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Features\Context; +namespace Ibexa\Bundle\Core\Features\Context; use Behat\Behat\Context\Context; -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Repository; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Core\Repository\Values\Content\Content; /** * Sentences for simple Contents creation. @@ -26,13 +27,13 @@ class BasicContentContext implements Context */ private $contentPaths = []; - /** @var \eZ\Publish\API\Repository\ContentTypeService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */ private $contentTypeService; - /** @var \eZ\Publish\API\Repository\ContentService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentService */ private $contentService; - /** @var \eZ\Publish\API\Repository\Repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository */ private $repository; public function __construct( @@ -65,8 +66,6 @@ public function createContent($contentType, $fields, $parentLocationId) /** * Publishes a content draft. - * - * @param eZ\Publish\API\Repository\Values\Content\Content $content */ public function publishDraft(Content $content) { @@ -76,12 +75,12 @@ public function publishDraft(Content $content) /** * Creates a content draft. * - * @param eZ\Publish\API\Repository\Values\Content\Location $parentLocationId + * @param int $parentLocationId * @param string $contentTypeIdentifier * @param string $languageCode * @param array $fields Fields, as primitives understood by setField * - * @return eZ\Publish\API\Repository\Values\Content\Content an unpublished Content draft + * @return \Ibexa\Core\Repository\Values\Content\Content an unpublished Content draft */ public function createContentDraft($parentLocationId, $contentTypeIdentifier, $fields, $languageCode = null) { @@ -191,6 +190,8 @@ private function getTitleFromPath($path) */ private function getDummyXmlText() { - return '<?xml version="1.0" encoding="UTF-8"?><section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"><para>This is a paragraph.</para></section>'; + return '<?xml version="1.0" encoding="UTF-8"?><section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ibexa.co/xmlns/dxp/docbook/xhtml" xmlns:ezcustom="http://ibexa.co/xmlns/dxp/docbook/custom" version="5.0-variant ezpublish-1.0"><para>This is a paragraph.</para></section>'; } } + +class_alias(BasicContentContext::class, 'eZ\Bundle\EzPublishCoreBundle\Features\Context\BasicContentContext'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Context/ConsoleContext.php b/src/bundle/Core/Features/Context/ConsoleContext.php similarity index 93% rename from eZ/Bundle/EzPublishCoreBundle/Features/Context/ConsoleContext.php rename to src/bundle/Core/Features/Context/ConsoleContext.php index 6064e05b77..1bb0cfc1e9 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/Context/ConsoleContext.php +++ b/src/bundle/Core/Features/Context/ConsoleContext.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Features\Context; +namespace Ibexa\Bundle\Core\Features\Context; use Behat\Behat\Context\Context; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use PHPUnit\Framework\Assert as Assertion; use Symfony\Component\Process\PhpExecutableFinder; use Symfony\Component\Process\Process; class ConsoleContext implements Context { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; /** @var string[] */ @@ -33,7 +33,7 @@ class ConsoleContext implements Context private $it = []; /** - * @param \eZ\Publish\Core\MVC\ConfigResolverInterface $configResolver + * @param \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface $configResolver * @param string[] $siteaccessList * @param string $defaultSiteaccess */ @@ -196,7 +196,7 @@ private function getNonDefaultSiteaccessName() } /** - * @return \eZ\Publish\Core\MVC\ConfigResolverInterface + * @return \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private function getConfigResolver() { @@ -208,3 +208,5 @@ private function getDefaultSiteaccessName() return $this->defaultSiteaccess; } } + +class_alias(ConsoleContext::class, 'eZ\Bundle\EzPublishCoreBundle\Features\Context\ConsoleContext'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Context/ContentContext.php b/src/bundle/Core/Features/Context/ContentContext.php similarity index 83% rename from eZ/Bundle/EzPublishCoreBundle/Features/Context/ContentContext.php rename to src/bundle/Core/Features/Context/ContentContext.php index 4f29045f7f..3202babf06 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/Context/ContentContext.php +++ b/src/bundle/Core/Features/Context/ContentContext.php @@ -4,23 +4,23 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Features\Context; +namespace Ibexa\Bundle\Core\Features\Context; use Behat\Behat\Context\Context; use Behat\Behat\Context\SnippetAcceptingContext; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; use RuntimeException; class ContentContext implements Context, SnippetAcceptingContext { - /** @var \eZ\Publish\API\Repository\Values\Content\Content */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content */ private $currentContent; - /** @var \eZ\Publish\API\Repository\Values\Content\Content */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content */ private $currentDraft; - /** @var \eZ\Publish\API\Repository\Repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository */ private $repository; public function __construct(Repository $repository) @@ -37,7 +37,7 @@ public function iCreateAnFolderDraft() 'folder', [ 'name' => 'Preview draft ' . date('c'), - 'short_description' => '<?xml version="1.0" encoding="UTF-8"?><section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"><para>This is a paragraph.</para></section>', + 'short_description' => '<?xml version="1.0" encoding="UTF-8"?><section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ibexa.co/xmlns/dxp/docbook/xhtml" xmlns:ezcustom="http://ibexa.co/xmlns/dxp/docbook/custom" version="5.0-variant ezpublish-1.0"><para>This is a paragraph.</para></section>', ] ); } @@ -62,7 +62,7 @@ public function iCreateADraftOfAnExistingContentItem() * @param string $contentTypeIdentifier * @param array $fields Hash of field def identifier => field value * - * @return \eZ\Publish\API\Repository\Values\Content\Content the created content item. + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content the created content item. */ public function createContentItem($contentTypeIdentifier, array $fields) { @@ -125,7 +125,7 @@ public function updateDraft($fields) * @param string $contentTypeIdentifier * @param array $fields Hash of field def identifier => field value * - * @return \eZ\Publish\API\Repository\Values\Content\Content the created draft. + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content the created draft. */ public function createDraft($contentTypeIdentifier, array $fields) { @@ -154,3 +154,5 @@ function () use ($createStruct, $locationCreateStruct) { return $this->currentDraft; } } + +class_alias(ContentContext::class, 'eZ\Bundle\EzPublishCoreBundle\Features\Context\ContentContext'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Context/ContentPreviewContext.php b/src/bundle/Core/Features/Context/ContentPreviewContext.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/Features/Context/ContentPreviewContext.php rename to src/bundle/Core/Features/Context/ContentPreviewContext.php index 2bcf7c2de6..f4bb0678bb 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/Context/ContentPreviewContext.php +++ b/src/bundle/Core/Features/Context/ContentPreviewContext.php @@ -4,16 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Features\Context; +namespace Ibexa\Bundle\Core\Features\Context; use Behat\Behat\Hook\Scope\BeforeScenarioScope; use Behat\MinkExtension\Context\RawMinkContext; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; use PHPUnit\Framework\Assert as Assertion; class ContentPreviewContext extends RawMinkContext { - /** @var \eZ\Bundle\EzPublishCoreBundle\Features\Context\ContentContext */ + /** @var \Ibexa\Bundle\Core\Features\Context\ContentContext */ private $contentContext; /** @BeforeScenario */ @@ -21,7 +21,7 @@ public function gatherContexts(BeforeScenarioScope $scope) { $environment = $scope->getEnvironment(); - $this->contentContext = $environment->getContext('eZ\Bundle\EzPublishCoreBundle\Features\Context\ContentContext'); + $this->contentContext = $environment->getContext(ContentContext::class); } /** @@ -33,7 +33,7 @@ public function iCreateDraftOfContentTypeWithCustomLocationController() 'blog_post', [ 'title' => 'Preview draft ' . date('c'), - 'body' => '<?xml version="1.0" encoding="UTF-8"?><section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ez.no/xmlns/ezpublish/docbook/xhtml" xmlns:ezcustom="http://ez.no/xmlns/ezpublish/docbook/custom" version="5.0-variant ezpublish-1.0"><para>This is a paragraph.</para></section>', + 'body' => '<?xml version="1.0" encoding="UTF-8"?><section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ezxhtml="http://ibexa.co/xmlns/dxp/docbook/xhtml" xmlns:ezcustom="http://ibexa.co/xmlns/dxp/docbook/custom" version="5.0-variant ezpublish-1.0"><para>This is a paragraph.</para></section>', ] ); } @@ -113,3 +113,5 @@ public function iModifyAFieldFromTheDraft() ); } } + +class_alias(ContentPreviewContext::class, 'eZ\Bundle\EzPublishCoreBundle\Features\Context\ContentPreviewContext'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Context/ContentTypeContext.php b/src/bundle/Core/Features/Context/ContentTypeContext.php similarity index 81% rename from eZ/Bundle/EzPublishCoreBundle/Features/Context/ContentTypeContext.php rename to src/bundle/Core/Features/Context/ContentTypeContext.php index 3edf8e5f89..e67fb58664 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/Context/ContentTypeContext.php +++ b/src/bundle/Core/Features/Context/ContentTypeContext.php @@ -4,16 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Features\Context; +namespace Ibexa\Bundle\Core\Features\Context; use Behat\Behat\Context\Context; use Behat\Gherkin\Node\TableNode; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Exceptions as ApiExceptions; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Exceptions as ApiExceptions; use PHPUnit\Framework\Assert as Assertion; /** - * Sentences for Content Types. + * Sentences for content types. */ class ContentTypeContext implements Context { @@ -27,20 +27,17 @@ class ContentTypeContext implements Context */ public const DEFAULT_LANGUAGE = 'eng-GB'; - /** @var \eZ\Publish\API\Repository\ContentTypeService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */ protected $contentTypeService; - /** @var \EzSystems\BehatBundle\Context\Object\ContentTypeGroup */ - protected $contentTypeGroupContext; - public function __construct(ContentTypeService $contentTypeService) { $this->contentTypeService = $contentTypeService; } /** - * @Given (that) a Content Type exists with identifier :identifier with fields: - * @Given (that) a Content Type exists with identifier :identifier in Group with identifier :groupIdentifier with fields: + * @Given (that) a content type exists with identifier :identifier with fields: + * @Given (that) a content type exists with identifier :identifier in Group with identifier :groupIdentifier with fields: * | Identifier | Type | Name | * | title | ezstring | Title | * | body | ezxml | Body | @@ -63,7 +60,7 @@ public function ensureContentTypeWithIndentifier( } /** - * @Given (that) a Content Type does not exist with identifier :identifier + * @Given (that) a content type does not exist with identifier :identifier * * Makes sure a content type with $identifier does not exist. * If it exists deletes it. @@ -77,7 +74,7 @@ public function ensureContentTypeDoesntExist($identifier) } /** - * @Then Content Type (with identifier) :identifier exists + * @Then content type (with identifier) :identifier exists * * Verifies that a content type with $identifier exists. */ @@ -85,12 +82,12 @@ public function assertContentTypeExistsByIdentifier($identifier) { Assertion::assertTrue( $this->checkContentTypeExistenceByIdentifier($identifier), - "Couldn't find a Content Type with identifier '$identifier'." + "Couldn't find a content type with identifier '$identifier'." ); } /** - * @Then Content Type (with identifier) :identifier does not exist + * @Then content type (with identifier) :identifier does not exist * * Verifies that a content type with $identifier does not exist. */ @@ -98,12 +95,12 @@ public function assertContentTypeDoesntExistsByIdentifier($identifier) { Assertion::assertFalse( $this->checkContentTypeExistenceByIdentifier($identifier), - "Found a Content Type with identifier '$identifier'." + "Found a content type with identifier '$identifier'." ); } /** - * @Then Content Type (with identifier) :identifier exists in Group with identifier :groupIdentifier + * @Then content type (with identifier) :identifier exists in Group with identifier :groupIdentifier * * Verifies that a content type with $identifier exists in group with identifier $groupIdentifier. */ @@ -111,17 +108,17 @@ public function assertContentTypeExistsByIdentifierOnGroup($identifier, $groupId { Assertion::assertTrue( $this->checkContentTypeExistenceByIdentifier($identifier, $groupIdentifier), - "Couldn't find Content Type with identifier '$identifier' on '$groupIdentifier." + "Couldn't find content type with identifier '$identifier' on '$groupIdentifier." ); } /** * Load and return a content type by its identifier. * - * @param string $identifier content type identifier + * @param string $identifier Content type identifier * @param bool $throwIfNotFound if true, throws an exception if it is not found. * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup|null + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup|null */ protected function loadContentTypeByIdentifier($identifier, $throwIfNotFound = true) { @@ -143,11 +140,11 @@ protected function loadContentTypeByIdentifier($identifier, $throwIfNotFound = t * Creates a content type with $identifier on content type group with identifier $groupIdentifier and with the * given 'fields' definitions. * - * @param string $groupIdentifier content type group identifier - * @param string $identifier content type identifier - * @param array $fields content type fields definitions + * @param string $groupIdentifier Content type group identifier + * @param string $identifier Content type identifier + * @param array $fields Content type fields definitions * - * @return eZ\Publish\API\Repository\Values\ContentType\ContentType + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType */ public function createContentType($groupIdentifier, $identifier, $fields) { @@ -195,7 +192,7 @@ public function createContentType($groupIdentifier, $identifier, $fields) /** * Remove the given 'ContentType' object. * - * @param eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType */ protected function removeContentType($contentType) { @@ -207,8 +204,8 @@ protected function removeContentType($contentType) } /** - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup $contentTypeGroup + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup $contentTypeGroup */ protected function assignContentGroupTypeToContentType($contentType, $contentTypeGroup) { @@ -243,3 +240,5 @@ protected function checkContentTypeExistenceByIdentifier($identifier, $groupIden return $contentType ? true : false; } } + +class_alias(ContentTypeContext::class, 'eZ\Bundle\EzPublishCoreBundle\Features\Context\ContentTypeContext'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Context/ExceptionContext.php b/src/bundle/Core/Features/Context/ExceptionContext.php similarity index 92% rename from eZ/Bundle/EzPublishCoreBundle/Features/Context/ExceptionContext.php rename to src/bundle/Core/Features/Context/ExceptionContext.php index b66ead0fef..18043fb104 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/Context/ExceptionContext.php +++ b/src/bundle/Core/Features/Context/ExceptionContext.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Features\Context; +namespace Ibexa\Bundle\Core\Features\Context; use Behat\Behat\Context\Context; use Behat\Behat\Context\SnippetAcceptingContext; @@ -64,3 +64,5 @@ public function anAccessDeniedExceptionIsThrown($exceptionString) $this->assertSession()->elementExists('xpath', "//abbr[@title='$exceptionString']"); } } + +class_alias(ExceptionContext::class, 'eZ\Bundle\EzPublishCoreBundle\Features\Context\ExceptionContext'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Context/FieldTypeContext.php b/src/bundle/Core/Features/Context/FieldTypeContext.php similarity index 93% rename from eZ/Bundle/EzPublishCoreBundle/Features/Context/FieldTypeContext.php rename to src/bundle/Core/Features/Context/FieldTypeContext.php index ffbf3508dc..9ce8cd3b85 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/Context/FieldTypeContext.php +++ b/src/bundle/Core/Features/Context/FieldTypeContext.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Features\Context; +namespace Ibexa\Bundle\Core\Features\Context; use Behat\Behat\Context\Context; use Behat\Gherkin\Node\TableNode; -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\LocationService; /** * Sentences for Fields. @@ -58,13 +58,13 @@ class FieldTypeContext implements Context 'integer' => 1, ]; - /** @var \eZ\Publish\API\Repository\ContentTypeService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */ private $contentTypeService; - /** @var \eZ\Publish\API\Repository\ContentService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentService */ private $contentService; - /** @var \eZ\Publish\API\Repository\LocationService */ + /** @var \Ibexa\Contracts\Core\Repository\LocationService */ private $locationService; public function __construct( @@ -339,8 +339,8 @@ private function getActualFieldPosition() } /** - * @Given a Content Type with an :fieldType field exists - * @Given a Content Type with an :fieldType with field definition name :name exists + * @Given a content type with an :fieldType field exists + * @Given a content type with an :fieldType with field definition name :name exists * * Creates a ContentType with only the desired FieldType. */ @@ -350,8 +350,8 @@ public function createContentTypeWithFieldType($fieldType, $name = null) } /** - * @Given a Content Type with a required :fieldType field exists - * @Given a Content Type with a required :fieldType with field definition name :name exists + * @Given a content type with a required :fieldType field exists + * @Given a content type with a required :fieldType with field definition name :name exists * * Creates a ContentType with only the desired FieldType. */ @@ -372,8 +372,8 @@ public function createContentOfThisType($field = null, $value = null) } /** - * @Given a Content Type with an :fieldType field exists with Properties: - * @Given a Content Type with an :fieldType field with name :name exists with Properties: + * @Given a content type with an :fieldType field exists with Properties: + * @Given a content type with an :fieldType field with name :name exists with Properties: */ public function createContentOfThisTypeWithProperties($fieldType, TableNode $properties, $name = null) { @@ -387,3 +387,5 @@ public function createContentOfThisTypeWithProperties($fieldType, TableNode $pro } } } + +class_alias(FieldTypeContext::class, 'eZ\Bundle\EzPublishCoreBundle\Features\Context\FieldTypeContext'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Context/QueryControllerContext.php b/src/bundle/Core/Features/Context/QueryControllerContext.php similarity index 94% rename from eZ/Bundle/EzPublishCoreBundle/Features/Context/QueryControllerContext.php rename to src/bundle/Core/Features/Context/QueryControllerContext.php index bb21773509..225ef2210c 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/Context/QueryControllerContext.php +++ b/src/bundle/Core/Features/Context/QueryControllerContext.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Features\Context; +namespace Ibexa\Bundle\Core\Features\Context; use Behat\MinkExtension\Context\RawMinkContext; use PHPUnit\Framework\Assert; @@ -90,3 +90,5 @@ private function getVariableTypesFromTemplate(): array return $items; } } + +class_alias(QueryControllerContext::class, 'eZ\Bundle\EzPublishCoreBundle\Features\Context\QueryControllerContext'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Context/RoleContext.php b/src/bundle/Core/Features/Context/RoleContext.php similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/Features/Context/RoleContext.php rename to src/bundle/Core/Features/Context/RoleContext.php index 2c04bed61d..526215d06f 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/Context/RoleContext.php +++ b/src/bundle/Core/Features/Context/RoleContext.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Features\Context; +namespace Ibexa\Bundle\Core\Features\Context; use Behat\Behat\Context\Context; -use eZ\Publish\API\Repository\Exceptions as ApiExceptions; -use eZ\Publish\API\Repository\RoleService; +use Ibexa\Contracts\Core\Repository\Exceptions as ApiExceptions; +use Ibexa\Contracts\Core\Repository\RoleService; use PHPUnit\Framework\Assert as Assertion; /** @@ -16,7 +16,7 @@ */ class RoleContext implements Context { - /** @var \eZ\Publish\API\Repository\roleService */ + /** @var \Ibexa\Contracts\Core\Repository\roleService */ protected $roleService; public function __construct(RoleService $roleService) @@ -29,7 +29,7 @@ public function __construct(RoleService $roleService) * * @param string $name Role identifier * - * @return \eZ\Publish\API\Repository\Values\User\Role + * @return \Ibexa\Contracts\Core\Repository\Values\User\Role */ public function ensureRoleExists($name) { @@ -50,7 +50,7 @@ public function ensureRoleExists($name) * * @param string $identifier Role identifier * - * @return \eZ\Publish\API\Repository\Values\User\Role + * @return \Ibexa\Contracts\Core\Repository\Values\User\Role */ public function getRole($identifier) { @@ -69,7 +69,7 @@ public function getRole($identifier) * * Ensures a role exists with name ':name', creating a new one if necessary. * - * @return \eZ\Publish\API\Repository\Values\User\Role + * @return \Ibexa\Contracts\Core\Repository\Values\User\Role */ public function iHaveRole($name) { @@ -132,3 +132,5 @@ public function iDontSeeRole($name) ); } } + +class_alias(RoleContext::class, 'eZ\Bundle\EzPublishCoreBundle\Features\Context\RoleContext'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Context/UserContext.php b/src/bundle/Core/Features/Context/UserContext.php similarity index 90% rename from eZ/Bundle/EzPublishCoreBundle/Features/Context/UserContext.php rename to src/bundle/Core/Features/Context/UserContext.php index 885ac2569b..e239bcf462 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/Context/UserContext.php +++ b/src/bundle/Core/Features/Context/UserContext.php @@ -4,16 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Features\Context; +namespace Ibexa\Bundle\Core\Features\Context; use Behat\Behat\Context\Context; use Behat\Gherkin\Node\TableNode; -use eZ\Publish\API\Repository\Exceptions as ApiExceptions; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Exceptions as ApiExceptions; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; use PHPUnit\Framework\Assert as Assertion; /** @@ -24,7 +24,7 @@ class UserContext implements Context public const DEFAULT_LANGUAGE = 'eng-GB'; /** - * These values are set by the default eZ Publish installation. + * These values are set by the default Ibexa installation. */ public const USER_IDENTIFIER = 'user'; @@ -33,10 +33,10 @@ class UserContext implements Context public const USERGROUP_ROOT_SUBTREE = '/1/5/'; public const USERGROUP_CONTENT_IDENTIFIER = 'user_group'; - /** @var \eZ\Publish\API\Repository\UserService */ + /** @var \Ibexa\Contracts\Core\Repository\UserService */ protected $userService; - /** @var \eZ\Publish\API\Repository\SearchService */ + /** @var \Ibexa\Contracts\Core\Repository\SearchService */ protected $searchService; public function __construct(UserService $userService, SearchService $searchService) @@ -51,7 +51,7 @@ public function __construct(UserService $userService, SearchService $searchServi * @param string $username name of User to search for * @param string $parentGroupLocationId where to search, in User Group tree * - * @return User found + * @return \Ibexa\Core\Repository\Values\User\User found */ public function searchUserByLogin($username, $parentGroupId = null) { @@ -82,7 +82,7 @@ public function searchUserByLogin($username, $parentGroupId = null) * @param string $name name of User Group to search for * @param string $parentLocationId (optional) parent location id to search in * - * @return search results + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] search results */ public function searchUserGroups($name, $parentLocationId = null) { @@ -105,12 +105,12 @@ public function searchUserGroups($name, $parentLocationId = null) /** * Create user inside given User Group; DELETES existing User if login already exists! * - * @param $username username of the user to create - * @param $email email address of user to create - * @param $password account password for user to create - * @param $parentGroup pathstring wherein to create user + * @param string $username username of the user to create + * @param string $email email address of user to create + * @param string $password account password for user to create + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $parentGroup * - * @return eZ\Publish\API\Repository\Values\User\User + * @return \Ibexa\Contracts\Core\Repository\Values\User\User */ protected function createUser($username, $email, $password, $parentGroup = null, $fields = []) { @@ -147,9 +147,9 @@ protected function createUser($username, $email, $password, $parentGroup = null, * Create new User Group inside existing parent User Group. * * @param string $name User Group name - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $parentGroup (optional) parent user group, defaults to UserGroup "/Users" + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $parentGroup (optional) parent user group, defaults to UserGroup "/Users" * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup */ public function createUserGroup($name, $parentGroup = null) { @@ -171,7 +171,7 @@ public function createUserGroup($name, $parentGroup = null) * @param string $password User's password * @param string $parentGroupName (optional) name of the parent group to check * - * @return \eZ\Publish\API\Repository\Values\User\User + * @return \Ibexa\Contracts\Core\Repository\Values\User\User */ public function ensureUserExists($username, $email, $password, $parentGroupName = null) { @@ -328,7 +328,7 @@ public function createPasswordHash($login, $password, $type) * * Ensures a user with username ':username' exists, creating a new one if necessary. * - * @return \eZ\Publish\API\Repository\Values\User\User + * @return \Ibexa\Contracts\Core\Repository\Values\User\User */ public function iHaveUser($username) { @@ -342,7 +342,7 @@ public function iHaveUser($username) * * Ensures a user exists with given username/email/password, creating a new one if necessary. * - * @return \eZ\Publish\API\Repository\Values\User\User + * @return \Ibexa\Contracts\Core\Repository\Values\User\User */ public function iHaveUserWithUsernameEmailAndPassword($username, $email, $password) { @@ -354,7 +354,7 @@ public function iHaveUserWithUsernameEmailAndPassword($username, $email, $passwo * * Ensures a user with username ':username' exists as a child of ':parentGroup' user group, can create either one. * - * @return \eZ\Publish\API\Repository\Values\User\User + * @return \Ibexa\Contracts\Core\Repository\Values\User\User */ public function iHaveUserInGroup($username, $parentGroupName) { @@ -368,7 +368,7 @@ public function iHaveUserInGroup($username, $parentGroupName) * * Ensures a user with given username/email/password as a child of ':parentGroup' user group, can create either one. * - * @return \eZ\Publish\API\Repository\Values\User\User + * @return \Ibexa\Contracts\Core\Repository\Values\User\User */ public function iHaveUserWithUsernameEmailAndPasswordInGroup($username, $email, $password, $parentGroupName) { @@ -475,7 +475,7 @@ public function assertUserWithNameDoesntExistInGroup($username, $parentGroup) * Checks that user ':username' does not exist in any of the provided groups. Example: * | parentGroup | * | Partners | - * | Anonymous Users | + * | Anonymous users | * | Editors | * | Administrator users | */ @@ -498,7 +498,7 @@ public function assertUserWithNameDoesntExistInGroups($username, TableNode $tabl * * Checks that user ':username' exists with the values provided in the field/value table. example: * | Name | value | - * | email | testuser@ez.no | + * | email | testuser@ibexa.co | * | password | testuser | * | first_name | Test | * | last_name | User | @@ -544,13 +544,13 @@ public function assertUserWithNameExistsWithFields($username, TableNode $table) */ private function findNonExistingUserEmail($username = 'User') { - $email = "{$username}@ez.no"; + $email = "{$username}@ibexa.co"; if ($this->checkUserExistenceByEmail($email)) { return $email; } for ($i = 0; $i < 20; ++$i) { - $email = uniqid('User#', true) . '@ez.no'; + $email = uniqid('User#', true) . '@ibexa.co'; if (!$this->checkUserExistenceByEmail($email)) { return $email; } @@ -578,3 +578,5 @@ private function findNonExistingUserName() throw new \Exception('Possible endless loop when attempting to find a new name for the User.'); } } + +class_alias(UserContext::class, 'eZ\Bundle\EzPublishCoreBundle\Features\Context\UserContext'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Context/YamlConfigurationContext.php b/src/bundle/Core/Features/Context/YamlConfigurationContext.php similarity index 95% rename from eZ/Bundle/EzPublishCoreBundle/Features/Context/YamlConfigurationContext.php rename to src/bundle/Core/Features/Context/YamlConfigurationContext.php index b8f80c1c58..7526fec88b 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/Context/YamlConfigurationContext.php +++ b/src/bundle/Core/Features/Context/YamlConfigurationContext.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Features\Context; +namespace Ibexa\Bundle\Core\Features\Context; use Behat\Behat\Context\Context; use RuntimeException; @@ -115,3 +115,5 @@ public function clearSymfonyCache(): void $application->run($input); } } + +class_alias(YamlConfigurationContext::class, 'eZ\Bundle\EzPublishCoreBundle\Features\Context\YamlConfigurationContext'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/Exception/unauthorized_login_form.feature b/src/bundle/Core/Features/Exception/unauthorized_login_form.feature similarity index 88% rename from eZ/Bundle/EzPublishCoreBundle/Features/Exception/unauthorized_login_form.feature rename to src/bundle/Core/Features/Exception/unauthorized_login_form.feature index 78bbfc32e4..7f2505321f 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/Exception/unauthorized_login_form.feature +++ b/src/bundle/Core/Features/Exception/unauthorized_login_form.feature @@ -7,5 +7,5 @@ Feature: Handling of Unauthorized repository exceptions Scenario: When a Repository UnauthorizedException is throw, authenticated users are shown the exception Given that I am logged in When a repository UnauthorizedException is thrown during an HTTP request - Then an eZ\Publish\Core\Base\Exceptions\UnauthorizedException is displayed + Then an Ibexa\Core\Base\Exceptions\UnauthorizedException is displayed And an Symfony\Component\Security\Core\Exception\AccessDeniedException is displayed diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/QueryController/query_controller.feature b/src/bundle/Core/Features/QueryController/query_controller.feature similarity index 95% rename from eZ/Bundle/EzPublishCoreBundle/Features/QueryController/query_controller.feature rename to src/bundle/Core/Features/QueryController/query_controller.feature index cf24ab542b..f946c637e8 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/QueryController/query_controller.feature +++ b/src/bundle/Core/Features/QueryController/query_controller.feature @@ -9,7 +9,7 @@ Scenario: A content view can be configured to run and render a query Scenario: A content view can be configured to run and render a query and return a PagerFanta Object When I go to "QueryControllerContainer/QueryControllerItem2" - Then the Query results assigned to the "children" twig variable is a "eZ\Publish\Core\Pagination\Pagerfanta\Pagerfanta" object + Then the Query results assigned to the "children" twig variable is a "Ibexa\Core\Pagination\Pagerfanta\Pagerfanta" object Scenario: A content view can be configured to run and render a query return a PagerFanta Object and set limit and page name When I go to "QueryControllerContainer/QueryControllerItem3" diff --git a/eZ/Bundle/EzPublishCoreBundle/Features/QueryController/setup.feature b/src/bundle/Core/Features/QueryController/setup.feature similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/Features/QueryController/setup.feature rename to src/bundle/Core/Features/QueryController/setup.feature index be1f1569d0..2bbc7a10fc 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Features/QueryController/setup.feature +++ b/src/bundle/Core/Features/QueryController/setup.feature @@ -18,10 +18,10 @@ Feature: Query controller And I append configuration to "default" siteaccess under "content_view.full" key """ query_controller_item_1: - template: "@eZBehat/tests/dump.html.twig" + template: "@IbexaBehat/tests/dump.html.twig" match: Id\Location: "%location_id(QueryControllerContainer/QueryControllerItem1)%" - controller: ez_query:locationQueryAction + controller: ibexa_query:locationQueryAction params: query: query_type: 'LocationChildren' @@ -29,10 +29,10 @@ Feature: Query controller parentLocationId: 2 assign_results_to: 'children' query_controller_item_2: - template: "@eZBehat/tests/dump.html.twig" + template: "@IbexaBehat/tests/dump.html.twig" match: Id\Location: "%location_id(QueryControllerContainer/QueryControllerItem2)%" - controller: ez_query:pagingQueryAction + controller: ibexa_query:pagingQueryAction params: query: query_type: 'LocationChildren' @@ -43,7 +43,7 @@ Feature: Query controller template: tests.html.twig match: Id\Location: "%location_id(QueryControllerContainer/QueryControllerItem3)%" - controller: ez_query:pagingQueryAction + controller: ibexa_query:pagingQueryAction params: query: query_type: 'LocationChildren' @@ -55,7 +55,7 @@ Feature: Query controller template: tests.html.twig match: Id\Location: "%location_id(QueryControllerContainer/QueryControllerItem4)%" - controller: ez_query:pagingQueryAction + controller: ibexa_query:pagingQueryAction params: query: query_type: 'LocationChildren' @@ -70,9 +70,9 @@ Feature: Query controller <?php namespace App\QueryType; - use eZ\Publish\API\Repository\Values\Content\LocationQuery; - use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId; - use eZ\Publish\Core\QueryType\QueryType; + use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; + use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ParentLocationId; + use Ibexa\Core\QueryType\QueryType; class LocationChildrenQueryType implements QueryType { diff --git a/eZ/Bundle/EzPublishCoreBundle/Fragment/DecoratedFragmentRenderer.php b/src/bundle/Core/Fragment/DecoratedFragmentRenderer.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/Fragment/DecoratedFragmentRenderer.php rename to src/bundle/Core/Fragment/DecoratedFragmentRenderer.php index 55d58c1f15..cf7acadec9 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Fragment/DecoratedFragmentRenderer.php +++ b/src/bundle/Core/Fragment/DecoratedFragmentRenderer.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Fragment; +namespace Ibexa\Bundle\Core\Fragment; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessAware; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; @@ -20,7 +20,7 @@ class DecoratedFragmentRenderer implements FragmentRendererInterface, SiteAccess /** @var \Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface */ private $innerRenderer; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ private $siteAccess; public function __construct(FragmentRendererInterface $innerRenderer) @@ -29,7 +29,7 @@ public function __construct(FragmentRendererInterface $innerRenderer) } /** - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess $siteAccess + * @param \Ibexa\Core\MVC\Symfony\SiteAccess|null $siteAccess */ public function setSiteAccess(SiteAccess $siteAccess = null) { @@ -62,7 +62,7 @@ public function render($uri, Request $request, array $options = []) { if ($uri instanceof ControllerReference && $request->attributes->has('siteaccess')) { // Serialize the siteaccess to get it back after. - // @see eZ\Publish\Core\MVC\Symfony\EventListener\SiteAccessMatchListener + // @see \Ibexa\Core\MVC\Symfony\EventListener\SiteAccessMatchListener $siteAccess = $request->attributes->get('siteaccess'); $this->serializeSiteAccess($siteAccess, $uri); } @@ -80,3 +80,5 @@ public function getName() return $this->innerRenderer->getName(); } } + +class_alias(DecoratedFragmentRenderer::class, 'eZ\Bundle\EzPublishCoreBundle\Fragment\DecoratedFragmentRenderer'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Fragment/DirectFragmentRenderer.php b/src/bundle/Core/Fragment/DirectFragmentRenderer.php similarity index 90% rename from eZ/Bundle/EzPublishCoreBundle/Fragment/DirectFragmentRenderer.php rename to src/bundle/Core/Fragment/DirectFragmentRenderer.php index 4651b55ad1..9a832a5eae 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Fragment/DirectFragmentRenderer.php +++ b/src/bundle/Core/Fragment/DirectFragmentRenderer.php @@ -6,12 +6,12 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Fragment; +namespace Ibexa\Bundle\Core\Fragment; -use eZ\Bundle\EzPublishCoreBundle\EventListener\ViewControllerListener; -use eZ\Publish\Core\MVC\Symfony\Templating\Exception\InvalidResponseException; -use eZ\Publish\Core\MVC\Symfony\View\Renderer\TemplateRenderer; -use eZ\Publish\Core\MVC\Symfony\View\View; +use Ibexa\Bundle\Core\EventListener\ViewControllerListener; +use Ibexa\Core\MVC\Symfony\Templating\Exception\InvalidResponseException; +use Ibexa\Core\MVC\Symfony\View\Renderer\TemplateRenderer; +use Ibexa\Core\MVC\Symfony\View\View; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface; @@ -30,7 +30,7 @@ class DirectFragmentRenderer extends InlineFragmentRenderer implements FragmentR /** @var \Symfony\Component\HttpKernel\KernelInterface */ protected $kernel; - /** @var \eZ\Bundle\EzPublishCoreBundle\EventListener\ViewControllerListener */ + /** @var \Ibexa\Bundle\Core\EventListener\ViewControllerListener */ protected $controllerListener; /** @var \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface */ @@ -42,7 +42,7 @@ class DirectFragmentRenderer extends InlineFragmentRenderer implements FragmentR /** @var \Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface */ protected $argumentValueResolver; - /** @var \eZ\Publish\Core\MVC\Symfony\View\Renderer\TemplateRenderer */ + /** @var \Ibexa\Core\MVC\Symfony\View\Renderer\TemplateRenderer */ protected $viewTemplateRenderer; public function __construct( @@ -115,7 +115,7 @@ protected function getFragmentUri( /** * @param string|\Symfony\Component\HttpKernel\Controller\ControllerReference $uri * - * @throws \eZ\Publish\Core\MVC\Symfony\Templating\Exception\InvalidResponseException + * @throws \Ibexa\Core\MVC\Symfony\Templating\Exception\InvalidResponseException */ public function render( $uri, @@ -160,3 +160,5 @@ public function getName(): string return self::NAME; } } + +class_alias(DirectFragmentRenderer::class, 'eZ\Bundle\EzPublishCoreBundle\Fragment\DirectFragmentRenderer'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Fragment/FragmentListenerFactory.php b/src/bundle/Core/Fragment/FragmentListenerFactory.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/Fragment/FragmentListenerFactory.php rename to src/bundle/Core/Fragment/FragmentListenerFactory.php index 5e8b45c4d8..bb1c53b647 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Fragment/FragmentListenerFactory.php +++ b/src/bundle/Core/Fragment/FragmentListenerFactory.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Fragment; +namespace Ibexa\Bundle\Core\Fragment; -use eZ\Publish\Core\MVC\Symfony\RequestStackAware; +use Ibexa\Core\MVC\Symfony\RequestStackAware; use Symfony\Component\HttpKernel\UriSigner; /** @@ -35,3 +35,5 @@ public function buildFragmentListener(UriSigner $uriSigner, $fragmentPath, $frag return new $fragmentListenerClass($uriSigner, $fragmentPath); } } + +class_alias(FragmentListenerFactory::class, 'eZ\Bundle\EzPublishCoreBundle\Fragment\FragmentListenerFactory'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Fragment/InlineFragmentRenderer.php b/src/bundle/Core/Fragment/InlineFragmentRenderer.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/Fragment/InlineFragmentRenderer.php rename to src/bundle/Core/Fragment/InlineFragmentRenderer.php index 05e532569a..d47bc11a1e 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Fragment/InlineFragmentRenderer.php +++ b/src/bundle/Core/Fragment/InlineFragmentRenderer.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Fragment; +namespace Ibexa\Bundle\Core\Fragment; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessAware; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; @@ -21,7 +21,7 @@ class InlineFragmentRenderer extends BaseRenderer implements SiteAccessAware /** @var \Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface */ private $innerRenderer; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ private $siteAccess; public function __construct(FragmentRendererInterface $innerRenderer) @@ -45,7 +45,7 @@ public function render($uri, Request $request, array $options = []) { if ($uri instanceof ControllerReference) { if ($request->attributes->has('siteaccess')) { - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess $siteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess $siteAccess */ $siteAccess = $request->attributes->get('siteaccess'); $this->serializeSiteAccess($siteAccess, $uri); } @@ -65,3 +65,5 @@ public function getName() return $this->innerRenderer->getName(); } } + +class_alias(InlineFragmentRenderer::class, 'eZ\Bundle\EzPublishCoreBundle\Fragment\InlineFragmentRenderer'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Fragment/SiteAccessSerializationTrait.php b/src/bundle/Core/Fragment/SiteAccessSerializationTrait.php similarity index 78% rename from eZ/Bundle/EzPublishCoreBundle/Fragment/SiteAccessSerializationTrait.php rename to src/bundle/Core/Fragment/SiteAccessSerializationTrait.php index 465f356c51..0bde8b1b38 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Fragment/SiteAccessSerializationTrait.php +++ b/src/bundle/Core/Fragment/SiteAccessSerializationTrait.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Fragment; +namespace Ibexa\Bundle\Core\Fragment; -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\SerializerTrait; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\Component\Serializer\SerializerTrait; +use Ibexa\Core\MVC\Symfony\SiteAccess; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\Serializer\Normalizer\AbstractNormalizer; @@ -19,7 +19,7 @@ trait SiteAccessSerializationTrait public function serializeSiteAccess(SiteAccess $siteAccess, ControllerReference $uri): void { - // Serialize the siteaccess to get it back after. @see eZ\Publish\Core\MVC\Symfony\EventListener\SiteAccessMatchListener + // Serialize the siteaccess to get it back after. @see \Ibexa\Core\MVC\Symfony\EventListener\SiteAccessMatchListener $uri->attributes['serialized_siteaccess'] = json_encode($siteAccess); $uri->attributes['serialized_siteaccess_matcher'] = $this->getSerializer()->serialize( $siteAccess->matcher, @@ -38,3 +38,5 @@ public function serializeSiteAccess(SiteAccess $siteAccess, ControllerReference } } } + +class_alias(SiteAccessSerializationTrait::class, 'eZ\Bundle\EzPublishCoreBundle\Fragment\SiteAccessSerializationTrait'); diff --git a/src/bundle/Core/IbexaCoreBundle.php b/src/bundle/Core/IbexaCoreBundle.php new file mode 100644 index 0000000000..be9b0ab962 --- /dev/null +++ b/src/bundle/Core/IbexaCoreBundle.php @@ -0,0 +1,140 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core; + +use Ibexa\Bundle\Core\DependencyInjection\Compiler\BinaryContentDownloadPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\ChainConfigResolverPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\ChainRoutingPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\ConsoleCacheWarmupPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\ConsoleCommandPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\EntityManagerFactoryServiceLocatorPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\FieldTypeParameterProviderRegistryPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\FragmentPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\ImaginePass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\InjectEntityManagerMappingsPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\LazyDoctrineRepositoriesPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\NotificationRendererPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\PlaceholderProviderPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\QueryTypePass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\RegisterSearchEngineIndexerPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\RegisterSearchEnginePass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\RegisterStorageEnginePass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\RouterPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\SecurityPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\SessionConfigurationPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\SiteAccessMatcherRegistryPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\SlugConverterConfigurationPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\StorageConnectionPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\TranslationCollectorPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\URLHandlerPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\ViewProvidersPass; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser as ConfigParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser\Repository as RepositoryConfigParser; +use Ibexa\Bundle\Core\DependencyInjection\IbexaCoreExtension; +use Ibexa\Bundle\Core\DependencyInjection\Security\HttpBasicFactory; +use Ibexa\Contracts\Core\MVC\View\VariableProvider; +use Ibexa\Core\Base\Container\Compiler\FieldTypeRegistryPass; +use Ibexa\Core\Base\Container\Compiler\GenericFieldTypeConverterPass; +use Ibexa\Core\Base\Container\Compiler\Persistence\FieldTypeRegistryPass as PersistenceFieldTypeRegistryPass; +use Ibexa\Core\Base\Container\Compiler\Search\AggregateFieldValueMapperPass; +use Ibexa\Core\Base\Container\Compiler\Search\FieldRegistryPass; +use Ibexa\Core\Base\Container\Compiler\Storage\ExternalStorageRegistryPass; +use Ibexa\Core\Base\Container\Compiler\Storage\Legacy\FieldValueConverterRegistryPass; +use Ibexa\Core\Base\Container\Compiler\Storage\Legacy\RoleLimitationConverterPass; +use Symfony\Component\DependencyInjection\Compiler\PassConfig; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class IbexaCoreBundle extends Bundle +{ + public function build(ContainerBuilder $container) + { + parent::build($container); + $container->addCompilerPass(new GenericFieldTypeConverterPass(), PassConfig::TYPE_OPTIMIZE); + $container->addCompilerPass(new FieldTypeRegistryPass(), PassConfig::TYPE_OPTIMIZE); + $container->addCompilerPass(new PersistenceFieldTypeRegistryPass(), PassConfig::TYPE_OPTIMIZE); + $container->addCompilerPass(new FieldTypeParameterProviderRegistryPass()); + $container->addCompilerPass(new ChainRoutingPass()); + $container->addCompilerPass(new ChainConfigResolverPass()); + $container->addCompilerPass(new RegisterStorageEnginePass()); + $container->addCompilerPass(new RegisterSearchEnginePass()); + $container->addCompilerPass(new RegisterSearchEngineIndexerPass()); + $container->addCompilerPass(new AggregateFieldValueMapperPass()); + $container->addCompilerPass(new FieldRegistryPass()); + $container->addCompilerPass(new RouterPass()); + $container->addCompilerPass(new SecurityPass()); + $container->addCompilerPass(new FragmentPass()); + $container->addCompilerPass(new StorageConnectionPass()); + $container->addCompilerPass(new ImaginePass()); + $container->addCompilerPass(new URLHandlerPass()); + $container->addCompilerPass(new BinaryContentDownloadPass()); + $container->addCompilerPass(new ViewProvidersPass()); + $container->addCompilerPass(new PlaceholderProviderPass()); + $container->addCompilerPass(new NotificationRendererPass()); + $container->addCompilerPass(new ConsoleCacheWarmupPass()); + $container->addCompilerPass(new SiteAccessMatcherRegistryPass()); + $container->addCompilerPass(new ConsoleCommandPass()); + $container->addCompilerPass(new LazyDoctrineRepositoriesPass(), PassConfig::TYPE_BEFORE_REMOVING); + $container->addCompilerPass(new EntityManagerFactoryServiceLocatorPass()); + $container->addCompilerPass(new InjectEntityManagerMappingsPass()); + $container->addCompilerPass(new SessionConfigurationPass()); + + // Storage passes + $container->addCompilerPass(new ExternalStorageRegistryPass()); + // Legacy Storage passes + $container->addCompilerPass(new FieldValueConverterRegistryPass()); + $container->addCompilerPass(new RoleLimitationConverterPass()); + $container->addCompilerPass(new QueryTypePass()); + + $securityExtension = $container->getExtension('security'); + $securityExtension->addSecurityListenerFactory(new HttpBasicFactory()); + $container->addCompilerPass(new TranslationCollectorPass()); + $container->addCompilerPass(new SlugConverterConfigurationPass()); + + $container->registerForAutoconfiguration(VariableProvider::class)->addTag('ezplatform.view.variable_provider'); + } + + public function getContainerExtension() + { + if (!isset($this->extension)) { + $this->extension = new IbexaCoreExtension( + [ + // LocationView config parser needs to be specified AFTER ContentView config + // parser since it is used to convert location view override rules to content + // view override rules. If it were specified before, ContentView provider would + // just undo the conversion LocationView did. + new ConfigParser\ContentView(), + new ConfigParser\LocationView(), + new ConfigParser\Common(), + new ConfigParser\Content(), + new ConfigParser\FieldType\ImageAsset(), + new ConfigParser\FieldTemplates(), + new ConfigParser\FieldEditTemplates(), + new ConfigParser\FieldDefinitionSettingsTemplates(), + new ConfigParser\FieldDefinitionEditTemplates(), + new ConfigParser\Image(), + new ConfigParser\Languages(), + new ConfigParser\IO(new ComplexSettingParser()), + new ConfigParser\UrlChecker(), + new ConfigParser\TwigVariablesParser(), + new ConfigParser\UserContentTypeIdentifier(), + ], + [ + new RepositoryConfigParser\Storage(), + new RepositoryConfigParser\Search(), + new RepositoryConfigParser\FieldGroups(), + new RepositoryConfigParser\Options(), + ] + ); + } + + return $this->extension; + } +} + +class_alias(IbexaCoreBundle::class, 'eZ\Bundle\EzPublishCoreBundle\EzPublishCoreBundle'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/AliasCleaner.php b/src/bundle/Core/Imagine/AliasCleaner.php similarity index 78% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/AliasCleaner.php rename to src/bundle/Core/Imagine/AliasCleaner.php index 2ec5324d64..9873da912d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/AliasCleaner.php +++ b/src/bundle/Core/Imagine/AliasCleaner.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine; +namespace Ibexa\Bundle\Core\Imagine; -use eZ\Publish\Core\FieldType\Image\AliasCleanerInterface; +use Ibexa\Core\FieldType\Image\AliasCleanerInterface; use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface; class AliasCleaner implements AliasCleanerInterface @@ -24,3 +24,5 @@ public function removeAliases($originalPath) $this->aliasResolver->remove([$originalPath], []); } } + +class_alias(AliasCleaner::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\AliasCleaner'); diff --git a/src/bundle/Core/Imagine/AliasGenerator.php b/src/bundle/Core/Imagine/AliasGenerator.php new file mode 100644 index 0000000000..77ddc22bca --- /dev/null +++ b/src/bundle/Core/Imagine/AliasGenerator.php @@ -0,0 +1,169 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine; + +use Ibexa\Contracts\Core\FieldType\Value; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidVariationException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Variation\Values\ImageVariation; +use Ibexa\Contracts\Core\Variation\VariationHandler; +use Ibexa\Core\FieldType\Image\Value as ImageValue; +use Ibexa\Core\MVC\Exception\SourceImageNotFoundException; +use Imagine\Exception\RuntimeException; +use InvalidArgumentException; +use Liip\ImagineBundle\Binary\BinaryInterface; +use Liip\ImagineBundle\Binary\Loader\LoaderInterface; +use Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException; +use Liip\ImagineBundle\Exception\Imagine\Cache\Resolver\NotResolvableException; +use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface; +use Liip\ImagineBundle\Imagine\Filter\FilterConfiguration; +use Liip\ImagineBundle\Imagine\Filter\FilterManager; +use Psr\Log\LoggerInterface; +use Psr\Log\NullLogger; +use SplFileInfo; + +/** + * Image alias generator using LiipImagineBundle API. + * Doesn't use DataManager/CacheManager as it's directly bound to IO Repository for convenience. + */ +class AliasGenerator implements VariationHandler +{ + public const ALIAS_ORIGINAL = 'original'; + + /** @var \Psr\Log\LoggerInterface */ + private $logger; + + /** + * Loader used to retrieve the original image. + * DataManager is not used to remain independent from ImagineBundle configuration. + * + * @var \Liip\ImagineBundle\Binary\Loader\LoaderInterface + */ + private $dataLoader; + + /** @var \Liip\ImagineBundle\Imagine\Filter\FilterManager */ + private $filterManager; + + /** @var \Liip\ImagineBundle\Imagine\Filter\FilterConfiguration */ + private $filterConfiguration; + + /** @var \Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface */ + private $ioResolver; + + public function __construct( + LoaderInterface $dataLoader, + FilterManager $filterManager, + ResolverInterface $ioResolver, + FilterConfiguration $filterConfiguration, + LoggerInterface $logger = null + ) { + $this->dataLoader = $dataLoader; + $this->filterManager = $filterManager; + $this->ioResolver = $ioResolver; + $this->filterConfiguration = $filterConfiguration; + $this->logger = null !== $logger ? $logger : new NullLogger(); + } + + /** + * {@inheritdoc} + * + * @throws \InvalidArgumentException If field value is not an instance of {@see \Ibexa\Core\FieldType\Image\Value}. + * @throws \Ibexa\Core\MVC\Exception\SourceImageNotFoundException If source image cannot be found. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidVariationException If a problem occurs with generated variation. + */ + public function getVariation(Field $field, VersionInfo $versionInfo, $variationName, array $parameters = []) + { + /** @var \Ibexa\Core\FieldType\Image\Value $imageValue */ + $imageValue = $field->value; + $fieldId = $field->id; + $fieldDefIdentifier = $field->fieldDefIdentifier; + if (!$this->supportsValue($imageValue)) { + throw new InvalidArgumentException("Value of Field with ID $fieldId ($fieldDefIdentifier) cannot be used for generating an image variation."); + } + + $originalPath = $imageValue->id; + + $variationWidth = $variationHeight = null; + // Create the image alias only if it does not already exist. + if ($variationName !== IORepositoryResolver::VARIATION_ORIGINAL && !$this->ioResolver->isStored($originalPath, $variationName)) { + try { + $originalBinary = $this->dataLoader->find($originalPath); + } catch (NotLoadableException $e) { + throw new SourceImageNotFoundException((string)$originalPath, 0, $e); + } + + $this->logger->debug("Generating '$variationName' variation on $originalPath, field #$fieldId ($fieldDefIdentifier)"); + + $this->ioResolver->store( + $this->applyFilter($originalBinary, $variationName), + $originalPath, + $variationName + ); + } else { + if ($variationName === IORepositoryResolver::VARIATION_ORIGINAL) { + $variationWidth = $imageValue->width; + $variationHeight = $imageValue->height; + } + $this->logger->debug("'$variationName' variation on $originalPath is already generated. Loading from cache."); + } + + try { + $aliasInfo = new SplFileInfo( + $this->ioResolver->resolve($originalPath, $variationName) + ); + } catch (NotResolvableException $e) { + // If for some reason image alias cannot be resolved, throw the appropriate exception. + throw new InvalidVariationException($variationName, 'image', 0, $e); + } catch (RuntimeException $e) { + throw new InvalidVariationException($variationName, 'image', 0, $e); + } + + return new ImageVariation( + [ + 'name' => $variationName, + 'fileName' => $aliasInfo->getFilename(), + 'dirPath' => $aliasInfo->getPath(), + 'uri' => $aliasInfo->getPathname(), + 'imageId' => $imageValue->imageId, + 'width' => $variationWidth, + 'height' => $variationHeight, + ] + ); + } + + /** + * Applies $variationName filters on $image. + * + * Both variations configured in Ibexa (SiteAccess context) and LiipImagineBundle are used. + * An Ibexa variation may have a "reference". + * In that case, reference's filters are applied first, recursively (a reference may also have another reference). + * Reference must be a valid variation name, configured in Ibexa or in LiipImagineBundle. + * + * @param \Liip\ImagineBundle\Binary\BinaryInterface $image + * @param string $variationName + * + * @return \Liip\ImagineBundle\Binary\BinaryInterface + */ + private function applyFilter(BinaryInterface $image, $variationName) + { + $filterConfig = $this->filterConfiguration->get($variationName); + // If the variation has a reference, we recursively call this method to apply reference's filters. + if (isset($filterConfig['reference']) && $filterConfig['reference'] !== IORepositoryResolver::VARIATION_ORIGINAL) { + $image = $this->applyFilter($image, $filterConfig['reference']); + } + + return $this->filterManager->applyFilter($image, $variationName); + } + + public function supportsValue(Value $value) + { + return $value instanceof ImageValue; + } +} + +class_alias(AliasGenerator::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\AliasGenerator'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/BinaryLoader.php b/src/bundle/Core/Imagine/BinaryLoader.php similarity index 81% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/BinaryLoader.php rename to src/bundle/Core/Imagine/BinaryLoader.php index ac5e90c0e8..1c40589515 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/BinaryLoader.php +++ b/src/bundle/Core/Imagine/BinaryLoader.php @@ -4,24 +4,24 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine; +namespace Ibexa\Bundle\Core\Imagine; use Exception; -use eZ\Publish\Core\IO\Exception\InvalidBinaryFileIdException; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\MissingBinaryFile; +use Ibexa\Core\IO\Exception\InvalidBinaryFileIdException; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\MissingBinaryFile; use Liip\ImagineBundle\Binary\Loader\LoaderInterface; use Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException; use Liip\ImagineBundle\Model\Binary; use Symfony\Component\Mime\MimeTypesInterface; /** - * Binary loader using eZ IOService. + * Binary loader using Ibexa IOService. * To be used by LiipImagineBundle. */ class BinaryLoader implements LoaderInterface { - /** @var \eZ\Publish\Core\IO\IOServiceInterface */ + /** @var \Ibexa\Core\IO\IOServiceInterface */ private $ioService; /** @var \Symfony\Component\Mime\MimeTypesInterface */ @@ -33,6 +33,11 @@ public function __construct(IOServiceInterface $ioService, MimeTypesInterface $m $this->mimeTypes = $mimeTypes; } + /** + * @param string $path + * + * @return \Liip\ImagineBundle\Binary\BinaryInterface + */ public function find($path) { try { @@ -65,3 +70,5 @@ public function find($path) } } } + +class_alias(BinaryLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\BinaryLoader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Cache/AliasGeneratorDecorator.php b/src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php similarity index 81% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Cache/AliasGeneratorDecorator.php rename to src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php index 18db58a313..4beef2de61 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Cache/AliasGeneratorDecorator.php +++ b/src/bundle/Core/Imagine/Cache/AliasGeneratorDecorator.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Cache; +namespace Ibexa\Bundle\Core\Imagine\Cache; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessAware; -use eZ\Publish\SPI\Variation\VariationHandler; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Variation\VariationHandler; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware; use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface; use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; use Symfony\Component\Routing\RequestContext; @@ -28,13 +28,13 @@ class AliasGeneratorDecorator implements VariationHandler, SiteAccessAware private const CONTENT_IDENTIFIER = 'content'; private const CONTENT_VERSION_IDENTIFIER = 'content_version'; - /** @var \eZ\Publish\SPI\Variation\VariationHandler */ + /** @var \Ibexa\Contracts\Core\Variation\VariationHandler */ private $aliasGenerator; /** @var \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface */ private $cache; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ private $siteAccess; /** @var \Symfony\Component\Routing\RequestContext */ @@ -44,7 +44,7 @@ class AliasGeneratorDecorator implements VariationHandler, SiteAccessAware private $cacheIdentifierGenerator; /** - * @param \eZ\Publish\SPI\Variation\VariationHandler $aliasGenerator + * @param \Ibexa\Contracts\Core\Variation\VariationHandler $aliasGenerator * @param \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface $cache * @param \Symfony\Component\Routing\RequestContext $requestContext * @param \Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface $cacheIdentifierGenerator @@ -62,12 +62,12 @@ public function __construct( } /** - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo * @param string $variationName * @param array $parameters * - * @return \eZ\Publish\SPI\Variation\Values\Variation + * @return \Ibexa\Contracts\Core\Variation\Values\Variation * * @throws \Psr\Cache\InvalidArgumentException */ @@ -86,7 +86,7 @@ public function getVariation(Field $field, VersionInfo $versionInfo, $variationN } /** - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess $siteAccess + * @param \Ibexa\Core\MVC\Symfony\SiteAccess|null $siteAccess */ public function setSiteAccess(SiteAccess $siteAccess = null) { @@ -94,8 +94,8 @@ public function setSiteAccess(SiteAccess $siteAccess = null) } /** - * @param \eZ\Publish\API\Repository\Values\Content\Field $field - * @param \eZ\Publish\API\Repository\Values\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo * @param string $variationName * * @return string @@ -135,3 +135,5 @@ public function getVariationNameTag(): string return self::IMAGE_VARIATION_NAME_IDENTIFIER; } } + +class_alias(AliasGeneratorDecorator::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\AliasGeneratorDecorator'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Cache/Resolver/ProxyResolver.php b/src/bundle/Core/Imagine/Cache/Resolver/ProxyResolver.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Cache/Resolver/ProxyResolver.php rename to src/bundle/Core/Imagine/Cache/Resolver/ProxyResolver.php index 5588a544d9..08ba10db5d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Cache/Resolver/ProxyResolver.php +++ b/src/bundle/Core/Imagine/Cache/Resolver/ProxyResolver.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\Resolver; +namespace Ibexa\Bundle\Core\Imagine\Cache\Resolver; use Liip\ImagineBundle\Imagine\Cache\Resolver\ProxyResolver as ImagineProxyResolver; @@ -32,3 +32,5 @@ protected function rewriteUrl($url) return $proxyHost . $relativeUrl; } } + +class_alias(ProxyResolver::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\Resolver\ProxyResolver'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Cache/Resolver/RelativeResolver.php b/src/bundle/Core/Imagine/Cache/Resolver/RelativeResolver.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Cache/Resolver/RelativeResolver.php rename to src/bundle/Core/Imagine/Cache/Resolver/RelativeResolver.php index a06a50a2cd..4b2796fa79 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Cache/Resolver/RelativeResolver.php +++ b/src/bundle/Core/Imagine/Cache/Resolver/RelativeResolver.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\Resolver; +namespace Ibexa\Bundle\Core\Imagine\Cache\Resolver; use Liip\ImagineBundle\Imagine\Cache\Resolver\ProxyResolver as ImagineProxyResolver; use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface; @@ -34,3 +34,5 @@ protected function rewriteUrl($url) return parse_url($url, PHP_URL_PATH); } } + +class_alias(RelativeResolver::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\Resolver\RelativeResolver'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Cache/ResolverFactory.php b/src/bundle/Core/Imagine/Cache/ResolverFactory.php similarity index 83% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Cache/ResolverFactory.php rename to src/bundle/Core/Imagine/Cache/ResolverFactory.php index db8c91de1e..b7f79d7c50 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Cache/ResolverFactory.php +++ b/src/bundle/Core/Imagine/Cache/ResolverFactory.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Cache; +namespace Ibexa\Bundle\Core\Imagine\Cache; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface; class ResolverFactory { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; /** @var \Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface */ @@ -27,7 +27,7 @@ class ResolverFactory private $relativeResolverClass; /** - * @param \eZ\Publish\Core\MVC\ConfigResolverInterface $configResolver + * @param \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface $configResolver * @param \Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface $resolver * @param string $proxyResolverClass * @param string $relativeResolverClass @@ -66,3 +66,5 @@ public function createCacheResolver() return new $this->resolverDecoratorClass($this->resolver, [$imageHost]); } } + +class_alias(ResolverFactory::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\ResolverFactory'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/AbstractFilter.php b/src/bundle/Core/Imagine/Filter/AbstractFilter.php similarity index 87% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/AbstractFilter.php rename to src/bundle/Core/Imagine/Filter/AbstractFilter.php index 603b1c1873..ea3e38d24d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/AbstractFilter.php +++ b/src/bundle/Core/Imagine/Filter/AbstractFilter.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter; +namespace Ibexa\Bundle\Core\Imagine\Filter; /** * Base implementation of FilterInterface, handling options. @@ -44,3 +44,5 @@ public function getOptions() return $this->options; } } + +class_alias(AbstractFilter::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\AbstractFilter'); diff --git a/src/bundle/Core/Imagine/Filter/FilterConfiguration.php b/src/bundle/Core/Imagine/Filter/FilterConfiguration.php new file mode 100644 index 0000000000..1d99af786b --- /dev/null +++ b/src/bundle/Core/Imagine/Filter/FilterConfiguration.php @@ -0,0 +1,99 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine\Filter; + +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Liip\ImagineBundle\Imagine\Filter\FilterConfiguration as BaseFilterConfiguration; + +class FilterConfiguration extends BaseFilterConfiguration +{ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ + private $configResolver; + + /** + * @param \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface $configResolver + */ + public function setConfigResolver(ConfigResolverInterface $configResolver) + { + $this->configResolver = $configResolver; + } + + public function get($filter) + { + $configuredVariations = $this->configResolver->getParameter('image_variations'); + if (!array_key_exists($filter, $configuredVariations)) { + return parent::get($filter); + } + + $filterConfig = isset($this->filters[$filter]) ? parent::get($filter) : []; + + return [ + 'cache' => 'ibexa', + 'data_loader' => 'ibexa', + 'reference' => isset($configuredVariations[$filter]['reference']) ? $configuredVariations[$filter]['reference'] : null, + 'filters' => $this->getVariationFilters($filter, $configuredVariations), + 'post_processors' => $this->getVariationPostProcessors($filter, $configuredVariations), + ] + $filterConfig; + } + + public function all() + { + return $this->configResolver->getParameter('image_variations') + parent::all(); + } + + /** + * Returns filters to be used for $variationName. + * + * Both variations configured in Ibexa (SiteAccess context) and LiipImagineBundle are used. + * Ibexa variations always have precedence. + * + * @param string $variationName + * @param array $configuredVariations Variations set in eZ. + * + * @return array + */ + private function getVariationFilters($variationName, array $configuredVariations) + { + if (!isset($configuredVariations[$variationName]['filters']) && !isset($this->filters[$variationName]['filters'])) { + return []; + } + + // Check variations configured in Ibexa config first. + if (isset($configuredVariations[$variationName]['filters'])) { + $filters = $configuredVariations[$variationName]['filters']; + } else { + // Falback to variations configured in LiipImagineBundle. + $filters = $this->filters[$variationName]['filters']; + } + + return $filters; + } + + /** + * Returns post processors to be used for $variationName. + * + * Both variations configured in Ibexa and LiipImagineBundle are used. + * Ibexa variations always have precedence. + * + * @param string $variationName + * @param array $configuredVariations Variations set in eZ. + * + * @return array + */ + private function getVariationPostProcessors($variationName, array $configuredVariations) + { + if (isset($configuredVariations[$variationName]['post_processors'])) { + return $configuredVariations[$variationName]['post_processors']; + } elseif (isset($this->filters[$variationName]['post_processors'])) { + return $this->filters[$variationName]['post_processors']; + } + + return []; + } +} + +class_alias(FilterConfiguration::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\FilterConfiguration'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/FilterInterface.php b/src/bundle/Core/Imagine/Filter/FilterInterface.php similarity index 89% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/FilterInterface.php rename to src/bundle/Core/Imagine/Filter/FilterInterface.php index 80e8500e62..be74b2a81c 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/FilterInterface.php +++ b/src/bundle/Core/Imagine/Filter/FilterInterface.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter; +namespace Ibexa\Bundle\Core\Imagine\Filter; use Imagine\Filter\FilterInterface as BaseFilterInterface; @@ -52,3 +52,5 @@ public function setOptions(array $options); */ public function getOptions(); } + +class_alias(FilterInterface::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\FilterInterface'); diff --git a/src/bundle/Core/Imagine/Filter/Gmagick/ReduceNoiseFilter.php b/src/bundle/Core/Imagine/Filter/Gmagick/ReduceNoiseFilter.php new file mode 100644 index 0000000000..1e826b4d9c --- /dev/null +++ b/src/bundle/Core/Imagine/Filter/Gmagick/ReduceNoiseFilter.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine\Filter\Gmagick; + +use Ibexa\Bundle\Core\Imagine\Filter\AbstractFilter; +use Imagine\Image\ImageInterface; + +class ReduceNoiseFilter extends AbstractFilter +{ + /** + * @param \Imagine\Image\ImageInterface|\Imagine\Gmagick\Image $image + * + * @return \Imagine\Image\ImageInterface + */ + public function apply(ImageInterface $image) + { + /** @var \Gmagick $gmagick */ + $gmagick = $image->getGmagick(); + $gmagick->reduceNoiseImage((float)$this->getOption('radius', 0)); + + return $image; + } +} + +class_alias(ReduceNoiseFilter::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Gmagick\ReduceNoiseFilter'); diff --git a/src/bundle/Core/Imagine/Filter/Gmagick/SwirlFilter.php b/src/bundle/Core/Imagine/Filter/Gmagick/SwirlFilter.php new file mode 100644 index 0000000000..d5dcfe01a2 --- /dev/null +++ b/src/bundle/Core/Imagine/Filter/Gmagick/SwirlFilter.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine\Filter\Gmagick; + +use Ibexa\Bundle\Core\Imagine\Filter\AbstractFilter; +use Imagine\Image\ImageInterface; + +class SwirlFilter extends AbstractFilter +{ + /** + * @param \Imagine\Image\ImageInterface|\Imagine\Gmagick\Image $image + * + * @return \Imagine\Image\ImageInterface + */ + public function apply(ImageInterface $image) + { + /** @var \Gmagick $gmagick */ + $gmagick = $image->getGmagick(); + $gmagick->swirlimage((float)$this->getOption('degrees', 60)); + + return $image; + } +} + +class_alias(SwirlFilter::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Gmagick\SwirlFilter'); diff --git a/src/bundle/Core/Imagine/Filter/Imagick/ReduceNoiseFilter.php b/src/bundle/Core/Imagine/Filter/Imagick/ReduceNoiseFilter.php new file mode 100644 index 0000000000..fb931f71e8 --- /dev/null +++ b/src/bundle/Core/Imagine/Filter/Imagick/ReduceNoiseFilter.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine\Filter\Imagick; + +use Ibexa\Bundle\Core\Imagine\Filter\AbstractFilter; +use Imagine\Image\ImageInterface; + +class ReduceNoiseFilter extends AbstractFilter +{ + /** + * @param \Imagine\Image\ImageInterface|\Imagine\Imagick\Image $image + * + * @return \Imagine\Image\ImageInterface + */ + public function apply(ImageInterface $image) + { + /** @var \Imagick $imagick */ + $imagick = $image->getImagick(); + $imagick->reduceNoiseImage((float)$this->getOption('radius', 0)); + + return $image; + } +} + +class_alias(ReduceNoiseFilter::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Imagick\ReduceNoiseFilter'); diff --git a/src/bundle/Core/Imagine/Filter/Imagick/SwirlFilter.php b/src/bundle/Core/Imagine/Filter/Imagick/SwirlFilter.php new file mode 100644 index 0000000000..67b4306797 --- /dev/null +++ b/src/bundle/Core/Imagine/Filter/Imagick/SwirlFilter.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine\Filter\Imagick; + +use Ibexa\Bundle\Core\Imagine\Filter\AbstractFilter; +use Imagine\Image\ImageInterface; + +class SwirlFilter extends AbstractFilter +{ + /** + * @param \Imagine\Image\ImageInterface|\Imagine\Imagick\Image $image + * + * @return \Imagine\Image\ImageInterface + */ + public function apply(ImageInterface $image) + { + /** @var \Imagick $imagick */ + $imagick = $image->getImagick(); + $imagick->swirlImage((float)$this->getOption('degrees', 60)); + + return $image; + } +} + +class_alias(SwirlFilter::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Imagick\SwirlFilter'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/BorderFilterLoader.php b/src/bundle/Core/Imagine/Filter/Loader/BorderFilterLoader.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/BorderFilterLoader.php rename to src/bundle/Core/Imagine/Filter/Loader/BorderFilterLoader.php index 6ddcead68a..c30e8a07c0 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/BorderFilterLoader.php +++ b/src/bundle/Core/Imagine/Filter/Loader/BorderFilterLoader.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader; +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; use Imagine\Exception\InvalidArgumentException; use Imagine\Filter\Advanced\Border; @@ -19,6 +19,8 @@ */ class BorderFilterLoader implements LoaderInterface { + public const IDENTIFIER = 'border'; + public const DEFAULT_BORDER_COLOR = '#000'; public function load(ImageInterface $image, array $options = []) @@ -40,3 +42,5 @@ public function load(ImageInterface $image, array $options = []) return $border->apply($image); } } + +class_alias(BorderFilterLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\BorderFilterLoader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/CropFilterLoader.php b/src/bundle/Core/Imagine/Filter/Loader/CropFilterLoader.php similarity index 80% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/CropFilterLoader.php rename to src/bundle/Core/Imagine/Filter/Loader/CropFilterLoader.php index cdf3ae0eb0..014cfd7852 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/CropFilterLoader.php +++ b/src/bundle/Core/Imagine/Filter/Loader/CropFilterLoader.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader; +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; use Imagine\Exception\InvalidArgumentException; use Imagine\Image\ImageInterface; @@ -15,6 +15,8 @@ */ class CropFilterLoader extends FilterLoaderWrapped { + public const IDENTIFIER = 'geometry/crop'; + public function load(ImageInterface $image, array $options = []) { if (count($options) < 4) { @@ -30,3 +32,5 @@ public function load(ImageInterface $image, array $options = []) ); } } + +class_alias(CropFilterLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\CropFilterLoader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/FilterLoaderWrapped.php b/src/bundle/Core/Imagine/Filter/Loader/FilterLoaderWrapped.php similarity index 79% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/FilterLoaderWrapped.php rename to src/bundle/Core/Imagine/Filter/Loader/FilterLoaderWrapped.php index 1e8b0d6132..366ccce2fc 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/FilterLoaderWrapped.php +++ b/src/bundle/Core/Imagine/Filter/Loader/FilterLoaderWrapped.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader; +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; use Liip\ImagineBundle\Imagine\Filter\Loader\LoaderInterface; @@ -21,3 +21,5 @@ public function setInnerLoader(LoaderInterface $innerLoader) $this->innerLoader = $innerLoader; } } + +class_alias(FilterLoaderWrapped::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\FilterLoaderWrapped'); diff --git a/src/bundle/Core/Imagine/Filter/Loader/GrayscaleFilterLoader.php b/src/bundle/Core/Imagine/Filter/Loader/GrayscaleFilterLoader.php new file mode 100644 index 0000000000..6e49081cfa --- /dev/null +++ b/src/bundle/Core/Imagine/Filter/Loader/GrayscaleFilterLoader.php @@ -0,0 +1,28 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; + +use Imagine\Image\ImageInterface; +use Liip\ImagineBundle\Imagine\Filter\Loader\LoaderInterface; + +/** + * Grayscale filter loader. + * Makes an image use grayscale. + */ +class GrayscaleFilterLoader implements LoaderInterface +{ + public const IDENTIFIER = 'colorspace/gray'; + + public function load(ImageInterface $image, array $options = []) + { + $image->effects()->grayscale(); + + return $image; + } +} + +class_alias(GrayscaleFilterLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\GrayscaleFilterLoader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ReduceNoiseFilterLoader.php b/src/bundle/Core/Imagine/Filter/Loader/ReduceNoiseFilterLoader.php similarity index 76% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ReduceNoiseFilterLoader.php rename to src/bundle/Core/Imagine/Filter/Loader/ReduceNoiseFilterLoader.php index acf8afa81d..25364fa509 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ReduceNoiseFilterLoader.php +++ b/src/bundle/Core/Imagine/Filter/Loader/ReduceNoiseFilterLoader.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader; +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\FilterInterface; +use Ibexa\Bundle\Core\Imagine\Filter\FilterInterface; use Imagine\Exception\NotSupportedException; use Imagine\Gmagick\Image as GmagickImage; use Imagine\Image\ImageInterface; @@ -19,7 +19,9 @@ */ class ReduceNoiseFilterLoader implements LoaderInterface { - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\FilterInterface */ + public const IDENTIFIER = 'filter/noise'; + + /** @var \Ibexa\Bundle\Core\Imagine\Filter\FilterInterface */ private $filter; public function __construct(FilterInterface $filter) @@ -40,3 +42,5 @@ public function load(ImageInterface $image, array $options = []) return $this->filter->apply($image); } } + +class_alias(ReduceNoiseFilterLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ReduceNoiseFilterLoader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleDownOnlyFilterLoader.php b/src/bundle/Core/Imagine/Filter/Loader/ScaleDownOnlyFilterLoader.php similarity index 82% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleDownOnlyFilterLoader.php rename to src/bundle/Core/Imagine/Filter/Loader/ScaleDownOnlyFilterLoader.php index b9983e3ca9..a52a014e4d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleDownOnlyFilterLoader.php +++ b/src/bundle/Core/Imagine/Filter/Loader/ScaleDownOnlyFilterLoader.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader; +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; use Imagine\Exception\InvalidArgumentException; use Imagine\Image\ImageInterface; @@ -15,6 +15,8 @@ */ class ScaleDownOnlyFilterLoader extends FilterLoaderWrapped { + public const IDENTIFIER = 'geometry/scaledownonly'; + /** * Loads and applies a filter on the given image. * @@ -40,3 +42,5 @@ public function load(ImageInterface $image, array $options = []) ); } } + +class_alias(ScaleDownOnlyFilterLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleDownOnlyFilterLoader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleExactFilterLoader.php b/src/bundle/Core/Imagine/Filter/Loader/ScaleExactFilterLoader.php similarity index 75% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleExactFilterLoader.php rename to src/bundle/Core/Imagine/Filter/Loader/ScaleExactFilterLoader.php index 44f11bf490..cdf73e4f54 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleExactFilterLoader.php +++ b/src/bundle/Core/Imagine/Filter/Loader/ScaleExactFilterLoader.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader; +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; use Imagine\Exception\InvalidArgumentException; use Imagine\Image\ImageInterface; @@ -15,6 +15,8 @@ */ class ScaleExactFilterLoader extends FilterLoaderWrapped { + public const IDENTIFIER = 'geometry/scaleexact'; + public function load(ImageInterface $image, array $options = []) { if (count($options) < 2) { @@ -24,3 +26,5 @@ public function load(ImageInterface $image, array $options = []) return $this->innerLoader->load($image, ['size' => $options]); } } + +class_alias(ScaleExactFilterLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleExactFilterLoader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleFilterLoader.php b/src/bundle/Core/Imagine/Filter/Loader/ScaleFilterLoader.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleFilterLoader.php rename to src/bundle/Core/Imagine/Filter/Loader/ScaleFilterLoader.php index 41c3d68821..8800a23858 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleFilterLoader.php +++ b/src/bundle/Core/Imagine/Filter/Loader/ScaleFilterLoader.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader; +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; use Imagine\Exception\InvalidArgumentException; use Imagine\Image\ImageInterface; @@ -15,6 +15,8 @@ */ class ScaleFilterLoader extends FilterLoaderWrapped { + public const IDENTIFIER = 'geometry/scale'; + public function load(ImageInterface $image, array $options = []) { if (count($options) < 2) { @@ -39,3 +41,5 @@ public function load(ImageInterface $image, array $options = []) return $this->innerLoader->load($image, [$method => $value]); } } + +class_alias(ScaleFilterLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleFilterLoader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoader.php b/src/bundle/Core/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoader.php similarity index 76% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoader.php rename to src/bundle/Core/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoader.php index fd1546f767..d92486330b 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoader.php +++ b/src/bundle/Core/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoader.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader; +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; use Imagine\Exception\InvalidArgumentException; use Imagine\Image\ImageInterface; @@ -15,6 +15,8 @@ */ class ScaleHeightDownOnlyFilterLoader extends FilterLoaderWrapped { + public const IDENTIFIER = 'geometry/scaleheightdownonly'; + public function load(ImageInterface $image, array $options = []) { if (empty($options)) { @@ -30,3 +32,5 @@ public function load(ImageInterface $image, array $options = []) ); } } + +class_alias(ScaleHeightDownOnlyFilterLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleHeightDownOnlyFilterLoader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleHeightFilterLoader.php b/src/bundle/Core/Imagine/Filter/Loader/ScaleHeightFilterLoader.php similarity index 75% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleHeightFilterLoader.php rename to src/bundle/Core/Imagine/Filter/Loader/ScaleHeightFilterLoader.php index 18d09cfd61..c45029b62d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleHeightFilterLoader.php +++ b/src/bundle/Core/Imagine/Filter/Loader/ScaleHeightFilterLoader.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader; +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; use Imagine\Exception\InvalidArgumentException; use Imagine\Image\ImageInterface; @@ -15,6 +15,8 @@ */ class ScaleHeightFilterLoader extends FilterLoaderWrapped { + public const IDENTIFIER = 'geometry/scaleheight'; + public function load(ImageInterface $image, array $options = []) { if (empty($options)) { @@ -24,3 +26,5 @@ public function load(ImageInterface $image, array $options = []) return $this->innerLoader->load($image, ['heighten' => $options[0]]); } } + +class_alias(ScaleHeightFilterLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleHeightFilterLoader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScalePercentFilterLoader.php b/src/bundle/Core/Imagine/Filter/Loader/ScalePercentFilterLoader.php similarity index 81% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScalePercentFilterLoader.php rename to src/bundle/Core/Imagine/Filter/Loader/ScalePercentFilterLoader.php index 5dcc3a703c..30d47e6565 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScalePercentFilterLoader.php +++ b/src/bundle/Core/Imagine/Filter/Loader/ScalePercentFilterLoader.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader; +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; use Imagine\Exception\InvalidArgumentException; use Imagine\Image\ImageInterface; @@ -15,6 +15,8 @@ */ class ScalePercentFilterLoader extends FilterLoaderWrapped { + public const IDENTIFIER = 'geometry/scalepercent'; + public function load(ImageInterface $image, array $options = []) { if (count($options) < 2) { @@ -32,3 +34,5 @@ public function load(ImageInterface $image, array $options = []) return $this->innerLoader->load($image, ['size' => [$targetWidth, $targetHeight]]); } } + +class_alias(ScalePercentFilterLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScalePercentFilterLoader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoader.php b/src/bundle/Core/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoader.php similarity index 76% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoader.php rename to src/bundle/Core/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoader.php index 07cc07d268..36ce89b184 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoader.php +++ b/src/bundle/Core/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoader.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader; +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; use Imagine\Exception\InvalidArgumentException; use Imagine\Image\ImageInterface; @@ -15,6 +15,8 @@ */ class ScaleWidthDownOnlyFilterLoader extends FilterLoaderWrapped { + public const IDENTIFIER = 'geometry/scalewidthdownonly'; + public function load(ImageInterface $image, array $options = []) { if (empty($options)) { @@ -30,3 +32,5 @@ public function load(ImageInterface $image, array $options = []) ); } } + +class_alias(ScaleWidthDownOnlyFilterLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleWidthDownOnlyFilterLoader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleWidthFilterLoader.php b/src/bundle/Core/Imagine/Filter/Loader/ScaleWidthFilterLoader.php similarity index 75% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleWidthFilterLoader.php rename to src/bundle/Core/Imagine/Filter/Loader/ScaleWidthFilterLoader.php index 03635659bf..c07ce2a4d7 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/Loader/ScaleWidthFilterLoader.php +++ b/src/bundle/Core/Imagine/Filter/Loader/ScaleWidthFilterLoader.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader; +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; use Imagine\Exception\InvalidArgumentException; use Imagine\Image\ImageInterface; @@ -15,6 +15,8 @@ */ class ScaleWidthFilterLoader extends FilterLoaderWrapped { + public const IDENTIFIER = 'geometry/scalewidth'; + public function load(ImageInterface $image, array $options = []) { if (empty($options)) { @@ -24,3 +26,5 @@ public function load(ImageInterface $image, array $options = []) return $this->innerLoader->load($image, ['widen' => $options[0]]); } } + +class_alias(ScaleWidthFilterLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleWidthFilterLoader'); diff --git a/src/bundle/Core/Imagine/Filter/Loader/SwirlFilterLoader.php b/src/bundle/Core/Imagine/Filter/Loader/SwirlFilterLoader.php new file mode 100644 index 0000000000..ac00c75cc6 --- /dev/null +++ b/src/bundle/Core/Imagine/Filter/Loader/SwirlFilterLoader.php @@ -0,0 +1,35 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine\Filter\Loader; + +use Ibexa\Bundle\Core\Imagine\Filter\FilterInterface; +use Imagine\Image\ImageInterface; +use Liip\ImagineBundle\Imagine\Filter\Loader\LoaderInterface; + +class SwirlFilterLoader implements LoaderInterface +{ + public const IDENTIFIER = 'filter/swirl'; + + /** @var \Ibexa\Bundle\Core\Imagine\Filter\FilterInterface */ + private $filter; + + public function __construct(FilterInterface $filter) + { + $this->filter = $filter; + } + + public function load(ImageInterface $image, array $options = []) + { + if (!empty($options)) { + $this->filter->setOption('degrees', $options[0]); + } + + return $this->filter->apply($image); + } +} + +class_alias(SwirlFilterLoader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\SwirlFilterLoader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/UnsupportedFilter.php b/src/bundle/Core/Imagine/Filter/UnsupportedFilter.php similarity index 78% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/UnsupportedFilter.php rename to src/bundle/Core/Imagine/Filter/UnsupportedFilter.php index db38c798b5..b5801c1b58 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/Filter/UnsupportedFilter.php +++ b/src/bundle/Core/Imagine/Filter/UnsupportedFilter.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\Filter; +namespace Ibexa\Bundle\Core\Imagine\Filter; use Imagine\Exception\NotSupportedException; use Imagine\Image\ImageInterface; @@ -19,3 +19,5 @@ public function apply(ImageInterface $image) throw new NotSupportedException('The filter is not supported by your current configuration.'); } } + +class_alias(UnsupportedFilter::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\UnsupportedFilter'); diff --git a/src/bundle/Core/Imagine/IORepositoryResolver.php b/src/bundle/Core/Imagine/IORepositoryResolver.php new file mode 100644 index 0000000000..a70715421c --- /dev/null +++ b/src/bundle/Core/Imagine/IORepositoryResolver.php @@ -0,0 +1,131 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine; + +use Ibexa\Bundle\Core\Variation\PathResolver; +use Ibexa\Contracts\Core\Variation\VariationPathGenerator; +use Ibexa\Contracts\Core\Variation\VariationPurger; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\MissingBinaryFile; +use Liip\ImagineBundle\Binary\BinaryInterface; +use Liip\ImagineBundle\Exception\Imagine\Cache\Resolver\NotResolvableException; +use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface; +use Liip\ImagineBundle\Imagine\Filter\FilterConfiguration; +use Symfony\Component\Routing\RequestContext; + +/** + * LiipImagineBundle cache resolver using Ibexa IO repository. + */ +class IORepositoryResolver extends PathResolver implements ResolverInterface +{ + public const VARIATION_ORIGINAL = 'original'; + + /** @var \Ibexa\Core\IO\IOServiceInterface */ + private $ioService; + + /** @var \Liip\ImagineBundle\Imagine\Filter\FilterConfiguration */ + private $filterConfiguration; + + /** @var \Ibexa\Contracts\Core\Variation\VariationPurger */ + private $variationPurger; + + public function __construct( + IOServiceInterface $ioService, + RequestContext $requestContext, + FilterConfiguration $filterConfiguration, + VariationPurger $variationPurger, + VariationPathGenerator $variationPathGenerator + ) { + parent::__construct($requestContext, $variationPathGenerator); + + $this->ioService = $ioService; + $this->filterConfiguration = $filterConfiguration; + $this->variationPurger = $variationPurger; + } + + public function isStored($path, $filter) + { + return $this->ioService->exists($this->getFilePath($path, $filter)); + } + + public function resolve($path, $filter): string + { + try { + $binaryFile = $this->ioService->loadBinaryFile($path); + + // Treat a MissingBinaryFile as a not loadable file. + if ($binaryFile instanceof MissingBinaryFile) { + throw new NotResolvableException("Variation image not found in $path"); + } + + if ($filter !== static::VARIATION_ORIGINAL) { + $variationPath = $this->getFilePath($path, $filter); + $variationBinaryFile = $this->ioService->loadBinaryFile($variationPath); + $path = $variationBinaryFile->uri; + } else { + $path = $binaryFile->uri; + } + + return sprintf( + '%s%s', + $path[0] === '/' ? $this->getBaseUrl() : '', + $path + ); + } catch (NotFoundException $e) { + throw new NotResolvableException("Variation image not found in $path", 0, $e); + } + } + + /** + * Stores image alias in the IO Repository. + * A temporary file is created to dump the filtered image and is used as basis for creation in the IO Repository. + * + * {@inheritdoc} + */ + public function store(BinaryInterface $binary, $path, $filter) + { + $tmpFile = tmpfile(); + fwrite($tmpFile, $binary->getContent()); + $tmpMetadata = stream_get_meta_data($tmpFile); + + $binaryCreateStruct = $this->ioService->newBinaryCreateStructFromLocalFile($tmpMetadata['uri']); + $binaryCreateStruct->id = $this->getFilePath($path, $filter); + $this->ioService->createBinaryFile($binaryCreateStruct); + + fclose($tmpFile); + } + + /** + * @param string[] $paths The paths where the original files are expected to be. + * @param string[] $filters The imagine filters in effect. + */ + public function remove(array $paths, array $filters) + { + if (empty($filters)) { + $filters = array_keys($this->filterConfiguration->all()); + } + + if (empty($paths)) { + $this->variationPurger->purge($filters); + } + + foreach ($paths as $path) { + foreach ($filters as $filter) { + $filteredImagePath = $this->getFilePath($path, $filter); + if (!$this->ioService->exists($filteredImagePath)) { + continue; + } + + $binaryFile = $this->ioService->loadBinaryFile($filteredImagePath); + $this->ioService->deleteBinaryFile($binaryFile); + } + } + } +} + +class_alias(IORepositoryResolver::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\IORepositoryResolver'); diff --git a/src/bundle/Core/Imagine/ImageAsset/AliasGenerator.php b/src/bundle/Core/Imagine/ImageAsset/AliasGenerator.php new file mode 100644 index 0000000000..9c4f8fab73 --- /dev/null +++ b/src/bundle/Core/Imagine/ImageAsset/AliasGenerator.php @@ -0,0 +1,83 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\Imagine\ImageAsset; + +use Ibexa\Contracts\Core\FieldType\Value; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Variation\Values\Variation; +use Ibexa\Contracts\Core\Variation\VariationHandler; +use Ibexa\Core\FieldType\ImageAsset\AssetMapper; +use Ibexa\Core\FieldType\ImageAsset\Value as ImageAssetValue; + +/** + * Alias Generator Decorator allowing generate variations based on passed ImageAsset\Value. + */ +class AliasGenerator implements VariationHandler +{ + /** @var \Ibexa\Contracts\Core\Variation\VariationHandler */ + private $innerAliasGenerator; + + /** @var \Ibexa\Contracts\Core\Repository\ContentService */ + private $contentService; + + /** @var \Ibexa\Core\FieldType\ImageAsset\AssetMapper */ + private $assetMapper; + + /** + * @param \Ibexa\Contracts\Core\Variation\VariationHandler $innerAliasGenerator + * @param \Ibexa\Contracts\Core\Repository\ContentService $contentService + * @param \Ibexa\Core\FieldType\ImageAsset\AssetMapper $assetMapper + */ + public function __construct( + VariationHandler $innerAliasGenerator, + ContentService $contentService, + AssetMapper $assetMapper + ) { + $this->innerAliasGenerator = $innerAliasGenerator; + $this->contentService = $contentService; + $this->assetMapper = $assetMapper; + } + + /** + * {@inheritdoc} + */ + public function getVariation(Field $field, VersionInfo $versionInfo, $variationName, array $parameters = []): Variation + { + if ($this->supportsValue($field->value)) { + $destinationContent = $this->contentService->loadContent( + (int)$field->value->destinationContentId + ); + + return $this->innerAliasGenerator->getVariation( + $this->assetMapper->getAssetField($destinationContent), + $destinationContent->versionInfo, + $variationName, + $parameters + ); + } + + return $this->innerAliasGenerator->getVariation($field, $versionInfo, $variationName, $parameters); + } + + /** + * Returns TRUE if the value is supported by alias generator. + * + * @param \Ibexa\Contracts\Core\FieldType\Value $value + * + * @return bool + */ + public function supportsValue(Value $value): bool + { + return $value instanceof ImageAssetValue; + } +} + +class_alias(AliasGenerator::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\ImageAsset\AliasGenerator'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderAliasGenerator.php b/src/bundle/Core/Imagine/PlaceholderAliasGenerator.php similarity index 79% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderAliasGenerator.php rename to src/bundle/Core/Imagine/PlaceholderAliasGenerator.php index 5605e11707..6bdb880aa5 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderAliasGenerator.php +++ b/src/bundle/Core/Imagine/PlaceholderAliasGenerator.php @@ -4,32 +4,32 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine; - -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException as APIInvalidArgumentException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\FieldType\Image\Value as ImageValue; -use eZ\Publish\Core\FieldType\Value; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\SPI\Variation\VariationHandler; +namespace Ibexa\Bundle\Core\Imagine; + +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException as APIInvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Variation\VariationHandler; +use Ibexa\Core\FieldType\Image\Value as ImageValue; +use Ibexa\Core\FieldType\Value; +use Ibexa\Core\IO\IOServiceInterface; use InvalidArgumentException; use Liip\ImagineBundle\Exception\Imagine\Cache\Resolver\NotResolvableException; use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface; class PlaceholderAliasGenerator implements VariationHandler { - /** @var \eZ\Publish\SPI\Variation\VariationHandler */ + /** @var \Ibexa\Contracts\Core\Variation\VariationHandler */ private $aliasGenerator; /** @var \Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface */ private $ioResolver; - /** @var \eZ\Publish\Core\IO\IOServiceInterface */ + /** @var \Ibexa\Core\IO\IOServiceInterface */ private $ioService; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider|null */ + /** @var \Ibexa\Bundle\Core\Imagine\PlaceholderProvider|null */ private $placeholderProvider; /** @var array */ @@ -54,7 +54,7 @@ public function __construct( public function getVariation(Field $field, VersionInfo $versionInfo, $variationName, array $parameters = []) { if ($this->placeholderProvider !== null) { - /** @var \eZ\Publish\Core\FieldType\Image\Value $imageValue */ + /** @var \Ibexa\Core\FieldType\Image\Value $imageValue */ $imageValue = $field->value; if (!$this->supportsValue($imageValue)) { throw new InvalidArgumentException("Value of Field with ID {$field->id} ($field->fieldDefIdentifier) cannot be used for generating an image placeholder."); @@ -116,3 +116,5 @@ private function isOriginalImageAvailable(ImageValue $imageValue): bool return true; } } + +class_alias(PlaceholderAliasGenerator::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderAliasGenerator'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderAliasGeneratorConfigurator.php b/src/bundle/Core/Imagine/PlaceholderAliasGeneratorConfigurator.php similarity index 76% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderAliasGeneratorConfigurator.php rename to src/bundle/Core/Imagine/PlaceholderAliasGeneratorConfigurator.php index a179b6f4f4..3148e7382f 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderAliasGeneratorConfigurator.php +++ b/src/bundle/Core/Imagine/PlaceholderAliasGeneratorConfigurator.php @@ -4,16 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine; +namespace Ibexa\Bundle\Core\Imagine; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; class PlaceholderAliasGeneratorConfigurator { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProviderRegistry */ + /** @var \Ibexa\Bundle\Core\Imagine\PlaceholderProviderRegistry */ private $providerRegistry; /** @var array */ @@ -43,3 +43,5 @@ public function configure(PlaceholderAliasGenerator $generator) } } } + +class_alias(PlaceholderAliasGeneratorConfigurator::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderAliasGeneratorConfigurator'); diff --git a/src/bundle/Core/Imagine/PlaceholderProvider.php b/src/bundle/Core/Imagine/PlaceholderProvider.php new file mode 100644 index 0000000000..59ed1eed02 --- /dev/null +++ b/src/bundle/Core/Imagine/PlaceholderProvider.php @@ -0,0 +1,24 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine; + +use Ibexa\Core\FieldType\Image\Value as ImageValue; + +interface PlaceholderProvider +{ + /** + * Provides a placeholder image path for a given Image FieldType value. + * + * @param \Ibexa\Core\FieldType\Image\Value $value + * @param array $options + * + * @return string Path to placeholder + */ + public function getPlaceholder(ImageValue $value, array $options = []): string; +} + +class_alias(PlaceholderProvider::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProvider/GenericProvider.php b/src/bundle/Core/Imagine/PlaceholderProvider/GenericProvider.php similarity index 91% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProvider/GenericProvider.php rename to src/bundle/Core/Imagine/PlaceholderProvider/GenericProvider.php index 3b91351caa..2be35b5ae4 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProvider/GenericProvider.php +++ b/src/bundle/Core/Imagine/PlaceholderProvider/GenericProvider.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider; +namespace Ibexa\Bundle\Core\Imagine\PlaceholderProvider; -use eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider; -use eZ\Publish\Core\FieldType\Image\Value as ImageValue; +use Ibexa\Bundle\Core\Imagine\PlaceholderProvider; +use Ibexa\Core\FieldType\Image\Value as ImageValue; use Imagine\Image as Image; use Imagine\Image\ImagineInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -102,3 +102,5 @@ private function resolveOptions(array $options): array return $resolver->resolve($options); } } + +class_alias(GenericProvider::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider\GenericProvider'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProvider/RemoteProvider.php b/src/bundle/Core/Imagine/PlaceholderProvider/RemoteProvider.php similarity index 88% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProvider/RemoteProvider.php rename to src/bundle/Core/Imagine/PlaceholderProvider/RemoteProvider.php index 85652e1842..f85ad9d29e 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProvider/RemoteProvider.php +++ b/src/bundle/Core/Imagine/PlaceholderProvider/RemoteProvider.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider; +namespace Ibexa\Bundle\Core\Imagine\PlaceholderProvider; -use eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider; -use eZ\Publish\Core\FieldType\Image\Value as ImageValue; +use Ibexa\Bundle\Core\Imagine\PlaceholderProvider; +use Ibexa\Core\FieldType\Image\Value as ImageValue; use RuntimeException; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -73,3 +73,5 @@ private function resolveOptions(array $options): array return $resolver->resolve($options); } } + +class_alias(RemoteProvider::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider\RemoteProvider'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProviderRegistry.php b/src/bundle/Core/Imagine/PlaceholderProviderRegistry.php similarity index 81% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProviderRegistry.php rename to src/bundle/Core/Imagine/PlaceholderProviderRegistry.php index b4530678d2..7935234de6 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/PlaceholderProviderRegistry.php +++ b/src/bundle/Core/Imagine/PlaceholderProviderRegistry.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine; +namespace Ibexa\Bundle\Core\Imagine; use InvalidArgumentException; class PlaceholderProviderRegistry { - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider */ + /** @var \Ibexa\Bundle\Core\Imagine\PlaceholderProvider */ private $providers; /** @@ -42,3 +42,5 @@ public function getProvider(string $type): PlaceholderProvider return $this->providers[$type]; } } + +class_alias(PlaceholderProviderRegistry::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProviderRegistry'); diff --git a/src/bundle/Core/Imagine/Variation/ImagineAwareAliasGenerator.php b/src/bundle/Core/Imagine/Variation/ImagineAwareAliasGenerator.php new file mode 100644 index 0000000000..b861eda957 --- /dev/null +++ b/src/bundle/Core/Imagine/Variation/ImagineAwareAliasGenerator.php @@ -0,0 +1,123 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine\Variation; + +use Ibexa\Bundle\Core\Imagine\IORepositoryResolver; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Variation\Values\ImageVariation; +use Ibexa\Contracts\Core\Variation\VariationHandler; +use Ibexa\Contracts\Core\Variation\VariationPathGenerator; +use Ibexa\Core\IO\IOServiceInterface; +use Imagine\Image\ImagineInterface; + +/** + * Alias Generator Decorator which ensures (using Imagine if needed) that ImageVariation has proper + * dimensions. + */ +class ImagineAwareAliasGenerator implements VariationHandler +{ + /** @var \Ibexa\Contracts\Core\Variation\VariationHandler */ + private $aliasGenerator; + + /** @var \Ibexa\Contracts\Core\Variation\VariationPathGenerator */ + private $variationPathGenerator; + + /** @var \Ibexa\Core\IO\IOServiceInterface */ + private $ioService; + + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ + private $configResolver; + + /** @var \Imagine\Image\ImagineInterface */ + private $imagine; + + public function __construct( + VariationHandler $aliasGenerator, + VariationPathGenerator $variationPathGenerator, + IOServiceInterface $ioService, + ImagineInterface $imagine + ) { + $this->aliasGenerator = $aliasGenerator; + $this->variationPathGenerator = $variationPathGenerator; + $this->ioService = $ioService; + $this->imagine = $imagine; + } + + /** + * Returns a Variation object, ensuring proper image dimensions. + * + * {@inheritdoc} + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function getVariation( + Field $field, + VersionInfo $versionInfo, + $variationName, + array $parameters = [] + ) { + /** @var \Ibexa\Contracts\Core\Variation\Values\ImageVariation $variation */ + $variation = $this->aliasGenerator->getVariation( + $field, + $versionInfo, + $variationName, + $parameters + ); + + if (null === $variation->width || null === $variation->height) { + $variationBinaryFile = $this->getVariationBinaryFile($field->value->id, $variationName); + $image = $this->imagine->load($this->ioService->getFileContents($variationBinaryFile)); + $dimensions = $image->getSize(); + + return new ImageVariation( + [ + 'name' => $variation->name, + 'fileName' => $variation->fileName, + 'dirPath' => $variation->dirPath, + 'uri' => $variation->uri, + 'imageId' => $variation->imageId, + 'width' => $dimensions->getWidth(), + 'height' => $dimensions->getHeight(), + 'fileSize' => $variationBinaryFile->size, + 'mimeType' => $this->ioService->getMimeType($variationBinaryFile->id), + 'lastModified' => $variationBinaryFile->mtime, + ] + ); + } + + return $variation; + } + + /** + * Get image variation filesystem path. + * + * @param string $originalPath + * @param string $variationName + * + * @return \Ibexa\Core\IO\Values\BinaryFile + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function getVariationBinaryFile($originalPath, $variationName) + { + if ($variationName !== IORepositoryResolver::VARIATION_ORIGINAL) { + $variationPath = $this->variationPathGenerator->getVariationPath( + $originalPath, + $variationName + ); + } else { + $variationPath = $originalPath; + } + + return $this->ioService->loadBinaryFile($variationPath); + } +} + +class_alias(ImagineAwareAliasGenerator::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\Variation\ImagineAwareAliasGenerator'); diff --git a/src/bundle/Core/Imagine/VariationPathGenerator.php b/src/bundle/Core/Imagine/VariationPathGenerator.php new file mode 100644 index 0000000000..1c6decc7ac --- /dev/null +++ b/src/bundle/Core/Imagine/VariationPathGenerator.php @@ -0,0 +1,18 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine; + +use Ibexa\Contracts\Core\Variation\VariationPathGenerator as VariationPathGeneratorContract; + +/** + * @deprecated 4.4.0 Use \Ibexa\Contracts\Core\Variation\VariationPathGenerator instead. + */ +interface VariationPathGenerator extends VariationPathGeneratorContract +{ +} + +class_alias(VariationPathGenerator::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator'); diff --git a/src/bundle/Core/Imagine/VariationPathGenerator/AliasDirectoryVariationPathGenerator.php b/src/bundle/Core/Imagine/VariationPathGenerator/AliasDirectoryVariationPathGenerator.php new file mode 100644 index 0000000000..3b30f66d35 --- /dev/null +++ b/src/bundle/Core/Imagine/VariationPathGenerator/AliasDirectoryVariationPathGenerator.php @@ -0,0 +1,33 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine\VariationPathGenerator; + +use Ibexa\Contracts\Core\Variation\VariationPathGenerator; + +/** + * Puts variations in the an _alias/<aliasName> subfolder. + * + * Example: + * my/image/file.jpg -> _aliases/large/my/image/file.jpg + */ +class AliasDirectoryVariationPathGenerator implements VariationPathGenerator +{ + public function getVariationPath($originalPath, $filter) + { + $info = pathinfo($originalPath); + + return sprintf( + '_aliases/%s/%s/%s%s', + $filter, + $info['dirname'], + $info['filename'], + empty($info['extension']) ? '' : '.' . $info['extension'] + ); + } +} + +class_alias(AliasDirectoryVariationPathGenerator::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator\AliasDirectoryVariationPathGenerator'); diff --git a/src/bundle/Core/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGenerator.php b/src/bundle/Core/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGenerator.php new file mode 100644 index 0000000000..630d7a9f08 --- /dev/null +++ b/src/bundle/Core/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGenerator.php @@ -0,0 +1,33 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine\VariationPathGenerator; + +use Ibexa\Contracts\Core\Variation\VariationPathGenerator; + +/** + * Puts variations in the same folder than the original, suffixed with the filter name:. + * + * Example: + * my/image/file.jpg -> my/image/file_large.jpg + */ +class OriginalDirectoryVariationPathGenerator implements VariationPathGenerator +{ + public function getVariationPath($originalPath, $filter) + { + $info = pathinfo($originalPath); + + return sprintf( + '%s/%s_%s%s', + $info['dirname'], + $info['filename'], + $filter, + empty($info['extension']) ? '' : '.' . $info['extension'] + ); + } +} + +class_alias(OriginalDirectoryVariationPathGenerator::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator\OriginalDirectoryVariationPathGenerator'); diff --git a/src/bundle/Core/Imagine/VariationPathGenerator/WebpFormatVariationPathGenerator.php b/src/bundle/Core/Imagine/VariationPathGenerator/WebpFormatVariationPathGenerator.php index 0eff5700c8..62d9809e45 100644 --- a/src/bundle/Core/Imagine/VariationPathGenerator/WebpFormatVariationPathGenerator.php +++ b/src/bundle/Core/Imagine/VariationPathGenerator/WebpFormatVariationPathGenerator.php @@ -8,7 +8,7 @@ namespace Ibexa\Bundle\Core\Imagine\VariationPathGenerator; -use eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator; +use Ibexa\Contracts\Core\Variation\VariationPathGenerator; use Liip\ImagineBundle\Imagine\Filter\FilterConfiguration; /** @@ -16,11 +16,9 @@ */ final class WebpFormatVariationPathGenerator implements VariationPathGenerator { - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator */ - private $innerVariationPathGenerator; + private VariationPathGenerator $innerVariationPathGenerator; - /** @var \Liip\ImagineBundle\Imagine\Filter\FilterConfiguration */ - private $filterConfiguration; + private FilterConfiguration $filterConfiguration; public function __construct( VariationPathGenerator $innerVariationPathGenerator, diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/IOVariationPurger.php b/src/bundle/Core/Imagine/VariationPurger/IOVariationPurger.php similarity index 82% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/IOVariationPurger.php rename to src/bundle/Core/Imagine/VariationPurger/IOVariationPurger.php index 9beec249ad..73393f4afe 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/IOVariationPurger.php +++ b/src/bundle/Core/Imagine/VariationPurger/IOVariationPurger.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger; +namespace Ibexa\Bundle\Core\Imagine\VariationPurger; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\AliasGeneratorDecorator; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\SPI\Variation\VariationPurger; +use Ibexa\Bundle\Core\Imagine\Cache\AliasGeneratorDecorator; +use Ibexa\Contracts\Core\Variation\VariationPurger; +use Ibexa\Core\IO\IOServiceInterface; use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface; use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; @@ -19,7 +19,7 @@ */ class IOVariationPurger implements VariationPurger { - /** @var \eZ\Publish\Core\IO\IOServiceInterface */ + /** @var \Ibexa\Core\IO\IOServiceInterface */ private $io; /** @var \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface */ @@ -28,7 +28,7 @@ class IOVariationPurger implements VariationPurger /** @var \Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface */ private $cacheIdentifierGenerator; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\AliasGeneratorDecorator */ + /** @var \Ibexa\Bundle\Core\Imagine\Cache\AliasGeneratorDecorator */ private $aliasGeneratorDecorator; /** @var \Psr\Log\LoggerInterface */ @@ -71,3 +71,5 @@ public function purge(array $aliasNames) } } } + +class_alias(IOVariationPurger::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\IOVariationPurger'); diff --git a/src/bundle/Core/Imagine/VariationPurger/ImageFileList.php b/src/bundle/Core/Imagine/VariationPurger/ImageFileList.php new file mode 100644 index 0000000000..d3b609e938 --- /dev/null +++ b/src/bundle/Core/Imagine/VariationPurger/ImageFileList.php @@ -0,0 +1,19 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Imagine\VariationPurger; + +use Countable; +use Iterator; + +/** + * Iterates over BinaryFile id entries for original images. + */ +interface ImageFileList extends Countable, Iterator +{ +} + +class_alias(ImageFileList::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\ImageFileList'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/ImageFileRowReader.php b/src/bundle/Core/Imagine/VariationPurger/ImageFileRowReader.php similarity index 80% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/ImageFileRowReader.php rename to src/bundle/Core/Imagine/VariationPurger/ImageFileRowReader.php index f5005f1838..cf8b031ee1 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/ImageFileRowReader.php +++ b/src/bundle/Core/Imagine/VariationPurger/ImageFileRowReader.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger; +namespace Ibexa\Bundle\Core\Imagine\VariationPurger; /** * Reads original image files from a data source. @@ -32,3 +32,5 @@ public function getRow(); */ public function getCount(); } + +class_alias(ImageFileRowReader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\ImageFileRowReader'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/ImageFileVariationPurger.php b/src/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurger.php similarity index 81% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/ImageFileVariationPurger.php rename to src/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurger.php index edce820486..081a029deb 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/ImageFileVariationPurger.php +++ b/src/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurger.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger; +namespace Ibexa\Bundle\Core\Imagine\VariationPurger; -use eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\SPI\Variation\VariationPurger; +use Ibexa\Contracts\Core\Variation\VariationPathGenerator; +use Ibexa\Contracts\Core\Variation\VariationPurger; +use Ibexa\Core\IO\IOServiceInterface; use Iterator; /** @@ -22,10 +22,10 @@ class ImageFileVariationPurger implements VariationPurger /** @var ImageFileList */ private $imageFileList; - /** @var \eZ\Publish\Core\IO\IOServiceInterface */ + /** @var \Ibexa\Core\IO\IOServiceInterface */ private $ioService; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator */ + /** @var \Ibexa\Contracts\Core\Variation\VariationPathGenerator */ private $variationPathGenerator; /** @var \Psr\Log\LoggerInterface */ @@ -69,3 +69,5 @@ public function setLogger($logger) $this->logger = $logger; } } + +class_alias(ImageFileVariationPurger::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\ImageFileVariationPurger'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/LegacyStorageImageFileList.php b/src/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileList.php similarity index 77% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/LegacyStorageImageFileList.php rename to src/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileList.php index e6e4ff3772..ab959e6327 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/LegacyStorageImageFileList.php +++ b/src/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileList.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger; +namespace Ibexa\Bundle\Core\Imagine\VariationPurger; -use eZ\Publish\Core\IO\IOConfigProvider; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\IO\IOConfigProvider; /** * Iterator for entries in legacy's ezimagefile table. * - * The returned items are id of Image BinaryFile (ez-mountains/mount-aconcagua/605-1-eng-GB/Mount-Aconcagua.jpg). + * The returned items are id of Image BinaryFile (ibexa-mountains/mount-aconcagua/605-1-eng-GB/Mount-Aconcagua.jpg). */ class LegacyStorageImageFileList implements ImageFileList { @@ -33,14 +33,14 @@ class LegacyStorageImageFileList implements ImageFileList /** * Used to get ezimagefile rows. * - * @var \eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\ImageFileRowReader + * @var \Ibexa\Bundle\Core\Imagine\VariationPurger\ImageFileRowReader */ private $rowReader; - /** @var \eZ\Publish\Core\IO\IOConfigProvider */ + /** @var \Ibexa\Core\IO\IOConfigProvider */ private $ioConfigResolver; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; public function __construct( @@ -91,7 +91,7 @@ public function count(): int */ private function fetchRow(): void { - // Folder, relative to the root, where files are stored. Example: var/ezdemo_site/storage + // Folder, relative to the root, where files are stored. Example: var/ibexa_demo_site/storage $storageDir = $this->ioConfigResolver->getLegacyUrlPrefix(); $prefix = $storageDir . '/' . $this->configResolver->getParameter('image.published_images_dir'); ++$this->cursor; @@ -104,3 +104,5 @@ private function fetchRow(): void $this->item = $imageId; } } + +class_alias(LegacyStorageImageFileList::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\LegacyStorageImageFileList'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/LegacyStorageImageFileRowReader.php b/src/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileRowReader.php similarity index 82% rename from eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/LegacyStorageImageFileRowReader.php rename to src/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileRowReader.php index 0368a2c171..37194de053 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Imagine/VariationPurger/LegacyStorageImageFileRowReader.php +++ b/src/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileRowReader.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger; +namespace Ibexa\Bundle\Core\Imagine\VariationPurger; use Doctrine\DBAL\Connection; @@ -38,3 +38,5 @@ public function getCount() return $this->statement->rowCount(); } } + +class_alias(LegacyStorageImageFileRowReader::class, 'eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\LegacyStorageImageFileRowReader'); diff --git a/src/bundle/Core/Matcher/ServiceAwareMatcherFactory.php b/src/bundle/Core/Matcher/ServiceAwareMatcherFactory.php new file mode 100644 index 0000000000..8c6d749f5e --- /dev/null +++ b/src/bundle/Core/Matcher/ServiceAwareMatcherFactory.php @@ -0,0 +1,51 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Bundle\Core\Matcher; + +use Ibexa\Contracts\Core\MVC\View\ViewMatcherRegistryInterface; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Core\MVC\Symfony\Matcher\ClassNameMatcherFactory; +use Ibexa\Core\MVC\Symfony\Matcher\ViewMatcherInterface; + +/** + * A view matcher factory that also accepts services as matchers. + * + * If a service id is passed as the MatcherIdentifier, this service will be used for the matching. + * If a view matcher service is registered with `identifier` attribute, that service will be used for matching. * + * Otherwise, it will fall back to the class name-based matcher factory. + */ +final class ServiceAwareMatcherFactory extends ClassNameMatcherFactory +{ + private ViewMatcherRegistryInterface $viewMatcherRegistry; + + public function __construct( + ViewMatcherRegistryInterface $viewMatcherRegistry, + Repository $repository, + ?string $relativeNamespace = null, + array $matchConfig = [] + ) { + $this->viewMatcherRegistry = $viewMatcherRegistry; + + parent::__construct($repository, $relativeNamespace, $matchConfig); + } + + /** + * @param string $matcherIdentifier + */ + protected function getMatcher($matcherIdentifier): ViewMatcherInterface + { + if (strpos($matcherIdentifier, '@') === 0) { + $matcherIdentifier = substr($matcherIdentifier, 1); + } + + return $this->viewMatcherRegistry->hasMatcher($matcherIdentifier) + ? $this->viewMatcherRegistry->getMatcher($matcherIdentifier) + : parent::getMatcher($matcherIdentifier); + } +} + +class_alias(ServiceAwareMatcherFactory::class, 'eZ\Bundle\EzPublishCoreBundle\Matcher\ServiceAwareMatcherFactory'); diff --git a/src/bundle/Core/Matcher/ViewMatcherRegistry.php b/src/bundle/Core/Matcher/ViewMatcherRegistry.php new file mode 100644 index 0000000000..96c7275c01 --- /dev/null +++ b/src/bundle/Core/Matcher/ViewMatcherRegistry.php @@ -0,0 +1,61 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Bundle\Core\Matcher; + +use Ibexa\Contracts\Core\MVC\View\ViewMatcherRegistryInterface; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\MVC\Symfony\Matcher\ViewMatcherInterface; + +/** + * @internal + */ +final class ViewMatcherRegistry implements ViewMatcherRegistryInterface +{ + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ViewMatcherInterface[] */ + private $matchers; + + /** + * @param iterable<\Ibexa\Core\MVC\Symfony\Matcher\ViewMatcherInterface> $matchers + */ + public function __construct(iterable $matchers = []) + { + $this->matchers = []; + foreach ($matchers as $identifier => $matcher) { + $this->matchers[$identifier] = $matcher; + } + } + + public function setMatcher(string $matcherIdentifier, ViewMatcherInterface $matcher): void + { + $this->matchers[$matcherIdentifier] = $matcher; + } + + /** + * @param string $matcherIdentifier + * + * @return \Ibexa\Core\MVC\Symfony\Matcher\ViewMatcherInterface + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function getMatcher(string $matcherIdentifier): ViewMatcherInterface + { + if (!isset($this->matchers[$matcherIdentifier])) { + throw new NotFoundException('Matcher', $matcherIdentifier); + } + + return $this->matchers[$matcherIdentifier]; + } + + public function hasMatcher(string $matcherIdentifier): bool + { + return isset($this->matchers[$matcherIdentifier]); + } +} + +class_alias(ViewMatcherRegistry::class, 'eZ\Bundle\EzPublishCoreBundle\Matcher\ViewMatcherRegistry'); diff --git a/src/bundle/Core/Resources/config/cache.yml b/src/bundle/Core/Resources/config/cache.yml new file mode 100644 index 0000000000..659f24f637 --- /dev/null +++ b/src/bundle/Core/Resources/config/cache.yml @@ -0,0 +1,17 @@ +services: + ibexa.cache_pool: + # As we support custom TagAware services, we set class as interface here so lazy class is "correct" + class: Symfony\Component\Cache\Adapter\TagAwareAdapterInterface + factory: ['@Ibexa\Bundle\Core\ApiLoader\CacheFactory', getCachePool] + arguments: ['@ibexa.config.resolver'] + + Ibexa\Bundle\Core\ApiLoader\CacheFactory: + class: Ibexa\Bundle\Core\ApiLoader\CacheFactory + calls: + - [setContainer, ["@service_container"]] + + Ibexa\Bundle\Core\Cache\Warmer\ProxyCacheWarmer: + arguments: + - '@Ibexa\Core\Repository\ProxyFactory\ProxyGeneratorInterface' + tags: + - { name: kernel.cache_warmer } diff --git a/src/bundle/Core/Resources/config/commands.yml b/src/bundle/Core/Resources/config/commands.yml new file mode 100644 index 0000000000..bd92d66a04 --- /dev/null +++ b/src/bundle/Core/Resources/config/commands.yml @@ -0,0 +1,71 @@ +services: + Ibexa\Bundle\Core\Command\RegenerateUrlAliasesCommand: + class: Ibexa\Bundle\Core\Command\RegenerateUrlAliasesCommand + arguments: + # intentionally passing inner repository to avoid sending Events due to performance issues + - '@Ibexa\Core\Repository\Repository' + - '@?logger' + tags: + - { name: console.command } + + Ibexa\Bundle\Core\Command\DebugConfigResolverCommand: + class: Ibexa\Bundle\Core\Command\DebugConfigResolverCommand + arguments: + - '@ibexa.config.resolver' + - '@Ibexa\Core\MVC\Symfony\SiteAccess' + tags: + - { name: console.command } + + Ibexa\Bundle\Core\Command\CheckURLsCommand: + class: Ibexa\Bundle\Core\Command\CheckURLsCommand + arguments: + - '@Ibexa\Contracts\Core\Repository\UserService' + - '@Ibexa\Contracts\Core\Repository\PermissionResolver' + - '@Ibexa\Contracts\Core\Repository\URLService' + - '@Ibexa\Bundle\Core\URLChecker\URLChecker' + tags: + - { name: console.command } + + Ibexa\Bundle\Core\Command\UpdateTimestampsToUTCCommand: + arguments: + - '@ibexa.api.search_engine.legacy.connection' + tags: + - { name: console.command } + + Ibexa\Bundle\Core\Command\ReindexCommand: + autowire: true + autoconfigure: true + arguments: + $searchIndexer: '@ibexa.spi.search.indexer' + $locationHandler: '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + $siteaccess: '@Ibexa\Core\MVC\Symfony\SiteAccess' + $env: '%kernel.environment%' + $projectDir: '%kernel.project_dir%' + $isDebug: '%kernel.debug%' + $contentIdListGeneratorStrategy: '@Ibexa\Bundle\Core\Command\Indexer\ContentIdList\ContentTypeInputGeneratorStrategy' + tags: + - { name: console.command } + + Ibexa\Bundle\Core\Command\NormalizeImagesPathsCommand: + autowire: true + autoconfigure: true + arguments: + $connection: '@ibexa.persistence.connection' + $imageGateway: '@Ibexa\Core\FieldType\Image\ImageStorage\Gateway\DoctrineStorage' + $ioService: '@Ibexa\Core\FieldType\Image\IO\Legacy' + tags: + - { name: console.command } + + Ibexa\Bundle\Core\Command\SetSystemContentTypeGroupCommand: + autowire: true + autoconfigure: true + tags: + - { name: console.command } + + Ibexa\Bundle\Core\Command\ExpireUserPasswordsCommand: + autowire: true + autoconfigure: true + + # Dedicated services for commands + Ibexa\Bundle\Core\Command\Indexer\ContentIdList\ContentTypeInputGeneratorStrategy: + autowire: true diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/debug/cache_validator.yaml b/src/bundle/Core/Resources/config/debug/cache_validator.yaml similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Resources/config/debug/cache_validator.yaml rename to src/bundle/Core/Resources/config/debug/cache_validator.yaml diff --git a/src/bundle/Core/Resources/config/default_settings.yml b/src/bundle/Core/Resources/config/default_settings.yml new file mode 100644 index 0000000000..7f34d7fe3f --- /dev/null +++ b/src/bundle/Core/Resources/config/default_settings.yml @@ -0,0 +1,273 @@ +parameters: + # Kernel related params + webroot_dir: "%kernel.project_dir%/public" + + ibexa.trusted_header_client_ip_name: ~ + + ### + # ibexa.site_access.config namespace, default scope + ### + + # Content/Location view + ibexa.site_access.config.default.location_view: {} + ibexa.site_access.config.default.content_view: {} + ibexa.site_access.config.default.block_view: {} + + # Default Twig variables + ibexa.site_access.config.default.twig_variables: {} + + # Default view templates + ibexa.default_view_templates.content.full: '@@IbexaCore/default/content/full.html.twig' + ibexa.default_view_templates.content.line: '@@IbexaCore/default/content/line.html.twig' + ibexa.default_view_templates.content.text_linked: '@@IbexaCore/default/content/text_linked.html.twig' + ibexa.default_view_templates.content.embed: '@@IbexaCore/default/content/embed.html.twig' + ibexa.default_view_templates.content.embed_inline: '@@IbexaCore/default/content/embed_inline.html.twig' + ibexa.default_view_templates.content.embed_image: '@@IbexaCore/default/content/embed_image.html.twig' + ibexa.default_view_templates.content.asset_image: '@@IbexaCore/default/content/asset_image.html.twig' + ibexa.default_view_templates.block: '@@IbexaCore/default/block/block.html.twig' + + # Default templates + ibexa.default_templates.pagelayout: '@@IbexaCore/pagelayout.html.twig' + ibexa.default_templates.field_templates: '@@IbexaCore/content_fields.html.twig' + ibexa.default_templates.fielddefinition_settings_templates: '@@IbexaCore/fielddefinition_settings.html.twig' + + # Image Asset mappings + ibexa.site_access.config.default.fieldtypes.ezimageasset.mappings: + content_type_identifier: image + content_field_identifier: image + name_field_identifier: name + parent_location_id: 51 + + ibexa.site_access.config.default.pagelayout: '%ibexa.default_templates.pagelayout%' + ibexa.site_access.config.default.page_layout: '%ibexa.default_templates.pagelayout%' + + # List of content type identifiers to display as image when embedded + ibexa.content_view.image_embed_content_types_identifiers: ['image'] + + ibexa.site_access.config.default.content_view_defaults: + full: + default: + template: '%ibexa.default_view_templates.content.full%' + match: [] + line: + default: + template: '%ibexa.default_view_templates.content.line%' + match: [] + text_linked: + default: + template: '%ibexa.default_view_templates.content.text_linked%' + match: [] + embed: + image: + template: '%ibexa.default_view_templates.content.embed_image%' + match: + Identifier\ContentType: '%ibexa.content_view.image_embed_content_types_identifiers%' + default: + template: '%ibexa.default_view_templates.content.embed%' + match: [] + embed-inline: + default: + template: '%ibexa.default_view_templates.content.embed_inline%' + match: [] + asset_image: + default: + template: '%ibexa.default_view_templates.content.asset_image%' + match: [] + + ibexa.site_access.config.default.block_view_defaults: + block: + default: + template: '%ibexa.default_view_templates.block%' + match: [] + + # Common settings + ibexa.repositories: {} + ibexa.site_access.config.default.repository: ~ + ibexa.session_name.default: "eZSESSID{siteaccess_hash}" + ibexa.site_access.config.default.session_name: '%ibexa.session_name.default%' # Using "{siteaccess_hash}" in session name makes it unique per siteaccess + ibexa.site_access.config.default.session: { name: '%ibexa.session_name.default%' } # Session options that will override options from framework + ibexa.site_access.config.default.url_alias_router: true # Use UrlAliasRouter by default + ibexa.site_access.config.default.index_page: ~ # The page to show when accessing IndexPage (/) + ibexa.site_access.config.default.default_page: ~ # The default page to show, e.g. after user login this will be used for default redirection + ibexa.site_access.config.default.languages: [] + ibexa.site_access.config.default.translation_siteaccesses: [] + ibexa.site_access.config.default.related_siteaccesses: [] + ibexa.site_access.config.default.cache_service_name: "cache.app" # The cache pool serive name to use for a siteaccess / siteaccess-group + ibexa.site_access.config.default.var_dir: "var" # The root directory where all log files, cache files and other stored files are created + ibexa.site_access.config.default.storage_dir: "storage" # Where to place new files for storage, it's relative to var directory + ibexa.site_access.config.default.binary_dir: "original" + ibexa.site_access.config.default.anonymous_user_id: 10 # The ID of the user to be used for everyone who is not logged in + ibexa.site_access.config.default.user_content_type_identifier: ['user'] + ibexa.site_access.config.default.api_keys: { google_maps: ~ } # Google Maps APIs v3 key (https://developers.google.com/maps/documentation/javascript/get-api-key) + + # IO + ibexa.site_access.config.default.io.metadata_handler: "default" + ibexa.site_access.config.default.io.binarydata_handler: "default" + ibexa.site_access.config.default.io.url_prefix: "$var_dir$/$storage_dir$" + ibexa.site_access.config.default.io.legacy_url_prefix: "$var_dir$/$storage_dir$" + ibexa.site_access.config.default.io.root_dir: "%webroot_dir%/$var_dir$/$storage_dir$" + ibexa.site_access.config.default.io.permissions.files: 0644 + ibexa.site_access.config.default.io.permissions.directories: 0755 + # Blacklist against storing certain file types, validation will be refused for these + ibexa.site_access.config.default.io.file_storage.file_type_blacklist: + - php + - php3 + - php4 + - php5 + - phps + - phar + - phpt + - pht + - phtml + - pgif + - hta + - htm + - html + - xhtm + - xhtml + - jar + - js + - jse + - svg + - swf + + # Content settings + ibexa.site_access.config.default.content.view_cache: true # Whether to use content view cache or not (Etag/Last-Modified based) + ibexa.site_access.config.default.content.ttl_cache: true # Whether to use TTL Cache for content (i.e. Max-Age response header) + ibexa.site_access.config.default.content.default_ttl: 60 # Default TTL cache value for content + ibexa.site_access.config.default.content.tree_root.location_id: 2 # Root locationId for routing and link generation. Useful for multisite apps with one repository. + ibexa.site_access.config.default.content.tree_root.excluded_uri_prefixes: [] # URI prefixes that are allowed to be outside the content tree + ibexa.site_access.config.default.content.field_groups.list: ['content', 'metadata'] + ibexa.site_access.config.default.content.field_groups.default: 'content' + + # URL Wilcards + ibexa.site_access.config.default.url_wildcards.enabled: '%ibexa.url_wildcards.enabled%' + + # FieldType settings + + # Cache settings + # Server(s) URL(s) that will be used for purging HTTP cache with BAN requests. + ibexa.site_access.config.default.http_cache.purge_servers: [] + + # Treemenu settings (admin interface) + ibexa.site_access.config.default.treemenu.http_cache: true # Whether to use HttpCache or not for admin tree menu + ibexa.site_access.config.default.treemenu.ttl_cache: 86400 # If HttpCache is used, cache time to live in seconds + + # Templates to use while rendering fields + ibexa.site_access.config.default.field_templates: + - {template: '%ibexa.default_templates.field_templates%', priority: 0} + + # Templates for Field edition. Follows the same structure than for field_templates + ibexa.site_access.config.default.field_edit_templates: [] + + # Templates to use while rendering field definition settings + ibexa.site_access.config.default.fielddefinition_settings_templates: + - {template: '%ibexa.default_templates.fielddefinition_settings_templates%', priority: 0} + + # Templates for FieldDefinition edition. Follows the same structure than for field_templates + ibexa.site_access.config.default.fielddefinition_edit_templates: [] + + # Security settings + ibexa.site_access.config.default.security.login_template: "@@IbexaCore/Security/login.html.twig" + ibexa.site_access.config.default.security.base_layout: "%ibexa.site_access.config.default.page_layout%" + + # Image settings + ibexa.site_access.config.default.image.temporary_dir: imagetmp + ibexa.site_access.config.default.image.published_images_dir: images + ibexa.site_access.config.default.image.versioned_images_dir: images-versioned + + ibexa.site_access.config.default.variation_handler_identifier: alias + ibexa.site_access.config.default.image_variations: + original: + reference: + reference: ~ + filters: + geometry/scaledownonly: [600, 600] + small: + reference: reference + filters: + geometry/scaledownonly: [100, 100] + tiny: + reference: reference + filters: + geometry/scaledownonly: [30, 30] + medium: + reference: reference + filters: + geometry/scaledownonly: [200, 200] + large: + reference: reference + filters: + geometry/scaledownonly: [300, 300] + + # ImageMagick + # TODO: Deprecated. Move this to ezpublish_legacy. + ibexa.image.imagemagick.enabled: false + ibexa.image.imagemagick.executable_path: + ibexa.image.imagemagick.executable: convert + ibexa.site_access.config.default.imagemagick.pre_parameters: + ibexa.site_access.config.default.imagemagick.post_parameters: + ibexa.image.imagemagick.filters: + geometry/scale: "-geometry {1}x{2}" + geometry/scalewidth: "-geometry {1}" + geometry/scaleheight: "-geometry x{1}" + geometry/scaledownonly: "-geometry {1}x{2}>" + geometry/scalewidthdownonly: "-geometry {1}>" + geometry/scaleheightdownonly: "-geometry x{1}>" + geometry/scaleexact: "-geometry {1}x{2}!" + geometry/scalepercent: "-geometry {1}x{2}%" + geometry/crop: "-crop {1}x{2}+{3}+{4}" + filter/noise: "-noise {1}" + filter/swirl: "-swirl {1}" + colorspace/gray: "-colorspace GRAY" + colorspace/transparent: "-colorspace Transparent" + colorspace: "-colorspace {1}" + border: "-border {1}x{2}" + border/color: "-bordercolor rgb({1},{2},{3})" + border/width: "-borderwidth {1}" + flatten: "-flatten" + resize: "-resize {1}" + optimize: "-strip" + + ibexa.site_access.config.default.image_host: '' + + ibexa.site_access.config.default.url_handler.http.options: + timeout: 10 + connection_timeout: 5 + batch_size: 25 + ignore_certificate: false + ibexa.site_access.config.default.url_handler.https.options: + timeout: 10 + connection_timeout: 5 + batch_size: 25 + ignore_certificate: false + ibexa.site_access.config.default.url_handler.mailto.options: {} + + ### + # Internal settings + ### + ibexa.site_access.list: [] + ibexa.site_access.groups: {} + ibexa.site_access.groups_by_site_access: {} + ibexa.site_access.default: ~ + # SiteAccesses relation map. 2 dimensions array. + # First dimension is indexed by repository identifier. + # Second dimension is indexed by root location Id. + ibexa.site_access.relation_map: {} + # SiteAccesses, indexed by language. + ibexa.site_access.by_language: {} + + ## + # Siteaccess aware Entity Manager + ## + ibexa.orm.entity_mappings: [] + + # fallback for existing project configuration, should be overridden + dfs_nfs_path: '%ibexa.io.dir.storage%' + + ibexa.io.nfs.adapter.config: + root: '%dfs_nfs_path%' + path: '$var_dir$/$storage_dir$/' + writeFlags: ~ + linkHandling: ~ + permissions: [ ] diff --git a/src/bundle/Core/Resources/config/feature_contexts.yml b/src/bundle/Core/Resources/config/feature_contexts.yml new file mode 100644 index 0000000000..57e30a29f6 --- /dev/null +++ b/src/bundle/Core/Resources/config/feature_contexts.yml @@ -0,0 +1,49 @@ +services: + Ibexa\Bundle\Core\Features\Context\ContentContext: + public: true + arguments: + $repository: '@ibexa.api.repository' + + Ibexa\Bundle\Core\Features\Context\ConsoleContext: + public: true + arguments: + $configResolver: '@ibexa.config.resolver' + $siteaccessList: '%ibexa.site_access.list%' + $defaultSiteaccess: '%ibexa.site_access.default%' + + Ibexa\Bundle\Core\Features\Context\YamlConfigurationContext: + public: true + arguments: + $kernel: '@kernel' + + Ibexa\Bundle\Core\Features\Context\QueryControllerContext: ~ + + Ibexa\Bundle\Core\Features\Context\ContentTypeContext: + public: true + arguments: + $contentTypeService: '@ibexa.api.service.content_type' + + Ibexa\Bundle\Core\Features\Context\BasicContentContext: + public: true + arguments: + $repository: '@ibexa.api.repository' + $contentTypeService: '@ibexa.api.service.content_type' + $contentService: '@ibexa.api.service.content' + + Ibexa\Bundle\Core\Features\Context\FieldTypeContext: + public: true + arguments: + $contentTypeService: '@ibexa.api.service.content_type' + $contentService: '@ibexa.api.service.content' + $locationService: '@ibexa.api.service.location' + + Ibexa\Bundle\Core\Features\Context\RoleContext: + public: true + arguments: + $roleService: '@ibexa.api.service.role' + + Ibexa\Bundle\Core\Features\Context\UserContext: + public: true + arguments: + $userService: '@ibexa.api.service.user' + $searchService: '@ibexa.api.service.search' diff --git a/src/bundle/Core/Resources/config/fieldtype_services.yml b/src/bundle/Core/Resources/config/fieldtype_services.yml new file mode 100644 index 0000000000..e99056d71d --- /dev/null +++ b/src/bundle/Core/Resources/config/fieldtype_services.yml @@ -0,0 +1,125 @@ +services: + # Parameter providers + Ibexa\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry: + class: Ibexa\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry + + Ibexa\Core\MVC\Symfony\FieldType\View\ParameterProvider\LocaleParameterProvider: + class: Ibexa\Core\MVC\Symfony\FieldType\View\ParameterProvider\LocaleParameterProvider + arguments: ['@Ibexa\Core\MVC\Symfony\Locale\LocaleConverter'] + calls: + - [setRequestStack, ["@request_stack"]] + tags: + - {name: ibexa.field_type.view.parameter.provider, alias: ezdatetime} + - {name: ibexa.field_type.view.parameter.provider, alias: ezdate} + - {name: ibexa.field_type.view.parameter.provider, alias: eztime} + + Ibexa\Core\MVC\Symfony\FieldType\Relation\ParameterProvider: + class: Ibexa\Core\MVC\Symfony\FieldType\Relation\ParameterProvider + arguments: + - '@ibexa.api.service.content' + tags: + - {name: ibexa.field_type.view.parameter.provider, alias: ezobjectrelation} + + Ibexa\Core\MVC\Symfony\FieldType\RelationList\ParameterProvider: + class: Ibexa\Core\MVC\Symfony\FieldType\RelationList\ParameterProvider + arguments: + - '@ibexa.api.service.content' + tags: + - {name: ibexa.field_type.view.parameter.provider, alias: ezobjectrelationlist} + + ibexa.field_type.ezimageasset.parameterProvider: + class: Ibexa\Core\MVC\Symfony\FieldType\ImageAsset\ParameterProvider + lazy: true + arguments: + - '@ibexa.siteaccessaware.repository' + tags: + - {name: ibexa.field_type.view.parameter.provider, alias: ezimageasset} + + Ibexa\Core\MVC\Symfony\FieldType\User\ParameterProvider: + lazy: true + arguments: + - '@ibexa.api.service.user' + tags: + - {name: ibexa.field_type.view.parameter.provider, alias: ezuser} + + # Image + Ibexa\Core\FieldType\Image\IO\Legacy: + class: Ibexa\Core\FieldType\Image\IO\Legacy + arguments: + - '@ibexa.field_type.ezimage.io_service.published' + - '@ibexa.field_type.ezimage.io_service.draft' + - '@Ibexa\Core\FieldType\Image\IO\OptionsProvider' + # Required by ezpublish.core.io.stream_file_listener. Request listeners are initialized very early. + lazy: true + + Ibexa\Core\FieldType\Image\IO\OptionsProvider: + class: Ibexa\Core\FieldType\Image\IO\OptionsProvider + arguments: + $configResolver: '@ibexa.config.resolver' + + ibexa.field_type.ezimage.io_service.published: + parent: ibexa.core.io.service + + ibexa.field_type.ezimage.io_service.draft: + parent: ibexa.core.io.service + + ibexa.field_type.ezimage.io_service.published.config_scope_change_aware: + class: Ibexa\Core\IO\ConfigScopeChangeAwareIOService + decorates: ibexa.field_type.ezimage.io_service.published + autoconfigure: true + lazy: true + arguments: + $configResolver: '@ibexa.config.resolver' + $innerIOService: '@ibexa.field_type.ezimage.io_service.published.config_scope_change_aware.inner' + $prefixParameterName: 'image.published_images_dir' + + ibexa.field_type.ezimage.io_service.draft.config_scope_change_aware: + class: Ibexa\Core\IO\ConfigScopeChangeAwareIOService + decorates: ibexa.field_type.ezimage.io_service.draft + lazy: true + arguments: + $configResolver: '@ibexa.config.resolver' + $innerIOService: '@ibexa.field_type.ezimage.io_service.draft.config_scope_change_aware.inner' + $prefixParameterName: 'image.versioned_images_dir' + + Ibexa\Core\FieldType\Image\PathGenerator\LegacyPathGenerator: + class: Ibexa\Core\FieldType\Image\PathGenerator\LegacyPathGenerator + + Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator: + class: Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator + arguments: + - '@ibexa.config.resolver' + + Ibexa\Core\FieldType\Validator\ImageValidator: + class: Ibexa\Core\FieldType\Validator\ImageValidator + + # BinaryFile + ibexa.field_type.ezbinaryfile.io_service: + parent: ibexa.core.io.service + + ibexa.field_type.ezbinaryfile.io_service.config_scope_change_aware: + class: Ibexa\Core\IO\ConfigScopeChangeAwareIOService + decorates: ibexa.field_type.ezbinaryfile.io_service + lazy: true + arguments: + $configResolver: '@ibexa.config.resolver' + $innerIOService: '@ibexa.field_type.ezbinaryfile.io_service.config_scope_change_aware.inner' + $prefixParameterName: 'binary_dir' + + Ibexa\Core\FieldType\BinaryBase\PathGenerator\LegacyPathGenerator: + class: Ibexa\Core\FieldType\BinaryBase\PathGenerator\LegacyPathGenerator + + # Will be added to binaryfile & mediafile external storage handlers by a compiler pass + Ibexa\Core\MVC\Symfony\FieldType\BinaryBase\ContentDownloadUrlGenerator: + public: true # @todo should be private + class: Ibexa\Core\MVC\Symfony\FieldType\BinaryBase\ContentDownloadUrlGenerator + arguments: ["@router"] + + # Symfony 3.4+ service definitions: + Ibexa\Core\FieldType\ImageAsset\AssetMapper: + lazy: true + arguments: + $contentService: '@ibexa.api.service.content' + $locationService: '@ibexa.api.service.location' + $contentTypeService: '@ibexa.api.service.content_type' + $configResolver: '@ibexa.config.resolver' diff --git a/src/bundle/Core/Resources/config/helpers.yml b/src/bundle/Core/Resources/config/helpers.yml new file mode 100644 index 0000000000..1ea501e559 --- /dev/null +++ b/src/bundle/Core/Resources/config/helpers.yml @@ -0,0 +1,67 @@ +parameters: + # Helpers + ibexa.config_resolver.resettable_services: [] + ibexa.config_resolver.updateable_services: [] + +services: + # Helpers + Ibexa\Core\Helper\TranslationHelper: + class: Ibexa\Core\Helper\TranslationHelper + arguments: + - '@ibexa.config.resolver' + - '@ibexa.api.service.content' + - '%ibexa.site_access.by_language%' + - "@?logger" + + Ibexa\Core\Helper\FieldHelper: + class: Ibexa\Core\Helper\FieldHelper + arguments: ['@Ibexa\Core\Helper\TranslationHelper', '@ibexa.api.service.content_type', '@ibexa.api.service.field_type'] + + Ibexa\Core\Helper\ContentPreviewHelper: + class: Ibexa\Core\Helper\ContentPreviewHelper + arguments: ["@event_dispatcher", '@Ibexa\Core\MVC\Symfony\SiteAccess\Router'] + calls: + - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']] + + Ibexa\Bundle\Core\EventListener\ConfigScopeListener: + class: Ibexa\Bundle\Core\EventListener\ConfigScopeListener + arguments: + $configResolvers: !tagged ibexa.site.config.resolver + $viewManager: '@Ibexa\Bundle\Core\View\Manager' + tags: + - { name: kernel.event_subscriber } + + Ibexa\Core\Helper\PreviewLocationProvider: + class: Ibexa\Core\Helper\PreviewLocationProvider + arguments: + - '@ibexa.api.service.location' + - '@ibexa.api.service.content' + - '@Ibexa\Core\Persistence\Cache\LocationHandler' + + Ibexa\Core\Helper\ContentInfoLocationLoader\SudoMainLocationLoader: + class: 'Ibexa\Core\Helper\ContentInfoLocationLoader\SudoMainLocationLoader' + arguments: + - '@ibexa.api.repository' + + Ibexa\Bundle\Core\SiteAccess\LanguageResolver: + parent: Ibexa\Core\Repository\SiteAccessAware\Language\AbstractLanguageResolver + arguments: + $configResolver: '@ibexa.config.resolver' + + Ibexa\Contracts\Core\Repository\LanguageResolver: + alias: Ibexa\Bundle\Core\SiteAccess\LanguageResolver + + Ibexa\Core\IO\IOConfigProvider: + alias: Ibexa\Bundle\Core\SiteAccess\Config\IOConfigResolver + + Ibexa\Bundle\Core\SiteAccess\Config\ComplexConfigProcessor: + arguments: + $configResolver: '@ibexa.config.resolver' + $siteAccessService: '@Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService' + + Ibexa\Contracts\Core\SiteAccess\ConfigProcessor: + alias: Ibexa\Bundle\Core\SiteAccess\Config\ComplexConfigProcessor + + Ibexa\Bundle\Core\SiteAccess\Config\IOConfigResolver: + arguments: + $complexConfigProcessor: '@Ibexa\Bundle\Core\SiteAccess\Config\ComplexConfigProcessor' diff --git a/src/bundle/Core/Resources/config/http_kernel.yml b/src/bundle/Core/Resources/config/http_kernel.yml new file mode 100644 index 0000000000..5a8e2975a6 --- /dev/null +++ b/src/bundle/Core/Resources/config/http_kernel.yml @@ -0,0 +1,5 @@ +services: + Ibexa\Bundle\Core\EventListener\RejectExplicitFrontControllerRequestsListener: + class: Ibexa\Bundle\Core\EventListener\RejectExplicitFrontControllerRequestsListener + tags: + - { name: kernel.event_subscriber } diff --git a/src/bundle/Core/Resources/config/image.yml b/src/bundle/Core/Resources/config/image.yml new file mode 100644 index 0000000000..91d4f5966d --- /dev/null +++ b/src/bundle/Core/Resources/config/image.yml @@ -0,0 +1,313 @@ +services: + # Filters + Ibexa\Bundle\Core\Imagine\Filter\UnsupportedFilter: + class: Ibexa\Bundle\Core\Imagine\Filter\UnsupportedFilter + public: false + + Ibexa\Bundle\Core\Imagine\Filter\Imagick\ReduceNoiseFilter: + class: Ibexa\Bundle\Core\Imagine\Filter\Imagick\ReduceNoiseFilter + public: false + + Ibexa\Bundle\Core\Imagine\Filter\Gmagick\ReduceNoiseFilter: + class: Ibexa\Bundle\Core\Imagine\Filter\Gmagick\ReduceNoiseFilter + public: false + + # Aliasing by default to unsupported filter as it's not supported by GD. + # Alias is changed by Imagine compiler pass to use current driver. + ibexa.image_alias.imagine.filter.reduce_noise: + alias: Ibexa\Bundle\Core\Imagine\Filter\UnsupportedFilter + + Ibexa\Bundle\Core\Imagine\Filter\Imagick\SwirlFilter: + class: Ibexa\Bundle\Core\Imagine\Filter\Imagick\SwirlFilter + public: false + + Ibexa\Bundle\Core\Imagine\Filter\Gmagick\SwirlFilter: + class: Ibexa\Bundle\Core\Imagine\Filter\Gmagick\SwirlFilter + public: false + + ibexa.image_alias.imagine.filter.swirl: + alias: Ibexa\Bundle\Core\Imagine\Filter\UnsupportedFilter + + # Filter loaders + Ibexa\Bundle\Core\Imagine\BinaryLoader: + class: Ibexa\Bundle\Core\Imagine\BinaryLoader + arguments: ['@Ibexa\Core\FieldType\Image\IO\Legacy', "@mime_types"] + tags: + - { name: liip_imagine.binary.loader, loader: ibexa } + + ibexa.image_alias.imagine.cache_resolver: + class: Ibexa\Bundle\Core\Imagine\IORepositoryResolver + arguments: + - '@Ibexa\Core\FieldType\Image\IO\Legacy' + - "@router.request_context" + - "@liip_imagine.filter.configuration" + - '@ibexa.image_alias.variation_purger' + - '@ibexa.image_alias.variation_path_generator' + tags: + - { name: liip_imagine.cache.resolver, resolver: ibexa } + + Ibexa\Bundle\Core\Imagine\Cache\ResolverFactory: + class: Ibexa\Bundle\Core\Imagine\Cache\ResolverFactory + arguments: + - '@ibexa.config.resolver' + - '@ibexa.image_alias.imagine.cache_resolver_decorator.inner' + - 'Ibexa\Bundle\Core\Imagine\Cache\Resolver\ProxyResolver' + - 'Ibexa\Bundle\Core\Imagine\Cache\Resolver\RelativeResolver' + + ibexa.image_alias.imagine.cache_resolver_decorator: + class: Liip\ImagineBundle\Imagine\Cache\Resolver\ProxyResolver + factory: ['@Ibexa\Bundle\Core\Imagine\Cache\ResolverFactory', 'createCacheResolver'] + decorates: ibexa.image_alias.imagine.cache_resolver + lazy: true + + Ibexa\Bundle\Core\Imagine\Cache\AliasGeneratorDecorator: + class: Ibexa\Bundle\Core\Imagine\Cache\AliasGeneratorDecorator + lazy: true + arguments: + - '@Ibexa\Bundle\Core\Imagine\Variation\ImagineAwareAliasGenerator' + - '@ibexa.cache_pool' + - '@router.request_context' + - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface' + calls: + - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']] + + ibexa.image_alias.imagine.alias_generator: + class: Ibexa\Bundle\Core\Imagine\AliasGenerator + arguments: + - '@Ibexa\Bundle\Core\Imagine\BinaryLoader' + - "@liip_imagine.filter.manager" + - '@ibexa.image_alias.imagine.cache_resolver' + - "@liip_imagine.filter.configuration" + - "@?logger" + + Ibexa\Bundle\Core\Imagine\AliasCleaner: + class: Ibexa\Bundle\Core\Imagine\AliasCleaner + arguments: ['@ibexa.image_alias.imagine.cache_resolver'] + + Ibexa\Core\FieldType\Image\AliasCleanerInterface: + alias: Ibexa\Bundle\Core\Imagine\AliasCleaner + + Ibexa\Bundle\Core\Imagine\PlaceholderAliasGeneratorConfigurator: + class: 'Ibexa\Bundle\Core\Imagine\PlaceholderAliasGeneratorConfigurator' + arguments: + - '@ibexa.config.resolver' + - '@Ibexa\Bundle\Core\Imagine\PlaceholderProviderRegistry' + - '%ibexa.io.images.alias.placeholder_provider%' + + Ibexa\Bundle\Core\Imagine\PlaceholderAliasGenerator: + class: 'Ibexa\Bundle\Core\Imagine\PlaceholderAliasGenerator' + decorates: 'ibexa.image_alias.imagine.alias_generator' + configurator: ['@Ibexa\Bundle\Core\Imagine\PlaceholderAliasGeneratorConfigurator', 'configure'] + arguments: + - '@Ibexa\Bundle\Core\Imagine\PlaceholderAliasGenerator.inner' + - '@ibexa.image_alias.imagine.cache_resolver' + - '@Ibexa\Core\FieldType\Image\IO\Legacy' + public: false + + Ibexa\Bundle\Core\Imagine\ImageAsset\AliasGenerator: + class: 'Ibexa\Bundle\Core\Imagine\ImageAsset\AliasGenerator' + arguments: + - '@Ibexa\Bundle\Core\Imagine\Cache\AliasGeneratorDecorator' + - '@ibexa.api.service.content' + - '@Ibexa\Core\FieldType\ImageAsset\AssetMapper' + public: false + tags: + - { name: 'ibexa.media.images.variation.handler', identifier: 'alias', priority: -100 } + + Ibexa\Bundle\Core\Imagine\PlaceholderProviderRegistry: + class: 'Ibexa\Bundle\Core\Imagine\PlaceholderProviderRegistry' + + Ibexa\Bundle\Core\Imagine\PlaceholderProvider\GenericProvider: + class: 'Ibexa\Bundle\Core\Imagine\PlaceholderProvider\GenericProvider' + arguments: + - '@liip_imagine' + tags: + - { name: 'ibexa.media.images.placeholder.provider', type: 'generic' } + + Ibexa\Bundle\Core\Imagine\PlaceholderProvider\RemoteProvider: + class: 'Ibexa\Bundle\Core\Imagine\PlaceholderProvider\RemoteProvider' + tags: + - { name: 'ibexa.media.images.placeholder.provider', type: 'remote' } + + ibexa.image_alias.imagine.filter.loader.scaledown.base: + abstract: true + public: false + calls: + - [setInnerLoader, ["@liip_imagine.filter.loader.thumbnail"]] + + Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleDownOnlyFilterLoader: + parent: ibexa.image_alias.imagine.filter.loader.scaledown.base + class: Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleDownOnlyFilterLoader + tags: + - { name: liip_imagine.filter.loader, loader: "geometry/scaledownonly" } + + Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleWidthDownOnlyFilterLoader: + parent: ibexa.image_alias.imagine.filter.loader.scaledown.base + class: Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleWidthDownOnlyFilterLoader + tags: + - { name: liip_imagine.filter.loader, loader: "geometry/scalewidthdownonly" } + + Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleHeightDownOnlyFilterLoader: + parent: ibexa.image_alias.imagine.filter.loader.scaledown.base + class: Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleHeightDownOnlyFilterLoader + tags: + - { name: liip_imagine.filter.loader, loader: "geometry/scaleheightdownonly" } + + ibexa.image_alias.imagine.filter.loader.relative_scale: + abstract: true + public: false + calls: + - [setInnerLoader, ["@liip_imagine.filter.loader.relative_resize"]] + + Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleWidthFilterLoader: + parent: ibexa.image_alias.imagine.filter.loader.relative_scale + class: Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleWidthFilterLoader + tags: + - { name: liip_imagine.filter.loader, loader: "geometry/scalewidth" } + + Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleHeightFilterLoader: + parent: ibexa.image_alias.imagine.filter.loader.relative_scale + class: Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleHeightFilterLoader + tags: + - { name: liip_imagine.filter.loader, loader: "geometry/scaleheight" } + + Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleFilterLoader: + parent: ibexa.image_alias.imagine.filter.loader.relative_scale + class: Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleFilterLoader + tags: + - { name: liip_imagine.filter.loader, loader: "geometry/scale" } + + ibexa.image_alias.imagine.filter.loader.scale_exact.base: + abstract: true + public: false + calls: + - [setInnerLoader, ["@liip_imagine.filter.loader.resize"]] + + Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleExactFilterLoader: + parent: ibexa.image_alias.imagine.filter.loader.scale_exact.base + class: Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleExactFilterLoader + tags: + - { name: liip_imagine.filter.loader, loader: "geometry/scaleexact" } + + Ibexa\Bundle\Core\Imagine\Filter\Loader\ScalePercentFilterLoader: + parent: ibexa.image_alias.imagine.filter.loader.scale_exact.base + class: Ibexa\Bundle\Core\Imagine\Filter\Loader\ScalePercentFilterLoader + tags: + - { name: liip_imagine.filter.loader, loader: "geometry/scalepercent" } + + Ibexa\Bundle\Core\Imagine\Filter\Loader\CropFilterLoader: + class: Ibexa\Bundle\Core\Imagine\Filter\Loader\CropFilterLoader + public: false + calls: + - [setInnerLoader, ["@liip_imagine.filter.loader.crop"]] + tags: + - { name: liip_imagine.filter.loader, loader: "geometry/crop" } + + Ibexa\Bundle\Core\Imagine\Filter\Loader\BorderFilterLoader: + class: Ibexa\Bundle\Core\Imagine\Filter\Loader\BorderFilterLoader + public: false + tags: + - { name: liip_imagine.filter.loader, loader: "border" } + + Ibexa\Bundle\Core\Imagine\Filter\Loader\ReduceNoiseFilterLoader: + class: Ibexa\Bundle\Core\Imagine\Filter\Loader\ReduceNoiseFilterLoader + arguments: ['@ibexa.image_alias.imagine.filter.reduce_noise'] + public: false + tags: + - { name: liip_imagine.filter.loader, loader: "filter/noise" } + + Ibexa\Bundle\Core\Imagine\Filter\Loader\SwirlFilterLoader: + class: Ibexa\Bundle\Core\Imagine\Filter\Loader\SwirlFilterLoader + arguments: ['@ibexa.image_alias.imagine.filter.swirl'] + public: false + tags: + - { name: liip_imagine.filter.loader, loader: "filter/swirl" } + + Ibexa\Bundle\Core\Imagine\Filter\Loader\GrayscaleFilterLoader: + class: Ibexa\Bundle\Core\Imagine\Filter\Loader\GrayscaleFilterLoader + public: false + tags: + - { name: liip_imagine.filter.loader, loader: "colorspace/gray" } + + ibexa.image_alias.variation_purger: + # < platform 2015.05 + # alias: Ibexa\Bundle\Core\Imagine\VariationPurger\ImageFileVariationPurger + # >= platform 2015.05 + alias: Ibexa\Bundle\Core\Imagine\VariationPurger\IOVariationPurger + + Ibexa\Bundle\Core\Imagine\VariationPurger\IOVariationPurger: + class: Ibexa\Bundle\Core\Imagine\VariationPurger\IOVariationPurger + arguments: + - '@Ibexa\Core\FieldType\Image\IO\Legacy' + - '@ibexa.cache_pool' + - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface' + - '@Ibexa\Bundle\Core\Imagine\Cache\AliasGeneratorDecorator' + - '@Ibexa\Bundle\Core\Imagine\VariationPathGenerator\AliasDirectoryVariationPathGenerator' + calls: + - [setLogger, ["@?logger"]] + + Ibexa\Bundle\Core\Imagine\VariationPurger\ImageFileVariationPurger: + class: Ibexa\Bundle\Core\Imagine\VariationPurger\ImageFileVariationPurger + arguments: + - '@Ibexa\Bundle\Core\Imagine\VariationPurger\LegacyStorageImageFileList' + - '@Ibexa\Core\FieldType\Image\IO\Legacy' + - '@Ibexa\Bundle\Core\Imagine\VariationPathGenerator\OriginalDirectoryVariationPathGenerator' + calls: + - [setLogger, ["@?logger"]] + + Ibexa\Bundle\Core\Imagine\VariationPurger\LegacyStorageImageFileList: + class: Ibexa\Bundle\Core\Imagine\VariationPurger\LegacyStorageImageFileList + arguments: + - '@Ibexa\Bundle\Core\Imagine\VariationPurger\LegacyStorageImageFileRowReader' + - '@Ibexa\Core\IO\IOConfigProvider' + - '@ibexa.config.resolver' + + Ibexa\Bundle\Core\Imagine\VariationPurger\LegacyStorageImageFileRowReader: + class: Ibexa\Bundle\Core\Imagine\VariationPurger\LegacyStorageImageFileRowReader + arguments: + $connection: '@ibexa.persistence.connection' + + ibexa.image_alias.variation_path_generator: + # < platform 2015.05 + # alias: Ibexa\Bundle\Core\Imagine\VariationPathGenerator\OriginalDirectoryVariationPathGenerator + # >= platform 2015.05 + alias: Ibexa\Bundle\Core\Imagine\VariationPathGenerator\AliasDirectoryVariationPathGenerator + + Ibexa\Bundle\Core\Imagine\VariationPathGenerator\OriginalDirectoryVariationPathGenerator: + class: Ibexa\Bundle\Core\Imagine\VariationPathGenerator\OriginalDirectoryVariationPathGenerator + + Ibexa\Bundle\Core\Imagine\VariationPathGenerator\AliasDirectoryVariationPathGenerator: + class: Ibexa\Bundle\Core\Imagine\VariationPathGenerator\AliasDirectoryVariationPathGenerator + + # Abstract Image Variations + + Ibexa\Core\Variation\VariationHandlerRegistry: + arguments: + $variationHandlers: !tagged_iterator { tag: 'ibexa.media.images.variation.handler', index_by: 'identifier' } + + Ibexa\Bundle\Core\Variation\VariationHandlerResolver: + autowire: true + autoconfigure: true + + Ibexa\Bundle\Core\Imagine\Variation\ImagineAwareAliasGenerator: + class: Ibexa\Bundle\Core\Imagine\Variation\ImagineAwareAliasGenerator + arguments: + - '@ibexa.image_alias.imagine.alias_generator' + - '@ibexa.image_alias.variation_path_generator' + - '@Ibexa\Core\FieldType\Image\IO\Legacy' + - '@liip_imagine' + tags: + - { name: 'ibexa.media.images.variation.handler', identifier: 'alias', priority: -50 } + + Ibexa\Bundle\Core\Imagine\VariationPathGenerator\WebpFormatVariationPathGenerator: + decorates: ibexa.image_alias.variation_path_generator + arguments: + $innerVariationPathGenerator: '@.inner' + $filterConfiguration: '@liip_imagine.filter.configuration' + + # SPI Aliases + Ibexa\Contracts\Core\Variation\VariationHandler: '@Ibexa\Bundle\Core\Variation\VariationHandlerResolver' + + # Image alias generator + ibexa.field_type.ezimage.variation_service: + alias: Ibexa\Contracts\Core\Variation\VariationHandler diff --git a/src/bundle/Core/Resources/config/locale.yml b/src/bundle/Core/Resources/config/locale.yml new file mode 100644 index 0000000000..e19bcb7fd2 --- /dev/null +++ b/src/bundle/Core/Resources/config/locale.yml @@ -0,0 +1,126 @@ +parameters: + ibexa.locale.conversion_map: + alb-AL: sq_AL + ara-SA: ar_SA + bos-BA: bs_BA + cat-ES: ca_ES + chi-CN: zh_CN + chi-HK: zh_HK + chi-TW: zh_TW + cro-HR: hr_HR + cze-CZ: cs_CZ + dan-DK: da_DK + dut-BE: nl_BE + dut-NL: nl_NL + ell-GR: el_GR + eng-AU: en_AU + eng-CA: en_CA + eng-GB: en_GB + eng-NZ: en_NZ + eng-US: en_US + epo-EO: eo + esl-ES: es_ES + esl-MX: es_MX + fas-IR: fa_IR + fin-FI: fi_FI + fre-BE: fr_BE + fre-CA: fr_CA + fre-CH: fr_CH + fre-FR: fr_FR + ger-BE: de_BE + ger-CH: de_CH + ger-DE: de_DE + heb-IL: he_IL + hin-IN: hi_IN + hun-HU: hu_HU + ind-ID: id_ID + ita-CH: it_CH + ita-IT: it_IT + jpn-JP: ja_JP + kor-KR: ko_KR + mkd-MK: mk_MK + nno-NO: nn_NO + nor-NO: no_NO + pol-PL: pl_PL + por-BR: pt_BR + por-MZ: pt_MZ + por-PT: pt_PT + rus-RU: ru_RU + ser-SR: sr_RS + slk-SK: sk_SK + slo-SI: sl_SI + sqi-AL: sq_AL + srp-RS: sr_RS + swe-SE: sv_SE + tur-TR: tr_TR + ukr-UA: uk_UA + vie-VN: vi_VN + + ibexa.locale.browser_map: + au: ['eng-AU'] + be: ['fre-BE', 'dut-BE', 'ger-BE'] + br: ['por-BR'] + ca: ['eng-CA'] + cn: ['chi-CN'] + cz: ['cze-CZ'] + de: ['ger-DE'] + dk: ['dan-DK'] + en: ['eng-GB', 'eng-US'] + en_us: ['eng-US'] + es: ['esl-ES'] + fa: ['fas-IR'] + fi: ['fin-FI'] + fr: ['fre-FR'] + gb: ['eng-GB'] + gr: ['ell-GR'] + hk: ['chi-HK'] + hr: ['cro-HR'] + hu: ['hun-HU'] + id: ['ind-ID'] + il: ['heb-IL'] + in: ['hin-IN'] + it: ['ita-IT'] + jp: ['jpn-JP'] + kr: ['kor-KR'] + mx: ['esl-MX'] + mz: ['por-MZ'] + nl: ['dut-NL'] + no: ['nor-NO'] + nz: ['eng-NZ'] + pl: ['pol-PL'] + pt: ['por-PT'] + rs: ['srp-RS'] + ru: ['rus-RU'] + sa: ['ara-SA'] + se: ['swe-SE'] + sk: ['slk-SK'] + tr: ['tur-TR'] + tw: ['chi-TW'] + ua: ['ukr-UA'] + +services: + Ibexa\Core\MVC\Symfony\Locale\LocaleConverter: + class: Ibexa\Core\MVC\Symfony\Locale\LocaleConverter + arguments: ['%ibexa.locale.conversion_map%', "@logger"] + public: true + + Ibexa\Core\MVC\Symfony\Locale\LocaleConverterInterface: + alias: Ibexa\Core\MVC\Symfony\Locale\LocaleConverter + + # Overriding the original locale listener to trigger the internal locale conversion correctly. + Ibexa\Bundle\Core\EventListener\LocaleListener: + decorates: locale_listener + arguments: + - '@Ibexa\Bundle\Core\EventListener\LocaleListener.inner' + - '@ibexa.config.resolver' + - '@Ibexa\Core\MVC\Symfony\Locale\LocaleConverter' + tags: + - { name: kernel.event_subscriber } + + Ibexa\Core\MVC\Symfony\Locale\UserLanguagePreferenceProvider: + autowire: true + arguments: + $languageCodesMap: '%ibexa.locale.browser_map%' + $localeFallback: '%locale_fallback%' + + Ibexa\Core\MVC\Symfony\Locale\UserLanguagePreferenceProviderInterface: '@Ibexa\Core\MVC\Symfony\Locale\UserLanguagePreferenceProvider' diff --git a/src/bundle/Core/Resources/config/papi.yml b/src/bundle/Core/Resources/config/papi.yml new file mode 100644 index 0000000000..2669a3f1bd --- /dev/null +++ b/src/bundle/Core/Resources/config/papi.yml @@ -0,0 +1,74 @@ +parameters: + ibexa.kernel.root_dir: "%kernel.project_dir%/vendor/ibexa/core" + + # API + ibexa.kernel.proxy_cache_dir: '%kernel.cache_dir%/repository/proxy' + + # Using legacy storage engine for data compatibility with 4.x + ibexa.api.storage_engine.default: legacy + ibexa.api.search_engine.default: legacy + +services: + # API + Ibexa\Bundle\Core\ApiLoader\RepositoryFactory: + arguments: + - '@ibexa.config.resolver' + - Ibexa\Core\Repository\Repository + - '%ibexa.api.role.policy_map%' + - '@Ibexa\Contracts\Core\Repository\LanguageResolver' + - "@?logger" + calls: + - [setContainer, ["@service_container"]] + + Ibexa\Bundle\Core\ApiLoader\StorageEngineFactory: + class: Ibexa\Bundle\Core\ApiLoader\StorageEngineFactory + arguments: + - '@Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider' + + ibexa.api.persistence_handler: + #To disable cache, switch alias to Ibexa\Contracts\Core\Persistence\Handler + alias: Ibexa\Core\Persistence\Cache\Handler + + Ibexa\Contracts\Core\Persistence\Handler: + class: Ibexa\Contracts\Core\Persistence\Handler + factory: ['@Ibexa\Bundle\Core\ApiLoader\StorageEngineFactory', buildStorageEngine] + public: false + + Ibexa\Bundle\Core\ApiLoader\SearchEngineFactory: + class: Ibexa\Bundle\Core\ApiLoader\SearchEngineFactory + arguments: + - '@Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider' + + Ibexa\Bundle\Core\ApiLoader\SearchEngineIndexerFactory: + class: Ibexa\Bundle\Core\ApiLoader\SearchEngineIndexerFactory + arguments: + - '@Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider' + + ibexa.spi.search: + alias: Ibexa\Contracts\Core\Search\VersatileHandler + + ibexa.spi.search.indexer: + alias: Ibexa\Core\Search\Common\Indexer + + Ibexa\Contracts\Core\Search\VersatileHandler: + class: Ibexa\Contracts\Core\Search\VersatileHandler + factory: ['@Ibexa\Bundle\Core\ApiLoader\SearchEngineFactory', buildSearchEngine] + public: false + lazy: true + + Ibexa\Core\Search\Common\Indexer: + class: Ibexa\Core\Search\Common\Indexer + factory: ['@Ibexa\Bundle\Core\ApiLoader\SearchEngineIndexerFactory', buildSearchEngineIndexer] + public: false + + # Redefine background indexer to the one provided here which works on kernel/console.terminate. + Ibexa\Bundle\Core\EventListener\BackgroundIndexingTerminateListener: + class: Ibexa\Bundle\Core\EventListener\BackgroundIndexingTerminateListener + arguments: + - '@ibexa.api.persistence_handler' + - '@ibexa.spi.search' + tags: + - { name: kernel.event_subscriber } + + Ibexa\Contracts\Core\Search\Content\IndexerGateway: + alias: Ibexa\Core\Search\Legacy\Content\IndexerGateway diff --git a/src/bundle/Core/Resources/config/query_types.yml b/src/bundle/Core/Resources/config/query_types.yml new file mode 100644 index 0000000000..498609e16b --- /dev/null +++ b/src/bundle/Core/Resources/config/query_types.yml @@ -0,0 +1,36 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + Ibexa\Core\QueryType\BuiltIn\AncestorsQueryType: + tags: + - { name: ibexa.query_type, alias: 'Ancestors' } + + Ibexa\Core\QueryType\BuiltIn\ChildrenQueryType: + tags: + - { name: ibexa.query_type, alias: 'Children' } + + Ibexa\Core\QueryType\BuiltIn\SiblingsQueryType: + tags: + - { name: ibexa.query_type, alias: 'Siblings' } + + Ibexa\Core\QueryType\BuiltIn\RelatedToContentQueryType: + tags: + - { name: ibexa.query_type, alias: 'RelatedTo' } + + Ibexa\Core\QueryType\BuiltIn\GeoLocationQueryType: + tags: + - { name: ibexa.query_type, alias: 'GeoLocation' } + + Ibexa\Core\QueryType\BuiltIn\SubtreeQueryType: + tags: + - { name: ibexa.query_type, alias: 'Subtree' } + + Ibexa\Core\QueryType\BuiltIn\SortClausesFactory: + arguments: + $sortClauseArgsParser: '@Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParserDispatcher' + + Ibexa\Core\QueryType\BuiltIn\SortClausesFactoryInterface: + alias: 'Ibexa\Core\QueryType\BuiltIn\SortClausesFactory' diff --git a/src/bundle/Core/Resources/config/routing.yml b/src/bundle/Core/Resources/config/routing.yml new file mode 100644 index 0000000000..b746b92942 --- /dev/null +++ b/src/bundle/Core/Resources/config/routing.yml @@ -0,0 +1,166 @@ +parameters: + ibexa.default_router.non_site_access_aware_routes: ['_assetic_', '_wdt', '_profiler', '_configurator_', 'ibexa.user_hash'] + # characters that may require encoding in the urlalias generator + ibexa.urlalias_generator.charmap: + "\"" : "%22" + "'" : "%27" + "<" : "%3C" + ">" : "%3E" + +services: + Ibexa\Core\MVC\Symfony\Routing\ChainRouter: + class: Ibexa\Core\MVC\Symfony\Routing\ChainRouter + arguments: ["@?logger"] + calls: + - [setContext, ["@router.request_context"]] + + ibexa.siteaccess_match_listener: + class: Ibexa\Core\MVC\Symfony\EventListener\SiteAccessMatchListener + arguments: + $siteAccessRouter: '@Ibexa\Core\MVC\Symfony\SiteAccess\Router' + $eventDispatcher: '@event_dispatcher' + $siteAccessMatcherRegistry: '@Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistryInterface' + tags: + - { name: kernel.event_subscriber } + + Ibexa\Core\MVC\Symfony\Component\Serializer\CompoundMatcherNormalizer: + tags: + - { name: serializer.normalizer } + + Ibexa\Core\MVC\Symfony\Routing\Generator: + class: Ibexa\Core\MVC\Symfony\Routing\Generator + abstract: true + calls: + - [setRequestContext, ["@router.request_context"]] + - [setSiteAccess, ['@?Ibexa\Core\MVC\Symfony\SiteAccess']] + - [setSiteAccessRouter, ['@Ibexa\Core\MVC\Symfony\SiteAccess\Router']] + - [setLogger, ["@?logger"]] + + Ibexa\Bundle\Core\Routing\UrlAliasRouter: + class: Ibexa\Bundle\Core\Routing\UrlAliasRouter + arguments: + - '@ibexa.api.service.location' + - '@ibexa.api.service.url_alias' + - '@ibexa.api.service.content' + - '@Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator' + - "@?router.request_context" + - "@?logger" + calls: + - [setConfigResolver, ['@ibexa.config.resolver']] + tags: + - {name: router, priority: 200} + + Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator: + class: Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator + arguments: + - '@ibexa.api.repository' + - "@router.default" + - '@ibexa.config.resolver' + - '%ibexa.urlalias_generator.charmap%' + parent: Ibexa\Core\MVC\Symfony\Routing\Generator + + Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistry: ~ + Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistryInterface: + alias: 'Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistry' + + Ibexa\Bundle\Core\SiteAccess\MatcherBuilder: + class: Ibexa\Bundle\Core\SiteAccess\MatcherBuilder + arguments: + - '@Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistry' + + Ibexa\Core\MVC\Symfony\SiteAccess\Provider\StaticSiteAccessProvider: + class: Ibexa\Core\MVC\Symfony\SiteAccess\Provider\StaticSiteAccessProvider + arguments: + - '%ibexa.site_access.list%' + - '%ibexa.site_access.groups_by_site_access%' + tags: + - { name: ibexa.site_access.provider, priority: 10 } + + Ibexa\Core\MVC\Symfony\SiteAccess\Provider\ChainSiteAccessProvider: + class: Ibexa\Core\MVC\Symfony\SiteAccess\Provider\ChainSiteAccessProvider + arguments: + $providers: !tagged ibexa.site_access.provider + + ibexa.siteaccess.provider: + alias: Ibexa\Core\MVC\Symfony\SiteAccess\Provider\ChainSiteAccessProvider + + Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService: + class: Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService + arguments: + - '@ibexa.siteaccess.provider' + - '@ibexa.config.resolver' + calls: + - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']] + + Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface: + alias: 'Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService' + + Ibexa\Core\MVC\Symfony\SiteAccess\Router: + class: Ibexa\Core\MVC\Symfony\SiteAccess\Router + arguments: + - '@Ibexa\Bundle\Core\SiteAccess\MatcherBuilder' + - "@logger" + - '%ibexa.site_access.default%' + - '%ibexa.site_access.match_config%' + - '@ibexa.siteaccess.provider' + - 'Ibexa\Core\MVC\Symfony\SiteAccess' + - "%kernel.debug%" + + Ibexa\Bundle\Core\EventListener\SiteAccessListener: + class: Ibexa\Bundle\Core\EventListener\SiteAccessListener + arguments: + - '@Ibexa\Core\MVC\Symfony\SiteAccess' + tags: + - { name: kernel.event_subscriber } + + Ibexa\Bundle\Core\EventListener\RoutingListener: + class: Ibexa\Bundle\Core\EventListener\RoutingListener + arguments: ['@ibexa.config.resolver', '@Ibexa\Bundle\Core\Routing\UrlAliasRouter', '@Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator'] + tags: + - { name: kernel.event_subscriber } + + Ibexa\Bundle\Core\EventListener\RequestEventListener: + class: Ibexa\Bundle\Core\EventListener\RequestEventListener + arguments: + - '@ibexa.config.resolver' + - "@router" + - '%ibexa.site_access.default%' + - "@?logger" + tags: + - { name: kernel.event_subscriber } + + Ibexa\Bundle\Core\EventListener\IndexRequestListener: + class: Ibexa\Bundle\Core\EventListener\IndexRequestListener + arguments: + - '@ibexa.config.resolver' + tags: + - { name: kernel.event_subscriber } + + Ibexa\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator: + class: Ibexa\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator + arguments: ["@event_dispatcher"] + calls: + - [setRequestStack, ["@request_stack"]] + + Ibexa\Core\MVC\Symfony\EventListener\LanguageSwitchListener: + class: Ibexa\Core\MVC\Symfony\EventListener\LanguageSwitchListener + arguments: ['@Ibexa\Core\Helper\TranslationHelper'] + tags: + - { name: kernel.event_subscriber } + + Ibexa\Bundle\Core\EventListener\OriginalRequestListener: + class: Ibexa\Bundle\Core\EventListener\OriginalRequestListener + tags: + - { name: kernel.event_subscriber } + + Ibexa\Bundle\Core\EventListener\PreviewRequestListener: + class: Ibexa\Bundle\Core\EventListener\PreviewRequestListener + arguments: ["@request_stack"] + tags: + - { name: kernel.event_subscriber } + + Ibexa\Bundle\Core\EventListener\ContentDownloadRouteReferenceListener: + class: Ibexa\Bundle\Core\EventListener\ContentDownloadRouteReferenceListener + tags: + - { name: kernel.event_subscriber } + arguments: ['@Ibexa\Core\Helper\TranslationHelper'] diff --git a/src/bundle/Core/Resources/config/routing/internal.yml b/src/bundle/Core/Resources/config/routing/internal.yml new file mode 100644 index 0000000000..9cec7a542d --- /dev/null +++ b/src/bundle/Core/Resources/config/routing/internal.yml @@ -0,0 +1,45 @@ +ibexa.content.translation.view: + path: /view/content/{contentId}/{viewType}/{layout}/translation/{languageCode}/{locationId} + defaults: + _controller: ibexa_content::viewAction + viewType: full + locationId: null + layout: true + options: + expose: true + +ibexa.content.view: + path: /view/content/{contentId}/{viewType}/{layout}/{locationId} + defaults: + _controller: ibexa_content::viewAction + viewType: full + locationId: null + layout: true + options: + expose: true + +ibexa.version.preview: + path: /content/versionview/{contentId}/{versionNo}/{language}/site_access/{siteAccessName} + controller: ibexa.controller.content.preview:previewContentAction + defaults: + siteAccessName: null + methods: [GET] + +ibexa.content.preview.default: + path: /content/versionview/{contentId}/{versionNo}/{language} + defaults: { _controller: ibexa.controller.content.preview:previewContentAction } + methods: [GET] + +ibexa.user_hash: + path: /_fos_user_context_hash + +ibexa.content.download: + path: /content/download/{contentId}/{fieldIdentifier}/{filename} + defaults: { _controller: Ibexa\Core\MVC\Symfony\Controller\Content\DownloadController:downloadBinaryFileAction } + +ibexa.content.download.field_id: + path: /content/download/{contentId}/{fieldId} + defaults: { _controller: Ibexa\Core\MVC\Symfony\Controller\Content\DownloadController:downloadBinaryFileByIdAction } + requirements: + contentId: '\d+' + fieldId: '\d+' diff --git a/src/bundle/Core/Resources/config/routing/js_routing.yml b/src/bundle/Core/Resources/config/routing/js_routing.yml new file mode 100644 index 0000000000..4e88dc366e --- /dev/null +++ b/src/bundle/Core/Resources/config/routing/js_routing.yml @@ -0,0 +1,7 @@ +services: + Ibexa\Bundle\Core\Routing\JsRouting\ExposedRoutesExtractor: + class: Ibexa\Bundle\Core\Routing\JsRouting\ExposedRoutesExtractor + decorates: 'fos_js_routing.extractor' + arguments: + - '@Ibexa\Bundle\Core\Routing\JsRouting\ExposedRoutesExtractor.inner' + - '@request_stack' diff --git a/src/bundle/Core/Resources/config/security.yml b/src/bundle/Core/Resources/config/security.yml new file mode 100644 index 0000000000..3d96abf9cb --- /dev/null +++ b/src/bundle/Core/Resources/config/security.yml @@ -0,0 +1,58 @@ +parameters: + # Constant authentication execution time in seconds (float). Blocks timing attacks. + # Must be larger than expected real execution time, with a good margin. + # If set to zero, constant time authentication is disabled. Do not do this on production environments. + ibexa.security.authentication.constant_auth_time: !php/const Ibexa\Bundle\Core\DependencyInjection\Compiler\SecurityPass::CONSTANT_AUTH_TIME_DEFAULT + +services: + Ibexa\Core\MVC\Symfony\Security\User\UsernameProvider: + class: Ibexa\Core\MVC\Symfony\Security\User\UsernameProvider + arguments: + - '@Ibexa\Contracts\Core\Repository\UserService' + - '@Ibexa\Contracts\Core\Repository\PermissionResolver' + + Ibexa\Core\MVC\Symfony\Security\User\EmailProvider: + class: Ibexa\Core\MVC\Symfony\Security\User\EmailProvider + arguments: + - '@Ibexa\Contracts\Core\Repository\UserService' + - '@Ibexa\Contracts\Core\Repository\PermissionResolver' + + Ibexa\Core\MVC\Symfony\Security\UserChecker: + arguments: + - '@Ibexa\Contracts\Core\Repository\UserService' + + Ibexa\Core\MVC\Symfony\Security\Authorization\Voter\CoreVoter: + class: Ibexa\Core\MVC\Symfony\Security\Authorization\Voter\CoreVoter + arguments: ['@Ibexa\Contracts\Core\Repository\PermissionResolver'] + public: false + tags: + - { name: security.voter } + + Ibexa\Core\MVC\Symfony\Security\Authorization\Voter\ValueObjectVoter: + class: Ibexa\Core\MVC\Symfony\Security\Authorization\Voter\ValueObjectVoter + arguments: ['@Ibexa\Contracts\Core\Repository\PermissionResolver'] + public: false + tags: + - { name: security.voter } + + Ibexa\Core\MVC\Symfony\Controller\SecurityController: + public: true + class: Ibexa\Core\MVC\Symfony\Controller\SecurityController + arguments: ["@twig", '@ibexa.config.resolver', "@security.authentication_utils"] + + Ibexa\Core\MVC\Symfony\Security\EventListener\SecurityListener: + class: Ibexa\Core\MVC\Symfony\Security\EventListener\SecurityListener + arguments: + - '@Ibexa\Contracts\Core\Repository\PermissionResolver' + - '@Ibexa\Contracts\Core\Repository\UserService' + - '@ibexa.config.resolver' + - "@event_dispatcher" + - "@security.token_storage" + - "@security.authorization_checker" + - "%fragment.path%" + tags: + - { name: kernel.event_subscriber } + + ibexa.security.user_provider: '@Ibexa\Core\MVC\Symfony\Security\User\UsernameProvider' + ibexa.security.user_provider.username: '@Ibexa\Core\MVC\Symfony\Security\User\UsernameProvider' + ibexa.security.user_provider.email: '@Ibexa\Core\MVC\Symfony\Security\User\EmailProvider' diff --git a/src/bundle/Core/Resources/config/services.yml b/src/bundle/Core/Resources/config/services.yml new file mode 100644 index 0000000000..0af04b30a3 --- /dev/null +++ b/src/bundle/Core/Resources/config/services.yml @@ -0,0 +1,372 @@ +imports: + - { resource: commands.yml } + +parameters: + ibexa.site_access.default.name: default + ibexa.config.default_scope: ibexa.site_access.config + + # Param converters + ibexa.param_converter.content.priority: -2 + ibexa.param_converter.location.priority: -2 + +services: + # Siteaccess is injected in the container at runtime + Ibexa\Core\MVC\Symfony\SiteAccess: + class: Ibexa\Core\MVC\Symfony\SiteAccess + arguments: ['%ibexa.site_access.default.name%', 'uninitialized'] + + Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver\DefaultScopeConfigResolver: + arguments: + - '%ibexa.config.default_scope%' + calls: + - [setContainer, ['@service_container']] + lazy: true + tags: + - { name: ibexa.site.config.resolver, priority: 0 } + + Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver\SiteAccessGroupConfigResolver: + arguments: + - '@ibexa.siteaccess.provider' + - '%ibexa.config.default_scope%' + - '%ibexa.site_access.groups%' + calls: + - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']] + - [setContainer, ['@service_container']] + lazy: true + tags: + - { name: ibexa.site.config.resolver, priority: 50 } + + Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver\StaticSiteAccessConfigResolver: + arguments: + - '@ibexa.siteaccess.provider' + - '%ibexa.config.default_scope%' + calls: + - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']] + - [setContainer, ['@service_container']] + lazy: true + tags: + - { name: ibexa.site.config.resolver, priority: 100 } + + Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver\GlobalScopeConfigResolver: + arguments: + - '%ibexa.config.default_scope%' + calls: + - [setContainer, ['@service_container']] + lazy: true + tags: + - { name: ibexa.site.config.resolver, priority: 255 } + + Ibexa\Bundle\Core\DependencyInjection\Configuration\ChainConfigResolver: + public: true # @todo should be private + + ibexa.config.resolver: + public: true # @todo should be private + alias: Ibexa\Bundle\Core\DependencyInjection\Configuration\ChainConfigResolver + + Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\DynamicSettingParser: + class: Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\DynamicSettingParser + + Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings\ComplexSettingValueResolver: + class: Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings\ComplexSettingValueResolver + + Ibexa\Bundle\Core\EventListener\ConsoleCommandListener: + class: Ibexa\Bundle\Core\EventListener\ConsoleCommandListener + arguments: + - '%ibexa.site_access.default%' + - '@ibexa.siteaccess.provider' + - "@event_dispatcher" + - "%kernel.debug%" + calls: + - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']] + tags: + - { name: kernel.event_subscriber } + + Ibexa\Bundle\Core\EventListener\BackwardCompatibleCommandListener: + tags: + - { name: kernel.event_subscriber } + + Ibexa\Core\MVC\Symfony\Controller\Controller: + class: Ibexa\Core\MVC\Symfony\Controller\Controller + abstract: true + calls: + - [ setContainer, ["@service_container"] ] + + Ibexa\Core\MVC\Symfony\Controller\Content\ViewController: + class: Ibexa\Core\MVC\Symfony\Controller\Content\ViewController + arguments: + - '@Ibexa\Bundle\Core\View\Manager' + - "@security.authorization_checker" + parent: Ibexa\Core\MVC\Symfony\Controller\Controller + tags: + - { name: controller.service_arguments } + + Ibexa\Core\MVC\Symfony\Controller\Content\PreviewController: + class: Ibexa\Core\MVC\Symfony\Controller\Content\PreviewController + arguments: + $contentService: '@ibexa.api.service.content' + $locationService: '@ibexa.api.service.location' + $kernel: "@http_kernel" + $previewHelper: '@Ibexa\Core\Helper\ContentPreviewHelper' + $authorizationChecker: "@security.authorization_checker" + $locationProvider: '@Ibexa\Core\Helper\PreviewLocationProvider' + $controllerChecker: '@Ibexa\Core\MVC\Symfony\View\CustomLocationControllerChecker' + tags: + - { name: controller.service_arguments } + + ibexa.controller.content.preview: + alias: Ibexa\Core\MVC\Symfony\Controller\Content\PreviewController + public: true + + Ibexa\Core\MVC\Symfony\Controller\Content\DownloadController: + class: Ibexa\Core\MVC\Symfony\Controller\Content\DownloadController + arguments: + - '@ibexa.api.service.content' + - '@ibexa.field_type.ezbinaryfile.io_service' + - '@Ibexa\Core\Helper\TranslationHelper' + - "@router" + - '@Ibexa\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator' + parent: Ibexa\Core\MVC\Symfony\Controller\Controller + tags: + - { name: controller.service_arguments } + + Ibexa\Core\MVC\Symfony\Controller\Content\DownloadRedirectionController: + class: Ibexa\Core\MVC\Symfony\Controller\Content\DownloadRedirectionController + arguments: + - '@ibexa.api.service.content' + - "@router" + - '@Ibexa\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator' + parent: Ibexa\Core\MVC\Symfony\Controller\Controller + tags: + - { name: controller.service_arguments } + + # This alias allows easier management for subrequests + # {{ render( controller( "ibexa_content::viewAction", {"contentId": 12, "locationId": 123, "viewType": "line"} ) ) } + ibexa_content: + public: true + alias: Ibexa\Core\MVC\Symfony\Controller\Content\ViewController + + Ibexa\Bundle\Core\EventListener\ViewControllerListener: + class: Ibexa\Bundle\Core\EventListener\ViewControllerListener + arguments: + - "@controller_resolver" + - '@Ibexa\Core\MVC\Symfony\View\Builder\Registry\ControllerMatch' + - "@event_dispatcher" + - "@logger" + tags: + - { name: kernel.event_subscriber } + + Ibexa\Bundle\Core\Fragment\FragmentListenerFactory: + class: Ibexa\Bundle\Core\Fragment\FragmentListenerFactory + arguments: ["%fragment.path%"] + calls: + - [setRequestStack, ["@request_stack"]] + + Ibexa\Bundle\Core\Fragment\DecoratedFragmentRenderer: + class: Ibexa\Bundle\Core\Fragment\DecoratedFragmentRenderer + # Arguments replaced at compile time + arguments: [] + calls: + - [setFragmentPath, ["%fragment.path%"]] + - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']] + abstract: true + + Ibexa\Bundle\Core\Fragment\DirectFragmentRenderer: + arguments: + $kernel: '@kernel' + $controllerListener: '@Ibexa\Bundle\Core\EventListener\ViewControllerListener' + $controllerResolver: '@controller_resolver' + $argumentMetadataFactory: '@argument_metadata_factory' + $argumentValueResolver: '@argument_resolver.request_attribute' + $viewTemplateRenderer: '@Ibexa\Core\MVC\Symfony\View\Renderer\TemplateRenderer' + tags: + - { name: kernel.fragment_renderer, alias: !php/const Ibexa\Bundle\Core\Fragment\DirectFragmentRenderer::NAME } + + Ibexa\Bundle\Core\Converter\ContentParamConverter: + class: Ibexa\Bundle\Core\Converter\ContentParamConverter + arguments: + - '@ibexa.siteaccessaware.service.content' + tags: + - { name: request.param_converter, priority: '%ibexa.param_converter.content.priority%', converter: ez_content_converter } + + Ibexa\Bundle\Core\Converter\LocationParamConverter: + class: Ibexa\Bundle\Core\Converter\LocationParamConverter + arguments: + $locationService: '@ibexa.siteaccessaware.service.location' + $contentPreviewHelper: '@Ibexa\Core\Helper\ContentPreviewHelper' + tags: + - { name: request.param_converter, priority: '%ibexa.param_converter.location.priority%', converter: ez_location_converter } + + Ibexa\Bundle\Core\ControllerArgumentResolver\LocationArgumentResolver: + autowire: true + autoconfigure: true + tags: + - { name: controller.argument_value_resolver, priority: 50 } + + Ibexa\Bundle\Core\EventListener\ExceptionListener: + class: Ibexa\Bundle\Core\EventListener\ExceptionListener + arguments: ["@translator"] + tags: + - { name: kernel.event_subscriber } + + Ibexa\Core\QueryType\ArrayQueryTypeRegistry: + class: Ibexa\Core\QueryType\ArrayQueryTypeRegistry + + Ibexa\Core\Query\QueryFactory: + arguments: + $queryTypeRegistry: '@Ibexa\Core\QueryType\ArrayQueryTypeRegistry' + + Ibexa\Core\Query\QueryFactoryInterface: + alias: Ibexa\Core\Query\QueryFactory + + Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactory: + arguments: + $searchService: '@ibexa.api.service.search' + + Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface: + alias: Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactory + + Ibexa\Core\Helper\FieldsGroups\ArrayTranslatorFieldsGroupsList: + lazy: Ibexa\Core\Helper\FieldsGroups\FieldsGroupsList + factory: ['@Ibexa\Core\Helper\FieldsGroups\RepositoryConfigFieldsGroupsListFactory', "build"] + arguments: + - "@translator" + + Ibexa\Core\Helper\FieldsGroups\RepositoryConfigFieldsGroupsListFactory: + class: Ibexa\Core\Helper\FieldsGroups\RepositoryConfigFieldsGroupsListFactory + arguments: + - '@Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider' + + Ibexa\Core\QueryType\QueryParameterContentViewQueryTypeMapper: + class: Ibexa\Core\QueryType\QueryParameterContentViewQueryTypeMapper + arguments: + - '@Ibexa\Core\QueryType\ArrayQueryTypeRegistry' + + Ibexa\Core\MVC\Symfony\Controller\Content\QueryController: + class: Ibexa\Core\MVC\Symfony\Controller\Content\QueryController + arguments: + - '@Ibexa\Core\QueryType\QueryParameterContentViewQueryTypeMapper' + - '@ibexa.api.service.search' + tags: + - { name: controller.service_arguments } + + Ibexa\Core\MVC\Symfony\Controller\QueryRenderController: + arguments: + $queryFactory: '@Ibexa\Core\Query\QueryFactoryInterface' + $searchHitAdapterFactory: '@Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface' + tags: + - { name: controller.service_arguments } + + ibexa_query: + alias: Ibexa\Core\MVC\Symfony\Controller\Content\QueryController + public: true + + ibexa_query_render: + alias: 'Ibexa\Core\MVC\Symfony\Controller\QueryRenderController' + public: true + + Ibexa\Core\MVC\Symfony\Translation\CatalogueMapperFileWriter: + class: Ibexa\Core\MVC\Symfony\Translation\CatalogueMapperFileWriter + decorates: jms_translation.file_writer + arguments: + - '@Ibexa\Core\MVC\Symfony\Translation\CatalogueMapperFileWriter.inner' + - "@jms_translation.loader_manager" + public: false + + Ibexa\Core\MVC\Symfony\Translation\FieldTypesTranslationExtractor: + class: Ibexa\Core\MVC\Symfony\Translation\FieldTypesTranslationExtractor + arguments: + - '@Ibexa\Core\FieldType\FieldTypeRegistry' + tags: + - { name: jms_translation.extractor, alias: ez_fieldtypes } + + Ibexa\Core\MVC\Symfony\Translation\ExceptionMessageTemplateFileVisitor: + parent: jms_translation.extractor.file.default_php_extractor + class: Ibexa\Core\MVC\Symfony\Translation\ExceptionMessageTemplateFileVisitor + tags: + - { name: jms_translation.file_visitor, alias: ez_exception_message_template } + + Ibexa\Core\MVC\Symfony\Translation\TranslatableExceptionsFileVisitor: + parent: jms_translation.extractor.file.default_php_extractor + class: Ibexa\Core\MVC\Symfony\Translation\TranslatableExceptionsFileVisitor + tags: + - { name: jms_translation.file_visitor, alias: ez_translatable_exception } + + Ibexa\Core\MVC\Symfony\Translation\ValidationErrorFileVisitor: + parent: jms_translation.extractor.file.default_php_extractor + class: Ibexa\Core\MVC\Symfony\Translation\ValidationErrorFileVisitor + tags: + - { name: jms_translation.file_visitor, alias: ez_validation_error } + + Ibexa\Bundle\Core\EventSubscriber\CrowdinRequestLocaleSubscriber: + class: Ibexa\Bundle\Core\EventSubscriber\CrowdinRequestLocaleSubscriber + tags: + - {name: kernel.event_subscriber} + + Ibexa\Bundle\Core\EventSubscriber\TrustedHeaderClientIpEventSubscriber: + arguments: + $trustedHeaderName: '%ibexa.trusted_header_client_ip_name%' + tags: + - {name: kernel.event_subscriber} + + Ibexa\Bundle\Core\Command\DeleteContentTranslationCommand: + class: Ibexa\Bundle\Core\Command\DeleteContentTranslationCommand + arguments: + - '@ibexa.api.repository' + tags: + - { name: console.command } + + Ibexa\Bundle\Core\Command\CleanupVersionsCommand: + class: Ibexa\Bundle\Core\Command\CleanupVersionsCommand + arguments: + - '@Ibexa\Core\Event\Repository' + - '@Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider' + - '@ibexa.persistence.connection' + tags: + - { name: console.command } + + Ibexa\Bundle\Core\Session\Handler\NativeSessionHandler: + class: Ibexa\Bundle\Core\Session\Handler\NativeSessionHandler + arguments: + - '%session.save_path%' + - 'redis' + + Ibexa\Bundle\Core\Command\CopySubtreeCommand: + class: Ibexa\Bundle\Core\Command\CopySubtreeCommand + autowire: true + autoconfigure: true + arguments: + $locationService: '@ibexa.api.service.location' + $permissionResolver: '@Ibexa\Contracts\Core\Repository\PermissionResolver' + $userService: '@ibexa.api.service.user' + $contentTypeService: '@ibexa.api.service.content_type' + $searchService: '@ibexa.api.service.search' + tags: + - { name: console.command } + + Ibexa\Bundle\Core\Command\ResizeOriginalImagesCommand: + class: Ibexa\Bundle\Core\Command\ResizeOriginalImagesCommand + autowire: true + autoconfigure: true + arguments: + $ioService: '@ibexa.field_type.ezimage.io_service.published' + $imagine: '@liip_imagine' + $filterManager: '@liip_imagine.filter.manager' + $permissionResolver: '@Ibexa\Contracts\Core\Repository\PermissionResolver' + $userService: '@ibexa.api.service.user' + $mimeTypes: '@mime_types' + tags: + - { name: console.command } + + ibexa.doctrine.orm.entity_manager: + class: Doctrine\ORM\EntityManager + lazy: true + factory: ['@ibexa.doctrine.orm.entity_manager_factory', 'getEntityManager'] + + ibexa.doctrine.orm.entity_manager_factory: + class: Ibexa\Bundle\Core\Entity\EntityManagerFactory + arguments: + $repositoryConfigurationProvider: '@Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider' + $defaultConnection: '%doctrine.default_connection%' + $entityManagers: '%doctrine.entity_managers%' + + Ibexa\Bundle\Core\Translation\Policy\PolicyTranslationDefinitionProvider: ~ diff --git a/src/bundle/Core/Resources/config/session.yml b/src/bundle/Core/Resources/config/session.yml new file mode 100644 index 0000000000..81b28b1bae --- /dev/null +++ b/src/bundle/Core/Resources/config/session.yml @@ -0,0 +1,20 @@ +parameters: + ibexa.session.attribute_bag.storage_key: "_ezpublish" + +services: + Ibexa\Bundle\Core\EventListener\SessionSetDynamicNameListener: + class: Ibexa\Bundle\Core\EventListener\SessionSetDynamicNameListener + arguments: ['@ibexa.config.resolver', "@session.storage.factory"] + tags: + - { name: kernel.event_subscriber } + + Ibexa\Bundle\Core\EventListener\SessionInitByPostListener: + class: Ibexa\Bundle\Core\EventListener\SessionInitByPostListener + tags: + - { name: kernel.event_subscriber } + + # Override the session attribute bag to set custom storage key so same is used as legacy + # @deprecated To be removed in 7.0 kernel, see 594b083d94f1cff7008fb0d7d54f40d0ce0a2ace + session.attribute_bag: + class: Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag + arguments: ['%ibexa.session.attribute_bag.storage_key%'] diff --git a/src/bundle/Core/Resources/config/sort_spec.yml b/src/bundle/Core/Resources/config/sort_spec.yml new file mode 100644 index 0000000000..526c33d8e7 --- /dev/null +++ b/src/bundle/Core/Resources/config/sort_spec.yml @@ -0,0 +1,45 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParserDispatcher: + arguments: + $parsers: !tagged_iterator ibexa.query_type.sort_clause.parser + + Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\CustomFieldSortClauseParser: + tags: + - { name: ibexa.query_type.sort_clause.parser } + + Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\FieldSortClauseParser: + tags: + - { name: ibexa.query_type.sort_clause.parser } + + Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\MapDistanceSortClauseParser: + tags: + - { name: ibexa.query_type.sort_clause.parser } + + Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\RandomSortClauseParser: + tags: + - { name: ibexa.query_type.sort_clause.parser } + + Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\DefaultSortClauseParser: + arguments: + $valueObjectClassMap: + content_id: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\ContentId + content_name: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\ContentName + content_translated_name: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\ContentTranslatedName + date_modified: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\DateModified + date_published: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\DatePublished + section_identifier: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\SectionIdentifier + section_name: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\SectionName + score: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Score + location_depth: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Location\Depth + location_id: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Location\Id + location_is_main: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Location\IsMainLocation + location_path: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Location\Path + location_priority: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Location\Priority + location_visibility: \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Location\Visibility + tags: + - { name: ibexa.query_type.sort_clause.parser } diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/config/storage/legacy/schema.yaml b/src/bundle/Core/Resources/config/storage/legacy/schema.yaml similarity index 96% rename from eZ/Bundle/EzPublishCoreBundle/Resources/config/storage/legacy/schema.yaml rename to src/bundle/Core/Resources/config/storage/legacy/schema.yaml index c720ddc612..7b44491b5e 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/config/storage/legacy/schema.yaml +++ b/src/bundle/Core/Resources/config/storage/legacy/schema.yaml @@ -141,6 +141,7 @@ tables: ezcontentclass_attribute: indexes: ezcontentclass_attr_ccid: { fields: [contentclass_id] } + ezcontentclass_attr_dts: { fields: [data_type_string] } id: id: { type: integer, nullable: false, options: { autoincrement: true } } version: { type: integer, nullable: false, options: { default: '0' } } @@ -209,6 +210,7 @@ tables: modified: { type: integer, nullable: false, options: { default: '0' } } modifier_id: { type: integer, nullable: false, options: { default: '0' } } name: { type: string, nullable: true, length: 255 } + is_system: { type: boolean, nullable: false, options: { default: '0' } } ezcontentobject: indexes: ezcontentobject_classid: { fields: [contentclass_id] } @@ -262,7 +264,7 @@ tables: indexes: ezco_link_to_co_id: { fields: [to_contentobject_id] } ezco_link_from: { fields: [from_contentobject_id, from_contentobject_version, contentclassattribute_id] } - ezco_link_cca_id: { fields: [contentclassattribute_id] } + ezco_link_cca_id: { fields: [ contentclassattribute_id ] } id: id: { type: integer, nullable: false, options: { autoincrement: true } } fields: @@ -640,3 +642,29 @@ tables: group: { type: string, nullable: false, length: 128 } identifier: { type: string, nullable: false, length: 128 } value: { type: json, nullable: false, length: 0 } + ibexa_token_type: + uniqueConstraints: + ibexa_token_type_unique: { fields: [identifier] } + id: + id: { type: integer, nullable: false, options: { autoincrement: true } } + fields: + identifier: { type: string, nullable: false, length: 64 } + ibexa_token: + uniqueConstraints: + ibexa_token_unique: { fields: [token, identifier, type_id] } + foreignKeys: + ibexa_token_type_id_fk: + fields: [type_id] + foreignTable: ibexa_token_type + foreignFields: [id] + options: + onDelete: CASCADE + id: + id: { type: integer, nullable: false, options: { autoincrement: true } } + fields: + type_id: { type: integer, nullable: false } + token: { type: string, nullable: false, length: 255 } + identifier: { type: string, nullable: true, length: 128 } + created: { type: integer, nullable: false, options: { default: '0' } } + expires: { type: integer, nullable: false, options: { default: '0' } } + revoked: { type: boolean, nullable: false, options: { default: '0' } } diff --git a/src/bundle/Core/Resources/config/storage_engines.yml b/src/bundle/Core/Resources/config/storage_engines.yml new file mode 100644 index 0000000000..ad6c90bd16 --- /dev/null +++ b/src/bundle/Core/Resources/config/storage_engines.yml @@ -0,0 +1,24 @@ +services: + Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider: + public: true # @todo should be private + class: Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider + arguments: + - '@ibexa.config.resolver' + - '%ibexa.repositories%' + + Ibexa\Bundle\Core\ApiLoader\StorageConnectionFactory: + class: Ibexa\Bundle\Core\ApiLoader\StorageConnectionFactory + arguments: + - '@Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider' + calls: + - [setContainer, ["@service_container"]] + + ibexa.persistence.connection: + public: true # @todo should be private + class: Doctrine\DBAL\Connection + factory: ['@Ibexa\Bundle\Core\ApiLoader\StorageConnectionFactory', getConnection] + lazy: true + + # Legacy storage engine + ibexa.api.storage_engine.legacy: + alias: Ibexa\Core\Persistence\Legacy\Handler diff --git a/src/bundle/Core/Resources/config/templating.yml b/src/bundle/Core/Resources/config/templating.yml new file mode 100644 index 0000000000..ad7839456e --- /dev/null +++ b/src/bundle/Core/Resources/config/templating.yml @@ -0,0 +1,324 @@ +parameters: + # @todo drop once core dependencies stop relying on those parameters + + ibexa.content_view.viewbase_layout: "@@IbexaCore/viewbase_layout.html.twig" + ibexa.content_view.content_block_name: "content" + + ibexa.twig.extension.filesize.suffixes: ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB'] + +services: + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\ContentExtension: + class: Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\ContentExtension + arguments: + - '@ibexa.api.repository' + - '@Ibexa\Core\Helper\TranslationHelper' + - '@Ibexa\Core\Helper\FieldHelper' + - '@Ibexa\Core\Helper\FieldsGroups\FieldsGroupsList' + - "@?logger" + tags: + - {name: twig.extension} + + Ibexa\Bundle\Core\View\Manager: + class: Ibexa\Bundle\Core\View\Manager + arguments: + - "@twig" + - "@event_dispatcher" + - '@ibexa.siteaccessaware.repository' + - '@ibexa.config.resolver' + - '%ibexa.content_view.viewbase_layout%' + - '@Ibexa\Core\MVC\Symfony\View\Configurator\ViewProvider' + - "@?logger" + + Ibexa\Contracts\Core\MVC\View\ViewMatcherRegistryInterface: + alias: Ibexa\Bundle\Core\Matcher\ViewMatcherRegistry + + Ibexa\Bundle\Core\Matcher\ViewMatcherRegistry: + arguments: + $matchers: !tagged_iterator { tag: 'ibexa.view.matcher', index_by: identifier } + + ibexa.content_view_provider.configured: + class: Ibexa\Bundle\Core\View\Provider\Configured + arguments: ['@ibexa.content_view.matcher_factory'] + tags: + - {name: ibexa.view.provider, type: 'Ibexa\Core\MVC\Symfony\View\ContentView', priority: 10} + + ibexa.content_view.matcher_factory: + class: Ibexa\Bundle\Core\Matcher\ServiceAwareMatcherFactory + arguments: + - '@Ibexa\Contracts\Core\MVC\View\ViewMatcherRegistryInterface' + - '@ibexa.api.repository' + - 'Ibexa\Core\MVC\Symfony\Matcher\ContentBased' + + ibexa.content_view.matcher_factory.dynamically_configured: + class: Ibexa\Core\MVC\Symfony\Matcher\DynamicallyConfiguredMatcherFactoryDecorator + decorates: ibexa.content_view.matcher_factory + arguments: + $innerConfigurableMatcherFactory: '@ibexa.content_view.matcher_factory.dynamically_configured.inner' + $configResolver: '@ibexa.config.resolver' + $parameterName: content_view + + ibexa.content_view_provider.default_configured: + class: Ibexa\Bundle\Core\View\Provider\Configured + arguments: ['@ibexa.content_view.default_matcher_factory'] + tags: + - {name: ibexa.view.provider, type: 'Ibexa\Core\MVC\Symfony\View\ContentView', priority: -1} + + ibexa.content_view.default_matcher_factory: + class: Ibexa\Bundle\Core\Matcher\ServiceAwareMatcherFactory + arguments: + - '@Ibexa\Contracts\Core\MVC\View\ViewMatcherRegistryInterface' + - '@ibexa.api.repository' + - 'Ibexa\Core\MVC\Symfony\Matcher\ContentBased' + + ibexa.content_view.default_matcher_factory.dynamically_configured: + class: Ibexa\Core\MVC\Symfony\Matcher\DynamicallyConfiguredMatcherFactoryDecorator + decorates: ibexa.content_view.default_matcher_factory + arguments: + $innerConfigurableMatcherFactory: '@ibexa.content_view.default_matcher_factory.dynamically_configured.inner' + $configResolver: '@ibexa.config.resolver' + $parameterName: content_view_defaults + + ibexa.location_view_provider.configured: + class: Ibexa\Bundle\Core\View\Provider\Configured + arguments: ['@ibexa.location_view.matcher_factory'] + tags: + - {name: ibexa.view.provider, type: 'Ibexa\Core\MVC\Symfony\View\ContentView', priority: 10} + + ibexa.location_view.matcher_factory: + class: Ibexa\Bundle\Core\Matcher\ServiceAwareMatcherFactory + arguments: + $viewMatcherRegistry: '@Ibexa\Contracts\Core\MVC\View\ViewMatcherRegistryInterface' + $repository: '@ibexa.api.repository' + $relativeNamespace: 'Ibexa\Core\MVC\Symfony\Matcher\ContentBased' + + ibexa.location_view.matcher_factory.dynamically_configured: + class: Ibexa\Core\MVC\Symfony\Matcher\DynamicallyConfiguredMatcherFactoryDecorator + decorates: ibexa.location_view.matcher_factory + arguments: + $innerConfigurableMatcherFactory: '@ibexa.location_view.matcher_factory.dynamically_configured.inner' + $configResolver: '@ibexa.config.resolver' + $parameterName: location_view + + Ibexa\Core\MVC\Symfony\Templating\GlobalHelper: + class: Ibexa\Core\MVC\Symfony\Templating\GlobalHelper + arguments: ['@ibexa.config.resolver', '@ibexa.siteaccessaware.service.location', "@router", '@Ibexa\Core\Helper\TranslationHelper'] + calls: + - [setRequestStack, ["@request_stack"]] + + ibexa.templating.global_helper: + alias: Ibexa\Core\MVC\Symfony\Templating\GlobalHelper + + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\CoreExtension: + class: Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\CoreExtension + arguments: ['@ibexa.templating.global_helper'] + tags: + - {name: twig.extension} + + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\FileSizeExtension: + class: Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\FileSizeExtension + arguments: ["@translator", '%ibexa.twig.extension.filesize.suffixes%', '@ibexa.config.resolver', '@Ibexa\Core\MVC\Symfony\Locale\LocaleConverter' ] + tags: + - {name: twig.extension} + + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\RoutingExtension: + class: Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\RoutingExtension + arguments: ['@Ibexa\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator', "@router"] + tags: + - {name: twig.extension} + + Ibexa\Core\MVC\Symfony\Templating\Twig\ResourceProvider: + arguments: + $configResolver: '@ibexa.config.resolver' + + Ibexa\Core\MVC\Symfony\Templating\Twig\FieldBlockRenderer: + class: Ibexa\Core\MVC\Symfony\Templating\Twig\FieldBlockRenderer + arguments: + $twig: '@twig' + $resourceProvider: '@Ibexa\Core\MVC\Symfony\Templating\Twig\ResourceProvider' + $baseTemplate: '%ibexa.content_view.viewbase_layout%' + + ibexa.templating.field_block_renderer: + alias: Ibexa\Core\MVC\Symfony\Templating\Twig\FieldBlockRenderer + + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\FieldRenderingExtension: + class: Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\FieldRenderingExtension + arguments: + - '@ibexa.templating.field_block_renderer' + - '@Ibexa\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry' + - '@Ibexa\Core\Helper\TranslationHelper' + tags: + - { name: twig.extension } + + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\QueryRenderingExtension: + arguments: + - '@fragment.handler' + tags: + - { name: twig.extension } + + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\RenderExtension: + arguments: + $renderStrategy: '@Ibexa\Contracts\Core\MVC\Templating\RenderStrategy' + $eventDispatcher: '@event_dispatcher' + tags: + - { name: twig.extension } + + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\RenderContentExtension: + arguments: + $renderContentStrategy: '@Ibexa\Core\MVC\Symfony\Templating\RenderContentStrategy' + $eventDispatcher: '@event_dispatcher' + tags: + - { name: twig.extension } + + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\RenderLocationExtension: + arguments: + $renderLocationStrategy: '@Ibexa\Core\MVC\Symfony\Templating\RenderLocationStrategy' + $eventDispatcher: '@event_dispatcher' + tags: + - { name: twig.extension } + + Ibexa\Core\MVC\Symfony\Templating\RenderStrategy: + arguments: + $strategies: !tagged_iterator ibexa.view.render.strategy + + Ibexa\Contracts\Core\MVC\Templating\RenderStrategy: '@Ibexa\Core\MVC\Symfony\Templating\RenderStrategy' + + Ibexa\Core\MVC\Symfony\Templating\RenderContentStrategy: + arguments: + $fragmentRenderers: !tagged_iterator kernel.fragment_renderer + $defaultRenderer: !php/const Ibexa\Bundle\Core\Fragment\DirectFragmentRenderer::NAME + $siteAccess: '@Ibexa\Core\MVC\Symfony\SiteAccess' + $requestStack: '@request_stack' + tags: + - { name: ibexa.view.render.strategy } + + Ibexa\Core\MVC\Symfony\Templating\RenderLocationStrategy: + arguments: + $fragmentRenderers: !tagged_iterator kernel.fragment_renderer + $defaultRenderer: !php/const Ibexa\Bundle\Core\Fragment\DirectFragmentRenderer::NAME + $siteAccess: '@Ibexa\Core\MVC\Symfony\SiteAccess' + $requestStack: '@request_stack' + tags: + - { name: ibexa.view.render.strategy } + + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\ImageExtension: + class: Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\ImageExtension + arguments: + - '@ibexa.field_type.ezimage.variation_service' + - '@Ibexa\Core\FieldType\ImageAsset\AssetMapper' + tags: + - { name: twig.extension } + + Ibexa\Core\MVC\Symfony\View\CustomLocationControllerChecker: + class: Ibexa\Core\MVC\Symfony\View\CustomLocationControllerChecker + + Ibexa\Core\MVC\Symfony\View\Provider\Registry: + class: Ibexa\Core\MVC\Symfony\View\Provider\Registry + + Ibexa\Core\MVC\Symfony\View\Configurator\ViewProvider: + class: Ibexa\Core\MVC\Symfony\View\Configurator\ViewProvider + arguments: ['@Ibexa\Core\MVC\Symfony\View\Provider\Registry'] + + Ibexa\Core\MVC\Symfony\View\Builder\Registry\ControllerMatch: + class: Ibexa\Core\MVC\Symfony\View\Builder\Registry\ControllerMatch + arguments: + $viewBuilders: !tagged_iterator { tag: ibexa.view.builder } + + Ibexa\Core\MVC\Symfony\View\Builder\ContentViewBuilder: + class: Ibexa\Core\MVC\Symfony\View\Builder\ContentViewBuilder + arguments: + - '@ibexa.siteaccessaware.repository' + - '@Ibexa\Core\MVC\Symfony\View\Configurator\ViewProvider' + - '@Ibexa\Core\MVC\Symfony\View\ParametersInjector\EventDispatcherInjector' + - "@request_stack" + - '@Ibexa\Core\Helper\ContentInfoLocationLoader\SudoMainLocationLoader' + tags: + - { name: ibexa.view.builder } + + Ibexa\Core\MVC\Symfony\View\Builder\ParametersFilter\RequestAttributes: + class: Ibexa\Core\MVC\Symfony\View\Builder\ParametersFilter\RequestAttributes + tags: + - {name: kernel.event_subscriber} + + Ibexa\Core\MVC\Symfony\View\Renderer\TemplateRenderer: + class: Ibexa\Core\MVC\Symfony\View\Renderer\TemplateRenderer + arguments: ["@twig", "@event_dispatcher"] + + Ibexa\Bundle\Core\EventListener\ViewRendererListener: + class: Ibexa\Bundle\Core\EventListener\ViewRendererListener + arguments: ['@Ibexa\Core\MVC\Symfony\View\Renderer\TemplateRenderer'] + tags: + - { name: kernel.event_subscriber } + + Ibexa\Core\MVC\Symfony\View\ParametersInjector\EventDispatcherInjector: + class: Ibexa\Core\MVC\Symfony\View\ParametersInjector\EventDispatcherInjector + arguments: ["@event_dispatcher"] + + Ibexa\Core\MVC\Symfony\View\ParametersInjector\CustomParameters: + class: Ibexa\Core\MVC\Symfony\View\ParametersInjector\CustomParameters + tags: + - { name: kernel.event_subscriber } + + Ibexa\Core\MVC\Symfony\View\ParametersInjector\EmbedObjectParameters: + class: Ibexa\Core\MVC\Symfony\View\ParametersInjector\EmbedObjectParameters + tags: + - { name: kernel.event_subscriber } + + Ibexa\Core\MVC\Symfony\View\ParametersInjector\NoLayout: + class: Ibexa\Core\MVC\Symfony\View\ParametersInjector\NoLayout + tags: + - { name: kernel.event_subscriber } + + Ibexa\Core\MVC\Symfony\View\ParametersInjector\ValueObjectsIds: + class: Ibexa\Core\MVC\Symfony\View\ParametersInjector\ValueObjectsIds + tags: + - { name: kernel.event_subscriber } + + Ibexa\Core\MVC\Symfony\View\ParametersInjector\ViewbaseLayout: + class: Ibexa\Core\MVC\Symfony\View\ParametersInjector\ViewbaseLayout + arguments: + - '%ibexa.content_view.viewbase_layout%' + - '@ibexa.config.resolver' + tags: + - { name: kernel.event_subscriber } + + ezpublish.view.cache_response_listener: + class: Ibexa\Bundle\Core\EventListener\CacheViewResponseListener + arguments: + $configResolver: '@ibexa.config.resolver' + tags: + - { name: kernel.event_subscriber } + + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\DataAttributesExtension: + autoconfigure: true + public: false + + Ibexa\Bundle\Core\Templating\Twig\ContextAwareTwigVariablesExtension: + arguments: + $configResolver: '@ibexa.config.resolver' + tags: + - { name: twig.extension } + + Ibexa\Core\MVC\Symfony\EventListener\ContentViewTwigVariablesSubscriber: + autoconfigure: true + autowire: true + + Ibexa\Core\MVC\Symfony\View\GenericVariableProviderRegistry: + arguments: + $twigVariableProviders: !tagged ezplatform.view.variable_provider + + Ibexa\Core\MVC\Symfony\View\VariableProviderRegistry: '@Ibexa\Core\MVC\Symfony\View\GenericVariableProviderRegistry' + + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\UserExtension: + arguments: + $userService: '@Ibexa\Contracts\Core\Repository\UserService' + $permissionResolver: '@Ibexa\Contracts\Core\Repository\PermissionResolver' + tags: + - { name: twig.extension } + + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\UserPreferenceExtension: + autowire: true + autoconfigure: true + + Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\UserPreferenceRuntime: + autowire: true + autoconfigure: true diff --git a/src/bundle/Core/Resources/config/thumbnails.yml b/src/bundle/Core/Resources/config/thumbnails.yml new file mode 100644 index 0000000000..42423771d6 --- /dev/null +++ b/src/bundle/Core/Resources/config/thumbnails.yml @@ -0,0 +1,39 @@ +services: + _defaults: + public: false + autoconfigure: true + autowire: true + + Ibexa\Core\FieldType\Image\ImageThumbnailStrategy: + arguments: + $variationHandler: '@Ibexa\Contracts\Core\Variation\VariationHandler' + $variationName: 'medium' + + Ibexa\Core\FieldType\Image\ImageThumbnailProxyStrategy: + decorates: Ibexa\Core\FieldType\Image\ImageThumbnailStrategy + arguments: + $imageThumbnailStrategy: '@.inner' + $proxyGenerator: '@Ibexa\Core\Repository\ProxyFactory\ProxyGeneratorInterface' + + Ibexa\Core\FieldType\ImageAsset\ImageAssetThumbnailStrategy: + lazy: true + arguments: + $thumbnailStrategy: '@Ibexa\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy' + $contentService: '@ibexa.api.service.content' + + Ibexa\Core\Repository\Strategy\ContentThumbnail\Field\ContentFieldStrategy: + arguments: + $strategies: + ezimage: '@Ibexa\Core\FieldType\Image\ImageThumbnailStrategy' + ezimageasset: '@Ibexa\Core\FieldType\ImageAsset\ImageAssetThumbnailStrategy' + + Ibexa\Core\Repository\Strategy\ContentThumbnail\FirstMatchingFieldStrategy: + arguments: + $fieldTypeService: '@ibexa.api.service.field_type' + $contentFieldStrategy: '@Ibexa\Core\Repository\Strategy\ContentThumbnail\Field\ContentFieldStrategy' + tags: + - { name: ibexa.repository.thumbnail.strategy.content, priority: 0 } + + Ibexa\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy: + arguments: + $strategies: !tagged_iterator ibexa.repository.thumbnail.strategy.content diff --git a/src/bundle/Core/Resources/config/url_checker.yml b/src/bundle/Core/Resources/config/url_checker.yml new file mode 100644 index 0000000000..40e295bc13 --- /dev/null +++ b/src/bundle/Core/Resources/config/url_checker.yml @@ -0,0 +1,45 @@ +services: + Ibexa\Bundle\Core\URLChecker\URLChecker: + class: 'Ibexa\Bundle\Core\URLChecker\URLChecker' + arguments: + - '@Ibexa\Core\Repository\URLService' + - '@Ibexa\Bundle\Core\URLChecker\URLHandlerRegistry' + calls: + - ['setLogger', ['@?logger']] + lazy: true + + Ibexa\Bundle\Core\URLChecker\URLHandlerRegistry: + class: 'Ibexa\Bundle\Core\URLChecker\URLHandlerRegistry' + + ibexa.url_checker.handler.base: + abstract: true + arguments: + - '@Ibexa\Core\Repository\URLService' + calls: + - ['setLogger', ['@?logger']] + + ibexa.url_checker.handler.http: + class: 'Ibexa\Bundle\Core\URLChecker\Handler\HTTPHandler' + parent: ibexa.url_checker.handler.base + arguments: + $configResolver: '@ibexa.config.resolver' + $parameterName: url_handler.http.options + tags: + - { name: ibexa.url_checker.handler, scheme: http } + + ibexa.url_checker.handler.https: + class: 'Ibexa\Bundle\Core\URLChecker\Handler\HTTPHandler' + parent: ibexa.url_checker.handler.base + arguments: + $configResolver: '@ibexa.config.resolver' + $parameterName: url_handler.https.options + tags: + - { name: ibexa.url_checker.handler, scheme: https } + + Ibexa\Bundle\Core\URLChecker\Handler\MailToHandler: + class: 'Ibexa\Bundle\Core\URLChecker\Handler\MailToHandler' + parent: ibexa.url_checker.handler.base + arguments: + $configResolver: '@ibexa.config.resolver' + tags: + - { name: ibexa.url_checker.handler, scheme: mailto } diff --git a/src/bundle/Core/Resources/config/url_wildcard.yml b/src/bundle/Core/Resources/config/url_wildcard.yml new file mode 100644 index 0000000000..854696e31a --- /dev/null +++ b/src/bundle/Core/Resources/config/url_wildcard.yml @@ -0,0 +1,12 @@ +services: + ezpublish.urlwildcard_router: + class: Ibexa\Core\MVC\Symfony\Routing\UrlWildcardRouter + public: true + arguments: + - '@ibexa.api.service.url_wildcard' + - '@Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator' + - '@router.request_context' + calls: + - [setLogger, ['@?logger']] + tags: + - { name: router, priority: 210 } diff --git a/src/bundle/Core/Resources/translations/forms.en.xlf b/src/bundle/Core/Resources/translations/forms.en.xlf new file mode 100644 index 0000000000..6ff9c6c80e --- /dev/null +++ b/src/bundle/Core/Resources/translations/forms.en.xlf @@ -0,0 +1,356 @@ +<?xml version="1.0" encoding="utf-8"?> +<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2"> + <file source-language="en" target-language="en" datatype="plaintext" original="not.available"> + <header> + <tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> + <note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note> + </header> + <body> + <trans-unit id="d4690e1d0c7d2cf0468ab5aa04caa891e1b5efbd" resname="role.policy.all_modules_all_functions"> + <source>All modules / All functions</source> + <target state="new">All modules / All functions</target> + <note>key: role.policy.all_modules_all_functions</note> + </trans-unit> + <trans-unit id="f52e45b25ff70b3d1d07c57911ceda58dcdf7431" resname="role.policy.class"> + <source>Content type</source> + <target>Content type</target> + <note>key: role.policy.class</note> + </trans-unit> + <trans-unit id="2f746c35c587bdc84cacf38d0e63e08855e831ab" resname="role.policy.class.all_functions"> + <source>Content type / All functions</source> + <target>Content type / All functions</target> + <note>key: role.policy.class.all_functions</note> + </trans-unit> + <trans-unit id="02049080d0f8dea3f723bbd1a280900b0657914e" resname="role.policy.class.create"> + <source>Content type / Create</source> + <target>Content type / Create</target> + <note>key: role.policy.class.create</note> + </trans-unit> + <trans-unit id="17ee6a3320ad9ac02c376bdcb875b266c28e34db" resname="role.policy.class.delete"> + <source>Content type / Delete</source> + <target>Content type / Delete</target> + <note>key: role.policy.class.delete</note> + </trans-unit> + <trans-unit id="f47b14b0adc8b08abd44c37e801d6b2c3180a2ee" resname="role.policy.class.update"> + <source>Content type / Update</source> + <target>Content type / Update</target> + <note>key: role.policy.class.update</note> + </trans-unit> + <trans-unit id="ffe83a713e8b33558df040efa1eeadd0b1cc18e0" resname="role.policy.content"> + <source>Content</source> + <target>Content</target> + <note>key: role.policy.content</note> + </trans-unit> + <trans-unit id="ea72f76a43bc0ec3eff965e68279dab5597b19c0" resname="role.policy.content.all_functions"> + <source>Content / All functions</source> + <target>Content / All functions</target> + <note>key: role.policy.content.all_functions</note> + </trans-unit> + <trans-unit id="e050709df5ae56b1e9168038182f4c52221ffab9" resname="role.policy.content.cleantrash"> + <source>Content / Clean trash</source> + <target>Content / Clean trash</target> + <note>key: role.policy.content.cleantrash</note> + </trans-unit> + <trans-unit id="597c25abf1e2d91e50b82d2699f8b85f68c9f305" resname="role.policy.content.create"> + <source>Content / Create</source> + <target>Content / Create</target> + <note>key: role.policy.content.create</note> + </trans-unit> + <trans-unit id="019bae55ae37c03117c05eb25cc208e9b2396827" resname="role.policy.content.diff"> + <source>Content / Diff</source> + <target>Content / Diff</target> + <note>key: role.policy.content.diff</note> + </trans-unit> + <trans-unit id="bdb3e6e10d770d20ffa7966acb58d3e9cf9805e2" resname="role.policy.content.edit"> + <source>Content / Edit</source> + <target>Content / Edit</target> + <note>key: role.policy.content.edit</note> + </trans-unit> + <trans-unit id="1049e14eb7fec2dc053571f607e0eb044a06e455" resname="role.policy.content.hide"> + <source>Content / Hide</source> + <target>Content / Hide</target> + <note>key: role.policy.content.hide</note> + </trans-unit> + <trans-unit id="d05f0b35c91f7d444c466871ff02886d74dc8f55" resname="role.policy.content.manage_locations"> + <source>Content / Manage locations</source> + <target>Content / Manage locations</target> + <note>key: role.policy.content.manage_locations</note> + </trans-unit> + <trans-unit id="0c943bab11cd582ac439e67289d4efd86d8599e7" resname="role.policy.content.pendinglist"> + <source>Content / Pending list</source> + <target>Content / Pending list</target> + <note>key: role.policy.content.pendinglist</note> + </trans-unit> + <trans-unit id="6f595f392af03d8b2e46449d527530e3e1f8eb94" resname="role.policy.content.publish"> + <source>Content / Publish</source> + <target>Content / Publish</target> + <note>key: role.policy.content.publish</note> + </trans-unit> + <trans-unit id="2b5e52d4783100540d791657f21d9dc1a9fbbb30" resname="role.policy.content.read"> + <source>Content / Read</source> + <target>Content / Read</target> + <note>key: role.policy.content.read</note> + </trans-unit> + <trans-unit id="967c7d2238b619a63fd0fe00bc0ccd4589e9de4f" resname="role.policy.content.remove"> + <source>Content / Remove</source> + <target>Content / Remove</target> + <note>key: role.policy.content.remove</note> + </trans-unit> + <trans-unit id="4ce8821efab15ebdd683e28f0dcffaa352d1ef0f" resname="role.policy.content.restore"> + <source>Content / Restore</source> + <target>Content / Restore</target> + <note>key: role.policy.content.restore</note> + </trans-unit> + <trans-unit id="7ad7cd36512ca57f6d6cb31b7de0740d4754961c" resname="role.policy.content.reverserelatedlist"> + <source>Content / Reverse related list</source> + <target>Content / Reverse related list</target> + <note>key: role.policy.content.reverserelatedlist</note> + </trans-unit> + <trans-unit id="c9a69e432793ed3480c2252f3ca1b4e6564e853e" resname="role.policy.content.translate"> + <source>Content / Translate</source> + <target>Content / Translate</target> + <note>key: role.policy.content.translate</note> + </trans-unit> + <trans-unit id="3f9650d96394bc9e6d53360117d2dd3d6842735f" resname="role.policy.content.translations"> + <source>Content / Translations</source> + <target>Content / Translations</target> + <note>key: role.policy.content.translations</note> + </trans-unit> + <trans-unit id="74ccbb06e1226201f9642eefa451b67360a4145b" resname="role.policy.content.unlock"> + <source>Content / Unlock</source> + <target>Content / Unlock</target> + <note>key: role.policy.content.unlock</note> + </trans-unit> + <trans-unit id="79ffa7a2f2449d08574ef7c5b1f447b439db97b2" resname="role.policy.content.urltranslator"> + <source>Content / Url translator</source> + <target>Content / Url translator</target> + <note>key: role.policy.content.urltranslator</note> + </trans-unit> + <trans-unit id="0a641d7ab31b48b6b87a9d2cd4763afae237219d" resname="role.policy.content.versionread"> + <source>Content / Version read</source> + <target>Content / Version read</target> + <note>key: role.policy.content.versionread</note> + </trans-unit> + <trans-unit id="4e615fcc14a6f5b70fb3e7957518c27bc508d92f" resname="role.policy.content.versionremove"> + <source>Content / Version remove</source> + <target>Content / Version remove</target> + <note>key: role.policy.content.versionremove</note> + </trans-unit> + <trans-unit id="81703573067457c42db3d0c8cce70e1d1e0128a5" resname="role.policy.content.view_embed"> + <source>Content / View embed</source> + <target state="new">Content / View embed</target> + <note>key: role.policy.content.view_embed</note> + </trans-unit> + <trans-unit id="6ff0bcc1b70d0dbde113af7649393f667612cace" resname="role.policy.role"> + <source>Role</source> + <target>Role</target> + <note>key: role.policy.role</note> + </trans-unit> + <trans-unit id="17f55c17c76ccfcf636ce429b867f89069609bcd" resname="role.policy.role.all_functions"> + <source>Role / All functions</source> + <target>Role / All functions</target> + <note>key: role.policy.role.all_functions</note> + </trans-unit> + <trans-unit id="cf922ebcfc51ae098cb57a938984916e03c0481c" resname="role.policy.role.assign"> + <source>Role / Assign</source> + <target>Role / Assign</target> + <note>key: role.policy.role.assign</note> + </trans-unit> + <trans-unit id="f12cc8bc381274c232a90deae7ce981dbc3634cb" resname="role.policy.role.create"> + <source>Role / Create</source> + <target>Role / Create</target> + <note>key: role.policy.role.create</note> + </trans-unit> + <trans-unit id="d17c0339a48c6e273c56673c31037427fd95ecee" resname="role.policy.role.delete"> + <source>Role / Delete</source> + <target>Role / Delete</target> + <note>key: role.policy.role.delete</note> + </trans-unit> + <trans-unit id="1e809cc5ab4e914e5dc51fa2dc0a6b62a90bc2f1" resname="role.policy.role.read"> + <source>Role / Read</source> + <target>Role / Read</target> + <note>key: role.policy.role.read</note> + </trans-unit> + <trans-unit id="2f8dccfd705bd25a633826ff76f595e118a71dcf" resname="role.policy.role.update"> + <source>Role / Update</source> + <target>Role / Update</target> + <note>key: role.policy.role.update</note> + </trans-unit> + <trans-unit id="7153cdd53211bedab219659c1d1656693d3bb4ae" resname="role.policy.section"> + <source>Section</source> + <target>Section</target> + <note>key: role.policy.section</note> + </trans-unit> + <trans-unit id="f5e387481bf702e73317dcbccff5d55fc111550e" resname="role.policy.section.all_functions"> + <source>Section / All functions</source> + <target>Section / All functions</target> + <note>key: role.policy.section.all_functions</note> + </trans-unit> + <trans-unit id="2f63b236417b33905613e757497387c99e47a848" resname="role.policy.section.assign"> + <source>Section / Assign</source> + <target>Section / Assign</target> + <note>key: role.policy.section.assign</note> + </trans-unit> + <trans-unit id="9ce67233e7377c7ac36c6ccbdf1a49a582aca59d" resname="role.policy.section.edit"> + <source>Section / Edit</source> + <target>Section / Edit</target> + <note>key: role.policy.section.edit</note> + </trans-unit> + <trans-unit id="c80fb60940421ce0b94226255211ec10dc9208b4" resname="role.policy.section.view"> + <source>Section / View</source> + <target>Section / View</target> + <note>key: role.policy.section.view</note> + </trans-unit> + <trans-unit id="5f165ef2e938f6710b56e2e8ee416414e430b77c" resname="role.policy.setting"> + <source>Setting</source> + <target>Setting</target> + <note>key: role.policy.setting</note> + </trans-unit> + <trans-unit id="8775d52b987252ee1ad0867fe4f9e23dec668b5b" resname="role.policy.setting.all_functions"> + <source>Setting / All functions</source> + <target>Setting / All functions</target> + <note>key: role.policy.setting.all_functions</note> + </trans-unit> + <trans-unit id="686f427740ca0cf4df247266b8a1300d747cf3a4" resname="role.policy.setting.create"> + <source>Setting / Create</source> + <target>Setting / Create</target> + <note>key: role.policy.setting.create</note> + </trans-unit> + <trans-unit id="d41141e8fa3c214bc546a6925304c15ee9a28022" resname="role.policy.setting.remove"> + <source>Setting / Remove</source> + <target>Setting / Remove</target> + <note>key: role.policy.setting.remove</note> + </trans-unit> + <trans-unit id="14fa9c5afba9d51072c6a66dede46535b718f77e" resname="role.policy.setting.update"> + <source>Setting / Update</source> + <target>Setting / Update</target> + <note>key: role.policy.setting.update</note> + </trans-unit> + <trans-unit id="33092ebd51699d4d3f598b8488e7bc5969038bff" resname="role.policy.setup"> + <source>Setup</source> + <target>Setup</target> + <note>key: role.policy.setup</note> + </trans-unit> + <trans-unit id="a72fa98205c4f68fc53506f9b57e645be450ce86" resname="role.policy.setup.administrate"> + <source>Setup / Administrate</source> + <target>Setup / Administrate</target> + <note>key: role.policy.setup.administrate</note> + </trans-unit> + <trans-unit id="df52828e26336a4a6f3186e0d6eb75d98056aeaa" resname="role.policy.setup.all_functions"> + <source>Setup / All functions</source> + <target>Setup / All functions</target> + <note>key: role.policy.setup.all_functions</note> + </trans-unit> + <trans-unit id="32c1032c29386bcf41793f2542b3476887249bc5" resname="role.policy.setup.install"> + <source>Setup / Install</source> + <target>Setup / Install</target> + <note>key: role.policy.setup.install</note> + </trans-unit> + <trans-unit id="4ce0aa17f80167751257ddb6e0cc8e06ca9c1bbb" resname="role.policy.setup.setup"> + <source>Setup / Setup</source> + <target>Setup / Setup</target> + <note>key: role.policy.setup.setup</note> + </trans-unit> + <trans-unit id="a77dcd2893dc76a7e94fe088b775045fde8651e4" resname="role.policy.setup.system_info"> + <source>Setup / System info</source> + <target>Setup / System info</target> + <note>key: role.policy.setup.system_info</note> + </trans-unit> + <trans-unit id="012dce76ef569ba377ffe98ddbee8d3c3d270e15" resname="role.policy.state"> + <source>State</source> + <target>State</target> + <note>key: role.policy.state</note> + </trans-unit> + <trans-unit id="0dc3b2e6978f026be583bcf078e15c4157a51d19" resname="role.policy.state.administrate"> + <source>State / Administrate</source> + <target>State / Administrate</target> + <note>key: role.policy.state.administrate</note> + </trans-unit> + <trans-unit id="5a57c4086c786d6c767dfca68486d64c60ec58bf" resname="role.policy.state.all_functions"> + <source>State / All functions</source> + <target>State / All functions</target> + <note>key: role.policy.state.all_functions</note> + </trans-unit> + <trans-unit id="8c144b2e58f6ba9388517a5929bfd12a0773ddd2" resname="role.policy.state.assign"> + <source>State / Assign</source> + <target>State / Assign</target> + <note>key: role.policy.state.assign</note> + </trans-unit> + <trans-unit id="73c3d3a21c7264a991abf4e6ffe68751a1dbdb58" resname="role.policy.url"> + <source>URL</source> + <target state="new">URL</target> + <note>key: role.policy.url</note> + </trans-unit> + <trans-unit id="4b00222e7c5b268480fe9db2de1744d0f25b677f" resname="role.policy.url.all_functions"> + <source>URL / All functions</source> + <target state="new">URL / All functions</target> + <note>key: role.policy.url.all_functions</note> + </trans-unit> + <trans-unit id="4bf7f289b67892bf61f10b4f9a1db7b6dc09e69a" resname="role.policy.url.update"> + <source>URL / Update</source> + <target state="new">URL / Update</target> + <note>key: role.policy.url.update</note> + </trans-unit> + <trans-unit id="a9e0e8261157424542b507b9ae39b4e1193d24cd" resname="role.policy.url.view"> + <source>URL / View</source> + <target state="new">URL / View</target> + <note>key: role.policy.url.view</note> + </trans-unit> + <trans-unit id="78b9d0d52e636ef2d0d1f919de16154404c2be0f" resname="role.policy.user"> + <source>User</source> + <target>User</target> + <note>key: role.policy.user</note> + </trans-unit> + <trans-unit id="647dfa7992026277b6d73295b2ffaefe4cfa12d0" resname="role.policy.user.activation"> + <source>User / Activation</source> + <target>User / Activation</target> + <note>key: role.policy.user.activation</note> + </trans-unit> + <trans-unit id="d44ce8c33cbc780cff9ca6f05ad2f70002550cd0" resname="role.policy.user.all_functions"> + <source>User / All functions</source> + <target>User / All functions</target> + <note>key: role.policy.user.all_functions</note> + </trans-unit> + <trans-unit id="587199065d14880416b8061b73788e731a201c27" resname="role.policy.user.invite"> + <source>User / Invite</source> + <target>User / Invite</target> + <note>key: role.policy.user.invite</note> + </trans-unit> + <trans-unit id="f25505dc2f59df21e3125664c4fdad951d82df3b" resname="role.policy.user.login"> + <source>User / Login</source> + <target>User / Login</target> + <note>key: role.policy.user.login</note> + </trans-unit> + <trans-unit id="d971078868184d3d233fb14032fda9ef614c2baa" resname="role.policy.user.password"> + <source>User / Password</source> + <target>User / Password</target> + <note>key: role.policy.user.password</note> + </trans-unit> + <trans-unit id="28d15501824f89951d4068af32fbc52eb1412b8d" resname="role.policy.user.preferences"> + <source>User / Preferences</source> + <target>User / Preferences</target> + <note>key: role.policy.user.preferences</note> + </trans-unit> + <trans-unit id="8f79f6008557e588c0a8b13ccdc42d6babc073c9" resname="role.policy.user.register"> + <source>User / Register</source> + <target>User / Register</target> + <note>key: role.policy.user.register</note> + </trans-unit> + <trans-unit id="106b2a3fa38c660c838efe1905c904ce689a2d75" resname="role.policy.user.selfedit"> + <source>User / Self edit</source> + <target>User / Self edit</target> + <note>key: role.policy.user.selfedit</note> + </trans-unit> + <trans-unit id="caaf0c6eabe978a17eefcedf501b2506383a8267" resname="role.policy.user.update"> + <source>User / Update</source> + <target>User / Update</target> + <note>key: role.policy.user.update</note> + </trans-unit> + <trans-unit id="b445a673ef3c893b783c58b6fd4b2469224a04db" resname="role.policy.user.view"> + <source>User / View</source> + <target>User / View</target> + <note>key: role.policy.user.view</note> + </trans-unit> + </body> + </file> +</xliff> diff --git a/src/bundle/Core/Resources/translations/ibexa_content_fields.en.xlf b/src/bundle/Core/Resources/translations/ibexa_content_fields.en.xlf new file mode 100644 index 0000000000..38a1439791 --- /dev/null +++ b/src/bundle/Core/Resources/translations/ibexa_content_fields.en.xlf @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2"> + <file source-language="en" target-language="en" datatype="plaintext" original="not.available"> + <header> + <tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> + <note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note> + </header> + <body> + <trans-unit id="f337a8fd9042dbc4de8b5d305b6d8df3c0c374c4" resname="content-field.latitude.not_set"> + <source>Not set</source> + <target>Not set</target> + <note>key: content-field.latitude.not_set</note> + </trans-unit> + <trans-unit id="687973ca20983f27a99c50243e364e346c9c4814" resname="content-field.longitude.not_set"> + <source>Not set</source> + <target>Not set</target> + <note>key: content-field.longitude.not_set</note> + </trans-unit> + </body> + </file> +</xliff> diff --git a/src/bundle/Core/Resources/translations/ibexa_fielddefinition.en.xlf b/src/bundle/Core/Resources/translations/ibexa_fielddefinition.en.xlf new file mode 100644 index 0000000000..9e67f3842c --- /dev/null +++ b/src/bundle/Core/Resources/translations/ibexa_fielddefinition.en.xlf @@ -0,0 +1,321 @@ +<?xml version="1.0" encoding="utf-8"?> +<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2"> + <file source-language="en" target-language="en" datatype="plaintext" original="not.available"> + <header> + <tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> + <note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note> + </header> + <body> + <trans-unit id="9c2eb5d844a5e2507a5c0f1ba534b5a392b1a40d" resname="fielddefinition.allowed-content-types.any"> + <source>Any</source> + <target>Any</target> + <note>key: fielddefinition.allowed-content-types.any</note> + </trans-unit> + <trans-unit id="70d065216b588dd0cf8c624132941a29f1c1459f" resname="fielddefinition.allowed-content-types.label"> + <source>Allowed content types:</source> + <target>Allowed content types:</target> + <note>key: fielddefinition.allowed-content-types.label</note> + </trans-unit> + <trans-unit id="eb0eeb83b3f40e1293f0edd593a8cce658d9f35d" resname="fielddefinition.default-value.checked"> + <source>Checked</source> + <target>Checked</target> + <note>key: fielddefinition.default-value.checked</note> + </trans-unit> + <trans-unit id="aa6b4bece2c401e90fc0828a11ad372eaf2f3695" resname="fielddefinition.default-value.current_author"> + <source>Current User</source> + <target>Current User</target> + <note>key: fielddefinition.default-value.current_author</note> + </trans-unit> + <trans-unit id="3410f38fd1f44cb841ec374c13daca04dcc5910b" resname="fielddefinition.default-value.current_date"> + <source>Current date</source> + <target>Current date</target> + <note>key: fielddefinition.default-value.current_date</note> + </trans-unit> + <trans-unit id="d3cda4b47dfae160f1e7f982570b8b544a494701" resname="fielddefinition.default-value.current_datetime"> + <source>Current datetime</source> + <target>Current datetime</target> + <note>key: fielddefinition.default-value.current_datetime</note> + </trans-unit> + <trans-unit id="5cdfd0738c76b8fe520f77e56b819fd3a781a514" resname="fielddefinition.default-value.current_datetime_adjust_by"> + <source>Current datetime adjusted by</source> + <target>Current datetime adjusted by</target> + <note>key: fielddefinition.default-value.current_datetime_adjust_by</note> + </trans-unit> + <trans-unit id="baf973c669d404a5aa072edce1d84deaaf936a0f" resname="fielddefinition.default-value.current_time"> + <source>Current time</source> + <target>Current time</target> + <note>key: fielddefinition.default-value.current_time</note> + </trans-unit> + <trans-unit id="fa5a2f0bf185c7e872f77b81a24867afb9ef0ec7" resname="fielddefinition.default-value.empty"> + <source>Empty</source> + <target>Empty</target> + <note>key: fielddefinition.default-value.empty</note> + </trans-unit> + <trans-unit id="ad6776fd58e0ae4b2ba44ff014986ec6b1ec4de4" resname="fielddefinition.default-value.label"> + <source>Default value:</source> + <target>Default value:</target> + <note>key: fielddefinition.default-value.label</note> + </trans-unit> + <trans-unit id="232dd99b54158ff1f3be77113aae16e691ce406a" resname="fielddefinition.default-value.unchecked"> + <source>Unchecked</source> + <target>Unchecked</target> + <note>key: fielddefinition.default-value.unchecked</note> + </trans-unit> + <trans-unit id="908870d411d8599211d188e35d0240bfecb98b5e" resname="fielddefinition.default-value.undefined"> + <source>No default value</source> + <target>No default value</target> + <note>key: fielddefinition.default-value.undefined</note> + </trans-unit> + <trans-unit id="34ced944f5d7159b0597b98279e73be045c0ccd9" resname="fielddefinition.interval.day"> + <source>%default% %day% day(s)</source> + <target>%default% %day% day(s)</target> + <note>key: fielddefinition.interval.day</note> + </trans-unit> + <trans-unit id="4b31f5a6cfde5d347f3ad264daa7db77e704a5e0" resname="fielddefinition.interval.hour"> + <source>%default% %hour% hour(s)</source> + <target>%default% %hour% hour(s)</target> + <note>key: fielddefinition.interval.hour</note> + </trans-unit> + <trans-unit id="388ff2c36717a410a72060911657dce61108ed3c" resname="fielddefinition.interval.minute"> + <source>%default% %minute% minute(s)</source> + <target>%default% %minute% minute(s)</target> + <note>key: fielddefinition.interval.minute</note> + </trans-unit> + <trans-unit id="b93b6a1548ffa873b1ef5a95c727f93d7be2fd0c" resname="fielddefinition.interval.month"> + <source>%default% %month% month(s)</source> + <target>%default% %month% month(s)</target> + <note>key: fielddefinition.interval.month</note> + </trans-unit> + <trans-unit id="c410f54bc82c293dcce4b252928dc4a020ab1705" resname="fielddefinition.interval.second"> + <source>%default% %second% second(s)</source> + <target>%default% %second% second(s)</target> + <note>key: fielddefinition.interval.second</note> + </trans-unit> + <trans-unit id="c88fa282a777c6f3a778f513fc9e46800ffe0198" resname="fielddefinition.interval.year"> + <source>%default% %year% year(s)</source> + <target>%default% %year% year(s)</target> + <note>key: fielddefinition.interval.year</note> + </trans-unit> + <trans-unit id="fa2595f063cfd4284226372dc99eb3dfbbc36498" resname="fielddefinition.isbn.label"> + <source>Selected ISBN format:</source> + <target>Selected ISBN format:</target> + <note>key: fielddefinition.isbn.label</note> + </trans-unit> + <trans-unit id="c963dbac20e5526d70d08b17ebffdaaf44e1c2c3" resname="fielddefinition.max-length.label"> + <source>Max string length:</source> + <target>Max string length:</target> + <note>key: fielddefinition.max-length.label</note> + </trans-unit> + <trans-unit id="c8c3fa9b71c848069950539011a09b45abb1c11f" resname="fielddefinition.max-length.undefined"> + <source>No defined maximum string length</source> + <target>No defined maximum string length</target> + <note>key: fielddefinition.max-length.undefined</note> + </trans-unit> + <trans-unit id="9a3d23487e45edf331407c501ead33d70461f9e3" resname="fielddefinition.max-length.value"> + <source>%max% characters</source> + <target>%max% characters</target> + <note>key: fielddefinition.max-length.value</note> + </trans-unit> + <trans-unit id="11e7b0a01edea69919ea6191acdbd5ad7c2bf279" resname="fielddefinition.max-value.label"> + <source>Maximum value:</source> + <target>Maximum value:</target> + <note>key: fielddefinition.max-value.label</note> + </trans-unit> + <trans-unit id="444423d6eff78ccc1c92077813a212b4e5a002c8" resname="fielddefinition.max-value.undefined"> + <source>No defined maximum value</source> + <target>No defined maximum value</target> + <note>key: fielddefinition.max-value.undefined</note> + </trans-unit> + <trans-unit id="63f939a170c5c3675f13ca3e330ea981d4a5540c" resname="fielddefinition.maximum-file-size.label"> + <source>Maximum file size:</source> + <target>Maximum file size:</target> + <note>key: fielddefinition.maximum-file-size.label</note> + </trans-unit> + <trans-unit id="e58e37a5cd49fa5884d15ea8af61ad6973f33412" resname="fielddefinition.maximum-file-size.undefined"> + <source>No defined maximum size</source> + <target>No defined maximum size</target> + <note>key: fielddefinition.maximum-file-size.undefined</note> + </trans-unit> + <trans-unit id="3e08813722e7959e982f4d8624c98b83d077cd55" resname="fielddefinition.maximum-file-size.value"> + <source>%max% MB</source> + <target>%max% MB</target> + <note>key: fielddefinition.maximum-file-size.value</note> + </trans-unit> + <trans-unit id="b9bb83913e23ad5a59eb7485ca48386bd7fce66e" resname="fielddefinition.media-player-type.flash"> + <source>Flash</source> + <target>Flash</target> + <note>key: fielddefinition.media-player-type.flash</note> + </trans-unit> + <trans-unit id="1c31870437c9baa0d08c67140d35fa54c6223922" resname="fielddefinition.media-player-type.html5_audio"> + <source>HTML5 Audio</source> + <target>HTML5 Audio</target> + <note>key: fielddefinition.media-player-type.html5_audio</note> + </trans-unit> + <trans-unit id="22881c3ebee12191bc6f269d943c1140eea4bfb1" resname="fielddefinition.media-player-type.html5_video"> + <source>HTML5 Video</source> + <target>HTML5 Video</target> + <note>key: fielddefinition.media-player-type.html5_video</note> + </trans-unit> + <trans-unit id="0aaa90efdf7cc5042723e7500891db70da3f8850" resname="fielddefinition.media-player-type.label"> + <source>Media player type:</source> + <target>Media player type:</target> + <note>key: fielddefinition.media-player-type.label</note> + </trans-unit> + <trans-unit id="c7a28f2ea418dc17e634bf386d5f1db091582fc8" resname="fielddefinition.media-player-type.quick_time"> + <source>Quicktime</source> + <target>Quicktime</target> + <note>key: fielddefinition.media-player-type.quick_time</note> + </trans-unit> + <trans-unit id="edd8db0307e234e095d1788540bd026e48e173f8" resname="fielddefinition.media-player-type.real_player"> + <source>Real Player</source> + <target>Real Player</target> + <note>key: fielddefinition.media-player-type.real_player</note> + </trans-unit> + <trans-unit id="4ad14874021f1b806f9c93b41ef195b12fe5ab3f" resname="fielddefinition.media-player-type.silverlight"> + <source>Silverlight</source> + <target>Silverlight</target> + <note>key: fielddefinition.media-player-type.silverlight</note> + </trans-unit> + <trans-unit id="738ddf9e6b12ae22e5f8e62839f63221c24f1431" resname="fielddefinition.media-player-type.undefined"> + <source>No defined value</source> + <target>No defined value</target> + <note>key: fielddefinition.media-player-type.undefined</note> + </trans-unit> + <trans-unit id="61d9fc8a2cbcb067d39496dba7e884caf51cf245" resname="fielddefinition.media-player-type.windows_media_player"> + <source>Window Media Player</source> + <target>Window Media Player</target> + <note>key: fielddefinition.media-player-type.windows_media_player</note> + </trans-unit> + <trans-unit id="ee5c85f0dae011c10d9d2bfb1fcd86e529c55536" resname="fielddefinition.min-length.label"> + <source>Min string length:</source> + <target>Min string length:</target> + <note>key: fielddefinition.min-length.label</note> + </trans-unit> + <trans-unit id="09fff8d9a67512b01bb3e58b5d25cf5d042ba195" resname="fielddefinition.min-length.undefined"> + <source>No defined minimum string length</source> + <target>No defined minimum string length</target> + <note>key: fielddefinition.min-length.undefined</note> + </trans-unit> + <trans-unit id="904a83f949dd73a0bed006f8e78a211c80b4816a" resname="fielddefinition.min-length.value"> + <source>%min% characters</source> + <target>%min% characters</target> + <note>key: fielddefinition.min-length.value</note> + </trans-unit> + <trans-unit id="1feb12108139bb4d34877c40ade8362c0872b528" resname="fielddefinition.min-value.label"> + <source>Minimum value:</source> + <target>Minimum value:</target> + <note>key: fielddefinition.min-value.label</note> + </trans-unit> + <trans-unit id="625d3f00da19e0e839ad54eb5cc6b3504033b882" resname="fielddefinition.min-value.undefined"> + <source>No defined minimum value</source> + <target>No defined minimum value</target> + <note>key: fielddefinition.min-value.undefined</note> + </trans-unit> + <trans-unit id="694dc611591facca075a581721e399e272b758fe" resname="fielddefinition.multiple.label"> + <source>Allow multiple choices:</source> + <target>Allow multiple choices:</target> + <note>key: fielddefinition.multiple.label</note> + </trans-unit> + <trans-unit id="0b8ef090df7e5c7780364c51f060db3f5b8f076a" resname="fielddefinition.multiple.no"> + <source>No</source> + <target>No</target> + <note>key: fielddefinition.multiple.no</note> + </trans-unit> + <trans-unit id="ec31a6088a9e8cb3266bf0b99e057ca0677fcd82" resname="fielddefinition.multiple.yes"> + <source>Yes</source> + <target>Yes</target> + <note>key: fielddefinition.multiple.yes</note> + </trans-unit> + <trans-unit id="4d59776118124c81734aacab8a71198ee555479c" resname="fielddefinition.options.label"> + <source>Defined options</source> + <target>Defined options</target> + <note>key: fielddefinition.options.label</note> + </trans-unit> + <trans-unit id="030aa959f56ae9ba0e1d84a99606995a02c5bd97" resname="fielddefinition.preferred-rows-number.label"> + <source>Preferred number of rows:</source> + <target>Preferred number of rows:</target> + <note>key: fielddefinition.preferred-rows-number.label</note> + </trans-unit> + <trans-unit id="f552c76a800b38e41d8b16afa53d3d22d3b37692" resname="fielddefinition.preferred-rows-number.undefined"> + <source>No preferred number of rows</source> + <target>No preferred number of rows</target> + <note>key: fielddefinition.preferred-rows-number.undefined</note> + </trans-unit> + <trans-unit id="ac0aff5c558fb078621da0f46b2ae5b0e04afcaa" resname="fielddefinition.preferred-rows-number.value"> + <source>%rows% rows</source> + <target>%rows% rows</target> + <note>key: fielddefinition.preferred-rows-number.value</note> + </trans-unit> + <trans-unit id="b21527196a76acac750d478bfe8137cae7aff914" resname="fielddefinition.selection-method.browse"> + <source>Browse</source> + <target>Browse</target> + <note>key: fielddefinition.selection-method.browse</note> + </trans-unit> + <trans-unit id="02c78e1886bdf844e359ddd54b190eb718286557" resname="fielddefinition.selection-method.checkbox"> + <source>List with checkboxes</source> + <target>List with checkboxes</target> + <note>key: fielddefinition.selection-method.checkbox</note> + </trans-unit> + <trans-unit id="a60c81e556aa14c00b3b44ffb6e49f90b882c286" resname="fielddefinition.selection-method.label"> + <source>Selection method:</source> + <target>Selection method:</target> + <note>key: fielddefinition.selection-method.label</note> + </trans-unit> + <trans-unit id="cd94d6146477b427bdc6dbbe19be20bd6db31542" resname="fielddefinition.selection-method.list"> + <source>Drop-down list</source> + <target>Drop-down list</target> + <note>key: fielddefinition.selection-method.list</note> + </trans-unit> + <trans-unit id="7d8f219b8fc1dab2be8b6a82898b0e7a8920c928" resname="fielddefinition.selection-method.multi_template"> + <source>Template based, multi</source> + <target>Template based, multi</target> + <note>key: fielddefinition.selection-method.multi_template</note> + </trans-unit> + <trans-unit id="1095b3a335cbfeed0e396f081895affe81e7b7fd" resname="fielddefinition.selection-method.multiple_list"> + <source>Multiple selection list</source> + <target>Multiple selection list</target> + <note>key: fielddefinition.selection-method.multiple_list</note> + </trans-unit> + <trans-unit id="60f06115f038572328285e2ca1fc5ad811105c49" resname="fielddefinition.selection-method.radio"> + <source>List with radio buttons</source> + <target>List with radio buttons</target> + <note>key: fielddefinition.selection-method.radio</note> + </trans-unit> + <trans-unit id="37121f3c9e8054432510a9f85fd3235e5f549734" resname="fielddefinition.selection-method.single_template"> + <source>Template based, single</source> + <target>Template based, single</target> + <note>key: fielddefinition.selection-method.single_template</note> + </trans-unit> + <trans-unit id="e0b6bc6950269d05c7318553912abe4042d6a423" resname="fielddefinition.selection-method.tree"> + <source>Drop-down tree</source> + <target>Drop-down tree</target> + <note>key: fielddefinition.selection-method.tree</note> + </trans-unit> + <trans-unit id="ccf4d68283a388e6a965caf9e507c6a39fffc46f" resname="fielddefinition.selection-root.label"> + <source>Selection root:</source> + <target>Selection root:</target> + <note>key: fielddefinition.selection-root.label</note> + </trans-unit> + <trans-unit id="c7b711d3eb6c197d83e064f309bd2e9791a0950d" resname="fielddefinition.selection-root.undefined"> + <source>No defined root</source> + <target>No defined root</target> + <note>key: fielddefinition.selection-root.undefined</note> + </trans-unit> + <trans-unit id="4c9eb21a084528340d7944eeb0ffe525fc5e5e75" resname="fielddefinition.use-seconds.label"> + <source>Use seconds:</source> + <target>Use seconds:</target> + <note>key: fielddefinition.use-seconds.label</note> + </trans-unit> + <trans-unit id="8c785cf266419d05f444b0e88bbf34f07c0b6fde" resname="fielddefinition.use-seconds.no"> + <source>No</source> + <target>No</target> + <note>key: fielddefinition.use-seconds.no</note> + </trans-unit> + <trans-unit id="edc26cd0186a48016fc5bc286c4d64e238043658" resname="fielddefinition.use-seconds.yes"> + <source>Yes</source> + <target>Yes</target> + <note>key: fielddefinition.use-seconds.yes</note> + </trans-unit> + </body> + </file> +</xliff> diff --git a/src/bundle/Core/Resources/translations/ibexa_fieldtypes.en.xlf b/src/bundle/Core/Resources/translations/ibexa_fieldtypes.en.xlf new file mode 100644 index 0000000000..6731222065 --- /dev/null +++ b/src/bundle/Core/Resources/translations/ibexa_fieldtypes.en.xlf @@ -0,0 +1,126 @@ +<?xml version="1.0" encoding="utf-8"?> +<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2"> + <file source-language="en" target-language="en" datatype="plaintext" original="not.available"> + <header> + <tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> + <note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note> + </header> + <body> + <trans-unit id="06a34ac23ac86f7c4368380203e3dd4d91489fa0" resname="ezauthor.name"> + <source>Authors</source> + <target>Authors</target> + <note>key: ezauthor.name</note> + </trans-unit> + <trans-unit id="e4ca4a21a1e442e6a1232320b2184b9e4105c73f" resname="ezbinaryfile.name"> + <source>File</source> + <target>File</target> + <note>key: ezbinaryfile.name</note> + </trans-unit> + <trans-unit id="129dd46eabe280c49a0e9d424d9cbf676621aec9" resname="ezboolean.name"> + <source>Checkbox</source> + <target>Checkbox</target> + <note>key: ezboolean.name</note> + </trans-unit> + <trans-unit id="c4c3e3c536a09969467c7c9bb584459b47317fed" resname="ezcountry.name"> + <source>Country</source> + <target>Country</target> + <note>key: ezcountry.name</note> + </trans-unit> + <trans-unit id="3bd547a05c5a7b5ee111cc5ca608df9199f04c51" resname="ezdate.name"> + <source>Date</source> + <target>Date</target> + <note>key: ezdate.name</note> + </trans-unit> + <trans-unit id="42d9e26b6e8e65856f709461b589cb7925a9f43e" resname="ezdatetime.name"> + <source>Date and time</source> + <target>Date and time</target> + <note>key: ezdatetime.name</note> + </trans-unit> + <trans-unit id="d772ac8909929964906cbefd41c468c9a5d2be26" resname="ezemail.name"> + <source>Email address</source> + <target>Email address</target> + <note>key: ezemail.name</note> + </trans-unit> + <trans-unit id="0a1f3297ccc08882f5a4610f9af8fc4148c74adc" resname="ezfloat.name"> + <source>Float</source> + <target>Float</target> + <note>key: ezfloat.name</note> + </trans-unit> + <trans-unit id="fcabb29fdb097d916c88328daad461c1ea765121" resname="ezgmaplocation.name"> + <source>Map location</source> + <target>Map location</target> + <note>key: ezgmaplocation.name</note> + </trans-unit> + <trans-unit id="6300f890f00d9999466b4abc478857b0d29022b4" resname="ezimage.name"> + <source>Image</source> + <target>Image</target> + <note>key: ezimage.name</note> + </trans-unit> + <trans-unit id="3afdca17c734ecb3a077bcc4ba7f75a7544e845d" resname="ezimageasset.name"> + <source>Image Asset</source> + <target>Image Asset</target> + <note>key: ezimageasset.name</note> + </trans-unit> + <trans-unit id="3b0f44cd332c71e78209a1a4b235bc6a1e6d374b" resname="ezinteger.name"> + <source>Integer</source> + <target>Integer</target> + <note>key: ezinteger.name</note> + </trans-unit> + <trans-unit id="b55e4568224820cbddc794f6c2bd002baec6a5d0" resname="ezisbn.name"> + <source>ISBN</source> + <target>ISBN</target> + <note>key: ezisbn.name</note> + </trans-unit> + <trans-unit id="c564457bc5723491788fa3d0723e3716824b4a0d" resname="ezkeyword.name"> + <source>Keywords</source> + <target>Keywords</target> + <note>key: ezkeyword.name</note> + </trans-unit> + <trans-unit id="fc0d2c8719e7c36f101ff6ddcbdfc804bf8f3da7" resname="ezmedia.name"> + <source>Media</source> + <target>Media</target> + <note>key: ezmedia.name</note> + </trans-unit> + <trans-unit id="feb25b24199466f077038c5e6e38209deae21471" resname="ezobjectrelation.name"> + <source>Content relation (single)</source> + <target>Content relation (single)</target> + <note>key: ezobjectrelation.name</note> + </trans-unit> + <trans-unit id="44d2ac9ae83f6b73fa8f61fad66a77d76d2159f0" resname="ezobjectrelationlist.name"> + <source>Content relations (multiple)</source> + <target>Content relations (multiple)</target> + <note>key: ezobjectrelationlist.name</note> + </trans-unit> + <trans-unit id="3229c3fc41a4af84f0fd1636f0678aac770359bc" resname="ezselection.name"> + <source>Selection</source> + <target>Selection</target> + <note>key: ezselection.name</note> + </trans-unit> + <trans-unit id="d958019d9702d5e89f247c24563da97ea0205f58" resname="ezstring.name"> + <source>Text line</source> + <target>Text line</target> + <note>key: ezstring.name</note> + </trans-unit> + <trans-unit id="e35280b88546fdd18c84ce9c401e17cbfca56e72" resname="eztext.name"> + <source>Text block</source> + <target>Text block</target> + <note>key: eztext.name</note> + </trans-unit> + <trans-unit id="503c2801427b2d773d020ef953413d14c17968bd" resname="eztime.name"> + <source>Time</source> + <target>Time</target> + <note>key: eztime.name</note> + </trans-unit> + <trans-unit id="69c0e446226061b7f2ca2b22fd4eb09e6b4e9518" resname="ezurl.name"> + <source>URL</source> + <target>URL</target> + <note>key: ezurl.name</note> + </trans-unit> + <trans-unit id="0317c233c19b3c191acc92971bfb616c7fe298de" resname="ezuser.name"> + <source>User account</source> + <target>User account</target> + <note>key: ezuser.name</note> + </trans-unit> + </body> + </file> +</xliff> diff --git a/src/bundle/Core/Resources/translations/ibexa_repository_exceptions.en.xlf b/src/bundle/Core/Resources/translations/ibexa_repository_exceptions.en.xlf new file mode 100644 index 0000000000..d30ee0dec4 --- /dev/null +++ b/src/bundle/Core/Resources/translations/ibexa_repository_exceptions.en.xlf @@ -0,0 +1,146 @@ +<?xml version="1.0" encoding="utf-8"?> +<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2"> + <file source-language="en" target-language="en" datatype="plaintext" original="not.available"> + <header> + <tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> + <note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note> + </header> + <body> + <trans-unit id="bdaae3444afba4eebebe2e0f684a0e56e81afafd" resname="'%actualValue%' is incorrect value"> + <source>'%actualValue%' is incorrect value</source> + <target>'%actualValue%' is incorrect value</target> + <note>key: '%actualValue%' is incorrect value</note> + </trans-unit> + <trans-unit id="4769b7caf9489fa7d54ca298414c0f10a5f43a56" resname="'%actualValue%' is incorrect value in class '%className%'"> + <source>'%actualValue%' is incorrect value in class '%className%'</source> + <target>'%actualValue%' is incorrect value in class '%className%'</target> + <note>key: '%actualValue%' is incorrect value in class '%className%'</note> + </trans-unit> + <trans-unit id="80e5a0a47ac1df407b0218c2283872a86aba0fc1" resname="Argument '%argumentName%' has a bad state: %whatIsWrong%"> + <source>Argument '%argumentName%' has a bad state: %whatIsWrong%</source> + <target>Argument '%argumentName%' has a bad state: %whatIsWrong%</target> + <note>key: Argument '%argumentName%' has a bad state: %whatIsWrong%</note> + </trans-unit> + <trans-unit id="ec22a1547bcd0b3131346b94db7783d8ba4fc12f" resname="Argument '%argumentName%' is invalid: %whatIsWrong%"> + <source>Argument '%argumentName%' is invalid: %whatIsWrong%</source> + <target>Argument '%argumentName%' is invalid: %whatIsWrong%</target> + <note>key: Argument '%argumentName%' is invalid: %whatIsWrong%</note> + </trans-unit> + <trans-unit id="ddc2eb42746874988689fbc618bce5487f245b5c" resname="Argument '%argumentName%' is invalid: value must be of type '%expectedType%'"> + <source>Argument '%argumentName%' is invalid: value must be of type '%expectedType%'</source> + <target>Argument '%argumentName%' is invalid: value must be of type '%expectedType%'</target> + <note>key: Argument '%argumentName%' is invalid: value must be of type '%expectedType%'</note> + </trans-unit> + <trans-unit id="ea737c09fcf59eb54ff1a1caca4242bdb2afda03" resname="Argument '%argumentName%' is invalid: value must be of type '%expectedType%', not '%actualType%'"> + <source>Argument '%argumentName%' is invalid: value must be of type '%expectedType%', not '%actualType%'</source> + <target>Argument '%argumentName%' is invalid: value must be of type '%expectedType%', not '%actualType%'</target> + <note>key: Argument '%argumentName%' is invalid: value must be of type '%expectedType%', not '%actualType%'</note> + </trans-unit> + <trans-unit id="6a5a5e4df0f09cc7587d11571aa90c31e68c15c6" resname="Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, binary file ids can not begin with a '/'"> + <source>Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, binary file ids can not begin with a '/'</source> + <target>Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, binary file ids can not begin with a '/'</target> + <note>key: Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, binary file ids can not begin with a '/'</note> + </trans-unit> + <trans-unit id="0afe736d44da00b87ed3fe777775661ddf10a455" resname="Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, it does not contain prefix '%prefix%'. Is 'var_dir' config correct?"> + <source>Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, it does not contain prefix '%prefix%'. Is 'var_dir' config correct?</source> + <target>Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, it does not contain prefix '%prefix%'. Is 'var_dir' config correct?</target> + <note>key: Argument 'BinaryFile::id' is invalid: '%id%' is wrong value, it does not contain prefix '%prefix%'. Is 'var_dir' config correct?</note> + </trans-unit> + <trans-unit id="e88ed78511f95299a41927e3ca7336fed174046b" resname="Content "%contentName%" fields did not validate: %errors%"> + <source>Content "%contentName%" fields did not validate: %errors%</source> + <target>Content "%contentName%" fields did not validate: %errors%</target> + <note>key: Content "%contentName%" fields did not validate: %errors%</note> + </trans-unit> + <trans-unit id="8967aa151e1b84f60c505163d42350eb6a96e948" resname="Content fields did not validate"> + <source>Content fields did not validate</source> + <target>Content fields did not validate</target> + <note>key: Content fields did not validate</note> + </trans-unit> + <trans-unit id="8f64e46d8c92143a1a8fde9790a6a2423759a0ce" resname="Content type field definitions did not validate"> + <source>Content type field definitions did not validate</source> + <target>Content type field definitions did not validate</target> + <note>key: Content type field definitions did not validate</note> + </trans-unit> + <trans-unit id="dbf0b883e33a6e7c433ffa9a536fd214cda39b5c" resname="Could not find %classType% class '%className%'"> + <source>Could not find %classType% class '%className%'</source> + <target>Could not find %classType% class '%className%'</target> + <note>key: Could not find %classType% class '%className%'</note> + </trans-unit> + <trans-unit id="ff3e1dacaa764a0e94a86b173f9c93509fcaadeb" resname="Could not find '%what%' with identifier '%identifier%'"> + <source>Could not find '%what%' with identifier '%identifier%'</source> + <target>Could not find '%what%' with identifier '%identifier%'</target> + <note>key: Could not find '%what%' with identifier '%identifier%'</note> + </trans-unit> + <trans-unit id="2b84fad03f5a98d0fa25c78b1d3717ba108835e6" resname="Could not find class '%className%'"> + <source>Could not find class '%className%'</source> + <target>Could not find class '%className%'</target> + <note>key: Could not find class '%className%'</note> + </trans-unit> + <trans-unit id="c6136a74d1562ea6d15321ee75b9b307fc787e79" resname="Field Type '%fieldType%' not found. It must be implemented or configured to use %nullType%"> + <source>Field Type '%fieldType%' not found. It must be implemented or configured to use %nullType%</source> + <target>Field Type '%fieldType%' not found. It must be implemented or configured to use %nullType%</target> + <note>key: Field Type '%fieldType%' not found. It must be implemented or configured to use %nullType%</note> + </trans-unit> + <trans-unit id="5dc8e460be7495d772cdb7bc4d4bd2913e480273" resname="Field definition '%identifier%' does not exist in given content type"> + <source>Field definition '%identifier%' does not exist in given content type</source> + <target>Field definition '%identifier%' does not exist in given content type</target> + <note>key: Field definition '%identifier%' does not exist in given content type</note> + </trans-unit> + <trans-unit id="1469ef88c3de1616907207891c1611a07459534a" resname="Field definition '%identifier%' does not exist in the given content type"> + <source>Field definition '%identifier%' does not exist in the given content type</source> + <target>Field definition '%identifier%' does not exist in the given content type</target> + <note>key: Field definition '%identifier%' does not exist in the given content type</note> + </trans-unit> + <trans-unit id="c6f0fd742d6b8ce0686dc4e7a3a070efbf924204" resname="Invalid URL wildcards provided."> + <source>Invalid URL wildcards provided.</source> + <target>Invalid URL wildcards provided.</target> + <note>key: Invalid URL wildcards provided.</note> + </trans-unit> + <trans-unit id="87d1caa3303a7197201d77b1bcb03ae38356097b" resname="Limitation '%limitation%' not found. It must be implemented or configured to use %blockingLimitation%"> + <source>Limitation '%limitation%' not found. It must be implemented or configured to use %blockingLimitation%</source> + <target>Limitation '%limitation%' not found. It must be implemented or configured to use %blockingLimitation%</target> + <note>key: Limitation '%limitation%' not found. It must be implemented or configured to use %blockingLimitation%</note> + </trans-unit> + <trans-unit id="71fd5f8839faaadfe7c61def7e4b42d429959952" resname="Limitations did not validate"> + <source>Limitations did not validate</source> + <target>Limitations did not validate</target> + <note>key: Limitations did not validate</note> + </trans-unit> + <trans-unit id="ef12e750e94380df0d1de86b1cc274b45dc94e94" resname="Path '%path%' already exists for the given context"> + <source>Path '%path%' already exists for the given context</source> + <target>Path '%path%' already exists for the given context</target> + <note>key: Path '%path%' already exists for the given context</note> + </trans-unit> + <trans-unit id="300b5656b51de352e89aa5d7dd4ec6abb173bf6f" resname="Placeholders do not match the wildcards."> + <source>Placeholders do not match the wildcards.</source> + <target>Placeholders do not match the wildcards.</target> + <note>key: Placeholders do not match the wildcards.</note> + </trans-unit> + <trans-unit id="1677f5311a0f3fc35be40d540553e892d187618e" resname="The User does not have the '%function%' '%module%' permission"> + <source>The User does not have the '%function%' '%module%' permission</source> + <target>The User does not have the '%function%' '%module%' permission</target> + <note>key: The User does not have the '%function%' '%module%' permission</note> + </trans-unit> + <trans-unit id="250a16261172f0d2985c8bec3a2b06280f7c863b" resname="The User does not have the '%function%' '%module%' permission with: %with%"> + <source>The User does not have the '%function%' '%module%' permission with: %with%</source> + <target>The User does not have the '%function%' '%module%' permission with: %with%</target> + <note>key: The User does not have the '%function%' '%module%' permission with: %with%</note> + </trans-unit> + <trans-unit id="3b2e044f9c9821aef27834972d90f59f28b1a0b4" resname="Token '%tokenType%:%token%' expired on '%when%'"> + <source>Token '%tokenType%:%token%' expired on '%when%'</source> + <target>Token '%tokenType%:%token%' expired on '%when%'</target> + <note>key: Token '%tokenType%:%token%' expired on '%when%'</note> + </trans-unit> + <trans-unit id="27a77deaffe8551b169928675adc64ae8450937f" resname="User "%login%" already exists"> + <source>User "%login%" already exists</source> + <target>User "%login%" already exists</target> + <note>key: User "%login%" already exists</note> + </trans-unit> + <trans-unit id="e37bafa4a8807f7a425f8d967cadc8e704c629b3" resname="You cannot set a value for the non-translatable Field definition '%identifier%' in language '%languageCode%'"> + <source>You cannot set a value for the non-translatable Field definition '%identifier%' in language '%languageCode%'</source> + <target>You cannot set a value for the non-translatable Field definition '%identifier%' in language '%languageCode%'</target> + <note>key: You cannot set a value for the non-translatable Field definition '%identifier%' in language '%languageCode%'</note> + </trans-unit> + </body> + </file> +</xliff> diff --git a/src/bundle/Core/Resources/translations/messages.en.xlf b/src/bundle/Core/Resources/translations/messages.en.xlf new file mode 100644 index 0000000000..2dfd2528cd --- /dev/null +++ b/src/bundle/Core/Resources/translations/messages.en.xlf @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2"> + <file source-language="en" target-language="en" datatype="plaintext" original="not.available"> + <header> + <tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/> + <note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note> + </header> + <body> + <trans-unit id="e2b8dff16cf1b8ad7848630c4d7c42cc31ac5e21" resname="Enter login or email"> + <source>Enter login or email</source> + <target>Enter login or email</target> + <note>key: Enter login or email</note> + </trans-unit> + <trans-unit id="570591c060a999e9ab1592763c74d881b7654ee1" resname="Enter password"> + <source>Enter password</source> + <target>Enter password</target> + <note>key: Enter password</note> + </trans-unit> + <trans-unit id="4e5a2893bdcc7d239c1db72e4c4ffbe4bea73174" resname="Login"> + <source>Login</source> + <target>Login</target> + <note>key: Login</note> + </trans-unit> + <trans-unit id="be81ab98cb9ccf753975f50fa70cd32581a821fc" resname="Password:"> + <source>Password:</source> + <target>Password:</target> + <note>key: Password:</note> + </trans-unit> + <trans-unit id="17dfb031e086e3415d71f3928e6ce08d6b1c1d4d" resname="Username:"> + <source>Username:</source> + <target>Username:</target> + <note>key: Username:</note> + </trans-unit> + </body> + </file> +</xliff> diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/translations/validators.en.xlf b/src/bundle/Core/Resources/translations/validators.en.xlf similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Resources/translations/validators.en.xlf rename to src/bundle/Core/Resources/translations/validators.en.xlf diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig b/src/bundle/Core/Resources/views/Security/login.html.twig similarity index 93% rename from eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig rename to src/bundle/Core/Resources/views/Security/login.html.twig index 8111f4ec6f..4790763f51 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/views/Security/login.html.twig +++ b/src/bundle/Core/Resources/views/Security/login.html.twig @@ -29,7 +29,7 @@ is redirected to on success (more details below) <input type="hidden" name="_target_path" value="/account" /> #} - <button type="submit" class="btn btn-primary">{{ 'Login'|trans }}</button> + <button type="submit" class="btn ibexa-btn ibexa-btn--primary">{{ 'Login'|trans }}</button> </fieldset> {% endblock %} </form> diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/content_fields.html.twig b/src/bundle/Core/Resources/views/content_fields.html.twig similarity index 90% rename from eZ/Bundle/EzPublishCoreBundle/Resources/views/content_fields.html.twig rename to src/bundle/Core/Resources/views/content_fields.html.twig index b70317aad4..19626c2303 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Resources/views/content_fields.html.twig +++ b/src/bundle/Core/Resources/views/content_fields.html.twig @@ -1,16 +1,16 @@ {# Template blocks to be used by content fields #} {# Block naming convention is <fieldDefinitionIdentifier>_field #} {# Following variables are passed: - # - \eZ\Publish\API\Repository\Values\Content\Field field the field to display - # - \eZ\Publish\API\Repository\Values\Content\ContentInfo contentInfo the contentInfo to which the field belongs to - # - \eZ\Publish\API\Repository\Values\Content\VersionInfo versionInfo the versionInfo to which the field belongs to + # - \Ibexa\Contracts\Core\Repository\Values\Content\Field field the field to display + # - \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo contentInfo the contentInfo to which the field belongs to + # - \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo versionInfo the versionInfo to which the field belongs to # - mixed fieldSettings settings of the field (depends on the fieldtype) # - array parameters options passed to ez_render_field under the parameters key # - array attr the attributes to add the generate HTML, contains at least a "class" entry # containing <fieldtypeidentifier>-field #} -{% trans_default_domain "content_fields" %} +{% trans_default_domain 'ibexa_content_fields' %} {% block ezstring_field %} {% apply spaceless %} @@ -66,7 +66,7 @@ {% block ezdatetime_field %} {% apply spaceless %} - {% if not ez_field_is_empty( content, field ) %} + {% if not ibexa_field_is_empty( content, field ) %} {% if fieldSettings.useSeconds %} {% set field_value = field.value.value|format_datetime( 'short', 'medium', locale=parameters.locale ) %} {% else %} @@ -79,7 +79,7 @@ {% block ezdate_field %} {% apply spaceless %} - {% if not ez_field_is_empty( content, field ) %} + {% if not ibexa_field_is_empty( content, field ) %} {% set field_value = field.value.date|format_date( 'short', locale=parameters.locale, timezone='UTC' ) %} {{ block( 'simple_block_field' ) }} {% endif %} @@ -88,7 +88,7 @@ {% block eztime_field %} {% apply spaceless %} - {% if not ez_field_is_empty( content, field ) %} + {% if not ibexa_field_is_empty( content, field ) %} {% if fieldSettings.useSeconds %} {% set field_value = field.value.time|format_time( 'medium', locale=parameters.locale, timezone='UTC' ) %} {% else %} @@ -101,7 +101,7 @@ {% block ezemail_field %} {% apply spaceless %} - {% if not ez_field_is_empty( content, field ) %} + {% if not ibexa_field_is_empty( content, field ) %} {% set field_value = field.value.email %} <a href="mailto:{{ field.value.email|escape( 'url' ) }}" {{ block( 'field_attributes' ) }}>{{ field.value.email }}</a> {% endif %} @@ -110,7 +110,7 @@ {% block ezinteger_field %} {% apply spaceless %} - {% if not ez_field_is_empty( content, field ) %} + {% if not ibexa_field_is_empty( content, field ) %} {% set field_value = field.value.value %} {{ block( 'simple_inline_field' ) }} {% endif %} @@ -120,7 +120,7 @@ {# @todo: handle localization #} {% block ezfloat_field %} {% apply spaceless %} - {% if not ez_field_is_empty( content, field ) %} + {% if not ibexa_field_is_empty( content, field ) %} {% set field_value = field.value.value %} {{ block( 'simple_inline_field' ) }} {% endif %} @@ -129,7 +129,7 @@ {% block ezurl_field %} {% apply spaceless %} - {% if not ez_field_is_empty( content, field ) %} + {% if not ibexa_field_is_empty( content, field ) %} <a href="{{ field.value.link }}" {{ block( 'field_attributes' ) }}>{{ field.value.text ? field.value.text : field.value.link }}</a> {% endif %} @@ -145,7 +145,7 @@ {% block ezkeyword_field %} {% apply spaceless %} - {% if not ez_field_is_empty( content, field ) %} + {% if not ibexa_field_is_empty( content, field ) %} <ul {{ block( 'field_attributes' ) }}> {% for keyword in field.value.values %} <li>{{ keyword }}</li> @@ -201,30 +201,30 @@ {% block ezbinaryfile_field %} {% apply spaceless %} - {% if not ez_field_is_empty( content, field ) %} - {% set route_reference = ez_route( 'ez_content_download', { + {% if not ibexa_field_is_empty( content, field ) %} + {% set route_reference = ibexa_route( 'ibexa.content.download', { 'content': content, 'fieldIdentifier': field.fieldDefIdentifier, 'inLanguage': content.prioritizedFieldLanguageCode, 'version': versionInfo.versionNo } ) %} - <a href="{{ ez_path( route_reference ) }}" - {{ block( 'field_attributes' ) }}>{{ field.value.fileName }}</a> ({{ field.value.fileSize|ez_file_size( 1 ) }}) + <a href="{{ ibexa_path( route_reference ) }}" + {{ block( 'field_attributes' ) }}>{{ field.value.fileName }}</a> ({{ field.value.fileSize|ibexa_file_size( 1 ) }}) {% endif %} {% endapply %} {% endblock %} {% block ezmedia_field %} -{% if not ez_field_is_empty( content, field ) %} +{% if not ibexa_field_is_empty( content, field ) %} {% apply spaceless %} {% set type = fieldSettings.mediaType %} {% set value = field.value %} - {% set route_reference = ez_route( 'ez_content_download', { + {% set route_reference = ibexa_route( 'ibexa.content.download', { 'content': content, 'fieldIdentifier': field.fieldDefIdentifier, 'version': versionInfo.versionNo } ) %} - {% set download = ez_path( route_reference ) %} + {% set download = ibexa_path( route_reference ) %} {% set width = value.width > 0 ? 'width="' ~ value.width ~ '"' : "" %} {% set height = value.height > 0 ? 'height="' ~ value.height ~ '"' : "" %} <div {{ block( 'field_attributes' ) }}> @@ -277,12 +277,12 @@ {% block ezobjectrelationlist_field %} {% apply spaceless %} - {% if not ez_field_is_empty( content, field ) %} + {% if not ibexa_field_is_empty( content, field ) %} <ul {{ block( 'field_attributes' ) }}> {% for contentId in field.value.destinationContentIds %} {% if parameters.available[contentId] %} <li> - {{ render( controller( "ez_content:viewAction", {'contentId': contentId, 'viewType': 'embed', 'layout': false} ) ) }} + {{ render( controller( "ibexa_content::viewAction", {'contentId': contentId, 'viewType': 'embed', 'layout': false} ) ) }} </li>{% endif %} {% endfor %} </ul> @@ -431,9 +431,9 @@ #} {% block ezimage_field %} {% apply spaceless %} -{% if not ez_field_is_empty( content, field ) %} +{% if not ibexa_field_is_empty( content, field ) %} <figure {{ block( 'field_attributes' ) }}> - {% set imageAlias = ez_image_alias( field, versionInfo, parameters.alias|default( 'original' ) ) %} + {% set imageAlias = ibexa_image_alias( field, versionInfo, parameters.alias|default( 'original' ) ) %} {% set src = imageAlias ? asset( imageAlias.uri ) : "//:0" %} {% set attrs = { class: parameters.class|default(''), @@ -458,9 +458,9 @@ {% block ezimageasset_field %} {% apply spaceless %} - {% if not ez_field_is_empty(content, field) and parameters.available %} + {% if not ibexa_field_is_empty(content, field) and parameters.available %} <div {{ block('field_attributes') }}> - {{ render(controller('ez_content:embedAction', { + {{ render(controller('ibexa_content:embedAction', { contentId: field.value.destinationContentId, viewType: 'asset_image', no_layout: true, @@ -475,9 +475,9 @@ {% block ezobjectrelation_field %} {% apply spaceless %} -{% if not ez_field_is_empty( content, field ) and parameters.available %} +{% if not ibexa_field_is_empty( content, field ) and parameters.available %} <div {{ block( 'field_attributes' ) }}> - {{ render( controller( "ez_content:viewAction", {'contentId': field.value.destinationContentId, 'viewType': 'text_linked', 'layout': false} ) ) }} + {{ render( controller( "ibexa_content::viewAction", {'contentId': field.value.destinationContentId, 'viewType': 'text_linked', 'layout': false} ) ) }} </div> {% endif %} {% endapply %} diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/default/block/block.html.twig b/src/bundle/Core/Resources/views/default/block/block.html.twig similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Resources/views/default/block/block.html.twig rename to src/bundle/Core/Resources/views/default/block/block.html.twig diff --git a/src/bundle/Core/Resources/views/default/content/asset_image.html.twig b/src/bundle/Core/Resources/views/default/content/asset_image.html.twig new file mode 100644 index 0000000000..f8b0e10f59 --- /dev/null +++ b/src/bundle/Core/Resources/views/default/content/asset_image.html.twig @@ -0,0 +1,8 @@ +{% set image_field_identifier = ibexa_content_field_identifier_image_asset() %} + +{% if image_field_identifier is not null %} + {{ ibexa_render_field(content, image_field_identifier, { + template: '@IbexaCore/content_fields.html.twig', + parameters: parameters + }) }} +{% endif %} diff --git a/src/bundle/Core/Resources/views/default/content/embed.html.twig b/src/bundle/Core/Resources/views/default/content/embed.html.twig new file mode 100644 index 0000000000..b94d19afba --- /dev/null +++ b/src/bundle/Core/Resources/views/default/content/embed.html.twig @@ -0,0 +1,13 @@ +{% set content_name=ibexa_content_name(content) %} + +{% if objectParameters.doNotGenerateEmbedUrl is defined and objectParameters.doNotGenerateEmbedUrl %} + <h3>{{ content.name }}</h3> +{% else %} + {% if location is defined %} + <h3><a href="{{ ibexa_path(location) }}">{{ content_name }}</a></h3> + {% elseif content.contentInfo.mainLocationId is not null %} + <h3><a href="{{ ibexa_path(content) }}">{{ content_name }}</a></h3> + {% else %} + <h3>{{ content_name }}</h3> + {% endif %} +{% endif %} diff --git a/src/bundle/Core/Resources/views/default/content/embed_image.html.twig b/src/bundle/Core/Resources/views/default/content/embed_image.html.twig new file mode 100644 index 0000000000..0750710faa --- /dev/null +++ b/src/bundle/Core/Resources/views/default/content/embed_image.html.twig @@ -0,0 +1,16 @@ +{% set image_field_identifier = ibexa_content_field_identifier_first_filled_image(content) %} + +{% if image_field_identifier is not null %} + {{ ibexa_render_field( + content, + image_field_identifier, + { + template: '@IbexaCore/content_fields.html.twig', + parameters: + { + alias: objectParameters.size|default('original'), + ezlink: linkParameters|default({}) + } + } + ) }} +{% endif %} diff --git a/src/bundle/Core/Resources/views/default/content/embed_inline.html.twig b/src/bundle/Core/Resources/views/default/content/embed_inline.html.twig new file mode 100644 index 0000000000..e3491930f8 --- /dev/null +++ b/src/bundle/Core/Resources/views/default/content/embed_inline.html.twig @@ -0,0 +1,15 @@ +{% + set data_attributes_str = (data_attributes is defined) + ? ' ' ~ data_attributes|ibexa_data_attributes_serialize + : '' +%} + +{% if objectParameters.doNotGenerateEmbedUrl is defined and objectParameters.doNotGenerateEmbedUrl %} + <span {% if class is defined %} class="{{ class }}"{% endif %}{{ data_attributes_str|raw }}>{{ content.name }}</span> +{% else %} + {% if location is defined %} + <a href="{{ ibexa_path(location) }}"{% if class is defined %} class="{{ class }}"{% endif %}{{ data_attributes_str|raw }}>{{ content.name }}</a> + {% else %} + <a href="{{ ibexa_path(content) }}"{% if class is defined %} class="{{ class }}"{% endif %}{{ data_attributes_str|raw }}>{{ content.name }}</a> + {% endif %} +{% endif %} diff --git a/src/bundle/Core/Resources/views/default/content/full.html.twig b/src/bundle/Core/Resources/views/default/content/full.html.twig new file mode 100644 index 0000000000..4e727c230e --- /dev/null +++ b/src/bundle/Core/Resources/views/default/content/full.html.twig @@ -0,0 +1,8 @@ +{% extends no_layout == true ? view_base_layout : page_layout %} +{% block content %} + <h2>{{ ibexa_content_name(content) }}</h2> + {% for field in content.fieldsByLanguage(language|default(null)) %} + <h3>{{ field.fieldDefIdentifier }}</h3> + {{ ibexa_render_field(content, field.fieldDefIdentifier, {location: location|default(null)}) }} + {% endfor %} +{% endblock %} diff --git a/src/bundle/Core/Resources/views/default/content/line.html.twig b/src/bundle/Core/Resources/views/default/content/line.html.twig new file mode 100644 index 0000000000..164b565038 --- /dev/null +++ b/src/bundle/Core/Resources/views/default/content/line.html.twig @@ -0,0 +1,7 @@ +{% set content_name=ibexa_content_name(content) %} + +{% if location is defined %} + <p><a href="{{ ibexa_path(location) }}">{{ content_name }}</a></p> +{% else %} + <p>{{ content_name }}</p> +{% endif %} diff --git a/src/bundle/Core/Resources/views/default/content/text_linked.html.twig b/src/bundle/Core/Resources/views/default/content/text_linked.html.twig new file mode 100644 index 0000000000..c1ca27800a --- /dev/null +++ b/src/bundle/Core/Resources/views/default/content/text_linked.html.twig @@ -0,0 +1,7 @@ +{% set content_name=ibexa_content_name(content) %} + +{% if location is defined %} + <a href="{{ ibexa_path(location) }}">{{ content_name }}</a> +{% else %} + <p>{{ content_name }}</p> +{% endif %} diff --git a/src/bundle/Core/Resources/views/fielddefinition_settings.html.twig b/src/bundle/Core/Resources/views/fielddefinition_settings.html.twig new file mode 100644 index 0000000000..1805bc25a4 --- /dev/null +++ b/src/bundle/Core/Resources/views/fielddefinition_settings.html.twig @@ -0,0 +1,402 @@ +{# Template blocks used to render the settings of each field definition #} +{# Block naming convention is <fieldTypeIdentifier>_settings> #} +{# The following variables are available in each block: + # - \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition fielddefinition the field definition + # - array settings settings of the field definition + #} + +{% trans_default_domain 'ibexa_fielddefinition' %} + +{% block ezstring_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + {% set defaultValue = null %} + {% if fielddefinition.defaultValue.text is not same as('') %} + {% set defaultValue = fielddefinition.defaultValue.text %} + {% endif %} + {{ block( 'settings_defaultvalue' ) }} + <li class="ibexa-fielddefinition-setting min-length"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.min-length.label'|trans|desc("Min string length:")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + {% if fielddefinition.validatorConfiguration.StringLengthValidator.minStringLength %} + {{ 'fielddefinition.min-length.value'|trans({'%min%': fielddefinition.validatorConfiguration.StringLengthValidator.minStringLength})|desc("%min% characters")}} + {% else %} + <em>{{ 'fielddefinition.min-length.undefined'|trans|desc("No defined minimum string length")}}</em> + {% endif %} + </div> + </li> + <li class="ibexa-fielddefinition-setting max-length"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.max-length.label'|trans|desc("Max string length:")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + {% if fielddefinition.validatorConfiguration.StringLengthValidator.maxStringLength %} + {{ 'fielddefinition.max-length.value'|trans({'%max%': fielddefinition.validatorConfiguration.StringLengthValidator.maxStringLength})|desc("%max% characters")}} + {% else %} + <em>{{ 'fielddefinition.max-length.undefined'|trans|desc("No defined maximum string length")}}</em> + {% endif %} + </div> + </li> +</ul> +{% endblock %} + +{% block eztext_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + {% set rows = settings.textRows %} + {{ block( 'settings_preferredrows' ) }} +</ul> +{% endblock %} + +{% block ezcountry_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + {% set defaultValue = null %} + {% if fielddefinition.defaultValue.countries %} + {% for country in fielddefinition.defaultValue.countries %} + {% set defaultValue = defaultValue ~ country.Name ~ ( not loop.last ? ', ' : '' ) %} + {% endfor %} + {% endif %} + {{ block( 'settings_defaultvalue' ) }} + {% set isMultiple = settings.isMultiple %} + {{ block( 'settings_allowmultiple' ) }} +</ul> +{% endblock %} + +{% block ezboolean_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + <li class="ibexa-fielddefinition-setting default-value"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.default-value.label'|trans|desc("Default value:")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + {% if fielddefinition.defaultValue.bool %} + {{ 'fielddefinition.default-value.checked'|trans|desc("Checked")}} + {% else %} + {{ 'fielddefinition.default-value.unchecked'|trans|desc("Unchecked")}} + {% endif %} + </div> + </li> +</ul> +{% endblock %} + +{% block ezdatetime_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + {% if settings.defaultType == constant( 'Ibexa\\Core\\Core\\FieldType\\DateAndTime\\Type::DEFAULT_EMPTY' ) %} + {% set defaultValue = 'fielddefinition.default-value.empty'|trans|desc("Empty") %} + {% elseif settings.defaultType == constant( 'Ibexa\\Core\\Core\\FieldType\\DateAndTime\\Type::DEFAULT_CURRENT_DATE' ) %} + {% set defaultValue = 'fielddefinition.default-value.current_datetime'|trans|desc("Current datetime") %} + {% else %} + {% set interval = settings.dateInterval %} + {% set defaultValue = 'fielddefinition.default-value.current_datetime_adjust_by'|trans|desc("Current datetime adjusted by") %} + {% set defaultValue = interval.y ? 'fielddefinition.interval.year'|trans({'%default%': defaultValue, '%year%': interval.y})|desc("%default% %year% year(s)") : defaultValue %} + {% set defaultValue = interval.m ? 'fielddefinition.interval.month'|trans({'%default%': defaultValue, '%month%': interval.m})|desc("%default% %month% month(s)") : defaultValue %} + {% set defaultValue = interval.d ? 'fielddefinition.interval.day'|trans({'%default%': defaultValue, '%day%': interval.d})|desc("%default% %day% day(s)") : defaultValue %} + {% set defaultValue = interval.h ? 'fielddefinition.interval.hour'|trans({'%default%': defaultValue, '%hour%': interval.h})|desc("%default% %hour% hour(s)") : defaultValue %} + {% set defaultValue = interval.i ? 'fielddefinition.interval.minute'|trans({'%default%': defaultValue, '%minute%': interval.i})|desc("%default% %minute% minute(s)") : defaultValue %} + {% set defaultValue = interval.s and settings.useSeconds ? 'fielddefinition.interval.second'|trans({'%default%': defaultValue, '%second%': interval.s})|desc("%default% %second% second(s)") : defaultValue %} + {% endif %} + {{ block( 'settings_defaultvalue' ) }} + <li class="ibexa-fielddefinition-setting use-seconds"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.use-seconds.label'|trans|desc("Use seconds:")}}</div> + <div class="ibexa-fielddefinition-setting-value">{{ settings.useSeconds ? 'fielddefinition.use-seconds.yes'|trans|desc("Yes") : 'fielddefinition.use-seconds.no'|trans|desc("No") }}</div> + </li> +</ul> +{% endblock %} + +{% block ezdate_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + {% if settings.defaultType == constant( 'Ibexa\\Core\\Core\\FieldType\\Date\\Type::DEFAULT_EMPTY' ) %} + {% set defaultValue = 'fielddefinition.default-value.empty'|trans|desc("Empty") %} + {% else %} + {% set defaultValue = 'fielddefinition.default-value.current_date'|trans|desc("Current date") %} + {% endif %} + {{ block( 'settings_defaultvalue' ) }} +</ul> +{% endblock %} + +{% block eztime_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + {% if settings.defaultType == constant( 'Ibexa\\Core\\Core\\FieldType\\Time\\Type::DEFAULT_EMPTY' ) %} + {% set defaultValue = 'fielddefinition.default-value.empty'|trans|desc("Empty") %} + {% else %} + {% set defaultValue = 'fielddefinition.default-value.current_time'|trans|desc("Current time") %} + {% endif %} + {{ block( 'settings_defaultvalue' ) }} + <li class="ibexa-fielddefinition-setting use-seconds"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.use-seconds.label'|trans|desc("Use seconds:")}}</div> + <div class="ibexa-fielddefinition-setting-value">{{ settings.useSeconds ? 'fielddefinition.use-seconds.yes'|trans|desc("Yes") : 'fielddefinition.use-seconds.no'|trans|desc("No") }}</div> + </li> +</ul> +{% endblock %} + +{% block ezinteger_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + {% set defaultValue = fielddefinition.defaultValue.value %} + {{ block( 'settings_defaultvalue' ) }} + {% set minValue = fielddefinition.validatorConfiguration.IntegerValueValidator.minIntegerValue %} + {{ block( 'settings_minimumvalue' ) }} + {% set maxValue = fielddefinition.validatorConfiguration.IntegerValueValidator.maxIntegerValue %} + {{ block( 'settings_maximumvalue' ) }} +</ul> +{% endblock %} + +{% block ezfloat_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + {% set defaultValue = fielddefinition.defaultValue.value %} + {{ block( 'settings_defaultvalue' ) }} + {% set minValue = fielddefinition.validatorConfiguration.FloatValueValidator.minFloatValue %} + {{ block( 'settings_minimumvalue' ) }} + {% set maxValue = fielddefinition.validatorConfiguration.FloatValueValidator.maxFloatValue %} + {{ block( 'settings_maximumvalue' ) }} +</ul> +{% endblock %} + +{% block ezselection_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + <li class="ibexa-fielddefinition-setting options"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.options.label'|trans|desc("Defined options")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + <ul> + {% for option in settings.options %} + <li>{{ option }}</li> + {% endfor %} + </ul> + </div> + </li> + {% set isMultiple = settings.isMultiple %} + {{ block( 'settings_allowmultiple' ) }} +</ul> +{% endblock %} + + +{% block ezbinaryfile_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + {{ block( 'settings_maxfilesize' ) }} +</ul> +{% endblock %} + +{% block ezmedia_settings %} +{% set type = settings.mediaType %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + {{ block( 'settings_maxfilesize' ) }} + <li class="ibexa-fielddefinition-setting media-player-type"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.media-player-type.label'|trans|desc("Media player type:")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + {% if type == 'flash' %} + {{ 'fielddefinition.media-player-type.flash'|trans|desc("Flash")}} + {% elseif type == 'quick_time' %} + {{ 'fielddefinition.media-player-type.quick_time'|trans|desc("Quicktime")}} + {% elseif type == 'real_player' %} + {{ 'fielddefinition.media-player-type.real_player'|trans|desc("Real Player")}} + {% elseif type == 'silverlight' %} + {{ 'fielddefinition.media-player-type.silverlight'|trans|desc("Silverlight")}} + {% elseif type == 'windows_media_player' %} + {{ 'fielddefinition.media-player-type.windows_media_player'|trans|desc("Window Media Player")}} + {% elseif type == 'html5_video' %} + {{ 'fielddefinition.media-player-type.html5_video'|trans|desc("HTML5 Video")}} + {% elseif type == 'html5_audio' %} + {{ 'fielddefinition.media-player-type.html5_audio'|trans|desc("HTML5 Audio")}} + {% else %} + <em>{{ 'fielddefinition.media-player-type.undefined'|trans|desc("No defined value")}}</em> + {% endif %} + </div> + </li> +</ul> +{% endblock %} + +{% block ezimage_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + {{ block( 'settings_maxfilesize' ) }} +</ul> +{% endblock %} + +{% block ezobjectrelation_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + <li class="ibexa-fielddefinition-setting selection-method"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.selection-method.label'|trans|desc("Selection method:")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + {% if settings.selectionMethod == 0 %} + {{ 'fielddefinition.selection-method.browse'|trans|desc("Browse")}} + {% elseif settings.selectionMethod == 1 %} + {{ 'fielddefinition.selection-method.list'|trans|desc("Drop-down list")}} + {% else %} + {{ 'fielddefinition.selection-method.tree'|trans|desc("Drop-down tree")}} + {% endif %} + </div> + </li> + {{ block( 'settings_selection_content_types' ) }} + + {% set rootLocationId = settings.selectionRoot %} + {{ block( 'settings_selectionroot' ) }} +</ul> +{% endblock %} + +{% block ezobjectrelationlist_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + <li class="ibexa-fielddefinition-setting selection-method"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.selection-method.label'|trans|desc("Selection method:")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + {% if settings.selectionMethod == 0 %} + {{ 'fielddefinition.selection-method.browse'|trans|desc("Browse")}} + {% elseif settings.selectionMethod == 1 %} + {{ 'fielddefinition.selection-method.list'|trans|desc("Drop-down list")}} + {% elseif settings.selectionMethod == 2 %} + {{ 'fielddefinition.selection-method.radio'|trans|desc("List with radio buttons")}} + {% elseif settings.selectionMethod == 3 %} + {{ 'fielddefinition.selection-method.checkbox'|trans|desc("List with checkboxes")}} + {% elseif settings.selectionMethod == 4 %} + {{ 'fielddefinition.selection-method.multiple_list'|trans|desc("Multiple selection list")}} + {% elseif settings.selectionMethod == 5 %} + {{ 'fielddefinition.selection-method.multi_template'|trans|desc("Template based, multi")}} + {% else %} + {{ 'fielddefinition.selection-method.single_template'|trans|desc("Template based, single")}} + {% endif %} + </div> + </li> + + {{ block( 'settings_selection_content_types' ) }} + + {% set rootLocationId = settings.selectionDefaultLocation %} + {{ block( 'settings_selectionroot' ) }} +</ul> +{% endblock %} + +{% block ezauthor_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + {% if settings.defaultType == constant( 'Ibexa\\Core\\Core\\FieldType\\Author\\Type::DEFAULT_EMPTY' ) %} + {% set defaultValue = 'fielddefinition.default-value.empty'|trans|desc("Empty") %} + {% else %} + {% set defaultValue = 'fielddefinition.default-value.current_author'|trans|desc("Current User") %} + {% endif %} + {{ block( 'settings_defaultvalue' ) }} +</ul> +{% endblock %} + +{% block ezurl_settings %}{% endblock %} + +{% block ezisbn_settings %} +<ul class="ibexa-fielddefinition-settings ibexa-fielddefinition-{{ fielddefinition.fieldTypeIdentifier }}-settings"> + {% set defaultValue = null %} + {% if fielddefinition.defaultValue.isbn %} + {% set defaultValue = fielddefinition.defaultValue.isbn %} + {% endif %} + {{ block( 'settings_defaultvalue' ) }} + {% set isISBN13 = settings.isISBN13 %} + {{ block( 'settings_allowisbn13' ) }} +</ul> +{% endblock %} + +{% block ezkeyword_settings %}{% endblock %} + +{% block ezuser_settings %}{% endblock %} + +{% block ezemail_settings %}{% endblock %} + +{% block ezgmaplocation_settings %}{% endblock %} + +{% block settings_maxfilesize %} + <li class="ibexa-fielddefinition-setting maximum-file-size"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.maximum-file-size.label'|trans|desc("Maximum file size:")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + {% if fielddefinition.validatorConfiguration.FileSizeValidator.maxFileSize %} + {{ 'fielddefinition.maximum-file-size.value'|trans({'%max%': fielddefinition.validatorConfiguration.FileSizeValidator.maxFileSize})|desc("%max% MB")}} + {% else %} + <em>{{ 'fielddefinition.maximum-file-size.undefined'|trans|desc("No defined maximum size")}}</em> + {% endif %} + </div> + </li> +{% endblock %} + +{% block settings_preferredrows %} + <li class="ibexa-fielddefinition-setting preferred-rows-number"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.preferred-rows-number.label'|trans|desc("Preferred number of rows:")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + {% if rows %} + {{ 'fielddefinition.preferred-rows-number.value'|trans({'%rows%': rows})|desc("%rows% rows")}} + {% else %} + <em>{{ 'fielddefinition.preferred-rows-number.undefined'|trans|desc("No preferred number of rows")}}</em> + {% endif %} + </div> + </li> +{% endblock %} + +{% block settings_selectionroot %} + <li class="ibexa-fielddefinition-setting selection-root"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.selection-root.label'|trans|desc("Selection root:")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + {% if rootLocationId %} + {{ render( controller( "ibexa_content::viewAction", {'locationId': rootLocationId, 'viewType': 'line', 'layout': false} ), {'strategy': 'esi'}) }} + {% else %} + <em>{{ 'fielddefinition.selection-root.undefined'|trans|desc("No defined root")}}</em> + {% endif %} + </div> + </li> +{% endblock %} + +{% block settings_selection_content_types %} + <li class="ibexa-fielddefinition-setting allowed-content-types"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.allowed-content-types.label'|trans|desc("Allowed content types:")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + {% if settings.selectionContentTypes %} + {# TODO display content type name #} + <ul> + {% for typeIdentifier in settings.selectionContentTypes %} + <li>{{ typeIdentifier }}</li> + {% endfor %} + </ul> + {% else %} + <em>{{ 'fielddefinition.allowed-content-types.any'|trans|desc("Any")}}</em> + {% endif %} + </div> + </li> +{% endblock %} + +{% block settings_defaultvalue %} + <li class="ibexa-fielddefinition-setting default-value"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.default-value.label'|trans|desc("Default value:")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + {% if defaultValue is not null %} + {{ defaultValue }} + {% else %} + <em>{{ 'fielddefinition.default-value.undefined'|trans|desc("No default value")}}</em> + {% endif %} + </div> + </li> +{% endblock %} + +{% block settings_minimumvalue %} + <li class="ibexa-fielddefinition-setting min-value"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.min-value.label'|trans|desc("Minimum value:")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + {% if minValue %} + {{ minValue }} + {% else %} + <em>{{ 'fielddefinition.min-value.undefined'|trans|desc("No defined minimum value")}}</em> + {% endif %} + </div> + </li> +{% endblock %} + +{% block settings_maximumvalue %} + <li class="ibexa-fielddefinition-setting max-value"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.max-value.label'|trans|desc("Maximum value:")}}</div> + <div class="ibexa-fielddefinition-setting-value"> + {% if maxValue %} + {{ maxValue }} + {% else %} + <em>{{ 'fielddefinition.max-value.undefined'|trans|desc("No defined maximum value")}}</em> + {% endif %} + </div> + </li> +{% endblock %} + +{% block settings_allowmultiple %} + <li class="ibexa-fielddefinition-setting multiple"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.multiple.label'|trans|desc("Allow multiple choices:")}}</div> + <div class="ibexa-fielddefinition-setting-value">{{ isMultiple ? 'fielddefinition.multiple.yes'|trans|desc("Yes") : 'fielddefinition.multiple.no'|trans|desc("No") }}</div> + </li> +{% endblock %} + +{% block settings_allowisbn13 %} + <li class="ibexa-fielddefinition-setting isbn"> + <div class="ibexa-fielddefinition-setting-name">{{ 'fielddefinition.isbn.label'|trans|desc("Selected ISBN format:")}}</div> + <div class="ibexa-fielddefinition-setting-value">{{ isISBN13 ? 'ISBN-13' : 'ISBN-10' }}</div> + </li> +{% endblock %} + +{% block ezimageasset_settings %} +{% endblock %} diff --git a/src/bundle/Core/Resources/views/pagelayout.html.twig b/src/bundle/Core/Resources/views/pagelayout.html.twig new file mode 100644 index 0000000000..62fe3e4d4b --- /dev/null +++ b/src/bundle/Core/Resources/views/pagelayout.html.twig @@ -0,0 +1,19 @@ +<!doctype html> +<html lang="{{ app.request.locale|replace({'_': '-'}) }}"> +<head> + <meta charset="utf-8"> + {% if content is defined and title is not defined %} + {% set title = ibexa_content_name( content ) %} + {% endif %} + <title>{{ title|default( 'Home' ) }} + + {% if content is defined and location is defined and location is not null and location.id is not null %} + + {% elseif content is defined and content.contentInfo.mainLocationId %} + + {% endif %} + + +{% block content %}{% endblock %} + + diff --git a/eZ/Bundle/EzPublishCoreBundle/Resources/views/viewbase_layout.html.twig b/src/bundle/Core/Resources/views/viewbase_layout.html.twig similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Resources/views/viewbase_layout.html.twig rename to src/bundle/Core/Resources/views/viewbase_layout.html.twig diff --git a/eZ/Bundle/EzPublishCoreBundle/Routing/DefaultRouter.php b/src/bundle/Core/Routing/DefaultRouter.php similarity index 83% rename from eZ/Bundle/EzPublishCoreBundle/Routing/DefaultRouter.php rename to src/bundle/Core/Routing/DefaultRouter.php index 22535ca4ac..209869e2fa 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Routing/DefaultRouter.php +++ b/src/bundle/Core/Routing/DefaultRouter.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Routing; - -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessAware; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\URILexer; +namespace Ibexa\Bundle\Core\Routing; + +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess\URILexer; use Symfony\Bundle\FrameworkBundle\Routing\Router; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Exception\RouteNotFoundException; @@ -22,15 +22,15 @@ */ class DefaultRouter extends Router implements RequestMatcherInterface, SiteAccessAware { - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ protected $siteAccess; protected $nonSiteAccessAwareRoutes = []; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ protected $configResolver; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface */ protected $siteAccessRouter; public function setConfigResolver(ConfigResolverInterface $configResolver) @@ -55,7 +55,7 @@ public function setNonSiteAccessAwareRoutes(array $routes) } /** - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface $siteAccessRouter + * @param \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface $siteAccessRouter */ public function setSiteAccessRouter(SiteAccessRouterInterface $siteAccessRouter) { @@ -64,7 +64,15 @@ public function setSiteAccessRouter(SiteAccessRouterInterface $siteAccessRouter) public function matchRequest(Request $request) { - return $this->match($request->attributes->get('semanticPathinfo', $request->getPathInfo())); + if ($request->attributes->has('semanticPathinfo')) { + $request = $request->duplicate(); + $request->server->set( + 'REQUEST_URI', + $request->attributes->get('semanticPathinfo') + ); + } + + return parent::matchRequest($request); } public function generate($name, $parameters = [], $referenceType = self::ABSOLUTE_PATH) @@ -144,7 +152,7 @@ protected function isSiteAccessAwareRoute($routeName) /** * Merges context from $simplifiedRequest into a clone of the current context. * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $simplifiedRequest + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $simplifiedRequest * * @return \Symfony\Component\Routing\RequestContext */ @@ -177,3 +185,5 @@ public function getContextBySimplifiedRequest(SimplifiedRequest $simplifiedReque return $context; } } + +class_alias(DefaultRouter::class, 'eZ\Bundle\EzPublishCoreBundle\Routing\DefaultRouter'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Routing/JsRouting/ExposedRoutesExtractor.php b/src/bundle/Core/Routing/JsRouting/ExposedRoutesExtractor.php similarity index 90% rename from eZ/Bundle/EzPublishCoreBundle/Routing/JsRouting/ExposedRoutesExtractor.php rename to src/bundle/Core/Routing/JsRouting/ExposedRoutesExtractor.php index b6301642e2..deaf1baf62 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Routing/JsRouting/ExposedRoutesExtractor.php +++ b/src/bundle/Core/Routing/JsRouting/ExposedRoutesExtractor.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Routing\JsRouting; +namespace Ibexa\Bundle\Core\Routing\JsRouting; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; use FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractorInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\Route; use Symfony\Component\Routing\RouteCollection; @@ -47,7 +47,7 @@ public function getRoutes(): RouteCollection public function getBaseUrl(): string { $baseUrl = $this->innerExtractor->getBaseUrl(); - $request = $this->requestStack->getMasterRequest(); + $request = $this->requestStack->getMainRequest(); if ($request === null) { return $baseUrl; } @@ -95,3 +95,5 @@ public function isRouteExposed(Route $route, $name): bool return $this->innerExtractor->isRouteExposed($route, $name); } } + +class_alias(ExposedRoutesExtractor::class, 'eZ\Bundle\EzPublishCoreBundle\Routing\JsRouting\ExposedRoutesExtractor'); diff --git a/src/bundle/Core/Routing/UrlAliasRouter.php b/src/bundle/Core/Routing/UrlAliasRouter.php new file mode 100644 index 0000000000..bbc42258e8 --- /dev/null +++ b/src/bundle/Core/Routing/UrlAliasRouter.php @@ -0,0 +1,60 @@ +configResolver = $configResolver; + } + + public function matchRequest(Request $request) + { + // UrlAliasRouter might be disabled from configuration. + // An example is for running the admin interface: it needs to be entirely run through the legacy kernel. + if ($this->configResolver->getParameter('url_alias_router') === false) { + throw new ResourceNotFoundException('Config requires bypassing UrlAliasRouter'); + } + + return parent::matchRequest($request); + } + + /** + * Will return the right UrlAlias in regards to configured root location. + * + * @param string $pathinfo + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the path does not exist or is not valid for the given language + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + */ + protected function getUrlAlias($pathinfo) + { + $pathPrefix = $this->generator->getPathPrefixByRootLocationId($this->rootLocationId); + + if ( + $this->rootLocationId === null || + $this->generator->isUriPrefixExcluded($pathinfo) || + $pathPrefix === '/' + ) { + return parent::getUrlAlias($pathinfo); + } + + return $this->urlAliasService->lookup($pathPrefix . $pathinfo); + } +} + +class_alias(UrlAliasRouter::class, 'eZ\Bundle\EzPublishCoreBundle\Routing\UrlAliasRouter'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Session/Handler/NativeSessionHandler.php b/src/bundle/Core/Session/Handler/NativeSessionHandler.php similarity index 88% rename from eZ/Bundle/EzPublishCoreBundle/Session/Handler/NativeSessionHandler.php rename to src/bundle/Core/Session/Handler/NativeSessionHandler.php index 5dbff8a701..c60e14e099 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Session/Handler/NativeSessionHandler.php +++ b/src/bundle/Core/Session/Handler/NativeSessionHandler.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Session\Handler; +namespace Ibexa\Bundle\Core\Session\Handler; /** * Class NativeSessionHandler. @@ -34,3 +34,5 @@ public function __construct($savePath = null, $saveHandler = null) } } } + +class_alias(NativeSessionHandler::class, 'eZ\Bundle\EzPublishCoreBundle\Session\Handler\NativeSessionHandler'); diff --git a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/Config/ComplexConfigProcessor.php b/src/bundle/Core/SiteAccess/Config/ComplexConfigProcessor.php similarity index 75% rename from eZ/Bundle/EzPublishCoreBundle/SiteAccess/Config/ComplexConfigProcessor.php rename to src/bundle/Core/SiteAccess/Config/ComplexConfigProcessor.php index 45845053a5..a5da7d4a1e 100644 --- a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/Config/ComplexConfigProcessor.php +++ b/src/bundle/Core/SiteAccess/Config/ComplexConfigProcessor.php @@ -6,26 +6,26 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\SiteAccess\Config; +namespace Ibexa\Bundle\Core\SiteAccess\Config; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParser; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Exception\ParameterNotFoundException; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessService; -use eZ\Publish\SPI\SiteAccess\ConfigProcessor; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParser; +use Ibexa\Contracts\Core\SiteAccess\ConfigProcessor; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Exception\ParameterNotFoundException; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService; use function str_replace; final class ComplexConfigProcessor implements ConfigProcessor { - private const DEFAULT_NAMESPACE = 'ezsettings'; + private const DEFAULT_NAMESPACE = 'ibexa.site_access.config'; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessService */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService */ private $siteAccessService; - /** @var \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParserInterface */ + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParserInterface */ private $complexSettingParser; public function __construct( @@ -83,3 +83,5 @@ public function processSettingValue(string $value): string return $value; } } + +class_alias(ComplexConfigProcessor::class, 'eZ\Bundle\EzPublishCoreBundle\SiteAccess\Config\ComplexConfigProcessor'); diff --git a/src/bundle/Core/SiteAccess/Config/IOConfigResolver.php b/src/bundle/Core/SiteAccess/Config/IOConfigResolver.php new file mode 100644 index 0000000000..4df7118c8a --- /dev/null +++ b/src/bundle/Core/SiteAccess/Config/IOConfigResolver.php @@ -0,0 +1,43 @@ +complexConfigProcessor = $complexConfigProcessor; + } + + public function getRootDir(): string + { + return $this->complexConfigProcessor->processComplexSetting('io.root_dir'); + } + + public function getLegacyUrlPrefix(): string + { + return $this->complexConfigProcessor->processComplexSetting('io.legacy_url_prefix'); + } + + public function getUrlPrefix(): string + { + return $this->complexConfigProcessor->processComplexSetting('io.url_prefix'); + } +} + +class_alias(IOConfigResolver::class, 'eZ\Bundle\EzPublishCoreBundle\SiteAccess\Config\IOConfigResolver'); diff --git a/src/bundle/Core/SiteAccess/LanguageResolver.php b/src/bundle/Core/SiteAccess/LanguageResolver.php new file mode 100644 index 0000000000..a22db5f3e4 --- /dev/null +++ b/src/bundle/Core/SiteAccess/LanguageResolver.php @@ -0,0 +1,40 @@ +configResolver = $configResolver; + parent::__construct($defaultUseAlwaysAvailable, $defaultShowAllTranslations); + } + + /** + * Get list of languages configured via scope/SiteAccess context. + * + * @return string[] + */ + protected function getConfiguredLanguages(): array + { + return $this->configResolver->getParameter('languages'); + } +} + +class_alias(LanguageResolver::class, 'eZ\Bundle\EzPublishCoreBundle\SiteAccess\LanguageResolver'); diff --git a/src/bundle/Core/SiteAccess/Matcher.php b/src/bundle/Core/SiteAccess/Matcher.php new file mode 100644 index 0000000000..eed312fa5a --- /dev/null +++ b/src/bundle/Core/SiteAccess/Matcher.php @@ -0,0 +1,24 @@ +siteAccessMatcherRegistry = $siteAccessMatcherRegistry; + } + + /** + * Builds siteaccess matcher. + * If $matchingClass begins with "@", it will be considered as a service identifier. + * + * @param $matchingClass + * @param $matchingConfiguration + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $request + * + * @return \Ibexa\Bundle\Core\SiteAccess\Matcher + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function buildMatcher($matchingClass, $matchingConfiguration, SimplifiedRequest $request) + { + if (strpos($matchingClass, '@') === 0) { + $matcher = $this->siteAccessMatcherRegistry->getMatcher(substr($matchingClass, 1)); + + $matcher->setMatchingConfiguration($matchingConfiguration); + $matcher->setRequest($request); + + return $matcher; + } + + return parent::buildMatcher($matchingClass, $matchingConfiguration, $request); + } +} + +class_alias(MatcherBuilder::class, 'eZ\Bundle\EzPublishCoreBundle\SiteAccess\MatcherBuilder'); diff --git a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/SiteAccessConfigurationFilter.php b/src/bundle/Core/SiteAccess/SiteAccessConfigurationFilter.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/SiteAccess/SiteAccessConfigurationFilter.php rename to src/bundle/Core/SiteAccess/SiteAccessConfigurationFilter.php index a706ff7ff9..d07f0e142e 100644 --- a/eZ/Bundle/EzPublishCoreBundle/SiteAccess/SiteAccessConfigurationFilter.php +++ b/src/bundle/Core/SiteAccess/SiteAccessConfigurationFilter.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\SiteAccess; +namespace Ibexa\Bundle\Core\SiteAccess; /** * Allows to filter SiteAccess configuration before it gets processed. @@ -31,3 +31,5 @@ interface SiteAccessConfigurationFilter */ public function filter(array $siteAccessConfiguration); } + +class_alias(SiteAccessConfigurationFilter::class, 'eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessConfigurationFilter'); diff --git a/src/bundle/Core/SiteAccess/SiteAccessMatcherRegistry.php b/src/bundle/Core/SiteAccess/SiteAccessMatcherRegistry.php new file mode 100644 index 0000000000..bbab246675 --- /dev/null +++ b/src/bundle/Core/SiteAccess/SiteAccessMatcherRegistry.php @@ -0,0 +1,49 @@ +matchers = $matchers; + } + + public function setMatcher(string $identifier, Matcher $matcher): void + { + $this->matchers[$identifier] = $matcher; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function getMatcher(string $identifier): Matcher + { + if (!$this->hasMatcher($identifier)) { + throw new NotFoundException('SiteAccess Matcher', $identifier); + } + + return $this->matchers[$identifier]; + } + + public function hasMatcher(string $identifier): bool + { + return isset($this->matchers[$identifier]); + } +} + +class_alias(SiteAccessMatcherRegistry::class, 'eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessMatcherRegistry'); diff --git a/src/bundle/Core/SiteAccess/SiteAccessMatcherRegistryInterface.php b/src/bundle/Core/SiteAccess/SiteAccessMatcherRegistryInterface.php new file mode 100644 index 0000000000..2a4190286e --- /dev/null +++ b/src/bundle/Core/SiteAccess/SiteAccessMatcherRegistryInterface.php @@ -0,0 +1,28 @@ +configResolver = $configResolver; + } + + public function getGlobals(): array + { + return $this->configResolver->getParameter('twig_variables'); + } +} + +class_alias(ContextAwareTwigVariablesExtension::class, 'eZ\Bundle\EzPublishCoreBundle\Templating\Twig\ContextAwareTwigVariablesExtension'); diff --git a/src/bundle/Core/Translation/Collector.php b/src/bundle/Core/Translation/Collector.php new file mode 100644 index 0000000000..0794c08562 --- /dev/null +++ b/src/bundle/Core/Translation/Collector.php @@ -0,0 +1,20 @@ +tranlationPattern = $kernelRootDir . sprintf('%1$svendor%1$sibexa%1$si18n%1$stranslations%1$s*%1$s*%1$s*.xlf', DIRECTORY_SEPARATOR); + } + + /** + * @return array + */ + public function collect() + { + $meta = []; + foreach (glob($this->tranlationPattern) as $file) { + [$domain, $locale, $format] = explode('.', basename($file), 3); + $meta[] = [ + 'file' => $file, + 'domain' => $domain, + 'locale' => $locale, + 'format' => $format, + ]; + } + + return $meta; + } +} + +class_alias(GlobCollector::class, 'eZ\Bundle\EzPublishCoreBundle\Translation\GlobCollector'); diff --git a/src/bundle/Core/Translation/Policy/PolicyTranslationDefinitionProvider.php b/src/bundle/Core/Translation/Policy/PolicyTranslationDefinitionProvider.php new file mode 100644 index 0000000000..72f9d3aad5 --- /dev/null +++ b/src/bundle/Core/Translation/Policy/PolicyTranslationDefinitionProvider.php @@ -0,0 +1,177 @@ + + */ + public static function getTranslationMessages(): array + { + return [ + (new Message('role.policy.all_modules_all_functions', self::TRANSLATION_DOMAIN)) + ->setDesc('All modules / All functions'), + (new Message('role.policy.content', self::TRANSLATION_DOMAIN)) + ->setDesc('Content'), + (new Message('role.policy.content.all_functions', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / All functions'), + (new Message('role.policy.content.cleantrash', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Clean trash'), + (new Message('role.policy.content.create', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Create'), + (new Message('role.policy.content.diff', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Diff'), + (new Message('role.policy.content.edit', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Edit'), + (new Message('role.policy.content.hide', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Hide'), + (new Message('role.policy.content.view_embed', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / View embed'), + (new Message('role.policy.content.manage_locations', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Manage locations'), + (new Message('role.policy.content.pendinglist', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Pending list'), + (new Message('role.policy.content.publish', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Publish'), + (new Message('role.policy.content.read', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Read'), + (new Message('role.policy.content.remove', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Remove'), + (new Message('role.policy.content.restore', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Restore'), + (new Message('role.policy.content.reverserelatedlist', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Reverse related list'), + (new Message('role.policy.content.translate', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Translate'), + (new Message('role.policy.content.translations', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Translations'), + (new Message('role.policy.content.unlock', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Unlock'), + (new Message('role.policy.content.urltranslator', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Url translator'), + (new Message('role.policy.content.versionread', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Version read'), + (new Message('role.policy.content.versionremove', self::TRANSLATION_DOMAIN)) + ->setDesc('Content / Version remove'), + + (new Message('role.policy.class', self::TRANSLATION_DOMAIN)) + ->setDesc('Content type'), + (new Message('role.policy.class.all_functions', self::TRANSLATION_DOMAIN)) + ->setDesc('Content type / All functions'), + (new Message('role.policy.class.create', self::TRANSLATION_DOMAIN)) + ->setDesc('Content type / Create'), + (new Message('role.policy.class.delete', self::TRANSLATION_DOMAIN)) + ->setDesc('Content type / Delete'), + (new Message('role.policy.class.update', self::TRANSLATION_DOMAIN)) + ->setDesc('Content type / Update'), + + (new Message('role.policy.state', self::TRANSLATION_DOMAIN)) + ->setDesc('State'), + (new Message('role.policy.state.all_functions', self::TRANSLATION_DOMAIN)) + ->setDesc('State / All functions'), + (new Message('role.policy.state.administrate', self::TRANSLATION_DOMAIN)) + ->setDesc('State / Administrate'), + (new Message('role.policy.state.assign', self::TRANSLATION_DOMAIN)) + ->setDesc('State / Assign'), + + (new Message('role.policy.role', self::TRANSLATION_DOMAIN)) + ->setDesc('Role'), + (new Message('role.policy.role.all_functions', self::TRANSLATION_DOMAIN)) + ->setDesc('Role / All functions'), + (new Message('role.policy.role.assign', self::TRANSLATION_DOMAIN)) + ->setDesc('Role / Assign'), + (new Message('role.policy.role.create', self::TRANSLATION_DOMAIN)) + ->setDesc('Role / Create'), + (new Message('role.policy.role.delete', self::TRANSLATION_DOMAIN)) + ->setDesc('Role / Delete'), + (new Message('role.policy.role.read', self::TRANSLATION_DOMAIN)) + ->setDesc('Role / Read'), + (new Message('role.policy.role.update', self::TRANSLATION_DOMAIN)) + ->setDesc('Role / Update'), + + (new Message('role.policy.section', self::TRANSLATION_DOMAIN)) + ->setDesc('Section'), + (new Message('role.policy.section.all_functions', self::TRANSLATION_DOMAIN)) + ->setDesc('Section / All functions'), + (new Message('role.policy.section.assign', self::TRANSLATION_DOMAIN)) + ->setDesc('Section / Assign'), + (new Message('role.policy.section.edit', self::TRANSLATION_DOMAIN)) + ->setDesc('Section / Edit'), + (new Message('role.policy.section.view', self::TRANSLATION_DOMAIN)) + ->setDesc('Section / View'), + + (new Message('role.policy.setup', self::TRANSLATION_DOMAIN)) + ->setDesc('Setup'), + (new Message('role.policy.setup.all_functions', self::TRANSLATION_DOMAIN)) + ->setDesc('Setup / All functions'), + (new Message('role.policy.setup.administrate', self::TRANSLATION_DOMAIN)) + ->setDesc('Setup / Administrate'), + (new Message('role.policy.setup.install', self::TRANSLATION_DOMAIN)) + ->setDesc('Setup / Install'), + (new Message('role.policy.setup.setup', self::TRANSLATION_DOMAIN)) + ->setDesc('Setup / Setup'), + (new Message('role.policy.setup.system_info', self::TRANSLATION_DOMAIN)) + ->setDesc('Setup / System info'), + + (new Message('role.policy.url', self::TRANSLATION_DOMAIN)) + ->setDesc('URL'), + (new Message('role.policy.url.all_functions', self::TRANSLATION_DOMAIN)) + ->setDesc('URL / All functions'), + (new Message('role.policy.url.view', self::TRANSLATION_DOMAIN)) + ->setDesc('URL / View'), + (new Message('role.policy.url.update', self::TRANSLATION_DOMAIN)) + ->setDesc('URL / Update'), + + (new Message('role.policy.user', self::TRANSLATION_DOMAIN)) + ->setDesc('User'), + (new Message('role.policy.user.all_functions', self::TRANSLATION_DOMAIN)) + ->setDesc('User / All functions'), + (new Message('role.policy.user.activation', self::TRANSLATION_DOMAIN)) + ->setDesc('User / Activation'), + (new Message('role.policy.user.invite', self::TRANSLATION_DOMAIN)) + ->setDesc('User / Invite'), + (new Message('role.policy.user.login', self::TRANSLATION_DOMAIN)) + ->setDesc('User / Login'), + (new Message('role.policy.user.password', self::TRANSLATION_DOMAIN)) + ->setDesc('User / Password'), + (new Message('role.policy.user.preferences', self::TRANSLATION_DOMAIN)) + ->setDesc('User / Preferences'), + (new Message('role.policy.user.register', self::TRANSLATION_DOMAIN)) + ->setDesc('User / Register'), + (new Message('role.policy.user.selfedit', self::TRANSLATION_DOMAIN)) + ->setDesc('User / Self edit'), + + (new Message('role.policy.user', self::TRANSLATION_DOMAIN)) + ->setDesc('User'), + (new Message('role.policy.user.all_functions', self::TRANSLATION_DOMAIN)) + ->setDesc('User / All functions'), + (new Message('role.policy.user.update', self::TRANSLATION_DOMAIN)) + ->setDesc('User / Update'), + (new Message('role.policy.user.view', self::TRANSLATION_DOMAIN)) + ->setDesc('User / View'), + + (new Message('role.policy.setting', self::TRANSLATION_DOMAIN)) + ->setDesc('Setting'), + (new Message('role.policy.setting.all_functions', self::TRANSLATION_DOMAIN)) + ->setDesc('Setting / All functions'), + (new Message('role.policy.setting.create', self::TRANSLATION_DOMAIN)) + ->setDesc('Setting / Create'), + (new Message('role.policy.setting.remove', self::TRANSLATION_DOMAIN)) + ->setDesc('Setting / Remove'), + (new Message('role.policy.setting.update', self::TRANSLATION_DOMAIN)) + ->setDesc('Setting / Update'), + ]; + } +} diff --git a/eZ/Bundle/EzPublishCoreBundle/URLChecker/Handler/AbstractConfigResolverBasedURLHandler.php b/src/bundle/Core/URLChecker/Handler/AbstractConfigResolverBasedURLHandler.php similarity index 77% rename from eZ/Bundle/EzPublishCoreBundle/URLChecker/Handler/AbstractConfigResolverBasedURLHandler.php rename to src/bundle/Core/URLChecker/Handler/AbstractConfigResolverBasedURLHandler.php index 0e68b78135..ba5c363ddd 100644 --- a/eZ/Bundle/EzPublishCoreBundle/URLChecker/Handler/AbstractConfigResolverBasedURLHandler.php +++ b/src/bundle/Core/URLChecker/Handler/AbstractConfigResolverBasedURLHandler.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\URLChecker\Handler; +namespace Ibexa\Bundle\Core\URLChecker\Handler; -use eZ\Publish\API\Repository\URLService; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Contracts\Core\Repository\URLService; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; /** * URLHandler based on ConfigResolver configured using $parameterName, $namespace and $scope properties. */ abstract class AbstractConfigResolverBasedURLHandler extends AbstractURLHandler { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ protected $configResolver; /** @var string */ @@ -52,3 +52,5 @@ public function getOptions(): array return $this->getOptionsResolver()->resolve($options); } } + +class_alias(AbstractConfigResolverBasedURLHandler::class, 'eZ\Bundle\EzPublishCoreBundle\URLChecker\Handler\AbstractConfigResolverBasedURLHandler'); diff --git a/eZ/Bundle/EzPublishCoreBundle/URLChecker/Handler/AbstractURLHandler.php b/src/bundle/Core/URLChecker/Handler/AbstractURLHandler.php similarity index 75% rename from eZ/Bundle/EzPublishCoreBundle/URLChecker/Handler/AbstractURLHandler.php rename to src/bundle/Core/URLChecker/Handler/AbstractURLHandler.php index 8b0daa6043..101c198353 100644 --- a/eZ/Bundle/EzPublishCoreBundle/URLChecker/Handler/AbstractURLHandler.php +++ b/src/bundle/Core/URLChecker/Handler/AbstractURLHandler.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\URLChecker\Handler; +namespace Ibexa\Bundle\Core\URLChecker\Handler; use DateTime; use Exception; -use eZ\Bundle\EzPublishCoreBundle\URLChecker\URLHandlerInterface; -use eZ\Publish\API\Repository\URLService; -use eZ\Publish\API\Repository\Values\URL\URL; +use Ibexa\Bundle\Core\URLChecker\URLHandlerInterface; +use Ibexa\Contracts\Core\Repository\URLService; +use Ibexa\Contracts\Core\Repository\Values\URL\URL; use Psr\Log\LoggerAwareTrait; use Psr\Log\NullLogger; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -19,7 +19,7 @@ abstract class AbstractURLHandler implements URLHandlerInterface { use LoggerAwareTrait; - /** @var \eZ\Publish\API\Repository\URLService */ + /** @var \Ibexa\Contracts\Core\Repository\URLService */ protected $urlService; public function __construct(URLService $urlService) @@ -35,7 +35,7 @@ abstract public function getOptions(): array; /** * Sets URL status. * - * @param \eZ\Publish\API\Repository\Values\URL\URL $url + * @param \Ibexa\Contracts\Core\Repository\Values\URL\URL $url * @param bool $isValid */ protected function setUrlStatus(URL $url, $isValid) @@ -53,3 +53,5 @@ protected function setUrlStatus(URL $url, $isValid) } } } + +class_alias(AbstractURLHandler::class, 'eZ\Bundle\EzPublishCoreBundle\URLChecker\Handler\AbstractURLHandler'); diff --git a/eZ/Bundle/EzPublishCoreBundle/URLChecker/Handler/HTTPHandler.php b/src/bundle/Core/URLChecker/Handler/HTTPHandler.php similarity index 93% rename from eZ/Bundle/EzPublishCoreBundle/URLChecker/Handler/HTTPHandler.php rename to src/bundle/Core/URLChecker/Handler/HTTPHandler.php index 89fabc9d7a..12977be304 100644 --- a/eZ/Bundle/EzPublishCoreBundle/URLChecker/Handler/HTTPHandler.php +++ b/src/bundle/Core/URLChecker/Handler/HTTPHandler.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\URLChecker\Handler; +namespace Ibexa\Bundle\Core\URLChecker\Handler; -use eZ\Publish\API\Repository\Values\URL\URL; +use Ibexa\Contracts\Core\Repository\Values\URL\URL; use Symfony\Component\OptionsResolver\OptionsResolver; class HTTPHandler extends AbstractConfigResolverBasedURLHandler @@ -107,7 +107,7 @@ public function getOptions(): array /** * Initialize and return a cURL session for given URL. * - * @param \eZ\Publish\API\Repository\Values\URL\URL $url + * @param \Ibexa\Contracts\Core\Repository\Values\URL\URL $url * @param array $handlers * @param int $connectionTimeout * @param int $timeout @@ -144,7 +144,7 @@ private function createCurlHandlerForUrl(URL $url, array &$handlers, int $connec /** * Validate single response. * - * @param \eZ\Publish\API\Repository\Values\URL\URL $url + * @param \Ibexa\Contracts\Core\Repository\Values\URL\URL $url * @param resource $handler CURL handler */ private function doValidate(URL $url, $handler) @@ -157,3 +157,5 @@ private function isSuccessful($statusCode) return $statusCode >= 200 && $statusCode < 300; } } + +class_alias(HTTPHandler::class, 'eZ\Bundle\EzPublishCoreBundle\URLChecker\Handler\HTTPHandler'); diff --git a/eZ/Bundle/EzPublishCoreBundle/URLChecker/Handler/MailToHandler.php b/src/bundle/Core/URLChecker/Handler/MailToHandler.php similarity index 83% rename from eZ/Bundle/EzPublishCoreBundle/URLChecker/Handler/MailToHandler.php rename to src/bundle/Core/URLChecker/Handler/MailToHandler.php index 8984242504..b112be2406 100644 --- a/eZ/Bundle/EzPublishCoreBundle/URLChecker/Handler/MailToHandler.php +++ b/src/bundle/Core/URLChecker/Handler/MailToHandler.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\URLChecker\Handler; +namespace Ibexa\Bundle\Core\URLChecker\Handler; -use eZ\Publish\API\Repository\URLService; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Contracts\Core\Repository\URLService; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use Symfony\Component\OptionsResolver\OptionsResolver; class MailToHandler extends AbstractConfigResolverBasedURLHandler @@ -53,3 +53,5 @@ protected function getOptionsResolver(): OptionsResolver return $resolver; } } + +class_alias(MailToHandler::class, 'eZ\Bundle\EzPublishCoreBundle\URLChecker\Handler\MailToHandler'); diff --git a/src/bundle/Core/URLChecker/URLChecker.php b/src/bundle/Core/URLChecker/URLChecker.php new file mode 100644 index 0000000000..055949fb52 --- /dev/null +++ b/src/bundle/Core/URLChecker/URLChecker.php @@ -0,0 +1,99 @@ +urlService = $urlService; + $this->handlerRegistry = $handlerRegistry; + $this->logger = new NullLogger(); + } + + /** + * {@inheritdoc} + */ + public function check(URLQuery $query) + { + $grouped = $this->fetchUrls($query); + foreach ($grouped as $scheme => $urls) { + if (!$this->handlerRegistry->supported($scheme)) { + $this->logger->error('Unsupported URL schema: ' . $scheme); + continue; + } + + $handler = $this->handlerRegistry->getHandler($scheme); + $handler->validate($urls); + } + } + + /** + * Fetch URLs to check. + * + * @param \Ibexa\Contracts\Core\Repository\Values\URL\URLQuery $query + * + * @return array + */ + protected function fetchUrls(URLQuery $query) + { + return $this->groupByScheme( + $this->urlService->findUrls($query) + ); + } + + /** + * Group URLs by schema. + * + * @param \Ibexa\Contracts\Core\Repository\Values\URL\SearchResult $urls + * + * @return array + */ + private function groupByScheme(SearchResult $urls) + { + $grouped = []; + + foreach ($urls as $url) { + $scheme = parse_url($url->url, PHP_URL_SCHEME); + if (!$scheme) { + continue; + } + + if (!isset($grouped[$scheme])) { + $grouped[$scheme] = []; + } + + $grouped[$scheme][] = $url; + } + + return $grouped; + } +} + +class_alias(URLChecker::class, 'eZ\Bundle\EzPublishCoreBundle\URLChecker\URLChecker'); diff --git a/src/bundle/Core/URLChecker/URLCheckerInterface.php b/src/bundle/Core/URLChecker/URLCheckerInterface.php new file mode 100644 index 0000000000..b5a0353144 --- /dev/null +++ b/src/bundle/Core/URLChecker/URLCheckerInterface.php @@ -0,0 +1,21 @@ +handlers[$scheme]; } } + +class_alias(URLHandlerRegistry::class, 'eZ\Bundle\EzPublishCoreBundle\URLChecker\URLHandlerRegistry'); diff --git a/src/bundle/Core/URLChecker/URLHandlerRegistryInterface.php b/src/bundle/Core/URLChecker/URLHandlerRegistryInterface.php new file mode 100644 index 0000000000..e919451f18 --- /dev/null +++ b/src/bundle/Core/URLChecker/URLHandlerRegistryInterface.php @@ -0,0 +1,40 @@ +requestContext = $requestContext; + $this->variationPathGenerator = $variationPathGenerator; + } + + abstract public function resolve($path, $variation): string; + + /** + * Returns path for filtered image from original path, using the VariationPathGenerator. + * + * @return string + */ + public function getFilePath(?string $path, string $variation) + { + return $this->variationPathGenerator->getVariationPath($path, $variation); + } + + /** + * Returns base URL, with scheme, host and port, for current request context. + * If no delivery URL is configured for current SiteAccess, will return base URL from current RequestContext. + */ + protected function getBaseUrl(): string + { + $port = ''; + if ($this->requestContext->getScheme() === 'https' && $this->requestContext->getHttpsPort() != 443) { + $port = ":{$this->requestContext->getHttpsPort()}"; + } + + if ($this->requestContext->getScheme() === 'http' && $this->requestContext->getHttpPort() != 80) { + $port = ":{$this->requestContext->getHttpPort()}"; + } + + $baseUrl = $this->requestContext->getBaseUrl(); + if (substr($this->requestContext->getBaseUrl(), -4) === '.php') { + $baseUrl = pathinfo($this->requestContext->getBaseurl(), PATHINFO_DIRNAME); + } + $baseUrl = rtrim($baseUrl, '/\\'); + + return sprintf( + '%s://%s%s%s', + $this->requestContext->getScheme(), + $this->requestContext->getHost(), + $port, + $baseUrl + ); + } +} diff --git a/src/bundle/Core/Variation/VariationHandlerResolver.php b/src/bundle/Core/Variation/VariationHandlerResolver.php new file mode 100644 index 0000000000..c220c64b95 --- /dev/null +++ b/src/bundle/Core/Variation/VariationHandlerResolver.php @@ -0,0 +1,46 @@ +variationHandlerRegistry = $variationHandlerRegistry; + $this->configResolver = $configResolver; + } + + public function getVariation( + Field $field, + VersionInfo $versionInfo, + $variationName, + array $parameters = [] + ): Variation { + $variationHandlerIdentifier = $this->configResolver->getParameter('variation_handler_identifier'); + $handler = $this->variationHandlerRegistry->getVariationHandler($variationHandlerIdentifier); + + return $handler->getVariation( + $field, + $versionInfo, + $variationName, + $parameters + ); + } +} diff --git a/src/bundle/Core/View/Manager.php b/src/bundle/Core/View/Manager.php new file mode 100644 index 0000000000..3fd98d84c6 --- /dev/null +++ b/src/bundle/Core/View/Manager.php @@ -0,0 +1,40 @@ +logger) { + $this->logger->debug('Changing SiteAccess in view providers'); + } + + $providers = array_merge( + $this->getAllLocationViewProviders(), + $this->getAllContentViewProviders() + ); + foreach ($providers as $provider) { + if ($provider instanceof SiteAccessAware) { + $provider->setSiteAccess($siteAccess); + } + } + } +} + +class_alias(Manager::class, 'eZ\Bundle\EzPublishCoreBundle\View\Manager'); diff --git a/src/bundle/Core/View/Provider/Configured.php b/src/bundle/Core/View/Provider/Configured.php new file mode 100644 index 0000000000..09cacbd760 --- /dev/null +++ b/src/bundle/Core/View/Provider/Configured.php @@ -0,0 +1,28 @@ +matcherFactory instanceof SiteAccessAware) { + $this->matcherFactory->setSiteAccess($siteAccess); + } + } +} + +class_alias(Configured::class, 'eZ\Bundle\EzPublishCoreBundle\View\Provider\Configured'); diff --git a/src/bundle/Core/behat_suites.yaml b/src/bundle/Core/behat_suites.yaml new file mode 100644 index 0000000000..ce342323eb --- /dev/null +++ b/src/bundle/Core/behat_suites.yaml @@ -0,0 +1,34 @@ +# This file is meant to be imported from Ibexa behat.yml.dist. +# All path are relative to the root Ibexa project directory. +core: + suites: + console: + paths: + - vendor/ibexa/core/src/bundle/Core/Features/Console + contexts: + - Ibexa\Bundle\Core\Features\Context\ConsoleContext + web: + paths: + - vendor/ibexa/core/src/bundle/Core/Features/Content + - vendor/ibexa/core/src/bundle/Core/Features/Exception + contexts: + - Ibexa\Bundle\Core\Features\Context\ContentPreviewContext + - Ibexa\Bundle\Core\Features\Context\ContentContext + - Ibexa\Bundle\Core\Features\Context\ExceptionContext + query_controller: + paths: + - vendor/ibexa/core/src/bundle/Core/Features/QueryController/query_controller.feature + contexts: + - Behat\MinkExtension\Context\MinkContext + - Ibexa\Behat\API\Context\ContentContext + - Ibexa\Behat\API\Context\TestContext + - Ibexa\Behat\Core\Context\ConfigurationContext + - Ibexa\Bundle\Core\Features\Context\QueryControllerContext + setup: + paths: + - vendor/ibexa/core/src/bundle/Core/Features/QueryController/setup.feature + contexts: + - Ibexa\Behat\API\Context\ContentContext + - Ibexa\Behat\API\Context\TestContext + - Ibexa\Behat\Core\Context\ConfigurationContext + - Ibexa\Behat\Core\Context\FileContext diff --git a/src/bundle/Debug/Collector/IbexaCoreCollector.php b/src/bundle/Debug/Collector/IbexaCoreCollector.php new file mode 100644 index 0000000000..29e029366f --- /dev/null +++ b/src/bundle/Debug/Collector/IbexaCoreCollector.php @@ -0,0 +1,115 @@ +reset(); + } + + public function collect(Request $request, Response $response, \Throwable $exception = null) + { + /** @var \Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface $innerCollector */ + foreach ($this->data['collectors'] as $innerCollector) { + $innerCollector->collect($request, $response, $exception); + } + } + + public function getName() + { + return 'ezpublish.debug.toolbar'; + } + + /** + * @param \Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface $collector + */ + public function addCollector(DataCollectorInterface $collector, $panelTemplate = null, $toolbarTemplate = null) + { + $name = $collector->getName(); + $this->data['collectors'][$name] = $collector; + $this->data['panelTemplates'][$name] = $panelTemplate; + $this->data['toolbarTemplates'][$name] = $toolbarTemplate; + } + + /** + * @param string $name Name of the collector + * + * @return \Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface + * + * @throws \InvalidArgumentException + */ + public function getCollector($name) + { + if (!isset($this->data['collectors'][$name])) { + throw new InvalidArgumentException("Invalid debug collector '$name'"); + } + + return $this->data['collectors'][$name]; + } + + /** + * @return \Symfony\Component\HttpKernel\DataCollector\DataCollectorInterface[] + */ + public function getAllCollectors() + { + return $this->data['collectors']; + } + + /** + * Returns toolbar template for given collector name. + * + * @param string $collectorName Name of corresponding collector. + * + * @return string + */ + public function getToolbarTemplate($collectorName) + { + if (!isset($this->data['toolbarTemplates'][$collectorName])) { + return null; + } + + return $this->data['toolbarTemplates'][$collectorName]; + } + + /** + * Returns panel template to use for given collector name. + * + * @param string $collectorName Name of corresponding collector. + * + * @return string + */ + public function getPanelTemplate($collectorName) + { + if (!isset($this->data['panelTemplates'][$collectorName])) { + return null; + } + + return $this->data['panelTemplates'][$collectorName]; + } + + /** + * {@inheritdoc} + */ + public function reset() + { + $this->data = [ + 'collectors' => [], + 'panelTemplates' => [], + 'toolbarTemplates' => [], + ]; + } +} + +class_alias(IbexaCoreCollector::class, 'eZ\Bundle\EzPublishDebugBundle\Collector\EzPublishCoreCollector'); diff --git a/eZ/Bundle/EzPublishDebugBundle/Collector/PersistenceCacheCollector.php b/src/bundle/Debug/Collector/PersistenceCacheCollector.php similarity index 89% rename from eZ/Bundle/EzPublishDebugBundle/Collector/PersistenceCacheCollector.php rename to src/bundle/Debug/Collector/PersistenceCacheCollector.php index 205cf8833a..c81df9f417 100644 --- a/eZ/Bundle/EzPublishDebugBundle/Collector/PersistenceCacheCollector.php +++ b/src/bundle/Debug/Collector/PersistenceCacheCollector.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishDebugBundle\Collector; +namespace Ibexa\Bundle\Debug\Collector; -use eZ\Publish\Core\Persistence\Cache\PersistenceLogger; +use Ibexa\Core\Persistence\Cache\PersistenceLogger; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DataCollector\DataCollector; @@ -16,7 +16,7 @@ */ class PersistenceCacheCollector extends DataCollector { - /** @var \eZ\Publish\Core\Persistence\Cache\PersistenceLogger */ + /** @var \Ibexa\Core\Persistence\Cache\PersistenceLogger */ private $logger; public function __construct(PersistenceLogger $logger) @@ -134,13 +134,11 @@ public function getHandlers() } /** - * Returns un cached handlers being loaded. - * - * @return array + * Returns uncached handlers being loaded. */ - public function getHandlersCount() + public function getHandlersCount(): int { - return array_sum($this->data['handlers']); + return (int)array_sum($this->data['handlers']); } public function reset(): void @@ -148,3 +146,5 @@ public function reset(): void $this->data = []; } } + +class_alias(PersistenceCacheCollector::class, 'eZ\Bundle\EzPublishDebugBundle\Collector\PersistenceCacheCollector'); diff --git a/eZ/Bundle/EzPublishDebugBundle/Collector/SiteAccessCollector.php b/src/bundle/Debug/Collector/SiteAccessCollector.php similarity index 80% rename from eZ/Bundle/EzPublishDebugBundle/Collector/SiteAccessCollector.php rename to src/bundle/Debug/Collector/SiteAccessCollector.php index eefff50666..6ccd04b427 100644 --- a/eZ/Bundle/EzPublishDebugBundle/Collector/SiteAccessCollector.php +++ b/src/bundle/Debug/Collector/SiteAccessCollector.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishDebugBundle\Collector; +namespace Ibexa\Bundle\Debug\Collector; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\DataCollector\DataCollector; @@ -31,7 +31,7 @@ public function getName() /** * Returns siteAccess. * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess + * @return \Ibexa\Core\MVC\Symfony\SiteAccess */ public function getSiteAccess() { @@ -43,3 +43,5 @@ public function reset(): void $this->data = []; } } + +class_alias(SiteAccessCollector::class, 'eZ\Bundle\EzPublishDebugBundle\Collector\SiteAccessCollector'); diff --git a/src/bundle/Debug/DependencyInjection/Compiler/DataCollectorPass.php b/src/bundle/Debug/DependencyInjection/Compiler/DataCollectorPass.php new file mode 100644 index 0000000000..f5fceeaedc --- /dev/null +++ b/src/bundle/Debug/DependencyInjection/Compiler/DataCollectorPass.php @@ -0,0 +1,38 @@ +hasDefinition(IbexaCoreCollector::class)) { + return; + } + + $dataCollectorDef = $container->getDefinition(IbexaCoreCollector::class); + foreach ($container->findTaggedServiceIds('ibexa.debug.data_collector') as $id => $attributes) { + foreach ($attributes as $attribute) { + $dataCollectorDef->addMethodCall( + 'addCollector', + [ + new Reference($id), + isset($attribute['panelTemplate']) ? $attribute['panelTemplate'] : null, + isset($attribute['toolbarTemplate']) ? $attribute['toolbarTemplate'] : null, + ] + ); + } + } + } +} + +class_alias(DataCollectorPass::class, 'eZ\Bundle\EzPublishDebugBundle\DependencyInjection\Compiler\DataCollectorPass'); diff --git a/src/bundle/Debug/DependencyInjection/IbexaDebugExtension.php b/src/bundle/Debug/DependencyInjection/IbexaDebugExtension.php new file mode 100644 index 0000000000..418e4a6974 --- /dev/null +++ b/src/bundle/Debug/DependencyInjection/IbexaDebugExtension.php @@ -0,0 +1,35 @@ +load('services.yml'); + } +} + +class_alias(IbexaDebugExtension::class, 'eZ\Bundle\EzPublishDebugBundle\DependencyInjection\EzPublishDebugExtension'); diff --git a/src/bundle/Debug/IbexaDebugBundle.php b/src/bundle/Debug/IbexaDebugBundle.php new file mode 100644 index 0000000000..eeca1ebdee --- /dev/null +++ b/src/bundle/Debug/IbexaDebugBundle.php @@ -0,0 +1,22 @@ +addCompilerPass(new DataCollectorPass()); + } +} + +class_alias(IbexaDebugBundle::class, 'eZ\Bundle\EzPublishDebugBundle\EzPublishDebugBundle'); diff --git a/src/bundle/Debug/Resources/config/services.yml b/src/bundle/Debug/Resources/config/services.yml new file mode 100644 index 0000000000..7358edeb1c --- /dev/null +++ b/src/bundle/Debug/Resources/config/services.yml @@ -0,0 +1,27 @@ +services: + Ibexa\Bundle\Debug\Collector\IbexaCoreCollector: + class: Ibexa\Bundle\Debug\Collector\IbexaCoreCollector + tags: + - + name: data_collector + template: '@IbexaDebug/Profiler/layout.html.twig' + id: "ezpublish.debug.toolbar" + + Ibexa\Bundle\Debug\Collector\SiteAccessCollector: + class: Ibexa\Bundle\Debug\Collector\SiteAccessCollector + tags: + - + name: ibexa.debug.data_collector + id: "ezpublish.debug.siteaccess" + panelTemplate: '@IbexaDebug/Profiler/siteaccess/panel.html.twig' + toolbarTemplate: '@IbexaDebug/Profiler/siteaccess/toolbar.html.twig' + + Ibexa\Bundle\Debug\Collector\PersistenceCacheCollector: + class: Ibexa\Bundle\Debug\Collector\PersistenceCacheCollector + arguments: ['@Ibexa\Core\Persistence\Cache\PersistenceLogger'] + tags: + - + name: ibexa.debug.data_collector + id: "ezpublish.debug.persistence" + panelTemplate: '@IbexaDebug/Profiler/persistence/panel.html.twig' + toolbarTemplate: '@IbexaDebug/Profiler/persistence/toolbar.html.twig' diff --git a/src/bundle/Debug/Resources/views/Profiler/layout.html.twig b/src/bundle/Debug/Resources/views/Profiler/layout.html.twig new file mode 100644 index 0000000000..5a0ee04b15 --- /dev/null +++ b/src/bundle/Debug/Resources/views/Profiler/layout.html.twig @@ -0,0 +1,62 @@ +{% extends '@WebProfiler/Profiler/layout.html.twig' %} + +{% macro ibexa_logo() %} + + + + + + + + + + +{% endmacro %} + +{% block toolbar %} + {% set icon %} + {{ _self.ibexa_logo() }} + {% endset %} + + {% set text %} + {% for name, inner_collector in collector.allCollectors %} + {% set inner_template = collector.getToolbarTemplate( name ) %} + {% if inner_template %} + {% include inner_template with { "collector": inner_collector } %} + + {% if not loop.last %}
    {% endif %} + {% endif %} + + {% endfor %} + + {% endset %} + + {# Set to red if over 100 uncached, and to yellow if either over 15 uncached or over 100 cache hits lookups #} + {% set stats = collector.getCollector('ezpublish.debug.persistence').stats %} + {% set total_uncached = stats.uncached + stats.miss %} + {% set status_logo = total_uncached > 100 ? 'red' : (total_uncached > 15 ? 'yellow' : (stats.hit > 100 ? 'yellow' : '')) %} + + {{ include('@WebProfiler/Profiler/toolbar_item.html.twig', { link: profiler_url, status: status_logo|default('') }) }} +{% endblock %} + +{% block menu %} + + + {{ _self.ibexa_logo() }} + + Ibexa DXP + +{% endblock %} + +{% block panel %} +

    Usage Information

    + + {% for name, inner_collector in collector.allCollectors %} + {% set inner_template = collector.getPanelTemplate( name ) %} + {% if inner_template %}{% include inner_template with { "collector": inner_collector } %}{% endif %} + + {% if not loop.last %}
    {% endif %} + + {% endfor %} + +{% endblock %} diff --git a/eZ/Bundle/EzPublishDebugBundle/Resources/views/Profiler/persistence/panel.html.twig b/src/bundle/Debug/Resources/views/Profiler/persistence/panel.html.twig similarity index 92% rename from eZ/Bundle/EzPublishDebugBundle/Resources/views/Profiler/persistence/panel.html.twig rename to src/bundle/Debug/Resources/views/Profiler/persistence/panel.html.twig index e70f6d567e..fcc6a8bdfb 100644 --- a/eZ/Bundle/EzPublishDebugBundle/Resources/views/Profiler/persistence/panel.html.twig +++ b/src/bundle/Debug/Resources/views/Profiler/persistence/panel.html.twig @@ -81,5 +81,5 @@ {% else %} -

    NOTE: Call logging is by default only enabled in debug mode as ezpublish.spi.persistence.cache.persistenceLogger.enableCallLogging: "%kernel.debug%", enable debug or change the setting in order to see calls made and trace for where the calls comes from.

    +

    NOTE: Call logging is by default only enabled in debug mode as ibexa.spi.persistence.cache.persistenceLogger.enableCallLogging: "%kernel.debug%", enable debug or change the setting in order to see calls made and trace for where the calls comes from.

    {% endif %} diff --git a/eZ/Bundle/EzPublishDebugBundle/Resources/views/Profiler/persistence/toolbar.html.twig b/src/bundle/Debug/Resources/views/Profiler/persistence/toolbar.html.twig similarity index 100% rename from eZ/Bundle/EzPublishDebugBundle/Resources/views/Profiler/persistence/toolbar.html.twig rename to src/bundle/Debug/Resources/views/Profiler/persistence/toolbar.html.twig diff --git a/eZ/Bundle/EzPublishDebugBundle/Resources/views/Profiler/siteaccess/panel.html.twig b/src/bundle/Debug/Resources/views/Profiler/siteaccess/panel.html.twig similarity index 100% rename from eZ/Bundle/EzPublishDebugBundle/Resources/views/Profiler/siteaccess/panel.html.twig rename to src/bundle/Debug/Resources/views/Profiler/siteaccess/panel.html.twig diff --git a/eZ/Bundle/EzPublishDebugBundle/Resources/views/Profiler/siteaccess/toolbar.html.twig b/src/bundle/Debug/Resources/views/Profiler/siteaccess/toolbar.html.twig similarity index 100% rename from eZ/Bundle/EzPublishDebugBundle/Resources/views/Profiler/siteaccess/toolbar.html.twig rename to src/bundle/Debug/Resources/views/Profiler/siteaccess/toolbar.html.twig diff --git a/src/bundle/IO/ApiLoader/HandlerRegistry.php b/src/bundle/IO/ApiLoader/HandlerRegistry.php new file mode 100644 index 0000000000..3e568928fb --- /dev/null +++ b/src/bundle/IO/ApiLoader/HandlerRegistry.php @@ -0,0 +1,45 @@ +handlersMap = $handlersMap; + } + + /** + * @param string $handlerName + * + * @return object an instance of the requested handler + * + * @throws \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException If the requested handler doesn't exist + */ + public function getConfiguredHandler($handlerName) + { + if (!isset($this->handlersMap[$handlerName])) { + throw new InvalidConfigurationException("Unknown handler $handlerName"); + } + + return $this->handlersMap[$handlerName]; + } +} + +class_alias(HandlerRegistry::class, 'eZ\Bundle\EzPublishIOBundle\ApiLoader\HandlerRegistry'); diff --git a/eZ/Bundle/EzPublishIOBundle/BinaryStreamResponse.php b/src/bundle/IO/BinaryStreamResponse.php similarity index 92% rename from eZ/Bundle/EzPublishIOBundle/BinaryStreamResponse.php rename to src/bundle/IO/BinaryStreamResponse.php index 3f4058fb19..d1f0638f0a 100644 --- a/eZ/Bundle/EzPublishIOBundle/BinaryStreamResponse.php +++ b/src/bundle/IO/BinaryStreamResponse.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishIOBundle; +namespace Ibexa\Bundle\IO; use DateTime; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\BinaryFile; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\BinaryFile; use LogicException; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\Request; @@ -21,10 +21,10 @@ class BinaryStreamResponse extends Response { protected static $trustXSendfileTypeHeader = false; - /** @var \eZ\Publish\Core\IO\Values\BinaryFile */ + /** @var \Ibexa\Core\IO\Values\BinaryFile */ protected $file; - /** @var \eZ\Publish\Core\IO\IOServiceInterface */ + /** @var \Ibexa\Core\IO\IOServiceInterface */ protected $ioService; protected $offset; @@ -34,8 +34,8 @@ class BinaryStreamResponse extends Response /** * Constructor. * - * @param \eZ\Publish\Core\IO\Values\BinaryFile $binaryFile The name of the file to stream - * @param \eZ\Publish\Core\IO\IOServiceInterface $ioService The name of the file to stream + * @param \Ibexa\Core\IO\Values\BinaryFile $binaryFile The name of the file to stream + * @param \Ibexa\Core\IO\IOServiceInterface $ioService The name of the file to stream * @param int $status The response status code * @param array $headers An array of response headers * @param bool $public Files are public by default @@ -84,7 +84,7 @@ public function setFile($file, $contentDisposition = null, $autoLastModified = t /** * Gets the file. * - * @return \eZ\Publish\Core\IO\Values\BinaryFile The file to stream + * @return \Ibexa\Core\IO\Values\BinaryFile The file to stream */ public function getFile() { @@ -225,3 +225,5 @@ public function getContent() return null; } } + +class_alias(BinaryStreamResponse::class, 'eZ\Bundle\EzPublishIOBundle\BinaryStreamResponse'); diff --git a/eZ/Bundle/EzPublishIOBundle/Command/MigrateFilesCommand.php b/src/bundle/IO/Command/MigrateFilesCommand.php similarity index 95% rename from eZ/Bundle/EzPublishIOBundle/Command/MigrateFilesCommand.php rename to src/bundle/IO/Command/MigrateFilesCommand.php index ce2ee6edc0..213369d40f 100644 --- a/eZ/Bundle/EzPublishIOBundle/Command/MigrateFilesCommand.php +++ b/src/bundle/IO/Command/MigrateFilesCommand.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishIOBundle\Command; +namespace Ibexa\Bundle\IO\Command; -use eZ\Bundle\EzPublishCoreBundle\Command\BackwardCompatibleCommand; -use eZ\Bundle\EzPublishIOBundle\Migration\FileListerRegistry; -use eZ\Bundle\EzPublishIOBundle\Migration\FileMigratorInterface; +use Ibexa\Bundle\Core\Command\BackwardCompatibleCommand; +use Ibexa\Bundle\IO\Migration\FileListerRegistry; +use Ibexa\Bundle\IO\Migration\FileMigratorInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputInterface; @@ -24,13 +24,13 @@ final class MigrateFilesCommand extends Command implements BackwardCompatibleCom /** @var mixed Configuration for binary data handlers */ private $configuredBinarydataHandlers; - /** @var \eZ\Bundle\EzPublishIOBundle\Migration\FileListerRegistry */ + /** @var \Ibexa\Bundle\IO\Migration\FileListerRegistry */ private $fileListerRegistry; - /** @var \eZ\Bundle\EzPublishIOBundle\Migration\FileListerInterface[] */ + /** @var \Ibexa\Bundle\IO\Migration\FileListerInterface[] */ private $fileListers; - /** @var \eZ\Bundle\EzPublishIOBundle\Migration\FileMigratorInterface */ + /** @var \Ibexa\Bundle\IO\Migration\FileMigratorInterface */ private $fileMigrator; public function __construct( @@ -127,7 +127,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $output->writeln([ - "Migrating from '$fromHandlers[0],$fromHandlers[1]' to '$toHandlers[0],$toHandlers[1]'", + "Migrating from '{$fromHandlers[0]},{$fromHandlers[1]}' to '{$toHandlers[0]},{$toHandlers[1]}'", '', ]); @@ -324,3 +324,5 @@ public function getDeprecatedAliases(): array return ['ezplatform:io:migrate-files']; } } + +class_alias(MigrateFilesCommand::class, 'eZ\Bundle\EzPublishIOBundle\Command\MigrateFilesCommand'); diff --git a/src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php b/src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php new file mode 100644 index 0000000000..097b94c782 --- /dev/null +++ b/src/bundle/IO/DependencyInjection/Compiler/IOConfigurationPass.php @@ -0,0 +1,125 @@ +metadataHandlerFactories = $metadataHandlerFactories; + $this->binarydataHandlerFactories = $binarydataHandlerFactories; + } + + /** + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * + * @throws \LogicException + */ + public function process(ContainerBuilder $container) + { + $ioMetadataHandlers = $container->hasParameter('ibexa.io.metadata_handlers') ? + $container->getParameter('ibexa.io.metadata_handlers') : + []; + $this->processHandlers( + $container, + $container->getDefinition('ibexa.core.io.metadata_handler.registry'), + $ioMetadataHandlers, + $this->metadataHandlerFactories, + 'ibexa.core.io.metadata_handler.flysystem.default' + ); + + $ioBinarydataHandlers = $container->hasParameter('ibexa.io.binarydata_handlers') ? + $container->getParameter('ibexa.io.binarydata_handlers') : + []; + $this->processHandlers( + $container, + $container->getDefinition('ibexa.core.io.binarydata_handler.registry'), + $ioBinarydataHandlers, + $this->binarydataHandlerFactories, + 'ibexa.core.io.binarydata_handler.flysystem.default' + ); + + // Unset parameters that are no longer required ? + } + + /** + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param \Symfony\Component\DependencyInjection\Definition $factory The factory service that should receive the list of handlers + * @param array $configuredHandlers Handlers configuration declared via semantic config + * @param \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory[]|\ArrayObject $factories Map of alias => handler service id + * @param string $defaultHandler default handler id + */ + protected function processHandlers( + ContainerBuilder $container, + Definition $factory, + array $configuredHandlers, + ArrayObject $factories, + $defaultHandler + ) { + $handlers = ['default' => new Reference($defaultHandler)]; + + foreach ($configuredHandlers as $name => $config) { + $configurationFactory = $this->getFactory($factories, $config['type'], $container); + + $parentHandlerId = $configurationFactory->getParentServiceId(); + $handlerId = sprintf('%s.%s', $parentHandlerId, $name); + $handlerServiceDefinition = new ChildDefinition($parentHandlerId); + $definition = $container->setDefinition($handlerId, $handlerServiceDefinition); + + $configurationFactory->configureHandler($definition, $config); + + $handlers[$name] = new Reference($handlerId); + } + + $factory->addMethodCall('setHandlersMap', [$handlers]); + } + + /** + * Returns from $factories the factory for handler $type. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory[]|\ArrayObject $factories + * @param string $type + * + * @return \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory + */ + protected function getFactory(ArrayObject $factories, $type, ContainerBuilder $container) + { + if (!isset($factories[$type])) { + throw new InvalidConfigurationException("Unknown handler type $type"); + } + if ($factories[$type] instanceof ContainerAwareInterface) { + $factories[$type]->setContainer($container); + } + + return $factories[$type]; + } +} + +class_alias(IOConfigurationPass::class, 'eZ\Bundle\EzPublishIOBundle\DependencyInjection\Compiler\IOConfigurationPass'); diff --git a/src/bundle/IO/DependencyInjection/Compiler/MigrationFileListerPass.php b/src/bundle/IO/DependencyInjection/Compiler/MigrationFileListerPass.php new file mode 100644 index 0000000000..2f5908cd97 --- /dev/null +++ b/src/bundle/IO/DependencyInjection/Compiler/MigrationFileListerPass.php @@ -0,0 +1,41 @@ +has(ConfigurableRegistry::class)) { + return; + } + + $fileListersTagged = $container->findTaggedServiceIds('ibexa.io.migration.file_lister'); + + $fileListers = []; + foreach ($fileListersTagged as $id => $tags) { + foreach ($tags as $attributes) { + $fileListers[$attributes['identifier']] = new Reference($id); + } + } + + $fileListerRegistryDef = $container->findDefinition(ConfigurableRegistry::class); + $fileListerRegistryDef->setArguments([$fileListers]); + } +} + +class_alias(MigrationFileListerPass::class, 'eZ\Bundle\EzPublishIOBundle\DependencyInjection\Compiler\MigrationFileListerPass'); diff --git a/src/bundle/IO/DependencyInjection/Configuration.php b/src/bundle/IO/DependencyInjection/Configuration.php new file mode 100644 index 0000000000..0d599ad820 --- /dev/null +++ b/src/bundle/IO/DependencyInjection/Configuration.php @@ -0,0 +1,80 @@ +metadataHandlerFactories = $factories; + } + + public function setBinarydataHandlerFactories(ArrayObject $factories) + { + $this->binarydataHandlerFactories = $factories; + } + + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(IbexaIOExtension::EXTENSION_NAME); + + $rootNode = $treeBuilder->getRootNode(); + + $this->addHandlersSection( + $rootNode, + 'metadata_handlers', + 'Handlers for files metadata, that read & write files metadata (size, modification time...)', + $this->metadataHandlerFactories + ); + $this->addHandlersSection( + $rootNode, + 'binarydata_handlers', + 'Handlers for files binary data. Reads & write files binary content', + $this->binarydataHandlerFactories + ); + + $rootNode->children()->end(); + + return $treeBuilder; + } + + /** + * @param \Symfony\Component\Config\Definition\Builder\NodeDefinition $node + * @param string $name + * @param string $info block info line + * @param ConfigurationFactory[]|\ArrayObject $factories + */ + private function addHandlersSection(NodeDefinition $node, $name, $info, ArrayObject $factories) + { + $handlersNodeBuilder = $node + ->children() + ->arrayNode($name) + ->info($info) + ->useAttributeAsKey('name') + ->prototype('array') + ->performNoDeepMerging() + ->children(); + + foreach ($factories as $name => $factory) { + $factoryNode = $handlersNodeBuilder->arrayNode($name)->canBeUnset(); + $factory->addConfiguration($factoryNode); + } + } +} + +class_alias(Configuration::class, 'eZ\Bundle\EzPublishIOBundle\DependencyInjection\Configuration'); diff --git a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/ConfigurationFactory.php b/src/bundle/IO/DependencyInjection/ConfigurationFactory.php similarity index 92% rename from eZ/Bundle/EzPublishIOBundle/DependencyInjection/ConfigurationFactory.php rename to src/bundle/IO/DependencyInjection/ConfigurationFactory.php index b9ecdc6026..8feeda29de 100644 --- a/eZ/Bundle/EzPublishIOBundle/DependencyInjection/ConfigurationFactory.php +++ b/src/bundle/IO/DependencyInjection/ConfigurationFactory.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishIOBundle\DependencyInjection; +namespace Ibexa\Bundle\IO\DependencyInjection; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; use Symfony\Component\DependencyInjection\Definition as ServiceDefinition; @@ -57,3 +57,5 @@ public function getParentServiceId(); */ public function configureHandler(ServiceDefinition $serviceDefinition, array $config); } + +class_alias(ConfigurationFactory::class, 'eZ\Bundle\EzPublishIOBundle\DependencyInjection\ConfigurationFactory'); diff --git a/src/bundle/IO/DependencyInjection/ConfigurationFactory/BinarydataHandler/Flysystem.php b/src/bundle/IO/DependencyInjection/ConfigurationFactory/BinarydataHandler/Flysystem.php new file mode 100644 index 0000000000..f463dfddf1 --- /dev/null +++ b/src/bundle/IO/DependencyInjection/ConfigurationFactory/BinarydataHandler/Flysystem.php @@ -0,0 +1,19 @@ +info( + 'Handler based on league/flysystem, an abstract filesystem library. ' . + 'Yes, the metadata handler and binarydata handler look the same; it is NOT a mistake :)' + ) + ->children() + ->scalarNode('adapter') + ->info( + 'Flysystem adapter identifier. Should be configured using oneup flysystem bundle. ' . + 'Yes, the same adapter can be used for a binarydata and metadata handler' + ) + ->isRequired() + ->example('nfs') + ->end() + ->end(); + } + + public function configureHandler(ServiceDefinition $definition, array $config) + { + $filesystemId = $this->createFilesystem($this->container, $config['name'], $config['adapter']); + $definition->replaceArgument(0, new Reference($filesystemId)); + } + + /** + * Creates a flysystem filesystem $name service. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param string $name filesystem name (nfs, local...) + * @param string $adapter adapter name + * + * @return string + */ + private function createFilesystem(ContainerBuilder $container, $name, $adapter) + { + $adapterId = sprintf('oneup_flysystem.%s_adapter', $adapter); + // has either definition or alias + if (!$container->has($adapterId)) { + throw new InvalidConfigurationException("Unknown flysystem adapter $adapter"); + } + + $filesystemId = sprintf('ezpublish.core.io.flysystem.%s_filesystem', $name); + $filesystemServiceDefinition = new ChildDefinition('ibexa.core.io.flysystem.base_filesystem'); + $definition = $container->setDefinition( + $filesystemId, + $filesystemServiceDefinition + ); + $definition->setArguments([new Reference($adapterId)]); + + return $filesystemId; + } +} + +class_alias(Flysystem::class, 'eZ\Bundle\EzPublishIOBundle\DependencyInjection\ConfigurationFactory\Flysystem'); diff --git a/src/bundle/IO/DependencyInjection/ConfigurationFactory/MetadataHandler/Flysystem.php b/src/bundle/IO/DependencyInjection/ConfigurationFactory/MetadataHandler/Flysystem.php new file mode 100644 index 0000000000..b31246e49f --- /dev/null +++ b/src/bundle/IO/DependencyInjection/ConfigurationFactory/MetadataHandler/Flysystem.php @@ -0,0 +1,19 @@ +replaceArgument(0, new Reference($config['connection'])); + } + + public function addConfiguration(ArrayNodeDefinition $node) + { + $node + ->info( + 'A MySQL based handler, compatible with the legacy DFS one, that stores metadata in the ezdfsfile table' + ) + ->children() + ->scalarNode('connection') + ->info('Doctrine connection service') + ->example('doctrine.dbal.cluster_connection') + ->end() + ->end(); + } +} + +class_alias(LegacyDFSCluster::class, 'eZ\Bundle\EzPublishIOBundle\DependencyInjection\ConfigurationFactory\MetadataHandler\LegacyDFSCluster'); diff --git a/src/bundle/IO/DependencyInjection/IbexaIOExtension.php b/src/bundle/IO/DependencyInjection/IbexaIOExtension.php new file mode 100644 index 0000000000..5a558db932 --- /dev/null +++ b/src/bundle/IO/DependencyInjection/IbexaIOExtension.php @@ -0,0 +1,131 @@ +metadataHandlerFactories = new ArrayObject(); + $this->binarydataHandlerFactories = new ArrayObject(); + } + + /** + * Registers a metadata handler configuration $factory for handler with $alias. + * + * @param string $alias + * @param \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory $factory + */ + public function addMetadataHandlerFactory($alias, ConfigurationFactory $factory) + { + $this->metadataHandlerFactories[$alias] = $factory; + } + + /** + * Registers a binarydata handler configuration $factory for handler with $alias. + * + * @param string $alias + * @param \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory $factory + */ + public function addBinarydataHandlerFactory($alias, ConfigurationFactory $factory) + { + $this->binarydataHandlerFactories[$alias] = $factory; + } + + /** + * @return \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory[]|\ArrayObject + */ + public function getMetadataHandlerFactories() + { + return $this->metadataHandlerFactories; + } + + /** + * @return \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory[]|\ArrayObject + */ + public function getBinarydataHandlerFactories() + { + return $this->binarydataHandlerFactories; + } + + public function getAlias() + { + return self::EXTENSION_NAME; + } + + /** + * {@inheritdoc} + */ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); + + $configuration = $this->getConfiguration($configs, $container); + + $config = $this->processConfiguration($configuration, $configs); + + $loader->load('io.yml'); + $loader->load('default_settings.yml'); + + $this->processHandlers($container, $config, 'metadata_handlers'); + $this->processHandlers($container, $config, 'binarydata_handlers'); + } + + /** + * Processes the config key $key, and registers the result in ez_io.$key. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $container + * @param string $key Configuration key, either binarydata or metadata + */ + private function processHandlers(ContainerBuilder $container, $config, $key) + { + $handlers = []; + if (isset($config[$key])) { + foreach ($config[$key] as $name => $value) { + if (isset($handlers[$name])) { + throw new InvalidConfigurationException("A $key called $name already exists"); + } + $handlerConfig = current($value); + $handlerConfig['type'] = key($value); + $handlerConfig['name'] = $name; + $handlers[$name] = $handlerConfig; + } + } + $container->setParameter("ibexa.io.{$key}", $handlers); + } + + public function getConfiguration(array $config, ContainerBuilder $container) + { + $configuration = new Configuration(); + $configuration->setMetadataHandlerFactories($this->getMetadataHandlerFactories()); + $configuration->setBinarydataHandlerFactories($this->getBinarydataHandlerFactories()); + + return $configuration; + } +} + +class_alias(IbexaIOExtension::class, 'eZ\Bundle\EzPublishIOBundle\DependencyInjection\EzPublishIOExtension'); diff --git a/eZ/Bundle/EzPublishIOBundle/EventListener/StreamFileListener.php b/src/bundle/IO/EventListener/StreamFileListener.php similarity index 84% rename from eZ/Bundle/EzPublishIOBundle/EventListener/StreamFileListener.php rename to src/bundle/IO/EventListener/StreamFileListener.php index 641a3bd2fd..19adcca341 100644 --- a/eZ/Bundle/EzPublishIOBundle/EventListener/StreamFileListener.php +++ b/src/bundle/IO/EventListener/StreamFileListener.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishIOBundle\EventListener; +namespace Ibexa\Bundle\IO\EventListener; -use eZ\Bundle\EzPublishIOBundle\BinaryStreamResponse; -use eZ\Publish\Core\IO\IOConfigProvider; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\MissingBinaryFile; +use Ibexa\Bundle\IO\BinaryStreamResponse; +use Ibexa\Core\IO\IOConfigProvider; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\MissingBinaryFile; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -21,10 +21,10 @@ */ class StreamFileListener implements EventSubscriberInterface { - /** @var \eZ\Publish\Core\IO\IOServiceInterface */ + /** @var \Ibexa\Core\IO\IOServiceInterface */ private $ioService; - /** @var \eZ\Publish\Core\IO\IOConfigProvider */ + /** @var \Ibexa\Core\IO\IOConfigProvider */ private $ioConfigResolver; public function __construct(IOServiceInterface $ioService, IOConfigProvider $ioConfigResolver) @@ -86,3 +86,5 @@ private function isIoUri($uri, $urlPrefix) return strpos(ltrim($uri, '/'), $urlPrefix) === 0; } } + +class_alias(StreamFileListener::class, 'eZ\Bundle\EzPublishIOBundle\EventListener\StreamFileListener'); diff --git a/src/bundle/IO/IbexaIOBundle.php b/src/bundle/IO/IbexaIOBundle.php new file mode 100644 index 0000000000..4a526cb9ed --- /dev/null +++ b/src/bundle/IO/IbexaIOBundle.php @@ -0,0 +1,46 @@ +getContainerExtension(); + $container->addCompilerPass( + new Compiler\IOConfigurationPass( + $extension->getMetadataHandlerFactories(), + $extension->getBinarydataHandlerFactories() + ) + ); + $container->addCompilerPass(new Compiler\MigrationFileListerPass()); + parent::build($container); + } + + public function getContainerExtension() + { + if (!isset($this->extension)) { + $this->extension = new IbexaIOExtension(); + $this->extension->addMetadataHandlerFactory('flysystem', new ConfigurationFactory\MetadataHandler\Flysystem()); + $this->extension->addMetadataHandlerFactory('legacy_dfs_cluster', new ConfigurationFactory\MetadataHandler\LegacyDFSCluster()); + $this->extension->addBinarydataHandlerFactory('flysystem', new ConfigurationFactory\BinarydataHandler\Flysystem()); + } + + return $this->extension; + } +} + +class_alias(IbexaIOBundle::class, 'eZ\Bundle\EzPublishIOBundle\EzPublishIOBundle'); diff --git a/src/bundle/IO/Migration/FileLister/BinaryFileLister.php b/src/bundle/IO/Migration/FileLister/BinaryFileLister.php new file mode 100644 index 0000000000..f002b171c8 --- /dev/null +++ b/src/bundle/IO/Migration/FileLister/BinaryFileLister.php @@ -0,0 +1,71 @@ +fileList = $fileList; + $this->filesDir = $filesDir; + + $this->fileList->rewind(); + + parent::__construct($metadataHandlerRegistry, $binarydataHandlerRegistry, $logger); + } + + public function countFiles() + { + return count($this->fileList); + } + + public function loadMetadataList($limit = null, $offset = null) + { + $metadataList = []; + $fileLimitList = new LimitIterator($this->fileList, $offset, $limit); + + foreach ($fileLimitList as $fileId) { + try { + $metadataList[] = $this->fromMetadataHandler->load($this->filesDir . '/' . $fileId); + } catch (BinaryFileNotFoundException $e) { + $this->logMissingFile($fileId); + + continue; + } + } + + return $metadataList; + } +} + +class_alias(BinaryFileLister::class, 'eZ\Bundle\EzPublishIOBundle\Migration\FileLister\BinaryFileLister'); diff --git a/src/bundle/IO/Migration/FileLister/FileIterator/LegacyStorageFileIterator.php b/src/bundle/IO/Migration/FileLister/FileIterator/LegacyStorageFileIterator.php new file mode 100644 index 0000000000..57ec109144 --- /dev/null +++ b/src/bundle/IO/Migration/FileLister/FileIterator/LegacyStorageFileIterator.php @@ -0,0 +1,82 @@ +rowReader = $rowReader; + } + + #[\ReturnTypeWillChange] + public function current() + { + return $this->item; + } + + public function next(): void + { + $this->fetchRow(); + } + + #[\ReturnTypeWillChange] + public function key() + { + return $this->cursor; + } + + public function valid(): bool + { + return $this->cursor < $this->count(); + } + + public function rewind(): void + { + $this->cursor = -1; + $this->rowReader->init(); + $this->fetchRow(); + } + + public function count(): int + { + return $this->rowReader->getCount(); + } + + /** + * Fetches the next item from the resultset and moves the cursor forward. + */ + private function fetchRow() + { + ++$this->cursor; + $fileId = $this->rowReader->getRow(); + + $this->item = $fileId; + } +} + +class_alias(LegacyStorageFileIterator::class, 'eZ\Bundle\EzPublishIOBundle\Migration\FileLister\FileIterator\LegacyStorageFileIterator'); diff --git a/src/bundle/IO/Migration/FileLister/FileIteratorInterface.php b/src/bundle/IO/Migration/FileLister/FileIteratorInterface.php new file mode 100644 index 0000000000..5e377135c0 --- /dev/null +++ b/src/bundle/IO/Migration/FileLister/FileIteratorInterface.php @@ -0,0 +1,19 @@ +registry = $items; + } + + /** + * Returns the FileListerInterface matching the argument. + * + * @param string $identifier An identifier string. + * + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException If no FileListerInterface exists with this identifier + * + * @return \Ibexa\Bundle\IO\Migration\FileListerInterface The FileListerInterface given by the identifier. + */ + public function getItem($identifier) + { + if (isset($this->registry[$identifier])) { + return $this->registry[$identifier]; + } + + throw new NotFoundException('Migration file lister', $identifier); + } + + /** + * Returns the identifiers of all registered FileListerInterfaces. + * + * @return string[] Array of identifier strings. + */ + public function getIdentifiers() + { + return array_keys($this->registry); + } +} + +class_alias(ConfigurableRegistry::class, 'eZ\Bundle\EzPublishIOBundle\Migration\FileListerRegistry\ConfigurableRegistry'); diff --git a/eZ/Bundle/EzPublishIOBundle/Migration/FileMigrator/FileMigrator.php b/src/bundle/IO/Migration/FileMigrator/FileMigrator.php similarity index 85% rename from eZ/Bundle/EzPublishIOBundle/Migration/FileMigrator/FileMigrator.php rename to src/bundle/IO/Migration/FileMigrator/FileMigrator.php index fbd68d4c66..fec473538c 100644 --- a/eZ/Bundle/EzPublishIOBundle/Migration/FileMigrator/FileMigrator.php +++ b/src/bundle/IO/Migration/FileMigrator/FileMigrator.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishIOBundle\Migration\FileMigrator; +namespace Ibexa\Bundle\IO\Migration\FileMigrator; -use eZ\Bundle\EzPublishIOBundle\Migration\FileMigratorInterface; -use eZ\Bundle\EzPublishIOBundle\Migration\MigrationHandler; -use eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException; -use eZ\Publish\SPI\IO\BinaryFile; -use eZ\Publish\SPI\IO\BinaryFileCreateStruct; +use Ibexa\Bundle\IO\Migration\FileMigratorInterface; +use Ibexa\Bundle\IO\Migration\MigrationHandler; +use Ibexa\Contracts\Core\IO\BinaryFile; +use Ibexa\Contracts\Core\IO\BinaryFileCreateStruct; +use Ibexa\Core\IO\Exception\BinaryFileNotFoundException; final class FileMigrator extends MigrationHandler implements FileMigratorInterface { @@ -77,3 +77,5 @@ private function migrateMetadata(BinaryFile $binaryFile): bool return true; } } + +class_alias(FileMigrator::class, 'eZ\Bundle\EzPublishIOBundle\Migration\FileMigrator\FileMigrator'); diff --git a/src/bundle/IO/Migration/FileMigratorInterface.php b/src/bundle/IO/Migration/FileMigratorInterface.php new file mode 100644 index 0000000000..b84c57bcf7 --- /dev/null +++ b/src/bundle/IO/Migration/FileMigratorInterface.php @@ -0,0 +1,26 @@ +logInfo("File with id $id not found"); } } + +class_alias(MigrationHandler::class, 'eZ\Bundle\EzPublishIOBundle\Migration\MigrationHandler'); diff --git a/eZ/Bundle/EzPublishIOBundle/Migration/MigrationHandlerInterface.php b/src/bundle/IO/Migration/MigrationHandlerInterface.php similarity index 84% rename from eZ/Bundle/EzPublishIOBundle/Migration/MigrationHandlerInterface.php rename to src/bundle/IO/Migration/MigrationHandlerInterface.php index bb0fc20490..793a715c98 100644 --- a/eZ/Bundle/EzPublishIOBundle/Migration/MigrationHandlerInterface.php +++ b/src/bundle/IO/Migration/MigrationHandlerInterface.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishIOBundle\Migration; +namespace Ibexa\Bundle\IO\Migration; interface MigrationHandlerInterface { @@ -26,3 +26,5 @@ public function setIODataHandlersByIdentifiers( $toBinarydataHandlerIdentifier ); } + +class_alias(MigrationHandlerInterface::class, 'eZ\Bundle\EzPublishIOBundle\Migration\MigrationHandlerInterface'); diff --git a/src/bundle/IO/Resources/config/default_settings.yml b/src/bundle/IO/Resources/config/default_settings.yml new file mode 100644 index 0000000000..712fc66a46 --- /dev/null +++ b/src/bundle/IO/Resources/config/default_settings.yml @@ -0,0 +1,4 @@ +parameters: + # User configured IO handlers + ibexa.io.metadata_handlers: {} + ibexa.io.binarydata_handlers: {} diff --git a/src/bundle/IO/Resources/config/io.yml b/src/bundle/IO/Resources/config/io.yml new file mode 100644 index 0000000000..cd694a0b2a --- /dev/null +++ b/src/bundle/IO/Resources/config/io.yml @@ -0,0 +1,139 @@ +services: + Ibexa\Bundle\IO\Command\MigrateFilesCommand: + class: Ibexa\Bundle\IO\Command\MigrateFilesCommand + arguments: + - '%ibexa.io.metadata_handlers%' + - '%ibexa.io.binarydata_handlers%' + - '@Ibexa\Bundle\IO\Migration\FileListerRegistry\ConfigurableRegistry' + - '@Ibexa\Bundle\IO\Migration\FileMigrator\FileMigrator' + tags: + - { name: console.command } + + Ibexa\Bundle\IO\Migration\FileListerRegistry\ConfigurableRegistry: + class: Ibexa\Bundle\IO\Migration\FileListerRegistry\ConfigurableRegistry + + Ibexa\Bundle\IO\Migration\MigrationHandler: + class: Ibexa\Bundle\IO\Migration\MigrationHandler + arguments: + - '@ibexa.core.io.metadata_handler.registry' + - '@ibexa.core.io.binarydata_handler.registry' + - "@logger" + + ibexa.core.io.migration.file_lister.binary_file_lister: + class: Ibexa\Bundle\IO\Migration\FileLister\BinaryFileLister + parent: Ibexa\Bundle\IO\Migration\MigrationHandler + arguments: + - '@ibexa.core.io.migration.file_lister.file_iterator.binary_file_iterator' + - "%ibexa.site_access.config.default.binary_dir%" + tags: + - { name: "ibexa.io.migration.file_lister", identifier: "binary_file" } + lazy: true + + ibexa.core.io.migration.file_lister.media_file_lister: + class: Ibexa\Bundle\IO\Migration\FileLister\BinaryFileLister + parent: Ibexa\Bundle\IO\Migration\MigrationHandler + arguments: + - '@ibexa.core.io.migration.file_lister.file_iterator.media_file_iterator' + - "%ibexa.site_access.config.default.binary_dir%" + tags: + - { name: "ibexa.io.migration.file_lister", identifier: "media_file" } + lazy: true + + Ibexa\Bundle\IO\Migration\FileLister\ImageFileLister: + class: Ibexa\Bundle\IO\Migration\FileLister\ImageFileLister + parent: Ibexa\Bundle\IO\Migration\MigrationHandler + arguments: + - '@Ibexa\Bundle\Core\Imagine\VariationPurger\LegacyStorageImageFileList' + - '@ibexa.image_alias.variation_path_generator' + - "@liip_imagine.filter.configuration" + - "%ibexa.site_access.config.default.image.published_images_dir%" + tags: + - { name: "ibexa.io.migration.file_lister", identifier: "image_file" } + lazy: true + + ibexa.core.io.migration.file_lister.file_iterator.binary_file_iterator: + class: Ibexa\Bundle\IO\Migration\FileLister\FileIterator\LegacyStorageFileIterator + arguments: + - '@Ibexa\Bundle\IO\Migration\FileLister\FileRowReader\LegacyStorageBinaryFileRowReader' + + ibexa.core.io.migration.file_lister.file_iterator.media_file_iterator: + class: Ibexa\Bundle\IO\Migration\FileLister\FileIterator\LegacyStorageFileIterator + arguments: + - '@Ibexa\Bundle\IO\Migration\FileLister\FileRowReader\LegacyStorageMediaFileRowReader' + + Ibexa\Bundle\IO\Migration\FileLister\FileRowReader\LegacyStorageFileRowReader: + arguments: + $connection: '@ibexa.persistence.connection' + + Ibexa\Bundle\IO\Migration\FileLister\FileRowReader\LegacyStorageBinaryFileRowReader: + class: Ibexa\Bundle\IO\Migration\FileLister\FileRowReader\LegacyStorageBinaryFileRowReader + parent: Ibexa\Bundle\IO\Migration\FileLister\FileRowReader\LegacyStorageFileRowReader + + Ibexa\Bundle\IO\Migration\FileLister\FileRowReader\LegacyStorageMediaFileRowReader: + class: Ibexa\Bundle\IO\Migration\FileLister\FileRowReader\LegacyStorageMediaFileRowReader + parent: Ibexa\Bundle\IO\Migration\FileLister\FileRowReader\LegacyStorageFileRowReader + + Ibexa\Bundle\IO\Migration\FileMigrator\FileMigrator: + class: Ibexa\Bundle\IO\Migration\FileMigrator\FileMigrator + parent: Ibexa\Bundle\IO\Migration\MigrationHandler + + # Builds the binarydata and metadata handler based on the siteaccess config + Ibexa\Core\IO\IOBinarydataHandler\SiteAccessDependentMetadataHandler: + class: Ibexa\Core\IO\IOBinarydataHandler\SiteAccessDependentMetadataHandler + arguments: + $configResolver: '@ibexa.config.resolver' + $dataHandlerRegistry: '@ibexa.core.io.metadata_handler.registry' + + Ibexa\Core\IO\IOBinarydataHandler\SiteAccessDependentBinaryDataHandler: + class: Ibexa\Core\IO\IOBinarydataHandler\SiteAccessDependentBinaryDataHandler + arguments: + $configResolver: '@ibexa.config.resolver' + $dataHandlerRegistry: '@ibexa.core.io.binarydata_handler.registry' + + ibexa.core.io.metadata_handler.registry: + class: Ibexa\Bundle\IO\ApiLoader\HandlerRegistry + + ibexa.core.io.binarydata_handler.registry: + class: Ibexa\Bundle\IO\ApiLoader\HandlerRegistry + + # Inject the siteaccess config into a few io services + Ibexa\Core\IO\UrlDecorator\AbsolutePrefix: + class: Ibexa\Core\IO\UrlDecorator\AbsolutePrefix + arguments: + - '@Ibexa\Core\IO\IOConfigProvider' + + # Default flysystem metadata handler + ibexa.core.io.metadata_handler.flysystem.default: + class: Ibexa\Core\IO\IOMetadataHandler\Flysystem + arguments: + - '@ibexa.core.io.flysystem.default_filesystem' + tags: + - { name: monolog.logger, channel: ibexa.core.io } + + # Base service for flysystem binarydata handler + ibexa.core.io.binarydata_handler.flysystem: + abstract: true + class: Ibexa\Core\IO\IOBinarydataHandler\Flysystem + arguments: + - ~ + - '@ibexa.core.io.default_url_decorator' + + # Default flysystem binarydata handler + ibexa.core.io.binarydata_handler.flysystem.default: + class: Ibexa\Core\IO\IOBinarydataHandler\Flysystem + arguments: + - '@ibexa.core.io.flysystem.default_filesystem' + - '@ibexa.core.io.default_url_decorator' + + Ibexa\Core\IO\UrlDecorator\Prefix: + class: Ibexa\Core\IO\UrlDecorator\Prefix + arguments: + - '@Ibexa\Core\IO\IOConfigProvider' + + Ibexa\Bundle\IO\EventListener\StreamFileListener: + class: Ibexa\Bundle\IO\EventListener\StreamFileListener + arguments: + - '@Ibexa\Core\FieldType\Image\IO\Legacy' + - '@Ibexa\Core\IO\IOConfigProvider' + tags: + - { name: kernel.event_subscriber } diff --git a/eZ/Bundle/EzPublishLegacySearchEngineBundle/ApiLoader/ConnectionFactory.php b/src/bundle/LegacySearchEngine/ApiLoader/ConnectionFactory.php similarity index 86% rename from eZ/Bundle/EzPublishLegacySearchEngineBundle/ApiLoader/ConnectionFactory.php rename to src/bundle/LegacySearchEngine/ApiLoader/ConnectionFactory.php index 99471918ac..be802fe2b7 100644 --- a/eZ/Bundle/EzPublishLegacySearchEngineBundle/ApiLoader/ConnectionFactory.php +++ b/src/bundle/LegacySearchEngine/ApiLoader/ConnectionFactory.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishLegacySearchEngineBundle\ApiLoader; +namespace Ibexa\Bundle\LegacySearchEngine\ApiLoader; -use eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider; +use Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider; use InvalidArgumentException; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; @@ -15,7 +15,7 @@ class ConnectionFactory implements ContainerAwareInterface { use ContainerAwareTrait; - /** @var \eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider */ + /** @var \Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider */ protected $repositoryConfigurationProvider; public function __construct(RepositoryConfigurationProvider $repositoryConfigurationProvider) @@ -53,3 +53,5 @@ public function getConnection() return $this->container->get($doctrineConnectionId); } } + +class_alias(ConnectionFactory::class, 'eZ\Bundle\EzPublishLegacySearchEngineBundle\ApiLoader\ConnectionFactory'); diff --git a/src/bundle/LegacySearchEngine/DependencyInjection/IbexaLegacySearchEngineExtension.php b/src/bundle/LegacySearchEngine/DependencyInjection/IbexaLegacySearchEngineExtension.php new file mode 100644 index 0000000000..0f7f47aecb --- /dev/null +++ b/src/bundle/LegacySearchEngine/DependencyInjection/IbexaLegacySearchEngineExtension.php @@ -0,0 +1,38 @@ +load('search_engines/legacy.yml'); + + $loader = new YamlFileLoader( + $container, + new FileLocator(__DIR__ . '/../Resources/config') + ); + $loader->load('services.yml'); + } +} + +class_alias(IbexaLegacySearchEngineExtension::class, 'eZ\Bundle\EzPublishLegacySearchEngineBundle\DependencyInjection\EzPublishLegacySearchEngineExtension'); diff --git a/src/bundle/LegacySearchEngine/IbexaLegacySearchEngineBundle.php b/src/bundle/LegacySearchEngine/IbexaLegacySearchEngineBundle.php new file mode 100644 index 0000000000..2110f94a71 --- /dev/null +++ b/src/bundle/LegacySearchEngine/IbexaLegacySearchEngineBundle.php @@ -0,0 +1,38 @@ +addCompilerPass(new CriteriaConverterPass()); + $container->addCompilerPass(new CriterionFieldValueHandlerRegistryPass()); + $container->addCompilerPass(new SortClauseConverterPass()); + $container->addCompilerPass(new FieldRegistryPass()); + } + + public function getContainerExtension() + { + if (!isset($this->extension)) { + $this->extension = new DependencyInjection\IbexaLegacySearchEngineExtension(); + } + + return $this->extension; + } +} + +class_alias(IbexaLegacySearchEngineBundle::class, 'eZ\Bundle\EzPublishLegacySearchEngineBundle\EzPublishLegacySearchEngineBundle'); diff --git a/src/bundle/LegacySearchEngine/Resources/config/services.yml b/src/bundle/LegacySearchEngine/Resources/config/services.yml new file mode 100644 index 0000000000..84eb05d611 --- /dev/null +++ b/src/bundle/LegacySearchEngine/Resources/config/services.yml @@ -0,0 +1,12 @@ +services: + Ibexa\Bundle\LegacySearchEngine\ApiLoader\ConnectionFactory: + class: Ibexa\Bundle\LegacySearchEngine\ApiLoader\ConnectionFactory + arguments: + - '@Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider' + calls: + - [setContainer, ["@service_container"]] + + ibexa.api.search_engine.legacy.connection: + class: Doctrine\DBAL\Connection + factory: ['@Ibexa\Bundle\LegacySearchEngine\ApiLoader\ConnectionFactory', getConnection] + lazy: true diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Stub/AnnotationEntityBundle/Entity/.gitkeep b/src/bundle/RepositoryInstaller/.gitkeep similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Stub/AnnotationEntityBundle/Entity/.gitkeep rename to src/bundle/RepositoryInstaller/.gitkeep diff --git a/eZ/Bundle/PlatformInstallerBundle/src/Command/InstallPlatformCommand.php b/src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php similarity index 89% rename from eZ/Bundle/PlatformInstallerBundle/src/Command/InstallPlatformCommand.php rename to src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php index 9244935978..0011d9d37c 100644 --- a/eZ/Bundle/PlatformInstallerBundle/src/Command/InstallPlatformCommand.php +++ b/src/bundle/RepositoryInstaller/Command/InstallPlatformCommand.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace EzSystems\PlatformInstallerBundle\Command; +namespace Ibexa\Bundle\RepositoryInstaller\Command; use Doctrine\DBAL\Connection; -use eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider; -use eZ\Bundle\EzPublishCoreBundle\Command\BackwardCompatibleCommand; +use Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider; +use Ibexa\Bundle\Core\Command\BackwardCompatibleCommand; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; @@ -16,6 +16,7 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Process\PhpExecutableFinder; use Symfony\Component\Process\Process; @@ -33,10 +34,10 @@ final class InstallPlatformCommand extends Command implements BackwardCompatible /** @var string */ private $environment; - /** @var \EzSystems\PlatformInstallerBundle\Installer\Installer[] */ + /** @var \Ibexa\Bundle\RepositoryInstaller\Installer\Installer[] */ private $installers = []; - /** @var \eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider */ + /** @var \Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider */ private $repositoryConfigurationProvider; public const EXIT_GENERAL_DATABASE_ERROR = 4; @@ -73,7 +74,7 @@ protected function configure() 'skip-indexing', null, InputOption::VALUE_NONE, - 'Skip indexing (ezplaform:reindex)' + 'Skip indexing (ibexa:reindex)' ); } @@ -84,12 +85,20 @@ protected function execute(InputInterface $input, OutputInterface $output): int $this->checkParameters(); $this->checkCreateDatabase($output); + $schemaManager = $this->connection->getSchemaManager(); + if (!empty($schemaManager->listTables())) { + $io = new SymfonyStyle($input, $output); + if (!$io->confirm('Running this command will delete data in all Ibexa generated tables. Continue?', )) { + return 0; + } + } + $type = $input->getArgument('type'); $siteaccess = $input->getOption('siteaccess'); $installer = $this->getInstaller($type); if ($installer === false) { $output->writeln( - "Unknown install type '$type', available options in currently installed eZ Platform package: " . + "Unknown install type '$type', available options in currently installed Ibexa package: " . implode(', ', array_keys($this->installers)) ); exit(self::EXIT_UNKNOWN_INSTALL_TYPE); @@ -195,7 +204,7 @@ private function indexData(OutputInterface $output, $siteaccess = null) /** * @param $type * - * @return \EzSystems\PlatformInstallerBundle\Installer\Installer + * @return \Ibexa\Bundle\RepositoryInstaller\Installer\Installer */ private function getInstaller($type) { @@ -214,7 +223,7 @@ private function getInstaller($type) * Based on {@see \Sensio\Bundle\DistributionBundle\Composer\ScriptHandler::executeCommand}. * * @param \Symfony\Component\Console\Output\OutputInterface $output - * @param string $cmd eZ Platform command to execute, like 'ezplatform:solr_create_index' + * @param string $cmd Ibexa command to execute, like 'ezplatform:solr_create_index' * Escape any user provided arguments, like: 'assets:install '.escapeshellarg($webDir) * @param int $timeout */ @@ -270,3 +279,5 @@ public function getDeprecatedAliases(): array return ['ezplatform:install']; } } + +class_alias(InstallPlatformCommand::class, 'EzSystems\PlatformInstallerBundle\Command\InstallPlatformCommand'); diff --git a/eZ/Bundle/PlatformInstallerBundle/src/Command/ValidatePasswordHashesCommand.php b/src/bundle/RepositoryInstaller/Command/ValidatePasswordHashesCommand.php similarity index 80% rename from eZ/Bundle/PlatformInstallerBundle/src/Command/ValidatePasswordHashesCommand.php rename to src/bundle/RepositoryInstaller/Command/ValidatePasswordHashesCommand.php index 7c7e8517e4..74db9309c6 100644 --- a/eZ/Bundle/PlatformInstallerBundle/src/Command/ValidatePasswordHashesCommand.php +++ b/src/bundle/RepositoryInstaller/Command/ValidatePasswordHashesCommand.php @@ -4,21 +4,21 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace EzSystems\PlatformInstallerBundle\Command; +namespace Ibexa\Bundle\RepositoryInstaller\Command; -use eZ\Bundle\EzPublishCoreBundle\Command\BackwardCompatibleCommand; -use eZ\Publish\API\Repository\PasswordHashService; -use eZ\Publish\Core\FieldType\User\UserStorage; +use Ibexa\Bundle\Core\Command\BackwardCompatibleCommand; +use Ibexa\Contracts\Core\Repository\PasswordHashService; +use Ibexa\Core\FieldType\User\UserStorage; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; final class ValidatePasswordHashesCommand extends Command implements BackwardCompatibleCommand { - /** @var \eZ\Publish\Core\FieldType\User\UserStorage */ + /** @var \Ibexa\Core\FieldType\User\UserStorage */ private $userStorage; - /** @var \eZ\Publish\API\Repository\PasswordHashService */ + /** @var \Ibexa\Contracts\Core\Repository\PasswordHashService */ private $passwordHashService; public function __construct( @@ -58,3 +58,5 @@ public function getDeprecatedAliases(): array return ['ezplatform:user:validate-password-hashes']; } } + +class_alias(ValidatePasswordHashesCommand::class, 'EzSystems\PlatformInstallerBundle\Command\ValidatePasswordHashesCommand'); diff --git a/eZ/Bundle/PlatformInstallerBundle/src/DependencyInjection/Compiler/InstallerTagPass.php b/src/bundle/RepositoryInstaller/DependencyInjection/Compiler/InstallerTagPass.php similarity index 75% rename from eZ/Bundle/PlatformInstallerBundle/src/DependencyInjection/Compiler/InstallerTagPass.php rename to src/bundle/RepositoryInstaller/DependencyInjection/Compiler/InstallerTagPass.php index db03e95eea..cf0df72c3e 100644 --- a/eZ/Bundle/PlatformInstallerBundle/src/DependencyInjection/Compiler/InstallerTagPass.php +++ b/src/bundle/RepositoryInstaller/DependencyInjection/Compiler/InstallerTagPass.php @@ -4,20 +4,21 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace EzSystems\PlatformInstallerBundle\DependencyInjection\Compiler; +namespace Ibexa\Bundle\RepositoryInstaller\DependencyInjection\Compiler; -use EzSystems\PlatformInstallerBundle\Command\InstallPlatformCommand; +use Ibexa\Bundle\RepositoryInstaller\Command\InstallPlatformCommand; use LogicException; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Reference; /** - * Compiles services tagged as ezplatform.installer to %ezplatform.installers%. + * Injects services tagged as "ibexa.installer" into + * {@see \Ibexa\Bundle\RepositoryInstaller\Command\InstallPlatformCommand::$installers}. */ class InstallerTagPass implements CompilerPassInterface { - public const INSTALLER_TAG = 'ezplatform.installer'; + public const INSTALLER_TAG = 'ibexa.installer'; public function process(ContainerBuilder $container) { @@ -47,3 +48,5 @@ public function process(ContainerBuilder $container) $installCommandDef->replaceArgument('$installers', $installers); } } + +class_alias(InstallerTagPass::class, 'EzSystems\PlatformInstallerBundle\DependencyInjection\Compiler\InstallerTagPass'); diff --git a/src/bundle/RepositoryInstaller/DependencyInjection/IbexaRepositoryInstallerExtension.php b/src/bundle/RepositoryInstaller/DependencyInjection/IbexaRepositoryInstallerExtension.php new file mode 100644 index 0000000000..87f8cfd728 --- /dev/null +++ b/src/bundle/RepositoryInstaller/DependencyInjection/IbexaRepositoryInstallerExtension.php @@ -0,0 +1,23 @@ +load('services.yml'); + } +} + +class_alias(IbexaRepositoryInstallerExtension::class, 'EzSystems\PlatformInstallerBundle\DependencyInjection\EzSystemsPlatformInstallerExtension'); diff --git a/src/bundle/RepositoryInstaller/Event/Subscriber/BuildSchemaSubscriber.php b/src/bundle/RepositoryInstaller/Event/Subscriber/BuildSchemaSubscriber.php new file mode 100644 index 0000000000..b886bbbcd9 --- /dev/null +++ b/src/bundle/RepositoryInstaller/Event/Subscriber/BuildSchemaSubscriber.php @@ -0,0 +1,51 @@ +schemaFilePath = $schemaFilePath; + } + + /** + * Returns an array of events this subscriber wants to listen to. + * + * @return array + */ + public static function getSubscribedEvents(): array + { + return [ + SchemaBuilderEvents::BUILD_SCHEMA => ['onBuildSchema', 200], + ]; + } + + /** + * @param \Ibexa\Contracts\DoctrineSchema\Event\SchemaBuilderEvent $event + */ + public function onBuildSchema(SchemaBuilderEvent $event): void + { + $event + ->getSchemaBuilder() + ->importSchemaFromFile($this->schemaFilePath); + } +} + +class_alias(BuildSchemaSubscriber::class, 'EzSystems\PlatformInstallerBundle\Event\Subscriber\BuildSchemaSubscriber'); diff --git a/src/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundle.php b/src/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundle.php new file mode 100644 index 0000000000..b5934d8080 --- /dev/null +++ b/src/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundle.php @@ -0,0 +1,36 @@ +hasExtension('ibexa_doctrine_schema')) { + throw new RuntimeException( + sprintf( + 'Ibexa Installer requires Doctrine Schema Bundle (enable %s)', + DoctrineSchemaBundle::class + ) + ); + } + + parent::build($container); + $container->addCompilerPass(new InstallerTagPass()); + } +} + +class_alias(IbexaRepositoryInstallerBundle::class, 'EzSystems\PlatformInstallerBundle\EzSystemsPlatformInstallerBundle'); diff --git a/eZ/Bundle/PlatformInstallerBundle/src/Installer/CoreInstaller.php b/src/bundle/RepositoryInstaller/Installer/CoreInstaller.php similarity index 82% rename from eZ/Bundle/PlatformInstallerBundle/src/Installer/CoreInstaller.php rename to src/bundle/RepositoryInstaller/Installer/CoreInstaller.php index 0f2ba48f98..6b05899ffe 100644 --- a/eZ/Bundle/PlatformInstallerBundle/src/Installer/CoreInstaller.php +++ b/src/bundle/RepositoryInstaller/Installer/CoreInstaller.php @@ -6,12 +6,12 @@ */ declare(strict_types=1); -namespace EzSystems\PlatformInstallerBundle\Installer; +namespace Ibexa\Bundle\RepositoryInstaller\Installer; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Schema\Schema; -use EzSystems\DoctrineSchema\API\Builder\SchemaBuilder; +use Ibexa\Contracts\DoctrineSchema\Builder\SchemaBuilderInterface; use Symfony\Component\Console\Helper\ProgressBar; /** @@ -19,14 +19,14 @@ */ class CoreInstaller extends DbBasedInstaller implements Installer { - /** @var \EzSystems\DoctrineSchema\API\Builder\SchemaBuilder */ + /** @var \Ibexa\Contracts\DoctrineSchema\Builder\SchemaBuilderInterface */ protected $schemaBuilder; /** * @param \Doctrine\DBAL\Connection $db - * @param \EzSystems\DoctrineSchema\API\Builder\SchemaBuilder $schemaBuilder + * @param \Ibexa\Contracts\DoctrineSchema\Builder\SchemaBuilderInterface $schemaBuilder */ - public function __construct(Connection $db, SchemaBuilder $schemaBuilder) + public function __construct(Connection $db, SchemaBuilderInterface $schemaBuilder) { parent::__construct($db); @@ -34,12 +34,12 @@ public function __construct(Connection $db, SchemaBuilder $schemaBuilder) } /** - * Import Schema using event-driven Schema Builder API from eZ Systems DoctrineSchema Bundle. + * Import Schema using event-driven Schema Builder API from Ibexa DoctrineSchema Bundle. * * If you wish to extend schema, implement your own EventSubscriber * - * @see \EzSystems\DoctrineSchema\API\Event\SchemaBuilderEvent - * @see \EzSystems\PlatformInstallerBundle\Event\Subscriber\BuildSchemaSubscriber + * @see \Ibexa\Contracts\DoctrineSchema\Event\SchemaBuilderEvent + * @see \Ibexa\Bundle\RepositoryInstaller\Event\Subscriber\BuildSchemaSubscriber * * @throws \Doctrine\DBAL\DBALException */ @@ -80,7 +80,7 @@ public function importSchema() /** * @throws \Doctrine\DBAL\DBALException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function importData() { @@ -125,3 +125,5 @@ public function createConfiguration() { } } + +class_alias(CoreInstaller::class, 'EzSystems\PlatformInstallerBundle\Installer\CoreInstaller'); diff --git a/eZ/Bundle/PlatformInstallerBundle/src/Installer/DbBasedInstaller.php b/src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php similarity index 88% rename from eZ/Bundle/PlatformInstallerBundle/src/Installer/DbBasedInstaller.php rename to src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php index 1aa2f91518..5c343563e4 100644 --- a/eZ/Bundle/PlatformInstallerBundle/src/Installer/DbBasedInstaller.php +++ b/src/bundle/RepositoryInstaller/Installer/DbBasedInstaller.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace EzSystems\PlatformInstallerBundle\Installer; +namespace Ibexa\Bundle\RepositoryInstaller\Installer; use Doctrine\DBAL\Connection; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; use Symfony\Component\Filesystem\Filesystem; class DbBasedInstaller @@ -25,7 +25,7 @@ public function __construct(Connection $db) { $this->db = $db; // parametrized so other installer implementations can override this - $this->baseDataDir = __DIR__ . '/../../../../../data'; + $this->baseDataDir = __DIR__ . '/../../../../data'; } /** @@ -79,7 +79,7 @@ protected function runQueriesFromFile($file) * * @return string absolute existing file path * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException * @throws \Doctrine\DBAL\DBALException * * @since 6.13 @@ -104,3 +104,5 @@ final protected function getKernelSQLFileForDBMS($relativeFilePath) return realpath($filePath); } } + +class_alias(DbBasedInstaller::class, 'EzSystems\PlatformInstallerBundle\Installer\DbBasedInstaller'); diff --git a/eZ/Bundle/PlatformInstallerBundle/src/Installer/Installer.php b/src/bundle/RepositoryInstaller/Installer/Installer.php similarity index 75% rename from eZ/Bundle/PlatformInstallerBundle/src/Installer/Installer.php rename to src/bundle/RepositoryInstaller/Installer/Installer.php index 81a62f7c53..c9d95c46ed 100644 --- a/eZ/Bundle/PlatformInstallerBundle/src/Installer/Installer.php +++ b/src/bundle/RepositoryInstaller/Installer/Installer.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace EzSystems\PlatformInstallerBundle\Installer; +namespace Ibexa\Bundle\RepositoryInstaller\Installer; /** * Interface Installer. @@ -12,7 +12,7 @@ * Simple SQL based installer interface for eZ Platform 1.0, will be replaced by a new interface in the future that * uses API/SPI (via future import/export functionality) to support cluster and several different storage engines. * Such change will also move responsibility of repository init (base schema and minimal data) to storage engine - * so this is not in installers. Further info: https://jira.ez.no/browse/EZP-25368 + * so this is not in installers. Further info: https://issues.ibexa.co/browse/EZP-25368 */ interface Installer { @@ -27,7 +27,7 @@ public function importSchema(); public function importData(); /** - * @deprecated Inactive since 6.1, further info: https://jira.ez.no/browse/EZP-25369 + * @deprecated Inactive since 6.1, further info: https://issues.ibexa.co/browse/EZP-25369 */ public function createConfiguration(); @@ -36,3 +36,5 @@ public function createConfiguration(); */ public function importBinaries(); } + +class_alias(Installer::class, 'EzSystems\PlatformInstallerBundle\Installer\Installer'); diff --git a/src/bundle/RepositoryInstaller/Resources/config/services.yml b/src/bundle/RepositoryInstaller/Resources/config/services.yml new file mode 100644 index 0000000000..965404750b --- /dev/null +++ b/src/bundle/RepositoryInstaller/Resources/config/services.yml @@ -0,0 +1,34 @@ +services: + Ibexa\Bundle\RepositoryInstaller\Event\Subscriber\BuildSchemaSubscriber: + autoconfigure: true + public: false + arguments: + - '@=service("kernel").locateResource("@IbexaCoreBundle/Resources/config/storage/legacy/schema.yaml")' + + Ibexa\Bundle\RepositoryInstaller\Installer\DbBasedInstaller: + abstract: true + arguments: ['@ibexa.persistence.connection'] + lazy: true + + Ibexa\Bundle\RepositoryInstaller\Installer\CoreInstaller: + autowire: true + parent: Ibexa\Bundle\RepositoryInstaller\Installer\DbBasedInstaller + tags: + - { name: ibexa.installer, type: ibexa-oss } + + Ibexa\Bundle\RepositoryInstaller\Command\InstallPlatformCommand: + arguments: + $connection: '@ibexa.persistence.connection' + $installers: [] + $cachePool: '@ibexa.cache_pool' + $environment: "%kernel.environment%" + $repositoryConfigurationProvider: '@Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider' + tags: + - { name: console.command } + + Ibexa\Bundle\RepositoryInstaller\Command\ValidatePasswordHashesCommand: + arguments: + $userStorage: '@Ibexa\Core\FieldType\User\UserStorage' + $passwordHashService: '@Ibexa\Contracts\Core\Repository\PasswordHashService' + tags: + - { name: console.command } diff --git a/src/contracts/Collection/AbstractInMemoryCollection.php b/src/contracts/Collection/AbstractInMemoryCollection.php new file mode 100644 index 0000000000..5acfc79edb --- /dev/null +++ b/src/contracts/Collection/AbstractInMemoryCollection.php @@ -0,0 +1,98 @@ + + * @template-implements \Ibexa\Contracts\Core\Collection\StreamableInterface + */ +abstract class AbstractInMemoryCollection implements CollectionInterface, StreamableInterface +{ + /** @phpstan-var TValue[] */ + protected array $items; + + /** + * @phpstan-param TValue[] $items + */ + public function __construct(array $items = []) + { + $this->items = $items; + } + + public function isEmpty(): bool + { + return empty($this->items); + } + + public function toArray(): array + { + return $this->items; + } + + public function getIterator(): Iterator + { + return new ArrayIterator($this->items); + } + + public function count(): int + { + return count($this->items); + } + + /** + * @phpstan-return static + */ + public function filter(Closure $predicate): self + { + return $this->createFrom(array_filter($this->items, $predicate, ARRAY_FILTER_USE_BOTH)); + } + + /** + * @phpstan-return static + */ + public function map(Closure $function): self + { + return $this->createFrom(array_map($function, $this->items)); + } + + public function forAll(Closure $predicate): bool + { + foreach ($this->items as $i => $item) { + if (!$predicate($item, $i)) { + return false; + } + } + + return true; + } + + public function exists(Closure $predicate): bool + { + foreach ($this->items as $i => $item) { + if ($predicate($item, $i)) { + return true; + } + } + + return false; + } + + /** + * @param TValue[] $items + * + * @phpstan-return static + */ + abstract protected function createFrom(array $items): self; +} diff --git a/src/contracts/Collection/ArrayList.php b/src/contracts/Collection/ArrayList.php new file mode 100644 index 0000000000..4a639645a8 --- /dev/null +++ b/src/contracts/Collection/ArrayList.php @@ -0,0 +1,64 @@ + + * @template-implements \Ibexa\Contracts\Core\Collection\ListInterface + */ +class ArrayList extends AbstractInMemoryCollection implements ListInterface +{ + /** + * @phpstan-param TValue[] $items + */ + public function __construct(array $items = []) + { + parent::__construct(array_values($items)); + } + + public function first() + { + if (($result = reset($this->items)) !== false) { + return $result; + } + + throw new OutOfBoundsException('Collection is empty'); + } + + public function last() + { + if (($result = end($this->items)) !== false) { + return $result; + } + + throw new OutOfBoundsException('Collection is empty'); + } + + /** + * @phpstan-param TValue $value + */ + public function contains($value): bool + { + return in_array($value, $this->items, true); + } + + /** + * @phpstan-param TValue[] $items + * + * @phpstan-return \Ibexa\Contracts\Core\Collection\ArrayList + */ + protected function createFrom(array $items): self + { + return new self($items); + } +} diff --git a/src/contracts/Collection/ArrayMap.php b/src/contracts/Collection/ArrayMap.php new file mode 100644 index 0000000000..698dc3a0f7 --- /dev/null +++ b/src/contracts/Collection/ArrayMap.php @@ -0,0 +1,45 @@ + + * @template-implements \Ibexa\Contracts\Core\Collection\MapInterface + */ +class ArrayMap extends AbstractInMemoryCollection implements MapInterface +{ + public function get($key) + { + if (!$this->has($key)) { + throw new OutOfBoundsException(sprintf("Collection does not contain element with key '%s'", $key)); + } + + return $this->items[$key]; + } + + public function has($key): bool + { + return array_key_exists($key, $this->items); + } + + /** + * @phpstan-param TValue[] $items + * + * @phpstan-return \Ibexa\Contracts\Core\Collection\ArrayMap + */ + protected function createFrom(array $items): self + { + return new self($items); + } +} diff --git a/src/contracts/Collection/CollectionInterface.php b/src/contracts/Collection/CollectionInterface.php new file mode 100644 index 0000000000..50c446ae2f --- /dev/null +++ b/src/contracts/Collection/CollectionInterface.php @@ -0,0 +1,33 @@ + + */ +interface CollectionInterface extends Countable, IteratorAggregate +{ + public function isEmpty(): bool; + + /** + * @return TValue[] + */ + public function toArray(): array; + + /** + * @return \Iterator + */ + public function getIterator(): Iterator; +} diff --git a/src/contracts/Collection/ListInterface.php b/src/contracts/Collection/ListInterface.php new file mode 100644 index 0000000000..3ed8e5cf4c --- /dev/null +++ b/src/contracts/Collection/ListInterface.php @@ -0,0 +1,40 @@ + + */ +interface ListInterface extends CollectionInterface +{ + /** + * Return first element of collection. + * + * @return TValue + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\OutOfBoundsException if collection is empty + */ + public function first(); + + /** + * Return last element of collection. + * + * @return TValue + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\OutOfBoundsException if collection is empty + */ + public function last(); + + /** + * @param TValue $value + */ + public function contains($value): bool; +} diff --git a/src/contracts/Collection/MapInterface.php b/src/contracts/Collection/MapInterface.php new file mode 100644 index 0000000000..433b44f2eb --- /dev/null +++ b/src/contracts/Collection/MapInterface.php @@ -0,0 +1,36 @@ + + */ +interface MapInterface extends CollectionInterface +{ + /** + * Returns value associated with given key. + * + * @param TKey $key + * + * @return TValue + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\OutOfBoundsException if map does not contain element with given key + */ + public function get($key); + + /** + * Returns true if the given key is defined within the map. + * + * @param TKey $key + */ + public function has($key): bool; +} diff --git a/src/contracts/Collection/MutableArrayList.php b/src/contracts/Collection/MutableArrayList.php new file mode 100644 index 0000000000..ef8958c5f4 --- /dev/null +++ b/src/contracts/Collection/MutableArrayList.php @@ -0,0 +1,46 @@ + + * @template-implements \Ibexa\Contracts\Core\Collection\MutableListInterface + */ +class MutableArrayList extends ArrayList implements MutableListInterface +{ + public function append($value): void + { + $this->items[] = $value; + } + + public function prepend($value): void + { + array_unshift($this->items, $value); + } + + public function remove($value): void + { + $idx = array_search($value, $this->items, true); + if ($idx !== false) { + array_splice($this->items, $idx, 1); + } + } + + public function clear(): void + { + $this->items = []; + } + + protected function createFrom(array $items): MutableArrayList + { + return new MutableArrayList($items); + } +} diff --git a/src/contracts/Collection/MutableArrayMap.php b/src/contracts/Collection/MutableArrayMap.php new file mode 100644 index 0000000000..3dbbf96d18 --- /dev/null +++ b/src/contracts/Collection/MutableArrayMap.php @@ -0,0 +1,39 @@ + + * @template-implements \Ibexa\Contracts\Core\Collection\MutableMapInterface + */ +class MutableArrayMap extends ArrayMap implements MutableMapInterface +{ + public function set($key, $value): void + { + $this->items[$key] = $value; + } + + public function unset($key): void + { + unset($this->items[$key]); + } + + public function clear(): void + { + $this->items = []; + } + + protected function createFrom(array $items): MutableArrayMap + { + return new MutableArrayMap($items); + } +} diff --git a/src/contracts/Collection/MutableListInterface.php b/src/contracts/Collection/MutableListInterface.php new file mode 100644 index 0000000000..dfe4a38c5f --- /dev/null +++ b/src/contracts/Collection/MutableListInterface.php @@ -0,0 +1,34 @@ + + */ +interface MutableListInterface extends ListInterface +{ + /** + * @param TValue $value + */ + public function append($value): void; + + /** + * @param TValue $value + */ + public function prepend($value): void; + + /** + * @param TValue $value + */ + public function remove($value): void; + + public function clear(): void; +} diff --git a/src/contracts/Collection/MutableMapInterface.php b/src/contracts/Collection/MutableMapInterface.php new file mode 100644 index 0000000000..0805a66c83 --- /dev/null +++ b/src/contracts/Collection/MutableMapInterface.php @@ -0,0 +1,31 @@ + + */ +interface MutableMapInterface extends MapInterface +{ + /** + * @param TKey $key + * @param TValue $value + */ + public function set($key, $value): void; + + /** + * @param TKey $key + */ + public function unset($key): void; + + public function clear(): void; +} diff --git a/src/contracts/Collection/StreamableInterface.php b/src/contracts/Collection/StreamableInterface.php new file mode 100644 index 0000000000..c75a89545f --- /dev/null +++ b/src/contracts/Collection/StreamableInterface.php @@ -0,0 +1,43 @@ + + */ + public function filter(Closure $predicate): self; + + /** + * Applies the given function to each element in the collection and returns + * a new collection with the elements returned by the function. + * + * @phpstan-return static + */ + public function map(Closure $function): self; + + /** + * Tests whether the given predicate holds for all elements of this collection. + */ + public function forAll(Closure $predicate): bool; + + /** + * Tests the existence of an element that satisfies the given predicate. + */ + public function exists(Closure $predicate): bool; +} diff --git a/src/contracts/Container.php b/src/contracts/Container.php new file mode 100644 index 0000000000..0998f158ab --- /dev/null +++ b/src/contracts/Container.php @@ -0,0 +1,26 @@ +containerBuilder = $containerBuilder; + } + + /** + * @param array> $webpackConfigNames + * + * @throws \JsonException + */ + public function dumpCustomConfiguration( + array $webpackConfigNames + ): void { + $bundlesMetadata = $this->containerBuilder->getParameter('kernel.bundles_metadata'); + $rootPath = $this->containerBuilder->getParameter('kernel.project_dir') . '/'; + foreach ($webpackConfigNames as $configName => $configFiles) { + $paths = $this->locateConfigurationFiles($bundlesMetadata, $configFiles, $rootPath); + $this->dumpConfigurationPaths( + $configName, + $rootPath . self::ENCORE_TARGET_PATH, + $paths + ); + } + } + + private function locateConfigurationFiles( + array $bundlesMetadata, + array $configFiles, + string $rootPath + ): array { + $paths = []; + foreach ($configFiles as $configFile => $options) { + $finder = $this->createFinder($bundlesMetadata, $configFile, $rootPath); + + /** @var \Symfony\Component\Finder\SplFileInfo $fileInfo */ + foreach ($finder as $fileInfo) { + if ($options['deprecated'] ?? false) { + trigger_deprecation( + 'ibexa/core', + '4.0.0', + 'Support for old configuration files is deprecated, please update name of %s file, to %s', + $fileInfo->getPathname(), + $options['alternative'] + ); + } + + $path = $fileInfo->getRealPath(); + if (strpos($path, $rootPath) === 0) { + $path = './' . substr($path, strlen($rootPath)); + } + + $paths[] = $path; + } + } + + return $paths; + } + + /** + * @throws \JsonException + */ + private function dumpConfigurationPaths( + string $configName, + string $targetPath, + array $paths + ): void { + $filesystem = new Filesystem(); + $filesystem->dumpFile( + $targetPath . '/' . $configName, + sprintf('module.exports = %s;', json_encode($paths, JSON_THROW_ON_ERROR)) + ); + } + + private function createFinder( + array $bundlesMetadata, + string $configFile, + string $rootPath + ): Finder { + $finder = new Finder(); + $finder + ->in(array_column($bundlesMetadata, 'path')) + ->path('Resources/' . self::ENCORE_DIR) + ->name($configFile) + // include top-level project resources + ->append( + (new Finder()) + ->in($rootPath) + ->path(self::ENCORE_DIR) + ->name($configFile) + ->depth(1) + ->files() + ) + ->files(); + + return $finder; + } +} diff --git a/src/contracts/Event/Mapper/ResolveMissingFieldEvent.php b/src/contracts/Event/Mapper/ResolveMissingFieldEvent.php index af0f0c2282..df579341c1 100644 --- a/src/contracts/Event/Mapper/ResolveMissingFieldEvent.php +++ b/src/contracts/Event/Mapper/ResolveMissingFieldEvent.php @@ -4,29 +4,27 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); + namespace Ibexa\Contracts\Core\Event\Mapper; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; use Symfony\Contracts\EventDispatcher\Event; final class ResolveMissingFieldEvent extends Event { - /** @var \eZ\Publish\SPI\Persistence\Content */ - private $content; + private Content $content; - /** @var \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition */ - private $fieldDefinition; + private FieldDefinition $fieldDefinition; - /** @var string */ - private $languageCode; + private string $languageCode; /** @var array */ - private $context; + private array $context; - /** @var \eZ\Publish\SPI\Persistence\Content\Field|null */ - private $field; + private ?Field $field; /** * @param array $context diff --git a/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php new file mode 100644 index 0000000000..35e59cb927 --- /dev/null +++ b/src/contracts/Event/NameSchema/AbstractNameSchemaEvent.php @@ -0,0 +1,56 @@ +> */ + private array $fieldMap; + + /** @var array */ + private array $languageCodes; + + /** + * @param array> $schemaIdentifiers + * @param array> $fieldMap + * @param array $languageCodes + */ + public function __construct( + array $schemaIdentifiers, + ContentType $contentType, + array $fieldMap, + array $languageCodes + ) { + parent::__construct($schemaIdentifiers); + $this->contentType = $contentType; + $this->fieldMap = $fieldMap; + $this->languageCodes = $languageCodes; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } + + /** @return array> */ + public function getFieldMap(): array + { + return $this->fieldMap; + } + + /** @return array */ + public function getLanguageCodes(): array + { + return $this->languageCodes; + } +} diff --git a/src/contracts/Event/NameSchema/AbstractSchemaEvent.php b/src/contracts/Event/NameSchema/AbstractSchemaEvent.php new file mode 100644 index 0000000000..869ecc0d90 --- /dev/null +++ b/src/contracts/Event/NameSchema/AbstractSchemaEvent.php @@ -0,0 +1,52 @@ +> */ + private array $schemaIdentifiers; + + /** @var array> */ + private array $tokenValues = []; + + /** + * @param array> $schemaIdentifiers + */ + public function __construct(array $schemaIdentifiers) + { + $this->schemaIdentifiers = $schemaIdentifiers; + } + + /** + * @return array> + */ + final public function getTokenValues(): array + { + return $this->tokenValues; + } + + /** + * @param array> $names + */ + final public function setTokenValues(array $names): void + { + $this->tokenValues = $names; + } + + /** + * @return array> + */ + public function getSchemaIdentifiers(): array + { + return $this->schemaIdentifiers; + } +} diff --git a/src/contracts/Event/NameSchema/ContentAwareEventInterface.php b/src/contracts/Event/NameSchema/ContentAwareEventInterface.php new file mode 100644 index 0000000000..18072e6854 --- /dev/null +++ b/src/contracts/Event/NameSchema/ContentAwareEventInterface.php @@ -0,0 +1,16 @@ +> $schemaIdentifiers + * @param array> $fieldMap + * @param array $languageCodes + */ + public function __construct( + Content $content, + array $schemaIdentifiers, + ContentType $contentType, + array $fieldMap, + array $languageCodes + ) { + parent::__construct($schemaIdentifiers, $contentType, $fieldMap, $languageCodes); + $this->content = $content; + } + + public function getContent(): Content + { + return $this->content; + } +} diff --git a/src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php b/src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php new file mode 100644 index 0000000000..d74df806ca --- /dev/null +++ b/src/contracts/Event/NameSchema/ResolveNameSchemaEvent.php @@ -0,0 +1,13 @@ +> $schemaIdentifiers + */ + public function __construct( + array $schemaIdentifiers, + Content $content + ) { + parent::__construct($schemaIdentifiers); + $this->content = $content; + } + + public function getContent(): Content + { + return $this->content; + } +} diff --git a/src/contracts/Event/View/PostBuildViewEvent.php b/src/contracts/Event/View/PostBuildViewEvent.php index 99b4ca0694..a4636f570d 100644 --- a/src/contracts/Event/View/PostBuildViewEvent.php +++ b/src/contracts/Event/View/PostBuildViewEvent.php @@ -8,12 +8,12 @@ namespace Ibexa\Contracts\Core\Event\View; -use eZ\Publish\Core\MVC\Symfony\View\View; +use Ibexa\Core\MVC\Symfony\View\View; use Symfony\Contracts\EventDispatcher\Event; final class PostBuildViewEvent extends Event { - /** @var \eZ\Publish\Core\MVC\Symfony\View\View */ + /** @var \Ibexa\Core\MVC\Symfony\View\View */ private $view; public function __construct(View $view) diff --git a/src/contracts/Exception/InvalidArgumentException.php b/src/contracts/Exception/InvalidArgumentException.php new file mode 100644 index 0000000000..73fcdd53df --- /dev/null +++ b/src/contracts/Exception/InvalidArgumentException.php @@ -0,0 +1,36 @@ +$field value property with default data based on the external data. * - * $field->value is a {@see \eZ\Publish\SPI\Persistence\Content\FieldValue} object. - * This value holds the data as a {@see \eZ\Publish\Core\FieldType\Value} based object, according to - * the field type (e.g. for TextLine, it will be a {@see \eZ\Publish\Core\FieldType\TextLine\Value} object). + * $field->value is a {@see \Ibexa\Contracts\Core\Persistence\Content\FieldValue} object. + * This value holds the data as a {@see \Ibexa\Core\FieldType\Value} based object, according to + * the field type (e.g. for TextLine, it will be a {@see \Ibexa\Core\FieldType\TextLine\Value} object). */ public function getDefaultFieldData(VersionInfo $versionInfo, Field $field): void; } diff --git a/src/contracts/FieldType/FieldConstraintsStorage.php b/src/contracts/FieldType/FieldConstraintsStorage.php new file mode 100644 index 0000000000..ece6ffb252 --- /dev/null +++ b/src/contracts/FieldType/FieldConstraintsStorage.php @@ -0,0 +1,32 @@ +id = unique ID from the attribute tables (needs to be generated by + * database back end on create, before the external data source may be + * called from storing). + * + * The context array is deprecated and will be dropped in the next major version. + * + * This method might return true if $field needs to be updated after storage done here (to store a PK for instance). + * In any other case, this method must not return anything (null). + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param array $context Deprecated. Rely on injected Connection instead. + * + * @return mixed null|true + */ + public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context); + + /** + * Populates $field value property based on the external data. + * $field->value is a {@see \Ibexa\Contracts\Core\Persistence\Content\FieldValue} object. + * This value holds the data as a {@see \Ibexa\Core\FieldType\Value} based object, + * according to the field type (e.g. for TextLine, it will be a {@see \Ibexa\Core\FieldType\TextLine\Value} object). + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param array $context Deprecated. Rely on injected Connection instead. + */ + public function getFieldData(VersionInfo $versionInfo, Field $field, array $context); + + /** + * Deletes field data for all $fieldIds in the version identified by + * $versionInfo. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param array $fieldIds Array of field IDs + * @param array $context Deprecated. Rely on injected Connection instead. + * + * @return bool + */ + public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context); + + /** + * Checks if field type has external data to deal with. + * + * @return bool + */ + public function hasFieldData(); + + /** + * Get index data for external data for search backend. + * + * @deprecated Use {@see \Ibexa\Contracts\Core\FieldType\Indexable} + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param array $context Deprecated. Rely on injected Connection instead. + * + * @return \Ibexa\Contracts\Core\Search\Field[] + */ + public function getIndexData(VersionInfo $versionInfo, Field $field, array $context); +} + +class_alias(FieldStorage::class, 'eZ\Publish\SPI\FieldType\FieldStorage'); diff --git a/src/contracts/FieldType/FieldType.php b/src/contracts/FieldType/FieldType.php new file mode 100644 index 0000000000..46694a2458 --- /dev/null +++ b/src/contracts/FieldType/FieldType.php @@ -0,0 +1,383 @@ + + * array( + * 'stringLength' => array( + * 'minStringLength' => array( + * 'type' => 'int', + * 'default' => 0, + * ), + * 'maxStringLength' => array( + * 'type' => 'int' + * 'default' => null, + * ) + * ), + * ); + * + * + * @return mixed + */ + abstract public function getValidatorConfigurationSchema(); + + /** + * Validates a field based on the validator configuration in the field definition. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDef The field definition of the field + * @param \Ibexa\Contracts\Core\FieldType\Value $value The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + abstract public function validate(FieldDefinition $fieldDef, Value $value); + + /** + * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * This methods determines if the given $validatorConfiguration is + * structurally correct and complies to the validator configuration schema + * returned by {@see FieldType::getValidatorConfigurationSchema()}. + * + * @param mixed $validatorConfiguration + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + abstract public function validateValidatorConfiguration($validatorConfiguration); + + /** + * Applies the default values to the given $validatorConfiguration of a FieldDefinitionCreateStruct. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param mixed $validatorConfiguration + */ + abstract public function applyDefaultValidatorConfiguration(&$validatorConfiguration); + + /** + * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * This methods determines if the given $fieldSettings are structurally + * correct and comply to the settings schema returned by {@see FieldType::getSettingsSchema()}. + * + * @param mixed $fieldSettings + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + abstract public function validateFieldSettings($fieldSettings); + + /** + * Applies the default values to the fieldSettings of a FieldDefinitionCreateStruct. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param mixed $fieldSettings + */ + abstract public function applyDefaultSettings(&$fieldSettings); + + /** + * Indicates if the field type supports indexing and sort keys for searching. + * + * @return bool + */ + abstract public function isSearchable(); + + /** + * Indicates if the field definition of this type can appear only once in the same ContentType. + * + * @return bool + */ + abstract public function isSingular(); + + /** + * Indicates if the field definition of this type can be added to a ContentType with Content instances. + * + * @return bool + */ + abstract public function onlyEmptyInstance(); + + /** + * Returns the empty value for this field type. + * + * This value will be used, if no value was provided for a field of this + * type and no default value was specified in the field definition. It is + * also used to determine that a user intentionally (or unintentionally) did not + * set a non-empty value. + * + * @return \Ibexa\Contracts\Core\FieldType\Value + */ + abstract public function getEmptyValue(); + + /** + * Returns if the given $value is considered empty by the field type. + * + * Usually, only the value returned by {@see FieldType::getEmptyValue()} is + * considered empty. The given $value can be safely assumed to have already + * been processed by {@see FieldType::acceptValue()}. + * + * @param \Ibexa\Contracts\Core\FieldType\Value $value + * + * @return bool + */ + abstract public function isEmptyValue(Value $value); + + /** + * Potentially builds and checks the type and structure of the $inputValue. + * + * This method first inspects $inputValue and convert it into a dedicated + * value object. + * + * After that, the value is checked for structural validity. + * Note that this does not include validation after the rules + * from validators, but only plausibility checks for the general data + * format. + * + * Note that this method must also cope with the empty value for the field + * type as e.g. returned by {@see FieldType::getEmptyValue()}. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the parameter is not of the supported value sub type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the value does not match the expected structure + * + * @param mixed $inputValue + * + * @return \Ibexa\Contracts\Core\FieldType\Value The potentially converted and structurally plausible value. + */ + abstract public function acceptValue($inputValue); + + /** + * Converts an $hash to the Value defined by the field type. + * + * This is the reverse operation to {@see FieldType::toHash()}. At least the hash + * format generated by {@see FieldType::toHash()} must be converted in reverse. + * Additional formats might be supported in the rare case that this is + * necessary. See the class description for more details on a hash format. + * + * @param mixed $hash + * + * @return \Ibexa\Contracts\Core\FieldType\Value + */ + abstract public function fromHash($hash); + + /** + * Converts the given $value into a plain hash format. + * + * Converts the given $value into a plain hash format, which can be used to + * transfer the value through plain text formats, e.g. XML, which do not + * support complex structures like objects. See the class level doc block + * for additional information. See the class description for more details on a hash format. + * + * @param \Ibexa\Contracts\Core\FieldType\Value $value + * + * @return mixed + */ + abstract public function toHash(Value $value); + + /** + * Converts the given $fieldSettings to a simple hash format. + * + * See the class description for more details on a hash format. + * + * @param mixed $fieldSettings + * + * @return array|scalar|null + */ + abstract public function fieldSettingsToHash($fieldSettings); + + /** + * Converts the given $fieldSettingsHash to field settings of the type. + * + * This is the reverse operation of {@see FieldType::fieldSettingsToHash()}. + * See the class description for more details on a hash format. + * + * @param array|scalar|null $fieldSettingsHash + * + * @return mixed + */ + abstract public function fieldSettingsFromHash($fieldSettingsHash); + + /** + * Converts the given $validatorConfiguration to a simple hash format. + * + * See the class description for more details on a hash format. + * + * @param mixed $validatorConfiguration + * + * @return array|scalar|null + */ + abstract public function validatorConfigurationToHash($validatorConfiguration); + + /** + * Converts the given $validatorConfigurationHash to a validator + * configuration of the type. + * + * See the class description for more details on a hash format. + * + * @param array|scalar|null $validatorConfigurationHash + * + * @return mixed + */ + abstract public function validatorConfigurationFromHash($validatorConfigurationHash); + + /** + * Converts a $value to a persistence value. + * + * In this method the field type puts the data which is stored in the field of content in the repository + * into the property FieldValue::data. The format of $data is a primitive, an array (map) or an object, which + * is then canonically converted to e.g. json/xml structures by future storage engines without + * further conversions. For mapping the $data to the legacy database an appropriate Converter + * (implementing {@see \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter}) has implemented for the field + * type. Note: $data should only hold data which is actually stored in the field. It must not + * hold data which is stored externally. + * + * The $externalData property in the FieldValue is used for storing data externally by the + * FieldStorage interface method storeFieldData. + * + * The FieldValuer::sortKey is build by the field type for using by sort operations. + * + * @see \Ibexa\Contracts\Core\Persistence\Content\FieldValue + * + * @param \Ibexa\Contracts\Core\FieldType\Value $value The value of the field type + * + * @return \Ibexa\Contracts\Core\Persistence\Content\FieldValue the value processed by the storage engine + */ + abstract public function toPersistenceValue(Value $value); + + /** + * Converts a persistence $value to a Value. + * + * This method builds a field type value from the $data and $externalData properties. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + * + * @return \Ibexa\Contracts\Core\FieldType\Value + */ + abstract public function fromPersistenceValue(FieldValue $fieldValue); + + /** + * Returns relation data extracted from value. + * + * Not intended for \Ibexa\Contracts\Core\Repository\Values\Content\Relation::COMMON type relations, + * there is an API for handling those. + * + * @param \Ibexa\Contracts\Core\FieldType\Value $value + * + * @return array Hash with relation type as key and array of destination content ids as value. + * + * Example: + * + * array( + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::LINK => array( + * "contentIds" => array( 12, 13, 14 ), + * "locationIds" => array( 24 ) + * ), + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::EMBED => array( + * "contentIds" => array( 12 ), + * "locationIds" => array( 24, 45 ) + * ), + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::FIELD => array( 12 ) + * ) + * + */ + abstract public function getRelations(Value $value); +} + +class_alias(FieldType::class, 'eZ\Publish\SPI\FieldType\FieldType'); diff --git a/src/contracts/FieldType/GatewayBasedStorage.php b/src/contracts/FieldType/GatewayBasedStorage.php new file mode 100644 index 0000000000..e54fabfcb7 --- /dev/null +++ b/src/contracts/FieldType/GatewayBasedStorage.php @@ -0,0 +1,56 @@ +gateway = $gateway; + } + + /** + * This method is used exclusively by Legacy Storage to copy external data of existing field in main language to + * the untranslatable field not passed in create or update struct, but created implicitly in storage layer. + * + * By default the method falls back to the {@see \Ibexa\Contracts\Core\FieldType\FieldStorage::storeFieldData()}. + * External storages implement this method as needed. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $originalField + * @param array $context + * + * @return bool|null Same as {@see \Ibexa\Contracts\Core\FieldType\FieldStorage::storeFieldData()}. + */ + public function copyLegacyField(VersionInfo $versionInfo, Field $field, Field $originalField, array $context) + { + return $this->storeFieldData($versionInfo, $field, $context); + } +} + +class_alias(GatewayBasedStorage::class, 'eZ\Publish\SPI\FieldType\GatewayBasedStorage'); diff --git a/src/contracts/FieldType/Generic/Type.php b/src/contracts/FieldType/Generic/Type.php new file mode 100644 index 0000000000..aa0028cd67 --- /dev/null +++ b/src/contracts/FieldType/Generic/Type.php @@ -0,0 +1,361 @@ +serializer = $serializer; + $this->validator = $validator; + } + + public function getName(Value $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + return (string)$value; + } + + public function getEmptyValue(): Value + { + $class = $this->getValueClass(); + + return new $class(); + } + + public function fromHash($hash): Value + { + if ($hash) { + return $this->serializer->denormalize($hash, $this->getValueClass()); + } + + return $this->getEmptyValue(); + } + + public function toHash(Value $value): ?array + { + if ($this->isEmptyValue($value)) { + return null; + } + + return $this->serializer->normalize($value); + } + + /** + * @see https://symfony.com/doc/current/validation/raw_values.html + */ + protected function getFieldSettingsConstraints(): ?Assert\Collection + { + return null; + } + + /** + * @see https://symfony.com/doc/current/validation/raw_values.html + */ + protected function getFieldValueConstraints(FieldDefinition $fieldDefinition): ?Assert\Collection + { + return null; + } + + protected function mapConstraintViolationList(ConstraintViolationListInterface $constraintViolationList): array + { + $errors = []; + + /** @var \Symfony\Component\Validator\ConstraintViolationInterface $constraintViolation */ + foreach ($constraintViolationList as $constraintViolation) { + $errors[] = new ConstraintViolationAdapter($constraintViolation); + } + + return $errors; + } + + public function getSettingsSchema(): array + { + return []; + } + + public function getValidatorConfigurationSchema(): array + { + return []; + } + + public function validate(FieldDefinition $fieldDefinition, Value $value): array + { + if ($this->isEmptyValue($value)) { + return []; + } + + return $this->mapConstraintViolationList( + $this->validator->validate($value, $this->getFieldValueConstraints($fieldDefinition)) + ); + } + + public function validateValidatorConfiguration($validatorConfiguration): array + { + $validationErrors = []; + + foreach ((array)$validatorConfiguration as $validatorIdentifier => $constraints) { + $validationErrors[] = new UnknownValidatorValidationError( + $validatorIdentifier, + "[$validatorIdentifier]" + ); + } + + return $validationErrors; + } + + public function applyDefaultValidatorConfiguration(&$validatorConfiguration): void + { + if ($validatorConfiguration !== null && !is_array($validatorConfiguration)) { + throw new InvalidArgumentType('$validatorConfiguration', 'array|null', $validatorConfiguration); + } + + foreach ($this->getValidatorConfigurationSchema() as $validatorName => $configurationSchema) { + // Set configuration of specific validator to empty array if it is not already provided + if (!isset($validatorConfiguration[$validatorName])) { + $validatorConfiguration[$validatorName] = []; + } + + foreach ($configurationSchema as $settingName => $settingConfiguration) { + // Check that a default entry exists in the configuration schema for the validator but that no value has been provided + if (!isset($validatorConfiguration[$validatorName][$settingName]) && array_key_exists('default', $settingConfiguration)) { + $validatorConfiguration[$validatorName][$settingName] = $settingConfiguration['default']; + } + } + } + } + + public function validateFieldSettings($fieldSettings): array + { + if (empty($this->getSettingsSchema()) && !empty($fieldSettings)) { + return [ + new NonConfigurableValidationError($this->getFieldTypeIdentifier(), 'fieldType'), + ]; + } + + if (empty($fieldSettings)) { + return []; + } + + return $this->mapConstraintViolationList( + $this->validator->validate($fieldSettings, $this->getFieldSettingsConstraints()) + ); + } + + public function applyDefaultSettings(&$fieldSettings): void + { + if ($fieldSettings !== null && !is_array($fieldSettings)) { + throw new InvalidArgumentType('$fieldSettings', 'array|null', $fieldSettings); + } + + foreach ($this->getSettingsSchema() as $settingName => $settingConfiguration) { + // Checking that a default entry exists in the settingsSchema but that no value has been provided + if (!array_key_exists($settingName, (array)$fieldSettings) && array_key_exists('default', $settingConfiguration)) { + $fieldSettings[$settingName] = $settingConfiguration['default']; + } + } + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * Return value is mixed. It should be something which is sensible for + * sorting. + * + * It is up to the persistence implementation to handle those values. + * Common string and integer values are safe. + * + * For the legacy storage it is up to the field converters to set this + * value in either sort_key_string or sort_key_int. + * + * In case of multi value, values should be string and separated by "-" or ",". + * + * @return mixed + */ + protected function getSortInfo(Value $value) + { + return null; + } + + public function toPersistenceValue(Value $value): PersistenceValue + { + return new PersistenceValue( + [ + 'data' => $this->toHash($value), + 'externalData' => null, + 'sortKey' => $this->getSortInfo($value), + ] + ); + } + + public function fromPersistenceValue(PersistenceValue $fieldValue) + { + return $this->fromHash($fieldValue->data); + } + + public function isSearchable(): bool + { + return false; + } + + public function isSingular(): bool + { + return false; + } + + public function onlyEmptyInstance(): bool + { + return false; + } + + public function isEmptyValue(Value $value): bool + { + return $value == $this->getEmptyValue(); + } + + final public function acceptValue($inputValue): Value + { + if ($inputValue === null) { + return $this->getEmptyValue(); + } + + $value = $this->createValueFromInput($inputValue); + + $this->checkValueType($value); + + if ($this->isEmptyValue($value)) { + return $this->getEmptyValue(); + } + + return $value; + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * If given $inputValue could not be converted or is already an instance of dedicate value object, + * the method should simply return it. + * + * This is an operation method for {@see acceptValue()}. + * + * Example implementation: + * + * protected function createValueFromInput( $inputValue ) + * { + * if ( is_array( $inputValue ) ) + * { + * $inputValue = \My\FieldType\CookieJar\Value( $inputValue ); + * } + * + * return $inputValue; + * } + * + * + * @param mixed $inputValue + * + * @return mixed The potentially converted input value. + */ + protected function createValueFromInput($inputValue) + { + if (is_string($inputValue)) { + $inputValue = $this->serializer->denormalize( + $this->serializer->decode($inputValue), + $this->getValueClass() + ); + } + + return $inputValue; + } + + /** + * Returns FQN of class representing Field Type Value. + * + * @return string + */ + protected function getValueClass(): string + { + return substr_replace(static::class, 'Value', strrpos(static::class, '\\') + 1); + } + + /** + * Throws an exception if the given $value is not an instance of the supported value subtype. + * + * This is an operation method for {@see acceptValue()}. + * + * Default implementation expects the value class to reside in the same namespace as its + * FieldType class and is named "Value". + * + * Example implementation: + * + * protected function checkValueType($value): void + * { + * if ( !$inputValue instanceof \My\FieldType\CookieJar\Value ) ) + * { + * throw new InvalidArgumentException( "Given value type is not supported." ); + * } + * } + * + * + * @param mixed $value A value returned by {@see createValueFromInput()}. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the parameter is not an instance of the supported value subtype. + */ + protected function checkValueType($value): void + { + $valueClass = $this->getValueClass(); + if (!$value instanceof $valueClass) { + throw new InvalidArgumentType('$value', $valueClass, $value); + } + } + + public function fieldSettingsToHash($fieldSettings) + { + return $fieldSettings; + } + + public function fieldSettingsFromHash($fieldSettingsHash) + { + return $fieldSettingsHash; + } + + public function validatorConfigurationToHash($validatorConfiguration) + { + return $validatorConfiguration; + } + + public function validatorConfigurationFromHash($validatorConfiguration) + { + return $validatorConfiguration; + } + + public function getRelations(Value $value): array + { + return []; + } +} + +class_alias(Type::class, 'eZ\Publish\SPI\FieldType\Generic\Type'); diff --git a/src/contracts/FieldType/Generic/ValidationError/ConstraintViolationAdapter.php b/src/contracts/FieldType/Generic/ValidationError/ConstraintViolationAdapter.php new file mode 100644 index 0000000000..a7f63aa101 --- /dev/null +++ b/src/contracts/FieldType/Generic/ValidationError/ConstraintViolationAdapter.php @@ -0,0 +1,60 @@ +violation = $violation; + $this->target = $violation->getPropertyPath(); + } + + public function getTranslatableMessage(): Translation + { + return new Message( + $this->violation->getMessageTemplate(), + $this->violation->getParameters() + ); + } + + public function setTarget($target): void + { + $this->target = $target; + } + + public function getTarget(): string + { + return $this->target; + } +} + +class_alias(ConstraintViolationAdapter::class, 'eZ\Publish\SPI\FieldType\Generic\ValidationError\ConstraintViolationAdapter'); diff --git a/src/contracts/FieldType/Indexable.php b/src/contracts/FieldType/Indexable.php new file mode 100644 index 0000000000..1229d34279 --- /dev/null +++ b/src/contracts/FieldType/Indexable.php @@ -0,0 +1,58 @@ + + */ + public function getIndexDefinition(); + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string|null + */ + public function getDefaultMatchField(); + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string|null + */ + public function getDefaultSortField(); +} + +class_alias(Indexable::class, 'eZ\Publish\SPI\FieldType\Indexable'); diff --git a/src/contracts/FieldType/StorageGateway.php b/src/contracts/FieldType/StorageGateway.php new file mode 100644 index 0000000000..07688479ac --- /dev/null +++ b/src/contracts/FieldType/StorageGateway.php @@ -0,0 +1,28 @@ + Target is "minStringLength" key under "StringLengthValidator" key (fieldtype validator configuration) + * - "my_field_definition_identifier" + * + * @param string $target + */ + public function setTarget($target); + + /** + * Returns the target element on which the error occurred. + * + * @return string + */ + public function getTarget(); +} + +class_alias(ValidationError::class, 'eZ\Publish\SPI\FieldType\ValidationError'); diff --git a/eZ/Publish/SPI/FieldType/ValidationError/AbstractValidationError.php b/src/contracts/FieldType/ValidationError/AbstractValidationError.php similarity index 77% rename from eZ/Publish/SPI/FieldType/ValidationError/AbstractValidationError.php rename to src/contracts/FieldType/ValidationError/AbstractValidationError.php index 84bcb339b7..6126bc18b1 100644 --- a/eZ/Publish/SPI/FieldType/ValidationError/AbstractValidationError.php +++ b/src/contracts/FieldType/ValidationError/AbstractValidationError.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\FieldType\ValidationError; +namespace Ibexa\Contracts\Core\FieldType\ValidationError; -use eZ\Publish\API\Repository\Values\Translation; -use eZ\Publish\API\Repository\Values\Translation\Message; -use eZ\Publish\SPI\FieldType\ValidationError; +use Ibexa\Contracts\Core\FieldType\ValidationError; +use Ibexa\Contracts\Core\Repository\Values\Translation; +use Ibexa\Contracts\Core\Repository\Values\Translation\Message; /** * @internal @@ -55,3 +55,5 @@ public function getTarget(): string return $this->target; } } + +class_alias(AbstractValidationError::class, 'eZ\Publish\SPI\FieldType\ValidationError\AbstractValidationError'); diff --git a/eZ/Publish/SPI/FieldType/ValidationError/NonConfigurableValidationError.php b/src/contracts/FieldType/ValidationError/NonConfigurableValidationError.php similarity index 76% rename from eZ/Publish/SPI/FieldType/ValidationError/NonConfigurableValidationError.php rename to src/contracts/FieldType/ValidationError/NonConfigurableValidationError.php index 31c8e8e667..979902fdf5 100644 --- a/eZ/Publish/SPI/FieldType/ValidationError/NonConfigurableValidationError.php +++ b/src/contracts/FieldType/ValidationError/NonConfigurableValidationError.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\FieldType\ValidationError; +namespace Ibexa\Contracts\Core\FieldType\ValidationError; final class NonConfigurableValidationError extends AbstractValidationError { @@ -21,3 +21,5 @@ public function __construct(string $fieldTypeValidatorIdentifier, string $target ); } } + +class_alias(NonConfigurableValidationError::class, 'eZ\Publish\SPI\FieldType\ValidationError\NonConfigurableValidationError'); diff --git a/eZ/Publish/SPI/FieldType/ValidationError/UnknownValidatorValidationError.php b/src/contracts/FieldType/ValidationError/UnknownValidatorValidationError.php similarity index 75% rename from eZ/Publish/SPI/FieldType/ValidationError/UnknownValidatorValidationError.php rename to src/contracts/FieldType/ValidationError/UnknownValidatorValidationError.php index bf7c39a175..6c0f577909 100644 --- a/eZ/Publish/SPI/FieldType/ValidationError/UnknownValidatorValidationError.php +++ b/src/contracts/FieldType/ValidationError/UnknownValidatorValidationError.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\FieldType\ValidationError; +namespace Ibexa\Contracts\Core\FieldType\ValidationError; final class UnknownValidatorValidationError extends AbstractValidationError { @@ -21,3 +21,5 @@ public function __construct(string $validatorIdentifier, string $target) ); } } + +class_alias(UnknownValidatorValidationError::class, 'eZ\Publish\SPI\FieldType\ValidationError\UnknownValidatorValidationError'); diff --git a/src/contracts/FieldType/Value.php b/src/contracts/FieldType/Value.php new file mode 100644 index 0000000000..f58deadf24 --- /dev/null +++ b/src/contracts/FieldType/Value.php @@ -0,0 +1,22 @@ +inputStream; + } + + /** + * Sets the file's input resource. + * + * @param resource $inputStream + */ + public function setInputStream($inputStream) + { + $this->inputStream = $inputStream; + } +} + +class_alias(BinaryFileCreateStruct::class, 'eZ\Publish\SPI\IO\BinaryFileCreateStruct'); diff --git a/eZ/Publish/SPI/IO/MimeTypeDetector.php b/src/contracts/IO/MimeTypeDetector.php similarity index 83% rename from eZ/Publish/SPI/IO/MimeTypeDetector.php rename to src/contracts/IO/MimeTypeDetector.php index d1c8770eef..8b8c80d419 100644 --- a/eZ/Publish/SPI/IO/MimeTypeDetector.php +++ b/src/contracts/IO/MimeTypeDetector.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\SPI\IO; +namespace Ibexa\Contracts\Core\IO; interface MimeTypeDetector { @@ -26,3 +26,5 @@ public function getFromPath($path); */ public function getFromBuffer($buffer); } + +class_alias(MimeTypeDetector::class, 'eZ\Publish\SPI\IO\MimeTypeDetector'); diff --git a/src/contracts/Ibexa.php b/src/contracts/Ibexa.php new file mode 100644 index 0000000000..705660bedf --- /dev/null +++ b/src/contracts/Ibexa.php @@ -0,0 +1,17 @@ +VersionInfo::STATUS_DRAFT, VersionInfo::STATUS_PUBLISHED, VersionInfo::STATUS_ARCHIVED * - * @see \eZ\Publish\API\Repository\Values\Content\VersionInfo + * @see \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo * * @param int $status * * @return self * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function changeStatusTo(int $status): self { @@ -116,7 +114,7 @@ public function changeStatusTo(int $status): self * Set intent to update Content Version Fields. * * @param string|null $initialLanguageCode - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $fields + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $fields * * @return self */ @@ -144,7 +142,7 @@ static function (Field $field) { * * @return self * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function publishTranslations(array $languageCodes): self { @@ -159,3 +157,5 @@ public function publishTranslations(array $languageCodes): self return $this; } } + +class_alias(VersionBuilder::class, 'eZ\Publish\SPI\Limitation\Target\Builder\VersionBuilder'); diff --git a/src/contracts/Limitation/Target/DestinationLocation.php b/src/contracts/Limitation/Target/DestinationLocation.php index c944a3d6ee..31bd9269b6 100644 --- a/src/contracts/Limitation/Target/DestinationLocation.php +++ b/src/contracts/Limitation/Target/DestinationLocation.php @@ -8,9 +8,9 @@ namespace Ibexa\Contracts\Core\Limitation\Target; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\SPI\Limitation\Target; -use eZ\Publish\SPI\Persistence\ValueObject; +use Ibexa\Contracts\Core\Limitation\Target; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; /** * Location Limitation target. @@ -20,7 +20,7 @@ final class DestinationLocation extends ValueObject implements Target /** @var int */ private $locationId; - /** @var \eZ\Publish\API\Repository\Values\Content\ContentInfo */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo */ private $targetContentInfo; public function __construct(int $locationId, ContentInfo $targetContentInfo, array $properties = []) diff --git a/eZ/Publish/SPI/Limitation/Target/Version.php b/src/contracts/Limitation/Target/Version.php similarity index 78% rename from eZ/Publish/SPI/Limitation/Target/Version.php rename to src/contracts/Limitation/Target/Version.php index 96651f31d5..196dae3949 100644 --- a/eZ/Publish/SPI/Limitation/Target/Version.php +++ b/src/contracts/Limitation/Target/Version.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Limitation\Target; +namespace Ibexa\Contracts\Core\Limitation\Target; -use eZ\Publish\SPI\Limitation\Target; -use eZ\Publish\SPI\Persistence\ValueObject; +use Ibexa\Contracts\Core\Limitation\Target; +use Ibexa\Contracts\Core\Persistence\ValueObject; /** * Version Limitation target. Indicates an intent to create new Version. @@ -20,7 +20,7 @@ * @property-read string $forUpdateInitialLanguageCode * @property-read string[] $forUpdateLanguageCodesList * @property-read string[] $forPublishLanguageCodesList - * @property-read \eZ\Publish\API\Repository\Values\Content\Field[] $updatedFields + * @property-read \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $updatedFields */ final class Version extends ValueObject implements Target { @@ -62,15 +62,15 @@ final class Version extends ValueObject implements Target /** * One of the following: STATUS_DRAFT, STATUS_PUBLISHED, STATUS_ARCHIVED. * - * @see \eZ\Publish\API\Repository\Values\Content\VersionInfo::STATUS_DRAFT - * @see \eZ\Publish\API\Repository\Values\Content\VersionInfo::STATUS_PUBLISHED - * @see \eZ\Publish\API\Repository\Values\Content\VersionInfo::STATUS_ARCHIVED + * @see \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo::STATUS_DRAFT + * @see \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo::STATUS_PUBLISHED + * @see \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo::STATUS_ARCHIVED * * @var int|null */ protected $newStatus; - /** @var \eZ\Publish\API\Repository\Values\Content\Field[] */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Field[] */ protected $updatedFields = []; /** @@ -98,3 +98,5 @@ public function getTranslationsToDelete(): array return $this->translationsToDelete; } } + +class_alias(Version::class, 'eZ\Publish\SPI\Limitation\Target\Version'); diff --git a/src/contracts/Limitation/TargetAwareType.php b/src/contracts/Limitation/TargetAwareType.php new file mode 100644 index 0000000000..de65652542 --- /dev/null +++ b/src/contracts/Limitation/TargetAwareType.php @@ -0,0 +1,46 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue); + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@see Type::acceptValue()} is checked first. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue); + + /** + * Create the Limitation Value. + * + * The is the method to create values as Limitation type needs value knowledge anyway in acceptValue, + * the reverse relation is provided by means of identifier lookup (Value has identifier, and so does RoleService). + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues); + + /** + * Evaluate ("Vote") against a main value object and targets for the context. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * However if $object or $targets is unsupported by ROLE limitation, ACCESS_ABSTAIN should be returned! + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets An array of location, parent or "assignment" + * objects, if null: none where provided by caller + * + * @return bool|null Returns one of ACCESS_* constants + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, APIValueObject $object, array $targets = null); + + /** + * Returns Criterion for use in find() query. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException If the limitation does not support + * being used as a Criterion. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface|\Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalOperator + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser); + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_* constants. + * Note: The hash might be an instance of Traversable, and not a native php array. + */ + public function valueSchema(); +} + +class_alias(Type::class, 'eZ\Publish\SPI\Limitation\Type'); diff --git a/src/contracts/MVC/EventSubscriber/ConfigScopeChangeSubscriber.php b/src/contracts/MVC/EventSubscriber/ConfigScopeChangeSubscriber.php new file mode 100644 index 0000000000..18cdb0a38f --- /dev/null +++ b/src/contracts/MVC/EventSubscriber/ConfigScopeChangeSubscriber.php @@ -0,0 +1,21 @@ +fragmentRenderers[$name]; } } + +class_alias(BaseRenderStrategy::class, 'eZ\Publish\SPI\MVC\Templating\BaseRenderStrategy'); diff --git a/src/contracts/MVC/Templating/RenderStrategy.php b/src/contracts/MVC/Templating/RenderStrategy.php new file mode 100644 index 0000000000..94df12f6fb --- /dev/null +++ b/src/contracts/MVC/Templating/RenderStrategy.php @@ -0,0 +1,27 @@ +content = $content; + $this->contentInfo = $contentInfo; + $this->type = $type; + } + + public function getContent(): Content + { + return $this->content; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getType(): Type + { + return $this->type; + } +} + +class_alias(ContentItem::class, 'eZ\Publish\SPI\Persistence\Content\ContentItem'); diff --git a/src/contracts/Persistence/Content/CreateStruct.php b/src/contracts/Persistence/Content/CreateStruct.php new file mode 100644 index 0000000000..c4eb7f4dd9 --- /dev/null +++ b/src/contracts/Persistence/Content/CreateStruct.php @@ -0,0 +1,82 @@ + "New Article" ) */ + public $name; + + /** @var int */ + public $typeId; + + /** @var int */ + public $sectionId; + + /** @var int */ + public $ownerId; + + /** + * ContentId, contentVersion and mainLocationId are allowed to be left empty + * when used on with this struct as these values are created by the create method. + * + * @var \Ibexa\Contracts\Core\Persistence\Content\Location\CreateStruct[] + */ + public $locations = []; + + /** + * Contains *all* fields of the object to be created. + * + * This attribute should contain *all* fields (in all language) of the + * object to be created. If a field is not translatable, it may only occur + * once. The storage layer will automatically take care that such fields + * are assigned to each language version. + * + * @var Field[] + */ + public $fields = []; + + /** @var bool Always available flag */ + public $alwaysAvailable = false; + + /** @var string Remote identifier used as a custom identifier for the object */ + public $remoteId; + + /** + * Language id the content was initially created in. + * + * @var mixed + */ + public $initialLanguageId; + + /** + * Optional, main language of the content, if not set $initialLanguageId will be used instead. + * + * Typical use is copy operations where content main language and version initial language might differ. + * + * @var mixed|null + */ + public $mainLanguageId; + + /** + * Modification date. + * + * @var int + */ + public $modified; + + /** + * Is hidden flag. + * + * @var bool + */ + public $isHidden; +} + +class_alias(CreateStruct::class, 'eZ\Publish\SPI\Persistence\Content\CreateStruct'); diff --git a/src/contracts/Persistence/Content/Field.php b/src/contracts/Persistence/Content/Field.php new file mode 100644 index 0000000000..7c45810edd --- /dev/null +++ b/src/contracts/Persistence/Content/Field.php @@ -0,0 +1,68 @@ +value = clone $this->value; + } +} + +class_alias(Field::class, 'eZ\Publish\SPI\Persistence\Content\Field'); diff --git a/eZ/Publish/SPI/Persistence/Content/FieldTypeConstraints.php b/src/contracts/Persistence/Content/FieldTypeConstraints.php similarity index 75% rename from eZ/Publish/SPI/Persistence/Content/FieldTypeConstraints.php rename to src/contracts/Persistence/Content/FieldTypeConstraints.php index c7c29d99ed..ad28914353 100644 --- a/eZ/Publish/SPI/Persistence/Content/FieldTypeConstraints.php +++ b/src/contracts/Persistence/Content/FieldTypeConstraints.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\SPI\Persistence\Content; +namespace Ibexa\Contracts\Core\Persistence\Content; -use eZ\Publish\SPI\Persistence\ValueObject; +use Ibexa\Contracts\Core\Persistence\ValueObject; class FieldTypeConstraints extends ValueObject { @@ -17,7 +17,7 @@ class FieldTypeConstraints extends ValueObject * Note that contents of this property must be serializable and exportable * (i.e. no circular references, resources and friends). * - * @see \eZ\Publish\SPI\FieldType\FieldType + * @see \Ibexa\Contracts\Core\FieldType\FieldType * * @var mixed */ @@ -30,9 +30,11 @@ class FieldTypeConstraints extends ValueObject * Note that contents of this property must be serializable and exportable * (i.e. no circular references, resources and friends). * - * @see \eZ\Publish\SPI\FieldType\FieldType + * @see \Ibexa\Contracts\Core\FieldType\FieldType * * @var mixed */ public $fieldSettings; } + +class_alias(FieldTypeConstraints::class, 'eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints'); diff --git a/eZ/Publish/SPI/Persistence/Content/FieldValue.php b/src/contracts/Persistence/Content/FieldValue.php similarity index 87% rename from eZ/Publish/SPI/Persistence/Content/FieldValue.php rename to src/contracts/Persistence/Content/FieldValue.php index 45b8057792..f946b467bf 100644 --- a/eZ/Publish/SPI/Persistence/Content/FieldValue.php +++ b/src/contracts/Persistence/Content/FieldValue.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\SPI\Persistence\Content; +namespace Ibexa\Contracts\Core\Persistence\Content; -use eZ\Publish\SPI\Persistence\ValueObject; +use Ibexa\Contracts\Core\Persistence\ValueObject; class FieldValue extends ValueObject { @@ -46,3 +46,5 @@ class FieldValue extends ValueObject */ public $sortKey; } + +class_alias(FieldValue::class, 'eZ\Publish\SPI\Persistence\Content\FieldValue'); diff --git a/src/contracts/Persistence/Content/Handler.php b/src/contracts/Persistence/Content/Handler.php new file mode 100644 index 0000000000..5bfb72ed37 --- /dev/null +++ b/src/contracts/Persistence/Content/Handler.php @@ -0,0 +1,407 @@ +versionNo and + * $updateStruct->id as the published one. + * + * @param int $contentId + * @param int $versionNo + * @param \Ibexa\Contracts\Core\Persistence\Content\MetadataUpdateStruct $metaDataUpdateStruct + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * + * @return \Ibexa\Contracts\Core\Persistence\Content The published Content + */ + public function publish($contentId, $versionNo, MetadataUpdateStruct $metaDataUpdateStruct); + + /** + * Delete the specified translation from all the Versions of a Content Object. + * + * @param int $contentId + * @param string $languageCode language code of the translation + */ + public function deleteTranslationFromContent($contentId, $languageCode); + + /** + * Remove the specified Translation from the given Version Draft of a Content Object. + * + * @param int $contentId + * @param int $versionNo + * @param string $languageCode + * + * @return \Ibexa\Contracts\Core\Persistence\Content The Content Draft w/o removed Translation + */ + public function deleteTranslationFromDraft($contentId, $versionNo, $languageCode); + + /** + * @param array $contentIds + * + * @return array<\Ibexa\Contracts\Core\Persistence\Content\VersionInfo> + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function loadVersionInfoList(array $contentIds): array; +} + +class_alias(Handler::class, 'eZ\Publish\SPI\Persistence\Content\Handler'); diff --git a/src/contracts/Persistence/Content/Language.php b/src/contracts/Persistence/Content/Language.php new file mode 100644 index 0000000000..eb80b7a084 --- /dev/null +++ b/src/contracts/Persistence/Content/Language.php @@ -0,0 +1,45 @@ +parentId. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Location\CreateStruct $location + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Location + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if parent Location does not exist + */ + public function create(CreateStruct $location); + + /** + * Removes all Locations under and including $locationId. + * + * Performs a recursive delete on the location identified by $locationId, + * including all of its child locations. Content which is not referred to + * by any other location is automatically removed. Content which looses its + * main Location will get the first of its other Locations assigned as the + * new main Location. + * + * @param mixed $locationId + * + * @return bool + */ + public function removeSubtree($locationId); + + /** + * Set section on all content objects in the subtree. + * Only main locations will be updated. + * + * @todo This can be confusing (regarding permissions and main/multi location). + * So method is for the time being not in PublicAPI so people can instead + * write scripts using their own logic against the assignSectionToContent() api. + * + * @param mixed $locationId + * @param mixed $sectionId + */ + public function setSectionForSubtree($locationId, $sectionId); + + /** + * Changes main location of content identified by given $contentId to location identified by given $locationId. + * + * @param mixed $contentId + * @param mixed $locationId + */ + public function changeMainLocation($contentId, $locationId); + + /** + * Get the total number of all existing Locations. Can be combined with loadAllLocations. + * + * @return int + */ + public function countAllLocations(); + + /** + * Bulk-load all existing Locations, constrained by $limit and $offset to paginate results. + * + * @param int $offset + * @param int $limit + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Location[] + */ + public function loadAllLocations($offset, $limit); + + /** + * Counts locations for a given content represented by its id. + */ + public function countLocationsByContent(int $contentId): int; +} + +class_alias(Handler::class, 'eZ\Publish\SPI\Persistence\Content\Location\Handler'); diff --git a/src/contracts/Persistence/Content/Location/Trash/Handler.php b/src/contracts/Persistence/Content/Location/Trash/Handler.php new file mode 100644 index 0000000000..99bc01f0d5 --- /dev/null +++ b/src/contracts/Persistence/Content/Location/Trash/Handler.php @@ -0,0 +1,95 @@ +totalCount will ignore limit and offset and representing the total amount of trashed items + * matching the criterion. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion|null $criterion + * @param int $offset Offset to start listing from, 0 by default + * @param int $limit Limit for the listing. Null by default (no limit) + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause[] $sort + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Location\Trashed[]|\Ibexa\Contracts\Core\Persistence\Content\Location\Trash\TrashResult + */ + public function findTrashItems(Criterion $criterion = null, $offset = 0, $limit = null, array $sort = null); + + /** + * Empties the trash + * Everything contained in the trash must be removed. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResultList + */ + public function emptyTrash(); + + /** + * Removes a trashed location identified by $trashedLocationId from trash + * Associated content has to be deleted. + * + * @param int $trashedId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResult + */ + public function deleteTrashItem($trashedId); +} + +class_alias(Handler::class, 'eZ\Publish\SPI\Persistence\Content\Location\Trash\Handler'); diff --git a/src/contracts/Persistence/Content/Location/Trash/TrashResult.php b/src/contracts/Persistence/Content/Location/Trash/TrashResult.php new file mode 100644 index 0000000000..a5181bcc3a --- /dev/null +++ b/src/contracts/Persistence/Content/Location/Trash/TrashResult.php @@ -0,0 +1,37 @@ +items); + } +} + +class_alias(TrashResult::class, 'eZ\Publish\SPI\Persistence\Content\Location\Trash\TrashResult'); diff --git a/src/contracts/Persistence/Content/Location/Trashed.php b/src/contracts/Persistence/Content/Location/Trashed.php new file mode 100644 index 0000000000..e643a31c9e --- /dev/null +++ b/src/contracts/Persistence/Content/Location/Trashed.php @@ -0,0 +1,27 @@ + Location ID to a Content ID map of removed items */ + public array $removedLocationContentIdMap = []; +} + +class_alias(Trashed::class, 'eZ\Publish\SPI\Persistence\Content\Location\Trashed'); diff --git a/src/contracts/Persistence/Content/Location/UpdateStruct.php b/src/contracts/Persistence/Content/Location/UpdateStruct.php new file mode 100644 index 0000000000..afb3db2a03 --- /dev/null +++ b/src/contracts/Persistence/Content/Location/UpdateStruct.php @@ -0,0 +1,51 @@ +location = $location; + $this->contentInfo = $contentInfo; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } +} + +class_alias(LocationWithContentInfo::class, 'eZ\Publish\SPI\Persistence\Content\LocationWithContentInfo'); diff --git a/eZ/Publish/SPI/Persistence/Content/MetadataUpdateStruct.php b/src/contracts/Persistence/Content/MetadataUpdateStruct.php similarity index 86% rename from eZ/Publish/SPI/Persistence/Content/MetadataUpdateStruct.php rename to src/contracts/Persistence/Content/MetadataUpdateStruct.php index 5686619dfb..6b19f3a013 100644 --- a/eZ/Publish/SPI/Persistence/Content/MetadataUpdateStruct.php +++ b/src/contracts/Persistence/Content/MetadataUpdateStruct.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\SPI\Persistence\Content; +namespace Ibexa\Contracts\Core\Persistence\Content; -use eZ\Publish\SPI\Persistence\ValueObject; +use Ibexa\Contracts\Core\Persistence\ValueObject; class MetadataUpdateStruct extends ValueObject { @@ -68,3 +68,5 @@ class MetadataUpdateStruct extends ValueObject */ public $isHidden; } + +class_alias(MetadataUpdateStruct::class, 'eZ\Publish\SPI\Persistence\Content\MetadataUpdateStruct'); diff --git a/src/contracts/Persistence/Content/ObjectState.php b/src/contracts/Persistence/Content/ObjectState.php new file mode 100644 index 0000000000..b95d4ffffd --- /dev/null +++ b/src/contracts/Persistence/Content/ObjectState.php @@ -0,0 +1,83 @@ + + * array( 'eng-US' => '', 'ger-DE' => '' ); + * + * + * @var string[] + */ + public $name; + + /** + * Human readable description of the object state. + * + * The structure of this field is: + * + * array( 'eng-US' => '', 'ger-DE' => '' ); + * + * + * @var string[] + */ + public $description; +} + +class_alias(ObjectState::class, 'eZ\Publish\SPI\Persistence\Content\ObjectState'); diff --git a/src/contracts/Persistence/Content/ObjectState/Group.php b/src/contracts/Persistence/Content/ObjectState/Group.php new file mode 100644 index 0000000000..1d9c58bb6a --- /dev/null +++ b/src/contracts/Persistence/Content/ObjectState/Group.php @@ -0,0 +1,69 @@ + + * array( 'eng-US' => '', 'ger-DE' => '' ); + * + * + * @var string[] + */ + public $name; + + /** + * Human readable description of the object state group. + * + * The structure of this field is: + * + * array( 'eng-US' => '', 'ger-DE' => '' ); + * + * + * @var string[] + */ + public $description; +} + +class_alias(Group::class, 'eZ\Publish\SPI\Persistence\Content\ObjectState\Group'); diff --git a/src/contracts/Persistence/Content/ObjectState/Handler.php b/src/contracts/Persistence/Content/ObjectState/Handler.php new file mode 100644 index 0000000000..48bc70740a --- /dev/null +++ b/src/contracts/Persistence/Content/ObjectState/Handler.php @@ -0,0 +1,176 @@ + + * array( 'eng' => '', 'de' => '' ); + * + * + * @var string[] + */ + public $name; + + /** + * Human readable description of the content type. + * + * The structure of this field is: + * + * array( 'eng' => '', 'de' => '' ); + * + * + * @var string[] + */ + public $description = []; + + /** + * String identifier of a type. + * + * @var string + */ + public $identifier; + + /** + * Creation date (timestamp). + * + * @var int + */ + public $created; + + /** + * Modification date (timestamp). + * + * @var int + */ + public $modified; + + /** + * Creator user id. + * + * @var mixed + */ + public $creatorId; + + /** + * Modifier user id. + * + * @var mixed + */ + public $modifierId; + + /** + * Unique remote ID. + * + * @var string + */ + public $remoteId; + + /** + * URL alias schema. + * + * Same as {@see \Ibexa\Contracts\Core\Persistence\Content\Type::$nameSchema}. + * If nothing is provided, $nameSchema will be used instead. + * + * @var string + * + * @see \Ibexa\Contracts\Core\Persistence\Content\Type::$nameSchema + */ + public $urlAliasSchema; + + /** + * Name schema. + * Can be composed of FieldDefinition identifier place holders. + * These place holders must comply this pattern : . + * An OR condition can be used : + * + * In this example, field_def will be used if available. If not, other_field_def will be used for content name generation. + * + * @var string + */ + public $nameSchema; + + /** + * Determines if the type is a container. + * + * @var bool + */ + public $isContainer; + + /** + * Initial language. + * + * @var mixed + */ + public $initialLanguageId; + + /** + * Specifies which property the child locations should be sorted on by default when created. + * + * Valid values are found at {@link Location::SORT_FIELD_*} + * + * @var mixed + */ + public $sortField = Location::SORT_FIELD_PUBLISHED; + + /** + * Specifies whether the sort order should be ascending or descending by default when created. + * + * Valid values are {@link Location::SORT_ORDER_*} + * + * @var mixed + */ + public $sortOrder = Location::SORT_ORDER_DESC; + + /** + * Contains an array of type group IDs. + * + * @var mixed[] + */ + public $groupIds = []; + + /** + * Definitions for Content fields in this type. + * + * @var \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition[] + */ + public $fieldDefinitions = []; + + /** + * Defines if content objects should have always available enabled or not by default. + * + * Always available (when enabled) means main language is always available, and works as a editorial fallback + * language on load operations when translation filter is provided but no match is found. + * + * @var bool + */ + public $defaultAlwaysAvailable = false; + + /** + * Translations language codes. + * + * @var array + */ + public $languageCodes = []; +} + +class_alias(Type::class, 'eZ\Publish\SPI\Persistence\Content\Type'); diff --git a/src/contracts/Persistence/Content/Type/CreateStruct.php b/src/contracts/Persistence/Content/Type/CreateStruct.php new file mode 100644 index 0000000000..e86d3a7578 --- /dev/null +++ b/src/contracts/Persistence/Content/Type/CreateStruct.php @@ -0,0 +1,165 @@ + + * array( 'eng' => '', 'de' => '' ); + * + * + * @var string[] + */ + public $name; + + /** + * Version (state) to create. + * + * @var int + */ + public $status; + + /** + * Human readable description of the content type. + * + * The structure of this field is: + * + * array( 'eng' => '', 'de' => '' ); + * + * + * @var string[] + */ + public $description = []; + + /** + * String identifier of a type. + * + * @var string + */ + public $identifier; + + /** + * Creation date (timestamp). + * + * @var int + */ + public $created; + + /** + * Modification date (timestamp). + * + * @var int + */ + public $modified; + + /** + * Creator user id. + * + * @var mixed + */ + public $creatorId; + + /** + * Modifier user id. + * + * @var mixed + */ + public $modifierId; + + /** + * Unique remote ID. + * + * @var string + */ + public $remoteId; + + /** + * URL alias schema. + * + * @var string + */ + public $urlAliasSchema; + + /** + * Name schema. + * + * @var string + */ + public $nameSchema; + + /** + * Determines if the type is a container. + * + * @var bool + */ + public $isContainer; + + /** + * Initial language. + * + * @var mixed + */ + public $initialLanguageId; + + /** + * Specifies which property the child locations should be sorted on by default when created. + * + * Valid values are found at {@link Location::SORT_FIELD_*} + * + * @var mixed + */ + public $sortField = Location::SORT_FIELD_PUBLISHED; + + /** + * Specifies whether the sort order should be ascending or descending by default when created. + * + * Valid values are {@link Location::SORT_ORDER_*} + * + * @var mixed + */ + public $sortOrder = Location::SORT_ORDER_DESC; + + /** + * Contains an array of type group IDs. + * + * @var mixed[] + */ + public $groupIds = []; + + /** + * Content fields in this type. + * + * @var \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition[] + */ + public $fieldDefinitions = []; + + /** + * @todo: Document. + * + * @var bool + */ + public $defaultAlwaysAvailable = false; + + /** + * Performs a deep cloning. + */ + public function __clone() + { + foreach ($this->fieldDefinitions as $id => $fieldDef) { + $this->fieldDefinitions[$id] = clone $fieldDef; + } + } +} + +class_alias(CreateStruct::class, 'eZ\Publish\SPI\Persistence\Content\Type\CreateStruct'); diff --git a/src/contracts/Persistence/Content/Type/DeleteByParamsStruct.php b/src/contracts/Persistence/Content/Type/DeleteByParamsStruct.php new file mode 100644 index 0000000000..69bdfa07e5 --- /dev/null +++ b/src/contracts/Persistence/Content/Type/DeleteByParamsStruct.php @@ -0,0 +1,24 @@ +fieldTypeConstraints = new FieldTypeConstraints(); + $this->defaultValue = new FieldValue(); + parent::__construct($properties); + } +} + +class_alias(FieldDefinition::class, 'eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition'); diff --git a/src/contracts/Persistence/Content/Type/Group.php b/src/contracts/Persistence/Content/Type/Group.php new file mode 100644 index 0000000000..fde5eceb8b --- /dev/null +++ b/src/contracts/Persistence/Content/Type/Group.php @@ -0,0 +1,83 @@ +_' or another unique string. + * + * @param mixed $userId + * @param mixed $contentTypeId + * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If user or type with provided status is not found + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + public function copy($userId, $contentTypeId, $status); + + /** + * Unlink a content type group from a content type. + * + * @param mixed $groupId + * @param mixed $contentTypeId + * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If group or type with provided status is not found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If $groupId is last group on $contentTypeId or + * not a group assigned to type + */ + public function unlink($groupId, $contentTypeId, $status); + + /** + * Link a content type group with a content type. + * + * @param mixed $groupId + * @param mixed $contentTypeId + * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If group or type with provided status is not found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If type is already part of group + */ + public function link($groupId, $contentTypeId, $status); + + /** + * Returns field definition for the given field definition id. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If field definition is not found + * + * @param mixed $id + * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition + */ + public function getFieldDefinition($id, $status); + + /** + * Counts the number of Content instances of the ContentType identified by given $contentTypeId. + * + * @param mixed $contentTypeId + * + * @return int + */ + public function getContentCount($contentTypeId); + + /** + * Adds a new field definition to an existing Type. + * + * This method creates a new version of the Type with the $fieldDefinition + * added. It does not update existing content objects depending on the + * field (default) values. + * + * @param mixed $contentTypeId + * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDefinition + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If type is not found + * + * @todo Add FieldDefinition\CreateStruct? + */ + public function addFieldDefinition($contentTypeId, $status, FieldDefinition $fieldDefinition); + + /** + * Removes a field definition from an existing Type. + * + * This method creates a new version of the Type with the field definition + * referred to by $fieldDefinitionId removed. It does not update existing + * content objects depending on the field (default) values. + * + * @param mixed $contentTypeId + * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED + * @param mixed $fieldDefinitionId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If field is not found + */ + public function removeFieldDefinition( + int $contentTypeId, + int $status, + FieldDefinition $fieldDefinition + ): void; + + /** + * This method updates the given $fieldDefinition on a Type. + * + * This method creates a new version of the Type with the updated + * $fieldDefinition. It does not update existing content objects depending + * on the + * field (default) values. + * + * @param mixed $contentTypeId + * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDefinition + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If field is not found + * + * @todo Add FieldDefinition\UpdateStruct? + */ + public function updateFieldDefinition($contentTypeId, $status, FieldDefinition $fieldDefinition); + + /** + * Update content objects. + * + * Updates content objects, depending on the changed field definitions. + * + * A content type has a state which tells if its content objects yet have + * been adapted. + * + * Flags the content type as updated. + * + * @param mixed $contentTypeId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If type with $contentTypeId and Type::STATUS_DRAFT is not found + */ + public function publish($contentTypeId); + + /** + * Returns content type, field definition and field type mapping information + * for search engine usage. Only searchable field definitions will be included + * in the returned data. + * + * Returns an array in the form: + * + * + * array( + * "" => array( + * "" => array( + * "field_definition_id" => "", + * "field_type_identifier" => "", + * ), + * ... + * ), + * ... + * ) + * + * + * @return array + */ + public function getSearchableFieldMap(); + + /** + * @param int $contentTypeId + * @param string $languageCode + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + public function removeContentTypeTranslation(int $contentTypeId, string $languageCode): Type; + + /** + * @param int $userId + * @param int $status + */ + public function deleteByUserAndStatus(int $userId, int $status): void; +} + +class_alias(Handler::class, 'eZ\Publish\SPI\Persistence\Content\Type\Handler'); diff --git a/src/contracts/Persistence/Content/Type/UpdateStruct.php b/src/contracts/Persistence/Content/Type/UpdateStruct.php new file mode 100644 index 0000000000..3c6a1783e3 --- /dev/null +++ b/src/contracts/Persistence/Content/Type/UpdateStruct.php @@ -0,0 +1,119 @@ + + * array( 'eng' => '', 'de' => '' ); + * + * + * @var string[] + */ + public $name; + + /** + * Human readable description of the content type. + * + * The structure of this field is: + * + * array( 'eng' => '', 'de' => '' ); + * + * + * @var string[] + */ + public $description = []; + + /** + * String identifier of a type. + * + * @var string + */ + public $identifier; + + /** + * Modification date (timestamp). + * + * @var int + */ + public $modified; + + /** + * Modifier user id. + * + * @var mixed + */ + public $modifierId; + + /** + * Unique remote ID. + * + * @var string + */ + public $remoteId; + + /** + * URL alias schema. + * + * @var string + */ + public $urlAliasSchema; + + /** + * Name schema. + * + * @var string + */ + public $nameSchema; + + /** + * Determines if the type is a container. + * + * @var bool + */ + public $isContainer; + + /** + * Initial language. + * + * @var mixed + */ + public $initialLanguageId; + + /** + * Specifies which property the child locations should be sorted on by default when created. + * + * Valid values are found at {@link Location::SORT_FIELD_*} + * + * @var mixed + */ + public $sortField; + + /** + * Specifies whether the sort order should be ascending or descending by default when created. + * + * Valid values are {@link Location::SORT_ORDER_*} + * + * @var mixed + */ + public $sortOrder; + + /** + * @todo: Document. + * + * @var bool + */ + public $defaultAlwaysAvailable; +} + +class_alias(UpdateStruct::class, 'eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct'); diff --git a/src/contracts/Persistence/Content/UpdateStruct.php b/src/contracts/Persistence/Content/UpdateStruct.php new file mode 100644 index 0000000000..a7d0b6e479 --- /dev/null +++ b/src/contracts/Persistence/Content/UpdateStruct.php @@ -0,0 +1,46 @@ + "New Article" ) */ + public $name = []; + + /** + * Creator user ID for the version. + * + * @var int + */ + public $creatorId; + + /** + * Contains fields to be updated. + * + * @var \Ibexa\Contracts\Core\Persistence\Content\Field[] + */ + public $fields = []; + + /** + * Modification date for the version. + * Unix timestamp. + * + * @var int + */ + public $modificationDate; + + /** + * ID for initial (main) language for this version. + * + * @var mixed + */ + public $initialLanguageId = false; +} + +class_alias(UpdateStruct::class, 'eZ\Publish\SPI\Persistence\Content\UpdateStruct'); diff --git a/src/contracts/Persistence/Content/UrlAlias.php b/src/contracts/Persistence/Content/UrlAlias.php new file mode 100644 index 0000000000..3d3aa741a4 --- /dev/null +++ b/src/contracts/Persistence/Content/UrlAlias.php @@ -0,0 +1,107 @@ +-). + * + * @var string + */ + public $id; + + /** + * The type of the URL Alias i.e. one of URLAlias::LOCATION, URLAlias::RESOURCE, URLAlias::VIRTUAL. + * + * @var int + */ + public $type; + + /** + * If type = URLAlias::LOCATION the locationId + * otherwise a string (e.g. /content/search). + * + * @var mixed + */ + public $destination; + + /** + * Holds normalized path data. + * + * Example: + * + * array( + * array( + * "always-available" => true, + * "translations" => array( + * "cro-HR" => "jedan" + * ) + * ), + * array( + * "always-available" => false, + * "translations" => array( + * "cro-HR" => "dva", + * "eng-GB" => "two", + * ) + * ) + * ) + * + * + * @var array + */ + public $pathData; + + /** + * Language code of url alias entry. + * + * @var string[] + */ + public $languageCodes; + + /** + * Fallback indicator for other languages. + * + * @var bool + */ + public $alwaysAvailable; + + /** + * Indicates that this alias was autogenerated for an in the meanwhile archived version of the content. + * + * @var bool + */ + public $isHistory; + + /** + * If false this alias was autogenerated otherwise manuel created. + * + * @var bool + */ + public $isCustom; + + /** + * Indicates if the url should be redirected. + * + * @var bool + */ + public $forward; +} + +class_alias(UrlAlias::class, 'eZ\Publish\SPI\Persistence\Content\UrlAlias'); diff --git a/src/contracts/Persistence/Content/UrlAlias/Handler.php b/src/contracts/Persistence/Content/UrlAlias/Handler.php new file mode 100644 index 0000000000..1899770d73 --- /dev/null +++ b/src/contracts/Persistence/Content/UrlAlias/Handler.php @@ -0,0 +1,212 @@ +-" + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlAlias + */ + public function loadUrlAlias($id); + + /** + * Notifies the underlying engine that a location has moved. + * + * This method triggers the change of the autogenerated aliases + * + * @param mixed $locationId + * @param mixed $oldParentId + * @param mixed $newParentId + */ + public function locationMoved($locationId, $oldParentId, $newParentId); + + /** + * Notifies the underlying engine that a location was copied. + * + * This method triggers the creation of the autogenerated aliases for the copied locations + * + * @param mixed $locationId + * @param mixed $newLocationId + * @param mixed $newParentId + */ + public function locationCopied($locationId, $newLocationId, $newParentId); + + /** + * Notifies the underlying engine that a location was swapped. + * + * This method triggers the change of the autogenerated aliases. + * + * @param string|int $location1Id + * @param string|int $location1ParentId + * @param string|int $location2Id + * @param string|int $location2ParentId + */ + public function locationSwapped($location1Id, $location1ParentId, $location2Id, $location2ParentId); + + /** + * Notifies the underlying engine that a location was deleted or moved to trash. + * + * @param mixed $locationId + * + * @return array + */ + public function locationDeleted($locationId): array; + + /** + * Notifies the underlying engine that Locations Content Translation was removed. + * + * @param int[] $locationIds all Locations of the Content that got Translation removed + * @param string $languageCode language code of the removed Translation + */ + public function translationRemoved(array $locationIds, $languageCode); + + /** + * Archive UrlAliases for Translations that were removed from the underlying published content. + * + * @param int $locationId Location of underlying published Content Object + * @param int $parentLocationId + * @param array $languageCodes Language codes of currently published Content Object Translations + */ + public function archiveUrlAliasesForDeletedTranslations($locationId, $parentLocationId, array $languageCodes); + + /** + * Delete corrupted URL aliases (global, custom and system). + * + * @return int Number of deleted URL aliases + */ + public function deleteCorruptedUrlAliases(); + + /** + * Attempt repairing auto-generated URL aliases for the given Location (including history). + * + * Note: it is assumed that at this point original, working, URL Alias for Location is published. + * + * @param int $locationId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + public function repairBrokenUrlAliasesForLocation(int $locationId); +} + +class_alias(Handler::class, 'eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler'); diff --git a/eZ/Publish/SPI/Persistence/Content/UrlWildcard.php b/src/contracts/Persistence/Content/UrlWildcard.php similarity index 79% rename from eZ/Publish/SPI/Persistence/Content/UrlWildcard.php rename to src/contracts/Persistence/Content/UrlWildcard.php index 6afdde9dd1..57345165a5 100644 --- a/eZ/Publish/SPI/Persistence/Content/UrlWildcard.php +++ b/src/contracts/Persistence/Content/UrlWildcard.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\SPI\Persistence\Content; +namespace Ibexa\Contracts\Core\Persistence\Content; -use eZ\Publish\SPI\Persistence\ValueObject; +use Ibexa\Contracts\Core\Persistence\ValueObject; /** * UrlWildCard models one url alias path with wild cards. @@ -41,3 +41,5 @@ class UrlWildcard extends ValueObject */ public $forward; } + +class_alias(UrlWildcard::class, 'eZ\Publish\SPI\Persistence\Content\UrlWildcard'); diff --git a/src/contracts/Persistence/Content/UrlWildcard/Handler.php b/src/contracts/Persistence/Content/UrlWildcard/Handler.php new file mode 100644 index 0000000000..5ba68a395f --- /dev/null +++ b/src/contracts/Persistence/Content/UrlWildcard/Handler.php @@ -0,0 +1,101 @@ + "New Article" ). + * + * @return string[] + */ + public $names; + + /** + * Creation date of this version, as a UNIX timestamp. + * + * @var int + */ + public $creationDate; + + /** + * Last modified date of this version, as a UNIX timestamp. + * + * @var int + */ + public $modificationDate; + + /** + * Creator user ID. + * + * Creator of the version, in the search API this is referred to as the modifier of the published content. + * + * @var int + */ + public $creatorId; + + /** + * One of VersionInfo::STATUS_DRAFT, VersionInfo::STATUS_PUBLISHED, VersionInfo::STATUS_ARCHIVED. + * + * @var int + */ + public $status; + + /** + * In 4.x this is the language code which is used for labeling a translation. + * + * @var string + */ + public $initialLanguageCode; + + /** + * List of languages in this version. + * + * Reflects which languages fields exists in for this version. + * + * @var string[] + */ + public $languageCodes = []; +} + +class_alias(VersionInfo::class, 'eZ\Publish\SPI\Persistence\Content\VersionInfo'); diff --git a/src/contracts/Persistence/FieldType.php b/src/contracts/Persistence/FieldType.php new file mode 100644 index 0000000000..9a24d26389 --- /dev/null +++ b/src/contracts/Persistence/FieldType.php @@ -0,0 +1,24 @@ +totalCount; } - #[\ReturnTypeWillChange] + #[ReturnTypeWillChange] public function getIterator(): iterable { if (0 === $this->totalCount) { @@ -69,3 +70,5 @@ public function getIterator(): iterable } } } + +class_alias(LazyListIterator::class, 'eZ\Publish\SPI\Persistence\Filter\LazyListIterator'); diff --git a/src/contracts/Persistence/Filter/Location/Handler.php b/src/contracts/Persistence/Filter/Location/Handler.php new file mode 100644 index 0000000000..90e947cdc4 --- /dev/null +++ b/src/contracts/Persistence/Filter/Location/Handler.php @@ -0,0 +1,28 @@ +beginTransaction()} + */ + public function beginTransaction(); + + /** + * Commit transaction. + * + * Commit transaction, or throw exceptions if no transactions has been started. + * + * @throws \RuntimeException If no transaction has been started + * + * @deprecated Since 5.3 {@use transactionHandler()->commit()} + */ + public function commit(); + + /** + * Rollback transaction. + * + * Rollback transaction, or throw exceptions if no transactions has been started. + * + * @throws \RuntimeException If no transaction has been started + * + * @deprecated Since 5.3 {@use transactionHandler()->rollback()} + */ + public function rollback(); +} + +class_alias(Handler::class, 'eZ\Publish\SPI\Persistence\Handler'); diff --git a/src/contracts/Persistence/Notification/CreateStruct.php b/src/contracts/Persistence/Notification/CreateStruct.php new file mode 100644 index 0000000000..8945d82733 --- /dev/null +++ b/src/contracts/Persistence/Notification/CreateStruct.php @@ -0,0 +1,31 @@ +limitation is empty (null, empty string/array..) + */ + public function addPolicyByRoleDraft($roleId, Policy $policy); + + /** + * Adds a policy to a role. + * + * @param mixed $roleId + * @param \Ibexa\Contracts\Core\Persistence\User\Policy $policy + * + * @return \Ibexa\Contracts\Core\Persistence\User\Policy + * + * @todo Throw on invalid Role Id? + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If $policy->limitation is empty (null, empty string/array..) + */ + public function addPolicy($roleId, Policy $policy); + + /** + * Update a policy. + * + * Replaces limitations values with new values. + * + * @param \Ibexa\Contracts\Core\Persistence\User\Policy $policy + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If $policy->limitation is empty (null, empty string/array..) + */ + public function updatePolicy(Policy $policy); + + /** + * Removes a policy from a role. + * + * @param mixed $policyId + * @param mixed $roleId + * + * @todo Throw exception on missing role / policy? + */ + public function deletePolicy($policyId, $roleId); + + /** + * Returns the user policies associated with the user (including inherited policies from user groups). + * + * @deprecated Since 6.8, not currently in use as permission system needs to know about role assignment limitations. + * + * @param mixed $userId + * In legacy storage engine this is the content object id roles are assigned to in ezuser_role. + * + * @return \Ibexa\Contracts\Core\Persistence\User\Policy[] + */ + public function loadPoliciesByUserId($userId); + + /** + * Assigns role to a user or user group with given limitations. + * + * The limitation array looks like: + * + * array( + * 'Subtree' => array( + * '/1/2/', + * '/1/4/', + * ), + * 'Foo' => array( 'Bar' ), + * … + * ) + * + * + * Where the keys are the limitation identifiers, and the respective values + * are an array of limitation values. The limitation parameter is optional. + * + * @param mixed $contentId The groupId or userId to assign the role to. + * @param mixed $roleId + * @param array $limitation + */ + public function assignRole($contentId, $roleId, array $limitation = null); + + /** + * Un-assign a role. + * + * @param mixed $contentId The user or user group Id to un-assign the role from. + * @param mixed $roleId + */ + public function unassignRole($contentId, $roleId); + + /** + * Un-assign a role, by assignment ID. + * + * @param mixed $roleAssignmentId The assignment ID. + */ + public function removeRoleAssignment($roleAssignmentId); +} + +class_alias(Handler::class, 'eZ\Publish\SPI\Persistence\User\Handler'); diff --git a/src/contracts/Persistence/User/Policy.php b/src/contracts/Persistence/User/Policy.php new file mode 100644 index 0000000000..fb39e2013f --- /dev/null +++ b/src/contracts/Persistence/User/Policy.php @@ -0,0 +1,80 @@ + + * array( + * 'Subtree' => array( + * '/1/2/', + * '/1/4/', + * ), + * 'Foo' => array( 'Bar' ), + * … + * ) + * + * + * Where the keys are the limitation identifiers, and the respective values + * are an array of limitation values + * + * @var array|string If string, then only the value '*' is allowed, meaning all limitations. + * Can not be a empty array as '*' should be used in this case. + */ + public $limitations; +} + +class_alias(Policy::class, 'eZ\Publish\SPI\Persistence\User\Policy'); diff --git a/src/contracts/Persistence/User/Role.php b/src/contracts/Persistence/User/Role.php new file mode 100644 index 0000000000..176b188b4f --- /dev/null +++ b/src/contracts/Persistence/User/Role.php @@ -0,0 +1,81 @@ + $contentInfoList + * + * @return array List of VersionInfo items with Content Ids as keys + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function loadVersionInfoListByContentInfo(array $contentInfoList): array; + + /** + * Loads content in a version for the given content info object. + * + * If no version number is given, the method returns the current version + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if version with the given number does not exist + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param array $languages A language priority, filters returned fields and is used as prioritized language code on + * returned value object. If not given all languages are returned. + * @param int|null $versionNo the version number. If not given the current version is returned from $contentInfo + * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function loadContentByContentInfo(ContentInfo $contentInfo, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content; + + /** + * Loads content in the version given by version info. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on + * returned value object. If not given all languages are returned. + * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function loadContentByVersionInfo(VersionInfo $versionInfo, array $languages = null, bool $useAlwaysAvailable = true): Content; + + /** + * Loads content in a version of the given content object. + * + * If no version number is given, the method returns the current version + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the content or version with the given id and languages does not exist + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions + * + * @param mixed $contentId + * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on + * returned value object. If not given all languages are returned. + * @param int|null $versionNo the version number. If not given the current version is returned + * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function loadContent(int $contentId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content; + + /** + * Loads content in a version for the content object reference by the given remote id. + * + * If no version is given, the method returns the current version + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the content or version with the given remote id does not exist + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions + * + * @param string $remoteId + * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on + * returned value object. If not given all languages are returned. + * @param int|null $versionNo the version number. If not given the current version is returned + * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function loadContentByRemoteId(string $remoteId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content; + + /** + * Bulk-load Content items by the list of ContentInfo Value Objects. + * + * Note: it does not throw exceptions on load, just ignores erroneous Content item. + * Moreover, since the method works on pre-loaded ContentInfo list, it is assumed that user is + * allowed to access every Content on the list. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo[] $contentInfoList + * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on + * returned value object. If not given all languages are returned. + * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true, + * unless all languages have been asked for. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content[] list of Content items with Content Ids as keys + */ + public function loadContentListByContentInfo(array $contentInfoList, array $languages = [], bool $useAlwaysAvailable = true): iterable; + + /** + * Creates a new content draft assigned to the authenticated user. + * + * If a different userId is given in $contentCreateStruct it is assigned to the given user + * but this required special rights for the authenticated user + * (this is useful for content staging where the transfer process does not + * have to authenticate with the user which created the content object in the source server). + * The user has to publish the draft if it should be visible. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to create the content in the given location + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is a provided remote ID which exists in the system or multiple Locations + * are under the same parent or if the a field value is not accepted by the field type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException if a required field is missing or is set to an empty value + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct $contentCreateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs an array of {@see \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct} for each location parent under which a location should be created for the content + * While optional, it's highly recommended to use Locations for content as a lot of features in the system is usually tied to the tree structure (including default Role policies). + * @param string[]|null $fieldIdentifiersToValidate List of field identifiers for partial validation or null + * for case of full validation. Empty identifiers array is equal to no validation. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content - the newly created content draft + */ + public function createContent(ContentCreateStruct $contentCreateStruct, array $locationCreateStructs = [], ?array $fieldIdentifiersToValidate = null): Content; + + /** + * Updates the metadata. + * + * See {@see ContentMetadataUpdateStruct} of a content object - to update fields use updateContent + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to update the content meta data + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the remoteId in $contentMetadataUpdateStruct is set but already exists + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentMetadataUpdateStruct $contentMetadataUpdateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content the content with the updated attributes + */ + public function updateContentMetadata(ContentInfo $contentInfo, ContentMetadataUpdateStruct $contentMetadataUpdateStruct): Content; + + /** + * Deletes a content object including all its versions and locations including their subtrees. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete the content (in one of the locations of the given content object) + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return int[] Affected Location Id's (List of Locations of the Content that was deleted) + */ + public function deleteContent(ContentInfo $contentInfo): iterable; + + /** + * Creates a draft from a published or archived version. + * + * If no version is given, the current published version is used. + * 4.x: The draft is created with the initialLanguage code of the source version or if not present with the main language. + * It can be changed on updating the version. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to create the draft + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo|null $versionInfo + * @param \Ibexa\Contracts\Core\Repository\Values\User\User|null $creator Used as creator of the draft if given - otherwise uses current-user + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Language|null if not set the draft is created with the initialLanguage code of the source version or if not present with the main language. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content - the newly created content draft + */ + public function createContentDraft( + ContentInfo $contentInfo, + ?VersionInfo $versionInfo = null, + ?User $creator = null, + ?Language $language = null + ): Content; + + /** + * Counts drafts for a user. + * + * If no user is given the number of drafts for the authenticated user are returned + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user The user to load drafts for, if defined, otherwise drafts for current-user + * + * @return int The number of drafts ({@see VersionInfo}) owned by the given user + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function countContentDrafts(?User $user = null): int; + + /** + * Loads drafts for a user. + * + * If no user is given the drafts for the authenticated user are returned + * + * @deprecated Please use {@see ContentService::loadContentDraftList()} instead to avoid risking loading too much data. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to load the draft list + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user The user to load drafts for, if defined, otherwise drafts for current-user + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo[] the drafts ({@see VersionInfo}) owned by the given user + */ + public function loadContentDrafts(?User $user = null): iterable; + + /** + * Loads drafts for a user when content is not in the trash. The list is sorted by modification date. + * + * If no user is given the drafts for the authenticated user are returned + * + * @since 7.5.5 + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User|null $user The user to load drafts for, if defined, otherwise drafts for current-user + * @param int $offset + * @param int $limit + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentDraftList + */ + public function loadContentDraftList(?User $user = null, int $offset = 0, int $limit = -1): ContentDraftList; + + /** + * Updates the fields of a draft. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to update this version + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException if a field in the $contentUpdateStruct is not valid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException if a required field is set to an empty value + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if a field value is not accepted by the field type + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct + * @param string[]|null $fieldIdentifiersToValidate List of field identifiers for partial validation or null + * for case of full validation. Empty identifiers array is equal to no validation. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content the content draft with the updated fields + */ + public function updateContent(VersionInfo $versionInfo, ContentUpdateStruct $contentUpdateStruct, ?array $fieldIdentifiersToValidate = null): Content; + + /** + * Publishes a content version. + * + * Publishes a content version and deletes archive versions if they overflow max archive versions. + * Max archive versions are currently a configuration for default max limit, by default set to 5. + * + * @todo Introduce null|int ContentType->versionArchiveLimit to be able to let admins override this per type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to publish this version + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + * @param string[] $translations List of language codes of translations which will be included + * in a published version. + * By default all translations from the current version will be published. + * If the list is provided but does not cover all currently published translations, + * the missing ones will be copied from the currently published version, + * overriding those in the current version. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function publishVersion(VersionInfo $versionInfo, array $translations = Language::ALL): Content; + + /** + * Removes the given version. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is in + * published state or is a last version of Content in non draft state + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove this version + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + */ + public function deleteVersion(VersionInfo $versionInfo): void; + + /** + * Loads all versions for the given content. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to list versions + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the given status is invalid + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param int|null $status + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo[] an array of {@see \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo} sorted by creation date + */ + public function loadVersions(ContentInfo $contentInfo, ?int $status = null): iterable; + + /** + * Copies the content to a new location. If no version is given, + * all versions are copied, otherwise only the given version. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to copy the content to the given location + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct $destinationLocationCreateStruct the target location where the content is copied to + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function copyContent(ContentInfo $contentInfo, LocationCreateStruct $destinationLocationCreateStruct, ?VersionInfo $versionInfo = null): Content; + + /** + * Loads all outgoing relations for the given version. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version + * + * @deprecated 4.5.7 The "ContentService::loadRelations()" method is deprecated, will be removed in 5.0. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] + */ + public function loadRelations(VersionInfo $versionInfo): iterable; + + /** + * Loads all outgoing relations for the given version. + * + * If the user is not allowed to read specific version then a returned `RelationList` will contain `UnauthorizedRelationListItem` + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * + * @see \Ibexa\Contracts\Core\Repository\Values\Content\RelationList\Item\UnauthorizedRelationListItem + */ + public function loadRelationList( + VersionInfo $versionInfo, + int $offset = 0, + int $limit = self::DEFAULT_PAGE_SIZE + ): RelationList; + + /** + * Counts all outgoing relations for the given version. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + public function countRelations(VersionInfo $versionInfo): int; + + /** + * Counts all incoming relations for the given content object. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return int The number of reverse relations ({@see \Ibexa\Contracts\Core\Repository\Values\Content\Relation}) + */ + public function countReverseRelations(ContentInfo $contentInfo): int; + + /** + * Loads all incoming relations for a content object. + * + * The relations come only from published versions of the source content objects + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] + */ + public function loadReverseRelations(ContentInfo $contentInfo): iterable; + + /** + * Loads all incoming relations for a content object. + * + * The relations come only from published versions of the source content objects. + * If the user is not allowed to read specific version then UnauthorizedRelationListItem is returned + * {@see \Ibexa\Contracts\Core\Repository\Values\Content\RelationList\Item\UnauthorizedRelationListItem} + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param int $offset + * @param int $limit + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\RelationList + */ + public function loadReverseRelationList(ContentInfo $contentInfo, int $offset = 0, int $limit = -1): RelationList; + + /** + * Adds a relation of type common. + * + * The source of the relation is the content and version + * referenced by $versionInfo. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit this version + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $sourceVersion + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $destinationContent the destination of the relation + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation the newly created relation + */ + public function addRelation(VersionInfo $sourceVersion, ContentInfo $destinationContent): Relation; + + /** + * Removes a relation of type COMMON from a draft. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed edit this version + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is no relation of type COMMON for the given destination + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $sourceVersion + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $destinationContent + */ + public function deleteRelation(VersionInfo $sourceVersion, ContentInfo $destinationContent): void; + + /** + * Delete Content item Translation from all Versions (including archived ones) of a Content Object. + * + * NOTE: this operation is risky and permanent, so user interface should provide a warning before performing it. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the specified Translation + * is the Main Translation of a Content Item. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed + * to delete the content (in one of the locations of the given Content Item). + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if languageCode argument + * is invalid for the given content. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param string $languageCode + * + * @since 6.13 + */ + public function deleteTranslation(ContentInfo $contentInfo, string $languageCode): void; + + /** + * Delete specified Translation from a Content Draft. + * + * When using together with ContentService::publishVersion() method, make sure to not provide deleted translation + * in translations array, as it is going to be copied again from published version. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the specified Translation + * is the only one the Content Draft has or it is the main Translation of a Content Object. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed + * to edit the Content (in one of the locations of the given Content Object). + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if languageCode argument + * is invalid for the given Draft. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if specified Version was not found + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo Content Version Draft + * @param string $languageCode Language code of the Translation to be removed + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content Content Draft w/o the specified Translation + * + * @since 6.12 + */ + public function deleteTranslationFromDraft(VersionInfo $versionInfo, string $languageCode): Content; + + /** + * Hides Content by making all the Locations appear hidden. + * It does not persist hidden state on Location object itself. + * + * Content hidden by this API can be revealed by revealContent API. + * + * @see ContentService::revealContent() + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + */ + public function hideContent(ContentInfo $contentInfo): void; + + /** + * Reveals Content hidden by hideContent API. + * Locations which were hidden before hiding Content will remain hidden. + * + * @see ContentService::hideContent() + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + */ + public function revealContent(ContentInfo $contentInfo): void; + + /** + * Instantiates a new content create struct object. + * + * alwaysAvailable is set to the ContentType's defaultAlwaysAvailable + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param string $mainLanguageCode + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct + */ + public function newContentCreateStruct(ContentType $contentType, string $mainLanguageCode): ContentCreateStruct; + + /** + * Instantiates a new content meta data update struct. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentMetadataUpdateStruct + */ + public function newContentMetadataUpdateStruct(): ContentMetadataUpdateStruct; + + /** + * Instantiates a new content update struct. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct + */ + public function newContentUpdateStruct(): ContentUpdateStruct; + + /** + * Validates given content related ValueObject returning field errors structure as a result. + * + * @param array $context Additional context parameters to be used by validators. + * @param string[]|null $fieldIdentifiersToValidate List of field identifiers for partial validation or null + * for case of full validation. Empty identifiers array is equal to no validation. + * + * @return array Validation errors grouped by field definition and language code, in format: + * $returnValue[string|int $fieldDefinitionId][string $languageCode] = $fieldErrors; + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function validate(ValueObject $object, array $context, ?array $fieldIdentifiersToValidate = null): array; + + /** + * Fetch Content items from the Repository filtered by the given conditions. + * + * @param string[] $languages a list of language codes to be added as additional constraints. + * If skipped, by default, unless SiteAccessAware layer has been disabled, languages set + * for a SiteAccess in a current context will be used. + */ + public function find(Filter $filter, ?array $languages = null): ContentList; + + /** + * Count total number of items returned by {@see ContentService::find()} method. + * + * @param string[] $languages a list of language codes to be added as additional constraints. + * If skipped, by default, unless SiteAccessAware layer has been disabled, languages set + * for a SiteAccess in a current context will be used. + */ + public function count(Filter $filter, ?array $languages = null): int; +} + +class_alias(ContentService::class, 'eZ\Publish\API\Repository\ContentService'); diff --git a/src/contracts/Repository/ContentTypeService.php b/src/contracts/Repository/ContentTypeService.php new file mode 100644 index 0000000000..d17aafe53c --- /dev/null +++ b/src/contracts/Repository/ContentTypeService.php @@ -0,0 +1,398 @@ +innerService = $innerService; + } + + public function createBookmark(Location $location): void + { + $this->innerService->createBookmark($location); + } + + public function deleteBookmark(Location $location): void + { + $this->innerService->deleteBookmark($location); + } + + public function loadBookmarks( + int $offset = 0, + int $limit = 25 + ): BookmarkList { + return $this->innerService->loadBookmarks($offset, $limit); + } + + public function isBookmarked(Location $location): bool + { + return $this->innerService->isBookmarked($location); + } +} + +class_alias(BookmarkServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\BookmarkServiceDecorator'); diff --git a/eZ/Publish/SPI/Repository/Decorator/ContentServiceDecorator.php b/src/contracts/Repository/Decorator/ContentServiceDecorator.php similarity index 83% rename from eZ/Publish/SPI/Repository/Decorator/ContentServiceDecorator.php rename to src/contracts/Repository/Decorator/ContentServiceDecorator.php index e45b5038be..3575c0df42 100644 --- a/eZ/Publish/SPI/Repository/Decorator/ContentServiceDecorator.php +++ b/src/contracts/Repository/Decorator/ContentServiceDecorator.php @@ -6,29 +6,29 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Decorator; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentDraftList; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\ContentList; -use eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\Content\RelationList; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\ValueObject; +namespace Ibexa\Contracts\Core\Repository\Decorator; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentDraftList; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentList; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentMetadataUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation; +use Ibexa\Contracts\Core\Repository\Values\Content\RelationList; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; abstract class ContentServiceDecorator implements ContentService { - /** @var \eZ\Publish\API\Repository\ContentService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentService */ protected $innerService; public function __construct(ContentService $innerService) @@ -193,6 +193,16 @@ public function loadRelations(VersionInfo $versionInfo): iterable return $this->innerService->loadRelations($versionInfo); } + public function countRelations(VersionInfo $versionInfo): int + { + return $this->innerService->countRelations($versionInfo); + } + + public function loadRelationList(VersionInfo $versionInfo, int $offset = 0, int $limit = self::DEFAULT_PAGE_SIZE): RelationList + { + return $this->innerService->loadRelationList($versionInfo, $offset, $limit); + } + public function countReverseRelations(ContentInfo $contentInfo): int { return $this->innerService->countReverseRelations($contentInfo); @@ -281,3 +291,5 @@ public function count(Filter $filter, ?array $languages = null): int return $this->innerService->count($filter, $languages); } } + +class_alias(ContentServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\ContentServiceDecorator'); diff --git a/eZ/Publish/SPI/Repository/Decorator/ContentTypeServiceDecorator.php b/src/contracts/Repository/Decorator/ContentTypeServiceDecorator.php similarity index 86% rename from eZ/Publish/SPI/Repository/Decorator/ContentTypeServiceDecorator.php rename to src/contracts/Repository/Decorator/ContentTypeServiceDecorator.php index 0879dfe2c8..863e725d19 100644 --- a/eZ/Publish/SPI/Repository/Decorator/ContentTypeServiceDecorator.php +++ b/src/contracts/Repository/Decorator/ContentTypeServiceDecorator.php @@ -6,24 +6,24 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Decorator; - -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct; -use eZ\Publish\API\Repository\Values\User\User; +namespace Ibexa\Contracts\Core\Repository\Decorator; + +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\User; abstract class ContentTypeServiceDecorator implements ContentTypeService { - /** @var \eZ\Publish\API\Repository\ContentTypeService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService */ protected $innerService; public function __construct(ContentTypeService $innerService) @@ -228,3 +228,5 @@ public function deleteUserDrafts(int $userId): void $this->innerService->deleteUserDrafts($userId); } } + +class_alias(ContentTypeServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\ContentTypeServiceDecorator'); diff --git a/src/contracts/Repository/Decorator/FieldTypeServiceDecorator.php b/src/contracts/Repository/Decorator/FieldTypeServiceDecorator.php new file mode 100644 index 0000000000..f4411748e7 --- /dev/null +++ b/src/contracts/Repository/Decorator/FieldTypeServiceDecorator.php @@ -0,0 +1,40 @@ +innerService = $innerService; + } + + public function getFieldTypes(): iterable + { + return $this->innerService->getFieldTypes(); + } + + public function getFieldType(string $identifier): FieldType + { + return $this->innerService->getFieldType($identifier); + } + + public function hasFieldType(string $identifier): bool + { + return $this->innerService->hasFieldType($identifier); + } +} + +class_alias(FieldTypeServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\FieldTypeServiceDecorator'); diff --git a/eZ/Publish/SPI/Repository/Decorator/LanguageServiceDecorator.php b/src/contracts/Repository/Decorator/LanguageServiceDecorator.php similarity index 84% rename from eZ/Publish/SPI/Repository/Decorator/LanguageServiceDecorator.php rename to src/contracts/Repository/Decorator/LanguageServiceDecorator.php index 701614cc59..0dae7624aa 100644 --- a/eZ/Publish/SPI/Repository/Decorator/LanguageServiceDecorator.php +++ b/src/contracts/Repository/Decorator/LanguageServiceDecorator.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Decorator; +namespace Ibexa\Contracts\Core\Repository\Decorator; -use eZ\Publish\API\Repository\LanguageService; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\LanguageCreateStruct; +use Ibexa\Contracts\Core\Repository\LanguageService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\LanguageCreateStruct; abstract class LanguageServiceDecorator implements LanguageService { - /** @var \eZ\Publish\API\Repository\LanguageService */ + /** @var \Ibexa\Contracts\Core\Repository\LanguageService */ protected $innerService; public function __construct(LanguageService $innerService) @@ -84,3 +84,5 @@ public function newLanguageCreateStruct(): LanguageCreateStruct return $this->innerService->newLanguageCreateStruct(); } } + +class_alias(LanguageServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\LanguageServiceDecorator'); diff --git a/eZ/Publish/SPI/Repository/Decorator/LocationServiceDecorator.php b/src/contracts/Repository/Decorator/LocationServiceDecorator.php similarity index 86% rename from eZ/Publish/SPI/Repository/Decorator/LocationServiceDecorator.php rename to src/contracts/Repository/Decorator/LocationServiceDecorator.php index 31885e27b9..5f11c4cab3 100644 --- a/eZ/Publish/SPI/Repository/Decorator/LocationServiceDecorator.php +++ b/src/contracts/Repository/Decorator/LocationServiceDecorator.php @@ -6,20 +6,20 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Decorator; +namespace Ibexa\Contracts\Core\Repository\Decorator; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\LocationList; -use eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\Filter\Filter; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationList; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; abstract class LocationServiceDecorator implements LocationService { - /** @var \eZ\Publish\API\Repository\LocationService */ + /** @var \Ibexa\Contracts\Core\Repository\LocationService */ protected $innerService; public function __construct(LocationService $innerService) @@ -165,3 +165,5 @@ public function count(Filter $filter, ?array $languages = null): int return $this->innerService->count($filter, $languages); } } + +class_alias(LocationServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\LocationServiceDecorator'); diff --git a/src/contracts/Repository/Decorator/NotificationServiceDecorator.php b/src/contracts/Repository/Decorator/NotificationServiceDecorator.php new file mode 100644 index 0000000000..34bfac43a0 --- /dev/null +++ b/src/contracts/Repository/Decorator/NotificationServiceDecorator.php @@ -0,0 +1,64 @@ +innerService = $innerService; + } + + public function loadNotifications( + int $offset, + int $limit + ): NotificationList { + return $this->innerService->loadNotifications($offset, $limit); + } + + public function getNotification(int $notificationId): Notification + { + return $this->innerService->getNotification($notificationId); + } + + public function markNotificationAsRead(Notification $notification): void + { + $this->innerService->markNotificationAsRead($notification); + } + + public function getPendingNotificationCount(): int + { + return $this->innerService->getPendingNotificationCount(); + } + + public function getNotificationCount(): int + { + return $this->innerService->getNotificationCount(); + } + + public function createNotification(CreateStruct $createStruct): Notification + { + return $this->innerService->createNotification($createStruct); + } + + public function deleteNotification(Notification $notification): void + { + $this->innerService->deleteNotification($notification); + } +} + +class_alias(NotificationServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\NotificationServiceDecorator'); diff --git a/eZ/Publish/SPI/Repository/Decorator/ObjectStateServiceDecorator.php b/src/contracts/Repository/Decorator/ObjectStateServiceDecorator.php similarity index 85% rename from eZ/Publish/SPI/Repository/Decorator/ObjectStateServiceDecorator.php rename to src/contracts/Repository/Decorator/ObjectStateServiceDecorator.php index 31ddc37dac..848dd1e11d 100644 --- a/eZ/Publish/SPI/Repository/Decorator/ObjectStateServiceDecorator.php +++ b/src/contracts/Repository/Decorator/ObjectStateServiceDecorator.php @@ -6,20 +6,20 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Decorator; +namespace Ibexa\Contracts\Core\Repository\Decorator; -use eZ\Publish\API\Repository\ObjectStateService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectState; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateUpdateStruct; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateUpdateStruct; abstract class ObjectStateServiceDecorator implements ObjectStateService { - /** @var \eZ\Publish\API\Repository\ObjectStateService */ + /** @var \Ibexa\Contracts\Core\Repository\ObjectStateService */ protected $innerService; public function __construct(ObjectStateService $innerService) @@ -158,3 +158,5 @@ public function newObjectStateUpdateStruct(): ObjectStateUpdateStruct return $this->innerService->newObjectStateUpdateStruct(); } } + +class_alias(ObjectStateServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\ObjectStateServiceDecorator'); diff --git a/eZ/Publish/SPI/Repository/Decorator/RoleServiceDecorator.php b/src/contracts/Repository/Decorator/RoleServiceDecorator.php similarity index 83% rename from eZ/Publish/SPI/Repository/Decorator/RoleServiceDecorator.php rename to src/contracts/Repository/Decorator/RoleServiceDecorator.php index db5916b537..4b13dcd2bd 100644 --- a/eZ/Publish/SPI/Repository/Decorator/RoleServiceDecorator.php +++ b/src/contracts/Repository/Decorator/RoleServiceDecorator.php @@ -5,31 +5,31 @@ * @license For full copyright and license information view LICENSE file distributed with this source code. */ /** - * @copyright Copyright (C) eZ Systems AS. All rights reserved. + * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Decorator; - -use eZ\Publish\API\Repository\RoleService; -use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation; -use eZ\Publish\API\Repository\Values\User\PolicyCreateStruct; -use eZ\Publish\API\Repository\Values\User\PolicyDraft; -use eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct; -use eZ\Publish\API\Repository\Values\User\Role; -use eZ\Publish\API\Repository\Values\User\RoleAssignment; -use eZ\Publish\API\Repository\Values\User\RoleCopyStruct; -use eZ\Publish\API\Repository\Values\User\RoleCreateStruct; -use eZ\Publish\API\Repository\Values\User\RoleDraft; -use eZ\Publish\API\Repository\Values\User\RoleUpdateStruct; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\SPI\Limitation\Type; +namespace Ibexa\Contracts\Core\Repository\Decorator; + +use Ibexa\Contracts\Core\Limitation\Type; +use Ibexa\Contracts\Core\Repository\RoleService; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\Role; +use Ibexa\Contracts\Core\Repository\Values\User\RoleAssignment; +use Ibexa\Contracts\Core\Repository\Values\User\RoleCopyStruct; +use Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\RoleDraft; +use Ibexa\Contracts\Core\Repository\Values\User\RoleUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroup; abstract class RoleServiceDecorator implements RoleService { - /** @var \eZ\Publish\API\Repository\RoleService */ + /** @var \Ibexa\Contracts\Core\Repository\RoleService */ protected $innerService; public function __construct(RoleService $innerService) @@ -215,3 +215,5 @@ public function getLimitationTypesByModuleFunction( return $this->innerService->getLimitationTypesByModuleFunction($module, $function); } } + +class_alias(RoleServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\RoleServiceDecorator'); diff --git a/eZ/Publish/SPI/Repository/Decorator/SearchServiceDecorator.php b/src/contracts/Repository/Decorator/SearchServiceDecorator.php similarity index 75% rename from eZ/Publish/SPI/Repository/Decorator/SearchServiceDecorator.php rename to src/contracts/Repository/Decorator/SearchServiceDecorator.php index 18ed551273..82f75f5f55 100644 --- a/eZ/Publish/SPI/Repository/Decorator/SearchServiceDecorator.php +++ b/src/contracts/Repository/Decorator/SearchServiceDecorator.php @@ -6,18 +6,18 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Decorator; +namespace Ibexa\Contracts\Core\Repository\Decorator; -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; abstract class SearchServiceDecorator implements SearchService { - /** @var \eZ\Publish\API\Repository\SearchService */ + /** @var \Ibexa\Contracts\Core\Repository\SearchService */ protected $innerService; public function __construct(SearchService $innerService) @@ -71,3 +71,5 @@ public function supports(int $capabilityFlag): bool return $this->innerService->supports($capabilityFlag); } } + +class_alias(SearchServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\SearchServiceDecorator'); diff --git a/eZ/Publish/SPI/Repository/Decorator/SectionServiceDecorator.php b/src/contracts/Repository/Decorator/SectionServiceDecorator.php similarity index 78% rename from eZ/Publish/SPI/Repository/Decorator/SectionServiceDecorator.php rename to src/contracts/Repository/Decorator/SectionServiceDecorator.php index ae1d874e41..735b2361c3 100644 --- a/eZ/Publish/SPI/Repository/Decorator/SectionServiceDecorator.php +++ b/src/contracts/Repository/Decorator/SectionServiceDecorator.php @@ -5,23 +5,23 @@ * @license For full copyright and license information view LICENSE file distributed with this source code. */ /** - * @copyright Copyright (C) eZ Systems AS. All rights reserved. + * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Decorator; +namespace Ibexa\Contracts\Core\Repository\Decorator; -use eZ\Publish\API\Repository\SectionService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Section; -use eZ\Publish\API\Repository\Values\Content\SectionCreateStruct; -use eZ\Publish\API\Repository\Values\Content\SectionUpdateStruct; +use Ibexa\Contracts\Core\Repository\SectionService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\Section; +use Ibexa\Contracts\Core\Repository\Values\Content\SectionCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\SectionUpdateStruct; abstract class SectionServiceDecorator implements SectionService { - /** @var \eZ\Publish\API\Repository\SectionService */ + /** @var \Ibexa\Contracts\Core\Repository\SectionService */ protected $innerService; public function __construct(SectionService $innerService) @@ -95,3 +95,5 @@ public function newSectionUpdateStruct(): SectionUpdateStruct return $this->innerService->newSectionUpdateStruct(); } } + +class_alias(SectionServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\SectionServiceDecorator'); diff --git a/src/contracts/Repository/Decorator/SettingServiceDecorator.php b/src/contracts/Repository/Decorator/SettingServiceDecorator.php new file mode 100644 index 0000000000..d9cef09631 --- /dev/null +++ b/src/contracts/Repository/Decorator/SettingServiceDecorator.php @@ -0,0 +1,58 @@ +innerService = $innerService; + } + + public function loadSetting(string $group, string $identifier): Setting + { + return $this->innerService->loadSetting($group, $identifier); + } + + public function updateSetting(Setting $setting, SettingUpdateStruct $settingUpdateStruct): Setting + { + return $this->innerService->updateSetting($setting, $settingUpdateStruct); + } + + public function createSetting(SettingCreateStruct $settingCreateStruct): Setting + { + return $this->innerService->createSetting($settingCreateStruct); + } + + public function deleteSetting(Setting $setting): void + { + $this->innerService->deleteSetting($setting); + } + + public function newSettingCreateStruct(array $properties = []): SettingCreateStruct + { + return $this->innerService->newSettingCreateStruct($properties); + } + + public function newSettingUpdateStruct(array $properties = []): SettingUpdateStruct + { + return $this->innerService->newSettingUpdateStruct($properties); + } +} + +class_alias(SettingServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\SettingServiceDecorator'); diff --git a/src/contracts/Repository/Decorator/TokenServiceDecorator.php b/src/contracts/Repository/Decorator/TokenServiceDecorator.php new file mode 100644 index 0000000000..4beda23a7f --- /dev/null +++ b/src/contracts/Repository/Decorator/TokenServiceDecorator.php @@ -0,0 +1,79 @@ +innerService = $innerService; + } + + public function getToken( + string $tokenType, + string $token, + ?string $identifier = null + ): Token { + return $this->innerService->getToken( + $tokenType, + $token, + $identifier + ); + } + + public function checkToken( + string $tokenType, + string $token, + ?string $identifier = null + ): bool { + return $this->innerService->checkToken( + $tokenType, + $token, + $identifier + ); + } + + public function generateToken( + string $type, + int $ttl, + ?string $identifier = null, + int $tokenLength = 64, + ?TokenGeneratorInterface $tokenGenerator = null + ): Token { + return $this->innerService->generateToken( + $type, + $ttl, + $identifier, + $tokenLength, + $tokenGenerator + ); + } + + public function revokeToken(Token $token): void + { + $this->innerService->revokeToken($token); + } + + public function revokeTokenByIdentifier(string $tokenType, ?string $identifier): void + { + $this->innerService->revokeTokenByIdentifier($tokenType, $identifier); + } + + public function deleteToken(Token $token): void + { + $this->innerService->deleteToken($token); + } +} diff --git a/src/contracts/Repository/Decorator/TranslationServiceDecorator.php b/src/contracts/Repository/Decorator/TranslationServiceDecorator.php new file mode 100644 index 0000000000..7e1d6b99b3 --- /dev/null +++ b/src/contracts/Repository/Decorator/TranslationServiceDecorator.php @@ -0,0 +1,39 @@ +innerService = $innerService; + } + + public function translate( + Translation $translation, + $locale + ) { + return $this->innerService->translate($translation, $locale); + } + + public function translateString( + $translation, + $locale + ) { + return $this->innerService->translateString($translation, $locale); + } +} + +class_alias(TranslationServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\TranslationServiceDecorator'); diff --git a/src/contracts/Repository/Decorator/TrashServiceDecorator.php b/src/contracts/Repository/Decorator/TrashServiceDecorator.php new file mode 100644 index 0000000000..2a4a3dabe1 --- /dev/null +++ b/src/contracts/Repository/Decorator/TrashServiceDecorator.php @@ -0,0 +1,62 @@ +innerService = $innerService; + } + + public function loadTrashItem(int $trashItemId): TrashItem + { + return $this->innerService->loadTrashItem($trashItemId); + } + + public function trash(Location $location): ?TrashItem + { + return $this->innerService->trash($location); + } + + public function recover( + TrashItem $trashItem, + Location $newParentLocation = null + ): Location { + return $this->innerService->recover($trashItem, $newParentLocation); + } + + public function emptyTrash(): TrashItemDeleteResultList + { + return $this->innerService->emptyTrash(); + } + + public function deleteTrashItem(TrashItem $trashItem): TrashItemDeleteResult + { + return $this->innerService->deleteTrashItem($trashItem); + } + + public function findTrashItems(Query $query): SearchResult + { + return $this->innerService->findTrashItems($query); + } +} + +class_alias(TrashServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\TrashServiceDecorator'); diff --git a/eZ/Publish/SPI/Repository/Decorator/URLAliasServiceDecorator.php b/src/contracts/Repository/Decorator/URLAliasServiceDecorator.php similarity index 87% rename from eZ/Publish/SPI/Repository/Decorator/URLAliasServiceDecorator.php rename to src/contracts/Repository/Decorator/URLAliasServiceDecorator.php index 91a7bc67b6..df87d13f3b 100644 --- a/eZ/Publish/SPI/Repository/Decorator/URLAliasServiceDecorator.php +++ b/src/contracts/Repository/Decorator/URLAliasServiceDecorator.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Decorator; +namespace Ibexa\Contracts\Core\Repository\Decorator; -use eZ\Publish\API\Repository\URLAliasService; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\URLAlias; +use Ibexa\Contracts\Core\Repository\URLAliasService; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; abstract class URLAliasServiceDecorator implements URLAliasService { - /** @var \eZ\Publish\API\Repository\URLAliasService */ + /** @var \Ibexa\Contracts\Core\Repository\URLAliasService */ protected $innerService; public function __construct(URLAliasService $innerService) @@ -107,3 +107,5 @@ public function deleteCorruptedUrlAliases(): int return $this->innerService->deleteCorruptedUrlAliases(); } } + +class_alias(URLAliasServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\URLAliasServiceDecorator'); diff --git a/src/contracts/Repository/Decorator/URLServiceDecorator.php b/src/contracts/Repository/Decorator/URLServiceDecorator.php new file mode 100644 index 0000000000..24932e9d8c --- /dev/null +++ b/src/contracts/Repository/Decorator/URLServiceDecorator.php @@ -0,0 +1,64 @@ +innerService = $innerService; + } + + public function createUpdateStruct(): URLUpdateStruct + { + return $this->innerService->createUpdateStruct(); + } + + public function findUrls(URLQuery $query): SearchResult + { + return $this->innerService->findUrls($query); + } + + public function findUsages( + URL $url, + int $offset = 0, + int $limit = -1 + ): UsageSearchResult { + return $this->innerService->findUsages($url, $offset, $limit); + } + + public function loadById(int $id): URL + { + return $this->innerService->loadById($id); + } + + public function loadByUrl(string $url): URL + { + return $this->innerService->loadByUrl($url); + } + + public function updateUrl( + URL $url, + URLUpdateStruct $struct + ): URL { + return $this->innerService->updateUrl($url, $struct); + } +} + +class_alias(URLServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\URLServiceDecorator'); diff --git a/eZ/Publish/SPI/Repository/Decorator/URLWildcardServiceDecorator.php b/src/contracts/Repository/Decorator/URLWildcardServiceDecorator.php similarity index 77% rename from eZ/Publish/SPI/Repository/Decorator/URLWildcardServiceDecorator.php rename to src/contracts/Repository/Decorator/URLWildcardServiceDecorator.php index 912c296d0f..9edc6aa26d 100644 --- a/eZ/Publish/SPI/Repository/Decorator/URLWildcardServiceDecorator.php +++ b/src/contracts/Repository/Decorator/URLWildcardServiceDecorator.php @@ -6,18 +6,18 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Decorator; +namespace Ibexa\Contracts\Core\Repository\Decorator; -use eZ\Publish\API\Repository\URLWildcardService; -use eZ\Publish\API\Repository\Values\Content\URLWildcard; -use eZ\Publish\API\Repository\Values\Content\URLWildcardTranslationResult; -use eZ\Publish\API\Repository\Values\Content\URLWildcardUpdateStruct; +use Ibexa\Contracts\Core\Repository\URLWildcardService; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard; use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\SearchResult; use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\URLWildcardQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcardTranslationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcardUpdateStruct; abstract class URLWildcardServiceDecorator implements URLWildcardService { - /** @var \eZ\Publish\API\Repository\URLWildcardService */ + /** @var \Ibexa\Contracts\Core\Repository\URLWildcardService */ protected $innerService; public function __construct(URLWildcardService $innerService) @@ -72,3 +72,5 @@ public function countAll(): int return $this->innerService->countAll(); } } + +class_alias(URLWildcardServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\URLWildcardServiceDecorator'); diff --git a/src/contracts/Repository/Decorator/UserPreferenceServiceDecorator.php b/src/contracts/Repository/Decorator/UserPreferenceServiceDecorator.php new file mode 100644 index 0000000000..633ac544de --- /dev/null +++ b/src/contracts/Repository/Decorator/UserPreferenceServiceDecorator.php @@ -0,0 +1,48 @@ +innerService = $innerService; + } + + public function setUserPreference(array $userPreferenceSetStructs): void + { + $this->innerService->setUserPreference($userPreferenceSetStructs); + } + + public function getUserPreference(string $userPreferenceName): UserPreference + { + return $this->innerService->getUserPreference($userPreferenceName); + } + + public function loadUserPreferences( + int $offset = 0, + int $limit = 25 + ): UserPreferenceList { + return $this->innerService->loadUserPreferences($offset, $limit); + } + + public function getUserPreferenceCount(): int + { + return $this->innerService->getUserPreferenceCount(); + } +} + +class_alias(UserPreferenceServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\UserPreferenceServiceDecorator'); diff --git a/eZ/Publish/SPI/Repository/Decorator/UserServiceDecorator.php b/src/contracts/Repository/Decorator/UserServiceDecorator.php similarity index 86% rename from eZ/Publish/SPI/Repository/Decorator/UserServiceDecorator.php rename to src/contracts/Repository/Decorator/UserServiceDecorator.php index 39657f1287..784a7d669e 100644 --- a/eZ/Publish/SPI/Repository/Decorator/UserServiceDecorator.php +++ b/src/contracts/Repository/Decorator/UserServiceDecorator.php @@ -5,29 +5,29 @@ * @license For full copyright and license information view LICENSE file distributed with this source code. */ /** - * @copyright Copyright (C) eZ Systems AS. All rights reserved. + * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Decorator; - -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\User\PasswordInfo; -use eZ\Publish\API\Repository\Values\User\PasswordValidationContext; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserTokenUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserUpdateStruct; +namespace Ibexa\Contracts\Core\Repository\Decorator; + +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\User\PasswordInfo; +use Ibexa\Contracts\Core\Repository\Values\User\PasswordValidationContext; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroup; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserTokenUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserUpdateStruct; abstract class UserServiceDecorator implements UserService { - /** @var \eZ\Publish\API\Repository\UserService */ + /** @var \Ibexa\Contracts\Core\Repository\UserService */ protected $innerService; public function __construct(UserService $innerService) @@ -100,6 +100,7 @@ public function loadUser( public function checkUserCredentials( User $user, + #[\SensitiveParameter] string $credentials ): bool { return $this->innerService->checkUserCredentials($user, $credentials); @@ -245,3 +246,5 @@ public function getPasswordInfo(User $user): PasswordInfo return $this->innerService->getPasswordInfo($user); } } + +class_alias(UserServiceDecorator::class, 'eZ\Publish\SPI\Repository\Decorator\UserServiceDecorator'); diff --git a/src/contracts/Repository/Event/AfterEvent.php b/src/contracts/Repository/Event/AfterEvent.php new file mode 100644 index 0000000000..f71f471916 --- /dev/null +++ b/src/contracts/Repository/Event/AfterEvent.php @@ -0,0 +1,20 @@ +location = $location; + } + + public function getLocation(): Location + { + return $this->location; + } +} + +class_alias(BeforeCreateBookmarkEvent::class, 'eZ\Publish\API\Repository\Events\Bookmark\BeforeCreateBookmarkEvent'); diff --git a/src/contracts/Repository/Events/Bookmark/BeforeDeleteBookmarkEvent.php b/src/contracts/Repository/Events/Bookmark/BeforeDeleteBookmarkEvent.php new file mode 100644 index 0000000000..cd4aca5e05 --- /dev/null +++ b/src/contracts/Repository/Events/Bookmark/BeforeDeleteBookmarkEvent.php @@ -0,0 +1,30 @@ +location = $location; + } + + public function getLocation(): Location + { + return $this->location; + } +} + +class_alias(BeforeDeleteBookmarkEvent::class, 'eZ\Publish\API\Repository\Events\Bookmark\BeforeDeleteBookmarkEvent'); diff --git a/src/contracts/Repository/Events/Bookmark/CreateBookmarkEvent.php b/src/contracts/Repository/Events/Bookmark/CreateBookmarkEvent.php new file mode 100644 index 0000000000..6fe915c94b --- /dev/null +++ b/src/contracts/Repository/Events/Bookmark/CreateBookmarkEvent.php @@ -0,0 +1,30 @@ +location = $location; + } + + public function getLocation(): Location + { + return $this->location; + } +} + +class_alias(CreateBookmarkEvent::class, 'eZ\Publish\API\Repository\Events\Bookmark\CreateBookmarkEvent'); diff --git a/src/contracts/Repository/Events/Bookmark/DeleteBookmarkEvent.php b/src/contracts/Repository/Events/Bookmark/DeleteBookmarkEvent.php new file mode 100644 index 0000000000..38faaddda7 --- /dev/null +++ b/src/contracts/Repository/Events/Bookmark/DeleteBookmarkEvent.php @@ -0,0 +1,30 @@ +location = $location; + } + + public function getLocation(): Location + { + return $this->location; + } +} + +class_alias(DeleteBookmarkEvent::class, 'eZ\Publish\API\Repository\Events\Bookmark\DeleteBookmarkEvent'); diff --git a/src/contracts/Repository/Events/Content/AddRelationEvent.php b/src/contracts/Repository/Events/Content/AddRelationEvent.php new file mode 100644 index 0000000000..5e7776cf01 --- /dev/null +++ b/src/contracts/Repository/Events/Content/AddRelationEvent.php @@ -0,0 +1,53 @@ +relation = $relation; + $this->sourceVersion = $sourceVersion; + $this->destinationContent = $destinationContent; + } + + public function getRelation(): Relation + { + return $this->relation; + } + + public function getSourceVersion(): VersionInfo + { + return $this->sourceVersion; + } + + public function getDestinationContent(): ContentInfo + { + return $this->destinationContent; + } +} + +class_alias(AddRelationEvent::class, 'eZ\Publish\API\Repository\Events\Content\AddRelationEvent'); diff --git a/src/contracts/Repository/Events/Content/BeforeAddRelationEvent.php b/src/contracts/Repository/Events/Content/BeforeAddRelationEvent.php new file mode 100644 index 0000000000..5ff91d2f81 --- /dev/null +++ b/src/contracts/Repository/Events/Content/BeforeAddRelationEvent.php @@ -0,0 +1,64 @@ +sourceVersion = $sourceVersion; + $this->destinationContent = $destinationContent; + } + + public function getSourceVersion(): VersionInfo + { + return $this->sourceVersion; + } + + public function getDestinationContent(): ContentInfo + { + return $this->destinationContent; + } + + public function getRelation(): Relation + { + if (!$this->hasRelation()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasRelation() or set it using setRelation() before you call the getter.', Relation::class)); + } + + return $this->relation; + } + + public function setRelation(?Relation $relation): void + { + $this->relation = $relation; + } + + public function hasRelation(): bool + { + return $this->relation instanceof Relation; + } +} + +class_alias(BeforeAddRelationEvent::class, 'eZ\Publish\API\Repository\Events\Content\BeforeAddRelationEvent'); diff --git a/src/contracts/Repository/Events/Content/BeforeCopyContentEvent.php b/src/contracts/Repository/Events/Content/BeforeCopyContentEvent.php new file mode 100644 index 0000000000..70ef82e562 --- /dev/null +++ b/src/contracts/Repository/Events/Content/BeforeCopyContentEvent.php @@ -0,0 +1,77 @@ +contentInfo = $contentInfo; + $this->destinationLocationCreateStruct = $destinationLocationCreateStruct; + $this->versionInfo = $versionInfo; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getDestinationLocationCreateStruct(): LocationCreateStruct + { + return $this->destinationLocationCreateStruct; + } + + public function getVersionInfo(): ?VersionInfo + { + return $this->versionInfo; + } + + public function getContent(): Content + { + if (!$this->hasContent()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not a type of %s. Check hasContent() or set it using setContent() before you call the getter.', Content::class)); + } + + return $this->content; + } + + public function setContent(?Content $content): void + { + $this->content = $content; + } + + public function hasContent(): bool + { + return $this->content instanceof Content; + } +} + +class_alias(BeforeCopyContentEvent::class, 'eZ\Publish\API\Repository\Events\Content\BeforeCopyContentEvent'); diff --git a/src/contracts/Repository/Events/Content/BeforeCreateContentDraftEvent.php b/src/contracts/Repository/Events/Content/BeforeCreateContentDraftEvent.php new file mode 100644 index 0000000000..7e7976f627 --- /dev/null +++ b/src/contracts/Repository/Events/Content/BeforeCreateContentDraftEvent.php @@ -0,0 +1,88 @@ +contentInfo = $contentInfo; + $this->versionInfo = $versionInfo; + $this->creator = $creator; + $this->language = $language; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getVersionInfo(): ?VersionInfo + { + return $this->versionInfo; + } + + public function getCreator(): ?User + { + return $this->creator; + } + + public function getLanguage(): ?Language + { + return $this->language; + } + + public function getContentDraft(): Content + { + if (!$this->hasContentDraft()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContentDraft() or set it using setContentDraft() before you call the getter.', Content::class)); + } + + return $this->contentDraft; + } + + public function setContentDraft(?Content $contentDraft): void + { + $this->contentDraft = $contentDraft; + } + + public function hasContentDraft(): bool + { + return $this->contentDraft instanceof Content; + } +} + +class_alias(BeforeCreateContentDraftEvent::class, 'eZ\Publish\API\Repository\Events\Content\BeforeCreateContentDraftEvent'); diff --git a/eZ/Publish/API/Repository/Events/Content/BeforeCreateContentEvent.php b/src/contracts/Repository/Events/Content/BeforeCreateContentEvent.php similarity index 77% rename from eZ/Publish/API/Repository/Events/Content/BeforeCreateContentEvent.php rename to src/contracts/Repository/Events/Content/BeforeCreateContentEvent.php index 47c2db3ebd..e6d95d55ec 100644 --- a/eZ/Publish/API/Repository/Events/Content/BeforeCreateContentEvent.php +++ b/src/contracts/Repository/Events/Content/BeforeCreateContentEvent.php @@ -6,22 +6,22 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Events\Content; +namespace Ibexa\Contracts\Core\Repository\Events\Content; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\SPI\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct; use UnexpectedValueException; final class BeforeCreateContentEvent extends BeforeEvent { - /** @var \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct */ private $contentCreateStruct; /** @var array */ private $locationCreateStructs; - /** @var \eZ\Publish\API\Repository\Values\Content\Content|null */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content|null */ private $content; /** @var string[]|null */ @@ -74,3 +74,5 @@ public function hasContent(): bool return $this->content instanceof Content; } } + +class_alias(BeforeCreateContentEvent::class, 'eZ\Publish\API\Repository\Events\Content\BeforeCreateContentEvent'); diff --git a/src/contracts/Repository/Events/Content/BeforeDeleteContentEvent.php b/src/contracts/Repository/Events/Content/BeforeDeleteContentEvent.php new file mode 100644 index 0000000000..48e20156e1 --- /dev/null +++ b/src/contracts/Repository/Events/Content/BeforeDeleteContentEvent.php @@ -0,0 +1,53 @@ +contentInfo = $contentInfo; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getLocations(): array + { + if (!$this->hasLocations()) { + throw new UnexpectedValueException('If you use stopPropagation(), you must set the event return value to be an array using setLocations()'); + } + + return $this->locations; + } + + public function setLocations(?array $locations): void + { + $this->locations = $locations; + } + + public function hasLocations(): bool + { + return is_array($this->locations); + } +} + +class_alias(BeforeDeleteContentEvent::class, 'eZ\Publish\API\Repository\Events\Content\BeforeDeleteContentEvent'); diff --git a/src/contracts/Repository/Events/Content/BeforeDeleteRelationEvent.php b/src/contracts/Repository/Events/Content/BeforeDeleteRelationEvent.php new file mode 100644 index 0000000000..473e7ae0c0 --- /dev/null +++ b/src/contracts/Repository/Events/Content/BeforeDeleteRelationEvent.php @@ -0,0 +1,40 @@ +sourceVersion = $sourceVersion; + $this->destinationContent = $destinationContent; + } + + public function getSourceVersion(): VersionInfo + { + return $this->sourceVersion; + } + + public function getDestinationContent(): ContentInfo + { + return $this->destinationContent; + } +} + +class_alias(BeforeDeleteRelationEvent::class, 'eZ\Publish\API\Repository\Events\Content\BeforeDeleteRelationEvent'); diff --git a/src/contracts/Repository/Events/Content/BeforeDeleteTranslationEvent.php b/src/contracts/Repository/Events/Content/BeforeDeleteTranslationEvent.php new file mode 100644 index 0000000000..a58bcb4742 --- /dev/null +++ b/src/contracts/Repository/Events/Content/BeforeDeleteTranslationEvent.php @@ -0,0 +1,38 @@ +contentInfo = $contentInfo; + $this->languageCode = $languageCode; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getLanguageCode() + { + return $this->languageCode; + } +} + +class_alias(BeforeDeleteTranslationEvent::class, 'eZ\Publish\API\Repository\Events\Content\BeforeDeleteTranslationEvent'); diff --git a/src/contracts/Repository/Events/Content/BeforeDeleteVersionEvent.php b/src/contracts/Repository/Events/Content/BeforeDeleteVersionEvent.php new file mode 100644 index 0000000000..11a32ac01a --- /dev/null +++ b/src/contracts/Repository/Events/Content/BeforeDeleteVersionEvent.php @@ -0,0 +1,30 @@ +versionInfo = $versionInfo; + } + + public function getVersionInfo(): VersionInfo + { + return $this->versionInfo; + } +} + +class_alias(BeforeDeleteVersionEvent::class, 'eZ\Publish\API\Repository\Events\Content\BeforeDeleteVersionEvent'); diff --git a/src/contracts/Repository/Events/Content/BeforeHideContentEvent.php b/src/contracts/Repository/Events/Content/BeforeHideContentEvent.php new file mode 100644 index 0000000000..b97db556a3 --- /dev/null +++ b/src/contracts/Repository/Events/Content/BeforeHideContentEvent.php @@ -0,0 +1,30 @@ +contentInfo = $contentInfo; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } +} + +class_alias(BeforeHideContentEvent::class, 'eZ\Publish\API\Repository\Events\Content\BeforeHideContentEvent'); diff --git a/src/contracts/Repository/Events/Content/BeforePublishVersionEvent.php b/src/contracts/Repository/Events/Content/BeforePublishVersionEvent.php new file mode 100644 index 0000000000..faa2149f72 --- /dev/null +++ b/src/contracts/Repository/Events/Content/BeforePublishVersionEvent.php @@ -0,0 +1,63 @@ +versionInfo = $versionInfo; + $this->translations = $translations; + } + + public function getVersionInfo(): VersionInfo + { + return $this->versionInfo; + } + + public function getTranslations(): array + { + return $this->translations; + } + + public function getContent(): Content + { + if (!$this->hasContent()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContent() or set it using setContent() before you call the getter.', Content::class)); + } + + return $this->content; + } + + public function setContent(?Content $content): void + { + $this->content = $content; + } + + public function hasContent(): bool + { + return $this->content instanceof Content; + } +} + +class_alias(BeforePublishVersionEvent::class, 'eZ\Publish\API\Repository\Events\Content\BeforePublishVersionEvent'); diff --git a/src/contracts/Repository/Events/Content/BeforeRevealContentEvent.php b/src/contracts/Repository/Events/Content/BeforeRevealContentEvent.php new file mode 100644 index 0000000000..0d188c34a1 --- /dev/null +++ b/src/contracts/Repository/Events/Content/BeforeRevealContentEvent.php @@ -0,0 +1,30 @@ +contentInfo = $contentInfo; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } +} + +class_alias(BeforeRevealContentEvent::class, 'eZ\Publish\API\Repository\Events\Content\BeforeRevealContentEvent'); diff --git a/src/contracts/Repository/Events/Content/BeforeUpdateContentEvent.php b/src/contracts/Repository/Events/Content/BeforeUpdateContentEvent.php new file mode 100644 index 0000000000..df52d36216 --- /dev/null +++ b/src/contracts/Repository/Events/Content/BeforeUpdateContentEvent.php @@ -0,0 +1,79 @@ +versionInfo = $versionInfo; + $this->contentUpdateStruct = $contentUpdateStruct; + $this->fieldIdentifiersToValidate = $fieldIdentifiersToValidate; + } + + public function getVersionInfo(): VersionInfo + { + return $this->versionInfo; + } + + public function getContentUpdateStruct(): ContentUpdateStruct + { + return $this->contentUpdateStruct; + } + + /** + * @return string[]|null + */ + public function getFieldIdentifiersToValidate(): ?array + { + return $this->fieldIdentifiersToValidate; + } + + public function getContent(): Content + { + if (!$this->hasContent()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContent() or set it using setContent() before you call the getter.', Content::class)); + } + + return $this->content; + } + + public function setContent(?Content $content): void + { + $this->content = $content; + } + + public function hasContent(): bool + { + return $this->content instanceof Content; + } +} + +class_alias(BeforeUpdateContentEvent::class, 'eZ\Publish\API\Repository\Events\Content\BeforeUpdateContentEvent'); diff --git a/src/contracts/Repository/Events/Content/BeforeUpdateContentMetadataEvent.php b/src/contracts/Repository/Events/Content/BeforeUpdateContentMetadataEvent.php new file mode 100644 index 0000000000..a36fad381d --- /dev/null +++ b/src/contracts/Repository/Events/Content/BeforeUpdateContentMetadataEvent.php @@ -0,0 +1,64 @@ +contentInfo = $contentInfo; + $this->contentMetadataUpdateStruct = $contentMetadataUpdateStruct; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getContentMetadataUpdateStruct(): ContentMetadataUpdateStruct + { + return $this->contentMetadataUpdateStruct; + } + + public function getContent(): Content + { + if (!$this->hasContent()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContent() or set it using setContent() before you call the getter.', Content::class)); + } + + return $this->content; + } + + public function setContent(?Content $content): void + { + $this->content = $content; + } + + public function hasContent(): bool + { + return $this->content instanceof Content; + } +} + +class_alias(BeforeUpdateContentMetadataEvent::class, 'eZ\Publish\API\Repository\Events\Content\BeforeUpdateContentMetadataEvent'); diff --git a/src/contracts/Repository/Events/Content/CopyContentEvent.php b/src/contracts/Repository/Events/Content/CopyContentEvent.php new file mode 100644 index 0000000000..45488792ff --- /dev/null +++ b/src/contracts/Repository/Events/Content/CopyContentEvent.php @@ -0,0 +1,64 @@ +content = $content; + $this->contentInfo = $contentInfo; + $this->destinationLocationCreateStruct = $destinationLocationCreateStruct; + $this->versionInfo = $versionInfo; + } + + public function getContent(): Content + { + return $this->content; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getDestinationLocationCreateStruct(): LocationCreateStruct + { + return $this->destinationLocationCreateStruct; + } + + public function getVersionInfo(): ?VersionInfo + { + return $this->versionInfo; + } +} + +class_alias(CopyContentEvent::class, 'eZ\Publish\API\Repository\Events\Content\CopyContentEvent'); diff --git a/src/contracts/Repository/Events/Content/CreateContentDraftEvent.php b/src/contracts/Repository/Events/Content/CreateContentDraftEvent.php new file mode 100644 index 0000000000..4474ef9e82 --- /dev/null +++ b/src/contracts/Repository/Events/Content/CreateContentDraftEvent.php @@ -0,0 +1,75 @@ +contentDraft = $contentDraft; + $this->contentInfo = $contentInfo; + $this->versionInfo = $versionInfo; + $this->creator = $creator; + $this->language = $language; + } + + public function getContentDraft(): Content + { + return $this->contentDraft; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getVersionInfo(): ?VersionInfo + { + return $this->versionInfo; + } + + public function getCreator(): ?User + { + return $this->creator; + } + + public function getLanguage(): ?Language + { + return $this->language; + } +} + +class_alias(CreateContentDraftEvent::class, 'eZ\Publish\API\Repository\Events\Content\CreateContentDraftEvent'); diff --git a/src/contracts/Repository/Events/Content/CreateContentEvent.php b/src/contracts/Repository/Events/Content/CreateContentEvent.php new file mode 100644 index 0000000000..43f5899265 --- /dev/null +++ b/src/contracts/Repository/Events/Content/CreateContentEvent.php @@ -0,0 +1,65 @@ +content = $content; + $this->contentCreateStruct = $contentCreateStruct; + $this->locationCreateStructs = $locationCreateStructs; + $this->fieldIdentifiersToValidate = $fieldIdentifiersToValidate; + } + + public function getContentCreateStruct(): ContentCreateStruct + { + return $this->contentCreateStruct; + } + + public function getLocationCreateStructs(): array + { + return $this->locationCreateStructs; + } + + public function getContent(): Content + { + return $this->content; + } + + /** + * @return string[]|null + */ + public function getFieldIdentifiersToValidate(): ?array + { + return $this->fieldIdentifiersToValidate; + } +} + +class_alias(CreateContentEvent::class, 'eZ\Publish\API\Repository\Events\Content\CreateContentEvent'); diff --git a/src/contracts/Repository/Events/Content/DeleteContentEvent.php b/src/contracts/Repository/Events/Content/DeleteContentEvent.php new file mode 100644 index 0000000000..4f4f703ab4 --- /dev/null +++ b/src/contracts/Repository/Events/Content/DeleteContentEvent.php @@ -0,0 +1,41 @@ +locations = $locations; + $this->contentInfo = $contentInfo; + } + + public function getLocations(): array + { + return $this->locations; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } +} + +class_alias(DeleteContentEvent::class, 'eZ\Publish\API\Repository\Events\Content\DeleteContentEvent'); diff --git a/src/contracts/Repository/Events/Content/DeleteRelationEvent.php b/src/contracts/Repository/Events/Content/DeleteRelationEvent.php new file mode 100644 index 0000000000..38f8379629 --- /dev/null +++ b/src/contracts/Repository/Events/Content/DeleteRelationEvent.php @@ -0,0 +1,42 @@ +sourceVersion = $sourceVersion; + $this->destinationContent = $destinationContent; + } + + public function getSourceVersion(): VersionInfo + { + return $this->sourceVersion; + } + + public function getDestinationContent(): ContentInfo + { + return $this->destinationContent; + } +} + +class_alias(DeleteRelationEvent::class, 'eZ\Publish\API\Repository\Events\Content\DeleteRelationEvent'); diff --git a/src/contracts/Repository/Events/Content/DeleteTranslationEvent.php b/src/contracts/Repository/Events/Content/DeleteTranslationEvent.php new file mode 100644 index 0000000000..6c494523eb --- /dev/null +++ b/src/contracts/Repository/Events/Content/DeleteTranslationEvent.php @@ -0,0 +1,40 @@ +contentInfo = $contentInfo; + $this->languageCode = $languageCode; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getLanguageCode() + { + return $this->languageCode; + } +} + +class_alias(DeleteTranslationEvent::class, 'eZ\Publish\API\Repository\Events\Content\DeleteTranslationEvent'); diff --git a/src/contracts/Repository/Events/Content/DeleteVersionEvent.php b/src/contracts/Repository/Events/Content/DeleteVersionEvent.php new file mode 100644 index 0000000000..b4eebac6ff --- /dev/null +++ b/src/contracts/Repository/Events/Content/DeleteVersionEvent.php @@ -0,0 +1,30 @@ +versionInfo = $versionInfo; + } + + public function getVersionInfo(): VersionInfo + { + return $this->versionInfo; + } +} + +class_alias(DeleteVersionEvent::class, 'eZ\Publish\API\Repository\Events\Content\DeleteVersionEvent'); diff --git a/src/contracts/Repository/Events/Content/HideContentEvent.php b/src/contracts/Repository/Events/Content/HideContentEvent.php new file mode 100644 index 0000000000..a35dbd814c --- /dev/null +++ b/src/contracts/Repository/Events/Content/HideContentEvent.php @@ -0,0 +1,30 @@ +contentInfo = $contentInfo; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } +} + +class_alias(HideContentEvent::class, 'eZ\Publish\API\Repository\Events\Content\HideContentEvent'); diff --git a/src/contracts/Repository/Events/Content/PublishVersionEvent.php b/src/contracts/Repository/Events/Content/PublishVersionEvent.php new file mode 100644 index 0000000000..7a826a6d97 --- /dev/null +++ b/src/contracts/Repository/Events/Content/PublishVersionEvent.php @@ -0,0 +1,52 @@ +content = $content; + $this->versionInfo = $versionInfo; + $this->translations = $translations; + } + + public function getContent(): Content + { + return $this->content; + } + + public function getVersionInfo(): VersionInfo + { + return $this->versionInfo; + } + + public function getTranslations(): array + { + return $this->translations; + } +} + +class_alias(PublishVersionEvent::class, 'eZ\Publish\API\Repository\Events\Content\PublishVersionEvent'); diff --git a/src/contracts/Repository/Events/Content/RevealContentEvent.php b/src/contracts/Repository/Events/Content/RevealContentEvent.php new file mode 100644 index 0000000000..6cf9e2ce6a --- /dev/null +++ b/src/contracts/Repository/Events/Content/RevealContentEvent.php @@ -0,0 +1,30 @@ +contentInfo = $contentInfo; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } +} + +class_alias(RevealContentEvent::class, 'eZ\Publish\API\Repository\Events\Content\RevealContentEvent'); diff --git a/src/contracts/Repository/Events/Content/UpdateContentEvent.php b/src/contracts/Repository/Events/Content/UpdateContentEvent.php new file mode 100644 index 0000000000..f42860ed82 --- /dev/null +++ b/src/contracts/Repository/Events/Content/UpdateContentEvent.php @@ -0,0 +1,66 @@ +content = $content; + $this->versionInfo = $versionInfo; + $this->contentUpdateStruct = $contentUpdateStruct; + $this->fieldIdentifiersToValidate = $fieldIdentifiersToValidate; + } + + public function getContent(): Content + { + return $this->content; + } + + public function getVersionInfo(): VersionInfo + { + return $this->versionInfo; + } + + public function getContentUpdateStruct(): ContentUpdateStruct + { + return $this->contentUpdateStruct; + } + + /** + * @return string[]|null + */ + public function getFieldIdentifiersToValidate(): ?array + { + return $this->fieldIdentifiersToValidate; + } +} + +class_alias(UpdateContentEvent::class, 'eZ\Publish\API\Repository\Events\Content\UpdateContentEvent'); diff --git a/src/contracts/Repository/Events/Content/UpdateContentMetadataEvent.php b/src/contracts/Repository/Events/Content/UpdateContentMetadataEvent.php new file mode 100644 index 0000000000..356787dcd8 --- /dev/null +++ b/src/contracts/Repository/Events/Content/UpdateContentMetadataEvent.php @@ -0,0 +1,53 @@ +content = $content; + $this->contentInfo = $contentInfo; + $this->contentMetadataUpdateStruct = $contentMetadataUpdateStruct; + } + + public function getContent(): Content + { + return $this->content; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getContentMetadataUpdateStruct(): ContentMetadataUpdateStruct + { + return $this->contentMetadataUpdateStruct; + } +} + +class_alias(UpdateContentMetadataEvent::class, 'eZ\Publish\API\Repository\Events\Content\UpdateContentMetadataEvent'); diff --git a/src/contracts/Repository/Events/ContentType/AddFieldDefinitionEvent.php b/src/contracts/Repository/Events/ContentType/AddFieldDefinitionEvent.php new file mode 100644 index 0000000000..1660554ffb --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/AddFieldDefinitionEvent.php @@ -0,0 +1,42 @@ +contentTypeDraft = $contentTypeDraft; + $this->fieldDefinitionCreateStruct = $fieldDefinitionCreateStruct; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } + + public function getFieldDefinitionCreateStruct(): FieldDefinitionCreateStruct + { + return $this->fieldDefinitionCreateStruct; + } +} + +class_alias(AddFieldDefinitionEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\AddFieldDefinitionEvent'); diff --git a/src/contracts/Repository/Events/ContentType/AssignContentTypeGroupEvent.php b/src/contracts/Repository/Events/ContentType/AssignContentTypeGroupEvent.php new file mode 100644 index 0000000000..f677e8fed3 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/AssignContentTypeGroupEvent.php @@ -0,0 +1,42 @@ +contentType = $contentType; + $this->contentTypeGroup = $contentTypeGroup; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } + + public function getContentTypeGroup(): ContentTypeGroup + { + return $this->contentTypeGroup; + } +} + +class_alias(AssignContentTypeGroupEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\AssignContentTypeGroupEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeAddFieldDefinitionEvent.php b/src/contracts/Repository/Events/ContentType/BeforeAddFieldDefinitionEvent.php new file mode 100644 index 0000000000..a51f96eedd --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeAddFieldDefinitionEvent.php @@ -0,0 +1,40 @@ +contentTypeDraft = $contentTypeDraft; + $this->fieldDefinitionCreateStruct = $fieldDefinitionCreateStruct; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } + + public function getFieldDefinitionCreateStruct(): FieldDefinitionCreateStruct + { + return $this->fieldDefinitionCreateStruct; + } +} + +class_alias(BeforeAddFieldDefinitionEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeAddFieldDefinitionEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeAssignContentTypeGroupEvent.php b/src/contracts/Repository/Events/ContentType/BeforeAssignContentTypeGroupEvent.php new file mode 100644 index 0000000000..5fc0e5027a --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeAssignContentTypeGroupEvent.php @@ -0,0 +1,40 @@ +contentType = $contentType; + $this->contentTypeGroup = $contentTypeGroup; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } + + public function getContentTypeGroup(): ContentTypeGroup + { + return $this->contentTypeGroup; + } +} + +class_alias(BeforeAssignContentTypeGroupEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeAssignContentTypeGroupEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeCopyContentTypeEvent.php b/src/contracts/Repository/Events/ContentType/BeforeCopyContentTypeEvent.php new file mode 100644 index 0000000000..279fc193b9 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeCopyContentTypeEvent.php @@ -0,0 +1,63 @@ +contentType = $contentType; + $this->creator = $creator; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } + + public function getCreator(): ?User + { + return $this->creator; + } + + public function getContentTypeCopy(): ContentType + { + if (!$this->hasContentTypeCopy()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContentTypeCopy() or set it using setContentTypeCopy() before you call the getter.', ContentType::class)); + } + + return $this->contentTypeCopy; + } + + public function setContentTypeCopy(?ContentType $contentTypeCopy): void + { + $this->contentTypeCopy = $contentTypeCopy; + } + + public function hasContentTypeCopy(): bool + { + return $this->contentTypeCopy instanceof ContentType; + } +} + +class_alias(BeforeCopyContentTypeEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeCopyContentTypeEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeDraftEvent.php b/src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeDraftEvent.php new file mode 100644 index 0000000000..b89c178196 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeDraftEvent.php @@ -0,0 +1,54 @@ +contentType = $contentType; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + if (!$this->hasContentTypeDraft()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContentTypeDraft() or set it using setContentTypeDraft() before you call the getter.', ContentTypeDraft::class)); + } + + return $this->contentTypeDraft; + } + + public function setContentTypeDraft(?ContentTypeDraft $contentTypeDraft): void + { + $this->contentTypeDraft = $contentTypeDraft; + } + + public function hasContentTypeDraft(): bool + { + return $this->contentTypeDraft instanceof ContentTypeDraft; + } +} + +class_alias(BeforeCreateContentTypeDraftEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeCreateContentTypeDraftEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeEvent.php b/src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeEvent.php new file mode 100644 index 0000000000..c6ddf7daf8 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeEvent.php @@ -0,0 +1,63 @@ +contentTypeCreateStruct = $contentTypeCreateStruct; + $this->contentTypeGroups = $contentTypeGroups; + } + + public function getContentTypeCreateStruct(): ContentTypeCreateStruct + { + return $this->contentTypeCreateStruct; + } + + public function getContentTypeGroups(): array + { + return $this->contentTypeGroups; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + if (!$this->hasContentTypeDraft()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContentTypeDraft() or set it using setContentTypeDraft() before you call the getter.', ContentTypeDraft::class)); + } + + return $this->contentTypeDraft; + } + + public function setContentTypeDraft(?ContentTypeDraft $contentTypeDraft): void + { + $this->contentTypeDraft = $contentTypeDraft; + } + + public function hasContentTypeDraft(): bool + { + return $this->contentTypeDraft instanceof ContentTypeDraft; + } +} + +class_alias(BeforeCreateContentTypeEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeCreateContentTypeEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeGroupEvent.php b/src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeGroupEvent.php new file mode 100644 index 0000000000..a7acbba343 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeCreateContentTypeGroupEvent.php @@ -0,0 +1,54 @@ +contentTypeGroupCreateStruct = $contentTypeGroupCreateStruct; + } + + public function getContentTypeGroupCreateStruct(): ContentTypeGroupCreateStruct + { + return $this->contentTypeGroupCreateStruct; + } + + public function getContentTypeGroup(): ContentTypeGroup + { + if (!$this->hasContentTypeGroup()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasContentTypeGroup() or set it using setContentTypeGroup() before you call the getter.', ContentTypeGroup::class)); + } + + return $this->contentTypeGroup; + } + + public function setContentTypeGroup(?ContentTypeGroup $contentTypeGroup): void + { + $this->contentTypeGroup = $contentTypeGroup; + } + + public function hasContentTypeGroup(): bool + { + return $this->contentTypeGroup instanceof ContentTypeGroup; + } +} + +class_alias(BeforeCreateContentTypeGroupEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeCreateContentTypeGroupEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeDeleteContentTypeEvent.php b/src/contracts/Repository/Events/ContentType/BeforeDeleteContentTypeEvent.php new file mode 100644 index 0000000000..0964d0dffe --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeDeleteContentTypeEvent.php @@ -0,0 +1,30 @@ +contentType = $contentType; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } +} + +class_alias(BeforeDeleteContentTypeEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeDeleteContentTypeEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeDeleteContentTypeGroupEvent.php b/src/contracts/Repository/Events/ContentType/BeforeDeleteContentTypeGroupEvent.php new file mode 100644 index 0000000000..9ec79e77be --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeDeleteContentTypeGroupEvent.php @@ -0,0 +1,30 @@ +contentTypeGroup = $contentTypeGroup; + } + + public function getContentTypeGroup(): ContentTypeGroup + { + return $this->contentTypeGroup; + } +} + +class_alias(BeforeDeleteContentTypeGroupEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeDeleteContentTypeGroupEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforePublishContentTypeDraftEvent.php b/src/contracts/Repository/Events/ContentType/BeforePublishContentTypeDraftEvent.php new file mode 100644 index 0000000000..5d63755db7 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforePublishContentTypeDraftEvent.php @@ -0,0 +1,30 @@ +contentTypeDraft = $contentTypeDraft; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } +} + +class_alias(BeforePublishContentTypeDraftEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforePublishContentTypeDraftEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeRemoveContentTypeTranslationEvent.php b/src/contracts/Repository/Events/ContentType/BeforeRemoveContentTypeTranslationEvent.php new file mode 100644 index 0000000000..f7c115219b --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeRemoveContentTypeTranslationEvent.php @@ -0,0 +1,62 @@ +contentTypeDraft = $contentTypeDraft; + $this->languageCode = $languageCode; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } + + public function getLanguageCode(): string + { + return $this->languageCode; + } + + public function getNewContentTypeDraft(): ContentTypeDraft + { + if (!$this->hasNewContentTypeDraft()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasNewContentTypeDraft() or set it using setNewContentTypeDraft() before you call the getter.', ContentTypeDraft::class)); + } + + return $this->newContentTypeDraft; + } + + public function setNewContentTypeDraft(?ContentTypeDraft $newContentTypeDraft): void + { + $this->newContentTypeDraft = $newContentTypeDraft; + } + + public function hasNewContentTypeDraft(): bool + { + return $this->newContentTypeDraft instanceof ContentTypeDraft; + } +} + +class_alias(BeforeRemoveContentTypeTranslationEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeRemoveContentTypeTranslationEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeRemoveFieldDefinitionEvent.php b/src/contracts/Repository/Events/ContentType/BeforeRemoveFieldDefinitionEvent.php new file mode 100644 index 0000000000..e5321118f0 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeRemoveFieldDefinitionEvent.php @@ -0,0 +1,40 @@ +contentTypeDraft = $contentTypeDraft; + $this->fieldDefinition = $fieldDefinition; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } + + public function getFieldDefinition(): FieldDefinition + { + return $this->fieldDefinition; + } +} + +class_alias(BeforeRemoveFieldDefinitionEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeRemoveFieldDefinitionEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeUnassignContentTypeGroupEvent.php b/src/contracts/Repository/Events/ContentType/BeforeUnassignContentTypeGroupEvent.php new file mode 100644 index 0000000000..2afad1f525 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeUnassignContentTypeGroupEvent.php @@ -0,0 +1,40 @@ +contentType = $contentType; + $this->contentTypeGroup = $contentTypeGroup; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } + + public function getContentTypeGroup(): ContentTypeGroup + { + return $this->contentTypeGroup; + } +} + +class_alias(BeforeUnassignContentTypeGroupEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeUnassignContentTypeGroupEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeUpdateContentTypeDraftEvent.php b/src/contracts/Repository/Events/ContentType/BeforeUpdateContentTypeDraftEvent.php new file mode 100644 index 0000000000..5bd9b37e2d --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeUpdateContentTypeDraftEvent.php @@ -0,0 +1,40 @@ +contentTypeDraft = $contentTypeDraft; + $this->contentTypeUpdateStruct = $contentTypeUpdateStruct; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } + + public function getContentTypeUpdateStruct(): ContentTypeUpdateStruct + { + return $this->contentTypeUpdateStruct; + } +} + +class_alias(BeforeUpdateContentTypeDraftEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeUpdateContentTypeDraftEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeUpdateContentTypeGroupEvent.php b/src/contracts/Repository/Events/ContentType/BeforeUpdateContentTypeGroupEvent.php new file mode 100644 index 0000000000..07d42b213f --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeUpdateContentTypeGroupEvent.php @@ -0,0 +1,40 @@ +contentTypeGroup = $contentTypeGroup; + $this->contentTypeGroupUpdateStruct = $contentTypeGroupUpdateStruct; + } + + public function getContentTypeGroup(): ContentTypeGroup + { + return $this->contentTypeGroup; + } + + public function getContentTypeGroupUpdateStruct(): ContentTypeGroupUpdateStruct + { + return $this->contentTypeGroupUpdateStruct; + } +} + +class_alias(BeforeUpdateContentTypeGroupEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeUpdateContentTypeGroupEvent'); diff --git a/src/contracts/Repository/Events/ContentType/BeforeUpdateFieldDefinitionEvent.php b/src/contracts/Repository/Events/ContentType/BeforeUpdateFieldDefinitionEvent.php new file mode 100644 index 0000000000..4c9b0790c2 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/BeforeUpdateFieldDefinitionEvent.php @@ -0,0 +1,53 @@ +contentTypeDraft = $contentTypeDraft; + $this->fieldDefinition = $fieldDefinition; + $this->fieldDefinitionUpdateStruct = $fieldDefinitionUpdateStruct; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } + + public function getFieldDefinition(): FieldDefinition + { + return $this->fieldDefinition; + } + + public function getFieldDefinitionUpdateStruct(): FieldDefinitionUpdateStruct + { + return $this->fieldDefinitionUpdateStruct; + } +} + +class_alias(BeforeUpdateFieldDefinitionEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\BeforeUpdateFieldDefinitionEvent'); diff --git a/src/contracts/Repository/Events/ContentType/CopyContentTypeEvent.php b/src/contracts/Repository/Events/ContentType/CopyContentTypeEvent.php new file mode 100644 index 0000000000..79e1563112 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/CopyContentTypeEvent.php @@ -0,0 +1,52 @@ +contentTypeCopy = $contentTypeCopy; + $this->contentType = $contentType; + $this->creator = $creator; + } + + public function getContentTypeCopy(): ContentType + { + return $this->contentTypeCopy; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } + + public function getCreator(): ?User + { + return $this->creator; + } +} + +class_alias(CopyContentTypeEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\CopyContentTypeEvent'); diff --git a/src/contracts/Repository/Events/ContentType/CreateContentTypeDraftEvent.php b/src/contracts/Repository/Events/ContentType/CreateContentTypeDraftEvent.php new file mode 100644 index 0000000000..620e33e243 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/CreateContentTypeDraftEvent.php @@ -0,0 +1,42 @@ +contentTypeDraft = $contentTypeDraft; + $this->contentType = $contentType; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } +} + +class_alias(CreateContentTypeDraftEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\CreateContentTypeDraftEvent'); diff --git a/src/contracts/Repository/Events/ContentType/CreateContentTypeEvent.php b/src/contracts/Repository/Events/ContentType/CreateContentTypeEvent.php new file mode 100644 index 0000000000..7c17106fc4 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/CreateContentTypeEvent.php @@ -0,0 +1,52 @@ +contentTypeDraft = $contentTypeDraft; + $this->contentTypeCreateStruct = $contentTypeCreateStruct; + $this->contentTypeGroups = $contentTypeGroups; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } + + public function getContentTypeCreateStruct(): ContentTypeCreateStruct + { + return $this->contentTypeCreateStruct; + } + + public function getContentTypeGroups(): array + { + return $this->contentTypeGroups; + } +} + +class_alias(CreateContentTypeEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\CreateContentTypeEvent'); diff --git a/src/contracts/Repository/Events/ContentType/CreateContentTypeGroupEvent.php b/src/contracts/Repository/Events/ContentType/CreateContentTypeGroupEvent.php new file mode 100644 index 0000000000..e42bd6e672 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/CreateContentTypeGroupEvent.php @@ -0,0 +1,42 @@ +contentTypeGroup = $contentTypeGroup; + $this->contentTypeGroupCreateStruct = $contentTypeGroupCreateStruct; + } + + public function getReturnValue(): ContentTypeGroup + { + return $this->contentTypeGroup; + } + + public function getContentTypeGroupCreateStruct(): ContentTypeGroupCreateStruct + { + return $this->contentTypeGroupCreateStruct; + } +} + +class_alias(CreateContentTypeGroupEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\CreateContentTypeGroupEvent'); diff --git a/src/contracts/Repository/Events/ContentType/DeleteContentTypeEvent.php b/src/contracts/Repository/Events/ContentType/DeleteContentTypeEvent.php new file mode 100644 index 0000000000..5b52a48780 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/DeleteContentTypeEvent.php @@ -0,0 +1,30 @@ +contentType = $contentType; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } +} + +class_alias(DeleteContentTypeEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\DeleteContentTypeEvent'); diff --git a/src/contracts/Repository/Events/ContentType/DeleteContentTypeGroupEvent.php b/src/contracts/Repository/Events/ContentType/DeleteContentTypeGroupEvent.php new file mode 100644 index 0000000000..3e3e7b6752 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/DeleteContentTypeGroupEvent.php @@ -0,0 +1,30 @@ +contentTypeGroup = $contentTypeGroup; + } + + public function getContentTypeGroup(): ContentTypeGroup + { + return $this->contentTypeGroup; + } +} + +class_alias(DeleteContentTypeGroupEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\DeleteContentTypeGroupEvent'); diff --git a/src/contracts/Repository/Events/ContentType/PublishContentTypeDraftEvent.php b/src/contracts/Repository/Events/ContentType/PublishContentTypeDraftEvent.php new file mode 100644 index 0000000000..eadc6aef42 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/PublishContentTypeDraftEvent.php @@ -0,0 +1,30 @@ +contentTypeDraft = $contentTypeDraft; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } +} + +class_alias(PublishContentTypeDraftEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\PublishContentTypeDraftEvent'); diff --git a/src/contracts/Repository/Events/ContentType/RemoveContentTypeTranslationEvent.php b/src/contracts/Repository/Events/ContentType/RemoveContentTypeTranslationEvent.php new file mode 100644 index 0000000000..0081a4ccfb --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/RemoveContentTypeTranslationEvent.php @@ -0,0 +1,51 @@ +newContentTypeDraft = $newContentTypeDraft; + $this->contentTypeDraft = $contentTypeDraft; + $this->languageCode = $languageCode; + } + + public function getNewContentTypeDraft(): ContentTypeDraft + { + return $this->newContentTypeDraft; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } + + public function getLanguageCode(): string + { + return $this->languageCode; + } +} + +class_alias(RemoveContentTypeTranslationEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\RemoveContentTypeTranslationEvent'); diff --git a/src/contracts/Repository/Events/ContentType/RemoveFieldDefinitionEvent.php b/src/contracts/Repository/Events/ContentType/RemoveFieldDefinitionEvent.php new file mode 100644 index 0000000000..ee1e78de4e --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/RemoveFieldDefinitionEvent.php @@ -0,0 +1,42 @@ +contentTypeDraft = $contentTypeDraft; + $this->fieldDefinition = $fieldDefinition; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } + + public function getFieldDefinition(): FieldDefinition + { + return $this->fieldDefinition; + } +} + +class_alias(RemoveFieldDefinitionEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\RemoveFieldDefinitionEvent'); diff --git a/src/contracts/Repository/Events/ContentType/UnassignContentTypeGroupEvent.php b/src/contracts/Repository/Events/ContentType/UnassignContentTypeGroupEvent.php new file mode 100644 index 0000000000..5f8b2561a7 --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/UnassignContentTypeGroupEvent.php @@ -0,0 +1,42 @@ +contentType = $contentType; + $this->contentTypeGroup = $contentTypeGroup; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } + + public function getContentTypeGroup(): ContentTypeGroup + { + return $this->contentTypeGroup; + } +} + +class_alias(UnassignContentTypeGroupEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\UnassignContentTypeGroupEvent'); diff --git a/src/contracts/Repository/Events/ContentType/UpdateContentTypeDraftEvent.php b/src/contracts/Repository/Events/ContentType/UpdateContentTypeDraftEvent.php new file mode 100644 index 0000000000..91f1993e9e --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/UpdateContentTypeDraftEvent.php @@ -0,0 +1,42 @@ +contentTypeDraft = $contentTypeDraft; + $this->contentTypeUpdateStruct = $contentTypeUpdateStruct; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } + + public function getContentTypeUpdateStruct(): ContentTypeUpdateStruct + { + return $this->contentTypeUpdateStruct; + } +} + +class_alias(UpdateContentTypeDraftEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\UpdateContentTypeDraftEvent'); diff --git a/src/contracts/Repository/Events/ContentType/UpdateContentTypeGroupEvent.php b/src/contracts/Repository/Events/ContentType/UpdateContentTypeGroupEvent.php new file mode 100644 index 0000000000..b42b5eebcf --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/UpdateContentTypeGroupEvent.php @@ -0,0 +1,42 @@ +contentTypeGroup = $contentTypeGroup; + $this->contentTypeGroupUpdateStruct = $contentTypeGroupUpdateStruct; + } + + public function getContentTypeGroup(): ContentTypeGroup + { + return $this->contentTypeGroup; + } + + public function getContentTypeGroupUpdateStruct(): ContentTypeGroupUpdateStruct + { + return $this->contentTypeGroupUpdateStruct; + } +} + +class_alias(UpdateContentTypeGroupEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\UpdateContentTypeGroupEvent'); diff --git a/src/contracts/Repository/Events/ContentType/UpdateFieldDefinitionEvent.php b/src/contracts/Repository/Events/ContentType/UpdateFieldDefinitionEvent.php new file mode 100644 index 0000000000..4f89a944ee --- /dev/null +++ b/src/contracts/Repository/Events/ContentType/UpdateFieldDefinitionEvent.php @@ -0,0 +1,53 @@ +contentTypeDraft = $contentTypeDraft; + $this->fieldDefinition = $fieldDefinition; + $this->fieldDefinitionUpdateStruct = $fieldDefinitionUpdateStruct; + } + + public function getContentTypeDraft(): ContentTypeDraft + { + return $this->contentTypeDraft; + } + + public function getFieldDefinition(): FieldDefinition + { + return $this->fieldDefinition; + } + + public function getFieldDefinitionUpdateStruct(): FieldDefinitionUpdateStruct + { + return $this->fieldDefinitionUpdateStruct; + } +} + +class_alias(UpdateFieldDefinitionEvent::class, 'eZ\Publish\API\Repository\Events\ContentType\UpdateFieldDefinitionEvent'); diff --git a/src/contracts/Repository/Events/Language/BeforeCreateLanguageEvent.php b/src/contracts/Repository/Events/Language/BeforeCreateLanguageEvent.php new file mode 100644 index 0000000000..2ef254fe9e --- /dev/null +++ b/src/contracts/Repository/Events/Language/BeforeCreateLanguageEvent.php @@ -0,0 +1,54 @@ +languageCreateStruct = $languageCreateStruct; + } + + public function getLanguageCreateStruct(): LanguageCreateStruct + { + return $this->languageCreateStruct; + } + + public function getLanguage(): Language + { + if (!$this->hasLanguage()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasLanguage() or set it using setLanguage() before you call the getter.', Language::class)); + } + + return $this->language; + } + + public function setLanguage(?Language $language): void + { + $this->language = $language; + } + + public function hasLanguage(): bool + { + return $this->language instanceof Language; + } +} + +class_alias(BeforeCreateLanguageEvent::class, 'eZ\Publish\API\Repository\Events\Language\BeforeCreateLanguageEvent'); diff --git a/src/contracts/Repository/Events/Language/BeforeDeleteLanguageEvent.php b/src/contracts/Repository/Events/Language/BeforeDeleteLanguageEvent.php new file mode 100644 index 0000000000..10e75759a5 --- /dev/null +++ b/src/contracts/Repository/Events/Language/BeforeDeleteLanguageEvent.php @@ -0,0 +1,30 @@ +language = $language; + } + + public function getLanguage(): Language + { + return $this->language; + } +} + +class_alias(BeforeDeleteLanguageEvent::class, 'eZ\Publish\API\Repository\Events\Language\BeforeDeleteLanguageEvent'); diff --git a/src/contracts/Repository/Events/Language/BeforeDisableLanguageEvent.php b/src/contracts/Repository/Events/Language/BeforeDisableLanguageEvent.php new file mode 100644 index 0000000000..8c514a9cf4 --- /dev/null +++ b/src/contracts/Repository/Events/Language/BeforeDisableLanguageEvent.php @@ -0,0 +1,53 @@ +language = $language; + } + + public function getLanguage(): Language + { + return $this->language; + } + + public function getDisabledLanguage(): Language + { + if (!$this->hasDisabledLanguage()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasDisabledLanguage() or set it using setDisabledLanguage() before you call the getter.', Language::class)); + } + + return $this->disabledLanguage; + } + + public function setDisabledLanguage(?Language $disabledLanguage): void + { + $this->disabledLanguage = $disabledLanguage; + } + + public function hasDisabledLanguage(): bool + { + return $this->disabledLanguage instanceof Language; + } +} + +class_alias(BeforeDisableLanguageEvent::class, 'eZ\Publish\API\Repository\Events\Language\BeforeDisableLanguageEvent'); diff --git a/src/contracts/Repository/Events/Language/BeforeEnableLanguageEvent.php b/src/contracts/Repository/Events/Language/BeforeEnableLanguageEvent.php new file mode 100644 index 0000000000..007de39ec1 --- /dev/null +++ b/src/contracts/Repository/Events/Language/BeforeEnableLanguageEvent.php @@ -0,0 +1,53 @@ +language = $language; + } + + public function getLanguage(): Language + { + return $this->language; + } + + public function getEnabledLanguage(): Language + { + if (!$this->hasEnabledLanguage()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasEnabledLanguage() or set it using setEnabledLanguage() before you call the getter.', Language::class)); + } + + return $this->enabledLanguage; + } + + public function setEnabledLanguage(?Language $enabledLanguage): void + { + $this->enabledLanguage = $enabledLanguage; + } + + public function hasEnabledLanguage(): bool + { + return $this->enabledLanguage instanceof Language; + } +} + +class_alias(BeforeEnableLanguageEvent::class, 'eZ\Publish\API\Repository\Events\Language\BeforeEnableLanguageEvent'); diff --git a/eZ/Publish/API/Repository/Events/Language/BeforeUpdateLanguageNameEvent.php b/src/contracts/Repository/Events/Language/BeforeUpdateLanguageNameEvent.php similarity index 75% rename from eZ/Publish/API/Repository/Events/Language/BeforeUpdateLanguageNameEvent.php rename to src/contracts/Repository/Events/Language/BeforeUpdateLanguageNameEvent.php index 1507b2bd7c..da7a9e70a4 100644 --- a/eZ/Publish/API/Repository/Events/Language/BeforeUpdateLanguageNameEvent.php +++ b/src/contracts/Repository/Events/Language/BeforeUpdateLanguageNameEvent.php @@ -6,21 +6,21 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Events\Language; +namespace Ibexa\Contracts\Core\Repository\Events\Language; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\SPI\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; use UnexpectedValueException; final class BeforeUpdateLanguageNameEvent extends BeforeEvent { - /** @var \eZ\Publish\API\Repository\Values\Content\Language */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Language */ private $language; /** @var string */ private $newName; - /** @var \eZ\Publish\API\Repository\Values\Content\Language|null */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Language|null */ private $updatedLanguage; public function __construct(Language $language, string $newName) @@ -58,3 +58,5 @@ public function hasUpdatedLanguage(): bool return $this->updatedLanguage instanceof Language; } } + +class_alias(BeforeUpdateLanguageNameEvent::class, 'eZ\Publish\API\Repository\Events\Language\BeforeUpdateLanguageNameEvent'); diff --git a/src/contracts/Repository/Events/Language/CreateLanguageEvent.php b/src/contracts/Repository/Events/Language/CreateLanguageEvent.php new file mode 100644 index 0000000000..a67d68cec2 --- /dev/null +++ b/src/contracts/Repository/Events/Language/CreateLanguageEvent.php @@ -0,0 +1,42 @@ +language = $language; + $this->languageCreateStruct = $languageCreateStruct; + } + + public function getLanguage(): Language + { + return $this->language; + } + + public function getLanguageCreateStruct(): LanguageCreateStruct + { + return $this->languageCreateStruct; + } +} + +class_alias(CreateLanguageEvent::class, 'eZ\Publish\API\Repository\Events\Language\CreateLanguageEvent'); diff --git a/src/contracts/Repository/Events/Language/DeleteLanguageEvent.php b/src/contracts/Repository/Events/Language/DeleteLanguageEvent.php new file mode 100644 index 0000000000..7a60c0dca8 --- /dev/null +++ b/src/contracts/Repository/Events/Language/DeleteLanguageEvent.php @@ -0,0 +1,30 @@ +language = $language; + } + + public function getLanguage(): Language + { + return $this->language; + } +} + +class_alias(DeleteLanguageEvent::class, 'eZ\Publish\API\Repository\Events\Language\DeleteLanguageEvent'); diff --git a/src/contracts/Repository/Events/Language/DisableLanguageEvent.php b/src/contracts/Repository/Events/Language/DisableLanguageEvent.php new file mode 100644 index 0000000000..0f2d22decd --- /dev/null +++ b/src/contracts/Repository/Events/Language/DisableLanguageEvent.php @@ -0,0 +1,41 @@ +disabledLanguage = $disabledLanguage; + $this->language = $language; + } + + public function getDisabledLanguage(): Language + { + return $this->disabledLanguage; + } + + public function getLanguage(): Language + { + return $this->language; + } +} + +class_alias(DisableLanguageEvent::class, 'eZ\Publish\API\Repository\Events\Language\DisableLanguageEvent'); diff --git a/src/contracts/Repository/Events/Language/EnableLanguageEvent.php b/src/contracts/Repository/Events/Language/EnableLanguageEvent.php new file mode 100644 index 0000000000..12fb30de7a --- /dev/null +++ b/src/contracts/Repository/Events/Language/EnableLanguageEvent.php @@ -0,0 +1,41 @@ +enabledLanguage = $enabledLanguage; + $this->language = $language; + } + + public function getEnabledLanguage(): Language + { + return $this->enabledLanguage; + } + + public function getLanguage(): Language + { + return $this->language; + } +} + +class_alias(EnableLanguageEvent::class, 'eZ\Publish\API\Repository\Events\Language\EnableLanguageEvent'); diff --git a/src/contracts/Repository/Events/Language/UpdateLanguageNameEvent.php b/src/contracts/Repository/Events/Language/UpdateLanguageNameEvent.php new file mode 100644 index 0000000000..c39fb372cc --- /dev/null +++ b/src/contracts/Repository/Events/Language/UpdateLanguageNameEvent.php @@ -0,0 +1,51 @@ +updatedLanguage = $updatedLanguage; + $this->language = $language; + $this->newName = $newName; + } + + public function getUpdatedLanguage(): Language + { + return $this->updatedLanguage; + } + + public function getLanguage(): Language + { + return $this->language; + } + + public function getNewName(): string + { + return $this->newName; + } +} + +class_alias(UpdateLanguageNameEvent::class, 'eZ\Publish\API\Repository\Events\Language\UpdateLanguageNameEvent'); diff --git a/src/contracts/Repository/Events/Location/BeforeCopySubtreeEvent.php b/src/contracts/Repository/Events/Location/BeforeCopySubtreeEvent.php new file mode 100644 index 0000000000..94865160c5 --- /dev/null +++ b/src/contracts/Repository/Events/Location/BeforeCopySubtreeEvent.php @@ -0,0 +1,62 @@ +subtree = $subtree; + $this->targetParentLocation = $targetParentLocation; + } + + public function getSubtree(): Location + { + return $this->subtree; + } + + public function getTargetParentLocation(): Location + { + return $this->targetParentLocation; + } + + public function getLocation(): Location + { + if (!$this->hasLocation()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasLocation() or set it using setLocation() before you call the getter.', Location::class)); + } + + return $this->location; + } + + public function setLocation(?Location $location): void + { + $this->location = $location; + } + + public function hasLocation(): bool + { + return $this->location instanceof Location; + } +} + +class_alias(BeforeCopySubtreeEvent::class, 'eZ\Publish\API\Repository\Events\Location\BeforeCopySubtreeEvent'); diff --git a/src/contracts/Repository/Events/Location/BeforeCreateLocationEvent.php b/src/contracts/Repository/Events/Location/BeforeCreateLocationEvent.php new file mode 100644 index 0000000000..fda1f6b50e --- /dev/null +++ b/src/contracts/Repository/Events/Location/BeforeCreateLocationEvent.php @@ -0,0 +1,64 @@ +contentInfo = $contentInfo; + $this->locationCreateStruct = $locationCreateStruct; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getLocationCreateStruct(): LocationCreateStruct + { + return $this->locationCreateStruct; + } + + public function getLocation(): Location + { + if (!$this->hasLocation()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasLocation() or set it using setLocation() before you call the getter.', Location::class)); + } + + return $this->location; + } + + public function setLocation(?Location $location): void + { + $this->location = $location; + } + + public function hasLocation(): bool + { + return $this->location instanceof Location; + } +} + +class_alias(BeforeCreateLocationEvent::class, 'eZ\Publish\API\Repository\Events\Location\BeforeCreateLocationEvent'); diff --git a/src/contracts/Repository/Events/Location/BeforeDeleteLocationEvent.php b/src/contracts/Repository/Events/Location/BeforeDeleteLocationEvent.php new file mode 100644 index 0000000000..9120e41b7e --- /dev/null +++ b/src/contracts/Repository/Events/Location/BeforeDeleteLocationEvent.php @@ -0,0 +1,30 @@ +location = $location; + } + + public function getLocation(): Location + { + return $this->location; + } +} + +class_alias(BeforeDeleteLocationEvent::class, 'eZ\Publish\API\Repository\Events\Location\BeforeDeleteLocationEvent'); diff --git a/src/contracts/Repository/Events/Location/BeforeHideLocationEvent.php b/src/contracts/Repository/Events/Location/BeforeHideLocationEvent.php new file mode 100644 index 0000000000..1cde181680 --- /dev/null +++ b/src/contracts/Repository/Events/Location/BeforeHideLocationEvent.php @@ -0,0 +1,53 @@ +location = $location; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getHiddenLocation(): Location + { + if (!$this->hasHiddenLocation()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasHiddenLocation() or set it using setHiddenLocation() before you call the getter.', Location::class)); + } + + return $this->hiddenLocation; + } + + public function setHiddenLocation(?Location $hiddenLocation): void + { + $this->hiddenLocation = $hiddenLocation; + } + + public function hasHiddenLocation(): bool + { + return $this->hiddenLocation instanceof Location; + } +} + +class_alias(BeforeHideLocationEvent::class, 'eZ\Publish\API\Repository\Events\Location\BeforeHideLocationEvent'); diff --git a/src/contracts/Repository/Events/Location/BeforeMoveSubtreeEvent.php b/src/contracts/Repository/Events/Location/BeforeMoveSubtreeEvent.php new file mode 100644 index 0000000000..b116119355 --- /dev/null +++ b/src/contracts/Repository/Events/Location/BeforeMoveSubtreeEvent.php @@ -0,0 +1,39 @@ +location = $location; + $this->newParentLocation = $newParentLocation; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getNewParentLocation(): Location + { + return $this->newParentLocation; + } +} + +class_alias(BeforeMoveSubtreeEvent::class, 'eZ\Publish\API\Repository\Events\Location\BeforeMoveSubtreeEvent'); diff --git a/src/contracts/Repository/Events/Location/BeforeSwapLocationEvent.php b/src/contracts/Repository/Events/Location/BeforeSwapLocationEvent.php new file mode 100644 index 0000000000..6f10e9f4fb --- /dev/null +++ b/src/contracts/Repository/Events/Location/BeforeSwapLocationEvent.php @@ -0,0 +1,39 @@ +location1 = $location1; + $this->location2 = $location2; + } + + public function getLocation1(): Location + { + return $this->location1; + } + + public function getLocation2(): Location + { + return $this->location2; + } +} + +class_alias(BeforeSwapLocationEvent::class, 'eZ\Publish\API\Repository\Events\Location\BeforeSwapLocationEvent'); diff --git a/src/contracts/Repository/Events/Location/BeforeUnhideLocationEvent.php b/src/contracts/Repository/Events/Location/BeforeUnhideLocationEvent.php new file mode 100644 index 0000000000..336a707363 --- /dev/null +++ b/src/contracts/Repository/Events/Location/BeforeUnhideLocationEvent.php @@ -0,0 +1,53 @@ +location = $location; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getRevealedLocation(): Location + { + if (!$this->hasRevealedLocation()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasRevealedLocation() or set it using setRevealedLocation() before you call the getter.', Location::class)); + } + + return $this->revealedLocation; + } + + public function setRevealedLocation(?Location $revealedLocation): void + { + $this->revealedLocation = $revealedLocation; + } + + public function hasRevealedLocation(): bool + { + return $this->revealedLocation instanceof Location; + } +} + +class_alias(BeforeUnhideLocationEvent::class, 'eZ\Publish\API\Repository\Events\Location\BeforeUnhideLocationEvent'); diff --git a/src/contracts/Repository/Events/Location/BeforeUpdateLocationEvent.php b/src/contracts/Repository/Events/Location/BeforeUpdateLocationEvent.php new file mode 100644 index 0000000000..9d14ef3af6 --- /dev/null +++ b/src/contracts/Repository/Events/Location/BeforeUpdateLocationEvent.php @@ -0,0 +1,63 @@ +location = $location; + $this->locationUpdateStruct = $locationUpdateStruct; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getLocationUpdateStruct(): LocationUpdateStruct + { + return $this->locationUpdateStruct; + } + + public function getUpdatedLocation(): Location + { + if (!$this->hasUpdatedLocation()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedLocation() or set it using setUpdatedLocation() before you call the getter.', Location::class)); + } + + return $this->updatedLocation; + } + + public function setUpdatedLocation(?Location $updatedLocation): void + { + $this->updatedLocation = $updatedLocation; + } + + public function hasUpdatedLocation(): bool + { + return $this->updatedLocation instanceof Location; + } +} + +class_alias(BeforeUpdateLocationEvent::class, 'eZ\Publish\API\Repository\Events\Location\BeforeUpdateLocationEvent'); diff --git a/src/contracts/Repository/Events/Location/CopySubtreeEvent.php b/src/contracts/Repository/Events/Location/CopySubtreeEvent.php new file mode 100644 index 0000000000..acb0be4fe4 --- /dev/null +++ b/src/contracts/Repository/Events/Location/CopySubtreeEvent.php @@ -0,0 +1,51 @@ +location = $location; + $this->subtree = $subtree; + $this->targetParentLocation = $targetParentLocation; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getSubtree(): Location + { + return $this->subtree; + } + + public function getTargetParentLocation(): Location + { + return $this->targetParentLocation; + } +} + +class_alias(CopySubtreeEvent::class, 'eZ\Publish\API\Repository\Events\Location\CopySubtreeEvent'); diff --git a/src/contracts/Repository/Events/Location/CreateLocationEvent.php b/src/contracts/Repository/Events/Location/CreateLocationEvent.php new file mode 100644 index 0000000000..7aef04e238 --- /dev/null +++ b/src/contracts/Repository/Events/Location/CreateLocationEvent.php @@ -0,0 +1,53 @@ +location = $location; + $this->contentInfo = $contentInfo; + $this->locationCreateStruct = $locationCreateStruct; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getLocationCreateStruct(): LocationCreateStruct + { + return $this->locationCreateStruct; + } +} + +class_alias(CreateLocationEvent::class, 'eZ\Publish\API\Repository\Events\Location\CreateLocationEvent'); diff --git a/src/contracts/Repository/Events/Location/DeleteLocationEvent.php b/src/contracts/Repository/Events/Location/DeleteLocationEvent.php new file mode 100644 index 0000000000..8fc5aca85f --- /dev/null +++ b/src/contracts/Repository/Events/Location/DeleteLocationEvent.php @@ -0,0 +1,30 @@ +location = $location; + } + + public function getLocation(): Location + { + return $this->location; + } +} + +class_alias(DeleteLocationEvent::class, 'eZ\Publish\API\Repository\Events\Location\DeleteLocationEvent'); diff --git a/src/contracts/Repository/Events/Location/HideLocationEvent.php b/src/contracts/Repository/Events/Location/HideLocationEvent.php new file mode 100644 index 0000000000..f3bd956d09 --- /dev/null +++ b/src/contracts/Repository/Events/Location/HideLocationEvent.php @@ -0,0 +1,41 @@ +location = $location; + $this->hiddenLocation = $hiddenLocation; + } + + public function getHiddenLocation(): Location + { + return $this->hiddenLocation; + } + + public function getLocation(): Location + { + return $this->location; + } +} + +class_alias(HideLocationEvent::class, 'eZ\Publish\API\Repository\Events\Location\HideLocationEvent'); diff --git a/src/contracts/Repository/Events/Location/MoveSubtreeEvent.php b/src/contracts/Repository/Events/Location/MoveSubtreeEvent.php new file mode 100644 index 0000000000..6ffbea80e7 --- /dev/null +++ b/src/contracts/Repository/Events/Location/MoveSubtreeEvent.php @@ -0,0 +1,41 @@ +location = $location; + $this->newParentLocation = $newParentLocation; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getNewParentLocation(): Location + { + return $this->newParentLocation; + } +} + +class_alias(MoveSubtreeEvent::class, 'eZ\Publish\API\Repository\Events\Location\MoveSubtreeEvent'); diff --git a/src/contracts/Repository/Events/Location/SwapLocationEvent.php b/src/contracts/Repository/Events/Location/SwapLocationEvent.php new file mode 100644 index 0000000000..1f6c259781 --- /dev/null +++ b/src/contracts/Repository/Events/Location/SwapLocationEvent.php @@ -0,0 +1,41 @@ +location1 = $location1; + $this->location2 = $location2; + } + + public function getLocation1(): Location + { + return $this->location1; + } + + public function getLocation2(): Location + { + return $this->location2; + } +} + +class_alias(SwapLocationEvent::class, 'eZ\Publish\API\Repository\Events\Location\SwapLocationEvent'); diff --git a/src/contracts/Repository/Events/Location/UnhideLocationEvent.php b/src/contracts/Repository/Events/Location/UnhideLocationEvent.php new file mode 100644 index 0000000000..7266762257 --- /dev/null +++ b/src/contracts/Repository/Events/Location/UnhideLocationEvent.php @@ -0,0 +1,41 @@ +revealedLocation = $revealedLocation; + $this->location = $location; + } + + public function getRevealedLocation(): Location + { + return $this->revealedLocation; + } + + public function getLocation(): Location + { + return $this->location; + } +} + +class_alias(UnhideLocationEvent::class, 'eZ\Publish\API\Repository\Events\Location\UnhideLocationEvent'); diff --git a/src/contracts/Repository/Events/Location/UpdateLocationEvent.php b/src/contracts/Repository/Events/Location/UpdateLocationEvent.php new file mode 100644 index 0000000000..c645374c6d --- /dev/null +++ b/src/contracts/Repository/Events/Location/UpdateLocationEvent.php @@ -0,0 +1,52 @@ +updatedLocation = $updatedLocation; + $this->location = $location; + $this->locationUpdateStruct = $locationUpdateStruct; + } + + public function getUpdatedLocation(): Location + { + return $this->updatedLocation; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getLocationUpdateStruct(): LocationUpdateStruct + { + return $this->locationUpdateStruct; + } +} + +class_alias(UpdateLocationEvent::class, 'eZ\Publish\API\Repository\Events\Location\UpdateLocationEvent'); diff --git a/src/contracts/Repository/Events/Notification/BeforeCreateNotificationEvent.php b/src/contracts/Repository/Events/Notification/BeforeCreateNotificationEvent.php new file mode 100644 index 0000000000..f9d3a273f9 --- /dev/null +++ b/src/contracts/Repository/Events/Notification/BeforeCreateNotificationEvent.php @@ -0,0 +1,54 @@ +createStruct = $createStruct; + } + + public function getCreateStruct(): CreateStruct + { + return $this->createStruct; + } + + public function getNotification(): Notification + { + if (!$this->hasNotification()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasNotification() or set it using setNotification() before you call the getter.', Notification::class)); + } + + return $this->notification; + } + + public function setNotification(?Notification $notification): void + { + $this->notification = $notification; + } + + public function hasNotification(): bool + { + return $this->notification instanceof Notification; + } +} + +class_alias(BeforeCreateNotificationEvent::class, 'eZ\Publish\API\Repository\Events\Notification\BeforeCreateNotificationEvent'); diff --git a/src/contracts/Repository/Events/Notification/BeforeDeleteNotificationEvent.php b/src/contracts/Repository/Events/Notification/BeforeDeleteNotificationEvent.php new file mode 100644 index 0000000000..0a63162d9c --- /dev/null +++ b/src/contracts/Repository/Events/Notification/BeforeDeleteNotificationEvent.php @@ -0,0 +1,30 @@ +notification = $notification; + } + + public function getNotification(): Notification + { + return $this->notification; + } +} + +class_alias(BeforeDeleteNotificationEvent::class, 'eZ\Publish\API\Repository\Events\Notification\BeforeDeleteNotificationEvent'); diff --git a/src/contracts/Repository/Events/Notification/BeforeMarkNotificationAsReadEvent.php b/src/contracts/Repository/Events/Notification/BeforeMarkNotificationAsReadEvent.php new file mode 100644 index 0000000000..76197b069c --- /dev/null +++ b/src/contracts/Repository/Events/Notification/BeforeMarkNotificationAsReadEvent.php @@ -0,0 +1,30 @@ +notification = $notification; + } + + public function getNotification(): Notification + { + return $this->notification; + } +} + +class_alias(BeforeMarkNotificationAsReadEvent::class, 'eZ\Publish\API\Repository\Events\Notification\BeforeMarkNotificationAsReadEvent'); diff --git a/src/contracts/Repository/Events/Notification/CreateNotificationEvent.php b/src/contracts/Repository/Events/Notification/CreateNotificationEvent.php new file mode 100644 index 0000000000..8c340bbd2c --- /dev/null +++ b/src/contracts/Repository/Events/Notification/CreateNotificationEvent.php @@ -0,0 +1,42 @@ +notification = $notification; + $this->createStruct = $createStruct; + } + + public function getNotification(): Notification + { + return $this->notification; + } + + public function getCreateStruct(): CreateStruct + { + return $this->createStruct; + } +} + +class_alias(CreateNotificationEvent::class, 'eZ\Publish\API\Repository\Events\Notification\CreateNotificationEvent'); diff --git a/src/contracts/Repository/Events/Notification/DeleteNotificationEvent.php b/src/contracts/Repository/Events/Notification/DeleteNotificationEvent.php new file mode 100644 index 0000000000..1a25c93738 --- /dev/null +++ b/src/contracts/Repository/Events/Notification/DeleteNotificationEvent.php @@ -0,0 +1,30 @@ +notification = $notification; + } + + public function getNotification(): Notification + { + return $this->notification; + } +} + +class_alias(DeleteNotificationEvent::class, 'eZ\Publish\API\Repository\Events\Notification\DeleteNotificationEvent'); diff --git a/src/contracts/Repository/Events/Notification/MarkNotificationAsReadEvent.php b/src/contracts/Repository/Events/Notification/MarkNotificationAsReadEvent.php new file mode 100644 index 0000000000..0efdc49357 --- /dev/null +++ b/src/contracts/Repository/Events/Notification/MarkNotificationAsReadEvent.php @@ -0,0 +1,30 @@ +notification = $notification; + } + + public function getNotification(): Notification + { + return $this->notification; + } +} + +class_alias(MarkNotificationAsReadEvent::class, 'eZ\Publish\API\Repository\Events\Notification\MarkNotificationAsReadEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/BeforeCreateObjectStateEvent.php b/src/contracts/Repository/Events/ObjectState/BeforeCreateObjectStateEvent.php new file mode 100644 index 0000000000..b723a943ab --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/BeforeCreateObjectStateEvent.php @@ -0,0 +1,64 @@ +objectStateGroup = $objectStateGroup; + $this->objectStateCreateStruct = $objectStateCreateStruct; + } + + public function getObjectStateGroup(): ObjectStateGroup + { + return $this->objectStateGroup; + } + + public function getObjectStateCreateStruct(): ObjectStateCreateStruct + { + return $this->objectStateCreateStruct; + } + + public function getObjectState(): ObjectState + { + if (!$this->hasObjectState()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasObjectState() or set it using setObjectState() before you call the getter.', ObjectState::class)); + } + + return $this->objectState; + } + + public function setObjectState(?ObjectState $objectState): void + { + $this->objectState = $objectState; + } + + public function hasObjectState(): bool + { + return $this->objectState instanceof ObjectState; + } +} + +class_alias(BeforeCreateObjectStateEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\BeforeCreateObjectStateEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/BeforeCreateObjectStateGroupEvent.php b/src/contracts/Repository/Events/ObjectState/BeforeCreateObjectStateGroupEvent.php new file mode 100644 index 0000000000..d293261e1c --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/BeforeCreateObjectStateGroupEvent.php @@ -0,0 +1,54 @@ +objectStateGroupCreateStruct = $objectStateGroupCreateStruct; + } + + public function getObjectStateGroupCreateStruct(): ObjectStateGroupCreateStruct + { + return $this->objectStateGroupCreateStruct; + } + + public function getObjectStateGroup(): ObjectStateGroup + { + if (!$this->hasObjectStateGroup()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasObjectStateGroup() or set it using setObjectStateGroup() before you call the getter.', ObjectStateGroup::class)); + } + + return $this->objectStateGroup; + } + + public function setObjectStateGroup(?ObjectStateGroup $objectStateGroup): void + { + $this->objectStateGroup = $objectStateGroup; + } + + public function hasObjectStateGroup(): bool + { + return $this->objectStateGroup instanceof ObjectStateGroup; + } +} + +class_alias(BeforeCreateObjectStateGroupEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\BeforeCreateObjectStateGroupEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/BeforeDeleteObjectStateEvent.php b/src/contracts/Repository/Events/ObjectState/BeforeDeleteObjectStateEvent.php new file mode 100644 index 0000000000..256c0ba2f8 --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/BeforeDeleteObjectStateEvent.php @@ -0,0 +1,30 @@ +objectState = $objectState; + } + + public function getObjectState(): ObjectState + { + return $this->objectState; + } +} + +class_alias(BeforeDeleteObjectStateEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\BeforeDeleteObjectStateEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/BeforeDeleteObjectStateGroupEvent.php b/src/contracts/Repository/Events/ObjectState/BeforeDeleteObjectStateGroupEvent.php new file mode 100644 index 0000000000..96f3a79ec8 --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/BeforeDeleteObjectStateGroupEvent.php @@ -0,0 +1,30 @@ +objectStateGroup = $objectStateGroup; + } + + public function getObjectStateGroup(): ObjectStateGroup + { + return $this->objectStateGroup; + } +} + +class_alias(BeforeDeleteObjectStateGroupEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\BeforeDeleteObjectStateGroupEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/BeforeSetContentStateEvent.php b/src/contracts/Repository/Events/ObjectState/BeforeSetContentStateEvent.php new file mode 100644 index 0000000000..88642fac0f --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/BeforeSetContentStateEvent.php @@ -0,0 +1,50 @@ +contentInfo = $contentInfo; + $this->objectStateGroup = $objectStateGroup; + $this->objectState = $objectState; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getObjectStateGroup(): ObjectStateGroup + { + return $this->objectStateGroup; + } + + public function getObjectState(): ObjectState + { + return $this->objectState; + } +} + +class_alias(BeforeSetContentStateEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\BeforeSetContentStateEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/BeforeSetPriorityOfObjectStateEvent.php b/src/contracts/Repository/Events/ObjectState/BeforeSetPriorityOfObjectStateEvent.php new file mode 100644 index 0000000000..c913b01409 --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/BeforeSetPriorityOfObjectStateEvent.php @@ -0,0 +1,38 @@ +objectState = $objectState; + $this->priority = $priority; + } + + public function getObjectState(): ObjectState + { + return $this->objectState; + } + + public function getPriority() + { + return $this->priority; + } +} + +class_alias(BeforeSetPriorityOfObjectStateEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\BeforeSetPriorityOfObjectStateEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/BeforeUpdateObjectStateEvent.php b/src/contracts/Repository/Events/ObjectState/BeforeUpdateObjectStateEvent.php new file mode 100644 index 0000000000..32cbba6212 --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/BeforeUpdateObjectStateEvent.php @@ -0,0 +1,63 @@ +objectState = $objectState; + $this->objectStateUpdateStruct = $objectStateUpdateStruct; + } + + public function getObjectState(): ObjectState + { + return $this->objectState; + } + + public function getObjectStateUpdateStruct(): ObjectStateUpdateStruct + { + return $this->objectStateUpdateStruct; + } + + public function getUpdatedObjectState(): ObjectState + { + if (!$this->hasUpdatedObjectState()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedObjectState() or set it using setUpdatedObjectState() before you call the getter.', ObjectState::class)); + } + + return $this->updatedObjectState; + } + + public function setUpdatedObjectState(?ObjectState $updatedObjectState): void + { + $this->updatedObjectState = $updatedObjectState; + } + + public function hasUpdatedObjectState(): bool + { + return $this->updatedObjectState instanceof ObjectState; + } +} + +class_alias(BeforeUpdateObjectStateEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\BeforeUpdateObjectStateEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/BeforeUpdateObjectStateGroupEvent.php b/src/contracts/Repository/Events/ObjectState/BeforeUpdateObjectStateGroupEvent.php new file mode 100644 index 0000000000..a6a5e694e7 --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/BeforeUpdateObjectStateGroupEvent.php @@ -0,0 +1,63 @@ +objectStateGroup = $objectStateGroup; + $this->objectStateGroupUpdateStruct = $objectStateGroupUpdateStruct; + } + + public function getObjectStateGroup(): ObjectStateGroup + { + return $this->objectStateGroup; + } + + public function getObjectStateGroupUpdateStruct(): ObjectStateGroupUpdateStruct + { + return $this->objectStateGroupUpdateStruct; + } + + public function getUpdatedObjectStateGroup(): ObjectStateGroup + { + if (!$this->hasUpdatedObjectStateGroup()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedObjectStateGroup() or set it using setUpdatedObjectStateGroup() before you call the getter.', ObjectStateGroup::class)); + } + + return $this->updatedObjectStateGroup; + } + + public function setUpdatedObjectStateGroup(?ObjectStateGroup $updatedObjectStateGroup): void + { + $this->updatedObjectStateGroup = $updatedObjectStateGroup; + } + + public function hasUpdatedObjectStateGroup(): bool + { + return $this->updatedObjectStateGroup instanceof ObjectStateGroup; + } +} + +class_alias(BeforeUpdateObjectStateGroupEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\BeforeUpdateObjectStateGroupEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/CreateObjectStateEvent.php b/src/contracts/Repository/Events/ObjectState/CreateObjectStateEvent.php new file mode 100644 index 0000000000..f524c51894 --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/CreateObjectStateEvent.php @@ -0,0 +1,53 @@ +objectState = $objectState; + $this->objectStateGroup = $objectStateGroup; + $this->objectStateCreateStruct = $objectStateCreateStruct; + } + + public function getObjectState(): ObjectState + { + return $this->objectState; + } + + public function getObjectStateGroup(): ObjectStateGroup + { + return $this->objectStateGroup; + } + + public function getObjectStateCreateStruct(): ObjectStateCreateStruct + { + return $this->objectStateCreateStruct; + } +} + +class_alias(CreateObjectStateEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\CreateObjectStateEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/CreateObjectStateGroupEvent.php b/src/contracts/Repository/Events/ObjectState/CreateObjectStateGroupEvent.php new file mode 100644 index 0000000000..c185405f51 --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/CreateObjectStateGroupEvent.php @@ -0,0 +1,42 @@ +objectStateGroup = $objectStateGroup; + $this->objectStateGroupCreateStruct = $objectStateGroupCreateStruct; + } + + public function getObjectStateGroup(): ObjectStateGroup + { + return $this->objectStateGroup; + } + + public function getObjectStateGroupCreateStruct(): ObjectStateGroupCreateStruct + { + return $this->objectStateGroupCreateStruct; + } +} + +class_alias(CreateObjectStateGroupEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\CreateObjectStateGroupEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/DeleteObjectStateEvent.php b/src/contracts/Repository/Events/ObjectState/DeleteObjectStateEvent.php new file mode 100644 index 0000000000..4a6f2bf412 --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/DeleteObjectStateEvent.php @@ -0,0 +1,30 @@ +objectState = $objectState; + } + + public function getObjectState(): ObjectState + { + return $this->objectState; + } +} + +class_alias(DeleteObjectStateEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\DeleteObjectStateEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/DeleteObjectStateGroupEvent.php b/src/contracts/Repository/Events/ObjectState/DeleteObjectStateGroupEvent.php new file mode 100644 index 0000000000..5707efc456 --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/DeleteObjectStateGroupEvent.php @@ -0,0 +1,30 @@ +objectStateGroup = $objectStateGroup; + } + + public function getObjectStateGroup(): ObjectStateGroup + { + return $this->objectStateGroup; + } +} + +class_alias(DeleteObjectStateGroupEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\DeleteObjectStateGroupEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/SetContentStateEvent.php b/src/contracts/Repository/Events/ObjectState/SetContentStateEvent.php new file mode 100644 index 0000000000..8949497f8d --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/SetContentStateEvent.php @@ -0,0 +1,53 @@ +contentInfo = $contentInfo; + $this->objectStateGroup = $objectStateGroup; + $this->objectState = $objectState; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getObjectStateGroup(): ObjectStateGroup + { + return $this->objectStateGroup; + } + + public function getObjectState(): ObjectState + { + return $this->objectState; + } +} + +class_alias(SetContentStateEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\SetContentStateEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/SetPriorityOfObjectStateEvent.php b/src/contracts/Repository/Events/ObjectState/SetPriorityOfObjectStateEvent.php new file mode 100644 index 0000000000..f717fbacee --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/SetPriorityOfObjectStateEvent.php @@ -0,0 +1,40 @@ +objectState = $objectState; + $this->priority = $priority; + } + + public function getObjectState(): ObjectState + { + return $this->objectState; + } + + public function getPriority() + { + return $this->priority; + } +} + +class_alias(SetPriorityOfObjectStateEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\SetPriorityOfObjectStateEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/UpdateObjectStateEvent.php b/src/contracts/Repository/Events/ObjectState/UpdateObjectStateEvent.php new file mode 100644 index 0000000000..bc31e657c4 --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/UpdateObjectStateEvent.php @@ -0,0 +1,52 @@ +updatedObjectState = $updatedObjectState; + $this->objectState = $objectState; + $this->objectStateUpdateStruct = $objectStateUpdateStruct; + } + + public function getUpdatedObjectState(): ObjectState + { + return $this->updatedObjectState; + } + + public function getObjectState(): ObjectState + { + return $this->objectState; + } + + public function getObjectStateUpdateStruct(): ObjectStateUpdateStruct + { + return $this->objectStateUpdateStruct; + } +} + +class_alias(UpdateObjectStateEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\UpdateObjectStateEvent'); diff --git a/src/contracts/Repository/Events/ObjectState/UpdateObjectStateGroupEvent.php b/src/contracts/Repository/Events/ObjectState/UpdateObjectStateGroupEvent.php new file mode 100644 index 0000000000..0bc1afb3e4 --- /dev/null +++ b/src/contracts/Repository/Events/ObjectState/UpdateObjectStateGroupEvent.php @@ -0,0 +1,52 @@ +updatedObjectStateGroup = $updatedObjectStateGroup; + $this->objectStateGroup = $objectStateGroup; + $this->objectStateGroupUpdateStruct = $objectStateGroupUpdateStruct; + } + + public function getUpdatedObjectStateGroup(): ObjectStateGroup + { + return $this->updatedObjectStateGroup; + } + + public function getObjectStateGroup(): ObjectStateGroup + { + return $this->objectStateGroup; + } + + public function getObjectStateGroupUpdateStruct(): ObjectStateGroupUpdateStruct + { + return $this->objectStateGroupUpdateStruct; + } +} + +class_alias(UpdateObjectStateGroupEvent::class, 'eZ\Publish\API\Repository\Events\ObjectState\UpdateObjectStateGroupEvent'); diff --git a/src/contracts/Repository/Events/Role/AddPolicyByRoleDraftEvent.php b/src/contracts/Repository/Events/Role/AddPolicyByRoleDraftEvent.php new file mode 100644 index 0000000000..c9c7146090 --- /dev/null +++ b/src/contracts/Repository/Events/Role/AddPolicyByRoleDraftEvent.php @@ -0,0 +1,51 @@ +roleDraft = $roleDraft; + $this->policyCreateStruct = $policyCreateStruct; + $this->updatedRoleDraft = $updatedRoleDraft; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } + + public function getPolicyCreateStruct(): PolicyCreateStruct + { + return $this->policyCreateStruct; + } + + public function getUpdatedRoleDraft(): RoleDraft + { + return $this->updatedRoleDraft; + } +} + +class_alias(AddPolicyByRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\AddPolicyByRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Role/AssignRoleToUserEvent.php b/src/contracts/Repository/Events/Role/AssignRoleToUserEvent.php new file mode 100644 index 0000000000..4cffe97741 --- /dev/null +++ b/src/contracts/Repository/Events/Role/AssignRoleToUserEvent.php @@ -0,0 +1,53 @@ +role = $role; + $this->user = $user; + $this->roleLimitation = $roleLimitation; + } + + public function getRole(): Role + { + return $this->role; + } + + public function getUser(): User + { + return $this->user; + } + + public function getRoleLimitation(): ?RoleLimitation + { + return $this->roleLimitation; + } +} + +class_alias(AssignRoleToUserEvent::class, 'eZ\Publish\API\Repository\Events\Role\AssignRoleToUserEvent'); diff --git a/src/contracts/Repository/Events/Role/AssignRoleToUserGroupEvent.php b/src/contracts/Repository/Events/Role/AssignRoleToUserGroupEvent.php new file mode 100644 index 0000000000..74e1aea1e7 --- /dev/null +++ b/src/contracts/Repository/Events/Role/AssignRoleToUserGroupEvent.php @@ -0,0 +1,53 @@ +role = $role; + $this->userGroup = $userGroup; + $this->roleLimitation = $roleLimitation; + } + + public function getRole(): Role + { + return $this->role; + } + + public function getUserGroup(): UserGroup + { + return $this->userGroup; + } + + public function getRoleLimitation(): ?RoleLimitation + { + return $this->roleLimitation; + } +} + +class_alias(AssignRoleToUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\Role\AssignRoleToUserGroupEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforeAddPolicyByRoleDraftEvent.php b/src/contracts/Repository/Events/Role/BeforeAddPolicyByRoleDraftEvent.php new file mode 100644 index 0000000000..ba3cda04b4 --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforeAddPolicyByRoleDraftEvent.php @@ -0,0 +1,63 @@ +roleDraft = $roleDraft; + $this->policyCreateStruct = $policyCreateStruct; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } + + public function getPolicyCreateStruct(): PolicyCreateStruct + { + return $this->policyCreateStruct; + } + + public function getUpdatedRoleDraft(): RoleDraft + { + if (!$this->hasUpdatedRoleDraft()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedRoleDraft() or set it using setUpdatedRoleDraft() before you call the getter.', RoleDraft::class)); + } + + return $this->updatedRoleDraft; + } + + public function setUpdatedRoleDraft(?RoleDraft $updatedRoleDraft): void + { + $this->updatedRoleDraft = $updatedRoleDraft; + } + + public function hasUpdatedRoleDraft(): bool + { + return $this->updatedRoleDraft instanceof RoleDraft; + } +} + +class_alias(BeforeAddPolicyByRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforeAddPolicyByRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforeAssignRoleToUserEvent.php b/src/contracts/Repository/Events/Role/BeforeAssignRoleToUserEvent.php new file mode 100644 index 0000000000..09fce4ac3a --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforeAssignRoleToUserEvent.php @@ -0,0 +1,50 @@ +role = $role; + $this->user = $user; + $this->roleLimitation = $roleLimitation; + } + + public function getRole(): Role + { + return $this->role; + } + + public function getUser(): User + { + return $this->user; + } + + public function getRoleLimitation(): ?RoleLimitation + { + return $this->roleLimitation; + } +} + +class_alias(BeforeAssignRoleToUserEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforeAssignRoleToUserEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforeAssignRoleToUserGroupEvent.php b/src/contracts/Repository/Events/Role/BeforeAssignRoleToUserGroupEvent.php new file mode 100644 index 0000000000..56ef34393f --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforeAssignRoleToUserGroupEvent.php @@ -0,0 +1,50 @@ +role = $role; + $this->userGroup = $userGroup; + $this->roleLimitation = $roleLimitation; + } + + public function getRole(): Role + { + return $this->role; + } + + public function getUserGroup(): UserGroup + { + return $this->userGroup; + } + + public function getRoleLimitation(): ?RoleLimitation + { + return $this->roleLimitation; + } +} + +class_alias(BeforeAssignRoleToUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforeAssignRoleToUserGroupEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforeCopyRoleEvent.php b/src/contracts/Repository/Events/Role/BeforeCopyRoleEvent.php new file mode 100644 index 0000000000..9380d86af1 --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforeCopyRoleEvent.php @@ -0,0 +1,66 @@ +role = $role; + $this->roleCopyStruct = $roleCopyStruct; + } + + public function getRole(): Role + { + return $this->role; + } + + public function getRoleCopyStruct(): RoleCopyStruct + { + return $this->roleCopyStruct; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + public function getCopiedRole(): Role + { + if (!$this->hasCopiedRole()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasCopiedRole() or set it using setCopiedRole() before you call the getter.', Role::class)); + } + + return $this->copiedRole; + } + + public function setCopiedRole(?Role $copiedRole): void + { + $this->copiedRole = $copiedRole; + } + + public function hasCopiedRole(): bool + { + return $this->copiedRole instanceof Role; + } +} + +class_alias(BeforeCopyRoleEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforeCopyRoleEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforeCreateRoleDraftEvent.php b/src/contracts/Repository/Events/Role/BeforeCreateRoleDraftEvent.php new file mode 100644 index 0000000000..43082aa118 --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforeCreateRoleDraftEvent.php @@ -0,0 +1,54 @@ +role = $role; + } + + public function getRole(): Role + { + return $this->role; + } + + public function getRoleDraft(): RoleDraft + { + if (!$this->hasRoleDraft()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasRoleDraft() or set it using setRoleDraft() before you call the getter.', RoleDraft::class)); + } + + return $this->roleDraft; + } + + public function setRoleDraft(?RoleDraft $roleDraft): void + { + $this->roleDraft = $roleDraft; + } + + public function hasRoleDraft(): bool + { + return $this->roleDraft instanceof RoleDraft; + } +} + +class_alias(BeforeCreateRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforeCreateRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforeCreateRoleEvent.php b/src/contracts/Repository/Events/Role/BeforeCreateRoleEvent.php new file mode 100644 index 0000000000..779f219e2d --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforeCreateRoleEvent.php @@ -0,0 +1,54 @@ +roleCreateStruct = $roleCreateStruct; + } + + public function getRoleCreateStruct(): RoleCreateStruct + { + return $this->roleCreateStruct; + } + + public function getRoleDraft(): RoleDraft + { + if (!$this->hasRoleDraft()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasRoleDraft() or set it using setRoleDraft() before you call the getter.', RoleDraft::class)); + } + + return $this->roleDraft; + } + + public function setRoleDraft(?RoleDraft $roleDraft): void + { + $this->roleDraft = $roleDraft; + } + + public function hasRoleDraft(): bool + { + return $this->roleDraft instanceof RoleDraft; + } +} + +class_alias(BeforeCreateRoleEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforeCreateRoleEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforeDeletePolicyEvent.php b/src/contracts/Repository/Events/Role/BeforeDeletePolicyEvent.php new file mode 100644 index 0000000000..0396a2544a --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforeDeletePolicyEvent.php @@ -0,0 +1,30 @@ +policy = $policy; + } + + public function getPolicy(): Policy + { + return $this->policy; + } +} + +class_alias(BeforeDeletePolicyEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforeDeletePolicyEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforeDeleteRoleDraftEvent.php b/src/contracts/Repository/Events/Role/BeforeDeleteRoleDraftEvent.php new file mode 100644 index 0000000000..08353c1c41 --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforeDeleteRoleDraftEvent.php @@ -0,0 +1,30 @@ +roleDraft = $roleDraft; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } +} + +class_alias(BeforeDeleteRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforeDeleteRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforeDeleteRoleEvent.php b/src/contracts/Repository/Events/Role/BeforeDeleteRoleEvent.php new file mode 100644 index 0000000000..8eaa962331 --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforeDeleteRoleEvent.php @@ -0,0 +1,30 @@ +role = $role; + } + + public function getRole(): Role + { + return $this->role; + } +} + +class_alias(BeforeDeleteRoleEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforeDeleteRoleEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforePublishRoleDraftEvent.php b/src/contracts/Repository/Events/Role/BeforePublishRoleDraftEvent.php new file mode 100644 index 0000000000..0cdde2af79 --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforePublishRoleDraftEvent.php @@ -0,0 +1,30 @@ +roleDraft = $roleDraft; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } +} + +class_alias(BeforePublishRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforePublishRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforeRemovePolicyByRoleDraftEvent.php b/src/contracts/Repository/Events/Role/BeforeRemovePolicyByRoleDraftEvent.php new file mode 100644 index 0000000000..6ba8bc875f --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforeRemovePolicyByRoleDraftEvent.php @@ -0,0 +1,63 @@ +roleDraft = $roleDraft; + $this->policyDraft = $policyDraft; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } + + public function getPolicyDraft(): PolicyDraft + { + return $this->policyDraft; + } + + public function getUpdatedRoleDraft(): RoleDraft + { + if (!$this->hasUpdatedRoleDraft()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedRoleDraft() or set it using setUpdatedRoleDraft() before you call the getter.', RoleDraft::class)); + } + + return $this->updatedRoleDraft; + } + + public function setUpdatedRoleDraft(?RoleDraft $updatedRoleDraft): void + { + $this->updatedRoleDraft = $updatedRoleDraft; + } + + public function hasUpdatedRoleDraft(): bool + { + return $this->updatedRoleDraft instanceof RoleDraft; + } +} + +class_alias(BeforeRemovePolicyByRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforeRemovePolicyByRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforeRemoveRoleAssignmentEvent.php b/src/contracts/Repository/Events/Role/BeforeRemoveRoleAssignmentEvent.php new file mode 100644 index 0000000000..eef46ac408 --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforeRemoveRoleAssignmentEvent.php @@ -0,0 +1,30 @@ +roleAssignment = $roleAssignment; + } + + public function getRoleAssignment(): RoleAssignment + { + return $this->roleAssignment; + } +} + +class_alias(BeforeRemoveRoleAssignmentEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforeRemoveRoleAssignmentEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforeUpdatePolicyByRoleDraftEvent.php b/src/contracts/Repository/Events/Role/BeforeUpdatePolicyByRoleDraftEvent.php new file mode 100644 index 0000000000..9899d269fb --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforeUpdatePolicyByRoleDraftEvent.php @@ -0,0 +1,73 @@ +roleDraft = $roleDraft; + $this->policy = $policy; + $this->policyUpdateStruct = $policyUpdateStruct; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } + + public function getPolicy(): PolicyDraft + { + return $this->policy; + } + + public function getPolicyUpdateStruct(): PolicyUpdateStruct + { + return $this->policyUpdateStruct; + } + + public function getUpdatedPolicyDraft(): PolicyDraft + { + if (!$this->hasUpdatedPolicyDraft()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedPolicyDraft() or set it using setUpdatedPolicyDraft() before you call the getter.', PolicyDraft::class)); + } + + return $this->updatedPolicyDraft; + } + + public function setUpdatedPolicyDraft(?PolicyDraft $updatedPolicyDraft): void + { + $this->updatedPolicyDraft = $updatedPolicyDraft; + } + + public function hasUpdatedPolicyDraft(): bool + { + return $this->updatedPolicyDraft instanceof PolicyDraft; + } +} + +class_alias(BeforeUpdatePolicyByRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforeUpdatePolicyByRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Role/BeforeUpdateRoleDraftEvent.php b/src/contracts/Repository/Events/Role/BeforeUpdateRoleDraftEvent.php new file mode 100644 index 0000000000..5522f56ce1 --- /dev/null +++ b/src/contracts/Repository/Events/Role/BeforeUpdateRoleDraftEvent.php @@ -0,0 +1,63 @@ +roleDraft = $roleDraft; + $this->roleUpdateStruct = $roleUpdateStruct; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } + + public function getRoleUpdateStruct(): RoleUpdateStruct + { + return $this->roleUpdateStruct; + } + + public function getUpdatedRoleDraft(): RoleDraft + { + if (!$this->hasUpdatedRoleDraft()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedRoleDraft() or set it using setUpdatedRoleDraft() before you call the getter.', RoleDraft::class)); + } + + return $this->updatedRoleDraft; + } + + public function setUpdatedRoleDraft(?RoleDraft $updatedRoleDraft): void + { + $this->updatedRoleDraft = $updatedRoleDraft; + } + + public function hasUpdatedRoleDraft(): bool + { + return $this->updatedRoleDraft instanceof RoleDraft; + } +} + +class_alias(BeforeUpdateRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\BeforeUpdateRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Role/CopyRoleEvent.php b/src/contracts/Repository/Events/Role/CopyRoleEvent.php new file mode 100644 index 0000000000..9eeedd480d --- /dev/null +++ b/src/contracts/Repository/Events/Role/CopyRoleEvent.php @@ -0,0 +1,52 @@ +copiedRole = $copiedRole; + $this->role = $role; + $this->roleCopyStruct = $roleCopyStruct; + } + + public function getRole(): Role + { + return $this->role; + } + + public function getCopiedRole(): Role + { + return $this->copiedRole; + } + + public function getRoleCopyStruct(): RoleCopyStruct + { + return $this->roleCopyStruct; + } +} + +class_alias(CopyRoleEvent::class, 'eZ\Publish\API\Repository\Events\Role\CopyRoleEvent'); diff --git a/src/contracts/Repository/Events/Role/CreateRoleDraftEvent.php b/src/contracts/Repository/Events/Role/CreateRoleDraftEvent.php new file mode 100644 index 0000000000..b7b258e268 --- /dev/null +++ b/src/contracts/Repository/Events/Role/CreateRoleDraftEvent.php @@ -0,0 +1,42 @@ +role = $role; + $this->roleDraft = $roleDraft; + } + + public function getRole(): Role + { + return $this->role; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } +} + +class_alias(CreateRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\CreateRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Role/CreateRoleEvent.php b/src/contracts/Repository/Events/Role/CreateRoleEvent.php new file mode 100644 index 0000000000..5d912cb6d4 --- /dev/null +++ b/src/contracts/Repository/Events/Role/CreateRoleEvent.php @@ -0,0 +1,42 @@ +roleCreateStruct = $roleCreateStruct; + $this->roleDraft = $roleDraft; + } + + public function getRoleCreateStruct(): RoleCreateStruct + { + return $this->roleCreateStruct; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } +} + +class_alias(CreateRoleEvent::class, 'eZ\Publish\API\Repository\Events\Role\CreateRoleEvent'); diff --git a/src/contracts/Repository/Events/Role/DeletePolicyEvent.php b/src/contracts/Repository/Events/Role/DeletePolicyEvent.php new file mode 100644 index 0000000000..ae4afd5416 --- /dev/null +++ b/src/contracts/Repository/Events/Role/DeletePolicyEvent.php @@ -0,0 +1,31 @@ +policy = $policy; + } + + public function getPolicy(): Policy + { + return $this->policy; + } +} + +class_alias(DeletePolicyEvent::class, 'eZ\Publish\API\Repository\Events\Role\DeletePolicyEvent'); diff --git a/src/contracts/Repository/Events/Role/DeleteRoleDraftEvent.php b/src/contracts/Repository/Events/Role/DeleteRoleDraftEvent.php new file mode 100644 index 0000000000..ae0d31c339 --- /dev/null +++ b/src/contracts/Repository/Events/Role/DeleteRoleDraftEvent.php @@ -0,0 +1,31 @@ +roleDraft = $roleDraft; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } +} + +class_alias(DeleteRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\DeleteRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Role/DeleteRoleEvent.php b/src/contracts/Repository/Events/Role/DeleteRoleEvent.php new file mode 100644 index 0000000000..88cf49b18a --- /dev/null +++ b/src/contracts/Repository/Events/Role/DeleteRoleEvent.php @@ -0,0 +1,31 @@ +role = $role; + } + + public function getRole(): Role + { + return $this->role; + } +} + +class_alias(DeleteRoleEvent::class, 'eZ\Publish\API\Repository\Events\Role\DeleteRoleEvent'); diff --git a/src/contracts/Repository/Events/Role/PublishRoleDraftEvent.php b/src/contracts/Repository/Events/Role/PublishRoleDraftEvent.php new file mode 100644 index 0000000000..99fafd2123 --- /dev/null +++ b/src/contracts/Repository/Events/Role/PublishRoleDraftEvent.php @@ -0,0 +1,31 @@ +roleDraft = $roleDraft; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } +} + +class_alias(PublishRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\PublishRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Role/RemovePolicyByRoleDraftEvent.php b/src/contracts/Repository/Events/Role/RemovePolicyByRoleDraftEvent.php new file mode 100644 index 0000000000..bacdf040a7 --- /dev/null +++ b/src/contracts/Repository/Events/Role/RemovePolicyByRoleDraftEvent.php @@ -0,0 +1,52 @@ +roleDraft = $roleDraft; + $this->policyDraft = $policyDraft; + $this->updatedRoleDraft = $updatedRoleDraft; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } + + public function getPolicyDraft(): PolicyDraft + { + return $this->policyDraft; + } + + public function getUpdatedRoleDraft(): RoleDraft + { + return $this->updatedRoleDraft; + } +} + +class_alias(RemovePolicyByRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\RemovePolicyByRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Role/RemoveRoleAssignmentEvent.php b/src/contracts/Repository/Events/Role/RemoveRoleAssignmentEvent.php new file mode 100644 index 0000000000..8cb8208638 --- /dev/null +++ b/src/contracts/Repository/Events/Role/RemoveRoleAssignmentEvent.php @@ -0,0 +1,31 @@ +roleAssignment = $roleAssignment; + } + + public function getRoleAssignment(): RoleAssignment + { + return $this->roleAssignment; + } +} + +class_alias(RemoveRoleAssignmentEvent::class, 'eZ\Publish\API\Repository\Events\Role\RemoveRoleAssignmentEvent'); diff --git a/src/contracts/Repository/Events/Role/UpdatePolicyByRoleDraftEvent.php b/src/contracts/Repository/Events/Role/UpdatePolicyByRoleDraftEvent.php new file mode 100644 index 0000000000..294558812d --- /dev/null +++ b/src/contracts/Repository/Events/Role/UpdatePolicyByRoleDraftEvent.php @@ -0,0 +1,63 @@ +roleDraft = $roleDraft; + $this->policy = $policy; + $this->policyUpdateStruct = $policyUpdateStruct; + $this->updatedPolicyDraft = $updatedPolicyDraft; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } + + public function getPolicy(): PolicyDraft + { + return $this->policy; + } + + public function getPolicyUpdateStruct(): PolicyUpdateStruct + { + return $this->policyUpdateStruct; + } + + public function getUpdatedPolicyDraft(): PolicyDraft + { + return $this->updatedPolicyDraft; + } +} + +class_alias(UpdatePolicyByRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\UpdatePolicyByRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Role/UpdateRoleDraftEvent.php b/src/contracts/Repository/Events/Role/UpdateRoleDraftEvent.php new file mode 100644 index 0000000000..c4fb732f94 --- /dev/null +++ b/src/contracts/Repository/Events/Role/UpdateRoleDraftEvent.php @@ -0,0 +1,52 @@ +roleDraft = $roleDraft; + $this->roleUpdateStruct = $roleUpdateStruct; + $this->updatedRoleDraft = $updatedRoleDraft; + } + + public function getRoleDraft(): RoleDraft + { + return $this->roleDraft; + } + + public function getRoleUpdateStruct(): RoleUpdateStruct + { + return $this->roleUpdateStruct; + } + + public function getUpdatedRoleDraft(): RoleDraft + { + return $this->updatedRoleDraft; + } +} + +class_alias(UpdateRoleDraftEvent::class, 'eZ\Publish\API\Repository\Events\Role\UpdateRoleDraftEvent'); diff --git a/src/contracts/Repository/Events/Section/AssignSectionEvent.php b/src/contracts/Repository/Events/Section/AssignSectionEvent.php new file mode 100644 index 0000000000..5e0c078502 --- /dev/null +++ b/src/contracts/Repository/Events/Section/AssignSectionEvent.php @@ -0,0 +1,42 @@ +contentInfo = $contentInfo; + $this->section = $section; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getSection(): Section + { + return $this->section; + } +} + +class_alias(AssignSectionEvent::class, 'eZ\Publish\API\Repository\Events\Section\AssignSectionEvent'); diff --git a/src/contracts/Repository/Events/Section/AssignSectionToSubtreeEvent.php b/src/contracts/Repository/Events/Section/AssignSectionToSubtreeEvent.php new file mode 100644 index 0000000000..b85bfa5713 --- /dev/null +++ b/src/contracts/Repository/Events/Section/AssignSectionToSubtreeEvent.php @@ -0,0 +1,42 @@ +location = $location; + $this->section = $section; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getSection(): Section + { + return $this->section; + } +} + +class_alias(AssignSectionToSubtreeEvent::class, 'eZ\Publish\API\Repository\Events\Section\AssignSectionToSubtreeEvent'); diff --git a/src/contracts/Repository/Events/Section/BeforeAssignSectionEvent.php b/src/contracts/Repository/Events/Section/BeforeAssignSectionEvent.php new file mode 100644 index 0000000000..d42b68b1a2 --- /dev/null +++ b/src/contracts/Repository/Events/Section/BeforeAssignSectionEvent.php @@ -0,0 +1,40 @@ +contentInfo = $contentInfo; + $this->section = $section; + } + + public function getContentInfo(): ContentInfo + { + return $this->contentInfo; + } + + public function getSection(): Section + { + return $this->section; + } +} + +class_alias(BeforeAssignSectionEvent::class, 'eZ\Publish\API\Repository\Events\Section\BeforeAssignSectionEvent'); diff --git a/src/contracts/Repository/Events/Section/BeforeAssignSectionToSubtreeEvent.php b/src/contracts/Repository/Events/Section/BeforeAssignSectionToSubtreeEvent.php new file mode 100644 index 0000000000..b9e2610af0 --- /dev/null +++ b/src/contracts/Repository/Events/Section/BeforeAssignSectionToSubtreeEvent.php @@ -0,0 +1,40 @@ +location = $location; + $this->section = $section; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getSection(): Section + { + return $this->section; + } +} + +class_alias(BeforeAssignSectionToSubtreeEvent::class, 'eZ\Publish\API\Repository\Events\Section\BeforeAssignSectionToSubtreeEvent'); diff --git a/src/contracts/Repository/Events/Section/BeforeCreateSectionEvent.php b/src/contracts/Repository/Events/Section/BeforeCreateSectionEvent.php new file mode 100644 index 0000000000..ee9b71dcfd --- /dev/null +++ b/src/contracts/Repository/Events/Section/BeforeCreateSectionEvent.php @@ -0,0 +1,54 @@ +sectionCreateStruct = $sectionCreateStruct; + } + + public function getSectionCreateStruct(): SectionCreateStruct + { + return $this->sectionCreateStruct; + } + + public function getSection(): Section + { + if (!$this->hasSection()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasSection() or set it using setSection() before you call the getter.', Section::class)); + } + + return $this->section; + } + + public function setSection(?Section $section): void + { + $this->section = $section; + } + + public function hasSection(): bool + { + return $this->section instanceof Section; + } +} + +class_alias(BeforeCreateSectionEvent::class, 'eZ\Publish\API\Repository\Events\Section\BeforeCreateSectionEvent'); diff --git a/src/contracts/Repository/Events/Section/BeforeDeleteSectionEvent.php b/src/contracts/Repository/Events/Section/BeforeDeleteSectionEvent.php new file mode 100644 index 0000000000..41bb5a3b0b --- /dev/null +++ b/src/contracts/Repository/Events/Section/BeforeDeleteSectionEvent.php @@ -0,0 +1,30 @@ +section = $section; + } + + public function getSection(): Section + { + return $this->section; + } +} + +class_alias(BeforeDeleteSectionEvent::class, 'eZ\Publish\API\Repository\Events\Section\BeforeDeleteSectionEvent'); diff --git a/src/contracts/Repository/Events/Section/BeforeUpdateSectionEvent.php b/src/contracts/Repository/Events/Section/BeforeUpdateSectionEvent.php new file mode 100644 index 0000000000..00af4e1d38 --- /dev/null +++ b/src/contracts/Repository/Events/Section/BeforeUpdateSectionEvent.php @@ -0,0 +1,63 @@ +section = $section; + $this->sectionUpdateStruct = $sectionUpdateStruct; + } + + public function getSection(): Section + { + return $this->section; + } + + public function getSectionUpdateStruct(): SectionUpdateStruct + { + return $this->sectionUpdateStruct; + } + + public function getUpdatedSection(): Section + { + if (!$this->hasUpdatedSection()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedSection() or set it using setUpdatedSection() before you call the getter.', Section::class)); + } + + return $this->updatedSection; + } + + public function setUpdatedSection(?Section $updatedSection): void + { + $this->updatedSection = $updatedSection; + } + + public function hasUpdatedSection(): bool + { + return $this->updatedSection instanceof Section; + } +} + +class_alias(BeforeUpdateSectionEvent::class, 'eZ\Publish\API\Repository\Events\Section\BeforeUpdateSectionEvent'); diff --git a/src/contracts/Repository/Events/Section/CreateSectionEvent.php b/src/contracts/Repository/Events/Section/CreateSectionEvent.php new file mode 100644 index 0000000000..f87e872cd0 --- /dev/null +++ b/src/contracts/Repository/Events/Section/CreateSectionEvent.php @@ -0,0 +1,42 @@ +sectionCreateStruct = $sectionCreateStruct; + $this->section = $section; + } + + public function getSectionCreateStruct(): SectionCreateStruct + { + return $this->sectionCreateStruct; + } + + public function getSection(): Section + { + return $this->section; + } +} + +class_alias(CreateSectionEvent::class, 'eZ\Publish\API\Repository\Events\Section\CreateSectionEvent'); diff --git a/src/contracts/Repository/Events/Section/DeleteSectionEvent.php b/src/contracts/Repository/Events/Section/DeleteSectionEvent.php new file mode 100644 index 0000000000..420314a188 --- /dev/null +++ b/src/contracts/Repository/Events/Section/DeleteSectionEvent.php @@ -0,0 +1,31 @@ +section = $section; + } + + public function getSection(): Section + { + return $this->section; + } +} + +class_alias(DeleteSectionEvent::class, 'eZ\Publish\API\Repository\Events\Section\DeleteSectionEvent'); diff --git a/src/contracts/Repository/Events/Section/UpdateSectionEvent.php b/src/contracts/Repository/Events/Section/UpdateSectionEvent.php new file mode 100644 index 0000000000..2368fcb9b4 --- /dev/null +++ b/src/contracts/Repository/Events/Section/UpdateSectionEvent.php @@ -0,0 +1,52 @@ +section = $section; + $this->sectionUpdateStruct = $sectionUpdateStruct; + $this->updatedSection = $updatedSection; + } + + public function getSection(): Section + { + return $this->section; + } + + public function getSectionUpdateStruct(): SectionUpdateStruct + { + return $this->sectionUpdateStruct; + } + + public function getUpdatedSection(): Section + { + return $this->updatedSection; + } +} + +class_alias(UpdateSectionEvent::class, 'eZ\Publish\API\Repository\Events\Section\UpdateSectionEvent'); diff --git a/src/contracts/Repository/Events/Setting/BeforeCreateSettingEvent.php b/src/contracts/Repository/Events/Setting/BeforeCreateSettingEvent.php new file mode 100644 index 0000000000..58bab2c7b2 --- /dev/null +++ b/src/contracts/Repository/Events/Setting/BeforeCreateSettingEvent.php @@ -0,0 +1,54 @@ +settingCreateStruct = $settingCreateStruct; + } + + public function getSettingCreateStruct(): SettingCreateStruct + { + return $this->settingCreateStruct; + } + + public function getSetting(): Setting + { + if (!$this->hasSetting()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasSetting() or set it using setSetting() before you call the getter.', Setting::class)); + } + + return $this->setting; + } + + public function setSetting(?Setting $setting): void + { + $this->setting = $setting; + } + + public function hasSetting(): bool + { + return $this->setting instanceof Setting; + } +} + +class_alias(BeforeCreateSettingEvent::class, 'eZ\Publish\API\Repository\Events\Setting\BeforeCreateSettingEvent'); diff --git a/src/contracts/Repository/Events/Setting/BeforeDeleteSettingEvent.php b/src/contracts/Repository/Events/Setting/BeforeDeleteSettingEvent.php new file mode 100644 index 0000000000..ab347b4675 --- /dev/null +++ b/src/contracts/Repository/Events/Setting/BeforeDeleteSettingEvent.php @@ -0,0 +1,30 @@ +setting = $setting; + } + + public function getSetting(): Setting + { + return $this->setting; + } +} + +class_alias(BeforeDeleteSettingEvent::class, 'eZ\Publish\API\Repository\Events\Setting\BeforeDeleteSettingEvent'); diff --git a/src/contracts/Repository/Events/Setting/BeforeUpdateSettingEvent.php b/src/contracts/Repository/Events/Setting/BeforeUpdateSettingEvent.php new file mode 100644 index 0000000000..07668054f2 --- /dev/null +++ b/src/contracts/Repository/Events/Setting/BeforeUpdateSettingEvent.php @@ -0,0 +1,63 @@ +setting = $setting; + $this->settingUpdateStruct = $settingUpdateStruct; + } + + public function getSetting(): Setting + { + return $this->setting; + } + + public function getSettingUpdateStruct(): SettingUpdateStruct + { + return $this->settingUpdateStruct; + } + + public function getUpdatedSetting(): Setting + { + if (!$this->hasUpdatedSetting()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedSetting() or set it using setUpdatedSetting() before you call the getter.', Setting::class)); + } + + return $this->updatedSetting; + } + + public function setUpdatedSetting(?Setting $updatedSetting): void + { + $this->updatedSetting = $updatedSetting; + } + + public function hasUpdatedSetting(): bool + { + return $this->updatedSetting instanceof Setting; + } +} + +class_alias(BeforeUpdateSettingEvent::class, 'eZ\Publish\API\Repository\Events\Setting\BeforeUpdateSettingEvent'); diff --git a/src/contracts/Repository/Events/Setting/CreateSettingEvent.php b/src/contracts/Repository/Events/Setting/CreateSettingEvent.php new file mode 100644 index 0000000000..bbdb870a7b --- /dev/null +++ b/src/contracts/Repository/Events/Setting/CreateSettingEvent.php @@ -0,0 +1,42 @@ +setting = $setting; + $this->settingCreateStruct = $settingCreateStruct; + } + + public function getSetting(): Setting + { + return $this->setting; + } + + public function getSettingCreateStruct(): SettingCreateStruct + { + return $this->settingCreateStruct; + } +} + +class_alias(CreateSettingEvent::class, 'eZ\Publish\API\Repository\Events\Setting\CreateSettingEvent'); diff --git a/src/contracts/Repository/Events/Setting/DeleteSettingEvent.php b/src/contracts/Repository/Events/Setting/DeleteSettingEvent.php new file mode 100644 index 0000000000..9fde763eb5 --- /dev/null +++ b/src/contracts/Repository/Events/Setting/DeleteSettingEvent.php @@ -0,0 +1,30 @@ +setting = $setting; + } + + public function getSetting(): Setting + { + return $this->setting; + } +} + +class_alias(DeleteSettingEvent::class, 'eZ\Publish\API\Repository\Events\Setting\DeleteSettingEvent'); diff --git a/src/contracts/Repository/Events/Setting/UpdateSettingEvent.php b/src/contracts/Repository/Events/Setting/UpdateSettingEvent.php new file mode 100644 index 0000000000..58f587aa68 --- /dev/null +++ b/src/contracts/Repository/Events/Setting/UpdateSettingEvent.php @@ -0,0 +1,52 @@ +updatedSetting = $updatedSetting; + $this->setting = $setting; + $this->settingUpdateStruct = $settingUpdateStruct; + } + + public function getUpdatedSetting(): Setting + { + return $this->updatedSetting; + } + + public function getSetting(): Setting + { + return $this->setting; + } + + public function getSettingUpdateStruct(): SettingUpdateStruct + { + return $this->settingUpdateStruct; + } +} + +class_alias(UpdateSettingEvent::class, 'eZ\Publish\API\Repository\Events\Setting\UpdateSettingEvent'); diff --git a/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php new file mode 100644 index 0000000000..cb18bd54bd --- /dev/null +++ b/src/contracts/Repository/Events/Token/BeforeCheckTokenEvent.php @@ -0,0 +1,70 @@ +tokenType = $tokenType; + $this->token = $token; + $this->identifier = $identifier; + } + + public function getResult(): bool + { + if (!$this->hasResult()) { + throw new UnexpectedValueException( + 'Return value is not set.' . PHP_EOL + . 'Check hasResult() or set it using setResult() before you call the getter.' + ); + } + + return $this->result; + } + + public function setResult(?bool $result): void + { + $this->result = $result; + } + + public function hasResult(): bool + { + return $this->result !== null; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getToken(): string + { + return $this->token; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } +} diff --git a/src/contracts/Repository/Events/Token/BeforeDeleteTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeDeleteTokenEvent.php new file mode 100644 index 0000000000..0656b688bb --- /dev/null +++ b/src/contracts/Repository/Events/Token/BeforeDeleteTokenEvent.php @@ -0,0 +1,27 @@ +token = $token; + } + + public function getToken(): Token + { + return $this->token; + } +} diff --git a/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php new file mode 100644 index 0000000000..06d0fe1f88 --- /dev/null +++ b/src/contracts/Repository/Events/Token/BeforeGenerateTokenEvent.php @@ -0,0 +1,90 @@ +tokenType = $type; + $this->ttl = $ttl; + $this->identifier = $identifier; + $this->tokenLength = $tokenLength; + $this->tokenGenerator = $tokenGenerator; + } + + public function getToken(): Token + { + if (!$this->hasToken()) { + throw new UnexpectedValueException( + 'Return value is not set.' . PHP_EOL + . 'Check hasToken() or set it using setToken() before you call the getter.', + ); + } + + return $this->token; + } + + public function setToken(?Token $token): void + { + $this->token = $token; + } + + public function hasToken(): bool + { + return $this->token instanceof Token; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } + + public function getTtl(): int + { + return $this->ttl; + } + + public function getTokenLength(): int + { + return $this->tokenLength; + } + + public function getTokenGenerator(): ?TokenGeneratorInterface + { + return $this->tokenGenerator; + } +} diff --git a/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php new file mode 100644 index 0000000000..517b8bc496 --- /dev/null +++ b/src/contracts/Repository/Events/Token/BeforeGetTokenEvent.php @@ -0,0 +1,71 @@ +tokenType = $tokenType; + $this->token = $token; + $this->identifier = $identifier; + } + + public function getResult(): Token + { + if (!$this->hasResult()) { + throw new UnexpectedValueException( + 'Return value is not set.' . PHP_EOL + . 'Check hasResult() or set it using setResult() before you call the getter.' + ); + } + + return $this->result; + } + + public function setResult(?Token $result): void + { + $this->result = $result; + } + + public function hasResult(): bool + { + return $this->result instanceof Token; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getToken(): string + { + return $this->token; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } +} diff --git a/src/contracts/Repository/Events/Token/BeforeRevokeTokenByIdentifierEvent.php b/src/contracts/Repository/Events/Token/BeforeRevokeTokenByIdentifierEvent.php new file mode 100644 index 0000000000..9f36f5bc1b --- /dev/null +++ b/src/contracts/Repository/Events/Token/BeforeRevokeTokenByIdentifierEvent.php @@ -0,0 +1,34 @@ +tokenType = $tokenType; + $this->identifier = $identifier; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } +} diff --git a/src/contracts/Repository/Events/Token/BeforeRevokeTokenEvent.php b/src/contracts/Repository/Events/Token/BeforeRevokeTokenEvent.php new file mode 100644 index 0000000000..6c965d82eb --- /dev/null +++ b/src/contracts/Repository/Events/Token/BeforeRevokeTokenEvent.php @@ -0,0 +1,27 @@ +token = $token; + } + + public function getToken(): Token + { + return $this->token; + } +} diff --git a/src/contracts/Repository/Events/Token/CheckTokenEvent.php b/src/contracts/Repository/Events/Token/CheckTokenEvent.php new file mode 100644 index 0000000000..0918a52b5e --- /dev/null +++ b/src/contracts/Repository/Events/Token/CheckTokenEvent.php @@ -0,0 +1,54 @@ +result = $result; + $this->tokenType = $tokenType; + $this->token = $token; + $this->identifier = $identifier; + } + + public function getResult(): bool + { + return $this->result; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getToken(): string + { + return $this->token; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } +} diff --git a/src/contracts/Repository/Events/Token/DeleteTokenEvent.php b/src/contracts/Repository/Events/Token/DeleteTokenEvent.php new file mode 100644 index 0000000000..e93d4e34c8 --- /dev/null +++ b/src/contracts/Repository/Events/Token/DeleteTokenEvent.php @@ -0,0 +1,27 @@ +token = $token; + } + + public function getToken(): Token + { + return $this->token; + } +} diff --git a/src/contracts/Repository/Events/Token/GenerateTokenEvent.php b/src/contracts/Repository/Events/Token/GenerateTokenEvent.php new file mode 100644 index 0000000000..6c60905cdf --- /dev/null +++ b/src/contracts/Repository/Events/Token/GenerateTokenEvent.php @@ -0,0 +1,74 @@ +token = $token; + $this->tokenType = $tokenType; + $this->identifier = $identifier; + $this->ttl = $ttl; + $this->tokenLength = $tokenLength; + $this->tokenGenerator = $tokenGenerator; + } + + public function getToken(): Token + { + return $this->token; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } + + public function getTtl(): int + { + return $this->ttl; + } + + public function getTokenLength(): int + { + return $this->tokenLength; + } + + public function getTokenGenerator(): ?TokenGeneratorInterface + { + return $this->tokenGenerator; + } +} diff --git a/src/contracts/Repository/Events/Token/GetTokenEvent.php b/src/contracts/Repository/Events/Token/GetTokenEvent.php new file mode 100644 index 0000000000..9e3ebc0842 --- /dev/null +++ b/src/contracts/Repository/Events/Token/GetTokenEvent.php @@ -0,0 +1,55 @@ +result = $result; + $this->tokenType = $tokenType; + $this->token = $token; + $this->identifier = $identifier; + } + + public function getResult(): Token + { + return $this->result; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getToken(): string + { + return $this->token; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } +} diff --git a/src/contracts/Repository/Events/Token/RevokeTokenByIdentifierEvent.php b/src/contracts/Repository/Events/Token/RevokeTokenByIdentifierEvent.php new file mode 100644 index 0000000000..2eac5ab2de --- /dev/null +++ b/src/contracts/Repository/Events/Token/RevokeTokenByIdentifierEvent.php @@ -0,0 +1,34 @@ +tokenType = $tokenType; + $this->identifier = $identifier; + } + + public function getTokenType(): string + { + return $this->tokenType; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } +} diff --git a/src/contracts/Repository/Events/Token/RevokeTokenEvent.php b/src/contracts/Repository/Events/Token/RevokeTokenEvent.php new file mode 100644 index 0000000000..a2ec0c016c --- /dev/null +++ b/src/contracts/Repository/Events/Token/RevokeTokenEvent.php @@ -0,0 +1,27 @@ +token = $token; + } + + public function getToken(): Token + { + return $this->token; + } +} diff --git a/src/contracts/Repository/Events/Trash/BeforeDeleteTrashItemEvent.php b/src/contracts/Repository/Events/Trash/BeforeDeleteTrashItemEvent.php new file mode 100644 index 0000000000..76b99bc23c --- /dev/null +++ b/src/contracts/Repository/Events/Trash/BeforeDeleteTrashItemEvent.php @@ -0,0 +1,54 @@ +trashItem = $trashItem; + } + + public function getTrashItem(): TrashItem + { + return $this->trashItem; + } + + public function getResult(): TrashItemDeleteResult + { + if (!$this->hasResult()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasResult() or set it using setResult() before you call the getter.', TrashItemDeleteResult::class)); + } + + return $this->result; + } + + public function setResult(?TrashItemDeleteResult $result): void + { + $this->result = $result; + } + + public function hasResult(): bool + { + return $this->result instanceof TrashItemDeleteResult; + } +} + +class_alias(BeforeDeleteTrashItemEvent::class, 'eZ\Publish\API\Repository\Events\Trash\BeforeDeleteTrashItemEvent'); diff --git a/src/contracts/Repository/Events/Trash/BeforeEmptyTrashEvent.php b/src/contracts/Repository/Events/Trash/BeforeEmptyTrashEvent.php new file mode 100644 index 0000000000..95c0851a19 --- /dev/null +++ b/src/contracts/Repository/Events/Trash/BeforeEmptyTrashEvent.php @@ -0,0 +1,44 @@ +hasResultList()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasResultList() or set it using setResultList() before you call the getter.', TrashItemDeleteResultList::class)); + } + + return $this->resultList; + } + + public function setResultList(?TrashItemDeleteResultList $resultList): void + { + $this->resultList = $resultList; + } + + public function hasResultList(): bool + { + return $this->resultList instanceof TrashItemDeleteResultList; + } +} + +class_alias(BeforeEmptyTrashEvent::class, 'eZ\Publish\API\Repository\Events\Trash\BeforeEmptyTrashEvent'); diff --git a/src/contracts/Repository/Events/Trash/BeforeRecoverEvent.php b/src/contracts/Repository/Events/Trash/BeforeRecoverEvent.php new file mode 100644 index 0000000000..9a10f276c2 --- /dev/null +++ b/src/contracts/Repository/Events/Trash/BeforeRecoverEvent.php @@ -0,0 +1,63 @@ +trashItem = $trashItem; + $this->newParentLocation = $newParentLocation; + } + + public function getTrashItem(): TrashItem + { + return $this->trashItem; + } + + public function getNewParentLocation(): ?Location + { + return $this->newParentLocation; + } + + public function getLocation(): Location + { + if (!$this->hasLocation()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasLocation() or set it using setLocation() before you call the getter.', Location::class)); + } + + return $this->location; + } + + public function setLocation(?Location $location): void + { + $this->location = $location; + } + + public function hasLocation(): bool + { + return $this->location instanceof Location; + } +} + +class_alias(BeforeRecoverEvent::class, 'eZ\Publish\API\Repository\Events\Trash\BeforeRecoverEvent'); diff --git a/src/contracts/Repository/Events/Trash/BeforeTrashEvent.php b/src/contracts/Repository/Events/Trash/BeforeTrashEvent.php new file mode 100644 index 0000000000..89e6fd6261 --- /dev/null +++ b/src/contracts/Repository/Events/Trash/BeforeTrashEvent.php @@ -0,0 +1,69 @@ +location = $location; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getResult(): ?TrashItem + { + if (!$this->isResultSet()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s (or null). Check isResultSet() or set it using setResult() before you call the getter.', TrashItem::class)); + } + + return $this->result; + } + + public function setResult(?TrashItem $result): void + { + $this->result = $result; + $this->resultSet = true; + } + + public function hasTrashItem(): bool + { + return $this->result instanceof TrashItem; + } + + public function resetResult(): void + { + $this->result = null; + $this->resultSet = false; + } + + public function isResultSet(): bool + { + return $this->resultSet; + } +} + +class_alias(BeforeTrashEvent::class, 'eZ\Publish\API\Repository\Events\Trash\BeforeTrashEvent'); diff --git a/src/contracts/Repository/Events/Trash/DeleteTrashItemEvent.php b/src/contracts/Repository/Events/Trash/DeleteTrashItemEvent.php new file mode 100644 index 0000000000..73d667d4fe --- /dev/null +++ b/src/contracts/Repository/Events/Trash/DeleteTrashItemEvent.php @@ -0,0 +1,42 @@ +trashItem = $trashItem; + $this->result = $result; + } + + public function getTrashItem(): TrashItem + { + return $this->trashItem; + } + + public function getResult(): TrashItemDeleteResult + { + return $this->result; + } +} + +class_alias(DeleteTrashItemEvent::class, 'eZ\Publish\API\Repository\Events\Trash\DeleteTrashItemEvent'); diff --git a/src/contracts/Repository/Events/Trash/EmptyTrashEvent.php b/src/contracts/Repository/Events/Trash/EmptyTrashEvent.php new file mode 100644 index 0000000000..b9c6f093a6 --- /dev/null +++ b/src/contracts/Repository/Events/Trash/EmptyTrashEvent.php @@ -0,0 +1,30 @@ +resultList = $resultList; + } + + public function getResultList(): TrashItemDeleteResultList + { + return $this->resultList; + } +} + +class_alias(EmptyTrashEvent::class, 'eZ\Publish\API\Repository\Events\Trash\EmptyTrashEvent'); diff --git a/src/contracts/Repository/Events/Trash/RecoverEvent.php b/src/contracts/Repository/Events/Trash/RecoverEvent.php new file mode 100644 index 0000000000..3b56497732 --- /dev/null +++ b/src/contracts/Repository/Events/Trash/RecoverEvent.php @@ -0,0 +1,52 @@ +trashItem = $trashItem; + $this->newParentLocation = $newParentLocation; + $this->location = $location; + } + + public function getTrashItem(): TrashItem + { + return $this->trashItem; + } + + public function getNewParentLocation(): ?Location + { + return $this->newParentLocation; + } + + public function getLocation(): Location + { + return $this->location; + } +} + +class_alias(RecoverEvent::class, 'eZ\Publish\API\Repository\Events\Trash\RecoverEvent'); diff --git a/src/contracts/Repository/Events/Trash/TrashEvent.php b/src/contracts/Repository/Events/Trash/TrashEvent.php new file mode 100644 index 0000000000..a8d87b9675 --- /dev/null +++ b/src/contracts/Repository/Events/Trash/TrashEvent.php @@ -0,0 +1,42 @@ +location = $location; + $this->trashItem = $trashItem; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getTrashItem(): ?TrashItem + { + return $this->trashItem; + } +} + +class_alias(TrashEvent::class, 'eZ\Publish\API\Repository\Events\Trash\TrashEvent'); diff --git a/src/contracts/Repository/Events/URL/BeforeUpdateUrlEvent.php b/src/contracts/Repository/Events/URL/BeforeUpdateUrlEvent.php new file mode 100644 index 0000000000..36b71f7213 --- /dev/null +++ b/src/contracts/Repository/Events/URL/BeforeUpdateUrlEvent.php @@ -0,0 +1,63 @@ +url = $url; + $this->struct = $struct; + } + + public function getUrl(): URL + { + return $this->url; + } + + public function getStruct(): URLUpdateStruct + { + return $this->struct; + } + + public function getUpdatedUrl(): URL + { + if (!$this->hasUpdatedUrl()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedUrl() or set it using setUpdatedUrl() before you call the getter.', URL::class)); + } + + return $this->updatedUrl; + } + + public function setUpdatedUrl(?URL $updatedUrl): void + { + $this->updatedUrl = $updatedUrl; + } + + public function hasUpdatedUrl(): bool + { + return $this->updatedUrl instanceof URL; + } +} + +class_alias(BeforeUpdateUrlEvent::class, 'eZ\Publish\API\Repository\Events\URL\BeforeUpdateUrlEvent'); diff --git a/src/contracts/Repository/Events/URL/UpdateUrlEvent.php b/src/contracts/Repository/Events/URL/UpdateUrlEvent.php new file mode 100644 index 0000000000..05de8cd6c6 --- /dev/null +++ b/src/contracts/Repository/Events/URL/UpdateUrlEvent.php @@ -0,0 +1,52 @@ +url = $url; + $this->struct = $struct; + $this->updatedUrl = $updatedUrl; + } + + public function getUrl(): URL + { + return $this->url; + } + + public function getStruct(): URLUpdateStruct + { + return $this->struct; + } + + public function getUpdatedUrl(): URL + { + return $this->updatedUrl; + } +} + +class_alias(UpdateUrlEvent::class, 'eZ\Publish\API\Repository\Events\URL\UpdateUrlEvent'); diff --git a/eZ/Publish/API/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php b/src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php similarity index 81% rename from eZ/Publish/API/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php rename to src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php index 9aa8dbd9b5..0cdc836a30 100644 --- a/eZ/Publish/API/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php +++ b/src/contracts/Repository/Events/URLAlias/BeforeCreateGlobalUrlAliasEvent.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Events\URLAlias; +namespace Ibexa\Contracts\Core\Repository\Events\URLAlias; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use eZ\Publish\SPI\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; use UnexpectedValueException; final class BeforeCreateGlobalUrlAliasEvent extends BeforeEvent @@ -24,7 +24,7 @@ final class BeforeCreateGlobalUrlAliasEvent extends BeforeEvent private $alwaysAvailable; - /** @var \eZ\Publish\API\Repository\Values\Content\URLAlias|null */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias|null */ private $urlAlias; public function __construct($resource, $path, $languageCode, $forwarding, $alwaysAvailable) @@ -80,3 +80,5 @@ public function hasUrlAlias(): bool return $this->urlAlias instanceof URLAlias; } } + +class_alias(BeforeCreateGlobalUrlAliasEvent::class, 'eZ\Publish\API\Repository\Events\URLAlias\BeforeCreateGlobalUrlAliasEvent'); diff --git a/eZ/Publish/API/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php b/src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php similarity index 77% rename from eZ/Publish/API/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php rename to src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php index 95d6b2f254..8bca8af375 100644 --- a/eZ/Publish/API/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php +++ b/src/contracts/Repository/Events/URLAlias/BeforeCreateUrlAliasEvent.php @@ -6,16 +6,16 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Events\URLAlias; +namespace Ibexa\Contracts\Core\Repository\Events\URLAlias; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use eZ\Publish\SPI\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; use UnexpectedValueException; final class BeforeCreateUrlAliasEvent extends BeforeEvent { - /** @var \eZ\Publish\API\Repository\Values\Content\Location */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location */ private $location; private $path; @@ -26,7 +26,7 @@ final class BeforeCreateUrlAliasEvent extends BeforeEvent private $alwaysAvailable; - /** @var \eZ\Publish\API\Repository\Values\Content\URLAlias|null */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias|null */ private $urlAlias; public function __construct(Location $location, $path, $languageCode, $forwarding, $alwaysAvailable) @@ -82,3 +82,5 @@ public function hasUrlAlias(): bool return $this->urlAlias instanceof URLAlias; } } + +class_alias(BeforeCreateUrlAliasEvent::class, 'eZ\Publish\API\Repository\Events\URLAlias\BeforeCreateUrlAliasEvent'); diff --git a/src/contracts/Repository/Events/URLAlias/BeforeRefreshSystemUrlAliasesForLocationEvent.php b/src/contracts/Repository/Events/URLAlias/BeforeRefreshSystemUrlAliasesForLocationEvent.php new file mode 100644 index 0000000000..aaecf069cf --- /dev/null +++ b/src/contracts/Repository/Events/URLAlias/BeforeRefreshSystemUrlAliasesForLocationEvent.php @@ -0,0 +1,30 @@ +location = $location; + } + + public function getLocation(): Location + { + return $this->location; + } +} + +class_alias(BeforeRefreshSystemUrlAliasesForLocationEvent::class, 'eZ\Publish\API\Repository\Events\URLAlias\BeforeRefreshSystemUrlAliasesForLocationEvent'); diff --git a/src/contracts/Repository/Events/URLAlias/BeforeRemoveAliasesEvent.php b/src/contracts/Repository/Events/URLAlias/BeforeRemoveAliasesEvent.php new file mode 100644 index 0000000000..74f04418b6 --- /dev/null +++ b/src/contracts/Repository/Events/URLAlias/BeforeRemoveAliasesEvent.php @@ -0,0 +1,29 @@ +aliasList = $aliasList; + } + + public function getAliasList(): array + { + return $this->aliasList; + } +} + +class_alias(BeforeRemoveAliasesEvent::class, 'eZ\Publish\API\Repository\Events\URLAlias\BeforeRemoveAliasesEvent'); diff --git a/eZ/Publish/API/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php b/src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php similarity index 78% rename from eZ/Publish/API/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php rename to src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php index 4164913ba2..d7496da705 100644 --- a/eZ/Publish/API/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php +++ b/src/contracts/Repository/Events/URLAlias/CreateGlobalUrlAliasEvent.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Events\URLAlias; +namespace Ibexa\Contracts\Core\Repository\Events\URLAlias; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use eZ\Publish\SPI\Repository\Event\AfterEvent; +use Ibexa\Contracts\Core\Repository\Event\AfterEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; final class CreateGlobalUrlAliasEvent extends AfterEvent { @@ -23,7 +23,7 @@ final class CreateGlobalUrlAliasEvent extends AfterEvent private $alwaysAvailable; - /** @var \eZ\Publish\API\Repository\Values\Content\URLAlias */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias */ private $urlAlias; public function __construct( @@ -72,3 +72,5 @@ public function getUrlAlias(): URLAlias return $this->urlAlias; } } + +class_alias(CreateGlobalUrlAliasEvent::class, 'eZ\Publish\API\Repository\Events\URLAlias\CreateGlobalUrlAliasEvent'); diff --git a/src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php b/src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php new file mode 100644 index 0000000000..e3de71a35c --- /dev/null +++ b/src/contracts/Repository/Events/URLAlias/CreateUrlAliasEvent.php @@ -0,0 +1,78 @@ +location = $location; + $this->path = $path; + $this->languageCode = $languageCode; + $this->forwarding = $forwarding; + $this->alwaysAvailable = $alwaysAvailable; + $this->urlAlias = $urlAlias; + } + + public function getLocation(): Location + { + return $this->location; + } + + public function getPath() + { + return $this->path; + } + + public function getLanguageCode() + { + return $this->languageCode; + } + + public function getForwarding() + { + return $this->forwarding; + } + + public function getAlwaysAvailable() + { + return $this->alwaysAvailable; + } + + public function getUrlAlias(): URLAlias + { + return $this->urlAlias; + } +} + +class_alias(CreateUrlAliasEvent::class, 'eZ\Publish\API\Repository\Events\URLAlias\CreateUrlAliasEvent'); diff --git a/src/contracts/Repository/Events/URLAlias/RefreshSystemUrlAliasesForLocationEvent.php b/src/contracts/Repository/Events/URLAlias/RefreshSystemUrlAliasesForLocationEvent.php new file mode 100644 index 0000000000..912833f6f7 --- /dev/null +++ b/src/contracts/Repository/Events/URLAlias/RefreshSystemUrlAliasesForLocationEvent.php @@ -0,0 +1,30 @@ +location = $location; + } + + public function getLocation(): Location + { + return $this->location; + } +} + +class_alias(RefreshSystemUrlAliasesForLocationEvent::class, 'eZ\Publish\API\Repository\Events\URLAlias\RefreshSystemUrlAliasesForLocationEvent'); diff --git a/src/contracts/Repository/Events/URLAlias/RemoveAliasesEvent.php b/src/contracts/Repository/Events/URLAlias/RemoveAliasesEvent.php new file mode 100644 index 0000000000..535d24469b --- /dev/null +++ b/src/contracts/Repository/Events/URLAlias/RemoveAliasesEvent.php @@ -0,0 +1,30 @@ +aliasList = $aliasList; + } + + public function getAliasList(): array + { + return $this->aliasList; + } +} + +class_alias(RemoveAliasesEvent::class, 'eZ\Publish\API\Repository\Events\URLAlias\RemoveAliasesEvent'); diff --git a/eZ/Publish/API/Repository/Events/URLWildcard/BeforeCreateEvent.php b/src/contracts/Repository/Events/URLWildcard/BeforeCreateEvent.php similarity index 79% rename from eZ/Publish/API/Repository/Events/URLWildcard/BeforeCreateEvent.php rename to src/contracts/Repository/Events/URLWildcard/BeforeCreateEvent.php index 889568826e..8da1253adb 100644 --- a/eZ/Publish/API/Repository/Events/URLWildcard/BeforeCreateEvent.php +++ b/src/contracts/Repository/Events/URLWildcard/BeforeCreateEvent.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Events\URLWildcard; +namespace Ibexa\Contracts\Core\Repository\Events\URLWildcard; -use eZ\Publish\API\Repository\Values\Content\URLWildcard; -use eZ\Publish\SPI\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard; use UnexpectedValueException; final class BeforeCreateEvent extends BeforeEvent @@ -20,7 +20,7 @@ final class BeforeCreateEvent extends BeforeEvent private $forward; - /** @var \eZ\Publish\API\Repository\Values\Content\URLWildcard|null */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard|null */ private $urlWildcard; public function __construct($sourceUrl, $destinationUrl, $forward) @@ -64,3 +64,5 @@ public function hasUrlWildcard(): bool return $this->urlWildcard instanceof URLWildcard; } } + +class_alias(BeforeCreateEvent::class, 'eZ\Publish\API\Repository\Events\URLWildcard\BeforeCreateEvent'); diff --git a/src/contracts/Repository/Events/URLWildcard/BeforeRemoveEvent.php b/src/contracts/Repository/Events/URLWildcard/BeforeRemoveEvent.php new file mode 100644 index 0000000000..a3ae15f78d --- /dev/null +++ b/src/contracts/Repository/Events/URLWildcard/BeforeRemoveEvent.php @@ -0,0 +1,30 @@ +urlWildcard = $urlWildcard; + } + + public function getUrlWildcard(): URLWildcard + { + return $this->urlWildcard; + } +} + +class_alias(BeforeRemoveEvent::class, 'eZ\Publish\API\Repository\Events\URLWildcard\BeforeRemoveEvent'); diff --git a/src/contracts/Repository/Events/URLWildcard/BeforeTranslateEvent.php b/src/contracts/Repository/Events/URLWildcard/BeforeTranslateEvent.php new file mode 100644 index 0000000000..01f484cacc --- /dev/null +++ b/src/contracts/Repository/Events/URLWildcard/BeforeTranslateEvent.php @@ -0,0 +1,52 @@ +url = $url; + } + + public function getUrl() + { + return $this->url; + } + + public function getResult(): URLWildcardTranslationResult + { + if (!$this->hasResult()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasResult() or set it using setResult() before you call the getter.', URLWildcardTranslationResult::class)); + } + + return $this->result; + } + + public function setResult(?URLWildcardTranslationResult $result): void + { + $this->result = $result; + } + + public function hasResult(): bool + { + return $this->result instanceof URLWildcardTranslationResult; + } +} + +class_alias(BeforeTranslateEvent::class, 'eZ\Publish\API\Repository\Events\URLWildcard\BeforeTranslateEvent'); diff --git a/src/contracts/Repository/Events/URLWildcard/BeforeUpdateEvent.php b/src/contracts/Repository/Events/URLWildcard/BeforeUpdateEvent.php new file mode 100644 index 0000000000..a58d96fb38 --- /dev/null +++ b/src/contracts/Repository/Events/URLWildcard/BeforeUpdateEvent.php @@ -0,0 +1,42 @@ +urlWildcard = $urlWildcard; + $this->updateStruct = $updateStruct; + } + + public function getUrlWildcard(): URLWildcard + { + return $this->urlWildcard; + } + + public function getUpdateStruct(): URLWildcardUpdateStruct + { + return $this->updateStruct; + } +} + +class_alias(BeforeUpdateEvent::class, 'eZ\Publish\API\Repository\Events\URLWildcard\BeforeUpdateEvent'); diff --git a/src/contracts/Repository/Events/URLWildcard/CreateEvent.php b/src/contracts/Repository/Events/URLWildcard/CreateEvent.php new file mode 100644 index 0000000000..b724c71639 --- /dev/null +++ b/src/contracts/Repository/Events/URLWildcard/CreateEvent.php @@ -0,0 +1,58 @@ +sourceUrl = $sourceUrl; + $this->destinationUrl = $destinationUrl; + $this->forward = $forward; + $this->urlWildcard = $urlWildcard; + } + + public function getSourceUrl() + { + return $this->sourceUrl; + } + + public function getDestinationUrl() + { + return $this->destinationUrl; + } + + public function getForward() + { + return $this->forward; + } + + public function getUrlWildcard(): URLWildcard + { + return $this->urlWildcard; + } +} + +class_alias(CreateEvent::class, 'eZ\Publish\API\Repository\Events\URLWildcard\CreateEvent'); diff --git a/src/contracts/Repository/Events/URLWildcard/RemoveEvent.php b/src/contracts/Repository/Events/URLWildcard/RemoveEvent.php new file mode 100644 index 0000000000..e6b539567d --- /dev/null +++ b/src/contracts/Repository/Events/URLWildcard/RemoveEvent.php @@ -0,0 +1,31 @@ +urlWildcard = $urlWildcard; + } + + public function getUrlWildcard(): URLWildcard + { + return $this->urlWildcard; + } +} + +class_alias(RemoveEvent::class, 'eZ\Publish\API\Repository\Events\URLWildcard\RemoveEvent'); diff --git a/src/contracts/Repository/Events/URLWildcard/TranslateEvent.php b/src/contracts/Repository/Events/URLWildcard/TranslateEvent.php new file mode 100644 index 0000000000..e959e66bad --- /dev/null +++ b/src/contracts/Repository/Events/URLWildcard/TranslateEvent.php @@ -0,0 +1,40 @@ +url = $url; + $this->result = $result; + } + + public function getUrl() + { + return $this->url; + } + + public function getResult(): URLWildcardTranslationResult + { + return $this->result; + } +} + +class_alias(TranslateEvent::class, 'eZ\Publish\API\Repository\Events\URLWildcard\TranslateEvent'); diff --git a/src/contracts/Repository/Events/URLWildcard/UpdateEvent.php b/src/contracts/Repository/Events/URLWildcard/UpdateEvent.php new file mode 100644 index 0000000000..8b41cb3d59 --- /dev/null +++ b/src/contracts/Repository/Events/URLWildcard/UpdateEvent.php @@ -0,0 +1,42 @@ +urlWildcard = $urlWildcard; + $this->updateStruct = $updateStruct; + } + + public function getUrlWildcard(): URLWildcard + { + return $this->urlWildcard; + } + + public function getUpdateStruct(): URLWildcardUpdateStruct + { + return $this->updateStruct; + } +} + +class_alias(UpdateEvent::class, 'eZ\Publish\API\Repository\Events\URLWildcard\UpdateEvent'); diff --git a/src/contracts/Repository/Events/User/AssignUserToUserGroupEvent.php b/src/contracts/Repository/Events/User/AssignUserToUserGroupEvent.php new file mode 100644 index 0000000000..a640cd8f3f --- /dev/null +++ b/src/contracts/Repository/Events/User/AssignUserToUserGroupEvent.php @@ -0,0 +1,42 @@ +user = $user; + $this->userGroup = $userGroup; + } + + public function getUser(): User + { + return $this->user; + } + + public function getUserGroup(): UserGroup + { + return $this->userGroup; + } +} + +class_alias(AssignUserToUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\User\AssignUserToUserGroupEvent'); diff --git a/src/contracts/Repository/Events/User/BeforeAssignUserToUserGroupEvent.php b/src/contracts/Repository/Events/User/BeforeAssignUserToUserGroupEvent.php new file mode 100644 index 0000000000..66ac63b513 --- /dev/null +++ b/src/contracts/Repository/Events/User/BeforeAssignUserToUserGroupEvent.php @@ -0,0 +1,40 @@ +user = $user; + $this->userGroup = $userGroup; + } + + public function getUser(): User + { + return $this->user; + } + + public function getUserGroup(): UserGroup + { + return $this->userGroup; + } +} + +class_alias(BeforeAssignUserToUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\User\BeforeAssignUserToUserGroupEvent'); diff --git a/src/contracts/Repository/Events/User/BeforeCreateUserEvent.php b/src/contracts/Repository/Events/User/BeforeCreateUserEvent.php new file mode 100644 index 0000000000..71fc90d243 --- /dev/null +++ b/src/contracts/Repository/Events/User/BeforeCreateUserEvent.php @@ -0,0 +1,63 @@ +userCreateStruct = $userCreateStruct; + $this->parentGroups = $parentGroups; + } + + public function getUserCreateStruct(): UserCreateStruct + { + return $this->userCreateStruct; + } + + public function getParentGroups(): array + { + return $this->parentGroups; + } + + public function getUser(): User + { + if (!$this->hasUser()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUser() or set it using setUser() before you call the getter.', User::class)); + } + + return $this->user; + } + + public function setUser(?User $user): void + { + $this->user = $user; + } + + public function hasUser(): bool + { + return $this->user instanceof User; + } +} + +class_alias(BeforeCreateUserEvent::class, 'eZ\Publish\API\Repository\Events\User\BeforeCreateUserEvent'); diff --git a/src/contracts/Repository/Events/User/BeforeCreateUserGroupEvent.php b/src/contracts/Repository/Events/User/BeforeCreateUserGroupEvent.php new file mode 100644 index 0000000000..911be9b545 --- /dev/null +++ b/src/contracts/Repository/Events/User/BeforeCreateUserGroupEvent.php @@ -0,0 +1,63 @@ +userGroupCreateStruct = $userGroupCreateStruct; + $this->parentGroup = $parentGroup; + } + + public function getUserGroupCreateStruct(): UserGroupCreateStruct + { + return $this->userGroupCreateStruct; + } + + public function getParentGroup(): UserGroup + { + return $this->parentGroup; + } + + public function getUserGroup(): UserGroup + { + if (!$this->hasUserGroup()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUserGroup() or set it using setUserGroup() before you call the getter.', UserGroup::class)); + } + + return $this->userGroup; + } + + public function setUserGroup(?UserGroup $userGroup): void + { + $this->userGroup = $userGroup; + } + + public function hasUserGroup(): bool + { + return $this->userGroup instanceof UserGroup; + } +} + +class_alias(BeforeCreateUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\User\BeforeCreateUserGroupEvent'); diff --git a/eZ/Publish/API/Repository/Events/User/BeforeDeleteUserEvent.php b/src/contracts/Repository/Events/User/BeforeDeleteUserEvent.php similarity index 75% rename from eZ/Publish/API/Repository/Events/User/BeforeDeleteUserEvent.php rename to src/contracts/Repository/Events/User/BeforeDeleteUserEvent.php index 3332090f00..f557d152dc 100644 --- a/eZ/Publish/API/Repository/Events/User/BeforeDeleteUserEvent.php +++ b/src/contracts/Repository/Events/User/BeforeDeleteUserEvent.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Events\User; +namespace Ibexa\Contracts\Core\Repository\Events\User; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\SPI\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Values\User\User; use UnexpectedValueException; final class BeforeDeleteUserEvent extends BeforeEvent { - /** @var \eZ\Publish\API\Repository\Values\User\User */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\User */ private $user; /** @var array|null */ @@ -49,3 +49,5 @@ public function hasLocations(): bool return is_array($this->locations); } } + +class_alias(BeforeDeleteUserEvent::class, 'eZ\Publish\API\Repository\Events\User\BeforeDeleteUserEvent'); diff --git a/eZ/Publish/API/Repository/Events/User/BeforeDeleteUserGroupEvent.php b/src/contracts/Repository/Events/User/BeforeDeleteUserGroupEvent.php similarity index 75% rename from eZ/Publish/API/Repository/Events/User/BeforeDeleteUserGroupEvent.php rename to src/contracts/Repository/Events/User/BeforeDeleteUserGroupEvent.php index 141e513b37..04e79d3402 100644 --- a/eZ/Publish/API/Repository/Events/User/BeforeDeleteUserGroupEvent.php +++ b/src/contracts/Repository/Events/User/BeforeDeleteUserGroupEvent.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Events\User; +namespace Ibexa\Contracts\Core\Repository\Events\User; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\SPI\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroup; use UnexpectedValueException; final class BeforeDeleteUserGroupEvent extends BeforeEvent { - /** @var \eZ\Publish\API\Repository\Values\User\UserGroup */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\UserGroup */ private $userGroup; /** @var array|null */ @@ -49,3 +49,5 @@ public function hasLocations(): bool return is_array($this->locations); } } + +class_alias(BeforeDeleteUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\User\BeforeDeleteUserGroupEvent'); diff --git a/src/contracts/Repository/Events/User/BeforeMoveUserGroupEvent.php b/src/contracts/Repository/Events/User/BeforeMoveUserGroupEvent.php new file mode 100644 index 0000000000..010dcbb68f --- /dev/null +++ b/src/contracts/Repository/Events/User/BeforeMoveUserGroupEvent.php @@ -0,0 +1,39 @@ +userGroup = $userGroup; + $this->newParent = $newParent; + } + + public function getUserGroup(): UserGroup + { + return $this->userGroup; + } + + public function getNewParent(): UserGroup + { + return $this->newParent; + } +} + +class_alias(BeforeMoveUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\User\BeforeMoveUserGroupEvent'); diff --git a/src/contracts/Repository/Events/User/BeforeUnAssignUserFromUserGroupEvent.php b/src/contracts/Repository/Events/User/BeforeUnAssignUserFromUserGroupEvent.php new file mode 100644 index 0000000000..86ba559534 --- /dev/null +++ b/src/contracts/Repository/Events/User/BeforeUnAssignUserFromUserGroupEvent.php @@ -0,0 +1,40 @@ +user = $user; + $this->userGroup = $userGroup; + } + + public function getUser(): User + { + return $this->user; + } + + public function getUserGroup(): UserGroup + { + return $this->userGroup; + } +} + +class_alias(BeforeUnAssignUserFromUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\User\BeforeUnAssignUserFromUserGroupEvent'); diff --git a/src/contracts/Repository/Events/User/BeforeUpdateUserEvent.php b/src/contracts/Repository/Events/User/BeforeUpdateUserEvent.php new file mode 100644 index 0000000000..f5404329ea --- /dev/null +++ b/src/contracts/Repository/Events/User/BeforeUpdateUserEvent.php @@ -0,0 +1,63 @@ +user = $user; + $this->userUpdateStruct = $userUpdateStruct; + } + + public function getUser(): User + { + return $this->user; + } + + public function getUserUpdateStruct(): UserUpdateStruct + { + return $this->userUpdateStruct; + } + + public function getUpdatedUser(): User + { + if (!$this->hasUpdatedUser()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedUser() or set it using setUpdatedUser() before you call the getter.', User::class)); + } + + return $this->updatedUser; + } + + public function setUpdatedUser(?User $updatedUser): void + { + $this->updatedUser = $updatedUser; + } + + public function hasUpdatedUser(): bool + { + return $this->updatedUser instanceof User; + } +} + +class_alias(BeforeUpdateUserEvent::class, 'eZ\Publish\API\Repository\Events\User\BeforeUpdateUserEvent'); diff --git a/src/contracts/Repository/Events/User/BeforeUpdateUserGroupEvent.php b/src/contracts/Repository/Events/User/BeforeUpdateUserGroupEvent.php new file mode 100644 index 0000000000..c6822d9adc --- /dev/null +++ b/src/contracts/Repository/Events/User/BeforeUpdateUserGroupEvent.php @@ -0,0 +1,63 @@ +userGroup = $userGroup; + $this->userGroupUpdateStruct = $userGroupUpdateStruct; + } + + public function getUserGroup(): UserGroup + { + return $this->userGroup; + } + + public function getUserGroupUpdateStruct(): UserGroupUpdateStruct + { + return $this->userGroupUpdateStruct; + } + + public function getUpdatedUserGroup(): UserGroup + { + if (!$this->hasUpdatedUserGroup()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedUserGroup() or set it using setUpdatedUserGroup() before you call the getter.', UserGroup::class)); + } + + return $this->updatedUserGroup; + } + + public function setUpdatedUserGroup(?UserGroup $updatedUserGroup): void + { + $this->updatedUserGroup = $updatedUserGroup; + } + + public function hasUpdatedUserGroup(): bool + { + return $this->updatedUserGroup instanceof UserGroup; + } +} + +class_alias(BeforeUpdateUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\User\BeforeUpdateUserGroupEvent'); diff --git a/eZ/Publish/API/Repository/Events/User/BeforeUpdateUserPasswordEvent.php b/src/contracts/Repository/Events/User/BeforeUpdateUserPasswordEvent.php similarity index 75% rename from eZ/Publish/API/Repository/Events/User/BeforeUpdateUserPasswordEvent.php rename to src/contracts/Repository/Events/User/BeforeUpdateUserPasswordEvent.php index 49aab5a018..d5beeca70a 100644 --- a/eZ/Publish/API/Repository/Events/User/BeforeUpdateUserPasswordEvent.php +++ b/src/contracts/Repository/Events/User/BeforeUpdateUserPasswordEvent.php @@ -6,21 +6,21 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Events\User; +namespace Ibexa\Contracts\Core\Repository\Events\User; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\SPI\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Event\BeforeEvent; +use Ibexa\Contracts\Core\Repository\Values\User\User; use UnexpectedValueException; final class BeforeUpdateUserPasswordEvent extends BeforeEvent { - /** @var \eZ\Publish\API\Repository\Values\User\User */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\User */ private $user; /** @var string */ private $newPassword; - /** @var \eZ\Publish\API\Repository\Values\User\User|null */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\User|null */ private $updatedUser; public function __construct(User $user, string $newPassword) @@ -58,3 +58,5 @@ public function hasUpdatedUser(): bool return $this->updatedUser instanceof User; } } + +class_alias(BeforeUpdateUserPasswordEvent::class, 'eZ\Publish\API\Repository\Events\User\BeforeUpdateUserPasswordEvent'); diff --git a/src/contracts/Repository/Events/User/BeforeUpdateUserTokenEvent.php b/src/contracts/Repository/Events/User/BeforeUpdateUserTokenEvent.php new file mode 100644 index 0000000000..a0a262e606 --- /dev/null +++ b/src/contracts/Repository/Events/User/BeforeUpdateUserTokenEvent.php @@ -0,0 +1,63 @@ +user = $user; + $this->userTokenUpdateStruct = $userTokenUpdateStruct; + } + + public function getUser(): User + { + return $this->user; + } + + public function getUserTokenUpdateStruct(): UserTokenUpdateStruct + { + return $this->userTokenUpdateStruct; + } + + public function getUpdatedUser(): User + { + if (!$this->hasUpdatedUser()) { + throw new UnexpectedValueException(sprintf('Return value is not set or not of type %s. Check hasUpdatedUser() or set it using setUpdatedUser() before you call the getter.', User::class)); + } + + return $this->updatedUser; + } + + public function setUpdatedUser(?User $updatedUser): void + { + $this->updatedUser = $updatedUser; + } + + public function hasUpdatedUser(): bool + { + return $this->updatedUser instanceof User; + } +} + +class_alias(BeforeUpdateUserTokenEvent::class, 'eZ\Publish\API\Repository\Events\User\BeforeUpdateUserTokenEvent'); diff --git a/src/contracts/Repository/Events/User/CreateUserEvent.php b/src/contracts/Repository/Events/User/CreateUserEvent.php new file mode 100644 index 0000000000..517ffc4327 --- /dev/null +++ b/src/contracts/Repository/Events/User/CreateUserEvent.php @@ -0,0 +1,52 @@ +userCreateStruct = $userCreateStruct; + $this->parentGroups = $parentGroups; + $this->user = $user; + } + + public function getUserCreateStruct(): UserCreateStruct + { + return $this->userCreateStruct; + } + + public function getParentGroups(): array + { + return $this->parentGroups; + } + + public function getUser(): User + { + return $this->user; + } +} + +class_alias(CreateUserEvent::class, 'eZ\Publish\API\Repository\Events\User\CreateUserEvent'); diff --git a/src/contracts/Repository/Events/User/CreateUserGroupEvent.php b/src/contracts/Repository/Events/User/CreateUserGroupEvent.php new file mode 100644 index 0000000000..4fcfe9536b --- /dev/null +++ b/src/contracts/Repository/Events/User/CreateUserGroupEvent.php @@ -0,0 +1,52 @@ +userGroupCreateStruct = $userGroupCreateStruct; + $this->parentGroup = $parentGroup; + $this->userGroup = $userGroup; + } + + public function getUserGroupCreateStruct(): UserGroupCreateStruct + { + return $this->userGroupCreateStruct; + } + + public function getParentGroup(): UserGroup + { + return $this->parentGroup; + } + + public function getUserGroup(): UserGroup + { + return $this->userGroup; + } +} + +class_alias(CreateUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\User\CreateUserGroupEvent'); diff --git a/src/contracts/Repository/Events/User/DeleteUserEvent.php b/src/contracts/Repository/Events/User/DeleteUserEvent.php new file mode 100644 index 0000000000..b9a44523ec --- /dev/null +++ b/src/contracts/Repository/Events/User/DeleteUserEvent.php @@ -0,0 +1,41 @@ +user = $user; + $this->locations = $locations; + } + + public function getUser(): User + { + return $this->user; + } + + public function getLocations(): array + { + return $this->locations; + } +} + +class_alias(DeleteUserEvent::class, 'eZ\Publish\API\Repository\Events\User\DeleteUserEvent'); diff --git a/src/contracts/Repository/Events/User/DeleteUserGroupEvent.php b/src/contracts/Repository/Events/User/DeleteUserGroupEvent.php new file mode 100644 index 0000000000..a86db43307 --- /dev/null +++ b/src/contracts/Repository/Events/User/DeleteUserGroupEvent.php @@ -0,0 +1,41 @@ +userGroup = $userGroup; + $this->locations = $locations; + } + + public function getUserGroup(): UserGroup + { + return $this->userGroup; + } + + public function getLocations(): array + { + return $this->locations; + } +} + +class_alias(DeleteUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\User\DeleteUserGroupEvent'); diff --git a/src/contracts/Repository/Events/User/MoveUserGroupEvent.php b/src/contracts/Repository/Events/User/MoveUserGroupEvent.php new file mode 100644 index 0000000000..7ffbaa6147 --- /dev/null +++ b/src/contracts/Repository/Events/User/MoveUserGroupEvent.php @@ -0,0 +1,41 @@ +userGroup = $userGroup; + $this->newParent = $newParent; + } + + public function getUserGroup(): UserGroup + { + return $this->userGroup; + } + + public function getNewParent(): UserGroup + { + return $this->newParent; + } +} + +class_alias(MoveUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\User\MoveUserGroupEvent'); diff --git a/src/contracts/Repository/Events/User/UnAssignUserFromUserGroupEvent.php b/src/contracts/Repository/Events/User/UnAssignUserFromUserGroupEvent.php new file mode 100644 index 0000000000..36c0e38aba --- /dev/null +++ b/src/contracts/Repository/Events/User/UnAssignUserFromUserGroupEvent.php @@ -0,0 +1,42 @@ +user = $user; + $this->userGroup = $userGroup; + } + + public function getUser(): User + { + return $this->user; + } + + public function getUserGroup(): UserGroup + { + return $this->userGroup; + } +} + +class_alias(UnAssignUserFromUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\User\UnAssignUserFromUserGroupEvent'); diff --git a/src/contracts/Repository/Events/User/UpdateUserEvent.php b/src/contracts/Repository/Events/User/UpdateUserEvent.php new file mode 100644 index 0000000000..e6c14152f0 --- /dev/null +++ b/src/contracts/Repository/Events/User/UpdateUserEvent.php @@ -0,0 +1,52 @@ +user = $user; + $this->userUpdateStruct = $userUpdateStruct; + $this->updatedUser = $updatedUser; + } + + public function getUser(): User + { + return $this->user; + } + + public function getUserUpdateStruct(): UserUpdateStruct + { + return $this->userUpdateStruct; + } + + public function getUpdatedUser(): User + { + return $this->updatedUser; + } +} + +class_alias(UpdateUserEvent::class, 'eZ\Publish\API\Repository\Events\User\UpdateUserEvent'); diff --git a/src/contracts/Repository/Events/User/UpdateUserGroupEvent.php b/src/contracts/Repository/Events/User/UpdateUserGroupEvent.php new file mode 100644 index 0000000000..0affad6ad6 --- /dev/null +++ b/src/contracts/Repository/Events/User/UpdateUserGroupEvent.php @@ -0,0 +1,51 @@ +userGroup = $userGroup; + $this->userGroupUpdateStruct = $userGroupUpdateStruct; + $this->updatedUserGroup = $updatedUserGroup; + } + + public function getUserGroup(): UserGroup + { + return $this->userGroup; + } + + public function getUserGroupUpdateStruct(): UserGroupUpdateStruct + { + return $this->userGroupUpdateStruct; + } + + public function getUpdatedUserGroup(): UserGroup + { + return $this->updatedUserGroup; + } +} + +class_alias(UpdateUserGroupEvent::class, 'eZ\Publish\API\Repository\Events\User\UpdateUserGroupEvent'); diff --git a/src/contracts/Repository/Events/User/UpdateUserPasswordEvent.php b/src/contracts/Repository/Events/User/UpdateUserPasswordEvent.php new file mode 100644 index 0000000000..22b8c1f166 --- /dev/null +++ b/src/contracts/Repository/Events/User/UpdateUserPasswordEvent.php @@ -0,0 +1,52 @@ +user = $user; + $this->newPassword = $newPassword; + $this->updatedUser = $updatedUser; + } + + public function getUser(): User + { + return $this->user; + } + + public function getNewPassword(): string + { + return $this->newPassword; + } + + public function getUpdatedUser(): User + { + return $this->updatedUser; + } +} + +class_alias(UpdateUserPasswordEvent::class, 'eZ\Publish\API\Repository\Events\User\UpdateUserPasswordEvent'); diff --git a/src/contracts/Repository/Events/User/UpdateUserTokenEvent.php b/src/contracts/Repository/Events/User/UpdateUserTokenEvent.php new file mode 100644 index 0000000000..6270dfb94a --- /dev/null +++ b/src/contracts/Repository/Events/User/UpdateUserTokenEvent.php @@ -0,0 +1,52 @@ +user = $user; + $this->userTokenUpdateStruct = $userTokenUpdateStruct; + $this->updatedUser = $updatedUser; + } + + public function getUser(): User + { + return $this->user; + } + + public function getUserTokenUpdateStruct(): UserTokenUpdateStruct + { + return $this->userTokenUpdateStruct; + } + + public function getUpdatedUser(): User + { + return $this->updatedUser; + } +} + +class_alias(UpdateUserTokenEvent::class, 'eZ\Publish\API\Repository\Events\User\UpdateUserTokenEvent'); diff --git a/src/contracts/Repository/Events/UserPreference/BeforeSetUserPreferenceEvent.php b/src/contracts/Repository/Events/UserPreference/BeforeSetUserPreferenceEvent.php new file mode 100644 index 0000000000..145b20927f --- /dev/null +++ b/src/contracts/Repository/Events/UserPreference/BeforeSetUserPreferenceEvent.php @@ -0,0 +1,29 @@ +userPreferenceSetStructs = $userPreferenceSetStructs; + } + + public function getUserPreferenceSetStructs(): array + { + return $this->userPreferenceSetStructs; + } +} + +class_alias(BeforeSetUserPreferenceEvent::class, 'eZ\Publish\API\Repository\Events\UserPreference\BeforeSetUserPreferenceEvent'); diff --git a/src/contracts/Repository/Events/UserPreference/SetUserPreferenceEvent.php b/src/contracts/Repository/Events/UserPreference/SetUserPreferenceEvent.php new file mode 100644 index 0000000000..2d8cc367db --- /dev/null +++ b/src/contracts/Repository/Events/UserPreference/SetUserPreferenceEvent.php @@ -0,0 +1,29 @@ +userPreferenceSetStructs = $userPreferenceSetStructs; + } + + public function getUserPreferenceSetStructs(): array + { + return $this->userPreferenceSetStructs; + } +} + +class_alias(SetUserPreferenceEvent::class, 'eZ\Publish\API\Repository\Events\UserPreference\SetUserPreferenceEvent'); diff --git a/src/contracts/Repository/Exceptions/BadStateException.php b/src/contracts/Repository/Exceptions/BadStateException.php new file mode 100644 index 0000000000..93b8ad71f9 --- /dev/null +++ b/src/contracts/Repository/Exceptions/BadStateException.php @@ -0,0 +1,18 @@ + + * array( + * 'stringLength' => array( + * 'minStringLength' => array( + * 'type' => 'int', + * 'default' => 0, + * ), + * 'maxStringLength' => array( + * 'type' => 'int' + * 'default' => null, + * ) + * ), + * ); + * + * + * @return mixed + */ + public function getValidatorConfigurationSchema(); + + /** + * Indicates if the field type supports indexing and sort keys for searching. + * + * @return bool + */ + public function isSearchable(): bool; + + /** + * Indicates if the field definition of this type can appear only once in the same ContentType. + * + * @return bool + */ + public function isSingular(): bool; + + /** + * Indicates if the field definition of this type can be added to a ContentType with Content instances. + * + * @return bool + */ + public function onlyEmptyInstance(): bool; + + /** + * Returns the empty value for this field type. + * + * @return mixed + */ + public function getEmptyValue(); + + /** + * Returns if the given $value is considered empty by the field type. + * + * Usually, only the value returned by {@see FieldType::getEmptyValue()} is + * considered empty but that is not always the case. + * + * Note: This function assumes that $value is valid so this function can only + * be used reliably on $values that came from the API, not from the user. + * + * @param mixed $value + * + * @return bool + */ + public function isEmptyValue($value): bool; + + /** + * Converts an $hash to the Value defined by the field type. + * + * This is the reverse operation to {@see FieldType::toHash()}. + * + * @param mixed $hash + * + * @return mixed + */ + public function fromHash($hash); + + /** + * Converts the given $value into a plain hash format. + * + * @param mixed $value + * + * @return mixed + */ + public function toHash($value); + + /** + * Converts the given $fieldSettings to a simple hash format. + * + * @param mixed $fieldSettings + * + * @return array|scalar|null + */ + public function fieldSettingsToHash($fieldSettings); + + /** + * Converts the given $fieldSettingsHash to field settings of the type. + * + * This is the reverse operation of {@see FieldType::fieldSettingsToHash()}. + * + * @param array|scalar|null $fieldSettingsHash + * + * @return mixed + */ + public function fieldSettingsFromHash($fieldSettingsHash); + + /** + * Converts the given $validatorConfiguration to a simple hash format. + * + * @param mixed $validatorConfiguration + * + * @return array|scalar|null + */ + public function validatorConfigurationToHash($validatorConfiguration); + + /** + * Converts the given $validatorConfigurationHash to a validator + * configuration of the type. + * + * @param array|scalar|null $validatorConfigurationHash + * + * @return mixed + */ + public function validatorConfigurationFromHash($validatorConfigurationHash); + + /** + * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * This methods determines if the given $validatorConfiguration is + * structurally correct and complies to the validator configuration schema as defined in FieldType. + * + * @param mixed $validatorConfiguration + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateValidatorConfiguration($validatorConfiguration): iterable; + + /** + * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * This methods determines if the given $fieldSettings are structurally + * correct and comply to the settings schema as defined in FieldType. + * + * @param mixed $fieldSettings + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateFieldSettings($fieldSettings): iterable; + + /** + * Validates a field value based on the validator configuration in the field definition. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDef The field definition of the field + * @param \Ibexa\Contracts\Core\FieldType\Value $value The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateValue(FieldDefinition $fieldDef, Value $value): iterable; +} + +class_alias(FieldType::class, 'eZ\Publish\API\Repository\FieldType'); diff --git a/src/contracts/Repository/FieldTypeService.php b/src/contracts/Repository/FieldTypeService.php new file mode 100644 index 0000000000..a096857a64 --- /dev/null +++ b/src/contracts/Repository/FieldTypeService.php @@ -0,0 +1,46 @@ +adapter->fetch($this->position, $this->batchSize); } } + +class_alias(BatchIterator::class, 'eZ\Publish\API\Repository\Iterator\BatchIterator'); diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter.php new file mode 100644 index 0000000000..abb3c6e2a8 --- /dev/null +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter.php @@ -0,0 +1,18 @@ +searchService = $searchService; + $this->query = $query; + $this->languageFilter = $languageFilter; + $this->filterOnUserPermissions = $filterOnUserPermissions; + } + + final public function fetch(int $offset, int $limit): Iterator + { + $query = clone $this->query; + $query->offset = $offset; + $query->limit = $limit; + + return $this->executeSearch($query)->getIterator(); + } + + abstract protected function executeSearch(Query $query): SearchResult; +} + +class_alias(AbstractSearchAdapter::class, 'eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter'); diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentFilteringAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentFilteringAdapter.php new file mode 100644 index 0000000000..02305e3995 --- /dev/null +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentFilteringAdapter.php @@ -0,0 +1,43 @@ +contentService = $contentService; + $this->filter = $filter; + $this->languages = $languages; + } + + public function fetch(int $offset, int $limit): Iterator + { + $filter = clone $this->filter; + $filter->sliceBy($limit, $offset); + + return $this->contentService->find($filter, $this->languages)->getIterator(); + } +} + +class_alias(ContentFilteringAdapter::class, 'eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\ContentFilteringAdapter'); diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php new file mode 100644 index 0000000000..86f716cd96 --- /dev/null +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapter.php @@ -0,0 +1,26 @@ +searchService->findContentInfo( + $query, + $this->languageFilter, + $this->filterOnUserPermissions + ); + } +} + +class_alias(ContentInfoSearchAdapter::class, 'eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\ContentInfoSearchAdapter'); diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php new file mode 100644 index 0000000000..790e465e6e --- /dev/null +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapter.php @@ -0,0 +1,26 @@ +searchService->findContent( + $query, + $this->languageFilter, + $this->filterOnUserPermissions + ); + } +} + +class_alias(ContentSearchAdapter::class, 'eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\ContentSearchAdapter'); diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationFilteringAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationFilteringAdapter.php new file mode 100644 index 0000000000..16fba5dc00 --- /dev/null +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationFilteringAdapter.php @@ -0,0 +1,43 @@ +locationService = $locationService; + $this->filter = $filter; + $this->languages = $languages; + } + + public function fetch(int $offset, int $limit): Iterator + { + $filter = clone $this->filter; + $filter->sliceBy($limit, $offset); + + return $this->locationService->find($filter, $this->languages)->getIterator(); + } +} + +class_alias(LocationFilteringAdapter::class, 'eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\LocationFilteringAdapter'); diff --git a/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php b/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php new file mode 100644 index 0000000000..b25c8a750d --- /dev/null +++ b/src/contracts/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapter.php @@ -0,0 +1,37 @@ +searchService->findLocations( + $query, + $this->languageFilter, + $this->filterOnUserPermissions + ); + } +} + +class_alias(LocationSearchAdapter::class, 'eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\LocationSearchAdapter'); diff --git a/src/contracts/Repository/LanguageResolver.php b/src/contracts/Repository/LanguageResolver.php new file mode 100644 index 0000000000..1ff75e985d --- /dev/null +++ b/src/contracts/Repository/LanguageResolver.php @@ -0,0 +1,72 @@ +payload; } } + +class_alias(UnauthorizedListItem::class, 'eZ\Publish\API\Repository\Lists\UnauthorizedListItem'); diff --git a/src/contracts/Repository/LocationService.php b/src/contracts/Repository/LocationService.php new file mode 100644 index 0000000000..124f7201d3 --- /dev/null +++ b/src/contracts/Repository/LocationService.php @@ -0,0 +1,279 @@ + key value map of names for a language code + */ + public function resolveUrlAliasSchema( + Content $content, + ContentType $contentType = null + ): array; + + /** + * @param array> $fieldMap + * @param array $languageCodes + * + * @return array + */ + public function resolveContentNameSchema( + Content $content, + array $fieldMap = [], + array $languageCodes = [], + ContentType $contentType = null + ): array; + + /** + * Returns the real name for a content name pattern. + * + * @param array> $fieldMap + * @param array $languageCodes + * + * @return array + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function resolveNameSchema( + string $nameSchema, + ContentType $contentType, + array $fieldMap, + array $languageCodes + ): array; +} diff --git a/src/contracts/Repository/NameSchema/SchemaIdentifierExtractorInterface.php b/src/contracts/Repository/NameSchema/SchemaIdentifierExtractorInterface.php new file mode 100644 index 0000000000..51cc0d4a46 --- /dev/null +++ b/src/contracts/Repository/NameSchema/SchemaIdentifierExtractorInterface.php @@ -0,0 +1,17 @@ +> + */ + public function extract(string $schemaString): array; +} diff --git a/src/contracts/Repository/NotificationService.php b/src/contracts/Repository/NotificationService.php new file mode 100644 index 0000000000..0079f5e0fc --- /dev/null +++ b/src/contracts/Repository/NotificationService.php @@ -0,0 +1,85 @@ + + * }, + * > + */ + public function hasAccess(string $module, string $function, ?UserReference $userReference = null); + + /** + * Indicates if the current user is allowed to perform an action given by the function on the given + * objects. + * + * Example: canUser( 'content', 'edit', $content, [$location] ); + * This will check edit permission on content given the specific location, if skipped if will check on all + * locations. + * + * Example2: canUser( 'section', 'assign', $content, [$section] ); + * Check if user has access to assign $content to $section. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * + * @param string $module The module, aka controller identifier to check permissions on + * @param string $function The function, aka the controller action to check permissions on + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object The object to check if the user has access to + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[] $targets An array of location, parent or "assignment" value objects + * + * @return bool + */ + public function canUser(string $module, string $function, ValueObject $object, array $targets = []): bool; + + /** + * @param string $module The module, aka controller identifier to check permissions on + * @param string $function The function, aka the controller action to check permissions on + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object The object to check if the user has access to + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[] $targets An array of location, parent or "assignment" value objects + * @param string[] $limitationsIdentifiers An array of Limitations identifiers to filter from all which will pass + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\LookupLimitationResult + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + public function lookupLimitations( + string $module, + string $function, + ValueObject $object, + array $targets = [], + array $limitationsIdentifiers = [] + ): LookupLimitationResult; +} + +class_alias(PermissionResolver::class, 'eZ\Publish\API\Repository\PermissionResolver'); diff --git a/src/contracts/Repository/PermissionService.php b/src/contracts/Repository/PermissionService.php new file mode 100644 index 0000000000..4c12599f2c --- /dev/null +++ b/src/contracts/Repository/PermissionService.php @@ -0,0 +1,15 @@ +sudo(function (Repository $repo) use ($locationId) { + * return $repo->getLocationService()->loadLocation($locationId) + * } + * ); + * + * @template T + * + * @phpstan-param callable(\Ibexa\Contracts\Core\Repository\Repository): T $callback + * + * @param \Ibexa\Contracts\Core\Repository\Repository|null $outerRepository Optional, mostly + * for internal use but allows to specify Repository to pass to closure. + * + * @return T + */ + public function sudo(callable $callback, ?Repository $outerRepository = null); + + /** + * Get Content Service. + * + * Get service object to perform operations on Content objects and it's aggregate members. + * + * @return \Ibexa\Contracts\Core\Repository\ContentService + */ + public function getContentService(): ContentService; + + /** + * Get Content Language Service. + * + * Get service object to perform operations on Content language objects + * + * @return \Ibexa\Contracts\Core\Repository\LanguageService + */ + public function getContentLanguageService(): LanguageService; + + /** + * Get content type Service. + * + * Get service object to perform operations on content type objects and it's aggregate members. + * ( Group, Field & FieldCategory ) + * + * @return \Ibexa\Contracts\Core\Repository\ContentTypeService + */ + public function getContentTypeService(): ContentTypeService; + + /** + * Get Content Location Service. + * + * Get service object to perform operations on Location objects and subtrees + * + * @return \Ibexa\Contracts\Core\Repository\LocationService + */ + public function getLocationService(): LocationService; + + /** + * Get Content Trash service. + * + * Trash service allows to perform operations related to location trash + * (trash/untrash, load/list from trash...) + * + * @return \Ibexa\Contracts\Core\Repository\TrashService + */ + public function getTrashService(): TrashService; + + /** + * Get Content Section Service. + * + * Get Section service that lets you manipulate section objects + * + * @return \Ibexa\Contracts\Core\Repository\SectionService + */ + public function getSectionService(): SectionService; + + /** + * Get Search Service. + * + * Get search service that lets you find content objects + * + * @return \Ibexa\Contracts\Core\Repository\SearchService + */ + public function getSearchService(): SearchService; + + /** + * Get User Service. + * + * Get service object to perform operations on Users and UserGroup + * + * @return \Ibexa\Contracts\Core\Repository\UserService + */ + public function getUserService(): UserService; + + /** + * Get URLAliasService. + * + * @return \Ibexa\Contracts\Core\Repository\URLAliasService + */ + public function getURLAliasService(): URLAliasService; + + /** + * Get URLWildcardService. + * + * @return \Ibexa\Contracts\Core\Repository\URLWildcardService + */ + public function getURLWildcardService(): URLWildcardService; + + /** + * Get ObjectStateService. + * + * @return \Ibexa\Contracts\Core\Repository\ObjectStateService + */ + public function getObjectStateService(): ObjectStateService; + + /** + * Get RoleService. + * + * @return \Ibexa\Contracts\Core\Repository\RoleService + */ + public function getRoleService(): RoleService; + + /** + * Get FieldTypeService. + * + * @return \Ibexa\Contracts\Core\Repository\FieldTypeService + */ + public function getFieldTypeService(): FieldTypeService; + + /** + * Get PermissionResolver. + * + * @return \Ibexa\Contracts\Core\Repository\PermissionResolver + */ + public function getPermissionResolver(): PermissionResolver; + + /** + * Get URLService. + * + * @return \Ibexa\Contracts\Core\Repository\URLService + */ + public function getURLService(): URLService; + + /** + * Get BookmarkService. + * + * @return \Ibexa\Contracts\Core\Repository\BookmarkService + */ + public function getBookmarkService(): BookmarkService; + + /** + * Get NotificationService. + * + * @return \Ibexa\Contracts\Core\Repository\NotificationService + */ + public function getNotificationService(): NotificationService; + + /** + * Get UserPreferenceService. + * + * @return \Ibexa\Contracts\Core\Repository\UserPreferenceService + */ + public function getUserPreferenceService(): UserPreferenceService; + + /** + * Begin transaction. + * + * Begins an transaction, make sure you'll call commit or rollback when done, + * otherwise work will be lost. + */ + public function beginTransaction(): void; + + /** + * Commit transaction. + * + * Commit transaction, or throw exceptions if no transactions has been started. + * + * @throws \RuntimeException If no transaction has been started + */ + public function commit(): void; + + /** + * Rollback transaction. + * + * Rollback transaction, or throw exceptions if no transactions has been started. + * + * @throws \RuntimeException If no transaction has been started + */ + public function rollback(): void; +} + +class_alias(Repository::class, 'eZ\Publish\API\Repository\Repository'); diff --git a/src/contracts/Repository/RoleService.php b/src/contracts/Repository/RoleService.php new file mode 100644 index 0000000000..5332b69cc5 --- /dev/null +++ b/src/contracts/Repository/RoleService.php @@ -0,0 +1,406 @@ +score and SearchResult->maxScore + * properties as well as sort by this if no sort clauses are specified. + * + * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) + */ + public const CAPABILITY_SCORING = 1; + + /** + * Capability flag for facets feature for use with {@see ::supports()}. + * + * Faceted search: https://en.wikipedia.org/wiki/Faceted_search + * + * Note: Even if search engine tells you this is supported, beware: + * - It might not support all facets, by design it will only return facets for facet builders the search engine supports. + * - Some of the faceting features are still work in progress in API and won't be further matured before in 7 .x + * releases + * + * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) + */ + public const CAPABILITY_FACETS = 2; + + /** + * Capability flag for custom fields feature for use with {@see ::supports()}. + * + * Custom fields is the capability for search engines to 1. allow you to extend the search index via plugins to + * generate custom fields, like a different representation (format, ...) of an existing field or similar. And 2. + * allow you on some search criteria to specify this custom field to rather query on that instead of the default + * field generated by the system. + * + * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) + */ + public const CAPABILITY_CUSTOM_FIELDS = 4; + + /** + * Capability flag for spellcheck feature for use with {@see ::supports()}. + * + * Spell check within search capabilities refers to ability to suggest better wordings in fulltext search string. + * + * WARNING: This feature is considered experimental given it is not completely designed yet in terms of how it should + * interact with FullText criterion (singular) which is the most relevant here. Also given how FullText can be part of a more complex criteria it + * might imply a need to more strictly define where users are supposed to place FullText vs other criteria. + * + * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) + */ + public const CAPABILITY_SPELLCHECK = 8; + + /** + * Capability flag for highlight feature for use with {@see ::supports()}. + * + * Highlight in search refers to extracting relevant text from the search index that matches the search result, + * typically returning a chunk of text of a predefined size with matching text highlighted. + * + * WARNING: This feature is considered experimental given it is not completely designed yet in terms of how it should + * interact with hits within rich content of either Ibexa or custom field types. it is also unclear how it should + * hint what part of the highlight is matched. + * + * @internal Maybe it should rather give just matched text and hint of which field (several: one with best score) + * was matched and leave it to field type to render result with that info taking into account. But for now it is + * designed as simple string field, so should be string with for instance `` around matched text. + * + * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) + */ + public const CAPABILITY_HIGHLIGHT = 16; + + /** + * Capability flag for suggest feature for use with {@see ::supports()}. + * + * WARNING: This feature is considered experimental given it is not completely clear what it is supposed to do. Feature + * might be deprecated in the future. + * + * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) + */ + public const CAPABILITY_SUGGEST = 32; + + /** + * Capability flag for advanced fulltext feature for use with {@see ::supports()}. + * + * Advance full text is a feature making to possible by current engine to parse advance full text expressions. + * + * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) (constant added in 6.7.6 and up) + * @see \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\FullText + */ + public const CAPABILITY_ADVANCED_FULLTEXT = 64; + + /** + * Capability flag for aggregation feature for use with {@see ::supports()}. + * + * @since 3.2 eZ Platform 3.2 (ezplatform-kernel 1.2) + */ + public const CAPABILITY_AGGREGATIONS = 128; + + /** + * Finds content objects for the given query. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query + * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. + * Also used to define which field languages are loaded for the returned content. + * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * @param bool $filterOnUserPermissions if true only the objects which the user is allowed to read are returned. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + */ + public function findContent(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult; + + /** + * Finds contentInfo objects for the given query. + * + * This method works just like findContent, however does not load the full Content Objects. This means + * it can be more efficient for use cases where you don't need the full Content. Also including use cases + * where content will be loaded by separate code, like an ESI based sub requests that takes content ID as input. + * + * @since 0.5.4.5 eZ Publish 5.4 (ezpublish-kernel 5.4) + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query + * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. + * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * @param bool $filterOnUserPermissions if true (default) only the objects which is the user allowed to read are returned. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + */ + public function findContentInfo(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult; + + /** + * Performs a query for a single content object. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if criterion is not valid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $filter + * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. + * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function findSingle(Criterion $filter, array $languageFilter = [], bool $filterOnUserPermissions = true): Content; + + /** + * Suggests a list of values for the given prefix. + * + * @param string $prefix + * @param string[] $fieldPaths + * @param int $limit + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $filter + */ + public function suggest(string $prefix, array $fieldPaths = [], int $limit = 10, Criterion $filter = null); + + /** + * Finds Locations for the given query. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query + * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. + * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + */ + public function findLocations(LocationQuery $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult; + + /** + * Query for supported capability of currently configured search engine. + * + * Will return false if search engine does not implement {@see \Ibexa\Contracts\Core\Search\Capable}. + * + * @since 1.12 eZ Platform 1.12 (ezpublish-kernel 6.12) + * + * @param int $capabilityFlag One of CAPABILITY_* constants. + * + * @return bool + */ + public function supports(int $capabilityFlag): bool; +} + +class_alias(SearchService::class, 'eZ\Publish\API\Repository\SearchService'); diff --git a/src/contracts/Repository/SectionService.php b/src/contracts/Repository/SectionService.php new file mode 100644 index 0000000000..5395ddf0cd --- /dev/null +++ b/src/contracts/Repository/SectionService.php @@ -0,0 +1,147 @@ + + * > + * + * @return array Grouped validation errors by field definition ID and language code, in format: + * $returnValue[int $fieldDefinitionId][string $languageCode] = $fieldErrors; + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function validate( + ValueObject $object, + array $context = [], + ?array $fieldIdentifiers = null + ): array; +} + +class_alias(ContentValidator::class, 'eZ\Publish\SPI\Repository\Validator\ContentValidator'); diff --git a/src/contracts/Repository/Values/Bookmark/BookmarkList.php b/src/contracts/Repository/Values/Bookmark/BookmarkList.php new file mode 100644 index 0000000000..e59ebc32b8 --- /dev/null +++ b/src/contracts/Repository/Values/Bookmark/BookmarkList.php @@ -0,0 +1,44 @@ +items); + } +} + +class_alias(BookmarkList::class, 'eZ\Publish\API\Repository\Values\Bookmark\BookmarkList'); diff --git a/src/contracts/Repository/Values/Content/Content.php b/src/contracts/Repository/Values/Content/Content.php new file mode 100644 index 0000000000..5997ade311 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Content.php @@ -0,0 +1,126 @@ +getContentInfo()->getId(); + } + + public function getContentInfo(): ContentInfo + { + return $this->getVersionInfo()->getContentInfo(); + } + + /** + * Returns the VersionInfo for this version. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo + */ + abstract public function getVersionInfo(): VersionInfo; + + /** + * Shorthand method for getVersionInfo()->getName(). + * + * @see \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo::getName() + * + * @param string|null $languageCode + * + * @return string|null The name for a given language, or null if $languageCode is not set + * or does not exist. + */ + public function getName(?string $languageCode = null): ?string + { + return $this->getVersionInfo()->getName($languageCode); + } + + /** + * Returns a field value for the given value. + * + * - If $languageCode is defined, + * return if available, otherwise null + * - If not pick using the following languages codes when applicable: + * 1. Prioritized languages (if provided to api on object retrieval) + * 2. Main language + * + * On non translatable fields this method ignores the languageCode parameter, and return main language field value. + * + * @param string $fieldDefIdentifier + * @param string|null $languageCode + * + * @return \Ibexa\Contracts\Core\FieldType\Value|null a primitive type or a field type Value object depending on the field type. + */ + abstract public function getFieldValue(string $fieldDefIdentifier, ?string $languageCode = null): ?Value; + + /** + * This method returns the complete fields collection. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field[] An array of {@see \Ibexa\Contracts\Core\Repository\Values\Content\Field} + */ + abstract public function getFields(): iterable; + + /** + * This method returns the fields for a given language and non translatable fields. + * + * - If $languageCode is defined, return if available + * - If not pick using prioritized languages (if provided to api on object retrieval) + * - Otherwise return in main language + * + * @param string|null $languageCode + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field[] An array of {@see \Ibexa\Contracts\Core\Repository\Values\Content\Field} with field identifier as keys + */ + abstract public function getFieldsByLanguage(?string $languageCode = null): iterable; + + /** + * This method returns the field for a given field definition identifier and language. + * + * - If $languageCode is defined, + * return if available, otherwise null + * - If not pick using the following languages codes when applicable: + * 1. Prioritized languages (if provided to api on object retrieval) + * 2. Main language + * + * On non translatable fields this method ignores the languageCode parameter, and return main language field. + * + * @param string $fieldDefIdentifier + * @param string|null $languageCode + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field|null A {@see \Ibexa\Contracts\Core\Repository\Values\Content\Field} or null if nothing is found + */ + abstract public function getField(string $fieldDefIdentifier, ?string $languageCode = null): ?Field; + + /** + * Returns the ContentType for this content. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + */ + abstract public function getContentType(): ContentType; + + abstract public function getThumbnail(): ?Thumbnail; + + abstract public function getDefaultLanguageCode(): string; +} + +class_alias(Content::class, 'eZ\Publish\API\Repository\Values\Content\Content'); diff --git a/src/contracts/Repository/Values/Content/ContentAwareInterface.php b/src/contracts/Repository/Values/Content/ContentAwareInterface.php new file mode 100644 index 0000000000..29a9d06bec --- /dev/null +++ b/src/contracts/Repository/Values/Content/ContentAwareInterface.php @@ -0,0 +1,14 @@ +remoteId string value. + * + * @var string + */ + public $remoteId; + + /** + * the main language code for the content. This language will also + * be used for as initial language for the first created version. + * It is also used as default language for added fields. + * + * Required. + * + * @var string + */ + public $mainLanguageCode; + + /** + * Modification date. If not given the current timestamp is used. + * + * @var \DateTime + */ + public $modificationDate; +} + +class_alias(ContentCreateStruct::class, 'eZ\Publish\API\Repository\Values\Content\ContentCreateStruct'); diff --git a/src/contracts/Repository/Values/Content/ContentDraftList.php b/src/contracts/Repository/Values/Content/ContentDraftList.php new file mode 100644 index 0000000000..f326db8f52 --- /dev/null +++ b/src/contracts/Repository/Values/Content/ContentDraftList.php @@ -0,0 +1,40 @@ +items); + } +} + +class_alias(ContentDraftList::class, 'eZ\Publish\API\Repository\Values\Content\ContentDraftList'); diff --git a/src/contracts/Repository/Values/Content/ContentInfo.php b/src/contracts/Repository/Values/Content/ContentInfo.php new file mode 100644 index 0000000000..8d2c3cce09 --- /dev/null +++ b/src/contracts/Repository/Values/Content/ContentInfo.php @@ -0,0 +1,244 @@ +status === self::STATUS_DRAFT; + } + + /** + * @return bool + */ + public function isPublished(): bool + { + return $this->status === self::STATUS_PUBLISHED; + } + + /** + * @return bool + */ + public function isTrashed(): bool + { + return $this->status === self::STATUS_TRASHED; + } + + public function isHidden(): bool + { + return $this->isHidden; + } + + public function getContentType(): ContentType + { + return $this->contentType; + } + + public function getSection(): Section + { + return $this->section; + } + + public function getSectionId(): int + { + return $this->sectionId; + } + + public function getMainLanguage(): Language + { + return $this->mainLanguage; + } + + public function getMainLanguageCode(): string + { + return $this->mainLanguageCode; + } + + public function getMainLocation(): ?Location + { + return $this->mainLocation; + } + + public function getOwner(): User + { + return $this->owner; + } + + public function getMainLocationId(): ?int + { + return $this->mainLocationId; + } + + public function getId(): int + { + return $this->id; + } + + public function getName(): string + { + return $this->name; + } +} + +class_alias(ContentInfo::class, 'eZ\Publish\API\Repository\Values\Content\ContentInfo'); diff --git a/src/contracts/Repository/Values/Content/ContentList.php b/src/contracts/Repository/Values/Content/ContentList.php new file mode 100644 index 0000000000..ff5911b1d5 --- /dev/null +++ b/src/contracts/Repository/Values/Content/ContentList.php @@ -0,0 +1,51 @@ + $contentItems + */ + public function __construct(int $totalCount, array $contentItems) + { + $this->totalCount = $totalCount; + $this->contentItems = $contentItems; + } + + public function getTotalCount(): int + { + return $this->totalCount; + } + + /** + * @return \ArrayIterator + */ + public function getIterator(): ArrayIterator + { + return new ArrayIterator($this->contentItems); + } +} + +class_alias(ContentList::class, 'eZ\Publish\API\Repository\Values\Content\ContentList'); diff --git a/eZ/Publish/API/Repository/Values/Content/ContentMetadataUpdateStruct.php b/src/contracts/Repository/Values/Content/ContentMetadataUpdateStruct.php similarity index 88% rename from eZ/Publish/API/Repository/Values/Content/ContentMetadataUpdateStruct.php rename to src/contracts/Repository/Values/Content/ContentMetadataUpdateStruct.php index f9f991ffcd..06083bfca4 100644 --- a/eZ/Publish/API/Repository/Values/Content/ContentMetadataUpdateStruct.php +++ b/src/contracts/Repository/Values/Content/ContentMetadataUpdateStruct.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\Content; +namespace Ibexa\Contracts\Core\Repository\Values\Content; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; /** * With this class data can be provided to update version independent fields of the content. @@ -77,3 +77,5 @@ class ContentMetadataUpdateStruct extends ValueObject */ public $name; } + +class_alias(ContentMetadataUpdateStruct::class, 'eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct'); diff --git a/eZ/Publish/API/Repository/Values/Content/ContentStruct.php b/src/contracts/Repository/Values/Content/ContentStruct.php similarity index 79% rename from eZ/Publish/API/Repository/Values/Content/ContentStruct.php rename to src/contracts/Repository/Values/Content/ContentStruct.php index c11d72ce81..5f730920e4 100644 --- a/eZ/Publish/API/Repository/Values/Content/ContentStruct.php +++ b/src/contracts/Repository/Values/Content/ContentStruct.php @@ -6,14 +6,14 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\Content; +namespace Ibexa\Contracts\Core\Repository\Values\Content; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; /** * Base struct for content create/update structs. * - * @property \eZ\Publish\API\Repository\Values\Content\Field[] $fields + * @property \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $fields */ abstract class ContentStruct extends ValueObject { @@ -31,3 +31,5 @@ abstract class ContentStruct extends ValueObject */ abstract public function setField(string $fieldDefIdentifier, $value, ?string $language = null): void; } + +class_alias(ContentStruct::class, 'eZ\Publish\API\Repository\Values\Content\ContentStruct'); diff --git a/src/contracts/Repository/Values/Content/ContentUpdateStruct.php b/src/contracts/Repository/Values/Content/ContentUpdateStruct.php new file mode 100644 index 0000000000..f079a3c01b --- /dev/null +++ b/src/contracts/Repository/Values/Content/ContentUpdateStruct.php @@ -0,0 +1,42 @@ +versionInfo = $versionInfo; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo|null + */ + public function getVersionInfo(): ?VersionInfo + { + return $this->versionInfo; + } + + /** + * @return bool + */ + public function hasVersionInfo(): bool + { + return $this->versionInfo instanceof VersionInfo; + } +} + +class_alias(ContentDraftListItem::class, 'eZ\Publish\API\Repository\Values\Content\DraftList\Item\ContentDraftListItem'); diff --git a/eZ/Publish/API/Repository/Values/Content/DraftList/Item/UnauthorizedContentDraftListItem.php b/src/contracts/Repository/Values/Content/DraftList/Item/UnauthorizedContentDraftListItem.php similarity index 75% rename from eZ/Publish/API/Repository/Values/Content/DraftList/Item/UnauthorizedContentDraftListItem.php rename to src/contracts/Repository/Values/Content/DraftList/Item/UnauthorizedContentDraftListItem.php index d4d57d4237..fa1a2f4e54 100644 --- a/eZ/Publish/API/Repository/Values/Content/DraftList/Item/UnauthorizedContentDraftListItem.php +++ b/src/contracts/Repository/Values/Content/DraftList/Item/UnauthorizedContentDraftListItem.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\Content\DraftList\Item; +namespace Ibexa\Contracts\Core\Repository\Values\Content\DraftList\Item; -use eZ\Publish\API\Repository\Values\Content\DraftList\ContentDraftListItemInterface; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\DraftList\ContentDraftListItemInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; /** * Item of content drafts list which represents draft to which user has no access for. @@ -62,7 +62,7 @@ public function getPayload(): array } /** - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo|null + * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo|null */ public function getVersionInfo(): ?VersionInfo { @@ -77,3 +77,5 @@ public function hasVersionInfo(): bool return false; } } + +class_alias(UnauthorizedContentDraftListItem::class, 'eZ\Publish\API\Repository\Values\Content\DraftList\Item\UnauthorizedContentDraftListItem'); diff --git a/src/contracts/Repository/Values/Content/Field.php b/src/contracts/Repository/Values/Content/Field.php new file mode 100644 index 0000000000..627502d6a5 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Field.php @@ -0,0 +1,99 @@ +id; + } + + public function getFieldDefinitionIdentifier(): string + { + return $this->fieldDefIdentifier; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + public function getLanguageCode(): ?string + { + return $this->languageCode; + } + + public function getFieldTypeIdentifier(): string + { + return $this->fieldTypeIdentifier; + } + + /** + * @phpstan-assert-if-true !null $this->getId() + */ + public function isVirtual(): bool + { + return null === $this->id; + } +} + +class_alias(Field::class, 'eZ\Publish\API\Repository\Values\Content\Field'); diff --git a/src/contracts/Repository/Values/Content/Language.php b/src/contracts/Repository/Values/Content/Language.php new file mode 100644 index 0000000000..0594395ab0 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Language.php @@ -0,0 +1,69 @@ +id; + } + + public function getLanguageCode(): string + { + return $this->languageCode; + } + + public function getName(): string + { + return $this->name; + } + + public function isEnabled(): bool + { + return $this->enabled; + } +} + +class_alias(Language::class, 'eZ\Publish\API\Repository\Values\Content\Language'); diff --git a/eZ/Publish/API/Repository/Values/Content/LanguageCreateStruct.php b/src/contracts/Repository/Values/Content/LanguageCreateStruct.php similarity index 75% rename from eZ/Publish/API/Repository/Values/Content/LanguageCreateStruct.php rename to src/contracts/Repository/Values/Content/LanguageCreateStruct.php index ed8bea00bd..3efe724cb5 100644 --- a/eZ/Publish/API/Repository/Values/Content/LanguageCreateStruct.php +++ b/src/contracts/Repository/Values/Content/LanguageCreateStruct.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\Content; +namespace Ibexa\Contracts\Core\Repository\Values\Content; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; /** * This class represents a value for creating a language. @@ -38,3 +38,5 @@ class LanguageCreateStruct extends ValueObject */ public $enabled = true; } + +class_alias(LanguageCreateStruct::class, 'eZ\Publish\API\Repository\Values\Content\LanguageCreateStruct'); diff --git a/src/contracts/Repository/Values/Content/Location.php b/src/contracts/Repository/Values/Content/Location.php new file mode 100644 index 0000000000..972302e695 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Location.php @@ -0,0 +1,326 @@ + SortClause\Location\Path::class, + self::SORT_FIELD_PUBLISHED => SortClause\DatePublished::class, + self::SORT_FIELD_MODIFIED => SortClause\DateModified::class, + self::SORT_FIELD_SECTION => SortClause\SectionIdentifier::class, + self::SORT_FIELD_DEPTH => SortClause\Location\Depth::class, + //self::SORT_FIELD_CLASS_IDENTIFIER => false, + //self::SORT_FIELD_CLASS_NAME => false, + self::SORT_FIELD_PRIORITY => SortClause\Location\Priority::class, + self::SORT_FIELD_NAME => SortClause\ContentName::class, + //self::SORT_FIELD_MODIFIED_SUBNODE => false, + self::SORT_FIELD_NODE_ID => SortClause\Location\Id::class, + self::SORT_FIELD_CONTENTOBJECT_ID => SortClause\ContentId::class, + ]; + + /** + * Map for Location sort order to their respective Query SORT constants. + */ + public const SORT_ORDER_MAP = [ + self::SORT_ORDER_DESC => Query::SORT_DESC, + self::SORT_ORDER_ASC => Query::SORT_ASC, + ]; + + /** + * Location ID. + * + * @var int Location ID. + */ + protected $id; + + /** + * the status of the location. + * + * a location gets the status DRAFT on newly created content which is not published. When content is published the + * location gets the status STATUS_PUBLISHED + * + * @var int + */ + public $status = self::STATUS_PUBLISHED; + + /** + * Location priority. + * + * Position of the Location among its siblings when sorted using priority + * sort order. + * + * @var int + */ + protected $priority; + + /** + * Indicates that the Location entity is hidden (explicitly or hidden by content). + * + * @var bool + */ + protected $hidden; + + /** + * Indicates that the Location is not visible, being either marked as hidden itself, + * or implicitly hidden by its Content or an ancestor Location. + * + * @var bool + */ + protected $invisible; + + /** + * Indicates that the Location entity has been explicitly marked as hidden. + * + * @var bool + */ + protected $explicitlyHidden; + + /** + * Remote ID. + * + * A universally unique identifier. + * + * @var string + */ + protected $remoteId; + + /** + * Parent ID. + * + * @var int Location ID. + */ + protected $parentLocationId; + + /** + * The materialized path of the location entry, eg: /1/2/. + * + * @var string + */ + protected $pathString; + + /** + * Same as {@see Location::$pathString} but as array, e.g.: [ '1', '2', '4', '23' ]. + * + * @var string[] + */ + protected array $path; + + /** + * Depth location has in the location tree. + * + * @var int + */ + protected $depth; + + /** + * Specifies which property the child locations should be sorted on. + * + * Valid values are found at {@link Location::SORT_FIELD_*} + * + * @var int + */ + protected $sortField; + + /** + * Specifies whether the sort order should be ascending or descending. + * + * Valid values are {@link Location::SORT_ORDER_*} + * + * @var int + */ + protected $sortOrder; + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content */ + protected $content; + + /** + * Returns the content info of the content object of this location. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo + */ + abstract public function getContentInfo(): ContentInfo; + + /** + * Return the parent location of this location. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location|null + */ + abstract public function getParentLocation(): ?Location; + + public function getId(): int + { + return $this->id; + } + + public function getContentId(): int + { + return $this->getContentInfo()->getId(); + } + + /** + * Returns true if current location is a draft. + * + * @return bool + */ + public function isDraft(): bool + { + return $this->status === self::STATUS_DRAFT; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function getContent(): Content + { + return $this->content; + } + + /** + * Get SortClause objects built from Locations' sort options. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException If sort field has a deprecated/unsupported value which does not have a Sort Clause. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause[] + */ + public function getSortClauses(): array + { + $map = self::SORT_FIELD_MAP; + if (!isset($map[$this->sortField])) { + throw new NotImplementedException( + "Sort Clause not implemented for Location sort Field {$this->sortField}" + ); + } + + $sortClause = new $map[$this->sortField](); + $sortClause->direction = self::SORT_ORDER_MAP[$this->sortOrder]; + + return [$sortClause]; + } + + /** + * The path to the Location represented by the current instance, + * e.g. /1/2/4/23 where 23 is current id. + */ + public function getPathString(): string + { + return $this->pathString; + } + + /** + * Same as {@see Location::getPathString()} but as array, e.g.: [ '1', '2', '4', '23' ]. + * + * @return string[] + */ + public function getPath(): array + { + if (isset($this->path)) { + return $this->path; + } + + $pathString = trim($this->pathString ?? '', '/'); + + return $this->path = !empty($pathString) ? explode('/', $pathString) : []; + } + + /** + * Indicates that the Location is not visible, being either marked as hidden itself, or implicitly hidden by + * its Content or an ancestor Location. + */ + public function isInvisible(): bool + { + return $this->invisible; + } + + /** + * Indicates that the Location is hidden either explicitly or by content. + */ + public function isHidden(): bool + { + return $this->hidden; + } + + public function getDepth(): int + { + return $this->depth; + } + + public function __isset($property) + { + if ($property === 'path') { + return true; + } + + return parent::__isset($property); + } + + public function __get($property) + { + if ($property === 'path') { + return $this->getPath(); + } + + return parent::__get($property); + } +} + +class_alias(Location::class, 'eZ\Publish\API\Repository\Values\Content\Location'); diff --git a/eZ/Publish/API/Repository/Values/Content/LocationCreateStruct.php b/src/contracts/Repository/Values/Content/LocationCreateStruct.php similarity index 88% rename from eZ/Publish/API/Repository/Values/Content/LocationCreateStruct.php rename to src/contracts/Repository/Values/Content/LocationCreateStruct.php index df497a3433..2e80acd181 100644 --- a/eZ/Publish/API/Repository/Values/Content/LocationCreateStruct.php +++ b/src/contracts/Repository/Values/Content/LocationCreateStruct.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\Content; +namespace Ibexa\Contracts\Core\Repository\Values\Content; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; /** * This class is used to create a new Location for a content object. @@ -72,3 +72,5 @@ class LocationCreateStruct extends ValueObject */ public $parentLocationId; } + +class_alias(LocationCreateStruct::class, 'eZ\Publish\API\Repository\Values\Content\LocationCreateStruct'); diff --git a/src/contracts/Repository/Values/Content/LocationList.php b/src/contracts/Repository/Values/Content/LocationList.php new file mode 100644 index 0000000000..cf6d624d33 --- /dev/null +++ b/src/contracts/Repository/Values/Content/LocationList.php @@ -0,0 +1,57 @@ +locations); + } + + public function getTotalCount(): int + { + return $this->totalCount; + } +} + +class_alias(LocationList::class, 'eZ\Publish\API\Repository\Values\Content\LocationList'); diff --git a/src/contracts/Repository/Values/Content/LocationQuery.php b/src/contracts/Repository/Values/Content/LocationQuery.php new file mode 100644 index 0000000000..c35464f1f9 --- /dev/null +++ b/src/contracts/Repository/Values/Content/LocationQuery.php @@ -0,0 +1,18 @@ +name = $name; + $this->ranges = $ranges; + } + + public function getRanges(): array + { + return $this->ranges; + } + + public function getName(): string + { + return $this->name; + } +} + +class_alias(AbstractRangeAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractRangeAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/AbstractStatsAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/AbstractStatsAggregation.php new file mode 100644 index 0000000000..5e15dbc4c5 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/AbstractStatsAggregation.php @@ -0,0 +1,33 @@ +name = $name; + } + + public function getName(): string + { + return $this->name; + } +} + +class_alias(AbstractStatsAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractStatsAggregation'); diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/AbstractTermAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/AbstractTermAggregation.php similarity index 83% rename from eZ/Publish/API/Repository/Values/Content/Query/Aggregation/AbstractTermAggregation.php rename to src/contracts/Repository/Values/Content/Query/Aggregation/AbstractTermAggregation.php index 4c8ab5edb0..2886cf61b7 100644 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/AbstractTermAggregation.php +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/AbstractTermAggregation.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; +namespace Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; abstract class AbstractTermAggregation implements Aggregation { @@ -70,3 +70,5 @@ public function setMinCount(int $minCount): self return $this; } } + +class_alias(AbstractTermAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\AbstractTermAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/ContentTypeGroupTermAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/ContentTypeGroupTermAggregation.php new file mode 100644 index 0000000000..ca41b42449 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/ContentTypeGroupTermAggregation.php @@ -0,0 +1,15 @@ +type = $type; + } + + public function getType(): string + { + return $this->type; + } + + public static function fromGenerator( + string $name, + string $type, + RangesGeneratorInterface $generator + ): self { + $ranges = $generator->generate(); + if ($ranges instanceof Traversable) { + $ranges = iterator_to_array($ranges); + } + + return new self($name, $type, $ranges); + } +} + +class_alias(DateMetadataRangeAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\DateMetadataRangeAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldRangeAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldRangeAggregation.php new file mode 100644 index 0000000000..e63e888c96 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldRangeAggregation.php @@ -0,0 +1,31 @@ +contentTypeIdentifier = $contentTypeIdentifier; + $this->fieldDefinitionIdentifier = $fieldDefinitionIdentifier; + } +} + +class_alias(AbstractFieldRangeAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\AbstractFieldRangeAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldStatsAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldStatsAggregation.php new file mode 100644 index 0000000000..4cbf8e9d11 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldStatsAggregation.php @@ -0,0 +1,30 @@ +contentTypeIdentifier = $contentTypeIdentifier; + $this->fieldDefinitionIdentifier = $fieldDefinitionIdentifier; + } +} + +class_alias(AbstractFieldStatsAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\AbstractFieldStatsAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldTermAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldTermAggregation.php new file mode 100644 index 0000000000..acf34c9065 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/AbstractFieldTermAggregation.php @@ -0,0 +1,30 @@ +contentTypeIdentifier = $contentTypeIdentifier; + $this->fieldDefinitionIdentifier = $fieldDefinitionIdentifier; + } +} + +class_alias(AbstractFieldTermAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\AbstractFieldTermAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Field/AuthorTermAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/AuthorTermAggregation.php new file mode 100644 index 0000000000..f179b17751 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/AuthorTermAggregation.php @@ -0,0 +1,15 @@ +type; } } + +class_alias(CountryTermAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\CountryTermAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Field/DateRangeAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/DateRangeAggregation.php new file mode 100644 index 0000000000..8b225caf25 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/DateRangeAggregation.php @@ -0,0 +1,31 @@ +generate(); + if ($ranges instanceof Traversable) { + $ranges = iterator_to_array($ranges); + } + + return new self($name, $contentTypeIdentifier, $fieldDefinitionIdentifier, $ranges); + } +} + +class_alias(DateRangeAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\DateRangeAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Field/DateTimeRangeAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/DateTimeRangeAggregation.php new file mode 100644 index 0000000000..3963444ecc --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/DateTimeRangeAggregation.php @@ -0,0 +1,31 @@ +generate(); + if ($ranges instanceof Traversable) { + $ranges = iterator_to_array($ranges); + } + + return new self($name, $contentTypeIdentifier, $fieldDefinitionIdentifier, $ranges); + } +} + +class_alias(DateTimeRangeAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\DateTimeRangeAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Field/FieldAggregationTrait.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/FieldAggregationTrait.php new file mode 100644 index 0000000000..065dda94e1 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/FieldAggregationTrait.php @@ -0,0 +1,30 @@ +contentTypeIdentifier; + } + + public function getFieldDefinitionIdentifier(): string + { + return $this->fieldDefinitionIdentifier; + } +} + +class_alias(FieldAggregationTrait::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\FieldAggregationTrait'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Field/FloatRangeAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/FloatRangeAggregation.php new file mode 100644 index 0000000000..188a15396f --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/FloatRangeAggregation.php @@ -0,0 +1,31 @@ +generate(); + if ($ranges instanceof Traversable) { + $ranges = iterator_to_array($ranges); + } + + return new self($name, $contentTypeIdentifier, $fieldDefinitionIdentifier, $ranges); + } +} + +class_alias(FloatRangeAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\FloatRangeAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Field/FloatStatsAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/FloatStatsAggregation.php new file mode 100644 index 0000000000..8a7e3e236d --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/FloatStatsAggregation.php @@ -0,0 +1,15 @@ +generate(); + if ($ranges instanceof Traversable) { + $ranges = iterator_to_array($ranges); + } + + return new static($name, $contentTypeIdentifier, $fieldDefinitionIdentifier, $ranges); + } +} + +class_alias(IntegerRangeAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\IntegerRangeAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Field/IntegerStatsAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/IntegerStatsAggregation.php new file mode 100644 index 0000000000..ce84a7f915 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Field/IntegerStatsAggregation.php @@ -0,0 +1,15 @@ +generate(); + if ($ranges instanceof Traversable) { + $ranges = iterator_to_array($ranges); + } + + return new self($name, $contentTypeIdentifier, $fieldDefinitionIdentifier, $ranges); + } +} + +class_alias(TimeRangeAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\TimeRangeAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/FieldAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/FieldAggregation.php new file mode 100644 index 0000000000..868371ed47 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/FieldAggregation.php @@ -0,0 +1,18 @@ +isValidPathString($pathString)) { + throw new InvalidArgumentException( + '$pathString', + "'$pathString' value must follow the path string format, e.g. /1/2/" + ); + } + + $this->pathString = $pathString; + } + + public function getPathString(): string + { + return $this->pathString; + } + + private function isValidPathString(string $pathString): bool + { + return preg_match('/^(\/\w+)+\/$/', $pathString) === 1; + } + + public static function fromLocation(string $name, Location $location): self + { + return new self($name, $location->pathString); + } +} + +class_alias(SubtreeTermAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Location\SubtreeTermAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/LocationAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/LocationAggregation.php new file mode 100644 index 0000000000..5e9a3d3d26 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/LocationAggregation.php @@ -0,0 +1,18 @@ +objectStateGroupIdentifier; } } + +class_alias(ObjectStateTermAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\ObjectStateTermAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Range.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Range.php new file mode 100644 index 0000000000..aea16b7214 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Range.php @@ -0,0 +1,115 @@ +from = $from; + $this->to = $to; + $this->label = $label; + } + + public function getFrom() + { + return $this->from; + } + + public function getTo() + { + return $this->to; + } + + public function getLabel(): ?string + { + return $this->label; + } + + public function setLabel(?string $label): void + { + $this->label = $label; + } + + public function __toString(): string + { + if ($this->label !== null) { + return sprintf( + '%s:[%s;%s)', + $this->label, + $this->getRangeValueAsString($this->from), + $this->getRangeValueAsString($this->to) + ); + } + + return sprintf( + '[%s;%s)', + $this->getRangeValueAsString($this->from), + $this->getRangeValueAsString($this->to) + ); + } + + public function equalsTo(Range $value): bool + { + return $this->from == $value->from && $this->to == $value->to; + } + + private function getRangeValueAsString($value): string + { + if ($value === null) { + return '*'; + } + + if ($value instanceof DateTimeInterface) { + return $value->format(DateTimeInterface::ISO8601); + } + + return (string)$value; + } + + public static function ofInt(?int $from, ?int $to): self + { + return new self($from, $to); + } + + public static function ofFloat(?float $from, ?float $to): self + { + return new self($from, $to); + } + + public static function ofDateTime(?DateTimeInterface $from, ?DateTimeInterface $to): self + { + return new self($from, $to); + } +} + +class_alias(Range::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Range'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Ranges/DateTimeStepRangesGenerator.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Ranges/DateTimeStepRangesGenerator.php new file mode 100644 index 0000000000..d68036c815 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Ranges/DateTimeStepRangesGenerator.php @@ -0,0 +1,129 @@ +start = $start; + $this->end = $end; + $this->step = new DateInterval('P1D'); + } + + public function getStart(): DateTimeInterface + { + return $this->start; + } + + public function setStart(DateTimeInterface $start): self + { + $this->start = $start; + + return $this; + } + + public function getEnd(): DateTimeInterface + { + return $this->end; + } + + public function setEnd(DateTimeInterface $end): self + { + $this->end = $end; + + return $this; + } + + public function getStep(): DateInterval + { + return $this->step; + } + + public function setStep(DateInterval $step): self + { + $this->step = $step; + + return $this; + } + + public function isLeftOpen(): bool + { + return $this->isLeftOpen; + } + + public function setLeftOpen(bool $isLeftOpen): void + { + $this->isLeftOpen = $isLeftOpen; + } + + public function isRightOpen(): bool + { + return $this->isRightOpen; + } + + public function setRightOpen(bool $isRightOpen): self + { + $this->isRightOpen = $isRightOpen; + + return $this; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range[] + */ + public function generate(): array + { + if ($this->start == $this->end && $this->isLeftOpen && $this->isRightOpen) { + return [ + new Range(Range::INF, Range::INF), + ]; + } + + $ranges = []; + + if ($this->isLeftOpen) { + $ranges[] = Range::ofDateTime(Range::INF, $this->start); + } + + /** @var \DateTimeImmutable $current */ + $current = $this->start; + if ($current instanceof DateTime) { + $current = DateTimeImmutable::createFromMutable($current); + } + + while ($current < $this->end) { + $next = $current->add($this->step); + $ranges[] = Range::ofDateTime($current, $next); + $current = $next; + } + + if ($this->isRightOpen) { + $ranges[] = Range::ofDateTime($this->end, Range::INF); + } + + return $ranges; + } +} diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Ranges/FloatStepRangesGenerator.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Ranges/FloatStepRangesGenerator.php new file mode 100644 index 0000000000..fd265d190d --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Ranges/FloatStepRangesGenerator.php @@ -0,0 +1,119 @@ +start = $start; + $this->end = $end; + } + + public function getStart(): float + { + return $this->start; + } + + public function setStart(float $start): self + { + $this->start = $start; + + return $this; + } + + public function getEnd(): float + { + return $this->end; + } + + public function setEnd(float $end): self + { + $this->end = $end; + + return $this; + } + + public function getStep(): float + { + return $this->step; + } + + public function setStep(float $step): self + { + $this->step = $step; + + return $this; + } + + public function isLeftOpen(): bool + { + return $this->isLeftOpen; + } + + public function setLeftOpen(bool $isLeftOpen): self + { + $this->isLeftOpen = $isLeftOpen; + + return $this; + } + + public function isRightOpen(): bool + { + return $this->isRightOpen; + } + + public function setRightOpen(bool $isRightOpen): self + { + $this->isRightOpen = $isRightOpen; + + return $this; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range[] + */ + public function generate(): array + { + if ($this->start === $this->end && $this->isLeftOpen && $this->isRightOpen) { + return [ + new Range(Range::INF, Range::INF), + ]; + } + + $ranges = []; + + if ($this->isLeftOpen) { + $ranges[] = Range::ofFloat(Range::INF, $this->start); + } + + $values = range($this->start, $this->end, $this->step); + for ($i = 1, $count = count($values); $i < $count; ++$i) { + $ranges[] = Range::ofFloat($values[$i - 1], $values[$i]); + } + + if ($this->isRightOpen) { + $ranges[] = Range::ofFloat($this->end, Range::INF); + } + + return $ranges; + } +} diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Ranges/IntegerStepRangesGenerator.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Ranges/IntegerStepRangesGenerator.php new file mode 100644 index 0000000000..154f172e53 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Ranges/IntegerStepRangesGenerator.php @@ -0,0 +1,116 @@ +start = $start; + $this->end = $end; + } + + public function getStart(): int + { + return $this->start; + } + + public function setStart(int $start): self + { + $this->start = $start; + + return $this; + } + + public function getEnd(): int + { + return $this->end; + } + + public function setEnd(int $end): self + { + $this->end = $end; + + return $this; + } + + public function getStep(): int + { + return $this->step; + } + + public function setStep(int $step): self + { + $this->step = $step; + + return $this; + } + + public function isLeftOpen(): bool + { + return $this->isLeftOpen; + } + + public function setLeftOpen(bool $isLeftOpen): self + { + $this->isLeftOpen = $isLeftOpen; + + return $this; + } + + public function isRightOpen(): bool + { + return $this->isRightOpen; + } + + public function setRightOpen(bool $isRightOpen): self + { + $this->isRightOpen = $isRightOpen; + + return $this; + } + + /** + * @return \Generator<\Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range> + */ + public function generate(): Generator + { + if ($this->start === $this->end && $this->isLeftOpen && $this->isRightOpen) { + yield new Range(Range::INF, Range::INF); + + return; + } + + if ($this->isLeftOpen) { + yield Range::ofInt(Range::INF, $this->start); + } + + $values = range($this->start, $this->end, $this->step); + for ($i = 1, $count = count($values); $i < $count; ++$i) { + yield Range::ofInt($values[$i - 1], $values[$i]); + } + + if ($this->isRightOpen) { + yield Range::ofInt($this->end, Range::INF); + } + } +} diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/Ranges/RangesGeneratorInterface.php b/src/contracts/Repository/Values/Content/Query/Aggregation/Ranges/RangesGeneratorInterface.php new file mode 100644 index 0000000000..e059013ca6 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/Ranges/RangesGeneratorInterface.php @@ -0,0 +1,17 @@ +fieldName = $fieldName; + } + + public function getFieldName(): string + { + return $this->fieldName; + } + + public static function fromGenerator( + string $name, + string $fieldName, + RangesGeneratorInterface $generator + ): self { + $ranges = $generator->generate(); + if ($ranges instanceof Traversable) { + $ranges = iterator_to_array($ranges); + } + + return new self($name, $fieldName, $ranges); + } +} + +class_alias(RawRangeAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\RawRangeAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/RawStatsAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/RawStatsAggregation.php new file mode 100644 index 0000000000..f8ea562a64 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/RawStatsAggregation.php @@ -0,0 +1,29 @@ +fieldName = $fieldName; + } + + public function getFieldName(): string + { + return $this->fieldName; + } +} + +class_alias(RawStatsAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\RawStatsAggregation'); diff --git a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/RawTermAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/RawTermAggregation.php similarity index 75% rename from eZ/Publish/API/Repository/Values/Content/Query/Aggregation/RawTermAggregation.php rename to src/contracts/Repository/Values/Content/Query/Aggregation/RawTermAggregation.php index 79b30a92ee..9002a92f0f 100644 --- a/eZ/Publish/API/Repository/Values/Content/Query/Aggregation/RawTermAggregation.php +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/RawTermAggregation.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\Content\Query\Aggregation; +namespace Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; final class RawTermAggregation extends AbstractTermAggregation implements RawAggregation { @@ -27,3 +27,5 @@ public function getFieldName(): string return $this->fieldName; } } + +class_alias(RawTermAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\RawTermAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/SectionTermAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/SectionTermAggregation.php new file mode 100644 index 0000000000..9a860f8780 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/SectionTermAggregation.php @@ -0,0 +1,15 @@ +type; } } + +class_alias(UserMetadataTermAggregation::class, 'eZ\Publish\API\Repository\Values\Content\Query\Aggregation\UserMetadataTermAggregation'); diff --git a/src/contracts/Repository/Values/Content/Query/Aggregation/VisibilityTermAggregation.php b/src/contracts/Repository/Values/Content/Query/Aggregation/VisibilityTermAggregation.php new file mode 100644 index 0000000000..77a0642328 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Aggregation/VisibilityTermAggregation.php @@ -0,0 +1,15 @@ +getSpecifications() as $operatorSpecifications) { + if ($operatorSpecifications->operator != $operator) { + continue; + } + $operatorFound = true; + + // input format check (single/array) + switch ($operatorSpecifications->valueFormat) { + case Specifications::FORMAT_SINGLE: + if (is_array($value)) { + throw new InvalidArgumentException('The Criterion expects a single value'); + } + break; + + case Specifications::FORMAT_ARRAY: + if (!is_array($value)) { + throw new InvalidArgumentException('The Criterion expects an array of values'); + } + break; + } + + // input value check + if ($operatorSpecifications->valueTypes !== null) { + $callback = $this->getValueTypeCheckCallback($operatorSpecifications->valueTypes); + if (!is_array($value)) { + $value = [$value]; + } + foreach ($value as $item) { + if ($callback($item) === false) { + throw new InvalidArgumentException('Unsupported value (' . gettype($item) . ")$item"); + } + } + } + } + + // Operator wasn't found in the criterion specifications + if ($operatorFound === false) { + throw new InvalidArgumentException("Operator $operator isn't supported by Criterion " . static::class); + } + + $this->operator = $operator; + $this->value = $value; + $this->target = $target; + $this->valueData = $valueData; + } + + /** + * Criterion description function. + * + * Returns the combination of the Criterion's supported operator/value, + * as an array of {@see \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator\Specifications} objects + * - Operator is one supported Operator, as an Operator::* constant + * - ValueType is the type of input value this operator requires, either array or single + * - SupportedTypes is an array of types the operator will accept + * - ValueCountLimitation is an integer saying how many values are expected. + * + * + * // IN and EQ are supported + * return [ + * // The EQ operator expects a single value, either as an integer or a string + * new Specifications( + * Operator::EQ, + * Specifications::FORMAT_SINGLE, + * Specifications::TYPE_INTEGER | Specifications::TYPE_STRING + * ), + * // The IN operator expects an array of values, of either integers or strings + * new Specifications( + * Operator::IN, + * Specifications::FORMAT_ARRAY, + * Specifications::TYPE_INTEGER | Specifications::TYPE_STRING + * ) + * ] + * + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator\Specifications[] + */ + abstract public function getSpecifications(): array; + + /** + * Returns a callback that checks the values types depending on the operator specifications. + * + * @param int $valueTypes The accepted values, as a bit field of Specifications::TYPE_* constants + * + * @return callable + */ + private function getValueTypeCheckCallback(int $valueTypes): callable + { + $callback = static function ($value) { + return false; + }; + + // the callback code will return true as soon as an accepted value type is found + if ($valueTypes & Specifications::TYPE_INTEGER) { + $callback = static function ($value) use ($callback) { + return is_numeric($value) || $callback($value); + }; + } + if ($valueTypes & Specifications::TYPE_STRING) { + $callback = static function ($value) use ($callback) { + return is_string($value) || $callback($value); + }; + } + if ($valueTypes & Specifications::TYPE_BOOLEAN) { + $callback = static function ($value) use ($callback) { + return is_bool($value) || $callback($value); + }; + } + + return $callback; + } +} + +class_alias(Criterion::class, 'eZ\Publish\API\Repository\Values\Content\Query\Criterion'); diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Ancestor.php b/src/contracts/Repository/Values/Content/Query/Criterion/Ancestor.php new file mode 100644 index 0000000000..984e53fdcb --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Ancestor.php @@ -0,0 +1,61 @@ +criteria = $criteria; + } + + public function getSpecifications(): array + { + throw new NotImplementedException('getSpecifications() not implemented for CompositeCriterion'); + } +} + +class_alias(CompositeCriterion::class, 'eZ\Publish\API\Repository\Values\Content\Query\Criterion\CompositeCriterion'); diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/ContentId.php b/src/contracts/Repository/Values/Content/Query/Criterion/ContentId.php new file mode 100644 index 0000000000..ec7158bc5a --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/ContentId.php @@ -0,0 +1,48 @@ + + */ + public function getSpecifications(): array + { + return [ + new Specifications(Operator::LIKE, Specifications::FORMAT_SINGLE), + ]; + } +} diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/ContentTypeGroupId.php b/src/contracts/Repository/Values/Content/Query/Criterion/ContentTypeGroupId.php new file mode 100644 index 0000000000..f68507ad20 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/ContentTypeGroupId.php @@ -0,0 +1,58 @@ + + * $createdCriterion = new Criterion\DateMetadata( + * Criterion\DateMetadata::CREATED, + * Operator::GTE, + * strtotime( 'yesterday' ) + * ); + * + */ +class DateMetadata extends Criterion implements TrashCriterion, FilteringCriterion +{ + public const MODIFIED = 'modified'; + + public const CREATED = 'created'; + + public const PUBLISHED = 'published'; + + /** + * (applies to TrashService::findTrashItems only). + */ + public const TRASHED = 'trashed'; + + public const TARGETS = [ + self::MODIFIED, + self::CREATED, + self::PUBLISHED, + self::TRASHED, + ]; + + /** + * Creates a new DateMetadata criterion on $metadata. + * + * @throws \InvalidArgumentException If target is unknown + * + * @param string $target One of DateMetadata::CREATED, DateMetadata::MODIFIED or DateMetadata::TRASHED (applies to TrashService::findTrashItems only) + * @param string $operator One of the Operator constants + * @param mixed $value The match value, either as an array of as a single value, depending on the operator + */ + public function __construct(string $target, string $operator, $value) + { + if (!in_array($target, self::TARGETS)) { + throw new InvalidArgumentException(sprintf( + 'Unknown DateMetadata target "%s". Expected one of: "%s"', + $target, + implode('", "', self::TARGETS), + )); + } + parent::__construct($target, $operator, $value); + } + + public function getSpecifications(): array + { + return [ + new Specifications(Operator::EQ, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), + new Specifications(Operator::GT, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), + new Specifications(Operator::GTE, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), + new Specifications(Operator::LT, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), + new Specifications(Operator::LTE, Specifications::FORMAT_SINGLE, Specifications::TYPE_INTEGER), + new Specifications(Operator::IN, Specifications::FORMAT_ARRAY, Specifications::TYPE_INTEGER), + new Specifications(Operator::BETWEEN, Specifications::FORMAT_ARRAY, Specifications::TYPE_INTEGER, 2), + ]; + } +} + +class_alias(DateMetadata::class, 'eZ\Publish\API\Repository\Values\Content\Query\Criterion\DateMetadata'); diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Field.php b/src/contracts/Repository/Values/Content/Query/Criterion/Field.php new file mode 100644 index 0000000000..db05599206 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Field.php @@ -0,0 +1,79 @@ +customFields[$type][$field] = $customField; + } + + /** + * Return custom field. + * + * If no custom field is set, return null + * + * @param string $type + * @param string $field + * + * @return string|null + */ + public function getCustomField(string $type, string $field): ?string + { + if (!isset($this->customFields[$type]) || + !isset($this->customFields[$type][$field])) { + return null; + } + + return $this->customFields[$type][$field]; + } +} + +class_alias(Field::class, 'eZ\Publish\API\Repository\Values\Content\Query\Criterion\Field'); diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/FieldRelation.php b/src/contracts/Repository/Values/Content/Query/Criterion/FieldRelation.php new file mode 100644 index 0000000000..1e613e2832 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/FieldRelation.php @@ -0,0 +1,37 @@ + + * array( + * 'title' => 2, + * … + * ) + * + * + * @var array + */ + public $boost = []; + + /** + * Analyzer configuration. + * + * @TODO: Define how this could look like + * + * @var mixed + */ + public $analyzers; + + /** + * Analyzer wildcard handling configuration. + * + * @TODO: Define how this could look like + * + * @var mixed + */ + public $wildcards; + + /** + * Custom field definitions to query instead of default field. + * + * @var array + */ + protected $customFields = []; + + public function __construct($value, array $properties = []) + { + parent::__construct(null, Operator::LIKE, $value); + + // Assign additional properties, ugly but with the existing constructor + // API the only sensible way, I guess. + foreach ($properties as $name => $value) { + if (!isset($this->$name)) { + throw new \InvalidArgumentException("Unknown property $name."); + } + + $this->$name = $value; + } + } + + public function getSpecifications(): array + { + return [ + new Specifications(Operator::LIKE, Specifications::FORMAT_SINGLE), + ]; + } + + /** + * Set a custom field to query. + * + * Set a custom field to query for a defined field in a defined type. + * + * @param string $type + * @param string $field + * @param string $customField + */ + public function setCustomField(string $type, string $field, string $customField): void + { + $this->customFields[$type][$field] = $customField; + } + + /** + * Retun custom field. + * + * If no custom field is set, return null + * + * @param string $type + * @param string $field + * + * @return mixed + */ + public function getCustomField(string $type, string $field): ?string + { + if (!isset($this->customFields[$type]) || + !isset($this->customFields[$type][$field])) { + return null; + } + + return $this->customFields[$type][$field]; + } +} + +class_alias(FullText::class, 'eZ\Publish\API\Repository\Values\Content\Query\Criterion\FullText'); diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Image.php b/src/contracts/Repository/Values/Content/Query/Criterion/Image.php new file mode 100644 index 0000000000..ce64e7faa5 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Image.php @@ -0,0 +1,102 @@ +, + * size?: Range, + * width?: Range, + * height?: Range, + * orientation?: string|array, + * } + * + * @template-extends \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Image\AbstractImageCompositeCriterion + */ +final class Image extends AbstractImageCompositeCriterion +{ + public const IMAGE_SEARCH_CRITERIA = [ + 'mimeTypes', + 'size', + 'width', + 'height', + 'orientation', + ]; + + protected function getSupportedCriteria(): array + { + return self::IMAGE_SEARCH_CRITERIA; + } + + /** + * @phpstan-param ImageCriteria $imageCriteriaData + * + * @return array<\Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion> + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + protected function buildCriteria( + string $fieldDefIdentifier, + array $imageCriteriaData + ): array { + $criteria = []; + + if (isset($imageCriteriaData['mimeTypes'])) { + $criteria[] = new MimeType( + $fieldDefIdentifier, + $imageCriteriaData['mimeTypes'] + ); + } + + if (isset($imageCriteriaData['size'])) { + $size = $imageCriteriaData['size']; + $criteria[] = new FileSize( + $fieldDefIdentifier, + $this->getMinValue($size), + $this->getMaxValue($size), + ); + } + + if (isset($imageCriteriaData['width'])) { + $width = $imageCriteriaData['width']; + $criteria[] = new Width( + $fieldDefIdentifier, + $this->getMinValue($width), + $this->getMaxValue($width) + ); + } + + if (isset($imageCriteriaData['height'])) { + $height = $imageCriteriaData['height']; + $criteria[] = new Height( + $fieldDefIdentifier, + $this->getMinValue($height), + $this->getMaxValue($height) + ); + } + + if (isset($imageCriteriaData['orientation'])) { + $criteria[] = new Orientation( + $fieldDefIdentifier, + $imageCriteriaData['orientation'] + ); + } + + return $criteria; + } +} diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Image/AbstractImageCompositeCriterion.php b/src/contracts/Repository/Values/Content/Query/Criterion/Image/AbstractImageCompositeCriterion.php new file mode 100644 index 0000000000..47066404f6 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Image/AbstractImageCompositeCriterion.php @@ -0,0 +1,112 @@ +validate($imageCriteriaData, $this->getSupportedCriteria()); + + $criteria = new Criterion\LogicalAnd( + $this->buildCriteria($fieldDefIdentifier, $imageCriteriaData) + ); + + parent::__construct($criteria); + } + + /** + * @phpstan-param TImageCriteria $imageCriteriaData + * + * @return array<\Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion> + */ + abstract protected function buildCriteria(string $fieldDefIdentifier, array $imageCriteriaData): array; + + /** + * @return array + */ + abstract protected function getSupportedCriteria(): array; + + /** + * @phpstan-param TImageCriteria $imageCriteriaData + * + * @param array $supportedCriteria + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + protected function validate( + array $imageCriteriaData, + array $supportedCriteria + ): void { + if (empty($imageCriteriaData)) { + throw new InvalidArgumentException( + '$data', + sprintf( + 'At least one of the supported criteria should be passed: "%s"', + implode(', ', $supportedCriteria) + ) + ); + } + + $notSupportedCriteria = array_diff( + array_keys($imageCriteriaData), + $supportedCriteria + ); + + if (!empty($notSupportedCriteria)) { + throw new InvalidArgumentException( + '$data', + sprintf( + 'Given criteria are not supported: "%s". Supported image criteria: "%s"', + implode(', ', $notSupportedCriteria), + implode(', ', $supportedCriteria) + ) + ); + } + } + + /** + * @param array{min?: numeric|null} $data + * + * @return numeric + */ + protected function getMinValue(array $data) + { + return $data['min'] ?? 0; + } + + /** + * @param array{max?: numeric|null} $data + * + * @return numeric|null + */ + protected function getMaxValue(array $data) + { + return $data['max'] ?? null; + } +} diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Image/AbstractImageRangeCriterion.php b/src/contracts/Repository/Values/Content/Query/Criterion/Image/AbstractImageRangeCriterion.php new file mode 100644 index 0000000000..7540c6a3d5 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Image/AbstractImageRangeCriterion.php @@ -0,0 +1,97 @@ +validate($minValue, $maxValue); + + $value[] = $minValue; + $operator = Operator::GTE; + + if ($maxValue > 0) { + $operator = Operator::BETWEEN; + $value[] = $maxValue; + } + + parent::__construct( + $fieldDefIdentifier, + $operator, + $value + ); + } + + public function getSpecifications(): array + { + return [ + new Specifications( + Operator::BETWEEN, + Specifications::FORMAT_ARRAY, + Specifications::TYPE_INTEGER + ), + new Specifications( + Operator::GTE, + Specifications::FORMAT_ARRAY, + Specifications::TYPE_INTEGER + ), + ]; + } + + /** + * @param numeric $minValue + * @param numeric|null $maxValue + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + protected function validate($minValue, $maxValue): void + { + if ($minValue < 0) { + throw new InvalidArgumentException( + '$minValue', + 'Value should be greater or equal 0' + ); + } + + if ( + null !== $maxValue + && $maxValue <= 0 + ) { + throw new InvalidArgumentException( + '$maxValue', + 'Value should be greater than 0' + ); + } + + if ( + null !== $maxValue + && $minValue > $maxValue + ) { + throw new InvalidArgumentException( + '$minValue', + 'Value should be greater than' . $maxValue + ); + } + } +} diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Image/Dimensions.php b/src/contracts/Repository/Values/Content/Query/Criterion/Image/Dimensions.php new file mode 100644 index 0000000000..c3179ef09c --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Image/Dimensions.php @@ -0,0 +1,69 @@ + + */ +final class Dimensions extends AbstractImageCompositeCriterion +{ + public const IMAGE_DIMENSIONS_CRITERIA = [ + 'width', + 'height', + ]; + + /** + * @return array + */ + protected function getSupportedCriteria(): array + { + return self::IMAGE_DIMENSIONS_CRITERIA; + } + + /** + * @phpstan-param ImageCriteria $imageCriteriaData + * + * @return array<\Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion> + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + protected function buildCriteria( + string $fieldDefIdentifier, + array $imageCriteriaData + ): array { + $criteria = []; + + if (isset($imageCriteriaData['width'])) { + $width = $imageCriteriaData['width']; + $criteria[] = new Width( + $fieldDefIdentifier, + $this->getMinValue($width), + $this->getMaxValue($width) + ); + } + + if (isset($imageCriteriaData['height'])) { + $height = $imageCriteriaData['height']; + $criteria[] = new Height( + $fieldDefIdentifier, + $this->getMinValue($height), + $this->getMaxValue($height) + ); + } + + return $criteria; + } +} diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Image/FileSize.php b/src/contracts/Repository/Values/Content/Query/Criterion/Image/FileSize.php new file mode 100644 index 0000000000..9ca824e6e4 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Image/FileSize.php @@ -0,0 +1,32 @@ + 0) { + $minValue *= 1024 * 1024; + } + + if ($maxValue > 0) { + $maxValue *= 1024 * 1024; + } + + parent::__construct( + $fieldDefIdentifier, + $minValue, + $maxValue + ); + } +} diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Image/Height.php b/src/contracts/Repository/Values/Content/Query/Criterion/Image/Height.php new file mode 100644 index 0000000000..78719e5d6e --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Image/Height.php @@ -0,0 +1,13 @@ + $type + */ + public function __construct( + string $fieldDefIdentifier, + $type + ) { + parent::__construct($fieldDefIdentifier, null, $type); + } + + public function getSpecifications(): array + { + return [ + new Specifications( + Operator::EQ, + Specifications::FORMAT_SINGLE, + Specifications::TYPE_STRING + ), + new Specifications( + Operator::IN, + Specifications::FORMAT_ARRAY, + Specifications::TYPE_STRING + ), + ]; + } +} diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Image/Orientation.php b/src/contracts/Repository/Values/Content/Query/Criterion/Image/Orientation.php new file mode 100644 index 0000000000..5bae6e19e2 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Image/Orientation.php @@ -0,0 +1,100 @@ + $orientation + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + */ + public function __construct( + string $fieldDefIdentifier, + $orientation + ) { + $this->validate($orientation); + + parent::__construct($fieldDefIdentifier, null, $orientation); + } + + public function getSpecifications(): array + { + return [ + new Specifications( + Operator::EQ, + Specifications::FORMAT_SINGLE, + Specifications::TYPE_STRING + ), + new Specifications( + Operator::IN, + Specifications::FORMAT_ARRAY, + Specifications::TYPE_STRING + ), + ]; + } + + /** + * @param string|array $orientation + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + */ + private function validate($orientation): void + { + if ( + is_string($orientation) + && !$this->isSupportedOrientation($orientation) + ) { + $this->throwException($orientation); + } + + if (is_array($orientation)) { + $invalidOrientations = array_filter( + $orientation, + fn ($value): bool => !$this->isSupportedOrientation($value) + ); + + if (!empty($invalidOrientations)) { + $this->throwException(implode(', ', $invalidOrientations)); + } + } + } + + private function isSupportedOrientation(string $orientation): bool + { + return in_array($orientation, self::ALLOWED_ORIENTATIONS, true); + } + + /** + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + */ + private function throwException(string $whatIsWrong): void + { + throw new InvalidArgumentException( + '$orientation', + sprintf( + 'Invalid image orientation: "%s". Allowed orientations: %s', + $whatIsWrong, + implode(', ', self::ALLOWED_ORIENTATIONS) + ) + ); + } +} diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Image/Width.php b/src/contracts/Repository/Values/Content/Query/Criterion/Image/Width.php new file mode 100644 index 0000000000..4a5293a5e1 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Image/Width.php @@ -0,0 +1,13 @@ +matchAlwaysAvailable = $matchAlwaysAvailable; + parent::__construct(null, null, $value); + } + + public function getSpecifications(): array + { + return [ + new Specifications( + Operator::IN, + Specifications::FORMAT_ARRAY, + Specifications::TYPE_STRING + ), + new Specifications( + Operator::EQ, + Specifications::FORMAT_SINGLE, + Specifications::TYPE_STRING + ), + ]; + } +} + +class_alias(LanguageCode::class, 'eZ\Publish\API\Repository\Values\Content\Query\Criterion\LanguageCode'); diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Location.php b/src/contracts/Repository/Values/Content/Query/Criterion/Location.php new file mode 100644 index 0000000000..dfcaa1a7bc --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Location.php @@ -0,0 +1,20 @@ + $criterion) { + if (!$criterion instanceof Criterion) { + throw new InvalidCriterionArgumentException($key, $criterion, Criterion::class); + } + + $this->criteria[] = $criterion; + } + } + + /** + * @return array + * + * @deprecated in LogicalOperators since 7.2. + * It will be removed in 8.0 when Logical Operator no longer extends Criterion. + */ + public function getSpecifications(): array + { + @trigger_error('The ' . __METHOD__ . ' method is deprecated since version 7.2 and will be removed in 8.0.', E_USER_DEPRECATED); + + throw new NotImplementedException('getSpecifications() not implemented for LogicalOperators'); + } +} + +class_alias(LogicalOperator::class, 'eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOperator'); diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/LogicalOr.php b/src/contracts/Repository/Values/Content/Query/Criterion/LogicalOr.php new file mode 100644 index 0000000000..324f98d4f2 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/LogicalOr.php @@ -0,0 +1,22 @@ +customFields[$type][$field] = $customField; + } + + /** + * Return custom field. + * + * If no custom field is set, return null + * + * @param string $type + * @param string $field + * + * @return ?string + */ + public function getCustomField(string $type, string $field): ?string + { + if (!isset($this->customFields[$type]) || + !isset($this->customFields[$type][$field])) { + return null; + } + + return $this->customFields[$type][$field]; + } +} + +class_alias(MapLocationDistance::class, 'eZ\Publish\API\Repository\Values\Content\Query\Criterion\MapLocationDistance'); diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/MatchAll.php b/src/contracts/Repository/Values/Content/Query/Criterion/MatchAll.php new file mode 100644 index 0000000000..f672690bf7 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/MatchAll.php @@ -0,0 +1,34 @@ +operator = $operator; + $this->valueFormat = $valueFormat; + $this->valueTypes = $valueTypes; + $this->valueCount = $valueCount; + } +} + +class_alias(Specifications::class, 'eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator\Specifications'); diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/ParentLocationId.php b/src/contracts/Repository/Values/Content/Query/Criterion/ParentLocationId.php new file mode 100644 index 0000000000..7630f2044f --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/ParentLocationId.php @@ -0,0 +1,56 @@ +id, $location->parentLocationId); + } +} + +class_alias(Sibling::class, 'eZ\Publish\API\Repository\Values\Content\Query\Criterion\Sibling'); diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Subtree.php b/src/contracts/Repository/Values/Content/Query/Criterion/Subtree.php new file mode 100644 index 0000000000..d6026c81e0 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Subtree.php @@ -0,0 +1,59 @@ + + * $createdCriterion = new Criterion\UserMetadata( + * Criterion\UserMetadata::OWNER, + * Operator::IN, + * array( 10, 14 ) + * ); + * + */ +class UserMetadata extends Criterion implements TrashCriterion, FilteringCriterion +{ + /** + * UserMetadata target: Owner user. + */ + public const OWNER = 'owner'; + + /** + * UserMetadata target: Owner user group. + */ + public const GROUP = 'group'; + + /** + * UserMetadata target: Modifier. + */ + public const MODIFIER = 'modifier'; + + /** + * Creates a new UserMetadata criterion on $metadata. + * + * @throws \InvalidArgumentException If target is unknown + * + * @param string $target One of UserMetadata::OWNER, UserMetadata::GROUP or UserMetadata::MODIFIED + * @param string|null $operator The operator the Criterion uses. If null is given, will default to Operator::IN if $value is an array, Operator::EQ if it is not. + * @param mixed $value The match value, either as an array of as a single value, depending on the operator + */ + public function __construct(string $target, ?string $operator, $value) + { + switch ($target) { + case self::OWNER: + case self::GROUP: + case self::MODIFIER: + parent::__construct($target, $operator, $value); + + return; + } + + throw new InvalidArgumentException("Unknown UserMetadata $target"); + } + + public function getSpecifications(): array + { + return [ + new Specifications( + Operator::EQ, + Specifications::FORMAT_SINGLE, + Specifications::TYPE_INTEGER | Specifications::TYPE_STRING + ), + new Specifications( + Operator::IN, + Specifications::FORMAT_ARRAY, + Specifications::TYPE_INTEGER | Specifications::TYPE_STRING + ), + ]; + } +} + +class_alias(UserMetadata::class, 'eZ\Publish\API\Repository\Values\Content\Query\Criterion\UserMetadata'); diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Value.php b/src/contracts/Repository/Values/Content/Query/Criterion/Value.php new file mode 100644 index 0000000000..1e3767f92e --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Value.php @@ -0,0 +1,18 @@ +longitude = $longitude; } } + +class_alias(MapLocationValue::class, 'eZ\Publish\API\Repository\Values\Content\Query\Criterion\Value\MapLocationValue'); diff --git a/src/contracts/Repository/Values/Content/Query/Criterion/Visibility.php b/src/contracts/Repository/Values/Content/Query/Criterion/Visibility.php new file mode 100644 index 0000000000..889d2b86d2 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/Criterion/Visibility.php @@ -0,0 +1,63 @@ +direction = $sortDirection; + $this->target = $sortTarget; + + if ($targetData !== null) { + $this->targetData = $targetData; + } + } +} + +class_alias(SortClause::class, 'eZ\Publish\API\Repository\Values\Content\Query\SortClause'); diff --git a/src/contracts/Repository/Values/Content/Query/SortClause/ContentId.php b/src/contracts/Repository/Values/Content/Query/SortClause/ContentId.php new file mode 100644 index 0000000000..33113a0cb3 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/SortClause/ContentId.php @@ -0,0 +1,40 @@ +customFields[$type][$field] = $customField; + } + + /** + * Return custom field. + * + * If no custom field is set, return null + * + * @param string $type + * @param string $field + * + * @return mixed + */ + public function getCustomField(string $type, string $field): ?string + { + if (!isset($this->customFields[$type][$field])) { + return null; + } + + return $this->customFields[$type][$field]; + } +} + +class_alias(Field::class, 'eZ\Publish\API\Repository\Values\Content\Query\SortClause\Field'); diff --git a/src/contracts/Repository/Values/Content/Query/SortClause/Location.php b/src/contracts/Repository/Values/Content/Query/SortClause/Location.php new file mode 100644 index 0000000000..af1c901e6a --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/SortClause/Location.php @@ -0,0 +1,20 @@ +customFields[$type][$field] = $customField; + } + + /** + * Return custom field. + * + * If no custom field is set, return null + * + * @param string $type + * @param string $field + * + * @return mixed + */ + public function getCustomField(string $type, string $field): ?string + { + if (!isset($this->customFields[$type]) || + !isset($this->customFields[$type][$field])) { + return null; + } + + return $this->customFields[$type][$field]; + } +} + +class_alias(MapLocationDistance::class, 'eZ\Publish\API\Repository\Values\Content\Query\SortClause\MapLocationDistance'); diff --git a/src/contracts/Repository/Values/Content/Query/SortClause/Random.php b/src/contracts/Repository/Values/Content/Query/SortClause/Random.php new file mode 100644 index 0000000000..14b1c9d073 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/SortClause/Random.php @@ -0,0 +1,29 @@ +fieldName = $fieldName; + } +} + +class_alias(CustomFieldTarget::class, 'eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target\CustomFieldTarget'); diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target/FieldTarget.php b/src/contracts/Repository/Values/Content/Query/SortClause/Target/FieldTarget.php similarity index 75% rename from eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target/FieldTarget.php rename to src/contracts/Repository/Values/Content/Query/SortClause/Target/FieldTarget.php index 9e430dfd4b..579f64ca55 100644 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target/FieldTarget.php +++ b/src/contracts/Repository/Values/Content/Query/SortClause/Target/FieldTarget.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target; +namespace Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Target; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Target; /** * Struct that stores extra target informations for a SortClause object. @@ -35,3 +35,5 @@ public function __construct(string $typeIdentifier, string $fieldIdentifier) $this->fieldIdentifier = $fieldIdentifier; } } + +class_alias(FieldTarget::class, 'eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target\FieldTarget'); diff --git a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target/MapLocationTarget.php b/src/contracts/Repository/Values/Content/Query/SortClause/Target/MapLocationTarget.php similarity index 82% rename from eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target/MapLocationTarget.php rename to src/contracts/Repository/Values/Content/Query/SortClause/Target/MapLocationTarget.php index 7c966299d3..07439e846d 100644 --- a/eZ/Publish/API/Repository/Values/Content/Query/SortClause/Target/MapLocationTarget.php +++ b/src/contracts/Repository/Values/Content/Query/SortClause/Target/MapLocationTarget.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target; +namespace Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Target; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Target; /** * Struct that stores extra value information for a MapLocationDistance SortClause object. @@ -61,3 +61,5 @@ public function __construct( $this->fieldIdentifier = $fieldIdentifier; } } + +class_alias(MapLocationTarget::class, 'eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target\MapLocationTarget'); diff --git a/src/contracts/Repository/Values/Content/Query/SortClause/Target/RandomTarget.php b/src/contracts/Repository/Values/Content/Query/SortClause/Target/RandomTarget.php new file mode 100644 index 0000000000..a9aa814aa6 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/SortClause/Target/RandomTarget.php @@ -0,0 +1,32 @@ +seed = $seed; + } +} + +class_alias(RandomTarget::class, 'eZ\Publish\API\Repository\Values\Content\Query\SortClause\Target\RandomTarget'); diff --git a/src/contracts/Repository/Values/Content/Query/SortClause/Trash/ContentTypeName.php b/src/contracts/Repository/Values/Content/Query/SortClause/Trash/ContentTypeName.php new file mode 100644 index 0000000000..f7c0556f1b --- /dev/null +++ b/src/contracts/Repository/Values/Content/Query/SortClause/Trash/ContentTypeName.php @@ -0,0 +1,23 @@ +query = $query; + } + + public function getQuery(): string + { + return $this->query; + } +} diff --git a/src/contracts/Repository/Values/Content/Relation.php b/src/contracts/Repository/Values/Content/Relation.php new file mode 100644 index 0000000000..0ba1694316 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Relation.php @@ -0,0 +1,103 @@ +items); + } +} + +class_alias(RelationList::class, 'eZ\Publish\API\Repository\Values\Content\RelationList'); diff --git a/src/contracts/Repository/Values/Content/RelationList/Item/RelationListItem.php b/src/contracts/Repository/Values/Content/RelationList/Item/RelationListItem.php new file mode 100644 index 0000000000..bb78a2d18a --- /dev/null +++ b/src/contracts/Repository/Values/Content/RelationList/Item/RelationListItem.php @@ -0,0 +1,38 @@ +relation = $relation; + } + + public function getRelation(): ?Relation + { + return $this->relation; + } + + public function hasRelation(): bool + { + return true; + } +} + +class_alias(RelationListItem::class, 'eZ\Publish\API\Repository\Values\Content\RelationList\Item\RelationListItem'); diff --git a/src/contracts/Repository/Values/Content/RelationList/Item/UnauthorizedRelationListItem.php b/src/contracts/Repository/Values/Content/RelationList/Item/UnauthorizedRelationListItem.php new file mode 100644 index 0000000000..45a24f72d3 --- /dev/null +++ b/src/contracts/Repository/Values/Content/RelationList/Item/UnauthorizedRelationListItem.php @@ -0,0 +1,31 @@ +name = $name; + } + + public function getName(): string + { + return $this->name; + } +} + +class_alias(AggregationResult::class, 'eZ\Publish\API\Repository\Values\Content\Search\AggregationResult'); diff --git a/src/contracts/Repository/Values/Content/Search/AggregationResult/RangeAggregationResult.php b/src/contracts/Repository/Values/Content/Search/AggregationResult/RangeAggregationResult.php new file mode 100644 index 0000000000..8f7d98908e --- /dev/null +++ b/src/contracts/Repository/Values/Content/Search/AggregationResult/RangeAggregationResult.php @@ -0,0 +1,91 @@ +entries = $entries; + } + + public function isEmpty(): bool + { + return empty($this->entries); + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry[] + */ + public function getEntries(): iterable + { + return $this->entries; + } + + public function getEntry(Range $key): ?RangeAggregationResultEntry + { + foreach ($this->entries as $entry) { + if ($entry->getKey() == $key) { + return $entry; + } + } + + return null; + } + + public function hasEntry(Range $key): bool + { + return $this->getEntry($key) !== null; + } + + /** + * Return available keys (ranges). + * + * @return iterable<\Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range> + */ + public function getKeys(): iterable + { + foreach ($this->entries as $entry) { + yield $entry->getKey(); + } + } + + public function count(): int + { + return count($this->entries); + } + + public function getIterator(): Iterator + { + if (empty($this->entries)) { + return new ArrayIterator(); + } + + foreach ($this->entries as $entry) { + yield $entry->getKey() => $entry->getCount(); + } + } +} + +class_alias(RangeAggregationResult::class, 'eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult'); diff --git a/src/contracts/Repository/Values/Content/Search/AggregationResult/RangeAggregationResultEntry.php b/src/contracts/Repository/Values/Content/Search/AggregationResult/RangeAggregationResultEntry.php new file mode 100644 index 0000000000..bc461f4631 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Search/AggregationResult/RangeAggregationResultEntry.php @@ -0,0 +1,41 @@ +key = $key; + $this->count = $count; + } + + public function getKey(): Range + { + return $this->key; + } + + public function getCount(): int + { + return $this->count; + } +} + +class_alias(RangeAggregationResultEntry::class, 'eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry'); diff --git a/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/StatsAggregationResult.php b/src/contracts/Repository/Values/Content/Search/AggregationResult/StatsAggregationResult.php similarity index 80% rename from eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/StatsAggregationResult.php rename to src/contracts/Repository/Values/Content/Search/AggregationResult/StatsAggregationResult.php index 2512538192..da744bba84 100644 --- a/eZ/Publish/API/Repository/Values/Content/Search/AggregationResult/StatsAggregationResult.php +++ b/src/contracts/Repository/Values/Content/Search/AggregationResult/StatsAggregationResult.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\Content\Search\AggregationResult; +namespace Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult; final class StatsAggregationResult extends AggregationResult { @@ -63,3 +63,5 @@ public function getSum(): ?float return $this->sum; } } + +class_alias(StatsAggregationResult::class, 'eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\StatsAggregationResult'); diff --git a/src/contracts/Repository/Values/Content/Search/AggregationResult/TermAggregationResult.php b/src/contracts/Repository/Values/Content/Search/AggregationResult/TermAggregationResult.php new file mode 100644 index 0000000000..cf2f0e4412 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Search/AggregationResult/TermAggregationResult.php @@ -0,0 +1,99 @@ +entries = $entries; + } + + public function isEmpty(): bool + { + return empty($this->entries); + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry[] + */ + public function getEntries(): iterable + { + return $this->entries; + } + + /** + * @param object|string|int $key + */ + public function getEntry($key): ?TermAggregationResultEntry + { + foreach ($this->entries as $entry) { + if ($entry->getKey() == $key) { + return $entry; + } + } + + return null; + } + + /** + * @param object|string|int $key + */ + public function hasEntry($key): bool + { + return $this->getEntry($key) !== null; + } + + /** + * Returns available keys (terms). + * + * @return iterable + */ + public function getKeys(): iterable + { + foreach ($this->entries as $entry) { + yield $entry->getKey(); + } + } + + public function count(): int + { + return count($this->entries); + } + + public function getIterator(): Iterator + { + if (empty($this->entries)) { + return new ArrayIterator(); + } + + foreach ($this->entries as $entry) { + yield $entry->getKey() => $entry->getCount(); + } + } + + public static function createForAggregation(Aggregation $aggregation, iterable $entries = []): self + { + return new self($aggregation->getName(), $entries); + } +} + +class_alias(TermAggregationResult::class, 'eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResult'); diff --git a/src/contracts/Repository/Values/Content/Search/AggregationResult/TermAggregationResultEntry.php b/src/contracts/Repository/Values/Content/Search/AggregationResult/TermAggregationResultEntry.php new file mode 100644 index 0000000000..b97b643202 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Search/AggregationResult/TermAggregationResultEntry.php @@ -0,0 +1,43 @@ +key = $key; + $this->count = $count; + } + + /** + * @return mixed + */ + public function getKey() + { + return $this->key; + } + + public function getCount(): int + { + return $this->count; + } +} + +class_alias(TermAggregationResultEntry::class, 'eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry'); diff --git a/src/contracts/Repository/Values/Content/Search/AggregationResultAwareInterface.php b/src/contracts/Repository/Values/Content/Search/AggregationResultAwareInterface.php new file mode 100644 index 0000000000..0be479ccb8 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Search/AggregationResultAwareInterface.php @@ -0,0 +1,14 @@ +entries); } } + +class_alias(AggregationResultCollection::class, 'eZ\Publish\API\Repository\Values\Content\Search\AggregationResultCollection'); diff --git a/src/contracts/Repository/Values/Content/Search/Facet.php b/src/contracts/Repository/Values/Content/Search/Facet.php new file mode 100644 index 0000000000..8ff27831de --- /dev/null +++ b/src/contracts/Repository/Values/Content/Search/Facet.php @@ -0,0 +1,28 @@ +performCount was set to false and search engine avoids search lookup. + * + * @var int|null + */ + public $totalCount; + + public function __construct(array $properties = []) + { + if (!isset($properties['aggregations'])) { + $properties['aggregations'] = new AggregationResultCollection(); + } + + parent::__construct($properties); + } + + public function getSpellcheck(): ?SpellcheckResult + { + return $this->spellcheck; + } + + public function getAggregations(): ?AggregationResultCollection + { + return $this->aggregations; + } + + public function getIterator(): Iterator + { + return new ArrayIterator($this->searchHits); + } +} + +class_alias(SearchResult::class, 'eZ\Publish\API\Repository\Values\Content\Search\SearchResult'); diff --git a/src/contracts/Repository/Values/Content/Search/SpellcheckResult.php b/src/contracts/Repository/Values/Content/Search/SpellcheckResult.php new file mode 100644 index 0000000000..952095d17f --- /dev/null +++ b/src/contracts/Repository/Values/Content/Search/SpellcheckResult.php @@ -0,0 +1,38 @@ +query = $query; + $this->incorrect = $incorrect; + } + + public function getQuery(): ?string + { + return $this->query; + } + + public function isIncorrect(): bool + { + return $this->incorrect; + } +} diff --git a/src/contracts/Repository/Values/Content/Section.php b/src/contracts/Repository/Values/Content/Section.php new file mode 100644 index 0000000000..959b5ef953 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Section.php @@ -0,0 +1,44 @@ +identifier string value. + * + * @var string + */ + public $identifier; + + /** + * If set the name of the section is changed. + * + * @var string + */ + public $name; +} + +class_alias(SectionStruct::class, 'eZ\Publish\API\Repository\Values\Content\SectionStruct'); diff --git a/src/contracts/Repository/Values/Content/SectionUpdateStruct.php b/src/contracts/Repository/Values/Content/SectionUpdateStruct.php new file mode 100644 index 0000000000..f5f57cba0e --- /dev/null +++ b/src/contracts/Repository/Values/Content/SectionUpdateStruct.php @@ -0,0 +1,18 @@ +count = $properties['totalCount']; + } + + parent::__construct($properties); + } + + /** + * The total number of Trash items. + * + * @var int + */ + public $totalCount = 0; + + /** + * The total number of Trash items. + * + * @deprecated Property is here purely for BC with 5.x/6.x. + * + * @var int + */ + public $count = 0; + + /** + * The Trash items found for the query. + * + * @var \Ibexa\Contracts\Core\Repository\Values\Content\TrashItem[] + */ + public $items = []; + + public function getIterator(): Traversable + { + return new ArrayIterator($this->items); + } +} + +class_alias(SearchResult::class, 'eZ\Publish\API\Repository\Values\Content\Trash\SearchResult'); diff --git a/src/contracts/Repository/Values/Content/Trash/TrashItemDeleteResult.php b/src/contracts/Repository/Values/Content/Trash/TrashItemDeleteResult.php new file mode 100644 index 0000000000..b0f4689a8a --- /dev/null +++ b/src/contracts/Repository/Values/Content/Trash/TrashItemDeleteResult.php @@ -0,0 +1,32 @@ + */ + public $reverseRelationContentIds = []; +} + +class_alias(TrashItemDeleteResult::class, 'eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult'); diff --git a/src/contracts/Repository/Values/Content/Trash/TrashItemDeleteResultList.php b/src/contracts/Repository/Values/Content/Trash/TrashItemDeleteResultList.php new file mode 100644 index 0000000000..16a87041a4 --- /dev/null +++ b/src/contracts/Repository/Values/Content/Trash/TrashItemDeleteResultList.php @@ -0,0 +1,29 @@ +items); + } +} + +class_alias(TrashItemDeleteResultList::class, 'eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResultList'); diff --git a/src/contracts/Repository/Values/Content/TrashItem.php b/src/contracts/Repository/Values/Content/TrashItem.php new file mode 100644 index 0000000000..3e4492d6c4 --- /dev/null +++ b/src/contracts/Repository/Values/Content/TrashItem.php @@ -0,0 +1,24 @@ +sourceUrl = $sourceUrl; diff --git a/src/contracts/Repository/Values/Content/URLWildcard/Query/Criterion/Type.php b/src/contracts/Repository/Values/Content/URLWildcard/Query/Criterion/Type.php index fbfcae9143..4f08eff086 100644 --- a/src/contracts/Repository/Values/Content/URLWildcard/Query/Criterion/Type.php +++ b/src/contracts/Repository/Values/Content/URLWildcard/Query/Criterion/Type.php @@ -13,8 +13,7 @@ */ final class Type extends Matcher { - /** @var bool */ - public $forward; + public bool $forward; public function __construct(bool $forward) { diff --git a/src/contracts/Repository/Values/Content/URLWildcard/Query/SortClause.php b/src/contracts/Repository/Values/Content/URLWildcard/Query/SortClause.php index 0ad4507218..b46935139a 100644 --- a/src/contracts/Repository/Values/Content/URLWildcard/Query/SortClause.php +++ b/src/contracts/Repository/Values/Content/URLWildcard/Query/SortClause.php @@ -8,7 +8,7 @@ namespace Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; /** * This class is the base for SortClause classes, used to set sorting of URL queries. @@ -18,16 +18,14 @@ abstract class SortClause public const SORT_ASC = 'ascending'; public const SORT_DESC = 'descending'; - /** @var string */ - public $direction = self::SORT_ASC; + public string $direction = self::SORT_ASC; - /** @var string */ - public $target; + public string $target; /** * @phpstan-param SortClause::SORT_* $sortDirection * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException if the given sort order isn't one of SortClause::SORT_ASC or SortClause::SORT_DESC + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the given sort order isn't one of SortClause::SORT_ASC or SortClause::SORT_DESC */ public function __construct(string $sortTarget, string $sortDirection) { diff --git a/src/contracts/Repository/Values/Content/URLWildcard/SearchResult.php b/src/contracts/Repository/Values/Content/URLWildcard/SearchResult.php index 3b3481b904..3ff7b6dd5e 100644 --- a/src/contracts/Repository/Values/Content/URLWildcard/SearchResult.php +++ b/src/contracts/Repository/Values/Content/URLWildcard/SearchResult.php @@ -9,7 +9,7 @@ namespace Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard; use ArrayIterator; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; use IteratorAggregate; use Traversable; @@ -17,17 +17,15 @@ class SearchResult extends ValueObject implements IteratorAggregate { /** * The total number of URLs. - * - * @var int|null */ - public $totalCount = 0; + public ?int $totalCount = 0; /** * The value objects found for the query. * - * @var \eZ\Publish\API\Repository\Values\Content\URLWildcard[] + * @var \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard[] */ - public $items = []; + public array $items = []; /** * {@inheritdoc} diff --git a/src/contracts/Repository/Values/Content/URLWildcard/URLWildcardQuery.php b/src/contracts/Repository/Values/Content/URLWildcard/URLWildcardQuery.php index 61ce4fac94..38f9cd7e25 100644 --- a/src/contracts/Repository/Values/Content/URLWildcard/URLWildcardQuery.php +++ b/src/contracts/Repository/Values/Content/URLWildcard/URLWildcardQuery.php @@ -8,7 +8,8 @@ namespace Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; /** * This class is used to perform a URLWildcard query. @@ -17,41 +18,33 @@ class URLWildcardQuery extends ValueObject { /** * The Query filter. - * - * @var \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\Criterion */ - public $filter; + public Criterion $filter; /** * Query sorting clauses. * * @var \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\SortClause[] */ - public $sortClauses = []; + public array $sortClauses = []; /** * Query offset. * * Sets the offset for search hits, used for paging the results. - * - * @var int */ - public $offset = 0; + public int $offset = 0; /** * Query limit. * * Limit for number of search hits to return. * If value is `0`, search query will not return any search hits, useful for doing a count. - * - * @var int */ - public $limit = 25; + public int $limit = 25; /** * If true, search engine should perform count even if that means extra lookup. - * - * @var bool */ - public $performCount = true; + public bool $performCount = true; } diff --git a/src/contracts/Repository/Values/Content/URLWildcardStruct.php b/src/contracts/Repository/Values/Content/URLWildcardStruct.php new file mode 100644 index 0000000000..52633c113d --- /dev/null +++ b/src/contracts/Repository/Values/Content/URLWildcardStruct.php @@ -0,0 +1,25 @@ + + */ + public function getLanguageCodes(): array + { + return $this->languageCodes; + } + + public function getVersionNo(): int + { + return $this->versionNo; + } + + /** + * Returns true if version is a draft. + * + * @return bool + */ + public function isDraft(): bool + { + return $this->status === self::STATUS_DRAFT; + } + + /** + * Returns true if version is published. + * + * @return bool + */ + public function isPublished(): bool + { + return $this->status === self::STATUS_PUBLISHED; + } + + /** + * Returns true if version is archived. + * + * @return bool + */ + public function isArchived(): bool + { + return $this->status === self::STATUS_ARCHIVED; + } +} + +class_alias(VersionInfo::class, 'eZ\Publish\API\Repository\Values\Content\VersionInfo'); diff --git a/src/contracts/Repository/Values/ContentType/ContentType.php b/src/contracts/Repository/Values/ContentType/ContentType.php new file mode 100644 index 0000000000..b76e351963 --- /dev/null +++ b/src/contracts/Repository/Values/ContentType/ContentType.php @@ -0,0 +1,243 @@ +. + * An OR condition can be used : + * + * In this example, field_def will be used if available. If not, other_field_def will be used for content name generation + * + * @var string + */ + protected $nameSchema; + + /** + * A flag used to hint if content of this type may have children or not. It is highly recommended to respect this flag and not create/move content below non-containers. + * But this flag is not considered as part of the content model and the API will not in any way enforce this flag to be respected. + * + * @var bool + */ + protected $isContainer; + + /** + * If an instance of a content type is created the always available flag is set + * by default to this value. + * + * @var bool + */ + protected $defaultAlwaysAvailable = true; + + /** + * Specifies which property the child locations should be sorted on by default when created. + * + * Valid values are found at {@link Location::SORT_FIELD_*} + * + * @var int + */ + protected $defaultSortField; + + /** + * Specifies whether the sort order should be ascending or descending by default when created. + * + * Valid values are {@link Location::SORT_ORDER_*} + * + * @var int + */ + protected $defaultSortOrder; + + /** + * List of language codes used by translations. + * + * @var string[] + */ + protected $languageCodes; + + /** + * This method returns the content type groups this content type is assigned to. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup[] + */ + abstract public function getContentTypeGroups(); + + /** + * This method returns the content type field definitions from this type. + */ + abstract public function getFieldDefinitions(): FieldDefinitionCollection; + + public function getIdentifier(): string + { + return $this->identifier; + } + + /** + * This method returns the field definition for the given identifier. + * + * @param string $fieldDefinitionIdentifier + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition|null + */ + public function getFieldDefinition($fieldDefinitionIdentifier): ?FieldDefinition + { + if ($this->hasFieldDefinition($fieldDefinitionIdentifier)) { + return $this->getFieldDefinitions()->get($fieldDefinitionIdentifier); + } + + return null; + } + + /** + * This method returns true if the field definition for the given identifier exists. + */ + public function hasFieldDefinition(string $fieldDefinitionIdentifier): bool + { + return $this->getFieldDefinitions()->has($fieldDefinitionIdentifier); + } + + /** + * Returns true if field definition with given field type identifier exists. + */ + public function hasFieldDefinitionOfType(string $fieldTypeIdentifier): bool + { + return $this->getFieldDefinitions()->anyOfType($fieldTypeIdentifier); + } + + /** + * Returns collection of the field definition for the given field type identifier. + */ + public function getFieldDefinitionsOfType(string $fieldTypeIdentifier): FieldDefinitionCollection + { + return $this->getFieldDefinitions()->filterByType($fieldTypeIdentifier); + } + + /** + * Returns true if field definition with given field type identifier or null. + */ + public function getFirstFieldDefinitionOfType(string $fieldTypeIdentifier): ?FieldDefinition + { + $fieldDefinitionsOfType = $this->getFieldDefinitionsOfType($fieldTypeIdentifier); + if (!$fieldDefinitionsOfType->isEmpty()) { + return $fieldDefinitionsOfType->first(); + } + + return null; + } + + public function isContainer(): bool + { + return $this->isContainer; + } +} + +class_alias(ContentType::class, 'eZ\Publish\API\Repository\Values\ContentType\ContentType'); diff --git a/src/contracts/Repository/Values/ContentType/ContentTypeCreateStruct.php b/src/contracts/Repository/Values/ContentType/ContentTypeCreateStruct.php new file mode 100644 index 0000000000..068651a572 --- /dev/null +++ b/src/contracts/Repository/Values/ContentType/ContentTypeCreateStruct.php @@ -0,0 +1,131 @@ +id; + } + + public function getFieldGroup(): string + { + return $this->fieldGroup; + } + + public function getPosition(): int + { + return $this->position; + } + + public function isTranslatable(): bool + { + return $this->isTranslatable; + } + + public function isRequired(): bool + { + return $this->isRequired; + } + + public function isInfoCollector(): bool + { + return $this->isInfoCollector; + } + + /** + * @return mixed + */ + public function getDefaultValue() + { + return $this->defaultValue; + } + + public function isSearchable(): bool + { + return $this->isSearchable; + } + + public function getMainLanguageCode(): string + { + return $this->mainLanguageCode; + } + + public function isThumbnail(): bool + { + return $this->isThumbnail; + } + + public function getIdentifier(): string + { + return $this->identifier; + } + + public function getFieldTypeIdentifier(): string + { + return $this->fieldTypeIdentifier; + } +} + +class_alias(FieldDefinition::class, 'eZ\Publish\API\Repository\Values\ContentType\FieldDefinition'); diff --git a/src/contracts/Repository/Values/ContentType/FieldDefinitionCollection.php b/src/contracts/Repository/Values/ContentType/FieldDefinitionCollection.php new file mode 100644 index 0000000000..f43b4c749d --- /dev/null +++ b/src/contracts/Repository/Values/ContentType/FieldDefinitionCollection.php @@ -0,0 +1,115 @@ + + */ +interface FieldDefinitionCollection extends Countable, IteratorAggregate, ArrayAccess +{ + /** + * This method returns the field definition for the given identifier. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\OutOfBoundsException + */ + public function get(string $fieldDefinitionIdentifier): FieldDefinition; + + /** + * This method returns true if the field definition for the given identifier exists. + */ + public function has(string $fieldDefinitionIdentifier): bool; + + /** + * Return first element of collection. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\OutOfBoundsException + */ + public function first(): FieldDefinition; + + /** + * Return last element of collection. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\OutOfBoundsException + */ + public function last(): FieldDefinition; + + /** + * Checks whether the collection is empty (contains no elements). + * + * @return bool TRUE if the collection is empty, FALSE otherwise. + */ + public function isEmpty(): bool; + + /** + * Returns all the elements of this collection that satisfy the predicate p. + * The order of the elements is preserved. + */ + public function filter(Closure $predicate): FieldDefinitionCollection; + + /** + * Returns field definitions with given field type identifier. + */ + public function filterByType(string $fieldTypeIdentifier): FieldDefinitionCollection; + + /** + * Returns field definitions with given group. + */ + public function filterByGroup(string $fieldGroup): FieldDefinitionCollection; + + /** + * Applies the given function to each element in the collection and returns + * a new collection with the elements returned by the function. + */ + public function map(Closure $predicate): array; + + /** + * Tests whether the given predicate holds for all elements of this collection. + */ + public function all(Closure $predicate): bool; + + /** + * Tests for the existence of an element that satisfies the given predicate. + */ + public function any(Closure $predicate): bool; + + /** + * Tests for the existence of an field definition with given field type identifier. + */ + public function anyOfType(string $fieldTypeIdentifier): bool; + + /** + * Tests for the existence of an field definition in given field group. + */ + public function anyInGroup(string $fieldGroup): bool; + + /** + * Partitions this collection in two collections according to a predicate. + * + * Result is an array with two elements. The first element contains the collection + * of elements where the predicate returned TRUE, the second element + * contains the collection of elements where the predicate returned FALSE. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCollection[] + */ + public function partition(Closure $predicate): array; + + /** + * Gets a native PHP array representation of the collection. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition[] + */ + public function toArray(): array; +} + +class_alias(FieldDefinitionCollection::class, 'eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCollection'); diff --git a/eZ/Publish/API/Repository/Values/ContentType/FieldDefinitionCreateStruct.php b/src/contracts/Repository/Values/ContentType/FieldDefinitionCreateStruct.php similarity index 87% rename from eZ/Publish/API/Repository/Values/ContentType/FieldDefinitionCreateStruct.php rename to src/contracts/Repository/Values/ContentType/FieldDefinitionCreateStruct.php index 8e3d695331..c4763233dc 100644 --- a/eZ/Publish/API/Repository/Values/ContentType/FieldDefinitionCreateStruct.php +++ b/src/contracts/Repository/Values/ContentType/FieldDefinitionCreateStruct.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\ContentType; +namespace Ibexa\Contracts\Core\Repository\Values\ContentType; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; /** * this class is used to create a field definition. @@ -27,7 +27,7 @@ class FieldDefinitionCreateStruct extends ValueObject /** * Readable string identifier of a field definition. * - * Needs to be unique within the context of the Content Type this is added to. + * Needs to be unique within the context of the content type this is added to. * * Required. * @@ -120,3 +120,5 @@ class FieldDefinitionCreateStruct extends ValueObject */ public $isSearchable; } + +class_alias(FieldDefinitionCreateStruct::class, 'eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct'); diff --git a/eZ/Publish/API/Repository/Values/ContentType/FieldDefinitionUpdateStruct.php b/src/contracts/Repository/Values/ContentType/FieldDefinitionUpdateStruct.php similarity index 87% rename from eZ/Publish/API/Repository/Values/ContentType/FieldDefinitionUpdateStruct.php rename to src/contracts/Repository/Values/ContentType/FieldDefinitionUpdateStruct.php index 6a6d2539bc..d2bb8c0d35 100644 --- a/eZ/Publish/API/Repository/Values/ContentType/FieldDefinitionUpdateStruct.php +++ b/src/contracts/Repository/Values/ContentType/FieldDefinitionUpdateStruct.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\ContentType; +namespace Ibexa\Contracts\Core\Repository\Values\ContentType; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; /** * this class is used to update a field definition. @@ -18,7 +18,7 @@ class FieldDefinitionUpdateStruct extends ValueObject /** * If set the identifier of a field definition is changed to this value. * - * Needs to be unique within the context of the Content Type this is added to. + * Needs to be unique within the context of the content type this is added to. * * @var string */ @@ -108,3 +108,5 @@ class FieldDefinitionUpdateStruct extends ValueObject */ public $isSearchable; } + +class_alias(FieldDefinitionUpdateStruct::class, 'eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct'); diff --git a/eZ/Publish/SPI/Repository/Values/Filter/CriterionQueryBuilder.php b/src/contracts/Repository/Values/Filter/CriterionQueryBuilder.php similarity index 76% rename from eZ/Publish/SPI/Repository/Values/Filter/CriterionQueryBuilder.php rename to src/contracts/Repository/Values/Filter/CriterionQueryBuilder.php index 23e480cea8..ca3a57b47d 100644 --- a/eZ/Publish/SPI/Repository/Values/Filter/CriterionQueryBuilder.php +++ b/src/contracts/Repository/Values/Filter/CriterionQueryBuilder.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Values\Filter; +namespace Ibexa\Contracts\Core\Repository\Values\Filter; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; +use Ibexa\Contracts\Core\Persistence\Filter\Doctrine\FilteringQueryBuilder; /** * Extension point to build filtering query for a given Criterion. @@ -26,3 +26,5 @@ public function accepts(FilteringCriterion $criterion): bool; */ public function buildQueryConstraint(FilteringQueryBuilder $queryBuilder, FilteringCriterion $criterion): ?string; } + +class_alias(CriterionQueryBuilder::class, 'eZ\Publish\SPI\Repository\Values\Filter\CriterionQueryBuilder'); diff --git a/eZ/Publish/API/Repository/Values/Filter/Filter.php b/src/contracts/Repository/Values/Filter/Filter.php similarity index 84% rename from eZ/Publish/API/Repository/Values/Filter/Filter.php rename to src/contracts/Repository/Values/Filter/Filter.php index c475a6aa9b..5c2403d76a 100644 --- a/eZ/Publish/API/Repository/Values/Filter/Filter.php +++ b/src/contracts/Repository/Values/Filter/Filter.php @@ -6,13 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\Filter; +namespace Ibexa\Contracts\Core\Repository\Values\Filter; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Core\Base\Exceptions\BadStateException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; use function sprintf; /** @@ -20,10 +18,10 @@ */ final class Filter { - /** @var \eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion|null */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Filter\FilteringCriterion|null */ private $criterion; - /** @var \eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause[] */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Filter\FilteringSortClause[] */ private $sortClauses = []; /** @var int */ @@ -37,7 +35,7 @@ final class Filter * * It's recommended to skip arguments and use `with...` and `andWith...` methods to build Filter. * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException for invalid Sort Clause + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException for invalid Sort Clause */ public function __construct(?FilteringCriterion $criterion = null, array $sortClauses = []) { @@ -78,13 +76,13 @@ public function reset(): self * * To re-build Criterion from scratch `reset` it first. * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException if Criterion is already set + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if Criterion is already set * * @see reset * @see andWithCriterion * @see orWithCriterion - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOr - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd + * @see \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalOr + * @see \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalAnd */ public function withCriterion(FilteringCriterion $criterion): self { @@ -163,7 +161,7 @@ public function withLimit(int $limit): self * * @param int $limit >=0, use 0 for no limit. * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function sliceBy(int $limit, int $offset): self { @@ -193,7 +191,7 @@ public function getCriterion(): ?FilteringCriterion } /** - * @return \eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause[] + * @return \Ibexa\Contracts\Core\Repository\Values\Filter\FilteringSortClause[] */ public function getSortClauses(): array { @@ -231,3 +229,5 @@ static function (FilteringSortClause $sortClause): FilteringSortClause { ); } } + +class_alias(Filter::class, 'eZ\Publish\API\Repository\Values\Filter\Filter'); diff --git a/src/contracts/Repository/Values/Filter/FilteringCriterion.php b/src/contracts/Repository/Values/Filter/FilteringCriterion.php new file mode 100644 index 0000000000..5e30e9d632 --- /dev/null +++ b/src/contracts/Repository/Values/Filter/FilteringCriterion.php @@ -0,0 +1,18 @@ +items); + } +} + +class_alias(NotificationList::class, 'eZ\Publish\API\Repository\Values\Notification\NotificationList'); diff --git a/src/contracts/Repository/Values/ObjectState/ObjectState.php b/src/contracts/Repository/Values/ObjectState/ObjectState.php new file mode 100644 index 0000000000..f4c8c6e331 --- /dev/null +++ b/src/contracts/Repository/Values/ObjectState/ObjectState.php @@ -0,0 +1,62 @@ +value = $value; } } + +class_alias(SettingCreateStruct::class, 'eZ\Publish\API\Repository\Values\Setting\SettingCreateStruct'); diff --git a/src/contracts/Repository/Values/Setting/SettingUpdateStruct.php b/src/contracts/Repository/Values/Setting/SettingUpdateStruct.php new file mode 100644 index 0000000000..59820c268e --- /dev/null +++ b/src/contracts/Repository/Values/Setting/SettingUpdateStruct.php @@ -0,0 +1,22 @@ +value = $value; + } +} + +class_alias(SettingUpdateStruct::class, 'eZ\Publish\API\Repository\Values\Setting\SettingUpdateStruct'); diff --git a/src/contracts/Repository/Values/Token/Token.php b/src/contracts/Repository/Values/Token/Token.php new file mode 100644 index 0000000000..93ea650555 --- /dev/null +++ b/src/contracts/Repository/Values/Token/Token.php @@ -0,0 +1,91 @@ +id = $id; + $this->type = $type; + $this->token = $token; + $this->identifier = $identifier; + $this->created = $created; + $this->expires = $expires; + $this->revoked = $revoked; + } + + public function getId(): int + { + return $this->id; + } + + public function getType(): string + { + return $this->type; + } + + public function getToken(): string + { + return $this->token; + } + + public function getIdentifier(): ?string + { + return $this->identifier; + } + + public function getCreated(): DateTimeImmutable + { + return $this->created; + } + + public function getExpires(): DateTimeImmutable + { + return $this->expires; + } + + public function isRevoked(): bool + { + return $this->revoked; + } + + public function __toString(): string + { + return $this->token; + } +} diff --git a/src/contracts/Repository/Values/Translation.php b/src/contracts/Repository/Values/Translation.php new file mode 100644 index 0000000000..76a3d9a0bd --- /dev/null +++ b/src/contracts/Repository/Values/Translation.php @@ -0,0 +1,20 @@ + + */ + protected array $values; + + /** + * Construct singular only message from string and optional value array. + * + * @param array $values + */ + public function __construct(string $message, array $values = []) + { + $this->message = $message; + $this->values = $values; + + parent::__construct(); + } + + public function __toString() + { + return strtr($this->message, $this->values); + } +} + +class_alias(Message::class, 'eZ\Publish\API\Repository\Values\Translation\Message'); diff --git a/src/contracts/Repository/Values/Translation/Plural.php b/src/contracts/Repository/Values/Translation/Plural.php new file mode 100644 index 0000000000..720c8f5347 --- /dev/null +++ b/src/contracts/Repository/Values/Translation/Plural.php @@ -0,0 +1,77 @@ +%[A-Za-z]%. Those are + * replaced by the values provided. A raw % can be escaped like %%. + * + * You need to provide a singular and plural variant for the string. The + * strings provided should be English and will be translated depending on the + * environment language. + * + * This interface follows the interfaces of XLIFF, gettext, Symfony Translations and Zend_Translate. + * For singular forms you just provide a plain string (with optional placeholders without effects on the plural forms). + * For potential plural forms you always provide a singular variant and an English simple plural variant. + * An instance of this class can be cast to a string. In such case whether to use singular or plural form is determined + * based on the value of first element of $values array (it needs to be 1 for singular, anything else for plural). + * If plurality cannot be inferred from $values, a plural form is assumed as default. To force singular form, + * use {@see \Ibexa\Contracts\Core\Repository\Values\Translation\Message} instead. + * + * No implementation supports multiple different plural forms in one single message. + * + * The singular / plural string could, for Symfony, for example be converted + * to "$singular|$plural", and you would call gettext like: ngettext($singular, $plural, $count ). + */ +class Plural extends Translation +{ + /** + * Singular string. Might use replacements like %foo%, which are replaced by + * the values specified in the values array. + */ + protected string $singular; + + /** + * Message string. Might use replacements like %foo%, which are replaced by + * the values specified in the values array. + */ + protected string $plural; + + /** + * Translation value objects. + * + * @var array + */ + protected array $values; + + /** + * Construct plural message from singular, plural and value array. + * + * @param array $values + */ + public function __construct(string $singular, string $plural, array $values) + { + $this->singular = $singular; + $this->plural = $plural; + $this->values = $values; + + parent::__construct(); + } + + public function __toString() + { + $firstValue = !empty($this->values) ? current(array_values($this->values)) : null; + + return strtr((int)$firstValue === 1 ? $this->singular : $this->plural, $this->values); + } +} + +class_alias(Plural::class, 'eZ\Publish\API\Repository\Values\Translation\Plural'); diff --git a/src/contracts/Repository/Values/Trash/Query/Criterion.php b/src/contracts/Repository/Values/Trash/Query/Criterion.php new file mode 100644 index 0000000000..2e794f42f8 --- /dev/null +++ b/src/contracts/Repository/Values/Trash/Query/Criterion.php @@ -0,0 +1,18 @@ + $criterion) { + if (!$criterion instanceof Criterion) { + throw new InvalidCriterionArgumentException($key, $criterion, Criterion::class); + } + + $this->criteria[] = $criterion; + } + } +} + +class_alias(LogicalOperator::class, 'eZ\Publish\API\Repository\Values\URL\Query\Criterion\LogicalOperator'); diff --git a/src/contracts/Repository/Values/URL/Query/Criterion/LogicalOr.php b/src/contracts/Repository/Values/URL/Query/Criterion/LogicalOr.php new file mode 100644 index 0000000000..cf9bf9c386 --- /dev/null +++ b/src/contracts/Repository/Values/URL/Query/Criterion/LogicalOr.php @@ -0,0 +1,15 @@ +pattern = $pattern; + } +} + +class_alias(Pattern::class, 'eZ\Publish\API\Repository\Values\URL\Query\Criterion\Pattern'); diff --git a/src/contracts/Repository/Values/URL/Query/Criterion/SectionId.php b/src/contracts/Repository/Values/URL/Query/Criterion/SectionId.php new file mode 100644 index 0000000000..1ee93d904d --- /dev/null +++ b/src/contracts/Repository/Values/URL/Query/Criterion/SectionId.php @@ -0,0 +1,32 @@ +sectionIds = $sectionIds; + } +} + +class_alias(SectionId::class, 'eZ\Publish\API\Repository\Values\URL\Query\Criterion\SectionId'); diff --git a/src/contracts/Repository/Values/URL/Query/Criterion/SectionIdentifier.php b/src/contracts/Repository/Values/URL/Query/Criterion/SectionIdentifier.php new file mode 100644 index 0000000000..0cadb43744 --- /dev/null +++ b/src/contracts/Repository/Values/URL/Query/Criterion/SectionIdentifier.php @@ -0,0 +1,32 @@ +sectionIdentifiers = $sectionIdentifiers; + } +} + +class_alias(SectionIdentifier::class, 'eZ\Publish\API\Repository\Values\URL\Query\Criterion\SectionIdentifier'); diff --git a/src/contracts/Repository/Values/URL/Query/Criterion/Validity.php b/src/contracts/Repository/Values/URL/Query/Criterion/Validity.php new file mode 100644 index 0000000000..4e57beee20 --- /dev/null +++ b/src/contracts/Repository/Values/URL/Query/Criterion/Validity.php @@ -0,0 +1,34 @@ +isValid = $isValid; + } +} + +class_alias(Validity::class, 'eZ\Publish\API\Repository\Values\URL\Query\Criterion\Validity'); diff --git a/src/contracts/Repository/Values/URL/Query/Criterion/VisibleOnly.php b/src/contracts/Repository/Values/URL/Query/Criterion/VisibleOnly.php new file mode 100644 index 0000000000..2e545075e7 --- /dev/null +++ b/src/contracts/Repository/Values/URL/Query/Criterion/VisibleOnly.php @@ -0,0 +1,18 @@ +direction = $sortDirection; + $this->target = $sortTarget; + } +} + +class_alias(SortClause::class, 'eZ\Publish\API\Repository\Values\URL\Query\SortClause'); diff --git a/src/contracts/Repository/Values/URL/Query/SortClause/Id.php b/src/contracts/Repository/Values/URL/Query/SortClause/Id.php new file mode 100644 index 0000000000..91928eb6a3 --- /dev/null +++ b/src/contracts/Repository/Values/URL/Query/SortClause/Id.php @@ -0,0 +1,26 @@ +items); + } +} + +class_alias(SearchResult::class, 'eZ\Publish\API\Repository\Values\URL\SearchResult'); diff --git a/src/contracts/Repository/Values/URL/URL.php b/src/contracts/Repository/Values/URL/URL.php new file mode 100644 index 0000000000..a7d531b048 --- /dev/null +++ b/src/contracts/Repository/Values/URL/URL.php @@ -0,0 +1,58 @@ +items); + } +} + +class_alias(UsageSearchResult::class, 'eZ\Publish\API\Repository\Values\URL\UsageSearchResult'); diff --git a/eZ/Publish/API/Repository/Values/User/Limitation.php b/src/contracts/Repository/Values/User/Limitation.php similarity index 88% rename from eZ/Publish/API/Repository/Values/User/Limitation.php rename to src/contracts/Repository/Values/User/Limitation.php index 68a2505551..236aa177a3 100644 --- a/eZ/Publish/API/Repository/Values/User/Limitation.php +++ b/src/contracts/Repository/Values/User/Limitation.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\User; +namespace Ibexa\Contracts\Core\Repository\Values\User; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; /** * This class represents a Limitation applied to a policy. @@ -51,3 +51,5 @@ abstract class Limitation extends ValueObject */ abstract public function getIdentifier(): string; } + +class_alias(Limitation::class, 'eZ\Publish\API\Repository\Values\User\Limitation'); diff --git a/eZ/Publish/API/Repository/Values/User/Limitation/BlockingLimitation.php b/src/contracts/Repository/Values/User/Limitation/BlockingLimitation.php similarity index 78% rename from eZ/Publish/API/Repository/Values/User/Limitation/BlockingLimitation.php rename to src/contracts/Repository/Values/User/Limitation/BlockingLimitation.php index 3d27b589f0..f97b07d13e 100644 --- a/eZ/Publish/API/Repository/Values/User/Limitation/BlockingLimitation.php +++ b/src/contracts/Repository/Values/User/Limitation/BlockingLimitation.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\User\Limitation; +namespace Ibexa\Contracts\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; /* * A always blocking limitation @@ -38,7 +38,7 @@ public function __construct(string $identifier, array $limitationValues) } /** - * @see \eZ\Publish\API\Repository\Values\User\Limitation::getIdentifier() + * @see \Ibexa\Contracts\Core\Repository\Values\User\Limitation::getIdentifier() * * @return string */ @@ -47,3 +47,5 @@ public function getIdentifier(): string return $this->identifier; } } + +class_alias(BlockingLimitation::class, 'eZ\Publish\API\Repository\Values\User\Limitation\BlockingLimitation'); diff --git a/src/contracts/Repository/Values/User/Limitation/ChangeOwnerLimitation.php b/src/contracts/Repository/Values/User/Limitation/ChangeOwnerLimitation.php new file mode 100644 index 0000000000..765769fa58 --- /dev/null +++ b/src/contracts/Repository/Values/User/Limitation/ChangeOwnerLimitation.php @@ -0,0 +1,33 @@ + $limitationValues, + ]); + } + + public function getIdentifier(): string + { + return self::IDENTIFIER; + } +} diff --git a/src/contracts/Repository/Values/User/Limitation/ContentTypeLimitation.php b/src/contracts/Repository/Values/User/Limitation/ContentTypeLimitation.php new file mode 100644 index 0000000000..4954687e14 --- /dev/null +++ b/src/contracts/Repository/Values/User/Limitation/ContentTypeLimitation.php @@ -0,0 +1,26 @@ +hasAccess = $hasAccess; + $this->lookupPolicyLimitations = $lookupPolicyLimitations; + $this->roleLimitations = $roleLimitations; + } + + public function hasAccess(): bool + { + return $this->hasAccess; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation[] + */ + public function getRoleLimitations(): array + { + return $this->roleLimitations; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\User\LookupPolicyLimitations[] + */ + public function getLookupPolicyLimitations(): array + { + return $this->lookupPolicyLimitations; + } +} + +class_alias(LookupLimitationResult::class, 'eZ\Publish\API\Repository\Values\User\LookupLimitationResult'); diff --git a/src/contracts/Repository/Values/User/LookupPolicyLimitations.php b/src/contracts/Repository/Values/User/LookupPolicyLimitations.php new file mode 100644 index 0000000000..cc221d0fcc --- /dev/null +++ b/src/contracts/Repository/Values/User/LookupPolicyLimitations.php @@ -0,0 +1,37 @@ +policy = $policy; + $this->limitations = $limitations; + } +} + +class_alias(LookupPolicyLimitations::class, 'eZ\Publish\API\Repository\Values\User\LookupPolicyLimitations'); diff --git a/eZ/Publish/API/Repository/Values/User/PasswordInfo.php b/src/contracts/Repository/Values/User/PasswordInfo.php similarity index 87% rename from eZ/Publish/API/Repository/Values/User/PasswordInfo.php rename to src/contracts/Repository/Values/User/PasswordInfo.php index e2eb4a47fa..d1207053ec 100644 --- a/eZ/Publish/API/Repository/Values/User/PasswordInfo.php +++ b/src/contracts/Repository/Values/User/PasswordInfo.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Values\User; +namespace Ibexa\Contracts\Core\Repository\Values\User; use DateTime; use DateTimeImmutable; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; final class PasswordInfo extends ValueObject { @@ -55,3 +55,5 @@ public function getExpirationWarningDate(): ?DateTimeImmutable return $this->expirationWarningDate; } } + +class_alias(PasswordInfo::class, 'eZ\Publish\API\Repository\Values\User\PasswordInfo'); diff --git a/src/contracts/Repository/Values/User/PasswordValidationContext.php b/src/contracts/Repository/Values/User/PasswordValidationContext.php new file mode 100644 index 0000000000..de81c4ca0c --- /dev/null +++ b/src/contracts/Repository/Values/User/PasswordValidationContext.php @@ -0,0 +1,36 @@ +status; + } + + /** + * Returns the list of policies of this role. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Policy[] + */ + abstract public function getPolicies(): iterable; +} + +class_alias(Role::class, 'eZ\Publish\API\Repository\Values\User\Role'); diff --git a/src/contracts/Repository/Values/User/RoleAssignment.php b/src/contracts/Repository/Values/User/RoleAssignment.php new file mode 100644 index 0000000000..e7ebbce0ab --- /dev/null +++ b/src/contracts/Repository/Values/User/RoleAssignment.php @@ -0,0 +1,44 @@ +getId(); + } + + public function getLogin(): string + { + return $this->login; + } + + public function getPasswordHash(): string + { + return $this->passwordHash; + } +} + +class_alias(User::class, 'eZ\Publish\API\Repository\Values\User\User'); diff --git a/src/contracts/Repository/Values/User/UserCreateStruct.php b/src/contracts/Repository/Values/User/UserCreateStruct.php new file mode 100644 index 0000000000..e8fed34f45 --- /dev/null +++ b/src/contracts/Repository/Values/User/UserCreateStruct.php @@ -0,0 +1,53 @@ +items); + } +} + +class_alias(UserPreferenceList::class, 'eZ\Publish\API\Repository\Values\UserPreference\UserPreferenceList'); diff --git a/src/contracts/Repository/Values/UserPreference/UserPreferenceSetStruct.php b/src/contracts/Repository/Values/UserPreference/UserPreferenceSetStruct.php new file mode 100644 index 0000000000..29791faa22 --- /dev/null +++ b/src/contracts/Repository/Values/UserPreference/UserPreferenceSetStruct.php @@ -0,0 +1,22 @@ + <$var> annotation in class doc in addition to inline property doc. + * Writable properties must be public and must be documented inline. + */ +abstract class ValueObject +{ + /** + * Construct object optionally with a set of properties. + * + * Readonly properties values must be set using $properties as they are not writable anymore + * after object has been created. + * + * @param array $properties + */ + public function __construct(array $properties = []) + { + foreach ($properties as $property => $value) { + $this->$property = $value; + } + } + + /** + * Function where list of properties are returned. + * + * Used by {@see attributes()}, override to add dynamic properties + * + * @uses ::__isset() + * + * @todo Make object traversable and reuse this function there (hence why this is not exposed) + * + * @param array $dynamicProperties Additional dynamic properties exposed on the object + * + * @return array + */ + protected function getProperties($dynamicProperties = []) + { + $properties = $dynamicProperties; + foreach (get_object_vars($this) as $property => $propertyValue) { + if ($this->__isset($property)) { + $properties[] = $property; + } + } + + return $properties; + } + + /** + * Magic set function handling writes to non public properties. + * + * @ignore This method is for internal use + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException When property does not exist + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\PropertyReadOnlyException When property is readonly (protected) + * + * @param string $property Name of the property + * @param string $value + */ + public function __set($property, $value) + { + if (property_exists($this, $property)) { + throw new PropertyReadOnlyException($property, static::class); + } + throw new PropertyNotFoundException($property, static::class); + } + + /** + * Magic get function handling read to non public properties. + * + * Returns value for all readonly (protected) properties. + * + * @ignore This method is for internal use + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException exception on all reads to undefined properties so typos are not silently accepted. + * + * @param string $property Name of the property + * + * @return mixed + */ + public function __get($property) + { + if (property_exists($this, $property)) { + return $this->$property; + } + throw new PropertyNotFoundException($property, static::class); + } + + /** + * Magic isset function handling isset() to non public properties. + * + * Returns true for all (public/)protected/private properties. + * + * @ignore This method is for internal use + * + * @param string $property Name of the property + * + * @return bool + */ + public function __isset($property) + { + return property_exists($this, $property); + } + + /** + * Magic unset function handling unset() to non public properties. + * + * @ignore This method is for internal use + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException exception on all writes to undefined properties so typos are not silently accepted and + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\PropertyReadOnlyException exception on readonly (protected) properties. + * + * @uses ::__set() + * + * @param string $property Name of the property + * + * @return bool + */ + public function __unset($property) + { + $this->__set($property, null); + } + + /** + * Returns a new instance of this class with the data specified by $array. + * + * $array contains all the data members of this class in the form: + * array('member_name'=>value). + * + * __set_state makes this class exportable with var_export. + * var_export() generates code, that calls this method when it + * is parsed with PHP. + * + * @ignore This method is for internal use + * + * @param mixed[] $array + * + * @return ValueObject + */ + public static function __set_state(array $array) + { + return new static($array); + } + + /** + * Internal function for Legacy template engine compatibility to get property value. + * + * @ignore This method is for internal use + * + * @deprecated Since 5.0, available purely for legacy eZTemplate compatibility + * + * @uses ::__get() + * + * @param string $property + * + * @return mixed + */ + final public function attribute($property) + { + return $this->__get($property); + } + + /** + * Internal function for Legacy template engine compatibility to get properties. + * + * @ignore This method is for internal use + * + * @deprecated Since 5.0, available purely for legacy eZTemplate compatibility + * + * @uses ::__isset() + * + * @return array + */ + final public function attributes() + { + return $this->getProperties(); + } + + /** + * Internal function for Legacy template engine compatibility to check existence of property. + * + * @ignore This method is for internal use + * + * @deprecated Since 5.0, available purely for legacy eZTemplate compatibility + * + * @uses ::__isset() + * + * @param string $property + * + * @return bool + */ + final public function hasAttribute($property) + { + return $this->__isset($property); + } +} + +class_alias(ValueObject::class, 'eZ\Publish\API\Repository\Values\ValueObject'); diff --git a/src/contracts/Search/Capable.php b/src/contracts/Search/Capable.php new file mode 100644 index 0000000000..a6504d9595 --- /dev/null +++ b/src/contracts/Search/Capable.php @@ -0,0 +1,28 @@ +name = $name; + $this->value = $value; + $this->type = $type; + } + + public function getName(): string + { + return $this->name; + } + + /** + * @return mixed + */ + public function getValue() + { + return $this->value; + } + + public function getType(): FieldType + { + return $this->type; + } +} + +class_alias(Field::class, 'eZ\Publish\SPI\Search\Field'); diff --git a/src/contracts/Search/FieldType.php b/src/contracts/Search/FieldType.php new file mode 100644 index 0000000000..5163df332c --- /dev/null +++ b/src/contracts/Search/FieldType.php @@ -0,0 +1,66 @@ +type; + } +} + +class_alias(FieldType::class, 'eZ\Publish\SPI\Search\FieldType'); diff --git a/src/contracts/Search/FieldType/BooleanField.php b/src/contracts/Search/FieldType/BooleanField.php new file mode 100644 index 0000000000..60d50e479d --- /dev/null +++ b/src/contracts/Search/FieldType/BooleanField.php @@ -0,0 +1,24 @@ +array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult With ContentInfo as SearchHit->valueObject + */ + public function findContent(Query $query, array $languageFilter = []); + + /** + * Performs a query for a single content object. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if Criterion is not applicable to its target + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $filter + * @param array $languageFilter a map of language related filters specifying languages query will be performed on. + * Also used to define which field languages are loaded for the returned content. + * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo + */ + public function findSingle(Criterion $filter, array $languageFilter = []); + + /** + * Finds locations for the given $query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query + * @param array $languageFilter a map of language related filters specifying languages query will be performed on. + * Also used to define which field languages are loaded for the returned content. + * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult With Location as SearchHit->valueObject + */ + public function findLocations(LocationQuery $query, array $languageFilter = []); + + /** + * Suggests a list of values for the given prefix. + * + * @param string $prefix + * @param string[] $fieldPaths + * @param int $limit + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion|null $filter + */ + public function suggest($prefix, $fieldPaths = [], $limit = 10, Criterion $filter = null); + + /** + * Indexes a content object. + * + * @param \Ibexa\Contracts\Core\Persistence\Content $content + */ + public function indexContent(Content $content); + + /** + * Deletes a content object from the index. + * + * @param int $contentId + * @param int|null $versionId + */ + public function deleteContent($contentId, $versionId = null); + + /** + * Indexes a Location in the index storage. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Location $location + */ + public function indexLocation(Location $location); + + /** + * Deletes a location from the index. + * + * @param mixed $locationId + * @param mixed $contentId + */ + public function deleteLocation($locationId, $contentId); + + /** + * Purges all contents from the index. + */ + public function purgeIndex(); +} + +class_alias(Handler::class, 'eZ\Publish\SPI\Search\Handler'); diff --git a/eZ/Publish/SPI/Search/VersatileHandler.php b/src/contracts/Search/VersatileHandler.php similarity index 81% rename from eZ/Publish/SPI/Search/VersatileHandler.php rename to src/contracts/Search/VersatileHandler.php index dfbe37f1b7..6b5a09ed6e 100644 --- a/eZ/Publish/SPI/Search/VersatileHandler.php +++ b/src/contracts/Search/VersatileHandler.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Search; +namespace Ibexa\Contracts\Core\Search; /** * @internal for internal use by Symfony DI configuration. Inject Handler and use instance of for @@ -17,3 +17,5 @@ interface VersatileHandler extends Handler, Capable, ContentTranslationHandler { } + +class_alias(VersatileHandler::class, 'eZ\Publish\SPI\Search\VersatileHandler'); diff --git a/eZ/Publish/SPI/SiteAccess/ConfigProcessor.php b/src/contracts/SiteAccess/ConfigProcessor.php similarity index 75% rename from eZ/Publish/SPI/SiteAccess/ConfigProcessor.php rename to src/contracts/SiteAccess/ConfigProcessor.php index 7a7901145f..b9fb8cda8a 100644 --- a/eZ/Publish/SPI/SiteAccess/ConfigProcessor.php +++ b/src/contracts/SiteAccess/ConfigProcessor.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\SiteAccess; +namespace Ibexa\Contracts\Core\SiteAccess; /** * @internal @@ -17,3 +17,5 @@ public function processComplexSetting(string $setting): string; public function processSettingValue(string $value): string; } + +class_alias(ConfigProcessor::class, 'eZ\Publish\SPI\SiteAccess\ConfigProcessor'); diff --git a/eZ/Publish/Core/MVC/ConfigResolverInterface.php b/src/contracts/SiteAccess/ConfigResolverInterface.php similarity index 88% rename from eZ/Publish/Core/MVC/ConfigResolverInterface.php rename to src/contracts/SiteAccess/ConfigResolverInterface.php index 9389f4d809..c2fb705b54 100644 --- a/eZ/Publish/Core/MVC/ConfigResolverInterface.php +++ b/src/contracts/SiteAccess/ConfigResolverInterface.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC; +namespace Ibexa\Contracts\Core\SiteAccess; /** * Interface for config resolvers. * * Classes implementing this interface will help you get settings for a specific scope. - * In eZ Publish context, this is useful to get a setting for a specific siteaccess for example. + * In Ibexa context, this is useful to get a setting for a specific siteaccess for example. * * The idea is to check the different scopes available for a given namespace to find the appropriate parameter. * To work, the dynamic setting must comply internally to the following name format : "..parameter.name". @@ -47,3 +47,5 @@ public function setDefaultNamespace(string $defaultNamespace): void; */ public function getDefaultNamespace(): string; } + +class_alias(ConfigResolverInterface::class, 'eZ\Publish\Core\MVC\ConfigResolverInterface'); diff --git a/src/contracts/Specification/AbstractSpecification.php b/src/contracts/Specification/AbstractSpecification.php new file mode 100644 index 0000000000..3877313ef2 --- /dev/null +++ b/src/contracts/Specification/AbstractSpecification.php @@ -0,0 +1,29 @@ +one = $one; + $this->two = $two; + } + + public function isSatisfiedBy($item): bool + { + return $this->one->isSatisfiedBy($item) && $this->two->isSatisfiedBy($item); + } +} diff --git a/src/contracts/Specification/Content/ContentContainerSpecification.php b/src/contracts/Specification/Content/ContentContainerSpecification.php new file mode 100644 index 0000000000..be23125afe --- /dev/null +++ b/src/contracts/Specification/Content/ContentContainerSpecification.php @@ -0,0 +1,21 @@ +getContentType()->isContainer; + } +} + +class_alias(ContentContainerSpecification::class, 'eZ\Publish\SPI\Specification\Content\ContentContainerSpecification'); diff --git a/src/contracts/Specification/Content/ContentSpecification.php b/src/contracts/Specification/Content/ContentSpecification.php new file mode 100644 index 0000000000..1443d685c3 --- /dev/null +++ b/src/contracts/Specification/Content/ContentSpecification.php @@ -0,0 +1,18 @@ +expectedType = $expectedType; + } + + public function isSatisfiedBy(Content $content): bool + { + return $content->getContentType()->identifier === $this->expectedType; + } +} + +class_alias(ContentTypeSpecification::class, 'eZ\Publish\SPI\Specification\Content\ContentTypeSpecification'); diff --git a/src/contracts/Specification/NotSpecification.php b/src/contracts/Specification/NotSpecification.php new file mode 100644 index 0000000000..f63fafe690 --- /dev/null +++ b/src/contracts/Specification/NotSpecification.php @@ -0,0 +1,24 @@ +specification = $specification; + } + + public function isSatisfiedBy($item): bool + { + return !$this->specification->isSatisfiedBy($item); + } +} diff --git a/src/contracts/Specification/OrSpecification.php b/src/contracts/Specification/OrSpecification.php new file mode 100644 index 0000000000..be5d195c38 --- /dev/null +++ b/src/contracts/Specification/OrSpecification.php @@ -0,0 +1,27 @@ +one = $one; + $this->two = $two; + } + + public function isSatisfiedBy($item): bool + { + return $this->one->isSatisfiedBy($item) || $this->two->isSatisfiedBy($item); + } +} diff --git a/src/contracts/Specification/SpecificationInterface.php b/src/contracts/Specification/SpecificationInterface.php new file mode 100644 index 0000000000..1b4b73f38a --- /dev/null +++ b/src/contracts/Specification/SpecificationInterface.php @@ -0,0 +1,23 @@ + + * @return iterable<\Ibexa\Contracts\Core\Test\Persistence\Fixture> */ protected static function getFixtures(): iterable { @@ -90,11 +91,11 @@ final protected static function getServiceByClassName(string $className, ?string protected static function getTestServiceId(?string $id, string $className): string { $kernel = self::$kernel; - if (!$kernel instanceof IbexaTestKernel) { + if (!$kernel instanceof IbexaTestKernelInterface) { throw new RuntimeException(sprintf( 'Expected %s to be an instance of %s.', get_class($kernel), - IbexaTestKernel::class, + IbexaTestKernelInterface::class, )); } @@ -163,6 +164,11 @@ protected static function getSectionService(): SectionService return self::getServiceByClassName(SectionService::class); } + protected static function getUrlAliasService(): URLAliasService + { + return self::getServiceByClassName(URLAliasService::class); + } + protected static function setAnonymousUser(): void { $anonymousUserId = 10; diff --git a/src/contracts/Test/IbexaTestKernel.php b/src/contracts/Test/IbexaTestKernel.php index 497aa6148a..0e547c3ff1 100644 --- a/src/contracts/Test/IbexaTestKernel.php +++ b/src/contracts/Test/IbexaTestKernel.php @@ -10,15 +10,16 @@ use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; use Doctrine\DBAL\Connection; -use eZ\Bundle\EzPublishCoreBundle\EzPublishCoreBundle; -use eZ\Bundle\EzPublishLegacySearchEngineBundle\EzPublishLegacySearchEngineBundle; -use eZ\Publish\API\Repository; -use eZ\Publish\Core\IO\Adapter\LocalAdapter; -use eZ\Publish\SPI\Persistence\TransactionHandler; -use eZ\Publish\SPI\Tests\Persistence\YamlFixture; use FOS\JsRoutingBundle\FOSJsRoutingBundle; +use Ibexa\Bundle\Core\IbexaCoreBundle; +use Ibexa\Bundle\LegacySearchEngine\IbexaLegacySearchEngineBundle; +use Ibexa\Contracts\Core\Persistence\TransactionHandler; +use Ibexa\Contracts\Core\Repository; +use Ibexa\Contracts\Core\Test\Persistence\Fixture\YamlFixture; +use Ibexa\Tests\Integration\Core\IO\FlysystemTestAdapter; +use Ibexa\Tests\Integration\Core\IO\FlysystemTestAdapterInterface; use JMS\TranslationBundle\JMSTranslationBundle; -use League\Flysystem\Memory\MemoryAdapter; +use League\Flysystem\InMemory\InMemoryFilesystemAdapter; use Liip\ImagineBundle\LiipImagineBundle; use LogicException; use Psr\Log\NullLogger; @@ -28,6 +29,7 @@ use Symfony\Component\Config\Loader\LoaderInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; use Symfony\Component\HttpKernel\Kernel; /** @@ -67,7 +69,7 @@ * If don't need the repository services (or not all), you can replace the IbexaTestKernel::EXPOSED_SERVICES_BY_CLASS and * IbexaTestKernel::EXPOSED_SERVICES_BY_ID consts in extending class, without changing the methods above. */ -class IbexaTestKernel extends Kernel +class IbexaTestKernel extends Kernel implements IbexaTestKernelInterface { /** * @var iterable @@ -86,6 +88,8 @@ class IbexaTestKernel extends Kernel Repository\SearchService::class, Repository\SectionService::class, Repository\UserService::class, + Repository\TokenService::class, + Repository\URLAliasService::class, ]; /** @@ -106,15 +110,15 @@ public static function getAliasServiceId(string $id): string */ public function getSchemaFiles(): iterable { - yield $this->locateResource('@EzPublishCoreBundle/Resources/config/storage/legacy/schema.yaml'); + yield $this->locateResource('@IbexaCoreBundle/Resources/config/storage/legacy/schema.yaml'); } /** - * @return iterable<\eZ\Publish\SPI\Tests\Persistence\Fixture> + * @return iterable<\Ibexa\Contracts\Core\Test\Persistence\Fixture> */ public function getFixtures(): iterable { - yield new YamlFixture(dirname(__DIR__, 3) . '/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/data/test_data.yaml'); + yield new YamlFixture(dirname(__DIR__, 3) . '/tests/integration/Core/Repository/_fixtures/Legacy/data/test_data.yaml'); } public function getCacheDir(): string @@ -130,8 +134,8 @@ public function getBuildDir(): string public function registerBundles(): iterable { yield new SecurityBundle(); - yield new EzPublishCoreBundle(); - yield new EzPublishLegacySearchEngineBundle(); + yield new IbexaCoreBundle(); + yield new IbexaLegacySearchEngineBundle(); yield new JMSTranslationBundle(); yield new FOSJsRoutingBundle(); yield new FrameworkBundle(); @@ -147,7 +151,7 @@ public function registerContainerConfiguration(LoaderInterface $loader): void { $loader->load(static function (ContainerBuilder $container): void { $container->setParameter('ibexa.core.test.resource_dir', self::getResourcesPath()); - $container->setParameter('ezpublish.kernel.root_dir', dirname(__DIR__, 3)); + $container->setParameter('ibexa.kernel.root_dir', dirname(__DIR__, 3)); }); $this->loadConfiguration($loader); @@ -205,20 +209,43 @@ protected static function getExposedServicesById(): iterable private static function getResourcesPath(): string { - return dirname(__DIR__, 3) . '/eZ/Bundle/EzPublishCoreBundle/Tests/Resources'; + return dirname(__DIR__, 3) . '/tests/bundle/Core/Resources'; } private static function prepareIOServices(ContainerBuilder $container): void { - if (!class_exists(MemoryAdapter::class)) { - throw new LogicException(sprintf( - 'Missing %s class. Ensure that %s package is installed as a dev dependency', - MemoryAdapter::class, - 'league/flysystem-memory', - )); + if (!class_exists(InMemoryFilesystemAdapter::class)) { + throw new LogicException( + sprintf( + 'Missing %s class. Ensure that %s package is installed as a dev dependency', + InMemoryFilesystemAdapter::class, + 'league/flysystem-memory', + ) + ); } - $container->setDefinition(LocalAdapter::class, new Definition(MemoryAdapter::class)); + $container->setParameter('webroot_dir', dirname(__DIR__, 3) . '/var/public'); + $inMemoryAdapter = new Definition(InMemoryFilesystemAdapter::class); + $container->setDefinition(InMemoryFilesystemAdapter::class, $inMemoryAdapter); + + $testAdapterDefinition = new Definition(FlysystemTestAdapter::class); + $testAdapterDefinition->setDecoratedService( + 'ibexa.core.io.flysystem.adapter.site_access_aware', + null, + -10 + ); + $testAdapterDefinition->setArgument('$inMemoryAdapter', $inMemoryAdapter); + $testAdapterDefinition->setArgument( + '$localAdapter', + new Reference( + '.inner' + ) + ); + $testAdapterDefinition->setPublic(true); + $container->setDefinition( + FlysystemTestAdapterInterface::class, + $testAdapterDefinition + ); } private static function createPublicAliasesForServicesUnderTest(ContainerBuilder $container): void diff --git a/src/contracts/Test/IbexaTestKernelInterface.php b/src/contracts/Test/IbexaTestKernelInterface.php new file mode 100644 index 0000000000..55f6e42186 --- /dev/null +++ b/src/contracts/Test/IbexaTestKernelInterface.php @@ -0,0 +1,31 @@ + + */ + public function getSchemaFiles(): iterable; + + /** + * @return iterable<\Ibexa\Contracts\Core\Test\Persistence\Fixture> + */ + public function getFixtures(): iterable; +} diff --git a/eZ/Publish/SPI/Tests/Persistence/Fixture.php b/src/contracts/Test/Persistence/Fixture.php similarity index 79% rename from eZ/Publish/SPI/Tests/Persistence/Fixture.php rename to src/contracts/Test/Persistence/Fixture.php index c9994f4a10..2c71018a32 100644 --- a/eZ/Publish/SPI/Tests/Persistence/Fixture.php +++ b/src/contracts/Test/Persistence/Fixture.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Tests\Persistence; +namespace Ibexa\Contracts\Core\Test\Persistence; /** * Represents database fixture. @@ -22,3 +22,5 @@ interface Fixture */ public function load(): array; } + +class_alias(Fixture::class, 'eZ\Publish\SPI\Tests\Persistence\Fixture'); diff --git a/eZ/Publish/SPI/Tests/Persistence/BaseInMemoryCachedFileFixture.php b/src/contracts/Test/Persistence/Fixture/BaseInMemoryCachedFileFixture.php similarity index 86% rename from eZ/Publish/SPI/Tests/Persistence/BaseInMemoryCachedFileFixture.php rename to src/contracts/Test/Persistence/Fixture/BaseInMemoryCachedFileFixture.php index 9c554d149c..2a60192d5c 100644 --- a/eZ/Publish/SPI/Tests/Persistence/BaseInMemoryCachedFileFixture.php +++ b/src/contracts/Test/Persistence/Fixture/BaseInMemoryCachedFileFixture.php @@ -6,8 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Tests\Persistence; +namespace Ibexa\Contracts\Core\Test\Persistence\Fixture; +use Ibexa\Contracts\Core\Test\Persistence\Fixture; use PHPUnit\Runner\Exception; /** @@ -51,3 +52,5 @@ final public function load(): array return self::$inMemoryCachedLoadedData[$this->filePath] ?? []; } } + +class_alias(BaseInMemoryCachedFileFixture::class, 'eZ\Publish\SPI\Tests\Persistence\BaseInMemoryCachedFileFixture'); diff --git a/eZ/Publish/SPI/Tests/Persistence/FileFixtureFactory.php b/src/contracts/Test/Persistence/Fixture/FileFixtureFactory.php similarity index 77% rename from eZ/Publish/SPI/Tests/Persistence/FileFixtureFactory.php rename to src/contracts/Test/Persistence/Fixture/FileFixtureFactory.php index 6752c8027c..5a66b13e3d 100644 --- a/eZ/Publish/SPI/Tests/Persistence/FileFixtureFactory.php +++ b/src/contracts/Test/Persistence/Fixture/FileFixtureFactory.php @@ -6,15 +6,16 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Tests\Persistence; +namespace Ibexa\Contracts\Core\Test\Persistence\Fixture; +use Ibexa\Contracts\Core\Test\Persistence\Fixture; use RuntimeException; use SplFileInfo; /** * Factory building an instance of Fixture depending on a file type. * - * @see \eZ\Publish\SPI\Tests\Persistence\Fixture + * @see \Ibexa\Contracts\Core\Test\Persistence\Fixture */ final class FileFixtureFactory { @@ -34,3 +35,5 @@ public function buildFixture(string $filePath): Fixture } } } + +class_alias(FileFixtureFactory::class, 'eZ\Publish\SPI\Tests\Persistence\FileFixtureFactory'); diff --git a/eZ/Publish/SPI/Tests/Persistence/FixtureImporter.php b/src/contracts/Test/Persistence/Fixture/FixtureImporter.php similarity index 83% rename from eZ/Publish/SPI/Tests/Persistence/FixtureImporter.php rename to src/contracts/Test/Persistence/Fixture/FixtureImporter.php index 7d6daa98f4..3cf0609118 100644 --- a/eZ/Publish/SPI/Tests/Persistence/FixtureImporter.php +++ b/src/contracts/Test/Persistence/Fixture/FixtureImporter.php @@ -6,12 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Tests\Persistence; +namespace Ibexa\Contracts\Core\Test\Persistence\Fixture; use Doctrine\DBAL\Connection; use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver\PDOException; use Doctrine\DBAL\Schema\Column; +use Ibexa\Contracts\Core\Test\Persistence\Fixture; /** * Database fixture importer. @@ -49,31 +50,8 @@ static function ($tableData) { } ); foreach ($nonEmptyTablesData as $table => $rows) { - $firstRow = current($rows); - $query = $this->connection->createQueryBuilder(); - $columns = array_keys($firstRow); - $query - ->insert($table) - ->values( - array_combine( - array_map( - function (string $columnName) { - return $this->connection->quoteIdentifier($columnName); - }, - $columns - ), - array_map( - static function (string $columnName) { - return ":{$columnName}"; - }, - $columns - ) - ) - ); - foreach ($rows as $row) { - $query->setParameters($row); - $query->execute(); + $this->connection->insert($table, $row); } } @@ -178,3 +156,5 @@ private function findAutoincrementColumn(array $columns): ?Column return null; } } + +class_alias(FixtureImporter::class, 'eZ\Publish\SPI\Tests\Persistence\FixtureImporter'); diff --git a/eZ/Publish/SPI/Tests/Persistence/PhpArrayFileFixture.php b/src/contracts/Test/Persistence/Fixture/PhpArrayFileFixture.php similarity index 76% rename from eZ/Publish/SPI/Tests/Persistence/PhpArrayFileFixture.php rename to src/contracts/Test/Persistence/Fixture/PhpArrayFileFixture.php index d217e20c36..4c9bdb4e09 100644 --- a/eZ/Publish/SPI/Tests/Persistence/PhpArrayFileFixture.php +++ b/src/contracts/Test/Persistence/Fixture/PhpArrayFileFixture.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Tests\Persistence; +namespace Ibexa\Contracts\Core\Test\Persistence\Fixture; /** * Data fixture stored in PHP file which returns it as an array. @@ -20,3 +20,5 @@ protected function loadFixture(): array return require $this->getFilePath(); } } + +class_alias(PhpArrayFileFixture::class, 'eZ\Publish\SPI\Tests\Persistence\PhpArrayFileFixture'); diff --git a/eZ/Publish/SPI/Tests/Persistence/YamlFixture.php b/src/contracts/Test/Persistence/Fixture/YamlFixture.php similarity index 78% rename from eZ/Publish/SPI/Tests/Persistence/YamlFixture.php rename to src/contracts/Test/Persistence/Fixture/YamlFixture.php index f04fed8b5c..27c8575dcc 100644 --- a/eZ/Publish/SPI/Tests/Persistence/YamlFixture.php +++ b/src/contracts/Test/Persistence/Fixture/YamlFixture.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Tests\Persistence; +namespace Ibexa\Contracts\Core\Test\Persistence\Fixture; use Symfony\Component\Yaml\Yaml; @@ -22,3 +22,5 @@ protected function loadFixture(): array return Yaml::parseFile($this->getFilePath()); } } + +class_alias(YamlFixture::class, 'eZ\Publish\SPI\Tests\Persistence\YamlFixture'); diff --git a/eZ/Publish/API/Repository/Tests/SetupFactory.php b/src/contracts/Test/Repository/SetupFactory.php similarity index 76% rename from eZ/Publish/API/Repository/Tests/SetupFactory.php rename to src/contracts/Test/Repository/SetupFactory.php index 82d6eb6a8c..eb2d57f9cb 100644 --- a/eZ/Publish/API/Repository/Tests/SetupFactory.php +++ b/src/contracts/Test/Repository/SetupFactory.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Contracts\Core\Test\Repository; /** * A Test Factory is used to setup the infrastructure for a tests, based on a @@ -18,14 +18,14 @@ abstract class SetupFactory * @param bool $initializeFromScratch if the back end should be initialized * from scratch or re-used * - * @return \eZ\Publish\API\Repository\Repository + * @return \Ibexa\Contracts\Core\Repository\Repository */ abstract public function getRepository($initializeFromScratch = true); /** * Returns a repository specific ID manager. * - * @return \eZ\Publish\API\Repository\Tests\IdManager + * @return \Ibexa\Tests\Integration\Core\Repository\IdManager */ abstract public function getIdManager(); @@ -34,7 +34,7 @@ abstract public function getIdManager(); * * @param string $configKey * - * @throws Exception if $configKey could not be found. + * @throws \Exception if $configKey could not be found. * * @return mixed */ @@ -45,7 +45,9 @@ abstract public function getConfigValue($configKey); * * Most tests should not use this at all!! * - * @return \eZ\Publish\Core\Base\ServiceContainer + * @return \Ibexa\Core\Base\ServiceContainer */ abstract public function getServiceContainer(); } + +class_alias(SetupFactory::class, 'eZ\Publish\API\Repository\Tests\SetupFactory'); diff --git a/src/contracts/Test/Repository/SetupFactory/Legacy.php b/src/contracts/Test/Repository/SetupFactory/Legacy.php new file mode 100644 index 0000000000..d690e77406 --- /dev/null +++ b/src/contracts/Test/Repository/SetupFactory/Legacy.php @@ -0,0 +1,395 @@ +repositoryReference = $repositoryReference; + } + + self::$db = preg_replace('(^([a-z]+).*)', '\\1', self::$dsn); + + if (!isset(self::$ioRootDir)) { + self::$ioRootDir = $this->createTemporaryDirectory(); + } + } + + /** + * Creates a temporary directory and returns its path name. + * + * @throw \RuntimeException If the root directory can't be created + */ + private function createTemporaryDirectory(): string + { + $tmpFile = tempnam( + sys_get_temp_dir(), + 'ibexa_core_io_tests_' . time() + ); + unlink($tmpFile); + + $fs = new Filesystem(); + $fs->mkdir($tmpFile); + + $varDir = $tmpFile . '/var'; + if ($fs->exists($varDir)) { + $fs->remove($varDir); + } + $fs->mkdir($varDir); + + return $tmpFile; + } + + /** + * Returns a configured repository for testing. + * + * @param bool $initializeFromScratch if the back end should be initialized + * from scratch or re-used + * + * @return \Ibexa\Contracts\Core\Repository\Repository + */ + public function getRepository($initializeFromScratch = true) + { + if ($initializeFromScratch || !self::$schemaInitialized) { + $this->initializeSchema(); + $this->insertData(); + } + + $this->clearInternalCaches(); + /** @var \Ibexa\Contracts\Core\Repository\Repository $repository */ + $repository = $this->getServiceContainer()->get($this->repositoryReference); + + // Set admin user as current user by default + $repository->getPermissionResolver()->setCurrentUserReference( + new UserReference(14) + ); + + return $repository; + } + + /** + * Returns a config value for $configKey. + * + * @param string $configKey + * + * @throws \Exception if $configKey could not be found. + * + * @return mixed + */ + public function getConfigValue($configKey) + { + return $this->getServiceContainer()->getParameter($configKey); + } + + /** + * Returns a repository specific ID manager. + * + * @return \Ibexa\Tests\Integration\Core\Repository\IdManager + */ + public function getIdManager() + { + return new IdManager\Php(); + } + + /** + * Insert the database data. + * + * @throws \Doctrine\DBAL\DBALException + */ + public function insertData(): void + { + $connection = $this->getDatabaseConnection(); + $this->cleanupVarDir($this->getInitialVarDir()); + + $fixtureImporter = new FixtureImporter($connection); + $fixtureImporter->import($this->getInitialDataFixture()); + } + + protected function getInitialVarDir(): string + { + return __DIR__ . '/../../../../../var'; + } + + protected function cleanupVarDir($sourceDir) + { + $fs = new Filesystem(); + $varDir = self::$ioRootDir . '/var'; + if ($fs->exists($varDir)) { + $fs->remove($varDir); + } + $fs->mkdir($varDir); + $fs->mirror($sourceDir, $varDir); + } + + /** + * CLears internal in memory caches after inserting data circumventing the + * API. + */ + protected function clearInternalCaches() + { + /** @var $handler \Ibexa\Core\Persistence\Legacy\Handler */ + $handler = $this->getServiceContainer()->get(Handler::class); + + $contentLanguageHandler = $handler->contentLanguageHandler(); + if ($contentLanguageHandler instanceof CachingLanguageHandler) { + $contentLanguageHandler->clearCache(); + } + + $contentTypeHandler = $handler->contentTypeHandler(); + if ($contentTypeHandler instanceof CachingContentTypeHandler) { + $contentTypeHandler->clearCache(); + } + + /** @var $cachePool \Psr\Cache\CacheItemPoolInterface */ + $cachePool = $this->getServiceContainer()->get('ibexa.cache_pool'); + + $cachePool->clear(); + } + + protected function getInitialDataFixture(): Fixture + { + if (!isset(self::$initialDataFixture)) { + self::$initialDataFixture = new YamlFixture( + dirname(__DIR__, 5) . '/tests/integration/Core/Repository/_fixtures/Legacy/data/test_data.yaml' + ); + } + + return self::$initialDataFixture; + } + + /** + * Initializes the database schema. + * + * @throws \Doctrine\DBAL\ConnectionException + */ + protected function initializeSchema(): void + { + if (!self::$schemaInitialized) { + $schemaImporter = new LegacySchemaImporter($this->getDatabaseConnection()); + $schemaImporter->importSchema( + dirname(__DIR__, 5) . + '/src/bundle/Core/Resources/config/storage/legacy/schema.yaml' + ); + + self::$schemaInitialized = true; + } + } + + /** + * Returns the raw database connection from the service container. + * + * @return \Doctrine\DBAL\Connection + */ + private function getDatabaseConnection(): Connection + { + if (null === $this->connection) { + $this->connection = $this->getServiceContainer()->get('ibexa.persistence.connection'); + } + + return $this->connection; + } + + /** + * Returns the service container used for initialization of the repository. + * + * @return \Ibexa\Core\Base\ServiceContainer + */ + public function getServiceContainer() + { + if (!isset(self::$serviceContainer)) { + $installDir = self::getInstallationDir(); + + $containerBuilder = new LegacyTestContainerBuilder(); + $loader = $containerBuilder->getCoreLoader(); + /* @var \Symfony\Component\DependencyInjection\Loader\YamlFileLoader $loader */ + $loader->load('search_engines/legacy.yml'); + + // tests/integration/Core/Resources/settings/integration_legacy.yml + $loader->load('integration_legacy.yml'); + + $this->externalBuildContainer($containerBuilder); + + $containerBuilder->setParameter( + 'ibexa.persistence.legacy.dsn', + self::$dsn + ); + + $containerBuilder->setParameter( + 'ibexa.io.dir.root', + self::$ioRootDir . '/' . $containerBuilder->getParameter('ibexa.io.dir.storage') + ); + + $containerBuilder->addCompilerPass(new Compiler\Search\FieldRegistryPass()); + + $this->registerForAutoConfiguration($containerBuilder); + + // load overrides just before creating test Container + // tests/integration/Core/Resources/settings/override.yml + $loader->load('override.yml'); + + self::$serviceContainer = new ServiceContainer( + $containerBuilder, + $installDir, + $this->getCacheDir(), + true, + true + ); + } + + return self::$serviceContainer; + } + + /** + * This is intended to be used from external repository in order to + * enable container customization. + * + * @param \Symfony\Component\DependencyInjection\ContainerBuilder $containerBuilder + */ + protected function externalBuildContainer(ContainerBuilder $containerBuilder) + { + // Does nothing by default + } + + /** + * Get the Database name. + * + * @return string + */ + public function getDB() + { + return self::$db; + } + + /** + * Apply automatic configuration to needed Symfony Services. + * + * Note: Based on + * {@see \Ibexa\Bundle\Core\DependencyInjection\IbexaCoreExtension::registerForAutoConfiguration}, + * but only for services needed by integration test setup. + * + * @see + */ + private function registerForAutoConfiguration(ContainerBuilder $containerBuilder): void + { + $containerBuilder->registerForAutoconfiguration(FilteringCriterionQueryBuilder::class) + ->addTag(ServiceTags::FILTERING_CRITERION_QUERY_BUILDER); + + $containerBuilder->registerForAutoconfiguration(FilteringSortClauseQueryBuilder::class) + ->addTag(ServiceTags::FILTERING_SORT_CLAUSE_QUERY_BUILDER); + } + + /** + * @deprecated since Ibexa 4.0, rewrite test case to use {@see \Ibexa\Contracts\Core\Test\IbexaKernelTestCase} instead. + */ + public static function getInstallationDir(): string + { + // package root directory: + return realpath(__DIR__ . '/../../../../../'); + } + + /** + * @deprecated since Ibexa 4.0, rewrite test case to use {@see \Ibexa\Contracts\Core\Test\IbexaKernelTestCase} instead. + */ + public static function getCacheDir(): string + { + return self::getInstallationDir() . '/var/cache'; + } +} + +class_alias(Legacy::class, 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy'); diff --git a/src/contracts/Token/TokenGeneratorInterface.php b/src/contracts/Token/TokenGeneratorInterface.php new file mode 100644 index 0000000000..c897c509a3 --- /dev/null +++ b/src/contracts/Token/TokenGeneratorInterface.php @@ -0,0 +1,14 @@ +repositoryClass = $repositoryClass; + $this->policyMap = $policyMap; + $this->languageResolver = $languageResolver; + } + + /** + * Builds the main repository, heart of Ibexa API. + * + * This always returns the true inner Repository, please depend on ezpublish.api.repository and not this method + * directly to make sure you get an instance wrapped inside Event / Cache / * functionality. + * + * @param string[] $languages + */ + public function buildRepository( + PersistenceHandler $persistenceHandler, + SearchHandler $searchHandler, + BackgroundIndexer $backgroundIndexer, + RelationProcessor $relationProcessor, + FieldTypeRegistry $fieldTypeRegistry, + PasswordHashService $passwordHashService, + ThumbnailStrategy $thumbnailStrategy, + ProxyDomainMapperFactoryInterface $proxyDomainMapperFactory, + Mapper\ContentDomainMapper $contentDomainMapper, + Mapper\ContentTypeDomainMapper $contentTypeDomainMapper, + Mapper\RoleDomainMapper $roleDomainMapper, + Mapper\ContentMapper $contentMapper, + ContentValidator $contentValidator, + LimitationService $limitationService, + PermissionService $permissionService, + ContentFilteringHandler $contentFilteringHandler, + LocationFilteringHandler $locationFilteringHandler, + PasswordValidatorInterface $passwordValidator, + ConfigResolverInterface $configResolver, + NameSchemaServiceInterface $nameSchemaService, + array $languages + ): Repository { + return new $this->repositoryClass( + $persistenceHandler, + $searchHandler, + $backgroundIndexer, + $relationProcessor, + $fieldTypeRegistry, + $passwordHashService, + $thumbnailStrategy, + $proxyDomainMapperFactory, + $contentDomainMapper, + $contentTypeDomainMapper, + $roleDomainMapper, + $contentMapper, + $contentValidator, + $limitationService, + $this->languageResolver, + $permissionService, + $contentFilteringHandler, + $locationFilteringHandler, + $passwordValidator, + $configResolver, + $nameSchemaService, + [ + 'role' => [ + 'policyMap' => $this->policyMap, + ], + 'languages' => $languages, + ], + ); + } + + /** + * Returns a service based on a name string (content => contentService, etc). + * + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param string $serviceName + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @return mixed + */ + public function buildService(Repository $repository, $serviceName) + { + $methodName = 'get' . $serviceName . 'Service'; + if (!method_exists($repository, $methodName)) { + throw new InvalidArgumentException($serviceName, 'No such service'); + } + + return $repository->$methodName(); + } +} + +class_alias(RepositoryFactory::class, 'eZ\Publish\Core\Base\Container\ApiLoader\RepositoryFactory'); diff --git a/src/lib/Base/Container/Compiler/AbstractFieldTypeBasedPass.php b/src/lib/Base/Container/Compiler/AbstractFieldTypeBasedPass.php new file mode 100644 index 0000000000..4a2c3068db --- /dev/null +++ b/src/lib/Base/Container/Compiler/AbstractFieldTypeBasedPass.php @@ -0,0 +1,51 @@ +findTaggedServiceIds(self::FIELD_TYPE_SERVICE_TAG); + foreach ($serviceTags as $serviceId => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['alias'])) { + throw new LogicException( + sprintf( + 'Service "%s" tagged with "%s" needs an "alias" attribute to identify the search engine', + $serviceId, + self::FIELD_TYPE_SERVICE_TAG + ) + ); + } + } + } + + return $serviceTags; + } + + abstract public function process(ContainerBuilder $container); +} + +class_alias(AbstractFieldTypeBasedPass::class, 'eZ\Publish\Core\Base\Container\Compiler\AbstractFieldTypeBasedPass'); diff --git a/src/lib/Base/Container/Compiler/FieldTypeRegistryPass.php b/src/lib/Base/Container/Compiler/FieldTypeRegistryPass.php new file mode 100644 index 0000000000..955da2ef37 --- /dev/null +++ b/src/lib/Base/Container/Compiler/FieldTypeRegistryPass.php @@ -0,0 +1,54 @@ +hasDefinition(FieldTypeRegistry::class)) { + return; + } + + $fieldTypeRegistryDefinition = $container->getDefinition(FieldTypeRegistry::class); + + foreach ($this->getFieldTypeServiceIds($container) as $id => $attributes) { + foreach ($attributes as $attribute) { + $fieldTypeRegistryDefinition->addMethodCall( + 'registerFieldType', + [ + $attribute['alias'], + new Reference($id), + ] + ); + + // Add FieldType to the "concrete" list if it's not a fake. + if (!is_a($container->findDefinition($id)->getClass(), Type::class, true)) { + $fieldTypeRegistryDefinition->addMethodCall( + 'registerConcreteFieldTypeIdentifier', + [$attribute['alias']] + ); + } + } + } + } +} + +class_alias(FieldTypeRegistryPass::class, 'eZ\Publish\Core\Base\Container\Compiler\FieldTypeRegistryPass'); diff --git a/eZ/Publish/Core/Base/Container/Compiler/GenericFieldTypeConverterPass.php b/src/lib/Base/Container/Compiler/GenericFieldTypeConverterPass.php similarity index 92% rename from eZ/Publish/Core/Base/Container/Compiler/GenericFieldTypeConverterPass.php rename to src/lib/Base/Container/Compiler/GenericFieldTypeConverterPass.php index 1b38af3f7a..111e15b610 100644 --- a/eZ/Publish/Core/Base/Container/Compiler/GenericFieldTypeConverterPass.php +++ b/src/lib/Base/Container/Compiler/GenericFieldTypeConverterPass.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Base\Container\Compiler; +namespace Ibexa\Core\Base\Container\Compiler; -use eZ\Publish\Core\Base\Container\Compiler\Storage\Legacy\FieldValueConverterRegistryPass; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SerializableConverter; -use eZ\Publish\SPI\FieldType\Generic\Type as GenericType; +use Ibexa\Contracts\Core\FieldType\Generic\Type as GenericType; +use Ibexa\Core\Base\Container\Compiler\Storage\Legacy\FieldValueConverterRegistryPass; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\SerializableConverter; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Exception\LogicException; @@ -146,3 +146,5 @@ private function isGenericFieldType(ContainerBuilder $container, string $service return $reflection->isSubclassOf(GenericType::class); } } + +class_alias(GenericFieldTypeConverterPass::class, 'eZ\Publish\Core\Base\Container\Compiler\GenericFieldTypeConverterPass'); diff --git a/src/lib/Base/Container/Compiler/Persistence/FieldTypeRegistryPass.php b/src/lib/Base/Container/Compiler/Persistence/FieldTypeRegistryPass.php new file mode 100644 index 0000000000..ec0e6c617a --- /dev/null +++ b/src/lib/Base/Container/Compiler/Persistence/FieldTypeRegistryPass.php @@ -0,0 +1,43 @@ +hasDefinition(FieldTypeRegistry::class)) { + return; + } + + $fieldTypeRegistryDefinition = $container->getDefinition(FieldTypeRegistry::class); + + foreach ($this->getFieldTypeServiceIds($container) as $id => $attributes) { + foreach ($attributes as $attribute) { + $fieldTypeRegistryDefinition->addMethodCall( + 'register', + [ + $attribute['alias'], + new Reference($id), + ] + ); + } + } + } +} + +class_alias(FieldTypeRegistryPass::class, 'eZ\Publish\Core\Base\Container\Compiler\Persistence\FieldTypeRegistryPass'); diff --git a/src/lib/Base/Container/Compiler/Search/AggregateFieldValueMapperPass.php b/src/lib/Base/Container/Compiler/Search/AggregateFieldValueMapperPass.php new file mode 100644 index 0000000000..fdda21cfac --- /dev/null +++ b/src/lib/Base/Container/Compiler/Search/AggregateFieldValueMapperPass.php @@ -0,0 +1,43 @@ +hasDefinition(Aggregate::class)) { + return; + } + + $aggregateFieldValueMapperDefinition = $container->getDefinition(Aggregate::class); + $taggedServiceIds = $container->findTaggedServiceIds(self::TAG); + foreach ($taggedServiceIds as $id => $tags) { + foreach ($tags as $tagAttributes) { + $aggregateFieldValueMapperDefinition->addMethodCall( + 'addMapper', + [new Reference($id), $tagAttributes['maps'] ?? null] + ); + } + } + } +} + +class_alias(AggregateFieldValueMapperPass::class, 'eZ\Publish\Core\Base\Container\Compiler\Search\AggregateFieldValueMapperPass'); diff --git a/src/lib/Base/Container/Compiler/Search/FieldRegistryPass.php b/src/lib/Base/Container/Compiler/Search/FieldRegistryPass.php new file mode 100644 index 0000000000..a2b81739f6 --- /dev/null +++ b/src/lib/Base/Container/Compiler/Search/FieldRegistryPass.php @@ -0,0 +1,60 @@ +hasDefinition(FieldRegistry::class)) { + return; + } + + $fieldRegistryDefinition = $container->getDefinition(FieldRegistry::class); + + $serviceTags = $container->findTaggedServiceIds(self::FIELD_TYPE_INDEXABLE_SERVICE_TAG); + foreach ($serviceTags as $serviceId => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['alias'])) { + throw new LogicException( + sprintf( + 'Service "%s" tagged with "%s" needs an "alias" attribute to identify the search engine', + $serviceId, + self::FIELD_TYPE_INDEXABLE_SERVICE_TAG + ) + ); + } + + $fieldRegistryDefinition->addMethodCall( + 'registerType', + [ + $attribute['alias'], + new Reference($serviceId), + ] + ); + } + } + } +} + +class_alias(FieldRegistryPass::class, 'eZ\Publish\Core\Base\Container\Compiler\Search\FieldRegistryPass'); diff --git a/src/lib/Base/Container/Compiler/Search/Legacy/CriteriaConverterPass.php b/src/lib/Base/Container/Compiler/Search/Legacy/CriteriaConverterPass.php new file mode 100644 index 0000000000..f28331e82e --- /dev/null +++ b/src/lib/Base/Container/Compiler/Search/Legacy/CriteriaConverterPass.php @@ -0,0 +1,73 @@ +hasDefinition('ibexa.search.legacy.gateway.criteria_converter.content') && + !$container->hasDefinition('ibexa.search.legacy.gateway.criteria_converter.location') && + !$container->hasDefinition('ibexa.core.trash.search.legacy.gateway.criteria_converter') && + !$container->hasDefinition(CriteriaConverter::class) + ) { + return; + } + + if ($container->hasDefinition('ibexa.search.legacy.gateway.criteria_converter.content')) { + $criteriaConverterContent = $container->getDefinition('ibexa.search.legacy.gateway.criteria_converter.content'); + + $contentHandlers = $container->findTaggedServiceIds('ibexa.search.legacy.gateway.criterion_handler.content'); + + $this->addHandlers($criteriaConverterContent, $contentHandlers); + } + + if ($container->hasDefinition('ibexa.search.legacy.gateway.criteria_converter.location')) { + $criteriaConverterLocation = $container->getDefinition('ibexa.search.legacy.gateway.criteria_converter.location'); + + $locationHandlers = $container->findTaggedServiceIds('ibexa.search.legacy.gateway.criterion_handler.location'); + + $this->addHandlers($criteriaConverterLocation, $locationHandlers); + } + + if ($container->hasDefinition('ibexa.core.trash.search.legacy.gateway.criteria_converter')) { + $trashCriteriaConverter = $container->getDefinition('ibexa.core.trash.search.legacy.gateway.criteria_converter'); + $trashCriteriaHandlers = $container->findTaggedServiceIds('ibexa.search.legacy.trash.gateway.criterion.handler'); + + $this->addHandlers($trashCriteriaConverter, $trashCriteriaHandlers); + } + + if ($container->hasDefinition(CriteriaConverter::class)) { + $urlCriteriaConverter = $container->getDefinition(CriteriaConverter::class); + $urlCriteriaHandlers = $container->findTaggedServiceIds('ibexa.storage.legacy.url.criterion.handler'); + + $this->addHandlers($urlCriteriaConverter, $urlCriteriaHandlers); + } + } + + protected function addHandlers(Definition $definition, $handlers) + { + foreach ($handlers as $id => $attributes) { + $definition->addMethodCall('addHandler', [new Reference($id)]); + } + } +} + +class_alias(CriteriaConverterPass::class, 'eZ\Publish\Core\Base\Container\Compiler\Search\Legacy\CriteriaConverterPass'); diff --git a/src/lib/Base/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPass.php b/src/lib/Base/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPass.php new file mode 100644 index 0000000000..0fe22241d3 --- /dev/null +++ b/src/lib/Base/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPass.php @@ -0,0 +1,60 @@ +hasDefinition(HandlerRegistry::class)) { + return; + } + + $registry = $container->getDefinition(HandlerRegistry::class); + + $taggedServiceIds = $container->findTaggedServiceIds( + self::SEARCH_LEGACY_GATEWAY_CRITERION_HANDLER_FIELD_VALUE_TAG + ); + foreach ($taggedServiceIds as $serviceId => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['alias'])) { + throw new LogicException( + sprintf( + 'Service "%s" tagged with "%s" service tag needs an "alias" attribute to identify the Field Type.', + $serviceId, + self::SEARCH_LEGACY_GATEWAY_CRITERION_HANDLER_FIELD_VALUE_TAG + ) + ); + } + + $registry->addMethodCall( + 'register', + [ + $attribute['alias'], + new Reference($serviceId), + ] + ); + } + } + } +} + +class_alias(CriterionFieldValueHandlerRegistryPass::class, 'eZ\Publish\Core\Base\Container\Compiler\Search\Legacy\CriterionFieldValueHandlerRegistryPass'); diff --git a/src/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPass.php b/src/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPass.php new file mode 100644 index 0000000000..8de87e3cdd --- /dev/null +++ b/src/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPass.php @@ -0,0 +1,65 @@ +hasDefinition('ibexa.search.legacy.gateway.sort_clause_converter.content') && + !$container->hasDefinition('ibexa.search.legacy.gateway.sort_clause_converter.location') && + !$container->hasDefinition('ibexa.core.trash.search.legacy.gateway.sort_clause_converter') + ) { + return; + } + + if ($container->hasDefinition('ibexa.search.legacy.gateway.sort_clause_converter.content')) { + $sortClauseConverterContent = $container->getDefinition('ibexa.search.legacy.gateway.sort_clause_converter.content'); + + $contentHandlers = $container->findTaggedServiceIds('ibexa.search.legacy.gateway.sort_clause_handler.content'); + + $this->addHandlers($sortClauseConverterContent, $contentHandlers); + } + + if ($container->hasDefinition('ibexa.search.legacy.gateway.sort_clause_converter.location')) { + $sortClauseConverterLocation = $container->getDefinition('ibexa.search.legacy.gateway.sort_clause_converter.location'); + + $locationHandlers = $container->findTaggedServiceIds('ibexa.search.legacy.gateway.sort_clause_handler.location'); + + $this->addHandlers($sortClauseConverterLocation, $locationHandlers); + } + + if ($container->hasDefinition('ibexa.core.trash.search.legacy.gateway.sort_clause_converter')) { + $sortClauseConverterTrash = $container->getDefinition('ibexa.core.trash.search.legacy.gateway.sort_clause_converter'); + + $trashHandlers = $container->findTaggedServiceIds('ibexa.search.legacy.trash.gateway.sort_clause.handler'); + + $this->addHandlers($sortClauseConverterTrash, $trashHandlers); + } + } + + protected function addHandlers(Definition $definition, $handlers) + { + foreach ($handlers as $id => $attributes) { + $definition->addMethodCall('addHandler', [new Reference($id)]); + } + } +} + +class_alias(SortClauseConverterPass::class, 'eZ\Publish\Core\Base\Container\Compiler\Search\Legacy\SortClauseConverterPass'); diff --git a/src/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPass.php b/src/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPass.php new file mode 100644 index 0000000000..165ec9e1e5 --- /dev/null +++ b/src/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPass.php @@ -0,0 +1,138 @@ +hasDefinition(StorageRegistry::class)) { + return; + } + + $externalStorageRegistryDefinition = $container->getDefinition( + StorageRegistry::class + ); + + // Gateways for external storage handlers. + // Alias attribute is the corresponding field type string. + $externalStorageGateways = []; + + $serviceTags = $container->findTaggedServiceIds( + self::EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG + ); + // Referencing the services by alias (field type string) + foreach ($serviceTags as $serviceId => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['alias'])) { + throw new LogicException( + sprintf( + 'Service "%s" tagged with "%s" needs an "alias" attribute to identify the search engine', + $serviceId, + self::EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG + ) + ); + } + + if (!isset($attribute['identifier'])) { + throw new LogicException( + sprintf( + 'Service "%s" tagged with "%s" needs an "alias" attribute to identify the search engine', + $serviceId, + self::EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG + ) + ); + } + + $externalStorageGateways[$attribute['alias']] = [ + 'id' => $serviceId, + 'identifier' => $attribute['identifier'], + ]; + } + } + + $serviceTags = $container->findTaggedServiceIds(self::EXTERNAL_STORAGE_HANDLER_SERVICE_TAG); + // External storage handlers for field types that need them. + // Alias attribute is the field type string. + foreach ($serviceTags as $serviceId => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['alias'])) { + throw new LogicException( + sprintf( + 'Service "%s" tagged with "%s" needs an "alias" attribute to identify the search engine', + $serviceId, + self::EXTERNAL_STORAGE_HANDLER_SERVICE_TAG + ) + ); + } + + // If the storage handler is gateway based, then we need to add a corresponding gateway to it. + // Will throw a LogicException if no gateway is defined for this field type. + $storageHandlerDef = $container->findDefinition($serviceId); + $storageHandlerClass = $storageHandlerDef->getClass(); + if (preg_match('/^%([^%\s]+)%$/', (string)$storageHandlerClass, $match)) { + $storageHandlerClass = $container->getParameter($match[1]); + } + + if ( + is_subclass_of( + $storageHandlerClass, + GatewayBasedStorage::class + ) + ) { + if (!isset($externalStorageGateways[$attribute['alias']])) { + throw new LogicException( + sprintf( + 'External storage handler "%s" for Field Type "%s" needs a storage gateway. ' . + 'Consider defining a storage gateway as a service for this Field Type and add the "%s" tag', + $serviceId, + $attribute['alias'], + self::EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG + ) + ); + } + + $storageHandlerDef->addMethodCall( + 'addGateway', + [ + $externalStorageGateways[$attribute['alias']]['identifier'], + new Reference($externalStorageGateways[$attribute['alias']]['id']), + ] + ); + } + + $externalStorageRegistryDefinition->addMethodCall( + 'register', + [ + $attribute['alias'], + new Reference($serviceId), + ] + ); + } + } + } +} + +class_alias(ExternalStorageRegistryPass::class, 'eZ\Publish\Core\Base\Container\Compiler\Storage\ExternalStorageRegistryPass'); diff --git a/src/lib/Base/Container/Compiler/Storage/Legacy/FieldValueConverterRegistryPass.php b/src/lib/Base/Container/Compiler/Storage/Legacy/FieldValueConverterRegistryPass.php new file mode 100644 index 0000000000..e877da9d60 --- /dev/null +++ b/src/lib/Base/Container/Compiler/Storage/Legacy/FieldValueConverterRegistryPass.php @@ -0,0 +1,65 @@ +hasDefinition(self::CONVERTER_REGISTRY_SERVICE_ID)) { + return; + } + + $registry = $container->getDefinition(self::CONVERTER_REGISTRY_SERVICE_ID); + + $serviceTags = $container->findTaggedServiceIds(self::CONVERTER_SERVICE_TAG); + + foreach ($serviceTags as $serviceId => $attributes) { + foreach ($attributes as $attribute) { + if (!isset($attribute['alias'])) { + throw new LogicException( + sprintf( + 'Service "%s" tagged with "%s" needs an "alias" attribute to identify the search engine', + $serviceId, + self::CONVERTER_SERVICE_TAG + ) + ); + } + + $registry->addMethodCall( + 'register', + [ + $attribute['alias'], + new Reference($serviceId), + ] + ); + } + } + } +} + +class_alias(FieldValueConverterRegistryPass::class, 'eZ\Publish\Core\Base\Container\Compiler\Storage\Legacy\FieldValueConverterRegistryPass'); diff --git a/src/lib/Base/Container/Compiler/Storage/Legacy/RoleLimitationConverterPass.php b/src/lib/Base/Container/Compiler/Storage/Legacy/RoleLimitationConverterPass.php new file mode 100644 index 0000000000..c78295b118 --- /dev/null +++ b/src/lib/Base/Container/Compiler/Storage/Legacy/RoleLimitationConverterPass.php @@ -0,0 +1,43 @@ +hasDefinition(LimitationConverter::class)) { + return; + } + + $roleLimitationConverter = $container->getDefinition(LimitationConverter::class); + + foreach ($container->findTaggedServiceIds('ibexa.storage.legacy.role.limitation.handler') as $id => $attributes) { + foreach ($attributes as $attribute) { + $roleLimitationConverter->addMethodCall( + 'addHandler', + [new Reference($id)] + ); + } + } + } +} + +class_alias(RoleLimitationConverterPass::class, 'eZ\Publish\Core\Base\Container\Compiler\Storage\Legacy\RoleLimitationConverterPass'); diff --git a/eZ/Publish/Core/Base/Container/Compiler/TaggedServiceIdsIterator/BackwardCompatibleIterator.php b/src/lib/Base/Container/Compiler/TaggedServiceIdsIterator/BackwardCompatibleIterator.php similarity index 86% rename from eZ/Publish/Core/Base/Container/Compiler/TaggedServiceIdsIterator/BackwardCompatibleIterator.php rename to src/lib/Base/Container/Compiler/TaggedServiceIdsIterator/BackwardCompatibleIterator.php index 373efbf8b0..c71b2056a0 100644 --- a/eZ/Publish/Core/Base/Container/Compiler/TaggedServiceIdsIterator/BackwardCompatibleIterator.php +++ b/src/lib/Base/Container/Compiler/TaggedServiceIdsIterator/BackwardCompatibleIterator.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Base\Container\Compiler\TaggedServiceIdsIterator; +namespace Ibexa\Core\Base\Container\Compiler\TaggedServiceIdsIterator; use Iterator; use IteratorAggregate; @@ -39,7 +39,7 @@ public function getIterator(): Iterator foreach ($serviceIdsWithDeprecatedTags as $serviceId => $tags) { @trigger_error( sprintf( - 'Service tag `%s` is deprecated and will be removed in eZ Platform 4.0. Tag %s with `%s` instead.', + 'Service tag `%s` is deprecated and will be removed in Ibexa 4.0. Tag %s with `%s` instead.', $this->deprecatedServiceTag, $serviceId, $this->serviceTag @@ -58,3 +58,5 @@ public function getIterator(): Iterator yield from []; } } + +class_alias(BackwardCompatibleIterator::class, 'eZ\Publish\Core\Base\Container\Compiler\TaggedServiceIdsIterator\BackwardCompatibleIterator'); diff --git a/src/lib/Base/Exceptions/BadStateException.php b/src/lib/Base/Exceptions/BadStateException.php new file mode 100644 index 0000000000..a4c41bd537 --- /dev/null +++ b/src/lib/Base/Exceptions/BadStateException.php @@ -0,0 +1,38 @@ +setMessageTemplate("Argument '%argumentName%' has a bad state: %whatIsWrong%"); + $this->setParameters(['%argumentName%' => $argumentName, '%whatIsWrong%' => $whatIsWrong]); + parent::__construct($this->getBaseTranslation(), 0, $previous); + } +} + +class_alias(BadStateException::class, 'eZ\Publish\Core\Base\Exceptions\BadStateException'); diff --git a/src/lib/Base/Exceptions/ContentFieldValidationException.php b/src/lib/Base/Exceptions/ContentFieldValidationException.php new file mode 100644 index 0000000000..0b8091dca4 --- /dev/null +++ b/src/lib/Base/Exceptions/ContentFieldValidationException.php @@ -0,0 +1,123 @@ + + * $fieldErrors = $exception->getFieldErrors(); + * $fieldErrors[43]["eng-GB"]->getTranslatableMessage(); + * + * + * @var array> + */ + protected $errors; + + /** @var string|null */ + protected $contentName; + + /** + * Generates: Content fields did not validate. + * + * Also sets the given $fieldErrors to the internal property, retrievable by getFieldErrors() + * + * @param array> $errors + */ + public function __construct(array $errors) + { + $this->errors = $errors; + $this->setMessageTemplate('Content fields did not validate'); + parent::__construct($this->getBaseTranslation()); + } + + /** + * Generates: Content fields did not validate exception with additional information on affected fields. + * + * @param array> $errors + */ + public static function createNewWithMultiline(array $errors, ?string $contentName = null): self + { + $exception = new self($errors); + $exception->contentName = $contentName; + + $exception->setMessageTemplate('Content "%contentName%" fields did not validate: %errors%'); + $exception->setParameters([ + '%errors%' => $exception->generateValidationErrorsMessages(), + '%contentName%' => $exception->contentName !== null ? $exception->contentName : '', + ]); + $exception->message = $exception->getBaseTranslation(); + + return $exception; + } + + /** + * Returns an array of field validation error messages. + * + * @return array> + */ + public function getFieldErrors() + { + return $this->errors; + } + + private function generateValidationErrorsMessages(): string + { + $validationErrors = $this->collectValidationErrors(); + $maxMessagesNumber = self::MAX_MESSAGES_NUMBER; + + if (count($validationErrors) > $maxMessagesNumber) { + array_splice($validationErrors, $maxMessagesNumber); + $validationErrors[] = sprintf('Limit: %d of validation errors has been exceeded.', $maxMessagesNumber); + } + + /** @var callable(string|\Ibexa\Contracts\Core\Repository\Values\Translation): string $convertToString */ + $convertToString = static function ($error): string { + return (string)$error; + }; + $validationErrors = array_map($convertToString, $validationErrors); + + return "\n- " . implode("\n- ", $validationErrors); + } + + /** + * @return array<\Ibexa\Contracts\Core\Repository\Values\Translation> + */ + private function collectValidationErrors(): array + { + $messages = []; + foreach ($this->getFieldErrors() as $validationErrors) { + foreach ($validationErrors as $validationError) { + if (is_array($validationError)) { + foreach ($validationError as $item) { + $messages[] = $item->getTranslatableMessage(); + } + } else { + $messages[] = $validationError->getTranslatableMessage(); + } + } + } + + return $messages; + } +} + +class_alias(ContentFieldValidationException::class, 'eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException'); diff --git a/src/lib/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php b/src/lib/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php new file mode 100644 index 0000000000..a2046b4870 --- /dev/null +++ b/src/lib/Base/Exceptions/ContentTypeFieldDefinitionValidationException.php @@ -0,0 +1,58 @@ + + * $fieldErrors = $exception->getFieldErrors(); + * $fieldErrors["43"]["eng-GB"]->getTranslatableMessage(); + * + * + * @var \Ibexa\Core\FieldType\ValidationError[] + */ + protected $errors; + + /** + * Generates: Content fields did not validate. + * + * Also sets the given $fieldErrors to the internal property, retrievable by getFieldErrors() + * + * @param \Ibexa\Core\FieldType\ValidationError[] $errors + */ + public function __construct(array $errors) + { + $this->errors = $errors; + $this->setMessageTemplate('Content type field definitions did not validate'); + parent::__construct($this->getBaseTranslation()); + } + + /** + * Returns an array of field validation error messages. + * + * @return \Ibexa\Core\FieldType\ValidationError[] + */ + public function getFieldErrors() + { + return $this->errors; + } +} + +class_alias(ContentTypeFieldDefinitionValidationException::class, 'eZ\Publish\Core\Base\Exceptions\ContentTypeFieldDefinitionValidationException'); diff --git a/src/lib/Base/Exceptions/ContentTypeValidationException.php b/src/lib/Base/Exceptions/ContentTypeValidationException.php new file mode 100644 index 0000000000..43f2e768db --- /dev/null +++ b/src/lib/Base/Exceptions/ContentTypeValidationException.php @@ -0,0 +1,36 @@ + 123). + */ + public function __construct($messageTemplate, array $parameters = []) + { + $this->setMessageTemplate(/** @Ignore */$messageTemplate); + $this->setParameters($parameters); + + parent::__construct($this->getBaseTranslation()); + } +} + +class_alias(ContentTypeValidationException::class, 'eZ\Publish\Core\Base\Exceptions\ContentTypeValidationException'); diff --git a/src/lib/Base/Exceptions/ContentValidationException.php b/src/lib/Base/Exceptions/ContentValidationException.php new file mode 100644 index 0000000000..83ba4aa0c4 --- /dev/null +++ b/src/lib/Base/Exceptions/ContentValidationException.php @@ -0,0 +1,36 @@ + 123). + */ + public function __construct($messageTemplate, array $parameters = []) + { + $this->setMessageTemplate(/** @Ignore */$messageTemplate); + $this->setParameters($parameters); + + parent::__construct($this->getBaseTranslation()); + } +} + +class_alias(ContentValidationException::class, 'eZ\Publish\Core\Base\Exceptions\ContentValidationException'); diff --git a/eZ/Publish/Core/Base/Exceptions/DatabaseException.php b/src/lib/Base/Exceptions/DatabaseException.php similarity index 83% rename from eZ/Publish/Core/Base/Exceptions/DatabaseException.php rename to src/lib/Base/Exceptions/DatabaseException.php index 9678fb6269..7d4c4ee8dc 100644 --- a/eZ/Publish/Core/Base/Exceptions/DatabaseException.php +++ b/src/lib/Base/Exceptions/DatabaseException.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Base\Exceptions; +namespace Ibexa\Core\Base\Exceptions; use RuntimeException; use Throwable; @@ -26,3 +26,5 @@ public static function wrap( return new self($message, $code, $previous); } } + +class_alias(DatabaseException::class, 'eZ\Publish\Core\Base\Exceptions\DatabaseException'); diff --git a/src/lib/Base/Exceptions/ForbiddenException.php b/src/lib/Base/Exceptions/ForbiddenException.php new file mode 100644 index 0000000000..5f80bab8ff --- /dev/null +++ b/src/lib/Base/Exceptions/ForbiddenException.php @@ -0,0 +1,36 @@ + 123). + */ + public function __construct($messageTemplate, array $parameters = []) + { + /** @Ignore */ + $this->setMessageTemplate($messageTemplate); + $this->setParameters($parameters); + + parent::__construct($this->getBaseTranslation()); + } +} + +class_alias(ForbiddenException::class, 'eZ\Publish\Core\Base\Exceptions\ForbiddenException'); diff --git a/eZ/Publish/Core/Base/Exceptions/Httpable.php b/src/lib/Base/Exceptions/Httpable.php similarity index 88% rename from eZ/Publish/Core/Base/Exceptions/Httpable.php rename to src/lib/Base/Exceptions/Httpable.php index 46eb4c7f0c..5a8c44f056 100644 --- a/eZ/Publish/Core/Base/Exceptions/Httpable.php +++ b/src/lib/Base/Exceptions/Httpable.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Base\Exceptions; +namespace Ibexa\Core\Base\Exceptions; /** * Interface for exceptions that maps to http status codes. @@ -29,3 +29,5 @@ interface Httpable public const NOT_IMPLEMENTED = 501; public const SERVICE_UNAVAILABLE = 503; } + +class_alias(Httpable::class, 'eZ\Publish\Core\Base\Exceptions\Httpable'); diff --git a/src/lib/Base/Exceptions/InvalidArgumentException.php b/src/lib/Base/Exceptions/InvalidArgumentException.php new file mode 100644 index 0000000000..95aafad9cf --- /dev/null +++ b/src/lib/Base/Exceptions/InvalidArgumentException.php @@ -0,0 +1,38 @@ +setMessageTemplate("Argument '%argumentName%' is invalid: %whatIsWrong%"); + $this->setParameters(['%argumentName%' => $argumentName, '%whatIsWrong%' => $whatIsWrong]); + parent::__construct($this->getBaseTranslation(), 0, $previous); + } +} + +class_alias(InvalidArgumentException::class, 'eZ\Publish\Core\Base\Exceptions\InvalidArgumentException'); diff --git a/src/lib/Base/Exceptions/InvalidArgumentType.php b/src/lib/Base/Exceptions/InvalidArgumentType.php new file mode 100644 index 0000000000..7a4e7799fe --- /dev/null +++ b/src/lib/Base/Exceptions/InvalidArgumentType.php @@ -0,0 +1,42 @@ + $argumentName, '%expectedType%' => $expectedType]; + $this->setMessageTemplate("Argument '%argumentName%' is invalid: value must be of type '%expectedType%'"); + if ($value) { + $this->setMessageTemplate("Argument '%argumentName%' is invalid: value must be of type '%expectedType%', not '%actualType%'"); + $actualType = is_object($value) ? get_class($value) : gettype($value); + $parameters['%actualType%'] = $actualType; + } + + /** @Ignore */ + $this->addParameters($parameters); + $this->message = $this->getBaseTranslation(); + } +} + +class_alias(InvalidArgumentType::class, 'eZ\Publish\Core\Base\Exceptions\InvalidArgumentType'); diff --git a/eZ/Publish/Core/Base/Exceptions/InvalidArgumentValue.php b/src/lib/Base/Exceptions/InvalidArgumentValue.php similarity index 92% rename from eZ/Publish/Core/Base/Exceptions/InvalidArgumentValue.php rename to src/lib/Base/Exceptions/InvalidArgumentValue.php index b9c52742a8..02dad42371 100644 --- a/eZ/Publish/Core/Base/Exceptions/InvalidArgumentValue.php +++ b/src/lib/Base/Exceptions/InvalidArgumentValue.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Base\Exceptions; +namespace Ibexa\Core\Base\Exceptions; use Exception; @@ -43,3 +43,5 @@ public function __construct($argumentName, $value, $className = null, Exception $this->message = $this->getBaseTranslation(); } } + +class_alias(InvalidArgumentValue::class, 'eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue'); diff --git a/src/lib/Base/Exceptions/LimitationValidationException.php b/src/lib/Base/Exceptions/LimitationValidationException.php new file mode 100644 index 0000000000..d16dec73f3 --- /dev/null +++ b/src/lib/Base/Exceptions/LimitationValidationException.php @@ -0,0 +1,53 @@ +validationErrors = $errors; + $this->setMessageTemplate('Limitations did not validate'); + parent::__construct($this->getBaseTranslation()); + } + + /** + * Returns an array of limitation ValidationError objects. + * + * @return \Ibexa\Core\FieldType\ValidationError[] + */ + public function getLimitationErrors() + { + return $this->errors; + } +} + +class_alias(LimitationValidationException::class, 'eZ\Publish\Core\Base\Exceptions\LimitationValidationException'); diff --git a/eZ/Publish/Core/Base/Exceptions/MissingClass.php b/src/lib/Base/Exceptions/MissingClass.php similarity index 87% rename from eZ/Publish/Core/Base/Exceptions/MissingClass.php rename to src/lib/Base/Exceptions/MissingClass.php index bb9943ac6a..76ea1008ad 100644 --- a/eZ/Publish/Core/Base/Exceptions/MissingClass.php +++ b/src/lib/Base/Exceptions/MissingClass.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Base\Exceptions; +namespace Ibexa\Core\Base\Exceptions; use Exception; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; +use Ibexa\Core\Base\Translatable; +use Ibexa\Core\Base\TranslatableBase; use LogicException; /** @@ -43,3 +43,5 @@ public function __construct($className, $classType = null, Exception $previous = parent::__construct($this->getBaseTranslation(), 0, $previous); } } + +class_alias(MissingClass::class, 'eZ\Publish\Core\Base\Exceptions\MissingClass'); diff --git a/src/lib/Base/Exceptions/MissingUserFieldTypeException.php b/src/lib/Base/Exceptions/MissingUserFieldTypeException.php new file mode 100644 index 0000000000..495acb4553 --- /dev/null +++ b/src/lib/Base/Exceptions/MissingUserFieldTypeException.php @@ -0,0 +1,28 @@ + $contentType->identifier, + 'fieldType' => $fieldType, + ] + ); + } +} + +class_alias(MissingUserFieldTypeException::class, 'eZ\Publish\Core\Base\Exceptions\MissingUserFieldTypeException'); diff --git a/eZ/Publish/Core/Base/Exceptions/NotFound/FieldTypeNotFoundException.php b/src/lib/Base/Exceptions/NotFound/FieldTypeNotFoundException.php similarity index 76% rename from eZ/Publish/Core/Base/Exceptions/NotFound/FieldTypeNotFoundException.php rename to src/lib/Base/Exceptions/NotFound/FieldTypeNotFoundException.php index da1b77eb75..1ea37f1d2b 100644 --- a/eZ/Publish/Core/Base/Exceptions/NotFound/FieldTypeNotFoundException.php +++ b/src/lib/Base/Exceptions/NotFound/FieldTypeNotFoundException.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Base\Exceptions\NotFound; +namespace Ibexa\Core\Base\Exceptions\NotFound; use Exception; -use eZ\Publish\Core\Base\Exceptions\Httpable; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; -use eZ\Publish\Core\FieldType\Null\Type as NullType; +use Ibexa\Core\Base\Exceptions\Httpable; +use Ibexa\Core\Base\Translatable; +use Ibexa\Core\Base\TranslatableBase; +use Ibexa\Core\FieldType\Null\Type as NullType; use RuntimeException; /** @@ -41,3 +41,5 @@ public function __construct($fieldType, Exception $previous = null) parent::__construct($this->getBaseTranslation(), self::INTERNAL_ERROR, $previous); } } + +class_alias(FieldTypeNotFoundException::class, 'eZ\Publish\Core\Base\Exceptions\NotFound\FieldTypeNotFoundException'); diff --git a/eZ/Publish/Core/Base/Exceptions/NotFound/LimitationNotFoundException.php b/src/lib/Base/Exceptions/NotFound/LimitationNotFoundException.php similarity index 76% rename from eZ/Publish/Core/Base/Exceptions/NotFound/LimitationNotFoundException.php rename to src/lib/Base/Exceptions/NotFound/LimitationNotFoundException.php index 6470ff3a94..45125f777a 100644 --- a/eZ/Publish/Core/Base/Exceptions/NotFound/LimitationNotFoundException.php +++ b/src/lib/Base/Exceptions/NotFound/LimitationNotFoundException.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Base\Exceptions\NotFound; +namespace Ibexa\Core\Base\Exceptions\NotFound; use Exception; -use eZ\Publish\Core\Base\Exceptions\Httpable; -use eZ\Publish\Core\Base\Translatable; -use eZ\Publish\Core\Base\TranslatableBase; -use eZ\Publish\Core\Limitation\BlockingLimitationType; +use Ibexa\Core\Base\Exceptions\Httpable; +use Ibexa\Core\Base\Translatable; +use Ibexa\Core\Base\TranslatableBase; +use Ibexa\Core\Limitation\BlockingLimitationType; use RuntimeException; /** @@ -41,3 +41,5 @@ public function __construct($limitation, Exception $previous = null) parent::__construct($this->getBaseTranslation(), self::INTERNAL_ERROR, $previous); } } + +class_alias(LimitationNotFoundException::class, 'eZ\Publish\Core\Base\Exceptions\NotFound\LimitationNotFoundException'); diff --git a/src/lib/Base/Exceptions/NotFoundException.php b/src/lib/Base/Exceptions/NotFoundException.php new file mode 100644 index 0000000000..efbd4242e5 --- /dev/null +++ b/src/lib/Base/Exceptions/NotFoundException.php @@ -0,0 +1,40 @@ +setMessageTemplate("Could not find '%what%' with identifier '%identifier%'"); + $this->setParameters(['%what%' => $what, '%identifier%' => $identifierStr]); + parent::__construct($this->getBaseTranslation(), self::NOT_FOUND, $previous); + } +} + +class_alias(NotFoundException::class, 'eZ\Publish\Core\Base\Exceptions\NotFoundException'); diff --git a/src/lib/Base/Exceptions/TokenExpiredException.php b/src/lib/Base/Exceptions/TokenExpiredException.php new file mode 100644 index 0000000000..4157cc2c8d --- /dev/null +++ b/src/lib/Base/Exceptions/TokenExpiredException.php @@ -0,0 +1,36 @@ +setMessageTemplate("Token '%tokenType%:%token%' expired on '%when%'"); + $this->setParameters([ + '%tokenType%' => $tokenType, + '%token%' => $token, + '%when%' => $when->format(DateTimeInterface::ATOM), + ]); + + parent::__construct($this->getBaseTranslation(), self::UNAUTHORIZED, $previous); + } +} diff --git a/src/lib/Base/Exceptions/TokenLengthException.php b/src/lib/Base/Exceptions/TokenLengthException.php new file mode 100644 index 0000000000..f3507ddec3 --- /dev/null +++ b/src/lib/Base/Exceptions/TokenLengthException.php @@ -0,0 +1,32 @@ + 42 ) ); + */ +class UnauthorizedException extends APIUnauthorizedException implements Httpable, Translatable +{ + use TranslatableBase; + + /** + * Generates: User does not have access to '{$function}' '{$module}'[ with: %property.key% '%property.value%']. + * + * Example: User does not have access to 'read' 'content' with: id '44', type 'article' + * + * @param string $module The module name should be in sync with the name of the domain object in question + * @param string $function + * @param array $properties Key value pair with non sensitive data on what kind of data user does not have access to + * @param \Exception|null $previous + */ + public function __construct($module, $function, array $properties = null, Exception $previous = null) + { + $this->setMessageTemplate("The User does not have the '%function%' '%module%' permission"); + $this->setParameters(['%module%' => $module, '%function%' => $function]); + + if ($properties) { + $this->setMessageTemplate("The User does not have the '%function%' '%module%' permission with: %with%"); + $with = []; + foreach ($properties as $name => $value) { + $with[] = "{$name} '{$value}'"; + } + $this->addParameter('%with%', implode(', ', $with)); + } + + parent::__construct($this->getBaseTranslation(), self::UNAUTHORIZED, $previous); + } +} + +class_alias(UnauthorizedException::class, 'eZ\Publish\Core\Base\Exceptions\UnauthorizedException'); diff --git a/eZ/Publish/Core/Base/Exceptions/UserPasswordValidationException.php b/src/lib/Base/Exceptions/UserPasswordValidationException.php similarity index 82% rename from eZ/Publish/Core/Base/Exceptions/UserPasswordValidationException.php rename to src/lib/Base/Exceptions/UserPasswordValidationException.php index b8c0c65285..638e5f9c6d 100644 --- a/eZ/Publish/Core/Base/Exceptions/UserPasswordValidationException.php +++ b/src/lib/Base/Exceptions/UserPasswordValidationException.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Base\Exceptions; +namespace Ibexa\Core\Base\Exceptions; use Exception; -use eZ\Publish\Core\FieldType\ValidationError; +use Ibexa\Core\FieldType\ValidationError; class UserPasswordValidationException extends InvalidArgumentException { @@ -29,3 +29,5 @@ public function __construct(string $argumentName, array $errors, Exception $prev parent::__construct($argumentName, 'The password does not match the following rules: ' . implode(', ', $rules), $previous); } } + +class_alias(UserPasswordValidationException::class, 'eZ\Publish\Core\Base\Exceptions\UserPasswordValidationException'); diff --git a/eZ/Publish/Core/Base/ServiceContainer.php b/src/lib/Base/ServiceContainer.php similarity index 85% rename from eZ/Publish/Core/Base/ServiceContainer.php rename to src/lib/Base/ServiceContainer.php index eb96a30788..3dc1ae862d 100644 --- a/eZ/Publish/Core/Base/ServiceContainer.php +++ b/src/lib/Base/ServiceContainer.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Base; +namespace Ibexa\Core\Base; -use eZ\Publish\API\Container; +use Ibexa\Contracts\Core\Container; use RuntimeException; use Symfony\Bridge\ProxyManager\LazyProxy\PhpDumper\ProxyDumper; use Symfony\Component\Config\ConfigCache; @@ -25,7 +25,7 @@ class ServiceContainer implements Container * * @var string */ - protected $containerClassName = 'EzPublishPublicAPIServiceContainer'; + protected $containerClassName = 'IbexaPublicAPIServiceContainer'; /** * Holds inner Symfony container instance. @@ -64,7 +64,7 @@ class ServiceContainer implements Container /** * @param string|\Symfony\Component\DependencyInjection\ContainerInterface $container Path to the container file or container instance - * @param string $installDir Installation directory, required by default 'containerBuilder.php' file + * @param string $installDir Installation directory * @param string $cacheDir Directory where PHP container cache will be stored * @param bool $debug Default false should be used for production, if true resources will be checked * and cache will be regenerated if necessary @@ -86,11 +86,11 @@ public function __construct($container, $installDir, $cacheDir, $debug = false, * * Public API for * - * @return \eZ\Publish\API\Repository\Repository + * @return \Ibexa\Contracts\Core\Repository\Repository */ public function getRepository() { - return $this->innerContainer->get('ezpublish.api.repository'); + return $this->innerContainer->get('ibexa.api.repository'); } /** @@ -162,25 +162,13 @@ protected function initializeContainer() } /** - * Returns ContainerBuilder by including the default file 'containerBuilder.php' from settings directory. - * * @throws \RuntimeException */ protected function getContainer() { - if ($this->innerContainer instanceof ContainerInterface) { + if (!$this->innerContainer instanceof ContainerInterface) { + throw new RuntimeException('Inner Container is not initialized'); // Do nothing - } elseif (!is_readable($this->innerContainer)) { - throw new RuntimeException( - sprintf( - "Unable to read file %s\n", - $this->innerContainer - ) - ); - } else { - // 'containerBuilder.php' file expects $installDir variable to be set by caller - $installDir = $this->installDir; - $this->innerContainer = require_once $this->innerContainer; } // Compile container if necessary @@ -243,3 +231,5 @@ protected function prepareDirectory($directory, $name) } } } + +class_alias(ServiceContainer::class, 'eZ\Publish\Core\Base\ServiceContainer'); diff --git a/src/lib/Base/Translatable.php b/src/lib/Base/Translatable.php new file mode 100644 index 0000000000..479d41e9a2 --- /dev/null +++ b/src/lib/Base/Translatable.php @@ -0,0 +1,69 @@ + 123). + * + * @return array + */ + public function getParameters(); + + /** + * Injects the hash map, with param placeholder as key and its corresponding value. + * E.g. array('%contentId%' => 123). + * If parameters already existed, they will be replaced by the passed here. + * + * @param array $parameters + */ + public function setParameters(array $parameters); + + /** + * Adds a parameter to existing hash map. + * + * @param string $name + * @param string $value + */ + public function addParameter($name, $value); + + /** + * Adds $parameters to existing hash map. + * + * @param array $parameters + */ + public function addParameters(array $parameters); + + /** + * Returns base translation, computed with message template and parameters. + * + * @return string + */ + public function getBaseTranslation(); +} + +class_alias(Translatable::class, 'eZ\Publish\Core\Base\Translatable'); diff --git a/eZ/Publish/Core/Base/TranslatableBase.php b/src/lib/Base/TranslatableBase.php similarity index 90% rename from eZ/Publish/Core/Base/TranslatableBase.php rename to src/lib/Base/TranslatableBase.php index b3c7f33150..f3f15507e7 100644 --- a/eZ/Publish/Core/Base/TranslatableBase.php +++ b/src/lib/Base/TranslatableBase.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Base; +namespace Ibexa\Core\Base; /** * Trait providing a default implementation of Translatable. @@ -50,3 +50,5 @@ public function getBaseTranslation() return strtr($this->messageTemplate, $this->parameters); } } + +class_alias(TranslatableBase::class, 'eZ\Publish\Core\Base\TranslatableBase'); diff --git a/src/lib/Base/Utils/DeprecationWarner.php b/src/lib/Base/Utils/DeprecationWarner.php new file mode 100644 index 0000000000..dd4017eaa5 --- /dev/null +++ b/src/lib/Base/Utils/DeprecationWarner.php @@ -0,0 +1,17 @@ +eventDispatcher = $eventDispatcher; + } + + public function createBookmark(Location $location): void + { + $eventData = [$location]; + + $beforeEvent = new BeforeCreateBookmarkEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->createBookmark($location); + + $this->eventDispatcher->dispatch(new CreateBookmarkEvent(...$eventData)); + } + + public function deleteBookmark(Location $location): void + { + $eventData = [$location]; + + $beforeEvent = new BeforeDeleteBookmarkEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteBookmark($location); + + $this->eventDispatcher->dispatch(new DeleteBookmarkEvent(...$eventData)); + } +} + +class_alias(BookmarkService::class, 'eZ\Publish\Core\Event\BookmarkService'); diff --git a/src/lib/Event/ContentService.php b/src/lib/Event/ContentService.php new file mode 100644 index 0000000000..dee89f597c --- /dev/null +++ b/src/lib/Event/ContentService.php @@ -0,0 +1,385 @@ +eventDispatcher = $eventDispatcher; + } + + public function createContent( + ContentCreateStruct $contentCreateStruct, + array $locationCreateStructs = [], + ?array $fieldIdentifiersToValidate = null + ): Content { + $eventData = [ + $contentCreateStruct, + $locationCreateStructs, + $fieldIdentifiersToValidate, + ]; + + $beforeEvent = new BeforeCreateContentEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getContent(); + } + + $content = $beforeEvent->hasContent() + ? $beforeEvent->getContent() + : $this->innerService->createContent($contentCreateStruct, $locationCreateStructs, $fieldIdentifiersToValidate); + + $this->eventDispatcher->dispatch( + new CreateContentEvent($content, ...$eventData) + ); + + return $content; + } + + public function updateContentMetadata( + ContentInfo $contentInfo, + ContentMetadataUpdateStruct $contentMetadataUpdateStruct + ): Content { + $eventData = [ + $contentInfo, + $contentMetadataUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdateContentMetadataEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getContent(); + } + + $content = $beforeEvent->hasContent() + ? $beforeEvent->getContent() + : $this->innerService->updateContentMetadata($contentInfo, $contentMetadataUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdateContentMetadataEvent($content, ...$eventData) + ); + + return $content; + } + + public function deleteContent(ContentInfo $contentInfo): iterable + { + $eventData = [$contentInfo]; + + $beforeEvent = new BeforeDeleteContentEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getLocations(); + } + + $locations = $beforeEvent->hasLocations() + ? $beforeEvent->getLocations() + : $this->innerService->deleteContent($contentInfo); + + $this->eventDispatcher->dispatch( + new DeleteContentEvent($locations, ...$eventData) + ); + + return $locations; + } + + public function createContentDraft( + ContentInfo $contentInfo, + ?VersionInfo $versionInfo = null, + ?User $creator = null, + ?Language $language = null + ): Content { + $eventData = [ + $contentInfo, + $versionInfo, + $creator, + $language, + ]; + + $beforeEvent = new BeforeCreateContentDraftEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getContentDraft(); + } + + $contentDraft = $beforeEvent->hasContentDraft() + ? $beforeEvent->getContentDraft() + : $this->innerService->createContentDraft($contentInfo, $versionInfo, $creator, $language); + + $this->eventDispatcher->dispatch( + new CreateContentDraftEvent($contentDraft, ...$eventData) + ); + + return $contentDraft; + } + + public function updateContent( + VersionInfo $versionInfo, + ContentUpdateStruct $contentUpdateStruct, + ?array $fieldIdentifiersToValidate = null + ): Content { + $eventData = [ + $versionInfo, + $contentUpdateStruct, + $fieldIdentifiersToValidate, + ]; + + $beforeEvent = new BeforeUpdateContentEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getContent(); + } + + $content = $beforeEvent->hasContent() + ? $beforeEvent->getContent() + : $this->innerService->updateContent($versionInfo, $contentUpdateStruct, $fieldIdentifiersToValidate); + + $this->eventDispatcher->dispatch( + new UpdateContentEvent($content, ...$eventData) + ); + + return $content; + } + + public function publishVersion(VersionInfo $versionInfo, array $translations = Language::ALL): Content + { + $eventData = [ + $versionInfo, + $translations, + ]; + + $beforeEvent = new BeforePublishVersionEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getContent(); + } + + $content = $beforeEvent->hasContent() + ? $beforeEvent->getContent() + : $this->innerService->publishVersion($versionInfo, $translations); + + $this->eventDispatcher->dispatch( + new PublishVersionEvent($content, ...$eventData) + ); + + return $content; + } + + public function deleteVersion(VersionInfo $versionInfo): void + { + $eventData = [$versionInfo]; + + $beforeEvent = new BeforeDeleteVersionEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteVersion($versionInfo); + + $this->eventDispatcher->dispatch( + new DeleteVersionEvent(...$eventData) + ); + } + + public function copyContent( + ContentInfo $contentInfo, + LocationCreateStruct $destinationLocationCreateStruct, + ?VersionInfo $versionInfo = null + ): Content { + $eventData = [ + $contentInfo, + $destinationLocationCreateStruct, + $versionInfo, + ]; + + $beforeEvent = new BeforeCopyContentEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getContent(); + } + + $content = $beforeEvent->hasContent() + ? $beforeEvent->getContent() + : $this->innerService->copyContent($contentInfo, $destinationLocationCreateStruct, $versionInfo); + + $this->eventDispatcher->dispatch( + new CopyContentEvent($content, ...$eventData) + ); + + return $content; + } + + public function addRelation( + VersionInfo $sourceVersion, + ContentInfo $destinationContent + ): Relation { + $eventData = [ + $sourceVersion, + $destinationContent, + ]; + + $beforeEvent = new BeforeAddRelationEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getRelation(); + } + + $relation = $beforeEvent->hasRelation() + ? $beforeEvent->getRelation() + : $this->innerService->addRelation($sourceVersion, $destinationContent); + + $this->eventDispatcher->dispatch( + new AddRelationEvent($relation, ...$eventData) + ); + + return $relation; + } + + public function deleteRelation( + VersionInfo $sourceVersion, + ContentInfo $destinationContent + ): void { + $eventData = [ + $sourceVersion, + $destinationContent, + ]; + + $beforeEvent = new BeforeDeleteRelationEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteRelation($sourceVersion, $destinationContent); + + $this->eventDispatcher->dispatch( + new DeleteRelationEvent(...$eventData) + ); + } + + public function deleteTranslation( + ContentInfo $contentInfo, + string $languageCode + ): void { + $eventData = [ + $contentInfo, + $languageCode, + ]; + + $beforeEvent = new BeforeDeleteTranslationEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteTranslation($contentInfo, $languageCode); + + $this->eventDispatcher->dispatch( + new DeleteTranslationEvent(...$eventData) + ); + } + + public function hideContent(ContentInfo $contentInfo): void + { + $eventData = [$contentInfo]; + + $beforeEvent = new BeforeHideContentEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->hideContent($contentInfo); + + $this->eventDispatcher->dispatch( + new HideContentEvent(...$eventData) + ); + } + + public function revealContent(ContentInfo $contentInfo): void + { + $eventData = [$contentInfo]; + + $beforeEvent = new BeforeRevealContentEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->revealContent($contentInfo); + + $this->eventDispatcher->dispatch( + new RevealContentEvent(...$eventData) + ); + } +} + +class_alias(ContentService::class, 'eZ\Publish\Core\Event\ContentService'); diff --git a/src/lib/Event/ContentTypeService.php b/src/lib/Event/ContentTypeService.php new file mode 100644 index 0000000000..b1beeec89b --- /dev/null +++ b/src/lib/Event/ContentTypeService.php @@ -0,0 +1,413 @@ +eventDispatcher = $eventDispatcher; + } + + public function createContentTypeGroup(ContentTypeGroupCreateStruct $contentTypeGroupCreateStruct): ContentTypeGroup + { + $eventData = [$contentTypeGroupCreateStruct]; + + $beforeEvent = new BeforeCreateContentTypeGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getContentTypeGroup(); + } + + $contentTypeGroup = $beforeEvent->hasContentTypeGroup() + ? $beforeEvent->getContentTypeGroup() + : $this->innerService->createContentTypeGroup($contentTypeGroupCreateStruct); + + $this->eventDispatcher->dispatch( + new CreateContentTypeGroupEvent($contentTypeGroup, ...$eventData) + ); + + return $contentTypeGroup; + } + + public function updateContentTypeGroup( + ContentTypeGroup $contentTypeGroup, + ContentTypeGroupUpdateStruct $contentTypeGroupUpdateStruct + ): void { + $eventData = [ + $contentTypeGroup, + $contentTypeGroupUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdateContentTypeGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->updateContentTypeGroup($contentTypeGroup, $contentTypeGroupUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdateContentTypeGroupEvent(...$eventData) + ); + } + + public function deleteContentTypeGroup(ContentTypeGroup $contentTypeGroup): void + { + $eventData = [$contentTypeGroup]; + + $beforeEvent = new BeforeDeleteContentTypeGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteContentTypeGroup($contentTypeGroup); + + $this->eventDispatcher->dispatch( + new DeleteContentTypeGroupEvent(...$eventData) + ); + } + + public function createContentType( + ContentTypeCreateStruct $contentTypeCreateStruct, + array $contentTypeGroups + ): ContentTypeDraft { + $eventData = [ + $contentTypeCreateStruct, + $contentTypeGroups, + ]; + + $beforeEvent = new BeforeCreateContentTypeEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getContentTypeDraft(); + } + + $contentTypeDraft = $beforeEvent->hasContentTypeDraft() + ? $beforeEvent->getContentTypeDraft() + : $this->innerService->createContentType($contentTypeCreateStruct, $contentTypeGroups); + + $this->eventDispatcher->dispatch( + new CreateContentTypeEvent($contentTypeDraft, ...$eventData) + ); + + return $contentTypeDraft; + } + + public function createContentTypeDraft(ContentType $contentType): ContentTypeDraft + { + $eventData = [$contentType]; + + $beforeEvent = new BeforeCreateContentTypeDraftEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getContentTypeDraft(); + } + + $contentTypeDraft = $beforeEvent->hasContentTypeDraft() + ? $beforeEvent->getContentTypeDraft() + : $this->innerService->createContentTypeDraft($contentType); + + $this->eventDispatcher->dispatch( + new CreateContentTypeDraftEvent($contentTypeDraft, ...$eventData) + ); + + return $contentTypeDraft; + } + + public function updateContentTypeDraft( + ContentTypeDraft $contentTypeDraft, + ContentTypeUpdateStruct $contentTypeUpdateStruct + ): void { + $eventData = [ + $contentTypeDraft, + $contentTypeUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdateContentTypeDraftEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->updateContentTypeDraft($contentTypeDraft, $contentTypeUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdateContentTypeDraftEvent(...$eventData) + ); + } + + public function deleteContentType(ContentType $contentType): void + { + $eventData = [$contentType]; + + $beforeEvent = new BeforeDeleteContentTypeEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteContentType($contentType); + + $this->eventDispatcher->dispatch( + new DeleteContentTypeEvent(...$eventData) + ); + } + + public function copyContentType( + ContentType $contentType, + User $creator = null + ): ContentType { + $eventData = [ + $contentType, + $creator, + ]; + + $beforeEvent = new BeforeCopyContentTypeEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getContentTypeCopy(); + } + + $contentTypeCopy = $beforeEvent->hasContentTypeCopy() + ? $beforeEvent->getContentTypeCopy() + : $this->innerService->copyContentType($contentType, $creator); + + $this->eventDispatcher->dispatch( + new CopyContentTypeEvent($contentTypeCopy, ...$eventData) + ); + + return $contentTypeCopy; + } + + public function assignContentTypeGroup( + ContentType $contentType, + ContentTypeGroup $contentTypeGroup + ): void { + $eventData = [ + $contentType, + $contentTypeGroup, + ]; + + $beforeEvent = new BeforeAssignContentTypeGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->assignContentTypeGroup($contentType, $contentTypeGroup); + + $this->eventDispatcher->dispatch( + new AssignContentTypeGroupEvent(...$eventData) + ); + } + + public function unassignContentTypeGroup( + ContentType $contentType, + ContentTypeGroup $contentTypeGroup + ): void { + $eventData = [ + $contentType, + $contentTypeGroup, + ]; + + $beforeEvent = new BeforeUnassignContentTypeGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->unassignContentTypeGroup($contentType, $contentTypeGroup); + + $this->eventDispatcher->dispatch( + new UnassignContentTypeGroupEvent(...$eventData) + ); + } + + public function addFieldDefinition( + ContentTypeDraft $contentTypeDraft, + FieldDefinitionCreateStruct $fieldDefinitionCreateStruct + ): void { + $eventData = [ + $contentTypeDraft, + $fieldDefinitionCreateStruct, + ]; + + $beforeEvent = new BeforeAddFieldDefinitionEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->addFieldDefinition($contentTypeDraft, $fieldDefinitionCreateStruct); + + $this->eventDispatcher->dispatch( + new AddFieldDefinitionEvent(...$eventData) + ); + } + + public function removeFieldDefinition( + ContentTypeDraft $contentTypeDraft, + FieldDefinition $fieldDefinition + ): void { + $eventData = [ + $contentTypeDraft, + $fieldDefinition, + ]; + + $beforeEvent = new BeforeRemoveFieldDefinitionEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->removeFieldDefinition($contentTypeDraft, $fieldDefinition); + + $this->eventDispatcher->dispatch( + new RemoveFieldDefinitionEvent(...$eventData) + ); + } + + public function updateFieldDefinition( + ContentTypeDraft $contentTypeDraft, + FieldDefinition $fieldDefinition, + FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct + ): void { + $eventData = [ + $contentTypeDraft, + $fieldDefinition, + $fieldDefinitionUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdateFieldDefinitionEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->updateFieldDefinition($contentTypeDraft, $fieldDefinition, $fieldDefinitionUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdateFieldDefinitionEvent(...$eventData) + ); + } + + public function publishContentTypeDraft(ContentTypeDraft $contentTypeDraft): void + { + $eventData = [$contentTypeDraft]; + + $beforeEvent = new BeforePublishContentTypeDraftEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->publishContentTypeDraft($contentTypeDraft); + + $this->eventDispatcher->dispatch( + new PublishContentTypeDraftEvent(...$eventData) + ); + } + + public function removeContentTypeTranslation( + ContentTypeDraft $contentTypeDraft, + string $languageCode + ): ContentTypeDraft { + $eventData = [ + $contentTypeDraft, + $languageCode, + ]; + + $beforeEvent = new BeforeRemoveContentTypeTranslationEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getNewContentTypeDraft(); + } + + $newContentTypeDraft = $beforeEvent->hasNewContentTypeDraft() + ? $beforeEvent->getNewContentTypeDraft() + : $this->innerService->removeContentTypeTranslation($contentTypeDraft, $languageCode); + + $this->eventDispatcher->dispatch( + new RemoveContentTypeTranslationEvent($newContentTypeDraft, ...$eventData) + ); + + return $newContentTypeDraft; + } +} + +class_alias(ContentTypeService::class, 'eZ\Publish\Core\Event\ContentTypeService'); diff --git a/src/lib/Event/FieldTypeService.php b/src/lib/Event/FieldTypeService.php new file mode 100644 index 0000000000..2c5404827d --- /dev/null +++ b/src/lib/Event/FieldTypeService.php @@ -0,0 +1,30 @@ +eventDispatcher = $eventDispatcher; + } +} + +class_alias(FieldTypeService::class, 'eZ\Publish\Core\Event\FieldTypeService'); diff --git a/src/lib/Event/LanguageService.php b/src/lib/Event/LanguageService.php new file mode 100644 index 0000000000..937452a68d --- /dev/null +++ b/src/lib/Event/LanguageService.php @@ -0,0 +1,153 @@ +eventDispatcher = $eventDispatcher; + } + + public function createLanguage(LanguageCreateStruct $languageCreateStruct): Language + { + $eventData = [$languageCreateStruct]; + + $beforeEvent = new BeforeCreateLanguageEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getLanguage(); + } + + $language = $beforeEvent->hasLanguage() + ? $beforeEvent->getLanguage() + : $this->innerService->createLanguage($languageCreateStruct); + + $this->eventDispatcher->dispatch( + new CreateLanguageEvent($language, ...$eventData) + ); + + return $language; + } + + public function updateLanguageName( + Language $language, + string $newName + ): Language { + $eventData = [ + $language, + $newName, + ]; + + $beforeEvent = new BeforeUpdateLanguageNameEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedLanguage(); + } + + $updatedLanguage = $beforeEvent->hasUpdatedLanguage() + ? $beforeEvent->getUpdatedLanguage() + : $this->innerService->updateLanguageName($language, $newName); + + $this->eventDispatcher->dispatch( + new UpdateLanguageNameEvent($updatedLanguage, ...$eventData) + ); + + return $updatedLanguage; + } + + public function enableLanguage(Language $language): Language + { + $eventData = [$language]; + + $beforeEvent = new BeforeEnableLanguageEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getEnabledLanguage(); + } + + $enabledLanguage = $beforeEvent->hasEnabledLanguage() + ? $beforeEvent->getEnabledLanguage() + : $this->innerService->enableLanguage($language); + + $this->eventDispatcher->dispatch( + new EnableLanguageEvent($enabledLanguage, ...$eventData) + ); + + return $enabledLanguage; + } + + public function disableLanguage(Language $language): Language + { + $eventData = [$language]; + + $beforeEvent = new BeforeDisableLanguageEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getDisabledLanguage(); + } + + $disabledLanguage = $beforeEvent->hasDisabledLanguage() + ? $beforeEvent->getDisabledLanguage() + : $this->innerService->disableLanguage($language); + + $this->eventDispatcher->dispatch( + new DisableLanguageEvent($disabledLanguage, ...$eventData) + ); + + return $disabledLanguage; + } + + public function deleteLanguage(Language $language): void + { + $eventData = [$language]; + + $beforeEvent = new BeforeDeleteLanguageEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteLanguage($language); + + $this->eventDispatcher->dispatch( + new DeleteLanguageEvent(...$eventData) + ); + } +} + +class_alias(LanguageService::class, 'eZ\Publish\Core\Event\LanguageService'); diff --git a/src/lib/Event/LocationService.php b/src/lib/Event/LocationService.php new file mode 100644 index 0000000000..8cd0e0e7d3 --- /dev/null +++ b/src/lib/Event/LocationService.php @@ -0,0 +1,239 @@ +eventDispatcher = $eventDispatcher; + } + + public function copySubtree( + Location $subtree, + Location $targetParentLocation + ): Location { + $eventData = [ + $subtree, + $targetParentLocation, + ]; + + $beforeEvent = new BeforeCopySubtreeEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getLocation(); + } + + $location = $beforeEvent->hasLocation() + ? $beforeEvent->getLocation() + : $this->innerService->copySubtree($subtree, $targetParentLocation); + + $this->eventDispatcher->dispatch( + new CopySubtreeEvent($location, ...$eventData) + ); + + return $location; + } + + public function createLocation( + ContentInfo $contentInfo, + LocationCreateStruct $locationCreateStruct + ): Location { + $eventData = [ + $contentInfo, + $locationCreateStruct, + ]; + + $beforeEvent = new BeforeCreateLocationEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getLocation(); + } + + $location = $beforeEvent->hasLocation() + ? $beforeEvent->getLocation() + : $this->innerService->createLocation($contentInfo, $locationCreateStruct); + + $this->eventDispatcher->dispatch( + new CreateLocationEvent($location, ...$eventData) + ); + + return $location; + } + + public function updateLocation( + Location $location, + LocationUpdateStruct $locationUpdateStruct + ): Location { + $eventData = [ + $location, + $locationUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdateLocationEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedLocation(); + } + + $updatedLocation = $beforeEvent->hasUpdatedLocation() + ? $beforeEvent->getUpdatedLocation() + : $this->innerService->updateLocation($location, $locationUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdateLocationEvent($updatedLocation, ...$eventData) + ); + + return $updatedLocation; + } + + public function swapLocation( + Location $location1, + Location $location2 + ): void { + $eventData = [ + $location1, + $location2, + ]; + + $beforeEvent = new BeforeSwapLocationEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->swapLocation($location1, $location2); + + $this->eventDispatcher->dispatch( + new SwapLocationEvent(...$eventData) + ); + } + + public function hideLocation(Location $location): Location + { + $eventData = [$location]; + + $beforeEvent = new BeforeHideLocationEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getHiddenLocation(); + } + + $hiddenLocation = $beforeEvent->hasHiddenLocation() + ? $beforeEvent->getHiddenLocation() + : $this->innerService->hideLocation($location); + + $this->eventDispatcher->dispatch( + new HideLocationEvent($hiddenLocation, ...$eventData) + ); + + return $hiddenLocation; + } + + public function unhideLocation(Location $location): Location + { + $eventData = [$location]; + + $beforeEvent = new BeforeUnhideLocationEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getRevealedLocation(); + } + + $revealedLocation = $beforeEvent->hasRevealedLocation() + ? $beforeEvent->getRevealedLocation() + : $this->innerService->unhideLocation($location); + + $this->eventDispatcher->dispatch( + new UnhideLocationEvent($revealedLocation, ...$eventData) + ); + + return $revealedLocation; + } + + public function moveSubtree( + Location $location, + Location $newParentLocation + ): void { + $eventData = [ + $location, + $newParentLocation, + ]; + + $beforeEvent = new BeforeMoveSubtreeEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->moveSubtree($location, $newParentLocation); + + $this->eventDispatcher->dispatch( + new MoveSubtreeEvent(...$eventData) + ); + } + + public function deleteLocation(Location $location): void + { + $eventData = [$location]; + + $beforeEvent = new BeforeDeleteLocationEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteLocation($location); + + $this->eventDispatcher->dispatch( + new DeleteLocationEvent(...$eventData) + ); + } +} + +class_alias(LocationService::class, 'eZ\Publish\Core\Event\LocationService'); diff --git a/src/lib/Event/NotificationService.php b/src/lib/Event/NotificationService.php new file mode 100644 index 0000000000..26e8da9fb3 --- /dev/null +++ b/src/lib/Event/NotificationService.php @@ -0,0 +1,96 @@ +eventDispatcher = $eventDispatcher; + } + + public function markNotificationAsRead(Notification $notification): void + { + $eventData = [$notification]; + + $beforeEvent = new BeforeMarkNotificationAsReadEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->markNotificationAsRead($notification); + + $this->eventDispatcher->dispatch( + new MarkNotificationAsReadEvent(...$eventData) + ); + } + + public function createNotification(CreateStruct $createStruct): Notification + { + $eventData = [$createStruct]; + + $beforeEvent = new BeforeCreateNotificationEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getNotification(); + } + + $notification = $beforeEvent->hasNotification() + ? $beforeEvent->getNotification() + : $this->innerService->createNotification($createStruct); + + $this->eventDispatcher->dispatch( + new CreateNotificationEvent($notification, ...$eventData) + ); + + return $notification; + } + + public function deleteNotification(Notification $notification): void + { + $eventData = [$notification]; + + $beforeEvent = new BeforeDeleteNotificationEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteNotification($notification); + + $this->eventDispatcher->dispatch( + new DeleteNotificationEvent(...$eventData) + ); + } +} + +class_alias(NotificationService::class, 'eZ\Publish\Core\Event\NotificationService'); diff --git a/src/lib/Event/ObjectStateService.php b/src/lib/Event/ObjectStateService.php new file mode 100644 index 0000000000..2147002e71 --- /dev/null +++ b/src/lib/Event/ObjectStateService.php @@ -0,0 +1,240 @@ +eventDispatcher = $eventDispatcher; + } + + public function createObjectStateGroup(ObjectStateGroupCreateStruct $objectStateGroupCreateStruct): ObjectStateGroup + { + $eventData = [$objectStateGroupCreateStruct]; + + $beforeEvent = new BeforeCreateObjectStateGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getObjectStateGroup(); + } + + $objectStateGroup = $beforeEvent->hasObjectStateGroup() + ? $beforeEvent->getObjectStateGroup() + : $this->innerService->createObjectStateGroup($objectStateGroupCreateStruct); + + $this->eventDispatcher->dispatch( + new CreateObjectStateGroupEvent($objectStateGroup, ...$eventData) + ); + + return $objectStateGroup; + } + + public function updateObjectStateGroup( + ObjectStateGroup $objectStateGroup, + ObjectStateGroupUpdateStruct $objectStateGroupUpdateStruct + ): ObjectStateGroup { + $eventData = [ + $objectStateGroup, + $objectStateGroupUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdateObjectStateGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedObjectStateGroup(); + } + + $updatedObjectStateGroup = $beforeEvent->hasUpdatedObjectStateGroup() + ? $beforeEvent->getUpdatedObjectStateGroup() + : $this->innerService->updateObjectStateGroup($objectStateGroup, $objectStateGroupUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdateObjectStateGroupEvent($updatedObjectStateGroup, ...$eventData) + ); + + return $updatedObjectStateGroup; + } + + public function deleteObjectStateGroup(ObjectStateGroup $objectStateGroup): void + { + $eventData = [$objectStateGroup]; + + $beforeEvent = new BeforeDeleteObjectStateGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteObjectStateGroup($objectStateGroup); + + $this->eventDispatcher->dispatch( + new DeleteObjectStateGroupEvent(...$eventData) + ); + } + + public function createObjectState( + ObjectStateGroup $objectStateGroup, + ObjectStateCreateStruct $objectStateCreateStruct + ): ObjectState { + $eventData = [ + $objectStateGroup, + $objectStateCreateStruct, + ]; + + $beforeEvent = new BeforeCreateObjectStateEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getObjectState(); + } + + $objectState = $beforeEvent->hasObjectState() + ? $beforeEvent->getObjectState() + : $this->innerService->createObjectState($objectStateGroup, $objectStateCreateStruct); + + $this->eventDispatcher->dispatch( + new CreateObjectStateEvent($objectState, ...$eventData) + ); + + return $objectState; + } + + public function updateObjectState( + ObjectState $objectState, + ObjectStateUpdateStruct $objectStateUpdateStruct + ): ObjectState { + $eventData = [ + $objectState, + $objectStateUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdateObjectStateEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedObjectState(); + } + + $updatedObjectState = $beforeEvent->hasUpdatedObjectState() + ? $beforeEvent->getUpdatedObjectState() + : $this->innerService->updateObjectState($objectState, $objectStateUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdateObjectStateEvent($updatedObjectState, ...$eventData) + ); + + return $updatedObjectState; + } + + public function setPriorityOfObjectState( + ObjectState $objectState, + int $priority + ): void { + $eventData = [ + $objectState, + $priority, + ]; + + $beforeEvent = new BeforeSetPriorityOfObjectStateEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->setPriorityOfObjectState($objectState, $priority); + + $this->eventDispatcher->dispatch( + new SetPriorityOfObjectStateEvent(...$eventData) + ); + } + + public function deleteObjectState(ObjectState $objectState): void + { + $eventData = [$objectState]; + + $beforeEvent = new BeforeDeleteObjectStateEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteObjectState($objectState); + + $this->eventDispatcher->dispatch( + new DeleteObjectStateEvent(...$eventData) + ); + } + + public function setContentState( + ContentInfo $contentInfo, + ObjectStateGroup $objectStateGroup, + ObjectState $objectState + ): void { + $eventData = [ + $contentInfo, + $objectStateGroup, + $objectState, + ]; + + $beforeEvent = new BeforeSetContentStateEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->setContentState($contentInfo, $objectStateGroup, $objectState); + + $this->eventDispatcher->dispatch( + new SetContentStateEvent(...$eventData) + ); + } +} + +class_alias(ObjectStateService::class, 'eZ\Publish\Core\Event\ObjectStateService'); diff --git a/src/lib/Event/Repository.php b/src/lib/Event/Repository.php new file mode 100644 index 0000000000..c8164d2eb7 --- /dev/null +++ b/src/lib/Event/Repository.php @@ -0,0 +1,238 @@ +repository = $repository; + $this->bookmarkService = $bookmarkService; + $this->contentService = $contentService; + $this->contentTypeService = $contentTypeService; + $this->fieldTypeService = $fieldTypeService; + $this->languageService = $languageService; + $this->locationService = $locationService; + $this->notificationService = $notificationService; + $this->objectStateService = $objectStateService; + $this->roleService = $roleService; + $this->searchService = $searchService; + $this->sectionService = $sectionService; + $this->trashService = $trashService; + $this->urlAliasService = $urlAliasService; + $this->urlService = $urlService; + $this->urlWildcardService = $urlWildcardService; + $this->userPreferenceService = $userPreferenceService; + $this->userService = $userService; + } + + public function sudo(callable $callback, ?RepositoryInterface $outerRepository = null) + { + return $this->repository->sudo($callback, $outerRepository); + } + + public function beginTransaction(): void + { + $this->repository->beginTransaction(); + } + + public function commit(): void + { + $this->repository->commit(); + } + + public function rollback(): void + { + $this->repository->rollback(); + } + + public function getPermissionResolver(): PermissionResolverInterface + { + return $this->repository->getPermissionResolver(); + } + + public function getBookmarkService(): BookmarkServiceInterface + { + return $this->bookmarkService; + } + + public function getContentService(): ContentServiceInterface + { + return $this->contentService; + } + + public function getContentTypeService(): ContentTypeServiceInterface + { + return $this->contentTypeService; + } + + public function getFieldTypeService(): FieldTypeServiceInterface + { + return $this->fieldTypeService; + } + + public function getContentLanguageService(): LanguageServiceInterface + { + return $this->languageService; + } + + public function getLocationService(): LocationServiceInterface + { + return $this->locationService; + } + + public function getNotificationService(): NotificationServiceInterface + { + return $this->notificationService; + } + + public function getObjectStateService(): ObjectStateServiceInterface + { + return $this->objectStateService; + } + + public function getRoleService(): RoleServiceInterface + { + return $this->roleService; + } + + public function getSearchService(): SearchServiceInterface + { + return $this->searchService; + } + + public function getSectionService(): SectionServiceInterface + { + return $this->sectionService; + } + + public function getTrashService(): TrashServiceInterface + { + return $this->trashService; + } + + public function getURLAliasService(): URLAliasServiceInterface + { + return $this->urlAliasService; + } + + public function getURLService(): URLServiceInterface + { + return $this->urlService; + } + + public function getURLWildcardService(): URLWildcardServiceInterface + { + return $this->urlWildcardService; + } + + public function getUserPreferenceService(): UserPreferenceServiceInterface + { + return $this->userPreferenceService; + } + + public function getUserService(): UserServiceInterface + { + return $this->userService; + } +} + +class_alias(Repository::class, 'eZ\Publish\Core\Event\Repository'); diff --git a/src/lib/Event/RoleService.php b/src/lib/Event/RoleService.php new file mode 100644 index 0000000000..3849dade53 --- /dev/null +++ b/src/lib/Event/RoleService.php @@ -0,0 +1,371 @@ +eventDispatcher = $eventDispatcher; + } + + public function createRole(RoleCreateStruct $roleCreateStruct): RoleDraft + { + $eventData = [$roleCreateStruct]; + + $beforeEvent = new BeforeCreateRoleEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getRoleDraft(); + } + + $roleDraft = $beforeEvent->hasRoleDraft() + ? $beforeEvent->getRoleDraft() + : $this->innerService->createRole($roleCreateStruct); + + $this->eventDispatcher->dispatch( + new CreateRoleEvent($roleDraft, ...$eventData) + ); + + return $roleDraft; + } + + public function createRoleDraft(Role $role): RoleDraft + { + $eventData = [$role]; + + $beforeEvent = new BeforeCreateRoleDraftEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getRoleDraft(); + } + + $roleDraft = $beforeEvent->hasRoleDraft() + ? $beforeEvent->getRoleDraft() + : $this->innerService->createRoleDraft($role); + + $this->eventDispatcher->dispatch( + new CreateRoleDraftEvent($roleDraft, ...$eventData) + ); + + return $roleDraft; + } + + public function copyRole( + Role $role, + RoleCopyStruct $roleCopyStruct + ): Role { + $eventData = [ + $role, + $roleCopyStruct, + ]; + + $beforeEvent = new BeforeCopyRoleEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getCopiedRole(); + } + + $copiedRole = $beforeEvent->hasCopiedRole() + ? $beforeEvent->getCopiedRole() + : $this->innerService->copyRole($role, $roleCopyStruct); + + $this->eventDispatcher->dispatch( + new CopyRoleEvent($copiedRole, ...$eventData) + ); + + return $copiedRole; + } + + public function updateRoleDraft( + RoleDraft $roleDraft, + RoleUpdateStruct $roleUpdateStruct + ): RoleDraft { + $eventData = [ + $roleDraft, + $roleUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdateRoleDraftEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedRoleDraft(); + } + + $updatedRoleDraft = $beforeEvent->hasUpdatedRoleDraft() + ? $beforeEvent->getUpdatedRoleDraft() + : $this->innerService->updateRoleDraft($roleDraft, $roleUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdateRoleDraftEvent($updatedRoleDraft, ...$eventData) + ); + + return $updatedRoleDraft; + } + + public function addPolicyByRoleDraft( + RoleDraft $roleDraft, + PolicyCreateStruct $policyCreateStruct + ): RoleDraft { + $eventData = [ + $roleDraft, + $policyCreateStruct, + ]; + + $beforeEvent = new BeforeAddPolicyByRoleDraftEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedRoleDraft(); + } + + $updatedRoleDraft = $beforeEvent->hasUpdatedRoleDraft() + ? $beforeEvent->getUpdatedRoleDraft() + : $this->innerService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct); + + $this->eventDispatcher->dispatch( + new AddPolicyByRoleDraftEvent($updatedRoleDraft, ...$eventData) + ); + + return $updatedRoleDraft; + } + + public function removePolicyByRoleDraft( + RoleDraft $roleDraft, + PolicyDraft $policyDraft + ): RoleDraft { + $eventData = [ + $roleDraft, + $policyDraft, + ]; + + $beforeEvent = new BeforeRemovePolicyByRoleDraftEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedRoleDraft(); + } + + $updatedRoleDraft = $beforeEvent->hasUpdatedRoleDraft() + ? $beforeEvent->getUpdatedRoleDraft() + : $this->innerService->removePolicyByRoleDraft($roleDraft, $policyDraft); + + $this->eventDispatcher->dispatch( + new RemovePolicyByRoleDraftEvent($updatedRoleDraft, ...$eventData) + ); + + return $updatedRoleDraft; + } + + public function updatePolicyByRoleDraft( + RoleDraft $roleDraft, + PolicyDraft $policy, + PolicyUpdateStruct $policyUpdateStruct + ): PolicyDraft { + $eventData = [ + $roleDraft, + $policy, + $policyUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdatePolicyByRoleDraftEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedPolicyDraft(); + } + + $updatedPolicyDraft = $beforeEvent->hasUpdatedPolicyDraft() + ? $beforeEvent->getUpdatedPolicyDraft() + : $this->innerService->updatePolicyByRoleDraft($roleDraft, $policy, $policyUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdatePolicyByRoleDraftEvent($updatedPolicyDraft, ...$eventData) + ); + + return $updatedPolicyDraft; + } + + public function deleteRoleDraft(RoleDraft $roleDraft): void + { + $eventData = [$roleDraft]; + + $beforeEvent = new BeforeDeleteRoleDraftEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteRoleDraft($roleDraft); + + $this->eventDispatcher->dispatch( + new DeleteRoleDraftEvent(...$eventData) + ); + } + + public function publishRoleDraft(RoleDraft $roleDraft): void + { + $eventData = [$roleDraft]; + + $beforeEvent = new BeforePublishRoleDraftEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->publishRoleDraft($roleDraft); + + $this->eventDispatcher->dispatch( + new PublishRoleDraftEvent(...$eventData) + ); + } + + public function deleteRole(Role $role): void + { + $eventData = [$role]; + + $beforeEvent = new BeforeDeleteRoleEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteRole($role); + + $this->eventDispatcher->dispatch( + new DeleteRoleEvent(...$eventData) + ); + } + + public function assignRoleToUserGroup( + Role $role, + UserGroup $userGroup, + RoleLimitation $roleLimitation = null + ): void { + $eventData = [ + $role, + $userGroup, + $roleLimitation, + ]; + + $beforeEvent = new BeforeAssignRoleToUserGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->assignRoleToUserGroup($role, $userGroup, $roleLimitation); + + $this->eventDispatcher->dispatch( + new AssignRoleToUserGroupEvent(...$eventData) + ); + } + + public function assignRoleToUser( + Role $role, + User $user, + RoleLimitation $roleLimitation = null + ): void { + $eventData = [ + $role, + $user, + $roleLimitation, + ]; + + $beforeEvent = new BeforeAssignRoleToUserEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->assignRoleToUser($role, $user, $roleLimitation); + + $this->eventDispatcher->dispatch( + new AssignRoleToUserEvent(...$eventData) + ); + } + + public function removeRoleAssignment(RoleAssignment $roleAssignment): void + { + $eventData = [$roleAssignment]; + + $beforeEvent = new BeforeRemoveRoleAssignmentEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->removeRoleAssignment($roleAssignment); + + $this->eventDispatcher->dispatch( + new RemoveRoleAssignmentEvent(...$eventData) + ); + } +} + +class_alias(RoleService::class, 'eZ\Publish\Core\Event\RoleService'); diff --git a/src/lib/Event/SearchService.php b/src/lib/Event/SearchService.php new file mode 100644 index 0000000000..bdfa3047d5 --- /dev/null +++ b/src/lib/Event/SearchService.php @@ -0,0 +1,30 @@ +eventDispatcher = $eventDispatcher; + } +} + +class_alias(SearchService::class, 'eZ\Publish\Core\Event\SearchService'); diff --git a/src/lib/Event/SectionService.php b/src/lib/Event/SectionService.php new file mode 100644 index 0000000000..f2ea08d2ef --- /dev/null +++ b/src/lib/Event/SectionService.php @@ -0,0 +1,158 @@ +eventDispatcher = $eventDispatcher; + } + + public function createSection(SectionCreateStruct $sectionCreateStruct): Section + { + $eventData = [$sectionCreateStruct]; + + $beforeEvent = new BeforeCreateSectionEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getSection(); + } + + $section = $beforeEvent->hasSection() + ? $beforeEvent->getSection() + : $this->innerService->createSection($sectionCreateStruct); + + $this->eventDispatcher->dispatch( + new CreateSectionEvent($section, ...$eventData) + ); + + return $section; + } + + public function updateSection( + Section $section, + SectionUpdateStruct $sectionUpdateStruct + ): Section { + $eventData = [ + $section, + $sectionUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdateSectionEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedSection(); + } + + $updatedSection = $beforeEvent->hasUpdatedSection() + ? $beforeEvent->getUpdatedSection() + : $this->innerService->updateSection($section, $sectionUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdateSectionEvent($updatedSection, ...$eventData) + ); + + return $updatedSection; + } + + public function assignSection( + ContentInfo $contentInfo, + Section $section + ): void { + $eventData = [ + $contentInfo, + $section, + ]; + + $beforeEvent = new BeforeAssignSectionEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->assignSection($contentInfo, $section); + + $this->eventDispatcher->dispatch( + new AssignSectionEvent(...$eventData) + ); + } + + public function assignSectionToSubtree( + Location $location, + Section $section + ): void { + $eventData = [ + $location, + $section, + ]; + + $beforeEvent = new BeforeAssignSectionToSubtreeEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->assignSectionToSubtree($location, $section); + + $this->eventDispatcher->dispatch( + new AssignSectionToSubtreeEvent(...$eventData) + ); + } + + public function deleteSection(Section $section): void + { + $eventData = [$section]; + + $beforeEvent = new BeforeDeleteSectionEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteSection($section); + + $this->eventDispatcher->dispatch( + new DeleteSectionEvent(...$eventData) + ); + } +} + +class_alias(SectionService::class, 'eZ\Publish\Core\Event\SectionService'); diff --git a/src/lib/Event/SettingService.php b/src/lib/Event/SettingService.php new file mode 100644 index 0000000000..5edf08c714 --- /dev/null +++ b/src/lib/Event/SettingService.php @@ -0,0 +1,104 @@ +eventDispatcher = $eventDispatcher; + } + + public function createSetting(SettingCreateStruct $settingCreateStruct): Setting + { + $eventData = [$settingCreateStruct]; + + $beforeEvent = new BeforeCreateSettingEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getSetting(); + } + + $setting = $beforeEvent->hasSetting() + ? $beforeEvent->getSetting() + : $this->innerService->createSetting($settingCreateStruct); + + $this->eventDispatcher->dispatch( + new CreateSettingEvent($setting, ...$eventData) + ); + + return $setting; + } + + public function updateSetting(Setting $setting, SettingUpdateStruct $settingUpdateStruct): Setting + { + $eventData = [ + $setting, + $settingUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdateSettingEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedSetting(); + } + + $updatedSetting = $beforeEvent->hasUpdatedSetting() + ? $beforeEvent->getUpdatedSetting() + : $this->innerService->updateSetting($setting, $settingUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdateSettingEvent($updatedSetting, ...$eventData) + ); + + return $updatedSetting; + } + + public function deleteSetting(Setting $setting): void + { + $eventData = [$setting]; + + $beforeEvent = new BeforeDeleteSettingEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteSetting($setting); + + $this->eventDispatcher->dispatch( + new DeleteSettingEvent(...$eventData) + ); + } +} + +class_alias(SettingService::class, 'eZ\Publish\Core\Event\SettingService'); diff --git a/src/lib/Event/TokenService.php b/src/lib/Event/TokenService.php new file mode 100644 index 0000000000..161b9d9493 --- /dev/null +++ b/src/lib/Event/TokenService.php @@ -0,0 +1,172 @@ +eventDispatcher = $eventDispatcher; + } + + public function getToken( + string $tokenType, + string $token, + ?string $identifier = null + ): Token { + $eventData = [$tokenType, $token, $identifier]; + + $beforeEvent = new BeforeGetTokenEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getResult(); + } + + $result = $beforeEvent->hasResult() + ? $beforeEvent->getToken() + : $this->innerService->getToken(...$eventData); + + $this->eventDispatcher->dispatch( + new GetTokenEvent($result, ...$eventData) + ); + + return $result; + } + + public function checkToken( + string $tokenType, + string $token, + ?string $identifier = null + ): bool { + $eventData = [$tokenType, $token, $identifier]; + + $beforeEvent = new BeforeCheckTokenEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getResult(); + } + + $result = $beforeEvent->hasResult() + ? $beforeEvent->getResult() + : $this->innerService->checkToken(...$eventData); + + $this->eventDispatcher->dispatch( + new CheckTokenEvent($result, ...$eventData) + ); + + return $result; + } + + public function generateToken( + string $type, + int $ttl, + ?string $identifier = null, + int $tokenLength = 64, + ?TokenGeneratorInterface $tokenGenerator = null + ): Token { + $eventData = [$type, $ttl, $identifier, $tokenLength, $tokenGenerator]; + + $beforeEvent = new BeforeGenerateTokenEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getToken(); + } + + $token = $beforeEvent->hasToken() + ? $beforeEvent->getToken() + : $this->innerService->generateToken(...$eventData); + + $this->eventDispatcher->dispatch( + new GenerateTokenEvent($token, ...$eventData) + ); + + return $token; + } + + public function deleteToken(Token $token): void + { + $eventData = [$token]; + + $beforeEvent = new BeforeDeleteTokenEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->deleteToken($token); + + $this->eventDispatcher->dispatch( + new DeleteTokenEvent(...$eventData) + ); + } + + public function revokeToken(Token $token): void + { + $eventData = [$token]; + + $beforeEvent = new BeforeRevokeTokenEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->revokeToken($token); + + $this->eventDispatcher->dispatch( + new RevokeTokenEvent(...$eventData) + ); + } + + public function revokeTokenByIdentifier(string $tokenType, ?string $identifier): void + { + $eventData = [$tokenType, $identifier]; + + $beforeEvent = new BeforeRevokeTokenByIdentifierEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->revokeTokenByIdentifier($tokenType, $identifier); + + $this->eventDispatcher->dispatch( + new RevokeTokenByIdentifierEvent(...$eventData) + ); + } +} diff --git a/src/lib/Event/TranslationService.php b/src/lib/Event/TranslationService.php new file mode 100644 index 0000000000..2eb5549293 --- /dev/null +++ b/src/lib/Event/TranslationService.php @@ -0,0 +1,30 @@ +eventDispatcher = $eventDispatcher; + } +} + +class_alias(TranslationService::class, 'eZ\Publish\Core\Event\TranslationService'); diff --git a/src/lib/Event/TrashService.php b/src/lib/Event/TrashService.php new file mode 100644 index 0000000000..3deae86516 --- /dev/null +++ b/src/lib/Event/TrashService.php @@ -0,0 +1,133 @@ +eventDispatcher = $eventDispatcher; + } + + public function trash(Location $location): ?TrashItem + { + $eventData = [$location]; + + $beforeEvent = new BeforeTrashEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getResult(); + } + + $trashItem = $beforeEvent->isResultSet() + ? $beforeEvent->getResult() + : $this->innerService->trash($location); + + $this->eventDispatcher->dispatch( + new TrashEvent($trashItem, ...$eventData) + ); + + return $trashItem; + } + + public function recover( + TrashItem $trashItem, + ?Location $newParentLocation = null + ): Location { + $eventData = [ + $trashItem, + $newParentLocation, + ]; + + $beforeEvent = new BeforeRecoverEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getLocation(); + } + + $location = $beforeEvent->hasLocation() + ? $beforeEvent->getLocation() + : $this->innerService->recover($trashItem, $newParentLocation); + + $this->eventDispatcher->dispatch( + new RecoverEvent($location, ...$eventData) + ); + + return $location; + } + + public function emptyTrash(): TrashItemDeleteResultList + { + $beforeEvent = new BeforeEmptyTrashEvent(); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getResultList(); + } + + $resultList = $beforeEvent->hasResultList() + ? $beforeEvent->getResultList() + : $this->innerService->emptyTrash(); + + $this->eventDispatcher->dispatch( + new EmptyTrashEvent($resultList) + ); + + return $resultList; + } + + public function deleteTrashItem(TrashItem $trashItem): TrashItemDeleteResult + { + $eventData = [$trashItem]; + + $beforeEvent = new BeforeDeleteTrashItemEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getResult(); + } + + $result = $beforeEvent->hasResult() + ? $beforeEvent->getResult() + : $this->innerService->deleteTrashItem($trashItem); + + $this->eventDispatcher->dispatch( + new DeleteTrashItemEvent($result, ...$eventData) + ); + + return $result; + } +} + +class_alias(TrashService::class, 'eZ\Publish\Core\Event\TrashService'); diff --git a/src/lib/Event/URLAliasService.php b/src/lib/Event/URLAliasService.php new file mode 100644 index 0000000000..d5e72a466e --- /dev/null +++ b/src/lib/Event/URLAliasService.php @@ -0,0 +1,142 @@ +eventDispatcher = $eventDispatcher; + } + + public function createUrlAlias( + Location $location, + string $path, + string $languageCode, + bool $forwarding = false, + bool $alwaysAvailable = false + ): URLAlias { + $eventData = [ + $location, + $path, + $languageCode, + $forwarding, + $alwaysAvailable, + ]; + + $beforeEvent = new BeforeCreateUrlAliasEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUrlAlias(); + } + + $urlAlias = $beforeEvent->hasUrlAlias() + ? $beforeEvent->getUrlAlias() + : $this->innerService->createUrlAlias($location, $path, $languageCode, $forwarding, $alwaysAvailable); + + $this->eventDispatcher->dispatch( + new CreateUrlAliasEvent($urlAlias, ...$eventData) + ); + + return $urlAlias; + } + + public function createGlobalUrlAlias( + string $resource, + string $path, + string $languageCode, + bool $forwarding = false, + bool $alwaysAvailable = false + ): URLAlias { + $eventData = [ + $resource, + $path, + $languageCode, + $forwarding, + $alwaysAvailable, + ]; + + $beforeEvent = new BeforeCreateGlobalUrlAliasEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUrlAlias(); + } + + $urlAlias = $beforeEvent->hasUrlAlias() + ? $beforeEvent->getUrlAlias() + : $this->innerService->createGlobalUrlAlias($resource, $path, $languageCode, $forwarding, $alwaysAvailable); + + $this->eventDispatcher->dispatch( + new CreateGlobalUrlAliasEvent($urlAlias, ...$eventData) + ); + + return $urlAlias; + } + + public function removeAliases(array $aliasList): void + { + $eventData = [$aliasList]; + + $beforeEvent = new BeforeRemoveAliasesEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->removeAliases($aliasList); + + $this->eventDispatcher->dispatch( + new RemoveAliasesEvent(...$eventData) + ); + } + + public function refreshSystemUrlAliasesForLocation(Location $location): void + { + $eventData = [$location]; + + $beforeEvent = new BeforeRefreshSystemUrlAliasesForLocationEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->refreshSystemUrlAliasesForLocation($location); + + $this->eventDispatcher->dispatch( + new RefreshSystemUrlAliasesForLocationEvent(...$eventData) + ); + } +} + +class_alias(URLAliasService::class, 'eZ\Publish\Core\Event\URLAliasService'); diff --git a/src/lib/Event/URLService.php b/src/lib/Event/URLService.php new file mode 100644 index 0000000000..9558fdd67f --- /dev/null +++ b/src/lib/Event/URLService.php @@ -0,0 +1,61 @@ +eventDispatcher = $eventDispatcher; + } + + public function updateUrl( + URL $url, + URLUpdateStruct $struct + ): URL { + $eventData = [ + $url, + $struct, + ]; + + $beforeEvent = new BeforeUpdateUrlEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedUrl(); + } + + $updatedUrl = $beforeEvent->hasUpdatedUrl() + ? $beforeEvent->getUpdatedUrl() + : $this->innerService->updateUrl($url, $struct); + + $this->eventDispatcher->dispatch( + new UpdateUrlEvent($updatedUrl, ...$eventData) + ); + + return $updatedUrl; + } +} + +class_alias(URLService::class, 'eZ\Publish\Core\Event\URLService'); diff --git a/src/lib/Event/URLWildcardService.php b/src/lib/Event/URLWildcardService.php new file mode 100644 index 0000000000..7bfc0d698d --- /dev/null +++ b/src/lib/Event/URLWildcardService.php @@ -0,0 +1,133 @@ +eventDispatcher = $eventDispatcher; + } + + public function create( + string $sourceUrl, + string $destinationUrl, + bool $forward = false + ): UrlWildcard { + $eventData = [ + $sourceUrl, + $destinationUrl, + $forward, + ]; + + $beforeEvent = new BeforeCreateEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUrlWildcard(); + } + + $urlWildcard = $beforeEvent->hasUrlWildcard() + ? $beforeEvent->getUrlWildcard() + : $this->innerService->create($sourceUrl, $destinationUrl, $forward); + + $this->eventDispatcher->dispatch( + new CreateEvent($urlWildcard, ...$eventData) + ); + + return $urlWildcard; + } + + public function update( + URLWildcard $urlWildcard, + URLWildcardUpdateStruct $updateStruct + ): void { + $eventData = [ + $urlWildcard, + $updateStruct, + ]; + + $beforeEvent = new BeforeUpdateEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->update($urlWildcard, $updateStruct); + + $this->eventDispatcher->dispatch( + new UpdateEvent(...$eventData) + ); + } + + public function remove(URLWildcard $urlWildcard): void + { + $eventData = [$urlWildcard]; + + $beforeEvent = new BeforeRemoveEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->remove($urlWildcard); + + $this->eventDispatcher->dispatch( + new RemoveEvent(...$eventData) + ); + } + + public function translate(string $url): URLWildcardTranslationResult + { + $eventData = [$url]; + + $beforeEvent = new BeforeTranslateEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getResult(); + } + + $result = $beforeEvent->hasResult() + ? $beforeEvent->getResult() + : $this->innerService->translate($url); + + $this->eventDispatcher->dispatch( + new TranslateEvent($result, ...$eventData) + ); + + return $result; + } +} + +class_alias(URLWildcardService::class, 'eZ\Publish\Core\Event\URLWildcardService'); diff --git a/src/lib/Event/UserPreferenceService.php b/src/lib/Event/UserPreferenceService.php new file mode 100644 index 0000000000..e03dea9af4 --- /dev/null +++ b/src/lib/Event/UserPreferenceService.php @@ -0,0 +1,50 @@ +eventDispatcher = $eventDispatcher; + } + + public function setUserPreference(array $userPreferenceSetStructs): void + { + $eventData = [$userPreferenceSetStructs]; + + $beforeEvent = new BeforeSetUserPreferenceEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->setUserPreference($userPreferenceSetStructs); + + $this->eventDispatcher->dispatch( + new SetUserPreferenceEvent(...$eventData) + ); + } +} + +class_alias(UserPreferenceService::class, 'eZ\Publish\Core\Event\UserPreferenceService'); diff --git a/src/lib/Event/UserService.php b/src/lib/Event/UserService.php new file mode 100644 index 0000000000..c7d6b81f93 --- /dev/null +++ b/src/lib/Event/UserService.php @@ -0,0 +1,359 @@ +eventDispatcher = $eventDispatcher; + } + + public function createUserGroup( + UserGroupCreateStruct $userGroupCreateStruct, + UserGroup $parentGroup + ): UserGroup { + $eventData = [ + $userGroupCreateStruct, + $parentGroup, + ]; + + $beforeEvent = new BeforeCreateUserGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUserGroup(); + } + + $userGroup = $beforeEvent->hasUserGroup() + ? $beforeEvent->getUserGroup() + : $this->innerService->createUserGroup($userGroupCreateStruct, $parentGroup); + + $this->eventDispatcher->dispatch( + new CreateUserGroupEvent($userGroup, ...$eventData) + ); + + return $userGroup; + } + + public function deleteUserGroup(UserGroup $userGroup): iterable + { + $eventData = [$userGroup]; + + $beforeEvent = new BeforeDeleteUserGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getLocations(); + } + + $locations = $beforeEvent->hasLocations() + ? $beforeEvent->getLocations() + : $this->innerService->deleteUserGroup($userGroup); + + $this->eventDispatcher->dispatch( + new DeleteUserGroupEvent($locations, ...$eventData) + ); + + return $locations; + } + + public function moveUserGroup( + UserGroup $userGroup, + UserGroup $newParent + ): void { + $eventData = [ + $userGroup, + $newParent, + ]; + + $beforeEvent = new BeforeMoveUserGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->moveUserGroup($userGroup, $newParent); + + $this->eventDispatcher->dispatch( + new MoveUserGroupEvent(...$eventData) + ); + } + + public function updateUserGroup( + UserGroup $userGroup, + UserGroupUpdateStruct $userGroupUpdateStruct + ): UserGroup { + $eventData = [ + $userGroup, + $userGroupUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdateUserGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedUserGroup(); + } + + $updatedUserGroup = $beforeEvent->hasUpdatedUserGroup() + ? $beforeEvent->getUpdatedUserGroup() + : $this->innerService->updateUserGroup($userGroup, $userGroupUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdateUserGroupEvent($updatedUserGroup, ...$eventData) + ); + + return $updatedUserGroup; + } + + public function createUser( + UserCreateStruct $userCreateStruct, + array $parentGroups + ): User { + $eventData = [ + $userCreateStruct, + $parentGroups, + ]; + + $beforeEvent = new BeforeCreateUserEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUser(); + } + + $user = $beforeEvent->hasUser() + ? $beforeEvent->getUser() + : $this->innerService->createUser($userCreateStruct, $parentGroups); + + $this->eventDispatcher->dispatch( + new CreateUserEvent($user, ...$eventData) + ); + + return $user; + } + + public function deleteUser(User $user): iterable + { + $eventData = [$user]; + + $beforeEvent = new BeforeDeleteUserEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getLocations(); + } + + $locations = $beforeEvent->hasLocations() + ? $beforeEvent->getLocations() + : $this->innerService->deleteUser($user); + + $this->eventDispatcher->dispatch( + new DeleteUserEvent($locations, ...$eventData) + ); + + return $locations; + } + + public function updateUser( + User $user, + UserUpdateStruct $userUpdateStruct + ): User { + $eventData = [ + $user, + $userUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdateUserEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedUser(); + } + + $updatedUser = $beforeEvent->hasUpdatedUser() + ? $beforeEvent->getUpdatedUser() + : $this->innerService->updateUser($user, $userUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdateUserEvent($updatedUser, ...$eventData) + ); + + return $updatedUser; + } + + public function updateUserPassword( + User $user, + string $newPassword + ): User { + $eventData = [ + $user, + new UserUpdateStruct([ + 'password' => $newPassword, + ]), + ]; + + /** + * @deprecated since eZ Platform by Ibexa v3.1. listening on BeforeUpdateUserEvent when updating password has been deprecated. Use BeforeUpdateUserPasswordEvent instead. + */ + $beforeEvent = new BeforeUpdateUserEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedUser(); + } + + $beforePasswordEvent = new BeforeUpdateUserPasswordEvent($user, $newPassword); + + $this->eventDispatcher->dispatch($beforePasswordEvent); + if ($beforePasswordEvent->isPropagationStopped()) { + return $beforePasswordEvent->getUpdatedUser(); + } + + if ($beforeEvent->hasUpdatedUser()) { + $updatedUser = $beforeEvent->getUpdatedUser(); + } elseif ($beforePasswordEvent->hasUpdatedUser()) { + $updatedUser = $beforePasswordEvent->getUpdatedUser(); + } else { + $updatedUser = $this->innerService->updateUserPassword($user, $newPassword); + } + + /** + * @deprecated since eZ Platform by Ibexa v3.1. Listening on UpdateUserEvent when updating password has been deprecated. Use UpdateUserPasswordEvent instead. + */ + $afterEvent = new UpdateUserEvent($updatedUser, ...$eventData); + $this->eventDispatcher->dispatch( + $afterEvent + ); + + $afterPasswordEvent = new UpdateUserPasswordEvent($updatedUser, $user, $newPassword); + $this->eventDispatcher->dispatch( + $afterPasswordEvent + ); + + return $updatedUser; + } + + public function updateUserToken( + User $user, + UserTokenUpdateStruct $userTokenUpdateStruct + ): User { + $eventData = [ + $user, + $userTokenUpdateStruct, + ]; + + $beforeEvent = new BeforeUpdateUserTokenEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return $beforeEvent->getUpdatedUser(); + } + + $updatedUser = $beforeEvent->hasUpdatedUser() + ? $beforeEvent->getUpdatedUser() + : $this->innerService->updateUserToken($user, $userTokenUpdateStruct); + + $this->eventDispatcher->dispatch( + new UpdateUserTokenEvent($updatedUser, ...$eventData) + ); + + return $updatedUser; + } + + public function assignUserToUserGroup( + User $user, + UserGroup $userGroup + ): void { + $eventData = [ + $user, + $userGroup, + ]; + + $beforeEvent = new BeforeAssignUserToUserGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->assignUserToUserGroup($user, $userGroup); + + $this->eventDispatcher->dispatch( + new AssignUserToUserGroupEvent(...$eventData) + ); + } + + public function unAssignUserFromUserGroup( + User $user, + UserGroup $userGroup + ): void { + $eventData = [ + $user, + $userGroup, + ]; + + $beforeEvent = new BeforeUnAssignUserFromUserGroupEvent(...$eventData); + + $this->eventDispatcher->dispatch($beforeEvent); + if ($beforeEvent->isPropagationStopped()) { + return; + } + + $this->innerService->unAssignUserFromUserGroup($user, $userGroup); + + $this->eventDispatcher->dispatch( + new UnAssignUserFromUserGroupEvent(...$eventData) + ); + } +} + +class_alias(UserService::class, 'eZ\Publish\Core\Event\UserService'); diff --git a/eZ/Publish/Core/FieldType/Author/Author.php b/src/lib/FieldType/Author/Author.php similarity index 79% rename from eZ/Publish/Core/FieldType/Author/Author.php rename to src/lib/FieldType/Author/Author.php index 15c118fdfe..51a1bee824 100644 --- a/eZ/Publish/Core/FieldType/Author/Author.php +++ b/src/lib/FieldType/Author/Author.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Author; +namespace Ibexa\Core\FieldType\Author; -use eZ\Publish\SPI\Persistence\ValueObject; +use Ibexa\Contracts\Core\Persistence\ValueObject; /** * Value object for an author. @@ -35,3 +35,5 @@ class Author extends ValueObject */ public $email; } + +class_alias(Author::class, 'eZ\Publish\Core\FieldType\Author\Author'); diff --git a/src/lib/FieldType/Author/AuthorCollection.php b/src/lib/FieldType/Author/AuthorCollection.php new file mode 100644 index 0000000000..fa78b1a2d5 --- /dev/null +++ b/src/lib/FieldType/Author/AuthorCollection.php @@ -0,0 +1,80 @@ + + */ +class AuthorCollection extends ArrayObject +{ + /** + * @param \Ibexa\Core\FieldType\Author\Author[] $elements + */ + public function __construct(array $elements = []) + { + // Call parent constructor without $elements because all author elements + // must be given an id by $this->offsetSet() + parent::__construct(); + foreach ($elements as $i => $author) { + $this->offsetSet($i, $author); + } + } + + /** + * Adds a new author to the collection. + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType When $value is not of type Author + * + * @param int $offset + * @param \Ibexa\Core\FieldType\Author\Author $value + */ + public function offsetSet($offset, $value): void + { + if (!$value instanceof Author) { + throw new InvalidArgumentType( + '$value', + Author::class, + $value + ); + } + + $aAuthors = $this->getArrayCopy(); + parent::offsetSet($offset, $value); + if (!isset($value->id) || $value->id == -1) { + if (!empty($aAuthors)) { + $value->id = end($aAuthors)->id + 1; + } else { + $value->id = 1; + } + } + } + + /** + * Removes authors from current collection with a list of Ids. + * + * @param array $authorIds Author's Ids to remove from current collection + */ + public function removeAuthorsById(array $authorIds) + { + $aAuthors = $this->getArrayCopy(); + foreach ($aAuthors as $i => $author) { + if (in_array($author->id, $authorIds)) { + unset($aAuthors[$i]); + } + } + + $this->exchangeArray($aAuthors); + } +} + +class_alias(AuthorCollection::class, 'eZ\Publish\Core\FieldType\Author\AuthorCollection'); diff --git a/src/lib/FieldType/Author/SearchField.php b/src/lib/FieldType/Author/SearchField.php new file mode 100644 index 0000000000..7f63151ac8 --- /dev/null +++ b/src/lib/FieldType/Author/SearchField.php @@ -0,0 +1,118 @@ +value->data as $author) { + $name[] = $author['name']; + $id[] = $author['id']; + $email[] = $author['email']; + + $aggregationValues[] = json_encode([ + 'name' => $author['name'], + 'email' => $author['email'], + ]); + } + + return [ + new Search\Field( + 'name', + $name, + new Search\FieldType\MultipleStringField() + ), + new Search\Field( + 'id', + $id, + new Search\FieldType\MultipleIntegerField() + ), + new Search\Field( + 'email', + $email, + new Search\FieldType\MultipleStringField() + ), + new Search\Field( + 'count', + count($field->value->data), + new Search\FieldType\IntegerField() + ), + new Search\Field( + 'aggregation_value', + $aggregationValues, + new MultipleIdentifierField(['raw' => true]), + ), + new Search\Field( + 'sort_value', + implode('-', $name), + new Search\FieldType\StringField() + ), + new Search\Field( + 'fulltext', + $name, + new Search\FieldType\FullTextField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'name' => new Search\FieldType\MultipleStringField(), + 'id' => new Search\FieldType\MultipleIntegerField(), + 'email' => new Search\FieldType\MultipleStringField(), + 'count' => new Search\FieldType\IntegerField(), + 'aggregation_value' => new MultipleIdentifierField(['raw' => true]), + 'sort_value' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'name'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return 'sort_value'; + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\Author\SearchField'); diff --git a/src/lib/FieldType/Author/Type.php b/src/lib/FieldType/Author/Type.php new file mode 100644 index 0000000000..61db3acff0 --- /dev/null +++ b/src/lib/FieldType/Author/Type.php @@ -0,0 +1,261 @@ + [ + 'type' => 'choice', + 'default' => self::DEFAULT_VALUE_EMPTY, + ], + ]; + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezauthor'; + } + + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + return $value->authors[0]->name ?? ''; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Author\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param array|\Ibexa\Core\FieldType\Author\Value $inputValue + * + * @return \Ibexa\Core\FieldType\Author\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_array($inputValue)) { + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Author\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!$value->authors instanceof AuthorCollection) { + throw new InvalidArgumentType( + '$value->authors', + AuthorCollection::class, + $value->authors + ); + } + } + + /** + * {@inheritdoc} + */ + protected function getSortInfo(BaseValue $value) + { + if (empty($value->authors)) { + return false; + } + + $authors = []; + foreach ($value->authors as $author) { + $authors[] = $this->transformationProcessor->transformByGroup($author->name, 'lowercase'); + } + + sort($authors); + + return implode(',', $authors); + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\Author\Value $value + */ + public function fromHash($hash) + { + return new Value( + array_map( + static function ($author) { + return new Author($author); + }, + $hash + ) + ); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Author\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + return array_map( + static function ($author) { + return (array)$author; + }, + $value->authors->getArrayCopy() + ); + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + /** + * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param array $fieldSettings + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateFieldSettings($fieldSettings) + { + $validationErrors = []; + + foreach ($fieldSettings as $name => $value) { + $settingNameError = $this->validateSettingName($name); + + if ($settingNameError instanceof ValidationError) { + $validationErrors[] = $settingNameError; + } + + switch ($name) { + case 'defaultAuthor': + $settingValueError = $this->validateDefaultAuthorSetting($name, $value); + if ($settingValueError instanceof ValidationError) { + $validationErrors[] = $settingValueError; + } + break; + } + } + + return $validationErrors; + } + + /** + * Validates the fieldSetting name. + * + * @param string $name + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError|null + */ + private function validateSettingName($name) + { + if (!isset($this->settingsSchema[$name])) { + return new ValidationError( + "Setting '%setting%' is unknown", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + + return null; + } + + /** + * Validates if the defaultAuthor setting has one of the defined values. + * + * @param string $name + * @param string $value + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError|null + */ + private function validateDefaultAuthorSetting($name, $value) + { + $definedValues = [ + self::DEFAULT_VALUE_EMPTY, + self::DEFAULT_CURRENT_USER, + ]; + + if (!in_array($value, $definedValues, true)) { + return new ValidationError( + "Setting '%setting%' has unknown default value", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + + return null; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezauthor.name', 'ibexa_fieldtypes')->setDesc('Authors'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Author\Type'); diff --git a/src/lib/FieldType/Author/Value.php b/src/lib/FieldType/Author/Value.php new file mode 100644 index 0000000000..1e61cdc12e --- /dev/null +++ b/src/lib/FieldType/Author/Value.php @@ -0,0 +1,51 @@ +authors = new AuthorCollection($authors); + } + + public function __toString() + { + if (empty($this->authors)) { + return ''; + } + + $authorNames = []; + + if ($this->authors instanceof AuthorCollection) { + foreach ($this->authors as $author) { + $authorNames[] = $author->name; + } + } + + return implode(', ', $authorNames); + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\Author\Value'); diff --git a/eZ/Publish/Core/FieldType/BinaryBase/BinaryBaseStorage.php b/src/lib/FieldType/BinaryBase/BinaryBaseStorage.php similarity index 83% rename from eZ/Publish/Core/FieldType/BinaryBase/BinaryBaseStorage.php rename to src/lib/FieldType/BinaryBase/BinaryBaseStorage.php index 928cb70988..7817c92801 100644 --- a/eZ/Publish/Core/FieldType/BinaryBase/BinaryBaseStorage.php +++ b/src/lib/FieldType/BinaryBase/BinaryBaseStorage.php @@ -4,18 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\BinaryBase; - -use eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException; -use eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\SPI\FieldType\BinaryBase\PathGenerator; -use eZ\Publish\SPI\FieldType\BinaryBase\RouteAwarePathGenerator; -use eZ\Publish\SPI\FieldType\GatewayBasedStorage; -use eZ\Publish\SPI\FieldType\StorageGateway; -use eZ\Publish\SPI\IO\MimeTypeDetector; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; +namespace Ibexa\Core\FieldType\BinaryBase; + +use Ibexa\Contracts\Core\FieldType\BinaryBase\PathGenerator; +use Ibexa\Contracts\Core\FieldType\BinaryBase\RouteAwarePathGenerator; +use Ibexa\Contracts\Core\FieldType\GatewayBasedStorage; +use Ibexa\Contracts\Core\FieldType\StorageGatewayInterface; +use Ibexa\Contracts\Core\IO\MimeTypeDetector; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Core\Base\Exceptions\ContentFieldValidationException; +use Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator; +use Ibexa\Core\IO\IOServiceInterface; /** * Storage for binary files. @@ -25,27 +25,27 @@ class BinaryBaseStorage extends GatewayBasedStorage /** * An instance of IOService configured to store to the images folder. * - * @var \eZ\Publish\Core\IO\IOServiceInterface + * @var \Ibexa\Core\IO\IOServiceInterface */ protected $ioService; - /** @var \eZ\Publish\SPI\FieldType\BinaryBase\PathGenerator */ + /** @var \Ibexa\Contracts\Core\FieldType\BinaryBase\PathGenerator */ protected $pathGenerator; - /** @var \eZ\Publish\SPI\IO\MimeTypeDetector */ + /** @var \Ibexa\Contracts\Core\IO\MimeTypeDetector */ protected $mimeTypeDetector; - /** @var \eZ\Publish\SPI\FieldType\BinaryBase\PathGenerator */ + /** @var \Ibexa\Contracts\Core\FieldType\BinaryBase\PathGenerator */ protected $downloadUrlGenerator; - /** @var \eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage\Gateway */ + /** @var \Ibexa\Core\FieldType\BinaryBase\BinaryBaseStorage\Gateway */ protected $gateway; - /** @var \eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator */ + /** @var \Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator */ protected $fileExtensionBlackListValidator; public function __construct( - StorageGateway $gateway, + StorageGatewayInterface $gateway, IOServiceInterface $ioService, PathGenerator $pathGenerator, MimeTypeDetector $mimeTypeDetector, @@ -59,7 +59,7 @@ public function __construct( } /** - * @param \eZ\Publish\SPI\FieldType\BinaryBase\PathGenerator $downloadUrlGenerator + * @param \Ibexa\Contracts\Core\FieldType\BinaryBase\PathGenerator $downloadUrlGenerator */ public function setDownloadUrlGenerator(PathGenerator $downloadUrlGenerator) { @@ -67,8 +67,8 @@ public function setDownloadUrlGenerator(PathGenerator $downloadUrlGenerator) } /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException */ public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context) { @@ -208,3 +208,5 @@ public function getIndexData(VersionInfo $versionInfo, Field $field, array $cont { } } + +class_alias(BinaryBaseStorage::class, 'eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage'); diff --git a/src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php b/src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php new file mode 100644 index 0000000000..ade24ff438 --- /dev/null +++ b/src/lib/FieldType/BinaryBase/BinaryBaseStorage/Gateway.php @@ -0,0 +1,69 @@ +connection = $connection; + } + + /** + * Return the table name to store data in. + * + * @return string + */ + abstract protected function getStorageTable(); + + /** + * Return a column to property mapping for the storage table. + * + * @return array + */ + protected function getPropertyMapping() + { + return [ + 'filename' => [ + 'name' => 'id', + 'cast' => 'strval', + ], + 'mime_type' => [ + 'name' => 'mimeType', + 'cast' => 'strval', + ], + 'original_filename' => [ + 'name' => 'fileName', + 'cast' => 'strval', + ], + ]; + } + + /** + * Set columns to be fetched from the database. + * + * This method is intended to be overwritten by derived classes in order to + * add additional columns to be fetched from the database. Please do not + * forget to call the parent when overwriting this method. + * + * @param \Doctrine\DBAL\Query\QueryBuilder $queryBuilder + * @param int $fieldId + * @param int $versionNo + */ + protected function setFetchColumns(QueryBuilder $queryBuilder, $fieldId, $versionNo) + { + $queryBuilder->select( + $this->connection->quoteIdentifier('filename'), + $this->connection->quoteIdentifier('mime_type'), + $this->connection->quoteIdentifier('original_filename') + ); + } + + /** + * Set the required insert columns to insert query builder. + * + * This method is intended to be overwritten by derived classes in order to + * add additional columns to be set in the database. Please do not forget + * to call the parent when overwriting this method. + * + * @param \Doctrine\DBAL\Query\QueryBuilder $queryBuilder + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + */ + protected function setInsertColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field) + { + $queryBuilder + ->setValue('contentobject_attribute_id', ':fieldId') + ->setValue('filename', ':filename') + ->setValue('mime_type', ':mimeType') + ->setValue('original_filename', ':originalFilename') + ->setValue('version', ':versionNo') + ->setParameter(':fieldId', $field->id, PDO::PARAM_INT) + ->setParameter(':filename', $this->removeMimeFromPath($field->value->externalData['id'])) + ->setParameter(':mimeType', $field->value->externalData['mimeType']) + ->setParameter(':originalFilename', $field->value->externalData['fileName']) + ->setParameter(':versionNo', $versionInfo->versionNo, PDO::PARAM_INT) + ; + } + + /** + * @param \Doctrine\DBAL\Query\QueryBuilder $queryBuilder + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + */ + protected function setUpdateColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field) + { + $queryBuilder + ->set('contentobject_attribute_id', ':fieldId') + ->set('filename', ':filename') + ->set('mime_type', ':mimeType') + ->set('original_filename', ':originalFilename') + ->set('version', ':versionNo') + ->setParameter(':fieldId', $field->id, ParameterType::INTEGER) + ->setParameter(':filename', $this->removeMimeFromPath($field->value->externalData['id'])) + ->setParameter(':mimeType', $field->value->externalData['mimeType']) + ->setParameter(':originalFilename', $field->value->externalData['fileName']) + ->setParameter(':versionNo', $versionInfo->versionNo, ParameterType::INTEGER) + ; + } + + /** + * Store the file reference in $field for $versionNo. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * + * @return bool + */ + public function storeFileReference(VersionInfo $versionInfo, Field $field) + { + $referencedData = $this->getFileReferenceData($field->id, $versionInfo->versionNo); + + if ($referencedData === null) { + $this->storeNewFieldData($versionInfo, $field); + } elseif (is_array($referencedData) && !empty(array_diff_assoc($referencedData, $field->value->externalData))) { + $this->updateFieldData($versionInfo, $field); + } + + return false; + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + */ + protected function updateFieldData(VersionInfo $versionInfo, Field $field) + { + $updateQuery = $this->connection->createQueryBuilder(); + $updateQuery->update( + $this->connection->quoteIdentifier($this->getStorageTable()) + ); + + $this->setUpdateColumns($updateQuery, $versionInfo, $field); + $updateQuery + ->where( + $updateQuery->expr()->andX( + $updateQuery->expr()->eq( + $this->connection->quoteIdentifier('contentobject_attribute_id'), + ':fieldId' + ), + $updateQuery->expr()->eq( + $this->connection->quoteIdentifier('version'), + ':versionNo' + ) + ) + ) + ->setParameter(':fieldId', $field->id, ParameterType::INTEGER) + ->setParameter(':versionNo', $versionInfo->versionNo, ParameterType::INTEGER) + ; + + $updateQuery->execute(); + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + */ + protected function storeNewFieldData(VersionInfo $versionInfo, Field $field) + { + $insertQuery = $this->connection->createQueryBuilder(); + $insertQuery->insert( + $this->connection->quoteIdentifier($this->getStorageTable()) + ); + + $this->setInsertColumns($insertQuery, $versionInfo, $field); + + $insertQuery->execute(); + } + + /** + * Remove the prepended mime-type directory from $path for legacy storage. + * + * @param string $path + * + * @return string + */ + public function removeMimeFromPath($path) + { + $path = (string)$path; + + return substr($path, strpos($path, '/') + 1); + } + + /** + * Return the file reference data for the given $fieldId in $versionNo. + * + * @param int $fieldId + * @param int $versionNo + * + * @return array|null + */ + public function getFileReferenceData($fieldId, $versionNo) + { + $selectQuery = $this->connection->createQueryBuilder(); + + $this->setFetchColumns($selectQuery, $fieldId, $versionNo); + + $selectQuery + ->from($this->connection->quoteIdentifier($this->getStorageTable())) + ->where( + $selectQuery->expr()->andX( + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('contentobject_attribute_id'), + ':fieldId' + ), + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('version'), + ':versionNo' + ) + ) + ) + ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) + ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) + ; + + $statement = $selectQuery->execute(); + + $result = $statement->fetchAll(PDO::FETCH_ASSOC); + + if (count($result) < 1) { + return null; + } + + $convertedResult = []; + foreach (reset($result) as $column => $value) { + $convertedResult[$this->toPropertyName($column)] = $this->castToPropertyValue($value, $column); + } + $convertedResult['id'] = $this->prependMimeToPath( + $convertedResult['id'], + $convertedResult['mimeType'] + ); + + return $convertedResult; + } + + /** + * Return the property name for the given $columnName. + * + * @param string $columnName + * + * @return string + */ + protected function toPropertyName($columnName) + { + $propertyMap = $this->getPropertyMapping(); + + return $propertyMap[$columnName]['name']; + } + + /** + * Return $value casted as specified by {@link getPropertyMapping()}. + * + * @param mixed $value + * @param string $columnName + * + * @return mixed + */ + protected function castToPropertyValue($value, $columnName) + { + $propertyMap = $this->getPropertyMapping(); + $castFunction = $propertyMap[$columnName]['cast']; + + return $castFunction($value); + } + + /** + * Prepend $path with the first part of the given $mimeType. + * + * @param string $path + * @param string $mimeType + * + * @return string + */ + public function prependMimeToPath($path, $mimeType) + { + $res = substr($mimeType, 0, strpos($mimeType, '/')) . '/' . $path; + + return $res; + } + + /** + * Remove all file references for the given $fieldIds. + * + * @param array $fieldIds + * @param int $versionNo + */ + public function removeFileReferences(array $fieldIds, $versionNo) + { + if (empty($fieldIds)) { + return; + } + + $deleteQuery = $this->connection->createQueryBuilder(); + $deleteQuery + ->delete($this->connection->quoteIdentifier($this->getStorageTable())) + ->where( + $deleteQuery->expr()->andX( + $deleteQuery->expr()->in( + $this->connection->quoteIdentifier('contentobject_attribute_id'), + ':fieldIds' + ), + $deleteQuery->expr()->eq( + $this->connection->quoteIdentifier('version'), + ':versionNo' + ) + ) + ) + ->setParameter(':fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY) + ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) + ; + + $deleteQuery->execute(); + } + + /** + * Remove a specific file reference for $fieldId and $versionId. + * + * @param int $fieldId + * @param int $versionNo + */ + public function removeFileReference($fieldId, $versionNo) + { + $deleteQuery = $this->connection->createQueryBuilder(); + $deleteQuery + ->delete($this->connection->quoteIdentifier($this->getStorageTable())) + ->where( + $deleteQuery->expr()->andX( + $deleteQuery->expr()->eq( + $this->connection->quoteIdentifier('contentobject_attribute_id'), + ':fieldId' + ), + $deleteQuery->expr()->eq( + $this->connection->quoteIdentifier('version'), + ':versionNo' + ) + ) + ) + ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) + ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) + ; + + $deleteQuery->execute(); + } + + /** + * Return a set o file references, referenced by the given $fieldIds. + * + * @param array $fieldIds + * + * @return array + */ + public function getReferencedFiles(array $fieldIds, $versionNo) + { + if (empty($fieldIds)) { + return []; + } + + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery + ->select( + $this->connection->quoteIdentifier('filename'), + $this->connection->quoteIdentifier('mime_type') + ) + ->from($this->connection->quoteIdentifier($this->getStorageTable())) + ->where( + $selectQuery->expr()->andX( + $selectQuery->expr()->in( + $this->connection->quoteIdentifier('contentobject_attribute_id'), + ':fieldIds' + ), + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('version'), + ':versionNo' + ) + ) + ) + ->setParameter(':fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY) + ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) + ; + + $statement = $selectQuery->execute(); + + return array_map( + function ($row) { + return $this->prependMimeToPath($row['filename'], $row['mime_type']); + }, + $statement->fetchAll(PDO::FETCH_ASSOC) + ); + } + + /** + * Return a map with the number of references each file from $files has. + * + * @param array $files + * + * @return array + */ + public function countFileReferences(array $files) + { + if (empty($files)) { + return []; + } + + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery + ->select( + $this->connection->quoteIdentifier('filename'), + $this->connection->quoteIdentifier('mime_type'), + sprintf( + 'COUNT(%s) AS count', + $this->connection->quoteIdentifier('contentobject_attribute_id') + ) + ) + ->from($this->connection->quoteIdentifier($this->getStorageTable())) + ->where( + $selectQuery->expr()->in( + $this->connection->quoteIdentifier('filename'), + ':filenames' + ) + ) + ->groupBy( + $this->connection->quoteIdentifier('filename'), + $this->connection->quoteIdentifier('mime_type') + ) + ->setParameter( + ':filenames', + array_map( + [$this, 'removeMimeFromPath'], + $files + ), + Connection::PARAM_STR_ARRAY + ) + ; + + $statement = $selectQuery->execute(); + + $countMap = []; + foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $row) { + $path = $this->prependMimeToPath($row['filename'], $row['mime_type']); + $countMap[$path] = (int)$row['count']; + } + + // Complete counts + foreach ($files as $path) { + // This is already the correct path + if (!isset($countMap[$path])) { + $countMap[$path] = 0; + } + } + + return $countMap; + } +} + +class_alias(DoctrineStorage::class, 'eZ\Publish\Core\FieldType\BinaryBase\BinaryBaseStorage\Gateway\DoctrineStorage'); diff --git a/src/lib/FieldType/BinaryBase/PathGenerator/LegacyPathGenerator.php b/src/lib/FieldType/BinaryBase/PathGenerator/LegacyPathGenerator.php new file mode 100644 index 0000000000..221228d294 --- /dev/null +++ b/src/lib/FieldType/BinaryBase/PathGenerator/LegacyPathGenerator.php @@ -0,0 +1,37 @@ +value->externalData['fileName'], PATHINFO_EXTENSION); + + return $this->getFirstPartOfMimeType($field->value->externalData['mimeType']) + . '/' . bin2hex(random_bytes(16)) + . (!empty($extension) ? '.' . $extension : ''); + } + + /** + * Extracts the first part (before the '/') from the given $mimeType. + * + * @param string $mimeType + * + * @return string + */ + protected function getFirstPartOfMimeType($mimeType) + { + return substr($mimeType, 0, strpos($mimeType, '/')); + } +} + +class_alias(LegacyPathGenerator::class, 'eZ\Publish\Core\FieldType\BinaryBase\PathGenerator\LegacyPathGenerator'); diff --git a/src/lib/FieldType/BinaryBase/Type.php b/src/lib/FieldType/BinaryBase/Type.php new file mode 100644 index 0000000000..fa9b750b16 --- /dev/null +++ b/src/lib/FieldType/BinaryBase/Type.php @@ -0,0 +1,378 @@ + [ + 'maxFileSize' => [ + 'type' => 'int', + 'default' => null, + ], + ], + ]; + + /** @var \Ibexa\Core\FieldType\Validator[] */ + private $validators; + + /** @var \Ibexa\Contracts\Core\FieldType\BinaryBase\RouteAwarePathGenerator|null */ + protected $routeAwarePathGenerator; + + /** + * @param \Ibexa\Core\FieldType\Validator[] $validators + */ + public function __construct(array $validators, ?RouteAwarePathGenerator $routeAwarePathGenerator = null) + { + $this->validators = $validators; + $this->routeAwarePathGenerator = $routeAwarePathGenerator; + } + + /** + * Creates a specific value of the derived class from $inputValue. + * + * @param array $inputValue + * + * @return \Ibexa\Core\FieldType\Media\Value + */ + abstract protected function createValue(array $inputValue); + + final protected function regenerateUri(array $inputValue): array + { + if (isset($this->routeAwarePathGenerator, $inputValue['route'])) { + $inputValue['uri'] = $this->routeAwarePathGenerator->generate( + $inputValue['route'], + $inputValue['route_parameters'] ?? [] + ); + } + + unset($inputValue['route'], $inputValue['route_parameters']); + + return $inputValue; + } + + /** + * @param \Ibexa\Core\FieldType\BinaryBase\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + return (string)$value->fileName; + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param string|array|\Ibexa\Core\FieldType\BinaryBase\Value $inputValue + * + * @return \Ibexa\Core\FieldType\BinaryBase\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + // construction only from path + if (is_string($inputValue)) { + $inputValue = ['inputUri' => $inputValue]; + } + + // default construction from array + if (is_array($inputValue)) { + $inputValue = $this->createValue($inputValue); + } + + $this->completeValue($inputValue); + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\BinaryBase\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + // Input file URI, if set needs to point to existing file + if (isset($value->inputUri)) { + if (!file_exists($value->inputUri)) { + throw new InvalidArgumentValue( + '$value->inputUri', + $value->inputUri, + static::class + ); + } + } elseif (!isset($value->id)) { + throw new InvalidArgumentValue( + '$value->id', + $value->id, + static::class + ); + } + + // Required parameter $fileName + if (!isset($value->fileName) || !is_string($value->fileName)) { + throw new InvalidArgumentValue( + '$value->fileName', + $value->fileName, + static::class + ); + } + + // Optional parameter $fileSize + if (isset($value->fileSize) && !is_int($value->fileSize)) { + throw new InvalidArgumentValue( + '$value->fileSize', + $value->fileSize, + static::class + ); + } + } + + /** + * Attempts to complete the data in $value. + * + * @param \Ibexa\Core\FieldType\BinaryBase\Value|\Ibexa\Core\FieldType\Value $value + */ + protected function completeValue(BaseValue $value) + { + if (!isset($value->inputUri) || !file_exists($value->inputUri)) { + return; + } + + if (!isset($value->fileName)) { + // @todo this may not always work... + $value->fileName = basename($value->inputUri); + } + + if (!isset($value->fileSize)) { + $value->fileSize = filesize($value->inputUri); + } + } + + /** + * BinaryBase does not support sorting, yet. + * + * @param \Ibexa\Core\FieldType\BinaryBase\Value $value + * + * @return mixed + */ + protected function getSortInfo(BaseValue $value) + { + return false; + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\BinaryBase\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + return $this->createValue($hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\BinaryBase\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + return [ + 'id' => $value->id, + // Kept for BC with Ibexa 5.0 (EZP-20948, EZP-22808) + 'path' => $value->inputUri, + 'inputUri' => $value->inputUri, + 'fileName' => $value->fileName, + 'fileSize' => $value->fileSize, + 'mimeType' => $value->mimeType, + 'uri' => $value->uri, + ]; + } + + public function toPersistenceValue(SPIValue $value) + { + // Store original data as external (to indicate they need to be stored) + return new PersistenceValue( + [ + 'data' => null, + 'externalData' => $this->toHash($value), + 'sortKey' => $this->getSortInfo($value), + ] + ); + } + + /** + * Converts a persistence $fieldValue to a Value. + * + * This method builds a field type value from the $data and $externalData properties. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + * + * @return \Ibexa\Core\FieldType\BinaryBase\Value + */ + public function fromPersistenceValue(PersistenceValue $fieldValue) + { + // Restored data comes in $data, since it has already been processed + // there might be more data in the persistence value than needed here + $hash = [ + 'id' => $fieldValue->externalData['id'] ?? null, + 'fileName' => $fieldValue->externalData['fileName'] ?? null, + 'fileSize' => $fieldValue->externalData['fileSize'] ?? null, + 'mimeType' => $fieldValue->externalData['mimeType'] ?? null, + 'uri' => $fieldValue->externalData['uri'] ?? null, + ]; + + if (isset($fieldValue->externalData['route'])) { + $hash['route'] = $fieldValue->externalData['route']; + } + + if (isset($fieldValue->externalData['route_parameters'])) { + $hash['route_parameters'] = $fieldValue->externalData['route_parameters']; + } + + return $this->fromHash($hash); + } + + /** + * Validates a field based on the validators in the field definition. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field + * @param \Ibexa\Core\FieldType\BinaryBase\Value $fieldValue The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) + { + $errors = []; + + if ($this->isEmptyValue($fieldValue)) { + return $errors; + } + + foreach ($this->validators as $externalValidator) { + if (!$externalValidator->validate($fieldValue)) { + $errors = array_merge($errors, $externalValidator->getMessage()); + } + } + + foreach ((array)$fieldDefinition->getValidatorConfiguration() as $validatorIdentifier => $parameters) { + switch ($validatorIdentifier) { + // @todo There is a risk if we rely on a user built Value, since the FileSize + // property can be set manually, making this validation pointless + case 'FileSizeValidator': + if (empty($parameters['maxFileSize'])) { + // No file size limit + break; + } + // Database stores maxFileSize in MB + if (($parameters['maxFileSize'] * 1024 * 1024) < $fieldValue->fileSize) { + $errors[] = new ValidationError( + 'The file size cannot exceed %size% megabyte.', + 'The file size cannot exceed %size% megabytes.', + [ + '%size%' => $parameters['maxFileSize'], + ], + 'fileSize' + ); + } + break; + } + } + + return $errors; + } + + /** + * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $validatorConfiguration + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateValidatorConfiguration($validatorConfiguration) + { + $validationErrors = []; + + foreach ($validatorConfiguration as $validatorIdentifier => $parameters) { + switch ($validatorIdentifier) { + case 'FileSizeValidator': + if (!array_key_exists('maxFileSize', $parameters)) { + $validationErrors[] = new ValidationError( + 'Validator %validator% expects parameter %parameter% to be set.', + null, + [ + '%validator%' => $validatorIdentifier, + '%parameter%' => 'maxFileSize', + ], + "[$validatorIdentifier][maxFileSize]" + ); + break; + } + if (!is_int($parameters['maxFileSize']) && $parameters['maxFileSize'] !== null) { + $validationErrors[] = new ValidationError( + 'Validator %validator% expects parameter %parameter% to be of %type%.', + null, + [ + '%validator%' => $validatorIdentifier, + '%parameter%' => 'maxFileSize', + '%type%' => 'integer', + "[$validatorIdentifier][maxFileSize]", + ] + ); + } + break; + default: + $validationErrors[] = new ValidationError( + "Validator '%validator%' is unknown", + null, + [ + '%validator%' => $validatorIdentifier, + ], + "[$validatorIdentifier]" + ); + } + } + + return $validationErrors; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\BinaryBase\Type'); diff --git a/src/lib/FieldType/BinaryBase/Value.php b/src/lib/FieldType/BinaryBase/Value.php new file mode 100644 index 0000000000..e3b1608e9f --- /dev/null +++ b/src/lib/FieldType/BinaryBase/Value.php @@ -0,0 +1,126 @@ +uri; + } + + public function __get($propertyName) + { + if ($propertyName === 'path') { + return $this->inputUri; + } + + return parent::__get($propertyName); + } + + public function __set($propertyName, $propertyValue) + { + // BC with 5.0 (EZP-20948) + if ($propertyName === 'path') { + $this->inputUri = $propertyValue; + } elseif ($propertyName === 'id' && file_exists($propertyValue)) { // BC with 5.2 (EZP-22808) + $this->inputUri = $propertyValue; + } else { + parent::__set($propertyName, $propertyValue); + } + } + + public function __isset($propertyName) + { + if ($propertyName === 'path') { + return true; + } + + return parent::__isset($propertyName); + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\BinaryBase\Value'); diff --git a/src/lib/FieldType/BinaryFile/BinaryFileStorage.php b/src/lib/FieldType/BinaryFile/BinaryFileStorage.php new file mode 100644 index 0000000000..5657a5d967 --- /dev/null +++ b/src/lib/FieldType/BinaryFile/BinaryFileStorage.php @@ -0,0 +1,18 @@ + 'downloadCount', + 'cast' => 'intval', + ]; + + return $propertyMap; + } + + /** + * {@inheritdoc} + */ + protected function setFetchColumns(QueryBuilder $queryBuilder, $fieldId, $versionNo) + { + parent::setFetchColumns($queryBuilder, $fieldId, $versionNo); + + $queryBuilder->addSelect( + $this->connection->quoteIdentifier('download_count') + ); + } + + /** + * {@inheritdoc} + */ + protected function setInsertColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field) + { + parent::setInsertColumns($queryBuilder, $versionInfo, $field); + + $queryBuilder + ->setValue('download_count', ':downloadCount') + ->setParameter( + ':downloadCount', + $field->value->externalData['downloadCount'], + PDO::PARAM_INT + ) + ; + } +} + +class_alias(DoctrineStorage::class, 'eZ\Publish\Core\FieldType\BinaryFile\BinaryFileStorage\Gateway\DoctrineStorage'); diff --git a/src/lib/FieldType/BinaryFile/SearchField.php b/src/lib/FieldType/BinaryFile/SearchField.php new file mode 100644 index 0000000000..0d58746314 --- /dev/null +++ b/src/lib/FieldType/BinaryFile/SearchField.php @@ -0,0 +1,78 @@ +value->externalData['fileName'] ?? null, + new Search\FieldType\StringField() + ), + new Search\Field( + 'file_size', + $field->value->externalData['fileSize'] ?? null, + new Search\FieldType\IntegerField() + ), + new Search\Field( + 'mime_type', + $field->value->externalData['mimeType'] ?? null, + new Search\FieldType\StringField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'file_name' => new Search\FieldType\StringField(), + 'file_size' => new Search\FieldType\IntegerField(), + 'mime_type' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'file_name'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\BinaryFile\SearchField'); diff --git a/src/lib/FieldType/BinaryFile/Type.php b/src/lib/FieldType/BinaryFile/Type.php new file mode 100644 index 0000000000..c14a35e070 --- /dev/null +++ b/src/lib/FieldType/BinaryFile/Type.php @@ -0,0 +1,124 @@ +regenerateUri($inputValue); + + return new Value($inputValue); + } + + /** + * Attempts to complete the data in $value. + * + * @param \Ibexa\Core\FieldType\BinaryFile\Value|\Ibexa\Core\FieldType\Value $value + */ + protected function completeValue(Basevalue $value) + { + parent::completeValue($value); + + if (isset($value->downloadCount) && $value->downloadCount === null) { + $value->downloadCount = 0; + } + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\BinaryFile\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + $hash = parent::toHash($value); + + $hash['downloadCount'] = $value->downloadCount; + + return $hash; + } + + /** + * Converts a persistence $fieldValue to a Value. + * + * This method builds a field type value from the $data and $externalData properties. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + * + * @return \Ibexa\Core\FieldType\BinaryFile\Value + */ + public function fromPersistenceValue(FieldValue $fieldValue) + { + if ($fieldValue->externalData === null) { + return $this->getEmptyValue(); + } + + $result = parent::fromPersistenceValue($fieldValue); + + $result->downloadCount = (isset($fieldValue->externalData['downloadCount']) + ? $fieldValue->externalData['downloadCount'] + : 0); + + return $result; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezbinaryfile.name', 'ibexa_fieldtypes')->setDesc('File'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\BinaryFile\Type'); diff --git a/src/lib/FieldType/BinaryFile/Value.php b/src/lib/FieldType/BinaryFile/Value.php new file mode 100644 index 0000000000..a92c6329b3 --- /dev/null +++ b/src/lib/FieldType/BinaryFile/Value.php @@ -0,0 +1,24 @@ +value->data, + new Search\FieldType\BooleanField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value' => new Search\FieldType\BooleanField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\Checkbox\SearchField'); diff --git a/src/lib/FieldType/Checkbox/Type.php b/src/lib/FieldType/Checkbox/Type.php new file mode 100644 index 0000000000..c0971f6bc2 --- /dev/null +++ b/src/lib/FieldType/Checkbox/Type.php @@ -0,0 +1,154 @@ +bool ? '1' : '0'; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Checkbox\Value + */ + public function getEmptyValue() + { + return new Value(false); + } + + public function isEmptyValue(SPIValue $value): bool + { + return false; + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param bool|\Ibexa\Core\FieldType\Checkbox\Value $inputValue + * + * @return \Ibexa\Core\FieldType\Checkbox\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_bool($inputValue)) { + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Checkbox\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!$value instanceof Value) { + throw new InvalidArgumentType( + '$value', + Value::class, + $value + ); + } + + if (!is_bool($value->bool)) { + throw new InvalidArgumentType( + '$value->bool', + 'boolean', + $value->bool + ); + } + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * @param \Ibexa\Core\FieldType\Checkbox\Value $value + * + * @return int + */ + protected function getSortInfo(BaseValue $value) + { + return (int)$value->bool; + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\Checkbox\Value $value + */ + public function fromHash($hash) + { + return new Value($hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Checkbox\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + return $value->bool; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezboolean.name', 'ibexa_fieldtypes')->setDesc('Checkbox'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Checkbox\Type'); diff --git a/src/lib/FieldType/Checkbox/Value.php b/src/lib/FieldType/Checkbox/Value.php new file mode 100644 index 0000000000..016e95d513 --- /dev/null +++ b/src/lib/FieldType/Checkbox/Value.php @@ -0,0 +1,42 @@ +bool = $boolValue; + } + + /** + * @return string + */ + public function __toString() + { + return $this->bool ? '1' : '0'; + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\Checkbox\Value'); diff --git a/src/lib/FieldType/Country/Exception/InvalidValue.php b/src/lib/FieldType/Country/Exception/InvalidValue.php new file mode 100644 index 0000000000..5a7f714e06 --- /dev/null +++ b/src/lib/FieldType/Country/Exception/InvalidValue.php @@ -0,0 +1,27 @@ +countriesInfo = $countriesInfo; + } + + public function getIndexData(Field $field, FieldDefinition $fieldDefinition) + { + if (empty($field->value->data)) { + return []; + } + + $nameList = []; + $IDCList = []; + $alpha2List = []; + $alpha3List = []; + + foreach ($field->value->data as $alpha2) { + if (isset($this->countriesInfo[$alpha2])) { + $nameList[] = $this->countriesInfo[$alpha2]['Name']; + $IDCList[] = $this->countriesInfo[$alpha2]['IDC']; + $alpha2List[] = $this->countriesInfo[$alpha2]['Alpha2']; + $alpha3List[] = $this->countriesInfo[$alpha2]['Alpha3']; + } + } + + return [ + new Search\Field( + 'idc', + $IDCList, + new Search\FieldType\MultipleIntegerField() + ), + new Search\Field( + 'alpha2', + $alpha2List, + new Search\FieldType\MultipleStringField() + ), + new Search\Field( + 'alpha3', + $alpha3List, + new Search\FieldType\MultipleStringField() + ), + new Search\Field( + 'name', + $nameList, + new Search\FieldType\MultipleStringField() + ), + new Search\Field( + 'sort_value', + $field->value->sortKey, + new Search\FieldType\StringField() + ), + new Search\Field( + 'fulltext', + $nameList, + new Search\FieldType\FullTextField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'idc' => new Search\FieldType\MultipleIntegerField(), + 'alpha2' => new Search\FieldType\MultipleStringField(), + 'alpha3' => new Search\FieldType\MultipleStringField(), + 'name' => new Search\FieldType\MultipleStringField(), + 'sort_value' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'name'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return 'sort_value'; + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\Country\SearchField'); diff --git a/src/lib/FieldType/Country/Type.php b/src/lib/FieldType/Country/Type.php new file mode 100644 index 0000000000..7bb257ad38 --- /dev/null +++ b/src/lib/FieldType/Country/Type.php @@ -0,0 +1,278 @@ + [ + 'type' => 'boolean', + 'default' => false, + ], + ]; + + /** @var array */ + protected $countriesInfo; + + /** + * @param array $countriesInfo Array of countries data + */ + public function __construct(array $countriesInfo) + { + $this->countriesInfo = $countriesInfo; + } + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezcountry'; + } + + /** + * @param \Ibexa\Core\FieldType\Country\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + return (string)$value; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Country\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param array|\Ibexa\Core\FieldType\Country\Value $inputValue + * + * @return \Ibexa\Core\FieldType\Country\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_array($inputValue)) { + $inputValue = $this->fromHash($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Country\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!is_array($value->countries)) { + throw new InvalidArgumentType( + '$value->countries', + 'array', + $value->countries + ); + } + } + + /** + * Validates field value against 'isMultiple' setting. + * + * Does not use validators. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field + * @param \Ibexa\Core\FieldType\Country\Value $fieldValue The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) + { + $validationErrors = []; + + if ($this->isEmptyValue($fieldValue)) { + return $validationErrors; + } + + $fieldSettings = $fieldDefinition->getFieldSettings(); + + if ((!isset($fieldSettings['isMultiple']) || $fieldSettings['isMultiple'] === false) + && count($fieldValue->countries) > 1) { + $validationErrors[] = new ValidationError( + 'Field definition does not allow multiple countries to be selected.', + null, + [], + 'countries' + ); + } + + foreach ($fieldValue->countries as $alpha2 => $countryInfo) { + if (!isset($this->countriesInfo[$alpha2])) { + $validationErrors[] = new ValidationError( + "Country with Alpha2 code '%alpha2%' is not defined in FieldType settings.", + null, + [ + '%alpha2%' => $alpha2, + ], + 'countries' + ); + } + } + + return $validationErrors; + } + + /** + * {@inheritdoc} + */ + protected function getSortInfo(BaseValue $value) + { + $countries = []; + foreach ($value->countries as $countryInfo) { + $countries[] = $this->transformationProcessor->transformByGroup($countryInfo['Name'], 'lowercase'); + } + + sort($countries); + + return implode(',', $countries); + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\Country\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + $countries = []; + foreach ($hash as $country) { + foreach ($this->countriesInfo as $countryInfo) { + switch ($country) { + case $countryInfo['Name']: + case $countryInfo['Alpha2']: + case $countryInfo['Alpha3']: + $countries[$countryInfo['Alpha2']] = $countryInfo; + continue 3; + } + } + + throw new InvalidValue($country); + } + + return new Value($countries); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Country\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + return array_keys($value->countries); + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + /** + * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $fieldSettings + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateFieldSettings($fieldSettings) + { + $validationErrors = []; + + foreach ($fieldSettings as $name => $value) { + if (!isset($this->settingsSchema[$name])) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is unknown", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + continue; + } + + switch ($name) { + case 'isMultiple': + if (!is_bool($value)) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' value must be of boolean type", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + } + } + + return $validationErrors; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezcountry.name', 'ibexa_fieldtypes')->setDesc('Country'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Country\Type'); diff --git a/src/lib/FieldType/Country/Value.php b/src/lib/FieldType/Country/Value.php new file mode 100644 index 0000000000..a032be5477 --- /dev/null +++ b/src/lib/FieldType/Country/Value.php @@ -0,0 +1,51 @@ + + * array( + * "JP" => array( + * "Name" => "Japan", + * "Alpha2" => "JP", + * "Alpha3" => "JPN", + * "IDC" => 81 + * ) + * ) + * + * + * @var array[] + */ + public $countries = []; + + /** + * Construct a new Value object and initialize it with given $data. + * + * @param array[] $countries + */ + public function __construct(array $countries = []) + { + $this->countries = $countries; + } + + public function __toString() + { + return implode(', ', array_column($this->countries, 'Name')); + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\Country\Value'); diff --git a/src/lib/FieldType/Date/SearchField.php b/src/lib/FieldType/Date/SearchField.php new file mode 100644 index 0000000000..0e8eeeb513 --- /dev/null +++ b/src/lib/FieldType/Date/SearchField.php @@ -0,0 +1,75 @@ +value->data !== null) { + $dateTime = new DateTime("@{$field->value->data['timestamp']}"); + + $value = $dateTime->format('Y-m-d\\Z'); + } else { + $value = null; + } + + return [ + new Search\Field( + 'value', + $value, + new Search\FieldType\DateField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value' => new Search\FieldType\DateField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\Date\SearchField'); diff --git a/src/lib/FieldType/Date/Type.php b/src/lib/FieldType/Date/Type.php new file mode 100644 index 0000000000..4af0fa813c --- /dev/null +++ b/src/lib/FieldType/Date/Type.php @@ -0,0 +1,248 @@ + [ + 'type' => 'choice', + 'default' => self::DEFAULT_EMPTY, + ], + ]; + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezdate'; + } + + /** + * @param \Ibexa\Core\FieldType\Date\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + if ($this->isEmptyValue($value)) { + return ''; + } + + return $value->date->format('l d F Y'); + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Date\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param string|int|\DateTime|\Ibexa\Core\FieldType\Date\Value $inputValue + * + * @return \Ibexa\Core\FieldType\Date\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_string($inputValue)) { + $inputValue = Value::fromString($inputValue); + } + + if (is_int($inputValue)) { + $inputValue = Value::fromTimestamp($inputValue); + } + + if ($inputValue instanceof DateTime) { + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Date\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!$value->date instanceof DateTime) { + throw new InvalidArgumentType( + (string)$value->date, + 'DateTime', + $value->date + ); + } + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * @param \Ibexa\Core\FieldType\Date\Value $value + * + * @return mixed + */ + protected function getSortInfo(BaseValue $value) + { + if ($value->date === null) { + return null; + } + + return $value->date->getTimestamp(); + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash Null or associative array containing one of the following (first value found in the order below is picked): + * 'rfc850': Date in RFC 850 format (DateTime::RFC850) + * 'timestring': Date in parseable string format supported by DateTime (e.g. 'now', '+3 days') + * 'timestamp': Unix timestamp + * + * @return \Ibexa\Core\FieldType\Date\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + if (isset($hash['rfc850']) && $hash['rfc850']) { + return Value::fromString($hash['rfc850']); + } + + if (isset($hash['timestring']) && is_string($hash['timestring'])) { + return Value::fromString($hash['timestring']); + } + + return Value::fromTimestamp((int)$hash['timestamp']); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Date\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + if ($value->date instanceof DateTime) { + return [ + 'timestamp' => $value->date->getTimestamp(), + 'rfc850' => $value->date->format(DateTime::RFC850), + ]; + } + + return [ + 'timestamp' => 0, + 'rfc850' => null, + ]; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + /** + * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $fieldSettings + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateFieldSettings($fieldSettings) + { + $validationErrors = []; + + foreach ($fieldSettings as $name => $value) { + if (!isset($this->settingsSchema[$name])) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is unknown", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + continue; + } + + switch ($name) { + case 'defaultType': + $definedTypes = [ + self::DEFAULT_EMPTY, + self::DEFAULT_CURRENT_DATE, + ]; + if (!in_array($value, $definedTypes, true)) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is of unknown type", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + } + } + + return $validationErrors; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezdate.name', 'ibexa_fieldtypes')->setDesc('Date'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Date\Type'); diff --git a/src/lib/FieldType/Date/Value.php b/src/lib/FieldType/Date/Value.php new file mode 100644 index 0000000000..126fbee346 --- /dev/null +++ b/src/lib/FieldType/Date/Value.php @@ -0,0 +1,95 @@ +setTime(0, 0, 0); + } + $this->date = $dateTime; + } + + /** + * Creates a Value from the given $dateString. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param string $dateString + * + * @return \Ibexa\Core\FieldType\Date\Value + */ + public static function fromString($dateString) + { + try { + return new static(new DateTime($dateString, new DateTimeZone('UTC'))); + } catch (Exception $e) { + throw new InvalidArgumentValue('$dateString', $dateString, __CLASS__, $e); + } + } + + /** + * Creates a Value from the given $timestamp. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param int $timestamp + * + * @return \Ibexa\Core\FieldType\Date\Value + */ + public static function fromTimestamp($timestamp) + { + try { + return new static(new DateTime("@{$timestamp}")); + } catch (Exception $e) { + throw new InvalidArgumentValue('$timestamp', $timestamp, __CLASS__, $e); + } + } + + public function __toString() + { + if (!$this->date instanceof DateTime) { + return ''; + } + + return $this->date->format($this->stringFormat); + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\Date\Value'); diff --git a/src/lib/FieldType/DateAndTime/SearchField.php b/src/lib/FieldType/DateAndTime/SearchField.php new file mode 100644 index 0000000000..55274cc62e --- /dev/null +++ b/src/lib/FieldType/DateAndTime/SearchField.php @@ -0,0 +1,66 @@ +value->data['timestamp'] ?? null, + new Search\FieldType\DateField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value' => new Search\FieldType\DateField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\DateAndTime\SearchField'); diff --git a/src/lib/FieldType/DateAndTime/Type.php b/src/lib/FieldType/DateAndTime/Type.php new file mode 100644 index 0000000000..0303719a76 --- /dev/null +++ b/src/lib/FieldType/DateAndTime/Type.php @@ -0,0 +1,339 @@ + [ + 'type' => 'bool', + 'default' => false, + ], + // One of the DEFAULT_* class constants + 'defaultType' => [ + 'type' => 'choice', + 'default' => self::DEFAULT_EMPTY, + ], + /* + * @var DateInterval + * Used only if defaultValueType is set to DEFAULT_CURRENT_DATE_ADJUSTED + */ + 'dateInterval' => [ + 'type' => 'dateInterval', + 'default' => null, + ], + ]; + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezdatetime'; + } + + /** + * @param \Ibexa\Core\FieldType\DateAndTime\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + if ($this->isEmptyValue($value)) { + return ''; + } + + return $value->value->format('D Y-d-m H:i:s'); + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\DateAndTime\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param string|int|\DateTime|\Ibexa\Core\FieldType\DateAndTime\Value $inputValue + * + * @return \Ibexa\Core\FieldType\DateAndTime\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_string($inputValue)) { + $inputValue = Value::fromString($inputValue); + } + + if (is_int($inputValue)) { + $inputValue = Value::fromTimestamp($inputValue); + } + + if ($inputValue instanceof DateTime) { + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\DateAndTime\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!$value->value instanceof DateTime) { + throw new InvalidArgumentType( + '$value->value', + 'DateTime', + $value->value + ); + } + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * @param \Ibexa\Core\FieldType\DateAndTime\Value $value + * + * @return int|null + */ + protected function getSortInfo(BaseValue $value) + { + if ($value->value === null) { + return null; + } + + return $value->value->getTimestamp(); + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash Null or associative array containing one of the following (first value found in the order below is picked): + * 'rfc850': Date in RFC 850 format (DateTime::RFC850) + * 'timestring': Date in parseable string format supported by DateTime (e.g. 'now', '+3 days') + * 'timestamp': Unix timestamp + * + * @return \Ibexa\Core\FieldType\DateAndTime\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + if (isset($hash['rfc850']) && $hash['rfc850']) { + return Value::fromString($hash['rfc850']); + } + + if (isset($hash['timestring']) && is_string($hash['timestring'])) { + return Value::fromString($hash['timestring']); + } + + return Value::fromTimestamp((int)$hash['timestamp']); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\DateAndTime\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + if ($value->value instanceof DateTime) { + return [ + 'timestamp' => $value->value->getTimestamp(), + 'rfc850' => $value->value->format(DateTime::RFC850), + ]; + } + + return [ + 'timestamp' => 0, + 'rfc850' => null, + ]; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + /** + * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $fieldSettings + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateFieldSettings($fieldSettings) + { + $validationErrors = []; + + foreach ($fieldSettings as $name => $value) { + if (isset($this->settingsSchema[$name])) { + switch ($name) { + case 'useSeconds': + if (!is_bool($value)) { + $validationErrors[] = new ValidationError( + "Setting 'Use seconds' value must be of boolean type", + null, + [], + "[$name]" + ); + } + break; + case 'defaultType': + $definedTypes = [ + self::DEFAULT_EMPTY, + self::DEFAULT_CURRENT_DATE, + self::DEFAULT_CURRENT_DATE_ADJUSTED, + ]; + if (!in_array($value, $definedTypes, true)) { + $validationErrors[] = new ValidationError( + "Setting 'Default value' is of unknown type", + null, + [], + "[$name]" + ); + } + break; + case 'dateInterval': + if (isset($value)) { + if ($value instanceof DateInterval) { + // String conversion of $value, because DateInterval objects cannot be compared directly + if ( + isset($fieldSettings['defaultType']) + && $fieldSettings['defaultType'] !== self::DEFAULT_CURRENT_DATE_ADJUSTED + && $value->format('%y%m%d%h%i%s') !== '000000' + ) { + $validationErrors[] = new ValidationError( + "Setting 'Current date and time adjusted by' can be used only when setting 'Default value' is set to 'Adjusted current datetime'", + null, + [], + "[$name]" + ); + } + } else { + $validationErrors[] = new ValidationError( + "Setting 'Current date and time adjusted by' value must be an instance of 'DateInterval' class", + null, + [], + "[$name]" + ); + } + } + break; + } + } else { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is unknown", + null, + ['%setting%' => $name], + "[$name]" + ); + } + } + + return $validationErrors; + } + + /** + * Converts the given $fieldSettings to a simple hash format. + * + * This is the default implementation, which just returns the given + * $fieldSettings, assuming they are already in a hash format. Overwrite + * this in your specific implementation, if necessary. + * + * @param mixed $fieldSettings + * + * @return array|scalar|null + */ + public function fieldSettingsToHash($fieldSettings) + { + $fieldSettingsHash = parent::fieldSettingsToHash($fieldSettings); + + if (isset($fieldSettingsHash['dateInterval'])) { + $fieldSettingsHash['dateInterval'] = $fieldSettingsHash['dateInterval']->format( + 'P%r%yY%r%mM%r%dDT%r%hH%iM%r%sS' + ); + } + + return $fieldSettingsHash; + } + + /** + * Converts the given $fieldSettingsHash to field settings of the type. + * + * This is the reverse operation of {@link fieldSettingsToHash()}. + * + * This is the default implementation, which just returns the given + * $fieldSettingsHash, assuming the supported field settings are already in + * a hash format. Overwrite this in your specific implementation, if + * necessary. + * + * @param array|scalar|null $fieldSettingsHash + * + * @return mixed + */ + public function fieldSettingsFromHash($fieldSettingsHash) + { + $fieldSettings = parent::fieldSettingsFromHash($fieldSettingsHash); + + if (isset($fieldSettings['dateInterval'])) { + $fieldSettings['dateInterval'] = new DateInterval($fieldSettings['dateInterval']); + } + + return $fieldSettings; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezdatetime.name', 'ibexa_fieldtypes')->setDesc('Date and time'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\DateAndTime\Type'); diff --git a/src/lib/FieldType/DateAndTime/Value.php b/src/lib/FieldType/DateAndTime/Value.php new file mode 100644 index 0000000000..a13959c054 --- /dev/null +++ b/src/lib/FieldType/DateAndTime/Value.php @@ -0,0 +1,85 @@ +value = $dateTime; + } + + /** + * Creates a Value from the given $dateString. + * + * @param string $dateString + * + * @return \Ibexa\Core\FieldType\DateAndTime\Value + */ + public static function fromString($dateString) + { + try { + return new static(new DateTime($dateString)); + } catch (Exception $e) { + throw new InvalidArgumentValue('$dateString', $dateString, __CLASS__, $e); + } + } + + /** + * Creates a Value from the given $timestamp. + * + * @param int $timestamp + * + * @return \Ibexa\Core\FieldType\DateAndTime\Value + */ + public static function fromTimestamp($timestamp) + { + try { + return new static(new DateTime("@{$timestamp}")); + } catch (Exception $e) { + throw new InvalidArgumentValue('$timestamp', $timestamp, __CLASS__, $e); + } + } + + public function __toString() + { + if (!$this->value instanceof DateTime) { + return ''; + } + + return $this->value->format($this->stringFormat); + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\DateAndTime\Value'); diff --git a/src/lib/FieldType/EmailAddress/SearchField.php b/src/lib/FieldType/EmailAddress/SearchField.php new file mode 100644 index 0000000000..328e3f3fd9 --- /dev/null +++ b/src/lib/FieldType/EmailAddress/SearchField.php @@ -0,0 +1,79 @@ +value->data, + new Search\FieldType\StringField() + ), + new Search\Field( + 'fulltext', + $field->value->data, + new Search\FieldType\FullTextField([ + 'space_normalize', + 'latin1_lowercase', + 'ascii_lowercase', + 'cyrillic_lowercase', + 'greek_lowercase', + 'latin_lowercase', + 'latin-exta_lowercase', + ], false) + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\EmailAddress\SearchField'); diff --git a/src/lib/FieldType/EmailAddress/Type.php b/src/lib/FieldType/EmailAddress/Type.php new file mode 100644 index 0000000000..0880c8bfb8 --- /dev/null +++ b/src/lib/FieldType/EmailAddress/Type.php @@ -0,0 +1,219 @@ + [], + ]; + + /** + * @param \Ibexa\Core\FieldType\EmailAddress\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + return $this->transformationProcessor->transformByGroup((string)$value, 'lowercase'); + } + + /** + * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $validatorConfiguration + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateValidatorConfiguration($validatorConfiguration) + { + $validationErrors = []; + $validator = new EmailAddressValidator(); + + foreach ($validatorConfiguration as $validatorIdentifier => $constraints) { + if ($validatorIdentifier !== 'EmailAddressValidator') { + $validationErrors[] = new ValidationError( + "Validator '%validator%' is unknown", + null, + [ + '%validator%' => $validatorIdentifier, + ], + "[$validatorIdentifier]" + ); + continue; + } + $validationErrors += $validator->validateConstraints($constraints); + } + + return $validationErrors; + } + + /** + * Validates a field based on the validators in the field definition. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field + * @param \Ibexa\Core\FieldType\EmailAddress\Value $fieldValue The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) + { + $errors = []; + + if ($this->isEmptyValue($fieldValue)) { + return $errors; + } + + $validatorConfiguration = $fieldDefinition->getValidatorConfiguration(); + $constraints = isset($validatorConfiguration['EmailAddressValidator']) ? + $validatorConfiguration['EmailAddressValidator'] : + []; + $validator = new EmailAddressValidator(); + $validator->initializeWithConstraints($constraints); + + if (!$validator->validate($fieldValue)) { + return $validator->getMessage(); + } + + return []; + } + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezemail'; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\EmailAddress\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param string|\Ibexa\Core\FieldType\EmailAddress\Value $inputValue + * + * @return \Ibexa\Core\FieldType\EmailAddress\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_string($inputValue)) { + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\EmailAddress\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!is_string($value->email)) { + throw new InvalidArgumentType( + '$value->email', + 'string', + $value->email + ); + } + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * @todo String normalization should occur here. + * + * @param \Ibexa\Core\FieldType\EmailAddress\Value $value + * + * @return string + */ + protected function getSortInfo(BaseValue $value) + { + return $value->email; + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\EmailAddress\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + return new Value($hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\EmailAddress\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + return $value->email; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezemail.name', 'ibexa_fieldtypes')->setDesc('Email address'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\EmailAddress\Type'); diff --git a/src/lib/FieldType/EmailAddress/Value.php b/src/lib/FieldType/EmailAddress/Value.php new file mode 100644 index 0000000000..ef3e029f7b --- /dev/null +++ b/src/lib/FieldType/EmailAddress/Value.php @@ -0,0 +1,39 @@ +email = $email; + } + + public function __toString() + { + return (string)$this->email; + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\EmailAddress\Value'); diff --git a/src/lib/FieldType/FieldSettings.php b/src/lib/FieldType/FieldSettings.php new file mode 100644 index 0000000000..083fb576e7 --- /dev/null +++ b/src/lib/FieldType/FieldSettings.php @@ -0,0 +1,60 @@ +transformationProcessor = $transformationProcessor; + } + + /** + * Returns a schema for the settings expected by the FieldType. + * + * This implementation returns an array. + * where the key is the setting name, and the value is the default value for given + * setting and set to null if no particular default should be set. + * + * @return mixed + */ + public function getSettingsSchema() + { + return $this->settingsSchema; + } + + /** + * Returns a schema for the validator configuration expected by the FieldType. + * + * @see FieldTypeInterface::getValidatorConfigurationSchema() + * + * This implementation returns a three dimensional map containing for each validator configuration + * referenced by identifier a map of supported parameters which are defined by a type and a default value + * (see example). + * + * + * array( + * 'stringLength' => array( + * 'minStringLength' => array( + * 'type' => 'int', + * 'default' => 0, + * ), + * 'maxStringLength' => array( + * 'type' => 'int' + * 'default' => null, + * ) + * ), + * ); + * + * + * @return mixed + */ + public function getValidatorConfigurationSchema() + { + return $this->validatorConfigurationSchema; + } + + /** + * Validates a field based on the validators in the field definition. + * + * This is a base implementation, returning an empty array() that indicates + * that no validation errors occurred. Overwrite in derived types, if + * validation is supported. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field + * @param \Ibexa\Core\FieldType\Value $value The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $value) + { + return []; + } + + /** + * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * This method expects that given $validatorConfiguration is complete, for this purpose method + * {@link self::applyDefaultValidatorConfiguration()} is provided. + * + * This is a base implementation, returning a validation error for each + * specified validator, since by default no validators are supported. + * Overwrite in derived types, if validation is supported. + * + * @param mixed $validatorConfiguration + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateValidatorConfiguration($validatorConfiguration) + { + $validationErrors = []; + + foreach ((array)$validatorConfiguration as $validatorIdentifier => $constraints) { + $validationErrors[] = new ValidationError( + "Validator '%validator%' is unknown", + null, + [ + 'validator' => $validatorIdentifier, + ], + "[$validatorIdentifier]" + ); + } + + return $validationErrors; + } + + /** + * Applies the default values to the given $validatorConfiguration of a FieldDefinitionCreateStruct. + * + * This is a base implementation, expecting best practice validator configuration format used by + * field types in standard Ibexa installation. Overwrite in derived types if needed. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param mixed $validatorConfiguration + */ + public function applyDefaultValidatorConfiguration(&$validatorConfiguration) + { + if ($validatorConfiguration !== null && !is_array($validatorConfiguration)) { + throw new InvalidArgumentType('$validatorConfiguration', 'array|null', $validatorConfiguration); + } + + foreach ($this->getValidatorConfigurationSchema() as $validatorName => $configurationSchema) { + // Set configuration of specific validator to empty array if it is not already provided + if (!isset($validatorConfiguration[$validatorName])) { + $validatorConfiguration[$validatorName] = []; + } + + foreach ($configurationSchema as $settingName => $settingConfiguration) { + // Check that a default entry exists in the configuration schema for the validator but that no value has been provided + if (!isset($validatorConfiguration[$validatorName][$settingName]) && array_key_exists('default', $settingConfiguration)) { + $validatorConfiguration[$validatorName][$settingName] = $settingConfiguration['default']; + } + } + } + } + + /** + * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * This method expects that given $fieldSettings are complete, for this purpose method + * {@link self::applyDefaultSettings()} is provided. + * + * @param mixed $fieldSettings + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateFieldSettings($fieldSettings) + { + if (!empty($fieldSettings)) { + return [ + new ValidationError( + "FieldType '%fieldType%' does not accept settings", + null, + [ + 'fieldType' => $this->getFieldTypeIdentifier(), + ], + 'fieldType' + ), + ]; + } + + return []; + } + + /** + * Applies the default values to the fieldSettings of a FieldDefinitionCreateStruct. + * + * This is a base implementation, expecting best practice field settings format used by + * field types in standard Ibexa installation. Overwrite in derived types if needed. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param mixed $fieldSettings + */ + public function applyDefaultSettings(&$fieldSettings) + { + if ($fieldSettings !== null && !is_array($fieldSettings)) { + throw new InvalidArgumentType('$fieldSettings', 'array|null', $fieldSettings); + } + + foreach ($this->getSettingsSchema() as $settingName => $settingConfiguration) { + // Checking that a default entry exists in the settingsSchema but that no value has been provided + if (!array_key_exists($settingName, (array)$fieldSettings) && array_key_exists('default', $settingConfiguration)) { + $fieldSettings[$settingName] = $settingConfiguration['default']; + } + } + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * Return value is mixed. It should be something which is sensible for + * sorting. + * + * It is up to the persistence implementation to handle those values. + * Common string and integer values are safe. + * + * For the legacy storage it is up to the field converters to set this + * value in either sort_key_string or sort_key_int. + * + * In case of multi value, values should be string and separated by "-" or ",". + * + * @param \Ibexa\Core\FieldType\Value $value + * + * @return mixed + */ + protected function getSortInfo(Value $value) + { + return null; + } + + /** + * Converts a $value to a persistence value. + * + * @param \Ibexa\Core\FieldType\Value $value + * + * @return \Ibexa\Contracts\Core\Persistence\Content\FieldValue + */ + public function toPersistenceValue(SPIValue $value) + { + // @todo Evaluate if creating the sortKey in every case is really needed + // Couldn't this be retrieved with a method, which would initialize + // that info on request only? + return new PersistenceValue( + [ + 'data' => $this->toHash($value), + 'externalData' => null, + 'sortKey' => $this->getSortInfo($value), + ] + ); + } + + /** + * Converts a persistence $fieldValue to a Value. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + * + * @return \Ibexa\Core\FieldType\Value + */ + public function fromPersistenceValue(PersistenceValue $fieldValue) + { + return $this->fromHash($fieldValue->data); + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return false; + } + + /** + * Indicates if the field definition of this type can appear only once in the same ContentType. + * + * @return bool + */ + public function isSingular() + { + return false; + } + + /** + * Indicates if the field definition of this type can be added to a ContentType with Content instances. + * + * @return bool + */ + public function onlyEmptyInstance() + { + return false; + } + + /** + * Returns if the given $value is considered empty by the field type. + * + * Default implementation, which performs a "==" check with the value + * returned by {@link getEmptyValue()}. Overwrite in the specific field + * type, if necessary. + * + * @param \Ibexa\Core\FieldType\Value $value + * + * @return bool + */ + public function isEmptyValue(SPIValue $value) + { + return $value == $this->getEmptyValue(); + } + + /** + * Potentially builds and checks the type and structure of the $inputValue. + * + * This method first inspects $inputValue and convert it into a dedicated + * value object. + * + * After that, the value is checked for structural validity. + * Note that this does not include validation after the rules + * from validators, but only plausibility checks for the general data + * format. + * + * Note that this method must also cope with the empty value for the field + * type as e.g. returned by {@link getEmptyValue()}. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the parameter is not of the supported value sub type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the value does not match the expected structure + * + * @param mixed $inputValue + * + * @return \Ibexa\Core\FieldType\Value The potentially converted and structurally plausible value. + */ + final public function acceptValue($inputValue) + { + if ($inputValue === null) { + return $this->getEmptyValue(); + } + + $value = $this->createValueFromInput($inputValue); + + static::checkValueType($value); + + if ($this->isEmptyValue($value)) { + return $this->getEmptyValue(); + } + + $this->checkValueStructure($value); + + return $value; + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * If given $inputValue could not be converted or is already an instance of dedicate value object, + * the method should simply return it. + * + * This is an operation method for {@see acceptValue()}. + * + * Example implementation: + * + * protected function createValueFromInput( $inputValue ) + * { + * if ( is_array( $inputValue ) ) + * { + * $inputValue = \My\FieldType\CookieJar\Value( $inputValue ); + * } + * + * return $inputValue; + * } + * + * + * @param mixed $inputValue + * + * @return mixed The potentially converted input value. + */ + abstract protected function createValueFromInput($inputValue); + + /** + * Throws an exception if the given $value is not an instance of the supported value subtype. + * + * This is an operation method for {@see acceptValue()}. + * + * Default implementation expects the value class to reside in the same namespace as its + * FieldType class and is named "Value". + * + * Example implementation: + * + * static protected function checkValueType( $value ) + * { + * if ( !$inputValue instanceof \My\FieldType\CookieJar\Value ) ) + * { + * throw new InvalidArgumentException( "Given value type is not supported." ); + * } + * } + * + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the parameter is not an instance of the supported value subtype. + * + * @param mixed $value A value returned by {@see createValueFromInput()}. + */ + protected static function checkValueType($value) + { + $fieldTypeFQN = static::class; + $valueFQN = substr_replace($fieldTypeFQN, 'Value', strrpos($fieldTypeFQN, '\\') + 1); + + if (!$value instanceof $valueFQN) { + throw new InvalidArgumentType('$value', $valueFQN, $value); + } + } + + /** + * Throws an exception if value structure is not of expected format. + * + * Note that this does not include validation after the rules + * from validators, but only plausibility checks for the general data + * format. + * + * This is an operation method for {@see acceptValue()}. + * + * Example implementation: + * + * protected function checkValueStructure( Value $value ) + * { + * if ( !is_array( $value->cookies ) ) + * { + * throw new InvalidArgumentException( "An array of assorted cookies was expected." ); + * } + * } + * + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Value $value + */ + abstract protected function checkValueStructure(Value $value); + + /** + * Converts the given $fieldSettings to a simple hash format. + * + * This is the default implementation, which just returns the given + * $fieldSettings, assuming they are already in a hash format. Overwrite + * this in your specific implementation, if necessary. + * + * @param mixed $fieldSettings + * + * @return array|scalar|null + */ + public function fieldSettingsToHash($fieldSettings) + { + return $fieldSettings; + } + + /** + * Converts the given $fieldSettingsHash to field settings of the type. + * + * This is the reverse operation of {@link fieldSettingsToHash()}. + * + * This is the default implementation, which just returns the given + * $fieldSettingsHash, assuming the supported field settings are already in + * a hash format. Overwrite this in your specific implementation, if + * necessary. + * + * @param array|scalar|null $fieldSettingsHash + * + * @return mixed + */ + public function fieldSettingsFromHash($fieldSettingsHash) + { + return $fieldSettingsHash; + } + + /** + * Converts the given $validatorConfiguration to a simple hash format. + * + * Default implementation, which just returns the given + * $validatorConfiguration, which is by convention an array for all + * internal field types. Overwrite this method, if necessary. + * + * @param mixed $validatorConfiguration + * + * @return array|scalar|null + */ + public function validatorConfigurationToHash($validatorConfiguration) + { + return $validatorConfiguration; + } + + /** + * Converts the given $validatorConfigurationHash to a validator + * configuration of the type. + * + * Default implementation, which just returns the given + * $validatorConfigurationHash, since the validator configuration is by + * convention an array for all internal field types. Overwrite this method, + * if necessary. + * + * @param array|scalar|null $validatorConfigurationHash + * + * @return mixed + */ + public function validatorConfigurationFromHash($validatorConfigurationHash) + { + return $validatorConfigurationHash; + } + + /** + * Returns relation data extracted from value. + * + * Not intended for \Ibexa\Contracts\Core\Repository\Values\Content\Relation::COMMON type relations, + * there is an API for handling those. + * + * @param \Ibexa\Core\FieldType\Value $fieldValue + * + * @return array Hash with relation type as key and array of destination content ids as value. + * + * Example: + * + * array( + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::LINK => array( + * "contentIds" => array( 12, 13, 14 ), + * "locationIds" => array( 24 ) + * ), + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::EMBED => array( + * "contentIds" => array( 12 ), + * "locationIds" => array( 24, 45 ) + * ), + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::FIELD => array( 12 ) + * ) + * + */ + public function getRelations(SPIValue $fieldValue) + { + return []; + } + + public function valuesEqual(SPIValue $value1, SPIValue $value2): bool + { + return $this->toHash($value1) === $this->toHash($value2); + } +} + +class_alias(FieldType::class, 'eZ\Publish\Core\FieldType\FieldType'); diff --git a/src/lib/FieldType/FieldTypeRegistry.php b/src/lib/FieldType/FieldTypeRegistry.php new file mode 100644 index 0000000000..1b463bbe63 --- /dev/null +++ b/src/lib/FieldType/FieldTypeRegistry.php @@ -0,0 +1,95 @@ +fieldTypes = $fieldTypes; + } + + /** + * Returns a list of all SPI FieldTypes. + * + * @return \Ibexa\Contracts\Core\FieldType\FieldType[] + */ + public function getFieldTypes(): array + { + return $this->fieldTypes; + } + + /** + * Return a SPI FieldType object. + * + * @throws \Ibexa\Core\Base\Exceptions\NotFound\FieldTypeNotFoundException If $identifier was not found + * + * @param string $identifier + * + * @return \Ibexa\Contracts\Core\FieldType\FieldType + */ + public function getFieldType($identifier): SPIFieldType + { + if (!isset($this->fieldTypes[$identifier])) { + throw new FieldTypeNotFoundException($identifier); + } + + return $this->fieldTypes[$identifier]; + } + + public function registerFieldType(string $identifier, SPIFieldType $fieldType): void + { + $this->fieldTypes[$identifier] = $fieldType; + } + + /** + * Returns if there is a SPI FieldType registered under $identifier. + * + * @param string $identifier + * + * @return bool + */ + public function hasFieldType($identifier): bool + { + return isset($this->fieldTypes[$identifier]); + } + + /** + * Registers $fieldTypeIdentifier as "concrete" FieldType (i.e. not using NullFieldType). + */ + public function registerConcreteFieldTypeIdentifier(string $fieldTypeIdentifier): void + { + $this->concreteFieldTypesIdentifiers[] = $fieldTypeIdentifier; + } + + /** + * @return string[] + */ + public function getConcreteFieldTypesIdentifiers(): array + { + return $this->concreteFieldTypesIdentifiers; + } +} + +class_alias(FieldTypeRegistry::class, 'eZ\Publish\Core\FieldType\FieldTypeRegistry'); diff --git a/src/lib/FieldType/Float/SearchField.php b/src/lib/FieldType/Float/SearchField.php new file mode 100644 index 0000000000..7d8b535d03 --- /dev/null +++ b/src/lib/FieldType/Float/SearchField.php @@ -0,0 +1,71 @@ +value->data, + new Search\FieldType\FloatField() + ), + new Search\Field( + 'fulltext', + $field->value->data, + new Search\FieldType\FullTextField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value' => new Search\FieldType\FloatField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\Float\SearchField'); diff --git a/src/lib/FieldType/Float/Type.php b/src/lib/FieldType/Float/Type.php new file mode 100644 index 0000000000..a8e2a3001b --- /dev/null +++ b/src/lib/FieldType/Float/Type.php @@ -0,0 +1,277 @@ + [ + 'minFloatValue' => [ + 'type' => 'float', + 'default' => null, + ], + 'maxFloatValue' => [ + 'type' => 'float', + 'default' => null, + ], + ], + ]; + + /** + * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $validatorConfiguration + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateValidatorConfiguration($validatorConfiguration) + { + $validationErrors = []; + + foreach ($validatorConfiguration as $validatorIdentifier => $constraints) { + if ($validatorIdentifier !== 'FloatValueValidator') { + $validationErrors[] = new ValidationError( + "Validator '%validator%' is unknown", + null, + [ + '%validator%' => $validatorIdentifier, + ], + "[$validatorIdentifier]" + ); + + continue; + } + + foreach ($constraints as $name => $value) { + switch ($name) { + case 'minFloatValue': + case 'maxFloatValue': + if ($value !== null && !is_numeric($value)) { + $validationErrors[] = new ValidationError( + "Validator parameter '%parameter%' value must be of numeric type", + null, + [ + '%parameter%' => $name, + ], + "[$validatorIdentifier][$name]" + ); + } + break; + default: + $validationErrors[] = new ValidationError( + "Validator parameter '%parameter%' is unknown", + null, + [ + '%parameter%' => $name, + ], + "[$validatorIdentifier][$name]" + ); + } + } + } + + return $validationErrors; + } + + /** + * Validates a field based on the validators in the field definition. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field + * @param \Ibexa\Core\FieldType\Float\Value $fieldValue The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) + { + $validationErrors = []; + + if ($this->isEmptyValue($fieldValue)) { + return $validationErrors; + } + + $validatorConfiguration = $fieldDefinition->getValidatorConfiguration(); + $constraints = isset($validatorConfiguration['FloatValueValidator']) ? + $validatorConfiguration['FloatValueValidator'] : + []; + + $validationErrors = []; + + if (isset($constraints['maxFloatValue']) && + $constraints['maxFloatValue'] !== null && $fieldValue->value > $constraints['maxFloatValue']) { + $validationErrors[] = new ValidationError( + 'The value can not be higher than %size%.', + null, + [ + '%size%' => $constraints['maxFloatValue'], + ], + 'value' + ); + } + + if (isset($constraints['minFloatValue']) && + $constraints['minFloatValue'] !== null && $fieldValue->value < $constraints['minFloatValue']) { + $validationErrors[] = new ValidationError( + 'The value can not be lower than %size%.', + null, + [ + '%size%' => $constraints['minFloatValue'], + ], + 'value' + ); + } + + return $validationErrors; + } + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezfloat'; + } + + /** + * @param \Ibexa\Core\FieldType\Float\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + return (string)$value; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Float\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Implements the core of {@see isEmptyValue()}. + * + * @param mixed $value + * + * @return bool + */ + public function isEmptyValue(SPIValue $value) + { + return $value->value === null; + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param int|float|\Ibexa\Core\FieldType\Float\Value $inputValue + * + * @return \Ibexa\Core\FieldType\Float\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_numeric($inputValue)) { + $inputValue = (float)$inputValue; + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Float\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!is_float($value->value)) { + throw new InvalidArgumentType( + '$value->value', + 'float', + $value->value + ); + } + } + + /** + * {@inheritdoc} + * + * @param \Ibexa\Core\FieldType\Float\Value $value + */ + protected function getSortInfo(BaseValue $value) + { + return $value->value; + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\Float\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + return new Value((float)$hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Float\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + return $value->value; + } + + public function isSearchable(): bool + { + return true; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezfloat.name', 'ibexa_fieldtypes')->setDesc('Float'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Float\Type'); diff --git a/src/lib/FieldType/Float/Value.php b/src/lib/FieldType/Float/Value.php new file mode 100644 index 0000000000..d2aa2707ff --- /dev/null +++ b/src/lib/FieldType/Float/Value.php @@ -0,0 +1,39 @@ +value = $value; + } + + public function __toString() + { + return (string)$this->value; + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\Float\Value'); diff --git a/src/lib/FieldType/GatewayBasedStorage.php b/src/lib/FieldType/GatewayBasedStorage.php new file mode 100644 index 0000000000..4b0a43efeb --- /dev/null +++ b/src/lib/FieldType/GatewayBasedStorage.php @@ -0,0 +1,119 @@ + $gateway) { + $this->addGateway($identifier, $gateway); + } + } + + /** + * Adds a storage $gateway assigned to the given $identifier. + * + * @param string $identifier + * @param \Ibexa\Core\FieldType\StorageGateway $gateway + */ + public function addGateway($identifier, StorageGateway $gateway) + { + $this->gateways[$identifier] = $gateway; + } + + /** + * Retrieve the fitting gateway, base on the identifier in $context. + * + * @deprecated Since 6.11. Retrieving gateway based on $context is deprecated + * and will be removed in 7.0. Inject gateway directly into FieldStorage + * + * @param array $context + * + * @return \Ibexa\Core\FieldType\StorageGateway + */ + protected function getGateway(array $context) + { + @trigger_error( + sprintf( + '%s: Retrieving gateway based on $context is deprecated and will be removed in 7.0. Inject gateway directly into FieldStorage', + static::class + ), + E_USER_DEPRECATED + ); + + if (!isset($this->gateways[$context['identifier']])) { + throw new \OutOfBoundsException("No gateway for {$context['identifier']} available."); + } + + $gateway = $this->gateways[$context['identifier']]; + $gateway->setConnection($context['connection']); + + return $gateway; + } + + /** + * This method is used exclusively by Legacy Storage to copy external data of existing field in main language to + * the untranslatable field not passed in create or update struct, but created implicitly in storage layer. + * + * By default the method falls back to the {@link \Ibexa\Contracts\Core\FieldType\FieldStorage::storeFieldData()}. + * External storages implement this method as needed. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $originalField + * @param array $context + * + * @return bool|null Same as {@link \Ibexa\Contracts\Core\FieldType\FieldStorage::storeFieldData()}. + */ + public function copyLegacyField(VersionInfo $versionInfo, Field $field, Field $originalField, array $context) + { + return $this->storeFieldData($versionInfo, $field, $context); + } +} + +class_alias(GatewayBasedStorage::class, 'eZ\Publish\Core\FieldType\GatewayBasedStorage'); diff --git a/src/lib/FieldType/Handler.php b/src/lib/FieldType/Handler.php new file mode 100644 index 0000000000..edac92ce1f --- /dev/null +++ b/src/lib/FieldType/Handler.php @@ -0,0 +1,33 @@ +value->data, + new Search\FieldType\StringField() + ), + new Search\Field( + 'fulltext', + $field->value->data, + new Search\FieldType\FullTextField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\ISBN\SearchField'); diff --git a/src/lib/FieldType/ISBN/Type.php b/src/lib/FieldType/ISBN/Type.php new file mode 100644 index 0000000000..18de50fd9b --- /dev/null +++ b/src/lib/FieldType/ISBN/Type.php @@ -0,0 +1,358 @@ + [ + 'type' => 'boolean', + 'default' => true, + ], + ]; + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezisbn'; + } + + /** + * @param \Ibexa\Core\FieldType\ISBN\Value|\Ibexa\Contracts\Core\FieldType\Value $value + * + * @return string + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + return (string)$value->isbn; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\ISBN\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Returns if the given $value is considered empty by the field type. + * + * @param mixed $value + * + * @return bool + */ + public function isEmptyValue(SPIValue $value) + { + return $value->isbn === null || trim($value->isbn) === ''; + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param string|\Ibexa\Core\FieldType\ISBN\Value $inputValue + * + * @return \Ibexa\Core\FieldType\ISBN\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_string($inputValue)) { + $inputValue = $this->fromHash($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\ISBN\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!is_string($value->isbn)) { + throw new InvalidArgumentType( + '$value->isbn', + 'string', + $value->isbn + ); + } + } + + /** + * Validates a field based on the validators in the field definition. + * + * Does not use validators. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field + * @param \Ibexa\Core\FieldType\ISBN\Value $fieldValue The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) + { + $validationErrors = []; + if ($this->isEmptyValue($fieldValue)) { + return $validationErrors; + } + + $fieldSettings = $fieldDefinition->getFieldSettings(); + $isbnTestNumber = preg_replace("/[\s|\-]/", '', trim($fieldValue->isbn)); + + // Check if value and settings are inline + if ((!isset($fieldSettings['isISBN13']) || $fieldSettings['isISBN13'] === false) + && strlen($isbnTestNumber) !== 10) { + $validationErrors[] = new ValidationError( + 'ISBN-10 must be 10 character length', + null, + [], + 'isbn' + ); + } elseif (strlen($isbnTestNumber) === 10) { + // ISBN-10 check + if (!$this->validateISBNChecksum($isbnTestNumber)) { + $validationErrors[] = new ValidationError( + 'ISBN value must be in a valid ISBN-10 format', + null, + [], + 'isbn' + ); + } + } else { + // ISBN-13 check + if (!$this->validateISBN13Checksum($isbnTestNumber, $error)) { + $validationErrors[] = new ValidationError( + $error, + null, + [], + 'isbn' + ); + } + } + + return $validationErrors; + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * @param \Ibexa\Core\FieldType\ISBN\Value $value + * + * @return string + */ + protected function getSortInfo(BaseValue $value) + { + return $this->transformationProcessor->transformByGroup((string)$value, 'lowercase'); + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\ISBN\Value $value + */ + public function fromHash($hash) + { + if ($hash === null || $hash === '') { + return $this->getEmptyValue(); + } + + return new Value($hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\ISBN\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + return $value->isbn; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + /** + * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $fieldSettings + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateFieldSettings($fieldSettings) + { + $validationErrors = []; + + foreach ($fieldSettings as $name => $value) { + if (!isset($this->settingsSchema[$name])) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is unknown", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + continue; + } + + switch ($name) { + case 'isISBN13': + if (!is_bool($value)) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' value must be of boolean type", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + } + } + + return $validationErrors; + } + + /** + * Validates the ISBN number. + * All characters should be numeric except the last digit that may be the character X, + * which should be calculated as 10. + * + * @param string $isbnNr A string containing the number without any dashes. + * + * @return bool + */ + private function validateISBNChecksum($isbnNr) + { + $result = 0; + $isbnNr = strtoupper($isbnNr); + for ($i = 10; $i > 0; --$i) { + if (is_numeric($isbnNr[$i - 1]) || ($i === 10 && $isbnNr[$i - 1] === 'X')) { + if (($i === 1) && ($isbnNr[9] === 'X')) { + $result += 10 * $i; + } else { + $result += $isbnNr[10 - $i] * $i; + } + } else { + return false; + } + } + + return $result % 11 === 0; + } + + /** + * Validates the ISBN-13 number. + * + * @param string $isbnNr A string containing the number without any dashes. + * @param string $error is used to send back an error message that will be shown to the user if the + * ISBN number validated. + * + * @return bool + */ + private function validateISBN13Checksum($isbnNr, &$error) + { + if (!$isbnNr) { + return false; + } + + if (strlen($isbnNr) !== self::ISBN13_LENGTH) { + $error = 'ISBN-13 must be 13 digit, digit count is: ' . strlen($isbnNr); + + return false; + } + + if (substr($isbnNr, 0, self::ISBN13_PREFIX_LENGTH) !== self::ISBN13_PREFIX_978 && + substr($isbnNr, 0, self::ISBN13_PREFIX_LENGTH) !== self::ISBN13_PREFIX_979) { + $error = 'ISBN-13 value must start with 978 or 979, got: ' . substr($isbnNr, 0, self::ISBN13_PREFIX_LENGTH); + + return false; + } + + $checksum13 = 0; + $weight13 = 1; + //compute checksum + $val = 0; + for ($i = 0; $i < self::ISBN13_LENGTH; ++$i) { + $val = $isbnNr[$i]; + if (!is_numeric($isbnNr[$i])) { + $error = 'All ISBN-13 characters need to be numeric'; + + return false; + } + $checksum13 = $checksum13 + $weight13 * $val; + $weight13 = ($weight13 + 2) % 4; + } + if (($checksum13 % 10) !== 0) { + // Calculate the last digit from the 12 first numbers. + $checkDigit = (10 - (($checksum13 - (($weight13 + 2) % 4) * $val) % 10)) % 10; + //bad checksum + $error = 'Bad checksum, last digit of ISBN-13 should be ' . $checkDigit; + + return false; + } + + return true; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezisbn.name', 'ibexa_fieldtypes')->setDesc('ISBN'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\ISBN\Type'); diff --git a/src/lib/FieldType/ISBN/Value.php b/src/lib/FieldType/ISBN/Value.php new file mode 100644 index 0000000000..ecf41c3dfe --- /dev/null +++ b/src/lib/FieldType/ISBN/Value.php @@ -0,0 +1,39 @@ +isbn = $isbn; + } + + public function __toString() + { + return (string)$this->isbn; + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\ISBN\Value'); diff --git a/eZ/Publish/Core/FieldType/Image/AliasCleanerInterface.php b/src/lib/FieldType/Image/AliasCleanerInterface.php similarity index 78% rename from eZ/Publish/Core/FieldType/Image/AliasCleanerInterface.php rename to src/lib/FieldType/Image/AliasCleanerInterface.php index 92795e2f4c..a0a094b9cb 100644 --- a/eZ/Publish/Core/FieldType/Image/AliasCleanerInterface.php +++ b/src/lib/FieldType/Image/AliasCleanerInterface.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Image; +namespace Ibexa\Core\FieldType\Image; /** * Interface for image alias cleaners. @@ -18,3 +18,5 @@ interface AliasCleanerInterface */ public function removeAliases($originalPath); } + +class_alias(AliasCleanerInterface::class, 'eZ\Publish\Core\FieldType\Image\AliasCleanerInterface'); diff --git a/src/lib/FieldType/Image/IO/Legacy.php b/src/lib/FieldType/Image/IO/Legacy.php new file mode 100644 index 0000000000..957dd7a704 --- /dev/null +++ b/src/lib/FieldType/Image/IO/Legacy.php @@ -0,0 +1,266 @@ +publishedIOService = $publishedIOService; + $this->draftIOService = $draftIOService; + $this->optionsProvider = $optionsProvider; + $this->setPrefixes(); + } + + /** + * Sets the IOService prefix. + */ + public function setPrefix($prefix) + { + $this->publishedIOService->setPrefix($prefix); + $this->draftIOService->setPrefix($prefix); + } + + /** + * Computes the paths to published & draft images path using the options from the provider. + */ + private function setPrefixes() + { + $pathArray = [$this->optionsProvider->getVarDir()]; + + // The storage dir itself might be null + if ($storageDir = $this->optionsProvider->getStorageDir()) { + $pathArray[] = $storageDir; + } + + $this->draftPrefix = implode('/', array_merge($pathArray, [$this->optionsProvider->getDraftImagesDir()])); + $this->publishedPrefix = implode('/', array_merge($pathArray, [$this->optionsProvider->getPublishedImagesDir()])); + } + + public function getExternalPath($internalId) + { + return $this->publishedIOService->getExternalPath($internalId); + } + + public function newBinaryCreateStructFromLocalFile($localFile) + { + return $this->publishedIOService->newBinaryCreateStructFromLocalFile($localFile); + } + + public function exists($binaryFileId) + { + return $this->publishedIOService->exists($binaryFileId); + } + + public function getInternalPath($externalId) + { + return $this->publishedIOService->getInternalPath($externalId); + } + + public function loadBinaryFile($binaryFileId) + { + // If the id is an internal (absolute) path to a draft image, use the draft service to get external path & load + if ($this->isDraftImagePath($binaryFileId)) { + return $this->draftIOService->loadBinaryFile($this->draftIOService->getExternalPath($binaryFileId)); + } + + // If the id is an internal path (absolute) to a published image, replace with the internal path + if ($this->isPublishedImagePath($binaryFileId)) { + $binaryFileId = $this->publishedIOService->getExternalPath($binaryFileId); + } + + try { + $image = $this->publishedIOService->loadBinaryFile($binaryFileId); + + if ($image instanceof MissingBinaryFile) { + throw new InvalidArgumentException('binaryFileId', sprintf('Cannot find file with ID %s', $binaryFileId)); + } + + return $image; + } catch (InvalidArgumentException $prefixException) { + // InvalidArgumentException means that the prefix didn't match, NotFound can pass through + try { + return $this->draftIOService->loadBinaryFile($binaryFileId); + } catch (InvalidArgumentException $e) { + throw $prefixException; + } + } + } + + /** + * Since both services should use the same uri, we can use any of them to *GET* the URI. + */ + public function loadBinaryFileByUri($binaryFileUri) + { + try { + $image = $this->publishedIOService->loadBinaryFileByUri($binaryFileUri); + + if ($image instanceof MissingBinaryFile) { + throw new InvalidArgumentException('binaryFileUri', sprintf('Cannot find file with URL %s', $binaryFileUri)); + } + + return $image; + } catch (InvalidArgumentException $prefixException) { + // InvalidArgumentException means that the prefix didn't match, NotFound can pass through + try { + return $this->draftIOService->loadBinaryFileByUri($binaryFileUri); + } catch (InvalidArgumentException $e) { + throw $prefixException; + } + } + } + + public function getFileContents(BinaryFile $binaryFile) + { + if ($this->draftIOService->exists($binaryFile->id)) { + return $this->draftIOService->getFileContents($binaryFile); + } + + return $this->publishedIOService->getFileContents($binaryFile); + } + + public function createBinaryFile(BinaryFileCreateStruct $binaryFileCreateStruct) + { + return $this->publishedIOService->createBinaryFile($binaryFileCreateStruct); + } + + public function getUri($binaryFileId) + { + return $this->publishedIOService->getUri($binaryFileId); + } + + public function getMimeType($binaryFileId) + { + // If the id is an internal (absolute) path to a draft image, use the draft service to get external path & load + if ($this->isDraftImagePath($binaryFileId)) { + return $this->draftIOService->getMimeType($this->draftIOService->getExternalPath($binaryFileId)); + } + + // If the id is an internal path (absolute) to a published image, replace with the internal path + if ($this->isPublishedImagePath($binaryFileId)) { + $binaryFileId = $this->publishedIOService->getExternalPath($binaryFileId); + } + + if ($this->draftIOService->exists($binaryFileId)) { + return $this->draftIOService->getMimeType($binaryFileId); + } + + return $this->publishedIOService->getMimeType($binaryFileId); + } + + public function getFileInputStream(BinaryFile $binaryFile) + { + return $this->publishedIOService->getFileInputStream($binaryFile); + } + + public function deleteBinaryFile(BinaryFile $binaryFile) + { + $this->publishedIOService->deleteBinaryFile($binaryFile); + } + + /** + * Deletes a directory. + * + * @param string $path + */ + public function deleteDirectory($path) + { + $this->publishedIOService->deleteDirectory($path); + } + + public function newBinaryCreateStructFromUploadedFile(array $uploadedFile) + { + return $this->publishedIOService->newBinaryCreateStructFromUploadedFile($uploadedFile); + } + + /** + * Checks if $internalPath is a published image path. + * + * @param string $internalPath + * + * @return bool true if $internalPath is the path to a published image + */ + protected function isPublishedImagePath($internalPath) + { + return strpos($internalPath, $this->publishedPrefix) === 0; + } + + /** + * Checks if $internalPath is a published image path. + * + * @param string $internalPath + * + * @return bool true if $internalPath is the path to a published image + */ + protected function isDraftImagePath($internalPath) + { + return strpos($internalPath, $this->draftPrefix) === 0; + } +} + +class_alias(Legacy::class, 'eZ\Publish\Core\FieldType\Image\IO\Legacy'); diff --git a/eZ/Publish/Core/FieldType/Image/IO/OptionsProvider.php b/src/lib/FieldType/Image/IO/OptionsProvider.php similarity index 79% rename from eZ/Publish/Core/FieldType/Image/IO/OptionsProvider.php rename to src/lib/FieldType/Image/IO/OptionsProvider.php index 92d917eecc..fdc8eca1cc 100644 --- a/eZ/Publish/Core/FieldType/Image/IO/OptionsProvider.php +++ b/src/lib/FieldType/Image/IO/OptionsProvider.php @@ -4,16 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Image\IO; +namespace Ibexa\Core\FieldType\Image\IO; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; /** * @internal */ class OptionsProvider { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ protected $configResolver; public function __construct(ConfigResolverInterface $configResolver) @@ -48,3 +48,5 @@ public function getPublishedImagesDir() return $this->getSetting('image.published_images_dir'); } } + +class_alias(OptionsProvider::class, 'eZ\Publish\Core\FieldType\Image\IO\OptionsProvider'); diff --git a/eZ/Publish/Core/FieldType/Image/ImageStorage.php b/src/lib/FieldType/Image/ImageStorage.php similarity index 81% rename from eZ/Publish/Core/FieldType/Image/ImageStorage.php rename to src/lib/FieldType/Image/ImageStorage.php index 92b0dc7981..60d1236a73 100644 --- a/eZ/Publish/Core/FieldType/Image/ImageStorage.php +++ b/src/lib/FieldType/Image/ImageStorage.php @@ -4,55 +4,50 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Image; - -use eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Utils\DeprecationWarnerInterface as DeprecationWarner; -use eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator; -use eZ\Publish\Core\IO\FilePathNormalizerInterface; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\MetadataHandler; -use eZ\Publish\SPI\FieldType\GatewayBasedStorage; -use eZ\Publish\SPI\FieldType\StorageGateway; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; +namespace Ibexa\Core\FieldType\Image; + +use Ibexa\Contracts\Core\FieldType\GatewayBasedStorage; +use Ibexa\Contracts\Core\FieldType\StorageGatewayInterface; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Core\Base\Exceptions\ContentFieldValidationException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator; +use Ibexa\Core\IO\FilePathNormalizerInterface; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\MetadataHandler; /** * Converter for Image field type external storage. */ class ImageStorage extends GatewayBasedStorage { - /** @var \eZ\Publish\Core\IO\IOServiceInterface */ + /** @var \Ibexa\Core\IO\IOServiceInterface */ protected $ioService; - /** @var \eZ\Publish\Core\FieldType\Image\PathGenerator */ + /** @var \Ibexa\Core\FieldType\Image\PathGenerator */ protected $pathGenerator; - /** @var \eZ\Publish\Core\IO\MetadataHandler */ + /** @var \Ibexa\Core\IO\MetadataHandler */ protected $imageSizeMetadataHandler; - /** @var \eZ\Publish\Core\Base\Utils\DeprecationWarnerInterface */ - private $deprecationWarner; - - /** @var \eZ\Publish\Core\FieldType\Image\AliasCleanerInterface */ + /** @var \Ibexa\Core\FieldType\Image\AliasCleanerInterface */ protected $aliasCleaner; - /** @var \eZ\Publish\Core\FieldType\Image\ImageStorage\Gateway */ + /** @var \Ibexa\Core\FieldType\Image\ImageStorage\Gateway */ protected $gateway; - /** @var \eZ\Publish\Core\IO\FilePathNormalizerInterface */ + /** @var \Ibexa\Core\IO\FilePathNormalizerInterface */ protected $filePathNormalizer; - /** @var \eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator */ + /** @var \Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator */ protected $fileExtensionBlackListValidator; public function __construct( - StorageGateway $gateway, + StorageGatewayInterface $gateway, IOServiceInterface $ioService, PathGenerator $pathGenerator, MetadataHandler $imageSizeMetadataHandler, - DeprecationWarner $deprecationWarner, AliasCleanerInterface $aliasCleaner, FilePathNormalizerInterface $filePathNormalizer, FileExtensionBlackListValidator $fileExtensionBlackListValidator @@ -61,17 +56,15 @@ public function __construct( $this->ioService = $ioService; $this->pathGenerator = $pathGenerator; $this->imageSizeMetadataHandler = $imageSizeMetadataHandler; - $this->deprecationWarner = $deprecationWarner; $this->aliasCleaner = $aliasCleaner; $this->filePathNormalizer = $filePathNormalizer; $this->fileExtensionBlackListValidator = $fileExtensionBlackListValidator; } /** - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException */ public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context) { @@ -217,3 +210,5 @@ private function buildImageId(VersionInfo $versionInfo, Field $field) ); } } + +class_alias(ImageStorage::class, 'eZ\Publish\Core\FieldType\Image\ImageStorage'); diff --git a/src/lib/FieldType/Image/ImageStorage/Gateway.php b/src/lib/FieldType/Image/ImageStorage/Gateway.php new file mode 100644 index 0000000000..2c8138cb7c --- /dev/null +++ b/src/lib/FieldType/Image/ImageStorage/Gateway.php @@ -0,0 +1,85 @@ + 'fieldId', + 'version' => 'versionNo', + 'language_code' => 'languageCode', + 'path_identification_string' => 'nodePathString', + 'data_string' => 'xml', + ]; + + /** @var \Ibexa\Core\IO\UrlRedecoratorInterface */ + private $redecorator; + + public function __construct(UrlRedecoratorInterface $redecorator, Connection $connection) + { + $this->redecorator = $redecorator; + $this->connection = $connection; + } + + /** + * Return the node path string of $versionInfo. + * + * @return string + */ + public function getNodePathString(VersionInfo $versionInfo) + { + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery + ->select($this->connection->quoteIdentifier('path_identification_string')) + ->from($this->connection->quoteIdentifier('ezcontentobject_tree')) + ->where( + $selectQuery->expr()->andX( + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('contentobject_id'), + ':contentObjectId' + ), + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('contentobject_version'), + ':versionNo' + ), + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('node_id'), + $this->connection->quoteIdentifier('main_node_id') + ) + ) + ) + ->setParameter(':contentObjectId', $versionInfo->contentInfo->id, PDO::PARAM_INT) + ->setParameter(':versionNo', $versionInfo->versionNo, PDO::PARAM_INT) + ; + + $statement = $selectQuery->execute(); + + return $statement->fetchColumn(); + } + + /** + * Store a reference to the image in $path for $fieldId. + * + * @param string $uri File IO uri (not legacy) + * @param int $fieldId + */ + public function storeImageReference($uri, $fieldId) + { + // legacy stores the path to the image without a leading / + $path = $this->redecorator->redecorateFromSource($uri); + + $insertQuery = $this->connection->createQueryBuilder(); + $insertQuery + ->insert($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) + ->values( + [ + $this->connection->quoteIdentifier('contentobject_attribute_id') => ':fieldId', + $this->connection->quoteIdentifier('filepath') => ':path', + ] + ) + ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) + ->setParameter(':path', $path) + ; + + $insertQuery->execute(); + } + + /** + * Return an XML content stored for the given $fieldIds. + * + * @param int $versionNo + * + * @return array + */ + public function getXmlForImages($versionNo, array $fieldIds) + { + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery + ->select( + $this->connection->quoteIdentifier('attr.id'), + $this->connection->quoteIdentifier('attr.data_text') + ) + ->from($this->connection->quoteIdentifier('ezcontentobject_attribute'), 'attr') + ->where( + $selectQuery->expr()->andX( + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('attr.version'), + ':versionNo' + ), + $selectQuery->expr()->in( + $this->connection->quoteIdentifier('attr.id'), + ':fieldIds' + ) + ) + ) + ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) + ->setParameter(':fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY) + ; + + $statement = $selectQuery->execute(); + + $fieldLookup = []; + foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $row) { + $fieldLookup[$row['id']] = $row['data_text']; + } + + return $fieldLookup; + } + + public function getAllVersionsImageXmlForFieldId(int $fieldId): array + { + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery + ->select( + 'field.id', + 'field.version', + 'field.data_text' + ) + ->from($this->connection->quoteIdentifier('ezcontentobject_attribute'), 'field') + ->where( + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('id'), + ':field_id' + ) + ) + ->setParameter(':field_id', $fieldId, PDO::PARAM_INT) + ; + + $statement = $selectQuery->execute(); + + $fieldLookup = []; + foreach ($statement->fetchAllAssociative() as $row) { + $fieldLookup[] = [ + 'version' => $row['version'], + 'data_text' => $row['data_text'], + ]; + } + + return $fieldLookup; + } + + /** + * Remove all references from $fieldId to a path that starts with $path. + * + * @param string $uri File IO uri (not legacy) + * @param int $versionNo + * @param int $fieldId + * + * @throws \Ibexa\Core\IO\Exception\InvalidBinaryFileIdException + */ + public function removeImageReferences($uri, $versionNo, $fieldId): void + { + if (!$this->canRemoveImageReference($uri, $versionNo, $fieldId)) { + return; + } + + $path = $this->redecorator->redecorateFromSource($uri); + + $deleteQuery = $this->connection->createQueryBuilder(); + $deleteQuery + ->delete($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) + ->where( + $deleteQuery->expr()->andX( + $deleteQuery->expr()->eq( + $this->connection->quoteIdentifier('contentobject_attribute_id'), + ':fieldId' + ), + $deleteQuery->expr()->like( + $this->connection->quoteIdentifier('filepath'), + ':likePath' + ) + ) + ) + ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) + ->setParameter(':likePath', $path . '%') + ; + + $deleteQuery->execute(); + } + + /** + * Return the number of recorded references to the given $path. + * + * @param string $uri File IO uri (not legacy) + * + * @return int + */ + public function countImageReferences($uri) + { + $path = $this->redecorator->redecorateFromSource($uri); + + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery + ->select('COUNT(' . $this->connection->quoteIdentifier('id') . ')') + ->from($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) + ->where( + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('filepath'), + ':filepath' + ) + ) + ->setParameter(':filepath', $path) + ; + + $statement = $selectQuery->execute(); + + return (int) $statement->fetchColumn(); + } + + public function isImageReferenced(string $uri): bool + { + $path = $this->redecorator->redecorateFromSource($uri); + + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery + ->select(1) + ->from($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) + ->where( + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('filepath'), + ':likePath' + ) + ) + ->setParameter(':likePath', $path) + ; + + $statement = $selectQuery->execute(); + + return (bool)$statement->fetchOne(); + } + + public function countDistinctImagesData(): int + { + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery + ->select($this->connection->getDatabasePlatform()->getCountExpression('id')) + ->from($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) + ; + + $statement = $selectQuery->execute(); + + return (int) $statement->fetchOne(); + } + + /** + * Check if image $path can be removed when deleting $versionNo and $fieldId. + * + * @param string $path legacy image path (var/storage/images...) + * @param int $versionNo + * @param int $fieldId + */ + protected function canRemoveImageReference($path, $versionNo, $fieldId): bool + { + $selectQuery = $this->connection->createQueryBuilder(); + $expressionBuilder = $selectQuery->expr(); + $selectQuery + ->select('attr.data_text') + ->from($this->connection->quoteIdentifier('ezcontentobject_attribute'), 'attr') + ->innerJoin( + 'attr', + $this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE), + 'img', + $expressionBuilder->eq( + $this->connection->quoteIdentifier('img.contentobject_attribute_id'), + $this->connection->quoteIdentifier('attr.id') + ) + ) + ->where( + $expressionBuilder->eq( + $this->connection->quoteIdentifier('contentobject_attribute_id'), + ':fieldId' + ) + ) + ->andWhere( + $expressionBuilder->neq( + $this->connection->quoteIdentifier('version'), + ':versionNo' + ) + ) + ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) + ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) + ; + + $imageXMLs = $selectQuery->execute()->fetchAll(FetchMode::COLUMN); + foreach ($imageXMLs as $imageXML) { + $storedFilePath = $this->extractFilesFromXml($imageXML)['original'] ?? null; + if ($storedFilePath === $path) { + return false; + } + } + + return true; + } + + /** + * Extract, stored in DocBook XML, file paths. + * + * @param string $xml + * + * @return array|null + */ + public function extractFilesFromXml($xml) + { + if (empty($xml)) { + // Empty image value + return null; + } + + $files = []; + + $dom = new DOMDocument(); + $dom->loadXml($xml); + if ($dom->documentElement->hasAttribute('dirpath')) { + $url = $dom->documentElement->getAttribute('url'); + if (empty($url)) { + return null; + } + + $files['original'] = $this->redecorator->redecorateFromTarget($url); + foreach ($dom->documentElement->childNodes as $childNode) { + /** @var \DOMElement $childNode */ + if ($childNode->nodeName !== 'alias') { + continue; + } + + $files[$childNode->getAttribute('name')] = $this->redecorator->redecorateFromTarget( + $childNode->getAttribute('url') + ); + } + + return $files; + } + + return null; + } + + public function getImagesData(int $offset, int $limit): array + { + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery + ->select( + 'img.contentobject_attribute_id', + 'img.filepath' + ) + ->distinct() + ->from($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE), 'img') + ->setFirstResult($offset) + ->setMaxResults($limit); + + return $selectQuery->execute()->fetchAllAssociative(); + } + + public function updateImageData(int $fieldId, int $versionNo, string $xml): void + { + $updateQuery = $this->connection->createQueryBuilder(); + $expressionBuilder = $updateQuery->expr(); + $updateQuery + ->update( + $this->connection->quoteIdentifier('ezcontentobject_attribute') + ) + ->set( + $this->connection->quoteIdentifier('data_text'), + ':xml' + ) + ->where( + $expressionBuilder->eq( + $this->connection->quoteIdentifier('id'), + ':field_id' + ) + ) + ->andWhere( + $expressionBuilder->eq( + $this->connection->quoteIdentifier('version'), + ':version_no' + ) + ) + ->setParameter(':field_id', $fieldId, ParameterType::INTEGER) + ->setParameter(':version_no', $versionNo, ParameterType::INTEGER) + ->setParameter(':xml', $xml, ParameterType::STRING) + ->execute() + ; + } + + public function updateImagePath(int $fieldId, string $oldPath, string $newPath): void + { + $updateQuery = $this->connection->createQueryBuilder(); + $expressionBuilder = $updateQuery->expr(); + $updateQuery + ->update( + $this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE) + ) + ->set( + $this->connection->quoteIdentifier('filepath'), + ':new_path' + ) + ->where( + $expressionBuilder->eq( + $this->connection->quoteIdentifier('contentobject_attribute_id'), + ':field_id' + ) + ) + ->andWhere( + $expressionBuilder->eq( + $this->connection->quoteIdentifier('filepath'), + ':old_path' + ) + ) + ->setParameter(':field_id', $fieldId, ParameterType::INTEGER) + ->setParameter(':old_path', $oldPath, ParameterType::STRING) + ->setParameter(':new_path', $newPath, ParameterType::STRING) + ->execute() + ; + } + + /** + * @throws \Ibexa\Core\IO\Exception\InvalidBinaryFileIdException + * @throws \Doctrine\DBAL\Driver\Exception + * @throws \Doctrine\DBAL\Exception + */ + public function hasImageReference(string $uri, int $fieldId): bool + { + $path = $this->redecorator->redecorateFromSource($uri); + + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery + ->select(1) + ->from($this->connection->quoteIdentifier(self::IMAGE_FILE_TABLE)) + ->andWhere( + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('filepath'), + ':path' + ) + ) + ->andWhere( + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('contentobject_attribute_id'), + ':field_id' + ) + ) + ->setParameter(':path', $path) + ->setParameter(':field_id', $fieldId) + ; + + $statement = $selectQuery->execute(); + + return (bool)$statement->fetchOne(); + } +} + +class_alias(DoctrineStorage::class, 'eZ\Publish\Core\FieldType\Image\ImageStorage\Gateway\DoctrineStorage'); diff --git a/src/lib/FieldType/Image/ImageThumbnailProxyStrategy.php b/src/lib/FieldType/Image/ImageThumbnailProxyStrategy.php new file mode 100644 index 0000000000..eac301a992 --- /dev/null +++ b/src/lib/FieldType/Image/ImageThumbnailProxyStrategy.php @@ -0,0 +1,59 @@ +imageThumbnailStrategy = $imageThumbnailStrategy; + $this->proxyGenerator = $proxyGenerator; + } + + public function getFieldTypeIdentifier(): string + { + return $this->imageThumbnailStrategy->getFieldTypeIdentifier(); + } + + public function getThumbnail(Field $field, ?VersionInfo $versionInfo = null): ?Thumbnail + { + $initializer = function ( + &$wrappedObject, + LazyLoadingInterface $proxy, + $method, + array $parameters, + &$initializer + ) use ($field, $versionInfo): bool { + $initializer = null; + + $wrappedObject = $this->imageThumbnailStrategy->getThumbnail($field, $versionInfo); + + return true; + }; + + return $this->proxyGenerator->createProxy(Thumbnail::class, $initializer); + } +} + +class_alias(ImageThumbnailProxyStrategy::class, 'eZ\Publish\Core\FieldType\Image\ImageThumbnailProxyStrategy'); diff --git a/eZ/Publish/Core/FieldType/Image/ImageThumbnailStrategy.php b/src/lib/FieldType/Image/ImageThumbnailStrategy.php similarity index 79% rename from eZ/Publish/Core/FieldType/Image/ImageThumbnailStrategy.php rename to src/lib/FieldType/Image/ImageThumbnailStrategy.php index 19190d5baa..4c4b63eb8d 100644 --- a/eZ/Publish/Core/FieldType/Image/ImageThumbnailStrategy.php +++ b/src/lib/FieldType/Image/ImageThumbnailStrategy.php @@ -6,16 +6,16 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\FieldType\Image; +namespace Ibexa\Core\FieldType\Image; use Exception; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\Core\MVC\Exception\SourceImageNotFoundException; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field\FieldTypeBasedThumbnailStrategy; -use eZ\Publish\SPI\Variation\VariationHandler; +use Ibexa\Contracts\Core\Repository\Strategy\ContentThumbnail\Field\FieldTypeBasedThumbnailStrategy; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\Thumbnail; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Variation\VariationHandler; +use Ibexa\Core\MVC\Exception\SourceImageNotFoundException; +use Ibexa\Core\Repository\Values\Content\VersionInfo; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use Psr\Log\LoggerInterface; @@ -28,7 +28,7 @@ class ImageThumbnailStrategy implements FieldTypeBasedThumbnailStrategy, LoggerA /** @var string */ private $fieldTypeIdentifier; - /** @var \eZ\Publish\SPI\Variation\VariationHandler */ + /** @var \Ibexa\Contracts\Core\Variation\VariationHandler */ private $variationHandler; /** @var string */ @@ -54,7 +54,7 @@ public function getFieldTypeIdentifier(): string public function getThumbnail(Field $field, ?APIVersionInfo $versionInfo = null): ?Thumbnail { try { - /** @var \eZ\Publish\SPI\Variation\Values\ImageVariation $variation */ + /** @var \Ibexa\Contracts\Core\Variation\Values\ImageVariation $variation */ $variation = $this->variationHandler->getVariation( $field, $versionInfo ?? new VersionInfo(), @@ -107,3 +107,5 @@ private function generateContentDetailsMessage(?APIVersionInfo $versionInfo): st : ''; } } + +class_alias(ImageThumbnailStrategy::class, 'eZ\Publish\Core\FieldType\Image\ImageThumbnailStrategy'); diff --git a/eZ/Publish/Core/FieldType/Image/NullAliasCleaner.php b/src/lib/FieldType/Image/NullAliasCleaner.php similarity index 83% rename from eZ/Publish/Core/FieldType/Image/NullAliasCleaner.php rename to src/lib/FieldType/Image/NullAliasCleaner.php index 871e2c8f32..d8cfc90f85 100644 --- a/eZ/Publish/Core/FieldType/Image/NullAliasCleaner.php +++ b/src/lib/FieldType/Image/NullAliasCleaner.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\FieldType\Image; +namespace Ibexa\Core\FieldType\Image; /** * Default, IO-independent, implementation of image variation (alias) cleaner. @@ -23,3 +23,5 @@ public function removeAliases($originalPath): void // Nothing to do } } + +class_alias(NullAliasCleaner::class, 'eZ\Publish\Core\FieldType\Image\NullAliasCleaner'); diff --git a/src/lib/FieldType/Image/Orientation.php b/src/lib/FieldType/Image/Orientation.php new file mode 100644 index 0000000000..f77a60def3 --- /dev/null +++ b/src/lib/FieldType/Image/Orientation.php @@ -0,0 +1,16 @@ +getDirectoryStructure($fieldId), + $fieldId, + $versionNo, + $languageCode + ); + } + + /** + * Computes a 4 levels directory structure from $id. + * + * @param string $id + * + * @return string + */ + private function getDirectoryStructure($id) + { + // our base string is the last 4 digits, defaulting to 0, reversed + $idString = strrev( + substr(str_pad($id, 4, 0, STR_PAD_LEFT), -4) + ); + + return trim( + chunk_split($idString, 1, '/'), + '/' + ); + } +} + +class_alias(LegacyPathGenerator::class, 'eZ\Publish\Core\FieldType\Image\PathGenerator\LegacyPathGenerator'); diff --git a/src/lib/FieldType/Image/SearchField.php b/src/lib/FieldType/Image/SearchField.php new file mode 100644 index 0000000000..cce5068a1e --- /dev/null +++ b/src/lib/FieldType/Image/SearchField.php @@ -0,0 +1,127 @@ +value->data['width']) && $field->value->data['width'] !== null + ? (int)$field->value->data['width'] + : null; + + $height = isset($field->value->data['height']) && $field->value->data['height'] !== null + ? (int)$field->value->data['height'] + : null; + + return [ + new Search\Field( + 'filename', + $field->value->data['fileName'] ?? null, + new Search\FieldType\StringField() + ), + new Search\Field( + 'alternative_text', + $field->value->data['alternativeText'] ?? null, + new Search\FieldType\StringField() + ), + new Search\Field( + 'file_size', + $field->value->data['fileSize'] ?? null, + new Search\FieldType\IntegerField() + ), + new Search\Field( + 'mime_type', + $field->value->data['mime'] ?? null, + new Search\FieldType\StringField() + ), + new Search\Field( + 'width', + $width, + new Search\FieldType\IntegerField() + ), + new Search\Field( + 'height', + $height, + new Search\FieldType\IntegerField() + ), + new Search\Field( + 'orientation', + $this->getOrientation($width, $height), + new Search\FieldType\StringField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'filename' => new Search\FieldType\StringField(), + 'alternative_text' => new Search\FieldType\StringField(), + 'file_size' => new Search\FieldType\IntegerField(), + 'mime_type' => new Search\FieldType\StringField(), + 'width' => new Search\FieldType\IntegerField(), + 'height' => new Search\FieldType\IntegerField(), + 'orientation' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'filename'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } + + private function getOrientation( + ?int $width, + ?int $height + ): ?string { + if (null === $width || null === $height) { + return null; + } + + if ($width === $height) { + return Orientation::SQUARE; + } + + return $width > $height + ? Orientation::LANDSCAPE + : Orientation::PORTRAIT; + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\Image\SearchField'); diff --git a/src/lib/FieldType/Image/Type.php b/src/lib/FieldType/Image/Type.php new file mode 100644 index 0000000000..6bf4531614 --- /dev/null +++ b/src/lib/FieldType/Image/Type.php @@ -0,0 +1,478 @@ + [ + 'maxFileSize' => [ + 'type' => 'numeric', + 'default' => null, + ], + ], + 'AlternativeTextValidator' => [ + 'required' => [ + 'type' => 'bool', + 'default' => false, + ], + ], + ]; + + /** + * @var array{ + * mimeTypes: array{ + * type: string, + * default: array{}, + * } + * } + */ + protected $settingsSchema = [ + 'mimeTypes' => [ + 'type' => 'choice', + 'default' => [], + ], + ]; + + /** @var \Ibexa\Core\FieldType\Validator[] */ + private $validators; + + /** @var array */ + private array $mimeTypes; + + /** + * @param array<\Ibexa\Core\FieldType\Validator> $validators + * @param array $mimeTypes + */ + public function __construct( + array $validators, + array $mimeTypes = [] + ) { + $this->validators = $validators; + $this->mimeTypes = $mimeTypes; + } + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezimage'; + } + + /** + * @param \Ibexa\Core\FieldType\Image\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + return $value->alternativeText ?? (string)$value->fileName; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Image\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param string|array|\Ibexa\Core\FieldType\Image\Value $inputValue + * + * @return \Ibexa\Core\FieldType\Image\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_string($inputValue)) { + $inputValue = Value::fromString($inputValue); + } + + if (is_array($inputValue)) { + if (isset($inputValue['inputUri']) && file_exists($inputValue['inputUri'])) { + $inputValue['fileSize'] = filesize($inputValue['inputUri']); + if (!isset($inputValue['fileName'])) { + $inputValue['fileName'] = basename($inputValue['inputUri']); + } + } + + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Image\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (isset($value->inputUri) && !is_string($value->inputUri)) { + throw new InvalidArgumentType('$value->inputUri', 'string', $value->inputUri); + } + + if (isset($value->id) && !is_string($value->id)) { + throw new InvalidArgumentType('$value->id', 'string', $value->id); + } + + // Required parameter $fileName + if (!isset($value->fileName) || !is_string($value->fileName)) { + throw new InvalidArgumentType('$value->fileName', 'string', $value->fileName); + } + + // Optional parameter $alternativeText + if (isset($value->alternativeText) && !is_string($value->alternativeText)) { + throw new InvalidArgumentType( + '$value->alternativeText', + 'string', + $value->alternativeText + ); + } + + if (isset($value->fileSize) && ((!is_int($value->fileSize) && !is_float($value->fileSize)) || $value->fileSize < 0)) { + throw new InvalidArgumentType( + '$value->fileSize', + 'numeric', + $value->fileSize + ); + } + + if (isset($value->additionalData) && !\is_array($value->additionalData)) { + throw new InvalidArgumentType('$value->additionalData', 'array', $value->additionalData); + } + } + + /** + * Validates a field based on the validators in the field definition. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field + * @param \Ibexa\Core\FieldType\Image\Value $fieldValue The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) + { + $errors = []; + + if ($this->isEmptyValue($fieldValue)) { + return $errors; + } + + foreach ($this->validators as $externalValidator) { + if (!$externalValidator->validate($fieldValue, $fieldDefinition)) { + $errors = array_merge($errors, $externalValidator->getMessage()); + } + } + + foreach ((array)$fieldDefinition->getValidatorConfiguration() as $validatorIdentifier => $parameters) { + switch ($validatorIdentifier) { + case 'FileSizeValidator': + if (empty($parameters['maxFileSize'])) { + // No file size limit + break; + } + + // Database stores maxFileSize in MB + if (($parameters['maxFileSize'] * 1024 * 1024) < $fieldValue->fileSize) { + $errors[] = new ValidationError( + 'The file size cannot exceed %size% megabyte.', + 'The file size cannot exceed %size% megabytes.', + [ + '%size%' => $parameters['maxFileSize'], + ], + 'fileSize' + ); + } + break; + case 'AlternativeTextValidator': + if ($parameters['required'] && $fieldValue->isAlternativeTextEmpty()) { + $errors[] = new ValidationError( + 'Alternative text is required.', + null, + [], + 'alternativeText' + ); + } + break; + } + } + + return $errors; + } + + public function validateFieldSettings($fieldSettings): array + { + $validationErrors = []; + + foreach ($fieldSettings as $name => $value) { + if (!isset($this->settingsSchema[$name])) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is unknown", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + + if ( + $name === 'mimeTypes' + && !empty($this->mimeTypes) + && !empty(array_diff($value, $this->mimeTypes)) + ) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' contains unsupported mime types", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + } + + return $validationErrors; + } + + /** + * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $validatorConfiguration + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateValidatorConfiguration($validatorConfiguration) + { + $validationErrors = []; + + foreach ($validatorConfiguration as $validatorIdentifier => $parameters) { + switch ($validatorIdentifier) { + case 'FileSizeValidator': + if (!array_key_exists('maxFileSize', $parameters)) { + $validationErrors[] = new ValidationError( + 'Validator %validator% expects parameter %parameter% to be set.', + null, + [ + '%validator%' => $validatorIdentifier, + '%parameter%' => 'maxFileSize', + ], + "[$validatorIdentifier]" + ); + break; + } + if (!is_numeric($parameters['maxFileSize']) && $parameters['maxFileSize'] !== null) { + $validationErrors[] = new ValidationError( + 'Validator %validator% expects parameter %parameter% to be of %type%.', + null, + [ + '%validator%' => $validatorIdentifier, + '%parameter%' => 'maxFileSize', + '%type%' => 'numeric', + ], + "[$validatorIdentifier][maxFileSize]" + ); + } + break; + case 'AlternativeTextValidator': + if (!array_key_exists('required', $parameters)) { + $validationErrors[] = new ValidationError( + 'Validator %validator% expects parameter %parameter% to be set.', + null, + [ + '%validator%' => $validatorIdentifier, + '%parameter%' => 'required', + ], + "[$validatorIdentifier]" + ); + } + break; + default: + $validationErrors[] = new ValidationError( + "Validator '%validator%' is unknown", + null, + [ + '%validator%' => $validatorIdentifier, + ], + "[$validatorIdentifier]" + ); + } + } + + return $validationErrors; + } + + /** + * {@inheritdoc} + */ + protected function getSortInfo(BaseValue $value) + { + return false; + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\Image\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + return new Value($hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Image\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + return [ + 'id' => $value->id, + 'path' => $value->inputUri ?: $value->id, + 'alternativeText' => $value->alternativeText, + 'fileName' => $value->fileName, + 'fileSize' => $value->fileSize, + 'imageId' => $value->imageId, + 'uri' => $value->uri, + 'inputUri' => $value->inputUri, + 'width' => $value->width, + 'height' => $value->height, + 'additionalData' => $value->additionalData, + 'mime' => $value->mime, + ]; + } + + /** + * Converts a $value to a persistence value. + * + * @param \Ibexa\Core\FieldType\Image\Value $value + * + * @return \Ibexa\Contracts\Core\Persistence\Content\FieldValue + */ + public function toPersistenceValue(SPIValue $value) + { + // Store original data as external (to indicate they need to be stored) + return new FieldValue( + [ + 'data' => null, + 'externalData' => $this->toHash($value), + 'sortKey' => $this->getSortInfo($value), + ] + ); + } + + /** + * Converts a persistence $fieldValue to a Value. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + * + * @return \Ibexa\Core\FieldType\Image\Value + */ + public function fromPersistenceValue(FieldValue $fieldValue) + { + if ($fieldValue->data === null) { + return $this->getEmptyValue(); + } + + // Restored data comes in $data, since it has already been processed + // there might be more data in the persistence value than needed here + $result = $this->fromHash( + [ + 'id' => (isset($fieldValue->data['id']) + ? $fieldValue->data['id'] + : null), + 'alternativeText' => (isset($fieldValue->data['alternativeText']) + ? $fieldValue->data['alternativeText'] + : null), + 'fileName' => (isset($fieldValue->data['fileName']) + ? $fieldValue->data['fileName'] + : null), + 'fileSize' => (isset($fieldValue->data['fileSize']) + ? $fieldValue->data['fileSize'] + : null), + 'uri' => (isset($fieldValue->data['uri']) + ? $fieldValue->data['uri'] + : null), + 'imageId' => (isset($fieldValue->data['imageId']) + ? $fieldValue->data['imageId'] + : null), + 'width' => (isset($fieldValue->data['width']) + ? $fieldValue->data['width'] + : null), + 'height' => (isset($fieldValue->data['height']) + ? $fieldValue->data['height'] + : null), + 'additionalData' => $fieldValue->data['additionalData'] ?? [], + 'mime' => $fieldValue->data['mime'] ?? null, + ] + ); + + return $result; + } + + public function valuesEqual(SPIValue $value1, SPIValue $value2): bool + { + $hashValue1 = $this->toHash($value1); + $hashValue2 = $this->toHash($value2); + + unset($hashValue1['imageId'], $hashValue2['imageId']); + + return $hashValue1 === $hashValue2; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezimage.name', 'ibexa_fieldtypes')->setDesc('Image'), + ]; + } + + public function isSearchable(): bool + { + return true; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Image\Type'); diff --git a/src/lib/FieldType/Image/Value.php b/src/lib/FieldType/Image/Value.php new file mode 100644 index 0000000000..f8cf5645df --- /dev/null +++ b/src/lib/FieldType/Image/Value.php @@ -0,0 +1,187 @@ + $value) { + try { + $this->$key = $value; + } catch (PropertyNotFoundException $e) { + throw new InvalidArgumentType( + sprintf('Image\Value::$%s', $key), + 'Existing property', + $value + ); + } + } + } + + public function isAlternativeTextEmpty(): bool + { + return $this->alternativeText === null || trim($this->alternativeText) === ''; + } + + /** + * Creates a value only from a file path. + * + * @param string $path + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType + * + * @return Value + * + * @deprecated Starting with 5.3.3, handled by Image\Type::acceptValue() + */ + public static function fromString($path) + { + if (!file_exists($path)) { + throw new InvalidArgumentType( + '$path', + 'existing file', + $path + ); + } + + return new static( + [ + 'inputUri' => $path, + 'fileName' => basename($path), + 'fileSize' => filesize($path), + ] + ); + } + + /** + * Returns the image file size in byte. + * + * @return int + */ + public function getFileSize() + { + return $this->fileSize; + } + + public function __toString() + { + return (string)$this->fileName; + } + + public function __get($propertyName) + { + if ($propertyName === 'path') { + return $this->inputUri ?: $this->id; + } + + throw new PropertyNotFoundException($propertyName, static::class); + } + + public function __set($propertyName, $propertyValue) + { + if ($propertyName === 'path') { + $this->inputUri = $propertyValue; + + return; + } + + throw new PropertyNotFoundException($propertyName, static::class); + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\Image\Value'); diff --git a/src/lib/FieldType/ImageAsset/AssetMapper.php b/src/lib/FieldType/ImageAsset/AssetMapper.php new file mode 100644 index 0000000000..179409f217 --- /dev/null +++ b/src/lib/FieldType/ImageAsset/AssetMapper.php @@ -0,0 +1,190 @@ +contentService = $contentService; + $this->locationService = $locationService; + $this->contentTypeService = $contentTypeService; + $this->configResolver = $configResolver; + } + + /** + * Creates an Image Asset. + * + * @param string $name + * @param \Ibexa\Core\FieldType\Image\Value $image + * @param string $languageCode + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function createAsset(string $name, ImageValue $image, string $languageCode): Content + { + $mappings = $this->getMappings(); + + $contentType = $this->contentTypeService->loadContentTypeByIdentifier( + $mappings['content_type_identifier'] + ); + + $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, $languageCode); + $contentCreateStruct->setField($mappings['name_field_identifier'], $name); + $contentCreateStruct->setField($mappings['content_field_identifier'], $image); + + $contentDraft = $this->contentService->createContent($contentCreateStruct, [ + $this->locationService->newLocationCreateStruct($mappings['parent_location_id']), + ]); + + return $this->contentService->publishVersion($contentDraft->versionInfo); + } + + /** + * Returns field which is used to store the Image Asset value from specified content. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function getAssetField(Content $content): Field + { + if (!$this->isAsset($content)) { + throw new InvalidArgumentException('contentId', "Content {$content->id} is not an image asset."); + } + + return $content->getField($this->getContentFieldIdentifier()); + } + + /** + * Returns definition of the field which is used to store value of the Image Asset. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition + */ + public function getAssetFieldDefinition(): FieldDefinition + { + $mappings = $this->getMappings(); + + $contentType = $this->contentTypeService->loadContentTypeByIdentifier( + $mappings['content_type_identifier'] + ); + + return $contentType->getFieldDefinition( + $mappings['content_field_identifier'] + ); + } + + /** + * Returns field value of the Image Asset from specified content. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @return \Ibexa\Core\FieldType\Image\Value + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function getAssetValue(Content $content): ImageValue + { + if (!$this->isAsset($content)) { + throw new InvalidArgumentException('contentId', "Content {$content->id} is not an image asset."); + } + + return $content->getFieldValue($this->getContentFieldIdentifier()); + } + + /** + * Returns TRUE if content is an Image Asset. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @return bool + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function isAsset(Content $content): bool + { + if ($this->contentTypeId === null) { + $contentType = $this->contentTypeService->loadContentTypeByIdentifier( + $this->getContentTypeIdentifier() + ); + + $this->contentTypeId = $contentType->id; + } + + return $content->contentInfo->contentTypeId === $this->contentTypeId; + } + + /** + * Return identifier of the content type used as Assets. + */ + public function getContentTypeIdentifier(): string + { + return $this->getMappings()['content_type_identifier']; + } + + /** + * Return identifier of the field used to store Image Asset value. + * + * @return string + */ + public function getContentFieldIdentifier(): string + { + return $this->getMappings()['content_field_identifier']; + } + + /** + * Return ID of the base location for the Image Assets. + * + * @return int + */ + public function getParentLocationId(): int + { + return $this->getMappings()['parent_location_id']; + } + + protected function getMappings(): array + { + return $this->configResolver->getParameter('fieldtypes.ezimageasset.mappings'); + } +} + +class_alias(AssetMapper::class, 'eZ\Publish\Core\FieldType\ImageAsset\AssetMapper'); diff --git a/src/lib/FieldType/ImageAsset/ImageAssetThumbnailStrategy.php b/src/lib/FieldType/ImageAsset/ImageAssetThumbnailStrategy.php new file mode 100644 index 0000000000..8a18935059 --- /dev/null +++ b/src/lib/FieldType/ImageAsset/ImageAssetThumbnailStrategy.php @@ -0,0 +1,65 @@ +fieldTypeIdentifier = $fieldTypeIdentifier; + $this->contentService = $contentService; + $this->thumbnailStrategy = $thumbnailStrategy; + } + + public function getFieldTypeIdentifier(): string + { + return $this->fieldTypeIdentifier; + } + + public function getThumbnail(Field $field, ?VersionInfo $versionInfo = null): ?Thumbnail + { + try { + $content = $this->contentService->loadContent( + (int) $field->value->destinationContentId, + null, + $versionInfo ? $versionInfo->versionNo : null + ); + } catch (NotFoundException $e) { + return null; + } + + return $this->thumbnailStrategy->getThumbnail( + $content->getContentType(), + $content->getFields(), + $content->versionInfo + ); + } +} + +class_alias(ImageAssetThumbnailStrategy::class, 'eZ\Publish\Core\FieldType\ImageAsset\ImageAssetThumbnailStrategy'); diff --git a/src/lib/FieldType/ImageAsset/SearchField.php b/src/lib/FieldType/ImageAsset/SearchField.php new file mode 100644 index 0000000000..d09771717c --- /dev/null +++ b/src/lib/FieldType/ImageAsset/SearchField.php @@ -0,0 +1,68 @@ +value->data), + new Search\FieldType\StringField() + ), + ]; + } + + public function getIndexDefinition(): array + { + return [ + 'value' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField(): string + { + return 'value'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField(): string + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\ImageAsset\SearchField'); diff --git a/src/lib/FieldType/ImageAsset/Type.php b/src/lib/FieldType/ImageAsset/Type.php new file mode 100644 index 0000000000..ac77946f59 --- /dev/null +++ b/src/lib/FieldType/ImageAsset/Type.php @@ -0,0 +1,331 @@ +contentService = $contentService; + $this->assetMapper = $mapper; + $this->handler = $handler; + } + + /** + * Validates a field based on the validators in the field definition. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field + * @param \Ibexa\Core\FieldType\ImageAsset\Value $fieldValue The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue): array + { + $errors = []; + + if ($this->isEmptyValue($fieldValue)) { + return $errors; + } + + $content = $this->contentService->loadContent( + (int)$fieldValue->destinationContentId + ); + + if (!$this->assetMapper->isAsset($content)) { + return [ + new ValidationError( + 'Content %type% is not a valid asset target', + null, + [ + '%type%' => $content->getContentType()->identifier, + ], + 'destinationContentId' + ), + ]; + } + + $validationError = $this->validateMaxFileSize($content); + if (null !== $validationError) { + $errors[] = $validationError; + } + + return $errors; + } + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier(): string + { + return self::FIELD_TYPE_IDENTIFIER; + } + + /** + * @param \Ibexa\Core\FieldType\ImageAsset\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + if (empty($value->destinationContentId)) { + return ''; + } + + try { + $contentInfo = $this->handler->loadContentInfo($value->destinationContentId); + $versionInfo = $this->handler->loadVersionInfo($value->destinationContentId, $contentInfo->currentVersionNo); + } catch (NotFoundException $e) { + return ''; + } + + return $versionInfo->names[$languageCode] ?? $versionInfo->names[$contentInfo->mainLanguageCode]; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\ImageAsset\Value + */ + public function getEmptyValue(): Value + { + return new Value(); + } + + /** + * Returns if the given $value is considered empty by the field type. + * + * @param mixed $value + * + * @return bool + */ + public function isEmptyValue(SPIValue $value): bool + { + return null === $value->destinationContentId; + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param int|string|\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo|\Ibexa\Core\FieldType\Relation\Value $inputValue + * + * @return \Ibexa\Core\FieldType\ImageAsset\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if ($inputValue instanceof ContentInfo) { + $inputValue = new Value($inputValue->id); + } elseif (is_int($inputValue) || is_string($inputValue)) { + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\ImageAsset\Value $value + */ + protected function checkValueStructure(BaseValue $value): void + { + if (!is_int($value->destinationContentId) && !is_string($value->destinationContentId)) { + throw new InvalidArgumentType( + '$value->destinationContentId', + 'string|int', + $value->destinationContentId + ); + } + + if ($value->alternativeText !== null && !is_string($value->alternativeText)) { + throw new InvalidArgumentType( + '$value->alternativeText', + 'string|null', + $value->alternativeText + ); + } + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * For this FieldType, the related object's name is returned. + * + * @param \Ibexa\Core\FieldType\Relation\Value $value + * + * @return bool + */ + protected function getSortInfo(BaseValue $value): bool + { + return false; + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\ImageAsset\Value $value + */ + public function fromHash($hash): Value + { + if (!$hash) { + return new Value(); + } + + $destinationContentId = $hash['destinationContentId']; + if ($destinationContentId !== null) { + $destinationContentId = (int)$destinationContentId; + } + + return new Value($destinationContentId, $hash['alternativeText']); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\ImageAsset\Value $value + * + * @return array + */ + public function toHash(SPIValue $value): array + { + $destinationContentId = null; + if ($value->destinationContentId !== null) { + $destinationContentId = (int)$value->destinationContentId; + } + + return [ + 'destinationContentId' => $destinationContentId, + 'alternativeText' => $value->alternativeText, + ]; + } + + /** + * Returns relation data extracted from value. + * + * Not intended for \Ibexa\Contracts\Core\Repository\Values\Content\Relation::COMMON type relations, + * there is an API for handling those. + * + * @param \Ibexa\Core\FieldType\ImageAsset\Value $fieldValue + * + * @return array Hash with relation type as key and array of destination content ids as value. + * + * Example: + * + * array( + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::LINK => array( + * "contentIds" => array( 12, 13, 14 ), + * "locationIds" => array( 24 ) + * ), + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::EMBED => array( + * "contentIds" => array( 12 ), + * "locationIds" => array( 24, 45 ) + * ), + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::FIELD => array( 12 ) + * ) + * + */ + public function getRelations(SPIValue $fieldValue): array + { + $relations = []; + if ($fieldValue->destinationContentId !== null) { + $relations[Relation::ASSET] = [$fieldValue->destinationContentId]; + } + + return $relations; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable(): bool + { + return true; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezimageasset.name', 'ibexa_fieldtypes')->setDesc('Image Asset'), + ]; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + private function validateMaxFileSize(Content $content): ?ValidationError + { + $fileSize = $this->assetMapper + ->getAssetValue($content) + ->getFileSize(); + + $assetValidatorConfiguration = $this->assetMapper + ->getAssetFieldDefinition() + ->getValidatorConfiguration(); + + $maxFileSizeMB = $assetValidatorConfiguration['FileSizeValidator']['maxFileSize']; + $maxFileSizeKB = $maxFileSizeMB * 1024 * 1024; + + if ( + $maxFileSizeKB > 0 + && $fileSize > $maxFileSizeKB + ) { + return new ValidationError( + 'The file size cannot exceed %size% megabyte.', + 'The file size cannot exceed %size% megabytes.', + [ + '%size%' => $maxFileSizeMB, + ], + 'fileSize' + ); + } + + return null; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\ImageAsset\Type'); diff --git a/src/lib/FieldType/ImageAsset/Value.php b/src/lib/FieldType/ImageAsset/Value.php new file mode 100644 index 0000000000..f02c1abde5 --- /dev/null +++ b/src/lib/FieldType/ImageAsset/Value.php @@ -0,0 +1,50 @@ + $destinationContentId, + 'alternativeText' => $alternativeText, + ]); + } + + /** + * {@inheritdoc} + */ + public function __toString() + { + return (string) $this->destinationContentId; + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\ImageAsset\Value'); diff --git a/src/lib/FieldType/Integer/SearchField.php b/src/lib/FieldType/Integer/SearchField.php new file mode 100644 index 0000000000..ae96902227 --- /dev/null +++ b/src/lib/FieldType/Integer/SearchField.php @@ -0,0 +1,71 @@ +value->data, + new Search\FieldType\IntegerField() + ), + new Search\Field( + 'fulltext', + $field->value->data, + new Search\FieldType\FullTextField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value' => new Search\FieldType\IntegerField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\Integer\SearchField'); diff --git a/src/lib/FieldType/Integer/Type.php b/src/lib/FieldType/Integer/Type.php new file mode 100644 index 0000000000..495b787944 --- /dev/null +++ b/src/lib/FieldType/Integer/Type.php @@ -0,0 +1,282 @@ + [ + 'minIntegerValue' => [ + 'type' => 'int', + 'default' => null, + ], + 'maxIntegerValue' => [ + 'type' => 'int', + 'default' => null, + ], + ], + ]; + + /** + * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $validatorConfiguration + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateValidatorConfiguration($validatorConfiguration) + { + $validationErrors = []; + + foreach ($validatorConfiguration as $validatorIdentifier => $constraints) { + if ($validatorIdentifier !== 'IntegerValueValidator') { + $validationErrors[] = new ValidationError( + "Validator '%validator%' is unknown", + null, + [ + '%validator%' => $validatorIdentifier, + ], + "[$validatorIdentifier]" + ); + + continue; + } + foreach ($constraints as $name => $value) { + switch ($name) { + case 'minIntegerValue': + case 'maxIntegerValue': + if ($value !== null && !is_int($value)) { + $validationErrors[] = new ValidationError( + "Validator parameter '%parameter%' value must be of integer type", + null, + [ + '%parameter%' => $name, + ], + "[$validatorIdentifier][$name]" + ); + } + break; + default: + $validationErrors[] = new ValidationError( + "Validator parameter '%parameter%' is unknown", + null, + [ + '%parameter%' => $name, + ], + "[$validatorIdentifier][$name]" + ); + } + } + } + + return $validationErrors; + } + + /** + * Validates a field based on the validators in the field definition. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field + * @param \Ibexa\Core\FieldType\Integer\Value $fieldValue The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) + { + $validationErrors = []; + + if ($this->isEmptyValue($fieldValue)) { + return $validationErrors; + } + + $validatorConfiguration = $fieldDefinition->getValidatorConfiguration(); + $constraints = isset($validatorConfiguration['IntegerValueValidator']) ? + $validatorConfiguration['IntegerValueValidator'] : + []; + + $validationErrors = []; + + // 0 and False are unlimited value for maxIntegerValue + if (isset($constraints['maxIntegerValue']) && + $constraints['maxIntegerValue'] !== 0 && + $constraints['maxIntegerValue'] !== false && + $fieldValue->value > $constraints['maxIntegerValue'] + ) { + $validationErrors[] = new ValidationError( + 'The value can not be higher than %size%.', + null, + [ + '%size%' => $constraints['maxIntegerValue'], + ], + 'value' + ); + } + + if (isset($constraints['minIntegerValue']) && + $constraints['minIntegerValue'] !== false && $fieldValue->value < $constraints['minIntegerValue']) { + $validationErrors[] = new ValidationError( + 'The value can not be lower than %size%.', + null, + [ + '%size%' => $constraints['minIntegerValue'], + ], + 'value' + ); + } + + return $validationErrors; + } + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezinteger'; + } + + /** + * @param \Ibexa\Core\FieldType\Integer\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + return (string)$value; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Integer\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Returns if the given $value is considered empty by the field type. + * + * @param mixed $value + * + * @return bool + */ + public function isEmptyValue(SPIValue $value) + { + return $value->value === null; + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param int|\Ibexa\Core\FieldType\Integer\Value $inputValue + * + * @return \Ibexa\Core\FieldType\Integer\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_int($inputValue)) { + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Integer\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!is_int($value->value)) { + throw new InvalidArgumentType( + '$value->value', + 'integer', + $value->value + ); + } + } + + /** + * {@inheritdoc} + */ + protected function getSortInfo(BaseValue $value) + { + return $value->value; + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\Integer\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + return new Value($hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Integer\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + return $value->value; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezinteger.name', 'ibexa_fieldtypes')->setDesc('Integer'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Integer\Type'); diff --git a/src/lib/FieldType/Integer/Value.php b/src/lib/FieldType/Integer/Value.php new file mode 100644 index 0000000000..5d04f25e7b --- /dev/null +++ b/src/lib/FieldType/Integer/Value.php @@ -0,0 +1,39 @@ +value = $value; + } + + public function __toString() + { + return (string)$this->value; + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\Integer\Value'); diff --git a/src/lib/FieldType/Keyword/KeywordStorage.php b/src/lib/FieldType/Keyword/KeywordStorage.php new file mode 100644 index 0000000000..2a9418fcd0 --- /dev/null +++ b/src/lib/FieldType/Keyword/KeywordStorage.php @@ -0,0 +1,86 @@ +value->externalData. $field->value->data is simply empty, because no + * internal data is store. + */ +class KeywordStorage extends GatewayBasedStorage +{ + /** @var \Ibexa\Core\FieldType\Keyword\KeywordStorage\Gateway */ + protected $gateway; + + /** + * @see \Ibexa\Contracts\Core\FieldType\FieldStorage + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param array $context + * + * @return mixed + */ + public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context) + { + $contentTypeId = $this->gateway->getContentTypeId($field); + + return $this->gateway->storeFieldData($field, $contentTypeId); + } + + public function getFieldData(VersionInfo $versionInfo, Field $field, array $context) + { + // @todo: This should already retrieve the ContentType ID + return $this->gateway->getFieldData($field); + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param array $fieldIds + * @param array $context + * + * @return bool + */ + public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context) + { + foreach ($fieldIds as $fieldId) { + $this->gateway->deleteFieldData($fieldId, $versionInfo->versionNo); + } + + return true; + } + + /** + * Checks if field type has external data to deal with. + * + * @return bool + */ + public function hasFieldData() + { + return true; + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param array $context + * + * @return \Ibexa\Contracts\Core\Search\Field[]|null + */ + public function getIndexData(VersionInfo $versionInfo, Field $field, array $context) + { + return null; + } +} + +class_alias(KeywordStorage::class, 'eZ\Publish\Core\FieldType\Keyword\KeywordStorage'); diff --git a/src/lib/FieldType/Keyword/KeywordStorage/Gateway.php b/src/lib/FieldType/Keyword/KeywordStorage/Gateway.php new file mode 100644 index 0000000000..4685d94453 --- /dev/null +++ b/src/lib/FieldType/Keyword/KeywordStorage/Gateway.php @@ -0,0 +1,44 @@ +value->externalData. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + */ + abstract public function getFieldData(Field $field); + + /** + * Retrieve the ContentType ID for the given $field. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * + * @return mixed + */ + abstract public function getContentTypeId(Field $field); + + /** + * @see \Ibexa\Contracts\Core\FieldType\FieldStorage::deleteFieldData() + */ + abstract public function deleteFieldData($fieldId, $versionNo); +} + +class_alias(Gateway::class, 'eZ\Publish\Core\FieldType\Keyword\KeywordStorage\Gateway'); diff --git a/src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php b/src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php new file mode 100644 index 0000000000..797a5090ce --- /dev/null +++ b/src/lib/FieldType/Keyword/KeywordStorage/Gateway/DoctrineStorage.php @@ -0,0 +1,369 @@ +connection = $connection; + } + + /** + * Stores the keyword list from $field->value->externalData. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Field + * @param int $contentTypeId + */ + public function storeFieldData(Field $field, $contentTypeId) + { + if (empty($field->value->externalData) && !empty($field->id)) { + $this->deleteFieldData($field->id, $field->versionNo); + + return; + } + + $existingKeywordMap = $this->getExistingKeywords( + $field->value->externalData, + $contentTypeId + ); + + $this->deleteOldKeywordAssignments($field->id, $field->versionNo); + + $this->assignKeywords( + $field->id, + $this->insertKeywords( + array_diff_key( + array_fill_keys($field->value->externalData, true), + $existingKeywordMap + ), + $contentTypeId + ) + $existingKeywordMap, + $field->versionNo + ); + + $this->deleteOrphanedKeywords(); + } + + /** + * Sets the list of assigned keywords into $field->value->externalData. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + */ + public function getFieldData(Field $field) + { + $field->value->externalData = $this->getAssignedKeywords($field->id, $field->versionNo); + } + + /** + * Retrieve the ContentType ID for the given $field. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * + * @return int + */ + public function getContentTypeId(Field $field) + { + return $this->loadContentTypeId($field->fieldDefinitionId); + } + + /** + * Deletes keyword data for the given $fieldId. + * + * @param int $fieldId + * @param int $versionNo + */ + public function deleteFieldData($fieldId, $versionNo) + { + $this->deleteOldKeywordAssignments($fieldId, $versionNo); + $this->deleteOrphanedKeywords(); + } + + /** + * Returns a list of keywords assigned to $fieldId. + */ + protected function getAssignedKeywords(int $fieldId, int $versionNo): array + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select($this->connection->quoteIdentifier('keyword')) + ->from($this->connection->quoteIdentifier(self::KEYWORD_TABLE), 'kwd') + ->innerJoin( + 'kwd', + $this->connection->quoteIdentifier(self::KEYWORD_ATTRIBUTE_LINK_TABLE), + 'attr', + $expr->eq( + $this->connection->quoteIdentifier('kwd.id'), + $this->connection->quoteIdentifier('attr.keyword_id') + ) + ) + ->where( + $expr->eq( + $this->connection->quoteIdentifier('attr.objectattribute_id'), + ':field_id' + ) + ) + ->andWhere( + $expr->eq( + $this->connection->quoteIdentifier('attr.version'), + ':version_no' + ) + ) + ->orderBy('kwd.id') + ->setParameter('field_id', $fieldId, ParameterType::INTEGER) + ->setParameter('version_no', $versionNo, ParameterType::INTEGER); + + return $query->execute()->fetchFirstColumn(); + } + + /** + * Retrieves the content type ID for the given $fieldDefinitionId. + * + * @param int $fieldDefinitionId + * + * @return int + */ + protected function loadContentTypeId($fieldDefinitionId) + { + $query = $this->connection->createQueryBuilder(); + $query + ->select($this->connection->quoteIdentifier('contentclass_id')) + ->from($this->connection->quoteIdentifier('ezcontentclass_attribute')) + ->where( + $query->expr()->eq('id', ':fieldDefinitionId') + ) + ->setParameter(':fieldDefinitionId', $fieldDefinitionId); + + $statement = $query->execute(); + + $row = $statement->fetch(\PDO::FETCH_ASSOC); + + if ($row === false) { + throw new RuntimeException( + sprintf( + 'Content type ID cannot be retrieved based on the Field definition ID "%s"', + $fieldDefinitionId + ) + ); + } + + return (int) $row['contentclass_id']; + } + + /** + * Returns already existing keywords from $keywordList as a map. + * + * The map has the following format: + * + * array( + * '' => , + * // ... + * ); + * + * + * @param string[] $keywordList + * @param int $contentTypeId + * + * @return int[] + */ + protected function getExistingKeywords($keywordList, $contentTypeId) + { + // Retrieving potentially existing keywords + $query = $this->connection->createQueryBuilder(); + $query + ->select( + $this->connection->quoteIdentifier('id'), + $this->connection->quoteIdentifier('keyword') + ) + ->from($this->connection->quoteIdentifier(self::KEYWORD_TABLE)) + ->where( + $query->expr()->andX( + $query->expr()->in( + $this->connection->quoteIdentifier('keyword'), + ':keywordList' + ), + $query->expr()->eq( + $this->connection->quoteIdentifier('class_id'), + ':contentTypeId' + ) + ) + ) + ->setParameter(':keywordList', $keywordList, Connection::PARAM_STR_ARRAY) + ->setParameter(':contentTypeId', $contentTypeId); + + $statement = $query->execute(); + + $existingKeywordMap = []; + foreach ($statement->fetchAll(\PDO::FETCH_ASSOC) as $row) { + // filter out keywords that aren't the exact match (e.g. differ by case) + if (!in_array($row['keyword'], $keywordList)) { + continue; + } + $existingKeywordMap[$row['keyword']] = $row['id']; + } + + return $existingKeywordMap; + } + + /** + * Inserts $keywordsToInsert for $fieldDefinitionId and returns a map of + * these keywords to their ID. + * + * The returned array has the following format: + * + * array( + * '' => , + * // ... + * ); + * + * + * @param string[] $keywordsToInsert + * @param int $contentTypeId + * + * @return int[] + */ + protected function insertKeywords(array $keywordsToInsert, $contentTypeId) + { + $keywordIdMap = []; + // Inserting keywords not yet registered + if (!empty($keywordsToInsert)) { + $insertQuery = $this->connection->createQueryBuilder(); + $insertQuery + ->insert($this->connection->quoteIdentifier(self::KEYWORD_TABLE)) + ->values( + [ + $this->connection->quoteIdentifier('class_id') => ':contentTypeId', + $this->connection->quoteIdentifier('keyword') => ':keyword', + ] + ) + ->setParameter(':contentTypeId', $contentTypeId, \PDO::PARAM_INT); + + foreach (array_keys($keywordsToInsert) as $keyword) { + $insertQuery->setParameter(':keyword', $keyword); + $insertQuery->execute(); + $keywordIdMap[$keyword] = (int)$this->connection->lastInsertId( + $this->getSequenceName(self::KEYWORD_TABLE, 'id') + ); + } + } + + return $keywordIdMap; + } + + protected function deleteOldKeywordAssignments(int $fieldId, int $versionNo): void + { + $deleteQuery = $this->connection->createQueryBuilder(); + $deleteQuery + ->delete($this->connection->quoteIdentifier(self::KEYWORD_ATTRIBUTE_LINK_TABLE)) + ->where( + $deleteQuery->expr()->andX( + $deleteQuery->expr()->eq( + $this->connection->quoteIdentifier('objectattribute_id'), + ':fieldId' + ), + $deleteQuery->expr()->eq( + $this->connection->quoteIdentifier('version'), + ':versionNo' + ) + ) + ) + ->setParameter('fieldId', $fieldId, ParameterType::INTEGER) + ->setParameter('versionNo', $versionNo, ParameterType::INTEGER); + + $deleteQuery->execute(); + } + + /** + * Assigns keywords from $keywordMap to the field with $fieldId and specific $versionNo. + * + * $keywordMap has the format: + * + * array( + * '' => , + * // ... + * ); + * + */ + protected function assignKeywords(int $fieldId, array $keywordMap, int $versionNo): void + { + $insertQuery = $this->connection->createQueryBuilder(); + $insertQuery + ->insert($this->connection->quoteIdentifier(self::KEYWORD_ATTRIBUTE_LINK_TABLE)) + ->values( + [ + $this->connection->quoteIdentifier('keyword_id') => ':keywordId', + $this->connection->quoteIdentifier('objectattribute_id') => ':fieldId', + $this->connection->quoteIdentifier('version') => ':versionNo', + ] + ) + ; + + foreach ($keywordMap as $keyword => $keywordId) { + $insertQuery + ->setParameter('keywordId', $keywordId, ParameterType::INTEGER) + ->setParameter('fieldId', $fieldId, ParameterType::INTEGER) + ->setParameter('versionNo', $versionNo, ParameterType::INTEGER); + $insertQuery->execute(); + } + } + + /** + * Deletes all orphaned keywords. + * + * Keyword is orphaned if it is not linked to a content attribute through + * ezkeyword_attribute_link table. + */ + protected function deleteOrphanedKeywords() + { + $query = $this->connection->createQueryBuilder(); + $query + ->select($this->connection->quoteIdentifier('kwd.id')) + ->from($this->connection->quoteIdentifier(self::KEYWORD_TABLE), 'kwd') + ->leftJoin( + 'kwd', + $this->connection->quoteIdentifier(self::KEYWORD_ATTRIBUTE_LINK_TABLE), + 'attr', + $query->expr()->eq( + $this->connection->quoteIdentifier('attr.keyword_id'), + $this->connection->quoteIdentifier('kwd.id') + ) + ) + ->where($query->expr()->isNull('attr.id')); + + $statement = $query->execute(); + $ids = $statement->fetchAll(\PDO::FETCH_COLUMN); + + if (empty($ids)) { + return; + } + + $deleteQuery = $this->connection->createQueryBuilder(); + $deleteQuery + ->delete($this->connection->quoteIdentifier(self::KEYWORD_TABLE)) + ->where( + $deleteQuery->expr()->in($this->connection->quoteIdentifier('id'), ':ids') + ) + ->setParameter(':ids', $ids, Connection::PARAM_INT_ARRAY); + + $deleteQuery->execute(); + } +} + +class_alias(DoctrineStorage::class, 'eZ\Publish\Core\FieldType\Keyword\KeywordStorage\Gateway\DoctrineStorage'); diff --git a/src/lib/FieldType/Keyword/SearchField.php b/src/lib/FieldType/Keyword/SearchField.php new file mode 100644 index 0000000000..f162de1c07 --- /dev/null +++ b/src/lib/FieldType/Keyword/SearchField.php @@ -0,0 +1,59 @@ +value->externalData, + new Search\FieldType\MultipleStringField() + ), + new Search\Field( + 'sort_value', + implode(' ', $field->value->externalData), + new Search\FieldType\StringField() + ), + new Search\Field( + 'fulltext', + $field->value->externalData, + new Search\FieldType\FullTextField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value' => new Search\FieldType\MultipleStringField(), + 'sort_value' => new Search\FieldType\StringField(), + ]; + } + + public function getDefaultMatchField() + { + return 'value'; + } + + public function getDefaultSortField() + { + return 'sort_value'; + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\Keyword\SearchField'); diff --git a/src/lib/FieldType/Keyword/Type.php b/src/lib/FieldType/Keyword/Type.php new file mode 100644 index 0000000000..6ef4f2e24a --- /dev/null +++ b/src/lib/FieldType/Keyword/Type.php @@ -0,0 +1,172 @@ +values); + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Keyword\Value + */ + public function getEmptyValue() + { + return new Value([]); + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param mixed $inputValue + * + * @return \Ibexa\Core\FieldType\Keyword\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_string($inputValue)) { + $inputValue = [$inputValue]; + } + + if (is_array($inputValue)) { + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Keyword\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!is_array($value->values)) { + throw new InvalidArgumentType( + '$value->values', + 'array', + $value->values + ); + } + } + + /** + * {@inheritdoc} + */ + protected function getSortInfo(BaseValue $value) + { + return implode(',', $value->values); + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\Keyword\Value $value + */ + public function fromHash($hash) + { + return new Value($hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Keyword\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + return $value->values; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + /** + * Converts a $value to a persistence value. + * + * @param \Ibexa\Core\FieldType\Keyword\Value $value + * + * @return \Ibexa\Contracts\Core\Persistence\Content\FieldValue + */ + public function toPersistenceValue(SPIValue $value) + { + return new FieldValue( + [ + 'data' => null, + 'externalData' => $value->values, + 'sortKey' => $this->getSortInfo($value), + ] + ); + } + + /** + * Converts a persistence $fieldValue to a Value. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + * + * @return \Ibexa\Core\FieldType\Keyword\Value + */ + public function fromPersistenceValue(FieldValue $fieldValue) + { + return new Value($fieldValue->externalData); + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezkeyword.name', 'ibexa_fieldtypes')->setDesc('Keywords'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Keyword\Type'); diff --git a/src/lib/FieldType/Keyword/Value.php b/src/lib/FieldType/Keyword/Value.php new file mode 100644 index 0000000000..e3e73df0aa --- /dev/null +++ b/src/lib/FieldType/Keyword/Value.php @@ -0,0 +1,57 @@ +values = array_unique($values); + } + } + + /** + * Returns a string representation of the keyword value. + * + * @return string A comma separated list of tags, eg: "php, Ibexa, html5" + */ + public function __toString() + { + return implode(', ', $this->values); + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\Keyword\Value'); diff --git a/src/lib/FieldType/MapLocation/MapLocationStorage.php b/src/lib/FieldType/MapLocation/MapLocationStorage.php new file mode 100644 index 0000000000..7d3485e5ff --- /dev/null +++ b/src/lib/FieldType/MapLocation/MapLocationStorage.php @@ -0,0 +1,75 @@ +gateway->storeFieldData($versionInfo, $field); + } + + public function getFieldData(VersionInfo $versionInfo, Field $field, array $context) + { + $this->gateway->getFieldData($versionInfo, $field); + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param array $fieldIds + * @param array $context + * + * @return bool + */ + public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context) + { + $this->gateway->deleteFieldData($versionInfo, $fieldIds); + } + + /** + * Checks if field type has external data to deal with. + * + * @return bool + */ + public function hasFieldData() + { + return true; + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param array $context + * + * @return \Ibexa\Contracts\Core\Search\Field[]|null + */ + public function getIndexData(VersionInfo $versionInfo, Field $field, array $context) + { + return is_array($field->value->externalData) ? $field->value->externalData['address'] : null; + } +} + +class_alias(MapLocationStorage::class, 'eZ\Publish\Core\FieldType\MapLocation\MapLocationStorage'); diff --git a/src/lib/FieldType/MapLocation/MapLocationStorage/Gateway.php b/src/lib/FieldType/MapLocation/MapLocationStorage/Gateway.php new file mode 100644 index 0000000000..06c3a8f93a --- /dev/null +++ b/src/lib/FieldType/MapLocation/MapLocationStorage/Gateway.php @@ -0,0 +1,50 @@ +externalData. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * + * @return array + */ + abstract public function getFieldData(VersionInfo $versionInfo, Field $field); + + /** + * Deletes the data for all given $fieldIds. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param array $fieldIds + */ + abstract public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds); +} + +class_alias(Gateway::class, 'eZ\Publish\Core\FieldType\MapLocation\MapLocationStorage\Gateway'); diff --git a/src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php b/src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php new file mode 100644 index 0000000000..ddc8e0213b --- /dev/null +++ b/src/lib/FieldType/MapLocation/MapLocationStorage/Gateway/DoctrineStorage.php @@ -0,0 +1,240 @@ +connection = $connection; + } + + /** + * Store the data stored in the given $field. + * + * Potentially rewrites data in $field and returns true, if the $field + * needs to be updated in the database. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * + * @return bool If restoring of the internal field data is required + */ + public function storeFieldData(VersionInfo $versionInfo, Field $field) + { + if ($field->value->externalData === null) { + // Store empty value and return + $this->deleteFieldData($versionInfo, [$field->id]); + $field->value->data = [ + 'sortKey' => null, + 'hasData' => false, + ]; + + return false; + } + + if ($this->hasFieldData($field->id, $versionInfo->versionNo)) { + $this->updateFieldData($versionInfo, $field); + } else { + $this->storeNewFieldData($versionInfo, $field); + } + + $field->value->data = [ + 'sortKey' => $field->value->externalData['address'], + 'hasData' => true, + ]; + + return true; + } + + /** + * Perform an update on the field data. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + */ + protected function updateFieldData(VersionInfo $versionInfo, Field $field) + { + $updateQuery = $this->connection->createQueryBuilder(); + $updateQuery->update($this->connection->quoteIdentifier(self::MAP_LOCATION_TABLE)) + ->set($this->connection->quoteIdentifier('latitude'), ':latitude') + ->set($this->connection->quoteIdentifier('longitude'), ':longitude') + ->set($this->connection->quoteIdentifier('address'), ':address') + ->where( + $updateQuery->expr()->andX( + $updateQuery->expr()->eq( + $this->connection->quoteIdentifier('contentobject_attribute_id'), + ':fieldId' + ), + $updateQuery->expr()->eq( + $this->connection->quoteIdentifier('contentobject_version'), + ':versionNo' + ) + ) + ) + ->setParameter(':latitude', $field->value->externalData['latitude']) + ->setParameter(':longitude', $field->value->externalData['longitude']) + ->setParameter(':address', $field->value->externalData['address']) + ->setParameter(':fieldId', $field->id, PDO::PARAM_INT) + ->setParameter(':versionNo', $versionInfo->versionNo, PDO::PARAM_INT) + ; + + $updateQuery->execute(); + } + + /** + * Store new field data. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + */ + protected function storeNewFieldData(VersionInfo $versionInfo, Field $field) + { + $insertQuery = $this->connection->createQueryBuilder(); + $insertQuery + ->insert($this->connection->quoteIdentifier(self::MAP_LOCATION_TABLE)) + ->values([ + 'latitude' => ':latitude', + 'longitude' => ':longitude', + 'address' => ':address', + 'contentobject_attribute_id' => ':fieldId', + 'contentobject_version' => ':versionNo', + ]) + ->setParameter(':latitude', $field->value->externalData['latitude']) + ->setParameter(':longitude', $field->value->externalData['longitude']) + ->setParameter(':address', $field->value->externalData['address']) + ->setParameter(':fieldId', $field->id) + ->setParameter(':versionNo', $versionInfo->versionNo) + ; + + $insertQuery->execute(); + } + + /** + * Set the loaded field data into $field->externalData. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * + * @return array + */ + public function getFieldData(VersionInfo $versionInfo, Field $field) + { + $field->value->externalData = $this->loadFieldData($field->id, $versionInfo->versionNo); + } + + /** + * Return the data for the given $fieldId. + * + * If no data is found, null is returned. + * + * @param int $fieldId + * @param int $versionNo + * + * @return array|null + */ + protected function loadFieldData($fieldId, $versionNo) + { + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery + ->select( + $this->connection->quoteIdentifier('latitude'), + $this->connection->quoteIdentifier('longitude'), + $this->connection->quoteIdentifier('address') + ) + ->from($this->connection->quoteIdentifier('ezgmaplocation')) + ->where( + $selectQuery->expr()->andX( + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('contentobject_attribute_id'), + ':fieldId' + ), + $selectQuery->expr()->eq( + $this->connection->quoteIdentifier('contentobject_version'), + ':versionNo' + ) + ) + ) + ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) + ->setParameter(':versionNo', $versionNo, PDO::PARAM_INT) + ; + + $statement = $selectQuery->execute(); + + $rows = $statement->fetchAll(PDO::FETCH_ASSOC); + if (!isset($rows[0])) { + return null; + } + + // Cast coordinates as the DB can return them as strings + $rows[0]['latitude'] = (float)$rows[0]['latitude']; + $rows[0]['longitude'] = (float)$rows[0]['longitude']; + + return $rows[0]; + } + + /** + * Return if field data exists for $fieldId. + * + * @param int $fieldId + * @param int $versionNo + * + * @return bool + */ + protected function hasFieldData($fieldId, $versionNo) + { + return $this->loadFieldData($fieldId, $versionNo) !== null; + } + + /** + * Delete the data for all given $fieldIds. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param int[] $fieldIds + */ + public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds) + { + if (empty($fieldIds)) { + // Nothing to do + return; + } + + $deleteQuery = $this->connection->createQueryBuilder(); + $deleteQuery + ->delete($this->connection->quoteIdentifier(self::MAP_LOCATION_TABLE)) + ->where( + $deleteQuery->expr()->andX( + $deleteQuery->expr()->in( + $this->connection->quoteIdentifier('contentobject_attribute_id'), + ':fieldIds' + ), + $deleteQuery->expr()->eq( + $this->connection->quoteIdentifier('contentobject_version'), + ':versionNo' + ) + ) + ) + ->setParameter(':fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY) + ->setParameter(':versionNo', $versionInfo->versionNo, PDO::PARAM_INT) + ; + + $deleteQuery->execute(); + } +} + +class_alias(DoctrineStorage::class, 'eZ\Publish\Core\FieldType\MapLocation\MapLocationStorage\Gateway\DoctrineStorage'); diff --git a/src/lib/FieldType/MapLocation/SearchField.php b/src/lib/FieldType/MapLocation/SearchField.php new file mode 100644 index 0000000000..69760c4603 --- /dev/null +++ b/src/lib/FieldType/MapLocation/SearchField.php @@ -0,0 +1,80 @@ +value->externalData['address'] ?? null, + new Search\FieldType\StringField() + ), + new Search\Field( + 'value_location', + [ + 'latitude' => $field->value->externalData['latitude'] ?? null, + 'longitude' => $field->value->externalData['longitude'] ?? null, + ], + new Search\FieldType\GeoLocationField() + ), + new Search\Field( + 'fulltext', + $field->value->externalData['address'] ?? null, + new Search\FieldType\FullTextField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value_address' => new Search\FieldType\StringField(), + 'value_location' => new Search\FieldType\GeoLocationField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value_address'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\MapLocation\SearchField'); diff --git a/src/lib/FieldType/MapLocation/Type.php b/src/lib/FieldType/MapLocation/Type.php new file mode 100644 index 0000000000..f6a6ca12f9 --- /dev/null +++ b/src/lib/FieldType/MapLocation/Type.php @@ -0,0 +1,214 @@ +address; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\MapLocation\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Returns if the given $value is considered empty by the field type. + * + * @param mixed $value + * + * @return bool + */ + public function isEmptyValue(SPIValue $value) + { + return $value->latitude === null && $value->longitude === null; + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param array|\Ibexa\Core\FieldType\MapLocation\Value $inputValue + * + * @return \Ibexa\Core\FieldType\MapLocation\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_array($inputValue)) { + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\MapLocation\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!is_float($value->latitude) && !is_int($value->latitude)) { + throw new InvalidArgumentType( + '$value->latitude', + 'float', + $value->latitude + ); + } + if (!is_float($value->longitude) && !is_int($value->longitude)) { + throw new InvalidArgumentType( + '$value->longitude', + 'float', + $value->longitude + ); + } + if (!is_string($value->address)) { + throw new InvalidArgumentType( + '$value->address', + 'string', + $value->address + ); + } + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * @param \Ibexa\Core\FieldType\MapLocation\Value $value + * + * @return string + */ + protected function getSortInfo(BaseValue $value) + { + return $this->transformationProcessor->transformByGroup((string)$value, 'lowercase'); + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\MapLocation\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + return new Value($hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\MapLocation\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + return [ + 'latitude' => $value->latitude, + 'longitude' => $value->longitude, + 'address' => $value->address, + ]; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + /** + * Converts a $value to a persistence value. + * + * @param \Ibexa\Core\FieldType\MapLocation\Value $value + * + * @return \Ibexa\Contracts\Core\Persistence\Content\FieldValue + */ + public function toPersistenceValue(SPIValue $value) + { + return new FieldValue( + [ + 'data' => null, + 'externalData' => $this->toHash($value), + 'sortKey' => $this->getSortInfo($value), + ] + ); + } + + /** + * Converts a persistence $fieldValue to a Value. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + * + * @return \Ibexa\Core\FieldType\MapLocation\Value + */ + public function fromPersistenceValue(FieldValue $fieldValue) + { + if ($fieldValue->externalData === null) { + return $this->getEmptyValue(); + } + + return $this->fromHash($fieldValue->externalData); + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezgmaplocation.name', 'ibexa_fieldtypes')->setDesc('Map location'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\MapLocation\Type'); diff --git a/src/lib/FieldType/MapLocation/Value.php b/src/lib/FieldType/MapLocation/Value.php new file mode 100644 index 0000000000..0e8f92da01 --- /dev/null +++ b/src/lib/FieldType/MapLocation/Value.php @@ -0,0 +1,64 @@ + $value) { + $this->$key = $value; + } + } + + /** + * Returns a string representation of the keyword value. + * + * @return string A comma separated list of tags, eg: "php, Ibexa, html5" + */ + public function __toString() + { + if (is_array($this->address)) { + return implode(', ', $this->address); + } + + return (string)$this->address; + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\MapLocation\Value'); diff --git a/src/lib/FieldType/Media/MediaStorage.php b/src/lib/FieldType/Media/MediaStorage.php new file mode 100644 index 0000000000..069751a68b --- /dev/null +++ b/src/lib/FieldType/Media/MediaStorage.php @@ -0,0 +1,18 @@ + 'hasController', + 'cast' => static function ($val) { + return (bool)$val; + }, + ]; + $propertyMap['is_autoplay'] = [ + 'name' => 'autoplay', + 'cast' => static function ($val) { + return (bool)$val; + }, + ]; + $propertyMap['is_loop'] = [ + 'name' => 'loop', + 'cast' => static function ($val) { + return (bool)$val; + }, + ]; + $propertyMap['width'] = [ + 'name' => 'width', + 'cast' => 'intval', + ]; + $propertyMap['height'] = [ + 'name' => 'height', + 'cast' => 'intval', + ]; + + return $propertyMap; + } + + /** + * {@inheritdoc} + */ + protected function setFetchColumns(QueryBuilder $queryBuilder, $fieldId, $versionNo) + { + parent::setFetchColumns($queryBuilder, $fieldId, $versionNo); + + $queryBuilder->addSelect( + [ + $this->connection->quoteIdentifier('has_controller'), + $this->connection->quoteIdentifier('is_autoplay'), + $this->connection->quoteIdentifier('is_loop'), + $this->connection->quoteIdentifier('width'), + $this->connection->quoteIdentifier('height'), + ] + ); + } + + /** + * {@inheritdoc} + */ + protected function setInsertColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field) + { + parent::setInsertColumns($queryBuilder, $versionInfo, $field); + + $queryBuilder + ->setValue('controls', ':controls') + ->setValue('has_controller', ':hasController') + ->setValue('height', ':height') + ->setValue('is_autoplay', ':isAutoplay') + ->setValue('is_loop', ':isLoop') + ->setValue('pluginspage', ':pluginsPage') + ->setValue('quality', ':quality') + ->setValue('width', ':width') + ->setParameter(':controls', '') + ->setParameter( + ':hasController', + $field->value->externalData['hasController'], + PDO::PARAM_INT + ) + ->setParameter(':height', $field->value->externalData['height'], PDO::PARAM_INT) + ->setParameter(':isAutoplay', $field->value->externalData['autoplay'], PDO::PARAM_INT) + ->setParameter(':isLoop', $field->value->externalData['loop'], PDO::PARAM_INT) + ->setParameter(':pluginsPage', '') + ->setParameter(':quality', 'high') + ->setParameter(':width', $field->value->externalData['width'], PDO::PARAM_INT) + ; + } + + /** + * {@inheritdoc} + */ + protected function setUpdateColumns(QueryBuilder $queryBuilder, VersionInfo $versionInfo, Field $field) + { + parent::setUpdateColumns($queryBuilder, $versionInfo, $field); + + $queryBuilder + ->set('controls', ':controls') + ->set('has_controller', ':hasController') + ->set('height', ':height') + ->set('is_autoplay', ':isAutoplay') + ->set('is_loop', ':isLoop') + ->set('pluginspage', ':pluginsPage') + ->set('quality', ':quality') + ->set('width', ':width') + ->setParameter(':controls', '') + ->setParameter( + ':hasController', + $field->value->externalData['hasController'], + ParameterType::INTEGER + ) + ->setParameter(':height', $field->value->externalData['height'], ParameterType::INTEGER) + ->setParameter(':isAutoplay', $field->value->externalData['autoplay'], ParameterType::INTEGER) + ->setParameter(':isLoop', $field->value->externalData['loop'], ParameterType::INTEGER) + ->setParameter(':pluginsPage', '') + ->setParameter(':quality', 'high') + ->setParameter(':width', $field->value->externalData['width'], ParameterType::INTEGER) + ; + } +} + +class_alias(DoctrineStorage::class, 'eZ\Publish\Core\FieldType\Media\MediaStorage\Gateway\DoctrineStorage'); diff --git a/src/lib/FieldType/Media/SearchField.php b/src/lib/FieldType/Media/SearchField.php new file mode 100644 index 0000000000..17c4bd233e --- /dev/null +++ b/src/lib/FieldType/Media/SearchField.php @@ -0,0 +1,78 @@ +value->externalData['fileName'] ?? null, + new Search\FieldType\StringField() + ), + new Search\Field( + 'file_size', + $field->value->externalData['fileSize'] ?? null, + new Search\FieldType\IntegerField() + ), + new Search\Field( + 'mime_type', + $field->value->externalData['mimeType'] ?? null, + new Search\FieldType\StringField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'file_name' => new Search\FieldType\StringField(), + 'file_size' => new Search\FieldType\IntegerField(), + 'mime_type' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'file_name'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\Media\SearchField'); diff --git a/src/lib/FieldType/Media/Type.php b/src/lib/FieldType/Media/Type.php new file mode 100644 index 0000000000..c416bf0cd7 --- /dev/null +++ b/src/lib/FieldType/Media/Type.php @@ -0,0 +1,278 @@ + [ + 'type' => 'choice', + 'default' => self::TYPE_HTML5_VIDEO, + ], + ]; + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezmedia'; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Media\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $fieldSettings + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateFieldSettings($fieldSettings) + { + $validationErrors = []; + + foreach ($fieldSettings as $name => $value) { + if (isset($this->settingsSchema[$name])) { + switch ($name) { + case 'mediaType': + if (!in_array($value, self::$availableTypes)) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is of unknown type", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + } + } else { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is unknown", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + } + + return $validationErrors; + } + + /** + * Creates a specific value of the derived class from $inputValue. + * + * @param array $inputValue + * + * @return \Ibexa\Core\FieldType\Media\Value + */ + protected function createValue(array $inputValue) + { + $inputValue = $this->regenerateUri($inputValue); + + return new Value($inputValue); + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Media\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + parent::checkValueStructure($value); + + if (!is_bool($value->hasController)) { + throw new InvalidArgumentType( + '$value->hasController', + 'bool', + $value->hasController + ); + } + if (!is_bool($value->autoplay)) { + throw new InvalidArgumentType( + '$value->autoplay', + 'bool', + $value->autoplay + ); + } + if (!is_bool($value->loop)) { + throw new InvalidArgumentType( + '$value->loop', + 'bool', + $value->loop + ); + } + + if (!is_int($value->height)) { + throw new InvalidArgumentType( + '$value->height', + 'int', + $value->height + ); + } + if (!is_int($value->width)) { + throw new InvalidArgumentType( + '$value->width', + 'int', + $value->width + ); + } + } + + /** + * Attempts to complete the data in $value. + * + * @param \Ibexa\Core\FieldType\Media\Value|\Ibexa\Core\FieldType\Value $value + */ + protected function completeValue(BaseValue $value) + { + parent::completeValue($value); + + if (isset($value->hasController) && $value->hasController === null) { + $value->hasController = false; + } + if (isset($value->autoplay) && $value->autoplay === null) { + $value->autoplay = false; + } + if (isset($value->loop) && $value->loop === null) { + $value->loop = false; + } + + if (isset($value->height) && $value->height === null) { + $value->height = 0; + } + if (isset($value->width) && $value->width === null) { + $value->width = 0; + } + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Media\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + $hash = parent::toHash($value); + + $hash['hasController'] = $value->hasController; + $hash['autoplay'] = $value->autoplay; + $hash['loop'] = $value->loop; + $hash['width'] = $value->width; + $hash['height'] = $value->height; + + return $hash; + } + + /** + * Converts a persistence $fieldValue to a Value. + * + * This method builds a field type value from the $data and $externalData properties. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + * + * @return \Ibexa\Core\FieldType\Media\Value + */ + public function fromPersistenceValue(FieldValue $fieldValue) + { + if ($fieldValue->externalData === null) { + return $this->getEmptyValue(); + } + + $result = parent::fromPersistenceValue($fieldValue); + + $result->hasController = $fieldValue->externalData['hasController'] ?? false; + $result->autoplay = $fieldValue->externalData['autoplay'] ?? false; + $result->loop = $fieldValue->externalData['loop'] ?? false; + $result->height = $fieldValue->externalData['height'] ?? 0; + $result->width = $fieldValue->externalData['width'] ?? 0; + + return $result; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return false; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezmedia.name', 'ibexa_fieldtypes')->setDesc('Media'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Media\Type'); diff --git a/src/lib/FieldType/Media/Value.php b/src/lib/FieldType/Media/Value.php new file mode 100644 index 0000000000..87583b6018 --- /dev/null +++ b/src/lib/FieldType/Media/Value.php @@ -0,0 +1,52 @@ +fieldTypeIdentifier = $fieldTypeIdentifier; + } + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return $this->fieldTypeIdentifier; + } + + /** + * @param \Ibexa\Core\FieldType\Null\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + return (string)$value->value; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Null\Value + */ + public function getEmptyValue() + { + return new Value(null); + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param \Ibexa\Core\FieldType\Null\Value $inputValue + * + * @return \Ibexa\Core\FieldType\Null\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Null\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + // Does nothing + } + + /** + * {@inheritdoc} + */ + protected function getSortInfo(BaseValue $value) + { + return null; + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\Null\Value $value + */ + public function fromHash($hash) + { + return new Value($hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Null\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if (isset($value->value)) { + return $value->value; + } + + return null; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Null\Type'); diff --git a/src/lib/FieldType/Null/Value.php b/src/lib/FieldType/Null/Value.php new file mode 100644 index 0000000000..d307001cf1 --- /dev/null +++ b/src/lib/FieldType/Null/Value.php @@ -0,0 +1,39 @@ +value = $value; + } + + public function __toString() + { + return (string)$this->value; + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\Null\Value'); diff --git a/src/lib/FieldType/NullStorage.php b/src/lib/FieldType/NullStorage.php new file mode 100644 index 0000000000..4fc7d87fe1 --- /dev/null +++ b/src/lib/FieldType/NullStorage.php @@ -0,0 +1,80 @@ +value->data), + new Search\FieldType\StringField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\Relation\SearchField'); diff --git a/src/lib/FieldType/Relation/Type.php b/src/lib/FieldType/Relation/Type.php new file mode 100644 index 0000000000..90dc13f508 --- /dev/null +++ b/src/lib/FieldType/Relation/Type.php @@ -0,0 +1,364 @@ + (int)$destinationContentId ); + */ +class Type extends FieldType implements TranslationContainerInterface +{ + public const SELECTION_BROWSE = 0; + public const SELECTION_DROPDOWN = 1; + + protected $settingsSchema = [ + 'selectionMethod' => [ + 'type' => 'int', + 'default' => self::SELECTION_BROWSE, + ], + 'selectionRoot' => [ + 'type' => 'string', + 'default' => null, + ], + 'rootDefaultLocation' => [ + 'type' => 'bool', + 'default' => false, + ], + 'selectionContentTypes' => [ + 'type' => 'array', + 'default' => [], + ], + ]; + + /** @var \Ibexa\Contracts\Core\Persistence\Content\Handler */ + private $handler; + + /** @var \Ibexa\Core\Repository\Validator\TargetContentValidatorInterface */ + private $targetContentValidator; + + public function __construct( + SPIContentHandler $handler, + TargetContentValidatorInterface $targetContentValidator + ) { + $this->handler = $handler; + $this->targetContentValidator = $targetContentValidator; + } + + public function validateFieldSettings($fieldSettings) + { + $validationErrors = []; + + foreach ($fieldSettings as $name => $value) { + if (!isset($this->settingsSchema[$name])) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is unknown", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + continue; + } + + switch ($name) { + case 'selectionMethod': + if ($value !== self::SELECTION_BROWSE && $value !== self::SELECTION_DROPDOWN) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' must be either %selection_browse% or %selection_dropdown%", + null, + [ + '%setting%' => $name, + '%selection_browse%' => self::SELECTION_BROWSE, + '%selection_dropdown%' => self::SELECTION_DROPDOWN, + ], + "[$name]" + ); + } + break; + case 'selectionRoot': + if (!is_int($value) && !is_string($value) && $value !== null) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' value must be of either null, string or integer", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + case 'rootDefaultLocation': + if (!is_bool($value) && $value !== null) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' value must be of either null or bool", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + case 'selectionContentTypes': + if (!is_array($value)) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' value must be of array type", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + } + } + + return $validationErrors; + } + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezobjectrelation'; + } + + /** + * @param \Ibexa\Core\FieldType\Relation\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + if (empty($value->destinationContentId)) { + return ''; + } + + try { + $contentInfo = $this->handler->loadContentInfo($value->destinationContentId); + $versionInfo = $this->handler->loadVersionInfo($value->destinationContentId, $contentInfo->currentVersionNo); + } catch (NotFoundException $e) { + return ''; + } + + if (isset($versionInfo->names[$languageCode])) { + return $versionInfo->names[$languageCode]; + } + + return $versionInfo->names[$contentInfo->mainLanguageCode]; + } + + /** + * Validates a field based on the validators in the field definition. + * + * @return \Ibexa\Core\FieldType\ValidationError[] + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue): array + { + $validationErrors = []; + + if ($this->isEmptyValue($fieldValue)) { + return $validationErrors; + } + + $allowedContentTypes = $fieldDefinition->getFieldSettings()['selectionContentTypes'] ?? []; + + $validationError = $this->targetContentValidator->validate( + (int) $fieldValue->destinationContentId, + $allowedContentTypes + ); + + return $validationError === null ? $validationErrors : [$validationError]; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Relation\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Returns if the given $value is considered empty by the field type. + * + * @param mixed $value + * + * @return bool + */ + public function isEmptyValue(SPIValue $value) + { + return $value->destinationContentId === null; + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param int|string|\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo|\Ibexa\Core\FieldType\Relation\Value $inputValue + * + * @return \Ibexa\Core\FieldType\Relation\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + // ContentInfo + if ($inputValue instanceof ContentInfo) { + $inputValue = new Value($inputValue->id); + } elseif (is_int($inputValue) || is_string($inputValue)) { // content id + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Relation\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!is_int($value->destinationContentId) && !is_string($value->destinationContentId)) { + throw new InvalidArgumentType( + '$value->destinationContentId', + 'string|int', + $value->destinationContentId + ); + } + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * For this FieldType, the related object's name is returned. + * + * @param \Ibexa\Core\FieldType\Relation\Value $value + * + * @return string + */ + protected function getSortInfo(BaseValue $value) + { + return (string)$value; + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\Relation\Value $value + */ + public function fromHash($hash) + { + if ($hash !== null) { + $destinationContentId = $hash['destinationContentId']; + if ($destinationContentId !== null) { + return new Value((int)$destinationContentId); + } + } + + return $this->getEmptyValue(); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Relation\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + $destinationContentId = null; + if ($value->destinationContentId !== null) { + $destinationContentId = (int)$value->destinationContentId; + } + + return [ + 'destinationContentId' => $destinationContentId, + ]; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + /** + * Returns relation data extracted from value. + * + * Not intended for \Ibexa\Contracts\Core\Repository\Values\Content\Relation::COMMON type relations, + * there is an API for handling those. + * + * @param \Ibexa\Core\FieldType\Relation\Value $fieldValue + * + * @return array Hash with relation type as key and array of destination content ids as value. + * + * Example: + * + * array( + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::LINK => array( + * "contentIds" => array( 12, 13, 14 ), + * "locationIds" => array( 24 ) + * ), + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::EMBED => array( + * "contentIds" => array( 12 ), + * "locationIds" => array( 24, 45 ) + * ), + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::FIELD => array( 12 ) + * ) + * + */ + public function getRelations(SPIValue $fieldValue) + { + $relations = []; + if ($fieldValue->destinationContentId !== null) { + $relations[Relation::FIELD] = [$fieldValue->destinationContentId]; + } + + return $relations; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezobjectrelation.name', 'ibexa_fieldtypes') + ->setDesc('Content relation (single)'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Relation\Type'); diff --git a/src/lib/FieldType/Relation/Value.php b/src/lib/FieldType/Relation/Value.php new file mode 100644 index 0000000000..3a0abe0e0c --- /dev/null +++ b/src/lib/FieldType/Relation/Value.php @@ -0,0 +1,42 @@ +destinationContentId = $destinationContentId; + } + + /** + * Returns the related content's name. + */ + public function __toString() + { + return (string)$this->destinationContentId; + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\Relation\Value'); diff --git a/src/lib/FieldType/RelationList/SearchField.php b/src/lib/FieldType/RelationList/SearchField.php new file mode 100644 index 0000000000..5251215d63 --- /dev/null +++ b/src/lib/FieldType/RelationList/SearchField.php @@ -0,0 +1,72 @@ +value->data['destinationContentIds'], + new Search\FieldType\MultipleStringField() + ), + new Search\Field( + 'sort_value', + implode('-', $field->value->data['destinationContentIds']), + new Search\FieldType\StringField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value' => new Search\FieldType\MultipleStringField(), + 'sort_value' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return 'sort_value'; + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\RelationList\SearchField'); diff --git a/src/lib/FieldType/RelationList/Type.php b/src/lib/FieldType/RelationList/Type.php new file mode 100644 index 0000000000..abc34b59a1 --- /dev/null +++ b/src/lib/FieldType/RelationList/Type.php @@ -0,0 +1,503 @@ + array( (int)$destinationContentId ) ); + */ +class Type extends FieldType implements TranslationContainerInterface +{ + public const SELECTION_BROWSE = 0; + /** + * @todo following selection methods comes from legacy and may be interpreted as SELECTION_BROWSE by UI. + * UI support will be evaluated on a case by case basis for future versions. + */ + public const SELECTION_DROPDOWN = 1; + public const SELECTION_LIST_WITH_RADIO_BUTTONS = 2; + public const SELECTION_LIST_WITH_CHECKBOXES = 3; + public const SELECTION_MULTIPLE_SELECTION_LIST = 4; + public const SELECTION_TEMPLATE_BASED_MULTIPLE = 5; + public const SELECTION_TEMPLATE_BASED_SINGLE = 6; + + protected $settingsSchema = [ + 'selectionMethod' => [ + 'type' => 'int', + 'default' => self::SELECTION_BROWSE, + ], + 'selectionDefaultLocation' => [ + 'type' => 'string', + 'default' => null, + ], + 'rootDefaultLocation' => [ + 'type' => 'bool', + 'default' => false, + ], + 'selectionContentTypes' => [ + 'type' => 'array', + 'default' => [], + ], + ]; + + protected $validatorConfigurationSchema = [ + 'RelationListValueValidator' => [ + 'selectionLimit' => [ + 'type' => 'int', + 'default' => 0, + ], + ], + ]; + + /** @var \Ibexa\Contracts\Core\Persistence\Content\Handler */ + private $handler; + + /** @var \Ibexa\Core\Repository\Validator\TargetContentValidatorInterface */ + private $targetContentValidator; + + public function __construct( + SPIContentHandler $handler, + TargetContentValidatorInterface $targetContentValidator + ) { + $this->handler = $handler; + $this->targetContentValidator = $targetContentValidator; + } + + public function validateFieldSettings($fieldSettings) + { + $validationErrors = []; + + foreach ($fieldSettings as $name => $value) { + if (!isset($this->settingsSchema[$name])) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is unknown", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + continue; + } + + switch ($name) { + case 'selectionMethod': + if (!$this->isValidSelectionMethod($value)) { + $validationErrors[] = new ValidationError( + "The following options are available for setting '%setting%': %selection_browse%, %selection_dropdown%, %selection_list_with_radio_buttons%, %selection_list_with_checkboxes%, %selection_multiple_selection_list%, %selection_template_based_multiple%, %selection_template_based_single%", + null, + [ + '%setting%' => $name, + '%selection_browse%' => self::SELECTION_BROWSE, + '%selection_dropdown%' => self::SELECTION_DROPDOWN, + '%selection_list_with_radio_buttons%' => self::SELECTION_LIST_WITH_RADIO_BUTTONS, + '%selection_list_with_checkboxes%' => self::SELECTION_LIST_WITH_CHECKBOXES, + '%selection_multiple_selection_list%' => self::SELECTION_MULTIPLE_SELECTION_LIST, + '%selection_template_based_multiple%' => self::SELECTION_TEMPLATE_BASED_MULTIPLE, + '%selection_template_based_single%' => self::SELECTION_TEMPLATE_BASED_SINGLE, + ], + "[$name]" + ); + } + break; + case 'selectionDefaultLocation': + if (!is_int($value) && !is_string($value) && $value !== null) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' value must be of either null, string or integer", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + case 'rootDefaultLocation': + if (!is_bool($value) && $value !== null) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' value must be of either null or bool", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + case 'selectionContentTypes': + if (!is_array($value)) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' value must be of array type", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + case 'selectionLimit': + if (!is_int($value)) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' value must be of integer type", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + if ($value < 0) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' value cannot be lower than 0", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + } + } + + return $validationErrors; + } + + /** + * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $validatorConfiguration + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateValidatorConfiguration($validatorConfiguration) + { + $validationErrors = []; + + foreach ($validatorConfiguration as $validatorIdentifier => $constraints) { + if ($validatorIdentifier !== 'RelationListValueValidator') { + $validationErrors[] = new ValidationError( + "Validator '%validator%' is unknown", + null, + [ + '%validator%' => $validatorIdentifier, + ], + "[$validatorIdentifier]" + ); + + continue; + } + + foreach ($constraints as $name => $value) { + if ($name === 'selectionLimit') { + if (!is_int($value) && !ctype_digit($value)) { + $validationErrors[] = new ValidationError( + "Validator parameter '%parameter%' value must be an integer", + null, + [ + '%parameter%' => $name, + ], + "[$validatorIdentifier][$name]" + ); + } + if ($value < 0) { + $validationErrors[] = new ValidationError( + "Validator parameter '%parameter%' value must be equal to/greater than 0", + null, + [ + '%parameter%' => $name, + ], + "[$validatorIdentifier][$name]" + ); + } + } else { + $validationErrors[] = new ValidationError( + "Validator parameter '%parameter%' is unknown", + null, + [ + '%parameter%' => $name, + ], + "[$validatorIdentifier][$name]" + ); + } + } + } + + return $validationErrors; + } + + /** + * Validates a field based on the validators in the field definition. + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue): array + { + $validationErrors = []; + + if ($this->isEmptyValue($fieldValue)) { + return $validationErrors; + } + + $validatorConfiguration = $fieldDefinition->getValidatorConfiguration(); + $constraints = $validatorConfiguration['RelationListValueValidator'] ?? []; + + $validationErrors = []; + + if (isset($constraints['selectionLimit']) && + $constraints['selectionLimit'] > 0 && count($fieldValue->destinationContentIds) > $constraints['selectionLimit']) { + $validationErrors[] = new ValidationError( + 'The selected content items number cannot be higher than %limit%.', + null, + [ + '%limit%' => $constraints['selectionLimit'], + ], + 'destinationContentIds' + ); + } + + $allowedContentTypes = $fieldDefinition->getFieldSettings()['selectionContentTypes'] ?? []; + + foreach ($fieldValue->destinationContentIds as $destinationContentId) { + $validationError = $this->targetContentValidator->validate((int) $destinationContentId, $allowedContentTypes); + if ($validationError !== null) { + $validationErrors[] = $validationError; + } + } + + return $validationErrors; + } + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezobjectrelationlist'; + } + + /** + * @param \Ibexa\Core\FieldType\RelationList\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + if (empty($value->destinationContentIds)) { + return ''; + } + + $names = []; + foreach ($value->destinationContentIds as $contentId) { + try { + $contentInfo = $this->handler->loadContentInfo($contentId); + $versionInfo = $this->handler->loadVersionInfo($contentId, $contentInfo->currentVersionNo); + } catch (NotFoundException $e) { + continue; + } + + if (isset($versionInfo->names[$languageCode])) { + $names[] = $versionInfo->names[$languageCode]; + } else { + $names[] = $versionInfo->names[$contentInfo->mainLanguageCode]; + } + } + + return implode(' ', $names); + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\RelationList\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param int|string|array|\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo|\Ibexa\Core\FieldType\RelationList\Value $inputValue + * + * @return \Ibexa\Core\FieldType\RelationList\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + // ContentInfo + if ($inputValue instanceof ContentInfo) { + $inputValue = new Value([$inputValue->id]); + } elseif (is_int($inputValue) || is_string($inputValue)) { + // content id + $inputValue = new Value([$inputValue]); + } elseif (is_array($inputValue)) { + // content id's + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\RelationList\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!is_array($value->destinationContentIds)) { + throw new InvalidArgumentType( + '$value->destinationContentIds', + 'array', + $value->destinationContentIds + ); + } + + foreach ($value->destinationContentIds as $key => $destinationContentId) { + if (!is_int($destinationContentId) && !is_string($destinationContentId)) { + throw new InvalidArgumentType( + "\$value->destinationContentIds[$key]", + 'string|int', + $destinationContentId + ); + } + } + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * For this FieldType, the related objects IDs are returned, separated by ",". + * + * @param \Ibexa\Core\FieldType\RelationList\Value $value + * + * @return string + */ + protected function getSortInfo(BaseValue $value) + { + return implode(',', $value->destinationContentIds); + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\RelationList\Value $value + */ + public function fromHash($hash) + { + return new Value($hash['destinationContentIds']); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\RelationList\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + return ['destinationContentIds' => $value->destinationContentIds]; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + /** + * Returns relation data extracted from value. + * + * Not intended for \Ibexa\Contracts\Core\Repository\Values\Content\Relation::COMMON type relations, + * there is an API for handling those. + * + * @param \Ibexa\Core\FieldType\RelationList\Value $value + * + * @return array Hash with relation type as key and array of destination content ids as value. + * + * Example: + * + * array( + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::LINK => array( + * "contentIds" => array( 12, 13, 14 ), + * "locationIds" => array( 24 ) + * ), + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::EMBED => array( + * "contentIds" => array( 12 ), + * "locationIds" => array( 24, 45 ) + * ), + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::FIELD => array( 12 ) + * ) + * + */ + public function getRelations(SPIValue $value) + { + /* @var \Ibexa\Core\FieldType\RelationList\Value $value */ + return [ + Relation::FIELD => $value->destinationContentIds, + ]; + } + + /** + * Checks whether given selectionMethod is valid. + * + * @param int $selectionMethod + * + * @return bool + */ + private function isValidSelectionMethod($selectionMethod) + { + return in_array($selectionMethod, [ + self::SELECTION_BROWSE, + self::SELECTION_DROPDOWN, + self::SELECTION_LIST_WITH_RADIO_BUTTONS, + self::SELECTION_LIST_WITH_CHECKBOXES, + self::SELECTION_MULTIPLE_SELECTION_LIST, + self::SELECTION_TEMPLATE_BASED_MULTIPLE, + self::SELECTION_TEMPLATE_BASED_SINGLE, + ], true); + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezobjectrelationlist.name', 'ibexa_fieldtypes') + ->setDesc('Content relations (multiple)'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\RelationList\Type'); diff --git a/src/lib/FieldType/RelationList/Value.php b/src/lib/FieldType/RelationList/Value.php new file mode 100644 index 0000000000..023d9ffd76 --- /dev/null +++ b/src/lib/FieldType/RelationList/Value.php @@ -0,0 +1,39 @@ +destinationContentIds = $destinationContentIds; + } + + public function __toString() + { + return implode(',', $this->destinationContentIds); + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\RelationList\Value'); diff --git a/src/lib/FieldType/Selection/SearchField.php b/src/lib/FieldType/Selection/SearchField.php new file mode 100644 index 0000000000..de52b0a262 --- /dev/null +++ b/src/lib/FieldType/Selection/SearchField.php @@ -0,0 +1,103 @@ +fieldTypeConstraints->fieldSettings; + $positionSet = array_flip($field->value->data); + + $options = $fieldSettings['multilingualOptions'][$field->languageCode] ?? $fieldSettings['options']; + + foreach ($options as $index => $value) { + if (isset($positionSet[$index])) { + $values[] = $value; + $indexes[] = $index; + } + } + + return [ + new Search\Field( + 'selected_option_value', + $values, + new Search\FieldType\MultipleStringField() + ), + new Search\Field( + 'selected_option_index', + $indexes, + new Search\FieldType\MultipleIntegerField() + ), + new Search\Field( + 'selected_option_count', + count($indexes), + new Search\FieldType\IntegerField() + ), + new Search\Field( + 'sort_value', + implode('-', $indexes), + new Search\FieldType\StringField() + ), + new Search\Field( + 'fulltext', + $values, + new Search\FieldType\FullTextField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'selected_option_value' => new Search\FieldType\MultipleStringField(), + 'selected_option_index' => new Search\FieldType\MultipleIntegerField(), + 'selected_option_count' => new Search\FieldType\IntegerField(), + 'sort_value' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'selected_option_index'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return 'sort_value'; + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\Selection\SearchField'); diff --git a/src/lib/FieldType/Selection/Type.php b/src/lib/FieldType/Selection/Type.php new file mode 100644 index 0000000000..cfb091bf2a --- /dev/null +++ b/src/lib/FieldType/Selection/Type.php @@ -0,0 +1,322 @@ + [ + 'type' => 'bool', + 'default' => false, + ], + 'options' => [ + 'type' => 'hash', + 'default' => [], + ], + 'multilingualOptions' => [ + 'type' => 'hash', + 'default' => [], + ], + ]; + + /** + * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $fieldSettings + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateFieldSettings($fieldSettings) + { + $validationErrors = []; + + foreach ($fieldSettings as $settingKey => $settingValue) { + switch ($settingKey) { + case 'isMultiple': + if (!is_bool($settingValue)) { + $validationErrors[] = new ValidationError( + "FieldType '%fieldType%' expects setting '%setting%' to be of type '%type%'", + null, + [ + '%fieldType%' => $this->getFieldTypeIdentifier(), + '%setting%' => $settingKey, + '%type%' => 'bool', + ], + "[$settingKey]" + ); + } + break; + case 'options': + if (!is_array($settingValue)) { + $validationErrors[] = new ValidationError( + "FieldType '%fieldType%' expects setting '%setting%' to be of type '%type%'", + null, + [ + '%fieldType%' => $this->getFieldTypeIdentifier(), + '%setting%' => $settingKey, + '%type%' => 'hash', + ], + "[$settingKey]" + ); + } + break; + case 'multilingualOptions': + if (!is_array($settingValue) && !is_array(reset($settingValue))) { + $validationErrors[] = new ValidationError( + "FieldType '%fieldType%' expects setting '%setting%' to be of type '%type%'", + null, + [ + '%fieldType%' => $this->getFieldTypeIdentifier(), + '%setting%' => $settingKey, + '%type%' => 'hash', + ], + "[$settingKey]" + ); + } + break; + default: + $validationErrors[] = new ValidationError( + "Setting '%setting%' is unknown", + null, + [ + '%setting%' => $settingKey, + ], + "[$settingKey]" + ); + } + } + + return $validationErrors; + } + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezselection'; + } + + /** + * @param \Ibexa\Core\FieldType\Selection\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + if (empty($value->selection)) { + return ''; + } + + $names = []; + $fieldSettings = $fieldDefinition->getFieldSettings(); + + foreach ($value->selection as $optionIndex) { + if (isset($fieldSettings['multilingualOptions'][$languageCode][$optionIndex])) { + $names[] = $fieldSettings['multilingualOptions'][$languageCode][$optionIndex]; + } elseif (isset($fieldSettings['multilingualOptions'][$fieldDefinition->mainLanguageCode][$optionIndex])) { + $names[] = $fieldSettings['multilingualOptions'][$fieldDefinition->mainLanguageCode][$optionIndex]; + } elseif (isset($fieldSettings['options'][$optionIndex])) { + $names[] = $fieldSettings['options'][$optionIndex]; + } + } + + return implode(' ', $names); + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Selection\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param array|\Ibexa\Core\FieldType\Selection\Value $inputValue + * + * @return \Ibexa\Core\FieldType\Selection\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_array($inputValue)) { + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Selection\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!is_array($value->selection)) { + throw new InvalidArgumentType( + '$value->selection', + 'array', + $value->selection + ); + } + } + + /** + * Validates field value against 'isMultiple' and 'options' settings. + * + * Does not use validators. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field + * @param \Ibexa\Core\FieldType\Selection\Value $fieldValue The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) + { + $validationErrors = []; + + if ($this->isEmptyValue($fieldValue)) { + return $validationErrors; + } + + $fieldSettings = $fieldDefinition->getFieldSettings(); + + if ((!isset($fieldSettings['isMultiple']) || $fieldSettings['isMultiple'] === false) + && count($fieldValue->selection) > 1) { + $validationErrors[] = new ValidationError( + 'Field definition does not allow multiple options to be selected.', + null, + [], + 'selection' + ); + } + + foreach ($fieldValue->selection as $optionIndex) { + if (!isset($fieldSettings['options'][$optionIndex]) && empty($fieldSettings['multilingualOptions'])) { + $validationErrors[] = new ValidationError( + 'Option with index %index% does not exist in the field definition.', + null, + [ + '%index%' => $optionIndex, + ], + 'selection' + ); + } + } + + //@todo: find a way to include selection language + if (isset($fieldSettings['multilingualOptions'])) { + $possibleOptionIndexesByLanguage = array_map(static function ($languageOptionIndexes) { + return array_keys($languageOptionIndexes); + }, $fieldSettings['multilingualOptions']); + + $possibleOptionIndexes = array_merge(...array_values($possibleOptionIndexesByLanguage)); + + foreach ($fieldValue->selection as $optionIndex) { + if (!in_array($optionIndex, $possibleOptionIndexes)) { + $validationErrors[] = new ValidationError( + 'Option with index %index% does not exist in the field definition.', + null, + [ + '%index%' => $optionIndex, + ], + 'selection' + ); + } + } + } + + return $validationErrors; + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * @param \Ibexa\Core\FieldType\Selection\Value $value + * + * @return string + */ + protected function getSortInfo(BaseValue $value) + { + return implode('-', $value->selection); + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\Selection\Value $value + */ + public function fromHash($hash) + { + return new Value($hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Selection\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + return $value->selection; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezselection.name', 'ibexa_fieldtypes')->setDesc('Selection'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Selection\Type'); diff --git a/src/lib/FieldType/Selection/Value.php b/src/lib/FieldType/Selection/Value.php new file mode 100644 index 0000000000..455321d44b --- /dev/null +++ b/src/lib/FieldType/Selection/Value.php @@ -0,0 +1,39 @@ +selection = $selection; + } + + public function __toString() + { + return implode(',', $this->selection); + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\Selection\Value'); diff --git a/src/lib/FieldType/StorageGateway.php b/src/lib/FieldType/StorageGateway.php new file mode 100644 index 0000000000..4c696fb96c --- /dev/null +++ b/src/lib/FieldType/StorageGateway.php @@ -0,0 +1,32 @@ +extractShortText($field->value->data), + new Search\FieldType\StringField() + ), + new Search\Field( + 'fulltext', + $field->value->data, + new Search\FieldType\FullTextField() + ), + ]; + } + + /** + * Extracts short snippet of the given $string. + * + * @param string $string + * + * @return string + */ + private function extractShortText($string) + { + return mb_substr(strtok(trim((string)$string), "\r\n"), 0, 255); + } + + public function getIndexDefinition() + { + return [ + 'value' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\TextBlock\SearchField'); diff --git a/src/lib/FieldType/TextBlock/Type.php b/src/lib/FieldType/TextBlock/Type.php new file mode 100644 index 0000000000..a9184cabb2 --- /dev/null +++ b/src/lib/FieldType/TextBlock/Type.php @@ -0,0 +1,216 @@ + [ + 'type' => 'int', + 'default' => 10, + ], + ]; + + protected $validatorConfigurationSchema = []; + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'eztext'; + } + + /** + * @param \Ibexa\Core\FieldType\TextBlock\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + return (string)$value->text; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\TextBlock\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Returns if the given $value is considered empty by the field type. + * + * @param mixed $value + * + * @return bool + */ + public function isEmptyValue(SPIValue $value) + { + return $value->text === null || trim($value->text) === ''; + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param string|\Ibexa\Core\FieldType\TextBlock\Value $inputValue + * + * @return \Ibexa\Core\FieldType\TextBlock\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_string($inputValue)) { + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\TextBlock\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!is_string($value->text)) { + throw new InvalidArgumentType( + '$value->text', + 'string', + $value->text + ); + } + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * @param \Ibexa\Core\FieldType\TextBlock\Value $value + * + * @return string + */ + protected function getSortInfo(BaseValue $value) + { + return $this->transformationProcessor->transformByGroup( + mb_substr(strtok(trim($value->text), "\r\n"), 0, 255), + 'lowercase' + ); + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\TextBlock\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + return new Value($hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\TextBlock\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + return $value->text; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + /** + * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $fieldSettings + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateFieldSettings($fieldSettings) + { + $validationErrors = []; + + foreach ($fieldSettings as $name => $value) { + if (isset($this->settingsSchema[$name])) { + switch ($name) { + case 'textRows': + if (!is_int($value)) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' value must be of integer type", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + } + } else { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is unknown", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + } + + return $validationErrors; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('eztext.name', 'ibexa_fieldtypes')->setDesc('Text block'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\TextBlock\Type'); diff --git a/src/lib/FieldType/TextBlock/Value.php b/src/lib/FieldType/TextBlock/Value.php new file mode 100644 index 0000000000..edf9a30463 --- /dev/null +++ b/src/lib/FieldType/TextBlock/Value.php @@ -0,0 +1,18 @@ +value->data, + new Search\FieldType\StringField() + ), + new Search\Field( + 'fulltext', + $field->value->data, + new Search\FieldType\FullTextField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\TextLine\SearchField'); diff --git a/src/lib/FieldType/TextLine/Type.php b/src/lib/FieldType/TextLine/Type.php new file mode 100644 index 0000000000..465de19244 --- /dev/null +++ b/src/lib/FieldType/TextLine/Type.php @@ -0,0 +1,259 @@ + [ + 'minStringLength' => [ + 'type' => 'int', + 'default' => 0, + ], + 'maxStringLength' => [ + 'type' => 'int', + 'default' => null, + ], + ], + ]; + + /** + * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $validatorConfiguration + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateValidatorConfiguration($validatorConfiguration) + { + $validationErrors = []; + $validator = new StringLengthValidator(); + + foreach ($validatorConfiguration as $validatorIdentifier => $constraints) { + if ($validatorIdentifier !== 'StringLengthValidator') { + $validationErrors[] = new ValidationError( + "Validator '%validator%' is unknown", + null, + [ + '%validator%' => $validatorIdentifier, + ] + ); + continue; + } + $validationErrors += $validator->validateConstraints($constraints); + } + + return $validationErrors; + } + + /** + * Validates a field based on the validators in the field definition. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field + * @param \Ibexa\Core\FieldType\TextLine\Value $fieldValue The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) + { + $validationErrors = []; + + if ($this->isEmptyValue($fieldValue)) { + return $validationErrors; + } + + $validatorConfiguration = $fieldDefinition->getValidatorConfiguration(); + $constraints = isset($validatorConfiguration['StringLengthValidator']) + ? $validatorConfiguration['StringLengthValidator'] + : []; + + if (isset($constraints['maxStringLength']) && + $constraints['maxStringLength'] !== false && + $constraints['maxStringLength'] !== 0 && + mb_strlen($fieldValue->text) > $constraints['maxStringLength']) { + $validationErrors[] = new ValidationError( + 'The string can not exceed %size% character.', + 'The string can not exceed %size% characters.', + [ + '%size%' => $constraints['maxStringLength'], + ], + 'text' + ); + } + + if (isset($constraints['minStringLength']) && + $constraints['minStringLength'] !== false && + $constraints['minStringLength'] !== 0 && + mb_strlen($fieldValue->text) < $constraints['minStringLength']) { + $validationErrors[] = new ValidationError( + 'The string cannot be shorter than %size% character.', + 'The string cannot be shorter than %size% characters.', + [ + '%size%' => $constraints['minStringLength'], + ], + 'text' + ); + } + + return $validationErrors; + } + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'ezstring'; + } + + /** + * @param \Ibexa\Core\FieldType\TextLine\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + return (string)$value->text; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\TextLine\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Returns if the given $value is considered empty by the field type. + * + * @param mixed $value + * + * @return bool + */ + public function isEmptyValue(SPIValue $value) + { + return $value->text === null || trim($value->text) === ''; + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param string|\Ibexa\Core\FieldType\TextLine\Value $inputValue + * + * @return \Ibexa\Core\FieldType\TextLine\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_string($inputValue)) { + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\TextLine\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!is_string($value->text)) { + throw new InvalidArgumentType( + '$value->text', + 'string', + $value->text + ); + } + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * @param \Ibexa\Core\FieldType\TextLine\Value $value + * + * @return string + */ + protected function getSortInfo(BaseValue $value) + { + return $this->transformationProcessor->transformByGroup((string)$value, 'lowercase'); + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\TextLine\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + return new Value($hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\TextLine\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + return $value->text; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezstring.name', 'ibexa_fieldtypes')->setDesc('Text line'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\TextLine\Type'); diff --git a/src/lib/FieldType/TextLine/Value.php b/src/lib/FieldType/TextLine/Value.php new file mode 100644 index 0000000000..5425a88237 --- /dev/null +++ b/src/lib/FieldType/TextLine/Value.php @@ -0,0 +1,39 @@ +text = $text; + } + + public function __toString() + { + return (string)$this->text; + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\TextLine\Value'); diff --git a/src/lib/FieldType/Time/SearchField.php b/src/lib/FieldType/Time/SearchField.php new file mode 100644 index 0000000000..d7b750bbc5 --- /dev/null +++ b/src/lib/FieldType/Time/SearchField.php @@ -0,0 +1,66 @@ +value->data, + new Search\FieldType\IntegerField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value' => new Search\FieldType\IntegerField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\Time\SearchField'); diff --git a/src/lib/FieldType/Time/Type.php b/src/lib/FieldType/Time/Type.php new file mode 100644 index 0000000000..ec78fecd9a --- /dev/null +++ b/src/lib/FieldType/Time/Type.php @@ -0,0 +1,260 @@ + [ + 'type' => 'bool', + 'default' => false, + ], + // One of the DEFAULT_* class constants + 'defaultType' => [ + 'type' => 'choice', + 'default' => self::DEFAULT_EMPTY, + ], + ]; + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return 'eztime'; + } + + /** + * @param \Ibexa\Core\FieldType\Time\Value|\Ibexa\Contracts\Core\FieldType\Value $value + * + * @throws \Exception + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + if ($this->isEmptyValue($value)) { + return ''; + } + + $dateTime = new DateTime("@{$value->time}"); + + return $dateTime->format('g:i:s a'); + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Time\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param string|int|\DateTime|\Ibexa\Core\FieldType\Time\Value $inputValue + * + * @return \Ibexa\Core\FieldType\Time\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_string($inputValue)) { + $inputValue = Value::fromString($inputValue); + } + + if (is_int($inputValue)) { + $inputValue = Value::fromTimestamp($inputValue); + } + + if ($inputValue instanceof DateTime) { + $inputValue = Value::fromDateTime($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Time\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (!is_int($value->time)) { + throw new InvalidArgumentType( + '$value->time', + 'DateTime', + $value->time + ); + } + } + + /** + * Returns information for FieldValue->$sortKey relevant to the field type. + * + * @param \Ibexa\Core\FieldType\Time\Value $value + * + * @return int + */ + protected function getSortInfo(BaseValue $value) + { + return $value->time; + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param int $hash Number of seconds since Unix Epoch + * + * @return \Ibexa\Core\FieldType\Time\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + return new Value($hash); + } + + /** + * Returns if the given $value is considered empty by the field type. + * + * + * @param \Ibexa\Core\FieldType\Value $value + * + * @return bool + */ + public function isEmptyValue(SPIValue $value) + { + if ($value->time === null) { + return true; + } + + return false; + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Time\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + return $value->time; + } + + /** + * Returns whether the field type is searchable. + * + * @return bool + */ + public function isSearchable() + { + return true; + } + + /** + * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * @param mixed $fieldSettings + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateFieldSettings($fieldSettings) + { + $validationErrors = []; + + foreach ($fieldSettings as $name => $value) { + if (!isset($this->settingsSchema[$name])) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is unknown", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + continue; + } + + switch ($name) { + case 'useSeconds': + if (!is_bool($value)) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' value must be of boolean type", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + case 'defaultType': + $definedTypes = [ + self::DEFAULT_EMPTY, + self::DEFAULT_CURRENT_TIME, + ]; + if (!in_array($value, $definedTypes, true)) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is of unknown type", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + break; + } + } + + return $validationErrors; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('eztime.name', 'ibexa_fieldtypes')->setDesc('Time'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Time\Type'); diff --git a/src/lib/FieldType/Time/Value.php b/src/lib/FieldType/Time/Value.php new file mode 100644 index 0000000000..fec0f4c929 --- /dev/null +++ b/src/lib/FieldType/Time/Value.php @@ -0,0 +1,107 @@ +time = $seconds; + } + + /** + * Creates a Value from the given $dateTime. + * + * @param \DateTime $dateTime + * + * @return \Ibexa\Core\FieldType\Time\Value + */ + public static function fromDateTime(DateTime $dateTime) + { + $dateTime = clone $dateTime; + + return new static($dateTime->getTimestamp() - $dateTime->setTime(0, 0, 0)->getTimestamp()); + } + + /** + * Creates a Value from the given $timeString. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param string $timeString + * + * @return \Ibexa\Core\FieldType\Time\Value + */ + public static function fromString($timeString) + { + try { + return static::fromDateTime(new DateTime($timeString)); + } catch (Exception $e) { + throw new InvalidArgumentValue('$timeString', $timeString, __CLASS__, $e); + } + } + + /** + * Creates a Value from the given $timestamp. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param int $timestamp + * + * @return static + */ + public static function fromTimestamp($timestamp) + { + try { + $dateTime = new DateTime("@{$timestamp}"); + + return static::fromDateTime($dateTime); + } catch (Exception $e) { + throw new InvalidArgumentValue('$timestamp', $timestamp, __CLASS__, $e); + } + } + + public function __toString() + { + if ($this->time === null) { + return ''; + } + + $dateTime = new DateTime("@{$this->time}"); + + return $dateTime->format($this->stringFormat); + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\Time\Value'); diff --git a/src/lib/FieldType/Unindexed.php b/src/lib/FieldType/Unindexed.php new file mode 100644 index 0000000000..30fde7f162 --- /dev/null +++ b/src/lib/FieldType/Unindexed.php @@ -0,0 +1,55 @@ +value->externalData, + new Search\FieldType\StringField() + ), + new Search\Field( + 'value_id', + isset($field->value->data['urlId']) ? $field->value->data['urlId'] : '', + new Search\FieldType\StringField() + ), + new Search\Field( + 'value_text', + $text = (isset($field->value->data['text']) ? $field->value->data['text'] : ''), + new Search\FieldType\StringField() + ), + new Search\Field( + 'fulltext', + $text, + new Search\FieldType\FullTextField() + ), + ]; + } + + public function getIndexDefinition() + { + return [ + 'value_url' => new Search\FieldType\StringField(), + 'value_id' => new Search\FieldType\StringField(), + 'value_text' => new Search\FieldType\StringField(), + ]; + } + + /** + * Get name of the default field to be used for matching. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for matching. Default field is typically used by Field criterion. + * + * @return string + */ + public function getDefaultMatchField() + { + return 'value_url'; + } + + /** + * Get name of the default field to be used for sorting. + * + * As field types can index multiple fields (see MapLocation field type's + * implementation of this interface), this method is used to define default + * field for sorting. Default field is typically used by Field sort clause. + * + * @return string + */ + public function getDefaultSortField() + { + return $this->getDefaultMatchField(); + } +} + +class_alias(SearchField::class, 'eZ\Publish\Core\FieldType\Url\SearchField'); diff --git a/src/lib/FieldType/Url/Type.php b/src/lib/FieldType/Url/Type.php new file mode 100644 index 0000000000..6650a591f1 --- /dev/null +++ b/src/lib/FieldType/Url/Type.php @@ -0,0 +1,193 @@ +text; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\Url\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param string|\Ibexa\Core\FieldType\Url\Value $inputValue + * + * @return \Ibexa\Core\FieldType\Url\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_string($inputValue)) { + $inputValue = new Value($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\Url\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + if (null !== $value->link && !is_string($value->link)) { + throw new InvalidArgumentType( + '$value->link', + 'string', + $value->link + ); + } + + if (null !== $value->text && !is_string($value->text)) { + throw new InvalidArgumentType( + '$value->text', + 'string', + $value->text + ); + } + } + + /** + * {@inheritdoc} + */ + protected function getSortInfo(BaseValue $value) + { + return false; + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\Url\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + if (isset($hash['text'])) { + return new Value($hash['link'], $hash['text']); + } + + return new Value($hash['link']); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\Url\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + return ['link' => $value->link, 'text' => $value->text]; + } + + public function toPersistenceValue(SPIValue $value) + { + if ($value === null) { + return new FieldValue( + [ + 'data' => [], + 'externalData' => null, + 'sortKey' => null, + ] + ); + } + + return new FieldValue( + [ + 'data' => [ + 'urlId' => null, + 'text' => $value->text, + ], + 'externalData' => $value->link, + 'sortKey' => $this->getSortInfo($value), + ] + ); + } + + /** + * Converts a persistence $fieldValue to a Value. + * + * This method builds a field type value from the $data and $externalData properties. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + * + * @return \Ibexa\Core\FieldType\Url\Value + */ + public function fromPersistenceValue(FieldValue $fieldValue) + { + if ($fieldValue->externalData === null) { + return $this->getEmptyValue(); + } + + return new Value( + $fieldValue->externalData, + $fieldValue->data['text'] + ); + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezurl.name', 'ibexa_fieldtypes')->setDesc('URL'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\Url\Type'); diff --git a/src/lib/FieldType/Url/UrlStorage.php b/src/lib/FieldType/Url/UrlStorage.php new file mode 100644 index 0000000000..ff448030e2 --- /dev/null +++ b/src/lib/FieldType/Url/UrlStorage.php @@ -0,0 +1,135 @@ +logger = $logger; + } + + /** + * @see \Ibexa\Contracts\Core\FieldType\FieldStorage + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param array $context + * + * @return bool|mixed + */ + public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context) + { + $url = $field->value->externalData; + + if (empty($url)) { + return false; + } + + $map = $this->gateway->getUrlIdMap([$url]); + + if (isset($map[$url])) { + $urlId = $map[$url]; + } else { + $urlId = $this->gateway->insertUrl($url); + } + + $this->gateway->linkUrl($urlId, $field->id, $versionInfo->versionNo); + + $this->gateway->unlinkUrl( + $field->id, + $versionInfo->versionNo, + [$urlId] + ); + + $field->value->data['urlId'] = $urlId; + + // Signals that the Value has been modified and that an update is to be performed + return true; + } + + public function getFieldData(VersionInfo $versionInfo, Field $field, array $context) + { + $id = $field->value->data['urlId']; + if (empty($id)) { + $field->value->externalData = null; + + return; + } + + $map = $this->gateway->getIdUrlMap([$id]); + + // URL id is not in the DB + if (!isset($map[$id]) && isset($this->logger)) { + $this->logger->error("URL with ID '{$id}' not found"); + } + + $field->value->externalData = isset($map[$id]) ? $map[$id] : ''; + } + + /** + * Deletes field data for all $fieldIds in the version identified by + * $versionInfo. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param array $fieldIds + * @param array $context + * + * @return bool + */ + public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context) + { + foreach ($fieldIds as $fieldId) { + $this->gateway->unlinkUrl($fieldId, $versionInfo->versionNo); + } + } + + /** + * Checks if field type has external data to deal with. + * + * @return bool + */ + public function hasFieldData() + { + return true; + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param array $context + * + * @return \Ibexa\Contracts\Core\Search\Field[] + */ + public function getIndexData(VersionInfo $versionInfo, Field $field, array $context) + { + } +} + +class_alias(UrlStorage::class, 'eZ\Publish\Core\FieldType\Url\UrlStorage'); diff --git a/src/lib/FieldType/Url/UrlStorage/Gateway.php b/src/lib/FieldType/Url/UrlStorage/Gateway.php new file mode 100644 index 0000000000..679ab0dbd5 --- /dev/null +++ b/src/lib/FieldType/Url/UrlStorage/Gateway.php @@ -0,0 +1,67 @@ +connection = $connection; + } + + /** + * Return a list of URLs for a list of URL ids. + * + * Non-existent ids are ignored. + * + * @param int[] $ids An array of URL ids + * + * @return array An array of URLs, with ids as keys + */ + public function getIdUrlMap(array $ids) + { + $map = []; + + if (!empty($ids)) { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + $this->connection->quoteIdentifier('id'), + $this->connection->quoteIdentifier('url') + ) + ->from(self::URL_TABLE) + ->where('id IN (:ids)') + ->setParameter(':ids', $ids, Connection::PARAM_INT_ARRAY); + + $statement = $query->execute(); + foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $row) { + $map[$row['id']] = $row['url']; + } + } + + return $map; + } + + /** + * Return a list of URL ids for a list of URLs. + * + * Non-existent URLs are ignored. + * + * @param string[] $urls An array of URLs + * + * @return array An array of URL ids, with URLs as keys + */ + public function getUrlIdMap(array $urls) + { + $map = []; + + if (!empty($urls)) { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + $this->connection->quoteIdentifier('id'), + $this->connection->quoteIdentifier('url') + ) + ->from(self::URL_TABLE) + ->where( + $query->expr()->in('url', ':urls') + ) + ->setParameter(':urls', $urls, Connection::PARAM_STR_ARRAY); + + $statement = $query->execute(); + foreach ($statement->fetchAll(PDO::FETCH_ASSOC) as $row) { + $map[$row['url']] = $row['id']; + } + } + + return $map; + } + + /** + * Insert a new $url and returns its id. + * + * @param string $url The URL to insert in the database + * + * @return string + */ + public function insertUrl($url) + { + $time = time(); + + $query = $this->connection->createQueryBuilder(); + + $query + ->insert($this->connection->quoteIdentifier(self::URL_TABLE)) + ->values( + [ + 'created' => ':created', + 'modified' => ':modified', + 'original_url_md5' => ':original_url_md5', + 'url' => ':url', + ] + ) + ->setParameter(':created', $time, PDO::PARAM_INT) + ->setParameter(':modified', $time, PDO::PARAM_INT) + ->setParameter(':original_url_md5', md5($url)) + ->setParameter(':url', $url) + ; + + $query->execute(); + + return (int)$this->connection->lastInsertId( + $this->getSequenceName(self::URL_TABLE, 'id') + ); + } + + /** + * Create link to URL with $urlId for field with $fieldId in $versionNo. + * + * @param int $urlId + * @param int $fieldId + * @param int $versionNo + */ + public function linkUrl($urlId, $fieldId, $versionNo) + { + $query = $this->connection->createQueryBuilder(); + + $query + ->insert($this->connection->quoteIdentifier(self::URL_LINK_TABLE)) + ->values( + [ + 'contentobject_attribute_id' => ':contentobject_attribute_id', + 'contentobject_attribute_version' => ':contentobject_attribute_version', + 'url_id' => ':url_id', + ] + ) + ->setParameter(':contentobject_attribute_id', $fieldId, PDO::PARAM_INT) + ->setParameter(':contentobject_attribute_version', $versionNo, PDO::PARAM_INT) + ->setParameter(':url_id', $urlId, PDO::PARAM_INT) + ; + + $query->execute(); + } + + /** + * Remove link to URL for $fieldId in $versionNo and cleans up possibly orphaned URLs. + * + * @param int $fieldId + * @param int $versionNo + * @param int[] $excludeUrlIds + */ + public function unlinkUrl($fieldId, $versionNo, array $excludeUrlIds = []): void + { + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery + ->select('link.url_id') + ->from($this->connection->quoteIdentifier(self::URL_LINK_TABLE), 'link') + ->where( + $selectQuery->expr()->andX( + $selectQuery->expr()->in( + 'link.contentobject_attribute_id', + ':contentobject_attribute_id' + ), + $selectQuery->expr()->in( + 'link.contentobject_attribute_version', + ':contentobject_attribute_version' + ) + ) + ) + ->setParameter(':contentobject_attribute_id', $fieldId, ParameterType::INTEGER) + ->setParameter(':contentobject_attribute_version', $versionNo, ParameterType::INTEGER); + + $statement = $selectQuery->execute(); + $potentiallyOrphanedUrls = $statement->fetchFirstColumn(); + + if (empty($potentiallyOrphanedUrls)) { + return; + } + + $deleteQuery = $this->connection->createQueryBuilder(); + $deleteQuery + ->delete($this->connection->quoteIdentifier(self::URL_LINK_TABLE)) + ->where( + $deleteQuery->expr()->and( + $deleteQuery->expr()->in( + 'contentobject_attribute_id', + ':contentobject_attribute_id' + ), + $deleteQuery->expr()->in( + 'contentobject_attribute_version', + ':contentobject_attribute_version' + ) + ) + ) + ->setParameter(':contentobject_attribute_id', $fieldId, ParameterType::INTEGER) + ->setParameter(':contentobject_attribute_version', $versionNo, ParameterType::INTEGER); + + if (empty($excludeUrlIds) === false) { + $deleteQuery + ->andWhere( + $deleteQuery->expr()->notIn( + 'url_id', + ':url_ids' + ) + ) + ->setParameter('url_ids', $excludeUrlIds, Connection::PARAM_INT_ARRAY); + } + + $deleteQuery->execute(); + + $this->deleteOrphanedUrls($potentiallyOrphanedUrls); + } + + /** + * Delete potentially orphaned URLs. + * + * That could be avoided if the feature is implemented there. + * + * URL is orphaned if it is not linked to a content attribute through ezurl_object_link table. + * + * @param int[] $potentiallyOrphanedUrls + */ + private function deleteOrphanedUrls(array $potentiallyOrphanedUrls): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->select($this->connection->quoteIdentifier('url.id')) + ->from($this->connection->quoteIdentifier(self::URL_TABLE), 'url') + ->leftJoin( + 'url', + $this->connection->quoteIdentifier(self::URL_LINK_TABLE), + 'link', + 'url.id = link.url_id' + ) + ->where( + $query->expr()->in( + 'url.id', + ':url_ids' + ) + ) + ->andWhere($query->expr()->isNull('link.url_id')) + ->setParameter('url_ids', $potentiallyOrphanedUrls, Connection::PARAM_INT_ARRAY) + ; + + $statement = $query->execute(); + + $ids = $statement->fetchAll(PDO::FETCH_COLUMN); + if (empty($ids)) { + return; + } + + $deleteQuery = $this->connection->createQueryBuilder(); + $deleteQuery + ->delete($this->connection->quoteIdentifier(self::URL_TABLE)) + ->where($deleteQuery->expr()->in('id', ':ids')) + ->setParameter(':ids', $ids, Connection::PARAM_STR_ARRAY) + ; + + $deleteQuery->execute(); + } +} + +class_alias(DoctrineStorage::class, 'eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage'); diff --git a/src/lib/FieldType/Url/Value.php b/src/lib/FieldType/Url/Value.php new file mode 100644 index 0000000000..5f234fc0d7 --- /dev/null +++ b/src/lib/FieldType/Url/Value.php @@ -0,0 +1,48 @@ +link = $link; + $this->text = $text; + } + + public function __toString() + { + return (string)$this->link; + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\Url\Value'); diff --git a/src/lib/FieldType/User/Type.php b/src/lib/FieldType/User/Type.php new file mode 100644 index 0000000000..97d9611bee --- /dev/null +++ b/src/lib/FieldType/User/Type.php @@ -0,0 +1,557 @@ + [ + 'type' => 'int', + 'default' => null, + ], + self::PASSWORD_TTL_WARNING_SETTING => [ + 'type' => 'int', + 'default' => null, + ], + self::REQUIRE_UNIQUE_EMAIL => [ + 'type' => 'bool', + 'default' => true, + ], + self::USERNAME_PATTERN => [ + 'type' => 'string', + 'default' => '^[^@]+$', + ], + ]; + + /** @var array */ + protected $validatorConfigurationSchema = [ + 'PasswordValueValidator' => [ + 'requireAtLeastOneUpperCaseCharacter' => [ + 'type' => 'int', + 'default' => 1, + ], + 'requireAtLeastOneLowerCaseCharacter' => [ + 'type' => 'int', + 'default' => 1, + ], + 'requireAtLeastOneNumericCharacter' => [ + 'type' => 'int', + 'default' => 1, + ], + 'requireAtLeastOneNonAlphanumericCharacter' => [ + 'type' => 'int', + 'default' => null, + ], + 'requireNewPassword' => [ + 'type' => 'int', + 'default' => null, + ], + 'requireNotCompromisedPassword' => [ + 'type' => 'bool', + 'default' => false, + ], + 'minLength' => [ + 'type' => 'int', + 'default' => 10, + ], + ], + ]; + + /** @var \Ibexa\Contracts\Core\Persistence\User\Handler */ + private $userHandler; + + /** @var \Ibexa\Contracts\Core\Repository\PasswordHashService */ + private $passwordHashService; + + /** @var \Ibexa\Core\Repository\User\PasswordValidatorInterface */ + private $passwordValidator; + + public function __construct( + SPIUserHandler $userHandler, + PasswordHashService $passwordHashGenerator, + PasswordValidatorInterface $passwordValidator + ) { + $this->userHandler = $userHandler; + $this->passwordHashService = $passwordHashGenerator; + $this->passwordValidator = $passwordValidator; + } + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier() + { + return self::FIELD_TYPE_IDENTIFIER; + } + + /** + * @param \Ibexa\Core\FieldType\User\Value|\Ibexa\Contracts\Core\FieldType\Value $value + */ + public function getName(SPIValue $value, FieldDefinition $fieldDefinition, string $languageCode): string + { + return (string)$value->login; + } + + /** + * Indicates if the field definition of this type can appear only once in the same ContentType. + * + * @return bool + */ + public function isSingular() + { + return true; + } + + /** + * Indicates if the field definition of this type can be added to a ContentType with Content instances. + * + * @return bool + */ + public function onlyEmptyInstance() + { + return true; + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return \Ibexa\Core\FieldType\User\Value + */ + public function getEmptyValue() + { + return new Value(); + } + + /** + * Inspects given $inputValue and potentially converts it into a dedicated value object. + * + * @param array|\Ibexa\Core\FieldType\User\Value $inputValue + * + * @return \Ibexa\Core\FieldType\User\Value The potentially converted and structurally plausible value. + */ + protected function createValueFromInput($inputValue) + { + if (is_array($inputValue)) { + $inputValue = $this->fromHash($inputValue); + } + + return $inputValue; + } + + /** + * Throws an exception if value structure is not of expected format. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected structure. + * + * @param \Ibexa\Core\FieldType\User\Value $value + */ + protected function checkValueStructure(BaseValue $value) + { + // Does nothing + } + + /** + * {@inheritdoc} + */ + protected function getSortInfo(BaseValue $value) + { + return false; + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return \Ibexa\Core\FieldType\User\Value $value + */ + public function fromHash($hash) + { + if ($hash === null) { + return $this->getEmptyValue(); + } + + if (isset($hash['passwordUpdatedAt']) && $hash['passwordUpdatedAt'] !== null) { + $hash['passwordUpdatedAt'] = new DateTimeImmutable('@' . $hash['passwordUpdatedAt']); + } + + return new Value($hash); + } + + /** + * Converts a $Value to a hash. + * + * @param \Ibexa\Core\FieldType\User\Value $value + * + * @return mixed + */ + public function toHash(SPIValue $value) + { + if ($this->isEmptyValue($value)) { + return null; + } + + $hash = (array)$value; + if ($hash['passwordUpdatedAt'] instanceof DateTimeInterface) { + $hash['passwordUpdatedAt'] = $hash['passwordUpdatedAt']->getTimestamp(); + } + + return $hash; + } + + public function toPersistenceValue(SPIValue $value) + { + $value->passwordHashType = $this->getPasswordHashTypeForPersistenceValue($value); + if ($value->plainPassword) { + $value->passwordHash = $this->passwordHashService->createPasswordHash( + $value->plainPassword, + $value->passwordHashType + ); + $value->passwordUpdatedAt = new DateTimeImmutable(); + } + + return new FieldValue( + [ + 'data' => null, + 'externalData' => $this->toHash($value), + 'sortKey' => null, + ] + ); + } + + private function getPasswordHashTypeForPersistenceValue(SPIValue $value): int + { + if (null === $value->passwordHashType) { + return $this->passwordHashService->getDefaultHashType(); + } + + if (!$this->passwordHashService->isHashTypeSupported($value->passwordHashType)) { + return $this->passwordHashService->getDefaultHashType(); + } + + return $value->passwordHashType; + } + + /** + * Converts a persistence $fieldValue to a Value. + * + * This method builds a field type value from the $data and $externalData properties. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + * + * @return \Ibexa\Core\FieldType\User\Value + */ + public function fromPersistenceValue(FieldValue $fieldValue) + { + return $this->acceptValue($fieldValue->externalData); + } + + /** + * Validates a field based on the validators in the field definition. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition The field definition of the field + * @param \Ibexa\Core\FieldType\User\Value $fieldValue The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(FieldDefinition $fieldDefinition, SPIValue $fieldValue) + { + $errors = []; + + if ($this->isEmptyValue($fieldValue)) { + return $errors; + } + + if (!is_string($fieldValue->login) || empty($fieldValue->login)) { + $errors[] = new ValidationError( + 'Login required', + null, + [], + 'username' + ); + } + + $pattern = sprintf('/%s/', $fieldDefinition->fieldSettings[self::USERNAME_PATTERN]); + $loginFormatValid = preg_match($pattern, $fieldValue->login); + if (!$fieldValue->hasStoredLogin && !$loginFormatValid) { + $errors[] = new ValidationError( + 'Invalid login format', + null, + [], + 'username' + ); + } + + if (!is_string($fieldValue->email) || empty($fieldValue->email)) { + $errors[] = new ValidationError( + 'Email required', + null, + [], + 'email' + ); + } elseif (false === filter_var($fieldValue->email, FILTER_VALIDATE_EMAIL)) { + $errors[] = new ValidationError( + "The given e-mail '%email%' is invalid", + null, + ['%email%' => $fieldValue->email], + 'email' + ); + } + + if (!$fieldValue->hasStoredLogin && (!is_string($fieldValue->plainPassword) || empty($fieldValue->plainPassword))) { + $errors[] = new ValidationError( + 'Password required', + null, + [], + 'password' + ); + } + + if (!is_bool($fieldValue->enabled)) { + $errors[] = new ValidationError( + 'Enabled must be boolean value', + null, + [], + 'enabled' + ); + } + + if (!$fieldValue->hasStoredLogin && isset($fieldValue->login)) { + try { + $login = $fieldValue->login; + $this->userHandler->loadByLogin($login); + + // If you want to change this ValidationError message, please remember to change it also in Content Forms in lib/Validator/Constraints/FieldValueValidatorMessages class + $errors[] = new ValidationError( + "The user login '%login%' is used by another user. You must enter a unique login.", + null, + [ + '%login%' => $login, + ], + 'username' + ); + } catch (NotFoundException $e) { + // Do nothing + } + } + + if ($fieldDefinition->fieldSettings[self::REQUIRE_UNIQUE_EMAIL]) { + try { + $email = $fieldValue->email; + try { + $user = $this->userHandler->loadByEmail($email); + } catch (LogicException $exception) { + // There are multiple users with the same email + } + + // Don't prevent email update + if (empty($user) || $user->id != $fieldValue->contentId) { + // If you want to change this ValidationError message, please remember to change it also in Content Forms in lib/Validator/Constraints/FieldValueValidatorMessages class + $errors[] = new ValidationError( + "Email '%email%' is used by another user. You must enter a unique email.", + null, + [ + '%email%' => $email, + ], + 'email' + ); + } + } catch (NotFoundException $e) { + // Do nothing + } + } + + if (!empty($fieldValue->plainPassword)) { + $passwordValidationErrors = $this->passwordValidator->validatePassword( + $fieldValue->plainPassword, + $fieldDefinition + ); + + $errors = array_merge($errors, $passwordValidationErrors); + + if (!empty($fieldValue->passwordHash) && $this->isNewPasswordRequired($fieldDefinition)) { + $isPasswordReused = $this->passwordHashService->isValidPassword( + $fieldValue->plainPassword, + $fieldValue->passwordHash, + $fieldValue->passwordHashType + ); + + if ($isPasswordReused) { + $errors[] = new ValidationError('New password cannot be the same as old password', null, [], 'password'); + } + } + } + + return $errors; + } + + /** + * {@inheritdoc} + */ + public function validateValidatorConfiguration($validatorConfiguration) + { + $validationErrors = []; + + foreach ((array)$validatorConfiguration as $validatorIdentifier => $constraints) { + if ($validatorIdentifier !== 'PasswordValueValidator') { + $validationErrors[] = new ValidationError( + "Validator '%validator%' is unknown", + null, + [ + 'validator' => $validatorIdentifier, + ], + "[$validatorIdentifier]" + ); + } + } + + return $validationErrors; + } + + /** + * {@inheritdoc} + */ + public function validateFieldSettings($fieldSettings) + { + $validationErrors = []; + + foreach ($fieldSettings as $name => $value) { + if (!isset($this->settingsSchema[$name])) { + $validationErrors[] = new ValidationError( + "Setting '%setting%' is unknown", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + + continue; + } + + $error = null; + switch ($name) { + case self::PASSWORD_TTL_SETTING: + $error = $this->validatePasswordTTLSetting($name, $value); + break; + case self::PASSWORD_TTL_WARNING_SETTING: + $error = $this->validatePasswordTTLWarningSetting($name, $value, $fieldSettings); + break; + } + + if ($error !== null) { + $validationErrors[] = $error; + } + } + + return $validationErrors; + } + + private function validatePasswordTTLSetting(string $name, $value): ?ValidationError + { + if ($value !== null && !is_int($value)) { + return new ValidationError( + "Setting '%setting%' value must be of integer type", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + + return null; + } + + private function validatePasswordTTLWarningSetting(string $name, $value, $fieldSettings): ?ValidationError + { + if ($value !== null) { + if (!is_int($value)) { + return new ValidationError( + "Setting '%setting%' value must be of integer type", + null, + [ + '%setting%' => $name, + ], + "[$name]" + ); + } + + if ($value > 0) { + $passwordTTL = $fieldSettings[self::PASSWORD_TTL_SETTING] ?? null; + if ($value >= (int)$passwordTTL) { + return new ValidationError( + 'Password expiration warning value should be lower then password expiration value', + null, + [], + "[$name]" + ); + } + } + } + + return null; + } + + private function isNewPasswordRequired(FieldDefinition $fieldDefinition): bool + { + $isExplicitRequired = $fieldDefinition->validatorConfiguration['PasswordValueValidator']['requireNewPassword'] ?? false; + if ($isExplicitRequired) { + return true; + } + + return $this->isPasswordTTLEnabled($fieldDefinition); + } + + private function isPasswordTTLEnabled(FieldDefinition $fieldDefinition): bool + { + return ($fieldDefinition->fieldSettings[self::PASSWORD_TTL_SETTING] ?? null) > 0; + } + + public static function getTranslationMessages(): array + { + return [ + Message::create('ezuser.name', 'ibexa_fieldtypes')->setDesc('User account'), + ]; + } +} + +class_alias(Type::class, 'eZ\Publish\Core\FieldType\User\Type'); diff --git a/src/lib/FieldType/User/UserStorage.php b/src/lib/FieldType/User/UserStorage.php new file mode 100644 index 0000000000..d682e5bac0 --- /dev/null +++ b/src/lib/FieldType/User/UserStorage.php @@ -0,0 +1,91 @@ +gateway->storeFieldData($versionInfo, $field); + } + + public function getFieldData(VersionInfo $versionInfo, Field $field, array $context) + { + $field->value->externalData = $this->gateway->getFieldData($field->id); + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param int[] $fieldIds Array of field Ids + * @param array $context + * + * @return bool + * + * @throws \Doctrine\DBAL\DBALException + */ + public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context) + { + return $this->gateway->deleteFieldData($versionInfo, $fieldIds); + } + + /** + * Checks if field type has external data to deal with. + * + * @return bool + */ + public function hasFieldData() + { + return true; + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param array $context + * + * @return \Ibexa\Contracts\Core\Search\Field[] + */ + public function getIndexData(VersionInfo $versionInfo, Field $field, array $context) + { + } + + /** + * @param int[] $supportedHashTypes + */ + public function countUsersWithUnsupportedHashType(array $supportedHashTypes): int + { + return $this->gateway->countUsersWithUnsupportedHashType($supportedHashTypes); + } +} + +class_alias(UserStorage::class, 'eZ\Publish\Core\FieldType\User\UserStorage'); diff --git a/src/lib/FieldType/User/UserStorage/Gateway.php b/src/lib/FieldType/User/UserStorage/Gateway.php new file mode 100644 index 0000000000..0d6218c621 --- /dev/null +++ b/src/lib/FieldType/User/UserStorage/Gateway.php @@ -0,0 +1,59 @@ + false, + 'contentId' => null, + 'login' => null, + 'email' => null, + 'passwordHash' => null, + 'passwordHashType' => null, + 'passwordUpdatedAt' => null, + 'enabled' => false, + 'maxLogin' => null, + ]; + + public function __construct( + Connection $connection + ) { + $this->connection = $connection; + } + + /** + * {@inheritdoc} + */ + public function getFieldData($fieldId, $userId = null) + { + $userId = $userId ?: $this->fetchUserId($fieldId); + $userData = $this->fetchUserData($userId); + + if (!isset($userData['login'])) { + return $this->defaultValues; + } + + $result = array_merge( + $this->defaultValues, + [ + 'hasStoredLogin' => true, + ], + $userData, + $this->fetchUserSettings($userId) + ); + + return $result; + } + + /** + * Map legacy database column names to property names. + * + * @return array + */ + protected function getPropertyMap() + { + return [ + 'has_stored_login' => [ + 'name' => 'hasStoredlogin', + 'cast' => static function ($input) { + return $input == '1'; + }, + ], + 'contentobject_id' => [ + 'name' => 'contentId', + 'cast' => 'intval', + ], + 'login' => [ + 'name' => 'login', + 'cast' => 'strval', + ], + 'email' => [ + 'name' => 'email', + 'cast' => 'strval', + ], + 'password_hash' => [ + 'name' => 'passwordHash', + 'cast' => 'strval', + ], + 'password_hash_type' => [ + 'name' => 'passwordHashType', + 'cast' => 'strval', + ], + 'password_updated_at' => [ + 'name' => 'passwordUpdatedAt', + 'cast' => static function ($timestamp) { + return $timestamp ? (int)$timestamp : null; + }, + ], + 'is_enabled' => [ + 'name' => 'enabled', + 'cast' => static function ($input) { + return $input == '1'; + }, + ], + 'max_login' => [ + 'name' => 'maxLogin', + 'cast' => 'intval', + ], + ]; + } + + /** + * Convert the given database values to properties. + * + * @param array $databaseValues + * + * @return array + */ + protected function convertColumnsToProperties(array $databaseValues) + { + $propertyValues = []; + $propertyMap = $this->getPropertyMap(); + + foreach ($databaseValues as $columnName => $value) { + $conversionFunction = $propertyMap[$columnName]['cast']; + + $propertyValues[$propertyMap[$columnName]['name']] = $conversionFunction($value); + } + + return $propertyValues; + } + + /** + * Fetch user content object id for the given field id. + * + * @param int $fieldId + * + * @return int + */ + protected function fetchUserId($fieldId) + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + $this->connection->quoteIdentifier('contentobject_id') + ) + ->from($this->connection->quoteIdentifier('ezcontentobject_attribute')) + ->where( + $query->expr()->eq( + $this->connection->quoteIdentifier('id'), + ':fieldId' + ) + ) + ->setParameter(':fieldId', $fieldId, PDO::PARAM_INT) + ; + + $statement = $query->execute(); + + return (int) $statement->fetchColumn(); + } + + /** + * Fetch user data. + * + * @param int $userId + * + * @return array + */ + protected function fetchUserData($userId) + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + $this->connection->quoteIdentifier('usr.contentobject_id'), + $this->connection->quoteIdentifier('usr.login'), + $this->connection->quoteIdentifier('usr.email'), + $this->connection->quoteIdentifier('usr.password_hash'), + $this->connection->quoteIdentifier('usr.password_hash_type'), + $this->connection->quoteIdentifier('usr.password_updated_at') + ) + ->from($this->connection->quoteIdentifier(self::USER_TABLE), 'usr') + ->where( + $query->expr()->eq( + $this->connection->quoteIdentifier('usr.contentobject_id'), + ':userId' + ) + ) + ->setParameter(':userId', $userId, PDO::PARAM_INT) + ; + + $statement = $query->execute(); + + $rows = $statement->fetchAll(PDO::FETCH_ASSOC); + + return isset($rows[0]) ? $this->convertColumnsToProperties($rows[0]) : []; + } + + /** + * Fetch user settings. + * + * @param int $userId + * + * @return array + */ + protected function fetchUserSettings($userId) + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + $this->connection->quoteIdentifier('s.is_enabled'), + $this->connection->quoteIdentifier('s.max_login') + ) + ->from($this->connection->quoteIdentifier(self::USER_SETTING_TABLE), 's') + ->where( + $query->expr()->eq( + $this->connection->quoteIdentifier('s.user_id'), + ':userId' + ) + ) + ->setParameter(':userId', $userId, PDO::PARAM_INT) + ; + + $statement = $query->execute(); + + $rows = $statement->fetchAll(PDO::FETCH_ASSOC); + + return isset($rows[0]) ? $this->convertColumnsToProperties($rows[0]) : []; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + */ + public function storeFieldData(VersionInfo $versionInfo, Field $field): bool + { + if ($field->value->externalData === null) { + //to avoid unnecessary modifications when provided field is empty (like missing data for languageCode) + return false; + } + + try { + if (!empty($this->fetchUserData($versionInfo->contentInfo->id))) { + $this->updateFieldData($versionInfo, $field); + } else { + $this->insertFieldData($versionInfo, $field); + } + } catch (UniqueConstraintViolationException $e) { + throw new ForbiddenException( + 'User "%login%" already exists', + [ + '%login%' => $field->value->externalData['login'] ?? '?', + ] + ); + } + + return true; + } + + protected function insertFieldData(VersionInfo $versionInfo, Field $field): void + { + $insertQuery = $this->connection->createQueryBuilder(); + + $insertQuery + ->insert($this->connection->quoteIdentifier(self::USER_TABLE)) + ->setValue('contentobject_id', ':userId') + ->setValue('login', ':login') + ->setValue('email', ':email') + ->setValue('password_hash', ':passwordHash') + ->setValue('password_hash_type', ':passwordHashType') + ->setValue('password_updated_at', ':passwordUpdatedAt') + ->setParameter(':userId', $versionInfo->contentInfo->id, ParameterType::INTEGER) + ->setParameter(':login', $field->value->externalData['login'], ParameterType::STRING) + ->setParameter(':email', $field->value->externalData['email'], ParameterType::STRING) + ->setParameter(':passwordHash', $field->value->externalData['passwordHash'], ParameterType::STRING) + ->setParameter(':passwordHashType', $field->value->externalData['passwordHashType'], ParameterType::INTEGER) + ->setParameter(':passwordUpdatedAt', $field->value->externalData['passwordUpdatedAt']) + ; + + $insertQuery->execute(); + + $settingsQuery = $this->connection->createQueryBuilder(); + + $settingsQuery + ->insert($this->connection->quoteIdentifier(self::USER_SETTING_TABLE)) + ->setValue('user_id', ':userId') + ->setValue('is_enabled', ':isEnabled') + ->setValue('max_login', ':maxLogin') + ->setParameter(':userId', $versionInfo->contentInfo->id, ParameterType::INTEGER) + ->setParameter(':isEnabled', $field->value->externalData['enabled'], ParameterType::INTEGER) + ->setParameter(':maxLogin', $field->value->externalData['maxLogin'], ParameterType::INTEGER); + + $settingsQuery->execute(); + } + + protected function updateFieldData(VersionInfo $versionInfo, Field $field): void + { + $queryBuilder = $this->connection->createQueryBuilder(); + + $queryBuilder + ->update($this->connection->quoteIdentifier(self::USER_TABLE)) + ->set('login', ':login') + ->set('email', ':email') + ->set('password_hash', ':passwordHash') + ->set('password_hash_type', ':passwordHashType') + ->set('password_updated_at', ':passwordUpdatedAt') + ->setParameter(':login', $field->value->externalData['login'], ParameterType::STRING) + ->setParameter(':email', $field->value->externalData['email'], ParameterType::STRING) + ->setParameter(':passwordHash', $field->value->externalData['passwordHash'], ParameterType::STRING) + ->setParameter(':passwordHashType', $field->value->externalData['passwordHashType'], ParameterType::INTEGER) + ->setParameter(':passwordUpdatedAt', $field->value->externalData['passwordUpdatedAt']) + ->where( + $queryBuilder->expr()->eq( + $this->connection->quoteIdentifier('contentobject_id'), + ':userId' + ) + ) + ->setParameter(':userId', $versionInfo->contentInfo->id, ParameterType::INTEGER) + ; + + $queryBuilder->execute(); + + $settingsQuery = $this->connection->createQueryBuilder(); + + $settingsQuery + ->update($this->connection->quoteIdentifier(self::USER_SETTING_TABLE)) + ->set('is_enabled', ':isEnabled') + ->set('max_login', ':maxLogin') + ->setParameter(':isEnabled', $field->value->externalData['enabled'], ParameterType::INTEGER) + ->setParameter(':maxLogin', $field->value->externalData['maxLogin'], ParameterType::INTEGER) + ->where( + $queryBuilder->expr()->eq( + $this->connection->quoteIdentifier('user_id'), + ':userId' + ) + ) + ->setParameter(':userId', $versionInfo->contentInfo->id, ParameterType::INTEGER); + + $settingsQuery->execute(); + } + + public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds): bool + { + // Delete external storage only, when when deleting last relation to fieldType + // to avoid removing it when deleting draft, translation or by exceeding archive limit + if (!$this->isLastRelationToFieldType($fieldIds)) { + return false; + } + + $query = $this->connection->createQueryBuilder(); + $query + ->delete($this->connection->quoteIdentifier(self::USER_SETTING_TABLE)) + ->where( + $query->expr()->eq( + $this->connection->quoteIdentifier('user_id'), + ':userId' + ) + ) + ->setParameter(':userId', $versionInfo->contentInfo->id, ParameterType::INTEGER); + + $query->execute(); + + $query = $this->connection->createQueryBuilder(); + $query + ->delete($this->connection->quoteIdentifier(self::USER_TABLE)) + ->where( + $query->expr()->eq( + $this->connection->quoteIdentifier('contentobject_id'), + ':userId' + ) + ) + ->setParameter(':userId', $versionInfo->contentInfo->id, ParameterType::INTEGER); + + $query->execute(); + + return true; + } + + /** + * @param int[] $fieldIds + * + * @return bool + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function isLastRelationToFieldType(array $fieldIds): bool + { + $countExpr = $this->connection->getDatabasePlatform()->getCountExpression('id'); + + $checkQuery = $this->connection->createQueryBuilder(); + $checkQuery + ->select($countExpr) + ->from('ezcontentobject_attribute') + ->where( + $checkQuery->expr()->in( + $this->connection->quoteIdentifier('id'), + ':fieldIds' + ) + ) + ->setParameter(':fieldIds', $fieldIds, Connection::PARAM_INT_ARRAY) + ->groupBy('id') + ->having($countExpr . ' > 1'); + + $numRows = (int)$checkQuery->execute()->fetchColumn(); + + return $numRows === 0; + } + + public function countUsersWithUnsupportedHashType(array $supportedHashTypes): int + { + $selectQuery = $this->connection->createQueryBuilder(); + + $selectQuery + ->select( + $this->connection->getDatabasePlatform()->getCountExpression('u.login') + ) + ->from(self::USER_TABLE, 'u') + ->andWhere( + $selectQuery->expr()->notIn('u.password_hash_type', ':supportedPasswordHashes') + ) + ->setParameter('supportedPasswordHashes', $supportedHashTypes, Connection::PARAM_INT_ARRAY); + + return $selectQuery + ->execute() + ->fetchColumn(); + } +} + +class_alias(DoctrineStorage::class, 'eZ\Publish\Core\FieldType\User\UserStorage\Gateway\DoctrineStorage'); diff --git a/src/lib/FieldType/User/Value.php b/src/lib/FieldType/User/Value.php new file mode 100644 index 0000000000..e5b83f1109 --- /dev/null +++ b/src/lib/FieldType/User/Value.php @@ -0,0 +1,88 @@ +login; + } +} + +class_alias(Value::class, 'eZ\Publish\Core\FieldType\User\Value'); diff --git a/src/lib/FieldType/ValidationError.php b/src/lib/FieldType/ValidationError.php new file mode 100644 index 0000000000..3faa82714b --- /dev/null +++ b/src/lib/FieldType/ValidationError.php @@ -0,0 +1,82 @@ +singular = $singular; + $this->plural = $plural; + $this->values = $values; + $this->target = $target; + } + + /** + * Returns a translatable Message. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Translation + */ + public function getTranslatableMessage() + { + if (isset($this->plural)) { + return new Plural( + $this->singular, + $this->plural, + $this->values + ); + } else { + return new Message( + $this->singular, + $this->values + ); + } + } + + public function setTarget($target) + { + $this->target = $target; + } + + public function getTarget() + { + return $this->target; + } +} + +class_alias(ValidationError::class, 'eZ\Publish\Core\FieldType\ValidationError'); diff --git a/eZ/Publish/Core/FieldType/Validator.php b/src/lib/FieldType/Validator.php similarity index 84% rename from eZ/Publish/Core/FieldType/Validator.php rename to src/lib/FieldType/Validator.php index 92c5507ade..2832efcf24 100644 --- a/eZ/Publish/Core/FieldType/Validator.php +++ b/src/lib/FieldType/Validator.php @@ -4,9 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType; +namespace Ibexa\Core\FieldType; -use eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException as PropertyNotFound; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException as PropertyNotFound; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; /** * Base field type validator validator. @@ -16,7 +17,7 @@ abstract class Validator /** * The errors collected during validation. * - * @var \eZ\Publish\SPI\FieldType\ValidationError[] + * @var \Ibexa\Contracts\Core\FieldType\ValidationError[] */ protected $errors = []; @@ -94,18 +95,18 @@ abstract public function validateConstraints($constraints); * When a check against a constraint has failed, an entry will be added to the * $errors array. * - * @param \eZ\Publish\Core\FieldType\Value $value + * @param \Ibexa\Core\FieldType\Value $value * * @return bool */ - abstract public function validate(Value $value); + abstract public function validate(Value $value, ?FieldDefinition $fieldDefinition = null); /** * Returns array of messages on performed validations. * * When no validation errors occurred, the returned array should be empty. * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] */ public function getMessage() { @@ -117,7 +118,7 @@ public function getMessage() * * @internal * - * @throws \eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException * * @param array $constraints */ @@ -145,7 +146,7 @@ public function initializeWithConstraints(array $constraints) * * @param string $name * - * @throws \eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException * * @return mixed */ @@ -165,7 +166,7 @@ public function __get($name) * @param string $name * @param mixed $value * - * @throws \eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException */ public function __set($name, $value) { @@ -176,3 +177,5 @@ public function __set($name, $value) $this->constraints[$name] = $value; } } + +class_alias(Validator::class, 'eZ\Publish\Core\FieldType\Validator'); diff --git a/eZ/Publish/Core/FieldType/Validator/EmailAddressValidator.php b/src/lib/FieldType/Validator/EmailAddressValidator.php similarity index 86% rename from eZ/Publish/Core/FieldType/Validator/EmailAddressValidator.php rename to src/lib/FieldType/Validator/EmailAddressValidator.php index 7a60666d5e..2deff8a00e 100644 --- a/eZ/Publish/Core/FieldType/Validator/EmailAddressValidator.php +++ b/src/lib/FieldType/Validator/EmailAddressValidator.php @@ -4,11 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Validator; +namespace Ibexa\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\Value as BaseValue; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\FieldType\Validator; +use Ibexa\Core\FieldType\Value as BaseValue; /** * Validator for checking validity of email addresses. Both form and MX record validity checking are provided. @@ -80,11 +81,11 @@ public function validateConstraints($constraints) * * @abstract * - * @param \eZ\Publish\Core\FieldType\Value $value + * @param \Ibexa\Core\FieldType\Value $value * * @return bool */ - public function validate(BaseValue $value) + public function validate(BaseValue $value, ?FieldDefinition $fieldDefinition = null) { $pattern = '/^((\"[^\"\f\n\r\t\v\b]+\")|([A-Za-z0-9_\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+(\.[A-Za-z0-9_\!\#\$\%\&\'\*\+\-\~\/\^\`\|\{\}]+)*))@((\[(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))\])|(((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9]))\.((25[0-5])|(2[0-4][0-9])|([0-1]?[0-9]?[0-9])))|((([A-Za-z0-9\-])+\.)+[A-Za-z\-]{2,}))$/'; @@ -102,3 +103,5 @@ public function validate(BaseValue $value) return false; } } + +class_alias(EmailAddressValidator::class, 'eZ\Publish\Core\FieldType\Validator\EmailAddressValidator'); diff --git a/src/lib/FieldType/Validator/FileExtensionBlackListValidator.php b/src/lib/FieldType/Validator/FileExtensionBlackListValidator.php new file mode 100644 index 0000000000..510d5d2ee9 --- /dev/null +++ b/src/lib/FieldType/Validator/FileExtensionBlackListValidator.php @@ -0,0 +1,88 @@ + [], + ]; + + protected $constraintsSchema = [ + 'extensionsBlackList' => [ + 'type' => 'array', + 'default' => [], + ], + ]; + + /** + * @param \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface $configResolver + */ + public function __construct(ConfigResolverInterface $configResolver) + { + $this->constraints['extensionsBlackList'] = $configResolver->getParameter( + 'io.file_storage.file_type_blacklist' + ); + } + + /** + * {@inheritdoc} + */ + public function validateConstraints($constraints) + { + return []; + } + + /** + * {@inheritdoc} + */ + public function validate(BaseValue $value, ?FieldDefinition $fieldDefinition = null) + { + $this->errors = []; + + $this->validateFileExtension($value->fileName); + + return empty($this->errors); + } + + public function validateFileExtension(string $fileName): void + { + if ( + pathinfo($fileName, PATHINFO_BASENAME) !== $fileName + || in_array( + strtolower(pathinfo($fileName, PATHINFO_EXTENSION)), + $this->constraints['extensionsBlackList'], + true + ) + ) { + $this->errors[] = new ValidationError( + 'A valid file is required. The following file extensions are not allowed: %extensionsBlackList%', + null, + [ + '%extensionsBlackList%' => implode(', ', $this->constraints['extensionsBlackList']), + ], + 'fileExtensionBlackList' + ); + } + } + + /** + * @return array<\Ibexa\Contracts\Core\FieldType\ValidationError> + */ + public function getErrors(): array + { + return $this->errors; + } +} + +class_alias(FileExtensionBlackListValidator::class, 'eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator'); diff --git a/eZ/Publish/Core/FieldType/Validator/FileSizeValidator.php b/src/lib/FieldType/Validator/FileSizeValidator.php similarity index 82% rename from eZ/Publish/Core/FieldType/Validator/FileSizeValidator.php rename to src/lib/FieldType/Validator/FileSizeValidator.php index 9881766a45..3fd2f5ef77 100644 --- a/eZ/Publish/Core/FieldType/Validator/FileSizeValidator.php +++ b/src/lib/FieldType/Validator/FileSizeValidator.php @@ -4,11 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Validator; +namespace Ibexa\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\Value as BaseValue; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\FieldType\Validator; +use Ibexa\Core\FieldType\Value as BaseValue; /** * Validator for checking max. size of binary files. @@ -62,11 +63,11 @@ public function validateConstraints($constraints) /** * Checks if $value->file has the appropriate size. * - * @param \eZ\Publish\Core\FieldType\BinaryFile\Value $value + * @param \Ibexa\Core\FieldType\BinaryFile\Value $value * * @return bool */ - public function validate(BaseValue $value) + public function validate(BaseValue $value, ?FieldDefinition $fieldDefinition = null) { $isValid = true; @@ -84,3 +85,5 @@ public function validate(BaseValue $value) return $isValid; } } + +class_alias(FileSizeValidator::class, 'eZ\Publish\Core\FieldType\Validator\FileSizeValidator'); diff --git a/eZ/Publish/Core/FieldType/Validator/FloatValueValidator.php b/src/lib/FieldType/Validator/FloatValueValidator.php similarity index 86% rename from eZ/Publish/Core/FieldType/Validator/FloatValueValidator.php rename to src/lib/FieldType/Validator/FloatValueValidator.php index b5620dff14..1fb4013611 100644 --- a/eZ/Publish/Core/FieldType/Validator/FloatValueValidator.php +++ b/src/lib/FieldType/Validator/FloatValueValidator.php @@ -4,11 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Validator; +namespace Ibexa\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\Value as BaseValue; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\FieldType\Validator; +use Ibexa\Core\FieldType\Value as BaseValue; /** * Validator to validate ranges in float values. @@ -78,11 +79,11 @@ public function validateConstraints($constraints) * When a check against a constant has failed, an entry will be added to the * $errors array. * - * @param \eZ\Publish\Core\FieldType\Float\Value $value + * @param \Ibexa\Core\FieldType\Float\Value $value * * @return bool */ - public function validate(BaseValue $value) + public function validate(BaseValue $value, ?FieldDefinition $fieldDefinition = null) { $isValid = true; @@ -111,3 +112,5 @@ public function validate(BaseValue $value) return $isValid; } } + +class_alias(FloatValueValidator::class, 'eZ\Publish\Core\FieldType\Validator\FloatValueValidator'); diff --git a/src/lib/FieldType/Validator/ImageValidator.php b/src/lib/FieldType/Validator/ImageValidator.php new file mode 100644 index 0000000000..ae57f81f4a --- /dev/null +++ b/src/lib/FieldType/Validator/ImageValidator.php @@ -0,0 +1,90 @@ +getFieldSettings()['mimeTypes'] ?? []; + } + + $isValid = true; + if (isset($value->inputUri) && !$this->innerValidate($value->inputUri, $mimeTypes)) { + $isValid = false; + } + + // BC: Check if file is a valid image if the value of 'id' matches a local file + if (isset($value->id) && file_exists($value->id) && !$this->innerValidate($value->id, $mimeTypes)) { + $isValid = false; + } + + return $isValid; + } + + /** + * @param array $mimeTypes + */ + private function innerValidate($filePath, array $mimeTypes): bool + { + // silence `getimagesize` error as extension-wise valid image files might produce it anyway + // note that file extension checking is done using other validation which should be called before this one + $imageData = @getimagesize($filePath); + if (false === $imageData) { + $this->errors[] = new ValidationError( + 'A valid image file is required.', + null, + [], + 'id' + ); + + return false; + } + + // If array with mime types is empty, it means that all mime types are allowed + if (empty($mimeTypes)) { + return true; + } + + $mimeType = $imageData['mime']; + if (!in_array($mimeType, $mimeTypes, true)) { + $this->errors[] = new ValidationError( + 'The mime type of the file is invalid (%mimeType%). Allowed mime types are %mimeTypes%.', + null, + [ + '%mimeType%' => $mimeType, + '%mimeTypes%' => implode(', ', $mimeTypes), + ], + 'id' + ); + + return false; + } + + return true; + } +} + +class_alias(ImageValidator::class, 'eZ\Publish\Core\FieldType\Validator\ImageValidator'); diff --git a/eZ/Publish/Core/FieldType/Validator/IntegerValueValidator.php b/src/lib/FieldType/Validator/IntegerValueValidator.php similarity index 87% rename from eZ/Publish/Core/FieldType/Validator/IntegerValueValidator.php rename to src/lib/FieldType/Validator/IntegerValueValidator.php index 12e21fb91d..3a69d22a47 100644 --- a/eZ/Publish/Core/FieldType/Validator/IntegerValueValidator.php +++ b/src/lib/FieldType/Validator/IntegerValueValidator.php @@ -4,11 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Validator; +namespace Ibexa\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\Value as BaseValue; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\FieldType\Validator; +use Ibexa\Core\FieldType\Value as BaseValue; /** * Validate ranges of integer value. @@ -81,11 +82,11 @@ public function validateConstraints($constraints) * When a check against a constraint has failed, an entry will be added to the * $errors array. * - * @param \eZ\Publish\Core\FieldType\Integer\Value $value + * @param \Ibexa\Core\FieldType\Integer\Value $value * * @return bool */ - public function validate(BaseValue $value) + public function validate(BaseValue $value, ?FieldDefinition $fieldDefinition = null) { $isValid = true; @@ -114,3 +115,5 @@ public function validate(BaseValue $value) return $isValid; } } + +class_alias(IntegerValueValidator::class, 'eZ\Publish\Core\FieldType\Validator\IntegerValueValidator'); diff --git a/eZ/Publish/Core/FieldType/Validator/StringLengthValidator.php b/src/lib/FieldType/Validator/StringLengthValidator.php similarity index 90% rename from eZ/Publish/Core/FieldType/Validator/StringLengthValidator.php rename to src/lib/FieldType/Validator/StringLengthValidator.php index c343a21325..23ffd68f32 100644 --- a/eZ/Publish/Core/FieldType/Validator/StringLengthValidator.php +++ b/src/lib/FieldType/Validator/StringLengthValidator.php @@ -4,11 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Validator; +namespace Ibexa\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\Value as BaseValue; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\FieldType\Validator; +use Ibexa\Core\FieldType\Value as BaseValue; /** * Validator for checking min. and max. length of strings. @@ -100,11 +101,11 @@ protected function validateConstraintsOrder($constraints) * * The range is determined by $maxStringLength and $minStringLength. * - * @param \eZ\Publish\Core\FieldType\TextLine\Value $value + * @param \Ibexa\Core\FieldType\TextLine\Value $value * * @return bool */ - public function validate(BaseValue $value) + public function validate(BaseValue $value, ?FieldDefinition $fieldDefinition = null) { $isValid = true; @@ -136,3 +137,5 @@ public function validate(BaseValue $value) return $isValid; } } + +class_alias(StringLengthValidator::class, 'eZ\Publish\Core\FieldType\Validator\StringLengthValidator'); diff --git a/src/lib/FieldType/Value.php b/src/lib/FieldType/Value.php new file mode 100644 index 0000000000..a57338a6e2 --- /dev/null +++ b/src/lib/FieldType/Value.php @@ -0,0 +1,26 @@ +decoder->decode($data, $this->format, $context); } } + +class_alias(SymfonySerializerAdapter::class, 'eZ\Publish\Core\FieldType\ValueSerializer\SymfonySerializerAdapter'); diff --git a/src/lib/Helper/ContentInfoLocationLoader.php b/src/lib/Helper/ContentInfoLocationLoader.php new file mode 100644 index 0000000000..ba4d2f110d --- /dev/null +++ b/src/lib/Helper/ContentInfoLocationLoader.php @@ -0,0 +1,29 @@ +repository = $repository; + } + + public function loadLocation(ContentInfo $contentInfo) + { + if (null === $contentInfo->mainLocationId) { + throw new NotFoundException('main location of content', $contentInfo->id); + } + + try { + return $this->repository->sudo( + static function (Repository $repository) use ($contentInfo) { + return $repository->getLocationService()->loadLocation($contentInfo->mainLocationId); + } + ); + } catch (Exception $e) { + throw new NotFoundException('main location of content', $contentInfo->id); + } + } +} + +class_alias(SudoMainLocationLoader::class, 'eZ\Publish\Core\Helper\ContentInfoLocationLoader\SudoMainLocationLoader'); diff --git a/src/lib/Helper/ContentPreviewHelper.php b/src/lib/Helper/ContentPreviewHelper.php new file mode 100644 index 0000000000..ae32cedc11 --- /dev/null +++ b/src/lib/Helper/ContentPreviewHelper.php @@ -0,0 +1,137 @@ +eventDispatcher = $eventDispatcher; + $this->siteAccessRouter = $siteAccessRouter; + } + + public function setSiteAccess(SiteAccess $siteAccess = null) + { + $this->originalSiteAccess = $siteAccess; + } + + /** + * Return original SiteAccess. + * + * @return \Ibexa\Core\MVC\Symfony\SiteAccess + */ + public function getOriginalSiteAccess() + { + return $this->originalSiteAccess; + } + + /** + * Switches configuration scope to $siteAccessName and returns the new SiteAccess to use for preview. + * + * @param string $siteAccessName + * + * @return \Ibexa\Core\MVC\Symfony\SiteAccess + */ + public function changeConfigScope($siteAccessName) + { + $event = new ScopeChangeEvent($this->siteAccessRouter->matchByName($siteAccessName)); + $this->eventDispatcher->dispatch($event, MVCEvents::CONFIG_SCOPE_CHANGE); + + return $event->getSiteAccess(); + } + + /** + * Restores original config scope. + * + * @return \Ibexa\Core\MVC\Symfony\SiteAccess + */ + public function restoreConfigScope() + { + $event = new ScopeChangeEvent($this->originalSiteAccess); + $this->eventDispatcher->dispatch($event, MVCEvents::CONFIG_SCOPE_RESTORE); + + return $event->getSiteAccess(); + } + + /** + * @return bool + */ + public function isPreviewActive() + { + return $this->previewActive; + } + + /** + * @param bool $previewActive + */ + public function setPreviewActive($previewActive) + { + $this->previewActive = (bool)$previewActive; + $this->originalSiteAccess = clone $this->originalSiteAccess; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function getPreviewedContent() + { + return $this->previewedContent; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $previewedContent + */ + public function setPreviewedContent(Content $previewedContent) + { + $this->previewedContent = $previewedContent; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location + */ + public function getPreviewedLocation() + { + return $this->previewedLocation; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $previewedLocation + */ + public function setPreviewedLocation(Location $previewedLocation) + { + $this->previewedLocation = $previewedLocation; + } +} + +class_alias(ContentPreviewHelper::class, 'eZ\Publish\Core\Helper\ContentPreviewHelper'); diff --git a/src/lib/Helper/FieldHelper.php b/src/lib/Helper/FieldHelper.php new file mode 100644 index 0000000000..c628846c11 --- /dev/null +++ b/src/lib/Helper/FieldHelper.php @@ -0,0 +1,71 @@ +fieldTypeService = $fieldTypeService; + $this->contentTypeService = $contentTypeService; + $this->translationHelper = $translationHelper; + } + + /** + * Checks if provided field can be considered empty. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param string $fieldDefIdentifier + * @param string|null $forcedLanguage + * + * @return bool + */ + public function isFieldEmpty(Content $content, $fieldDefIdentifier, $forcedLanguage = null) + { + $field = $this->translationHelper->getTranslatedField($content, $fieldDefIdentifier, $forcedLanguage); + $fieldDefinition = $content->getContentType()->getFieldDefinition($fieldDefIdentifier); + + return $this + ->fieldTypeService + ->getFieldType($fieldDefinition->fieldTypeIdentifier) + ->isEmptyValue($field->value); + } + + /** + * Returns FieldDefinition object based on $contentInfo and $fieldDefIdentifier. + * + * @deprecated If you have Content you can instead do: $content->getContentType()->getFieldDefinition($identifier) + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param string $fieldDefIdentifier + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition + */ + public function getFieldDefinition(ContentInfo $contentInfo, $fieldDefIdentifier) + { + return $this + ->contentTypeService + ->loadContentType($contentInfo->contentTypeId) + ->getFieldDefinition($fieldDefIdentifier); + } +} + +class_alias(FieldHelper::class, 'eZ\Publish\Core\Helper\FieldHelper'); diff --git a/eZ/Publish/Core/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsList.php b/src/lib/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsList.php similarity index 82% rename from eZ/Publish/Core/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsList.php rename to src/lib/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsList.php index 57ed901258..3343fd4bd7 100644 --- a/eZ/Publish/Core/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsList.php +++ b/src/lib/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsList.php @@ -4,9 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Helper\FieldsGroups; +namespace Ibexa\Core\Helper\FieldsGroups; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use JMS\TranslationBundle\Annotation\Ignore; use Symfony\Contracts\Translation\TranslatorInterface; /** @@ -39,9 +40,10 @@ public function getGroups() foreach ($this->groups as $groupIdentifier) { $translatedGroups[$groupIdentifier] = $this->translator->trans( + /** @Ignore */ $groupIdentifier, [], - 'ezplatform_fields_groups' + 'ibexa_fields_groups' ); } @@ -62,3 +64,5 @@ public function getFieldGroup(FieldDefinition $fieldDefinition): string return $fieldDefinition->fieldGroup; } } + +class_alias(ArrayTranslatorFieldsGroupsList::class, 'eZ\Publish\Core\Helper\FieldsGroups\ArrayTranslatorFieldsGroupsList'); diff --git a/eZ/Publish/Core/Helper/FieldsGroups/FieldsGroupsList.php b/src/lib/Helper/FieldsGroups/FieldsGroupsList.php similarity index 76% rename from eZ/Publish/Core/Helper/FieldsGroups/FieldsGroupsList.php rename to src/lib/Helper/FieldsGroups/FieldsGroupsList.php index a36bdcc2a2..be128c0906 100644 --- a/eZ/Publish/Core/Helper/FieldsGroups/FieldsGroupsList.php +++ b/src/lib/Helper/FieldsGroups/FieldsGroupsList.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Helper\FieldsGroups; +namespace Ibexa\Core\Helper\FieldsGroups; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; /** * List of content fields groups. @@ -32,9 +32,11 @@ public function getGroups(); public function getDefaultGroup(); /** - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition * * @return string */ public function getFieldGroup(FieldDefinition $fieldDefinition): string; } + +class_alias(FieldsGroupsList::class, 'eZ\Publish\Core\Helper\FieldsGroups\FieldsGroupsList'); diff --git a/src/lib/Helper/FieldsGroups/RepositoryConfigFieldsGroupsListFactory.php b/src/lib/Helper/FieldsGroups/RepositoryConfigFieldsGroupsListFactory.php new file mode 100644 index 0000000000..dad8611d2d --- /dev/null +++ b/src/lib/Helper/FieldsGroups/RepositoryConfigFieldsGroupsListFactory.php @@ -0,0 +1,37 @@ +configProvider = $configProvider; + } + + public function build(TranslatorInterface $translator) + { + $repositoryConfig = $this->configProvider->getRepositoryConfig(); + + return new ArrayTranslatorFieldsGroupsList( + $translator, + $repositoryConfig['fields_groups']['default'], + $repositoryConfig['fields_groups']['list'] + ); + } +} + +class_alias(RepositoryConfigFieldsGroupsListFactory::class, 'eZ\Publish\Core\Helper\FieldsGroups\RepositoryConfigFieldsGroupsListFactory'); diff --git a/src/lib/Helper/PreviewLocationProvider.php b/src/lib/Helper/PreviewLocationProvider.php new file mode 100644 index 0000000000..7d3bd6b662 --- /dev/null +++ b/src/lib/Helper/PreviewLocationProvider.php @@ -0,0 +1,117 @@ +locationService = $locationService; + $this->contentService = $contentService; + $this->locationHandler = $locationHandler; + } + + /** + * Loads the main location for $contentId. + * + * If the content does not have a location (yet), but has a Location draft, it is returned instead. + * Location drafts do not have an id (it is set to null), and can be tested using the isDraft() method. + * + * If the content doesn't have a location nor a location draft, null is returned. + * + * @deprecated Since 7.5.4, rather use loadMainLocationByContent. + * @see loadMainLocationByContent + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * + * @param mixed $contentId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location|null + */ + public function loadMainLocation($contentId) + { + return $this->loadMainLocationByContent( + $this->contentService->loadContent($contentId) + ); + } + + /** + * Loads the main location for $content. + * + * If the content does not have a location (yet), but has a Location draft, it is returned instead. + * Location drafts do not have an id (it is set to null), and can be tested using the isDraft() method. + * + * If the content doesn't have a location nor a location draft, null is returned. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location|null + */ + public function loadMainLocationByContent(APIContent $content): ?APILocation + { + $location = null; + $contentInfo = $content + ->getVersionInfo() + ->getContentInfo(); + + // mainLocationId already exists, content has been published at least once. + if ($contentInfo->mainLocationId) { + $location = $this->locationService->loadLocation($contentInfo->mainLocationId); + } elseif (!$contentInfo->published) { + // New Content, never published, create a virtual location object. + // In cases content is missing locations this will return empty array + $parentLocations = $this->locationHandler->loadParentLocationsForDraftContent($contentInfo->id); + if (empty($parentLocations)) { + return null; + } + + // NOTE: Once Repository adds support for draft locations (and draft location ops), then this can be removed + $location = new Location( + [ + 'content' => $content, + 'contentInfo' => $contentInfo, + 'status' => Location::STATUS_DRAFT, + 'parentLocationId' => $parentLocations[0]->id, + 'depth' => $parentLocations[0]->depth + 1, + 'pathString' => $parentLocations[0]->pathString . 'x/', + ] + ); + } + + return $location; + } +} + +class_alias(PreviewLocationProvider::class, 'eZ\Publish\Core\Helper\PreviewLocationProvider'); diff --git a/eZ/Publish/Core/Helper/TranslationHelper.php b/src/lib/Helper/TranslationHelper.php similarity index 86% rename from eZ/Publish/Core/Helper/TranslationHelper.php rename to src/lib/Helper/TranslationHelper.php index dc1e81237f..7942a5a710 100644 --- a/eZ/Publish/Core/Helper/TranslationHelper.php +++ b/src/lib/Helper/TranslationHelper.php @@ -4,18 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Helper; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +namespace Ibexa\Core\Helper; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; use Psr\Log\LoggerInterface; /** @@ -23,10 +23,10 @@ */ class TranslationHelper { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ protected $configResolver; - /** @var \eZ\Publish\API\Repository\ContentService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentService */ protected $contentService; /** @var array */ @@ -47,7 +47,7 @@ public function __construct(ConfigResolverInterface $configResolver, ContentServ * Returns content name, translated. * By default this method uses prioritized languages, unless $forcedLanguage is provided. * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) * * @return string @@ -82,10 +82,10 @@ public function getTranslatedContentNameByVersionInfo( * Returns content name, translated, from a ContentInfo object. * By default this method uses prioritized languages, unless $forcedLanguage is provided. * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) * - * @todo Remove ContentService usage when translated names are available in ContentInfo (see https://jira.ez.no/browse/EZP-21755) + * @todo Remove ContentService usage when translated names are available in ContentInfo (see https://issues.ibexa.co/browse/EZP-21755) * * @return string */ @@ -110,11 +110,11 @@ public function getTranslatedContentNameByContentInfo(ContentInfo $contentInfo, * By default, this method will return the field in current language if translation is present. If not, main language will be used. * If $forcedLanguage is provided, will return the field in this language, if translation is present. * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content * @param string $fieldDefIdentifier Field definition identifier. * @param string $forcedLanguage Locale we want the field translation in (e.g. "fre-FR"). Null by default (takes current locale) * - * @return \eZ\Publish\API\Repository\Values\Content\Field|null + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field|null */ public function getTranslatedField(Content $content, $fieldDefIdentifier, $forcedLanguage = null) { @@ -133,12 +133,12 @@ public function getTranslatedField(Content $content, $fieldDefIdentifier, $force * By default, this method will return the field definition name in current language if translation is present. If not, main language will be used. * If $forcedLanguage is provided, will return the field definition name in this language, if translation is present. * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType * @param string $fieldDefIdentifier Field Definition identifier * @param string $property Specifies if 'name' or 'description' should be used * @param string $forcedLanguage Locale we want the field definition name translated in in (e.g. "fre-FR"). Null by default (takes current locale) * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException * * @return string|null */ @@ -178,11 +178,11 @@ public function getTranslatedFieldDefinitionProperty( * Languages will consist of either forced language or current languages list, in addition helper will check if for * mainLanguage property and append that to languages if alwaysAvailable property is true or non-existing. * - * @param \eZ\Publish\API\Repository\Values\ValueObject $object Can be any kid of Value object which directly holds the translated property + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object Can be any kid of Value object which directly holds the translated property * @param string $property Property name, example 'names', 'descriptions' * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException * * @return string|null */ @@ -217,11 +217,11 @@ public function getTranslatedByProperty(ValueObject $object, $property, $forcedL * Languages will consist of either forced language or current languages list, in addition helper will append null * to list of languages so method may fallback to main/initial language if supported by domain. * - * @param \eZ\Publish\API\Repository\Values\ValueObject $object Can be any kind of Value object which directly holds the methods that provides translated value. + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object Can be any kind of Value object which directly holds the methods that provides translated value. * @param string $method Method name, example 'getName', 'description' * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException * * @return string|null */ @@ -312,3 +312,5 @@ private function getLanguages($forcedLanguage = null, $fallbackLanguage = null) return $languages; } } + +class_alias(TranslationHelper::class, 'eZ\Publish\Core\Helper\TranslationHelper'); diff --git a/eZ/Publish/Core/IO/ConfigScopeChangeAwareIOService.php b/src/lib/IO/ConfigScopeChangeAwareIOService.php similarity index 86% rename from eZ/Publish/Core/IO/ConfigScopeChangeAwareIOService.php rename to src/lib/IO/ConfigScopeChangeAwareIOService.php index ccffb376a3..467a2abe2b 100644 --- a/eZ/Publish/Core/IO/ConfigScopeChangeAwareIOService.php +++ b/src/lib/IO/ConfigScopeChangeAwareIOService.php @@ -6,20 +6,20 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\IO; +namespace Ibexa\Core\IO; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\IO\Values\BinaryFileCreateStruct; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Event\ScopeChangeEvent; -use eZ\Publish\SPI\MVC\EventSubscriber\ConfigScopeChangeSubscriber; +use Ibexa\Contracts\Core\MVC\EventSubscriber\ConfigScopeChangeSubscriber; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\IO\Values\BinaryFile; +use Ibexa\Core\IO\Values\BinaryFileCreateStruct; +use Ibexa\Core\MVC\Symfony\Event\ScopeChangeEvent; class ConfigScopeChangeAwareIOService implements IOServiceInterface, ConfigScopeChangeSubscriber { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; - /** @var \eZ\Publish\Core\IO\IOServiceInterface */ + /** @var \Ibexa\Core\IO\IOServiceInterface */ private $innerIOService; /** @var string */ @@ -118,3 +118,5 @@ public function onConfigScopeChange(ScopeChangeEvent $event): void $this->setPrefix($this->configResolver->getParameter($this->prefixParameterName)); } } + +class_alias(ConfigScopeChangeAwareIOService::class, 'eZ\Publish\Core\IO\ConfigScopeChangeAwareIOService'); diff --git a/src/lib/IO/Exception/BinaryFileNotFoundException.php b/src/lib/IO/Exception/BinaryFileNotFoundException.php new file mode 100644 index 0000000000..10ac691b25 --- /dev/null +++ b/src/lib/IO/Exception/BinaryFileNotFoundException.php @@ -0,0 +1,20 @@ +getBaseTranslation(), $code); } } + +class_alias(InvalidBinaryAbsolutePathException::class, 'eZ\Publish\Core\IO\Exception\InvalidBinaryAbsolutePathException'); diff --git a/src/lib/IO/Exception/InvalidBinaryFileIdException.php b/src/lib/IO/Exception/InvalidBinaryFileIdException.php new file mode 100644 index 0000000000..1e660c7368 --- /dev/null +++ b/src/lib/IO/Exception/InvalidBinaryFileIdException.php @@ -0,0 +1,19 @@ +getBaseTranslation(), $code); } } + +class_alias(InvalidBinaryPrefixException::class, 'eZ\Publish\Core\IO\Exception\InvalidBinaryPrefixException'); diff --git a/src/lib/IO/FilePathNormalizer/Flysystem.php b/src/lib/IO/FilePathNormalizer/Flysystem.php new file mode 100644 index 0000000000..94472cd60f --- /dev/null +++ b/src/lib/IO/FilePathNormalizer/Flysystem.php @@ -0,0 +1,47 @@ +slugConverter = $slugConverter; + $this->pathNormalizer = $pathNormalizer; + } + + public function normalizePath(string $filePath, bool $doHash = true): string + { + $fileName = pathinfo($filePath, PATHINFO_BASENAME); + $directory = pathinfo($filePath, PATHINFO_DIRNAME); + + $fileName = $this->slugConverter->convert($fileName, '_1', 'urlalias'); + + $hash = $doHash + ? (preg_match(self::HASH_PATTERN, $fileName) ? '' : bin2hex(random_bytes(6)) . '-') + : ''; + + $filePath = $directory . \DIRECTORY_SEPARATOR . $hash; + $normalizedFileName = $this->pathNormalizer->normalizePath($fileName); + + return $filePath . $normalizedFileName; + } +} + +class_alias(Flysystem::class, 'eZ\Publish\Core\IO\FilePathNormalizer\Flysystem'); diff --git a/src/lib/IO/FilePathNormalizerInterface.php b/src/lib/IO/FilePathNormalizerInterface.php new file mode 100644 index 0000000000..3d72bcce5c --- /dev/null +++ b/src/lib/IO/FilePathNormalizerInterface.php @@ -0,0 +1,19 @@ +innerAdapter = $innerAdapter; + $this->prefixer = $prefixer; + } + + public function fileExists(string $path): bool + { + $path = $this->prefixer->prefixPath($path); + + return $this->innerAdapter->fileExists($path); + } + + public function write(string $path, string $contents, Config $config): void + { + $path = $this->prefixer->prefixPath($path); + + $this->innerAdapter->write($path, $contents, $config); + } + + public function writeStream(string $path, $contents, Config $config): void + { + $path = $this->prefixer->prefixPath($path); + + $this->innerAdapter->writeStream($path, $contents, $config); + } + + public function read(string $path): string + { + $path = $this->prefixer->prefixPath($path); + + return $this->innerAdapter->read($path); + } + + public function readStream(string $path) + { + $path = $this->prefixer->prefixPath($path); + + return $this->innerAdapter->readStream($path); + } + + public function delete(string $path): void + { + $path = $this->prefixer->prefixPath($path); + + $this->innerAdapter->delete($path); + } + + public function deleteDirectory(string $path): void + { + $path = $this->prefixer->prefixPath($path); + + $this->innerAdapter->deleteDirectory($path); + } + + public function createDirectory(string $path, Config $config): void + { + $path = $this->prefixer->prefixPath($path); + + $this->innerAdapter->createDirectory($path, $config); + } + + public function setVisibility(string $path, string $visibility): void + { + $path = $this->prefixer->prefixPath($path); + + $this->innerAdapter->setVisibility($path, $visibility); + } + + public function visibility(string $path): FileAttributes + { + $path = $this->prefixer->prefixPath($path); + + return $this->innerAdapter->visibility($path); + } + + public function mimeType(string $path): FileAttributes + { + $path = $this->prefixer->prefixPath($path); + + return $this->innerAdapter->mimeType($path); + } + + public function lastModified(string $path): FileAttributes + { + $path = $this->prefixer->prefixPath($path); + + return $this->innerAdapter->lastModified($path); + } + + public function fileSize(string $path): FileAttributes + { + $path = $this->prefixer->prefixPath($path); + + return $this->innerAdapter->fileSize($path); + } + + public function listContents(string $path, bool $deep): iterable + { + $path = $this->prefixer->prefixPath($path); + + foreach ($this->innerAdapter->listContents($path, $deep) as $storageAttributes) { + $itemPath = $this->prefixer->stripPrefix($storageAttributes->path()); + + yield $storageAttributes->withPath($itemPath); + } + + yield from []; + } + + public function move(string $source, string $destination, Config $config): void + { + $source = $this->prefixer->prefixPath($source); + $destination = $this->prefixer->prefixPath($destination); + + $this->innerAdapter->move($source, $destination, $config); + } + + public function copy(string $source, string $destination, Config $config): void + { + $sourcePath = $this->prefixer->prefixPath($source); + $destinationPath = $this->prefixer->prefixPath($destination); + + $this->innerAdapter->copy($sourcePath, $destinationPath, $config); + } +} diff --git a/src/lib/IO/Flysystem/PathPrefixer/BaseSiteAccessAwarePathPrefixer.php b/src/lib/IO/Flysystem/PathPrefixer/BaseSiteAccessAwarePathPrefixer.php new file mode 100644 index 0000000000..5063535a6e --- /dev/null +++ b/src/lib/IO/Flysystem/PathPrefixer/BaseSiteAccessAwarePathPrefixer.php @@ -0,0 +1,58 @@ +separator = $separator; + } + + abstract protected function getSiteAccessAwarePathPrefix(): string; + + final public function prefixPath(string $path): string + { + $siteAccessAwarePathPrefix = $this->getSiteAccessAwarePathPrefix(); + $prefix = rtrim($siteAccessAwarePathPrefix, '\\/'); + if ($prefix !== '' || $siteAccessAwarePathPrefix === $this->separator) { + $prefix .= $this->separator; + } + + return $prefix . ltrim($path, '\\/'); + } + + final public function stripPrefix(string $path): string + { + return substr($path, strlen($this->getSiteAccessAwarePathPrefix())); + } + + final public function stripDirectoryPrefix(string $path): string + { + return rtrim($this->stripPrefix($path), '\\/'); + } + + final public function prefixDirectoryPath(string $path): string + { + $prefixedPath = $this->prefixPath(rtrim($path, '\\/')); + + if ($prefixedPath === '' || str_ends_with($prefixedPath, $this->separator)) { + return $prefixedPath; + } + + return $prefixedPath . $this->separator; + } +} diff --git a/src/lib/IO/Flysystem/PathPrefixer/DFSSiteAccessAwarePathPrefixer.php b/src/lib/IO/Flysystem/PathPrefixer/DFSSiteAccessAwarePathPrefixer.php new file mode 100644 index 0000000000..863173e67a --- /dev/null +++ b/src/lib/IO/Flysystem/PathPrefixer/DFSSiteAccessAwarePathPrefixer.php @@ -0,0 +1,47 @@ +rootDir = $rootDir; + $this->path = $path; + $this->configProcessor = $configProcessor; + } + + protected function getSiteAccessAwarePathPrefix(): string + { + return sprintf( + '%s%s%s', + rtrim($this->rootDir, $this->separator), + $this->separator, + $this->configProcessor->processSettingValue($this->path) + ); + } +} diff --git a/src/lib/IO/Flysystem/PathPrefixer/LocalSiteAccessAwarePathPrefixer.php b/src/lib/IO/Flysystem/PathPrefixer/LocalSiteAccessAwarePathPrefixer.php new file mode 100644 index 0000000000..1460a22ecc --- /dev/null +++ b/src/lib/IO/Flysystem/PathPrefixer/LocalSiteAccessAwarePathPrefixer.php @@ -0,0 +1,33 @@ +ioConfigProvider = $ioConfigProvider; + } + + protected function getSiteAccessAwarePathPrefix(): string + { + return $this->ioConfigProvider->getRootDir(); + } +} diff --git a/src/lib/IO/Flysystem/PathPrefixer/PathPrefixerInterface.php b/src/lib/IO/Flysystem/PathPrefixer/PathPrefixerInterface.php new file mode 100644 index 0000000000..bb3e2ee778 --- /dev/null +++ b/src/lib/IO/Flysystem/PathPrefixer/PathPrefixerInterface.php @@ -0,0 +1,23 @@ +nativeVisibilityConverter = $nativeVisibilityConverter; + } + + abstract protected function getPublicFilePermissions(): int; + + abstract protected function getPublicDirectoryPermissions(): int; + + final public function forFile(string $visibility): int + { + PortableVisibilityGuard::guardAgainstInvalidInput($visibility); + + return $visibility === Visibility::PUBLIC + ? $this->getPublicFilePermissions() + : $this->nativeVisibilityConverter->forFile($visibility); + } + + final public function forDirectory(string $visibility): int + { + PortableVisibilityGuard::guardAgainstInvalidInput($visibility); + + return $visibility === Visibility::PUBLIC + ? $this->getPublicDirectoryPermissions() + : $this->nativeVisibilityConverter->forDirectory($visibility); + } + + final public function inverseForFile(int $visibility): string + { + if ($visibility === $this->getPublicFilePermissions()) { + return Visibility::PUBLIC; + } + + if ($visibility === $this->nativeVisibilityConverter->forFile(Visibility::PRIVATE)) { + return Visibility::PRIVATE; + } + + return Visibility::PUBLIC; // default + } + + final public function inverseForDirectory(int $visibility): string + { + if ($visibility === $this->getPublicDirectoryPermissions()) { + return Visibility::PUBLIC; + } + + if ($visibility === $this->nativeVisibilityConverter->forDirectory(Visibility::PRIVATE)) { + return Visibility::PRIVATE; + } + + return Visibility::PUBLIC; // default + } + + final public function defaultForDirectories(): int + { + return $this->nativeVisibilityConverter->defaultForDirectories(); + } +} diff --git a/src/lib/IO/Flysystem/VisibilityConverter/DFSVisibilityConverter.php b/src/lib/IO/Flysystem/VisibilityConverter/DFSVisibilityConverter.php new file mode 100644 index 0000000000..522733d70a --- /dev/null +++ b/src/lib/IO/Flysystem/VisibilityConverter/DFSVisibilityConverter.php @@ -0,0 +1,54 @@ +permissions = $permissions; + } + + protected function getPublicFilePermissions(): int + { + return $this->permissions['files'] ?? $this->nativeVisibilityConverter->forFile( + Visibility::PUBLIC + ); + } + + protected function getPublicDirectoryPermissions(): int + { + return $this->permissions['directories'] ?? $this->nativeVisibilityConverter->forDirectory( + Visibility::PUBLIC + ); + } +} diff --git a/src/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverter.php b/src/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverter.php new file mode 100644 index 0000000000..7c94201627 --- /dev/null +++ b/src/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverter.php @@ -0,0 +1,51 @@ +configResolver = $configResolver; + } + + protected function getPublicFilePermissions(): int + { + return $this->configResolver->getParameter( + self::SITE_CONFIG_IO_FILE_PERMISSIONS_PARAM_NAME + ); + } + + protected function getPublicDirectoryPermissions(): int + { + return $this->configResolver->getParameter(self::SITE_CONFIG_IO_DIR_PERMISSIONS_PARAM_NAME); + } +} diff --git a/eZ/Publish/Core/IO/IOBinarydataHandler.php b/src/lib/IO/IOBinarydataHandler.php similarity index 75% rename from eZ/Publish/Core/IO/IOBinarydataHandler.php rename to src/lib/IO/IOBinarydataHandler.php index b0e5759ef2..36d07baf8d 100644 --- a/eZ/Publish/Core/IO/IOBinarydataHandler.php +++ b/src/lib/IO/IOBinarydataHandler.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\IO; +namespace Ibexa\Core\IO; -use eZ\Publish\SPI\IO\BinaryFileCreateStruct; +use Ibexa\Contracts\Core\IO\BinaryFileCreateStruct; /** * Provides reading & writing of files binary data. @@ -16,7 +16,7 @@ interface IOBinarydataHandler /** * Creates a new file with data from $binaryFileCreateStruct. * - * @param \eZ\Publish\SPI\IO\BinaryFileCreateStruct $binaryFileCreateStruct + * @param \Ibexa\Contracts\Core\IO\BinaryFileCreateStruct $binaryFileCreateStruct * * @throws \RuntimeException if an error occured creating the file */ @@ -27,7 +27,7 @@ public function create(BinaryFileCreateStruct $binaryFileCreateStruct); * * @param string $spiBinaryFileId * - * @throws \eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException If the file is not found + * @throws \Ibexa\Core\IO\Exception\BinaryFileNotFoundException If the file is not found */ public function delete($spiBinaryFileId); @@ -36,7 +36,7 @@ public function delete($spiBinaryFileId); * * @param $spiBinaryFileId * - * @throws \eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException If $path is not found + * @throws \Ibexa\Core\IO\Exception\BinaryFileNotFoundException If $path is not found * * @return string */ @@ -48,6 +48,8 @@ public function getContents($spiBinaryFileId); * @param string $spiBinaryFileId * * @return resource A read-only binary resource to $path + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function getResource($spiBinaryFileId); @@ -76,3 +78,5 @@ public function getIdFromUri($binaryFileUri); */ public function deleteDirectory($spiPath); } + +class_alias(IOBinarydataHandler::class, 'eZ\Publish\Core\IO\IOBinarydataHandler'); diff --git a/src/lib/IO/IOBinarydataHandler/Flysystem.php b/src/lib/IO/IOBinarydataHandler/Flysystem.php new file mode 100644 index 0000000000..596b9c6988 --- /dev/null +++ b/src/lib/IO/IOBinarydataHandler/Flysystem.php @@ -0,0 +1,104 @@ +filesystem = $filesystem; + $this->urlDecorator = $urlDecorator; + } + + public function create(BinaryFileCreateStruct $binaryFileCreateStruct): void + { + try { + $this->filesystem->writeStream( + $binaryFileCreateStruct->id, + $binaryFileCreateStruct->getInputStream(), + [ + 'mimetype' => $binaryFileCreateStruct->mimeType, + Config::OPTION_VISIBILITY => Visibility::PUBLIC, + Config::OPTION_DIRECTORY_VISIBILITY => Visibility::PUBLIC, + ] + ); + } catch (FilesystemException $e) { + throw new IOException("Failed to create file '{$binaryFileCreateStruct->id}'", $e); + } + } + + public function delete($spiBinaryFileId): void + { + try { + $this->filesystem->delete($spiBinaryFileId); + } catch (FilesystemException $e) { + throw new BinaryFileNotFoundException($spiBinaryFileId, $e); + } + } + + public function getContents($spiBinaryFileId): string + { + try { + return $this->filesystem->read($spiBinaryFileId); + } catch (FilesystemException $e) { + throw new BinaryFileNotFoundException($spiBinaryFileId, $e); + } + } + + public function getResource($spiBinaryFileId) + { + try { + return $this->filesystem->readStream($spiBinaryFileId); + } catch (FilesystemException $e) { + throw new BinaryFileNotFoundException($spiBinaryFileId, $e); + } + } + + public function getUri($spiBinaryFileId): string + { + return null !== $this->urlDecorator + ? $this->urlDecorator->decorate($spiBinaryFileId) + : '/' . $spiBinaryFileId; + } + + public function getIdFromUri($binaryFileUri): string + { + if (isset($this->urlDecorator)) { + return $this->urlDecorator->undecorate($binaryFileUri); + } + + return ltrim($binaryFileUri, '/'); + } + + public function deleteDirectory($spiPath): void + { + try { + $this->filesystem->deleteDirectory($spiPath); + } catch (FilesystemException $e) { + throw new IOException("'Unable to delete directory '$spiPath'", $e); + } + } +} + +class_alias(Flysystem::class, 'eZ\Publish\Core\IO\IOBinarydataHandler\Flysystem'); diff --git a/eZ/Publish/Core/IO/IOBinarydataHandler/SiteAccessDependentBinaryDataHandler.php b/src/lib/IO/IOBinarydataHandler/SiteAccessDependentBinaryDataHandler.php similarity index 77% rename from eZ/Publish/Core/IO/IOBinarydataHandler/SiteAccessDependentBinaryDataHandler.php rename to src/lib/IO/IOBinarydataHandler/SiteAccessDependentBinaryDataHandler.php index 070df87549..f1c70d8818 100644 --- a/eZ/Publish/Core/IO/IOBinarydataHandler/SiteAccessDependentBinaryDataHandler.php +++ b/src/lib/IO/IOBinarydataHandler/SiteAccessDependentBinaryDataHandler.php @@ -4,22 +4,22 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\IO\IOBinarydataHandler; +namespace Ibexa\Core\IO\IOBinarydataHandler; -use eZ\Bundle\EzPublishIOBundle\ApiLoader\HandlerRegistry; -use eZ\Publish\Core\IO\IOBinarydataHandler; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\SPI\IO\BinaryFileCreateStruct; +use Ibexa\Bundle\IO\ApiLoader\HandlerRegistry; +use Ibexa\Contracts\Core\IO\BinaryFileCreateStruct; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\IO\IOBinarydataHandler; /** * @internal */ final class SiteAccessDependentBinaryDataHandler implements IOBinaryDataHandler { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; - /** @var \eZ\Bundle\EzPublishIOBundle\ApiLoader\HandlerRegistry */ + /** @var \Ibexa\Bundle\IO\ApiLoader\HandlerRegistry */ private $dataHandlerRegistry; public function __construct( @@ -72,3 +72,5 @@ public function deleteDirectory($spiPath) return $this->getHandler()->deleteDirectory($spiPath); } } + +class_alias(SiteAccessDependentBinaryDataHandler::class, 'eZ\Publish\Core\IO\IOBinarydataHandler\SiteAccessDependentBinaryDataHandler'); diff --git a/eZ/Publish/Core/IO/IOBinarydataHandler/SiteAccessDependentMetadataHandler.php b/src/lib/IO/IOBinarydataHandler/SiteAccessDependentMetadataHandler.php similarity index 75% rename from eZ/Publish/Core/IO/IOBinarydataHandler/SiteAccessDependentMetadataHandler.php rename to src/lib/IO/IOBinarydataHandler/SiteAccessDependentMetadataHandler.php index 620ac1ddc2..577dda4049 100644 --- a/eZ/Publish/Core/IO/IOBinarydataHandler/SiteAccessDependentMetadataHandler.php +++ b/src/lib/IO/IOBinarydataHandler/SiteAccessDependentMetadataHandler.php @@ -4,22 +4,22 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\IO\IOBinarydataHandler; +namespace Ibexa\Core\IO\IOBinarydataHandler; -use eZ\Bundle\EzPublishIOBundle\ApiLoader\HandlerRegistry; -use eZ\Publish\Core\IO\IOMetadataHandler; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\SPI\IO\BinaryFileCreateStruct; +use Ibexa\Bundle\IO\ApiLoader\HandlerRegistry; +use Ibexa\Contracts\Core\IO\BinaryFileCreateStruct; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\IO\IOMetadataHandler; /** * @internal */ final class SiteAccessDependentMetadataHandler implements IOMetadataHandler { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; - /** @var \eZ\Bundle\EzPublishIOBundle\ApiLoader\HandlerRegistry */ + /** @var \Ibexa\Bundle\IO\ApiLoader\HandlerRegistry */ private $dataHandlerRegistry; public function __construct( @@ -67,3 +67,5 @@ public function deleteDirectory($spiPath) return $this->getHandler()->deleteDirectory($spiPath); } } + +class_alias(SiteAccessDependentMetadataHandler::class, 'eZ\Publish\Core\IO\IOBinarydataHandler\SiteAccessDependentMetadataHandler'); diff --git a/eZ/Publish/Core/IO/IOConfigProvider.php b/src/lib/IO/IOConfigProvider.php similarity index 80% rename from eZ/Publish/Core/IO/IOConfigProvider.php rename to src/lib/IO/IOConfigProvider.php index 637f425fc4..e481d33053 100644 --- a/eZ/Publish/Core/IO/IOConfigProvider.php +++ b/src/lib/IO/IOConfigProvider.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\IO; +namespace Ibexa\Core\IO; /** * Resolves IO complex settings. @@ -21,3 +21,5 @@ public function getLegacyUrlPrefix(): string; public function getUrlPrefix(): string; } + +class_alias(IOConfigProvider::class, 'eZ\Publish\Core\IO\IOConfigProvider'); diff --git a/src/lib/IO/IOMetadataHandler.php b/src/lib/IO/IOMetadataHandler.php new file mode 100644 index 0000000000..29f549d6cc --- /dev/null +++ b/src/lib/IO/IOMetadataHandler.php @@ -0,0 +1,68 @@ +filesystem = $filesystem; + $this->logger = $logger ?? new NullLogger(); + } + + public function setLogger(LoggerInterface $logger): void + { + $this->logger = $logger; + } + + /** + * Only reads & returns metadata, since the binary data handler took care of creating the file already. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function create(SPIBinaryFileCreateStruct $spiBinaryFileCreateStruct): IOBinaryFile + { + return $this->load($spiBinaryFileCreateStruct->id); + } + + /** + * Does really nothing, the binary data handler takes care of it. + * + * @param $spiBinaryFileId + */ + public function delete($spiBinaryFileId) + { + } + + public function load($spiBinaryFileId): IOBinaryFile + { + try { + return $this->getIOBinaryFile($spiBinaryFileId); + } catch (FilesystemException $e) { + throw new BinaryFileNotFoundException($spiBinaryFileId); + } + } + + public function exists($spiBinaryFileId): bool + { + try { + return $this->filesystem->fileExists($spiBinaryFileId); + } catch (CorruptedPathDetected $e) { + $this->logger->error( + sprintf('Binary file with ID="%s" does not exist: %s', $spiBinaryFileId, $e->getMessage()), + ['exception' => $e], + ); + + return false; + } catch (FilesystemException $e) { + throw new IOException( + "Unable to check if file '$spiBinaryFileId' exists: {$e->getMessage()}", + $e + ); + } + } + + public function getMimeType($spiBinaryFileId): string + { + try { + return $this->filesystem->mimeType($spiBinaryFileId); + } catch (FilesystemException $e) { + throw new IOException( + "Unable to get mime type of file '$spiBinaryFileId': {$e->getMessage()}", + $e + ); + } + } + + /** + * Does nothing, as the binary data handler takes care of it. + */ + public function deleteDirectory($spiPath) + { + } + + /** + * @throws \League\Flysystem\FilesystemException + */ + private function getIOBinaryFile(string $spiBinaryFileId): IOBinaryFile + { + $spiBinaryFile = new IOBinaryFile(); + $spiBinaryFile->id = $spiBinaryFileId; + $spiBinaryFile->size = $this->filesystem->fileSize($spiBinaryFileId); + $spiBinaryFile->mtime = new DateTime( + '@' . $this->filesystem->lastModified($spiBinaryFileId) + ); + + return $spiBinaryFile; + } +} + +class_alias(Flysystem::class, 'eZ\Publish\Core\IO\IOMetadataHandler\Flysystem'); diff --git a/src/lib/IO/IOMetadataHandler/LegacyDFSCluster.php b/src/lib/IO/IOMetadataHandler/LegacyDFSCluster.php new file mode 100644 index 0000000000..3b6f0f9c34 --- /dev/null +++ b/src/lib/IO/IOMetadataHandler/LegacyDFSCluster.php @@ -0,0 +1,326 @@ +db = $connection; + $this->urlDecorator = $urlDecorator; + } + + /** + * Inserts a new reference to file $spiBinaryFileId. + * + * @since 6.10 The mtime of the $binaryFileCreateStruct must be a DateTime, as specified in the struct doc. + * + * @param \Ibexa\Contracts\Core\IO\BinaryFileCreateStruct $binaryFileCreateStruct + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException if the $binaryFileCreateStruct is invalid + * @throws \RuntimeException if a DBAL error occurs + * + * @return \Ibexa\Contracts\Core\IO\BinaryFile + */ + public function create(SPIBinaryFileCreateStruct $binaryFileCreateStruct) + { + if (!($binaryFileCreateStruct->mtime instanceof DateTime)) { + throw new InvalidArgumentException('$binaryFileCreateStruct', 'Property \'mtime\' must be a DateTime'); + } + + $path = (string)$this->addPrefix($binaryFileCreateStruct->id); + $params = [ + 'name' => $path, + 'name_hash' => md5($path), + 'name_trunk' => $this->getNameTrunk($binaryFileCreateStruct), + 'mtime' => $binaryFileCreateStruct->mtime->getTimestamp(), + 'size' => $binaryFileCreateStruct->size, + 'scope' => $this->getScope($binaryFileCreateStruct), + 'datatype' => $binaryFileCreateStruct->mimeType, + ]; + + try { + $this->db->insert('ezdfsfile', $params); + } catch (Exception $e) { + $this->db->update('ezdfsfile', [ + 'mtime' => $params['mtime'], + 'size' => $params['size'], + 'scope' => $params['scope'], + 'datatype' => $params['datatype'], + ], [ + 'name_hash' => $params['name_hash'], + ]); + } + + return $this->mapSPIBinaryFileCreateStructToSPIBinaryFile($binaryFileCreateStruct); + } + + /** + * Deletes file $spiBinaryFileId. + * + * @throws \Ibexa\Core\IO\Exception\BinaryFileNotFoundException If $spiBinaryFileId is not found + * + * @param string $spiBinaryFileId + */ + public function delete($spiBinaryFileId) + { + $path = (string)$this->addPrefix($spiBinaryFileId); + + // Unlike the legacy cluster, the file is directly deleted. It was inherited from the DB cluster anyway + $affectedRows = (int)$this->db->delete('ezdfsfile', [ + 'name_hash' => md5($path), + ]); + + if ($affectedRows !== 1) { + // Is this really necessary ? + throw new BinaryFileNotFoundException($path); + } + } + + /** + * Loads and returns metadata for $spiBinaryFileId. + * + * @param string $spiBinaryFileId + * + * @return \Ibexa\Contracts\Core\IO\BinaryFile + * + * @throws \Ibexa\Core\IO\Exception\BinaryFileNotFoundException if no row is found for $spiBinaryFileId + * @throws \Doctrine\DBAL\DBALException Any unhandled DBAL exception + */ + public function load($spiBinaryFileId) + { + $path = (string)$this->addPrefix($spiBinaryFileId); + + $queryBuilder = $this->db->createQueryBuilder(); + $result = $queryBuilder + ->select( + 'e.name_hash', + 'e.name', + 'e.name_trunk', + 'e.datatype', + 'e.scope', + 'e.size', + 'e.mtime', + 'e.expired', + 'e.status', + ) + ->from('ezdfsfile', 'e') + ->andWhere('e.name_hash = :name_hash') + ->andWhere('e.expired != true') + ->andWhere('e.mtime > 0') + ->setParameter('name_hash', md5($path)) + ->execute() + ; + + if ($result->rowCount() === 0) { + throw new BinaryFileNotFoundException($path); + } + + $row = $result->fetchAssociative() + ['id' => $spiBinaryFileId]; + + return $this->mapArrayToSPIBinaryFile($row); + } + + /** + * Checks if a file $spiBinaryFileId exists. + * + * @param string $spiBinaryFileId + * + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + * @throws \Doctrine\DBAL\DBALException Any unhandled DBAL exception + * + * @return bool + */ + public function exists($spiBinaryFileId) + { + $path = (string)$this->addPrefix($spiBinaryFileId); + + $queryBuilder = $this->db->createQueryBuilder(); + $result = $queryBuilder + ->select( + 'e.name_hash', + 'e.name', + 'e.name_trunk', + 'e.datatype', + 'e.scope', + 'e.size', + 'e.mtime', + 'e.expired', + 'e.status', + ) + ->from('ezdfsfile', 'e') + ->andWhere('e.name_hash = :name_hash') + ->andWhere('e.expired != true') + ->andWhere('e.mtime > 0') + ->setParameter('name_hash', md5($path)) + ->execute() + ; + + return $result->rowCount() === 1; + } + + /** + * @param \Ibexa\Contracts\Core\IO\BinaryFileCreateStruct $binaryFileCreateStruct + * + * @return mixed + */ + protected function getNameTrunk(SPIBinaryFileCreateStruct $binaryFileCreateStruct) + { + return $this->addPrefix($binaryFileCreateStruct->id); + } + + /** + * Returns the value for the scope meta field, based on the created file's path. + * + * Note that this is slightly incorrect, as it will return binaryfile for media files as well. It is a bit + * of an issue, but shouldn't be a blocker given that this meta field isn't used that much. + * + * @param \Ibexa\Contracts\Core\IO\BinaryFileCreateStruct $binaryFileCreateStruct + * + * @return string + */ + protected function getScope(SPIBinaryFileCreateStruct $binaryFileCreateStruct) + { + [$filePrefix] = explode('/', $binaryFileCreateStruct->id); + + switch ($filePrefix) { + case 'images': + return 'image'; + + case 'original': + return 'binaryfile'; + } + + return 'UNKNOWN_SCOPE'; + } + + /** + * Adds the internal prefix string to $id. + * + * @param string $id + * + * @return string prefixed id + */ + protected function addPrefix($id) + { + return isset($this->urlDecorator) ? $this->urlDecorator->decorate($id) : $id; + } + + /** + * Removes the internal prefix string from $prefixedId. + * + * @param string $prefixedId + * + * @return string the id without the prefix + * + * @throws \Ibexa\Core\IO\Exception\InvalidBinaryFileIdException if the prefix isn't found in $prefixedId + */ + protected function removePrefix($prefixedId) + { + return isset($this->urlDecorator) ? $this->urlDecorator->undecorate($prefixedId) : $prefixedId; + } + + public function getMimeType($spiBinaryFileId) + { + $queryBuilder = $this->db->createQueryBuilder(); + $result = $queryBuilder + ->select('e.datatype') + ->from('ezdfsfile', 'e') + ->andWhere('e.name_hash = :name_hash') + ->andWhere('e.expired != true') + ->andWhere('e.mtime > 0') + ->setParameter('name_hash', md5($this->addPrefix($spiBinaryFileId))) + ->execute() + ; + + if ($result->rowCount() == 0) { + throw new BinaryFileNotFoundException($spiBinaryFileId); + } + + $row = $result->fetchAssociative(); + + return $row['datatype']; + } + + /** + * Delete directory and all the content under specified directory. + * + * @param string $spiPath SPI Path, not prefixed by URL decoration + */ + public function deleteDirectory($spiPath) + { + $query = $this->db->createQueryBuilder(); + $query + ->delete('ezdfsfile') + ->where('name LIKE :spiPath ESCAPE :esc') + ->setParameter(':esc', '\\') + ->setParameter( + ':spiPath', + addcslashes($this->addPrefix(rtrim($spiPath, '/')), '%_') . '/%' + ); + $query->execute(); + } + + /** + * Maps an array of data base properties (id, size, mtime, datatype, md5_path, path...) to an SPIBinaryFile object. + * + * @param array $properties database properties array + * + * @return \Ibexa\Contracts\Core\IO\BinaryFile + */ + protected function mapArrayToSPIBinaryFile(array $properties) + { + $spiBinaryFile = new SPIBinaryFile(); + $spiBinaryFile->id = $properties['id']; + $spiBinaryFile->size = $properties['size']; + $spiBinaryFile->mtime = new DateTime('@' . $properties['mtime']); + $spiBinaryFile->mimeType = $properties['datatype']; + + return $spiBinaryFile; + } + + /** + * @param \Ibexa\Contracts\Core\IO\BinaryFileCreateStruct $binaryFileCreateStruct + * + * @return \Ibexa\Contracts\Core\IO\BinaryFile + */ + protected function mapSPIBinaryFileCreateStructToSPIBinaryFile(SPIBinaryFileCreateStruct $binaryFileCreateStruct) + { + $spiBinaryFile = new SPIBinaryFile(); + $spiBinaryFile->id = $binaryFileCreateStruct->id; + $spiBinaryFile->mtime = $binaryFileCreateStruct->mtime; + $spiBinaryFile->size = $binaryFileCreateStruct->size; + $spiBinaryFile->mimeType = $binaryFileCreateStruct->mimeType; + + return $spiBinaryFile; + } +} diff --git a/eZ/Publish/Core/IO/IOService.php b/src/lib/IO/IOService.php similarity index 88% rename from eZ/Publish/Core/IO/IOService.php rename to src/lib/IO/IOService.php index 40dcc2dafd..2df7587935 100644 --- a/eZ/Publish/Core/IO/IOService.php +++ b/src/lib/IO/IOService.php @@ -4,33 +4,33 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\IO; +namespace Ibexa\Core\IO; use Exception; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException; -use eZ\Publish\Core\IO\Exception\InvalidBinaryFileIdException; -use eZ\Publish\Core\IO\Exception\InvalidBinaryPrefixException; -use eZ\Publish\Core\IO\Exception\IOException; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\IO\Values\BinaryFileCreateStruct; -use eZ\Publish\SPI\IO\BinaryFile as SPIBinaryFile; -use eZ\Publish\SPI\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct; -use eZ\Publish\SPI\IO\MimeTypeDetector; +use Ibexa\Contracts\Core\IO\BinaryFile as SPIBinaryFile; +use Ibexa\Contracts\Core\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct; +use Ibexa\Contracts\Core\IO\MimeTypeDetector; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentValue; +use Ibexa\Core\IO\Exception\BinaryFileNotFoundException; +use Ibexa\Core\IO\Exception\InvalidBinaryFileIdException; +use Ibexa\Core\IO\Exception\InvalidBinaryPrefixException; +use Ibexa\Core\IO\Exception\IOException; +use Ibexa\Core\IO\Values\BinaryFile; +use Ibexa\Core\IO\Values\BinaryFileCreateStruct; /** * The io service for managing binary files. */ class IOService implements IOServiceInterface { - /** @var \eZ\Publish\Core\IO\IOBinarydataHandler */ + /** @var \Ibexa\Core\IO\IOBinarydataHandler */ protected $binarydataHandler; - /** @var \eZ\Publish\Core\IO\IOMetadataHandler */ + /** @var \Ibexa\Core\IO\IOMetadataHandler */ protected $metadataHandler; - /** @var \eZ\Publish\SPI\IO\MimeTypeDetector */ + /** @var \Ibexa\Contracts\Core\IO\MimeTypeDetector */ protected $mimeTypeDetector; /** @var array */ @@ -224,9 +224,9 @@ public function exists($binaryFileId) /** * Generates SPI BinaryFileCreateStruct object from provided API BinaryFileCreateStruct object. * - * @param \eZ\Publish\Core\IO\Values\BinaryFileCreateStruct $binaryFileCreateStruct + * @param \Ibexa\Core\IO\Values\BinaryFileCreateStruct $binaryFileCreateStruct * - * @return \eZ\Publish\SPI\IO\BinaryFileCreateStruct + * @return \Ibexa\Contracts\Core\IO\BinaryFileCreateStruct */ protected function buildSPIBinaryFileCreateStructObject(BinaryFileCreateStruct $binaryFileCreateStruct) { @@ -244,9 +244,9 @@ protected function buildSPIBinaryFileCreateStructObject(BinaryFileCreateStruct $ /** * Generates API BinaryFile object from provided SPI BinaryFile object. * - * @param \eZ\Publish\SPI\IO\BinaryFile $spiBinaryFile + * @param \Ibexa\Contracts\Core\IO\BinaryFile $spiBinaryFile * - * @return \eZ\Publish\Core\IO\Values\BinaryFile + * @return \Ibexa\Core\IO\Values\BinaryFile */ protected function buildDomainBinaryFileObject(SPIBinaryFile $spiBinaryFile) { @@ -283,7 +283,7 @@ protected function getPrefixedUri($binaryFileId) * * @return string * - * @throws \eZ\Publish\Core\IO\Exception\InvalidBinaryPrefixException + * @throws \Ibexa\Core\IO\Exception\InvalidBinaryPrefixException */ protected function removeUriPrefix($spiBinaryFileId) { @@ -301,7 +301,7 @@ protected function removeUriPrefix($spiBinaryFileId) /** * @param string $binaryFileId * - * @throws \eZ\Publish\Core\IO\Exception\InvalidBinaryFileIdException If the id is invalid + * @throws \Ibexa\Core\IO\Exception\InvalidBinaryFileIdException If the id is invalid */ protected function checkBinaryFileId($binaryFileId) { @@ -334,3 +334,5 @@ protected function isAbsolutePath($path) return $path[0] === '/' || (PHP_OS === 'WINNT' && $path[1] === ':'); } } + +class_alias(IOService::class, 'eZ\Publish\Core\IO\IOService'); diff --git a/src/lib/IO/IOServiceInterface.php b/src/lib/IO/IOServiceInterface.php new file mode 100644 index 0000000000..c676310600 --- /dev/null +++ b/src/lib/IO/IOServiceInterface.php @@ -0,0 +1,175 @@ +fileInfo; } } + +class_alias(FileInfo::class, 'eZ\Publish\Core\IO\MimeTypeDetector\FileInfo'); diff --git a/eZ/Publish/Core/IO/TolerantIOService.php b/src/lib/IO/TolerantIOService.php similarity index 81% rename from eZ/Publish/Core/IO/TolerantIOService.php rename to src/lib/IO/TolerantIOService.php index 2cef339ee3..a92363f57b 100644 --- a/eZ/Publish/Core/IO/TolerantIOService.php +++ b/src/lib/IO/TolerantIOService.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\IO; +namespace Ibexa\Core\IO; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException; -use eZ\Publish\Core\IO\Exception\InvalidBinaryAbsolutePathException; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\IO\Values\MissingBinaryFile; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\IO\Exception\BinaryFileNotFoundException; +use Ibexa\Core\IO\Exception\InvalidBinaryAbsolutePathException; +use Ibexa\Core\IO\Values\BinaryFile; +use Ibexa\Core\IO\Values\MissingBinaryFile; use Psr\Log\LoggerInterface; /** @@ -33,10 +33,10 @@ public function setLogger(LoggerInterface $logger = null) /** * Deletes $binaryFile. * - * @param \eZ\Publish\Core\IO\Values\BinaryFile $binaryFile + * @param \Ibexa\Core\IO\Values\BinaryFile $binaryFile * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue If the binary file is invalid - * @throws \eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException If the binary file isn't found + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentValue If the binary file is invalid + * @throws \Ibexa\Core\IO\Exception\BinaryFileNotFoundException If the binary file isn't found */ public function deleteBinaryFile(BinaryFile $binaryFile) { @@ -64,9 +64,9 @@ public function deleteBinaryFile(BinaryFile $binaryFile) * * @param string $binaryFileId * - * @return \eZ\Publish\Core\IO\Values\BinaryFile|\eZ\Publish\Core\IO\Values\MissingBinaryFile + * @return \Ibexa\Core\IO\Values\BinaryFile|\Ibexa\Core\IO\Values\MissingBinaryFile * - * @throws \eZ\Publish\Core\IO\Exception\InvalidBinaryAbsolutePathException + * @throws \Ibexa\Core\IO\Exception\InvalidBinaryAbsolutePathException */ public function loadBinaryFile($binaryFileId) { @@ -128,3 +128,5 @@ private function logMissingFile($id) $this->logger->info("BinaryFile with id $id not found"); } } + +class_alias(TolerantIOService::class, 'eZ\Publish\Core\IO\TolerantIOService'); diff --git a/eZ/Publish/Core/IO/UrlDecorator.php b/src/lib/IO/UrlDecorator.php similarity index 85% rename from eZ/Publish/Core/IO/UrlDecorator.php rename to src/lib/IO/UrlDecorator.php index 36aec360fa..6eb2d30cfc 100644 --- a/eZ/Publish/Core/IO/UrlDecorator.php +++ b/src/lib/IO/UrlDecorator.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\IO; +namespace Ibexa\Core\IO; /** * Modifies, both way, and URI. @@ -29,3 +29,5 @@ public function decorate($uri); */ public function undecorate($uri); } + +class_alias(UrlDecorator::class, 'eZ\Publish\Core\IO\UrlDecorator'); diff --git a/eZ/Publish/Core/IO/UrlDecorator/AbsolutePrefix.php b/src/lib/IO/UrlDecorator/AbsolutePrefix.php similarity index 87% rename from eZ/Publish/Core/IO/UrlDecorator/AbsolutePrefix.php rename to src/lib/IO/UrlDecorator/AbsolutePrefix.php index f1e3f9e084..661053b13d 100644 --- a/eZ/Publish/Core/IO/UrlDecorator/AbsolutePrefix.php +++ b/src/lib/IO/UrlDecorator/AbsolutePrefix.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\IO\UrlDecorator; +namespace Ibexa\Core\IO\UrlDecorator; /** * Prefixes the URI with a string, and makes it absolute. @@ -32,3 +32,5 @@ public function getPrefix(): string return $prefix; } } + +class_alias(AbsolutePrefix::class, 'eZ\Publish\Core\IO\UrlDecorator\AbsolutePrefix'); diff --git a/eZ/Publish/Core/IO/UrlDecorator/Prefix.php b/src/lib/IO/UrlDecorator/Prefix.php similarity index 76% rename from eZ/Publish/Core/IO/UrlDecorator/Prefix.php rename to src/lib/IO/UrlDecorator/Prefix.php index 98484906ae..66dbcfc2b9 100644 --- a/eZ/Publish/Core/IO/UrlDecorator/Prefix.php +++ b/src/lib/IO/UrlDecorator/Prefix.php @@ -6,18 +6,18 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\IO\UrlDecorator; +namespace Ibexa\Core\IO\UrlDecorator; -use eZ\Publish\Core\IO\Exception\InvalidBinaryPrefixException; -use eZ\Publish\Core\IO\IOConfigProvider; -use eZ\Publish\Core\IO\UrlDecorator; +use Ibexa\Core\IO\Exception\InvalidBinaryPrefixException; +use Ibexa\Core\IO\IOConfigProvider; +use Ibexa\Core\IO\UrlDecorator; /** * Prefixes the URI with a string. Ensures an initial / in the parameter. */ class Prefix implements UrlDecorator { - /** @var \eZ\Publish\Core\IO\IOConfigProvider */ + /** @var \Ibexa\Core\IO\IOConfigProvider */ protected $ioConfigResolver; public function __construct(IOConfigProvider $IOConfigResolver) @@ -43,7 +43,7 @@ public function decorate($id) } /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function undecorate($url) { @@ -59,3 +59,5 @@ public function undecorate($url) return trim(substr($url, strlen($prefix)), '/'); } } + +class_alias(Prefix::class, 'eZ\Publish\Core\IO\UrlDecorator\Prefix'); diff --git a/eZ/Publish/Core/IO/UrlRedecorator.php b/src/lib/IO/UrlRedecorator.php similarity index 90% rename from eZ/Publish/Core/IO/UrlRedecorator.php rename to src/lib/IO/UrlRedecorator.php index e2d77091a4..7f393d0a9d 100644 --- a/eZ/Publish/Core/IO/UrlRedecorator.php +++ b/src/lib/IO/UrlRedecorator.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\IO; +namespace Ibexa\Core\IO; /** * Converts urls between two decorators. @@ -37,3 +37,5 @@ public function redecorateFromTarget($uri) ); } } + +class_alias(UrlRedecorator::class, 'eZ\Publish\Core\IO\UrlRedecorator'); diff --git a/src/lib/IO/UrlRedecoratorInterface.php b/src/lib/IO/UrlRedecoratorInterface.php new file mode 100644 index 0000000000..bcc0c0d886 --- /dev/null +++ b/src/lib/IO/UrlRedecoratorInterface.php @@ -0,0 +1,50 @@ +redecorateFromSource( 'a/url' ); + * // 'b/url' + * + * $redecorator->redecorateFromTarget( 'b/url' ); + * // 'a/url' + * ``` + */ +interface UrlRedecoratorInterface +{ + /** + * Redecorates $uri from source to target. + * + * @param string $uri + * + * @return string + * + * @throws \Ibexa\Core\IO\Exception\InvalidBinaryFileIdException If $uri couldn't be interpreted b y the target decorator + */ + public function redecorateFromSource($uri); + + /** + * Redecorates $uri from source to target. + * + * @param string $uri + * + * @return string + * + * @throws \Ibexa\Core\IO\Exception\InvalidBinaryFileIdException If $uri couldn't be interpreted b y the target decorator + */ + public function redecorateFromTarget($uri); +} + +class_alias(UrlRedecoratorInterface::class, 'eZ\Publish\Core\IO\UrlRedecoratorInterface'); diff --git a/src/lib/IO/Values/BinaryFile.php b/src/lib/IO/Values/BinaryFile.php new file mode 100644 index 0000000000..247f026a62 --- /dev/null +++ b/src/lib/IO/Values/BinaryFile.php @@ -0,0 +1,64 @@ +persistence = $persistence; + } +} + +class_alias(AbstractPersistenceLimitationType::class, 'eZ\Publish\Core\Limitation\AbstractPersistenceLimitationType'); diff --git a/src/lib/Limitation/BlockingLimitationType.php b/src/lib/Limitation/BlockingLimitationType.php new file mode 100644 index 0000000000..b8bad0a85b --- /dev/null +++ b/src/lib/Limitation/BlockingLimitationType.php @@ -0,0 +1,153 @@ +identifier = $identifier; + } + + /** + * Accepts a Blocking Limitation value and checks for structural validity. + * + * Makes sure LimitationValue object and ->limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APIBlockingLimitation) { + throw new InvalidArgumentType('$limitationValue', 'BlockingLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + if (empty($limitationValue->limitationValues)) { + $validationErrors[] = new ValidationError( + "\$limitationValue->limitationValues => '%value%' can not be empty", + null, + [ + 'value' => $limitationValue->limitationValues, + ] + ); + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APIBlockingLimitation($this->identifier, $limitationValues); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + if (!$value instanceof APIBlockingLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: BlockingLimitation'); + } + + return false; + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + return new Criterion\MatchNone(); + } + + /** + * Returns info on valid $limitationValues. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException Values will need to be injected in ctor. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + throw new NotImplementedException(__METHOD__); + } +} + +class_alias(BlockingLimitationType::class, 'eZ\Publish\Core\Limitation\BlockingLimitationType'); diff --git a/src/lib/Limitation/ChangeOwnerLimitationType.php b/src/lib/Limitation/ChangeOwnerLimitationType.php new file mode 100644 index 0000000000..ed0005b578 --- /dev/null +++ b/src/lib/Limitation/ChangeOwnerLimitationType.php @@ -0,0 +1,120 @@ +limitationValues)) { + throw new InvalidArgumentType( + '$limitationValue->limitationValues', + 'array', + $limitationValue->limitationValues + ); + } + + foreach ($limitationValue->limitationValues as $key => $value) { + if (is_string($value) && is_numeric($value)) { + $limitationValue->limitationValues[$key] = (int)$value; + } + } + + $limitationValue->limitationValues = array_filter($limitationValue->limitationValues); + $limitationValue->limitationValues = array_unique($limitationValue->limitationValues); + } + + public function validate(Limitation $limitationValue): array + { + $validationErrors = []; + + foreach ($limitationValue->limitationValues as $key => $value) { + if (is_int($value)) { + continue; + } + + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' must be an integer", + null, + [ + 'value' => $value, + 'key' => $key, + ] + ); + } + + return $validationErrors; + } + + public function buildValue(array $limitationValues): ChangeOwnerLimitation + { + return new ChangeOwnerLimitation($limitationValues); + } + + public function evaluate( + Limitation $value, + APIUserReference $currentUser, + APIValueObject $object, + array $targets = null + ): ?bool { + if (!$object instanceof ContentCreateStruct) { + return self::ACCESS_ABSTAIN; + } + + $limitationValues = array_filter($value->limitationValues); + + if (empty($limitationValues)) { + return self::ACCESS_GRANTED; + } + + $userId = $currentUser->getUserId(); + $limitationValues = array_map( + static fn (int $value): int => $value === ChangeOwnerLimitation::LIMITATION_VALUE_SELF ? $userId : $value, + $limitationValues + ); + + if (!is_numeric($object->ownerId)) { + return self::ACCESS_ABSTAIN; + } + + if (in_array((int)$object->ownerId, $limitationValues, true)) { + return self::ACCESS_GRANTED; + } + + return self::ACCESS_DENIED; + } + + public function getCriterion(Limitation $value, APIUserReference $currentUser): Criterion\UserMetadata + { + return new Criterion\UserMetadata( + Criterion\UserMetadata::OWNER, + Criterion\Operator::IN, + $value->limitationValues + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function valueSchema(): void + { + throw new NotImplementedException(__METHOD__); + } +} diff --git a/src/lib/Limitation/ContentTypeLimitationType.php b/src/lib/Limitation/ContentTypeLimitationType.php new file mode 100644 index 0000000000..62e0e73136 --- /dev/null +++ b/src/lib/Limitation/ContentTypeLimitationType.php @@ -0,0 +1,237 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APIContentTypeLimitation) { + throw new InvalidArgumentType('$limitationValue', 'APIContentTypeLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $id) { + if (!is_string($id) && !is_int($id)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + foreach ($limitationValue->limitationValues as $key => $id) { + try { + $this->persistence->contentTypeHandler()->load($id); + } catch (APINotFoundException $e) { + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' does not exist in the backend", + null, + [ + 'value' => $id, + 'key' => $key, + ] + ); + } + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APIContentTypeLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool|null + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null): ?bool + { + $targets = $targets ?? []; + foreach ($targets as $target) { + if (!$target instanceof Target\Version) { + continue; + } + + $accessVote = $this->evaluateVersionTarget($target, $value); + + // continue evaluation of targets if there was no explicit grant/deny + if ($accessVote === self::ACCESS_ABSTAIN) { + continue; + } + + return $accessVote; + } + + if (!$value instanceof APIContentTypeLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: APIContentTypeLimitation'); + } + + if ($object instanceof Content) { + $object = $object->getVersionInfo()->getContentInfo(); + } elseif ($object instanceof VersionInfo) { + $object = $object->getContentInfo(); + } elseif (!$object instanceof ContentInfo && !$object instanceof ContentCreateStruct) { + throw new InvalidArgumentException( + '$object', + 'Must be of type: ContentCreateStruct, Content, VersionInfo or ContentInfo' + ); + } + + if (empty($value->limitationValues)) { + return false; + } + + if ($object instanceof ContentCreateStruct) { + return in_array($object->contentType->id, $value->limitationValues); + } + + /* + * @var $object ContentInfo + */ + return in_array($object->contentTypeId, $value->limitationValues); + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + if (empty($value->limitationValues)) { + // A Policy should not have empty limitationValues stored + throw new \RuntimeException('$value->limitationValues is empty'); + } + + if (!isset($value->limitationValues[1])) { + // 1 limitation value: EQ operation + return new Criterion\ContentTypeId($value->limitationValues[0]); + } + + // several limitation values: IN operation + return new Criterion\ContentTypeId($value->limitationValues); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Evaluate permissions to create new Version. + * + * @param \Ibexa\Contracts\Core\Limitation\Target\Version $version + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * + * @return bool|null + */ + private function evaluateVersionTarget( + Target\Version $version, + APILimitationValue $value + ): ?bool { + $accessVote = self::ACCESS_ABSTAIN; + + // ... unless there's a specific list of target translations + if (!empty($version->allContentTypeIdsList)) { + $accessVote = $this->evaluateMatchingAnyLimitation( + $version->allContentTypeIdsList, + $value->limitationValues + ); + } + + return $accessVote; + } + + /** + * Allow access if any of the given ContentTypes matches any of the limitation values. + * + * @param int[] $contentTypeIdsList + * @param string[] $limitationValues + * + * @return bool + */ + private function evaluateMatchingAnyLimitation( + array $contentTypeIdsList, + array $limitationValues + ): bool { + return empty(array_intersect(array_map('strval', $contentTypeIdsList), $limitationValues)) + ? self::ACCESS_DENIED + : self::ACCESS_GRANTED; + } +} + +class_alias(ContentTypeLimitationType::class, 'eZ\Publish\Core\Limitation\ContentTypeLimitationType'); diff --git a/eZ/Publish/Core/Limitation/LanguageLimitation/ContentDeleteEvaluator.php b/src/lib/Limitation/LanguageLimitation/ContentDeleteEvaluator.php similarity index 75% rename from eZ/Publish/Core/Limitation/LanguageLimitation/ContentDeleteEvaluator.php rename to src/lib/Limitation/LanguageLimitation/ContentDeleteEvaluator.php index bc2e4523ef..1019b1f9be 100644 --- a/eZ/Publish/Core/Limitation/LanguageLimitation/ContentDeleteEvaluator.php +++ b/src/lib/Limitation/LanguageLimitation/ContentDeleteEvaluator.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Limitation\LanguageLimitation; +namespace Ibexa\Core\Limitation\LanguageLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\Core\Limitation\LanguageLimitationType; -use eZ\Publish\SPI\Limitation\Target; +use Ibexa\Contracts\Core\Limitation\Target; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Core\Limitation\LanguageLimitationType; /** * @internal for internal use by LanguageLimitation @@ -38,3 +38,5 @@ public function evaluate(Target\Version $targetVersion, Limitation $limitationVa return $accessVote; } } + +class_alias(ContentDeleteEvaluator::class, 'eZ\Publish\Core\Limitation\LanguageLimitation\ContentDeleteEvaluator'); diff --git a/eZ/Publish/Core/Limitation/LanguageLimitation/ContentTranslationEvaluator.php b/src/lib/Limitation/LanguageLimitation/ContentTranslationEvaluator.php similarity index 75% rename from eZ/Publish/Core/Limitation/LanguageLimitation/ContentTranslationEvaluator.php rename to src/lib/Limitation/LanguageLimitation/ContentTranslationEvaluator.php index c97d6ad861..f868fbbe9f 100644 --- a/eZ/Publish/Core/Limitation/LanguageLimitation/ContentTranslationEvaluator.php +++ b/src/lib/Limitation/LanguageLimitation/ContentTranslationEvaluator.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Limitation\LanguageLimitation; +namespace Ibexa\Core\Limitation\LanguageLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\Core\Limitation\LanguageLimitationType; -use eZ\Publish\SPI\Limitation\Target; +use Ibexa\Contracts\Core\Limitation\Target; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Core\Limitation\LanguageLimitationType; /** * @internal for internal use by LanguageLimitation @@ -37,3 +37,5 @@ public function evaluate(Target\Version $targetVersion, Limitation $limitationVa : LanguageLimitationType::ACCESS_GRANTED; } } + +class_alias(ContentTranslationEvaluator::class, 'eZ\Publish\Core\Limitation\LanguageLimitation\ContentTranslationEvaluator'); diff --git a/src/lib/Limitation/LanguageLimitation/NewDraftEvaluator.php b/src/lib/Limitation/LanguageLimitation/NewDraftEvaluator.php new file mode 100644 index 0000000000..5dbe8a261b --- /dev/null +++ b/src/lib/Limitation/LanguageLimitation/NewDraftEvaluator.php @@ -0,0 +1,32 @@ +newStatus === VersionInfo::STATUS_DRAFT; + } + + public function evaluate(Target\Version $targetVersion, Limitation $limitationValue): ?bool + { + return LanguageLimitationType::ACCESS_GRANTED; + } +} + +class_alias(NewDraftEvaluator::class, 'eZ\Publish\Core\Limitation\LanguageLimitation\NewDraftEvaluator'); diff --git a/src/lib/Limitation/LanguageLimitation/VersionPublishingEvaluator.php b/src/lib/Limitation/LanguageLimitation/VersionPublishingEvaluator.php new file mode 100644 index 0000000000..7788285e23 --- /dev/null +++ b/src/lib/Limitation/LanguageLimitation/VersionPublishingEvaluator.php @@ -0,0 +1,41 @@ +forPublishLanguageCodesList); + } + + /** + * Evaluate publishing a specific translation of a Version. + */ + public function evaluate(Target\Version $targetVersion, Limitation $limitationValue): ?bool + { + $diff = array_diff( + $targetVersion->forPublishLanguageCodesList, + $limitationValue->limitationValues + ); + + return empty($diff) + ? LanguageLimitationType::ACCESS_GRANTED + : LanguageLimitationType::ACCESS_DENIED; + } +} + +class_alias(VersionPublishingEvaluator::class, 'eZ\Publish\Core\Limitation\LanguageLimitation\VersionPublishingEvaluator'); diff --git a/src/lib/Limitation/LanguageLimitation/VersionTargetEvaluator.php b/src/lib/Limitation/LanguageLimitation/VersionTargetEvaluator.php new file mode 100644 index 0000000000..c0893ef662 --- /dev/null +++ b/src/lib/Limitation/LanguageLimitation/VersionTargetEvaluator.php @@ -0,0 +1,24 @@ +persistenceLanguageHandler = $persistenceLanguageHandler; + $this->persistenceContentHandler = $persistenceContentHandler; + $this->versionTargetEvaluators = $versionTargetEvaluators; + } + + /** + * Accepts a Limitation value and checks for structural validity. + * + * Makes sure LimitationValue object and ->limitationValues is of correct type. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + */ + public function acceptValue(APILimitationValue $limitationValue): void + { + if (!$limitationValue instanceof APILanguageLimitation) { + throw new InvalidArgumentType( + '$limitationValue', + APILanguageLimitation::class, + $limitationValue + ); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType( + '$limitationValue->limitationValues', + 'array', + $limitationValue->limitationValues + ); + } + + foreach ($limitationValue->limitationValues as $key => $value) { + if (!is_string($value)) { + throw new InvalidArgumentType( + "\$limitationValue->limitationValues[{$key}]", + 'string', + $value + ); + } + } + } + + /** + * Makes sure every language code defined as limitation exists. + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue): array + { + $validationErrors = []; + $existingLanguages = $this->persistenceLanguageHandler->loadListByLanguageCodes( + $limitationValue->limitationValues + ); + $missingLanguages = array_diff( + $limitationValue->limitationValues, + array_keys($existingLanguages) + ); + if (!empty($missingLanguages)) { + $validationErrors[] = new ValidationError( + "limitationValues[] => '%languageCodes%' translation(s) do not exist", + null, + [ + 'languageCodes' => implode(', ', $missingLanguages), + ] + ); + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param array[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues): APILimitationValue + { + return new APILanguageLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target. + * + * {@inheritdoc} + */ + public function evaluate( + APILimitationValue $value, + APIUserReference $currentUser, + ValueObject $object, + array $targets = null + ): ?bool { + if (null === $targets) { + $targets = []; + } + + // the main focus here is an intent to update to a new Version and if not, validate if target is Location + foreach ($targets as $target) { + if (!$target instanceof Target) { + continue; + } + + if ($target instanceof Target\Version) { + $accessVote = $this->evaluateVersionTarget($target, $value); + } elseif ($target instanceof DestinationLocationTarget) { + $accessVote = $this->evaluateLocationTarget($target, $value); + } else { + continue; + } + + // continue evaluation of targets if there was no explicit grant/deny + if ($accessVote === self::ACCESS_ABSTAIN) { + continue; + } + + return $accessVote; + } + + // in other cases we need to evaluate object + return $this->evaluateObject($object, $value); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * + * @return bool|null + */ + private function evaluateObject(ValueObject $object, APILimitationValue $value): ?bool + { + // by default abstain from making decision for unknown object + $accessVote = self::ACCESS_ABSTAIN; + + // load for evaluation VersionInfo for Content & ContentInfo objects + if ($object instanceof Content) { + $object = $object->getVersionInfo(); + } elseif ($object instanceof ContentInfo) { + $object = $this->tryLoadingVersionInfo($object); + if ($object === null) { + return self::ACCESS_DENIED; + } + } + + // cover creating Content Draft for new Content item + if ($object instanceof ContentCreateStruct) { + $accessVote = $this->evaluateContentCreateStruct($object, $value); + } elseif ($object instanceof VersionInfo || $object instanceof SPIVersionInfo) { + $accessVote = in_array($object->initialLanguageCode, $value->limitationValues, true) + ? self::ACCESS_GRANTED + : self::ACCESS_DENIED; + } + + return $accessVote; + } + + private function tryLoadingVersionInfo(ContentInfo $contentInfo): ?SPIVersionInfo + { + try { + return $this->persistenceContentHandler->loadVersionInfo( + $contentInfo->id, + $contentInfo->currentVersionNo + ); + } catch (NotFoundException $e) { + return null; + } + } + + /** + * Evaluate language codes of allowed translations for ContentCreateStruct. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct $object + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * + * @return bool|null + */ + private function evaluateContentCreateStruct( + ContentCreateStruct $object, + APILimitationValue $value + ): ?bool { + $languageCodes = $this->getAllLanguageCodesFromCreateStruct($object); + + // check if object contains only allowed language codes + return empty(array_diff($languageCodes, $value->limitationValues)) + ? self::ACCESS_GRANTED + : self::ACCESS_DENIED; + } + + /** + * Evaluate permissions to create new Version. + * + * @param \Ibexa\Contracts\Core\Limitation\Target\Version $version + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * + * @return bool|null + */ + private function evaluateVersionTarget( + Target\Version $version, + APILimitationValue $value + ): ?bool { + $accessVote = self::ACCESS_ABSTAIN; + + foreach ($this->versionTargetEvaluators as $evaluator) { + if ($evaluator->accept($version)) { + $accessVote = $evaluator->evaluate($version, $value); + if ($accessVote === self::ACCESS_DENIED) { + return $accessVote; + } + } + } + + return $accessVote; + } + + private function evaluateLocationTarget( + DestinationLocationTarget $location, + APILimitationValue $value + ): ?bool { + $versionInfo = $this->tryLoadingVersionInfo($location->getTargetContentInfo()); + if ($versionInfo === null) { + return self::ACCESS_DENIED; + } + + return empty(array_diff($versionInfo->languageCodes, $value->limitationValues)) + ? self::ACCESS_GRANTED + : self::ACCESS_DENIED; + } + + /** + * Get unique list of language codes for all used translations, including mainLanguageCode. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct $contentCreateStruct + * + * @return string[] + */ + private function getAllLanguageCodesFromCreateStruct( + ContentCreateStruct $contentCreateStruct + ): array { + $languageCodes = [$contentCreateStruct->mainLanguageCode]; + foreach ($contentCreateStruct->fields as $field) { + $languageCodes[] = $field->languageCode; + } + + return array_unique($languageCodes); + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + public function getCriterion( + APILimitationValue $value, + APIUserReference $currentUser + ): CriterionInterface { + if (empty($value->limitationValues)) { + // A Policy should not have empty limitationValues stored + throw new BadStateException( + '$value', + '$value->limitationValues is empty' + ); + } + + // several limitation values: IN operation + return new Criterion\LanguageCode($value->limitationValues); + } + + /** + * For LanguageLimitationType it returns an empty array because schema is not deterministic. + * + * @see validate for business logic. + */ + public function valueSchema(): array + { + return []; + } +} + +class_alias(LanguageLimitationType::class, 'eZ\Publish\Core\Limitation\LanguageLimitationType'); diff --git a/src/lib/Limitation/LimitationIdentifierToLabelConverter.php b/src/lib/Limitation/LimitationIdentifierToLabelConverter.php new file mode 100644 index 0000000000..34b75ab1c7 --- /dev/null +++ b/src/lib/Limitation/LimitationIdentifierToLabelConverter.php @@ -0,0 +1,22 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APILocationLimitation) { + throw new InvalidArgumentType('$limitationValue', 'APILocationLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $id) { + if (!is_string($id) && !is_int($id)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + foreach ($limitationValue->limitationValues as $key => $id) { + try { + $this->persistence->locationHandler()->load($id); + } catch (APINotFoundException $e) { + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' does not exist in the backend", + null, + [ + 'value' => $id, + 'key' => $key, + ] + ); + } + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APILocationLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + if (!$value instanceof APILocationLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: APILocationLimitation'); + } + + if ($object instanceof ContentCreateStruct) { + return $this->evaluateForContentCreateStruct($value, $targets); + } elseif ($object instanceof Content) { + $object = $object->getVersionInfo()->getContentInfo(); + } elseif ($object instanceof VersionInfo) { + $object = $object->getContentInfo(); + } elseif (!$object instanceof ContentInfo) { + throw new InvalidArgumentException( + '$object', + 'Must be of type: ContentCreateStruct, Content, VersionInfo or ContentInfo' + ); + } + + // Load locations if no specific placement was provided + if ($targets === null) { + if ($object->published) { + $targets = $this->persistence->locationHandler()->loadLocationsByContent($object->id); + } elseif ($object->isTrashed()) { + $targets = $this->persistence->locationHandler()->loadLocationsByTrashContent($object->id); + } else { + // @todo Need support for draft locations to to work correctly + $targets = $this->persistence->locationHandler()->loadParentLocationsForDraftContent($object->id); + } + } + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location[]|\Ibexa\Contracts\Core\Persistence\Content\Location[] $targets */ + $targets = array_filter($targets, static function ($target) { + return $target instanceof Location || $target instanceof SPILocation; + }); + + if (empty($targets)) { + throw new InvalidArgumentException('$targets', 'Must contain at least one Location object'); + } + + foreach ($targets as $target) { + if (in_array($target->id, $value->limitationValues)) { + return self::ACCESS_GRANTED; + } + } + + return self::ACCESS_DENIED; + } + + /** + * Evaluate permissions for ContentCreateStruct against LocationCreateStruct placements. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If $targets does not contain + * objects of type LocationCreateStruct + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param array|null $targets + * + * @return bool + */ + protected function evaluateForContentCreateStruct(APILimitationValue $value, array $targets = null) + { + // If targets is empty/null return false as user does not have access + // to content w/o location with this limitation + if (empty($targets)) { + return false; + } + + $hasMandatoryTarget = false; + foreach ($targets as $target) { + if ($target instanceof LocationCreateStruct) { + $hasMandatoryTarget = true; + + // For ContentCreateStruct all placements must match + if (!in_array($target->parentLocationId, $value->limitationValues)) { + return false; + } + } + } + + if (false === $hasMandatoryTarget) { + throw new InvalidArgumentException( + '$targets', + 'If $object is ContentCreateStruct, it must contain LocationCreateStruct objects' + ); + } + + return true; + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + if (empty($value->limitationValues)) { + // A Policy should not have empty limitationValues stored + throw new \RuntimeException('$value->limitationValues is empty'); + } + + if (!isset($value->limitationValues[1])) { + // 1 limitation value: EQ operation + return new Criterion\LocationId($value->limitationValues[0]); + } + + // several limitation values: IN operation + return new Criterion\LocationId($value->limitationValues); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + return self::VALUE_SCHEMA_LOCATION_ID; + } +} + +class_alias(LocationLimitationType::class, 'eZ\Publish\Core\Limitation\LocationLimitationType'); diff --git a/src/lib/Limitation/MemberOfLimitationType.php b/src/lib/Limitation/MemberOfLimitationType.php index f47903ba0d..aaecc7683e 100644 --- a/src/lib/Limitation/MemberOfLimitationType.php +++ b/src/lib/Limitation/MemberOfLimitationType.php @@ -8,28 +8,27 @@ namespace Ibexa\Core\Limitation; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\API\Repository\Values\User\UserGroupRoleAssignment; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\User\UserRoleAssignment; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\Limitation\AbstractPersistenceLimitationType; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; +use Ibexa\Contracts\Core\Limitation\Type as SPILimitationTypeInterface; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation as APILimitationValue; use Ibexa\Contracts\Core\Repository\Values\User\Limitation\MemberOfLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroup; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroupRoleAssignment; +use Ibexa\Contracts\Core\Repository\Values\User\UserReference as APIUserReference; +use Ibexa\Contracts\Core\Repository\Values\User\UserRoleAssignment; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\ValidationError; final class MemberOfLimitationType extends AbstractPersistenceLimitationType implements SPILimitationTypeInterface { public const SELF_USER_GROUP = -1; /** - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException */ public function acceptValue(APILimitationValue $limitationValue): void { @@ -83,8 +82,6 @@ public function validate(APILimitationValue $limitationValue) /** * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation */ public function buildValue(array $limitationValues): APILimitationValue { diff --git a/src/lib/Limitation/NewObjectStateLimitationType.php b/src/lib/Limitation/NewObjectStateLimitationType.php new file mode 100644 index 0000000000..bc0006b24c --- /dev/null +++ b/src/lib/Limitation/NewObjectStateLimitationType.php @@ -0,0 +1,170 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APINewObjectStateLimitation) { + throw new InvalidArgumentType('$limitationValue', 'NewObjectStateLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $id) { + if (!is_string($id) && !is_int($id)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + foreach ($limitationValue->limitationValues as $key => $id) { + try { + $this->persistence->objectStateHandler()->load($id); + } catch (APINotFoundException $e) { + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' does not exist in the backend", + null, + [ + 'value' => $id, + 'key' => $key, + ] + ); + } + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APINewObjectStateLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + if (!$value instanceof APINewObjectStateLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: NewObjectStateLimitation'); + } + + if (!$object instanceof ContentInfo && !$object instanceof Content && !$object instanceof VersionInfo) { + throw new InvalidArgumentException('$object', 'Must be of type: Content, VersionInfo or ContentInfo'); + } + + if (empty($targets)) { + throw new InvalidArgumentException('$targets', 'Must contain ObjectState objects'); + } + + if (empty($value->limitationValues)) { + return false; + } + + foreach ($targets as $target) { + if (!$target instanceof ObjectState && !$target instanceof SPIObjectState) { + throw new InvalidArgumentException('$targets', 'Must contain ObjectState objects'); + } + + if (!in_array($target->id, $value->limitationValues)) { + return false; + } + } + + return true; + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException Not applicable, needs context of new state. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + throw new NotImplementedException(__METHOD__); + } +} + +class_alias(NewObjectStateLimitationType::class, 'eZ\Publish\Core\Limitation\NewObjectStateLimitationType'); diff --git a/src/lib/Limitation/NewSectionLimitationType.php b/src/lib/Limitation/NewSectionLimitationType.php new file mode 100644 index 0000000000..b9186c1feb --- /dev/null +++ b/src/lib/Limitation/NewSectionLimitationType.php @@ -0,0 +1,203 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APINewSectionLimitation) { + throw new InvalidArgumentType('$limitationValue', 'APINewSectionLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $id) { + if (!is_string($id) && !is_int($id)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + foreach ($limitationValue->limitationValues as $key => $id) { + try { + $this->persistence->sectionHandler()->load($id); + } catch (APINotFoundException $e) { + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' does not exist in the backend", + null, + [ + 'value' => $id, + 'key' => $key, + ] + ); + } + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APINewSectionLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + if (!$value instanceof APINewSectionLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: APINewSectionLimitation'); + } + + if (!$object instanceof ContentInfo && !$object instanceof Content && !$object instanceof VersionInfo) { + throw new InvalidArgumentException('$object', 'Must be of type: Content, VersionInfo, ContentInfo'); + } + + if (empty($targets)) { + throw new InvalidArgumentException('$targets', 'Must contain Section objects'); + } + + if (empty($value->limitationValues)) { + return false; + } + + return $this->doEvaluate($value, $targets); + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException Not applicable, needs context of new section. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + throw new NotImplementedException(__METHOD__); + } + + /** + * {@inheritdoc} + */ + public function getCriterionByTarget(APILimitationValue $value, APIUserReference $currentUser, ?array $targets): CriterionInterface + { + if (empty($targets)) { + throw new InvalidArgumentException('$targets', 'Must contain Section objects'); + } + + if ($this->doEvaluate($value, $targets)) { + return new MatchAll(); + } else { + return new MatchNone(); + } + } + + /** + * Returns true if given limitation value allows all given sections. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param array|null $targets + * + * @return bool + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + */ + private function doEvaluate(APILimitationValue $value, array $targets): bool + { + foreach ($targets as $target) { + if (!$target instanceof Section && !$target instanceof SPISection) { + throw new InvalidArgumentException('$targets', 'Must contain Section objects'); + } + + if (!in_array($target->id, $value->limitationValues)) { + return false; + } + } + + return true; + } +} + +class_alias(NewSectionLimitationType::class, 'eZ\Publish\Core\Limitation\NewSectionLimitationType'); diff --git a/eZ/Publish/Core/Limitation/ObjectStateLimitationType.php b/src/lib/Limitation/ObjectStateLimitationType.php similarity index 76% rename from eZ/Publish/Core/Limitation/ObjectStateLimitationType.php rename to src/lib/Limitation/ObjectStateLimitationType.php index 28aa2fa218..c1b015ca54 100644 --- a/eZ/Publish/Core/Limitation/ObjectStateLimitationType.php +++ b/src/lib/Limitation/ObjectStateLimitationType.php @@ -4,26 +4,26 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Limitation; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation as APIObjectStateLimitation; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Handler; +namespace Ibexa\Core\Limitation; + +use Ibexa\Contracts\Core\Limitation\Type as SPILimitationTypeInterface; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\Handler; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as APINotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation as APILimitationValue; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation as APIObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\UserReference as APIUserReference; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\BadStateException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\ValidationError; use RuntimeException; /** @@ -36,9 +36,9 @@ class ObjectStateLimitationType extends AbstractPersistenceLimitationType implem * * Makes sure LimitationValue object and ->limitationValues is of correct type. * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue */ public function acceptValue(APILimitationValue $limitationValue) { @@ -62,9 +62,9 @@ public function acceptValue(APILimitationValue $limitationValue) * * Make sure {@link acceptValue()} is checked first! * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitationValue + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue * - * @return \eZ\Publish\SPI\FieldType\ValidationError[] + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] */ public function validate(APILimitationValue $limitationValue) { @@ -92,7 +92,7 @@ public function validate(APILimitationValue $limitationValue) * * @param mixed[] $limitationValues * - * @return \eZ\Publish\API\Repository\Values\User\Limitation + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation */ public function buildValue(array $limitationValues) { @@ -102,20 +102,24 @@ public function buildValue(array $limitationValues) /** * Evaluate permission against content & target(placement/parent/assignment). * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser - * @param \eZ\Publish\API\Repository\Values\ValueObject $object - * @param \eZ\Publish\API\Repository\Values\ValueObject[]| $targets An array of location, parent or "assignment" value objects + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets An array of location, parent or "assignment" value objects * * @return bool */ - public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null): bool - { + public function evaluate( + APILimitationValue $value, + APIUserReference $currentUser, + ValueObject $object, + array $targets = null + ): bool { if (!$value instanceof APIObjectStateLimitation) { throw new InvalidArgumentException('$value', 'Must be of type: APIObjectStateLimitation'); } @@ -201,10 +205,10 @@ private function areObjectStatesMatchingTheLimitation( /** * Returns Criterion for use in find() query. * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $value - * @param \eZ\Publish\API\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser * - * @return \eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface|\eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOperator + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface|\Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalOperator */ public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) { @@ -261,7 +265,7 @@ private function groupLimitationValues(array $limitationValues) } /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException */ public function valueSchema() { @@ -269,7 +273,7 @@ public function valueSchema() } /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ private function getContentInfo(ValueObject $object): ContentInfo { @@ -292,7 +296,7 @@ private function getContentInfo(ValueObject $object): ContentInfo * * @return int[] Object State IDs to verify * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException */ private function getObjectStateIdsForContentCreate( Handler $objectStateHandler, @@ -320,9 +324,9 @@ private function getObjectStateIdsForContentCreate( } /** - * @param \eZ\Publish\SPI\Persistence\Content\ObjectState[] $states + * @param \Ibexa\Contracts\Core\Persistence\Content\ObjectState[] $states * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException */ private function getDefaultStateId(array $states, Group $stateGroup): int { @@ -364,3 +368,5 @@ private function getObjectStateIdsToVerify( return $objectStateIdsToVerify; } } + +class_alias(ObjectStateLimitationType::class, 'eZ\Publish\Core\Limitation\ObjectStateLimitationType'); diff --git a/src/lib/Limitation/OwnerLimitationType.php b/src/lib/Limitation/OwnerLimitationType.php new file mode 100644 index 0000000000..c1a4da4c06 --- /dev/null +++ b/src/lib/Limitation/OwnerLimitationType.php @@ -0,0 +1,192 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APIOwnerLimitation) { + throw new InvalidArgumentType('$limitationValue', 'APIOwnerLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $value) { + // Cast integers passed as string to int + if (is_string($value) && ctype_digit($value)) { + $limitationValue->limitationValues[$key] = (int)$value; + } elseif (!is_int($value)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int', $value); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + foreach ($limitationValue->limitationValues as $key => $value) { + if ($value !== 1 && $value !== 2) { + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' must be either 1 (owner) or 2 (session)", + null, + [ + 'value' => $value, + 'key' => $key, + ] + ); + } + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APIOwnerLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + * + * @todo Add support for $limitationValues[0] == 2 when session values can be injected somehow, or deprecate + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + if (!$value instanceof APIOwnerLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: APIOwnerLimitation'); + } + + if ($value->limitationValues[0] != 1 && $value->limitationValues[0] != 2) { + throw new BadStateException( + 'Owner limitation', + 'Expected Limitation value to be 1 or 2 instead of' . $value->limitationValues[0] + ); + } + + if ($object instanceof Content) { + $object = $object->getVersionInfo()->getContentInfo(); + } elseif ($object instanceof VersionInfo) { + $object = $object->getContentInfo(); + } elseif (!$object instanceof ContentInfo && !$object instanceof ContentCreateStruct) { + throw new InvalidArgumentException( + '$object', + 'Must be of type: ContentCreateStruct, Content, VersionInfo or ContentInfo' + ); + } + + $userId = $currentUser->getUserId(); + + /* + * @var $object ContentInfo + */ + $isOwner = $object->ownerId === $userId; + $isSelf = $object instanceof ContentInfo && $object->id === $userId; + + return $isOwner || $isSelf; + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + * + * @todo Add support for $limitationValues[0] == 2 when session values can be injected somehow, or deprecate + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + if (empty($value->limitationValues)) { + // A Policy should not have empty limitationValues stored + throw new \RuntimeException('$value->limitationValues is empty'); + } + + if ($value->limitationValues[0] != 1 && $value->limitationValues[0] != 2) { + throw new BadStateException( + 'Parent User Group limitation', + 'Expected Limitation value to be 1 or 2 instead of' . $value->limitationValues[0] + ); + } + + return new Criterion\UserMetadata( + Criterion\UserMetadata::OWNER, + Criterion\Operator::EQ, + $currentUser->getUserId() + ); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + throw new NotImplementedException(__METHOD__); + } +} + +class_alias(OwnerLimitationType::class, 'eZ\Publish\Core\Limitation\OwnerLimitationType'); diff --git a/src/lib/Limitation/ParentContentTypeLimitationType.php b/src/lib/Limitation/ParentContentTypeLimitationType.php new file mode 100644 index 0000000000..f1e7fbe761 --- /dev/null +++ b/src/lib/Limitation/ParentContentTypeLimitationType.php @@ -0,0 +1,261 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APIParentContentTypeLimitation) { + throw new InvalidArgumentType('$limitationValue', 'APIParentContentTypeLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $id) { + if (!is_string($id) && !is_int($id)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + foreach ($limitationValue->limitationValues as $key => $id) { + try { + $this->persistence->contentTypeHandler()->load($id); + } catch (APINotFoundException $e) { + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' does not exist in the backend", + null, + [ + 'value' => $id, + 'key' => $key, + ] + ); + } + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APIParentContentTypeLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + if (!$value instanceof APIParentContentTypeLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: APIParentContentTypeLimitation'); + } + + if ($object instanceof ContentCreateStruct) { + return $this->evaluateForContentCreateStruct($value, $targets); + } elseif ($object instanceof Content) { + $object = $object->getVersionInfo()->getContentInfo(); + } elseif ($object instanceof VersionInfo) { + $object = $object->getContentInfo(); + } elseif (!$object instanceof ContentInfo) { + throw new InvalidArgumentException( + '$object', + 'Must be of type: ContentCreateStruct, Content, VersionInfo or ContentInfo' + ); + } + + // Try to load locations if no targets were provided + if (empty($targets)) { + if ($object->published) { + $targets = $this->loadParentLocations($object); + } else { + // @todo Need support for draft locations to to work correctly + $targets = $this->persistence->locationHandler()->loadParentLocationsForDraftContent($object->id); + } + } + + // If targets is empty/null return false as user does not have access + // to content w/o location with this limitation + if (empty($targets)) { + return false; + } + + foreach ($targets as $target) { + if ($target instanceof LocationCreateStruct) { + $target = $this->persistence->locationHandler()->load($target->parentLocationId); + } + + if ($target instanceof Location) { + $contentTypeId = $target->getContentInfo()->contentTypeId; + } elseif ($target instanceof SPILocation) { + $spiContentInfo = $this->persistence->contentHandler()->loadContentInfo($target->contentId); + $contentTypeId = $spiContentInfo->contentTypeId; + } else { + throw new InvalidArgumentException( + '$targets', + 'Must contain Location or LocationCreateStruct objects' + ); + } + + if (!in_array($contentTypeId, $value->limitationValues)) { + return false; + } + } + + return true; + } + + /** + * Evaluate permissions for ContentCreateStruct against LocationCreateStruct placements. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If $targets does not contain + * objects of type LocationCreateStruct + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param array $targets + * + * @return bool + */ + protected function evaluateForContentCreateStruct(APILimitationValue $value, array $targets = null) + { + // If targets is empty/null return false as user does not have access + // to content w/o location with this limitation + if (empty($targets)) { + return false; + } + + $hasMandatoryTarget = false; + foreach ($targets as $target) { + if ($target instanceof LocationCreateStruct) { + $hasMandatoryTarget = true; + $location = $this->persistence->locationHandler()->load($target->parentLocationId); + $contentTypeId = $this->persistence->contentHandler()->loadContentInfo($location->contentId)->contentTypeId; + + if (!in_array($contentTypeId, $value->limitationValues)) { + return false; + } + } + } + + if (false === $hasMandatoryTarget) { + throw new InvalidArgumentException( + '$targets', + 'If $object is ContentCreateStruct, it must contain LocationCreateStruct objects' + ); + } + + return true; + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + throw new NotImplementedException(__METHOD__); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Location[] + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function loadParentLocations(ContentInfo $contentInfo) + { + $locations = $this->persistence->locationHandler()->loadLocationsByContent($contentInfo->id); + $parentLocations = []; + foreach ($locations as $location) { + if ($location->depth > 0) { + $parentLocations[] = $this->persistence->locationHandler()->load($location->parentId); + } + } + + return $parentLocations; + } +} + +class_alias(ParentContentTypeLimitationType::class, 'eZ\Publish\Core\Limitation\ParentContentTypeLimitationType'); diff --git a/src/lib/Limitation/ParentDepthLimitationType.php b/src/lib/Limitation/ParentDepthLimitationType.php new file mode 100644 index 0000000000..32ad40fb32 --- /dev/null +++ b/src/lib/Limitation/ParentDepthLimitationType.php @@ -0,0 +1,220 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APIParentDepthLimitation) { + throw new InvalidArgumentType('$limitationValue', 'APIParentDepthLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $value) { + // Cast integers passed as string to int + if (is_string($value) && ctype_digit($value)) { + $limitationValue->limitationValues[$key] = (int)$value; + } elseif (!is_int($value)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int', $value); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APIParentDepthLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + if (!$value instanceof APIParentDepthLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: APIParentDepthLimitation'); + } + + if ($object instanceof ContentCreateStruct) { + return $this->evaluateForContentCreateStruct($value, $targets); + } elseif ($object instanceof Content) { + $object = $object->getVersionInfo()->getContentInfo(); + } elseif ($object instanceof VersionInfo) { + $object = $object->getContentInfo(); + } elseif (!$object instanceof ContentInfo) { + throw new InvalidArgumentException( + '$object', + 'Must be of type: ContentCreateStruct, Content, VersionInfo or ContentInfo' + ); + } + + // Load locations if no specific placement was provided + if (empty($targets)) { + if ($object->published) { + $targets = $this->persistence->locationHandler()->loadLocationsByContent($object->id); + } else { + // @todo Need support for draft locations to to work correctly + $targets = $this->persistence->locationHandler()->loadParentLocationsForDraftContent($object->id); + } + } + + // Parent Limitations are usually used by content/create where target is specified, + // so we return false if not provided. + if (empty($targets)) { + return false; + } + + foreach ($targets as $target) { + if ($target instanceof Location || $target instanceof SPILocation) { + $depth = $target->depth; + } else { + throw new InvalidArgumentException( + '$targets', + 'Must contain Location objects' + ); + } + + // All placements must match + if (!in_array($depth, $value->limitationValues)) { + return false; + } + } + + return true; + } + + /** + * Evaluate permissions for ContentCreateStruct against LocationCreateStruct placements. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If $targets does not contain + * objects of type LocationCreateStruct + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param array|null $targets + * + * @return bool + */ + protected function evaluateForContentCreateStruct(APILimitationValue $value, array $targets = null) + { + // If targets is empty/null return false as user does not have access + // to content w/o location with this limitation + if (empty($targets)) { + return false; + } + $hasMandatoryTarget = false; + foreach ($targets as $target) { + if ($target instanceof LocationCreateStruct) { + $hasMandatoryTarget = true; + $depth = $this->persistence->locationHandler()->load($target->parentLocationId)->depth; + + // All placements must match + if (!in_array($depth, $value->limitationValues)) { + return false; + } + } + } + + if (false === $hasMandatoryTarget) { + throw new InvalidArgumentException( + '$targets', + 'If $object is ContentCreateStruct, it must contain LocationCreateStruct objects' + ); + } + + return true; + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + throw new NotImplementedException(__METHOD__); + } +} + +class_alias(ParentDepthLimitationType::class, 'eZ\Publish\Core\Limitation\ParentDepthLimitationType'); diff --git a/src/lib/Limitation/ParentOwnerLimitationType.php b/src/lib/Limitation/ParentOwnerLimitationType.php new file mode 100644 index 0000000000..7c7059d953 --- /dev/null +++ b/src/lib/Limitation/ParentOwnerLimitationType.php @@ -0,0 +1,193 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APIParentOwnerLimitation) { + throw new InvalidArgumentType('$limitationValue', 'APIParentOwnerLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $value) { + // Cast integers passed as string to int + if (is_string($value) && ctype_digit($value)) { + $limitationValue->limitationValues[$key] = (int)$value; + } elseif (!is_int($value)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int', $value); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + foreach ($limitationValue->limitationValues as $key => $value) { + if ($value !== 1 && $value !== 2) { + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' must be either 1 (owner) or 2 (session)", + null, + [ + 'value' => $value, + 'key' => $key, + ] + ); + } + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APIParentOwnerLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + * + * @todo Add support for $limitationValues[0] == 2 when session values can be injected somehow + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + if (!$value instanceof APIParentOwnerLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: APIParentOwnerLimitation'); + } + + if ($value->limitationValues[0] != 1 && $value->limitationValues[0] != 2) { + throw new BadStateException( + 'Parent Owner limitation', + 'Expected Limitation value to be 1 or 2 instead of' . $value->limitationValues[0] + ); + } + + // Parent Limitations are usually used by content/create where target is specified, so we return false if not provided. + if (empty($targets)) { + return false; + } + + $hasMandatoryTarget = false; + foreach ($targets as $target) { + if ($target instanceof LocationCreateStruct) { + $hasMandatoryTarget = true; + $target = $this->persistence->locationHandler()->load($target->parentLocationId); + } + + if ($target instanceof Location) { + $hasMandatoryTarget = true; + $targetContentInfo = $target->getContentInfo(); + } elseif ($target instanceof SPILocation) { + $hasMandatoryTarget = true; + $targetContentInfo = $this->persistence->contentHandler()->loadContentInfo($target->contentId); + } else { + continue; + } + + $userId = $currentUser->getUserId(); + + $isOwner = $targetContentInfo->ownerId === $userId; + $isSelf = $targetContentInfo->id === $userId; + + if (!($isOwner || $isSelf)) { + return false; + } + } + + if (false === $hasMandatoryTarget) { + throw new InvalidArgumentException( + '$targets', + 'Must contain Location or LocationCreateStruct objects' + ); + } + + return true; + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + throw new NotImplementedException(__METHOD__); + } +} + +class_alias(ParentOwnerLimitationType::class, 'eZ\Publish\Core\Limitation\ParentOwnerLimitationType'); diff --git a/src/lib/Limitation/ParentUserGroupLimitationType.php b/src/lib/Limitation/ParentUserGroupLimitationType.php new file mode 100644 index 0000000000..b4239508c0 --- /dev/null +++ b/src/lib/Limitation/ParentUserGroupLimitationType.php @@ -0,0 +1,217 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APIParentUserGroupLimitation) { + throw new InvalidArgumentType('$limitationValue', 'APIParentUserGroupLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $value) { + // Accept a true value for b/c with 5.0 + if ($value === true) { + $limitationValue->limitationValues[$key] = 1; + } elseif (is_string($value) && ctype_digit($value)) { + // Cast integers passed as string to int + $limitationValue->limitationValues[$key] = (int)$value; + } elseif (!is_int($value)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int', $value); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + foreach ($limitationValue->limitationValues as $key => $value) { + if ($value !== 1) { + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' must be 1 (owner)", + null, + [ + 'value' => $value, + 'key' => $key, + ] + ); + } + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APIParentUserGroupLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + if (!$value instanceof APIParentUserGroupLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: APIParentUserGroupLimitation'); + } + + if ($value->limitationValues[0] != 1) { + throw new BadStateException( + 'Parent User Group limitation', + 'Expected Limitation value to be 1 instead of' . $value->limitationValues[0] + ); + } + + // Parent Limitations are usually used by content/create where target is specified, so we return false if not provided. + if (empty($targets)) { + return false; + } + + $locationHandler = $this->persistence->locationHandler(); + $currentUserLocations = $locationHandler->loadLocationsByContent($currentUser->getUserId()); + if (empty($currentUserLocations)) { + return false; + } + + $hasMandatoryTarget = false; + foreach ($targets as $target) { + if ($target instanceof LocationCreateStruct) { + $hasMandatoryTarget = true; + $target = $locationHandler->load($target->parentLocationId); + } + + if ($target instanceof Location) { + $hasMandatoryTarget = true; + // $target is assumed to be parent in this case + $parentOwnerId = $target->getContentInfo()->ownerId; + } elseif ($target instanceof SPILocation) { + $hasMandatoryTarget = true; + // $target is assumed to be parent in this case + $spiContentInfo = $this->persistence->contentHandler()->loadContentInfo($target->contentId); + $parentOwnerId = $spiContentInfo->ownerId; + } else { + continue; + } + + if ($parentOwnerId === $currentUser->getUserId()) { + continue; + } + + /* + * As long as SPI userHandler and API UserService does not speak the same language, this is the ugly truth; + */ + $locationHandler = $this->persistence->locationHandler(); + $parentOwnerLocations = $locationHandler->loadLocationsByContent($parentOwnerId); + if (empty($parentOwnerLocations)) { + return false; + } + + foreach ($parentOwnerLocations as $parentOwnerLocation) { + foreach ($currentUserLocations as $currentUserLocation) { + if ($parentOwnerLocation->parentId === $currentUserLocation->parentId) { + continue 3; + } + } + } + + return false; + } + + if (false === $hasMandatoryTarget) { + throw new InvalidArgumentException( + '$targets', + 'Must contain Location or LocationCreateStruct objects' + ); + } + + return true; + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + throw new NotImplementedException(__METHOD__); + } +} + +class_alias(ParentUserGroupLimitationType::class, 'eZ\Publish\Core\Limitation\ParentUserGroupLimitationType'); diff --git a/src/lib/Limitation/RoleLimitationType.php b/src/lib/Limitation/RoleLimitationType.php index 7089f17e25..5940e1d021 100644 --- a/src/lib/Limitation/RoleLimitationType.php +++ b/src/lib/Limitation/RoleLimitationType.php @@ -8,34 +8,33 @@ namespace Ibexa\Core\Limitation; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitationValue; -use eZ\Publish\API\Repository\Values\User\Role; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\API\Repository\Values\User\UserGroupRoleAssignment; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\API\Repository\Values\User\UserRoleAssignment; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\Limitation\AbstractPersistenceLimitationType; -use eZ\Publish\SPI\Limitation\Type as SPILimitationTypeInterface; -use Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation; +use Ibexa\Contracts\Core\Limitation\Type as SPILimitationTypeInterface; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation as APILimitationValue; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\UserRoleLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Role; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroup; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroupRoleAssignment; +use Ibexa\Contracts\Core\Repository\Values\User\UserReference as APIUserReference; +use Ibexa\Contracts\Core\Repository\Values\User\UserRoleAssignment; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\ValidationError; final class RoleLimitationType extends AbstractPersistenceLimitationType implements SPILimitationTypeInterface { /** - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException */ public function acceptValue(APILimitationValue $limitationValue): void { - if (!$limitationValue instanceof RoleLimitation) { + if (!$limitationValue instanceof UserRoleLimitation) { throw new InvalidArgumentType( '$limitationValue', - RoleLimitation::class, + UserRoleLimitation::class, $limitationValue ); } @@ -50,7 +49,7 @@ public function acceptValue(APILimitationValue $limitationValue): void foreach ($limitationValue->limitationValues as $key => $id) { if (!is_int($id)) { - throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int', $id); } } } @@ -79,20 +78,18 @@ public function validate(APILimitationValue $limitationValue) /** * @param mixed[] $limitationValues - * - * @return \eZ\Publish\API\Repository\Values\User\Limitation */ public function buildValue(array $limitationValues): APILimitationValue { - return new RoleLimitation(['limitationValues' => $limitationValues]); + return new UserRoleLimitation(['limitationValues' => $limitationValues]); } public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) { - if (!$value instanceof RoleLimitation) { + if (!$value instanceof UserRoleLimitation) { throw new InvalidArgumentException( '$value', - sprintf('Must be of type: %s', RoleLimitation::class) + sprintf('Must be of type: %s', UserRoleLimitation::class) ); } @@ -136,7 +133,7 @@ public function valueSchema() throw new NotImplementedException(__METHOD__); } - private function evaluateRole(RoleLimitation $value, Role $role): bool + private function evaluateRole(UserRoleLimitation $value, Role $role): bool { return in_array($role->id, $value->limitationValues); } diff --git a/src/lib/Limitation/SectionLimitationType.php b/src/lib/Limitation/SectionLimitationType.php new file mode 100644 index 0000000000..99769e3958 --- /dev/null +++ b/src/lib/Limitation/SectionLimitationType.php @@ -0,0 +1,187 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APISectionLimitation) { + throw new InvalidArgumentType('$limitationValue', 'APISectionLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $id) { + if (!is_string($id) && !is_int($id)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + foreach ($limitationValue->limitationValues as $key => $id) { + try { + $this->persistence->sectionHandler()->load($id); + } catch (APINotFoundException $e) { + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' does not exist in the backend", + null, + [ + 'value' => $id, + 'key' => $key, + ] + ); + } + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APISectionLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + if (!$value instanceof APISectionLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: APISectionLimitation'); + } + + if (empty($value->limitationValues)) { + return false; + } + + /** + * Two cases supported: + * 1. $object is Section, for possible future support on Section limitations, i.e. be able to limit section/edit + * 2. $object is Content[Info]/VersionInfo, for all existing content policies, to limit by Section. + */ + if ($object instanceof Section) { + return in_array($object->id, $value->limitationValues); + } elseif ($object instanceof Content) { + $object = $object->getVersionInfo()->getContentInfo(); + } elseif ($object instanceof VersionInfo) { + $object = $object->getContentInfo(); + } elseif (!$object instanceof ContentInfo && !$object instanceof ContentCreateStruct) { + // As this is Role limitation we need to signal abstain on unsupported $object + return self::ACCESS_ABSTAIN; + } + + /* + * We ignore Targets here, they are only interesting in NewState limitation as we on this one is more interested + * the section already assigned to object. + * + * We can't use strict comparison because limitationValues is an array of string + */ + if ($object instanceof ContentCreateStruct) { + return in_array($object->sectionId, $value->limitationValues); + } + + return in_array($object->getSectionId(), $value->limitationValues); + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + if (empty($value->limitationValues)) { + // A Policy should not have empty limitationValues stored + throw new \RuntimeException('$value->limitationValues is empty'); + } + + if (!isset($value->limitationValues[1])) { + // 1 limitation value: EQ operation + return new Criterion\SectionId($value->limitationValues[0]); + } + + // several limitation values: IN operation + return new Criterion\SectionId($value->limitationValues); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + throw new NotImplementedException(__METHOD__); + } +} + +class_alias(SectionLimitationType::class, 'eZ\Publish\Core\Limitation\SectionLimitationType'); diff --git a/src/lib/Limitation/SiteAccessLimitationType.php b/src/lib/Limitation/SiteAccessLimitationType.php new file mode 100644 index 0000000000..13a087195d --- /dev/null +++ b/src/lib/Limitation/SiteAccessLimitationType.php @@ -0,0 +1,187 @@ +siteAccessService = $siteAccessService; + } + + /** + * Generates the SiteAccess value as CRC32. + */ + public function generateSiteAccessValue(string $sa): string + { + return sprintf('%u', crc32($sa)); + } + + /** + * Accepts a Limitation value and checks for structural validity. + * + * Makes sure LimitationValue object and ->limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APISiteAccessLimitation) { + throw new InvalidArgumentType('$limitationValue', 'APISiteAccessLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $value) { + // Value must be a CRC32, so can be either as string or integer. + if (!is_string($value) && !is_int($value)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'string or integer', $value); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + $siteAccessList = $this->getSiteAccessList(); + foreach ($limitationValue->limitationValues as $key => $value) { + if (!isset($siteAccessList[$value])) { + $validationErrors[] = new ValidationError( + "\$limitationValue->limitationValues[%key%] => Invalid SiteAccess value \"$value\"", + null, + [ + 'value' => $value, + 'key' => $key, + ] + ); + } + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APISiteAccessLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * SiteAccess limitation takes a SiteAccess as ValueObject, and is hence like in legacy only suitable for user/login + * and similar policies. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + if (!$value instanceof APISiteAccessLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: APISiteAccessLimitation'); + } + + if (!$object instanceof SiteAccess) { + throw new InvalidArgumentException('$object', 'Must be of type: SiteAccess'); + } + + if (empty($value->limitationValues)) { + return false; + } + + if (empty($object->name)) { + return false; + } + + $currentSiteAccessHash = $this->generateSiteAccessValue($object->name); + + return in_array($currentSiteAccessHash, $value->limitationValues); + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + throw new NotImplementedException(__METHOD__); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + throw new NotImplementedException(__METHOD__); + } + + /** + * @return string[] + */ + private function getSiteAccessList(): array + { + $siteAccessList = []; + foreach ($this->siteAccessService->getAll() as $sa) { + $siteAccessList[$this->generateSiteAccessValue($sa->name)] = $sa->name; + } + + return $siteAccessList; + } +} + +class_alias(SiteAccessLimitationType::class, 'eZ\Publish\Core\Limitation\SiteAccessLimitationType'); diff --git a/src/lib/Limitation/StatusLimitationType.php b/src/lib/Limitation/StatusLimitationType.php new file mode 100644 index 0000000000..93648c5340 --- /dev/null +++ b/src/lib/Limitation/StatusLimitationType.php @@ -0,0 +1,165 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APIStatusLimitation) { + throw new InvalidArgumentType('$limitationValue', 'APIStatusLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $id) { + if (!is_string($id) && !is_int($id)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int|string', $id); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + // For limitation values used here see \Ibexa\Contracts\Core\Persistence\Content\VersionInfo::STATUS_* constants. + $availableStatusSet = [ + VersionInfo::STATUS_DRAFT => true, + VersionInfo::STATUS_PUBLISHED => true, + VersionInfo::STATUS_ARCHIVED => true, + ]; + + $validationErrors = []; + foreach ($limitationValue->limitationValues as $key => $status) { + if (!isset($availableStatusSet[$status])) { + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' does not exist in the backend", + null, + [ + 'value' => $status, + 'key' => $key, + ] + ); + } + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APIStatusLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + if (!$value instanceof APIStatusLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: APIStatusLimitation'); + } + + if ($object instanceof Content) { + $object = $object->getVersionInfo(); + } elseif (!$object instanceof VersionInfo) { + throw new InvalidArgumentException( + '$object', + 'Must be of type: Content or VersionInfo' + ); + } + + if (empty($value->limitationValues)) { + return false; + } + + /* + * @var $object \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo + */ + return in_array($object->status, $value->limitationValues); + } + + /** + * Returns Criterion for use in find() query. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException If the limitation does not support + * being used as a Criterion. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + throw new NotImplementedException('Status Limitation Criterion'); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + throw new NotImplementedException(__METHOD__); + } +} + +class_alias(StatusLimitationType::class, 'eZ\Publish\Core\Limitation\StatusLimitationType'); diff --git a/src/lib/Limitation/SubtreeLimitationType.php b/src/lib/Limitation/SubtreeLimitationType.php new file mode 100644 index 0000000000..c642ec64e8 --- /dev/null +++ b/src/lib/Limitation/SubtreeLimitationType.php @@ -0,0 +1,272 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APISubtreeLimitation) { + throw new InvalidArgumentType('$limitationValue', 'APISubtreeLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $path) { + if (!is_string($path)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'string', $path); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + foreach ($limitationValue->limitationValues as $key => $path) { + try { + $pathArray = explode('/', trim($path, '/')); + $subtreeRootLocationId = end($pathArray); + $spiLocation = $this->persistence->locationHandler()->load($subtreeRootLocationId); + } catch (APINotFoundException $e) { + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' does not exist in the backend", + null, + [ + 'value' => $path, + 'key' => $key, + ] + ); + + continue; + } + + if (strpos($spiLocation->pathString, $path) !== 0) { + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' does not equal Location's path string: '%path_string%'", + null, + [ + 'value' => $path, + 'key' => $key, + 'path_string' => $spiLocation->pathString, + ] + ); + } + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APISubtreeLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1, 2 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + $targets = $targets ?? []; + + if (!$value instanceof APISubtreeLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: APISubtreeLimitation'); + } + + if ($object instanceof ContentCreateStruct) { + return $this->evaluateForContentCreateStruct($value, $targets); + } elseif ($object instanceof Content) { + $object = $object->getVersionInfo()->getContentInfo(); + } elseif ($object instanceof VersionInfo) { + $object = $object->getContentInfo(); + } elseif (!$object instanceof ContentInfo) { + // As this is Role limitation we need to signal abstain on unsupported $object + return self::ACCESS_ABSTAIN; + } + + $targets = array_filter($targets, static function ($target) { + return !$target instanceof Version; + }); + + // Load locations if no specific placement was provided + if (empty($targets)) { + if ($object->isTrashed()) { + $targets = $this->persistence->locationHandler()->loadLocationsByTrashContent($object->id); + } elseif ($object->isPublished()) { + $targets = $this->persistence->locationHandler()->loadLocationsByContent($object->id); + } else { + // @todo Need support for draft locations to work correctly + $targets = $this->persistence->locationHandler()->loadParentLocationsForDraftContent($object->id); + } + } + + foreach ($targets as $target) { + if (!$target instanceof Location && !$target instanceof SPILocation) { + // As this is Role limitation we need to signal abstain on unsupported $targets + return self::ACCESS_ABSTAIN; + } + + foreach ($value->limitationValues as $limitationPathString) { + if ($target->pathString === $limitationPathString) { + return true; + } + if (strpos($target->pathString, $limitationPathString) === 0) { + return true; + } + } + } + + return false; + } + + /** + * Evaluate permissions for ContentCreateStruct against LocationCreateStruct placements. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If $targets does not contain + * objects of type LocationCreateStruct + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param array $targets + * + * @return bool + */ + protected function evaluateForContentCreateStruct(APILimitationValue $value, array $targets) + { + // If targets is empty/null return false as user does not have access + // to content w/o location with this limitation + if (empty($targets)) { + return false; + } + + $hasLocationCreateStruct = false; + foreach ($targets as $target) { + if (!$target instanceof LocationCreateStruct) { + continue; + } + + $hasLocationCreateStruct = true; + $target = $this->persistence->locationHandler()->load($target->parentLocationId); + + // For ContentCreateStruct all placements must match + foreach ($value->limitationValues as $limitationPathString) { + if ($target->pathString === $limitationPathString) { + continue 2; + } + if (strpos($target->pathString, $limitationPathString) === 0) { + continue 2; + } + } + + return false; + } + + if (false === $hasLocationCreateStruct) { + throw new InvalidArgumentException( + '$targets', + 'If $object is ContentCreateStruct, it must contain LocationCreateStruct objects' + ); + } + + return true; + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + if (empty($value->limitationValues)) { + // A Policy should not have empty limitationValues store + throw new \RuntimeException('$value->limitationValues is empty'); + } + + if (!isset($value->limitationValues[1])) { + // 1 limitation value: EQ operation + return new PermissionSubtree($value->limitationValues[0]); + } + + // several limitation values: IN operation + return new PermissionSubtree($value->limitationValues); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + return self::VALUE_SCHEMA_LOCATION_PATH; + } +} + +class_alias(SubtreeLimitationType::class, 'eZ\Publish\Core\Limitation\SubtreeLimitationType'); diff --git a/src/lib/Limitation/TargetOnlyLimitationType.php b/src/lib/Limitation/TargetOnlyLimitationType.php new file mode 100644 index 0000000000..b2f84b2768 --- /dev/null +++ b/src/lib/Limitation/TargetOnlyLimitationType.php @@ -0,0 +1,37 @@ +limitationValues is of correct type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the value does not match the expected type/structure + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + */ + public function acceptValue(APILimitationValue $limitationValue) + { + if (!$limitationValue instanceof APIUserGroupLimitation) { + throw new InvalidArgumentType('$limitationValue', 'APIUserGroupLimitation', $limitationValue); + } elseif (!is_array($limitationValue->limitationValues)) { + throw new InvalidArgumentType('$limitationValue->limitationValues', 'array', $limitationValue->limitationValues); + } + + foreach ($limitationValue->limitationValues as $key => $value) { + // Accept a true value for b/c with 5.0 + if ($value === true) { + $limitationValue->limitationValues[$key] = 1; + } elseif (is_string($value) && ctype_digit($value)) { + // Cast integers passed as string to int + $limitationValue->limitationValues[$key] = (int)$value; + } elseif (!is_int($value)) { + throw new InvalidArgumentType("\$limitationValue->limitationValues[{$key}]", 'int', $value); + } + } + } + + /** + * Makes sure LimitationValue->limitationValues is valid according to valueSchema(). + * + * Make sure {@link acceptValue()} is checked first! + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitationValue + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate(APILimitationValue $limitationValue) + { + $validationErrors = []; + foreach ($limitationValue->limitationValues as $key => $value) { + if ($value !== 1) { + $validationErrors[] = new ValidationError( + "limitationValues[%key%] => '%value%' must be 1 (owner)", + null, + [ + 'value' => $value, + 'key' => $key, + ] + ); + } + } + + return $validationErrors; + } + + /** + * Create the Limitation Value. + * + * @param mixed[] $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation + */ + public function buildValue(array $limitationValues) + { + return new APIUserGroupLimitation(['limitationValues' => $limitationValues]); + } + + /** + * Evaluate permission against content & target(placement/parent/assignment). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If any of the arguments are invalid + * Example: If LimitationValue is instance of ContentTypeLimitationValue, and Type is SectionLimitationType. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If value of the LimitationValue is unsupported + * Example if OwnerLimitationValue->limitationValues[0] is not one of: [ 1 ] + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject[]|null $targets The context of the $object, like Location of Content, if null none where provided by caller + * + * @return bool + */ + public function evaluate(APILimitationValue $value, APIUserReference $currentUser, ValueObject $object, array $targets = null) + { + if (!$value instanceof APIUserGroupLimitation) { + throw new InvalidArgumentException('$value', 'Must be of type: APIUserGroupLimitation'); + } + + if ($value->limitationValues[0] != 1) { + throw new BadStateException( + 'Parent User Group limitation', + 'Expected Limitation value to be 1 instead of' . $value->limitationValues[0] + ); + } + + if ($object instanceof Content) { + $object = $object->getVersionInfo()->getContentInfo(); + } elseif ($object instanceof VersionInfo) { + $object = $object->getContentInfo(); + } elseif (!$object instanceof ContentInfo && !$object instanceof ContentCreateStruct) { + throw new InvalidArgumentException( + '$object', + 'Must be of type: ContentCreateStruct, Content, VersionInfo or ContentInfo' + ); + } + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo|\Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct */ + if ($object->ownerId === $currentUser->getUserId()) { + return true; + } + + /* + * As long as SPI userHandler and API UserService does not speak the same language, this is the ugly truth; + */ + $locationHandler = $this->persistence->locationHandler(); + $ownerLocations = $locationHandler->loadLocationsByContent($object->ownerId); + if (empty($ownerLocations)) { + return false; + } + + $currentUserLocations = $locationHandler->loadLocationsByContent($currentUser->getUserId()); + if (empty($currentUserLocations)) { + return false; + } + + // @todo Needs to take care of inherited groups as well when UserHandler gets knowledge about user groups + foreach ($ownerLocations as $ownerLocation) { + foreach ($currentUserLocations as $currentUserLocation) { + if ($ownerLocation->parentId === $currentUserLocation->parentId) { + return true; + } + } + } + + return false; + } + + /** + * Returns Criterion for use in find() query. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $value + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUser + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface + */ + public function getCriterion(APILimitationValue $value, APIUserReference $currentUser) + { + if (empty($value->limitationValues)) { + // A Policy should not have empty limitationValues stored + throw new \RuntimeException('$value->limitationValues is empty'); + } + + if ($value->limitationValues[0] != 1) { + throw new BadStateException( + 'Parent User Group limitation', + 'Expected Limitation value to be 1 instead of' . $value->limitationValues[0] + ); + } + + $groupIds = []; + $locationHandler = $this->persistence->locationHandler(); + $currentUserLocations = $locationHandler->loadLocationsByContent($currentUser->getUserId()); + foreach ($currentUserLocations as $currentUserLocation) { + try { + $parentLocation = $locationHandler->load($currentUserLocation->parentId); + $groupIds[] = $parentLocation->contentId; + } catch (NotFoundException $e) { + // there is no need for any action - carrying on with checking other user locations + continue; + } + } + + return new Criterion\UserMetadata( + Criterion\UserMetadata::GROUP, + Criterion\Operator::IN, + $groupIds + ); + } + + /** + * Returns info on valid $limitationValues. + * + * @return mixed[]|int In case of array, a hash with key as valid limitations value and value as human readable name + * of that option, in case of int on of VALUE_SCHEMA_ constants. + */ + public function valueSchema() + { + throw new NotImplementedException(__METHOD__); + } +} + +class_alias(UserGroupLimitationType::class, 'eZ\Publish\Core\Limitation\UserGroupLimitationType'); diff --git a/src/lib/MVC/Exception/HiddenLocationException.php b/src/lib/MVC/Exception/HiddenLocationException.php new file mode 100644 index 0000000000..b8785a4704 --- /dev/null +++ b/src/lib/MVC/Exception/HiddenLocationException.php @@ -0,0 +1,32 @@ +location = $location; + parent::__construct($message, $previous, $code); + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location + */ + public function getLocation() + { + return $this->location; + } +} + +class_alias(HiddenLocationException::class, 'eZ\Publish\Core\MVC\Exception\HiddenLocationException'); diff --git a/eZ/Publish/Core/MVC/Exception/InvalidSiteAccessException.php b/src/lib/MVC/Exception/InvalidSiteAccessException.php similarity index 77% rename from eZ/Publish/Core/MVC/Exception/InvalidSiteAccessException.php rename to src/lib/MVC/Exception/InvalidSiteAccessException.php index 7a9f318f70..e16fcbd159 100644 --- a/eZ/Publish/Core/MVC/Exception/InvalidSiteAccessException.php +++ b/src/lib/MVC/Exception/InvalidSiteAccessException.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Exception; +namespace Ibexa\Core\MVC\Exception; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface; use RuntimeException; /** @@ -16,7 +16,7 @@ class InvalidSiteAccessException extends RuntimeException { /** * @param string $siteAccess The invalid SiteAccess - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface $siteAccessProvider + * @param \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface $siteAccessProvider * @param string $matchType How $siteAccess was matched * @param bool $debug If true, Symfony environment is a debug one (like 'dev') */ @@ -35,3 +35,5 @@ public function __construct( parent::__construct($message); } } + +class_alias(InvalidSiteAccessException::class, 'eZ\Publish\Core\MVC\Exception\InvalidSiteAccessException'); diff --git a/eZ/Publish/Core/MVC/Exception/NoViewTemplateException.php b/src/lib/MVC/Exception/NoViewTemplateException.php similarity index 76% rename from eZ/Publish/Core/MVC/Exception/NoViewTemplateException.php rename to src/lib/MVC/Exception/NoViewTemplateException.php index aeb8cfdfe4..bd6af39de7 100644 --- a/eZ/Publish/Core/MVC/Exception/NoViewTemplateException.php +++ b/src/lib/MVC/Exception/NoViewTemplateException.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Exception; +namespace Ibexa\Core\MVC\Exception; use Exception; -use eZ\Publish\Core\MVC\Symfony\View\View; +use Ibexa\Core\MVC\Symfony\View\View; /** * Thrown when a view is attempted to be rendered without a template set. */ class NoViewTemplateException extends Exception { - /** @var \eZ\Publish\Core\MVC\Symfony\View\View */ + /** @var \Ibexa\Core\MVC\Symfony\View\View */ private $view; public function __construct(View $view) @@ -33,3 +33,5 @@ public function getView() return $this->view; } } + +class_alias(NoViewTemplateException::class, 'eZ\Publish\Core\MVC\Exception\NoViewTemplateException'); diff --git a/eZ/Publish/Core/MVC/Exception/ParameterNotFoundException.php b/src/lib/MVC/Exception/ParameterNotFoundException.php similarity index 82% rename from eZ/Publish/Core/MVC/Exception/ParameterNotFoundException.php rename to src/lib/MVC/Exception/ParameterNotFoundException.php index a65b3949e4..f7ede1eb92 100644 --- a/eZ/Publish/Core/MVC/Exception/ParameterNotFoundException.php +++ b/src/lib/MVC/Exception/ParameterNotFoundException.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Exception; +namespace Ibexa\Core\MVC\Exception; use InvalidArgumentException; @@ -21,3 +21,5 @@ public function __construct($paramName, $namespace, array $triedScopes = []) } } } + +class_alias(ParameterNotFoundException::class, 'eZ\Publish\Core\MVC\Exception\ParameterNotFoundException'); diff --git a/src/lib/MVC/Exception/SourceImageNotFoundException.php b/src/lib/MVC/Exception/SourceImageNotFoundException.php new file mode 100644 index 0000000000..a277a9a549 --- /dev/null +++ b/src/lib/MVC/Exception/SourceImageNotFoundException.php @@ -0,0 +1,15 @@ +repository = $repository; + } +} + +class_alias(RepositoryAware::class, 'eZ\Publish\Core\MVC\RepositoryAware'); diff --git a/src/lib/MVC/RepositoryAwareInterface.php b/src/lib/MVC/RepositoryAwareInterface.php new file mode 100644 index 0000000000..f1484d5eff --- /dev/null +++ b/src/lib/MVC/RepositoryAwareInterface.php @@ -0,0 +1,19 @@ + $object->getMapKey(), + 'map' => [], + 'reverseMap' => [], + ]; + } + + public function supportsNormalization($data, string $format = null) + { + return $data instanceof Map; + } +} + +class_alias(MapNormalizer::class, 'eZ\Publish\Core\MVC\Symfony\Component\Serializer\MapNormalizer'); diff --git a/src/lib/MVC/Symfony/Component/Serializer/RegexHostNormalizer.php b/src/lib/MVC/Symfony/Component/Serializer/RegexHostNormalizer.php new file mode 100644 index 0000000000..2430cd45cd --- /dev/null +++ b/src/lib/MVC/Symfony/Component/Serializer/RegexHostNormalizer.php @@ -0,0 +1,26 @@ +contentService = $contentService; + $this->ioService = $ioService; + $this->translationHelper = $translationHelper; + } + + /** + * Download binary file identified by field ID. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the field $fieldId can't be found, or the translation can't be found. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If the content is trashed, or can't be found. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions. + */ + public function downloadBinaryFileByIdAction(Request $request, int $contentId, int $fieldId): BinaryStreamResponse + { + $content = $this->contentService->loadContent($contentId); + try { + $field = $this->findFieldInContent($fieldId, $content); + } catch (InvalidArgumentException $e) { + throw new NotFoundException('File', $fieldId); + } + + return $this->downloadBinaryFileAction($contentId, $field->fieldDefIdentifier, $field->value->fileName, $request); + } + + /** + * Finds the field with id $fieldId in $content. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the field $fieldId can't be found, or the translation can't be found. + */ + protected function findFieldInContent(int $fieldId, Content $content): Field + { + foreach ($content->getFields() as $field) { + if ($field->getId() === $fieldId) { + return $field; + } + } + + throw new InvalidArgumentException( + '$fieldId', + "Field with id $fieldId not found in Content with id {$content->id}" + ); + } + + /** + * Download binary file identified by field identifier. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the field can't be found, or the translation can't be found. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If the content is trashed, or can't be found. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions. + */ + public function downloadBinaryFileAction(int $contentId, string $fieldIdentifier, string $filename, Request $request): BinaryStreamResponse + { + if ($request->query->has('version')) { + $version = (int) $request->query->get('version'); + if ($version <= 0) { + throw new NotFoundException('File', $filename); + } + $content = $this->contentService->loadContent($contentId, null, $version); + } else { + $content = $this->contentService->loadContent($contentId); + } + + if ($content->contentInfo->isTrashed()) { + throw new NotFoundException('File', $filename); + } + + $field = $this->translationHelper->getTranslatedField( + $content, + $fieldIdentifier, + $request->query->has('inLanguage') ? $request->query->get('inLanguage') : null + ); + if (!$field instanceof Field) { + throw new InvalidArgumentException( + '$fieldIdentifier', + "'{$fieldIdentifier}' field not present on content #{$content->contentInfo->id} '{$content->contentInfo->name}'" + ); + } + + $response = new BinaryStreamResponse($this->ioService->loadBinaryFile($field->value->id), $this->ioService); + $response->setContentDisposition( + ResponseHeaderBag::DISPOSITION_ATTACHMENT, + $field->value->fileName, + bin2hex(random_bytes(8)) + ); + + return $response; + } +} + +class_alias(DownloadController::class, 'eZ\Publish\Core\MVC\Symfony\Controller\Content\DownloadController'); diff --git a/eZ/Publish/Core/MVC/Symfony/Controller/Content/DownloadRedirectionController.php b/src/lib/MVC/Symfony/Controller/Content/DownloadRedirectionController.php similarity index 76% rename from eZ/Publish/Core/MVC/Symfony/Controller/Content/DownloadRedirectionController.php rename to src/lib/MVC/Symfony/Controller/Content/DownloadRedirectionController.php index 7235812b59..e40ceba30c 100644 --- a/eZ/Publish/Core/MVC/Symfony/Controller/Content/DownloadRedirectionController.php +++ b/src/lib/MVC/Symfony/Controller/Content/DownloadRedirectionController.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Controller\Content; +namespace Ibexa\Core\MVC\Symfony\Controller\Content; -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\MVC\Symfony\Controller\Controller; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\MVC\Symfony\Controller\Controller; +use Ibexa\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator; use InvalidArgumentException; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -18,13 +18,13 @@ class DownloadRedirectionController extends Controller { - /** @var \eZ\Publish\API\Repository\ContentService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentService */ private $contentService; /** @var \Symfony\Component\Routing\RouterInterface */ private $router; - /** @var \eZ\Publish\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator */ + /** @var \Ibexa\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator */ private $routeReferenceGenerator; public function __construct(ContentService $contentService, RouterInterface $router, RouteReferenceGenerator $routeReferenceGenerator) @@ -61,7 +61,7 @@ public function redirectToContentDownloadAction($contentId, $fieldId, Request $r } $downloadRouteRef = $this->routeReferenceGenerator->generate( - 'ez_content_download', + 'ibexa.content.download', $params ); @@ -77,9 +77,9 @@ public function redirectToContentDownloadAction($contentId, $fieldId, Request $r * Finds the field with id $fieldId in $content. * * @param int $fieldId - * @param \eZ\Publish\API\Repository\Values\Content\Content $content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content * - * @return \eZ\Publish\API\Repository\Values\Content\Field + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field */ protected function findFieldInContent($fieldId, Content $content) { @@ -91,3 +91,5 @@ protected function findFieldInContent($fieldId, Content $content) throw new InvalidArgumentException("Could not find any Field with ID $fieldId in Content item with ID {$content->id}"); } } + +class_alias(DownloadRedirectionController::class, 'eZ\Publish\Core\MVC\Symfony\Controller\Content\DownloadRedirectionController'); diff --git a/src/lib/MVC/Symfony/Controller/Content/PreviewController.php b/src/lib/MVC/Symfony/Controller/Content/PreviewController.php new file mode 100644 index 0000000000..2bfec8d11f --- /dev/null +++ b/src/lib/MVC/Symfony/Controller/Content/PreviewController.php @@ -0,0 +1,192 @@ +contentService = $contentService; + $this->locationService = $locationService; + $this->kernel = $kernel; + $this->previewHelper = $previewHelper; + $this->authorizationChecker = $authorizationChecker; + $this->locationProvider = $locationProvider; + $this->controllerChecker = $controllerChecker; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException If Content is missing location as this is not supported in current version + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function previewContentAction( + Request $request, + int $contentId, + int $versionNo, + string $language, + ?string $siteAccessName = null, + ?int $locationId = null + ): Response { + $this->previewHelper->setPreviewActive(true); + + try { + $content = $this->contentService->loadContent($contentId, [$language], $versionNo); + $location = $locationId !== null + ? $this->locationService->loadLocation($locationId) + : $this->locationProvider->loadMainLocationByContent($content); + + if (!$location instanceof Location) { + throw new NotImplementedException('Preview for content without Locations'); + } + + $this->previewHelper->setPreviewedContent($content); + $this->previewHelper->setPreviewedLocation($location); + } catch (UnauthorizedException $e) { + throw new AccessDeniedException(); + } + + if (!$this->authorizationChecker->isGranted(new AuthorizationAttribute('content', 'versionread', ['valueObject' => $content]))) { + throw new AccessDeniedException(); + } + + $siteAccess = $this->previewHelper->getOriginalSiteAccess(); + // Only switch if $siteAccessName is set and different from original + if ($siteAccessName !== null && $siteAccessName !== $siteAccess->name) { + $siteAccess = $this->previewHelper->changeConfigScope($siteAccessName); + } + + try { + $viewType = $request->query->get('viewType', ViewManagerInterface::VIEW_TYPE_FULL); + $response = $this->kernel->handle( + $this->getForwardRequest($location, $content, $siteAccess, $request, $language, $viewType), + HttpKernelInterface::SUB_REQUEST, + false + ); + } catch (\Exception $e) { + if ($location->isDraft() && $this->controllerChecker->usesCustomController($content, $location)) { + // @todo This should probably be an exception that embeds the original one + $message = <<The view that rendered this location draft uses a custom controller, and resulted in a fatal error.

    +

    Location View is deprecated, as it causes issues with preview, such as an empty location id when previewing the first version of a content.

    +EOF; + + throw new Exception($message, 0, $e); + } else { + throw $e; + } + } + $response->headers->addCacheControlDirective('no-cache', true); + $response->setPrivate(); + + $this->previewHelper->restoreConfigScope(); + $this->previewHelper->setPreviewActive(false); + + return $response; + } + + /** + * Returns the Request object that will be forwarded to the kernel for previewing the content. + */ + protected function getForwardRequest( + Location $location, + Content $content, + SiteAccess $previewSiteAccess, + Request $request, + string $language, + string $viewType = ViewManagerInterface::VIEW_TYPE_FULL + ): Request { + $forwardRequestParameters = [ + '_controller' => UrlAliasRouter::VIEW_ACTION, + // specify a route for RouteReference generator + '_route' => UrlAliasGenerator::INTERNAL_CONTENT_VIEW_ROUTE, + '_route_params' => [ + 'contentId' => $content->id, + 'locationId' => $location->id, + ], + 'location' => $location, + 'content' => $content, + 'viewType' => $viewType, + 'layout' => true, + 'params' => [ + 'content' => $content, + 'location' => $location, + self::PREVIEW_PARAMETER_NAME => true, + 'language' => $language, + ], + 'siteaccess' => $previewSiteAccess, + 'semanticPathinfo' => $request->attributes->get('semanticPathinfo'), + ]; + + if ($this->controllerChecker->usesCustomController($content, $location)) { + $forwardRequestParameters = [ + '_controller' => 'ibexa_content::viewAction', + '_route' => self::CONTENT_VIEW_ROUTE, + ] + $forwardRequestParameters; + } + + return $request->duplicate( + null, + null, + $forwardRequestParameters + ); + } +} + +class_alias(PreviewController::class, 'eZ\Publish\Core\MVC\Symfony\Controller\Content\PreviewController'); diff --git a/src/lib/MVC/Symfony/Controller/Content/QueryController.php b/src/lib/MVC/Symfony/Controller/Content/QueryController.php new file mode 100644 index 0000000000..17ba947b93 --- /dev/null +++ b/src/lib/MVC/Symfony/Controller/Content/QueryController.php @@ -0,0 +1,148 @@ +contentViewQueryTypeMapper = $contentViewQueryTypeMapper; + $this->searchService = $searchService; + } + + /** + * Runs a content search. + * + * @param \Ibexa\Core\MVC\Symfony\View\ContentView $view + * + * @return \Ibexa\Core\MVC\Symfony\View\ContentView + */ + public function contentQueryAction(ContentView $view) + { + $this->runQuery($view, 'findContent'); + + return $view; + } + + /** + * Runs a location search. + * + * @param \Ibexa\Core\MVC\Symfony\View\ContentView $view + * + * @return \Ibexa\Core\MVC\Symfony\View\ContentView + */ + public function locationQueryAction(ContentView $view) + { + $this->runQuery($view, 'findLocations'); + + return $view; + } + + /** + * Runs a contentInfo search. + * + * @param \Ibexa\Core\MVC\Symfony\View\ContentView $view + * + * @return \Ibexa\Core\MVC\Symfony\View\ContentView + */ + public function contentInfoQueryAction(ContentView $view) + { + $this->runQuery($view, 'findContentInfo'); + + return $view; + } + + /** + * Runs the Query defined in $view using $method on SearchService. + * + * @param \Ibexa\Core\MVC\Symfony\View\ContentView $view + * @param string $method Name of the SearchService method to run. + */ + private function runQuery(ContentView $view, $method) + { + $searchResults = $this->searchService->$method( + $this->contentViewQueryTypeMapper->map($view) + ); + $view->addParameters([$view->getParameter('query')['assign_results_to'] => $searchResults]); + } + + /** + * @param \Ibexa\Core\MVC\Symfony\View\ContentView $view + * @param \Symfony\Component\HttpFoundation\Request $request + * + * @return \Ibexa\Core\MVC\Symfony\View\ContentView + */ + public function pagingQueryAction(ContentView $view, Request $request) + { + $this->runPagingQuery($view, $request); + + return $view; + } + + /** + * @param \Ibexa\Core\MVC\Symfony\View\ContentView $view + * @param \Symfony\Component\HttpFoundation\Request $request + */ + private function runPagingQuery(ContentView $view, Request $request) + { + $queryParameters = $view->getParameter('query'); + + $limit = $queryParameters['limit'] ?? 10; + $pageParam = $queryParameters['page_param'] ?? 'page'; + + $page = $request->get($pageParam, 1); + + $pager = new Pagerfanta( + $this->getAdapter($this->contentViewQueryTypeMapper->map($view)) + ); + + $pager->setMaxPerPage($limit); + $pager->setCurrentPage($page); + + $view->addParameters([$queryParameters['assign_results_to'] => $pager]); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query + * + * @return \Pagerfanta\Adapter\AdapterInterface + */ + private function getAdapter(Query $query): AdapterInterface + { + if ($query instanceof LocationQuery) { + return new LocationSearchHitAdapter($query, $this->searchService); + } + + return new ContentSearchHitAdapter($query, $this->searchService); + } +} + +class_alias(QueryController::class, 'eZ\Publish\Core\MVC\Symfony\Controller\Content\QueryController'); diff --git a/eZ/Publish/Core/MVC/Symfony/Controller/Content/ViewController.php b/src/lib/MVC/Symfony/Controller/Content/ViewController.php similarity index 82% rename from eZ/Publish/Core/MVC/Symfony/Controller/Content/ViewController.php rename to src/lib/MVC/Symfony/Controller/Content/ViewController.php index 87ca8030f4..9173379433 100644 --- a/eZ/Publish/Core/MVC/Symfony/Controller/Content/ViewController.php +++ b/src/lib/MVC/Symfony/Controller/Content/ViewController.php @@ -4,18 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Controller\Content; +namespace Ibexa\Core\MVC\Symfony\Controller\Content; use DateTime; use Exception; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Controller\Controller; -use eZ\Publish\Core\MVC\Symfony\Event\APIContentExceptionEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute as AuthorizationAttribute; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; -use eZ\Publish\Core\MVC\Symfony\View\ViewManagerInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Core\MVC\Symfony\Controller\Controller; +use Ibexa\Core\MVC\Symfony\Event\APIContentExceptionEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute as AuthorizationAttribute; +use Ibexa\Core\MVC\Symfony\View\ContentView; +use Ibexa\Core\MVC\Symfony\View\ViewManagerInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; use Symfony\Component\Security\Core\Exception\AccessDeniedException; @@ -27,7 +27,7 @@ */ class ViewController extends Controller { - /** @var \eZ\Publish\Core\MVC\Symfony\View\ViewManagerInterface */ + /** @var \Ibexa\Core\MVC\Symfony\View\ViewManagerInterface */ protected $viewManager; /** @var \Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface */ @@ -51,9 +51,9 @@ public function __construct(ViewManagerInterface $viewManager, AuthorizationChec * * Cache is in both cases handled by the CacheViewResponseListener. * - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentView $view + * @param \Ibexa\Core\MVC\Symfony\View\ContentView $view * - * @return \eZ\Publish\Core\MVC\Symfony\View\ContentView + * @return \Ibexa\Core\MVC\Symfony\View\ContentView */ public function viewAction(ContentView $view) { @@ -64,9 +64,9 @@ public function viewAction(ContentView $view) * Embed a content. * Behaves mostly like viewAction(), but with specific content load permission handling. * - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentView $view + * @param \Ibexa\Core\MVC\Symfony\View\ContentView $view * - * @return \eZ\Publish\Core\MVC\Symfony\View\ContentView + * @return \Ibexa\Core\MVC\Symfony\View\ContentView */ public function embedAction(ContentView $view) { @@ -140,7 +140,7 @@ protected function handleViewException(Response $response, $params, Exception $e /** * Creates the content to be returned when viewing a Location. * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location * @param string $viewType * @param bool $layout * @param array $params @@ -155,7 +155,7 @@ protected function renderLocation(Location $location, $viewType, $layout = false /** * Creates the content to be returned when viewing a Content. * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content * @param string $viewType * @param bool $layout * @param array $params @@ -177,3 +177,5 @@ protected function performAccessChecks() } } } + +class_alias(ViewController::class, 'eZ\Publish\Core\MVC\Symfony\Controller\Content\ViewController'); diff --git a/src/lib/MVC/Symfony/Controller/Controller.php b/src/lib/MVC/Symfony/Controller/Controller.php new file mode 100644 index 0000000000..a931508152 --- /dev/null +++ b/src/lib/MVC/Symfony/Controller/Controller.php @@ -0,0 +1,129 @@ +getConfigResolver()->hasParameter($parameterName)) { + return $this->getConfigResolver()->getParameter($parameterName); + } + + return $defaultValue; + } + + /** + * Checks if $parameterName is defined. + * + * @param string $parameterName + * + * @return bool + */ + public function hasParameter($parameterName) + { + return $this->getConfigResolver()->hasParameter($parameterName); + } + + /** + * @return \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface + */ + public function getConfigResolver() + { + return $this->container->get('ibexa.config.resolver'); + } + + /** + * Renders a view. + * + * @param string $view The view name + * @param array $parameters An array of parameters to pass to the view + * @param \Symfony\Component\HttpFoundation\Response $response + * + * @return \Symfony\Component\HttpFoundation\Response + */ + public function render($view, array $parameters = [], Response $response = null) + { + if (!isset($response)) { + $response = new Response(); + } + + $response->setContent($this->getTemplateEngine()->render($view, $parameters)); + + return $response; + } + + /** + * @return \Symfony\Component\Templating\EngineInterface + */ + public function getTemplateEngine() + { + return $this->container->get('templating'); + } + + /** + * @return \Psr\Log\LoggerInterface|null + */ + public function getLogger() + { + return $this->container->get('logger', ContainerInterface::NULL_ON_INVALID_REFERENCE); + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Repository + */ + public function getRepository() + { + return $this->container->get('ibexa.api.repository'); + } + + /** + * @return \Symfony\Component\HttpFoundation\Request + */ + public function getRequest() + { + return $this->container->get('request_stack')->getCurrentRequest(); + } + + /** + * @return \Symfony\Component\EventDispatcher\EventDispatcherInterface + */ + public function getEventDispatcher() + { + return $this->container->get('event_dispatcher'); + } + + /** + * Checks if current user has granted access to provided attribute. + * + * @param \Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute $attribute + * + * @return bool + */ + public function isGranted(AuthorizationAttribute $attribute) + { + return $this->container->get('security.authorization_checker')->isGranted($attribute); + } +} + +class_alias(Controller::class, 'eZ\Publish\Core\MVC\Symfony\Controller\Controller'); diff --git a/eZ/Publish/Core/MVC/Symfony/Controller/QueryRenderController.php b/src/lib/MVC/Symfony/Controller/QueryRenderController.php similarity index 85% rename from eZ/Publish/Core/MVC/Symfony/Controller/QueryRenderController.php rename to src/lib/MVC/Symfony/Controller/QueryRenderController.php index a54170433d..cca6fd0bd9 100644 --- a/eZ/Publish/Core/MVC/Symfony/Controller/QueryRenderController.php +++ b/src/lib/MVC/Symfony/Controller/QueryRenderController.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Controller; +namespace Ibexa\Core\MVC\Symfony\Controller; -use eZ\Publish\Core\MVC\Symfony\View\QueryView; -use eZ\Publish\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface; -use eZ\Publish\Core\Pagination\Pagerfanta\Pagerfanta; -use eZ\Publish\Core\Pagination\Pagerfanta\SearchResultAdapter; -use eZ\Publish\Core\Query\QueryFactoryInterface; +use Ibexa\Core\MVC\Symfony\View\QueryView; +use Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface; +use Ibexa\Core\Pagination\Pagerfanta\Pagerfanta; +use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; +use Ibexa\Core\Query\QueryFactoryInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -23,10 +23,10 @@ */ final class QueryRenderController { - /** @var \eZ\Publish\Core\Query\QueryFactoryInterface */ + /** @var \Ibexa\Core\Query\QueryFactoryInterface */ private $queryFactory; - /** @var \eZ\Publish\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface */ + /** @var \Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface */ private $searchHitAdapterFactory; public function __construct( @@ -117,3 +117,5 @@ private function createQueryView(string $template, string $assignResultsTo, iter return $view; } } + +class_alias(QueryRenderController::class, 'eZ\Publish\Core\MVC\Symfony\Controller\QueryRenderController'); diff --git a/eZ/Publish/Core/MVC/Symfony/Controller/SecurityController.php b/src/lib/MVC/Symfony/Controller/SecurityController.php similarity index 80% rename from eZ/Publish/Core/MVC/Symfony/Controller/SecurityController.php rename to src/lib/MVC/Symfony/Controller/SecurityController.php index c4da371e6b..134f09acad 100644 --- a/eZ/Publish/Core/MVC/Symfony/Controller/SecurityController.php +++ b/src/lib/MVC/Symfony/Controller/SecurityController.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Controller; +namespace Ibexa\Core\MVC\Symfony\Controller; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\View\LoginFormView; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\View\LoginFormView; use Symfony\Component\Security\Http\Authentication\AuthenticationUtils; use Twig\Environment; @@ -16,7 +16,7 @@ class SecurityController /** @var \Twig\Environment */ protected $templateEngine; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ protected $configResolver; /** @var \Symfony\Component\Security\Http\Authentication\AuthenticationUtils */ @@ -41,3 +41,5 @@ public function loginAction() return $view; } } + +class_alias(SecurityController::class, 'eZ\Publish\Core\MVC\Symfony\Controller\SecurityController'); diff --git a/eZ/Publish/Core/MVC/Symfony/Event/APIContentExceptionEvent.php b/src/lib/MVC/Symfony/Event/APIContentExceptionEvent.php similarity index 80% rename from eZ/Publish/Core/MVC/Symfony/Event/APIContentExceptionEvent.php rename to src/lib/MVC/Symfony/Event/APIContentExceptionEvent.php index 943ad4cea6..04f8d8926a 100644 --- a/eZ/Publish/Core/MVC/Symfony/Event/APIContentExceptionEvent.php +++ b/src/lib/MVC/Symfony/Event/APIContentExceptionEvent.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Event; +namespace Ibexa\Core\MVC\Symfony\Event; use Exception; -use eZ\Publish\Core\MVC\Symfony\View\View; +use Ibexa\Core\MVC\Symfony\View\View; use Symfony\Contracts\EventDispatcher\Event; /** - * This event is dispatched when an Exception from eZ Publish API is thrown and could not be caught before. + * This event is dispatched when an Exception from Ibexa API is thrown and could not be caught before. * It allows you to handle this exception and affect a specific Response for it. */ class APIContentExceptionEvent extends Event @@ -19,7 +19,7 @@ class APIContentExceptionEvent extends Event /** @var \Exception */ private $apiException; - /** @var \eZ\Publish\Core\MVC\Symfony\View\View */ + /** @var \Ibexa\Core\MVC\Symfony\View\View */ private $contentView; /** @var array */ @@ -43,7 +43,7 @@ public function getApiException() * Injects the ContentView object to display content from. * It is a good idea to call {@link stopPropagation()} after that so that other listeners won't override it. * - * @param \eZ\Publish\Core\MVC\Symfony\View\View $contentView + * @param \Ibexa\Core\MVC\Symfony\View\View $contentView */ public function setContentView(View $contentView) { @@ -51,7 +51,7 @@ public function setContentView(View $contentView) } /** - * @return \eZ\Publish\Core\MVC\Symfony\View\View + * @return \Ibexa\Core\MVC\Symfony\View\View */ public function getContentView() { @@ -80,3 +80,5 @@ public function getContentMeta() return $this->contentMeta; } } + +class_alias(APIContentExceptionEvent::class, 'eZ\Publish\Core\MVC\Symfony\Event\APIContentExceptionEvent'); diff --git a/eZ/Publish/Core/MVC/Symfony/Event/ConsoleInitEvent.php b/src/lib/MVC/Symfony/Event/ConsoleInitEvent.php similarity index 82% rename from eZ/Publish/Core/MVC/Symfony/Event/ConsoleInitEvent.php rename to src/lib/MVC/Symfony/Event/ConsoleInitEvent.php index b907649110..3c1dddfdf8 100644 --- a/eZ/Publish/Core/MVC/Symfony/Event/ConsoleInitEvent.php +++ b/src/lib/MVC/Symfony/Event/ConsoleInitEvent.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Event; +namespace Ibexa\Core\MVC\Symfony\Event; use Symfony\Component\Console\Event\ConsoleEvent; use Symfony\Component\Console\Input\InputInterface; @@ -20,3 +20,5 @@ public function __construct(InputInterface $input, OutputInterface $output) parent::__construct(null, $input, $output); } } + +class_alias(ConsoleInitEvent::class, 'eZ\Publish\Core\MVC\Symfony\Event\ConsoleInitEvent'); diff --git a/src/lib/MVC/Symfony/Event/ContentCacheClearEvent.php b/src/lib/MVC/Symfony/Event/ContentCacheClearEvent.php new file mode 100644 index 0000000000..a692c8723f --- /dev/null +++ b/src/lib/MVC/Symfony/Event/ContentCacheClearEvent.php @@ -0,0 +1,73 @@ +contentInfo = $contentInfo; + } + + /** + * Returns ContentInfo object we're clearing the cache for. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo + */ + public function getContentInfo() + { + return $this->contentInfo; + } + + /** + * Returns all location objects registered to the cache clear process. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location[] + */ + public function getLocationsToClear() + { + return $this->locationsToClear; + } + + /** + * Adds a location that needs to be cleared. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + */ + public function addLocationToClear(Location $location) + { + $this->locationsToClear[] = $location; + } + + /** + * Replaces the list of locations to clear. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location[] $locationsToClear + */ + public function setLocationsToClear(array $locationsToClear) + { + $this->locationsToClear = $locationsToClear; + } +} + +class_alias(ContentCacheClearEvent::class, 'eZ\Publish\Core\MVC\Symfony\Event\ContentCacheClearEvent'); diff --git a/eZ/Publish/Core/MVC/Symfony/Event/InteractiveLoginEvent.php b/src/lib/MVC/Symfony/Event/InteractiveLoginEvent.php similarity index 78% rename from eZ/Publish/Core/MVC/Symfony/Event/InteractiveLoginEvent.php rename to src/lib/MVC/Symfony/Event/InteractiveLoginEvent.php index b55424eb8b..ec5e545455 100644 --- a/eZ/Publish/Core/MVC/Symfony/Event/InteractiveLoginEvent.php +++ b/src/lib/MVC/Symfony/Event/InteractiveLoginEvent.php @@ -6,16 +6,16 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Event; +namespace Ibexa\Core\MVC\Symfony\Event; -use eZ\Publish\API\Repository\Values\User\User as APIUser; +use Ibexa\Contracts\Core\Repository\Values\User\User as APIUser; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Contracts\EventDispatcher\Event; class InteractiveLoginEvent extends Event { - /** @var \eZ\Publish\API\Repository\Values\User\User */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\User */ private $apiUser; /** @var \Symfony\Component\HttpFoundation\Request */ @@ -53,7 +53,7 @@ public function hasAPIUser(): bool /** * Injects an API user to be injected in the repository. * - * @param \eZ\Publish\API\Repository\Values\User\User $apiUser + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $apiUser */ public function setApiUser(APIUser $apiUser): void { @@ -61,10 +61,12 @@ public function setApiUser(APIUser $apiUser): void } /** - * @return \eZ\Publish\API\Repository\Values\User\User + * @return \Ibexa\Contracts\Core\Repository\Values\User\User */ public function getAPIUser(): APIUser { return $this->apiUser; } } + +class_alias(InteractiveLoginEvent::class, 'eZ\Publish\Core\MVC\Symfony\Event\InteractiveLoginEvent'); diff --git a/eZ/Publish/Core/MVC/Symfony/Event/PostSiteAccessMatchEvent.php b/src/lib/MVC/Symfony/Event/PostSiteAccessMatchEvent.php similarity index 84% rename from eZ/Publish/Core/MVC/Symfony/Event/PostSiteAccessMatchEvent.php rename to src/lib/MVC/Symfony/Event/PostSiteAccessMatchEvent.php index 1ba26de1b3..35520f3619 100644 --- a/eZ/Publish/Core/MVC/Symfony/Event/PostSiteAccessMatchEvent.php +++ b/src/lib/MVC/Symfony/Event/PostSiteAccessMatchEvent.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Event; +namespace Ibexa\Core\MVC\Symfony\Event; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess; use Symfony\Component\HttpFoundation\Request; use Symfony\Contracts\EventDispatcher\Event; @@ -15,7 +15,7 @@ */ class PostSiteAccessMatchEvent extends Event { - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ private $siteAccess; /** @var \Symfony\Component\HttpFoundation\Request */ @@ -47,7 +47,7 @@ public function getRequest() /** * Returns matched SiteAccess instance. * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess + * @return \Ibexa\Core\MVC\Symfony\SiteAccess */ public function getSiteAccess() { @@ -65,3 +65,5 @@ public function getRequestType() return $this->requestType; } } + +class_alias(PostSiteAccessMatchEvent::class, 'eZ\Publish\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent'); diff --git a/eZ/Publish/Core/MVC/Symfony/Event/PreContentViewEvent.php b/src/lib/MVC/Symfony/Event/PreContentViewEvent.php similarity index 82% rename from eZ/Publish/Core/MVC/Symfony/Event/PreContentViewEvent.php rename to src/lib/MVC/Symfony/Event/PreContentViewEvent.php index 72930fdcf2..fc5e13207d 100644 --- a/eZ/Publish/Core/MVC/Symfony/Event/PreContentViewEvent.php +++ b/src/lib/MVC/Symfony/Event/PreContentViewEvent.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Event; +namespace Ibexa\Core\MVC\Symfony\Event; -use eZ\Publish\Core\MVC\Symfony\View\View; +use Ibexa\Core\MVC\Symfony\View\View; use Symfony\Contracts\EventDispatcher\Event; /** @@ -34,7 +34,7 @@ */ class PreContentViewEvent extends Event { - /** @var \eZ\Publish\Core\MVC\Symfony\View\View */ + /** @var \Ibexa\Core\MVC\Symfony\View\View */ private $contentView; public function __construct(View $contentView) @@ -43,10 +43,12 @@ public function __construct(View $contentView) } /** - * @return \eZ\Publish\Core\MVC\Symfony\View\View + * @return \Ibexa\Core\MVC\Symfony\View\View */ public function getContentView() { return $this->contentView; } } + +class_alias(PreContentViewEvent::class, 'eZ\Publish\Core\MVC\Symfony\Event\PreContentViewEvent'); diff --git a/src/lib/MVC/Symfony/Event/ResolveRenderOptionsEvent.php b/src/lib/MVC/Symfony/Event/ResolveRenderOptionsEvent.php new file mode 100644 index 0000000000..fed74d63d0 --- /dev/null +++ b/src/lib/MVC/Symfony/Event/ResolveRenderOptionsEvent.php @@ -0,0 +1,36 @@ +renderOptions = $renderOptions; + } + + public function getRenderOptions(): RenderOptions + { + return $this->renderOptions; + } + + public function setRenderOptions(RenderOptions $renderOptions): void + { + $this->renderOptions = $renderOptions; + } +} + +class_alias(ResolveRenderOptionsEvent::class, 'eZ\Publish\Core\MVC\Symfony\Event\ResolveRenderOptionsEvent'); diff --git a/src/lib/MVC/Symfony/Event/RouteReferenceGenerationEvent.php b/src/lib/MVC/Symfony/Event/RouteReferenceGenerationEvent.php new file mode 100644 index 0000000000..19571aede2 --- /dev/null +++ b/src/lib/MVC/Symfony/Event/RouteReferenceGenerationEvent.php @@ -0,0 +1,55 @@ +routeReference = $routeReference; + $this->request = $request; + } + + /** + * @return \Symfony\Component\HttpFoundation\Request + */ + public function getRequest() + { + return $this->request; + } + + /** + * @return \Ibexa\Core\MVC\Symfony\Routing\RouteReference + */ + public function getRouteReference() + { + return $this->routeReference; + } + + /** + * @param \Ibexa\Core\MVC\Symfony\Routing\RouteReference $routeReference + */ + public function setRouteReference($routeReference) + { + $this->routeReference = $routeReference; + } +} + +class_alias(RouteReferenceGenerationEvent::class, 'eZ\Publish\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent'); diff --git a/src/lib/MVC/Symfony/Event/ScopeChangeEvent.php b/src/lib/MVC/Symfony/Event/ScopeChangeEvent.php new file mode 100644 index 0000000000..121c58e329 --- /dev/null +++ b/src/lib/MVC/Symfony/Event/ScopeChangeEvent.php @@ -0,0 +1,34 @@ +siteAccess = $siteAccess; + } + + /** + * @return \Ibexa\Core\MVC\Symfony\SiteAccess + */ + public function getSiteAccess() + { + return $this->siteAccess; + } +} + +class_alias(ScopeChangeEvent::class, 'eZ\Publish\Core\MVC\Symfony\Event\ScopeChangeEvent'); diff --git a/eZ/Publish/Core/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriber.php b/src/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriber.php similarity index 79% rename from eZ/Publish/Core/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriber.php rename to src/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriber.php index 84743039c9..814e863690 100644 --- a/eZ/Publish/Core/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriber.php +++ b/src/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriber.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\EventListener; - -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Event\PreContentViewEvent; -use eZ\Publish\Core\MVC\Symfony\ExpressionLanguage\ExpressionLanguage; -use eZ\Publish\Core\MVC\Symfony\ExpressionLanguage\TwigVariableProviderExtension; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\View\VariableProviderRegistry; -use eZ\Publish\Core\MVC\Symfony\View\View; +namespace Ibexa\Core\MVC\Symfony\EventListener; + +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Event\PreContentViewEvent; +use Ibexa\Core\MVC\Symfony\ExpressionLanguage\ExpressionLanguage; +use Ibexa\Core\MVC\Symfony\ExpressionLanguage\TwigVariableProviderExtension; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\View\VariableProviderRegistry; +use Ibexa\Core\MVC\Symfony\View\View; use Symfony\Component\EventDispatcher\EventSubscriberInterface; final class ContentViewTwigVariablesSubscriber implements EventSubscriberInterface @@ -23,10 +23,10 @@ final class ContentViewTwigVariablesSubscriber implements EventSubscriberInterfa public const PARAMETERS_KEY = 'params'; - /** @var \eZ\Publish\Core\MVC\Symfony\View\VariableProviderRegistry */ + /** @var \Ibexa\Core\MVC\Symfony\View\VariableProviderRegistry */ private $parameterProviderRegistry; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; /** @var \Symfony\Component\ExpressionLanguage\ExpressionLanguage */ @@ -88,3 +88,5 @@ private function getExpression(string $twigVariable): string return substr($twigVariable, strlen(self::EXPRESSION_INDICATOR)); } } + +class_alias(ContentViewTwigVariablesSubscriber::class, 'eZ\Publish\Core\MVC\Symfony\EventListener\ContentViewTwigVariablesSubscriber'); diff --git a/src/lib/MVC/Symfony/EventListener/LanguageSwitchListener.php b/src/lib/MVC/Symfony/EventListener/LanguageSwitchListener.php new file mode 100644 index 0000000000..14dc3bc582 --- /dev/null +++ b/src/lib/MVC/Symfony/EventListener/LanguageSwitchListener.php @@ -0,0 +1,61 @@ +translationHelper = $translationHelper; + } + + public static function getSubscribedEvents() + { + return [ + MVCEvents::ROUTE_REFERENCE_GENERATION => 'onRouteReferenceGeneration', + ]; + } + + /** + * If "language" parameter is present, will try to get corresponding SiteAccess for translation. + * If found, it will add "siteaccess" parameter to the RouteReference, to trigger SiteAccess switch when generating + * the final link. + * + * @see \Ibexa\Core\MVC\Symfony\Routing\Generator::generate + * @see \Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator::doGenerate + * + * @param \Ibexa\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent $event + */ + public function onRouteReferenceGeneration(RouteReferenceGenerationEvent $event) + { + $routeReference = $event->getRouteReference(); + if (!$routeReference->has('language')) { + return; + } + + $language = $routeReference->get('language'); + $routeReference->remove('language'); + $siteAccess = $this->translationHelper->getTranslationSiteAccess($language); + if ($siteAccess !== null) { + $routeReference->set('siteaccess', $siteAccess); + } + } +} + +class_alias(LanguageSwitchListener::class, 'eZ\Publish\Core\MVC\Symfony\EventListener\LanguageSwitchListener'); diff --git a/eZ/Publish/Core/MVC/Symfony/EventListener/SiteAccessMatchListener.php b/src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php similarity index 82% rename from eZ/Publish/Core/MVC/Symfony/EventListener/SiteAccessMatchListener.php rename to src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php index 5aeab5ba7e..e55cb6a6a0 100644 --- a/eZ/Publish/Core/MVC/Symfony/EventListener/SiteAccessMatchListener.php +++ b/src/lib/MVC/Symfony/EventListener/SiteAccessMatchListener.php @@ -4,18 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\EventListener; - -// @todo Move SiteAccessMatcherRegistryInterface to eZ\Publish\Core\MVC\Symfony\Matcher -use eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessMatcherRegistryInterface; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\SerializerTrait; -use eZ\Publish\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router as SiteAccessRouter; -use eZ\Publish\Core\MVC\Symfony\SiteAccessGroup; +namespace Ibexa\Core\MVC\Symfony\EventListener; + +// @todo Move SiteAccessMatcherRegistryInterface to \Ibexa\Core\MVC\Symfony\SiteAccess\Matcher +use Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistryInterface; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\MVC\Symfony\Component\Serializer\SerializerTrait; +use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router as SiteAccessRouter; +use Ibexa\Core\MVC\Symfony\SiteAccessGroup; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; @@ -31,13 +31,13 @@ class SiteAccessMatchListener implements EventSubscriberInterface { use SerializerTrait; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\Router */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\Router */ protected $siteAccessRouter; /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ protected $eventDispatcher; - /** @var \eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessMatcherRegistryInterface */ + /** @var \Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistryInterface */ private $siteAccessMatcherRegistry; public function __construct( @@ -61,8 +61,8 @@ public static function getSubscribedEvents() /** * @param \Symfony\Component\HttpKernel\Event\RequestEvent $event * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function onKernelRequest(RequestEvent $event): void { @@ -71,7 +71,7 @@ public function onKernelRequest(RequestEvent $event): void // We have a serialized siteaccess object from a fragment (sub-request), we need to get it back. if ($request->attributes->has('serialized_siteaccess')) { $serializer = $this->getSerializer(); - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess $siteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess $siteAccess */ $siteAccess = $serializer->deserialize($request->attributes->get('serialized_siteaccess'), SiteAccess::class, 'json'); if ($siteAccess->matcher !== null) { $siteAccess->matcher = $this->deserializeMatcher( @@ -108,7 +108,7 @@ public function onKernelRequest(RequestEvent $event): void /** * @param \Symfony\Component\HttpFoundation\Request $request * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess + * @return \Ibexa\Core\MVC\Symfony\SiteAccess */ private function getSiteAccessFromRequest(Request $request) { @@ -128,8 +128,8 @@ private function getSiteAccessFromRequest(Request $request) } /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ private function deserializeMatcher( SerializerInterface $serializer, @@ -172,7 +172,7 @@ private function deserializeMatcher( /** * @param array $serializedGroups * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccessGroup[] + * @return \Ibexa\Core\MVC\Symfony\SiteAccessGroup[] */ private function buildGroups( array $serializedGroups @@ -186,7 +186,7 @@ static function (array $serializedGroup): SiteAccessGroup { } /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ private function buildMatcherFromSerializedClass( SerializerInterface $serializer, @@ -207,3 +207,5 @@ private function buildMatcherFromSerializedClass( return $matcher; } } + +class_alias(SiteAccessMatchListener::class, 'eZ\Publish\Core\MVC\Symfony\EventListener\SiteAccessMatchListener'); diff --git a/eZ/Publish/Core/MVC/Symfony/ExpressionLanguage/ExpressionLanguage.php b/src/lib/MVC/Symfony/ExpressionLanguage/ExpressionLanguage.php similarity index 79% rename from eZ/Publish/Core/MVC/Symfony/ExpressionLanguage/ExpressionLanguage.php rename to src/lib/MVC/Symfony/ExpressionLanguage/ExpressionLanguage.php index 7ab81a2bec..5b608b8ab2 100644 --- a/eZ/Publish/Core/MVC/Symfony/ExpressionLanguage/ExpressionLanguage.php +++ b/src/lib/MVC/Symfony/ExpressionLanguage/ExpressionLanguage.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\ExpressionLanguage; +namespace Ibexa\Core\MVC\Symfony\ExpressionLanguage; use Psr\Cache\CacheItemPoolInterface; use Symfony\Component\ExpressionLanguage\ExpressionLanguage as BaseExpressionLanguage; @@ -22,3 +22,5 @@ public function __construct( parent::__construct($cache, $providers); } } + +class_alias(ExpressionLanguage::class, 'eZ\Publish\Core\MVC\Symfony\ExpressionLanguage\ExpressionLanguage'); diff --git a/eZ/Publish/Core/MVC/Symfony/ExpressionLanguage/TwigVariableProviderExtension.php b/src/lib/MVC/Symfony/ExpressionLanguage/TwigVariableProviderExtension.php similarity index 85% rename from eZ/Publish/Core/MVC/Symfony/ExpressionLanguage/TwigVariableProviderExtension.php rename to src/lib/MVC/Symfony/ExpressionLanguage/TwigVariableProviderExtension.php index 1de76b9e66..756364f84f 100644 --- a/eZ/Publish/Core/MVC/Symfony/ExpressionLanguage/TwigVariableProviderExtension.php +++ b/src/lib/MVC/Symfony/ExpressionLanguage/TwigVariableProviderExtension.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\ExpressionLanguage; +namespace Ibexa\Core\MVC\Symfony\ExpressionLanguage; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; -use eZ\Publish\Core\MVC\Symfony\View\VariableProviderRegistry; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\MVC\Symfony\View\ContentView; +use Ibexa\Core\MVC\Symfony\View\VariableProviderRegistry; use Symfony\Component\ExpressionLanguage\ExpressionFunction; use Symfony\Component\ExpressionLanguage\ExpressionFunctionProviderInterface; @@ -55,3 +55,5 @@ private function hasParameterProvider(array $variables): bool && $variables[self::PROVIDER_REGISTRY_PARAMETER] instanceof VariableProviderRegistry; } } + +class_alias(TwigVariableProviderExtension::class, 'eZ\Publish\Core\MVC\Symfony\ExpressionLanguage\TwigVariableProviderExtension'); diff --git a/src/lib/MVC/Symfony/FieldType/BinaryBase/ContentDownloadUrlGenerator.php b/src/lib/MVC/Symfony/FieldType/BinaryBase/ContentDownloadUrlGenerator.php new file mode 100644 index 0000000000..14b7b7849a --- /dev/null +++ b/src/lib/MVC/Symfony/FieldType/BinaryBase/ContentDownloadUrlGenerator.php @@ -0,0 +1,53 @@ +router = $router; + } + + public function getStoragePathForField(Field $field, VersionInfo $versionInfo) + { + return $this->generate($this->route, $this->getParameters($field, $versionInfo)); + } + + public function generate(string $route, ?array $parameters = []): string + { + return $this->router->generate($route, $parameters ?? []); + } + + public function getRoute(Field $field, VersionInfo $versionInfo): string + { + return $this->route; + } + + public function getParameters(Field $field, VersionInfo $versionInfo): array + { + return [ + 'contentId' => $versionInfo->contentInfo->id, + 'fieldId' => $field->id, + 'version' => $versionInfo->versionNo, + ]; + } +} + +class_alias(ContentDownloadUrlGenerator::class, 'eZ\Publish\Core\MVC\Symfony\FieldType\BinaryBase\ContentDownloadUrlGenerator'); diff --git a/src/lib/MVC/Symfony/FieldType/ImageAsset/ParameterProvider.php b/src/lib/MVC/Symfony/FieldType/ImageAsset/ParameterProvider.php new file mode 100644 index 0000000000..b71e6afa2f --- /dev/null +++ b/src/lib/MVC/Symfony/FieldType/ImageAsset/ParameterProvider.php @@ -0,0 +1,101 @@ +repository = $repository; + $this->permissionsResolver = $repository->getPermissionResolver(); + $this->fieldTypeService = $repository->getFieldTypeService(); + } + + /** + * {@inheritdoc} + */ + public function getViewParameters(Field $field): array + { + $fieldType = $this->fieldTypeService->getFieldType($field->fieldTypeIdentifier); + + if ($fieldType->isEmptyValue($field->value)) { + return [ + 'available' => null, + ]; + } + + try { + $contentInfo = $this->loadContentInfo( + (int)$field->value->destinationContentId + ); + + return [ + 'available' => !$contentInfo->isTrashed() && $this->userHasPermissions($contentInfo), + ]; + } catch (NotFoundException $exception) { + return [ + 'available' => false, + ]; + } + } + + /** + * @param int $id + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function loadContentInfo(int $id): ContentInfo + { + return $this->repository->sudo( + static function (Repository $repository) use ($id) { + return $repository->getContentService()->loadContentInfo($id); + } + ); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return bool + */ + private function userHasPermissions(ContentInfo $contentInfo): bool + { + if ($this->permissionsResolver->canUser('content', 'read', $contentInfo)) { + return true; + } + + if ($this->permissionsResolver->canUser('content', 'view_embed', $contentInfo)) { + return true; + } + + return false; + } +} + +class_alias(ParameterProvider::class, 'eZ\Publish\Core\MVC\Symfony\FieldType\ImageAsset\ParameterProvider'); diff --git a/src/lib/MVC/Symfony/FieldType/Relation/ParameterProvider.php b/src/lib/MVC/Symfony/FieldType/Relation/ParameterProvider.php new file mode 100644 index 0000000000..625b009089 --- /dev/null +++ b/src/lib/MVC/Symfony/FieldType/Relation/ParameterProvider.php @@ -0,0 +1,60 @@ +contentService = $contentService; + } + + /** + * Returns a hash of parameters to inject to the associated fieldtype's view template. + * Returned parameters will only be available for associated field type. + * + * Key is the parameter name (the variable name exposed in the template, in the 'parameters' array). + * Value is the parameter's value. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field The field parameters are provided for. + * + * @return array + */ + public function getViewParameters(Field $field): array + { + try { + $contentInfo = null; + if ($field->value->destinationContentId !== null) { + $contentInfo = $this->contentService->loadContentInfo( + $field->value->destinationContentId + ); + } + + return [ + 'available' => $contentInfo !== null && !$contentInfo->isTrashed(), + ]; + } catch (NotFoundException | UnauthorizedException $exception) { + return [ + 'available' => false, + ]; + } + } +} + +class_alias(ParameterProvider::class, 'eZ\Publish\Core\MVC\Symfony\FieldType\Relation\ParameterProvider'); diff --git a/src/lib/MVC/Symfony/FieldType/RelationList/ParameterProvider.php b/src/lib/MVC/Symfony/FieldType/RelationList/ParameterProvider.php new file mode 100644 index 0000000000..b5a2cb4b46 --- /dev/null +++ b/src/lib/MVC/Symfony/FieldType/RelationList/ParameterProvider.php @@ -0,0 +1,59 @@ +contentService = $contentService; + } + + /** + * Returns a hash of parameters to inject to the associated fieldtype's view template. + * Returned parameters will only be available for associated field type. + * + * Key is the parameter name (the variable name exposed in the template, in the 'parameters' array). + * Value is the parameter's value. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field The field parameters are provided for. + * + * @return array + */ + public function getViewParameters(Field $field) + { + $ids = $field->value->destinationContentIds; + $list = $this->contentService->loadContentInfoList($ids); + + // Start by setting missing ids as false on $available + $available = array_fill_keys( + array_diff($ids, array_keys($list)), + false + ); + + // Check if loaded items are in trash or not, for availability + foreach ($list as $contentId => $contentInfo) { + $available[$contentId] = !$contentInfo->isTrashed(); + } + + return [ + 'available' => $available, + ]; + } +} + +class_alias(ParameterProvider::class, 'eZ\Publish\Core\MVC\Symfony\FieldType\RelationList\ParameterProvider'); diff --git a/src/lib/MVC/Symfony/FieldType/User/ParameterProvider.php b/src/lib/MVC/Symfony/FieldType/User/ParameterProvider.php new file mode 100644 index 0000000000..4660da692e --- /dev/null +++ b/src/lib/MVC/Symfony/FieldType/User/ParameterProvider.php @@ -0,0 +1,45 @@ +userService = $userService; + } + + public function getViewParameters(Field $field): array + { + $passwordInfo = $this->userService->getPasswordInfo( + $this->userService->loadUser($field->value->contentId) + ); + + $passwordExpiresIn = null; + if (!$passwordInfo->isPasswordExpired() && $passwordInfo->hasExpirationDate()) { + $passwordExpiresIn = $passwordInfo->getExpirationDate()->diff(new DateTime()); + } + + return [ + 'is_password_expired' => $passwordInfo->isPasswordExpired(), + 'password_expires_at' => $passwordInfo->getExpirationDate(), + 'password_expires_in' => $passwordExpiresIn, + ]; + } +} + +class_alias(ParameterProvider::class, 'eZ\Publish\Core\MVC\Symfony\FieldType\User\ParameterProvider'); diff --git a/src/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProvider.php b/src/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProvider.php new file mode 100644 index 0000000000..729156cdc7 --- /dev/null +++ b/src/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProvider.php @@ -0,0 +1,55 @@ +localeConverter = $localeConverter; + } + + /** + * Returns a hash with 'locale' as key and locale string in POSIX format as value. + * + * Locale from request object will be used as locale if set, otherwise field language code + * will be converted to locale string. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + * + * @return array + */ + public function getViewParameters(Field $field) + { + $parameters = []; + + $request = $this->getCurrentRequest(); + if ($request && $request->attributes->has('_locale')) { + $parameters['locale'] = $request->attributes->get('_locale'); + } else { + $parameters['locale'] = $this->localeConverter->convertToPOSIX($field->languageCode); + } + + return $parameters; + } +} + +class_alias(LocaleParameterProvider::class, 'eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProvider\LocaleParameterProvider'); diff --git a/src/lib/MVC/Symfony/FieldType/View/ParameterProviderInterface.php b/src/lib/MVC/Symfony/FieldType/View/ParameterProviderInterface.php new file mode 100644 index 0000000000..f5e5536732 --- /dev/null +++ b/src/lib/MVC/Symfony/FieldType/View/ParameterProviderInterface.php @@ -0,0 +1,33 @@ +providers[$fieldTypeIdentifier] = $parameterProvider; } } + +class_alias(ParameterProviderRegistry::class, 'eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry'); diff --git a/src/lib/MVC/Symfony/FieldType/View/ParameterProviderRegistryInterface.php b/src/lib/MVC/Symfony/FieldType/View/ParameterProviderRegistryInterface.php new file mode 100644 index 0000000000..3af3df6d52 --- /dev/null +++ b/src/lib/MVC/Symfony/FieldType/View/ParameterProviderRegistryInterface.php @@ -0,0 +1,43 @@ +conversionMap = $conversionMap; + $this->reverseConversionMap = array_flip($conversionMap); + $this->logger = $logger; + } + + /** + * Converts a locale in Ibexa internal format to POSIX format. + * Returns null if conversion cannot be made. + * + * @param string $ezpLocale + * + * @return string|null + */ + public function convertToPOSIX($ezpLocale) + { + if (!isset($this->conversionMap[$ezpLocale])) { + $this->logger->warning("Could not convert locale '$ezpLocale' to POSIX format. Please check your locale configuration in ezplatform.yml"); + + return; + } + + return $this->conversionMap[$ezpLocale]; + } + + /** + * Converts a locale in POSIX format to Ibexa internal format. + * Returns null if conversion cannot be made. + * + * @param string $posixLocale + * + * @return string|null + */ + public function convertToEz($posixLocale) + { + if (!isset($this->reverseConversionMap[$posixLocale])) { + $this->logger->warning("Could not convert locale '$posixLocale' to Ibexa format. Please check your locale configuration in ezplatform.yml"); + + return; + } + + return $this->reverseConversionMap[$posixLocale]; + } + + public function convertToRepository(string $posixLocale): ?string + { + if (!isset($this->reverseConversionMap[$posixLocale])) { + $this->logger->warning("Could not convert locale '$posixLocale' to Repository format. Please check your locale configuration in ibexa.yaml"); + + return null; + } + + return $this->reverseConversionMap[$posixLocale]; + } +} + +class_alias(LocaleConverter::class, 'eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverter'); diff --git a/src/lib/MVC/Symfony/Locale/LocaleConverterInterface.php b/src/lib/MVC/Symfony/Locale/LocaleConverterInterface.php new file mode 100644 index 0000000000..08bd545578 --- /dev/null +++ b/src/lib/MVC/Symfony/Locale/LocaleConverterInterface.php @@ -0,0 +1,47 @@ +- locale format (mostly, some supported locales being out of this format, e.g. cro-HR). + * Symfony uses the standard POSIX locale format (_), which is supported by Intl PHP extension. + * + * Locale converters are meant to convert in those 2 formats back and forth. + */ +interface LocaleConverterInterface +{ + /** + * Converts a locale in Ibexa internal format to POSIX format. + * Returns null if conversion cannot be made. + * + * @param string $ezpLocale + * + * @return string|null + */ + public function convertToPOSIX($ezpLocale); + + /** + * Converts a locale in POSIX format to Ibexa internal format. + * Returns null if conversion cannot be made. + * + * @deprecated 4.5.2 To be removed in 5.0. Use {@see convertToRepository()} instead. + * + * @param string $posixLocale + * + * @return string|null + */ + public function convertToEz($posixLocale); + + /** + * Converts a locale in POSIX format to Repository internal format. + * Returns null if conversion cannot be made. + */ + public function convertToRepository(string $posixLocale): ?string; +} + +class_alias(LocaleConverterInterface::class, 'eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface'); diff --git a/eZ/Publish/Core/MVC/Symfony/Locale/UserLanguagePreferenceProvider.php b/src/lib/MVC/Symfony/Locale/UserLanguagePreferenceProvider.php similarity index 83% rename from eZ/Publish/Core/MVC/Symfony/Locale/UserLanguagePreferenceProvider.php rename to src/lib/MVC/Symfony/Locale/UserLanguagePreferenceProvider.php index d7e591fbc5..cd317fbe52 100644 --- a/eZ/Publish/Core/MVC/Symfony/Locale/UserLanguagePreferenceProvider.php +++ b/src/lib/MVC/Symfony/Locale/UserLanguagePreferenceProvider.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Locale; +namespace Ibexa\Core\MVC\Symfony\Locale; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\UserPreferenceService; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\UserPreferenceService; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -18,7 +18,7 @@ class UserLanguagePreferenceProvider implements UserLanguagePreferenceProviderIn /** @var \Symfony\Component\HttpFoundation\RequestStack */ private $requestStack; - /** @var \eZ\Publish\API\Repository\UserPreferenceService */ + /** @var \Ibexa\Contracts\Core\Repository\UserPreferenceService */ private $userPreferenceService; /** @var array */ @@ -29,7 +29,7 @@ class UserLanguagePreferenceProvider implements UserLanguagePreferenceProviderIn /** * @param \Symfony\Component\HttpFoundation\RequestStack $requestStack - * @param \eZ\Publish\API\Repository\UserPreferenceService $userPreferenceService + * @param \Ibexa\Contracts\Core\Repository\UserPreferenceService $userPreferenceService * @param array $languageCodesMap * @param string $localeFallback */ @@ -81,3 +81,5 @@ public function getPreferredLanguages(): array return array_unique(array_merge(...$languageCodes)); } } + +class_alias(UserLanguagePreferenceProvider::class, 'eZ\Publish\Core\MVC\Symfony\Locale\UserLanguagePreferenceProvider'); diff --git a/eZ/Publish/Core/MVC/Symfony/Locale/UserLanguagePreferenceProviderInterface.php b/src/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderInterface.php similarity index 78% rename from eZ/Publish/Core/MVC/Symfony/Locale/UserLanguagePreferenceProviderInterface.php rename to src/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderInterface.php index 4833c6cbe4..49d892625c 100644 --- a/eZ/Publish/Core/MVC/Symfony/Locale/UserLanguagePreferenceProviderInterface.php +++ b/src/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderInterface.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Locale; +namespace Ibexa\Core\MVC\Symfony\Locale; use Symfony\Component\HttpFoundation\Request; @@ -25,9 +25,11 @@ interface UserLanguagePreferenceProviderInterface public function getPreferredLocales(Request $request = null): array; /** - * List of eZ Language codes. + * List of Ibexa Language codes. * * @return string[] */ public function getPreferredLanguages(): array; } + +class_alias(UserLanguagePreferenceProviderInterface::class, 'eZ\Publish\Core\MVC\Symfony\Locale\UserLanguagePreferenceProviderInterface'); diff --git a/src/lib/MVC/Symfony/MVCEvents.php b/src/lib/MVC/Symfony/MVCEvents.php new file mode 100644 index 0000000000..903d67e6db --- /dev/null +++ b/src/lib/MVC/Symfony/MVCEvents.php @@ -0,0 +1,80 @@ +repository = $repository; $this->matcherRelativeNamespace = $relativeNamespace; @@ -66,7 +64,7 @@ public function __construct(Repository $repository, $relativeNamespace = null, a * * @throws \InvalidArgumentException * - * @return \eZ\Publish\Core\MVC\Symfony\Matcher\ViewMatcherInterface + * @return \Ibexa\Core\MVC\Symfony\Matcher\ViewMatcherInterface */ protected function getMatcher($matcherIdentifier) { @@ -97,7 +95,7 @@ protected function getMatcher($matcherIdentifier) * If so, the configuration hash will be returned. * $valueObject can be for example a Location or a Content object. * - * @param \eZ\Publish\Core\MVC\Symfony\View\View $view + * @param \Ibexa\Core\MVC\Symfony\View\View $view * * @return array|null The matched configuration as a hash, containing template or controller to use, or null if not matched. */ @@ -145,3 +143,5 @@ public function setMatchConfig(array $matchConfig): void $this->matchConfig = $matchConfig; } } + +class_alias(ClassNameMatcherFactory::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ClassNameMatcherFactory'); diff --git a/src/lib/MVC/Symfony/Matcher/ConfigurableMatcherFactoryInterface.php b/src/lib/MVC/Symfony/Matcher/ConfigurableMatcherFactoryInterface.php new file mode 100644 index 0000000000..ef9c6a0a59 --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ConfigurableMatcherFactoryInterface.php @@ -0,0 +1,17 @@ +values[$location->depth]); + } + + /** + * Checks if a ContentInfo object matches. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return bool + */ + public function matchContentInfo(ContentInfo $contentInfo) + { + $location = $this->repository->sudo( + static function (Repository $repository) use ($contentInfo) { + return $repository->getLocationService()->loadLocation($contentInfo->mainLocationId); + } + ); + + return isset($this->values[$location->depth]); + } + + public function match(View $view) + { + if ($view instanceof LocationValueView) { + return isset($this->values[$view->getLocation()->depth]); + } + + if ($view instanceof ContentValueView) { + return $this->matchContentInfo($view->getContent()->contentInfo); + } + + return false; + } +} + +class_alias(Depth::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Depth'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/Id/Content.php b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/Content.php new file mode 100644 index 0000000000..3158b989b0 --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/Content.php @@ -0,0 +1,51 @@ +values[$location->getContentInfo()->id]); + } + + /** + * Checks if a ContentInfo object matches. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return bool + */ + public function matchContentInfo(ContentInfo $contentInfo) + { + return isset($this->values[$contentInfo->id]); + } + + public function match(View $view) + { + if (!$view instanceof ContentValueView) { + return false; + } + + return isset($this->values[$view->getContent()->contentInfo->id]); + } +} + +class_alias(Content::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Content'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentType.php b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentType.php new file mode 100644 index 0000000000..630e11205e --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentType.php @@ -0,0 +1,51 @@ +values[$location->getContentInfo()->contentTypeId]); + } + + /** + * Checks if a ContentInfo object matches. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return bool + */ + public function matchContentInfo(ContentInfo $contentInfo) + { + return isset($this->values[$contentInfo->contentTypeId]); + } + + public function match(View $view) + { + if (!$view instanceof ContentValueView) { + return false; + } + + return isset($this->values[$view->getContent()->contentInfo->contentTypeId]); + } +} + +class_alias(ContentType::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentType'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroup.php b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroup.php new file mode 100644 index 0000000000..8b3b6fb74d --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroup.php @@ -0,0 +1,70 @@ +matchContentTypeId($location->getContentInfo()->contentTypeId); + } + + /** + * Checks if a ContentInfo object matches. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return bool + */ + public function matchContentInfo(ContentInfo $contentInfo) + { + return $this->matchContentTypeId($contentInfo->contentTypeId); + } + + public function match(View $view) + { + if (!$view instanceof ContentValueView) { + return false; + } + + return $this->matchContentTypeId($view->getContent()->contentInfo->contentTypeId); + } + + /** + * @return bool + */ + private function matchContentTypeId($contentTypeId) + { + $contentTypeGroups = $this->repository + ->getContentTypeService() + ->loadContentType($contentTypeId) + ->getContentTypeGroups(); + + foreach ($contentTypeGroups as $group) { + if (isset($this->values[$group->id])) { + return true; + } + } + + return false; + } +} + +class_alias(ContentTypeGroup::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentTypeGroup'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/Id/Location.php b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/Location.php new file mode 100644 index 0000000000..412c836aec --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/Location.php @@ -0,0 +1,55 @@ +values[$location->id]); + } + + /** + * Checks if a ContentInfo object matches. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return bool + */ + public function matchContentInfo(ContentInfo $contentInfo) + { + return isset($this->values[$contentInfo->mainLocationId]); + } + + public function match(View $view) + { + if (!$view instanceof LocationValueView) { + return false; + } + + if (null === $view->getLocation()) { + return false; + } + + return isset($this->values[$view->getLocation()->id]); + } +} + +class_alias(Location::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Location'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/Id/LocationRemote.php b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/LocationRemote.php new file mode 100644 index 0000000000..0f3b870650 --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/LocationRemote.php @@ -0,0 +1,49 @@ +values[$view->getLocation()->remoteId]); + } +} + +class_alias(LocationRemote::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\LocationRemote'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentType.php b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentType.php new file mode 100644 index 0000000000..8b0ac09a3f --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentType.php @@ -0,0 +1,79 @@ +repository->sudo( + static function (Repository $repository) use ($location) { + return $repository->getLocationService()->loadLocation($location->parentLocationId); + } + ); + + return isset($this->values[$parent->getContentInfo()->contentTypeId]); + } + + /** + * Checks if a ContentInfo object matches. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return bool + */ + public function matchContentInfo(ContentInfo $contentInfo) + { + $location = $this->repository->sudo( + static function (Repository $repository) use ($contentInfo) { + return $repository->getLocationService()->loadLocation($contentInfo->mainLocationId); + } + ); + + return $this->matchLocation($location); + } + + public function match(View $view) + { + if (!$view instanceof LocationValueView) { + return false; + } + $parent = $this->loadParentLocation( + $view->getLocation()->parentLocationId + ); + + return isset($this->values[$parent->getContentInfo()->contentTypeId]); + } + + /** + * @return Location + */ + private function loadParentLocation($locationId) + { + return $this->repository->sudo( + static function (Repository $repository) use ($locationId) { + return $repository->getLocationService()->loadLocation($locationId); + } + ); + } +} + +class_alias(ParentContentType::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentContentType'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentLocation.php b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentLocation.php new file mode 100644 index 0000000000..3b5dd3e6ce --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentLocation.php @@ -0,0 +1,58 @@ +values[$location->parentLocationId]); + } + + /** + * Checks if a ContentInfo object matches. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return bool + */ + public function matchContentInfo(ContentInfo $contentInfo) + { + $location = $this->repository->sudo( + static function (Repository $repository) use ($contentInfo) { + return $repository->getLocationService()->loadLocation($contentInfo->mainLocationId); + } + ); + + return isset($this->values[$location->parentLocationId]); + } + + public function match(View $view) + { + if (!$view instanceof LocationValueView) { + return false; + } + + return isset($this->values[$view->getLocation()->parentLocationId]); + } +} + +class_alias(ParentLocation::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentLocation'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/Id/Remote.php b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/Remote.php new file mode 100644 index 0000000000..8b8f15c619 --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/Remote.php @@ -0,0 +1,51 @@ +values[$location->remoteId]); + } + + /** + * Checks if a ContentInfo object matches. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return bool + */ + public function matchContentInfo(ContentInfo $contentInfo) + { + return isset($this->values[$contentInfo->remoteId]); + } + + public function match(View $view) + { + if (!$view instanceof ContentValueView) { + return false; + } + + return isset($this->values[$view->getContent()->contentInfo->remoteId]); + } +} + +class_alias(Remote::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Remote'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/Id/Section.php b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/Section.php new file mode 100644 index 0000000000..6573b56ea7 --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/Id/Section.php @@ -0,0 +1,51 @@ +values[$location->getContentInfo()->getSectionId()]); + } + + /** + * Checks if a ContentInfo object matches. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return bool + */ + public function matchContentInfo(ContentInfo $contentInfo) + { + return isset($this->values[$contentInfo->getSectionId()]); + } + + public function match(View $view) + { + if (!$view instanceof ContentValueView) { + return false; + } + + return isset($this->values[$view->getContent()->contentInfo->getSectionId()]); + } +} + +class_alias(Section::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\Section'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ContentType.php b/src/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ContentType.php new file mode 100644 index 0000000000..62deaf6a80 --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ContentType.php @@ -0,0 +1,63 @@ +repository + ->getContentTypeService() + ->loadContentType($location->getContentInfo()->contentTypeId); + + return isset($this->values[$contentType->identifier]); + } + + /** + * Checks if a ContentInfo object matches. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return bool + */ + public function matchContentInfo(ContentInfo $contentInfo) + { + $contentType = $this->repository + ->getContentTypeService() + ->loadContentType($contentInfo->contentTypeId); + + return isset($this->values[$contentType->identifier]); + } + + public function match(View $view) + { + if (!$view instanceof ContentValueView) { + return false; + } + + $contentType = $this->repository + ->getContentTypeService() + ->loadContentType($view->getContent()->contentInfo->contentTypeId); + + return isset($this->values[$contentType->identifier]); + } +} + +class_alias(ContentType::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ContentType'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentType.php b/src/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentType.php new file mode 100644 index 0000000000..932c049929 --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentType.php @@ -0,0 +1,73 @@ +repository->sudo( + static function (Repository $repository) use ($location) { + $parent = $repository->getLocationService()->loadLocation($location->parentLocationId); + + return $repository + ->getContentTypeService() + ->loadContentType($parent->getContentInfo()->contentTypeId); + } + ); + + return isset($this->values[$parentContentType->identifier]); + } + + /** + * Checks if a ContentInfo object matches. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return bool + */ + public function matchContentInfo(ContentInfo $contentInfo) + { + $location = $this->repository->sudo( + static function (Repository $repository) use ($contentInfo) { + return $repository->getLocationService()->loadLocation($contentInfo->mainLocationId); + } + ); + + return $this->matchLocation($location); + } + + public function match(View $view) + { + if ($view instanceof LocationValueView) { + return $this->matchLocation($view->getLocation()); + } + + if ($view instanceof ContentValueView) { + return $this->matchContentInfo($view->getContent()->contentInfo); + } + + return false; + } +} + +class_alias(ParentContentType::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ParentContentType'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/Identifier/Section.php b/src/lib/MVC/Symfony/Matcher/ContentBased/Identifier/Section.php new file mode 100644 index 0000000000..97cff3130d --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/Identifier/Section.php @@ -0,0 +1,77 @@ +repository->sudo( + static function (Repository $repository) use ($location) { + return $repository->getSectionService()->loadSection( + $location->getContentInfo()->getSectionId() + ); + } + ); + + return isset($this->values[$section->identifier]); + } + + /** + * Checks if a ContentInfo object matches. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return bool + */ + public function matchContentInfo(ContentInfo $contentInfo) + { + $section = $this->repository->sudo( + static function (Repository $repository) use ($contentInfo) { + return $repository->getSectionService()->loadSection( + $contentInfo->getSectionId() + ); + } + ); + + return isset($this->values[$section->identifier]); + } + + public function match(View $view) + { + if (!$view instanceof ContentValueView) { + return false; + } + + $contentInfo = $view->getContent()->contentInfo; + $section = $this->repository->sudo( + static function (Repository $repository) use ($contentInfo) { + return $repository->getSectionService()->loadSection( + $contentInfo->getSectionId() + ); + } + ); + + return isset($this->values[$section->identifier]); + } +} + +class_alias(Section::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Identifier\Section'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/IsPreview.php b/src/lib/MVC/Symfony/Matcher/ContentBased/IsPreview.php new file mode 100644 index 0000000000..c9221cc466 --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/IsPreview.php @@ -0,0 +1,48 @@ +isPreview = $matchingConfig; + } + + public function match(View $view): bool + { + $isPreview = $view->hasParameter(PreviewController::PREVIEW_PARAMETER_NAME) + && $view->getParameter(PreviewController::PREVIEW_PARAMETER_NAME); + + return $this->isPreview === $isPreview; + } +} diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/MatcherInterface.php b/src/lib/MVC/Symfony/Matcher/ContentBased/MatcherInterface.php new file mode 100644 index 0000000000..4489855485 --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/MatcherInterface.php @@ -0,0 +1,37 @@ +repository; } } + +class_alias(MultipleValued::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued'); diff --git a/src/lib/MVC/Symfony/Matcher/ContentBased/UrlAlias.php b/src/lib/MVC/Symfony/Matcher/ContentBased/UrlAlias.php new file mode 100644 index 0000000000..ec7f0b07a5 --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/ContentBased/UrlAlias.php @@ -0,0 +1,82 @@ +repository->getURLAliasService(); + $locationUrls = array_merge( + $urlAliasService->listLocationAliases($location), + $urlAliasService->listLocationAliases($location, false) + ); + + foreach ($this->values as $pattern => $val) { + foreach ($locationUrls as $urlAlias) { + if (strpos((string)$urlAlias->path, "/$pattern") === 0) { + return true; + } + } + } + + return false; + } + + /** + * Not supported since UrlAlias is meaningful for location objects only. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @throws \RuntimeException + * + * @return bool + */ + public function matchContentInfo(ContentInfo $contentInfo) + { + throw new \RuntimeException('matchContentInfo() is not supported by the UrlAlias matcher'); + } + + public function setMatchingConfig($matchingConfig) + { + if (!is_array($matchingConfig)) { + $matchingConfig = [$matchingConfig]; + } + + array_walk( + $matchingConfig, + static function (&$item) { + $item = trim($item, '/ '); + } + ); + + parent::setMatchingConfig($matchingConfig); + } + + public function match(View $view) + { + if (!$view instanceof LocationValueView) { + return false; + } + + return $this->matchLocation($view->getLocation()); + } +} + +class_alias(UrlAlias::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias'); diff --git a/src/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecorator.php b/src/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecorator.php new file mode 100644 index 0000000000..d8a0be6c9c --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecorator.php @@ -0,0 +1,55 @@ +innerConfigurableMatcherFactory = $innerConfigurableMatcherFactory; + $this->configResolver = $configResolver; + $this->parameterName = $parameterName; + $this->namespace = $namespace; + $this->scope = $scope; + } + + public function match(View $view) + { + $matchConfig = $this->configResolver->getParameter($this->parameterName, $this->namespace, $this->scope); + $this->innerConfigurableMatcherFactory->setMatchConfig($matchConfig); + + return $this->innerConfigurableMatcherFactory->match($view); + } +} + +class_alias(DynamicallyConfiguredMatcherFactoryDecorator::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\DynamicallyConfiguredMatcherFactoryDecorator'); diff --git a/src/lib/MVC/Symfony/Matcher/MatcherFactoryInterface.php b/src/lib/MVC/Symfony/Matcher/MatcherFactoryInterface.php new file mode 100644 index 0000000000..b51e0d295e --- /dev/null +++ b/src/lib/MVC/Symfony/Matcher/MatcherFactoryInterface.php @@ -0,0 +1,26 @@ +requestStack->getCurrentRequest(); } } + +class_alias(RequestStackAware::class, 'eZ\Publish\Core\MVC\Symfony\RequestStackAware'); diff --git a/src/lib/MVC/Symfony/Routing/ChainRouter.php b/src/lib/MVC/Symfony/Routing/ChainRouter.php new file mode 100644 index 0000000000..1951c796b7 --- /dev/null +++ b/src/lib/MVC/Symfony/Routing/ChainRouter.php @@ -0,0 +1,17 @@ +getRouteReference(); } } + +class_alias(RouteReferenceGenerator::class, 'eZ\Publish\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator'); diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/Generator/RouteReferenceGeneratorInterface.php b/src/lib/MVC/Symfony/Routing/Generator/RouteReferenceGeneratorInterface.php similarity index 76% rename from eZ/Publish/Core/MVC/Symfony/Routing/Generator/RouteReferenceGeneratorInterface.php rename to src/lib/MVC/Symfony/Routing/Generator/RouteReferenceGeneratorInterface.php index 8f15b8c4d6..dc4c956c15 100644 --- a/eZ/Publish/Core/MVC/Symfony/Routing/Generator/RouteReferenceGeneratorInterface.php +++ b/src/lib/MVC/Symfony/Routing/Generator/RouteReferenceGeneratorInterface.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Routing\Generator; +namespace Ibexa\Core\MVC\Symfony\Routing\Generator; /** * Interface for RouteReference generators. @@ -18,7 +18,9 @@ interface RouteReferenceGeneratorInterface * @param mixed $resource The route name. Can be any resource supported by the different routers (e.g. Location object). * @param array $params Array of parameters, used to generate the final link along with $resource. * - * @return \eZ\Publish\Core\MVC\Symfony\Routing\RouteReference + * @return \Ibexa\Core\MVC\Symfony\Routing\RouteReference */ public function generate($resource = null, array $params = []); } + +class_alias(RouteReferenceGeneratorInterface::class, 'eZ\Publish\Core\MVC\Symfony\Routing\Generator\RouteReferenceGeneratorInterface'); diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php b/src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php similarity index 85% rename from eZ/Publish/Core/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php rename to src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php index 52428fa2ac..3da4acdd54 100644 --- a/eZ/Publish/Core/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php +++ b/src/lib/MVC/Symfony/Routing/Generator/UrlAliasGenerator.php @@ -4,24 +4,24 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Routing\Generator; +namespace Ibexa\Core\MVC\Symfony\Routing\Generator; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Routing\Generator; use Symfony\Component\Routing\RouterInterface; /** * URL generator for UrlAlias based links. * - * @see \eZ\Publish\Core\MVC\Symfony\Routing\UrlAliasRouter + * @see \Ibexa\Core\MVC\Symfony\Routing\UrlAliasRouter */ class UrlAliasGenerator extends Generator { - public const INTERNAL_CONTENT_VIEW_ROUTE = '_ez_content_view'; + public const INTERNAL_CONTENT_VIEW_ROUTE = 'ibexa.content.view'; - /** @var \eZ\Publish\Core\Repository\Repository */ + /** @var \Ibexa\Core\Repository\Repository */ private $repository; /** @@ -40,7 +40,7 @@ class UrlAliasGenerator extends Generator /** @var array */ private $pathPrefixMap = []; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; /** @@ -63,7 +63,7 @@ public function __construct(Repository $repository, RouterInterface $defaultRout * Generates the URL from $urlResource and $parameters. * Entries in $parameters will be added in the query string. * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location * @param array $parameters * * @return string @@ -103,10 +103,12 @@ public function setExcludedUriPrefixes(array $excludedUriPrefixes) * Returns path corresponding to $rootLocationId. * * @param int $rootLocationId - * @param array $languages + * @param array|null $languages * @param string $siteaccess * * @return string + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function getPathPrefixByRootLocationId($rootLocationId, $languages = null, $siteaccess = null) { @@ -122,7 +124,7 @@ public function getPathPrefixByRootLocationId($rootLocationId, $languages = null $this->pathPrefixMap[$siteaccess][$rootLocationId] = $this->repository ->getURLAliasService() ->reverseLookup( - $this->loadLocation($rootLocationId), + $this->loadLocation($rootLocationId, $languages), null, false, $languages @@ -157,21 +159,22 @@ public function isUriPrefixExcluded($uri) * Not to be used for link generation. * * @param int $locationId + * @param array|null $languages * - * @return \eZ\Publish\Core\Repository\Values\Content\Location + * @return \Ibexa\Core\Repository\Values\Content\Location */ - public function loadLocation($locationId) + public function loadLocation($locationId, ?array $languages = null) { return $this->repository->sudo( - static function (Repository $repository) use ($locationId) { - /* @var $repository \eZ\Publish\Core\Repository\Repository */ - return $repository->getLocationService()->loadLocation($locationId); + static function (Repository $repository) use ($locationId, $languages) { + /* @var $repository \Ibexa\Core\Repository\Repository */ + return $repository->getLocationService()->loadLocation($locationId, $languages); } ); } /** - * @param \eZ\Publish\API\Repository\Values\Content\Location $location + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location * @param string|null $siteAccess * * @return string @@ -257,3 +260,5 @@ private function filterCharactersOfURL(string $url): string return strtr($url, $this->unsafeCharMap); } } + +class_alias(UrlAliasGenerator::class, 'eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator'); diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/RouteReference.php b/src/lib/MVC/Symfony/Routing/RouteReference.php similarity index 93% rename from eZ/Publish/Core/MVC/Symfony/Routing/RouteReference.php rename to src/lib/MVC/Symfony/Routing/RouteReference.php index c831ebdd18..dca38f7f8e 100644 --- a/eZ/Publish/Core/MVC/Symfony/Routing/RouteReference.php +++ b/src/lib/MVC/Symfony/Routing/RouteReference.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Routing; +namespace Ibexa\Core\MVC\Symfony\Routing; use Symfony\Component\HttpFoundation\ParameterBag; @@ -86,3 +86,5 @@ public function remove($parameterName) $this->params->remove($parameterName); } } + +class_alias(RouteReference::class, 'eZ\Publish\Core\MVC\Symfony\Routing\RouteReference'); diff --git a/src/lib/MVC/Symfony/Routing/SimplifiedRequest.php b/src/lib/MVC/Symfony/Routing/SimplifiedRequest.php new file mode 100644 index 0000000000..602f50bf16 --- /dev/null +++ b/src/lib/MVC/Symfony/Routing/SimplifiedRequest.php @@ -0,0 +1,221 @@ +headers = $headers; + } + + /** + * @param string $host + */ + public function setHost($host) + { + $this->host = $host; + } + + /** + * @param array $languages + */ + public function setLanguages(array $languages) + { + $this->languages = $languages; + } + + /** + * @param string $pathinfo + */ + public function setPathinfo($pathinfo) + { + $this->pathinfo = $pathinfo; + } + + /** + * @param string $port + */ + public function setPort($port) + { + $this->port = $port; + } + + /** + * @param array $queryParams + */ + public function setQueryParams(array $queryParams) + { + $this->queryParams = $queryParams; + } + + /** + * @param string $scheme + */ + public function setScheme($scheme) + { + $this->scheme = $scheme; + } + + /** + * Constructs a SimplifiedRequest object from a standard URL (http://www.example.com/foo/bar?queryParam=value). + * + * @param string $url + * + * @internal + * + * @return \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest + */ + public static function fromUrl($url) + { + $elements = parse_url($url); + $elements['pathinfo'] = isset($elements['path']) ? $elements['path'] : ''; + + if (isset($elements['query'])) { + parse_str($elements['query'], $queryParams); + $elements['queryParams'] = $queryParams; + } + + // Remove unwanted keys returned by parse_url() so that we don't have them as properties. + unset($elements['path'], $elements['query'], $elements['user'], $elements['pass'], $elements['fragment']); + + return new static($elements); + } + + public function __sleep() + { + // Clean up headers for serialization not have a too heavy string (i.e. for ESI/Hinclude tags). + $this->headers = []; + + return ['scheme', 'host', 'port', 'pathinfo', 'queryParams', 'languages', 'headers']; + } + + /** + * The request scheme - http or https. + */ + public function getScheme(): string + { + return $this->scheme; + } + + public function getHost(): string + { + return $this->host; + } + + public function getPort(): string + { + return $this->port; + } + + /** + * The path being requested relative to the executed script. + */ + public function getPathInfo(): string + { + return $this->pathinfo; + } + + /** + * @return array + */ + public function getQueryParams(): array + { + return $this->queryParams; + } + + /** + * @return string[] + */ + public function getLanguages(): array + { + return $this->languages; + } + + /** + * @return array|null + */ + public function getHeader(string $headerName): ?array + { + return $this->headers[$headerName] ?? null; + } + + /** + * @return array> + */ + public function getHeaders(): array + { + return $this->headers; + } +} + +class_alias(SimplifiedRequest::class, 'eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest'); diff --git a/src/lib/MVC/Symfony/Routing/UrlAliasRouter.php b/src/lib/MVC/Symfony/Routing/UrlAliasRouter.php new file mode 100644 index 0000000000..e65c821831 --- /dev/null +++ b/src/lib/MVC/Symfony/Routing/UrlAliasRouter.php @@ -0,0 +1,407 @@ +locationService = $locationService; + $this->urlAliasService = $urlAliasService; + $this->contentService = $contentService; + $this->generator = $generator; + $this->requestContext = $requestContext !== null ? $requestContext : new RequestContext(); + $this->logger = $logger; + } + + /** + * Injects current root Location id. + * + * @param int|string $rootLocationId + */ + public function setRootLocationId($rootLocationId) + { + $this->rootLocationId = $rootLocationId; + } + + /** + * Tries to match a request with a set of routes. + * + * If the matcher can not find information, it must throw one of the exceptions documented + * below. + * + * @param \Symfony\Component\HttpFoundation\Request $request The request to match + * + * @return array An array of parameters + * + * @throws \Symfony\Component\Routing\Exception\ResourceNotFoundException If no matching resource could be found + */ + public function matchRequest(Request $request) + { + try { + $requestedPath = $request->attributes->get('semanticPathinfo', $request->getPathInfo()); + $urlAlias = $this->getUrlAlias($requestedPath); + if ($this->rootLocationId === null) { + $pathPrefix = '/'; + } else { + $pathPrefix = $this->generator->getPathPrefixByRootLocationId($this->rootLocationId); + } + + $params = [ + '_route' => static::URL_ALIAS_ROUTE_NAME, + ]; + switch ($urlAlias->type) { + case URLAlias::LOCATION: + $location = $this->generator->loadLocation($urlAlias->destination); + $params += [ + '_controller' => static::VIEW_ACTION, + 'contentId' => $location->contentId, + 'locationId' => $urlAlias->destination, + 'viewType' => ViewManager::VIEW_TYPE_FULL, + 'layout' => true, + ]; + + // For Location alias setup 301 redirect to Location's current URL when: + // 1. alias is history + // 2. alias is custom with forward flag true + // 3. requested URL is not case-sensitive equal with the one loaded + if ($urlAlias->isHistory === true || ($urlAlias->isCustom === true && $urlAlias->forward === true)) { + $params += [ + 'semanticPathinfo' => $this->generator->generate($location, []), + 'needsRedirect' => true, + // Specify not to prepend siteaccess while redirecting when applicable since it would be already present (see UrlAliasGenerator::doGenerate()) + 'prependSiteaccessOnRedirect' => false, + ]; + } elseif ($this->needsCaseRedirect($urlAlias, $requestedPath, $pathPrefix)) { + $params += [ + 'semanticPathinfo' => $this->removePathPrefix($urlAlias->path, $pathPrefix), + 'needsRedirect' => true, + ]; + + if ($urlAlias->destination instanceof Location) { + $params += ['locationId' => $urlAlias->destination->id]; + } + } + + if (isset($this->logger)) { + $this->logger->info("UrlAlias matched location #{$urlAlias->destination}. Forwarding to ViewController"); + } + + break; + + case URLAlias::RESOURCE: + // In URLAlias terms, "forward" means "redirect". + if ($urlAlias->forward) { + $params += [ + 'semanticPathinfo' => '/' . trim($urlAlias->destination, '/'), + 'needsRedirect' => true, + ]; + } elseif ($this->needsCaseRedirect($urlAlias, $requestedPath, $pathPrefix)) { + // Handle case-correction redirect + $params += [ + 'semanticPathinfo' => $this->removePathPrefix($urlAlias->path, $pathPrefix), + 'needsRedirect' => true, + ]; + } else { + $params += [ + 'semanticPathinfo' => '/' . trim($urlAlias->destination, '/'), + 'needsForward' => true, + ]; + } + + break; + + case URLAlias::VIRTUAL: + // Handle case-correction redirect + if ($this->needsCaseRedirect($urlAlias, $requestedPath, $pathPrefix)) { + $params += [ + 'semanticPathinfo' => $this->removePathPrefix($urlAlias->path, $pathPrefix), + 'needsRedirect' => true, + ]; + } else { + // Virtual aliases should load the Content at homepage URL + $params += [ + 'semanticPathinfo' => '/', + 'needsForward' => true, + ]; + } + + break; + } + + return $params; + } catch (NotFoundException $e) { + throw new ResourceNotFoundException($e->getMessage(), $e->getCode(), $e); + } + } + + /** + * Removes prefix from path. + * + * Checks for presence of $prefix and removes it from $path if found. + * + * @param string $path + * @param string $prefix + * + * @return string + */ + protected function removePathPrefix($path, $prefix) + { + if ($prefix !== '/' && mb_stripos($path, $prefix) === 0) { + $path = mb_substr($path, mb_strlen($prefix)); + } + + return $path; + } + + /** + * Returns true of false on comparing $urlAlias->path and $path with case sensitivity. + * + * Used to determine if redirect is needed because requested path is case-different + * from the stored one. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias $loadedUrlAlias + * @param string $requestedPath + * @param string $pathPrefix + * + * @return bool + */ + protected function needsCaseRedirect(URLAlias $loadedUrlAlias, $requestedPath, $pathPrefix) + { + // If requested path is excluded from tree root jail, compare it to loaded UrlAlias directly. + if ($this->generator->isUriPrefixExcluded($requestedPath)) { + return strcmp($loadedUrlAlias->path, $requestedPath) !== 0; + } + + // Compare loaded UrlAlias with requested path, prefixed with configured path prefix. + return + strcmp( + $loadedUrlAlias->path, + $pathPrefix . ($pathPrefix === '/' ? trim($requestedPath, '/') : rtrim($requestedPath, '/')) + ) !== 0 + ; + } + + /** + * Returns the UrlAlias object to use, starting from the request. + * + * @param $pathinfo + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the path does not exist or is not valid for the given language + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + */ + protected function getUrlAlias($pathinfo) + { + return $this->urlAliasService->lookup($pathinfo); + } + + /** + * Gets the RouteCollection instance associated with this Router. + * + * @return \Symfony\Component\Routing\RouteCollection A RouteCollection instance + */ + public function getRouteCollection() + { + return new RouteCollection(); + } + + /** + * Generates a URL for a location, from the given parameters. + * + * It is possible to directly pass a Location object as the route name, as the ChainRouter allows it through ChainedRouterInterface. + * + * If $name is a route name, the "location" key in $parameters must be set to a valid {@see \Ibexa\Contracts\Core\Repository\Values\Content\Location} object. + * "locationId" can also be provided. + * + * If the generator is not able to generate the url, it must throw the RouteNotFoundException + * as documented below. + * + * @see UrlAliasRouter::supports() + * + * @param string $name The name of the route or a Location instance + * @param array $parameters An array of parameters + * @param int $referenceType The type of reference to be generated (one of the constants) + * + * @throws \LogicException + * @throws \Symfony\Component\Routing\Exception\RouteNotFoundException + * @throws \InvalidArgumentException + * + * @return string The generated URL + * + * @api + */ + public function generate(string $name, array $parameters = [], int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string + { + if ($name === '' && + array_key_exists(RouteObjectInterface::ROUTE_OBJECT, $parameters) && + $this->supportsObject($parameters[RouteObjectInterface::ROUTE_OBJECT]) + ) { + $location = $parameters[RouteObjectInterface::ROUTE_OBJECT]; + unset($parameters[RouteObjectInterface::ROUTE_OBJECT]); + + return $this->generator->generate($location, $parameters, $referenceType); + } + + // Normal route name + if ($name === static::URL_ALIAS_ROUTE_NAME) { + if (isset($parameters['location']) || isset($parameters['locationId'])) { + // Check if location is a valid Location object + if (isset($parameters['location']) && !$parameters['location'] instanceof Location) { + throw new LogicException( + "When generating a UrlAlias route, the 'location' parameter must be a valid " . Location::class . '.' + ); + } + + $location = isset($parameters['location']) ? $parameters['location'] : $this->locationService->loadLocation($parameters['locationId']); + unset($parameters['location'], $parameters['locationId'], $parameters['viewType'], $parameters['layout']); + + return $this->generator->generate($location, $parameters, $referenceType); + } + + if (isset($parameters['contentId'])) { + $contentInfo = $this->contentService->loadContentInfo($parameters['contentId']); + unset($parameters['contentId'], $parameters['viewType'], $parameters['layout']); + + if (empty($contentInfo->mainLocationId)) { + throw new LogicException('Cannot generate a UrlAlias route for content without main Location.'); + } + + return $this->generator->generate( + $this->locationService->loadLocation($contentInfo->mainLocationId), + $parameters, + $referenceType + ); + } + + throw new InvalidArgumentException( + "When generating a UrlAlias route, either 'location', 'locationId', or 'contentId' must be provided." + ); + } + + throw new RouteNotFoundException('Could not match route'); + } + + public function setContext(RequestContext $context) + { + $this->requestContext = $context; + $this->generator->setRequestContext($context); + } + + public function getContext() + { + return $this->requestContext; + } + + /** + * Not supported. Please use matchRequest() instead. + * + * @param $pathinfo + * + * @throws \RuntimeException + */ + public function match($pathinfo) + { + throw new \RuntimeException("The UrlAliasRouter doesn't support the match() method. Use matchRequest() instead."); + } + + /** + * Whether the router supports the thing in $name to generate a route. + * + * This check does not need to look if the specific instance can be + * resolved to a route, only whether the router can generate routes from + * objects of this class. + * + * @param mixed $name The route name or route object + * + * @return bool + */ + public function supports($name) + { + return $name === static::URL_ALIAS_ROUTE_NAME || $this->supportsObject($name); + } + + private function supportsObject($object): bool + { + return $object instanceof Location; + } + + /** + * @see \Symfony\Cmf\Component\Routing\VersatileGeneratorInterface::getRouteDebugMessage() + */ + public function getRouteDebugMessage($name, array $parameters = []) + { + if ($name instanceof RouteObjectInterface) { + return 'Route with key ' . $name->getRouteKey(); + } + + if ($name instanceof SymfonyRoute) { + return 'Route with pattern ' . $name->getPath(); + } + + return $name; + } +} + +class_alias(UrlAliasRouter::class, 'eZ\Publish\Core\MVC\Symfony\Routing\UrlAliasRouter'); diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/UrlWildcardRouter.php b/src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php similarity index 86% rename from eZ/Publish/Core/MVC/Symfony/Routing/UrlWildcardRouter.php rename to src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php index dacadc7d7c..4b83a27ca7 100644 --- a/eZ/Publish/Core/MVC/Symfony/Routing/UrlWildcardRouter.php +++ b/src/lib/MVC/Symfony/Routing/UrlWildcardRouter.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Routing; +namespace Ibexa\Core\MVC\Symfony\Routing; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\URLWildcardService; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\URLWildcardService; +use Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator; use Symfony\Cmf\Component\Routing\ChainedRouterInterface; use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; @@ -21,12 +21,12 @@ class UrlWildcardRouter implements ChainedRouterInterface, RequestMatcherInterface { - public const URL_ALIAS_ROUTE_NAME = 'ez_urlalias'; + public const URL_ALIAS_ROUTE_NAME = 'ibexa.url.alias'; - /** @var \eZ\Publish\API\Repository\URLWildcardService */ + /** @var \Ibexa\Contracts\Core\Repository\URLWildcardService */ private $wildcardService; - /** @var \eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator */ + /** @var \Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator */ private $generator; /** @var \Symfony\Component\Routing\RequestContext */ @@ -36,8 +36,8 @@ class UrlWildcardRouter implements ChainedRouterInterface, RequestMatcherInterfa private $logger; /** - * @param \eZ\Publish\API\Repository\URLWildcardService $wildcardService - * @param \eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator $generator + * @param \Ibexa\Contracts\Core\Repository\URLWildcardService $wildcardService + * @param \Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator $generator * @param \Symfony\Component\Routing\RequestContext $requestContext */ public function __construct( @@ -151,7 +151,7 @@ public function match($pathinfo): array */ public function supports($name): bool { - return $name === self::URL_ALIAS_ROUTE_NAME; + return $name === static::URL_ALIAS_ROUTE_NAME; } /** @@ -170,3 +170,5 @@ public function getRouteDebugMessage($name, array $parameters = []): string return $name; } } + +class_alias(UrlWildcardRouter::class, 'eZ\Publish\Core\MVC\Symfony\Routing\UrlWildcardRouter'); diff --git a/src/lib/MVC/Symfony/Security/Authentication/AnonymousAuthenticationProvider.php b/src/lib/MVC/Symfony/Security/Authentication/AnonymousAuthenticationProvider.php new file mode 100644 index 0000000000..eb50be4155 --- /dev/null +++ b/src/lib/MVC/Symfony/Security/Authentication/AnonymousAuthenticationProvider.php @@ -0,0 +1,42 @@ +configResolver = $configResolver; + } + + public function setPermissionResolver(PermissionResolver $permissionResolver) + { + $this->permissionResolver = $permissionResolver; + } + + public function authenticate(TokenInterface $token) + { + $token = parent::authenticate($token); + $this->permissionResolver->setCurrentUserReference(new UserReference($this->configResolver->getParameter('anonymous_user_id'))); + + return $token; + } +} + +class_alias(AnonymousAuthenticationProvider::class, 'eZ\Publish\Core\MVC\Symfony\Security\Authentication\AnonymousAuthenticationProvider'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Authentication/AuthenticatorInterface.php b/src/lib/MVC/Symfony/Security/Authentication/AuthenticatorInterface.php similarity index 79% rename from eZ/Publish/Core/MVC/Symfony/Security/Authentication/AuthenticatorInterface.php rename to src/lib/MVC/Symfony/Security/Authentication/AuthenticatorInterface.php index 72a9066da7..fb52589a3e 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Authentication/AuthenticatorInterface.php +++ b/src/lib/MVC/Symfony/Security/Authentication/AuthenticatorInterface.php @@ -4,13 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Authentication; +namespace Ibexa\Core\MVC\Symfony\Security\Authentication; use Symfony\Component\HttpFoundation\Request; /** * This interface is to be implemented by authenticator classes. * Authenticators are meant to be used to run authentication programmatically, i.e. outside the firewall context. + * + * @deprecated 4.6.7 this class is deprecated. Symfony Security has received major changes in 5.3, therefore Ibexa DXP relies on authenticator system from now on. Will be removed in 5.0. */ interface AuthenticatorInterface { @@ -40,3 +42,5 @@ public function authenticate(Request $request); */ public function logout(Request $request); } + +class_alias(AuthenticatorInterface::class, 'eZ\Publish\Core\MVC\Symfony\Security\Authentication\AuthenticatorInterface'); diff --git a/src/lib/MVC/Symfony/Security/Authentication/DefaultAuthenticationSuccessHandler.php b/src/lib/MVC/Symfony/Security/Authentication/DefaultAuthenticationSuccessHandler.php new file mode 100644 index 0000000000..bbd684225a --- /dev/null +++ b/src/lib/MVC/Symfony/Security/Authentication/DefaultAuthenticationSuccessHandler.php @@ -0,0 +1,53 @@ +configResolver = $configResolver; + } + + public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): void + { + $this->eventDispatcher = $eventDispatcher; + } + + protected function determineTargetUrl(Request $request) + { + if (isset($this->configResolver)) { + $defaultPage = $this->configResolver->getParameter('default_page'); + if ($defaultPage !== null) { + $this->options['default_target_path'] = $defaultPage; + } + } + + if (isset($this->eventDispatcher)) { + $event = new DetermineTargetUrlEvent($request, $this->options, $this->getFirewallName()); + $this->eventDispatcher->dispatch($event); + + $this->options = $event->getOptions(); + } + + return parent::determineTargetUrl($request); + } +} + +class_alias(DefaultAuthenticationSuccessHandler::class, 'eZ\Publish\Core\MVC\Symfony\Security\Authentication\DefaultAuthenticationSuccessHandler'); diff --git a/src/lib/MVC/Symfony/Security/Authentication/DetermineTargetUrlEvent.php b/src/lib/MVC/Symfony/Security/Authentication/DetermineTargetUrlEvent.php new file mode 100644 index 0000000000..9842522c98 --- /dev/null +++ b/src/lib/MVC/Symfony/Security/Authentication/DetermineTargetUrlEvent.php @@ -0,0 +1,57 @@ + */ + private array $options; + + /** + * @param array $options + **/ + public function __construct( + Request $request, + array $options, + string $firewallName + ) { + $this->request = $request; + $this->firewallName = $firewallName; + $this->options = $options; + } + + public function getRequest(): Request + { + return $this->request; + } + + public function getFirewallName(): string + { + return $this->firewallName; + } + + /** @return array */ + public function getOptions(): array + { + return $this->options; + } + + /** @param array $options */ + public function setOptions(array $options): void + { + $this->options = $options; + } +} diff --git a/src/lib/MVC/Symfony/Security/Authentication/GuardRepositoryAuthenticationProvider.php b/src/lib/MVC/Symfony/Security/Authentication/GuardRepositoryAuthenticationProvider.php new file mode 100644 index 0000000000..bcbe7b7c3d --- /dev/null +++ b/src/lib/MVC/Symfony/Security/Authentication/GuardRepositoryAuthenticationProvider.php @@ -0,0 +1,41 @@ +permissionResolver = $permissionResolver; + } + + public function authenticate(TokenInterface $token): TokenInterface + { + $authenticatedToken = parent::authenticate($token); + if (empty($authenticatedToken)) { + throw new AuthenticationException('The token is not supported by this authentication provider.'); + } + + if ($authenticatedToken->getUser() instanceof UserInterface) { + $this->permissionResolver->setCurrentUserReference( + $authenticatedToken->getUser()->getAPIUser() + ); + } + + return $authenticatedToken; + } +} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProvider.php b/src/lib/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProvider.php similarity index 76% rename from eZ/Publish/Core/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProvider.php rename to src/lib/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProvider.php index d0839e0963..f40ef6a1e0 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProvider.php +++ b/src/lib/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProvider.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Authentication; +namespace Ibexa\Core\MVC\Symfony\Security\Authentication; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\Core\MVC\Symfony\Security\UserInterface; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Core\MVC\Symfony\Security\UserInterface; use Symfony\Component\Security\Core\Authentication\Provider\RememberMeAuthenticationProvider; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; class RememberMeRepositoryAuthenticationProvider extends RememberMeAuthenticationProvider { - /** @var \eZ\Publish\API\Repository\PermissionResolver */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ private $permissionResolver; public function setPermissionResolver(PermissionResolver $permissionResolver) @@ -41,3 +41,5 @@ public function authenticate(TokenInterface $token) return $authenticatedToken; } } + +class_alias(RememberMeRepositoryAuthenticationProvider::class, 'eZ\Publish\Core\MVC\Symfony\Security\Authentication\RememberMeRepositoryAuthenticationProvider'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProvider.php b/src/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProvider.php similarity index 79% rename from eZ/Publish/Core/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProvider.php rename to src/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProvider.php index 2fb492c6f2..cd50a94366 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProvider.php +++ b/src/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProvider.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Authentication; - -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\SecurityPass; -use eZ\Publish\API\Repository\Exceptions\PasswordInUnsupportedFormatException; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\Core\MVC\Symfony\Security\UserInterface as EzUserInterface; -use eZ\Publish\Core\Repository\User\Exception\UnsupportedPasswordHashType; +namespace Ibexa\Core\MVC\Symfony\Security\Authentication; + +use Ibexa\Bundle\Core\DependencyInjection\Compiler\SecurityPass; +use Ibexa\Contracts\Core\Repository\Exceptions\PasswordInUnsupportedFormatException; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Core\MVC\Symfony\Security\UserInterface as IbexaUserInterface; +use Ibexa\Core\Repository\User\Exception\UnsupportedPasswordHashType; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use Symfony\Component\Security\Core\Authentication\Provider\DaoAuthenticationProvider; @@ -29,10 +29,10 @@ class RepositoryAuthenticationProvider extends DaoAuthenticationProvider impleme /** @var float|null */ private $constantAuthTime; - /** @var \eZ\Publish\API\Repository\PermissionResolver */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ private $permissionResolver; - /** @var \eZ\Publish\API\Repository\UserService */ + /** @var \Ibexa\Contracts\Core\Repository\UserService */ private $userService; public function setConstantAuthTime(float $constantAuthTime) @@ -52,7 +52,7 @@ public function setUserService(UserService $userService) protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token) { - if (!$user instanceof EzUserInterface) { + if (!$user instanceof IbexaUserInterface) { parent::checkAuthentication($user, $token); return; @@ -61,7 +61,7 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke $apiUser = $user->getAPIUser(); // $currentUser can either be an instance of UserInterface or just the username (e.g. during form login). - /** @var \eZ\Publish\Core\MVC\Symfony\Security\UserInterface|string $currentUser */ + /** @var \Ibexa\Core\MVC\Symfony\Security\UserInterface|string $currentUser */ $currentUser = $token->getUser(); if ($currentUser instanceof UserInterface) { if ($currentUser->getAPIUser()->passwordHash !== $apiUser->passwordHash) { @@ -82,7 +82,7 @@ protected function checkAuthentication(UserInterface $user, UsernamePasswordToke } /** - * @throws \eZ\Publish\API\Repository\Exceptions\PasswordInUnsupportedFormatException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\PasswordInUnsupportedFormatException */ public function authenticate(TokenInterface $token) { @@ -130,3 +130,5 @@ private function sleepUsingConstantTimer(float $startTime): void } } } + +class_alias(RepositoryAuthenticationProvider::class, 'eZ\Publish\Core\MVC\Symfony\Security\Authentication\RepositoryAuthenticationProvider'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Authorization/Attribute.php b/src/lib/MVC/Symfony/Security/Authorization/Attribute.php similarity index 87% rename from eZ/Publish/Core/MVC/Symfony/Security/Authorization/Attribute.php rename to src/lib/MVC/Symfony/Security/Authorization/Attribute.php index 5c080f29d9..e63bf61bbd 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Authorization/Attribute.php +++ b/src/lib/MVC/Symfony/Security/Authorization/Attribute.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Authorization; +namespace Ibexa\Core\MVC\Symfony\Security\Authorization; /** * Authorization attribute class to be used with SecurityContextInterface::isGranted(). @@ -18,7 +18,7 @@ * * Usage example: * - * use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute as AuthorizationAttribute; + * use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute as AuthorizationAttribute; * * // From inside a controller * // Will check if current user can assign a content to a section, $section being a Section value object. @@ -55,3 +55,5 @@ public function __toString() return "EZ_ROLE_{$this->module}_{$this->function}"; } } + +class_alias(Attribute::class, 'eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Authorization/Voter/CoreVoter.php b/src/lib/MVC/Symfony/Security/Authorization/Voter/CoreVoter.php similarity index 85% rename from eZ/Publish/Core/MVC/Symfony/Security/Authorization/Voter/CoreVoter.php rename to src/lib/MVC/Symfony/Security/Authorization/Voter/CoreVoter.php index b149bb7452..fe508515ed 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Authorization/Voter/CoreVoter.php +++ b/src/lib/MVC/Symfony/Security/Authorization/Voter/CoreVoter.php @@ -4,16 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Authorization\Voter; +namespace Ibexa\Core\MVC\Symfony\Security\Authorization\Voter; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute as AuthorizationAttribute; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute as AuthorizationAttribute; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; class CoreVoter implements VoterInterface { - /** @var \eZ\Publish\API\Repository\PermissionResolver */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ private $permissionResolver; public function __construct(PermissionResolver $permissionResolver) @@ -72,3 +72,5 @@ public function vote(TokenInterface $token, $object, array $attributes) return VoterInterface::ACCESS_ABSTAIN; } } + +class_alias(CoreVoter::class, 'eZ\Publish\Core\MVC\Symfony\Security\Authorization\Voter\CoreVoter'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Authorization/Voter/ValueObjectVoter.php b/src/lib/MVC/Symfony/Security/Authorization/Voter/ValueObjectVoter.php similarity index 81% rename from eZ/Publish/Core/MVC/Symfony/Security/Authorization/Voter/ValueObjectVoter.php rename to src/lib/MVC/Symfony/Security/Authorization/Voter/ValueObjectVoter.php index bd8654bb79..2674cb8271 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Authorization/Voter/ValueObjectVoter.php +++ b/src/lib/MVC/Symfony/Security/Authorization/Voter/ValueObjectVoter.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Authorization\Voter; +namespace Ibexa\Core\MVC\Symfony\Security\Authorization\Voter; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute as AuthorizationAttribute; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute as AuthorizationAttribute; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; @@ -16,7 +16,7 @@ */ class ValueObjectVoter implements VoterInterface { - /** @var \eZ\Publish\API\Repository\PermissionResolver */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ private $permissionResolver; public function __construct(PermissionResolver $permissionResolver) @@ -39,13 +39,13 @@ public function supportsClass($class) * Checks if user has access to a given action on a given value object. * * $attributes->limitations is a hash that contains: - * - 'valueObject' - The ValueObject to check access on (eZ\Publish\API\Repository\Values\ValueObject). e.g. Location or Content. + * - 'valueObject' - The {@see \Ibexa\Contracts\Core\Repository\Values\ValueObject} to check access on. e.g. Location or Content. * - 'targets' - The location, parent or "assignment" value object, or an array of the same. * * This method must return one of the following constants: * ACCESS_GRANTED, ACCESS_DENIED, or ACCESS_ABSTAIN. * - * @see \eZ\Publish\API\Repository\PermissionResolver::canUser() + * @see \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser() * * @param \Symfony\Component\Security\Core\Authentication\Token\TokenInterface $token A TokenInterface instance * @param object $object The object to secure @@ -76,3 +76,5 @@ public function vote(TokenInterface $token, $object, array $attributes) return VoterInterface::ACCESS_ABSTAIN; } } + +class_alias(ValueObjectVoter::class, 'eZ\Publish\Core\MVC\Symfony\Security\Authorization\Voter\ValueObjectVoter'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/EventListener/SecurityListener.php b/src/lib/MVC/Symfony/Security/EventListener/SecurityListener.php similarity index 78% rename from eZ/Publish/Core/MVC/Symfony/Security/EventListener/SecurityListener.php rename to src/lib/MVC/Symfony/Security/EventListener/SecurityListener.php index 835aa59703..b836339099 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/EventListener/SecurityListener.php +++ b/src/lib/MVC/Symfony/Security/EventListener/SecurityListener.php @@ -4,20 +4,20 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\EventListener; - -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Event\InteractiveLoginEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute; -use eZ\Publish\Core\MVC\Symfony\Security\Exception\UnauthorizedSiteAccessException; -use eZ\Publish\Core\MVC\Symfony\Security\InteractiveLoginToken; -use eZ\Publish\Core\MVC\Symfony\Security\UserInterface as eZUser; -use eZ\Publish\Core\MVC\Symfony\Security\UserWrapped; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +namespace Ibexa\Core\MVC\Symfony\Security\EventListener; + +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\User\User as APIUser; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Event\InteractiveLoginEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute; +use Ibexa\Core\MVC\Symfony\Security\Exception\UnauthorizedSiteAccessException; +use Ibexa\Core\MVC\Symfony\Security\InteractiveLoginToken; +use Ibexa\Core\MVC\Symfony\Security\UserInterface as IbexaUser; +use Ibexa\Core\MVC\Symfony\Security\UserWrapped; +use Ibexa\Core\MVC\Symfony\SiteAccess; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; @@ -32,7 +32,7 @@ /** * This security listener listens to security.interactive_login event to: - * - Give a chance to retrieve an eZ user when using multiple user providers + * - Give a chance to retrieve an Ibexa user when using multiple user providers * - Check if user can actually login to the current SiteAccess. * * Also listens to kernel.request to: @@ -40,13 +40,13 @@ */ class SecurityListener implements EventSubscriberInterface { - /** @var \eZ\Publish\API\Repository\PermissionResolver */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ protected $permissionResolver; - /** @var \eZ\Publish\API\Repository\UserService */ + /** @var \Ibexa\Contracts\Core\Repository\UserService */ protected $userService; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ protected $configResolver; /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ @@ -96,8 +96,8 @@ public static function getSubscribedEvents() } /** - * Tries to retrieve a valid eZ user if authenticated user doesn't come from the repository (foreign user provider). - * Will dispatch an event allowing listeners to return a valid eZ user for current authenticated user. + * Tries to retrieve a valid Ibexa user if authenticated user doesn't come from the repository (foreign user provider). + * Will dispatch an event allowing listeners to return a valid Ibexa user for current authenticated user. * Will by default let the repository load the anonymous user. * * @param \Symfony\Component\Security\Http\Event\InteractiveLoginEvent $event @@ -106,15 +106,15 @@ public function onInteractiveLogin(BaseInteractiveLoginEvent $event) { $token = $event->getAuthenticationToken(); $originalUser = $token->getUser(); - if ($originalUser instanceof eZUser || !$originalUser instanceof UserInterface) { + if ($originalUser instanceof IbexaUser || !$originalUser instanceof UserInterface) { return; } /* * 1. Send the event. - * 2. If no eZ user is returned, load Anonymous user. - * 3. Inject eZ user in repository. - * 4. Create the UserWrapped user object (implementing eZ UserInterface) with loaded eZ user. + * 2. If no Ibexa user is returned, load Anonymous user. + * 3. Inject Ibexa user in repository. + * 4. Create the UserWrapped user object (implementing Ibexa UserInterface) with loaded Ibexa user. * 5. Create new token with UserWrapped user * 6. Inject the new token in security context */ @@ -139,6 +139,7 @@ public function onInteractiveLogin(BaseInteractiveLoginEvent $event) $providerKey, $token->getRoleNames() ); + $interactiveToken->setOriginalToken($token); $interactiveToken->setAttributes($token->getAttributes()); $this->tokenStorage->setToken($interactiveToken); } @@ -148,9 +149,9 @@ public function onInteractiveLogin(BaseInteractiveLoginEvent $event) * One may want to override this method to use their own user class. * * @param \Symfony\Component\Security\Core\User\UserInterface $originalUser - * @param \eZ\Publish\API\Repository\Values\User\User $apiUser + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $apiUser * - * @return \eZ\Publish\Core\MVC\Symfony\Security\UserInterface + * @return \Ibexa\Core\MVC\Symfony\Security\UserInterface */ protected function getUser(UserInterface $originalUser, APIUser $apiUser) { @@ -162,7 +163,7 @@ protected function getUser(UserInterface $originalUser, APIUser $apiUser) * * @param \Symfony\Component\Security\Http\Event\InteractiveLoginEvent $event * - * @throws \eZ\Publish\Core\MVC\Symfony\Security\Exception\UnauthorizedSiteAccessException + * @throws \Ibexa\Core\MVC\Symfony\Security\Exception\UnauthorizedSiteAccessException */ public function checkSiteAccessPermission(BaseInteractiveLoginEvent $event) { @@ -170,7 +171,7 @@ public function checkSiteAccessPermission(BaseInteractiveLoginEvent $event) $originalUser = $token->getUser(); $request = $event->getRequest(); $siteAccess = $request->attributes->get('siteaccess'); - if (!($originalUser instanceof eZUser && $siteAccess instanceof SiteAccess)) { + if (!($originalUser instanceof IbexaUser && $siteAccess instanceof SiteAccess)) { return; } @@ -184,7 +185,7 @@ public function checkSiteAccessPermission(BaseInteractiveLoginEvent $event) * * @param \Symfony\Component\HttpKernel\Event\RequestEvent $event * - * @throws \eZ\Publish\Core\MVC\Symfony\Security\Exception\UnauthorizedSiteAccessException + * @throws \Ibexa\Core\MVC\Symfony\Security\Exception\UnauthorizedSiteAccessException */ public function onKernelRequest(RequestEvent $event) { @@ -237,7 +238,7 @@ private function isMasterRequest(Request $request, $requestType) /** * Returns true if current user has access to given SiteAccess. * - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess $siteAccess + * @param \Ibexa\Core\MVC\Symfony\SiteAccess $siteAccess * * @return bool */ @@ -248,3 +249,5 @@ protected function hasAccess(SiteAccess $siteAccess) ); } } + +class_alias(SecurityListener::class, 'eZ\Publish\Core\MVC\Symfony\Security\EventListener\SecurityListener'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Exception/UnauthorizedSiteAccessException.php b/src/lib/MVC/Symfony/Security/Exception/UnauthorizedSiteAccessException.php similarity index 76% rename from eZ/Publish/Core/MVC/Symfony/Security/Exception/UnauthorizedSiteAccessException.php rename to src/lib/MVC/Symfony/Security/Exception/UnauthorizedSiteAccessException.php index 707dc1983a..e18c12dee7 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Exception/UnauthorizedSiteAccessException.php +++ b/src/lib/MVC/Symfony/Security/Exception/UnauthorizedSiteAccessException.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Exception; +namespace Ibexa\Core\MVC\Symfony\Security\Exception; use Exception; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess; use Symfony\Component\Security\Core\Exception\AccessDeniedException; /** @@ -21,3 +21,5 @@ public function __construct(SiteAccess $siteAccess, $username, Exception $previo parent::__construct("User '$username' doesn't have user/login permission to SiteAccess '$siteAccess->name'", $previous); } } + +class_alias(UnauthorizedSiteAccessException::class, 'eZ\Publish\Core\MVC\Symfony\Security\Exception\UnauthorizedSiteAccessException'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/HttpUtils.php b/src/lib/MVC/Symfony/Security/HttpUtils.php similarity index 82% rename from eZ/Publish/Core/MVC/Symfony/Security/HttpUtils.php rename to src/lib/MVC/Symfony/Security/HttpUtils.php index 88101da209..12059476cd 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/HttpUtils.php +++ b/src/lib/MVC/Symfony/Security/HttpUtils.php @@ -4,20 +4,20 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security; +namespace Ibexa\Core\MVC\Symfony\Security; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessAware; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessAware; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Security\Http\HttpUtils as BaseHttpUtils; class HttpUtils extends BaseHttpUtils implements SiteAccessAware { - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ private $siteAccess; /** - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess $siteAccess + * @param \Ibexa\Core\MVC\Symfony\SiteAccess|null $siteAccess */ public function setSiteAccess(SiteAccess $siteAccess = null) { @@ -58,3 +58,5 @@ private function isRouteName($path) return $path && strpos($path, 'http') !== 0 && strpos($path, '/') !== 0; } } + +class_alias(HttpUtils::class, 'eZ\Publish\Core\MVC\Symfony\Security\HttpUtils'); diff --git a/src/lib/MVC/Symfony/Security/InteractiveLoginToken.php b/src/lib/MVC/Symfony/Security/InteractiveLoginToken.php new file mode 100644 index 0000000000..af7fc23494 --- /dev/null +++ b/src/lib/MVC/Symfony/Security/InteractiveLoginToken.php @@ -0,0 +1,88 @@ +originalTokenType = $originalTokenType; + } + + public function getOriginalTokenType(): string + { + return $this->originalTokenType; + } + + /** + * @return array{ + * string, + * mixed, + * null|\Symfony\Component\Security\Core\Authentication\Token\TokenInterface + * } $data + */ + public function __serialize(): array + { + return [ + $this->originalTokenType, + parent::__serialize(), + $this->originalToken, + ]; + } + + /** + * @param array{ + * string, + * mixed, + * 2?: \Symfony\Component\Security\Core\Authentication\Token\TokenInterface + * } $data + */ + public function __unserialize(array $data): void + { + if (isset($data[2])) { + [$this->originalTokenType, $parentData, $this->originalToken] = $data; + } else { + [$this->originalTokenType, $parentData] = $data; + } + + parent::__unserialize($parentData); + } + + public function setOriginalToken(TokenInterface $token): void + { + $this->originalToken = $token; + } + + public function getOriginalToken(): ?TokenInterface + { + return $this->originalToken; + } + + public function isAuthenticated(): bool + { + if (null !== $this->originalToken) { + return $this->originalToken->isAuthenticated(); + } + + return parent::isAuthenticated(); + } +} + +class_alias(InteractiveLoginToken::class, 'eZ\Publish\Core\MVC\Symfony\Security\InteractiveLoginToken'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/ReferenceUserInterface.php b/src/lib/MVC/Symfony/Security/ReferenceUserInterface.php similarity index 78% rename from eZ/Publish/Core/MVC/Symfony/Security/ReferenceUserInterface.php rename to src/lib/MVC/Symfony/Security/ReferenceUserInterface.php index a52779680e..8e6d308277 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/ReferenceUserInterface.php +++ b/src/lib/MVC/Symfony/Security/ReferenceUserInterface.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security; +namespace Ibexa\Core\MVC\Symfony\Security; /** * Interface for Repository based users, where we only serialize user id / Reference in session values. @@ -18,7 +18,7 @@ interface ReferenceUserInterface extends UserInterface { /** - * @return \eZ\Publish\API\Repository\Values\User\UserReference + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserReference */ public function getAPIUserReference(); @@ -26,7 +26,9 @@ public function getAPIUserReference(); * @throws \LogicException If api user has not been refreshed yet by UserProvider after being * unserialized from session. * - * @return \eZ\Publish\API\Repository\Values\User\User + * @return \Ibexa\Contracts\Core\Repository\Values\User\User */ public function getAPIUser(); } + +class_alias(ReferenceUserInterface::class, 'eZ\Publish\Core\MVC\Symfony\Security\ReferenceUserInterface'); diff --git a/src/lib/MVC/Symfony/Security/User.php b/src/lib/MVC/Symfony/Security/User.php new file mode 100644 index 0000000000..0e2b1d65a2 --- /dev/null +++ b/src/lib/MVC/Symfony/Security/User.php @@ -0,0 +1,157 @@ +user = $user; + $this->reference = new UserReference($user->getUserId()); + $this->roles = $roles; + } + + /** + * Returns the roles granted to the user. + * + * + * public function getRoles() + * { + * return array( 'ROLE_USER' ); + * } + * + * + * Alternatively, the roles might be stored on a ``roles`` property, + * and populated in any number of different ways when the user object + * is created. + * + * @return string[] The user roles + */ + public function getRoles() + { + return $this->roles; + } + + /** + * Returns the password used to authenticate the user. + * + * This should be the encoded password. On authentication, a plain-text + * password will be salted, encoded, and then compared to this value. + */ + public function getPassword(): string + { + return $this->getAPIUser()->getPasswordHash(); + } + + /** + * Returns the salt that was originally used to encode the password. + * + * This can return null if the password was not encoded using a salt. + * + * @return string The salt + */ + public function getSalt() + { + return null; + } + + /** + * Returns the username used to authenticate the user. + */ + public function getUsername(): string + { + return $this->getAPIUser()->getLogin(); + } + + /** + * Removes sensitive data from the user. + * + * This is important if, at any given point, sensitive information like + * the plain-text password is stored on this object. + */ + public function eraseCredentials() + { + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserReference + */ + public function getAPIUserReference() + { + return $this->reference; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + */ + public function getAPIUser() + { + if (!$this->user instanceof APIUser) { + throw new \LogicException( + 'Attempted to get APIUser before it has been set by UserProvider, APIUser is not serialized to session' + ); + } + + return $this->user; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + */ + public function setAPIUser(APIUser $user) + { + $this->user = $user; + $this->reference = new UserReference($user->getUserId()); + } + + public function isEqualTo(BaseUserInterface $user) + { + // Check for the lighter ReferenceUserInterface first + if ($user instanceof ReferenceUserInterface) { + return $user->getAPIUserReference()->getUserId() === $this->reference->getUserId(); + } elseif ($user instanceof UserInterface) { + return $user->getAPIUser()->getUserId() === $this->reference->getUserId(); + } + + return false; + } + + public function __toString() + { + return $this->getAPIUser()->getContentInfo()->getName(); + } + + /** + * Make sure we don't serialize the whole API user object given it's a full fledged api content object. We set + * (& either way refresh) the user object in {@see \Ibexa\Core\MVC\Symfony\Security\User\BaseProvider::refreshUser} + * when object wakes back up from session. + * + * @return array + */ + public function __sleep() + { + return ['reference', 'roles']; + } +} + +class_alias(User::class, 'eZ\Publish\Core\MVC\Symfony\Security\User'); diff --git a/src/lib/MVC/Symfony/Security/User/APIUserProviderInterface.php b/src/lib/MVC/Symfony/Security/User/APIUserProviderInterface.php new file mode 100644 index 0000000000..97a8655796 --- /dev/null +++ b/src/lib/MVC/Symfony/Security/User/APIUserProviderInterface.php @@ -0,0 +1,27 @@ +permissionResolver = $permissionResolver; + $this->userService = $userService; + } + + public function refreshUser(CoreUserInterface $user) + { + if (!$user instanceof UserInterface) { + throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', get_class($user))); + } + + try { + $refreshedAPIUser = $this->userService->loadUser( + $user instanceof ReferenceUserInterface + ? $user->getAPIUserReference()->getUserId() + : $user->getAPIUser()->id + ); + $user->setAPIUser($refreshedAPIUser); + $this->permissionResolver->setCurrentUserReference( + new UserReference($refreshedAPIUser->getUserId()) + ); + + return $user; + } catch (NotFoundException $e) { + throw new UsernameNotFoundException($e->getMessage(), 0, $e); + } + } + + /** + * Whether this provider supports the given user class. + * + * @param string $class + * + * @return bool + */ + public function supportsClass($class) + { + return $class === UserInterface::class || is_subclass_of($class, UserInterface::class); + } + + /** + * Loads a regular user object, usable by Symfony Security component, from a user object returned by Public API. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $apiUser + * + * @return \Ibexa\Core\MVC\Symfony\Security\User + */ + public function loadUserByAPIUser(APIUser $apiUser) + { + return $this->createSecurityUser($apiUser); + } + + /** + * Creates user object, usable by Symfony Security component, from a user object returned by Public API. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $apiUser + * + * @return \Ibexa\Core\MVC\Symfony\Security\User + */ + protected function createSecurityUser(APIUser $apiUser): User + { + return new User($apiUser, ['ROLE_USER']); + } +} + +class_alias(BaseProvider::class, 'eZ\Publish\Core\MVC\Symfony\Security\User\BaseProvider'); diff --git a/src/lib/MVC/Symfony/Security/User/EmailProvider.php b/src/lib/MVC/Symfony/Security/User/EmailProvider.php new file mode 100644 index 0000000000..02e7f6dd92 --- /dev/null +++ b/src/lib/MVC/Symfony/Security/User/EmailProvider.php @@ -0,0 +1,36 @@ +createSecurityUser( + $this->userService->loadUserByEmail($user) + ); + } catch (NotFoundException $e) { + throw new UsernameNotFoundException($e->getMessage(), 0, $e); + } + } +} + +class_alias(EmailProvider::class, 'eZ\Publish\Core\MVC\Symfony\Security\User\EmailProvider'); diff --git a/src/lib/MVC/Symfony/Security/User/UsernameProvider.php b/src/lib/MVC/Symfony/Security/User/UsernameProvider.php new file mode 100644 index 0000000000..e78196e206 --- /dev/null +++ b/src/lib/MVC/Symfony/Security/User/UsernameProvider.php @@ -0,0 +1,36 @@ +createSecurityUser( + $this->userService->loadUserByLogin($user) + ); + } catch (NotFoundException $e) { + throw new UsernameNotFoundException($e->getMessage(), 0, $e); + } + } +} + +class_alias(UsernameProvider::class, 'eZ\Publish\Core\MVC\Symfony\Security\User\UsernameProvider'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/UserChecker.php b/src/lib/MVC/Symfony/Security/UserChecker.php similarity index 76% rename from eZ/Publish/Core/MVC/Symfony/Security/UserChecker.php rename to src/lib/MVC/Symfony/Security/UserChecker.php index a2dc89c888..1383a21cb2 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/UserChecker.php +++ b/src/lib/MVC/Symfony/Security/UserChecker.php @@ -6,18 +6,18 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Security; +namespace Ibexa\Core\MVC\Symfony\Security; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\Core\MVC\Symfony\Security\UserInterface as EzUserInterface; +use Ibexa\Contracts\Core\Repository\UserService; use Ibexa\Core\MVC\Symfony\Security\Exception\PasswordExpiredException; +use Ibexa\Core\MVC\Symfony\Security\UserInterface as IbexaUserInterface; use Symfony\Component\Security\Core\Exception\DisabledException; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; final class UserChecker implements UserCheckerInterface { - /** @var \eZ\Publish\API\Repository\UserService */ + /** @var \Ibexa\Contracts\Core\Repository\UserService */ private $userService; public function __construct(UserService $userService) @@ -27,7 +27,7 @@ public function __construct(UserService $userService) public function checkPreAuth(UserInterface $user): void { - if (!$user instanceof EzUserInterface) { + if (!$user instanceof IbexaUserInterface) { return; } @@ -41,7 +41,7 @@ public function checkPreAuth(UserInterface $user): void public function checkPostAuth(UserInterface $user): void { - if (!$user instanceof EzUserInterface) { + if (!$user instanceof IbexaUserInterface) { return; } @@ -53,3 +53,5 @@ public function checkPostAuth(UserInterface $user): void } } } + +class_alias(UserChecker::class, 'eZ\Publish\Core\MVC\Symfony\Security\UserChecker'); diff --git a/src/lib/MVC/Symfony/Security/UserInterface.php b/src/lib/MVC/Symfony/Security/UserInterface.php new file mode 100644 index 0000000000..05bb390f5d --- /dev/null +++ b/src/lib/MVC/Symfony/Security/UserInterface.php @@ -0,0 +1,30 @@ +wrappedUser instanceof EquatableInterface ? $this->wrappedUser->isEqualTo($user) : true; } - /* - * Make sure we don't serialize the whole API user object given it's a full fledged api content object. We set - * (& either way refresh) the user object in \eZ\Publish\Core\MVC\Symfony\Security\User\Provider->refreshUser() - * when object wakes back up from session. - * - * @return array - */ + /** + * @see \Ibexa\Core\MVC\Symfony\Security\User::__sleep + */ public function __sleep(): array { return ['wrappedUser', 'apiUserReference']; } } + +class_alias(UserWrapped::class, 'eZ\Publish\Core\MVC\Symfony\Security\UserWrapped'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess.php b/src/lib/MVC/Symfony/SiteAccess.php similarity index 87% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess.php rename to src/lib/MVC/Symfony/SiteAccess.php index c496b7fac4..9ed34bbe6c 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess.php +++ b/src/lib/MVC/Symfony/SiteAccess.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony; +namespace Ibexa\Core\MVC\Symfony; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; use JsonSerializable; /** @@ -23,7 +23,7 @@ class SiteAccess extends ValueObject implements JsonSerializable */ public $name; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccessGroup[] */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccessGroup[] */ public $groups = []; /** @@ -37,7 +37,7 @@ class SiteAccess extends ValueObject implements JsonSerializable /** * The matcher instance that has been used to discover the siteaccess. * - * @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher + * @var \Ibexa\Core\MVC\Symfony\SiteAccess\Matcher */ public $matcher; @@ -80,3 +80,5 @@ public function jsonSerialize(): array ]; } } + +class_alias(SiteAccess::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess'); diff --git a/src/lib/MVC/Symfony/SiteAccess/Matcher.php b/src/lib/MVC/Symfony/SiteAccess/Matcher.php new file mode 100644 index 0000000000..e68057ea00 --- /dev/null +++ b/src/lib/MVC/Symfony/SiteAccess/Matcher.php @@ -0,0 +1,39 @@ +config as $i => $rule) { + foreach ($rule['matchers'] as $subMatcherClass => $matchingConfig) { + // If at least one sub matcher doesn't match, jump to the next rule set. + if ($this->matchersMap[$i][$subMatcherClass]->match() === false) { + continue 2; + } + } + + $this->subMatchers = $this->matchersMap[$i]; + + return $rule['match']; + } + + return false; + } + + public function reverseMatch($siteAccessName) + { + foreach ($this->config as $i => $rule) { + if ($rule['match'] === $siteAccessName) { + $subMatchers = []; + foreach ($this->matchersMap[$i] as $subMatcher) { + if (!$subMatcher instanceof VersatileMatcher) { + return null; + } + + $reverseMatcher = $subMatcher->reverseMatch($siteAccessName); + if (!$reverseMatcher) { + return null; + } + + $subMatchers[] = $subMatcher; + } + + $this->setSubMatchers($subMatchers); + + return $this; + } + } + } +} + +class_alias(LogicalAnd::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound\LogicalAnd'); diff --git a/src/lib/MVC/Symfony/SiteAccess/Matcher/Compound/LogicalOr.php b/src/lib/MVC/Symfony/SiteAccess/Matcher/Compound/LogicalOr.php new file mode 100644 index 0000000000..6e72132d57 --- /dev/null +++ b/src/lib/MVC/Symfony/SiteAccess/Matcher/Compound/LogicalOr.php @@ -0,0 +1,57 @@ +config as $i => $rule) { + foreach ($rule['matchers'] as $subMatcherClass => $matchingConfig) { + if ($this->matchersMap[$i][$subMatcherClass]->match()) { + $this->subMatchers = $this->matchersMap[$i]; + + return $rule['match']; + } + } + } + + return false; + } + + public function reverseMatch($siteAccessName) + { + foreach ($this->config as $i => $rule) { + if ($rule['match'] === $siteAccessName) { + foreach ($this->matchersMap[$i] as $subMatcher) { + if (!$subMatcher instanceof VersatileMatcher) { + continue; + } + + $reverseMatcher = $subMatcher->reverseMatch($siteAccessName); + if (!$reverseMatcher) { + continue; + } + + $this->setSubMatchers([$subMatcher]); + + return $this; + } + } + } + } +} + +class_alias(LogicalOr::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound\LogicalOr'); diff --git a/src/lib/MVC/Symfony/SiteAccess/Matcher/CompoundInterface.php b/src/lib/MVC/Symfony/SiteAccess/Matcher/CompoundInterface.php new file mode 100644 index 0000000000..b2308956f6 --- /dev/null +++ b/src/lib/MVC/Symfony/SiteAccess/Matcher/CompoundInterface.php @@ -0,0 +1,37 @@ +hostElements = $elements; } } + +class_alias(HostElement::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\HostElement'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/HostText.php b/src/lib/MVC/Symfony/SiteAccess/Matcher/HostText.php similarity index 83% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/HostText.php rename to src/lib/MVC/Symfony/SiteAccess/Matcher/HostText.php index fcc0dc31f9..86c46df428 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/HostText.php +++ b/src/lib/MVC/Symfony/SiteAccess/Matcher/HostText.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; +namespace Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\VersatileMatcher; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\VersatileMatcher; class HostText extends Regex implements VersatileMatcher { @@ -46,7 +46,7 @@ public function getName() /** * Injects the request object to match against. * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $request */ public function setRequest(SimplifiedRequest $request) { @@ -69,3 +69,5 @@ public function getRequest() return $this->request; } } + +class_alias(HostText::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\HostText'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Map.php b/src/lib/MVC/Symfony/SiteAccess/Matcher/Map.php similarity index 85% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Map.php rename to src/lib/MVC/Symfony/SiteAccess/Matcher/Map.php index a19ab7bbc3..76d4db7b85 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Map.php +++ b/src/lib/MVC/Symfony/SiteAccess/Matcher/Map.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; +namespace Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\VersatileMatcher; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\VersatileMatcher; abstract class Map implements VersatileMatcher { @@ -32,7 +32,7 @@ abstract class Map implements VersatileMatcher */ protected $reverseMap = []; - /** @var \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest */ + /** @var \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest */ protected $request; /** @@ -48,7 +48,7 @@ public function __construct(array $map) /** * Do not serialize the Siteaccess configuration in order to reduce ESI request URL size. * - * @see https://jira.ez.no/browse/EZP-23168 + * @see https://issues.ibexa.co/browse/EZP-23168 * * @return array */ @@ -103,7 +103,7 @@ public function match() /** * @param string $siteAccessName * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher|Map|null + * @return \Ibexa\Core\MVC\Symfony\SiteAccess\Matcher|Map|null */ public function reverseMatch($siteAccessName) { @@ -135,3 +135,5 @@ private function getReverseMap($defaultSiteAccess) return $this->reverseMap = array_flip($map); } } + +class_alias(Map::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map'); diff --git a/src/lib/MVC/Symfony/SiteAccess/Matcher/Map/Host.php b/src/lib/MVC/Symfony/SiteAccess/Matcher/Map/Host.php new file mode 100644 index 0000000000..0658f59dc6 --- /dev/null +++ b/src/lib/MVC/Symfony/SiteAccess/Matcher/Map/Host.php @@ -0,0 +1,44 @@ +key) { + $this->setMapKey($request->host); + } + + parent::setRequest($request); + } + + public function reverseMatch($siteAccessName) + { + $matcher = parent::reverseMatch($siteAccessName); + if ($matcher instanceof self) { + $matcher->getRequest()->setHost($matcher->getMapKey()); + } + + return $matcher; + } +} + +class_alias(Host::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map\Host'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Map/Port.php b/src/lib/MVC/Symfony/SiteAccess/Matcher/Map/Port.php similarity index 78% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Map/Port.php rename to src/lib/MVC/Symfony/SiteAccess/Matcher/Map/Port.php index dcad6b3610..de1e7dd0a1 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Map/Port.php +++ b/src/lib/MVC/Symfony/SiteAccess/Matcher/Map/Port.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map; +namespace Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Map; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Map; class Port extends Map { @@ -19,7 +19,7 @@ public function getName() /** * Injects the request object to match against. * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $request */ public function setRequest(SimplifiedRequest $request) { @@ -54,3 +54,5 @@ public function reverseMatch($siteAccessName) return $matcher; } } + +class_alias(Port::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map\Port'); diff --git a/src/lib/MVC/Symfony/SiteAccess/Matcher/Map/URI.php b/src/lib/MVC/Symfony/SiteAccess/Matcher/Map/URI.php new file mode 100644 index 0000000000..9e5eb6e256 --- /dev/null +++ b/src/lib/MVC/Symfony/SiteAccess/Matcher/Map/URI.php @@ -0,0 +1,91 @@ +key) { + sscanf($request->pathinfo, '/%[^/]', $key); + $this->setMapKey(rawurldecode((string)$key)); + } + + parent::setRequest($request); + } + + public function getName() + { + return 'uri:map'; + } + + /** + * Fixes up $uri to remove the siteaccess part, if needed. + * + * @param string $uri The original URI + * + * @return string + */ + public function analyseURI($uri) + { + if (($siteaccessPart = "/$this->key") === $uri) { + return '/'; + } + + if (mb_strpos($uri, $siteaccessPart) === 0) { + return mb_substr($uri, mb_strlen($siteaccessPart)); + } + + return $uri; + } + + /** + * Analyses $linkUri when generating a link to a route, in order to have the siteaccess part back in the URI. + * + * @param string $linkUri + * + * @return string The modified link URI + */ + public function analyseLink($linkUri) + { + // Joining slash between uriElements and actual linkUri must be present, except if $linkUri is empty or is just the slash root. + $joiningSlash = empty($linkUri) || ($linkUri === '/') ? '' : '/'; + $linkUri = ltrim($linkUri, '/'); + // Removing query string to analyse as SiteAccess might be in it. + $qsPos = strpos($linkUri, '?'); + $queryString = ''; + if ($qsPos !== false) { + $queryString = substr($linkUri, $qsPos); + $linkUri = substr($linkUri, 0, $qsPos); + } + + return "/{$this->key}{$joiningSlash}{$linkUri}{$queryString}"; + } + + public function reverseMatch($siteAccessName) + { + $matcher = parent::reverseMatch($siteAccessName); + if ($matcher instanceof self) { + $request = $matcher->getRequest(); + // Clean up "old" siteaccess prefix and add the new prefix. + $request->setPathinfo($this->analyseLink($request->pathinfo)); + } + + return $matcher; + } +} + +class_alias(URI::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map\URI'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Regex.php b/src/lib/MVC/Symfony/SiteAccess/Matcher/Regex.php similarity index 84% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Regex.php rename to src/lib/MVC/Symfony/SiteAccess/Matcher/Regex.php index 189a846a0d..795d4a2002 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/Regex.php +++ b/src/lib/MVC/Symfony/SiteAccess/Matcher/Regex.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; +namespace Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; abstract class Regex implements Matcher { @@ -32,7 +32,7 @@ abstract class Regex implements Matcher */ protected $itemNumber; - /** @var \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest */ + /** @var \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest */ protected $request; /** @var string */ @@ -85,7 +85,7 @@ protected function getMatchedSiteAccess() /** * Injects the request object to match against. * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $request */ public function setRequest(SimplifiedRequest $request) { @@ -102,3 +102,5 @@ public function setMatchElement($element) $this->element = $element; } } + +class_alias(Regex::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex'); diff --git a/src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/Host.php b/src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/Host.php new file mode 100644 index 0000000000..8bc64d4dcc --- /dev/null +++ b/src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/Host.php @@ -0,0 +1,59 @@ +siteAccessesConfiguration = $siteAccessesConfiguration; + } + + public function getName() + { + return 'host:regexp'; + } + + /** + * Injects the request object to match against. + * + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $request + */ + public function setRequest(SimplifiedRequest $request) + { + if (!$this->element) { + $this->setMatchElement($request->host); + } + + parent::setRequest($request); + } +} + +class_alias(Host::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex\Host'); diff --git a/src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/URI.php b/src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/URI.php new file mode 100644 index 0000000000..c0f2da883c --- /dev/null +++ b/src/lib/MVC/Symfony/SiteAccess/Matcher/Regex/URI.php @@ -0,0 +1,59 @@ +siteAccessesConfiguration = $siteAccessesConfiguration; + } + + public function getName() + { + return 'uri:regexp'; + } + + /** + * Injects the request object to match against. + * + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $request + */ + public function setRequest(SimplifiedRequest $request) + { + if (!$this->element) { + $this->setMatchElement($request->pathinfo); + } + + parent::setRequest($request); + } +} + +class_alias(URI::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex\URI'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/URIElement.php b/src/lib/MVC/Symfony/SiteAccess/Matcher/URIElement.php similarity index 88% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/URIElement.php rename to src/lib/MVC/Symfony/SiteAccess/Matcher/URIElement.php index 90c5a62f46..b9f4df7b70 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/URIElement.php +++ b/src/lib/MVC/Symfony/SiteAccess/Matcher/URIElement.php @@ -4,16 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; +namespace Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\URILexer; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\VersatileMatcher; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\URILexer; +use Ibexa\Core\MVC\Symfony\SiteAccess\VersatileMatcher; use LogicException; class URIElement implements VersatileMatcher, URILexer { - /** @var \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest */ + /** @var \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest */ private $request; /** @@ -107,7 +107,7 @@ public function getName() /** * Injects the request object to match against. * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $request */ public function setRequest(SimplifiedRequest $request) { @@ -115,7 +115,7 @@ public function setRequest(SimplifiedRequest $request) } /** - * @return \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest + * @return \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest */ public function getRequest() { @@ -168,7 +168,7 @@ public function analyseLink($linkUri) * * @param string $siteAccessName * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIElement|null + * @return \Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\URIElement|null */ public function reverseMatch($siteAccessName) { @@ -183,3 +183,5 @@ public function reverseMatch($siteAccessName) return $this; } } + +class_alias(URIElement::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIElement'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/URIText.php b/src/lib/MVC/Symfony/SiteAccess/Matcher/URIText.php similarity index 87% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/URIText.php rename to src/lib/MVC/Symfony/SiteAccess/Matcher/URIText.php index 0949d3da6e..b459630b88 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Matcher/URIText.php +++ b/src/lib/MVC/Symfony/SiteAccess/Matcher/URIText.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; +namespace Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\URILexer; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\VersatileMatcher; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\URILexer; +use Ibexa\Core\MVC\Symfony\SiteAccess\VersatileMatcher; class URIText extends Regex implements VersatileMatcher, URILexer { @@ -50,7 +50,7 @@ public function getName() /** * Injects the request object to match against. * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $request */ public function setRequest(SimplifiedRequest $request) { @@ -102,3 +102,5 @@ public function getRequest() return $this->request; } } + +class_alias(URIText::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIText'); diff --git a/src/lib/MVC/Symfony/SiteAccess/MatcherBuilder.php b/src/lib/MVC/Symfony/SiteAccess/MatcherBuilder.php new file mode 100644 index 0000000000..f7705c9d19 --- /dev/null +++ b/src/lib/MVC/Symfony/SiteAccess/MatcherBuilder.php @@ -0,0 +1,43 @@ +setRequest($request); + + return $matcher; + } +} + +class_alias(MatcherBuilder::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilder'); diff --git a/src/lib/MVC/Symfony/SiteAccess/MatcherBuilderInterface.php b/src/lib/MVC/Symfony/SiteAccess/MatcherBuilderInterface.php new file mode 100644 index 0000000000..ed6bb9c85f --- /dev/null +++ b/src/lib/MVC/Symfony/SiteAccess/MatcherBuilderInterface.php @@ -0,0 +1,27 @@ +providers = $providers; + } + + public function getSiteAccesses(): Traversable + { + foreach ($this->providers as $provider) { + foreach ($provider->getSiteAccesses() as $siteAccess) { + yield $siteAccess; + } + } + + yield from []; + } + + public function isDefined(string $name): bool + { + foreach ($this->providers as $provider) { + if ($provider->isDefined($name)) { + return true; + } + } + + return false; + } + + /** + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + */ + public function getSiteAccess(string $name): SiteAccess + { + foreach ($this->providers as $provider) { + if ($provider->isDefined($name)) { + return $provider->getSiteAccess($name); + } + } + + throw new NotFoundException('Site Access', $name); + } +} + +class_alias(ChainSiteAccessProvider::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Provider\ChainSiteAccessProvider'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Provider/StaticSiteAccessProvider.php b/src/lib/MVC/Symfony/SiteAccess/Provider/StaticSiteAccessProvider.php similarity index 83% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Provider/StaticSiteAccessProvider.php rename to src/lib/MVC/Symfony/SiteAccess/Provider/StaticSiteAccessProvider.php index 02dcfe3b2f..136694ecfa 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Provider/StaticSiteAccessProvider.php +++ b/src/lib/MVC/Symfony/SiteAccess/Provider/StaticSiteAccessProvider.php @@ -6,12 +6,12 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Provider; +namespace Ibexa\Core\MVC\Symfony\SiteAccess\Provider; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccessGroup; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface; +use Ibexa\Core\MVC\Symfony\SiteAccessGroup; use Traversable; /** @@ -75,3 +75,5 @@ static function ($groupName) { return $siteAccess; } } + +class_alias(StaticSiteAccessProvider::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Provider\StaticSiteAccessProvider'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Router.php b/src/lib/MVC/Symfony/SiteAccess/Router.php similarity index 78% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Router.php rename to src/lib/MVC/Symfony/SiteAccess/Router.php index 1531796b94..5445aa47c5 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Router.php +++ b/src/lib/MVC/Symfony/SiteAccess/Router.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess; +namespace Ibexa\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Exception\InvalidSiteAccessException; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\CompoundInterface; +use Ibexa\Core\MVC\Exception\InvalidSiteAccessException; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\CompoundInterface; use InvalidArgumentException; use Psr\Log\LoggerInterface; @@ -35,16 +35,16 @@ class Router implements SiteAccessRouterInterface, SiteAccessAware * array( * // Using built-in URI matcher. Key is the prefix that matches the siteaccess, in the value * "Map\\URI" => array( - * "ezdemo_site" => "ezdemo_site", - * "ezdemo_site_admin" => "ezdemo_site_admin", + * "ibexa_demo_site" => "ibexa_demo_site", + * "ibexa_demo_site_admin" => "ibexa_demo_site_admin", * ), * // Using built-in HOST matcher. Key is the hostname, value is the siteaccess name * "Map\\Host" => array( - * "ezpublish.dev" => "ezdemo_site", - * "ezpublish.admin.dev" => "ezdemo_site_admin", + * "ezpublish.dev" => "ibexa_demo_site", + * "ezpublish.admin.dev" => "ibexa_demo_site_admin", * ), * // Using a custom matcher (class must begin with a '\', as a full qualified class name). - * // The custom matcher must implement eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher interface. + * // The custom matcher must implement {@see \Ibexa\Core\MVC\Symfony\SiteAccess} interface. * "\\My\\Custom\\Matcher" => array( * "something_to_match_against" => "siteaccess_name" * ) @@ -55,10 +55,10 @@ class Router implements SiteAccessRouterInterface, SiteAccessAware */ protected $siteAccessesConfiguration; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface */ protected $siteAccessProvider; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ protected $siteAccess; /** @var string */ @@ -67,21 +67,21 @@ class Router implements SiteAccessRouterInterface, SiteAccessAware /** @var \Psr\Log\LoggerInterface */ protected $logger; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface */ protected $matcherBuilder; - /** @var \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest */ + /** @var \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest */ protected $request; /** @var bool */ protected $debug; /** - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface $matcherBuilder + * @param \Ibexa\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface $matcherBuilder * @param \Psr\Log\LoggerInterface $logger * @param string $defaultSiteAccess * @param array $siteAccessesConfiguration - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface $siteAccessProvider + * @param \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface $siteAccessProvider * @param string|null $siteAccessClass * @param bool $debug */ @@ -99,13 +99,13 @@ public function __construct( $this->defaultSiteAccess = $defaultSiteAccess; $this->siteAccessesConfiguration = $siteAccessesConfiguration; $this->siteAccessProvider = $siteAccessProvider; - $this->siteAccessClass = $siteAccessClass ?: 'eZ\\Publish\\Core\\MVC\\Symfony\\SiteAccess'; + $this->siteAccessClass = $siteAccessClass ?: SiteAccess::class; $this->request = new SimplifiedRequest(); $this->debug = $debug; } /** - * @return \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest + * @return \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest */ public function getRequest() { @@ -115,11 +115,11 @@ public function getRequest() /** * Performs SiteAccess matching given the $request. * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $request * - * @throws \eZ\Publish\Core\MVC\Exception\InvalidSiteAccessException + * @throws \Ibexa\Core\MVC\Exception\InvalidSiteAccessException * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess + * @return \Ibexa\Core\MVC\Symfony\SiteAccess */ public function match(SimplifiedRequest $request) { @@ -130,19 +130,20 @@ public function match(SimplifiedRequest $request) } // Request header always have precedence - // Note: request headers are always in lower cased. - if (!empty($request->headers['x-siteaccess'])) { - $siteaccessName = $request->headers['x-siteaccess'][0]; - if (!$this->siteAccessProvider->isDefined($siteaccessName)) { + // Note: request headers are always lower case. + $xSiteAccess = $request->getHeader('x-siteaccess'); + if (!empty($xSiteAccess) && is_array($xSiteAccess)) { + $siteAccessName = $xSiteAccess[0]; + if (!$this->siteAccessProvider->isDefined($siteAccessName)) { throw new InvalidSiteAccessException( - $siteaccessName, + $siteAccessName, $this->siteAccessProvider, 'X-Siteaccess request header', $this->debug ); } - $this->siteAccess = $this->siteAccessProvider->getSiteAccess($siteaccessName); + $this->siteAccess = $this->siteAccessProvider->getSiteAccess($siteAccessName); $this->siteAccess->matchingType = self::HEADER_SA_MATCHING_TYPE; return $this->siteAccess; @@ -173,9 +174,9 @@ public function match(SimplifiedRequest $request) * Returns the SiteAccess object matched against $request and the siteaccess configuration. * If nothing could be matched, the default siteaccess is returned, with "default" as matching type. * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $request * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess + * @return \Ibexa\Core\MVC\Symfony\SiteAccess */ private function doMatch(SimplifiedRequest $request) { @@ -210,7 +211,7 @@ private function doMatch(SimplifiedRequest $request) * * @throws \InvalidArgumentException If $siteAccessName is invalid (i.e. not present in configured list). * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess|null + * @return \Ibexa\Core\MVC\Symfony\SiteAccess|null */ public function matchByName($siteAccessName) { @@ -240,7 +241,7 @@ public function matchByName($siteAccessName) continue; } - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess $siteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess $siteAccess */ $siteAccess = new $siteAccessClass($siteAccessName); $siteAccess->matcher = $reverseMatcher; $siteAccess->matchingType = $reverseMatcher->getName(); @@ -255,7 +256,7 @@ public function matchByName($siteAccessName) } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess|null + * @return \Ibexa\Core\MVC\Symfony\SiteAccess|null */ public function getSiteAccess() { @@ -263,10 +264,12 @@ public function getSiteAccess() } /** - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess $siteAccess + * @param \Ibexa\Core\MVC\Symfony\SiteAccess|null $siteAccess */ public function setSiteAccess(SiteAccess $siteAccess = null) { $this->siteAccess = $siteAccess; } } + +class_alias(Router::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Router'); diff --git a/src/lib/MVC/Symfony/SiteAccess/SiteAccessAware.php b/src/lib/MVC/Symfony/SiteAccess/SiteAccessAware.php new file mode 100644 index 0000000000..612c35513e --- /dev/null +++ b/src/lib/MVC/Symfony/SiteAccess/SiteAccessAware.php @@ -0,0 +1,19 @@ +provider = $provider; + $this->configResolver = $configResolver; + } + + public function setSiteAccess(SiteAccess $siteAccess = null): void + { + $this->siteAccess = $siteAccess; + } + + public function exists(string $name): bool + { + return $this->provider->isDefined($name); + } + + public function get(string $name): SiteAccess + { + if ($this->provider->isDefined($name)) { + return $this->provider->getSiteAccess($name); + } + + throw new NotFoundException('SiteAccess', $name); + } + + public function getAll(): iterable + { + return $this->provider->getSiteAccesses(); + } + + public function getCurrent(): ?SiteAccess + { + return $this->siteAccess ?? null; + } + + public function getSiteAccessesRelation(?SiteAccess $siteAccess = null): array + { + $siteAccess = $siteAccess ?? $this->siteAccess; + $saRelationMap = []; + + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess[] $saList */ + $saList = iterator_to_array($this->provider->getSiteAccesses()); + // First build the SiteAccess relation map, indexed by repository and rootLocationId. + foreach ($saList as $sa) { + $siteAccessName = $sa->name; + + $repository = $this->configResolver->getParameter('repository', 'ibexa.site_access.config', $siteAccessName); + if (!isset($saRelationMap[$repository])) { + $saRelationMap[$repository] = []; + } + + $rootLocationId = $this->configResolver->getParameter('content.tree_root.location_id', 'ibexa.site_access.config', $siteAccessName); + if (!isset($saRelationMap[$repository][$rootLocationId])) { + $saRelationMap[$repository][$rootLocationId] = []; + } + + $saRelationMap[$repository][$rootLocationId][] = $siteAccessName; + } + + $siteAccessName = $siteAccess->name; + $repository = $this->configResolver->getParameter('repository', 'ibexa.site_access.config', $siteAccessName); + $rootLocationId = $this->configResolver->getParameter('content.tree_root.location_id', 'ibexa.site_access.config', $siteAccessName); + + return $saRelationMap[$repository][$rootLocationId]; + } +} + +class_alias(SiteAccessService::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessService'); diff --git a/src/lib/MVC/Symfony/SiteAccess/SiteAccessServiceInterface.php b/src/lib/MVC/Symfony/SiteAccess/SiteAccessServiceInterface.php new file mode 100644 index 0000000000..2bd1dd7342 --- /dev/null +++ b/src/lib/MVC/Symfony/SiteAccess/SiteAccessServiceInterface.php @@ -0,0 +1,40 @@ + $whatIsWrong, + ] + ); + } +} + +class_alias(InvalidResponseException::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Exception\InvalidResponseException'); diff --git a/src/lib/MVC/Symfony/Templating/Exception/MissingFieldBlockException.php b/src/lib/MVC/Symfony/Templating/Exception/MissingFieldBlockException.php new file mode 100644 index 0000000000..1a80329451 --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Exception/MissingFieldBlockException.php @@ -0,0 +1,15 @@ +attributes->get('_route') === UrlAliasRouter::URL_ALIAS_ROUTE_NAME) { return $this->router ->generate( - '_ez_content_view', + 'ibexa.content.view', [ 'contentId' => $request->attributes->get('contentId'), 'locationId' => $request->attributes->get('locationId'), @@ -130,7 +130,7 @@ public function getSystemUriString() /** * Returns the root location. * - * @return \eZ\Publish\API\Repository\Values\Content\Location + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location */ public function getRootLocation() { @@ -164,10 +164,12 @@ public function getAvailableLanguages() /** * Returns the config resolver. * - * @return \eZ\Publish\Core\MVC\ConfigResolverInterface + * @return \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ public function getConfigResolver() { return $this->configResolver; } } + +class_alias(GlobalHelper::class, 'eZ\Publish\Core\MVC\Symfony\Templating\GlobalHelper'); diff --git a/src/lib/MVC/Symfony/Templating/RenderContentStrategy.php b/src/lib/MVC/Symfony/Templating/RenderContentStrategy.php new file mode 100644 index 0000000000..fead09659d --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/RenderContentStrategy.php @@ -0,0 +1,52 @@ +supports($valueObject)) { + throw new InvalidArgumentException( + 'valueObject', + 'Must be a type of ' . Location::class + ); + } + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $content */ + $content = $valueObject; + + $currentRequest = $this->requestStack->getCurrentRequest(); + $controllerReference = new ControllerReference('ibexa_content::viewAction', [ + 'contentId' => $content->id, + 'viewType' => $options->get('viewType', self::DEFAULT_VIEW_TYPE), + ]); + + $renderer = $this->getFragmentRenderer($options->get('method', $this->defaultRenderer)); + + return $renderer->render($controllerReference, $currentRequest)->getContent(); + } +} + +class_alias(RenderContentStrategy::class, 'eZ\Publish\Core\MVC\Symfony\Templating\RenderContentStrategy'); diff --git a/src/lib/MVC/Symfony/Templating/RenderLocationStrategy.php b/src/lib/MVC/Symfony/Templating/RenderLocationStrategy.php new file mode 100644 index 0000000000..15b794d85e --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/RenderLocationStrategy.php @@ -0,0 +1,53 @@ +supports($valueObject)) { + throw new InvalidArgumentException( + 'valueObject', + 'Must be a type of ' . Location::class + ); + } + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $location */ + $location = $valueObject; + $content = $location->getContent(); + + $currentRequest = $this->requestStack->getCurrentRequest(); + $controllerReference = new ControllerReference('ibexa_content::viewAction', [ + 'contentId' => $content->id, + 'locationId' => $location->id, + 'viewType' => $options->get('viewType', self::DEFAULT_VIEW_TYPE), + ]); + + $renderer = $this->getFragmentRenderer($options->get('method', $this->defaultRenderer)); + + return $renderer->render($controllerReference, $currentRequest)->getContent(); + } +} + +class_alias(RenderLocationStrategy::class, 'eZ\Publish\Core\MVC\Symfony\Templating\RenderLocationStrategy'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/RenderOptions.php b/src/lib/MVC/Symfony/Templating/RenderOptions.php similarity index 85% rename from eZ/Publish/Core/MVC/Symfony/Templating/RenderOptions.php rename to src/lib/MVC/Symfony/Templating/RenderOptions.php index c0bae1df9f..1abf2f7a36 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/RenderOptions.php +++ b/src/lib/MVC/Symfony/Templating/RenderOptions.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Templating; +namespace Ibexa\Core\MVC\Symfony\Templating; -use eZ\Publish\SPI\Options\MutableOptionsBag; +use Ibexa\Contracts\Core\Options\MutableOptionsBag; final class RenderOptions implements MutableOptionsBag { @@ -57,3 +57,5 @@ public function remove(string $key): void unset($this->options[$key]); } } + +class_alias(RenderOptions::class, 'eZ\Publish\Core\MVC\Symfony\Templating\RenderOptions'); diff --git a/src/lib/MVC/Symfony/Templating/RenderStrategy.php b/src/lib/MVC/Symfony/Templating/RenderStrategy.php new file mode 100644 index 0000000000..c709a851ee --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/RenderStrategy.php @@ -0,0 +1,52 @@ +strategies = $strategies; + } + + public function supports(ValueObject $valueObject): bool + { + foreach ($this->strategies as $strategy) { + if ($strategy->supports($valueObject)) { + return true; + } + } + + return false; + } + + public function render(ValueObject $valueObject, RenderOptions $options): string + { + foreach ($this->strategies as $strategy) { + if ($strategy->supports($valueObject)) { + return $strategy->render($valueObject, $options); + } + } + + throw new InvalidArgumentException('valueObject', sprintf( + "Method '%s' is not supported for %s.", + $options->get('method'), + get_class($valueObject) + )); + } +} + +class_alias(RenderStrategy::class, 'eZ\Publish\Core\MVC\Symfony\Templating\RenderStrategy'); diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php new file mode 100644 index 0000000000..348a7cd7e4 --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtension.php @@ -0,0 +1,330 @@ +repository = $repository; + $this->translationHelper = $translationHelper; + $this->fieldHelper = $fieldHelper; + $this->fieldsGroupsList = $fieldsGroupsList; + $this->logger = $logger; + } + + /** + * Returns a list of functions to add to the existing list. + * + * @return array + */ + public function getFunctions() + { + return [ + new TwigFunction( + 'ez_content_name', + [$this, 'getTranslatedContentName'], + [ + 'deprecated' => '4.0', + 'alternative' => 'ibexa_content_name', + ] + ), + new TwigFunction( + 'ibexa_content_name', + [$this, 'getTranslatedContentName'] + ), + new TwigFunction( + 'ez_field_value', + [$this, 'getTranslatedFieldValue'], + [ + 'deprecated' => '4.0', + 'alternative' => 'ibexa_field_value', + ] + ), + new TwigFunction( + 'ibexa_field_value', + [$this, 'getTranslatedFieldValue'] + ), + new TwigFunction( + 'ez_field', + [$this, 'getTranslatedField'], + [ + 'deprecated' => '4.0', + 'alternative' => 'ibexa_field', + ] + ), + new TwigFunction( + 'ibexa_field', + [$this, 'getTranslatedField'] + ), + new TwigFunction( + 'ez_field_is_empty', + [$this, 'isFieldEmpty'], + [ + 'deprecated' => '4.0', + 'alternative' => 'ibexa_field_is_empty', + ] + ), + new TwigFunction( + 'ibexa_has_field', + [$this, 'hasField'] + ), + new TwigFunction( + 'ibexa_field_is_empty', + [$this, 'isFieldEmpty'] + ), + new TwigFunction( + 'ez_field_name', + [$this, 'getTranslatedFieldDefinitionName'], + [ + 'deprecated' => '4.0', + 'alternative' => 'ibexa_field_name', + ] + ), + new TwigFunction( + 'ibexa_field_name', + [$this, 'getTranslatedFieldDefinitionName'] + ), + new TwigFunction( + 'ez_field_description', + [$this, 'getTranslatedFieldDefinitionDescription'], + [ + 'deprecated' => '4.0', + 'alternative' => 'ibexa_field_description', + ] + ), + new TwigFunction( + 'ibexa_field_description', + [$this, 'getTranslatedFieldDefinitionDescription'] + ), + new TwigFunction( + 'ibexa_field_group_name', + [$this, 'getFieldGroupName'] + ), + new TwigFunction( + 'ez_content_field_identifier_first_filled_image', + [$this, 'getFirstFilledImageFieldIdentifier'], + [ + 'deprecated' => '4.0', + 'alternative' => 'ibexa_content_field_identifier_first_filled_image', + ] + ), + new TwigFunction( + 'ibexa_content_field_identifier_first_filled_image', + [$this, 'getFirstFilledImageFieldIdentifier'] + ), + ]; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $content Must be a valid Content or ContentInfo object. + * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType When $content is not a valid Content or ContentInfo object. + * + * @return string + */ + public function getTranslatedContentName(ValueObject $content, $forcedLanguage = null) + { + if ($content instanceof Content) { + return $this->translationHelper->getTranslatedContentName($content, $forcedLanguage); + } elseif ($content instanceof ContentInfo) { + return $this->translationHelper->getTranslatedContentNameByContentInfo($content, $forcedLanguage); + } + + throw new InvalidArgumentType( + '$content', + sprintf('%s or %s', Content::class, ContentInfo::class), + $content + ); + } + + /** + * Returns the translated field, very similar to getTranslatedFieldValue but this returns the whole field. + * To be used with ibexa_image_alias for example, which requires the whole field. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param string $fieldDefIdentifier Identifier for the field we want to get. + * @param string $forcedLanguage Locale we want the field in (e.g. "cro-HR"). Null by default (takes current locale). + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field + */ + public function getTranslatedField(Content $content, $fieldDefIdentifier, $forcedLanguage = null) + { + return $this->translationHelper->getTranslatedField($content, $fieldDefIdentifier, $forcedLanguage); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param string $fieldDefIdentifier Identifier for the field we want to get the value from. + * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale). + * + * @return mixed A primitive type or a field type Value object depending on the field type. + */ + public function getTranslatedFieldValue(Content $content, $fieldDefIdentifier, $forcedLanguage = null) + { + return $this->translationHelper->getTranslatedField($content, $fieldDefIdentifier, $forcedLanguage)->value; + } + + /** + * Gets name of a FieldDefinition name by loading ContentType based on Content/ContentInfo object. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $content Must be Content or ContentInfo object + * @param string $fieldDefIdentifier Identifier for the field we want to get the name from + * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType When $content is not a valid Content object. + * + * @return string|null + */ + public function getTranslatedFieldDefinitionName(ValueObject $content, $fieldDefIdentifier, $forcedLanguage = null) + { + if ($contentType = $this->getContentType($content)) { + return $this->translationHelper->getTranslatedFieldDefinitionProperty( + $contentType, + $fieldDefIdentifier, + 'name', + $forcedLanguage + ); + } + + throw new InvalidArgumentType('$content', 'Content|ContentInfo', $content); + } + + /** + * Gets name of a FieldDefinition description by loading ContentType based on Content/ContentInfo object. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $content Must be Content or ContentInfo object + * @param string $fieldDefIdentifier Identifier for the field we want to get the name from + * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). Null by default (takes current locale) + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType When $content is not a valid Content object. + * + * @return string|null + */ + public function getTranslatedFieldDefinitionDescription(ValueObject $content, $fieldDefIdentifier, $forcedLanguage = null) + { + if ($contentType = $this->getContentType($content)) { + return $this->translationHelper->getTranslatedFieldDefinitionProperty( + $contentType, + $fieldDefIdentifier, + 'description', + $forcedLanguage + ); + } + + throw new InvalidArgumentType('$content', 'Content|ContentInfo', $content); + } + + public function hasField(Content $content, string $fieldDefIdentifier): bool + { + return $content->getContentType()->hasFieldDefinition($fieldDefIdentifier); + } + + public function getFieldGroupName(string $identifier): ?string + { + return $this->fieldsGroupsList->getGroups()[$identifier] ?? null; + } + + /** + * Checks if a given field is considered empty. + * This method accepts field as Objects or by identifiers. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field|string $fieldDefIdentifier Field or Field Identifier to + * get the value from. + * @param string $forcedLanguage Locale we want the content name translation in (e.g. "fre-FR"). + * Null by default (takes current locale). + * + * @return bool + */ + public function isFieldEmpty(Content $content, $fieldDefIdentifier, $forcedLanguage = null) + { + if ($fieldDefIdentifier instanceof Field) { + $fieldDefIdentifier = $fieldDefIdentifier->fieldDefIdentifier; + } + + return $this->fieldHelper->isFieldEmpty($content, $fieldDefIdentifier, $forcedLanguage); + } + + /** + * Get ContentType by Content/ContentInfo. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|\Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $content + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType|null + */ + private function getContentType(ValueObject $content) + { + if ($content instanceof Content) { + return $this->repository->getContentTypeService()->loadContentType( + $content->getVersionInfo()->getContentInfo()->contentTypeId + ); + } elseif ($content instanceof ContentInfo) { + return $this->repository->getContentTypeService()->loadContentType($content->contentTypeId); + } + } + + public function getFirstFilledImageFieldIdentifier(Content $content) + { + foreach ($content->getFieldsByLanguage() as $field) { + $fieldTypeIdentifier = $content->getContentType() + ->getFieldDefinition($field->fieldDefIdentifier) + ->fieldTypeIdentifier; + + if ($fieldTypeIdentifier !== 'ezimage') { + continue; + } + + if ($this->fieldHelper->isFieldEmpty($content, $field->fieldDefIdentifier)) { + continue; + } + + return $field->fieldDefIdentifier; + } + + return null; + } +} + +class_alias(ContentExtension::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\ContentExtension'); diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/CoreExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/CoreExtension.php new file mode 100644 index 0000000000..86ecb6ad85 --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/CoreExtension.php @@ -0,0 +1,36 @@ +globalHelper = $globalHelper; + } + + /** + * @return array + */ + public function getGlobals(): array + { + return [ + /** @deprecated ezplatform is deprecated since 4.0, use ibexa instead */ + 'ezplatform' => $this->globalHelper, + 'ibexa' => $this->globalHelper, + ]; + } +} + +class_alias(CoreExtension::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\CoreExtension'); diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/DataAttributesExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/DataAttributesExtension.php new file mode 100644 index 0000000000..4e6b1c170c --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/DataAttributesExtension.php @@ -0,0 +1,67 @@ + ['html'], + 'deprecated' => '4.0', + 'alternative' => 'ibexa_data_attributes_serialize', + ] + ), + new TwigFilter( + 'ibexa_data_attributes_serialize', + [$this, 'serializeDataAttributes'], + ['is_safe' => ['html']] + ), + ]; + } + + /** + * Processes an associative list of data attributes and returns them as HTML attributes list + * in the form of data-="". + * + * @param array $dataAttributes + * + * @return string + */ + public function serializeDataAttributes(array $dataAttributes): string + { + $result = ''; + foreach ($dataAttributes as $attributeName => $attributeValue) { + if (!\is_string($attributeValue)) { + $attributeValue = json_encode($attributeValue); + } + + $result .= sprintf('data-%s="%s" ', $attributeName, htmlspecialchars($attributeValue)); + } + + return rtrim($result); + } +} + +class_alias(DataAttributesExtension::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\DataAttributesExtension'); diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php new file mode 100644 index 0000000000..a63c6f8e1b --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtension.php @@ -0,0 +1,211 @@ +fieldBlockRenderer = $fieldBlockRenderer; + $this->parameterProviderRegistry = $parameterProviderRegistry; + $this->translationHelper = $translationHelper; + } + + public function getFunctions() + { + $renderFieldCallable = function (Environment $environment, Content $content, $fieldIdentifier, array $params = []) { + $this->fieldBlockRenderer->setTwig($environment); + + return $this->renderField($content, $fieldIdentifier, $params); + }; + + $renderFieldDefinitionSettingsCallable = function (Environment $environment, FieldDefinition $fieldDefinition, array $params = []) { + $this->fieldBlockRenderer->setTwig($environment); + + return $this->renderFieldDefinitionSettings($fieldDefinition, $params); + }; + + return [ + new TwigFunction( + 'ez_render_field', + $renderFieldCallable, + [ + 'is_safe' => ['html'], + 'needs_environment' => true, + 'deprecated' => '4.0', + 'alternative' => 'ibexa_render_field', + ] + ), + new TwigFunction( + 'ibexa_render_field', + $renderFieldCallable, + ['is_safe' => ['html'], 'needs_environment' => true] + ), + new TwigFunction( + 'ez_render_field_definition_settings', + $renderFieldDefinitionSettingsCallable, + [ + 'is_safe' => ['html'], + 'needs_environment' => true, + 'deprecated' => '4.0', + 'alternative' => 'ibexa_render_field_definition_settings', + ] + ), + new TwigFunction( + 'ibexa_render_field_definition_settings', + $renderFieldDefinitionSettingsCallable, + ['is_safe' => ['html'], 'needs_environment' => true] + ), + ]; + } + + /** + * Renders the HTML for the settings for the given field definition + * $definition. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition + * + * @return string + */ + public function renderFieldDefinitionSettings(FieldDefinition $fieldDefinition, array $params = []) + { + return $this->fieldBlockRenderer->renderFieldDefinitionView($fieldDefinition, $params); + } + + /** + * Renders the HTML for a given field. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param string $fieldIdentifier Identifier for the field we want to render + * @param array $params An array of parameters to pass to the field view + * + * @return string The HTML markup + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + */ + public function renderField(Content $content, $fieldIdentifier, array $params = []) + { + $field = $this->translationHelper->getTranslatedField($content, $fieldIdentifier, isset($params['lang']) ? $params['lang'] : null); + if (!$field instanceof Field) { + throw new InvalidArgumentException( + '$fieldIdentifier', + "'{$fieldIdentifier}' Field does not exist in Content item {$content->contentInfo->id} '{$content->contentInfo->name}'" + ); + } + + $params = $this->getRenderFieldBlockParameters($content, $field, $params); + $fieldTypeIdentifier = $this->getFieldTypeIdentifier($content, $field); + + return $this->fieldBlockRenderer->renderContentFieldView($field, $fieldTypeIdentifier, $params); + } + + /** + * Generates the array of parameter to pass to the field template. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field the Field to display + * @param array $params An array of parameters to pass to the field view + * + * @return array + */ + private function getRenderFieldBlockParameters(Content $content, Field $field, array $params = []) + { + // Merging passed parameters to default ones + $params += [ + 'parameters' => [], // parameters dedicated to template processing + 'attr' => [], // attributes to add on the enclosing HTML tags + ]; + + $versionInfo = $content->getVersionInfo(); + $contentInfo = $versionInfo->getContentInfo(); + $contentType = $content->getContentType(); + $fieldDefinition = $contentType->getFieldDefinition($field->fieldDefIdentifier); + // Adding Field, FieldSettings and ContentInfo objects to + // parameters to be passed to the template + $params += [ + 'field' => $field, + 'content' => $content, + 'contentInfo' => $contentInfo, + 'versionInfo' => $versionInfo, + 'fieldSettings' => $fieldDefinition->getFieldSettings(), + ]; + + // Adding field type specific parameters if any. + if ($this->parameterProviderRegistry->hasParameterProvider($fieldDefinition->fieldTypeIdentifier)) { + $params['parameters'] += $this->parameterProviderRegistry + ->getParameterProvider($fieldDefinition->fieldTypeIdentifier) + ->getViewParameters($field); + } + + // make sure we can easily add class="-field" to the + // generated HTML + if (isset($params['attr']['class'])) { + $params['attr']['class'] .= ' ' . $this->getFieldTypeIdentifier($content, $field) . '-field'; + } else { + $params['attr']['class'] = $this->getFieldTypeIdentifier($content, $field) . '-field'; + } + + return $params; + } + + /** + * Returns the field type identifier for $field. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + * + * @return string + */ + private function getFieldTypeIdentifier(Content $content, Field $field) + { + $contentType = $content->getContentType(); + $key = $contentType->id . ' ' . $field->fieldDefIdentifier; + + if (!isset($this->fieldTypeIdentifiers[$key])) { + $this->fieldTypeIdentifiers[$key] = $contentType + ->getFieldDefinition($field->fieldDefIdentifier) + ->fieldTypeIdentifier; + } + + return $this->fieldTypeIdentifiers[$key]; + } +} + +class_alias(FieldRenderingExtension::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\FieldRenderingExtension'); diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php new file mode 100644 index 0000000000..9c0d700ec7 --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtension.php @@ -0,0 +1,123 @@ +translator = $translator; + $this->suffixes = $suffixes; + $this->configResolver = $configResolver; + $this->localeConverter = $localeConverter; + } + + private function getLocale() + { + foreach ($this->configResolver->getParameter('languages') as $locale) { + $convertedLocale = $this->localeConverter->convertToPOSIX($locale); + if ($convertedLocale !== null) { + return $convertedLocale; + } + } + + return Locale::getDefault(); + } + + /** + * Returns a list of filters to add to the existing list. + * + * @return array An array of filters + */ + public function getFilters() + { + return [ + new TwigFilter( + 'ez_file_size', + [$this, 'sizeFilter'], + [ + 'deprecated' => '4.0', + 'alternative' => 'ibexa_file_size', + ] + ), + new TwigFilter( + 'ibexa_file_size', + [$this, 'sizeFilter'] + ), + ]; + } + + /** + * Returns the binary file size, $precision will determine the decimal number precision, + * and the Locale will alter the format of the result by choosing between coma or point pattern. + * + * @param int $number + * @param int $precision + * + * @return string + */ + public function sizeFilter($number, $precision) + { + $mod = 1024; + $index = count($this->suffixes); + if ($number < ($mod ** $index)) { + for ($i = 0; $number >= $mod; ++$i) { + $number /= $mod; + } + } else { + $number /= $mod ** ($index - 1); + $i = ($index - 1); + } + $formatter = new NumberFormatter($this->getLocale(), NumberFormatter::PATTERN_DECIMAL); + $formatter->setPattern( + $formatter->getPattern() . ' ' . $this->translator->trans(/** @Ignore */$this->suffixes[$i]) + ); + $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $precision); + + return $formatter->format($number); + } +} + +class_alias(FileSizeExtension::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\FileSizeExtension'); diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/ImageExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/ImageExtension.php new file mode 100644 index 0000000000..0a85394329 --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/ImageExtension.php @@ -0,0 +1,112 @@ +imageVariationService = $imageVariationService; + $this->assetMapper = $assetMapper; + } + + public function getFunctions() + { + return [ + new TwigFunction( + 'ez_image_alias', + [$this, 'getImageVariation'], + [ + 'is_safe' => ['html'], + 'deprecated' => '4.0', + 'alternative' => 'ibexa_image_alias', + ] + ), + new TwigFunction( + 'ibexa_image_alias', + [$this, 'getImageVariation'], + ['is_safe' => ['html']] + ), + new TwigFunction( + 'ez_content_field_identifier_image_asset', + [$this, 'getImageAssetContentFieldIdentifier'], + [ + 'is_safe' => ['html'], + 'deprecated' => '4.0', + 'alternative' => 'ibexa_content_field_identifier_image_asset', + ] + ), + new TwigFunction( + 'ibexa_content_field_identifier_image_asset', + [$this, 'getImageAssetContentFieldIdentifier'], + ['is_safe' => ['html']] + ), + ]; + } + + /** + * Returns the image variation object for $field/$versionInfo. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + * @param string $variationName + * + * @return \Ibexa\Contracts\Core\Variation\Values\Variation|null + */ + public function getImageVariation(Field $field, VersionInfo $versionInfo, $variationName) + { + try { + return $this->imageVariationService->getVariation($field, $versionInfo, $variationName); + } catch (InvalidVariationException $e) { + if (isset($this->logger)) { + $this->logger->error("Couldn't get variation '{$variationName}' for image with id {$field->value->id}"); + } + } catch (SourceImageNotFoundException $e) { + if (isset($this->logger)) { + $this->logger->error( + "Couldn't create variation '{$variationName}' for image with id {$field->value->id} because source image can't be found" + ); + } + } catch (InvalidArgumentException $e) { + if (isset($this->logger)) { + $this->logger->error( + "Couldn't create variation '{$variationName}' for image with id {$field->value->id} because an image could not be created from the given input" + ); + } + } + } + + /** + * Return identifier of the field used to store Image Asset value. + * + * Typically used to create generic view of the Image Asset field. + * + * @return string + */ + public function getImageAssetContentFieldIdentifier(): string + { + return $this->assetMapper->getContentFieldIdentifier(); + } +} + +class_alias(ImageExtension::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\ImageExtension'); diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/QueryRenderingExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/QueryRenderingExtension.php new file mode 100644 index 0000000000..989fdb568c --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/QueryRenderingExtension.php @@ -0,0 +1,101 @@ +fragmentHandler = $fragmentHandler; + } + + public function getFunctions(): array + { + $typeCallable = function (string $type, array $options): ?string { + $this->assertTypeIsValid($type); + + return $this->fragmentHandler->render( + $this->createControllerReference($options) + ); + }; + + $typeAndRendererCallable = function (string $type, string $renderer, array $options): ?string { + $this->assertTypeIsValid($type); + + return $this->fragmentHandler->render( + $this->createControllerReference($options), + $renderer + ); + }; + + return [ + new TwigFunction( + 'ez_render_*_query', + $typeCallable, + [ + 'is_safe' => ['html'], + 'deprecated' => '4.0', + 'alternative' => 'ibexa_render_*_query', + ] + ), + new TwigFunction( + 'ibexa_render_*_query', + $typeCallable, + ['is_safe' => ['html']] + ), + new TwigFunction( + 'ez_render_*_query_*', + $typeAndRendererCallable, + [ + 'is_safe' => ['html'], + 'deprecated' => '4.0', + 'alternative' => 'ibexa_render_*_query_', + ] + ), + new TwigFunction( + 'ibexa_render_*_query_*', + $typeAndRendererCallable, + ['is_safe' => ['html']] + ), + ]; + } + + private function createControllerReference(array $options): ControllerReference + { + return new ControllerReference('ibexa_query_render::renderQuery', [ + 'options' => $options, + ]); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + private function assertTypeIsValid(string $type): void + { + if (!in_array($type, self::VALID_TYPES)) { + throw new InvalidArgumentException( + '$type', + 'Expected value to be of ' . implode(', ', self::VALID_TYPES) + ); + } + } +} + +class_alias(QueryRenderingExtension::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\QueryRenderingExtension'); diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/RenderContentExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/RenderContentExtension.php new file mode 100644 index 0000000000..9fa3ebaf9f --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/RenderContentExtension.php @@ -0,0 +1,69 @@ +renderContentStrategy = $renderContentStrategy; + $this->eventDispatcher = $eventDispatcher; + } + + public function getFunctions(): array + { + return [ + new TwigFunction( + 'ez_render_content', + [$this, 'renderContent'], + [ + 'is_safe' => ['html'], + 'deprecated' => '4.0', + 'alternative' => 'ibexa_render_content', + ] + ), + new TwigFunction( + 'ibexa_render_content', + [$this, 'renderContent'], + ['is_safe' => ['html']] + ), + ]; + } + + public function renderContent(Content $content, array $options = []): string + { + $renderOptions = new RenderOptions($options); + $event = $this->eventDispatcher->dispatch( + new ResolveRenderOptionsEvent($renderOptions) + ); + + return $this->renderContentStrategy->render($content, $event->getRenderOptions()); + } +} + +class_alias(RenderContentExtension::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\RenderContentExtension'); diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/RenderExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/RenderExtension.php new file mode 100644 index 0000000000..c441f8b13c --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/RenderExtension.php @@ -0,0 +1,77 @@ +renderStrategy = $renderStrategy; + $this->eventDispatcher = $eventDispatcher; + } + + public function getFunctions(): array + { + return [ + new TwigFunction( + 'ez_render', + [$this, 'render'], + [ + 'is_safe' => ['html'], + 'deprecated' => '4.0', + 'alternative' => 'ibexa_render', + ] + ), + new TwigFunction( + 'ibexa_render', + [$this, 'render'], + ['is_safe' => ['html']] + ), + ]; + } + + public function render(ValueObject $valueObject, array $options = []): string + { + if (!$this->renderStrategy->supports($valueObject)) { + throw new InvalidArgumentException( + 'valueObject', + sprintf('%s is not supported.', get_class($valueObject)) + ); + } + + $renderOptions = new RenderOptions($options); + $event = $this->eventDispatcher->dispatch( + new ResolveRenderOptionsEvent($renderOptions) + ); + + return $this->renderStrategy->render($valueObject, $event->getRenderOptions()); + } +} + +class_alias(RenderExtension::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\RenderExtension'); diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/RenderLocationExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/RenderLocationExtension.php new file mode 100644 index 0000000000..ef7f24cd4b --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/RenderLocationExtension.php @@ -0,0 +1,69 @@ +renderLocationStrategy = $renderLocationStrategy; + $this->eventDispatcher = $eventDispatcher; + } + + public function getFunctions(): array + { + return [ + new TwigFunction( + 'ez_render_location', + [$this, 'renderLocation'], + [ + 'is_safe' => ['html'], + 'deprecated' => '4.0', + 'alternative' => 'ibexa_render_location', + ] + ), + new TwigFunction( + 'ibexa_render_location', + [$this, 'renderLocation'], + ['is_safe' => ['html']] + ), + ]; + } + + public function renderLocation(Location $location, array $options = []): string + { + $renderOptions = new RenderOptions($options); + $event = $this->eventDispatcher->dispatch( + new ResolveRenderOptionsEvent($renderOptions) + ); + + return $this->renderLocationStrategy->render($location, $event->getRenderOptions()); + } +} + +class_alias(RenderLocationExtension::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\RenderLocationExtension'); diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtension.php new file mode 100644 index 0000000000..ac5f0dfffd --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtension.php @@ -0,0 +1,183 @@ +routeReferenceGenerator = $routeReferenceGenerator; + $this->urlGenerator = $urlGenerator; + } + + public function getFunctions(): array + { + return [ + new TwigFunction( + 'ez_route', + [$this, 'getRouteReference'], + [ + 'deprecated' => '4.0', + 'alternative' => 'ibexa_route', + ] + ), + new TwigFunction( + 'ibexa_route', + [$this, 'getRouteReference'] + ), + new TwigFunction( + 'ez_path', + [$this, 'getPath'], + [ + 'is_safe_callback' => [$this, 'isUrlGenerationSafe'], + 'deprecated' => '4.0', + 'alternative' => 'ibexa_path', + ] + ), + new TwigFunction( + 'ibexa_path', + [$this, 'getPath'], + [ + 'is_safe_callback' => [$this, 'isUrlGenerationSafe'], + ] + ), + new TwigFunction( + 'ez_url', + [$this, 'getUrl'], + [ + 'is_safe_callback' => [$this, 'isUrlGenerationSafe'], + 'deprecated' => '4.0', + 'alternative' => 'ibexa_url', + ] + ), + new TwigFunction( + 'ibexa_url', + [$this, 'getUrl'], + [ + 'is_safe_callback' => [$this, 'isUrlGenerationSafe'], + ] + ), + ]; + } + + /** + * @param mixed $resource + * @param array $params + * + * @return \Ibexa\Core\MVC\Symfony\Routing\RouteReference + */ + public function getRouteReference($resource = null, $params = []): RouteReference + { + return $this->routeReferenceGenerator->generate($resource, $params); + } + + public function getPath(object $name, array $parameters = [], bool $relative = false): string + { + $referenceType = $relative ? UrlGeneratorInterface::RELATIVE_PATH : UrlGeneratorInterface::ABSOLUTE_PATH; + + return $this->tryGeneratingUrlForObject($name, $parameters, $referenceType); + } + + public function getUrl(object $name, array $parameters = [], bool $schemeRelative = false): string + { + $referenceType = $schemeRelative ? UrlGeneratorInterface::NETWORK_PATH : UrlGeneratorInterface::ABSOLUTE_URL; + + return $this->tryGeneratingUrlForObject($name, $parameters, $referenceType); + } + + /** + * @param array $parameters + */ + private function tryGeneratingUrlForObject(object $object, array $parameters, int $referenceType): string + { + try { + return $this->generateUrlForObject($object, $parameters, $referenceType); + } catch (NotFoundException $e) { + return ''; + } + } + + private function generateUrlForObject(object $object, array $parameters, int $referenceType): string + { + if ($object instanceof Location) { + $routeName = UrlAliasRouter::URL_ALIAS_ROUTE_NAME; + $parameters += [ + 'locationId' => $object->id, + ]; + } elseif ($object instanceof Content || $object instanceof ContentInfo) { + $routeName = UrlAliasRouter::URL_ALIAS_ROUTE_NAME; + $parameters += [ + 'contentId' => $object->id, + ]; + } elseif ($object instanceof ContentAwareInterface) { + $routeName = UrlAliasRouter::URL_ALIAS_ROUTE_NAME; + $parameters += [ + 'contentId' => $object->getContent()->getVersionInfo()->getContentInfo()->id, + ]; + } elseif ($object instanceof RouteReference) { + $routeName = $object->getRoute(); + $parameters += $object->getParams(); + } else { + $routeName = ''; + $parameters += [ + RouteObjectInterface::ROUTE_OBJECT => $object, + ]; + } + + return $this->urlGenerator->generate($routeName, $parameters, $referenceType); + } + + /** + * Determines at compile time whether the generated URL will be safe and thus + * saving the unneeded automatic escaping for performance reasons. + * + * @see \Symfony\Bridge\Twig\Extension\RoutingExtension::isUrlGenerationSafe + */ + public function isUrlGenerationSafe(Node $argsNode): array + { + // support named arguments + $paramsNode = $argsNode->hasNode('parameters') ? $argsNode->getNode('parameters') : ( + $argsNode->hasNode('1') ? $argsNode->getNode('1') : null + ); + + if (null === $paramsNode || $paramsNode instanceof ArrayExpression && \count($paramsNode) <= 2 && + (!$paramsNode->hasNode('1') || $paramsNode->getNode('1') instanceof ConstantExpression) + ) { + return ['html']; + } + + return []; + } +} + +class_alias(RoutingExtension::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\RoutingExtension'); diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/UserExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/UserExtension.php new file mode 100644 index 0000000000..b1734c98d6 --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/UserExtension.php @@ -0,0 +1,54 @@ +userService = $userService; + $this->permissionResolver = $permissionResolver; + } + + public function getFunctions(): array + { + return [ + new TwigFunction( + 'ibexa_current_user', + [$this, 'getCurrentUser'] + ), + new TwigFunction( + 'ibexa_is_current_user', + [$this, 'isCurrentUser'] + ), + ]; + } + + public function getCurrentUser(): User + { + return $this->userService->loadUser( + $this->permissionResolver->getCurrentUserReference()->getUserId() + ); + } + + public function isCurrentUser(User $user): bool + { + return $this->permissionResolver->getCurrentUserReference()->getUserId() === $user->getUserId(); + } +} diff --git a/src/lib/MVC/Symfony/Templating/Twig/Extension/UserPreferenceExtension.php b/src/lib/MVC/Symfony/Templating/Twig/Extension/UserPreferenceExtension.php new file mode 100644 index 0000000000..ec25562eef --- /dev/null +++ b/src/lib/MVC/Symfony/Templating/Twig/Extension/UserPreferenceExtension.php @@ -0,0 +1,32 @@ +userPreferenceService = $userPreferenceService; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function getUserPreferenceValue(string $identifier, string $default): string + { + try { + return $this->userPreferenceService->getUserPreference($identifier)->value; + } catch (NotFoundException $e) { + return $default; + } + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function hasUserPreference(string $identifier): bool + { + try { + $this->userPreferenceService->getUserPreference($identifier); + + return true; + } catch (NotFoundException $e) { + return false; + } + } +} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php b/src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php similarity index 90% rename from eZ/Publish/Core/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php rename to src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php index df2f14c45d..5475e30bb7 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php +++ b/src/lib/MVC/Symfony/Templating/Twig/FieldBlockRenderer.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig; +namespace Ibexa\Core\MVC\Symfony\Templating\Twig; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\MVC\Symfony\Templating\Exception\MissingFieldBlockException; -use eZ\Publish\Core\MVC\Symfony\Templating\FieldBlockRendererInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\MVC\Symfony\Templating\Exception\MissingFieldBlockException; +use Ibexa\Core\MVC\Symfony\Templating\FieldBlockRendererInterface; use Twig\Environment; use Twig\Template; @@ -39,7 +39,7 @@ class FieldBlockRenderer implements FieldBlockRendererInterface /** @var \Twig\Environment */ private $twig; - /** @var \eZ\Publish\Core\MVC\Symfony\Templating\Twig\ResourceProviderInterface */ + /** @var \Ibexa\Core\MVC\Symfony\Templating\Twig\ResourceProviderInterface */ private $resourceProvider; /** @@ -58,7 +58,7 @@ class FieldBlockRenderer implements FieldBlockRendererInterface /** * @param \Twig\Environment $twig - * @param \eZ\Publish\Core\MVC\Symfony\Templating\Twig\ResourceProviderInterface $resourceProvider + * @param \Ibexa\Core\MVC\Symfony\Templating\Twig\ResourceProviderInterface $resourceProvider * @param string|\Twig\Template $baseTemplate * @param array $blocks */ @@ -93,12 +93,12 @@ public function renderContentFieldEdit(Field $field, $fieldTypeIdentifier, array } /** - * @param \eZ\Publish\API\Repository\Values\Content\Field $field + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field * @param string $fieldTypeIdentifier * @param array $params * @param int $type Either self::VIEW or self::EDIT * - * @throws \eZ\Publish\Core\MVC\Symfony\Templating\Exception\MissingFieldBlockException If no template block can be found for $field + * @throws \Ibexa\Core\MVC\Symfony\Templating\Exception\MissingFieldBlockException If no template block can be found for $field * * @return string */ @@ -143,7 +143,7 @@ public function renderFieldDefinitionEdit(FieldDefinition $fieldDefinition, arra } /** - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition * @param array $params * @param int $type Either self::VIEW or self::EDIT * @@ -232,7 +232,7 @@ private function getBlocksByField($fieldTypeIdentifier, $type, $localTemplate = /** * Returns the template block for the settings of the field definition $definition. * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $definition + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $definition * @param int $type Either self::VIEW or self::EDIT * * @return array @@ -315,7 +315,7 @@ private function getRenderFieldDefinitionBlockName($fieldTypeIdentifier, $type): /** * @return array|\Twig\Template[] * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ private function getResources(string $resourceType): array { @@ -345,3 +345,5 @@ private function sortResources(array $resources): array return $resources; } } + +class_alias(FieldBlockRenderer::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\FieldBlockRenderer'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/ResourceProvider.php b/src/lib/MVC/Symfony/Templating/Twig/ResourceProvider.php similarity index 78% rename from eZ/Publish/Core/MVC/Symfony/Templating/Twig/ResourceProvider.php rename to src/lib/MVC/Symfony/Templating/Twig/ResourceProvider.php index 5e6573e7b0..2737ca7780 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/ResourceProvider.php +++ b/src/lib/MVC/Symfony/Templating/Twig/ResourceProvider.php @@ -6,16 +6,16 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig; +namespace Ibexa\Core\MVC\Symfony\Templating\Twig; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; /** * @internal */ final class ResourceProvider implements ResourceProviderInterface { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; public function __construct(ConfigResolverInterface $configResolver) @@ -43,3 +43,5 @@ public function getFieldDefinitionEditResources(): array return $this->configResolver->getParameter('fielddefinition_edit_templates'); } } + +class_alias(ResourceProvider::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\ResourceProvider'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/ResourceProviderInterface.php b/src/lib/MVC/Symfony/Templating/Twig/ResourceProviderInterface.php similarity index 80% rename from eZ/Publish/Core/MVC/Symfony/Templating/Twig/ResourceProviderInterface.php rename to src/lib/MVC/Symfony/Templating/Twig/ResourceProviderInterface.php index aa87ba65e4..b7bc1a9e33 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Twig/ResourceProviderInterface.php +++ b/src/lib/MVC/Symfony/Templating/Twig/ResourceProviderInterface.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Templating\Twig; +namespace Ibexa\Core\MVC\Symfony\Templating\Twig; interface ResourceProviderInterface { @@ -30,3 +30,5 @@ public function getFieldDefinitionViewResources(): array; */ public function getFieldDefinitionEditResources(): array; } + +class_alias(ResourceProviderInterface::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Twig\ResourceProviderInterface'); diff --git a/eZ/Publish/Core/MVC/Symfony/Translation/CatalogueMapperFileWriter.php b/src/lib/MVC/Symfony/Translation/CatalogueMapperFileWriter.php similarity index 96% rename from eZ/Publish/Core/MVC/Symfony/Translation/CatalogueMapperFileWriter.php rename to src/lib/MVC/Symfony/Translation/CatalogueMapperFileWriter.php index 9c7d8b4f40..ec2562f5c5 100644 --- a/eZ/Publish/Core/MVC/Symfony/Translation/CatalogueMapperFileWriter.php +++ b/src/lib/MVC/Symfony/Translation/CatalogueMapperFileWriter.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Translation; +namespace Ibexa\Core\MVC\Symfony\Translation; use JMS\TranslationBundle\Exception\InvalidArgumentException; use JMS\TranslationBundle\Model\Message; @@ -125,3 +125,5 @@ private function makeXliffMessage(Message $message) return $newMessage; } } + +class_alias(CatalogueMapperFileWriter::class, 'eZ\Publish\Core\MVC\Symfony\Translation\CatalogueMapperFileWriter'); diff --git a/src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php b/src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php new file mode 100644 index 0000000000..c975d605b3 --- /dev/null +++ b/src/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitor.php @@ -0,0 +1,131 @@ + */ + protected $methodsToExtractFrom = ['setMessageTemplate' => -1]; + + protected string $defaultDomain = 'ibexa_repository_exceptions'; + + private FileSourceFactory $fileSourceFactory; + + private NodeTraverser $traverser; + + private SplFileInfo $file; + + private MessageCatalogue $catalogue; + + private Node $previousNode; + + private DocParser $docParser; + + public function __construct(DocParser $docParser, FileSourceFactory $fileSourceFactory) + { + parent::__construct($docParser, $fileSourceFactory); + $this->fileSourceFactory = $fileSourceFactory; + $this->docParser = $docParser; + $this->traverser = new NodeTraverser(); + $this->traverser->addVisitor($this); + } + + public function enterNode(Node $node): void + { + $methodCallNodeName = null; + if ($node instanceof Node\Expr\MethodCall) { + $methodCallNodeName = $node->name instanceof Node\Identifier ? $node->name->name : $node->name; + } + if ( + !is_string($methodCallNodeName) + || !array_key_exists($methodCallNodeName, $this->methodsToExtractFrom) + ) { + $this->previousNode = $node; + + return; + } + + $ignore = $this->isIgnore($node); + + if (!$node->args[0]->value instanceof String_) { + if ($ignore) { + return; + } + + $message = sprintf('Can only extract the translation id from a scalar string, but got "%s". Please refactor your code to make it extractable, or add the doc comment /** @Ignore */ to this code element (in %s on line %d).', get_class($node->args[0]->value), $this->file, $node->args[0]->value->getLine()); + + $this->logger->error($message); + + return; + } + + $id = $node->args[0]->value->value; + + $message = new Message($id, $this->defaultDomain); + $message->addSource($this->fileSourceFactory->create($this->file, $node->getLine())); + $this->catalogue->add($message); + } + + public function visitPhpFile(SplFileInfo $file, MessageCatalogue $catalogue, array $ast): void + { + $this->file = $file; + $this->catalogue = $catalogue; + $this->traverser->traverse($ast); + } + + private function getDocCommentForNode(Node $node): ?string + { + if (null !== $comment = $node->args[0]->getDocComment()) { + return $comment->getText(); + } + + if (null !== $comment = $node->getDocComment()) { + return $comment->getText(); + } + + if (null !== $this->previousNode && $this->previousNode->getDocComment() !== null) { + $comment = $this->previousNode->getDocComment(); + + return is_object($comment) ? $comment->getText() : $comment; + } + + return null; + } + + private function isIgnore($node): bool + { + if (null !== $docComment = $this->getDocCommentForNode($node)) { + $annotations = $this->docParser->parse( + $docComment, + 'file ' . $this->file . ' near line ' . $node->getLine() + ); + foreach ($annotations as $annot) { + if ($annot instanceof Ignore) { + return true; + } + } + } + + return false; + } +} + +class_alias(ExceptionMessageTemplateFileVisitor::class, 'eZ\Publish\Core\MVC\Symfony\Translation\ExceptionMessageTemplateFileVisitor'); diff --git a/eZ/Publish/Core/MVC/Symfony/Translation/FieldTypesTranslationExtractor.php b/src/lib/MVC/Symfony/Translation/FieldTypesTranslationExtractor.php similarity index 76% rename from eZ/Publish/Core/MVC/Symfony/Translation/FieldTypesTranslationExtractor.php rename to src/lib/MVC/Symfony/Translation/FieldTypesTranslationExtractor.php index cb23ed836d..cb30455b20 100644 --- a/eZ/Publish/Core/MVC/Symfony/Translation/FieldTypesTranslationExtractor.php +++ b/src/lib/MVC/Symfony/Translation/FieldTypesTranslationExtractor.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Translation; +namespace Ibexa\Core\MVC\Symfony\Translation; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; +use Ibexa\Core\FieldType\FieldTypeRegistry; use JMS\TranslationBundle\Model\Message; use JMS\TranslationBundle\Model\MessageCatalogue; use JMS\TranslationBundle\Translation\ExtractorInterface; @@ -16,7 +16,7 @@ */ class FieldTypesTranslationExtractor implements ExtractorInterface { - /** @var \eZ\Publish\Core\FieldType\FieldTypeRegistry */ + /** @var \Ibexa\Core\FieldType\FieldTypeRegistry */ private $fieldTypeRegistry; public function __construct(FieldTypeRegistry $fieldTypeRegistry) @@ -31,7 +31,7 @@ public function extract() $catalogue->add( new Message( $fieldTypeIdentifier . '.name', - 'fieldtypes' + 'ibexa_fieldtypes' ) ); } @@ -39,3 +39,5 @@ public function extract() return $catalogue; } } + +class_alias(FieldTypesTranslationExtractor::class, 'eZ\Publish\Core\MVC\Symfony\Translation\FieldTypesTranslationExtractor'); diff --git a/eZ/Publish/Core/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php b/src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php similarity index 96% rename from eZ/Publish/Core/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php rename to src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php index b7d55a425a..d7ffbeec1f 100644 --- a/eZ/Publish/Core/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php +++ b/src/lib/MVC/Symfony/Translation/TranslatableExceptionsFileVisitor.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Translation; +namespace Ibexa\Core\MVC\Symfony\Translation; use Doctrine\Common\Annotations\DocParser; use JMS\TranslationBundle\Annotation\Desc; @@ -51,7 +51,7 @@ class TranslatableExceptionsFileVisitor implements LoggerAwareInterface, FileVis private $previousNode; /** @var string */ - protected $defaultDomain = 'repository_exceptions'; + protected $defaultDomain = 'ibexa_repository_exceptions'; /** * Methods and "domain" parameter offset to extract from PHP code. @@ -225,3 +225,5 @@ private function getDocCommentForNode(Node $node) return null; } } + +class_alias(TranslatableExceptionsFileVisitor::class, 'eZ\Publish\Core\MVC\Symfony\Translation\TranslatableExceptionsFileVisitor'); diff --git a/eZ/Publish/Core/MVC/Symfony/Translation/ValidationErrorFileVisitor.php b/src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php similarity index 96% rename from eZ/Publish/Core/MVC/Symfony/Translation/ValidationErrorFileVisitor.php rename to src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php index 5d4c4e52dd..81d6ace9e5 100644 --- a/eZ/Publish/Core/MVC/Symfony/Translation/ValidationErrorFileVisitor.php +++ b/src/lib/MVC/Symfony/Translation/ValidationErrorFileVisitor.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Translation; +namespace Ibexa\Core\MVC\Symfony\Translation; use Doctrine\Common\Annotations\DocParser; use JMS\TranslationBundle\Annotation\Desc; @@ -51,7 +51,7 @@ class ValidationErrorFileVisitor implements LoggerAwareInterface, FileVisitorInt private $previousNode; /** @var string */ - protected $defaultDomain = 'repository_exceptions'; + protected $defaultDomain = 'ibexa_repository_exceptions'; /** * Methods and "domain" parameter offset to extract from PHP code. @@ -225,3 +225,5 @@ private function getDocCommentForNode(Node $node) return null; } } + +class_alias(ValidationErrorFileVisitor::class, 'eZ\Publish\Core\MVC\Symfony\Translation\ValidationErrorFileVisitor'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/BaseView.php b/src/lib/MVC/Symfony/View/BaseView.php similarity index 88% rename from eZ/Publish/Core/MVC/Symfony/View/BaseView.php rename to src/lib/MVC/Symfony/View/BaseView.php index 75a85411ce..195274bbc2 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/BaseView.php +++ b/src/lib/MVC/Symfony/View/BaseView.php @@ -4,15 +4,19 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\View; +namespace Ibexa\Core\MVC\Symfony\View; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Controller\ControllerReference; abstract class BaseView implements View { - /** @var string|\Closure */ + /** + * @phpstan-var string|(\Closure(array):string) + * + * @var string|\Closure + */ protected $templateIdentifier; /** @var array */ @@ -34,11 +38,13 @@ abstract class BaseView implements View private $isCacheEnabled = true; /** + * @phpstan-param string|(\Closure(array):string) $templateIdentifier + * * @param string|\Closure $templateIdentifier Valid path to the template. Can also be a closure. * @param string $viewType * @param array $parameters Hash of parameters to pass to the template/closure. * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType */ public function __construct($templateIdentifier = null, array $parameters = [], $viewType = 'full') { @@ -108,9 +114,11 @@ public function getParameter($parameterName) } /** + * @phpstan-param string|(\Closure(array):string) $templateIdentifier + * * @param string|\Closure $templateIdentifier * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType */ public function setTemplateIdentifier($templateIdentifier) { @@ -123,6 +131,8 @@ public function setTemplateIdentifier($templateIdentifier) /** * @return string|\Closure + * + * @phpstan-return string|(\Closure(array):string) */ public function getTemplateIdentifier() { @@ -206,3 +216,5 @@ public function isCacheEnabled() return $this->isCacheEnabled; } } + +class_alias(BaseView::class, 'eZ\Publish\Core\MVC\Symfony\View\BaseView'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/Builder/ContentViewBuilder.php b/src/lib/MVC/Symfony/View/Builder/ContentViewBuilder.php similarity index 80% rename from eZ/Publish/Core/MVC/Symfony/View/Builder/ContentViewBuilder.php rename to src/lib/MVC/Symfony/View/Builder/ContentViewBuilder.php index 0059ff77f1..8f2c36ba7b 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/Builder/ContentViewBuilder.php +++ b/src/lib/MVC/Symfony/View/Builder/ContentViewBuilder.php @@ -4,22 +4,22 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\View\Builder; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\Core\Helper\ContentInfoLocationLoader; -use eZ\Publish\Core\MVC\Exception\HiddenLocationException; -use eZ\Publish\Core\MVC\Symfony\Controller\Content\PreviewController; -use eZ\Publish\Core\MVC\Symfony\View\Configurator; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; -use eZ\Publish\Core\MVC\Symfony\View\EmbedView; -use eZ\Publish\Core\MVC\Symfony\View\ParametersInjector; +namespace Ibexa\Core\MVC\Symfony\View\Builder; + +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\UnauthorizedException; +use Ibexa\Core\Helper\ContentInfoLocationLoader; +use Ibexa\Core\MVC\Exception\HiddenLocationException; +use Ibexa\Core\MVC\Symfony\Controller\Content\PreviewController; +use Ibexa\Core\MVC\Symfony\View\Configurator; +use Ibexa\Core\MVC\Symfony\View\ContentView; +use Ibexa\Core\MVC\Symfony\View\EmbedView; +use Ibexa\Core\MVC\Symfony\View\ParametersInjector; use Symfony\Component\HttpFoundation\RequestStack; /** @@ -27,16 +27,16 @@ */ class ContentViewBuilder implements ViewBuilder { - /** @var \eZ\Publish\API\Repository\Repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository */ private $repository; - /** @var \eZ\Publish\API\Repository\PermissionResolver */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ private $permissionResolver; - /** @var \eZ\Publish\Core\MVC\Symfony\View\Configurator */ + /** @var \Ibexa\Core\MVC\Symfony\View\Configurator */ private $viewConfigurator; - /** @var \eZ\Publish\Core\MVC\Symfony\View\ParametersInjector */ + /** @var \Ibexa\Core\MVC\Symfony\View\ParametersInjector */ private $viewParametersInjector; /** @var \Symfony\Component\HttpFoundation\RequestStack */ @@ -49,7 +49,7 @@ class ContentViewBuilder implements ViewBuilder */ private $defaultTemplates; - /** @var \eZ\Publish\Core\Helper\ContentInfoLocationLoader */ + /** @var \Ibexa\Core\Helper\ContentInfoLocationLoader */ private $locationLoader; public function __construct( @@ -69,18 +69,18 @@ public function __construct( public function matches($argument) { - return strpos($argument, 'ez_content:') !== false; + return strpos($argument, 'ibexa_content:') !== false; } /** * @param array $parameters * - * @return \eZ\Publish\Core\MVC\Symfony\View\ContentView|\eZ\Publish\Core\MVC\Symfony\View\View + * @return \Ibexa\Core\MVC\Symfony\View\ContentView|\Ibexa\Core\MVC\Symfony\View\View * If both contentId and locationId parameters are missing * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException * If both contentId and locationId parameters are missing - * @throws \eZ\Publish\Core\Base\Exceptions\UnauthorizedException + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException */ public function buildView(array $parameters) { @@ -180,9 +180,9 @@ private function resolveMainRequestLanguageCode(): ?string * * @param mixed $contentId * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content * - * @throws \eZ\Publish\Core\Base\Exceptions\UnauthorizedException + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException */ private function loadContent($contentId, ?string $languageCode = null) { @@ -198,11 +198,11 @@ private function loadContent($contentId, ?string $languageCode = null) * if provided. * * @param mixed $contentId - * @param \eZ\Publish\API\Repository\Values\Content\Location $location + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location|null $location * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content * - * @throws \eZ\Publish\Core\Base\Exceptions\UnauthorizedException + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException */ private function loadEmbeddedContent($contentId, Location $location = null, ?string $languageCode = null) { @@ -236,7 +236,7 @@ static function (Repository $repository) use ($contentId, $languageCode) { * * @param $locationId * - * @return \eZ\Publish\API\Repository\Values\Content\Location + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location */ private function loadLocation($locationId) { @@ -259,8 +259,8 @@ static function (Repository $repository) use ($locationId) { /** * Checks if a user can read a content, or view it as an embed. * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content - * @param \eZ\Publish\API\Repository\Values\Content\Location $location + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location|null $location * @param bool $isEmbed * * @return bool @@ -284,7 +284,7 @@ private function canRead(Content $content, Location $location = null, bool $isEm */ private function isEmbed($parameters) { - if ($parameters['_controller'] === 'ez_content:embedAction') { + if ($parameters['_controller'] === 'ibexa_content:embedAction') { return true; } if (\in_array($parameters['viewType'], ['embed', 'embed-inline'])) { @@ -294,3 +294,5 @@ private function isEmbed($parameters) return false; } } + +class_alias(ContentViewBuilder::class, 'eZ\Publish\Core\MVC\Symfony\View\Builder\ContentViewBuilder'); diff --git a/src/lib/MVC/Symfony/View/Builder/ParametersFilter/RequestAttributes.php b/src/lib/MVC/Symfony/View/Builder/ParametersFilter/RequestAttributes.php new file mode 100644 index 0000000000..82098c6341 --- /dev/null +++ b/src/lib/MVC/Symfony/View/Builder/ParametersFilter/RequestAttributes.php @@ -0,0 +1,40 @@ + 'addRequestAttributes']; + } + + /** + * Adds all the request attributes to the parameters. + * + * @param \Ibexa\Core\MVC\Symfony\View\Event\FilterViewBuilderParametersEvent $e + */ + public function addRequestAttributes(FilterViewBuilderParametersEvent $e) + { + $parameterBag = $e->getParameters(); + $parameterBag->add($e->getRequest()->attributes->all()); + + // maybe this should be in its own listener ? The ViewBuilder needs it. + if (!$parameterBag->has('viewType')) { + $parameterBag->add(['viewType' => null]); + } + } +} + +class_alias(RequestAttributes::class, 'eZ\Publish\Core\MVC\Symfony\View\Builder\ParametersFilter\RequestAttributes'); diff --git a/src/lib/MVC/Symfony/View/Builder/Registry/ControllerMatch.php b/src/lib/MVC/Symfony/View/Builder/Registry/ControllerMatch.php new file mode 100644 index 0000000000..36fb40c5c8 --- /dev/null +++ b/src/lib/MVC/Symfony/View/Builder/Registry/ControllerMatch.php @@ -0,0 +1,60 @@ +addToRegistry($toAdd); + } + + /** + * @param \Ibexa\Core\MVC\Symfony\View\Builder\ViewBuilder[] $viewBuilders + */ + public function addToRegistry(array $viewBuilders) + { + $this->registry = array_merge($this->registry, $viewBuilders); + } + + /** + * Returns the ViewBuilder that matches the given controller string. + * + * @param string $controllerString A controller string to match against. Example: ibexa_content::viewAction. + * + * @return \Ibexa\Core\MVC\Symfony\View\Builder\ViewBuilder|null + */ + public function getFromRegistry($controllerString) + { + if (!is_string($controllerString)) { + return null; + } + + foreach ($this->registry as $viewBuilder) { + if ($viewBuilder->matches($controllerString)) { + return $viewBuilder; + } + } + + return null; + } +} + +class_alias(ControllerMatch::class, 'eZ\Publish\Core\MVC\Symfony\View\Builder\Registry\ControllerMatch'); diff --git a/src/lib/MVC/Symfony/View/Builder/ViewBuilder.php b/src/lib/MVC/Symfony/View/Builder/ViewBuilder.php new file mode 100644 index 0000000000..533da06fca --- /dev/null +++ b/src/lib/MVC/Symfony/View/Builder/ViewBuilder.php @@ -0,0 +1,35 @@ +providerRegistry = $providersRegistry; + } + + public function configure(View $view) + { + foreach ($this->providerRegistry->getViewProviders($view) as $viewProvider) { + if ($providerView = $viewProvider->getView($view)) { + $view->setConfigHash($providerView->getConfigHash()); + if (($templateIdentifier = $providerView->getTemplateIdentifier()) !== null) { + $view->setTemplateIdentifier($templateIdentifier); + } + + if (($controllerReference = $providerView->getControllerReference()) !== null) { + $view->setControllerReference($controllerReference); + } + + $view->addParameters($providerView->getParameters()); + + return; + } + } + } +} + +class_alias(ViewProvider::class, 'eZ\Publish\Core\MVC\Symfony\View\Configurator\ViewProvider'); diff --git a/src/lib/MVC/Symfony/View/ContentValueView.php b/src/lib/MVC/Symfony/View/ContentValueView.php new file mode 100644 index 0000000000..76160e3fd4 --- /dev/null +++ b/src/lib/MVC/Symfony/View/ContentValueView.php @@ -0,0 +1,22 @@ + + * namespace Foo; + * use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; + * use Ibexa\Contracts\Core\Repository\Values\Content\Location; + * + * // For a content + * function ( ContentInfo $contentInfo, array $parameters = array() ) + * { + * // Do something to render + * // Must return a string to display + * } + * + * // For a location + * function ( Location $location, array $parameters = array() ) + * { + * // Do something to render + * // Must return a string to display + * } + * + */ +class ContentView extends BaseView implements View, ContentValueView, LocationValueView, EmbedView, CachableView +{ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content */ + private $content; + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location|null */ + private $location; + + /** @var bool */ + private $isEmbed = false; + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function setContent(Content $content) + { + $this->content = $content; + } + + /** + * Returns the Content. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function getContent() + { + return $this->content; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + */ + public function setLocation(Location $location) + { + $this->location = $location; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location|null + */ + public function getLocation() + { + return $this->location; + } + + protected function getInternalParameters() + { + $parameters = ['content' => $this->content]; + if ($this->location !== null) { + $parameters['location'] = $this->location; + } + + return $parameters; + } + + /** + * Sets the value as embed / not embed. + * + * @param bool $value + */ + public function setIsEmbed($value) + { + $this->isEmbed = (bool)$value; + } + + /** + * Is the view an embed or not. + * + * @return bool True if the view is an embed, false if it is not. + */ + public function isEmbed() + { + return $this->isEmbed; + } +} + +class_alias(ContentView::class, 'eZ\Publish\Core\MVC\Symfony\View\ContentView'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/CustomLocationControllerChecker.php b/src/lib/MVC/Symfony/View/CustomLocationControllerChecker.php similarity index 75% rename from eZ/Publish/Core/MVC/Symfony/View/CustomLocationControllerChecker.php rename to src/lib/MVC/Symfony/View/CustomLocationControllerChecker.php index 4c0a311a3f..4aaab8fb4b 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/CustomLocationControllerChecker.php +++ b/src/lib/MVC/Symfony/View/CustomLocationControllerChecker.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\View; +namespace Ibexa\Core\MVC\Symfony\View; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; /** * Used to check if a Location is rendered using a custom controller. */ class CustomLocationControllerChecker { - /** @var \eZ\Publish\Core\MVC\Symfony\View\ViewProvider[] */ + /** @var \Ibexa\Core\MVC\Symfony\View\ViewProvider[] */ private $viewProviders; /** @@ -47,10 +47,12 @@ public function usesCustomController(Content $content, Location $location, $view } /** - * @param $viewProviders \eZ\Publish\Core\MVC\Symfony\View\ViewProvider[] + * @param \Ibexa\Core\MVC\Symfony\View\ViewProvider[] $viewProviders */ public function addViewProviders(array $viewProviders) { $this->viewProviders = $viewProviders; } } + +class_alias(CustomLocationControllerChecker::class, 'eZ\Publish\Core\MVC\Symfony\View\CustomLocationControllerChecker'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/EmbedView.php b/src/lib/MVC/Symfony/View/EmbedView.php similarity index 85% rename from eZ/Publish/Core/MVC/Symfony/View/EmbedView.php rename to src/lib/MVC/Symfony/View/EmbedView.php index 5666ef6e31..a13a7c467e 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/EmbedView.php +++ b/src/lib/MVC/Symfony/View/EmbedView.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\View; +namespace Ibexa\Core\MVC\Symfony\View; /** * A view that can embedded into another element. @@ -32,3 +32,5 @@ public function setIsEmbed($value); */ public function isEmbed(); } + +class_alias(EmbedView::class, 'eZ\Publish\Core\MVC\Symfony\View\EmbedView'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/Event/FilterViewBuilderParametersEvent.php b/src/lib/MVC/Symfony/View/Event/FilterViewBuilderParametersEvent.php similarity index 87% rename from eZ/Publish/Core/MVC/Symfony/View/Event/FilterViewBuilderParametersEvent.php rename to src/lib/MVC/Symfony/View/Event/FilterViewBuilderParametersEvent.php index 3ed4dd12a2..81f9eeec7f 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/Event/FilterViewBuilderParametersEvent.php +++ b/src/lib/MVC/Symfony/View/Event/FilterViewBuilderParametersEvent.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\View\Event; +namespace Ibexa\Core\MVC\Symfony\View\Event; use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\Request; @@ -49,3 +49,5 @@ public function getParameters() return $this->parameters; } } + +class_alias(FilterViewBuilderParametersEvent::class, 'eZ\Publish\Core\MVC\Symfony\View\Event\FilterViewBuilderParametersEvent'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/Event/FilterViewParametersEvent.php b/src/lib/MVC/Symfony/View/Event/FilterViewParametersEvent.php similarity index 87% rename from eZ/Publish/Core/MVC/Symfony/View/Event/FilterViewParametersEvent.php rename to src/lib/MVC/Symfony/View/Event/FilterViewParametersEvent.php index fcfe509531..266f6ae301 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/Event/FilterViewParametersEvent.php +++ b/src/lib/MVC/Symfony/View/Event/FilterViewParametersEvent.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\View\Event; +namespace Ibexa\Core\MVC\Symfony\View\Event; -use eZ\Publish\Core\MVC\Symfony\View\View; +use Ibexa\Core\MVC\Symfony\View\View; use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Contracts\EventDispatcher\Event; @@ -20,7 +20,7 @@ class FilterViewParametersEvent extends Event /** * Copy of the view object that is being built. * - * @var \eZ\Publish\Core\MVC\Symfony\View\View + * @var \Ibexa\Core\MVC\Symfony\View\View */ private $view; @@ -78,10 +78,12 @@ public function getParameterBag() /** * Returns the copy of the View object. * - * @return \eZ\Publish\Core\MVC\Symfony\View\View + * @return \Ibexa\Core\MVC\Symfony\View\View */ public function getView() { return $this->view; } } + +class_alias(FilterViewParametersEvent::class, 'eZ\Publish\Core\MVC\Symfony\View\Event\FilterViewParametersEvent'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/GenericVariableProviderRegistry.php b/src/lib/MVC/Symfony/View/GenericVariableProviderRegistry.php similarity index 78% rename from eZ/Publish/Core/MVC/Symfony/View/GenericVariableProviderRegistry.php rename to src/lib/MVC/Symfony/View/GenericVariableProviderRegistry.php index 1caeb78f24..f3b69beba7 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/GenericVariableProviderRegistry.php +++ b/src/lib/MVC/Symfony/View/GenericVariableProviderRegistry.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\View; +namespace Ibexa\Core\MVC\Symfony\View; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\SPI\MVC\View\VariableProvider; +use Ibexa\Contracts\Core\MVC\View\VariableProvider; +use Ibexa\Core\Base\Exceptions\NotFoundException; use Traversable; final class GenericVariableProviderRegistry implements VariableProviderRegistry { - /** @var \eZ\Publish\SPI\MVC\View\VariableProvider[] */ + /** @var \Ibexa\Contracts\Core\MVC\View\VariableProvider[] */ private $twigVariableProviders; public function __construct(Traversable $twigVariableProviders) @@ -43,3 +43,5 @@ public function hasTwigVariableProvider(string $identifier): bool return isset($this->twigVariableProviders[$identifier]); } } + +class_alias(GenericVariableProviderRegistry::class, 'eZ\Publish\Core\MVC\Symfony\View\GenericVariableProviderRegistry'); diff --git a/src/lib/MVC/Symfony/View/LocationValueView.php b/src/lib/MVC/Symfony/View/LocationValueView.php new file mode 100644 index 0000000000..1f6029a0a3 --- /dev/null +++ b/src/lib/MVC/Symfony/View/LocationValueView.php @@ -0,0 +1,17 @@ +templateEngine = $templateEngine; + $this->eventDispatcher = $eventDispatcher; + $this->repository = $repository; + $this->configResolver = $configResolver; + $this->viewBaseLayout = $viewBaseLayout; + $this->logger = $logger; + $this->viewConfigurator = $viewConfigurator; + } + + /** + * Helper for {@see addContentViewProvider()} and {@see addLocationViewProvider()}. + * + * @param array $property + * @param \Ibexa\Core\MVC\Symfony\View\ViewProvider $viewProvider + * @param int $priority + */ + private function addViewProvider(&$property, $viewProvider, $priority) + { + $priority = (int)$priority; + if (!isset($property[$priority])) { + $property[$priority] = []; + } + + $property[$priority][] = $viewProvider; + } + + /** + * Registers $viewProvider as a valid content view provider. + * When this view provider will be called in the chain depends on $priority. The highest $priority is, the earliest the router will be called. + * + * @param \Ibexa\Core\MVC\Symfony\View\ViewProvider $viewProvider + * @param int $priority + */ + public function addContentViewProvider(ViewProvider $viewProvider, $priority = 0) + { + $this->addViewProvider($this->contentViewProviders, $viewProvider, $priority); + } + + /** + * Registers $viewProvider as a valid location view provider. + * When this view provider will be called in the chain depends on $priority. The highest $priority is, the earliest the router will be called. + * + * @param \Ibexa\Core\MVC\Symfony\View\ViewProvider $viewProvider + * @param int $priority + */ + public function addLocationViewProvider(ViewProvider $viewProvider, $priority = 0) + { + $this->addViewProvider($this->locationViewProviders, $viewProvider, $priority); + } + + /** + * @return \Ibexa\Core\MVC\Symfony\View\ViewProvider[] + */ + public function getAllContentViewProviders() + { + if (empty($this->sortedContentViewProviders)) { + $this->sortedContentViewProviders = $this->sortViewProviders($this->contentViewProviders); + } + + return $this->sortedContentViewProviders; + } + + /** + * @return \Ibexa\Core\MVC\Symfony\View\ViewProvider[] + */ + public function getAllLocationViewProviders() + { + if (empty($this->sortedLocationViewProviders)) { + $this->sortedLocationViewProviders = $this->sortViewProviders($this->locationViewProviders); + } + + return $this->sortedLocationViewProviders; + } + + /** + * Sort the registered view providers by priority. + * The highest priority number is the highest priority (reverse sorting). + * + * @param array $property view providers to sort + * + * @return \Ibexa\Core\MVC\Symfony\View\Provider\Content[]|\Ibexa\Core\MVC\Symfony\View\Provider\Location[] + */ + protected function sortViewProviders($property) + { + $sortedViewProviders = []; + krsort($property); + + foreach ($property as $viewProvider) { + $sortedViewProviders = array_merge($sortedViewProviders, $viewProvider); + } + + return $sortedViewProviders; + } + + /** + * Renders $content by selecting the right template. + * $content will be injected in the selected template. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param string $viewType Variation of display for your content. Default is 'full'. + * @param array $parameters Parameters to pass to the template called to + * render the view. By default, it's empty. 'content' entry is + * reserved for the Content that is rendered. + * + * @throws \RuntimeException + * + * @return string + */ + public function renderContent(Content $content, $viewType = ViewManagerInterface::VIEW_TYPE_FULL, $parameters = []) + { + $view = new ContentView(null, $parameters, $viewType); + $view->setContent($content); + if (isset($parameters['location'])) { + $view->setLocation($parameters['location']); + } + + $this->viewConfigurator->configure($view); + + if ($view->getTemplateIdentifier() === null) { + throw new RuntimeException('Unable to find a template for #' . $content->contentInfo->id); + } + + return $this->renderContentView($view, $parameters); + } + + /** + * Renders $location by selecting the right template for $viewType. + * $content and $location will be injected in the selected template. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param string $viewType Variation of display for your content. Default is 'full'. + * @param array $parameters Parameters to pass to the template called to + * render the view. By default, it's empty. 'location' and 'content' + * entries are reserved for the Location (and its Content) that is + * viewed. + * + * @throws \RuntimeException + * + * @return string + */ + public function renderLocation(Location $location, $viewType = ViewManagerInterface::VIEW_TYPE_FULL, $parameters = []) + { + if (!isset($parameters['location'])) { + $parameters['location'] = $location; + } + + if (!isset($parameters['content'])) { + $parameters['content'] = $this->repository->getContentService()->loadContentByContentInfo( + $location->contentInfo, + $this->configResolver->getParameter('languages') + ); + } + + return $this->renderContent($parameters['content'], $viewType, $parameters); + } + + /** + * Renders passed ContentView object via the template engine. + * If $view's template identifier is a closure, then it is called directly and the result is returned as is. + * + * @param \Ibexa\Core\MVC\Symfony\View\View $view + * @param array $defaultParams + * + * @return string + */ + public function renderContentView(View $view, array $defaultParams = []) + { + $defaultParams['view_base_layout'] = $this->viewBaseLayout; + $view->addParameters($defaultParams); + $this->eventDispatcher->dispatch(new PreContentViewEvent($view), MVCEvents::PRE_CONTENT_VIEW); + + $templateIdentifier = $view->getTemplateIdentifier(); + $params = $view->getParameters(); + if ($templateIdentifier instanceof \Closure) { + return $templateIdentifier($params); + } + + return $this->templateEngine->render($templateIdentifier, $params); + } +} + +class_alias(Manager::class, 'eZ\Publish\Core\MVC\Symfony\View\Manager'); diff --git a/src/lib/MVC/Symfony/View/ParametersInjector.php b/src/lib/MVC/Symfony/View/ParametersInjector.php new file mode 100644 index 0000000000..2f4468f34b --- /dev/null +++ b/src/lib/MVC/Symfony/View/ParametersInjector.php @@ -0,0 +1,17 @@ +eventDispatcher = $eventDispatcher; + } + + public function injectViewParameters(View $view, array $parameters) + { + $event = new FilterViewParametersEvent($view, $parameters); + $this->eventDispatcher->dispatch($event, ViewEvents::FILTER_VIEW_PARAMETERS); + $view->addParameters($event->getViewParameters()); + } +} + +class_alias(EventDispatcherInjector::class, 'eZ\Publish\Core\MVC\Symfony\View\ParametersInjector\EventDispatcherInjector'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/ParametersInjector/NoLayout.php b/src/lib/MVC/Symfony/View/ParametersInjector/NoLayout.php similarity index 76% rename from eZ/Publish/Core/MVC/Symfony/View/ParametersInjector/NoLayout.php rename to src/lib/MVC/Symfony/View/ParametersInjector/NoLayout.php index e520b9be3c..b8ffd98e36 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/ParametersInjector/NoLayout.php +++ b/src/lib/MVC/Symfony/View/ParametersInjector/NoLayout.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\View\ParametersInjector; +namespace Ibexa\Core\MVC\Symfony\View\ParametersInjector; -use eZ\Publish\Core\MVC\Symfony\View\Event\FilterViewParametersEvent; -use eZ\Publish\Core\MVC\Symfony\View\ViewEvents; +use Ibexa\Core\MVC\Symfony\View\Event\FilterViewParametersEvent; +use Ibexa\Core\MVC\Symfony\View\ViewEvents; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -30,3 +30,5 @@ public function injectCustomParameters(FilterViewParametersEvent $event) ); } } + +class_alias(NoLayout::class, 'eZ\Publish\Core\MVC\Symfony\View\ParametersInjector\NoLayout'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/ParametersInjector/ValueObjectsIds.php b/src/lib/MVC/Symfony/View/ParametersInjector/ValueObjectsIds.php similarity index 82% rename from eZ/Publish/Core/MVC/Symfony/View/ParametersInjector/ValueObjectsIds.php rename to src/lib/MVC/Symfony/View/ParametersInjector/ValueObjectsIds.php index 077b5598ca..ad96c283ff 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/ParametersInjector/ValueObjectsIds.php +++ b/src/lib/MVC/Symfony/View/ParametersInjector/ValueObjectsIds.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\View\ParametersInjector; +namespace Ibexa\Core\MVC\Symfony\View\ParametersInjector; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\View; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Core\MVC\Symfony\View; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -37,3 +37,5 @@ public function injectValueObjectsIds(View\Event\FilterViewParametersEvent $even } } } + +class_alias(ValueObjectsIds::class, 'eZ\Publish\Core\MVC\Symfony\View\ParametersInjector\ValueObjectsIds'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/ParametersInjector/ViewbaseLayout.php b/src/lib/MVC/Symfony/View/ParametersInjector/ViewbaseLayout.php similarity index 76% rename from eZ/Publish/Core/MVC/Symfony/View/ParametersInjector/ViewbaseLayout.php rename to src/lib/MVC/Symfony/View/ParametersInjector/ViewbaseLayout.php index 39a6a785be..05f0096c01 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/ParametersInjector/ViewbaseLayout.php +++ b/src/lib/MVC/Symfony/View/ParametersInjector/ViewbaseLayout.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\View\ParametersInjector; +namespace Ibexa\Core\MVC\Symfony\View\ParametersInjector; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\View\Event\FilterViewParametersEvent; -use eZ\Publish\Core\MVC\Symfony\View\ViewEvents; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\View\Event\FilterViewParametersEvent; +use Ibexa\Core\MVC\Symfony\View\ViewEvents; use Symfony\Component\EventDispatcher\EventSubscriberInterface; /** @@ -19,7 +19,7 @@ class ViewbaseLayout implements EventSubscriberInterface /** @var string */ private $viewbaseLayout; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; public function __construct($viewbaseLayout, ConfigResolverInterface $configResolver) @@ -48,3 +48,5 @@ public function injectViewbaseLayout(FilterViewParametersEvent $event) $event->getParameterBag()->set('page_layout', $pageLayout); } } + +class_alias(ViewbaseLayout::class, 'eZ\Publish\Core\MVC\Symfony\View\ParametersInjector\ViewbaseLayout'); diff --git a/src/lib/MVC/Symfony/View/Provider/Configured.php b/src/lib/MVC/Symfony/View/Provider/Configured.php new file mode 100644 index 0000000000..3f56c1713f --- /dev/null +++ b/src/lib/MVC/Symfony/View/Provider/Configured.php @@ -0,0 +1,65 @@ +matcherFactory = $matcherFactory; + } + + public function getView(View $view) + { + if (($configHash = $this->matcherFactory->match($view)) === null) { + return null; + } + + return $this->buildContentView($configHash); + } + + /** + * Builds a ContentView object from $viewConfig. + * + * @param array $viewConfig + * + * @return \Ibexa\Core\MVC\Symfony\View\ContentView + */ + protected function buildContentView(array $viewConfig) + { + $view = new ContentView(); + $view->setConfigHash($viewConfig); + if (isset($viewConfig['template'])) { + $view->setTemplateIdentifier($viewConfig['template']); + } + if (isset($viewConfig['controller'])) { + $view->setControllerReference(new ControllerReference($viewConfig['controller'])); + } + if (isset($viewConfig['params']) && is_array($viewConfig['params'])) { + $view->addParameters($viewConfig['params']); + } + + return $view; + } +} + +class_alias(Configured::class, 'eZ\Publish\Core\MVC\Symfony\View\Provider\Configured'); diff --git a/src/lib/MVC/Symfony/View/Provider/Content.php b/src/lib/MVC/Symfony/View/Provider/Content.php new file mode 100644 index 0000000000..07ea6de95c --- /dev/null +++ b/src/lib/MVC/Symfony/View/Provider/Content.php @@ -0,0 +1,31 @@ +viewProviders) as $type) { + if ($view instanceof $type) { + return $this->viewProviders[$type]; + } + } + throw new InvalidArgumentException('view', 'No compatible ViewProvider found for ' . gettype($view)); + } + + /** + * Sets the complete list of view providers. + * + * @param array $viewProviders ['type' => [ViewProvider1, ViewProvider2]] + */ + public function setViewProviders(array $viewProviders) + { + $this->viewProviders = $viewProviders; + } +} + +class_alias(Registry::class, 'eZ\Publish\Core\MVC\Symfony\View\Provider\Registry'); diff --git a/src/lib/MVC/Symfony/View/QueryView.php b/src/lib/MVC/Symfony/View/QueryView.php new file mode 100644 index 0000000000..c006faaf24 --- /dev/null +++ b/src/lib/MVC/Symfony/View/QueryView.php @@ -0,0 +1,15 @@ +templateEngine = $templateEngine; + $this->eventDispatcher = $eventDispatcher; + } + + /** + * @param \Ibexa\Core\MVC\Symfony\View\View $view + * + * @throws \Ibexa\Core\MVC\Exception\NoViewTemplateException + * + * @return string + */ + public function render(View $view) + { + $this->eventDispatcher->dispatch(new PreContentViewEvent($view), MVCEvents::PRE_CONTENT_VIEW); + + $templateIdentifier = $view->getTemplateIdentifier(); + if ($templateIdentifier instanceof Closure) { + return $templateIdentifier($view->getParameters()); + } + + if ($view->getTemplateIdentifier() === null) { + throw new NoViewTemplateException($view); + } + + return $this->templateEngine->render( + $view->getTemplateIdentifier(), + $view->getParameters() + ); + } +} + +class_alias(TemplateRenderer::class, 'eZ\Publish\Core\MVC\Symfony\View\Renderer\TemplateRenderer'); diff --git a/src/lib/MVC/Symfony/View/VariableProviderRegistry.php b/src/lib/MVC/Symfony/View/VariableProviderRegistry.php new file mode 100644 index 0000000000..3e6ed27488 --- /dev/null +++ b/src/lib/MVC/Symfony/View/VariableProviderRegistry.php @@ -0,0 +1,22 @@ + + * function (array $params = []) + * { + * // Do something to render + * // Must return a string to display + * } + * + * Must throw a {@see \Ibexa\Core\Base\Exceptions\InvalidArgumentType} exception if $templateIdentifier is invalid. + * + * @param string|\Closure $templateIdentifier + * + * @phpstan-param string|(\Closure(array):string) $templateIdentifier + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType + */ + public function setTemplateIdentifier($templateIdentifier); + + /** + * Returns the registered template identifier. + * + * @return string|\Closure + * + * @phpstan-return string|(\Closure(array):string) + */ + public function getTemplateIdentifier(); + + /** + * Sets $parameters that will later be injected to the template/closure. + * If some parameters were already present, $parameters will replace them. + * + * @param array $parameters Hash of parameters + */ + public function setParameters(array $parameters); + + /** + * Adds a hash of parameters to the existing parameters. + * + * @param array $parameters + */ + public function addParameters(array $parameters); + + /** + * Returns registered parameters. + * + * @return array + */ + public function getParameters(); + + /** + * Checks if $parameterName exists. + * + * @param string $parameterName + * + * @return bool + */ + public function hasParameter($parameterName); + + /** + * Returns parameter value by $parameterName. + * Throws an \InvalidArgumentException if $parameterName is not set. + * + * @param string $parameterName + * + * @throws \InvalidArgumentException + * + * @return mixed + */ + public function getParameter($parameterName); + + /** + * Injects the config hash that was used to match and generate the current view. + * Typically, the hash would have as keys: + * - template : The template that has been matched + * - match : The matching configuration, including the matcher "identifier" and what has been passed to it. + * - matcher : The matcher object. + * + * @param array $config + */ + public function setConfigHash(array $config); + + /** + * Returns the config hash. + * + * @return array|null + */ + public function getConfigHash(); + + public function setViewType($viewType); + + public function getViewType(); + + public function setControllerReference(ControllerReference $controllerReference); + + /** + * @return \Symfony\Component\HttpKernel\Controller\ControllerReference + */ + public function getControllerReference(); + + /** + * Sets a pre-configured Response that will be used to render the View. + * + * @param \Symfony\Component\HttpFoundation\Response $response + */ + public function setResponse(Response $response); + + /** + * Returns the pre-configured Response. + * + * @return \Symfony\Component\HttpFoundation\Response|null + */ + public function getResponse(); +} + +class_alias(View::class, 'eZ\Publish\Core\MVC\Symfony\View\View'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/ViewEvents.php b/src/lib/MVC/Symfony/View/ViewEvents.php similarity index 90% rename from eZ/Publish/Core/MVC/Symfony/View/ViewEvents.php rename to src/lib/MVC/Symfony/View/ViewEvents.php index 46c5300002..689af41599 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/ViewEvents.php +++ b/src/lib/MVC/Symfony/View/ViewEvents.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\View; +namespace Ibexa\Core\MVC\Symfony\View; /** * Events constants of views. @@ -29,3 +29,5 @@ final class ViewEvents */ public const FILTER_VIEW_PARAMETERS = 'view.parameters_injection'; } + +class_alias(ViewEvents::class, 'eZ\Publish\Core\MVC\Symfony\View\ViewEvents'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/ViewManagerInterface.php b/src/lib/MVC/Symfony/View/ViewManagerInterface.php similarity index 82% rename from eZ/Publish/Core/MVC/Symfony/View/ViewManagerInterface.php rename to src/lib/MVC/Symfony/View/ViewManagerInterface.php index 45aee54c50..cc72431fdf 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/ViewManagerInterface.php +++ b/src/lib/MVC/Symfony/View/ViewManagerInterface.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\View; +namespace Ibexa\Core\MVC\Symfony\View; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; interface ViewManagerInterface { @@ -18,7 +18,7 @@ interface ViewManagerInterface * Renders $content by selecting the right template. * $content will be injected in the selected template. * - * @param \eZ\Publish\API\Repository\Values\Content\Content $content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content * @param string $viewType Variation of display for your content. Default is 'full'. * @param array $parameters Parameters to pass to the template called to * render the view. By default, it's empty. 'content' entry is @@ -34,7 +34,7 @@ public function renderContent(Content $content, $viewType = self::VIEW_TYPE_FULL * Renders $location by selecting the right template for $viewType. * $content and $location will be injected in the selected template. * - * @param \eZ\Publish\API\Repository\Values\Content\Location $location + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location * @param string $viewType Variation of display for your content. Default is 'full'. * @param array $parameters Parameters to pass to the template called to * render the view. By default, it's empty. 'location' and 'content' @@ -51,10 +51,11 @@ public function renderLocation(Location $location, $viewType = self::VIEW_TYPE_F * Renders passed ContentView object via the template engine. * If $view's template identifier is a closure, then it is called directly and the result is returned as is. * - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentViewInterface $view * @param array $defaultParams * * @return string */ public function renderContentView(View $view, array $defaultParams = []); } + +class_alias(ViewManagerInterface::class, 'eZ\Publish\Core\MVC\Symfony\View\ViewManagerInterface'); diff --git a/src/lib/MVC/Symfony/View/ViewProvider.php b/src/lib/MVC/Symfony/View/ViewProvider.php new file mode 100644 index 0000000000..8de5df92f5 --- /dev/null +++ b/src/lib/MVC/Symfony/View/ViewProvider.php @@ -0,0 +1,17 @@ +registry[$alias] = $notificationRenderer; + } + + /** + * @param string $alias + * + * @return \Ibexa\Core\Notification\Renderer\NotificationRenderer + */ + public function getRenderer(string $alias): NotificationRenderer + { + return $this->registry[$alias]; + } + + /** + * @param string $alias + * + * @return bool + */ + public function hasRenderer(string $alias): bool + { + return isset($this->registry[$alias]); + } +} + +class_alias(Registry::class, 'eZ\Publish\Core\Notification\Renderer\Registry'); diff --git a/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php b/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php new file mode 100644 index 0000000000..fb274cc338 --- /dev/null +++ b/src/lib/Pagination/Pagerfanta/AbstractSearchResultAdapter.php @@ -0,0 +1,177 @@ +query = $query; + $this->searchService = $searchService; + $this->languageFilter = $languageFilter; + } + + /** + * Returns the number of results. + * + * @return int The number of results. + */ + public function getNbResults() + { + if (isset($this->totalCount)) { + return $this->totalCount; + } + + $countQuery = clone $this->query; + $countQuery->limit = 0; + // Skip facets/aggregations & spellcheck computing + $countQuery->facetBuilders = []; + $countQuery->aggregations = []; + $countQuery->spellcheck = null; + + $searchResults = $this->executeQuery( + $this->searchService, + $countQuery, + $this->languageFilter + ); + + return $this->totalCount = $searchResults->totalCount; + } + + /** + * Returns a slice of the results, as SearchHit objects. + * + * @param int $offset The offset. + * @param int $length The length. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] + */ + public function getSlice($offset, $length) + { + $query = clone $this->query; + $query->offset = $offset; + $query->limit = $length; + $query->performCount = false; + + $searchResult = $this->executeQuery( + $this->searchService, + $query, + $this->languageFilter + ); + + $this->aggregations = $searchResult->getAggregations(); + $this->time = $searchResult->time; + $this->timedOut = $searchResult->timedOut; + $this->maxScore = $searchResult->maxScore; + $this->spellcheck = $searchResult->getSpellcheck(); + + // Set count for further use if returned by search engine despite !performCount (Solr, ES) + if (!isset($this->totalCount) && isset($searchResult->totalCount)) { + $this->totalCount = $searchResult->totalCount; + } + + return $searchResult->searchHits; + } + + public function getAggregations(): AggregationResultCollection + { + if ($this->aggregations === null) { + $aggregationQuery = clone $this->query; + $aggregationQuery->offset = 0; + $aggregationQuery->limit = 0; + $aggregationQuery->spellcheck = null; + + $searchResults = $this->executeQuery( + $this->searchService, + $aggregationQuery, + $this->languageFilter + ); + + $this->aggregations = $searchResults->aggregations; + } + + return $this->aggregations; + } + + public function getSpellcheck(): ?SpellcheckResult + { + if ($this->spellcheck === null) { + $spellcheckQuery = clone $this->query; + $spellcheckQuery->offset = 0; + $spellcheckQuery->limit = 0; + $spellcheckQuery->aggregations = []; + + $searchResults = $this->executeQuery( + $this->searchService, + $spellcheckQuery, + $this->languageFilter + ); + + $this->spellcheck = $searchResults->spellcheck; + } + + return $this->spellcheck; + } + + public function getTime(): ?float + { + return $this->time; + } + + public function getTimedOut(): ?bool + { + return $this->timedOut; + } + + public function getMaxScore(): ?float + { + return $this->maxScore; + } + + abstract protected function executeQuery( + SearchService $searchService, + Query $query, + array $languageFilter + ): SearchResult; +} + +class_alias(AbstractSearchResultAdapter::class, 'eZ\Publish\Core\Pagination\Pagerfanta\AbstractSearchResultAdapter'); diff --git a/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php new file mode 100644 index 0000000000..949cd8d311 --- /dev/null +++ b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactory.php @@ -0,0 +1,53 @@ +searchService = $searchService; + } + + public function createAdapter(Query $query, array $languageFilter = []): AdapterInterface + { + if ($query instanceof LocationQuery) { + return new LocationSearchHitAdapter($query, $this->searchService, $languageFilter); + } + + return new ContentSearchHitAdapter($query, $this->searchService, $languageFilter); + } + + public function createFixedAdapter(Query $query, array $languageFilter = []): AdapterInterface + { + if ($query instanceof LocationQuery) { + $searchResults = $this->searchService->findLocations($query, $languageFilter); + } else { + $searchResults = $this->searchService->findContent($query, $languageFilter); + } + + return new FixedSearchResultHitAdapter($searchResults); + } +} + +class_alias(SearchHitAdapterFactory::class, 'eZ\Publish\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactory'); diff --git a/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php new file mode 100644 index 0000000000..ebdfc54c04 --- /dev/null +++ b/src/lib/Pagination/Pagerfanta/AdapterFactory/SearchHitAdapterFactoryInterface.php @@ -0,0 +1,24 @@ +contentService = $contentService; + $this->filter = $filter; + $this->languageFilter = $languageFilter; + } + + public function getNbResults(): int + { + if ($this->totalCount === null) { + $this->totalCount = $this->contentService->count($this->filter, $this->languageFilter); + } + + return $this->totalCount; + } + + public function getSlice($offset, $length): iterable + { + $selectFilter = clone $this->filter; + $selectFilter->sliceBy($length, $offset); + + $results = $this->contentService->find($selectFilter, $this->languageFilter); + if ($this->totalCount === null) { + $this->totalCount = $results->getTotalCount(); + } + + return $results; + } +} + +class_alias(ContentFilteringAdapter::class, 'eZ\Publish\Core\Pagination\Pagerfanta\ContentFilteringAdapter'); diff --git a/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php b/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php new file mode 100644 index 0000000000..24317cec7d --- /dev/null +++ b/src/lib/Pagination/Pagerfanta/ContentSearchAdapter.php @@ -0,0 +1,34 @@ +valueObject; + } + + return $list; + } +} + +class_alias(ContentSearchAdapter::class, 'eZ\Publish\Core\Pagination\Pagerfanta\ContentSearchAdapter'); diff --git a/src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php b/src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php new file mode 100644 index 0000000000..891beb1ddb --- /dev/null +++ b/src/lib/Pagination/Pagerfanta/ContentSearchHitAdapter.php @@ -0,0 +1,28 @@ +findContent($query, $languageFilter); + } +} + +class_alias(ContentSearchHitAdapter::class, 'eZ\Publish\Core\Pagination\Pagerfanta\ContentSearchHitAdapter'); diff --git a/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php b/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php new file mode 100644 index 0000000000..5a19b0aa14 --- /dev/null +++ b/src/lib/Pagination/Pagerfanta/FixedSearchResultHitAdapter.php @@ -0,0 +1,55 @@ +searchResult = $searchResult; + } + + public function getNbResults(): int + { + return $this->searchResult->totalCount ?? -1; + } + + public function getSlice($offset, $length) + { + return $this->searchResult->searchHits; + } + + public function getAggregations(): AggregationResultCollection + { + return $this->searchResult->getAggregations(); + } + + public function getTime(): ?float + { + return $this->searchResult->time; + } + + public function getTimedOut(): ?bool + { + return $this->searchResult->timedOut; + } + + public function getMaxScore(): ?float + { + return $this->searchResult->maxScore; + } +} + +class_alias(FixedSearchResultHitAdapter::class, 'eZ\Publish\Core\Pagination\Pagerfanta\FixedSearchResultHitAdapter'); diff --git a/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php b/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php new file mode 100644 index 0000000000..92bd4e6316 --- /dev/null +++ b/src/lib/Pagination/Pagerfanta/LocationFilteringAdapter.php @@ -0,0 +1,62 @@ +locationService = $locationService; + $this->filter = $filter; + $this->languageFilter = $languageFilter; + } + + public function getNbResults(): int + { + if ($this->totalCount === null) { + $this->totalCount = $this->locationService->count($this->filter, $this->languageFilter); + } + + return $this->totalCount; + } + + public function getSlice($offset, $length): iterable + { + $selectFilter = clone $this->filter; + $selectFilter->sliceBy($length, $offset); + + $results = $this->locationService->find($selectFilter, $this->languageFilter); + if ($this->totalCount === null) { + $this->totalCount = $results->getTotalCount(); + } + + return $results; + } +} + +class_alias(LocationFilteringAdapter::class, 'eZ\Publish\Core\Pagination\Pagerfanta\LocationFilteringAdapter'); diff --git a/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php b/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php new file mode 100644 index 0000000000..8bfaa34ad3 --- /dev/null +++ b/src/lib/Pagination/Pagerfanta/LocationSearchAdapter.php @@ -0,0 +1,34 @@ +valueObject; + } + + return $list; + } +} + +class_alias(LocationSearchAdapter::class, 'eZ\Publish\Core\Pagination\Pagerfanta\LocationSearchAdapter'); diff --git a/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php b/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php new file mode 100644 index 0000000000..c0ec9fc2bf --- /dev/null +++ b/src/lib/Pagination/Pagerfanta/LocationSearchHitAdapter.php @@ -0,0 +1,34 @@ +findLocations($query, $languageFilter); + } +} + +class_alias(LocationSearchHitAdapter::class, 'eZ\Publish\Core\Pagination\Pagerfanta\LocationSearchHitAdapter'); diff --git a/eZ/Publish/Core/Pagination/Pagerfanta/Pagerfanta.php b/src/lib/Pagination/Pagerfanta/Pagerfanta.php similarity index 79% rename from eZ/Publish/Core/Pagination/Pagerfanta/Pagerfanta.php rename to src/lib/Pagination/Pagerfanta/Pagerfanta.php index 642eba6c87..0e3344f128 100644 --- a/eZ/Publish/Core/Pagination/Pagerfanta/Pagerfanta.php +++ b/src/lib/Pagination/Pagerfanta/Pagerfanta.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Pagination\Pagerfanta; +namespace Ibexa\Core\Pagination\Pagerfanta; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResultCollection; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; use Pagerfanta\Pagerfanta as BasePagerfanta; final class Pagerfanta extends BasePagerfanta @@ -38,3 +38,5 @@ public function getMaxScore(): ?float return $this->getAdapter()->getMaxScore(); } } + +class_alias(Pagerfanta::class, 'eZ\Publish\Core\Pagination\Pagerfanta\Pagerfanta'); diff --git a/eZ/Publish/Core/Pagination/Pagerfanta/SearchResultAdapter.php b/src/lib/Pagination/Pagerfanta/SearchResultAdapter.php similarity index 78% rename from eZ/Publish/Core/Pagination/Pagerfanta/SearchResultAdapter.php rename to src/lib/Pagination/Pagerfanta/SearchResultAdapter.php index 6da52ba91c..ea0b989932 100644 --- a/eZ/Publish/Core/Pagination/Pagerfanta/SearchResultAdapter.php +++ b/src/lib/Pagination/Pagerfanta/SearchResultAdapter.php @@ -6,13 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Pagination\Pagerfanta; +namespace Ibexa\Core\Pagination\Pagerfanta; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResultCollection; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; use Pagerfanta\Adapter\AdapterInterface; /** - * Contract for \eZ\Publish\API\Repository\SearchService based adapters. + * Contract for SearchService based adapters. + * + * @see \Ibexa\Contracts\Core\Repository\SearchService */ interface SearchResultAdapter extends AdapterInterface { @@ -44,3 +46,5 @@ public function getTimedOut(): ?bool; */ public function getMaxScore(): ?float; } + +class_alias(SearchResultAdapter::class, 'eZ\Publish\Core\Pagination\Pagerfanta\SearchResultAdapter'); diff --git a/eZ/Publish/Core/Persistence/Cache/AbstractHandler.php b/src/lib/Persistence/Cache/AbstractHandler.php similarity index 91% rename from eZ/Publish/Core/Persistence/Cache/AbstractHandler.php rename to src/lib/Persistence/Cache/AbstractHandler.php index 9daaec953a..04fdddd34a 100644 --- a/eZ/Publish/Core/Persistence/Cache/AbstractHandler.php +++ b/src/lib/Persistence/Cache/AbstractHandler.php @@ -4,12 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\SPI\Persistence\Handler as PersistenceHandler; +use Ibexa\Contracts\Core\Persistence\Handler as PersistenceHandler; use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface; use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierSanitizer; -use Ibexa\Core\Persistence\Cache\LocationPathConverter; use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; /** @@ -22,10 +21,10 @@ abstract class AbstractHandler /** @var \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface */ protected $cache; - /** @var \eZ\Publish\SPI\Persistence\Handler */ + /** @var \Ibexa\Contracts\Core\Persistence\Handler */ protected $persistenceHandler; - /** @var \eZ\Publish\Core\Persistence\Cache\PersistenceLogger */ + /** @var \Ibexa\Core\Persistence\Cache\PersistenceLogger */ protected $logger; /** @var \Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface */ @@ -41,8 +40,8 @@ abstract class AbstractHandler * Setups current handler with everything needed. * * @param \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface $cache - * @param \eZ\Publish\SPI\Persistence\Handler $persistenceHandler - * @param \eZ\Publish\Core\Persistence\Cache\PersistenceLogger $logger + * @param \Ibexa\Contracts\Core\Persistence\Handler $persistenceHandler + * @param \Ibexa\Core\Persistence\Cache\PersistenceLogger $logger * @param \Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface $cacheIdentifierGenerator * @param \Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierSanitizer $cacheIdentifierSanitizer * @param \Ibexa\Core\Persistence\Cache\LocationPathConverter $locationPathConverter @@ -138,3 +137,5 @@ final protected function getMultipleCacheItems( return $list; } } + +class_alias(AbstractHandler::class, 'eZ\Publish\Core\Persistence\Cache\AbstractHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/AbstractInMemoryHandler.php b/src/lib/Persistence/Cache/AbstractInMemoryHandler.php similarity index 95% rename from eZ/Publish/Core/Persistence/Cache/AbstractInMemoryHandler.php rename to src/lib/Persistence/Cache/AbstractInMemoryHandler.php index 7424f45d26..8fa1ae1072 100644 --- a/eZ/Publish/Core/Persistence/Cache/AbstractInMemoryHandler.php +++ b/src/lib/Persistence/Cache/AbstractInMemoryHandler.php @@ -4,11 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\Core\Persistence\Cache\Adapter\TransactionAwareAdapterInterface; -use eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache; -use Ibexa\Core\Persistence\Cache\CacheIndicesValidatorInterface; +use Ibexa\Core\Persistence\Cache\Adapter\TransactionAwareAdapterInterface; +use Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache; /** * Abstract handler for use in other SPI Handlers. @@ -21,18 +20,18 @@ abstract class AbstractInMemoryHandler /** * NOTE: Instance of this must be TransactionalInMemoryCacheAdapter in order for cache clearing to affect in-memory cache. * - * @var \eZ\Publish\Core\Persistence\Cache\Adapter\TransactionAwareAdapterInterface + * @var \Ibexa\Core\Persistence\Cache\Adapter\TransactionAwareAdapterInterface */ protected $cache; - /** @var \eZ\Publish\Core\Persistence\Cache\PersistenceLogger */ + /** @var \Ibexa\Core\Persistence\Cache\PersistenceLogger */ protected $logger; /** * NOTE: On purpose private as it's only supposed to be interacted with in tandem with symfony cache here, * hence the cache decorator and the reusable methods here. * - * @var \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache + * @var \Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache */ private $inMemory; @@ -277,3 +276,5 @@ final protected function getMultipleCacheValues( return $list; } } + +class_alias(AbstractInMemoryHandler::class, 'eZ\Publish\Core\Persistence\Cache\AbstractInMemoryHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/AbstractInMemoryPersistenceHandler.php b/src/lib/Persistence/Cache/AbstractInMemoryPersistenceHandler.php similarity index 79% rename from eZ/Publish/Core/Persistence/Cache/AbstractInMemoryPersistenceHandler.php rename to src/lib/Persistence/Cache/AbstractInMemoryPersistenceHandler.php index fc674cc62b..6d76d31c11 100644 --- a/eZ/Publish/Core/Persistence/Cache/AbstractInMemoryPersistenceHandler.php +++ b/src/lib/Persistence/Cache/AbstractInMemoryPersistenceHandler.php @@ -4,24 +4,22 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\Core\Persistence\Cache\Adapter\TransactionAwareAdapterInterface; -use eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache; -use eZ\Publish\SPI\Persistence\Handler as PersistenceHandler; -use Ibexa\Core\Persistence\Cache\CacheIndicesValidatorInterface; +use Ibexa\Contracts\Core\Persistence\Handler as PersistenceHandler; +use Ibexa\Core\Persistence\Cache\Adapter\TransactionAwareAdapterInterface; use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface; use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierSanitizer; -use Ibexa\Core\Persistence\Cache\LocationPathConverter; +use Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache; /** * Internal abstract handler for use in other SPI Persistence Cache Handlers. * - * @internal Only for use as abstract in eZ\Publish\Core\Persistence\Cache\*Handlers. + * @internal Only for use as a Handler abstract in {@see \Ibexa\Core\Persistence\Cache\}. */ abstract class AbstractInMemoryPersistenceHandler extends AbstractInMemoryHandler { - /** @var \eZ\Publish\SPI\Persistence\Handler */ + /** @var \Ibexa\Contracts\Core\Persistence\Handler */ protected $persistenceHandler; /** @var \Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface */ @@ -61,3 +59,5 @@ protected function init(): void // overload to add init logic if needed in handler } } + +class_alias(AbstractInMemoryPersistenceHandler::class, 'eZ\Publish\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/Adapter/README.md b/src/lib/Persistence/Cache/Adapter/README.md similarity index 100% rename from eZ/Publish/Core/Persistence/Cache/Adapter/README.md rename to src/lib/Persistence/Cache/Adapter/README.md diff --git a/eZ/Publish/Core/Persistence/Cache/Adapter/TransactionAwareAdapterInterface.php b/src/lib/Persistence/Cache/Adapter/TransactionAwareAdapterInterface.php similarity index 85% rename from eZ/Publish/Core/Persistence/Cache/Adapter/TransactionAwareAdapterInterface.php rename to src/lib/Persistence/Cache/Adapter/TransactionAwareAdapterInterface.php index 05c8ba7d5e..bb4c17f388 100644 --- a/eZ/Publish/Core/Persistence/Cache/Adapter/TransactionAwareAdapterInterface.php +++ b/src/lib/Persistence/Cache/Adapter/TransactionAwareAdapterInterface.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache\Adapter; +namespace Ibexa\Core\Persistence\Cache\Adapter; use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; @@ -37,3 +37,5 @@ public function commitTransaction(): void; */ public function rollbackTransaction(): void; } + +class_alias(TransactionAwareAdapterInterface::class, 'eZ\Publish\Core\Persistence\Cache\Adapter\TransactionAwareAdapterInterface'); diff --git a/eZ/Publish/Core/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php b/src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php similarity index 92% rename from eZ/Publish/Core/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php rename to src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php index ac3ef03739..7f473511a6 100644 --- a/eZ/Publish/Core/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php +++ b/src/lib/Persistence/Cache/Adapter/TransactionalInMemoryCacheAdapter.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache\Adapter; +namespace Ibexa\Core\Persistence\Cache\Adapter; use Psr\Cache\CacheItemInterface; use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; @@ -16,14 +16,14 @@ /** * Internal proxy adapter invalidating our isolated in-memory cache, and defer shared pool changes during transactions. * - * @internal For type hinting inside eZ\Publish\Core\Persistence\Cache\*. For external, type hint on TagAwareAdapterInterface. + * @internal For type hinting inside {@see \Ibexa\Core\Persistence\Cache\}. For external, type hint on TagAwareAdapterInterface. */ class TransactionalInMemoryCacheAdapter implements TransactionAwareAdapterInterface { /** @var \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface */ protected $sharedPool; - /** @var \eZ\Publish\Core\Persistence\Cache\inMemory\InMemoryCache[] */ + /** @var \Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache[] */ private $inMemoryPools; /** @var int */ @@ -40,7 +40,7 @@ class TransactionalInMemoryCacheAdapter implements TransactionAwareAdapterInterf /** * @param \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface $sharedPool - * @param \eZ\Publish\Core\Persistence\Cache\inMemory\InMemoryCache[] $inMemoryPools + * @param \Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache[] $inMemoryPools * @param int $transactionDepth * @param array $deferredTagsInvalidation * @param array $deferredItemsDeletion @@ -231,7 +231,7 @@ public function rollbackTransaction(): void /** * {@inheritdoc} * - * Symfony cache feature for deferring saves, not used by eZ & not related to transaction handling here. + * Symfony cache feature for deferring saves, not used by Ibexa & not related to transaction handling here. */ public function saveDeferred(CacheItemInterface $item) { @@ -241,7 +241,7 @@ public function saveDeferred(CacheItemInterface $item) /** * {@inheritdoc} * - * Symfony cache feature for committing deferred saves, not used by eZ & not related to transaction handling here. + * Symfony cache feature for committing deferred saves, not used by Ibexa & not related to transaction handling here. */ public function commit() { @@ -306,3 +306,5 @@ private function clearInMemoryPools(): void } } } + +class_alias(TransactionalInMemoryCacheAdapter::class, 'eZ\Publish\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter'); diff --git a/eZ/Publish/Core/Persistence/Cache/BookmarkHandler.php b/src/lib/Persistence/Cache/BookmarkHandler.php similarity index 92% rename from eZ/Publish/Core/Persistence/Cache/BookmarkHandler.php rename to src/lib/Persistence/Cache/BookmarkHandler.php index 0966283473..c8565d5449 100644 --- a/eZ/Publish/Core/Persistence/Cache/BookmarkHandler.php +++ b/src/lib/Persistence/Cache/BookmarkHandler.php @@ -6,15 +6,12 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\SPI\Persistence\Bookmark\Bookmark; -use eZ\Publish\SPI\Persistence\Bookmark\CreateStruct; -use eZ\Publish\SPI\Persistence\Bookmark\Handler as BookmarkHandlerInterface; +use Ibexa\Contracts\Core\Persistence\Bookmark\Bookmark; +use Ibexa\Contracts\Core\Persistence\Bookmark\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Bookmark\Handler as BookmarkHandlerInterface; -/** - * @see \eZ\Publish\SPI\Persistence\Bookmark\Handler - */ class BookmarkHandler extends AbstractHandler implements BookmarkHandlerInterface { private const BOOKMARK_IDENTIFIER = 'bookmark'; @@ -124,3 +121,5 @@ public function locationSwapped(int $location1Id, int $location2Id): void $this->persistenceHandler->bookmarkHandler()->locationSwapped($location1Id, $location2Id); } } + +class_alias(BookmarkHandler::class, 'eZ\Publish\Core\Persistence\Cache\BookmarkHandler'); diff --git a/src/lib/Persistence/Cache/ContentHandler.php b/src/lib/Persistence/Cache/ContentHandler.php new file mode 100644 index 0000000000..9047a4c182 --- /dev/null +++ b/src/lib/Persistence/Cache/ContentHandler.php @@ -0,0 +1,786 @@ +getContentInfoTags = function (ContentInfo $info, array $tags = []) { + $tags[] = $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$info->id]); + + if ($info->mainLocationId) { + $locations = $this->persistenceHandler->locationHandler()->loadLocationsByContent($info->id); + foreach ($locations as $location) { + $tags[] = $this->cacheIdentifierGenerator->generateTag(self::LOCATION_IDENTIFIER, [$location->id]); + $pathIds = $this->locationPathConverter->convertToPathIds($location->pathString); + foreach ($pathIds as $pathId) { + $tags[] = $this->cacheIdentifierGenerator->generateTag(self::LOCATION_PATH_IDENTIFIER, [$pathId]); + } + } + } + + return $tags; + }; + $this->getContentInfoKeys = function (ContentInfo $info) { + return [ + $this->cacheIdentifierGenerator->generateKey(self::CONTENT_INFO_IDENTIFIER, [$info->id], true), + $this->cacheIdentifierGenerator->generateKey( + self::CONTENT_INFO_BY_REMOTE_ID_IDENTIFIER, + [$this->cacheIdentifierSanitizer->escapeForCacheKey($info->remoteId)], + true + ), + ]; + }; + + $this->getContentTags = function (Content $content) { + $versionInfo = $content->versionInfo; + $tags = [ + $this->cacheIdentifierGenerator->generateTag( + self::CONTENT_FIELDS_TYPE_IDENTIFIER, + [$versionInfo->contentInfo->contentTypeId] + ), + ]; + + return $this->getCacheTagsForVersion($versionInfo, $tags); + }; + } + + /** + * {@inheritdoc} + */ + public function create(CreateStruct $struct) + { + // Cached on demand when published or loaded + $this->logger->logCall(__METHOD__, ['struct' => $struct]); + + return $this->persistenceHandler->contentHandler()->create($struct); + } + + /** + * {@inheritdoc} + */ + public function createDraftFromVersion($contentId, $srcVersion, $userId, ?string $languageCode = null) + { + $this->logger->logCall(__METHOD__, ['content' => $contentId, 'version' => $srcVersion, 'user' => $userId]); + $draft = $this->persistenceHandler->contentHandler()->createDraftFromVersion($contentId, $srcVersion, $userId, $languageCode); + $this->cache->deleteItems([ + $this->cacheIdentifierGenerator->generateKey(self::CONTENT_VERSION_LIST_IDENTIFIER, [$contentId], true), + ]); + + return $draft; + } + + /** + * {@inheritdoc} + */ + public function copy($contentId, $versionNo = null, $newOwnerId = null) + { + $this->logger->logCall(__METHOD__, [ + 'content' => $contentId, + 'version' => $versionNo, + 'newOwner' => $newOwnerId, + ]); + + return $this->persistenceHandler->contentHandler()->copy($contentId, $versionNo, $newOwnerId); + } + + /** + * {@inheritdoc} + */ + public function load($contentId, $versionNo = null, array $translations = null) + { + $keySuffix = $versionNo ? "-{$versionNo}-" : '-'; + $keySuffix .= empty($translations) ? self::ALL_TRANSLATIONS_KEY : implode('|', $translations); + + return $this->getCacheValue( + (int) $contentId, + $this->cacheIdentifierGenerator->generateKey(self::CONTENT_IDENTIFIER, [], true) . '-', + function ($id) use ($versionNo, $translations) { + return $this->persistenceHandler->contentHandler()->load($id, $versionNo, $translations); + }, + $this->getContentTags, + function (Content $content) use ($keySuffix) { + // Version number & translations is part of keySuffix here and depends on what user asked for + return [ + $this->cacheIdentifierGenerator->generateKey( + self::CONTENT_IDENTIFIER, + [$content->versionInfo->contentInfo->id], + true + ) . $keySuffix, + ]; + }, + $keySuffix, + ['content' => $contentId, 'version' => $versionNo, 'translations' => $translations] + ); + } + + public function loadContentList(array $contentIds, array $translations = null): array + { + $keySuffix = '-' . (empty($translations) ? self::ALL_TRANSLATIONS_KEY : implode('|', $translations)); + + return $this->getMultipleCacheValues( + $contentIds, + $this->cacheIdentifierGenerator->generateKey(self::CONTENT_IDENTIFIER, [], true) . '-', + function (array $cacheMissIds) use ($translations) { + return $this->persistenceHandler->contentHandler()->loadContentList($cacheMissIds, $translations); + }, + $this->getContentTags, + function (Content $content) use ($keySuffix) { + // Translations is part of keySuffix here and depends on what user asked for + return [ + $this->cacheIdentifierGenerator->generateKey( + self::CONTENT_IDENTIFIER, + [$content->versionInfo->contentInfo->id], + true + ) . $keySuffix, + ]; + }, + $keySuffix, + ['content' => $contentIds, 'translations' => $translations] + ); + } + + /** + * {@inheritdoc} + */ + public function loadContentInfo($contentId) + { + return $this->getCacheValue( + $contentId, + $this->cacheIdentifierGenerator->generateKey(self::CONTENT_INFO_IDENTIFIER, [], true) . '-', + function ($contentId) { + return $this->persistenceHandler->contentHandler()->loadContentInfo($contentId); + }, + $this->getContentInfoTags, + $this->getContentInfoKeys, + '', + ['content' => $contentId] + ); + } + + public function loadContentInfoList(array $contentIds) + { + return $this->getMultipleCacheValues( + $contentIds, + $this->cacheIdentifierGenerator->generateKey(self::CONTENT_INFO_IDENTIFIER, [], true) . '-', + function (array $cacheMissIds) { + return $this->persistenceHandler->contentHandler()->loadContentInfoList($cacheMissIds); + }, + $this->getContentInfoTags, + $this->getContentInfoKeys, + '', + ['content' => $contentIds] + ); + } + + /** + * {@inheritdoc} + */ + public function loadContentInfoByRemoteId($remoteId) + { + return $this->getCacheValue( + $this->cacheIdentifierSanitizer->escapeForCacheKey($remoteId), + $this->cacheIdentifierGenerator->generateKey(self::CONTENT_INFO_BY_REMOTE_ID_IDENTIFIER, [], true) . '-', + function () use ($remoteId) { + return $this->persistenceHandler->contentHandler()->loadContentInfoByRemoteId($remoteId); + }, + $this->getContentInfoTags, + $this->getContentInfoKeys, + '', + ['content' => $remoteId] + ); + } + + /** + * {@inheritdoc} + */ + public function loadVersionInfo($contentId, $versionNo = null) + { + $keySuffix = $versionNo ? "-{$versionNo}" : ''; + $cacheItem = $this->cache->getItem( + $this->cacheIdentifierGenerator->generateKey( + self::CONTENT_VERSION_INFO_IDENTIFIER, + [$contentId], + true + ) . $keySuffix + ); + + if ($cacheItem->isHit()) { + $this->logger->logCacheHit(['content' => $contentId, 'version' => $versionNo]); + + return $cacheItem->get(); + } + + $this->logger->logCacheMiss(['content' => $contentId, 'version' => $versionNo]); + $versionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo($contentId, $versionNo); + $cacheItem->set($versionInfo); + $cacheItem->tag($this->getCacheTagsForVersion($versionInfo)); + $this->cache->save($cacheItem); + + return $versionInfo; + } + + public function countDraftsForUser(int $userId): int + { + $this->logger->logCall(__METHOD__, ['user' => $userId]); + + return $this->persistenceHandler->contentHandler()->countDraftsForUser($userId); + } + + /** + * {@inheritdoc} + */ + public function loadDraftsForUser($userId) + { + $this->logger->logCall(__METHOD__, ['user' => $userId]); + + return $this->persistenceHandler->contentHandler()->loadDraftsForUser($userId); + } + + public function loadDraftListForUser(int $userId, int $offset = 0, int $limit = -1): array + { + $this->logger->logCall(__METHOD__, ['user' => $userId, 'offset' => $offset, 'limit' => $limit]); + + return $this->persistenceHandler->contentHandler()->loadDraftListForUser($userId, $offset, $limit); + } + + /** + * {@inheritdoc} + */ + public function setStatus($contentId, $status, $versionNo) + { + $this->logger->logCall(__METHOD__, ['content' => $contentId, 'status' => $status, 'version' => $versionNo]); + $return = $this->persistenceHandler->contentHandler()->setStatus($contentId, $status, $versionNo); + + if ($status === VersionInfo::STATUS_PUBLISHED) { + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]), + ]); + } else { + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag( + self::CONTENT_VERSION_IDENTIFIER, + [$contentId, $versionNo] + ), + ]); + } + + return $return; + } + + /** + * {@inheritdoc} + */ + public function updateMetadata($contentId, MetadataUpdateStruct $struct) + { + $this->logger->logCall(__METHOD__, ['content' => $contentId, 'struct' => $struct]); + $contentInfo = $this->persistenceHandler->contentHandler()->updateMetadata($contentId, $struct); + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]), + ]); + + return $contentInfo; + } + + /** + * {@inheritdoc} + */ + public function updateContent($contentId, $versionNo, UpdateStruct $struct) + { + $this->logger->logCall(__METHOD__, ['content' => $contentId, 'version' => $versionNo, 'struct' => $struct]); + $content = $this->persistenceHandler->contentHandler()->updateContent($contentId, $versionNo, $struct); + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag( + self::CONTENT_VERSION_IDENTIFIER, + [$contentId, $versionNo] + ), + ]); + + return $content; + } + + /** + * {@inheritdoc} + */ + public function deleteContent($contentId) + { + $this->logger->logCall(__METHOD__, ['content' => $contentId]); + + // Load reverse field relations first + $reverseRelations = $this->persistenceHandler->contentHandler()->loadReverseRelations( + $contentId, + APIRelation::FIELD | APIRelation::ASSET + ); + + $return = $this->persistenceHandler->contentHandler()->deleteContent($contentId); + + if (!empty($reverseRelations)) { + $tags = \array_map( + function ($relation) { + // only the full content object *with* fields is affected by this + return $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$relation->sourceContentId]); + }, + $reverseRelations + ); + } else { + $tags = []; + } + $tags[] = $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]); + $this->cache->invalidateTags($tags); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function deleteVersion($contentId, $versionNo) + { + $this->logger->logCall(__METHOD__, ['content' => $contentId, 'version' => $versionNo]); + $return = $this->persistenceHandler->contentHandler()->deleteVersion($contentId, $versionNo); + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag( + self::CONTENT_VERSION_IDENTIFIER, + [$contentId, $versionNo] + ), + ]); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function listVersions($contentId, $status = null, $limit = -1) + { + // Don't cache non typical lookups to avoid filling up cache and tags. + if ($status !== null || $limit !== -1) { + $this->logger->logCall(__METHOD__, ['content' => $contentId, 'status' => $status]); + + return $this->persistenceHandler->contentHandler()->listVersions($contentId, $status, $limit); + } + + // Cache default lookups + $cacheItem = $this->cache->getItem( + $this->cacheIdentifierGenerator->generateKey(self::CONTENT_VERSION_LIST_IDENTIFIER, [$contentId], true) + ); + + if ($cacheItem->isHit()) { + $this->logger->logCacheHit(['content' => $contentId]); + + return $cacheItem->get(); + } + + $this->logger->logCacheMiss(['content' => $contentId]); + $versions = $this->persistenceHandler->contentHandler()->listVersions($contentId, $status, $limit); + $cacheItem->set($versions); + $tags = [ + $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]), + ]; + + foreach ($versions as $version) { + $tags = $this->getCacheTagsForVersion($version, $tags); + } + $cacheItem->tag($tags); + $this->cache->save($cacheItem); + + return $versions; + } + + /** + * {@inheritdoc} + */ + public function addRelation(RelationCreateStruct $relation) + { + $this->logger->logCall(__METHOD__, ['struct' => $relation]); + + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag( + self::CONTENT_IDENTIFIER, + [$relation->destinationContentId] + ), + ]); + + return $this->persistenceHandler->contentHandler()->addRelation($relation); + } + + /** + * {@inheritdoc} + */ + public function removeRelation($relationId, $type, ?int $destinationContentId = null): void + { + if (null === $destinationContentId) { + @trigger_error('Expecting to pass $destinationContentId argument since version 4.1.5', E_USER_DEPRECATED); + } + + $this->logger->logCall(__METHOD__, ['relation' => $relationId, 'type' => $type]); + + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag( + self::CONTENT_IDENTIFIER, + [$destinationContentId ?? $this->loadRelation($relationId)->destinationContentId] + ), + $this->cacheIdentifierGenerator->generateTag( + self::RELATION_IDENTIFIER, + [$relationId] + ), + ]); + + $this->persistenceHandler->contentHandler()->removeRelation($relationId, $type, $destinationContentId); + } + + public function loadRelation(int $relationId): Relation + { + $cacheItem = $this->cache->getItem( + $this->cacheIdentifierGenerator->generateKey( + self::RELATION_IDENTIFIER, + [$relationId], + true + ) + ); + + if ($cacheItem->isHit()) { + $this->logger->logCacheHit(['relationId' => $relationId]); + + return $cacheItem->get(); + } + + $this->logger->logCacheMiss(['relationId' => $relationId]); + $relation = $this->persistenceHandler->contentHandler()->loadRelation($relationId); + $cacheItem->set($relation); + $tags = [ + $this->cacheIdentifierGenerator->generateTag(self::RELATION_IDENTIFIER, [$relationId]), + ]; + + $cacheItem->tag($tags); + $this->cache->save($cacheItem); + + return $relation; + } + + /** + * {@inheritdoc} + */ + public function loadRelations($sourceContentId, $sourceContentVersionNo = null, $type = null) + { + $this->logger->logCall( + __METHOD__, + [ + 'content' => $sourceContentId, + 'version' => $sourceContentVersionNo, + 'type' => $type, + ] + ); + + return $this->persistenceHandler->contentHandler()->loadRelations($sourceContentId, $sourceContentVersionNo, $type); + } + + public function countRelations(int $sourceContentId, ?int $sourceContentVersionNo = null, ?int $type = null): int + { + $cacheItem = $this->cache->getItem( + $this->cacheIdentifierGenerator->generateKey( + self::CONTENT_RELATIONS_COUNT_WITH_VERSION_TYPE_IDENTIFIER, + [$sourceContentId, $sourceContentVersionNo, $type], + true + ) + ); + + if ($cacheItem->isHit()) { + $this->logger->logCacheHit(['content' => $sourceContentId, 'version' => $sourceContentVersionNo, 'type' => $type]); + + return $cacheItem->get(); + } + + $this->logger->logCacheMiss(['content' => $sourceContentId, 'version' => $sourceContentVersionNo, 'type' => $type]); + $relationsCount = $this->persistenceHandler->contentHandler()->countRelations( + $sourceContentId, + $sourceContentVersionNo, + $type + ); + $cacheItem->set($relationsCount); + $tags = [ + $this->cacheIdentifierGenerator->generateTag( + self::CONTENT_IDENTIFIER, + [$sourceContentId] + ), + ]; + + $cacheItem->tag($tags); + $this->cache->save($cacheItem); + + return $relationsCount; + } + + public function loadRelationList( + int $sourceContentId, + int $limit, + int $offset = 0, + ?int $sourceContentVersionNo = null, + ?int $type = null + ): array { + return $this->getListCacheValue( + $this->cacheIdentifierGenerator->generateKey( + self::CONTENT_RELATIONS_LIST_WITH_VERSION_TYPE_IDENTIFIER, + [$sourceContentId, $limit, $offset, $sourceContentVersionNo, $type], + true + ), + function () use ($sourceContentId, $limit, $offset, $sourceContentVersionNo, $type): array { + return $this->persistenceHandler->contentHandler()->loadRelationList( + $sourceContentId, + $limit, + $offset, + $sourceContentVersionNo, + $type + ); + }, + function (Relation $relation): array { + return [ + $this->cacheIdentifierGenerator->generateTag( + self::CONTENT_RELATION_IDENTIFIER, + [$relation->destinationContentId] + ), + $this->cacheIdentifierGenerator->generateTag( + self::CONTENT_IDENTIFIER, + [$relation->destinationContentId] + ), + ]; + }, + function (Relation $relation): array { + return [ + $this->cacheIdentifierGenerator->generateKey(self::CONTENT_IDENTIFIER, [$relation->destinationContentId], true), + ]; + }, + function () use ($sourceContentId): array { + return [ + $this->cacheIdentifierGenerator->generateTag( + self::CONTENT_RELATIONS_LIST_IDENTIFIER, + [$sourceContentId] + ), + $this->cacheIdentifierGenerator->generateTag( + self::CONTENT_IDENTIFIER, + [$sourceContentId] + ), + ]; + }, + [$sourceContentId, $limit, $offset, $sourceContentVersionNo, $type] + ); + } + + /** + * {@inheritdoc} + */ + public function countReverseRelations(int $destinationContentId, ?int $type = null): int + { + $cacheItem = $this->cache->getItem( + $this->cacheIdentifierGenerator->generateKey( + self::CONTENT_REVERSE_RELATIONS_COUNT_IDENTIFIER, + [$destinationContentId], + true + ) + ); + + if ($cacheItem->isHit()) { + $this->logger->logCacheHit(['content' => $destinationContentId]); + + return $cacheItem->get(); + } + + $this->logger->logCacheMiss(['content' => $destinationContentId]); + $reverseRelationsCount = $this->persistenceHandler->contentHandler()->countReverseRelations($destinationContentId, $type); + $cacheItem->set($reverseRelationsCount); + $tags = [ + $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$destinationContentId]), + ]; + + $cacheItem->tag($tags); + $this->cache->save($cacheItem); + + return $reverseRelationsCount; + } + + /** + * {@inheritdoc} + */ + public function loadReverseRelations($destinationContentId, $type = null) + { + $this->logger->logCall(__METHOD__, ['content' => $destinationContentId, 'type' => $type]); + + return $this->persistenceHandler->contentHandler()->loadReverseRelations($destinationContentId, $type); + } + + /** + * {@inheritdoc} + */ + public function loadReverseRelationList( + int $destinationContentId, + int $offset = 0, + int $limit = -1, + ?int $type = null + ): array { + $this->logger->logCall(__METHOD__, [ + 'content' => $destinationContentId, + 'offset' => $offset, + 'limit' => $limit, + 'type' => $type, + ]); + + return $this->persistenceHandler->contentHandler()->loadReverseRelationList( + $destinationContentId, + $offset, + $limit, + $type + ); + } + + /** + * {@inheritdoc} + */ + public function publish($contentId, $versionNo, MetadataUpdateStruct $struct) + { + $this->logger->logCall(__METHOD__, ['content' => $contentId, 'version' => $versionNo, 'struct' => $struct]); + $content = $this->persistenceHandler->contentHandler()->publish($contentId, $versionNo, $struct); + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]), + ]); + + return $content; + } + + /** + * {@inheritdoc} + */ + public function deleteTranslationFromContent($contentId, $languageCode) + { + $this->logger->logCall( + __METHOD__, + [ + 'contentId' => $contentId, + 'languageCode' => $languageCode, + ] + ); + + $this->persistenceHandler->contentHandler()->deleteTranslationFromContent($contentId, $languageCode); + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]), + ]); + } + + /** + * {@inheritdoc} + */ + public function deleteTranslationFromDraft($contentId, $versionNo, $languageCode) + { + $this->logger->logCall( + __METHOD__, + ['content' => $contentId, 'version' => $versionNo, 'languageCode' => $languageCode] + ); + $content = $this->persistenceHandler->contentHandler()->deleteTranslationFromDraft( + $contentId, + $versionNo, + $languageCode + ); + + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag(self::CONTENT_VERSION_IDENTIFIER, [$contentId, $versionNo]), + ]); + + return $content; + } + + /** + * Return relevant content and location tags so cache can be purged reliably. + * + * For use when generating cache, not on invalidation. + * + * @param array $tags Optional, can be used to specify other tags. + */ + private function getCacheTagsForVersion(VersionInfo $versionInfo, array $tags = []): array + { + $contentInfo = $versionInfo->contentInfo; + $tags[] = $this->cacheIdentifierGenerator->generateTag( + self::CONTENT_VERSION_IDENTIFIER, + [$contentInfo->id, $versionInfo->versionNo] + ); + + $getContentInfoTagsFn = $this->getContentInfoTags; + + return $getContentInfoTagsFn($contentInfo, $tags); + } + + public function loadVersionInfoList(array $contentIds): array + { + return $this->getMultipleCacheValues( + $contentIds, + $this->cacheIdentifierGenerator->generateKey( + self::CONTENT_VERSION_INFO_IDENTIFIER, + [], + true + ) . '-', + function (array $cacheMissIds): array { + return $this->persistenceHandler->contentHandler()->loadVersionInfoList($cacheMissIds); + }, + function (VersionInfo $versionInfo): array { + return $this->getCacheTagsForVersion($versionInfo); + }, + function (VersionInfo $versionInfo) { + return [ + $this->cacheIdentifierGenerator->generateKey( + self::CONTENT_VERSION_INFO_IDENTIFIER, + [$versionInfo->contentInfo->id], + true + ), + ]; + }, + '', + ['content' => $contentIds] + ); + } +} + +class_alias(ContentHandler::class, 'eZ\Publish\Core\Persistence\Cache\ContentHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/ContentLanguageHandler.php b/src/lib/Persistence/Cache/ContentLanguageHandler.php similarity index 93% rename from eZ/Publish/Core/Persistence/Cache/ContentLanguageHandler.php rename to src/lib/Persistence/Cache/ContentLanguageHandler.php index c6908a5b91..96c6d167e1 100644 --- a/eZ/Publish/Core/Persistence/Cache/ContentLanguageHandler.php +++ b/src/lib/Persistence/Cache/ContentLanguageHandler.php @@ -4,15 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\SPI\Persistence\Content\Language; -use eZ\Publish\SPI\Persistence\Content\Language\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as ContentLanguageHandlerInterface; +use Ibexa\Contracts\Core\Persistence\Content\Language; +use Ibexa\Contracts\Core\Persistence\Content\Language\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as ContentLanguageHandlerInterface; -/** - * @see \eZ\Publish\SPI\Persistence\Content\Language\Handler - */ class ContentLanguageHandler extends AbstractInMemoryPersistenceHandler implements ContentLanguageHandlerInterface { private const LANGUAGE_IDENTIFIER = 'language'; @@ -176,3 +173,5 @@ public function delete($id) return $return; } } + +class_alias(ContentLanguageHandler::class, 'eZ\Publish\Core\Persistence\Cache\ContentLanguageHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/ContentTypeHandler.php b/src/lib/Persistence/Cache/ContentTypeHandler.php similarity index 92% rename from eZ/Publish/Core/Persistence/Cache/ContentTypeHandler.php rename to src/lib/Persistence/Cache/ContentTypeHandler.php index befe75dfb5..ed24ed5fc7 100644 --- a/eZ/Publish/Core/Persistence/Cache/ContentTypeHandler.php +++ b/src/lib/Persistence/Cache/ContentTypeHandler.php @@ -4,23 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\Group\CreateStruct as GroupCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Group\UpdateStruct as GroupUpdateStruct; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandlerInterface; -use eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Type; +use Ibexa\Contracts\Core\Persistence\Content\Type\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\Type\Group\CreateStruct as GroupCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Type\Group\UpdateStruct as GroupUpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as ContentTypeHandlerInterface; +use Ibexa\Contracts\Core\Persistence\Content\Type\UpdateStruct; -/** - * ContentType cache. - * - * Caches defined (published) content types and content type groups. - * - * @see \eZ\Publish\SPI\Persistence\Content\Type\Handler - */ class ContentTypeHandler extends AbstractInMemoryPersistenceHandler implements ContentTypeHandlerInterface { private const TYPE_IDENTIFIER = 'type'; @@ -34,6 +27,7 @@ class ContentTypeHandler extends AbstractInMemoryPersistenceHandler implements C private const BY_IDENTIFIER_SUFFIX = 'by_identifier_suffix'; private const CONTENT_TYPE_LIST_BY_GROUP_IDENTIFIER = 'content_type_list_by_group'; private const BY_REMOTE_SUFFIX = 'by_remote_suffix'; + private const CONTENT_TYPE_LIST_BY_FIELD_DEFINITION_IDENTIFIER = 'content_type_list_by_field_definition_identifier'; private const TYPE_MAP_IDENTIFIER = 'type_map'; private const CONTENT_FIELDS_TYPE_IDENTIFIER = 'content_fields_type'; private const TYPE_WITHOUT_VALUE_IDENTIFIER = 'type_without_value'; @@ -311,6 +305,20 @@ function () use ($remoteId) { ); } + public function loadContentTypesByFieldDefinitionIdentifier(string $identifier): array + { + return $this->getListCacheValue( + $this->cacheIdentifierGenerator->generateKey(self::CONTENT_TYPE_LIST_BY_FIELD_DEFINITION_IDENTIFIER, [$identifier], true), + function () use ($identifier): array { + return $this->persistenceHandler->contentTypeHandler()->loadContentTypesByFieldDefinitionIdentifier($identifier); + }, + $this->getTypeTags, + $this->getTypeKeys, + null, + [$identifier] + ); + } + /** * {@inheritdoc} */ @@ -490,13 +498,21 @@ public function addFieldDefinition($typeId, $status, FieldDefinition $struct) /** * {@inheritdoc} */ - public function removeFieldDefinition($typeId, $status, $fieldDefinitionId) - { - $this->logger->logCall(__METHOD__, ['type' => $typeId, 'status' => $status, 'field' => $fieldDefinitionId]); + public function removeFieldDefinition( + int $typeId, + int $status, + FieldDefinition $fieldDefinition + ): void { + $this->logger->logCall(__METHOD__, [ + 'type' => $typeId, + 'status' => $status, + 'field' => $fieldDefinition, + ]); + $this->persistenceHandler->contentTypeHandler()->removeFieldDefinition( $typeId, $status, - $fieldDefinitionId + $fieldDefinition ); if ($status === Type::STATUS_DEFINED) { @@ -544,7 +560,7 @@ public function publish($typeId) $this->cacheIdentifierGenerator->generateTag(self::CONTENT_FIELDS_TYPE_IDENTIFIER, [$typeId]), ]); - // Clear Content Type Groups list cache + // Clear content type groups list cache $contentType = $this->load($typeId); $this->cache->deleteItems( array_map( @@ -584,7 +600,7 @@ function () { * @param int $contentTypeId * @param string $languageCode * - * @return \eZ\Publish\SPI\Persistence\Content\Type + * @return \Ibexa\Contracts\Core\Persistence\Content\Type * * @throws \Psr\Cache\InvalidArgumentException */ @@ -618,3 +634,5 @@ public function deleteByUserAndStatus(int $userId, int $status): void } } } + +class_alias(ContentTypeHandler::class, 'eZ\Publish\Core\Persistence\Cache\ContentTypeHandler'); diff --git a/src/lib/Persistence/Cache/Handler.php b/src/lib/Persistence/Cache/Handler.php new file mode 100644 index 0000000000..08d4cd17f6 --- /dev/null +++ b/src/lib/Persistence/Cache/Handler.php @@ -0,0 +1,279 @@ +persistenceHandler = $persistenceHandler; + $this->sectionHandler = $sectionHandler; + $this->locationHandler = $locationHandler; + $this->contentHandler = $contentHandler; + $this->contentLanguageHandler = $contentLanguageHandler; + $this->contentTypeHandler = $contentTypeHandler; + $this->userHandler = $userHandler; + $this->transactionHandler = $transactionHandler; + $this->trashHandler = $trashHandler; + $this->urlAliasHandler = $urlAliasHandler; + $this->objectStateHandler = $objectStateHandler; + $this->urlHandler = $urlHandler; + $this->bookmarkHandler = $bookmarkHandler; + $this->notificationHandler = $notificationHandler; + $this->userPreferenceHandler = $userPreferenceHandler; + $this->urlWildcardHandler = $urlWildcardHandler; + $this->settingHandler = $settingHandler; + $this->logger = $logger; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Handler + */ + public function contentHandler() + { + return $this->contentHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Handler + */ + public function contentTypeHandler() + { + return $this->contentTypeHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Language\Handler + */ + public function contentLanguageHandler() + { + return $this->contentLanguageHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Location\Handler + */ + public function locationHandler() + { + return $this->locationHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Handler + */ + public function objectStateHandler() + { + return $this->objectStateHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\User\Handler + */ + public function userHandler() + { + return $this->userHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Section\Handler + */ + public function sectionHandler() + { + return $this->sectionHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Location\Trash\Handler + */ + public function trashHandler() + { + return $this->trashHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlAlias\Handler + */ + public function urlAliasHandler() + { + return $this->urlAliasHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlWildcard\Handler + */ + public function urlWildcardHandler() + { + return $this->urlWildcardHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\TransactionHandler + */ + public function transactionHandler() + { + return $this->transactionHandler; + } + + public function settingHandler(): SPISettingHandler + { + return $this->settingHandler; + } + + /** + * @return \Ibexa\Core\Persistence\Cache\URLHandler + */ + public function urlHandler() + { + return $this->urlHandler; + } + + /** + * @return \Ibexa\Core\Persistence\Cache\BookmarkHandler + */ + public function bookmarkHandler() + { + return $this->bookmarkHandler; + } + + /** + * @return \Ibexa\Core\Persistence\Cache\NotificationHandler + */ + public function notificationHandler() + { + return $this->notificationHandler; + } + + /** + * @return \Ibexa\Core\Persistence\Cache\UserPreferenceHandler + */ + public function userPreferenceHandler() + { + return $this->userPreferenceHandler; + } + + /** + * {@inheritdoc} + */ + public function beginTransaction() + { + $this->transactionHandler->beginTransaction(); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + $this->transactionHandler->commit(); + } + + /** + * {@inheritdoc} + */ + public function rollback() + { + $this->transactionHandler->rollback(); + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Cache\Handler'); diff --git a/src/lib/Persistence/Cache/Identifier/CacheIdentifierGenerator.php b/src/lib/Persistence/Cache/Identifier/CacheIdentifierGenerator.php index 6d5cf47812..f0b0233922 100644 --- a/src/lib/Persistence/Cache/Identifier/CacheIdentifierGenerator.php +++ b/src/lib/Persistence/Cache/Identifier/CacheIdentifierGenerator.php @@ -8,15 +8,20 @@ namespace Ibexa\Core\Persistence\Cache\Identifier; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Psr\Log\LoggerAwareInterface; +use Psr\Log\LoggerAwareTrait; +use Psr\Log\NullLogger; /** * @internal */ -final class CacheIdentifierGenerator implements CacheIdentifierGeneratorInterface +final class CacheIdentifierGenerator implements CacheIdentifierGeneratorInterface, LoggerAwareInterface { private const PLACEHOLDER = '-%s'; + use LoggerAwareTrait; + /** @var string */ private $prefix; @@ -31,10 +36,11 @@ public function __construct(string $prefix, array $tagPatterns, array $keyPatter $this->prefix = $prefix; $this->tagPatterns = $tagPatterns; $this->keyPatterns = $keyPatterns; + $this->logger = new NullLogger(); } /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function generateTag(string $patternName, array $values = [], bool $withPrefix = false): string { @@ -50,7 +56,7 @@ public function generateTag(string $patternName, array $values = [], bool $withP } /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function generateKey(string $patternName, array $values = [], bool $withPrefix = false): string { @@ -77,6 +83,12 @@ private function generate(string $pattern, array $values, bool $withPrefix = fal $cacheIdentifier = $this->prefix . $cacheIdentifier; } + $this->logger->debug(sprintf('Generated cache identifier: %s', $cacheIdentifier), [ + 'values' => $values, + 'pattern' => $pattern, + 'prefix' => $withPrefix ? $this->prefix : null, + ]); + return $cacheIdentifier; } } diff --git a/src/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorInterface.php b/src/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorInterface.php index 3513adc934..ddaf07e548 100644 --- a/src/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorInterface.php +++ b/src/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorInterface.php @@ -18,7 +18,7 @@ interface CacheIdentifierGeneratorInterface * @param array $values containing scalars, mostly integers and strings * @param bool $withPrefix used mainly by keys, if set to true, tags will be prefixed with ibx- * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function generateTag(string $patternName, array $values = [], bool $withPrefix = false): string; @@ -27,7 +27,7 @@ public function generateTag(string $patternName, array $values = [], bool $withP * @param array $values containing scalars, mostly integers and strings * @param bool $withPrefix used mainly by keys, if set to true, tags will be prefixed with ibx- * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function generateKey(string $patternName, array $values = [], bool $withPrefix = false): string; } diff --git a/eZ/Publish/Core/Persistence/Cache/InMemory/InMemoryCache.php b/src/lib/Persistence/Cache/InMemory/InMemoryCache.php similarity index 98% rename from eZ/Publish/Core/Persistence/Cache/InMemory/InMemoryCache.php rename to src/lib/Persistence/Cache/InMemory/InMemoryCache.php index 323117382e..49c69805fd 100644 --- a/eZ/Publish/Core/Persistence/Cache/InMemory/InMemoryCache.php +++ b/src/lib/Persistence/Cache/InMemory/InMemoryCache.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache\InMemory; +namespace Ibexa\Core\Persistence\Cache\InMemory; /** * Simple internal In-Memory Cache Pool. @@ -218,3 +218,5 @@ private function vacuum(): void } } } + +class_alias(InMemoryCache::class, 'eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache'); diff --git a/eZ/Publish/Core/Persistence/Cache/InMemory/README.md b/src/lib/Persistence/Cache/InMemory/README.md similarity index 100% rename from eZ/Publish/Core/Persistence/Cache/InMemory/README.md rename to src/lib/Persistence/Cache/InMemory/README.md diff --git a/eZ/Publish/Core/Persistence/Cache/LocationHandler.php b/src/lib/Persistence/Cache/LocationHandler.php similarity index 97% rename from eZ/Publish/Core/Persistence/Cache/LocationHandler.php rename to src/lib/Persistence/Cache/LocationHandler.php index d34c03d3b0..dc07fd9f93 100644 --- a/eZ/Publish/Core/Persistence/Cache/LocationHandler.php +++ b/src/lib/Persistence/Cache/LocationHandler.php @@ -4,16 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as LocationHandlerInterface; -use eZ\Publish\SPI\Persistence\Content\Location\UpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\Location\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandlerInterface; +use Ibexa\Contracts\Core\Persistence\Content\Location\UpdateStruct; -/** - * @see \eZ\Publish\SPI\Persistence\Content\Location\Handler - */ class LocationHandler extends AbstractInMemoryPersistenceHandler implements LocationHandlerInterface { private const CONTENT_IDENTIFIER = 'content'; @@ -462,7 +459,7 @@ public function countAllLocations() * @param int $offset * @param int $limit * - * @return \eZ\Publish\SPI\Persistence\Content\Location[] + * @return \Ibexa\Contracts\Core\Persistence\Content\Location[] */ public function loadAllLocations($offset, $limit) { @@ -510,3 +507,5 @@ public function countLocationsByContent(int $contentId): int return $this->persistenceHandler->locationHandler()->countLocationsByContent($contentId); } } + +class_alias(LocationHandler::class, 'eZ\Publish\Core\Persistence\Cache\LocationHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/NotificationHandler.php b/src/lib/Persistence/Cache/NotificationHandler.php similarity index 91% rename from eZ/Publish/Core/Persistence/Cache/NotificationHandler.php rename to src/lib/Persistence/Cache/NotificationHandler.php index 0442b104bb..572687d4cc 100644 --- a/eZ/Publish/Core/Persistence/Cache/NotificationHandler.php +++ b/src/lib/Persistence/Cache/NotificationHandler.php @@ -6,19 +6,14 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\API\Repository\Values\Notification\Notification as APINotification; -use eZ\Publish\SPI\Persistence\Notification\CreateStruct; -use eZ\Publish\SPI\Persistence\Notification\Handler; -use eZ\Publish\SPI\Persistence\Notification\Notification; -use eZ\Publish\SPI\Persistence\Notification\UpdateStruct; +use Ibexa\Contracts\Core\Persistence\Notification\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Notification\Handler; +use Ibexa\Contracts\Core\Persistence\Notification\Notification; +use Ibexa\Contracts\Core\Persistence\Notification\UpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Notification\Notification as APINotification; -/** - * SPI cache for Notification Handler. - * - * @see \eZ\Publish\SPI\Persistence\Notification\Handler - */ class NotificationHandler extends AbstractHandler implements Handler { private const NOTIFICATION_IDENTIFIER = 'notification'; @@ -169,3 +164,5 @@ public function loadUserNotifications(int $userId, int $offset, int $limit): arr return $this->persistenceHandler->notificationHandler()->loadUserNotifications($userId, $offset, $limit); } } + +class_alias(NotificationHandler::class, 'eZ\Publish\Core\Persistence\Cache\NotificationHandler'); diff --git a/src/lib/Persistence/Cache/ObjectStateHandler.php b/src/lib/Persistence/Cache/ObjectStateHandler.php new file mode 100644 index 0000000000..20b68167f7 --- /dev/null +++ b/src/lib/Persistence/Cache/ObjectStateHandler.php @@ -0,0 +1,372 @@ +logger->logCall(__METHOD__, ['struct' => $input]); + $group = $this->persistenceHandler->objectStateHandler()->createGroup($input); + + $this->cache->deleteItem( + $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_ALL_IDENTIFIER, [], true) + ); + + return $group; + } + + /** + * {@inheritdoc} + */ + public function loadGroup($groupId) + { + return $this->getCacheValue( + (int) $groupId, + $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_IDENTIFIER, [], true) . '-', + function (int $groupId): Group { + $this->logger->logCall(__CLASS__ . '::loadGroup', ['groupId' => (int) $groupId]); + + return $this->persistenceHandler->objectStateHandler()->loadGroup($groupId); + }, + function () use ($groupId): array { + return [ + $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [(int) $groupId]), + ]; + }, + function () use ($groupId) { + return [ + $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_IDENTIFIER, [(int) $groupId], true), + ]; + } + ); + } + + /** + * {@inheritdoc} + */ + public function loadGroupByIdentifier($identifier) + { + $escapedIdentifier = $this->cacheIdentifierSanitizer->escapeForCacheKey($identifier); + + return $this->getCacheValue( + $identifier, + $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_IDENTIFIER, [], true) . '-', + function (string $identifier): Group { + $this->logger->logCall(__CLASS__ . '::loadGroupByIdentifier', ['groupId' => $identifier]); + + return $this->persistenceHandler->objectStateHandler()->loadGroupByIdentifier($identifier); + }, + function (Group $group): array { + return [ + $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$group->id]), + ]; + }, + function (Group $group) use ($escapedIdentifier): array { + return [ + $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_WITH_ID_SUFFIX_IDENTIFIER, [$escapedIdentifier], true), + $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_IDENTIFIER, [$group->id], true), + ]; + }, + $this->cacheIdentifierGenerator->generateKey(self::BY_IDENTIFIER_SUFFIX) + ); + } + + /** + * {@inheritdoc} + */ + public function loadAllGroups($offset = 0, $limit = -1) + { + $stateGroups = $this->getListCacheValue( + $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_ALL_IDENTIFIER, [], true), + function () use ($offset, $limit): array { + $this->logger->logCall(__CLASS__ . '::loadAllGroups', ['offset' => (int) $offset, 'limit' => (int) $limit]); + + return $this->persistenceHandler->objectStateHandler()->loadAllGroups(0, -1); + }, + function (Group $group): array { + return [ + $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$group->id]), + ]; + }, + function (Group $group): array { + return [ + $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_IDENTIFIER, [$group->id], true), + $this->cacheIdentifierGenerator->generateKey(self::STATE_GROUP_WITH_ID_SUFFIX_IDENTIFIER, [$group->id], true), + ]; + } + ); + + return \array_slice($stateGroups, $offset, $limit > -1 ? $limit : null); + } + + /** + * {@inheritdoc} + */ + public function loadObjectStates($groupId) + { + return $this->getCacheValue( + $groupId, + $this->cacheIdentifierGenerator->generateKey(self::STATE_LIST_BY_GROUP_IDENTIFIER, [], true) . '-', + function (int $groupId): array { + $this->logger->logCall(__CLASS__ . '::loadObjectStates', ['groupId' => (int) $groupId]); + + return $this->persistenceHandler->objectStateHandler()->loadObjectStates($groupId); + }, + function (array $objectStates) use ($groupId): array { + $cacheTags = []; + $cacheTags[] = $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$groupId]); + foreach ($objectStates as $state) { + $cacheTags[] = $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$state->id]); + } + + return $cacheTags; + }, + function () use ($groupId): array { + return [ + $this->cacheIdentifierGenerator->generateKey(self::STATE_LIST_BY_GROUP_IDENTIFIER, [$groupId], true), + ]; + } + ); + } + + /** + * {@inheritdoc} + */ + public function updateGroup($groupId, InputStruct $input) + { + $this->logger->logCall(__METHOD__, ['groupId' => $groupId, 'struct' => $input]); + $return = $this->persistenceHandler->objectStateHandler()->updateGroup($groupId, $input); + + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$groupId]), + ]); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function deleteGroup($groupId) + { + $this->logger->logCall(__METHOD__, ['groupId' => $groupId]); + $return = $this->persistenceHandler->objectStateHandler()->deleteGroup($groupId); + + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$groupId]), + ]); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function create($groupId, InputStruct $input) + { + $this->logger->logCall(__METHOD__, ['groupId' => $groupId, 'struct' => $input]); + $return = $this->persistenceHandler->objectStateHandler()->create($groupId, $input); + + $this->cache->deleteItem( + $this->cacheIdentifierGenerator->generateKey(self::STATE_LIST_BY_GROUP_IDENTIFIER, [$groupId], true) + ); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function load($stateId) + { + return $this->getCacheValue( + (int) $stateId, + $this->cacheIdentifierGenerator->generateKey(self::STATE_IDENTIFIER, [], true) . '-', + function (int $stateId): ObjectState { + $this->logger->logCall(__CLASS__ . '::load', ['stateId' => $stateId]); + + return $this->persistenceHandler->objectStateHandler()->load($stateId); + }, + function (ObjectState $objectState): array { + return [ + $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$objectState->id]), + $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$objectState->groupId]), + ]; + }, + function () use ($stateId): array { + return [ + $this->cacheIdentifierGenerator->generateKey(self::STATE_IDENTIFIER, [$stateId], true), + ]; + } + ); + } + + /** + * {@inheritdoc} + */ + public function loadByIdentifier($identifier, $groupId) + { + $escapedIdentifier = $this->cacheIdentifierSanitizer->escapeForCacheKey($identifier); + + return $this->getCacheValue( + $identifier, + $this->cacheIdentifierGenerator->generateKey(self::STATE_ID_IDENTIFIER, [], true) . '-', + function (string $identifier) use ($groupId): ObjectState { + $this->logger->logCall(__CLASS__ . '::loadByIdentifier', ['identifier' => $identifier, 'groupId' => (int) $groupId]); + + return $this->persistenceHandler->objectStateHandler()->loadByIdentifier($identifier, (int) $groupId); + }, + function (ObjectState $objectState): array { + return [ + $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$objectState->id]), + $this->cacheIdentifierGenerator->generateTag(self::STATE_GROUP_IDENTIFIER, [$objectState->groupId]), + ]; + }, + function () use ($escapedIdentifier, $groupId): array { + return [ + $this->cacheIdentifierGenerator->generateKey( + self::STATE_ID_IDENTIFIER_WITH_BY_GROUP_SUFFIX_IDENTIFIER, + [$escapedIdentifier, $groupId], + true + ), + ]; + }, + '-' . $this->cacheIdentifierGenerator->generateKey(self::BY_GROUP_IDENTIFIER, [$groupId]) + ); + } + + /** + * {@inheritdoc} + */ + public function update($stateId, InputStruct $input) + { + $this->logger->logCall(__METHOD__, ['stateId' => $stateId, 'struct' => $input]); + $return = $this->persistenceHandler->objectStateHandler()->update($stateId, $input); + + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$stateId]), + ]); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function setPriority($stateId, $priority) + { + $this->logger->logCall(__METHOD__, ['stateId' => $stateId, 'priority' => $priority]); + $return = $this->persistenceHandler->objectStateHandler()->setPriority($stateId, $priority); + + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$stateId]), + ]); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function delete($stateId) + { + $this->logger->logCall(__METHOD__, ['stateId' => $stateId]); + $return = $this->persistenceHandler->objectStateHandler()->delete($stateId); + + $this->cache->invalidateTags([ + $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$stateId]), + ]); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function setContentState($contentId, $groupId, $stateId) + { + $this->logger->logCall(__METHOD__, ['contentId' => $contentId, 'groupId' => $groupId, 'stateId' => $stateId]); + $return = $this->persistenceHandler->objectStateHandler()->setContentState($contentId, $groupId, $stateId); + + $this->cache->deleteItem( + $this->cacheIdentifierGenerator->generateKey( + self::STATE_BY_GROUP_ON_CONTENT_IDENTIFIER, + [$groupId, $contentId], + true + ) + ); + + return $return; + } + + /** + * {@inheritdoc} + */ + public function getContentState($contentId, $stateGroupId) + { + return $this->getCacheValue( + (int) $stateGroupId, + $this->cacheIdentifierGenerator->generateKey(self::STATE_BY_GROUP_IDENTIFIER, [], true) . '-', + function (int $stateGroupId) use ($contentId): ObjectState { + $this->logger->logCall(__CLASS__ . '::getContentState', ['contentId' => (int) $contentId, 'stateGroupId' => $stateGroupId]); + + return $this->persistenceHandler->objectStateHandler()->getContentState((int) $contentId, $stateGroupId); + }, + function (ObjectState $contentState) use ($contentId): array { + return [ + $this->cacheIdentifierGenerator->generateTag(self::STATE_IDENTIFIER, [$contentState->id]), + $this->cacheIdentifierGenerator->generateTag(self::CONTENT_IDENTIFIER, [$contentId]), + ]; + }, + function () use ($contentId, $stateGroupId): array { + return [ + $this->cacheIdentifierGenerator->generateKey( + self::STATE_BY_GROUP_ON_CONTENT_IDENTIFIER, + [$stateGroupId, $contentId], + true + ), + ]; + }, + '-' . $this->cacheIdentifierGenerator->generateKey(self::ON_CONTENT_IDENTIFIER, [$contentId]) + ); + } + + /** + * {@inheritdoc} + */ + public function getContentCount($stateId) + { + $this->logger->logCall(__METHOD__, ['stateId' => $stateId]); + + return $this->persistenceHandler->objectStateHandler()->getContentCount($stateId); + } +} + +class_alias(ObjectStateHandler::class, 'eZ\Publish\Core\Persistence\Cache\ObjectStateHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/PersistenceLogger.php b/src/lib/Persistence/Cache/PersistenceLogger.php similarity index 96% rename from eZ/Publish/Core/Persistence/Cache/PersistenceLogger.php rename to src/lib/Persistence/Cache/PersistenceLogger.php index cb9c27e99b..53da8fbb47 100644 --- a/eZ/Publish/Core/Persistence/Cache/PersistenceLogger.php +++ b/src/lib/Persistence/Cache/PersistenceLogger.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; /** * Log un-cached & cached use of SPI Persistence. @@ -71,10 +71,10 @@ public function logCall(string $method, array $arguments = []): void /** * Log Cache miss, gets info it needs by backtrace if needed. * + * @since 7.5 + * * @param array $arguments * @param int $traceOffset - * - * @since 7.5 */ public function logCacheMiss(array $arguments = [], int $traceOffset = 2): void { @@ -95,12 +95,12 @@ public function logCacheMiss(array $arguments = [], int $traceOffset = 2): void /** * Log a Cache hit, gets info it needs by backtrace if needed. * + * @since 7.5 + * * @param array $arguments * @param int $traceOffset * @param bool $inMemory Denotes is cache hit was from memory (php variable), as opposed to from cache pool which * is usually disk or remote cache service. - * - * @since 7.5 */ public function logCacheHit(array $arguments = [], int $traceOffset = 2, bool $inMemory = false): void { @@ -164,9 +164,9 @@ private function collectCacheCallData($method, array $arguments, array $trimmedB /** * Simplify trace to an array of strings. * - * Skipps any traces from Syfony proxies or closures to make trace as readable as possible in as few lines as + * Skips any traces from Symfony proxies or closures to make trace as readable as possible in as few lines as * possible. And point is to identify which code outside kernel is triggering the SPI call, so trace stops one - * call after namespace is no longer in eZ\Publish\Core\. + * call after namespace is no longer in \Ibexa\Core\. * * @param array $backtrace Partial backtrace from |debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS) or similar. * @@ -189,7 +189,7 @@ private function getSimpleCallTrace(array $backtrace): array } // Break out as soon as we have listed 2 classes outside of kernel - if ($call['class'][0] !== 'e' && \strpos($call['class'], 'eZ\\Publish\\Core\\') !== 0) { + if ($call['class'][0] !== 'e' && \strpos($call['class'], 'Ibexa\\Core\\') !== 0) { $exitOnNext = true; } } @@ -250,3 +250,5 @@ public function getLoadedUnCachedHandlers(): array return $this->unCachedHandlers; } } + +class_alias(PersistenceLogger::class, 'eZ\Publish\Core\Persistence\Cache\PersistenceLogger'); diff --git a/src/lib/Persistence/Cache/Readme.md b/src/lib/Persistence/Cache/Readme.md new file mode 100644 index 0000000000..24a667f75f --- /dev/null +++ b/src/lib/Persistence/Cache/Readme.md @@ -0,0 +1,73 @@ + +# CORE DEV DOC: Persistence\Cache + +SPI Persistence Cache is a layer aiming to cache calls to Persistence (backend database) deemed costly under load. It +tries to balance this need up against the complexity and additional [system overhead](#guidelines-for-core-development) imposed by caching. + +## Design + +Handlers using `AbstractHandler` internally consist of one cache layer: + +- "Shared cache": A Symfony Cache based cache pool, supporting a range of cache adapters like filesystem, Redis, Memcached. + Note: Due to being shared by design, clusters need remote cache, thus multi lookups are strongly advised to reduce round trip latency. + +Handlers using `AbstractInMemoryHandler` / `AbstractInMemoryPersistenceHandler` in addition add a second cache layer in front of "Shared cache": + +- "InMemory cache": A burst cache in PHP aiming at covering the most heavily used parts of the Content model to reduce repeated lookups to remote cache system. + Note: It's not shared but per request/process. To keep risk of race condition negligible, it has own milliseconds ttl & item limits. + +There are abstract test classes for the respective abstract classes above, these are opinionated and enforce conventions to: +- Avoid too much logic in cache logic _(e.g. warm-up logic)_, which can be a source of bugs. +- Avoids having to write error-prone test cases for every method. + +_This ensures the cache layer is far less complex to maintain and evolve than what was the case in 1.x._ + + +### Tags + +List of content tags that can be somewhat safely "semi-officially" used to clear related entities in cache: +- `c-`: Cache tag which refers to Content/ContentInfo entity. +- `l-`: Cache tag which refers to Locations and/or their assigned Content/ContentInfo entities. +- `lp-`: Like the tag above but applied to all Content/Locations in the subtree of this ID, so it can be used by tree operations. +- `cft-`: Cache tag which refers to entries containing Field data. It's used on content type changes that affect all Content items of the type. + +_For further tags used for other internal use cases, see the *Handlers for how they are used._ + + +## Guidelines for core development + +### Shared Cache: When to use, when not to use + +It's worth noting that shared cache comes at a cost: +- Increased complexity +- Latency per round trip +- Memory use + +Because of that, *typically* avoid introducing cache if: +- Lookup is per user => _it will consume a lot of memory and have very low hit ratio_ +- For drafts => _usually belongs to a single user and is short-lived_ +- Infrequently used lookups +- Lookups that are fast against DB even under load _(see also notes in "Possible future plans")_ + + +### Tags: When to use, when not to use + +Like cache, tags also comes at a cost: +- Slower invalidation +- Memory cost + - _E.g.: ATM on RedisTagAwareAdapter tag relation data is even non-expiring as it needs to guarantee surviving cache._ + +For those reasons, only introduce use a tag: +- Mainly to represent an entity _(e.g. `c-`)_ +- Only if it's represented on many different cache keys or if a key can have a lot of different variants. + - _Tip: Otherwise prefer to delete by cache key(s) when cache clear is needed, it will be faster and consume less memory._ + +### Possible future considerations + +Ideally and for best effect, the cache should be in place to cache results of complex calculations based on multiple backend +lookups. Caching lightweight SPI lookups sometimes might not provide benefits, while still consume Redis/Memcached memory. + +This is why it's been discussed to move some of the caching to API instead in a future major release. +However, that would require that permissions are split into their own Repo layer => In order to not end +up having to cache per user => Which would result in waste of memory and low cache hit ratio. +It would also need to deal with serialization of API value objects, which are more complex (lazy properties, xmldocument, ...). diff --git a/eZ/Publish/Core/Persistence/Cache/SectionHandler.php b/src/lib/Persistence/Cache/SectionHandler.php similarity index 95% rename from eZ/Publish/Core/Persistence/Cache/SectionHandler.php rename to src/lib/Persistence/Cache/SectionHandler.php index 714dc016eb..09755f9aaf 100644 --- a/eZ/Publish/Core/Persistence/Cache/SectionHandler.php +++ b/src/lib/Persistence/Cache/SectionHandler.php @@ -4,13 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\SPI\Persistence\Content\Section\Handler as SectionHandlerInterface; +use Ibexa\Contracts\Core\Persistence\Content\Section\Handler as SectionHandlerInterface; -/** - * @see \eZ\Publish\SPI\Persistence\Content\Section\Handler - */ class SectionHandler extends AbstractHandler implements SectionHandlerInterface { private const SECTION_IDENTIFIER = 'section'; @@ -168,3 +165,5 @@ public function countRoleAssignmentsUsingSection($sectionId) return $this->persistenceHandler->sectionHandler()->countRoleAssignmentsUsingSection($sectionId); } } + +class_alias(SectionHandler::class, 'eZ\Publish\Core\Persistence\Cache\SectionHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/SettingHandler.php b/src/lib/Persistence/Cache/SettingHandler.php similarity index 90% rename from eZ/Publish/Core/Persistence/Cache/SettingHandler.php rename to src/lib/Persistence/Cache/SettingHandler.php index 49be5cc8c9..ea295842f5 100644 --- a/eZ/Publish/Core/Persistence/Cache/SettingHandler.php +++ b/src/lib/Persistence/Cache/SettingHandler.php @@ -4,14 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\SPI\Persistence\Setting\Handler as SettingHandlerInterface; -use eZ\Publish\SPI\Persistence\Setting\Setting; +use Ibexa\Contracts\Core\Persistence\Setting\Handler as SettingHandlerInterface; +use Ibexa\Contracts\Core\Persistence\Setting\Setting; -/** - * @see \eZ\Publish\SPI\Persistence\Setting\Handler - */ final class SettingHandler extends AbstractInMemoryPersistenceHandler implements SettingHandlerInterface { private const SETTING_IDENTIFIER = 'setting'; @@ -80,3 +77,5 @@ private function getSettingTag(string $group, string $identifier): string ); } } + +class_alias(SettingHandler::class, 'eZ\Publish\Core\Persistence\Cache\SettingHandler'); diff --git a/src/lib/Persistence/Cache/TransactionHandler.php b/src/lib/Persistence/Cache/TransactionHandler.php new file mode 100644 index 0000000000..580bb4b610 --- /dev/null +++ b/src/lib/Persistence/Cache/TransactionHandler.php @@ -0,0 +1,50 @@ +cache->beginTransaction(); + + $this->logger->logCall(__METHOD__); + $this->persistenceHandler->transactionHandler()->beginTransaction(); + } + + /** + * {@inheritdoc} + */ + public function commit() + { + $this->logger->logCall(__METHOD__); + $this->persistenceHandler->transactionHandler()->commit(); + + $this->cache->commitTransaction(); + } + + /** + * {@inheritdoc} + */ + public function rollback() + { + $this->logger->logCall(__METHOD__); + $this->persistenceHandler->transactionHandler()->rollback(); + + $this->cache->rollbackTransaction(); + } +} + +class_alias(TransactionHandler::class, 'eZ\Publish\Core\Persistence\Cache\TransactionHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/TrashHandler.php b/src/lib/Persistence/Cache/TrashHandler.php similarity index 94% rename from eZ/Publish/Core/Persistence/Cache/TrashHandler.php rename to src/lib/Persistence/Cache/TrashHandler.php index 78e9e0eb0c..a8f5fdd39f 100644 --- a/eZ/Publish/Core/Persistence/Cache/TrashHandler.php +++ b/src/lib/Persistence/Cache/TrashHandler.php @@ -4,15 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\SPI\Persistence\Content\Location\Trash\Handler as TrashHandlerInterface; -use eZ\Publish\SPI\Persistence\Content\Relation; +use Ibexa\Contracts\Core\Persistence\Content\Location\Trash\Handler as TrashHandlerInterface; +use Ibexa\Contracts\Core\Persistence\Content\Relation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; -/** - * @see \eZ\Publish\SPI\Persistence\Content\Location\Trash\Handler - */ class TrashHandler extends AbstractHandler implements TrashHandlerInterface { private const EMPTY_TRASH_BULK_SIZE = 100; @@ -171,3 +168,5 @@ public function deleteTrashItem($trashedId) return $return; } } + +class_alias(TrashHandler::class, 'eZ\Publish\Core\Persistence\Cache\TrashHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/URLHandler.php b/src/lib/Persistence/Cache/URLHandler.php similarity index 88% rename from eZ/Publish/Core/Persistence/Cache/URLHandler.php rename to src/lib/Persistence/Cache/URLHandler.php index 77c98a74a9..517e0a7948 100644 --- a/eZ/Publish/Core/Persistence/Cache/URLHandler.php +++ b/src/lib/Persistence/Cache/URLHandler.php @@ -4,17 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\API\Repository\Values\URL\URLQuery; -use eZ\Publish\SPI\Persistence\URL\Handler as URLHandlerInterface; -use eZ\Publish\SPI\Persistence\URL\URLUpdateStruct; +use Ibexa\Contracts\Core\Persistence\URL\Handler as URLHandlerInterface; +use Ibexa\Contracts\Core\Persistence\URL\URLUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\URL\URLQuery; -/** - * SPI cache for URL Handler. - * - * @see \eZ\Publish\SPI\Persistence\URL\Handler - */ class URLHandler extends AbstractHandler implements URLHandlerInterface { private const URL_IDENTIFIER = 'url'; @@ -103,3 +98,5 @@ public function findUsages($id) return $this->persistenceHandler->urlHandler()->findUsages($id); } } + +class_alias(URLHandler::class, 'eZ\Publish\Core\Persistence\Cache\URLHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/UrlAliasHandler.php b/src/lib/Persistence/Cache/UrlAliasHandler.php similarity index 96% rename from eZ/Publish/Core/Persistence/Cache/UrlAliasHandler.php rename to src/lib/Persistence/Cache/UrlAliasHandler.php index 5555f0cfb3..0edfaeb3f6 100644 --- a/eZ/Publish/Core/Persistence/Cache/UrlAliasHandler.php +++ b/src/lib/Persistence/Cache/UrlAliasHandler.php @@ -4,16 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\SPI\Persistence\Content\UrlAlias; -use eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler as UrlAliasHandlerInterface; +use Ibexa\Contracts\Core\Persistence\Content\UrlAlias; +use Ibexa\Contracts\Core\Persistence\Content\UrlAlias\Handler as UrlAliasHandlerInterface; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as APINotFoundException; +use Ibexa\Core\Base\Exceptions\NotFoundException; -/** - * @see \eZ\Publish\SPI\Persistence\Content\UrlAlias\Handler - */ class UrlAliasHandler extends AbstractInMemoryPersistenceHandler implements UrlAliasHandlerInterface { private const URL_ALIAS_LOCATION_IDENTIFIER = 'url_alias_location'; @@ -445,7 +442,7 @@ public function archiveUrlAliasesForDeletedTranslations($locationId, $parentLoca * * For use when generating cache, not on invalidation. * - * @param \eZ\Publish\SPI\Persistence\Content\UrlAlias $urlAlias + * @param \Ibexa\Contracts\Core\Persistence\Content\UrlAlias $urlAlias * @param array $tags Optional, can be used to specify other tags. * * @return array @@ -492,7 +489,7 @@ public function deleteCorruptedUrlAliases() * * @param int $locationId * - * @throws \eZ\Publish\Core\Base\Exceptions\BadStateException + * @throws \Ibexa\Core\Base\Exceptions\BadStateException * @throws \Psr\Cache\InvalidArgumentException */ public function repairBrokenUrlAliasesForLocation(int $locationId) @@ -509,3 +506,5 @@ public function repairBrokenUrlAliasesForLocation(int $locationId) ); } } + +class_alias(UrlAliasHandler::class, 'eZ\Publish\Core\Persistence\Cache\UrlAliasHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/UrlWildcardHandler.php b/src/lib/Persistence/Cache/UrlWildcardHandler.php similarity index 84% rename from eZ/Publish/Core/Persistence/Cache/UrlWildcardHandler.php rename to src/lib/Persistence/Cache/UrlWildcardHandler.php index b0eed0b8d8..fb92a76ba7 100644 --- a/eZ/Publish/Core/Persistence/Cache/UrlWildcardHandler.php +++ b/src/lib/Persistence/Cache/UrlWildcardHandler.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler as UrlWildcardHandlerInterface; +use Ibexa\Contracts\Core\Persistence\Content\UrlWildcard; +use Ibexa\Contracts\Core\Persistence\Content\UrlWildcard\Handler as UrlWildcardHandlerInterface; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as APINotFoundException; use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\URLWildcardQuery; +use Ibexa\Core\Base\Exceptions\NotFoundException; class UrlWildcardHandler extends AbstractHandler implements UrlWildcardHandlerInterface { @@ -22,9 +22,6 @@ class UrlWildcardHandler extends AbstractHandler implements UrlWildcardHandlerIn private const URL_WILDCARD_NOT_FOUND_IDENTIFIER = 'url_wildcard_not_found'; private const URL_WILDCARD_SOURCE_IDENTIFIER = 'url_wildcard_source'; - /** - * @see \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler::create - */ public function create($sourceUrl, $destinationUrl, $forward = false) { $this->logger->logCall( @@ -83,9 +80,6 @@ public function update( return $urlWildcard; } - /** - * @see \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler::remove - */ public function remove($id) { $this->logger->logCall(__METHOD__, ['id' => $id]); @@ -97,9 +91,6 @@ public function remove($id) ]); } - /** - * @see \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler::load - */ public function load($id) { $cacheItem = $this->cache->getItem( @@ -123,9 +114,6 @@ public function load($id) return $urlWildcard; } - /** - * @see \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler::loadAll - */ public function loadAll($offset = 0, $limit = -1) { $this->logger->logCall(__METHOD__, ['offset' => $offset, 'limit' => $limit]); @@ -134,7 +122,7 @@ public function loadAll($offset = 0, $limit = -1) } /** - * @see \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler::find + * @see \Ibexa\Contracts\Core\Persistence\Content\UrlWildcard::find */ public function find(URLWildcardQuery $query): array { @@ -143,9 +131,6 @@ public function find(URLWildcardQuery $query): array return $this->persistenceHandler->urlWildcardHandler()->find($query); } - /** - * @see \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler::lookup - */ public function translate(string $sourceUrl): UrlWildcard { $cacheItem = $this->cache->getItem( @@ -187,9 +172,6 @@ public function translate(string $sourceUrl): UrlWildcard return $urlWildcard; } - /** - * @see \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler::exactSourceUrlExists() - */ public function exactSourceUrlExists(string $sourceUrl): bool { $this->logger->logCall(__METHOD__, ['source' => $sourceUrl]); @@ -197,9 +179,6 @@ public function exactSourceUrlExists(string $sourceUrl): bool return $this->persistenceHandler->urlWildcardHandler()->exactSourceUrlExists($sourceUrl); } - /** - * @see \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler::countAll() - */ public function countAll(): int { $this->logger->logCall(__METHOD__); @@ -207,3 +186,5 @@ public function countAll(): int return $this->persistenceHandler->urlWildcardHandler()->countAll(); } } + +class_alias(UrlWildcardHandler::class, 'eZ\Publish\Core\Persistence\Cache\UrlWildcardHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/UserHandler.php b/src/lib/Persistence/Cache/UserHandler.php similarity index 97% rename from eZ/Publish/Core/Persistence/Cache/UserHandler.php rename to src/lib/Persistence/Cache/UserHandler.php index 8af1005f73..0a5999dffc 100644 --- a/eZ/Publish/Core/Persistence/Cache/UserHandler.php +++ b/src/lib/Persistence/Cache/UserHandler.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache; - -use eZ\Publish\SPI\Persistence\User; -use eZ\Publish\SPI\Persistence\User\Handler as UserHandlerInterface; -use eZ\Publish\SPI\Persistence\User\Policy; -use eZ\Publish\SPI\Persistence\User\Role; -use eZ\Publish\SPI\Persistence\User\RoleAssignment; -use eZ\Publish\SPI\Persistence\User\RoleCopyStruct; -use eZ\Publish\SPI\Persistence\User\RoleCreateStruct; -use eZ\Publish\SPI\Persistence\User\RoleUpdateStruct; -use eZ\Publish\SPI\Persistence\User\UserTokenUpdateStruct; +namespace Ibexa\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence\User; +use Ibexa\Contracts\Core\Persistence\User\Handler as UserHandlerInterface; +use Ibexa\Contracts\Core\Persistence\User\Policy; +use Ibexa\Contracts\Core\Persistence\User\Role; +use Ibexa\Contracts\Core\Persistence\User\RoleAssignment; +use Ibexa\Contracts\Core\Persistence\User\RoleCopyStruct; +use Ibexa\Contracts\Core\Persistence\User\RoleCreateStruct; +use Ibexa\Contracts\Core\Persistence\User\RoleUpdateStruct; +use Ibexa\Contracts\Core\Persistence\User\UserTokenUpdateStruct; /** * Cache handler for user module. @@ -189,7 +189,7 @@ function () use ($login) { */ public function loadByEmail(string $email): User { - /** @var \eZ\Publish\SPI\Persistence\User $cachedValue */ + /** @var \Ibexa\Contracts\Core\Persistence\User $cachedValue */ $cachedValue = $this->getCacheValue( $this->cacheIdentifierSanitizer->escapeForCacheKey($email), $this->cacheIdentifierGenerator->generateKey(self::USER_IDENTIFIER, [], true) . '-', @@ -764,3 +764,5 @@ public function removeRoleAssignment($roleAssignmentId): void ]); } } + +class_alias(UserHandler::class, 'eZ\Publish\Core\Persistence\Cache\UserHandler'); diff --git a/eZ/Publish/Core/Persistence/Cache/UserPreferenceHandler.php b/src/lib/Persistence/Cache/UserPreferenceHandler.php similarity index 87% rename from eZ/Publish/Core/Persistence/Cache/UserPreferenceHandler.php rename to src/lib/Persistence/Cache/UserPreferenceHandler.php index f5b43960f1..2164563c75 100644 --- a/eZ/Publish/Core/Persistence/Cache/UserPreferenceHandler.php +++ b/src/lib/Persistence/Cache/UserPreferenceHandler.php @@ -6,19 +6,14 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache; +namespace Ibexa\Core\Persistence\Cache; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\SPI\Persistence\UserPreference\Handler; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreference; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreferenceSetStruct; +use Ibexa\Contracts\Core\Persistence\UserPreference\Handler; +use Ibexa\Contracts\Core\Persistence\UserPreference\UserPreference; +use Ibexa\Contracts\Core\Persistence\UserPreference\UserPreferenceSetStruct; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as APINotFoundException; +use Ibexa\Core\Base\Exceptions\NotFoundException; -/** - * SPI cache for UserPreference Handler. - * - * @see \eZ\Publish\SPI\Persistence\UserPreference\Handler - */ class UserPreferenceHandler extends AbstractInMemoryPersistenceHandler implements Handler { private const USER_PREFERENCE_IDENTIFIER = 'user_preference'; @@ -117,3 +112,5 @@ public function loadUserPreferences(int $userId, int $offset, int $limit): array return $this->persistenceHandler->userPreferenceHandler()->loadUserPreferences($userId, $offset, $limit); } } + +class_alias(UserPreferenceHandler::class, 'eZ\Publish\Core\Persistence\Cache\UserPreferenceHandler'); diff --git a/src/lib/Persistence/FieldType.php b/src/lib/Persistence/FieldType.php new file mode 100644 index 0000000000..259f6c6ebe --- /dev/null +++ b/src/lib/Persistence/FieldType.php @@ -0,0 +1,57 @@ +internalFieldType = $fieldType; + } + + /** + * Returns the empty value for the field type that can be processed by the storage engine. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\FieldValue + */ + public function getEmptyValue() + { + return $this->internalFieldType->toPersistenceValue( + $this->internalFieldType->getEmptyValue() + ); + } + + public function isEmptyValue(FieldValue $fieldValue): bool + { + return $this->internalFieldType->isEmptyValue( + $this->internalFieldType->fromPersistenceValue($fieldValue) + ); + } +} + +class_alias(FieldType::class, 'eZ\Publish\Core\Persistence\FieldType'); diff --git a/src/lib/Persistence/FieldTypeRegistry.php b/src/lib/Persistence/FieldTypeRegistry.php new file mode 100644 index 0000000000..fe013d35ce --- /dev/null +++ b/src/lib/Persistence/FieldTypeRegistry.php @@ -0,0 +1,83 @@ +coreFieldTypes = $coreFieldTypes; + $this->fieldTypes = $fieldTypes; + } + + /** + * Returns the FieldType object for given $identifier. + * + * @param string $identifier + * + * @return \Ibexa\Contracts\Core\Persistence\FieldType + * + * @throws \RuntimeException If field type for given $identifier is not instance or callable. + * @throws \Ibexa\Core\Base\Exceptions\NotFound\FieldTypeNotFoundException If field type for given $identifier is not found. + */ + public function getFieldType(string $identifier): FieldTypeInterface + { + if (!isset($this->fieldTypes[$identifier])) { + $this->fieldTypes[$identifier] = new FieldType($this->getCoreFieldType($identifier)); + } + + return $this->fieldTypes[$identifier]; + } + + public function register(string $identifier, SPIFieldType $fieldType): void + { + $this->coreFieldTypes[$identifier] = $fieldType; + } + + protected function getCoreFieldType(string $identifier): SPIFieldType + { + if (!isset($this->coreFieldTypes[$identifier])) { + throw new FieldTypeNotFoundException($identifier); + } + + return $this->coreFieldTypes[$identifier]; + } +} + +class_alias(FieldTypeRegistry::class, 'eZ\Publish\Core\Persistence\FieldTypeRegistry'); diff --git a/src/lib/Persistence/Legacy/Bookmark/Gateway.php b/src/lib/Persistence/Legacy/Bookmark/Gateway.php new file mode 100644 index 0000000000..5caaeda334 --- /dev/null +++ b/src/lib/Persistence/Legacy/Bookmark/Gateway.php @@ -0,0 +1,73 @@ +connection = $connection; + } + + /** + * {@inheritdoc} + */ + public function insertBookmark(Bookmark $bookmark): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::TABLE_BOOKMARKS) + ->values([ + self::COLUMN_NAME => ':name', + self::COLUMN_USER_ID => ':user_id', + self::COLUMN_LOCATION_ID => ':location_id', + ]) + ->setParameter(':name', $bookmark->name, PDO::PARAM_STR) + ->setParameter(':user_id', $bookmark->userId, PDO::PARAM_INT) + ->setParameter(':location_id', $bookmark->locationId, PDO::PARAM_INT); + + $query->execute(); + + return (int) $this->connection->lastInsertId(); + } + + /** + * {@inheritdoc} + */ + public function deleteBookmark(int $id): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::TABLE_BOOKMARKS) + ->where($query->expr()->eq(self::COLUMN_ID, ':id')) + ->setParameter(':id', $id, PDO::PARAM_INT); + + $query->execute(); + } + + /** + * {@inheritdoc} + */ + public function loadBookmarkDataById(int $id): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$this->getColumns()) + ->from(self::TABLE_BOOKMARKS) + ->where($query->expr()->eq(self::COLUMN_ID, ':id')) + ->setParameter(':id', $id, PDO::PARAM_INT); + + return $query->execute()->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * {@inheritdoc} + */ + public function loadBookmarkDataByUserIdAndLocationId(int $userId, array $locationIds): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$this->getColumns()) + ->from(self::TABLE_BOOKMARKS) + ->where($query->expr()->andX( + $query->expr()->eq(self::COLUMN_USER_ID, ':user_id'), + $query->expr()->in(self::COLUMN_LOCATION_ID, ':location_id') + )) + ->setParameter(':user_id', $userId, PDO::PARAM_INT) + ->setParameter(':location_id', $locationIds, Connection::PARAM_INT_ARRAY); + + return $query->execute()->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * {@inheritdoc} + */ + public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$this->getColumns()) + ->from(self::TABLE_BOOKMARKS) + ->where($query->expr()->eq(self::COLUMN_USER_ID, ':user_id')) + ->setFirstResult($offset); + + if ($limit > 0) { + $query->setMaxResults($limit); + } + + $query->orderBy(self::COLUMN_ID, 'DESC'); + $query->setParameter(':user_id', $userId, PDO::PARAM_INT); + + return $query->execute()->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * {@inheritdoc} + */ + public function countUserBookmarks(int $userId): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('COUNT(' . self::COLUMN_ID . ')') + ->from(self::TABLE_BOOKMARKS) + ->where($query->expr()->eq(self::COLUMN_USER_ID, ':user_id')) + ->setParameter(':user_id', $userId, PDO::PARAM_INT); + + return (int) $query->execute()->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function locationSwapped(int $location1Id, int $location2Id): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::TABLE_BOOKMARKS) + ->set(self::COLUMN_LOCATION_ID, '(CASE WHEN node_id = :source_id THEN :target_id ELSE :source_id END)') + ->where($query->expr()->orX( + $query->expr()->eq(self::COLUMN_LOCATION_ID, ':source_id'), + $query->expr()->eq(self::COLUMN_LOCATION_ID, ':target_id') + )); + + $stmt = $this->connection->prepare($query->getSQL()); + $stmt->bindValue('source_id', $location1Id, PDO::PARAM_INT); + $stmt->bindValue('target_id', $location2Id, PDO::PARAM_INT); + $stmt->execute(); + } + + private function getColumns(): array + { + return [ + self::COLUMN_ID, + self::COLUMN_NAME, + self::COLUMN_USER_ID, + self::COLUMN_LOCATION_ID, + ]; + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/Bookmark/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Bookmark/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..75cc30c728 --- /dev/null +++ b/src/lib/Persistence/Legacy/Bookmark/Gateway/ExceptionConversion.php @@ -0,0 +1,87 @@ +innerGateway = $innerGateway; + } + + public function insertBookmark(Bookmark $bookmark): int + { + try { + return $this->innerGateway->insertBookmark($bookmark); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteBookmark(int $id): void + { + try { + $this->innerGateway->deleteBookmark($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadBookmarkDataByUserIdAndLocationId(int $userId, array $locationId): array + { + try { + return $this->innerGateway->loadBookmarkDataByUserIdAndLocationId($userId, $locationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1): array + { + try { + return $this->innerGateway->loadUserBookmarks($userId, $offset, $limit); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countUserBookmarks(int $userId): int + { + try { + return $this->innerGateway->countUserBookmarks($userId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function locationSwapped(int $location1Id, int $location2Id): void + { + try { + $this->innerGateway->locationSwapped($location1Id, $location2Id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\Bookmark\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/Bookmark/Handler.php b/src/lib/Persistence/Legacy/Bookmark/Handler.php new file mode 100644 index 0000000000..2772388f00 --- /dev/null +++ b/src/lib/Persistence/Legacy/Bookmark/Handler.php @@ -0,0 +1,103 @@ +gateway = $gateway; + $this->mapper = $mapper; + } + + /** + * {@inheritdoc} + */ + public function create(CreateStruct $createStruct): Bookmark + { + $bookmark = $this->mapper->createBookmarkFromCreateStruct( + $createStruct + ); + $bookmark->id = $this->gateway->insertBookmark($bookmark); + + return $bookmark; + } + + /** + * {@inheritdoc} + */ + public function delete(int $bookmarkId): void + { + $this->gateway->deleteBookmark($bookmarkId); + } + + /** + * {@inheritdoc} + */ + public function loadByUserIdAndLocationId(int $userId, array $locationIds): array + { + $bookmarks = $this->mapper->extractBookmarksFromRows( + $this->gateway->loadBookmarkDataByUserIdAndLocationId($userId, $locationIds) + ); + + $list = []; + foreach ($bookmarks as $bookmark) { + $list[$bookmark->locationId] = $bookmark; + } + + return $list; + } + + /** + * {@inheritdoc} + */ + public function loadUserBookmarks(int $userId, int $offset = 0, int $limit = -1): array + { + return $this->mapper->extractBookmarksFromRows( + $this->gateway->loadUserBookmarks($userId, $offset, $limit) + ); + } + + /** + * {@inheritdoc} + */ + public function countUserBookmarks(int $userId): int + { + return $this->gateway->countUserBookmarks($userId); + } + + /** + * {@inheritdoc} + */ + public function locationSwapped(int $location1Id, int $location2Id): void + { + $this->gateway->locationSwapped($location1Id, $location2Id); + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\Bookmark\Handler'); diff --git a/src/lib/Persistence/Legacy/Bookmark/Mapper.php b/src/lib/Persistence/Legacy/Bookmark/Mapper.php new file mode 100644 index 0000000000..774a11e088 --- /dev/null +++ b/src/lib/Persistence/Legacy/Bookmark/Mapper.php @@ -0,0 +1,72 @@ +name = $createStruct->name; + $bookmark->locationId = $createStruct->locationId; + $bookmark->userId = $createStruct->userId; + + return $bookmark; + } + + /** + * Extracts Bookmark objects from $rows. + * + * @param array $rows + * + * @return \Ibexa\Contracts\Core\Persistence\Bookmark\Bookmark[] + */ + public function extractBookmarksFromRows(array $rows): array + { + $bookmarks = []; + foreach ($rows as $row) { + $bookmarks[] = $this->extractBookmarkFromRow($row); + } + + return $bookmarks; + } + + /** + * Extract Bookmark object from $row. + * + * @param array $row + * + * @return \Ibexa\Contracts\Core\Persistence\Bookmark\Bookmark + */ + private function extractBookmarkFromRow(array $row): Bookmark + { + $bookmark = new Bookmark(); + $bookmark->id = (int)$row['id']; + $bookmark->name = $row['name']; + $bookmark->userId = (int)$row['user_id']; + $bookmark->locationId = (int)$row['node_id']; + + return $bookmark; + } +} + +class_alias(Mapper::class, 'eZ\Publish\Core\Persistence\Legacy\Bookmark\Mapper'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldHandler.php b/src/lib/Persistence/Legacy/Content/FieldHandler.php similarity index 84% rename from eZ/Publish/Core/Persistence/Legacy/Content/FieldHandler.php rename to src/lib/Persistence/Legacy/Content/FieldHandler.php index 5bb2dc0460..816df404a0 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldHandler.php +++ b/src/lib/Persistence/Legacy/Content/FieldHandler.php @@ -4,16 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content; +namespace Ibexa\Core\Persistence\Legacy\Content; -use eZ\Publish\Core\Persistence\FieldTypeRegistry; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\UpdateStruct; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler; +use Ibexa\Contracts\Core\Persistence\Content\Type; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\UpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Core\Persistence\FieldTypeRegistry; /** * Field Handler. @@ -23,31 +23,31 @@ class FieldHandler /** * Content Gateway. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway + * @var \Ibexa\Core\Persistence\Legacy\Content\Gateway */ protected $contentGateway; - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler */ + /** @var \Ibexa\Core\Persistence\Legacy\Content\Language\Handler */ protected $languageHandler; /** * Content Mapper. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Mapper + * @var \Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected $mapper; /** * Storage Handler. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler + * @var \Ibexa\Core\Persistence\Legacy\Content\StorageHandler */ protected $storageHandler; /** * FieldType registry. * - * @var \eZ\Publish\Core\Persistence\FieldTypeRegistry + * @var \Ibexa\Core\Persistence\FieldTypeRegistry */ protected $fieldTypeRegistry; @@ -61,11 +61,11 @@ class FieldHandler /** * Creates a new Field Handler. * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Gateway $contentGateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Mapper $mapper - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler $storageHandler - * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $languageHandler - * @param \eZ\Publish\Core\Persistence\FieldTypeRegistry $fieldTypeRegistry + * @param \Ibexa\Core\Persistence\Legacy\Content\Gateway $contentGateway + * @param \Ibexa\Core\Persistence\Legacy\Content\Mapper $mapper + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageHandler $storageHandler + * @param \Ibexa\Contracts\Core\Persistence\Content\Language\Handler $languageHandler + * @param \Ibexa\Core\Persistence\FieldTypeRegistry $fieldTypeRegistry */ public function __construct( Gateway $contentGateway, @@ -84,8 +84,8 @@ public function __construct( /** * Creates new fields in the database from $content of $contentType. * - * @param \eZ\Publish\SPI\Persistence\Content $content - * @param \eZ\Publish\SPI\Persistence\Content\Type $contentType + * @param \Ibexa\Contracts\Core\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content\Type $contentType */ public function createNewFields(Content $content, Type $contentType) { @@ -124,10 +124,10 @@ public function createNewFields(Content $content, Type $contentType) * * Uses FieldType to create empty field value. * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDefinition + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDefinition * @param string $languageCode * - * @return \eZ\Publish\SPI\Persistence\Content\Field + * @return \Ibexa\Contracts\Core\Persistence\Content\Field */ protected function getEmptyField(FieldDefinition $fieldDefinition, $languageCode) { @@ -146,7 +146,7 @@ protected function getEmptyField(FieldDefinition $fieldDefinition, $languageCode /** * Creates existing fields in a new version for $content. * - * @param \eZ\Publish\SPI\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content $content */ public function createExistingFieldsInNewVersion(Content $content): void { @@ -164,8 +164,8 @@ public function createExistingFieldsInNewVersion(Content $content): void * * Used by self::createNewFields() and self::updateFields() * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param \Ibexa\Contracts\Core\Persistence\Content $content */ protected function createNewField(Field $field, Content $content) { @@ -190,7 +190,7 @@ protected function createNewField(Field $field, Content $content) /** * @param array $fields - * @param \eZ\Publish\SPI\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content $content */ protected function copyFields(array $fields, Content $content) { @@ -206,9 +206,9 @@ protected function copyFields(array $fields, Content $content) * * Used by self::createNewFields() and self::updateFields() * - * @param \eZ\Publish\SPI\Persistence\Content\Field $originalField + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $originalField * @param string $languageCode - * @param \eZ\Publish\SPI\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content $content */ protected function copyField(Field $originalField, $languageCode, Content $content) { @@ -240,8 +240,8 @@ protected function copyField(Field $originalField, $languageCode, Content $conte * * Used by self::createNewFields() and self::updateFields() * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param \Ibexa\Contracts\Core\Persistence\Content $content */ protected function updateField(Field $field, Content $content) { @@ -271,8 +271,8 @@ protected function updateField(Field $field, Content $content) * By default copying falls back to storing, it is upon external storage implementation to override * the behaviour as needed. * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param \Ibexa\Contracts\Core\Persistence\Content $content */ protected function createExistingFieldInNewVersion(Field $field, Content $content) { @@ -299,7 +299,7 @@ protected function createExistingFieldInNewVersion(Field $field, Content $conten /** * Performs external loads for the fields in $content. * - * @param \eZ\Publish\SPI\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content $content */ public function loadExternalFieldData(Content $content) { @@ -311,9 +311,9 @@ public function loadExternalFieldData(Content $content) /** * Updates the fields in for content identified by $contentId and $versionNo in the database in respect to $updateStruct. * - * @param \eZ\Publish\SPI\Persistence\Content $content - * @param \eZ\Publish\SPI\Persistence\Content\UpdateStruct $updateStruct - * @param \eZ\Publish\SPI\Persistence\Content\Type $contentType + * @param \Ibexa\Contracts\Core\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content\UpdateStruct $updateStruct + * @param \Ibexa\Contracts\Core\Persistence\Content\Type $contentType */ public function updateFields(Content $content, UpdateStruct $updateStruct, Type $contentType) { @@ -392,10 +392,10 @@ public function updateFields(Content $content, UpdateStruct $updateStruct, Type * By default copying falls back to storing, it is upon external storage implementation to override * the behaviour as needed. * - * @param \eZ\Publish\SPI\Persistence\Content\Field $field - * @param \eZ\Publish\SPI\Persistence\Content\Field $updateField - * @param \eZ\Publish\SPI\Persistence\Content\Field $originalField - * @param \eZ\Publish\SPI\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $updateField + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $originalField + * @param \Ibexa\Contracts\Core\Persistence\Content $content */ protected function updateCopiedField(Field $field, Field $updateField, Field $originalField, Content $content) { @@ -421,10 +421,10 @@ protected function updateCopiedField(Field $field, Field $updateField, Field $or /** * Returns given $fields structured in hash array with field definition ids and language codes as keys. * - * @param \eZ\Publish\SPI\Persistence\Content\Field[] $fields + * @param \Ibexa\Contracts\Core\Persistence\Content\Field[] $fields * @param array $languageCodes * - * @return array> + * @return array */ protected function getFieldMap(array $fields, &$languageCodes = null) { @@ -443,7 +443,7 @@ protected function getFieldMap(array $fields, &$languageCodes = null) * Deletes the fields for $contentId in $versionInfo from the database. * * @param int $contentId - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo */ public function deleteFields($contentId, VersionInfo $versionInfo) { @@ -457,7 +457,7 @@ public function deleteFields($contentId, VersionInfo $versionInfo) * Deletes translated fields and their external storage data for the given Content Versions. * * @param int $contentId - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo[] $versions + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo[] $versions * @param string $languageCode */ public function deleteTranslationFromContentFields($contentId, array $versions, $languageCode) @@ -481,7 +481,7 @@ public function deleteTranslationFromContentFields($contentId, array $versions, /** * Deletes translated fields and their external storage data for the given $versionInfo. * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo * @param string $languageCode */ public function deleteTranslationFromVersionFields(VersionInfo $versionInfo, $languageCode) @@ -501,3 +501,5 @@ public function deleteTranslationFromVersionFields(VersionInfo $versionInfo, $la ); } } + +class_alias(FieldHandler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter.php new file mode 100644 index 0000000000..95601cafe9 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter.php @@ -0,0 +1,69 @@ +createContentDraft(). + * Rather allow invalid value or omit it to let validation layer in FieldType handle issues when user tried + * to publish the given draft. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $value + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue + */ + public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue); + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue); + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef); + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef); + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * If the indexing is not supported, this method must return false. + * + * @return string|false + */ + public function getIndexColumn(); +} + +class_alias(Converter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php new file mode 100644 index 0000000000..379d821ce7 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorConverter.php @@ -0,0 +1,159 @@ +dataText = $this->generateXmlString($value->data); + $storageFieldValue->sortKeyString = $value->sortKey; + } + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + $fieldValue->data = $this->restoreValueFromXmlString($value->dataText); + $fieldValue->sortKey = $value->sortKeyString; + } + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + $fieldSettings = $fieldDef->fieldTypeConstraints->fieldSettings; + + if ($fieldSettings !== null) { + $storageDef->dataInt1 = (int)$fieldSettings['defaultAuthor']; + } + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'defaultAuthor' => $storageDef->dataInt1 ?? AuthorType::DEFAULT_VALUE_EMPTY, + ] + ); + + $fieldDef->defaultValue->data = []; + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + return 'sort_key_string'; + } + + /** + * Generates XML string from $authorValue to be stored in storage engine. + * + * @param array $authorValue + * + * @return string The generated XML string + */ + private function generateXmlString(array $authorValue) + { + $doc = new DOMDocument('1.0', 'utf-8'); + + $root = $doc->createElement('ezauthor'); + $doc->appendChild($root); + + $authors = $doc->createElement('authors'); + $root->appendChild($authors); + + foreach ($authorValue as $author) { + $authorNode = $doc->createElement('author'); + $authorNode->setAttribute('id', $author['id']); + $authorNode->setAttribute('name', $author['name']); + $authorNode->setAttribute('email', $author['email']); + $authors->appendChild($authorNode); + unset($authorNode); + } + + return $doc->saveXML(); + } + + /** + * Restores an author Value object from $xmlString. + * + * @param string $xmlString XML String stored in storage engine + * + * @return \Ibexa\Core\FieldType\Author\Value[] + */ + private function restoreValueFromXmlString($xmlString) + { + $dom = new DOMDocument('1.0', 'utf-8'); + $authors = []; + + if ($dom->loadXML($xmlString) === true) { + foreach ($dom->getElementsByTagName('author') as $author) { + $authors[] = [ + 'id' => $author->getAttribute('id'), + 'name' => $author->getAttribute('name'), + 'email' => $author->getAttribute('email'), + ]; + } + } + + return $authors; + } +} + +class_alias(AuthorConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/BinaryFileConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/BinaryFileConverter.php new file mode 100644 index 0000000000..5789e2bc8e --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/BinaryFileConverter.php @@ -0,0 +1,102 @@ +dataInt1 = (isset($fieldDef->fieldTypeConstraints->validators['FileSizeValidator']['maxFileSize']) + ? $fieldDef->fieldTypeConstraints->validators['FileSizeValidator']['maxFileSize'] + : 0); + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + $fieldDef->fieldTypeConstraints = new FieldTypeConstraints( + [ + 'validators' => [ + 'FileSizeValidator' => [ + 'maxFileSize' => ($storageDef->dataInt1 != 0 + ? $storageDef->dataInt1 + : null), + ], + ], + ] + ); + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + // @todo: Correct? + return 'sort_key_string'; + } +} + +class_alias(BinaryFileConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\BinaryFileConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxConverter.php new file mode 100644 index 0000000000..dd3486bbf4 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxConverter.php @@ -0,0 +1,92 @@ +dataInt = (int)$value->data; + $storageFieldValue->sortKeyInt = (int)$value->data; + } + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + $fieldValue->data = (bool)$value->dataInt; + $fieldValue->sortKey = $value->dataInt; + } + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + $storageDef->dataInt3 = (int)$fieldDef->defaultValue->data; + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + $fieldDef->defaultValue->data = !empty($storageDef->dataInt3) ? (bool)$storageDef->dataInt3 : false; + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + return 'sort_key_int'; + } +} + +class_alias(CheckboxConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php new file mode 100644 index 0000000000..670839dddb --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryConverter.php @@ -0,0 +1,110 @@ +dataText = empty($value->data) ? '' : implode(',', $value->data); + $storageFieldValue->sortKeyString = $value->sortKey; + } + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + $fieldValue->data = empty($value->dataText) ? null : explode(',', $value->dataText); + $fieldValue->sortKey = $value->sortKeyString; + } + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + if (isset($fieldDef->fieldTypeConstraints->fieldSettings['isMultiple'])) { + $storageDef->dataInt1 = (int)$fieldDef->fieldTypeConstraints->fieldSettings['isMultiple']; + } + + $storageDef->dataText5 = $fieldDef->defaultValue->data === null + ? '' + : implode(',', $fieldDef->defaultValue->data); + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'isMultiple' => !empty($storageDef->dataInt1) ? (bool)$storageDef->dataInt1 : false, + ] + ); + + $fieldDef->defaultValue->data = empty($storageDef->dataText5) + ? null + : explode(',', $storageDef->dataText5); + // TODO This will contain comma separated country codes, which is correct for value but not for sort key. + // Sort key should contain comma separated lowercased country names. + $fieldDef->defaultValue->sortKey = $storageDef->dataText5; + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + return 'sort_key_string'; + } +} + +class_alias(CountryConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php similarity index 82% rename from eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php rename to src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php index d8ab9d5214..42a2efc4f1 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeConverter.php @@ -4,18 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; +namespace Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; use DateInterval; use DateTime; use DOMDocument; -use eZ\Publish\Core\FieldType\DateAndTime\Type as DateAndTimeType; -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Core\FieldType\DateAndTime\Type as DateAndTimeType; +use Ibexa\Core\FieldType\FieldSettings; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; use SimpleXMLElement; class DateAndTimeConverter implements Converter @@ -27,7 +27,7 @@ class DateAndTimeConverter implements Converter * * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter + * @return \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter */ public static function create() { @@ -37,8 +37,8 @@ public static function create() /** * Converts data from $value to $storageFieldValue. * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $value + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue */ public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) { @@ -51,8 +51,8 @@ public function toStorageValue(FieldValue $value, StorageFieldValue $storageFiel /** * Converts data from $value to $fieldValue. * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue */ public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) { @@ -70,8 +70,8 @@ public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) /** * Converts field definition data in $fieldDef into $storageFieldDef. * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef */ public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) { @@ -91,8 +91,8 @@ public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageField /** * Converts field definition data in $storageDef into $fieldDef. * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef */ public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) { @@ -225,3 +225,5 @@ protected function getDateIntervalFromXML($xmlText) return DateInterval::createFromDateString(implode(', ', $aIntervalString)); } } + +class_alias(DateAndTimeConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateConverter.php new file mode 100644 index 0000000000..c311520a42 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/DateConverter.php @@ -0,0 +1,123 @@ +dataInt = ($value->data !== null ? $value->data['timestamp'] : null); + $storageFieldValue->sortKeyInt = (int)$value->sortKey; + } + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + if ($value->dataInt === null || $value->dataInt == 0) { + return; + } + + $fieldValue->data = [ + 'timestamp' => $value->dataInt, + 'rfc850' => null, + ]; + $fieldValue->sortKey = $value->sortKeyInt; + } + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + $storageDef->dataInt1 = $fieldDef->fieldTypeConstraints->fieldSettings['defaultType'] ?? null; + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'defaultType' => $storageDef->dataInt1, + ] + ); + + // Building default value + switch ($fieldDef->fieldTypeConstraints->fieldSettings['defaultType']) { + case DateType::DEFAULT_CURRENT_DATE: + $data = [ + 'timestamp' => time(), // @deprecated timestamp is no longer used and will be removed in a future version. + 'rfc850' => null, + 'timestring' => 'now', + ]; + break; + default: + $data = null; + } + + $fieldDef->defaultValue->data = $data; + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + return 'sort_key_int'; + } +} + +class_alias(DateConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php new file mode 100644 index 0000000000..b1bd4aae4b --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/EmailAddressConverter.php @@ -0,0 +1,97 @@ +dataText = $value->data; + $storageFieldValue->sortKeyString = $value->sortKey; + } + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + $fieldValue->data = $value->dataText; + $fieldValue->sortKey = $value->sortKeyString; + } + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + $storageDef->dataText1 = $fieldDef->defaultValue->data; + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + $validatorConstraints = [self::VALIDATOR_IDENTIFIER => []]; + $fieldDef->fieldTypeConstraints->validators = $validatorConstraints; + $fieldDef->defaultValue->data = isset($storageDef->dataText1) ? $storageDef->dataText1 : ''; + $fieldDef->defaultValue->sortKey = $fieldDef->defaultValue->data; + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + return 'sort_key_string'; + } +} + +class_alias(EmailAddressConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\EmailAddressConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/Exception/NotFound.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/Exception/NotFound.php new file mode 100644 index 0000000000..d11218747c --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/Exception/NotFound.php @@ -0,0 +1,30 @@ +dataText = $value->data; + $storageFieldValue->sortKeyString = $value->sortKey; + } + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + $fieldValue->data = $value->dataText; + $fieldValue->sortKey = $value->sortKeyString; + } + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + if (isset($fieldDef->fieldTypeConstraints->fieldSettings['isISBN13'])) { + $storageDef->dataInt1 = $fieldDef->fieldTypeConstraints->fieldSettings['isISBN13']; + } else { + $storageDef->dataInt1 = 1; + } + + $storageDef->dataText1 = $fieldDef->defaultValue->data; + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'isISBN13' => !empty($storageDef->dataInt1) ? (bool)$storageDef->dataInt1 : false, + ] + ); + + $fieldDef->defaultValue->data = $storageDef->dataText1 ?: null; + $fieldDef->defaultValue->sortKey = $storageDef->dataText1 ?: ''; + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + return 'sort_key_string'; + } +} + +class_alias(ISBNConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ISBNConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageAssetConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageAssetConverter.php new file mode 100644 index 0000000000..c7ef6bb3a4 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageAssetConverter.php @@ -0,0 +1,86 @@ +dataInt = !empty($value->data['destinationContentId']) + ? $value->data['destinationContentId'] + : null; + $storageFieldValue->dataText = !empty($value->data['alternativeText']) + ? $value->data['alternativeText'] + : null; + $storageFieldValue->sortKeyInt = (int)$value->sortKey; + } + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + $fieldValue->data = [ + 'destinationContentId' => $value->dataInt ?: null, + 'alternativeText' => $value->dataText ?: null, + ]; + $fieldValue->sortKey = (int)$value->sortKeyInt; + } + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + return 'sort_key_string'; + } +} + +class_alias(ImageAssetConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageAssetConverter'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php similarity index 81% rename from eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php rename to src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php index f9fce21f4d..3b30bd924a 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/ImageConverter.php @@ -4,23 +4,23 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; - -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\UrlRedecoratorInterface; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; +namespace Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\UrlRedecoratorInterface; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; use SimpleXMLElement; class ImageConverter extends BinaryFileConverter { - /** @var \eZ\Publish\Core\IO\IOServiceInterface */ + /** @var \Ibexa\Core\IO\IOServiceInterface */ private $imageIoService; - /** @var \eZ\Publish\Core\IO\UrlRedecoratorInterface */ + /** @var \Ibexa\Core\IO\UrlRedecoratorInterface */ private $urlRedecorator; public function __construct(IOServiceInterface $imageIoService, UrlRedecoratorInterface $urlRedecorator) @@ -32,8 +32,8 @@ public function __construct(IOServiceInterface $imageIoService, UrlRedecoratorIn /** * Converts data from $value to $storageFieldValue. * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $value + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue */ public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) { @@ -137,7 +137,7 @@ protected function fillXml($imageData, $pathInfo, $timestamp) htmlspecialchars($imageData['mime']), // mime_type htmlspecialchars($imageData['width']), // width htmlspecialchars($imageData['height']), // height - htmlspecialchars($imageData['alternativeText']), // alternative_text + htmlspecialchars($imageData['alternativeText'] ?? ''), // alternative_text htmlspecialchars(1293033771), // alias_key, fixed for the original image htmlspecialchars($timestamp), // timestamp // @@ -167,8 +167,8 @@ private function buildAdditionalDataTag(array $imageEditorData): string /** * Converts data from $value to $fieldValue. * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue */ public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) { @@ -183,8 +183,13 @@ public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageField { $validators = $fieldDef->fieldTypeConstraints->validators; - $storageDef->dataInt1 = $validators['FileSizeValidator']['maxFileSize'] ?? 0; + $storageDef->dataFloat1 = $validators['FileSizeValidator']['maxFileSize'] ?? 0.0; $storageDef->dataInt2 = (int)($validators['AlternativeTextValidator']['required'] ?? 0); + $storageDef->dataText1 = 'MB'; + + $fieldSettings = $fieldDef->fieldTypeConstraints->fieldSettings; + + $storageDef->dataText5 = json_encode($fieldSettings['mimeTypes'] ?? [], JSON_THROW_ON_ERROR); } public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef): void @@ -193,16 +198,33 @@ public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefin [ 'validators' => [ 'FileSizeValidator' => [ - 'maxFileSize' => $storageDef->dataInt1 !== 0 ? $storageDef->dataInt1 : null, + 'maxFileSize' => $storageDef->dataFloat1 !== 0.0 ? $storageDef->dataFloat1 : null, ], 'AlternativeTextValidator' => [ 'required' => (bool)$storageDef->dataInt2, ], ], + 'fieldSettings' => $this->getFieldSettings($storageDef), ] ); } + /** + * @return array + * + * @throws \JsonException + */ + private function getFieldSettings(StorageFieldDefinition $storageFieldDefinition): array + { + $mimeTypes = $storageFieldDefinition->dataText5; + + return [ + 'mimeTypes' => !empty($mimeTypes) + ? json_decode($mimeTypes, true, 512, JSON_THROW_ON_ERROR) + : [], + ]; + } + /** * Parses the XML from the legacy database. * @@ -262,3 +284,5 @@ protected function parseLegacyXml($xml) return $extractedData; } } + +class_alias(ImageConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageConverter'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/IntegerConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/IntegerConverter.php similarity index 80% rename from eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/IntegerConverter.php rename to src/lib/Persistence/Legacy/Content/FieldValue/Converter/IntegerConverter.php index 07cf17e9d1..63e494280e 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/IntegerConverter.php +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/IntegerConverter.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; +namespace Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; class IntegerConverter implements Converter { @@ -36,8 +36,8 @@ public static function create() /** * Converts data from $value to $storageFieldValue. * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $value + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue */ public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) { @@ -48,8 +48,8 @@ public function toStorageValue(FieldValue $value, StorageFieldValue $storageFiel /** * Converts data from $value to $fieldValue. * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue */ public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) { @@ -60,8 +60,8 @@ public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) /** * Converts field definition data in $fieldDef into $storageFieldDef. * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef */ public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) { @@ -89,8 +89,8 @@ public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageField /** * Converts field definition data in $storageDef into $fieldDef. * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef */ public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) { @@ -147,3 +147,5 @@ private function getStorageDefValidatorState($minValue, $maxValue) return $state; } } + +class_alias(IntegerConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\IntegerConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordConverter.php new file mode 100644 index 0000000000..8832a09702 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordConverter.php @@ -0,0 +1,89 @@ +sortKeyString = $value->sortKey; + } + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + $fieldValue->data = []; + $fieldValue->sortKey = $value->sortKeyString; + } + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + return 'sort_key_string'; + } +} + +class_alias(KeywordConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/MapLocationConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/MapLocationConverter.php new file mode 100644 index 0000000000..ec78361fe1 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/MapLocationConverter.php @@ -0,0 +1,89 @@ +dataInt = isset($value->externalData['address']) ? 1 : 0; + $storageFieldValue->dataText = ''; + $storageFieldValue->sortKeyString = (string)$value->sortKey; + } + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + } + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + return 'sort_key_string'; + } +} + +class_alias(MapLocationConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\MapLocationConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/MediaConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/MediaConverter.php new file mode 100644 index 0000000000..9f64106d7d --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/MediaConverter.php @@ -0,0 +1,76 @@ +dataText1 = (isset($fieldDef->fieldTypeConstraints->fieldSettings['mediaType']) + ? $fieldDef->fieldTypeConstraints->fieldSettings['mediaType'] + : MediaType::TYPE_HTML5_VIDEO); + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + parent::toFieldDefinition($storageDef, $fieldDef); + $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'mediaType' => $storageDef->dataText1, + ] + ); + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + return false; + } +} + +class_alias(MediaConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\MediaConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/NullConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/NullConverter.php new file mode 100644 index 0000000000..909ef9defa --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/NullConverter.php @@ -0,0 +1,97 @@ +setAttribute('value', $selectionMethod); $root->appendChild($selectionType); + $rootDefaultLocation = $doc->createElement('root_default_location'); + $rootDefaultLocation->setAttribute('value', (bool)($fieldSettings['rootDefaultLocation'] ?? false)); + $root->appendChild($rootDefaultLocation); + $defaultLocation = $doc->createElement('contentobject-placement'); if (!empty($fieldSettings['selectionRoot'])) { $defaultLocation->setAttribute('node-id', (int)$fieldSettings['selectionRoot']); @@ -124,8 +128,8 @@ public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageField * * * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef */ public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) { @@ -162,6 +166,13 @@ public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefin $fieldSettings['selectionRoot'] = (int)$defaultLocation->getAttribute('node-id'); } + if ( + ($rootDefaultLocation = $dom->getElementsByTagName('root_default_location')->item(0)) && + $rootDefaultLocation->hasAttribute('value') + ) { + $fieldSettings['rootDefaultLocation'] = (bool)$rootDefaultLocation->getAttribute('value'); + } + if (!($constraints = $dom->getElementsByTagName('constraints'))) { return; } @@ -185,3 +196,5 @@ public function getIndexColumn() return 'sort_key_int'; } } + +class_alias(RelationConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationConverter'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php similarity index 91% rename from eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php rename to src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php index 50ea355d8e..172d157953 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListConverter.php @@ -4,21 +4,21 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; +namespace Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; use Doctrine\DBAL\Connection; use Doctrine\DBAL\FetchMode; use Doctrine\DBAL\ParameterType; use DOMDocument; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway as ContentTypeGateway; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type as ContentType; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type as ContentType; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; +use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use Ibexa\Core\Persistence\Legacy\Content\Type\Gateway as ContentTypeGateway; class RelationListConverter implements Converter { @@ -143,6 +143,10 @@ public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageField } $root->appendChild($defaultLocation); + $rootDefaultLocation = $doc->createElement('root_default_location'); + $rootDefaultLocation->setAttribute('value', (bool)($fieldSettings['rootDefaultLocation'] ?? false)); + $root->appendChild($rootDefaultLocation); + $selectionLimit = $doc->createElement('selection_limit'); if (isset($validators['RelationListValueValidator']['selectionLimit'])) { $selectionLimit->setAttribute('value', (int)$validators['RelationListValueValidator']['selectionLimit']); @@ -218,6 +222,13 @@ public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefin $fieldSettings['selectionMethod'] = (int)$selectionType->getAttribute('value'); } + if ( + ($rootDefaultLocation = $dom->getElementsByTagName('root_default_location')->item(0)) && + $rootDefaultLocation->hasAttribute('value') + ) { + $fieldSettings['rootDefaultLocation'] = (bool)$rootDefaultLocation->getAttribute('value'); + } + if ( ($defaultLocation = $dom->getElementsByTagName('contentobject-placement')->item(0)) && $defaultLocation->hasAttribute('node-id') @@ -397,3 +408,5 @@ static function (array $row) use ($id): bool { return $groupedData; } } + +class_alias(RelationListConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationListConverter'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php similarity index 76% rename from eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php rename to src/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php index 7450d731d5..f2f14815f1 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionConverter.php @@ -4,31 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; +namespace Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; use DOMDocument; -use eZ\Publish\API\Repository\LanguageService; -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Core\FieldType\FieldSettings; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; class SelectionConverter implements Converter { - /** @var \eZ\Publish\API\Repository\LanguageService */ - private $languageService; - - /** - * @param \eZ\Publish\API\Repository\LanguageService $languageService - */ - public function __construct( - LanguageService $languageService - ) { - $this->languageService = $languageService; - } - /** * Factory for current class. * @@ -36,7 +23,7 @@ public function __construct( * * @deprecated since 6.8, will be removed in 7.x, use default constructor instead. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter + * @return \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter */ public static function create() { @@ -46,8 +33,8 @@ public static function create() /** * Converts data from $value to $storageFieldValue. * - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $value - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $value + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $storageFieldValue */ public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue) { @@ -57,8 +44,8 @@ public function toStorageValue(FieldValue $value, StorageFieldValue $storageFiel /** * Converts data from $value to $fieldValue. * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue $value - * @param \eZ\Publish\SPI\Persistence\Content\FieldValue $fieldValue + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue */ public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) { @@ -76,8 +63,8 @@ public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) /** * Converts field definition data in $fieldDef into $storageFieldDef. * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef */ public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) { @@ -110,8 +97,8 @@ public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageField /** * Converts field definition data in $storageDef into $fieldDef. * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef */ public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) { @@ -193,3 +180,5 @@ private function buildOptionsXml(array $selectionOptions): DOMDocument return $xml; } } + +class_alias(SelectionConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverter.php new file mode 100644 index 0000000000..cd79d6824d --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverter.php @@ -0,0 +1,77 @@ +serializer = $serializer; + } + + public function toStorageValue(FieldValue $value, StorageFieldValue $storageFieldValue): void + { + $data = $value->data; + if ($data !== null) { + $data = $this->serializer->encode($data); + } + + $storageFieldValue->dataText = $data; + $storageFieldValue->sortKeyString = (string)$value->sortKey; + } + + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue): void + { + $data = $value->dataText; + if ($data !== null) { + $data = $this->serializer->decode($data); + } + + $fieldValue->data = $data; + $fieldValue->sortKey = $value->sortKeyString; + } + + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef): void + { + $settings = $fieldDef->fieldTypeConstraints->fieldSettings; + if ($settings !== null) { + $settings = $this->serializer->encode((array)$settings); + } + + $storageDef->dataText5 = $settings; + } + + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef): void + { + $settings = $storageDef->dataText5; + if ($settings !== null) { + $settings = new FieldSettings($this->serializer->decode($settings)); + } + + $fieldDef->fieldTypeConstraints->fieldSettings = $settings; + } + + public function getIndexColumn(): string + { + return 'sort_key_string'; + } +} + +class_alias(SerializableConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SerializableConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockConverter.php new file mode 100644 index 0000000000..59460d3e03 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockConverter.php @@ -0,0 +1,101 @@ +dataText = $value->data; + $storageFieldValue->sortKeyString = $value->sortKey; + } + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + $fieldValue->data = $value->dataText; + $fieldValue->sortKey = $value->sortKeyString; + } + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + if (isset($fieldDef->fieldTypeConstraints->fieldSettings['textRows'])) { + $storageDef->dataInt1 = $fieldDef->fieldTypeConstraints->fieldSettings['textRows']; + } + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'textRows' => $storageDef->dataInt1, + ] + ); + $fieldDef->defaultValue->data = null; + $fieldDef->defaultValue->sortKey = ''; + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + return 'sort_key_string'; + } +} + +class_alias(TextBlockConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php new file mode 100644 index 0000000000..30e0c13726 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineConverter.php @@ -0,0 +1,123 @@ +dataText = $value->data; + $storageFieldValue->sortKeyString = $value->sortKey; + } + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + $fieldValue->data = $value->dataText; + $fieldValue->sortKey = $value->sortKeyString; + } + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + if (isset($fieldDef->fieldTypeConstraints->validators[self::STRING_LENGTH_VALIDATOR_IDENTIFIER]['maxStringLength'])) { + $storageDef->dataInt1 = $fieldDef->fieldTypeConstraints->validators[self::STRING_LENGTH_VALIDATOR_IDENTIFIER]['maxStringLength']; + } else { + $storageDef->dataInt1 = 0; + } + + if (isset($fieldDef->fieldTypeConstraints->validators[self::STRING_LENGTH_VALIDATOR_IDENTIFIER]['minStringLength'])) { + $storageDef->dataInt2 = $fieldDef->fieldTypeConstraints->validators[self::STRING_LENGTH_VALIDATOR_IDENTIFIER]['minStringLength']; + } else { + $storageDef->dataInt2 = 0; + } + + $storageDef->dataText1 = $fieldDef->defaultValue->data; + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + $validatorConstraints = []; + + if (isset($storageDef->dataInt1)) { + $validatorConstraints[self::STRING_LENGTH_VALIDATOR_IDENTIFIER]['maxStringLength'] = + $storageDef->dataInt1 != 0 ? + (int)$storageDef->dataInt1 : + null; + } + if (isset($storageDef->dataInt2)) { + $validatorConstraints[self::STRING_LENGTH_VALIDATOR_IDENTIFIER]['minStringLength'] = + $storageDef->dataInt2 != 0 ? + (int)$storageDef->dataInt2 : + null; + } + + $fieldDef->fieldTypeConstraints->validators = $validatorConstraints; + $fieldDef->defaultValue->data = $storageDef->dataText1 ?: null; + $fieldDef->defaultValue->sortKey = $storageDef->dataText1 ?: ''; + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + return 'sort_key_string'; + } +} + +class_alias(TextLineConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeConverter.php new file mode 100644 index 0000000000..df9b133fb8 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeConverter.php @@ -0,0 +1,124 @@ +dataInt = $value->data; + $storageFieldValue->sortKeyInt = (int)$value->sortKey; + } + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + if ($value->dataInt === null) { + return; + } + + $fieldValue->data = $value->dataInt; + $fieldValue->sortKey = $value->sortKeyInt; + } + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + $fieldSettings = $fieldDef->fieldTypeConstraints->fieldSettings; + + if ($fieldSettings !== null) { + $storageDef->dataInt1 = $fieldSettings['defaultType']; + $storageDef->dataInt2 = $fieldSettings['useSeconds'] ? 1 : 0; + } + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + $fieldDef->fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'defaultType' => $storageDef->dataInt1, + 'useSeconds' => (bool)$storageDef->dataInt2, + ] + ); + + // Building default value + switch ($fieldDef->fieldTypeConstraints->fieldSettings['defaultType']) { + case TimeType::DEFAULT_CURRENT_TIME: + $dateTime = new DateTime(); + $data = $dateTime->getTimestamp() - $dateTime->setTime(0, 0, 0)->getTimestamp(); + break; + default: + $data = null; + } + + $fieldDef->defaultValue->data = $data; + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return string + */ + public function getIndexColumn() + { + return 'sort_key_int'; + } +} + +class_alias(TimeConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter'); diff --git a/src/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlConverter.php new file mode 100644 index 0000000000..cdbd95203a --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlConverter.php @@ -0,0 +1,100 @@ +dataText = isset($value->data['text']) + ? $value->data['text'] + : null; + $storageFieldValue->dataInt = isset($value->data['urlId']) + ? $value->data['urlId'] + : null; + } + + /** + * Converts data from $value to $fieldValue. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue $value + * @param \Ibexa\Contracts\Core\Persistence\Content\FieldValue $fieldValue + */ + public function toFieldValue(StorageFieldValue $value, FieldValue $fieldValue) + { + $fieldValue->data = [ + 'urlId' => $value->dataInt, + 'text' => $value->dataText, + ]; + $fieldValue->sortKey = false; + } + + /** + * Converts field definition data in $fieldDef into $storageFieldDef. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + */ + public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageFieldDefinition $storageDef) + { + } + + /** + * Converts field definition data in $storageDef into $fieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefinition $fieldDef) + { + // @todo: Is it possible to store a default value in the DB? + $fieldDef->defaultValue = new FieldValue(); + $fieldDef->defaultValue->data = ['text' => null]; + } + + /** + * Returns the name of the index column in the attribute table. + * + * Returns the name of the index column the datatype uses, which is either + * "sort_key_int" or "sort_key_string". This column is then used for + * filtering and sorting for this type. + * + * @return false + */ + public function getIndexColumn() + { + return false; + } +} + +class_alias(UrlConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/UserConverter.php b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/UserConverter.php similarity index 84% rename from eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/UserConverter.php rename to src/lib/Persistence/Legacy/Content/FieldValue/Converter/UserConverter.php index 1e61b1b6d1..d619e6d890 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/Converter/UserConverter.php +++ b/src/lib/Persistence/Legacy/Content/FieldValue/Converter/UserConverter.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; +namespace Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\FieldType\User\Type as UserType; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Core\FieldType\FieldSettings; +use Ibexa\Core\FieldType\User\Type as UserType; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; class UserConverter implements Converter { @@ -26,6 +26,7 @@ class UserConverter implements Converter private const REQUIRE_AT_LEAST_ONE_NON_ALPHANUMERIC_CHAR = 8; private const REQUIRE_NEW_PASSWORD = 16; private const REQUIRE_UNIQUE_EMAIL = 32; + private const REQUIRE_NOT_COMPROMISED = 64; /** * {@inheritdoc} @@ -59,6 +60,7 @@ public function toStorageFieldDefinition(FieldDefinition $fieldDef, StorageField 'requireAtLeastOneNumericCharacter' => self::REQUIRE_AT_LEAST_ONE_NUMERIC_CHAR, 'requireAtLeastOneNonAlphanumericCharacter' => self::REQUIRE_AT_LEAST_ONE_NON_ALPHANUMERIC_CHAR, 'requireNewPassword' => self::REQUIRE_NEW_PASSWORD, + 'requireNotCompromisedPassword' => self::REQUIRE_NOT_COMPROMISED, ]; $fieldSettings = $fieldDef->fieldTypeConstraints->fieldSettings; @@ -98,6 +100,7 @@ public function toFieldDefinition(StorageFieldDefinition $storageDef, FieldDefin self::REQUIRE_AT_LEAST_ONE_NUMERIC_CHAR => 'requireAtLeastOneNumericCharacter', self::REQUIRE_AT_LEAST_ONE_NON_ALPHANUMERIC_CHAR => 'requireAtLeastOneNonAlphanumericCharacter', self::REQUIRE_NEW_PASSWORD => 'requireNewPassword', + self::REQUIRE_NOT_COMPROMISED => 'requireNotCompromisedPassword', ]; foreach ($rules as $flag => $rule) { @@ -123,3 +126,5 @@ public function getIndexColumn(): bool return false; } } + +class_alias(UserConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\UserConverter'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/ConverterRegistry.php b/src/lib/Persistence/Legacy/Content/FieldValue/ConverterRegistry.php similarity index 82% rename from eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/ConverterRegistry.php rename to src/lib/Persistence/Legacy/Content/FieldValue/ConverterRegistry.php index cfd710e357..0a2da82760 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/FieldValue/ConverterRegistry.php +++ b/src/lib/Persistence/Legacy/Content/FieldValue/ConverterRegistry.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\FieldValue; +namespace Ibexa\Core\Persistence\Legacy\Content\FieldValue; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound; class ConverterRegistry { @@ -25,7 +25,7 @@ class ConverterRegistry * factory converter object should be returned on execution. The object * is used to convert content fields and content type field definitions * to the legacy storage engine. The given class names must derive the - * {@link \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter} + * {@link \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter} * interface. * * @param array $converterMap A map where key is field type name, and value @@ -52,10 +52,10 @@ public function register($typeName, $converter) * * @param string $typeName * - * @throws \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound + * @throws \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound * @throws \RuntimeException When type is neither Converter instance or callable factory * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter + * @return \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter */ public function getConverter($typeName) { @@ -79,3 +79,5 @@ public function getConverter($typeName) return $this->converterMap[$typeName]; } } + +class_alias(ConverterRegistry::class, 'eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry'); diff --git a/src/lib/Persistence/Legacy/Content/Gateway.php b/src/lib/Persistence/Legacy/Content/Gateway.php new file mode 100644 index 0000000000..95f12bc0b4 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Gateway.php @@ -0,0 +1,501 @@ + Boolean indicating if content's language mask contains alwaysAvailable bit field + * - main_language_code => Language code for main (initial) language. E.g. "eng-GB". + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + abstract public function loadContentInfo(int $contentId): array; + + /** + * Loads rows of info for content identified by $contentIds. + * + * @see loadContentInfo For the returned structure. + * @see \Ibexa\Contracts\Core\Persistence\Content\Handler::loadContentInfoList For how this will only return items found and not throw. + * + * @param int[] $contentIds + */ + abstract public function loadContentInfoList(array $contentIds): array; + + /** + * Load version info for content identified by $contentId and $versionNo. + * Will basically return a hash containing all field values from ezcontentobject_version table plus following keys: + * - names => Hash of content object names. Key is the language code, value is the name. + * - languages => Hash of language ids. Key is the language code (e.g. "eng-GB"), value is the language numeric id without the always available bit. + * - initial_language_code => Language code for initial language in this version. + * + * @param int|null $versionNo Load current version if null. + */ + abstract public function loadVersionInfo(int $contentId, ?int $versionNo = null): array; + + /** + * Return the number of all versions with given status created by the given $userId. + */ + abstract public function countVersionsForUser( + int $userId, + int $status = VersionInfo::STATUS_DRAFT + ): int; + + /** + * Returns data for all versions with given status created by the given $userId. + * + * @return string[][] + */ + abstract public function listVersionsForUser( + int $userId, + int $status = VersionInfo::STATUS_DRAFT + ); + + /** + * Return data for all versions with given status created by the given $userId when content is not in the trash. + * + * The list is sorted by modification date. + */ + abstract public function loadVersionsForUser( + int $userId, + int $status = VersionInfo::STATUS_DRAFT, + int $offset = 0, + int $limit = -1 + ): array; + + /** + * Returns all version data for the given $contentId. + * + * Result is returned with oldest version first (using version id as it has index and is auto increment). + * + * @param int|null $status Optional argument to filter versions by status, like {@see VersionInfo::STATUS_ARCHIVED}. + * @param int $limit Limit for items returned, -1 means none. + * + * @return string[][] + */ + abstract public function listVersions( + int $contentId, + ?int $status = null, + int $limit = -1 + ): array; + + /** + * Return all version numbers for the given $contentId. + * + * @return int[] + */ + abstract public function listVersionNumbers(int $contentId): array; + + /** + * Return last version number for content identified by $contentId. + */ + abstract public function getLastVersionNumber(int $contentId): int; + + /** + * Returns all IDs for locations that refer to $contentId. + * + * @return int[] + */ + abstract public function getAllLocationIds(int $contentId): array; + + /** + * Returns all field IDs of $contentId grouped by their type. + * If $versionNo is set only field IDs for that version are returned. + * If $languageCode is set, only field IDs for that language are returned. + * + * @return int[][] + */ + abstract public function getFieldIdsByType( + int $contentId, + ?int $versionNo = null, + ?string $languageCode = null + ): array; + + /** + * Deletes relations to and from $contentId. + * If $versionNo is set only relations for that version are deleted. + */ + abstract public function deleteRelations(int $contentId, ?int $versionNo = null): void; + + /** + * Remove relations to Content with $contentId from Relation and RelationList field type fields. + */ + abstract public function removeReverseFieldRelations(int $contentId): void; + + /** + * Removes orphaned relations resulting from deleted relation fieldtype. + */ + abstract public function removeRelationsByFieldDefinitionId(int $fieldDefinitionId): void; + + /** + * Delete the field with the given $fieldId. + */ + abstract public function deleteField(int $fieldId): void; + + /** + * Delete all fields of $contentId in all versions. + * + * If $versionNo is set only fields for that version are deleted. + */ + abstract public function deleteFields(int $contentId, ?int $versionNo = null): void; + + /** + * Delete all versions of $contentId. + * + * If $versionNo is set only that version is deleted. + */ + abstract public function deleteVersions(int $contentId, ?int $versionNo = null): void; + + /** + * Delete all names of $contentId. + * + * If $versionNo is set only names for that version are deleted. + */ + abstract public function deleteNames(int $contentId, ?int $versionNo = null): void; + + /** + * Set the content object name. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + abstract public function setName( + int $contentId, + int $version, + string $name, + string $languageCode + ): void; + + /** + * Delete the actual content object referred to by $contentId. + */ + abstract public function deleteContent(int $contentId): void; + + /** + * Load data of related to/from $contentId. + * + * @return array Content data, array structured like {@see \Ibexa\Core\Persistence\Legacy\Content\Gateway::load} + */ + abstract public function loadRelations( + int $contentId, + ?int $contentVersionNo = null, + ?int $relationType = null + ): array; + + /** + * Counts number of related to/from $contentId. + */ + abstract public function countRelations( + int $contentId, + ?int $contentVersionNo = null, + ?int $relationType = null + ): int; + + /** + * Loads paginated data of related to/from $contentId. + * + * @return array> + */ + abstract public function listRelations( + int $contentId, + int $limit, + int $offset = 0, + ?int $contentVersionNo = null, + ?int $relationType = null + ): array; + + /** + * Counts number of related to/from $contentId. + */ + abstract public function countReverseRelations(int $contentId, ?int $relationType = null): int; + + /** + * Load data of related to/from $contentId. + * + * @return array Content data, array structured like {@see \Ibexa\Core\Persistence\Legacy\Content\Gateway::load} + */ + abstract public function loadReverseRelations(int $contentId, ?int $relationType = null): array; + + /** + * Load paginated data of related to/from $contentId. + */ + abstract public function listReverseRelations( + int $contentId, + int $offset = 0, + int $limit = -1, + ?int $relationType = null + ): array; + + /** + * Delete the relation with the given $relationId. + * + * @param int $type one of Relation type constants. + * + * @see \Ibexa\Contracts\Core\Repository\Values\Content\Relation + */ + abstract public function deleteRelation(int $relationId, int $type): void; + + /** + * Insert a new content relation. + */ + abstract public function insertRelation(RelationCreateStruct $createStruct): int; + + /** + * Load Relation object. + * + * @see \Ibexa\Contracts\Core\Persistence\Content\Relation + */ + abstract public function loadRelation(int $relationId): array; + + /** + * Return all Content IDs for the given $contentTypeId. + * + * @return int[] + */ + abstract public function getContentIdsByContentTypeId(int $contentTypeId): array; + + /** + * Load name data for set of content id's and corresponding version number. + * + * @param array[] $rows array of hashes with 'id' and 'version' to load names for + */ + abstract public function loadVersionedNameData(array $rows): array; + + /** + * Bulk-copy all relations meta data for a copied Content item. + * + * Is meant to be used during content copy, so assumes the following: + * - version number is the same + * - content type, and hence content type attribute is the same + * - relation type is the same + * - target relation is the same + * + * @param int|null $versionNo If specified only copy for a given version number, otherwise all. + */ + abstract public function copyRelations( + int $originalContentId, + int $copiedContentId, + ?int $versionNo = null + ): void; + + /** + * Remove the specified translation from the Content Object Version. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + abstract public function deleteTranslationFromContent( + int $contentId, + string $languageCode + ): void; + + /** + * Delete Content fields (attributes) for the given Translation. + * + * If $versionNo is given, fields for that Version only will be deleted. + */ + abstract public function deleteTranslatedFields( + string $languageCode, + int $contentId, + ?int $versionNo = null + ): void; + + /** + * Delete the specified Translation from the given Version. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + abstract public function deleteTranslationFromVersion( + int $contentId, + int $versionNo, + string $languageCode + ): void; + + /** + * @param array $contentIds + * + * @throws \Ibexa\Core\Base\Exceptions\DatabaseException + */ + abstract public function loadVersionInfoList(array $contentIds): array; +} + +class_alias(Gateway::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Gateway'); diff --git a/src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php new file mode 100644 index 0000000000..29d1569b52 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase.php @@ -0,0 +1,2082 @@ +connection = $connection; + $this->databasePlatform = $connection->getDatabasePlatform(); + $this->sharedGateway = $sharedGateway; + $this->queryBuilder = $queryBuilder; + $this->languageHandler = $languageHandler; + $this->languageMaskGenerator = $languageMaskGenerator; + } + + public function insertContentObject(CreateStruct $struct, int $currentVersionNo = 1): int + { + $initialLanguageId = !empty($struct->mainLanguageId) ? $struct->mainLanguageId : $struct->initialLanguageId; + $initialLanguageCode = $this->languageHandler->load($initialLanguageId)->languageCode; + + $name = $struct->name[$initialLanguageCode] ?? ''; + + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::CONTENT_ITEM_TABLE) + ->values( + [ + 'current_version' => $query->createPositionalParameter( + $currentVersionNo, + ParameterType::INTEGER + ), + 'name' => $query->createPositionalParameter($name), + 'contentclass_id' => $query->createPositionalParameter( + $struct->typeId, + ParameterType::INTEGER + ), + 'section_id' => $query->createPositionalParameter( + $struct->sectionId, + ParameterType::INTEGER + ), + 'owner_id' => $query->createPositionalParameter( + $struct->ownerId, + ParameterType::INTEGER + ), + 'initial_language_id' => $query->createPositionalParameter( + $initialLanguageId, + ParameterType::INTEGER + ), + 'remote_id' => $query->createPositionalParameter($struct->remoteId), + 'modified' => $query->createPositionalParameter(0, ParameterType::INTEGER), + 'published' => $query->createPositionalParameter(0, ParameterType::INTEGER), + 'status' => $query->createPositionalParameter( + ContentInfo::STATUS_DRAFT, + ParameterType::INTEGER + ), + 'language_mask' => $query->createPositionalParameter( + $this->languageMaskGenerator->generateLanguageMaskForFields( + $struct->fields, + $initialLanguageCode, + $struct->alwaysAvailable + ), + ParameterType::INTEGER + ), + ] + ); + + $query->execute(); + + return (int)$this->connection->lastInsertId(self::CONTENT_ITEM_SEQ); + } + + public function insertVersion(VersionInfo $versionInfo, array $fields): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::CONTENT_VERSION_TABLE) + ->values( + [ + 'version' => $query->createPositionalParameter( + $versionInfo->versionNo, + ParameterType::INTEGER + ), + 'modified' => $query->createPositionalParameter( + $versionInfo->modificationDate, + ParameterType::INTEGER + ), + 'creator_id' => $query->createPositionalParameter( + $versionInfo->creatorId, + ParameterType::INTEGER + ), + 'created' => $query->createPositionalParameter( + $versionInfo->creationDate, + ParameterType::INTEGER + ), + 'status' => $query->createPositionalParameter( + $versionInfo->status, + ParameterType::INTEGER + ), + 'initial_language_id' => $query->createPositionalParameter( + $this->languageHandler->loadByLanguageCode( + $versionInfo->initialLanguageCode + )->id, + ParameterType::INTEGER + ), + 'contentobject_id' => $query->createPositionalParameter( + $versionInfo->contentInfo->id, + ParameterType::INTEGER + ), + 'language_mask' => $query->createPositionalParameter( + $this->languageMaskGenerator->generateLanguageMaskForFields( + $fields, + $versionInfo->initialLanguageCode, + $versionInfo->contentInfo->alwaysAvailable + ), + ParameterType::INTEGER + ), + ] + ); + + $query->execute(); + + return (int)$this->connection->lastInsertId(self::CONTENT_VERSION_SEQ); + } + + public function updateContent( + int $contentId, + MetadataUpdateStruct $struct, + ?VersionInfo $prePublishVersionInfo = null + ): void { + $query = $this->connection->createQueryBuilder(); + $query->update(self::CONTENT_ITEM_TABLE); + + $fieldsForUpdateMap = [ + 'name' => [ + 'value' => $struct->name, + 'type' => ParameterType::STRING, + ], + 'initial_language_id' => [ + 'value' => $struct->mainLanguageId, + 'type' => ParameterType::INTEGER, + ], + 'modified' => [ + 'value' => $struct->modificationDate, + 'type' => ParameterType::INTEGER, + ], + 'owner_id' => [ + 'value' => $struct->ownerId, + 'type' => ParameterType::INTEGER, + ], + 'published' => [ + 'value' => $struct->publicationDate, + 'type' => ParameterType::INTEGER, + ], + 'remote_id' => [ + 'value' => $struct->remoteId, + 'type' => ParameterType::STRING, + ], + 'is_hidden' => [ + 'value' => $struct->isHidden, + 'type' => ParameterType::BOOLEAN, + ], + ]; + + foreach ($fieldsForUpdateMap as $fieldName => $field) { + if (null === $field['value']) { + continue; + } + $query->set( + $fieldName, + $query->createNamedParameter($field['value'], $field['type'], ":{$fieldName}") + ); + } + + if ($prePublishVersionInfo !== null) { + $mask = $this->languageMaskGenerator->generateLanguageMaskFromLanguageCodes( + $prePublishVersionInfo->languageCodes, + $struct->alwaysAvailable ?? $prePublishVersionInfo->contentInfo->alwaysAvailable + ); + $query->set( + 'language_mask', + $query->createNamedParameter($mask, ParameterType::INTEGER, ':languageMask') + ); + } + + $query->where( + $query->expr()->eq( + 'id', + $query->createNamedParameter($contentId, ParameterType::INTEGER, ':contentId') + ) + ); + + if (!empty($query->getQueryPart('set'))) { + $query->execute(); + } + + // Handle alwaysAvailable flag update separately as it's a more complex task and has impact on several tables + if (isset($struct->alwaysAvailable) || isset($struct->mainLanguageId)) { + $this->updateAlwaysAvailableFlag($contentId, $struct->alwaysAvailable); + } + } + + /** + * Updates version $versionNo for content identified by $contentId, in respect to $struct. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function updateVersion(int $contentId, int $versionNo, UpdateStruct $struct): void + { + $query = $this->connection->createQueryBuilder(); + + $query + ->update(self::CONTENT_VERSION_TABLE) + ->set('creator_id', ':creator_id') + ->set('modified', ':modified') + ->set('initial_language_id', ':initial_language_id') + ->set( + 'language_mask', + $this->databasePlatform->getBitOrComparisonExpression( + 'language_mask', + ':language_mask' + ) + ) + ->setParameter('creator_id', $struct->creatorId, ParameterType::INTEGER) + ->setParameter('modified', $struct->modificationDate, ParameterType::INTEGER) + ->setParameter( + 'initial_language_id', + $struct->initialLanguageId, + ParameterType::INTEGER + ) + ->setParameter( + 'language_mask', + $this->languageMaskGenerator->generateLanguageMaskForFields( + $struct->fields, + $this->languageHandler->load($struct->initialLanguageId)->languageCode, + false + ), + ParameterType::INTEGER + ) + ->where('contentobject_id = :content_id') + ->andWhere('version = :version_no') + ->setParameter('content_id', $contentId, ParameterType::INTEGER) + ->setParameter('version_no', $versionNo, ParameterType::INTEGER); + + $query->execute(); + } + + public function updateAlwaysAvailableFlag(int $contentId, ?bool $alwaysAvailable = null): void + { + // We will need to know some info on the current language mask to update the flag + // everywhere needed + $contentInfoRow = $this->loadContentInfo($contentId); + $versionNo = (int)$contentInfoRow['current_version']; + $languageMask = (int)$contentInfoRow['language_mask']; + $initialLanguageId = (int)$contentInfoRow['initial_language_id']; + if (!isset($alwaysAvailable)) { + $alwaysAvailable = 1 === ($languageMask & 1); + } + + $this->updateContentItemAlwaysAvailableFlag($contentId, $alwaysAvailable); + $this->updateContentNameAlwaysAvailableFlag( + $contentId, + $versionNo, + $alwaysAvailable + ); + $this->updateContentFieldsAlwaysAvailableFlag( + $contentId, + $versionNo, + $alwaysAvailable, + $languageMask, + $initialLanguageId + ); + } + + private function updateContentItemAlwaysAvailableFlag( + int $contentId, + bool $alwaysAvailable + ): void { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->update(self::CONTENT_ITEM_TABLE); + $this + ->setLanguageMaskForUpdateQuery($alwaysAvailable, $query, 'language_mask') + ->where( + $expr->eq( + 'id', + $query->createNamedParameter($contentId, ParameterType::INTEGER, ':contentId') + ) + ); + $query->execute(); + } + + private function updateContentNameAlwaysAvailableFlag( + int $contentId, + int $versionNo, + bool $alwaysAvailable + ): void { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->update(self::CONTENT_NAME_TABLE); + $this + ->setLanguageMaskForUpdateQuery($alwaysAvailable, $query, 'language_id') + ->where( + $expr->eq( + 'contentobject_id', + $query->createNamedParameter($contentId, ParameterType::INTEGER, ':contentId') + ) + ) + ->andWhere( + $expr->eq( + 'content_version', + $query->createNamedParameter($versionNo, ParameterType::INTEGER, ':versionNo') + ) + ); + $query->execute(); + } + + private function updateContentFieldsAlwaysAvailableFlag( + int $contentId, + int $versionNo, + bool $alwaysAvailable, + int $languageMask, + int $initialLanguageId + ): void { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->update(self::CONTENT_FIELD_TABLE) + ->where( + $expr->eq( + 'contentobject_id', + $query->createNamedParameter($contentId, ParameterType::INTEGER, ':contentId') + ) + ) + ->andWhere( + $expr->eq( + 'version', + $query->createNamedParameter($versionNo, ParameterType::INTEGER, ':versionNo') + ) + ); + + // If there is only a single language, update all fields and return + if (!$this->languageMaskGenerator->isLanguageMaskComposite($languageMask)) { + $this->setLanguageMaskForUpdateQuery($alwaysAvailable, $query, 'language_id'); + + $query->execute(); + + return; + } + + // Otherwise: + // 1. Remove always available flag on all fields + $query + ->set( + 'language_id', + $this->databasePlatform->getBitAndComparisonExpression( + 'language_id', + ':languageMaskOperand' + ) + ) + ->setParameter('languageMaskOperand', self::REMOVE_ALWAYS_AVAILABLE_LANG_MASK_OPERAND) + ; + $query->execute(); + $query->resetQueryPart('set'); + + // 2. If Content is always available set the flag only on fields in main language + if ($alwaysAvailable) { + $query + ->set( + 'language_id', + $this->databasePlatform->getBitOrComparisonExpression( + 'language_id', + ':languageMaskOperand' + ) + ) + ->setParameter( + 'languageMaskOperand', + $alwaysAvailable ? 1 : self::REMOVE_ALWAYS_AVAILABLE_LANG_MASK_OPERAND + ); + + $query->andWhere( + $expr->gt( + $this->databasePlatform->getBitAndComparisonExpression( + 'language_id', + $query->createNamedParameter($initialLanguageId, ParameterType::INTEGER, ':initialLanguageId') + ), + $query->createNamedParameter(0, ParameterType::INTEGER, ':zero') + ) + ); + $query->execute(); + } + } + + public function setStatus(int $contentId, int $version, int $status): bool + { + if ($status !== APIVersionInfo::STATUS_PUBLISHED) { + $query = $this->queryBuilder->getSetVersionStatusQuery($contentId, $version, $status); + $rowCount = $query->execute(); + + return $rowCount > 0; + } else { + // If the version's status is PUBLISHED, we use dedicated method for publishing + $this->setPublishedStatus($contentId, $version); + + return true; + } + } + + public function setPublishedStatus(int $contentId, int $versionNo): void + { + $query = $this->queryBuilder->getSetVersionStatusQuery( + $contentId, + $versionNo, + VersionInfo::STATUS_PUBLISHED + ); + + /* this part allows set status `published` only if there is no other published version of the content */ + $notExistPublishedVersion = <<andWhere($notExistPublishedVersion); + if (0 === $query->execute()) { + throw new BadStateException( + '$contentId', + "Someone just published another version of Content item {$contentId}" + ); + } + $this->markContentAsPublished($contentId, $versionNo); + } + + private function markContentAsPublished(int $contentId, int $versionNo): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update('ezcontentobject') + ->set('status', ':status') + ->set('current_version', ':versionNo') + ->where('id =:contentId') + ->setParameter('status', ContentInfo::STATUS_PUBLISHED, ParameterType::INTEGER) + ->setParameter('versionNo', $versionNo, ParameterType::INTEGER) + ->setParameter('contentId', $contentId, ParameterType::INTEGER); + $query->execute(); + } + + /** + * @return int ID + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function insertNewField(Content $content, Field $field, StorageFieldValue $value): int + { + $query = $this->connection->createQueryBuilder(); + + $this->setInsertFieldValues($query, $content, $field, $value); + + // Insert with auto increment ID + $nextId = $this->sharedGateway->getColumnNextIntegerValue( + self::CONTENT_FIELD_TABLE, + 'id', + self::CONTENT_FIELD_SEQ + ); + // avoid trying to insert NULL to trigger default column value behavior + if (null !== $nextId) { + $query + ->setValue('id', ':field_id') + ->setParameter('field_id', $nextId, ParameterType::INTEGER); + } + + $query->execute(); + + return (int)$this->sharedGateway->getLastInsertedId(self::CONTENT_FIELD_SEQ); + } + + public function insertExistingField( + Content $content, + Field $field, + StorageFieldValue $value + ): void { + $query = $this->connection->createQueryBuilder(); + + $this->setInsertFieldValues($query, $content, $field, $value); + + $query + ->setValue('id', ':field_id') + ->setParameter('field_id', $field->id, ParameterType::INTEGER); + + $query->execute(); + } + + /** + * Set the given query field (ezcontentobject_attribute) values. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function setInsertFieldValues( + DoctrineQueryBuilder $query, + Content $content, + Field $field, + StorageFieldValue $value + ): void { + $query + ->insert(self::CONTENT_FIELD_TABLE) + ->values( + [ + 'contentobject_id' => ':content_id', + 'contentclassattribute_id' => ':field_definition_id', + 'data_type_string' => ':data_type_string', + 'language_code' => ':language_code', + 'version' => ':version_no', + 'data_float' => ':data_float', + 'data_int' => ':data_int', + 'data_text' => ':data_text', + 'sort_key_int' => ':sort_key_int', + 'sort_key_string' => ':sort_key_string', + 'language_id' => ':language_id', + ] + ) + ->setParameter( + 'content_id', + $content->versionInfo->contentInfo->id, + ParameterType::INTEGER + ) + ->setParameter('field_definition_id', $field->fieldDefinitionId, ParameterType::INTEGER) + ->setParameter('data_type_string', $field->type, ParameterType::STRING) + ->setParameter('language_code', $field->languageCode, ParameterType::STRING) + ->setParameter('version_no', $field->versionNo, ParameterType::INTEGER) + ->setParameter('data_float', $value->dataFloat) + ->setParameter('data_int', $value->dataInt, ParameterType::INTEGER) + ->setParameter('data_text', $value->dataText, ParameterType::STRING) + ->setParameter('sort_key_int', $value->sortKeyInt, ParameterType::INTEGER) + ->setParameter( + 'sort_key_string', + mb_substr((string)$value->sortKeyString, 0, 255), + ParameterType::STRING + ) + ->setParameter( + 'language_id', + $this->languageMaskGenerator->generateLanguageIndicator( + $field->languageCode, + $this->isLanguageAlwaysAvailable($content, $field->languageCode) + ), + ParameterType::INTEGER + ); + } + + /** + * Check if $languageCode is always available in $content. + */ + private function isLanguageAlwaysAvailable(Content $content, string $languageCode): bool + { + return + $content->versionInfo->contentInfo->alwaysAvailable && + $content->versionInfo->contentInfo->mainLanguageCode === $languageCode + ; + } + + public function updateField(Field $field, StorageFieldValue $value): void + { + // Note, no need to care for language_id here, since Content->$alwaysAvailable + // cannot change on update + $query = $this->connection->createQueryBuilder(); + $this->setFieldUpdateValues($query, $value); + $query + ->where('id = :field_id') + ->andWhere('version = :version_no') + ->setParameter('field_id', $field->id, ParameterType::INTEGER) + ->setParameter('version_no', $field->versionNo, ParameterType::INTEGER); + + $query->execute(); + } + + /** + * Set update fields on $query based on $value. + */ + private function setFieldUpdateValues( + DoctrineQueryBuilder $query, + StorageFieldValue $value + ): void { + $query + ->update(self::CONTENT_FIELD_TABLE) + ->set('data_float', ':data_float') + ->set('data_int', ':data_int') + ->set('data_text', ':data_text') + ->set('sort_key_int', ':sort_key_int') + ->set('sort_key_string', ':sort_key_string') + ->setParameter('data_float', $value->dataFloat) + ->setParameter('data_int', $value->dataInt, ParameterType::INTEGER) + ->setParameter('data_text', $value->dataText, ParameterType::STRING) + ->setParameter('sort_key_int', $value->sortKeyInt, ParameterType::INTEGER) + ->setParameter('sort_key_string', mb_substr((string)$value->sortKeyString, 0, 255)) + ; + } + + /** + * Update an existing, non-translatable field. + */ + public function updateNonTranslatableField( + Field $field, + StorageFieldValue $value, + int $contentId + ): void { + // Note, no need to care for language_id here, since Content->$alwaysAvailable + // cannot change on update + $query = $this->connection->createQueryBuilder(); + $this->setFieldUpdateValues($query, $value); + $query + ->where('contentclassattribute_id = :field_definition_id') + ->andWhere('contentobject_id = :content_id') + ->andWhere('version = :version_no') + ->setParameter('field_definition_id', $field->fieldDefinitionId, ParameterType::INTEGER) + ->setParameter('content_id', $contentId, ParameterType::INTEGER) + ->setParameter('version_no', $field->versionNo, ParameterType::INTEGER); + + $query->execute(); + } + + public function load(int $contentId, ?int $version = null, ?array $translations = null): array + { + return $this->internalLoadContent([$contentId], $version, $translations); + } + + public function loadContentList(array $contentIds, ?array $translations = null): array + { + return $this->internalLoadContent($contentIds, null, $translations); + } + + /** + * Build query for the load and loadContentList methods. + * + * @param int[] $contentIds + * @param string[]|null $translations a list of language codes + * + * @see load(), loadContentList() + */ + private function internalLoadContent( + array $contentIds, + ?int $version = null, + ?array $translations = null + ): array { + $queryBuilder = $this->connection->createQueryBuilder(); + $expr = $queryBuilder->expr(); + $queryBuilder + ->select( + 'c.id AS ezcontentobject_id', + 'c.contentclass_id AS ezcontentobject_contentclass_id', + 'c.section_id AS ezcontentobject_section_id', + 'c.owner_id AS ezcontentobject_owner_id', + 'c.remote_id AS ezcontentobject_remote_id', + 'c.current_version AS ezcontentobject_current_version', + 'c.initial_language_id AS ezcontentobject_initial_language_id', + 'c.modified AS ezcontentobject_modified', + 'c.published AS ezcontentobject_published', + 'c.status AS ezcontentobject_status', + 'c.name AS ezcontentobject_name', + 'c.language_mask AS ezcontentobject_language_mask', + 'c.is_hidden AS ezcontentobject_is_hidden', + 'v.id AS ezcontentobject_version_id', + 'v.version AS ezcontentobject_version_version', + 'v.modified AS ezcontentobject_version_modified', + 'v.creator_id AS ezcontentobject_version_creator_id', + 'v.created AS ezcontentobject_version_created', + 'v.status AS ezcontentobject_version_status', + 'v.language_mask AS ezcontentobject_version_language_mask', + 'v.initial_language_id AS ezcontentobject_version_initial_language_id', + 'a.id AS ezcontentobject_attribute_id', + 'a.contentclassattribute_id AS ezcontentobject_attribute_contentclassattribute_id', + 'a.data_type_string AS ezcontentobject_attribute_data_type_string', + 'a.language_code AS ezcontentobject_attribute_language_code', + 'a.language_id AS ezcontentobject_attribute_language_id', + 'a.data_float AS ezcontentobject_attribute_data_float', + 'a.data_int AS ezcontentobject_attribute_data_int', + 'a.data_text AS ezcontentobject_attribute_data_text', + 'a.sort_key_int AS ezcontentobject_attribute_sort_key_int', + 'a.sort_key_string AS ezcontentobject_attribute_sort_key_string', + 't.main_node_id AS ezcontentobject_tree_main_node_id' + ) + ->from('ezcontentobject', 'c') + ->innerJoin( + 'c', + 'ezcontentobject_version', + 'v', + $expr->andX( + $expr->eq('c.id', 'v.contentobject_id'), + $expr->eq('v.version', $version ?? 'c.current_version') + ) + ) + ->innerJoin( + 'v', + 'ezcontentobject_attribute', + 'a', + $expr->andX( + $expr->eq('v.contentobject_id', 'a.contentobject_id'), + $expr->eq('v.version', 'a.version') + ) + ) + ->leftJoin( + 'c', + 'ezcontentobject_tree', + 't', + $expr->andX( + $expr->eq('c.id', 't.contentobject_id'), + $expr->eq('t.node_id', 't.main_node_id') + ) + ); + + $queryBuilder->where( + $expr->in( + 'c.id', + $queryBuilder->createNamedParameter($contentIds, Connection::PARAM_INT_ARRAY) + ) + ); + + if (!empty($translations)) { + $queryBuilder->andWhere( + $expr->in( + 'a.language_code', + $queryBuilder->createNamedParameter($translations, Connection::PARAM_STR_ARRAY) + ) + ); + } + + return $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadContentInfo(int $contentId): array + { + $queryBuilder = $this->queryBuilder->createLoadContentInfoQueryBuilder(); + $queryBuilder + ->where('c.id = :id') + ->setParameter('id', $contentId, ParameterType::INTEGER); + + $results = $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); + if (empty($results)) { + throw new NotFound('content', "id: $contentId"); + } + + return $results[0]; + } + + public function loadContentInfoList(array $contentIds): array + { + $queryBuilder = $this->queryBuilder->createLoadContentInfoQueryBuilder(); + $queryBuilder + ->where('c.id IN (:ids)') + ->setParameter('ids', $contentIds, Connection::PARAM_INT_ARRAY); + + return $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadContentInfoByRemoteId(string $remoteId): array + { + $queryBuilder = $this->queryBuilder->createLoadContentInfoQueryBuilder(); + $queryBuilder + ->where('c.remote_id = :id') + ->setParameter('id', $remoteId, ParameterType::STRING); + + $results = $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); + if (empty($results)) { + throw new NotFound('content', "remote_id: $remoteId"); + } + + return $results[0]; + } + + public function loadContentInfoByLocationId(int $locationId): array + { + $queryBuilder = $this->queryBuilder->createLoadContentInfoQueryBuilder(false); + $queryBuilder + ->where('t.node_id = :id') + ->setParameter('id', $locationId, ParameterType::INTEGER); + + $results = $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); + if (empty($results)) { + throw new NotFound('content', "node_id: $locationId"); + } + + return $results[0]; + } + + public function loadVersionInfo(int $contentId, ?int $versionNo = null): array + { + $queryBuilder = $this->queryBuilder->createVersionInfoFindQueryBuilder(); + $expr = $queryBuilder->expr(); + + $queryBuilder + ->where( + $expr->eq( + 'v.contentobject_id', + $queryBuilder->createNamedParameter( + $contentId, + ParameterType::INTEGER, + ':content_id' + ) + ) + ); + + if (null !== $versionNo) { + $queryBuilder + ->andWhere( + $expr->eq( + 'v.version', + $queryBuilder->createNamedParameter( + $versionNo, + ParameterType::INTEGER, + ':version_no' + ) + ) + ); + } else { + $queryBuilder->andWhere($expr->eq('v.version', 'c.current_version')); + } + + return $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function countVersionsForUser(int $userId, int $status = VersionInfo::STATUS_DRAFT): int + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select($this->databasePlatform->getCountExpression('v.id')) + ->from('ezcontentobject_version', 'v') + ->innerJoin( + 'v', + 'ezcontentobject', + 'c', + $expr->andX( + $expr->eq('c.id', 'v.contentobject_id'), + $expr->neq('c.status', ContentInfo::STATUS_TRASHED) + ) + ) + ->where( + $query->expr()->andX( + $query->expr()->eq('v.status', ':status'), + $query->expr()->eq('v.creator_id', ':user_id') + ) + ) + ->setParameter(':status', $status, ParameterType::INTEGER) + ->setParameter(':user_id', $userId, ParameterType::INTEGER); + + return (int) $query->execute()->fetchColumn(); + } + + /** + * Return data for all versions with the given status created by the given $userId. + * + * @return string[][] + */ + public function listVersionsForUser(int $userId, int $status = VersionInfo::STATUS_DRAFT): array + { + $query = $this->queryBuilder->createVersionInfoFindQueryBuilder(); + $query + ->where('v.status = :status') + ->andWhere('v.creator_id = :user_id') + ->setParameter('status', $status, ParameterType::INTEGER) + ->setParameter('user_id', $userId, ParameterType::INTEGER) + ->orderBy('v.id'); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadVersionsForUser( + int $userId, + int $status = VersionInfo::STATUS_DRAFT, + int $offset = 0, + int $limit = -1 + ): array { + $query = $this->queryBuilder->createVersionInfoFindQueryBuilder(); + $expr = $query->expr(); + $query->where( + $expr->andX( + $expr->eq('v.status', ':status'), + $expr->eq('v.creator_id', ':user_id'), + $expr->neq('c.status', ContentInfo::STATUS_TRASHED) + ) + ) + ->setFirstResult($offset) + ->setParameter(':status', $status, ParameterType::INTEGER) + ->setParameter(':user_id', $userId, ParameterType::INTEGER); + + if ($limit > 0) { + $query->setMaxResults($limit); + } + + $query->orderBy('v.modified', 'DESC'); + $query->addOrderBy('v.id', 'DESC'); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function listVersions(int $contentId, ?int $status = null, int $limit = -1): array + { + $query = $this->queryBuilder->createVersionInfoFindQueryBuilder(); + $query + ->where('v.contentobject_id = :content_id') + ->setParameter('content_id', $contentId, ParameterType::INTEGER); + + if ($status !== null) { + $query + ->andWhere('v.status = :status') + ->setParameter('status', $status); + } + + if ($limit > 0) { + $query->setMaxResults($limit); + } + + $query->orderBy('v.id'); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + /** + * @return int[] + */ + public function listVersionNumbers(int $contentId): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('version') + ->from(self::CONTENT_VERSION_TABLE) + ->where('contentobject_id = :contentId') + ->groupBy('version') + ->setParameter('contentId', $contentId, ParameterType::INTEGER); + + return array_map('intval', $query->execute()->fetchAll(FetchMode::COLUMN)); + } + + public function getLastVersionNumber(int $contentId): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select($this->databasePlatform->getMaxExpression('version')) + ->from(self::CONTENT_VERSION_TABLE) + ->where('contentobject_id = :content_id') + ->setParameter('content_id', $contentId, ParameterType::INTEGER); + + $statement = $query->execute(); + + return (int)$statement->fetchColumn(); + } + + /** + * @return int[] + */ + public function getAllLocationIds(int $contentId): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('node_id') + ->from('ezcontentobject_tree') + ->where('contentobject_id = :content_id') + ->setParameter('content_id', $contentId, ParameterType::INTEGER); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::COLUMN); + } + + /** + * @return int[][] + */ + public function getFieldIdsByType( + int $contentId, + ?int $versionNo = null, + ?string $languageCode = null + ): array { + $query = $this->connection->createQueryBuilder(); + $query + ->select('id', 'data_type_string') + ->from(self::CONTENT_FIELD_TABLE) + ->where('contentobject_id = :content_id') + ->setParameter('content_id', $contentId, ParameterType::INTEGER); + + if (null !== $versionNo) { + $query + ->andWhere('version = :version_no') + ->setParameter('version_no', $versionNo, ParameterType::INTEGER); + } + + if (!empty($languageCode)) { + $query + ->andWhere('language_code = :language_code') + ->setParameter('language_code', $languageCode, ParameterType::STRING); + } + + $statement = $query->execute(); + + $result = []; + foreach ($statement->fetchAll(FetchMode::ASSOCIATIVE) as $row) { + if (!isset($result[$row['data_type_string']])) { + $result[$row['data_type_string']] = []; + } + $result[$row['data_type_string']][] = (int)$row['id']; + } + + return $result; + } + + public function deleteRelations(int $contentId, ?int $versionNo = null): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::CONTENT_RELATION_TABLE) + ->where('from_contentobject_id = :content_id') + ->setParameter('content_id', $contentId, ParameterType::INTEGER); + + if (null !== $versionNo) { + $query + ->andWhere('from_contentobject_version = :version_no') + ->setParameter('version_no', $versionNo, ParameterType::INTEGER); + } else { + $query->orWhere('to_contentobject_id = :content_id'); + } + + $query->execute(); + } + + public function removeReverseFieldRelations(int $contentId): void + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select(['a.id', 'a.version', 'a.data_type_string', 'a.data_text']) + ->from(self::CONTENT_FIELD_TABLE, 'a') + ->innerJoin( + 'a', + 'ezcontentobject_link', + 'l', + $expr->andX( + 'l.from_contentobject_id = a.contentobject_id', + 'l.from_contentobject_version = a.version', + 'l.contentclassattribute_id = a.contentclassattribute_id' + ) + ) + ->where('l.to_contentobject_id = :content_id') + ->andWhere( + $expr->gt( + $this->databasePlatform->getBitAndComparisonExpression( + 'l.relation_type', + ':relation_type' + ), + 0 + ) + ) + ->setParameter('content_id', $contentId, ParameterType::INTEGER) + ->setParameter('relation_type', Relation::FIELD, ParameterType::INTEGER); + + $statement = $query->execute(); + + while ($row = $statement->fetch(FetchMode::ASSOCIATIVE)) { + if ($row['data_type_string'] === 'ezobjectrelation') { + $this->removeRelationFromRelationField($row); + } + + if ($row['data_type_string'] === 'ezobjectrelationlist') { + $this->removeRelationFromRelationListField($contentId, $row); + } + } + } + + public function removeRelationsByFieldDefinitionId(int $fieldDefinitionId): void + { + $query = $this->connection->createQueryBuilder(); + $query->delete(self::CONTENT_RELATION_TABLE) + ->where('contentclassattribute_id = :field_definition_id') + ->setParameter('field_definition_id', $fieldDefinitionId, ParameterType::INTEGER); + + $query->execute(); + } + + /** + * Update field value of RelationList field type identified by given $row data, + * removing relations toward given $contentId. + * + * @param array $row + */ + private function removeRelationFromRelationListField(int $contentId, array $row): void + { + $document = new DOMDocument('1.0', 'utf-8'); + $document->loadXML($row['data_text']); + + $xpath = new DOMXPath($document); + $xpathExpression = "//related-objects/relation-list/relation-item[@contentobject-id='{$contentId}']"; + + $relationItems = $xpath->query($xpathExpression); + foreach ($relationItems as $relationItem) { + $relationItem->parentNode->removeChild($relationItem); + } + + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::CONTENT_FIELD_TABLE) + ->set('data_text', ':data_text') + ->setParameter('data_text', $document->saveXML(), ParameterType::STRING) + ->where('id = :attribute_id') + ->andWhere('version = :version_no') + ->setParameter('attribute_id', (int)$row['id'], ParameterType::INTEGER) + ->setParameter('version_no', (int)$row['version'], ParameterType::INTEGER); + + $query->execute(); + } + + /** + * Update field value of Relation field type identified by given $row data, + * removing relation data. + * + * @param array $row + */ + private function removeRelationFromRelationField(array $row): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::CONTENT_FIELD_TABLE) + ->set('data_int', ':data_int') + ->set('sort_key_int', ':sort_key_int') + ->setParameter('data_int', null, ParameterType::NULL) + ->setParameter('sort_key_int', 0, ParameterType::INTEGER) + ->where('id = :attribute_id') + ->andWhere('version = :version_no') + ->setParameter('attribute_id', (int)$row['id'], ParameterType::INTEGER) + ->setParameter('version_no', (int)$row['version'], ParameterType::INTEGER); + + $query->execute(); + } + + public function deleteField(int $fieldId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::CONTENT_FIELD_TABLE) + ->where('id = :field_id') + ->setParameter('field_id', $fieldId, ParameterType::INTEGER) + ; + + $query->execute(); + } + + public function deleteFields(int $contentId, ?int $versionNo = null): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::CONTENT_FIELD_TABLE) + ->where('contentobject_id = :content_id') + ->setParameter('content_id', $contentId, ParameterType::INTEGER); + + if (null !== $versionNo) { + $query + ->andWhere('version = :version_no') + ->setParameter('version_no', $versionNo, ParameterType::INTEGER); + } + + $query->execute(); + } + + public function deleteVersions(int $contentId, ?int $versionNo = null): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::CONTENT_VERSION_TABLE) + ->where('contentobject_id = :content_id') + ->setParameter('content_id', $contentId, ParameterType::INTEGER); + + if (null !== $versionNo) { + $query + ->andWhere('version = :version_no') + ->setParameter('version_no', $versionNo, ParameterType::INTEGER); + } + + $query->execute(); + } + + public function deleteNames(int $contentId, int $versionNo = null): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::CONTENT_NAME_TABLE) + ->where('contentobject_id = :content_id') + ->setParameter('content_id', $contentId, ParameterType::INTEGER); + + if (isset($versionNo)) { + $query + ->andWhere('content_version = :version_no') + ->setParameter('version_no', $versionNo, ParameterType::INTEGER); + } + + $query->execute(); + } + + /** + * Query Content name table to find if a name record for the given parameters exists. + */ + private function contentNameExists(int $contentId, int $version, string $languageCode): bool + { + $query = $this->connection->createQueryBuilder(); + $query + ->select($this->databasePlatform->getCountExpression('contentobject_id')) + ->from(self::CONTENT_NAME_TABLE) + ->where('contentobject_id = :content_id') + ->andWhere('content_version = :version_no') + ->andWhere('content_translation = :language_code') + ->setParameter('content_id', $contentId, ParameterType::INTEGER) + ->setParameter('version_no', $version, ParameterType::INTEGER) + ->setParameter('language_code', $languageCode, ParameterType::STRING); + + $stmt = $query->execute(); + + return (int)$stmt->fetch(FetchMode::COLUMN) > 0; + } + + public function setName(int $contentId, int $version, string $name, string $languageCode): void + { + $language = $this->languageHandler->loadByLanguageCode($languageCode); + + $query = $this->connection->createQueryBuilder(); + + // prepare parameters + $query + ->setParameter('name', $name, ParameterType::STRING) + ->setParameter('content_id', $contentId, ParameterType::INTEGER) + ->setParameter('version_no', $version, ParameterType::INTEGER) + ->setParameter('language_id', $language->id, ParameterType::INTEGER) + ->setParameter('language_code', $language->languageCode, ParameterType::STRING) + ; + + if (!$this->contentNameExists($contentId, $version, $language->languageCode)) { + $query + ->insert(self::CONTENT_NAME_TABLE) + ->values( + [ + 'contentobject_id' => ':content_id', + 'content_version' => ':version_no', + 'content_translation' => ':language_code', + 'name' => ':name', + 'language_id' => $this->getSetNameLanguageMaskSubQuery(), + 'real_translation' => ':language_code', + ] + ); + } else { + $query + ->update(self::CONTENT_NAME_TABLE) + ->set('name', ':name') + ->set('language_id', $this->getSetNameLanguageMaskSubQuery()) + ->set('real_translation', ':language_code') + ->where('contentobject_id = :content_id') + ->andWhere('content_version = :version_no') + ->andWhere('content_translation = :language_code'); + } + + $query->execute(); + } + + /** + * Return a language sub select query for setName. + * + * The query generates the proper language mask at the runtime of the INSERT/UPDATE query + * generated by setName. + * + * @see setName + */ + private function getSetNameLanguageMaskSubQuery(): string + { + return << 0 ) + THEN (:language_id | 1) + ELSE :language_id + END + FROM ezcontentobject + WHERE id = :content_id) + SQL; + } + + public function deleteContent(int $contentId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::CONTENT_ITEM_TABLE) + ->where('id = :content_id') + ->setParameter('content_id', $contentId, ParameterType::INTEGER) + ; + + $query->execute(); + } + + public function loadRelations( + int $contentId, + ?int $contentVersionNo = null, + ?int $relationType = null + ): array { + $query = $this->queryBuilder->createRelationFindQueryBuilder(); + $query = $this->prepareRelationQuery($query, $contentId, $contentVersionNo, $relationType); + + return $query->execute()->fetchAllAssociative(); + } + + public function countRelations( + int $contentId, + ?int $contentVersionNo = null, + ?int $relationType = null + ): int { + $query = $this->connection->createQueryBuilder(); + $query->select($this->databasePlatform->getCountExpression('l.id')) + ->from(self::CONTENT_RELATION_TABLE, 'l'); + + $query = $this->prepareRelationQuery($query, $contentId, $contentVersionNo, $relationType); + + return (int)$query->execute()->fetchOne(); + } + + public function listRelations( + int $contentId, + int $limit, + int $offset = 0, + ?int $contentVersionNo = null, + ?int $relationType = null + ): array { + $query = $this->queryBuilder->createRelationFindQueryBuilder(); + $query = $this->prepareRelationQuery($query, $contentId, $contentVersionNo, $relationType); + + $query->setFirstResult($offset) + ->setMaxResults($limit); + + $query->orderBy('l.id', 'DESC'); + + return $query->execute()->fetchAllAssociative(); + } + + private function prepareRelationQuery( + DoctrineQueryBuilder $query, + int $contentId, + ?int $contentVersionNo = null, + ?int $relationType = null + ): DoctrineQueryBuilder { + $expr = $query->expr(); + $query + ->innerJoin( + 'l', + self::CONTENT_ITEM_TABLE, + 'c_to', + $expr->and( + 'l.to_contentobject_id = c_to.id', + 'c_to.status = :status' + ) + ) + ->andWhere( + 'l.from_contentobject_id = :content_id' + ) + ->setParameter( + 'status', + ContentInfo::STATUS_PUBLISHED, + ParameterType::INTEGER + ) + ->setParameter('content_id', $contentId, ParameterType::INTEGER); + + // source version number + if ($contentVersionNo !== null) { + $query + ->andWhere('l.from_contentobject_version = :version_no') + ->setParameter('version_no', $contentVersionNo, ParameterType::INTEGER); + } else { + // from published version only + $query + ->innerJoin( + 'c_to', + self::CONTENT_ITEM_TABLE, + 'c', + $expr->and( + 'c.id = l.from_contentobject_id', + 'c.current_version = l.from_contentobject_version' + ) + ); + } + + // relation type + if (null !== $relationType) { + $query + ->andWhere( + $expr->gt( + $this->databasePlatform->getBitAndComparisonExpression( + 'l.relation_type', + ':relation_type' + ), + 0 + ) + ) + ->setParameter('relation_type', $relationType, ParameterType::INTEGER); + } + + return $query; + } + + public function countReverseRelations(int $toContentId, ?int $relationType = null): int + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select($this->databasePlatform->getCountExpression('l.id')) + ->from(self::CONTENT_RELATION_TABLE, 'l') + ->innerJoin( + 'l', + 'ezcontentobject', + 'c', + $expr->andX( + $expr->eq('l.from_contentobject_id', 'c.id'), + $expr->eq('l.from_contentobject_version', 'c.current_version'), + $expr->eq('c.status', ':status') + ) + ) + ->where( + $expr->eq('l.to_contentobject_id', ':to_content_id') + ) + ->setParameter('to_content_id', $toContentId, ParameterType::INTEGER) + ->setParameter('status', ContentInfo::STATUS_PUBLISHED, ParameterType::INTEGER) + ; + + // relation type + if ($relationType !== null) { + $query->andWhere( + $expr->gt( + $this->databasePlatform->getBitAndComparisonExpression( + 'l.relation_type', + $relationType + ), + 0 + ) + ); + } + + return (int)$query->execute()->fetchColumn(); + } + + public function loadReverseRelations(int $toContentId, ?int $relationType = null): array + { + $query = $this->queryBuilder->createRelationFindQueryBuilder(); + $expr = $query->expr(); + $query + ->join( + 'l', + 'ezcontentobject', + 'c', + $expr->andX( + 'c.id = l.from_contentobject_id', + 'c.current_version = l.from_contentobject_version', + 'c.status = :status' + ) + ) + ->where('l.to_contentobject_id = :to_content_id') + ->setParameter('to_content_id', $toContentId, ParameterType::INTEGER) + ->setParameter( + 'status', + ContentInfo::STATUS_PUBLISHED, + ParameterType::INTEGER + ); + + // relation type + if (null !== $relationType) { + $query->andWhere( + $expr->gt( + $this->databasePlatform->getBitAndComparisonExpression( + 'l.relation_type', + ':relation_type' + ), + 0 + ) + ) + ->setParameter('relation_type', $relationType, ParameterType::INTEGER); + } + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function listReverseRelations( + int $toContentId, + int $offset = 0, + int $limit = -1, + ?int $relationType = null + ): array { + $query = $this->queryBuilder->createRelationFindQueryBuilder(); + $expr = $query->expr(); + $query + ->innerJoin( + 'l', + 'ezcontentobject', + 'c', + $expr->andX( + $expr->eq('l.from_contentobject_id', 'c.id'), + $expr->eq('l.from_contentobject_version', 'c.current_version'), + $expr->eq('c.status', ContentInfo::STATUS_PUBLISHED) + ) + ) + ->where( + $expr->eq('l.to_contentobject_id', ':toContentId') + ) + ->setParameter(':toContentId', $toContentId, ParameterType::INTEGER); + + // relation type + if ($relationType !== null) { + $query->andWhere( + $expr->gt( + $this->databasePlatform->getBitAndComparisonExpression( + 'l.relation_type', + $relationType + ), + 0 + ) + ); + } + $query->setFirstResult($offset); + if ($limit > 0) { + $query->setMaxResults($limit); + } + $query->orderBy('l.id', 'DESC'); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function insertRelation(RelationCreateStruct $createStruct): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::CONTENT_RELATION_TABLE) + ->values( + [ + 'contentclassattribute_id' => ':field_definition_id', + 'from_contentobject_id' => ':from_content_id', + 'from_contentobject_version' => ':from_version_no', + 'relation_type' => ':relation_type', + 'to_contentobject_id' => ':to_content_id', + ] + ) + ->setParameter( + 'field_definition_id', + (int)$createStruct->sourceFieldDefinitionId, + ParameterType::INTEGER + ) + ->setParameter( + 'from_content_id', + $createStruct->sourceContentId, + ParameterType::INTEGER + ) + ->setParameter( + 'from_version_no', + $createStruct->sourceContentVersionNo, + ParameterType::INTEGER + ) + ->setParameter('relation_type', $createStruct->type, ParameterType::INTEGER) + ->setParameter( + 'to_content_id', + $createStruct->destinationContentId, + ParameterType::INTEGER + ); + + $query->execute(); + + return (int)$this->connection->lastInsertId(self::CONTENT_RELATION_SEQ); + } + + /** + * @throws \Doctrine\DBAL\Driver\Exception + * @throws \Doctrine\DBAL\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function loadRelation(int $relationId): array + { + $query = $this->queryBuilder->createRelationFindQueryBuilder(); + $expr = $query->expr(); + + $query + ->andWhere( + $expr->eq('id', ':relationId') + ) + ->setParameter('relationId', $relationId, ParameterType::INTEGER); + + $result = $query->execute()->fetchAllAssociative(); + $resultCount = count($result); + if ($resultCount === 0) { + throw new NotFoundException('Relation', $relationId); + } + + if ($resultCount > 1) { + throw new LogicException('More then one row found for the relation id: ' . $relationId); + } + + return current($result); + } + + public function deleteRelation(int $relationId, int $type): void + { + // Legacy Storage stores COMMON, LINK and EMBED types using bitmask, therefore first load + // existing relation type by given $relationId for comparison + $query = $this->connection->createQueryBuilder(); + $query + ->select('relation_type') + ->from(self::CONTENT_RELATION_TABLE) + ->where('id = :relation_id') + ->setParameter('relation_id', $relationId, ParameterType::INTEGER) + ; + + $loadedRelationType = $query->execute()->fetchColumn(); + + if (!$loadedRelationType) { + return; + } + + $query = $this->connection->createQueryBuilder(); + // If relation type matches then delete + if (((int)$loadedRelationType) === ((int)$type)) { + $query + ->delete(self::CONTENT_RELATION_TABLE) + ->where('id = :relation_id') + ->setParameter('relation_id', $relationId, ParameterType::INTEGER) + ; + + $query->execute(); + } elseif ($loadedRelationType & $type) { + // If relation type is composite update bitmask + + $query + ->update(self::CONTENT_RELATION_TABLE) + ->set( + 'relation_type', + // make & operation removing given $type from the bitmask + $this->databasePlatform->getBitAndComparisonExpression( + 'relation_type', + ':relation_type' + ) + ) + // set the relation type as needed for the above & expression + ->setParameter('relation_type', ~$type, ParameterType::INTEGER) + ->where('id = :relation_id') + ->setParameter('relation_id', $relationId, ParameterType::INTEGER) + ; + + $query->execute(); + } + } + + /** + * @return int[] + */ + public function getContentIdsByContentTypeId(int $contentTypeId): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('id') + ->from(self::CONTENT_ITEM_TABLE) + ->where('contentclass_id = :content_type_id') + ->setParameter('content_type_id', $contentTypeId, ParameterType::INTEGER); + + $statement = $query->execute(); + + return array_map('intval', $statement->fetchAll(FetchMode::COLUMN)); + } + + public function loadVersionedNameData(array $rows): array + { + $query = $this->queryBuilder->createNamesQuery(); + $expr = $query->expr(); + $conditions = []; + foreach ($rows as $row) { + $conditions[] = $expr->andX( + $expr->eq( + 'contentobject_id', + $query->createPositionalParameter($row['id'], ParameterType::INTEGER) + ), + $expr->eq( + 'content_version', + $query->createPositionalParameter($row['version'], ParameterType::INTEGER) + ), + ); + } + + $query->where($expr->orX(...$conditions)); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function copyRelations( + int $originalContentId, + int $copiedContentId, + ?int $versionNo = null + ): void { + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery + ->select( + 'l.contentclassattribute_id', + ':copied_id', + 'l.from_contentobject_version', + 'l.relation_type', + 'l.to_contentobject_id' + ) + ->from(self::CONTENT_RELATION_TABLE, 'l') + ->where('l.from_contentobject_id = :original_id') + ->setParameter('copied_id', $copiedContentId, ParameterType::INTEGER) + ->setParameter('original_id', $originalContentId, ParameterType::INTEGER); + + if ($versionNo) { + $selectQuery + ->andWhere('l.from_contentobject_version = :version') + ->setParameter(':version', $versionNo, ParameterType::INTEGER); + } + // Given we can retain all columns, we just create copies with new `from_contentobject_id` using INSERT INTO SELECT + $insertQuery = <<getSQL(); + + $this->connection->executeUpdate( + $insertQuery, + $selectQuery->getParameters(), + $selectQuery->getParameterTypes() + ); + } + + /** + * {@inheritdoc} + * + * @throws \Doctrine\DBAL\ConnectionException + * @throws \Doctrine\DBAL\DBALException + */ + public function deleteTranslationFromContent(int $contentId, string $languageCode): void + { + $language = $this->languageHandler->loadByLanguageCode($languageCode); + + $this->connection->beginTransaction(); + try { + $this->deleteTranslationFromContentVersions($contentId, $language->id); + $this->deleteTranslationFromContentNames($contentId, $languageCode); + $this->deleteTranslationFromContentObject($contentId, $language->id); + + $this->connection->commit(); + } catch (DBALException $e) { + $this->connection->rollBack(); + throw $e; + } + } + + public function deleteTranslatedFields( + string $languageCode, + int $contentId, + ?int $versionNo = null + ): void { + $query = $this->connection->createQueryBuilder(); + $query + ->delete('ezcontentobject_attribute') + ->where('contentobject_id = :contentId') + ->andWhere('language_code = :languageCode') + ->setParameters( + [ + ':contentId' => $contentId, + ':languageCode' => $languageCode, + ] + ) + ; + + if (null !== $versionNo) { + $query + ->andWhere('version = :versionNo') + ->setParameter(':versionNo', $versionNo) + ; + } + + $query->execute(); + } + + /** + * {@inheritdoc} + * + * @throws \Doctrine\DBAL\DBALException + */ + public function deleteTranslationFromVersion( + int $contentId, + int $versionNo, + string $languageCode + ): void { + $language = $this->languageHandler->loadByLanguageCode($languageCode); + + $this->connection->beginTransaction(); + try { + $this->deleteTranslationFromContentVersions($contentId, $language->id, $versionNo); + $this->deleteTranslationFromContentNames($contentId, $languageCode, $versionNo); + + $this->connection->commit(); + } catch (DBALException $e) { + $this->connection->rollBack(); + throw $e; + } + } + + /** + * Delete translation from the ezcontentobject_name table. + * + * @param int $versionNo optional, if specified, apply to this Version only. + */ + private function deleteTranslationFromContentNames( + int $contentId, + string $languageCode, + ?int $versionNo = null + ) { + $query = $this->connection->createQueryBuilder(); + $query + ->delete('ezcontentobject_name') + ->where('contentobject_id=:contentId') + ->andWhere('real_translation=:languageCode') + ->setParameters( + [ + ':languageCode' => $languageCode, + ':contentId' => $contentId, + ] + ) + ; + + if (null !== $versionNo) { + $query + ->andWhere('content_version = :versionNo') + ->setParameter(':versionNo', $versionNo) + ; + } + + $query->execute(); + } + + /** + * Remove language from language_mask of ezcontentobject. + * + * @param int $contentId + * @param int $languageId + * + * @throws \Ibexa\Core\Base\Exceptions\BadStateException + */ + private function deleteTranslationFromContentObject($contentId, $languageId) + { + $query = $this->connection->createQueryBuilder(); + $query->update('ezcontentobject') + // parameter for bitwise operation has to be placed verbatim (w/o binding) for this to work cross-DBMS + ->set('language_mask', 'language_mask & ~ ' . $languageId) + ->set('modified', ':now') + ->where('id = :contentId') + ->andWhere( + // make sure removed translation is not the last one (incl. alwaysAvailable) + $query->expr()->andX( + 'language_mask & ~ ' . $languageId . ' <> 0', + 'language_mask & ~ ' . $languageId . ' <> 1' + ) + ) + ->setParameter(':now', time()) + ->setParameter(':contentId', $contentId) + ; + + $rowCount = $query->execute(); + + // no rows updated means that most likely somehow it was the last remaining translation + if ($rowCount === 0) { + throw new BadStateException( + '$languageCode', + 'The provided translation is the only translation in this version' + ); + } + } + + /** + * Remove language from language_mask of ezcontentobject_version and update initialLanguageId + * if it matches the removed one. + * + * @param int|null $versionNo optional, if specified, apply to this Version only. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + private function deleteTranslationFromContentVersions( + int $contentId, + int $languageId, + ?int $versionNo = null + ) { + $query = $this->connection->createQueryBuilder(); + $query->update('ezcontentobject_version') + // parameter for bitwise operation has to be placed verbatim (w/o binding) for this to work cross-DBMS + ->set('language_mask', 'language_mask & ~ ' . $languageId) + ->set('modified', ':now') + // update initial_language_id only if it matches removed translation languageId + ->set( + 'initial_language_id', + 'CASE WHEN initial_language_id = :languageId ' . + 'THEN (SELECT initial_language_id AS main_language_id FROM ezcontentobject c WHERE c.id = :contentId) ' . + 'ELSE initial_language_id END' + ) + ->where('contentobject_id = :contentId') + ->andWhere( + // make sure removed translation is not the last one (incl. alwaysAvailable) + $query->expr()->andX( + 'language_mask & ~ ' . $languageId . ' <> 0', + 'language_mask & ~ ' . $languageId . ' <> 1' + ) + ) + ->setParameter(':now', time()) + ->setParameter(':contentId', $contentId) + ->setParameter(':languageId', $languageId) + ; + + if (null !== $versionNo) { + $query + ->andWhere('version = :versionNo') + ->setParameter(':versionNo', $versionNo) + ; + } + + $rowCount = $query->execute(); + + // no rows updated means that most likely somehow it was the last remaining translation + if ($rowCount === 0) { + throw new BadStateException( + '$languageCode', + 'The provided translation is the only translation in this version' + ); + } + } + + /** + * Compute language mask and append it to a QueryBuilder (both column and parameter). + * + * **Can be used on UPDATE queries only!** + */ + private function setLanguageMaskForUpdateQuery( + bool $alwaysAvailable, + DoctrineQueryBuilder $query, + string $languageMaskColumnName + ): DoctrineQueryBuilder { + if ($alwaysAvailable) { + $languageMaskExpr = $this->databasePlatform->getBitOrComparisonExpression( + $languageMaskColumnName, + ':languageMaskOperand' + ); + } else { + $languageMaskExpr = $this->databasePlatform->getBitAndComparisonExpression( + $languageMaskColumnName, + ':languageMaskOperand' + ); + } + + $query + ->set($languageMaskColumnName, $languageMaskExpr) + ->setParameter( + 'languageMaskOperand', + $alwaysAvailable ? 1 : self::REMOVE_ALWAYS_AVAILABLE_LANG_MASK_OPERAND + ); + + return $query; + } + + /** + * @throws \Doctrine\DBAL\Driver\Exception + * @throws \Doctrine\DBAL\Exception + */ + public function loadVersionInfoList(array $contentIds): array + { + $queryBuilder = $this->queryBuilder->createVersionInfoFindQueryBuilder(); + $expr = $queryBuilder->expr(); + + $queryBuilder + ->andWhere( + $expr->in( + 'c.id', + $queryBuilder->createNamedParameter($contentIds, Connection::PARAM_INT_ARRAY) + ) + ) + ->andWhere( + $expr->eq('v.version', 'c.current_version') + ); + + return $queryBuilder->execute()->fetchAllAssociative(); + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Gateway/DoctrineDatabase/QueryBuilder.php b/src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase/QueryBuilder.php similarity index 91% rename from eZ/Publish/Core/Persistence/Legacy/Content/Gateway/DoctrineDatabase/QueryBuilder.php rename to src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase/QueryBuilder.php index 5c5ffb5adc..cdc28887e9 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Gateway/DoctrineDatabase/QueryBuilder.php +++ b/src/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabase/QueryBuilder.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase; +namespace Ibexa\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase; use Doctrine\DBAL\Connection; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Query\QueryBuilder as DoctrineQueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Gateway; use function time; /** @@ -92,10 +92,10 @@ public function getSetVersionStatusQuery( /** * Create a select query to load Content Info data. * - * @see Gateway::loadContentInfo() - * @see Gateway::loadContentInfoList() - * @see Gateway::loadContentInfoByRemoteId() - * @see Gateway::loadContentInfoByLocationId() + * @see \Ibexa\Core\Persistence\Legacy\Content\Gateway::loadContentInfo() + * @see \Ibexa\Core\Persistence\Legacy\Content\Gateway::loadContentInfoList() + * @see \Ibexa\Core\Persistence\Legacy\Content\Gateway::loadContentInfoByRemoteId() + * @see \Ibexa\Core\Persistence\Legacy\Content\Gateway::loadContentInfoByLocationId() */ public function createLoadContentInfoQueryBuilder( bool $joinMainLocation = true @@ -185,3 +185,5 @@ public function createVersionInfoFindQueryBuilder(): DoctrineQueryBuilder return $query; } } + +class_alias(QueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..f4e8f9dc32 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Gateway/ExceptionConversion.php @@ -0,0 +1,571 @@ +innerGateway = $innerGateway; + } + + public function insertContentObject(CreateStruct $struct, int $currentVersionNo = 1): int + { + try { + return $this->innerGateway->insertContentObject($struct, $currentVersionNo); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function insertVersion(VersionInfo $versionInfo, array $fields): int + { + try { + return $this->innerGateway->insertVersion($versionInfo, $fields); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateContent( + int $contentId, + MetadataUpdateStruct $struct, + ?VersionInfo $prePublishVersionInfo = null + ): void { + try { + $this->innerGateway->updateContent($contentId, $struct, $prePublishVersionInfo); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + /** + * Updates version $versionNo for content identified by $contentId, in respect to $struct. + */ + public function updateVersion(int $contentId, int $versionNo, UpdateStruct $struct): void + { + try { + $this->innerGateway->updateVersion($contentId, $versionNo, $struct); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateAlwaysAvailableFlag( + int $contentId, + ?bool $newAlwaysAvailable = null + ): void { + try { + $this->innerGateway->updateAlwaysAvailableFlag($contentId, $newAlwaysAvailable); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function setStatus(int $contentId, int $version, int $status): bool + { + try { + return $this->innerGateway->setStatus($contentId, $version, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function setPublishedStatus(int $contentId, int $status): void + { + try { + $this->innerGateway->setPublishedStatus($contentId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function insertNewField(Content $content, Field $field, StorageFieldValue $value): int + { + try { + return $this->innerGateway->insertNewField($content, $field, $value); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function insertExistingField( + Content $content, + Field $field, + StorageFieldValue $value + ): void { + try { + $this->innerGateway->insertExistingField($content, $field, $value); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateField(Field $field, StorageFieldValue $value): void + { + try { + $this->innerGateway->updateField($field, $value); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateNonTranslatableField( + Field $field, + StorageFieldValue $value, + int $contentId + ): void { + try { + $this->innerGateway->updateNonTranslatableField($field, $value, $contentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function load(int $contentId, ?int $version = null, ?array $translations = null): array + { + try { + return $this->innerGateway->load($contentId, $version, $translations); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadContentList(array $contentIds, ?array $translations = null): array + { + try { + return $this->innerGateway->loadContentList($contentIds, $translations); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadContentInfoByRemoteId(string $remoteId): array + { + try { + return $this->innerGateway->loadContentInfoByRemoteId($remoteId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadContentInfoByLocationId(int $locationId): array + { + try { + return $this->innerGateway->loadContentInfoByLocationId($locationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadContentInfo(int $contentId): array + { + try { + return $this->innerGateway->loadContentInfo($contentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadContentInfoList(array $contentIds): array + { + try { + return $this->innerGateway->loadContentInfoList($contentIds); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadVersionInfo(int $contentId, ?int $versionNo = null): array + { + try { + return $this->innerGateway->loadVersionInfo($contentId, $versionNo); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countVersionsForUser(int $userId, int $status = VersionInfo::STATUS_DRAFT): int + { + try { + return $this->innerGateway->countVersionsForUser($userId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + /** + * @return string[][] + */ + public function listVersionsForUser(int $userId, int $status = VersionInfo::STATUS_DRAFT): array + { + try { + return $this->innerGateway->listVersionsForUser($userId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadVersionsForUser( + int $userId, + int $status = VersionInfo::STATUS_DRAFT, + int $offset = 0, + int $limit = -1 + ): array { + try { + return $this->innerGateway->loadVersionsForUser($userId, $status, $offset, $limit); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function listVersions(int $contentId, ?int $status = null, int $limit = -1): array + { + try { + return $this->innerGateway->listVersions($contentId, $status, $limit); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function listVersionNumbers(int $contentId): array + { + try { + return $this->innerGateway->listVersionNumbers($contentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getLastVersionNumber(int $contentId): int + { + try { + return $this->innerGateway->getLastVersionNumber($contentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getAllLocationIds(int $contentId): array + { + try { + return $this->innerGateway->getAllLocationIds($contentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getFieldIdsByType( + int $contentId, + ?int $versionNo = null, + ?string $languageCode = null + ): array { + try { + return $this->innerGateway->getFieldIdsByType($contentId, $versionNo, $languageCode); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteRelations(int $contentId, ?int $versionNo = null): void + { + try { + $this->innerGateway->deleteRelations($contentId, $versionNo); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function removeReverseFieldRelations(int $contentId): void + { + try { + $this->innerGateway->removeReverseFieldRelations($contentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function removeRelationsByFieldDefinitionId(int $fieldDefinitionId): void + { + try { + $this->innerGateway->removeRelationsByFieldDefinitionId($fieldDefinitionId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteField(int $fieldId): void + { + try { + $this->innerGateway->deleteField($fieldId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteFields(int $contentId, ?int $versionNo = null): void + { + try { + $this->innerGateway->deleteFields($contentId, $versionNo); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteVersions(int $contentId, ?int $versionNo = null): void + { + try { + $this->innerGateway->deleteVersions($contentId, $versionNo); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteNames(int $contentId, ?int $versionNo = null): void + { + try { + $this->innerGateway->deleteNames($contentId, $versionNo); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function setName(int $contentId, int $version, string $name, string $languageCode): void + { + try { + $this->innerGateway->setName($contentId, $version, $name, $languageCode); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteContent(int $contentId): void + { + try { + $this->innerGateway->deleteContent($contentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadRelations( + int $contentId, + ?int $contentVersionNo = null, + ?int $relationType = null + ): array { + try { + return $this->innerGateway->loadRelations($contentId, $contentVersionNo, $relationType); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countRelations( + int $contentId, + ?int $contentVersionNo = null, + ?int $relationType = null + ): int { + try { + return $this->innerGateway->countRelations($contentId, $contentVersionNo, $relationType); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function listRelations( + int $contentId, + int $limit, + int $offset = 0, + ?int $contentVersionNo = null, + ?int $relationType = null + ): array { + try { + return $this->innerGateway->listRelations( + $contentId, + $limit, + $offset, + $contentVersionNo, + $relationType + ); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countReverseRelations(int $contentId, ?int $relationType = null): int + { + try { + return $this->innerGateway->countReverseRelations($contentId, $relationType); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadReverseRelations(int $contentId, ?int $relationType = null): array + { + try { + return $this->innerGateway->loadReverseRelations($contentId, $relationType); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function listReverseRelations( + int $contentId, + int $offset = 0, + int $limit = -1, + ?int $relationType = null + ): array { + try { + return $this->innerGateway->listReverseRelations( + $contentId, + $offset, + $limit, + $relationType + ); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteRelation(int $relationId, int $type): void + { + try { + $this->innerGateway->deleteRelation($relationId, $type); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function insertRelation(RelationCreateStruct $struct): int + { + try { + return $this->innerGateway->insertRelation($struct); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadRelation(int $relationId): array + { + try { + return $this->innerGateway->loadRelation($relationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getContentIdsByContentTypeId($contentTypeId): array + { + try { + return $this->innerGateway->getContentIdsByContentTypeId($contentTypeId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadVersionedNameData(array $rows): array + { + try { + return $this->innerGateway->loadVersionedNameData($rows); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function copyRelations( + int $originalContentId, + int $copiedContentId, + ?int $versionNo = null + ): void { + try { + $this->innerGateway->copyRelations($originalContentId, $copiedContentId, $versionNo); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteTranslationFromContent(int $contentId, string $languageCode): void + { + try { + $this->innerGateway->deleteTranslationFromContent($contentId, $languageCode); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteTranslatedFields( + string $languageCode, + int $contentId, + ?int $versionNo = null + ): void { + try { + $this->innerGateway->deleteTranslatedFields( + $languageCode, + $contentId, + $versionNo + ); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteTranslationFromVersion( + int $contentId, + int $versionNo, + string $languageCode + ): void { + try { + $this->innerGateway->deleteTranslationFromVersion( + $contentId, + $versionNo, + $languageCode + ); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadVersionInfoList(array $contentIds): array + { + try { + return $this->innerGateway->loadVersionInfoList($contentIds); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/Content/Handler.php b/src/lib/Persistence/Legacy/Content/Handler.php new file mode 100644 index 0000000000..610f722996 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Handler.php @@ -0,0 +1,992 @@ +contentGateway = $contentGateway; + $this->locationGateway = $locationGateway; + $this->mapper = $mapper; + $this->fieldHandler = $fieldHandler; + $this->slugConverter = $slugConverter; + $this->urlAliasGateway = $urlAliasGateway; + $this->contentTypeHandler = $contentTypeHandler; + $this->treeHandler = $treeHandler; + $this->languageHandler = $languageHandler; + $this->logger = null !== $logger ? $logger : new NullLogger(); + } + + /** + * Creates a new Content entity in the storage engine. + * + * The values contained inside the $content will form the basis of stored + * entity. + * + * Will contain always a complete list of fields. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\CreateStruct $struct Content creation struct. + * + * @return \Ibexa\Contracts\Core\Persistence\Content Content value object + */ + public function create(CreateStruct $struct) + { + return $this->internalCreate($struct); + } + + /** + * Creates a new Content entity in the storage engine. + * + * The values contained inside the $content will form the basis of stored + * entity. + * + * Will contain always a complete list of fields. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\CreateStruct $struct Content creation struct. + * @param mixed $versionNo Used by self::copy() to maintain version numbers + * + * @return \Ibexa\Contracts\Core\Persistence\Content Content value object + */ + protected function internalCreate(CreateStruct $struct, $versionNo = 1) + { + $content = new Content(); + + $content->fields = $struct->fields; + $content->versionInfo = $this->mapper->createVersionInfoFromCreateStruct($struct, $versionNo); + + $content->versionInfo->contentInfo->id = $this->contentGateway->insertContentObject($struct, $versionNo); + $content->versionInfo->id = $this->contentGateway->insertVersion( + $content->versionInfo, + $struct->fields + ); + + $contentType = $this->contentTypeHandler->load($struct->typeId); + $this->fieldHandler->createNewFields($content, $contentType); + + // Create node assignments + foreach ($struct->locations as $location) { + $location->contentId = $content->versionInfo->contentInfo->id; + $location->contentVersion = $content->versionInfo->versionNo; + $this->locationGateway->createNodeAssignment( + $location, + $location->parentId, + LocationGateway::NODE_ASSIGNMENT_OP_CODE_CREATE + ); + } + + // Create names + foreach ($content->versionInfo->names as $language => $name) { + $this->contentGateway->setName( + $content->versionInfo->contentInfo->id, + $content->versionInfo->versionNo, + $name, + $language + ); + } + + return $content; + } + + /** + * Performs the publishing operations required to set the version identified by $updateStruct->versionNo and + * $updateStruct->id as the published one. + * + * The publish procedure will: + * - Create location nodes based on the node assignments + * - Update the content object using the provided metadata update struct + * - Update the node assignments + * - Update location nodes of the content with the new published version + * - Set content and version status to published + * + * @param int $contentId + * @param int $versionNo + * @param \Ibexa\Contracts\Core\Persistence\Content\MetadataUpdateStruct $metaDataUpdateStruct + * + * @return \Ibexa\Contracts\Core\Persistence\Content The published Content + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function publish($contentId, $versionNo, MetadataUpdateStruct $metaDataUpdateStruct) + { + // Archive currently published version + $versionInfo = $this->loadVersionInfo($contentId, $versionNo); + if ($versionInfo->contentInfo->currentVersionNo != $versionNo) { + $this->setStatus( + $contentId, + VersionInfo::STATUS_ARCHIVED, + $versionInfo->contentInfo->currentVersionNo + ); + } + + // Set always available name for the content + $metaDataUpdateStruct->name = $versionInfo->names[$versionInfo->contentInfo->mainLanguageCode]; + + $this->contentGateway->updateContent($contentId, $metaDataUpdateStruct, $versionInfo); + $this->locationGateway->createLocationsFromNodeAssignments( + $contentId, + $versionNo + ); + + $this->locationGateway->updateLocationsContentVersionNo($contentId, $versionNo); + $this->contentGateway->setPublishedStatus($contentId, $versionNo); + + return $this->load($contentId, $versionNo); + } + + /** + * Creates a new draft version from $contentId in $version. + * + * Copies all fields from $contentId in $srcVersion and creates a new + * version of the referred Content from it. + * + * Note: When creating a new draft in the old admin interface there will + * also be an entry in the `eznode_assignment` created for the draft. This + * is ignored in this implementation. + * + * @param mixed $contentId + * @param mixed $srcVersion + * @param mixed $userId + * @param string|null $languageCode + * + * @return \Ibexa\Contracts\Core\Persistence\Content + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function createDraftFromVersion($contentId, $srcVersion, $userId, ?string $languageCode = null) + { + $content = $this->load($contentId, $srcVersion); + + // Create new version + $content->versionInfo = $this->mapper->createVersionInfoForContent( + $content, + $this->contentGateway->getLastVersionNumber($contentId) + 1, + $userId, + $languageCode + ); + $content->versionInfo->id = $this->contentGateway->insertVersion( + $content->versionInfo, + $content->fields + ); + + // Clone fields from previous version and append them to the new one + $this->fieldHandler->createExistingFieldsInNewVersion($content); + + // Persist virtual fields + $contentType = $this->contentTypeHandler->load($content->versionInfo->contentInfo->contentTypeId); + $this->fieldHandler->updateFields($content, new UpdateStruct([ + 'initialLanguageId' => $this->languageHandler->loadByLanguageCode( + $content->versionInfo->initialLanguageCode + )->id, + ]), $contentType); + + // Create relations for new version + $relations = $this->contentGateway->loadRelations($contentId, $srcVersion); + foreach ($relations as $relation) { + $this->contentGateway->insertRelation( + new RelationCreateStruct( + [ + 'sourceContentId' => $contentId, + 'sourceContentVersionNo' => $content->versionInfo->versionNo, + 'sourceFieldDefinitionId' => $relation['ezcontentobject_link_contentclassattribute_id'], + 'destinationContentId' => $relation['ezcontentobject_link_to_contentobject_id'], + 'type' => (int)$relation['ezcontentobject_link_relation_type'], + ] + ) + ); + } + + // Create content names for new version + foreach ($content->versionInfo->names as $language => $name) { + $this->contentGateway->setName( + $contentId, + $content->versionInfo->versionNo, + $name, + $language + ); + } + + return $content; + } + + /** + * {@inheritdoc} + */ + public function load($id, $version = null, array $translations = null) + { + $rows = $this->contentGateway->load($id, $version, $translations); + + if (empty($rows)) { + throw new NotFound('content', "contentId: $id, versionNo: $version"); + } + + $contentObjects = $this->mapper->extractContentFromRows( + $rows, + $this->contentGateway->loadVersionedNameData([[ + 'id' => $id, + 'version' => $rows[0]['ezcontentobject_version_version'], + ]]), + 'ezcontentobject_', + $translations + ); + $content = $contentObjects[0]; + unset($rows, $contentObjects); + + $this->fieldHandler->loadExternalFieldData($content); + + return $content; + } + + /** + * {@inheritdoc} + */ + public function loadContentList(array $contentIds, array $translations = null): array + { + $rawList = $this->contentGateway->loadContentList($contentIds, $translations); + if (empty($rawList)) { + return []; + } + + $idVersionPairs = []; + foreach ($rawList as $row) { + // As there is only one version per id, set id as key to avoid duplicates + $idVersionPairs[$row['ezcontentobject_id']] = [ + 'id' => $row['ezcontentobject_id'], + 'version' => $row['ezcontentobject_version_version'], + ]; + } + + // group name data per Content Id + $nameData = $this->contentGateway->loadVersionedNameData(array_values($idVersionPairs)); + $contentItemNameData = []; + foreach ($nameData as $nameDataRow) { + $contentId = $nameDataRow['ezcontentobject_name_contentobject_id']; + $contentItemNameData[$contentId][] = $nameDataRow; + } + + // group rows per Content Id be able to ignore Content items with erroneous data + $contentItemsRows = []; + foreach ($rawList as $row) { + $contentId = $row['ezcontentobject_id']; + $contentItemsRows[$contentId][] = $row; + } + unset($rawList, $idVersionPairs); + + // try to extract Content from each Content data + $contentItems = []; + foreach ($contentItemsRows as $contentId => $contentItemsRow) { + try { + $contentList = $this->mapper->extractContentFromRows( + $contentItemsRow, + $contentItemNameData[$contentId], + 'ezcontentobject_', + $translations + ); + $contentItems[$contentId] = $contentList[0]; + } catch (Exception $e) { + $this->logger->warning( + sprintf( + '%s: Content %d not loaded: %s', + __METHOD__, + $contentId, + $e->getMessage() + ) + ); + } + } + + // try to load External Storage data for each Content, ignore Content items for which it failed + foreach ($contentItems as $contentId => $content) { + try { + $this->fieldHandler->loadExternalFieldData($content); + } catch (Exception $e) { + unset($contentItems[$contentId]); + $this->logger->warning( + sprintf( + '%s: Content %d not loaded: %s', + __METHOD__, + $contentId, + $e->getMessage() + ) + ); + } + } + + return $contentItems; + } + + /** + * Returns the metadata object for a content identified by $contentId. + * + * @param int|string $contentId + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo + */ + public function loadContentInfo($contentId) + { + return $this->treeHandler->loadContentInfo($contentId); + } + + public function loadContentInfoList(array $contentIds) + { + $list = $this->mapper->extractContentInfoFromRows( + $this->contentGateway->loadContentInfoList($contentIds) + ); + + $listByContentId = []; + foreach ($list as $item) { + $listByContentId[$item->id] = $item; + } + + return $listByContentId; + } + + /** + * Returns the metadata object for a content identified by $remoteId. + * + * @param mixed $remoteId + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo + */ + public function loadContentInfoByRemoteId($remoteId) + { + return $this->mapper->extractContentInfoFromRow( + $this->contentGateway->loadContentInfoByRemoteId($remoteId) + ); + } + + /** + * {@inheritdoc} + */ + public function loadVersionInfo($contentId, $versionNo = null) + { + $rows = $this->contentGateway->loadVersionInfo($contentId, $versionNo); + if (empty($rows)) { + throw new NotFound('content', $contentId); + } + + $versionInfo = $this->mapper->extractVersionInfoListFromRows( + $rows, + $this->contentGateway->loadVersionedNameData([['id' => $contentId, 'version' => $rows[0]['ezcontentobject_version_version']]]) + ); + + return reset($versionInfo); + } + + public function countDraftsForUser(int $userId): int + { + return $this->contentGateway->countVersionsForUser($userId, VersionInfo::STATUS_DRAFT); + } + + /** + * Returns all versions with draft status created by the given $userId. + * + * @param int $userId + * + * @return \Ibexa\Contracts\Core\Persistence\Content\VersionInfo[] + */ + public function loadDraftsForUser($userId) + { + $rows = $this->contentGateway->listVersionsForUser($userId, VersionInfo::STATUS_DRAFT); + if (empty($rows)) { + return []; + } + + $idVersionPairs = array_map( + static function ($row) { + return [ + 'id' => $row['ezcontentobject_version_contentobject_id'], + 'version' => $row['ezcontentobject_version_version'], + ]; + }, + $rows + ); + $nameRows = $this->contentGateway->loadVersionedNameData($idVersionPairs); + + return $this->mapper->extractVersionInfoListFromRows($rows, $nameRows); + } + + public function loadDraftListForUser(int $userId, int $offset = 0, int $limit = -1): array + { + $rows = $this->contentGateway->loadVersionsForUser($userId, VersionInfo::STATUS_DRAFT, $offset, $limit); + if (empty($rows)) { + return []; + } + + $idVersionPairs = array_map( + static function (array $row): array { + return [ + 'id' => $row['ezcontentobject_version_contentobject_id'], + 'version' => $row['ezcontentobject_version_version'], + ]; + }, + $rows + ); + $nameRows = $this->contentGateway->loadVersionedNameData($idVersionPairs); + + return $this->mapper->extractVersionInfoListFromRows($rows, $nameRows); + } + + /** + * Sets the status of object identified by $contentId and $version to $status. + * + * The $status can be one of VersionInfo::STATUS_DRAFT, VersionInfo::STATUS_PUBLISHED, VersionInfo::STATUS_ARCHIVED + * When status is set to VersionInfo::STATUS_PUBLISHED content status is updated to ContentInfo::STATUS_PUBLISHED + * + * @param int $contentId + * @param int $status + * @param int $version + * + * @return bool + */ + public function setStatus($contentId, $status, $version) + { + return $this->contentGateway->setStatus($contentId, $version, $status); + } + + /** + * Updates a content object meta data, identified by $contentId. + * + * @param int $contentId + * @param \Ibexa\Contracts\Core\Persistence\Content\MetadataUpdateStruct $content + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo + */ + public function updateMetadata($contentId, MetadataUpdateStruct $content) + { + $this->contentGateway->updateContent($contentId, $content); + $this->updatePathIdentificationString($contentId, $content); + + return $this->loadContentInfo($contentId); + } + + /** + * Updates path identification string for locations of given $contentId if main language + * is set in update struct. + * + * This is specific to the Legacy storage engine, as path identification string is deprecated. + * + * @param int $contentId + * @param \Ibexa\Contracts\Core\Persistence\Content\MetadataUpdateStruct $content + */ + protected function updatePathIdentificationString($contentId, MetadataUpdateStruct $content) + { + if (isset($content->mainLanguageId)) { + $contentLocationsRows = $this->locationGateway->loadLocationDataByContent($contentId); + foreach ($contentLocationsRows as $row) { + $locationName = ''; + $urlAliasRows = $this->urlAliasGateway->loadLocationEntries( + $row['node_id'], + false, + $content->mainLanguageId + ); + if (!empty($urlAliasRows)) { + $locationName = $urlAliasRows[0]['text']; + } + $this->locationGateway->updatePathIdentificationString( + $row['node_id'], + $row['parent_node_id'], + $this->slugConverter->convert( + $locationName, + 'node_' . $row['node_id'], + 'urlalias_compat' + ) + ); + } + } + } + + /** + * Updates a content version, identified by $contentId and $versionNo. + * + * @param int $contentId + * @param int $versionNo + * @param \Ibexa\Contracts\Core\Persistence\Content\UpdateStruct $updateStruct + * + * @return \Ibexa\Contracts\Core\Persistence\Content + */ + public function updateContent($contentId, $versionNo, UpdateStruct $updateStruct) + { + $content = $this->load($contentId, $versionNo); + $this->contentGateway->updateVersion($contentId, $versionNo, $updateStruct); + $contentType = $this->contentTypeHandler->load($content->versionInfo->contentInfo->contentTypeId); + $this->fieldHandler->updateFields($content, $updateStruct, $contentType); + foreach ($updateStruct->name as $language => $name) { + $this->contentGateway->setName( + $contentId, + $versionNo, + $name, + $language + ); + } + + return $this->load($contentId, $versionNo); + } + + /** + * Deletes all versions and fields, all locations (subtree), and all relations. + * + * Removes the relations, but not the related objects. All subtrees of the + * assigned nodes of this content objects are removed (recursively). + * + * @param int $contentId + * + * @return bool + */ + public function deleteContent($contentId) + { + $contentLocations = $this->contentGateway->getAllLocationIds($contentId); + if (empty($contentLocations)) { + $this->removeRawContent($contentId); + } else { + foreach ($contentLocations as $locationId) { + $this->treeHandler->removeSubtree($locationId); + } + } + } + + /** + * Deletes raw content data. + * + * @param int $contentId + */ + public function removeRawContent($contentId) + { + $this->treeHandler->removeRawContent($contentId); + } + + /** + * Deletes given version, its fields, node assignment, relations and names. + * + * Removes the relations, but not the related objects. + * + * @param int $contentId + * @param int $versionNo + * + * @return bool + */ + public function deleteVersion($contentId, $versionNo) + { + $versionInfo = $this->loadVersionInfo($contentId, $versionNo); + + $this->locationGateway->deleteNodeAssignment($contentId, $versionNo); + + $this->fieldHandler->deleteFields($contentId, $versionInfo); + + $this->contentGateway->deleteRelations($contentId, $versionNo); + $this->contentGateway->deleteVersions($contentId, $versionNo); + $this->contentGateway->deleteNames($contentId, $versionNo); + } + + /** + * Returns the versions for $contentId. + * + * Result is returned with oldest version first (sorted by created, alternatively version id if auto increment). + * + * @param int $contentId + * @param mixed|null $status Optional argument to filter versions by status, like {@see VersionInfo::STATUS_ARCHIVED}. + * @param int $limit Limit for items returned, -1 means none. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\VersionInfo[] + */ + public function listVersions($contentId, $status = null, $limit = -1) + { + return $this->treeHandler->listVersions($contentId, $status, $limit); + } + + /** + * Copy Content with Fields, Versions & Relations from $contentId in $version. + * + * {@inheritdoc} + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If content or version is not found + * + * @param mixed $contentId + * @param mixed|null $versionNo Copy all versions if left null + * @param int|null $newOwnerId + * + * @return \Ibexa\Contracts\Core\Persistence\Content + */ + public function copy($contentId, $versionNo = null, $newOwnerId = null) + { + $currentVersionNo = isset($versionNo) ? + $versionNo : + $this->loadContentInfo($contentId)->currentVersionNo; + + // Copy content in given version or current version + $createStruct = $this->mapper->createCreateStructFromContent( + $this->load($contentId, $currentVersionNo) + ); + if ($newOwnerId) { + $createStruct->ownerId = $newOwnerId; + } + $content = $this->internalCreate($createStruct, $currentVersionNo); + + // If version was not passed also copy other versions + if (!isset($versionNo)) { + $contentType = $this->contentTypeHandler->load($createStruct->typeId); + + foreach ($this->listVersions($contentId) as $versionInfo) { + if ($versionInfo->versionNo === $currentVersionNo) { + continue; + } + + $versionContent = $this->load($contentId, $versionInfo->versionNo); + + $versionContent->versionInfo->contentInfo->id = $content->versionInfo->contentInfo->id; + $versionContent->versionInfo->modificationDate = $createStruct->modified; + $versionContent->versionInfo->creationDate = $createStruct->modified; + $versionContent->versionInfo->creatorId = $createStruct->ownerId; + + $versionContent->versionInfo->id = $this->contentGateway->insertVersion( + $versionContent->versionInfo, + $versionContent->fields + ); + + $this->fieldHandler->createNewFields($versionContent, $contentType); + + // Create names + foreach ($versionContent->versionInfo->names as $language => $name) { + $this->contentGateway->setName( + $content->versionInfo->contentInfo->id, + $versionInfo->versionNo, + $name, + $language + ); + } + } + + // Batch copy relations for all versions + $this->contentGateway->copyRelations($contentId, $content->versionInfo->contentInfo->id); + } else { + // Batch copy relations for published version + $this->contentGateway->copyRelations($contentId, $content->versionInfo->contentInfo->id, $versionNo); + } + + return $content; + } + + /** + * Creates a relation between $sourceContentId in $sourceContentVersionNo + * and $destinationContentId with a specific $type. + * + * @todo Should the existence verifications happen here or is this supposed to be handled at a higher level? + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Relation\CreateStruct $createStruct + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Relation + */ + public function addRelation(RelationCreateStruct $createStruct) + { + $relation = $this->mapper->createRelationFromCreateStruct($createStruct); + + $relation->id = $this->contentGateway->insertRelation($createStruct); + + return $relation; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function loadRelation(int $relationId): Relation + { + return $this->mapper->extractRelationFromRow( + $this->contentGateway->loadRelation($relationId) + ); + } + + /** + * Removes a relation by relation Id. + * + * @todo Should the existence verifications happen here or is this supposed to be handled at a higher level? + * + * @param mixed $relationId + * @param int $type {@see \Ibexa\Contracts\Core\Repository\Values\Content\Relation::COMMON, + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::EMBED, + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::LINK, + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::FIELD} + */ + public function removeRelation($relationId, $type, ?int $destinationContentId = null): void + { + $this->contentGateway->deleteRelation($relationId, $type); + } + + /** + * Loads relations from $sourceContentId. Optionally, loads only those with $type and $sourceContentVersionNo. + * + * @param mixed $sourceContentId Source Content ID + * @param mixed|null $sourceContentVersionNo Source Content Version, null if not specified + * @param int|null $type {@see \Ibexa\Contracts\Core\Repository\Values\Content\Relation::COMMON, + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::EMBED, + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::LINK, + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::FIELD} + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Relation[] + */ + public function loadRelations($sourceContentId, $sourceContentVersionNo = null, $type = null) + { + return $this->mapper->extractRelationsFromRows( + $this->contentGateway->loadRelations($sourceContentId, $sourceContentVersionNo, $type) + ); + } + + public function countRelations(int $sourceContentId, ?int $sourceContentVersionNo = null, ?int $type = null): int + { + return $this->contentGateway->countRelations($sourceContentId, $sourceContentVersionNo, $type); + } + + public function loadRelationList( + int $sourceContentId, + int $limit, + int $offset = 0, + ?int $sourceContentVersionNo = null, + ?int $type = null + ): array { + return $this->mapper->extractRelationsFromRows( + $this->contentGateway->listRelations($sourceContentId, $limit, $offset, $sourceContentVersionNo, $type) + ); + } + + /** + * {@inheritdoc} + */ + public function countReverseRelations(int $destinationContentId, ?int $type = null): int + { + return $this->contentGateway->countReverseRelations($destinationContentId, $type); + } + + /** + * Loads relations from $contentId. Optionally, loads only those with $type. + * + * Only loads relations against published versions. + * + * @param mixed $destinationContentId Destination Content ID + * @param int|null $type {@see \Ibexa\Contracts\Core\Repository\Values\Content\Relation::COMMON, + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::EMBED, + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::LINK, + * \Ibexa\Contracts\Core\Repository\Values\Content\Relation::FIELD} + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Relation[] + */ + public function loadReverseRelations($destinationContentId, $type = null) + { + return $this->mapper->extractRelationsFromRows( + $this->contentGateway->loadReverseRelations($destinationContentId, $type) + ); + } + + /** + * {@inheritdoc} + */ + public function loadReverseRelationList( + int $destinationContentId, + int $offset = 0, + int $limit = -1, + ?int $type = null + ): array { + return $this->mapper->extractRelationsFromRows( + $this->contentGateway->listReverseRelations($destinationContentId, $offset, $limit, $type) + ); + } + + /** + * {@inheritdoc} + */ + public function deleteTranslationFromContent($contentId, $languageCode) + { + $this->fieldHandler->deleteTranslationFromContentFields( + $contentId, + $this->listVersions($contentId), + $languageCode + ); + $this->contentGateway->deleteTranslationFromContent($contentId, $languageCode); + } + + /** + * {@inheritdoc} + */ + public function deleteTranslationFromDraft($contentId, $versionNo, $languageCode) + { + $versionInfo = $this->loadVersionInfo($contentId, $versionNo); + + $this->fieldHandler->deleteTranslationFromVersionFields( + $versionInfo, + $languageCode + ); + $this->contentGateway->deleteTranslationFromVersion( + $contentId, + $versionNo, + $languageCode + ); + + // get all [languageCode => name] entries except the removed Translation + $names = array_filter( + $versionInfo->names, + static function ($lang) use ($languageCode) { + return $lang !== $languageCode; + }, + ARRAY_FILTER_USE_KEY + ); + // set new Content name + foreach ($names as $language => $name) { + $this->contentGateway->setName( + $contentId, + $versionNo, + $name, + $language + ); + } + + // reload entire Version w/o removed Translation + return $this->load($contentId, $versionNo); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function loadVersionInfoList(array $contentIds): array + { + $rows = $this->contentGateway->loadVersionInfoList($contentIds); + + if (empty($rows)) { + return []; + } + + $mappedRows = array_map( + static function (array $row): array { + return [ + 'id' => $row['ezcontentobject_id'], + 'version' => $row['ezcontentobject_version_version'], + ]; + }, + $rows, + ); + + $versionInfoList = $this->mapper->extractVersionInfoListFromRows( + $rows, + $this->contentGateway->loadVersionedNameData($mappedRows) + ); + + $versionInfoListById = []; + foreach ($versionInfoList as $versionInfo) { + $versionInfoListById[$versionInfo->contentInfo->id] = $versionInfo; + } + + return $versionInfoListById; + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Handler'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Language/CachingHandler.php b/src/lib/Persistence/Legacy/Content/Language/CachingHandler.php similarity index 82% rename from eZ/Publish/Core/Persistence/Legacy/Content/Language/CachingHandler.php rename to src/lib/Persistence/Legacy/Content/Language/CachingHandler.php index ec1124b001..6b4e8f16ef 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Language/CachingHandler.php +++ b/src/lib/Persistence/Legacy/Content/Language/CachingHandler.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Language; +namespace Ibexa\Core\Persistence\Legacy\Content\Language; -use eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache; -use eZ\Publish\SPI\Persistence\Content\Language; -use eZ\Publish\SPI\Persistence\Content\Language\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as BaseLanguageHandler; +use Ibexa\Contracts\Core\Persistence\Content\Language; +use Ibexa\Contracts\Core\Persistence\Content\Language\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as BaseLanguageHandler; use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface; +use Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache; /** * Language Handler. @@ -24,14 +24,14 @@ class CachingHandler implements BaseLanguageHandler /** * Inner Language handler. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler + * @var \Ibexa\Core\Persistence\Legacy\Content\Language\Handler */ protected $innerHandler; /** * Language cache. * - * @var \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache + * @var \Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache */ protected $cache; @@ -41,8 +41,8 @@ class CachingHandler implements BaseLanguageHandler /** * Creates a caching handler around $innerHandler. * - * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $innerHandler - * @param \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache $cache + * @param \Ibexa\Contracts\Core\Persistence\Content\Language\Handler $innerHandler + * @param \Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache $cache * @param \Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface $cacheIdentifierGenerator */ public function __construct( @@ -58,9 +58,9 @@ public function __construct( /** * Create a new language. * - * @param \eZ\Publish\SPI\Persistence\Content\Language\CreateStruct $struct + * @param \Ibexa\Contracts\Core\Persistence\Content\Language\CreateStruct $struct * - * @return \eZ\Publish\SPI\Persistence\Content\Language + * @return \Ibexa\Contracts\Core\Persistence\Content\Language */ public function create(CreateStruct $struct) { @@ -73,7 +73,7 @@ public function create(CreateStruct $struct) /** * Update language. * - * @param \eZ\Publish\SPI\Persistence\Content\Language $language + * @param \Ibexa\Contracts\Core\Persistence\Content\Language $language */ public function update(Language $language) { @@ -86,9 +86,9 @@ public function update(Language $language) * * @param mixed $id * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language could not be found by $id + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If language could not be found by $id * - * @return \eZ\Publish\SPI\Persistence\Content\Language + * @return \Ibexa\Contracts\Core\Persistence\Content\Language */ public function load($id) { @@ -146,9 +146,9 @@ public function loadList(array $ids): iterable * * @param string $languageCode * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language could not be found by $languageCode + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If language could not be found by $languageCode * - * @return \eZ\Publish\SPI\Persistence\Content\Language + * @return \Ibexa\Contracts\Core\Persistence\Content\Language */ public function loadByLanguageCode($languageCode) { @@ -192,7 +192,7 @@ public function loadListByLanguageCodes(array $languageCodes): iterable /** * Get all languages. * - * @return \eZ\Publish\SPI\Persistence\Content\Language[] + * @return \Ibexa\Contracts\Core\Persistence\Content\Language[] */ public function loadAll() { @@ -252,3 +252,5 @@ static function (Language $language) use ($generator) { ); } } + +class_alias(CachingHandler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler'); diff --git a/src/lib/Persistence/Legacy/Content/Language/Gateway.php b/src/lib/Persistence/Legacy/Content/Language/Gateway.php new file mode 100644 index 0000000000..1efd27ff30 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Language/Gateway.php @@ -0,0 +1,90 @@ +./src/bundle/Core/Resources/config/storage/legacy/schema.yaml + */ + public const MULTILINGUAL_TABLES_COLUMNS = [ + 'ezcobj_state' => ['language_mask', 'default_language_id'], + 'ezcobj_state_group_language' => ['language_id'], + 'ezcobj_state_group' => ['language_mask', 'default_language_id'], + 'ezcobj_state_language' => ['language_id'], + 'ezcontentclass_attribute_ml' => ['language_id'], + 'ezcontentclass_name' => ['language_id'], + 'ezcontentclass' => ['language_mask', 'initial_language_id'], + 'ezcontentobject_attribute' => ['language_id'], + 'ezcontentobject_name' => ['language_id'], + 'ezcontentobject_version' => ['language_mask', 'initial_language_id'], + 'ezcontentobject' => ['language_mask', 'initial_language_id'], + 'ezurlalias_ml' => ['lang_mask'], + ]; + + /** + * Insert the given $language. + */ + abstract public function insertLanguage(Language $language): int; + + /** + * Update the data of the given $language. + */ + abstract public function updateLanguage(Language $language): void; + + /** + * Load data list for the Language with $ids. + * + * @param int[] $ids + * + * @return string[][]|iterable + */ + abstract public function loadLanguageListData(array $ids): iterable; + + /** + * Load data list for Languages by $languageCodes (eg: eng-GB). + * + * @param string[] $languageCodes + * + * @return string[][]|iterable + */ + abstract public function loadLanguageListDataByLanguageCode(array $languageCodes): iterable; + + /** + * Load the data for all languages. + */ + abstract public function loadAllLanguagesData(): array; + + /** + * Delete the language with $id. + */ + abstract public function deleteLanguage(int $id): void; + + /** + * Check whether a language may be deleted. + */ + abstract public function canDeleteLanguage(int $id): bool; +} + +class_alias(Gateway::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway'); diff --git a/src/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabase.php new file mode 100644 index 0000000000..a16d32c69f --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabase.php @@ -0,0 +1,228 @@ +connection = $connection; + $this->dbPlatform = $this->connection->getDatabasePlatform(); + } + + public function insertLanguage(Language $language): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + $this->dbPlatform->getMaxExpression('id') + ) + ->from(self::CONTENT_LANGUAGE_TABLE); + + $statement = $query->execute(); + + $lastId = (int)$statement->fetchColumn(); + + // Legacy only supports 8 * PHP_INT_SIZE - 2 languages: + // One bit cannot be used because PHP uses signed integers and a second one is reserved for the + // "always available flag". + if ($lastId == (2 ** (8 * PHP_INT_SIZE - 2))) { + throw new RuntimeException('Maximum number of languages reached.'); + } + // Next power of 2 for bit masks + $nextId = ($lastId !== 0 ? $lastId << 1 : 2); + + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::CONTENT_LANGUAGE_TABLE) + ->values( + [ + 'id' => ':id', + 'locale' => ':language_code', + 'name' => ':name', + 'disabled' => ':disabled', + ] + ) + ->setParameter('id', $nextId, ParameterType::INTEGER); + + $this->setLanguageQueryParameters($query, $language); + + $query->execute(); + + return $nextId; + } + + /** + * Set columns for $query based on $language. + */ + private function setLanguageQueryParameters(QueryBuilder $query, Language $language): void + { + $query + ->setParameter('language_code', $language->languageCode, ParameterType::STRING) + ->setParameter('name', $language->name, ParameterType::STRING) + ->setParameter('disabled', (int)!$language->isEnabled, ParameterType::INTEGER); + } + + public function updateLanguage(Language $language): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::CONTENT_LANGUAGE_TABLE) + ->set('locale', ':language_code') + ->set('name', ':name') + ->set('disabled', ':disabled'); + + $this->setLanguageQueryParameters($query, $language); + + $query->where( + $query->expr()->eq( + 'id', + $query->createNamedParameter($language->id, ParameterType::INTEGER, ':id') + ) + ); + + $query->execute(); + } + + public function loadLanguageListData(array $ids): iterable + { + $query = $this->createFindQuery(); + $query + ->where('id IN (:ids)') + ->setParameter('ids', $ids, Connection::PARAM_INT_ARRAY); + + return $query->execute()->fetchAll(); + } + + public function loadLanguageListDataByLanguageCode(array $languageCodes): iterable + { + $query = $this->createFindQuery(); + $query + ->where('locale IN (:locale)') + ->setParameter('locale', $languageCodes, Connection::PARAM_STR_ARRAY); + + return $query->execute()->fetchAll(); + } + + /** + * Build a Language find (fetch) query. + */ + private function createFindQuery(): QueryBuilder + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('id', 'locale', 'name', 'disabled') + ->from(self::CONTENT_LANGUAGE_TABLE); + + return $query; + } + + public function loadAllLanguagesData(): array + { + return $this->createFindQuery()->execute()->fetchAll(); + } + + public function deleteLanguage(int $id): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::CONTENT_LANGUAGE_TABLE) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + + $query->execute(); + } + + public function canDeleteLanguage(int $id): bool + { + // note: at some point this should be delegated to specific gateways + foreach (self::MULTILINGUAL_TABLES_COLUMNS as $tableName => $columns) { + $languageMaskColumn = $columns[0]; + $languageIdColumn = $columns[1] ?? null; + if ( + $this->countTableData($id, $tableName, $languageMaskColumn, $languageIdColumn) > 0 + ) { + return false; + } + } + + return true; + } + + /** + * Count table data rows related to the given language. + * + * @param string|null $languageIdColumn optional column name containing explicit language id + */ + private function countTableData( + int $languageId, + string $tableName, + string $languageMaskColumn, + ?string $languageIdColumn = null + ): int { + $query = $this->connection->createQueryBuilder(); + $query + // avoiding using "*" as count argument, but don't specify column name because it varies + ->select($this->dbPlatform->getCountExpression(1)) + ->from($tableName) + ->where( + $query->expr()->gt( + $this->dbPlatform->getBitAndComparisonExpression( + $languageMaskColumn, + $query->createPositionalParameter($languageId, ParameterType::INTEGER) + ), + 0 + ) + ); + if (null !== $languageIdColumn) { + $query + ->orWhere( + $query->expr()->eq( + $languageIdColumn, + $query->createPositionalParameter($languageId, ParameterType::INTEGER) + ) + ); + } + + return (int)$query->execute()->fetchColumn(); + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/Content/Language/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Content/Language/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..81f3c8fc8b --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Language/Gateway/ExceptionConversion.php @@ -0,0 +1,101 @@ +innerGateway = $innerGateway; + } + + public function insertLanguage(Language $language): int + { + try { + return $this->innerGateway->insertLanguage($language); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateLanguage(Language $language): void + { + try { + $this->innerGateway->updateLanguage($language); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadLanguageListData(array $ids): iterable + { + try { + return $this->innerGateway->loadLanguageListData($ids); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadLanguageListDataByLanguageCode(array $languageCodes): iterable + { + try { + return $this->innerGateway->loadLanguageListDataByLanguageCode($languageCodes); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadAllLanguagesData(): array + { + try { + return $this->innerGateway->loadAllLanguagesData(); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteLanguage(int $id): void + { + try { + $this->innerGateway->deleteLanguage($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function canDeleteLanguage(int $id): bool + { + try { + return $this->innerGateway->canDeleteLanguage($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/Content/Language/Handler.php b/src/lib/Persistence/Legacy/Content/Language/Handler.php new file mode 100644 index 0000000000..a3db53098c --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Language/Handler.php @@ -0,0 +1,167 @@ +languageGateway = $languageGateway; + $this->languageMapper = $languageMapper; + } + + /** + * Create a new language. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Language\CreateStruct $struct + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Language + */ + public function create(CreateStruct $struct) + { + $language = $this->languageMapper->createLanguageFromCreateStruct( + $struct + ); + $language->id = $this->languageGateway->insertLanguage($language); + + return $language; + } + + /** + * Update language. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Language $language + */ + public function update(Language $language) + { + $this->languageGateway->updateLanguage($language); + } + + /** + * Get language by id. + * + * @param mixed $id + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If language could not be found by $id + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Language + */ + public function load($id) + { + $languages = $this->languageMapper->extractLanguagesFromRows( + $this->languageGateway->loadLanguageListData([$id]) + ); + + if (count($languages) < 1) { + throw new NotFoundException('Language', $id); + } + + return reset($languages); + } + + /** + * {@inheritdoc} + */ + public function loadList(array $ids): iterable + { + return $this->languageMapper->extractLanguagesFromRows( + $this->languageGateway->loadLanguageListData($ids), + 'id' + ); + } + + /** + * Get language by Language Code (eg: eng-GB). + * + * @param string $languageCode + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If language could not be found by $languageCode + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Language + */ + public function loadByLanguageCode($languageCode) + { + $languages = $this->languageMapper->extractLanguagesFromRows( + $this->languageGateway->loadLanguageListDataByLanguageCode([$languageCode]) + ); + + if (count($languages) < 1) { + throw new NotFoundException('Language', $languageCode); + } + + return reset($languages); + } + + /** + * {@inheritdoc} + */ + public function loadListByLanguageCodes(array $languageCodes): iterable + { + return $this->languageMapper->extractLanguagesFromRows( + $this->languageGateway->loadLanguageListDataByLanguageCode($languageCodes) + ); + } + + /** + * Get all languages. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Language[] + */ + public function loadAll() + { + return $this->languageMapper->extractLanguagesFromRows( + $this->languageGateway->loadAllLanguagesData() + ); + } + + /** + * Delete a language. + * + * @param mixed $id + * + * @throws \LogicException If language could not be deleted + */ + public function delete($id) + { + if (!$this->languageGateway->canDeleteLanguage($id)) { + throw new LogicException('Cannot delete language: some content still references the language'); + } + + $this->languageGateway->deleteLanguage($id); + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler'); diff --git a/src/lib/Persistence/Legacy/Content/Language/Mapper.php b/src/lib/Persistence/Legacy/Content/Language/Mapper.php new file mode 100644 index 0000000000..ec3bc16224 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Language/Mapper.php @@ -0,0 +1,62 @@ +languageCode = $struct->languageCode; + $language->name = $struct->name; + $language->isEnabled = $struct->isEnabled; + + return $language; + } + + /** + * Extracts Language objects from $rows. + * + * @param array $rows + * @param string $key Column name for use as key in returned array. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Language[] + */ + public function extractLanguagesFromRows(array $rows, string $key = 'locale'): array + { + $languages = []; + + foreach ($rows as $row) { + $language = new Language(); + + $language->id = (int)$row['id']; + $language->languageCode = $row['locale']; + $language->name = $row['name']; + $language->isEnabled = !((int)$row['disabled']); + + $languages[$row[$key]] = $language; + } + + return $languages; + } +} + +class_alias(Mapper::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Language\Mapper'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Language/MaskGenerator.php b/src/lib/Persistence/Legacy/Content/Language/MaskGenerator.php similarity index 87% rename from eZ/Publish/Core/Persistence/Legacy/Content/Language/MaskGenerator.php rename to src/lib/Persistence/Legacy/Content/Language/MaskGenerator.php index 232c8d1f07..c51c84f85f 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Language/MaskGenerator.php +++ b/src/lib/Persistence/Legacy/Content/Language/MaskGenerator.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Language; +namespace Ibexa\Core\Persistence\Legacy\Content\Language; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler; +use Ibexa\Core\Base\Exceptions\NotFoundException; /** * Language MaskGenerator. @@ -17,14 +17,14 @@ class MaskGenerator /** * Language lookup. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler + * @var \Ibexa\Core\Persistence\Legacy\Content\Language\Handler */ protected $languageHandler; /** * Creates a new Language MaskGenerator. * - * @param \eZ\Publish\SPI\Persistence\Content\Language\Handler $languageHandler + * @param \Ibexa\Contracts\Core\Persistence\Content\Language\Handler $languageHandler */ public function __construct(LanguageHandler $languageHandler) { @@ -36,7 +36,7 @@ public function __construct(LanguageHandler $languageHandler) * * @deprecated Move towards using {@see generateLanguageMaskFromLanguageCodes()} or the other generate* methods. * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language(s) in $languageCodes was not be found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If language(s) in $languageCodes was not be found * * @param array $languages * @@ -91,7 +91,7 @@ public function generateLanguageMaskFromLanguageIds(array $languageIds, $alwaysA * * @return int * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function generateLanguageIndicator($languageCode, $alwaysAvailable) { @@ -205,7 +205,7 @@ public function isLanguageMaskComposite($languageMask): bool /** * Generates a language mask from plain array of language codes and always available flag. * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language(s) in $languageCodes was not be found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If language(s) in $languageCodes was not be found * * @param string[] $languageCodes * @param bool $isAlwaysAvailable @@ -231,9 +231,9 @@ public function generateLanguageMaskFromLanguageCodes(array $languageCodes, bool /** * Collect all translations of the given Persistence Fields and generate language mask. * - * @param \eZ\Publish\SPI\Persistence\Content\Field[] $fields + * @param \Ibexa\Contracts\Core\Persistence\Content\Field[] $fields * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function generateLanguageMaskForFields( array $fields, @@ -255,3 +255,5 @@ public function generateLanguageMaskForFields( ); } } + +class_alias(MaskGenerator::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator'); diff --git a/src/lib/Persistence/Legacy/Content/Location/Gateway.php b/src/lib/Persistence/Legacy/Content/Location/Gateway.php new file mode 100644 index 0000000000..57e7838eeb --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Location/Gateway.php @@ -0,0 +1,372 @@ +connection = $connection; + $this->dbPlatform = $this->connection->getDatabasePlatform(); + $this->languageMaskGenerator = $languageMaskGenerator; + $this->trashCriteriaConverter = $trashCriteriaConverter; + $this->trashSortClauseConverter = $trashSortClauseConverter; + } + + public function getBasicNodeData( + int $nodeId, + array $translations = null, + bool $useAlwaysAvailable = true + ): array { + $query = $this->createNodeQueryBuilder(['t.*'], $translations, $useAlwaysAvailable); + $query->andWhere( + $query->expr()->eq('t.node_id', $query->createNamedParameter($nodeId, ParameterType::INTEGER)) + ); + + if ($row = $query->execute()->fetch(FetchMode::ASSOCIATIVE)) { + return $row; + } + + throw new NotFound('location', $nodeId); + } + + public function getNodeDataList(array $locationIds, array $translations = null, bool $useAlwaysAvailable = true): iterable + { + $query = $this->createNodeQueryBuilder(['t.*'], $translations, $useAlwaysAvailable); + $query->andWhere( + $query->expr()->in( + 't.node_id', + $query->createNamedParameter($locationIds, Connection::PARAM_INT_ARRAY) + ) + ); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function getBasicNodeDataByRemoteId( + string $remoteId, + array $translations = null, + bool $useAlwaysAvailable = true + ): array { + $query = $this->createNodeQueryBuilder(['t.*'], $translations, $useAlwaysAvailable); + $query->andWhere( + $query->expr()->eq('t.remote_id', $query->createNamedParameter($remoteId, ParameterType::STRING)) + ); + + if ($row = $query->execute()->fetch(FetchMode::ASSOCIATIVE)) { + return $row; + } + + throw new NotFound('location', $remoteId); + } + + public function loadLocationDataByContent(int $contentId, ?int $rootLocationId = null): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('*') + ->from(self::CONTENT_TREE_TABLE, 't') + ->where( + $query->expr()->eq( + 't.contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ); + + if ($rootLocationId !== null) { + $query + ->andWhere( + $this->getSubtreeLimitationExpression($query, $rootLocationId) + ) + ; + } + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + /** + * {@inheritdoc} + */ + public function loadLocationDataByTrashContent(int $contentId, ?int $rootLocationId = null): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('*') + ->from($this->connection->quoteIdentifier('ezcontentobject_trash'), 't') + ->where('t.contentobject_id = :contentobject_id') + ->setParameter('contentobject_id', $contentId, ParameterType::INTEGER); + + if ($rootLocationId !== null) { + $query + ->andWhere( + $this->getSubtreeLimitationExpression($query, $rootLocationId) + ) + ; + } + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadParentLocationsDataForDraftContent(int $contentId): array + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select('DISTINCT t.*') + ->from(self::CONTENT_TREE_TABLE, 't') + ->innerJoin( + 't', + 'eznode_assignment', + 'a', + $expr->andX( + $expr->eq( + 't.node_id', + 'a.parent_node' + ), + $expr->eq( + 'a.contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ), + $expr->eq( + 'a.op_code', + $query->createPositionalParameter( + self::NODE_ASSIGNMENT_OP_CODE_CREATE, + ParameterType::INTEGER + ) + ) + ) + ) + ->innerJoin( + 'a', + 'ezcontentobject', + 'c', + $expr->andX( + $expr->eq( + 'a.contentobject_id', + 'c.id' + ), + $expr->eq( + 'c.status', + $query->createPositionalParameter( + ContentInfo::STATUS_DRAFT, + ParameterType::INTEGER + ) + ) + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function getSubtreeContent(int $sourceId, bool $onlyIds = false): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select($onlyIds ? 'node_id, contentobject_id, depth' : '*') + ->from(self::CONTENT_TREE_TABLE, 't') + ->where($this->getSubtreeLimitationExpression($query, $sourceId)) + ->orderBy('t.depth') + ->addOrderBy('t.node_id'); + $statement = $query->execute(); + + $results = $statement->fetchAll($onlyIds ? (FetchMode::COLUMN | PDO::FETCH_GROUP) : FetchMode::ASSOCIATIVE); + // array_map() is used to map all elements stored as $results[$i][0] to $results[$i] + return $onlyIds + ? array_map(static function (array $result) { + return $result[0]; + }, $results) + : $results; + } + + public function getSubtreeSize(string $path): int + { + $query = $this->createNodeQueryBuilder([$this->dbPlatform->getCountExpression('node_id')]); + $query->andWhere( + $query->expr()->like( + 't.path_string', + $query->createPositionalParameter( + $path . '%', + ) + ) + ); + + return (int) $query->execute()->fetchOne(); + } + + /** + * Return constraint which limits the given $query to the subtree starting at $rootLocationId. + */ + private function getSubtreeLimitationExpression( + QueryBuilder $query, + int $rootLocationId + ): string { + return $query->expr()->like( + 't.path_string', + $query->createPositionalParameter( + '%/' . ((string)$rootLocationId) . '/%', + ParameterType::STRING + ) + ); + } + + public function getChildren(int $locationId): array + { + $query = $this->connection->createQueryBuilder(); + $query->select('*')->from( + self::CONTENT_TREE_TABLE + )->where( + $query->expr()->eq( + 'ezcontentobject_tree.parent_node_id', + $query->createPositionalParameter($locationId, ParameterType::INTEGER) + ) + ); + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + private function getSubtreeNodesData(string $pathString): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + 'node_id', + 'parent_node_id', + 'path_string', + 'path_identification_string', + 'is_hidden' + ) + ->from(self::CONTENT_TREE_TABLE) + ->where( + $query->expr()->like( + 'path_string', + $query->createPositionalParameter($pathString . '%', ParameterType::STRING) + ) + ); + $statement = $query->execute(); + + return $statement->fetchAll(); + } + + /** + * @throws \Doctrine\DBAL\Exception + * @throws \Doctrine\DBAL\Driver\Exception + */ + public function moveSubtreeNodes(array $sourceNodeData, array $destinationNodeData): void + { + $fromPathString = $sourceNodeData['path_string']; + $contentObjectId = $sourceNodeData['contentobject_id']; + + $rows = $this->getSubtreeNodesData($fromPathString); + + $oldParentPathString = implode('/', array_slice(explode('/', $fromPathString), 0, -2)) . '/'; + $oldParentPathIdentificationString = implode( + '/', + array_slice(explode('/', $sourceNodeData['path_identification_string']), 0, -1) + ); + + $hiddenNodeIds = $this->getHiddenNodeIds($contentObjectId); + foreach ($rows as $row) { + // Prefixing ensures correct replacement when old parent is root node + $newPathString = str_replace( + 'prefix' . $oldParentPathString, + $destinationNodeData['path_string'], + 'prefix' . $row['path_string'] + ); + $replace = rtrim($destinationNodeData['path_identification_string'], '/'); + if (empty($oldParentPathIdentificationString)) { + $replace .= '/'; + } + $newPathIdentificationString = str_replace( + 'prefix' . $oldParentPathIdentificationString, + $replace, + 'prefix' . $row['path_identification_string'] + ); + $newParentId = $row['parent_node_id']; + if ($row['path_string'] === $fromPathString) { + $newParentId = (int)implode('', array_slice(explode('/', $newPathString), -3, 1)); + } + + $this->moveSingleSubtreeNode( + (int)$row['node_id'], + $sourceNodeData, + $destinationNodeData, + $newPathString, + $newPathIdentificationString, + $newParentId, + $hiddenNodeIds + ); + } + } + + /** + * @param int $contentObjectId + * + * @return int[] + * + * @throws \Doctrine\DBAL\Driver\Exception + * @throws \Doctrine\DBAL\Exception + */ + private function getHiddenNodeIds(int $contentObjectId): array + { + $query = $this->buildHiddenSubtreeQuery('node_id'); + $expr = $query->expr(); + $query + ->andWhere( + $expr->eq( + 'id', + $query->createPositionalParameter( + $contentObjectId, + ParameterType::INTEGER + ) + ) + ); + $statement = $query->execute(); + + $result = $statement->fetchFirstColumn(); + + return array_map('intval', $result); + } + + /** + * @param int[] $hiddenNodeIds + */ + private function isHiddenByParentOrSelf(string $pathString, array $hiddenNodeIds): bool + { + $parentNodeIds = array_map('intval', explode('/', trim($pathString, '/'))); + foreach ($parentNodeIds as $parentNodeId) { + if (in_array($parentNodeId, $hiddenNodeIds, true)) { + return true; + } + } + + return false; + } + + /** + * @param array $sourceNodeData + * @param array $destinationNodeData + * @param int[] $hiddenNodeIds + */ + private function moveSingleSubtreeNode( + int $nodeId, + array $sourceNodeData, + array $destinationNodeData, + string $newPathString, + string $newPathIdentificationString, + int $newParentId, + array $hiddenNodeIds + ): void { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::CONTENT_TREE_TABLE) + ->set( + 'path_string', + $query->createPositionalParameter($newPathString, ParameterType::STRING) + ) + ->set( + 'path_identification_string', + $query->createPositionalParameter( + $newPathIdentificationString, + ParameterType::STRING + ) + ) + ->set( + 'depth', + $query->createPositionalParameter( + substr_count($newPathString, '/') - 2, + ParameterType::INTEGER + ) + ) + ->set( + 'parent_node_id', + $query->createPositionalParameter($newParentId, ParameterType::INTEGER) + ); + + if ($destinationNodeData['is_hidden'] || $destinationNodeData['is_invisible']) { + // CASE 1: Mark whole tree as invisible if destination is invisible and/or hidden + $query->set( + 'is_invisible', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ); + } elseif (!$sourceNodeData['is_hidden'] && $sourceNodeData['is_invisible']) { + // CASE 2: source is only invisible, we will need to re-calculate whole moved tree visibility + $query->set( + 'is_invisible', + $query->createPositionalParameter( + $this->isHiddenByParentOrSelf($newPathString, $hiddenNodeIds) ? 1 : 0, + ParameterType::INTEGER + ) + ); + } + + $query->where( + $query->expr()->eq( + 'node_id', + $query->createPositionalParameter($nodeId, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + public function updateSubtreeModificationTime(string $pathString, ?int $timestamp = null): void + { + $nodes = array_filter(explode('/', $pathString)); + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::CONTENT_TREE_TABLE) + ->set( + 'modified_subnode', + $query->createPositionalParameter( + $timestamp ?: time(), + ParameterType::INTEGER + ) + ) + ->where( + $query->expr()->in( + 'node_id', + $nodes + ) + ); + $query->execute(); + } + + public function hideSubtree(string $pathString): void + { + $this->setNodeWithChildrenInvisible($pathString); + $this->setNodeHidden($pathString); + } + + public function setNodeWithChildrenInvisible(string $pathString): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::CONTENT_TREE_TABLE) + ->set( + 'is_invisible', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ) + ->set( + 'modified_subnode', + $query->createPositionalParameter(time(), ParameterType::INTEGER) + ) + ->where( + $query->expr()->like( + 'path_string', + $query->createPositionalParameter($pathString . '%', ParameterType::STRING) + ) + ); + + $query->execute(); + } + + public function setNodeHidden(string $pathString): void + { + $this->setNodeHiddenStatus($pathString, true); + } + + private function setNodeHiddenStatus(string $pathString, bool $isHidden): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::CONTENT_TREE_TABLE) + ->set( + 'is_hidden', + $query->createPositionalParameter((int) $isHidden, ParameterType::INTEGER) + ) + ->where( + $query->expr()->eq( + 'path_string', + $query->createPositionalParameter($pathString, ParameterType::STRING) + ) + ); + + $query->execute(); + } + + public function unHideSubtree(string $pathString): void + { + $this->setNodeUnhidden($pathString); + $this->setNodeWithChildrenVisible($pathString); + } + + public function setNodeWithChildrenVisible(string $pathString): void + { + // Check if any parent nodes are explicitly hidden + if ($this->isAnyNodeInPathExplicitlyHidden($pathString)) { + // There are parent nodes set hidden, so that we can skip marking + // something visible again. + return; + } + + // Find nodes of explicitly hidden subtrees in the subtree which + // should remain unhidden + $hiddenSubtrees = $this->loadHiddenSubtreesByPath($pathString); + + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->update(self::CONTENT_TREE_TABLE) + ->set( + 'is_invisible', + $query->createPositionalParameter(0, ParameterType::INTEGER) + ) + ->set( + 'modified_subnode', + $query->createPositionalParameter(time(), ParameterType::INTEGER) + ); + + // Build where expression selecting the nodes, which should not be made hidden + $query + ->where( + $expr->like( + 'path_string', + $query->createPositionalParameter($pathString . '%', ParameterType::STRING) + ) + ); + if (count($hiddenSubtrees) > 0) { + foreach ($hiddenSubtrees as $subtreePathString) { + $query + ->andWhere( + $expr->notLike( + 'path_string', + $query->createPositionalParameter( + $subtreePathString . '%', + ParameterType::STRING + ) + ) + ); + } + } + + $query->execute(); + } + + private function isAnyNodeInPathExplicitlyHidden(string $pathString): bool + { + $query = $this->buildHiddenSubtreeQuery( + $this->dbPlatform->getCountExpression('path_string') + ); + $expr = $query->expr(); + $query + ->andWhere( + $expr->in( + 't.node_id', + $query->createPositionalParameter( + array_filter(explode('/', $pathString)), + Connection::PARAM_INT_ARRAY + ) + ) + ); + $count = (int)$query->execute()->fetchColumn(); + + return $count > 0; + } + + /** + * @return array list of path strings + */ + private function loadHiddenSubtreesByPath(string $pathString): array + { + $query = $this->buildHiddenSubtreeQuery('path_string'); + $expr = $query->expr(); + $query + ->andWhere( + $expr->like( + 'path_string', + $query->createPositionalParameter( + $pathString . '%', + ParameterType::STRING + ) + ) + ); + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::COLUMN); + } + + private function buildHiddenSubtreeQuery(string $selectExpr): QueryBuilder + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select($selectExpr) + ->from(self::CONTENT_TREE_TABLE, 't') + ->leftJoin('t', 'ezcontentobject', 'c', 't.contentobject_id = c.id') + ->where( + $expr->orX( + $expr->eq( + 't.is_hidden', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ), + $expr->eq( + 'c.is_hidden', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ) + ) + ); + + return $query; + } + + public function setNodeUnhidden(string $pathString): void + { + $this->setNodeHiddenStatus($pathString, false); + } + + public function swap(int $locationId1, int $locationId2): bool + { + $queryBuilder = $this->connection->createQueryBuilder(); + $expr = $queryBuilder->expr(); + $queryBuilder + ->select('node_id', 'main_node_id', 'contentobject_id', 'contentobject_version') + ->from(self::CONTENT_TREE_TABLE) + ->where( + $expr->in( + 'node_id', + ':locationIds' + ) + ) + ->setParameter('locationIds', [$locationId1, $locationId2], Connection::PARAM_INT_ARRAY) + ; + $statement = $queryBuilder->execute(); + $contentObjects = []; + foreach ($statement->fetchAll(FetchMode::ASSOCIATIVE) as $row) { + $row['is_main_node'] = (int)$row['main_node_id'] === (int)$row['node_id']; + $contentObjects[$row['node_id']] = $row; + } + + if (!isset($contentObjects[$locationId1], $contentObjects[$locationId2])) { + throw new RuntimeException( + sprintf( + '%s: failed to fetch either Location %d or Location %d', + __METHOD__, + $locationId1, + $locationId2 + ) + ); + } + $content1data = $contentObjects[$locationId1]; + $content2data = $contentObjects[$locationId2]; + + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder + ->update(self::CONTENT_TREE_TABLE) + ->set('contentobject_id', ':contentId') + ->set('contentobject_version', ':versionNo') + ->set('main_node_id', ':mainNodeId') + ->where( + $expr->eq('node_id', ':locationId') + ); + + $queryBuilder + ->setParameter(':contentId', $content2data['contentobject_id']) + ->setParameter(':versionNo', $content2data['contentobject_version']) + ->setParameter( + ':mainNodeId', + // make main Location main again, preserve main Location id of non-main one + $content2data['is_main_node'] + ? $content1data['node_id'] + : $content2data['main_node_id'] + ) + ->setParameter('locationId', $locationId1); + + // update Location 1 entry + $queryBuilder->execute(); + + $queryBuilder + ->setParameter(':contentId', $content1data['contentobject_id']) + ->setParameter(':versionNo', $content1data['contentobject_version']) + ->setParameter( + ':mainNodeId', + $content1data['is_main_node'] + // make main Location main again, preserve main Location id of non-main one + ? $content2data['node_id'] + : $content1data['main_node_id'] + ) + ->setParameter('locationId', $locationId2); + + // update Location 2 entry + $queryBuilder->execute(); + + return true; + } + + public function create(CreateStruct $createStruct, array $parentNode): Location + { + $location = $this->insertLocationIntoContentTree($createStruct, $parentNode); + + $mainLocationId = $createStruct->mainLocationId === true ? $location->id : $createStruct->mainLocationId; + $location->pathString = $parentNode['path_string'] . $location->id . '/'; + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::CONTENT_TREE_TABLE) + ->set( + 'path_string', + $query->createPositionalParameter($location->pathString, ParameterType::STRING) + ) + ->set( + 'main_node_id', + $query->createPositionalParameter($mainLocationId, ParameterType::INTEGER) + ) + ->where( + $query->expr()->eq( + 'node_id', + $query->createPositionalParameter($location->id, ParameterType::INTEGER) + ) + ); + + $query->execute(); + + return $location; + } + + public function createNodeAssignment( + CreateStruct $createStruct, + int $parentNodeId, + int $type = self::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP + ): void { + $isMain = ($createStruct->mainLocationId === true ? 1 : 0); + + $query = $this->connection->createQueryBuilder(); + $query + ->insert('eznode_assignment') + ->values( + [ + 'contentobject_id' => ':contentobject_id', + 'contentobject_version' => ':contentobject_version', + 'from_node_id' => ':from_node_id', + 'is_main' => ':is_main', + 'op_code' => ':op_code', + 'parent_node' => ':parent_node', + 'parent_remote_id' => ':parent_remote_id', + 'remote_id' => ':remote_id', + 'sort_field' => ':sort_field', + 'sort_order' => ':sort_order', + 'priority' => ':priority', + 'is_hidden' => ':is_hidden', + ] + ) + ->setParameters( + [ + 'contentobject_id' => $createStruct->contentId, + 'contentobject_version' => $createStruct->contentVersion, + // from_node_id: unused field + 'from_node_id' => 0, + // is_main: changed by the business layer, later + 'is_main' => $isMain, + 'op_code' => $type, + 'parent_node' => $parentNodeId, + // parent_remote_id column should contain the remote id of the corresponding Location + 'parent_remote_id' => $createStruct->remoteId, + // remote_id column should contain the remote id of the node assignment itself, + // however this was never implemented completely in Legacy Stack, so we just set + // it to default value '0' + 'remote_id' => '0', + 'sort_field' => $createStruct->sortField, + 'sort_order' => $createStruct->sortOrder, + 'priority' => $createStruct->priority, + 'is_hidden' => $createStruct->hidden, + ], + [ + 'contentobject_id' => ParameterType::INTEGER, + 'contentobject_version' => ParameterType::INTEGER, + 'from_node_id' => ParameterType::INTEGER, + 'is_main' => ParameterType::INTEGER, + 'op_code' => ParameterType::INTEGER, + 'parent_node' => ParameterType::INTEGER, + 'parent_remote_id' => ParameterType::STRING, + 'remote_id' => ParameterType::STRING, + 'sort_field' => ParameterType::INTEGER, + 'sort_order' => ParameterType::INTEGER, + 'priority' => ParameterType::INTEGER, + 'is_hidden' => ParameterType::INTEGER, + ] + ); + $query->execute(); + } + + public function deleteNodeAssignment(int $contentId, ?int $versionNo = null): void + { + $query = $this->connection->createQueryBuilder(); + $query->delete( + 'eznode_assignment' + )->where( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ); + if (isset($versionNo)) { + $query->andWhere( + $query->expr()->eq( + 'contentobject_version', + $query->createPositionalParameter($versionNo, ParameterType::INTEGER) + ) + ); + } + $query->execute(); + } + + public function updateNodeAssignment( + int $contentObjectId, + int $oldParent, + int $newParent, + int $opcode + ): void { + $query = $this->connection->createQueryBuilder(); + $query + ->update('eznode_assignment') + ->set( + 'parent_node', + $query->createPositionalParameter($newParent, ParameterType::INTEGER) + ) + ->set( + 'op_code', + $query->createPositionalParameter($opcode, ParameterType::INTEGER) + ) + ->where( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter( + $contentObjectId, + ParameterType::INTEGER + ) + ) + ) + ->andWhere( + $query->expr()->eq( + 'parent_node', + $query->createPositionalParameter( + $oldParent, + ParameterType::INTEGER + ) + ) + ); + $query->execute(); + } + + public function createLocationsFromNodeAssignments(int $contentId, int $versionNo): void + { + // select all node assignments with OP_CODE_CREATE (3) for this content + $query = $this->connection->createQueryBuilder(); + $query + ->select('*') + ->from('eznode_assignment') + ->where( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ) + ->andWhere( + $query->expr()->eq( + 'contentobject_version', + $query->createPositionalParameter($versionNo, ParameterType::INTEGER) + ) + ) + ->andWhere( + $query->expr()->eq( + 'op_code', + $query->createPositionalParameter( + self::NODE_ASSIGNMENT_OP_CODE_CREATE, + ParameterType::INTEGER + ) + ) + ) + ->orderBy('id'); + $statement = $query->execute(); + + // convert all these assignments to nodes + + while ($row = $statement->fetch(FetchMode::ASSOCIATIVE)) { + $isMain = (bool)$row['is_main']; + // set null for main to indicate that new Location ID is required + $mainLocationId = $isMain ? null : $this->getMainNodeId($contentId); + + $parentLocationData = $this->getBasicNodeData((int)$row['parent_node']); + $isInvisible = $row['is_hidden'] || $parentLocationData['is_hidden'] || $parentLocationData['is_invisible']; + $this->create( + new CreateStruct( + [ + 'contentId' => $row['contentobject_id'], + 'contentVersion' => $row['contentobject_version'], + // BC layer: for CreateStruct "true" means that a main Location should be created + 'mainLocationId' => $mainLocationId ?? true, + 'remoteId' => $row['parent_remote_id'], + 'sortField' => $row['sort_field'], + 'sortOrder' => $row['sort_order'], + 'priority' => $row['priority'], + 'hidden' => $row['is_hidden'], + 'invisible' => $isInvisible, + ] + ), + $parentLocationData + ); + + $this->updateNodeAssignment( + (int)$row['contentobject_id'], + (int)$row['parent_node'], + (int)$row['parent_node'], + self::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP + ); + } + } + + public function updateLocationsContentVersionNo(int $contentId, int $versionNo): void + { + $query = $this->connection->createQueryBuilder(); + $query->update( + self::CONTENT_TREE_TABLE + )->set( + 'contentobject_version', + $query->createPositionalParameter($versionNo, ParameterType::INTEGER) + )->where( + $query->expr()->eq( + 'contentobject_id', + $contentId + ) + ); + $query->execute(); + } + + /** + * Search for the main nodeId of $contentId. + */ + private function getMainNodeId(int $contentId): ?int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('node_id') + ->from(self::CONTENT_TREE_TABLE) + ->where( + $query->expr()->andX( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'node_id', + 'main_node_id' + ) + ) + ); + $statement = $query->execute(); + + $result = $statement->fetchColumn(); + + return false !== $result ? (int)$result : null; + } + + /** + * Updates an existing location. + * + * Will not throw anything if location id is invalid or no entries are affected. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Location\UpdateStruct $location + * @param int $locationId + */ + public function update(UpdateStruct $location, $locationId): void + { + $query = $this->connection->createQueryBuilder(); + + $query + ->update(self::CONTENT_TREE_TABLE) + ->set( + 'priority', + $query->createPositionalParameter($location->priority, ParameterType::INTEGER) + ) + ->set( + 'remote_id', + $query->createPositionalParameter($location->remoteId, ParameterType::STRING) + ) + ->set( + 'sort_order', + $query->createPositionalParameter($location->sortOrder, ParameterType::INTEGER) + ) + ->set( + 'sort_field', + $query->createPositionalParameter($location->sortField, ParameterType::INTEGER) + ) + ->where( + $query->expr()->eq( + 'node_id', + $locationId + ) + ); + $query->execute(); + } + + public function updatePathIdentificationString($locationId, $parentLocationId, $text): void + { + $parentData = $this->getBasicNodeData($parentLocationId); + + $newPathIdentificationString = empty($parentData['path_identification_string']) ? + $text : + $parentData['path_identification_string'] . '/' . $text; + + $query = $this->connection->createQueryBuilder(); + $query->update( + self::CONTENT_TREE_TABLE + )->set( + 'path_identification_string', + $query->createPositionalParameter($newPathIdentificationString, ParameterType::STRING) + )->where( + $query->expr()->eq( + 'node_id', + $query->createPositionalParameter($locationId, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + /** + * Deletes ezcontentobject_tree row for given $locationId (node_id). + * + * @param mixed $locationId + */ + public function removeLocation($locationId): void + { + $query = $this->connection->createQueryBuilder(); + $query->delete( + self::CONTENT_TREE_TABLE + )->where( + $query->expr()->eq( + 'node_id', + $query->createPositionalParameter($locationId, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + /** + * Return data of the next in line node to be set as a new main node. + * + * This returns lowest node id for content identified by $contentId, and not of + * the node identified by given $locationId (current main node). + * Assumes that content has more than one location. + * + * @param mixed $contentId + * @param mixed $locationId + * + * @return array + */ + public function getFallbackMainNodeData($contentId, $locationId): array + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select( + 'node_id', + 'contentobject_version', + 'parent_node_id' + ) + ->from(self::CONTENT_TREE_TABLE) + ->where( + $expr->eq( + 'contentobject_id', + $query->createPositionalParameter( + $contentId, + ParameterType::INTEGER + ) + ) + ) + ->andWhere( + $expr->neq( + 'node_id', + $query->createPositionalParameter( + $locationId, + ParameterType::INTEGER + ) + ) + ) + ->orderBy('node_id', 'ASC') + ->setMaxResults(1); + + $statement = $query->execute(); + + return $statement->fetch(FetchMode::ASSOCIATIVE); + } + + public function trashLocation(int $locationId): void + { + $locationRow = $this->getBasicNodeData($locationId); + + $query = $this->connection->createQueryBuilder(); + $query->insert('ezcontentobject_trash'); + + unset($locationRow['contentobject_is_published']); + $locationRow['trashed'] = time(); + foreach ($locationRow as $key => $value) { + $query->setValue($key, $query->createPositionalParameter($value)); + } + + $query->execute(); + + $this->removeLocation($locationRow['node_id']); + $this->setContentStatus((int)$locationRow['contentobject_id'], ContentInfo::STATUS_TRASHED); + } + + public function untrashLocation(int $locationId, ?int $newParentId = null): Location + { + $row = $this->loadTrashByLocation($locationId); + + $newLocation = $this->create( + new CreateStruct( + [ + 'priority' => $row['priority'], + 'hidden' => $row['is_hidden'], + 'invisible' => $row['is_invisible'], + 'remoteId' => $row['remote_id'], + 'contentId' => $row['contentobject_id'], + 'contentVersion' => $row['contentobject_version'], + 'mainLocationId' => true, // Restored location is always main location + 'sortField' => $row['sort_field'], + 'sortOrder' => $row['sort_order'], + ] + ), + $this->getBasicNodeData($newParentId ?? (int)$row['parent_node_id']) + ); + + $this->removeElementFromTrash($locationId); + $this->setContentStatus((int)$row['contentobject_id'], ContentInfo::STATUS_PUBLISHED); + + return $newLocation; + } + + private function setContentStatus(int $contentId, int $status): void + { + $query = $this->connection->createQueryBuilder(); + $query->update( + 'ezcontentobject' + )->set( + 'status', + $query->createPositionalParameter($status, ParameterType::INTEGER) + )->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + public function loadTrashByLocation(int $locationId): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('*') + ->from('ezcontentobject_trash') + ->where( + $query->expr()->eq( + 'node_id', + $query->createPositionalParameter($locationId, ParameterType::INTEGER) + ) + ); + $statement = $query->execute(); + + if ($row = $statement->fetch(FetchMode::ASSOCIATIVE)) { + return $row; + } + + throw new NotFound('trash', $locationId); + } + + public function listTrashed( + int $offset, + ?int $limit, + array $sort = null, + ?Criterion $criterion = null + ): array { + $query = $this->connection->createQueryBuilder(); + $query + ->select('t.*') + ->from(self::TRASH_TABLE, 't') + ->leftJoin('t', ContentGateway::CONTENT_ITEM_TABLE, 'c', 't.contentobject_id = c.id'); + + $this->addSort($sort, $query); + $this->addConditionsByCriterion($criterion, $query); + + if ($limit !== null) { + $query->setMaxResults($limit); + $query->setFirstResult($offset); + } + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function countTrashed(?Criterion $criterion = null): int + { + $query = $this->connection->createQueryBuilder() + ->select($this->dbPlatform->getCountExpression(1)) + ->from(self::TRASH_TABLE, 't') + ->innerJoin('t', ContentGateway::CONTENT_ITEM_TABLE, 'c', 't.contentobject_id = c.id'); + + $this->addConditionsByCriterion($criterion, $query); + + return (int)$query->execute()->fetchColumn(); + } + + /** + * Removes every entries in the trash. + * Will NOT remove associated content objects nor attributes. + * + * Basically truncates ezcontentobject_trash table. + */ + public function cleanupTrash(): void + { + $query = $this->connection->createQueryBuilder(); + $query->delete('ezcontentobject_trash'); + $query->execute(); + } + + public function removeElementFromTrash(int $id): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete('ezcontentobject_trash') + ->where( + $query->expr()->eq( + 'node_id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + public function setSectionForSubtree(string $pathString, int $sectionId): bool + { + $selectContentIdsQuery = $this->connection->createQueryBuilder(); + $selectContentIdsQuery + ->select('t.contentobject_id') + ->from(self::CONTENT_TREE_TABLE, 't') + ->where( + $selectContentIdsQuery->expr()->like( + 't.path_string', + $selectContentIdsQuery->createPositionalParameter("{$pathString}%") + ) + ); + + $contentIds = array_map( + 'intval', + $selectContentIdsQuery->execute()->fetchAll(FetchMode::COLUMN) + ); + + if (empty($contentIds)) { + return false; + } + + $updateSectionQuery = $this->connection->createQueryBuilder(); + $updateSectionQuery + ->update('ezcontentobject') + ->set( + 'section_id', + $updateSectionQuery->createPositionalParameter($sectionId, ParameterType::INTEGER) + ) + ->where( + $updateSectionQuery->expr()->in( + 'id', + $contentIds + ) + ); + $affectedRows = $updateSectionQuery->execute(); + + return $affectedRows > 0; + } + + public function countLocationsByContentId(int $contentId): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + $this->dbPlatform->getCountExpression('*') + ) + ->from(self::CONTENT_TREE_TABLE) + ->where( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ); + $stmt = $query->execute(); + + return (int)$stmt->fetchColumn(); + } + + public function changeMainLocation( + int $contentId, + int $locationId, + int $versionNo, + int $parentLocationId + ): void { + // Update ezcontentobject_tree table + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::CONTENT_TREE_TABLE) + ->set( + 'main_node_id', + $query->createPositionalParameter($locationId, ParameterType::INTEGER) + ) + ->where( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ) + ; + $query->execute(); + + // Update is_main in eznode_assignment table + $this->setIsMainForContentVersionParentNodeAssignment( + $contentId, + $versionNo, + $parentLocationId + ); + } + + public function countAllLocations(): int + { + $query = $this->createNodeQueryBuilder(['count(node_id)']); + // exclude absolute Root Location (not to be confused with SiteAccess Tree Root) + $query->where($query->expr()->neq('node_id', 'parent_node_id')); + + $statement = $query->execute(); + + return (int) $statement->fetch(FetchMode::COLUMN); + } + + public function loadAllLocationsData(int $offset, int $limit): array + { + $query = $this + ->createNodeQueryBuilder( + [ + 'node_id', + 'priority', + 'is_hidden', + 'is_invisible', + 'remote_id', + 'contentobject_id', + 'parent_node_id', + 'path_identification_string', + 'path_string', + 'depth', + 'sort_field', + 'sort_order', + ] + ); + $query + // exclude absolute Root Location (not to be confused with SiteAccess Tree Root) + ->where($query->expr()->neq('node_id', 'parent_node_id')) + ->setFirstResult($offset) + ->setMaxResults($limit) + ->orderBy('depth', 'ASC') + ->addOrderBy('node_id', 'ASC') + ; + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + /** + * Create QueryBuilder for selecting Location (node) data. + * + * @param array $columns column or expression list + * @param array|null $translations Filters on language mask of content if provided. + * @param bool $useAlwaysAvailable Respect always available flag on content when filtering on $translations. + * + * @return \Doctrine\DBAL\Query\QueryBuilder + */ + private function createNodeQueryBuilder( + array $columns, + array $translations = null, + bool $useAlwaysAvailable = true + ): QueryBuilder { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder + ->select($columns) + ->from(self::CONTENT_TREE_TABLE, 't') + ; + + if (!empty($translations)) { + $this->appendContentItemTranslationsConstraint($queryBuilder, $translations, $useAlwaysAvailable); + } + + return $queryBuilder; + } + + private function appendContentItemTranslationsConstraint( + QueryBuilder $queryBuilder, + array $translations, + bool $useAlwaysAvailable + ): void { + $expr = $queryBuilder->expr(); + try { + $mask = $this->languageMaskGenerator->generateLanguageMaskFromLanguageCodes( + $translations, + $useAlwaysAvailable + ); + } catch (NotFoundException $e) { + return; + } + + $queryBuilder->leftJoin( + 't', + 'ezcontentobject', + 'c', + $expr->eq('t.contentobject_id', 'c.id') + ); + + $queryBuilder->andWhere( + $expr->orX( + $expr->gt( + $this->dbPlatform->getBitAndComparisonExpression('c.language_mask', $mask), + 0 + ), + // Root location doesn't have language mask + $expr->eq( + 't.node_id', + 't.parent_node_id' + ) + ) + ); + } + + /** + * Mark eznode_assignment entry, identified by Content ID and Version ID, as main for the given + * parent Location ID. + * + * **NOTE**: The method erases is_main from the other entries related to Content and Version IDs + */ + private function setIsMainForContentVersionParentNodeAssignment( + int $contentId, + int $versionNo, + int $parentLocationId + ): void { + $query = $this->connection->createQueryBuilder(); + $query + ->update('eznode_assignment') + ->set( + 'is_main', + // set is_main = 1 only for current parent, set 0 for other entries + 'CASE WHEN parent_node <> :parent_location_id THEN 0 ELSE 1 END' + ) + ->where('contentobject_id = :content_id') + ->andWhere('contentobject_version = :version_no') + ->setParameter('parent_location_id', $parentLocationId, ParameterType::INTEGER) + ->setParameter('content_id', $contentId, ParameterType::INTEGER) + ->setParameter('version_no', $versionNo, ParameterType::INTEGER); + + $query->execute(); + } + + /** + * @param array $parentNode raw Location data + */ + private function insertLocationIntoContentTree( + CreateStruct $createStruct, + array $parentNode + ): Location { + $location = new Location(); + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::CONTENT_TREE_TABLE) + ->values( + [ + 'contentobject_id' => ':content_id', + 'contentobject_is_published' => ':is_published', + 'contentobject_version' => ':version_no', + 'depth' => ':depth', + 'is_hidden' => ':is_hidden', + 'is_invisible' => ':is_invisible', + 'modified_subnode' => ':modified_subnode', + 'parent_node_id' => ':parent_node_id', + 'path_string' => ':path_string', + 'priority' => ':priority', + 'remote_id' => ':remote_id', + 'sort_field' => ':sort_field', + 'sort_order' => ':sort_order', + ] + ) + ->setParameters( + [ + 'content_id' => $location->contentId = $createStruct->contentId, + 'is_published' => 1, + 'version_no' => $createStruct->contentVersion, + 'depth' => $location->depth = $parentNode['depth'] + 1, + 'is_hidden' => $location->hidden = $createStruct->hidden, + 'is_invisible' => $location->invisible = $createStruct->invisible, + 'modified_subnode' => time(), + 'parent_node_id' => $location->parentId = $parentNode['node_id'], + 'path_string' => '', // Set later + 'priority' => $location->priority = $createStruct->priority, + 'remote_id' => $location->remoteId = $createStruct->remoteId, + 'sort_field' => $location->sortField = $createStruct->sortField, + 'sort_order' => $location->sortOrder = $createStruct->sortOrder, + ], + [ + 'contentobject_id' => ParameterType::INTEGER, + 'contentobject_is_published' => ParameterType::INTEGER, + 'contentobject_version' => ParameterType::INTEGER, + 'depth' => ParameterType::INTEGER, + 'is_hidden' => ParameterType::INTEGER, + 'is_invisible' => ParameterType::INTEGER, + 'modified_subnode' => ParameterType::INTEGER, + 'parent_node_id' => ParameterType::INTEGER, + 'path_string' => ParameterType::STRING, + 'priority' => ParameterType::INTEGER, + 'remote_id' => ParameterType::STRING, + 'sort_field' => ParameterType::INTEGER, + 'sort_order' => ParameterType::INTEGER, + ] + ); + $query->execute(); + + $location->id = (int)$this->connection->lastInsertId(self::CONTENT_TREE_SEQ); + + return $location; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + private function addConditionsByCriterion(?Criterion $criterion, QueryBuilder $query): void + { + if (null === $criterion) { + return; + } + + $languageSettings = []; + + $query->where( + $this->trashCriteriaConverter->convertCriteria($query, $criterion, $languageSettings) + ); + } + + private function addSort(?array $sort, QueryBuilder $query, array $languageSettings = []): void + { + if (empty($sort)) { + return; + } + + $this->trashSortClauseConverter->applySelect($query, $sort); + $this->trashSortClauseConverter->applyJoin($query, $sort, $languageSettings); + $this->trashSortClauseConverter->applyOrderBy($query); + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..70fe4b717c --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Location/Gateway/ExceptionConversion.php @@ -0,0 +1,425 @@ +innerGateway = $innerGateway; + } + + public function getBasicNodeData( + int $nodeId, + array $translations = null, + bool $useAlwaysAvailable = true + ): array { + try { + return $this->innerGateway->getBasicNodeData($nodeId, $translations, $useAlwaysAvailable); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getNodeDataList(array $locationIds, array $translations = null, bool $useAlwaysAvailable = true): iterable + { + try { + return $this->innerGateway->getNodeDataList($locationIds, $translations, $useAlwaysAvailable); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getBasicNodeDataByRemoteId( + string $remoteId, + array $translations = null, + bool $useAlwaysAvailable = true + ): array { + try { + return $this->innerGateway->getBasicNodeDataByRemoteId($remoteId, $translations, $useAlwaysAvailable); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadLocationDataByContent(int $contentId, ?int $rootLocationId = null): array + { + try { + return $this->innerGateway->loadLocationDataByContent($contentId, $rootLocationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadLocationDataByTrashContent(int $contentId, ?int $rootLocationId = null): array + { + try { + return $this->innerGateway->loadLocationDataByTrashContent($contentId, $rootLocationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadParentLocationsDataForDraftContent(int $contentId): array + { + try { + return $this->innerGateway->loadParentLocationsDataForDraftContent($contentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getSubtreeContent(int $sourceId, bool $onlyIds = false): array + { + try { + return $this->innerGateway->getSubtreeContent($sourceId, $onlyIds); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getSubtreeSize(string $path): int + { + try { + return $this->innerGateway->getSubtreeSize($path); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getChildren(int $locationId): array + { + try { + return $this->innerGateway->getChildren($locationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function moveSubtreeNodes(array $fromPathString, array $toPathString): void + { + try { + $this->innerGateway->moveSubtreeNodes($fromPathString, $toPathString); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateSubtreeModificationTime(string $pathString, ?int $timestamp = null): void + { + try { + $this->innerGateway->updateSubtreeModificationTime($pathString, $timestamp); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateNodeAssignment( + int $contentObjectId, + int $oldParent, + int $newParent, + int $opcode + ): void { + try { + $this->innerGateway->updateNodeAssignment($contentObjectId, $oldParent, $newParent, $opcode); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function createLocationsFromNodeAssignments(int $contentId, int $versionNo): void + { + try { + $this->innerGateway->createLocationsFromNodeAssignments($contentId, $versionNo); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateLocationsContentVersionNo(int $contentId, int $versionNo): void + { + try { + $this->innerGateway->updateLocationsContentVersionNo($contentId, $versionNo); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function hideSubtree(string $pathString): void + { + try { + $this->innerGateway->hideSubtree($pathString); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function unHideSubtree(string $pathString): void + { + try { + $this->innerGateway->unHideSubtree($pathString); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function setNodeWithChildrenInvisible(string $pathString): void + { + try { + $this->innerGateway->setNodeWithChildrenInvisible($pathString); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function setNodeHidden(string $pathString): void + { + try { + $this->innerGateway->setNodeHidden($pathString); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function setNodeWithChildrenVisible(string $pathString): void + { + try { + $this->innerGateway->setNodeWithChildrenVisible($pathString); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function setNodeUnhidden(string $pathString): void + { + try { + $this->innerGateway->setNodeUnhidden($pathString); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function swap(int $locationId1, int $locationId2): bool + { + try { + return $this->innerGateway->swap($locationId1, $locationId2); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function create(CreateStruct $createStruct, array $parentNode): Location + { + try { + return $this->innerGateway->create($createStruct, $parentNode); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function createNodeAssignment( + CreateStruct $createStruct, + int $parentNodeId, + int $type = self::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP + ): void { + try { + $this->innerGateway->createNodeAssignment($createStruct, $parentNodeId, $type); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteNodeAssignment(int $contentId, ?int $versionNo = null): void + { + try { + $this->innerGateway->deleteNodeAssignment($contentId, $versionNo); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function update(UpdateStruct $location, int $locationId): void + { + try { + $this->innerGateway->update($location, $locationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updatePathIdentificationString( + int $locationId, + int $parentLocationId, + string $text + ): void { + try { + $this->innerGateway->updatePathIdentificationString($locationId, $parentLocationId, $text); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function removeLocation(int $locationId): void + { + try { + $this->innerGateway->removeLocation($locationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getFallbackMainNodeData(int $contentId, int $locationId): array + { + try { + return $this->innerGateway->getFallbackMainNodeData($contentId, $locationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function trashLocation(int $locationId): void + { + try { + $this->innerGateway->trashLocation($locationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function untrashLocation(int $locationId, ?int $newParentId = null): Location + { + try { + return $this->innerGateway->untrashLocation($locationId, $newParentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadTrashByLocation(int $locationId): array + { + try { + return $this->innerGateway->loadTrashByLocation($locationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function cleanupTrash(): void + { + try { + $this->innerGateway->cleanupTrash(); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function listTrashed( + int $offset, + ?int $limit, + array $sort = null, + ?Criterion $criterion = null + ): array { + try { + return $this->innerGateway->listTrashed($offset, $limit, $sort, $criterion); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countTrashed(?Criterion $criterion = null): int + { + try { + return $this->innerGateway->countTrashed($criterion); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function removeElementFromTrash(int $id): void + { + try { + $this->innerGateway->removeElementFromTrash($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function setSectionForSubtree(string $pathString, int $sectionId): bool + { + try { + return $this->innerGateway->setSectionForSubtree($pathString, $sectionId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countLocationsByContentId(int $contentId): int + { + try { + return $this->innerGateway->countLocationsByContentId($contentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function changeMainLocation( + int $contentId, + int $locationId, + int $versionNo, + int $parentLocationId + ): void { + try { + $this->innerGateway->changeMainLocation($contentId, $locationId, $versionNo, $parentLocationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countAllLocations(): int + { + try { + return $this->innerGateway->countAllLocations(); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadAllLocationsData(int $offset, int $limit): array + { + try { + return $this->innerGateway->loadAllLocationsData($offset, $limit); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/Content/Location/Handler.php b/src/lib/Persistence/Legacy/Content/Location/Handler.php new file mode 100644 index 0000000000..758e221c4a --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Location/Handler.php @@ -0,0 +1,604 @@ +locationGateway = $locationGateway; + $this->locationMapper = $locationMapper; + $this->contentHandler = $contentHandler; + $this->objectStateHandler = $objectStateHandler; + $this->treeHandler = $treeHandler; + } + + /** + * Returns parent path string for a path string. + * + * @param string $pathString + * + * @return string + */ + protected function getParentPathString($pathString) + { + return implode('/', array_slice(explode('/', $pathString), 0, -2)) . '/'; + } + + /** + * {@inheritdoc} + */ + public function load($locationId, array $translations = null, bool $useAlwaysAvailable = true) + { + return $this->treeHandler->loadLocation($locationId, $translations, $useAlwaysAvailable); + } + + /** + * {@inheritdoc} + */ + public function loadList(array $locationIds, array $translations = null, bool $useAlwaysAvailable = true): iterable + { + $list = $this->locationGateway->getNodeDataList($locationIds, $translations, $useAlwaysAvailable); + + $locations = []; + foreach ($list as $row) { + $id = (int)$row['node_id']; + if (!isset($locations[$id])) { + $locations[$id] = $this->locationMapper->createLocationFromRow($row); + } + } + + return $locations; + } + + /** + * Loads the subtree ids of the location identified by $locationId. + * + * @param int $locationId + * + * @return array Location ids are in the index, Content ids in the value. + */ + public function loadSubtreeIds($locationId) + { + return $this->locationGateway->getSubtreeContent($locationId, true); + } + + /** + * {@inheritdoc} + */ + public function loadByRemoteId($remoteId, array $translations = null, bool $useAlwaysAvailable = true) + { + $data = $this->locationGateway->getBasicNodeDataByRemoteId($remoteId, $translations, $useAlwaysAvailable); + + return $this->locationMapper->createLocationFromRow($data); + } + + /** + * Loads all locations for $contentId, optionally limited to a sub tree + * identified by $rootLocationId. + * + * @param int $contentId + * @param int $rootLocationId + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Location[] + */ + public function loadLocationsByContent($contentId, $rootLocationId = null) + { + $rows = $this->locationGateway->loadLocationDataByContent($contentId, $rootLocationId); + + return $this->locationMapper->createLocationsFromRows($rows); + } + + /** + * {@inheritdoc} + */ + public function loadLocationsByTrashContent(int $contentId, ?int $rootLocationId = null): array + { + $rows = $this->locationGateway->loadLocationDataByTrashContent($contentId, $rootLocationId); + + return $this->locationMapper->createLocationsFromRows($rows, '', new Trashed()); + } + + public function loadParentLocationsForDraftContent($contentId) + { + $rows = $this->locationGateway->loadParentLocationsDataForDraftContent($contentId); + + return $this->locationMapper->createLocationsFromRows($rows); + } + + /** + * Returns an array of default content states with content state group id as key. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState[] + */ + protected function getDefaultContentStates() + { + $defaultObjectStatesMap = []; + + foreach ($this->objectStateHandler->loadAllGroups() as $objectStateGroup) { + foreach ($this->objectStateHandler->loadObjectStates($objectStateGroup->id) as $objectState) { + // Only register the first object state which is the default one. + $defaultObjectStatesMap[$objectStateGroup->id] = $objectState; + break; + } + } + + return $defaultObjectStatesMap; + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content\ObjectState[] $contentStates + */ + protected function setContentStates(Content $content, array $contentStates) + { + foreach ($contentStates as $contentStateGroupId => $contentState) { + $this->objectStateHandler->setContentState( + $content->versionInfo->contentInfo->id, + $contentStateGroupId, + $contentState->id + ); + } + } + + /** + * Copy location object identified by $sourceId, into destination identified by $destinationParentId. + * + * Performs a deep copy of the location identified by $sourceId and all of + * its child locations, copying the most recent published content object + * for each location to a new content object without any additional version + * information. Relations are not copied. URLs are not touched at all. + * + * @todo Either move to async/batch or find ways toward optimizing away operations per object. + * @todo Optionally retain dates and set creator + * + * @param mixed $sourceId + * @param mixed $destinationParentId + * @param int|null $newOwnerId + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Location the newly created Location. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function copySubtree($sourceId, $destinationParentId, $newOwnerId = null) + { + $children = $this->locationGateway->getSubtreeContent($sourceId); + $destinationParentData = $this->locationGateway->getBasicNodeData($destinationParentId); + $defaultObjectStates = $this->getDefaultContentStates(); + $contentMap = []; + $locationMap = [ + $children[0]['parent_node_id'] => [ + 'id' => $destinationParentId, + 'hidden' => (bool)$destinationParentData['is_hidden'], + 'invisible' => (bool)$destinationParentData['is_invisible'], + 'path_identification_string' => $destinationParentData['path_identification_string'], + ], + ]; + + $locations = []; + foreach ($children as $child) { + $locations[$child['contentobject_id']][$child['node_id']] = true; + } + + $time = time(); + $mainLocations = []; + $mainLocationsUpdate = []; + foreach ($children as $index => $child) { + // Copy content + if (!isset($contentMap[$child['contentobject_id']])) { + $content = $this->contentHandler->copy( + (int)$child['contentobject_id'], + (int)$child['contentobject_version'], + $newOwnerId + ); + + $this->setContentStates($content, $defaultObjectStates); + + $content = $this->contentHandler->publish( + $content->versionInfo->contentInfo->id, + $content->versionInfo->contentInfo->currentVersionNo, + new MetadataUpdateStruct( + [ + 'publicationDate' => $time, + 'modificationDate' => $time, + ] + ) + ); + + $contentMap[$child['contentobject_id']] = $content->versionInfo->contentInfo->id; + } + + $createStruct = $this->locationMapper->getLocationCreateStruct($child); + $createStruct->contentId = $contentMap[$child['contentobject_id']]; + $parentData = $locationMap[$child['parent_node_id']]; + $createStruct->parentId = $parentData['id']; + $createStruct->invisible = $createStruct->invisible + || $createStruct->hidden + || $parentData['hidden'] + || $parentData['invisible']; + $pathString = explode('/', $child['path_identification_string']); + $pathString = end($pathString); + $createStruct->pathIdentificationString = strlen($pathString) > 0 + ? $parentData['path_identification_string'] . '/' . $pathString + : null; + + // Use content main location if already set, otherwise create location as main + if (isset($mainLocations[$child['contentobject_id']])) { + $createStruct->mainLocationId = $locationMap[$mainLocations[$child['contentobject_id']]]['id']; + } else { + $createStruct->mainLocationId = true; + $mainLocations[$child['contentobject_id']] = $child['node_id']; + + // If needed mark for update + if ( + isset($locations[$child['contentobject_id']][$child['main_node_id']]) && + count($locations[$child['contentobject_id']]) > 1 && + $child['node_id'] !== $child['main_node_id'] + ) { + $mainLocationsUpdate[$child['contentobject_id']] = $child['main_node_id']; + } + } + + $newLocation = $this->create($createStruct); + + $locationMap[$child['node_id']] = [ + 'id' => $newLocation->id, + 'hidden' => $newLocation->hidden, + 'invisible' => $newLocation->invisible, + 'path_identification_string' => $newLocation->pathIdentificationString, + ]; + if ($index === 0) { + $copiedSubtreeRootLocation = $newLocation; + } + } + + // Update main locations + foreach ($mainLocationsUpdate as $contentId => $mainLocationId) { + $this->changeMainLocation( + $contentMap[$contentId], + $locationMap[$mainLocationId]['id'] + ); + } + + $destinationParentSectionId = $this->getSectionId($destinationParentId); + + // potentially it may occur that the destination Location doesn't have a section (like in Location ID = 1), + // therefore assigning any or empty section will be invalid here + if ($destinationParentSectionId !== null) { + $this->updateSubtreeSectionIfNecessary($copiedSubtreeRootLocation, $destinationParentSectionId); + } + + return $copiedSubtreeRootLocation; + } + + public function getSubtreeSize(string $path): int + { + return $this->locationGateway->getSubtreeSize($path); + } + + /** + * Retrieves section ID of the location's content. + * + * @param int $locationId + * + * @return int|null + */ + private function getSectionId($locationId) + { + $location = $this->load($locationId); + + try { + $locationContentInfo = $this->contentHandler->loadContentInfo($location->contentId); + + return $locationContentInfo->sectionId; + } catch (NotFoundException $e) { + return null; + } + } + + /** + * If the location is the main location for its content, updates subtree section. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Location $location + * @param int $sectionId + */ + private function updateSubtreeSectionIfNecessary(Location $location, $sectionId) + { + if ($this->isMainLocation($location)) { + $this->setSectionForSubtree($location->id, $sectionId); + } + } + + /** + * Checks if the location is the main location for its content. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Location $location + * + * @return bool + */ + private function isMainLocation(Location $location) + { + $locationContentInfo = $this->contentHandler->loadContentInfo($location->contentId); + + return $locationContentInfo->mainLocationId === $location->id; + } + + /** + * Moves location identified by $sourceId into new parent identified by $destinationParentId. + * + * Performs a full move of the location identified by $sourceId to a new + * destination, identified by $destinationParentId. Relations do not need + * to be updated, since they refer to Content. URLs are not touched. + * + * @param mixed $sourceId + * @param mixed $destinationParentId + * + * @return bool + */ + public function move($sourceId, $destinationParentId) + { + $sourceNodeData = $this->locationGateway->getBasicNodeData($sourceId); + $destinationNodeData = $this->locationGateway->getBasicNodeData($destinationParentId); + + $this->locationGateway->moveSubtreeNodes( + $sourceNodeData, + $destinationNodeData + ); + + $this->locationGateway->updateNodeAssignment( + $sourceNodeData['contentobject_id'], + $sourceNodeData['parent_node_id'], + $destinationParentId, + Gateway::NODE_ASSIGNMENT_OP_CODE_MOVE + ); + + $sourceLocation = $this->load($sourceId); + $destinationParentSectionId = $this->getSectionId($destinationParentId); + + // potentially it may occur that the destination Location doesn't have a section (like in Location ID = 1), + // therefore assigning any or empty section will be invalid here + if ($destinationParentSectionId !== null) { + $this->updateSubtreeSectionIfNecessary($sourceLocation, $destinationParentSectionId); + } + } + + /** + * Marks the given nodes and all ancestors as modified. + * + * Optionally a time stamp with the modification date may be specified, + * otherwise the current time is used. + * + * @param int|string $locationId + * @param int $timestamp + */ + public function markSubtreeModified($locationId, $timestamp = null) + { + $nodeData = $this->locationGateway->getBasicNodeData($locationId); + $timestamp = $timestamp ?: time(); + $this->locationGateway->updateSubtreeModificationTime($nodeData['path_string'], $timestamp); + } + + /** + * Sets a location to be hidden, and it self + all children to invisible. + * + * @param mixed $id Location ID + */ + public function hide($id) + { + $sourceNodeData = $this->locationGateway->getBasicNodeData($id); + + $this->locationGateway->hideSubtree($sourceNodeData['path_string']); + } + + /** + * Sets a location to be unhidden, and self + children to visible unless a parent is hiding the tree. + * If not make sure only children down to first hidden node is marked visible. + * + * @param mixed $id + */ + public function unHide($id) + { + $sourceNodeData = $this->locationGateway->getBasicNodeData($id); + + $this->locationGateway->unhideSubtree($sourceNodeData['path_string']); + } + + /** + * Sets a location + all children to invisible. + * + * @param int $id Location ID + */ + public function setInvisible(int $id): void + { + $sourceNodeData = $this->locationGateway->getBasicNodeData($id); + + $this->locationGateway->setNodeWithChildrenInvisible($sourceNodeData['path_string']); + } + + /** + * Sets a location + all children to visible. + * + * @param int $id Location ID + */ + public function setVisible(int $id): void + { + $sourceNodeData = $this->locationGateway->getBasicNodeData($id); + + $this->locationGateway->setNodeWithChildrenVisible($sourceNodeData['path_string']); + } + + /** + * Swaps the content object being pointed to by a location object. + * + * Make the location identified by $locationId1 refer to the Content + * referred to by $locationId2 and vice versa. + * + * @param mixed $locationId1 + * @param mixed $locationId2 + * + * @return bool + */ + public function swap($locationId1, $locationId2) + { + $this->locationGateway->swap($locationId1, $locationId2); + } + + /** + * Updates an existing location. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Location\UpdateStruct $location + * @param int $locationId + */ + public function update(UpdateStruct $location, $locationId) + { + $this->locationGateway->update($location, $locationId); + } + + public function create(CreateStruct $createStruct) + { + $parentNodeData = $this->locationGateway->getBasicNodeData($createStruct->parentId); + $spiLocation = $this->locationGateway->create($createStruct, $parentNodeData); + $this->locationGateway->createNodeAssignment( + $createStruct, + $parentNodeData['node_id'], + LocationGateway::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP + ); + + return $spiLocation; + } + + /** + * Removes all Locations under and including $locationId. + * + * Performs a recursive delete on the location identified by $locationId, + * including all of its child locations. Content which is not referred to + * by any other location is automatically removed. Content which looses its + * main Location will get the first of its other Locations assigned as the + * new main Location. + * + * @param mixed $locationId + * + * @return bool + */ + public function removeSubtree($locationId) + { + $this->treeHandler->removeSubtree($locationId); + } + + /** + * Set section on all content objects in the subtree. + * + * @param mixed $locationId + * @param mixed $sectionId + */ + public function setSectionForSubtree($locationId, $sectionId) + { + $this->treeHandler->setSectionForSubtree($locationId, $sectionId); + } + + /** + * Changes main location of content identified by given $contentId to location identified by given $locationId. + * + * Updates ezcontentobject_tree and eznode_assignment tables (eznode_assignment for content current version number). + * + * @param mixed $contentId + * @param mixed $locationId + */ + public function changeMainLocation($contentId, $locationId) + { + $this->treeHandler->changeMainLocation($contentId, $locationId); + } + + /** + * Get the total number of all existing Locations. Can be combined with loadAllLocations. + * + * @return int + */ + public function countAllLocations() + { + return $this->locationGateway->countAllLocations(); + } + + /** + * Bulk-load all existing Locations, constrained by $limit and $offset to paginate results. + * + * @param int $offset + * @param int $limit + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Location[] + */ + public function loadAllLocations($offset, $limit) + { + $rows = $this->locationGateway->loadAllLocationsData($offset, $limit); + + return $this->locationMapper->createLocationsFromRows($rows); + } + + public function countLocationsByContent(int $contentId): int + { + return $this->locationGateway->countLocationsByContentId($contentId); + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Location\Handler'); diff --git a/src/lib/Persistence/Legacy/Content/Location/Mapper.php b/src/lib/Persistence/Legacy/Content/Location/Mapper.php new file mode 100644 index 0000000000..4afeb7109e --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Location/Mapper.php @@ -0,0 +1,104 @@ +id = (int)$data[$prefix . 'node_id']; + $location->priority = (int)$data[$prefix . 'priority']; + $location->hidden = (bool)$data[$prefix . 'is_hidden']; + $location->invisible = (bool)$data[$prefix . 'is_invisible']; + $location->remoteId = $data[$prefix . 'remote_id']; + $location->contentId = (int)$data[$prefix . 'contentobject_id']; + $location->parentId = (int)$data[$prefix . 'parent_node_id']; + $location->pathIdentificationString = $data[$prefix . 'path_identification_string']; + $location->pathString = $data[$prefix . 'path_string']; + $location->depth = (int)$data[$prefix . 'depth']; + $location->sortField = (int)$data[$prefix . 'sort_field']; + $location->sortOrder = (int)$data[$prefix . 'sort_order']; + if (isset($data[$prefix . 'trashed'])) { + $location->trashed = (int)$data[$prefix . 'trashed']; + } + + return $location; + } + + /** + * Creates Location objects from the given $rows, optionally with key + * $prefix. + * + * @param array $rows + * @param string $prefix + * @param \Ibexa\Contracts\Core\Persistence\Content\Location|null $location + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Location[] + */ + public function createLocationsFromRows(array $rows, $prefix = '', ?Location $location = null) + { + $locations = []; + + foreach ($rows as $row) { + $id = $row[$prefix . 'node_id']; + if (!isset($locations[$id])) { + $locations[$id] = $this->createLocationFromRow($row, $prefix, $location); + } + } + + return array_values($locations); + } + + /** + * Creates a Location CreateStruct from a $data row. + * + * @param array $data + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Location\CreateStruct + */ + public function getLocationCreateStruct(array $data) + { + $struct = new CreateStruct(); + + $struct->contentId = $data['contentobject_id']; + $struct->contentVersion = $data['contentobject_version']; + $struct->hidden = $data['is_hidden']; + $struct->invisible = $data['is_invisible']; + $struct->mainLocationId = $data['main_node_id']; + $struct->parentId = $data['parent_node_id']; + $struct->pathIdentificationString = $data['path_identification_string']; + $struct->priority = $data['priority']; + $struct->remoteId = md5(uniqid(static::class, true)); + $struct->sortField = $data['sort_field']; + $struct->sortOrder = $data['sort_order']; + + return $struct; + } +} + +class_alias(Mapper::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper'); diff --git a/src/lib/Persistence/Legacy/Content/Location/Trash/Handler.php b/src/lib/Persistence/Legacy/Content/Location/Trash/Handler.php new file mode 100644 index 0000000000..18cdd924b6 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Location/Trash/Handler.php @@ -0,0 +1,253 @@ +locationHandler = $locationHandler; + $this->locationGateway = $locationGateway; + $this->locationMapper = $locationMapper; + $this->contentHandler = $contentHandler; + } + + /** + * Loads the data for the trashed location identified by $id. + * $id is the same as original location (which has been previously trashed). + * + * @param int $id + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Location\Trashed + */ + public function loadTrashItem($id) + { + $data = $this->locationGateway->loadTrashByLocation($id); + + return $this->locationMapper->createLocationFromRow($data, null, new Trashed()); + } + + /** + * Sends a subtree starting to $locationId to the trash + * and returns a Trashed object corresponding to $locationId. + * + * Moves all locations in the subtree to the Trash. The associated content + * objects are left untouched. + * + * @param mixed $locationId + * + * @todo Handle field types actions + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Location\Trashed|null null if location was deleted, otherwise Trashed object + */ + public function trashSubtree($locationId) + { + $locationRows = $this->locationGateway->getSubtreeContent($locationId); + $isLocationRemoved = false; + $parentLocationId = null; + $removedLocationsContentMap = []; + + foreach ($locationRows as $locationRow) { + if ($locationRow['node_id'] == $locationId) { + $parentLocationId = $locationRow['parent_node_id']; + } + + if ($this->locationGateway->countLocationsByContentId($locationRow['contentobject_id']) == 1) { + $this->locationGateway->trashLocation($locationRow['node_id']); + $removedLocationsContentMap[(int)$locationRow['node_id']] = (int)$locationRow['contentobject_id']; + } else { + if ($locationRow['node_id'] == $locationId) { + $isLocationRemoved = true; + } + $this->locationGateway->removeLocation($locationRow['node_id']); + + if ($locationRow['node_id'] == $locationRow['main_node_id']) { + $newMainLocationRow = $this->locationGateway->getFallbackMainNodeData( + $locationRow['contentobject_id'], + $locationRow['node_id'] + ); + + $this->locationHandler->changeMainLocation( + $locationRow['contentobject_id'], + $newMainLocationRow['node_id'], + $newMainLocationRow['contentobject_version'], + $newMainLocationRow['parent_node_id'] + ); + } + } + } + + if (isset($parentLocationId)) { + $this->locationHandler->markSubtreeModified($parentLocationId, time()); + } + + if ($isLocationRemoved === true) { + return null; + } + + $trashItem = $this->loadTrashItem($locationId); + $trashItem->removedLocationContentIdMap = $removedLocationsContentMap; + + return $trashItem; + } + + /** + * Returns a trashed location to normal state. + * + * Recreates the originally trashed location in the new position. + * If this is not possible (because the old location does not exist any more), + * a ParentNotFound exception is thrown. + * + * Returns newly restored location Id. + * + * @param mixed $trashedId + * @param mixed $newParentId + * + * @return int Newly restored location id + * + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException If $newParentId is invalid + * + * @todo Handle field types actions + */ + public function recover($trashedId, $newParentId) + { + return $this->locationGateway->untrashLocation($trashedId, $newParentId)->id; + } + + /** + * {@inheritdoc}. + */ + public function findTrashItems(Criterion $criterion = null, $offset = 0, $limit = null, array $sort = null) + { + $totalCount = $this->locationGateway->countTrashed($criterion); + if ($totalCount === 0) { + return new TrashResult(); + } + + $rows = $this->locationGateway->listTrashed($offset, $limit, $sort, $criterion); + $items = []; + + foreach ($rows as $row) { + $items[] = $this->locationMapper->createLocationFromRow($row, null, new Trashed()); + } + + return new TrashResult([ + 'items' => $items, + 'totalCount' => $totalCount, + ]); + } + + /** + * {@inheritdoc} + */ + public function emptyTrash() + { + $resultList = new TrashItemDeleteResultList(); + do { + $trashedItems = $this->findTrashItems(null, 0, self::EMPTY_TRASH_BULK_SIZE); + foreach ($trashedItems as $item) { + $resultList->items[] = $this->delete($item); + } + } while ($trashedItems->totalCount > self::EMPTY_TRASH_BULK_SIZE); + + $this->locationGateway->cleanupTrash(); + + return $resultList; + } + + /** + * Removes a trashed location identified by $trashedLocationId from trash + * Associated content has to be deleted. + * + * @param int $trashedId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResult + */ + public function deleteTrashItem($trashedId) + { + return $this->delete($this->loadTrashItem($trashedId)); + } + + /** + * Triggers delete operations for $trashItem. + * If there is no more locations for corresponding content, then it will be deleted as well. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Location\Trashed $trashItem + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResult + */ + protected function delete(Trashed $trashItem) + { + $result = new TrashItemDeleteResult(); + $result->trashItemId = $trashItem->id; + $result->contentId = $trashItem->contentId; + + $reverseRelations = $this->contentHandler->loadReverseRelations($trashItem->contentId); + + $this->locationGateway->removeElementFromTrash($trashItem->id); + + if ($this->locationGateway->countLocationsByContentId($trashItem->contentId) < 1) { + $this->contentHandler->deleteContent($trashItem->contentId); + $result->contentRemoved = true; + $result->reverseRelationContentIds = array_column($reverseRelations, 'sourceContentId'); + } + + return $result; + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Location\Trash\Handler'); diff --git a/src/lib/Persistence/Legacy/Content/Mapper.php b/src/lib/Persistence/Legacy/Content/Mapper.php new file mode 100644 index 0000000000..3508c25656 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Mapper.php @@ -0,0 +1,716 @@ + + * > + * > + * > + * @phpstan-type TVersionedFieldMap array< + * int, array< + * int, array< + * int, \Ibexa\Contracts\Core\Persistence\Content\Field, + * > + * > + * > + * @phpstan-type TVersionedNameMap array< + * int, array< + * int, array + * > + * > + * @phpstan-type TContentInfoMap array + * @phpstan-type TVersionInfoMap array< + * int, array< + * int, \Ibexa\Contracts\Core\Persistence\Content\VersionInfo, + * > + * > + * @phpstan-type TRawContentRow array + */ +class Mapper +{ + protected Registry $converterRegistry; + + protected LanguageHandler $languageHandler; + + private ContentTypeHandler $contentTypeHandler; + + private EventDispatcherInterface $eventDispatcher; + + public function __construct( + Registry $converterRegistry, + LanguageHandler $languageHandler, + ContentTypeHandler $contentTypeHandler, + EventDispatcherInterface $eventDispatcher + ) { + $this->converterRegistry = $converterRegistry; + $this->languageHandler = $languageHandler; + $this->contentTypeHandler = $contentTypeHandler; + $this->eventDispatcher = $eventDispatcher; + } + + /** + * Creates a Content from the given $struct and $currentVersionNo. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\CreateStruct $struct + * @param mixed $currentVersionNo + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo + */ + private function createContentInfoFromCreateStruct(CreateStruct $struct, $currentVersionNo = 1) + { + $contentInfo = new ContentInfo(); + + $contentInfo->id = null; + $contentInfo->contentTypeId = $struct->typeId; + $contentInfo->sectionId = $struct->sectionId; + $contentInfo->ownerId = $struct->ownerId; + $contentInfo->alwaysAvailable = $struct->alwaysAvailable; + $contentInfo->remoteId = $struct->remoteId; + $contentInfo->mainLanguageCode = $this->languageHandler + ->load(isset($struct->mainLanguageId) ? $struct->mainLanguageId : $struct->initialLanguageId) + ->languageCode; + $contentInfo->name = isset($struct->name[$contentInfo->mainLanguageCode]) + ? $struct->name[$contentInfo->mainLanguageCode] + : ''; + // For drafts published and modified timestamps should be 0 + $contentInfo->publicationDate = 0; + $contentInfo->modificationDate = 0; + $contentInfo->currentVersionNo = $currentVersionNo; + $contentInfo->status = ContentInfo::STATUS_DRAFT; + $contentInfo->isPublished = false; + $contentInfo->isHidden = $struct->isHidden; + + return $contentInfo; + } + + /** + * Creates a new version for the given $struct and $versionNo. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\CreateStruct $struct + * @param mixed $versionNo + * + * @return \Ibexa\Contracts\Core\Persistence\Content\VersionInfo + */ + public function createVersionInfoFromCreateStruct(CreateStruct $struct, $versionNo) + { + $versionInfo = new VersionInfo(); + + $versionInfo->id = null; + $versionInfo->contentInfo = $this->createContentInfoFromCreateStruct($struct, $versionNo); + $versionInfo->versionNo = $versionNo; + $versionInfo->creatorId = $struct->ownerId; + $versionInfo->status = VersionInfo::STATUS_DRAFT; + $versionInfo->initialLanguageCode = $this->languageHandler->load($struct->initialLanguageId)->languageCode; + $versionInfo->creationDate = $struct->modified; + $versionInfo->modificationDate = $struct->modified; + $versionInfo->names = $struct->name; + + $languages = []; + foreach ($struct->fields as $field) { + if (!isset($languages[$field->languageCode])) { + $languages[$field->languageCode] = true; + } + } + $versionInfo->languageCodes = array_keys($languages); + + return $versionInfo; + } + + /** + * Creates a new version for the given $content. + * + * @param \Ibexa\Contracts\Core\Persistence\Content $content + * @param mixed $versionNo + * @param mixed $userId + * @param string|null $languageCode + * + * @return \Ibexa\Contracts\Core\Persistence\Content\VersionInfo + */ + public function createVersionInfoForContent(Content $content, $versionNo, $userId, ?string $languageCode = null) + { + $versionInfo = new VersionInfo(); + + $versionInfo->contentInfo = $content->versionInfo->contentInfo; + $versionInfo->versionNo = $versionNo; + $versionInfo->creatorId = $userId; + $versionInfo->status = VersionInfo::STATUS_DRAFT; + $versionInfo->initialLanguageCode = $languageCode ?? $content->versionInfo->initialLanguageCode; + $versionInfo->creationDate = time(); + $versionInfo->modificationDate = $versionInfo->creationDate; + $versionInfo->names = is_object($content->versionInfo) ? $content->versionInfo->names : []; + $versionInfo->languageCodes = $content->versionInfo->languageCodes; + + return $versionInfo; + } + + /** + * Converts value of $field to storage value. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * + * @return \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue + */ + public function convertToStorageValue(Field $field) + { + $converter = $this->converterRegistry->getConverter( + $field->type + ); + $storageValue = new StorageFieldValue(); + $converter->toStorageValue( + $field->value, + $storageValue + ); + + return $storageValue; + } + + /** + * Extracts Content objects (and nested) from database result $rows. + * + * Expects database rows to be indexed by keys of the format + * + * "$tableName_$columnName" + * + * @param array> $rows + * @param array> $nameRows + * @param string $prefix + * @param array|null $translations + * + * @return \Ibexa\Contracts\Core\Persistence\Content[] + */ + public function extractContentFromRows( + array $rows, + array $nameRows, + string $prefix = 'ezcontentobject_', + ?array $translations = null + ): array { + $versionedNameData = []; + + foreach ($nameRows as $row) { + $contentId = (int)$row["{$prefix}name_contentobject_id"]; + $versionNo = (int)$row["{$prefix}name_content_version"]; + $languageCode = (string)$row["{$prefix}name_content_translation"]; + $versionedNameData[$contentId][$versionNo][$languageCode] = (string)$row["{$prefix}name_name"]; + } + + $contentInfos = []; + $versionInfos = []; + $fields = []; + + $fieldDefinitions = $this->loadCachedVersionFieldDefinitionsPerLanguage( + $rows, + $prefix, + $translations + ); + + foreach ($rows as $row) { + $contentId = (int)$row["{$prefix}id"]; + $versionId = (int)$row["{$prefix}version_id"]; + + if (!isset($contentInfos[$contentId])) { + $contentInfos[$contentId] = $this->extractContentInfoFromRow($row, $prefix); + } + + if (!isset($versionInfos[$contentId])) { + $versionInfos[$contentId] = []; + } + + if (!isset($versionInfos[$contentId][$versionId])) { + $versionInfos[$contentId][$versionId] = $this->extractVersionInfoFromRow($row); + } + + $fieldId = (int)$row["{$prefix}attribute_id"]; + $fieldDefinitionId = (int)$row["{$prefix}attribute_contentclassattribute_id"]; + $languageCode = $row["{$prefix}attribute_language_code"]; + + if (!isset($fields[$contentId][$versionId][$fieldId]) + && isset($fieldDefinitions[$contentId][$versionId][$languageCode][$fieldDefinitionId]) + ) { + $fields[$contentId][$versionId][$fieldId] = $this->extractFieldFromRow($row); + unset($fieldDefinitions[$contentId][$versionId][$languageCode][$fieldDefinitionId]); + } + } + + return $this->buildContentObjects( + $contentInfos, + $versionInfos, + $fields, + $fieldDefinitions, + $versionedNameData + ); + } + + /** + * @phpstan-param TContentInfoMap $contentInfos + * @phpstan-param TVersionInfoMap $versionInfos + * @phpstan-param TVersionedFieldMap $fields + * @phpstan-param TVersionedLanguageFieldDefinitionsMap $missingFieldDefinitions + * @phpstan-param TVersionedNameMap $versionedNames + * + * @return \Ibexa\Contracts\Core\Persistence\Content[] + */ + private function buildContentObjects( + array $contentInfos, + array $versionInfos, + array $fields, + array $missingFieldDefinitions, + array $versionedNames + ): array { + $results = []; + + foreach ($contentInfos as $contentId => $contentInfo) { + foreach ($versionInfos[$contentId] as $versionId => $versionInfo) { + // Fallback to just main language name if versioned name data is missing + $names = $versionedNames[$contentId][$versionInfo->versionNo] + ?? [$contentInfo->mainLanguageCode => $contentInfo->name]; + + $content = new Content(); + $content->versionInfo = $versionInfo; + $content->versionInfo->names = $names; + $content->versionInfo->contentInfo = $contentInfo; + $content->fields = array_values($fields[$contentId][$versionId] ?? []); + + $missingVersionFieldDefinitions = $missingFieldDefinitions[$contentId][$versionId] ?? []; + + foreach ($missingVersionFieldDefinitions as $languageCode => $versionFieldDefinitions) { + foreach ($versionFieldDefinitions as $fieldDefinition) { + $event = $this->eventDispatcher->dispatch( + new ResolveMissingFieldEvent( + $content, + $fieldDefinition, + $languageCode + ) + ); + + $field = $event->getField(); + if ($field !== null) { + $content->fields[] = $field; + } + } + } + + $results[] = $content; + } + } + + return $results; + } + + /** + * @phpstan-param TRawContentRow[] $rows + * + * @phpstan-return TVersionedLanguageFieldDefinitionsMap + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function loadCachedVersionFieldDefinitionsPerLanguage( + array $rows, + string $prefix, + ?array $translations = null + ): array { + $fieldDefinitions = []; + $contentTypes = []; + $allLanguages = $this->loadAllLanguagesWithIdKey(); + + foreach ($rows as $row) { + $contentId = (int)$row["{$prefix}id"]; + $versionId = (int)$row["{$prefix}version_id"]; + $contentTypeId = (int)$row["{$prefix}contentclass_id"]; + $languageMask = (int)$row["{$prefix}version_language_mask"]; + + if (isset($fieldDefinitions[$contentId][$versionId])) { + continue; + } + + $allLanguagesCodes = $this->extractLanguageCodesFromMask($languageMask, $allLanguages); + $languageCodes = empty($translations) ? $allLanguagesCodes : array_intersect($translations, $allLanguagesCodes); + $contentTypes[$contentTypeId] = $contentTypes[$contentTypeId] ?? $this->contentTypeHandler->load($contentTypeId); + $contentType = $contentTypes[$contentTypeId]; + foreach ($contentType->fieldDefinitions as $fieldDefinition) { + foreach ($languageCodes as $languageCode) { + $id = (int)$fieldDefinition->id; + $languageCode = (string)$languageCode; + $fieldDefinitions[$contentId][$versionId][$languageCode][$id] = $fieldDefinition; + } + } + } + + return $fieldDefinitions; + } + + /** + * Extracts a ContentInfo object from $row. + * + * @phpstan-param TRawContentRow $row + * + * @param string $prefix Prefix for row keys, which are initially mapped by ezcontentobject fields + * @param string $treePrefix Prefix for tree row key, which are initially mapped by ezcontentobject_tree_ fields + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo + */ + public function extractContentInfoFromRow(array $row, $prefix = '', $treePrefix = 'ezcontentobject_tree_') + { + $contentInfo = new ContentInfo(); + $contentInfo->id = (int)$row["{$prefix}id"]; + $contentInfo->name = (string)$row["{$prefix}name"]; + $contentInfo->contentTypeId = (int)$row["{$prefix}contentclass_id"]; + $contentInfo->sectionId = (int)$row["{$prefix}section_id"]; + $contentInfo->currentVersionNo = (int)$row["{$prefix}current_version"]; + $contentInfo->ownerId = (int)$row["{$prefix}owner_id"]; + $contentInfo->publicationDate = (int)$row["{$prefix}published"]; + $contentInfo->modificationDate = (int)$row["{$prefix}modified"]; + $contentInfo->alwaysAvailable = 1 === ((int)$row["{$prefix}language_mask"] & 1); + $contentInfo->mainLanguageCode = $this->languageHandler->load($row["{$prefix}initial_language_id"])->languageCode; + $contentInfo->remoteId = (string)$row["{$prefix}remote_id"]; + $contentInfo->mainLocationId = ($row["{$treePrefix}main_node_id"] !== null ? (int)$row["{$treePrefix}main_node_id"] : null); + $contentInfo->status = (int)$row["{$prefix}status"]; + $contentInfo->isPublished = ($contentInfo->status == ContentInfo::STATUS_PUBLISHED); + $contentInfo->isHidden = (bool)$row["{$prefix}is_hidden"]; + + return $contentInfo; + } + + /** + * Extracts ContentInfo objects from $rows. + * + * @param array $rows + * @param string $prefix Prefix for row keys, which are initially mapped by ezcontentobject fields + * @param string $treePrefix Prefix for tree row key, which are initially mapped by ezcontentobject_tree_ fields + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo[] + */ + public function extractContentInfoFromRows(array $rows, $prefix = '', $treePrefix = 'ezcontentobject_tree_') + { + $contentInfoObjects = []; + foreach ($rows as $row) { + $contentInfoObjects[] = $this->extractContentInfoFromRow($row, $prefix, $treePrefix); + } + + return $contentInfoObjects; + } + + /** + * Extracts a VersionInfo object from $row. + * + * This method will return VersionInfo with incomplete data. It is intended to be used only by + * {@link self::extractContentFromRows} where missing data will be filled in. + * + * @param array $row + * @param array $names + * + * @return \Ibexa\Contracts\Core\Persistence\Content\VersionInfo + */ + private function extractVersionInfoFromRow(array $row, array $names = []) + { + $versionInfo = new VersionInfo(); + $versionInfo->id = (int)$row['ezcontentobject_version_id']; + $versionInfo->contentInfo = null; + $versionInfo->versionNo = (int)$row['ezcontentobject_version_version']; + $versionInfo->creatorId = (int)$row['ezcontentobject_version_creator_id']; + $versionInfo->creationDate = (int)$row['ezcontentobject_version_created']; + $versionInfo->modificationDate = (int)$row['ezcontentobject_version_modified']; + $versionInfo->status = (int)$row['ezcontentobject_version_status']; + $versionInfo->names = $names; + + // Map language codes + $allLanguages = $this->loadAllLanguagesWithIdKey(); + $versionInfo->languageCodes = $this->extractLanguageCodesFromMask( + (int)$row['ezcontentobject_version_language_mask'], + $allLanguages, + $missing + ); + $initialLanguageId = (int)$row['ezcontentobject_version_initial_language_id']; + if (isset($allLanguages[$initialLanguageId])) { + $versionInfo->initialLanguageCode = $allLanguages[$initialLanguageId]->languageCode; + } else { + $missing[] = $initialLanguageId; + } + + if (!empty($missing)) { + throw new NotFoundException( + 'Language', + implode(', ', $missing) . "' when building content '" . $row['ezcontentobject_id'] + ); + } + + return $versionInfo; + } + + /** + * Extracts a VersionInfo object from $row. + * + * @phpstan-param TRawContentRow[] $rows + * @phpstan-param TRawContentRow[] $nameRows + * + * @return \Ibexa\Contracts\Core\Persistence\Content\VersionInfo[] + */ + public function extractVersionInfoListFromRows(array $rows, array $nameRows) + { + $nameData = []; + foreach ($nameRows as $row) { + $versionId = $row['ezcontentobject_name_contentobject_id'] . '_' . $row['ezcontentobject_name_content_version']; + $nameData[$versionId][$row['ezcontentobject_name_content_translation']] = $row['ezcontentobject_name_name']; + } + + $allLanguages = $this->loadAllLanguagesWithIdKey(); + $versionInfoList = []; + foreach ($rows as $row) { + $versionId = $row['ezcontentobject_id'] . '_' . $row['ezcontentobject_version_version']; + if (!isset($versionInfoList[$versionId])) { + $versionInfo = new VersionInfo(); + $versionInfo->id = (int)$row['ezcontentobject_version_id']; + $versionInfo->contentInfo = $this->extractContentInfoFromRow($row, 'ezcontentobject_'); + $versionInfo->versionNo = (int)$row['ezcontentobject_version_version']; + $versionInfo->creatorId = (int)$row['ezcontentobject_version_creator_id']; + $versionInfo->creationDate = (int)$row['ezcontentobject_version_created']; + $versionInfo->modificationDate = (int)$row['ezcontentobject_version_modified']; + $versionInfo->status = (int)$row['ezcontentobject_version_status']; + $versionInfo->names = $nameData[$versionId]; + $versionInfoList[$versionId] = $versionInfo; + $versionInfo->languageCodes = $this->extractLanguageCodesFromMask( + (int)$row['ezcontentobject_version_language_mask'], + $allLanguages, + $missing + ); + $initialLanguageId = (int)$row['ezcontentobject_version_initial_language_id']; + if (isset($allLanguages[$initialLanguageId])) { + $versionInfo->initialLanguageCode = $allLanguages[$initialLanguageId]->languageCode; + } else { + $missing[] = $initialLanguageId; + } + + if (!empty($missing)) { + throw new NotFoundException( + 'Language', + implode(', ', $missing) . "' when building content '" . $row['ezcontentobject_id'] + ); + } + } + } + + return array_values($versionInfoList); + } + + /** + * @param int $languageMask + * @param \Ibexa\Contracts\Core\Persistence\Content\Language[] $allLanguages + * @param int[] &$missing + * + * @return string[] + */ + private function extractLanguageCodesFromMask(int $languageMask, array $allLanguages, &$missing = []) + { + $exp = 2; + $result = []; + + // Decomposition of $languageMask into its binary components to extract language codes + while ($exp <= $languageMask) { + if ($languageMask & $exp) { + if (isset($allLanguages[$exp])) { + $result[] = $allLanguages[$exp]->languageCode; + } else { + $missing[] = $exp; + } + } + + $exp *= 2; + } + + return $result; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Language[] + */ + private function loadAllLanguagesWithIdKey() + { + $languagesById = []; + foreach ($this->languageHandler->loadAll() as $language) { + $languagesById[$language->id] = $language; + } + + return $languagesById; + } + + /** + * Extracts a Field from $row. + * + * @param array $row + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Field + */ + protected function extractFieldFromRow(array $row) + { + $field = new Field(); + + $field->id = (int)$row['ezcontentobject_attribute_id']; + $field->fieldDefinitionId = (int)$row['ezcontentobject_attribute_contentclassattribute_id']; + $field->type = $row['ezcontentobject_attribute_data_type_string']; + $field->value = $this->extractFieldValueFromRow($row, $field->type); + $field->languageCode = $row['ezcontentobject_attribute_language_code']; + $field->versionNo = isset($row['ezcontentobject_version_version']) ? + (int)$row['ezcontentobject_version_version'] : + (int)$row['ezcontentobject_attribute_version']; + + return $field; + } + + /** + * Extracts a FieldValue of $type from $row. + * + * @param array $row + * @param string $type + * + * @return \Ibexa\Contracts\Core\Persistence\Content\FieldValue + * + * @throws \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound + * if the necessary converter for $type could not be found. + */ + protected function extractFieldValueFromRow(array $row, $type) + { + $storageValue = new StorageFieldValue(); + + // Nullable field + $storageValue->dataFloat = isset($row['ezcontentobject_attribute_data_float']) + ? (float)$row['ezcontentobject_attribute_data_float'] + : null; + // Nullable field + $storageValue->dataInt = isset($row['ezcontentobject_attribute_data_int']) + ? (int)$row['ezcontentobject_attribute_data_int'] + : null; + $storageValue->dataText = $row['ezcontentobject_attribute_data_text']; + // Not nullable field + $storageValue->sortKeyInt = (int)$row['ezcontentobject_attribute_sort_key_int']; + $storageValue->sortKeyString = $row['ezcontentobject_attribute_sort_key_string']; + + $fieldValue = new FieldValue(); + + $converter = $this->converterRegistry->getConverter($type); + $converter->toFieldValue($storageValue, $fieldValue); + + return $fieldValue; + } + + /** + * Creates CreateStruct from $content. + * + * @param \Ibexa\Contracts\Core\Persistence\Content $content + * + * @return \Ibexa\Contracts\Core\Persistence\Content\CreateStruct + */ + public function createCreateStructFromContent(Content $content) + { + $struct = new CreateStruct(); + $struct->name = $content->versionInfo->names; + $struct->typeId = $content->versionInfo->contentInfo->contentTypeId; + $struct->sectionId = $content->versionInfo->contentInfo->sectionId; + $struct->ownerId = $content->versionInfo->contentInfo->ownerId; + $struct->locations = []; + $struct->alwaysAvailable = $content->versionInfo->contentInfo->alwaysAvailable; + $struct->remoteId = md5(uniqid(static::class, true)); + $struct->initialLanguageId = $this->languageHandler->loadByLanguageCode($content->versionInfo->initialLanguageCode)->id; + $struct->mainLanguageId = $this->languageHandler->loadByLanguageCode($content->versionInfo->contentInfo->mainLanguageCode)->id; + $struct->modified = time(); + $struct->isHidden = $content->versionInfo->contentInfo->isHidden; + + foreach ($content->fields as $field) { + $newField = clone $field; + $newField->id = null; + $struct->fields[] = $newField; + } + + return $struct; + } + + /** + * Extracts relation objects from $rows. + */ + public function extractRelationsFromRows(array $rows) + { + $relations = []; + + foreach ($rows as $row) { + $id = (int)$row['ezcontentobject_link_id']; + if (!isset($relations[$id])) { + $relations[$id] = $this->extractRelationFromRow($row); + } + } + + return $relations; + } + + /** + * Extracts a Relation object from a $row. + * + * @param array $row Associative array representing a relation + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Relation + */ + public function extractRelationFromRow(array $row) + { + $relation = new Relation(); + $relation->id = (int)$row['ezcontentobject_link_id']; + $relation->sourceContentId = (int)$row['ezcontentobject_link_from_contentobject_id']; + $relation->sourceContentVersionNo = (int)$row['ezcontentobject_link_from_contentobject_version']; + $relation->destinationContentId = (int)$row['ezcontentobject_link_to_contentobject_id']; + $relation->type = (int)$row['ezcontentobject_link_relation_type']; + + $contentClassAttributeId = (int)$row['ezcontentobject_link_contentclassattribute_id']; + if ($contentClassAttributeId > 0) { + $relation->sourceFieldDefinitionId = $contentClassAttributeId; + } + + return $relation; + } + + /** + * Creates a Content from the given $struct. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Relation\CreateStruct $struct + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Relation + */ + public function createRelationFromCreateStruct(RelationCreateStruct $struct) + { + $relation = new Relation(); + + $relation->destinationContentId = $struct->destinationContentId; + $relation->sourceContentId = $struct->sourceContentId; + $relation->sourceContentVersionNo = $struct->sourceContentVersionNo; + $relation->sourceFieldDefinitionId = $struct->sourceFieldDefinitionId; + $relation->type = $struct->type; + + return $relation; + } +} + +class_alias(Mapper::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Mapper'); diff --git a/src/lib/Persistence/Legacy/Content/Mapper/ResolveVirtualFieldSubscriber.php b/src/lib/Persistence/Legacy/Content/Mapper/ResolveVirtualFieldSubscriber.php index 2443586f34..36903a50f3 100644 --- a/src/lib/Persistence/Legacy/Content/Mapper/ResolveVirtualFieldSubscriber.php +++ b/src/lib/Persistence/Legacy/Content/Mapper/ResolveVirtualFieldSubscriber.php @@ -8,30 +8,27 @@ namespace Ibexa\Core\Persistence\Legacy\Content\Mapper; -use eZ\Publish\Core\FieldType\NullStorage; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; use Ibexa\Contracts\Core\Event\Mapper\ResolveMissingFieldEvent; use Ibexa\Contracts\Core\FieldType\DefaultDataFieldStorage; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Core\FieldType\NullStorage; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; +use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use Ibexa\Core\Persistence\Legacy\Content\StorageRegistry; use Symfony\Component\EventDispatcher\EventSubscriberInterface; final class ResolveVirtualFieldSubscriber implements EventSubscriberInterface { - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry */ - private $converterRegistry; + private ConverterRegistry $converterRegistry; - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry */ - private $storageRegistry; + private StorageRegistry $storageRegistry; - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway */ - private $contentGateway; + private ContentGateway $contentGateway; public function __construct( ConverterRegistry $converterRegistry, @@ -76,13 +73,18 @@ public function resolveVirtualField(ResolveMissingFieldEvent $event): void } /** - * @throws \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound + * @throws \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound */ public function persistExternalStorageField(ResolveMissingFieldEvent $event): void { $field = $event->getField(); - if ($field !== null && $field->id !== null) { + if ($field === null) { + // Nothing to persist + return; + } + + if ($field->id !== null) { // Not a virtual field return; } @@ -138,7 +140,12 @@ public function resolveVirtualExternalStorageField(ResolveMissingFieldEvent $eve { $field = $event->getField(); - if ($field !== null && $field->id !== null) { + if ($field === null) { + // Nothing to resolve + return; + } + + if ($field->id !== null) { // Not a virtual field return; } @@ -169,7 +176,7 @@ public function resolveVirtualExternalStorageField(ResolveMissingFieldEvent $eve } /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ private function createEmptyField( VersionInfo $versionInfo, @@ -187,7 +194,7 @@ private function createEmptyField( } /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ private function getDefaultValue(FieldDefinition $fieldDefinition): FieldValue { @@ -204,8 +211,8 @@ private function getDefaultValue(FieldDefinition $fieldDefinition): FieldValue private function getDefaultStorageValue(): StorageFieldValue { $storageValue = new StorageFieldValue(); - $storageValue->dataFloat = null; - $storageValue->dataInt = null; + $storageValue->dataFloat = 0; + $storageValue->dataInt = 0; $storageValue->dataText = ''; $storageValue->sortKeyInt = 0; $storageValue->sortKeyString = ''; diff --git a/src/lib/Persistence/Legacy/Content/MultilingualStorageFieldDefinition.php b/src/lib/Persistence/Legacy/Content/MultilingualStorageFieldDefinition.php new file mode 100644 index 0000000000..e2b8e89a6c --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/MultilingualStorageFieldDefinition.php @@ -0,0 +1,31 @@ +connection = $connection; + $this->dbPlatform = $this->connection->getDatabasePlatform(); + $this->maskGenerator = $maskGenerator; + } + + public function loadObjectStateData(int $stateId): array + { + $query = $this->createObjectStateFindQuery(); + $query->where( + $query->expr()->eq( + 'state.id', + $query->createPositionalParameter($stateId, ParameterType::INTEGER) + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadObjectStateDataByIdentifier(string $identifier, int $groupId): array + { + $query = $this->createObjectStateFindQuery(); + $query->where( + $query->expr()->andX( + $query->expr()->eq( + 'state.identifier', + $query->createPositionalParameter($identifier, ParameterType::STRING) + ), + $query->expr()->eq( + 'state.group_id', + $query->createPositionalParameter($groupId, ParameterType::INTEGER) + ) + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadObjectStateListData(int $groupId): array + { + $query = $this->createObjectStateFindQuery(); + $query->where( + $query->expr()->eq( + 'state.group_id', + $query->createPositionalParameter($groupId, ParameterType::INTEGER) + ) + )->orderBy('state.priority', 'ASC'); + + $statement = $query->execute(); + + $rows = []; + while ($row = $statement->fetch(FetchMode::ASSOCIATIVE)) { + $rows[$row['ezcobj_state_id']][] = $row; + } + + return array_values($rows); + } + + public function loadObjectStateGroupData(int $groupId): array + { + $query = $this->createObjectStateGroupFindQuery(); + $query->where( + $query->expr()->eq( + 'state_group.id', + $query->createPositionalParameter($groupId, ParameterType::INTEGER) + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadObjectStateGroupDataByIdentifier(string $identifier): array + { + $query = $this->createObjectStateGroupFindQuery(); + $query->where( + $query->expr()->eq( + 'state_group.identifier', + $query->createPositionalParameter($identifier, ParameterType::STRING) + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadObjectStateGroupListData(int $offset, int $limit): array + { + $query = $this->createObjectStateGroupFindQuery(); + if ($limit > 0) { + $query->setMaxResults($limit); + $query->setFirstResult($offset); + } + + $statement = $query->execute(); + + $rows = []; + while ($row = $statement->fetch(FetchMode::ASSOCIATIVE)) { + $rows[$row['ezcobj_state_group_id']][] = $row; + } + + return array_values($rows); + } + + /** + * @throws \Doctrine\DBAL\DBALException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function insertObjectState(ObjectState $objectState, int $groupId): void + { + $maxPriority = $this->getMaxPriorityForObjectStatesInGroup($groupId); + + $objectState->priority = $maxPriority === null ? 0 : (int)$maxPriority + 1; + $objectState->groupId = (int)$groupId; + + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::OBJECT_STATE_TABLE) + ->values( + [ + 'group_id' => $query->createPositionalParameter( + $objectState->groupId, + ParameterType::INTEGER + ), + 'default_language_id' => $query->createPositionalParameter( + $this->maskGenerator->generateLanguageIndicator( + $objectState->defaultLanguage, + false + ), + ParameterType::INTEGER + ), + 'identifier' => $query->createPositionalParameter( + $objectState->identifier, + ParameterType::STRING + ), + 'language_mask' => $query->createPositionalParameter( + $this->maskGenerator->generateLanguageMaskFromLanguageCodes( + $objectState->languageCodes, + true + ), + ParameterType::INTEGER + ), + 'priority' => $query->createPositionalParameter( + $objectState->priority, + ParameterType::INTEGER + ), + ] + ); + + $query->execute(); + + $objectState->id = (int)$this->connection->lastInsertId(self::OBJECT_STATE_TABLE_SEQ); + + $this->insertObjectStateTranslations($objectState); + + // If this is a first state in group, assign it to all content objects + if ($maxPriority === null) { + $this->connection->executeUpdate( + 'INSERT INTO ezcobj_state_link (contentobject_id, contentobject_state_id) ' . + "SELECT id, {$objectState->id} FROM ezcontentobject" + ); + } + } + + /** + * @param string[] $languageCodes + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function updateObjectStateCommonFields( + string $tableName, + int $id, + string $identifier, + string $defaultLanguageCode, + array $languageCodes + ): void { + $query = $this->connection->createQueryBuilder(); + $query + ->update($tableName) + ->set( + 'default_language_id', + $query->createPositionalParameter( + $this->maskGenerator->generateLanguageIndicator( + $defaultLanguageCode, + false + ), + ParameterType::INTEGER + ) + ) + ->set( + 'identifier', + $query->createPositionalParameter( + $identifier, + ParameterType::STRING + ) + ) + ->set( + 'language_mask', + $query->createPositionalParameter( + $this->maskGenerator->generateLanguageMaskFromLanguageCodes( + $languageCodes, + true + ), + ParameterType::INTEGER + ) + ) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + + $query->execute(); + } + + public function updateObjectState(ObjectState $objectState): void + { + // First update the state + $this->updateObjectStateCommonFields( + self::OBJECT_STATE_TABLE, + $objectState->id, + $objectState->identifier, + $objectState->defaultLanguage, + $objectState->languageCodes + ); + + // And then refresh object state translations + // by removing existing ones and adding new ones + $this->deleteObjectStateTranslations($objectState->id); + $this->insertObjectStateTranslations($objectState); + } + + public function deleteObjectState(int $stateId): void + { + $this->deleteObjectStateTranslations($stateId); + + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::OBJECT_STATE_TABLE) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($stateId, ParameterType::INTEGER) + ) + ); + + $query->execute(); + } + + public function updateObjectStateLinks(int $oldStateId, int $newStateId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::OBJECT_STATE_LINK_TABLE) + ->set( + 'contentobject_state_id', + $query->createPositionalParameter($newStateId, ParameterType::INTEGER) + ) + ->where( + $query->expr()->eq( + 'contentobject_state_id', + $query->createPositionalParameter($oldStateId, ParameterType::INTEGER) + ) + ) + ; + + $query->execute(); + } + + /** + * Change Content to object state assignment. + */ + private function updateContentStateAssignment( + int $contentId, + int $stateId, + int $assignedStateId + ): void { + // no action required if there's no change + if ($stateId === $assignedStateId) { + return; + } + + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::OBJECT_STATE_LINK_TABLE) + ->set( + 'contentobject_state_id', + $query->createPositionalParameter($stateId, ParameterType::INTEGER) + ) + ->where( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ) + ->andWhere( + $query->expr()->eq( + 'contentobject_state_id', + $query->createPositionalParameter($assignedStateId, ParameterType::INTEGER) + ) + ) + ; + + $query->execute(); + } + + public function deleteObjectStateLinks(int $stateId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::OBJECT_STATE_LINK_TABLE) + ->where( + $query->expr()->eq( + 'contentobject_state_id', + $query->createPositionalParameter($stateId, ParameterType::INTEGER) + ) + ); + + $query->execute(); + } + + public function insertObjectStateGroup(Group $objectStateGroup): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::OBJECT_STATE_GROUP_TABLE) + ->values( + [ + 'default_language_id' => $query->createPositionalParameter( + $this->maskGenerator->generateLanguageIndicator( + $objectStateGroup->defaultLanguage, + false + ), + ParameterType::INTEGER + ), + 'identifier' => $query->createPositionalParameter( + $objectStateGroup->identifier, + ParameterType::STRING + ), + 'language_mask' => $query->createPositionalParameter( + $this->maskGenerator->generateLanguageMaskFromLanguageCodes( + $objectStateGroup->languageCodes, + true + ), + ParameterType::INTEGER + ), + ] + ) + ; + + $query->execute(); + + $objectStateGroup->id = (int)$this->connection->lastInsertId( + self::OBJECT_STATE_GROUP_TABLE_SEQ + ); + + $this->insertObjectStateGroupTranslations($objectStateGroup); + } + + public function updateObjectStateGroup(Group $objectStateGroup): void + { + // First update the group + $this->updateObjectStateCommonFields( + self::OBJECT_STATE_GROUP_TABLE, + $objectStateGroup->id, + $objectStateGroup->identifier, + $objectStateGroup->defaultLanguage, + $objectStateGroup->languageCodes + ); + + // And then refresh group translations + // by removing old ones and adding new ones + $this->deleteObjectStateGroupTranslations($objectStateGroup->id); + $this->insertObjectStateGroupTranslations($objectStateGroup); + } + + public function deleteObjectStateGroup(int $groupId): void + { + $this->deleteObjectStateGroupTranslations($groupId); + + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::OBJECT_STATE_GROUP_TABLE) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($groupId, ParameterType::INTEGER) + ) + ) + ; + + $query->execute(); + } + + public function setContentState(int $contentId, int $groupId, int $stateId): void + { + // First find out if $contentId is related to existing states in $groupId + $assignedStateId = $this->getContentStateId($contentId, $groupId); + + if (null !== $assignedStateId) { + // We already have a state assigned to $contentId, update to new one + $this->updateContentStateAssignment($contentId, $stateId, $assignedStateId); + } else { + // No state assigned to $contentId from specified group, create assignment + $this->insertContentStateAssignment($contentId, $stateId); + } + } + + public function loadObjectStateDataForContent(int $contentId, int $stateGroupId): array + { + $query = $this->createObjectStateFindQuery(); + $expr = $query->expr(); + $query + ->innerJoin( + 'state', + 'ezcobj_state_link', + 'link', + 'state.id = link.contentobject_state_id' + ) + ->where( + $expr->eq( + 'state.group_id', + $query->createPositionalParameter($stateGroupId, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + 'link.contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function getContentCount(int $stateId): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + $this->dbPlatform->getCountExpression('contentobject_id') + ) + ->from(self::OBJECT_STATE_LINK_TABLE) + ->where( + $query->expr()->eq( + 'contentobject_state_id', + $query->createPositionalParameter($stateId, ParameterType::INTEGER) + ) + ); + + return (int)$query->execute()->fetchColumn(); + } + + public function updateObjectStatePriority(int $stateId, int $priority): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::OBJECT_STATE_TABLE) + ->set('priority', $query->createPositionalParameter($priority, ParameterType::INTEGER)) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($stateId, ParameterType::INTEGER) + ) + ) + ; + + $query->execute(); + } + + /** + * Create a generic query for fetching object state(s). + */ + private function createObjectStateFindQuery(): QueryBuilder + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + // Object state + 'state.default_language_id AS ezcobj_state_default_language_id', + 'state.group_id AS ezcobj_state_group_id', + 'state.id AS ezcobj_state_id', + 'state.identifier AS ezcobj_state_identifier', + 'state.language_mask AS ezcobj_state_language_mask', + 'state.priority AS ezcobj_state_priority', + // Object state language + 'lang.description AS ezcobj_state_language_description', + 'lang.language_id AS ezcobj_state_language_language_id', + 'lang.name AS ezcobj_state_language_name' + ) + ->from(self::OBJECT_STATE_TABLE, 'state') + ->innerJoin( + 'state', + self::OBJECT_STATE_LANGUAGE_TABLE, + 'lang', + 'state.id = lang.contentobject_state_id', + ); + + return $query; + } + + /** + * Create a generic query for fetching object state group(s). + */ + private function createObjectStateGroupFindQuery(): QueryBuilder + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + // Object state group + 'state_group.default_language_id AS ezcobj_state_group_default_language_id', + 'state_group.id AS ezcobj_state_group_id', + 'state_group.identifier AS ezcobj_state_group_identifier', + 'state_group.language_mask AS ezcobj_state_group_language_mask', + // Object state group language + 'state_group_lang.description AS ezcobj_state_group_language_description', + 'state_group_lang.language_id AS ezcobj_state_group_language_language_id', + 'state_group_lang.real_language_id AS ezcobj_state_group_language_real_language_id', + 'state_group_lang.name AS ezcobj_state_group_language_name' + ) + ->from(self::OBJECT_STATE_GROUP_TABLE, 'state_group') + ->innerJoin( + 'state_group', + self::OBJECT_STATE_GROUP_LANGUAGE_TABLE, + 'state_group_lang', + 'state_group.id = state_group_lang.contentobject_state_group_id' + ); + + return $query; + } + + /** + * Insert object state group translations into database. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if Object State language does not exist + */ + private function insertObjectStateTranslations(ObjectState $objectState): void + { + foreach ($objectState->languageCodes as $languageCode) { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::OBJECT_STATE_LANGUAGE_TABLE) + ->values( + [ + 'contentobject_state_id' => $query->createPositionalParameter( + $objectState->id, + ParameterType::INTEGER + ), + 'description' => $query->createPositionalParameter( + $objectState->description[$languageCode], + ParameterType::STRING + ), + 'name' => $query->createPositionalParameter( + $objectState->name[$languageCode], + ParameterType::STRING + ), + 'language_id' => $query->createPositionalParameter( + $this->maskGenerator->generateLanguageIndicator( + $languageCode, + $languageCode === $objectState->defaultLanguage + ), + ParameterType::INTEGER + ), + ] + ); + + $query->execute(); + } + } + + /** + * Deletes all translations of the $stateId state. + * + * @param mixed $stateId + */ + private function deleteObjectStateTranslations(int $stateId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::OBJECT_STATE_LANGUAGE_TABLE) + ->where( + $query->expr()->eq( + 'contentobject_state_id', + $query->createPositionalParameter($stateId, ParameterType::INTEGER) + ) + ); + + $query->execute(); + } + + /** + * Insert object state group translations into database. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if Object State Group language does not exist + */ + private function insertObjectStateGroupTranslations(Group $objectStateGroup): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::OBJECT_STATE_GROUP_LANGUAGE_TABLE) + ->values( + [ + 'contentobject_state_group_id' => ':contentobject_state_group_id', + 'description' => ':description', + 'name' => ':name', + 'language_id' => ':language_id', + 'real_language_id' => ':real_language_id', + ] + ) + ; + foreach ($objectStateGroup->languageCodes as $languageCode) { + $languageId = $this->maskGenerator->generateLanguageIndicator( + $languageCode, + $languageCode === $objectStateGroup->defaultLanguage + ); + $query + ->setParameter('contentobject_state_group_id', $objectStateGroup->id, ParameterType::INTEGER) + ->setParameter('description', $objectStateGroup->description[$languageCode], ParameterType::STRING) + ->setParameter('name', $objectStateGroup->name[$languageCode], ParameterType::STRING) + ->setParameter('language_id', $languageId, ParameterType::INTEGER) + ->setParameter('real_language_id', $languageId & ~1, ParameterType::INTEGER); + + $query->execute(); + } + } + + /** + * Delete all translations of the $groupId state group. + */ + private function deleteObjectStateGroupTranslations(int $groupId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::OBJECT_STATE_GROUP_LANGUAGE_TABLE) + ->where( + $query->expr()->eq( + 'contentobject_state_group_id', + $query->createPositionalParameter($groupId, ParameterType::INTEGER) + ) + ); + + $query->execute(); + } + + private function getMaxPriorityForObjectStatesInGroup(int $groupId): ?int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + $this->dbPlatform->getMaxExpression('priority') + ) + ->from(self::OBJECT_STATE_TABLE) + ->where( + $query->expr()->eq( + 'group_id', + $query->createPositionalParameter($groupId, ParameterType::INTEGER) + ) + ); + + $priority = $query->execute()->fetchColumn(); + + return null !== $priority ? (int)$priority : null; + } + + private function getContentStateId(int $contentId, int $groupId): ?int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('state.id') + ->from(self::OBJECT_STATE_TABLE, 'state') + ->innerJoin( + 'state', + self::OBJECT_STATE_LINK_TABLE, + 'link', + 'state.id = link.contentobject_state_id' + ) + ->where( + $query->expr()->eq( + 'state.group_id', + $query->createPositionalParameter($groupId, ParameterType::INTEGER) + ) + ) + ->andWhere( + $query->expr()->eq( + 'link.contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ); + + $stateId = $query->execute()->fetch(FetchMode::COLUMN); + + return false !== $stateId ? (int)$stateId : null; + } + + private function insertContentStateAssignment(int $contentId, int $stateId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::OBJECT_STATE_LINK_TABLE) + ->values( + [ + 'contentobject_id' => $query->createPositionalParameter( + $contentId, + ParameterType::INTEGER + ), + 'contentobject_state_id' => $query->createPositionalParameter( + $stateId, + ParameterType::INTEGER + ), + ] + ); + + $query->execute(); + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..f205861ec5 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/ObjectState/Gateway/ExceptionConversion.php @@ -0,0 +1,203 @@ +innerGateway = $innerGateway; + } + + public function loadObjectStateData(int $stateId): array + { + try { + return $this->innerGateway->loadObjectStateData($stateId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadObjectStateDataByIdentifier(string $identifier, int $groupId): array + { + try { + return $this->innerGateway->loadObjectStateDataByIdentifier($identifier, $groupId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadObjectStateListData(int $groupId): array + { + try { + return $this->innerGateway->loadObjectStateListData($groupId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadObjectStateGroupData(int $groupId): array + { + try { + return $this->innerGateway->loadObjectStateGroupData($groupId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadObjectStateGroupDataByIdentifier(string $identifier): array + { + try { + return $this->innerGateway->loadObjectStateGroupDataByIdentifier($identifier); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadObjectStateGroupListData(int $offset, int $limit): array + { + try { + return $this->innerGateway->loadObjectStateGroupListData($offset, $limit); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function insertObjectState(ObjectState $objectState, int $groupId): void + { + try { + $this->innerGateway->insertObjectState($objectState, $groupId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateObjectState(ObjectState $objectState): void + { + try { + $this->innerGateway->updateObjectState($objectState); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteObjectState(int $stateId): void + { + try { + $this->innerGateway->deleteObjectState($stateId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateObjectStateLinks(int $oldStateId, int $newStateId): void + { + try { + $this->innerGateway->updateObjectStateLinks($oldStateId, $newStateId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteObjectStateLinks(int $stateId): void + { + try { + $this->innerGateway->deleteObjectStateLinks($stateId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function insertObjectStateGroup(Group $objectStateGroup): void + { + try { + $this->innerGateway->insertObjectStateGroup($objectStateGroup); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateObjectStateGroup(Group $objectStateGroup): void + { + try { + $this->innerGateway->updateObjectStateGroup($objectStateGroup); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteObjectStateGroup(int $groupId): void + { + try { + $this->innerGateway->deleteObjectStateGroup($groupId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function setContentState(int $contentId, int $groupId, int $stateId): void + { + try { + $this->innerGateway->setContentState($contentId, $groupId, $stateId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadObjectStateDataForContent(int $contentId, int $stateGroupId): array + { + try { + return $this->innerGateway->loadObjectStateDataForContent($contentId, $stateGroupId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getContentCount(int $stateId): int + { + try { + return $this->innerGateway->getContentCount($stateId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateObjectStatePriority(int $stateId, int $priority): void + { + try { + $this->innerGateway->updateObjectStatePriority($stateId, $priority); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/Content/ObjectState/Handler.php b/src/lib/Persistence/Legacy/Content/ObjectState/Handler.php new file mode 100644 index 0000000000..d27e0920b3 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/ObjectState/Handler.php @@ -0,0 +1,354 @@ +objectStateGateway = $objectStateGateway; + $this->objectStateMapper = $objectStateMapper; + } + + /** + * Creates a new object state group. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct $input + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group + */ + public function createGroup(InputStruct $input) + { + $objectStateGroup = $this->objectStateMapper->createObjectStateGroupFromInputStruct($input); + $this->objectStateGateway->insertObjectStateGroup($objectStateGroup); + + return $objectStateGroup; + } + + /** + * Loads an object state group. + * + * @param mixed $groupId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the group was not found + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group + */ + public function loadGroup($groupId) + { + $data = $this->objectStateGateway->loadObjectStateGroupData($groupId); + + if (empty($data)) { + throw new NotFoundException('ObjectStateGroup', $groupId); + } + + return $this->objectStateMapper->createObjectStateGroupFromData($data); + } + + /** + * Loads a object state group by identifier. + * + * @param string $identifier + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the group was not found + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group + */ + public function loadGroupByIdentifier($identifier) + { + $data = $this->objectStateGateway->loadObjectStateGroupDataByIdentifier($identifier); + + if (empty($data)) { + throw new NotFoundException('ObjectStateGroup', $identifier); + } + + return $this->objectStateMapper->createObjectStateGroupFromData($data); + } + + /** + * Loads all object state groups. + * + * @param int $offset + * @param int $limit + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group[] + */ + public function loadAllGroups($offset = 0, $limit = -1) + { + $data = $this->objectStateGateway->loadObjectStateGroupListData($offset, $limit); + + return $this->objectStateMapper->createObjectStateGroupListFromData($data); + } + + /** + * This method returns the ordered list of object states of a group. + * + * @param mixed $groupId + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState[] + */ + public function loadObjectStates($groupId) + { + $data = $this->objectStateGateway->loadObjectStateListData($groupId); + + return $this->objectStateMapper->createObjectStateListFromData($data); + } + + /** + * Updates an object state group. + * + * @param mixed $groupId + * @param \Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct $input + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group + */ + public function updateGroup($groupId, InputStruct $input) + { + $objectStateGroup = $this->objectStateMapper->createObjectStateGroupFromInputStruct($input); + $objectStateGroup->id = (int)$groupId; + + $this->objectStateGateway->updateObjectStateGroup($objectStateGroup); + + return $this->loadGroup($objectStateGroup->id); + } + + /** + * Deletes a object state group including all states and links to content. + * + * @param mixed $groupId + */ + public function deleteGroup($groupId) + { + $objectStates = $this->loadObjectStates($groupId); + foreach ($objectStates as $objectState) { + $this->objectStateGateway->deleteObjectStateLinks($objectState->id); + $this->objectStateGateway->deleteObjectState($objectState->id); + } + + $this->objectStateGateway->deleteObjectStateGroup($groupId); + } + + /** + * Creates a new object state in the given group. + * The new state gets the last priority. + * Note: in current kernel: If it is the first state all content objects will + * set to this state. + * + * @param mixed $groupId + * @param \Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct $input + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState + */ + public function create($groupId, InputStruct $input) + { + $objectState = $this->objectStateMapper->createObjectStateFromInputStruct($input); + $this->objectStateGateway->insertObjectState($objectState, $groupId); + + return $objectState; + } + + /** + * Loads an object state. + * + * @param mixed $stateId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the state was not found + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState + */ + public function load($stateId) + { + $data = $this->objectStateGateway->loadObjectStateData($stateId); + + if (empty($data)) { + throw new NotFoundException('ObjectState', $stateId); + } + + return $this->objectStateMapper->createObjectStateFromData($data); + } + + /** + * Loads an object state by identifier and group it belongs to. + * + * @param string $identifier + * @param mixed $groupId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the state was not found + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState + */ + public function loadByIdentifier($identifier, $groupId) + { + $data = $this->objectStateGateway->loadObjectStateDataByIdentifier($identifier, $groupId); + + if (empty($data)) { + throw new NotFoundException('ObjectState', ['identifier' => $identifier, 'groupId' => $groupId]); + } + + return $this->objectStateMapper->createObjectStateFromData($data); + } + + /** + * Updates an object state. + * + * @param mixed $stateId + * @param \Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct $input + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState + */ + public function update($stateId, InputStruct $input) + { + $objectState = $this->objectStateMapper->createObjectStateFromInputStruct($input); + $objectState->id = (int)$stateId; + + $this->objectStateGateway->updateObjectState($objectState); + + return $this->load($objectState->id); + } + + /** + * Changes the priority of the state. + * + * @param mixed $stateId + * @param int $priority + */ + public function setPriority($stateId, $priority) + { + $objectState = $this->load($stateId); + $groupObjectStates = $this->loadObjectStates($objectState->groupId); + + $priorityList = []; + foreach ($groupObjectStates as $index => $groupObjectState) { + // Update given state and push all other states with same or higher priority down + if ($objectState->id === $groupObjectState->id) { + $priorityList[$groupObjectState->id] = (int)$priority; + } else { + $priorityList[$groupObjectState->id] = ($index < $priority ? $index : $index + 1); + } + } + + asort($priorityList, SORT_NUMERIC); + + foreach (array_keys($priorityList) as $objectStatePriority => $objectStateId) { + $this->objectStateGateway->updateObjectStatePriority($objectStateId, $objectStatePriority); + } + } + + /** + * Deletes a object state. The state of the content objects is reset to the + * first object state in the group. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If state with $stateId doesn't exist + * + * @param mixed $stateId + */ + public function delete($stateId) + { + // Get the object state first as we need group ID + // to reorder the priorities and reassign content to another state in the group + $objectState = $this->load($stateId); + + $this->objectStateGateway->deleteObjectState($objectState->id); + + $remainingStates = $this->loadObjectStates($objectState->groupId); + if (empty($remainingStates)) { + // If there are no more states in the group, just remove the state links + $this->objectStateGateway->deleteObjectStateLinks($objectState->id); + + return; + } + + $priority = 0; + foreach ($remainingStates as $remainingState) { + $this->objectStateGateway->updateObjectStatePriority($remainingState->id, $priority); + ++$priority; + } + + $remainingStates = $this->loadObjectStates($objectState->groupId); + $this->objectStateGateway->updateObjectStateLinks($objectState->id, current($remainingStates)->id); + } + + /** + * Sets the object-state of a state group to $stateId for the given content. + * + * @param mixed $contentId + * @param mixed $groupId + * @param mixed $stateId + * + * @return bool + */ + public function setContentState($contentId, $groupId, $stateId) + { + $this->objectStateGateway->setContentState($contentId, $groupId, $stateId); + + return true; + } + + /** + * Gets the object-state of object identified by $contentId. + * + * The $state is the id of the state within one group. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If no state is found + * + * @param mixed $contentId + * @param mixed $stateGroupId + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState + */ + public function getContentState($contentId, $stateGroupId) + { + $data = $this->objectStateGateway->loadObjectStateDataForContent($contentId, $stateGroupId); + + if (empty($data)) { + throw new NotFoundException('ObjectState', ['groupId' => $stateGroupId]); + } + + return $this->objectStateMapper->createObjectStateFromData($data); + } + + /** + * Returns the number of objects which are in this state. + * + * @param mixed $stateId + * + * @return int + */ + public function getContentCount($stateId) + { + return $this->objectStateGateway->getContentCount($stateId); + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Handler'); diff --git a/src/lib/Persistence/Legacy/Content/ObjectState/Mapper.php b/src/lib/Persistence/Legacy/Content/ObjectState/Mapper.php new file mode 100644 index 0000000000..da6cb8d1f4 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/ObjectState/Mapper.php @@ -0,0 +1,195 @@ +languageHandler = $languageHandler; + } + + /** + * Creates ObjectState object from provided $data. + * + * @param array $data + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState + */ + public function createObjectStateFromData(array $data) + { + $objectState = new ObjectState(); + + $languageIds = [(int)$data[0]['ezcobj_state_default_language_id']]; + foreach ($data as $stateTranslation) { + $languageIds[] = (int)$stateTranslation['ezcobj_state_language_language_id'] & ~1; + } + $languages = $this->languageHandler->loadList($languageIds); + + $objectState->id = (int)$data[0]['ezcobj_state_id']; + $objectState->groupId = (int)$data[0]['ezcobj_state_group_id']; + $objectState->identifier = $data[0]['ezcobj_state_identifier']; + $objectState->priority = (int)$data[0]['ezcobj_state_priority']; + $objectState->defaultLanguage = $languages[(int)$data[0]['ezcobj_state_default_language_id']]->languageCode; + + $objectState->languageCodes = []; + $objectState->name = []; + $objectState->description = []; + + foreach ($data as $stateTranslation) { + $languageCode = $languages[$stateTranslation['ezcobj_state_language_language_id'] & ~1]->languageCode; + $objectState->languageCodes[] = $languageCode; + $objectState->name[$languageCode] = $stateTranslation['ezcobj_state_language_name']; + $objectState->description[$languageCode] = $stateTranslation['ezcobj_state_language_description']; + } + + return $objectState; + } + + /** + * Creates ObjectState array of objects from provided $data. + * + * @param array $data + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState[] + */ + public function createObjectStateListFromData(array $data) + { + $objectStates = []; + + foreach ($data as $objectStateData) { + $objectStates[] = $this->createObjectStateFromData($objectStateData); + } + + return $objectStates; + } + + /** + * Creates ObjectStateGroup object from provided $data. + * + * @param array $data + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group + */ + public function createObjectStateGroupFromData(array $data) + { + $objectStateGroup = new Group(); + + $languageIds = [(int)$data[0]['ezcobj_state_group_default_language_id']]; + foreach ($data as $groupTranslation) { + $languageIds[] = (int)$groupTranslation['ezcobj_state_group_language_real_language_id']; + } + $languages = $this->languageHandler->loadList($languageIds); + + $objectStateGroup->id = (int)$data[0]['ezcobj_state_group_id']; + $objectStateGroup->identifier = $data[0]['ezcobj_state_group_identifier']; + $objectStateGroup->defaultLanguage = $languages[ + (int)$data[0]['ezcobj_state_group_default_language_id'] + ]->languageCode; + + $objectStateGroup->languageCodes = []; + $objectStateGroup->name = []; + $objectStateGroup->description = []; + + foreach ($data as $groupTranslation) { + $languageCode = $languages[(int)$groupTranslation['ezcobj_state_group_language_real_language_id']]->languageCode; + $objectStateGroup->languageCodes[] = $languageCode; + $objectStateGroup->name[$languageCode] = $groupTranslation['ezcobj_state_group_language_name']; + $objectStateGroup->description[$languageCode] = $groupTranslation['ezcobj_state_group_language_description']; + } + + return $objectStateGroup; + } + + /** + * Creates ObjectStateGroup array of objects from provided $data. + * + * @param array $data + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group[] + */ + public function createObjectStateGroupListFromData(array $data) + { + $objectStateGroups = []; + + foreach ($data as $objectStateGroupData) { + $objectStateGroups[] = $this->createObjectStateGroupFromData($objectStateGroupData); + } + + return $objectStateGroups; + } + + /** + * Creates an instance of ObjectStateGroup object from provided $input struct. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct $input + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group + */ + public function createObjectStateGroupFromInputStruct(InputStruct $input) + { + $objectStateGroup = new Group(); + + $objectStateGroup->identifier = $input->identifier; + $objectStateGroup->defaultLanguage = $input->defaultLanguage; + $objectStateGroup->name = $input->name; + $objectStateGroup->description = $input->description; + + $objectStateGroup->languageCodes = []; + foreach ($input->name as $languageCode => $name) { + $objectStateGroup->languageCodes[] = $languageCode; + } + + return $objectStateGroup; + } + + /** + * Creates an instance of ObjectState object from provided $input struct. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct $input + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState + */ + public function createObjectStateFromInputStruct(InputStruct $input) + { + $objectState = new ObjectState(); + + $objectState->identifier = $input->identifier; + $objectState->defaultLanguage = $input->defaultLanguage; + $objectState->name = $input->name; + $objectState->description = $input->description; + + $objectState->languageCodes = []; + foreach ($input->name as $languageCode => $name) { + $objectState->languageCodes[] = $languageCode; + } + + return $objectState; + } +} + +class_alias(Mapper::class, 'eZ\Publish\Core\Persistence\Legacy\Content\ObjectState\Mapper'); diff --git a/src/lib/Persistence/Legacy/Content/Section/Gateway.php b/src/lib/Persistence/Legacy/Content/Section/Gateway.php new file mode 100644 index 0000000000..cb79ce1e52 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Section/Gateway.php @@ -0,0 +1,74 @@ +connection = $connection; + $this->dbPlatform = $this->connection->getDatabasePlatform(); + } + + public function insertSection(string $name, string $identifier): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::CONTENT_SECTION_TABLE) + ->values( + [ + 'name' => $query->createPositionalParameter($name), + 'identifier' => $query->createPositionalParameter($identifier), + ] + ); + + $query->execute(); + + return (int)$this->connection->lastInsertId(Gateway::CONTENT_SECTION_SEQ); + } + + public function updateSection(int $id, string $name, string $identifier): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::CONTENT_SECTION_TABLE) + ->set('name', $query->createPositionalParameter($name)) + ->set('identifier', $query->createPositionalParameter($identifier)) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + + $query->execute(); + } + + public function loadSectionData(int $id): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(['id', 'identifier', 'name']) + ->from(self::CONTENT_SECTION_TABLE) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadAllSectionData(): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(['id', 'identifier', 'name']) + ->from(self::CONTENT_SECTION_TABLE); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadSectionDataByIdentifier(string $identifier): array + { + $query = $this->connection->createQueryBuilder(); + $query->select( + 'id', + 'identifier', + 'name' + )->from( + self::CONTENT_SECTION_TABLE + )->where( + $query->expr()->eq( + 'identifier', + $query->createPositionalParameter($identifier, ParameterType::STRING) + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function countContentObjectsInSection(int $id): int + { + $query = $this->connection->createQueryBuilder(); + $query->select( + $this->dbPlatform->getCountExpression('id') + )->from( + 'ezcontentobject' + )->where( + $query->expr()->eq( + 'section_id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + + $statement = $query->execute(); + + return (int)$statement->fetchColumn(); + } + + public function countPoliciesUsingSection(int $id): int + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select($this->dbPlatform->getCountExpression('l.id')) + ->from('ezpolicy_limitation', 'l') + ->join( + 'l', + 'ezpolicy_limitation_value', + 'lv', + $expr->eq( + 'l.id', + 'lv.limitation_id' + ) + ) + ->where( + $expr->eq( + 'l.identifier', + $query->createPositionalParameter('Section', ParameterType::STRING) + ) + ) + ->andWhere( + $expr->eq( + 'lv.value', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ) + ; + + return (int)$query->execute()->fetchColumn(); + } + + public function countRoleAssignmentsUsingSection(int $id): int + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select($this->dbPlatform->getCountExpression('ur.id')) + ->from('ezuser_role', 'ur') + ->where( + $expr->eq( + 'ur.limit_identifier', + $query->createPositionalParameter('Section', ParameterType::STRING) + ) + ) + ->andWhere( + $expr->eq( + 'ur.limit_value', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ) + ; + + return (int)$query->execute()->fetchColumn(); + } + + public function deleteSection(int $id): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::CONTENT_SECTION_TABLE) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + + $query->execute(); + } + + public function assignSectionToContent(int $sectionId, int $contentId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update('ezcontentobject') + ->set( + 'section_id', + $query->createPositionalParameter($sectionId, ParameterType::INTEGER) + ) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ); + + $query->execute(); + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/Content/Section/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Content/Section/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..0df7abe090 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Section/Gateway/ExceptionConversion.php @@ -0,0 +1,129 @@ +innerGateway = $innerGateway; + } + + public function insertSection(string $name, string $identifier): int + { + try { + return $this->innerGateway->insertSection($name, $identifier); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateSection(int $id, string $name, string $identifier): void + { + try { + $this->innerGateway->updateSection($id, $name, $identifier); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadSectionData(int $id): array + { + try { + return $this->innerGateway->loadSectionData($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadAllSectionData(): array + { + try { + return $this->innerGateway->loadAllSectionData(); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadSectionDataByIdentifier(string $identifier): array + { + try { + return $this->innerGateway->loadSectionDataByIdentifier($identifier); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countContentObjectsInSection(int $id): int + { + try { + return $this->innerGateway->countContentObjectsInSection($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countPoliciesUsingSection(int $id): int + { + try { + return $this->innerGateway->countPoliciesUsingSection($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countRoleAssignmentsUsingSection(int $id): int + { + try { + return $this->innerGateway->countRoleAssignmentsUsingSection($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteSection(int $id): void + { + try { + $this->innerGateway->deleteSection($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function assignSectionToContent(int $sectionId, int $contentId): void + { + try { + $this->innerGateway->assignSectionToContent($sectionId, $contentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Section\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/Content/Section/Handler.php b/src/lib/Persistence/Legacy/Content/Section/Handler.php new file mode 100644 index 0000000000..76f03f85df --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Section/Handler.php @@ -0,0 +1,233 @@ +sectionGateway = $sectionGateway; + } + + /** + * Create a new section. + * + * @param string $name + * @param string $identifier + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Section + */ + public function create($name, $identifier) + { + $section = new Section(); + + $section->name = $name; + $section->identifier = $identifier; + + $section->id = $this->sectionGateway->insertSection($name, $identifier); + + return $section; + } + + /** + * Update name and identifier of a section. + * + * @param mixed $id + * @param string $name + * @param string $identifier + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Section + */ + public function update($id, $name, $identifier) + { + $this->sectionGateway->updateSection($id, $name, $identifier); + + $section = new Section(); + $section->id = $id; + $section->name = $name; + $section->identifier = $identifier; + + return $section; + } + + /** + * Get section data. + * + * @param mixed $id + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If section is not found + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Section + */ + public function load($id) + { + $rows = $this->sectionGateway->loadSectionData($id); + + if (empty($rows)) { + throw new NotFound('Section', $id); + } + + return $this->createSectionFromArray(reset($rows)); + } + + /** + * Get all section data. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Section[] + */ + public function loadAll() + { + $rows = $this->sectionGateway->loadAllSectionData(); + + return $this->createSectionsFromArray($rows); + } + + /** + * Get section data by identifier. + * + * @param string $identifier + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If section is not found + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Section + */ + public function loadByIdentifier($identifier) + { + $rows = $this->sectionGateway->loadSectionDataByIdentifier($identifier); + + if (empty($rows)) { + throw new NotFound('Section', $identifier); + } + + return $this->createSectionFromArray(reset($rows)); + } + + /** + * Creates a Section from the given $data. + * + * @param array $data + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Section + */ + protected function createSectionFromArray(array $data) + { + $section = new Section(); + + $section->id = (int)$data['id']; + $section->name = $data['name']; + $section->identifier = $data['identifier']; + + return $section; + } + + /** + * Creates a Section from the given $data. + * + * @param array $data + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Section[] + */ + protected function createSectionsFromArray(array $data) + { + $sections = []; + foreach ($data as $sectionData) { + $sections[] = $this->createSectionFromArray($sectionData); + } + + return $sections; + } + + /** + * Delete a section. + * + * Might throw an exception if the section is still associated with some + * content objects. Make sure that no content objects are associated with + * the section any more *before* calling this method. + * + * @param mixed $id + */ + public function delete($id) + { + $contentCount = $this->sectionGateway->countContentObjectsInSection($id); + + if ($contentCount > 0) { + throw new RuntimeException( + "Section with ID '{$id}' still has content assigned." + ); + } + $this->sectionGateway->deleteSection($id); + } + + /** + * Assigns section to single content object. + * + * @param mixed $sectionId + * @param mixed $contentId + */ + public function assign($sectionId, $contentId) + { + $this->sectionGateway->assignSectionToContent($sectionId, $contentId); + } + + /** + * Number of content assignments a Section has. + * + * @param mixed $sectionId + * + * @return int + */ + public function assignmentsCount($sectionId) + { + return $this->sectionGateway->countContentObjectsInSection($sectionId); + } + + /** + * Number of role policies using a Section in limitations. + * + * @param mixed $sectionId + * + * @return int + */ + public function policiesCount($sectionId) + { + return $this->sectionGateway->countPoliciesUsingSection($sectionId); + } + + /** + * Counts the number of role assignments using section with $sectionId in their limitations. + * + * @param int $sectionId + * + * @return int + */ + public function countRoleAssignmentsUsingSection($sectionId) + { + return $this->sectionGateway->countRoleAssignmentsUsingSection($sectionId); + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Section\Handler'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/StorageFieldDefinition.php b/src/lib/Persistence/Legacy/Content/StorageFieldDefinition.php similarity index 83% rename from eZ/Publish/Core/Persistence/Legacy/Content/StorageFieldDefinition.php rename to src/lib/Persistence/Legacy/Content/StorageFieldDefinition.php index 50b15a1e6d..e5a283184c 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/StorageFieldDefinition.php +++ b/src/lib/Persistence/Legacy/Content/StorageFieldDefinition.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content; +namespace Ibexa\Core\Persistence\Legacy\Content; -use eZ\Publish\SPI\Persistence\ValueObject; +use Ibexa\Contracts\Core\Persistence\ValueObject; class StorageFieldDefinition extends ValueObject { @@ -111,7 +111,9 @@ class StorageFieldDefinition extends ValueObject /** * Associative array with language codes keys. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\MultilingualStorageFieldDefinition[] + * @var \Ibexa\Core\Persistence\Legacy\Content\MultilingualStorageFieldDefinition[] */ public $multilingualData = []; } + +class_alias(StorageFieldDefinition::class, 'eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/StorageFieldValue.php b/src/lib/Persistence/Legacy/Content/StorageFieldValue.php similarity index 76% rename from eZ/Publish/Core/Persistence/Legacy/Content/StorageFieldValue.php rename to src/lib/Persistence/Legacy/Content/StorageFieldValue.php index a7054cb92f..2914c1baf7 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/StorageFieldValue.php +++ b/src/lib/Persistence/Legacy/Content/StorageFieldValue.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content; +namespace Ibexa\Core\Persistence\Legacy\Content; -use eZ\Publish\SPI\Persistence\ValueObject; +use Ibexa\Contracts\Core\Persistence\ValueObject; class StorageFieldValue extends ValueObject { @@ -45,3 +45,5 @@ class StorageFieldValue extends ValueObject */ public $sortKeyString = ''; } + +class_alias(StorageFieldValue::class, 'eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue'); diff --git a/src/lib/Persistence/Legacy/Content/StorageHandler.php b/src/lib/Persistence/Legacy/Content/StorageHandler.php new file mode 100644 index 0000000000..b4e9ded93d --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/StorageHandler.php @@ -0,0 +1,101 @@ +storageRegistry = $storageRegistry; + $this->context = $context; + } + + /** + * Stores data from $field in its corresponding external storage. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + */ + public function storeFieldData(VersionInfo $versionInfo, Field $field) + { + return $this->storageRegistry->getStorage($field->type)->storeFieldData( + $versionInfo, + $field, + $this->context + ); + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $originalField + */ + public function copyFieldData(VersionInfo $versionInfo, Field $field, Field $originalField) + { + return $this->storageRegistry->getStorage($field->type)->copyLegacyField( + $versionInfo, + $field, + $originalField, + $this->context + ); + } + + /** + * Fetches external data for $field from its corresponding external storage. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field + */ + public function getFieldData(VersionInfo $versionInfo, Field $field) + { + $storage = $this->storageRegistry->getStorage($field->type); + if ($field->id !== null && $storage->hasFieldData()) { + $storage->getFieldData($versionInfo, $field, $this->context); + } + } + + /** + * Deletes data for field $ids from external storage of $fieldType. + * + * @param string $fieldType + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $versionInfo + * @param mixed[] $ids + */ + public function deleteFieldData($fieldType, VersionInfo $versionInfo, array $ids) + { + $this->storageRegistry->getStorage($fieldType) + ->deleteFieldData($versionInfo, $ids, $this->context); + } +} + +class_alias(StorageHandler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler'); diff --git a/src/lib/Persistence/Legacy/Content/StorageRegistry.php b/src/lib/Persistence/Legacy/Content/StorageRegistry.php new file mode 100644 index 0000000000..0de52b627a --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/StorageRegistry.php @@ -0,0 +1,44 @@ +storageMap = $storageMap; + } + + public function register(string $typeName, FieldStorage $storage): void + { + $this->storageMap[$typeName] = $storage; + } + + public function getStorage(string $typeName): FieldStorage + { + if (!isset($this->storageMap[$typeName])) { + $this->storageMap[$typeName] = new NullStorage(); + } + + return $this->storageMap[$typeName]; + } +} + +class_alias(StorageRegistry::class, 'eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/TreeHandler.php b/src/lib/Persistence/Legacy/Content/TreeHandler.php similarity index 80% rename from eZ/Publish/Core/Persistence/Legacy/Content/TreeHandler.php rename to src/lib/Persistence/Legacy/Content/TreeHandler.php index 023a94c065..700b067e94 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/TreeHandler.php +++ b/src/lib/Persistence/Legacy/Content/TreeHandler.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content; +namespace Ibexa\Core\Persistence\Legacy\Content; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper as ContentMapper; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; +use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; +use Ibexa\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; +use Ibexa\Core\Persistence\Legacy\Content\Mapper as ContentMapper; /** * The TreeHandler is an intersect between ContentHandler and LocationHandler, @@ -21,44 +21,44 @@ class TreeHandler /** * Gateway for handling location data. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway + * @var \Ibexa\Core\Persistence\Legacy\Content\Location\Gateway */ protected $locationGateway; /** * Location Mapper. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper + * @var \Ibexa\Core\Persistence\Legacy\Content\Location\Mapper */ protected $locationMapper; /** * Content gateway. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway + * @var \Ibexa\Core\Persistence\Legacy\Content\Gateway */ protected $contentGateway; /** * Content handler. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Mapper + * @var \Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected $contentMapper; /** * FieldHandler. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler + * @var \Ibexa\Core\Persistence\Legacy\Content\FieldHandler */ protected $fieldHandler; /** - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway $locationGateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper $locationMapper - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Gateway $contentGateway - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Mapper $contentMapper - * @param \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler $fieldHandler + * @param \Ibexa\Core\Persistence\Legacy\Content\Location\Gateway $locationGateway + * @param \Ibexa\Core\Persistence\Legacy\Content\Location\Mapper $locationMapper + * @param \Ibexa\Core\Persistence\Legacy\Content\Gateway $contentGateway + * @param \Ibexa\Core\Persistence\Legacy\Content\Mapper $contentMapper + * @param \Ibexa\Core\Persistence\Legacy\Content\FieldHandler $fieldHandler */ public function __construct( LocationGateway $locationGateway, @@ -79,9 +79,9 @@ public function __construct( * * @param int|string $contentId * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo + * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo */ public function loadContentInfo($contentId) { @@ -95,7 +95,7 @@ public function loadContentInfo($contentId) * * @param int $contentId * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function removeRawContent($contentId) { @@ -125,7 +125,7 @@ public function removeRawContent($contentId) * @param mixed|null $status Optional argument to filter versions by status, like {@see VersionInfo::STATUS_ARCHIVED}. * @param int $limit Limit for items returned, -1 means none. * - * @return \eZ\Publish\SPI\Persistence\Content\VersionInfo[] + * @return \Ibexa\Contracts\Core\Persistence\Content\VersionInfo[] */ public function listVersions($contentId, $status = null, $limit = -1) { @@ -158,9 +158,9 @@ static function ($row) use ($contentId) { * @param string[]|null $translations If set, NotFound is thrown if content is not in given translation. * @param bool $useAlwaysAvailable Respect always available flag on content, where main language is valid translation fallback. * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException * - * @return \eZ\Publish\SPI\Persistence\Content\Location + * @return \Ibexa\Contracts\Core\Persistence\Content\Location */ public function loadLocation($locationId, array $translations = null, bool $useAlwaysAvailable = true) { @@ -180,7 +180,7 @@ public function loadLocation($locationId, array $translations = null, bool $useA * * @param mixed $locationId * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException * * @return bool */ @@ -221,7 +221,7 @@ public function removeSubtree($locationId) * @param mixed $locationId * @param mixed $sectionId * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function setSectionForSubtree($locationId, $sectionId) { @@ -238,7 +238,7 @@ public function setSectionForSubtree($locationId, $sectionId) * @param mixed $contentId * @param mixed $locationId * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function changeMainLocation($contentId, $locationId) { @@ -268,3 +268,5 @@ public function changeMainLocation($contentId, $locationId) } } } + +class_alias(TreeHandler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler'); diff --git a/src/lib/Persistence/Legacy/Content/Type/ContentUpdater.php b/src/lib/Persistence/Legacy/Content/Type/ContentUpdater.php new file mode 100644 index 0000000000..9c53b14628 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Type/ContentUpdater.php @@ -0,0 +1,154 @@ +contentGateway = $contentGateway; + $this->converterRegistry = $converterRegistry; + $this->storageHandler = $storageHandler; + $this->contentMapper = $contentMapper; + } + + /** + * Determines the necessary update actions. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type $fromType + * @param \Ibexa\Contracts\Core\Persistence\Content\Type $toType + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action[] + */ + public function determineActions(Type $fromType, Type $toType) + { + $actions = []; + foreach ($fromType->fieldDefinitions as $fieldDef) { + if (!$this->hasFieldDefinition($toType, $fieldDef)) { + $actions[] = new ContentUpdater\Action\RemoveField( + $this->contentGateway, + $fieldDef, + $this->storageHandler, + $this->contentMapper + ); + } + } + foreach ($toType->fieldDefinitions as $fieldDef) { + if (!$this->hasFieldDefinition($fromType, $fieldDef)) { + $actions[] = new ContentUpdater\Action\AddField( + $this->contentGateway, + $fieldDef, + $this->converterRegistry->getConverter( + $fieldDef->fieldType + ), + $this->storageHandler, + $this->contentMapper + ); + } + } + + return $actions; + } + + /** + * hasFieldDefinition. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type $type + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * + * @return bool + */ + protected function hasFieldDefinition(Type $type, FieldDefinition $fieldDef) + { + foreach ($type->fieldDefinitions as $existFieldDef) { + if ($existFieldDef->id == $fieldDef->id) { + return true; + } + } + + return false; + } + + /** + * Applies all given updates. + * + * @param mixed $contentTypeId + * @param \Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action[] $actions + */ + public function applyUpdates($contentTypeId, array $actions) + { + if (empty($actions)) { + return; + } + + foreach ($this->getContentIdsByContentTypeId($contentTypeId) as $contentId) { + foreach ($actions as $action) { + $action->apply($contentId); + } + } + } + + /** + * Returns all content objects of $contentTypeId. + * + * @param mixed $contentTypeId + * + * @return int[] + */ + protected function getContentIdsByContentTypeId($contentTypeId) + { + return $this->contentGateway->getContentIdsByContentTypeId($contentTypeId); + } +} + +class_alias(ContentUpdater::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater'); diff --git a/src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action.php b/src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action.php new file mode 100644 index 0000000000..cb7c3d8fff --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action.php @@ -0,0 +1,41 @@ +contentGateway = $contentGateway; + } + + /** + * Applies the action to the given $content. + * + * @param int $contentId + */ + abstract public function apply($contentId); +} + +class_alias(Action::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddField.php b/src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddField.php similarity index 77% rename from eZ/Publish/Core/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddField.php rename to src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddField.php index a2438a8f15..2bfceff98c 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddField.php +++ b/src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddField.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper as ContentMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; +namespace Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action; + +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Mapper as ContentMapper; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use Ibexa\Core\Persistence\Legacy\Content\StorageHandler; +use Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action; /** * Action to add a field to content objects. @@ -24,35 +24,35 @@ class AddField extends Action /** * Field definition of the field to add. * - * @var \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition + * @var \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition */ protected $fieldDefinition; /** * Storage handler. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler + * @var \Ibexa\Core\Persistence\Legacy\Content\StorageHandler */ protected $storageHandler; /** * Field value converter. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter + * @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter */ protected $fieldValueConverter; - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Mapper */ + /** @var \Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected $contentMapper; /** * Creates a new action. * - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Gateway $contentGateway - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $fieldDef - * @param \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter $converter - * @param \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler $storageHandler - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Mapper $contentMapper + * @param \Ibexa\Core\Persistence\Legacy\Content\Gateway $contentGateway + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter $converter + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageHandler $storageHandler + * @param \Ibexa\Core\Persistence\Legacy\Content\Mapper $contentMapper */ public function __construct( Gateway $contentGateway, @@ -130,8 +130,8 @@ static function ($versionNo) use ($contentId) { * If $field->id is null, creating new field id will be created. * Otherwise it will be inserted for the given $content version, reusing existing Field id. * - * @param \eZ\Publish\SPI\Persistence\Content $content - * @param \eZ\Publish\SPI\Persistence\Content\Field $field + * @param \Ibexa\Contracts\Core\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content\Field $field * * @return int The ID of the field that was inserted */ @@ -186,7 +186,7 @@ protected function insertField(Content $content, Field $field) * @param int $versionNo * @param string $languageCode * - * @return \eZ\Publish\SPI\Persistence\Content\Field + * @return \Ibexa\Contracts\Core\Persistence\Content\Field */ protected function createField($id, $versionNo, $languageCode) { @@ -202,3 +202,5 @@ protected function createField($id, $versionNo, $languageCode) return $field; } } + +class_alias(AddField::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\AddField'); diff --git a/src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveField.php b/src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveField.php new file mode 100644 index 0000000000..dc8123ff8f --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveField.php @@ -0,0 +1,107 @@ +contentGateway = $contentGateway; + $this->fieldDefinition = $fieldDef; + $this->storageHandler = $storageHandler; + $this->contentMapper = $contentMapper; + } + + /** + * Applies the action to the given $content. + * + * @param int $contentId + */ + public function apply($contentId) + { + $versionNumbers = $this->contentGateway->listVersionNumbers($contentId); + $fieldIdSet = []; + + $nameRows = $this->contentGateway->loadVersionedNameData( + array_map( + static function ($versionNo) use ($contentId) { + return ['id' => $contentId, 'version' => $versionNo]; + }, + $versionNumbers + ) + ); + + foreach ($versionNumbers as $versionNo) { + $contentRows = $this->contentGateway->load($contentId, $versionNo); + $contentList = $this->contentMapper->extractContentFromRows($contentRows, $nameRows); + $content = $contentList[0]; + $versionFieldIdSet = []; + + foreach ($content->fields as $field) { + if ($field->fieldDefinitionId == $this->fieldDefinition->id) { + $fieldIdSet[$field->id] = true; + $versionFieldIdSet[$field->id] = true; + } + } + + // Delete from external storage with list of IDs per version + $this->storageHandler->deleteFieldData( + $this->fieldDefinition->fieldType, + $content->versionInfo, + array_keys($versionFieldIdSet) + ); + } + + // Delete from relations storage + $this->contentGateway->removeRelationsByFieldDefinitionId($this->fieldDefinition->id); + + // Delete from internal storage -- field is always deleted from _all_ versions + foreach (array_keys($fieldIdSet) as $fieldId) { + $this->contentGateway->deleteField($fieldId); + } + } +} + +class_alias(RemoveField::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\RemoveField'); diff --git a/src/lib/Persistence/Legacy/Content/Type/Gateway.php b/src/lib/Persistence/Legacy/Content/Type/Gateway.php new file mode 100644 index 0000000000..f5e9b28a13 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Type/Gateway.php @@ -0,0 +1,176 @@ + + */ + abstract public function loadTypesDataByFieldDefinitionIdentifier(string $identifier): array; + + abstract public function loadTypeData(int $typeId, int $status): array; + + abstract public function loadTypeDataByIdentifier(string $identifier, int $status): array; + + abstract public function loadTypeDataByRemoteId(string $remoteId, int $status): array; + + abstract public function countInstancesOfType(int $typeId): int; + + /** + * Permanently delete a content type of the given status. + */ + abstract public function delete(int $typeId, int $status): void; + + abstract public function deleteFieldDefinitionsForType(int $typeId, int $status): void; + + /** + * Delete a content type. + * + * Does not delete Field Definitions! + */ + abstract public function deleteType(int $typeId, int $status): void; + + abstract public function deleteGroupAssignmentsForType(int $typeId, int $status): void; + + /** + * Publish a content type including its Field Definitions. + */ + abstract public function publishTypeAndFields( + int $typeId, + int $sourceStatus, + int $targetStatus + ): void; + + abstract public function getSearchableFieldMapData(): array; + + /** + * Remove Field Definition data from multilingual table. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the given language does not exist + */ + abstract public function removeFieldDefinitionTranslation( + int $fieldDefinitionId, + string $languageCode, + int $status + ): void; + + /** + * Remove items created or modified by User. + */ + abstract public function removeByUserAndVersion(int $userId, int $version): void; +} + +class_alias(Gateway::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway'); diff --git a/src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php b/src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php new file mode 100644 index 0000000000..83b40160f6 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabase.php @@ -0,0 +1,1484 @@ + [ + 'id', + 'always_available', + 'contentobject_name', + 'created', + 'creator_id', + 'modified', + 'modifier_id', + 'identifier', + 'initial_language_id', + 'is_container', + 'language_mask', + 'remote_id', + 'serialized_description_list', + 'serialized_name_list', + 'sort_field', + 'sort_order', + 'url_alias_name', + 'version', + ], + 'ezcontentclass_attribute' => [ + 'id', + 'can_translate', + 'category', + 'contentclass_id', + 'data_float1', + 'data_float2', + 'data_float3', + 'data_float4', + 'data_int1', + 'data_int2', + 'data_int3', + 'data_int4', + 'data_text1', + 'data_text2', + 'data_text3', + 'data_text4', + 'data_text5', + 'data_type_string', + 'identifier', + 'is_information_collector', + 'is_required', + 'is_searchable', + 'is_thumbnail', + 'placement', + 'serialized_data_text', + 'serialized_description_list', + 'serialized_name_list', + ], + ]; + + /** + * The native Doctrine connection. + * + * Meant to be used to transition from eZ/Zeta interface to Doctrine. + * + * @var \Doctrine\DBAL\Connection + */ + private $connection; + + /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */ + private $dbPlatform; + + /** @var \Ibexa\Core\Persistence\Legacy\SharedGateway\Gateway */ + private $sharedGateway; + + /** + * Language mask generator. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator + */ + private $languageMaskGenerator; + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function __construct( + Connection $connection, + SharedGateway $sharedGateway, + MaskGenerator $languageMaskGenerator + ) { + $this->connection = $connection; + $this->dbPlatform = $connection->getDatabasePlatform(); + $this->sharedGateway = $sharedGateway; + $this->languageMaskGenerator = $languageMaskGenerator; + } + + public function insertGroup(Group $group): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::CONTENT_TYPE_GROUP_TABLE) + ->values( + [ + 'created' => $query->createPositionalParameter( + $group->created, + ParameterType::INTEGER + ), + 'creator_id' => $query->createPositionalParameter( + $group->creatorId, + ParameterType::INTEGER + ), + 'modified' => $query->createPositionalParameter( + $group->modified, + ParameterType::INTEGER + ), + 'modifier_id' => $query->createPositionalParameter( + $group->modifierId, + ParameterType::INTEGER + ), + 'name' => $query->createPositionalParameter( + $group->identifier, + ParameterType::STRING + ), + 'is_system' => $query->createPositionalParameter( + $group->isSystem ?? false, + ParameterType::BOOLEAN + ), + ] + ); + $query->execute(); + + return (int)$this->connection->lastInsertId(self::CONTENT_TYPE_GROUP_SEQ); + } + + public function updateGroup(GroupUpdateStruct $group): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::CONTENT_TYPE_GROUP_TABLE) + ->set( + 'modified', + $query->createPositionalParameter($group->modified, ParameterType::INTEGER) + ) + ->set( + 'modifier_id', + $query->createPositionalParameter($group->modifierId, ParameterType::INTEGER) + ) + ->set( + 'name', + $query->createPositionalParameter($group->identifier, ParameterType::STRING) + ) + ->set( + 'is_system', + $query->createPositionalParameter($group->isSystem, ParameterType::BOOLEAN) + )->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($group->id, ParameterType::INTEGER) + ) + ); + + $query->execute(); + } + + public function countTypesInGroup(int $groupId): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select($this->dbPlatform->getCountExpression('contentclass_id')) + ->from(self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE) + ->where( + $query->expr()->eq( + 'group_id', + $query->createPositionalParameter($groupId, ParameterType::INTEGER) + ) + ); + + return (int)$query->execute()->fetchColumn(); + } + + public function countGroupsForType(int $typeId, int $status): int + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select($this->dbPlatform->getCountExpression('group_id')) + ->from(self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE) + ->where( + $expr->eq( + 'contentclass_id', + $query->createPositionalParameter($typeId, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + 'contentclass_version', + $query->createPositionalParameter($status, ParameterType::INTEGER) + ) + ); + + return (int)$query->execute()->fetchColumn(); + } + + public function deleteGroup(int $groupId): void + { + $query = $this->connection->createQueryBuilder(); + $query->delete(self::CONTENT_TYPE_GROUP_TABLE) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($groupId, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + /** + * @param string[] $languages + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if at least one of the used languages does not exist + */ + private function insertTypeNameData(int $typeId, int $typeStatus, array $languages): void + { + $tmpLanguages = $languages; + if (isset($tmpLanguages['always-available'])) { + unset($tmpLanguages['always-available']); + } + + foreach ($tmpLanguages as $language => $name) { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::CONTENT_TYPE_NAME_TABLE) + ->values( + [ + 'contentclass_id' => $query->createPositionalParameter( + $typeId, + ParameterType::INTEGER + ), + 'contentclass_version' => $query->createPositionalParameter( + $typeStatus, + ParameterType::INTEGER + ), + 'language_id' => $query->createPositionalParameter( + $this->languageMaskGenerator->generateLanguageIndicator( + $language, + $this->languageMaskGenerator->isLanguageAlwaysAvailable( + $language, + $languages + ) + ), + ParameterType::INTEGER + ), + 'language_locale' => $query->createPositionalParameter( + $language, + ParameterType::STRING + ), + 'name' => $query->createPositionalParameter($name, ParameterType::STRING), + ] + ); + $query->execute(); + } + } + + private function setNextAutoIncrementedValueIfAvailable( + QueryBuilder $queryBuilder, + string $tableName, + string $idColumnName, + string $sequenceName, + ?int $defaultValue = null + ): void { + if (null === $defaultValue) { + // usually returns null to trigger default column value behavior + $defaultValue = $this->sharedGateway->getColumnNextIntegerValue( + $tableName, + $idColumnName, + $sequenceName + ); + } + // avoid trying to insert NULL to trigger default column value behavior + if (null !== $defaultValue) { + $queryBuilder->setValue( + $idColumnName, + $queryBuilder->createNamedParameter( + $defaultValue, + ParameterType::INTEGER, + ":{$idColumnName}" + ) + ); + } + } + + public function insertType(Type $type, ?int $typeId = null): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::CONTENT_TYPE_TABLE) + ->values( + [ + // Legacy Storage: "version" stores CT status (draft, published) + 'version' => $query->createNamedParameter( + $type->status, + ParameterType::INTEGER, + ':status' + ), + 'created' => $query->createNamedParameter( + $type->created, + ParameterType::INTEGER, + ':created' + ), + 'creator_id' => $query->createNamedParameter( + $type->creatorId, + ParameterType::INTEGER, + ':creator_id' + ), + ] + ); + $this->setNextAutoIncrementedValueIfAvailable( + $query, + self::CONTENT_TYPE_TABLE, + 'id', + self::CONTENT_TYPE_SEQ, + $typeId + ); + + $columnQueryValueAndTypeMap = $this->mapCommonContentTypeColumnsToQueryValuesAndTypes( + $type + ); + foreach ($columnQueryValueAndTypeMap as $columnName => $data) { + [$value, $parameterType] = $data; + $query + ->setValue( + $columnName, + $query->createNamedParameter($value, $parameterType, ":{$columnName}") + ); + } + + $query->setParameter('status', $type->status, ParameterType::INTEGER); + $query->setParameter('created', $type->created, ParameterType::INTEGER); + $query->setParameter('creator_id', $type->creatorId, ParameterType::INTEGER); + + $query->execute(); + + if (empty($typeId)) { + $typeId = $this->sharedGateway->getLastInsertedId(self::CONTENT_TYPE_SEQ); + } + + $this->insertTypeNameData($typeId, $type->status, $type->name); + + // $typeId passed as the argument could still be non-int + return (int)$typeId; + } + + /** + * Get a map of content type storage column name to its value and parameter type. + * + * Key value of the map is represented as a two-elements array with column value and its type. + */ + private function mapCommonContentTypeColumnsToQueryValuesAndTypes(Type $type): array + { + return [ + 'serialized_name_list' => [serialize($type->name), ParameterType::STRING], + 'serialized_description_list' => [serialize($type->description), ParameterType::STRING], + 'identifier' => [$type->identifier, ParameterType::STRING], + 'modified' => [$type->modified, ParameterType::INTEGER], + 'modifier_id' => [$type->modifierId, ParameterType::INTEGER], + 'remote_id' => [$type->remoteId, ParameterType::STRING], + 'url_alias_name' => [$type->urlAliasSchema, ParameterType::STRING], + 'contentobject_name' => [$type->nameSchema, ParameterType::STRING], + 'is_container' => [(int)$type->isContainer, ParameterType::INTEGER], + 'language_mask' => [ + $this->languageMaskGenerator->generateLanguageMaskFromLanguageCodes( + $type->languageCodes, + array_key_exists('always-available', $type->name) + ), + ParameterType::INTEGER, + ], + 'initial_language_id' => [$type->initialLanguageId, ParameterType::INTEGER], + 'sort_field' => [$type->sortField, ParameterType::INTEGER], + 'sort_order' => [$type->sortOrder, ParameterType::INTEGER], + 'always_available' => [(int)$type->defaultAlwaysAvailable, ParameterType::INTEGER], + ]; + } + + public function insertGroupAssignment(int $groupId, int $typeId, int $status): void + { + $groups = $this->loadGroupData([$groupId]); + if (empty($groups)) { + throw new NotFoundException('Content type group', $groupId); + } + $group = $groups[0]; + + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE) + ->values( + [ + 'contentclass_id' => $query->createPositionalParameter( + $typeId, + ParameterType::INTEGER + ), + 'contentclass_version' => $query->createPositionalParameter( + $status, + ParameterType::INTEGER + ), + 'group_id' => $query->createPositionalParameter( + $groupId, + ParameterType::INTEGER + ), + 'group_name' => $query->createPositionalParameter( + $group['name'], + ParameterType::STRING + ), + ] + ); + + $query->execute(); + } + + public function deleteGroupAssignment(int $groupId, int $typeId, int $status): void + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->delete(self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE) + ->where( + $expr->eq( + 'contentclass_id', + $query->createPositionalParameter($typeId, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + 'contentclass_version', + $query->createPositionalParameter($status, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + 'group_id', + $query->createPositionalParameter($groupId, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + public function loadGroupData(array $groupIds): array + { + $query = $this->createGroupLoadQuery(); + $query + ->where($query->expr()->in('id', ':ids')) + ->setParameter('ids', $groupIds, Connection::PARAM_INT_ARRAY); + + return $query->execute()->fetchAll(); + } + + public function loadGroupDataByIdentifier(string $identifier): array + { + $query = $this->createGroupLoadQuery(); + $query->where( + $query->expr()->eq( + 'name', + $query->createPositionalParameter($identifier, ParameterType::STRING) + ) + ); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadAllGroupsData(): array + { + $query = $this->createGroupLoadQuery(); + + $query->andWhere( + $query->expr()->eq( + 'is_system', + $query->createPositionalParameter(false, ParameterType::BOOLEAN) + ) + ); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + /** + * Create the basic query to load Group data. + */ + private function createGroupLoadQuery(): QueryBuilder + { + $query = $this->connection->createQueryBuilder(); + $query->select( + 'created', + 'creator_id', + 'id', + 'modified', + 'modifier_id', + 'name', + 'is_system' + )->from(self::CONTENT_TYPE_GROUP_TABLE); + + return $query; + } + + public function loadTypesDataForGroup(int $groupId, int $status): array + { + $query = $this->getLoadTypeQueryBuilder(); + $expr = $query->expr(); + $query + ->where($expr->eq('g.group_id', ':gid')) + ->andWhere($expr->eq('c.version', ':version')) + ->addOrderBy('c.identifier') + ->setParameter('gid', $groupId, ParameterType::INTEGER) + ->setParameter('version', $status, ParameterType::INTEGER); + + return $query->execute()->fetchAll(); + } + + public function insertFieldDefinition( + int $typeId, + int $status, + FieldDefinition $fieldDefinition, + StorageFieldDefinition $storageFieldDef + ): int { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::FIELD_DEFINITION_TABLE) + ->values( + [ + 'contentclass_id' => $query->createNamedParameter( + $typeId, + ParameterType::INTEGER, + ':content_type_id' + ), + 'version' => $query->createNamedParameter( + $status, + ParameterType::INTEGER, + ':status' + ), + ] + ); + $this->setNextAutoIncrementedValueIfAvailable( + $query, + self::FIELD_DEFINITION_TABLE, + 'id', + self::FIELD_DEFINITION_SEQ, + $fieldDefinition->id + ); + $columnValueAndTypeMap = $this->mapCommonFieldDefinitionColumnsToQueryValuesAndTypes( + $fieldDefinition, + $storageFieldDef + ); + foreach ($columnValueAndTypeMap as $columnName => $data) { + [$columnValue, $parameterType] = $data; + $query + ->setValue($columnName, ":{$columnName}") + ->setParameter($columnName, $columnValue, $parameterType); + } + + $query->execute(); + + $fieldDefinitionId = $fieldDefinition->id ?? $this->sharedGateway->getLastInsertedId( + self::FIELD_DEFINITION_SEQ + ); + + foreach ($storageFieldDef->multilingualData as $multilingualData) { + $this->insertFieldDefinitionMultilingualData( + $fieldDefinitionId, + $multilingualData, + $status + ); + } + + return $fieldDefinitionId; + } + + private function insertFieldDefinitionMultilingualData( + int $fieldDefinitionId, + MultilingualStorageFieldDefinition $multilingualData, + int $status + ): void { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) + ->values( + [ + 'data_text' => ':data_text', + 'data_json' => ':data_json', + 'name' => ':name', + 'description' => ':description', + 'contentclass_attribute_id' => ':field_definition_id', + 'version' => ':status', + 'language_id' => ':language_id', + ] + ) + ->setParameter('data_text', $multilingualData->dataText) + ->setParameter('data_json', $multilingualData->dataJson) + ->setParameter('name', $multilingualData->name) + ->setParameter('description', $multilingualData->description) + ->setParameter('field_definition_id', $fieldDefinitionId, ParameterType::INTEGER) + ->setParameter('status', $status, ParameterType::INTEGER) + ->setParameter('language_id', $multilingualData->languageId, ParameterType::INTEGER); + + $query->execute(); + } + + /** + * Get a map of Field Definition storage column name to its value and parameter type. + * + * Key value of the map is represented as a two-elements array with column value and its type. + */ + private function mapCommonFieldDefinitionColumnsToQueryValuesAndTypes( + FieldDefinition $fieldDefinition, + StorageFieldDefinition $storageFieldDef + ): array { + return [ + 'serialized_name_list' => [serialize($fieldDefinition->name), ParameterType::STRING], + 'serialized_description_list' => [ + serialize($fieldDefinition->description), + ParameterType::STRING, + ], + 'serialized_data_text' => [ + serialize($storageFieldDef->serializedDataText), + ParameterType::STRING, + ], + 'identifier' => [$fieldDefinition->identifier, ParameterType::STRING], + 'category' => [$fieldDefinition->fieldGroup, ParameterType::STRING], + 'placement' => [$fieldDefinition->position, ParameterType::INTEGER], + 'data_type_string' => [$fieldDefinition->fieldType, ParameterType::STRING], + 'can_translate' => [(int)$fieldDefinition->isTranslatable, ParameterType::INTEGER], + 'is_thumbnail' => [(bool)$fieldDefinition->isThumbnail, ParameterType::INTEGER], + 'is_required' => [(int)$fieldDefinition->isRequired, ParameterType::INTEGER], + 'is_information_collector' => [ + (int)$fieldDefinition->isInfoCollector, + ParameterType::INTEGER, + ], + 'is_searchable' => [(int)$fieldDefinition->isSearchable, ParameterType::INTEGER], + 'data_float1' => [$storageFieldDef->dataFloat1, null], + 'data_float2' => [$storageFieldDef->dataFloat2, null], + 'data_float3' => [$storageFieldDef->dataFloat3, null], + 'data_float4' => [$storageFieldDef->dataFloat4, null], + 'data_int1' => [$storageFieldDef->dataInt1, ParameterType::INTEGER], + 'data_int2' => [$storageFieldDef->dataInt2, ParameterType::INTEGER], + 'data_int3' => [$storageFieldDef->dataInt3, ParameterType::INTEGER], + 'data_int4' => [$storageFieldDef->dataInt4, ParameterType::INTEGER], + 'data_text1' => [$storageFieldDef->dataText1, ParameterType::STRING], + 'data_text2' => [$storageFieldDef->dataText2, ParameterType::STRING], + 'data_text3' => [$storageFieldDef->dataText3, ParameterType::STRING], + 'data_text4' => [$storageFieldDef->dataText4, ParameterType::STRING], + 'data_text5' => [$storageFieldDef->dataText5, ParameterType::STRING], + ]; + } + + public function loadFieldDefinition(int $id, int $status): array + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $this + ->selectColumns($query, self::FIELD_DEFINITION_TABLE, 'f_def') + ->addSelect( + [ + 'ct.initial_language_id AS ezcontentclass_initial_language_id', + 'transl_f_def.name AS ezcontentclass_attribute_multilingual_name', + 'transl_f_def.description AS ezcontentclass_attribute_multilingual_description', + 'transl_f_def.language_id AS ezcontentclass_attribute_multilingual_language_id', + 'transl_f_def.data_text AS ezcontentclass_attribute_multilingual_data_text', + 'transl_f_def.data_json AS ezcontentclass_attribute_multilingual_data_json', + ] + ) + ->from(self::FIELD_DEFINITION_TABLE, 'f_def') + ->leftJoin( + 'f_def', + self::CONTENT_TYPE_TABLE, + 'ct', + $expr->andX( + $expr->eq('f_def.contentclass_id', 'ct.id'), + $expr->eq('f_def.version', 'ct.version') + ) + ) + ->leftJoin( + 'f_def', + self::MULTILINGUAL_FIELD_DEFINITION_TABLE, + 'transl_f_def', + $expr->andX( + $expr->eq( + 'f_def.id', + 'transl_f_def.contentclass_attribute_id' + ), + $expr->eq( + 'f_def.version', + 'transl_f_def.version' + ) + ) + ) + ->where( + $expr->eq( + 'f_def.id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + 'f_def.version', + $query->createPositionalParameter($status, ParameterType::INTEGER) + ) + ); + + $stmt = $query->execute(); + + return $stmt->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function deleteFieldDefinition( + int $typeId, + int $status, + int $fieldDefinitionId + ): void { + // Delete multilingual data first to keep DB integrity + $deleteQuery = $this->connection->createQueryBuilder(); + $deleteQuery + ->delete(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) + ->where('contentclass_attribute_id = :field_definition_id') + ->andWhere('version = :status') + ->setParameter('field_definition_id', $fieldDefinitionId, ParameterType::INTEGER) + ->setParameter('status', $status, ParameterType::INTEGER); + + $deleteQuery->execute(); + + // Delete legacy Field Definition data + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::FIELD_DEFINITION_TABLE) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($fieldDefinitionId, ParameterType::INTEGER) + ) + ) + // in Legacy Storage Field Definition table the "version" column stores status (e.g. draft, published, modified) + ->andWhere( + $query->expr()->eq( + 'version', + $query->createPositionalParameter($status, ParameterType::INTEGER) + ) + ) + ; + + $query->execute(); + } + + public function updateFieldDefinition( + int $typeId, + int $status, + FieldDefinition $fieldDefinition, + StorageFieldDefinition $storageFieldDef + ): void { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::FIELD_DEFINITION_TABLE) + ->where('id = :field_definition_id') + ->andWhere('version = :status') + ->setParameter('field_definition_id', $fieldDefinition->id, ParameterType::INTEGER) + ->setParameter('status', $status, ParameterType::INTEGER); + + $fieldDefinitionValueAndTypeMap = $this->mapCommonFieldDefinitionColumnsToQueryValuesAndTypes( + $fieldDefinition, + $storageFieldDef + ); + foreach ($fieldDefinitionValueAndTypeMap as $columnName => $data) { + [$value, $parameterType] = $data; + $query + ->set( + $columnName, + $query->createNamedParameter($value, $parameterType, ":{$columnName}") + ); + } + + $query->execute(); + + foreach ($storageFieldDef->multilingualData as $data) { + $dataExists = $this->fieldDefinitionMultilingualDataExist( + $fieldDefinition, + $data->languageId, + $status + ); + + if ($dataExists) { + $this->updateFieldDefinitionMultilingualData( + $fieldDefinition->id, + $data, + $status + ); + } else { + //When creating new translation there are no fields for update. + $this->insertFieldDefinitionMultilingualData( + $fieldDefinition->id, + $data, + $status + ); + } + } + } + + private function fieldDefinitionMultilingualDataExist( + FieldDefinition $fieldDefinition, + int $languageId, + int $status + ): bool { + $existQuery = $this->connection->createQueryBuilder(); + $existQuery + ->select($this->dbPlatform->getCountExpression('1')) + ->from(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) + ->where('contentclass_attribute_id = :field_definition_id') + ->andWhere('version = :status') + ->andWhere('language_id = :language_id') + ->setParameter('field_definition_id', $fieldDefinition->id, ParameterType::INTEGER) + ->setParameter('status', $status, ParameterType::INTEGER) + ->setParameter('language_id', $languageId, ParameterType::INTEGER); + + return 0 < (int)$existQuery->execute()->fetchColumn(); + } + + private function updateFieldDefinitionMultilingualData( + int $fieldDefinitionId, + MultilingualStorageFieldDefinition $multilingualData, + int $status + ): void { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) + ->set('data_text', ':data_text') + ->set('data_json', ':data_json') + ->set('name', ':name') + ->set('description', ':description') + ->where('contentclass_attribute_id = :field_definition_id') + ->andWhere('version = :status') + ->andWhere('language_id = :languageId') + ->setParameter('data_text', $multilingualData->dataText) + ->setParameter('data_json', $multilingualData->dataJson) + ->setParameter('name', $multilingualData->name) + ->setParameter('description', $multilingualData->description) + ->setParameter('field_definition_id', $fieldDefinitionId, ParameterType::INTEGER) + ->setParameter('status', $status, ParameterType::INTEGER) + ->setParameter('languageId', $multilingualData->languageId, ParameterType::INTEGER); + + $query->execute(); + } + + /** + * Delete entire name data for the given content type of the given status. + */ + private function deleteTypeNameData(int $typeId, int $typeStatus): void + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->delete(self::CONTENT_TYPE_NAME_TABLE) + ->where( + $expr->eq( + 'contentclass_id', + $query->createPositionalParameter($typeId, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + 'contentclass_version', + $query->createPositionalParameter($typeStatus, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + public function updateType(int $typeId, int $status, Type $type): void + { + $query = $this->connection->createQueryBuilder(); + $query->update(self::CONTENT_TYPE_TABLE); + + $columnQueryValueAndTypeMap = $this->mapCommonContentTypeColumnsToQueryValuesAndTypes( + $type + ); + foreach ($columnQueryValueAndTypeMap as $columnName => $data) { + [$value, $parameterType] = $data; + $query + ->set( + $columnName, + $query->createNamedParameter($value, $parameterType, ":{$columnName}") + ); + } + $expr = $query->expr(); + $query + ->where( + $expr->eq( + 'id', + $query->createNamedParameter($typeId, ParameterType::INTEGER, ':id') + ) + ) + ->andWhere( + $expr->eq( + 'version', + $query->createNamedParameter($status, ParameterType::INTEGER, ':status') + ) + ); + + $query->execute(); + + $this->deleteTypeNameData($typeId, $status); + $this->insertTypeNameData($typeId, $status, $type->name); + } + + public function loadTypesListData(array $typeIds): array + { + $query = $this->getLoadTypeQueryBuilder(); + + $query + ->where($query->expr()->in('c.id', ':ids')) + ->andWhere($query->expr()->eq('c.version', Type::STATUS_DEFINED)) + ->setParameter('ids', $typeIds, Connection::PARAM_INT_ARRAY); + + return $query->execute()->fetchAll(); + } + + public function loadTypesDataByFieldDefinitionIdentifier(string $identifier): array + { + $query = $this->getLoadTypeQueryBuilder(); + $query + ->andWhere( + $query->expr()->eq( + 'a.data_type_string', + $query->createNamedParameter($identifier) + ) + ); + + return $query->execute()->fetchAllAssociative(); + } + + public function loadTypeData(int $typeId, int $status): array + { + $query = $this->getLoadTypeQueryBuilder(); + $expr = $query->expr(); + $query + ->where($expr->eq('c.id', ':id')) + ->andWhere($expr->eq('c.version', ':version')) + ->setParameter('id', $typeId, ParameterType::INTEGER) + ->setParameter('version', $status, ParameterType::INTEGER); + + return $query->execute()->fetchAll(); + } + + public function loadTypeDataByIdentifier(string $identifier, int $status): array + { + $query = $this->getLoadTypeQueryBuilder(); + $expr = $query->expr(); + $query + ->where($expr->eq('c.identifier', ':identifier')) + ->andWhere($expr->eq('c.version', ':version')) + ->setParameter('identifier', $identifier, ParameterType::STRING) + ->setParameter('version', $status, ParameterType::INTEGER); + + return $query->execute()->fetchAll(); + } + + public function loadTypeDataByRemoteId(string $remoteId, int $status): array + { + $query = $this->getLoadTypeQueryBuilder(); + $query + ->where($query->expr()->eq('c.remote_id', ':remote')) + ->andWhere($query->expr()->eq('c.version', ':version')) + ->setParameter('remote', $remoteId, ParameterType::STRING) + ->setParameter('version', $status, ParameterType::INTEGER); + + return $query->execute()->fetchAll(); + } + + /** + * Return a basic query to retrieve Type data. + */ + private function getLoadTypeQueryBuilder(): QueryBuilder + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select( + [ + 'c.id AS ezcontentclass_id', + 'c.version AS ezcontentclass_version', + 'c.serialized_name_list AS ezcontentclass_serialized_name_list', + 'c.serialized_description_list AS ezcontentclass_serialized_description_list', + 'c.identifier AS ezcontentclass_identifier', + 'c.created AS ezcontentclass_created', + 'c.modified AS ezcontentclass_modified', + 'c.modifier_id AS ezcontentclass_modifier_id', + 'c.creator_id AS ezcontentclass_creator_id', + 'c.remote_id AS ezcontentclass_remote_id', + 'c.url_alias_name AS ezcontentclass_url_alias_name', + 'c.contentobject_name AS ezcontentclass_contentobject_name', + 'c.is_container AS ezcontentclass_is_container', + 'c.initial_language_id AS ezcontentclass_initial_language_id', + 'c.always_available AS ezcontentclass_always_available', + 'c.sort_field AS ezcontentclass_sort_field', + 'c.sort_order AS ezcontentclass_sort_order', + 'c.language_mask AS ezcontentclass_language_mask', + + 'a.id AS ezcontentclass_attribute_id', + 'a.serialized_name_list AS ezcontentclass_attribute_serialized_name_list', + 'a.serialized_description_list AS ezcontentclass_attribute_serialized_description_list', + 'a.identifier AS ezcontentclass_attribute_identifier', + 'a.category AS ezcontentclass_attribute_category', + 'a.data_type_string AS ezcontentclass_attribute_data_type_string', + 'a.can_translate AS ezcontentclass_attribute_can_translate', + 'a.is_required AS ezcontentclass_attribute_is_required', + 'a.is_information_collector AS ezcontentclass_attribute_is_information_collector', + 'a.is_searchable AS ezcontentclass_attribute_is_searchable', + 'a.is_thumbnail AS ezcontentclass_attribute_is_thumbnail', + 'a.placement AS ezcontentclass_attribute_placement', + 'a.data_float1 AS ezcontentclass_attribute_data_float1', + 'a.data_float2 AS ezcontentclass_attribute_data_float2', + 'a.data_float3 AS ezcontentclass_attribute_data_float3', + 'a.data_float4 AS ezcontentclass_attribute_data_float4', + 'a.data_int1 AS ezcontentclass_attribute_data_int1', + 'a.data_int2 AS ezcontentclass_attribute_data_int2', + 'a.data_int3 AS ezcontentclass_attribute_data_int3', + 'a.data_int4 AS ezcontentclass_attribute_data_int4', + 'a.data_text1 AS ezcontentclass_attribute_data_text1', + 'a.data_text2 AS ezcontentclass_attribute_data_text2', + 'a.data_text3 AS ezcontentclass_attribute_data_text3', + 'a.data_text4 AS ezcontentclass_attribute_data_text4', + 'a.data_text5 AS ezcontentclass_attribute_data_text5', + 'a.serialized_data_text AS ezcontentclass_attribute_serialized_data_text', + + 'g.group_id AS ezcontentclass_classgroup_group_id', + + 'ml.name AS ezcontentclass_attribute_multilingual_name', + 'ml.description AS ezcontentclass_attribute_multilingual_description', + 'ml.language_id AS ezcontentclass_attribute_multilingual_language_id', + 'ml.data_text AS ezcontentclass_attribute_multilingual_data_text', + 'ml.data_json AS ezcontentclass_attribute_multilingual_data_json', + ] + ) + ->from(self::CONTENT_TYPE_TABLE, 'c') + ->leftJoin( + 'c', + self::FIELD_DEFINITION_TABLE, + 'a', + $expr->andX( + $expr->eq('c.id', 'a.contentclass_id'), + $expr->eq('c.version', 'a.version') + ) + ) + ->leftJoin( + 'c', + self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE, + 'g', + $expr->andX( + $expr->eq('c.id', 'g.contentclass_id'), + $expr->eq('c.version', 'g.contentclass_version') + ) + ) + ->leftJoin( + 'a', + self::MULTILINGUAL_FIELD_DEFINITION_TABLE, + 'ml', + $expr->andX( + $expr->eq('a.id', 'ml.contentclass_attribute_id'), + $expr->eq('a.version', 'ml.version') + ) + ) + ->orderBy('a.placement'); + + return $query; + } + + public function countInstancesOfType(int $typeId): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select($this->dbPlatform->getCountExpression('id')) + ->from('ezcontentobject') + ->where( + $query->expr()->eq( + 'contentclass_id', + $query->createPositionalParameter($typeId, ParameterType::INTEGER) + ) + ); + + $stmt = $query->execute(); + + return (int)$stmt->fetchColumn(); + } + + public function deleteFieldDefinitionsForType(int $typeId, int $status): void + { + $subQuery = $this->connection->createQueryBuilder(); + $subQuery + ->select('f_def.id as ezcontentclass_attribute_id') + ->from(self::FIELD_DEFINITION_TABLE, 'f_def') + ->where('f_def.contentclass_id = :content_type_id') + ->andWhere('f_def.id = ezcontentclass_attribute_ml.contentclass_attribute_id'); + + $deleteQuery = $this->connection->createQueryBuilder(); + $deleteQuery + ->delete(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) + ->where( + sprintf('EXISTS (%s)', $subQuery->getSQL()) + ) + // note: not all drivers support aliasing tables in DELETE query, hence the following: + ->andWhere(sprintf('%s.version = :status', self::MULTILINGUAL_FIELD_DEFINITION_TABLE)) + ->setParameter('content_type_id', $typeId, ParameterType::INTEGER) + ->setParameter('status', $status, ParameterType::INTEGER); + + $deleteQuery->execute(); + + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->delete(self::FIELD_DEFINITION_TABLE) + ->where( + $query->expr()->eq( + 'contentclass_id', + $query->createPositionalParameter($typeId, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + 'version', + $query->createPositionalParameter($status, ParameterType::INTEGER) + ) + ); + + $query->execute(); + } + + public function delete(int $typeId, int $status): void + { + $this->deleteGroupAssignmentsForType($typeId, $status); + $this->deleteFieldDefinitionsForType($typeId, $status); + $this->deleteTypeNameData($typeId, $status); + $this->deleteType($typeId, $status); + } + + public function deleteType(int $typeId, int $status): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::CONTENT_TYPE_TABLE) + ->where( + $query->expr()->andX( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($typeId, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'version', + $query->createPositionalParameter($status, ParameterType::INTEGER) + ) + ) + ); + $query->execute(); + } + + public function deleteGroupAssignmentsForType(int $typeId, int $status): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE) + ->where( + $query->expr()->eq( + 'contentclass_id', + $query->createPositionalParameter($typeId, ParameterType::INTEGER) + ) + )->andWhere( + $query->expr()->eq( + 'contentclass_version', + $query->createPositionalParameter($status, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + /** + * Append all columns of a given table to the SELECT part of a query. + * + * Each column is aliased in the form of + * AS _. + */ + private function selectColumns( + QueryBuilder $queryBuilder, + string $tableName, + string $tableAlias = '' + ): QueryBuilder { + if (empty($tableAlias)) { + $tableAlias = $tableName; + } + $queryBuilder + ->addSelect( + array_map( + function (string $columnName) use ($tableName, $tableAlias): string { + return sprintf( + '%s.%s as %s_%s', + $tableAlias, + $this->connection->quoteIdentifier($columnName), + $tableName, + $columnName + ); + }, + $this->columns[$tableName] + ) + ); + + return $queryBuilder; + } + + public function internalChangeContentTypeStatus( + int $typeId, + int $sourceStatus, + int $targetStatus, + string $tableName, + string $typeIdColumnName, + string $statusColumnName + ): void { + $query = $this->connection->createQueryBuilder(); + $query + ->update($tableName) + ->set( + $statusColumnName, + $query->createPositionalParameter($targetStatus, ParameterType::INTEGER) + ) + ->where( + $query->expr()->eq( + $typeIdColumnName, + $query->createPositionalParameter($typeId, ParameterType::INTEGER) + ) + )->andWhere( + $query->expr()->eq( + $statusColumnName, + $query->createPositionalParameter($sourceStatus, ParameterType::INTEGER) + ) + ); + + $query->execute(); + } + + public function publishTypeAndFields(int $typeId, int $sourceStatus, int $targetStatus): void + { + $this->internalChangeContentTypeStatus( + $typeId, + $sourceStatus, + $targetStatus, + self::CONTENT_TYPE_TABLE, + 'id', + 'version' + ); + + $this->internalChangeContentTypeStatus( + $typeId, + $sourceStatus, + $targetStatus, + self::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE, + 'contentclass_id', + 'contentclass_version' + ); + + $this->internalChangeContentTypeStatus( + $typeId, + $sourceStatus, + $targetStatus, + self::FIELD_DEFINITION_TABLE, + 'contentclass_id', + 'version' + ); + + $this->internalChangeContentTypeStatus( + $typeId, + $sourceStatus, + $targetStatus, + self::CONTENT_TYPE_NAME_TABLE, + 'contentclass_id', + 'contentclass_version' + ); + + $subQuery = $this->connection->createQueryBuilder(); + $subQuery + ->select('f_def.id as ezcontentclass_attribute_id') + ->from(self::FIELD_DEFINITION_TABLE, 'f_def') + ->where('f_def.contentclass_id = :type_id') + ->andWhere('f_def.id = ezcontentclass_attribute_ml.contentclass_attribute_id'); + + $mlDataPublishQuery = $this->connection->createQueryBuilder(); + $mlDataPublishQuery + ->update(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) + ->set('version', ':target_status') + ->where( + sprintf('EXISTS (%s)', $subQuery->getSQL()) + ) + // note: not all drivers support aliasing tables in UPDATE query, hence the following: + ->andWhere( + sprintf('%s.version = :source_status', self::MULTILINGUAL_FIELD_DEFINITION_TABLE) + ) + ->setParameter('type_id', $typeId, ParameterType::INTEGER) + ->setParameter('target_status', $targetStatus, ParameterType::INTEGER) + ->setParameter('source_status', $sourceStatus, ParameterType::INTEGER); + + $mlDataPublishQuery->execute(); + } + + public function getSearchableFieldMapData(): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + 'f_def.identifier AS field_definition_identifier', + 'ct.identifier AS content_type_identifier', + 'f_def.id AS field_definition_id', + 'f_def.data_type_string AS field_type_identifier' + ) + ->from(self::FIELD_DEFINITION_TABLE, 'f_def') + ->innerJoin('f_def', self::CONTENT_TYPE_TABLE, 'ct', 'f_def.contentclass_id = ct.id') + ->where( + $query->expr()->eq( + 'f_def.is_searchable', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ) + ); + + $statement = $query->execute($query); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function removeFieldDefinitionTranslation( + int $fieldDefinitionId, + string $languageCode, + int $status + ): void { + $languageId = $this->languageMaskGenerator->generateLanguageMaskFromLanguageCodes( + [$languageCode] + ); + + $deleteQuery = $this->connection->createQueryBuilder(); + $deleteQuery + ->delete(self::MULTILINGUAL_FIELD_DEFINITION_TABLE) + ->where('contentclass_attribute_id = :field_definition_id') + ->andWhere('version = :status') + ->andWhere('language_id = :language_id') + ->setParameter('field_definition_id', $fieldDefinitionId, ParameterType::INTEGER) + ->setParameter('status', $status, ParameterType::INTEGER) + ->setParameter('language_id', $languageId, ParameterType::INTEGER); + + $deleteQuery->execute(); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function removeByUserAndVersion(int $userId, int $version): void + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder->delete(self::CONTENT_TYPE_TABLE) + ->where('creator_id = :user or modifier_id = :user') + ->andWhere('version = :version') + ->setParameter('user', $userId, ParameterType::INTEGER) + ->setParameter('version', $version, ParameterType::INTEGER) + ; + + try { + $this->connection->beginTransaction(); + + $queryBuilder->execute(); + $this->cleanupAssociations(); + + $this->connection->commit(); + } catch (DBALException $e) { + $this->connection->rollBack(); + + throw $e; + } + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + private function cleanupAssociations(): void + { + $this->cleanupClassAttributeTable(); + $this->cleanupClassAttributeMLTable(); + $this->cleanupClassGroupTable(); + $this->cleanupClassNameTable(); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + private function cleanupClassAttributeTable(): void + { + $sql = <<connection->executeUpdate($sql); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + private function cleanupClassAttributeMLTable(): void + { + $sql = <<connection->executeUpdate($sql); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + private function cleanupClassGroupTable(): void + { + $sql = <<connection->executeUpdate($sql); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + private function cleanupClassNameTable(): void + { + $sql = <<< SQL + DELETE FROM ezcontentclass_name + WHERE NOT EXISTS ( + SELECT 1 FROM ezcontentclass + WHERE ezcontentclass.id = ezcontentclass_name.contentclass_id + AND ezcontentclass.version = ezcontentclass_name.contentclass_version + ) +SQL; + $this->connection->executeUpdate($sql); + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..fd6229651d --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Type/Gateway/ExceptionConversion.php @@ -0,0 +1,346 @@ +innerGateway = $innerGateway; + } + + public function insertGroup(Group $group): int + { + try { + return $this->innerGateway->insertGroup($group); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateGroup(GroupUpdateStruct $group): void + { + try { + $this->innerGateway->updateGroup($group); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countTypesInGroup(int $groupId): int + { + try { + return $this->innerGateway->countTypesInGroup($groupId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countGroupsForType(int $typeId, int $status): int + { + try { + return $this->innerGateway->countGroupsForType($typeId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteGroup(int $groupId): void + { + try { + $this->innerGateway->deleteGroup($groupId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadGroupData(array $groupIds): array + { + try { + return $this->innerGateway->loadGroupData($groupIds); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadGroupDataByIdentifier(string $identifier): array + { + try { + return $this->innerGateway->loadGroupDataByIdentifier($identifier); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadAllGroupsData(): array + { + try { + return $this->innerGateway->loadAllGroupsData(); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadTypesDataForGroup(int $groupId, int $status): array + { + try { + return $this->innerGateway->loadTypesDataForGroup($groupId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function insertType(Type $type, ?int $typeId = null): int + { + try { + return $this->innerGateway->insertType($type, $typeId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function insertGroupAssignment(int $groupId, int $typeId, int $status): void + { + try { + $this->innerGateway->insertGroupAssignment($groupId, $typeId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteGroupAssignment(int $groupId, int $typeId, int $status): void + { + try { + $this->innerGateway->deleteGroupAssignment($groupId, $typeId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadFieldDefinition(int $id, int $status): array + { + try { + return $this->innerGateway->loadFieldDefinition($id, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function insertFieldDefinition( + int $typeId, + int $status, + FieldDefinition $fieldDefinition, + StorageFieldDefinition $storageFieldDef + ): int { + try { + return $this->innerGateway->insertFieldDefinition( + $typeId, + $status, + $fieldDefinition, + $storageFieldDef + ); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteFieldDefinition( + int $typeId, + int $status, + int $fieldDefinitionId + ): void { + try { + $this->innerGateway->deleteFieldDefinition($typeId, $status, $fieldDefinitionId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateFieldDefinition( + int $typeId, + int $status, + FieldDefinition $fieldDefinition, + StorageFieldDefinition $storageFieldDef + ): void { + try { + $this->innerGateway->updateFieldDefinition($typeId, $status, $fieldDefinition, $storageFieldDef); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateType(int $typeId, int $status, Type $type): void + { + try { + $this->innerGateway->updateType($typeId, $status, $type); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadTypesListData(array $typeIds): array + { + try { + return $this->innerGateway->loadTypesListData($typeIds); + } catch (PDOException | DBALException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadTypeData(int $typeId, int $status): array + { + try { + return $this->innerGateway->loadTypeData($typeId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadTypeDataByIdentifier(string $identifier, int $status): array + { + try { + return $this->innerGateway->loadTypeDataByIdentifier($identifier, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadTypeDataByRemoteId(string $remoteId, int $status): array + { + try { + return $this->innerGateway->loadTypeDataByRemoteId($remoteId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadTypesDataByFieldDefinitionIdentifier(string $identifier): array + { + try { + return $this->innerGateway->loadTypesDataByFieldDefinitionIdentifier($identifier); + } catch (PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countInstancesOfType(int $typeId): int + { + try { + return $this->innerGateway->countInstancesOfType($typeId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function delete(int $typeId, int $status): void + { + try { + $this->innerGateway->delete($typeId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteFieldDefinitionsForType(int $typeId, int $status): void + { + try { + $this->innerGateway->deleteFieldDefinitionsForType($typeId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteType(int $typeId, int $status): void + { + try { + $this->innerGateway->deleteType($typeId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteGroupAssignmentsForType(int $typeId, int $status): void + { + try { + $this->innerGateway->deleteGroupAssignmentsForType($typeId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function publishTypeAndFields(int $typeId, int $sourceStatus, int $targetStatus): void + { + try { + $this->innerGateway->publishTypeAndFields($typeId, $sourceStatus, $targetStatus); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getSearchableFieldMapData(): array + { + try { + return $this->innerGateway->getSearchableFieldMapData(); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function removeFieldDefinitionTranslation( + int $fieldDefinitionId, + string $languageCode, + int $status + ): void { + try { + $this->innerGateway->removeFieldDefinitionTranslation( + $fieldDefinitionId, + $languageCode, + $status + ); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function removeByUserAndVersion(int $userId, int $version): void + { + try { + $this->innerGateway->removeByUserAndVersion($userId, $version); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Type\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/Content/Type/Handler.php b/src/lib/Persistence/Legacy/Content/Type/Handler.php new file mode 100644 index 0000000000..88abfb59ef --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Type/Handler.php @@ -0,0 +1,710 @@ +contentTypeGateway = $contentTypeGateway; + $this->mapper = $mapper; + $this->updateHandler = $updateHandler; + $this->storageDispatcher = $storageDispatcher; + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\Group\CreateStruct $createStruct + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Group + */ + public function createGroup(GroupCreateStruct $createStruct) + { + $group = $this->mapper->createGroupFromCreateStruct( + $createStruct + ); + + $group->id = $this->contentTypeGateway->insertGroup( + $group + ); + + return $group; + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\Group\UpdateStruct $struct + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Group + */ + public function updateGroup(GroupUpdateStruct $struct) + { + $this->contentTypeGateway->updateGroup( + $struct + ); + + return $this->loadGroup($struct->id); + } + + /** + * @param mixed $groupId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If type group contains types + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If type group with id is not found + */ + public function deleteGroup($groupId) + { + if ($this->contentTypeGateway->countTypesInGroup($groupId) !== 0) { + throw new Exception\GroupNotEmpty($groupId); + } + $this->contentTypeGateway->deleteGroup($groupId); + } + + /** + * @param mixed $groupId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If type group with $groupId is not found + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Group + */ + public function loadGroup($groupId) + { + $groups = $this->mapper->extractGroupsFromRows( + $this->contentTypeGateway->loadGroupData([$groupId]) + ); + + if (count($groups) !== 1) { + throw new Exception\TypeGroupNotFound($groupId); + } + + return $groups[0]; + } + + /** + * {@inheritdoc} + */ + public function loadGroups(array $groupIds) + { + $groups = $this->mapper->extractGroupsFromRows( + $this->contentTypeGateway->loadGroupData($groupIds) + ); + + $listByGroupIds = []; + foreach ($groups as $group) { + $listByGroupIds[$group->id] = $group; + } + + return $listByGroupIds; + } + + /** + * @param string $identifier + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If type group with $identifier is not found + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Group + */ + public function loadGroupByIdentifier($identifier) + { + $groups = $this->mapper->extractGroupsFromRows( + $this->contentTypeGateway->loadGroupDataByIdentifier($identifier) + ); + + if (count($groups) !== 1) { + throw new Exception\TypeGroupNotFound($identifier); + } + + return $groups[0]; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Group[] + */ + public function loadAllGroups() + { + return $this->mapper->extractGroupsFromRows( + $this->contentTypeGateway->loadAllGroupsData() + ); + } + + /** + * @param mixed $groupId + * @param int $status + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type[] + */ + public function loadContentTypes($groupId, $status = 0) + { + return $this->mapper->extractTypesFromRows( + $this->contentTypeGateway->loadTypesDataForGroup($groupId, $status) + ); + } + + public function loadContentTypeList(array $contentTypeIds): array + { + return $this->mapper->extractTypesFromRows( + $this->contentTypeGateway->loadTypesListData($contentTypeIds), + true + ); + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Type[] + */ + public function loadContentTypesByFieldDefinitionIdentifier(string $identifier): array + { + return $this->mapper->extractTypesFromRows( + $this->contentTypeGateway->loadTypesDataByFieldDefinitionIdentifier($identifier) + ); + } + + /** + * Loads a content type by id and status. + * + * Note: This method is responsible of having the Field Definitions of the loaded ContentType sorted by placement. + * + * @param int $contentTypeId + * @param int $status + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + public function load($contentTypeId, $status = Type::STATUS_DEFINED) + { + return $this->loadFromRows( + $this->contentTypeGateway->loadTypeData($contentTypeId, $status), + $contentTypeId, + $status + ); + } + + /** + * Loads a (defined) content type by identifier. + * + * Note: This method is responsible of having the Field Definitions of the loaded ContentType sorted by placement. + * + * @param string $identifier + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If defined type is not found + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + public function loadByIdentifier($identifier) + { + $rows = $this->contentTypeGateway->loadTypeDataByIdentifier($identifier, Type::STATUS_DEFINED); + + return $this->loadFromRows($rows, $identifier, Type::STATUS_DEFINED); + } + + /** + * Loads a (defined) content type by remote id. + * + * Note: This method is responsible of having the Field Definitions of the loaded ContentType sorted by placement. + * + * @param mixed $remoteId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If defined type is not found + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + public function loadByRemoteId($remoteId) + { + return $this->loadFromRows( + $this->contentTypeGateway->loadTypeDataByRemoteId($remoteId, Type::STATUS_DEFINED), + $remoteId, + Type::STATUS_DEFINED + ); + } + + /** + * Loads a single Type from $rows. + * + * @param array $rows + * @param mixed $typeIdentifier + * @param int $status + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + protected function loadFromRows(array $rows, $typeIdentifier, $status) + { + $types = $this->mapper->extractTypesFromRows($rows); + if (count($types) !== 1) { + throw new Exception\TypeNotFound($typeIdentifier, $status); + } + + return $types[0]; + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\CreateStruct $createStruct + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + public function create(CreateStruct $createStruct) + { + return $this->internalCreate($createStruct); + } + + /** + * Internal method for creating ContentType. + * + * Used by self::create(), self::createDraft() and self::copy() + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\CreateStruct $createStruct + * @param mixed|null $contentTypeId Used by self::createDraft() to retain ContentType id in the draft + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + protected function internalCreate(CreateStruct $createStruct, $contentTypeId = null) + { + foreach ($createStruct->fieldDefinitions as $fieldDef) { + if (!is_int($fieldDef->position)) { + throw new InvalidArgumentException( + 'position', + "'" . var_export($fieldDef->position, true) . + "' is incorrect value in class FieldDefinition, an integer is required." + ); + } + } + + $createStruct = clone $createStruct; + $contentType = $this->mapper->createTypeFromCreateStruct( + $createStruct + ); + + $contentType->id = $this->contentTypeGateway->insertType( + $contentType, + $contentTypeId + ); + + foreach ($contentType->groupIds as $groupId) { + $this->contentTypeGateway->insertGroupAssignment( + $groupId, + $contentType->id, + $contentType->status + ); + } + + foreach ($contentType->fieldDefinitions as $fieldDef) { + $storageFieldDef = new StorageFieldDefinition(); + $this->mapper->toStorageFieldDefinition($fieldDef, $storageFieldDef); + $fieldDef->id = $this->contentTypeGateway->insertFieldDefinition( + $contentType->id, + $contentType->status, + $fieldDef, + $storageFieldDef + ); + + $this->storageDispatcher->storeFieldConstraintsData($fieldDef, $contentType->status); + } + + return $contentType; + } + + /** + * @param mixed $typeId + * @param int $status + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\UpdateStruct $updateStruct + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + public function update($typeId, $status, UpdateStruct $updateStruct) + { + $contentType = $this->mapper->createTypeFromUpdateStruct( + $updateStruct + ); + $this->contentTypeGateway->updateType($typeId, $status, $contentType); + + return $this->load($typeId, $status); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If type is defined and still has content + * + * @param mixed $contentTypeId + * @param int $status + * + * @return bool + */ + public function delete($contentTypeId, $status) + { + if (Type::STATUS_DEFINED === $status && $this->contentTypeGateway->countInstancesOfType($contentTypeId)) { + throw new BadStateException( + '$contentTypeId', + 'Content type with the given ID still has Content items and cannot be deleted' + ); + } + + try { + $fieldDefinitions = $this->load($contentTypeId, $status)->fieldDefinitions; + } catch (Exception\TypeNotFound $e) { + $fieldDefinitions = []; + } + + foreach ($fieldDefinitions as $fieldDefinition) { + $this->storageDispatcher->deleteFieldConstraintsData($fieldDefinition->fieldType, $fieldDefinition->id, $status); + } + + $this->contentTypeGateway->delete($contentTypeId, $status); + + // @todo FIXME: Return true only if deletion happened + return true; + } + + /** + * Creates a draft of existing defined content type. + * + * Updates modified date, sets $modifierId and status to Type::STATUS_DRAFT on the new returned draft. + * + * @param mixed $modifierId + * @param mixed $contentTypeId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If type with defined status is not found + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + public function createDraft($modifierId, $contentTypeId) + { + $createStruct = $this->mapper->createCreateStructFromType( + $this->load($contentTypeId, Type::STATUS_DEFINED) + ); + $createStruct->status = Type::STATUS_DRAFT; + $createStruct->modifierId = $modifierId; + $createStruct->modified = time(); + + return $this->internalCreate($createStruct, $contentTypeId); + } + + /** + * @param mixed $userId + * @param mixed $contentTypeId + * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + public function copy($userId, $contentTypeId, $status) + { + $createStruct = $this->mapper->createCreateStructFromType( + $this->load($contentTypeId, $status) + ); + $createStruct->modifierId = $userId; + $createStruct->created = $createStruct->modified = time(); + $createStruct->creatorId = $userId; + $createStruct->remoteId = md5(uniqid(get_class($createStruct), true)); + + // extract actual identifier name, without "copy_of_" and number + $originalIdentifier = preg_replace('/^copy_of_(.+)_\d+$/', '$1', $createStruct->identifier); + + // set temporary identifier + $createStruct->identifier = $createStruct->remoteId; + + // Set FieldDefinition ids to null to trigger creating new id + foreach ($createStruct->fieldDefinitions as $fieldDefinition) { + $fieldDefinition->id = null; + } + + $contentTypeCopy = $this->internalCreate($createStruct); + $updateStruct = $this->mapper->createUpdateStructFromType($contentTypeCopy); + $updateStruct->identifier = 'copy_of_' . $originalIdentifier . '_' . $contentTypeCopy->id; + + return $this->update($contentTypeCopy->id, $contentTypeCopy->status, $updateStruct); + } + + /** + * Unlink a content type group from a content type. + * + * @param mixed $groupId + * @param mixed $contentTypeId + * @param int $status + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If group or type with provided status is not found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If $groupId is last group on $contentTypeId or + * not a group assigned to type + * + * @todo Add throws for NotFound and BadState when group is not assigned to type + */ + public function unlink($groupId, $contentTypeId, $status) + { + $groupCount = $this->contentTypeGateway->countGroupsForType($contentTypeId, $status); + if ($groupCount < 2) { + throw new Exception\RemoveLastGroupFromType($contentTypeId, $status); + } + + $this->contentTypeGateway->deleteGroupAssignment($groupId, $contentTypeId, $status); + // @todo FIXME: What is to be returned? + return true; + } + + /** + * Link a content type group with a content type. + * + * @param mixed $groupId + * @param mixed $contentTypeId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If group or type with provided status is not found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If type is already part of group + * + * @todo Above throws are not implemented + */ + public function link($groupId, $contentTypeId, $status) + { + $this->contentTypeGateway->insertGroupAssignment($groupId, $contentTypeId, $status); + // @todo FIXME: What is to be returned? + return true; + } + + /** + * Returns field definition for the given field definition id. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If field definition is not found + * + * @param mixed $id + * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition + */ + public function getFieldDefinition($id, $status) + { + $rows = $this->contentTypeGateway->loadFieldDefinition($id, $status); + + if (count($rows) === 0) { + throw new NotFoundException( + 'FieldDefinition', + [ + 'id' => $id, + 'status' => $status, + ] + ); + } + + $multilingualData = $this->mapper->extractMultilingualData($rows); + + return $this->mapper->extractFieldFromRow(reset($rows), $multilingualData, $status); + } + + /** + * Counts the number of Content instances of the ContentType identified by given $contentTypeId. + * + * @param mixed $contentTypeId + * + * @return int + */ + public function getContentCount($contentTypeId) + { + return $this->contentTypeGateway->countInstancesOfType($contentTypeId); + } + + /** + * Adds a new field definition to an existing Type. + * + * This method creates a new status of the Type with the $fieldDefinition + * added. It does not update existing content objects depending on the + * field (default) values. + * + * @param mixed $contentTypeId + * @param int $status One of Type::STATUS_DEFINED|Type::STATUS_DRAFT|Type::STATUS_MODIFIED + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDefinition + */ + public function addFieldDefinition($contentTypeId, $status, FieldDefinition $fieldDefinition) + { + $storageFieldDef = new StorageFieldDefinition(); + $this->mapper->toStorageFieldDefinition($fieldDefinition, $storageFieldDef); + $fieldDefinition->id = $this->contentTypeGateway->insertFieldDefinition( + $contentTypeId, + $status, + $fieldDefinition, + $storageFieldDef + ); + + $this->storageDispatcher->storeFieldConstraintsData($fieldDefinition, $status); + } + + /** + * Removes a field definition from an existing Type. + * + * This method creates a new status of the Type with the field definition + * referred to by $fieldDefinitionId removed. It does not update existing + * content objects depending on the field (default) values. + * + * @param mixed $contentTypeId + * @param mixed $fieldDefinitionId + * + * @return bool + */ + public function removeFieldDefinition( + int $contentTypeId, + int $status, + FieldDefinition $fieldDefinition + ): void { + $this->storageDispatcher->deleteFieldConstraintsData( + $fieldDefinition->fieldType, + $fieldDefinition->id, + $status + ); + + $this->contentTypeGateway->deleteFieldDefinition( + $contentTypeId, + $status, + $fieldDefinition->id + ); + } + + /** + * This method updates the given $fieldDefinition on a Type. + * + * This method creates a new status of the Type with the updated + * $fieldDefinition. It does not update existing content objects depending + * on the + * field (default) values. + * + * @param mixed $contentTypeId + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDefinition + */ + public function updateFieldDefinition($contentTypeId, $status, FieldDefinition $fieldDefinition) + { + $storageFieldDef = new StorageFieldDefinition(); + $this->mapper->toStorageFieldDefinition($fieldDefinition, $storageFieldDef); + $this->contentTypeGateway->updateFieldDefinition($contentTypeId, $status, $fieldDefinition, $storageFieldDef); + $this->storageDispatcher->storeFieldConstraintsData($fieldDefinition, $status); + } + + /** + * Update content objects. + * + * Updates content objects, depending on the changed field definitions. + * + * A content type has a state which tells if its content objects yet have + * been adapted. + * + * Flags the content type as updated. + * + * @param mixed $contentTypeId + */ + public function publish($contentTypeId) + { + $toType = $this->load($contentTypeId, Type::STATUS_DRAFT); + + try { + $fromType = $this->load($contentTypeId, Type::STATUS_DEFINED); + $this->updateHandler->deleteOldType($fromType); + } catch (Exception\TypeNotFound $e) { + // If no old type is found, no updates are necessary to it + } + + $this->updateHandler->publishNewType($toType, Type::STATUS_DEFINED); + + foreach ($toType->fieldDefinitions as $fieldDefinition) { + $this->storageDispatcher->publishFieldConstraintsData($fieldDefinition); + } + } + + public function getSearchableFieldMap() + { + $fieldMap = []; + $rows = $this->contentTypeGateway->getSearchableFieldMapData(); + + foreach ($rows as $row) { + $fieldMap[$row['content_type_identifier']][$row['field_definition_identifier']] = [ + 'field_type_identifier' => $row['field_type_identifier'], + 'field_definition_id' => $row['field_definition_id'], + ]; + } + + return $fieldMap; + } + + /** + * @param int $contentTypeId + * @param string $languageCode + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + public function removeContentTypeTranslation(int $contentTypeId, string $languageCode): Type + { + $type = $this->load($contentTypeId, Type::STATUS_DRAFT); + + unset($type->name[$languageCode]); + unset($type->description[$languageCode]); + + foreach ($type->fieldDefinitions as $fieldDefinition) { + $this->contentTypeGateway->removeFieldDefinitionTranslation( + $fieldDefinition->id, + $languageCode, + Type::STATUS_DRAFT + ); + + //Refresh FieldDefinition object after removing translation data. + $fieldDefinition = $this->getFieldDefinition( + $fieldDefinition->id, + Type::STATUS_DRAFT + ); + unset($fieldDefinition->name[$languageCode]); + unset($fieldDefinition->description[$languageCode]); + $storageFieldDefinition = new StorageFieldDefinition(); + $this->mapper->toStorageFieldDefinition($fieldDefinition, $storageFieldDefinition); + $this->contentTypeGateway->updateFieldDefinition( + $contentTypeId, + Type::STATUS_DRAFT, + $fieldDefinition, + $storageFieldDefinition + ); + } + + $updateStruct = $this->mapper->createUpdateStructFromType($type); + + return $this->update($type->id, Type::STATUS_DRAFT, $updateStruct); + } + + public function deleteByUserAndStatus(int $userId, int $status): void + { + $this->contentTypeGateway->removeByUserAndVersion($userId, $status); + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler'); diff --git a/src/lib/Persistence/Legacy/Content/Type/Mapper.php b/src/lib/Persistence/Legacy/Content/Type/Mapper.php new file mode 100644 index 0000000000..af8c060d95 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Type/Mapper.php @@ -0,0 +1,533 @@ +converterRegistry = $converterRegistry; + $this->maskGenerator = $maskGenerator; + $this->storageDispatcher = $storageDispatcher; + } + + /** + * Creates a Group from its create struct. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\Group\CreateStruct $struct + * + * @todo $description is not supported by database, yet + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Group + */ + public function createGroupFromCreateStruct(GroupCreateStruct $struct) + { + $group = new Group(); + + $group->name = $struct->name; + + // $group->description is intentionally left out, since DB structure does not support it, yet + + $group->identifier = $struct->identifier; + $group->created = $struct->created; + $group->modified = $struct->modified; + $group->creatorId = $struct->creatorId; + $group->modifierId = $struct->modifierId; + $group->isSystem = $struct->isSystem; + + return $group; + } + + /** + * Extracts Group objects from the given $rows. + * + * @param array $rows + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Group[] + */ + public function extractGroupsFromRows(array $rows) + { + $groups = []; + + foreach ($rows as $row) { + $group = new Group(); + $group->id = (int)$row['id']; + $group->created = (int)$row['created']; + $group->creatorId = (int)$row['creator_id']; + $group->modified = (int)$row['modified']; + $group->modifierId = (int)$row['modifier_id']; + $group->identifier = $row['name']; + $group->isSystem = (bool)$row['is_system']; + + $groups[] = $group; + } + + return $groups; + } + + /** + * Extracts types and related data from the given $rows. + * + * @param array $rows + * @param bool $keepTypeIdAsKey + * + * @return array (Type) + */ + public function extractTypesFromRows(array $rows, bool $keepTypeIdAsKey = false) + { + $types = []; + $fields = []; + + foreach ($rows as $row) { + $typeId = (int)$row['ezcontentclass_id']; + if (!isset($types[$typeId])) { + $types[$typeId] = $this->extractTypeFromRow($row); + } + + $fieldId = (int)$row['ezcontentclass_attribute_id']; + + if ($fieldId && !isset($fields[$fieldId])) { + $fieldDataRows = array_filter($rows, static function (array $row) use ($fieldId) { + return (int) $row['ezcontentclass_attribute_id'] === (int) $fieldId; + }); + + $multilingualData = $this->extractMultilingualData($fieldDataRows); + + $types[$typeId]->fieldDefinitions[] = $fields[$fieldId] = $this->extractFieldFromRow($row, $multilingualData, $types[$typeId]->status); + } + + $groupId = (int)$row['ezcontentclass_classgroup_group_id']; + if (!in_array($groupId, $types[$typeId]->groupIds)) { + $types[$typeId]->groupIds[] = $groupId; + } + } + + foreach ($types as $type) { + sort($type->groupIds); + } + + if ($keepTypeIdAsKey) { + return $types; + } + + // Re-index $types to avoid people relying on ID keys + return array_values($types); + } + + public function extractMultilingualData(array $fieldDefinitionRows): array + { + return array_map(static function (array $fieldData) { + return [ + 'ezcontentclass_attribute_multilingual_name' => $fieldData['ezcontentclass_attribute_multilingual_name'] ?? null, + 'ezcontentclass_attribute_multilingual_description' => $fieldData['ezcontentclass_attribute_multilingual_description'] ?? null, + 'ezcontentclass_attribute_multilingual_language_id' => $fieldData['ezcontentclass_attribute_multilingual_language_id'] ?? null, + 'ezcontentclass_attribute_multilingual_data_text' => $fieldData['ezcontentclass_attribute_multilingual_data_text'] ?? null, + 'ezcontentclass_attribute_multilingual_data_json' => $fieldData['ezcontentclass_attribute_multilingual_data_json'] ?? null, + ]; + }, $fieldDefinitionRows); + } + + /** + * Creates a Type from the data in $row. + * + * @param array $row + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + protected function extractTypeFromRow(array $row) + { + $type = new Type(); + + $type->id = (int)$row['ezcontentclass_id']; + $type->status = (int)$row['ezcontentclass_version']; + $type->name = $this->unserialize($row['ezcontentclass_serialized_name_list']); + $type->description = $this->unserialize($row['ezcontentclass_serialized_description_list']); + // Unset redundant data + unset( + $type->name['always-available'], + $type->name[0], + $type->description['always-available'], + $type->description[0] + ); + $type->identifier = $row['ezcontentclass_identifier']; + $type->created = (int)$row['ezcontentclass_created']; + $type->modified = (int)$row['ezcontentclass_modified']; + $type->modifierId = (int)$row['ezcontentclass_modifier_id']; + $type->creatorId = (int)$row['ezcontentclass_creator_id']; + $type->remoteId = $row['ezcontentclass_remote_id']; + $type->urlAliasSchema = $row['ezcontentclass_url_alias_name']; + $type->nameSchema = $row['ezcontentclass_contentobject_name']; + $type->isContainer = ($row['ezcontentclass_is_container'] == 1); + $type->initialLanguageId = (int)$row['ezcontentclass_initial_language_id']; + $type->defaultAlwaysAvailable = ($row['ezcontentclass_always_available'] == 1); + $type->sortField = (int)$row['ezcontentclass_sort_field']; + $type->sortOrder = (int)$row['ezcontentclass_sort_order']; + $type->languageCodes = $this->maskGenerator->extractLanguageCodesFromMask((int)$row['ezcontentclass_language_mask']); + + $type->groupIds = []; + $type->fieldDefinitions = []; + + return $type; + } + + /** + * Creates a FieldDefinition from the data in $row. + * + * @param array $row + * @param array $multilingualData + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition + */ + public function extractFieldFromRow(array $row, array $multilingualData = [], int $status = Type::STATUS_DEFINED) + { + $storageFieldDef = $this->extractStorageFieldFromRow($row, $multilingualData); + + $field = new FieldDefinition(); + + $field->id = (int)$row['ezcontentclass_attribute_id']; + $field->name = $this->unserialize($row['ezcontentclass_attribute_serialized_name_list']); + $field->description = $this->unserialize($row['ezcontentclass_attribute_serialized_description_list']); + // Unset redundant data + unset( + $field->name['always-available'], + $field->name[0], + $field->description['always-available'], + $field->description[0] + ); + $field->identifier = $row['ezcontentclass_attribute_identifier']; + $field->fieldGroup = $row['ezcontentclass_attribute_category']; + $field->fieldType = $row['ezcontentclass_attribute_data_type_string']; + $field->isTranslatable = ($row['ezcontentclass_attribute_can_translate'] == 1); + $field->isRequired = $row['ezcontentclass_attribute_is_required'] == 1; + $field->isThumbnail = !empty($row['ezcontentclass_attribute_is_thumbnail']); + $field->isInfoCollector = $row['ezcontentclass_attribute_is_information_collector'] == 1; + + $field->isSearchable = (bool)$row['ezcontentclass_attribute_is_searchable']; + $field->position = (int)$row['ezcontentclass_attribute_placement']; + + $mainLanguageCode = $this->maskGenerator->extractLanguageCodesFromMask((int)$row['ezcontentclass_initial_language_id']); + $field->mainLanguageCode = array_shift($mainLanguageCode); + + $this->toFieldDefinition($storageFieldDef, $field, $status); + + return $field; + } + + /** + * Extracts a StorageFieldDefinition from $row. + * + * @param array $row + * @param array $multilingualDataRow + * + * @return \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition + */ + protected function extractStorageFieldFromRow(array $row, array $multilingualDataRow = []) + { + $storageFieldDef = new StorageFieldDefinition(); + + $storageFieldDef->dataFloat1 = isset($row['ezcontentclass_attribute_data_float1']) + ? (float)$row['ezcontentclass_attribute_data_float1'] + : null; + $storageFieldDef->dataFloat2 = isset($row['ezcontentclass_attribute_data_float2']) + ? (float)$row['ezcontentclass_attribute_data_float2'] + : null; + $storageFieldDef->dataFloat3 = isset($row['ezcontentclass_attribute_data_float3']) + ? (float)$row['ezcontentclass_attribute_data_float3'] + : null; + $storageFieldDef->dataFloat4 = isset($row['ezcontentclass_attribute_data_float4']) + ? (float)$row['ezcontentclass_attribute_data_float4'] + : null; + $storageFieldDef->dataInt1 = isset($row['ezcontentclass_attribute_data_int1']) + ? (int)$row['ezcontentclass_attribute_data_int1'] + : null; + $storageFieldDef->dataInt2 = isset($row['ezcontentclass_attribute_data_int2']) + ? (int)$row['ezcontentclass_attribute_data_int2'] + : null; + $storageFieldDef->dataInt3 = isset($row['ezcontentclass_attribute_data_int3']) + ? (int)$row['ezcontentclass_attribute_data_int3'] + : null; + $storageFieldDef->dataInt4 = isset($row['ezcontentclass_attribute_data_int4']) + ? (int)$row['ezcontentclass_attribute_data_int4'] + : null; + $storageFieldDef->dataText1 = $row['ezcontentclass_attribute_data_text1']; + $storageFieldDef->dataText2 = $row['ezcontentclass_attribute_data_text2']; + $storageFieldDef->dataText3 = $row['ezcontentclass_attribute_data_text3']; + $storageFieldDef->dataText4 = $row['ezcontentclass_attribute_data_text4']; + $storageFieldDef->dataText5 = $row['ezcontentclass_attribute_data_text5']; + $storageFieldDef->serializedDataText = $row['ezcontentclass_attribute_serialized_data_text']; + + foreach ($multilingualDataRow as $languageDataRow) { + $languageCodes = $this->maskGenerator->extractLanguageCodesFromMask((int)$languageDataRow['ezcontentclass_attribute_multilingual_language_id']); + + if (empty($languageCodes)) { + continue; + } + $languageCode = reset($languageCodes); + + $multilingualData = new MultilingualStorageFieldDefinition(); + + $nameList = $this->unserialize($row['ezcontentclass_attribute_serialized_name_list']); + $name = $nameList[$languageCode] ?? reset($nameList); + $description = $this->unserialize($row['ezcontentclass_attribute_serialized_description_list'])[$languageCode] ?? null; + + $multilingualData->name = $languageDataRow['ezcontentclass_attribute_multilingual_name'] ?? $name; + $multilingualData->description = $languageDataRow['ezcontentclass_attribute_multilingual_description'] ?? $description; + $multilingualData->dataText = $languageDataRow['ezcontentclass_attribute_multilingual_data_text']; + $multilingualData->dataJson = $languageDataRow['ezcontentclass_attribute_multilingual_data_json']; + $multilingualData->languageId = (int)$languageDataRow['ezcontentclass_attribute_multilingual_language_id']; + + $storageFieldDef->multilingualData[$languageCode] = $multilingualData; + } + + return $storageFieldDef; + } + + /** + * Maps properties from $struct to $type. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\CreateStruct $createStruct + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + public function createTypeFromCreateStruct(CreateStruct $createStruct) + { + $type = new Type(); + + $type->name = $createStruct->name; + $type->status = $createStruct->status; + $type->description = $createStruct->description; + $type->identifier = $createStruct->identifier; + $type->created = $createStruct->created; + $type->modified = $createStruct->modified; + $type->creatorId = $createStruct->creatorId; + $type->modifierId = $createStruct->modifierId; + $type->remoteId = $createStruct->remoteId; + $type->urlAliasSchema = $createStruct->urlAliasSchema; + $type->nameSchema = $createStruct->nameSchema; + $type->isContainer = $createStruct->isContainer; + $type->initialLanguageId = $createStruct->initialLanguageId; + $type->groupIds = $createStruct->groupIds; + $type->fieldDefinitions = $createStruct->fieldDefinitions; + $type->defaultAlwaysAvailable = $createStruct->defaultAlwaysAvailable; + $type->sortField = $createStruct->sortField; + $type->sortOrder = $createStruct->sortOrder; + $type->languageCodes = array_keys($createStruct->name); + + return $type; + } + + /** + * Creates a create struct from an existing $type. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type $type + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\CreateStruct + */ + public function createCreateStructFromType(Type $type) + { + $createStruct = new CreateStruct(); + + $createStruct->name = $type->name; + $createStruct->status = $type->status; + $createStruct->description = $type->description; + $createStruct->identifier = $type->identifier; + $createStruct->created = $type->created; + $createStruct->modified = $type->modified; + $createStruct->creatorId = $type->creatorId; + $createStruct->modifierId = $type->modifierId; + $createStruct->remoteId = $type->remoteId; + $createStruct->urlAliasSchema = $type->urlAliasSchema; + $createStruct->nameSchema = $type->nameSchema; + $createStruct->isContainer = $type->isContainer; + $createStruct->initialLanguageId = $type->initialLanguageId; + $createStruct->groupIds = $type->groupIds; + $createStruct->fieldDefinitions = $type->fieldDefinitions; + $createStruct->defaultAlwaysAvailable = $type->defaultAlwaysAvailable; + $createStruct->sortField = $type->sortField; + $createStruct->sortOrder = $type->sortOrder; + + return $createStruct; + } + + /** + * Creates an update struct from an existing $type. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type $type + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\UpdateStruct + */ + public function createUpdateStructFromType(Type $type) + { + $updateStruct = new UpdateStruct(); + + $updateStruct->name = $type->name; + $updateStruct->description = $type->description; + $updateStruct->identifier = $type->identifier; + $updateStruct->modified = $type->modified; + $updateStruct->modifierId = $type->modifierId; + $updateStruct->remoteId = $type->remoteId; + $updateStruct->urlAliasSchema = $type->urlAliasSchema; + $updateStruct->nameSchema = $type->nameSchema; + $updateStruct->isContainer = $type->isContainer; + $updateStruct->initialLanguageId = $type->initialLanguageId; + $updateStruct->defaultAlwaysAvailable = $type->defaultAlwaysAvailable; + $updateStruct->sortField = $type->sortField; + $updateStruct->sortOrder = $type->sortOrder; + + return $updateStruct; + } + + /** + * Maps $fieldDef to the legacy storage specific StorageFieldDefinition. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageFieldDef + */ + public function toStorageFieldDefinition( + FieldDefinition $fieldDef, + StorageFieldDefinition $storageFieldDef + ) { + foreach (array_keys($fieldDef->name) as $languageCode) { + $multilingualData = new MultilingualStorageFieldDefinition(); + $multilingualData->name = $fieldDef->name[$languageCode]; + $multilingualData->description = $fieldDef->description[$languageCode] ?? null; + $multilingualData->languageId = + $this->maskGenerator->generateLanguageMaskFromLanguageCodes([$languageCode]); + + $storageFieldDef->multilingualData[$languageCode] = $multilingualData; + } + + $converter = $this->converterRegistry->getConverter( + $fieldDef->fieldType + ); + + $converter->toStorageFieldDefinition( + $fieldDef, + $storageFieldDef + ); + } + + /** + * Maps a FieldDefinition from the given $storageFieldDef. + * + * @param \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition $storageFieldDef + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $fieldDef + */ + public function toFieldDefinition( + StorageFieldDefinition $storageFieldDef, + FieldDefinition $fieldDef, + int $status = Type::STATUS_DEFINED + ) { + $converter = $this->converterRegistry->getConverter( + $fieldDef->fieldType + ); + $converter->toFieldDefinition( + $storageFieldDef, + $fieldDef + ); + + $this->storageDispatcher->loadFieldConstraintsData($fieldDef, $status); + } + + /** + * Wrap unserialize to set default value in case of empty serialization. + * + * @param string $serialized Serialized structure to process + * @param mixed $default Default value in case of empty serialization + * + * @return array|mixed + */ + protected function unserialize($serialized, $default = []) + { + return $serialized + ? unserialize($serialized) + : $default; + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\UpdateStruct $updateStruct + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + public function createTypeFromUpdateStruct(UpdateStruct $updateStruct): Type + { + $type = new Type(); + + $type->name = $updateStruct->name; + $type->description = $updateStruct->description; + $type->identifier = $updateStruct->identifier; + $type->modified = $updateStruct->modified; + $type->modifierId = $updateStruct->modifierId; + $type->remoteId = $updateStruct->remoteId; + $type->urlAliasSchema = $updateStruct->urlAliasSchema; + $type->nameSchema = $updateStruct->nameSchema; + $type->isContainer = $updateStruct->isContainer; + $type->initialLanguageId = $updateStruct->initialLanguageId; + $type->defaultAlwaysAvailable = $updateStruct->defaultAlwaysAvailable; + $type->sortField = $updateStruct->sortField; + $type->sortOrder = $updateStruct->sortOrder; + $type->languageCodes = array_keys($updateStruct->name); + + return $type; + } + + public function extractMultilingualDataFromRows(array $mlFieldDefinitionsRows): array + { + $mlFieldDefinitionData = []; + foreach ($mlFieldDefinitionsRows as $row) { + $mlStorageFieldDefinition = new MultilingualStorageFieldDefinition(); + $mlStorageFieldDefinition->name = $row['ezcontentclass_attribute_multilingual_name']; + $mlStorageFieldDefinition->description = $row['ezcontentclass_attribute_multilingual_description']; + $mlStorageFieldDefinition->languageId = $row['ezcontentclass_attribute_multilingual_language_id']; + $mlStorageFieldDefinition->dataText = $row['ezcontentclass_attribute_multilingual_data_text']; + $mlStorageFieldDefinition->dataJson = $row['ezcontentclass_attribute_multilingual_data_json']; + + $mlFieldDefinitionData[] = $mlStorageFieldDefinition; + } + + return $mlFieldDefinitionData; + } +} + +class_alias(Mapper::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Type\Mapper'); diff --git a/src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php b/src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php new file mode 100644 index 0000000000..ec9c7e990d --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Type/MemoryCachingHandler.php @@ -0,0 +1,484 @@ +innerHandler = $handler; + $this->cache = $cache; + $this->generator = $generator; + } + + public function createGroup(GroupCreateStruct $createStruct): Group + { + $group = $this->innerHandler->createGroup($createStruct); + $this->storeGroupCache([$group]); + $this->cache->deleteMulti([ + $this->generator->generateKey(self::CONTENT_TYPE_GROUP_LIST, [], true), + ]); + + return $group; + } + + public function updateGroup(GroupUpdateStruct $struct): Group + { + $group = $this->innerHandler->updateGroup($struct); + $this->storeGroupCache([$group]); + $this->cache->deleteMulti([ + $this->generator->generateKey(self::CONTENT_TYPE_GROUP_LIST, [], true), + ]); + + return $group; + } + + public function deleteGroup($groupId): void + { + $this->innerHandler->deleteGroup($groupId); + // Delete by primary key will remove the object, so we don't need to clear `-by-identifier` variant here. + $this->cache->deleteMulti([ + $this->generator->generateKey(self::CONTENT_TYPE_GROUP, [$groupId], true), + $this->generator->generateKey(self::CONTENT_TYPE_GROUP_LIST, [], true), + ]); + } + + public function loadGroup($groupId): Group + { + $group = $this->cache->get( + $this->generator->generateKey(self::CONTENT_TYPE_GROUP, [$groupId], true) + ); + + if ($group === null) { + $group = $this->innerHandler->loadGroup($groupId); + $this->storeGroupCache([$group]); + } + + return $group; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Group[] + */ + public function loadGroups(array $groupIds): array + { + $groups = $missingIds = []; + foreach ($groupIds as $groupId) { + $group = $this->cache->get( + $this->generator->generateKey(self::CONTENT_TYPE_GROUP, [$groupId], true) + ); + + if ($group !== null) { + $groups[$groupId] = $group; + } else { + $missingIds[] = $groupId; + } + } + + if (!empty($missingIds)) { + $loaded = $this->innerHandler->loadGroups($missingIds); + $this->storeGroupCache($loaded); + /** @noinspection AdditionOperationOnArraysInspection */ + $groups += $loaded; + } + + return $groups; + } + + public function loadGroupByIdentifier($identifier): Group + { + $group = $this->cache->get( + $this->generator->generateKey(self::CONTENT_TYPE_GROUP, [$identifier], true) . + $this->generator->generateKey(self::BY_IDENTIFIER_SUFFIX) + ); + + if ($group === null) { + $group = $this->innerHandler->loadGroupByIdentifier($identifier); + $this->storeGroupCache([$group]); + } + + return $group; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Group[] + */ + public function loadAllGroups(): array + { + $contentTypeGroupListKey = $this->generator->generateKey(self::CONTENT_TYPE_GROUP_LIST, [], true); + $groups = $this->cache->get($contentTypeGroupListKey); + + if ($groups === null) { + $groups = $this->innerHandler->loadAllGroups(); + $this->storeGroupCache($groups, $contentTypeGroupListKey); + } + + return $groups; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Type[] + */ + public function loadContentTypes($groupId, $status = Type::STATUS_DEFINED): array + { + if ($status !== Type::STATUS_DEFINED) { + return $this->innerHandler->loadContentTypes($groupId, $status); + } + + $contentTypeGroupListByGroup = $this->generator->generateKey(self::CONTENT_TYPE_GROUP_LIST_BY_GROUP, [$groupId], true); + $types = $this->cache->get($contentTypeGroupListByGroup); + + if ($types === null) { + $types = $this->innerHandler->loadContentTypes($groupId, $status); + $this->storeTypeCache($types, $contentTypeGroupListByGroup); + } + + return $types; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Type[] + */ + public function loadContentTypeList(array $contentTypeIds): array + { + $contentTypes = $missingIds = []; + foreach ($contentTypeIds as $contentTypeId) { + $cacheKey = $this->generator->generateKey( + self::CONTENT_TYPE, + [$contentTypeId], + true + ) . '-' . Type::STATUS_DEFINED; + + $cacheItem = $this->cache->get($cacheKey); + if ($cacheItem !== null) { + $contentTypes[$contentTypeId] = $cacheItem; + } else { + $missingIds[] = $contentTypeId; + } + } + + if (!empty($missingIds)) { + $loaded = $this->innerHandler->loadContentTypeList($missingIds); + $this->storeTypeCache($loaded); + /** @noinspection AdditionOperationOnArraysInspection */ + $contentTypes += $loaded; + } + + return $contentTypes; + } + + /** + * {@inheritdoc} + */ + public function loadContentTypesByFieldDefinitionIdentifier(string $identifier): array + { + $cacheKey = $this->generator->generateKey( + self::CONTENT_TYPE_LIST_BY_FIELD_DEFINITION_IDENTIFIER, + [$identifier], + true + ); + + $types = $this->cache->get($cacheKey); + if ($types === null) { + $types = $this->innerHandler->loadContentTypesByFieldDefinitionIdentifier($identifier); + $this->storeTypeCache($types, $cacheKey); + } + + return $types; + } + + /** + * {@inheritdoc} + */ + public function load($contentTypeId, $status = Type::STATUS_DEFINED) + { + $contentType = $this->cache->get( + $this->generator->generateKey(self::CONTENT_TYPE, [$contentTypeId], true) . '-' . $status + ); + + if ($contentType === null) { + $contentType = $this->innerHandler->load($contentTypeId, $status); + $this->storeTypeCache([$contentType]); + } + + return $contentType; + } + + public function loadByIdentifier($identifier): Type + { + $contentType = $this->cache->get( + $this->generator->generateKey(self::CONTENT_TYPE, [$identifier], true) . + $this->generator->generateKey(self::BY_IDENTIFIER_SUFFIX) + ); + + if ($contentType === null) { + $contentType = $this->innerHandler->loadByIdentifier($identifier); + $this->storeTypeCache([$contentType]); + } + + return $contentType; + } + + public function loadByRemoteId($remoteId): Type + { + $contentType = $this->cache->get( + $this->generator->generateKey(self::CONTENT_TYPE, [$remoteId], true) . + $this->generator->generateKey(self::BY_REMOTE_SUFFIX) + ); + + if ($contentType === null) { + $contentType = $this->innerHandler->loadByRemoteId($remoteId); + $this->storeTypeCache([$contentType]); + } + + return $contentType; + } + + public function create(CreateStruct $createStruct): Type + { + $contentType = $this->innerHandler->create($createStruct); + // Don't store as FieldTypeConstraints is not setup fully here from Legacy SE side + $this->deleteTypeCache($contentType->id, $contentType->status); + + return $contentType; + } + + public function update($typeId, $status, UpdateStruct $contentType): Type + { + $contentType = $this->innerHandler->update($typeId, $status, $contentType); + $this->storeTypeCache([$contentType]); + + return $contentType; + } + + public function delete($contentTypeId, $status): void + { + $this->innerHandler->delete($contentTypeId, $status); + $this->deleteTypeCache($contentTypeId, $status); + } + + public function createDraft($modifierId, $contentTypeId): Type + { + $contentType = $this->innerHandler->createDraft($modifierId, $contentTypeId); + // Don't store as FieldTypeConstraints is not setup fully here from Legacy SE side + $this->deleteTypeCache($contentType->id, $contentType->status); + + return $contentType; + } + + public function copy($userId, $contentTypeId, $status): Type + { + $contentType = $this->innerHandler->copy($userId, $contentTypeId, $status); + $this->storeTypeCache([$contentType]); + + return $contentType; + } + + public function unlink($groupId, $contentTypeId, $status): bool + { + $keys = [ + $this->generator->generateKey(self::CONTENT_TYPE, [$contentTypeId], true) . '-' . $status, + ]; + + if ($status === Type::STATUS_DEFINED) { + $keys[] = $this->generator->generateKey(self::CONTENT_TYPE_LIST_BY_GROUP, [$groupId], true); + } + + $this->cache->deleteMulti($keys); + + return $this->innerHandler->unlink($groupId, $contentTypeId, $status); + } + + public function link($groupId, $contentTypeId, $status): bool + { + $keys = [ + $this->generator->generateKey(self::CONTENT_TYPE, [$contentTypeId], true) . '-' . $status, + ]; + + if ($status === Type::STATUS_DEFINED) { + $keys[] = $this->generator->generateKey(self::CONTENT_TYPE_LIST_BY_GROUP, [$groupId], true); + } + + $this->cache->deleteMulti($keys); + + return $this->innerHandler->link($groupId, $contentTypeId, $status); + } + + public function getFieldDefinition($id, $status): FieldDefinition + { + return $this->innerHandler->getFieldDefinition($id, $status); + } + + public function getContentCount($contentTypeId): int + { + return $this->innerHandler->getContentCount($contentTypeId); + } + + public function addFieldDefinition($contentTypeId, $status, FieldDefinition $fieldDefinition) + { + $this->deleteTypeCache($contentTypeId, $status); + + return $this->innerHandler->addFieldDefinition($contentTypeId, $status, $fieldDefinition); + } + + public function removeFieldDefinition( + int $contentTypeId, + int $status, + FieldDefinition $fieldDefinition + ): void { + $this->deleteTypeCache($contentTypeId, $status); + $this->innerHandler->removeFieldDefinition($contentTypeId, $status, $fieldDefinition); + } + + public function updateFieldDefinition($contentTypeId, $status, FieldDefinition $fieldDefinition): void + { + $this->deleteTypeCache($contentTypeId, $status); + + $this->innerHandler->updateFieldDefinition($contentTypeId, $status, $fieldDefinition); + } + + public function publish($contentTypeId): void + { + $this->clearCache(); + + $this->innerHandler->publish($contentTypeId); + } + + /** + * @return array + */ + public function getSearchableFieldMap(): array + { + $mapCacheKey = $this->generator->generateKey(self::CONTENT_TYPE_FIELD_MAP, [], true); + $map = $this->cache->get($mapCacheKey); + + if ($map === null) { + $map = $this->innerHandler->getSearchableFieldMap(); + + $this->cache->setMulti( + $map, + static function (): array { + return []; + }, + $mapCacheKey + ); + } + + return $map; + } + + public function removeContentTypeTranslation(int $contentTypeId, string $languageCode): Type + { + $this->clearCache(); + + return $this->innerHandler->removeContentTypeTranslation($contentTypeId, $languageCode); + } + + /** + * Clear internal caches. + */ + public function clearCache(): void + { + $this->cache->clear(); + } + + protected function deleteTypeCache(int $contentTypeId, int $status = Type::STATUS_DEFINED): void + { + if ($status !== Type::STATUS_DEFINED) { + // Delete by primary key will remove the object, so we don't need to clear other variants here. + $this->cache->deleteMulti([ + $this->generator->generateKey(self::CONTENT_TYPE, [$contentTypeId], true) . '-' . $status, + $this->generator->generateKey(self::CONTENT_TYPE_FIELD_MAP, [], true), + ]); + } else { + // We don't know group id in order to clear relevant "ez-content-type-list-by-group-$groupId". + $this->cache->clear(); + } + } + + protected function storeTypeCache(array $types, string $listIndex = null): void + { + $this->cache->setMulti( + $types, + function (Type $type): array { + if ($type->status !== Type::STATUS_DEFINED) { + return [ + $this->generator->generateKey(self::CONTENT_TYPE, [$type->id], true) . '-' . $type->status, + ]; + } + + return [ + $this->generator->generateKey(self::CONTENT_TYPE, [$type->id], true) . '-' . $type->status, + + $this->generator->generateKey(self::CONTENT_TYPE, [$type->identifier], true) . '-' . $type->status . + $this->generator->generateKey(self::BY_IDENTIFIER_SUFFIX), + + $this->generator->generateKey(self::CONTENT_TYPE, [$type->remoteId], true) . '-' . $type->status . + $this->generator->generateKey(self::BY_REMOTE_SUFFIX), + ]; + }, + $listIndex + ); + + $this->cache->deleteMulti([ + $this->generator->generateKey(self::CONTENT_TYPE_FIELD_MAP, [], true), + ]); + } + + protected function storeGroupCache(array $groups, string $listIndex = null): void + { + $this->cache->setMulti( + $groups, + function (Group $group): array { + return [ + $this->generator->generateKey(self::CONTENT_TYPE_GROUP, [$group->id], true), + $this->generator->generateKey(self::CONTENT_TYPE_GROUP, [$group->identifier], true) . + $this->generator->generateKey(self::BY_IDENTIFIER_SUFFIX), + ]; + }, + $listIndex + ); + } + + public function deleteByUserAndStatus(int $userId, int $status): void + { + $this->innerHandler->deleteByUserAndStatus($userId, $status); + } +} + +class_alias(MemoryCachingHandler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler'); diff --git a/src/lib/Persistence/Legacy/Content/Type/StorageDispatcher.php b/src/lib/Persistence/Legacy/Content/Type/StorageDispatcher.php new file mode 100644 index 0000000000..702e7e4879 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Type/StorageDispatcher.php @@ -0,0 +1,54 @@ +registry = $registry; + } + + public function publishFieldConstraintsData(FieldDefinition $fieldDefinition): void + { + if ($this->registry->hasStorage($fieldDefinition->fieldType)) { + $storage = $this->registry->getStorage($fieldDefinition->fieldType); + $storage->publishFieldConstraintsData($fieldDefinition->id); + } + } + + public function storeFieldConstraintsData(FieldDefinition $fieldDefinition, int $status): void + { + if ($this->registry->hasStorage($fieldDefinition->fieldType)) { + $storage = $this->registry->getStorage($fieldDefinition->fieldType); + $storage->storeFieldConstraintsData($fieldDefinition->id, $status, $fieldDefinition->fieldTypeConstraints); + } + } + + public function loadFieldConstraintsData(FieldDefinition $fieldDefinition, int $status): void + { + if ($this->registry->hasStorage($fieldDefinition->fieldType)) { + $storage = $this->registry->getStorage($fieldDefinition->fieldType); + + $fieldDefinition->fieldTypeConstraints = $storage->getFieldConstraintsData($fieldDefinition->id, $status); + } + } + + public function deleteFieldConstraintsData(string $fieldTypeIdentifier, int $fieldDefinitionId, int $status): void + { + if ($this->registry->hasStorage($fieldTypeIdentifier)) { + $storage = $this->registry->getStorage($fieldTypeIdentifier); + $storage->deleteFieldConstraintsData($fieldDefinitionId, $status); + } + } +} diff --git a/src/lib/Persistence/Legacy/Content/Type/StorageDispatcherInterface.php b/src/lib/Persistence/Legacy/Content/Type/StorageDispatcherInterface.php new file mode 100644 index 0000000000..cd2e182ade --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Type/StorageDispatcherInterface.php @@ -0,0 +1,22 @@ + */ + private iterable $storages; + + public function __construct(iterable $storages) + { + $this->storages = $storages; + } + + public function hasStorage(string $fieldTypeName): bool + { + return $this->findStorage($fieldTypeName) !== null; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function getStorage(string $fieldTypeName): FieldConstraintsStorage + { + $storage = $this->findStorage($fieldTypeName); + if ($storage === null) { + throw new InvalidArgumentException( + '$typeName', + sprintf('Undefined %s for "%s" field type', FieldConstraintsStorage::class, $fieldTypeName) + ); + } + + return $storage; + } + + private function findStorage(string $needle): ?FieldConstraintsStorage + { + foreach ($this->storages as $fieldTypeName => $storage) { + if ($fieldTypeName === $needle) { + return $storage; + } + } + + return null; + } +} diff --git a/src/lib/Persistence/Legacy/Content/Type/StorageRegistryInterface.php b/src/lib/Persistence/Legacy/Content/Type/StorageRegistryInterface.php new file mode 100644 index 0000000000..4b4edc2a24 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/Type/StorageRegistryInterface.php @@ -0,0 +1,21 @@ +contentTypeGateway = $contentTypeGateway; + } + + public function updateContentObjects(Type $fromType, Type $toType): void + { + // Do nothing, content objects are no longer updated + } + + public function deleteOldType(Type $fromType): void + { + $this->contentTypeGateway->delete($fromType->id, $fromType->status); + } + + public function publishNewType(Type $toType, int $newStatus): void + { + $this->contentTypeGateway->publishTypeAndFields( + $toType->id, + $toType->status, + $newStatus + ); + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/DTO/SwappedLocationProperties.php b/src/lib/Persistence/Legacy/Content/UrlAlias/DTO/SwappedLocationProperties.php similarity index 80% rename from eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/DTO/SwappedLocationProperties.php rename to src/lib/Persistence/Legacy/Content/UrlAlias/DTO/SwappedLocationProperties.php index 5d716220b2..903815c635 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/DTO/SwappedLocationProperties.php +++ b/src/lib/Persistence/Legacy/Content/UrlAlias/DTO/SwappedLocationProperties.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\DTO; +namespace Ibexa\Core\Persistence\Legacy\Content\UrlAlias\DTO; /** * @internal To be used internally by UrlAlias Persistence Handler. @@ -42,3 +42,5 @@ public function __construct($id, $parentId) */ public $entries; } + +class_alias(SwappedLocationProperties::class, 'eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\DTO\SwappedLocationProperties'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/DTO/UrlAliasForSwappedLocation.php b/src/lib/Persistence/Legacy/Content/UrlAlias/DTO/UrlAliasForSwappedLocation.php similarity index 83% rename from eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/DTO/UrlAliasForSwappedLocation.php rename to src/lib/Persistence/Legacy/Content/UrlAlias/DTO/UrlAliasForSwappedLocation.php index 1895380f79..670f5169ee 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Content/UrlAlias/DTO/UrlAliasForSwappedLocation.php +++ b/src/lib/Persistence/Legacy/Content/UrlAlias/DTO/UrlAliasForSwappedLocation.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\DTO; +namespace Ibexa\Core\Persistence\Legacy\Content\UrlAlias\DTO; /** * @internal To be used internally by UrlAlias Persistence Handler. @@ -45,3 +45,5 @@ public function __construct( /** @var int */ public $newId; } + +class_alias(UrlAliasForSwappedLocation::class, 'eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\DTO\UrlAliasForSwappedLocation'); diff --git a/src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php b/src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php new file mode 100644 index 0000000000..7b1e612898 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/UrlAlias/Gateway.php @@ -0,0 +1,248 @@ + ParameterType::INTEGER, + 'link' => ParameterType::INTEGER, + 'is_alias' => ParameterType::INTEGER, + 'alias_redirects' => ParameterType::INTEGER, + 'is_original' => ParameterType::INTEGER, + 'action' => ParameterType::STRING, + 'action_type' => ParameterType::STRING, + 'lang_mask' => ParameterType::INTEGER, + 'text' => ParameterType::STRING, + 'parent' => ParameterType::INTEGER, + 'text_md5' => ParameterType::STRING, + ]; + + /** @var \Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator */ + private $languageMaskGenerator; + + /** + * Main URL database table name. + * + * @var string + */ + private $table; + + /** @var \Doctrine\DBAL\Connection */ + private $connection; + + /** @var \Doctrine\DBAL\Platforms\AbstractPlatform */ + private $dbPlatform; + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function __construct( + Connection $connection, + LanguageMaskGenerator $languageMaskGenerator + ) { + $this->connection = $connection; + $this->languageMaskGenerator = $languageMaskGenerator; + $this->table = static::TABLE; + $this->dbPlatform = $this->connection->getDatabasePlatform(); + } + + public function setTable(string $name): void + { + $this->table = $name; + } + + /** + * Loads all list of aliases by given $locationId. + */ + public function loadAllLocationEntries(int $locationId): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(array_keys(self::URL_ALIAS_DATA_COLUMN_TYPE_MAP)) + ->from($this->connection->quoteIdentifier($this->table)) + ->where('action = :action') + ->andWhere('is_original = :is_original') + ->setParameter('action', "eznode:{$locationId}", ParameterType::STRING) + ->setParameter('is_original', 1, ParameterType::INTEGER); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadLocationEntries( + int $locationId, + bool $custom = false, + ?int $languageId = null + ): array { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select( + 'id', + 'link', + 'is_alias', + 'alias_redirects', + 'lang_mask', + 'is_original', + 'parent', + 'text', + 'text_md5', + 'action' + ) + ->from($this->connection->quoteIdentifier($this->table)) + ->where( + $expr->eq( + 'action', + $query->createPositionalParameter( + "eznode:{$locationId}", + ParameterType::STRING + ) + ) + ) + ->andWhere( + $expr->eq( + 'is_original', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + 'is_alias', + $query->createPositionalParameter($custom ? 1 : 0, ParameterType::INTEGER) + ) + ) + ; + + if (null !== $languageId) { + $query->andWhere( + $expr->gt( + $this->dbPlatform->getBitAndComparisonExpression( + 'lang_mask', + $query->createPositionalParameter($languageId, ParameterType::INTEGER) + ), + 0 + ) + ); + } + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function listGlobalEntries( + ?string $languageCode = null, + int $offset = 0, + int $limit = -1 + ): array { + $limit = $limit === -1 ? self::MAX_LIMIT : $limit; + + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select( + 'action', + 'id', + 'link', + 'is_alias', + 'alias_redirects', + 'lang_mask', + 'is_original', + 'parent', + 'text_md5' + ) + ->from($this->connection->quoteIdentifier($this->table)) + ->where( + $expr->eq( + 'action_type', + $query->createPositionalParameter( + 'module', + ParameterType::STRING + ) + ) + ) + ->andWhere( + $expr->eq( + 'is_original', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + 'is_alias', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ) + ) + ->setMaxResults( + $limit + ) + ->setFirstResult($offset); + + if (isset($languageCode)) { + $query->andWhere( + $expr->gt( + $this->dbPlatform->getBitAndComparisonExpression( + 'lang_mask', + $query->createPositionalParameter( + $this->languageMaskGenerator->generateLanguageIndicator( + $languageCode, + false + ), + ParameterType::INTEGER + ) + ), + 0 + ) + ); + } + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function isRootEntry(int $id): bool + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + 'text', + 'parent' + ) + ->from($this->connection->quoteIdentifier($this->table)) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + $statement = $query->execute(); + + $row = $statement->fetch(FetchMode::ASSOCIATIVE); + + return strlen($row['text']) == 0 && $row['parent'] == 0; + } + + public function cleanupAfterPublish( + string $action, + int $languageId, + int $newId, + int $parentId, + string $textMD5 + ): void { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select( + 'parent', + 'text_md5', + 'lang_mask' + ) + ->from($this->connection->quoteIdentifier($this->table)) + // 1) Autogenerated aliases that match action and language... + ->where( + $expr->eq( + 'action', + $query->createPositionalParameter($action, ParameterType::STRING) + ) + ) + ->andWhere( + $expr->eq( + 'is_original', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + 'is_alias', + $query->createPositionalParameter(0, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->gt( + $this->dbPlatform->getBitAndComparisonExpression( + 'lang_mask', + $query->createPositionalParameter($languageId, ParameterType::INTEGER) + ), + 0 + ) + ) + // 2) ...but not newly published entry + ->andWhere( + sprintf( + 'NOT (%s)', + $expr->andX( + $expr->eq( + 'parent', + $query->createPositionalParameter($parentId, ParameterType::INTEGER) + ), + $expr->eq( + 'text_md5', + $query->createPositionalParameter($textMD5, ParameterType::STRING) + ) + ) + ) + ); + + $statement = $query->execute(); + + $row = $statement->fetch(FetchMode::ASSOCIATIVE); + + if (!empty($row)) { + $this->archiveUrlAliasForDeletedTranslation( + (int)$row['lang_mask'], + (int)$languageId, + (int)$row['parent'], + $row['text_md5'], + (int)$newId + ); + } + } + + /** + * Archive (remove or historize) obsolete URL aliases (for translations that were removed). + * + * @param int $languageMask all languages bit mask + * @param int $languageId removed language Id + * @param string $textMD5 checksum + */ + private function archiveUrlAliasForDeletedTranslation( + int $languageMask, + int $languageId, + int $parent, + string $textMD5, + int $linkId + ): void { + // If language mask is composite (consists of multiple languages) then remove given language from entry + if ($languageMask & ~($languageId | 1)) { + $this->removeTranslation($parent, $textMD5, $languageId); + } else { + // Otherwise mark entry as history + $this->historize($parent, $textMD5, $linkId); + } + } + + public function historizeBeforeSwap(string $action, int $languageMask): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update($this->connection->quoteIdentifier($this->table)) + ->set( + 'is_original', + $query->createPositionalParameter(0, ParameterType::INTEGER) + ) + ->set( + 'id', + $query->createPositionalParameter( + $this->getNextId(), + ParameterType::INTEGER + ) + ) + ->where( + $query->expr()->andX( + $query->expr()->eq( + 'action', + $query->createPositionalParameter($action, ParameterType::STRING) + ), + $query->expr()->eq( + 'is_original', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ), + $query->expr()->gt( + $this->dbPlatform->getBitAndComparisonExpression( + 'lang_mask', + $query->createPositionalParameter( + $languageMask & ~1, + ParameterType::INTEGER + ) + ), + 0 + ) + ) + ); + + $query->execute(); + } + + /** + * Update single row matched by composite primary key. + * + * Sets "is_original" to 0 thus marking entry as history. + * + * Re-links history entries. + * + * When location alias is published we need to check for new history entries created with self::downgrade() + * with the same action and language, update their "link" column with id of the published entry. + * History entry "id" column is moved to next id value so that all active (non-history) entries are kept + * under the same id. + */ + private function historize(int $parentId, string $textMD5, int $newId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update($this->connection->quoteIdentifier($this->table)) + ->set( + 'is_original', + $query->createPositionalParameter(0, ParameterType::INTEGER) + ) + ->set( + 'link', + $query->createPositionalParameter($newId, ParameterType::INTEGER) + ) + ->set( + 'id', + $query->createPositionalParameter( + $this->getNextId(), + ParameterType::INTEGER + ) + ) + ->where( + $query->expr()->andX( + $query->expr()->eq( + 'parent', + $query->createPositionalParameter($parentId, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'text_md5', + $query->createPositionalParameter($textMD5, ParameterType::STRING) + ) + ) + ); + $query->execute(); + } + + /** + * Update single row data matched by composite primary key. + * + * Removes given $languageId from entry's language mask + */ + private function removeTranslation(int $parentId, string $textMD5, int $languageId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update($this->connection->quoteIdentifier($this->table)) + ->set( + 'lang_mask', + $this->dbPlatform->getBitAndComparisonExpression( + 'lang_mask', + $query->createPositionalParameter( + ~$languageId, + ParameterType::INTEGER + ) + ) + ) + ->where( + $query->expr()->eq( + 'parent', + $query->createPositionalParameter( + $parentId, + ParameterType::INTEGER + ) + ) + ) + ->andWhere( + $query->expr()->eq( + 'text_md5', + $query->createPositionalParameter( + $textMD5, + ParameterType::STRING + ) + ) + ) + ; + $query->execute(); + } + + public function historizeId(int $id, int $link): void + { + if ($id === $link) { + return; + } + + $query = $this->connection->createQueryBuilder(); + $query->select( + 'parent', + 'text_md5' + )->from( + $this->connection->quoteIdentifier($this->table) + )->where( + $query->expr()->andX( + $query->expr()->eq( + 'is_alias', + $query->createPositionalParameter(0, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'is_original', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'action_type', + $query->createPositionalParameter( + 'eznode', + ParameterType::STRING + ) + ), + $query->expr()->eq( + 'link', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ) + ); + + $statement = $query->execute(); + + $rows = $statement->fetchAll(FetchMode::ASSOCIATIVE); + + foreach ($rows as $row) { + $this->historize((int)$row['parent'], $row['text_md5'], $link); + } + } + + public function reparent(int $oldParentId, int $newParentId): void + { + $query = $this->connection->createQueryBuilder(); + $query->update( + $this->connection->quoteIdentifier($this->table) + )->set( + 'parent', + $query->createPositionalParameter($newParentId, ParameterType::INTEGER) + )->where( + $query->expr()->eq( + 'parent', + $query->createPositionalParameter( + $oldParentId, + ParameterType::INTEGER + ) + ) + ); + + $query->execute(); + } + + public function updateRow(int $parentId, string $textMD5, array $values): void + { + $query = $this->connection->createQueryBuilder(); + $query->update($this->connection->quoteIdentifier($this->table)); + foreach ($values as $columnName => $value) { + $query->set( + $columnName, + $query->createNamedParameter( + $value, + self::URL_ALIAS_DATA_COLUMN_TYPE_MAP[$columnName], + ":{$columnName}" + ) + ); + } + $query + ->where( + $query->expr()->eq( + 'parent', + $query->createNamedParameter($parentId, ParameterType::INTEGER, ':parent') + ) + ) + ->andWhere( + $query->expr()->eq( + 'text_md5', + $query->createNamedParameter($textMD5, ParameterType::STRING, ':text_md5') + ) + ); + $query->execute(); + } + + public function insertRow(array $values): int + { + if (!isset($values['id'])) { + $values['id'] = $this->getNextId(); + } + if (!isset($values['link'])) { + $values['link'] = $values['id']; + } + if (!isset($values['is_original'])) { + $values['is_original'] = ($values['id'] == $values['link'] ? 1 : 0); + } + if (!isset($values['is_alias'])) { + $values['is_alias'] = 0; + } + if (!isset($values['alias_redirects'])) { + $values['alias_redirects'] = 0; + } + if ( + !isset($values['action_type']) + && preg_match('#^(.+):.*#', $values['action'], $matches) + ) { + $values['action_type'] = $matches[1]; + } + if ($values['is_alias']) { + $values['is_original'] = 1; + } + if ($values['action'] === self::NOP_ACTION) { + $values['is_original'] = 0; + } + + $query = $this->connection->createQueryBuilder(); + $query->insert($this->connection->quoteIdentifier($this->table)); + foreach ($values as $columnName => $value) { + $query->setValue( + $columnName, + $query->createNamedParameter( + $value, + self::URL_ALIAS_DATA_COLUMN_TYPE_MAP[$columnName], + ":{$columnName}" + ) + ); + } + $query->execute(); + + return (int)$values['id']; + } + + public function getNextId(): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::INCR_TABLE) + ->values( + [ + 'id' => $this->dbPlatform->supportsSequences() + ? sprintf('NEXTVAL(\'%s\')', self::INCR_TABLE_SEQ) + : $query->createPositionalParameter(null, ParameterType::NULL), + ] + ); + + $query->execute(); + + return (int)$this->connection->lastInsertId(self::INCR_TABLE_SEQ); + } + + public function loadRow(int $parentId, string $textMD5): array + { + $query = $this->connection->createQueryBuilder(); + $query->select('*')->from( + $this->connection->quoteIdentifier($this->table) + )->where( + $query->expr()->andX( + $query->expr()->eq( + 'parent', + $query->createPositionalParameter( + $parentId, + ParameterType::INTEGER + ) + ), + $query->expr()->eq( + 'text_md5', + $query->createPositionalParameter( + $textMD5, + ParameterType::STRING + ) + ) + ) + ); + + $result = $query->execute()->fetch(FetchMode::ASSOCIATIVE); + + return false !== $result ? $result : []; + } + + public function loadUrlAliasData(array $urlHashes): array + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + + $count = count($urlHashes); + foreach ($urlHashes as $level => $urlPartHash) { + $tableAlias = $level !== $count - 1 ? $this->table . $level : 'u'; + $query + ->addSelect( + array_map( + static function (string $columnName) use ($tableAlias) { + // do not alias data for top level url part + $columnAlias = 'u' === $tableAlias + ? $columnName + : "{$tableAlias}_{$columnName}"; + $columnName = "{$tableAlias}.{$columnName}"; + + return "{$columnName} AS {$columnAlias}"; + }, + array_keys(self::URL_ALIAS_DATA_COLUMN_TYPE_MAP) + ) + ) + ->from($this->connection->quoteIdentifier($this->table), $tableAlias); + + $query + ->andWhere( + $expr->eq( + "{$tableAlias}.text_md5", + $query->createPositionalParameter($urlPartHash, ParameterType::STRING) + ) + ) + ->andWhere( + $expr->eq( + "{$tableAlias}.parent", + // root entry has parent column set to 0 + isset($previousTableName) ? $previousTableName . '.link' : $query->createPositionalParameter( + 0, + ParameterType::INTEGER + ) + ) + ); + + $previousTableName = $tableAlias; + } + $query->setMaxResults(1); + + $result = $query->execute()->fetch(FetchMode::ASSOCIATIVE); + + return false !== $result ? $result : []; + } + + public function loadAutogeneratedEntry(string $action, ?int $parentId = null): array + { + $query = $this->connection->createQueryBuilder(); + $query->select( + '*' + )->from( + $this->connection->quoteIdentifier($this->table) + )->where( + $query->expr()->andX( + $query->expr()->eq( + 'action', + $query->createPositionalParameter($action, ParameterType::STRING) + ), + $query->expr()->eq( + 'is_original', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'is_alias', + $query->createPositionalParameter(0, ParameterType::INTEGER) + ) + ) + ); + + if (isset($parentId)) { + $query->andWhere( + $query->expr()->eq( + 'parent', + $query->createPositionalParameter( + $parentId, + ParameterType::INTEGER + ) + ) + ); + } + + $entry = $query->execute()->fetch(FetchMode::ASSOCIATIVE); + + return false !== $entry ? $entry : []; + } + + public function loadPathData(int $id): array + { + $pathData = []; + + while ($id != 0) { + $query = $this->connection->createQueryBuilder(); + $query->select( + 'parent', + 'lang_mask', + 'text' + )->from( + $this->connection->quoteIdentifier($this->table) + )->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + + $statement = $query->execute(); + + $rows = $statement->fetchAll(FetchMode::ASSOCIATIVE); + if (empty($rows)) { + // Normally this should never happen + $pathDataArray = []; + foreach ($pathData as $path) { + if (!isset($path[0]['text'])) { + continue; + } + + $pathDataArray[] = $path[0]['text']; + } + + $path = implode('/', $pathDataArray); + throw new BadStateException( + 'id', + "Unable to load path data, path '{$path}' is broken, alias with ID '{$id}' not found. " . + 'To fix all broken paths run the ezplatform:urls:regenerate-aliases command' + ); + } + + $id = $rows[0]['parent']; + array_unshift($pathData, $rows); + } + + return $pathData; + } + + public function loadPathDataByHierarchy(array $hierarchyData): array + { + $query = $this->connection->createQueryBuilder(); + + $hierarchyConditions = []; + foreach ($hierarchyData as $levelData) { + $hierarchyConditions[] = $query->expr()->andX( + $query->expr()->eq( + 'parent', + $query->createPositionalParameter( + $levelData['parent'], + ParameterType::INTEGER + ) + ), + $query->expr()->eq( + 'action', + $query->createPositionalParameter( + $levelData['action'], + ParameterType::STRING + ) + ), + $query->expr()->eq( + 'id', + $query->createPositionalParameter( + $levelData['id'], + ParameterType::INTEGER + ) + ) + ); + } + + $query->select( + 'action', + 'lang_mask', + 'text' + )->from( + $this->connection->quoteIdentifier($this->table) + )->where( + $query->expr()->orX(...$hierarchyConditions) + ); + + $statement = $query->execute(); + + $rows = $statement->fetchAll(FetchMode::ASSOCIATIVE); + $rowsMap = []; + foreach ($rows as $row) { + $rowsMap[$row['action']][] = $row; + } + + if (count($rowsMap) !== count($hierarchyData)) { + throw new RuntimeException('The path is corrupted.'); + } + + $data = []; + foreach ($hierarchyData as $levelData) { + $data[] = $rowsMap[$levelData['action']]; + } + + return $data; + } + + public function removeCustomAlias(int $parentId, string $textMD5): bool + { + $query = $this->connection->createQueryBuilder(); + $query->delete( + $this->connection->quoteIdentifier($this->table) + )->where( + $query->expr()->andX( + $query->expr()->eq( + 'parent', + $query->createPositionalParameter( + $parentId, + ParameterType::INTEGER + ) + ), + $query->expr()->eq( + 'text_md5', + $query->createPositionalParameter( + $textMD5, + ParameterType::STRING + ) + ), + $query->expr()->eq( + 'is_alias', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ) + ) + ); + + return $query->execute() === 1; + } + + public function remove(string $action, ?int $id = null): void + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->delete($this->connection->quoteIdentifier($this->table)) + ->where( + $expr->eq( + 'action', + $query->createPositionalParameter($action, ParameterType::STRING) + ) + ); + + if ($id !== null) { + $query + ->andWhere( + $expr->eq( + 'is_alias', + $query->createPositionalParameter(0, ParameterType::INTEGER) + ), + ) + ->andWhere( + $expr->eq( + 'id', + $query->createPositionalParameter( + $id, + ParameterType::INTEGER + ) + ) + ); + } + + $query->execute(); + } + + public function loadAutogeneratedEntries(int $parentId, bool $includeHistory = false): array + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select('*') + ->from($this->connection->quoteIdentifier($this->table)) + ->where( + $expr->eq( + 'parent', + $query->createPositionalParameter( + $parentId, + ParameterType::INTEGER + ) + ), + ) + ->andWhere( + $expr->eq( + 'action_type', + $query->createPositionalParameter( + 'eznode', + ParameterType::STRING + ) + ) + ) + ->andWhere( + $expr->eq( + 'is_alias', + $query->createPositionalParameter(0, ParameterType::INTEGER) + ) + ); + + if (!$includeHistory) { + $query->andWhere( + $expr->eq( + 'is_original', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ) + ); + } + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function getLocationContentMainLanguageId(int $locationId): int + { + $queryBuilder = $this->connection->createQueryBuilder(); + $expr = $queryBuilder->expr(); + $queryBuilder + ->select('c.initial_language_id') + ->from('ezcontentobject', 'c') + ->join('c', 'ezcontentobject_tree', 't', $expr->eq('t.contentobject_id', 'c.id')) + ->where( + $expr->eq('t.node_id', ':locationId') + ) + ->setParameter('locationId', $locationId, ParameterType::INTEGER); + + $statement = $queryBuilder->execute(); + $languageId = $statement->fetchColumn(); + + if ($languageId === false) { + throw new RuntimeException("Could not find Content for Location #{$locationId}"); + } + + return (int)$languageId; + } + + public function bulkRemoveTranslation(int $languageId, array $actions): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update($this->connection->quoteIdentifier($this->table)) + // parameter for bitwise operation has to be placed verbatim (w/o binding) for this to work cross-DBMS + ->set('lang_mask', 'lang_mask & ~ ' . $languageId) + ->where('action IN (:actions)') + ->setParameter(':actions', $actions, Connection::PARAM_STR_ARRAY); + $query->execute(); + + // cleanup: delete single language rows (including alwaysAvailable) + $query = $this->connection->createQueryBuilder(); + $query + ->delete($this->connection->quoteIdentifier($this->table)) + ->where('action IN (:actions)') + ->andWhere('lang_mask IN (0, 1)') + ->setParameter(':actions', $actions, Connection::PARAM_STR_ARRAY); + $query->execute(); + } + + public function archiveUrlAliasesForDeletedTranslations( + int $locationId, + int $parentId, + array $languageIds + ): void { + // determine proper parent for linking historized entry + $existingLocationEntry = $this->loadAutogeneratedEntry( + 'eznode:' . $locationId, + $parentId + ); + + // filter existing URL alias entries by any of the specified removed languages + $rows = $this->loadLocationEntriesMatchingMultipleLanguages( + $locationId, + $languageIds + ); + + // remove specific languages from a bit mask + foreach ($rows as $row) { + // filter mask to reduce the number of calls to storage engine + $rowLanguageMask = (int)$row['lang_mask']; + $languageIdsToBeRemoved = array_filter( + $languageIds, + static function ($languageId) use ($rowLanguageMask) { + return $languageId & $rowLanguageMask; + } + ); + + if (empty($languageIdsToBeRemoved)) { + continue; + } + + // use existing entry to link archived alias or use current alias id + $linkToId = !empty($existingLocationEntry) + ? (int)$existingLocationEntry['id'] + : (int)$row['id']; + foreach ($languageIdsToBeRemoved as $languageId) { + $this->archiveUrlAliasForDeletedTranslation( + (int)$row['lang_mask'], + (int)$languageId, + (int)$row['parent'], + $row['text_md5'], + $linkToId + ); + } + } + } + + /** + * Load list of aliases for given $locationId matching any of the specified Languages. + * + * @param int[] $languageIds + */ + private function loadLocationEntriesMatchingMultipleLanguages( + int $locationId, + array $languageIds + ): array { + // note: alwaysAvailable for this use case is not relevant + $languageMask = $this->languageMaskGenerator->generateLanguageMaskFromLanguageIds( + $languageIds, + false + ); + + /** @var \Doctrine\DBAL\Connection $connection */ + $query = $this->connection->createQueryBuilder(); + $query + ->select('id', 'lang_mask', 'parent', 'text_md5') + ->from($this->connection->quoteIdentifier($this->table)) + ->where('action = :action') + // fetch rows matching any of the given Languages + ->andWhere('lang_mask & :languageMask <> 0') + ->setParameter(':action', 'eznode:' . $locationId) + ->setParameter(':languageMask', $languageMask); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function deleteUrlAliasesWithoutLocation(): int + { + $dbPlatform = $this->connection->getDatabasePlatform(); + + $subQuery = $this->connection->createQueryBuilder(); + $subQuery + ->select('node_id') + ->from('ezcontentobject_tree', 't') + ->where( + $subQuery->expr()->eq( + 't.node_id', + sprintf( + 'CAST(%s as %s)', + $dbPlatform->getSubstringExpression( + $this->connection->quoteIdentifier($this->table) . '.action', + 8 + ), + $this->getIntegerType() + ) + ) + ); + + $deleteQuery = $this->connection->createQueryBuilder(); + $deleteQuery + ->delete($this->connection->quoteIdentifier($this->table)) + ->where( + $deleteQuery->expr()->eq( + 'action_type', + $deleteQuery->createPositionalParameter('eznode') + ) + ) + ->andWhere( + sprintf('NOT EXISTS (%s)', $subQuery->getSQL()) + ); + + return $deleteQuery->execute(); + } + + public function deleteUrlAliasesWithoutParent(): int + { + $existingAliasesQuery = $this->getAllUrlAliasesQuery(); + + $query = $this->connection->createQueryBuilder(); + $query + ->delete($this->connection->quoteIdentifier($this->table)) + ->where( + $query->expr()->neq( + 'parent', + $query->createPositionalParameter(0, ParameterType::INTEGER) + ) + ) + ->andWhere( + $query->expr()->notIn( + 'parent', + $existingAliasesQuery + ) + ); + + return $query->execute(); + } + + public function deleteUrlAliasesWithBrokenLink(): int + { + $existingAliasesQuery = $this->getAllUrlAliasesQuery(); + + $query = $this->connection->createQueryBuilder(); + $query + ->delete($this->connection->quoteIdentifier($this->table)) + ->where( + $query->expr()->neq('id', 'link') + ) + ->andWhere( + $query->expr()->notIn( + 'link', + $existingAliasesQuery + ) + ); + + return (int)$query->execute(); + } + + public function repairBrokenUrlAliasesForLocation(int $locationId): void + { + $urlAliasesData = $this->getUrlAliasesForLocation($locationId); + + $originalUrlAliases = $this->filterOriginalAliases($urlAliasesData); + + if (count($originalUrlAliases) === count($urlAliasesData)) { + // no archived aliases - nothing to fix + return; + } + + $updateQueryBuilder = $this->connection->createQueryBuilder(); + $expr = $updateQueryBuilder->expr(); + $updateQueryBuilder + ->update($this->connection->quoteIdentifier($this->table)) + ->set('link', ':linkId') + ->set('parent', ':newParentId') + ->where( + $expr->eq('action', ':action') + ) + ->andWhere( + $expr->eq( + 'is_original', + $updateQueryBuilder->createNamedParameter(0, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq('parent', ':oldParentId') + ) + ->andWhere( + $expr->eq('text_md5', ':textMD5') + ) + ->setParameter(':action', "eznode:{$locationId}"); + + foreach ($urlAliasesData as $urlAliasData) { + if ($urlAliasData['is_original'] === 1 || !isset($originalUrlAliases[$urlAliasData['lang_mask']])) { + // ignore non-archived entries and deleted Translations + continue; + } + + $originalUrlAlias = $originalUrlAliases[$urlAliasData['lang_mask']]; + + if ($urlAliasData['link'] === $originalUrlAlias['link']) { + // ignore correct entries to avoid unnecessary updates + continue; + } + + $updateQueryBuilder + ->setParameter(':linkId', $originalUrlAlias['link'], ParameterType::INTEGER) + // attempt to fix missing parent case + ->setParameter( + ':newParentId', + $urlAliasData['existing_parent'] ?? $originalUrlAlias['parent'], + ParameterType::INTEGER + ) + ->setParameter(':oldParentId', $urlAliasData['parent'], ParameterType::INTEGER) + ->setParameter(':textMD5', $urlAliasData['text_md5']); + + try { + $updateQueryBuilder->execute(); + } catch (UniqueConstraintViolationException $e) { + // edge case: if such row already exists, there's no way to restore history + $this->deleteRow((int) $urlAliasData['parent'], $urlAliasData['text_md5']); + } + } + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function deleteUrlNopAliasesWithoutChildren(): int + { + $platform = $this->connection->getDatabasePlatform(); + $queryBuilder = $this->connection->createQueryBuilder(); + + // The wrapper select is needed for SQL "Derived Table Merge" issue for deleting + $wrapperQueryBuilder = clone $queryBuilder; + $selectQueryBuilder = clone $queryBuilder; + $expressionBuilder = $queryBuilder->expr(); + + $selectQueryBuilder + ->select('u_parent.id AS inner_id') + ->from($this->table, 'u_parent') + ->leftJoin( + 'u_parent', + $this->table, + 'u', + $expressionBuilder->eq('u_parent.id', 'u.parent') + ) + ->where( + $expressionBuilder->eq( + 'u_parent.action_type', + ':actionType' + ) + ) + ->groupBy('u_parent.id') + ->having( + $expressionBuilder->eq($platform->getCountExpression('u.id'), 0) + ); + + $wrapperQueryBuilder + ->select('inner_id') + ->from( + sprintf('(%s)', $selectQueryBuilder), + 'wrapper' + ) + ->where('id = inner_id'); + + $queryBuilder + ->delete($this->table) + ->where( + sprintf('EXISTS (%s)', $wrapperQueryBuilder) + ) + ->setParameter('actionType', self::NOP); + + return $queryBuilder->execute(); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function getAllChildrenAliases(int $parentId): array + { + $queryBuilder = $this->connection->createQueryBuilder(); + $expressionBuilder = $queryBuilder->expr(); + + $queryBuilder + ->select('parent', 'text_md5') + ->from($this->table) + ->where( + $expressionBuilder->eq( + 'parent', + $queryBuilder->createPositionalParameter($parentId, ParameterType::INTEGER) + ) + )->andWhere( + $expressionBuilder->eq( + 'is_alias', + $queryBuilder->createPositionalParameter(1, ParameterType::INTEGER) + ) + ); + + return $queryBuilder->execute()->fetchAll(); + } + + /** + * Filter from the given result set original (current) only URL aliases and index them by language_mask. + * + * Note: each language_mask can have one URL Alias. + * + * @param array $urlAliasesData + */ + private function filterOriginalAliases(array $urlAliasesData): array + { + $originalUrlAliases = array_filter( + $urlAliasesData, + static function ($urlAliasData) { + // filter is_original=true ignoring broken parent records (cleaned up elsewhere) + return (bool)$urlAliasData['is_original'] && $urlAliasData['existing_parent'] !== null; + } + ); + + // return language_mask-indexed array + return array_combine( + array_column($originalUrlAliases, 'lang_mask'), + $originalUrlAliases + ); + } + + /** + * Get sub-query for IDs of all URL aliases. + */ + private function getAllUrlAliasesQuery(): string + { + $existingAliasesQueryBuilder = $this->connection->createQueryBuilder(); + $innerQueryBuilder = $this->connection->createQueryBuilder(); + + return $existingAliasesQueryBuilder + ->select('tmp.id') + ->from( + // nest sub-query to avoid same-table update error + '(' . $innerQueryBuilder->select('id')->from( + $this->connection->quoteIdentifier($this->table) + )->getSQL() . ')', + 'tmp' + ) + ->getSQL(); + } + + /** + * Get DBMS-specific integer type. + */ + private function getIntegerType(): string + { + return $this->dbPlatform->getName() === 'mysql' ? 'signed' : 'integer'; + } + + /** + * Get all URL aliases for the given Location (including archived ones). + */ + private function getUrlAliasesForLocation(int $locationId): array + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder + ->select( + 't1.id', + 't1.is_original', + 't1.lang_mask', + 't1.link', + 't1.parent', + // show existing parent only if its row exists, special case for root parent + 'CASE t1.parent WHEN 0 THEN 0 ELSE t2.id END AS existing_parent', + 't1.text_md5' + ) + ->from($this->connection->quoteIdentifier($this->table), 't1') + // selecting t2.id above will result in null if parent is broken + ->leftJoin( + 't1', + $this->connection->quoteIdentifier($this->table), + 't2', + $queryBuilder->expr()->eq('t1.parent', 't2.id') + ) + ->where( + $queryBuilder->expr()->eq( + 't1.action', + $queryBuilder->createPositionalParameter("eznode:{$locationId}") + ) + ); + + return $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + /** + * Delete URL alias row by its primary composite key. + */ + private function deleteRow(int $parentId, string $textMD5): int + { + $queryBuilder = $this->connection->createQueryBuilder(); + $expr = $queryBuilder->expr(); + $queryBuilder + ->delete($this->connection->quoteIdentifier($this->table)) + ->where( + $expr->eq( + 'parent', + $queryBuilder->createPositionalParameter($parentId, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + 'text_md5', + $queryBuilder->createPositionalParameter($textMD5) + ) + ) + ; + + return $queryBuilder->execute(); + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..025be553d7 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/UrlAlias/Gateway/ExceptionConversion.php @@ -0,0 +1,314 @@ +innerGateway = $innerGateway; + } + + public function setTable(string $name): void + { + try { + $this->innerGateway->setTable($name); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadAllLocationEntries(int $locationId): array + { + try { + return $this->innerGateway->loadAllLocationEntries($locationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadLocationEntries( + int $locationId, + bool $custom = false, + ?int $languageId = null + ): array { + try { + return $this->innerGateway->loadLocationEntries($locationId, $custom); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function isRootEntry(int $id): bool + { + try { + return $this->innerGateway->isRootEntry($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function cleanupAfterPublish( + string $action, + int $languageId, + int $newId, + int $parentId, + string $textMD5 + ): void { + try { + $this->innerGateway->cleanupAfterPublish($action, $languageId, $newId, $parentId, $textMD5); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function historizeBeforeSwap(string $action, int $languageMask): void + { + try { + $this->innerGateway->historizeBeforeSwap($action, $languageMask); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function historizeId(int $id, int $link): void + { + try { + $this->innerGateway->historizeId($id, $link); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function reparent(int $oldParentId, int $newParentId): void + { + try { + $this->innerGateway->reparent($oldParentId, $newParentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateRow(int $parentId, string $textMD5, array $values): void + { + try { + $this->innerGateway->updateRow($parentId, $textMD5, $values); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function insertRow(array $values): int + { + try { + return $this->innerGateway->insertRow($values); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadRow(int $parentId, string $textMD5): array + { + try { + return $this->innerGateway->loadRow($parentId, $textMD5); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadAutogeneratedEntry(string $action, ?int $parentId = null): array + { + try { + return $this->innerGateway->loadAutogeneratedEntry($action, $parentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function remove(string $action, ?int $id = null): void + { + try { + $this->innerGateway->remove($action, $id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function listGlobalEntries( + ?string $languageCode = null, + int $offset = 0, + int $limit = -1 + ): array { + try { + return $this->innerGateway->listGlobalEntries($languageCode, $offset, $limit); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function removeCustomAlias(int $parentId, string $textMD5): bool + { + try { + return $this->innerGateway->removeCustomAlias($parentId, $textMD5); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadUrlAliasData(array $urlHashes): array + { + try { + return $this->innerGateway->loadUrlAliasData($urlHashes); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadPathData(int $id): array + { + try { + return $this->innerGateway->loadPathData($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadPathDataByHierarchy(array $hierarchyData): array + { + try { + return $this->innerGateway->loadPathDataByHierarchy($hierarchyData); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadAutogeneratedEntries(int $parentId, bool $includeHistory = false): array + { + try { + return $this->innerGateway->loadAutogeneratedEntries($parentId, $includeHistory); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getNextId(): int + { + try { + return $this->innerGateway->getNextId(); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getLocationContentMainLanguageId(int $locationId): int + { + try { + return $this->innerGateway->getLocationContentMainLanguageId($locationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function bulkRemoveTranslation(int $languageId, array $actions): void + { + try { + $this->innerGateway->bulkRemoveTranslation($languageId, $actions); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function archiveUrlAliasesForDeletedTranslations( + int $locationId, + int $parentId, + array $languageIds + ): void { + try { + $this->innerGateway->archiveUrlAliasesForDeletedTranslations($locationId, $parentId, $languageIds); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteUrlAliasesWithoutLocation(): int + { + try { + return $this->innerGateway->deleteUrlAliasesWithoutLocation(); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteUrlAliasesWithoutParent(): int + { + try { + return $this->innerGateway->deleteUrlAliasesWithoutParent(); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteUrlAliasesWithBrokenLink(): int + { + try { + return $this->innerGateway->deleteUrlAliasesWithBrokenLink(); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function repairBrokenUrlAliasesForLocation(int $locationId): void + { + try { + $this->innerGateway->repairBrokenUrlAliasesForLocation($locationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteUrlNopAliasesWithoutChildren(): int + { + try { + return $this->innerGateway->deleteUrlNopAliasesWithoutChildren(); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function getAllChildrenAliases(int $parentId): array + { + try { + return $this->innerGateway->getAllChildrenAliases($parentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php b/src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php new file mode 100644 index 0000000000..49d08729da --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/UrlAlias/Handler.php @@ -0,0 +1,1205 @@ +gateway = $gateway; + $this->mapper = $mapper; + $this->locationGateway = $locationGateway; + $this->languageHandler = $languageHandler; + $this->slugConverter = $slugConverter; + $this->contentGateway = $contentGateway; + $this->maskGenerator = $maskGenerator; + $this->transactionHandler = $transactionHandler; + } + + public function publishUrlAliasForLocation( + $locationId, + $parentLocationId, + $name, + $languageCode, + $alwaysAvailable = false, + $updatePathIdentificationString = false + ): string { + $languageId = $this->languageHandler->loadByLanguageCode($languageCode)->id; + + return $this->internalPublishUrlAliasForLocation( + $locationId, + $parentLocationId, + $name, + $languageId, + $alwaysAvailable, + $updatePathIdentificationString + ); + } + + /** + * Internal publish method, accepting language ID instead of language code and optionally + * new alias ID (used when swapping Locations). + * + * @see \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationSwapped + * + * @param int $locationId + * @param int $parentLocationId + * @param string $name + * @param int $languageId + * @param bool $alwaysAvailable + * @param bool $updatePathIdentificationString legacy storage specific for updating ezcontentobject_tree.path_identification_string + * @param int $newId + */ + private function internalPublishUrlAliasForLocation( + $locationId, + $parentLocationId, + $name, + $languageId, + $alwaysAvailable = false, + $updatePathIdentificationString = false, + $newId = null + ): string { + $parentId = $this->getRealAliasId($parentLocationId); + $name = $this->slugConverter->convert($name, 'location_' . $locationId); + $uniqueCounter = $this->slugConverter->getUniqueCounterValue($name, $parentId == 0); + $languageMask = $languageId | (int)$alwaysAvailable; + $action = 'eznode:' . $locationId; + $cleanup = false; + + // Exiting the loop with break; + while (true) { + $newText = ''; + if ($locationId != self::CONTENT_REPOSITORY_ROOT_LOCATION_ID) { + $newText = $name . ($name == '' || $uniqueCounter > 1 ? $uniqueCounter : ''); + } + $newTextMD5 = $this->getHash($newText); + + // Try to load existing entry + $row = $this->gateway->loadRow($parentId, $newTextMD5); + + // If nothing was returned insert new entry + if (empty($row)) { + // Check for existing active location entry on this level and reuse it's id + $existingLocationEntry = $this->gateway->loadAutogeneratedEntry($action, $parentId); + if (!empty($existingLocationEntry)) { + $cleanup = true; + $newId = $existingLocationEntry['id']; + } + + try { + $newId = $this->gateway->insertRow( + [ + 'id' => $newId, + 'link' => $newId, + 'parent' => $parentId, + 'action' => $action, + 'lang_mask' => $languageMask, + 'text' => $newText, + 'text_md5' => $newTextMD5, + ] + ); + } catch (\RuntimeException $e) { + while ($e->getPrevious() !== null) { + $e = $e->getPrevious(); + if ($e instanceof UniqueConstraintViolationException) { + // Concurrency! someone else inserted the same row that we where going to. + // let's do another loop pass + ++$uniqueCounter; + continue 2; + } + } + + throw $e; + } + + break; + } + + // Row exists, check if it is reusable. There are 3 cases when this is possible: + // 1. NOP entry + // 2. existing location or custom alias entry + // 3. history entry + if ( + $row['action'] === Gateway::NOP_ACTION || + $row['action'] === $action || + (int)$row['is_original'] === 0 + ) { + // Check for existing location entry on this level, if it exists and it's id differs from reusable + // entry id then reusable entry should be updated with the existing location entry id. + // Note: existing location entry may be downgraded and relinked later, depending on its language. + $existingLocationEntry = $this->gateway->loadAutogeneratedEntry($action, $parentId); + + if (!empty($existingLocationEntry)) { + // Always cleanup when active autogenerated entry exists on the same level + $cleanup = true; + $newId = $existingLocationEntry['id']; + if ($existingLocationEntry['id'] == $row['id']) { + // If we are reusing existing location entry merge existing language mask + $languageMask |= ($row['lang_mask'] & ~1); + } + } elseif ($newId === null) { + // Use reused row ID only if publishing normally, else use given $newId + $newId = $row['id']; + } + + $this->gateway->updateRow( + $parentId, + $newTextMD5, + [ + 'action' => $action, + // In case when NOP row was reused + 'action_type' => 'eznode', + 'lang_mask' => $languageMask, + // Updating text ensures that letter case changes are stored + 'text' => $newText, + // Set "id" and "link" for case when reusable entry is history + 'id' => $newId, + 'link' => $newId, + // Entry should be active location entry (original and not alias). + // Note: this takes care of taking over custom alias entry for the location on the same level + // and with same name and action. + 'alias_redirects' => 1, + 'is_original' => 1, + 'is_alias' => 0, + ] + ); + + break; + } + + // If existing row is not reusable, increment $uniqueCounter and try again + ++$uniqueCounter; + } + + /* @var $newText */ + if ($updatePathIdentificationString) { + $this->locationGateway->updatePathIdentificationString( + $locationId, + $parentLocationId, + $this->slugConverter->convert($newText, 'node_' . $locationId, 'urlalias_compat') + ); + } + + /* @var $newId */ + /* @var $newTextMD5 */ + // Note: cleanup does not touch custom and global entries + if ($cleanup) { + $this->gateway->cleanupAfterPublish($action, $languageId, $newId, $parentId, $newTextMD5); + } + + return $this->mapper->generateIdentityKey($parentId, $newTextMD5); + } + + /** + * Create a user chosen $alias pointing to $locationId in $languageCode. + * + * If $languageCode is null the $alias is created in the system's default + * language. $alwaysAvailable makes the alias available in all languages. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * + * @param mixed $locationId + * @param string $path + * @param bool $forwarding + * @param string $languageCode + * @param bool $alwaysAvailable + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlAlias + */ + public function createCustomUrlAlias($locationId, $path, $forwarding = false, $languageCode = null, $alwaysAvailable = false) + { + return $this->createUrlAlias( + 'eznode:' . $locationId, + $path, + $forwarding, + $languageCode, + $alwaysAvailable + ); + } + + /** + * Create a user chosen $alias pointing to a resource in $languageCode. + * This method does not handle location resources - if a user enters a location target + * the createCustomUrlAlias method has to be used. + * + * If $languageCode is null the $alias is created in the system's default + * language. $alwaysAvailable makes the alias available in all languages. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException if the path already exists for the given resource + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the path is broken + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * + * @param string $resource + * @param string $path + * @param bool $forwarding + * @param string $languageCode + * @param bool $alwaysAvailable + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlAlias + */ + public function createGlobalUrlAlias($resource, $path, $forwarding = false, $languageCode = null, $alwaysAvailable = false) + { + return $this->createUrlAlias( + $resource, + $path, + $forwarding, + $languageCode, + $alwaysAvailable + ); + } + + /** + * Internal method for creating global or custom URL alias (these are handled in the same way). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException if the path already exists for the given context + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * + * @param string $action + * @param string $path + * @param bool $forward + * @param string|null $languageCode + * @param bool $alwaysAvailable + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlAlias + */ + protected function createUrlAlias($action, $path, $forward, $languageCode, $alwaysAvailable): UrlAlias + { + $pathElements = explode('/', $path); + $topElement = array_pop($pathElements); + $languageId = $this->languageHandler->loadByLanguageCode($languageCode)->id; + $parentId = 0; + + // Handle all path elements except topmost one + $isPathNew = false; + foreach ($pathElements as $level => $pathElement) { + $pathElement = $this->slugConverter->convert($pathElement, 'noname' . ($level + 1)); + $pathElementMD5 = $this->getHash($pathElement); + if (!$isPathNew) { + $row = $this->gateway->loadRow($parentId, $pathElementMD5); + if (empty($row)) { + $isPathNew = true; + } else { + $parentId = $row['link']; + } + } + + if ($isPathNew) { + $parentId = $this->insertNopEntry($parentId, $pathElement, $pathElementMD5); + } + } + + // Handle topmost path element + $topElement = $this->slugConverter->convert( + $topElement, + 'noname' . (count($pathElements) + 1) + ); + + // If last (next to topmost) entry parent is special root entry we handle topmost entry as first level entry + // That is why we need to reset $parentId to 0 + if ($parentId !== 0 && $this->gateway->isRootEntry($parentId)) { + $parentId = 0; + } + + $topElementMD5 = $this->getHash($topElement); + // Set common values for two cases below + $data = [ + 'action' => $action, + 'is_alias' => 1, + 'alias_redirects' => $forward ? 1 : 0, + 'parent' => $parentId, + 'text' => $topElement, + 'text_md5' => $topElementMD5, + 'is_original' => 1, + ]; + // Try to load topmost element + if (!$isPathNew) { + $row = $this->gateway->loadRow($parentId, $topElementMD5); + } + + // If nothing was returned perform insert + if ($isPathNew || empty($row)) { + $data['lang_mask'] = $languageId | (int)$alwaysAvailable; + $id = $this->gateway->insertRow($data); + } elseif ($row['action'] === Gateway::NOP_ACTION || (int)$row['is_original'] === 0) { + // Row exists, check if it is reusable. There are 2 cases when this is possible: + // 1. NOP entry + // 2. history entry + $data['lang_mask'] = $languageId | (int)$alwaysAvailable; + // If history is reused move link to id + $data['link'] = $id = $row['id']; + $this->gateway->updateRow( + $parentId, + $topElementMD5, + $data + ); + } elseif ( + $row['action'] === $action && + (int)$row['is_alias'] === 1 && + 0 === ((int)$row['lang_mask'] & $languageId) + ) { + // add another language to the same custom alias + $data['link'] = $id = $row['id']; + $data['lang_mask'] = $row['lang_mask'] | $languageId | (int)$alwaysAvailable; + $this->gateway->updateRow( + $parentId, + $topElementMD5, + $data + ); + } else { + throw new ForbiddenException( + "Path '%path%' already exists for the given context", + ['%path%' => $path] + ); + } + + $data['raw_path_data'] = $this->gateway->loadPathData($id); + + return $this->mapper->extractUrlAliasFromData($data); + } + + /** + * Convenience method for inserting nop type row. + * + * @param mixed $parentId + * @param string $text + * @param string $textMD5 + * + * @return mixed + */ + protected function insertNopEntry($parentId, $text, $textMD5) + { + return $this->gateway->insertRow( + [ + 'lang_mask' => 1, + 'action' => Gateway::NOP_ACTION, + 'parent' => $parentId, + 'text' => $text, + 'text_md5' => $textMD5, + ] + ); + } + + /** + * List of user generated or autogenerated url entries, pointing to $locationId. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * + * @param mixed $locationId + * @param bool $custom if true the user generated aliases are listed otherwise the autogenerated + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlAlias[] + */ + public function listURLAliasesForLocation($locationId, $custom = false) + { + $data = $this->gateway->loadLocationEntries($locationId, $custom); + foreach ($data as &$entry) { + $entry['raw_path_data'] = $this->gateway->loadPathData($entry['id']); + } + + return $this->mapper->extractUrlAliasListFromData($data); + } + + /** + * List global aliases. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * + * @param string|null $languageCode + * @param int $offset + * @param int $limit + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlAlias[] + */ + public function listGlobalURLAliases($languageCode = null, $offset = 0, $limit = -1) + { + $data = $this->gateway->listGlobalEntries($languageCode, $offset, $limit); + foreach ($data as &$entry) { + $entry['raw_path_data'] = $this->gateway->loadPathData($entry['id']); + } + + return $this->mapper->extractUrlAliasListFromData($data); + } + + /** + * Removes url aliases. + * + * Autogenerated aliases are not removed by this method. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\UrlAlias[] $urlAliases + * + * @return bool + */ + public function removeURLAliases(array $urlAliases) + { + foreach ($urlAliases as $urlAlias) { + if ($urlAlias->isCustom) { + list($parentId, $textMD5) = explode('-', $urlAlias->id); + if (!$this->gateway->removeCustomAlias($parentId, $textMD5)) { + return false; + } + } + } + + return true; + } + + /** + * Looks up a url alias for the given url. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * + * @param string $url + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlAlias + */ + public function lookup($url) + { + $urlHashes = []; + foreach (explode('/', $url) as $level => $text) { + $urlHashes[$level] = $this->getHash($text); + } + + $pathDepth = count($urlHashes); + if ($pathDepth > self::MAX_URL_ALIAS_DEPTH_LEVEL) { + throw new InvalidArgumentException('$urlHashes', 'Exceeded maximum depth level for content URL alias.'); + } + + $data = $this->gateway->loadUrlAliasData($urlHashes); + if (empty($data)) { + throw new NotFoundException('URLAlias', $url); + } + + $hierarchyData = []; + $isPathHistory = false; + for ($level = 0; $level < $pathDepth; ++$level) { + $prefix = $level === $pathDepth - 1 ? '' : 'ezurlalias_ml' . $level . '_'; + $isPathHistory = $isPathHistory ?: ($data[$prefix . 'link'] != $data[$prefix . 'id']); + $hierarchyData[$level] = [ + 'id' => $data[$prefix . 'id'], + 'parent' => $data[$prefix . 'parent'], + 'action' => $data[$prefix . 'action'], + ]; + } + + $data['is_path_history'] = $isPathHistory; + $data['raw_path_data'] = ($data['action_type'] == 'eznode' && !$data['is_alias']) + ? $this->gateway->loadPathDataByHierarchy($hierarchyData) + : $this->gateway->loadPathData($data['id']); + + return $this->mapper->extractUrlAliasFromData($data); + } + + /** + * Loads URL alias by given $id. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * + * @param string $id + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlAlias + */ + public function loadUrlAlias($id) + { + list($parentId, $textMD5) = explode('-', $id); + $data = $this->gateway->loadRow((int)$parentId, $textMD5); + + if (empty($data)) { + throw new NotFoundException('URLAlias', $id); + } + + $data['raw_path_data'] = $this->gateway->loadPathData((int)$data['id']); + + return $this->mapper->extractUrlAliasFromData($data); + } + + /** + * Notifies the underlying engine that a location has moved. + * + * This method triggers the change of the autogenerated aliases. + * + * @param mixed $locationId + * @param mixed $oldParentId + * @param mixed $newParentId + */ + public function locationMoved($locationId, $oldParentId, $newParentId) + { + if ($oldParentId === $newParentId) { + return $newParentId; + } + + // @todo optimize: $newLocationAliasId is already available in self::publishUrlAliasForLocation() as $newId + $newParentLocationAliasId = $this->getRealAliasId($newParentId); + $newLocationAlias = $this->gateway->loadAutogeneratedEntry( + 'eznode:' . $locationId, + $newParentLocationAliasId + ); + + $oldParentLocationAliasId = $this->getRealAliasId($oldParentId); + $oldLocationAlias = $this->gateway->loadAutogeneratedEntry( + 'eznode:' . $locationId, + $oldParentLocationAliasId + ); + + // Historize alias for old location + $this->gateway->historizeId($oldLocationAlias['id'], $newLocationAlias['id']); + // Reparent subtree of old location to new location + $this->gateway->reparent($oldLocationAlias['id'], $newLocationAlias['id']); + } + + /** + * Notifies the underlying engine that a location was copied. + * + * This method triggers the creation of the autogenerated aliases for the copied locations + * + * @param mixed $locationId + * @param mixed $newLocationId + * @param mixed $newParentId + */ + public function locationCopied($locationId, $newLocationId, $newParentId) + { + $newParentAliasId = $this->getRealAliasId($newLocationId); + $oldParentAliasId = $this->getRealAliasId($locationId); + + $actionMap = $this->getCopiedLocationsMap($locationId, $newLocationId); + + $this->copySubtree( + $actionMap, + $oldParentAliasId, + $newParentAliasId + ); + } + + /** + * Notify the underlying engine that a Location has been swapped. + * + * This method triggers the change of the autogenerated aliases. + * + * @param int $location1Id + * @param int $location1ParentId + * @param int $location2Id + * @param int $location2ParentId + * + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + */ + public function locationSwapped($location1Id, $location1ParentId, $location2Id, $location2ParentId) + { + $location1 = new SwappedLocationProperties($location1Id, $location1ParentId); + $location2 = new SwappedLocationProperties($location2Id, $location2ParentId); + + $location1->entries = $this->gateway->loadAllLocationEntries($location1Id); + $location2->entries = $this->gateway->loadAllLocationEntries($location2Id); + + $location1->mainLanguageId = $this->gateway->getLocationContentMainLanguageId($location1Id); + $location2->mainLanguageId = $this->gateway->getLocationContentMainLanguageId($location2Id); + + // Load autogenerated entries to find alias ID + $location1->autogeneratedId = $this->gateway->loadAutogeneratedEntry("eznode:{$location1Id}")['id']; + $location2->autogeneratedId = $this->gateway->loadAutogeneratedEntry("eznode:{$location2Id}")['id']; + + $contentInfo1 = $this->contentGateway->loadContentInfoByLocationId($location1Id); + $contentInfo2 = $this->contentGateway->loadContentInfoByLocationId($location2Id); + + $names1 = $this->getNamesForAllLanguages($contentInfo1); + $names2 = $this->getNamesForAllLanguages($contentInfo2); + + $location1->isAlwaysAvailable = $this->maskGenerator->isAlwaysAvailable($contentInfo1['language_mask']); + $location2->isAlwaysAvailable = $this->maskGenerator->isAlwaysAvailable($contentInfo2['language_mask']); + + $languages = $this->languageHandler->loadAll(); + + // Historize everything first to avoid name conflicts in case swapped Locations are siblings + $this->historizeBeforeSwap($location1->entries, $location2->entries); + + foreach ($languages as $languageCode => $language) { + $location1->name = isset($names1[$languageCode]) ? $names1[$languageCode] : null; + $location2->name = isset($names2[$languageCode]) ? $names2[$languageCode] : null; + $urlAliasesForSwappedLocations = $this->getUrlAliasesForSwappedLocations( + $language, + $location1, + $location2 + ); + foreach ($urlAliasesForSwappedLocations as $urlAliasForLocation) { + $this->internalPublishUrlAliasForLocation( + $urlAliasForLocation->id, + $urlAliasForLocation->parentId, + $urlAliasForLocation->name, + $language->id, + $urlAliasForLocation->isAlwaysAvailable, + $urlAliasForLocation->isPathIdentificationStringModified, + $urlAliasForLocation->newId + ); + } + } + + $this->internalPublishCustomUrlAliasForLocation($location1, $contentInfo1['language_mask']); + $this->internalPublishCustomUrlAliasForLocation($location2, $contentInfo2['language_mask']); + } + + /** + * @param array $contentInfo + * + * @return array + */ + private function getNamesForAllLanguages(array $contentInfo) + { + $nameDataArray = $this->contentGateway->loadVersionedNameData([ + [ + 'id' => $contentInfo['id'], + 'version' => $contentInfo['current_version'], + ], + ]); + + $namesForAllLanguages = []; + foreach ($nameDataArray as $nameData) { + $namesForAllLanguages[$nameData['ezcontentobject_name_content_translation']] + = $nameData['ezcontentobject_name_name']; + } + + return $namesForAllLanguages; + } + + /** + * Historizes given existing active entries for two swapped Locations. + * + * This should be done before republishing URL aliases, in order to avoid unnecessary + * conflicts when swapped Locations are siblings. + * + * We need to historize everything separately per language (mask), in case the entries + * remain history future publishing reusages need to be able to take them over cleanly. + * + * @see \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Handler::locationSwapped + * + * @param array $location1Entries + * @param array $location2Entries + */ + private function historizeBeforeSwap($location1Entries, $location2Entries) + { + foreach ($location1Entries as $row) { + $this->gateway->historizeBeforeSwap($row['action'], $row['lang_mask']); + } + + foreach ($location2Entries as $row) { + $this->gateway->historizeBeforeSwap($row['action'], $row['lang_mask']); + } + } + + /** + * Decides if UrlAlias for $location2 should be published first. + * + * The order in which Locations are published only matters if swapped Locations are siblings and they have the same + * name in a given language. In this case, the UrlAlias for Location which previously had lower number at the end of + * its UrlAlias text (or no number at all) should be published first. This ensures that the number still stays lower + * for this Location after the swap. If it wouldn't stay lower, then swapping Locations in conjunction with swapping + * UrlAliases would effectively cancel each other. + * + * @param array $location1Entries + * @param int $location1ParentId + * @param string $name1 + * @param array $location2Entries + * @param int $location2ParentId + * @param string $name2 + * @param int $languageId + * + * @return bool + */ + private function shouldUrlAliasForSecondLocationBePublishedFirst( + array $location1Entries, + $location1ParentId, + $name1, + array $location2Entries, + $location2ParentId, + $name2, + $languageId + ) { + if ($location1ParentId === $location2ParentId && $name1 === $name2) { + $locationEntry1 = $this->getLocationEntryInLanguage($location1Entries, $languageId); + $locationEntry2 = $this->getLocationEntryInLanguage($location2Entries, $languageId); + + if ($locationEntry1 === null || $locationEntry2 === null) { + return false; + } + + if ($locationEntry2['text'] < $locationEntry1['text']) { + return true; + } + } + + return false; + } + + /** + * Get in a proper order - to be published - a list of URL aliases for swapped Locations. + * + * @see shouldUrlAliasForSecondLocationBePublishedFirst + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Language $language + * @param \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\DTO\SwappedLocationProperties $location1 + * @param \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\DTO\SwappedLocationProperties $location2 + * + * @return \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\DTO\UrlAliasForSwappedLocation[] + */ + private function getUrlAliasesForSwappedLocations( + Language $language, + SwappedLocationProperties $location1, + SwappedLocationProperties $location2 + ) { + $isMainLanguage1 = $language->id == $location1->mainLanguageId; + $isMainLanguage2 = $language->id == $location2->mainLanguageId; + $urlAliases = []; + if (isset($location1->name)) { + $urlAliases[] = new UrlAliasForSwappedLocation( + $location1->id, + $location1->parentId, + $location1->name, + $isMainLanguage2 && $location1->isAlwaysAvailable, + $isMainLanguage2, + $location1->autogeneratedId + ); + } + + if (isset($location2->name)) { + $urlAliases[] = new UrlAliasForSwappedLocation( + $location2->id, + $location2->parentId, + $location2->name, + $isMainLanguage1 && $location2->isAlwaysAvailable, + $isMainLanguage1, + $location2->autogeneratedId + ); + + if (isset($location1->name) && $this->shouldUrlAliasForSecondLocationBePublishedFirst( + $location1->entries, + $location1->parentId, + $location1->name, + $location2->entries, + $location2->parentId, + $location2->name, + $language->id + )) { + $urlAliases = array_reverse($urlAliases); + } + } + + return $urlAliases; + } + + /** + * @param array $locationEntries + * @param int $languageId + * + * @return array|null + */ + private function getLocationEntryInLanguage(array $locationEntries, $languageId) + { + $entries = array_filter( + $locationEntries, + static function (array $row) use ($languageId) { + return (bool) ($row['lang_mask'] & $languageId); + } + ); + + return !empty($entries) ? array_shift($entries) : null; + } + + /** + * Returns possibly corrected alias id for given $locationId !! For use as parent id in logic. + * + * First level entries must have parent id set to 0 instead of their parent location alias id. + * There are two cases when alias id needs to be corrected: + * 1) location is special location without URL alias (location with id=1 in standard installation) + * 2) location is site root location, having special root entry in the ezurlalias_ml table (location with id=2 + * in standard installation) + * + * @param mixed $locationId + * + * @return mixed + */ + protected function getRealAliasId($locationId) + { + // Absolute root location does have a url alias entry so we can skip lookup + if ($locationId == self::ROOT_LOCATION_ID) { + return 0; + } + + $data = $this->gateway->loadAutogeneratedEntry('eznode:' . $locationId); + + // Root entries (URL wise) can return 0 as the returned value is used as parent (parent is 0 for root entries) + if (empty($data) || ($data['id'] != 0 && $data['parent'] == 0 && strlen($data['text']) == 0)) { + $id = 0; + } else { + $id = $data['id']; + } + + return $id; + } + + /** + * Recursively copies aliases from old parent under new parent. + * + * @param array $actionMap + * @param mixed $oldParentAliasId + * @param mixed $newParentAliasId + * @param string[] $alreadyGeneratedAliases + */ + protected function copySubtree($actionMap, $oldParentAliasId, $newParentAliasId, array $alreadyGeneratedAliases = []): array + { + $rows = $this->gateway->loadAutogeneratedEntries($oldParentAliasId); + $newIdsMap = []; + foreach ($rows as $row) { + $identifier = $row['parent'] . $row['text_md5']; + if (in_array($identifier, $alreadyGeneratedAliases)) { + continue; + } + + $oldParentAliasId = $row['id']; + + // Ensure that same action entries remain grouped by the same id + if (!isset($newIdsMap[$oldParentAliasId])) { + $newIdsMap[$oldParentAliasId] = $this->gateway->getNextId(); + } + + $row['action'] = $actionMap[$row['action']]; + $row['parent'] = $newParentAliasId; + $row['id'] = $row['link'] = $newIdsMap[$oldParentAliasId]; + $this->gateway->insertRow($row); + + $alreadyGeneratedAliases[] = $identifier; + + $alreadyGeneratedAliases = $this->copySubtree( + $actionMap, + $oldParentAliasId, + $row['id'], + $alreadyGeneratedAliases + ); + } + + return $alreadyGeneratedAliases; + } + + /** + * @param mixed $oldParentId + * @param mixed $newParentId + * + * @return array + */ + protected function getCopiedLocationsMap($oldParentId, $newParentId) + { + $originalLocations = $this->locationGateway->getSubtreeContent($oldParentId); + $copiedLocations = $this->locationGateway->getSubtreeContent($newParentId); + + $map = []; + foreach ($originalLocations as $index => $originalLocation) { + $map['eznode:' . $originalLocation['node_id']] = 'eznode:' . $copiedLocations[$index]['node_id']; + } + + return $map; + } + + public function locationDeleted($locationId): array + { + $action = 'eznode:' . $locationId; + $entry = $this->gateway->loadAutogeneratedEntry($action); + $entryId = $entry['id']; + + $this->removeSubtree($entryId, $action, $entry['is_original']); + + // after location-delete process completed + // check if entry had children; if yes - then restore them as nop-type + // for historical aliases relates to that entry + $notDeletedChildrenAliases = $this->gateway->getAllChildrenAliases($entryId); + if (count($notDeletedChildrenAliases) > 0) { + $this->insertAliasEntryAsNop($entry); + } + + return $notDeletedChildrenAliases; + } + + /** + * Notifies the underlying engine that Locations Content Translation was removed. + * + * @param int[] $locationIds all Locations of the Content that got Translation removed + * @param string $languageCode language code of the removed Translation + */ + public function translationRemoved(array $locationIds, $languageCode) + { + $languageId = $this->languageHandler->loadByLanguageCode($languageCode)->id; + + $actions = []; + foreach ($locationIds as $locationId) { + $actions[] = 'eznode:' . $locationId; + } + $this->gateway->bulkRemoveTranslation($languageId, $actions); + } + + /** + * Recursively removes aliases by given $id and $action. + * + * $original parameter is used to limit removal of moved Location aliases to history entries only. + * + * @param mixed $id + * @param string $action + * @param mixed $original + */ + protected function removeSubtree($id, $action, $original) + { + // Remove first to avoid unnecessary recursion. + if ($original) { + // If entry is original remove all for action (history and custom entries included). + $this->gateway->remove($action); + } else { + // Else entry is history, so remove only for action with the id. + // This means $id grouped history entries are removed, other history, active autogenerated + // and custom are left alone. + $this->gateway->remove($action, $id); + } + + // Load all autogenerated for parent $id, including history. + $entries = $this->gateway->loadAutogeneratedEntries($id, true); + + foreach ($entries as $entry) { + $this->removeSubtree($entry['id'], $entry['action'], $entry['is_original']); + } + } + + /** + * @param string $text + * + * @return string + */ + protected function getHash($text) + { + return md5(mb_strtolower($text, 'UTF-8')); + } + + /** + * {@inheritdoc} + */ + public function archiveUrlAliasesForDeletedTranslations($locationId, $parentLocationId, array $languageCodes) + { + $parentId = $this->getRealAliasId($parentLocationId); + + $data = $this->gateway->loadLocationEntries($locationId); + // filter removed Translations + $removedLanguages = array_diff( + $this->mapper->extractLanguageCodesFromData($data), + $languageCodes + ); + + if (empty($removedLanguages)) { + return; + } + + // map languageCodes to their IDs + $languageIds = array_map( + function ($languageCode) { + return $this->languageHandler->loadByLanguageCode($languageCode)->id; + }, + $removedLanguages + ); + + $this->gateway->archiveUrlAliasesForDeletedTranslations($locationId, $parentId, $languageIds); + } + + /** + * Remove corrupted URL aliases (global, custom and system). + * + * @return int Number of removed URL aliases + * + * @throws \Exception + */ + public function deleteCorruptedUrlAliases() + { + $this->transactionHandler->beginTransaction(); + try { + $totalCount = $this->gateway->deleteUrlAliasesWithoutLocation(); + $totalCount += $this->gateway->deleteUrlAliasesWithoutParent(); + $totalCount += $this->gateway->deleteUrlAliasesWithBrokenLink(); + $totalCount += $this->gateway->deleteUrlNopAliasesWithoutChildren(); + + $this->transactionHandler->commit(); + + return $totalCount; + } catch (\Exception $e) { + $this->transactionHandler->rollback(); + throw $e; + } + } + + /** + * Attempt repairing auto-generated URL aliases for the given Location (including history). + * + * Note: it is assumed that at this point original, working, URL Alias for Location is published. + * + * @param int $locationId + * + * @throws \Ibexa\Core\Base\Exceptions\BadStateException + */ + public function repairBrokenUrlAliasesForLocation(int $locationId) + { + try { + $this->gateway->repairBrokenUrlAliasesForLocation($locationId); + } catch (\RuntimeException $e) { + throw new BadStateException('locationId', $e->getMessage(), $e); + } + } + + private function insertAliasEntryAsNop(array $aliasEntry): void + { + $aliasEntry['action'] = Gateway::NOP_ACTION; + $aliasEntry['action_type'] = Gateway::NOP; + + $this->gateway->insertRow($aliasEntry); + } + + /** + * Internal publish custom aliases method, accepting language mask to set correct language mask on url aliases + * new alias ID (used when swapping Locations). + */ + private function internalPublishCustomUrlAliasForLocation(SwappedLocationProperties $location, int $languageMask) + { + foreach ($location->entries as $entry) { + if ((int)$entry['is_alias'] === 0) { + continue; + } + + $mask = (int)$entry['lang_mask'] & $languageMask; + + if ($mask <= 1) { + continue; + } + + $this->gateway->updateRow( + (int)$entry['parent'], + $entry['text_md5'], + [ + 'id' => (int)$entry['id'], + 'is_original' => 1, + 'is_alias' => 1, + 'lang_mask' => $mask, + ] + ); + } + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Handler'); diff --git a/src/lib/Persistence/Legacy/Content/UrlAlias/Mapper.php b/src/lib/Persistence/Legacy/Content/UrlAlias/Mapper.php new file mode 100644 index 0000000000..17cad221a1 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/UrlAlias/Mapper.php @@ -0,0 +1,178 @@ +languageMaskGenerator = $languageMaskGenerator; + } + + /** + * Creates a UrlAlias object from database row data. + * + * @param mixed[] $data + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlAlias + */ + public function extractUrlAliasFromData($data) + { + $urlAlias = new UrlAlias(); + + list($type, $destination) = $this->matchTypeAndDestination($data['action']); + $urlAlias->id = $this->generateIdentityKey((int)$data['parent'], $data['text_md5']); + $urlAlias->pathData = $this->normalizePathData($data['raw_path_data']); + $urlAlias->languageCodes = $this->languageMaskGenerator->extractLanguageCodesFromMask($data['lang_mask']); + $urlAlias->alwaysAvailable = $this->languageMaskGenerator->isAlwaysAvailable($data['lang_mask']); + $urlAlias->isHistory = isset($data['is_path_history']) ? $data['is_path_history'] : !$data['is_original']; + $urlAlias->isCustom = (bool)$data['is_alias']; + $urlAlias->forward = $data['is_alias'] && $data['alias_redirects']; + $urlAlias->destination = $destination; + $urlAlias->type = $type; + + return $urlAlias; + } + + /** + * Extracts UrlAlias objects from database $rows. + * + * @param array $rows + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlAlias[] + */ + public function extractUrlAliasListFromData(array $rows) + { + $urlAliases = []; + foreach ($rows as $row) { + $urlAliases[] = $this->extractUrlAliasFromData($row); + } + + return $urlAliases; + } + + /** + * Extracts language codes from database $rows. + * + * @param array $rows + * + * @return string[] + */ + public function extractLanguageCodesFromData(array $rows): array + { + $languageMask = 0; + foreach ($rows as $row) { + $languageMask |= $row['lang_mask']; + } + + return $this->languageMaskGenerator->extractLanguageCodesFromMask($languageMask); + } + + public function generateIdentityKey(int $parentId, string $hash): string + { + return sprintf('%d-%s', $parentId, $hash); + } + + /** + * @throws \RuntimeException + * + * @param string $action + * + * @return array + */ + protected function matchTypeAndDestination($action) + { + if (preg_match('#^([a-zA-Z0-9_]+):(.+)?$#', $action, $matches)) { + $actionType = $matches[1]; + $actionValue = isset($matches[2]) ? $matches[2] : false; + + switch ($actionType) { + case 'eznode': + $type = UrlAlias::LOCATION; + $destination = (int)$actionValue; + break; + + case 'module': + $type = UrlAlias::RESOURCE; + $destination = $actionValue; + break; + + case 'nop': + $type = UrlAlias::VIRTUAL; + $destination = null; + break; + + default: + // @todo log message + throw new \RuntimeException("Action type '{$actionType}' is unknown"); + } + } else { + // @todo log message + throw new \RuntimeException("Action '{$action}' is not valid"); + } + + return [$type, $destination]; + } + + /** + * @param array $pathData + * + * @return array + */ + protected function normalizePathData(array $pathData) + { + $normalizedPathData = []; + foreach ($pathData as $level => $rows) { + $pathElementData = []; + foreach ($rows as $row) { + $this->normalizePathDataRow($pathElementData, $row); + } + + $normalizedPathData[$level] = $pathElementData; + } + + return $normalizedPathData; + } + + /** + * @param array $pathElementData + * @param array $row + */ + protected function normalizePathDataRow(array &$pathElementData, array $row) + { + $languageCodes = $this->languageMaskGenerator->extractLanguageCodesFromMask($row['lang_mask']); + $pathElementData['always-available'] = $this->languageMaskGenerator->isAlwaysAvailable($row['lang_mask']); + if (!empty($languageCodes)) { + foreach ($languageCodes as $languageCode) { + $pathElementData['translations'][$languageCode] = $row['text']; + } + } elseif ($pathElementData['always-available']) { + // NOP entry, lang_mask == 1 + $pathElementData['translations']['always-available'] = $row['text']; + } + } +} + +class_alias(Mapper::class, 'eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Mapper'); diff --git a/src/lib/Persistence/Legacy/Content/UrlAlias/SlugConverter.php b/src/lib/Persistence/Legacy/Content/UrlAlias/SlugConverter.php new file mode 100644 index 0000000000..d24883391c --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/UrlAlias/SlugConverter.php @@ -0,0 +1,384 @@ + 'dash', + 'urlAliasNameLimit' => 255, + 'transformation' => 'urlalias', + 'transformationGroups' => [ + 'urlalias' => [ + 'commands' => [ + //normalize + 'space_normalize', + 'hyphen_normalize', + 'apostrophe_normalize', + 'doublequote_normalize', + 'greek_normalize', + 'endline_search_normalize', + 'tab_search_normalize', + 'specialwords_search_normalize', + + //transform + 'apostrophe_to_doublequote', + 'math_to_ascii', + 'inverted_to_normal', + + //decompose + 'special_decompose', + 'latin_search_decompose', + + //transliterate + 'cyrillic_transliterate_ascii', + 'greek_transliterate_ascii', + 'hebrew_transliterate_ascii', + 'latin1_transliterate_ascii', + 'latin-exta_transliterate_ascii', + + //diacritical + 'cyrillic_diacritical', + 'greek_diacritical', + 'latin1_diacritical', + 'latin-exta_diacritical', + ], + 'cleanupMethod' => 'url_cleanup', + ], + 'urlalias_iri' => [ + 'commands' => [], + 'cleanupMethod' => 'url_cleanup_iri', + ], + 'urlalias_compat' => [ + 'commands' => [ + //normalize + 'space_normalize', + 'hyphen_normalize', + 'apostrophe_normalize', + 'doublequote_normalize', + 'greek_normalize', + 'endline_search_normalize', + 'tab_search_normalize', + 'specialwords_search_normalize', + + //transform + 'apostrophe_to_doublequote', + 'math_to_ascii', + 'inverted_to_normal', + + //decompose + 'special_decompose', + 'latin_search_decompose', + + //transliterate + 'cyrillic_transliterate_ascii', + 'greek_transliterate_ascii', + 'hebrew_transliterate_ascii', + 'latin1_transliterate_ascii', + 'latin-exta_transliterate_ascii', + + //diacritical + 'cyrillic_diacritical', + 'greek_diacritical', + 'latin1_diacritical', + 'latin-exta_diacritical', + + //lowercase + 'ascii_lowercase', + 'cyrillic_lowercase', + 'greek_lowercase', + 'latin1_lowercase', + 'latin-exta_lowercase', + 'latin_lowercase', + ], + 'cleanupMethod' => 'url_cleanup_compat', + ], + 'urlalias_lowercase' => [ + 'commands' => [ + 'ascii_lowercase', + 'cyrillic_lowercase', + 'greek_lowercase', + 'latin1_lowercase', + 'latin-exta_lowercase', + 'latin_lowercase', + + 'space_normalize', + 'hyphen_normalize', + 'apostrophe_normalize', + 'doublequote_normalize', + 'greek_normalize', + 'endline_search_normalize', + 'tab_search_normalize', + 'specialwords_search_normalize', + + 'apostrophe_to_doublequote', + 'math_to_ascii', + 'inverted_to_normal', + + 'special_decompose', + 'latin_search_decompose', + + 'cyrillic_transliterate_ascii', + 'greek_transliterate_ascii', + 'hebrew_transliterate_ascii', + 'latin1_transliterate_ascii', + 'latin-exta_transliterate_ascii', + + 'cyrillic_diacritical', + 'greek_diacritical', + 'latin1_diacritical', + 'latin-exta_diacritical', + ], + 'cleanupMethod' => 'url_cleanup', + ], + ], + 'reservedNames' => [ + 'class', + 'collaboration', + 'content', + 'error', + 'ezinfo', + 'infocollector', + 'layout', + 'notification', + 'oauth', + 'oauthadmin', + 'package', + 'pdf', + 'role', + 'rss', + 'search', + 'section', + 'settings', + 'setup', + 'shop', + 'state', + 'trigger', + 'url', + 'user', + 'visual', + 'workflow', + 'switchlanguage', + ], + ]; + + /** + * Transformation processor to normalize URL strings. + * + * @var \Ibexa\Core\Persistence\TransformationProcessor + */ + protected $transformationProcessor; + + /** @var array */ + protected $configuration; + + /** + * Creates a new URL slug converter. + * + * @param \Ibexa\Core\Persistence\TransformationProcessor $transformationProcessor + * @param array $configuration + */ + public function __construct( + TransformationProcessor $transformationProcessor, + array $configuration = [] + ) { + $this->transformationProcessor = $transformationProcessor; + $this->configuration = $configuration + self::DEFAULT_CONFIGURATION; + } + + /** + * Converts given $text into a URL slug consisting of URL valid characters. + * For non-Unicode setups this means character in the range a-z, numbers and _, for Unicode + * setups it means all characters except space, &, ;, /, :, =, ?, [, ], (, ), -. + * + * Invalid characters are converted to -. + * + * Example with a non-Unicode setup + * + * 'My car' => 'My-car' + * 'What is this?' => 'What-is-this' + * 'This & that' => 'This-that' + * 'myfile.tpl' => 'Myfile-tpl', + * 'øæå' => 'oeaeaa' + * + * @param string $text + * @param string $defaultText + * @param string|null $transformation + * + * @return string + */ + public function convert($text, $defaultText = '_1', $transformation = null) + { + if (!isset($transformation)) { + $transformation = $this->configuration['transformation']; + } + + if (strlen($text) === 0) { + $text = $defaultText; + } + + if (isset($this->configuration['transformationGroups'][$transformation]['commands']) + && !empty($this->configuration['transformationGroups'][$transformation]['commands']) + ) { + $text = $this->transformationProcessor->transform( + $text, + $this->configuration['transformationGroups'][$transformation]['commands'] + ); + } + + return $this->cleanupText( + $text, + $this->configuration['transformationGroups'][$transformation]['cleanupMethod'] + ); + } + + /** + * Returns unique counter number that is appended to the path element in order to make it unique + * against system reserved names and other entries on the same level. + * + * Comparison is done only if given $isRootLevel equals to true (it does by default), meaning that + * entry is at first level of URL. + * In a case when reserved name is matched method will return 2. + * When given $isRootLevel equals to false or when there is no match with reserved names this will + * return 1, which is default value not appended to name. + * + * Note: this is used only when publishing URL aliases, when creating global and custom aliases user + * is allowed to create first level entries that collide with reserved names. Also, in actual creation + * of the alias name will be further checked against existing elements under the same parent, using + * unique counter value determined here as starting unique counter value. + * + * @param string $text + * @param bool $isRootLevel + * + * @return int + */ + public function getUniqueCounterValue($text, $isRootLevel = true) + { + if ($isRootLevel) { + foreach ($this->configuration['reservedNames'] as $reservedName) { + // Case insensitive comparison + if (strcasecmp($text, $reservedName) === 0) { + return 2; + } + } + } + + return 1; + } + + /** + * Cleans up $text using given $method. + * + * @param string $text + * @param string $method + * + * @return string + */ + protected function cleanupText($text, $method) + { + switch ($method) { + case 'url_cleanup': + $sep = $this->getWordSeparator(); + $sepQ = preg_quote($sep); + $text = preg_replace( + [ + '#[^a-zA-Z0-9_!.-]+#', + '#^[.]+|[!.]+$#', // Remove dots at beginning/end + "#\.\.+#", // Remove double dots + "#[{$sepQ}]+#", // Turn multiple separators into one + "#^[{$sepQ}]+|[{$sepQ}]+$#", // Strip separator from beginning/end + ], + [ + $sep, + $sep, + $sep, + $sep, + '', + ], + $text + ); + break; + case 'url_cleanup_iri': + // With IRI support we keep all characters except some reserved ones, + // they are space, ampersand, semi-colon, forward slash, colon, equal sign, question mark, + // square brackets, parenthesis, plus. + // + // Note: Space is turned into a dash to make it easier for people to + // paste urls from the system and have the whole url recognized + // instead of being broken off + $sep = $this->getWordSeparator(); + $sepQ = preg_quote($sep); + $prepost = ' .' . $sepQ; + if ($sep != '-') { + $prepost .= '-'; + } + $text = preg_replace( + [ + "#[ \\\\%\#&;/:=?\[\]()+]+#", + '#^[.]+|[!.]+$#', // Remove dots at beginning/end + "#\.\.+#", // Remove double dots + "#[{$sepQ}]+#", // Turn multiple separators into one + "#^[{$prepost}]+|[{$prepost}]+$#", + ], + [ + $sep, + $sep, + $sep, + $sep, + '', + ], + $text + ); + break; + case 'url_cleanup_compat': + // Old style of url alias with lowercase only and underscores for separators + $text = strtolower($text); + $text = preg_replace( + [ + '#[^a-z0-9]+#', + '#^_+|_+$#', + ], + [ + '_', + '', + ], + $text + ); + break; + default: + // Nothing + } + + return $text; + } + + /** + * Returns word separator value. + * + * @return string + */ + protected function getWordSeparator() + { + switch ($this->configuration['wordSeparatorName']) { + case 'dash': + return '-'; + case 'underscore': + return '_'; + case 'space': + return ' '; + } + + return '-'; + } +} + +class_alias(SlugConverter::class, 'eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter'); diff --git a/src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway.php b/src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway.php new file mode 100644 index 0000000000..303e01611c --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway.php @@ -0,0 +1,81 @@ + 'ASC', + SortClause::SORT_DESC => 'DESC', + ]; + + public function __construct(Connection $connection, CriteriaConverter $criteriaConverter) + { + $this->connection = $connection; + $this->criteriaConverter = $criteriaConverter; + } + + public function insertUrlWildcard(UrlWildcard $urlWildcard): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::URL_WILDCARD_TABLE) + ->values( + [ + 'destination_url' => $query->createPositionalParameter( + $this->trimUrl($urlWildcard->destinationUrl), + ParameterType::STRING + ), + 'source_url' => $query->createPositionalParameter( + $this->trimUrl($urlWildcard->sourceUrl), + ParameterType::STRING + ), + 'type' => $query->createPositionalParameter( + $urlWildcard->forward ? 1 : 2, + ParameterType::INTEGER + ), + ] + ); + + $query->execute(); + + return (int)$this->connection->lastInsertId(self::URL_WILDCARD_SEQ); + } + + public function updateUrlWildcard( + int $id, + string $sourceUrl, + string $destinationUrl, + bool $forward + ): void { + $query = $this->connection->createQueryBuilder(); + + $query + ->update(self::URL_WILDCARD_TABLE) + ->set( + 'destination_url', + $query->createPositionalParameter( + $this->trimUrl($destinationUrl), + ParameterType::STRING + ), + )->set( + 'source_url', + $query->createPositionalParameter( + $this->trimUrl($sourceUrl), + ParameterType::STRING + ), + )->set( + 'type', + $query->createPositionalParameter( + $forward ? 1 : 2, + ParameterType::INTEGER + ) + ); + + $query->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter( + $id, + ParameterType::INTEGER + ) + ) + ); + + $query->execute(); + } + + public function deleteUrlWildcard(int $id): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::URL_WILDCARD_TABLE) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + private function buildLoadUrlWildcardDataQuery(): QueryBuilder + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('id', 'destination_url', 'source_url', 'type') + ->from(self::URL_WILDCARD_TABLE); + + return $query; + } + + public function loadUrlWildcardData(int $id): array + { + $query = $this->buildLoadUrlWildcardDataQuery(); + $query + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + $result = $query->execute()->fetch(FetchMode::ASSOCIATIVE); + + return false !== $result ? $result : []; + } + + public function loadUrlWildcardsData(int $offset = 0, int $limit = -1): array + { + $query = $this->buildLoadUrlWildcardDataQuery(); + $query + ->setMaxResults($limit > 0 ? $limit : self::MAX_LIMIT) + ->setFirstResult($offset); + + $stmt = $query->execute(); + + return $stmt->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function find( + Criterion $criterion, + int $offset, + int $limit, + array $sortClauses = [], + bool $doCount = true + ): array { + $count = $doCount ? $this->doCount($criterion) : null; + if (!$doCount && $limit === 0) { + throw new RuntimeException('Invalid query. Cannot disable count and request 0 items at the same time'); + } + + if ($limit === 0 || ($count !== null && $count <= $offset)) { + return [ + 'count' => $count, + 'rows' => [], + ]; + } + + if ($limit < 0) { + throw new InvalidArgumentException('$limit', 'The limit need be higher than 0'); + } + + $query = $this->buildLoadUrlWildcardDataQuery(); + $query + ->where($this->criteriaConverter->convertCriteria($query, $criterion)) + ->setMaxResults($limit) + ->setFirstResult($offset); + + foreach ($sortClauses as $sortClause) { + $query->addOrderBy($sortClause->target, $this->getQuerySortingDirection($sortClause->direction)); + } + + $statement = $query->execute(); + + return [ + 'count' => $count, + 'rows' => $statement->fetchAllAssociative(), + ]; + } + + public function loadUrlWildcardBySourceUrl(string $sourceUrl): array + { + $query = $this->buildLoadUrlWildcardDataQuery(); + $expr = $query->expr(); + $query + ->where( + $expr->eq( + 'source_url', + $query->createPositionalParameter($sourceUrl) + ) + ); + + $result = $query->execute()->fetch(FetchMode::ASSOCIATIVE); + + return false !== $result ? $result : []; + } + + public function countAll(): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select($this->connection->getDatabasePlatform()->getCountExpression('id')) + ->from(self::URL_WILDCARD_TABLE); + + return (int) $query->execute()->fetchColumn(); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\Criterion $criterion + * + * @throws \Doctrine\DBAL\Driver\Exception + * @throws \Doctrine\DBAL\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + protected function doCount(Criterion $criterion): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select($this->connection->getDatabasePlatform()->getCountExpression('url_wildcard.id')) + ->from(self::URL_WILDCARD_TABLE, 'url_wildcard') + ->where($this->criteriaConverter->convertCriteria($query, $criterion)); + + return (int)$query->execute()->fetchOne(); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + private function getQuerySortingDirection(string $direction): string + { + if (!isset(self::SORT_DIRECTION_MAP[$direction])) { + throw new InvalidArgumentException( + '$sortClause->direction', + sprintf( + 'Unsupported "%s" sorting direction, use one of the SortClause::SORT_* constants instead', + $direction + ) + ); + } + + return self::SORT_DIRECTION_MAP[$direction]; + } + + private function trimUrl(string $url): string + { + return trim($url, '/'); + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..7c9a85fc78 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/ExceptionConversion.php @@ -0,0 +1,127 @@ +innerGateway = $innerGateway; + } + + public function insertUrlWildcard(UrlWildcard $urlWildcard): int + { + try { + return $this->innerGateway->insertUrlWildcard($urlWildcard); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateUrlWildcard( + int $id, + string $sourceUrl, + string $destinationUrl, + bool $forward + ): void { + try { + $this->innerGateway->updateUrlWildcard( + $id, + $sourceUrl, + $destinationUrl, + $forward + ); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteUrlWildcard(int $id): void + { + try { + $this->innerGateway->deleteUrlWildcard($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadUrlWildcardData(int $id): array + { + try { + return $this->innerGateway->loadUrlWildcardData($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadUrlWildcardsData(int $offset = 0, int $limit = -1): array + { + try { + return $this->innerGateway->loadUrlWildcardsData($offset, $limit); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function find( + Criterion $criterion, + int $offset, + int $limit, + array $sortClauses = [], + bool $doCount = true + ): array { + try { + return $this->innerGateway->find($criterion, $offset, $limit, $sortClauses, $doCount); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadUrlWildcardBySourceUrl(string $sourceUrl): array + { + try { + return $this->innerGateway->loadUrlWildcardBySourceUrl($sourceUrl); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countAll(): int + { + try { + return $this->innerGateway->countAll(); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/Content/UrlWildcard/Handler.php b/src/lib/Persistence/Legacy/Content/UrlWildcard/Handler.php new file mode 100644 index 0000000000..4b72da8672 --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/UrlWildcard/Handler.php @@ -0,0 +1,276 @@ +gateway = $gateway; + $this->mapper = $mapper; + } + + /** + * Creates a new url wildcard. + * + * @param string $sourceUrl + * @param string $destinationUrl + * @param bool $forward + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlWildcard + */ + public function create($sourceUrl, $destinationUrl, $forward = false) + { + $urlWildcard = $this->mapper->createUrlWildcard( + $sourceUrl, + $destinationUrl, + $forward + ); + + $urlWildcard->id = $this->gateway->insertUrlWildcard($urlWildcard); + + return $urlWildcard; + } + + public function update( + int $id, + string $sourceUrl, + string $destinationUrl, + bool $forward + ): UrlWildcard { + $this->gateway->updateUrlWildcard( + $id, + $sourceUrl, + $destinationUrl, + $forward + ); + + $spiUrlWildcard = $this->mapper->createUrlWildcard( + $sourceUrl, + $destinationUrl, + $forward + ); + + $spiUrlWildcard->id = $id; + + return $spiUrlWildcard; + } + + /** + * removes an url wildcard. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the url wild card was not found + * + * @param mixed $id + */ + public function remove($id) + { + $this->gateway->deleteUrlWildcard($id); + } + + /** + * Loads a url wild card. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the url wild card was not found + * + * @param mixed $id + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlWildcard + */ + public function load($id) + { + $row = $this->gateway->loadUrlWildcardData($id); + + if (empty($row)) { + throw new NotFoundException('UrlWildcard', $id); + } + + return $this->mapper->extractUrlWildcardFromRow($row); + } + + /** + * Loads all url wild card (paged). + * + * @param mixed $offset + * @param mixed $limit + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlWildcard[] + */ + public function loadAll($offset = 0, $limit = -1) + { + return $this->mapper->extractUrlWildcardsFromRows( + $this->gateway->loadUrlWildcardsData($offset, $limit) + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function find(URLWildcardQuery $query): array + { + $results = $this->gateway->find( + $query->filter, + $query->offset, + $query->limit, + $query->sortClauses, + $query->performCount + ); + + return [ + 'count' => $results['count'], + 'items' => $this->mapper->extractUrlWildcardsFromRows($results['rows']), + ]; + } + + /** + * Performs lookup for given URL. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the url wild card was not found + * + * @param string $sourceUrl + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlWildcard + */ + public function translate(string $sourceUrl): UrlWildcard + { + $row = $this->gateway->loadUrlWildcardBySourceUrl($sourceUrl); + + if (!empty($row)) { + return $this->mapper->extractUrlWildcardFromRow($row); + } + + // can't find UrlWildcard by simple lookup, continue and try to translate + + $rows = $this->gateway->loadUrlWildcardsData(); + uasort( + $rows, + static function ($row1, $row2) { + return strlen($row2['source_url']) - strlen($row1['source_url']); + } + ); + + foreach ($rows as $row) { + if ($uri = $this->match($sourceUrl, $row)) { + $row['destination_url'] = $uri; + + return $this->mapper->extractUrlWildcardFromRow($row); + } + } + + throw new NotFoundException('URLWildcard', $sourceUrl); + } + + /** + * Checks whether UrlWildcard with given source url exits. + * + * @param string $sourceUrl + * + * @return bool + */ + public function exactSourceUrlExists(string $sourceUrl): bool + { + $row = $this->gateway->loadUrlWildcardBySourceUrl($sourceUrl); + + return !empty($row); + } + + /** + * {@inheritDoc} + */ + public function countAll(): int + { + return $this->gateway->countAll(); + } + + /** + * Tests if the given url matches against the given url wildcard. + * + * if the wildcard matches on the given url this method will return a ready + * to use destination url, otherwise this method will return NULL. + * + * @param string $url + * @param array $wildcard + * + * @return string|null + */ + private function match(string $url, array $wildcard): ?string + { + if (preg_match($this->compile($wildcard['source_url']), $url, $match)) { + return $this->substitute($wildcard['destination_url'], $match); + } + + return null; + } + + /** + * Compiles the given url pattern into a regular expression. + * + * @param string $sourceUrl + * + * @return string + */ + private function compile(string $sourceUrl): string + { + return '(^' . str_replace('\\*', '(.*)', preg_quote($sourceUrl)) . '$)U'; + } + + /** + * Substitutes all placeholders ({\d}) in the given $destinationUrl with + * the values from the given $values array. + * + * @param string $destinationUrl + * @param array $values + * + * @return string + */ + private function substitute(string $destinationUrl, array $values): string + { + preg_match_all(self::PLACEHOLDERS_REGEXP, $destinationUrl, $matches); + + foreach ($matches[1] as $match) { + $destinationUrl = str_replace("{{$match}}", $values[$match], $destinationUrl); + } + + return $destinationUrl; + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Handler'); diff --git a/src/lib/Persistence/Legacy/Content/UrlWildcard/Mapper.php b/src/lib/Persistence/Legacy/Content/UrlWildcard/Mapper.php new file mode 100644 index 0000000000..81009c90bb --- /dev/null +++ b/src/lib/Persistence/Legacy/Content/UrlWildcard/Mapper.php @@ -0,0 +1,89 @@ +destinationUrl = $this->cleanUrl($destinationUrl); + $urlWildcard->sourceUrl = $this->cleanUrl($sourceUrl); + $urlWildcard->forward = $forward; + + return $urlWildcard; + } + + /** + * Extracts UrlWildcard object from given database $row. + * + * @param array $row + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlWildcard + */ + public function extractUrlWildcardFromRow(array $row) + { + $urlWildcard = new UrlWildcard(); + + $urlWildcard->id = (int)$row['id']; + $urlWildcard->destinationUrl = $this->cleanUrl($row['destination_url']); + $urlWildcard->sourceUrl = $this->cleanUrl($row['source_url']); + $urlWildcard->forward = (int)$row['type'] === 1; + + return $urlWildcard; + } + + /** + * @param string $url + * + * @return string + */ + protected function cleanUrl($url) + { + // if given $url is an absolute URL, then it's not necessary to prepend it with slash + if (null !== parse_url($url, PHP_URL_SCHEME)) { + return trim($url); + } + + return '/' . trim($url, '/ '); + } + + /** + * Extracts UrlWildcard objects from database $rows. + * + * @param array $rows + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlWildcard[] + */ + public function extractUrlWildcardsFromRows(array $rows) + { + $urlWildcards = []; + + foreach ($rows as $row) { + $urlWildcards[] = $this->extractUrlWildcardFromRow($row); + } + + return $urlWildcards; + } +} + +class_alias(Mapper::class, 'eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Mapper'); diff --git a/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriteriaConverter.php b/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriteriaConverter.php index 5b450a8133..e04bd25109 100644 --- a/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriteriaConverter.php +++ b/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriteriaConverter.php @@ -9,7 +9,7 @@ namespace Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query; use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\Criterion; final class CriteriaConverter @@ -26,7 +26,7 @@ public function __construct(iterable $handlers = []) } /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException if Criterion is not applicable to its target + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException if Criterion is not applicable to its target * * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|string */ diff --git a/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriterionHandler/LogicalAnd.php b/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriterionHandler/LogicalAnd.php index 48b5b45c5c..53989cc4f9 100644 --- a/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriterionHandler/LogicalAnd.php +++ b/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriterionHandler/LogicalAnd.php @@ -24,7 +24,7 @@ public function accept(Criterion $criterion): bool /** * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\Criterion\LogicalAnd $criterion * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException */ public function handle(CriteriaConverter $converter, QueryBuilder $queryBuilder, Criterion $criterion): CompositeExpression { diff --git a/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriterionHandler/LogicalNot.php b/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriterionHandler/LogicalNot.php index 76e65066d9..aef234c1e5 100644 --- a/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriterionHandler/LogicalNot.php +++ b/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriterionHandler/LogicalNot.php @@ -23,7 +23,7 @@ public function accept(Criterion $criterion): bool /** * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\Criterion\LogicalNot $criterion * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException */ public function handle( CriteriaConverter $converter, diff --git a/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriterionHandler/LogicalOr.php b/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriterionHandler/LogicalOr.php index a7932ff226..264763aa97 100644 --- a/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriterionHandler/LogicalOr.php +++ b/src/lib/Persistence/Legacy/Content/UrlWildcard/Query/CriterionHandler/LogicalOr.php @@ -24,7 +24,7 @@ public function accept(Criterion $criterion): bool /** * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\Criterion\LogicalOr $criterion * - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException */ public function handle( CriteriaConverter $converter, diff --git a/eZ/Publish/Core/Persistence/Legacy/Exception/GroupNotEmpty.php b/src/lib/Persistence/Legacy/Exception/GroupNotEmpty.php similarity index 75% rename from eZ/Publish/Core/Persistence/Legacy/Exception/GroupNotEmpty.php rename to src/lib/Persistence/Legacy/Exception/GroupNotEmpty.php index b371116a1e..a494be98cd 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Exception/GroupNotEmpty.php +++ b/src/lib/Persistence/Legacy/Exception/GroupNotEmpty.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Exception; +namespace Ibexa\Core\Persistence\Legacy\Exception; -use eZ\Publish\Core\Base\Exceptions\BadStateException; +use Ibexa\Core\Base\Exceptions\BadStateException; /** * Exception thrown if a Content\Type\Group is to be deleted which is not @@ -27,3 +27,5 @@ public function __construct($groupId) ); } } + +class_alias(GroupNotEmpty::class, 'eZ\Publish\Core\Persistence\Legacy\Exception\GroupNotEmpty'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Exception/RemoveLastGroupFromType.php b/src/lib/Persistence/Legacy/Exception/RemoveLastGroupFromType.php similarity index 77% rename from eZ/Publish/Core/Persistence/Legacy/Exception/RemoveLastGroupFromType.php rename to src/lib/Persistence/Legacy/Exception/RemoveLastGroupFromType.php index 59de3b396d..2b7c914deb 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Exception/RemoveLastGroupFromType.php +++ b/src/lib/Persistence/Legacy/Exception/RemoveLastGroupFromType.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Exception; +namespace Ibexa\Core\Persistence\Legacy\Exception; -use eZ\Publish\Core\Base\Exceptions\BadStateException; +use Ibexa\Core\Base\Exceptions\BadStateException; /** * Exception thrown when a Type is to be unlinked from its last Group. @@ -31,3 +31,5 @@ public function __construct($typeId, $status) ); } } + +class_alias(RemoveLastGroupFromType::class, 'eZ\Publish\Core\Persistence\Legacy\Exception\RemoveLastGroupFromType'); diff --git a/src/lib/Persistence/Legacy/Exception/RoleNotFound.php b/src/lib/Persistence/Legacy/Exception/RoleNotFound.php new file mode 100644 index 0000000000..35373f0e8e --- /dev/null +++ b/src/lib/Persistence/Legacy/Exception/RoleNotFound.php @@ -0,0 +1,31 @@ +expr()->in( + 'content.id', + $queryBuilder->createNamedParameter( + array_map('intval', $criterion->value), + Connection::PARAM_INT_ARRAY + ) + ); + } +} + +class_alias(ContentIdQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\ContentIdQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/DateMetadataQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/DateMetadataQueryBuilder.php new file mode 100644 index 0000000000..b381b42801 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/DateMetadataQueryBuilder.php @@ -0,0 +1,47 @@ +target === DateMetadata::MODIFIED ? 'modified' : 'published'; + $column = "content.{$column}"; + + $value = (array)$criterion->value; + + return $queryBuilder->buildOperatorBasedCriterionConstraint( + $column, + $value, + $criterion->operator + ); + } +} + +class_alias(DateMetadataQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\DateMetadataQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/IsContainerQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/IsContainerQueryBuilder.php new file mode 100644 index 0000000000..fc4161230f --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/IsContainerQueryBuilder.php @@ -0,0 +1,52 @@ +joinOnce( + 'content', + Gateway::CONTENT_TYPE_TABLE, + 'content_type', + 'content.contentclass_id = content_type.id', + ); + + /** @var array{bool} $criterionValue */ + $criterionValue = $criterion->value; + $isContainer = reset($criterionValue); + + return $queryBuilder->expr()->in( + 'content_type.is_container', + $queryBuilder->createNamedParameter((int)$isContainer, ParameterType::INTEGER) + ); + } +} diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/LanguageCodeQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/LanguageCodeQueryBuilder.php new file mode 100644 index 0000000000..11cb4dbb0f --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/LanguageCodeQueryBuilder.php @@ -0,0 +1,63 @@ +joinOnce( + 'version', + Gateway::CONTENT_LANGUAGE_TABLE, + 'language', + // bitwise and for exact language ID match + 'language.id & version.language_mask = language.id' + ); + + // at this point $criterion->value is guaranteed to be an array + $expr = $queryBuilder->expr()->in( + 'language.locale', + $queryBuilder->createNamedParameter( + $criterion->value, + Connection::PARAM_STR_ARRAY + ) + ); + + if ($criterion->matchAlwaysAvailable) { + $expr = (string)$queryBuilder->expr()->orX($expr, 'version.language_mask & 1 = 1'); + } + + return $expr; + } +} + +class_alias(LanguageCodeQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\LanguageCodeQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ObjectStateIdQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ObjectStateIdQueryBuilder.php new file mode 100644 index 0000000000..6470af88bd --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ObjectStateIdQueryBuilder.php @@ -0,0 +1,52 @@ +join( + 'content', + Gateway::OBJECT_STATE_LINK_TABLE, + $tableAlias, + 'content.id = ' . $tableAlias . '.contentobject_id', + ); + + $value = (array)$criterion->value; + + return $queryBuilder->expr()->in( + $tableAlias . '.contentobject_state_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ); + } +} + +class_alias(ObjectStateIdQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\ObjectStateIdQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ObjectStateIdentifierQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ObjectStateIdentifierQueryBuilder.php new file mode 100644 index 0000000000..3354012191 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/ObjectStateIdentifierQueryBuilder.php @@ -0,0 +1,62 @@ +joinOnce( + 'content', + ObjectStateGateway::OBJECT_STATE_LINK_TABLE, + 'object_state_link', + 'content.id = object_state_link.contentobject_id', + ) + ->joinOnce( + 'content', + ObjectStateGateway::OBJECT_STATE_TABLE, + 'object_state', + 'object_state_link.contentobject_state_id = object_state.id' + ) + ->joinOnce( + 'object_state', + ObjectStateGateway::OBJECT_STATE_GROUP_TABLE, + 'object_state_group', + 'object_state.group_id = object_state_group.id' + ); + + $value = (array)$criterion->value; + + return $queryBuilder->expr()->in( + 'object_state.identifier', + $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) + ); + } +} + +class_alias(ObjectStateIdentifierQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\ObjectStateIdentifierQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/RemoteIdQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/RemoteIdQueryBuilder.php new file mode 100644 index 0000000000..65411fb086 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/RemoteIdQueryBuilder.php @@ -0,0 +1,42 @@ +expr()->in( + 'content.remote_id', + $queryBuilder->createNamedParameter( + $criterion->value, + Connection::PARAM_STR_ARRAY + ) + ); + } +} + +class_alias(RemoteIdQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\RemoteIdQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Section/IdQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Section/IdQueryBuilder.php new file mode 100644 index 0000000000..f8d4ab4a2c --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Section/IdQueryBuilder.php @@ -0,0 +1,44 @@ +expr()->in( + 'content.section_id', + $queryBuilder->createNamedParameter( + array_map('intval', $criterion->value), + Connection::PARAM_INT_ARRAY + ) + ); + } +} + +class_alias(IdQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Section\IdQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Section/IdentifierQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Section/IdentifierQueryBuilder.php new file mode 100644 index 0000000000..4200aae6da --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Section/IdentifierQueryBuilder.php @@ -0,0 +1,53 @@ +joinOnce( + 'content', + Gateway::CONTENT_SECTION_TABLE, + 'section', + 'content.section_id = section.id' + ); + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\SectionIdentifier $criterion */ + return $queryBuilder->expr()->in( + 'section.identifier', + $queryBuilder->createNamedParameter( + (array)$criterion->value, + Connection::PARAM_STR_ARRAY + ) + ); + } +} + +class_alias(IdentifierQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Section\IdentifierQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/SiblingQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/SiblingQueryBuilder.php new file mode 100644 index 0000000000..96add0ae2d --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/SiblingQueryBuilder.php @@ -0,0 +1,50 @@ +logicalAndQueryBuilder = $logicalAndQueryBuilder; + } + + public function accepts(FilteringCriterion $criterion): bool + { + return $criterion instanceof Sibling; + } + + public function buildQueryConstraint( + FilteringQueryBuilder $queryBuilder, + FilteringCriterion $criterion + ): ?string { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Sibling $criterion */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalAnd $_criterion */ + $_criterion = $criterion->criteria; + + return $this->logicalAndQueryBuilder->buildQueryConstraint($queryBuilder, $_criterion); + } +} + +class_alias(SiblingQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\SiblingQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/BaseQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/BaseQueryBuilder.php new file mode 100644 index 0000000000..0caa05a034 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/BaseQueryBuilder.php @@ -0,0 +1,40 @@ +joinOnce( + 'content', + ContentTypeGateway::CONTENT_TYPE_TABLE, + 'content_type', + 'content.contentclass_id = content_type.id AND content_type.version = 0' + ); + + // the returned query constraint depends on concrete implementations + return null; + } +} + +class_alias(BaseQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\BaseQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/GroupIdQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/GroupIdQueryBuilder.php new file mode 100644 index 0000000000..e447563a1b --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/GroupIdQueryBuilder.php @@ -0,0 +1,62 @@ +joinOnce( + 'content', + ContentTypeGateway::CONTENT_TYPE_TO_GROUP_ASSIGNMENT_TABLE, + 'content_type_group_assignment', + 'content.contentclass_id = content_type_group_assignment.contentclass_id' + ); + + $queryBuilder + ->joinOnce( + 'content_type_group_assignment', + ContentTypeGateway::CONTENT_TYPE_GROUP_TABLE, + 'content_type_group', + 'content_type_group_assignment.group_id = content_type_group.id' + ); + + return $queryBuilder->expr()->in( + 'content_type_group.id', + $queryBuilder->createNamedParameter( + $criterion->value, + Connection::PARAM_INT_ARRAY + ) + ); + } +} + +class_alias(GroupIdQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\GroupIdQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/IdQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/IdQueryBuilder.php new file mode 100644 index 0000000000..c90fcb0082 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/IdQueryBuilder.php @@ -0,0 +1,47 @@ +expr()->in( + 'content_type.id', + $queryBuilder->createNamedParameter( + $criterion->value, + Connection::PARAM_INT_ARRAY + ) + ); + } +} + +class_alias(IdQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\IdQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/IdentifierQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/IdentifierQueryBuilder.php new file mode 100644 index 0000000000..3f5349e4b8 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/IdentifierQueryBuilder.php @@ -0,0 +1,47 @@ +expr()->in( + 'content_type.identifier', + $queryBuilder->createNamedParameter( + $criterion->value, + Connection::PARAM_STR_ARRAY + ) + ); + } +} + +class_alias(IdentifierQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\IdentifierQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilder.php new file mode 100644 index 0000000000..cf8d4ae2c6 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilder.php @@ -0,0 +1,62 @@ +value e.g. = ['/1/2/', '/1/4/10/'] + $locationIDs = array_merge( + ...array_map( + static function (string $pathString) { + return array_map( + 'intval', + array_filter(explode('/', trim($pathString, '/'))) + ); + }, + $criterion->value + ) + ); + + return $queryBuilder->expr()->in( + 'location.node_id', + $queryBuilder->createNamedParameter( + array_values(array_unique($locationIDs)), + Connection::PARAM_INT_ARRAY + ) + ); + } +} + +class_alias(AncestorQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\AncestorQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/BaseLocationCriterionQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/BaseLocationCriterionQueryBuilder.php new file mode 100644 index 0000000000..b5189a4792 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/BaseLocationCriterionQueryBuilder.php @@ -0,0 +1,30 @@ +joinAllLocations(); + + return null; + } +} + +class_alias(BaseLocationCriterionQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\BaseLocationCriterionQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/DepthQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/DepthQueryBuilder.php new file mode 100644 index 0000000000..d02e78ad7b --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/DepthQueryBuilder.php @@ -0,0 +1,43 @@ +buildOperatorBasedCriterionConstraint( + 'location.depth', + $criterion->value, + $criterion->operator + ); + } +} + +class_alias(DepthQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\DepthQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/IdQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/IdQueryBuilder.php new file mode 100644 index 0000000000..1a92f6c249 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/IdQueryBuilder.php @@ -0,0 +1,43 @@ +expr()->in( + 'location.node_id', + $queryBuilder->createNamedParameter( + $criterion->value, + Connection::PARAM_INT_ARRAY + ) + ); + } +} + +class_alias(IdQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\IdQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/IsMainLocationQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/IsMainLocationQueryBuilder.php new file mode 100644 index 0000000000..99515d840a --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/IsMainLocationQueryBuilder.php @@ -0,0 +1,38 @@ +value[0] === Location\IsMainLocation::MAIN + ? 'location.node_id = location.main_node_id' + : 'location.node_id <> location.main_node_id'; + } +} + +class_alias(IsMainLocationQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\IsMainLocationQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/ParentLocationIdQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/ParentLocationIdQueryBuilder.php new file mode 100644 index 0000000000..aae12b33a4 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/ParentLocationIdQueryBuilder.php @@ -0,0 +1,43 @@ +expr()->in( + 'location.parent_node_id', + $queryBuilder->createNamedParameter( + $criterion->value, + Connection::PARAM_INT_ARRAY + ) + ); + } +} + +class_alias(ParentLocationIdQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\ParentLocationIdQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/PriorityQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/PriorityQueryBuilder.php new file mode 100644 index 0000000000..d36d310cc3 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/PriorityQueryBuilder.php @@ -0,0 +1,43 @@ +buildOperatorBasedCriterionConstraint( + 'location.priority', + $criterion->value, + $criterion->operator + ); + } +} + +class_alias(PriorityQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\PriorityQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/RemoteIdQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/RemoteIdQueryBuilder.php new file mode 100644 index 0000000000..a84bf4a6fb --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/RemoteIdQueryBuilder.php @@ -0,0 +1,43 @@ +expr()->in( + 'location.remote_id', + $queryBuilder->createNamedParameter( + $criterion->value, + Connection::PARAM_STR_ARRAY + ) + ); + } +} + +class_alias(RemoteIdQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\RemoteIdQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/SubtreeQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/SubtreeQueryBuilder.php new file mode 100644 index 0000000000..7c51fbeb87 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/SubtreeQueryBuilder.php @@ -0,0 +1,49 @@ +expr(); + $statements = array_map( + static function (string $pathString) use ($queryBuilder, $expressionBuilder): string { + return $expressionBuilder->like( + 'location.path_string', + $queryBuilder->createNamedParameter($pathString . '%', ParameterType::STRING) + ); + }, + $criterion->value + ); + + return (string)$expressionBuilder->orX(...$statements); + } +} + +class_alias(SubtreeQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\SubtreeQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/VisibilityQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/VisibilityQueryBuilder.php new file mode 100644 index 0000000000..ccbad37acc --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/VisibilityQueryBuilder.php @@ -0,0 +1,64 @@ +expr(); + $columnsExpressions = $this->getVisibilityColumnsExpressions( + $queryBuilder, + $criterion->value[0] + ); + + return $criterion->value[0] === Visibility::VISIBLE + ? (string)$expressionBuilder->andX(...$columnsExpressions) + : (string)$expressionBuilder->orX(...$columnsExpressions); + } + + private function getVisibilityColumnsExpressions( + QueryBuilder $queryBuilder, + int $visibleFlag + ): array { + $expressionBuilder = $queryBuilder->expr(); + + return [ + $expressionBuilder->eq( + 'location.is_hidden', + $queryBuilder->createNamedParameter($visibleFlag, ParameterType::INTEGER) + ), + $expressionBuilder->eq( + 'location.is_invisible', + $queryBuilder->createNamedParameter($visibleFlag, ParameterType::INTEGER) + ), + ]; + } +} + +class_alias(VisibilityQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\VisibilityQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalAndQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalAndQueryBuilder.php new file mode 100644 index 0000000000..d463e37fae --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalAndQueryBuilder.php @@ -0,0 +1,57 @@ +criterionVisitor = $criterionVisitor; + } + + public function accepts(FilteringCriterion $criterion): bool + { + return $criterion instanceof LogicalAnd; + } + + public function buildQueryConstraint( + FilteringQueryBuilder $queryBuilder, + FilteringCriterion $criterion + ): ?string { + $constraints = []; + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalAnd $criterion */ + foreach ($criterion->criteria as $_criterion) { + /** @var \Ibexa\Contracts\Core\Repository\Values\Filter\FilteringCriterion $_criterion */ + $constraint = $this->criterionVisitor->visitCriteria($queryBuilder, $_criterion); + if (null !== $constraint) { + $constraints[] = $constraint; + } + } + + if (empty($constraints)) { + return null; + } + + return (string)$queryBuilder->expr()->andX(...$constraints); + } +} + +class_alias(LogicalAndQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalAndQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalNotQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalNotQueryBuilder.php new file mode 100644 index 0000000000..84a3e5e828 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalNotQueryBuilder.php @@ -0,0 +1,63 @@ +criterionVisitor = $criterionVisitor; + } + + public function accepts(FilteringCriterion $criterion): bool + { + return $criterion instanceof LogicalNot; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function buildQueryConstraint( + FilteringQueryBuilder $queryBuilder, + FilteringCriterion $criterion + ): ?string { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalNot $criterion */ + if (!$criterion->criteria[0] instanceof FilteringCriterion) { + throw new InvalidArgumentException( + '$criterion', + sprintf( + 'Criterion needs to be a Filtering Criterion, got "%s"', + get_class($criterion->criteria[0]) + ) + ); + } + + $constraint = $this->criterionVisitor->visitCriteria( + $queryBuilder, + $criterion->criteria[0] + ); + + return null !== $constraint ? sprintf('NOT (%s)', $constraint) : null; + } +} + +class_alias(LogicalNotQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalNotQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalOrQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalOrQueryBuilder.php new file mode 100644 index 0000000000..a3cad98a2f --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalOrQueryBuilder.php @@ -0,0 +1,57 @@ +criterionVisitor = $criterionVisitor; + } + + public function accepts(FilteringCriterion $criterion): bool + { + return $criterion instanceof LogicalOr; + } + + public function buildQueryConstraint( + FilteringQueryBuilder $queryBuilder, + FilteringCriterion $criterion + ): ?string { + $constraints = []; + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalOr $criterion */ + foreach ($criterion->criteria as $_criterion) { + /** @var \Ibexa\Contracts\Core\Repository\Values\Filter\FilteringCriterion $_criterion */ + $constraint = $this->criterionVisitor->visitCriteria($queryBuilder, $_criterion); + if (null !== $constraint) { + $constraints[] = $constraint; + } + } + + if (empty($constraints)) { + return null; + } + + return (string)$queryBuilder->expr()->orX(...$constraints); + } +} + +class_alias(LogicalOrQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalOrQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/MatchAllQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/MatchAllQueryBuilder.php new file mode 100644 index 0000000000..96619c9b25 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/MatchAllQueryBuilder.php @@ -0,0 +1,34 @@ +transformationProcessor = $transformationProcessor; + } + + public function buildQueryConstraint( + FilteringQueryBuilder $queryBuilder, + FilteringCriterion $criterion + ): ?string { + $queryBuilder + ->joinOnce( + 'content', + DoctrineStorage::USER_TABLE, + 'user_storage', + 'content.id = user_storage.contentobject_id' + ); + + return null; + } + + protected function transformCriterionValueForLikeExpression(string $value): string + { + return str_replace( + '*', + '%', + addcslashes( + $this->transformationProcessor->transformByGroup( + $value, + 'lowercase' + ), + '%_' + ) + ); + } +} + +class_alias(BaseUserCriterionQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User\BaseUserCriterionQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserBasedQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserBasedQueryBuilder.php new file mode 100644 index 0000000000..4b97ef5281 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserBasedQueryBuilder.php @@ -0,0 +1,51 @@ +leftJoinOnce( + 'content', + 'ezuser', + 'user_storage', + 'content.id = user_storage.contentobject_id' + ); + + $isUserBased = (bool)reset($criterion->value); + $databasePlatform = $queryBuilder->getConnection()->getDatabasePlatform(); + + return $isUserBased + ? $databasePlatform->getIsNotNullExpression('user_storage.contentobject_id') + : $databasePlatform->getIsNullExpression('user_storage.contentobject_id'); + } +} + +class_alias(IsUserBasedQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User\IsUserBasedQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserEnabledQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserEnabledQueryBuilder.php new file mode 100644 index 0000000000..a5028cdf44 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/IsUserEnabledQueryBuilder.php @@ -0,0 +1,51 @@ +joinOnce( + 'user_storage', + DoctrineStorage::USER_SETTING_TABLE, + 'user_settings', + 'user_storage.contentobject_id = user_settings.user_id' + ); + + return $queryBuilder->expr()->eq( + 'user_settings.is_enabled', + $queryBuilder->createNamedParameter( + (int)reset($criterion->value), + ParameterType::INTEGER + ) + ); + } +} + +class_alias(IsUserEnabledQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User\IsUserEnabledQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/GroupQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/GroupQueryBuilder.php new file mode 100644 index 0000000000..85031e4c40 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/GroupQueryBuilder.php @@ -0,0 +1,56 @@ +target === UserMetadata::GROUP; + } + + public function buildQueryConstraint( + FilteringQueryBuilder $queryBuilder, + FilteringCriterion $criterion + ): ?string { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\UserMetadata $criterion */ + $value = (array)$criterion->value; + + $queryBuilder + ->joinOnce( + 'content', + LocationGateway::CONTENT_TREE_TABLE, + 'user_location', + 'content.owner_id = user_location.contentobject_id' + ) + ->joinOnce( + 'user_location', + LocationGateway::CONTENT_TREE_TABLE, + 'user_group_location', + 'user_location.parent_node_id = user_group_location.node_id' + ); + + return $queryBuilder->expr()->in( + 'user_group_location.contentobject_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ); + } +} + +class_alias(GroupQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User\Metadata\GroupQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/ModifierQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/ModifierQueryBuilder.php new file mode 100644 index 0000000000..df895163a3 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/ModifierQueryBuilder.php @@ -0,0 +1,41 @@ +target === UserMetadata::MODIFIER; + } + + public function buildQueryConstraint( + FilteringQueryBuilder $queryBuilder, + FilteringCriterion $criterion + ): ?string { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\UserMetadata $criterion */ + $value = (array)$criterion->value; + + return $queryBuilder->expr()->in( + 'version.creator_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ); + } +} + +class_alias(ModifierQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User\Metadata\ModifierQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/OwnerQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/OwnerQueryBuilder.php new file mode 100644 index 0000000000..5783d27ce1 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/Metadata/OwnerQueryBuilder.php @@ -0,0 +1,41 @@ +target === UserMetadata::OWNER; + } + + public function buildQueryConstraint( + FilteringQueryBuilder $queryBuilder, + FilteringCriterion $criterion + ): ?string { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\UserMetadata $criterion */ + $value = (array)$criterion->value; + + return $queryBuilder->expr()->in( + 'content.owner_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ); + } +} + +class_alias(OwnerQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User\Metadata\OwnerQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserEmailQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserEmailQueryBuilder.php new file mode 100644 index 0000000000..fa482912df --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserEmailQueryBuilder.php @@ -0,0 +1,53 @@ +operator) { + $expression = $queryBuilder->expr()->like( + 'user_storage.email', + $queryBuilder->createNamedParameter( + $this->transformCriterionValueForLikeExpression($criterion->value) + ) + ); + } else { + $value = (array)$criterion->value; + $expression = $queryBuilder->expr()->in( + 'user_storage.email', + $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) + ); + } + + return $expression; + } +} + +class_alias(UserEmailQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User\UserEmailQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserIdQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserIdQueryBuilder.php new file mode 100644 index 0000000000..13e6724633 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserIdQueryBuilder.php @@ -0,0 +1,42 @@ +value; + + return $queryBuilder->expr()->in( + 'user_storage.contentobject_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ); + } +} + +class_alias(UserIdQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User\UserIdQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserLoginQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserLoginQueryBuilder.php new file mode 100644 index 0000000000..c6dae4e71d --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/User/UserLoginQueryBuilder.php @@ -0,0 +1,53 @@ +expr(); + if (Operator::LIKE === $criterion->operator) { + return $expr->like( + 'user_storage.login', + $queryBuilder->createNamedParameter( + $this->transformCriterionValueForLikeExpression($criterion->value) + ) + ); + } + + $value = (array)$criterion->value; + + return $expr->in( + 'user_storage.login', + $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) + ); + } +} + +class_alias(UserLoginQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\User\UserLoginQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/CriterionVisitor.php b/src/lib/Persistence/Legacy/Filter/CriterionVisitor.php new file mode 100644 index 0000000000..61cfd8d638 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/CriterionVisitor.php @@ -0,0 +1,60 @@ +setCriterionQueryBuilders($criterionQueryBuilders); + } + + public function setCriterionQueryBuilders(iterable $criterionQueryBuilders): void + { + $this->criterionQueryBuilders = $criterionQueryBuilders; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException if there's no builder for a criterion + */ + public function visitCriteria( + FilteringQueryBuilder $queryBuilder, + FilteringCriterion $criterion + ): string { + foreach ($this->criterionQueryBuilders as $criterionQueryBuilder) { + if ($criterionQueryBuilder->accepts($criterion)) { + return $criterionQueryBuilder->buildQueryConstraint( + $queryBuilder, + $criterion + ); + } + } + + throw new NotImplementedException( + sprintf( + 'There is no Filtering Criterion Query Builder for %s Criterion', + get_class($criterion) + ) + ); + } +} + +class_alias(CriterionVisitor::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\CriterionVisitor'); diff --git a/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php new file mode 100644 index 0000000000..18a2424afc --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/Gateway/Content/Doctrine/DoctrineGateway.php @@ -0,0 +1,294 @@ + 'content.id', + 'content_type_id' => 'content.contentclass_id', + 'content_current_version' => 'content.current_version', + 'content_initial_language_id' => 'content.initial_language_id', + 'content_language_mask' => 'content.language_mask', + 'content_modified' => 'content.modified', + 'content_name' => 'content.name', + 'content_owner_id' => 'content.owner_id', + 'content_published' => 'content.published', + 'content_remote_id' => 'content.remote_id', + 'content_section_id' => 'content.section_id', + 'content_status' => 'content.status', + 'content_is_hidden' => 'content.is_hidden', + // Version Info + 'content_version_id' => 'version.id', + 'content_version_no' => 'version.version', + 'content_version_creator_id' => 'version.creator_id', + 'content_version_created' => 'version.created', + 'content_version_modified' => 'version.modified', + 'content_version_status' => 'version.status', + 'content_version_language_mask' => 'version.language_mask', + 'content_version_initial_language_id' => 'version.initial_language_id', + // Main Location (nullable) + 'content_main_location_id' => 'main_location.main_node_id', + ]; + + /** @var \Doctrine\DBAL\Connection */ + private $connection; + + /** @var \Ibexa\Contracts\Core\Persistence\Filter\CriterionVisitor */ + private $criterionVisitor; + + /** @var \Ibexa\Contracts\Core\Persistence\Filter\SortClauseVisitor */ + private $sortClauseVisitor; + + public function __construct( + Connection $connection, + CriterionVisitor $criterionVisitor, + SortClauseVisitor $sortClauseVisitor + ) { + $this->connection = $connection; + $this->criterionVisitor = $criterionVisitor; + $this->sortClauseVisitor = $sortClauseVisitor; + } + + private function getDatabasePlatform(): AbstractPlatform + { + try { + return $this->connection->getDatabasePlatform(); + } catch (DBALException $e) { + throw DatabaseException::wrap($e); + } + } + + public function count(FilteringCriterion $criterion): int + { + $query = $this->buildQuery( + [$this->getDatabasePlatform()->getCountExpression('DISTINCT content.id')], + $criterion + ); + + return (int)$query->execute()->fetch(FetchMode::COLUMN); + } + + public function find( + FilteringCriterion $criterion, + array $sortClauses, + int $limit, + int $offset + ): iterable { + $query = $this->buildQuery(iterator_to_array($this->getColumns()), $criterion); + $this->sortClauseVisitor->visitSortClauses($query, $sortClauses); + + // get additional data for the same query constraints + $names = $this->bulkFetchVersionNames(clone $query); + $fieldValues = $this->bulkFetchFieldValues(clone $query); + + // wrap query to avoid duplicate entries for multiple Locations + $wrappedQuery = $this->wrapMainQuery($query); + $wrappedQuery->setFirstResult($offset); + if ($limit > 0) { + $wrappedQuery->setMaxResults($limit); + } + + $resultStatement = $wrappedQuery->execute(); + while (false !== ($row = $resultStatement->fetch(FetchMode::ASSOCIATIVE))) { + $contentId = (int)$row['content_id']; + $versionNo = (int)$row['content_version_no']; + $row['content_version_names'] = $this->extractVersionNames( + $names, + $contentId, + $versionNo + ); + $row['content_version_fields'] = $this->extractFieldValues( + $fieldValues, + $contentId, + $versionNo + ); + + yield $row; + } + } + + private function buildQuery( + array $columns, + FilteringCriterion $criterion + ): FilteringQueryBuilder { + $queryBuilder = new FilteringQueryBuilder($this->connection); + + $expressionBuilder = $queryBuilder->expr(); + $queryBuilder + ->select($columns) + ->distinct() + ->from(ContentGateway::CONTENT_ITEM_TABLE, 'content') + ->joinPublishedVersion() + ->leftJoin( + 'content', + LocationGateway::CONTENT_TREE_TABLE, + 'main_location', + $expressionBuilder->andX( + 'content.id = main_location.contentobject_id', + 'main_location.main_node_id = main_location.node_id' + ) + ); + + $constraint = $this->criterionVisitor->visitCriteria($queryBuilder, $criterion); + if (null !== $constraint) { + $queryBuilder->where($constraint); + } + + return $queryBuilder; + } + + /** + * Return names as a map of '' => ''. + * + * Process data fetched by {@see bulkFetchVersionNames} + */ + private function extractVersionNames(array $names, int $contentId, int $versionNo): array + { + $rawVersionNames = $this->extractVersionData($names, $contentId, $versionNo); + + $names = []; + foreach ($rawVersionNames as $nameRow) { + $names[$nameRow['real_translation']] = $nameRow['name']; + } + + return $names; + } + + private function extractFieldValues(array $fieldValues, int $contentId, int $versionNo): array + { + return $this->extractVersionData($fieldValues, $contentId, $versionNo); + } + + /** + * Extract Version-specific data from bulk-loaded rows. + */ + private function extractVersionData(array $rows, int $contentId, int $versionNo): array + { + return array_filter( + $rows, + static function (array $row) use ($contentId, $versionNo) { + return (int)$row['content_id'] === $contentId + && (int)$row['version_no'] === $versionNo; + } + ); + } + + private function bulkFetchVersionNames(FilteringQueryBuilder $query): array + { + $query + // completely reset SELECT part to get only needed data + ->select( + 'content.id AS content_id', + 'version.version AS version_no', + 'content_name.name', + 'content_name.real_translation' + ) + ->distinct() + // join names table to pre-existing query + ->joinOnce( + 'content', + ContentGateway::CONTENT_NAME_TABLE, + 'content_name', + (string)$query->expr()->andX( + 'content.id = content_name.contentobject_id', + 'version.version = content_name.content_version', + 'version.language_mask & content_name.language_id > 0' + ) + ) + // reset not needed parts, keeping FROM, other JOINs, and WHERE constraints + ->setMaxResults(null) + ->setFirstResult(null) + ->resetQueryPart('orderBy'); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + private function bulkFetchFieldValues(FilteringQueryBuilder $query): array + { + $query + // completely reset SELECT part to get only needed data + ->select( + 'content_field.contentobject_id AS content_id', + 'content_field.version AS version_no', + 'content_field.id AS field_id', + 'content_field.contentclassattribute_id AS field_definition_id', + 'content_field.data_type_string AS field_type', + 'content_field.language_code AS field_language_code', + 'content_field.data_float AS field_data_float', + 'content_field.data_int AS field_data_int', + 'content_field.data_text AS field_data_text', + 'content_field.sort_key_int AS field_sort_key_int', + 'content_field.sort_key_string AS field_sort_key_string' + ) + ->distinct() + ->joinOnce( + 'content', + ContentGateway::CONTENT_FIELD_TABLE, + 'content_field', + (string)$query->expr()->andX( + 'content.id = content_field.contentobject_id', + 'version.version = content_field.version', + 'version.language_mask & content_field.language_id = content_field.language_id' + ) + ) + // reset not needed parts, keeping FROM, other JOINs, and WHERE constraints + ->setMaxResults(null) + ->setFirstResult(null) + ->resetQueryPart('orderBy'); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + private function getColumns(): Traversable + { + foreach (self::COLUMN_MAP as $columnAlias => $columnName) { + yield "{$columnName} AS {$columnAlias}"; + } + } + + /** + * Wrap query to avoid duplicate entries for multiple Locations. + */ + private function wrapMainQuery(FilteringQueryBuilder $query): QueryBuilder + { + $wrappedQuery = $this->connection->createQueryBuilder(); + $wrappedQuery + ->select(array_keys(self::COLUMN_MAP)) + ->distinct() + ->from(sprintf('(%s)', $query->getSQL()), 'wrapped') + ->setParameters($query->getParameters(), $query->getParameterTypes()); + + return $wrappedQuery; + } +} + +class_alias(DoctrineGateway::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Content\Doctrine\DoctrineGateway'); diff --git a/src/lib/Persistence/Legacy/Filter/Gateway/Content/GatewayDataMapper.php b/src/lib/Persistence/Legacy/Filter/Gateway/Content/GatewayDataMapper.php new file mode 100644 index 0000000000..f65196e3cf --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/Gateway/Content/GatewayDataMapper.php @@ -0,0 +1,27 @@ +connection = $connection; + $this->criterionVisitor = $criterionVisitor; + $this->sortClauseVisitor = $sortClauseVisitor; + } + + private function getDatabasePlatform(): AbstractPlatform + { + try { + return $this->connection->getDatabasePlatform(); + } catch (DBALException $e) { + throw DatabaseException::wrap($e); + } + } + + public function count(FilteringCriterion $criterion): int + { + $query = $this->buildQuery($criterion); + + $query->select($this->getDatabasePlatform()->getCountExpression('DISTINCT location.node_id')); + + return (int)$query->execute()->fetch(FetchMode::COLUMN); + } + + public function find( + FilteringCriterion $criterion, + array $sortClauses, + int $limit, + int $offset + ): iterable { + $query = $this->buildQuery($criterion); + $this->sortClauseVisitor->visitSortClauses($query, $sortClauses); + + $query->setFirstResult($offset); + if ($limit > 0) { + $query->setMaxResults($limit); + } + + $resultStatement = $query->execute(); + + while (false !== ($row = $resultStatement->fetch(FetchMode::ASSOCIATIVE))) { + yield $row; + } + } + + private function buildQuery(FilteringCriterion $criterion): FilteringQueryBuilder + { + $queryBuilder = new FilteringQueryBuilder($this->connection); + $queryBuilder + ->select( + [ + // Location + 'location.node_id AS location_node_id', + 'location.priority AS location_priority', + 'location.is_hidden AS location_is_hidden', + 'location.is_invisible AS location_is_invisible', + 'location.remote_id AS location_remote_id', + 'location.contentobject_id AS location_contentobject_id', + 'location.parent_node_id AS location_parent_node_id', + 'location.path_identification_string AS location_path_identification_string', + 'location.path_string AS location_path_string', + 'location.depth AS location_depth', + 'location.sort_field AS location_sort_field', + 'location.sort_order AS location_sort_order', + // Main Location (nullable) + 'location.main_node_id AS content_main_location_id', + // Content Info + 'content.id AS content_id', + 'content.contentclass_id AS content_type_id', + 'content.current_version AS content_current_version', + 'content.initial_language_id AS content_initial_language_id', + 'content.language_mask AS content_language_mask', + 'content.modified AS content_modified', + 'content.name AS content_name', + 'content.owner_id AS content_owner_id', + 'content.published AS content_published', + 'content.remote_id AS content_remote_id', + 'content.section_id AS content_section_id', + 'content.status AS content_status', + 'content.is_hidden AS content_is_hidden', + ] + ) + ->distinct() + ->from(LocationGateway::CONTENT_TREE_TABLE, 'location') + ->join( + 'location', + ContentGateway::CONTENT_ITEM_TABLE, + 'content', + 'content.id = location.contentobject_id' + ) + ->joinPublishedVersion() + ; + + $constraint = $this->criterionVisitor->visitCriteria($queryBuilder, $criterion); + if (null !== $constraint) { + $queryBuilder->where($constraint); + } + + return $queryBuilder; + } +} + +class_alias(DoctrineGateway::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\Gateway\Location\Doctrine\DoctrineGateway'); diff --git a/src/lib/Persistence/Legacy/Filter/Handler/ContentFilteringHandler.php b/src/lib/Persistence/Legacy/Filter/Handler/ContentFilteringHandler.php new file mode 100644 index 0000000000..7676b18a1c --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/Handler/ContentFilteringHandler.php @@ -0,0 +1,82 @@ +gateway = $gateway; + $this->mapper = $mapper; + $this->fieldHandler = $fieldHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Filter\Content\LazyContentItemListIterator + */ + public function find(Filter $filter): iterable + { + $count = $this->gateway->count($filter->getCriterion()); + + // wrapped list before creating the actual API ContentList to pass totalCount + // for paginated result a total count is not going to be a number of items in a current page + $list = new LazyContentItemListIterator($count); + if ($count === 0) { + return $list; + } + + $list->prepareIterator( + $this->gateway->find( + $filter->getCriterion(), + $filter->getSortClauses(), + $filter->getLimit(), + $filter->getOffset() + ), + // called on each iteration of the iterator returned by find + function (array $row): ContentItem { + $contentItem = $this->mapper->mapRawDataToPersistenceContentItem($row); + $this->fieldHandler->loadExternalFieldData($contentItem->getContent()); + + return $contentItem; + } + ); + + return $list; + } + + public function count(Filter $filter): int + { + return $this->gateway->count($filter->getCriterion()); + } +} + +class_alias(ContentFilteringHandler::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\Handler\ContentFilteringHandler'); diff --git a/src/lib/Persistence/Legacy/Filter/Handler/LocationFilteringHandler.php b/src/lib/Persistence/Legacy/Filter/Handler/LocationFilteringHandler.php new file mode 100644 index 0000000000..a5bdacb382 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/Handler/LocationFilteringHandler.php @@ -0,0 +1,78 @@ +gateway = $gateway; + $this->locationMapper = $locationMapper; + $this->contentGatewayDataMapper = $contentGatewayDataMapper; + } + + public function find(Filter $filter): iterable + { + $count = $this->gateway->count($filter->getCriterion()); + + // wrapped list before creating the actual API LocationList to pass totalCount + // for paginated result a total count is not going to be a number of items in a current page + $list = new LazyLocationListIterator($count); + if ($count === 0) { + return $list; + } + + $list->prepareIterator( + $this->gateway->find( + $filter->getCriterion(), + $filter->getSortClauses(), + $filter->getLimit(), + $filter->getOffset() + ), + // called on each iteration of the iterator returned by find + function (array $row): LocationWithContentInfo { + return new LocationWithContentInfo( + $this->locationMapper->createLocationFromRow($row, 'location_'), + $this->contentGatewayDataMapper->mapContentMetadataToPersistenceContentInfo( + $row + ) + ); + } + ); + + return $list; + } + + public function count(Filter $filter): int + { + return $this->gateway->count($filter->getCriterion()); + } +} + +class_alias(LocationFilteringHandler::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\Handler\LocationFilteringHandler'); diff --git a/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/DateModifiedSortClauseQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/DateModifiedSortClauseQueryBuilder.php new file mode 100644 index 0000000000..0ebd294116 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/DateModifiedSortClauseQueryBuilder.php @@ -0,0 +1,32 @@ +addOrderBy('content.modified', $sortClause->direction); + } +} + +class_alias(DateModifiedSortClauseQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Content\DateModifiedSortClauseQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/DatePublishedSortClauseQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/DatePublishedSortClauseQueryBuilder.php new file mode 100644 index 0000000000..e6ae81cea4 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/DatePublishedSortClauseQueryBuilder.php @@ -0,0 +1,32 @@ +addOrderBy('content.published', $sortClause->direction); + } +} + +class_alias(DatePublishedSortClauseQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Content\DatePublishedSortClauseQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/IdSortClauseQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/IdSortClauseQueryBuilder.php new file mode 100644 index 0000000000..2beae9b636 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/IdSortClauseQueryBuilder.php @@ -0,0 +1,32 @@ +addOrderBy('content.id', $sortClause->direction); + } +} + +class_alias(IdSortClauseQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Content\IdSortClauseQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/NameSortClauseQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/NameSortClauseQueryBuilder.php new file mode 100644 index 0000000000..51946da0bc --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/NameSortClauseQueryBuilder.php @@ -0,0 +1,32 @@ +addOrderBy('content.name', $sortClause->direction); + } +} + +class_alias(NameSortClauseQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Content\NameSortClauseQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/SectionIdentifierSortClauseQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/SectionIdentifierSortClauseQueryBuilder.php new file mode 100644 index 0000000000..c9bfd8a6ab --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/SectionIdentifierSortClauseQueryBuilder.php @@ -0,0 +1,42 @@ +addSelect('section.identifier') + ->joinOnce( + 'content', + SectionGateway::CONTENT_SECTION_TABLE, + 'section', + 'content.section_id = section.id' + ); + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause $sortClause */ + $queryBuilder->addOrderBy('section.identifier', $sortClause->direction); + } +} + +class_alias(SectionIdentifierSortClauseQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Content\SectionIdentifierSortClauseQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/SectionNameSortClauseQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/SectionNameSortClauseQueryBuilder.php new file mode 100644 index 0000000000..a2c95562d8 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Content/SectionNameSortClauseQueryBuilder.php @@ -0,0 +1,42 @@ +addSelect('section.name') + ->joinOnce( + 'content', + SectionGateway::CONTENT_SECTION_TABLE, + 'section', + 'content.section_id = section.id' + ); + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause $sortClause */ + $queryBuilder->addOrderBy('section.name', $sortClause->direction); + } +} + +class_alias(SectionNameSortClauseQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Content\SectionNameSortClauseQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/BaseLocationSortClauseQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/BaseLocationSortClauseQueryBuilder.php new file mode 100644 index 0000000000..25f09c0596 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/BaseLocationSortClauseQueryBuilder.php @@ -0,0 +1,36 @@ +getSortingExpression(); + $queryBuilder + ->addSelect($this->getSortingExpression()) + ->joinAllLocations(); + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause $sortClause */ + $queryBuilder->addOrderBy($sort, $sortClause->direction); + } + + abstract protected function getSortingExpression(): string; +} + +class_alias(BaseLocationSortClauseQueryBuilder::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\Location\BaseLocationSortClauseQueryBuilder'); diff --git a/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/DepthQueryBuilder.php b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/DepthQueryBuilder.php new file mode 100644 index 0000000000..2f862abc37 --- /dev/null +++ b/src/lib/Persistence/Legacy/Filter/SortClauseQueryBuilder/Location/DepthQueryBuilder.php @@ -0,0 +1,30 @@ +sortClauseQueryBuilders = $sortClauseQueryBuilders; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Filter\FilteringSortClause[] $sortClauses + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException if there's no builder for a Sort Clause + */ + public function visitSortClauses(FilteringQueryBuilder $queryBuilder, array $sortClauses): void + { + foreach ($sortClauses as $sortClause) { + $this + ->getQueryBuilderForSortClause($sortClause) + ->buildQuery($queryBuilder, $sortClause); + } + } + + /** + * Cache Query Builders in-memory and get the one for the given Sort Clause. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + private function getQueryBuilderForSortClause( + FilteringSortClause $sortClause + ): SortClauseQueryBuilder { + $sortClauseFQCN = get_class($sortClause); + if (!isset(self::$queryBuildersForSortClauses[$sortClauseFQCN])) { + foreach ($this->sortClauseQueryBuilders as $sortClauseQueryBuilder) { + if ($sortClauseQueryBuilder->accepts($sortClause)) { + self::$queryBuildersForSortClauses[$sortClauseFQCN] = $sortClauseQueryBuilder; + break; + } + } + } + + if (!isset(self::$queryBuildersForSortClauses[$sortClauseFQCN])) { + throw new NotImplementedException( + "There are no Query Builders for {$sortClauseFQCN} Sort Clause" + ); + } + + return self::$queryBuildersForSortClauses[$sortClauseFQCN]; + } +} + +class_alias(SortClauseVisitor::class, 'eZ\Publish\Core\Persistence\Legacy\Filter\SortClauseVisitor'); diff --git a/src/lib/Persistence/Legacy/Handler.php b/src/lib/Persistence/Legacy/Handler.php new file mode 100644 index 0000000000..4cccaefb4f --- /dev/null +++ b/src/lib/Persistence/Legacy/Handler.php @@ -0,0 +1,246 @@ +contentHandler = $contentHandler; + $this->contentTypeHandler = $contentTypeHandler; + $this->languageHandler = $languageHandler; + $this->locationHandler = $locationHandler; + $this->objectStateHandler = $objectStateHandler; + $this->sectionHandler = $sectionHandler; + $this->transactionHandler = $transactionHandler; + $this->trashHandler = $trashHandler; + $this->urlAliasHandler = $urlAliasHandler; + $this->urlWildcardHandler = $urlWildcardHandler; + $this->userHandler = $userHandler; + $this->urlHandler = $urlHandler; + $this->bookmarkHandler = $bookmarkHandler; + $this->notificationHandler = $notificationHandler; + $this->userPreferenceHandler = $userPreferenceHandler; + $this->settingHandler = $settingHandler; + } + + public function contentHandler() + { + return $this->contentHandler; + } + + public function contentTypeHandler() + { + return $this->contentTypeHandler; + } + + public function contentLanguageHandler() + { + return $this->languageHandler; + } + + public function locationHandler() + { + return $this->locationHandler; + } + + public function objectStateHandler() + { + return $this->objectStateHandler; + } + + public function sectionHandler() + { + return $this->sectionHandler; + } + + public function trashHandler() + { + return $this->trashHandler; + } + + public function urlAliasHandler() + { + return $this->urlAliasHandler; + } + + public function urlWildcardHandler() + { + return $this->urlWildcardHandler; + } + + public function userHandler() + { + return $this->userHandler; + } + + public function urlHandler() + { + return $this->urlHandler; + } + + public function bookmarkHandler() + { + return $this->bookmarkHandler; + } + + public function settingHandler(): SettingHandler + { + return $this->settingHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Notification\Handler + */ + public function notificationHandler(): NotificationHandler + { + return $this->notificationHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\UserPreference\Handler + */ + public function userPreferenceHandler(): UserPreferenceHandler + { + return $this->userPreferenceHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\TransactionHandler + */ + public function transactionHandler() + { + return $this->transactionHandler; + } + + /** + * Begin transaction. + * + * @deprecated Since 5.3 {@use transactionHandler()->beginTransaction()} + */ + public function beginTransaction() + { + $this->transactionHandler->beginTransaction(); + } + + /** + * Commit transaction. + * + * Commit transaction, or throw exceptions if no transactions has been started. + * + * @throws \RuntimeException If no transaction has been started + * + * @deprecated Since 5.3 {@use transactionHandler()->beginTransaction()} + */ + public function commit() + { + $this->transactionHandler->commit(); + } + + /** + * Rollback transaction. + * + * Rollback transaction, or throw exceptions if no transactions has been started. + * + * @throws \RuntimeException If no transaction has been started + * + * @deprecated Since 5.3 {@use transactionHandler()->beginTransaction()} + */ + public function rollback() + { + $this->transactionHandler->rollback(); + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\Handler'); diff --git a/src/lib/Persistence/Legacy/Notification/Gateway.php b/src/lib/Persistence/Legacy/Notification/Gateway.php new file mode 100644 index 0000000000..be47b14407 --- /dev/null +++ b/src/lib/Persistence/Legacy/Notification/Gateway.php @@ -0,0 +1,75 @@ +connection = $connection; + } + + /** + * {@inheritdoc} + */ + public function insert(CreateStruct $createStruct): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::TABLE_NOTIFICATION) + ->values([ + self::COLUMN_IS_PENDING => ':is_pending', + self::COLUMN_OWNER_ID => ':user_id', + self::COLUMN_CREATED => ':created', + self::COLUMN_TYPE => ':type', + self::COLUMN_DATA => ':data', + ]) + ->setParameter(':is_pending', $createStruct->isPending, PDO::PARAM_BOOL) + ->setParameter(':user_id', $createStruct->ownerId, PDO::PARAM_INT) + ->setParameter(':created', $createStruct->created, PDO::PARAM_INT) + ->setParameter(':type', $createStruct->type, PDO::PARAM_STR) + ->setParameter(':data', json_encode($createStruct->data), PDO::PARAM_STR); + + $query->execute(); + + return (int) $this->connection->lastInsertId(); + } + + /** + * {@inheritdoc} + */ + public function getNotificationById(int $notificationId): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$this->getColumns()) + ->from(self::TABLE_NOTIFICATION) + ->where($query->expr()->eq(self::COLUMN_ID, ':id')); + + $query->setParameter(':id', $notificationId, PDO::PARAM_INT); + + return $query->execute()->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * {@inheritdoc} + */ + public function updateNotification(Notification $notification): void + { + if (!isset($notification->id) || !is_numeric($notification->id)) { + throw new InvalidArgumentException(self::COLUMN_ID, 'Cannot update the notification'); + } + + $query = $this->connection->createQueryBuilder(); + + $query + ->update(self::TABLE_NOTIFICATION) + ->set(self::COLUMN_IS_PENDING, ':is_pending') + ->where($query->expr()->eq(self::COLUMN_ID, ':id')) + ->setParameter(':is_pending', $notification->isPending, PDO::PARAM_BOOL) + ->setParameter(':id', $notification->id, PDO::PARAM_INT); + + $query->execute(); + } + + /** + * {@inheritdoc} + */ + public function countUserNotifications(int $userId): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('COUNT(' . self::COLUMN_ID . ')') + ->from(self::TABLE_NOTIFICATION) + ->where($query->expr()->eq(self::COLUMN_OWNER_ID, ':user_id')) + ->setParameter(':user_id', $userId, PDO::PARAM_INT); + + return (int)$query->execute()->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function countUserPendingNotifications(int $userId): int + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select('COUNT(' . self::COLUMN_ID . ')') + ->from(self::TABLE_NOTIFICATION) + ->where($expr->eq(self::COLUMN_OWNER_ID, ':user_id')) + ->andWhere($expr->eq(self::COLUMN_IS_PENDING, ':is_pending')) + ->setParameter(':is_pending', true, PDO::PARAM_BOOL) + ->setParameter(':user_id', $userId, PDO::PARAM_INT); + + return (int)$query->execute()->fetchColumn(); + } + + /** + * {@inheritdoc} + */ + public function loadUserNotifications(int $userId, int $offset = 0, int $limit = -1): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$this->getColumns()) + ->from(self::TABLE_NOTIFICATION) + ->where($query->expr()->eq(self::COLUMN_OWNER_ID, ':user_id')) + ->setFirstResult($offset); + + if ($limit > 0) { + $query->setMaxResults($limit); + } + + $query->orderBy(self::COLUMN_ID, 'DESC'); + $query->setParameter(':user_id', $userId, PDO::PARAM_INT); + + return $query->execute()->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * {@inheritdoc} + */ + public function delete(int $notificationId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::TABLE_NOTIFICATION) + ->where($query->expr()->eq(self::COLUMN_ID, ':id')) + ->setParameter(':id', $notificationId, PDO::PARAM_INT); + + $query->execute(); + } + + /** + * @return array + */ + private function getColumns(): array + { + return [ + self::COLUMN_ID, + self::COLUMN_OWNER_ID, + self::COLUMN_IS_PENDING, + self::COLUMN_TYPE, + self::COLUMN_CREATED, + self::COLUMN_DATA, + ]; + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..562cd536a2 --- /dev/null +++ b/src/lib/Persistence/Legacy/Notification/Gateway/ExceptionConversion.php @@ -0,0 +1,101 @@ +innerGateway = $innerGateway; + } + + public function getNotificationById(int $notificationId): array + { + try { + return $this->innerGateway->getNotificationById($notificationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateNotification(Notification $notification): void + { + try { + $this->innerGateway->updateNotification($notification); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countUserNotifications(int $userId): int + { + try { + return $this->innerGateway->countUserNotifications($userId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countUserPendingNotifications(int $userId): int + { + try { + return $this->innerGateway->countUserPendingNotifications($userId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadUserNotifications(int $userId, int $offset = 0, int $limit = -1): array + { + try { + return $this->innerGateway->loadUserNotifications($userId, $offset, $limit); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function insert(CreateStruct $notification): int + { + try { + return $this->innerGateway->insert($notification); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function delete(int $notificationId): void + { + try { + $this->innerGateway->delete($notificationId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\Notification\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/Notification/Handler.php b/src/lib/Persistence/Legacy/Notification/Handler.php new file mode 100644 index 0000000000..9dd97545f2 --- /dev/null +++ b/src/lib/Persistence/Legacy/Notification/Handler.php @@ -0,0 +1,119 @@ +gateway = $gateway; + $this->mapper = $mapper; + } + + /** + * {@inheritdoc} + * + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + */ + public function createNotification(CreateStruct $createStruct): Notification + { + $id = $this->gateway->insert($createStruct); + + return $this->getNotificationById($id); + } + + /** + * {@inheritdoc} + */ + public function countPendingNotifications(int $ownerId): int + { + return $this->gateway->countUserPendingNotifications($ownerId); + } + + /** + * {@inheritdoc} + * + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + */ + public function getNotificationById(int $notificationId): Notification + { + $notification = $this->mapper->extractNotificationsFromRows( + $this->gateway->getNotificationById($notificationId) + ); + + if (count($notification) < 1) { + throw new NotFoundException('Notification', $notificationId); + } + + return reset($notification); + } + + /** + * {@inheritdoc} + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + */ + public function updateNotification(APINotification $apiNotification, UpdateStruct $updateStruct): Notification + { + $notification = $this->mapper->createNotificationFromUpdateStruct( + $updateStruct + ); + $notification->id = $apiNotification->id; + + $this->gateway->updateNotification($notification); + + return $this->getNotificationById($notification->id); + } + + /** + * {@inheritdoc} + */ + public function countNotifications(int $userId): int + { + return $this->gateway->countUserNotifications($userId); + } + + /** + * {@inheritdoc} + */ + public function loadUserNotifications(int $userId, int $offset, int $limit): array + { + return $this->mapper->extractNotificationsFromRows( + $this->gateway->loadUserNotifications($userId, $offset, $limit) + ); + } + + /** + * {@inheritdoc} + */ + public function delete(APINotification $notification): void + { + $this->gateway->delete($notification->id); + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\Notification\Handler'); diff --git a/src/lib/Persistence/Legacy/Notification/Mapper.php b/src/lib/Persistence/Legacy/Notification/Mapper.php new file mode 100644 index 0000000000..830b9db322 --- /dev/null +++ b/src/lib/Persistence/Legacy/Notification/Mapper.php @@ -0,0 +1,73 @@ +extractNotificationFromRow($row); + } + + return $notifications; + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Notification\UpdateStruct $updateStruct + * + * @return \Ibexa\Contracts\Core\Persistence\Notification\Notification + */ + public function createNotificationFromUpdateStruct(UpdateStruct $updateStruct): Notification + { + $notification = new Notification(); + $notification->isPending = $updateStruct->isPending; + + return $notification; + } + + /** + * Extract Bookmark object from $row. + * + * @param array $row + * + * @return \Ibexa\Contracts\Core\Persistence\Notification\Notification + */ + private function extractNotificationFromRow(array $row): Notification + { + $notification = new Notification(); + $notification->id = (int)$row['id']; + $notification->ownerId = (int)$row['owner_id']; + $notification->type = $row['type']; + $notification->created = (int)$row['created']; + $notification->isPending = (bool) $row['is_pending']; + if ($row['data'] !== null) { + $notification->data = json_decode($row['data'], true); + if (json_last_error() !== JSON_ERROR_NONE) { + throw new RuntimeException('Error while decoding notification data: ' . json_last_error_msg()); + } + } + + return $notification; + } +} + +class_alias(Mapper::class, 'eZ\Publish\Core\Persistence\Legacy\Notification\Mapper'); diff --git a/src/lib/Persistence/Legacy/Setting/Gateway.php b/src/lib/Persistence/Legacy/Setting/Gateway.php new file mode 100644 index 0000000000..f963d96639 --- /dev/null +++ b/src/lib/Persistence/Legacy/Setting/Gateway.php @@ -0,0 +1,61 @@ +connection = $connection; + } + + public function insertSetting(string $group, string $identifier, string $serializedValue): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::SETTING_TABLE) + ->values( + [ + $this->connection->quoteIdentifier('group') => $query->createPositionalParameter($group), + 'identifier' => $query->createPositionalParameter($identifier), + 'value' => $query->createPositionalParameter($serializedValue), + ] + ); + + $query->execute(); + + return (int)$this->connection->lastInsertId(Gateway::SETTING_SEQ); + } + + public function updateSetting(string $group, string $identifier, string $serializedValue): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::SETTING_TABLE) + ->set('value', $query->createPositionalParameter($serializedValue)) + ->where( + $query->expr()->eq( + $this->connection->quoteIdentifier('group'), + $query->createPositionalParameter($group, ParameterType::STRING) + ), + $query->expr()->eq( + 'identifier', + $query->createPositionalParameter($identifier, ParameterType::STRING) + ) + ); + + $query->execute(); + } + + public function loadSetting(string $group, string $identifier): ?array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select([ + $this->connection->quoteIdentifier('group'), + 'identifier', + 'value', + ]) + ->from(self::SETTING_TABLE) + ->where( + $query->expr()->eq( + $this->connection->quoteIdentifier('group'), + $query->createPositionalParameter($group, ParameterType::STRING) + ), + $query->expr()->eq( + 'identifier', + $query->createPositionalParameter($identifier, ParameterType::STRING) + ) + ); + + $statement = $query->execute(); + $result = $statement->fetchAssociative(); + + if (false === $result) { + return null; + } + + return $result; + } + + public function loadSettingById(int $id): ?array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select([ + $this->connection->quoteIdentifier('group'), + 'identifier', + 'value', + ]) + ->from(self::SETTING_TABLE) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + + $statement = $query->execute(); + $result = $statement->fetchAssociative(); + + if (false === $result) { + return null; + } + + return $result; + } + + public function deleteSetting(string $group, string $identifier): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::SETTING_TABLE) + ->where( + $query->expr()->eq( + $this->connection->quoteIdentifier('group'), + $query->createPositionalParameter($group, ParameterType::STRING) + ), + $query->expr()->eq( + 'identifier', + $query->createPositionalParameter($identifier, ParameterType::STRING) + ) + ); + + $query->execute(); + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\Setting\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/Setting/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/Setting/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..700066078d --- /dev/null +++ b/src/lib/Persistence/Legacy/Setting/Gateway/ExceptionConversion.php @@ -0,0 +1,90 @@ +innerGateway = $innerGateway; + } + + /** + * @throws \Ibexa\Core\Base\Exceptions\DatabaseException + */ + public function insertSetting(string $group, string $identifier, string $serializedValue): int + { + try { + return $this->innerGateway->insertSetting($group, $identifier, $serializedValue); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + /** + * @throws \Ibexa\Core\Base\Exceptions\DatabaseException + */ + public function updateSetting(string $group, string $identifier, string $serializedValue): void + { + try { + $this->innerGateway->updateSetting($group, $identifier, $serializedValue); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + /** + * @throws \Ibexa\Core\Base\Exceptions\DatabaseException + */ + public function loadSetting(string $group, string $identifier): ?array + { + try { + return $this->innerGateway->loadSetting($group, $identifier); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + /** + * @throws \Ibexa\Core\Base\Exceptions\DatabaseException + */ + public function loadSettingById(int $id): ?array + { + try { + return $this->innerGateway->loadSettingById($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + /** + * @throws \Ibexa\Core\Base\Exceptions\DatabaseException + */ + public function deleteSetting(string $group, string $identifier): void + { + try { + $this->innerGateway->deleteSetting($group, $identifier); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\Setting\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/Setting/Handler.php b/src/lib/Persistence/Legacy/Setting/Handler.php new file mode 100644 index 0000000000..b336a0bf92 --- /dev/null +++ b/src/lib/Persistence/Legacy/Setting/Handler.php @@ -0,0 +1,100 @@ +settingGateway = $settingGateway; + } + + public function create(string $group, string $identifier, string $serializedValue): Setting + { + $lastId = $this->settingGateway->insertSetting( + $group, + $identifier, + $serializedValue + ); + + $setting = $this->settingGateway->loadSettingById($lastId); + + if (empty($setting)) { + throw $this->createNotFoundException($group, $identifier); + } + + return new Setting([ + 'group' => $setting['group'], + 'identifier' => $setting['identifier'], + 'serializedValue' => $setting['value'], + ]); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function update(string $group, string $identifier, string $serializedValue): Setting + { + $this->settingGateway->updateSetting( + $group, + $identifier, + $serializedValue + ); + + $setting = $this->settingGateway->loadSetting($group, $identifier); + + if (empty($setting)) { + throw $this->createNotFoundException($group, $identifier); + } + + return new Setting([ + 'group' => $setting['group'], + 'identifier' => $setting['identifier'], + 'serializedValue' => $setting['value'], + ]); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function load(string $group, string $identifier): Setting + { + $setting = $this->settingGateway->loadSetting($group, $identifier); + + if (empty($setting)) { + throw $this->createNotFoundException($group, $identifier); + } + + return new Setting([ + 'group' => $setting['group'], + 'identifier' => $setting['identifier'], + 'serializedValue' => $setting['value'], + ]); + } + + public function delete(string $group, string $identifier): void + { + $this->settingGateway->deleteSetting($group, $identifier); + } + + private function createNotFoundException(string $group, string $identifier): NotFound + { + return new NotFound('Setting', [ + 'group' => $group, + 'identifier' => $identifier, + ]); + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\Setting\Handler'); diff --git a/eZ/Publish/Core/Persistence/Legacy/SharedGateway/DatabasePlatform/FallbackGateway.php b/src/lib/Persistence/Legacy/SharedGateway/DatabasePlatform/FallbackGateway.php similarity index 75% rename from eZ/Publish/Core/Persistence/Legacy/SharedGateway/DatabasePlatform/FallbackGateway.php rename to src/lib/Persistence/Legacy/SharedGateway/DatabasePlatform/FallbackGateway.php index 419515b419..7a7a483c54 100644 --- a/eZ/Publish/Core/Persistence/Legacy/SharedGateway/DatabasePlatform/FallbackGateway.php +++ b/src/lib/Persistence/Legacy/SharedGateway/DatabasePlatform/FallbackGateway.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Legacy\SharedGateway\DatabasePlatform; +namespace Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform; use Doctrine\DBAL\Connection; -use eZ\Publish\Core\Persistence\Legacy\SharedGateway\Gateway; +use Ibexa\Core\Persistence\Legacy\SharedGateway\Gateway; final class FallbackGateway implements Gateway { @@ -34,3 +34,5 @@ public function getLastInsertedId(string $sequenceName): int return (int)$this->connection->lastInsertId($sequenceName); } } + +class_alias(FallbackGateway::class, 'eZ\Publish\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\FallbackGateway'); diff --git a/eZ/Publish/Core/Persistence/Legacy/SharedGateway/DatabasePlatform/SqliteGateway.php b/src/lib/Persistence/Legacy/SharedGateway/DatabasePlatform/SqliteGateway.php similarity index 77% rename from eZ/Publish/Core/Persistence/Legacy/SharedGateway/DatabasePlatform/SqliteGateway.php rename to src/lib/Persistence/Legacy/SharedGateway/DatabasePlatform/SqliteGateway.php index fea446be49..31ed4d15bd 100644 --- a/eZ/Publish/Core/Persistence/Legacy/SharedGateway/DatabasePlatform/SqliteGateway.php +++ b/src/lib/Persistence/Legacy/SharedGateway/DatabasePlatform/SqliteGateway.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Legacy\SharedGateway\DatabasePlatform; +namespace Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\Core\Persistence\Legacy\SharedGateway\Gateway; +use Ibexa\Core\Base\Exceptions\DatabaseException; +use Ibexa\Core\Persistence\Legacy\SharedGateway\Gateway; final class SqliteGateway implements Gateway { @@ -34,7 +34,7 @@ public function getColumnNextIntegerValue( } /** - * @throws \eZ\Publish\Core\Base\Exceptions\DatabaseException if the sequence has no last value + * @throws \Ibexa\Core\Base\Exceptions\DatabaseException if the sequence has no last value */ public function getLastInsertedId(string $sequenceName): int { @@ -48,3 +48,5 @@ public function getLastInsertedId(string $sequenceName): int return $this->lastInsertedIds[$sequenceName]; } } + +class_alias(SqliteGateway::class, 'eZ\Publish\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\SqliteGateway'); diff --git a/src/lib/Persistence/Legacy/SharedGateway/Gateway.php b/src/lib/Persistence/Legacy/SharedGateway/Gateway.php new file mode 100644 index 0000000000..9b947b146c --- /dev/null +++ b/src/lib/Persistence/Legacy/SharedGateway/Gateway.php @@ -0,0 +1,46 @@ +fallbackGateway; } } + +class_alias(GatewayFactory::class, 'eZ\Publish\Core\Persistence\Legacy\SharedGateway\GatewayFactory'); diff --git a/src/lib/Persistence/Legacy/Token/AbstractGateway.php b/src/lib/Persistence/Legacy/Token/AbstractGateway.php new file mode 100644 index 0000000000..4d8b8f9752 --- /dev/null +++ b/src/lib/Persistence/Legacy/Token/AbstractGateway.php @@ -0,0 +1,32 @@ + $this->getAliasedColumn($column, $alias), + $columns + ); + } + + protected function getAliasedColumn( + string $column, + string $alias + ): string { + return sprintf('%s.%s', $alias, $column); + } +} diff --git a/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php new file mode 100644 index 0000000000..10975aa821 --- /dev/null +++ b/src/lib/Persistence/Legacy/Token/Gateway/Token/Doctrine/DoctrineGateway.php @@ -0,0 +1,251 @@ +connection = $connection; + } + + public static function getColumns(): array + { + return [ + self::COLUMN_ID, + self::COLUMN_TYPE_ID, + self::COLUMN_TOKEN, + self::COLUMN_IDENTIFIER, + self::COLUMN_CREATED, + self::COLUMN_EXPIRES, + self::COLUMN_REVOKED, + ]; + } + + public function insert( + int $typeId, + string $token, + ?string $identifier, + int $ttl + ): int { + $now = $this->getCurrentUnixTimestamp(); + $this->connection->insert( + self::TABLE_NAME, + [ + self::COLUMN_TYPE_ID => $typeId, + self::COLUMN_TOKEN => $token, + self::COLUMN_IDENTIFIER => $identifier, + self::COLUMN_CREATED => $now, + self::COLUMN_EXPIRES => $now + $ttl, + self::COLUMN_REVOKED => false, + ], + [ + self::COLUMN_TYPE_ID => ParameterType::INTEGER, + self::COLUMN_CREATED => ParameterType::INTEGER, + self::COLUMN_EXPIRES => ParameterType::INTEGER, + self::COLUMN_REVOKED => ParameterType::BOOLEAN, + ] + ); + + return (int)$this->connection->lastInsertId(self::TOKEN_SEQ); + } + + public function revoke(int $tokenId): void + { + $this->connection->update( + self::TABLE_NAME, + [ + self::COLUMN_REVOKED => true, + ], + [ + self::COLUMN_ID => $tokenId, + ], + [ + self::COLUMN_REVOKED => ParameterType::BOOLEAN, + ] + ); + } + + public function revokeByIdentifier(int $typeId, ?string $identifier): void + { + $this->connection->update( + self::TABLE_NAME, + [ + self::COLUMN_REVOKED => true, + ], + [ + self::COLUMN_TYPE_ID => $typeId, + self::COLUMN_IDENTIFIER => $identifier, + ], + [ + self::COLUMN_REVOKED => ParameterType::BOOLEAN, + ] + ); + } + + public function delete(int $tokenId): void + { + $this->connection->delete( + self::TABLE_NAME, + [ + self::COLUMN_ID => $tokenId, + ], + [ + self::COLUMN_ID => ParameterType::INTEGER, + ] + ); + } + + public function deleteExpired(?int $typeId = null): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::TABLE_NAME) + ->andWhere( + $query->expr()->lt(self::COLUMN_EXPIRES, ':now') + ) + ->setParameter(':now', $this->getCurrentUnixTimestamp(), ParameterType::INTEGER); + + if (null !== $typeId) { + $query->andWhere( + $query->expr()->eq( + self::COLUMN_TYPE_ID, + ':type_id' + ) + ); + $query->setParameter(':type_id', $typeId, ParameterType::INTEGER); + } + + $query->execute(); + } + + public function getToken( + string $tokenType, + string $token, + ?string $identifier = null + ): array { + $query = $this->getTokenSelectQueryBuilder($tokenType, $token, $identifier); + $row = $query->execute()->fetchAssociative(); + + if (false === $row) { + throw new NotFound('token', "token: $token, type: $tokenType, identifier: $identifier"); + } + + return $row; + } + + public function getTokenById(int $tokenId): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$this->getAliasedColumns(self::DEFAULT_TABLE_ALIAS, self::getColumns())) + ->from(self::TABLE_NAME, self::DEFAULT_TABLE_ALIAS) + ->andWhere( + $query->expr()->eq( + $this->getAliasedColumn(self::COLUMN_ID, self::DEFAULT_TABLE_ALIAS), + ':token_id' + ) + ); + + $query->setParameter(':token_id', $tokenId, ParameterType::INTEGER); + + $row = $query->execute()->fetchAssociative(); + + if (false === $row) { + throw new NotFound('token', "id: $tokenId"); + } + + return $row; + } + + private function getCurrentUnixTimestamp(): int + { + return time(); + } + + private function getTokenSelectQueryBuilder( + string $tokenType, + string $token, + ?string $identifier = null + ): QueryBuilder { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select(...$this->getAliasedColumns(self::DEFAULT_TABLE_ALIAS, self::getColumns())) + ->from(self::TABLE_NAME, self::DEFAULT_TABLE_ALIAS) + ->innerJoin( + self::DEFAULT_TABLE_ALIAS, + TokenTypeGateway::TABLE_NAME, + TokenTypeGateway::DEFAULT_TABLE_ALIAS, + $expr->eq( + $this->getAliasedColumn(self::COLUMN_TYPE_ID, self::DEFAULT_TABLE_ALIAS), + $this->getAliasedColumn( + TokenTypeGateway::COLUMN_ID, + TokenTypeGateway::DEFAULT_TABLE_ALIAS + ) + ) + ) + ->andWhere( + $query->expr()->eq( + $this->getAliasedColumn(self::COLUMN_TOKEN, self::DEFAULT_TABLE_ALIAS), + ':token' + ) + ) + ->andWhere( + $query->expr()->eq( + $this->getAliasedColumn( + TokenTypeGateway::COLUMN_IDENTIFIER, + TokenTypeGateway::DEFAULT_TABLE_ALIAS + ), + ':token_type' + ) + ); + + $query->setParameter(':token_type', $tokenType, ParameterType::STRING); + $query->setParameter(':token', $token, ParameterType::STRING); + + if (null !== $identifier) { + $query->andWhere( + $query->expr()->eq( + $this->getAliasedColumn(self::COLUMN_IDENTIFIER, self::DEFAULT_TABLE_ALIAS), + ':identifier' + ) + ); + $query->setParameter(':identifier', $identifier, ParameterType::STRING); + } + + return $query; + } +} diff --git a/src/lib/Persistence/Legacy/Token/Gateway/Token/Gateway.php b/src/lib/Persistence/Legacy/Token/Gateway/Token/Gateway.php new file mode 100644 index 0000000000..eeb2a57a63 --- /dev/null +++ b/src/lib/Persistence/Legacy/Token/Gateway/Token/Gateway.php @@ -0,0 +1,57 @@ +connection = $connection; + } + + public static function getColumns(): array + { + return [ + self::COLUMN_ID, + self::COLUMN_IDENTIFIER, + ]; + } + + public function insert(string $identifier): int + { + $this->connection->insert(self::TABLE_NAME, [ + self::COLUMN_IDENTIFIER => $identifier, + ]); + + return (int)$this->connection->lastInsertId(self::TOKEN_TYPE_SEQ); + } + + /** + * @throws \Doctrine\DBAL\Exception + */ + public function deleteById(int $typeId): void + { + $this->connection->delete(self::TABLE_NAME, [ + self::COLUMN_ID => $typeId, + ]); + } + + /** + * @throws \Doctrine\DBAL\Exception + */ + public function deleteByIdentifier(string $identifier): void + { + $this->connection->delete(self::TABLE_NAME, [ + self::COLUMN_IDENTIFIER => $identifier, + ]); + } + + public function getTypeById(int $typeId): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$this->getAliasedColumns(self::DEFAULT_TABLE_ALIAS, self::getColumns())) + ->from(self::TABLE_NAME, self::DEFAULT_TABLE_ALIAS) + ->andWhere( + $query->expr()->eq( + $this->getAliasedColumn(self::COLUMN_ID, self::DEFAULT_TABLE_ALIAS), + ':type_id' + ) + ); + + $query->setParameter(':type_id', $typeId, ParameterType::INTEGER); + + $row = $query->execute()->fetchAssociative(); + + if (false === $row) { + throw new NotFound('token_type', "id: $typeId"); + } + + return $row; + } + + public function getTypeByIdentifier(string $identifier): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$this->getAliasedColumns(self::DEFAULT_TABLE_ALIAS, self::getColumns())) + ->from(self::TABLE_NAME, self::DEFAULT_TABLE_ALIAS) + ->andWhere( + $query->expr()->eq( + $this->getAliasedColumn(self::COLUMN_IDENTIFIER, self::DEFAULT_TABLE_ALIAS), + ':identifier' + ) + ); + + $query->setParameter(':identifier', $identifier, ParameterType::STRING); + + $row = $query->execute()->fetchAssociative(); + + if (false === $row) { + throw new NotFound('token_type', "identifier: $identifier"); + } + + return $row; + } +} diff --git a/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Gateway.php b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Gateway.php new file mode 100644 index 0000000000..8acf21ffe9 --- /dev/null +++ b/src/lib/Persistence/Legacy/Token/Gateway/TokenType/Gateway.php @@ -0,0 +1,35 @@ +mapper = $mapper; + $this->tokenGateway = $tokenGateway; + $this->tokenTypeGateway = $tokenTypeGateway; + } + + /** + * @throws \Doctrine\DBAL\Driver\Exception + * @throws \Doctrine\DBAL\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\TokenExpiredException + * @throws \Exception + */ + public function getToken( + string $tokenType, + string $token, + ?string $identifier = null + ): Token { + $persistenceTokenValue = $this->mapper->mapToken( + $this->tokenGateway->getToken($tokenType, $token, $identifier) + ); + + if ($persistenceTokenValue->expires < time()) { + throw new TokenExpiredException( + $tokenType, + $persistenceTokenValue->token, + new DateTimeImmutable('@' . $persistenceTokenValue->expires) + ); + } + + return $persistenceTokenValue; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Doctrine\DBAL\Exception + * @throws \Doctrine\DBAL\Driver\Exception + */ + public function getTokenType( + string $identifier + ): TokenType { + return $this->mapper->mapTokenType( + $this->tokenTypeGateway->getTypeByIdentifier($identifier) + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Doctrine\DBAL\Driver\Exception + * @throws \Doctrine\DBAL\Exception + */ + public function createToken(CreateStruct $createStruct): Token + { + try { + $typeId = $this->getTokenType($createStruct->type)->id; + } catch (NotFoundException $exception) { + $typeId = $this->tokenTypeGateway->insert($createStruct->type); + } + + $tokenId = $this->tokenGateway->insert( + $typeId, + $createStruct->token, + $createStruct->identifier, + $createStruct->ttl + ); + + return $this->mapper->mapToken( + $this->tokenGateway->getTokenById($tokenId) + ); + } + + public function revokeTokenById(int $tokenId): void + { + $this->tokenGateway->revoke($tokenId); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Doctrine\DBAL\Exception + * @throws \Doctrine\DBAL\Driver\Exception + */ + public function revokeTokenByIdentifier(string $tokenType, ?string $identifier): void + { + $type = $this->getTokenType($tokenType); + $this->tokenGateway->revokeByIdentifier( + $type->id, + $identifier + ); + } + + public function deleteToken(Token $token): void + { + $this->deleteTokenById($token->id); + } + + public function deleteTokenById(int $tokenId): void + { + $this->tokenGateway->delete($tokenId); + } + + public function deleteExpiredTokens(?string $tokenType = null): void + { + try { + if (null !== $tokenType) { + $typeId = $this->getTokenType($tokenType)->id; + } + } catch (NotFoundException $exception) { + return; + } + + $this->tokenGateway->deleteExpired($typeId ?? null); + } +} diff --git a/src/lib/Persistence/Legacy/Token/Mapper.php b/src/lib/Persistence/Legacy/Token/Mapper.php new file mode 100644 index 0000000000..b1a796d477 --- /dev/null +++ b/src/lib/Persistence/Legacy/Token/Mapper.php @@ -0,0 +1,39 @@ + (int)$tokenRow['id'], + 'typeId' => (int)$tokenRow['type_id'], + 'token' => (string)$tokenRow['token'], + 'identifier' => $tokenRow['identifier'] === null ? null : (string)$tokenRow['identifier'], + 'created' => (int)$tokenRow['created'], + 'expires' => (int)$tokenRow['expires'], + 'revoked' => (bool)$tokenRow['revoked'], + ]); + } + + public function mapTokenType(array $tokenTypeRow): TokenType + { + return new TokenType([ + 'id' => (int)$tokenTypeRow['id'], + 'identifier' => (string)$tokenTypeRow['identifier'], + ]); + } +} diff --git a/src/lib/Persistence/Legacy/TransactionHandler.php b/src/lib/Persistence/Legacy/TransactionHandler.php new file mode 100644 index 0000000000..b450b9bdf3 --- /dev/null +++ b/src/lib/Persistence/Legacy/TransactionHandler.php @@ -0,0 +1,92 @@ +connection = $connection; + $this->contentTypeHandler = $contentTypeHandler; + $this->languageHandler = $languageHandler; + } + + /** + * Begin transaction. + */ + public function beginTransaction() + { + $this->connection->beginTransaction(); + } + + /** + * Commit transaction. + * + * Commit transaction, or throw exceptions if no transactions has been started. + * + * @throws \RuntimeException If no transaction has been started + */ + public function commit() + { + try { + $this->connection->commit(); + } catch (Exception $e) { + throw new RuntimeException($e->getMessage(), 0, $e); + } + } + + /** + * Rollback transaction. + * + * Rollback transaction, or throw exceptions if no transactions has been started. + * + * @throws \RuntimeException If no transaction has been started + */ + public function rollback() + { + try { + $this->connection->rollback(); + + // Clear all caches after rollback + if ($this->contentTypeHandler instanceof CachingContentTypeHandler) { + $this->contentTypeHandler->clearCache(); + } + + if ($this->languageHandler instanceof CachingLanguageHandler) { + $this->languageHandler->clearCache(); + } + } catch (Exception $e) { + throw new RuntimeException($e->getMessage(), 0, $e); + } + } +} + +class_alias(TransactionHandler::class, 'eZ\Publish\Core\Persistence\Legacy\TransactionHandler'); diff --git a/src/lib/Persistence/Legacy/URL/Gateway.php b/src/lib/Persistence/Legacy/URL/Gateway.php new file mode 100644 index 0000000000..ed83690ed7 --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Gateway.php @@ -0,0 +1,68 @@ + 'ASC', + SortClause::SORT_DESC => 'DESC', + ]; + + /** @var \Doctrine\DBAL\Connection */ + protected $connection; + + /** + * Criteria converter. + * + * @var \Ibexa\Core\Persistence\Legacy\URL\Query\CriteriaConverter + */ + protected $criteriaConverter; + + public function __construct(Connection $connection, CriteriaConverter $criteriaConverter) + { + $this->connection = $connection; + $this->criteriaConverter = $criteriaConverter; + } + + /** + * {@inheritdoc} + */ + public function find(Criterion $criterion, $offset, $limit, array $sortClauses = [], $doCount = true) + { + $count = $doCount ? $this->doCount($criterion) : null; + if (!$doCount && $limit === 0) { + throw new RuntimeException('Invalid query. Cannot disable count and request 0 items at the same time'); + } + + if ($limit === 0 || ($count !== null && $count <= $offset)) { + return [ + 'count' => $count, + 'rows' => [], + ]; + } + + $query = $this->createSelectDistinctQuery(); + $query + ->where($this->criteriaConverter->convertCriteria($query, $criterion)) + ->setMaxResults($limit > 0 ? $limit : PHP_INT_MAX) + ->setFirstResult($offset); + + foreach ($sortClauses as $sortClause) { + $column = sprintf('url.%s', $sortClause->target); + $query->addOrderBy($column, $this->getQuerySortingDirection($sortClause->direction)); + } + + $statement = $query->execute(); + + return [ + 'count' => $count, + 'rows' => $statement->fetchAllAssociative(), + ]; + } + + /** + * {@inheritdoc} + */ + public function findUsages($id): array + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select('DISTINCT c.id') + ->from(ContentGateway::CONTENT_ITEM_TABLE, 'c') + ->innerJoin( + 'c', + ContentGateway::CONTENT_FIELD_TABLE, + 'f_def', + $expr->andX( + 'c.id = f_def.contentobject_id', + 'c.current_version = f_def.version' + ) + ) + ->innerJoin( + 'f_def', + self::URL_LINK_TABLE, + 'u_lnk', + $expr->andX( + 'f_def.id = u_lnk.contentobject_attribute_id', + 'f_def.version = u_lnk.contentobject_attribute_version' + ) + ) + ->where( + $expr->eq( + 'u_lnk.url_id', + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + + return $query->execute()->fetchAll(FetchMode::COLUMN); + } + + /** + * {@inheritdoc} + */ + public function updateUrl(URL $url) + { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::URL_TABLE) + ->set( + self::COLUMN_URL, + $query->createPositionalParameter($url->url, ParameterType::STRING) + )->set( + self::COLUMN_ORIGINAL_URL_MD5, + $query->createPositionalParameter($url->originalUrlMd5, ParameterType::STRING) + ) + ->set( + self::COLUMN_MODIFIED, + $query->createPositionalParameter($url->modified, ParameterType::INTEGER) + ) + ->set( + self::COLUMN_IS_VALID, + $query->createPositionalParameter((int)$url->isValid, ParameterType::INTEGER) + ) + ->set( + self::COLUMN_LAST_CHECKED, + $query->createPositionalParameter($url->lastChecked, ParameterType::INTEGER) + ) + ->where( + $query->expr()->eq( + self::COLUMN_ID, + $query->createPositionalParameter($url->id, ParameterType::INTEGER) + ) + ); + + $query->execute(); + } + + /** + * {@inheritdoc} + */ + public function loadUrlData($id): array + { + $query = $this->createSelectQuery(); + $query->where( + $query->expr()->eq( + self::COLUMN_ID, + $query->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + /** + * {@inheritdoc} + */ + public function loadUrlDataByUrl($url): array + { + $query = $this->createSelectQuery(); + $query->where( + $query->expr()->eq( + self::COLUMN_URL, + $query->createPositionalParameter($url, ParameterType::STRING) + ) + ); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + protected function doCount(Criterion $criterion): int + { + $columnName = self::COLUMN_ID; + + $query = $this->connection->createQueryBuilder(); + $query + ->select("COUNT(DISTINCT url.{$columnName})") + ->from(self::URL_TABLE, 'url') + ->where($this->criteriaConverter->convertCriteria($query, $criterion)); + + return (int)$query->execute()->fetchColumn(); + } + + /** + * Creates a Url find query. + */ + protected function createSelectQuery(): QueryBuilder + { + return $this->connection + ->createQueryBuilder() + ->select($this->getSelectColumns()) + ->from(self::URL_TABLE, 'url'); + } + + private function createSelectDistinctQuery(): QueryBuilder + { + return $this->connection + ->createQueryBuilder() + ->select(sprintf('DISTINCT %s', implode(', ', $this->getSelectColumns()))) + ->from(self::URL_TABLE, 'url'); + } + + private function getSelectColumns(): array + { + return [ + sprintf('url.%s', self::COLUMN_ID), + sprintf('url.%s', self::COLUMN_URL), + sprintf('url.%s', self::COLUMN_ORIGINAL_URL_MD5), + sprintf('url.%s', self::COLUMN_IS_VALID), + sprintf('url.%s', self::COLUMN_LAST_CHECKED), + sprintf('url.%s', self::COLUMN_CREATED), + sprintf('url.%s', self::COLUMN_MODIFIED), + ]; + } + + /** + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + */ + private function getQuerySortingDirection(string $direction): string + { + if (!isset(self::SORT_DIRECTION_MAP[$direction])) { + throw new InvalidArgumentException( + '$sortClause->direction', + sprintf( + 'Unsupported "%s" sorting directions, use one of the SortClause::SORT_* constants instead', + $direction + ) + ); + } + + return self::SORT_DIRECTION_MAP[$direction]; + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/URL/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/URL/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..ff301d2ad1 --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Gateway/ExceptionConversion.php @@ -0,0 +1,81 @@ +innerGateway = $innerGateway; + } + + public function updateUrl(URL $url) + { + try { + return $this->innerGateway->updateUrl($url); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function find(Criterion $criterion, $offset, $limit, array $sortClauses = [], $doCount = true) + { + try { + return $this->innerGateway->find($criterion, $offset, $limit, $sortClauses, $doCount); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function findUsages($id) + { + try { + return $this->innerGateway->findUsages($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadUrlData($id) + { + try { + return $this->innerGateway->loadUrlData($id); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadUrlDataByUrl($url) + { + try { + return $this->innerGateway->loadUrlDataByUrl($url); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/URL/Handler.php b/src/lib/Persistence/Legacy/URL/Handler.php new file mode 100644 index 0000000000..5ce9869be6 --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Handler.php @@ -0,0 +1,114 @@ +urlGateway = $gateway; + $this->urlMapper = $mapper; + } + + /** + * {@inheritdoc} + */ + public function updateUrl($id, URLUpdateStruct $urlUpdateStruct) + { + $url = $this->urlMapper->createURLFromUpdateStruct( + $urlUpdateStruct + ); + $url->id = $id; + + $this->urlGateway->updateUrl($url); + + return $url; + } + + /** + * {@inheritdoc} + */ + public function find(URLQuery $query) + { + $results = $this->urlGateway->find( + $query->filter, + $query->offset, + $query->limit, + $query->sortClauses, + $query->performCount + ); + + return [ + 'count' => $results['count'], + 'items' => $this->urlMapper->extractURLsFromRows($results['rows']), + ]; + } + + /** + * {@inheritdoc} + */ + public function loadById($id) + { + $url = $this->urlMapper->extractURLsFromRows( + $this->urlGateway->loadUrlData($id) + ); + + if (count($url) < 1) { + throw new NotFoundException('URL', $id); + } + + return reset($url); + } + + /** + * {@inheritdoc} + */ + public function loadByUrl($url) + { + $urls = $this->urlMapper->extractURLsFromRows( + $this->urlGateway->loadUrlDataByUrl($url) + ); + + if (count($urls) < 1) { + throw new NotFoundException('URL', $url); + } + + return reset($urls); + } + + /** + * {@inheritdoc} + */ + public function findUsages($id) + { + $ids = $this->urlGateway->findUsages($id); + + return $ids; + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Handler'); diff --git a/src/lib/Persistence/Legacy/URL/Mapper.php b/src/lib/Persistence/Legacy/URL/Mapper.php new file mode 100644 index 0000000000..3b29647e3c --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Mapper.php @@ -0,0 +1,64 @@ +url = $struct->url; + $url->originalUrlMd5 = md5($struct->url); + $url->isValid = $struct->isValid; + $url->lastChecked = $struct->lastChecked; + $url->modified = time(); + + return $url; + } + + /** + * Extracts URL objects from $rows. + * + * @param array $rows + * + * @return \Ibexa\Contracts\Core\Persistence\URL\URL[] + */ + public function extractURLsFromRows(array $rows) + { + $urls = []; + + foreach ($rows as $row) { + $url = new URL(); + $url->id = (int)$row['id']; + $url->url = $row['url']; + $url->originalUrlMd5 = $row['original_url_md5']; + $url->isValid = (bool)$row['is_valid']; + $url->lastChecked = (int)$row['last_checked']; + $url->created = (int)$row['created']; + $url->modified = (int)$row['modified']; + + $urls[] = $url; + } + + return $urls; + } +} + +class_alias(Mapper::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Mapper'); diff --git a/src/lib/Persistence/Legacy/URL/Query/CriteriaConverter.php b/src/lib/Persistence/Legacy/URL/Query/CriteriaConverter.php new file mode 100644 index 0000000000..21e7002ac7 --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Query/CriteriaConverter.php @@ -0,0 +1,63 @@ +handlers = $handlers; + } + + /** + * Adds handler. + * + * @param \Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler $handler + */ + public function addHandler(CriterionHandler $handler) + { + $this->handlers[] = $handler; + } + + /** + * Generic converter of criteria into query fragments. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException if Criterion is not applicable to its target + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|string + */ + public function convertCriteria(QueryBuilder $queryBuilder, Criterion $criterion) + { + foreach ($this->handlers as $handler) { + if ($handler->accept($criterion)) { + return $handler->handle($this, $queryBuilder, $criterion); + } + } + + throw new NotImplementedException( + 'No visitor available for: ' . get_class($criterion) + ); + } +} + +class_alias(CriteriaConverter::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter'); diff --git a/src/lib/Persistence/Legacy/URL/Query/CriterionHandler.php b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler.php new file mode 100644 index 0000000000..dc8920b58c --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler.php @@ -0,0 +1,37 @@ +hasJoinedTable($query, DoctrineDatabase::URL_LINK_TABLE)) { + $query->innerJoin( + 'url', + DoctrineDatabase::URL_LINK_TABLE, + 'u_lnk', + 'url.id = u_lnk.url_id' + ); + } + } + + /** + * Inner join `ezcontentobject` table if not joined yet. + */ + protected function joinContentObject(QueryBuilder $query): void + { + if (!$this->hasJoinedTable($query, ContentGateway::CONTENT_ITEM_TABLE)) { + $query->innerJoin( + 'f_def', + ContentGateway::CONTENT_ITEM_TABLE, + 'c', + 'c.id = f_def.contentobject_id' + ); + } + } + + /** + * Inner join `ezcontentobject_attribute` table if not joined yet. + */ + protected function joinContentObjectAttribute(QueryBuilder $query): void + { + if (!$this->hasJoinedTable($query, ContentGateway::CONTENT_FIELD_TABLE)) { + $query->innerJoin( + 'u_lnk', + ContentGateway::CONTENT_FIELD_TABLE, + 'f_def', + $query->expr()->andX( + 'u_lnk.contentobject_attribute_id = f_def.id', + 'u_lnk.contentobject_attribute_version = f_def.version' + ) + ); + } + } + + protected function hasJoinedTable(QueryBuilder $queryBuilder, string $tableName): bool + { + // find table name in a structure: ['fromAlias' => [['joinTable' => ''], ...]] + $joinedParts = $queryBuilder->getQueryPart('join'); + foreach ($joinedParts as $joinedTables) { + foreach ($joinedTables as $join) { + if ($join['joinTable'] === $tableName) { + return true; + } + } + } + + return false; + } +} + +class_alias(Base::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\Base'); diff --git a/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalAnd.php b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalAnd.php new file mode 100644 index 0000000000..3367ea0168 --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalAnd.php @@ -0,0 +1,40 @@ +criteria as $subCriterion) { + $subexpressions[] = $converter->convertCriteria($queryBuilder, $subCriterion); + } + + return $queryBuilder->expr()->andX(...$subexpressions); + } +} + +class_alias(LogicalAnd::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalAnd'); diff --git a/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalNot.php b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalNot.php new file mode 100644 index 0000000000..35d8c9208e --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalNot.php @@ -0,0 +1,41 @@ +convertCriteria($queryBuilder, $criterion->criteria[0]) + ); + } +} + +class_alias(LogicalNot::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalNot'); diff --git a/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalOr.php b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalOr.php new file mode 100644 index 0000000000..07642ebb14 --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalOr.php @@ -0,0 +1,43 @@ +criteria as $subCriterion) { + $subexpressions[] = $converter->convertCriteria($queryBuilder, $subCriterion); + } + + return $queryBuilder->expr()->orX(...$subexpressions); + } +} + +class_alias(LogicalOr::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalOr'); diff --git a/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/MatchAll.php b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/MatchAll.php new file mode 100644 index 0000000000..f0374904c7 --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/MatchAll.php @@ -0,0 +1,36 @@ +expr()->like( + 'url', + $queryBuilder->createNamedParameter( + '%' . $criterion->pattern . '%', + ParameterType::STRING, + ':pattern' + ) + ); + } +} + +class_alias(Pattern::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\Pattern'); diff --git a/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/SectionId.php b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/SectionId.php new file mode 100644 index 0000000000..8718f3d120 --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/SectionId.php @@ -0,0 +1,49 @@ +joinContentObjectLink($queryBuilder); + $this->joinContentObjectAttribute($queryBuilder); + $this->joinContentObject($queryBuilder); + + return $queryBuilder->expr()->in( + 'c.section_id', + $queryBuilder->createNamedParameter( + $criterion->sectionIds, + Connection::PARAM_INT_ARRAY, + ':section_ids' + ) + ); + } +} + +class_alias(SectionId::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\SectionId'); diff --git a/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/SectionIdentifier.php b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/SectionIdentifier.php new file mode 100644 index 0000000000..678f067108 --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/SectionIdentifier.php @@ -0,0 +1,57 @@ +joinContentObjectLink($queryBuilder); + $this->joinContentObjectAttribute($queryBuilder); + $this->joinContentObject($queryBuilder); + + $queryBuilder->innerJoin( + 'c', + SectionGateway::CONTENT_SECTION_TABLE, + 's', + 'c.section_id = s.id' + ); + + return $queryBuilder->expr()->in( + 's.identifier', + $queryBuilder->createNamedParameter( + $criterion->sectionIdentifiers, + Connection::PARAM_STR_ARRAY, + ':section_identifiers' + ) + ); + } +} + +class_alias(SectionIdentifier::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\SectionIdentifier'); diff --git a/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/Validity.php b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/Validity.php new file mode 100644 index 0000000000..fafa42df36 --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/Validity.php @@ -0,0 +1,45 @@ +expr()->eq( + 'is_valid', + $queryBuilder->createNamedParameter( + (int)$criterion->isValid, + ParameterType::INTEGER, + ':is_valid' + ) + ); + } +} + +class_alias(Validity::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\Validity'); diff --git a/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/VisibleOnly.php b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/VisibleOnly.php new file mode 100644 index 0000000000..8564418beb --- /dev/null +++ b/src/lib/Persistence/Legacy/URL/Query/CriterionHandler/VisibleOnly.php @@ -0,0 +1,53 @@ +joinContentObjectLink($queryBuilder); + $this->joinContentObjectAttribute($queryBuilder); + + $queryBuilder->innerJoin( + 'f_def', + Gateway::CONTENT_TREE_TABLE, + 't', + $queryBuilder->expr()->andX( + 't.contentobject_id = f_def.contentobject_id', + 't.contentobject_version = f_def.version' + ) + ); + + return $queryBuilder->expr()->eq( + 't.is_invisible', + $queryBuilder->createNamedParameter(0, ParameterType::INTEGER, ':location_is_invisible') + ); + } +} + +class_alias(VisibleOnly::class, 'eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\VisibleOnly'); diff --git a/src/lib/Persistence/Legacy/User/Gateway.php b/src/lib/Persistence/Legacy/User/Gateway.php new file mode 100644 index 0000000000..bfd6e3a47c --- /dev/null +++ b/src/lib/Persistence/Legacy/User/Gateway.php @@ -0,0 +1,80 @@ +connection = $connection; + $this->dbPlatform = $this->connection->getDatabasePlatform(); + } + + public function load(int $userId): array + { + $query = $this->getLoadUserQueryBuilder(); + $query + ->where( + $query->expr()->eq( + 'u.contentobject_id', + $query->createPositionalParameter($userId, ParameterType::INTEGER) + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadByLogin(string $login): array + { + $query = $this->getLoadUserQueryBuilder(); + $expr = $query->expr(); + $query + ->where( + $expr->eq( + $this->dbPlatform->getLowerExpression('u.login'), + // Index is case in-sensitive, on some db's lowercase, so we lowercase $login + $query->createPositionalParameter( + mb_strtolower($login, 'UTF-8'), + ParameterType::STRING + ) + ) + ); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadByEmail(string $email): array + { + $query = $this->getLoadUserQueryBuilder(); + $query->where( + $query->expr()->eq( + 'u.email', + $query->createPositionalParameter($email, ParameterType::STRING) + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadUserByToken(string $hash): array + { + $query = $this->getLoadUserQueryBuilder(); + $query + ->leftJoin( + 'u', + 'ezuser_accountkey', + 'token', + $query->expr()->eq( + 'token.user_id', + 'u.contentobject_id' + ) + ) + ->where( + $query->expr()->eq( + 'token.hash_key', + $query->createPositionalParameter($hash, ParameterType::STRING) + ) + ) + ->andWhere( + $query->expr()->gte( + 'token.time', + $query->createPositionalParameter(time(), ParameterType::INTEGER) + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function updateUserPassword(User $user): void + { + $queryBuilder = $this->connection->createQueryBuilder(); + + $queryBuilder + ->update($this->connection->quoteIdentifier(self::USER_TABLE)) + ->set('password_hash', ':passwordHash') + ->set('password_hash_type', ':passwordHashType') + ->set('password_updated_at', ':passwordUpdatedAt') + ->setParameter(':passwordHash', $user->passwordHash, ParameterType::STRING) + ->setParameter(':passwordHashType', $user->hashAlgorithm, ParameterType::INTEGER) + ->setParameter(':passwordUpdatedAt', $user->passwordUpdatedAt) + ->where( + $queryBuilder->expr()->eq( + $this->connection->quoteIdentifier('contentobject_id'), + ':userId' + ) + ) + ->setParameter(':userId', $user->id, ParameterType::INTEGER); + + $queryBuilder->execute(); + } + + public function updateUserToken(UserTokenUpdateStruct $userTokenUpdateStruct): void + { + $query = $this->connection->createQueryBuilder(); + if (false === $this->userHasToken($userTokenUpdateStruct->userId)) { + $query + ->insert('ezuser_accountkey') + ->values( + [ + 'hash_key' => ':hash_key', + 'time' => ':time', + 'user_id' => ':user_id', + ] + ); + } else { + $query + ->update('ezuser_accountkey') + ->set('hash_key', ':hash_key') + ->set('time', ':time') + ->where('user_id = :user_id'); + } + + $query->setParameter('hash_key', $userTokenUpdateStruct->hashKey, ParameterType::STRING); + $query->setParameter('time', $userTokenUpdateStruct->time, ParameterType::INTEGER); + $query->setParameter('user_id', $userTokenUpdateStruct->userId, ParameterType::INTEGER); + + $query->execute(); + } + + public function expireUserToken(string $hash): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update('ezuser_accountkey') + ->set( + 'time', + $query->createPositionalParameter(0, ParameterType::INTEGER) + )->where( + $query->expr()->eq( + 'hash_key', + $query->createPositionalParameter($hash, ParameterType::STRING) + ) + ); + $query->execute(); + } + + public function assignRole(int $contentId, int $roleId, array $limitation): void + { + foreach ($limitation as $identifier => $values) { + foreach ($values as $value) { + $query = $this->connection->createQueryBuilder(); + $query + ->insert('ezuser_role') + ->values( + [ + 'contentobject_id' => $query->createPositionalParameter( + $contentId, + ParameterType::INTEGER + ), + 'role_id' => $query->createPositionalParameter( + $roleId, + ParameterType::INTEGER + ), + 'limit_identifier' => $query->createPositionalParameter( + $identifier, + ParameterType::STRING + ), + 'limit_value' => $query->createPositionalParameter( + $value, + ParameterType::STRING + ), + ] + ); + $query->execute(); + } + } + } + + public function removeRole(int $contentId, int $roleId): void + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->delete('ezuser_role') + ->where( + $expr->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + 'role_id', + $query->createPositionalParameter($roleId, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + public function removeRoleAssignmentById(int $roleAssignmentId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete('ezuser_role') + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($roleAssignmentId, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + private function getLoadUserQueryBuilder(): QueryBuilder + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select( + 'u.contentobject_id', + 'u.login', + 'u.email', + 'u.password_hash', + 'u.password_hash_type', + 'u.password_updated_at', + 's.is_enabled', + 's.max_login' + ) + ->from('ezuser', 'u') + ->leftJoin( + 'u', + 'ezuser_setting', + 's', + $expr->eq( + 's.user_id', + 'u.contentobject_id' + ) + ); + + return $query; + } + + private function userHasToken(int $userId): bool + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select('token.id') + ->from('ezuser_accountkey', 'token') + ->where( + $expr->eq( + 'token.user_id', + $query->createPositionalParameter( + $userId, + ParameterType::INTEGER + ) + ) + ); + + return !empty($query->execute()->fetch(FetchMode::ASSOCIATIVE)); + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\User\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/User/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/User/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..e4d6fbbc7b --- /dev/null +++ b/src/lib/Persistence/Legacy/User/Gateway/ExceptionConversion.php @@ -0,0 +1,131 @@ +innerGateway = $innerGateway; + } + + public function load(int $userId): array + { + try { + return $this->innerGateway->load($userId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadByLogin(string $login): array + { + try { + return $this->innerGateway->loadByLogin($login); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadByEmail(string $email): array + { + try { + return $this->innerGateway->loadByEmail($email); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadUserByToken(string $hash): array + { + try { + return $this->innerGateway->loadUserByToken($hash); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateUserPassword(User $user): void + { + try { + $this->innerGateway->updateUserPassword($user); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateUserToken(UserTokenUpdateStruct $userTokenUpdateStruct): void + { + try { + $this->innerGateway->updateUserToken($userTokenUpdateStruct); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function expireUserToken(string $hash): void + { + try { + $this->innerGateway->expireUserToken($hash); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function assignRole(int $contentId, int $roleId, array $limitation): void + { + try { + $this->innerGateway->assignRole($contentId, $roleId, $limitation); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function removeRole(int $contentId, int $roleId): void + { + try { + $this->innerGateway->removeRole($contentId, $roleId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function removeRoleAssignmentById(int $roleAssignmentId): void + { + try { + $this->innerGateway->removeRoleAssignmentById($roleAssignmentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\User\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/User/Handler.php b/src/lib/Persistence/Legacy/User/Handler.php new file mode 100644 index 0000000000..c476a49e35 --- /dev/null +++ b/src/lib/Persistence/Legacy/User/Handler.php @@ -0,0 +1,695 @@ +userGateway = $userGateway; + $this->roleGateway = $roleGateway; + $this->mapper = $mapper; + $this->limitationConverter = $limitationConverter; + } + + /** + * Create a user. + * + * The User struct used to create the user will contain an ID which is used + * to reference the user. + * + * @param \Ibexa\Contracts\Core\Persistence\User $user + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function create(User $user) + { + throw new NotImplementedException('This method should not be called, creation is done via content handler.'); + } + + /** + * Loads user with user ID. + * + * @param mixed $userId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If user is not found + * + * @return \Ibexa\Contracts\Core\Persistence\User + */ + public function load($userId) + { + $data = $this->userGateway->load($userId); + + if (empty($data)) { + throw new NotFound('user', $userId); + } + + return $this->mapper->mapUser(reset($data)); + } + + /** + * Loads user with user login. + * + * @param string $login + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If user is not found + * + * @return \Ibexa\Contracts\Core\Persistence\User + */ + public function loadByLogin($login) + { + $data = $this->userGateway->loadByLogin($login); + + if (empty($data)) { + throw new NotFound('user', $login); + } elseif (count($data) > 1) { + throw new LogicException("Found more then one user with login '{$login}'"); + } + + return $this->mapper->mapUser($data[0]); + } + + /** + * Loads user(s) with user email. + * + * As earlier Ibexa versions supported several users having same email (ini config), + * this function may return several users. + * + * @param string $email + * + * @return \Ibexa\Contracts\Core\Persistence\User + */ + public function loadByEmail(string $email): User + { + $data = $this->userGateway->loadByEmail($email); + + if (empty($data)) { + throw new NotFound('user', $email); + } elseif (count($data) > 1) { + throw new LogicException("Found more then one user with login '{$email}'"); + } + + return $this->mapper->mapUser($data[0]); + } + + /** + * Loads user(s) with user email. + * + * As earlier Ibexa versions supported several users having same email (ini config), + * this function may return several users. + * + * @param string $email + * + * @return \Ibexa\Contracts\Core\Persistence\User[] + */ + public function loadUsersByEmail(string $email): array + { + $data = $this->userGateway->loadByEmail($email); + + if (empty($data)) { + return []; + } + + return $this->mapper->mapUsers($data); + } + + /** + * Loads user with user hash. + * + * @param string $hash + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If user is not found + * + * @return \Ibexa\Contracts\Core\Persistence\User + */ + public function loadUserByToken($hash) + { + $data = $this->userGateway->loadUserByToken($hash); + + if (empty($data)) { + throw new NotFound('user', $hash); + } + + return $this->mapper->mapUser(reset($data)); + } + + /** + * Update the user information specified by the user struct. + * + * @param \Ibexa\Contracts\Core\Persistence\User $user + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function update(User $user) + { + throw new NotImplementedException('This method should not be called, update is done via content handler.'); + } + + public function updatePassword(User $user): void + { + $this->userGateway->updateUserPassword($user); + } + + /** + * Update the user token information specified by the userToken struct. + * + * @param \Ibexa\Contracts\Core\Persistence\User\UserTokenUpdateStruct $userTokenUpdateStruct + */ + public function updateUserToken(UserTokenUpdateStruct $userTokenUpdateStruct) + { + $this->userGateway->updateUserToken($userTokenUpdateStruct); + } + + /** + * Expires user account key with user hash. + * + * @param string $hash + */ + public function expireUserToken($hash) + { + $this->userGateway->expireUserToken($hash); + } + + /** + * Delete user with the given ID. + * + * @param mixed $userId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function delete($userId) + { + throw new NotImplementedException('This method should not be called, delete is done via content handler.'); + } + + /** + * Create new role draft. + * + * Sets status to Role::STATUS_DRAFT on the new returned draft. + * + * @param \Ibexa\Contracts\Core\Persistence\User\RoleCreateStruct $createStruct + * + * @return \Ibexa\Contracts\Core\Persistence\User\Role + */ + public function createRole(RoleCreateStruct $createStruct) + { + return $this->internalCreateRole($createStruct); + } + + /** + * Creates a draft of existing defined role. + * + * Sets status to Role::STATUS_DRAFT on the new returned draft. + * + * @param mixed $roleId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If role with defined status is not found + * + * @return \Ibexa\Contracts\Core\Persistence\User\Role + */ + public function createRoleDraft($roleId) + { + $createStruct = $this->mapper->createCreateStructFromRole( + $this->loadRole($roleId) + ); + + return $this->internalCreateRole($createStruct, $roleId); + } + + /** + * Internal method for creating Role. + * + * Used by self::createRole() and self::createRoleDraft() + * + * @param \Ibexa\Contracts\Core\Persistence\User\RoleCreateStruct $createStruct + * @param mixed|null $roleId Used by self::createRoleDraft() to retain Role id in the draft + * + * @return \Ibexa\Contracts\Core\Persistence\User\Role + */ + protected function internalCreateRole(RoleCreateStruct $createStruct, $roleId = null) + { + $createStruct = clone $createStruct; + $role = $this->mapper->createRoleFromCreateStruct( + $createStruct + ); + $role->id = $roleId; + $role->status = Role::STATUS_DRAFT; + + $this->roleGateway->createRole($role); + + foreach ($role->policies as $policy) { + $this->addPolicyByRoleDraft($role->id, $policy); + } + + return $role; + } + + public function copyRole(RoleCopyStruct $copyStruct): Role + { + $role = $this->mapper->createRoleFromCopyStruct( + $copyStruct + ); + + $this->roleGateway->copyRole($role); + + foreach ($role->policies as $policy) { + $this->addPolicy($role->id, $policy); + } + + return $role; + } + + /** + * Loads a specified role (draft) by $roleId and $status. + * + * @param mixed $roleId + * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If role with given status does not exist + * + * @return \Ibexa\Contracts\Core\Persistence\User\Role + */ + public function loadRole($roleId, $status = Role::STATUS_DEFINED) + { + $data = $this->roleGateway->loadRole($roleId, $status); + + if (empty($data)) { + throw new RoleNotFound($roleId, $status); + } + + $role = $this->mapper->mapRole($data); + foreach ($role->policies as $policy) { + $this->limitationConverter->toSPI($policy); + } + + return $role; + } + + /** + * Loads a specified role (draft) by $identifier and $status. + * + * @param string $identifier + * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If role is not found + * + * @return \Ibexa\Contracts\Core\Persistence\User\Role + */ + public function loadRoleByIdentifier($identifier, $status = Role::STATUS_DEFINED) + { + $data = $this->roleGateway->loadRoleByIdentifier($identifier, $status); + + if (empty($data)) { + throw new RoleNotFound($identifier, $status); + } + + $role = $this->mapper->mapRole($data); + foreach ($role->policies as $policy) { + $this->limitationConverter->toSPI($policy); + } + + return $role; + } + + /** + * Loads a role draft by the original role ID. + * + * @param mixed $roleId ID of the role the draft was created from. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If role is not found + * + * @return \Ibexa\Contracts\Core\Persistence\User\Role + */ + public function loadRoleDraftByRoleId($roleId) + { + $data = $this->roleGateway->loadRoleDraftByRoleId($roleId); + + if (empty($data)) { + throw new RoleNotFound($roleId, Role::STATUS_DRAFT); + } + + $role = $this->mapper->mapRole($data); + foreach ($role->policies as $policy) { + $this->limitationConverter->toSPI($policy); + } + + return $role; + } + + /** + * Loads all roles. + * + * @return \Ibexa\Contracts\Core\Persistence\User\Role[] + */ + public function loadRoles() + { + $data = $this->roleGateway->loadRoles(); + + $roles = $this->mapper->mapRoles($data); + foreach ($roles as $role) { + foreach ($role->policies as $policy) { + $this->limitationConverter->toSPI($policy); + } + } + + return $roles; + } + + /** + * Update role (draft). + * + * @param \Ibexa\Contracts\Core\Persistence\User\RoleUpdateStruct $role + */ + public function updateRole(RoleUpdateStruct $role) + { + $this->roleGateway->updateRole($role); + } + + /** + * Delete the specified role (draft). + * + * @param mixed $roleId + * @param int $status One of Role::STATUS_DEFINED|Role::STATUS_DRAFT + */ + public function deleteRole($roleId, $status = Role::STATUS_DEFINED) + { + $role = $this->loadRole($roleId, $status); + + foreach ($role->policies as $policy) { + $this->roleGateway->removePolicy($policy->id); + } + + $this->roleGateway->deleteRole($role->id, $status); + } + + /** + * Publish the specified role draft. + * + * @param mixed $roleDraftId + */ + public function publishRoleDraft($roleDraftId) + { + $roleDraft = $this->loadRole($roleDraftId, Role::STATUS_DRAFT); + + try { + $originalRoleId = $roleDraft->originalId; + $role = $this->loadRole($originalRoleId); + $roleAssignments = $this->loadRoleAssignmentsByRoleId($role->id); + $this->deleteRole($role->id); + + foreach ($roleAssignments as $roleAssignment) { + if (empty($roleAssignment->limitationIdentifier)) { + $this->assignRole($roleAssignment->contentId, $originalRoleId); + } else { + $this->assignRole( + $roleAssignment->contentId, + $originalRoleId, + [$roleAssignment->limitationIdentifier => $roleAssignment->values] + ); + } + } + $this->roleGateway->publishRoleDraft($roleDraft->id, $role->id); + } catch (NotFound $e) { + // If no published role is found, only publishing is needed, without specifying original role ID as there is none. + $this->roleGateway->publishRoleDraft($roleDraft->id); + } + } + + /** + * Adds a policy to a role draft. + * + * @param mixed $roleId + * @param \Ibexa\Contracts\Core\Persistence\User\Policy $policy + * + * @return \Ibexa\Contracts\Core\Persistence\User\Policy + */ + public function addPolicyByRoleDraft($roleId, Policy $policy) + { + $legacyPolicy = clone $policy; + $legacyPolicy->originalId = $policy->id; + $this->limitationConverter->toLegacy($legacyPolicy); + + $this->roleGateway->addPolicy($roleId, $legacyPolicy); + $policy->id = $legacyPolicy->id; + $policy->originalId = $legacyPolicy->originalId; + $policy->roleId = $legacyPolicy->roleId; + + return $policy; + } + + /** + * Adds a policy to a role. + * + * @param mixed $roleId + * @param \Ibexa\Contracts\Core\Persistence\User\Policy $policy + * + * @return \Ibexa\Contracts\Core\Persistence\User\Policy + */ + public function addPolicy($roleId, Policy $policy) + { + $legacyPolicy = clone $policy; + $this->limitationConverter->toLegacy($legacyPolicy); + + $this->roleGateway->addPolicy($roleId, $legacyPolicy); + $policy->id = $legacyPolicy->id; + $policy->roleId = $legacyPolicy->roleId; + + return $policy; + } + + /** + * Update a policy. + * + * Replaces limitations values with new values. + * + * @param \Ibexa\Contracts\Core\Persistence\User\Policy $policy + */ + public function updatePolicy(Policy $policy) + { + $policy = clone $policy; + $this->limitationConverter->toLegacy($policy); + + $this->roleGateway->removePolicyLimitations($policy->id); + $this->roleGateway->addPolicyLimitations($policy->id, $policy->limitations === '*' ? [] : $policy->limitations); + } + + /** + * Removes a policy from a role. + * + * @param mixed $policyId + * @param mixed $roleId + */ + public function deletePolicy($policyId, $roleId) + { + // Each policy can only be associated to exactly one role. Thus it is + // sufficient to use the policyId for identification and just remove + // the policy completely. + $this->roleGateway->removePolicy($policyId); + } + + /** + * Returns the user policies associated with the user (including inherited policies from user groups). + * + * @param mixed $userId + * + * @return \Ibexa\Contracts\Core\Persistence\User\Policy[] + */ + public function loadPoliciesByUserId($userId) + { + $data = $this->roleGateway->loadPoliciesByUserId($userId); + + $policies = $this->mapper->mapPolicies($data); + + foreach ($policies as $policy) { + $this->limitationConverter->toSPI($policy); + } + + return $policies; + } + + /** + * Assigns role to a user or user group with given limitations. + * + * The limitation array looks like: + * + * array( + * 'Subtree' => array( + * '/1/2/', + * '/1/4/', + * ), + * 'Foo' => array( 'Bar' ), + * … + * ) + * + * + * Where the keys are the limitation identifiers, and the respective values + * are an array of limitation values. The limitation parameter is optional. + * + * @param mixed $contentId The groupId or userId to assign the role to. + * @param mixed $roleId + * @param array $limitation + */ + public function assignRole($contentId, $roleId, array $limitation = null) + { + $limitation = $limitation ?: ['' => ['']]; + $this->userGateway->assignRole($contentId, $roleId, $limitation); + } + + /** + * Un-assign a role. + * + * @param mixed $contentId The user or user group Id to un-assign the role from. + * @param mixed $roleId + */ + public function unassignRole($contentId, $roleId) + { + $this->userGateway->removeRole($contentId, $roleId); + } + + /** + * Un-assign a role by assignment ID. + * + * @param mixed $roleAssignmentId The assignment ID. + */ + public function removeRoleAssignment($roleAssignmentId) + { + $this->userGateway->removeRoleAssignmentById($roleAssignmentId); + } + + /** + * Loads role assignment for specified assignment ID. + * + * @param mixed $roleAssignmentId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If role assignment is not found + * + * @return \Ibexa\Contracts\Core\Persistence\User\RoleAssignment + */ + public function loadRoleAssignment($roleAssignmentId) + { + $data = $this->roleGateway->loadRoleAssignment($roleAssignmentId); + + if (empty($data)) { + throw new NotFound('roleAssignment', $roleAssignmentId); + } + + return $this->mapper->mapRoleAssignments($data)[0]; + } + + /** + * Loads roles assignments Role. + * + * Role Assignments with same roleId and limitationIdentifier will be merged together into one. + * + * @param mixed $roleId + * + * @return \Ibexa\Contracts\Core\Persistence\User\RoleAssignment[] + */ + public function loadRoleAssignmentsByRoleId($roleId) + { + $data = $this->roleGateway->loadRoleAssignmentsByRoleId($roleId); + + if (empty($data)) { + return []; + } + + return $this->mapper->mapRoleAssignments($data); + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\User\RoleAssignment[] + */ + public function loadRoleAssignmentsByRoleIdWithOffsetAndLimit(int $roleId, int $offset, ?int $limit): array + { + $data = $this->roleGateway->loadRoleAssignmentsByRoleIdWithOffsetAndLimit($roleId, $offset, $limit); + + if (empty($data)) { + return []; + } + + return $this->mapper->mapRoleAssignments($data); + } + + public function countRoleAssignments(int $roleId): int + { + return $this->roleGateway->countRoleAssignments($roleId); + } + + /** + * Loads roles assignments to a user/group. + * + * Role Assignments with same roleId and limitationIdentifier will be merged together into one. + * + * @param mixed $groupId In legacy storage engine this is the content object id roles are assigned to in ezuser_role. + * By the nature of legacy this can currently also be used to get by $userId. + * @param bool $inherit If true also return inherited role assignments from user groups. + * + * @return \Ibexa\Contracts\Core\Persistence\User\RoleAssignment[] + */ + public function loadRoleAssignmentsByGroupId($groupId, $inherit = false) + { + $data = $this->roleGateway->loadRoleAssignmentsByGroupId($groupId, $inherit); + + if (empty($data)) { + return []; + } + + return $this->mapper->mapRoleAssignments($data); + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\User\Handler'); diff --git a/src/lib/Persistence/Legacy/User/Mapper.php b/src/lib/Persistence/Legacy/User/Mapper.php new file mode 100644 index 0000000000..df7abea750 --- /dev/null +++ b/src/lib/Persistence/Legacy/User/Mapper.php @@ -0,0 +1,255 @@ +id = (int)$data['contentobject_id']; + $user->login = $data['login']; + $user->email = $data['email']; + $user->passwordHash = $data['password_hash']; + $user->hashAlgorithm = (int)$data['password_hash_type']; + $user->passwordUpdatedAt = $data['password_updated_at'] !== null ? (int)$data['password_updated_at'] : null; + $user->isEnabled = (bool)$data['is_enabled']; + $user->maxLogin = $data['max_login']; + + return $user; + } + + /** + * Map data for a set of user data. + * + * @param array $data + * + * @return \Ibexa\Contracts\Core\Persistence\User[] + */ + public function mapUsers(array $data) + { + $users = []; + foreach ($data as $row) { + $users[] = $this->mapUser($row); + } + + return $users; + } + + /** + * Map policy data to an array of policies. + * + * @param array $data + * + * @return \Ibexa\Contracts\Core\Persistence\User\Policy[] + */ + public function mapPolicies(array $data) + { + /** @var \Ibexa\Contracts\Core\Persistence\User\Policy[] */ + $policies = []; + foreach ($data as $row) { + $policyId = $row['ezpolicy_id']; + if (!isset($policies[$policyId]) && ($policyId !== null)) { + $originalId = null; + if ($row['ezpolicy_original_id']) { + $originalId = (int)$row['ezpolicy_original_id']; + } elseif ($row['ezrole_version']) { + $originalId = (int)$policyId; + } + + $policies[$policyId] = new Policy( + [ + 'id' => (int)$policyId, + 'roleId' => (int)$row['ezrole_id'], + 'originalId' => $originalId, + 'module' => $row['ezpolicy_module_name'], + 'function' => $row['ezpolicy_function_name'], + 'limitations' => '*', // limitations must be '*' if not a non empty array of limitations + ] + ); + } + + if (!$row['ezpolicy_limitation_identifier']) { + continue; + } elseif ($policies[$policyId]->limitations === '*') { + $policies[$policyId]->limitations = []; + } + + if (!isset($policies[$policyId]->limitations[$row['ezpolicy_limitation_identifier']])) { + $policies[$policyId]->limitations[$row['ezpolicy_limitation_identifier']] = [$row['ezpolicy_limitation_value_value']]; + } elseif (!in_array($row['ezpolicy_limitation_value_value'], $policies[$policyId]->limitations[$row['ezpolicy_limitation_identifier']])) { + $policies[$policyId]->limitations[$row['ezpolicy_limitation_identifier']][] = $row['ezpolicy_limitation_value_value']; + } + } + + return array_values($policies); + } + + /** + * Map role data to a role. + * + * @return \Ibexa\Contracts\Core\Persistence\User\Role + */ + public function mapRole(array $data) + { + $role = new Role(); + + foreach ($data as $row) { + if (empty($role->id)) { + $role->id = (int)$row['ezrole_id']; + $role->identifier = $row['ezrole_name']; + $role->status = $row['ezrole_version'] != 0 ? Role::STATUS_DRAFT : Role::STATUS_DEFINED; + $role->originalId = $row['ezrole_version'] ? (int)$row['ezrole_version'] : Role::STATUS_DEFINED; + // skip name and description as they don't exist in legacy + } + } + + $role->policies = $this->mapPolicies($data); + + return $role; + } + + /** + * Map data for a set of roles. + * + * @return \Ibexa\Contracts\Core\Persistence\User\Role[] + */ + public function mapRoles(array $data) + { + $roleData = []; + foreach ($data as $row) { + $roleData[$row['ezrole_id']][] = $row; + } + + $roles = []; + foreach ($roleData as $data) { + $roles[] = $this->mapRole($data); + } + + return $roles; + } + + /** + * Map data for a set of role assignments. + * + * @param array $data + * + * @return \Ibexa\Contracts\Core\Persistence\User\RoleAssignment[] + */ + public function mapRoleAssignments(array $data) + { + $roleAssignmentData = []; + foreach ($data as $row) { + $id = (int)$row['id']; + $roleId = (int)$row['role_id']; + $contentId = (int)$row['contentobject_id']; + // if user already have full access to a role, continue + if (isset($roleAssignmentData[$roleId][$contentId]) + && $roleAssignmentData[$roleId][$contentId] instanceof RoleAssignment) { + continue; + } + + $limitIdentifier = $row['limit_identifier']; + if (!empty($limitIdentifier)) { + $roleAssignmentData[$roleId][$contentId][$limitIdentifier][$id] = new RoleAssignment( + [ + 'id' => $id, + 'roleId' => $roleId, + 'contentId' => $contentId, + 'limitationIdentifier' => $limitIdentifier, + 'values' => [$row['limit_value']], + ] + ); + } else { + $roleAssignmentData[$roleId][$contentId] = new RoleAssignment( + [ + 'id' => $id, + 'roleId' => $roleId, + 'contentId' => $contentId, + ] + ); + } + } + + $roleAssignments = []; + array_walk_recursive( + $roleAssignmentData, + static function ($roleAssignment) use (&$roleAssignments) { + $roleAssignments[] = $roleAssignment; + } + ); + + return $roleAssignments; + } + + /** + * Creates a create struct from an existing $role. + * + * @param \Ibexa\Contracts\Core\Persistence\User\Role $role + * + * @return \Ibexa\Contracts\Core\Persistence\User\RoleCreateStruct + */ + public function createCreateStructFromRole(Role $role) + { + $createStruct = new RoleCreateStruct(); + + $createStruct->identifier = $role->identifier; + $createStruct->policies = $role->policies; + + return $createStruct; + } + + /** + * Maps properties from $struct to $role. + * + * @param \Ibexa\Contracts\Core\Persistence\User\RoleCreateStruct $createStruct + * + * @return \Ibexa\Contracts\Core\Persistence\User\Role + */ + public function createRoleFromCreateStruct(RoleCreateStruct $createStruct) + { + $role = new Role(); + + $role->identifier = $createStruct->identifier; + $role->policies = $createStruct->policies; + $role->status = Role::STATUS_DRAFT; + + return $role; + } + + /** + * Maps properties from $struct to $role. + */ + public function createRoleFromCopyStruct(User\RoleCopyStruct $copyStruct): Role + { + $role = new Role(); + + $role->identifier = $copyStruct->newIdentifier; + $role->policies = $copyStruct->policies; + $role->status = Role::STATUS_DEFINED; + + return $role; + } +} + +class_alias(Mapper::class, 'eZ\Publish\Core\Persistence\Legacy\User\Mapper'); diff --git a/src/lib/Persistence/Legacy/User/Role/Gateway.php b/src/lib/Persistence/Legacy/User/Role/Gateway.php new file mode 100644 index 0000000000..f3d33d6722 --- /dev/null +++ b/src/lib/Persistence/Legacy/User/Role/Gateway.php @@ -0,0 +1,171 @@ +connection = $connection; + $this->dbPlatform = $this->connection->getDatabasePlatform(); + } + + public function createRole(Role $role): Role + { + // Role original ID is set when creating a draft from an existing role + if ($role->status === Role::STATUS_DRAFT && $role->id) { + $roleOriginalId = $role->id; + } elseif ($role->status === Role::STATUS_DRAFT) { + // Not using a constant here as this is legacy storage engine specific. + // -1 means "Newly created role". + $roleOriginalId = -1; + } else { + // Role::STATUS_DEFINED value is 0, which is the expected value for version column for this status. + $roleOriginalId = Role::STATUS_DEFINED; + } + + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::ROLE_TABLE) + ->values( + [ + 'is_new' => $query->createPositionalParameter(0, ParameterType::INTEGER), + 'name' => $query->createPositionalParameter( + $role->identifier, + ParameterType::STRING + ), + 'value' => $query->createPositionalParameter(0, ParameterType::INTEGER), + // BC: "version" stores originalId when creating a draft from an existing role + 'version' => $query->createPositionalParameter( + $roleOriginalId, + ParameterType::STRING + ), + ] + ); + $query->execute(); + + if (!isset($role->id) || (int)$role->id < 1 || $role->status === Role::STATUS_DRAFT) { + $role->id = (int)$this->connection->lastInsertId(self::ROLE_SEQ); + } + + $role->originalId = $roleOriginalId; + + return $role; + } + + public function copyRole(Role $role): Role + { + $status = $role->status; + + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::ROLE_TABLE) + ->values( + [ + 'is_new' => $query->createPositionalParameter(0, ParameterType::INTEGER), + 'name' => $query->createPositionalParameter( + $role->identifier, + ParameterType::STRING + ), + 'value' => $query->createPositionalParameter(0, ParameterType::INTEGER), + // BC: "version" stores originalId when creating a draft from an existing role + 'version' => $query->createPositionalParameter( + $status, + ParameterType::STRING + ), + ] + ); + $query->execute(); + + $role->id = (int)$this->connection->lastInsertId(self::ROLE_SEQ); + + return $role; + } + + private function getLoadRoleQueryBuilder(): QueryBuilder + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + 'r.id AS ezrole_id', + 'r.name AS ezrole_name', + 'r.version AS ezrole_version', + 'p.id AS ezpolicy_id', + 'p.function_name AS ezpolicy_function_name', + 'p.module_name AS ezpolicy_module_name', + 'p.original_id AS ezpolicy_original_id', + 'l.identifier AS ezpolicy_limitation_identifier', + 'v.value AS ezpolicy_limitation_value_value' + ) + ->from(self::ROLE_TABLE, 'r') + ->leftJoin('r', self::POLICY_TABLE, 'p', 'p.role_id = r.id') + ->leftJoin('p', self::POLICY_LIMITATION_TABLE, 'l', 'l.policy_id = p.id') + ->leftJoin('l', self::POLICY_LIMITATION_VALUE_TABLE, 'v', 'v.limitation_id = l.id'); + + return $query; + } + + /** + * @throws \Doctrine\DBAL\Exception + * @throws \Doctrine\DBAL\Driver\Exception + */ + public function loadRole(int $roleId, int $status = Role::STATUS_DEFINED): array + { + $query = $this->getLoadRoleQueryBuilder(); + $query + ->where( + $query->expr()->eq( + 'r.id', + $query->createPositionalParameter($roleId, ParameterType::INTEGER) + ) + ) + ->andWhere( + $this->buildRoleDraftQueryConstraint($status, $query) + ) + ->orderBy('p.id', 'ASC') + ->addOrderBy('l.identifier', 'ASC') + ->addOrderBy('v.value', 'ASC'); + + return $query->execute()->fetchAllAssociative(); + } + + /** + * @throws \Doctrine\DBAL\Exception + * @throws \Doctrine\DBAL\Driver\Exception + */ + public function loadRoleByIdentifier( + string $identifier, + int $status = Role::STATUS_DEFINED + ): array { + $query = $this->getLoadRoleQueryBuilder(); + $query + ->where( + $query->expr()->eq( + 'r.name', + $query->createPositionalParameter($identifier, ParameterType::STRING) + ) + ) + ->andWhere( + $this->buildRoleDraftQueryConstraint($status, $query) + ) + ->orderBy('p.id', 'ASC') + ->addOrderBy('l.identifier', 'ASC') + ->addOrderBy('v.value', 'ASC'); + + return $query->execute()->fetchAllAssociative(); + } + + public function loadRoleDraftByRoleId(int $roleId): array + { + $query = $this->getLoadRoleQueryBuilder(); + // BC: "version" stores originalId when creating a draft from an existing role + $query + ->where( + $query->expr()->eq( + 'r.version', + $query->createPositionalParameter($roleId, ParameterType::STRING) + ) + ) + ->orderBy('p.id', 'ASC'); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadRoles(int $status = Role::STATUS_DEFINED): array + { + $query = $this->getLoadRoleQueryBuilder(); + $query->where( + $this->buildRoleDraftQueryConstraint($status, $query) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadRolesForContentObjects( + array $contentIds, + int $status = Role::STATUS_DEFINED + ): array { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select( + 'ur.contentobject_id AS ezuser_role_contentobject_id', + 'r.id AS ezrole_id', + 'r.name AS ezrole_name', + 'r.version AS ezrole_version', + 'p.id AS ezpolicy_id', + 'p.function_name AS ezpolicy_function_name', + 'p.module_name AS ezpolicy_module_name', + 'p.original_id AS ezpolicy_original_id', + 'l.identifier AS ezpolicy_limitation_identifier', + 'v.value AS ezpolicy_limitation_value_value' + ) + ->from(self::USER_ROLE_TABLE, 'urs') + ->leftJoin( + 'urs', + self::ROLE_TABLE, + 'r', + // BC: for drafts the "version" column contains the original role ID + $expr->eq( + $status === Role::STATUS_DEFINED ? 'r.id' : 'r.version', + 'urs.role_id' + ) + ) + ->leftJoin('r', self::USER_ROLE_TABLE, 'ur', 'ur.role_id = r.id') + ->leftJoin('r', self::POLICY_TABLE, 'p', 'p.role_id = r.id') + ->leftJoin('p', self::POLICY_LIMITATION_TABLE, 'l', 'l.policy_id = p.id') + ->leftJoin('l', self::POLICY_LIMITATION_VALUE_TABLE, 'v', 'v.limitation_id = l.id') + ->where( + $expr->in( + 'urs.contentobject_id', + $query->createPositionalParameter($contentIds, Connection::PARAM_INT_ARRAY) + ) + ); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadRoleAssignment(int $roleAssignmentId): array + { + $query = $this->connection->createQueryBuilder(); + $query->select( + 'id', + 'contentobject_id', + 'limit_identifier', + 'limit_value', + 'role_id' + )->from( + self::USER_ROLE_TABLE + )->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($roleAssignmentId, ParameterType::INTEGER) + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadRoleAssignmentsByGroupId(int $groupId, bool $inherited = false): array + { + $query = $this->connection->createQueryBuilder(); + $query->select( + 'id', + 'contentobject_id', + 'limit_identifier', + 'limit_value', + 'role_id' + )->from( + self::USER_ROLE_TABLE + ); + + if ($inherited) { + $groupIds = $this->fetchUserGroups($groupId); + $groupIds[] = $groupId; + $query->where( + $query->expr()->in( + 'contentobject_id', + $groupIds + ) + ); + } else { + $query->where( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter($groupId, ParameterType::INTEGER) + ) + ); + } + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function loadRoleAssignmentsByRoleId(int $roleId): array + { + $query = $this->connection->createQueryBuilder(); + $query->select( + 'id', + 'contentobject_id', + 'limit_identifier', + 'limit_value', + 'role_id' + )->from( + self::USER_ROLE_TABLE + )->where( + $query->expr()->eq( + 'role_id', + $query->createPositionalParameter($roleId, ParameterType::INTEGER) + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } + + /** + * @throws \Doctrine\DBAL\Driver\Exception + * @throws \Doctrine\DBAL\Exception + */ + public function loadRoleAssignmentsByRoleIdWithOffsetAndLimit(int $roleId, int $offset, ?int $limit): array + { + $query = $this + ->buildLoadRoleAssignmentsQuery( + [ + 'user_role.id', + 'user_role.contentobject_id', + 'user_role.limit_identifier', + 'user_role.limit_value', + 'user_role.role_id', + ], + $roleId + ) + ->setFirstResult($offset); + + if ($limit !== null) { + $query->setMaxResults($limit); + } + + return $query + ->execute() + ->fetchAllAssociative(); + } + + /** + * @throws \Doctrine\DBAL\Driver\Exception + * @throws \Doctrine\DBAL\Exception + */ + public function countRoleAssignments(int $roleId): int + { + $query = $this->buildLoadRoleAssignmentsQuery( + [$this->connection->getDatabasePlatform()->getCountExpression('user_role.id')], + $roleId + ); + + return (int)$query->execute()->fetchOne(); + } + + /** + * @param array $columns + */ + private function buildLoadRoleAssignmentsQuery(array $columns, int $roleId): QueryBuilder + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$columns) + ->from(self::USER_ROLE_TABLE, 'user_role') + ->innerJoin( + 'user_role', + ContentGateway::CONTENT_ITEM_TABLE, + 'content_object', + 'user_role.contentobject_id = content_object.id' + )->where( + $query->expr()->eq( + 'role_id', + $query->createPositionalParameter($roleId, ParameterType::INTEGER) + ) + ); + + return $query; + } + + public function loadPoliciesByUserId(int $userId): array + { + $groupIds = $this->fetchUserGroups($userId); + $groupIds[] = $userId; + + return $this->loadRolesForContentObjects($groupIds); + } + + /** + * Fetch all group IDs the user belongs to. + * + * This method will return Content ids of all ancestor Locations for the given $userId. + * Note that not all of these might be used as user groups, + * but we will need to check all of them. + * + * @param int $userId + * + * @return array + */ + private function fetchUserGroups(int $userId): array + { + $nodeIDs = $this->getAncestorLocationIdsForUser($userId); + + if (empty($nodeIDs)) { + return []; + } + + $query = $this->connection->createQueryBuilder(); + $query + ->select('c.id') + ->from('ezcontentobject_tree', 't') + ->innerJoin('t', 'ezcontentobject', 'c', 'c.id = t.contentobject_id') + ->where( + $query->expr()->in( + 't.node_id', + $nodeIDs + ) + ); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::COLUMN); + } + + public function updateRole(RoleUpdateStruct $role): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::ROLE_TABLE) + ->set( + 'name', + $query->createPositionalParameter($role->identifier, ParameterType::STRING) + ) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($role->id, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + public function deleteRole(int $roleId, int $status = Role::STATUS_DEFINED): void + { + $query = $this->connection->createQueryBuilder(); + $expr = $query->expr(); + $query + ->delete(self::ROLE_TABLE) + ->where( + $expr->eq( + 'id', + $query->createPositionalParameter($roleId, ParameterType::INTEGER) + ) + ) + ->andWhere( + $this->buildRoleDraftQueryConstraint($status, $query, self::ROLE_TABLE) + ); + + if ($status !== Role::STATUS_DRAFT) { + $this->deleteRoleAssignments($roleId); + } + $query->execute(); + } + + public function publishRoleDraft(int $roleDraftId, ?int $originalRoleId = null): void + { + $this->markRoleAsPublished($roleDraftId, $originalRoleId); + $this->publishRolePolicies($roleDraftId, $originalRoleId); + } + + public function addPolicy(int $roleId, Policy $policy): Policy + { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::POLICY_TABLE) + ->values( + [ + 'function_name' => $query->createPositionalParameter( + $policy->function, + ParameterType::STRING + ), + 'module_name' => $query->createPositionalParameter( + $policy->module, + ParameterType::STRING + ), + 'original_id' => $query->createPositionalParameter( + $policy->originalId ?? 0, + ParameterType::INTEGER + ), + 'role_id' => $query->createPositionalParameter($roleId, ParameterType::INTEGER), + ] + ); + $query->execute(); + + $policy->id = (int)$this->connection->lastInsertId(self::POLICY_SEQ); + $policy->roleId = $roleId; + + // Handle the only valid non-array value "*" by not inserting + // anything. Still has not been documented by Ibexa. So we + // assume this is the right way to handle it. + if (is_array($policy->limitations)) { + $this->addPolicyLimitations($policy->id, $policy->limitations); + } + + return $policy; + } + + public function addPolicyLimitations(int $policyId, array $limitations): void + { + foreach ($limitations as $identifier => $values) { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::POLICY_LIMITATION_TABLE) + ->values( + [ + 'identifier' => $query->createPositionalParameter( + $identifier, + ParameterType::STRING + ), + 'policy_id' => $query->createPositionalParameter( + $policyId, + ParameterType::INTEGER + ), + ] + ); + $query->execute(); + + $limitationId = (int)$this->connection->lastInsertId(self::POLICY_LIMITATION_SEQ); + + foreach ($values as $value) { + $query = $this->connection->createQueryBuilder(); + $query + ->insert(self::POLICY_LIMITATION_VALUE_TABLE) + ->values( + [ + 'value' => $query->createPositionalParameter( + $value, + ParameterType::STRING + ), + 'limitation_id' => $query->createPositionalParameter( + $limitationId, + ParameterType::INTEGER + ), + ] + ); + $query->execute(); + } + } + } + + public function removePolicy(int $policyId): void + { + $this->removePolicyLimitations($policyId); + + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::POLICY_TABLE) + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($policyId, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + /** + * @param int[] $limitationIds + */ + private function deletePolicyLimitations(array $limitationIds): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::POLICY_LIMITATION_TABLE) + ->where( + $query->expr()->in( + 'id', + $query->createPositionalParameter( + $limitationIds, + Connection::PARAM_INT_ARRAY + ) + ) + ); + $query->execute(); + } + + /** + * @param int[] $limitationValueIds + */ + private function deletePolicyLimitationValues(array $limitationValueIds): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::POLICY_LIMITATION_VALUE_TABLE) + ->where( + $query->expr()->in( + 'id', + $query->createPositionalParameter( + $limitationValueIds, + Connection::PARAM_INT_ARRAY + ) + ) + ); + $query->execute(); + } + + private function loadPolicyLimitationValues(int $policyId): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + 'l.id AS ezpolicy_limitation_id', + 'v.id AS ezpolicy_limitation_value_id' + ) + ->from(self::POLICY_TABLE, 'p') + ->leftJoin('p', self::POLICY_LIMITATION_TABLE, 'l', 'l.policy_id = p.id') + ->leftJoin('l', self::POLICY_LIMITATION_VALUE_TABLE, 'v', 'v.limitation_id = l.id') + ->where( + $query->expr()->eq( + 'p.id', + $query->createPositionalParameter($policyId, ParameterType::INTEGER) + ) + ); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + public function removePolicyLimitations(int $policyId): void + { + $limitationValues = $this->loadPolicyLimitationValues($policyId); + + $limitationIds = array_map( + 'intval', + array_column($limitationValues, 'ezpolicy_limitation_id') + ); + $limitationValueIds = array_map( + 'intval', + array_column($limitationValues, 'ezpolicy_limitation_value_id') + ); + + if (!empty($limitationValueIds)) { + $this->deletePolicyLimitationValues($limitationValueIds); + } + + if (!empty($limitationIds)) { + $this->deletePolicyLimitations($limitationIds); + } + } + + /** + * Delete Role assignments to Users. + */ + private function deleteRoleAssignments(int $roleId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->delete(self::USER_ROLE_TABLE) + ->where( + $query->expr()->eq( + 'role_id', + $query->createPositionalParameter($roleId, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + /** + * Load all Ancestor Location IDs of the given User Location. + * + * @param int $userId + * + * @return int[] + */ + private function getAncestorLocationIdsForUser(int $userId): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('t.path_string') + ->from('ezcontentobject_tree', 't') + ->where( + $query->expr()->eq( + 't.contentobject_id', + $query->createPositionalParameter($userId, ParameterType::STRING) + ) + ); + + $paths = $query->execute()->fetchAll(FetchMode::COLUMN); + $nodeIds = array_unique( + array_reduce( + array_map( + static function ($pathString) { + return array_filter(explode('/', $pathString)); + }, + $paths + ), + 'array_merge_recursive', + [] + ) + ); + + return array_map('intval', $nodeIds); + } + + private function buildRoleDraftQueryConstraint( + int $status, + QueryBuilder $query, + string $columnAlias = 'r' + ): string { + if ($status === Role::STATUS_DEFINED) { + $draftCondition = $query->expr()->eq( + "{$columnAlias}.version", + $query->createPositionalParameter($status, ParameterType::INTEGER) + ); + } else { + // version stores original Role ID when Role is a draft... + $draftCondition = $query->expr()->neq( + "{$columnAlias}.version", + $query->createPositionalParameter(Role::STATUS_DEFINED, ParameterType::INTEGER) + ); + } + + return $draftCondition; + } + + private function markRoleAsPublished(int $roleDraftId, ?int $originalRoleId): void + { + $query = $this->connection->createQueryBuilder(); + $query + ->update(self::ROLE_TABLE) + ->set( + 'version', + $query->createPositionalParameter(Role::STATUS_DEFINED, ParameterType::INTEGER) + ); + // Draft was created from an existing role, so published role must get the original ID. + if ($originalRoleId !== null) { + $query->set( + 'id', + $query->createPositionalParameter($originalRoleId, ParameterType::INTEGER) + ); + } + + $query->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($roleDraftId, ParameterType::INTEGER) + ) + ); + $query->execute(); + } + + private function publishRolePolicies(int $roleDraftId, ?int $originalRoleId): void + { + $policyQuery = $this->connection->createQueryBuilder(); + $policyQuery + ->update(self::POLICY_TABLE) + ->set( + 'original_id', + $policyQuery->createPositionalParameter(0, ParameterType::INTEGER) + ); + // Draft was created from an existing role, so published policies must get the original role ID. + if ($originalRoleId !== null) { + $policyQuery->set( + 'role_id', + $policyQuery->createPositionalParameter($originalRoleId, ParameterType::INTEGER) + ); + } + + $policyQuery->where( + $policyQuery->expr()->eq( + 'role_id', + $policyQuery->createPositionalParameter($roleDraftId, ParameterType::INTEGER) + ) + ); + $policyQuery->execute(); + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..c7429bcc98 --- /dev/null +++ b/src/lib/Persistence/Legacy/User/Role/Gateway/ExceptionConversion.php @@ -0,0 +1,226 @@ +innerGateway = $innerGateway; + } + + public function createRole(Role $role): Role + { + try { + return $this->innerGateway->createRole($role); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function copyRole(Role $role): Role + { + try { + return $this->innerGateway->copyRole($role); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadRole(int $roleId, int $status = Role::STATUS_DEFINED): array + { + try { + return $this->innerGateway->loadRole($roleId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadRoleByIdentifier( + string $identifier, + int $status = Role::STATUS_DEFINED + ): array { + try { + return $this->innerGateway->loadRoleByIdentifier($identifier, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadRoleDraftByRoleId(int $roleId): array + { + try { + return $this->innerGateway->loadRoleDraftByRoleId($roleId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadRoles(int $status = Role::STATUS_DEFINED): array + { + try { + return $this->innerGateway->loadRoles(); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadRolesForContentObjects( + array $contentIds, + int $status = Role::STATUS_DEFINED + ): array { + try { + return $this->innerGateway->loadRolesForContentObjects($contentIds); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadRoleAssignment(int $roleAssignmentId): array + { + try { + return $this->innerGateway->loadRoleAssignment($roleAssignmentId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadRoleAssignmentsByGroupId(int $groupId, bool $inherited = false): array + { + try { + return $this->innerGateway->loadRoleAssignmentsByGroupId($groupId, $inherited); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadRoleAssignmentsByRoleId(int $roleId): array + { + try { + return $this->innerGateway->loadRoleAssignmentsByRoleId($roleId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadRoleAssignmentsByRoleIdWithOffsetAndLimit(int $roleId, int $offset, ?int $limit): array + { + try { + return $this->innerGateway->loadRoleAssignmentsByRoleIdWithOffsetAndLimit($roleId, $offset, $limit); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function countRoleAssignments(int $roleId): int + { + try { + return $this->innerGateway->countRoleAssignments($roleId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function loadPoliciesByUserId(int $userId): array + { + try { + return $this->innerGateway->loadPoliciesByUserId($userId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function updateRole(RoleUpdateStruct $role): void + { + try { + $this->innerGateway->updateRole($role); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function deleteRole(int $roleId, int $status = Role::STATUS_DEFINED): void + { + try { + $this->innerGateway->deleteRole($roleId, $status); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function publishRoleDraft(int $roleDraftId, ?int $originalRoleId = null): void + { + try { + $this->innerGateway->publishRoleDraft($roleDraftId, $originalRoleId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function addPolicy(int $roleId, Policy $policy): Policy + { + try { + return $this->innerGateway->addPolicy($roleId, $policy); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function addPolicyLimitations(int $policyId, array $limitations): void + { + try { + $this->innerGateway->addPolicyLimitations($policyId, $limitations); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function removePolicy(int $policyId): void + { + try { + $this->innerGateway->removePolicy($policyId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } + + public function removePolicyLimitations(int $policyId): void + { + try { + $this->innerGateway->removePolicyLimitations($policyId); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\User\Role\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/User/Role/LimitationConverter.php b/src/lib/Persistence/Legacy/User/Role/LimitationConverter.php new file mode 100644 index 0000000000..ca59e3c758 --- /dev/null +++ b/src/lib/Persistence/Legacy/User/Role/LimitationConverter.php @@ -0,0 +1,62 @@ +limitationHandlers = $limitationHandlers; + } + + /** + * Adds handler. + * + * @param \Ibexa\Core\Persistence\Legacy\User\Role\LimitationHandler $handler + */ + public function addHandler(LimitationHandler $handler) + { + $this->limitationHandlers[] = $handler; + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\User\Policy $policy + */ + public function toLegacy(Policy $policy) + { + foreach ($this->limitationHandlers as $limitationHandler) { + $limitationHandler->toLegacy($policy); + } + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\User\Policy $policy + */ + public function toSPI(Policy $policy) + { + foreach ($this->limitationHandlers as $limitationHandler) { + $limitationHandler->toSPI($policy); + } + } +} + +class_alias(LimitationConverter::class, 'eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter'); diff --git a/src/lib/Persistence/Legacy/User/Role/LimitationHandler.php b/src/lib/Persistence/Legacy/User/Role/LimitationHandler.php new file mode 100644 index 0000000000..89281cea7e --- /dev/null +++ b/src/lib/Persistence/Legacy/User/Role/LimitationHandler.php @@ -0,0 +1,29 @@ +connection = $connection; + } + + abstract public function toLegacy(Policy $policy): void; + + abstract public function toSPI(Policy $policy): void; +} + +class_alias(LimitationHandler::class, 'eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationHandler'); diff --git a/src/lib/Persistence/Legacy/User/Role/LimitationHandler/ObjectStateHandler.php b/src/lib/Persistence/Legacy/User/Role/LimitationHandler/ObjectStateHandler.php new file mode 100644 index 0000000000..4518c477ec --- /dev/null +++ b/src/lib/Persistence/Legacy/User/Role/LimitationHandler/ObjectStateHandler.php @@ -0,0 +1,136 @@ + limitations. + */ + public function toLegacy(Policy $policy): void + { + if ($policy->limitations !== '*' && isset($policy->limitations[Limitation::STATE])) { + if ($policy->limitations[Limitation::STATE] === '*') { + $map = array_fill_keys(array_keys($this->getGroupMap()), '*'); + } else { + $map = $this->getGroupMap($policy->limitations[Limitation::STATE]); + } + $policy->limitations += $map; + unset($policy->limitations[Limitation::STATE]); + } + } + + /** + * Translate Legacy StateGroup_ limitations to API STATE limitation. + */ + public function toSPI(Policy $policy): void + { + if ($policy->limitations === '*' || empty($policy->limitations)) { + return; + } + + // First iterate to prepare for the range of possible conditions below + $hasStateGroup = false; + $allWildCard = true; + $someWildCard = false; + $map = []; + foreach ($policy->limitations as $identifier => $limitationsValues) { + if (strncmp($identifier, self::STATE_GROUP, 11) === 0) { + $hasStateGroup = true; + if ($limitationsValues !== '*') { + $allWildCard = false; + } else { + $someWildCard = true; + } + + $map[$identifier] = $limitationsValues; + unset($policy->limitations[$identifier]); + } + } + + if (!$hasStateGroup) { + return; + } + + if ($allWildCard) { + $policy->limitations[Limitation::STATE] = '*'; + + return; + } + + if ($someWildCard) { + $fullMap = $this->getGroupMap(); + foreach ($map as $identifier => $limitationsValues) { + if ($limitationsValues === '*') { + $map[$identifier] = $fullMap[$identifier]; + } + } + } + + $policy->limitations[Limitation::STATE] = []; + foreach ($map as $limitationValues) { + $policy->limitations[Limitation::STATE] = array_merge( + $policy->limitations[Limitation::STATE], + $limitationValues + ); + } + } + + /** + * Query for groups identifiers and id's. + */ + protected function getGroupMap(array $limitIds = null): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select('sg.identifier', 's.id') + ->from('ezcobj_state', 's') + ->innerJoin( + 's', + 'ezcobj_state_group', + 'sg', + 's.group_id = sg.id' + ); + + if ($limitIds !== null) { + $query->where( + $query->expr()->in( + 's.id', + $query->createPositionalParameter( + array_map('intval', $limitIds), + Connection::PARAM_INT_ARRAY + ) + ) + ); + } + + $statement = $query->execute(); + + $map = []; + $groupValues = $statement->fetchAll(FetchMode::ASSOCIATIVE); + foreach ($groupValues as $groupValue) { + $map[self::STATE_GROUP . $groupValue['identifier']][] = (int)$groupValue['id']; + } + + return $map; + } +} + +class_alias(ObjectStateHandler::class, 'eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationHandler\ObjectStateHandler'); diff --git a/src/lib/Persistence/Legacy/UserPreference/Gateway.php b/src/lib/Persistence/Legacy/UserPreference/Gateway.php new file mode 100644 index 0000000000..78b5347d18 --- /dev/null +++ b/src/lib/Persistence/Legacy/UserPreference/Gateway.php @@ -0,0 +1,51 @@ +connection = $connection; + } + + /** + * {@inheritdoc} + */ + public function setUserPreference(UserPreferenceSetStruct $userPreference): int + { + $query = $this->connection->createQueryBuilder(); + + $userPreferences = $this->getUserPreferenceByUserIdAndName($userPreference->userId, $userPreference->name); + + if (0 < count($userPreferences)) { + $currentUserPreference = reset($userPreferences); + $currentUserPreferenceId = (int)$currentUserPreference['id']; + + $query + ->update(self::TABLE_USER_PREFERENCES) + ->set(self::COLUMN_VALUE, ':value') + ->where($query->expr()->eq(self::COLUMN_ID, ':id')) + ->setParameter(':id', $currentUserPreferenceId, ParameterType::INTEGER) + ->setParameter(':value', $userPreference->value, ParameterType::STRING); + + $query->execute(); + + return $currentUserPreferenceId; + } + + $query + ->insert(self::TABLE_USER_PREFERENCES) + ->values([ + self::COLUMN_NAME => ':name', + self::COLUMN_USER_ID => ':user_id', + self::COLUMN_VALUE => ':value', + ]) + ->setParameter(':name', $userPreference->name, ParameterType::STRING) + ->setParameter(':user_id', $userPreference->userId, ParameterType::INTEGER) + ->setParameter(':value', $userPreference->value, ParameterType::STRING); + + $query->execute(); + + return (int) $this->connection->lastInsertId(); + } + + public function getUserPreferenceByUserIdAndName(int $userId, string $name): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$this->getColumns()) + ->from(self::TABLE_USER_PREFERENCES) + ->where($query->expr()->eq(self::COLUMN_USER_ID, ':userId')) + ->andWhere($query->expr()->eq(self::COLUMN_NAME, ':name')); + + $query->setParameter(':userId', $userId, ParameterType::INTEGER); + $query->setParameter(':name', $name, ParameterType::STRING); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + /** + * {@inheritdoc} + */ + public function loadUserPreferences(int $userId, int $offset = 0, int $limit = -1): array + { + $query = $this->connection->createQueryBuilder(); + $query + ->select(...$this->getColumns()) + ->from(self::TABLE_USER_PREFERENCES) + ->where($query->expr()->eq(self::COLUMN_USER_ID, ':user_id')) + ->setFirstResult($offset); + + if ($limit > 0) { + $query->setMaxResults($limit); + } + + $query->orderBy(self::COLUMN_ID, 'ASC'); + $query->setParameter(':user_id', $userId, ParameterType::INTEGER); + + return $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + } + + /** + * {@inheritdoc} + */ + public function countUserPreferences(int $userId): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select( + $this->connection->getDatabasePlatform()->getCountExpression(self::COLUMN_ID) + ) + ->from(self::TABLE_USER_PREFERENCES) + ->where($query->expr()->eq(self::COLUMN_USER_ID, ':user_id')) + ->setParameter(':user_id', $userId, ParameterType::INTEGER); + + return (int) $query->execute()->fetchColumn(); + } + + private function getColumns(): array + { + return [ + self::COLUMN_ID, + self::COLUMN_NAME, + self::COLUMN_USER_ID, + self::COLUMN_VALUE, + ]; + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway\DoctrineDatabase'); diff --git a/src/lib/Persistence/Legacy/UserPreference/Gateway/ExceptionConversion.php b/src/lib/Persistence/Legacy/UserPreference/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..0c3f6b9d1c --- /dev/null +++ b/src/lib/Persistence/Legacy/UserPreference/Gateway/ExceptionConversion.php @@ -0,0 +1,85 @@ +innerGateway = $innerGateway; + } + + /** + * {@inheritdoc} + */ + public function getUserPreferenceByUserIdAndName(int $userId, string $name): array + { + try { + return $this->innerGateway->getUserPreferenceByUserIdAndName($userId, $name); + } catch (DBALException | PDOException $e) { + throw new RuntimeException('Database error', 0, $e); + } + } + + /** + * {@inheritdoc} + */ + public function countUserPreferences(int $userId): int + { + try { + return $this->innerGateway->countUserPreferences($userId); + } catch (DBALException | PDOException $e) { + throw new RuntimeException('Database error', 0, $e); + } + } + + /** + * {@inheritdoc} + */ + public function loadUserPreferences(int $userId, int $offset = 0, int $limit = -1): array + { + try { + return $this->innerGateway->loadUserPreferences($userId, $offset, $limit); + } catch (DBALException | PDOException $e) { + throw new RuntimeException('Database error', 0, $e); + } + } + + /** + * {@inheritdoc} + */ + public function setUserPreference(UserPreferenceSetStruct $setStruct): int + { + try { + return $this->innerGateway->setUserPreference($setStruct); + } catch (DBALException | PDOException $e) { + throw new RuntimeException('Database error', 0, $e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Persistence\Legacy\UserPreference\Gateway\ExceptionConversion'); diff --git a/src/lib/Persistence/Legacy/UserPreference/Handler.php b/src/lib/Persistence/Legacy/UserPreference/Handler.php new file mode 100644 index 0000000000..5346631719 --- /dev/null +++ b/src/lib/Persistence/Legacy/UserPreference/Handler.php @@ -0,0 +1,83 @@ +gateway = $gateway; + $this->mapper = $mapper; + } + + /** + * {@inheritdoc} + * + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + */ + public function setUserPreference(UserPreferenceSetStruct $setStruct): UserPreference + { + $this->gateway->setUserPreference($setStruct); + + return $this->getUserPreferenceByUserIdAndName($setStruct->userId, $setStruct->name); + } + + /** + * {@inheritdoc} + * + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + */ + public function getUserPreferenceByUserIdAndName(int $userId, string $name): UserPreference + { + $userPreference = $this->mapper->extractUserPreferencesFromRows( + $this->gateway->getUserPreferenceByUserIdAndName($userId, $name) + ); + + if (count($userPreference) < 1) { + throw new NotFoundException('User Preference', $userId . ',' . $name); + } + + return reset($userPreference); + } + + /** + * {@inheritdoc} + */ + public function countUserPreferences(int $userId): int + { + return $this->gateway->countUserPreferences($userId); + } + + /** + * {@inheritdoc} + */ + public function loadUserPreferences(int $userId, int $offset, int $limit): array + { + return $this->mapper->extractUserPreferencesFromRows( + $this->gateway->loadUserPreferences($userId, $offset, $limit) + ); + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Persistence\Legacy\UserPreference\Handler'); diff --git a/src/lib/Persistence/Legacy/UserPreference/Mapper.php b/src/lib/Persistence/Legacy/UserPreference/Mapper.php new file mode 100644 index 0000000000..ea8d97be61 --- /dev/null +++ b/src/lib/Persistence/Legacy/UserPreference/Mapper.php @@ -0,0 +1,51 @@ +extractUserPreferenceFromRow($row); + } + + return $userPreferences; + } + + /** + * Extract UserPreference object from $row. + * + * @param array $row + * + * @return \Ibexa\Contracts\Core\Persistence\UserPreference\UserPreference + */ + private function extractUserPreferenceFromRow(array $row): UserPreference + { + $userPreference = new UserPreference(); + $userPreference->id = (int)$row['id']; + $userPreference->userId = (int)$row['user_id']; + $userPreference->name = $row['name']; + $userPreference->value = $row['value']; + + return $userPreference; + } +} + +class_alias(Mapper::class, 'eZ\Publish\Core\Persistence\Legacy\UserPreference\Mapper'); diff --git a/src/lib/Persistence/Readme.md b/src/lib/Persistence/Readme.md new file mode 100644 index 0000000000..8c258c72f7 --- /dev/null +++ b/src/lib/Persistence/Readme.md @@ -0,0 +1,13 @@ +# Core Persistence + +Contains various implementations of SPI\Persistence, and common libraries for use by them. + +Folder | Description +------------------------|------------ +Cache | SPI Persistence implementation for cache using Symfony Cache (decorated) +Database | Interfaces for emulating Zeta Components Database as it was used in the beginning (planned to be removed) +Doctrine | Doctrine DBAL implementation of "Database" (planned to be removed in favour of direct Doctrine use) +Legacy | SPI Persistence implementation for sql database as used in eZ Publish 4.x "legacy" +Test | Test for common functionality in this folder, including TransformationProcessor (todo: move to ./Common) +TransformationProcessor | Common transformation processor for string transformation use by Storage and Search engines. +`` diff --git a/eZ/Publish/Core/Persistence/TransformationProcessor.php b/src/lib/Persistence/TransformationProcessor.php similarity index 89% rename from eZ/Publish/Core/Persistence/TransformationProcessor.php rename to src/lib/Persistence/TransformationProcessor.php index fdedd1f290..025e321403 100644 --- a/eZ/Publish/Core/Persistence/TransformationProcessor.php +++ b/src/lib/Persistence/TransformationProcessor.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence; +namespace Ibexa\Core\Persistence; -use eZ\Publish\Core\Persistence\TransformationProcessor\PcreCompiler; +use Ibexa\Core\Persistence\TransformationProcessor\PcreCompiler; /** * Interface for processing a set of transformations on a string. @@ -38,7 +38,7 @@ abstract class TransformationProcessor /** * Transformation compiler. * - * @var \eZ\Publish\Core\Persistence\TransformationProcessor\PcreCompiler + * @var \Ibexa\Core\Persistence\TransformationProcessor\PcreCompiler */ protected $compiler = null; @@ -48,7 +48,7 @@ abstract class TransformationProcessor * Through the $ruleFiles array, a list of files with full text * transformation rules is given. * - * @param \eZ\Publish\Core\Persistence\TransformationProcessor\PcreCompiler $compiler + * @param \Ibexa\Core\Persistence\TransformationProcessor\PcreCompiler $compiler * @param array $ruleFiles */ public function __construct(PcreCompiler $compiler, array $ruleFiles = []) @@ -129,3 +129,5 @@ public function transformByGroup($string, $ruleGroup) return $string; } } + +class_alias(TransformationProcessor::class, 'eZ\Publish\Core\Persistence\TransformationProcessor'); diff --git a/src/lib/Persistence/TransformationProcessor/DefinitionBased.php b/src/lib/Persistence/TransformationProcessor/DefinitionBased.php new file mode 100644 index 0000000000..60be031dd7 --- /dev/null +++ b/src/lib/Persistence/TransformationProcessor/DefinitionBased.php @@ -0,0 +1,65 @@ +parser = $parser; + } + + /** + * Loads rules. + * + * @return array + */ + protected function getRules() + { + if ($this->compiledRules === null) { + $rules = []; + foreach ($this->ruleFiles as $file) { + $rules = array_merge( + $rules, + $this->parser->parse($file) + ); + } + + $this->compiledRules = $this->compiler->compile($rules); + } + + return $this->compiledRules; + } +} + +class_alias(DefinitionBased::class, 'eZ\Publish\Core\Persistence\TransformationProcessor\DefinitionBased'); diff --git a/eZ/Publish/Core/Persistence/TransformationProcessor/DefinitionBased/Parser.php b/src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php similarity index 95% rename from eZ/Publish/Core/Persistence/TransformationProcessor/DefinitionBased/Parser.php rename to src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php index d705df35d8..4e2a31de38 100644 --- a/eZ/Publish/Core/Persistence/TransformationProcessor/DefinitionBased/Parser.php +++ b/src/lib/Persistence/TransformationProcessor/DefinitionBased/Parser.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\TransformationProcessor\DefinitionBased; +namespace Ibexa\Core\Persistence\TransformationProcessor\DefinitionBased; -use eZ\Publish\Core\Persistence\TransformationProcessor; +use Ibexa\Core\Persistence\TransformationProcessor; use RuntimeException; /** @@ -180,3 +180,5 @@ protected function filterValues(array $data) return $data; } } + +class_alias(Parser::class, 'eZ\Publish\Core\Persistence\TransformationProcessor\DefinitionBased\Parser'); diff --git a/eZ/Publish/Core/Persistence/TransformationProcessor/PcreCompiler.php b/src/lib/Persistence/TransformationProcessor/PcreCompiler.php similarity index 95% rename from eZ/Publish/Core/Persistence/TransformationProcessor/PcreCompiler.php rename to src/lib/Persistence/TransformationProcessor/PcreCompiler.php index 0fff489902..fc96e7de11 100644 --- a/eZ/Publish/Core/Persistence/TransformationProcessor/PcreCompiler.php +++ b/src/lib/Persistence/TransformationProcessor/PcreCompiler.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\TransformationProcessor; +namespace Ibexa\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Persistence\Utf8Converter; +use Ibexa\Core\Persistence\TransformationProcessor; +use Ibexa\Core\Persistence\Utf8Converter; use RuntimeException; /** @@ -19,14 +19,14 @@ class PcreCompiler /** * Class for converting UTF-8 characters. * - * @var \eZ\Publish\Core\Persistence\Utf8Converter + * @var \Ibexa\Core\Persistence\Utf8Converter */ protected $converter; /** * Construct from UTF8Converter. * - * @param \eZ\Publish\Core\Persistence\Utf8Converter $converter + * @param \Ibexa\Core\Persistence\Utf8Converter $converter */ public function __construct(Utf8Converter $converter) { @@ -286,3 +286,5 @@ private function hexdec(?string $value): int return hexdec(preg_replace('/[^[:xdigit:]]/', '', (string)$value)); } } + +class_alias(PcreCompiler::class, 'eZ\Publish\Core\Persistence\TransformationProcessor\PcreCompiler'); diff --git a/eZ/Publish/Core/Persistence/TransformationProcessor/PreprocessedBased.php b/src/lib/Persistence/TransformationProcessor/PreprocessedBased.php similarity index 76% rename from eZ/Publish/Core/Persistence/TransformationProcessor/PreprocessedBased.php rename to src/lib/Persistence/TransformationProcessor/PreprocessedBased.php index 7909d210ce..2af518212d 100644 --- a/eZ/Publish/Core/Persistence/TransformationProcessor/PreprocessedBased.php +++ b/src/lib/Persistence/TransformationProcessor/PreprocessedBased.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\TransformationProcessor; +namespace Ibexa\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Persistence\TransformationProcessor; +use Ibexa\Core\Persistence\TransformationProcessor; /** * Class for processing a set of transformations, loaded from .tr files, on a string. @@ -16,7 +16,7 @@ class PreprocessedBased extends TransformationProcessor /** * Constructor. * - * @param \eZ\Publish\Core\Persistence\TransformationProcessor\PcreCompiler $compiler + * @param \Ibexa\Core\Persistence\TransformationProcessor\PcreCompiler $compiler * @param string $installDir Base dir for rule loading * @param array $ruleFiles */ @@ -45,3 +45,5 @@ protected function getRules() return $this->compiledRules; } } + +class_alias(PreprocessedBased::class, 'eZ\Publish\Core\Persistence\TransformationProcessor\PreprocessedBased'); diff --git a/eZ/Publish/Core/Persistence/Utf8Converter.php b/src/lib/Persistence/Utf8Converter.php similarity index 98% rename from eZ/Publish/Core/Persistence/Utf8Converter.php rename to src/lib/Persistence/Utf8Converter.php index de71579b25..f5220226cc 100644 --- a/eZ/Publish/Core/Persistence/Utf8Converter.php +++ b/src/lib/Persistence/Utf8Converter.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence; +namespace Ibexa\Core\Persistence; use RuntimeException; @@ -178,3 +178,5 @@ public static function toUnicodeCodepoint($char) return $charCode; } } + +class_alias(Utf8Converter::class, 'eZ\Publish\Core\Persistence\Utf8Converter'); diff --git a/src/lib/Query/QueryFactory.php b/src/lib/Query/QueryFactory.php new file mode 100644 index 0000000000..00b50a3a08 --- /dev/null +++ b/src/lib/Query/QueryFactory.php @@ -0,0 +1,30 @@ +queryTypeRegistry = $queryTypeRegistry; + } + + public function create(string $type, array $parameters = []): Query + { + return $this->queryTypeRegistry->getQueryType($type)->getQuery($parameters); + } +} + +class_alias(QueryFactory::class, 'eZ\Publish\Core\Query\QueryFactory'); diff --git a/src/lib/Query/QueryFactoryInterface.php b/src/lib/Query/QueryFactoryInterface.php new file mode 100644 index 0000000000..4149075b8e --- /dev/null +++ b/src/lib/Query/QueryFactoryInterface.php @@ -0,0 +1,16 @@ +registry[$name]; } } + +class_alias(ArrayQueryTypeRegistry::class, 'eZ\Publish\Core\QueryType\ArrayQueryTypeRegistry'); diff --git a/eZ/Publish/Core/QueryType/BuiltIn/AbstractLocationQueryType.php b/src/lib/QueryType/BuiltIn/AbstractLocationQueryType.php similarity index 81% rename from eZ/Publish/Core/QueryType/BuiltIn/AbstractLocationQueryType.php rename to src/lib/QueryType/BuiltIn/AbstractLocationQueryType.php index 8831456b89..52e4b23d1c 100644 --- a/eZ/Publish/Core/QueryType/BuiltIn/AbstractLocationQueryType.php +++ b/src/lib/QueryType/BuiltIn/AbstractLocationQueryType.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\QueryType\BuiltIn; +namespace Ibexa\Core\QueryType\BuiltIn; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -76,3 +76,5 @@ protected function createQuery(): Query return new LocationQuery(); } } + +class_alias(AbstractLocationQueryType::class, 'eZ\Publish\Core\QueryType\BuiltIn\AbstractLocationQueryType'); diff --git a/eZ/Publish/Core/QueryType/BuiltIn/AbstractQueryType.php b/src/lib/QueryType/BuiltIn/AbstractQueryType.php similarity index 78% rename from eZ/Publish/Core/QueryType/BuiltIn/AbstractQueryType.php rename to src/lib/QueryType/BuiltIn/AbstractQueryType.php index a3b7c75c55..2ae696bea4 100644 --- a/eZ/Publish/Core/QueryType/BuiltIn/AbstractQueryType.php +++ b/src/lib/QueryType/BuiltIn/AbstractQueryType.php @@ -6,18 +6,18 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\QueryType\BuiltIn; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Visibility; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\QueryType\OptionsResolverBasedQueryType; +namespace Ibexa\Core\QueryType\BuiltIn; + +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalAnd; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Subtree; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Visibility; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\QueryType\OptionsResolverBasedQueryType; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -25,13 +25,13 @@ abstract class AbstractQueryType extends OptionsResolverBasedQueryType { public const DEFAULT_LIMIT = 25; - /** @var \eZ\Publish\API\Repository\Repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository */ protected $repository; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ protected $configResolver; - /** @var \eZ\Publish\Core\QueryType\BuiltIn\SortClausesFactoryInterface */ + /** @var \Ibexa\Core\QueryType\BuiltIn\SortClausesFactoryInterface */ private $sortClausesFactory; public function __construct( @@ -133,3 +133,5 @@ private function getRootLocationPathString(): string return $rootLocation->pathString; } } + +class_alias(AbstractQueryType::class, 'eZ\Publish\Core\QueryType\BuiltIn\AbstractQueryType'); diff --git a/src/lib/QueryType/BuiltIn/AncestorsQueryType.php b/src/lib/QueryType/BuiltIn/AncestorsQueryType.php new file mode 100644 index 0000000000..54b402c1f6 --- /dev/null +++ b/src/lib/QueryType/BuiltIn/AncestorsQueryType.php @@ -0,0 +1,42 @@ +resolveLocation($parameters); + + if ($location === null) { + return new MatchNone(); + } + + return new LogicalAnd([ + new Ancestor($location->pathString), + new LogicalNot( + new LocationId($location->id) + ), + ]); + } +} + +class_alias(AncestorsQueryType::class, 'eZ\Publish\Core\QueryType\BuiltIn\AncestorsQueryType'); diff --git a/src/lib/QueryType/BuiltIn/ChildrenQueryType.php b/src/lib/QueryType/BuiltIn/ChildrenQueryType.php new file mode 100644 index 0000000000..c0aded8238 --- /dev/null +++ b/src/lib/QueryType/BuiltIn/ChildrenQueryType.php @@ -0,0 +1,34 @@ +resolveLocation($parameters); + + if ($location === null) { + return new MatchNone(); + } + + return new ParentLocationId($location->id); + } +} + +class_alias(ChildrenQueryType::class, 'eZ\Publish\Core\QueryType\BuiltIn\ChildrenQueryType'); diff --git a/eZ/Publish/Core/QueryType/BuiltIn/GeoLocationQueryType.php b/src/lib/QueryType/BuiltIn/GeoLocationQueryType.php similarity index 81% rename from eZ/Publish/Core/QueryType/BuiltIn/GeoLocationQueryType.php rename to src/lib/QueryType/BuiltIn/GeoLocationQueryType.php index 6d7a30dc64..5713598cde 100644 --- a/eZ/Publish/Core/QueryType/BuiltIn/GeoLocationQueryType.php +++ b/src/lib/QueryType/BuiltIn/GeoLocationQueryType.php @@ -6,12 +6,12 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\QueryType\BuiltIn; +namespace Ibexa\Core\QueryType\BuiltIn; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MapLocationDistance; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\MapLocationDistance; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -69,3 +69,5 @@ protected function getQueryFilter(array $parameters): Criterion ); } } + +class_alias(GeoLocationQueryType::class, 'eZ\Publish\Core\QueryType\BuiltIn\GeoLocationQueryType'); diff --git a/eZ/Publish/Core/QueryType/BuiltIn/RelatedToContentQueryType.php b/src/lib/QueryType/BuiltIn/RelatedToContentQueryType.php similarity index 76% rename from eZ/Publish/Core/QueryType/BuiltIn/RelatedToContentQueryType.php rename to src/lib/QueryType/BuiltIn/RelatedToContentQueryType.php index 1c137f0505..4b02d4949e 100644 --- a/eZ/Publish/Core/QueryType/BuiltIn/RelatedToContentQueryType.php +++ b/src/lib/QueryType/BuiltIn/RelatedToContentQueryType.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\QueryType\BuiltIn; +namespace Ibexa\Core\QueryType\BuiltIn; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\FieldRelation; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\FieldRelation; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -57,3 +57,5 @@ protected function getQueryFilter(array $parameters): Criterion ); } } + +class_alias(RelatedToContentQueryType::class, 'eZ\Publish\Core\QueryType\BuiltIn\RelatedToContentQueryType'); diff --git a/src/lib/QueryType/BuiltIn/SiblingsQueryType.php b/src/lib/QueryType/BuiltIn/SiblingsQueryType.php new file mode 100644 index 0000000000..6176fdad3f --- /dev/null +++ b/src/lib/QueryType/BuiltIn/SiblingsQueryType.php @@ -0,0 +1,33 @@ +resolveLocation($parameters); + + if ($location === null) { + return new MatchNone(); + } + + return Criterion\Sibling::fromLocation($location); + } +} + +class_alias(SiblingsQueryType::class, 'eZ\Publish\Core\QueryType\BuiltIn\SiblingsQueryType'); diff --git a/src/lib/QueryType/BuiltIn/SortClausesFactory.php b/src/lib/QueryType/BuiltIn/SortClausesFactory.php new file mode 100644 index 0000000000..553e5abe01 --- /dev/null +++ b/src/lib/QueryType/BuiltIn/SortClausesFactory.php @@ -0,0 +1,44 @@ +sortClauseParser = $sortClauseArgsParser; + } + + /** + * @throws \Ibexa\Core\QueryType\BuiltIn\SortSpec\Exception\SyntaxErrorException + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause[] + */ + public function createFromSpecification(string $specification): array + { + $lexer = new SortSpecLexer(); + $lexer->tokenize($specification); + + $parser = new SortSpecParser($this->sortClauseParser, $lexer); + + return $parser->parseSortClausesList(); + } +} + +class_alias(SortClausesFactory::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortClausesFactory'); diff --git a/src/lib/QueryType/BuiltIn/SortClausesFactoryInterface.php b/src/lib/QueryType/BuiltIn/SortClausesFactoryInterface.php new file mode 100644 index 0000000000..a4a7c9f1ca --- /dev/null +++ b/src/lib/QueryType/BuiltIn/SortClausesFactoryInterface.php @@ -0,0 +1,24 @@ +match(Token::TYPE_ID)->getValue(); + $args[] = $parser->parseSortDirection(); + + return new CustomField(...$args); + } + + public function supports(string $name): bool + { + return $name === self::SUPPORTED_CLAUSE_NAME; + } +} + +class_alias(CustomFieldSortClauseParser::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\CustomFieldSortClauseParser'); diff --git a/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/DefaultSortClauseParser.php b/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/DefaultSortClauseParser.php new file mode 100644 index 0000000000..1862b99563 --- /dev/null +++ b/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/DefaultSortClauseParser.php @@ -0,0 +1,46 @@ +valueObjectClassMap = $valueObjectClassMap; + } + + public function parse(SortSpecParserInterface $parser, string $name): SortClause + { + if (isset($this->valueObjectClassMap[$name])) { + $class = $this->valueObjectClassMap[$name]; + + return new $class($parser->parseSortDirection()); + } + + throw new UnsupportedSortClauseException($name); + } + + public function supports(string $name): bool + { + return isset($this->valueObjectClassMap[$name]); + } +} + +class_alias(DefaultSortClauseParser::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\DefaultSortClauseParser'); diff --git a/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/FieldSortClauseParser.php b/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/FieldSortClauseParser.php new file mode 100644 index 0000000000..6d1eeaef02 --- /dev/null +++ b/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/FieldSortClauseParser.php @@ -0,0 +1,45 @@ +match(Token::TYPE_ID)->getValue(); + $parser->match(Token::TYPE_DOT); + $args[] = $parser->match(Token::TYPE_ID)->getValue(); + $args[] = $parser->parseSortDirection(); + + return new Field(...$args); + } + + public function supports(string $name): bool + { + return $name === self::SUPPORTED_CLAUSE_NAME; + } +} + +class_alias(FieldSortClauseParser::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\FieldSortClauseParser'); diff --git a/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/MapDistanceSortClauseParser.php b/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/MapDistanceSortClauseParser.php new file mode 100644 index 0000000000..bff9afc51d --- /dev/null +++ b/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/MapDistanceSortClauseParser.php @@ -0,0 +1,48 @@ +match(Token::TYPE_ID)->getValue(); + $parser->match(Token::TYPE_DOT); + $args[] = $parser->match(Token::TYPE_ID)->getValue(); + $args[] = $parser->match(Token::TYPE_FLOAT)->getValueAsFloat(); + $args[] = $parser->match(Token::TYPE_FLOAT)->getValueAsFloat(); + $args[] = $parser->parseSortDirection(); + + return new MapLocationDistance(...$args); + } + + public function supports(string $name): bool + { + return $name === self::SUPPORTED_CLAUSE_NAME; + } +} + +class_alias(MapDistanceSortClauseParser::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\MapDistanceSortClauseParser'); diff --git a/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/RandomSortClauseParser.php b/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/RandomSortClauseParser.php new file mode 100644 index 0000000000..5a452bb3b8 --- /dev/null +++ b/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/RandomSortClauseParser.php @@ -0,0 +1,47 @@ +isNextToken(Token::TYPE_INT)) { + $seed = $parser->match(Token::TYPE_INT)->getValueAsInt(); + } + + $sortDirection = $parser->parseSortDirection(); + + return new Random($seed, $sortDirection); + } + + public function supports(string $name): bool + { + return $name === self::SUPPORTED_CLAUSE_NAME; + } +} + +class_alias(RandomSortClauseParser::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\RandomSortClauseParser'); diff --git a/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParserDispatcher.php b/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParserDispatcher.php new file mode 100644 index 0000000000..8b27706613 --- /dev/null +++ b/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParserDispatcher.php @@ -0,0 +1,51 @@ +parsers = $parsers; + } + + public function parse(SortSpecParserInterface $parser, string $name): SortClause + { + $sortClauseParser = $this->findParser($name); + if ($sortClauseParser instanceof SortClauseParserInterface) { + return $sortClauseParser->parse($parser, $name); + } + + throw new UnsupportedSortClauseException($name); + } + + public function supports(string $name): bool + { + return $this->findParser($name) instanceof SortClauseParserInterface; + } + + private function findParser(string $name): ?SortClauseParserInterface + { + foreach ($this->parsers as $parser) { + if ($parser->supports($name)) { + return $parser; + } + } + + return null; + } +} + +class_alias(SortClauseParserDispatcher::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserDispatcher'); diff --git a/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParserInterface.php b/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParserInterface.php new file mode 100644 index 0000000000..4a934fe568 --- /dev/null +++ b/src/lib/QueryType/BuiltIn/SortSpec/SortClauseParserInterface.php @@ -0,0 +1,23 @@ +'; + public const TYPE_ASC = ''; + public const TYPE_DESC = ''; + public const TYPE_ID = ''; + public const TYPE_DOT = '<.>'; + public const TYPE_COMMA = '<,>'; + public const TYPE_INT = ''; + public const TYPE_FLOAT = ''; + public const TYPE_EOF = ''; + + /** @var string */ + private $type; + + /** @var string */ + private $value; + + /** @var int */ + private $position; + + public function __construct(string $type, string $value = '', int $position = -1) + { + $this->type = $type; + $this->value = $value; + $this->position = $position; + } + + public function isA(string $type): bool + { + return $this->type === $type; + } + + public function getType(): string + { + return $this->type; + } + + public function getValue(): string + { + return $this->value; + } + + public function getValueAsFloat(): float + { + return (float)$this->value; + } + + public function getValueAsInt(): int + { + return (int)$this->value; + } + + public function getPosition(): int + { + return $this->position; + } + + public function __toString(): string + { + if ($this->value !== null) { + return "{$this->value} ({$this->type})"; + } + + return "{$this->type}"; + } +} + +class_alias(Token::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Token'); diff --git a/src/lib/QueryType/BuiltIn/SubtreeQueryType.php b/src/lib/QueryType/BuiltIn/SubtreeQueryType.php new file mode 100644 index 0000000000..55a52fd387 --- /dev/null +++ b/src/lib/QueryType/BuiltIn/SubtreeQueryType.php @@ -0,0 +1,57 @@ +setDefaults([ + 'depth' => -1, + ]); + $resolver->setAllowedTypes('depth', 'int'); + } + + protected function getQueryFilter(array $parameters): Criterion + { + $location = $this->resolveLocation($parameters); + + if ($location === null) { + return new MatchNone(); + } + + if ($parameters['depth'] > -1) { + $depth = $location->depth + (int)$parameters['depth']; + + return new LogicalAnd([ + new Subtree($location->pathString), + new Depth(Operator::LTE, $depth), + ]); + } + + return new Subtree($location->pathString); + } +} + +class_alias(SubtreeQueryType::class, 'eZ\Publish\Core\QueryType\BuiltIn\SubtreeQueryType'); diff --git a/src/lib/QueryType/ContentViewQueryTypeMapper.php b/src/lib/QueryType/ContentViewQueryTypeMapper.php new file mode 100644 index 0000000000..8e3e5ea460 --- /dev/null +++ b/src/lib/QueryType/ContentViewQueryTypeMapper.php @@ -0,0 +1,24 @@ +resolver; } } + +class_alias(OptionsResolverBasedQueryType::class, 'eZ\Publish\Core\QueryType\OptionsResolverBasedQueryType'); diff --git a/eZ/Publish/Core/QueryType/QueryParameterContentViewQueryTypeMapper.php b/src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php similarity index 87% rename from eZ/Publish/Core/QueryType/QueryParameterContentViewQueryTypeMapper.php rename to src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php index 82d4ce75e7..e629ddf755 100644 --- a/eZ/Publish/Core/QueryType/QueryParameterContentViewQueryTypeMapper.php +++ b/src/lib/QueryType/QueryParameterContentViewQueryTypeMapper.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\QueryType; +namespace Ibexa\Core\QueryType; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; +use Ibexa\Core\MVC\Symfony\View\ContentView; use InvalidArgumentException; use Symfony\Component\ExpressionLanguage\ExpressionLanguage; @@ -40,7 +40,7 @@ public function map(ContentView $contentView) } /** - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentView $contentView + * @param \Ibexa\Core\MVC\Symfony\View\ContentView $contentView * * @return array */ @@ -59,7 +59,7 @@ private function extractParametersFromContentView(ContentView $contentView) } /** - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentView $contentView + * @param \Ibexa\Core\MVC\Symfony\View\ContentView $contentView * @param array $queryParameterValue * * @return array|string @@ -79,7 +79,7 @@ private function extractParameters(ContentView $contentView, $queryParameterValu } /** - * @param \eZ\Publish\Core\MVC\Symfony\View\ContentView $contentView + * @param \Ibexa\Core\MVC\Symfony\View\ContentView $contentView * @param string $queryParameterValue * * @return mixed @@ -102,3 +102,5 @@ private function evaluateExpression(ContentView $contentView, $queryParameterVal return $queryParameterValue; } } + +class_alias(QueryParameterContentViewQueryTypeMapper::class, 'eZ\Publish\Core\QueryType\QueryParameterContentViewQueryTypeMapper'); diff --git a/eZ/Publish/Core/QueryType/QueryType.php b/src/lib/QueryType/QueryType.php similarity index 80% rename from eZ/Publish/Core/QueryType/QueryType.php rename to src/lib/QueryType/QueryType.php index f2c5bb92c7..4f30d22ff6 100644 --- a/eZ/Publish/Core/QueryType/QueryType.php +++ b/src/lib/QueryType/QueryType.php @@ -4,14 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\QueryType; - -use eZ\Publish\API\Repository\Values\Content\Query; +namespace Ibexa\Core\QueryType; /** * A QueryType is a pre-defined content or location query. * - * QueryTypes must be registered with the service container using the `ezpublish.query_type` service tag. + * QueryTypes must be registered with the service container using the `ibexa.query_type` service tag. */ interface QueryType { @@ -20,7 +18,7 @@ interface QueryType * * @param array $parameters A hash of parameters that will be used to build the Query * - * @return \eZ\Publish\API\Repository\Values\Content\Query + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query */ public function getQuery(array $parameters = []); @@ -38,3 +36,5 @@ public function getSupportedParameters(); */ public static function getName(); } + +class_alias(QueryType::class, 'eZ\Publish\Core\QueryType\QueryType'); diff --git a/src/lib/QueryType/QueryTypeRegistry.php b/src/lib/QueryType/QueryTypeRegistry.php new file mode 100644 index 0000000000..74c96dfe7b --- /dev/null +++ b/src/lib/QueryType/QueryTypeRegistry.php @@ -0,0 +1,39 @@ +repository = $repository; + $this->bookmarkHandler = $bookmarkHandler; + } + + /** + * {@inheritdoc} + */ + public function createBookmark(Location $location): void + { + $loadedLocation = $this->repository->getLocationService()->loadLocation($location->id); + + if ($this->isBookmarked($loadedLocation)) { + throw new InvalidArgumentException('$location', 'Location is already bookmarked.'); + } + + $createStruct = new CreateStruct(); + $createStruct->name = $loadedLocation->contentInfo->name; + $createStruct->locationId = $loadedLocation->id; + $createStruct->userId = $this->getCurrentUserId(); + + $this->repository->beginTransaction(); + try { + $this->bookmarkHandler->create($createStruct); + $this->repository->commit(); + } catch (Exception $ex) { + $this->repository->rollback(); + throw $ex; + } + } + + /** + * {@inheritdoc} + */ + public function deleteBookmark(Location $location): void + { + $loadedLocation = $this->repository->getLocationService()->loadLocation($location->id); + + $bookmarks = $this->bookmarkHandler->loadByUserIdAndLocationId( + $this->getCurrentUserId(), + [$loadedLocation->id] + ); + + if (empty($bookmarks)) { + throw new InvalidArgumentException('$location', 'Location is not bookmarked.'); + } + + $this->repository->beginTransaction(); + try { + $this->bookmarkHandler->delete(reset($bookmarks)->id); + $this->repository->commit(); + } catch (Exception $ex) { + $this->repository->rollback(); + throw $ex; + } + } + + /** + * {@inheritdoc} + */ + public function loadBookmarks(int $offset = 0, int $limit = 25): BookmarkList + { + $currentUserId = $this->getCurrentUserId(); + + $list = new BookmarkList(); + $list->totalCount = $this->bookmarkHandler->countUserBookmarks($currentUserId); + if ($list->totalCount > 0) { + $bookmarks = $this->bookmarkHandler->loadUserBookmarks($currentUserId, $offset, $limit); + + $list->items = array_map(function (Bookmark $bookmark) { + return $this->repository->getLocationService()->loadLocation($bookmark->locationId); + }, $bookmarks); + } + + return $list; + } + + /** + * {@inheritdoc} + */ + public function isBookmarked(Location $location): bool + { + $bookmarks = $this->bookmarkHandler->loadByUserIdAndLocationId( + $this->getCurrentUserId(), + [$location->id] + ); + + return !empty($bookmarks); + } + + private function getCurrentUserId(): int + { + return $this->repository + ->getPermissionResolver() + ->getCurrentUserReference() + ->getUserId(); + } +} + +class_alias(BookmarkService::class, 'eZ\Publish\Core\Repository\BookmarkService'); diff --git a/src/lib/Repository/ContentService.php b/src/lib/Repository/ContentService.php new file mode 100644 index 0000000000..4aee3f990a --- /dev/null +++ b/src/lib/Repository/ContentService.php @@ -0,0 +1,2714 @@ +repository = $repository; + $this->persistenceHandler = $handler; + $this->contentDomainMapper = $contentDomainMapper; + $this->relationProcessor = $relationProcessor; + $this->nameSchemaService = $nameSchemaService; + $this->fieldTypeRegistry = $fieldTypeRegistry; + // Union makes sure default settings are ignored if provided in argument + $this->settings = $settings + [ + // Version archive limit (0-50), only enforced on publish, not on un-publish. + 'default_version_archive_limit' => 5, + 'remove_archived_versions_on_publish' => true, + ]; + $this->contentFilteringHandler = $contentFilteringHandler; + $this->permissionResolver = $permissionService; + $this->contentMapper = $contentMapper; + $this->contentValidator = $contentValidator; + } + + /** + * Loads a content info object. + * + * To load fields use loadContent + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the content with the given id does not exist + * + * @param int $contentId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo + */ + public function loadContentInfo(int $contentId): ContentInfo + { + $contentInfo = $this->internalLoadContentInfoById($contentId); + if (!$this->permissionResolver->canUser('content', 'read', $contentInfo)) { + throw new UnauthorizedException('content', 'read', ['contentId' => $contentId]); + } + + return $contentInfo; + } + + /** + * {@inheritdoc} + */ + public function loadContentInfoList(array $contentIds): iterable + { + $contentInfoList = []; + $spiInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList($contentIds); + foreach ($spiInfoList as $id => $spiInfo) { + $contentInfo = $this->contentDomainMapper->buildContentInfoDomainObject($spiInfo); + if ($this->permissionResolver->canUser('content', 'read', $contentInfo)) { + $contentInfoList[$id] = $contentInfo; + } + } + + return $contentInfoList; + } + + /** + * Loads a content info object. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the content with the given id does not exist + * + * @param int $id + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo + */ + public function internalLoadContentInfoById(int $id): ContentInfo + { + try { + return $this->contentDomainMapper->buildContentInfoDomainObject( + $this->persistenceHandler->contentHandler()->loadContentInfo($id) + ); + } catch (APINotFoundException $e) { + throw new NotFoundException('Content', $id, $e); + } + } + + /** + * Loads a content info object by remote id. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the content with the given id does not exist + * + * @param string $remoteId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo + */ + public function internalLoadContentInfoByRemoteId(string $remoteId): ContentInfo + { + try { + return $this->contentDomainMapper->buildContentInfoDomainObject( + $this->persistenceHandler->contentHandler()->loadContentInfoByRemoteId($remoteId) + ); + } catch (APINotFoundException $e) { + throw new NotFoundException('Content', $remoteId, $e); + } + } + + /** + * Loads a content info object for the given remoteId. + * + * To load fields use loadContent + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the content + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the content with the given remote id does not exist + * + * @param string $remoteId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo + */ + public function loadContentInfoByRemoteId(string $remoteId): ContentInfo + { + $contentInfo = $this->internalLoadContentInfoByRemoteId($remoteId); + + if (!$this->permissionResolver->canUser('content', 'read', $contentInfo)) { + throw new UnauthorizedException('content', 'read', ['remoteId' => $remoteId]); + } + + return $contentInfo; + } + + /** + * Loads a version info of the given content object. + * + * If no version number is given, the method returns the current version + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the version with the given number does not exist + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param int|null $versionNo the version number. If not given the current version is returned. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo + */ + public function loadVersionInfo(ContentInfo $contentInfo, ?int $versionNo = null): APIVersionInfo + { + return $this->loadVersionInfoById($contentInfo->getId(), $versionNo); + } + + /** + * Loads a version info of the given content object id. + * + * If no version number is given, the method returns the current version + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the version with the given number does not exist + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to load this version + * + * @param int $contentId + * @param int|null $versionNo the version number. If not given the current version is returned. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo + */ + public function loadVersionInfoById(int $contentId, ?int $versionNo = null): APIVersionInfo + { + try { + $spiVersionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo( + $contentId, + $versionNo + ); + } catch (APINotFoundException $e) { + throw new NotFoundException( + 'VersionInfo', + [ + 'contentId' => $contentId, + 'versionNo' => $versionNo, + ], + $e + ); + } + + $versionInfo = $this->contentDomainMapper->buildVersionInfoDomainObject($spiVersionInfo); + + if ($versionInfo->isPublished()) { + $function = 'read'; + } else { + $function = 'versionread'; + } + + if (!$this->permissionResolver->canUser('content', $function, $versionInfo)) { + throw new UnauthorizedException('content', $function, ['contentId' => $contentId]); + } + + return $versionInfo; + } + + public function loadVersionInfoListByContentInfo(array $contentInfoList): array + { + foreach ($contentInfoList as $idx => $contentInfo) { + if (!$contentInfo instanceof ContentInfo) { + throw new InvalidArgumentException( + '$contentInfoList', + sprintf( + 'Element at position %d is not an instance of %s', + $idx, + ContentInfo::class + ) + ); + } + } + + $contentIds = array_map( + static function (ContentInfo $contentInfo): int { + return $contentInfo->getId(); + }, + $contentInfoList + ); + + $persistenceVersionInfos = $this->persistenceHandler + ->contentHandler() + ->loadVersionInfoList($contentIds); + + $versionInfoList = []; + foreach ($persistenceVersionInfos as $persistenceVersionInfo) { + $versionInfo = $this->contentDomainMapper->buildVersionInfoDomainObject($persistenceVersionInfo); + if ($this->permissionResolver->canUser('content', 'read', $versionInfo)) { + $versionInfoList[$versionInfo->getContentInfo()->getId()] = $versionInfo; + } + } + + return $versionInfoList; + } + + /** + * {@inheritdoc} + */ + public function loadContentByContentInfo(ContentInfo $contentInfo, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): APIContent + { + // Change $useAlwaysAvailable to false to avoid contentInfo lookup if we know alwaysAvailable is disabled + if ($useAlwaysAvailable && !$contentInfo->alwaysAvailable) { + $useAlwaysAvailable = false; + } + + return $this->loadContent( + $contentInfo->id, + $languages, + $versionNo,// On purpose pass as-is and not use $contentInfo, to make sure to return actual current version on null + $useAlwaysAvailable + ); + } + + /** + * {@inheritdoc} + */ + public function loadContentByVersionInfo(APIVersionInfo $versionInfo, array $languages = null, bool $useAlwaysAvailable = true): APIContent + { + // Change $useAlwaysAvailable to false to avoid contentInfo lookup if we know alwaysAvailable is disabled + if ($useAlwaysAvailable && !$versionInfo->getContentInfo()->alwaysAvailable) { + $useAlwaysAvailable = false; + } + + return $this->loadContent( + $versionInfo->getContentInfo()->id, + $languages, + $versionInfo->versionNo, + $useAlwaysAvailable + ); + } + + /** + * {@inheritdoc} + */ + public function loadContent(int $contentId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): APIContent + { + $content = $this->internalLoadContentById($contentId, $languages, $versionNo, $useAlwaysAvailable); + + if (!$this->permissionResolver->canUser('content', 'read', $content)) { + throw new UnauthorizedException('content', 'read', ['contentId' => $contentId]); + } + if ( + !$content->getVersionInfo()->isPublished() + && !$this->permissionResolver->canUser('content', 'versionread', $content) + ) { + throw new UnauthorizedException('content', 'versionread', ['contentId' => $contentId, 'versionNo' => $versionNo]); + } + + return $content; + } + + public function internalLoadContentById( + int $id, + ?array $languages = null, + int $versionNo = null, + bool $useAlwaysAvailable = true + ): APIContent { + try { + $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($id); + + return $this->internalLoadContentBySPIContentInfo( + $spiContentInfo, + $languages, + $versionNo, + $useAlwaysAvailable + ); + } catch (APINotFoundException $e) { + throw new NotFoundException( + 'Content', + [ + 'id' => $id, + 'languages' => $languages, + 'versionNo' => $versionNo, + ], + $e + ); + } + } + + public function internalLoadContentByRemoteId( + string $remoteId, + array $languages = null, + int $versionNo = null, + bool $useAlwaysAvailable = true + ): APIContent { + try { + $spiContentInfo = $this->persistenceHandler->contentHandler()->loadContentInfoByRemoteId($remoteId); + + return $this->internalLoadContentBySPIContentInfo( + $spiContentInfo, + $languages, + $versionNo, + $useAlwaysAvailable + ); + } catch (APINotFoundException $e) { + throw new NotFoundException( + 'Content', + [ + 'remoteId' => $remoteId, + 'languages' => $languages, + 'versionNo' => $versionNo, + ], + $e + ); + } + } + + private function internalLoadContentBySPIContentInfo(SPIContentInfo $spiContentInfo, array $languages = null, int $versionNo = null, bool $useAlwaysAvailable = true): APIContent + { + $loadLanguages = $languages; + $alwaysAvailableLanguageCode = null; + // Set main language on $languages filter if not empty (all) and $useAlwaysAvailable being true + // @todo Move use always available logic to SPI load methods, like done in location handler in 7.x + if (!empty($loadLanguages) && $useAlwaysAvailable && $spiContentInfo->alwaysAvailable) { + $loadLanguages[] = $alwaysAvailableLanguageCode = $spiContentInfo->mainLanguageCode; + $loadLanguages = array_unique($loadLanguages); + } + + $spiContent = $this->persistenceHandler->contentHandler()->load( + $spiContentInfo->id, + $versionNo, + $loadLanguages + ); + + if ($languages === null) { + $languages = []; + } + + return $this->contentDomainMapper->buildContentDomainObject( + $spiContent, + $this->repository->getContentTypeService()->loadContentType( + $spiContent->versionInfo->contentInfo->contentTypeId, + $languages + ), + $languages, + $alwaysAvailableLanguageCode + ); + } + + /** + * Loads content in a version for the content object reference by the given remote id. + * + * If no version is given, the method returns the current version + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the content or version with the given remote id does not exist + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the user has no access to read content and in case of un-published content: read versions + * + * @param string $remoteId + * @param array $languages A language filter for fields. If not given all languages are returned + * @param int $versionNo the version number. If not given the current version is returned + * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function loadContentByRemoteId(string $remoteId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): APIContent + { + $content = $this->internalLoadContentByRemoteId($remoteId, $languages, $versionNo, $useAlwaysAvailable); + + if (!$this->permissionResolver->canUser('content', 'read', $content)) { + throw new UnauthorizedException('content', 'read', ['remoteId' => $remoteId]); + } + + if ( + !$content->getVersionInfo()->isPublished() + && !$this->permissionResolver->canUser('content', 'versionread', $content) + ) { + throw new UnauthorizedException('content', 'versionread', ['remoteId' => $remoteId, 'versionNo' => $versionNo]); + } + + return $content; + } + + /** + * Bulk-load Content items by the list of ContentInfo Value Objects. + * + * Note: it does not throw exceptions on load, just ignores erroneous Content item. + * Moreover, since the method works on pre-loaded ContentInfo list, it is assumed that user is + * allowed to access every Content on the list. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo[] $contentInfoList + * @param string[] $languages A language priority, filters returned fields and is used as prioritized language code on + * returned value object. If not given all languages are returned. + * @param bool $useAlwaysAvailable Add Main language to \$languages if true (default) and if alwaysAvailable is true, + * unless all languages have been asked for. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content[] list of Content items with Content Ids as keys + */ + public function loadContentListByContentInfo( + array $contentInfoList, + array $languages = [], + bool $useAlwaysAvailable = true + ): iterable { + $loadAllLanguages = $languages === Language::ALL; + $contentIds = []; + $contentTypeIds = []; + $translations = $languages; + foreach ($contentInfoList as $contentInfo) { + $contentIds[] = $contentInfo->id; + $contentTypeIds[] = $contentInfo->contentTypeId; + // Unless we are told to load all languages, we add main language to translations so they are loaded too + // Might in some case load more languages then intended, but prioritised handling will pick right one + if (!$loadAllLanguages && $useAlwaysAvailable && $contentInfo->alwaysAvailable) { + $translations[] = $contentInfo->mainLanguageCode; + } + } + + $contentList = []; + $translations = array_unique($translations); + $spiContentList = $this->persistenceHandler->contentHandler()->loadContentList( + $contentIds, + $translations + ); + $contentTypeList = $this->repository->getContentTypeService()->loadContentTypeList( + array_unique($contentTypeIds), + $languages + ); + foreach ($spiContentList as $contentId => $spiContent) { + $contentInfo = $spiContent->versionInfo->contentInfo; + $contentList[$contentId] = $this->contentDomainMapper->buildContentDomainObject( + $spiContent, + $contentTypeList[$contentInfo->contentTypeId], + $languages, + $contentInfo->alwaysAvailable ? $contentInfo->mainLanguageCode : null + ); + } + + return $contentList; + } + + /** + * Creates a new content draft assigned to the authenticated user. + * + * If a different userId is given in $contentCreateStruct it is assigned to the given user + * but this required special rights for the authenticated user + * (this is useful for content staging where the transfer process does not + * have to authenticate with the user which created the content object in the source server). + * The user has to publish the draft if it should be visible. + * In 4.x at least one location has to be provided in the location creation array. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to create the content in the given location + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the provided remoteId exists in the system, required properties on + * struct are missing or invalid, or if multiple locations are under the + * same parent. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid, + * or if a required field is missing / set to an empty value. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType, + * or value is set for non-translatable field in language + * other than main. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct $contentCreateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs For each location parent under which a location should be created for the content + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content - the newly created content draft + */ + public function createContent(APIContentCreateStruct $contentCreateStruct, array $locationCreateStructs = [], ?array $fieldIdentifiersToValidate = null): APIContent + { + if ($contentCreateStruct->mainLanguageCode === null) { + throw new InvalidArgumentException('$contentCreateStruct', "the 'mainLanguageCode' property must be set"); + } + + if ($contentCreateStruct->contentType === null) { + throw new InvalidArgumentException('$contentCreateStruct', "the 'contentType' property must be set"); + } + + $contentCreateStruct = clone $contentCreateStruct; + + if ($contentCreateStruct->ownerId === null) { + $contentCreateStruct->ownerId = $this->permissionResolver->getCurrentUserReference()->getUserId(); + } + + if ($contentCreateStruct->alwaysAvailable === null) { + $contentCreateStruct->alwaysAvailable = $contentCreateStruct->contentType->defaultAlwaysAvailable ?: false; + } + + $contentCreateStruct->contentType = $this->repository->getContentTypeService()->loadContentType( + $contentCreateStruct->contentType->id + ); + + $contentCreateStruct->fields = $this->contentMapper->getFieldsForCreate( + $contentCreateStruct->fields, + $contentCreateStruct->contentType + ); + + if (empty($contentCreateStruct->sectionId)) { + if (isset($locationCreateStructs[0])) { + $location = $this->repository->getLocationService()->loadLocation( + $locationCreateStructs[0]->parentLocationId + ); + $contentCreateStruct->sectionId = $location->getContentInfo()->getSectionId(); + } else { + $contentCreateStruct->sectionId = 1; + } + } + + if (!$this->permissionResolver->canUser('content', 'create', $contentCreateStruct, $locationCreateStructs)) { + throw new UnauthorizedException( + 'content', + 'create', + [ + 'parentLocationId' => isset($locationCreateStructs[0]) ? + $locationCreateStructs[0]->parentLocationId : + null, + 'sectionId' => $contentCreateStruct->sectionId, + ] + ); + } + + if (!empty($contentCreateStruct->remoteId)) { + try { + $this->loadContentByRemoteId($contentCreateStruct->remoteId); + + throw new InvalidArgumentException( + '$contentCreateStruct', + "Another Content item with remoteId '{$contentCreateStruct->remoteId}' exists" + ); + } catch (APINotFoundException $e) { + // Do nothing + } + } else { + $contentCreateStruct->remoteId = $this->contentDomainMapper->getUniqueHash($contentCreateStruct); + } + + $errors = $this->validate( + $contentCreateStruct, + [], + $fieldIdentifiersToValidate + ); + + if (!empty($errors)) { + throw new ContentFieldValidationException($errors); + } + + $spiLocationCreateStructs = $this->buildSPILocationCreateStructs( + $locationCreateStructs, + $contentCreateStruct->contentType + ); + + $languageCodes = $this->contentMapper->getLanguageCodesForCreate($contentCreateStruct); + $fields = $this->contentMapper->mapFieldsForCreate($contentCreateStruct); + + $fieldValues = []; + $spiFields = []; + $inputRelations = []; + $locationIdToContentIdMapping = []; + + foreach ($contentCreateStruct->contentType->getFieldDefinitions() as $fieldDefinition) { + /** @var $fieldType \Ibexa\Core\FieldType\FieldType */ + $fieldType = $this->fieldTypeRegistry->getFieldType( + $fieldDefinition->fieldTypeIdentifier + ); + + foreach ($languageCodes as $languageCode) { + $isEmptyValue = false; + $valueLanguageCode = $fieldDefinition->isTranslatable ? $languageCode : $contentCreateStruct->mainLanguageCode; + $isLanguageMain = $languageCode === $contentCreateStruct->mainLanguageCode; + + $fieldValue = $this->contentMapper->getFieldValueForCreate( + $fieldDefinition, + $fields[$fieldDefinition->identifier][$valueLanguageCode] ?? null + ); + + if ($fieldType->isEmptyValue($fieldValue)) { + $isEmptyValue = true; + } + + $this->relationProcessor->appendFieldRelations( + $inputRelations, + $locationIdToContentIdMapping, + $fieldType, + $fieldValue, + $fieldDefinition->id + ); + $fieldValues[$fieldDefinition->identifier][$languageCode] = $fieldValue; + + // Only non-empty value for: translatable field or in main language + if ( + (!$isEmptyValue && $fieldDefinition->isTranslatable) || + (!$isEmptyValue && $isLanguageMain) + ) { + $spiFields[] = new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => $fieldDefinition->id, + 'type' => $fieldDefinition->fieldTypeIdentifier, + 'value' => $fieldType->toPersistenceValue($fieldValue), + 'languageCode' => $languageCode, + 'versionNo' => null, + ] + ); + } + } + } + + $spiContentCreateStruct = new SPIContentCreateStruct( + [ + 'name' => $this->nameSchemaService->resolveNameSchema( + $contentCreateStruct->contentType->nameSchema, + $contentCreateStruct->contentType, + $fieldValues, + $languageCodes + ), + 'typeId' => $contentCreateStruct->contentType->id, + 'sectionId' => $contentCreateStruct->sectionId, + 'ownerId' => $contentCreateStruct->ownerId, + 'locations' => $spiLocationCreateStructs, + 'fields' => $spiFields, + 'alwaysAvailable' => $contentCreateStruct->alwaysAvailable, + 'remoteId' => $contentCreateStruct->remoteId, + 'modified' => isset($contentCreateStruct->modificationDate) ? $contentCreateStruct->modificationDate->getTimestamp() : time(), + 'initialLanguageId' => $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode( + $contentCreateStruct->mainLanguageCode + )->id, + ] + ); + + $defaultObjectStates = $this->getDefaultObjectStates(); + + $this->repository->beginTransaction(); + try { + $spiContent = $this->persistenceHandler->contentHandler()->create($spiContentCreateStruct); + $this->relationProcessor->processFieldRelations( + $inputRelations, + $spiContent->versionInfo->contentInfo->id, + $spiContent->versionInfo->versionNo, + $contentCreateStruct->contentType + ); + + $objectStateHandler = $this->persistenceHandler->objectStateHandler(); + foreach ($defaultObjectStates as $objectStateGroupId => $objectState) { + $objectStateHandler->setContentState( + $spiContent->versionInfo->contentInfo->id, + $objectStateGroupId, + $objectState->id + ); + } + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->contentDomainMapper->buildContentDomainObject( + $spiContent, + $contentCreateStruct->contentType + ); + } + + /** + * Returns an array of default content states with content state group id as key. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState[] + */ + protected function getDefaultObjectStates(): array + { + $defaultObjectStatesMap = []; + $objectStateHandler = $this->persistenceHandler->objectStateHandler(); + + foreach ($objectStateHandler->loadAllGroups() as $objectStateGroup) { + foreach ($objectStateHandler->loadObjectStates($objectStateGroup->id) as $objectState) { + // Only register the first object state which is the default one. + $defaultObjectStatesMap[$objectStateGroup->id] = $objectState; + break; + } + } + + return $defaultObjectStatesMap; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType|null $contentType + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Location\CreateStruct[] + */ + protected function buildSPILocationCreateStructs( + array $locationCreateStructs, + ?ContentType $contentType = null + ): array { + $spiLocationCreateStructs = []; + $parentLocationIdSet = []; + $mainLocation = true; + + foreach ($locationCreateStructs as $locationCreateStruct) { + if (isset($parentLocationIdSet[$locationCreateStruct->parentLocationId])) { + throw new InvalidArgumentException( + '$locationCreateStructs', + "You provided multiple LocationCreateStructs with the same parent Location '{$locationCreateStruct->parentLocationId}'" + ); + } + + if ($locationCreateStruct->sortField === null) { + $locationCreateStruct->sortField = $contentType->defaultSortField ?? Location::SORT_FIELD_NAME; + } + if ($locationCreateStruct->sortOrder === null) { + $locationCreateStruct->sortOrder = $contentType->defaultSortOrder ?? Location::SORT_ORDER_ASC; + } + + $parentLocationIdSet[$locationCreateStruct->parentLocationId] = true; + $parentLocation = $this->repository->getLocationService()->loadLocation( + $locationCreateStruct->parentLocationId + ); + + $spiLocationCreateStructs[] = $this->contentDomainMapper->buildSPILocationCreateStruct( + $locationCreateStruct, + $parentLocation, + $mainLocation, + // For Content draft contentId and contentVersionNo are set in ContentHandler upon draft creation + null, + null, + false + ); + + // First Location in the list will be created as main Location + $mainLocation = false; + } + + return $spiLocationCreateStructs; + } + + /** + * Updates the metadata. + * + * (see {@link ContentMetadataUpdateStruct}) of a content object - to update fields use updateContent + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to update the content meta data + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the remoteId in $contentMetadataUpdateStruct is set but already exists + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentMetadataUpdateStruct $contentMetadataUpdateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content the content with the updated attributes + */ + public function updateContentMetadata(ContentInfo $contentInfo, ContentMetadataUpdateStruct $contentMetadataUpdateStruct): APIContent + { + $propertyCount = 0; + foreach ($contentMetadataUpdateStruct as $propertyName => $propertyValue) { + if (isset($contentMetadataUpdateStruct->$propertyName)) { + ++$propertyCount; + } + } + if ($propertyCount === 0) { + throw new InvalidArgumentException( + '$contentMetadataUpdateStruct', + 'At least one property must be set' + ); + } + + $loadedContentInfo = $this->loadContentInfo($contentInfo->id); + + if (!$this->permissionResolver->canUser('content', 'edit', $loadedContentInfo)) { + throw new UnauthorizedException('content', 'edit', ['contentId' => $loadedContentInfo->id]); + } + + if (isset($contentMetadataUpdateStruct->remoteId)) { + try { + $existingContentInfo = $this->loadContentInfoByRemoteId($contentMetadataUpdateStruct->remoteId); + + if ($existingContentInfo->id !== $loadedContentInfo->id) { + throw new InvalidArgumentException( + '$contentMetadataUpdateStruct', + "Another Content item with remoteId '{$contentMetadataUpdateStruct->remoteId}' exists" + ); + } + } catch (APINotFoundException $e) { + // Do nothing + } + } + + $this->repository->beginTransaction(); + try { + if ($propertyCount > 1 || !isset($contentMetadataUpdateStruct->mainLocationId)) { + $this->persistenceHandler->contentHandler()->updateMetadata( + $loadedContentInfo->id, + new SPIMetadataUpdateStruct( + [ + 'ownerId' => $contentMetadataUpdateStruct->ownerId, + 'publicationDate' => isset($contentMetadataUpdateStruct->publishedDate) ? + $contentMetadataUpdateStruct->publishedDate->getTimestamp() : + null, + 'modificationDate' => isset($contentMetadataUpdateStruct->modificationDate) ? + $contentMetadataUpdateStruct->modificationDate->getTimestamp() : + null, + 'mainLanguageId' => isset($contentMetadataUpdateStruct->mainLanguageCode) ? + $this->repository->getContentLanguageService()->loadLanguage( + $contentMetadataUpdateStruct->mainLanguageCode + )->id : + null, + 'alwaysAvailable' => $contentMetadataUpdateStruct->alwaysAvailable, + 'remoteId' => $contentMetadataUpdateStruct->remoteId, + 'name' => $contentMetadataUpdateStruct->name, + ] + ) + ); + } + + // Change main location + if (isset($contentMetadataUpdateStruct->mainLocationId) + && $loadedContentInfo->mainLocationId !== $contentMetadataUpdateStruct->mainLocationId) { + $this->persistenceHandler->locationHandler()->changeMainLocation( + $loadedContentInfo->id, + $contentMetadataUpdateStruct->mainLocationId + ); + } + + // Republish URL aliases to update always-available flag + if (isset($contentMetadataUpdateStruct->alwaysAvailable) + && $loadedContentInfo->alwaysAvailable !== $contentMetadataUpdateStruct->alwaysAvailable) { + $content = $this->loadContent($loadedContentInfo->id); + $this->publishUrlAliasesForContent($content, false); + } + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return isset($content) ? $content : $this->loadContent($loadedContentInfo->id); + } + + /** + * Publishes URL aliases for all locations of a given content. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param bool $updatePathIdentificationString this parameter is legacy storage specific for updating + * ezcontentobject_tree.path_identification_string, it is ignored by other storage engines + */ + protected function publishUrlAliasesForContent(APIContent $content, bool $updatePathIdentificationString = true): void + { + $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content); + $locations = $this->repository->getLocationService()->loadLocations( + $content->getVersionInfo()->getContentInfo() + ); + $urlAliasHandler = $this->persistenceHandler->urlAliasHandler(); + foreach ($locations as $location) { + foreach ($urlAliasNames as $languageCode => $name) { + $urlAliasHandler->publishUrlAliasForLocation( + $location->id, + $location->parentLocationId, + $name, + $languageCode, + $content->contentInfo->alwaysAvailable, + $updatePathIdentificationString ? $languageCode === $content->contentInfo->mainLanguageCode : false + ); + } + // archive URL aliases of Translations that got deleted + $urlAliasHandler->archiveUrlAliasesForDeletedTranslations( + $location->id, + $location->parentLocationId, + $content->versionInfo->languageCodes + ); + } + } + + /** + * Deletes a content object including all its versions and locations including their subtrees. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete the content (in one of the locations of the given content object) + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return mixed[] Affected Location Id's + */ + public function deleteContent(ContentInfo $contentInfo): iterable + { + $contentInfo = $this->internalLoadContentInfoById($contentInfo->id); + $versionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo( + $contentInfo->id, + $contentInfo->currentVersionNo + ); + $translations = $versionInfo->languageCodes; + $target = (new Target\Version())->deleteTranslations($translations); + + if (!$this->permissionResolver->canUser('content', 'remove', $contentInfo, [$target])) { + throw new UnauthorizedException('content', 'remove', ['contentId' => $contentInfo->id]); + } + + $affectedLocations = []; + $this->repository->beginTransaction(); + try { + // Load Locations first as deleting Content also deletes belonging Locations + $spiLocations = $this->persistenceHandler->locationHandler()->loadLocationsByContent($contentInfo->id); + $this->persistenceHandler->contentHandler()->deleteContent($contentInfo->id); + $urlAliasHandler = $this->persistenceHandler->urlAliasHandler(); + foreach ($spiLocations as $spiLocation) { + $urlAliasHandler->locationDeleted($spiLocation->id); + $affectedLocations[] = $spiLocation->id; + } + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $affectedLocations; + } + + /** + * Creates a draft from a published or archived version. + * + * If no version is given, the current published version is used. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo|null $versionInfo + * @param \Ibexa\Contracts\Core\Repository\Values\User\User|null $creator if set given user is used to create the draft - otherwise the current-user is used + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Language|null if not set the draft is created with the initialLanguage code of the source version or if not present with the main language. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content - the newly created content draft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the current-user is not allowed to create the draft + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to create the draft + */ + public function createContentDraft( + ContentInfo $contentInfo, + ?APIVersionInfo $versionInfo = null, + ?User $creator = null, + ?Language $language = null + ): APIContent { + $contentInfo = $this->loadContentInfo($contentInfo->id); + + if ($versionInfo !== null) { + // Check that given $contentInfo and $versionInfo belong to the same content + if ($versionInfo->getContentInfo()->id != $contentInfo->id) { + throw new InvalidArgumentException( + '$versionInfo', + 'VersionInfo does not belong to the same Content item as the given ContentInfo' + ); + } + + $versionInfo = $this->loadVersionInfoById($contentInfo->id, $versionInfo->versionNo); + + switch ($versionInfo->status) { + case VersionInfo::STATUS_PUBLISHED: + case VersionInfo::STATUS_ARCHIVED: + break; + + default: + // @todo: throw an exception here, to be defined + throw new BadStateException( + '$versionInfo', + 'Cannot create a draft from a draft version' + ); + } + + $versionNo = $versionInfo->versionNo; + } elseif ($contentInfo->published) { + $versionNo = $contentInfo->currentVersionNo; + } else { + // @todo: throw an exception here, to be defined + throw new BadStateException( + '$contentInfo', + 'Content is not published. A draft can be created only from a published or archived version.' + ); + } + + if ($creator === null) { + $creator = $this->permissionResolver->getCurrentUserReference(); + } + + $fallbackLanguageCode = $versionInfo->initialLanguageCode ?? $contentInfo->mainLanguageCode; + $languageCode = $language->languageCode ?? $fallbackLanguageCode; + + if (!$this->permissionResolver->canUser( + 'content', + 'edit', + $contentInfo, + [ + (new Target\Builder\VersionBuilder()) + ->changeStatusTo(APIVersionInfo::STATUS_DRAFT) + ->build(), + ] + )) { + throw new UnauthorizedException( + 'content', + 'edit', + ['contentId' => $contentInfo->id] + ); + } + + $this->repository->beginTransaction(); + try { + $spiContent = $this->persistenceHandler->contentHandler()->createDraftFromVersion( + $contentInfo->id, + $versionNo, + $creator->getUserId(), + $languageCode + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->contentDomainMapper->buildContentDomainObject( + $spiContent, + $this->repository->getContentTypeService()->loadContentType( + $spiContent->versionInfo->contentInfo->contentTypeId + ) + ); + } + + public function countContentDrafts(?User $user = null): int + { + if ($this->permissionResolver->hasAccess('content', 'versionread') === false) { + return 0; + } + + return $this->persistenceHandler->contentHandler()->countDraftsForUser( + $this->resolveUser($user)->getUserId() + ); + } + + /** + * Loads drafts for a user. + * + * If no user is given the drafts for the authenticated user are returned + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User|null $user + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo[] Drafts owned by the given user + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function loadContentDrafts(?User $user = null): iterable + { + // throw early if user has absolutely no access to versionread + if ($this->permissionResolver->hasAccess('content', 'versionread') === false) { + throw new UnauthorizedException('content', 'versionread'); + } + + $spiVersionInfoList = $this->persistenceHandler->contentHandler()->loadDraftsForUser( + $this->resolveUser($user)->getUserId() + ); + $versionInfoList = []; + foreach ($spiVersionInfoList as $spiVersionInfo) { + $versionInfo = $this->contentDomainMapper->buildVersionInfoDomainObject($spiVersionInfo); + // @todo: Change this to filter returned drafts by permissions instead of throwing + if (!$this->permissionResolver->canUser('content', 'versionread', $versionInfo)) { + throw new UnauthorizedException('content', 'versionread', ['contentId' => $versionInfo->contentInfo->id]); + } + + $versionInfoList[] = $versionInfo; + } + + return $versionInfoList; + } + + public function loadContentDraftList(?User $user = null, int $offset = 0, int $limit = -1): ContentDraftList + { + $list = new ContentDraftList(); + if ($this->permissionResolver->hasAccess('content', 'versionread') === false) { + return $list; + } + + $list->totalCount = $this->persistenceHandler->contentHandler()->countDraftsForUser( + $this->resolveUser($user)->getUserId() + ); + if ($list->totalCount > 0) { + $spiVersionInfoList = $this->persistenceHandler->contentHandler()->loadDraftListForUser( + $this->resolveUser($user)->getUserId(), + $offset, + $limit + ); + foreach ($spiVersionInfoList as $spiVersionInfo) { + $versionInfo = $this->contentDomainMapper->buildVersionInfoDomainObject($spiVersionInfo); + if ($this->permissionResolver->canUser('content', 'versionread', $versionInfo)) { + $list->items[] = new ContentDraftListItem($versionInfo); + } else { + $list->items[] = new UnauthorizedContentDraftListItem( + 'content', + 'versionread', + ['contentId' => $versionInfo->contentInfo->id] + ); + } + } + } + + return $list; + } + + /** + * Updates the fields of a draft. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content the content draft with the updated fields + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid, + * or if a required field is missing / set to an empty value. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType, + * or value is set for non-translatable field in language + * other than main. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to update this version + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if a property on the struct is invalid. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function updateContent(APIVersionInfo $versionInfo, APIContentUpdateStruct $contentUpdateStruct, ?array $fieldIdentifiersToValidate = null): APIContent + { + /** @var $content \Ibexa\Core\Repository\Values\Content\Content */ + $content = $this->loadContent( + $versionInfo->getContentInfo()->id, + null, + $versionInfo->versionNo + ); + + $updatedFields = $this->contentMapper->getFieldsForUpdate($contentUpdateStruct->fields, $content); + + if (!$this->repository->getPermissionResolver()->canUser( + 'content', + 'edit', + $content, + [ + (new Target\Builder\VersionBuilder()) + ->updateFields($updatedFields) + ->updateFieldsTo( + $contentUpdateStruct->initialLanguageCode, + $contentUpdateStruct->fields + ) + ->build(), + ] + )) { + throw new UnauthorizedException('content', 'edit', ['contentId' => $content->id]); + } + + return $this->internalUpdateContent($versionInfo, $contentUpdateStruct, $fieldIdentifiersToValidate); + } + + /** + * Updates the fields of a draft without checking the permissions. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException if a field in the $contentCreateStruct is not valid, + * or if a required field is missing / set to an empty value. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType, + * or value is set for non-translatable field in language + * other than main. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if a property on the struct is invalid. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + protected function internalUpdateContent( + APIVersionInfo $versionInfo, + APIContentUpdateStruct $contentUpdateStruct, + ?array $fieldIdentifiersToValidate = null, + bool $copyEmptyField = false + ): Content { + $contentUpdateStruct = clone $contentUpdateStruct; + + /** @var $content \Ibexa\Core\Repository\Values\Content\Content */ + $content = $this->internalLoadContentById( + $versionInfo->getContentInfo()->id, + null, + $versionInfo->versionNo + ); + + if (!$content->versionInfo->isDraft()) { + throw new BadStateException( + '$versionInfo', + 'The version is not a draft and cannot be updated' + ); + } + + $errors = $this->validate( + $contentUpdateStruct, + ['content' => $content], + $fieldIdentifiersToValidate + ); + + if (!empty($errors)) { + throw ContentFieldValidationException::createNewWithMultiline($errors, $content->getName()); + } + + $mainLanguageCode = $content->contentInfo->mainLanguageCode; + if ($contentUpdateStruct->initialLanguageCode === null) { + $contentUpdateStruct->initialLanguageCode = $mainLanguageCode; + } + + $allLanguageCodes = $this->contentMapper->getLanguageCodesForUpdate($contentUpdateStruct, $content); + $contentLanguageHandler = $this->persistenceHandler->contentLanguageHandler(); + foreach ($allLanguageCodes as $languageCode) { + $contentLanguageHandler->loadByLanguageCode($languageCode); + } + + $contentType = $this->repository->getContentTypeService()->loadContentType( + $content->contentInfo->contentTypeId + ); + $fields = $this->contentMapper->mapFieldsForUpdate( + $contentUpdateStruct, + $contentType, + $mainLanguageCode + ); + + $fieldValues = []; + $spiFields = []; + $inputRelations = []; + $locationIdToContentIdMapping = []; + + foreach ($contentType->getFieldDefinitions() as $fieldDefinition) { + $fieldType = $this->fieldTypeRegistry->getFieldType( + $fieldDefinition->fieldTypeIdentifier + ); + + foreach ($allLanguageCodes as $languageCode) { + $isCopied = $isEmpty = $isRetained = false; + $isLanguageNew = !in_array($languageCode, $content->versionInfo->languageCodes); + $valueLanguageCode = $fieldDefinition->isTranslatable ? $languageCode : $mainLanguageCode; + $isFieldUpdated = isset($fields[$fieldDefinition->identifier][$valueLanguageCode]); + $isProcessed = isset($fieldValues[$fieldDefinition->identifier][$valueLanguageCode]); + + if (!$isFieldUpdated && !$isLanguageNew) { + $isRetained = true; + } elseif (!$isFieldUpdated && $isLanguageNew && !$fieldDefinition->isTranslatable) { + $isCopied = true; + } + + $fieldValue = $this->contentMapper->getFieldValueForUpdate( + $fields[$fieldDefinition->identifier][$valueLanguageCode] ?? null, + $content->getField($fieldDefinition->identifier, $valueLanguageCode), + $fieldDefinition, + $isLanguageNew + ); + + if ($fieldType->isEmptyValue($fieldValue)) { + $isEmpty = true; + } + + $this->relationProcessor->appendFieldRelations( + $inputRelations, + $locationIdToContentIdMapping, + $fieldType, + $fieldValue, + $fieldDefinition->id + ); + $fieldValues[$fieldDefinition->identifier][$languageCode] = $fieldValue; + + if ($isRetained || $isCopied || ($isLanguageNew && $isEmpty && !$copyEmptyField) || $isProcessed) { + continue; + } + + $spiFields[] = new SPIField( + [ + 'id' => $isLanguageNew ? + null : + $content->getField($fieldDefinition->identifier, $languageCode)->id, + 'fieldDefinitionId' => $fieldDefinition->id, + 'type' => $fieldDefinition->fieldTypeIdentifier, + 'value' => $fieldType->toPersistenceValue($fieldValue), + 'languageCode' => $languageCode, + 'versionNo' => $versionInfo->versionNo, + ] + ); + } + } + + $spiContentUpdateStruct = new SPIContentUpdateStruct( + [ + 'name' => $this->nameSchemaService->resolveContentNameSchema( + $content, + $fieldValues, + $allLanguageCodes, + $contentType + ), + 'creatorId' => $contentUpdateStruct->creatorId ?: $this->permissionResolver->getCurrentUserReference()->getUserId(), + 'fields' => $spiFields, + 'modificationDate' => time(), + 'initialLanguageId' => $this->persistenceHandler->contentLanguageHandler()->loadByLanguageCode( + $contentUpdateStruct->initialLanguageCode + )->id, + ] + ); + $existingRelations = $this->repository->sudo(fn (): array => $this->internalLoadRelations($versionInfo)); + + $this->repository->beginTransaction(); + try { + $spiContent = $this->persistenceHandler->contentHandler()->updateContent( + $versionInfo->getContentInfo()->id, + $versionInfo->versionNo, + $spiContentUpdateStruct + ); + $this->relationProcessor->processFieldRelations( + $inputRelations, + $spiContent->versionInfo->contentInfo->id, + $spiContent->versionInfo->versionNo, + $contentType, + $existingRelations + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->contentDomainMapper->buildContentDomainObject( + $spiContent, + $contentType + ); + } + + /** + * Publishes a content version. + * + * Publishes a content version and deletes archive versions if they overflow max archive versions. + * Max archive versions are currently a configuration, but might be moved to be a param of ContentType in the future. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + * @param string[] $translations + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function publishVersion(APIVersionInfo $versionInfo, array $translations = Language::ALL): APIContent + { + $content = $this->internalLoadContentById( + $versionInfo->contentInfo->id, + null, + $versionInfo->versionNo + ); + + $targets = []; + if (!empty($translations)) { + $targets[] = (new Target\Builder\VersionBuilder()) + ->publishTranslations($translations) + ->build(); + } + + if (!$this->permissionResolver->canUser( + 'content', + 'publish', + $content, + $targets + )) { + throw new UnauthorizedException( + 'content', + 'publish', + ['contentId' => $content->id] + ); + } + + $this->repository->beginTransaction(); + try { + $this->copyTranslationsFromPublishedVersion($content->versionInfo, $translations); + $this->copyNonTranslatableFieldsFromPublishedVersion($content); + $content = $this->internalPublishVersion($content->getVersionInfo(), null, $translations); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $content; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + protected function copyNonTranslatableFieldsFromPublishedVersion(APIContent $currentVersionContent): void + { + $versionInfo = $currentVersionContent->getVersionInfo(); + $contentType = $currentVersionContent->getContentType(); + + $publishedContent = $this->internalLoadContentById($versionInfo->getContentInfo()->getId()); + $publishedVersionInfo = $publishedContent->getVersionInfo(); + + if (!$publishedVersionInfo->isPublished()) { + return; + } + + $mainLanguageCode = $publishedContent->getVersionInfo()->getContentInfo()->getMainLanguageCode(); + $publishedContentFieldsInMainLanguage = $publishedContent->getFieldsByLanguage($mainLanguageCode); + + $fieldValues = []; + $persistenceFields = []; + foreach ($currentVersionContent->getFields() as $field) { + $fieldDefinition = $contentType->getFieldDefinition($field->fieldDefIdentifier); + $fieldValues[$fieldDefinition->identifier][$field->languageCode] = $field->getValue(); + + if ( + $fieldDefinition->isTranslatable + || $field->languageCode === $versionInfo->initialLanguageCode + ) { + continue; + } + + $fieldType = $this->fieldTypeRegistry->getFieldType( + $fieldDefinition->fieldTypeIdentifier + ); + + $newValue = ( + $versionInfo->versionNo >= $publishedVersionInfo->versionNo + && $versionInfo->initialLanguageCode === $mainLanguageCode + ) + ? $field->getValue() + : $publishedContentFieldsInMainLanguage[$field->fieldDefIdentifier]->getValue(); + $fieldValues[$fieldDefinition->identifier][$field->languageCode] = $newValue; + + $persistenceFields[] = new SPIField( + [ + 'id' => $field->id, + 'fieldDefinitionId' => $fieldDefinition->id, + 'type' => $fieldDefinition->fieldTypeIdentifier, + 'value' => $fieldType->toPersistenceValue($newValue), + 'languageCode' => $field->languageCode, + 'versionNo' => $versionInfo->versionNo, + ] + ); + } + + if (count($persistenceFields) === 0) { + return; + } + + $updateStruct = new SPIContentUpdateStruct(); + $updateStruct->name = $this->nameSchemaService->resolveContentNameSchema( + $currentVersionContent, + $fieldValues, + $versionInfo->languageCodes, + $contentType + ); + $updateStruct->initialLanguageId = $this->persistenceHandler + ->contentLanguageHandler() + ->loadByLanguageCode( + $versionInfo->initialLanguageCode + )->id; + $updateStruct->creatorId = $versionInfo->creatorId; + $updateStruct->modificationDate = time(); + $updateStruct->fields = $persistenceFields; + + $this->persistenceHandler->contentHandler()->updateContent( + $versionInfo->getContentInfo()->getId(), + $versionInfo->versionNo, + $updateStruct + ); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + * @param array $translations + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + protected function copyTranslationsFromPublishedVersion(APIVersionInfo $versionInfo, array $translations = []): void + { + $contendId = $versionInfo->contentInfo->id; + + $currentContent = $this->internalLoadContentById($contendId); + $currentVersionInfo = $currentContent->versionInfo; + + // Copying occurs only if: + // - There is published Version + // - Published version is older than the currently published one unless specific translations are provided. + if (!$currentVersionInfo->isPublished() || + ($versionInfo->versionNo >= $currentVersionInfo->versionNo && empty($translations))) { + return; + } + + if (empty($translations)) { + $languagesToCopy = array_diff( + $currentVersionInfo->languageCodes, + $versionInfo->languageCodes + ); + } else { + $languagesToCopy = array_diff( + $currentVersionInfo->languageCodes, + $translations + ); + } + + if (empty($languagesToCopy)) { + return; + } + + $contentType = $this->repository->getContentTypeService()->loadContentType( + $currentVersionInfo->contentInfo->contentTypeId + ); + + // Find only translatable fields to update with selected languages + $updateStruct = $this->newContentUpdateStruct(); + $updateStruct->initialLanguageCode = $versionInfo->initialLanguageCode; + + $contentToPublish = $this->internalLoadContentById($contendId, null, $versionInfo->versionNo); + $fallbackUpdateStruct = $this->newContentUpdateStruct(); + + foreach ($currentContent->getFields() as $field) { + $fieldDefinition = $contentType->getFieldDefinition($field->fieldDefIdentifier); + + if (!$fieldDefinition->isTranslatable || !\in_array($field->languageCode, $languagesToCopy)) { + continue; + } + + $fieldType = $this->fieldTypeRegistry->getFieldType( + $fieldDefinition->fieldTypeIdentifier + ); + + $newValue = $contentToPublish->getFieldValue( + $fieldDefinition->identifier, + $field->languageCode + ); + + $value = $field->value; + if ($fieldDefinition->isRequired && $fieldType->isEmptyValue($value)) { + if (!$fieldType->isEmptyValue($fieldDefinition->defaultValue)) { + $value = $fieldDefinition->defaultValue; + } else { + $value = $contentToPublish->getFieldValue($field->fieldDefIdentifier, $versionInfo->initialLanguageCode); + } + $fallbackUpdateStruct->setField( + $field->fieldDefIdentifier, + $value, + $field->languageCode + ); + continue; + } + + if ($newValue !== null + && $field->value !== null + && $this->fieldValuesAreEqual($fieldType, $newValue, $field->value) + ) { + continue; + } + + $updateStruct->setField($field->fieldDefIdentifier, $value, $field->languageCode); + } + + // Nothing to copy, skip update + if (empty($updateStruct->fields)) { + return; + } + + // Do fallback only if content needs to be updated + foreach ($fallbackUpdateStruct->fields as $fallbackField) { + $updateStruct->setField($fallbackField->fieldDefIdentifier, $fallbackField->value, $fallbackField->languageCode); + } + + $this->internalUpdateContent($versionInfo, $updateStruct, null, true); + } + + protected function fieldValuesAreEqual(FieldType $fieldType, Value $value1, Value $value2): bool + { + if ($fieldType instanceof Comparable) { + return $fieldType->valuesEqual($value1, $value2); + } else { + @trigger_error( + \sprintf( + 'In eZ Platform 2.5 and 3.x %s should implement %s. ' . + 'Since the 4.0 major release FieldType\Comparable contract will be a part of %s', + get_class($fieldType), + Comparable::class, + FieldType::class + ), + E_USER_DEPRECATED + ); + + return $fieldType->toHash($value1) === $fieldType->toHash($value2); + } + } + + /** + * Publishes a content version. + * + * Publishes a content version and deletes archive versions if they overflow max archive versions. + * Max archive versions are currently a configuration, but might be moved to be a param of ContentType in the future. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + * @param int|null $publicationDate If null existing date is kept if there is one, otherwise current time is used. + * @param string[] $translations + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + protected function internalPublishVersion(APIVersionInfo $versionInfo, $publicationDate = null, array $translations = Language::ALL) + { + if (!$versionInfo->isDraft()) { + throw new BadStateException('$versionInfo', 'Only versions in draft status can be published.'); + } + + $currentTime = $this->getUnixTimestamp(); + if ($publicationDate === null && $versionInfo->versionNo === 1) { + $publicationDate = $currentTime; + } + + $errors = $this->validate( + $versionInfo, + [ + 'content' => $this->internalLoadContentById( + $versionInfo->getContentInfo()->id, + null, + $versionInfo->versionNo + ), + 'translations' => $translations, + ] + ); + + if (!empty($errors)) { + throw ContentFieldValidationException::createNewWithMultiline($errors, $versionInfo->getContentInfo()->name); + } + + $contentInfo = $versionInfo->getContentInfo(); + $metadataUpdateStruct = new SPIMetadataUpdateStruct(); + $metadataUpdateStruct->publicationDate = $publicationDate; + $metadataUpdateStruct->modificationDate = $currentTime; + $metadataUpdateStruct->isHidden = $contentInfo->isHidden; + + $contentId = $contentInfo->id; + $spiContent = $this->persistenceHandler->contentHandler()->publish( + $contentId, + $versionInfo->versionNo, + $metadataUpdateStruct + ); + + $content = $this->contentDomainMapper->buildContentDomainObject( + $spiContent, + $this->repository->getContentTypeService()->loadContentType( + $spiContent->versionInfo->contentInfo->contentTypeId + ) + ); + + $this->publishUrlAliasesForContent($content); + + if ($this->settings['remove_archived_versions_on_publish']) { + $this->deleteArchivedVersionsOverLimit($contentId); + } + + return $content; + } + + protected function deleteArchivedVersionsOverLimit(int $contentId): void + { + // Delete version archive overflow if any, limit is 0-50 (however 0 will mean 1 if content is unpublished) + $archiveList = $this->persistenceHandler->contentHandler()->listVersions( + $contentId, + APIVersionInfo::STATUS_ARCHIVED, + 100 // Limited to avoid publishing taking to long, besides SE limitations this is why limit is max 50 + ); + + $maxVersionArchiveCount = max(0, min(50, $this->settings['default_version_archive_limit'])); + while (!empty($archiveList) && count($archiveList) > $maxVersionArchiveCount) { + /** @var \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $archiveVersion */ + $archiveVersion = array_shift($archiveList); + $this->persistenceHandler->contentHandler()->deleteVersion( + $contentId, + $archiveVersion->versionNo + ); + } + } + + /** + * @return int + */ + protected function getUnixTimestamp(): int + { + return time(); + } + + /** + * Removes the given version. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is in + * published state or is a last version of Content in non draft state + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove this version + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + */ + public function deleteVersion(APIVersionInfo $versionInfo): void + { + $contentHandler = $this->persistenceHandler->contentHandler(); + + if ($versionInfo->isPublished()) { + throw new BadStateException( + '$versionInfo', + 'The Version is published and cannot be removed' + ); + } + + if (!$this->permissionResolver->canUser('content', 'versionremove', $versionInfo)) { + throw new UnauthorizedException( + 'content', + 'versionremove', + ['contentId' => $versionInfo->contentInfo->id, 'versionNo' => $versionInfo->versionNo] + ); + } + + $versionList = $contentHandler->listVersions( + $versionInfo->contentInfo->id, + null, + 2 + ); + $versionsCount = count($versionList); + + if ($versionsCount === 1 && !$versionInfo->isDraft()) { + throw new BadStateException( + '$versionInfo', + 'The Version is the last version of the Content item and cannot be removed' + ); + } + + $this->repository->beginTransaction(); + try { + if ($versionsCount === 1) { + $contentHandler->deleteContent($versionInfo->contentInfo->id); + } else { + $contentHandler->deleteVersion( + $versionInfo->getContentInfo()->id, + $versionInfo->versionNo + ); + } + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Loads all versions for the given content. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to list versions + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the given status is invalid + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param int|null $status + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo[] Sorted by creation date + */ + public function loadVersions(ContentInfo $contentInfo, ?int $status = null): iterable + { + if (!$this->permissionResolver->canUser('content', 'versionread', $contentInfo)) { + throw new UnauthorizedException('content', 'versionread', ['contentId' => $contentInfo->id]); + } + + if ($status !== null && !in_array((int)$status, [VersionInfo::STATUS_DRAFT, VersionInfo::STATUS_PUBLISHED, VersionInfo::STATUS_ARCHIVED], true)) { + throw new InvalidArgumentException( + 'status', + sprintf( + 'available statuses are: %d (draft), %d (published), %d (archived), %d given', + VersionInfo::STATUS_DRAFT, + VersionInfo::STATUS_PUBLISHED, + VersionInfo::STATUS_ARCHIVED, + $status + ) + ); + } + + $spiVersionInfoList = $this->persistenceHandler->contentHandler()->listVersions($contentInfo->id, $status); + + $versions = []; + foreach ($spiVersionInfoList as $spiVersionInfo) { + $versionInfo = $this->contentDomainMapper->buildVersionInfoDomainObject($spiVersionInfo); + if (!$this->permissionResolver->canUser('content', 'versionread', $versionInfo)) { + throw new UnauthorizedException('content', 'versionread', ['versionId' => $versionInfo->id]); + } + + $versions[] = $versionInfo; + } + + return $versions; + } + + /** + * Copies the content to a new location. If no version is given, + * all versions are copied, otherwise only the given version. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to copy the content to the given location + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct $destinationLocationCreateStruct the target location where the content is copied to + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo|null $versionInfo + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function copyContent(ContentInfo $contentInfo, LocationCreateStruct $destinationLocationCreateStruct, ?APIVersionInfo $versionInfo = null): APIContent + { + $destinationLocation = $this->repository->getLocationService()->loadLocation( + $destinationLocationCreateStruct->parentLocationId + ); + $locationTarget = (new DestinationLocationTarget($destinationLocation->id, $contentInfo)); + if (!$this->permissionResolver->canUser( + 'content', + 'create', + $contentInfo, + [$destinationLocation, $locationTarget], + )) { + throw new UnauthorizedException( + 'content', + 'create', + [ + 'parentLocationId' => $destinationLocationCreateStruct->parentLocationId, + 'sectionId' => $contentInfo->getSectionId(), + ] + ); + } + + if (!$this->permissionResolver->canUser('content', 'manage_locations', $contentInfo, [$destinationLocation])) { + throw new UnauthorizedException('content', 'manage_locations', ['contentId' => $contentInfo->id]); + } + + $defaultObjectStates = $this->getDefaultObjectStates(); + + $this->repository->beginTransaction(); + try { + $spiContent = $this->persistenceHandler->contentHandler()->copy( + $contentInfo->id, + $versionInfo ? $versionInfo->versionNo : null, + $this->permissionResolver->getCurrentUserReference()->getUserId() + ); + + $objectStateHandler = $this->persistenceHandler->objectStateHandler(); + foreach ($defaultObjectStates as $objectStateGroupId => $objectState) { + $objectStateHandler->setContentState( + $spiContent->versionInfo->contentInfo->id, + $objectStateGroupId, + $objectState->id + ); + } + + $content = $this->internalPublishVersion( + $this->contentDomainMapper->buildVersionInfoDomainObject($spiContent->versionInfo), + $spiContent->versionInfo->creationDate + ); + + $this->repository->getLocationService()->createLocation( + $content->getVersionInfo()->getContentInfo(), + $destinationLocationCreateStruct + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->internalLoadContentById($content->id); + } + + public function loadRelations(APIVersionInfo $versionInfo): iterable + { + if ($versionInfo->isPublished()) { + $function = 'read'; + } else { + $function = 'versionread'; + } + + if (!$this->permissionResolver->canUser('content', $function, $versionInfo)) { + throw new UnauthorizedException('content', $function); + } + + return $this->internalLoadRelations($versionInfo); + } + + /** + * Loads all outgoing relations for the given version without checking the permissions. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] + */ + protected function internalLoadRelations(APIVersionInfo $versionInfo): array + { + $contentInfo = $versionInfo->getContentInfo(); + $spiRelations = $this->persistenceHandler->contentHandler()->loadRelations( + $contentInfo->id, + $versionInfo->versionNo + ); + + /** @var $relations \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] */ + $relations = []; + foreach ($spiRelations as $spiRelation) { + $destinationContentInfo = $this->internalLoadContentInfoById($spiRelation->destinationContentId); + if (!$this->permissionResolver->canUser('content', 'read', $destinationContentInfo)) { + continue; + } + + $relations[] = $this->contentDomainMapper->buildRelationDomainObject( + $spiRelation, + $contentInfo, + $destinationContentInfo + ); + } + + return $relations; + } + + public function countRelations(APIVersionInfo $versionInfo): int + { + $function = $versionInfo->isPublished() ? 'read' : 'versionread'; + + if (!$this->permissionResolver->canUser('content', $function, $versionInfo)) { + return 0; + } + + $contentInfo = $versionInfo->getContentInfo(); + + return $this->persistenceHandler->contentHandler()->countRelations( + $contentInfo->id, + $versionInfo->versionNo + ); + } + + public function loadRelationList(APIVersionInfo $versionInfo, int $offset = 0, int $limit = self::DEFAULT_PAGE_SIZE): RelationList + { + $function = $versionInfo->isPublished() ? 'read' : 'versionread'; + + $list = new RelationList(); + + if (!$this->permissionResolver->canUser('content', $function, $versionInfo)) { + return $list; + } + + $contentInfo = $versionInfo->getContentInfo(); + $list->totalCount = $this->persistenceHandler->contentHandler()->countRelations( + $contentInfo->id, + $versionInfo->versionNo + ); + + if ($list->totalCount === 0) { + return $list; + } + + $persistenceRelationList = $this->persistenceHandler->contentHandler()->loadRelationList( + $contentInfo->id, + $limit, + $offset, + $versionInfo->versionNo, + ); + + $destinationContentIds = array_column($persistenceRelationList, 'destinationContentId'); + $destinationContentInfos = $this->persistenceHandler->contentHandler()->loadContentInfoList($destinationContentIds); + + foreach ($persistenceRelationList as $persistenceRelation) { + $contentId = $persistenceRelation->destinationContentId; + $destinationContentInfo = $this->contentDomainMapper->buildContentInfoDomainObject( + $destinationContentInfos[$contentId] + ); + + if (!$this->permissionResolver->canUser('content', 'read', $destinationContentInfo)) { + $list->items[] = new UnauthorizedRelationListItem( + 'content', + 'read', + ['contentId' => $destinationContentInfo->id] + ); + + continue; + } + $relation = $this->contentDomainMapper->buildRelationDomainObject( + $persistenceRelation, + $contentInfo, + $destinationContentInfo + ); + $list->items[] = new RelationListItem($relation); + } + + return $list; + } + + /** + * {@inheritdoc} + */ + public function countReverseRelations(ContentInfo $contentInfo): int + { + if (!$this->permissionResolver->canUser('content', 'reverserelatedlist', $contentInfo)) { + return 0; + } + + return $this->persistenceHandler->contentHandler()->countReverseRelations($contentInfo->getId()); + } + + /** + * Loads all incoming relations for a content object. + * + * The relations come only from published versions of the source content objects + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read this version + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] + */ + public function loadReverseRelations(ContentInfo $contentInfo): iterable + { + if (!$this->permissionResolver->canUser('content', 'reverserelatedlist', $contentInfo)) { + throw new UnauthorizedException('content', 'reverserelatedlist', ['contentId' => $contentInfo->id]); + } + + $spiRelations = $this->persistenceHandler->contentHandler()->loadReverseRelations( + $contentInfo->id + ); + + $returnArray = []; + foreach ($spiRelations as $spiRelation) { + $sourceContentInfo = $this->internalLoadContentInfoById($spiRelation->sourceContentId); + if (!$this->permissionResolver->canUser('content', 'read', $sourceContentInfo)) { + continue; + } + + $returnArray[] = $this->contentDomainMapper->buildRelationDomainObject( + $spiRelation, + $sourceContentInfo, + $contentInfo + ); + } + + return $returnArray; + } + + /** + * {@inheritdoc} + */ + public function loadReverseRelationList(ContentInfo $contentInfo, int $offset = 0, int $limit = -1): RelationList + { + $list = new RelationList(); + if (!$this->repository->getPermissionResolver()->canUser('content', 'reverserelatedlist', $contentInfo)) { + return $list; + } + + $list->totalCount = $this->persistenceHandler->contentHandler()->countReverseRelations( + $contentInfo->id + ); + if ($list->totalCount > 0) { + $spiRelationList = $this->persistenceHandler->contentHandler()->loadReverseRelationList( + $contentInfo->id, + $offset, + $limit + ); + foreach ($spiRelationList as $spiRelation) { + $sourceContentInfo = $this->internalLoadContentInfoById($spiRelation->sourceContentId); + if ($this->repository->getPermissionResolver()->canUser('content', 'read', $sourceContentInfo)) { + $relation = $this->contentDomainMapper->buildRelationDomainObject( + $spiRelation, + $sourceContentInfo, + $contentInfo + ); + $list->items[] = new RelationListItem($relation); + } else { + $list->items[] = new UnauthorizedRelationListItem( + 'content', + 'read', + ['contentId' => $sourceContentInfo->id] + ); + } + } + } + + return $list; + } + + /** + * Adds a relation of type common. + * + * The source of the relation is the content and version + * referenced by $versionInfo. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit this version + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $sourceVersion + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $destinationContent the destination of the relation + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation the newly created relation + */ + public function addRelation(APIVersionInfo $sourceVersion, ContentInfo $destinationContent): APIRelation + { + $sourceVersion = $this->loadVersionInfoById( + $sourceVersion->contentInfo->id, + $sourceVersion->versionNo + ); + + if (!$sourceVersion->isDraft()) { + throw new BadStateException( + '$sourceVersion', + 'Relations of type common can only be added to draft versions' + ); + } + + if (!$this->permissionResolver->canUser('content', 'edit', $sourceVersion)) { + throw new UnauthorizedException('content', 'edit', ['contentId' => $sourceVersion->contentInfo->id]); + } + + $sourceContentInfo = $sourceVersion->getContentInfo(); + + $this->repository->beginTransaction(); + try { + $spiRelation = $this->persistenceHandler->contentHandler()->addRelation( + new SPIRelationCreateStruct( + [ + 'sourceContentId' => $sourceContentInfo->id, + 'sourceContentVersionNo' => $sourceVersion->versionNo, + 'sourceFieldDefinitionId' => null, + 'destinationContentId' => $destinationContent->id, + 'type' => APIRelation::COMMON, + ] + ) + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->contentDomainMapper->buildRelationDomainObject($spiRelation, $sourceContentInfo, $destinationContent); + } + + /** + * Removes a relation of type COMMON from a draft. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed edit this version + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the version is not a draft + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is no relation of type COMMON for the given destination + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $sourceVersion + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $destinationContent + */ + public function deleteRelation(APIVersionInfo $sourceVersion, ContentInfo $destinationContent): void + { + $sourceVersion = $this->loadVersionInfoById( + $sourceVersion->contentInfo->id, + $sourceVersion->versionNo + ); + + if (!$sourceVersion->isDraft()) { + throw new BadStateException( + '$sourceVersion', + 'Relations of type common can only be added to draft versions' + ); + } + + if (!$this->permissionResolver->canUser('content', 'edit', $sourceVersion)) { + throw new UnauthorizedException('content', 'edit', ['contentId' => $sourceVersion->contentInfo->id]); + } + + $spiRelations = $this->persistenceHandler->contentHandler()->loadRelations( + $sourceVersion->getContentInfo()->id, + $sourceVersion->versionNo, + APIRelation::COMMON + ); + + if (empty($spiRelations)) { + throw new InvalidArgumentException( + '$sourceVersion', + 'There are no Relations of type COMMON for the given destination' + ); + } + + // there should be only one relation of type COMMON for each destination, + // but in case there were ever more then one, we will remove them all + // @todo: alternatively, throw BadStateException? + $this->repository->beginTransaction(); + try { + foreach ($spiRelations as $spiRelation) { + if ($spiRelation->destinationContentId == $destinationContent->id) { + $this->persistenceHandler->contentHandler()->removeRelation( + $spiRelation->id, + APIRelation::COMMON, + $spiRelation->destinationContentId + ); + } + } + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Delete Content item Translation from all Versions (including archived ones) of a Content Object. + * + * NOTE: this operation is risky and permanent, so user interface should provide a warning before performing it. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the specified Translation + * is the Main Translation of a Content Item. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed + * to delete the content (in one of the locations of the given Content Item). + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if languageCode argument + * is invalid for the given content. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param string $languageCode + * + * @since 6.13 + */ + public function deleteTranslation(ContentInfo $contentInfo, string $languageCode): void + { + if ($contentInfo->mainLanguageCode === $languageCode) { + throw new BadStateException( + '$languageCode', + 'The provided translation is the main translation of the Content item' + ); + } + + $translationWasFound = false; + $this->repository->beginTransaction(); + try { + $target = (new Target\Builder\VersionBuilder())->translateToAnyLanguageOf([$languageCode])->build(); + + foreach ($this->loadVersions($contentInfo) as $versionInfo) { + if (!$this->permissionResolver->canUser('content', 'remove', $versionInfo, [$target])) { + throw new UnauthorizedException( + 'content', + 'remove', + ['contentId' => $contentInfo->id, 'versionNo' => $versionInfo->versionNo, 'languageCode' => $languageCode] + ); + } + + if (!in_array($languageCode, $versionInfo->languageCodes)) { + continue; + } + + $translationWasFound = true; + + // If the translation is the version's only one, delete the version + if (count($versionInfo->languageCodes) < 2) { + $this->persistenceHandler->contentHandler()->deleteVersion( + $versionInfo->getContentInfo()->id, + $versionInfo->versionNo + ); + } + } + + if (!$translationWasFound) { + throw new InvalidArgumentException( + '$languageCode', + sprintf( + '%s does not exist in the Content item(id=%d)', + $languageCode, + $contentInfo->id + ) + ); + } + + $this->persistenceHandler->contentHandler()->deleteTranslationFromContent( + $contentInfo->id, + $languageCode + ); + $locationIds = array_map( + static function (Location $location) { + return $location->id; + }, + $this->repository->getLocationService()->loadLocations($contentInfo) + ); + $this->persistenceHandler->urlAliasHandler()->translationRemoved( + $locationIds, + $languageCode + ); + $this->repository->commit(); + } catch (InvalidArgumentException $e) { + $this->repository->rollback(); + throw $e; + } catch (BadStateException $e) { + $this->repository->rollback(); + throw $e; + } catch (UnauthorizedException $e) { + $this->repository->rollback(); + throw $e; + } catch (Exception $e) { + $this->repository->rollback(); + // cover generic unexpected exception to fulfill API promise on @throws + throw new BadStateException('$contentInfo', 'Translation removal failed', $e); + } + } + + /** + * Delete specified Translation from a Content Draft. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the specified Translation + * is the only one the Content Draft has or it is the main Translation of a Content Object. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed + * to edit the Content (in one of the locations of the given Content Object). + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if languageCode argument + * is invalid for the given Draft. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if specified Version was not found + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo Content Version Draft + * @param string $languageCode Language code of the Translation to be removed + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content Content Draft w/o the specified Translation + * + * @since 6.12 + */ + public function deleteTranslationFromDraft(APIVersionInfo $versionInfo, string $languageCode): APIContent + { + if (!$versionInfo->isDraft()) { + throw new BadStateException( + '$versionInfo', + 'The version is not a draft, so translations cannot be modified. Create a draft before proceeding' + ); + } + + if ($versionInfo->contentInfo->mainLanguageCode === $languageCode) { + throw new BadStateException( + '$languageCode', + 'the specified translation is the main translation of the Content item. Change it before proceeding.' + ); + } + + if (!$this->permissionResolver->canUser('content', 'edit', $versionInfo->contentInfo)) { + throw new UnauthorizedException( + 'content', + 'edit', + ['contentId' => $versionInfo->contentInfo->id] + ); + } + + if (!in_array($languageCode, $versionInfo->languageCodes)) { + throw new InvalidArgumentException( + '$languageCode', + sprintf( + 'The version (ContentId=%d, VersionNo=%d) is not translated into %s', + $versionInfo->contentInfo->id, + $versionInfo->versionNo, + $languageCode + ) + ); + } + + if (count($versionInfo->languageCodes) === 1) { + throw new BadStateException( + '$languageCode', + 'The provided translation is the only translation in this version' + ); + } + + $this->repository->beginTransaction(); + try { + $spiContent = $this->persistenceHandler->contentHandler()->deleteTranslationFromDraft( + $versionInfo->contentInfo->id, + $versionInfo->versionNo, + $languageCode + ); + $this->repository->commit(); + + return $this->contentDomainMapper->buildContentDomainObject( + $spiContent, + $this->repository->getContentTypeService()->loadContentType( + $spiContent->versionInfo->contentInfo->contentTypeId + ) + ); + } catch (APINotFoundException $e) { + // avoid wrapping expected NotFoundException in BadStateException handled below + $this->repository->rollback(); + throw $e; + } catch (Exception $e) { + $this->repository->rollback(); + // cover generic unexpected exception to fulfill API promise on @throws + throw new BadStateException('$contentInfo', 'Could not remove the translation', $e); + } + } + + /** + * Hides Content by making all the Locations appear hidden. + * It does not persist hidden state on Location object itself. + * + * Content hidden by this API can be revealed by revealContent API. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function hideContent(ContentInfo $contentInfo): void + { + $locationTarget = (new DestinationLocationTarget($contentInfo->mainLocationId, $contentInfo)); + if (!$this->permissionResolver->canUser( + 'content', + 'hide', + $contentInfo, + [$locationTarget] + )) { + throw new UnauthorizedException('content', 'hide', ['contentId' => $contentInfo->id]); + } + + $this->repository->beginTransaction(); + try { + $this->persistenceHandler->contentHandler()->updateMetadata( + $contentInfo->id, + new SPIMetadataUpdateStruct([ + 'isHidden' => true, + ]) + ); + $locationHandler = $this->persistenceHandler->locationHandler(); + $childLocations = $locationHandler->loadLocationsByContent($contentInfo->id); + foreach ($childLocations as $childLocation) { + $locationHandler->setInvisible($childLocation->id); + } + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Reveals Content hidden by hideContent API. + * Locations which were hidden before hiding Content will remain hidden. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function revealContent(ContentInfo $contentInfo): void + { + $locationTarget = (new DestinationLocationTarget($contentInfo->mainLocationId, $contentInfo)); + if (!$this->permissionResolver->canUser( + 'content', + 'hide', + $contentInfo, + [$locationTarget] + )) { + throw new UnauthorizedException('content', 'hide', ['contentId' => $contentInfo->id]); + } + + $this->repository->beginTransaction(); + try { + $this->persistenceHandler->contentHandler()->updateMetadata( + $contentInfo->id, + new SPIMetadataUpdateStruct([ + 'isHidden' => false, + ]) + ); + $locationHandler = $this->persistenceHandler->locationHandler(); + $childLocations = $locationHandler->loadLocationsByContent($contentInfo->id); + foreach ($childLocations as $childLocation) { + $locationHandler->setVisible($childLocation->id); + } + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Instantiates a new content create struct object. + * + * alwaysAvailable is set to the ContentType's defaultAlwaysAvailable + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param string $mainLanguageCode + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct + */ + public function newContentCreateStruct(ContentType $contentType, string $mainLanguageCode): APIContentCreateStruct + { + return new ContentCreateStruct( + [ + 'contentType' => $contentType, + 'mainLanguageCode' => $mainLanguageCode, + 'alwaysAvailable' => $contentType->defaultAlwaysAvailable, + ] + ); + } + + /** + * Instantiates a new content meta data update struct. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentMetadataUpdateStruct + */ + public function newContentMetadataUpdateStruct(): ContentMetadataUpdateStruct + { + return new ContentMetadataUpdateStruct(); + } + + /** + * Instantiates a new content update struct. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct + */ + public function newContentUpdateStruct(): APIContentUpdateStruct + { + return new ContentUpdateStruct(); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\User\User|null $user + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserReference + */ + private function resolveUser(?User $user): UserReference + { + if ($user === null) { + $user = $this->permissionResolver->getCurrentUserReference(); + } + + return $user; + } + + public function validate( + ValueObject $object, + array $context = [], + ?array $fieldIdentifiersToValidate = null + ): array { + return $this->contentValidator->validate( + $object, + $context, + $fieldIdentifiersToValidate + ); + } + + public function find(Filter $filter, ?array $languages = null): ContentList + { + $filter = clone $filter; + if (!empty($languages)) { + $filter->andWithCriterion(new LanguageCode($languages)); + } + + $permissionCriterion = $this->permissionResolver->getQueryPermissionsCriterion(); + if ($permissionCriterion instanceof Criterion\MatchNone) { + return new ContentList(0, []); + } + + if (!$permissionCriterion instanceof Criterion\MatchAll) { + if (!$permissionCriterion instanceof FilteringCriterion) { + return new ContentList(0, []); + } + $filter->andWithCriterion($permissionCriterion); + } + + $contentItems = []; + $contentItemsIterator = $this->contentFilteringHandler->find($filter); + foreach ($contentItemsIterator as $contentItem) { + $contentItems[] = $this->contentDomainMapper->buildContentDomainObjectFromPersistence( + $contentItem->content, + $contentItem->type, + $languages, + ); + } + + return new ContentList($contentItemsIterator->getTotalCount(), $contentItems); + } + + public function count(Filter $filter, ?array $languages = null): int + { + $filter = clone $filter; + if (!empty($languages)) { + $filter->andWithCriterion(new LanguageCode($languages)); + } + + $permissionCriterion = $this->permissionResolver->getQueryPermissionsCriterion(); + if ($permissionCriterion instanceof Criterion\MatchNone) { + return 0; + } + + if (!$permissionCriterion instanceof Criterion\MatchAll) { + if (!$permissionCriterion instanceof FilteringCriterion) { + return 0; + } + + $filter->andWithCriterion($permissionCriterion); + } + + return $this->contentFilteringHandler->count($filter); + } +} + +class_alias(ContentService::class, 'eZ\Publish\Core\Repository\ContentService'); diff --git a/src/lib/Repository/ContentTypeService.php b/src/lib/Repository/ContentTypeService.php new file mode 100644 index 0000000000..8476c6f021 --- /dev/null +++ b/src/lib/Repository/ContentTypeService.php @@ -0,0 +1,1662 @@ +repository = $repository; + $this->contentTypeHandler = $contentTypeHandler; + $this->userHandler = $userHandler; + $this->contentDomainMapper = $contentDomainMapper; + $this->contentTypeDomainMapper = $contentTypeDomainMapper; + $this->fieldTypeRegistry = $fieldTypeRegistry; + // Union makes sure default settings are ignored if provided in argument + $this->settings = $settings + [ + //'defaultSetting' => array(), + ]; + $this->permissionResolver = $permissionResolver; + } + + /** + * Create a content type group object. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to create a content type group + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If a group with the same identifier already exists + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupCreateStruct $contentTypeGroupCreateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup + */ + public function createContentTypeGroup(ContentTypeGroupCreateStruct $contentTypeGroupCreateStruct): APIContentTypeGroup + { + if (!$this->permissionResolver->canUser('class', 'create', $contentTypeGroupCreateStruct)) { + throw new UnauthorizedException('ContentType', 'create'); + } + + try { + $this->loadContentTypeGroupByIdentifier($contentTypeGroupCreateStruct->identifier); + + throw new InvalidArgumentException( + '$contentTypeGroupCreateStruct', + "A group with the identifier '{$contentTypeGroupCreateStruct->identifier}' already exists" + ); + } catch (APINotFoundException $e) { + // Do nothing + } + + if ($contentTypeGroupCreateStruct->creationDate === null) { + $timestamp = time(); + } else { + $timestamp = $contentTypeGroupCreateStruct->creationDate->getTimestamp(); + } + + if ($contentTypeGroupCreateStruct->creatorId === null) { + $userId = $this->permissionResolver->getCurrentUserReference()->getUserId(); + } else { + $userId = $contentTypeGroupCreateStruct->creatorId; + } + + $spiGroupCreateStruct = new SPIContentTypeGroupCreateStruct( + [ + 'identifier' => $contentTypeGroupCreateStruct->identifier, + 'created' => $timestamp, + 'modified' => $timestamp, + 'creatorId' => $userId, + 'modifierId' => $userId, + 'isSystem' => $contentTypeGroupCreateStruct->isSystem, + ] + ); + + $this->repository->beginTransaction(); + try { + $spiContentTypeGroup = $this->contentTypeHandler->createGroup( + $spiGroupCreateStruct + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->contentTypeDomainMapper->buildContentTypeGroupDomainObject($spiContentTypeGroup); + } + + /** + * {@inheritdoc} + */ + public function loadContentTypeGroup(int $contentTypeGroupId, array $prioritizedLanguages = []): APIContentTypeGroup + { + $spiGroup = $this->contentTypeHandler->loadGroup( + $contentTypeGroupId + ); + + return $this->contentTypeDomainMapper->buildContentTypeGroupDomainObject($spiGroup, $prioritizedLanguages); + } + + /** + * {@inheritdoc} + */ + public function loadContentTypeGroupByIdentifier(string $contentTypeGroupIdentifier, array $prioritizedLanguages = []): APIContentTypeGroup + { + try { + $spiGroup = $this->contentTypeHandler->loadGroupByIdentifier( + $contentTypeGroupIdentifier + ); + } catch (APINotFoundException $e) { + throw new NotFoundException('ContentTypeGroup', $contentTypeGroupIdentifier); + } + + return $this->contentTypeDomainMapper->buildContentTypeGroupDomainObject($spiGroup, $prioritizedLanguages); + } + + /** + * {@inheritdoc} + */ + public function loadContentTypeGroups(array $prioritizedLanguages = []): iterable + { + $spiGroups = $this->contentTypeHandler->loadAllGroups(); + + $groups = []; + foreach ($spiGroups as $spiGroup) { + $groups[] = $this->contentTypeDomainMapper->buildContentTypeGroupDomainObject($spiGroup, $prioritizedLanguages); + } + + return $groups; + } + + /** + * Update a content type group object. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to create a content type group + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the given identifier (if set) already exists + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup $contentTypeGroup the content type group to be updated + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupUpdateStruct $contentTypeGroupUpdateStruct + */ + public function updateContentTypeGroup(APIContentTypeGroup $contentTypeGroup, ContentTypeGroupUpdateStruct $contentTypeGroupUpdateStruct): void + { + if (!$this->permissionResolver->canUser('class', 'update', $contentTypeGroup)) { + throw new UnauthorizedException('ContentType', 'update'); + } + + $loadedContentTypeGroup = $this->loadContentTypeGroup($contentTypeGroup->id); + + if ($contentTypeGroupUpdateStruct->identifier !== null + && $contentTypeGroupUpdateStruct->identifier !== $loadedContentTypeGroup->identifier) { + try { + $this->loadContentTypeGroupByIdentifier($contentTypeGroupUpdateStruct->identifier); + + throw new InvalidArgumentException( + '$contentTypeGroupUpdateStruct->identifier', + 'given identifier already exists' + ); + } catch (APINotFoundException $e) { + // Do nothing + } + } + + if ($contentTypeGroupUpdateStruct->modificationDate !== null) { + $modifiedTimestamp = $contentTypeGroupUpdateStruct->modificationDate->getTimestamp(); + } else { + $modifiedTimestamp = time(); + } + + $spiGroupUpdateStruct = new SPIContentTypeGroupUpdateStruct( + [ + 'id' => $loadedContentTypeGroup->id, + 'identifier' => $contentTypeGroupUpdateStruct->identifier === null ? + $loadedContentTypeGroup->identifier : + $contentTypeGroupUpdateStruct->identifier, + 'modified' => $modifiedTimestamp, + 'modifierId' => $contentTypeGroupUpdateStruct->modifierId === null ? + $this->permissionResolver->getCurrentUserReference()->getUserId() : + $contentTypeGroupUpdateStruct->modifierId, + 'isSystem' => $contentTypeGroupUpdateStruct->isSystem, + ] + ); + + $this->repository->beginTransaction(); + try { + $this->contentTypeHandler->updateGroup( + $spiGroupUpdateStruct + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Delete a content type group. + * + * This method only deletes an content type group which has content types without any content instances + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete a content type group + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If a to be deleted content type has instances + */ + public function deleteContentTypeGroup(APIContentTypeGroup $contentTypeGroup): void + { + if (!$this->permissionResolver->canUser('class', 'delete', $contentTypeGroup)) { + throw new UnauthorizedException('ContentType', 'delete'); + } + + $loadedContentTypeGroup = $this->loadContentTypeGroup($contentTypeGroup->id); + + $this->repository->beginTransaction(); + try { + $contentTypesDrafts = $this->contentTypeHandler->loadContentTypes($contentTypeGroup->id, SPIContentType::STATUS_DRAFT); + foreach ($contentTypesDrafts as $contentTypeDraft) { + $this->contentTypeHandler->delete($contentTypeDraft->id, SPIContentType::STATUS_DRAFT); + } + + $this->contentTypeHandler->deleteGroup( + $loadedContentTypeGroup->id + ); + $this->repository->commit(); + } catch (APIBadStateException $e) { + $this->repository->rollback(); + throw new InvalidArgumentException( + '$contentTypeGroup', + 'Content type group contains content types', + $e + ); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Validates input ContentType create struct. + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentValue + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeCreateStruct $contentTypeCreateStruct + */ + protected function validateInputContentTypeCreateStruct(APIContentTypeCreateStruct $contentTypeCreateStruct): void + { + // Required properties + + if ($contentTypeCreateStruct->identifier === null) { + throw new InvalidArgumentException('$contentTypeCreateStruct', "Property 'identifier' is required"); + } + + if (!is_string($contentTypeCreateStruct->identifier)) { + throw new InvalidArgumentType( + '$contentTypeCreateStruct->identifier', + 'string', + $contentTypeCreateStruct->identifier + ); + } + + if ($contentTypeCreateStruct->identifier === '') { + throw new InvalidArgumentValue( + '$contentTypeCreateStruct->identifier', + $contentTypeCreateStruct->identifier + ); + } + + if ($contentTypeCreateStruct->mainLanguageCode === null) { + throw new InvalidArgumentException('$contentTypeCreateStruct', "Property 'mainLanguageCode' is required"); + } + + if (!is_string($contentTypeCreateStruct->mainLanguageCode)) { + throw new InvalidArgumentType( + '$contentTypeCreateStruct->mainLanguageCode', + 'string', + $contentTypeCreateStruct->mainLanguageCode + ); + } + + if ($contentTypeCreateStruct->mainLanguageCode === '') { + throw new InvalidArgumentValue( + '$contentTypeCreateStruct->mainLanguageCode', + $contentTypeCreateStruct->mainLanguageCode + ); + } + + if ($contentTypeCreateStruct->names !== null) { + $this->contentDomainMapper->validateTranslatedList( + $contentTypeCreateStruct->names, + '$contentTypeCreateStruct->names' + ); + } + + if (!isset($contentTypeCreateStruct->names[$contentTypeCreateStruct->mainLanguageCode]) || + $contentTypeCreateStruct->names[$contentTypeCreateStruct->mainLanguageCode] === '' + ) { + throw new InvalidArgumentException( + '$contentTypeCreateStruct->names', + 'At least one name in the main language is required' + ); + } + + // Optional properties + + if ($contentTypeCreateStruct->descriptions !== null) { + $this->contentDomainMapper->validateTranslatedList( + $contentTypeCreateStruct->descriptions, + '$contentTypeCreateStruct->descriptions' + ); + } + + if ($contentTypeCreateStruct->defaultSortField !== null && !$this->contentDomainMapper->isValidLocationSortField($contentTypeCreateStruct->defaultSortField)) { + throw new InvalidArgumentValue( + '$contentTypeCreateStruct->defaultSortField', + $contentTypeCreateStruct->defaultSortField + ); + } + + if ($contentTypeCreateStruct->defaultSortOrder !== null && !$this->contentDomainMapper->isValidLocationSortOrder($contentTypeCreateStruct->defaultSortOrder)) { + throw new InvalidArgumentValue( + '$contentTypeCreateStruct->defaultSortOrder', + $contentTypeCreateStruct->defaultSortOrder + ); + } + + if ($contentTypeCreateStruct->creatorId !== null) { + $this->repository->getUserService()->loadUser($contentTypeCreateStruct->creatorId); + } + + if ($contentTypeCreateStruct->creationDate !== null && !$contentTypeCreateStruct->creationDate instanceof DateTime) { + throw new InvalidArgumentType( + '$contentTypeCreateStruct->creationDate', + 'DateTime', + $contentTypeCreateStruct->creationDate + ); + } + + if ($contentTypeCreateStruct->defaultAlwaysAvailable !== null && !is_bool($contentTypeCreateStruct->defaultAlwaysAvailable)) { + throw new InvalidArgumentType( + '$contentTypeCreateStruct->defaultAlwaysAvailable', + 'boolean', + $contentTypeCreateStruct->defaultAlwaysAvailable + ); + } + + if ($contentTypeCreateStruct->isContainer !== null && !is_bool($contentTypeCreateStruct->isContainer)) { + throw new InvalidArgumentType( + '$contentTypeCreateStruct->isContainer', + 'boolean', + $contentTypeCreateStruct->isContainer + ); + } + + if ($contentTypeCreateStruct->remoteId !== null && !is_string($contentTypeCreateStruct->remoteId)) { + throw new InvalidArgumentType( + '$contentTypeCreateStruct->remoteId', + 'string', + $contentTypeCreateStruct->remoteId + ); + } + + if ($contentTypeCreateStruct->nameSchema !== null && !is_string($contentTypeCreateStruct->nameSchema)) { + throw new InvalidArgumentType( + '$contentTypeCreateStruct->nameSchema', + 'string', + $contentTypeCreateStruct->nameSchema + ); + } + + if ($contentTypeCreateStruct->urlAliasSchema !== null && !is_string($contentTypeCreateStruct->urlAliasSchema)) { + throw new InvalidArgumentType( + '$contentTypeCreateStruct->urlAliasSchema', + 'string', + $contentTypeCreateStruct->urlAliasSchema + ); + } + + foreach ($contentTypeCreateStruct->fieldDefinitions as $key => $fieldDefinitionCreateStruct) { + if (!$fieldDefinitionCreateStruct instanceof FieldDefinitionCreateStruct) { + throw new InvalidArgumentType( + "\$contentTypeCreateStruct->fieldDefinitions[$key]", + FieldDefinitionCreateStruct::class, + $fieldDefinitionCreateStruct + ); + } + + $this->validateInputFieldDefinitionCreateStruct( + $fieldDefinitionCreateStruct, + "\$contentTypeCreateStruct->fieldDefinitions[$key]" + ); + } + } + + /** + * Validates input ContentTypeGroup array. + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup[] $contentTypeGroups + */ + protected function validateInputContentTypeGroups(array $contentTypeGroups): void + { + if (empty($contentTypeGroups)) { + throw new InvalidArgumentException( + '$contentTypeGroups', + 'The argument must contain at least one content type group' + ); + } + + foreach ($contentTypeGroups as $key => $contentTypeGroup) { + if (!$contentTypeGroup instanceof APIContentTypeGroup) { + throw new InvalidArgumentType( + "\$contentTypeGroups[{$key}]", + ContentTypeGroup::class, + $contentTypeGroup + ); + } + } + } + + /** + * Validates input FieldDefinitionCreateStruct. + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentValue + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct $fieldDefinitionCreateStruct + * @param string $argumentName + */ + protected function validateInputFieldDefinitionCreateStruct( + FieldDefinitionCreateStruct $fieldDefinitionCreateStruct, + string $argumentName = '$fieldDefinitionCreateStruct' + ): void { + // Required properties + + if ($fieldDefinitionCreateStruct->fieldTypeIdentifier === null) { + throw new InvalidArgumentException($argumentName, "Property 'fieldTypeIdentifier' is required"); + } + + if (!is_string($fieldDefinitionCreateStruct->fieldTypeIdentifier)) { + throw new InvalidArgumentType( + $argumentName . '->fieldTypeIdentifier', + 'string', + $fieldDefinitionCreateStruct->fieldTypeIdentifier + ); + } + + if ($fieldDefinitionCreateStruct->fieldTypeIdentifier === '') { + throw new InvalidArgumentValue( + $argumentName . '->fieldTypeIdentifier', + $fieldDefinitionCreateStruct->fieldTypeIdentifier + ); + } + + if ($fieldDefinitionCreateStruct->identifier === null) { + throw new InvalidArgumentException($argumentName, "Property 'identifier' is required"); + } + + if (!is_string($fieldDefinitionCreateStruct->identifier)) { + throw new InvalidArgumentType( + $argumentName . '->identifier', + 'string', + $fieldDefinitionCreateStruct->identifier + ); + } + + if ($fieldDefinitionCreateStruct->identifier === '') { + throw new InvalidArgumentValue( + $argumentName . '->identifier', + $fieldDefinitionCreateStruct->identifier + ); + } + + // Optional properties + + if ($fieldDefinitionCreateStruct->names !== null) { + $this->contentDomainMapper->validateTranslatedList( + $fieldDefinitionCreateStruct->names, + $argumentName . '->names' + ); + } + + if ($fieldDefinitionCreateStruct->descriptions !== null) { + $this->contentDomainMapper->validateTranslatedList( + $fieldDefinitionCreateStruct->descriptions, + $argumentName . '->descriptions' + ); + } + + if ($fieldDefinitionCreateStruct->fieldGroup !== null && !is_string($fieldDefinitionCreateStruct->fieldGroup)) { + throw new InvalidArgumentType( + $argumentName . '->fieldGroup', + 'string', + $fieldDefinitionCreateStruct->fieldGroup + ); + } + + if ($fieldDefinitionCreateStruct->position !== null && !is_int($fieldDefinitionCreateStruct->position)) { + throw new InvalidArgumentType( + $argumentName . '->position', + 'integer', + $fieldDefinitionCreateStruct->position + ); + } + + if ($fieldDefinitionCreateStruct->isTranslatable !== null && !is_bool($fieldDefinitionCreateStruct->isTranslatable)) { + throw new InvalidArgumentType( + $argumentName . '->isTranslatable', + 'boolean', + $fieldDefinitionCreateStruct->isTranslatable + ); + } + + if ($fieldDefinitionCreateStruct->isRequired !== null && !is_bool($fieldDefinitionCreateStruct->isRequired)) { + throw new InvalidArgumentType( + $argumentName . '->isRequired', + 'boolean', + $fieldDefinitionCreateStruct->isRequired + ); + } + + if ($fieldDefinitionCreateStruct->isThumbnail !== null && !is_bool($fieldDefinitionCreateStruct->isThumbnail)) { + throw new InvalidArgumentType( + $argumentName . '->isThumbnail', + 'boolean', + $fieldDefinitionCreateStruct->isThumbnail + ); + } + + if ($fieldDefinitionCreateStruct->isInfoCollector !== null && !is_bool($fieldDefinitionCreateStruct->isInfoCollector)) { + throw new InvalidArgumentType( + $argumentName . '->isInfoCollector', + 'boolean', + $fieldDefinitionCreateStruct->isInfoCollector + ); + } + + if ($fieldDefinitionCreateStruct->isSearchable !== null && !is_bool($fieldDefinitionCreateStruct->isSearchable)) { + throw new InvalidArgumentType( + $argumentName . '->isSearchable', + 'boolean', + $fieldDefinitionCreateStruct->isSearchable + ); + } + + // These properties are of type 'mixed' and are validated separately by the corresponding field type + // validatorConfiguration + // fieldSettings + // defaultValue + } + + /** + * Create a content type object. + * + * The content type is created in the state STATUS_DRAFT. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to create a content type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException In case when + * - array of content type groups does not contain at least one content type group + * - identifier or remoteId in the content type create struct already exists + * - there is a duplicate field identifier in the content type create struct + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException + * if a field definition in the $contentTypeCreateStruct is not valid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeValidationException + * if a multiple field definitions of a same singular type are given + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeCreateStruct $contentTypeCreateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup[] $contentTypeGroups Required array of {@link APIContentTypeGroup} to link type with (must contain one) + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft + */ + public function createContentType(APIContentTypeCreateStruct $contentTypeCreateStruct, array $contentTypeGroups): APIContentTypeDraft + { + if (!$this->permissionResolver->canUser('class', 'create', $contentTypeCreateStruct, $contentTypeGroups)) { + throw new UnauthorizedException('ContentType', 'create'); + } + + // Prevent argument mutation + $contentTypeCreateStruct = clone $contentTypeCreateStruct; + $this->validateInputContentTypeCreateStruct($contentTypeCreateStruct); + $this->validateInputContentTypeGroups($contentTypeGroups); + $initialLanguageId = $this->repository->getContentLanguageService()->loadLanguage( + $contentTypeCreateStruct->mainLanguageCode + )->id; + + try { + $this->contentTypeHandler->loadByIdentifier( + $contentTypeCreateStruct->identifier + ); + + throw new InvalidArgumentException( + '$contentTypeCreateStruct', + "Another content type with identifier '{$contentTypeCreateStruct->identifier}' exists" + ); + } catch (APINotFoundException $e) { + // Do nothing + } + + if ($contentTypeCreateStruct->remoteId !== null) { + try { + $this->contentTypeHandler->loadByRemoteId( + $contentTypeCreateStruct->remoteId + ); + + throw new InvalidArgumentException( + '$contentTypeCreateStruct', + "Another content type with remoteId '{$contentTypeCreateStruct->remoteId}' exists" + ); + } catch (APINotFoundException $e) { + // Do nothing + } + } + + $fieldDefinitionIdentifierSet = []; + $fieldDefinitionPositionSet = []; + foreach ($contentTypeCreateStruct->fieldDefinitions as $fieldDefinitionCreateStruct) { + // Check for duplicate identifiers + if (!isset($fieldDefinitionIdentifierSet[$fieldDefinitionCreateStruct->identifier])) { + $fieldDefinitionIdentifierSet[$fieldDefinitionCreateStruct->identifier] = true; + } else { + throw new InvalidArgumentException( + '$contentTypeCreateStruct', + "The argument contains duplicate Field definition identifier '{$fieldDefinitionCreateStruct->identifier}'" + ); + } + + // Check for duplicate positions + if (!isset($fieldDefinitionPositionSet[$fieldDefinitionCreateStruct->position])) { + $fieldDefinitionPositionSet[$fieldDefinitionCreateStruct->position] = true; + } else { + throw new InvalidArgumentException( + '$contentTypeCreateStruct', + "The argument contains duplicate Field definition position '{$fieldDefinitionCreateStruct->position}'" + ); + } + } + + $allValidationErrors = []; + $spiFieldDefinitions = []; + $fieldTypeIdentifierSet = []; + foreach ($contentTypeCreateStruct->fieldDefinitions as $fieldDefinitionCreateStruct) { + /** @var $fieldType \Ibexa\Contracts\Core\FieldType\FieldType */ + $fieldType = $this->fieldTypeRegistry->getFieldType( + $fieldDefinitionCreateStruct->fieldTypeIdentifier + ); + + if ($fieldType->isSingular() && isset($fieldTypeIdentifierSet[$fieldDefinitionCreateStruct->fieldTypeIdentifier])) { + throw new ContentTypeValidationException( + "Field Type '%identifier%' is singular and cannot be used more than once in a content type", + ['%identifier%' => $fieldDefinitionCreateStruct->fieldTypeIdentifier] + ); + } + + $fieldTypeIdentifierSet[$fieldDefinitionCreateStruct->fieldTypeIdentifier] = true; + + $fieldType->applyDefaultSettings($fieldDefinitionCreateStruct->fieldSettings); + $fieldType->applyDefaultValidatorConfiguration($fieldDefinitionCreateStruct->validatorConfiguration); + $validationErrors = $this->validateFieldDefinitionCreateStruct( + $fieldDefinitionCreateStruct, + $fieldType + ); + + if (!empty($validationErrors)) { + $allValidationErrors[$fieldDefinitionCreateStruct->identifier] = $validationErrors; + } + + if (!empty($allValidationErrors)) { + continue; + } + + $spiFieldDefinitions[] = $this->contentTypeDomainMapper->buildSPIFieldDefinitionFromCreateStruct( + $fieldDefinitionCreateStruct, + $fieldType, + $contentTypeCreateStruct->mainLanguageCode + ); + } + + if (!empty($allValidationErrors)) { + throw new ContentTypeFieldDefinitionValidationException($allValidationErrors); + } + + $groupIds = array_map( + static function (APIContentTypeGroup $contentTypeGroup) { + return $contentTypeGroup->id; + }, + $contentTypeGroups + ); + + if ($contentTypeCreateStruct->creatorId === null) { + $contentTypeCreateStruct->creatorId = $this->permissionResolver->getCurrentUserReference()->getUserId(); + } + + if ($contentTypeCreateStruct->creationDate === null) { + $timestamp = time(); + } else { + $timestamp = $contentTypeCreateStruct->creationDate->getTimestamp(); + } + + if ($contentTypeCreateStruct->remoteId === null) { + $contentTypeCreateStruct->remoteId = $this->contentDomainMapper->getUniqueHash($contentTypeCreateStruct); + } + + $spiContentTypeCreateStruct = new SPIContentTypeCreateStruct( + [ + 'identifier' => $contentTypeCreateStruct->identifier, + 'name' => $contentTypeCreateStruct->names, + 'status' => APIContentType::STATUS_DRAFT, + 'description' => $contentTypeCreateStruct->descriptions ?? [], + 'created' => $timestamp, + 'modified' => $timestamp, + 'creatorId' => $contentTypeCreateStruct->creatorId, + 'modifierId' => $contentTypeCreateStruct->creatorId, + 'remoteId' => $contentTypeCreateStruct->remoteId, + 'urlAliasSchema' => $contentTypeCreateStruct->urlAliasSchema ?? '', + 'nameSchema' => $contentTypeCreateStruct->nameSchema ?? '', + 'isContainer' => $contentTypeCreateStruct->isContainer ?? false, + 'initialLanguageId' => $initialLanguageId, + 'sortField' => $contentTypeCreateStruct->defaultSortField ?? Location::SORT_FIELD_PUBLISHED, + 'sortOrder' => $contentTypeCreateStruct->defaultSortOrder ?? Location::SORT_ORDER_DESC, + 'groupIds' => $groupIds, + 'fieldDefinitions' => $spiFieldDefinitions, + 'defaultAlwaysAvailable' => $contentTypeCreateStruct->defaultAlwaysAvailable, + ] + ); + + $this->repository->beginTransaction(); + try { + $spiContentType = $this->contentTypeHandler->create( + $spiContentTypeCreateStruct + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->contentTypeDomainMapper->buildContentTypeDraftDomainObject($spiContentType); + } + + /** + * Validates FieldDefinitionCreateStruct. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct $fieldDefinitionCreateStruct + * @param \Ibexa\Contracts\Core\FieldType\FieldType $fieldType + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + protected function validateFieldDefinitionCreateStruct(FieldDefinitionCreateStruct $fieldDefinitionCreateStruct, SPIFieldType $fieldType): array + { + $validationErrors = []; + + if ($fieldDefinitionCreateStruct->isSearchable && !$fieldType->isSearchable()) { + $validationErrors[] = new ValidationError( + "FieldType '{$fieldDefinitionCreateStruct->fieldTypeIdentifier}' is not searchable" + ); + } + + return array_merge( + $validationErrors, + $fieldType->validateValidatorConfiguration($fieldDefinitionCreateStruct->validatorConfiguration), + $fieldType->validateFieldSettings($fieldDefinitionCreateStruct->fieldSettings) + ); + } + + /** + * {@inheritdoc} + */ + public function loadContentType(int $contentTypeId, array $prioritizedLanguages = []): ContentType + { + $spiContentType = $this->contentTypeHandler->load($contentTypeId); + + return $this->contentTypeDomainMapper->buildContentTypeDomainObject( + $spiContentType, + $prioritizedLanguages + ); + } + + /** + * {@inheritdoc} + */ + public function loadContentTypeByIdentifier(string $identifier, array $prioritizedLanguages = []): ContentType + { + $spiContentType = $this->contentTypeHandler->loadByIdentifier( + $identifier + ); + + return $this->contentTypeDomainMapper->buildContentTypeDomainObject( + $spiContentType, + $prioritizedLanguages + ); + } + + /** + * {@inheritdoc} + */ + public function loadContentTypeByRemoteId(string $remoteId, array $prioritizedLanguages = []): ContentType + { + $spiContentType = $this->contentTypeHandler->loadByRemoteId($remoteId); + + return $this->contentTypeDomainMapper->buildContentTypeDomainObject( + $spiContentType, + $prioritizedLanguages + ); + } + + /** + * Get a content type object draft by id. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If the content type draft owned by the current user can not be found + * + * @param int $contentTypeId + * @param bool $ignoreOwnership if true, method will return draft even if the owner is different than currently logged in user + * + * @todo Use another exception when user of draft is someone else + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft + */ + public function loadContentTypeDraft(int $contentTypeId, bool $ignoreOwnership = false): APIContentTypeDraft + { + $spiContentType = $this->contentTypeHandler->load( + $contentTypeId, + SPIContentType::STATUS_DRAFT + ); + + if (!$ignoreOwnership && $spiContentType->modifierId != $this->permissionResolver->getCurrentUserReference()->getUserId()) { + throw new NotFoundException('The content type is owned by someone else', $contentTypeId); + } + + return $this->contentTypeDomainMapper->buildContentTypeDraftDomainObject($spiContentType); + } + + /** + * {@inheritdoc} + */ + public function loadContentTypeList(array $contentTypeIds, array $prioritizedLanguages = []): iterable + { + $spiContentTypes = $this->contentTypeHandler->loadContentTypeList($contentTypeIds); + $contentTypes = []; + + // @todo We could bulk load content type group proxies involved in the future & pass those relevant per type to mapper + foreach ($spiContentTypes as $spiContentType) { + $contentTypes[$spiContentType->id] = $this->contentTypeDomainMapper->buildContentTypeDomainObject( + $spiContentType, + $prioritizedLanguages + ); + } + + return $contentTypes; + } + + /** + * {@inheritdoc} + */ + public function loadContentTypes(APIContentTypeGroup $contentTypeGroup, array $prioritizedLanguages = []): iterable + { + $spiContentTypes = $this->contentTypeHandler->loadContentTypes( + $contentTypeGroup->id, + SPIContentType::STATUS_DEFINED + ); + $contentTypes = []; + + foreach ($spiContentTypes as $spiContentType) { + $contentTypes[] = $this->contentTypeDomainMapper->buildContentTypeDomainObject( + $spiContentType, + $prioritizedLanguages + ); + } + + return $contentTypes; + } + + /** + * Creates a draft from an existing content type. + * + * This is a complete copy of the content + * type which has the state STATUS_DRAFT. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit a content type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If there is already a draft assigned to another user + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft + */ + public function createContentTypeDraft(APIContentType $contentType): APIContentTypeDraft + { + if (!$this->permissionResolver->canUser('class', 'create', $contentType)) { + throw new UnauthorizedException('ContentType', 'create'); + } + + try { + $this->contentTypeHandler->load( + $contentType->id, + SPIContentType::STATUS_DRAFT + ); + + throw new BadStateException( + '$contentType', + 'Draft of the content type already exists' + ); + } catch (APINotFoundException $e) { + $this->repository->beginTransaction(); + try { + $spiContentType = $this->contentTypeHandler->createDraft( + $this->permissionResolver->getCurrentUserReference()->getUserId(), + $contentType->id + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + return $this->contentTypeDomainMapper->buildContentTypeDraftDomainObject($spiContentType); + } + + /** + * Update a content type object. + * + * Does not update fields (fieldDefinitions), use {@link updateFieldDefinition()} to update them. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to update a content type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the given identifier or remoteId already exists + * or there is no draft assigned to the authenticated user + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeUpdateStruct $contentTypeUpdateStruct + */ + public function updateContentTypeDraft(APIContentTypeDraft $contentTypeDraft, ContentTypeUpdateStruct $contentTypeUpdateStruct): void + { + if (!$this->permissionResolver->canUser('class', 'update', $contentTypeDraft)) { + throw new UnauthorizedException('ContentType', 'update'); + } + + try { + $loadedContentTypeDraft = $this->loadContentTypeDraft($contentTypeDraft->id); + } catch (APINotFoundException $e) { + throw new InvalidArgumentException( + '$contentTypeDraft', + 'There is no content type draft assigned to the authenticated user', + $e + ); + } + + if ($contentTypeUpdateStruct->identifier !== null + && $contentTypeUpdateStruct->identifier != $loadedContentTypeDraft->identifier) { + try { + $this->loadContentTypeByIdentifier($contentTypeUpdateStruct->identifier); + + throw new InvalidArgumentException( + '$contentTypeUpdateStruct', + "Another content type with identifier '{$contentTypeUpdateStruct->identifier}' exists" + ); + } catch (APINotFoundException $e) { + // Do nothing + } + } + + if ($contentTypeUpdateStruct->remoteId !== null + && $contentTypeUpdateStruct->remoteId != $loadedContentTypeDraft->remoteId) { + try { + $this->loadContentTypeByRemoteId($contentTypeUpdateStruct->remoteId); + + throw new InvalidArgumentException( + '$contentTypeUpdateStruct', + "Another content type with remoteId '{$contentTypeUpdateStruct->remoteId}' exists" + ); + } catch (APINotFoundException $e) { + // Do nothing + } + } + + //Merge new translations into existing before update + $contentTypeUpdateStruct->names = array_merge($contentTypeDraft->getNames(), $contentTypeUpdateStruct->names ?? []); + $contentTypeUpdateStruct->descriptions = array_merge($contentTypeDraft->getDescriptions(), $contentTypeUpdateStruct->descriptions ?? []); + + $this->repository->beginTransaction(); + try { + $this->contentTypeHandler->update( + $contentTypeDraft->id, + $contentTypeDraft->status, + $this->contentTypeDomainMapper->buildSPIContentTypeUpdateStruct( + $loadedContentTypeDraft, + $contentTypeUpdateStruct, + $this->permissionResolver->getCurrentUserReference() + ) + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Delete a content type object. + * + * Deletes a content type if it has no instances. If content type in state STATUS_DRAFT is + * given, only the draft content type will be deleted. Otherwise, if content type in state + * STATUS_DEFINED is given, all content type data will be deleted. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If there exist content objects of this type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete a content type + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + */ + public function deleteContentType(APIContentType $contentType): void + { + if (!$this->permissionResolver->canUser('class', 'delete', $contentType)) { + throw new UnauthorizedException('ContentType', 'delete'); + } + + $this->repository->beginTransaction(); + try { + if (!$contentType instanceof APIContentTypeDraft) { + $this->contentTypeHandler->delete( + $contentType->id, + APIContentTypeDraft::STATUS_DEFINED + ); + } + + $this->contentTypeHandler->delete( + $contentType->id, + APIContentTypeDraft::STATUS_DRAFT + ); + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Copy Type incl fields and groupIds to a new Type object. + * + * New Type will have $creator as creator / modifier, created / modified should be updated with current time, + * updated remoteId and identifier should be appended with '_' + unique string. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the current-user is not allowed to copy a content type + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param \Ibexa\Contracts\Core\Repository\Values\User\User|null $creator if null the current-user is used + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + */ + public function copyContentType(APIContentType $contentType, User $creator = null): ContentType + { + if (!$this->permissionResolver->canUser('class', 'create', $contentType)) { + throw new UnauthorizedException('ContentType', 'create'); + } + + if (empty($creator)) { + $creator = $this->permissionResolver->getCurrentUserReference(); + } + + $this->repository->beginTransaction(); + try { + $spiContentType = $this->contentTypeHandler->copy( + $creator->getUserId(), + $contentType->id, + SPIContentType::STATUS_DEFINED + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadContentType($spiContentType->id); + } + + /** + * Assigns a content type to a content type group. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to unlink a content type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the content type is already assigned the given group + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup $contentTypeGroup + */ + public function assignContentTypeGroup(APIContentType $contentType, APIContentTypeGroup $contentTypeGroup): void + { + if (!$this->permissionResolver->canUser('class', 'update', $contentType)) { + throw new UnauthorizedException('ContentType', 'update'); + } + + $spiContentType = $this->contentTypeHandler->load( + $contentType->id, + $contentType->status + ); + + if (in_array($contentTypeGroup->id, $spiContentType->groupIds)) { + throw new InvalidArgumentException( + '$contentTypeGroup', + 'The provided content type is already assigned to the content type group' + ); + } + + $this->repository->beginTransaction(); + try { + $this->contentTypeHandler->link( + $contentTypeGroup->id, + $contentType->id, + $contentType->status + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Unassign a content type from a group. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to link a content type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the content type is not assigned this the given group. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If $contentTypeGroup is the last group assigned to the content type + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup $contentTypeGroup + */ + public function unassignContentTypeGroup(APIContentType $contentType, APIContentTypeGroup $contentTypeGroup): void + { + if (!$this->permissionResolver->canUser('class', 'update', $contentType, [$contentTypeGroup])) { + throw new UnauthorizedException('ContentType', 'update'); + } + + $spiContentType = $this->contentTypeHandler->load( + $contentType->id, + $contentType->status + ); + + if (!in_array($contentTypeGroup->id, $spiContentType->groupIds)) { + throw new InvalidArgumentException( + '$contentTypeGroup', + 'The provided content type is not assigned the content type group' + ); + } + + $this->repository->beginTransaction(); + try { + $this->contentTypeHandler->unlink( + $contentTypeGroup->id, + $contentType->id, + $contentType->status + ); + $this->repository->commit(); + } catch (APIBadStateException $e) { + $this->repository->rollback(); + throw new BadStateException( + '$contentType', + 'The provided content type group is the last group assigned to the content type', + $e + ); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Adds a new field definition to an existing content type. + * + * The content type must be in state DRAFT. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the identifier in already exists in the content type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit a content type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException + * if a field definition in the $contentTypeCreateStruct is not valid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If field definition of the same non-repeatable type is being + * added to the ContentType that already contains one + * or field definition that can't be added to a ContentType that + * has Content instances is being added to such ContentType + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct $fieldDefinitionCreateStruct + */ + public function addFieldDefinition(APIContentTypeDraft $contentTypeDraft, FieldDefinitionCreateStruct $fieldDefinitionCreateStruct): void + { + if (!$this->permissionResolver->canUser('class', 'update', $contentTypeDraft)) { + throw new UnauthorizedException('ContentType', 'update'); + } + + $this->validateInputFieldDefinitionCreateStruct($fieldDefinitionCreateStruct); + $loadedContentTypeDraft = $this->loadContentTypeDraft($contentTypeDraft->id); + + if ($loadedContentTypeDraft->hasFieldDefinition($fieldDefinitionCreateStruct->identifier)) { + throw new InvalidArgumentException( + '$fieldDefinitionCreateStruct', + "Another Field definition with identifier '{$fieldDefinitionCreateStruct->identifier}' exists in the content type" + ); + } + //Fill default translations with default value for mainLanguageCode with fallback if no exist + if (is_array($fieldDefinitionCreateStruct->names)) { + foreach ($contentTypeDraft->languageCodes as $languageCode) { + if (!array_key_exists($languageCode, $fieldDefinitionCreateStruct->names)) { + $fieldDefinitionCreateStruct->names[$languageCode] = $fieldDefinitionCreateStruct->names[$contentTypeDraft->mainLanguageCode] ?? reset($fieldDefinitionCreateStruct->names); + } + } + } + + /** @var $fieldType \Ibexa\Contracts\Core\FieldType\FieldType */ + $fieldType = $this->fieldTypeRegistry->getFieldType( + $fieldDefinitionCreateStruct->fieldTypeIdentifier + ); + + $fieldType->applyDefaultSettings($fieldDefinitionCreateStruct->fieldSettings); + $fieldType->applyDefaultValidatorConfiguration($fieldDefinitionCreateStruct->validatorConfiguration); + $validationErrors = $this->validateFieldDefinitionCreateStruct($fieldDefinitionCreateStruct, $fieldType); + if (!empty($validationErrors)) { + $validationErrors = [$fieldDefinitionCreateStruct->identifier => $validationErrors]; + throw new ContentTypeFieldDefinitionValidationException($validationErrors); + } + + if ($fieldType->isSingular()) { + if ($loadedContentTypeDraft->hasFieldDefinitionOfType($fieldDefinitionCreateStruct->fieldTypeIdentifier)) { + throw new BadStateException( + '$contentTypeDraft', + "The content type already contains a Field definition of the singular Field Type '{$fieldDefinitionCreateStruct->fieldTypeIdentifier}'" + ); + } + } + + if ($fieldType->onlyEmptyInstance() && $this->contentTypeHandler->getContentCount($loadedContentTypeDraft->id) + ) { + throw new BadStateException( + '$contentTypeDraft', + "A Field definition of the '{$fieldDefinitionCreateStruct->fieldTypeIdentifier}' Field Type cannot be added because the content type already has Content items" + ); + } + + $spiFieldDefinition = $this->contentTypeDomainMapper->buildSPIFieldDefinitionFromCreateStruct( + $fieldDefinitionCreateStruct, + $fieldType, + $contentTypeDraft->mainLanguageCode + ); + + $this->repository->beginTransaction(); + try { + $this->contentTypeHandler->addFieldDefinition( + $contentTypeDraft->id, + $contentTypeDraft->status, + $spiFieldDefinition + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Remove a field definition from an existing Type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the given field definition does not belong to the given type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit a content type + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition + */ + public function removeFieldDefinition(APIContentTypeDraft $contentTypeDraft, APIFieldDefinition $fieldDefinition): void + { + if (!$this->permissionResolver->canUser('class', 'update', $contentTypeDraft)) { + throw new UnauthorizedException('ContentType', 'update'); + } + + $loadedFieldDefinition = $this->loadContentTypeDraft( + $contentTypeDraft->id + )->getFieldDefinition( + $fieldDefinition->identifier + ); + + if (empty($loadedFieldDefinition) || $loadedFieldDefinition->id != $fieldDefinition->id) { + throw new InvalidArgumentException( + '$fieldDefinition', + 'The given Field definition does not belong to the content type' + ); + } + + $this->repository->beginTransaction(); + try { + $this->contentTypeHandler->removeFieldDefinition( + $contentTypeDraft->id, + SPIContentType::STATUS_DRAFT, + $this->contentTypeHandler->getFieldDefinition( + $fieldDefinition->id, + SPIContentType::STATUS_DRAFT + ), + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Update a field definition. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the field id in the update struct is not found or does not belong to the content type + * If the given identifier is used in an existing field of the given content type + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to edit a content type + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft the content type draft + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition the field definition which should be updated + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct + */ + public function updateFieldDefinition(APIContentTypeDraft $contentTypeDraft, APIFieldDefinition $fieldDefinition, FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct): void + { + if (!$this->permissionResolver->canUser('class', 'update', $contentTypeDraft)) { + throw new UnauthorizedException('ContentType', 'update'); + } + + $loadedContentTypeDraft = $this->loadContentTypeDraft($contentTypeDraft->id); + $foundFieldId = false; + foreach ($loadedContentTypeDraft->fieldDefinitions as $existingFieldDefinition) { + if ($existingFieldDefinition->id == $fieldDefinition->id) { + $foundFieldId = true; + } elseif ($existingFieldDefinition->identifier == $fieldDefinitionUpdateStruct->identifier) { + throw new InvalidArgumentException( + '$fieldDefinitionUpdateStruct', + "Another Field definition with identifier '{$fieldDefinitionUpdateStruct->identifier}' exists in the content type" + ); + } + } + if (!$foundFieldId) { + throw new InvalidArgumentException( + '$fieldDefinition', + 'The given Field definition does not belong to the content type' + ); + } + + $spiFieldDefinition = $this->contentTypeDomainMapper->buildSPIFieldDefinitionFromUpdateStruct( + $fieldDefinitionUpdateStruct, + $fieldDefinition, + $contentTypeDraft->mainLanguageCode + ); + + $this->repository->beginTransaction(); + try { + $this->contentTypeHandler->updateFieldDefinition( + $contentTypeDraft->id, + SPIContentType::STATUS_DRAFT, + $spiFieldDefinition + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Publish the content type and update content objects. + * + * This method updates content objects, depending on the changed field definitions. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If the content type has no draft + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the content type has no field definitions + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to publish a content type + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft + */ + public function publishContentTypeDraft(APIContentTypeDraft $contentTypeDraft): void + { + if (!$this->permissionResolver->canUser('class', 'update', $contentTypeDraft)) { + throw new UnauthorizedException('ContentType', 'update'); + } + + try { + $loadedContentTypeDraft = $this->loadContentTypeDraft($contentTypeDraft->id); + } catch (APINotFoundException $e) { + throw new BadStateException( + '$contentTypeDraft', + 'The content type does not have a draft.', + $e + ); + } + + if ($loadedContentTypeDraft->getFieldDefinitions()->isEmpty()) { + throw new InvalidArgumentException( + '$contentTypeDraft', + 'The content type draft should have at least one Field definition.' + ); + } + + $this->repository->beginTransaction(); + try { + if (empty($loadedContentTypeDraft->nameSchema)) { + $fieldDefinitions = $loadedContentTypeDraft->getFieldDefinitions(); + $this->contentTypeHandler->update( + $contentTypeDraft->id, + $contentTypeDraft->status, + $this->contentTypeDomainMapper->buildSPIContentTypeUpdateStruct( + $loadedContentTypeDraft, + new ContentTypeUpdateStruct( + [ + 'nameSchema' => '<' . $fieldDefinitions[0]->identifier . '>', + ] + ), + $this->permissionResolver->getCurrentUserReference() + ) + ); + } + + $this->contentTypeHandler->publish( + $loadedContentTypeDraft->id + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Instantiates a new content type group create class. + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentValue if given identifier is not a string + * + * @param string $identifier + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupCreateStruct + */ + public function newContentTypeGroupCreateStruct(string $identifier): ContentTypeGroupCreateStruct + { + return new ContentTypeGroupCreateStruct( + [ + 'identifier' => $identifier, + ] + ); + } + + /** + * Instantiates a new content type create class. + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentValue if given identifier is not a string + * + * @param string $identifier + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeCreateStruct + */ + public function newContentTypeCreateStruct(string $identifier): APIContentTypeCreateStruct + { + if (!is_string($identifier)) { + throw new InvalidArgumentValue('$identifier', $identifier); + } + + return new ContentTypeCreateStruct( + [ + 'identifier' => $identifier, + ] + ); + } + + /** + * Instantiates a new content type update struct. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeUpdateStruct + */ + public function newContentTypeUpdateStruct(): ContentTypeUpdateStruct + { + return new ContentTypeUpdateStruct(); + } + + /** + * Instantiates a new content type update struct. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupUpdateStruct + */ + public function newContentTypeGroupUpdateStruct(): ContentTypeGroupUpdateStruct + { + return new ContentTypeGroupUpdateStruct(); + } + + /** + * Instantiates a field definition create struct. + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentValue if given identifier is not a string + * or given fieldTypeIdentifier is not a string + * + * @param string $fieldTypeIdentifier the required field type identifier + * @param string $identifier the required identifier for the field definition + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct + */ + public function newFieldDefinitionCreateStruct(string $identifier, string $fieldTypeIdentifier): FieldDefinitionCreateStruct + { + return new FieldDefinitionCreateStruct( + [ + 'identifier' => $identifier, + 'fieldTypeIdentifier' => $fieldTypeIdentifier, + ] + ); + } + + /** + * Instantiates a field definition update class. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionUpdateStruct + */ + public function newFieldDefinitionUpdateStruct(): FieldDefinitionUpdateStruct + { + return new FieldDefinitionUpdateStruct(); + } + + /** + * Returns true if the given content type $contentType has content instances. + * + * @since 6.0.1 + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * + * @return bool + */ + public function isContentTypeUsed(APIContentType $contentType): bool + { + return $this->contentTypeHandler->getContentCount($contentType->id) > 0; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft + * @param string $languageCode + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function removeContentTypeTranslation(APIContentTypeDraft $contentTypeDraft, string $languageCode): APIContentTypeDraft + { + if (!$this->permissionResolver->canUser('class', 'update', $contentTypeDraft)) { + throw new UnauthorizedException('ContentType', 'update'); + } + + $this->repository->beginTransaction(); + try { + $contentType = $this->contentTypeHandler->removeContentTypeTranslation( + $contentTypeDraft->id, + $languageCode + ); + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->contentTypeDomainMapper->buildContentTypeDraftDomainObject($contentType); + } + + public function deleteUserDrafts(int $userId): void + { + try { + $this->userHandler->load($userId); + } catch (APINotFoundException $e) { + $this->contentTypeHandler->deleteByUserAndStatus($userId, ContentType::STATUS_DRAFT); + + return; + } + + if ($this->repository->getPermissionResolver()->hasAccess('class', 'delete') !== true) { + throw new UnauthorizedException('ContentType', 'update'); + } + + $this->contentTypeHandler->deleteByUserAndStatus($userId, ContentType::STATUS_DRAFT); + } +} + +class_alias(ContentTypeService::class, 'eZ\Publish\Core\Repository\ContentTypeService'); diff --git a/src/lib/Repository/EventSubscriber/DeleteUserSubscriber.php b/src/lib/Repository/EventSubscriber/DeleteUserSubscriber.php new file mode 100644 index 0000000000..c92643c9b3 --- /dev/null +++ b/src/lib/Repository/EventSubscriber/DeleteUserSubscriber.php @@ -0,0 +1,38 @@ +contentTypeService = $contentTypeService; + } + + public static function getSubscribedEvents(): array + { + return [ + DeleteUserEvent::class => 'onDeleteUser', + ]; + } + + public function onDeleteUser(DeleteUserEvent $event): void + { + $this->contentTypeService->deleteUserDrafts($event->getUser()->id); + } +} + +class_alias(DeleteUserSubscriber::class, 'eZ\Publish\Core\Repository\EventSubscriber\DeleteUserSubscriber'); diff --git a/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php new file mode 100644 index 0000000000..63adea9b31 --- /dev/null +++ b/src/lib/Repository/EventSubscriber/NameSchemaSubscriber.php @@ -0,0 +1,182 @@ +fieldTypeRegistry = $fieldTypeRegistry; + } + + public static function getSubscribedEvents(): array + { + return [ + ResolveNameSchemaEvent::class => [ + ['onResolveNameSchema', -100], + ], + ResolveContentNameSchemaEvent::class => [ + ['onResolveContentNameSchema', -100], + ], + ResolveUrlAliasSchemaEvent::class => [ + ['onResolveUrlAliasSchema', -100], + ], + ]; + } + + public function onResolveNameSchema(ResolveNameSchemaEvent $event): void + { + if (!$this->isValid($event)) { + return; + } + + $tokenValues = $this->processEvent( + $event->getLanguageCodes(), + $event->getSchemaIdentifiers()['field'], + $event->getContentType(), + null, + $event->getTokenValues(), + $event->getFieldMap() + ); + + $event->setTokenValues($tokenValues); + } + + public function onResolveContentNameSchema(ResolveContentNameSchemaEvent $event): void + { + if (!$this->isValid($event)) { + return; + } + + $tokenValues = $this->processEvent( + $event->getLanguageCodes(), + $event->getSchemaIdentifiers()['field'], + $event->getContentType(), + $event->getContent(), + $event->getTokenValues(), + $event->getFieldMap() + ); + + $event->setTokenValues($tokenValues); + } + + public function onResolveUrlAliasSchema(ResolveUrlAliasSchemaEvent $event): void + { + if (!$this->isValid($event)) { + return; + } + + $languageList = array_map( + static fn (Language $language): string => $language->getLanguageCode(), + (array)$event->getContent()->getVersionInfo()->getLanguages() + ); + + $content = $event->getContent(); + $contentType = $content->getContentType(); + $tokenValues = $this->processEvent( + $languageList, + $event->getSchemaIdentifiers()['field'], + $contentType, + $content, + $event->getTokenValues() + ); + + $event->setTokenValues($tokenValues); + } + + public function isValid(AbstractSchemaEvent $event): bool + { + return array_key_exists('field', $event->getSchemaIdentifiers()); + } + + /** + * @param array $languages + * @param array $identifiers + * @param array> $tokenValues + * @param array> $fieldMap + * + * @return array> + */ + private function processEvent( + array $languages, + array $identifiers, + ContentType $contentType, + ?Content $content, + array $tokenValues, + array $fieldMap = [] + ): array { + foreach ($languages as $languageCode) { + $values = $content !== null || !empty($fieldMap) + ? $this->getValues( + $identifiers, + $contentType, + $content, + $fieldMap, + $languageCode + ) + : []; + $tokenValues[$languageCode] = array_merge($tokenValues[$languageCode] ?? [], $values); + } + + return $tokenValues; + } + + /** + * @param array $identifiers + * @param array> $fieldMap + * + * @return array + */ + private function getValues( + array $identifiers, + ContentType $contentType, + ?Content $content, + array $fieldMap, + string $languageCode + ): array { + $tokenValues = []; + foreach ($identifiers as $identifier) { + $fieldDefinition = $contentType->getFieldDefinition($identifier); + if (null === $fieldDefinition) { + continue; + } + + $persistenceFieldType = $this->fieldTypeRegistry->getFieldType($fieldDefinition->fieldTypeIdentifier); + + if (!empty($fieldMap)) { + $fieldValue = $fieldMap[$identifier][$languageCode] ?? null; + } else { + $fieldValue = $content !== null ? $content->getFieldValue($identifier, $languageCode) : null; + } + + $tokenValues[$identifier] = $fieldValue !== null ? $persistenceFieldType->getName( + $fieldValue, + $fieldDefinition, + $languageCode + ) : ''; + } + + return $tokenValues; + } +} diff --git a/src/lib/Repository/FieldTypeService.php b/src/lib/Repository/FieldTypeService.php new file mode 100644 index 0000000000..68e83470fa --- /dev/null +++ b/src/lib/Repository/FieldTypeService.php @@ -0,0 +1,92 @@ +fieldTypeRegistry = $fieldTypeRegistry; + } + + /** + * Returns a list of all field types. + * + * @return \Ibexa\Contracts\Core\Repository\FieldType[] + */ + public function getFieldTypes(): iterable + { + foreach ($this->fieldTypeRegistry->getFieldTypes() as $identifier => $spiFieldType) { + if (isset($this->fieldTypes[$identifier])) { + continue; + } + + $this->fieldTypes[$identifier] = new FieldType($spiFieldType); + } + + return $this->fieldTypes; + } + + /** + * Returns the FieldType registered with the given identifier. + * + * @param string $identifier + * + * @return \Ibexa\Contracts\Core\Repository\FieldType + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if there is no FieldType registered with $identifier + */ + public function getFieldType(string $identifier): APIFieldType + { + if (isset($this->fieldTypes[$identifier])) { + return $this->fieldTypes[$identifier]; + } + + return $this->fieldTypes[$identifier] = new FieldType($this->fieldTypeRegistry->getFieldType($identifier)); + } + + /** + * Returns if there is a FieldType registered under $identifier. + * + * @param string $identifier + * + * @return bool + */ + public function hasFieldType(string $identifier): bool + { + return $this->fieldTypeRegistry->hasFieldType($identifier); + } +} + +class_alias(FieldTypeService::class, 'eZ\Publish\Core\Repository\FieldTypeService'); diff --git a/src/lib/Repository/Helper/NameSchemaService.php b/src/lib/Repository/Helper/NameSchemaService.php new file mode 100644 index 0000000000..111edb167c --- /dev/null +++ b/src/lib/Repository/Helper/NameSchemaService.php @@ -0,0 +1,192 @@ +settings = $settings + [ + 'limit' => 150, + 'sequence' => '...', + ]; + + parent::__construct( + $fieldTypeRegistry, + new SchemaIdentifierExtractor(), + $eventDispatcher, + $settings + ); + $this->contentTypeHandler = $contentTypeHandler; + $this->contentTypeDomainMapper = $contentTypeDomainMapper; + } + + public function resolveUrlAliasSchema(Content $content, ContentType $contentType = null): array + { + $contentType = $contentType ?? $content->getContentType(); + + return $this->resolveUrlAliasSchema( + $content, + $contentType + ); + } + + public function resolveContentNameSchema( + Content $content, + array $fieldMap = [], + array $languageCodes = [], + ContentType $contentType = null + ): array { + $contentType ??= $content->getContentType(); + + $languageCodes = $languageCodes ?: $content->versionInfo->languageCodes; + + return $this->resolveNameSchema( + $contentType->nameSchema, + $contentType, + $this->mergeFieldMap( + $content, + $fieldMap, + $languageCodes + ), + $languageCodes + ); + } + + public function resolveNameSchema( + string $nameSchema, + ContentType $contentType, + array $fieldMap, + array $languageCodes + ): array { + [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); + $tokens = $this->extractTokens($filteredNameSchema); + $schemaIdentifiers = $this->getIdentifiers($nameSchema); + + $names = []; + + foreach ($languageCodes as $languageCode) { + // Fetch titles for language code + $titles = $this->getFieldTitles($schemaIdentifiers, $contentType, $fieldMap, $languageCode); + $name = $filteredNameSchema; + + // Replace tokens with real values + foreach ($tokens as $token) { + $string = $this->resolveToken($token, $titles, $groupLookupTable); + $name = str_replace($token, $string, $name); + } + $name = $this->validateNameLength($name); + + $names[$languageCode] = $name; + } + + return $names; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param array> $fieldMap + * @param array $languageCodes + * + * @return array> + */ + protected function mergeFieldMap(Content $content, array $fieldMap, array $languageCodes): array + { + $mergedFieldMap = []; + + foreach ($content->fields as $fieldIdentifier => $fieldLanguageMap) { + foreach ($languageCodes as $languageCode) { + $mergedFieldMap[$fieldIdentifier][$languageCode] = $fieldMap[$fieldIdentifier][$languageCode]; + } + } + + return $mergedFieldMap; + } + + /** + * Fetches the list of available Field identifiers in the token and returns + * an array of their current title value. + * + * @param array $schemaIdentifiers + * @param \Ibexa\Contracts\Core\Persistence\Content\Type|\Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param array> $fieldMap + * @param string $languageCode + * + * @return array Key is the field identifier, value is the title value + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType + * + * @see \Ibexa\Core\Repository\Values\ContentType\FieldType::getName() + */ + protected function getFieldTitles(array $schemaIdentifiers, $contentType, array $fieldMap, $languageCode): array + { + $fieldTitles = []; + + foreach ($schemaIdentifiers as $fieldDefinitionIdentifier) { + if (!isset($fieldMap[$fieldDefinitionIdentifier][$languageCode])) { + continue; + } + if ($contentType instanceof SPIContentType) { + $fieldDefinition = null; + foreach ($contentType->fieldDefinitions as $spiFieldDefinition) { + if ($spiFieldDefinition->identifier === $fieldDefinitionIdentifier) { + $fieldDefinition = $this->contentTypeDomainMapper->buildFieldDefinitionDomainObject( + $spiFieldDefinition, + // This is probably not main language, but as we don't expose it, it's ok for now. + $languageCode + ); + break; + } + } + if ($fieldDefinition === null) { + $fieldTitles[$fieldDefinitionIdentifier] = ''; + continue; + } + } elseif ($contentType instanceof ContentType) { + $fieldDefinition = $contentType->getFieldDefinition($fieldDefinitionIdentifier); + } else { + throw new InvalidArgumentType('$contentType', 'API or SPI variant of a content type'); + } + $fieldTypeService = $this->fieldTypeRegistry->getFieldType( + $fieldDefinition->fieldTypeIdentifier + ); + $fieldTitles[$fieldDefinitionIdentifier] = $fieldTypeService->getName( + $fieldMap[$fieldDefinitionIdentifier][$languageCode], + $fieldDefinition, + $languageCode + ); + } + + return $fieldTitles; + } +} + +class_alias(NameSchemaService::class, 'eZ\Publish\Core\Repository\Helper\NameSchemaService'); diff --git a/eZ/Publish/Core/Repository/Helper/Readme.md b/src/lib/Repository/Helper/Readme.md similarity index 100% rename from eZ/Publish/Core/Repository/Helper/Readme.md rename to src/lib/Repository/Helper/Readme.md diff --git a/eZ/Publish/Core/Repository/Helper/RelationProcessor.php b/src/lib/Repository/Helper/RelationProcessor.php similarity index 86% rename from eZ/Publish/Core/Repository/Helper/RelationProcessor.php rename to src/lib/Repository/Helper/RelationProcessor.php index 34a657d792..bcb4914ec7 100644 --- a/eZ/Publish/Core/Repository/Helper/RelationProcessor.php +++ b/src/lib/Repository/Helper/RelationProcessor.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Helper; +namespace Ibexa\Core\Repository\Helper; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Values\Content\Relation; -use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType; -use eZ\Publish\SPI\FieldType\Value as BaseValue; -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct as SPIRelationCreateStruct; -use eZ\Publish\SPI\Persistence\Handler; +use Ibexa\Contracts\Core\FieldType\FieldType as SPIFieldType; +use Ibexa\Contracts\Core\FieldType\Value as BaseValue; +use Ibexa\Contracts\Core\Persistence\Content\Relation\CreateStruct as SPIRelationCreateStruct; +use Ibexa\Contracts\Core\Persistence\Handler; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Values\Content\Relation; use Psr\Log\LoggerAwareTrait; use Psr\Log\NullLogger; @@ -25,13 +25,13 @@ class RelationProcessor { use LoggerAwareTrait; - /** @var \eZ\Publish\SPI\Persistence\Handler */ + /** @var \Ibexa\Contracts\Core\Persistence\Handler */ protected $persistenceHandler; /** * Setups service with reference to repository object that created it & corresponding handler. * - * @param \eZ\Publish\SPI\Persistence\Handler $handler + * @param \Ibexa\Contracts\Core\Persistence\Handler $handler */ public function __construct(Handler $handler) { @@ -46,8 +46,8 @@ public function __construct(Handler $handler) * * @param array $relations * @param array $locationIdToContentIdMapping An array with Location Ids as keys and corresponding Content Id as values - * @param \eZ\Publish\SPI\FieldType\FieldType $fieldType - * @param \eZ\Publish\SPI\FieldType\Value $fieldValue Accepted field value. + * @param \Ibexa\Contracts\Core\FieldType\FieldType $fieldType + * @param \Ibexa\Contracts\Core\FieldType\Value $fieldValue Accepted field value. * @param string $fieldDefinitionId */ public function appendFieldRelations( @@ -103,8 +103,8 @@ public function appendFieldRelations( * @param array $inputRelations * @param mixed $sourceContentId * @param mixed $sourceContentVersionNo - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType - * @param \eZ\Publish\API\Repository\Values\Content\Relation[] $existingRelations An array of existing relations for Content version (empty when creating new content) + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] $existingRelations An array of existing relations for Content version (empty when creating new content) */ public function processFieldRelations( array $inputRelations, @@ -192,7 +192,8 @@ public function processFieldRelations( foreach ($relationEntry as $relation) { $this->persistenceHandler->contentHandler()->removeRelation( $relation->id, - $relationType + $relationType, + $relation->destinationContentInfo->id ); } break; @@ -200,10 +201,13 @@ public function processFieldRelations( case Relation::EMBED: $this->persistenceHandler->contentHandler()->removeRelation( $relationEntry->id, - $relationType + $relationType, + $relationEntry->destinationContentInfo->id ); } } } } } + +class_alias(RelationProcessor::class, 'eZ\Publish\Core\Repository\Helper\RelationProcessor'); diff --git a/src/lib/Repository/Helper/RoleDomainMapper.php b/src/lib/Repository/Helper/RoleDomainMapper.php new file mode 100644 index 0000000000..abec45016a --- /dev/null +++ b/src/lib/Repository/Helper/RoleDomainMapper.php @@ -0,0 +1,22 @@ +repository = $repository; + $this->languageHandler = $languageHandler; + $this->permissionResolver = $permissionResolver; + // Union makes sure default settings are ignored if provided in argument + $this->settings = $settings + [ + 'languages' => ['eng-GB'], + ]; + } + + /** + * Creates the a new Language in the content repository. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LanguageCreateStruct $languageCreateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Language + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If user does not have access to content translations + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the languageCode already exists + */ + public function createLanguage(LanguageCreateStruct $languageCreateStruct): Language + { + if (!is_string($languageCreateStruct->name) || empty($languageCreateStruct->name)) { + throw new InvalidArgumentValue('name', $languageCreateStruct->name, 'LanguageCreateStruct'); + } + + if (!is_string($languageCreateStruct->languageCode) || empty($languageCreateStruct->languageCode)) { + throw new InvalidArgumentValue('languageCode', $languageCreateStruct->languageCode, 'LanguageCreateStruct'); + } + + if (!is_bool($languageCreateStruct->enabled)) { + throw new InvalidArgumentValue('enabled', $languageCreateStruct->enabled, 'LanguageCreateStruct'); + } + + if (!$this->permissionResolver->canUser('content', 'translations', $languageCreateStruct)) { + throw new UnauthorizedException('content', 'translations'); + } + + try { + if ($this->loadLanguage($languageCreateStruct->languageCode) !== null) { + throw new InvalidArgumentException( + 'languageCreateStruct', + sprintf('language with the "%s" language code already exists', $languageCreateStruct->languageCode) + ); + } + } catch (APINotFoundException $e) { + // Do nothing + } + + $createStruct = new CreateStruct( + [ + 'languageCode' => $languageCreateStruct->languageCode, + 'name' => $languageCreateStruct->name, + 'isEnabled' => $languageCreateStruct->enabled, + ] + ); + + $this->repository->beginTransaction(); + try { + $createdLanguage = $this->languageHandler->create($createStruct); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->buildDomainObject($createdLanguage); + } + + /** + * Changes the name of the language in the content repository. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Language $language + * @param string $newName + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Language + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if languageCode argument + * is not string + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If user does not have access to content translations + */ + public function updateLanguageName(Language $language, string $newName): Language + { + if (empty($newName)) { + throw new InvalidArgumentValue('newName', $newName); + } + + if (!$this->permissionResolver->canUser('content', 'translations', $language)) { + throw new UnauthorizedException('content', 'translations'); + } + + $loadedLanguage = $this->loadLanguageById($language->id); + + $updateLanguageStruct = new SPILanguage( + [ + 'id' => $loadedLanguage->id, + 'languageCode' => $loadedLanguage->languageCode, + 'name' => $newName, + 'isEnabled' => $loadedLanguage->enabled, + ] + ); + + $this->repository->beginTransaction(); + try { + $this->languageHandler->update($updateLanguageStruct); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadLanguageById($loadedLanguage->id); + } + + /** + * Enables a language. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Language $language + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Language + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If user does not have access to content translations + */ + public function enableLanguage(Language $language): Language + { + if (!$this->permissionResolver->canUser('content', 'translations', $language)) { + throw new UnauthorizedException('content', 'translations'); + } + + $loadedLanguage = $this->loadLanguageById($language->id); + + $updateLanguageStruct = new SPILanguage( + [ + 'id' => $loadedLanguage->id, + 'languageCode' => $loadedLanguage->languageCode, + 'name' => $loadedLanguage->name, + 'isEnabled' => true, + ] + ); + + $this->repository->beginTransaction(); + try { + $this->languageHandler->update($updateLanguageStruct); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadLanguageById($loadedLanguage->id); + } + + /** + * Disables a language. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Language $language + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Language + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If user does not have access to content translations + */ + public function disableLanguage(Language $language): Language + { + if (!$this->permissionResolver->canUser('content', 'translations', $language)) { + throw new UnauthorizedException('content', 'translations'); + } + + $loadedLanguage = $this->loadLanguageById($language->id); + + $updateLanguageStruct = new SPILanguage( + [ + 'id' => $loadedLanguage->id, + 'languageCode' => $loadedLanguage->languageCode, + 'name' => $loadedLanguage->name, + 'isEnabled' => false, + ] + ); + + $this->repository->beginTransaction(); + try { + $this->languageHandler->update($updateLanguageStruct); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadLanguageById($loadedLanguage->id); + } + + /** + * Loads a Language from its language code ($languageCode). + * + * @param string $languageCode + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Language + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if languageCode argument + * is not string + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if language could not be found + */ + public function loadLanguage(string $languageCode): Language + { + if (empty($languageCode)) { + throw new InvalidArgumentException('languageCode', 'language code has an invalid value'); + } + + $language = $this->languageHandler->loadByLanguageCode($languageCode); + + return $this->buildDomainObject($language); + } + + /** + * Loads all Languages. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Language[] + */ + public function loadLanguages(): iterable + { + $languages = $this->languageHandler->loadAll(); + + $returnArray = []; + foreach ($languages as $language) { + $returnArray[] = $this->buildDomainObject($language); + } + + return $returnArray; + } + + /** + * Loads a Language by its id ($languageId). + * + * @param mixed $languageId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Language + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if language could not be found + */ + public function loadLanguageById(int $languageId): Language + { + $language = $this->languageHandler->load($languageId); + + return $this->buildDomainObject($language); + } + + /** + * {@inheritdoc} + */ + public function loadLanguageListByCode(array $languageCodes): iterable + { + $languages = $this->languageHandler->loadListByLanguageCodes($languageCodes); + + $returnArray = []; + foreach ($languages as $language) { + $returnArray[$language->languageCode] = $this->buildDomainObject($language); + } + + return $returnArray; + } + + /** + * {@inheritdoc} + */ + public function loadLanguageListById(array $languageIds): iterable + { + $languages = $this->languageHandler->loadList($languageIds); + + $returnArray = []; + foreach ($languages as $language) { + $returnArray[$language->id] = $this->buildDomainObject($language); + } + + return $returnArray; + } + + /** + * Deletes a language from content repository. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Language $language + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if language can not be deleted + * because it is still assigned to some content / type / (...). + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If user does not have access to content translations + */ + public function deleteLanguage(Language $language): void + { + if (!$this->permissionResolver->canUser('content', 'translations', $language)) { + throw new UnauthorizedException('content', 'translations'); + } + + $loadedLanguage = $this->loadLanguageById($language->id); + + $this->repository->beginTransaction(); + try { + $this->languageHandler->delete($loadedLanguage->id); + $this->repository->commit(); + } catch (LogicException $e) { + $this->repository->rollback(); + throw new InvalidArgumentException('language', $e->getMessage(), $e); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Returns a configured default language code. + * + * @return string + */ + public function getDefaultLanguageCode(): string + { + return $this->settings['languages'][0]; + } + + /** + * Returns a configured list of prioritized languageCodes. + * + * + * @return string[] + */ + public function getPrioritizedLanguageCodeList() + { + return $this->settings['languages']; + } + + /** + * Instantiates an object to be used for creating languages. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\LanguageCreateStruct + */ + public function newLanguageCreateStruct(): LanguageCreateStruct + { + return new LanguageCreateStruct(); + } + + /** + * Builds Language domain object from ValueObject returned by Persistence API. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Language $spiLanguage + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Language + */ + protected function buildDomainObject(SPILanguage $spiLanguage) + { + return new Language( + [ + 'id' => $spiLanguage->id, + 'languageCode' => $spiLanguage->languageCode, + 'name' => $spiLanguage->name, + 'enabled' => $spiLanguage->isEnabled, + ] + ); + } +} + +class_alias(LanguageService::class, 'eZ\Publish\Core\Repository\LanguageService'); diff --git a/src/lib/Repository/LocationResolver/LocationResolver.php b/src/lib/Repository/LocationResolver/LocationResolver.php new file mode 100644 index 0000000000..2f3da0a99c --- /dev/null +++ b/src/lib/Repository/LocationResolver/LocationResolver.php @@ -0,0 +1,27 @@ +locationService = $locationService; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + public function resolveLocation(ContentInfo $contentInfo): Location + { + try { + if (null === $contentInfo->mainLocationId) { + throw new CoreNotFoundException('location', $contentInfo->mainLocationId); + } + + $location = $this->locationService->loadLocation($contentInfo->mainLocationId); + } catch (NotFoundException | UnauthorizedException $e) { + // try different locations if main location is not accessible for the user + $locations = $this->locationService->loadLocations($contentInfo); + if (empty($locations) || null === $contentInfo->mainLocationId) { + throw $e; + } + + // foreach to keep forward compatibility with a type of returned loadLocations() result + foreach ($locations as $location) { + return $location; + } + } + + return $location; + } +} + +class_alias(PermissionAwareLocationResolver::class, 'eZ\Publish\Core\Repository\LocationResolver\PermissionAwareLocationResolver'); diff --git a/src/lib/Repository/LocationService.php b/src/lib/Repository/LocationService.php new file mode 100644 index 0000000000..6c71114051 --- /dev/null +++ b/src/lib/Repository/LocationService.php @@ -0,0 +1,1025 @@ +repository = $repository; + $this->persistenceHandler = $handler; + $this->contentDomainMapper = $contentDomainMapper; + $this->nameSchemaService = $nameSchemaService; + $this->permissionResolver = $permissionResolver; + $this->locationFilteringHandler = $locationFilteringHandler; + // Union makes sure default settings are ignored if provided in argument + $this->settings = $settings + [ + //'defaultSetting' => array(), + ]; + $this->permissionCriterionResolver = $permissionCriterionResolver; + $this->contentTypeService = $contentTypeService; + $this->logger = null !== $logger ? $logger : new NullLogger(); + } + + /** + * Copies the subtree starting from $subtree as a new subtree of $targetLocation. + * + * Only the items on which the user has read access are copied. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user user is not allowed copy the subtree to the given parent location + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user user does not have read access to the whole source subtree + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the target location is a sub location of the given location + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $subtree - the subtree denoted by the location to copy + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $targetParentLocation - the target parent location for the copy operation + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location The newly created location of the copied subtree + */ + public function copySubtree(APILocation $subtree, APILocation $targetParentLocation): APILocation + { + $loadedSubtree = $this->loadLocation($subtree->id); + $loadedTargetLocation = $this->loadLocation($targetParentLocation->id); + + if (stripos($loadedTargetLocation->pathString, $loadedSubtree->pathString) !== false) { + throw new InvalidArgumentException('targetParentLocation', 'Cannot copy subtree to its own descendant Location'); + } + + $this->checkCreatePermissionOnSubtreeTarget($targetParentLocation, $loadedSubtree, $loadedTargetLocation); + + $this->repository->beginTransaction(); + try { + $newLocation = $this->persistenceHandler->locationHandler()->copySubtree( + $loadedSubtree->id, + $loadedTargetLocation->id, + $this->repository->getPermissionResolver()->getCurrentUserReference()->getUserId() + ); + + $content = $this->repository->getContentService()->loadContent($newLocation->contentId); + $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content); + foreach ($urlAliasNames as $languageCode => $name) { + $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation( + $newLocation->id, + $loadedTargetLocation->id, + $name, + $languageCode, + $content->contentInfo->alwaysAvailable + ); + } + + $this->persistenceHandler->urlAliasHandler()->locationCopied( + $loadedSubtree->id, + $newLocation->id, + $loadedTargetLocation->id + ); + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->contentDomainMapper->buildLocationWithContent($newLocation, $content); + } + + /** + * {@inheritdoc} + */ + public function loadLocation(int $locationId, ?array $prioritizedLanguages = null, ?bool $useAlwaysAvailable = null): APILocation + { + $spiLocation = $this->persistenceHandler->locationHandler()->load($locationId, $prioritizedLanguages, $useAlwaysAvailable ?? true); + $location = $this->contentDomainMapper->buildLocation($spiLocation, $prioritizedLanguages ?: [], $useAlwaysAvailable ?? true); + if (!$this->permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) { + throw new UnauthorizedException('content', 'read', ['locationId' => $location->id]); + } + + return $location; + } + + /** + * {@inheritdoc} + */ + public function loadLocationList(array $locationIds, ?array $prioritizedLanguages = null, ?bool $useAlwaysAvailable = null): iterable + { + $spiLocations = $this->persistenceHandler->locationHandler()->loadList( + $locationIds, + $prioritizedLanguages, + $useAlwaysAvailable ?? true + ); + if (empty($spiLocations)) { + return []; + } + + // Get content id's + $contentIds = []; + foreach ($spiLocations as $spiLocation) { + $contentIds[] = $spiLocation->contentId; + } + + // Load content info and Get content proxy + $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList($contentIds); + $contentProxyList = $this->contentDomainMapper->buildContentProxyList( + $spiContentInfoList, + $prioritizedLanguages ?? [], + $useAlwaysAvailable ?? true + ); + + // Build locations using the bulk retrieved content info and bulk lazy loaded content proxies. + $locations = []; + $permissionResolver = $this->repository->getPermissionResolver(); + foreach ($spiLocations as $spiLocation) { + $location = $this->contentDomainMapper->buildLocationWithContent( + $spiLocation, + $contentProxyList[$spiLocation->contentId] ?? null, + $spiContentInfoList[$spiLocation->contentId] ?? null + ); + + if ($permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) { + $locations[$spiLocation->id] = $location; + } + } + + return $locations; + } + + /** + * {@inheritdoc} + */ + public function loadLocationByRemoteId(string $remoteId, ?array $prioritizedLanguages = null, ?bool $useAlwaysAvailable = null): APILocation + { + $spiLocation = $this->persistenceHandler->locationHandler()->loadByRemoteId($remoteId, $prioritizedLanguages, $useAlwaysAvailable ?? true); + $location = $this->contentDomainMapper->buildLocation($spiLocation, $prioritizedLanguages ?: [], $useAlwaysAvailable ?? true); + if (!$this->permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) { + throw new UnauthorizedException('content', 'read', ['locationId' => $location->id]); + } + + return $location; + } + + /** + * {@inheritdoc} + */ + public function loadLocations(ContentInfo $contentInfo, ?APILocation $rootLocation = null, ?array $prioritizedLanguages = null): iterable + { + if (!$contentInfo->published) { + throw new BadStateException('$contentInfo', 'The Content item has no published versions'); + } + + $spiLocations = $this->persistenceHandler->locationHandler()->loadLocationsByContent( + $contentInfo->id, + $rootLocation !== null ? $rootLocation->id : null + ); + + $locations = []; + $spiInfo = $this->persistenceHandler->contentHandler()->loadContentInfo($contentInfo->id); + $content = $this->contentDomainMapper->buildContentProxy($spiInfo, $prioritizedLanguages ?: []); + foreach ($spiLocations as $spiLocation) { + $location = $this->contentDomainMapper->buildLocationWithContent($spiLocation, $content, $spiInfo); + if ($this->permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) { + $locations[] = $location; + } + } + + return $locations; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function loadLocationChildren( + APILocation $location, + int $offset = 0, + int $limit = 25, + ?array $prioritizedLanguages = null + ): LocationList { + if (!$this->contentDomainMapper->isValidLocationSortField($location->sortField)) { + throw new InvalidArgumentValue('sortField', $location->sortField, 'Location'); + } + + if (!$this->contentDomainMapper->isValidLocationSortOrder($location->sortOrder)) { + throw new InvalidArgumentValue('sortOrder', $location->sortOrder, 'Location'); + } + + if (!is_int($offset)) { + throw new InvalidArgumentValue('offset', $offset); + } + + if (!is_int($limit)) { + throw new InvalidArgumentValue('limit', $limit); + } + + $filter = $this->buildLocationChildrenFilter($location); + $filter + ->withLimit($limit) + ->withOffset($offset); + + $sortClauses = $location->getSortClauses(); + foreach ($sortClauses as $sortClause) { + if ($sortClause instanceof FilteringSortClause) { + $filter->withSortClause($sortClause); + } + } + + return $this->find($filter, $prioritizedLanguages); + } + + /** + * {@inheritdoc} + */ + public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?array $prioritizedLanguages = null): iterable + { + if (!$versionInfo->isDraft()) { + throw new BadStateException( + '$contentInfo', + sprintf( + 'Content item [%d] %s is already published. Use LocationService::loadLocations instead.', + $versionInfo->contentInfo->id, + $versionInfo->contentInfo->name + ) + ); + } + + $spiLocations = $this->persistenceHandler + ->locationHandler() + ->loadParentLocationsForDraftContent($versionInfo->contentInfo->id); + + $contentIds = []; + foreach ($spiLocations as $spiLocation) { + $contentIds[] = $spiLocation->contentId; + } + + $locations = []; + $permissionResolver = $this->repository->getPermissionResolver(); + $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList($contentIds); + $contentList = $this->contentDomainMapper->buildContentProxyList($spiContentInfoList, $prioritizedLanguages ?: []); + foreach ($spiLocations as $spiLocation) { + $location = $this->contentDomainMapper->buildLocationWithContent( + $spiLocation, + $contentList[$spiLocation->contentId], + $spiContentInfoList[$spiLocation->contentId] + ); + + if ($permissionResolver->canUser('content', 'read', $location->getContentInfo(), [$location])) { + $locations[] = $location; + } + } + + return $locations; + } + + /** + * Returns the number of children which are readable by the current user of a Location object. + */ + public function getLocationChildCount(APILocation $location): int + { + $filter = $this->buildLocationChildrenFilter($location); + + return $this->count($filter); + } + + public function getSubtreeSize(APILocation $location): int + { + return $this->persistenceHandler->locationHandler()->getSubtreeSize( + $location->getPathString() + ); + } + + protected function buildLocationChildrenFilter(APILocation $location): Filter + { + $filter = new Filter(); + $filter + ->withCriterion(new Criterion\ParentLocationId($location->id)); + + return $filter; + } + + /** + * Creates the new $location in the content repository for the given content. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to create this location + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the content is already below the specified parent + * or the parent is a sub location of the location of the content + * or if set the remoteId exists already + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct $locationCreateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location the newly created Location + */ + public function createLocation(ContentInfo $contentInfo, LocationCreateStruct $locationCreateStruct): APILocation + { + $content = $this->contentDomainMapper->buildContentDomainObjectFromPersistence( + $this->persistenceHandler->contentHandler()->load($contentInfo->id), + $this->persistenceHandler->contentTypeHandler()->load($contentInfo->contentTypeId) + ); + + $parentLocation = $this->contentDomainMapper->buildLocation( + $this->persistenceHandler->locationHandler()->load($locationCreateStruct->parentLocationId) + ); + + $contentType = $content->getContentType(); + + $locationCreateStruct->sortField = $locationCreateStruct->sortField + ?? ($contentType->defaultSortField ?? Location::SORT_FIELD_NAME); + $locationCreateStruct->sortOrder = $locationCreateStruct->sortOrder + ?? ($contentType->defaultSortOrder ?? Location::SORT_ORDER_ASC); + + $contentInfo = $content->contentInfo; + + if (!$this->permissionResolver->canUser('content', 'manage_locations', $contentInfo, [$parentLocation])) { + throw new UnauthorizedException('content', 'manage_locations', ['contentId' => $contentInfo->id]); + } + + $locationTarget = (new DestinationLocationTarget($parentLocation->id, $contentInfo)); + if (!$this->permissionResolver->canUser('content', 'create', $contentInfo, [$parentLocation, $locationTarget])) { + throw new UnauthorizedException('content', 'create', ['locationId' => $parentLocation->id]); + } + + // Check if the parent is a sub location of one of the existing content locations (this also solves the + // situation where parent location actually one of the content locations), + // or if the content already has location below given location create struct parent + $existingContentLocations = $this->loadLocations($contentInfo); + if (!empty($existingContentLocations)) { + foreach ($existingContentLocations as $existingContentLocation) { + if (stripos($parentLocation->pathString, $existingContentLocation->pathString) !== false) { + throw new InvalidArgumentException( + '$locationCreateStruct', + 'Specified parent is a descendant of one of the existing Locations of this content.' + ); + } + if ($parentLocation->id == $existingContentLocation->parentLocationId) { + throw new InvalidArgumentException( + '$locationCreateStruct', + 'Content is already below the specified parent.' + ); + } + } + } + + $spiLocationCreateStruct = $this->contentDomainMapper->buildSPILocationCreateStruct( + $locationCreateStruct, + $parentLocation, + $contentInfo->mainLocationId ?? true, + $contentInfo->id, + $contentInfo->currentVersionNo, + $contentInfo->isHidden + ); + + $this->repository->beginTransaction(); + try { + $newLocation = $this->persistenceHandler->locationHandler()->create($spiLocationCreateStruct); + $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content); + foreach ($urlAliasNames as $languageCode => $name) { + $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation( + $newLocation->id, + $newLocation->parentId, + $name, + $languageCode, + $contentInfo->alwaysAvailable, + // @todo: this is legacy storage specific for updating ezcontentobject_tree.path_identification_string, to be removed + $languageCode === $contentInfo->mainLanguageCode + ); + } + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->contentDomainMapper->buildLocationWithContent($newLocation, $content); + } + + /** + * Updates $location in the content repository. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the remoteId exists already + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user is not allowed to update this location + */ + public function updateLocation(APILocation $location, LocationUpdateStruct $locationUpdateStruct): APILocation + { + if (!$this->contentDomainMapper->isValidLocationPriority($locationUpdateStruct->priority)) { + throw new InvalidArgumentValue('priority', $locationUpdateStruct->priority, 'LocationUpdateStruct'); + } + + if ($locationUpdateStruct->remoteId !== null && (!is_string($locationUpdateStruct->remoteId) || empty($locationUpdateStruct->remoteId))) { + throw new InvalidArgumentValue('remoteId', $locationUpdateStruct->remoteId, 'LocationUpdateStruct'); + } + + if ($locationUpdateStruct->sortField !== null && !$this->contentDomainMapper->isValidLocationSortField($locationUpdateStruct->sortField)) { + throw new InvalidArgumentValue('sortField', $locationUpdateStruct->sortField, 'LocationUpdateStruct'); + } + + if ($locationUpdateStruct->sortOrder !== null && !$this->contentDomainMapper->isValidLocationSortOrder($locationUpdateStruct->sortOrder)) { + throw new InvalidArgumentValue('sortOrder', $locationUpdateStruct->sortOrder, 'LocationUpdateStruct'); + } + + $loadedLocation = $this->loadLocation($location->id); + + if ($locationUpdateStruct->remoteId !== null) { + try { + $existingLocation = $this->loadLocationByRemoteId($locationUpdateStruct->remoteId); + if ($existingLocation !== null && $existingLocation->id !== $loadedLocation->id) { + throw new InvalidArgumentException('locationUpdateStruct', 'Location with the provided remote ID already exists'); + } + } catch (APINotFoundException $e) { + } + } + + $locationTarget = (new DestinationLocationTarget($loadedLocation->id, $loadedLocation->contentInfo)); + if (!$this->permissionResolver->canUser( + 'content', + 'edit', + $loadedLocation->getContentInfo(), + [$loadedLocation, $locationTarget] + )) { + throw new UnauthorizedException('content', 'edit', ['locationId' => $loadedLocation->id]); + } + + $updateStruct = new UpdateStruct(); + $updateStruct->priority = $locationUpdateStruct->priority !== null ? $locationUpdateStruct->priority : $loadedLocation->priority; + $updateStruct->remoteId = $locationUpdateStruct->remoteId !== null ? trim($locationUpdateStruct->remoteId) : $loadedLocation->remoteId; + $updateStruct->sortField = $locationUpdateStruct->sortField !== null ? $locationUpdateStruct->sortField : $loadedLocation->sortField; + $updateStruct->sortOrder = $locationUpdateStruct->sortOrder !== null ? $locationUpdateStruct->sortOrder : $loadedLocation->sortOrder; + + $this->repository->beginTransaction(); + try { + $this->persistenceHandler->locationHandler()->update($updateStruct, $loadedLocation->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadLocation($loadedLocation->id); + } + + /** + * Swaps the contents held by $location1 and $location2. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user is not allowed to swap content + */ + public function swapLocation(APILocation $location1, APILocation $location2): void + { + $loadedLocation1 = $this->loadLocation($location1->id); + $loadedLocation2 = $this->loadLocation($location2->id); + + $locationTarget1 = (new DestinationLocationTarget($loadedLocation1->id, $loadedLocation1->contentInfo)); + $locationTarget2 = (new DestinationLocationTarget($loadedLocation2->id, $loadedLocation2->contentInfo)); + + if (!$this->permissionResolver->canUser( + 'content', + 'edit', + $loadedLocation1->getContentInfo(), + [$loadedLocation1, $locationTarget1] + )) { + throw new UnauthorizedException('content', 'edit', ['locationId' => $loadedLocation1->id]); + } + + if (!$this->permissionResolver->canUser( + 'content', + 'edit', + $loadedLocation2->getContentInfo(), + [$loadedLocation2, $locationTarget2] + )) { + throw new UnauthorizedException('content', 'edit', ['locationId' => $loadedLocation2->id]); + } + + $this->repository->beginTransaction(); + try { + $this->persistenceHandler->locationHandler()->swap($loadedLocation1->id, $loadedLocation2->id); + $this->persistenceHandler->urlAliasHandler()->locationSwapped( + $location1->id, + $location1->parentLocationId, + $location2->id, + $location2->parentLocationId + ); + $this->persistenceHandler->bookmarkHandler()->locationSwapped($loadedLocation1->id, $loadedLocation2->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Hides the $location and marks invisible all descendants of $location. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user is not allowed to hide this location + */ + public function hideLocation(APILocation $location): APILocation + { + $locationTarget = (new DestinationLocationTarget($location->id, $location->contentInfo)); + if (!$this->permissionResolver->canUser( + 'content', + 'hide', + $location->getContentInfo(), + [$location, $locationTarget] + )) { + throw new UnauthorizedException('content', 'hide', ['locationId' => $location->id]); + } + + $this->repository->beginTransaction(); + try { + $this->persistenceHandler->locationHandler()->hide($location->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadLocation($location->id); + } + + /** + * Unhides the $location. + * + * This method and marks visible all descendants of $locations + * until a hidden location is found. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user is not allowed to unhide this location + */ + public function unhideLocation(APILocation $location): APILocation + { + $locationTarget = (new DestinationLocationTarget($location->id, $location->contentInfo)); + if (!$this->permissionResolver->canUser( + 'content', + 'hide', + $location->getContentInfo(), + [$location, $locationTarget] + )) { + throw new UnauthorizedException('content', 'hide', ['locationId' => $location->id]); + } + + $this->repository->beginTransaction(); + try { + $this->persistenceHandler->locationHandler()->unHide($location->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadLocation($location->id); + } + + /** + * {@inheritdoc} + */ + public function moveSubtree(APILocation $location, APILocation $newParentLocation): void + { + $location = $this->loadLocation($location->id); + $newParentLocation = $this->loadLocation($newParentLocation->id); + + if ($newParentLocation->id === $location->parentLocationId) { + throw new InvalidArgumentException( + '$newParentLocation', + 'new parent Location is the same as the current one' + ); + } + if (strpos($newParentLocation->pathString, $location->pathString) === 0) { + throw new InvalidArgumentException( + '$newParentLocation', + 'new parent Location is a descendant of the given $location' + ); + } + $contentTypeId = $newParentLocation->contentInfo->contentTypeId; + if (!$this->contentTypeService->loadContentType($contentTypeId)->isContainer) { + throw new InvalidArgumentException( + '$newParentLocation', + 'Cannot move Location to a parent that is not a container' + ); + } + + $this->checkCreatePermissionOnSubtreeTarget($newParentLocation, $location, $newParentLocation); + + $this->repository->beginTransaction(); + try { + $this->persistenceHandler->locationHandler()->move($location->id, $newParentLocation->id); + + $content = $this->repository->getContentService()->loadContent($location->contentId); + $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content); + foreach ($urlAliasNames as $languageCode => $name) { + $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation( + $location->id, + $newParentLocation->id, + $name, + $languageCode, + $content->contentInfo->alwaysAvailable + ); + } + + $this->persistenceHandler->urlAliasHandler()->locationMoved( + $location->id, + $location->parentLocationId, + $newParentLocation->id + ); + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Deletes $location and all its descendants. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user is not allowed to delete this location or a descendant + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + */ + public function deleteLocation(APILocation $location): void + { + $location = $this->loadLocation($location->id); + $contentInfo = $location->contentInfo; + $versionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo( + $contentInfo->id, + $contentInfo->currentVersionNo + ); + $translations = $versionInfo->languageCodes; + $target = (new Target\Version())->deleteTranslations($translations); + + if (!$this->permissionResolver->canUser('content', 'manage_locations', $location->getContentInfo())) { + throw new UnauthorizedException('content', 'manage_locations', ['locationId' => $location->id]); + } + if (!$this->permissionResolver->canUser('content', 'remove', $location->getContentInfo(), [$location, $target])) { + throw new UnauthorizedException('content', 'remove', ['locationId' => $location->id]); + } + + // Check remove access to descendants + $contentReadCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'remove'); + if ($contentReadCriterion === false) { + throw new UnauthorizedException('content', 'remove'); + } elseif ($contentReadCriterion !== true) { + // Query if there are any content in subtree current user don't have access to + $query = new Query( + [ + 'limit' => 0, + 'filter' => new CriterionLogicalAnd( + [ + new CriterionSubtree($location->pathString), + new CriterionLogicalNot($contentReadCriterion), + ] + ), + ] + ); + $result = $this->repository->getSearchService()->findContent($query, [], false); + if ($result->totalCount > 0) { + throw new UnauthorizedException('content', 'remove'); + } + } + + $this->repository->beginTransaction(); + try { + $this->persistenceHandler->locationHandler()->removeSubtree($location->id); + $this->persistenceHandler->urlAliasHandler()->locationDeleted($location->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Instantiates a new location create class. + * + * @param mixed $parentLocationId the parent under which the new location should be created + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType|null $contentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct + */ + public function newLocationCreateStruct($parentLocationId, ContentType $contentType = null): LocationCreateStruct + { + $properties = [ + 'parentLocationId' => $parentLocationId, + ]; + if ($contentType) { + $properties['sortField'] = $contentType->defaultSortField; + $properties['sortOrder'] = $contentType->defaultSortOrder; + } + + return new LocationCreateStruct($properties); + } + + /** + * Instantiates a new location update class. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\LocationUpdateStruct + */ + public function newLocationUpdateStruct(): LocationUpdateStruct + { + return new LocationUpdateStruct(); + } + + /** + * Get the total number of all existing Locations. Can be combined with loadAllLocations. + * + * @see loadAllLocations + * + * @return int Total number of Locations + */ + public function getAllLocationsCount(): int + { + return $this->persistenceHandler->locationHandler()->countAllLocations(); + } + + /** + * Bulk-load all existing Locations, constrained by $limit and $offset to paginate results. + * + * @param int $offset + * @param int $limit + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location[] + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function loadAllLocations(int $offset = 0, int $limit = 25): array + { + $spiLocations = $this->persistenceHandler->locationHandler()->loadAllLocations( + $offset, + $limit + ); + $contentIds = array_unique( + array_map( + static function (SPILocation $spiLocation) { + return $spiLocation->contentId; + }, + $spiLocations + ) + ); + + $permissionResolver = $this->repository->getPermissionResolver(); + $spiContentInfoList = $this->persistenceHandler->contentHandler()->loadContentInfoList( + $contentIds + ); + $contentList = $this->contentDomainMapper->buildContentProxyList( + $spiContentInfoList, + Language::ALL, + false + ); + $locations = []; + foreach ($spiLocations as $spiLocation) { + if (!isset($spiContentInfoList[$spiLocation->contentId], $contentList[$spiLocation->contentId])) { + $this->logger->warning( + sprintf( + 'Location %d has missing content %d', + $spiLocation->id, + $spiLocation->contentId + ) + ); + continue; + } + + $location = $this->contentDomainMapper->buildLocationWithContent( + $spiLocation, + $contentList[$spiLocation->contentId], + $spiContentInfoList[$spiLocation->contentId] + ); + + $contentInfo = $location->getContentInfo(); + if (!$permissionResolver->canUser('content', 'read', $contentInfo, [$location])) { + continue; + } + $locations[] = $location; + } + + return $locations; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function find(Filter $filter, ?array $languages = null): LocationList + { + $filter = clone $filter; + if (!empty($languages)) { + $filter->andWithCriterion(new LanguageCode($languages)); + } + + $permissionCriterion = $this->permissionCriterionResolver->getQueryPermissionsCriterion(); + if ($permissionCriterion instanceof Criterion\MatchNone) { + return new LocationList(); + } + + if (!$permissionCriterion instanceof Criterion\MatchAll) { + if (!$permissionCriterion instanceof FilteringCriterion) { + return new LocationList(); + } + $filter->andWithCriterion($permissionCriterion); + } + + $locations = []; + $locationListIterator = $this->locationFilteringHandler->find($filter); + foreach ($locationListIterator as $locationWithContentInfo) { + $spiContentInfo = $locationWithContentInfo->getContentInfo(); + $locations[] = $this->contentDomainMapper->buildLocationWithContent( + $locationWithContentInfo->getLocation(), + $this->contentDomainMapper->buildContentProxy($spiContentInfo), + $spiContentInfo, + ); + } + + return new LocationList( + [ + 'totalCount' => $locationListIterator->getTotalCount(), + 'locations' => $locations, + ] + ); + } + + public function count(Filter $filter, ?array $languages = null): int + { + $filter = clone $filter; + if (!empty($languages)) { + $filter->andWithCriterion(new LanguageCode($languages)); + } + + $permissionCriterion = $this->permissionCriterionResolver->getQueryPermissionsCriterion(); + if ($permissionCriterion instanceof Criterion\MatchNone) { + return 0; + } + + if (!$permissionCriterion instanceof Criterion\MatchAll) { + if (!$permissionCriterion instanceof FilteringCriterion) { + return 0; + } + + $filter->andWithCriterion($permissionCriterion); + } + + return $this->locationFilteringHandler->count($filter); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function checkCreatePermissionOnSubtreeTarget( + APILocation $targetParentLocation, + APILocation $loadedSubtree, + APILocation $loadedTargetLocation + ): void { + $locationTarget = (new DestinationLocationTarget($targetParentLocation->id, $loadedSubtree->contentInfo)); + if (!$this->permissionResolver->canUser( + 'content', + 'create', + $loadedSubtree->getContentInfo(), + [$loadedTargetLocation, $locationTarget] + )) { + throw new UnauthorizedException( + 'content', + 'create', + ['locationId' => $loadedTargetLocation->id] + ); + } + + // Check read access to whole source subtree + $contentReadCriterion = $this->permissionCriterionResolver->getPermissionsCriterion( + 'content', + 'read' + ); + if ($contentReadCriterion === false) { + throw new UnauthorizedException('content', 'read'); + } + + if ($contentReadCriterion !== true) { + // Query if there are any content in subtree current user don't have access to + $query = new Query( + [ + 'limit' => 0, + 'filter' => new CriterionLogicalAnd( + [ + new CriterionSubtree($loadedSubtree->getPathString()), + new CriterionLogicalNot($contentReadCriterion), + // Do not take the same content into consideration as it can have more than one location + new CriterionLogicalNot(new Criterion\ContentId($loadedSubtree->getContentInfo()->getId())), + ] + ), + ] + ); + $result = $this->repository->getSearchService()->findContent($query, [], false); + if ($result->totalCount > 0) { + throw new UnauthorizedException('content', 'read'); + } + } + } +} + +class_alias(LocationService::class, 'eZ\Publish\Core\Repository\LocationService'); diff --git a/eZ/Publish/Core/Repository/Mapper/ContentDomainMapper.php b/src/lib/Repository/Mapper/ContentDomainMapper.php similarity index 79% rename from eZ/Publish/Core/Repository/Mapper/ContentDomainMapper.php rename to src/lib/Repository/Mapper/ContentDomainMapper.php index 3dd8003598..d2c2024d15 100644 --- a/eZ/Publish/Core/Repository/Mapper/ContentDomainMapper.php +++ b/src/lib/Repository/Mapper/ContentDomainMapper.php @@ -4,38 +4,38 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Mapper; +namespace Ibexa\Core\Repository\Mapper; use DateTime; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperInterface; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\Relation; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\SPI\Persistence\Content as SPIContent; -use eZ\Publish\SPI\Persistence\Content\ContentInfo as SPIContentInfo; -use eZ\Publish\SPI\Persistence\Content\Handler as ContentHandler; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; -use eZ\Publish\SPI\Persistence\Content\Location\CreateStruct as SPILocationCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as LocationHandler; -use eZ\Publish\SPI\Persistence\Content\Relation as SPIRelation; -use eZ\Publish\SPI\Persistence\Content\Type as SPIContentType; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as TypeHandler; -use eZ\Publish\SPI\Persistence\Content\VersionInfo as SPIVersionInfo; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy; +use Ibexa\Contracts\Core\Persistence\Content as SPIContent; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo as SPIContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler; +use Ibexa\Contracts\Core\Persistence\Content\Location as SPILocation; +use Ibexa\Contracts\Core\Persistence\Content\Location\CreateStruct as SPILocationCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; +use Ibexa\Contracts\Core\Persistence\Content\Relation as SPIRelation; +use Ibexa\Contracts\Core\Persistence\Content\Type as SPIContentType; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as TypeHandler; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo as SPIVersionInfo; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Strategy\ContentThumbnail\ThumbnailStrategy; +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\Location as APILocation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\Base\Exceptions\InvalidArgumentValue; +use Ibexa\Core\FieldType\FieldTypeRegistry; +use Ibexa\Core\Repository\ProxyFactory\ProxyDomainMapperInterface; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Repository\Values\Content\Relation; +use Ibexa\Core\Repository\Values\Content\VersionInfo; use Psr\Log\LoggerAwareInterface; use Psr\Log\LoggerAwareTrait; use Psr\Log\LoggerInterface; @@ -53,25 +53,25 @@ class ContentDomainMapper extends ProxyAwareDomainMapper implements LoggerAwareI public const MAX_LOCATION_PRIORITY = 2147483647; public const MIN_LOCATION_PRIORITY = -2147483648; - /** @var \eZ\Publish\SPI\Persistence\Content\Handler */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Handler */ protected $contentHandler; - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Handler */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Location\Handler */ protected $locationHandler; - /** @var \eZ\Publish\SPI\Persistence\Content\Type\Handler */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Type\Handler */ protected $contentTypeHandler; - /** @var \eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper */ + /** @var \Ibexa\Core\Repository\Mapper\ContentTypeDomainMapper */ protected $contentTypeDomainMapper; - /** @var \eZ\Publish\SPI\Persistence\Content\Language\Handler */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Language\Handler */ protected $contentLanguageHandler; - /** @var \eZ\Publish\Core\Repository\Helper\FieldTypeRegistry */ + /** @var \Ibexa\Core\FieldType\FieldTypeRegistry */ protected $fieldTypeRegistry; - /** @var \eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy */ + /** @var \Ibexa\Contracts\Core\Repository\Strategy\ContentThumbnail\ThumbnailStrategy */ private $thumbnailStrategy; public function __construct( @@ -99,12 +99,12 @@ public function __construct( /** * Builds a Content domain object from value object. * - * @param \eZ\Publish\SPI\Persistence\Content $spiContent - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType + * @param \Ibexa\Contracts\Core\Persistence\Content $spiContent + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType * @param array $prioritizedLanguages Prioritized language codes to filter fields on * @param string|null $fieldAlwaysAvailableLanguage Language code fallback if a given field is not found in $prioritizedLanguages * - * @return \eZ\Publish\Core\Repository\Values\Content\Content + * @return \Ibexa\Core\Repository\Values\Content\Content */ public function buildContentDomainObject( SPIContent $spiContent, @@ -154,12 +154,12 @@ public function buildContentDomainObject( /** * Builds a Content domain object from value object returned from persistence. * - * @param \eZ\Publish\SPI\Persistence\Content $spiContent - * @param \eZ\Publish\SPI\Persistence\Content\Type $spiContentType + * @param \Ibexa\Contracts\Core\Persistence\Content $spiContent + * @param \Ibexa\Contracts\Core\Persistence\Content\Type $spiContentType * @param string[] $prioritizedLanguages Prioritized language codes to filter fields on * @param string|null $fieldAlwaysAvailableLanguage Language code fallback if a given field is not found in $prioritizedLanguages * - * @return \eZ\Publish\Core\Repository\Values\Content\Content + * @return \Ibexa\Core\Repository\Values\Content\Content */ public function buildContentDomainObjectFromPersistence( SPIContent $spiContent, @@ -190,11 +190,11 @@ public function buildContentProxy( /** * Builds a list of Content proxy objects (lazy loaded, loads all as soon as one of them loads). * - * @param \eZ\Publish\SPI\Persistence\Content\ContentInfo[] $infoList + * @param \Ibexa\Contracts\Core\Persistence\Content\ContentInfo[] $infoList * @param string[] $prioritizedLanguages * @param bool $useAlwaysAvailable * - * @return \eZ\Publish\API\Repository\Values\Content\Content[] + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content[] */ public function buildContentProxyList( array $infoList, @@ -216,35 +216,39 @@ public function buildContentProxyList( /** * Returns an array of domain fields created from given array of SPI fields. * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentType On invalid $contentType + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType On invalid $contentType * - * @param \eZ\Publish\SPI\Persistence\Content\Field[] $spiFields - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType|\eZ\Publish\SPI\Persistence\Content\Type $contentType - * @param array $prioritizedLanguages A language priority, filters returned fields and is used as prioritized language code on + * @param \Ibexa\Contracts\Core\Persistence\Content\Field[] $spiFields + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType|\Ibexa\Contracts\Core\Persistence\Content\Type $contentType + * @param string[] $prioritizedLanguages A language priority, filters returned fields and is used as prioritized language code on * returned value object. If not given all languages are returned. * @param string|null $alwaysAvailableLanguage Language code fallback if a given field is not found in $prioritizedLanguages * - * @return array + * @return array<\Ibexa\Contracts\Core\Repository\Values\Content\Field> */ public function buildDomainFields( array $spiFields, $contentType, array $prioritizedLanguages = [], string $alwaysAvailableLanguage = null - ) { - if (!$contentType instanceof SPIContentType && !$contentType instanceof ContentType) { + ): array { + if ($contentType instanceof SPIContentType) { + $contentType = $this->mapPersistenceContentTypeToApi($contentType, $prioritizedLanguages, __METHOD__); + } + + if (!$contentType instanceof ContentType) { throw new InvalidArgumentType('$contentType', 'SPI ContentType | API ContentType'); } $fieldDefinitionsMap = []; - foreach ($contentType->fieldDefinitions as $fieldDefinition) { - $fieldDefinitionsMap[$fieldDefinition->id] = $fieldDefinition; + foreach ($contentType->getFieldDefinitions() as $fieldDefinition) { + $fieldDefinitionsMap[$fieldDefinition->getId()] = $fieldDefinition; } $fieldInFilterLanguagesMap = []; if (!empty($prioritizedLanguages) && $alwaysAvailableLanguage !== null) { foreach ($spiFields as $spiField) { - if (in_array($spiField->languageCode, $prioritizedLanguages)) { + if (in_array($spiField->languageCode, $prioritizedLanguages, true)) { $fieldInFilterLanguagesMap[$spiField->fieldDefinitionId] = true; } } @@ -259,7 +263,7 @@ public function buildDomainFields( $fieldDefinition = $fieldDefinitionsMap[$spiField->fieldDefinitionId]; - if (!empty($prioritizedLanguages) && !in_array($spiField->languageCode, $prioritizedLanguages)) { + if (!empty($prioritizedLanguages) && !in_array($spiField->languageCode, $prioritizedLanguages, true)) { // If filtering is enabled we ignore fields in other languages then $prioritizedLanguages, if: if ($alwaysAvailableLanguage === null) { // Ignore field if we don't have $alwaysAvailableLanguageCode fallback @@ -273,13 +277,13 @@ public function buildDomainFields( } } - $fields[$fieldDefinition->position][] = new Field( + $fields[$fieldDefinition->getPosition()][] = new Field( [ 'id' => $spiField->id, 'value' => $this->fieldTypeRegistry->getFieldType($spiField->type) ->fromPersistenceValue($spiField->value), 'languageCode' => $spiField->languageCode, - 'fieldDefIdentifier' => $fieldDefinition->identifier, + 'fieldDefIdentifier' => $fieldDefinition->getIdentifier(), 'fieldTypeIdentifier' => $spiField->type, ] ); @@ -295,10 +299,10 @@ public function buildDomainFields( /** * Builds a VersionInfo domain object from value object returned from persistence. * - * @param \eZ\Publish\SPI\Persistence\Content\VersionInfo $spiVersionInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\VersionInfo $spiVersionInfo * @param array $prioritizedLanguages * - * @return \eZ\Publish\Core\Repository\Values\Content\VersionInfo + * @return \Ibexa\Core\Repository\Values\Content\VersionInfo */ public function buildVersionInfoDomainObject(SPIVersionInfo $spiVersionInfo, array $prioritizedLanguages = []) { @@ -349,9 +353,9 @@ public function buildVersionInfoDomainObject(SPIVersionInfo $spiVersionInfo, arr /** * Builds a ContentInfo domain object from value object returned from persistence. * - * @param \eZ\Publish\SPI\Persistence\Content\ContentInfo $spiContentInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\ContentInfo $spiContentInfo * - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo */ public function buildContentInfoDomainObject(SPIContentInfo $spiContentInfo) { @@ -403,11 +407,11 @@ public function buildContentInfoDomainObject(SPIContentInfo $spiContentInfo) /** * Builds API Relation object from provided SPI Relation object. * - * @param \eZ\Publish\SPI\Persistence\Content\Relation $spiRelation - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $sourceContentInfo - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $destinationContentInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Relation $spiRelation + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $sourceContentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $destinationContentInfo * - * @return \eZ\Publish\API\Repository\Values\Content\Relation + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation */ public function buildRelationDomainObject( SPIRelation $spiRelation, @@ -476,13 +480,13 @@ public function buildLocation( } /** - * @param \eZ\Publish\SPI\Persistence\Content\Location $spiLocation - * @param \eZ\Publish\API\Repository\Values\Content\Content|null $content - * @param \eZ\Publish\SPI\Persistence\Content\ContentInfo|null $spiContentInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\Location $spiLocation + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|null $content + * @param \Ibexa\Contracts\Core\Persistence\Content\ContentInfo|null $spiContentInfo * - * @return \eZ\Publish\API\Repository\Values\Content\Location + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function buildLocationWithContent( SPILocation $spiLocation, @@ -513,13 +517,13 @@ public function buildLocationWithContent( /** * Builds API Location object for tree root. * - * @param \eZ\Publish\SPI\Persistence\Content\Location $spiLocation + * @param \Ibexa\Contracts\Core\Persistence\Content\Location $spiLocation * - * @return \eZ\Publish\API\Repository\Values\Content\Location + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location */ private function buildRootLocation(SPILocation $spiLocation): APILocation { - // first known commit of eZ Publish 3.x + // first known commit of Ibexa 3.x $legacyDateTime = $this->getDateTime(1030968000); $contentInfo = new ContentInfo([ @@ -536,12 +540,13 @@ private function buildRootLocation(SPILocation $spiLocation): APILocation 'alwaysAvailable' => 1, 'remoteId' => null, 'mainLanguageCode' => 'eng-GB', + 'isHidden' => false, ]); $content = new Content([ 'versionInfo' => new VersionInfo([ 'names' => [ - $contentInfo->mainLanguageCode => $contentInfo->name, + $contentInfo->getMainLanguageCode() => $contentInfo->getName(), ], 'contentInfo' => $contentInfo, 'versionNo' => $contentInfo->currentVersionNo, @@ -571,7 +576,7 @@ private function mapLocation( 'contentInfo' => $contentInfo, 'id' => $spiLocation->id, 'priority' => $spiLocation->priority, - 'hidden' => $spiLocation->hidden || $contentInfo->isHidden, + 'hidden' => $spiLocation->hidden || $contentInfo->isHidden(), 'invisible' => $spiLocation->invisible, 'explicitlyHidden' => $spiLocation->hidden, 'remoteId' => $spiLocation->remoteId, @@ -590,10 +595,10 @@ private function mapLocation( * * Loading of Content objects are done in bulk. * - * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $result SPI search result with SPI ContentInfo items as hits + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult $result SPI search result with SPI ContentInfo items as hits * @param array $languageFilter * - * @return \eZ\Publish\SPI\Persistence\Content\ContentInfo[] ContentInfo we did not find content for is returned. + * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo[] ContentInfo we did not find content for is returned. */ public function buildContentDomainObjectsOnSearchResult(SearchResult $result, array $languageFilter) { @@ -606,7 +611,7 @@ public function buildContentDomainObjectsOnSearchResult(SearchResult $result, ar $translations = $languageFilter['languages'] ?? []; $useAlwaysAvailable = $languageFilter['useAlwaysAvailable'] ?? true; foreach ($result->searchHits as $hit) { - /** @var \eZ\Publish\SPI\Persistence\Content\ContentInfo $info */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\ContentInfo $info */ $info = $hit->valueObject; $contentIds[] = $info->id; $contentTypeIds[] = $info->contentTypeId; @@ -647,10 +652,10 @@ public function buildContentDomainObjectsOnSearchResult(SearchResult $result, ar * This is done in order to be able to: * Load ContentInfo objects in bulk, generate proxy objects for Content that will loaded in bulk on-demand (on use). * - * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $result SPI search result with SPI Location items as hits + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult $result SPI search result with SPI Location items as hits * @param array $languageFilter * - * @return \eZ\Publish\SPI\Persistence\Content\Location[] Locations we did not find content info for is returned. + * @return \Ibexa\Contracts\Core\Persistence\Content\Location[] Locations we did not find content info for is returned. */ public function buildLocationDomainObjectsOnSearchResult(SearchResult $result, array $languageFilter) { @@ -689,16 +694,16 @@ public function buildLocationDomainObjectsOnSearchResult(SearchResult $result, a /** * Creates an array of SPI location create structs from given array of API location create structs. * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException * - * @param \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct $locationCreateStruct - * @param \eZ\Publish\API\Repository\Values\Content\Location $parentLocation + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct $locationCreateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $parentLocation * @param mixed $mainLocation * @param mixed $contentId * @param mixed $contentVersionNo * @param bool $isContentHidden * - * @return \eZ\Publish\SPI\Persistence\Content\Location\CreateStruct + * @return \Ibexa\Contracts\Core\Persistence\Content\Location\CreateStruct */ public function buildSPILocationCreateStruct( $locationCreateStruct, @@ -830,7 +835,7 @@ public function isValidLocationPriority($priority) /** * Validates given translated list $list, which should be an array of strings with language codes as keys. * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException * * @param mixed $list * @param string $argumentName @@ -885,7 +890,7 @@ public function getUniqueHash($object) /** * Returns true if given location is a tree root. * - * @param \eZ\Publish\SPI\Persistence\Content\Location $spiLocation + * @param \Ibexa\Contracts\Core\Persistence\Content\Location $spiLocation * * @return bool */ @@ -893,4 +898,34 @@ private function isRootLocation(SPILocation $spiLocation): bool { return $spiLocation->id === $spiLocation->parentId; } + + /** + * @param string[] $prioritizedLanguages + */ + private function mapPersistenceContentTypeToApi( + SPIContentType $contentType, + array $prioritizedLanguages, + string $methodName + ): ContentType { + trigger_deprecation( + 'ibexa/core', + '4.6', + sprintf( + 'Passing %s instead of %s as 2nd argument of %s() method is deprecated and will cause a fatal error in 5.0. ' . + 'Build %s using %s::buildContentTypeDomainObject prior passing it to the method', + SPIContentType::class, + ContentType::class, + $methodName, + ContentType::class, + ContentTypeDomainMapper::class + ) + ); + + return $this->contentTypeDomainMapper->buildContentTypeDomainObject( + $contentType, + $prioritizedLanguages + ); + } } + +class_alias(ContentDomainMapper::class, 'eZ\Publish\Core\Repository\Mapper\ContentDomainMapper'); diff --git a/src/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationService.php b/src/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationService.php index 98bed1c577..2530991b36 100644 --- a/src/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationService.php +++ b/src/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationService.php @@ -8,11 +8,11 @@ namespace Ibexa\Core\Repository\Mapper\ContentLocationMapper; -use eZ\Publish\API\Repository\LocationService as RepositoryLocationService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationList; -use eZ\Publish\SPI\Repository\Decorator\LocationServiceDecorator; +use Ibexa\Contracts\Core\Repository\Decorator\LocationServiceDecorator; +use Ibexa\Contracts\Core\Repository\LocationService as RepositoryLocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationList; /** * Service decorator hooking ContentLocationMapper to load* calls. @@ -117,14 +117,14 @@ public function loadAllLocations( } /** - * @param iterable<\eZ\Publish\API\Repository\Values\Content\Location> + * @param iterable<\Ibexa\Contracts\Core\Repository\Values\Content\Location> $locationList */ private function setLocationMappings(iterable $locationList): void { foreach ($locationList as $location) { $this->contentLocationMapper->setMapping( - $location->id, - $location->contentId + $location->getId(), + $location->getContentId() ); } } diff --git a/eZ/Publish/Core/Repository/Mapper/ContentMapper.php b/src/lib/Repository/Mapper/ContentMapper.php similarity index 82% rename from eZ/Publish/Core/Repository/Mapper/ContentMapper.php rename to src/lib/Repository/Mapper/ContentMapper.php index b1b441f272..6ca673191d 100644 --- a/eZ/Publish/Core/Repository/Mapper/ContentMapper.php +++ b/src/lib/Repository/Mapper/ContentMapper.php @@ -6,28 +6,28 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\Mapper; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Base\Exceptions\ContentValidationException; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\SPI\FieldType\Value; -use eZ\Publish\SPI\Persistence\Content\Language\Handler; +namespace Ibexa\Core\Repository\Mapper; + +use Ibexa\Contracts\Core\FieldType\Value; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Base\Exceptions\ContentValidationException; +use Ibexa\Core\FieldType\FieldTypeRegistry; /** * @internal Meant for internal use by Repository */ class ContentMapper { - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler */ + /** @var \Ibexa\Core\Persistence\Legacy\Content\Language\Handler */ private $contentLanguageHandler; - /** @var \eZ\Publish\Core\FieldType\FieldTypeRegistry */ + /** @var \Ibexa\Core\FieldType\FieldTypeRegistry */ private $fieldTypeRegistry; public function __construct( @@ -41,11 +41,11 @@ public function __construct( /** * Returns an array of fields like $fields[$field->fieldDefIdentifier][$field->languageCode]. * - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType * or value is set for non-translatable field in language * other than main * - * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct $contentCreateStruct * * @return array */ @@ -58,7 +58,7 @@ public function mapFieldsForCreate(ContentCreateStruct $contentCreateStruct): ar if ($fieldDefinition === null) { throw new ContentValidationException( - "Field definition '%identifier%' does not exist in the given Content Type", + "Field definition '%identifier%' does not exist in the given content type", ['%identifier%' => $field->fieldDefIdentifier] ); } @@ -86,7 +86,7 @@ public function mapFieldsForCreate(ContentCreateStruct $contentCreateStruct): ar /** * Returns all language codes used in given $fields. * - * @param \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct $contentCreateStruct * * @return string[] */ @@ -118,12 +118,12 @@ public function getLanguageCodesForCreate(ContentCreateStruct $contentCreateStru /** * Returns an array of fields like $fields[$field->fieldDefIdentifier][$field->languageCode]. * - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException If field definition does not exist in the ContentType * or value is set for non-translatable field in language * other than main * - * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType * @param string $mainLanguageCode * * @return array @@ -140,7 +140,7 @@ public function mapFieldsForUpdate( if ($fieldDefinition === null) { throw new ContentValidationException( - "Field definition '%identifier%' does not exist in given Content Type", + "Field definition '%identifier%' does not exist in given content type", ['%identifier%' => $field->fieldDefIdentifier] ); } @@ -212,8 +212,8 @@ public function getFieldValueForUpdate( /** * Returns all language codes used in given $fields. * - * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct - * @param \eZ\Publish\API\Repository\Values\Content\Content $content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content * * @return string[] */ @@ -233,7 +233,7 @@ public function getLanguageCodesForUpdate(ContentUpdateStruct $contentUpdateStru /** * Returns only updated language codes. * - * @param \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct * * @return string[] */ @@ -257,10 +257,10 @@ public function getUpdatedLanguageCodes(ContentUpdateStruct $contentUpdateStruct /** * Clones $field with overriding specific properties from given $overrides array. * - * @param \eZ\Publish\API\Repository\Values\Content\Field $field + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field * @param array $overrides * - * @return \eZ\Publish\API\Repository\Values\Content\Field + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field */ private function cloneField(Field $field, array $overrides = []): Field { @@ -279,9 +279,9 @@ private function cloneField(Field $field, array $overrides = []): Field } /** - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $updatedFields + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $updatedFields * - * @return \eZ\Publish\API\Repository\Values\Content\Field[] + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field[] */ public function getFieldsForUpdate(array $updatedFields, Content $content): array { @@ -293,7 +293,7 @@ public function getFieldsForUpdate(array $updatedFields, Content $content): arra if ($fieldDefinition === null) { throw new ContentValidationException( - "Field definition '%identifier%' does not exist in given Content Type", + "Field definition '%identifier%' does not exist in given content type", ['%identifier%' => $updatedField->fieldDefIdentifier] ); } @@ -325,13 +325,13 @@ public function getFieldsForCreate(array $createdFields, ContentType $contentTyp { $fields = []; - /** @var \eZ\Publish\API\Repository\Values\Content\Field $createdField */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Field $createdField */ foreach ($createdFields as $createdField) { $fieldDefinition = $contentType->getFieldDefinition($createdField->fieldDefIdentifier); if ($fieldDefinition === null) { throw new ContentValidationException( - "Field definition '%identifier%' does not exist in the given Content Type", + "Field definition '%identifier%' does not exist in the given content type", ['%identifier%' => $createdField->fieldDefIdentifier] ); } @@ -354,3 +354,5 @@ public function getFieldsForCreate(array $createdFields, ContentType $contentTyp return $fields; } } + +class_alias(ContentMapper::class, 'eZ\Publish\Core\Repository\Mapper\ContentMapper'); diff --git a/eZ/Publish/Core/Repository/Mapper/ContentTypeDomainMapper.php b/src/lib/Repository/Mapper/ContentTypeDomainMapper.php similarity index 82% rename from eZ/Publish/Core/Repository/Mapper/ContentTypeDomainMapper.php rename to src/lib/Repository/Mapper/ContentTypeDomainMapper.php index b2cc2432f9..9b8aff1b15 100644 --- a/eZ/Publish/Core/Repository/Mapper/ContentTypeDomainMapper.php +++ b/src/lib/Repository/Mapper/ContentTypeDomainMapper.php @@ -6,33 +6,33 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\Mapper; +namespace Ibexa\Core\Repository\Mapper; use DateTime; -use eZ\Publish\API\Repository\Values\ContentType\ContentType as APIContentType; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft as APIContentTypeDraft; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup as APIContentTypeGroup; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeUpdateStruct as APIContentTypeUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct as APIFieldDefinitionCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct as APIFieldDefinitionUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserReference as APIUserReference; -use eZ\Publish\Core\Base\Exceptions\ContentTypeFieldDefinitionValidationException; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperInterface; -use eZ\Publish\Core\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Values\ContentType\ContentTypeDraft; -use eZ\Publish\Core\Repository\Values\ContentType\ContentTypeGroup; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection; -use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as SPILanguageHandler; -use eZ\Publish\SPI\Persistence\Content\Type as SPIContentType; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition as SPIFieldDefinition; -use eZ\Publish\SPI\Persistence\Content\Type\Group as SPIContentTypeGroup; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as SPITypeHandler; -use eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct as SPIContentTypeUpdateStruct; +use Ibexa\Contracts\Core\FieldType\FieldType as SPIFieldType; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as SPILanguageHandler; +use Ibexa\Contracts\Core\Persistence\Content\Type as SPIContentType; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as SPIFieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\Type\Group as SPIContentTypeGroup; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as SPITypeHandler; +use Ibexa\Contracts\Core\Persistence\Content\Type\UpdateStruct as SPIContentTypeUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType as APIContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft as APIContentTypeDraft; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup as APIContentTypeGroup; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeUpdateStruct as APIContentTypeUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct as APIFieldDefinitionCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionUpdateStruct as APIFieldDefinitionUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserReference as APIUserReference; +use Ibexa\Core\Base\Exceptions\ContentTypeFieldDefinitionValidationException; +use Ibexa\Core\FieldType\FieldTypeRegistry; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\Repository\ProxyFactory\ProxyDomainMapperInterface; +use Ibexa\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Values\ContentType\ContentTypeDraft; +use Ibexa\Core\Repository\Values\ContentType\ContentTypeGroup; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinitionCollection; /** * ContentTypeDomainMapper is an internal service. @@ -41,13 +41,13 @@ */ class ContentTypeDomainMapper extends ProxyAwareDomainMapper { - /** @var \eZ\Publish\SPI\Persistence\Content\Type\Handler */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Type\Handler */ protected $contentTypeHandler; - /** @var \eZ\Publish\SPI\Persistence\Content\Language\Handler */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Language\Handler */ protected $contentLanguageHandler; - /** @var \eZ\Publish\Core\FieldType\FieldTypeRegistry */ + /** @var \Ibexa\Core\FieldType\FieldTypeRegistry */ protected $fieldTypeRegistry; public function __construct( @@ -115,11 +115,11 @@ public function buildContentTypeDomainObject( /** * Builds ContentType update struct for storage layer. * - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentTypeUpdateStruct $contentTypeUpdateStruct - * @param \eZ\Publish\API\Repository\Values\User\UserReference $user + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft $contentTypeDraft + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeUpdateStruct $contentTypeUpdateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $user * - * @return \eZ\Publish\SPI\Persistence\Content\Type\UpdateStruct + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\UpdateStruct */ public function buildSPIContentTypeUpdateStruct(APIContentTypeDraft $contentTypeDraft, APIContentTypeUpdateStruct $contentTypeUpdateStruct, APIUserReference $user) { @@ -203,6 +203,7 @@ public function buildContentTypeGroupDomainObject(SPIContentTypeGroup $spiGroup, 'names' => $spiGroup->name, 'descriptions' => $spiGroup->description, 'prioritizedLanguages' => $prioritizedLanguages, + 'isSystem' => $spiGroup->isSystem, ] ); } @@ -210,15 +211,15 @@ public function buildContentTypeGroupDomainObject(SPIContentTypeGroup $spiGroup, /** * Builds a FieldDefinition domain object from value object returned by persistence. * - * @param \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition $spiFieldDefinition + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition $spiFieldDefinition * @param string $mainLanguageCode * @param string[] $prioritizedLanguages * - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition */ public function buildFieldDefinitionDomainObject(SPIFieldDefinition $spiFieldDefinition, $mainLanguageCode, array $prioritizedLanguages = []) { - /** @var $fieldType \eZ\Publish\SPI\FieldType\FieldType */ + /** @var $fieldType \Ibexa\Contracts\Core\FieldType\FieldType */ $fieldType = $this->fieldTypeRegistry->getFieldType($spiFieldDefinition->fieldType); $fieldDefinition = new FieldDefinition( [ @@ -249,19 +250,19 @@ public function buildFieldDefinitionDomainObject(SPIFieldDefinition $spiFieldDef * Builds SPIFieldDefinition object using API FieldDefinitionUpdateStruct * and API FieldDefinition. * - * @deprecated use \eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper::buildSPIFieldDefinitionFromUpdateStruct() + * @deprecated use {@see \Ibexa\Core\Repository\Mapper\ContentTypeDomainMapper::buildSPIFieldDefinitionFromUpdateStruct} * - * @throws \eZ\Publish\API\Repository\Exceptions\ContentTypeFieldDefinitionValidationException if validator configuration or + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException if validator configuration or * field setting do not validate * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition * - * @return \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition */ public function buildSPIFieldDefinitionUpdate(APIFieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct, APIFieldDefinition $fieldDefinition) { - /** @var $fieldType \eZ\Publish\SPI\FieldType\FieldType */ + /** @var $fieldType \Ibexa\Contracts\Core\FieldType\FieldType */ $fieldType = $this->fieldTypeRegistry->getFieldType( $fieldDefinition->fieldTypeIdentifier ); @@ -339,21 +340,21 @@ public function buildSPIFieldDefinitionUpdate(APIFieldDefinitionUpdateStruct $fi } /** - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition $fieldDefinition + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDefinition * @param string $mainLanguageCode * - * @return \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\Core\Base\Exceptions\ContentTypeFieldDefinitionValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\ContentTypeFieldDefinitionValidationException */ public function buildSPIFieldDefinitionFromUpdateStruct( APIFieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct, APIFieldDefinition $fieldDefinition, string $mainLanguageCode ): SPIFieldDefinition { - /** @var $fieldType \eZ\Publish\SPI\FieldType\FieldType */ + /** @var $fieldType \Ibexa\Contracts\Core\FieldType\FieldType */ $fieldType = $this->fieldTypeRegistry->getFieldType( $fieldDefinition->fieldTypeIdentifier ); @@ -434,15 +435,15 @@ public function buildSPIFieldDefinitionFromUpdateStruct( /** * Builds SPIFieldDefinition object using API FieldDefinitionCreateStruct. * - * @deprecated use \eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper::buildSPIFieldDefinitionFromCreateStruct() + * @deprecated use {@see \Ibexa\Core\Repository\Mapper\ContentTypeDomainMapper::buildSPIFieldDefinitionFromCreateStruct} * - * @throws \eZ\Publish\API\Repository\Exceptions\ContentTypeFieldDefinitionValidationException if validator configuration or + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException if validator configuration or * field setting do not validate * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct $fieldDefinitionCreateStruct - * @param \eZ\Publish\SPI\FieldType\FieldType $fieldType + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct $fieldDefinitionCreateStruct + * @param \Ibexa\Contracts\Core\FieldType\FieldType $fieldType * - * @return \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition */ public function buildSPIFieldDefinitionCreate(APIFieldDefinitionCreateStruct $fieldDefinitionCreateStruct, SPIFieldType $fieldType) { @@ -492,13 +493,13 @@ public function buildSPIFieldDefinitionCreate(APIFieldDefinitionCreateStruct $fi } /** - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct $fieldDefinitionCreateStruct - * @param \eZ\Publish\SPI\FieldType\FieldType $fieldType + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct $fieldDefinitionCreateStruct + * @param \Ibexa\Contracts\Core\FieldType\FieldType $fieldType * @param string $mainLanguageCode * - * @return \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function buildSPIFieldDefinitionFromCreateStruct( APIFieldDefinitionCreateStruct $fieldDefinitionCreateStruct, @@ -560,3 +561,5 @@ protected function getDateTime(int $timestamp): DateTime return $dateTime; } } + +class_alias(ContentTypeDomainMapper::class, 'eZ\Publish\Core\Repository\Mapper\ContentTypeDomainMapper'); diff --git a/src/lib/Repository/Mapper/ProxyAwareDomainMapper.php b/src/lib/Repository/Mapper/ProxyAwareDomainMapper.php new file mode 100644 index 0000000000..13060ae286 --- /dev/null +++ b/src/lib/Repository/Mapper/ProxyAwareDomainMapper.php @@ -0,0 +1,39 @@ +proxyFactory = $proxyFactory; + } + + /** + * Setter for Proxy Factory to work around cyclic dependency issue on Repository. + * + * Note: to be resolved by Repository decoupling. + */ + final public function setProxyFactory(ProxyDomainMapperInterface $proxyFactory): void + { + $this->proxyFactory = $proxyFactory; + } +} + +class_alias(ProxyAwareDomainMapper::class, 'eZ\Publish\Core\Repository\Mapper\ProxyAwareDomainMapper'); diff --git a/eZ/Publish/Core/Repository/Mapper/Readme.md b/src/lib/Repository/Mapper/Readme.md similarity index 100% rename from eZ/Publish/Core/Repository/Mapper/Readme.md rename to src/lib/Repository/Mapper/Readme.md diff --git a/src/lib/Repository/Mapper/RoleDomainMapper.php b/src/lib/Repository/Mapper/RoleDomainMapper.php new file mode 100644 index 0000000000..3f5f6ecd0c --- /dev/null +++ b/src/lib/Repository/Mapper/RoleDomainMapper.php @@ -0,0 +1,253 @@ +limitationService = $limitationService; + } + + /** + * Maps provided SPI Role value object to API Role value object. + * + * @param \Ibexa\Contracts\Core\Persistence\User\Role $role + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Role + */ + public function buildDomainRoleObject(SPIRole $role) + { + $rolePolicies = []; + foreach ($role->policies as $spiPolicy) { + $rolePolicies[] = $this->buildDomainPolicyObject($spiPolicy); + } + + return new Role( + [ + 'id' => $role->id, + 'identifier' => $role->identifier, + 'status' => $role->status, + 'policies' => $rolePolicies, + ] + ); + } + + /** + * Builds a RoleDraft domain object from value object returned by persistence + * Decorates Role. + * + * @param \Ibexa\Contracts\Core\Persistence\User\Role $spiRole + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft + */ + public function buildDomainRoleDraftObject(SPIRole $spiRole) + { + return new RoleDraft( + [ + 'innerRole' => $this->buildDomainRoleObject($spiRole), + ] + ); + } + + /** + * Maps provided SPI Policy value object to API Policy value object. + * + * @param \Ibexa\Contracts\Core\Persistence\User\Policy $spiPolicy + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Policy|\Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft + */ + public function buildDomainPolicyObject(SPIPolicy $spiPolicy) + { + $policyLimitations = []; + if ($spiPolicy->module !== '*' && $spiPolicy->function !== '*' && $spiPolicy->limitations !== '*') { + foreach ($spiPolicy->limitations as $identifier => $values) { + $policyLimitations[] = $this->limitationService->getLimitationType($identifier)->buildValue($values); + } + } + + $policy = new Policy( + [ + 'id' => $spiPolicy->id, + 'roleId' => $spiPolicy->roleId, + 'module' => $spiPolicy->module, + 'function' => $spiPolicy->function, + 'limitations' => $policyLimitations, + ] + ); + + // Original ID is set on SPI policy, which means that it's a draft. + if ($spiPolicy->originalId) { + $policy = new PolicyDraft(['innerPolicy' => $policy, 'originalId' => $spiPolicy->originalId]); + } + + return $policy; + } + + /** + * Builds the API UserRoleAssignment object from provided SPI RoleAssignment object. + * + * @param \Ibexa\Contracts\Core\Persistence\User\RoleAssignment $spiRoleAssignment + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * @param \Ibexa\Contracts\Core\Repository\Values\User\Role $role + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserRoleAssignment + */ + public function buildDomainUserRoleAssignmentObject(SPIRoleAssignment $spiRoleAssignment, User $user, APIRole $role) + { + $limitation = null; + if (!empty($spiRoleAssignment->limitationIdentifier)) { + $limitation = $this + ->limitationService + ->getLimitationType($spiRoleAssignment->limitationIdentifier) + ->buildValue($spiRoleAssignment->values); + } + + return new UserRoleAssignment( + [ + 'id' => $spiRoleAssignment->id, + 'limitation' => $limitation, + 'role' => $role, + 'user' => $user, + ] + ); + } + + /** + * Builds the API UserGroupRoleAssignment object from provided SPI RoleAssignment object. + * + * @param \Ibexa\Contracts\Core\Persistence\User\RoleAssignment $spiRoleAssignment + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroup + * @param \Ibexa\Contracts\Core\Repository\Values\User\Role $role + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroupRoleAssignment + */ + public function buildDomainUserGroupRoleAssignmentObject(SPIRoleAssignment $spiRoleAssignment, UserGroup $userGroup, APIRole $role) + { + $limitation = null; + if (!empty($spiRoleAssignment->limitationIdentifier)) { + $limitation = $this + ->limitationService + ->getLimitationType($spiRoleAssignment->limitationIdentifier) + ->buildValue($spiRoleAssignment->values); + } + + return new UserGroupRoleAssignment( + [ + 'id' => $spiRoleAssignment->id, + 'limitation' => $limitation, + 'role' => $role, + 'userGroup' => $userGroup, + ] + ); + } + + /** + * Creates SPI Role create struct from provided API role create struct. + */ + public function buildPersistenceRoleCreateStruct(APIRoleCreateStruct $roleCreateStruct): SPIRoleCreateStruct + { + $policiesToCreate = $this->fillRoleStructWithPolicies($roleCreateStruct); + + return new SPIRoleCreateStruct( + [ + 'identifier' => $roleCreateStruct->identifier, + 'policies' => $policiesToCreate, + ] + ); + } + + /** + * Creates SPI Role copy struct from provided API role copy struct. + */ + public function buildPersistenceRoleCopyStruct(APIRoleCopyStruct $roleCopyStruct, int $clonedId, int $status): SPIRoleCopyStruct + { + $policiesToCopy = $this->fillRoleStructWithPolicies($roleCopyStruct); + + return new SPIRoleCopyStruct( + [ + 'clonedId' => $clonedId, + 'newIdentifier' => $roleCopyStruct->newIdentifier, + 'status' => $status, + 'policies' => $policiesToCopy, + ] + ); + } + + protected function fillRoleStructWithPolicies(APIRoleCreateStruct $struct): array + { + $policies = []; + foreach ($struct->getPolicies() as $policyStruct) { + $policies[] = $this->buildPersistencePolicyObject( + $policyStruct->module, + $policyStruct->function, + $policyStruct->getLimitations() + ); + } + + return $policies; + } + + /** + * Creates SPI Policy value object from provided module, function and limitations. + * + * @param string $module + * @param string $function + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation[] $limitations + * + * @return \Ibexa\Contracts\Core\Persistence\User\Policy + */ + public function buildPersistencePolicyObject($module, $function, array $limitations) + { + $limitationsToCreate = '*'; + if ($module !== '*' && $function !== '*' && !empty($limitations)) { + $limitationsToCreate = []; + foreach ($limitations as $limitation) { + $limitationsToCreate[$limitation->getIdentifier()] = $limitation->limitationValues; + } + } + + return new SPIPolicy( + [ + 'module' => $module, + 'function' => $function, + 'limitations' => $limitationsToCreate, + ] + ); + } +} + +class_alias(RoleDomainMapper::class, 'eZ\Publish\Core\Repository\Mapper\RoleDomainMapper'); diff --git a/src/lib/Repository/NameSchema/NameSchemaService.php b/src/lib/Repository/NameSchema/NameSchemaService.php new file mode 100644 index 0000000000..782a154191 --- /dev/null +++ b/src/lib/Repository/NameSchema/NameSchemaService.php @@ -0,0 +1,344 @@ + + * <attribute_identifier> + * <attribute_identifier> <2nd-identifier> + * User text <attribute_identifier>|(<2nd-identifier><3rd-identifier>) + * + * + * Example: + * + * <nickname|(<firstname> <lastname>)> + * + * + * Tokens are looked up from left to right. If a match is found for the + * leftmost token, the 2nd token will not be used. Tokens are representations + * of fields. So a match means that the current field has data. + * + * Tokens are the field definition identifiers which are used in the class edit-interface. + */ +class NameSchemaService implements NameSchemaServiceInterface +{ + /** + * The string to use to signify group tokens. + * + * @var string + */ + public const META_STRING = 'EZMETAGROUP_'; + + protected FieldTypeRegistry $fieldTypeRegistry; + + /** + * @param array{limit?: integer, sequence?: string} $settings + */ + protected array $settings; + + private EventDispatcherInterface $eventDispatcher; + + private SchemaIdentifierExtractorInterface $schemaIdentifierExtractor; + + /** + * @param array{limit?: integer, sequence?: string} $settings + */ + public function __construct( + FieldTypeRegistry $fieldTypeRegistry, + SchemaIdentifierExtractorInterface $schemaIdentifierExtractor, + EventDispatcherInterface $eventDispatcher, + array $settings = [] + ) { + $this->fieldTypeRegistry = $fieldTypeRegistry; + // Union makes sure default settings are ignored if provided in argument + $this->settings = $settings + [ + 'limit' => 150, + 'sequence' => '...', + ]; + $this->eventDispatcher = $eventDispatcher; + $this->schemaIdentifierExtractor = $schemaIdentifierExtractor; + } + + public function resolveUrlAliasSchema(Content $content, ContentType $contentType = null): array + { + $contentType ??= $content->getContentType(); + $schemaName = $contentType->urlAliasSchema ?: $contentType->nameSchema; + $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($schemaName); + + $event = $this->eventDispatcher->dispatch( + new ResolveUrlAliasSchemaEvent( + $schemaIdentifiers, + $content + ) + ); + + return $this->buildNames($event->getTokenValues(), $schemaName); + } + + public function resolveContentNameSchema( + Content $content, + array $fieldMap = [], + array $languageCodes = [], + ContentType $contentType = null + ): array { + $contentType ??= $content->getContentType(); + $schemaName = $contentType->nameSchema; + $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($schemaName); + + $event = $this->eventDispatcher->dispatch( + new ResolveContentNameSchemaEvent( + $content, + $schemaIdentifiers, + $contentType, + $fieldMap, + $languageCodes + ) + ); + + return $this->buildNames($event->getTokenValues(), $schemaName); + } + + public function resolveNameSchema( + string $nameSchema, + ContentType $contentType, + array $fieldMap, + array $languageCodes + ): array { + $schemaIdentifiers = $this->schemaIdentifierExtractor->extract($nameSchema); + $event = $this->eventDispatcher->dispatch( + new ResolveNameSchemaEvent( + $schemaIdentifiers, + $contentType, + $fieldMap, + $languageCodes + ) + ); + + return $this->buildNames($event->getTokenValues(), $nameSchema); + } + + /** + * Extract all tokens from $namePattern. + * + * Example: + * + * Text more text ==> + * + */ + protected function extractTokens(string $nameSchema): array + { + preg_match_all( + '|<([^>]+)>|U', + $nameSchema, + $tokenArray + ); + + return $tokenArray[0]; + } + + /** + * Looks up the value $token should be replaced with and returns this as + * a string. Meta strings denoting token groups are automatically + * inferred. + */ + protected function resolveToken(string $token, array $titles, array $groupLookupTable): string + { + $replaceString = ''; + $tokenParts = $this->tokenParts($token); + + foreach ($tokenParts as $tokenPart) { + if ($this->isTokenGroup($tokenPart)) { + $replaceString = $groupLookupTable[$tokenPart]; + $groupTokenArray = $this->extractTokens($replaceString); + + foreach ($groupTokenArray as $groupToken) { + $replaceString = str_replace( + $groupToken, + $this->resolveToken( + $groupToken, + $titles, + $groupLookupTable + ), + $replaceString + ); + } + + // We want to stop after the first matching token part / identifier is found + // if id1 has a value, id2 will not be used. + // In this case id1 or id1 is a token group. + break; + } + if (array_key_exists($tokenPart, $titles) + && $titles[$tokenPart] !== '' + && $titles[$tokenPart] !== null + ) { + $replaceString = $titles[$tokenPart]; + // We want to stop after the first matching token part / identifier is found + // if id1 has a value, id2 will not be used. + break; + } + } + + return $replaceString; + } + + /** + * Checks whether $identifier is a placeholder for a token group. + */ + protected function isTokenGroup(string $identifier): bool + { + return strpos($identifier, self::META_STRING) !== false; + } + + /** + * Returns the different constituents of $token in an array. + * The normal case here is that the different identifiers within one token + * will be tokenized and returned. + * + * Example: + * + * "<title|text>" ==> array( 'title', 'text' ) + * + * + * @param string $token + * + * @return array + */ + protected function tokenParts(string $token): array + { + return preg_split('/[^\w:]+/', $token, -1, PREG_SPLIT_NO_EMPTY); + } + + /** + * Builds a lookup / translation table for groups in the $namePattern. + * The groups are referenced with a generated meta-token in the original + * name pattern. + * + * Returns intermediate name pattern where groups are replaced with meta-tokens. + * + * @param string $nameSchema + * + * @return array{string, array} + */ + protected function filterNameSchema(string $nameSchema): array + { + $retNamePattern = $nameSchema; + $foundGroups = preg_match_all('/\((.+)\)/U', $nameSchema, $groupArray); + $groupLookupTable = []; + + if ($foundGroups) { + $i = 0; + foreach ($groupArray[1] as $group) { + // Create meta-token for group + $metaToken = self::META_STRING . $i; + + // Insert the group with its placeholder token + /** @var string $retNamePattern */ + $retNamePattern = str_replace($group, $metaToken, $retNamePattern); + + // Remove the pattern "(" ")" from the tokens + $group = str_replace(['(', ')'], '', $group); + + $groupLookupTable[$metaToken] = $group; + ++$i; + } + $nameSchema = $retNamePattern; + } + + return [$nameSchema, $groupLookupTable]; + } + + /** + * @param array> $tokenValues + * + * @return array + */ + public function buildNames(array $tokenValues, string $nameSchema): array + { + if (empty($tokenValues)) { + throw new UnresolvedTokenNamesException('$tokenValues', 'is Empty'); + } + + [$filteredNameSchema, $groupLookupTable] = $this->filterNameSchema($nameSchema); + $tokens = $this->extractTokens($filteredNameSchema); + + $names = []; + foreach ($tokenValues as $languageCode => $tokenValue) { + $names[$languageCode] = $this->validateNameLength( + str_replace( + $tokens, + array_map( + fn (string $token): string => $this->resolveToken($token, $tokenValue, $groupLookupTable), + $tokens + ), + $filteredNameSchema + ) + ); + } + + return $names; + } + + /** + * @return array + */ + protected function getIdentifiers(string $schemaString): array + { + $allTokens = '#<(.*)>#U'; + $identifiers = '#\\W#'; + + $tmpArray = []; + preg_match_all($allTokens, $schemaString, $matches); + + foreach ($matches[1] as $match) { + $tmpArray[] = preg_split($identifiers, $match, -1, PREG_SPLIT_NO_EMPTY); + } + + $retArray = []; + foreach ($tmpArray as $matchGroup) { + if (is_array($matchGroup)) { + foreach ($matchGroup as $item) { + $retArray[] = $item; + } + } else { + $retArray[] = $matchGroup; + } + } + + return $retArray; + } + + public function validateNameLength(string $name): string + { + // Make sure length is not longer than $limit unless it's 0 + if ($this->settings['limit'] && mb_strlen($name) > $this->settings['limit']) { + $name = rtrim( + mb_substr($name, 0, $this->settings['limit'] - strlen($this->settings['sequence'])) + ) . $this->settings['sequence']; + } + + return $name; + } +} diff --git a/src/lib/Repository/NameSchema/SchemaIdentifierExtractor.php b/src/lib/Repository/NameSchema/SchemaIdentifierExtractor.php new file mode 100644 index 0000000000..032e8ef279 --- /dev/null +++ b/src/lib/Repository/NameSchema/SchemaIdentifierExtractor.php @@ -0,0 +1,57 @@ +> + * + * @example + * $extractor = new SchemaIdentifierExtractor(); + * $schemaString = '--'; + * $result = $extractor->extract($schemaString); + * // $result will be: + * // [ + * // 'field' => ['foo', 'bar'], + * // 'attribute' => ['bar', 'baz'], + * // ] + */ + public function extract(string $schemaString): array + { + $allTokens = '/[<|\(]?([\w\d:_]+)[>\)]?/'; + + if (false === preg_match_all($allTokens, $schemaString, $matches)) { + return []; + } + + $strategyIdentifiers = []; + foreach ($matches[1] as $tokenExpression) { + $tokens = explode('|', $tokenExpression); + foreach ($tokens as $token) { + $strategyToken = explode(':', $token, 2); + + if (count($strategyToken) === 2) { + [$strategy, $token] = $strategyToken; + } else { + $token = $strategyToken[0]; + $strategy = 'field'; + } + + $strategyIdentifiers[$strategy][] = $token; + } + + $strategyIdentifiers[$strategy] = array_unique($strategyIdentifiers[$strategy]); + } + + return $strategyIdentifiers; + } +} diff --git a/src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php b/src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php new file mode 100644 index 0000000000..090664857e --- /dev/null +++ b/src/lib/Repository/NameSchema/UnresolvedTokenNamesException.php @@ -0,0 +1,15 @@ +persistenceHandler = $persistenceHandler; + $this->permissionResolver = $permissionResolver; + } + + /** + * {@inheritdoc} + */ + public function loadNotifications(int $offset = 0, int $limit = 25): NotificationList + { + $currentUserId = $this->getCurrentUserId(); + + $list = new NotificationList(); + $list->totalCount = $this->persistenceHandler->countNotifications($currentUserId); + if ($list->totalCount > 0) { + $list->items = array_map(function (Notification $spiNotification) { + return $this->buildDomainObject($spiNotification); + }, $this->persistenceHandler->loadUserNotifications($currentUserId, $offset, $limit)); + } + + return $list; + } + + /** + * {@inheritdoc} + */ + public function createNotification(APICreateStruct $createStruct): APINotification + { + $spiCreateStruct = new CreateStruct(); + + if (empty($createStruct->ownerId)) { + throw new InvalidArgumentException('ownerId', $createStruct->ownerId); + } + + $spiCreateStruct->ownerId = $createStruct->ownerId; + + if (empty($createStruct->type)) { + throw new InvalidArgumentException('type', $createStruct->type); + } + + $spiCreateStruct->type = $createStruct->type; + $spiCreateStruct->isPending = (bool) $createStruct->isPending; + $spiCreateStruct->data = $createStruct->data; + $spiCreateStruct->created = (new DateTime())->getTimestamp(); + + return $this->buildDomainObject( + $this->persistenceHandler->createNotification($spiCreateStruct) + ); + } + + /** + * {@inheritdoc} + */ + public function getNotification(int $notificationId): APINotification + { + $notification = $this->persistenceHandler->getNotificationById($notificationId); + + $currentUserId = $this->getCurrentUserId(); + if (!$notification->ownerId || $currentUserId != $notification->ownerId) { + throw new NotFoundException('Notification', $notificationId); + } + + return $this->buildDomainObject($notification); + } + + /** + * {@inheritdoc} + */ + public function markNotificationAsRead(APINotification $notification): void + { + $currentUserId = $this->getCurrentUserId(); + + if (!$notification->id) { + throw new NotFoundException('Notification', $notification->id); + } + + if ($notification->ownerId !== $currentUserId) { + throw new UnauthorizedException($notification->id, 'Notification'); + } + + if (!$notification->isPending) { + return; + } + + $updateStruct = new UpdateStruct(); + $updateStruct->isPending = false; + + $this->persistenceHandler->updateNotification($notification, $updateStruct); + } + + /** + * {@inheritdoc} + */ + public function getPendingNotificationCount(): int + { + return $this->persistenceHandler->countPendingNotifications( + $this->getCurrentUserId() + ); + } + + /** + * {@inheritdoc} + */ + public function getNotificationCount(): int + { + return $this->persistenceHandler->countNotifications( + $this->getCurrentUserId() + ); + } + + /** + * {@inheritdoc} + */ + public function deleteNotification(APINotification $notification): void + { + $this->persistenceHandler->delete($notification); + } + + /** + * Builds Notification domain object from ValueObject returned by Persistence API. + * + * @param \Ibexa\Contracts\Core\Persistence\Notification\Notification $spiNotification + * + * @return \Ibexa\Contracts\Core\Repository\Values\Notification\Notification + */ + protected function buildDomainObject(Notification $spiNotification): APINotification + { + return new APINotification([ + 'id' => $spiNotification->id, + 'ownerId' => $spiNotification->ownerId, + 'isPending' => $spiNotification->isPending, + 'type' => $spiNotification->type, + 'created' => new DateTime("@{$spiNotification->created}"), + 'data' => $spiNotification->data, + ]); + } + + private function getCurrentUserId(): int + { + return $this->permissionResolver + ->getCurrentUserReference() + ->getUserId(); + } +} + +class_alias(NotificationService::class, 'eZ\Publish\Core\Repository\NotificationService'); diff --git a/src/lib/Repository/ObjectStateService.php b/src/lib/Repository/ObjectStateService.php new file mode 100644 index 0000000000..e654a1621c --- /dev/null +++ b/src/lib/Repository/ObjectStateService.php @@ -0,0 +1,837 @@ +repository = $repository; + $this->objectStateHandler = $objectStateHandler; + $this->permissionResolver = $permissionResolver; + // Union makes sure default settings are ignored if provided in argument + $this->settings = $settings + [ + //'defaultSetting' => array(), + ]; + } + + /** + * Creates a new object state group. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to create an object state group + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the object state group with provided identifier already exists + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupCreateStruct $objectStateGroupCreateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup + */ + public function createObjectStateGroup(ObjectStateGroupCreateStruct $objectStateGroupCreateStruct): APIObjectStateGroup + { + if (!$this->permissionResolver->canUser('state', 'administrate', $objectStateGroupCreateStruct)) { + throw new UnauthorizedException('state', 'administrate'); + } + + $inputStruct = $this->buildCreateInputStruct( + $objectStateGroupCreateStruct->identifier, + $objectStateGroupCreateStruct->defaultLanguageCode, + $objectStateGroupCreateStruct->names, + $objectStateGroupCreateStruct->descriptions + ); + + try { + $this->objectStateHandler->loadGroupByIdentifier($inputStruct->identifier); + throw new InvalidArgumentException( + 'objectStateGroupCreateStruct', + 'An Object state group with the provided identifier already exists' + ); + } catch (APINotFoundException $e) { + // Do nothing + } + + $this->repository->beginTransaction(); + try { + $spiObjectStateGroup = $this->objectStateHandler->createGroup($inputStruct); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->buildDomainObjectStateGroupObject($spiObjectStateGroup); + } + + /** + * {@inheritdoc} + */ + public function loadObjectStateGroup(int $objectStateGroupId, array $prioritizedLanguages = []): APIObjectStateGroup + { + $spiObjectStateGroup = $this->objectStateHandler->loadGroup($objectStateGroupId); + + return $this->buildDomainObjectStateGroupObject($spiObjectStateGroup, $prioritizedLanguages); + } + + public function loadObjectStateGroupByIdentifier( + string $objectStateGroupIdentifier, + array $prioritizedLanguages = [] + ): APIObjectStateGroup { + $spiObjectStateGroup = $this->objectStateHandler->loadGroupByIdentifier($objectStateGroupIdentifier); + + return $this->buildDomainObjectStateGroupObject($spiObjectStateGroup, $prioritizedLanguages); + } + + /** + * {@inheritdoc} + */ + public function loadObjectStateGroups(int $offset = 0, int $limit = -1, array $prioritizedLanguages = []): iterable + { + $spiObjectStateGroups = $this->objectStateHandler->loadAllGroups($offset, $limit); + + $objectStateGroups = []; + foreach ($spiObjectStateGroups as $spiObjectStateGroup) { + $objectStateGroups[] = $this->buildDomainObjectStateGroupObject( + $spiObjectStateGroup, + $prioritizedLanguages + ); + } + + return $objectStateGroups; + } + + /** + * This method returns the ordered list of object states of a group. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup + * @param string[] $prioritizedLanguages + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState[] + */ + public function loadObjectStates( + APIObjectStateGroup $objectStateGroup, + array $prioritizedLanguages = [] + ): iterable { + $spiObjectStates = $this->objectStateHandler->loadObjectStates($objectStateGroup->id); + + $objectStates = []; + foreach ($spiObjectStates as $spiObjectState) { + $objectStates[] = $this->buildDomainObjectStateObject( + $spiObjectState, + $objectStateGroup, + $prioritizedLanguages + ); + } + + return $objectStates; + } + + /** + * Updates an object state group. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to update an object state group + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the object state group with provided identifier already exists + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct $objectStateGroupUpdateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup + */ + public function updateObjectStateGroup(APIObjectStateGroup $objectStateGroup, ObjectStateGroupUpdateStruct $objectStateGroupUpdateStruct): APIObjectStateGroup + { + if (!$this->permissionResolver->canUser('state', 'administrate', $objectStateGroup)) { + throw new UnauthorizedException('state', 'administrate'); + } + + $loadedObjectStateGroup = $this->loadObjectStateGroup($objectStateGroup->id); + + $inputStruct = $this->buildObjectStateGroupUpdateInputStruct( + $loadedObjectStateGroup, + $objectStateGroupUpdateStruct->identifier, + $objectStateGroupUpdateStruct->defaultLanguageCode, + $objectStateGroupUpdateStruct->names, + $objectStateGroupUpdateStruct->descriptions + ); + + if ($objectStateGroupUpdateStruct->identifier !== null) { + try { + $existingObjectStateGroup = $this->objectStateHandler->loadGroupByIdentifier($inputStruct->identifier); + if ($existingObjectStateGroup->id != $loadedObjectStateGroup->id) { + throw new InvalidArgumentException( + 'objectStateGroupUpdateStruct', + 'An Object state group with the provided identifier already exists' + ); + } + } catch (APINotFoundException $e) { + // Do nothing + } + } + + $this->repository->beginTransaction(); + try { + $spiObjectStateGroup = $this->objectStateHandler->updateGroup( + $loadedObjectStateGroup->id, + $inputStruct + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->buildDomainObjectStateGroupObject($spiObjectStateGroup, $objectStateGroup->prioritizedLanguages); + } + + /** + * Deletes a object state group including all states and links to content. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete an object state group + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup + */ + public function deleteObjectStateGroup(APIObjectStateGroup $objectStateGroup): void + { + if (!$this->permissionResolver->canUser('state', 'administrate', $objectStateGroup)) { + throw new UnauthorizedException('state', 'administrate'); + } + + $loadedObjectStateGroup = $this->loadObjectStateGroup($objectStateGroup->id); + + $this->repository->beginTransaction(); + try { + $this->objectStateHandler->deleteGroup($loadedObjectStateGroup->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Creates a new object state in the given group. + * + * Note: in current kernel: If it is the first state all content objects will + * set to this state. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to create an object state + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the object state with provided identifier already exists in the same group + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateCreateStruct $objectStateCreateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState + */ + public function createObjectState(APIObjectStateGroup $objectStateGroup, ObjectStateCreateStruct $objectStateCreateStruct): APIObjectState + { + if (!$this->permissionResolver->canUser('state', 'administrate', $objectStateCreateStruct, [$objectStateGroup])) { + throw new UnauthorizedException('state', 'administrate'); + } + + $inputStruct = $this->buildCreateInputStruct( + $objectStateCreateStruct->identifier, + $objectStateCreateStruct->defaultLanguageCode, + $objectStateCreateStruct->names, + $objectStateCreateStruct->descriptions + ); + + try { + $this->objectStateHandler->loadByIdentifier($inputStruct->identifier, $objectStateGroup->id); + throw new InvalidArgumentException( + 'objectStateCreateStruct', + 'An Object state with the provided identifier already exists in the provided Object state group' + ); + } catch (APINotFoundException $e) { + // Do nothing + } + + $this->repository->beginTransaction(); + try { + $spiObjectState = $this->objectStateHandler->create($objectStateGroup->id, $inputStruct); + + if (is_int($objectStateCreateStruct->priority)) { + $this->objectStateHandler->setPriority( + $spiObjectState->id, + $objectStateCreateStruct->priority + ); + + // Reload the object state to have the updated priority, + // considering that priorities are always incremental within a group + $spiObjectState = $this->objectStateHandler->load($spiObjectState->id); + } + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->buildDomainObjectStateObject($spiObjectState); + } + + /** + * {@inheritdoc} + */ + public function loadObjectState(int $stateId, array $prioritizedLanguages = []): APIObjectState + { + $spiObjectState = $this->objectStateHandler->load($stateId); + + return $this->buildDomainObjectStateObject($spiObjectState, null, $prioritizedLanguages); + } + + public function loadObjectStateByIdentifier( + APIObjectStateGroup $objectStateGroup, + string $objectStateIdentifier, + array $prioritizedLanguages = [] + ): APIObjectState { + $spiObjectState = $this->objectStateHandler->loadByIdentifier( + $objectStateIdentifier, + $objectStateGroup->id + ); + + return $this->buildDomainObjectStateObject($spiObjectState, null, $prioritizedLanguages); + } + + /** + * Updates an object state. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to update an object state + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the object state with provided identifier already exists in the same group + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState $objectState + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateUpdateStruct $objectStateUpdateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState + */ + public function updateObjectState(APIObjectState $objectState, ObjectStateUpdateStruct $objectStateUpdateStruct): APIObjectState + { + if (!$this->permissionResolver->canUser('state', 'administrate', $objectState)) { + throw new UnauthorizedException('state', 'administrate'); + } + + $loadedObjectState = $this->loadObjectState($objectState->id); + + $inputStruct = $this->buildObjectStateUpdateInputStruct( + $loadedObjectState, + $objectStateUpdateStruct->identifier, + $objectStateUpdateStruct->defaultLanguageCode, + $objectStateUpdateStruct->names, + $objectStateUpdateStruct->descriptions + ); + + if ($objectStateUpdateStruct->identifier !== null) { + try { + $existingObjectState = $this->objectStateHandler->loadByIdentifier( + $inputStruct->identifier, + $loadedObjectState->getObjectStateGroup()->id + ); + + if ($existingObjectState->id != $loadedObjectState->id) { + throw new InvalidArgumentException( + 'objectStateUpdateStruct', + 'An Object state with the provided identifier already exists in provided Object state group' + ); + } + } catch (APINotFoundException $e) { + // Do nothing + } + } + + $this->repository->beginTransaction(); + try { + $spiObjectState = $this->objectStateHandler->update( + $loadedObjectState->id, + $inputStruct + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->buildDomainObjectStateObject($spiObjectState, null, $objectState->prioritizedLanguages); + } + + /** + * Changes the priority of the state. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to change priority on an object state + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState $objectState + * @param int $priority + */ + public function setPriorityOfObjectState(APIObjectState $objectState, int $priority): void + { + if (!$this->permissionResolver->canUser('state', 'administrate', $objectState)) { + throw new UnauthorizedException('state', 'administrate'); + } + + $loadedObjectState = $this->loadObjectState($objectState->id); + + $this->repository->beginTransaction(); + try { + $this->objectStateHandler->setPriority( + $loadedObjectState->id, + $priority + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Deletes a object state. The state of the content objects is reset to the + * first object state in the group. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete an object state + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState $objectState + */ + public function deleteObjectState(APIObjectState $objectState): void + { + if (!$this->permissionResolver->canUser('state', 'administrate', $objectState)) { + throw new UnauthorizedException('state', 'administrate'); + } + + $loadedObjectState = $this->loadObjectState($objectState->id); + + $this->repository->beginTransaction(); + try { + $this->objectStateHandler->delete($loadedObjectState->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Sets the object-state of a state group to $state for the given content. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the object state does not belong to the given group + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to change the object state + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState $objectState + */ + public function setContentState(ContentInfo $contentInfo, APIObjectStateGroup $objectStateGroup, APIObjectState $objectState): void + { + if (!$this->permissionResolver->canUser('state', 'assign', $contentInfo, [$objectState])) { + throw new UnauthorizedException('state', 'assign', ['contentId' => $contentInfo->id]); + } + + $loadedObjectState = $this->loadObjectState($objectState->id); + + if ($loadedObjectState->getObjectStateGroup()->id != $objectStateGroup->id) { + throw new InvalidArgumentException('objectState', 'Object state does not belong to the given group'); + } + + $this->repository->beginTransaction(); + try { + $this->objectStateHandler->setContentState( + $contentInfo->id, + $objectStateGroup->id, + $loadedObjectState->id + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Gets the object-state of object identified by $contentId. + * + * The $state is the id of the state within one group. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState + */ + public function getContentState(ContentInfo $contentInfo, APIObjectStateGroup $objectStateGroup): APIObjectState + { + $spiObjectState = $this->objectStateHandler->getContentState( + $contentInfo->id, + $objectStateGroup->id + ); + + return $this->buildDomainObjectStateObject($spiObjectState, $objectStateGroup); + } + + /** + * Returns the number of objects which are in this state. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState $objectState + * + * @return int + */ + public function getContentCount(APIObjectState $objectState): int + { + return $this->objectStateHandler->getContentCount( + $objectState->id + ); + } + + /** + * Instantiates a new Object State Group Create Struct and sets $identified in it. + * + * @param string $identifier + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupCreateStruct + */ + public function newObjectStateGroupCreateStruct(string $identifier): ObjectStateGroupCreateStruct + { + $objectStateGroupCreateStruct = new ObjectStateGroupCreateStruct(); + $objectStateGroupCreateStruct->identifier = $identifier; + + return $objectStateGroupCreateStruct; + } + + /** + * Instantiates a new Object State Group Update Struct. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct + */ + public function newObjectStateGroupUpdateStruct(): ObjectStateGroupUpdateStruct + { + return new ObjectStateGroupUpdateStruct(); + } + + /** + * Instantiates a new Object State Create Struct and sets $identifier in it. + * + * @param string $identifier + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateCreateStruct + */ + public function newObjectStateCreateStruct(string $identifier): ObjectStateCreateStruct + { + $objectStateCreateStruct = new ObjectStateCreateStruct(); + $objectStateCreateStruct->identifier = $identifier; + + return $objectStateCreateStruct; + } + + /** + * Instantiates a new Object State Update Struct. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateUpdateStruct + */ + public function newObjectStateUpdateStruct(): ObjectStateUpdateStruct + { + return new ObjectStateUpdateStruct(); + } + + /** + * Converts the object state SPI value object to API value object. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\ObjectState $spiObjectState + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup|null $objectStateGroup + * @param string[] $prioritizedLanguages + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState + */ + protected function buildDomainObjectStateObject( + SPIObjectState $spiObjectState, + APIObjectStateGroup $objectStateGroup = null, + array $prioritizedLanguages = [] + ): APIObjectState { + $objectStateGroup = $objectStateGroup ?: $this->loadObjectStateGroup($spiObjectState->groupId, $prioritizedLanguages); + + return new ObjectState( + [ + 'id' => $spiObjectState->id, + 'identifier' => $spiObjectState->identifier, + 'priority' => $spiObjectState->priority, + 'mainLanguageCode' => $spiObjectState->defaultLanguage, + 'languageCodes' => $spiObjectState->languageCodes, + 'names' => $spiObjectState->name, + 'descriptions' => $spiObjectState->description, + 'objectStateGroup' => $objectStateGroup, + 'prioritizedLanguages' => $prioritizedLanguages, + ] + ); + } + + /** + * Converts the object state group SPI value object to API value object. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group $spiObjectStateGroup + * @param array $prioritizedLanguages + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup + */ + protected function buildDomainObjectStateGroupObject( + SPIObjectStateGroup $spiObjectStateGroup, + array $prioritizedLanguages = [] + ): APIObjectStateGroup { + return new ObjectStateGroup( + [ + 'id' => $spiObjectStateGroup->id, + 'identifier' => $spiObjectStateGroup->identifier, + 'mainLanguageCode' => $spiObjectStateGroup->defaultLanguage, + 'languageCodes' => $spiObjectStateGroup->languageCodes, + 'names' => $spiObjectStateGroup->name, + 'descriptions' => $spiObjectStateGroup->description, + 'prioritizedLanguages' => $prioritizedLanguages, + ] + ); + } + + /** + * Validates input for creating object states/groups and builds the InputStruct object. + * + * @param string $identifier + * @param string $defaultLanguageCode + * @param string[] $names + * @param string[]|null $descriptions + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct + */ + protected function buildCreateInputStruct( + string $identifier, + string $defaultLanguageCode, + array $names, + ?array $descriptions + ): InputStruct { + if (!is_string($identifier) || empty($identifier)) { + throw new InvalidArgumentValue('identifier', $identifier); + } + + if (!is_string($defaultLanguageCode) || empty($defaultLanguageCode)) { + throw new InvalidArgumentValue('defaultLanguageCode', $defaultLanguageCode); + } + + if (!is_array($names) || empty($names)) { + throw new InvalidArgumentValue('names', $names); + } + + if (!isset($names[$defaultLanguageCode])) { + throw new InvalidArgumentValue('names', $names); + } + + foreach ($names as $languageCode => $name) { + try { + $this->repository->getContentLanguageService()->loadLanguage($languageCode); + } catch (NotFoundException $e) { + throw new InvalidArgumentValue('names', $names); + } + + if (!is_string($name) || empty($name)) { + throw new InvalidArgumentValue('names', $names); + } + } + + $descriptions = $descriptions !== null ? $descriptions : []; + + $inputStruct = new InputStruct(); + $inputStruct->identifier = $identifier; + $inputStruct->defaultLanguage = $defaultLanguageCode; + $inputStruct->name = $names; + + $inputStruct->description = []; + foreach ($names as $languageCode => $name) { + if (isset($descriptions[$languageCode]) && !empty($descriptions[$languageCode])) { + $inputStruct->description[$languageCode] = $descriptions[$languageCode]; + } else { + $inputStruct->description[$languageCode] = ''; + } + } + + return $inputStruct; + } + + /** + * Validates input for updating object states and builds the InputStruct object. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState $objectState + * @param string|null $identifier + * @param string|null $defaultLanguageCode + * @param string[]|null $names + * @param string[]|null $descriptions + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct + */ + protected function buildObjectStateUpdateInputStruct( + APIObjectState $objectState, + ?string $identifier, + ?string $defaultLanguageCode, + ?array $names, + ?array $descriptions + ): InputStruct { + $inputStruct = new InputStruct(); + + if ($identifier !== null && (!is_string($identifier) || empty($identifier))) { + throw new InvalidArgumentValue('identifier', $identifier); + } + + $inputStruct->identifier = $identifier !== null ? $identifier : $objectState->identifier; + + if ($defaultLanguageCode !== null && (!is_string($defaultLanguageCode) || empty($defaultLanguageCode))) { + throw new InvalidArgumentValue('defaultLanguageCode', $defaultLanguageCode); + } + + $inputStruct->defaultLanguage = $defaultLanguageCode !== null ? $defaultLanguageCode : $objectState->defaultLanguageCode; + + if ($names !== null && (!is_array($names) || empty($names))) { + throw new InvalidArgumentValue('names', $names); + } + + $inputStruct->name = $names !== null ? $names : $objectState->getNames(); + + if (!isset($inputStruct->name[$inputStruct->defaultLanguage])) { + throw new InvalidArgumentValue('names', $inputStruct->name); + } + + foreach ($inputStruct->name as $languageCode => $name) { + try { + $this->repository->getContentLanguageService()->loadLanguage($languageCode); + } catch (NotFoundException $e) { + throw new InvalidArgumentValue('names', $inputStruct->name); + } + + if (!is_string($name) || empty($name)) { + throw new InvalidArgumentValue('names', $inputStruct->name); + } + } + + $descriptions = $descriptions !== null ? $descriptions : $objectState->getDescriptions(); + $descriptions = $descriptions !== null ? $descriptions : []; + + $inputStruct->description = []; + foreach ($inputStruct->name as $languageCode => $name) { + if (isset($descriptions[$languageCode]) && !empty($descriptions[$languageCode])) { + $inputStruct->description[$languageCode] = $descriptions[$languageCode]; + } else { + $inputStruct->description[$languageCode] = ''; + } + } + + return $inputStruct; + } + + /** + * Validates input for updating object state groups and builds the InputStruct object. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup + * @param string|null $identifier + * @param string|null $defaultLanguageCode + * @param string[]|null $names + * @param string[]|null $descriptions + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct + */ + protected function buildObjectStateGroupUpdateInputStruct( + APIObjectStateGroup $objectStateGroup, + ?string $identifier, + ?string $defaultLanguageCode, + ?array $names, + ?array $descriptions + ): InputStruct { + $inputStruct = new InputStruct(); + + if ($identifier !== null && empty($identifier)) { + throw new InvalidArgumentValue('identifier', $identifier); + } + + $inputStruct->identifier = $identifier !== null ? $identifier : $objectStateGroup->identifier; + + if ($defaultLanguageCode !== null && empty($defaultLanguageCode)) { + throw new InvalidArgumentValue('defaultLanguageCode', $defaultLanguageCode); + } + + $inputStruct->defaultLanguage = $defaultLanguageCode !== null ? $defaultLanguageCode : $objectStateGroup->defaultLanguageCode; + + if ($names !== null && empty($names)) { + throw new InvalidArgumentValue('names', $names); + } + + $inputStruct->name = $names !== null ? $names : $objectStateGroup->getNames(); + + if (!isset($inputStruct->name[$inputStruct->defaultLanguage])) { + throw new InvalidArgumentValue('names', $inputStruct->name); + } + + foreach ($inputStruct->name as $languageCode => $name) { + try { + $this->repository->getContentLanguageService()->loadLanguage($languageCode); + } catch (NotFoundException $e) { + throw new InvalidArgumentValue('names', $inputStruct->name); + } + + if (!is_string($name) || empty($name)) { + throw new InvalidArgumentValue('names', $inputStruct->name); + } + } + + $descriptions = $descriptions !== null ? $descriptions : $objectStateGroup->getDescriptions(); + $descriptions = $descriptions !== null ? $descriptions : []; + + $inputStruct->description = []; + foreach ($inputStruct->name as $languageCode => $name) { + if (isset($descriptions[$languageCode]) && !empty($descriptions[$languageCode])) { + $inputStruct->description[$languageCode] = $descriptions[$languageCode]; + } else { + $inputStruct->description[$languageCode] = ''; + } + } + + return $inputStruct; + } +} + +class_alias(ObjectStateService::class, 'eZ\Publish\Core\Repository\ObjectStateService'); diff --git a/eZ/Publish/Core/Repository/Permission/CachedPermissionService.php b/src/lib/Repository/Permission/CachedPermissionService.php similarity index 81% rename from eZ/Publish/Core/Repository/Permission/CachedPermissionService.php rename to src/lib/Repository/Permission/CachedPermissionService.php index 620d0fa679..2bdbdc37b7 100644 --- a/eZ/Publish/Core/Repository/Permission/CachedPermissionService.php +++ b/src/lib/Repository/Permission/CachedPermissionService.php @@ -6,17 +6,17 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\Permission; +namespace Ibexa\Core\Repository\Permission; use Exception; -use eZ\Publish\API\Repository\PermissionCriterionResolver as APIPermissionCriterionResolver; -use eZ\Publish\API\Repository\PermissionResolver as APIPermissionResolver; -use eZ\Publish\API\Repository\PermissionService; -use eZ\Publish\API\Repository\Repository as RepositoryInterface; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\User\LookupLimitationResult; -use eZ\Publish\API\Repository\Values\User\UserReference; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\PermissionCriterionResolver as APIPermissionCriterionResolver; +use Ibexa\Contracts\Core\Repository\PermissionResolver as APIPermissionResolver; +use Ibexa\Contracts\Core\Repository\PermissionService; +use Ibexa\Contracts\Core\Repository\Repository as RepositoryInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\User\LookupLimitationResult; +use Ibexa\Contracts\Core\Repository\Values\User\UserReference; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; /** * Cache implementation of PermissionResolver and PermissionCriterionResolver interface. @@ -31,10 +31,10 @@ */ class CachedPermissionService implements PermissionService { - /** @var \eZ\Publish\API\Repository\PermissionResolver */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ private $innerPermissionResolver; - /** @var \eZ\Publish\API\Repository\PermissionCriterionResolver */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionCriterionResolver */ private $permissionCriterionResolver; /** @var int */ @@ -52,7 +52,7 @@ class CachedPermissionService implements PermissionService * * Value is null if not yet set or cleared. * - * @var bool|\eZ\Publish\API\Repository\Values\Content\Query\Criterion + * @var bool|\Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion */ private $permissionCriterion; @@ -66,8 +66,8 @@ class CachedPermissionService implements PermissionService /** * CachedPermissionService constructor. * - * @param \eZ\Publish\API\Repository\PermissionResolver $innerPermissionResolver - * @param \eZ\Publish\API\Repository\PermissionCriterionResolver $permissionCriterionResolver + * @param \Ibexa\Contracts\Core\Repository\PermissionResolver $innerPermissionResolver + * @param \Ibexa\Contracts\Core\Repository\PermissionCriterionResolver $permissionCriterionResolver * @param int $cacheTTL By default set to 5 seconds, should be low to avoid to many permission exceptions on long running requests / processes (even if tolerant search service should handle that) */ public function __construct( @@ -157,3 +157,5 @@ public function getQueryPermissionsCriterion(): Criterion return $this->permissionCriterionResolver->getQueryPermissionsCriterion(); } } + +class_alias(CachedPermissionService::class, 'eZ\Publish\Core\Repository\Permission\CachedPermissionService'); diff --git a/src/lib/Repository/Permission/LimitationService.php b/src/lib/Repository/Permission/LimitationService.php new file mode 100644 index 0000000000..8c3d1dcb37 --- /dev/null +++ b/src/lib/Repository/Permission/LimitationService.php @@ -0,0 +1,102 @@ +limitationTypes = null !== $limitationTypes + ? iterator_to_array($limitationTypes) : + []; + } + + /** + * Returns the LimitationType registered with the given identifier. + * + * Returns the correct implementation of API Limitation value object + * based on provided identifier + * + * @throws \Ibexa\Core\Base\Exceptions\NotFound\LimitationNotFoundException + */ + public function getLimitationType(string $identifier): Type + { + if (!isset($this->limitationTypes[$identifier])) { + throw new LimitationNotFoundException($identifier); + } + + return $this->limitationTypes[$identifier]; + } + + /** + * Validates an array of Limitations. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation[] $limitations + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function validateLimitations(array $limitations): array + { + $allErrors = []; + foreach ($limitations as $limitation) { + $errors = $this->validateLimitation($limitation); + if (!empty($errors)) { + $allErrors[$limitation->getIdentifier()] = $errors; + } + } + + return $allErrors; + } + + /** + * Validates single Limitation. + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If the Role settings is in a bad state*@throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function validateLimitation(Limitation $limitation): array + { + $identifier = $limitation->getIdentifier(); + if (!isset($this->limitationTypes[$identifier])) { + throw new BadStateException( + '$identifier', + "limitationType[{$identifier}] is not configured" + ); + } + + $type = $this->limitationTypes[$identifier]; + + // This will throw if it does not pass + $type->acceptValue($limitation); + + // This return array of validation errors + return $type->validate($limitation); + } +} + +class_alias(LimitationService::class, 'eZ\Publish\Core\Repository\Permission\LimitationService'); diff --git a/src/lib/Repository/Permission/PermissionCriterionResolver.php b/src/lib/Repository/Permission/PermissionCriterionResolver.php new file mode 100644 index 0000000000..5f7605eb7c --- /dev/null +++ b/src/lib/Repository/Permission/PermissionCriterionResolver.php @@ -0,0 +1,176 @@ +innerPermissionResolver = $innerPermissionResolver; + $this->limitationService = $limitationService; + } + + /** + * Get permission criteria if needed and return false if no access at all. + * + * @uses \Ibexa\Contracts\Core\Repository\PermissionResolver::getCurrentUserReference() + * @uses \Ibexa\Contracts\Core\Repository\PermissionResolver::hasAccess() + * + * @param string $module + * @param string $function + * @param array $targets + * + * @return bool|\Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion + */ + public function getPermissionsCriterion(string $module = 'content', string $function = 'read', ?array $targets = null) + { + $permissionSets = $this->innerPermissionResolver->hasAccess($module, $function); + if (is_bool($permissionSets)) { + return $permissionSets; + } + + if (empty($permissionSets)) { + throw new RuntimeException("Received an empty array of limitations from hasAccess( '{$module}', '{$function}' )"); + } + + /* + * RoleAssignment is a OR condition, so is policy, while limitations is a AND condition + * + * If RoleAssignment has limitation then policy OR conditions are wrapped in a AND condition with the + * role limitation, otherwise it will be merged into RoleAssignment's OR condition. + */ + $currentUserRef = $this->innerPermissionResolver->getCurrentUserReference(); + $roleAssignmentOrCriteria = []; + foreach ($permissionSets as $permissionSet) { + // $permissionSet is a RoleAssignment, but in the form of role limitation & role policies hash + $policyOrCriteria = []; + /** @var \Ibexa\Contracts\Core\Repository\Values\User\Policy */ + foreach ($permissionSet['policies'] as $policy) { + $limitations = $policy->getLimitations(); + if (empty($limitations)) { + // Given policy gives full access, optimize away all role policies (but not role limitation if any) + // This should be optimized on create/update of Roles, however we keep this here for bc with older data + $policyOrCriteria = []; + break; + } + + $limitationsAndCriteria = []; + foreach ($limitations as $limitation) { + $limitationsAndCriteria[] = $this->getCriterionForLimitation($limitation, $currentUserRef, $targets); + } + + $policyOrCriteria[] = isset($limitationsAndCriteria[1]) ? + new LogicalAnd($limitationsAndCriteria) : + $limitationsAndCriteria[0]; + } + + /** + * Apply role limitations if there is one. + * + * @var \Ibexa\Contracts\Core\Repository\Values\User\Limitation[] + */ + if ($permissionSet['limitation'] instanceof Limitation) { + // We need to match both the limitation AND *one* of the policies, aka; roleLimit AND policies(OR) + if (!empty($policyOrCriteria)) { + $criterion = $this->getCriterionForLimitation($permissionSet['limitation'], $currentUserRef, $targets); + $roleAssignmentOrCriteria[] = new LogicalAnd( + [ + $criterion, + isset($policyOrCriteria[1]) ? new LogicalOr($policyOrCriteria) : $policyOrCriteria[0], + ] + ); + } else { + $roleAssignmentOrCriteria[] = $this->getCriterionForLimitation( + $permissionSet['limitation'], + $currentUserRef, + $targets + ); + } + } elseif (!empty($policyOrCriteria)) { + // Otherwise merge $policyOrCriteria into $roleAssignmentOrCriteria + // There is no role limitation, so any of the policies can globally match in the returned OR criteria + $roleAssignmentOrCriteria = empty($roleAssignmentOrCriteria) ? + $policyOrCriteria : + array_merge($roleAssignmentOrCriteria, $policyOrCriteria); + } + } + + if (empty($roleAssignmentOrCriteria)) { + return false; + } + + return isset($roleAssignmentOrCriteria[1]) ? + new LogicalOr($roleAssignmentOrCriteria) : + $roleAssignmentOrCriteria[0]; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUserRef + * @param array|null $targets + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface|\Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalOperator + */ + private function getCriterionForLimitation(Limitation $limitation, UserReference $currentUserRef, ?array $targets): CriterionInterface + { + $type = $this->limitationService->getLimitationType($limitation->getIdentifier()); + if ($type instanceof TargetOnlyLimitationType) { + return $type->getCriterionByTarget($limitation, $currentUserRef, $targets); + } + + return $type->getCriterion($limitation, $currentUserRef); + } + + public function getQueryPermissionsCriterion(): Criterion + { + // Permission Criterion handling work-around to avoid rewriting old architecture of perm. sys. + $permissionCriterion = $this->getPermissionsCriterion( + 'content', + 'read' + ); + if (true === $permissionCriterion) { + return new Criterion\MatchAll(); + } + if (false === $permissionCriterion) { + return new Criterion\MatchNone(); + } + + return $permissionCriterion; + } +} + +class_alias(PermissionCriterionResolver::class, 'eZ\Publish\Core\Repository\Permission\PermissionCriterionResolver'); diff --git a/src/lib/Repository/Permission/PermissionResolver.php b/src/lib/Repository/Permission/PermissionResolver.php new file mode 100644 index 0000000000..bdabb84e08 --- /dev/null +++ b/src/lib/Repository/Permission/PermissionResolver.php @@ -0,0 +1,467 @@ +roleDomainMapper = $roleDomainMapper; + $this->limitationService = $limitationService; + $this->userHandler = $userHandler; + $this->configResolver = $configResolver; + $this->policyMap = $policyMap; + } + + public function getCurrentUserReference(): APIUserReference + { + if (empty($this->currentUserRef)) { + $this->currentUserRef = new UserReference( + $this->configResolver->getParameter('anonymous_user_id') + ); + } + + return $this->currentUserRef; + } + + public function setCurrentUserReference(APIUserReference $userReference): void + { + $id = $userReference->getUserId(); + if (!$id) { + throw new InvalidArgumentValue('$user->getUserId()', $id); + } + + $this->currentUserRef = $userReference; + } + + public function hasAccess(string $module, string $function, ?APIUserReference $userReference = null) + { + if (!isset($this->policyMap[$module])) { + throw new InvalidArgumentValue('module', "module: {$module}/ function: {$function}"); + } elseif (!array_key_exists($function, $this->policyMap[$module])) { + throw new InvalidArgumentValue('function', "module: {$module}/ function: {$function}"); + } + + // Full access if sudo nesting level is set by {@see sudo()} + if ($this->sudoNestingLevel > 0) { + return true; + } + + if ($userReference === null) { + $userReference = $this->getCurrentUserReference(); + } + + // Uses SPI to avoid triggering permission checks in Role/User service + $permissionSets = []; + $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($userReference->getUserId(), true); + foreach ($spiRoleAssignments as $spiRoleAssignment) { + $permissionSet = ['limitation' => null, 'policies' => []]; + + $spiRole = $this->userHandler->loadRole($spiRoleAssignment->roleId); + foreach ($spiRole->policies as $spiPolicy) { + if ($spiPolicy->module === '*' && $spiRoleAssignment->limitationIdentifier === null) { + return true; + } + + if ($spiPolicy->module !== $module && $spiPolicy->module !== '*') { + continue; + } + + if ($spiPolicy->function === '*' && $spiRoleAssignment->limitationIdentifier === null) { + return true; + } + + if ($spiPolicy->function !== $function && $spiPolicy->function !== '*') { + continue; + } + + if ($spiPolicy->limitations === '*' && $spiRoleAssignment->limitationIdentifier === null) { + return true; + } + + $permissionSet['policies'][] = $this->roleDomainMapper->buildDomainPolicyObject($spiPolicy); + } + + if (!empty($permissionSet['policies'])) { + if ($spiRoleAssignment->limitationIdentifier !== null) { + $permissionSet['limitation'] = $this->limitationService + ->getLimitationType($spiRoleAssignment->limitationIdentifier) + ->buildValue($spiRoleAssignment->values); + } + + $permissionSets[] = $permissionSet; + } + } + + if (!empty($permissionSets)) { + return $permissionSets; + } + + return false; // No policies matching $module and $function, or they contained limitations + } + + public function canUser(string $module, string $function, ValueObject $object, array $targets = []): bool + { + $permissionSets = $this->hasAccess($module, $function); + if ($permissionSets === false || $permissionSets === true) { + return $permissionSets; + } + + if (empty($targets)) { + $targets = null; + } + + $currentUserRef = $this->getCurrentUserReference(); + foreach ($permissionSets as $permissionSet) { + /** + * First deal with Role limitation if any. + * + * Here we accept ACCESS_GRANTED and ACCESS_ABSTAIN, the latter in cases where $object and $targets + * are not supported by limitation. + * + * @var \Ibexa\Contracts\Core\Repository\Values\User\Limitation[] + */ + if ( + $permissionSet['limitation'] instanceof Limitation + && $this->isDeniedByRoleLimitation( + $permissionSet['limitation'], + $currentUserRef, + $object, + $targets + ) + ) { + continue; + } + + /** + * Loop over all policies. + * + * These are already filtered by hasAccess and given hasAccess did not return boolean + * there must be some, so only return true if one of them says yes. + * + * @var \Ibexa\Contracts\Core\Repository\Values\User\Policy + */ + foreach ($permissionSet['policies'] as $policy) { + $limitations = $policy->getLimitations(); + + /* + * Return true if policy gives full access (aka no limitations) + */ + if ($limitations === '*') { + return true; + } + + /* + * Loop over limitations, all must return ACCESS_GRANTED for policy to pass. + * If limitations was empty array this means same as '*' + */ + $limitationsPass = true; + foreach ($limitations as $limitation) { + $type = $this->limitationService->getLimitationType($limitation->getIdentifier()); + $accessVote = $type->evaluate( + $limitation, + $currentUserRef, + $object, + $this->prepareTargetsForType($targets, $type) + ); + /* + * For policy limitation atm only support ACCESS_GRANTED + * + * Reasoning: Right now, use of a policy limitation not valid for a policy is per definition a + * BadState. To reach this you would have to configure the "policyMap" wrongly, like using + * Node (Location) limitation on state/assign. So in this case Role Limitations will return + * ACCESS_ABSTAIN (== no access here), and other limitations will throw InvalidArgument above, + * both cases forcing dev to investigate to find miss configuration. This might be relaxed in + * the future if valid use cases for ACCESS_ABSTAIN on policy limitations becomes known. + */ + if ($accessVote !== LimitationType::ACCESS_GRANTED) { + $limitationsPass = false; + break; // Break to next policy, all limitations must pass + } + } + if ($limitationsPass) { + return true; + } + } + } + + return false; // None of the limitation sets wanted to let you in, sorry! + } + + /** + * {@inheritdoc} + */ + public function lookupLimitations( + string $module, + string $function, + ValueObject $object, + array $targets = [], + array $limitationsIdentifiers = [] + ): LookupLimitationResult { + $permissionSets = $this->hasAccess($module, $function); + + if (is_bool($permissionSets)) { + return new LookupLimitationResult($permissionSets); + } + + if (empty($targets)) { + $targets = null; + } + + $currentUserReference = $this->getCurrentUserReference(); + + $passedLimitations = []; + $passedRoleLimitations = []; + + foreach ($permissionSets as $permissionSet) { + if ($this->isDeniedByRoleLimitation($permissionSet['limitation'], $currentUserReference, $object, $targets)) { + continue; + } + + /** @var \Ibexa\Contracts\Core\Repository\Values\User\Policy $policy */ + foreach ($permissionSet['policies'] as $policy) { + $policyLimitations = $policy->getLimitations(); + + /** Return empty array if policy gives full access (aka no limitations) */ + if ($policyLimitations === '*') { + return new LookupLimitationResult(true); + } + + $limitationsPass = true; + $possibleLimitations = []; + $possibleRoleLimitation = $permissionSet['limitation']; + foreach ($policyLimitations as $limitation) { + $limitationsPass = $this->isGrantedByLimitation($limitation, $currentUserReference, $object, $targets); + if (!$limitationsPass) { + break; + } + + $possibleLimitations[] = $limitation; + } + + $limitationFilter = static function (Limitation $limitation) use ($limitationsIdentifiers) { + return \in_array($limitation->getIdentifier(), $limitationsIdentifiers, true); + }; + + if (!empty($limitationsIdentifiers)) { + $possibleLimitations = array_filter($possibleLimitations, $limitationFilter); + if (!\in_array($possibleRoleLimitation, $limitationsIdentifiers, true)) { + $possibleRoleLimitation = null; + } + } + + if ($limitationsPass) { + $passedLimitations[] = new LookupPolicyLimitations($policy, $possibleLimitations); + if (null !== $possibleRoleLimitation) { + $passedRoleLimitations[] = $possibleRoleLimitation; + } + } + } + } + + return new LookupLimitationResult(!empty($passedLimitations), $passedRoleLimitations, $passedLimitations); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUserReference + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param array|null $targets + * + * @return bool + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + private function isGrantedByLimitation( + Limitation $limitation, + APIUserReference $currentUserReference, + ValueObject $object, + ?array $targets + ): bool { + $type = $this->limitationService->getLimitationType($limitation->getIdentifier()); + $accessVote = $type->evaluate( + $limitation, + $currentUserReference, + $object, + $this->prepareTargetsForType($targets, $type) + ); + + return $accessVote === LimitationType::ACCESS_GRANTED; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation|null $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserReference $currentUserReference + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param array|null $targets + * + * @return bool + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + private function isDeniedByRoleLimitation( + ?Limitation $limitation, + APIUserReference $currentUserReference, + ValueObject $object, + ?array $targets + ): bool { + if (null === $limitation) { + return false; + } + + $type = $this->limitationService->getLimitationType($limitation->getIdentifier()); + $accessVote = $type->evaluate( + $limitation, + $currentUserReference, + $object, + $this->prepareTargetsForType($targets, $type) + ); + + return $accessVote === LimitationType::ACCESS_DENIED; + } + + /** + * @internal For internal use only, do not depend on this method. + * + * Allows API execution to be performed with full access sand-boxed. + * + * The closure sandbox will do a catch all on exceptions and rethrow after + * re-setting the sudo flag. + * + * Example use: + * $location = $repository->sudo( + * function ( Repository $repo ) use ( $locationId ) + * { + * return $repo->getLocationService()->loadLocation( $locationId ) + * } + * ); + * + * @param \callable(\Ibexa\Contracts\Core\Repository\Repository): mixed $callback + * @param \Ibexa\Contracts\Core\Repository\Repository $outerRepository + * + * @throws \RuntimeException Thrown on recursive sudo() use. + * @throws \Exception Re throws exceptions thrown inside $callback + * + * @return mixed + */ + public function sudo(callable $callback, RepositoryInterface $outerRepository) + { + ++$this->sudoNestingLevel; + try { + $returnValue = $callback($outerRepository); + } catch (Exception $e) { + --$this->sudoNestingLevel; + throw $e; + } + + --$this->sudoNestingLevel; + + return $returnValue; + } + + /** + * Prepare list of targets for the given Type keeping BC. + * + * @param array|null $targets + * @param \Ibexa\Contracts\Core\Limitation\Type $type + * + * @return array|null + */ + private function prepareTargetsForType(?array $targets, LimitationType $type): ?array + { + $isTargetAware = $type instanceof TargetAwareType; + + // BC: null for empty targets is still expected by some Limitations, so needs to be preserved + if (null === $targets) { + return $isTargetAware ? [] : null; + } + + // BC: for TargetAware Limitations return only instances of Target, for others return only non-Target instances + $targets = array_filter( + $targets, + static function ($target) use ($isTargetAware) { + $isTarget = $target instanceof Target; + + return $isTargetAware ? $isTarget : !$isTarget; + } + ); + + // BC: treat empty targets after filtering as if they were empty the whole time + if (!$isTargetAware && empty($targets)) { + return null; + } + + return $targets; + } +} + +class_alias(PermissionResolver::class, 'eZ\Publish\Core\Repository\Permission\PermissionResolver'); diff --git a/src/lib/Repository/PermissionsCriterionHandler.php b/src/lib/Repository/PermissionsCriterionHandler.php new file mode 100644 index 0000000000..09278d4841 --- /dev/null +++ b/src/lib/Repository/PermissionsCriterionHandler.php @@ -0,0 +1,50 @@ +getPermissionsCriterion(); + if ($permissionCriterion === true || $permissionCriterion === false) { + return $permissionCriterion; + } + + // Merge with original $criterion + if ($criterion instanceof LogicalAnd) { + $criterion->criteria[] = $permissionCriterion; + } else { + $criterion = new LogicalAnd( + [ + $criterion, + $permissionCriterion, + ] + ); + } + + return true; + } +} + +class_alias(PermissionsCriterionHandler::class, 'eZ\Publish\Core\Repository\PermissionsCriterionHandler'); diff --git a/eZ/Publish/Core/Repository/ProxyFactory/ProxyDomainMapper.php b/src/lib/Repository/ProxyFactory/ProxyDomainMapper.php similarity index 89% rename from eZ/Publish/Core/Repository/ProxyFactory/ProxyDomainMapper.php rename to src/lib/Repository/ProxyFactory/ProxyDomainMapper.php index 77ae30ea79..0fe7a5e15c 100644 --- a/eZ/Publish/Core/Repository/ProxyFactory/ProxyDomainMapper.php +++ b/src/lib/Repository/ProxyFactory/ProxyDomainMapper.php @@ -6,17 +6,17 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\ProxyFactory; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Section; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup; -use eZ\Publish\API\Repository\Values\User\User; +namespace Ibexa\Core\Repository\ProxyFactory; + +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\Section; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup; +use Ibexa\Contracts\Core\Repository\Values\User\User; use ProxyManager\Proxy\LazyLoadingInterface; /** @@ -24,7 +24,7 @@ */ final class ProxyDomainMapper implements ProxyDomainMapperInterface { - /** @var \eZ\Publish\API\Repository\Repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository */ private $repository; /** @var \ProxyManager\Factory\LazyLoadingValueHolderFactory */ @@ -54,7 +54,7 @@ public function createContentProxy( $contentId, $prioritizedLanguages, $versionNo, - $useAlwaysAvailable + $useAlwaysAvailable, ); return true; @@ -228,3 +228,5 @@ public function createUserProxy(int $userId, array $prioritizedLanguages = Langu return $this->proxyGenerator->createProxy(User::class, $initializer); } } + +class_alias(ProxyDomainMapper::class, 'eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapper'); diff --git a/src/lib/Repository/ProxyFactory/ProxyDomainMapperFactory.php b/src/lib/Repository/ProxyFactory/ProxyDomainMapperFactory.php new file mode 100644 index 0000000000..1e1e794628 --- /dev/null +++ b/src/lib/Repository/ProxyFactory/ProxyDomainMapperFactory.php @@ -0,0 +1,32 @@ +proxyGenerator = $proxyGenerator; + } + + public function create(Repository $repository): ProxyDomainMapperInterface + { + return new ProxyDomainMapper($repository, $this->proxyGenerator); + } +} + +class_alias(ProxyDomainMapperFactory::class, 'eZ\Publish\Core\Repository\ProxyFactory\ProxyDomainMapperFactory'); diff --git a/src/lib/Repository/ProxyFactory/ProxyDomainMapperFactoryInterface.php b/src/lib/Repository/ProxyFactory/ProxyDomainMapperFactoryInterface.php new file mode 100644 index 0000000000..8c7dce26be --- /dev/null +++ b/src/lib/Repository/ProxyFactory/ProxyDomainMapperFactoryInterface.php @@ -0,0 +1,19 @@ +persistenceHandler = $persistenceHandler; + $this->searchHandler = $searchHandler; + $this->backgroundIndexer = $backgroundIndexer; + $this->relationProcessor = $relationProcessor; + $this->fieldTypeRegistry = $fieldTypeRegistry; + $this->passwordHashService = $passwordHashGenerator; + $this->thumbnailStrategy = $thumbnailStrategy; + $this->proxyDomainMapperFactory = $proxyDomainMapperFactory; + $this->contentDomainMapper = $contentDomainMapper; + $this->contentTypeDomainMapper = $contentTypeDomainMapper; + $this->roleDomainMapper = $roleDomainMapper; + $this->limitationService = $limitationService; + $this->languageResolver = $languageResolver; + $this->contentFilteringHandler = $contentFilteringHandler; + $this->permissionService = $permissionService; + $this->locationFilteringHandler = $locationFilteringHandler; + + $this->serviceSettings = $serviceSettings + [ + 'content' => [], + 'contentType' => [], + 'location' => [], + 'section' => [], + 'role' => [], + 'user' => [ + 'anonymousUserID' => 10, + ], + 'language' => [], + 'trash' => [], + 'io' => [], + 'objectState' => [], + 'search' => [], + 'urlAlias' => [], + 'urlWildcard' => [], + 'nameSchema' => [], + 'languages' => [], + 'proxy_factory' => [], + ]; + + if (!empty($this->serviceSettings['languages'])) { + $this->serviceSettings['language']['languages'] = $this->serviceSettings['languages']; + } + + $this->logger = $logger ?? new NullLogger(); + $this->contentMapper = $contentMapper; + $this->contentValidator = $contentValidator; + $this->passwordValidator = $passwordValidator; + $this->configResolver = $configResolver; + $this->nameSchemaService = $nameSchemaService; + } + + public function sudo(callable $callback, ?RepositoryInterface $outerRepository = null) + { + return $this->getPermissionResolver()->sudo($callback, $outerRepository ?? $this); + } + + /** + * Get Content Service. + * + * Get service object to perform operations on Content objects and it's aggregate members. + * + * @return \Ibexa\Contracts\Core\Repository\ContentService + */ + public function getContentService(): ContentServiceInterface + { + if ($this->contentService !== null) { + return $this->contentService; + } + + $this->contentService = new ContentService( + $this, + $this->persistenceHandler, + $this->contentDomainMapper, + $this->getRelationProcessor(), + $this->getNameSchemaService(), + $this->fieldTypeRegistry, + $this->getPermissionService(), + $this->contentMapper, + $this->contentValidator, + $this->contentFilteringHandler, + $this->serviceSettings['content'], + ); + + return $this->contentService; + } + + /** + * Get Content Language Service. + * + * Get service object to perform operations on Content language objects + * + * @return \Ibexa\Contracts\Core\Repository\LanguageService + */ + public function getContentLanguageService(): LanguageServiceInterface + { + if ($this->languageService !== null) { + return $this->languageService; + } + + $this->languageService = new LanguageService( + $this, + $this->persistenceHandler->contentLanguageHandler(), + $this->getPermissionResolver(), + $this->serviceSettings['language'] + ); + + return $this->languageService; + } + + /** + * Get content type Service. + * + * Get service object to perform operations on content type objects and it's aggregate members. + * ( Group, Field & FieldCategory ) + * + * @return \Ibexa\Contracts\Core\Repository\ContentTypeService + */ + public function getContentTypeService(): ContentTypeServiceInterface + { + if ($this->contentTypeService !== null) { + return $this->contentTypeService; + } + + $this->contentTypeService = new ContentTypeService( + $this, + $this->persistenceHandler->contentTypeHandler(), + $this->persistenceHandler->userHandler(), + $this->contentDomainMapper, + $this->contentTypeDomainMapper, + $this->fieldTypeRegistry, + $this->getPermissionResolver(), + $this->serviceSettings['contentType'] + ); + + return $this->contentTypeService; + } + + /** + * Get Content Location Service. + * + * Get service object to perform operations on Location objects and subtrees + * + * @return \Ibexa\Contracts\Core\Repository\LocationService + */ + public function getLocationService(): LocationServiceInterface + { + if ($this->locationService !== null) { + return $this->locationService; + } + + $this->locationService = new LocationService( + $this, + $this->persistenceHandler, + $this->contentDomainMapper, + $this->getNameSchemaService(), + $this->getPermissionCriterionResolver(), + $this->getPermissionResolver(), + $this->locationFilteringHandler, + $this->getContentTypeService(), + $this->serviceSettings['location'], + $this->logger + ); + + return $this->locationService; + } + + /** + * Get Content Trash service. + * + * Trash service allows to perform operations related to location trash + * (trash/untrash, load/list from trash...) + * + * @return \Ibexa\Contracts\Core\Repository\TrashService + */ + public function getTrashService(): TrashServiceInterface + { + if ($this->trashService !== null) { + return $this->trashService; + } + + $this->trashService = new TrashService( + $this, + $this->persistenceHandler, + $this->getNameSchemaService(), + $this->getPermissionCriterionResolver(), + $this->getPermissionResolver(), + $this->getProxyDomainMapper(), + $this->serviceSettings['trash'] + ); + + return $this->trashService; + } + + /** + * Get Content Section Service. + * + * Get Section service that lets you manipulate section objects + * + * @return \Ibexa\Contracts\Core\Repository\SectionService + */ + public function getSectionService(): SectionServiceInterface + { + if ($this->sectionService !== null) { + return $this->sectionService; + } + + $this->sectionService = new SectionService( + $this, + $this->persistenceHandler->sectionHandler(), + $this->persistenceHandler->locationHandler(), + $this->getPermissionCriterionResolver(), + $this->serviceSettings['section'] + ); + + return $this->sectionService; + } + + /** + * Get User Service. + * + * Get service object to perform operations on Users and UserGroup + * + * @return \Ibexa\Contracts\Core\Repository\UserService + */ + public function getUserService(): UserServiceInterface + { + if ($this->userService !== null) { + return $this->userService; + } + + $this->userService = new UserService( + $this, + $this->getPermissionResolver(), + $this->persistenceHandler->userHandler(), + $this->persistenceHandler->locationHandler(), + $this->passwordHashService, + $this->passwordValidator, + $this->configResolver, + $this->serviceSettings['user'] + ); + + return $this->userService; + } + + /** + * Get URLAliasService. + * + * @return \Ibexa\Contracts\Core\Repository\URLAliasService + */ + public function getURLAliasService(): URLAliasServiceInterface + { + if ($this->urlAliasService !== null) { + return $this->urlAliasService; + } + + $this->urlAliasService = new URLAliasService( + $this, + $this->persistenceHandler->urlAliasHandler(), + $this->getNameSchemaService(), + $this->getPermissionResolver(), + $this->languageResolver + ); + + return $this->urlAliasService; + } + + /** + * Get URLWildcardService. + * + * @return \Ibexa\Contracts\Core\Repository\URLWildcardService + */ + public function getURLWildcardService(): URLWildcardServiceInterface + { + if ($this->urlWildcardService !== null) { + return $this->urlWildcardService; + } + + $this->urlWildcardService = new URLWildcardService( + $this, + $this->persistenceHandler->urlWildcardHandler(), + $this->getPermissionResolver(), + $this->serviceSettings['urlWildcard'] + ); + + return $this->urlWildcardService; + } + + /** + * Get URLService. + * + * @return \Ibexa\Contracts\Core\Repository\URLService + */ + public function getURLService(): URLServiceInterface + { + if ($this->urlService !== null) { + return $this->urlService; + } + + $this->urlService = new URLService( + $this, + $this->persistenceHandler->urlHandler(), + $this->getPermissionResolver() + ); + + return $this->urlService; + } + + /** + * Get BookmarkService. + * + * @return \Ibexa\Contracts\Core\Repository\BookmarkService + */ + public function getBookmarkService(): BookmarkServiceInterface + { + if ($this->bookmarkService === null) { + $this->bookmarkService = new BookmarkService( + $this, + $this->persistenceHandler->bookmarkHandler() + ); + } + + return $this->bookmarkService; + } + + /** + * Get UserPreferenceService. + * + * @return \Ibexa\Contracts\Core\Repository\UserPreferenceService + */ + public function getUserPreferenceService(): UserPreferenceServiceInterface + { + if ($this->userPreferenceService === null) { + $this->userPreferenceService = new UserPreferenceService( + $this, + $this->persistenceHandler->userPreferenceHandler() + ); + } + + return $this->userPreferenceService; + } + + /** + * Get ObjectStateService. + * + * @return \Ibexa\Contracts\Core\Repository\ObjectStateService + */ + public function getObjectStateService(): ObjectStateServiceInterface + { + if ($this->objectStateService !== null) { + return $this->objectStateService; + } + + $this->objectStateService = new ObjectStateService( + $this, + $this->persistenceHandler->objectStateHandler(), + $this->getPermissionResolver(), + $this->serviceSettings['objectState'] + ); + + return $this->objectStateService; + } + + /** + * Get RoleService. + * + * @return \Ibexa\Contracts\Core\Repository\RoleService + */ + public function getRoleService(): RoleServiceInterface + { + if ($this->roleService !== null) { + return $this->roleService; + } + + $this->roleService = new RoleService( + $this, + $this->persistenceHandler->userHandler(), + $this->limitationService, + $this->getRoleDomainMapper(), + $this->serviceSettings['role'] + ); + + return $this->roleService; + } + + protected function getRoleDomainMapper(): Mapper\RoleDomainMapper + { + return $this->roleDomainMapper; + } + + /** + * Get SearchService. + * + * @return \Ibexa\Contracts\Core\Repository\SearchService + */ + public function getSearchService(): SearchServiceInterface + { + if ($this->searchService !== null) { + return $this->searchService; + } + + $this->searchService = new SearchService( + $this, + $this->searchHandler, + $this->contentDomainMapper, + $this->getPermissionCriterionResolver(), + $this->backgroundIndexer, + $this->serviceSettings['search'] + ); + + return $this->searchService; + } + + /** + * Get FieldTypeService. + * + * @return \Ibexa\Contracts\Core\Repository\FieldTypeService + */ + public function getFieldTypeService(): FieldTypeServiceInterface + { + if ($this->fieldTypeService !== null) { + return $this->fieldTypeService; + } + + $this->fieldTypeService = new FieldTypeService($this->fieldTypeRegistry); + + return $this->fieldTypeService; + } + + public function getPermissionService(): PermissionService + { + return $this->permissionService; + } + + public function getPermissionResolver(): PermissionResolverInterface + { + return $this->getPermissionService(); + } + + /** + * @internal + * @private + */ + public function getNameSchemaService(): NameSchemaServiceInterface + { + return $this->nameSchemaService; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\NotificationService + */ + public function getNotificationService(): NotificationServiceInterface + { + if (null !== $this->notificationService) { + return $this->notificationService; + } + + $this->notificationService = new NotificationService( + $this->persistenceHandler->notificationHandler(), + $this->getPermissionResolver() + ); + + return $this->notificationService; + } + + /** + * Get RelationProcessor. + * + * + * @todo Move out from this & other repo instances when services becomes proper services in DIC terms using factory. + * + * @return \Ibexa\Core\Repository\Helper\RelationProcessor + */ + protected function getRelationProcessor(): RelationProcessor + { + return $this->relationProcessor; + } + + protected function getProxyDomainMapper(): ProxyDomainMapperInterface + { + if ($this->proxyDomainMapper !== null) { + return $this->proxyDomainMapper; + } + + $this->proxyDomainMapper = $this->proxyDomainMapperFactory->create($this); + + return $this->proxyDomainMapper; + } + + protected function getPermissionCriterionResolver(): PermissionCriterionResolverInterface + { + return $this->getPermissionService(); + } + + /** + * Begin transaction. + * + * Begins an transaction, make sure you'll call commit or rollback when done, + * otherwise work will be lost. + */ + public function beginTransaction(): void + { + $this->persistenceHandler->beginTransaction(); + } + + /** + * Commit transaction. + * + * Commit transaction, or throw exceptions if no transactions has been started. + * + * @throws \RuntimeException If no transaction has been started + */ + public function commit(): void + { + try { + $this->persistenceHandler->commit(); + } catch (Exception $e) { + throw new RuntimeException($e->getMessage(), 0, $e); + } + } + + /** + * Rollback transaction. + * + * Rollback transaction, or throw exceptions if no transactions has been started. + * + * @throws \RuntimeException If no transaction has been started + */ + public function rollback(): void + { + try { + $this->persistenceHandler->rollback(); + } catch (Exception $e) { + throw new RuntimeException($e->getMessage(), 0, $e); + } + } +} + +class_alias(Repository::class, 'eZ\Publish\Core\Repository\Repository'); diff --git a/src/lib/Repository/RoleService.php b/src/lib/Repository/RoleService.php new file mode 100644 index 0000000000..b57f1fda30 --- /dev/null +++ b/src/lib/Repository/RoleService.php @@ -0,0 +1,1299 @@ +repository = $repository; + $this->userHandler = $userHandler; + $this->limitationService = $limitationService; + $this->roleDomainMapper = $roleDomainMapper; + $this->settings = $settings; + $this->permissionResolver = $repository->getPermissionResolver(); + } + + /** + * Creates a new RoleDraft. + * + * @since 6.0 + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct $roleCreateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the name of the role already exists or if limitation of the same type + * is repeated in the policy create struct or if limitation is not allowed on module/function + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a RoleDraft + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException if a policy limitation in the $roleCreateStruct is not valid + */ + public function createRole(APIRoleCreateStruct $roleCreateStruct): APIRoleDraft + { + if (!is_string($roleCreateStruct->identifier) || empty($roleCreateStruct->identifier)) { + throw new InvalidArgumentValue('identifier', $roleCreateStruct->identifier, 'RoleCreateStruct'); + } + + if (!$this->permissionResolver->canUser('role', 'create', $roleCreateStruct)) { + throw new UnauthorizedException('role', 'create'); + } + + try { + $existingRole = $this->loadRoleByIdentifier($roleCreateStruct->identifier); + + throw new InvalidArgumentException( + '$roleCreateStruct', + "A Role '{$existingRole->id}' with identifier '{$roleCreateStruct->identifier}' " . + 'already exists' + ); + } catch (APINotFoundException $e) { + // Do nothing + } + + $limitationValidationErrors = $this->validateRoleCreateStruct($roleCreateStruct); + if (!empty($limitationValidationErrors)) { + throw new LimitationValidationException($limitationValidationErrors); + } + + $spiRoleCreateStruct = $this->roleDomainMapper->buildPersistenceRoleCreateStruct($roleCreateStruct); + + $this->repository->beginTransaction(); + try { + $spiRole = $this->userHandler->createRole($spiRoleCreateStruct); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole); + } + + /** + * Creates a new RoleDraft for an existing Role. + * + * @since 6.0 + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Role $role + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the Role already has a RoleDraft that will need to be removed first + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a RoleDraft + */ + public function createRoleDraft(APIRole $role): APIRoleDraft + { + if (!$this->permissionResolver->canUser('role', 'create', $role)) { + throw new UnauthorizedException('role', 'create'); + } + + try { + $this->userHandler->loadRole($role->id, Role::STATUS_DRAFT); + + // Throw exception, so platformui et al can do conflict management. Follow-up: EZP-24719 + throw new InvalidArgumentException( + '$role', + "Cannot create a draft for Role '{$role->identifier}' because another draft exists" + ); + } catch (APINotFoundException $e) { + $this->repository->beginTransaction(); + try { + $spiRole = $this->userHandler->createRoleDraft($role->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + return $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole); + } + + public function copyRole(APIRole $role, APIRoleCopyStruct $roleCopyStruct): APIRole + { + if (!is_string($roleCopyStruct->newIdentifier) || empty($roleCopyStruct->newIdentifier)) { + throw new InvalidArgumentValue('newIdentifier', $roleCopyStruct->newIdentifier, 'RoleCopyStruct'); + } + + if (!$this->permissionResolver->canUser('role', 'create', $roleCopyStruct)) { + throw new UnauthorizedException('role', 'create'); + } + + try { + $existingRole = $this->loadRoleByIdentifier($roleCopyStruct->newIdentifier); + + throw new InvalidArgumentException( + '$roleCopyStruct', + "Role '{$existingRole->id}' with the specified identifier '{$roleCopyStruct->newIdentifier}' " . + 'already exists' + ); + } catch (APINotFoundException $e) { + // Do nothing + } + + foreach ($role->getPolicies() as $policy) { + $policyCreateStruct = new PolicyCreateStruct([ + 'module' => $policy->module, + 'function' => $policy->function, + ]); + foreach ($policy->getLimitations() as $limitation) { + $policyCreateStruct->addLimitation($limitation); + } + $roleCopyStruct->addPolicy($policyCreateStruct); + } + + $limitationValidationErrors = $this->validateRoleCreateStruct($roleCopyStruct); + if (!empty($limitationValidationErrors)) { + throw new LimitationValidationException($limitationValidationErrors); + } + + $spiRoleCopyStruct = $this->roleDomainMapper->buildPersistenceRoleCopyStruct( + $roleCopyStruct, + $role->id, + $role->getStatus() + ); + + $this->repository->beginTransaction(); + try { + $spiRole = $this->userHandler->copyRole($spiRoleCopyStruct); + $this->repository->commit(); + } catch (\Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadRole($spiRole->id); + } + + /** + * Loads a RoleDraft for the given id. + * + * @since 6.0 + * + * @param int $id + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if a RoleDraft with the given id was not found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a RoleDraft + */ + public function loadRoleDraft(int $id): APIRoleDraft + { + $spiRole = $this->userHandler->loadRole($id, Role::STATUS_DRAFT); + + $role = $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole); + + if (!$this->permissionResolver->canUser('role', 'read', $role)) { + throw new UnauthorizedException('role', 'read'); + } + + return $role; + } + + /** + * Loads a RoleDraft by the ID of the role it was created from. + * + * @param int $roleId ID of the role the draft was created from. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if a RoleDraft with the given id was not found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role + */ + public function loadRoleDraftByRoleId(int $roleId): APIRoleDraft + { + $spiRole = $this->userHandler->loadRoleDraftByRoleId($roleId); + + $role = $this->roleDomainMapper->buildDomainRoleDraftObject($spiRole); + + if (!$this->permissionResolver->canUser('role', 'read', $role)) { + throw new UnauthorizedException('role', 'read'); + } + + return $role; + } + + /** + * Updates the properties of a RoleDraft. + * + * @since 6.0 + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft $roleDraft + * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleUpdateStruct $roleUpdateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the identifier of the RoleDraft already exists + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update a RoleDraft + */ + public function updateRoleDraft(APIRoleDraft $roleDraft, RoleUpdateStruct $roleUpdateStruct): APIRoleDraft + { + if ($roleUpdateStruct->identifier !== null && !is_string($roleUpdateStruct->identifier)) { + throw new InvalidArgumentValue('identifier', $roleUpdateStruct->identifier, 'RoleUpdateStruct'); + } + + $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id); + + if (!$this->permissionResolver->canUser('role', 'update', $loadedRoleDraft)) { + throw new UnauthorizedException('role', 'update'); + } + + if ($roleUpdateStruct->identifier !== null) { + try { + /* Throw exception if: + * - A published role with the same identifier exists, AND + * - The ID of the published role does not match the original ID of the draft + */ + $existingSPIRole = $this->userHandler->loadRoleByIdentifier($roleUpdateStruct->identifier); + $SPIRoleDraft = $this->userHandler->loadRole($loadedRoleDraft->id, Role::STATUS_DRAFT); + if ($existingSPIRole->id != $SPIRoleDraft->originalId) { + throw new InvalidArgumentException( + '$roleUpdateStruct', + "A Role '{$existingSPIRole->id}' with identifier '{$roleUpdateStruct->identifier}' " . + 'already exists' + ); + } + } catch (APINotFoundException $e) { + // Do nothing + } + } + + $this->repository->beginTransaction(); + try { + $this->userHandler->updateRole( + new SPIRoleUpdateStruct( + [ + 'id' => $loadedRoleDraft->id, + 'identifier' => $roleUpdateStruct->identifier ?: $loadedRoleDraft->identifier, + ] + ) + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadRoleDraft($loadedRoleDraft->id); + } + + /** + * Adds a new policy to the RoleDraft. + * + * @since 6.0 + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft $roleDraft + * @param \Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct $policyCreateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if limitation of the same type is repeated in policy create + * struct or if limitation is not allowed on module/function + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException if a limitation in the $policyCreateStruct is not valid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to add a policy + */ + public function addPolicyByRoleDraft(APIRoleDraft $roleDraft, APIPolicyCreateStruct $policyCreateStruct): APIRoleDraft + { + if (!is_string($policyCreateStruct->module) || empty($policyCreateStruct->module)) { + throw new InvalidArgumentValue('module', $policyCreateStruct->module, 'PolicyCreateStruct'); + } + + if (!is_string($policyCreateStruct->function) || empty($policyCreateStruct->function)) { + throw new InvalidArgumentValue('function', $policyCreateStruct->function, 'PolicyCreateStruct'); + } + + if ($policyCreateStruct->module === '*' && $policyCreateStruct->function !== '*') { + throw new InvalidArgumentValue('module', $policyCreateStruct->module, 'PolicyCreateStruct'); + } + + if (!$this->permissionResolver->canUser('role', 'update', $roleDraft)) { + throw new UnauthorizedException('role', 'update'); + } + + $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id); + + $limitations = $policyCreateStruct->getLimitations(); + $limitationValidationErrors = $this->validatePolicy( + $policyCreateStruct->module, + $policyCreateStruct->function, + $limitations + ); + if (!empty($limitationValidationErrors)) { + throw new LimitationValidationException($limitationValidationErrors); + } + + $spiPolicy = $this->roleDomainMapper->buildPersistencePolicyObject( + $policyCreateStruct->module, + $policyCreateStruct->function, + $limitations + ); + + $this->repository->beginTransaction(); + try { + $this->userHandler->addPolicyByRoleDraft($loadedRoleDraft->id, $spiPolicy); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadRoleDraft($loadedRoleDraft->id); + } + + /** + * Removes a policy from a RoleDraft. + * + * @since 6.0 + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft $roleDraft + * @param \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policyDraft the policy to remove from the RoleDraft + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if policy does not belong to the given RoleDraft + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a policy + */ + public function removePolicyByRoleDraft(APIRoleDraft $roleDraft, PolicyDraft $policyDraft): APIRoleDraft + { + if (!$this->permissionResolver->canUser('role', 'update', $roleDraft)) { + throw new UnauthorizedException('role', 'update'); + } + + if ($policyDraft->roleId != $roleDraft->id) { + throw new InvalidArgumentException('$policy', 'The Policy does not belong to the given Role'); + } + + $this->internalDeletePolicy($policyDraft); + + return $this->loadRoleDraft($roleDraft->id); + } + + /** + * Updates the limitations of a policy. The module and function cannot be changed and + * the limitations are replaced by the ones in $roleUpdateStruct. + * + * @since 6.0 + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft $roleDraft + * @param \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy + * @param \Ibexa\Contracts\Core\Repository\Values\User\PolicyUpdateStruct $policyUpdateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if limitation of the same type is repeated in policy update + * struct or if limitation is not allowed on module/function + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException if a limitation in the $policyUpdateStruct is not valid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update a policy + */ + public function updatePolicyByRoleDraft( + APIRoleDraft $roleDraft, + PolicyDraft $policy, + APIPolicyUpdateStruct $policyUpdateStruct + ): PolicyDraft { + if (!is_string($policy->module)) { + throw new InvalidArgumentValue('module', $policy->module, 'Policy'); + } + + if (!is_string($policy->function)) { + throw new InvalidArgumentValue('function', $policy->function, 'Policy'); + } + + if (!$this->permissionResolver->canUser('role', 'update', $roleDraft)) { + throw new UnauthorizedException('role', 'update'); + } + + if ($policy->roleId !== $roleDraft->id) { + throw new InvalidArgumentException('$policy', 'does not belong to the provided role draft'); + } + + $limitations = $policyUpdateStruct->getLimitations(); + $limitationValidationErrors = $this->validatePolicy( + $policy->module, + $policy->function, + $limitations + ); + if (!empty($limitationValidationErrors)) { + throw new LimitationValidationException($limitationValidationErrors); + } + + $spiPolicy = $this->roleDomainMapper->buildPersistencePolicyObject( + $policy->module, + $policy->function, + $limitations + ); + $spiPolicy->id = $policy->id; + $spiPolicy->roleId = $policy->roleId; + $spiPolicy->originalId = $policy->originalId; + + $this->repository->beginTransaction(); + try { + $this->userHandler->updatePolicy($spiPolicy); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->roleDomainMapper->buildDomainPolicyObject($spiPolicy); + } + + /** + * Deletes the given RoleDraft. + * + * @since 6.0 + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft $roleDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete this RoleDraft + */ + public function deleteRoleDraft(APIRoleDraft $roleDraft): void + { + $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id); + + $this->repository->beginTransaction(); + try { + $this->userHandler->deleteRole($loadedRoleDraft->id, Role::STATUS_DRAFT); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Publishes a given RoleDraft. + * + * @since 6.0 + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleDraft $roleDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException if the role draft cannot be loaded + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to publish this RoleDraft + */ + public function publishRoleDraft(APIRoleDraft $roleDraft): void + { + if (!$this->permissionResolver->canUser('role', 'update', $roleDraft)) { + throw new UnauthorizedException('role', 'update'); + } + + try { + $loadedRoleDraft = $this->loadRoleDraft($roleDraft->id); + } catch (APINotFoundException $e) { + throw new BadStateException( + '$roleDraft', + 'The Role does not have a draft.', + $e + ); + } + + $this->repository->beginTransaction(); + try { + $this->userHandler->publishRoleDraft($loadedRoleDraft->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Loads a role for the given id. + * + * @param int $id + * + * @return \Ibexa\Core\Repository\Values\User\Role + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if a role with the given id was not found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role + */ + public function loadRole(int $id): APIRole + { + $spiRole = $this->userHandler->loadRole($id); + + $role = $this->roleDomainMapper->buildDomainRoleObject($spiRole); + + if (!$this->permissionResolver->canUser('role', 'read', $role)) { + throw new UnauthorizedException('role', 'read'); + } + + return $role; + } + + /** + * Loads a role for the given identifier. + * + * @param string $identifier + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Role + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if a role with the given name was not found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role + */ + public function loadRoleByIdentifier(string $identifier): APIRole + { + $spiRole = $this->userHandler->loadRoleByIdentifier($identifier); + + $role = $this->roleDomainMapper->buildDomainRoleObject($spiRole); + + if (!$this->permissionResolver->canUser('role', 'read', $role)) { + throw new UnauthorizedException('role', 'read'); + } + + return $role; + } + + /** + * Loads all roles, excluding the ones the current user is not allowed to read. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Role[] + */ + public function loadRoles(): iterable + { + $roles = array_map( + function ($spiRole) { + return $this->roleDomainMapper->buildDomainRoleObject($spiRole); + }, + $this->userHandler->loadRoles() + ); + + return array_values( + array_filter( + $roles, + function ($role) { + return $this->permissionResolver->canUser('role', 'read', $role); + } + ) + ); + } + + /** + * Deletes the given role. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Role $role + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete this role + */ + public function deleteRole(APIRole $role): void + { + if (!$this->permissionResolver->canUser('role', 'delete', $role)) { + throw new UnauthorizedException('role', 'delete'); + } + + $loadedRole = $this->loadRole($role->id); + + $this->repository->beginTransaction(); + try { + $this->userHandler->deleteRole($loadedRole->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Assigns a role to the given user group. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Role $role + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroup + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation|null $roleLimitation an optional role limitation (which is either a subtree limitation or section limitation) + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If assignment already exists + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign a role + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException if $roleLimitation is not valid + */ + public function assignRoleToUserGroup(APIRole $role, UserGroup $userGroup, RoleLimitation $roleLimitation = null): void + { + if ($this->permissionResolver->canUser('role', 'assign', $userGroup, [$role]) !== true) { + throw new UnauthorizedException('role', 'assign'); + } + + if ($roleLimitation === null) { + $limitation = null; + } else { + $limitationValidationErrors = $this->limitationService->validateLimitation($roleLimitation); + if (!empty($limitationValidationErrors)) { + throw new LimitationValidationException($limitationValidationErrors); + } + + $limitation = [$roleLimitation->getIdentifier() => $roleLimitation->limitationValues]; + } + + // Check if objects exists + $spiRole = $this->userHandler->loadRole($role->id); + $loadedUserGroup = $this->repository->getUserService()->loadUserGroup($userGroup->id); + + $limitation = $this->checkAssignmentAndFilterLimitationValues($loadedUserGroup->id, $spiRole, $limitation); + + $this->repository->beginTransaction(); + try { + $this->userHandler->assignRole( + $loadedUserGroup->id, + $spiRole->id, + $limitation + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Assigns a role to the given user. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Role $role + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation|null $roleLimitation an optional role limitation (which is either a subtree limitation or section limitation) + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If assignment already exists + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException if $roleLimitation is not valid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign a role + */ + public function assignRoleToUser(APIRole $role, User $user, RoleLimitation $roleLimitation = null): void + { + if ($this->permissionResolver->canUser('role', 'assign', $user, [$role]) !== true) { + throw new UnauthorizedException('role', 'assign'); + } + + if ($roleLimitation === null) { + $limitation = null; + } else { + $limitationValidationErrors = $this->limitationService->validateLimitation($roleLimitation); + if (!empty($limitationValidationErrors)) { + throw new LimitationValidationException($limitationValidationErrors); + } + + $limitation = [$roleLimitation->getIdentifier() => $roleLimitation->limitationValues]; + } + + // Check if objects exists + $spiRole = $this->userHandler->loadRole($role->id); + $spiUser = $this->userHandler->load($user->id); + + $limitation = $this->checkAssignmentAndFilterLimitationValues($spiUser->id, $spiRole, $limitation); + + $this->repository->beginTransaction(); + try { + $this->userHandler->assignRole( + $spiUser->id, + $spiRole->id, + $limitation + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Removes the given role assignment. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleAssignment $roleAssignment + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove a role assignment + */ + public function removeRoleAssignment(RoleAssignment $roleAssignment): void + { + if ($this->permissionResolver->canUser('role', 'assign', $roleAssignment) !== true) { + throw new UnauthorizedException('role', 'assign'); + } + + $spiRoleAssignment = $this->userHandler->loadRoleAssignment($roleAssignment->id); + + $this->repository->beginTransaction(); + try { + $this->userHandler->removeRoleAssignment($spiRoleAssignment->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Loads a role assignment for the given id. + * + * @param int $roleAssignmentId + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleAssignment + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If the role assignment was not found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read this role + */ + public function loadRoleAssignment(int $roleAssignmentId): RoleAssignment + { + $spiRoleAssignment = $this->userHandler->loadRoleAssignment($roleAssignmentId); + $userService = $this->repository->getUserService(); + $role = $this->loadRole($spiRoleAssignment->roleId); + + if (!$this->permissionResolver->canUser('role', 'read', $role)) { + throw new UnauthorizedException('role', 'read'); + } + + $roleAssignment = null; + + // First check if the Role is assigned to a User + // If no User is found, see if it belongs to a UserGroup + try { + $user = $userService->loadUser($spiRoleAssignment->contentId); + $roleAssignment = $this->roleDomainMapper->buildDomainUserRoleAssignmentObject( + $spiRoleAssignment, + $user, + $role + ); + } catch (APINotFoundException $e) { + try { + $userGroup = $userService->loadUserGroup($spiRoleAssignment->contentId); + $roleAssignment = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject( + $spiRoleAssignment, + $userGroup, + $role + ); + } catch (APINotFoundException $e) { + // Do nothing + } + } + + return $roleAssignment; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleAssignment[] + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read a role + */ + public function getRoleAssignments(APIRole $role): iterable + { + if (!$this->permissionResolver->canUser('role', 'read', $role)) { + throw new UnauthorizedException('role', 'read'); + } + + $persistenceRoleAssignments = $this->userHandler->loadRoleAssignmentsByRoleId($role->id); + + return $this->buildRoleAssignmentsFromPersistence($role, $persistenceRoleAssignments); + } + + public function loadRoleAssignments(APIRole $role, int $offset = 0, ?int $limit = null): iterable + { + if (!$this->permissionResolver->canUser('role', 'read', $role)) { + throw new UnauthorizedException('role', 'read'); + } + + $persistenceRoleAssignments = $this->userHandler->loadRoleAssignmentsByRoleIdWithOffsetAndLimit( + $role->id, + $offset, + $limit + ); + + return $this->buildRoleAssignmentsFromPersistence($role, $persistenceRoleAssignments); + } + + /** + * @param array $persistenceRoleAssignments + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function buildRoleAssignmentsFromPersistence( + APIRole $role, + array $persistenceRoleAssignments + ): array { + $userService = $this->repository->getUserService(); + + $roleAssignments = []; + foreach ($persistenceRoleAssignments as $persistenceRoleAssignment) { + // First check if the Role is assigned to a User + // If no User is found, see if it belongs to a UserGroup + try { + $user = $userService->loadUser($persistenceRoleAssignment->contentId); + $roleAssignments[] = $this->roleDomainMapper->buildDomainUserRoleAssignmentObject( + $persistenceRoleAssignment, + $user, + $role + ); + } catch (APINotFoundException $e) { + try { + $userGroup = $userService->loadUserGroup($persistenceRoleAssignment->contentId); + $roleAssignments[] = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject( + $persistenceRoleAssignment, + $userGroup, + $role + ); + } catch (APINotFoundException $e) { + // Do nothing + } + } + } + + return $roleAssignments; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read a role + */ + public function countRoleAssignments(APIRole $role): int + { + if (!$this->permissionResolver->canUser('role', 'read', $role)) { + throw new UnauthorizedException('role', 'read'); + } + + // Skipping building domain user role assignment object as done in `buildRoleAssignmentsFromPersistence` + // due to inner joining user content which is sufficient in this case + return $this->userHandler->countRoleAssignments($role->id); + } + + /** + * @see \Ibexa\Contracts\Core\Repository\RoleService::getRoleAssignmentsForUser() + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * @param bool $inherited + * + * @return iterable + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function getRoleAssignmentsForUser(User $user, bool $inherited = false): iterable + { + $roleAssignments = []; + $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($user->id, $inherited); + foreach ($spiRoleAssignments as $spiRoleAssignment) { + $role = $this->loadRole($spiRoleAssignment->roleId); + + if (!$this->permissionResolver->canUser('role', 'read', $role)) { + continue; + } + + if (!$inherited || $spiRoleAssignment->contentId == $user->id) { + $roleAssignments[] = $this->roleDomainMapper->buildDomainUserRoleAssignmentObject( + $spiRoleAssignment, + $user, + $role + ); + } else { + $userGroup = $this->repository->getUserService()->loadUserGroup($spiRoleAssignment->contentId); + $roleAssignments[] = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject( + $spiRoleAssignment, + $userGroup, + $role + ); + } + } + + return $roleAssignments; + } + + /** + * Returns the roles assigned to the given user group, excluding the ones the current user is not allowed to read. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroup + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroupRoleAssignment[] + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function getRoleAssignmentsForUserGroup(UserGroup $userGroup): iterable + { + $roleAssignments = []; + $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($userGroup->id); + foreach ($spiRoleAssignments as $spiRoleAssignment) { + $role = $this->loadRole($spiRoleAssignment->roleId); + + if ($this->permissionResolver->canUser('role', 'read', $role)) { + $roleAssignments[] = $this->roleDomainMapper->buildDomainUserGroupRoleAssignmentObject( + $spiRoleAssignment, + $userGroup, + $role + ); + } + } + + return $roleAssignments; + } + + /** + * Instantiates a role create class. + * + * @param string $name + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct + */ + public function newRoleCreateStruct(string $name): APIRoleCreateStruct + { + return new RoleCreateStruct( + [ + 'identifier' => $name, + 'policies' => [], + ] + ); + } + + public function newRoleCopyStruct(string $name): APIRoleCopyStruct + { + return new RoleCopyStruct( + [ + 'newIdentifier' => $name, + 'policies' => [], + ] + ); + } + + /** + * Instantiates a policy create class. + * + * @param string $module + * @param string $function + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct + */ + public function newPolicyCreateStruct(string $module, string $function): APIPolicyCreateStruct + { + return new PolicyCreateStruct( + [ + 'module' => $module, + 'function' => $function, + 'limitations' => [], + ] + ); + } + + /** + * Instantiates a policy update class. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\PolicyUpdateStruct + */ + public function newPolicyUpdateStruct(): APIPolicyUpdateStruct + { + return new PolicyUpdateStruct( + [ + 'limitations' => [], + ] + ); + } + + /** + * Instantiates a policy update class. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleUpdateStruct + */ + public function newRoleUpdateStruct(): RoleUpdateStruct + { + return new RoleUpdateStruct(); + } + + /** + * Returns the LimitationType registered with the given identifier. + * + * Returns the correct implementation of API Limitation value object + * based on provided identifier + * + * @param string $identifier + * + * @return \Ibexa\Contracts\Core\Limitation\Type + * + * @throws \RuntimeException if there is no LimitationType with $identifier + */ + public function getLimitationType(string $identifier): Type + { + return $this->limitationService->getLimitationType($identifier); + } + + /** + * Returns the LimitationType's assigned to a given module/function. + * + * Typically used for: + * - Internal validation limitation value use on Policies + * - Role admin gui for editing policy limitations incl list limitation options via valueSchema() + * + * @param string $module Legacy name of "controller", it's a unique identifier like "content" + * @param string $function Legacy name of a controller "action", it's a unique within the controller like "read" + * + * @return \Ibexa\Contracts\Core\Limitation\Type[] + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If module/function to limitation type mapping + * refers to a non existing identifier. + */ + public function getLimitationTypesByModuleFunction(string $module, string $function): iterable + { + if (empty($this->settings['policyMap'][$module][$function])) { + return []; + } + + $types = []; + try { + foreach (array_keys($this->settings['policyMap'][$module][$function]) as $identifier) { + $types[$identifier] = $this->limitationService->getLimitationType($identifier); + } + } catch (LimitationNotFoundException $e) { + throw new BadStateException( + "{$module}/{$function}", + "policyMap configuration is referring to a non-existent identifier: {$identifier}", + $e + ); + } + + return $types; + } + + /** + * Validates Policies and Limitations in Role create struct. + * + * @uses ::validatePolicy() + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct $roleCreateStruct + * + * @return \Ibexa\Core\FieldType\ValidationError[][][] + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + protected function validateRoleCreateStruct(APIRoleCreateStruct $roleCreateStruct): iterable + { + $allErrors = []; + foreach ($roleCreateStruct->getPolicies() as $key => $policyCreateStruct) { + $errors = $this->validatePolicy( + $policyCreateStruct->module, + $policyCreateStruct->function, + $policyCreateStruct->getLimitations() + ); + + if (!empty($errors)) { + $allErrors[$key] = $errors; + } + } + + return $allErrors; + } + + /** + * Validates Policy context: Limitations on a module and function. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the same limitation is repeated or if + * limitation is not allowed on module/function + * + * @param string $module + * @param string $function + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation[] $limitations + * + * @return \Ibexa\Core\FieldType\ValidationError[][] + */ + protected function validatePolicy(string $module, string $function, array $limitations): iterable + { + if ($module !== '*' && $function !== '*' && !empty($limitations)) { + $limitationSet = []; + foreach ($limitations as $limitation) { + if (isset($limitationSet[$limitation->getIdentifier()])) { + throw new InvalidArgumentException( + 'limitations', + "{$limitation->getIdentifier()}' was found multiple times among Limitations" + ); + } + + if (!isset($this->settings['policyMap'][$module][$function][$limitation->getIdentifier()])) { + throw new InvalidArgumentException( + 'policy', + "Limitation '{$limitation->getIdentifier()}' is not applicable on '{$module}/{$function}'" + ); + } + + $limitationSet[$limitation->getIdentifier()] = true; + } + } + + return $this->limitationService->validateLimitations($limitations); + } + + /** + * Validate that assignments not already exists and filter validations against existing. + * + * @param int $contentId + * @param \Ibexa\Contracts\Core\Persistence\User\Role $spiRole + * @param array|null $limitation + * + * @return array[]|null Filtered version of $limitation + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If assignment already exists + */ + protected function checkAssignmentAndFilterLimitationValues( + int $contentId, + SPIRole $spiRole, + ?array $limitation = null + ): ?array { + $spiRoleAssignments = $this->userHandler->loadRoleAssignmentsByGroupId($contentId); + foreach ($spiRoleAssignments as $spiAssignment) { + // Ignore assignments to other roles + if ($spiAssignment->roleId !== $spiRole->id) { + continue; + } + + // Throw if Role is already assigned without limitations + if ($spiAssignment->limitationIdentifier === null) { + throw new InvalidArgumentException( + '$role', + "Role '{$spiRole->id}' already assigned without Limitations" + ); + } + + // Ignore if we are going to assign without limitations + if ($limitation === null) { + continue; + } + + // Ignore if not assigned with same limitation identifier + if (!isset($limitation[$spiAssignment->limitationIdentifier])) { + continue; + } + + // Throw if Role is already assigned with all the same limitations + $newValues = array_diff($limitation[$spiAssignment->limitationIdentifier], $spiAssignment->values); + if (empty($newValues)) { + throw new InvalidArgumentException( + '$role', + "Role '{$spiRole->id}' is already assigned with the same '{$spiAssignment->limitationIdentifier}' value" + ); + } + + // Continue using the filtered list of limitations + $limitation[$spiAssignment->limitationIdentifier] = $newValues; + } + + return $limitation; + } + + /** + * Deletes a policy. + * + * Used by {@link removePolicy()} and {@link deletePolicy()} + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Policy $policy + * + * @throws \Exception + */ + protected function internalDeletePolicy(APIPolicy $policy): void + { + $this->repository->beginTransaction(); + try { + $this->userHandler->deletePolicy($policy->id, $policy->roleId); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } +} + +class_alias(RoleService::class, 'eZ\Publish\Core\Repository\RoleService'); diff --git a/src/lib/Repository/SearchService.php b/src/lib/Repository/SearchService.php new file mode 100644 index 0000000000..a296a7710a --- /dev/null +++ b/src/lib/Repository/SearchService.php @@ -0,0 +1,355 @@ +repository = $repository; + $this->searchHandler = $searchHandler; + $this->contentDomainMapper = $contentDomainMapper; + // Union makes sure default settings are ignored if provided in argument + $this->settings = $settings + [ + //'defaultSetting' => array(), + ]; + $this->permissionCriterionResolver = $permissionCriterionResolver; + $this->backgroundIndexer = $backgroundIndexer; + } + + /** + * Finds content objects for the given query. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query + * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. + * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations. + * @param bool $filterOnUserPermissions if true only the objects which the user is allowed to read are returned. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + */ + public function findContent(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult + { + $result = $this->internalFindContentInfo($query, $languageFilter, $filterOnUserPermissions); + $missingContentList = $this->contentDomainMapper->buildContentDomainObjectsOnSearchResult($result, $languageFilter); + foreach ($missingContentList as $missingContent) { + $this->backgroundIndexer->registerContent($missingContent); + } + + return $result; + } + + /** + * Finds contentInfo objects for the given query. + * + * @see \Ibexa\Contracts\Core\Repository\SearchService::findContentInfo() + * @since 5.4.5 + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query + * @param array $languageFilter - a map of filters for the returned fields. + * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations. + * @param bool $filterOnUserPermissions if true (default) only the objects which is the user allowed to read are returned. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + */ + public function findContentInfo(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult + { + $result = $this->internalFindContentInfo($query, $languageFilter, $filterOnUserPermissions); + foreach ($result->searchHits as $hit) { + $hit->valueObject = $this->contentDomainMapper->buildContentInfoDomainObject( + $hit->valueObject + ); + } + + return $result; + } + + /** + * Finds SPI content info objects for the given query. + * + * Internal for use by {@link findContent} and {@link findContentInfo}. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query + * @param array $languageFilter - a map of filters for the returned fields. + * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations. + * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult With "raw" SPI contentInfo objects in result + */ + protected function internalFindContentInfo(Query $query, array $languageFilter = [], $filterOnUserPermissions = true) + { + if (!is_int($query->offset)) { + throw new InvalidArgumentType( + '$query->offset', + 'integer', + $query->offset + ); + } + + if (!is_int($query->limit)) { + throw new InvalidArgumentType( + '$query->limit', + 'integer', + $query->limit + ); + } + + $query = clone $query; + $query->filter = $query->filter ?: new Criterion\MatchAll(); + + $this->validateContentCriteria([$query->query], '$query'); + $this->validateContentCriteria([$query->filter], '$query'); + $this->validateContentSortClauses($query); + + if ($filterOnUserPermissions && !$this->addPermissionsCriterion($query->filter)) { + return new SearchResult(['time' => 0, 'totalCount' => 0]); + } + + return $this->searchHandler->findContent($query, $languageFilter); + } + + /** + * Checks that $criteria does not contain Location criterions. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion[] $criteria + * @param string $argumentName + */ + protected function validateContentCriteria(array $criteria, $argumentName) + { + foreach ($criteria as $criterion) { + if ($criterion instanceof LocationCriterion) { + throw new InvalidArgumentException( + $argumentName, + 'Location Criteria cannot be used in Content search' + ); + } + if ($criterion instanceof LogicalOperator) { + $this->validateContentCriteria($criterion->criteria, $argumentName); + } + } + } + + /** + * Checks that $query does not contain Location sort clauses. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query + */ + protected function validateContentSortClauses(Query $query) + { + foreach ($query->sortClauses as $sortClause) { + if ($sortClause instanceof LocationSortClause) { + throw new InvalidArgumentException('$query', 'Location Sort Clauses cannot be used in Content search'); + } + } + } + + /** + * Performs a query for a single content object. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if criterion is not valid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is more than one result matching the criterions + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $filter + * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. + * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations. + * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function findSingle(Criterion $filter, array $languageFilter = [], bool $filterOnUserPermissions = true): Content + { + $this->validateContentCriteria([$filter], '$filter'); + + if ($filterOnUserPermissions && !$this->addPermissionsCriterion($filter)) { + throw new NotFoundException('Content', '*'); + } + + $contentInfo = $this->searchHandler->findSingle($filter, $languageFilter); + + return $this->repository->getContentService()->internalLoadContentById( + $contentInfo->id, + (!empty($languageFilter['languages']) ? $languageFilter['languages'] : null), + null, + (isset($languageFilter['useAlwaysAvailable']) ? $languageFilter['useAlwaysAvailable'] : true) + ); + } + + /** + * Suggests a list of values for the given prefix. + * + * @param string $prefix + * @param string[] $fieldPaths + * @param int $limit + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion|null $filter + */ + public function suggest(string $prefix, array $fieldPaths = [], int $limit = 10, Criterion $filter = null) + { + } + + /** + * Finds Locations for the given query. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if query is not valid + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query + * @param array $languageFilter Configuration for specifying prioritized languages query will be performed on. + * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * @param bool $filterOnUserPermissions if true only the objects which is the user allowed to read are returned. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + */ + public function findLocations(LocationQuery $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult + { + if (!is_int($query->offset)) { + throw new InvalidArgumentType( + '$query->offset', + 'integer', + $query->offset + ); + } + + if (!is_int($query->limit)) { + throw new InvalidArgumentType( + '$query->limit', + 'integer', + $query->limit + ); + } + + $query = clone $query; + $query->filter = $query->filter ?: new Criterion\MatchAll(); + + if ($filterOnUserPermissions && !$this->addPermissionsCriterion($query->filter)) { + return new SearchResult(['time' => 0, 'totalCount' => 0]); + } + + $result = $this->searchHandler->findLocations($query, $languageFilter); + + $missingLocations = $this->contentDomainMapper->buildLocationDomainObjectsOnSearchResult($result, $languageFilter); + foreach ($missingLocations as $missingLocation) { + $this->backgroundIndexer->registerLocation($missingLocation); + } + + return $result; + } + + /** + * Adds content, read Permission criteria if needed and return false if no access at all. + * + * @uses \Ibexa\Contracts\Core\Repository\PermissionCriterionResolver::getPermissionsCriterion() + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion + * + * @return bool + */ + protected function addPermissionsCriterion(Criterion &$criterion): bool + { + $permissionCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'read'); + if ($permissionCriterion === true || $permissionCriterion === false) { + return $permissionCriterion; + } + + // Merge with original $criterion + if ($criterion instanceof LogicalAnd) { + $criterion->criteria[] = $permissionCriterion; + } else { + $criterion = new LogicalAnd( + [ + $criterion, + $permissionCriterion, + ] + ); + } + + return true; + } + + public function supports(int $capabilityFlag): bool + { + if ($this->searchHandler instanceof Capable) { + return $this->searchHandler->supports($capabilityFlag); + } + + return false; + } +} + +class_alias(SearchService::class, 'eZ\Publish\Core\Repository\SearchService'); diff --git a/src/lib/Repository/SectionService.php b/src/lib/Repository/SectionService.php new file mode 100644 index 0000000000..287b8b80d9 --- /dev/null +++ b/src/lib/Repository/SectionService.php @@ -0,0 +1,460 @@ +repository = $repository; + $this->sectionHandler = $sectionHandler; + $this->locationHandler = $locationHandler; + $this->permissionResolver = $repository->getPermissionResolver(); + $this->permissionCriterionResolver = $permissionCriterionResolver; + // Union makes sure default settings are ignored if provided in argument + $this->settings = $settings + [ + //'defaultSetting' => array(), + ]; + } + + /** + * Creates a new Section in the content repository. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to create a section + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the new identifier in $sectionCreateStruct already exists + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\SectionCreateStruct $sectionCreateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Section The newly created section + */ + public function createSection(SectionCreateStruct $sectionCreateStruct): Section + { + if (!is_string($sectionCreateStruct->name) || empty($sectionCreateStruct->name)) { + throw new InvalidArgumentValue('name', $sectionCreateStruct->name, 'SectionCreateStruct'); + } + + if (!is_string($sectionCreateStruct->identifier) || empty($sectionCreateStruct->identifier)) { + throw new InvalidArgumentValue('identifier', $sectionCreateStruct->identifier, 'SectionCreateStruct'); + } + + if (!$this->permissionResolver->canUser('section', 'edit', $sectionCreateStruct)) { + throw new UnauthorizedException('section', 'edit'); + } + + try { + $existingSection = $this->sectionHandler->loadByIdentifier($sectionCreateStruct->identifier); + if ($existingSection !== null) { + throw new InvalidArgumentException('sectionCreateStruct', 'A Section with the specified identifier already exists'); + } + } catch (APINotFoundException $e) { + // Do nothing + } + + $this->repository->beginTransaction(); + try { + $spiSection = $this->sectionHandler->create( + $sectionCreateStruct->name, + $sectionCreateStruct->identifier + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->buildDomainSectionObject($spiSection); + } + + /** + * Updates the given section in the content repository. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to create a section + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If the new identifier already exists (if set in the update struct) + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Section $section + * @param \Ibexa\Contracts\Core\Repository\Values\Content\SectionUpdateStruct $sectionUpdateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Section + */ + public function updateSection(Section $section, SectionUpdateStruct $sectionUpdateStruct): Section + { + if ($sectionUpdateStruct->name !== null && !is_string($sectionUpdateStruct->name)) { + throw new InvalidArgumentValue('name', $section->name, 'Section'); + } + + if ($sectionUpdateStruct->identifier !== null && !is_string($sectionUpdateStruct->identifier)) { + throw new InvalidArgumentValue('identifier', $section->identifier, 'Section'); + } + + if (!$this->permissionResolver->canUser('section', 'edit', $section)) { + throw new UnauthorizedException('section', 'edit'); + } + + if ($sectionUpdateStruct->identifier !== null) { + try { + $existingSection = $this->sectionHandler->loadByIdentifier($sectionUpdateStruct->identifier); + + // Allowing identifier update only for the same section + if ($existingSection->id != $section->id) { + throw new InvalidArgumentException('sectionUpdateStruct', 'A Section with the specified identifier already exists'); + } + } catch (APINotFoundException $e) { + // Do nothing + } + } + + $loadedSection = $this->sectionHandler->load($section->id); + + $this->repository->beginTransaction(); + try { + $spiSection = $this->sectionHandler->update( + $loadedSection->id, + $sectionUpdateStruct->name ?: $loadedSection->name, + $sectionUpdateStruct->identifier ?: $loadedSection->identifier + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->buildDomainSectionObject($spiSection); + } + + /** + * Loads a Section from its id ($sectionId). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if section could not be found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to read a section + * + * @param int $sectionId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Section + */ + public function loadSection(int $sectionId): Section + { + $section = $this->buildDomainSectionObject( + $this->sectionHandler->load($sectionId) + ); + + if (!$this->permissionResolver->canUser('section', 'view', $section)) { + throw new UnauthorizedException('section', 'view'); + } + + return $section; + } + + /** + * Loads all sections, excluding the ones the current user is not allowed to read. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Section[] + */ + public function loadSections(): iterable + { + $sections = array_map(function ($spiSection) { + return $this->buildDomainSectionObject($spiSection); + }, $this->sectionHandler->loadAll()); + + return array_values(array_filter($sections, function ($section) { + return $this->permissionResolver->canUser('section', 'view', $section); + })); + } + + /** + * Loads a Section from its identifier ($sectionIdentifier). + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if section could not be found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user user is not allowed to read a section + * + * @param string $sectionIdentifier + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Section + */ + public function loadSectionByIdentifier(string $sectionIdentifier): Section + { + if (empty($sectionIdentifier)) { + throw new InvalidArgumentValue('sectionIdentifier', $sectionIdentifier); + } + + $section = $this->buildDomainSectionObject( + $this->sectionHandler->loadByIdentifier($sectionIdentifier) + ); + + if (!$this->permissionResolver->canUser('section', 'view', $section)) { + throw new UnauthorizedException('section', 'view'); + } + + return $section; + } + + /** + * Counts the contents which $section is assigned to. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Section $section + * + * @return int + * + * @deprecated since 6.0 + */ + public function countAssignedContents(Section $section): int + { + return $this->sectionHandler->assignmentsCount($section->id); + } + + /** + * Returns true if the given section is assigned to contents, or used in role policies, or in role assignments. + * + * This does not check user permissions. + * + * @since 6.0 + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Section $section + * + * @return bool + */ + public function isSectionUsed(Section $section): bool + { + return $this->sectionHandler->assignmentsCount($section->id) > 0 || + $this->sectionHandler->policiesCount($section->id) > 0 || + $this->sectionHandler->countRoleAssignmentsUsingSection($section->id) > 0; + } + + /** + * Assigns the content to the given section + * this method overrides the current assigned section. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If user does not have access to view provided object + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Section $section + */ + public function assignSection(ContentInfo $contentInfo, Section $section): void + { + $loadedContentInfo = $this->repository->getContentService()->loadContentInfo($contentInfo->id); + $loadedSection = $this->loadSection($section->id); + + if (!$this->permissionResolver->canUser('section', 'assign', $loadedContentInfo, [$loadedSection])) { + throw new UnauthorizedException( + 'section', + 'assign', + [ + 'name' => $loadedSection->name, + 'content-name' => $loadedContentInfo->name, + ] + ); + } + + $this->repository->beginTransaction(); + try { + $this->sectionHandler->assign( + $loadedSection->id, + $loadedContentInfo->id + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Assigns the subtree to the given section + * this method overrides the current assigned section. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Section $section + */ + public function assignSectionToSubtree(Location $location, Section $section): void + { + $loadedSubtree = $this->repository->getLocationService()->loadLocation($location->id); + $loadedSection = $this->loadSection($section->id); + + /** + * Check read access to whole source subtree. + * + * @var bool|\Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion + */ + $sectionAssignCriterion = $this->permissionCriterionResolver->getPermissionsCriterion( + 'section', + 'assign', + [$loadedSection] + ); + if ($sectionAssignCriterion === false) { + throw new UnauthorizedException('section', 'assign', [ + 'name' => $loadedSection->name, + 'subtree' => $loadedSubtree->pathString, + ]); + } elseif ($sectionAssignCriterion !== true) { + // Query if there are any content in subtree current user don't have access to + $query = new Query( + [ + 'limit' => 0, + 'filter' => new CriterionLogicalAnd( + [ + new CriterionSubtree($loadedSubtree->pathString), + new CriterionLogicalNot($sectionAssignCriterion), + ] + ), + ] + ); + + $result = $this->repository->getSearchService()->findContent($query, [], false); + if ($result->totalCount > 0) { + throw new UnauthorizedException('section', 'assign', [ + 'name' => $loadedSection->name, + 'subtree' => $loadedSubtree->pathString, + ]); + } + } + + $this->repository->beginTransaction(); + try { + $this->locationHandler->setSectionForSubtree( + $loadedSubtree->id, + $loadedSection->id + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Deletes $section from content repository. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If the specified section is not found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException If the current user is not allowed to delete a section + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If section can not be deleted + * because it is still assigned to some contents, + * or because it is still being used in policy limitations. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Section $section + */ + public function deleteSection(Section $section): void + { + $loadedSection = $this->loadSection($section->id); + + if (!$this->permissionResolver->canUser('section', 'edit', $loadedSection)) { + throw new UnauthorizedException('section', 'edit', ['sectionId' => $loadedSection->id]); + } + + if ($this->sectionHandler->assignmentsCount($loadedSection->id) > 0) { + throw new BadStateException('section', 'The Section still has content assigned'); + } + + if ($this->sectionHandler->policiesCount($loadedSection->id) > 0) { + throw new BadStateException('section', 'the Section is still being used in Policy Limitations'); + } + + $this->repository->beginTransaction(); + try { + $this->sectionHandler->delete($loadedSection->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Instantiates a new SectionCreateStruct. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\SectionCreateStruct + */ + public function newSectionCreateStruct(): SectionCreateStruct + { + return new SectionCreateStruct(); + } + + /** + * Instantiates a new SectionUpdateStruct. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\SectionUpdateStruct + */ + public function newSectionUpdateStruct(): SectionUpdateStruct + { + return new SectionUpdateStruct(); + } + + /** + * Builds API Section object from provided SPI Section object. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\Section $spiSection + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Section + */ + protected function buildDomainSectionObject(SPISection $spiSection) + { + return new Section( + [ + 'id' => $spiSection->id, + 'identifier' => $spiSection->identifier, + 'name' => $spiSection->name, + ] + ); + } +} + +class_alias(SectionService::class, 'eZ\Publish\Core\Repository\SectionService'); diff --git a/src/lib/Repository/SettingService.php b/src/lib/Repository/SettingService.php new file mode 100644 index 0000000000..79b8514b00 --- /dev/null +++ b/src/lib/Repository/SettingService.php @@ -0,0 +1,118 @@ +settingHandler = $settingHandler; + $this->permissionResolver = $permissionResolver; + } + + public function loadSetting(string $group, string $identifier): Setting + { + return $this->buildSettingDomainObject( + $this->settingHandler->load($group, $identifier) + ); + } + + public function updateSetting(Setting $setting, SettingUpdateStruct $settingUpdateStruct): Setting + { + if (!$this->permissionResolver->canUser('setting', 'update', $setting)) { + throw new UnauthorizedException('setting', 'update'); + } + + return $this->buildSettingDomainObject( + $this->settingHandler->update( + $setting->group, + $setting->identifier, + json_encode($settingUpdateStruct->value) + ) + ); + } + + public function createSetting(SettingCreateStruct $settingCreateStruct): Setting + { + if (!$this->permissionResolver->canUser('setting', 'create', $settingCreateStruct)) { + throw new UnauthorizedException('setting', 'create'); + } + + try { + $existingSetting = $this->settingHandler->load($settingCreateStruct->group, $settingCreateStruct->identifier); + if ($existingSetting !== null) { + throw new InvalidArgumentException('settingCreateStruct', 'A Setting with the specified group and identifier already exists'); + } + } catch (APINotFoundException $e) { + // Do nothing + } + + return $this->buildSettingDomainObject( + $this->settingHandler->create( + $settingCreateStruct->group, + $settingCreateStruct->identifier, + json_encode($settingCreateStruct->value) + ) + ); + } + + public function deleteSetting(Setting $setting): void + { + if (!$this->permissionResolver->canUser('setting', 'remove', $setting)) { + throw new UnauthorizedException('setting', 'remove'); + } + + $existingSetting = $this->settingHandler->load($setting->group, $setting->identifier); + + $this->settingHandler->delete( + $existingSetting->group, + $existingSetting->identifier + ); + } + + public function newSettingCreateStruct(array $properties = []): SettingCreateStruct + { + return new SettingCreateStruct($properties); + } + + public function newSettingUpdateStruct(array $properties = []): SettingUpdateStruct + { + return new SettingUpdateStruct($properties); + } + + private function buildSettingDomainObject(SPISetting $setting): Setting + { + return new Setting([ + 'group' => $setting->group, + 'identifier' => $setting->identifier, + 'value' => json_decode($setting->serializedValue, true), + ]); + } +} + +class_alias(SettingService::class, 'eZ\Publish\Core\Repository\SettingService'); diff --git a/src/lib/Repository/SiteAccessAware/Config/IOConfigResolver.php b/src/lib/Repository/SiteAccessAware/Config/IOConfigResolver.php new file mode 100644 index 0000000000..b71e0fc188 --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/Config/IOConfigResolver.php @@ -0,0 +1,53 @@ +storageDir = $storageDir; + $this->legacyUrlPrefix = $legacyUrlPrefix; + $this->urlPrefix = $urlPrefix; + } + + public function getRootDir(): string + { + return $this->storageDir; + } + + public function getLegacyUrlPrefix(): string + { + return $this->legacyUrlPrefix; + } + + public function getUrlPrefix(): string + { + return $this->urlPrefix; + } +} + +class_alias(IOConfigResolver::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Config\IOConfigResolver'); diff --git a/src/lib/Repository/SiteAccessAware/ContentService.php b/src/lib/Repository/SiteAccessAware/ContentService.php new file mode 100644 index 0000000000..f931f59b1f --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/ContentService.php @@ -0,0 +1,309 @@ +service = $service; + $this->languageResolver = $languageResolver; + } + + public function loadContentInfo(int $contentId): ContentInfo + { + return $this->service->loadContentInfo($contentId); + } + + /** + * {@inheritdoc} + */ + public function loadContentInfoList(array $contentIds): iterable + { + return $this->service->loadContentInfoList($contentIds); + } + + public function loadContentInfoByRemoteId(string $remoteId): ContentInfo + { + return $this->service->loadContentInfoByRemoteId($remoteId); + } + + public function loadVersionInfo(ContentInfo $contentInfo, ?int $versionNo = null): VersionInfo + { + return $this->service->loadVersionInfo($contentInfo, $versionNo); + } + + public function loadVersionInfoById(int $contentId, ?int $versionNo = null): VersionInfo + { + return $this->service->loadVersionInfoById($contentId, $versionNo); + } + + public function loadVersionInfoListByContentInfo(array $contentInfoList): array + { + return $this->service->loadVersionInfoListByContentInfo($contentInfoList); + } + + public function loadContentByContentInfo(ContentInfo $contentInfo, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content + { + return $this->service->loadContentByContentInfo( + $contentInfo, + $this->languageResolver->getPrioritizedLanguages($languages), + $versionNo, + $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) + ); + } + + public function loadContentByVersionInfo(VersionInfo $versionInfo, array $languages = null, bool $useAlwaysAvailable = true): Content + { + return $this->service->loadContentByVersionInfo( + $versionInfo, + $this->languageResolver->getPrioritizedLanguages($languages), + $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) + ); + } + + public function loadContent(int $contentId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content + { + return $this->service->loadContent( + $contentId, + $this->languageResolver->getPrioritizedLanguages($languages), + $versionNo, + $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) + ); + } + + public function loadContentByRemoteId(string $remoteId, array $languages = null, ?int $versionNo = null, bool $useAlwaysAvailable = true): Content + { + return $this->service->loadContentByRemoteId( + $remoteId, + $this->languageResolver->getPrioritizedLanguages($languages), + $versionNo, + $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) + ); + } + + public function createContent( + ContentCreateStruct $contentCreateStruct, + array $locationCreateStructs = [], + ?array $fieldIdentifiersToValidate = null + ): Content { + return $this->service->createContent($contentCreateStruct, $locationCreateStructs, $fieldIdentifiersToValidate); + } + + public function updateContentMetadata(ContentInfo $contentInfo, ContentMetadataUpdateStruct $contentMetadataUpdateStruct): Content + { + return $this->service->updateContentMetadata($contentInfo, $contentMetadataUpdateStruct); + } + + public function deleteContent(ContentInfo $contentInfo): iterable + { + return $this->service->deleteContent($contentInfo); + } + + public function createContentDraft( + ContentInfo $contentInfo, + ?VersionInfo $versionInfo = null, + ?User $creator = null, + ?Language $language = null + ): Content { + return $this->service->createContentDraft($contentInfo, $versionInfo, $creator, $language); + } + + public function countContentDrafts(?User $user = null): int + { + return $this->service->countContentDrafts($user); + } + + public function loadContentDrafts(?User $user = null): iterable + { + return $this->service->loadContentDrafts($user); + } + + public function loadContentDraftList(?User $user = null, int $offset = 0, int $limit = -1): ContentDraftList + { + return $this->service->loadContentDraftList($user, $offset, $limit); + } + + public function updateContent(VersionInfo $versionInfo, ContentUpdateStruct $contentUpdateStruct, ?array $fieldIdentifiersToValidate = null): Content + { + return $this->service->updateContent($versionInfo, $contentUpdateStruct, $fieldIdentifiersToValidate); + } + + public function publishVersion(VersionInfo $versionInfo, array $translations = Language::ALL): Content + { + return $this->service->publishVersion($versionInfo, $translations); + } + + public function deleteVersion(VersionInfo $versionInfo): void + { + $this->service->deleteVersion($versionInfo); + } + + public function loadVersions(ContentInfo $contentInfo, ?int $status = null): iterable + { + return $this->service->loadVersions($contentInfo, $status); + } + + public function copyContent(ContentInfo $contentInfo, LocationCreateStruct $destinationLocationCreateStruct, ?VersionInfo $versionInfo = null): Content + { + return $this->service->copyContent($contentInfo, $destinationLocationCreateStruct, $versionInfo); + } + + public function loadRelations(VersionInfo $versionInfo): iterable + { + return $this->service->loadRelations($versionInfo); + } + + public function countRelations(VersionInfo $versionInfo): int + { + return $this->service->countRelations($versionInfo); + } + + public function loadRelationList( + VersionInfo $versionInfo, + int $offset = 0, + int $limit = self::DEFAULT_PAGE_SIZE + ): RelationList { + return $this->service->loadRelationList($versionInfo, $offset, $limit); + } + + /** + * {@inheritdoc} + */ + public function countReverseRelations(ContentInfo $contentInfo): int + { + return $this->service->countReverseRelations($contentInfo); + } + + public function loadReverseRelations(ContentInfo $contentInfo): iterable + { + return $this->service->loadReverseRelations($contentInfo); + } + + public function loadReverseRelationList(ContentInfo $contentInfo, int $offset = 0, int $limit = -1): RelationList + { + return $this->service->loadReverseRelationList($contentInfo, $offset, $limit); + } + + public function addRelation(VersionInfo $sourceVersion, ContentInfo $destinationContent): Relation + { + return $this->service->addRelation($sourceVersion, $destinationContent); + } + + public function deleteRelation(VersionInfo $sourceVersion, ContentInfo $destinationContent): void + { + $this->service->deleteRelation($sourceVersion, $destinationContent); + } + + public function deleteTranslation(ContentInfo $contentInfo, string $languageCode): void + { + $this->service->deleteTranslation($contentInfo, $languageCode); + } + + public function deleteTranslationFromDraft(VersionInfo $versionInfo, string $languageCode): Content + { + return $this->service->deleteTranslationFromDraft($versionInfo, $languageCode); + } + + public function loadContentListByContentInfo(array $contentInfoList, array $languages = null, bool $useAlwaysAvailable = true): iterable + { + return $this->service->loadContentListByContentInfo( + $contentInfoList, + $this->languageResolver->getPrioritizedLanguages($languages), + $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) + ); + } + + public function hideContent(ContentInfo $contentInfo): void + { + $this->service->hideContent($contentInfo); + } + + public function revealContent(ContentInfo $contentInfo): void + { + $this->service->revealContent($contentInfo); + } + + public function newContentCreateStruct(ContentType $contentType, string $mainLanguageCode): ContentCreateStruct + { + return $this->service->newContentCreateStruct($contentType, $mainLanguageCode); + } + + public function newContentMetadataUpdateStruct(): ContentMetadataUpdateStruct + { + return $this->service->newContentMetadataUpdateStruct(); + } + + public function newContentUpdateStruct(): ContentUpdateStruct + { + return $this->service->newContentUpdateStruct(); + } + + public function validate( + ValueObject $object, + array $context, + ?array $fieldIdentifiersToValidate = null + ): array { + return $this->service->validate($object, $context, $fieldIdentifiersToValidate); + } + + public function find(Filter $filter, ?array $languages = null): ContentList + { + return $this->service->find( + $filter, + $this->languageResolver->getPrioritizedLanguages($languages) + ); + } + + public function count(Filter $filter, ?array $languages = null): int + { + return $this->service->count( + $filter, + $this->languageResolver->getPrioritizedLanguages($languages) + ); + } +} + +class_alias(ContentService::class, 'eZ\Publish\Core\Repository\SiteAccessAware\ContentService'); diff --git a/src/lib/Repository/SiteAccessAware/ContentTypeService.php b/src/lib/Repository/SiteAccessAware/ContentTypeService.php new file mode 100644 index 0000000000..939c03f652 --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/ContentTypeService.php @@ -0,0 +1,225 @@ +service = $service; + $this->languageResolver = $languageResolver; + } + + public function createContentTypeGroup(ContentTypeGroupCreateStruct $contentTypeGroupCreateStruct): ContentTypeGroup + { + return $this->service->createContentTypeGroup($contentTypeGroupCreateStruct); + } + + public function loadContentTypeGroup(int $contentTypeGroupId, array $prioritizedLanguages = null): ContentTypeGroup + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadContentTypeGroup($contentTypeGroupId, $prioritizedLanguages); + } + + public function loadContentTypeGroupByIdentifier(string $contentTypeGroupIdentifier, array $prioritizedLanguages = null): ContentTypeGroup + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadContentTypeGroupByIdentifier($contentTypeGroupIdentifier, $prioritizedLanguages); + } + + public function loadContentTypeGroups(array $prioritizedLanguages = null): iterable + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadContentTypeGroups($prioritizedLanguages); + } + + public function updateContentTypeGroup(ContentTypeGroup $contentTypeGroup, ContentTypeGroupUpdateStruct $contentTypeGroupUpdateStruct): void + { + $this->service->updateContentTypeGroup($contentTypeGroup, $contentTypeGroupUpdateStruct); + } + + public function deleteContentTypeGroup(ContentTypeGroup $contentTypeGroup): void + { + $this->service->deleteContentTypeGroup($contentTypeGroup); + } + + public function createContentType(ContentTypeCreateStruct $contentTypeCreateStruct, array $contentTypeGroups): ContentTypeDraft + { + return $this->service->createContentType($contentTypeCreateStruct, $contentTypeGroups); + } + + public function loadContentType(int $contentTypeId, array $prioritizedLanguages = null): ContentType + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadContentType($contentTypeId, $prioritizedLanguages); + } + + public function loadContentTypeByIdentifier(string $identifier, array $prioritizedLanguages = null): ContentType + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadContentTypeByIdentifier($identifier, $prioritizedLanguages); + } + + public function loadContentTypeByRemoteId(string $remoteId, array $prioritizedLanguages = null): ContentType + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadContentTypeByRemoteId($remoteId, $prioritizedLanguages); + } + + public function loadContentTypeDraft(int $contentTypeId, bool $ignoreOwnership = false): ContentTypeDraft + { + return $this->service->loadContentTypeDraft($contentTypeId, $ignoreOwnership); + } + + public function loadContentTypeList(array $contentTypeIds, array $prioritizedLanguages = null): iterable + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadContentTypeList($contentTypeIds, $prioritizedLanguages); + } + + public function loadContentTypes(ContentTypeGroup $contentTypeGroup, array $prioritizedLanguages = null): iterable + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadContentTypes($contentTypeGroup, $prioritizedLanguages); + } + + public function createContentTypeDraft(ContentType $contentType): ContentTypeDraft + { + return $this->service->createContentTypeDraft($contentType); + } + + public function updateContentTypeDraft(ContentTypeDraft $contentTypeDraft, ContentTypeUpdateStruct $contentTypeUpdateStruct): void + { + $this->service->updateContentTypeDraft($contentTypeDraft, $contentTypeUpdateStruct); + } + + public function deleteContentType(ContentType $contentType): void + { + $this->service->deleteContentType($contentType); + } + + public function copyContentType(ContentType $contentType, User $creator = null): ContentType + { + return $this->service->copyContentType($contentType, $creator); + } + + public function assignContentTypeGroup(ContentType $contentType, ContentTypeGroup $contentTypeGroup): void + { + $this->service->assignContentTypeGroup($contentType, $contentTypeGroup); + } + + public function unassignContentTypeGroup(ContentType $contentType, ContentTypeGroup $contentTypeGroup): void + { + $this->service->unassignContentTypeGroup($contentType, $contentTypeGroup); + } + + public function addFieldDefinition(ContentTypeDraft $contentTypeDraft, FieldDefinitionCreateStruct $fieldDefinitionCreateStruct): void + { + $this->service->addFieldDefinition($contentTypeDraft, $fieldDefinitionCreateStruct); + } + + public function removeFieldDefinition(ContentTypeDraft $contentTypeDraft, FieldDefinition $fieldDefinition): void + { + $this->service->removeFieldDefinition($contentTypeDraft, $fieldDefinition); + } + + public function updateFieldDefinition(ContentTypeDraft $contentTypeDraft, FieldDefinition $fieldDefinition, FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct): void + { + $this->service->updateFieldDefinition($contentTypeDraft, $fieldDefinition, $fieldDefinitionUpdateStruct); + } + + public function publishContentTypeDraft(ContentTypeDraft $contentTypeDraft): void + { + $this->service->publishContentTypeDraft($contentTypeDraft); + } + + public function newContentTypeGroupCreateStruct(string $identifier): ContentTypeGroupCreateStruct + { + return $this->service->newContentTypeGroupCreateStruct($identifier); + } + + public function newContentTypeCreateStruct(string $identifier): ContentTypeCreateStruct + { + return $this->service->newContentTypeCreateStruct($identifier); + } + + public function newContentTypeUpdateStruct(): ContentTypeUpdateStruct + { + return $this->service->newContentTypeUpdateStruct(); + } + + public function newContentTypeGroupUpdateStruct(): ContentTypeGroupUpdateStruct + { + return $this->service->newContentTypeGroupUpdateStruct(); + } + + public function newFieldDefinitionCreateStruct(string $identifier, string $fieldTypeIdentifier): FieldDefinitionCreateStruct + { + return $this->service->newFieldDefinitionCreateStruct($identifier, $fieldTypeIdentifier); + } + + public function newFieldDefinitionUpdateStruct(): FieldDefinitionUpdateStruct + { + return $this->service->newFieldDefinitionUpdateStruct(); + } + + public function isContentTypeUsed(ContentType $contentType): bool + { + return $this->service->isContentTypeUsed($contentType); + } + + public function removeContentTypeTranslation(ContentTypeDraft $contentTypeDraft, string $languageCode): ContentTypeDraft + { + return $this->service->removeContentTypeTranslation($contentTypeDraft, $languageCode); + } + + public function deleteUserDrafts(int $userId): void + { + $this->service->deleteUserDrafts($userId); + } +} + +class_alias(ContentTypeService::class, 'eZ\Publish\Core\Repository\SiteAccessAware\ContentTypeService'); diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Language/AbstractLanguageResolver.php b/src/lib/Repository/SiteAccessAware/Language/AbstractLanguageResolver.php similarity index 94% rename from eZ/Publish/Core/Repository/SiteAccessAware/Language/AbstractLanguageResolver.php rename to src/lib/Repository/SiteAccessAware/Language/AbstractLanguageResolver.php index 22825b4d67..196e28894d 100644 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Language/AbstractLanguageResolver.php +++ b/src/lib/Repository/SiteAccessAware/Language/AbstractLanguageResolver.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\SiteAccessAware\Language; +namespace Ibexa\Core\Repository\SiteAccessAware\Language; -use eZ\Publish\API\Repository\LanguageResolver as APILanguageResolver; -use eZ\Publish\API\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\LanguageResolver as APILanguageResolver; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; /** * Common abstract implementation of Language resolver. @@ -162,3 +162,5 @@ final public function getShowAllTranslations(?bool $forcedShowAllTranslations = return $this->defaultShowAllTranslations; } } + +class_alias(AbstractLanguageResolver::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Language\AbstractLanguageResolver'); diff --git a/src/lib/Repository/SiteAccessAware/Language/LanguageResolver.php b/src/lib/Repository/SiteAccessAware/Language/LanguageResolver.php new file mode 100644 index 0000000000..da05360ee8 --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/Language/LanguageResolver.php @@ -0,0 +1,36 @@ +configLanguages = $configLanguages; + parent::__construct($defaultUseAlwaysAvailable, $defaultShowAllTranslations); + } + + protected function getConfiguredLanguages(): array + { + return $this->configLanguages; + } +} + +class_alias(LanguageResolver::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Language\LanguageResolver'); diff --git a/src/lib/Repository/SiteAccessAware/LanguageService.php b/src/lib/Repository/SiteAccessAware/LanguageService.php new file mode 100644 index 0000000000..1c43282c3a --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/LanguageService.php @@ -0,0 +1,95 @@ +service = $service; + } + + public function createLanguage(LanguageCreateStruct $languageCreateStruct): Language + { + return $this->service->createLanguage($languageCreateStruct); + } + + public function updateLanguageName(Language $language, string $newName): Language + { + return $this->service->updateLanguageName($language, $newName); + } + + public function enableLanguage(Language $language): Language + { + return $this->service->enableLanguage($language); + } + + public function disableLanguage(Language $language): Language + { + return $this->service->disableLanguage($language); + } + + public function loadLanguage(string $languageCode): Language + { + return $this->service->loadLanguage($languageCode); + } + + public function loadLanguages(): iterable + { + return $this->service->loadLanguages(); + } + + public function loadLanguageById(int $languageId): Language + { + return $this->service->loadLanguageById($languageId); + } + + public function loadLanguageListByCode(array $languageCodes): iterable + { + return $this->service->loadLanguageListByCode($languageCodes); + } + + public function loadLanguageListById(array $languageIds): iterable + { + return $this->service->loadLanguageListById($languageIds); + } + + public function deleteLanguage(Language $language): void + { + $this->service->deleteLanguage($language); + } + + public function getDefaultLanguageCode(): string + { + return $this->service->getDefaultLanguageCode(); + } + + public function newLanguageCreateStruct(): LanguageCreateStruct + { + return $this->service->newLanguageCreateStruct(); + } +} + +class_alias(LanguageService::class, 'eZ\Publish\Core\Repository\SiteAccessAware\LanguageService'); diff --git a/src/lib/Repository/SiteAccessAware/LocationService.php b/src/lib/Repository/SiteAccessAware/LocationService.php new file mode 100644 index 0000000000..43e6feb7c9 --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/LocationService.php @@ -0,0 +1,204 @@ +service = $service; + $this->languageResolver = $languageResolver; + } + + public function copySubtree(Location $subtree, Location $targetParentLocation): Location + { + return $this->service->copySubtree($subtree, $targetParentLocation); + } + + public function loadLocation(int $locationId, ?array $prioritizedLanguages = null, ?bool $useAlwaysAvailable = null): Location + { + return $this->service->loadLocation( + $locationId, + $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages), + $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) + ); + } + + public function loadLocationList(array $locationIds, ?array $prioritizedLanguages = null, ?bool $useAlwaysAvailable = null): iterable + { + return $this->service->loadLocationList( + $locationIds, + $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages), + $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) + ); + } + + public function loadLocationByRemoteId(string $remoteId, ?array $prioritizedLanguages = null, ?bool $useAlwaysAvailable = null): Location + { + return $this->service->loadLocationByRemoteId( + $remoteId, + $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages), + $this->languageResolver->getUseAlwaysAvailable($useAlwaysAvailable) + ); + } + + public function loadLocations(ContentInfo $contentInfo, ?Location $rootLocation = null, ?array $prioritizedLanguages = null): iterable + { + return $this->service->loadLocations( + $contentInfo, + $rootLocation, + $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages) + ); + } + + public function loadLocationChildren(Location $location, int $offset = 0, int $limit = 25, ?array $prioritizedLanguages = null): LocationList + { + return $this->service->loadLocationChildren( + $location, + $offset, + $limit, + $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages) + ); + } + + public function loadParentLocationsForDraftContent(VersionInfo $versionInfo, ?array $prioritizedLanguages = null): iterable + { + return $this->service->loadParentLocationsForDraftContent( + $versionInfo, + $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages) + ); + } + + public function getLocationChildCount(Location $location): int + { + return $this->service->getLocationChildCount($location); + } + + public function getSubtreeSize(Location $location): int + { + return $this->service->getSubtreeSize($location); + } + + public function createLocation(ContentInfo $contentInfo, LocationCreateStruct $locationCreateStruct): Location + { + return $this->service->createLocation($contentInfo, $locationCreateStruct); + } + + public function updateLocation(Location $location, LocationUpdateStruct $locationUpdateStruct): Location + { + return $this->service->updateLocation($location, $locationUpdateStruct); + } + + public function swapLocation(Location $location1, Location $location2): void + { + $this->service->swapLocation($location1, $location2); + } + + public function hideLocation(Location $location): Location + { + return $this->service->hideLocation($location); + } + + public function unhideLocation(Location $location): Location + { + return $this->service->unhideLocation($location); + } + + public function moveSubtree(Location $location, Location $newParentLocation): void + { + $this->service->moveSubtree($location, $newParentLocation); + } + + public function deleteLocation(Location $location): void + { + $this->service->deleteLocation($location); + } + + public function newLocationCreateStruct(int $parentLocationId): LocationCreateStruct + { + return $this->service->newLocationCreateStruct($parentLocationId); + } + + public function newLocationUpdateStruct(): LocationUpdateStruct + { + return $this->service->newLocationUpdateStruct(); + } + + /** + * Get the total number of all existing Locations. Can be combined with loadAllLocations. + * + * @see loadAllLocations + * + * @return int Total number of Locations + */ + public function getAllLocationsCount(): int + { + return $this->service->getAllLocationsCount(); + } + + /** + * Bulk-load all existing Locations, constrained by $limit and $offset to paginate results. + * + * @param int $limit + * @param int $offset + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location[] + */ + public function loadAllLocations(int $offset = 0, int $limit = 25): array + { + return $this->service->loadAllLocations($offset, $limit); + } + + public function find(Filter $filter, ?array $languages = null): LocationList + { + return $this->service->find( + $filter, + $this->languageResolver->getPrioritizedLanguages($languages) + ); + } + + public function count(Filter $filter, ?array $languages = null): int + { + return $this->service->count( + $filter, + $this->languageResolver->getPrioritizedLanguages($languages) + ); + } +} + +class_alias(LocationService::class, 'eZ\Publish\Core\Repository\SiteAccessAware\LocationService'); diff --git a/src/lib/Repository/SiteAccessAware/NotificationService.php b/src/lib/Repository/SiteAccessAware/NotificationService.php new file mode 100644 index 0000000000..203e8d7c9a --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/NotificationService.php @@ -0,0 +1,104 @@ +service = $service; + } + + /** + * Get currently logged user notifications. + * + * @param int $offset + * @param int $limit + * + * @return \Ibexa\Contracts\Core\Repository\Values\Notification\NotificationList + */ + public function loadNotifications(int $offset, int $limit): NotificationList + { + return $this->service->loadNotifications($offset, $limit); + } + + /** + * @param int $notificationId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Notification\Notification + */ + public function getNotification(int $notificationId): Notification + { + return $this->service->getNotification($notificationId); + } + + /** + * Mark notification as read so it no longer bother the user. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Notification\Notification $notification + */ + public function markNotificationAsRead(Notification $notification): void + { + $this->service->markNotificationAsRead($notification); + } + + /** + * Get count of unread users notifications. + * + * @return int + */ + public function getPendingNotificationCount(): int + { + return $this->service->getPendingNotificationCount(); + } + + /** + * Get count of total users notifications. + * + * @return int + */ + public function getNotificationCount(): int + { + return $this->service->getNotificationCount(); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Notification\Notification $notification + */ + public function deleteNotification(Notification $notification): void + { + $this->service->deleteNotification($notification); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Notification\CreateStruct $createStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\Notification\Notification + */ + public function createNotification(CreateStruct $createStruct): Notification + { + return $this->service->createNotification($createStruct); + } +} + +class_alias(NotificationService::class, 'eZ\Publish\Core\Repository\SiteAccessAware\NotificationService'); diff --git a/src/lib/Repository/SiteAccessAware/ObjectStateService.php b/src/lib/Repository/SiteAccessAware/ObjectStateService.php new file mode 100644 index 0000000000..86c587777b --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/ObjectStateService.php @@ -0,0 +1,166 @@ +service = $service; + $this->languageResolver = $languageResolver; + } + + public function createObjectStateGroup(ObjectStateGroupCreateStruct $objectStateGroupCreateStruct): ObjectStateGroup + { + return $this->service->createObjectStateGroup($objectStateGroupCreateStruct); + } + + public function loadObjectStateGroup(int $objectStateGroupId, array $prioritizedLanguages = null): ObjectStateGroup + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadObjectStateGroup($objectStateGroupId, $prioritizedLanguages); + } + + public function loadObjectStateGroupByIdentifier( + string $objectStateGroupIdentifier, + array $prioritizedLanguages = null + ): ObjectStateGroup { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadObjectStateGroupByIdentifier($objectStateGroupIdentifier, $prioritizedLanguages); + } + + public function loadObjectStateGroups(int $offset = 0, int $limit = -1, array $prioritizedLanguages = null): iterable + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadObjectStateGroups($offset, $limit, $prioritizedLanguages); + } + + public function loadObjectStates(ObjectStateGroup $objectStateGroup, array $prioritizedLanguages = null): iterable + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadObjectStates($objectStateGroup, $prioritizedLanguages); + } + + public function updateObjectStateGroup(ObjectStateGroup $objectStateGroup, ObjectStateGroupUpdateStruct $objectStateGroupUpdateStruct): ObjectStateGroup + { + return $this->service->updateObjectStateGroup($objectStateGroup, $objectStateGroupUpdateStruct); + } + + public function deleteObjectStateGroup(ObjectStateGroup $objectStateGroup): void + { + $this->service->deleteObjectStateGroup($objectStateGroup); + } + + public function createObjectState(ObjectStateGroup $objectStateGroup, ObjectStateCreateStruct $objectStateCreateStruct): ObjectState + { + return $this->service->createObjectState($objectStateGroup, $objectStateCreateStruct); + } + + public function loadObjectState(int $stateId, array $prioritizedLanguages = null): ObjectState + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadObjectState($stateId, $prioritizedLanguages); + } + + public function loadObjectStateByIdentifier( + ObjectStateGroup $objectStateGroup, + string $objectStateIdentifier, + array $prioritizedLanguages = null + ): ObjectState { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadObjectStateByIdentifier( + $objectStateGroup, + $objectStateIdentifier, + $prioritizedLanguages + ); + } + + public function updateObjectState(ObjectState $objectState, ObjectStateUpdateStruct $objectStateUpdateStruct): ObjectState + { + return $this->service->updateObjectState($objectState, $objectStateUpdateStruct); + } + + public function setPriorityOfObjectState(ObjectState $objectState, int $priority): void + { + $this->service->setPriorityOfObjectState($objectState, $priority); + } + + public function deleteObjectState(ObjectState $objectState): void + { + $this->service->deleteObjectState($objectState); + } + + public function setContentState(ContentInfo $contentInfo, ObjectStateGroup $objectStateGroup, ObjectState $objectState): void + { + $this->service->setContentState($contentInfo, $objectStateGroup, $objectState); + } + + public function getContentState(ContentInfo $contentInfo, ObjectStateGroup $objectStateGroup): ObjectState + { + return $this->service->getContentState($contentInfo, $objectStateGroup); + } + + public function getContentCount(ObjectState $objectState): int + { + return $this->service->getContentCount($objectState); + } + + public function newObjectStateGroupCreateStruct(string $identifier): ObjectStateGroupCreateStruct + { + return $this->service->newObjectStateGroupCreateStruct($identifier); + } + + public function newObjectStateGroupUpdateStruct(): ObjectStateGroupUpdateStruct + { + return $this->service->newObjectStateGroupUpdateStruct(); + } + + public function newObjectStateCreateStruct(string $identifier): ObjectStateCreateStruct + { + return $this->service->newObjectStateCreateStruct($identifier); + } + + public function newObjectStateUpdateStruct(): ObjectStateUpdateStruct + { + return $this->service->newObjectStateUpdateStruct(); + } +} + +class_alias(ObjectStateService::class, 'eZ\Publish\Core\Repository\SiteAccessAware\ObjectStateService'); diff --git a/src/lib/Repository/SiteAccessAware/Repository.php b/src/lib/Repository/SiteAccessAware/Repository.php new file mode 100644 index 0000000000..750ce2adcc --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/Repository.php @@ -0,0 +1,214 @@ +repository = $repository; + $this->contentService = $contentService; + $this->contentTypeService = $contentTypeService; + $this->objectStateService = $objectStateService; + $this->urlAliasService = $urlAliasService; + $this->userService = $userService; + $this->searchService = $searchService; + $this->sectionService = $sectionService; + $this->trashService = $trashService; + $this->locationService = $locationService; + $this->languageService = $languageService; + $this->notificationService = $notificationService; + } + + public function sudo(callable $callback, ?RepositoryInterface $outerRepository = null) + { + return $this->repository->sudo($callback, $outerRepository ?? $this); + } + + public function getContentService(): ContentServiceInterface + { + return $this->contentService; + } + + public function getContentLanguageService(): LanguageServiceInterface + { + return $this->languageService; + } + + public function getContentTypeService(): ContentTypeServiceInterface + { + return $this->contentTypeService; + } + + public function getLocationService(): LocationServiceInterface + { + return $this->locationService; + } + + public function getTrashService(): TrashServiceInterface + { + return $this->trashService; + } + + public function getSectionService(): SectionServiceInterface + { + return $this->sectionService; + } + + public function getUserService(): UserServiceInterface + { + return $this->userService; + } + + public function getURLAliasService(): URLAliasServiceInterface + { + return $this->urlAliasService; + } + + public function getURLWildcardService(): URLWildcardServiceInterface + { + return $this->repository->getURLWildcardService(); + } + + public function getObjectStateService(): ObjectStateServiceInterface + { + return $this->objectStateService; + } + + public function getRoleService(): RoleServiceInterface + { + return $this->repository->getRoleService(); + } + + public function getSearchService(): SearchServiceInterface + { + return $this->searchService; + } + + public function getFieldTypeService(): FieldTypeServiceInterface + { + return $this->repository->getFieldTypeService(); + } + + public function getPermissionResolver(): PermissionResolverInterface + { + return $this->repository->getPermissionResolver(); + } + + public function getURLService(): URLServiceInterface + { + return $this->repository->getURLService(); + } + + public function getBookmarkService(): BookmarkServiceInterface + { + return $this->repository->getBookmarkService(); + } + + public function getNotificationService(): NotificationServiceInterface + { + return $this->repository->getNotificationService(); + } + + public function getUserPreferenceService(): UserPreferenceServiceInterface + { + return $this->repository->getUserPreferenceService(); + } + + public function beginTransaction(): void + { + $this->repository->beginTransaction(); + } + + public function commit(): void + { + $this->repository->commit(); + } + + public function rollback(): void + { + $this->repository->rollback(); + } +} + +class_alias(Repository::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Repository'); diff --git a/src/lib/Repository/SiteAccessAware/SearchService.php b/src/lib/Repository/SiteAccessAware/SearchService.php new file mode 100644 index 0000000000..64b2177377 --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/SearchService.php @@ -0,0 +1,107 @@ +service = $service; + $this->languageResolver = $languageResolver; + } + + public function findContent(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult + { + $languageFilter['languages'] = $this->languageResolver->getPrioritizedLanguages( + $languageFilter['languages'] ?? null + ); + + $languageFilter['useAlwaysAvailable'] = $this->languageResolver->getUseAlwaysAvailable( + $languageFilter['useAlwaysAvailable'] ?? null + ); + + return $this->service->findContent($query, $languageFilter, $filterOnUserPermissions); + } + + public function findContentInfo(Query $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult + { + $languageFilter['languages'] = $this->languageResolver->getPrioritizedLanguages( + $languageFilter['languages'] ?? null + ); + + $languageFilter['useAlwaysAvailable'] = $this->languageResolver->getUseAlwaysAvailable( + $languageFilter['useAlwaysAvailable'] ?? null + ); + + return $this->service->findContentInfo($query, $languageFilter, $filterOnUserPermissions); + } + + public function findSingle(Criterion $filter, array $languageFilter = [], bool $filterOnUserPermissions = true): Content + { + $languageFilter['languages'] = $this->languageResolver->getPrioritizedLanguages( + $languageFilter['languages'] ?? null + ); + + $languageFilter['useAlwaysAvailable'] = $this->languageResolver->getUseAlwaysAvailable( + $languageFilter['useAlwaysAvailable'] ?? null + ); + + return $this->service->findSingle($filter, $languageFilter, $filterOnUserPermissions); + } + + public function suggest(string $prefix, array $fieldPaths = [], int $limit = 10, Criterion $filter = null) + { + return $this->service->suggest($prefix, $fieldPaths, $limit, $filter); + } + + public function findLocations(LocationQuery $query, array $languageFilter = [], bool $filterOnUserPermissions = true): SearchResult + { + $languageFilter['languages'] = $this->languageResolver->getPrioritizedLanguages( + $languageFilter['languages'] ?? null + ); + + $languageFilter['useAlwaysAvailable'] = $this->languageResolver->getUseAlwaysAvailable( + $languageFilter['useAlwaysAvailable'] ?? null + ); + + return $this->service->findLocations($query, $languageFilter, $filterOnUserPermissions); + } + + public function supports(int $capabilityFlag): bool + { + return $this->service->supports($capabilityFlag); + } +} + +class_alias(SearchService::class, 'eZ\Publish\Core\Repository\SiteAccessAware\SearchService'); diff --git a/src/lib/Repository/SiteAccessAware/SectionService.php b/src/lib/Repository/SiteAccessAware/SectionService.php new file mode 100644 index 0000000000..8522a7b58d --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/SectionService.php @@ -0,0 +1,100 @@ +service = $service; + } + + public function createSection(SectionCreateStruct $sectionCreateStruct): Section + { + return $this->service->createSection($sectionCreateStruct); + } + + public function updateSection(Section $section, SectionUpdateStruct $sectionUpdateStruct): Section + { + return $this->service->updateSection($section, $sectionUpdateStruct); + } + + public function loadSection(int $sectionId): Section + { + return $this->service->loadSection($sectionId); + } + + public function loadSections(): iterable + { + return $this->service->loadSections(); + } + + public function loadSectionByIdentifier(string $sectionIdentifier): Section + { + return $this->service->loadSectionByIdentifier($sectionIdentifier); + } + + public function countAssignedContents(Section $section): int + { + return $this->service->countAssignedContents($section); + } + + public function isSectionUsed(Section $section): bool + { + return $this->service->isSectionUsed($section); + } + + public function assignSection(ContentInfo $contentInfo, Section $section): void + { + $this->service->assignSection($contentInfo, $section); + } + + public function assignSectionToSubtree(Location $location, Section $section): void + { + $this->service->assignSectionToSubtree($location, $section); + } + + public function deleteSection(Section $section): void + { + $this->service->deleteSection($section); + } + + public function newSectionCreateStruct(): SectionCreateStruct + { + return $this->service->newSectionCreateStruct(); + } + + public function newSectionUpdateStruct(): SectionUpdateStruct + { + return $this->service->newSectionUpdateStruct(); + } +} + +class_alias(SectionService::class, 'eZ\Publish\Core\Repository\SiteAccessAware\SectionService'); diff --git a/src/lib/Repository/SiteAccessAware/SettingService.php b/src/lib/Repository/SiteAccessAware/SettingService.php new file mode 100644 index 0000000000..b8e9dac0b3 --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/SettingService.php @@ -0,0 +1,17 @@ +service = $service; + } + + public function loadTrashItem(int $trashItemId): TrashItem + { + return $this->service->loadTrashItem($trashItemId); + } + + public function trash(Location $location): ?TrashItem + { + return $this->service->trash($location); + } + + public function recover(TrashItem $trashItem, Location $newParentLocation = null): Location + { + return $this->service->recover($trashItem, $newParentLocation); + } + + public function emptyTrash(): TrashItemDeleteResultList + { + return $this->service->emptyTrash(); + } + + public function deleteTrashItem(TrashItem $trashItem): TrashItemDeleteResult + { + return $this->service->deleteTrashItem($trashItem); + } + + public function findTrashItems(Query $query): SearchResult + { + return $this->service->findTrashItems($query); + } +} + +class_alias(TrashService::class, 'eZ\Publish\Core\Repository\SiteAccessAware\TrashService'); diff --git a/src/lib/Repository/SiteAccessAware/URLAliasService.php b/src/lib/Repository/SiteAccessAware/URLAliasService.php new file mode 100644 index 0000000000..897490a9ec --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/URLAliasService.php @@ -0,0 +1,122 @@ +service = $service; + $this->languageResolver = $languageResolver; + } + + public function createUrlAlias( + Location $location, + string $path, + string $languageCode, + bool $forwarding = false, + bool $alwaysAvailable = false + ): URLAlias { + return $this->service->createUrlAlias($location, $path, $languageCode, $forwarding, $alwaysAvailable); + } + + public function createGlobalUrlAlias( + string $resource, + string $path, + string $languageCode, + bool $forwarding = false, + bool $alwaysAvailable = false + ): URLAlias { + return $this->service->createGlobalUrlAlias($resource, $path, $languageCode, $forwarding, $alwaysAvailable); + } + + public function listLocationAliases( + Location $location, + bool $custom = true, + ?string $languageCode = null, + ?bool $showAllTranslations = null, + ?array $prioritizedLanguages = null + ): iterable { + return $this->service->listLocationAliases( + $location, + $custom, + $languageCode, + $this->languageResolver->getShowAllTranslations($showAllTranslations), + $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages) + ); + } + + public function listGlobalAliases(?string $languageCode = null, int $offset = 0, int $limit = -1): iterable + { + return $this->service->listGlobalAliases($languageCode, $offset, $limit); + } + + public function removeAliases(array $aliasList): void + { + $this->service->removeAliases($aliasList); + } + + public function lookup(string $url, ?string $languageCode = null): URLAlias + { + return $this->service->lookup($url, $languageCode); + } + + public function reverseLookup( + Location $location, + ?string $languageCode = null, + ?bool $showAllTranslations = null, + ?array $prioritizedLanguages = null + ): URLAlias { + return $this->service->reverseLookup( + $location, + $languageCode, + $this->languageResolver->getShowAllTranslations($showAllTranslations), + $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages) + ); + } + + public function load(string $id): URLAlias + { + return $this->service->load($id); + } + + public function refreshSystemUrlAliasesForLocation(Location $location): void + { + $this->service->refreshSystemUrlAliasesForLocation($location); + } + + public function deleteCorruptedUrlAliases(): int + { + return $this->service->deleteCorruptedUrlAliases(); + } +} + +class_alias(URLAliasService::class, 'eZ\Publish\Core\Repository\SiteAccessAware\URLAliasService'); diff --git a/src/lib/Repository/SiteAccessAware/UserService.php b/src/lib/Repository/SiteAccessAware/UserService.php new file mode 100644 index 0000000000..b9b18efca6 --- /dev/null +++ b/src/lib/Repository/SiteAccessAware/UserService.php @@ -0,0 +1,232 @@ +service = $service; + $this->languageResolver = $languageResolver; + } + + public function createUserGroup(UserGroupCreateStruct $userGroupCreateStruct, UserGroup $parentGroup): UserGroup + { + return $this->service->createUserGroup($userGroupCreateStruct, $parentGroup); + } + + public function loadUserGroup(int $id, array $prioritizedLanguages = null): UserGroup + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadUserGroup($id, $prioritizedLanguages); + } + + public function loadUserGroupByRemoteId(string $remoteId, array $prioritizedLanguages = []): UserGroup + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadUserGroupByRemoteId($remoteId, $prioritizedLanguages); + } + + public function loadSubUserGroups(UserGroup $userGroup, int $offset = 0, int $limit = 25, array $prioritizedLanguages = null): iterable + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadSubUserGroups($userGroup, $offset, $limit, $prioritizedLanguages); + } + + public function deleteUserGroup(UserGroup $userGroup): iterable + { + return $this->service->deleteUserGroup($userGroup); + } + + public function moveUserGroup(UserGroup $userGroup, UserGroup $newParent): void + { + $this->service->moveUserGroup($userGroup, $newParent); + } + + public function updateUserGroup(UserGroup $userGroup, UserGroupUpdateStruct $userGroupUpdateStruct): UserGroup + { + return $this->service->updateUserGroup($userGroup, $userGroupUpdateStruct); + } + + public function createUser(UserCreateStruct $userCreateStruct, array $parentGroups): User + { + return $this->service->createUser($userCreateStruct, $parentGroups); + } + + public function loadUser(int $userId, array $prioritizedLanguages = null): User + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadUser($userId, $prioritizedLanguages); + } + + public function checkUserCredentials( + User $user, + #[\SensitiveParameter] + string $credentials + ): bool { + return $this->service->checkUserCredentials($user, $credentials); + } + + public function loadUserByLogin(string $login, array $prioritizedLanguages = null): User + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadUserByLogin($login, $prioritizedLanguages); + } + + public function loadUserByEmail(string $email, array $prioritizedLanguages = null): User + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadUserByEmail($email, $prioritizedLanguages); + } + + public function loadUsersByEmail(string $email, array $prioritizedLanguages = null): array + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadUsersByEmail($email, $prioritizedLanguages); + } + + public function deleteUser(User $user): iterable + { + return $this->service->deleteUser($user); + } + + public function updateUser(User $user, UserUpdateStruct $userUpdateStruct): User + { + return $this->service->updateUser($user, $userUpdateStruct); + } + + public function updateUserPassword(User $user, string $newPassword): User + { + return $this->service->updateUserPassword($user, $newPassword); + } + + public function assignUserToUserGroup(User $user, UserGroup $userGroup): void + { + $this->service->assignUserToUserGroup($user, $userGroup); + } + + public function unAssignUserFromUserGroup(User $user, UserGroup $userGroup): void + { + $this->service->unAssignUserFromUserGroup($user, $userGroup); + } + + public function loadUserGroupsOfUser(User $user, int $offset = 0, int $limit = 25, array $prioritizedLanguages = null): iterable + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadUserGroupsOfUser($user, $offset, $limit, $prioritizedLanguages); + } + + public function loadUsersOfUserGroup(UserGroup $userGroup, int $offset = 0, int $limit = 25, array $prioritizedLanguages = null): iterable + { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages); + + return $this->service->loadUsersOfUserGroup($userGroup, $offset, $limit, $prioritizedLanguages); + } + + public function loadUserByToken(string $hash, array $prioritizedLanguages = null): User + { + return $this->service->loadUserByToken( + $hash, + $this->languageResolver->getPrioritizedLanguages($prioritizedLanguages) + ); + } + + public function updateUserToken(User $user, UserTokenUpdateStruct $userTokenUpdateStruct): User + { + return $this->service->updateUserToken($user, $userTokenUpdateStruct); + } + + public function expireUserToken(string $hash): void + { + $this->service->expireUserToken($hash); + } + + public function isUser(Content $content): bool + { + return $this->service->isUser($content); + } + + public function isUserGroup(Content $content): bool + { + return $this->service->isUserGroup($content); + } + + public function newUserCreateStruct(string $login, string $email, string $password, string $mainLanguageCode, ?ContentType $contentType = null): UserCreateStruct + { + return $this->service->newUserCreateStruct($login, $email, $password, $mainLanguageCode, $contentType); + } + + public function newUserGroupCreateStruct(string $mainLanguageCode, ?ContentType $contentType = null): UserGroupCreateStruct + { + return $this->service->newUserGroupCreateStruct($mainLanguageCode, $contentType); + } + + public function newUserUpdateStruct(): UserUpdateStruct + { + return $this->service->newUserUpdateStruct(); + } + + public function newUserGroupUpdateStruct(): UserGroupUpdateStruct + { + return $this->service->newUserGroupUpdateStruct(); + } + + public function validatePassword(string $password, PasswordValidationContext $context = null): array + { + return $this->service->validatePassword($password, $context); + } + + public function getPasswordInfo(User $user): PasswordInfo + { + return $this->service->getPasswordInfo($user); + } +} + +class_alias(UserService::class, 'eZ\Publish\Core\Repository\SiteAccessAware\UserService'); diff --git a/src/lib/Repository/Strategy/ContentThumbnail/Field/ContentFieldStrategy.php b/src/lib/Repository/Strategy/ContentThumbnail/Field/ContentFieldStrategy.php new file mode 100644 index 0000000000..271074bc5d --- /dev/null +++ b/src/lib/Repository/Strategy/ContentThumbnail/Field/ContentFieldStrategy.php @@ -0,0 +1,82 @@ +addStrategy($strategy->getFieldTypeIdentifier(), $strategy); + } + } + } + + /** + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + */ + public function getThumbnail(Field $field, ?VersionInfo $versionInfo = null): ?Thumbnail + { + if (!$this->hasStrategy($field->fieldTypeIdentifier)) { + throw new NotFoundException('Field\ThumbnailStrategy', $field->fieldTypeIdentifier); + } + + $fieldStrategies = $this->strategies[$field->fieldTypeIdentifier]; + + /** @var \Ibexa\Contracts\Core\Repository\Strategy\ContentThumbnail\Field\FieldTypeBasedThumbnailStrategy $fieldStrategy */ + foreach ($fieldStrategies as $fieldStrategy) { + $thumbnail = $fieldStrategy->getThumbnail($field, $versionInfo); + + if ($thumbnail !== null) { + return $thumbnail; + } + } + + return null; + } + + public function hasStrategy(string $fieldTypeIdentifier): bool + { + return !empty($this->strategies[$fieldTypeIdentifier]); + } + + public function addStrategy(string $fieldTypeIdentifier, FieldTypeBasedThumbnailStrategy $thumbnailStrategy): void + { + $this->strategies[$fieldTypeIdentifier][] = $thumbnailStrategy; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Strategy\ContentThumbnail\Field\FieldTypeBasedThumbnailStrategy[]|\Traversable $thumbnailStrategies + */ + public function setStrategies(array $thumbnailStrategies): void + { + $this->strategies = []; + + foreach ($thumbnailStrategies as $thumbnailStrategy) { + $this->addStrategy($thumbnailStrategy->getFieldTypeIdentifier(), $thumbnailStrategy); + } + } +} + +class_alias(ContentFieldStrategy::class, 'eZ\Publish\Core\Repository\Strategy\ContentThumbnail\Field\ContentFieldStrategy'); diff --git a/src/lib/Repository/Strategy/ContentThumbnail/FirstMatchingFieldStrategy.php b/src/lib/Repository/Strategy/ContentThumbnail/FirstMatchingFieldStrategy.php new file mode 100644 index 0000000000..07acac5abe --- /dev/null +++ b/src/lib/Repository/Strategy/ContentThumbnail/FirstMatchingFieldStrategy.php @@ -0,0 +1,73 @@ +contentFieldStrategy = $contentFieldStrategy; + $this->fieldTypeService = $fieldTypeService; + } + + public function getThumbnail(ContentType $contentType, array $fields, ?VersionInfo $versionInfo = null): ?Thumbnail + { + $fieldDefinitions = $contentType->getFieldDefinitions(); + + foreach ($fieldDefinitions as $fieldDefinition) { + $field = $this->getFieldByIdentifier($fieldDefinition->getIdentifier(), $fields); + + if ($field === null) { + continue; + } + + $fieldType = $this->fieldTypeService->getFieldType($fieldDefinition->getFieldTypeIdentifier()); + + if ( + $fieldDefinition->isThumbnail() + && $this->contentFieldStrategy->hasStrategy($field->getFieldTypeIdentifier()) + && !$fieldType->isEmptyValue($field->getValue()) + ) { + return $this->contentFieldStrategy->getThumbnail($field, $versionInfo); + } + } + + return null; + } + + private function getFieldByIdentifier(string $identifier, array $fields): ?Field + { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Field $field */ + foreach ($fields as $field) { + if ($field->getFieldDefinitionIdentifier() === $identifier) { + return $field; + } + } + + return null; + } +} + +class_alias(FirstMatchingFieldStrategy::class, 'eZ\Publish\Core\Repository\Strategy\ContentThumbnail\FirstMatchingFieldStrategy'); diff --git a/src/lib/Repository/Strategy/ContentThumbnail/StaticStrategy.php b/src/lib/Repository/Strategy/ContentThumbnail/StaticStrategy.php new file mode 100644 index 0000000000..309952a438 --- /dev/null +++ b/src/lib/Repository/Strategy/ContentThumbnail/StaticStrategy.php @@ -0,0 +1,34 @@ +staticThumbnail = $staticThumbnail; + } + + public function getThumbnail(ContentType $contentType, array $fields, ?VersionInfo $versionInfo = null): Thumbnail + { + return new Thumbnail([ + 'resource' => $this->staticThumbnail, + ]); + } +} + +class_alias(StaticStrategy::class, 'eZ\Publish\Core\Repository\Strategy\ContentThumbnail\StaticStrategy'); diff --git a/src/lib/Repository/Strategy/ContentThumbnail/ThumbnailChainStrategy.php b/src/lib/Repository/Strategy/ContentThumbnail/ThumbnailChainStrategy.php new file mode 100644 index 0000000000..71a36ab186 --- /dev/null +++ b/src/lib/Repository/Strategy/ContentThumbnail/ThumbnailChainStrategy.php @@ -0,0 +1,43 @@ +strategies = $strategies; + } + + public function getThumbnail(ContentType $contentType, array $fields, ?VersionInfo $versionInfo = null): ?Thumbnail + { + foreach ($this->strategies as $strategy) { + $thumbnail = $strategy->getThumbnail($contentType, $fields, $versionInfo); + + if ($thumbnail !== null) { + return $thumbnail; + } + } + + return null; + } +} + +class_alias(ThumbnailChainStrategy::class, 'eZ\Publish\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy'); diff --git a/eZ/Publish/Core/Repository/Strategy/ContentValidator/ContentValidatorStrategy.php b/src/lib/Repository/Strategy/ContentValidator/ContentValidatorStrategy.php similarity index 81% rename from eZ/Publish/Core/Repository/Strategy/ContentValidator/ContentValidatorStrategy.php rename to src/lib/Repository/Strategy/ContentValidator/ContentValidatorStrategy.php index 301f260f1d..9b654ce827 100644 --- a/eZ/Publish/Core/Repository/Strategy/ContentValidator/ContentValidatorStrategy.php +++ b/src/lib/Repository/Strategy/ContentValidator/ContentValidatorStrategy.php @@ -6,18 +6,18 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\Strategy\ContentValidator; +namespace Ibexa\Core\Repository\Strategy\ContentValidator; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\SPI\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; /** * @internal Meant for internal use by Repository */ final class ContentValidatorStrategy implements ContentValidator { - /** @var \eZ\Publish\SPI\Repository\Validator\ContentValidator[] */ + /** @var \Ibexa\Contracts\Core\Repository\Validator\ContentValidator[] */ private $contentValidators; public function __construct(iterable $contentValidators) @@ -81,3 +81,5 @@ private function mergeErrors( return $fieldErrors; } } + +class_alias(ContentValidatorStrategy::class, 'eZ\Publish\Core\Repository\Strategy\ContentValidator\ContentValidatorStrategy'); diff --git a/src/lib/Repository/TokenService.php b/src/lib/Repository/TokenService.php new file mode 100644 index 0000000000..adf8d89b33 --- /dev/null +++ b/src/lib/Repository/TokenService.php @@ -0,0 +1,139 @@ +persistenceHandler = $persistenceHandler; + $this->defaultTokenGenerator = $defaultTokenGenerator; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Exception + */ + public function getToken( + string $tokenType, + string $token, + ?string $identifier = null + ): Token { + $type = $this->persistenceHandler->getTokenType($tokenType); + + return $this->buildDomainObject( + $this->persistenceHandler->getToken( + $type->identifier, + $token, + $identifier + ), + $type + ); + } + + /** + * @throws \Exception + */ + public function checkToken( + string $tokenType, + string $token, + ?string $identifier = null + ): bool { + try { + $token = $this->getToken($tokenType, $token, $identifier); + + return !$token->isRevoked(); + } catch (NotFoundException|UnauthorizedException $exception) { + return false; + } + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Exception + */ + public function generateToken( + string $type, + int $ttl, + ?string $identifier = null, + int $tokenLength = 64, + ?TokenGeneratorInterface $tokenGenerator = null + ): Token { + if ($tokenLength > Token::MAX_LENGTH) { + throw new TokenLengthException($tokenLength); + } + + $createStruct = new CreateStruct([ + 'type' => $type, + 'token' => ($tokenGenerator ?? $this->defaultTokenGenerator)->generateToken($tokenLength), + 'identifier' => $identifier, + 'ttl' => $ttl, + ]); + + $token = $this->persistenceHandler->createToken($createStruct); + $tokenType = $this->persistenceHandler->getTokenType($type); + + return $this->buildDomainObject( + $token, + $tokenType + ); + } + + public function revokeToken(Token $token): void + { + $this->persistenceHandler->revokeTokenById($token->getId()); + } + + public function revokeTokenByIdentifier(string $tokenType, ?string $identifier): void + { + $this->persistenceHandler->revokeTokenByIdentifier($tokenType, $identifier); + } + + public function deleteToken(Token $token): void + { + $this->persistenceHandler->deleteTokenById($token->getId()); + } + + /** + * @throws \Exception + */ + private function buildDomainObject( + PersistenceTokenValue $token, + PersistenceTokenTypeValue $tokenType + ): Token { + return new Token( + $token->id, + $tokenType->identifier, + $token->token, + $token->identifier, + new DateTimeImmutable('@' . $token->created), + new DateTimeImmutable('@' . $token->expires), + $token->revoked + ); + } +} diff --git a/src/lib/Repository/TrashService.php b/src/lib/Repository/TrashService.php new file mode 100644 index 0000000000..e32a0cea27 --- /dev/null +++ b/src/lib/Repository/TrashService.php @@ -0,0 +1,441 @@ +repository = $repository; + $this->persistenceHandler = $handler; + $this->nameSchemaService = $nameSchemaService; + // Union makes sure default settings are ignored if provided in argument + $this->settings = $settings + [ + //'defaultSetting' => array(), + ]; + $this->permissionCriterionResolver = $permissionCriterionResolver; + $this->permissionResolver = $permissionResolver; + $this->proxyDomainMapper = $proxyDomainMapper; + } + + /** + * Loads a trashed location object from its $id. + * + * Note that $id is identical to original location, which has been previously trashed + * + * @param mixed $trashItemId + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to read the trashed location + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException - if the location with the given id does not exist + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\TrashItem + */ + public function loadTrashItem(int $trashItemId): APITrashItem + { + $spiTrashItem = $this->persistenceHandler->trashHandler()->loadTrashItem($trashItemId); + $trash = $this->buildDomainTrashItemObject( + $spiTrashItem, + $this->repository->getContentService()->internalLoadContentById($spiTrashItem->contentId) + ); + if (!$this->permissionResolver->canUser('content', 'read', $trash->getContentInfo())) { + throw new UnauthorizedException('content', 'read'); + } + + if (!$this->permissionResolver->canUser('content', 'restore', $trash->getContentInfo())) { + throw new UnauthorizedException('content', 'restore'); + } + + return $trash; + } + + /** + * Sends $location and all its children to trash and returns the corresponding trash item. + * + * The current user may not have access to the returned trash item, check before using it. + * Content is left untouched. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to trash the given location + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\TrashItem|null null if location was deleted, otherwise TrashItem + */ + public function trash(Location $location): ?APITrashItem + { + if (empty($location->id)) { + throw new InvalidArgumentValue('id', $location->id, 'Location'); + } + + if (!$this->userHasPermissionsToRemove($location->getContentInfo(), $location)) { + throw new UnauthorizedException('content', 'remove'); + } + + $this->repository->beginTransaction(); + try { + $spiTrashItem = $this->persistenceHandler->trashHandler()->trashSubtree($location->id); + $this->persistenceHandler->urlAliasHandler()->locationDeleted($location->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + // Use internalLoadContent() as we want a trash item regardless of user access to the trash or not. + try { + return isset($spiTrashItem) + ? $this->buildDomainTrashItemObject( + $spiTrashItem, + $this->repository->getContentService()->internalLoadContentById($spiTrashItem->contentId) + ) + : null; + } catch (Exception $e) { + return null; + } + } + + /** + * Recovers the $trashedLocation at its original place if possible. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\TrashItem $trashItem + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location|null $newParentLocation + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to recover the trash item at the parent location location + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location the newly created or recovered location + */ + public function recover(APITrashItem $trashItem, Location $newParentLocation = null): Location + { + if (!is_numeric($trashItem->id)) { + throw new InvalidArgumentValue('id', $trashItem->id, 'TrashItem'); + } + + if ($newParentLocation === null && !is_numeric($trashItem->parentLocationId)) { + throw new InvalidArgumentValue('parentLocationId', $trashItem->parentLocationId, 'TrashItem'); + } + + if ($newParentLocation !== null && !is_numeric($newParentLocation->id)) { + throw new InvalidArgumentValue('parentLocationId', $newParentLocation->id, 'Location'); + } + + if (!$this->permissionResolver->canUser( + 'content', + 'restore', + $trashItem->getContentInfo(), + [$newParentLocation ?: $trashItem] + )) { + throw new UnauthorizedException('content', 'restore'); + } + + $this->repository->beginTransaction(); + try { + $newParentLocationId = $newParentLocation ? $newParentLocation->id : $trashItem->parentLocationId; + $newLocationId = $this->persistenceHandler->trashHandler()->recover( + $trashItem->id, + $newParentLocationId + ); + + $content = $this->repository->getContentService()->loadContent($trashItem->contentId); + $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content); + + // Publish URL aliases for recovered location + foreach ($urlAliasNames as $languageCode => $name) { + $this->persistenceHandler->urlAliasHandler()->publishUrlAliasForLocation( + $newLocationId, + $newParentLocationId, + $name, + $languageCode, + $content->contentInfo->alwaysAvailable + ); + } + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->repository->getLocationService()->loadLocation($newLocationId); + } + + /** + * Empties trash. + * + * All locations contained in the trash will be removed. Content objects will be removed + * if all locations of the content are gone. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to empty the trash + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResultList + */ + public function emptyTrash(): TrashItemDeleteResultList + { + if ($this->permissionResolver->hasAccess('content', 'cleantrash') === false) { + throw new UnauthorizedException('content', 'cleantrash'); + } + + $this->repository->beginTransaction(); + try { + // Persistence layer takes care of deleting content objects + $result = $this->persistenceHandler->trashHandler()->emptyTrash(); + $this->repository->commit(); + + return $result; + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Deletes a trash item. + * + * The corresponding content object will be removed + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\TrashItem $trashItem + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to delete this trash item + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResult + */ + public function deleteTrashItem(APITrashItem $trashItem): TrashItemDeleteResult + { + if (!$this->permissionResolver->canUser('content', 'cleantrash', $trashItem->getContentInfo())) { + throw new UnauthorizedException('content', 'cleantrash'); + } + + if (!is_numeric($trashItem->id)) { + throw new InvalidArgumentValue('id', $trashItem->id, 'TrashItem'); + } + + $this->repository->beginTransaction(); + try { + $trashItemDeleteResult = $this->persistenceHandler->trashHandler()->deleteTrashItem($trashItem->id); + $this->repository->commit(); + + return $trashItemDeleteResult; + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Returns a collection of Trashed locations contained in the trash, which are readable by the current user. + * + * $query allows to filter/sort the elements to be contained in the collection. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Trash\SearchResult + */ + public function findTrashItems(Query $query): SearchResult + { + if ($query->filter !== null && !$query->filter instanceof Criterion) { + throw new InvalidArgumentValue('query->filter', $query->filter, 'Query'); + } + + if ($query->sortClauses !== null) { + if (!is_array($query->sortClauses)) { + throw new InvalidArgumentValue('query->sortClauses', $query->sortClauses, 'Query'); + } + + foreach ($query->sortClauses as $sortClause) { + if (!$sortClause instanceof SortClause) { + throw new InvalidArgumentValue('query->sortClauses', 'only instances of the SortClause class are allowed'); + } + } + } + + if ($query->offset !== null && !is_numeric($query->offset)) { + throw new InvalidArgumentValue('query->offset', $query->offset, 'Query'); + } + + if ($query->limit !== null && !is_numeric($query->limit)) { + throw new InvalidArgumentValue('query->limit', $query->limit, 'Query'); + } + + $spiTrashResult = $this->persistenceHandler->trashHandler()->findTrashItems( + $query->filter, + $query->offset !== null && $query->offset > 0 ? (int)$query->offset : 0, + $query->limit !== null && $query->limit >= 0 ? (int)$query->limit : null, + $query->sortClauses + ); + + $trashItems = $this->buildDomainTrashItems($spiTrashResult->items); + $searchResult = new SearchResult(['items' => $trashItems, 'totalCount' => $spiTrashResult->totalCount]); + + return $searchResult; + } + + protected function buildDomainTrashItems(array $spiTrashItems): array + { + $trashItems = []; + // TODO: load content in bulk once API allows for it + foreach ($spiTrashItems as $spiTrashItem) { + try { + $trashItems[] = $this->buildDomainTrashItemObject( + $spiTrashItem, + $this->repository->getContentService()->loadContent($spiTrashItem->contentId) + ); + } catch (APIUnauthorizedException $e) { + // Do nothing, thus exclude items the current user doesn't have read access to. + } + } + + return $trashItems; + } + + protected function buildDomainTrashItemObject(Trashed $spiTrashItem, Content $content): APITrashItem + { + return new TrashItem( + [ + 'content' => $content, + 'contentInfo' => $content->contentInfo, + 'id' => $spiTrashItem->id, + 'priority' => $spiTrashItem->priority, + 'hidden' => $spiTrashItem->hidden, + 'invisible' => $spiTrashItem->invisible, + 'remoteId' => $spiTrashItem->remoteId, + 'parentLocationId' => $spiTrashItem->parentId, + 'pathString' => $spiTrashItem->pathString, + 'depth' => $spiTrashItem->depth, + 'sortField' => $spiTrashItem->sortField, + 'sortOrder' => $spiTrashItem->sortOrder, + 'trashed' => isset($spiTrashItem->trashed) + ? new DateTime('@' . $spiTrashItem->trashed) + : new DateTime('@0'), + 'removedLocationContentIdMap' => $spiTrashItem->removedLocationContentIdMap, + 'parentLocation' => $this->proxyDomainMapper->createLocationProxy($spiTrashItem->parentId), + ] + ); + } + + /** + * @param int $timestamp + * + * @return \DateTime + */ + protected function getDateTime($timestamp) + { + $dateTime = new DateTime(); + $dateTime->setTimestamp($timestamp); + + return $dateTime; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * + * @return bool + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + private function userHasPermissionsToRemove(ContentInfo $contentInfo, Location $location) + { + $versionInfo = $this->persistenceHandler->contentHandler()->loadVersionInfo( + $contentInfo->id, + $contentInfo->currentVersionNo + ); + $target = (new Target\Version())->deleteTranslations($versionInfo->languageCodes); + + if (!$this->permissionResolver->canUser('content', 'remove', $contentInfo, [$location, $target])) { + return false; + } + $contentRemoveCriterion = $this->permissionCriterionResolver->getPermissionsCriterion('content', 'remove'); + if (!$contentRemoveCriterion instanceof Criterion) { + return (bool)$contentRemoveCriterion; + } + $query = new Query( + [ + 'limit' => 0, + 'filter' => new CriterionLogicalAnd( + [ + new CriterionSubtree($location->pathString), + new CriterionLogicalNot($contentRemoveCriterion), + ] + ), + ] + ); + $result = $this->repository->getSearchService()->findContent($query, [], false); + + return $result->totalCount == 0; + } +} + +class_alias(TrashService::class, 'eZ\Publish\Core\Repository\TrashService'); diff --git a/src/lib/Repository/URLAliasService.php b/src/lib/Repository/URLAliasService.php new file mode 100644 index 0000000000..fb342f58cf --- /dev/null +++ b/src/lib/Repository/URLAliasService.php @@ -0,0 +1,841 @@ +repository = $repository; + $this->urlAliasHandler = $urlAliasHandler; + $this->nameSchemaService = $nameSchemaService; + $this->permissionResolver = $permissionResolver; + $this->languageResolver = $languageResolver; + } + + /** + * Create a user chosen $alias pointing to $location in $languageCode. + * + * This method runs URL filters and transformers before storing them. + * Hence the path returned in the URLAlias Value may differ from the given. + * $alwaysAvailable makes the alias available in all languages. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param string $path + * @param string $languageCode the languageCode for which this alias is valid + * @param bool $forwarding if true a redirect is performed + * @param bool $alwaysAvailable + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to create url alias + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the path already exists for the given context + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + */ + public function createUrlAlias( + Location $location, + string $path, + string $languageCode, + bool $forwarding = false, + bool $alwaysAvailable = false + ): URLAlias { + if (!$this->permissionResolver->canUser('content', 'urltranslator', $location)) { + throw new UnauthorizedException('content', 'urltranslator'); + } + + $path = $this->cleanUrl($path); + + $this->repository->beginTransaction(); + try { + $spiUrlAlias = $this->urlAliasHandler->createCustomUrlAlias( + $location->id, + $path, + $forwarding, + $languageCode, + $alwaysAvailable + ); + $this->repository->commit(); + } catch (ForbiddenException $e) { + $this->repository->rollback(); + throw new InvalidArgumentException( + '$path', + $e->getMessage(), + $e + ); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->buildUrlAliasDomainObject($spiUrlAlias, $path); + } + + /** + * Create a user chosen $alias pointing to a resource in $languageCode. + * + * This method does not handle location resources - if a user enters a location target + * the createCustomUrlAlias method has to be used. + * This method runs URL filters and and transformers before storing them. + * Hence the path returned in the URLAlias Value may differ from the given. + * + * $alwaysAvailable makes the alias available in all languages. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to create global + * url alias + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the path already exists for the given + * language or if resource is not valid + * + * @param string $resource + * @param string $path + * @param string $languageCode + * @param bool $forwarding + * @param bool $alwaysAvailable + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + */ + public function createGlobalUrlAlias( + string $resource, + string $path, + string $languageCode, + bool $forwarding = false, + bool $alwaysAvailable = false + ): URLAlias { + if ($this->permissionResolver->hasAccess('content', 'urltranslator') === false) { + throw new UnauthorizedException('content', 'urltranslator'); + } + + if (!preg_match('#^([a-zA-Z0-9_]+):(.+)$#', $resource, $matches)) { + throw new InvalidArgumentException('$resource', 'argument is not valid'); + } + + $path = $this->cleanUrl($path); + + if ($matches[1] === 'eznode' || 0 === strpos($resource, 'module:content/view/full/')) { + if ($matches[1] === 'eznode') { + $locationId = $matches[2]; + } else { + $resourcePath = explode('/', $matches[2]); + $locationId = end($resourcePath); + } + + $location = $this->repository->getLocationService()->loadLocation((int)$locationId); + + if (!$this->permissionResolver->canUser('content', 'urltranslator', $location)) { + throw new UnauthorizedException('content', 'urltranslator'); + } + + return $this->createUrlAlias( + $location, + $path, + $languageCode, + $forwarding, + $alwaysAvailable + ); + } + + $this->repository->beginTransaction(); + try { + $spiUrlAlias = $this->urlAliasHandler->createGlobalUrlAlias( + $matches[1] . ':' . $this->cleanUrl($matches[2]), + $path, + $forwarding, + $languageCode, + $alwaysAvailable + ); + $this->repository->commit(); + } catch (ForbiddenException $e) { + $this->repository->rollback(); + throw new InvalidArgumentException('$path', $e->getMessage(), $e); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->buildUrlAliasDomainObject($spiUrlAlias, $path); + } + + /** + * List of url aliases pointing to $location, sorted by language priority. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param bool $custom if true the user generated aliases are listed otherwise the autogenerated + * @param string $languageCode filters those which are valid for the given language + * @param bool|null $showAllTranslations If enabled will include all alias as if they where always available. + * @param string[]|null $prioritizedLanguages If set used as prioritized language codes, returning first match. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias[] + */ + public function listLocationAliases( + Location $location, + ?bool $custom = true, + ?string $languageCode = null, + ?bool $showAllTranslations = null, + ?array $prioritizedLanguages = null + ): iterable { + $spiUrlAliasList = $this->urlAliasHandler->listURLAliasesForLocation( + $location->id, + $custom + ); + if ($showAllTranslations === null) { + $showAllTranslations = $this->languageResolver->getShowAllTranslations(); + } + if ($prioritizedLanguages === null) { + $prioritizedLanguages = $this->languageResolver->getPrioritizedLanguages(); + } + $urlAliasList = []; + + foreach ($spiUrlAliasList as $spiUrlAlias) { + if ( + !$this->isUrlAliasLoadable( + $spiUrlAlias, + $languageCode, + $showAllTranslations, + $prioritizedLanguages + ) + ) { + continue; + } + + $path = $this->extractPath( + $spiUrlAlias, + $languageCode, + $showAllTranslations, + $prioritizedLanguages + ); + + if ($path === false) { + continue; + } + + $urlAliasList[$spiUrlAlias->id] = $this->buildUrlAliasDomainObject($spiUrlAlias, $path); + } + + $prioritizedAliasList = []; + foreach ($prioritizedLanguages as $prioritizedLanguageCode) { + foreach ($urlAliasList as $urlAlias) { + foreach ($urlAlias->languageCodes as $aliasLanguageCode) { + if ($aliasLanguageCode === $prioritizedLanguageCode) { + $prioritizedAliasList[$urlAlias->id] = $urlAlias; + break; + } + } + } + } + + // Add aliases not matched by prioritized language to the end of the list + return array_values($prioritizedAliasList + $urlAliasList); + } + + /** + * Determines alias language code. + * + * Method will return false if language code can't be matched against alias language codes or language settings. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\UrlAlias $spiUrlAlias + * @param string|null $languageCode + * @param bool $showAllTranslations + * @param string[] $prioritizedLanguageList + * + * @return string|bool + */ + protected function selectAliasLanguageCode( + SPIURLAlias $spiUrlAlias, + ?string $languageCode, + bool $showAllTranslations, + array $prioritizedLanguageList + ) { + if (isset($languageCode) && !in_array($languageCode, $spiUrlAlias->languageCodes)) { + return false; + } + + foreach ($prioritizedLanguageList as $prioritizedLanguageCode) { + if (in_array($prioritizedLanguageCode, $spiUrlAlias->languageCodes)) { + return $prioritizedLanguageCode; + } + } + + if ($spiUrlAlias->alwaysAvailable || $showAllTranslations) { + $lastLevelData = end($spiUrlAlias->pathData); + reset($lastLevelData['translations']); + + return key($lastLevelData['translations']); + } + + return false; + } + + /** + * Returns path extracted from normalized path data returned from persistence, using language settings. + * + * Will return false if path could not be determined. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\UrlAlias $spiUrlAlias + * @param string $languageCode + * @param bool $showAllTranslations + * @param string[] $prioritizedLanguageList + * + * @return string|bool + */ + protected function extractPath( + SPIURLAlias $spiUrlAlias, + $languageCode, + $showAllTranslations, + array $prioritizedLanguageList + ) { + $pathData = []; + $pathLevels = count($spiUrlAlias->pathData); + + foreach ($spiUrlAlias->pathData as $level => $levelEntries) { + if ($level === $pathLevels - 1) { + $prioritizedLanguageCode = $this->selectAliasLanguageCode( + $spiUrlAlias, + $languageCode, + $showAllTranslations, + $prioritizedLanguageList + ); + } else { + $prioritizedLanguageCode = $this->choosePrioritizedLanguageCode( + $levelEntries, + $showAllTranslations, + $prioritizedLanguageList + ); + } + + if ($prioritizedLanguageCode === false) { + return false; + } + + $pathData[$level] = $levelEntries['translations'][$prioritizedLanguageCode]; + } + + return implode('/', $pathData); + } + + /** + * Returns language code with highest priority. + * + * Will return false if language code could not be matched with language settings in place. + * + * @param array $entries + * @param bool $showAllTranslations + * @param string[] $prioritizedLanguageList + * + * @return string|bool + */ + protected function choosePrioritizedLanguageCode(array $entries, $showAllTranslations, array $prioritizedLanguageList) + { + foreach ($prioritizedLanguageList as $prioritizedLanguageCode) { + if (isset($entries['translations'][$prioritizedLanguageCode])) { + return $prioritizedLanguageCode; + } + } + + if ($entries['always-available'] || $showAllTranslations) { + reset($entries['translations']); + + return key($entries['translations']); + } + + return false; + } + + /** + * Matches path string with normalized path data returned from persistence. + * + * Returns matched path string (possibly case corrected) and array of corresponding language codes or false + * if path could not be matched. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\UrlAlias $spiUrlAlias + * @param string $path + * @param string $languageCode + * + * @return array + */ + protected function matchPath(SPIURLAlias $spiUrlAlias, $path, $languageCode) + { + $matchedPathElements = []; + $matchedPathLanguageCodes = []; + $pathElements = explode('/', $path); + $pathLevels = count($spiUrlAlias->pathData); + + foreach ($pathElements as $level => $pathElement) { + if ($level === $pathLevels - 1) { + $matchedLanguageCode = $this->selectAliasLanguageCode( + $spiUrlAlias, + $languageCode, + $this->languageResolver->getShowAllTranslations(), + $this->languageResolver->getPrioritizedLanguages() + ); + } else { + $matchedLanguageCode = $this->matchLanguageCode($spiUrlAlias->pathData[$level], $pathElement); + } + + if ($matchedLanguageCode === false) { + return [false, false]; + } + + $matchedPathLanguageCodes[] = $matchedLanguageCode; + $matchedPathElements[] = $spiUrlAlias->pathData[$level]['translations'][$matchedLanguageCode]; + } + + return [implode('/', $matchedPathElements), $matchedPathLanguageCodes]; + } + + /** + * @param array $pathElementData + * @param string $pathElement + * + * @return string|bool + */ + protected function matchLanguageCode(array $pathElementData, $pathElement) + { + foreach ($this->sortTranslationsByPrioritizedLanguages($pathElementData['translations']) as $translationData) { + if (strtolower($pathElement) === strtolower($translationData['text'])) { + return $translationData['lang']; + } + } + + return false; + } + + /** + * Needed when translations for the part of the alias are the same for multiple languages. + * In that case we need to ensure that more prioritized language is chosen. + * + * @param array $translations + * + * @return array + */ + private function sortTranslationsByPrioritizedLanguages(array $translations) + { + $sortedTranslations = []; + foreach ($this->languageResolver->getPrioritizedLanguages() as $languageCode) { + if (isset($translations[$languageCode])) { + $sortedTranslations[] = [ + 'lang' => $languageCode, + 'text' => $translations[$languageCode], + ]; + unset($translations[$languageCode]); + } + } + + foreach ($translations as $languageCode => $translation) { + $sortedTranslations[] = [ + 'lang' => $languageCode, + 'text' => $translation, + ]; + } + + return $sortedTranslations; + } + + /** + * Returns true or false depending if URL alias is loadable or not for language settings in place. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\UrlAlias $spiUrlAlias + * @param string|null $languageCode + * @param bool $showAllTranslations + * @param string[] $prioritizedLanguageList + * + * @return bool + */ + protected function isUrlAliasLoadable( + SPIURLAlias $spiUrlAlias, + ?string $languageCode, + bool $showAllTranslations, + array $prioritizedLanguageList + ) { + if (isset($languageCode) && !in_array($languageCode, $spiUrlAlias->languageCodes)) { + return false; + } + + if ($showAllTranslations) { + return true; + } + + foreach ($spiUrlAlias->pathData as $levelPathData) { + if ($levelPathData['always-available']) { + continue; + } + + foreach ($levelPathData['translations'] as $translationLanguageCode => $translation) { + if (in_array($translationLanguageCode, $prioritizedLanguageList)) { + continue 2; + } + } + + return false; + } + + return true; + } + + /** + * Returns true or false depending if URL alias is loadable or not for language settings in place. + * + * @param array $pathData + * @param array $languageCodes + * + * @return bool + */ + protected function isPathLoadable(array $pathData, array $languageCodes) + { + if ($this->languageResolver->getShowAllTranslations()) { + return true; + } + + foreach ($pathData as $level => $levelPathData) { + if ($levelPathData['always-available']) { + continue; + } + + if (in_array($languageCodes[$level], $this->languageResolver->getPrioritizedLanguages())) { + continue; + } + + return false; + } + + return true; + } + + /** + * List global aliases. + * + * @param string $languageCode filters those which are valid for the given language + * @param int $offset + * @param int $limit + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias[] + */ + public function listGlobalAliases(?string $languageCode = null, int $offset = 0, int $limit = -1): iterable + { + $urlAliasList = []; + $spiUrlAliasList = $this->urlAliasHandler->listGlobalURLAliases( + $languageCode, + $offset, + $limit + ); + + foreach ($spiUrlAliasList as $spiUrlAlias) { + $path = $this->extractPath( + $spiUrlAlias, + $languageCode, + $this->languageResolver->getShowAllTranslations(), + $this->languageResolver->getPrioritizedLanguages() + ); + + if ($path === false) { + continue; + } + + $urlAliasList[] = $this->buildUrlAliasDomainObject($spiUrlAlias, $path); + } + + return $urlAliasList; + } + + /** + * Removes urls aliases. + * + * This method does not remove autogenerated aliases for locations. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove url alias + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if alias list contains + * autogenerated alias + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias[] $aliasList + */ + public function removeAliases(array $aliasList): void + { + if ($this->permissionResolver->hasAccess('content', 'urltranslator') === false) { + throw new UnauthorizedException('content', 'urltranslator'); + } + + $spiUrlAliasList = []; + foreach ($aliasList as $alias) { + if (!$alias->isCustom) { + throw new InvalidArgumentException( + '$aliasList', + 'The alias list contains an autogenerated alias' + ); + } + $spiUrlAliasList[] = $this->buildSPIUrlAlias($alias); + } + + $this->repository->beginTransaction(); + try { + $this->urlAliasHandler->removeURLAliases($spiUrlAliasList); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Builds persistence domain object. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias $urlAlias + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UrlAlias + */ + protected function buildSPIUrlAlias(URLAlias $urlAlias) + { + return new SPIURLAlias( + [ + 'id' => $urlAlias->id, + 'type' => $urlAlias->type, + 'destination' => $urlAlias->destination, + 'isCustom' => $urlAlias->isCustom, + ] + ); + } + + /** + * looks up the URLAlias for the given url. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the path does not exist or is not valid for the given language + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the path exceeded maximum depth level + * + * @param string $url + * @param string|null $languageCode + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + */ + public function lookup(string $url, ?string $languageCode = null): URLAlias + { + $url = $this->cleanUrl($url); + + $spiUrlAlias = $this->urlAliasHandler->lookup($url); + + list($path, $languageCodes) = $this->matchPath($spiUrlAlias, $url, $languageCode); + if ($path === false || !$this->isPathLoadable($spiUrlAlias->pathData, $languageCodes)) { + throw new NotFoundException('URLAlias', $url); + } + + return $this->buildUrlAliasDomainObject($spiUrlAlias, $path); + } + + /** + * Returns the URL alias for the given location in the given language. + * + * If $languageCode is null the method returns the url alias in the most prioritized language. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if no url alias exist for the given language + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param string|null $languageCode + * @param bool|null $showAllTranslations + * @param string[]|null $prioritizedLanguageList + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + */ + public function reverseLookup( + Location $location, + ?string $languageCode = null, + ?bool $showAllTranslations = null, + ?array $prioritizedLanguageList = null + ): URLAlias { + if ($showAllTranslations === null) { + $showAllTranslations = $this->languageResolver->getShowAllTranslations(); + } + if ($prioritizedLanguageList === null) { + $prioritizedLanguageList = $this->languageResolver->getPrioritizedLanguages(); + } + $urlAliases = $this->listLocationAliases( + $location, + false, + $languageCode, + $showAllTranslations, + $prioritizedLanguageList + ); + + foreach ($prioritizedLanguageList as $prioritizedLanguageCode) { + foreach ($urlAliases as $urlAlias) { + if (in_array($prioritizedLanguageCode, $urlAlias->languageCodes)) { + return $urlAlias; + } + } + } + + foreach ($urlAliases as $urlAlias) { + if ($urlAlias->alwaysAvailable) { + return $urlAlias; + } + } + + if (!empty($urlAliases) && $showAllTranslations) { + return reset($urlAliases); + } + + throw new NotFoundException('URLAlias', $location->id); + } + + /** + * Loads URL alias by given $id. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * + * @param string $id + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + */ + public function load(string $id): URLAlias + { + $spiUrlAlias = $this->urlAliasHandler->loadUrlAlias($id); + $path = $this->extractPath( + $spiUrlAlias, + null, + $this->languageResolver->getShowAllTranslations(), + $this->languageResolver->getPrioritizedLanguages() + ); + + if ($path === false) { + throw new NotFoundException('URLAlias', $id); + } + + return $this->buildUrlAliasDomainObject($spiUrlAlias, $path); + } + + /** + * Refresh all system URL aliases for the given Location (and historize outdated if needed). + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Exception any unexpected exception that might come from custom Field Type implementation + */ + public function refreshSystemUrlAliasesForLocation(Location $location): void + { + if (!$this->repository->getPermissionResolver()->canUser('content', 'urltranslator', $location)) { + throw new UnauthorizedException('content', 'urltranslator'); + } + + $this->repository->beginTransaction(); + try { + $content = $location->getContent(); + $urlAliasNames = $this->nameSchemaService->resolveUrlAliasSchema($content); + foreach ($urlAliasNames as $languageCode => $name) { + $this->urlAliasHandler->publishUrlAliasForLocation( + $location->id, + $location->parentLocationId, + $name, + $languageCode, + $content->contentInfo->alwaysAvailable + ); + } + $this->urlAliasHandler->repairBrokenUrlAliasesForLocation($location->id); + + // handle URL aliases for missing Translations + $this->urlAliasHandler->archiveUrlAliasesForDeletedTranslations( + $location->id, + $location->parentLocationId, + $content->getVersionInfo()->languageCodes + ); + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Delete global, system or custom URL alias pointing to non-existent Locations. + * + * @return int Number of removed URL aliases + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function deleteCorruptedUrlAliases(): int + { + if ($this->repository->getPermissionResolver()->hasAccess('content', 'urltranslator') === false) { + throw new UnauthorizedException('content', 'urltranslator'); + } + + return $this->urlAliasHandler->deleteCorruptedUrlAliases(); + } + + /** + * @param string $url + * + * @return string + */ + protected function cleanUrl(string $url): string + { + return trim($url, '/ '); + } + + /** + * Builds API UrlAlias object from given SPI UrlAlias object. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\UrlAlias $spiUrlAlias + * @param string $path + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + */ + protected function buildUrlAliasDomainObject(SPIURLAlias $spiUrlAlias, string $path): URLAlias + { + return new URLAlias( + [ + 'id' => $spiUrlAlias->id, + 'type' => $spiUrlAlias->type, + 'destination' => $spiUrlAlias->destination, + 'languageCodes' => $spiUrlAlias->languageCodes, + 'alwaysAvailable' => $spiUrlAlias->alwaysAvailable, + 'path' => '/' . $path, + 'isHistory' => $spiUrlAlias->isHistory, + 'isCustom' => $spiUrlAlias->isCustom, + 'forward' => $spiUrlAlias->forward, + ] + ); + } +} + +class_alias(URLAliasService::class, 'eZ\Publish\Core\Repository\URLAliasService'); diff --git a/src/lib/Repository/URLService.php b/src/lib/Repository/URLService.php new file mode 100644 index 0000000000..4ad6f0d350 --- /dev/null +++ b/src/lib/Repository/URLService.php @@ -0,0 +1,268 @@ +repository = $repository; + $this->urlHandler = $urlHandler; + $this->permissionResolver = $permissionResolver; + } + + /** + * {@inheritdoc} + */ + public function findUrls(URLQuery $query): SearchResult + { + if ($this->permissionResolver->hasAccess('url', 'view') === false) { + throw new UnauthorizedException('url', 'view'); + } + + if ($query->offset !== null && !is_numeric($query->offset)) { + throw new InvalidArgumentValue('offset', $query->offset); + } + + if ($query->limit !== null && !is_numeric($query->limit)) { + throw new InvalidArgumentValue('limit', $query->limit); + } + + $results = $this->urlHandler->find($query); + + $items = []; + foreach ($results['items'] as $url) { + $items[] = $this->buildDomainObject($url); + } + + return new SearchResult([ + 'totalCount' => $results['count'], + 'items' => $items, + ]); + } + + /** + * {@inheritdoc} + */ + public function updateUrl(URL $url, URLUpdateStruct $struct): URL + { + if (!$this->permissionResolver->canUser('url', 'update', $url)) { + throw new UnauthorizedException('url', 'update'); + } + + if ($struct->url !== null && !$this->isUnique($url->id, $struct->url)) { + throw new InvalidArgumentException('struct', 'The URL already exists'); + } + + $updateStruct = $this->buildUpdateStruct($this->loadById($url->id), $struct); + + $this->repository->beginTransaction(); + try { + $this->urlHandler->updateUrl($url->id, $updateStruct); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadById($url->id); + } + + /** + * {@inheritdoc} + */ + public function loadById(int $id): URL + { + $url = $this->buildDomainObject( + $this->urlHandler->loadById($id) + ); + + if (!$this->permissionResolver->canUser('url', 'view', $url)) { + throw new UnauthorizedException('url', 'view'); + } + + return $url; + } + + /** + * {@inheritdoc} + */ + public function loadByUrl(string $url): URL + { + $apiUrl = $this->buildDomainObject( + $this->urlHandler->loadByUrl($url) + ); + + if (!$this->permissionResolver->canUser('url', 'view', $apiUrl)) { + throw new UnauthorizedException('url', 'view'); + } + + return $apiUrl; + } + + /** + * {@inheritdoc} + */ + public function createUpdateStruct(): URLUpdateStruct + { + return new URLUpdateStruct(); + } + + /** + * {@inheritdoc} + */ + public function findUsages(URL $url, int $offset = 0, int $limit = -1): UsageSearchResult + { + $contentIds = $this->urlHandler->findUsages($url->id); + if (empty($contentIds)) { + return new UsageSearchResult(); + } + + $query = new Query(); + $query->filter = new ContentCriterion\LogicalAnd([ + new ContentCriterion\ContentId($contentIds), + new ContentCriterion\Visibility(ContentCriterion\Visibility::VISIBLE), + ]); + + $query->offset = $offset; + if ($limit > -1) { + $query->limit = $limit; + } + + $searchResults = $this->repository->getSearchService()->findContentInfo($query); + + $usageResults = new UsageSearchResult(); + $usageResults->totalCount = $searchResults->totalCount; + foreach ($searchResults->searchHits as $hit) { + $usageResults->items[] = $hit->valueObject; + } + + return $usageResults; + } + + /** + * Builds domain object from ValueObject returned by Persistence API. + * + * @param \Ibexa\Contracts\Core\Persistence\URL\URL $data + * + * @return \Ibexa\Contracts\Core\Repository\Values\URL\URL + */ + protected function buildDomainObject(SPIUrl $data): URL + { + return new URL([ + 'id' => $data->id, + 'url' => $data->url, + 'isValid' => $data->isValid, + 'lastChecked' => $this->createDateTime($data->lastChecked), + 'created' => $this->createDateTime($data->created), + 'modified' => $this->createDateTime($data->modified), + ]); + } + + /** + * Builds SPI update structure used by Persistence API. + * + * @param \Ibexa\Contracts\Core\Repository\Values\URL\URL $url + * @param \Ibexa\Contracts\Core\Repository\Values\URL\URLUpdateStruct $data + * + * @return \Ibexa\Contracts\Core\Persistence\URL\URLUpdateStruct + */ + protected function buildUpdateStruct(URL $url, URLUpdateStruct $data): SPIUrlUpdateStruct + { + $updateStruct = new SPIUrlUpdateStruct(); + + if ($data->url !== null && $url->url !== $data->url) { + $updateStruct->url = $data->url; + // Reset URL validity + $updateStruct->lastChecked = 0; + $updateStruct->isValid = true; + } else { + $updateStruct->url = $url->url; + + if ($data->lastChecked !== null) { + $updateStruct->lastChecked = $data->lastChecked->getTimestamp(); + } elseif ($data->lastChecked !== null) { + $updateStruct->lastChecked = $url->lastChecked->getTimestamp(); + } else { + $updateStruct->lastChecked = 0; + } + + if ($data->isValid !== null) { + $updateStruct->isValid = $data->isValid; + } else { + $updateStruct->isValid = $url->isValid; + } + } + + return $updateStruct; + } + + /** + * Check if URL is unique. + * + * @param int $id + * @param string $url + * + * @return bool + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + protected function isUnique(int $id, string $url): bool + { + try { + return $this->loadByUrl($url)->id === $id; + } catch (NotFoundException $e) { + return true; + } + } + + private function createDateTime(?int $timestamp): ?DateTimeInterface + { + if ($timestamp > 0) { + return new DateTime("@{$timestamp}"); + } + + return null; + } +} + +class_alias(URLService::class, 'eZ\Publish\Core\Repository\URLService'); diff --git a/src/lib/Repository/URLWildcardService.php b/src/lib/Repository/URLWildcardService.php new file mode 100644 index 0000000000..f3c258d75d --- /dev/null +++ b/src/lib/Repository/URLWildcardService.php @@ -0,0 +1,333 @@ +repository = $repository; + $this->urlWildcardHandler = $urlWildcardHandler; + $this->permissionResolver = $permissionResolver; + $this->settings = $settings; + } + + /** + * Creates a new url wildcard. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the $sourceUrl pattern already exists + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to create url wildcards + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException if the number of "*" patterns in $sourceUrl and + * the numbers in {\d} placeholders in $destinationUrl does not match. + * + * @param string $sourceUrl + * @param string $destinationUrl + * @param bool $forward + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\UrlWildcard + */ + public function create(string $sourceUrl, string $destinationUrl, bool $forward = false): UrlWildcard + { + if (false === $this->permissionResolver->hasAccess('content', 'urltranslator')) { + throw new UnauthorizedException('content', 'urltranslator'); + } + + $sourceUrl = $this->cleanUrl($sourceUrl); + $destinationUrl = $this->cleanUrl($destinationUrl); + + if ($this->urlWildcardHandler->exactSourceUrlExists($this->cleanPath($sourceUrl))) { + throw new InvalidArgumentException( + '$sourceUrl', + 'Pattern already exists' + ); + } + + $this->validateUrls($sourceUrl, $destinationUrl); + + $this->repository->beginTransaction(); + try { + $spiUrlWildcard = $this->urlWildcardHandler->create( + $sourceUrl, + $destinationUrl, + $forward + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->buildUrlWildcardDomainObject($spiUrlWildcard); + } + + public function update( + URLWildcard $urlWildcard, + URLWildcardUpdateStruct $updateStruct + ): void { + if (false === $this->permissionResolver->canUser('content', 'urltranslator', $urlWildcard)) { + throw new UnauthorizedException('content', 'urltranslator'); + } + + $destinationUrl = $updateStruct->destinationUrl; + $sourceUrl = $updateStruct->sourceUrl; + + $this->validateUrls($sourceUrl, $destinationUrl); + + $this->repository->beginTransaction(); + + try { + $this->urlWildcardHandler->update( + $urlWildcard->id, + $sourceUrl, + $destinationUrl, + $updateStruct->forward + ); + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\UrlWildcard $urlWildcard the url wildcard to remove + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the user is not allowed to remove url wildcards + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + public function remove(URLWildcard $urlWildcard): void + { + if (false === $this->permissionResolver->canUser('content', 'urltranslator', $urlWildcard)) { + throw new UnauthorizedException('content', 'urltranslator'); + } + + $this->repository->beginTransaction(); + try { + $this->urlWildcardHandler->remove( + $urlWildcard->id + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Loads a url wild card. + * + * @param int $id + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\UrlWildcard + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the url wild card was not found + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException + */ + public function load(int $id): UrlWildcard + { + return $this->buildUrlWildcardDomainObject( + $this->urlWildcardHandler->load($id) + ); + } + + /** + * Loads all url wild card (paged). + * + * @param int $offset + * @param int $limit + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\UrlWildcard[] + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Base\Exceptions\UnauthorizedException + */ + public function loadAll(int $offset = 0, int $limit = -1): iterable + { + $spiUrlWildcards = $this->urlWildcardHandler->loadAll( + $offset, + $limit + ); + + $urlWildcards = []; + foreach ($spiUrlWildcards as $spiUrlWildcard) { + $urlWildcards[] = $this->buildUrlWildcardDomainObject($spiUrlWildcard); + } + + return $urlWildcards; + } + + /** + * Translates an url to an existing uri resource based on the + * source/destination patterns of the url wildcard. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the url could not be translated + * + * @param string $url + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcardTranslationResult + */ + public function translate(string $url): URLWildcardTranslationResult + { + $spiWildcard = $this->urlWildcardHandler->translate($this->cleanPath($url)); + + return new URLWildcardTranslationResult( + [ + 'uri' => $spiWildcard->destinationUrl, + 'forward' => $spiWildcard->forward, + ] + ); + } + + public function findUrlWildcards(URLWildcardQuery $query): SearchResult + { + if ($query->offset !== null && !is_numeric($query->offset)) { + throw new InvalidArgumentValue('offset', $query->offset); + } + + if ($query->limit !== null && !is_numeric($query->limit)) { + throw new InvalidArgumentValue('limit', $query->limit); + } + + $results = $this->urlWildcardHandler->find($query); + + $items = []; + foreach ($results['items'] as $urlWildcard) { + $items[] = $this->buildUrlWildcardDomainObject($urlWildcard); + } + + return new SearchResult([ + 'totalCount' => $results['count'], + 'items' => $items, + ]); + } + + /** + * {@inheritDoc} + */ + public function countAll(): int + { + return $this->urlWildcardHandler->countAll(); + } + + /** + * Builds API UrlWildcard object from given SPI UrlWildcard object. + * + * @param \Ibexa\Contracts\Core\Persistence\Content\UrlWildcard $wildcard + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard + */ + private function buildUrlWildcardDomainObject(SPIUrlWildcard $wildcard): URLWildcard + { + return new URLWildcard( + [ + 'id' => $wildcard->id, + 'destinationUrl' => $wildcard->destinationUrl, + 'sourceUrl' => $wildcard->sourceUrl, + 'forward' => $wildcard->forward, + ] + ); + } + + /** + * Removes leading and trailing slashes and spaces. + * + * @param string $url + * + * @return string + */ + private function cleanUrl(string $url): string + { + return '/' . trim($url, '/ '); + } + + /** + * Removes leading slash from given path. + * + * @param string $path + * + * @return string + */ + private function cleanPath(string $path): string + { + return trim($path, '/ '); + } + + /** + * @param string $sourceUrl + * @param string $destinationUrl + * + * @throws \Ibexa\Core\Base\Exceptions\ContentValidationException + */ + private function validateUrls(string $sourceUrl, string $destinationUrl): void + { + preg_match_all('(\\*)', $sourceUrl, $patterns); + preg_match_all('({(\d+)})', $destinationUrl, $placeholders); + + if (empty($patterns) || empty($placeholders)) { + throw new ContentValidationException('Invalid URL wildcards provided.'); + } + + $patterns = array_map('intval', $patterns[0]); + $placeholders = array_map('intval', $placeholders[1]); + + if (!empty($placeholders) && max($placeholders) > count($patterns)) { + throw new ContentValidationException('Placeholders do not match the wildcards.'); + } + } +} + +class_alias(URLWildcardService::class, 'eZ\Publish\Core\Repository\URLWildcardService'); diff --git a/src/lib/Repository/User/Exception/UnsupportedPasswordHashType.php b/src/lib/Repository/User/Exception/UnsupportedPasswordHashType.php new file mode 100644 index 0000000000..3dc3603397 --- /dev/null +++ b/src/lib/Repository/User/Exception/UnsupportedPasswordHashType.php @@ -0,0 +1,21 @@ +defaultHashType = $hashType; + } + + public function getSupportedHashTypes(): array + { + return User::SUPPORTED_PASSWORD_HASHES; + } + + public function isHashTypeSupported(int $hashType): bool + { + return in_array($hashType, $this->getSupportedHashTypes(), true); + } + + public function getDefaultHashType(): int + { + return $this->defaultHashType; + } + + /** + * @throws \Ibexa\Core\Repository\User\Exception\UnsupportedPasswordHashType + */ + public function createPasswordHash( + #[\SensitiveParameter] + string $password, + ?int $hashType = null + ): string { + $hashType = $hashType ?? $this->defaultHashType; + + switch ($hashType) { + case User::PASSWORD_HASH_BCRYPT: + return password_hash($password, PASSWORD_BCRYPT); + + case User::PASSWORD_HASH_PHP_DEFAULT: + return password_hash($password, PASSWORD_DEFAULT); + + default: + throw new UnsupportedPasswordHashType($hashType); + } + } + + public function isValidPassword( + #[\SensitiveParameter] + string $plainPassword, + #[\SensitiveParameter] + string $passwordHash, + ?int $hashType = null + ): bool { + if ($hashType === User::PASSWORD_HASH_BCRYPT || $hashType === User::PASSWORD_HASH_PHP_DEFAULT) { + // In case of bcrypt let PHP's password functionality do its magic + return password_verify($plainPassword, $passwordHash); + } + + return $passwordHash === $this->createPasswordHash($plainPassword, $hashType); + } +} + +class_alias(PasswordHashService::class, 'eZ\Publish\Core\Repository\User\PasswordHashService'); diff --git a/src/lib/Repository/User/PasswordHashServiceInterface.php b/src/lib/Repository/User/PasswordHashServiceInterface.php new file mode 100644 index 0000000000..3579da390e --- /dev/null +++ b/src/lib/Repository/User/PasswordHashServiceInterface.php @@ -0,0 +1,21 @@ +passwordHashService = $passwordHashService; } /** - * @return \eZ\Publish\SPI\FieldType\ValidationError[] + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] */ public function validatePassword( + #[\SensitiveParameter] string $password, FieldDefinition $userFieldDefinition, ?User $user = null @@ -114,3 +115,5 @@ private function userPasswordIsTheSame(string $password, APIUser $user): bool ); } } + +class_alias(PasswordValidator::class, 'eZ\Publish\Core\Repository\User\PasswordValidator'); diff --git a/src/lib/Repository/User/PasswordValidatorInterface.php b/src/lib/Repository/User/PasswordValidatorInterface.php new file mode 100644 index 0000000000..0ed961f324 --- /dev/null +++ b/src/lib/Repository/User/PasswordValidatorInterface.php @@ -0,0 +1,32 @@ +repository = $repository; + $this->userPreferenceHandler = $userPreferenceHandler; + } + + /** + * {@inheritdoc} + */ + public function loadUserPreferences(int $offset = 0, int $limit = 25): UserPreferenceList + { + $currentUserId = $this->getCurrentUserId(); + + $list = new UserPreferenceList(); + + $list->totalCount = $this->userPreferenceHandler->countUserPreferences($currentUserId); + if ($list->totalCount > 0) { + $list->items = array_map(function (UserPreference $spiUserPreference) { + return $this->buildDomainObject($spiUserPreference); + }, $this->userPreferenceHandler->loadUserPreferences($currentUserId, $offset, $limit)); + } + + return $list; + } + + /** + * {@inheritdoc} + */ + public function setUserPreference(array $userPreferenceSetStructs): void + { + $spiSetStructs = []; + foreach ($userPreferenceSetStructs as $key => $userPreferenceSetStruct) { + if (empty($userPreferenceSetStruct->name)) { + throw new InvalidArgumentException('name', $userPreferenceSetStruct->name . ' at index ' . $key); + } + + $value = $userPreferenceSetStruct->value; + + if (is_object($value) && !method_exists($value, '__toString')) { + throw new InvalidArgumentException('value', 'Cannot convert value to string at index ' . $key); + } + + try { + $value = (string)$userPreferenceSetStruct->value; + } catch (\Exception $exception) { + throw new InvalidArgumentException('value', 'Cannot convert value to string at index ' . $key); + } + + $spiSetStruct = new UserPreferenceSetStruct(); + $spiSetStruct->userId = $this->getCurrentUserId(); + $spiSetStruct->name = $userPreferenceSetStruct->name; + $spiSetStruct->value = $value; + + $spiSetStructs[] = $spiSetStruct; + } + + $this->repository->beginTransaction(); + try { + foreach ($spiSetStructs as $spiSetStruct) { + $this->userPreferenceHandler->setUserPreference($spiSetStruct); + } + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * {@inheritdoc} + */ + public function getUserPreference(string $userPreferenceName): APIUserPreference + { + $currentUserId = $this->getCurrentUserId(); + + $userPreference = $this->userPreferenceHandler->getUserPreferenceByUserIdAndName( + $currentUserId, + $userPreferenceName + ); + + return $this->buildDomainObject($userPreference); + } + + /** + * {@inheritdoc} + */ + public function getUserPreferenceCount(): int + { + return $this->userPreferenceHandler->countUserPreferences( + $this->getCurrentUserId() + ); + } + + /** + * Builds UserPreference domain object from ValueObject returned by Persistence API. + * + * @param \Ibexa\Contracts\Core\Persistence\UserPreference\UserPreference $spiUserPreference + * + * @return \Ibexa\Contracts\Core\Repository\Values\UserPreference\UserPreference + */ + protected function buildDomainObject(UserPreference $spiUserPreference): APIUserPreference + { + return new APIUserPreference([ + 'name' => $spiUserPreference->name, + 'value' => $spiUserPreference->value, + ]); + } + + private function getCurrentUserId(): int + { + return $this->repository + ->getPermissionResolver() + ->getCurrentUserReference() + ->getUserId(); + } +} + +class_alias(UserPreferenceService::class, 'eZ\Publish\Core\Repository\UserPreferenceService'); diff --git a/src/lib/Repository/UserService.php b/src/lib/Repository/UserService.php new file mode 100644 index 0000000000..b97730535d --- /dev/null +++ b/src/lib/Repository/UserService.php @@ -0,0 +1,1417 @@ +logger = $logger; + } + + /** + * Setups service with reference to repository object that created it & corresponding handler. + */ + public function __construct( + RepositoryInterface $repository, + PermissionResolver $permissionResolver, + Handler $userHandler, + LocationHandler $locationHandler, + PasswordHashService $passwordHashGenerator, + PasswordValidatorInterface $passwordValidator, + ConfigResolverInterface $configResolver, + array $settings = [] + ) { + $this->repository = $repository; + $this->permissionResolver = $permissionResolver; + $this->userHandler = $userHandler; + $this->locationHandler = $locationHandler; + // Union makes sure default settings are ignored if provided in argument + $this->settings = $settings + [ + 'defaultUserPlacement' => 12, + 'userClassID' => 4, // @deprecated, use `user_content_type_identifier` configuration instead + 'userGroupClassID' => 3, + 'hashType' => $passwordHashGenerator->getDefaultHashType(), + 'siteName' => 'ibexa.co', + ]; + $this->passwordHashService = $passwordHashGenerator; + $this->passwordValidator = $passwordValidator; + $this->configResolver = $configResolver; + } + + /** + * Creates a new user group using the data provided in the ContentCreateStruct parameter. + * + * In 4.x in the content type parameter in the profile is ignored + * - the content type is determined via configuration and can be set to null. + * The returned version is published. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroupCreateStruct $userGroupCreateStruct a structure for setting all necessary data to create this user group + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $parentGroup + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the input structure has invalid data + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException if a field in the $userGroupCreateStruct is not valid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException if a required field is missing or set to an empty value + */ + public function createUserGroup(APIUserGroupCreateStruct $userGroupCreateStruct, APIUserGroup $parentGroup): APIUserGroup + { + $contentService = $this->repository->getContentService(); + $locationService = $this->repository->getLocationService(); + $contentTypeService = $this->repository->getContentTypeService(); + + if ($userGroupCreateStruct->contentType === null) { + $userGroupContentType = $contentTypeService->loadContentType($this->settings['userGroupClassID']); + $userGroupCreateStruct->contentType = $userGroupContentType; + } + + $loadedParentGroup = $this->loadUserGroup($parentGroup->id); + + if ($loadedParentGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { + throw new InvalidArgumentException('parentGroup', 'parent User Group has no main Location'); + } + + $locationCreateStruct = $locationService->newLocationCreateStruct( + $loadedParentGroup->getVersionInfo()->getContentInfo()->mainLocationId + ); + + $this->repository->beginTransaction(); + try { + $contentDraft = $contentService->createContent($userGroupCreateStruct, [$locationCreateStruct]); + $publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo()); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->buildDomainUserGroupObject($publishedContent); + } + + /** + * Loads a user group for the given id. + * + * @param mixed $id + * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the user group with the given id was not found + */ + public function loadUserGroup(int $id, array $prioritizedLanguages = []): APIUserGroup + { + $content = $this->repository->getContentService()->loadContent($id, $prioritizedLanguages); + + return $this->buildDomainUserGroupObject($content); + } + + public function loadUserGroupByRemoteId(string $remoteId, array $prioritizedLanguages = []): APIUserGroup + { + $content = $this->repository->getContentService()->loadContentByRemoteId($remoteId, $prioritizedLanguages); + + return $this->buildDomainUserGroupObject($content); + } + + /** + * Loads the sub groups of a user group. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroup + * @param int $offset the start offset for paging + * @param int $limit the number of user groups returned + * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup[] + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read the user group + */ + public function loadSubUserGroups(APIUserGroup $userGroup, int $offset = 0, int $limit = 25, array $prioritizedLanguages = []): iterable + { + $locationService = $this->repository->getLocationService(); + + $loadedUserGroup = $this->loadUserGroup($userGroup->id); + if (!$this->permissionResolver->canUser('content', 'read', $loadedUserGroup)) { + throw new UnauthorizedException('content', 'read'); + } + + if ($loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { + return []; + } + + $mainGroupLocation = $locationService->loadLocation( + $loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId + ); + + $searchResult = $this->searchSubGroups($mainGroupLocation, $offset, $limit); + if ($searchResult->totalCount == 0) { + return []; + } + + $subUserGroups = []; + foreach ($searchResult->searchHits as $searchHit) { + $subUserGroups[] = $this->buildDomainUserGroupObject( + $this->repository->getContentService()->internalLoadContentById( + $searchHit->valueObject->contentInfo->id, + $prioritizedLanguages + ) + ); + } + + return $subUserGroups; + } + + /** + * Returns (searches) subgroups of a user group described by its main location. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param int $offset + * @param int $limit + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + */ + protected function searchSubGroups(Location $location, int $offset = 0, int $limit = 25): SearchResult + { + $searchQuery = new LocationQuery(); + + $searchQuery->offset = $offset; + $searchQuery->limit = $limit; + + $searchQuery->filter = new CriterionLogicalAnd([ + new CriterionContentTypeId($this->settings['userGroupClassID']), + new CriterionParentLocationId($location->id), + ]); + + $searchQuery->sortClauses = $location->getSortClauses(); + + return $this->repository->getSearchService()->findLocations($searchQuery, [], false); + } + + /** + * Removes a user group. + * + * the users which are not assigned to other groups will be deleted. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroup + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to create a user group + */ + public function deleteUserGroup(APIUserGroup $userGroup): iterable + { + $loadedUserGroup = $this->loadUserGroup($userGroup->id); + + $this->repository->beginTransaction(); + try { + foreach ($this->userHandler->loadRoleAssignmentsByGroupId($userGroup->id) as $roleAssignment) { + $this->userHandler->removeRoleAssignment($roleAssignment->id); + } + //@todo: what happens to sub user groups and users below sub user groups + $affectedLocationIds = $this->repository->getContentService()->deleteContent( + $loadedUserGroup->getVersionInfo()->getContentInfo() + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $affectedLocationIds; + } + + /** + * Moves the user group to another parent. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroup + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $newParent + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to move the user group + */ + public function moveUserGroup(APIUserGroup $userGroup, APIUserGroup $newParent): void + { + $loadedUserGroup = $this->loadUserGroup($userGroup->id); + $loadedNewParent = $this->loadUserGroup($newParent->id); + + $locationService = $this->repository->getLocationService(); + + if ($loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { + throw new BadStateException('userGroup', 'existing User Group is not stored and/or does not have any Location yet'); + } + + if ($loadedNewParent->getVersionInfo()->getContentInfo()->mainLocationId === null) { + throw new BadStateException('newParent', 'new User Group is not stored and/or does not have any Location yet'); + } + + $userGroupMainLocation = $locationService->loadLocation( + $loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId + ); + $newParentMainLocation = $locationService->loadLocation( + $loadedNewParent->getVersionInfo()->getContentInfo()->mainLocationId + ); + + $this->repository->beginTransaction(); + try { + $locationService->moveSubtree($userGroupMainLocation, $newParentMainLocation); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Updates the group profile with fields and meta data. + * + * 4.x: If the versionUpdateStruct is set in $userGroupUpdateStruct, this method internally creates a content draft, updates ts with the provided data + * and publishes the draft. If a draft is explicitly required, the user group can be updated via the content service methods. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroup + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroupUpdateStruct $userGroupUpdateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update the user group + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException if a field in the $userGroupUpdateStruct is not valid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException if a required field is set empty + */ + public function updateUserGroup(APIUserGroup $userGroup, UserGroupUpdateStruct $userGroupUpdateStruct): APIUserGroup + { + if ($userGroupUpdateStruct->contentUpdateStruct === null && + $userGroupUpdateStruct->contentMetadataUpdateStruct === null) { + // both update structs are empty, nothing to do + return $userGroup; + } + + $contentService = $this->repository->getContentService(); + + $loadedUserGroup = $this->loadUserGroup($userGroup->id); + + $this->repository->beginTransaction(); + try { + $publishedContent = $loadedUserGroup; + if ($userGroupUpdateStruct->contentUpdateStruct !== null) { + $contentDraft = $contentService->createContentDraft($loadedUserGroup->getVersionInfo()->getContentInfo()); + + $contentDraft = $contentService->updateContent( + $contentDraft->getVersionInfo(), + $userGroupUpdateStruct->contentUpdateStruct + ); + + $publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo()); + } + + if ($userGroupUpdateStruct->contentMetadataUpdateStruct !== null) { + $publishedContent = $contentService->updateContentMetadata( + $publishedContent->getVersionInfo()->getContentInfo(), + $userGroupUpdateStruct->contentMetadataUpdateStruct + ); + } + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->buildDomainUserGroupObject($publishedContent); + } + + /** + * Create a new user. The created user is published by this method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserCreateStruct $userCreateStruct the data used for creating the user + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup[] $parentGroups the groups which are assigned to the user after creation + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to move the user group + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException if a field in the $userCreateStruct is not valid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException if a required field is missing or set to an empty value + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if a user with provided login already exists + */ + public function createUser(APIUserCreateStruct $userCreateStruct, array $parentGroups): APIUser + { + $contentService = $this->repository->getContentService(); + $locationService = $this->repository->getLocationService(); + + $locationCreateStructs = []; + foreach ($parentGroups as $parentGroup) { + $parentGroup = $this->loadUserGroup($parentGroup->id); + if ($parentGroup->getVersionInfo()->getContentInfo()->mainLocationId !== null) { + $locationCreateStructs[] = $locationService->newLocationCreateStruct( + $parentGroup->getVersionInfo()->getContentInfo()->mainLocationId + ); + } + } + + // Search for the first ezuser field type in content type + $userFieldDefinition = $this->getUserFieldDefinition($userCreateStruct->contentType); + if ($userFieldDefinition === null) { + throw new MissingUserFieldTypeException($userCreateStruct->contentType, self::USER_FIELD_TYPE_NAME); + } + + $this->repository->beginTransaction(); + try { + $contentDraft = $contentService->createContent($userCreateStruct, $locationCreateStructs); + // There is no need to create user separately, just load it from SPI + $spiUser = $this->userHandler->load($contentDraft->id); + $publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo()); + + // User\Handler::create call is currently used to clear cache only + $this->userHandler->create( + new SPIUser( + [ + 'id' => $spiUser->id, + 'login' => $spiUser->login, + 'email' => $spiUser->email, + ] + ) + ); + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->buildDomainUserObject($spiUser, $publishedContent); + } + + /** + * Loads a user. + * + * @param int $userId + * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if a user with the given id was not found + */ + public function loadUser(int $userId, array $prioritizedLanguages = []): APIUser + { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $content */ + $content = $this->repository->getContentService()->internalLoadContentById($userId, $prioritizedLanguages); + // Get spiUser value from Field Value + foreach ($content->getFields() as $field) { + $fieldValue = $field->getValue(); + if (!$fieldValue instanceof UserValue) { + continue; + } + + $value = $fieldValue; + $spiUser = new SPIUser(); + $spiUser->id = $value->contentId; + $spiUser->login = $value->login; + $spiUser->email = $value->email; + $spiUser->hashAlgorithm = $value->passwordHashType; + $spiUser->passwordHash = $value->passwordHash; + $spiUser->passwordUpdatedAt = $value->passwordUpdatedAt ? $value->passwordUpdatedAt->getTimestamp() : null; + $spiUser->isEnabled = $value->enabled; + $spiUser->maxLogin = $value->maxLogin; + break; + } + + // If for some reason not found, load it + if (!isset($spiUser)) { + $spiUser = $this->userHandler->load($userId); + } + + return $this->buildDomainUserObject($spiUser, $content); + } + + /** + * Checks if credentials are valid for provided User. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * @param string $credentials + * + * @return bool + */ + public function checkUserCredentials( + APIUser $user, + #[\SensitiveParameter] + string $credentials + ): bool { + return $this->comparePasswordHashForAPIUser($user, $credentials); + } + + /** + * Loads a user for the given login. + * + * {@inheritdoc} + * + * @param string $login + * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if a user with the given credentials was not found + */ + public function loadUserByLogin(string $login, array $prioritizedLanguages = []): APIUser + { + if (empty($login)) { + throw new InvalidArgumentValue('login', $login); + } + + $spiUser = $this->userHandler->loadByLogin($login); + + return $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages); + } + + /** + * Loads a user for the given email. + * + * {@inheritdoc} + * + * @param string $email + * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function loadUserByEmail(string $email, array $prioritizedLanguages = []): APIUser + { + if (empty($email)) { + throw new InvalidArgumentValue('email', $email); + } + + $spiUser = $this->userHandler->loadByEmail($email); + + return $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages); + } + + /** + * Loads a user for the given email. + * + * {@inheritdoc} + * + * @param string $email + * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User[] + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function loadUsersByEmail(string $email, array $prioritizedLanguages = []): iterable + { + if (empty($email)) { + throw new InvalidArgumentValue('email', $email); + } + + $users = []; + foreach ($this->userHandler->loadUsersByEmail($email) as $spiUser) { + $users[] = $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages); + } + + return $users; + } + + /** + * Loads a user for the given token. + * + * {@inheritdoc} + * + * @param string $hash + * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentValue + */ + public function loadUserByToken(string $hash, array $prioritizedLanguages = []): APIUser + { + if (empty($hash)) { + throw new InvalidArgumentValue('hash', $hash); + } + + $spiUser = $this->userHandler->loadUserByToken($hash); + + return $this->buildDomainUserObject($spiUser, null, $prioritizedLanguages); + } + + /** + * This method deletes a user. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to delete the user + */ + public function deleteUser(APIUser $user): iterable + { + $loadedUser = $this->loadUser($user->id); + + $this->repository->beginTransaction(); + try { + foreach ($this->userHandler->loadRoleAssignmentsByGroupId($user->id) as $roleAssignment) { + $this->userHandler->removeRoleAssignment($roleAssignment->id); + } + + $affectedLocationIds = $this->repository->getContentService()->deleteContent( + $loadedUser->getVersionInfo()->getContentInfo() + ); + + // User\Handler::delete call is currently used to clear cache only + $this->userHandler->delete($loadedUser->id); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $affectedLocationIds ?? []; + } + + /** + * Updates a user. + * + * 4.x: If the versionUpdateStruct is set in the user update structure, this method internally creates a content draft, updates ts with the provided data + * and publishes the draft. If a draft is explicitly required, the user group can be updated via the content service methods. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserUpdateStruct $userUpdateStruct + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException if a field in the $userUpdateStruct is not valid + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException if a required field is set empty + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to update the user + */ + public function updateUser(APIUser $user, UserUpdateStruct $userUpdateStruct): APIUser + { + $loadedUser = $this->loadUser($user->id); + + $contentService = $this->repository->getContentService(); + + $canEditContent = $this->permissionResolver->canUser('content', 'edit', $loadedUser); + + if (!$canEditContent && $this->isUserProfileUpdateRequested($userUpdateStruct)) { + throw new UnauthorizedException('content', 'edit'); + } + + $userFieldDefinition = $this->getUserFieldDefinition($loadedUser->getContentType()); + if ($userFieldDefinition === null) { + throw new MissingUserFieldTypeException($loadedUser->getContentType(), self::USER_FIELD_TYPE_NAME); + } + + $userUpdateStruct->contentUpdateStruct = $userUpdateStruct->contentUpdateStruct ?? $contentService->newContentUpdateStruct(); + + $providedUserUpdateDataInField = false; + foreach ($userUpdateStruct->contentUpdateStruct->fields as $field) { + if ($field->value instanceof UserValue) { + $providedUserUpdateDataInField = true; + break; + } + } + + if (!$providedUserUpdateDataInField) { + $userUpdateStruct->contentUpdateStruct->setField( + $userFieldDefinition->identifier, + new UserValue([ + 'contentId' => $loadedUser->id, + 'hasStoredLogin' => true, + 'login' => $loadedUser->login, + 'email' => $userUpdateStruct->email ?? $loadedUser->email, + 'plainPassword' => $userUpdateStruct->password, + 'enabled' => $userUpdateStruct->enabled ?? $loadedUser->enabled, + 'maxLogin' => $userUpdateStruct->maxLogin ?? $loadedUser->maxLogin, + 'passwordHashType' => $user->hashAlgorithm, + 'passwordHash' => $user->passwordHash, + ]) + ); + } + + if (!empty($userUpdateStruct->password) && + !$canEditContent && + !$this->permissionResolver->canUser('user', 'password', $loadedUser, [$loadedUser]) + ) { + throw new UnauthorizedException('user', 'password'); + } + + $this->repository->beginTransaction(); + try { + $publishedContent = $loadedUser; + if ($userUpdateStruct->contentUpdateStruct !== null) { + $contentDraft = $contentService->createContentDraft($loadedUser->getVersionInfo()->getContentInfo()); + $contentDraft = $contentService->updateContent( + $contentDraft->getVersionInfo(), + $userUpdateStruct->contentUpdateStruct + ); + $publishedContent = $contentService->publishVersion($contentDraft->getVersionInfo()); + } + + if ($userUpdateStruct->contentMetadataUpdateStruct !== null) { + $contentService->updateContentMetadata( + $publishedContent->getVersionInfo()->getContentInfo(), + $userUpdateStruct->contentMetadataUpdateStruct + ); + } + + // User\Handler::update call is currently used to clear cache only + $this->userHandler->update( + new SPIUser( + [ + 'id' => $loadedUser->id, + 'login' => $loadedUser->login, + 'email' => $userUpdateStruct->email ?: $loadedUser->email, + ] + ) + ); + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadUser($loadedUser->id); + } + + public function updateUserPassword( + APIUser $user, + #[\SensitiveParameter] + string $newPassword + ): APIUser { + $loadedUser = $this->loadUser($user->id); + + if (!$this->permissionResolver->canUser('content', 'edit', $loadedUser) + && !$this->permissionResolver->canUser('user', 'password', $loadedUser) + ) { + throw new UnauthorizedException('user', 'password'); + } + + $userFieldDefinition = $this->getUserFieldDefinition($loadedUser->getContentType()); + if ($userFieldDefinition === null) { + throw new MissingUserFieldTypeException($loadedUser->getContentType(), self::USER_FIELD_TYPE_NAME); + } + + $errors = $this->passwordValidator->validatePassword( + $newPassword, + $userFieldDefinition, + $user + ); + if (!empty($errors)) { + // Note: @deprecated this should rather throw a list wrapper of `ValidationError`s + throw ContentFieldValidationException::createNewWithMultiline( + // build errors array as expected by ContentFieldValidationException + [$userFieldDefinition->id => [$userFieldDefinition->mainLanguageCode => $errors]], + $loadedUser->getName() + ); + } + + $passwordHashAlgorithm = (int) $loadedUser->hashAlgorithm; + try { + $passwordHash = $this->passwordHashService->createPasswordHash($newPassword, $passwordHashAlgorithm); + } catch (UnsupportedPasswordHashType $e) { + $passwordHashAlgorithm = $this->passwordHashService->getDefaultHashType(); + $passwordHash = $this->passwordHashService->createPasswordHash($newPassword, $passwordHashAlgorithm); + } + + $this->repository->beginTransaction(); + try { + $this->userHandler->updatePassword( + new SPIUser( + [ + 'id' => $loadedUser->id, + 'login' => $loadedUser->login, + 'email' => $loadedUser->email, + 'passwordHash' => $passwordHash, + 'hashAlgorithm' => $passwordHashAlgorithm, + 'passwordUpdatedAt' => time(), + ] + ) + ); + + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadUser($loadedUser->id); + } + + /** + * Update the user token information specified by the user token struct. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserTokenUpdateStruct $userTokenUpdateStruct + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentValue + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \RuntimeException + * @throws \Exception + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + */ + public function updateUserToken(APIUser $user, UserTokenUpdateStruct $userTokenUpdateStruct): APIUser + { + $loadedUser = $this->loadUser($user->id); + + if ($userTokenUpdateStruct->hashKey !== null && (!is_string($userTokenUpdateStruct->hashKey) || empty($userTokenUpdateStruct->hashKey))) { + throw new InvalidArgumentValue('hashKey', $userTokenUpdateStruct->hashKey, 'UserTokenUpdateStruct'); + } + + if ($userTokenUpdateStruct->time === null) { + throw new InvalidArgumentValue('time', $userTokenUpdateStruct->time, 'UserTokenUpdateStruct'); + } + + $this->repository->beginTransaction(); + try { + $this->userHandler->updateUserToken( + new SPIUserTokenUpdateStruct( + [ + 'userId' => $loadedUser->id, + 'hashKey' => $userTokenUpdateStruct->hashKey, + 'time' => $userTokenUpdateStruct->time->getTimestamp(), + ] + ) + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + + return $this->loadUser($loadedUser->id); + } + + /** + * Expires user token with user hash. + * + * @param string $hash + */ + public function expireUserToken(string $hash): void + { + $this->repository->beginTransaction(); + try { + $this->userHandler->expireUserToken($hash); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Assigns a new user group to the user. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroup + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to assign the user group to the user + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the user is already in the given user group + */ + public function assignUserToUserGroup(APIUser $user, APIUserGroup $userGroup): void + { + $loadedUser = $this->loadUser($user->id); + $loadedGroup = $this->loadUserGroup($userGroup->id); + $locationService = $this->repository->getLocationService(); + + $existingGroupIds = []; + $userLocations = $locationService->loadLocations($loadedUser->getVersionInfo()->getContentInfo()); + foreach ($userLocations as $userLocation) { + $existingGroupIds[] = $userLocation->parentLocationId; + } + + if ($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { + throw new BadStateException('userGroup', 'User Group has no main Location or no Locations'); + } + + if (in_array($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId, $existingGroupIds)) { + // user is already assigned to the user group + throw new InvalidArgumentException('user', 'User is already in the given User Group'); + } + + $locationCreateStruct = $locationService->newLocationCreateStruct( + $loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId + ); + + $this->repository->beginTransaction(); + try { + $locationService->createLocation( + $loadedUser->getVersionInfo()->getContentInfo(), + $locationCreateStruct + ); + $this->repository->commit(); + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + + /** + * Removes a user group from the user. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroup + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to remove the user group from the user + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if the user is not in the given user group + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException If $userGroup is the last assigned user group + */ + public function unAssignUserFromUserGroup(APIUser $user, APIUserGroup $userGroup): void + { + $loadedUser = $this->loadUser($user->id); + $loadedGroup = $this->loadUserGroup($userGroup->id); + $locationService = $this->repository->getLocationService(); + + $userLocations = $locationService->loadLocations($loadedUser->getVersionInfo()->getContentInfo()); + if (empty($userLocations)) { + throw new BadStateException('user', 'User has no Locations, cannot unassign from group'); + } + + if ($loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { + throw new BadStateException('userGroup', 'User Group has no main Location or no Locations, cannot unassign'); + } + + foreach ($userLocations as $userLocation) { + if ($userLocation->parentLocationId == $loadedGroup->getVersionInfo()->getContentInfo()->mainLocationId) { + // Throw this specific BadState when we know argument is valid + if (count($userLocations) === 1) { + throw new BadStateException('user', 'User only has one User Group, cannot unassign from last group'); + } + + $this->repository->beginTransaction(); + try { + $locationService->deleteLocation($userLocation); + $this->repository->commit(); + + return; + } catch (Exception $e) { + $this->repository->rollback(); + throw $e; + } + } + } + + throw new InvalidArgumentException('userGroup', 'User is not in the given User Group'); + } + + /** + * Loads the user groups the user belongs to. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed read the user or user group + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * @param int $offset the start offset for paging + * @param int $limit the number of user groups returned + * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup[] + */ + public function loadUserGroupsOfUser(APIUser $user, int $offset = 0, int $limit = 25, array $prioritizedLanguages = []): iterable + { + $locationService = $this->repository->getLocationService(); + + if (!$this->repository->getPermissionResolver()->canUser('content', 'read', $user)) { + throw new UnauthorizedException('content', 'read'); + } + + $userLocations = $locationService->loadLocations( + $user->getVersionInfo()->getContentInfo() + ); + + $parentLocationIds = []; + foreach ($userLocations as $userLocation) { + if ($userLocation->parentLocationId !== null) { + $parentLocationIds[] = $userLocation->parentLocationId; + } + } + + $searchQuery = new LocationQuery(); + + $searchQuery->offset = $offset; + $searchQuery->limit = $limit; + $searchQuery->performCount = false; + + $searchQuery->filter = new CriterionLogicalAnd( + [ + new CriterionContentTypeId($this->settings['userGroupClassID']), + new CriterionLocationId($parentLocationIds), + ] + ); + + $searchResult = $this->repository->getSearchService()->findLocations($searchQuery); + + $userGroups = []; + foreach ($searchResult->searchHits as $resultItem) { + $userGroups[] = $this->buildDomainUserGroupObject( + $this->repository->getContentService()->internalLoadContentById( + $resultItem->valueObject->contentInfo->id, + $prioritizedLanguages + ) + ); + } + + return $userGroups; + } + + /** + * Loads the users of a user group. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException if the authenticated user is not allowed to read the users or user group + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroup + * @param int $offset the start offset for paging + * @param int $limit the number of users returned + * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User[] + */ + public function loadUsersOfUserGroup( + APIUserGroup $userGroup, + int $offset = 0, + int $limit = 25, + array $prioritizedLanguages = [] + ): iterable { + $loadedUserGroup = $this->loadUserGroup($userGroup->id); + + if ($loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId === null) { + return []; + } + + $mainGroupLocation = $this->repository->getLocationService()->loadLocation( + $loadedUserGroup->getVersionInfo()->getContentInfo()->mainLocationId + ); + + $searchQuery = new LocationQuery(); + + $searchQuery->filter = new CriterionLogicalAnd( + [ + new CriterionContentTypeIdentifier($this->getUserContentTypeIdentifiers()), + new CriterionParentLocationId($mainGroupLocation->id), + ] + ); + + $searchQuery->offset = $offset; + $searchQuery->limit = $limit; + $searchQuery->performCount = false; + $searchQuery->sortClauses = $mainGroupLocation->getSortClauses(); + + $searchResult = $this->repository->getSearchService()->findLocations($searchQuery); + + $users = []; + foreach ($searchResult->searchHits as $resultItem) { + $users[] = $this->buildDomainUserObject( + $this->userHandler->load($resultItem->valueObject->contentInfo->id), + $this->repository->getContentService()->internalLoadContentById( + $resultItem->valueObject->contentInfo->id, + $prioritizedLanguages + ) + ); + } + + return $users; + } + + /** + * {@inheritdoc} + */ + public function isUser(APIContent $content): bool + { + // First check against config for fast check + if (in_array( + $content->getVersionInfo()->getContentInfo()->getContentType()->identifier, + $this->getUserContentTypeIdentifiers(), + true + )) { + return true; + } + + // For users we ultimately need to look for ezuser type as content type id could be several for users. + // And config might be different from one SA to the next, which we don't care about here. + foreach ($content->getFields() as $field) { + if ($field->fieldTypeIdentifier === self::USER_FIELD_TYPE_NAME) { + return true; + } + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function isUserGroup(APIContent $content): bool + { + return $this->settings['userGroupClassID'] == $content->getVersionInfo()->getContentInfo()->contentTypeId; + } + + /** + * Instantiate a user create class. + * + * @param string $login the login of the new user + * @param string $email the email of the new user + * @param string $password the plain password of the new user + * @param string $mainLanguageCode the main language for the underlying content object + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType|null $contentType content type for the underlying content item. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserCreateStruct + */ + public function newUserCreateStruct( + string $login, + string $email, + #[\SensitiveParameter] + string $password, + string $mainLanguageCode, + ?ContentType $contentType = null + ): APIUserCreateStruct { + if ($contentType === null) { + $userContentTypeIdentifiers = $this->getUserContentTypeIdentifiers(); + $defaultIdentifier = reset($userContentTypeIdentifiers); + $contentType = $this->repository->getContentTypeService()->loadContentTypeByIdentifier($defaultIdentifier); + } + + $fieldDefIdentifier = ''; + foreach ($contentType->fieldDefinitions as $fieldDefinition) { + if ($fieldDefinition->fieldTypeIdentifier === self::USER_FIELD_TYPE_NAME) { + $fieldDefIdentifier = $fieldDefinition->identifier; + break; + } + } + + return new UserCreateStruct( + [ + 'contentType' => $contentType, + 'mainLanguageCode' => $mainLanguageCode, + 'login' => $login, + 'email' => $email, + 'password' => $password, + 'enabled' => true, + 'fields' => [ + new Field([ + 'fieldDefIdentifier' => $fieldDefIdentifier, + 'languageCode' => $mainLanguageCode, + 'fieldTypeIdentifier' => self::USER_FIELD_TYPE_NAME, + 'value' => new UserValue([ + 'login' => $login, + 'email' => $email, + 'plainPassword' => $password, + 'enabled' => true, + 'passwordUpdatedAt' => new DateTime(), + ]), + ]), + ], + ] + ); + } + + /** + * Instantiate a user group create class. + * + * @param string $mainLanguageCode The main language for the underlying content object + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType|null $contentType 5.x the content type for the underlying content item. In 4.x it is ignored and taken from the configuration + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroupCreateStruct + */ + public function newUserGroupCreateStruct(string $mainLanguageCode, ?ContentType $contentType = null): APIUserGroupCreateStruct + { + if ($contentType === null) { + $contentType = $this->repository->getContentTypeService()->loadContentType( + $this->settings['userGroupClassID'] + ); + } + + return new UserGroupCreateStruct( + [ + 'contentType' => $contentType, + 'mainLanguageCode' => $mainLanguageCode, + 'fields' => [], + ] + ); + } + + /** + * Instantiate a new user update struct. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserUpdateStruct + */ + public function newUserUpdateStruct(): UserUpdateStruct + { + return new UserUpdateStruct(); + } + + /** + * Instantiate a new user group update struct. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroupUpdateStruct + */ + public function newUserGroupUpdateStruct(): UserGroupUpdateStruct + { + return new UserGroupUpdateStruct(); + } + + /** + * {@inheritdoc} + */ + public function validatePassword( + #[\SensitiveParameter] + string $password, + PasswordValidationContext $context = null + ): array { + if ($context === null) { + $userContentTypeIdentifiers = $this->getUserContentTypeIdentifiers(); + $defaultIdentifier = reset($userContentTypeIdentifiers); + $contentType = $this->repository->getContentTypeService()->loadContentTypeByIdentifier($defaultIdentifier); + $context = new PasswordValidationContext([ + 'contentType' => $contentType, + ]); + } + + // Search for the first ezuser field type in content type + $userFieldDefinition = $this->getUserFieldDefinition($context->contentType); + if ($userFieldDefinition === null) { + throw new MissingUserFieldTypeException($context->contentType, self::USER_FIELD_TYPE_NAME); + } + + return $this->passwordValidator->validatePassword( + $password, + $userFieldDefinition, + $context->user + ); + } + + /** + * Builds the domain UserGroup object from provided Content object. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup + */ + protected function buildDomainUserGroupObject(APIContent $content): APIUserGroup + { + $locationService = $this->repository->getLocationService(); + + if ($content->getVersionInfo()->getContentInfo()->mainLocationId !== null) { + $mainLocation = $locationService->loadLocation( + $content->getVersionInfo()->getContentInfo()->mainLocationId + ); + $parentLocation = $this->locationHandler->load($mainLocation->parentLocationId); + } + + return new UserGroup( + [ + 'content' => $content, + 'parentId' => $parentLocation->contentId ?? null, + ] + ); + } + + /** + * Builds the domain user object from provided persistence user object. + * + * @param \Ibexa\Contracts\Core\Persistence\User $spiUser + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content|null $content + * @param string[] $prioritizedLanguages Used as prioritized language code on translated properties of returned object. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + */ + protected function buildDomainUserObject( + SPIUser $spiUser, + APIContent $content = null, + array $prioritizedLanguages = [] + ): APIUser { + if ($content === null) { + $content = $this->repository->getContentService()->internalLoadContentById( + $spiUser->id, + $prioritizedLanguages + ); + } + + return new User( + [ + 'content' => $content, + 'login' => $spiUser->login, + 'email' => $spiUser->email, + 'passwordHash' => $spiUser->passwordHash, + 'passwordUpdatedAt' => $this->getDateTime($spiUser->passwordUpdatedAt), + 'hashAlgorithm' => (int)$spiUser->hashAlgorithm, + 'enabled' => $spiUser->isEnabled, + 'maxLogin' => (int)$spiUser->maxLogin, + ] + ); + } + + public function getPasswordInfo(APIUser $user): PasswordInfo + { + $definition = $this->getUserFieldDefinition($user->getContentType()); + + return $this->passwordValidator->getPasswordInfo($user, $definition); + } + + private function getUserFieldDefinition(ContentType $contentType): ?FieldDefinition + { + return $contentType->getFirstFieldDefinitionOfType(self::USER_FIELD_TYPE_NAME); + } + + /** + * Verifies if the provided login and password are valid for {@see \Ibexa\Contracts\Core\Persistence\User}. + * + * @return bool return true if the login and password are successfully validated and false, if not. + */ + protected function comparePasswordHashForSPIUser( + SPIUser $user, + #[\SensitiveParameter] + string $password + ): bool { + return $this->comparePasswordHashes($password, $user->passwordHash, $user->hashAlgorithm); + } + + /** + * Verifies if the provided login and password are valid for {@see \Ibexa\Contracts\Core\Repository\Values\User\User}. + * + * @return bool return true if the login and password are successfully validated and false, if not. + */ + protected function comparePasswordHashForAPIUser( + APIUser $user, + #[\SensitiveParameter] + string $password + ): bool { + return $this->comparePasswordHashes($password, $user->passwordHash, $user->hashAlgorithm); + } + + /** + * Verifies if the provided login and password are valid against given password hash and hash type. + * + * @param string $plainPassword User password + * @param string $passwordHash User password hash + * @param int $hashAlgorithm Hash type + * + * @return bool return true if the login and password are successfully validated and false, if not. + */ + private function comparePasswordHashes( + #[\SensitiveParameter] + string $plainPassword, + #[\SensitiveParameter] + string $passwordHash, + int $hashAlgorithm + ): bool { + return $this->passwordHashService->isValidPassword($plainPassword, $passwordHash, $hashAlgorithm); + } + + /** + * Return true if any of the UserUpdateStruct properties refers to User Profile (Content) update. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserUpdateStruct $userUpdateStruct + * + * @return bool + */ + private function isUserProfileUpdateRequested(UserUpdateStruct $userUpdateStruct) + { + return + !empty($userUpdateStruct->contentUpdateStruct) || + !empty($userUpdateStruct->contentMetadataUpdateStruct) || + !empty($userUpdateStruct->email) || + !empty($userUpdateStruct->enabled) || + !empty($userUpdateStruct->maxLogin); + } + + private function getDateTime(?int $timestamp): ?DateTimeInterface + { + if ($timestamp !== null) { + // Instead of using DateTime(ts) we use setTimeStamp() so timezone does not get set to UTC + $dateTime = new DateTime(); + $dateTime->setTimestamp($timestamp); + + return DateTimeImmutable::createFromMutable($dateTime); + } + + return null; + } + + /** + * @return string[] + */ + private function getUserContentTypeIdentifiers(): array + { + return $this->configResolver->getParameter('user_content_type_identifier'); + } +} + +class_alias(UserService::class, 'eZ\Publish\Core\Repository\UserService'); diff --git a/eZ/Publish/Core/Repository/Validator/ContentCreateStructValidator.php b/src/lib/Repository/Validator/ContentCreateStructValidator.php similarity index 79% rename from eZ/Publish/Core/Repository/Validator/ContentCreateStructValidator.php rename to src/lib/Repository/Validator/ContentCreateStructValidator.php index a26009136c..7714c5ff21 100644 --- a/eZ/Publish/Core/Repository/Validator/ContentCreateStructValidator.php +++ b/src/lib/Repository/Validator/ContentCreateStructValidator.php @@ -6,25 +6,25 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\Validator; +namespace Ibexa\Core\Repository\Validator; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\Repository\Mapper\ContentMapper; -use eZ\Publish\SPI\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\FieldTypeRegistry; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\Repository\Mapper\ContentMapper; /** * @internal Meant for internal use by Repository */ final class ContentCreateStructValidator implements ContentValidator { - /** @var \eZ\Publish\Core\Repository\Mapper\ContentMapper */ + /** @var \Ibexa\Core\Repository\Mapper\ContentMapper */ private $contentMapper; - /** @var \eZ\Publish\Core\FieldType\FieldTypeRegistry */ + /** @var \Ibexa\Core\FieldType\FieldTypeRegistry */ private $fieldTypeRegistry; public function __construct( @@ -49,7 +49,7 @@ public function validate( throw new InvalidArgumentException('$object', 'Not supported'); } - /** @var \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct $contentCreateStruct */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct $contentCreateStruct */ $contentCreateStruct = $object; $languageCodes = $this->contentMapper->getLanguageCodesForCreate($contentCreateStruct); @@ -62,7 +62,7 @@ public function validate( continue; } - /** @var \eZ\Publish\Core\FieldType\FieldType $fieldType */ + /** @var \Ibexa\Core\FieldType\FieldType $fieldType */ $fieldType = $this->fieldTypeRegistry->getFieldType( $fieldDefinition->fieldTypeIdentifier ); @@ -99,3 +99,5 @@ public function validate( return $allFieldErrors; } } + +class_alias(ContentCreateStructValidator::class, 'eZ\Publish\Core\Repository\Validator\ContentCreateStructValidator'); diff --git a/eZ/Publish/Core/Repository/Validator/ContentUpdateStructValidator.php b/src/lib/Repository/Validator/ContentUpdateStructValidator.php similarity index 83% rename from eZ/Publish/Core/Repository/Validator/ContentUpdateStructValidator.php rename to src/lib/Repository/Validator/ContentUpdateStructValidator.php index 80e76b763b..b0bf1630de 100644 --- a/eZ/Publish/Core/Repository/Validator/ContentUpdateStructValidator.php +++ b/src/lib/Repository/Validator/ContentUpdateStructValidator.php @@ -6,30 +6,30 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\Validator; - -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\Core\Repository\Mapper\ContentMapper; -use eZ\Publish\SPI\Persistence\Content\Language\Handler; -use eZ\Publish\SPI\Repository\Validator\ContentValidator; +namespace Ibexa\Core\Repository\Validator; + +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler; +use Ibexa\Contracts\Core\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\FieldTypeRegistry; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\Repository\Mapper\ContentMapper; /** * @internal Meant for internal use by Repository */ final class ContentUpdateStructValidator implements ContentValidator { - /** @var \eZ\Publish\Core\Repository\Mapper\ContentMapper */ + /** @var \Ibexa\Core\Repository\Mapper\ContentMapper */ private $contentMapper; - /** @var \eZ\Publish\Core\FieldType\FieldTypeRegistry */ + /** @var \Ibexa\Core\FieldType\FieldTypeRegistry */ private $fieldTypeRegistry; - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler */ + /** @var \Ibexa\Core\Persistence\Legacy\Content\Language\Handler */ private $contentLanguageHandler; public function __construct( @@ -62,7 +62,7 @@ public function validate( $content = $context['content']; - /** @var \eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct $contentUpdateStruct */ $contentUpdateStruct = $object; $contentType = $content->getContentType(); @@ -136,3 +136,5 @@ public function validate( return $allFieldErrors; } } + +class_alias(ContentUpdateStructValidator::class, 'eZ\Publish\Core\Repository\Validator\ContentUpdateStructValidator'); diff --git a/src/lib/Repository/Validator/TargetContentValidator.php b/src/lib/Repository/Validator/TargetContentValidator.php index 017ec719b6..0efcc870a9 100644 --- a/src/lib/Repository/Validator/TargetContentValidator.php +++ b/src/lib/Repository/Validator/TargetContentValidator.php @@ -8,9 +8,9 @@ namespace Ibexa\Core\Repository\Validator; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Core\FieldType\ValidationError; /** * Validator for checking existence of content and its content type. @@ -19,10 +19,10 @@ */ final class TargetContentValidator implements TargetContentValidatorInterface { - /** @var \eZ\Publish\SPI\Persistence\Content\Handler */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Handler */ private $contentHandler; - /** @var \eZ\Publish\SPI\Persistence\Content\Type\Handler */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Type\Handler */ private $contentTypeHandler; public function __construct( @@ -41,7 +41,7 @@ public function validate(int $value, array $allowedContentTypes = []): ?Validati if (!empty($allowedContentTypes) && !in_array($contentType->identifier, $allowedContentTypes, true)) { return new ValidationError( - 'Content Type %contentTypeIdentifier% is not a valid relation target', + 'Content type %contentTypeIdentifier% is not a valid relation target', null, [ '%contentTypeIdentifier%' => $contentType->identifier, diff --git a/src/lib/Repository/Validator/TargetContentValidatorInterface.php b/src/lib/Repository/Validator/TargetContentValidatorInterface.php index a24ff9d1d9..ac20548230 100644 --- a/src/lib/Repository/Validator/TargetContentValidatorInterface.php +++ b/src/lib/Repository/Validator/TargetContentValidatorInterface.php @@ -8,7 +8,7 @@ namespace Ibexa\Core\Repository\Validator; -use eZ\Publish\Core\FieldType\ValidationError; +use Ibexa\Contracts\Core\FieldType\ValidationError; /** * @internal diff --git a/src/lib/Repository/Validator/UserPasswordValidator.php b/src/lib/Repository/Validator/UserPasswordValidator.php new file mode 100644 index 0000000000..c1aa1cc67c --- /dev/null +++ b/src/lib/Repository/Validator/UserPasswordValidator.php @@ -0,0 +1,205 @@ +constraints = $constraints; + } + + /** + * Validates given $password. + * + * @param string $password + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validate( + #[\SensitiveParameter] + string $password + ): array { + $errors = []; + + if (!$this->isLongEnough($password)) { + $errors[] = $this->createValidationError('User password must be at least %length% characters long', [ + '%length%' => $this->constraints['minLength'], + ]); + } + + if (!$this->containsAtLeastOneLowerCaseCharacter($password)) { + $errors[] = $this->createValidationError('User password must include at least one lower case letter'); + } + + if (!$this->containsAtLeastOneUpperCaseCharacter($password)) { + $errors[] = $this->createValidationError('User password must include at least one upper case letter'); + } + + if (!$this->containsAtLeastOneNumericCharacter($password)) { + $errors[] = $this->createValidationError('User password must include at least one number'); + } + + if (!$this->containsAtLeastOneNonAlphanumericCharacter($password)) { + $errors[] = $this->createValidationError('User password must include at least one special character'); + } + + if ($this->isCompromised($password)) { + $errors[] = $this->createValidationError( + 'This password has been leaked in a data breach, it must not be used. Please use another password.' + ); + } + + return $errors; + } + + /** + * Checks if given $password satisfies length requirements. + * + * @param string $password + * + * @return bool + */ + private function isLongEnough( + #[\SensitiveParameter] + string $password + ): bool { + if ((int) $this->constraints['minLength'] > 0) { + return mb_strlen($password) >= (int) $this->constraints['minLength']; + } + + return true; + } + + /** + * Checks if given $password contains at least one lower case character (if rule is applicable). + * + * @param string $password + * + * @return bool + */ + private function containsAtLeastOneLowerCaseCharacter( + #[\SensitiveParameter] + string $password + ): bool { + if ($this->constraints['requireAtLeastOneLowerCaseCharacter']) { + return (bool)preg_match(self::AT_LEAST_ONE_LOWER_CASE_CHARACTER_REGEX, $password); + } + + return true; + } + + /** + * Checks if given $password contains at least one upper case character (if rule is applicable). + * + * @param string $password + * + * @return bool + */ + private function containsAtLeastOneUpperCaseCharacter( + #[\SensitiveParameter] + string $password + ): bool { + if ($this->constraints['requireAtLeastOneUpperCaseCharacter']) { + return (bool)preg_match(self::AT_LEAST_ONE_UPPER_CASE_CHARACTER_REGEX, $password); + } + + return true; + } + + /** + * Checks if given $password contains at least one numeric character (if rule is applicable). + * + * @param string $password + * + * @return bool + */ + private function containsAtLeastOneNumericCharacter( + #[\SensitiveParameter] + string $password + ): bool { + if ($this->constraints['requireAtLeastOneNumericCharacter']) { + return (bool)preg_match(self::AT_LEAST_ONE_NUMERIC_CHARACTER_REGEX, $password); + } + + return true; + } + + /** + * Checks if given $password contains at least one non alphanumeric character (if rule is applicable). + * + * @param string $password + * + * @return bool + */ + private function containsAtLeastOneNonAlphanumericCharacter( + #[\SensitiveParameter] + string $password + ): bool { + if ($this->constraints['requireAtLeastOneNonAlphanumericCharacter']) { + return (bool)preg_match(self::AT_LEAST_ONE_NON_ALPHANUMERIC_CHARACTER_REGEX, $password); + } + + return true; + } + + /** + * Checks if given $password is included in a public data breach tracked by https://haveibeenpwned.com. + */ + private function isCompromised( + #[\SensitiveParameter] + string $password + ): bool { + if ($this->constraints['requireNotCompromisedPassword']) { + $validator = Validation::createValidator(); + $constraintViolationList = $validator->validate($password, [new Assert\NotCompromisedPassword()]); + // Only one violation is possible for NotCompromisedPassword. + if (count($constraintViolationList) > 0) { + return true; + } + } + + return false; + } + + /** + * Creates a validation error with given messages and placeholders. + * + * @param string $message + * @param array $values + * + * @return \Ibexa\Core\FieldType\ValidationError + */ + private function createValidationError(string $message, array $values = []): ValidationError + { + return new ValidationError($message, null, $values, 'password'); + } +} + +class_alias(UserPasswordValidator::class, 'eZ\Publish\Core\Repository\Validator\UserPasswordValidator'); diff --git a/eZ/Publish/Core/Repository/Validator/VersionValidator.php b/src/lib/Repository/Validator/VersionValidator.php similarity index 75% rename from eZ/Publish/Core/Repository/Validator/VersionValidator.php rename to src/lib/Repository/Validator/VersionValidator.php index 69539b4211..7057780bc1 100644 --- a/eZ/Publish/Core/Repository/Validator/VersionValidator.php +++ b/src/lib/Repository/Validator/VersionValidator.php @@ -6,22 +6,22 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\Validator; +namespace Ibexa\Core\Repository\Validator; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\FieldTypeRegistry; +use Ibexa\Core\FieldType\ValidationError; /** * @internal Meant for internal use by Repository */ final class VersionValidator implements ContentValidator { - /** @var \eZ\Publish\Core\FieldType\FieldTypeRegistry */ + /** @var \Ibexa\Core\FieldType\FieldTypeRegistry */ private $fieldTypeRegistry; public function __construct( @@ -35,6 +35,12 @@ public function supports(ValueObject $object): bool return $object instanceof VersionInfo; } + /** + * @phpstan-param array{ + * content?: \Ibexa\Contracts\Core\Repository\Values\Content\Content, + * translations?: string[], + * } $context + */ public function validate( ValueObject $object, array $context = [], @@ -73,7 +79,7 @@ public function validate( if ($fieldType->isEmptyValue($fieldValue)) { if ($fieldDefinition->isRequired) { - $allFieldErrors[$fieldDefinition->identifier][$languageCode] = new ValidationError( + $allFieldErrors[$fieldDefinition->id][$languageCode] = new ValidationError( "Value for required field definition '%identifier%' with language '%languageCode%' is empty", null, ['%identifier%' => $fieldDefinition->identifier, '%languageCode%' => $languageCode], @@ -86,7 +92,7 @@ public function validate( $fieldValue ); if (!empty($fieldErrors)) { - $allFieldErrors[$fieldDefinition->identifier][$languageCode] = $fieldErrors; + $allFieldErrors[$fieldDefinition->id][$languageCode] = $fieldErrors; } } } @@ -95,3 +101,5 @@ public function validate( return $allFieldErrors; } } + +class_alias(VersionValidator::class, 'eZ\Publish\Core\Repository\Validator\VersionValidator'); diff --git a/src/lib/Repository/Values/Content/Content.php b/src/lib/Repository/Values/Content/Content.php new file mode 100644 index 0000000000..361ac6bb3c --- /dev/null +++ b/src/lib/Repository/Values/Content/Content.php @@ -0,0 +1,216 @@ +contentInfo + * @property-read \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType convenience getter for $versionInfo->contentInfo->contentType + * @property-read int $id convenience getter for retrieving the contentId: $versionInfo->content->id + * @property-read \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo calls getVersionInfo() + * @property-read \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $fields Access fields, calls getFields() + * + * @internal Meant for internal use by Repository, type hint against API object instead. + */ +class Content extends APIContent +{ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Thumbnail|null */ + protected $thumbnail; + + /** @var mixed[][] An array of array of field values like[$fieldDefIdentifier][$languageCode] */ + protected $fields; + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo */ + protected $versionInfo; + + /** @var \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType */ + protected $contentType; + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Field[] An array of {@link Field} */ + private $internalFields; + + /** + * In-memory cache of Field Definition Identifier and Language Code mapped to a Field instance. + * + * $fieldDefinitionTranslationMap[$fieldDefIdentifier][$languageCode] = $field + * + * @var array> + */ + private $fieldDefinitionTranslationMap = []; + + /** + * The first matched field language among user provided prioritized languages. + * + * The first matched language among user provided prioritized languages on object retrieval, or null if none + * provided (all languages) or on main fallback. + * + * @internal + * + * @var string|null + */ + protected $prioritizedFieldLanguageCode; + + public function __construct(array $data = []) + { + parent::__construct([]); + + $this->thumbnail = $data['thumbnail'] ?? null; + $this->versionInfo = $data['versionInfo'] ?? null; + $this->contentType = $data['contentType'] ?? null; + $this->internalFields = $data['internalFields'] ?? []; + $this->prioritizedFieldLanguageCode = $data['prioritizedFieldLanguageCode'] ?? null; + + foreach ($this->internalFields as $field) { + $languageCode = $field->getLanguageCode(); + $fieldDefinitionIdentifier = $field->getFieldDefinitionIdentifier(); + $this->fieldDefinitionTranslationMap[$fieldDefinitionIdentifier][$languageCode] = $field; + // kept for BC due to property-read magic getter + $this->fields[$fieldDefinitionIdentifier][$languageCode] = $field->getValue(); + } + } + + public function getThumbnail(): ?Thumbnail + { + return $this->thumbnail; + } + + /** + * {@inheritdoc} + */ + public function getVersionInfo(): APIVersionInfo + { + return $this->versionInfo; + } + + /** + * {@inheritdoc} + */ + public function getContentType(): ContentType + { + return $this->contentType; + } + + /** + * {@inheritdoc} + */ + public function getFieldValue(string $fieldDefIdentifier, ?string $languageCode = null): ?Value + { + if (null === $languageCode) { + $languageCode = $this->getDefaultLanguageCode(); + } + + if (!isset($this->fieldDefinitionTranslationMap[$fieldDefIdentifier][$languageCode])) { + return null; + } + + return $this->fieldDefinitionTranslationMap[$fieldDefIdentifier][$languageCode]->getValue(); + } + + /** + * {@inheritdoc} + */ + public function getFields(): iterable + { + return $this->internalFields; + } + + /** + * {@inheritdoc} + */ + public function getFieldsByLanguage(?string $languageCode = null): iterable + { + $fields = []; + + if (null === $languageCode) { + $languageCode = $this->getDefaultLanguageCode(); + } + + $filteredFields = array_filter( + $this->internalFields, + static function (Field $field) use ($languageCode): bool { + return $field->languageCode === $languageCode; + } + ); + foreach ($filteredFields as $field) { + $fields[$field->fieldDefIdentifier] = $field; + } + + return $fields; + } + + /** + * {@inheritdoc} + */ + public function getField(string $fieldDefIdentifier, ?string $languageCode = null): ?Field + { + if (null === $languageCode) { + $languageCode = $this->getDefaultLanguageCode(); + } + + return $this->fieldDefinitionTranslationMap[$fieldDefIdentifier][$languageCode] ?? null; + } + + public function getDefaultLanguageCode(): string + { + return $this->prioritizedFieldLanguageCode ?? $this->versionInfo->contentInfo->mainLanguageCode; + } + + /** + * {@inheritdoc} + */ + protected function getProperties($dynamicProperties = ['id', 'contentInfo']) + { + return parent::getProperties($dynamicProperties); + } + + /** + * {@inheritdoc} + */ + public function __get($property) + { + switch ($property) { + case 'id': + return $this->getVersionInfo()->getContentInfo()->getId(); + + case 'contentInfo': + return $this->getVersionInfo()->getContentInfo(); + + case 'thumbnail': + return $this->getThumbnail(); + } + + return parent::__get($property); + } + + /** + * {@inheritdoc} + */ + public function __isset($property) + { + if ($property === 'id') { + return true; + } + + if ($property === 'contentInfo') { + return true; + } + + return parent::__isset($property); + } +} + +class_alias(Content::class, 'eZ\Publish\Core\Repository\Values\Content\Content'); diff --git a/src/lib/Repository/Values/Content/ContentCreateStruct.php b/src/lib/Repository/Values/Content/ContentCreateStruct.php new file mode 100644 index 0000000000..2acd312d1f --- /dev/null +++ b/src/lib/Repository/Values/Content/ContentCreateStruct.php @@ -0,0 +1,58 @@ +mainLanguageCode; + } + + $this->fields[] = new Field( + [ + 'fieldDefIdentifier' => $fieldDefIdentifier, + 'value' => $value, + 'languageCode' => $language, + ] + ); + } +} + +class_alias(ContentCreateStruct::class, 'eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct'); diff --git a/src/lib/Repository/Values/Content/ContentUpdateStruct.php b/src/lib/Repository/Values/Content/ContentUpdateStruct.php new file mode 100644 index 0000000000..aed90dd745 --- /dev/null +++ b/src/lib/Repository/Values/Content/ContentUpdateStruct.php @@ -0,0 +1,52 @@ +fields[] = new Field( + [ + 'fieldDefIdentifier' => $fieldDefIdentifier, + 'value' => $value, + 'languageCode' => $language, + ] + ); + } +} + +class_alias(ContentUpdateStruct::class, 'eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct'); diff --git a/src/lib/Repository/Values/Content/Location.php b/src/lib/Repository/Values/Content/Location.php new file mode 100644 index 0000000000..4b5caf6b1f --- /dev/null +++ b/src/lib/Repository/Values/Content/Location.php @@ -0,0 +1,95 @@ +contentInfo; + } + + public function getParentLocation(): ?APILocation + { + return $this->parentLocation; + } + + /** + * Function where list of properties are returned. + * + * Override to add dynamic properties + * + * @uses \parent::getProperties() + * + * @param array $dynamicProperties + * + * @return array + */ + protected function getProperties($dynamicProperties = ['contentId']) + { + return parent::getProperties($dynamicProperties); + } + + /** + * Magic getter for retrieving convenience properties. + * + * @param string $property The name of the property to retrieve + * + * @return mixed + */ + public function __get($property) + { + if ($property === 'contentId') { + return $this->getContentInfo()->getId(); + } + + return parent::__get($property); + } + + /** + * Magic isset for signaling existence of convenience properties. + * + * @param string $property + * + * @return bool + */ + public function __isset($property) + { + if ($property === 'contentId') { + return true; + } + + return parent::__isset($property); + } +} + +class_alias(Location::class, 'eZ\Publish\Core\Repository\Values\Content\Location'); diff --git a/src/lib/Repository/Values/Content/Query/Criterion/PermissionSubtree.php b/src/lib/Repository/Values/Content/Query/Criterion/PermissionSubtree.php new file mode 100644 index 0000000000..4f0495f93f --- /dev/null +++ b/src/lib/Repository/Values/Content/Query/Criterion/PermissionSubtree.php @@ -0,0 +1,36 @@ +sourceContentInfo; + } + + /** + * the content of the destination content of the relation. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo + */ + public function getDestinationContentInfo(): APIContentInfo + { + return $this->destinationContentInfo; + } +} + +class_alias(Relation::class, 'eZ\Publish\Core\Repository\Values\Content\Relation'); diff --git a/src/lib/Repository/Values/Content/TrashItem.php b/src/lib/Repository/Values/Content/TrashItem.php new file mode 100644 index 0000000000..0c0bc1e30e --- /dev/null +++ b/src/lib/Repository/Values/Content/TrashItem.php @@ -0,0 +1,107 @@ + */ + protected array $removedLocationContentIdMap = []; + + /** + * Returns the content info of the content object of this trash item. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo + */ + public function getContentInfo(): APIContentInfo + { + return $this->contentInfo; + } + + public function getParentLocation(): ?Location + { + return $this->parentLocation; + } + + /** + * @return array + */ + public function getRemovedLocationContentIdMap(): array + { + return $this->removedLocationContentIdMap; + } + + /** + * Function where list of properties are returned. + * + * Override to add dynamic properties + * + * @uses \parent::getProperties() + * + * @param array $dynamicProperties + * + * @return array + */ + protected function getProperties($dynamicProperties = ['contentId', 'path']) + { + return parent::getProperties($dynamicProperties); + } + + /** + * Magic getter for retrieving convenience properties. + * + * @param string $property The name of the property to retrieve + * + * @return mixed + */ + public function __get($property) + { + if ($property === 'contentId') { + return $this->contentInfo->id; + } + + return parent::__get($property); + } + + /** + * Magic isset for signaling existence of convenience properties. + * + * @param string $property + * + * @return bool + */ + public function __isset($property) + { + if ($property === 'contentId') { + return true; + } + + return parent::__isset($property); + } +} + +class_alias(TrashItem::class, 'eZ\Publish\Core\Repository\Values\Content\TrashItem'); diff --git a/src/lib/Repository/Values/Content/VersionInfo.php b/src/lib/Repository/Values/Content/VersionInfo.php new file mode 100644 index 0000000000..891fb19137 --- /dev/null +++ b/src/lib/Repository/Values/Content/VersionInfo.php @@ -0,0 +1,120 @@ +contentInfo; + } + + /** + * {@inheritdoc} + */ + public function getCreator(): User + { + return $this->creator; + } + + /** + * {@inheritdoc} + */ + public function getInitialLanguage(): Language + { + return $this->initialLanguage; + } + + /** + * {@inheritdoc} + */ + public function getLanguages(): iterable + { + return $this->languages; + } + + /** + * {@inheritdoc} + */ + public function getNames() + { + return $this->names; + } + + /** + * {@inheritdoc} + */ + public function getName($languageCode = null) + { + if ($languageCode) { + return isset($this->names[$languageCode]) ? $this->names[$languageCode] : null; + } + + if ($this->prioritizedNameLanguageCode) { + return $this->names[$this->prioritizedNameLanguageCode]; + } elseif (!empty($this->contentInfo->alwaysAvailable) && isset($this->names[$this->contentInfo->mainLanguageCode])) { + return $this->names[$this->contentInfo->mainLanguageCode]; + } + + // Versioned name should always exists in initial language for a version so we use that as final fallback + return $this->names[$this->initialLanguageCode]; + } +} + +class_alias(VersionInfo::class, 'eZ\Publish\Core\Repository\Values\Content\VersionInfo'); diff --git a/src/lib/Repository/Values/ContentType/ContentType.php b/src/lib/Repository/Values/ContentType/ContentType.php new file mode 100644 index 0000000000..d20ce31e2a --- /dev/null +++ b/src/lib/Repository/Values/ContentType/ContentType.php @@ -0,0 +1,86 @@ +fieldDefinitions = new FieldDefinitionCollection(); + + parent::__construct($data); + } + + /** + * This method returns the content type groups this content type is assigned to. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup[] + */ + public function getContentTypeGroups() + { + return $this->contentTypeGroups; + } + + /** + * This method returns the content type field definitions from this type. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCollection + */ + public function getFieldDefinitions(): APIFieldDefinitionCollection + { + return $this->fieldDefinitions; + } +} + +class_alias(ContentType::class, 'eZ\Publish\Core\Repository\Values\ContentType\ContentType'); diff --git a/src/lib/Repository/Values/ContentType/ContentTypeCreateStruct.php b/src/lib/Repository/Values/ContentType/ContentTypeCreateStruct.php new file mode 100644 index 0000000000..c9d5c10188 --- /dev/null +++ b/src/lib/Repository/Values/ContentType/ContentTypeCreateStruct.php @@ -0,0 +1,39 @@ +fieldDefinitions[] = $fieldDef; + } +} + +class_alias(ContentTypeCreateStruct::class, 'eZ\Publish\Core\Repository\Values\ContentType\ContentTypeCreateStruct'); diff --git a/src/lib/Repository/Values/ContentType/ContentTypeDraft.php b/src/lib/Repository/Values/ContentType/ContentTypeDraft.php new file mode 100644 index 0000000000..b202f815e3 --- /dev/null +++ b/src/lib/Repository/Values/ContentType/ContentTypeDraft.php @@ -0,0 +1,153 @@ +innerContentType->$property; + } + + /** + * Magic set for routing set calls to innerContentType. + * + * @param string $property + * @param mixed $propertyValue + */ + public function __set($property, $propertyValue) + { + $this->innerContentType->$property = $propertyValue; + } + + /** + * Magic isset for routing isset calls to innerContentType. + * + * @param string $property + * + * @return bool + */ + public function __isset($property) + { + return $this->innerContentType->__isset($property); + } + + /** + * Holds internal content type object. + * + * @var \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + * + * @todo document + */ + protected $innerContentType; + + /** + * {@inheritdoc} + */ + public function getNames() + { + return $this->innerContentType->getNames(); + } + + /** + * {@inheritdoc} + */ + public function getName($languageCode = null) + { + return $this->innerContentType->getName($languageCode); + } + + /** + * {@inheritdoc} + */ + public function getDescriptions() + { + return $this->innerContentType->getDescriptions(); + } + + /** + * {@inheritdoc} + */ + public function getDescription($languageCode = null) + { + return $this->innerContentType->getDescription($languageCode); + } + + /** + * This method returns the content type groups this content type is assigned to. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup[] + */ + public function getContentTypeGroups() + { + return $this->innerContentType->contentTypeGroups; + } + + /** + * This method returns the content type field definitions from this type. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition[] + */ + public function getFieldDefinitions(): APIFieldDefinitionCollection + { + return $this->innerContentType->getFieldDefinitions(); + } + + /** + * This method returns the field definition for the given identifier. + * + * @param string $fieldDefinitionIdentifier + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition + */ + public function getFieldDefinition($fieldDefinitionIdentifier): ?FieldDefinition + { + return $this->innerContentType->getFieldDefinition($fieldDefinitionIdentifier); + } + + public function hasFieldDefinition(string $fieldDefinitionIdentifier): bool + { + return $this->innerContentType->hasFieldDefinition($fieldDefinitionIdentifier); + } +} + +class_alias(ContentTypeDraft::class, 'eZ\Publish\Core\Repository\Values\ContentType\ContentTypeDraft'); diff --git a/src/lib/Repository/Values/ContentType/ContentTypeGroup.php b/src/lib/Repository/Values/ContentType/ContentTypeGroup.php new file mode 100644 index 0000000000..b6c39bee0d --- /dev/null +++ b/src/lib/Repository/Values/ContentType/ContentTypeGroup.php @@ -0,0 +1,36 @@ +validatorConfiguration; + } + + /** + * This method returns settings for the field definition supported by the field type. + * + * @return array + */ + public function getFieldSettings(): array + { + return $this->fieldSettings; + } +} + +class_alias(FieldDefinition::class, 'eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition'); diff --git a/src/lib/Repository/Values/ContentType/FieldDefinitionCollection.php b/src/lib/Repository/Values/ContentType/FieldDefinitionCollection.php new file mode 100644 index 0000000000..31a6b7035f --- /dev/null +++ b/src/lib/Repository/Values/ContentType/FieldDefinitionCollection.php @@ -0,0 +1,197 @@ +fieldDefinitions = []; + $this->fieldDefinitionsByIdentifier = []; + + foreach ($fieldDefinitions as $fieldDefinition) { + $this->fieldDefinitions[] = $fieldDefinition; + $this->fieldDefinitionsByIdentifier[$fieldDefinition->getIdentifier()] = $fieldDefinition; + } + } + + public function get(string $fieldDefinitionIdentifier): FieldDefinition + { + if ($this->has($fieldDefinitionIdentifier)) { + return $this->fieldDefinitionsByIdentifier[$fieldDefinitionIdentifier]; + } + + throw new OutOfBoundsException( + sprintf("Field Definition Collection does not contain element with identifier '%s'", $fieldDefinitionIdentifier) + ); + } + + public function has(string $fieldDefinitionIdentifier): bool + { + return array_key_exists($fieldDefinitionIdentifier, $this->fieldDefinitionsByIdentifier); + } + + public function first(): FieldDefinition + { + if (($result = reset($this->fieldDefinitions)) !== false) { + return $result; + } + + throw new OutOfBoundsException('Field Definition Collection is empty'); + } + + public function last(): FieldDefinition + { + if (($result = end($this->fieldDefinitions)) !== false) { + return $result; + } + + throw new OutOfBoundsException('Field Definition Collection is empty'); + } + + public function isEmpty(): bool + { + return empty($this->fieldDefinitions); + } + + public function filter(Closure $predicate): FieldDefinitionCollectionInterface + { + return new self(array_filter($this->fieldDefinitions, $predicate)); + } + + public function filterByType(string $fieldTypeIdentifier): FieldDefinitionCollectionInterface + { + return $this->filter($this->getIsTypePredicate($fieldTypeIdentifier)); + } + + public function filterByGroup(string $fieldGroup): FieldDefinitionCollectionInterface + { + return $this->filter($this->getInGroupPredicate($fieldGroup)); + } + + public function map(Closure $predicate): array + { + return array_map($predicate, $this->fieldDefinitions); + } + + public function all(Closure $predicate): bool + { + foreach ($this->fieldDefinitions as $fieldDefinition) { + if (!$predicate($fieldDefinition)) { + return false; + } + } + + return true; + } + + public function any(Closure $predicate): bool + { + foreach ($this->fieldDefinitions as $fieldDefinition) { + if ($predicate($fieldDefinition)) { + return true; + } + } + + return false; + } + + public function anyOfType(string $fieldTypeIdentifier): bool + { + return $this->any($this->getIsTypePredicate($fieldTypeIdentifier)); + } + + public function anyInGroup(string $fieldGroup): bool + { + return $this->any($this->getInGroupPredicate($fieldGroup)); + } + + public function partition(Closure $predicate): array + { + $matches = $noMatches = []; + + foreach ($this->fieldDefinitions as $fieldDefinition) { + if ($predicate($fieldDefinition)) { + $matches[] = $fieldDefinition; + } else { + $noMatches[] = $fieldDefinition; + } + } + + return [new self($matches), new self($noMatches)]; + } + + public function count(): int + { + return count($this->fieldDefinitions); + } + + public function getIterator(): Iterator + { + return new ArrayIterator($this->fieldDefinitions); + } + + public function toArray(): array + { + return $this->fieldDefinitions; + } + + private function getIsTypePredicate(string $fieldTypeIdentifier): Closure + { + return static function (FieldDefinition $fieldDefinition) use ($fieldTypeIdentifier) { + return $fieldDefinition->getFieldTypeIdentifier() === $fieldTypeIdentifier; + }; + } + + private function getInGroupPredicate(string $fieldGroup): Closure + { + return static function (FieldDefinition $fieldDefinition) use ($fieldGroup) { + return $fieldDefinition->fieldGroup === $fieldGroup; + }; + } + + public function offsetExists($offset): bool + { + return isset($this->fieldDefinitions[$offset]); + } + + public function offsetGet($offset): FieldDefinition + { + return $this->fieldDefinitions[$offset]; + } + + public function offsetSet($offset, $value): void + { + throw new BadMethodCallException(self::class . ' is read-only!'); + } + + public function offsetUnset($offset): void + { + throw new BadMethodCallException(self::class . ' is read-only!'); + } +} + +class_alias(FieldDefinitionCollection::class, 'eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection'); diff --git a/src/lib/Repository/Values/ContentType/FieldType.php b/src/lib/Repository/Values/ContentType/FieldType.php new file mode 100644 index 0000000000..2d29dd6085 --- /dev/null +++ b/src/lib/Repository/Values/ContentType/FieldType.php @@ -0,0 +1,301 @@ +internalFieldType = $fieldType; + } + + /** + * Returns the field type identifier for this field type. + * + * @return string + */ + public function getFieldTypeIdentifier(): string + { + return $this->internalFieldType->getFieldTypeIdentifier(); + } + + public function getName(Value $value, APIFieldDefinition $fieldDefinition, string $languageCode): string + { + return $this->internalFieldType->getName($value, $fieldDefinition, $languageCode); + } + + /** + * Returns a schema for the settings expected by the FieldType. + * + * Returns an arbitrary value, representing a schema for the settings of + * the FieldType. + * + * Explanation: There are no possible generic schemas for defining settings + * input, which is why no schema for the return value of this method is + * defined. It is up to the implementer to define and document a schema for + * the return value and document it. In addition, it is necessary that all + * consumers of this interface (e.g. Public API, REST API, GUIs, ...) + * provide plugin mechanisms to hook adapters for the specific FieldType + * into. These adapters then need to be either shipped with the FieldType + * or need to be implemented by a third party. If there is no adapter + * available for a specific FieldType, it will not be usable with the + * consumer. + * + * @return mixed + */ + public function getSettingsSchema() + { + return $this->internalFieldType->getSettingsSchema(); + } + + /** + * Returns a schema for the validator configuration expected by the FieldType. + * + * Returns an arbitrary value, representing a schema for the validator + * configuration of the FieldType. + * + * Explanation: There are no possible generic schemas for defining settings + * input, which is why no schema for the return value of this method is + * defined. It is up to the implementer to define and document a schema for + * the return value and document it. In addition, it is necessary that all + * consumers of this interface (e.g. Public API, REST API, GUIs, ...) + * provide plugin mechanisms to hook adapters for the specific FieldType + * into. These adapters then need to be either shipped with the FieldType + * or need to be implemented by a third party. If there is no adapter + * available for a specific FieldType, it will not be usable with the + * consumer. + * + * Best practice: + * + * It is considered best practice to return a hash map, which contains + * rudimentary settings structures, like e.g. for the "ezstring" FieldType + * + * + * array( + * 'stringLength' => array( + * 'minStringLength' => array( + * 'type' => 'int', + * 'default' => 0, + * ), + * 'maxStringLength' => array( + * 'type' => 'int' + * 'default' => null, + * ) + * ), + * ); + * + * + * @return mixed + */ + public function getValidatorConfigurationSchema() + { + return $this->internalFieldType->getValidatorConfigurationSchema(); + } + + /** + * Indicates if the field type supports indexing and sort keys for searching. + * + * @return bool + */ + public function isSearchable(): bool + { + return $this->internalFieldType->isSearchable(); + } + + /** + * Indicates if the field definition of this type can appear only once in the same ContentType. + * + * @return bool + */ + public function isSingular(): bool + { + return $this->internalFieldType->isSingular(); + } + + /** + * Indicates if the field definition of this type can be added to a ContentType with Content instances. + * + * @return bool + */ + public function onlyEmptyInstance(): bool + { + return $this->internalFieldType->onlyEmptyInstance(); + } + + /** + * Returns the fallback default value of field type when no such default + * value is provided in the field definition in content types. + * + * @return mixed + */ + public function getEmptyValue() + { + return $this->internalFieldType->getEmptyValue(); + } + + /** + * Returns if the given $value is considered empty by the field type. + * + * Usually, only the value returned by {@link getEmptyValue()} is + * considered empty but that is not always the case. + * + * Note: This function assumes that $value is valid so this function can only + * be used reliably on $values that came from the API, not from the user. + * + * @param mixed $value + * + * @return bool + */ + public function isEmptyValue($value): bool + { + return $this->internalFieldType->isEmptyValue($value); + } + + /** + * Converts an $hash to the Value defined by the field type. + * + * @param mixed $hash + * + * @return mixed + */ + public function fromHash($hash) + { + return $this->internalFieldType->fromHash($hash); + } + + /** + * Converts a Value to a hash. + * + * @param mixed $value + * + * @return mixed + */ + public function toHash($value) + { + return $this->internalFieldType->toHash($value); + } + + /** + * Converts the given $fieldSettings to a simple hash format. + * + * @param mixed $fieldSettings + * + * @return array|scalar|null + */ + public function fieldSettingsToHash($fieldSettings) + { + return $this->internalFieldType->fieldSettingsToHash($fieldSettings); + } + + /** + * Converts the given $fieldSettingsHash to field settings of the type. + * + * This is the reverse operation of {@link fieldSettingsToHash()}. + * + * @param array|scalar|null $fieldSettingsHash + * + * @return mixed + */ + public function fieldSettingsFromHash($fieldSettingsHash) + { + return $this->internalFieldType->fieldSettingsFromHash($fieldSettingsHash); + } + + /** + * Converts the given $validatorConfiguration to a simple hash format. + * + * @param mixed $validatorConfiguration + * + * @return array|scalar|null + */ + public function validatorConfigurationToHash($validatorConfiguration) + { + return $this->internalFieldType->validatorConfigurationToHash($validatorConfiguration); + } + + /** + * Converts the given $validatorConfigurationHash to a validator + * configuration of the type. + * + * @param array|scalar|null $validatorConfigurationHash + * + * @return mixed + */ + public function validatorConfigurationFromHash($validatorConfigurationHash) + { + return $this->internalFieldType->validatorConfigurationFromHash($validatorConfigurationHash); + } + + /** + * Validates the validatorConfiguration of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * This methods determines if the given $validatorConfiguration is + * structurally correct and complies to the validator configuration schema as defined in FieldType. + * + * @param mixed $validatorConfiguration + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateValidatorConfiguration($validatorConfiguration): iterable + { + return $this->internalFieldType->validateValidatorConfiguration($validatorConfiguration); + } + + /** + * Validates the fieldSettings of a FieldDefinitionCreateStruct or FieldDefinitionUpdateStruct. + * + * This methods determines if the given $fieldSettings are structurally + * correct and comply to the settings schema as defined in FieldType. + * + * @param mixed $fieldSettings + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateFieldSettings($fieldSettings): iterable + { + return $this->internalFieldType->validateFieldSettings($fieldSettings); + } + + /** + * Validates a field value based on the validator configuration in the field definition. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition $fieldDef The field definition of the field + * @param \Ibexa\Contracts\Core\FieldType\Value $value The field value for which an action is performed + * + * @return \Ibexa\Contracts\Core\FieldType\ValidationError[] + */ + public function validateValue(APIFieldDefinition $fieldDef, Value $value): iterable + { + return $this->internalFieldType->validate($fieldDef, $value); + } +} + +class_alias(FieldType::class, 'eZ\Publish\Core\Repository\Values\ContentType\FieldType'); diff --git a/eZ/Publish/Core/Repository/Values/MultiLanguageDescriptionTrait.php b/src/lib/Repository/Values/MultiLanguageDescriptionTrait.php similarity index 88% rename from eZ/Publish/Core/Repository/Values/MultiLanguageDescriptionTrait.php rename to src/lib/Repository/Values/MultiLanguageDescriptionTrait.php index e62caedb35..ec3756504c 100644 --- a/eZ/Publish/Core/Repository/Values/MultiLanguageDescriptionTrait.php +++ b/src/lib/Repository/Values/MultiLanguageDescriptionTrait.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Values; +namespace Ibexa\Core\Repository\Values; /** * @internal Meant for internal use by Repository, type hint against API object instead. @@ -46,3 +46,5 @@ public function getDescription($languageCode = null) : reset($this->descriptions); } } + +class_alias(MultiLanguageDescriptionTrait::class, 'eZ\Publish\Core\Repository\Values\MultiLanguageDescriptionTrait'); diff --git a/eZ/Publish/Core/Repository/Values/MultiLanguageNameTrait.php b/src/lib/Repository/Values/MultiLanguageNameTrait.php similarity index 88% rename from eZ/Publish/Core/Repository/Values/MultiLanguageNameTrait.php rename to src/lib/Repository/Values/MultiLanguageNameTrait.php index c31beee6de..159c05c54f 100644 --- a/eZ/Publish/Core/Repository/Values/MultiLanguageNameTrait.php +++ b/src/lib/Repository/Values/MultiLanguageNameTrait.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Values; +namespace Ibexa\Core\Repository\Values; /** * @internal Meant for internal use by Repository, type hint against API object instead. @@ -46,3 +46,5 @@ public function getName($languageCode = null) : reset($this->names); } } + +class_alias(MultiLanguageNameTrait::class, 'eZ\Publish\Core\Repository\Values\MultiLanguageNameTrait'); diff --git a/eZ/Publish/Core/Repository/Values/MultiLanguageTrait.php b/src/lib/Repository/Values/MultiLanguageTrait.php similarity index 80% rename from eZ/Publish/Core/Repository/Values/MultiLanguageTrait.php rename to src/lib/Repository/Values/MultiLanguageTrait.php index 66f78c82a7..8643055ab6 100644 --- a/eZ/Publish/Core/Repository/Values/MultiLanguageTrait.php +++ b/src/lib/Repository/Values/MultiLanguageTrait.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Values; +namespace Ibexa\Core\Repository\Values; /** * @internal Meant for internal use by Repository, type hint against API object instead. @@ -25,3 +25,5 @@ trait MultiLanguageTrait */ protected $prioritizedLanguages = []; } + +class_alias(MultiLanguageTrait::class, 'eZ\Publish\Core\Repository\Values\MultiLanguageTrait'); diff --git a/src/lib/Repository/Values/ObjectState/ObjectState.php b/src/lib/Repository/Values/ObjectState/ObjectState.php new file mode 100644 index 0000000000..b9b8941135 --- /dev/null +++ b/src/lib/Repository/Values/ObjectState/ObjectState.php @@ -0,0 +1,91 @@ +objectStateGroup; + } + + /** + * Magic getter for BC reasons. + * + * @param string $property + * + * @return mixed + */ + public function __get($property) + { + if ($property === 'defaultLanguageCode') { + @trigger_error( + __CLASS__ . '::$defaultLanguageCode is deprecated. Use mainLanguageCode', + E_USER_DEPRECATED + ); + + return $this->mainLanguageCode; + } + + return parent::__get($property); + } + + /** + * Magic isset for BC reasons. + * + * @param string $property + * + * @return bool + */ + public function __isset($property) + { + if ($property === 'defaultLanguageCode') { + @trigger_error( + __CLASS__ . '::$defaultLanguageCode is deprecated. Use mainLanguageCode' + ); + + return true; + } + + return parent::__isset($property); + } +} + +class_alias(ObjectState::class, 'eZ\Publish\Core\Repository\Values\ObjectState\ObjectState'); diff --git a/src/lib/Repository/Values/ObjectState/ObjectStateGroup.php b/src/lib/Repository/Values/ObjectState/ObjectStateGroup.php new file mode 100644 index 0000000000..411490eb79 --- /dev/null +++ b/src/lib/Repository/Values/ObjectState/ObjectStateGroup.php @@ -0,0 +1,68 @@ +mainLanguageCode; + } + + return parent::__get($property); + } + + public function __isset($property) + { + if ($property === 'defaultLanguageCode') { + @trigger_error( + __CLASS__ . '::$defaultLanguageCode is deprecated. Use mainLanguageCode', + E_USER_DEPRECATED + ); + + return true; + } + + return parent::__isset($property); + } +} + +class_alias(ObjectStateGroup::class, 'eZ\Publish\Core\Repository\Values\ObjectState\ObjectStateGroup'); diff --git a/src/lib/Repository/Values/User/Policy.php b/src/lib/Repository/Values/User/Policy.php new file mode 100644 index 0000000000..5011b41c9d --- /dev/null +++ b/src/lib/Repository/Values/User/Policy.php @@ -0,0 +1,38 @@ +limitations; + } +} + +class_alias(Policy::class, 'eZ\Publish\Core\Repository\Values\User\Policy'); diff --git a/src/lib/Repository/Values/User/PolicyCreateStruct.php b/src/lib/Repository/Values/User/PolicyCreateStruct.php new file mode 100644 index 0000000000..98dc802905 --- /dev/null +++ b/src/lib/Repository/Values/User/PolicyCreateStruct.php @@ -0,0 +1,50 @@ +limitations; + } + + /** + * Adds a limitation with the given identifier and list of values. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + */ + public function addLimitation(Limitation $limitation): void + { + $limitationIdentifier = $limitation->getIdentifier(); + $this->limitations[$limitationIdentifier] = $limitation; + } +} + +class_alias(PolicyCreateStruct::class, 'eZ\Publish\Core\Repository\Values\User\PolicyCreateStruct'); diff --git a/src/lib/Repository/Values/User/PolicyDraft.php b/src/lib/Repository/Values/User/PolicyDraft.php new file mode 100644 index 0000000000..66b8d97889 --- /dev/null +++ b/src/lib/Repository/Values/User/PolicyDraft.php @@ -0,0 +1,66 @@ + true]; + + public function __get($property) + { + if (isset($this->draftProperties[$property])) { + return parent::__get($property); + } + + return $this->innerPolicy->$property; + } + + public function __set($property, $propertyValue) + { + if (isset($this->draftProperties[$property])) { + parent::__set($property, $propertyValue); + } + + $this->innerPolicy->$property = $propertyValue; + } + + public function __isset($property) + { + if (isset($this->draftProperties[$property])) { + return parent::__isset($property); + } + + return $this->innerPolicy->__isset($property); + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation[] + */ + public function getLimitations(): iterable + { + return $this->innerPolicy->getLimitations(); + } +} + +class_alias(PolicyDraft::class, 'eZ\Publish\Core\Repository\Values\User\PolicyDraft'); diff --git a/src/lib/Repository/Values/User/PolicyUpdateStruct.php b/src/lib/Repository/Values/User/PolicyUpdateStruct.php new file mode 100644 index 0000000000..93e5011c0b --- /dev/null +++ b/src/lib/Repository/Values/User/PolicyUpdateStruct.php @@ -0,0 +1,50 @@ +limitations; + } + + /** + * Adds a limitation to the policy - if a Limitation exists with the same identifier + * the existing limitation is replaced. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + */ + public function addLimitation(Limitation $limitation): void + { + $limitationIdentifier = $limitation->getIdentifier(); + $this->limitations[$limitationIdentifier] = $limitation; + } +} + +class_alias(PolicyUpdateStruct::class, 'eZ\Publish\Core\Repository\Values\User\PolicyUpdateStruct'); diff --git a/src/lib/Repository/Values/User/Role.php b/src/lib/Repository/Values/User/Role.php new file mode 100644 index 0000000000..fc1eae0e36 --- /dev/null +++ b/src/lib/Repository/Values/User/Role.php @@ -0,0 +1,38 @@ +policies; + } +} + +class_alias(Role::class, 'eZ\Publish\Core\Repository\Values\User\Role'); diff --git a/src/lib/Repository/Values/User/RoleCopyStruct.php b/src/lib/Repository/Values/User/RoleCopyStruct.php new file mode 100644 index 0000000000..cc4812590e --- /dev/null +++ b/src/lib/Repository/Values/User/RoleCopyStruct.php @@ -0,0 +1,49 @@ +policies; + } + + /** + * Adds a policy to this role. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct $policyCreateStruct + */ + public function addPolicy(APIPolicyCreateStruct $policyCreateStruct): void + { + $this->policies[] = $policyCreateStruct; + } +} + +class_alias(RoleCopyStruct::class, 'eZ\Publish\Core\Repository\Values\User\RoleCopyStruct'); diff --git a/src/lib/Repository/Values/User/RoleCreateStruct.php b/src/lib/Repository/Values/User/RoleCreateStruct.php new file mode 100644 index 0000000000..da5c69ef9b --- /dev/null +++ b/src/lib/Repository/Values/User/RoleCreateStruct.php @@ -0,0 +1,49 @@ +policies; + } + + /** + * Adds a policy to this role. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct $policyCreateStruct + */ + public function addPolicy(APIPolicyCreateStruct $policyCreateStruct): void + { + $this->policies[] = $policyCreateStruct; + } +} + +class_alias(RoleCreateStruct::class, 'eZ\Publish\Core\Repository\Values\User\RoleCreateStruct'); diff --git a/src/lib/Repository/Values/User/RoleDraft.php b/src/lib/Repository/Values/User/RoleDraft.php new file mode 100644 index 0000000000..e064fbf04f --- /dev/null +++ b/src/lib/Repository/Values/User/RoleDraft.php @@ -0,0 +1,75 @@ +innerRole->$property; + } + + /** + * Magic set for routing set calls to innerRole. + * + * @param string $property + * @param mixed $propertyValue + */ + public function __set($property, $propertyValue) + { + $this->innerRole->$property = $propertyValue; + } + + /** + * Magic isset for routing isset calls to innerRole. + * + * @param string $property + * + * @return bool + */ + public function __isset($property) + { + return $this->innerRole->__isset($property); + } + + /** + * Returns the list of policies of this role. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft[] + */ + public function getPolicies(): iterable + { + return $this->innerRole->getPolicies(); + } +} + +class_alias(RoleDraft::class, 'eZ\Publish\Core\Repository\Values\User\RoleDraft'); diff --git a/src/lib/Repository/Values/User/User.php b/src/lib/Repository/Values/User/User.php new file mode 100644 index 0000000000..8f757e0cb7 --- /dev/null +++ b/src/lib/Repository/Values/User/User.php @@ -0,0 +1,198 @@ +content->getVersionInfo(); + } + + public function getContentType(): ContentType + { + return $this->content->getContentType(); + } + + /** + * Returns a field value for the given value + * $version->fields[$fieldDefId][$languageCode] is an equivalent call + * if no language is given on a translatable field this method returns + * the value of the initial language of the version if present, otherwise null. + * On non translatable fields this method ignores the languageCode parameter. + * + * @param string $fieldDefIdentifier + * @param string $languageCode + * + * @return mixed a primitive type or a field type Value object depending on the field type. + */ + public function getFieldValue(string $fieldDefIdentifier, ?string $languageCode = null): ?Value + { + return $this->content->getFieldValue($fieldDefIdentifier, $languageCode); + } + + /** + * This method returns the complete fields collection. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field[] + */ + public function getFields(): iterable + { + return $this->content->getFields(); + } + + /** + * This method returns the fields for a given language and non translatable fields. + * + * If note set the initialLanguage of the content version is used. + * + * @param string $languageCode + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field[] with field identifier as keys + */ + public function getFieldsByLanguage(?string $languageCode = null): iterable + { + return $this->content->getFieldsByLanguage($languageCode); + } + + /** + * This method returns the field for a given field definition identifier and language. + * + * If not set the initialLanguage of the content version is used. + * + * @param string $fieldDefIdentifier + * @param string|null $languageCode + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field|null A {@link Field} or null if nothing is found + */ + public function getField(string $fieldDefIdentifier, ?string $languageCode = null): ?Field + { + return $this->content->getField($fieldDefIdentifier, $languageCode); + } + + /** + * Function where list of properties are returned. + * + * Override to add dynamic properties + * + * @uses \parent::getProperties() + * + * @param array $dynamicProperties + * + * @return array + */ + protected function getProperties($dynamicProperties = ['id', 'contentInfo', 'versionInfo', 'fields']) + { + return parent::getProperties($dynamicProperties); + } + + /** + * Magic getter for retrieving convenience properties. + * + * @param string $property The name of the property to retrieve + * + * @return mixed + */ + public function __get($property) + { + switch ($property) { + case 'contentInfo': + return $this->getVersionInfo()->getContentInfo(); + + case 'id': + return $this->getVersionInfo()->getContentInfo()->getId(); + + case 'versionInfo': + return $this->getVersionInfo(); + + case 'fields': + return $this->getFields(); + + case 'thumbnail': + return $this->getThumbnail(); + + case 'content': + // trigger error for this, but for BC let it pass on to normal __get lookup for now + @trigger_error( + sprintf('%s is an internal property, usage is deprecated as of 6.10. User itself exposes everything needed.', $property), + E_USER_DEPRECATED + ); + } + + return parent::__get($property); + } + + /** + * Magic isset for signaling existence of convenience properties. + * + * @param string $property + * + * @return bool + */ + public function __isset($property) + { + if ($property === 'contentInfo') { + return true; + } + + if ($property === 'id') { + return true; + } + + if ($property === 'versionInfo') { + return true; + } + + if ($property === 'thumbnail') { + return true; + } + + if ($property === 'fields') { + return true; + } + + return parent::__isset($property); + } + + public function getThumbnail(): ?Thumbnail + { + return $this->content->getThumbnail(); + } + + public function getDefaultLanguageCode(): string + { + return $this->content->getDefaultLanguageCode(); + } +} + +class_alias(User::class, 'eZ\Publish\Core\Repository\Values\User\User'); diff --git a/src/lib/Repository/Values/User/UserCreateStruct.php b/src/lib/Repository/Values/User/UserCreateStruct.php new file mode 100644 index 0000000000..e2426f53ef --- /dev/null +++ b/src/lib/Repository/Values/User/UserCreateStruct.php @@ -0,0 +1,56 @@ +mainLanguageCode; + } + + $this->fields[] = new Field( + [ + 'fieldDefIdentifier' => $fieldDefIdentifier, + 'value' => $value, + 'languageCode' => $language, + ] + ); + } +} + +class_alias(UserCreateStruct::class, 'eZ\Publish\Core\Repository\Values\User\UserCreateStruct'); diff --git a/src/lib/Repository/Values/User/UserGroup.php b/src/lib/Repository/Values/User/UserGroup.php new file mode 100644 index 0000000000..44e75313da --- /dev/null +++ b/src/lib/Repository/Values/User/UserGroup.php @@ -0,0 +1,198 @@ +content->getVersionInfo(); + } + + public function getContentType(): ContentType + { + return $this->content->getContentType(); + } + + /** + * Returns a field value for the given value + * $version->fields[$fieldDefId][$languageCode] is an equivalent call + * if no language is given on a translatable field this method returns + * the value of the initial language of the version if present, otherwise null. + * On non translatable fields this method ignores the languageCode parameter. + * + * @param string $fieldDefIdentifier + * @param string|null $languageCode + * + * @return mixed a primitive type or a field type Value object depending on the field type. + */ + public function getFieldValue(string $fieldDefIdentifier, ?string $languageCode = null): ?Value + { + return $this->content->getFieldValue($fieldDefIdentifier, $languageCode); + } + + /** + * This method returns the complete fields collection. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field[] + */ + public function getFields(): iterable + { + return $this->content->getFields(); + } + + /** + * This method returns the fields for a given language and non translatable fields. + * + * If note set the initialLanguage of the content version is used. + * + * @param string $languageCode + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field[] with field identifier as keys + */ + public function getFieldsByLanguage(?string $languageCode = null): iterable + { + return $this->content->getFieldsByLanguage($languageCode); + } + + /** + * This method returns the field for a given field definition identifier and language. + * + * If not set the initialLanguage of the content version is used. + * + * @param string $fieldDefIdentifier + * @param string|null $languageCode + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field|null A {@link Field} or null if nothing is found + */ + public function getField(string $fieldDefIdentifier, ?string $languageCode = null): ?Field + { + return $this->content->getField($fieldDefIdentifier, $languageCode); + } + + /** + * Function where list of properties are returned. + * + * Override to add dynamic properties + * + * @uses \parent::getProperties() + * + * @param array $dynamicProperties + * + * @return array + */ + protected function getProperties($dynamicProperties = ['id', 'contentInfo', 'versionInfo', 'fields']) + { + return parent::getProperties($dynamicProperties); + } + + /** + * Magic getter for retrieving convenience properties. + * + * @param string $property The name of the property to retrieve + * + * @return mixed + */ + public function __get($property) + { + switch ($property) { + case 'contentInfo': + return $this->getVersionInfo()->getContentInfo(); + + case 'id': + return $this->getVersionInfo()->getContentInfo()->id; + + case 'versionInfo': + return $this->getVersionInfo(); + + case 'fields': + return $this->getFields(); + + case 'thumbnail': + return $this->getThumbnail(); + + case 'content': + // trigger error for this, but for BC let it pass on to normal __get lookup for now + @trigger_error( + sprintf('%s is an internal property, usage is deprecated as of 6.10. User Group itself exposes everything needed.', $property), + E_USER_DEPRECATED + ); + } + + return parent::__get($property); + } + + /** + * Magic isset for signaling existence of convenience properties. + * + * @param string $property + * + * @return bool + */ + public function __isset($property) + { + if ($property === 'contentInfo') { + return true; + } + + if ($property === 'id') { + return true; + } + + if ($property === 'versionInfo') { + return true; + } + + if ($property === 'fields') { + return true; + } + + if ($property === 'thumbnail') { + return true; + } + + return parent::__isset($property); + } + + public function getThumbnail(): ?Thumbnail + { + return $this->content->getThumbnail(); + } + + public function getDefaultLanguageCode(): string + { + return $this->content->getDefaultLanguageCode(); + } +} + +class_alias(UserGroup::class, 'eZ\Publish\Core\Repository\Values\User\UserGroup'); diff --git a/src/lib/Repository/Values/User/UserGroupCreateStruct.php b/src/lib/Repository/Values/User/UserGroupCreateStruct.php new file mode 100644 index 0000000000..60fd8a186a --- /dev/null +++ b/src/lib/Repository/Values/User/UserGroupCreateStruct.php @@ -0,0 +1,56 @@ +mainLanguageCode; + } + + $this->fields[] = new Field( + [ + 'fieldDefIdentifier' => $fieldDefIdentifier, + 'value' => $value, + 'languageCode' => $language, + ] + ); + } +} + +class_alias(UserGroupCreateStruct::class, 'eZ\Publish\Core\Repository\Values\User\UserGroupCreateStruct'); diff --git a/src/lib/Repository/Values/User/UserGroupRoleAssignment.php b/src/lib/Repository/Values/User/UserGroupRoleAssignment.php new file mode 100644 index 0000000000..c4ee510ef3 --- /dev/null +++ b/src/lib/Repository/Values/User/UserGroupRoleAssignment.php @@ -0,0 +1,75 @@ +limitation; + } + + /** + * Returns the role to which the user group is assigned to. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Role + */ + public function getRole(): APIRole + { + return $this->role; + } + + /** + * Returns the user group to which the role is assigned to. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup + */ + public function getUserGroup(): APIUserGroup + { + return $this->userGroup; + } +} + +class_alias(UserGroupRoleAssignment::class, 'eZ\Publish\Core\Repository\Values\User\UserGroupRoleAssignment'); diff --git a/src/lib/Repository/Values/User/UserReference.php b/src/lib/Repository/Values/User/UserReference.php new file mode 100644 index 0000000000..b32061efa7 --- /dev/null +++ b/src/lib/Repository/Values/User/UserReference.php @@ -0,0 +1,39 @@ +userId = $userId; + } + + /** + * The User id of the User this reference represent. + * + * @return int + */ + public function getUserId(): int + { + return $this->userId; + } +} + +class_alias(UserReference::class, 'eZ\Publish\Core\Repository\Values\User\UserReference'); diff --git a/src/lib/Repository/Values/User/UserRoleAssignment.php b/src/lib/Repository/Values/User/UserRoleAssignment.php new file mode 100644 index 0000000000..c0fad74654 --- /dev/null +++ b/src/lib/Repository/Values/User/UserRoleAssignment.php @@ -0,0 +1,75 @@ +limitation; + } + + /** + * Returns the role to which the user is assigned to. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Role + */ + public function getRole(): APIRole + { + return $this->role; + } + + /** + * Returns the user to which the role is assigned to. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + */ + public function getUser(): APIUser + { + return $this->user; + } +} + +class_alias(UserRoleAssignment::class, 'eZ\Publish\Core\Repository\Values\User\UserRoleAssignment'); diff --git a/eZ/Publish/Core/settings/content_location_mapper.yml b/src/lib/Resources/settings/content_location_mapper.yml similarity index 89% rename from eZ/Publish/Core/settings/content_location_mapper.yml rename to src/lib/Resources/settings/content_location_mapper.yml index 53e78118c6..b11a52dde8 100644 --- a/eZ/Publish/Core/settings/content_location_mapper.yml +++ b/src/lib/Resources/settings/content_location_mapper.yml @@ -9,6 +9,6 @@ services: Ibexa\Core\Repository\Mapper\ContentLocationMapper\ContentLocationMapper: '@Ibexa\Core\Repository\Mapper\ContentLocationMapper\InMemoryContentLocationMapper' Ibexa\Core\Repository\Mapper\ContentLocationMapper\DecoratedLocationService: - decorates: ezpublish.api.service.inner_location + decorates: ibexa.api.service.inner_location arguments: $innerService: '@.inner' diff --git a/src/lib/Resources/settings/events.yml b/src/lib/Resources/settings/events.yml new file mode 100644 index 0000000000..4027da9755 --- /dev/null +++ b/src/lib/Resources/settings/events.yml @@ -0,0 +1,9 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + Ibexa\Core\Repository\EventSubscriber\DeleteUserSubscriber: ~ + + Ibexa\Core\Repository\EventSubscriber\NameSchemaSubscriber: ~ diff --git a/src/lib/Resources/settings/fieldtype_external_storages.yml b/src/lib/Resources/settings/fieldtype_external_storages.yml new file mode 100644 index 0000000000..d91e8f9144 --- /dev/null +++ b/src/lib/Resources/settings/fieldtype_external_storages.yml @@ -0,0 +1,64 @@ +services: + Ibexa\Core\FieldType\BinaryFile\BinaryFileStorage: + class: Ibexa\Core\FieldType\BinaryFile\BinaryFileStorage + arguments: + $gateway: '@Ibexa\Core\FieldType\BinaryFile\BinaryFileStorage\Gateway\DoctrineStorage' + $ioService: '@ibexa.field_type.ezbinaryfile.io_service' + $pathGenerator: '@Ibexa\Core\FieldType\BinaryBase\PathGenerator\LegacyPathGenerator' + $mimeTypeDetector: '@ibexa.core.io.mimeTypeDetector' + $fileExtensionBlackListValidator: '@Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator' + tags: + - {name: ibexa.field_type.storage.external.handler, alias: ezbinaryfile} + + Ibexa\Core\FieldType\Image\ImageStorage: + class: Ibexa\Core\FieldType\Image\ImageStorage + arguments: + $gateway: '@Ibexa\Core\FieldType\Image\ImageStorage\Gateway\DoctrineStorage' + $ioService: '@Ibexa\Core\FieldType\Image\IO\Legacy' + $pathGenerator: '@Ibexa\Core\FieldType\Image\PathGenerator\LegacyPathGenerator' + $imageSizeMetadataHandler: '@Ibexa\Core\IO\MetadataHandler\ImageSize' + $aliasCleaner: '@Ibexa\Core\FieldType\Image\AliasCleanerInterface' + $filePathNormalizer: '@Ibexa\Core\IO\FilePathNormalizerInterface' + $fileExtensionBlackListValidator: '@Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator' + tags: + - {name: ibexa.field_type.storage.external.handler, alias: ezimage} + + Ibexa\Core\FieldType\Keyword\KeywordStorage: + class: Ibexa\Core\FieldType\Keyword\KeywordStorage + arguments: ['@Ibexa\Core\FieldType\Keyword\KeywordStorage\Gateway\DoctrineStorage'] + tags: + - {name: ibexa.field_type.storage.external.handler, alias: ezkeyword} + + Ibexa\Core\FieldType\Media\MediaStorage: + class: Ibexa\Core\FieldType\Media\MediaStorage + arguments: + $gateway: '@Ibexa\Core\FieldType\Media\MediaStorage\Gateway\DoctrineStorage' + $ioService: '@ibexa.field_type.ezbinaryfile.io_service' + $pathGenerator: '@Ibexa\Core\FieldType\BinaryBase\PathGenerator\LegacyPathGenerator' + $mimeTypeDetector: '@ibexa.core.io.mimeTypeDetector' + $fileExtensionBlackListValidator: '@Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator' + tags: + - {name: ibexa.field_type.storage.external.handler, alias: ezmedia} + + Ibexa\Core\FieldType\Url\UrlStorage: + class: Ibexa\Core\FieldType\Url\UrlStorage + arguments: + - '@Ibexa\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage' + - "@?logger" + tags: + - {name: ibexa.field_type.storage.external.handler, alias: ezurl} + + Ibexa\Core\FieldType\MapLocation\MapLocationStorage: + class: Ibexa\Core\FieldType\MapLocation\MapLocationStorage + arguments: ['@Ibexa\Core\FieldType\MapLocation\MapLocationStorage\Gateway\DoctrineStorage'] + tags: + - {name: ibexa.field_type.storage.external.handler, alias: ezgmaplocation} + + Ibexa\Core\FieldType\User\UserStorage: + class: Ibexa\Core\FieldType\User\UserStorage + arguments: ['@Ibexa\Core\FieldType\User\UserStorage\Gateway\DoctrineStorage'] + tags: + - {name: ibexa.field_type.storage.external.handler, alias: ezuser} + + Ibexa\Core\IO\MetadataHandler\ImageSize: + class: Ibexa\Core\IO\MetadataHandler\ImageSize diff --git a/src/lib/Resources/settings/fieldtype_services.yml b/src/lib/Resources/settings/fieldtype_services.yml new file mode 100644 index 0000000000..d0d6da7668 --- /dev/null +++ b/src/lib/Resources/settings/fieldtype_services.yml @@ -0,0 +1,73 @@ +services: + # Deprecated since eZ Publish 5.4. Use Ibexa\Core\FieldType\Image\IO\Legacy instead. + ibexa.field_type.ezimage.io: + alias: Ibexa\Core\FieldType\Image\IO\Legacy + + # Custom IOService with a proxy that handles the legacy prefix (images-versioned) + Ibexa\Core\FieldType\Image\IO\Legacy: + class: Ibexa\Core\FieldType\Image\IO\Legacy + arguments: + - '@ibexa.field_type.ezimage.io_service.published' + - '@ibexa.field_type.ezimage.io_service.draft' + - '@Ibexa\Core\FieldType\Image\IO\OptionsProvider' + + Ibexa\Core\FieldType\Image\IO\OptionsProvider: + class: Ibexa\Core\FieldType\Image\IO\OptionsProvider + arguments: + $configResolver: '@ibexa.config.resolver' + + ibexa.field_type.ezimage.io_service.published: + parent: ibexa.core.io.service + calls: + - [ setPrefix, [ '%ibexa.io.images.storage.prefix%' ] ] + + # Used to manipulate images with a legacy 'images-versioned' path + ibexa.field_type.ezimage.io_service.draft: + parent: ibexa.core.io.service + calls: + - [ setPrefix, [ '%ibexa.io.images.storage.prefix.draft%' ] ] + + Ibexa\Core\FieldType\Image\PathGenerator\LegacyPathGenerator: + class: Ibexa\Core\FieldType\Image\PathGenerator\LegacyPathGenerator + + Ibexa\Core\FieldType\Image\NullAliasCleaner: ~ + Ibexa\Core\FieldType\Image\AliasCleanerInterface: '@Ibexa\Core\FieldType\Image\NullAliasCleaner' + + # BinaryFile + ibexa.field_type.ezbinaryfile.io_service: + parent: ibexa.core.io.service + calls: + - [ setPrefix, [ '%ibexa.io.binary_file.storage.prefix%' ] ] + + Ibexa\Core\FieldType\BinaryBase\PathGenerator\LegacyPathGenerator: + class: Ibexa\Core\FieldType\BinaryBase\PathGenerator\LegacyPathGenerator + + Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator: + class: Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator + arguments: + - '@ibexa.config.resolver' + + Ibexa\Core\FieldType\Validator\ImageValidator: + class: Ibexa\Core\FieldType\Validator\ImageValidator + + # Symfony 3.4+ service definitions: + Ibexa\Core\FieldType\ImageAsset\AssetMapper: + arguments: + $contentService: '@ibexa.api.service.content' + $locationService: '@ibexa.api.service.location' + $contentTypeService: '@ibexa.api.service.content_type' + $configResolver: '@ibexa.config.resolver' + + Ibexa\Core\FieldType\FieldTypeRegistry: ~ + + Ibexa\Core\Repository\User\PasswordHashService: ~ + + Ibexa\Contracts\Core\Repository\PasswordHashService: + alias: Ibexa\Core\Repository\User\PasswordHashService + + Ibexa\Core\Repository\User\PasswordValidator: + arguments: + $passwordHashService: '@Ibexa\Contracts\Core\Repository\PasswordHashService' + + Ibexa\Core\Repository\User\PasswordValidatorInterface: + alias: Ibexa\Core\Repository\User\PasswordValidator diff --git a/src/lib/Resources/settings/fieldtypes.yml b/src/lib/Resources/settings/fieldtypes.yml new file mode 100644 index 0000000000..5d782e79ef --- /dev/null +++ b/src/lib/Resources/settings/fieldtypes.yml @@ -0,0 +1,569 @@ +parameters: + ibexa.field_type.country.data: + AF: {Name: "Afghanistan", Alpha2: "AF", Alpha3: "AFG", IDC: "93"} + AX: {Name: "Åland", Alpha2: "AX", Alpha3: "ALA", IDC: "358"} + AL: {Name: "Albania", Alpha2: "AL", Alpha3: "ALB", IDC: "355"} + DZ: {Name: "Algeria", Alpha2: "DZ", Alpha3: "DZA", IDC: "213"} + AS: {Name: "American Samoa", Alpha2: "AS", Alpha3: "ASM", IDC: "1684"} + AD: {Name: "Andorra", Alpha2: "AD", Alpha3: "AND", IDC: "376"} + AO: {Name: "Angola", Alpha2: "AO", Alpha3: "AGO", IDC: "244"} + AI: {Name: "Anguilla", Alpha2: "AI", Alpha3: "AIA", IDC: "1264"} + AQ: {Name: "Antarctica", Alpha2: "AQ", Alpha3: "ATA", IDC: "672"} + AG: {Name: "Antigua and Barbuda", Alpha2: "AG", Alpha3: "ATG", IDC: "1268"} + AR: {Name: "Argentina", Alpha2: "AR", Alpha3: "ARG", IDC: "54"} + AM: {Name: "Armenia", Alpha2: "AM", Alpha3: "ARM", IDC: "374"} + AW: {Name: "Aruba", Alpha2: "AW", Alpha3: "ABW", IDC: "297"} + AU: {Name: "Australia", Alpha2: "AU", Alpha3: "AUS", IDC: "61"} + AT: {Name: "Austria", Alpha2: "AT", Alpha3: "AUT", IDC: "43"} + AZ: {Name: "Azerbaijan", Alpha2: "AZ", Alpha3: "AZE", IDC: "994"} + BS: {Name: "Bahamas", Alpha2: "BS", Alpha3: "BHS", IDC: "1242"} + BH: {Name: "Bahrain", Alpha2: "BH", Alpha3: "BHR", IDC: "973"} + BD: {Name: "Bangladesh", Alpha2: "BD", Alpha3: "BGD", IDC: "880"} + BB: {Name: "Barbados", Alpha2: "BB", Alpha3: "BRB", IDC: "1246"} + BY: {Name: "Belarus", Alpha2: "BY", Alpha3: "BLR", IDC: "375"} + BE: {Name: "Belgium", Alpha2: "BE", Alpha3: "BEL", IDC: "32"} + BZ: {Name: "Belize", Alpha2: "BZ", Alpha3: "BLZ", IDC: "501"} + BJ: {Name: "Benin", Alpha2: "BJ", Alpha3: "BEN", IDC: "229"} + BM: {Name: "Bermuda", Alpha2: "BM", Alpha3: "BMU", IDC: "1441"} + BT: {Name: "Bhutan", Alpha2: "BT", Alpha3: "BTN", IDC: "975"} + BO: {Name: "Bolivia", Alpha2: "BO", Alpha3: "BOL", IDC: "591"} + BA: {Name: "Bosnia and Herzegovina", Alpha2: "BA", Alpha3: "BIH", IDC: "387"} + BW: {Name: "Botswana", Alpha2: "BW", Alpha3: "BWA", IDC: "267"} + BV: {Name: "Bouvet Island", Alpha2: "BV", Alpha3: "BVT", IDC: "47"} + BR: {Name: "Brazil", Alpha2: "BR", Alpha3: "BRA", IDC: "55"} + IO: {Name: "British Indian Ocean Territory", Alpha2: "IO", Alpha3: "IOT", IDC: "246"} + BN: {Name: "Brunei Darussalam", Alpha2: "BN", Alpha3: "BRN", IDC: "673"} + BG: {Name: "Bulgaria", Alpha2: "BG", Alpha3: "BGR", IDC: "359"} + BF: {Name: "Burkina Faso", Alpha2: "BF", Alpha3: "BFA", IDC: "226"} + BI: {Name: "Burundi", Alpha2: "BI", Alpha3: "BDI", IDC: "257"} + KH: {Name: "Cambodia", Alpha2: "KH", Alpha3: "KHM", IDC: "855"} + CM: {Name: "Cameroon", Alpha2: "CM", Alpha3: "CMR", IDC: "237"} + CA: {Name: "Canada", Alpha2: "CA", Alpha3: "CAN", IDC: "1"} + CV: {Name: "Cape Verde", Alpha2: "CV", Alpha3: "CPV", IDC: "238"} + KY: {Name: "Cayman Islands", Alpha2: "KY", Alpha3: "CYM", IDC: "1345"} + CF: {Name: "Central African Republic", Alpha2: "CF", Alpha3: "CAF", IDC: "236"} + TD: {Name: "Chad", Alpha2: "TD", Alpha3: "TCD", IDC: "235"} + CL: {Name: "Chile", Alpha2: "CL", Alpha3: "CHL", IDC: "56"} + CN: {Name: "China", Alpha2: "CN", Alpha3: "CHN", IDC: "86"} + CX: {Name: "Christmas Island", Alpha2: "CX", Alpha3: "CXR", IDC: "61"} + CC: {Name: "Cocos (Keeling) Islands", Alpha2: "CC", Alpha3: "CCK", IDC: "61"} + CO: {Name: "Colombia", Alpha2: "CO", Alpha3: "COL", IDC: "57"} + KM: {Name: "Comoros", Alpha2: "KM", Alpha3: "COM", IDC: "269"} + CG: {Name: "Congo", Alpha2: "CG", Alpha3: "COG", IDC: "242"} + CD: {Name: "Congo, The Democratic Republic Of The", Alpha2: "CD", Alpha3: "COD", IDC: "243"} + CK: {Name: "Cook Islands", Alpha2: "CK", Alpha3: "COK", IDC: "682"} + CR: {Name: "Costa Rica", Alpha2: "CR", Alpha3: "CRI", IDC: "506"} + CI: {Name: "Côte d'Ivoire", Alpha2: "CI", Alpha3: "CIV", IDC: "225"} + HR: {Name: "Croatia", Alpha2: "HR", Alpha3: "HRV", IDC: "385"} + CU: {Name: "Cuba", Alpha2: "CU", Alpha3: "CUB", IDC: "53"} + CW: {Name: "Curaçao", Alpha2: "CW", Alpha3: "CUW", IDC: "599"} + CY: {Name: "Cyprus", Alpha2: "CY", Alpha3: "CYP", IDC: "357"} + CZ: {Name: "Czech Republic", Alpha2: "CZ", Alpha3: "CZE", IDC: "420"} + DK: {Name: "Denmark", Alpha2: "DK", Alpha3: "DNK", IDC: "45"} + DJ: {Name: "Djibouti", Alpha2: "DJ", Alpha3: "DJI", IDC: "253"} + DM: {Name: "Dominica", Alpha2: "DM", Alpha3: "DMA", IDC: "1767"} + DO: {Name: "Dominican Republic", Alpha2: "DO", Alpha3: "DOM", IDC: "1809"} + EC: {Name: "Ecuador", Alpha2: "EC", Alpha3: "ECU", IDC: "593"} + EG: {Name: "Egypt", Alpha2: "EG", Alpha3: "EGY", IDC: "20"} + SV: {Name: "El Salvador", Alpha2: "SV", Alpha3: "SLV", IDC: "503"} + GQ: {Name: "Equatorial Guinea", Alpha2: "GQ", Alpha3: "GNQ", IDC: "240"} + ER: {Name: "Eritrea", Alpha2: "ER", Alpha3: "ERI", IDC: "291"} + EE: {Name: "Estonia", Alpha2: "EE", Alpha3: "EST", IDC: "372"} + ET: {Name: "Ethiopia", Alpha2: "ET", Alpha3: "ETH", IDC: "251"} + FK: {Name: "Falkland Islands (Malvinas)", Alpha2: "FK", Alpha3: "FLK", IDC: "500"} + FO: {Name: "Faroe Islands", Alpha2: "FO", Alpha3: "FRO", IDC: "298"} + FJ: {Name: "Fiji", Alpha2: "FJ", Alpha3: "FJI", IDC: "679"} + FI: {Name: "Finland", Alpha2: "FI", Alpha3: "FIN", IDC: "358"} + FR: {Name: "France", Alpha2: "FR", Alpha3: "FRA", IDC: "33"} + GF: {Name: "French Guiana", Alpha2: "GF", Alpha3: "GUF", IDC: "594"} + PF: {Name: "French Polynesia", Alpha2: "PF", Alpha3: "PYF", IDC: "689"} + TF: {Name: "French Southern Territories", Alpha2: "TF", Alpha3: "ATF", IDC: "0"} + GA: {Name: "Gabon", Alpha2: "GA", Alpha3: "GAB", IDC: "241"} + GM: {Name: "Gambia", Alpha2: "GM", Alpha3: "GMB", IDC: "220"} + GE: {Name: "Georgia", Alpha2: "GE", Alpha3: "GEO", IDC: "995"} + DE: {Name: "Germany", Alpha2: "DE", Alpha3: "DEU", IDC: "49"} + GH: {Name: "Ghana", Alpha2: "GH", Alpha3: "GHA", IDC: "233"} + GI: {Name: "Gibraltar", Alpha2: "GI", Alpha3: "GIB", IDC: "350"} + GR: {Name: "Greece", Alpha2: "GR", Alpha3: "GRC", IDC: "30"} + GL: {Name: "Greenland", Alpha2: "GL", Alpha3: "GRL", IDC: "299"} + GD: {Name: "Grenada", Alpha2: "GD", Alpha3: "GRD", IDC: "1473"} + GP: {Name: "Guadeloupe", Alpha2: "GP", Alpha3: "GLP", IDC: "590"} + GU: {Name: "Guam", Alpha2: "GU", Alpha3: "GUM", IDC: "1671"} + GT: {Name: "Guatemala", Alpha2: "GT", Alpha3: "GTM", IDC: "502"} + GG: {Name: "Guernsey", Alpha2: "GG", Alpha3: "GGY", IDC: "44"} + GN: {Name: "Guinea", Alpha2: "GN", Alpha3: "GIN", IDC: "224"} + GW: {Name: "Guinea-Bissau", Alpha2: "GW", Alpha3: "GNB", IDC: "245"} + GY: {Name: "Guyana", Alpha2: "GY", Alpha3: "GUY", IDC: "592"} + HT: {Name: "Haiti", Alpha2: "HT", Alpha3: "HTI", IDC: "509"} + HM: {Name: "Heard Island and McDonald Islands", Alpha2: "HM", Alpha3: "HMD", IDC: "672"} + HN: {Name: "Honduras", Alpha2: "HN", Alpha3: "HND", IDC: "504"} + HK: {Name: "Hong Kong", Alpha2: "HK", Alpha3: "HKG", IDC: "852"} + HU: {Name: "Hungary", Alpha2: "HU", Alpha3: "HUN", IDC: "36"} + IS: {Name: "Iceland", Alpha2: "IS", Alpha3: "ISL", IDC: "354"} + IN: {Name: "India", Alpha2: "IN", Alpha3: "IND", IDC: "91"} + ID: {Name: "Indonesia", Alpha2: "ID", Alpha3: "IDN", IDC: "62"} + IR: {Name: "Iran, Islamic Republic of", Alpha2: "IR", Alpha3: "IRN", IDC: "98"} + IQ: {Name: "Iraq", Alpha2: "IQ", Alpha3: "IRQ", IDC: "964"} + IE: {Name: "Ireland", Alpha2: "IE", Alpha3: "IRL", IDC: "353"} + IM: {Name: "Isle of Man", Alpha2: "IM", Alpha3: "IMN", IDC: "44"} + IL: {Name: "Israel", Alpha2: "IL", Alpha3: "ISR", IDC: "972"} + IT: {Name: "Italy", Alpha2: "IT", Alpha3: "ITA", IDC: "39"} + JM: {Name: "Jamaica", Alpha2: "JM", Alpha3: "JAM", IDC: "1876"} + JP: {Name: "Japan", Alpha2: "JP", Alpha3: "JPN", IDC: "81"} + JE: {Name: "Jersey", Alpha2: "JE", Alpha3: "JEY", IDC: "44"} + JO: {Name: "Jordan", Alpha2: "JO", Alpha3: "JOR", IDC: "962"} + KZ: {Name: "Kazakhstan", Alpha2: "KZ", Alpha3: "KAZ", IDC: "7"} + KE: {Name: "Kenya", Alpha2: "KE", Alpha3: "KEN", IDC: "254"} + KI: {Name: "Kiribati", Alpha2: "KI", Alpha3: "KIR", IDC: "686"} + KP: {Name: "Korea, Democratic People's Republic of", Alpha2: "KP", Alpha3: "PRK", IDC: "850"} + KR: {Name: "Korea, Republic of", Alpha2: "KR", Alpha3: "KOR", IDC: "82"} + XK: {Name: "Kosovo", Alpha2: "XK", Alpha3: "XXK", IDC: "383"} + KW: {Name: "Kuwait", Alpha2: "KW", Alpha3: "KWT", IDC: "965"} + KG: {Name: "Kyrgyzstan", Alpha2: "KG", Alpha3: "KGZ", IDC: "996"} + LA: {Name: "Lao People's Democratic Republic", Alpha2: "LA", Alpha3: "LAO", IDC: "856"} + LV: {Name: "Latvia", Alpha2: "LV", Alpha3: "LVA", IDC: "371"} + LB: {Name: "Lebanon", Alpha2: "LB", Alpha3: "LBN", IDC: "961"} + LS: {Name: "Lesotho", Alpha2: "LS", Alpha3: "LSO", IDC: "266"} + LR: {Name: "Liberia", Alpha2: "LR", Alpha3: "LBR", IDC: "231"} + LY: {Name: "Libyan Arab Jamahiriya", Alpha2: "LY", Alpha3: "LBY", IDC: "218"} + LI: {Name: "Liechtenstein", Alpha2: "LI", Alpha3: "LIE", IDC: "423"} + LT: {Name: "Lithuania", Alpha2: "LT", Alpha3: "LTU", IDC: "370"} + LU: {Name: "Luxembourg", Alpha2: "LU", Alpha3: "LUX", IDC: "352"} + MO: {Name: "Macau", Alpha2: "MO", Alpha3: "MAC", IDC: "853"} + MG: {Name: "Madagascar", Alpha2: "MG", Alpha3: "MDG", IDC: "261"} + MW: {Name: "Malawi", Alpha2: "MW", Alpha3: "MWI", IDC: "265"} + MY: {Name: "Malaysia", Alpha2: "MY", Alpha3: "MYS", IDC: "60"} + MV: {Name: "Maldives", Alpha2: "MV", Alpha3: "MDV", IDC: "960"} + ML: {Name: "Mali", Alpha2: "ML", Alpha3: "MLI", IDC: "223"} + MT: {Name: "Malta", Alpha2: "MT", Alpha3: "MLT", IDC: "356"} + MH: {Name: "Marshall Islands", Alpha2: "MH", Alpha3: "MHL", IDC: "692"} + MQ: {Name: "Martinique", Alpha2: "MQ", Alpha3: "MTQ", IDC: "596"} + MR: {Name: "Mauritania", Alpha2: "MR", Alpha3: "MRT", IDC: "222"} + MU: {Name: "Mauritius", Alpha2: "MU", Alpha3: "MUS", IDC: "230"} + YT: {Name: "Mayotte", Alpha2: "YT", Alpha3: "MYT", IDC: "262"} + MX: {Name: "Mexico", Alpha2: "MX", Alpha3: "MEX", IDC: "52"} + FM: {Name: "Micronesia, Federated States of", Alpha2: "FM", Alpha3: "FSM", IDC: "691"} + MD: {Name: "Moldova, Republic of", Alpha2: "MD", Alpha3: "MDA", IDC: "373"} + MC: {Name: "Monaco", Alpha2: "MC", Alpha3: "MCO", IDC: "377"} + MN: {Name: "Mongolia", Alpha2: "MN", Alpha3: "MNG", IDC: "976"} + ME: {Name: "Montenegro", Alpha2: "ME", Alpha3: "MNE", IDC: "382"} + MS: {Name: "Montserrat", Alpha2: "MS", Alpha3: "MSR", IDC: "1664"} + MA: {Name: "Morocco", Alpha2: "MA", Alpha3: "MAR", IDC: "212"} + MZ: {Name: "Mozambique", Alpha2: "MZ", Alpha3: "MOZ", IDC: "258"} + MM: {Name: "Myanmar", Alpha2: "MM", Alpha3: "MMR", IDC: "95"} + NA: {Name: "Namibia", Alpha2: "NA", Alpha3: "NAM", IDC: "264"} + NR: {Name: "Nauru", Alpha2: "NR", Alpha3: "NRU", IDC: "674"} + NP: {Name: "Nepal", Alpha2: "NP", Alpha3: "NPL", IDC: "977"} + NL: {Name: "Netherlands", Alpha2: "NL", Alpha3: "NLD", IDC: "31"} + AN: {Name: "Netherlands Antilles", Alpha2: "AN", Alpha3: "ANT", IDC: "599"} + NC: {Name: "New Caledonia", Alpha2: "NC", Alpha3: "NCL", IDC: "687"} + NZ: {Name: "New Zealand", Alpha2: "NZ", Alpha3: "NZL", IDC: "64"} + NI: {Name: "Nicaragua", Alpha2: "NI", Alpha3: "NIC", IDC: "505"} + NE: {Name: "Niger", Alpha2: "NE", Alpha3: "NER", IDC: "227"} + NG: {Name: "Nigeria", Alpha2: "NG", Alpha3: "NGA", IDC: "234"} + NU: {Name: "Niue", Alpha2: "NU", Alpha3: "NIU", IDC: "683"} + NF: {Name: "Norfolk Island", Alpha2: "NF", Alpha3: "NFK", IDC: "6723"} + MP: {Name: "Northern Mariana Islands", Alpha2: "MP", Alpha3: "MNP", IDC: "1670"} + MK: {Name: "North Macedonia, Republic of", Alpha2: "MK", Alpha3: "MKD", IDC: "389"} + NO: {Name: "Norway", Alpha2: "NO", Alpha3: "NOR", IDC: "47"} + OM: {Name: "Oman", Alpha2: "OM", Alpha3: "OMN", IDC: "968"} + PK: {Name: "Pakistan", Alpha2: "PK", Alpha3: "PAK", IDC: "92"} + PW: {Name: "Palau", Alpha2: "PW", Alpha3: "PLW", IDC: "680"} + PS: {Name: "Palestinian Territory, Occupied", Alpha2: "PS", Alpha3: "PSE", IDC: "970"} + PA: {Name: "Panama", Alpha2: "PA", Alpha3: "PAN", IDC: "507"} + PG: {Name: "Papua New Guinea", Alpha2: "PG", Alpha3: "PNG", IDC: "675"} + PY: {Name: "Paraguay", Alpha2: "PY", Alpha3: "PRY", IDC: "595"} + PE: {Name: "Peru", Alpha2: "PE", Alpha3: "PER", IDC: "51"} + PH: {Name: "Philippines", Alpha2: "PH", Alpha3: "PHL", IDC: "63"} + PN: {Name: "Pitcairn", Alpha2: "PN", Alpha3: "PCN", IDC: "64"} + PL: {Name: "Poland", Alpha2: "PL", Alpha3: "POL", IDC: "48"} + PT: {Name: "Portugal", Alpha2: "PT", Alpha3: "PRT", IDC: "351"} + PR: {Name: "Puerto Rico", Alpha2: "PR", Alpha3: "PRI", IDC: "1787"} + QA: {Name: "Qatar", Alpha2: "QA", Alpha3: "QAT", IDC: "974"} + RE: {Name: "Reunion", Alpha2: "RE", Alpha3: "REU", IDC: "262"} + RO: {Name: "Romania", Alpha2: "RO", Alpha3: "ROU", IDC: "40"} + RU: {Name: "Russian Federation", Alpha2: "RU", Alpha3: "RUS", IDC: "7"} + RW: {Name: "Rwanda", Alpha2: "RW", Alpha3: "RWA", IDC: "250"} + BL: {Name: "Saint Barthélemy", Alpha2: "BL", Alpha3: "BLM", IDC: "590"} + SH: {Name: "Saint Helena", Alpha2: "SH", Alpha3: "SHN", IDC: "290"} + KN: {Name: "Saint Kitts and Nevis", Alpha2: "KN", Alpha3: "KNA", IDC: "1869"} + LC: {Name: "Saint Lucia", Alpha2: "LC", Alpha3: "LCA", IDC: "1758"} + MF: {Name: "Saint Martin", Alpha2: "MF", Alpha3: "MAF", IDC: "590"} + PM: {Name: "Saint Pierre and Miquelon", Alpha2: "PM", Alpha3: "SPM", IDC: "508"} + VC: {Name: "Saint Vincent and The Grenadines", Alpha2: "VC", Alpha3: "VCT", IDC: "1784"} + WS: {Name: "Samoa", Alpha2: "WS", Alpha3: "WSM", IDC: "685"} + SM: {Name: "San Marino", Alpha2: "SM", Alpha3: "SMR", IDC: "378"} + ST: {Name: "Sao Tome and Principe", Alpha2: "ST", Alpha3: "STP", IDC: "239"} + SA: {Name: "Saudi Arabia", Alpha2: "SA", Alpha3: "SAU", IDC: "966"} + SN: {Name: "Senegal", Alpha2: "SN", Alpha3: "SEN", IDC: "221"} + RS: {Name: "Serbia", Alpha2: "RS", Alpha3: "SRB", IDC: "381"} + SC: {Name: "Seychelles", Alpha2: "SC", Alpha3: "SYC", IDC: "248"} + SL: {Name: "Sierra Leone", Alpha2: "SL", Alpha3: "SLE", IDC: "232"} + SG: {Name: "Singapore", Alpha2: "SG", Alpha3: "SGP", IDC: "65"} + SK: {Name: "Slovakia", Alpha2: "SK", Alpha3: "SVK", IDC: "421"} + SI: {Name: "Slovenia", Alpha2: "SI", Alpha3: "SVN", IDC: "386"} + SB: {Name: "Solomon Islands", Alpha2: "SB", Alpha3: "SLB", IDC: "677"} + SO: {Name: "Somalia", Alpha2: "SO", Alpha3: "SOM", IDC: "252"} + ZA: {Name: "South Africa", Alpha2: "ZA", Alpha3: "ZAF", IDC: "27"} + GS: {Name: "South Georgia and The South Sandwich Islands", Alpha2: "GS", Alpha3: "SGS", IDC: "500"} + SS: {Name: "South Sudan, Republic of", Alpha2: "SS", Alpha3: "SSD", IDC: "211"} + ES: {Name: "Spain", Alpha2: "ES", Alpha3: "ESP", IDC: "34"} + LK: {Name: "Sri Lanka", Alpha2: "LK", Alpha3: "LKA", IDC: "94"} + SD: {Name: "Sudan", Alpha2: "SD", Alpha3: "SDN", IDC: "249"} + SR: {Name: "Suriname", Alpha2: "SR", Alpha3: "SUR", IDC: "597"} + SJ: {Name: "Svalbard and Jan Mayen", Alpha2: "SJ", Alpha3: "SJM", IDC: "47"} + SZ: {Name: "Swaziland", Alpha2: "SZ", Alpha3: "SWZ", IDC: "268"} + SE: {Name: "Sweden", Alpha2: "SE", Alpha3: "SWE", IDC: "46"} + CH: {Name: "Switzerland", Alpha2: "CH", Alpha3: "CHE", IDC: "41"} + SY: {Name: "Syrian Arab Republic", Alpha2: "SY", Alpha3: "SYR", IDC: "963"} + TW: {Name: "Taiwan", Alpha2: "TW", Alpha3: "TWN", IDC: "886"} + TJ: {Name: "Tajikistan", Alpha2: "TJ", Alpha3: "TJK", IDC: "992"} + TZ: {Name: "Tanzania, United Republic of", Alpha2: "TZ", Alpha3: "TZA", IDC: "255"} + TH: {Name: "Thailand", Alpha2: "TH", Alpha3: "THA", IDC: "66"} + TL: {Name: "Timor-Leste", Alpha2: "TL", Alpha3: "TLS", IDC: "670"} + TG: {Name: "Togo", Alpha2: "TG", Alpha3: "TGO", IDC: "228"} + TK: {Name: "Tokelau", Alpha2: "TK", Alpha3: "TKL", IDC: "690"} + TO: {Name: "Tonga", Alpha2: "TO", Alpha3: "TON", IDC: "676"} + TT: {Name: "Trinidad and Tobago", Alpha2: "TT", Alpha3: "TTO", IDC: "1868"} + TN: {Name: "Tunisia", Alpha2: "TN", Alpha3: "TUN", IDC: "216"} + TR: {Name: "Turkey", Alpha2: "TR", Alpha3: "TUR", IDC: "90"} + TM: {Name: "Turkmenistan", Alpha2: "TM", Alpha3: "TKM", IDC: "993"} + TC: {Name: "Turks and Caicos Islands", Alpha2: "TC", Alpha3: "TCA", IDC: "1649"} + TV: {Name: "Tuvalu", Alpha2: "TV", Alpha3: "TUV", IDC: "688"} + UG: {Name: "Uganda", Alpha2: "UG", Alpha3: "UGA", IDC: "256"} + UA: {Name: "Ukraine", Alpha2: "UA", Alpha3: "UKR", IDC: "380"} + AE: {Name: "United Arab Emirates", Alpha2: "AE", Alpha3: "ARE", IDC: "971"} + GB: {Name: "United Kingdom", Alpha2: "GB", Alpha3: "GBR", IDC: "44"} + UM: {Name: "United States Minor Outlying Islands", Alpha2: "UM", Alpha3: "UMI", IDC: "1"} + US: {Name: "United States of America", Alpha2: "US", Alpha3: "USA", IDC: "1"} + UY: {Name: "Uruguay", Alpha2: "UY", Alpha3: "URY", IDC: "598"} + UZ: {Name: "Uzbekistan", Alpha2: "UZ", Alpha3: "UZB", IDC: "998"} + VU: {Name: "Vanuatu", Alpha2: "VU", Alpha3: "VUT", IDC: "678"} + VA: {Name: "Holy See (Vatican City State)", Alpha2: "VA", Alpha3: "VAT", IDC: "3906"} + VE: {Name: "Venezuela", Alpha2: "VE", Alpha3: "VEN", IDC: "58"} + VN: {Name: "Viet Nam", Alpha2: "VN", Alpha3: "VNM", IDC: "84"} + VG: {Name: "Virgin Islands, British", Alpha2: "VG", Alpha3: "VGB", IDC: "1284"} + VI: {Name: "Virgin Islands, U.S.", Alpha2: "VI", Alpha3: "VIR", IDC: "1340"} + WF: {Name: "Wallis and Futuna", Alpha2: "WF", Alpha3: "WLF", IDC: "681"} + EH: {Name: "Western Sahara", Alpha2: "EH", Alpha3: "ESH", IDC: "212"} + YE: {Name: "Yemen", Alpha2: "YE", Alpha3: "YEM", IDC: "967"} + ZM: {Name: "Zambia", Alpha2: "ZM", Alpha3: "ZMB", IDC: "260"} + ZW: {Name: "Zimbabwe", Alpha2: "ZW", Alpha3: "ZWE", IDC: "263"} + + ibexa.field_type.ezimage.mime_types: + - image/jpeg + - image/pjpeg + - image/png + - image/bmp + - image/gif + - image/tiff + - image/x-icon + - image/webp + +services: + Ibexa\Core\FieldType\FieldType: + class: Ibexa\Core\FieldType\FieldType + calls: + - [setTransformationProcessor, ['@Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased']] + abstract: true + + Ibexa\Core\FieldType\Author\Type: + class: Ibexa\Core\FieldType\Author\Type + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezauthor} + + Ibexa\Core\FieldType\BinaryFile\Type: + class: Ibexa\Core\FieldType\BinaryFile\Type + arguments: + - ['@Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator'] + - '@?Ibexa\Core\MVC\Symfony\FieldType\BinaryBase\ContentDownloadUrlGenerator' + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezbinaryfile} + + Ibexa\Core\FieldType\Checkbox\Type: + class: Ibexa\Core\FieldType\Checkbox\Type + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezboolean} + + Ibexa\Core\FieldType\Country\Type: + class: Ibexa\Core\FieldType\Country\Type + arguments: ['%ibexa.field_type.country.data%'] + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezcountry} + + Ibexa\Core\FieldType\Date\Type: + class: Ibexa\Core\FieldType\Date\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezdate" ] + tags: + - {name: ibexa.field_type, alias: ezdate} + + Ibexa\Core\FieldType\DateAndTime\Type: + class: Ibexa\Core\FieldType\DateAndTime\Type + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezdatetime} + + Ibexa\Core\FieldType\Time\Type: + class: Ibexa\Core\FieldType\Time\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "eztime" ] + tags: + - {name: ibexa.field_type, alias: eztime} + + Ibexa\Core\FieldType\EmailAddress\Type: + class: Ibexa\Core\FieldType\EmailAddress\Type + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezemail} + + Ibexa\Core\FieldType\Float\Type: + class: Ibexa\Core\FieldType\Float\Type + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezfloat} + + Ibexa\Core\FieldType\Integer\Type: + class: Ibexa\Core\FieldType\Integer\Type + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezinteger} + + Ibexa\Core\FieldType\Image\Type: + class: Ibexa\Core\FieldType\Image\Type + arguments: + $validators: + - '@Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator' + - '@Ibexa\Core\FieldType\Validator\ImageValidator' + $mimeTypes: '%ibexa.field_type.ezimage.mime_types%' + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezimage} + + Ibexa\Core\FieldType\ISBN\Type: + class: Ibexa\Core\FieldType\ISBN\Type + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezisbn} + + Ibexa\Core\FieldType\Keyword\Type: + class: Ibexa\Core\FieldType\Keyword\Type + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezkeyword} + + Ibexa\Core\FieldType\Media\Type: + class: Ibexa\Core\FieldType\Media\Type + arguments: + - ['@Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator'] + - '@?Ibexa\Core\MVC\Symfony\FieldType\BinaryBase\ContentDownloadUrlGenerator' + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezmedia} + + Ibexa\Core\FieldType\Relation\Type: + class: Ibexa\Core\FieldType\Relation\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: + $handler: '@Ibexa\Core\Persistence\Cache\ContentHandler' + $targetContentValidator: '@Ibexa\Core\Repository\Validator\TargetContentValidatorInterface' + tags: + - {name: ibexa.field_type, alias: ezobjectrelation} + + Ibexa\Core\FieldType\Selection\Type: + class: Ibexa\Core\FieldType\Selection\Type + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezselection} + + Ibexa\Core\FieldType\TextBlock\Type: + class: Ibexa\Core\FieldType\TextBlock\Type + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: eztext} + + Ibexa\Core\FieldType\TextLine\Type: + class: Ibexa\Core\FieldType\TextLine\Type + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezstring} + + Ibexa\Core\FieldType\Url\Type: + class: Ibexa\Core\FieldType\Url\Type + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezurl} + + Ibexa\Core\FieldType\MapLocation\Type: + class: Ibexa\Core\FieldType\MapLocation\Type + parent: Ibexa\Core\FieldType\FieldType + tags: + - {name: ibexa.field_type, alias: ezgmaplocation} + + Ibexa\Core\FieldType\RelationList\Type: + class: Ibexa\Core\FieldType\RelationList\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: + $handler: '@Ibexa\Core\Persistence\Cache\ContentHandler' + $targetContentValidator: '@Ibexa\Core\Repository\Validator\TargetContentValidatorInterface' + tags: + - {name: ibexa.field_type, alias: ezobjectrelationlist} + + Ibexa\Core\FieldType\User\Type: + class: Ibexa\Core\FieldType\User\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: + - '@Ibexa\Core\Persistence\Cache\UserHandler' + - '@Ibexa\Contracts\Core\Repository\PasswordHashService' + - '@Ibexa\Core\Repository\User\PasswordValidatorInterface' + tags: + - {name: ibexa.field_type, alias: ezuser} + + # Not implemented fieldtypes + # Configured to use the Null type to not throw exception + + ibexa.field_type.ezenum: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezenum" ] + tags: + - {name: ibexa.field_type, alias: ezenum} + + ibexa.field_type.ezidentifier: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezidentifier" ] + tags: + - {name: ibexa.field_type, alias: ezidentifier} + + ibexa.field_type.ezinisetting: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezinisetting" ] + tags: + - {name: ibexa.field_type, alias: ezinisetting} + + ibexa.field_type.ezmatrix: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezmatrix" ] + tags: + - {name: ibexa.field_type, alias: ezmatrix} + + ibexa.field_type.ezmultioption: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezmultioption" ] + tags: + - {name: ibexa.field_type, alias: ezmultioption} + + ibexa.field_type.ezmultioption2: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezmultioption2" ] + tags: + - {name: ibexa.field_type, alias: ezmultioption2} + + ibexa.field_type.ezmultiprice: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezmultiprice" ] + tags: + - {name: ibexa.field_type, alias: ezmultiprice} + + ibexa.field_type.ezoption: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezoption" ] + tags: + - {name: ibexa.field_type, alias: ezoption} + + ibexa.field_type.ezpackage: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezpackage" ] + tags: + - {name: ibexa.field_type, alias: ezpackage} + + ibexa.field_type.ezproductcategory: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezproductcategory" ] + tags: + - {name: ibexa.field_type, alias: ezproductcategory} + + ibexa.field_type.ezrangeoption: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezrangeoption" ] + tags: + - {name: ibexa.field_type, alias: ezrangeoption} + + ibexa.field_type.ezsubtreesubscription: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezsubtreesubscription" ] + tags: + - {name: ibexa.field_type, alias: ezsubtreesubscription} + + # not implemented fieldtypes from extensions + ibexa.field_type.ezcomcomments: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezcomcomments" ] + tags: + - {name: ibexa.field_type, alias: ezcomcomments} + + ibexa.field_type.ezpaex: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezpaex" ] + tags: + - {name: ibexa.field_type, alias: ezpaex} + + ibexa.field_type.ezsurvey: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezsurvey" ] + tags: + - {name: ibexa.field_type, alias: ezsurvey} + + ibexa.field_type.eztags: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "eztags" ] + tags: + - {name: ibexa.field_type, alias: eztags} + + ibexa.field_type.ezrecommendation: + class: Ibexa\Core\FieldType\Null\Type + parent: Ibexa\Core\FieldType\FieldType + arguments: [ "ezrecommendation" ] + tags: + - {name: ibexa.field_type, alias: ezrecommendation} + + Ibexa\Core\FieldType\ImageAsset\Type: + parent: Ibexa\Core\FieldType\FieldType + arguments: + - '@ibexa.api.service.content' + - '@Ibexa\Core\FieldType\ImageAsset\AssetMapper' + - '@Ibexa\Core\Persistence\Cache\ContentHandler' + tags: + - {name: ibexa.field_type, alias: ezimageasset} + + ibexa.field_type.ezimageasset: + alias: Ibexa\Core\FieldType\ImageAsset\Type + + Ibexa\Core\FieldType\ValueSerializer\SymfonySerializerAdapter: + arguments: + $normalizer: '@serializer' + $denormalizer: '@serializer' + $encoder: '@serializer' + $decoder: '@serializer' + + Ibexa\Contracts\Core\FieldType\ValueSerializerInterface: + alias: 'Ibexa\Core\FieldType\ValueSerializer\SymfonySerializerAdapter' diff --git a/src/lib/Resources/settings/indexable_fieldtypes.yml b/src/lib/Resources/settings/indexable_fieldtypes.yml new file mode 100644 index 0000000000..70013417d9 --- /dev/null +++ b/src/lib/Resources/settings/indexable_fieldtypes.yml @@ -0,0 +1,136 @@ +parameters: + +services: + Ibexa\Core\FieldType\Keyword\SearchField: + class: Ibexa\Core\FieldType\Keyword\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezkeyword} + + Ibexa\Core\FieldType\Author\SearchField: + class: Ibexa\Core\FieldType\Author\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezauthor} + + Ibexa\Core\FieldType\TextLine\SearchField: + class: Ibexa\Core\FieldType\TextLine\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezstring} + + Ibexa\Core\FieldType\MapLocation\SearchField: + class: Ibexa\Core\FieldType\MapLocation\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezgmaplocation} + + Ibexa\Core\FieldType\Country\SearchField: + class: Ibexa\Core\FieldType\Country\SearchField + arguments: + - '%ibexa.field_type.country.data%' + tags: + - {name: ibexa.field_type.indexable, alias: ezcountry} + + Ibexa\Core\FieldType\Date\SearchField: + class: Ibexa\Core\FieldType\Date\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezdate} + + Ibexa\Core\FieldType\Integer\SearchField: + class: Ibexa\Core\FieldType\Integer\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezinteger} + + Ibexa\Core\FieldType\Float\SearchField: + class: Ibexa\Core\FieldType\Float\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezfloat} + + Ibexa\Core\FieldType\EmailAddress\SearchField: + class: Ibexa\Core\FieldType\EmailAddress\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezemail} + + Ibexa\Core\FieldType\Image\SearchField: + class: Ibexa\Core\FieldType\Image\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezimage} + + Ibexa\Core\FieldType\Media\SearchField: + class: Ibexa\Core\FieldType\Media\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezmedia} + + Ibexa\Core\FieldType\BinaryFile\SearchField: + class: Ibexa\Core\FieldType\BinaryFile\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezbinaryfile} + + Ibexa\Core\FieldType\Time\SearchField: + class: Ibexa\Core\FieldType\Time\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: eztime} + + Ibexa\Core\FieldType\TextBlock\SearchField: + class: Ibexa\Core\FieldType\TextBlock\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: eztext} + + Ibexa\Core\FieldType\Checkbox\SearchField: + class: Ibexa\Core\FieldType\Checkbox\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezboolean} + + Ibexa\Core\FieldType\DateAndTime\SearchField: + class: Ibexa\Core\FieldType\DateAndTime\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezdatetime} + + Ibexa\Core\FieldType\ISBN\SearchField: + class: Ibexa\Core\FieldType\ISBN\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezisbn} + + Ibexa\Core\FieldType\Relation\SearchField: + class: Ibexa\Core\FieldType\Relation\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezobjectrelation} + + Ibexa\Core\FieldType\Selection\SearchField: + class: Ibexa\Core\FieldType\Selection\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezselection} + + Ibexa\Core\FieldType\RelationList\SearchField: + class: Ibexa\Core\FieldType\RelationList\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezobjectrelationlist} + + Ibexa\Core\FieldType\Url\SearchField: + class: Ibexa\Core\FieldType\Url\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezurl} + + Ibexa\Core\FieldType\ImageAsset\SearchField: + class: Ibexa\Core\FieldType\ImageAsset\SearchField + tags: + - {name: ibexa.field_type.indexable, alias: ezimageasset} + + + ibexa.field_type.indexable.unindexed: + class: Ibexa\Core\FieldType\Unindexed + tags: + - {name: ibexa.field_type.indexable, alias: ezuser} + - {name: ibexa.field_type.indexable, alias: ezenum} + - {name: ibexa.field_type.indexable, alias: ezidentifier} + - {name: ibexa.field_type.indexable, alias: ezinisetting} + - {name: ibexa.field_type.indexable, alias: ezmatrix} + - {name: ibexa.field_type.indexable, alias: ezmultioption} + - {name: ibexa.field_type.indexable, alias: ezmultioption2} + - {name: ibexa.field_type.indexable, alias: ezmultiprice} + - {name: ibexa.field_type.indexable, alias: ezoption} + - {name: ibexa.field_type.indexable, alias: ezpackage} + - {name: ibexa.field_type.indexable, alias: ezproductcategory} + - {name: ibexa.field_type.indexable, alias: ezrangeoption} + - {name: ibexa.field_type.indexable, alias: ezsubtreesubscription} + - {name: ibexa.field_type.indexable, alias: ezcomcomments} + - {name: ibexa.field_type.indexable, alias: ezsurvey} + - {name: ibexa.field_type.indexable, alias: eztags} + - {name: ibexa.field_type.indexable, alias: ezrecommendation} diff --git a/src/lib/Resources/settings/io.yml b/src/lib/Resources/settings/io.yml new file mode 100644 index 0000000000..ad98eb6b8c --- /dev/null +++ b/src/lib/Resources/settings/io.yml @@ -0,0 +1,158 @@ +parameters: + # Needs to point to filesystem actual root / for decorators to work + ibexa.core.io.flysystem.filesystem_root: '/' + ibexa.core.io.flysystem.write_flags: !php/const LOCK_EX + ibexa.core.io.flysystem.link_handling: !php/const \League\Flysystem\Local\LocalFilesystemAdapter::DISALLOW_LINKS + +services: + ibexa.core.io.service: + class: Ibexa\Core\IO\TolerantIOService + lazy: true + arguments: + - '@Ibexa\Core\IO\IOBinarydataHandler\SiteAccessDependentMetadataHandler' + - '@Ibexa\Core\IO\IOBinarydataHandler\SiteAccessDependentBinaryDataHandler' + - '@ibexa.core.io.mimeTypeDetector' + calls: + - [ setLogger, ["@?logger" ] ] + + Ibexa\Core\IO\MimeTypeDetector\FileInfo: + class: Ibexa\Core\IO\MimeTypeDetector\FileInfo + + ibexa.core.io.mimeTypeDetector: + alias: Ibexa\Core\IO\MimeTypeDetector\FileInfo + + # metadata handlers + Ibexa\Core\IO\IOBinarydataHandler\SiteAccessDependentMetadataHandler: + alias: ibexa.core.io.metadata_handler.flysystem + + ibexa.core.io.metadata_handler.flysystem: + class: Ibexa\Core\IO\IOMetadataHandler\Flysystem + arguments: + - '@ibexa.core.io.flysystem.default_filesystem' + tags: + - { name: monolog.logger, channel: ibexa.core.io } + + # binarydata handlers + Ibexa\Core\IO\IOBinarydataHandler\SiteAccessDependentBinaryDataHandler: + alias: ibexa.core.io.binarydata_handler.flysystem + + ibexa.core.io.binarydata_handler.flysystem: + class: Ibexa\Core\IO\IOBinarydataHandler\Flysystem + arguments: + - '@ibexa.core.io.flysystem.default_filesystem' + - '@ibexa.core.io.default_url_decorator' + + ibexa.core.io.flysystem.base_filesystem: + class: League\Flysystem\Filesystem + abstract: true + + ibexa.core.io.flysystem.adapter.site_access_aware: + class: League\Flysystem\Local\LocalFilesystemAdapter + arguments: + $location: '%ibexa.core.io.flysystem.filesystem_root%' + $visibility: '@Ibexa\Core\IO\Flysystem\VisibilityConverter\SiteAccessAwareVisibilityConverter' + $writeFlags: '%ibexa.core.io.flysystem.write_flags%' + $linkHandling: '%ibexa.core.io.flysystem.link_handling%' + + ibexa.core.io.flysystem.adapter.dfs: + class: League\Flysystem\Local\LocalFilesystemAdapter + arguments: + $location: '%ibexa.core.io.flysystem.filesystem_root%' + $visibility: '@Ibexa\Core\IO\Flysystem\VisibilityConverter\DFSVisibilityConverter' + $writeFlags: "@=parameter('ibexa.io.nfs.adapter.config')['writeFlags'] ? parameter('ibexa.io.nfs.adapter.config')['writeFlags'] : parameter('ibexa.core.io.flysystem.write_flags')" + $linkHandling: "@=parameter('ibexa.io.nfs.adapter.config')['linkHandling'] ? parameter('ibexa.io.nfs.adapter.config')['linkHandling'] : parameter('ibexa.core.io.flysystem.link_handling')" + + ibexa.core.io.flysystem.adapter.dynamic_path.site_access_aware.decorator: + class: Ibexa\Core\IO\Flysystem\Adapter\DynamicPathFilesystemAdapterDecorator + decorates: ibexa.core.io.flysystem.adapter.site_access_aware + arguments: + $innerAdapter: '@.inner' + $prefixer: '@Ibexa\Core\IO\Flysystem\PathPrefixer\LocalSiteAccessAwarePathPrefixer' + + ibexa.core.io.flysystem.adapter.dynamic_path.dfs.decorator: + class: Ibexa\Core\IO\Flysystem\Adapter\DynamicPathFilesystemAdapterDecorator + decorates: ibexa.core.io.flysystem.adapter.dfs + arguments: + $innerAdapter: '@.inner' + $prefixer: '@Ibexa\Core\IO\Flysystem\PathPrefixer\DFSSiteAccessAwarePathPrefixer' + + ibexa.core.io.flysystem.default_filesystem: + parent: ibexa.core.io.flysystem.base_filesystem + arguments: + $adapter: '@ibexa.core.io.flysystem.adapter.dynamic_path.site_access_aware.decorator' + + ibexa.io.nfs.adapter.site_access_aware: + alias: ibexa.core.io.flysystem.adapter.dynamic_path.dfs.decorator + + ibexa.core.io.flysystem.visibility.portable_visibility.converter: + class: League\Flysystem\UnixVisibility\PortableVisibilityConverter + + Ibexa\Core\IO\Flysystem\VisibilityConverter\BaseVisibilityConverter: + abstract: true + arguments: + $nativeVisibilityConverter: '@ibexa.core.io.flysystem.visibility.portable_visibility.converter' + + Ibexa\Core\IO\Flysystem\VisibilityConverter\SiteAccessAwareVisibilityConverter: + parent: Ibexa\Core\IO\Flysystem\VisibilityConverter\BaseVisibilityConverter + arguments: + $configResolver: '@Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface' + + Ibexa\Core\IO\Flysystem\VisibilityConverter\DFSVisibilityConverter: + parent: Ibexa\Core\IO\Flysystem\VisibilityConverter\BaseVisibilityConverter + arguments: + $permissions: "@=parameter('ibexa.io.nfs.adapter.config')['permissions']" + + Ibexa\Core\IO\Flysystem\PathPrefixer\BaseSiteAccessAwarePathPrefixer: + abstract: true + + Ibexa\Core\IO\Flysystem\PathPrefixer\LocalSiteAccessAwarePathPrefixer: + arguments: + $ioConfigProvider: '@Ibexa\Core\IO\IOConfigProvider' + + Ibexa\Core\IO\Flysystem\PathPrefixer\DFSSiteAccessAwarePathPrefixer: + arguments: + $configProcessor: '@Ibexa\Contracts\Core\SiteAccess\ConfigProcessor' + $rootDir: "@=parameter('ibexa.io.nfs.adapter.config')['root']" + $path: "@=parameter('ibexa.io.nfs.adapter.config')['path']" + + ibexa.core.io.default_url_decorator: + alias: Ibexa\Core\IO\UrlDecorator\AbsolutePrefix + + Ibexa\Core\IO\UrlDecorator\AbsolutePrefix: + class: Ibexa\Core\IO\UrlDecorator\AbsolutePrefix + arguments: + - '@Ibexa\Core\IO\IOConfigProvider' + + # used by legacy in Image Converter to decorate its own url + Ibexa\Core\IO\UrlDecorator\Prefix: + class: Ibexa\Core\IO\UrlDecorator\Prefix + arguments: + - '@Ibexa\Core\IO\IOConfigProvider' + + Ibexa\Core\IO\UrlRedecorator: + class: Ibexa\Core\IO\UrlRedecorator + arguments: + - '@ibexa.core.io.default_url_decorator' + - '@Ibexa\Core\IO\UrlDecorator\Prefix' + + Ibexa\Core\IO\IOMetadataHandler\LegacyDFSCluster: + abstract: true + class: Ibexa\Core\IO\IOMetadataHandler\LegacyDFSCluster + arguments: + - ~ + - '@Ibexa\Core\IO\UrlDecorator\Prefix' + + ibexa.core.io.flysystem.path_normalizer: + class: League\Flysystem\PathNormalizer + abstract: true + + ibexa.core.io.flysystem.path_normalizer.whitespace: + parent: ibexa.core.io.flysystem.path_normalizer + class: League\Flysystem\WhitespacePathNormalizer + + Ibexa\Core\IO\FilePathNormalizer\Flysystem: + arguments: + $slugConverter: '@Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter' + $pathNormalizer: '@ibexa.core.io.flysystem.path_normalizer.whitespace' + + Ibexa\Core\IO\FilePathNormalizerInterface: '@Ibexa\Core\IO\FilePathNormalizer\Flysystem' diff --git a/src/lib/Resources/settings/limitations/language.yml b/src/lib/Resources/settings/limitations/language.yml new file mode 100644 index 0000000000..6160d4fcf7 --- /dev/null +++ b/src/lib/Resources/settings/limitations/language.yml @@ -0,0 +1,12 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + _instanceof: + Ibexa\Core\Limitation\LanguageLimitation\VersionTargetEvaluator: + tags: + - { name: ibexa.permissions.limitation_type.language_target_evaluator.version } + + Ibexa\Core\Limitation\LanguageLimitation\: + resource: '../../../Limitation/LanguageLimitation/*' diff --git a/src/lib/Resources/settings/notification.yml b/src/lib/Resources/settings/notification.yml new file mode 100644 index 0000000000..1976838c67 --- /dev/null +++ b/src/lib/Resources/settings/notification.yml @@ -0,0 +1,10 @@ +services: + Ibexa\Core\Notification\Renderer\Registry: + autowire: true + autoconfigure: false + public: false + + notification.renderer.registry: '@Ibexa\Core\Notification\Renderer\Registry' + + Ibexa\Contracts\Core\Repository\NotificationService: + alias: Ibexa\Core\Event\NotificationService diff --git a/eZ/Publish/Core/settings/policies.yml b/src/lib/Resources/settings/policies.yml similarity index 95% rename from eZ/Publish/Core/settings/policies.yml rename to src/lib/Resources/settings/policies.yml index dc1c87c910..a8b358bc3f 100644 --- a/eZ/Publish/Core/settings/policies.yml +++ b/src/lib/Resources/settings/policies.yml @@ -1,11 +1,11 @@ parameters: - ezpublish.api.role.policy_map: + ibexa.api.role.policy_map: content: read: { Class: true, Section: true, Owner: true, Group: true, Node: true, Subtree: true, State: true } diff: { Class: true, Section: true, Owner: true, Node: true, Subtree: true } view_embed: { Class: true, Section: true, Owner: true, Node: true, Subtree: true } - create: { Class: true, Section: true, ParentOwner: true, ParentGroup: true, ParentClass: true, ParentDepth: true, Node: true, Subtree: true, Language: true } - edit: { Class: true, Section: true, Owner: true, Group: true, Node: true, Subtree: true, Language: true, State: true } + create: { Class: true, Section: true, ParentOwner: true, ParentGroup: true, ParentClass: true, ParentDepth: true, Node: true, Subtree: true, Language: true, ChangeOwner: true } + edit: { Class: true, Section: true, Owner: true, Group: true, Node: true, Subtree: true, Language: true, State: true, ChangeOwner: true } publish: { Class: true, Section: true, Owner: true, Group: true, Node: true, Subtree: true, Language: true, State: true } manage_locations: { Class: true , Section: true , Owner: true, Subtree: true, State: true } hide: { Class: true, Section: true, Owner: true, Group: true, Node: true, Subtree: true, Language: true } diff --git a/src/lib/Resources/settings/repository.yml b/src/lib/Resources/settings/repository.yml new file mode 100644 index 0000000000..bf18749eb9 --- /dev/null +++ b/src/lib/Resources/settings/repository.yml @@ -0,0 +1,73 @@ +services: + # API Aliases + ibexa.api.repository: + alias: Ibexa\Core\Repository\SiteAccessAware\Repository + public: true + + ibexa.api.service.bookmark: + alias: Ibexa\Core\Event\BookmarkService + public: true + + ibexa.api.service.content: + alias: Ibexa\Core\Repository\SiteAccessAware\ContentService + public: true + + ibexa.api.service.content_type: + alias: Ibexa\Core\Repository\SiteAccessAware\ContentTypeService + public: true + + ibexa.api.service.field_type: + alias: Ibexa\Core\Event\FieldTypeService + public: true + + ibexa.api.service.role: + alias: Ibexa\Core\Event\RoleService + public: true + + ibexa.api.service.object_state: + alias: Ibexa\Core\Repository\SiteAccessAware\ObjectStateService + public: true + + ibexa.api.service.url_wildcard: + alias: Ibexa\Core\Event\URLWildcardService + public: true + + ibexa.api.service.url_alias: + alias: Ibexa\Core\Repository\SiteAccessAware\URLAliasService + public: true + + ibexa.api.service.user: + alias: Ibexa\Core\Repository\SiteAccessAware\UserService + public: true + + ibexa.api.service.search: + alias: Ibexa\Core\Repository\SiteAccessAware\SearchService + public: true + + ibexa.api.service.section: + alias: Ibexa\Core\Repository\SiteAccessAware\SectionService + public: true + + ibexa.api.service.trash: + alias: Ibexa\Core\Repository\SiteAccessAware\TrashService + public: true + + ibexa.api.service.location: + alias: Ibexa\Core\Repository\SiteAccessAware\LocationService + public: true + + ibexa.api.service.language: + alias: Ibexa\Core\Repository\SiteAccessAware\LanguageService + public: true + + ibexa.api.service.url: + alias: Ibexa\Core\Event\URLService + public: true + + ibexa.api.service.notification: + alias: Ibexa\Core\Repository\SiteAccessAware\NotificationService + public: true + + ibexa.api.service.user_preference: + alias: Ibexa\Core\Event\UserPreferenceService + public: true diff --git a/src/lib/Resources/settings/repository/autowire.yml b/src/lib/Resources/settings/repository/autowire.yml new file mode 100644 index 0000000000..a462cd5a12 --- /dev/null +++ b/src/lib/Resources/settings/repository/autowire.yml @@ -0,0 +1,30 @@ +services: + Ibexa\Contracts\Core\Repository\Repository: '@ibexa.api.repository' + + Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface: '@ibexa.config.resolver' + + Ibexa\Contracts\Core\Repository\BookmarkService: '@ibexa.api.service.bookmark' + Ibexa\Contracts\Core\Repository\ContentService: '@ibexa.api.service.content' + Ibexa\Contracts\Core\Repository\ContentTypeService: '@ibexa.api.service.content_type' + Ibexa\Contracts\Core\Repository\FieldTypeService: '@ibexa.api.service.field_type' + Ibexa\Contracts\Core\Repository\LanguageService: '@ibexa.api.service.language' + Ibexa\Contracts\Core\Repository\LocationService: '@ibexa.api.service.location' + Ibexa\Contracts\Core\Repository\NotificationService: '@ibexa.api.service.notification' + Ibexa\Contracts\Core\Repository\ObjectStateService: '@ibexa.api.service.object_state' + Ibexa\Contracts\Core\Repository\RoleService: '@ibexa.api.service.role' + Ibexa\Contracts\Core\Repository\SearchService: '@ibexa.api.service.search' + Ibexa\Contracts\Core\Repository\SectionService: '@ibexa.api.service.section' + Ibexa\Contracts\Core\Repository\UserPreferenceService: '@ibexa.api.service.user_preference' + Ibexa\Contracts\Core\Repository\UserService: '@ibexa.api.service.user' + Ibexa\Contracts\Core\Repository\URLService: '@ibexa.api.service.url' + Ibexa\Contracts\Core\Repository\URLWildcardService: '@ibexa.api.service.url_wildcard' + Ibexa\Contracts\Core\Repository\URLAliasService: '@ibexa.api.service.url_alias' + Ibexa\Contracts\Core\Repository\TrashService: '@ibexa.api.service.trash' + Ibexa\Contracts\Core\Repository\SettingService: '@Ibexa\Core\Event\SettingService' + Ibexa\Contracts\Core\Repository\TokenService: '@Ibexa\Core\Event\TokenService' + + Ibexa\Contracts\Core\Repository\PermissionService: '@Ibexa\Core\Repository\Permission\CachedPermissionService' + Ibexa\Contracts\Core\Repository\PermissionResolver: '@Ibexa\Contracts\Core\Repository\PermissionService' + Ibexa\Contracts\Core\Repository\PermissionCriterionResolver: '@Ibexa\Core\Repository\Permission\PermissionCriterionResolver' + + Ibexa\Core\Helper\FieldsGroups\FieldsGroupsList: '@Ibexa\Core\Helper\FieldsGroups\ArrayTranslatorFieldsGroupsList' diff --git a/src/lib/Resources/settings/repository/event.yml b/src/lib/Resources/settings/repository/event.yml new file mode 100644 index 0000000000..7007cad563 --- /dev/null +++ b/src/lib/Resources/settings/repository/event.yml @@ -0,0 +1,102 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + Ibexa\Core\Event\Repository: + arguments: + $repository: '@Ibexa\Core\Repository\Repository' + $bookmarkService: '@Ibexa\Core\Event\BookmarkService' + $contentService: '@Ibexa\Core\Event\ContentService' + $contentTypeService: '@Ibexa\Core\Event\ContentTypeService' + $fieldTypeService: '@Ibexa\Core\Event\FieldTypeService' + $languageService: '@Ibexa\Core\Event\LanguageService' + $locationService: '@Ibexa\Core\Event\LocationService' + $notificationService: '@Ibexa\Core\Event\NotificationService' + $objectStateService: '@Ibexa\Core\Event\ObjectStateService' + $roleService: '@Ibexa\Core\Event\RoleService' + $searchService: '@Ibexa\Core\Event\SearchService' + $sectionService: '@Ibexa\Core\Event\SectionService' + $trashService: '@Ibexa\Core\Event\TrashService' + $urlAliasService: '@Ibexa\Core\Event\URLAliasService' + $urlService: '@Ibexa\Core\Event\URLService' + $urlWildcardService: '@Ibexa\Core\Event\URLWildcardService' + $userPreferenceService: '@Ibexa\Core\Event\UserPreferenceService' + $userService: '@Ibexa\Core\Event\UserService' + + Ibexa\Core\Event\BookmarkService: + arguments: + $innerService: '@Ibexa\Core\Repository\BookmarkService' + + Ibexa\Core\Event\ContentService: + arguments: + $innerService: '@Ibexa\Core\Repository\ContentService' + + Ibexa\Core\Event\ContentTypeService: + arguments: + $innerService: '@Ibexa\Core\Repository\ContentTypeService' + + Ibexa\Core\Event\FieldTypeService: + arguments: + $innerService: '@Ibexa\Core\Repository\FieldTypeService' + + Ibexa\Core\Event\LanguageService: + arguments: + $innerService: '@Ibexa\Core\Repository\LanguageService' + + Ibexa\Core\Event\LocationService: + arguments: + $innerService: '@ibexa.api.service.inner_location' + + Ibexa\Core\Event\NotificationService: + arguments: + $innerService: '@Ibexa\Core\Repository\NotificationService' + + Ibexa\Core\Event\ObjectStateService: + arguments: + $innerService: '@Ibexa\Core\Repository\ObjectStateService' + + Ibexa\Core\Event\RoleService: + arguments: + $innerService: '@Ibexa\Core\Repository\RoleService' + + Ibexa\Core\Event\SearchService: + arguments: + $innerService: '@Ibexa\Core\Repository\SearchService' + + Ibexa\Core\Event\SectionService: + arguments: + $innerService: '@Ibexa\Core\Repository\SectionService' + + Ibexa\Core\Event\TrashService: + arguments: + $innerService: '@Ibexa\Core\Repository\TrashService' + + Ibexa\Core\Event\URLAliasService: + arguments: + $innerService: '@Ibexa\Core\Repository\URLAliasService' + + Ibexa\Core\Event\URLService: + arguments: + $innerService: '@Ibexa\Core\Repository\URLService' + + Ibexa\Core\Event\URLWildcardService: + arguments: + $innerService: '@Ibexa\Core\Repository\URLWildcardService' + + Ibexa\Core\Event\UserPreferenceService: + arguments: + $innerService: '@Ibexa\Core\Repository\UserPreferenceService' + + Ibexa\Core\Event\UserService: + arguments: + $innerService: '@Ibexa\Core\Repository\UserService' + + Ibexa\Core\Event\SettingService: + arguments: + $innerService: '@Ibexa\Core\Repository\SettingService' + + Ibexa\Core\Event\TokenService: + arguments: + $innerService: '@Ibexa\Core\Repository\TokenService' diff --git a/src/lib/Resources/settings/repository/inner.yml b/src/lib/Resources/settings/repository/inner.yml new file mode 100644 index 0000000000..f36d0b9a7e --- /dev/null +++ b/src/lib/Resources/settings/repository/inner.yml @@ -0,0 +1,273 @@ +imports: + - { resource: inner/name_schema.yaml } + +parameters: + ibexa.kernel.proxy_cache_dir: 'var/cache/repository/proxy' + + # intentionally defined class parameter to be used by the Repository Factory +services: + Ibexa\Bundle\Core\ApiLoader\RepositoryFactory: + class: Ibexa\Core\Base\Container\ApiLoader\RepositoryFactory + arguments: + - Ibexa\Core\Repository\Repository + - '%ibexa.api.role.policy_map%' + - '@Ibexa\Contracts\Core\Repository\LanguageResolver' + calls: + - [setContainer, ["@service_container"]] + + Ibexa\Core\Repository\Repository: + factory: ['@Ibexa\Bundle\Core\ApiLoader\RepositoryFactory', buildRepository] + arguments: + - '@ibexa.api.persistence_handler' + - '@ibexa.spi.search' + - '@Ibexa\Bundle\Core\EventListener\BackgroundIndexingTerminateListener' + - '@Ibexa\Core\Repository\Helper\RelationProcessor' + - '@Ibexa\Core\FieldType\FieldTypeRegistry' + - '@Ibexa\Core\Repository\User\PasswordHashService' + - '@Ibexa\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy' + - '@Ibexa\Core\Repository\ProxyFactory\ProxyDomainMapperFactory' + - '@Ibexa\Core\Repository\Mapper\ContentDomainMapper' + - '@Ibexa\Core\Repository\Mapper\ContentTypeDomainMapper' + - '@Ibexa\Core\Repository\Mapper\RoleDomainMapper' + - '@Ibexa\Core\Repository\Mapper\ContentMapper' + - '@Ibexa\Contracts\Core\Repository\Validator\ContentValidator' + - '@Ibexa\Core\Repository\Permission\LimitationService' + - '@Ibexa\Contracts\Core\Repository\PermissionService' + - '@Ibexa\Contracts\Core\Persistence\Filter\Content\Handler' + - '@Ibexa\Contracts\Core\Persistence\Filter\Location\Handler' + - '@Ibexa\Core\Repository\User\PasswordValidatorInterface' + - '@ibexa.config.resolver' + - '@Ibexa\Contracts\Core\Repository\NameSchema\NameSchemaServiceInterface' + - '%languages%' + + Ibexa\Core\Repository\ContentService: + class: Ibexa\Core\Repository\ContentService + factory: ['@Ibexa\Core\Repository\Repository', getContentService] + lazy: true + + Ibexa\Core\Repository\ContentTypeService: + class: Ibexa\Core\Repository\ContentTypeService + factory: ['@Ibexa\Core\Repository\Repository', getContentTypeService] + lazy: true + + Ibexa\Core\Repository\FieldTypeService: + class: Ibexa\Core\Repository\FieldTypeService + factory: ['@Ibexa\Core\Repository\Repository', getFieldTypeService] + lazy: true + + Ibexa\Core\Repository\RoleService: + class: Ibexa\Core\Repository\RoleService + factory: ['@Ibexa\Core\Repository\Repository', getRoleService] + lazy: true + + Ibexa\Core\Repository\ObjectStateService: + class: Ibexa\Core\Repository\ObjectStateService + factory: ['@Ibexa\Core\Repository\Repository', getObjectStateService] + lazy: true + + Ibexa\Core\Repository\URLWildcardService: + class: Ibexa\Core\Repository\URLWildcardService + factory: ['@Ibexa\Core\Repository\Repository', getURLWildcardService] + lazy: true + + Ibexa\Core\Repository\URLAliasService: + class: Ibexa\Core\Repository\URLAliasService + factory: ['@Ibexa\Core\Repository\Repository', getURLAliasService] + lazy: true + + Ibexa\Core\Repository\UserService: + class: Ibexa\Core\Repository\UserService + factory: ['@Ibexa\Core\Repository\Repository', getUserService] + calls: + - [setLogger, ["@?logger"]] + lazy: true + + Ibexa\Core\Repository\SearchService: + class: Ibexa\Core\Repository\SearchService + factory: ['@Ibexa\Core\Repository\Repository', getSearchService] + lazy: true + + Ibexa\Core\Repository\SectionService: + class: Ibexa\Core\Repository\SectionService + factory: ['@Ibexa\Core\Repository\Repository', getSectionService] + lazy: true + + Ibexa\Core\Repository\TrashService: + class: Ibexa\Core\Repository\TrashService + factory: ['@Ibexa\Core\Repository\Repository', getTrashService] + lazy: true + + ibexa.api.service.inner_location: + class: Ibexa\Core\Repository\LocationService + factory: ['@Ibexa\Core\Repository\Repository', getLocationService] + lazy: true + + Ibexa\Core\Repository\LanguageService: + class: Ibexa\Core\Repository\LanguageService + factory: ['@Ibexa\Core\Repository\Repository', getContentLanguageService] + lazy: true + + Ibexa\Core\Repository\URLService: + class: Ibexa\Core\Repository\URLService + factory: ['@Ibexa\Core\Repository\Repository', getUrlService] + lazy: true + + Ibexa\Core\Repository\BookmarkService: + class: Ibexa\Core\Repository\BookmarkService + factory: ['@Ibexa\Core\Repository\Repository', getBookmarkService] + lazy: true + + Ibexa\Core\Repository\NotificationService: + class: Ibexa\Core\Repository\NotificationService + factory: ['@Ibexa\Core\Repository\Repository', getNotificationService] + lazy: true + + Ibexa\Core\Repository\UserPreferenceService: + class: Ibexa\Core\Repository\UserPreferenceService + factory: ['@Ibexa\Core\Repository\Repository', getUserPreferenceService] + lazy: true + + Ibexa\Core\Repository\SettingService: + arguments: + $settingHandler: '@Ibexa\Core\Persistence\Cache\SettingHandler' + $permissionResolver: '@Ibexa\Contracts\Core\Repository\PermissionResolver' + + Ibexa\Core\Repository\TokenService: + autowire: true + autoconfigure: true + + # Factories + Ibexa\Bundle\Core\EventListener\BackgroundIndexingTerminateListener: + class: Ibexa\Core\Search\Common\BackgroundIndexer\NullIndexer + + Ibexa\Core\Repository\Helper\RelationProcessor: + class: Ibexa\Core\Repository\Helper\RelationProcessor + arguments: + - '@ibexa.api.persistence_handler' + calls: + - ['setLogger', ['@?logger']] + + # Domain mappers and proxies + Ibexa\Core\Repository\ProxyFactory\ProxyGenerator: + arguments: + $proxyCacheDir: '%ibexa.kernel.proxy_cache_dir%' + + Ibexa\Core\Repository\ProxyFactory\ProxyGeneratorInterface: + alias: 'Ibexa\Core\Repository\ProxyFactory\ProxyGenerator' + + Ibexa\Core\Repository\ProxyFactory\ProxyDomainMapperFactory: + arguments: + $proxyGenerator: '@Ibexa\Core\Repository\ProxyFactory\ProxyGeneratorInterface' + + Ibexa\Core\Repository\ProxyFactory\ProxyDomainMapper: + factory: ['@Ibexa\Core\Repository\ProxyFactory\ProxyDomainMapperFactory', 'create'] + arguments: + $repository: '@Ibexa\Core\Repository\Repository' + + Ibexa\Core\Repository\ProxyFactory\ProxyDomainMapperInterface: + alias: 'Ibexa\Core\Repository\ProxyFactory\ProxyDomainMapper' + + # Mappers + Ibexa\Core\Repository\Mapper\ProxyAwareDomainMapper: + abstract: true + calls: + - method: setProxyFactory + arguments: + $proxyFactory: '@Ibexa\Core\Repository\ProxyFactory\ProxyDomainMapperInterface' + + Ibexa\Core\Repository\Mapper\ContentMapper: + arguments: + $contentLanguageHandler: '@Ibexa\Contracts\Core\Persistence\Content\Language\Handler' + $fieldTypeRegistry: '@Ibexa\Core\FieldType\FieldTypeRegistry' + + Ibexa\Core\Repository\Mapper\ContentTypeDomainMapper: + parent: Ibexa\Core\Repository\Mapper\ProxyAwareDomainMapper + arguments: + $contentTypeHandler: '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler' + $contentLanguageHandler: '@Ibexa\Contracts\Core\Persistence\Content\Language\Handler' + $fieldTypeRegistry: '@Ibexa\Core\FieldType\FieldTypeRegistry' + + Ibexa\Core\Repository\Mapper\ContentDomainMapper: + parent: Ibexa\Core\Repository\Mapper\ProxyAwareDomainMapper + arguments: + $contentHandler: '@Ibexa\Contracts\Core\Persistence\Content\Handler' + $locationHandler: '@Ibexa\Contracts\Core\Persistence\Content\Location\Handler' + $contentTypeHandler: '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler' + $contentTypeDomainMapper: '@Ibexa\Core\Repository\Mapper\ContentTypeDomainMapper' + $contentLanguageHandler: '@Ibexa\Contracts\Core\Persistence\Content\Language\Handler' + $fieldTypeRegistry: '@Ibexa\Core\FieldType\FieldTypeRegistry' + $thumbnailStrategy: '@Ibexa\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy' + calls: + - [setLogger, ['@?logger']] + tags: + - { name: 'monolog.logger', channel: 'ibexa.core' } + + Ibexa\Core\Repository\Mapper\RoleDomainMapper: + arguments: + $limitationService: '@Ibexa\Core\Repository\Permission\LimitationService' + + # Permission-related + Ibexa\Core\Repository\Permission\LimitationService: + arguments: + $limitationTypes: !tagged_iterator { tag: ibexa.permissions.limitation_type, index_by: alias } + + Ibexa\Core\Repository\Permission\PermissionResolver: + arguments: + $roleDomainMapper: '@Ibexa\Core\Repository\Mapper\RoleDomainMapper' + $limitationService: '@Ibexa\Core\Repository\Permission\LimitationService' + $userHandler: '@Ibexa\Contracts\Core\Persistence\User\Handler' + $configResolver: '@ibexa.config.resolver' + $policyMap: '%ibexa.api.role.policy_map%' + + Ibexa\Core\Repository\Permission\PermissionCriterionResolver: + arguments: + $innerPermissionResolver: '@Ibexa\Core\Repository\Permission\PermissionResolver' + $limitationService: '@Ibexa\Core\Repository\Permission\LimitationService' + + Ibexa\Core\Repository\Permission\CachedPermissionService: + arguments: + $innerPermissionResolver: '@Ibexa\Core\Repository\Permission\PermissionResolver' + $permissionCriterionResolver: '@Ibexa\Core\Repository\Permission\PermissionCriterionResolver' + + Ibexa\Core\Repository\Strategy\ContentValidator\ContentValidatorStrategy: + arguments: + $contentValidators: !tagged_iterator ibexa.repository.content.validator + + Ibexa\Core\Repository\Validator\ContentCreateStructValidator: + arguments: + $contentMapper: '@Ibexa\Core\Repository\Mapper\ContentMapper' + $fieldTypeRegistry: '@Ibexa\Core\FieldType\FieldTypeRegistry' + tags: + - ibexa.repository.content.validator + + Ibexa\Core\Repository\Validator\ContentUpdateStructValidator: + arguments: + $contentMapper: '@Ibexa\Core\Repository\Mapper\ContentMapper' + $fieldTypeRegistry: '@Ibexa\Core\FieldType\FieldTypeRegistry' + $contentLanguageHandler: '@Ibexa\Contracts\Core\Persistence\Content\Language\Handler' + tags: + - ibexa.repository.content.validator + + Ibexa\Core\Repository\Validator\VersionValidator: + arguments: + $fieldTypeRegistry: '@Ibexa\Core\FieldType\FieldTypeRegistry' + tags: + - ibexa.repository.content.validator + + Ibexa\Contracts\Core\Repository\Validator\ContentValidator: '@Ibexa\Core\Repository\Strategy\ContentValidator\ContentValidatorStrategy' + + # LocationResolver + Ibexa\Core\Repository\LocationResolver\PermissionAwareLocationResolver: + arguments: + - '@ibexa.api.service.location' + + Ibexa\Core\Repository\LocationResolver\LocationResolver: + alias: Ibexa\Core\Repository\LocationResolver\PermissionAwareLocationResolver + + Ibexa\Core\Repository\Validator\TargetContentValidator: + arguments: + $contentHandler: '@Ibexa\Contracts\Core\Persistence\Content\Handler' + $contentTypeHandler: '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler' + + Ibexa\Core\Repository\Validator\TargetContentValidatorInterface: + alias: Ibexa\Core\Repository\Validator\TargetContentValidator diff --git a/src/lib/Resources/settings/repository/inner/name_schema.yaml b/src/lib/Resources/settings/repository/inner/name_schema.yaml new file mode 100644 index 0000000000..35cb97d184 --- /dev/null +++ b/src/lib/Resources/settings/repository/inner/name_schema.yaml @@ -0,0 +1,24 @@ +parameters: + ibexa.core.repository.name_schema.settings: [] + +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + Ibexa\Core\Repository\NameSchema\SchemaIdentifierExtractor: ~ + + Ibexa\Contracts\Core\Repository\NameSchema\SchemaIdentifierExtractorInterface: + alias: 'Ibexa\Core\Repository\NameSchema\SchemaIdentifierExtractor' + + Ibexa\Core\Repository\NameSchema\NameSchemaService: + arguments: + $settings: '%ibexa.core.repository.name_schema.settings%' + + Ibexa\Core\Repository\NameSchema\NameSchemaFilter: ~ + + Ibexa\Core\Repository\NameSchema\TokenHandler: ~ + + Ibexa\Contracts\Core\Repository\NameSchema\NameSchemaServiceInterface: + alias: 'Ibexa\Core\Repository\NameSchema\NameSchemaService' diff --git a/src/lib/Resources/settings/repository/siteaccessaware.yml b/src/lib/Resources/settings/repository/siteaccessaware.yml new file mode 100644 index 0000000000..b8182cf663 --- /dev/null +++ b/src/lib/Resources/settings/repository/siteaccessaware.yml @@ -0,0 +1,116 @@ +parameters: + languages: [] + ibexa.io.dir.storage: var/site/storage + ibexa.legacy.url_prefix: var/site/storage + ibexa.url_prefix: var/site/storage + ibexa.io.dir.root: '%ibexa.io.dir.storage%' + +services: + Ibexa\Core\Repository\SiteAccessAware\Repository: + arguments: + - '@Ibexa\Core\Event\Repository' + - '@Ibexa\Core\Repository\SiteAccessAware\ContentService' + - '@Ibexa\Core\Repository\SiteAccessAware\ContentTypeService' + - '@Ibexa\Core\Repository\SiteAccessAware\ObjectStateService' + - '@Ibexa\Core\Repository\SiteAccessAware\URLAliasService' + - '@Ibexa\Core\Repository\SiteAccessAware\UserService' + - '@Ibexa\Core\Repository\SiteAccessAware\SearchService' + - '@Ibexa\Core\Repository\SiteAccessAware\SectionService' + - '@Ibexa\Core\Repository\SiteAccessAware\TrashService' + - '@Ibexa\Core\Repository\SiteAccessAware\LocationService' + - '@Ibexa\Core\Repository\SiteAccessAware\LanguageService' + - '@Ibexa\Core\Repository\SiteAccessAware\NotificationService' + + Ibexa\Core\Repository\SiteAccessAware\ContentService: + arguments: + - '@Ibexa\Core\Event\ContentService' + - '@ibexa.helper.language_resolver' + + Ibexa\Core\Repository\SiteAccessAware\ContentTypeService: + arguments: + - '@Ibexa\Core\Event\ContentTypeService' + - '@ibexa.helper.language_resolver' + + Ibexa\Core\Repository\SiteAccessAware\ObjectStateService: + arguments: + - '@Ibexa\Core\Event\ObjectStateService' + - '@ibexa.helper.language_resolver' + + Ibexa\Core\Repository\SiteAccessAware\URLAliasService: + arguments: + - '@Ibexa\Core\Event\URLAliasService' + - '@ibexa.helper.language_resolver' + + Ibexa\Core\Repository\SiteAccessAware\UserService: + arguments: + - '@Ibexa\Core\Event\UserService' + - '@ibexa.helper.language_resolver' + + Ibexa\Core\Repository\SiteAccessAware\SearchService: + arguments: + - '@Ibexa\Core\Event\SearchService' + - '@ibexa.helper.language_resolver' + + Ibexa\Core\Repository\SiteAccessAware\SectionService: + arguments: + - '@Ibexa\Core\Event\SectionService' + + Ibexa\Core\Repository\SiteAccessAware\TrashService: + arguments: + - '@Ibexa\Core\Event\TrashService' + + Ibexa\Core\Repository\SiteAccessAware\LocationService: + arguments: + - '@Ibexa\Core\Event\LocationService' + - '@ibexa.helper.language_resolver' + + Ibexa\Core\Repository\SiteAccessAware\LanguageService: + arguments: + - '@Ibexa\Core\Event\LanguageService' + + Ibexa\Core\Repository\SiteAccessAware\NotificationService: + arguments: + - '@Ibexa\Core\Event\NotificationService' + + Ibexa\Core\Repository\SiteAccessAware\SettingService: + arguments: + - '@Ibexa\Core\Event\SettingService' + + ibexa.siteaccessaware.repository: '@Ibexa\Core\Repository\SiteAccessAware\Repository' + ibexa.siteaccessaware.service.content: '@Ibexa\Core\Repository\SiteAccessAware\ContentService' + ibexa.siteaccessaware.service.content_type: '@Ibexa\Core\Repository\SiteAccessAware\ContentTypeService' + ibexa.siteaccessaware.service.object_state: '@Ibexa\Core\Repository\SiteAccessAware\ObjectStateService' + ibexa.siteaccessaware.service.url_alias: '@Ibexa\Core\Repository\SiteAccessAware\URLAliasService' + ibexa.siteaccessaware.service.user: '@Ibexa\Core\Repository\SiteAccessAware\UserService' + ibexa.siteaccessaware.service.search: '@Ibexa\Core\Repository\SiteAccessAware\SearchService' + ibexa.siteaccessaware.service.section: '@Ibexa\Core\Repository\SiteAccessAware\SectionService' + ibexa.siteaccessaware.service.trash: '@Ibexa\Core\Repository\SiteAccessAware\TrashService' + ibexa.siteaccessaware.service.location: '@Ibexa\Core\Repository\SiteAccessAware\LocationService' + ibexa.siteaccessaware.service.language: '@Ibexa\Core\Repository\SiteAccessAware\LanguageService' + ibexa.siteaccessaware.service.notification: '@Ibexa\Core\Repository\SiteAccessAware\NotificationService' + ibexa.siteaccessaware.service.setting: '@Ibexa\Core\Repository\SiteAccessAware\SettingService' + + # Helpers + Ibexa\Core\Repository\SiteAccessAware\Language\AbstractLanguageResolver: + arguments: + $defaultUseAlwaysAvailable: true + $defaultShowAllTranslations: false + + Ibexa\Core\Repository\SiteAccessAware\Language\LanguageResolver: + parent: Ibexa\Core\Repository\SiteAccessAware\Language\AbstractLanguageResolver + arguments: ['%languages%'] + + Ibexa\Contracts\Core\Repository\LanguageResolver: + alias: Ibexa\Core\Repository\SiteAccessAware\Language\LanguageResolver + + ibexa.helper.language_resolver: + alias: Ibexa\Contracts\Core\Repository\LanguageResolver + + Ibexa\Core\IO\IOConfigProvider: + alias: Ibexa\Core\Repository\SiteAccessAware\Config\IOConfigResolver + + Ibexa\Core\Repository\SiteAccessAware\Config\IOConfigResolver: + arguments: + - '%ibexa.io.dir.root%' + - '%ibexa.legacy.url_prefix%' + - '%ibexa.url_prefix%' diff --git a/src/lib/Resources/settings/roles.yml b/src/lib/Resources/settings/roles.yml new file mode 100644 index 0000000000..290b991545 --- /dev/null +++ b/src/lib/Resources/settings/roles.yml @@ -0,0 +1,148 @@ +imports: + - { resource: limitations/language.yml } + +parameters: + ibexa.api.role.policy_map: {} + +services: + ## Implemented Limitations + Ibexa\Core\Limitation\ContentTypeLimitationType: + class: Ibexa\Core\Limitation\ContentTypeLimitationType + arguments: ['@ibexa.api.persistence_handler'] + tags: + - {name: ibexa.permissions.limitation_type, alias: Class} + + Ibexa\Core\Limitation\LanguageLimitationType: + class: Ibexa\Core\Limitation\LanguageLimitationType + arguments: + $persistenceLanguageHandler: '@Ibexa\Contracts\Core\Persistence\Content\Language\Handler' + $persistenceContentHandler: '@Ibexa\Contracts\Core\Persistence\Content\Handler' + $versionTargetEvaluators: !tagged ibexa.permissions.limitation_type.language_target_evaluator.version + tags: + - {name: ibexa.permissions.limitation_type, alias: Language} + + Ibexa\Core\Limitation\LocationLimitationType: + class: Ibexa\Core\Limitation\LocationLimitationType + arguments: ['@ibexa.api.persistence_handler'] + tags: + - {name: ibexa.permissions.limitation_type, alias: Node} + + Ibexa\Core\Limitation\OwnerLimitationType: + class: Ibexa\Core\Limitation\OwnerLimitationType + arguments: ['@ibexa.api.persistence_handler'] + tags: + - {name: ibexa.permissions.limitation_type, alias: Owner} + + Ibexa\Core\Limitation\ParentContentTypeLimitationType: + class: Ibexa\Core\Limitation\ParentContentTypeLimitationType + arguments: ['@ibexa.api.persistence_handler'] + tags: + - {name: ibexa.permissions.limitation_type, alias: ParentClass} + + Ibexa\Core\Limitation\ParentDepthLimitationType: + class: Ibexa\Core\Limitation\ParentDepthLimitationType + arguments: ['@ibexa.api.persistence_handler'] + tags: + - {name: ibexa.permissions.limitation_type, alias: ParentDepth} + + Ibexa\Core\Limitation\ParentOwnerLimitationType: + class: Ibexa\Core\Limitation\ParentOwnerLimitationType + arguments: ['@ibexa.api.persistence_handler'] + tags: + - {name: ibexa.permissions.limitation_type, alias: ParentOwner} + + Ibexa\Core\Limitation\ParentUserGroupLimitationType: + class: Ibexa\Core\Limitation\ParentUserGroupLimitationType + arguments: ['@ibexa.api.persistence_handler'] + tags: + - {name: ibexa.permissions.limitation_type, alias: ParentGroup} + + Ibexa\Core\Limitation\SectionLimitationType: + class: Ibexa\Core\Limitation\SectionLimitationType + arguments: ['@ibexa.api.persistence_handler'] + tags: + - {name: ibexa.permissions.limitation_type, alias: Section} + + Ibexa\Core\Limitation\NewSectionLimitationType: + class: Ibexa\Core\Limitation\NewSectionLimitationType + arguments: ['@ibexa.api.persistence_handler'] + tags: + - {name: ibexa.permissions.limitation_type, alias: NewSection} + + Ibexa\Core\Limitation\SiteAccessLimitationType: + class: Ibexa\Core\Limitation\SiteAccessLimitationType + arguments: ['@Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService'] + tags: + - {name: ibexa.permissions.limitation_type, alias: SiteAccess} + + Ibexa\Core\Limitation\ObjectStateLimitationType: + class: Ibexa\Core\Limitation\ObjectStateLimitationType + arguments: ['@ibexa.api.persistence_handler'] + tags: + - {name: ibexa.permissions.limitation_type, alias: State} + + Ibexa\Core\Limitation\NewObjectStateLimitationType: + class: Ibexa\Core\Limitation\NewObjectStateLimitationType + arguments: ['@ibexa.api.persistence_handler'] + tags: + - {name: ibexa.permissions.limitation_type, alias: NewState} + + Ibexa\Core\Limitation\SubtreeLimitationType: + class: Ibexa\Core\Limitation\SubtreeLimitationType + arguments: ['@ibexa.api.persistence_handler'] + tags: + - {name: ibexa.permissions.limitation_type, alias: Subtree} + + Ibexa\Core\Limitation\UserGroupLimitationType: + class: Ibexa\Core\Limitation\UserGroupLimitationType + arguments: ['@ibexa.api.persistence_handler'] + tags: + - {name: ibexa.permissions.limitation_type, alias: Group} + + Ibexa\Core\Limitation\StatusLimitationType: + class: Ibexa\Core\Limitation\StatusLimitationType + tags: + - {name: ibexa.permissions.limitation_type, alias: Status} + + Ibexa\Core\Limitation\ChangeOwnerLimitationType: + arguments: + $persistence: '@ibexa.api.persistence_handler' + tags: + - { name: ibexa.permissions.limitation_type, alias: ChangeOwner } + + ## Non implemented Limitations + # Configured to use "blocking" limitation (as they are not implemented) to avoid LimitationNotFoundException + + # ezjscore limitations, not applicable by API/Platform stack, users are adviced to use Symfony for ajax controllers + ibexa.api.role.limitation_type.function_list: + class: Ibexa\Core\Limitation\BlockingLimitationType + arguments: ['FunctionList'] + tags: [{name: ibexa.permissions.limitation_type, alias: FunctionList}] + + # Misc limitations used by ezcomments, not applicable to Platform replacement: EzCommentsBundle + ibexa.api.role.limitation_type.ezcomments.content_section: + class: Ibexa\Core\Limitation\BlockingLimitationType + arguments: ['ContentSection'] + tags: [{name: ibexa.permissions.limitation_type, alias: ContentSection}] + + ibexa.api.role.limitation_type.ezcomments.comment_creator: + class: Ibexa\Core\Limitation\BlockingLimitationType + arguments: ['CommentCreator'] + tags: [{name: ibexa.permissions.limitation_type, alias: CommentCreator}] + + ibexa.api.role.limitation_type.ezcomments.anti_spam: + class: Ibexa\Core\Limitation\BlockingLimitationType + arguments: ['AntiSpam'] + tags: [{name: ibexa.permissions.limitation_type, alias: AntiSpam}] + + Ibexa\Core\Limitation\MemberOfLimitationType: + arguments: + $persistence: '@ibexa.api.persistence_handler' + tags: + - { name: ibexa.permissions.limitation_type, alias: MemberOf } + + Ibexa\Core\Limitation\RoleLimitationType: + arguments: + $persistence: '@ibexa.api.persistence_handler' + tags: + - { name: ibexa.permissions.limitation_type, alias: Role } diff --git a/src/lib/Resources/settings/search_engines/common.yml b/src/lib/Resources/settings/search_engines/common.yml new file mode 100644 index 0000000000..3164615f7a --- /dev/null +++ b/src/lib/Resources/settings/search_engines/common.yml @@ -0,0 +1,58 @@ +imports: + - {resource: search_engines/field_value_mappers.yml} + +parameters: + ibexa.search.common.field_name_generator.map: + ez_integer: 'i' + ez_minteger: 'mi' + ez_id: 'id' + ez_mid: 'mid' + ez_string: 's' + ez_mstring: 'ms' + ez_long: 'l' + ez_text: 't' + ez_html: 'h' + ez_boolean: 'b' + ez_mboolean: 'mb' + ez_float: 'f' + ez_double: 'd' + ez_date: 'dt' + ez_point: 'p' + ez_currency: 'c' + ez_geolocation: 'gl' + ez_document: 'doc' + ez_fulltext: 'fulltext' + +services: + # Note: services tagged with 'ibexa.field_type.indexable' + # are registered to this one using compilation pass + Ibexa\Core\Search\Common\FieldRegistry: + class: Ibexa\Core\Search\Common\FieldRegistry + + # Mapping for our internal search field types + Ibexa\Core\Search\Common\FieldNameGenerator: + class: Ibexa\Core\Search\Common\FieldNameGenerator + arguments: + - '%ibexa.search.common.field_name_generator.map%' + + Ibexa\Core\Search\Common\FieldNameResolver: + class: Ibexa\Core\Search\Common\FieldNameResolver + arguments: + - '@Ibexa\Core\Search\Common\FieldRegistry' + - '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler' + - '@Ibexa\Core\Search\Common\FieldNameGenerator' + + # Note: services tagged with 'ibexa.search.common.field_value.mapper' + # are registered to this one using compilation pass + Ibexa\Core\Search\Common\FieldValueMapper\Aggregate: + class: Ibexa\Core\Search\Common\FieldValueMapper\Aggregate + + Ibexa\Core\Search\Common\EventSubscriber\: + resource: '../../../Search/Common/EventSubscriber/*' + exclude: '../../../Search/Common/EventSubscriber/{AbstractSearchEventSubscriber.php}' + autoconfigure: true + autowire: true + public: false + arguments: + $searchHandler: '@ibexa.spi.search' + $persistenceHandler: '@ibexa.api.persistence_handler' diff --git a/src/lib/Resources/settings/search_engines/field_value_mappers.yml b/src/lib/Resources/settings/search_engines/field_value_mappers.yml new file mode 100644 index 0000000000..0d5295ba6d --- /dev/null +++ b/src/lib/Resources/settings/search_engines/field_value_mappers.yml @@ -0,0 +1,75 @@ +services: + Ibexa\Core\Search\Common\FieldValueMapper\BooleanMapper: + class: Ibexa\Core\Search\Common\FieldValueMapper\BooleanMapper + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\BooleanField } + + Ibexa\Core\Search\Common\FieldValueMapper\DateMapper: + class: Ibexa\Core\Search\Common\FieldValueMapper\DateMapper + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\DateField } + + Ibexa\Core\Search\Common\FieldValueMapper\DocumentMapper: + class: Ibexa\Core\Search\Common\FieldValueMapper\DocumentMapper + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\DocumentField } + + Ibexa\Core\Search\Common\FieldValueMapper\GeoLocationMapper: + class: Ibexa\Core\Search\Common\FieldValueMapper\GeoLocationMapper + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\GeoLocationField } + + Ibexa\Core\Search\Common\FieldValueMapper\FloatMapper: + class: Ibexa\Core\Search\Common\FieldValueMapper\FloatMapper + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\FloatField } + + Ibexa\Core\Search\Common\FieldValueMapper\IdentifierMapper: + class: Ibexa\Core\Search\Common\FieldValueMapper\IdentifierMapper + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\IdentifierField } + + Ibexa\Core\Search\Common\FieldValueMapper\MultipleIntegerMapper: + class: Ibexa\Core\Search\Common\FieldValueMapper\MultipleIntegerMapper + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\MultipleIntegerField } + + Ibexa\Core\Search\Common\FieldValueMapper\IntegerMapper: + class: Ibexa\Core\Search\Common\FieldValueMapper\IntegerMapper + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\IntegerField } + + Ibexa\Core\Search\Common\FieldValueMapper\MultipleBooleanMapper: + class: Ibexa\Core\Search\Common\FieldValueMapper\MultipleBooleanMapper + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\MultipleBooleanField } + + Ibexa\Core\Search\Common\FieldValueMapper\MultipleIdentifierMapper: + class: Ibexa\Core\Search\Common\FieldValueMapper\MultipleIdentifierMapper + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\MultipleIdentifierField } + + Ibexa\Core\Search\Common\FieldValueMapper\MultipleStringMapper: + class: Ibexa\Core\Search\Common\FieldValueMapper\MultipleStringMapper + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\MultipleStringField } + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\TextField } + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\FullTextField } + + Ibexa\Core\Search\Common\FieldValueMapper\PriceMapper: + class: Ibexa\Core\Search\Common\FieldValueMapper\PriceMapper + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\PriceField } + + Ibexa\Core\Search\Common\FieldValueMapper\StringMapper: + class: Ibexa\Core\Search\Common\FieldValueMapper\StringMapper + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\StringField } + + Ibexa\Core\Search\Common\FieldValueMapper\RemoteIdentifierMapper: + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\RemoteIdentifierField } + + Ibexa\Core\Search\Common\FieldValueMapper\MultipleRemoteIdentifierMapper: + tags: + - { name: ibexa.search.common.field_value.mapper, maps: Ibexa\Contracts\Core\Search\FieldType\MultipleRemoteIdentifierField } diff --git a/src/lib/Resources/settings/search_engines/legacy.yml b/src/lib/Resources/settings/search_engines/legacy.yml new file mode 100644 index 0000000000..a028246951 --- /dev/null +++ b/src/lib/Resources/settings/search_engines/legacy.yml @@ -0,0 +1,87 @@ +imports: + - {resource: search_engines/legacy/criterion_handlers_common.yml} + - {resource: search_engines/legacy/criterion_handlers_content.yml} + - {resource: search_engines/legacy/criterion_handlers_location.yml} + - {resource: search_engines/legacy/indexer.yml} + - {resource: search_engines/legacy/sort_clause_handlers_common.yml} + - {resource: search_engines/legacy/sort_clause_handlers_content.yml} + - {resource: search_engines/legacy/sort_clause_handlers_location.yml} + - {resource: search_engines/common.yml} + +services: + # Aliasing storage connection in order to support sqlite://:memory: + # Otherwise it should be possible to use a separate database and/or database connection + ibexa.api.search_engine.legacy.connection: + alias: ibexa.api.storage_engine.legacy.connection + + Ibexa\Core\Search\Legacy\Content\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Search\Legacy\Content\Gateway\DoctrineDatabase + arguments: + $connection: '@ibexa.persistence.connection' + $criteriaConverter: '@ibexa.search.legacy.gateway.criteria_converter.content' + $sortClauseConverter: '@ibexa.search.legacy.gateway.sort_clause_converter.content' + $languageHandler: '@Ibexa\Contracts\Core\Persistence\Content\Language\Handler' + + Ibexa\Core\Search\Legacy\Content\Gateway\ExceptionConversion: + class: Ibexa\Core\Search\Legacy\Content\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Search\Legacy\Content\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.search.legacy.gateway.content: + alias: Ibexa\Core\Search\Legacy\Content\Gateway\ExceptionConversion + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\DoctrineDatabase + arguments: + $connection: '@ibexa.persistence.connection' + $criteriaConverter: '@ibexa.search.legacy.gateway.criteria_converter.location' + $sortClauseConverter: '@ibexa.search.legacy.gateway.sort_clause_converter.location' + $languageHandler: '@Ibexa\Contracts\Core\Persistence\Content\Language\Handler' + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\ExceptionConversion: + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Search\Legacy\Content\Location\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.search.legacy.gateway.location: + alias: Ibexa\Core\Search\Legacy\Content\Location\Gateway\ExceptionConversion + + Ibexa\Core\Search\Legacy\Content\Mapper\FullTextMapper: + class: Ibexa\Core\Search\Legacy\Content\Mapper\FullTextMapper + arguments: + - '@Ibexa\Core\Search\Common\FieldRegistry' + - '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler' + + ibexa.search.legacy.fulltext_mapper: + alias: Ibexa\Core\Search\Legacy\Content\Mapper\FullTextMapper + + Ibexa\Core\Search\Legacy\Content\Handler: + class: Ibexa\Core\Search\Legacy\Content\Handler + arguments: + $gateway: '@ibexa.search.legacy.gateway.content' + $locationGateway: '@ibexa.search.legacy.gateway.location' + $indexerGateway: '@Ibexa\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase' + $contentMapper: '@Ibexa\Core\Persistence\Legacy\Content\Mapper' + $locationMapper: '@Ibexa\Core\Persistence\Legacy\Content\Location\Mapper' + $languageHandler: '@Ibexa\Contracts\Core\Persistence\Content\Language\Handler' + $mapper: '@ibexa.search.legacy.fulltext_mapper' + tags: + - {name: ibexa.search.engine, alias: legacy} + lazy: true + + Ibexa\Core\Search\Legacy\Content\Indexer: + class: Ibexa\Core\Search\Legacy\Content\Indexer + arguments: + $logger: '@logger' + $persistenceHandler: '@Ibexa\Contracts\Core\Persistence\Handler' + $connection: '@ibexa.persistence.connection' + $searchHandler: '@Ibexa\Core\Search\Legacy\Content\Handler' + tags: + - {name: ibexa.search.engine.indexer, alias: legacy} + lazy: true + + Ibexa\Core\Search\Legacy\Content\IndexerGateway: + arguments: + $connection: '@ibexa.persistence.connection' diff --git a/src/lib/Resources/settings/search_engines/legacy/criterion_handlers_common.yml b/src/lib/Resources/settings/search_engines/legacy/criterion_handlers_common.yml new file mode 100644 index 0000000000..2b5edd76c0 --- /dev/null +++ b/src/lib/Resources/settings/search_engines/legacy/criterion_handlers_common.yml @@ -0,0 +1,353 @@ +parameters: + # Full text search configuration options. + ibexa.search.legacy.criterion_handler.full_text.configuration: + stopWordThresholdFactor: 0.66 + enableWildcards: true + commands: + - "ascii_search_cleanup" + - "cyrillic_diacritical" + - "cyrillic_search_cleanup" + - "cyrillic_transliterate_ascii" + - "doublequote_normalize" + - "endline_search_normalize" + - "greek_diacritical" + - "greek_normalize" + - "greek_transliterate_ascii" + - "hebrew_transliterate_ascii" + - "hyphen_normalize" + - "inverted_to_normal" + - "latin1_diacritical" + - "latin1_transliterate_ascii" + - "latin-exta_diacritical" + - "latin-exta_transliterate_ascii" + - "latin_search_cleanup" + - "latin_search_decompose" + - "math_to_ascii" + - "punctuation_normalize" + - "space_normalize" + - "special_decompose" + - "specialwords_search_normalize" + - "tab_search_normalize" + - "latin-exta_lowercase" + - "latin1_lowercase" + - "ascii_lowercase" + - "latin_lowercase" + - "cyrillic_lowercase" + - "greek_lowercase" + +services: + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler: + abstract: true + arguments: + $connection: '@ibexa.persistence.connection' + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldBase: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + abstract: true + arguments: + $contentTypeHandler: '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler' + $languageHandler: '@Ibexa\Contracts\Core\Persistence\Content\Language\Handler' + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler: + abstract: true + arguments: + $connection: '@ibexa.persistence.connection' + $transformationProcessor: '@Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased' + + # BC + ibexa.search.legacy.gateway.criterion_handler.base: '@Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler' + ibexa.search.legacy.gateway.criterion_handler.field_base: '@Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldBase' + ibexa.search.legacy.gateway.criterion_field_value_handler.base: '@Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler' + + # Criterion handlers + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\CompositeCriterion: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentId + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentName: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + tags: + - { name: ibexa.search.legacy.gateway.criterion_handler.content } + - { name: ibexa.search.legacy.gateway.criterion_handler.location } + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentTypeGroupId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentTypeGroupId + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentTypeId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentTypeId + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + - {name: ibexa.search.legacy.trash.gateway.criterion.handler} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentTypeIdentifier: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentTypeIdentifier + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + arguments: + $contentTypeHandler: '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler' + $logger: '@?logger' + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\DateMetadata: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\DateMetadata + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + - {name: ibexa.search.legacy.trash.gateway.criterion.handler} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\Field: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\Field + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldBase + arguments: + $fieldConverterRegistry: '@Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry' + $fieldValueConverter: '@Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Converter' + $transformationProcessor: '@Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased' + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldEmpty: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldEmpty + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldBase + arguments: + $fieldConverterRegistry: '@Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry' + $fieldTypeService: '@ibexa.api.service.field_type' + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FullText: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FullText + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + arguments: + $processor: '@Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased' + $languageMaskGenerator: '@Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator' + $configuration: '%ibexa.search.legacy.criterion_handler.full_text.configuration%' + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\IsContainer: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LanguageCode: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LanguageCode + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + arguments: + $maskGenerator: '@Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator' + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LogicalAnd: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LogicalAnd + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + - {name: ibexa.search.legacy.trash.gateway.criterion.handler} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LogicalNot: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LogicalNot + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + - {name: ibexa.search.legacy.trash.gateway.criterion.handler} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LogicalOr: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LogicalOr + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + - {name: ibexa.search.legacy.trash.gateway.criterion.handler} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\MapLocationDistance: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldBase + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\MapLocationDistance + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\MatchAll: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\MatchAll + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + - {name: ibexa.search.legacy.trash.gateway.criterion.handler} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\MatchNone: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\MatchNone + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + - {name: ibexa.search.legacy.trash.gateway.criterion.handler} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ObjectStateId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ObjectStateId + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ObjectStateIdentifier: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ObjectStateIdentifier + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldRelation: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldBase + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldRelation + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\RemoteId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\RemoteId + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\SectionId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\SectionId + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + - {name: ibexa.search.legacy.trash.gateway.criterion.handler} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\SectionIdentifier: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\SectionIdentifier + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserId + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserEmail: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserEmail + arguments: + $transformationProcessor: '@Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased' + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserLogin: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserLogin + arguments: + $transformationProcessor: '@Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased' + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\IsUserEnabled: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\IsUserEnabled + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserMetadata: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserMetadata + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + - {name: ibexa.search.legacy.trash.gateway.criterion.handler} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\IsUserBased: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\IsUserBased + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + # Criterion field value handlers + + # Note: services tagged with 'ibexa.search.legacy.gateway.criterion_handler.field_value' + # are registered to this one using compilation pass + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\HandlerRegistry: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\HandlerRegistry + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Converter: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Converter + arguments: + - '@Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\HandlerRegistry' + - '@ibexa.search.legacy.gateway.criterion_field_value_handler.default' + + ibexa.search.legacy.gateway.criterion_field_value_handler.collection.comma_separated: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Collection + arguments: + $separator: ',' + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.field_value, alias: ezauthor} + - {name: ibexa.search.legacy.gateway.criterion_handler.field_value, alias: ezcountry} + - {name: ibexa.search.legacy.gateway.criterion_handler.field_value, alias: ezobjectrelationlist} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Keyword: + parent: ibexa.search.legacy.gateway.criterion_field_value_handler.collection.comma_separated + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Keyword + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.field_value, alias: ezkeyword} + + ibexa.search.legacy.gateway.criterion_field_value_handler.collection.hypen_separated: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Collection + arguments: + $separator: '-' + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.field_value, alias: ezselection} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Composite: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Composite + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Simple: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Simple + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.field_value, alias: ezboolean} + - {name: ibexa.search.legacy.gateway.criterion_handler.field_value, alias: ezdate} + - {name: ibexa.search.legacy.gateway.criterion_handler.field_value, alias: ezdatetime} + - {name: ibexa.search.legacy.gateway.criterion_handler.field_value, alias: ezemail} + - {name: ibexa.search.legacy.gateway.criterion_handler.field_value, alias: ezinteger} + - {name: ibexa.search.legacy.gateway.criterion_handler.field_value, alias: ezobjectrelation} + - {name: ibexa.search.legacy.gateway.criterion_handler.field_value, alias: eztime} + + ibexa.search.legacy.gateway.criterion_field_value_handler.default: + alias: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Composite diff --git a/src/lib/Resources/settings/search_engines/legacy/criterion_handlers_content.yml b/src/lib/Resources/settings/search_engines/legacy/criterion_handlers_content.yml new file mode 100644 index 0000000000..12bd432b38 --- /dev/null +++ b/src/lib/Resources/settings/search_engines/legacy/criterion_handlers_content.yml @@ -0,0 +1,51 @@ +services: + # Note: services tagged with: + # - ibexa.search.legacy.gateway.criterion_handler.content + # are registered to this one using compilation pass + ibexa.search.legacy.gateway.criteria_converter.content: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter + + Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\Ancestor: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\Ancestor + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + + Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\LocationId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\LocationId + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + + Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\LocationRemoteId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\LocationRemoteId + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + + Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\ParentLocationId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\ParentLocationId + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + + # Needs to be before subtree, as permission_subtree extends it. + # Only needed for Content Search on SQL engines where applying Permissions Subtree criterion on all possible + # locations leads to peformance issues: https://issues.ibexa.co/browse/EZP-23037 + Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\PermissionSubtree: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\PermissionSubtree + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + + Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\Subtree: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\Subtree + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} + + Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\Visibility: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Gateway\CriterionHandler\Visibility + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.content} diff --git a/src/lib/Resources/settings/search_engines/legacy/criterion_handlers_location.yml b/src/lib/Resources/settings/search_engines/legacy/criterion_handlers_location.yml new file mode 100644 index 0000000000..1d811974b8 --- /dev/null +++ b/src/lib/Resources/settings/search_engines/legacy/criterion_handlers_location.yml @@ -0,0 +1,60 @@ +services: + # Note: services tagged with: + # - ibexa.search.legacy.gateway.criterion_handler.location + # are registered to this one using compilation pass + ibexa.search.legacy.gateway.criteria_converter.location: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Ancestor: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Ancestor + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location\Depth: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location\Depth + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\LocationId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\LocationId + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location\IsMainLocation: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location\IsMainLocation + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\ParentLocationId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\ParentLocationId + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location\Priority: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location\Priority + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\LocationRemoteId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\LocationRemoteId + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Subtree: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Subtree + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.location} + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Visibility: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Visibility + tags: + - {name: ibexa.search.legacy.gateway.criterion_handler.location} diff --git a/src/lib/Resources/settings/search_engines/legacy/indexer.yml b/src/lib/Resources/settings/search_engines/legacy/indexer.yml new file mode 100644 index 0000000000..1cf7757994 --- /dev/null +++ b/src/lib/Resources/settings/search_engines/legacy/indexer.yml @@ -0,0 +1,13 @@ +services: + Ibexa\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase: + arguments: + $connection: '@ibexa.persistence.connection' + $typeHandler: '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler' + $transformationProcessor: '@Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased' + $searchIndex: '@Ibexa\Core\Search\Legacy\Content\WordIndexer\Repository\SearchIndex' + $languageMaskGenerator: '@Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator' + $fullTextSearchConfiguration: '%ibexa.search.legacy.criterion_handler.full_text.configuration%' + + Ibexa\Core\Search\Legacy\Content\WordIndexer\Repository\SearchIndex: + arguments: + $connection: '@ibexa.persistence.connection' diff --git a/src/lib/Resources/settings/search_engines/legacy/sort_clause_handlers_common.yml b/src/lib/Resources/settings/search_engines/legacy/sort_clause_handlers_common.yml new file mode 100644 index 0000000000..cbe5e2363a --- /dev/null +++ b/src/lib/Resources/settings/search_engines/legacy/sort_clause_handlers_common.yml @@ -0,0 +1,111 @@ +services: + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler: + abstract: true + arguments: + $connection: '@ibexa.persistence.connection' + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\ContentId: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\ContentId + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.content} + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\ContentName: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\ContentName + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.content} + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + - {name: ibexa.search.legacy.trash.gateway.sort_clause.handler} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\DateModified: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\DateModified + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.content} + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\DatePublished: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\DatePublished + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.content} + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Field: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler + arguments: + $languageHandler: '@Ibexa\Contracts\Core\Persistence\Content\Language\Handler' + $contentTypeHandler: '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler' + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.content} + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\MapLocationDistance: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\MapLocationDistance + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Field + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.content} + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\SectionIdentifier: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\SectionIdentifier + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.content} + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\SectionName: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\SectionName + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.content} + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + - {name: ibexa.search.legacy.trash.gateway.sort_clause.handler} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Factory\RandomSortClauseHandlerFactory: + arguments: + - '@ibexa.persistence.connection' + - !tagged ibexa.search.legacy.gateway.sort_clause_handler.gateway.random + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom + factory: ['@Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Factory\RandomSortClauseHandlerFactory', 'getGateway'] + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.content} + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Random\MySqlRandom: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.gateway.random} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Random\SqlLiteRandom: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.gateway.random} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Random\PgSqlRandom: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.gateway.random} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Trash\ContentTypeName: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler + tags: + - {name: ibexa.search.legacy.trash.gateway.sort_clause.handler} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Trash\UserLogin: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler + tags: + - {name: ibexa.search.legacy.trash.gateway.sort_clause.handler} + + Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Trash\DateTrashed: + parent: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler + tags: + - {name: ibexa.search.legacy.trash.gateway.sort_clause.handler} + + # BC + ibexa.search.legacy.gateway.sort_clause_handler.base: '@Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler' + ibexa.search.legacy.gateway.sort_clause_handler.common.field: '@Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Field' diff --git a/src/lib/Resources/settings/search_engines/legacy/sort_clause_handlers_content.yml b/src/lib/Resources/settings/search_engines/legacy/sort_clause_handlers_content.yml new file mode 100644 index 0000000000..282151331e --- /dev/null +++ b/src/lib/Resources/settings/search_engines/legacy/sort_clause_handlers_content.yml @@ -0,0 +1,6 @@ +services: + # Note: services tagged with: + # - ibexa.search.legacy.gateway.sort_clause_handler.content + # are registered to this one using compilation pass + ibexa.search.legacy.gateway.sort_clause_converter.content: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter diff --git a/src/lib/Resources/settings/search_engines/legacy/sort_clause_handlers_location.yml b/src/lib/Resources/settings/search_engines/legacy/sort_clause_handlers_location.yml new file mode 100644 index 0000000000..10fff1633c --- /dev/null +++ b/src/lib/Resources/settings/search_engines/legacy/sort_clause_handlers_location.yml @@ -0,0 +1,45 @@ +services: + # Note: services tagged with: + # - ibexa.search.legacy.gateway.sort_clause_handler.location + # are registered to this one using compilation pass + ibexa.search.legacy.gateway.sort_clause_converter.location: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Id: + parent: ibexa.search.legacy.gateway.sort_clause_handler.base + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Id + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Depth: + parent: ibexa.search.legacy.gateway.sort_clause_handler.base + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Depth + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + - {name: ibexa.search.legacy.trash.gateway.sort_clause.handler} + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Path: + parent: ibexa.search.legacy.gateway.sort_clause_handler.base + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Path + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + - {name: ibexa.search.legacy.trash.gateway.sort_clause.handler} + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\IsMainLocation: + parent: ibexa.search.legacy.gateway.sort_clause_handler.base + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\IsMainLocation + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Priority: + parent: ibexa.search.legacy.gateway.sort_clause_handler.base + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Priority + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} + - {name: ibexa.search.legacy.trash.gateway.sort_clause.handler} + + Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Visibility: + parent: ibexa.search.legacy.gateway.sort_clause_handler.base + class: Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Visibility + tags: + - {name: ibexa.search.legacy.gateway.sort_clause_handler.location} diff --git a/src/lib/Resources/settings/settings.yml b/src/lib/Resources/settings/settings.yml new file mode 100644 index 0000000000..1ea2293059 --- /dev/null +++ b/src/lib/Resources/settings/settings.yml @@ -0,0 +1,26 @@ +parameters: + ibexa.persistence.legacy.dsn: 'sqlite://:memory:' + anonymous_user_id: 10 + kernel.debug: false + languages: [] + ibexa.io.images.storage.prefix: images + ibexa.io.images.storage.prefix.draft: images-versioned + ibexa.io.binary_file.storage.prefix: original + ibexa.site_access.list: [test] + ibexa.site_access.config.default.anonymous_user_id: 10 + ibexa.site_access.config.default.io.permissions.files: 0644 + ibexa.site_access.config.default.io.permissions.directories: 0755 + +services: + ibexa.api.persistence_handler: + #To disable cache, switch alias to Ibexa\Contracts\Core\Persistence\Handler + alias: Ibexa\Core\Persistence\Cache\Handler + + Ibexa\Contracts\Core\Persistence\Handler: + alias: Ibexa\Core\Persistence\Legacy\Handler + + ibexa.spi.search: + alias: Ibexa\Contracts\Core\Search\VersatileHandler + + Ibexa\Contracts\Core\Search\VersatileHandler: + alias: Ibexa\Core\Search\Legacy\Content\Handler diff --git a/src/lib/Resources/settings/storage_engines/cache.yml b/src/lib/Resources/settings/storage_engines/cache.yml new file mode 100644 index 0000000000..48bb148387 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/cache.yml @@ -0,0 +1,384 @@ +parameters: + # Make sure logging is only enabled for debug by default + ibexa.spi.persistence.cache.persistenceLogger.enableCallLogging: "%kernel.debug%" + # Global in-memory settings, for meta data + ibexa.spi.persistence.cache.inmemory.ttl: 3000 + ibexa.spi.persistence.cache.inmemory.limit: 100 + ibexa.spi.persistence.cache.inmemory.enable: true + # Global in-memory settings, for content in-memory cache + ## WARNING: TTL on purpose low to avoid getting outdated data in prod! For dev config you can safely increase by x3 + ibexa.spi.persistence.cache.inmemory.content.ttl: 300 + ibexa.spi.persistence.cache.inmemory.content.limit: 100 + ibexa.spi.persistence.cache.inmemory.content.enable: true + ibexa.core.persistence.cache.tag_prefix: 'ibx-' + ibexa.core.persistence.cache.tag_patterns: + by_group: 'bg-%s' + on_content: 'oc-%s' + bookmark: 'b-%s' + content: 'c-%s' + content_fields_type: 'cft-%s' + content_info: 'ci-%s' + content_info_by_remote_id: 'cibri-%s' + content_locations: 'cl-%s' + content_version_info: 'cvi-%s' + content_version_list: 'c-%s-vl' + content_version: 'c-%%s-v-%%s' + content_type: 'ct-%s' + content_type_with_by_remote_suffix: 'ct-%s-br' + content_type_with_id_suffix: 'ct-%s-bi' + content_type_field_map: 'ctfm' + content_type_group: 'ctg-%s' + content_type_group_with_id_suffix: 'ctg-%s-bi' + content_type_group_list: 'ctgl-%s' + content_type_list_by_group: 'ctlbg-%s' + content_relation: 'cr-%s' + content_relations_list: 'crl-%s' + image_variation: 'ig' + image_variation_name: 'ign-%s' + image_variation_siteaccess: 'igs-%s' + image_variation_content: 'igc-%s' + image_variation_field: 'igf-%s' + language: 'la-%s' + language_code: 'lac-%s' + language_list: 'lal' + location: 'l-%s' + location_path: 'lp-%s' + location_remote_id: 'lri' + location_subtree: 'ls-%s' + content_locations_with_parent_for_draft_suffix: 'cl-%s-pfd' + notification: 'n-%s' + notification_count: 'nc-%s' + notification_pending_count: 'npc-%s' + policy: 'p-%s' + relation: 're-%s' + role: 'r-%s' + role_assignment: 'ra-%s' + role_assignment_group_list: 'ragl-%s' + role_assignment_role_list: 'rarl-%s' + role_with_by_id_suffix: 'r-%s-bi' + role_assignment_with_by_role_suffix: 'ra-%s-bro' + role_assignment_with_by_role_offset_limit_suffix: 'ra-%%s-bro-%%s-%%s' + role_assignment_with_by_group_inherited_suffix: 'ra-%s-bgi' + role_assignment_with_by_group_suffix: 'ra-%s-bg' + section: 'se-%s' + section_with_by_id: 'se-%s-bi' + setting: 'set-%%s-%%s' + state: 's-%s' + state_by_group: 'sbg-%s' + state_group: 'sg-%s' + state_group_with_id_suffix: 'sg-%s-bi' + state_group_all: 'sga' + state_identifier: 'si-%s' + state_identifier_with_by_group_suffix: 'si-%%s-bg-%%s' + state_list_by_group: 'slbg-%s' + state_by_group_on_content: 'sbg-%%s-oc-%%s' + user: 'u-%s' + user_with_by_login_suffix: 'u-%s-bl' + user_with_by_email_suffix: 'u-%s-be' + users_with_by_email_suffix: 'us-%s-be' + user_with_account_key_suffix: 'u-%s-ak' + user_with_by_account_key_suffix: 'u-%s-bak' + url: 'url-%s' + url_alias: 'urla-%s' + url_alias_with_hash: 'urla-%%s-%%s' + url_alias_custom: 'urlac-%s' + url_alias_location: 'urlal-%s' + url_alias_location_list: 'urlall-%s' + url_alias_location_list_custom: 'urlall-%s-c' + url_alias_location_path: 'urlalp-%s' + url_alias_not_found: 'urlanf' + url_alias_url: 'urlau-%s' + url_wildcard: 'urlw-%s' + url_wildcard_not_found: 'urlwnf' + url_wildcard_source: 'urlws-%s' + user_preference: 'up' + user_preference_with_suffix: 'up-%%s-%%s' + type: 't-%s' + type_without_value: 't' + type_group: 'tg-%s' + type_map: 'tm' + version: 'v-%s' + ibexa.core.persistence.cache.key_patterns: + by_identifier_suffix: '-bi' + by_remote_suffix: '-br' + parent_for_draft_suffix: '-pfd' + by_login_suffix: '-bl' + by_email_suffix: '-be' + by_account_key_suffix: '-bak' + account_key_suffix: '-ak' + by_role_suffix: '-bro' + by_group_inherited_suffix: '-bgi' + by_group_suffix: '-bg' + on_content_suffix: '-oc' + custom_suffix: '-c' + by_group: 'bg-%s' + on_content: 'oc-%s' + bookmark: 'b-%s' + content: 'c-%s' + content_fields_type: 'cft-%s' + content_info: 'ci-%s' + content_info_by_remote_id: 'cibri-%s' + content_locations: 'cl-%s' + content_relations_count_with_by_version_type_suffix: 'crc-%%s-v-%%s-t-%%s' + content_relations_list_with_by_version_type_suffix: 'crl-%%s-l-%%s-o-%%s-v-%%s-t-%%s' + content_reverse_relations_count: 'crrc-%s' + content_version_info: 'cvi-%s' + content_version_list: 'c-%s-vl' + content_version: 'c-%%s-v-%%s' + content_type: 'ct-%s' + content_type_with_by_remote_suffix: 'ct-%s-br' + content_type_with_id_suffix: 'ct-%s-bi' + content_type_field_map: 'ctfm' + content_type_group: 'ctg-%s' + content_type_group_with_id_suffix: 'ctg-%s-bi' + content_type_list_by_field_definition_identifier: 'ctlbfdi-%s' + content_type_group_list: 'ctgl-%s' + content_type_list_by_group: 'ctlbg-%s' + image_variation: 'ig' + image_variation_name: 'ign-%s' + image_variation_siteaccess: 'igs-%s' + image_variation_content: 'igc-%s' + image_variation_field: 'igf-%s' + language: 'la-%s' + language_code: 'lac-%s' + language_list: 'lal' + location: 'l-%s' + location_path: 'lp-%s' + location_remote_id: 'lri' + location_subtree: 'ls-%s' + content_locations_with_parent_for_draft_suffix: 'cl-%s-pfd' + notification: 'n-%s' + notification_count: 'nc-%s' + notification_pending_count: 'npc-%s' + policy: 'p-%s' + relation: 're-%s' + role: 'r-%s' + role_assignment: 'ra-%s' + role_assignment_group_list: 'ragl-%s' + role_assignment_role_list: 'rarl-%s' + role_with_by_id_suffix: 'r-%s-bi' + role_assignment_with_by_role_suffix: 'ra-%s-bro' + role_assignment_with_by_role_offset_limit_suffix: 'ra-%%s-bro-%%s-%%s' + role_assignment_with_by_group_inherited_suffix: 'ra-%s-bgi' + role_assignment_with_by_group_suffix: 'ra-%s-bg' + section: 'se-%s' + section_with_by_id: 'se-%s-bi' + setting: 'set-%%s-%%s' + state: 's-%s' + state_by_group: 'sbg-%s' + state_group: 'sg-%s' + state_group_with_id_suffix: 'sg-%s-bi' + state_group_all: 'sga' + state_identifier: 'si-%s' + state_identifier_with_by_group_suffix: 'si-%%s-bg-%%s' + state_list_by_group: 'slbg-%s' + state_by_group_on_content: 'sbg-%%s-oc-%%s' + user: 'u-%s' + user_with_by_login_suffix: 'u-%s-bl' + user_with_by_email_suffix: 'u-%s-be' + users_with_by_email_suffix: 'us-%s-be' + user_with_account_key_suffix: 'u-%s-ak' + user_with_by_account_key_suffix: 'u-%s-bak' + url: 'url-%s' + url_alias: 'urla-%s' + url_alias_with_hash: 'urla-%%s-%%s' + url_alias_custom: 'urlac-%s' + url_alias_location: 'urlal-%s' + url_alias_location_list: 'urlall-%s' + url_alias_location_list_custom: 'urlall-%s-c' + url_alias_location_path: 'urlalp-%s' + url_alias_not_found: 'urlanf' + url_alias_url: 'urlau-%s' + url_wildcard: 'urlw-%s' + url_wildcard_not_found: 'urlwnf' + url_wildcard_source: 'urlws-%s' + user_preference: 'up' + user_preference_with_suffix: 'up-%%s-%%s' + type: 't-%s' + type_without_value: 't' + type_group: 'tg-%s' + type_map: 'tm' + version: 'v-%s' + +services: + # Setup cache pool, with InMemoryCacheAdapter as decorator + Ibexa\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter: + decorates: ibexa.cache_pool + lazy: true + arguments: + $sharedPool: '@Ibexa\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter.inner' + $inMemoryPools: !tagged ibexa.cache.persistence.inmemory + + ibexa.cache_pool: + public: true + class: Symfony\Component\Cache\Adapter\TagAwareAdapter + arguments: ['@ibexa.cache_pool.driver'] + + # Note for tests: Default changed to in-memory Array cache in tests/common.yml by default, and opt in for redis + # testing is defined in \Ibexa\Tests\Integration\Core\LegacyTestContainerBuilder + ibexa.cache_pool.driver: + public: false + class: Symfony\Component\Cache\Adapter\FilesystemAdapter + arguments: ["", 120] + + # Logger which logs info when in dev for Symfony web toolbar + Ibexa\Core\Persistence\Cache\PersistenceLogger: + class: Ibexa\Core\Persistence\Cache\PersistenceLogger + arguments: + - '%ibexa.spi.persistence.cache.persistenceLogger.enableCallLogging%' + + # In-Memory cache pools + ibexa.spi.persistence.cache.inmemory: + public: false + class: Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache + arguments: + - '%ibexa.spi.persistence.cache.inmemory.ttl%' + - '%ibexa.spi.persistence.cache.inmemory.limit%' + - '%ibexa.spi.persistence.cache.inmemory.enable%' + tags: [ ibexa.cache.persistence.inmemory ] + + ibexa.spi.persistence.cache.inmemory.content: + public: false + class: Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache + arguments: + - '%ibexa.spi.persistence.cache.inmemory.content.ttl%' + - '%ibexa.spi.persistence.cache.inmemory.content.limit%' + - '%ibexa.spi.persistence.cache.inmemory.content.enable%' + tags: [ ibexa.cache.persistence.inmemory ] + + Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface: + alias: Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGenerator + + Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierSanitizer: ~ + Ibexa\Core\Persistence\Cache\LocationPathConverter: ~ + + Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGenerator: + autoconfigure: true + arguments: + $prefix: '%ibexa.core.persistence.cache.tag_prefix%' + $tagPatterns: '%ibexa.core.persistence.cache.tag_patterns%' + $keyPatterns: '%ibexa.core.persistence.cache.key_patterns%' + tags: + - { name: monolog.logger, channel: 'ibexa.cache' } + + # SPI Persistence Cache Handlers, incl abstracts + Ibexa\Core\Persistence\Cache\AbstractHandler: + class: Ibexa\Core\Persistence\Cache\AbstractHandler + abstract: true + arguments: + - '@ibexa.cache_pool' + - '@Ibexa\Contracts\Core\Persistence\Handler' + - '@Ibexa\Core\Persistence\Cache\PersistenceLogger' + - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface' + - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierSanitizer' + - '@Ibexa\Core\Persistence\Cache\LocationPathConverter' + + Ibexa\Core\Persistence\Cache\AbstractInMemoryHandler: + class: Ibexa\Core\Persistence\Cache\AbstractInMemoryHandler + abstract: true + arguments: + - '@ibexa.cache_pool' + - '@Ibexa\Core\Persistence\Cache\PersistenceLogger' + - '@ibexa.spi.persistence.cache.inmemory' + + Ibexa\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler: + parent: Ibexa\Core\Persistence\Cache\AbstractInMemoryHandler + class: Ibexa\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler + abstract: true + arguments: + - '@Ibexa\Contracts\Core\Persistence\Handler' + - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface' + - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierSanitizer' + - '@Ibexa\Core\Persistence\Cache\LocationPathConverter' + - '@?Ibexa\Core\Persistence\Cache\CacheIndicesValidatorInterface' + + Ibexa\Core\Persistence\Cache\SectionHandler: + class: Ibexa\Core\Persistence\Cache\SectionHandler + parent: Ibexa\Core\Persistence\Cache\AbstractHandler + + Ibexa\Core\Persistence\Cache\LocationHandler: + class: Ibexa\Core\Persistence\Cache\LocationHandler + parent: Ibexa\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler + arguments: # Overload argument to use content in-memory service + index_2: '@ibexa.spi.persistence.cache.inmemory.content' + + Ibexa\Core\Persistence\Cache\ContentHandler: + class: Ibexa\Core\Persistence\Cache\ContentHandler + parent: Ibexa\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler + arguments: # Overload argument to use content in-memory service + index_2: '@ibexa.spi.persistence.cache.inmemory.content' + + Ibexa\Core\Persistence\Cache\ObjectStateHandler: + class: Ibexa\Core\Persistence\Cache\ObjectStateHandler + parent: Ibexa\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler + + Ibexa\Core\Persistence\Cache\ContentLanguageHandler: + class: Ibexa\Core\Persistence\Cache\ContentLanguageHandler + parent: Ibexa\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler + + Ibexa\Core\Persistence\Cache\ContentTypeHandler: + class: Ibexa\Core\Persistence\Cache\ContentTypeHandler + parent: Ibexa\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler + + Ibexa\Core\Persistence\Cache\UserHandler: + class: Ibexa\Core\Persistence\Cache\UserHandler + parent: Ibexa\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler + + Ibexa\Core\Persistence\Cache\TransactionHandler: + class: Ibexa\Core\Persistence\Cache\TransactionHandler + parent: Ibexa\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler + + Ibexa\Core\Persistence\Cache\TrashHandler: + class: Ibexa\Core\Persistence\Cache\TrashHandler + parent: Ibexa\Core\Persistence\Cache\AbstractHandler + + Ibexa\Core\Persistence\Cache\UrlAliasHandler: + class: Ibexa\Core\Persistence\Cache\UrlAliasHandler + parent: Ibexa\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler + arguments: # Overload argument to use content in-memory service + index_2: '@ibexa.spi.persistence.cache.inmemory.content' + + Ibexa\Core\Persistence\Cache\URLHandler: + class: Ibexa\Core\Persistence\Cache\URLHandler + parent: Ibexa\Core\Persistence\Cache\AbstractHandler + + Ibexa\Core\Persistence\Cache\BookmarkHandler: + class: Ibexa\Core\Persistence\Cache\BookmarkHandler + parent: Ibexa\Core\Persistence\Cache\AbstractHandler + + Ibexa\Core\Persistence\Cache\NotificationHandler: + class: Ibexa\Core\Persistence\Cache\NotificationHandler + parent: Ibexa\Core\Persistence\Cache\AbstractHandler + + Ibexa\Core\Persistence\Cache\UserPreferenceHandler: + class: Ibexa\Core\Persistence\Cache\UserPreferenceHandler + parent: Ibexa\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler + + Ibexa\Core\Persistence\Cache\UrlWildcardHandler: + class: Ibexa\Core\Persistence\Cache\UrlWildcardHandler + parent: Ibexa\Core\Persistence\Cache\AbstractHandler + + Ibexa\Core\Persistence\Cache\SettingHandler: + parent: Ibexa\Core\Persistence\Cache\AbstractInMemoryPersistenceHandler + + Ibexa\Core\Persistence\Cache\Handler: + class: Ibexa\Core\Persistence\Cache\Handler + arguments: + - '@Ibexa\Contracts\Core\Persistence\Handler' + - '@Ibexa\Core\Persistence\Cache\SectionHandler' + - '@Ibexa\Core\Persistence\Cache\LocationHandler' + - '@Ibexa\Core\Persistence\Cache\ContentHandler' + - '@Ibexa\Core\Persistence\Cache\ContentLanguageHandler' + - '@Ibexa\Core\Persistence\Cache\ContentTypeHandler' + - '@Ibexa\Core\Persistence\Cache\UserHandler' + - '@Ibexa\Core\Persistence\Cache\TransactionHandler' + - '@Ibexa\Core\Persistence\Cache\TrashHandler' + - '@Ibexa\Core\Persistence\Cache\UrlAliasHandler' + - '@Ibexa\Core\Persistence\Cache\ObjectStateHandler' + - '@Ibexa\Core\Persistence\Cache\URLHandler' + - '@Ibexa\Core\Persistence\Cache\BookmarkHandler' + - '@Ibexa\Core\Persistence\Cache\NotificationHandler' + - '@Ibexa\Core\Persistence\Cache\UserPreferenceHandler' + - '@Ibexa\Core\Persistence\Cache\UrlWildcardHandler' + - '@Ibexa\Core\Persistence\Cache\SettingHandler' + - '@Ibexa\Core\Persistence\Cache\PersistenceLogger' diff --git a/src/lib/Resources/settings/storage_engines/common.yml b/src/lib/Resources/settings/storage_engines/common.yml new file mode 100644 index 0000000000..161f746ce2 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/common.yml @@ -0,0 +1,70 @@ +parameters: + # Using definition files: + + # Transformation rules resources + ibexa.api.storage_engine.transformation_rules.resources: + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/ascii.tr" + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/basic.tr" + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/cyrillic.tr" + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/greek.tr" + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/hebrew.tr" + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/latin.tr" + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/search.tr" + + # Using preprocessed files: + + ibexa.api.storage_engine.preprocessed_transformation_rules.resources: + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/ascii.tr.result" + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/basic.tr.result" + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/cyrillic.tr.result" + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/greek.tr.result" + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/hebrew.tr.result" + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/latin.tr.result" + - "%ibexa.kernel.root_dir%/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/search.tr.result" + +services: + Ibexa\Core\Persistence\FieldTypeRegistry: + class: Ibexa\Core\Persistence\FieldTypeRegistry + + Ibexa\Core\Persistence\Legacy\Content\StorageRegistry: + class: Ibexa\Core\Persistence\Legacy\Content\StorageRegistry + + Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter: + class: Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter + arguments: + - '@Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased' + - [] + + Ibexa\Core\Persistence\TransformationProcessor\DefinitionBased\Parser: + class: Ibexa\Core\Persistence\TransformationProcessor\DefinitionBased\Parser + + Ibexa\Core\Persistence\TransformationProcessor\PcreCompiler: + class: Ibexa\Core\Persistence\TransformationProcessor\PcreCompiler + arguments: ['@Ibexa\Core\Persistence\Utf8Converter'] + + Ibexa\Core\Persistence\Utf8Converter: + class: Ibexa\Core\Persistence\Utf8Converter + + Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased: + class: Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased + arguments: + # Using definition files: + # + # - '@Ibexa\Core\Persistence\TransformationProcessor\DefinitionBased\Parser' + # - '@Ibexa\Core\Persistence\TransformationProcessor\PcreCompiler' + # - '%ibexa.api.storage_engine.transformation_rules.resources%' + + # Using preprocessed files: + - '@Ibexa\Core\Persistence\TransformationProcessor\PcreCompiler' + - '%ibexa.api.storage_engine.preprocessed_transformation_rules.resources%' + + ibexa.persistence.connection: + public: true # @todo should be private + alias: ibexa.api.storage_engine.legacy.connection + + ibexa.repository.transaction_handler: + alias: Ibexa\Core\Persistence\Cache\TransactionHandler + + Ibexa\Contracts\Core\Persistence\TransactionHandler: + alias: ibexa.repository.transaction_handler + diff --git a/src/lib/Resources/settings/storage_engines/legacy.yml b/src/lib/Resources/settings/storage_engines/legacy.yml new file mode 100644 index 0000000000..3942b9e426 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy.yml @@ -0,0 +1,57 @@ +imports: + - {resource: storage_engines/legacy/bookmark.yml} + - {resource: storage_engines/legacy/content.yml} + - {resource: storage_engines/legacy/content_type.yml} + - {resource: storage_engines/legacy/external_storage_gateways.yml} + - {resource: storage_engines/legacy/field_value_converters.yml} + - {resource: storage_engines/legacy/language.yml} + - {resource: storage_engines/legacy/location.yml} + - {resource: storage_engines/legacy/object_state.yml} + - {resource: storage_engines/legacy/filter.yaml} + - {resource: storage_engines/legacy/section.yml} + - {resource: storage_engines/legacy/shared_gateway.yaml} + - {resource: storage_engines/legacy/trash.yml} + - {resource: storage_engines/legacy/url_alias.yml} + - {resource: storage_engines/legacy/url_wildcard.yml} + - {resource: storage_engines/legacy/url.yml} + - {resource: storage_engines/legacy/url_criterion_handlers.yml} + - {resource: storage_engines/legacy/url_wildcard_criterion_handlers.yml} + - {resource: storage_engines/legacy/user.yml} + - {resource: storage_engines/legacy/notification.yml} + - {resource: storage_engines/legacy/user_preference.yml} + - {resource: storage_engines/legacy/setting.yml} + - {resource: storage_engines/legacy/token.yml} + +services: + Ibexa\Core\Persistence\Legacy\Handler: + class: Ibexa\Core\Persistence\Legacy\Handler + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\Handler' + - '@ibexa.spi.persistence.legacy.content_type.handler' + - '@ibexa.spi.persistence.legacy.language.handler' + - '@Ibexa\Core\Persistence\Legacy\Content\Location\Handler' + - '@Ibexa\Core\Persistence\Legacy\Content\ObjectState\Handler' + - '@Ibexa\Core\Persistence\Legacy\Content\Section\Handler' + - '@Ibexa\Core\Persistence\Legacy\TransactionHandler' + - '@Ibexa\Core\Persistence\Legacy\Content\Location\Trash\Handler' + - '@Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Handler' + - '@Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Handler' + - '@Ibexa\Core\Persistence\Legacy\User\Handler' + - '@Ibexa\Core\Persistence\Legacy\URL\Handler' + - '@ibexa.spi.persistence.legacy.bookmark.handler' + - '@ibexa.spi.persistence.legacy.notification.handler' + - '@ibexa.spi.persistence.legacy.user_preference.handler' + - '@Ibexa\Core\Persistence\Legacy\Setting\Handler' + tags: + - {name: ibexa.storage, alias: legacy} + lazy: true + public: true # @todo should be private + + ibexa.api.storage_engine.legacy.connection: '@ibexa.persistence.connection' + + Ibexa\Core\Persistence\Legacy\TransactionHandler: + class: Ibexa\Core\Persistence\Legacy\TransactionHandler + arguments: + $connection: '@ibexa.api.storage_engine.legacy.connection' + $contentTypeHandler: '@Ibexa\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler' + $languageHandler: '@Ibexa\Core\Persistence\Legacy\Content\Language\CachingHandler' diff --git a/src/lib/Resources/settings/storage_engines/legacy/bookmark.yml b/src/lib/Resources/settings/storage_engines/legacy/bookmark.yml new file mode 100644 index 0000000000..b5d5c79902 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/bookmark.yml @@ -0,0 +1,19 @@ +services: + Ibexa\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase: + arguments: + $connection: '@ibexa.persistence.connection' + + Ibexa\Core\Persistence\Legacy\Bookmark\Gateway\ExceptionConversion: + arguments: + $innerGateway: '@Ibexa\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase' + + Ibexa\Core\Persistence\Legacy\Bookmark\Mapper: ~ + + Ibexa\Core\Persistence\Legacy\Bookmark\Handler: + arguments: + $gateway: '@Ibexa\Core\Persistence\Legacy\Bookmark\Gateway\ExceptionConversion' + $mapper: '@Ibexa\Core\Persistence\Legacy\Bookmark\Mapper' + lazy: true + + ibexa.spi.persistence.legacy.bookmark.handler: + alias: 'Ibexa\Core\Persistence\Legacy\Bookmark\Handler' diff --git a/src/lib/Resources/settings/storage_engines/legacy/content.yml b/src/lib/Resources/settings/storage_engines/legacy/content.yml new file mode 100644 index 0000000000..fa9b13d1df --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/content.yml @@ -0,0 +1,73 @@ +services: + Ibexa\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder: + class: Ibexa\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder + arguments: + $connection: '@ibexa.api.storage_engine.legacy.connection' + + Ibexa\Core\Persistence\Legacy\Content\Mapper: + class: Ibexa\Core\Persistence\Legacy\Content\Mapper + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry' + - '@ibexa.spi.persistence.legacy.language.handler' + - '@ibexa.spi.persistence.legacy.content_type.handler' + - '@Symfony\Contracts\EventDispatcher\EventDispatcherInterface' + + Ibexa\Core\Persistence\Legacy\Content\Mapper\ResolveVirtualFieldSubscriber: + arguments: + $converterRegistry: '@Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry' + $storageRegistry: '@Ibexa\Core\Persistence\Legacy\Content\StorageRegistry' + $contentGateway: '@ibexa.persistence.legacy.content.gateway' + tags: + - { name: kernel.event_subscriber } + + Ibexa\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase + arguments: + - '@ibexa.api.storage_engine.legacy.connection' + - '@Ibexa\Core\Persistence\Legacy\SharedGateway\Gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase\QueryBuilder' + - '@ibexa.spi.persistence.legacy.language.handler' + - '@Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator' + + Ibexa\Core\Persistence\Legacy\Content\Gateway\ExceptionConversion: + class: Ibexa\Core\Persistence\Legacy\Content\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.persistence.legacy.content.gateway: + alias: Ibexa\Core\Persistence\Legacy\Content\Gateway\ExceptionConversion + + Ibexa\Core\Persistence\Legacy\Content\FieldHandler: + class: Ibexa\Core\Persistence\Legacy\Content\FieldHandler + arguments: + - '@ibexa.persistence.legacy.content.gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\Mapper' + - '@Ibexa\Core\Persistence\Legacy\Content\StorageHandler' + - '@ibexa.spi.persistence.legacy.language.handler' + - '@Ibexa\Core\Persistence\FieldTypeRegistry' + lazy: true + + Ibexa\Core\Persistence\Legacy\Content\TreeHandler: + class: Ibexa\Core\Persistence\Legacy\Content\TreeHandler + arguments: + - '@ibexa.persistence.legacy.location.gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\Location\Mapper' + - '@ibexa.persistence.legacy.content.gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\Mapper' + - '@Ibexa\Core\Persistence\Legacy\Content\FieldHandler' + + Ibexa\Core\Persistence\Legacy\Content\Handler: + class: Ibexa\Core\Persistence\Legacy\Content\Handler + arguments: + - '@ibexa.persistence.legacy.content.gateway' + - '@ibexa.persistence.legacy.location.gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\Mapper' + - '@Ibexa\Core\Persistence\Legacy\Content\FieldHandler' + - '@Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter' + - '@ibexa.persistence.legacy.url_alias.gateway' + - '@ibexa.spi.persistence.legacy.content_type.handler' + - '@Ibexa\Core\Persistence\Legacy\Content\TreeHandler' + - '@ibexa.spi.persistence.legacy.language.handler' + - "@logger" + lazy: true diff --git a/src/lib/Resources/settings/storage_engines/legacy/content_type.yml b/src/lib/Resources/settings/storage_engines/legacy/content_type.yml new file mode 100644 index 0000000000..a4c2d40a77 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/content_type.yml @@ -0,0 +1,77 @@ +services: + Ibexa\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase + arguments: + - '@ibexa.api.storage_engine.legacy.connection' + - '@Ibexa\Core\Persistence\Legacy\SharedGateway\Gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator' + + Ibexa\Core\Persistence\Legacy\Content\Type\Gateway\ExceptionConversion: + class: Ibexa\Core\Persistence\Legacy\Content\Type\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.persistence.legacy.content_type.gateway: + alias: Ibexa\Core\Persistence\Legacy\Content\Type\Gateway\ExceptionConversion + + Ibexa\Core\Persistence\Legacy\Content\Type\Mapper: + class: Ibexa\Core\Persistence\Legacy\Content\Type\Mapper + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry' + - '@Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator' + - '@Ibexa\Core\Persistence\Legacy\Content\Type\StorageDispatcherInterface' + + Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater: + class: Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater + arguments: + - '@ibexa.persistence.legacy.content.gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry' + - '@Ibexa\Core\Persistence\Legacy\Content\StorageHandler' + - '@Ibexa\Core\Persistence\Legacy\Content\Mapper' + + Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler: + abstract: true + class: Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler + + Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase: + parent: Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler + class: Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase + arguments: + - '@ibexa.persistence.legacy.content_type.gateway' + + ibexa.persistence.legacy.content_type.update_handler: + alias: Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase + + Ibexa\Core\Persistence\Legacy\Content\Type\Handler.inner: + class: Ibexa\Core\Persistence\Legacy\Content\Type\Handler + arguments: + - '@ibexa.persistence.legacy.content_type.gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\Type\Mapper' + - '@ibexa.persistence.legacy.content_type.update_handler' + - '@Ibexa\Core\Persistence\Legacy\Content\Type\StorageDispatcherInterface' + + Ibexa\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler: + class: Ibexa\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler + lazy: true + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\Type\Handler.inner' + - '@ibexa.spi.persistence.cache.inmemory' + - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface' + + ibexa.spi.persistence.legacy.content_type.handler: + alias: Ibexa\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler + + Ibexa\Core\Persistence\Legacy\Content\Type\StorageRegistryInterface: + alias: Ibexa\Core\Persistence\Legacy\Content\Type\StorageRegistry + + Ibexa\Core\Persistence\Legacy\Content\Type\StorageRegistry: + arguments: + $storages: !tagged_iterator { tag: ibexa.field_type.storage.external.constraints.handler, index_by: alias } + + Ibexa\Core\Persistence\Legacy\Content\Type\StorageDispatcherInterface: + alias: Ibexa\Core\Persistence\Legacy\Content\Type\StorageDispatcher + + Ibexa\Core\Persistence\Legacy\Content\Type\StorageDispatcher: + arguments: + $registry: '@Ibexa\Core\Persistence\Legacy\Content\Type\StorageRegistryInterface' diff --git a/src/lib/Resources/settings/storage_engines/legacy/external_storage_gateways.yml b/src/lib/Resources/settings/storage_engines/legacy/external_storage_gateways.yml new file mode 100644 index 0000000000..cc3fb8e3c6 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/external_storage_gateways.yml @@ -0,0 +1,38 @@ +services: + Ibexa\Core\Persistence\Legacy\Content\StorageHandler: + class: Ibexa\Core\Persistence\Legacy\Content\StorageHandler + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\StorageRegistry' + - + identifier: "LegacyStorage" + + Ibexa\Core\FieldType\BinaryFile\BinaryFileStorage\Gateway\DoctrineStorage: + class: Ibexa\Core\FieldType\BinaryFile\BinaryFileStorage\Gateway\DoctrineStorage + arguments: ['@ibexa.api.storage_engine.legacy.connection'] + + Ibexa\Core\FieldType\Keyword\KeywordStorage\Gateway\DoctrineStorage: + class: Ibexa\Core\FieldType\Keyword\KeywordStorage\Gateway\DoctrineStorage + arguments: ['@ibexa.api.storage_engine.legacy.connection'] + + Ibexa\Core\FieldType\Media\MediaStorage\Gateway\DoctrineStorage: + class: Ibexa\Core\FieldType\Media\MediaStorage\Gateway\DoctrineStorage + arguments: ['@ibexa.api.storage_engine.legacy.connection'] + + Ibexa\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage: + class: Ibexa\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage + arguments: ['@ibexa.api.storage_engine.legacy.connection'] + + Ibexa\Core\FieldType\Image\ImageStorage\Gateway\DoctrineStorage: + class: Ibexa\Core\FieldType\Image\ImageStorage\Gateway\DoctrineStorage + arguments: + - '@Ibexa\Core\IO\UrlRedecorator' + - '@ibexa.api.storage_engine.legacy.connection' + + Ibexa\Core\FieldType\MapLocation\MapLocationStorage\Gateway\DoctrineStorage: + class: Ibexa\Core\FieldType\MapLocation\MapLocationStorage\Gateway\DoctrineStorage + arguments: ['@ibexa.api.storage_engine.legacy.connection'] + + Ibexa\Core\FieldType\User\UserStorage\Gateway\DoctrineStorage: + class: Ibexa\Core\FieldType\User\UserStorage\Gateway\DoctrineStorage + arguments: + - '@ibexa.api.storage_engine.legacy.connection' diff --git a/src/lib/Resources/settings/storage_engines/legacy/field_value_converters.yml b/src/lib/Resources/settings/storage_engines/legacy/field_value_converters.yml new file mode 100644 index 0000000000..3298d32e09 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/field_value_converters.yml @@ -0,0 +1,220 @@ +services: + # Note: converter services tagged with 'ibexa.field_type.storage.legacy.converter' + # are registered to this one using compilation pass and factory + Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry + lazy: true + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\SerializableConverter: + arguments: + $serializer: '@Ibexa\Contracts\Core\FieldType\ValueSerializerInterface' + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezauthor} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\BinaryFileConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\BinaryFileConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezbinaryfile} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezboolean} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezcountry} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezdatetime} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\FloatConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\FloatConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezfloat} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\IntegerConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\IntegerConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezinteger} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezkeyword} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\MediaConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\MediaConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezmedia} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezselection} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezstring} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: eztext} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezurl} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageConverter + arguments: + - '@Ibexa\Core\FieldType\Image\IO\Legacy' + - '@Ibexa\Core\IO\UrlRedecorator' + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezimage} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\ISBNConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\ISBNConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezisbn} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\MapLocationConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\MapLocationConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezgmaplocation} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\EmailAddressConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\EmailAddressConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezemail} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezobjectrelation} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationListConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationListConverter + arguments: + $connection: '@ibexa.persistence.connection' + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezobjectrelationlist} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\UserConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\UserConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezuser} + + # Not implemented converters + # Configured to use the Null converter which does not nothing + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezdate} + + ibexa.field_type.ezenum.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezenum} + + ibexa.field_type.ezidentifier.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezidentifier} + + ibexa.field_type.ezinisetting.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezinisetting} + + ibexa.field_type.ezmatrix.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezmatrix} + + ibexa.field_type.ezmultioption.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezmultioption} + + ibexa.field_type.ezmultioption2.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezmultioption2} + + ibexa.field_type.ezmultiprice.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezmultiprice} + + ibexa.field_type.ezoption.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezoption} + + ibexa.field_type.ezpackage.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezpackage} + + ibexa.field_type.ezproductcategory.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezproductcategory} + + ibexa.field_type.ezrangeoption.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezrangeoption} + + ibexa.field_type.ezsubtreesubscription.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezsubtreesubscription} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: eztime} + + # not implemented converters from extensions + ibexa.field_type.ezcomcomments.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezcomcomments} + + ibexa.field_type.ezpaex.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezpaex} + + ibexa.field_type.ezsurvey.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezsurvey} + + ibexa.field_type.eztags.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: eztags} + + ibexa.field_type.ezrecommendation.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezrecommendation} + + Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageAssetConverter: + tags: + - {name: ibexa.field_type.storage.legacy.converter, alias: ezimageasset} + + ibexa.field_type.ezimageasset.converter: + alias: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageAssetConverter diff --git a/src/lib/Resources/settings/storage_engines/legacy/filter.yaml b/src/lib/Resources/settings/storage_engines/legacy/filter.yaml new file mode 100644 index 0000000000..4ac9426423 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/filter.yaml @@ -0,0 +1,58 @@ +imports: + - { resource: filter/query_builders.yaml } + +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + # injectables: + Ibexa\Contracts\Core\Persistence\Filter\Content\Handler: + alias: Ibexa\Core\Persistence\Legacy\Filter\Handler\ContentFilteringHandler + + Ibexa\Contracts\Core\Persistence\Filter\Location\Handler: + alias: Ibexa\Core\Persistence\Legacy\Filter\Handler\LocationFilteringHandler + + Ibexa\Core\Persistence\Legacy\Filter\Gateway\Content\GatewayDataMapper: + alias: Ibexa\Core\Persistence\Legacy\Filter\Gateway\Content\Mapper\DoctrineGatewayDataMapper + + Ibexa\Contracts\Core\Persistence\Filter\CriterionVisitor: + alias: Ibexa\Core\Persistence\Legacy\Filter\CriterionVisitor + + Ibexa\Contracts\Core\Persistence\Filter\SortClauseVisitor: + alias: Ibexa\Core\Persistence\Legacy\Filter\SortClauseVisitor + + # implementations: + Ibexa\Core\Persistence\Legacy\Filter\Gateway\Content\Mapper\DoctrineGatewayDataMapper: + arguments: + $languageHandler: '@Ibexa\Contracts\Core\Persistence\Content\Language\Handler' + $languageMaskGenerator: '@Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator' + $contentTypeHandler: '@Ibexa\Contracts\Core\Persistence\Content\Type\Handler' + $converterRegistry: '@Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry' + + Ibexa\Core\Persistence\Legacy\Filter\CriterionVisitor: + arguments: + $criterionQueryBuilders: !tagged_iterator ibexa.filter.criterion.query.builder + + Ibexa\Core\Persistence\Legacy\Filter\SortClauseVisitor: + arguments: + $sortClauseQueryBuilders: !tagged_iterator ibexa.filter.sort_clause.query.builder + + Ibexa\Core\Persistence\Legacy\Filter\Gateway\Content\Doctrine\DoctrineGateway: + arguments: + $connection: '@ibexa.persistence.connection' + + Ibexa\Core\Persistence\Legacy\Filter\Handler\ContentFilteringHandler: + arguments: + $gateway: '@Ibexa\Core\Persistence\Legacy\Filter\Gateway\Content\Doctrine\DoctrineGateway' + $fieldHandler: '@Ibexa\Core\Persistence\Legacy\Content\FieldHandler' + + Ibexa\Core\Persistence\Legacy\Filter\Gateway\Location\Doctrine\DoctrineGateway: + arguments: + $connection: '@ibexa.persistence.connection' + + Ibexa\Core\Persistence\Legacy\Filter\Handler\LocationFilteringHandler: + arguments: + $gateway: '@Ibexa\Core\Persistence\Legacy\Filter\Gateway\Location\Doctrine\DoctrineGateway' + $locationMapper: '@Ibexa\Core\Persistence\Legacy\Content\Location\Mapper' diff --git a/src/lib/Resources/settings/storage_engines/legacy/filter/query_builders.yaml b/src/lib/Resources/settings/storage_engines/legacy/filter/query_builders.yaml new file mode 100644 index 0000000000..4b102cf11d --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/filter/query_builders.yaml @@ -0,0 +1,14 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + bind: + $transformationProcessor: '@Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased' + + Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\: + resource: '../../../../../Persistence/Legacy/Filter/CriterionQueryBuilder/*' + + Ibexa\Core\Persistence\Legacy\Filter\SortClauseQueryBuilder\: + resource: '../../../../../Persistence/Legacy/Filter/SortClauseQueryBuilder/*' + diff --git a/src/lib/Resources/settings/storage_engines/legacy/language.yml b/src/lib/Resources/settings/storage_engines/legacy/language.yml new file mode 100644 index 0000000000..711cafdbc4 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/language.yml @@ -0,0 +1,38 @@ +services: + Ibexa\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase + arguments: + - '@ibexa.api.storage_engine.legacy.connection' + + Ibexa\Core\Persistence\Legacy\Content\Language\Gateway\ExceptionConversion: + class: Ibexa\Core\Persistence\Legacy\Content\Language\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.persistence.legacy.language.gateway: + alias: Ibexa\Core\Persistence\Legacy\Content\Language\Gateway\ExceptionConversion + + Ibexa\Core\Persistence\Legacy\Content\Language\Mapper: + class: Ibexa\Core\Persistence\Legacy\Content\Language\Mapper + + Ibexa\Core\Persistence\Legacy\Content\Language\Handler.inner: + class: Ibexa\Core\Persistence\Legacy\Content\Language\Handler + arguments: + - '@ibexa.persistence.legacy.language.gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\Language\Mapper' + + Ibexa\Core\Persistence\Legacy\Content\Language\CachingHandler: + class: Ibexa\Core\Persistence\Legacy\Content\Language\CachingHandler + lazy: true + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\Language\Handler.inner' + - '@ibexa.spi.persistence.cache.inmemory' + - '@Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface' + + ibexa.spi.persistence.legacy.language.handler: + alias: Ibexa\Core\Persistence\Legacy\Content\Language\CachingHandler + + Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator: + class: Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator + arguments: ['@ibexa.spi.persistence.legacy.language.handler'] diff --git a/src/lib/Resources/settings/storage_engines/legacy/location.yml b/src/lib/Resources/settings/storage_engines/legacy/location.yml new file mode 100644 index 0000000000..3b0ba0cde5 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/location.yml @@ -0,0 +1,30 @@ +services: + Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase + arguments: + - '@ibexa.api.storage_engine.legacy.connection' + - '@Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator' + - '@ibexa.core.trash.search.legacy.gateway.criteria_converter' + - '@ibexa.core.trash.search.legacy.gateway.sort_clause_converter' + + Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\ExceptionConversion: + class: Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.persistence.legacy.location.gateway: + alias: Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\ExceptionConversion + + Ibexa\Core\Persistence\Legacy\Content\Location\Mapper: + class: Ibexa\Core\Persistence\Legacy\Content\Location\Mapper + + Ibexa\Core\Persistence\Legacy\Content\Location\Handler: + class: Ibexa\Core\Persistence\Legacy\Content\Location\Handler + arguments: + - '@ibexa.persistence.legacy.location.gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\Location\Mapper' + - '@Ibexa\Core\Persistence\Legacy\Content\Handler' + - '@Ibexa\Core\Persistence\Legacy\Content\ObjectState\Handler' + - '@Ibexa\Core\Persistence\Legacy\Content\TreeHandler' + lazy: true diff --git a/src/lib/Resources/settings/storage_engines/legacy/notification.yml b/src/lib/Resources/settings/storage_engines/legacy/notification.yml new file mode 100644 index 0000000000..3a3616f271 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/notification.yml @@ -0,0 +1,19 @@ +services: + Ibexa\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase: + arguments: + $connection: '@ibexa.persistence.connection' + + Ibexa\Core\Persistence\Legacy\Notification\Gateway\ExceptionConversion: + arguments: + $innerGateway: '@Ibexa\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase' + + Ibexa\Core\Persistence\Legacy\Notification\Mapper: ~ + + Ibexa\Core\Persistence\Legacy\Notification\Handler: + arguments: + $gateway: '@Ibexa\Core\Persistence\Legacy\Notification\Gateway\ExceptionConversion' + $mapper: '@Ibexa\Core\Persistence\Legacy\Notification\Mapper' + lazy: true + + ibexa.spi.persistence.legacy.notification.handler: + alias: 'Ibexa\Core\Persistence\Legacy\Notification\Handler' diff --git a/src/lib/Resources/settings/storage_engines/legacy/object_state.yml b/src/lib/Resources/settings/storage_engines/legacy/object_state.yml new file mode 100644 index 0000000000..48de1bfe90 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/object_state.yml @@ -0,0 +1,27 @@ +services: + Ibexa\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase + arguments: + - '@ibexa.api.storage_engine.legacy.connection' + - '@Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator' + + Ibexa\Core\Persistence\Legacy\Content\ObjectState\Gateway\ExceptionConversion: + class: Ibexa\Core\Persistence\Legacy\Content\ObjectState\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.persistence.legacy.object_state.gateway: + alias: Ibexa\Core\Persistence\Legacy\Content\ObjectState\Gateway\ExceptionConversion + + Ibexa\Core\Persistence\Legacy\Content\ObjectState\Mapper: + class: Ibexa\Core\Persistence\Legacy\Content\ObjectState\Mapper + arguments: + - '@ibexa.spi.persistence.legacy.language.handler' + + Ibexa\Core\Persistence\Legacy\Content\ObjectState\Handler: + class: Ibexa\Core\Persistence\Legacy\Content\ObjectState\Handler + arguments: + - '@ibexa.persistence.legacy.object_state.gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\ObjectState\Mapper' + lazy: true diff --git a/src/lib/Resources/settings/storage_engines/legacy/section.yml b/src/lib/Resources/settings/storage_engines/legacy/section.yml new file mode 100644 index 0000000000..ae1dff47a5 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/section.yml @@ -0,0 +1,20 @@ +services: + Ibexa\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase + arguments: + - '@ibexa.api.storage_engine.legacy.connection' + + Ibexa\Core\Persistence\Legacy\Content\Section\Gateway\ExceptionConversion: + class: Ibexa\Core\Persistence\Legacy\Content\Section\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.persistence.legacy.section.gateway: + alias: Ibexa\Core\Persistence\Legacy\Content\Section\Gateway\ExceptionConversion + + Ibexa\Core\Persistence\Legacy\Content\Section\Handler: + class: Ibexa\Core\Persistence\Legacy\Content\Section\Handler + arguments: + - '@ibexa.persistence.legacy.section.gateway' + lazy: true diff --git a/src/lib/Resources/settings/storage_engines/legacy/setting.yml b/src/lib/Resources/settings/storage_engines/legacy/setting.yml new file mode 100644 index 0000000000..6f87a2168b --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/setting.yml @@ -0,0 +1,20 @@ +services: + Ibexa\Core\Persistence\Legacy\Setting\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Persistence\Legacy\Setting\Gateway\DoctrineDatabase + arguments: + - '@ibexa.api.storage_engine.legacy.connection' + + Ibexa\Core\Persistence\Legacy\Setting\Gateway\ExceptionConversion: + class: Ibexa\Core\Persistence\Legacy\Setting\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Persistence\Legacy\Setting\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.persistence.legacy.setting.gateway: + alias: Ibexa\Core\Persistence\Legacy\Setting\Gateway\ExceptionConversion + + Ibexa\Core\Persistence\Legacy\Setting\Handler: + class: Ibexa\Core\Persistence\Legacy\Setting\Handler + arguments: + - '@ibexa.persistence.legacy.setting.gateway' + lazy: true diff --git a/src/lib/Resources/settings/storage_engines/legacy/shared_gateway.yaml b/src/lib/Resources/settings/storage_engines/legacy/shared_gateway.yaml new file mode 100644 index 0000000000..5fdbe8c133 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/shared_gateway.yaml @@ -0,0 +1,21 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + bind: + $connection: '@ibexa.persistence.connection' + + Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\FallbackGateway: ~ + + Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\SqliteGateway: + tags: + - { name: ibexa.storage.legacy.gateway.shared, platform: sqlite } + + Ibexa\Core\Persistence\Legacy\SharedGateway\GatewayFactory: + arguments: + $fallbackGateway: '@Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\FallbackGateway' + $gateways: !tagged_iterator { tag: ibexa.storage.legacy.gateway.shared, index_by: platform } + + Ibexa\Core\Persistence\Legacy\SharedGateway\Gateway: + factory: ['@Ibexa\Core\Persistence\Legacy\SharedGateway\GatewayFactory', 'buildSharedGateway'] diff --git a/src/lib/Resources/settings/storage_engines/legacy/token.yml b/src/lib/Resources/settings/storage_engines/legacy/token.yml new file mode 100644 index 0000000000..11ace00162 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/token.yml @@ -0,0 +1,25 @@ +services: + _defaults: + autowire: true + autoconfigure: true + public: false + + Ibexa\Core\Persistence\Legacy\Token\Gateway\Token\Doctrine\DoctrineGateway: + arguments: + $connection: '@ibexa.persistence.connection' + + Ibexa\Core\Persistence\Legacy\Token\Gateway\Token\Gateway: + alias: Ibexa\Core\Persistence\Legacy\Token\Gateway\Token\Doctrine\DoctrineGateway + + Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Doctrine\DoctrineGateway: + arguments: + $connection: '@ibexa.persistence.connection' + + Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Gateway: + alias: Ibexa\Core\Persistence\Legacy\Token\Gateway\TokenType\Doctrine\DoctrineGateway + + Ibexa\Core\Persistence\Legacy\Token\Mapper: ~ + + Ibexa\Core\Persistence\Legacy\Token\Handler: ~ + + Ibexa\Contracts\Core\Persistence\Token\Handler: '@Ibexa\Core\Persistence\Legacy\Token\Handler' diff --git a/src/lib/Resources/settings/storage_engines/legacy/trash.yml b/src/lib/Resources/settings/storage_engines/legacy/trash.yml new file mode 100644 index 0000000000..02be99bc24 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/trash.yml @@ -0,0 +1,18 @@ +services: + Ibexa\Core\Persistence\Legacy\Content\Location\Trash\Handler: + class: Ibexa\Core\Persistence\Legacy\Content\Location\Trash\Handler + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\Location\Handler' + - '@Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\ExceptionConversion' + - '@Ibexa\Core\Persistence\Legacy\Content\Location\Mapper' + - '@Ibexa\Core\Persistence\Legacy\Content\Handler' + lazy: true + + # reusing parts of LSE + ibexa.core.trash.search.legacy.gateway.criteria_converter: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter + lazy: true + + ibexa.core.trash.search.legacy.gateway.sort_clause_converter: + class: Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter + lazy: true diff --git a/src/lib/Resources/settings/storage_engines/legacy/url.yml b/src/lib/Resources/settings/storage_engines/legacy/url.yml new file mode 100644 index 0000000000..93310b4380 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/url.yml @@ -0,0 +1,30 @@ +services: + Ibexa\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase + arguments: + - '@ibexa.persistence.connection' + - '@Ibexa\Core\Persistence\Legacy\URL\Query\CriteriaConverter' + + Ibexa\Core\Persistence\Legacy\URL\Gateway\ExceptionConversion: + class: Ibexa\Core\Persistence\Legacy\URL\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.persistence.legacy.url.gateway: + alias: Ibexa\Core\Persistence\Legacy\URL\Gateway\ExceptionConversion + + Ibexa\Core\Persistence\Legacy\URL\Mapper: + class: Ibexa\Core\Persistence\Legacy\URL\Mapper + + Ibexa\Core\Persistence\Legacy\URL\Handler: + class: Ibexa\Core\Persistence\Legacy\URL\Handler + arguments: + - '@ibexa.persistence.legacy.url.gateway' + - '@Ibexa\Core\Persistence\Legacy\URL\Mapper' + lazy: true + + Ibexa\Core\Persistence\Legacy\URL\Query\CriteriaConverter: + class: Ibexa\Core\Persistence\Legacy\URL\Query\CriteriaConverter + + diff --git a/src/lib/Resources/settings/storage_engines/legacy/url_alias.yml b/src/lib/Resources/settings/storage_engines/legacy/url_alias.yml new file mode 100644 index 0000000000..1e4e4f7571 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/url_alias.yml @@ -0,0 +1,33 @@ +services: + Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase + arguments: + - '@ibexa.api.storage_engine.legacy.connection' + - '@Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator' + + Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway\ExceptionConversion: + class: Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.persistence.legacy.url_alias.gateway: + alias: Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway\ExceptionConversion + + Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Mapper: + class: Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Mapper + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator' + + Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Handler: + class: Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Handler + arguments: + - '@ibexa.persistence.legacy.url_alias.gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Mapper' + - '@ibexa.persistence.legacy.location.gateway' + - '@ibexa.spi.persistence.legacy.language.handler' + - '@Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter' + - '@ibexa.persistence.legacy.content.gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator' + - '@Ibexa\Core\Persistence\Legacy\TransactionHandler' + lazy: true diff --git a/src/lib/Resources/settings/storage_engines/legacy/url_criterion_handlers.yml b/src/lib/Resources/settings/storage_engines/legacy/url_criterion_handlers.yml new file mode 100644 index 0000000000..47afc81db8 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/url_criterion_handlers.yml @@ -0,0 +1,63 @@ +services: + ibexa.persistence.legacy.url.criterion_handler.base: + abstract: true + + Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalAnd: + parent: ibexa.persistence.legacy.url.criterion_handler.base + class: Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalAnd + tags: + - { name: ibexa.storage.legacy.url.criterion.handler } + + Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalOr: + parent: ibexa.persistence.legacy.url.criterion_handler.base + class: Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalOr + tags: + - { name: ibexa.storage.legacy.url.criterion.handler } + + Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalNot: + parent: ibexa.persistence.legacy.url.criterion_handler.base + class: Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalNot + tags: + - { name: ibexa.storage.legacy.url.criterion.handler } + + Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\MatchAll: + parent: ibexa.persistence.legacy.url.criterion_handler.base + class: Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\MatchAll + tags: + - { name: ibexa.storage.legacy.url.criterion.handler } + + Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\MatchNone: + parent: ibexa.persistence.legacy.url.criterion_handler.base + class: Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\MatchNone + tags: + - { name: ibexa.storage.legacy.url.criterion.handler } + + Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\Validity: + parent: ibexa.persistence.legacy.url.criterion_handler.base + class: Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\Validity + tags: + - { name: ibexa.storage.legacy.url.criterion.handler } + + Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\Pattern: + parent: ibexa.persistence.legacy.url.criterion_handler.base + class: Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\Pattern + tags: + - { name: ibexa.storage.legacy.url.criterion.handler } + + Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\VisibleOnly: + parent: ibexa.persistence.legacy.url.criterion_handler.base + class: Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\VisibleOnly + tags: + - { name: ibexa.storage.legacy.url.criterion.handler } + + Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\SectionId: + parent: ibexa.persistence.legacy.url.criterion_handler.base + class: Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\SectionId + tags: + - { name: ibexa.storage.legacy.url.criterion.handler } + + Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\SectionIdentifier: + parent: ibexa.persistence.legacy.url.criterion_handler.base + class: Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\SectionIdentifier + tags: + - { name: ibexa.storage.legacy.url.criterion.handler } diff --git a/src/lib/Resources/settings/storage_engines/legacy/url_wildcard.yml b/src/lib/Resources/settings/storage_engines/legacy/url_wildcard.yml new file mode 100644 index 0000000000..2e9261a17d --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/url_wildcard.yml @@ -0,0 +1,29 @@ +services: + Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase + arguments: + - '@ibexa.api.storage_engine.legacy.connection' + - '@Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriteriaConverter' + + Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\ExceptionConversion: + class: Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.persistence.legacy.url_wildcard.gateway: + alias: Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\ExceptionConversion + + Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Mapper: + class: Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Mapper + + Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Handler: + class: Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Handler + arguments: + - '@ibexa.persistence.legacy.url_wildcard.gateway' + - '@Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Mapper' + lazy: true + + Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriteriaConverter: + arguments: + $handlers: !tagged_iterator ibexa.storage.legacy.url_wildcard.criterion.handler diff --git a/eZ/Publish/Core/settings/storage_engines/legacy/url_wildcard_criterion_handlers.yml b/src/lib/Resources/settings/storage_engines/legacy/url_wildcard_criterion_handlers.yml similarity index 100% rename from eZ/Publish/Core/settings/storage_engines/legacy/url_wildcard_criterion_handlers.yml rename to src/lib/Resources/settings/storage_engines/legacy/url_wildcard_criterion_handlers.yml diff --git a/src/lib/Resources/settings/storage_engines/legacy/user.yml b/src/lib/Resources/settings/storage_engines/legacy/user.yml new file mode 100644 index 0000000000..7d8c1c4d66 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/user.yml @@ -0,0 +1,57 @@ +services: + Ibexa\Core\Persistence\Legacy\User\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Persistence\Legacy\User\Gateway\DoctrineDatabase + arguments: + - '@ibexa.api.storage_engine.legacy.connection' + + Ibexa\Core\Persistence\Legacy\User\Gateway\ExceptionConversion: + class: Ibexa\Core\Persistence\Legacy\User\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Persistence\Legacy\User\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.persistence.legacy.user.gateway: + alias: Ibexa\Core\Persistence\Legacy\User\Gateway\ExceptionConversion + + Ibexa\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase.inner: + class: Ibexa\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase + arguments: + - '@ibexa.api.storage_engine.legacy.connection' + + Ibexa\Core\Persistence\Legacy\User\Role\Gateway\ExceptionConversion: + class: Ibexa\Core\Persistence\Legacy\User\Role\Gateway\ExceptionConversion + arguments: + - '@Ibexa\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase.inner' + + # To disable exception conversion layer override this alias so that it points to inner gateway + ibexa.persistence.legacy.role.gateway: + alias: Ibexa\Core\Persistence\Legacy\User\Role\Gateway\ExceptionConversion + + Ibexa\Core\Persistence\Legacy\User\Mapper: + class: Ibexa\Core\Persistence\Legacy\User\Mapper + + Ibexa\Core\Persistence\Legacy\User\Role\LimitationHandler: + abstract: true + class: Ibexa\Core\Persistence\Legacy\User\Role\LimitationHandler + arguments: + $connection: '@ibexa.api.storage_engine.legacy.connection' + + Ibexa\Core\Persistence\Legacy\User\Role\LimitationHandler\ObjectStateHandler: + parent: Ibexa\Core\Persistence\Legacy\User\Role\LimitationHandler + class: Ibexa\Core\Persistence\Legacy\User\Role\LimitationHandler\ObjectStateHandler + tags: + - {name: ibexa.storage.legacy.role.limitation.handler} + + # Note: services tagged with 'ibexa.storage.legacy.role.limitation.handler' + # are registered to this one using compilation pass + Ibexa\Core\Persistence\Legacy\User\Role\LimitationConverter: + class: Ibexa\Core\Persistence\Legacy\User\Role\LimitationConverter + + Ibexa\Core\Persistence\Legacy\User\Handler: + class: Ibexa\Core\Persistence\Legacy\User\Handler + arguments: + - '@ibexa.persistence.legacy.user.gateway' + - '@ibexa.persistence.legacy.role.gateway' + - '@Ibexa\Core\Persistence\Legacy\User\Mapper' + - '@Ibexa\Core\Persistence\Legacy\User\Role\LimitationConverter' + lazy: true diff --git a/src/lib/Resources/settings/storage_engines/legacy/user_preference.yml b/src/lib/Resources/settings/storage_engines/legacy/user_preference.yml new file mode 100644 index 0000000000..c116243570 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/legacy/user_preference.yml @@ -0,0 +1,19 @@ +services: + Ibexa\Core\Persistence\Legacy\UserPreference\Gateway\DoctrineDatabase: + arguments: + $connection: '@ibexa.persistence.connection' + + Ibexa\Core\Persistence\Legacy\UserPreference\Gateway\ExceptionConversion: + arguments: + $innerGateway: '@Ibexa\Core\Persistence\Legacy\UserPreference\Gateway\DoctrineDatabase' + + Ibexa\Core\Persistence\Legacy\UserPreference\Mapper: ~ + + Ibexa\Core\Persistence\Legacy\UserPreference\Handler: + arguments: + $gateway: '@Ibexa\Core\Persistence\Legacy\UserPreference\Gateway\ExceptionConversion' + $mapper: '@Ibexa\Core\Persistence\Legacy\UserPreference\Mapper' + lazy: true + + ibexa.spi.persistence.legacy.user_preference.handler: + alias: 'Ibexa\Core\Persistence\Legacy\UserPreference\Handler' diff --git a/src/lib/Resources/settings/storage_engines/shortcuts.yml b/src/lib/Resources/settings/storage_engines/shortcuts.yml new file mode 100644 index 0000000000..fca3bbffe5 --- /dev/null +++ b/src/lib/Resources/settings/storage_engines/shortcuts.yml @@ -0,0 +1,48 @@ +services: + Ibexa\Contracts\Core\Persistence\Content\Handler: + class: Ibexa\Contracts\Core\Persistence\Content\Handler + factory: ['@ibexa.api.persistence_handler', contentHandler] + + Ibexa\Contracts\Core\Persistence\Content\Type\Handler: + class: Ibexa\Contracts\Core\Persistence\Content\Type\Handler + factory: ['@ibexa.api.persistence_handler', contentTypeHandler] + + Ibexa\Contracts\Core\Persistence\Content\Language\Handler: + class: Ibexa\Contracts\Core\Persistence\Content\Language\Handler + factory: ['@ibexa.api.persistence_handler', contentLanguageHandler] + + Ibexa\Contracts\Core\Persistence\Content\Location\Handler: + class: Ibexa\Contracts\Core\Persistence\Content\Location\Handler + factory: ['@ibexa.api.persistence_handler', locationHandler] + + Ibexa\Contracts\Core\Persistence\Content\ObjectState\Handler: + class: Ibexa\Contracts\Core\Persistence\Content\ObjectState\Handler + factory: ['@ibexa.api.persistence_handler', objectStateHandler] + + Ibexa\Contracts\Core\Persistence\Content\Section\Handler: + class: Ibexa\Contracts\Core\Persistence\Content\Section\Handler + factory: ['@ibexa.api.persistence_handler', sectionHandler] + + Ibexa\Contracts\Core\Persistence\Content\Location\Trash\Handler: + class: Ibexa\Contracts\Core\Persistence\Content\Location\Trash\Handler + factory: ['@ibexa.api.persistence_handler', trashHandler] + + Ibexa\Contracts\Core\Persistence\Content\UrlAlias\Handler: + class: Ibexa\Contracts\Core\Persistence\Content\UrlAlias\Handler + factory: ['@ibexa.api.persistence_handler', urlAliasHandler] + + Ibexa\Contracts\Core\Persistence\Content\UrlWildcard\Handler: + class: Ibexa\Contracts\Core\Persistence\Content\UrlWildcard\Handler + factory: ['@ibexa.api.persistence_handler', urlWildcardHandler] + + Ibexa\Contracts\Core\Persistence\User\Handler: + class: Ibexa\Contracts\Core\Persistence\User\Handler + factory: ['@ibexa.api.persistence_handler', userHandler] + + Ibexa\Contracts\Core\Persistence\Bookmark\Handler: + class: Ibexa\Contracts\Core\Persistence\Bookmark\Handler + factory: ['@ibexa.api.persistence_handler', bookmarkHandler] + + Ibexa\Contracts\Core\Persistence\UserPreference\Handler: + class: Ibexa\Contracts\Core\Persistence\UserPreference\Handler + factory: ['@ibexa.api.persistence_handler', userPreferenceHandler] diff --git a/src/lib/Resources/settings/thumbnails.yml b/src/lib/Resources/settings/thumbnails.yml new file mode 100644 index 0000000000..f7e30160c2 --- /dev/null +++ b/src/lib/Resources/settings/thumbnails.yml @@ -0,0 +1,43 @@ +services: + _defaults: + public: false + autoconfigure: true + autowire: true + + Ibexa\Core\FieldType\Image\ImageThumbnailStrategy: + arguments: + $fieldTypeIdentifier: 'ezimage' + $variationHandler: '@Ibexa\Bundle\Core\Imagine\Cache\AliasGeneratorDecorator' + $variationName: 'medium' + tags: + - { name: ibexa.repository.thumbnail.strategy.field, priority: 0 } + + Ibexa\Core\FieldType\Image\ImageThumbnailProxyStrategy: + decorates: Ibexa\Core\FieldType\Image\ImageThumbnailStrategy + arguments: + $imageThumbnailStrategy: '@.inner' + $proxyGenerator: '@Ibexa\Core\Repository\ProxyFactory\ProxyGeneratorInterface' + + Ibexa\Core\FieldType\ImageAsset\ImageAssetThumbnailStrategy: + lazy: true + arguments: + $fieldTypeIdentifier: 'ezimageasset' + $thumbnailStrategy: '@Ibexa\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy' + $contentService: '@ibexa.api.service.content' + tags: + - { name: ibexa.repository.thumbnail.strategy.field, priority: 0 } + + Ibexa\Core\Repository\Strategy\ContentThumbnail\Field\ContentFieldStrategy: + arguments: + $strategies: !tagged_iterator ibexa.repository.thumbnail.strategy.field + + Ibexa\Core\Repository\Strategy\ContentThumbnail\FirstMatchingFieldStrategy: + arguments: + $fieldTypeService: '@ibexa.api.service.field_type' + $contentFieldStrategy: '@Ibexa\Core\Repository\Strategy\ContentThumbnail\Field\ContentFieldStrategy' + tags: + - { name: ibexa.repository.thumbnail.strategy.content, priority: 0 } + + Ibexa\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy: + arguments: + $strategies: !tagged_iterator ibexa.repository.thumbnail.strategy.content diff --git a/src/lib/Resources/settings/tokens.yml b/src/lib/Resources/settings/tokens.yml new file mode 100644 index 0000000000..6160e88aad --- /dev/null +++ b/src/lib/Resources/settings/tokens.yml @@ -0,0 +1,14 @@ +services: + _defaults: + public: false + autoconfigure: true + autowire: true + + Ibexa\Core\Token\RandomBytesGenerator: ~ + + Ibexa\Core\Token\WebSafeGenerator: + arguments: + $tokenGenerator: '@Ibexa\Core\Token\RandomBytesGenerator' + + Ibexa\Contracts\Core\Token\TokenGeneratorInterface: + alias: Ibexa\Core\Token\WebSafeGenerator diff --git a/src/lib/Resources/settings/user_preference.yml b/src/lib/Resources/settings/user_preference.yml new file mode 100644 index 0000000000..6eb8433b32 --- /dev/null +++ b/src/lib/Resources/settings/user_preference.yml @@ -0,0 +1,3 @@ +services: + Ibexa\Contracts\Core\Repository\UserPreferenceService: + alias: Ibexa\Core\Event\UserPreferenceService diff --git a/src/lib/Resources/settings/utils.yml b/src/lib/Resources/settings/utils.yml new file mode 100644 index 0000000000..d3d855e7ac --- /dev/null +++ b/src/lib/Resources/settings/utils.yml @@ -0,0 +1,3 @@ +services: + Ibexa\Core\Base\Utils\DeprecationWarner: + class: Ibexa\Core\Base\Utils\DeprecationWarner diff --git a/eZ/Publish/Core/Search/Common/BackgroundIndexer.php b/src/lib/Search/Common/BackgroundIndexer.php similarity index 76% rename from eZ/Publish/Core/Search/Common/BackgroundIndexer.php rename to src/lib/Search/Common/BackgroundIndexer.php index d691434339..0fb7eb39a3 100644 --- a/eZ/Publish/Core/Search/Common/BackgroundIndexer.php +++ b/src/lib/Search/Common/BackgroundIndexer.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Common; +namespace Ibexa\Core\Search\Common; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\Location; /** * Interface for performing indexing in background. @@ -28,7 +28,7 @@ interface BackgroundIndexer * * .. then item is removed from index, if not it is added/updated. * - * @param \eZ\Publish\SPI\Persistence\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Persistence\Content\ContentInfo $contentInfo */ public function registerContent(ContentInfo $contentInfo); @@ -41,7 +41,9 @@ public function registerContent(ContentInfo $contentInfo); * * .. then item is removed from index, if not it is added/updated. * - * @param \eZ\Publish\SPI\Persistence\Content\Location $location + * @param \Ibexa\Contracts\Core\Persistence\Content\Location $location */ public function registerLocation(Location $location); } + +class_alias(BackgroundIndexer::class, 'eZ\Publish\Core\Search\Common\BackgroundIndexer'); diff --git a/src/lib/Search/Common/BackgroundIndexer/NullIndexer.php b/src/lib/Search/Common/BackgroundIndexer/NullIndexer.php new file mode 100644 index 0000000000..87c4986740 --- /dev/null +++ b/src/lib/Search/Common/BackgroundIndexer/NullIndexer.php @@ -0,0 +1,27 @@ +searchHandler->indexLocation($this->persistenceHandler->locationHandler()->load($contentInfo->mainLocationId)); } } + +class_alias(ContentEventSubscriber::class, 'eZ\Publish\Core\Search\Common\EventSubscriber\ContentEventSubscriber'); diff --git a/eZ/Publish/Core/Search/Common/EventSubscriber/LocationEventSubscriber.php b/src/lib/Search/Common/EventSubscriber/LocationEventSubscriber.php similarity index 81% rename from eZ/Publish/Core/Search/Common/EventSubscriber/LocationEventSubscriber.php rename to src/lib/Search/Common/EventSubscriber/LocationEventSubscriber.php index 2bdd5b0c3a..8970e9a930 100644 --- a/eZ/Publish/Core/Search/Common/EventSubscriber/LocationEventSubscriber.php +++ b/src/lib/Search/Common/EventSubscriber/LocationEventSubscriber.php @@ -4,18 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Common\EventSubscriber; - -use eZ\Publish\API\Repository\Events\Location\CopySubtreeEvent; -use eZ\Publish\API\Repository\Events\Location\CreateLocationEvent; -use eZ\Publish\API\Repository\Events\Location\DeleteLocationEvent; -use eZ\Publish\API\Repository\Events\Location\HideLocationEvent; -use eZ\Publish\API\Repository\Events\Location\MoveSubtreeEvent; -use eZ\Publish\API\Repository\Events\Location\SwapLocationEvent; -use eZ\Publish\API\Repository\Events\Location\UnhideLocationEvent; -use eZ\Publish\API\Repository\Events\Location\UpdateLocationEvent; -use eZ\Publish\API\Repository\Events\Section\AssignSectionToSubtreeEvent; -use eZ\Publish\API\Repository\Values\Content\Location; +namespace Ibexa\Core\Search\Common\EventSubscriber; + +use Ibexa\Contracts\Core\Repository\Events\Location\CopySubtreeEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\CreateLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\DeleteLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\HideLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\MoveSubtreeEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\SwapLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\UnhideLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\UpdateLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Section\AssignSectionToSubtreeEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class LocationEventSubscriber extends AbstractSearchEventSubscriber implements EventSubscriberInterface @@ -131,3 +131,5 @@ public function onAssignSectionToSubtree(AssignSectionToSubtreeEvent $event): vo $this->indexSubtree($event->getLocation()->id); } } + +class_alias(LocationEventSubscriber::class, 'eZ\Publish\Core\Search\Common\EventSubscriber\LocationEventSubscriber'); diff --git a/eZ/Publish/Core/Search/Common/EventSubscriber/ObjectStateEventSubscriber.php b/src/lib/Search/Common/EventSubscriber/ObjectStateEventSubscriber.php similarity index 81% rename from eZ/Publish/Core/Search/Common/EventSubscriber/ObjectStateEventSubscriber.php rename to src/lib/Search/Common/EventSubscriber/ObjectStateEventSubscriber.php index f3f16d07cc..d8ebb44443 100644 --- a/eZ/Publish/Core/Search/Common/EventSubscriber/ObjectStateEventSubscriber.php +++ b/src/lib/Search/Common/EventSubscriber/ObjectStateEventSubscriber.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Common\EventSubscriber; +namespace Ibexa\Core\Search\Common\EventSubscriber; -use eZ\Publish\API\Repository\Events\ObjectState\SetContentStateEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\SetContentStateEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class ObjectStateEventSubscriber extends AbstractSearchEventSubscriber implements EventSubscriberInterface @@ -35,3 +35,5 @@ public function onSetContentState(SetContentStateEvent $event) } } } + +class_alias(ObjectStateEventSubscriber::class, 'eZ\Publish\Core\Search\Common\EventSubscriber\ObjectStateEventSubscriber'); diff --git a/eZ/Publish/Core/Search/Common/EventSubscriber/SectionEventSubscriber.php b/src/lib/Search/Common/EventSubscriber/SectionEventSubscriber.php similarity index 78% rename from eZ/Publish/Core/Search/Common/EventSubscriber/SectionEventSubscriber.php rename to src/lib/Search/Common/EventSubscriber/SectionEventSubscriber.php index 434170631c..700bd9c25e 100644 --- a/eZ/Publish/Core/Search/Common/EventSubscriber/SectionEventSubscriber.php +++ b/src/lib/Search/Common/EventSubscriber/SectionEventSubscriber.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Common\EventSubscriber; +namespace Ibexa\Core\Search\Common\EventSubscriber; -use eZ\Publish\API\Repository\Events\Section\AssignSectionEvent; +use Ibexa\Contracts\Core\Repository\Events\Section\AssignSectionEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class SectionEventSubscriber extends AbstractSearchEventSubscriber implements EventSubscriberInterface @@ -26,3 +26,5 @@ public function onAssignSection(AssignSectionEvent $event) ); } } + +class_alias(SectionEventSubscriber::class, 'eZ\Publish\Core\Search\Common\EventSubscriber\SectionEventSubscriber'); diff --git a/eZ/Publish/Core/Search/Common/EventSubscriber/TrashEventSubscriber.php b/src/lib/Search/Common/EventSubscriber/TrashEventSubscriber.php similarity index 78% rename from eZ/Publish/Core/Search/Common/EventSubscriber/TrashEventSubscriber.php rename to src/lib/Search/Common/EventSubscriber/TrashEventSubscriber.php index a41cd99c3c..0d9da88fab 100644 --- a/eZ/Publish/Core/Search/Common/EventSubscriber/TrashEventSubscriber.php +++ b/src/lib/Search/Common/EventSubscriber/TrashEventSubscriber.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Common\EventSubscriber; +namespace Ibexa\Core\Search\Common\EventSubscriber; -use eZ\Publish\API\Repository\Events\Trash\DeleteTrashItemEvent; -use eZ\Publish\API\Repository\Events\Trash\EmptyTrashEvent; -use eZ\Publish\API\Repository\Events\Trash\RecoverEvent; -use eZ\Publish\API\Repository\Events\Trash\TrashEvent; -use eZ\Publish\API\Repository\Values\Content\TrashItem; +use Ibexa\Contracts\Core\Repository\Events\Trash\DeleteTrashItemEvent; +use Ibexa\Contracts\Core\Repository\Events\Trash\EmptyTrashEvent; +use Ibexa\Contracts\Core\Repository\Events\Trash\RecoverEvent; +use Ibexa\Contracts\Core\Repository\Events\Trash\TrashEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\TrashItem; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class TrashEventSubscriber extends AbstractSearchEventSubscriber implements EventSubscriberInterface @@ -62,7 +62,7 @@ public function onEmptyTrashEvent(EmptyTrashEvent $event): void $results = $event->getResultList()->getIterator(); - /** @var \eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult $result */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResult $result */ foreach ($results as $result) { $reverseRelationContentIds = $result->reverseRelationContentIds; foreach ($reverseRelationContentIds as $contentId) { @@ -73,3 +73,5 @@ public function onEmptyTrashEvent(EmptyTrashEvent $event): void } } } + +class_alias(TrashEventSubscriber::class, 'eZ\Publish\Core\Search\Common\EventSubscriber\TrashEventSubscriber'); diff --git a/eZ/Publish/Core/Search/Common/EventSubscriber/UserEventSubscriber.php b/src/lib/Search/Common/EventSubscriber/UserEventSubscriber.php similarity index 87% rename from eZ/Publish/Core/Search/Common/EventSubscriber/UserEventSubscriber.php rename to src/lib/Search/Common/EventSubscriber/UserEventSubscriber.php index d36e9642d0..979e0313ae 100644 --- a/eZ/Publish/Core/Search/Common/EventSubscriber/UserEventSubscriber.php +++ b/src/lib/Search/Common/EventSubscriber/UserEventSubscriber.php @@ -4,19 +4,19 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Common\EventSubscriber; - -use eZ\Publish\API\Repository\Events\User\AssignUserToUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\BeforeUnAssignUserFromUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\CreateUserEvent; -use eZ\Publish\API\Repository\Events\User\CreateUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\DeleteUserEvent; -use eZ\Publish\API\Repository\Events\User\DeleteUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\MoveUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\UnAssignUserFromUserGroupEvent; -use eZ\Publish\API\Repository\Events\User\UpdateUserEvent; -use eZ\Publish\API\Repository\Events\User\UpdateUserGroupEvent; -use eZ\Publish\SPI\Repository\Event\AfterEvent; +namespace Ibexa\Core\Search\Common\EventSubscriber; + +use Ibexa\Contracts\Core\Repository\Event\AfterEvent; +use Ibexa\Contracts\Core\Repository\Events\User\AssignUserToUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\BeforeUnAssignUserFromUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\CreateUserEvent; +use Ibexa\Contracts\Core\Repository\Events\User\CreateUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\DeleteUserEvent; +use Ibexa\Contracts\Core\Repository\Events\User\DeleteUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\MoveUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\UnAssignUserFromUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\UpdateUserEvent; +use Ibexa\Contracts\Core\Repository\Events\User\UpdateUserGroupEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; class UserEventSubscriber extends AbstractSearchEventSubscriber implements EventSubscriberInterface @@ -199,3 +199,5 @@ private function indexUserContentWithLocation(AfterEvent $event): void } } } + +class_alias(UserEventSubscriber::class, 'eZ\Publish\Core\Search\Common\EventSubscriber\UserEventSubscriber'); diff --git a/eZ/Publish/Core/Search/Common/FieldNameGenerator.php b/src/lib/Search/Common/FieldNameGenerator.php similarity index 89% rename from eZ/Publish/Core/Search/Common/FieldNameGenerator.php rename to src/lib/Search/Common/FieldNameGenerator.php index c2b1b3cab6..48b2bed3c7 100644 --- a/eZ/Publish/Core/Search/Common/FieldNameGenerator.php +++ b/src/lib/Search/Common/FieldNameGenerator.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Common; +namespace Ibexa\Core\Search\Common; -use eZ\Publish\SPI\Search\FieldType; +use Ibexa\Contracts\Core\Search\FieldType; /** * Generator for search backend field names. @@ -21,7 +21,7 @@ class FieldNameGenerator * We implement this mapping, because those dynamic fields are common to * search backend configurations. * - * @see \eZ\Publish\SPI\Search\FieldType + * @see \Ibexa\Contracts\Core\Search\FieldType * * Code example: * @@ -80,3 +80,5 @@ public function getTypedName(string $name, FieldType $type): string return $name . '_' . $typeName; } } + +class_alias(FieldNameGenerator::class, 'eZ\Publish\Core\Search\Common\FieldNameGenerator'); diff --git a/eZ/Publish/Core/Search/Common/FieldNameResolver.php b/src/lib/Search/Common/FieldNameResolver.php similarity index 83% rename from eZ/Publish/Core/Search/Common/FieldNameResolver.php rename to src/lib/Search/Common/FieldNameResolver.php index 17684eae4a..1bce132395 100644 --- a/eZ/Publish/Core/Search/Common/FieldNameResolver.php +++ b/src/lib/Search/Common/FieldNameResolver.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Common; +namespace Ibexa\Core\Search\Common; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\CustomFieldInterface; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandler; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as ContentTypeHandler; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\CustomFieldInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; use RuntimeException; /** @@ -21,30 +21,30 @@ class FieldNameResolver /** * Field registry. * - * @var \eZ\Publish\Core\Search\Common\FieldRegistry + * @var \Ibexa\Core\Search\Common\FieldRegistry */ protected $fieldRegistry; /** * Content type handler. * - * @var \eZ\Publish\SPI\Persistence\Content\Type\Handler + * @var \Ibexa\Contracts\Core\Persistence\Content\Type\Handler */ protected $contentTypeHandler; /** * Field name generator. * - * @var \eZ\Publish\Core\Search\Common\FieldNameGenerator + * @var \Ibexa\Core\Search\Common\FieldNameGenerator */ protected $nameGenerator; /** * Create from search field registry, content type handler and field name generator. * - * @param \eZ\Publish\Core\Search\Common\FieldRegistry $fieldRegistry - * @param \eZ\Publish\SPI\Persistence\Content\Type\Handler $contentTypeHandler - * @param \eZ\Publish\Core\Search\Common\FieldNameGenerator $nameGenerator + * @param \Ibexa\Core\Search\Common\FieldRegistry $fieldRegistry + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\Handler $contentTypeHandler + * @param \Ibexa\Core\Search\Common\FieldNameGenerator $nameGenerator */ public function __construct( FieldRegistry $fieldRegistry, @@ -90,10 +90,10 @@ protected function getSearchableFieldMap() * can be targeted. * * @deprecated since 6.2, use getFieldTypes instead - * @see \eZ\Publish\API\Repository\Values\Content\Query\CustomFieldInterface - * @see \eZ\Publish\SPI\FieldType\Indexable + * @see \Ibexa\Contracts\Core\Repository\Values\Content\Query\CustomFieldInterface + * @see \Ibexa\Contracts\Core\FieldType\Indexable * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion * @param string $fieldDefinitionIdentifier * @param string|null $fieldTypeIdentifier * @param string|null $name @@ -119,15 +119,15 @@ public function getFieldNames( * $name specific field type and field from its Indexable implementation * can be targeted. * - * @see \eZ\Publish\API\Repository\Values\Content\Query\CustomFieldInterface - * @see \eZ\Publish\SPI\FieldType\Indexable + * @see \Ibexa\Contracts\Core\Repository\Values\Content\Query\CustomFieldInterface + * @see \Ibexa\Contracts\Core\FieldType\Indexable * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion * @param string $fieldDefinitionIdentifier * @param string|null $fieldTypeIdentifier * @param string|null $name * - * @return array + * @return array */ public function getFieldTypes( Criterion $criterion, @@ -180,10 +180,10 @@ public function getFieldTypes( * * Will return null if no sortable field is found. * - * @see \eZ\Publish\API\Repository\Values\Content\Query\CustomFieldInterface - * @see \eZ\Publish\SPI\FieldType\Indexable + * @see \Ibexa\Contracts\Core\Repository\Values\Content\Query\CustomFieldInterface + * @see \Ibexa\Contracts\Core\FieldType\Indexable * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause $sortClause * @param string $contentTypeIdentifier * @param string $fieldDefinitionIdentifier * @param string|null $name @@ -263,6 +263,12 @@ public function getIndexFieldName( $indexDefinition = $indexFieldType->getIndexDefinition(); // Should only happen by mistake, so let's throw if it does + if ($name === null) { + throw new RuntimeException( + "Undefined default sort or match field in '{$fieldTypeIdentifier}' Field Type's index definition" + ); + } + if (!isset($indexDefinition[$name])) { throw new RuntimeException( "Could not find Field '{$name}' in '{$fieldTypeIdentifier}' Field Type's index definition" @@ -307,3 +313,5 @@ public function getAggregationFieldName( return reset($fieldName); } } + +class_alias(FieldNameResolver::class, 'eZ\Publish\Core\Search\Common\FieldNameResolver'); diff --git a/src/lib/Search/Common/FieldRegistry.php b/src/lib/Search/Common/FieldRegistry.php new file mode 100644 index 0000000000..89d928544e --- /dev/null +++ b/src/lib/Search/Common/FieldRegistry.php @@ -0,0 +1,53 @@ + $type) { + $this->registerType($name, $type); + } + } + + public function registerType(string $name, Indexable $type): void + { + $this->types[$name] = $type; + } + + public function getType(string $name): Indexable + { + if (!isset($this->types[$name])) { + throw new OutOfBoundsException( + sprintf( + 'Field Type "%s" is not indexable. Provide %s implementation and register it with the "%s" tag.', + $name, + Indexable::class, + FieldRegistryPass::FIELD_TYPE_INDEXABLE_SERVICE_TAG + ) + ); + } + + return $this->types[$name]; + } +} + +class_alias(FieldRegistry::class, 'eZ\Publish\Core\Search\Common\FieldRegistry'); diff --git a/src/lib/Search/Common/FieldValueMapper.php b/src/lib/Search/Common/FieldValueMapper.php new file mode 100644 index 0000000000..5c9e2ab7b3 --- /dev/null +++ b/src/lib/Search/Common/FieldValueMapper.php @@ -0,0 +1,41 @@ + + */ + protected $simpleMappers = []; + + /** + * Construct from optional mapper array. + * + * @param \Ibexa\Core\Search\Common\FieldValueMapper[] $mappers + */ + public function __construct(array $mappers = []) + { + foreach ($mappers as $mapper) { + $this->addMapper($mapper); + } + } + + /** + * @param class-string<\Ibexa\Contracts\Core\Search\FieldType>|null $searchTypeFQCN + */ + public function addMapper(FieldValueMapper $mapper, ?string $searchTypeFQCN = null): void + { + if (null !== $searchTypeFQCN) { + $this->simpleMappers[$searchTypeFQCN] = $mapper; + } else { + $this->mappers[] = $mapper; + } + } + + public function canMap(Field $field): bool + { + return true; + } + + /** + * Map field value to a proper search engine representation. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + * + * @param \Ibexa\Contracts\Core\Search\Field $field + * + * @return mixed + */ + public function map(Field $field) + { + $mapper = $this->simpleMappers[get_class($field->getType())] + ?? $this->findMapper($field); + + return $mapper->map($field); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + private function findMapper(Field $field): FieldValueMapper + { + foreach ($this->mappers as $mapper) { + if ($mapper->canMap($field)) { + return $mapper; + } + } + + throw new NotImplementedException( + 'No mapper available for: ' . get_class($field->getType()) + ); + } +} + +class_alias(Aggregate::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\Aggregate'); diff --git a/src/lib/Search/Common/FieldValueMapper/BooleanMapper.php b/src/lib/Search/Common/FieldValueMapper/BooleanMapper.php new file mode 100644 index 0000000000..fe725e9d14 --- /dev/null +++ b/src/lib/Search/Common/FieldValueMapper/BooleanMapper.php @@ -0,0 +1,29 @@ +getType() instanceof BooleanField; + } + + public function map(Field $field) + { + return (bool)$field->getValue(); + } +} + +class_alias(BooleanMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\BooleanMapper'); diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/DateMapper.php b/src/lib/Search/Common/FieldValueMapper/DateMapper.php similarity index 76% rename from eZ/Publish/Core/Search/Common/FieldValueMapper/DateMapper.php rename to src/lib/Search/Common/FieldValueMapper/DateMapper.php index cee46f3da6..74bd043621 100644 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/DateMapper.php +++ b/src/lib/Search/Common/FieldValueMapper/DateMapper.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; +namespace Ibexa\Core\Search\Common\FieldValueMapper; use DateTime; use Exception; -use eZ\Publish\Core\Search\Common\FieldValueMapper; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\DateField; +use Ibexa\Contracts\Core\Search\Field; +use Ibexa\Contracts\Core\Search\FieldType\DateField; +use Ibexa\Core\Search\Common\FieldValueMapper; use InvalidArgumentException; /** @@ -39,3 +39,5 @@ public function map(Field $field) return $date->format('Y-m-d\\TH:i:s\\Z'); } } + +class_alias(DateMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\DateMapper'); diff --git a/src/lib/Search/Common/FieldValueMapper/DocumentMapper.php b/src/lib/Search/Common/FieldValueMapper/DocumentMapper.php new file mode 100644 index 0000000000..639b041502 --- /dev/null +++ b/src/lib/Search/Common/FieldValueMapper/DocumentMapper.php @@ -0,0 +1,29 @@ +getType() instanceof DocumentField; + } + + public function map(Field $field) + { + return $field->getValue(); + } +} + +class_alias(DocumentMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\DocumentMapper'); diff --git a/src/lib/Search/Common/FieldValueMapper/FloatMapper.php b/src/lib/Search/Common/FieldValueMapper/FloatMapper.php new file mode 100644 index 0000000000..4ca7584844 --- /dev/null +++ b/src/lib/Search/Common/FieldValueMapper/FloatMapper.php @@ -0,0 +1,29 @@ +getType() instanceof FloatField; + } + + public function map(Field $field) + { + return sprintf('%F', (float)$field->getValue()); + } +} + +class_alias(FloatMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\FloatMapper'); diff --git a/src/lib/Search/Common/FieldValueMapper/GeoLocationMapper.php b/src/lib/Search/Common/FieldValueMapper/GeoLocationMapper.php new file mode 100644 index 0000000000..83f33ea4dc --- /dev/null +++ b/src/lib/Search/Common/FieldValueMapper/GeoLocationMapper.php @@ -0,0 +1,34 @@ +getType() instanceof GeoLocationField; + } + + public function map(Field $field) + { + $value = $field->getValue(); + if ($value['latitude'] === null || $value['longitude'] === null) { + return null; + } + + return sprintf('%F,%F', $value['latitude'], $value['longitude']); + } +} + +class_alias(GeoLocationMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\GeoLocationMapper'); diff --git a/src/lib/Search/Common/FieldValueMapper/IdentifierMapper.php b/src/lib/Search/Common/FieldValueMapper/IdentifierMapper.php new file mode 100644 index 0000000000..c15c45a62f --- /dev/null +++ b/src/lib/Search/Common/FieldValueMapper/IdentifierMapper.php @@ -0,0 +1,51 @@ +getType() instanceof IdentifierField; + } + + /** + * Map field value to a proper search engine representation. + * + * @param \Ibexa\Contracts\Core\Search\Field $field + * + * @return mixed + */ + public function map(Field $field) + { + if ($field->getType()->raw) { + return $field->getValue(); + } + + return $this->convert($field->getValue()); + } + + /** + * Convert to a proper search engine representation. + * + * @param mixed $value + */ + protected function convert($value): string + { + // Remove non-printable characters + return preg_replace('([^A-Za-z0-9/]+)', '', $value); + } +} + +class_alias(IdentifierMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\IdentifierMapper'); diff --git a/src/lib/Search/Common/FieldValueMapper/IntegerMapper.php b/src/lib/Search/Common/FieldValueMapper/IntegerMapper.php new file mode 100644 index 0000000000..cb97133213 --- /dev/null +++ b/src/lib/Search/Common/FieldValueMapper/IntegerMapper.php @@ -0,0 +1,39 @@ +getType() instanceof IntegerField; + } + + public function map(Field $field) + { + return $this->convert($field->getValue()); + } + + /** + * Convert to a proper search engine representation. + * + * @param mixed $value + */ + protected function convert($value): int + { + return (int)$value; + } +} + +class_alias(IntegerMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\IntegerMapper'); diff --git a/src/lib/Search/Common/FieldValueMapper/MultipleBooleanMapper.php b/src/lib/Search/Common/FieldValueMapper/MultipleBooleanMapper.php new file mode 100644 index 0000000000..7f6c451ce3 --- /dev/null +++ b/src/lib/Search/Common/FieldValueMapper/MultipleBooleanMapper.php @@ -0,0 +1,35 @@ +getType() instanceof MultipleBooleanField; + } + + public function map(Field $field) + { + $values = []; + + foreach ((array)$field->getValue() as $value) { + $values[] = (bool)$value; + } + + return $values; + } +} + +class_alias(MultipleBooleanMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\MultipleBooleanMapper'); diff --git a/src/lib/Search/Common/FieldValueMapper/MultipleIdentifierMapper.php b/src/lib/Search/Common/FieldValueMapper/MultipleIdentifierMapper.php new file mode 100644 index 0000000000..c48b32b24c --- /dev/null +++ b/src/lib/Search/Common/FieldValueMapper/MultipleIdentifierMapper.php @@ -0,0 +1,46 @@ +getType() instanceof MultipleIdentifierField; + } + + /** + * Map field value to a proper search engine representation. + * + * @param \Ibexa\Contracts\Core\Search\Field $field + * + * @return mixed + */ + public function map(Field $field) + { + $values = []; + + $isRaw = $field->getType()->raw; + foreach ($field->getValue() as $value) { + if (!$isRaw) { + $value = $this->convert($value); + } + + $values[] = $value; + } + + return $values; + } +} + +class_alias(MultipleIdentifierMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\MultipleIdentifierMapper'); diff --git a/src/lib/Search/Common/FieldValueMapper/MultipleIntegerMapper.php b/src/lib/Search/Common/FieldValueMapper/MultipleIntegerMapper.php new file mode 100644 index 0000000000..c45d80ecb7 --- /dev/null +++ b/src/lib/Search/Common/FieldValueMapper/MultipleIntegerMapper.php @@ -0,0 +1,34 @@ +getType() instanceof MultipleIntegerField; + } + + public function map(Field $field) + { + $values = []; + + foreach ((array)$field->getValue() as $value) { + $values[] = $this->convert($value); + } + + return $values; + } +} + +class_alias(MultipleIntegerMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\MultipleIntegerMapper'); diff --git a/src/lib/Search/Common/FieldValueMapper/MultipleRemoteIdentifierMapper.php b/src/lib/Search/Common/FieldValueMapper/MultipleRemoteIdentifierMapper.php new file mode 100644 index 0000000000..287f332a59 --- /dev/null +++ b/src/lib/Search/Common/FieldValueMapper/MultipleRemoteIdentifierMapper.php @@ -0,0 +1,34 @@ +getType() instanceof MultipleRemoteIdentifierField; + } + + public function map(Field $field) + { + $values = []; + + foreach ($field->getValue() as $value) { + $values[] = $this->convert($value); + } + + return $values; + } +} + +class_alias(MultipleRemoteIdentifierMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\MultipleRemoteIdentifierMapper'); diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleStringMapper.php b/src/lib/Search/Common/FieldValueMapper/MultipleStringMapper.php similarity index 75% rename from eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleStringMapper.php rename to src/lib/Search/Common/FieldValueMapper/MultipleStringMapper.php index 2693c1a576..30f5b35e03 100644 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/MultipleStringMapper.php +++ b/src/lib/Search/Common/FieldValueMapper/MultipleStringMapper.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; +namespace Ibexa\Core\Search\Common\FieldValueMapper; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType; +use Ibexa\Contracts\Core\Search\Field; +use Ibexa\Contracts\Core\Search\FieldType; /** * Common multiple string field value mapper implementation. @@ -27,7 +27,7 @@ public function canMap(Field $field): bool /** * Map field value to a proper search engine representation. * - * @param \eZ\Publish\SPI\Search\Field $field + * @param \Ibexa\Contracts\Core\Search\Field $field * * @return array */ @@ -42,3 +42,5 @@ public function map(Field $field) return $values; } } + +class_alias(MultipleStringMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\MultipleStringMapper'); diff --git a/src/lib/Search/Common/FieldValueMapper/PriceMapper.php b/src/lib/Search/Common/FieldValueMapper/PriceMapper.php new file mode 100644 index 0000000000..7c5b803831 --- /dev/null +++ b/src/lib/Search/Common/FieldValueMapper/PriceMapper.php @@ -0,0 +1,36 @@ +getType() instanceof PriceField; + } + + /** + * Map field value to a proper search engine representation. + * + * @param \Ibexa\Contracts\Core\Search\Field $field + * + * @return mixed + */ + public function map(Field $field) + { + return (float)$field->getValue(); + } +} + +class_alias(PriceMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\PriceMapper'); diff --git a/src/lib/Search/Common/FieldValueMapper/RemoteIdentifierMapper.php b/src/lib/Search/Common/FieldValueMapper/RemoteIdentifierMapper.php new file mode 100644 index 0000000000..e1d7c9aecd --- /dev/null +++ b/src/lib/Search/Common/FieldValueMapper/RemoteIdentifierMapper.php @@ -0,0 +1,27 @@ +getType() instanceof RemoteIdentifierField; + } +} + +class_alias(RemoteIdentifierMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\RemoteIdentifierMapper'); diff --git a/eZ/Publish/Core/Search/Common/FieldValueMapper/StringMapper.php b/src/lib/Search/Common/FieldValueMapper/StringMapper.php similarity index 80% rename from eZ/Publish/Core/Search/Common/FieldValueMapper/StringMapper.php rename to src/lib/Search/Common/FieldValueMapper/StringMapper.php index e4c12d5aa4..1050b068ed 100644 --- a/eZ/Publish/Core/Search/Common/FieldValueMapper/StringMapper.php +++ b/src/lib/Search/Common/FieldValueMapper/StringMapper.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Common\FieldValueMapper; +namespace Ibexa\Core\Search\Common\FieldValueMapper; -use eZ\Publish\Core\Search\Common\FieldValueMapper; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType; +use Ibexa\Contracts\Core\Search\Field; +use Ibexa\Contracts\Core\Search\FieldType; +use Ibexa\Core\Search\Common\FieldValueMapper; /** * Common string field value mapper implementation. @@ -50,3 +50,5 @@ protected function convert($value): string ); } } + +class_alias(StringMapper::class, 'eZ\Publish\Core\Search\Common\FieldValueMapper\StringMapper'); diff --git a/eZ/Publish/Core/Search/Common/IncrementalIndexer.php b/src/lib/Search/Common/IncrementalIndexer.php similarity index 95% rename from eZ/Publish/Core/Search/Common/IncrementalIndexer.php rename to src/lib/Search/Common/IncrementalIndexer.php index e59cc77319..86b278141a 100644 --- a/eZ/Publish/Core/Search/Common/IncrementalIndexer.php +++ b/src/lib/Search/Common/IncrementalIndexer.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Common; +namespace Ibexa\Core\Search\Common; use Doctrine\DBAL\FetchMode; use Symfony\Component\Console\Helper\ProgressBar; @@ -88,3 +88,5 @@ abstract public function purge(); */ abstract public function getName(); } + +class_alias(IncrementalIndexer::class, 'eZ\Publish\Core\Search\Common\IncrementalIndexer'); diff --git a/src/lib/Search/Common/Indexer.php b/src/lib/Search/Common/Indexer.php new file mode 100644 index 0000000000..e13db89017 --- /dev/null +++ b/src/lib/Search/Common/Indexer.php @@ -0,0 +1,74 @@ +logger = $logger; + $this->persistenceHandler = $persistenceHandler; + $this->connection = $connection; + $this->searchHandler = $searchHandler; + } + + /** + * Create search engine index. + * + * @param \Symfony\Component\Console\Output\OutputInterface $output + * @param int $iterationCount + * @param bool $commit commit changes after each iteration + */ + abstract public function createSearchIndex(OutputInterface $output, $iterationCount, $commit); + + /** + * Get DB Statement to fetch metadata about content objects to be indexed. + * + * @param array $fields fields to select + */ + protected function getContentDbFieldsStmt(array $fields): Statement + { + $query = $this->connection->createQueryBuilder(); + $query + ->select($fields) + ->from(ContentGateway::CONTENT_ITEM_TABLE) + ->where($query->expr()->eq('status', ContentInfo::STATUS_PUBLISHED)); + + return $query->execute(); + } +} + +class_alias(Indexer::class, 'eZ\Publish\Core\Search\Common\Indexer'); diff --git a/src/lib/Search/Indexer/ContentIdBatchList.php b/src/lib/Search/Indexer/ContentIdBatchList.php new file mode 100644 index 0000000000..ae8d410594 --- /dev/null +++ b/src/lib/Search/Indexer/ContentIdBatchList.php @@ -0,0 +1,49 @@ +> + */ +final class ContentIdBatchList implements IteratorAggregate +{ + /** @var iterable> */ + private iterable $list; + + private int $totalCount; + + /** + * @param iterable> $list + */ + public function __construct(iterable $list, int $totalCount) + { + $this->list = $list; + $this->totalCount = $totalCount; + } + + /** + * return \Traversable>. + */ + public function getIterator(): Traversable + { + yield from $this->list; + } + + public function getCount(): int + { + return $this->totalCount; + } +} diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriteriaConverter.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriteriaConverter.php new file mode 100644 index 0000000000..dd474e2fb9 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriteriaConverter.php @@ -0,0 +1,71 @@ +handlers = $handlers; + } + + /** + * Adds handler. + * + * @param \Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler $handler + */ + public function addHandler(CriterionHandler $handler) + { + $this->handlers[] = $handler; + } + + /** + * Generic converter of criteria into query fragments. + * + * @param array $languageSettings + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|string + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function convertCriteria( + QueryBuilder $query, + Criterion $criterion, + array $languageSettings + ) { + foreach ($this->handlers as $handler) { + if ($handler->accept($criterion)) { + return $handler->handle($this, $query, $criterion, $languageSettings); + } + } + + throw new NotImplementedException( + 'No visitor available for: ' . get_class($criterion) . ' with operator ' . $criterion->operator + ); + } +} + +class_alias(CriteriaConverter::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler.php new file mode 100644 index 0000000000..832a977cd3 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler.php @@ -0,0 +1,90 @@ + 'eq', + Operator::GT => 'gt', + Operator::GTE => 'gte', + Operator::LT => 'lt', + Operator::LTE => 'lte', + Operator::LIKE => 'like', + ]; + + /** @var \Doctrine\DBAL\Connection */ + protected $connection; + + /** @var \Doctrine\DBAL\Platforms\AbstractPlatform|null */ + protected $dbPlatform; + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + $this->dbPlatform = $connection->getDatabasePlatform(); + } + + /** + * Check if this criterion handler accepts to handle the given criterion. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion + * + * @return bool + */ + abstract public function accept(Criterion $criterion); + + /** + * Generate query expression for a Criterion this handler accepts. + * + * accept() must be called before calling this method. + * + * @param array $languageSettings + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|string + */ + abstract public function handle( + CriteriaConverter $converter, + QueryBuilder $queryBuilder, + Criterion $criterion, + array $languageSettings + ); + + protected function hasJoinedTableAs(QueryBuilder $queryBuilder, string $tableAlias): bool + { + // find table name in a structure: ['fromAlias' => [['joinTable' => ''], ...]] + $joinedParts = $queryBuilder->getQueryPart('join'); + foreach ($joinedParts as $joinedTables) { + foreach ($joinedTables as $join) { + if ($join['joinAlias'] === $tableAlias) { + return true; + } + } + } + + return false; + } +} + +class_alias(CriterionHandler::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/CompositeCriterion.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/CompositeCriterion.php new file mode 100644 index 0000000000..48d1a03f44 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/CompositeCriterion.php @@ -0,0 +1,33 @@ +convertCriteria($queryBuilder, $criterion->criteria, $languageSettings); + } +} + +class_alias(CompositeCriterion::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\CompositeCriterion'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentId.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentId.php new file mode 100644 index 0000000000..6698700ae9 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentId.php @@ -0,0 +1,47 @@ +value; + + return $queryBuilder->expr()->in( + 'c.id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ); + } +} + +class_alias(ContentId::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentId'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentName.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentName.php new file mode 100644 index 0000000000..63314e961c --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentName.php @@ -0,0 +1,96 @@ +operator === Criterion\Operator::LIKE; + } + + /** + * @param array{ + * languages: array, + * useAlwaysAvailable: bool, + * } $languageSettings + * + * @throws \Doctrine\DBAL\Exception + */ + public function handle( + CriteriaConverter $converter, + QueryBuilder $queryBuilder, + Criterion $criterion, + array $languageSettings + ): string { + $subQuery = $this->connection->createQueryBuilder(); + $subQuery + ->select('1') + ->from(Gateway::CONTENT_NAME_TABLE, self::CONTENTOBJECT_NAME_ALIAS) + ->andWhere( + $queryBuilder->expr()->eq( + self::CONTENTOBJECT_NAME_ALIAS . '.contentobject_id', + self::CONTENTOBJECT_ALIAS . '.id' + ), + $queryBuilder->expr()->eq( + self::CONTENTOBJECT_NAME_ALIAS . '.content_version', + self::CONTENTOBJECT_ALIAS . '.current_version' + ), + $queryBuilder->expr()->like( + $this->toLowerCase(self::CONTENTOBJECT_NAME_ALIAS . '.name'), + $this->toLowerCase( + $queryBuilder->createNamedParameter( + $this->prepareValue($criterion) + ) + ) + ) + ); + + return sprintf( + 'EXISTS (%s)', + $subQuery->getSQL() + ); + } + + private function prepareValue(Criterion $criterion): string + { + /** @var string $value */ + $value = $criterion->value; + + return str_replace( + '*', + '%', + addcslashes( + $value, + '%_' + ) + ); + } + + /** + * @throws \Doctrine\DBAL\Exception + */ + private function toLowerCase(string $value): string + { + return $this->connection->getDatabasePlatform()->getLowerExpression($value); + } +} diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeGroupId.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeGroupId.php new file mode 100644 index 0000000000..b844dd6ce8 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeGroupId.php @@ -0,0 +1,58 @@ +connection->createQueryBuilder(); + $subSelect + ->select( + 'contentclass_id' + )->from( + 'ezcontentclass_classgroup' + )->where( + $queryBuilder->expr()->in( + 'group_id', + $queryBuilder->createNamedParameter($criterion->value, Connection::PARAM_INT_ARRAY) + ) + ); + + return $queryBuilder->expr()->in( + 'c.contentclass_id', + $subSelect->getSQL() + ); + } +} + +class_alias(ContentTypeGroupId::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentTypeGroupId'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeId.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeId.php new file mode 100644 index 0000000000..c705ad2539 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeId.php @@ -0,0 +1,45 @@ +expr()->in( + 'c.contentclass_id', + $queryBuilder->createNamedParameter($criterion->value, Connection::PARAM_INT_ARRAY) + ); + } +} + +class_alias(ContentTypeId::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentTypeId'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeIdentifier.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeIdentifier.php new file mode 100644 index 0000000000..c1c9504f42 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ContentTypeIdentifier.php @@ -0,0 +1,97 @@ +contentTypeHandler = $contentTypeHandler; + $this->logger = $logger ?? new NullLogger(); + } + + /** + * Check if this criterion handler accepts to handle the given criterion. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion + * + * @return bool + */ + public function accept(Criterion $criterion) + { + return $criterion instanceof Criterion\ContentTypeIdentifier; + } + + public function handle( + CriteriaConverter $converter, + QueryBuilder $queryBuilder, + Criterion $criterion, + array $languageSettings + ) { + $idList = []; + $invalidIdentifiers = []; + + foreach ($criterion->value as $identifier) { + try { + $idList[] = $this->contentTypeHandler->loadByIdentifier($identifier)->id; + } catch (NotFoundException $e) { + // Skip non-existing content types, but track for code below + $invalidIdentifiers[] = $identifier; + } + } + + if (count($invalidIdentifiers) > 0) { + $this->logger->warning( + sprintf( + 'Invalid content type identifiers provided for ContentTypeIdentifier criterion: %s', + implode(', ', $invalidIdentifiers) + ) + ); + } + + if (count($idList) === 0) { + return '1 = 0'; + } + + return $queryBuilder->expr()->in( + 'c.contentclass_id', + $queryBuilder->createNamedParameter($idList, Connection::PARAM_INT_ARRAY) + ); + } +} + +class_alias(ContentTypeIdentifier::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ContentTypeIdentifier'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/DateMetadata.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/DateMetadata.php new file mode 100644 index 0000000000..ca5c2e128f --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/DateMetadata.php @@ -0,0 +1,91 @@ +getColumnName($criterion); + + $value = (array)$criterion->value; + switch ($criterion->operator) { + case Criterion\Operator::IN: + return $queryBuilder->expr()->in( + $column, + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ); + + case Criterion\Operator::BETWEEN: + return $this->dbPlatform->getBetweenExpression( + $column, + $queryBuilder->createNamedParameter($value[0], ParameterType::INTEGER), + $queryBuilder->createNamedParameter($value[1], ParameterType::INTEGER) + ); + + case Criterion\Operator::EQ: + case Criterion\Operator::GT: + case Criterion\Operator::GTE: + case Criterion\Operator::LT: + case Criterion\Operator::LTE: + $operatorFunction = $this->comparatorMap[$criterion->operator]; + + return $queryBuilder->expr()->$operatorFunction( + $column, + $queryBuilder->createNamedParameter(reset($value), ParameterType::INTEGER) + ); + + default: + throw new RuntimeException( + "Unknown operator '{$criterion->operator}' for DateMetadata Criterion handler." + ); + } + } + + private function getColumnName(Criterion $criterion): string + { + switch ($criterion->target) { + case Criterion\DateMetadata::TRASHED: + return 't.' . Criterion\DateMetadata::TRASHED; + case Criterion\DateMetadata::MODIFIED: + return 'c.' . Criterion\DateMetadata::MODIFIED; + case Criterion\DateMetadata::CREATED: + case Criterion\DateMetadata::PUBLISHED: + default: + return 'c.' . Criterion\DateMetadata::PUBLISHED; + } + } +} + +class_alias(DateMetadata::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\DateMetadata'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/Field.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/Field.php new file mode 100644 index 0000000000..4ed7cb1cca --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/Field.php @@ -0,0 +1,178 @@ +fieldConverterRegistry = $fieldConverterRegistry; + $this->fieldValueConverter = $fieldValueConverter; + $this->transformationProcessor = $transformationProcessor; + } + + /** + * Check if this criterion handler accepts to handle the given criterion. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion + * + * @return bool + */ + public function accept(Criterion $criterion) + { + return $criterion instanceof Criterion\Field; + } + + /** + * Returns relevant field information for the specified field. + * + * The returned information is returned as an array of the attribute + * identifier and the sort column, which should be used. + * + * @param string $fieldIdentifier + * + * @return array + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If no searchable fields are found for the given $fieldIdentifier. + * @throws \RuntimeException if no converter is found + * @throws \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound + */ + protected function getFieldsInformation($fieldIdentifier) + { + $fieldMapArray = []; + $fieldMap = $this->contentTypeHandler->getSearchableFieldMap(); + + foreach ($fieldMap as $fieldIdentifierMap) { + // First check if field exists in the current ContentType, there is nothing to do if it doesn't + if (!isset($fieldIdentifierMap[$fieldIdentifier])) { + continue; + } + + $fieldTypeIdentifier = $fieldIdentifierMap[$fieldIdentifier]['field_type_identifier']; + $fieldMapArray[$fieldTypeIdentifier]['ids'][] = $fieldIdentifierMap[$fieldIdentifier]['field_definition_id']; + if (!isset($fieldMapArray[$fieldTypeIdentifier]['column'])) { + $fieldMapArray[$fieldTypeIdentifier]['column'] = $this->fieldConverterRegistry->getConverter( + $fieldTypeIdentifier + )->getIndexColumn(); + } + } + + if (empty($fieldMapArray)) { + throw new InvalidArgumentException( + '$criterion->target', + "No searchable Fields found for the provided Criterion target '{$fieldIdentifier}'." + ); + } + + return $fieldMapArray; + } + + /** + * @param array $languageSettings + * + * @return string + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException If no searchable fields are found for the given criterion target. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\Exception\NotFound + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function handle( + CriteriaConverter $converter, + QueryBuilder $queryBuilder, + Criterion $criterion, + array $languageSettings + ) { + $fieldsInformation = $this->getFieldsInformation($criterion->target); + + $subSelect = $this->connection->createQueryBuilder(); + $subSelect + ->select('contentobject_id') + ->from(ContentGateway::CONTENT_FIELD_TABLE, 'f_def'); + + $whereExpressions = []; + + foreach ($fieldsInformation as $fieldTypeIdentifier => $fieldsInfo) { + if ($fieldsInfo['column'] === false) { + continue; + } + + $filter = $this->fieldValueConverter->convertCriteria( + $fieldTypeIdentifier, + $queryBuilder, + $subSelect, + $criterion, + $fieldsInfo['column'] + ); + + $whereExpressions[] = $subSelect->expr()->andX( + $subSelect->expr()->in( + 'contentclassattribute_id', + $queryBuilder->createNamedParameter( + $fieldsInfo['ids'], + Connection::PARAM_INT_ARRAY + ) + ), + $filter + ); + } + + return $this->getInExpressionWithFieldConditions( + $queryBuilder, + $subSelect, + $languageSettings, + $whereExpressions, + $fieldsInformation + ); + } +} + +class_alias(Field::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\Field'); diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldBase.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldBase.php similarity index 85% rename from eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldBase.php rename to src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldBase.php index 22ef11794f..67cc3f2c67 100644 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldBase.php +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldBase.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; +namespace Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; use Doctrine\DBAL\Connection; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandler; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as ContentTypeHandler; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; /** * Base criterion handler for field criteria. @@ -20,16 +20,16 @@ abstract class FieldBase extends CriterionHandler { /** - * Content Type handler. + * Content type handler. * - * @var \eZ\Publish\SPI\Persistence\Content\Type\Handler + * @var \Ibexa\Contracts\Core\Persistence\Content\Type\Handler */ protected $contentTypeHandler; /** * Language handler. * - * @var \eZ\Publish\SPI\Persistence\Content\Language\Handler + * @var \Ibexa\Contracts\Core\Persistence\Content\Language\Handler */ protected $languageHandler; @@ -52,7 +52,7 @@ public function __construct( * * @param array $languageSettings * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ protected function getFieldCondition(QueryBuilder $query, array $languageSettings): string { @@ -145,8 +145,8 @@ protected function getFieldCondition(QueryBuilder $query, array $languageSetting * * @return string * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\NotImplementedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException */ protected function getInExpressionWithFieldConditions( QueryBuilder $query, @@ -180,3 +180,5 @@ protected function getInExpressionWithFieldConditions( ); } } + +class_alias(FieldBase::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldBase'); diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldEmpty.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldEmpty.php similarity index 78% rename from eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldEmpty.php rename to src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldEmpty.php index d66ea3a1ff..9a1d8499c3 100644 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldEmpty.php +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldEmpty.php @@ -6,18 +6,18 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; +namespace Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\FieldTypeService; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry as Registry; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandler; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as ContentTypeHandler; +use Ibexa\Contracts\Core\Repository\FieldTypeService; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry as Registry; +use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; /** * Field criterion handler. @@ -27,12 +27,12 @@ class FieldEmpty extends FieldBase /** * Field converter registry. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry + * @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry */ protected $fieldConverterRegistry; /** - * @var \eZ\Publish\API\Repository\FieldTypeService + * @var \Ibexa\Contracts\Core\Repository\FieldTypeService */ protected $fieldTypeService; @@ -63,9 +63,9 @@ public function accept(Criterion $criterion): bool * Returns an array of the attribute, * identifier and the sort column, which should be used. * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException If no searchable fields are found for the given $fieldIdentifier. + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If no searchable fields are found for the given $fieldIdentifier. * @throws \RuntimeException if no converter is found - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ protected function getFieldsInformation(string $fieldIdentifier): array { @@ -140,3 +140,5 @@ public function handle( ); } } + +class_alias(FieldEmpty::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldEmpty'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldRelation.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldRelation.php new file mode 100644 index 0000000000..2f1d13def5 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldRelation.php @@ -0,0 +1,202 @@ +contentTypeHandler->getSearchableFieldMap(); + + foreach ($fieldMap as $fieldIdentifierMap) { + // First check if field exists in the current ContentType, there is nothing to do if it doesn't + if (!isset($fieldIdentifierMap[$fieldDefinitionIdentifier])) { + continue; + } + + $fieldDefinitionIdList[] = $fieldIdentifierMap[$fieldDefinitionIdentifier]['field_definition_id']; + } + + if (empty($fieldDefinitionIdList)) { + throw new InvalidArgumentException( + '$criterion->target', + "No searchable Fields found for the provided Criterion target '{$fieldDefinitionIdentifier}'." + ); + } + + return $fieldDefinitionIdList; + } + + public function handle( + CriteriaConverter $converter, + QueryBuilder $queryBuilder, + Criterion $criterion, + array $languageSettings + ) { + $fieldDefinitionIds = $this->getFieldDefinitionsIds($criterion->target); + + $criterionValue = (array)$criterion->value; + switch ($criterion->operator) { + case Criterion\Operator::CONTAINS: + if (count($criterionValue) > 1) { + $subRequest = $this->buildQueryForContainsOperator( + $queryBuilder, + $criterionValue, + $fieldDefinitionIds + ); + + return $queryBuilder->expr()->andX(...$subRequest); + } + // Intentionally omitting break + + case Criterion\Operator::IN: + $subSelect = $this->buildQueryForInOperator( + $queryBuilder, + $criterionValue, + $fieldDefinitionIds + ); + + return $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + + default: + throw new RuntimeException( + "Unknown operator '{$criterion->operator}' for RelationList Criterion handler." + ); + } + } + + protected function buildQueryForContainsOperator( + QueryBuilder $queryBuilder, + array $criterionValue, + array $fieldDefinitionIds + ): array { + $subRequest = []; + + foreach ($criterionValue as $value) { + $subSelect = $this->connection->createQueryBuilder(); + $expr = $subSelect->expr(); + + $subSelect + ->select('from_contentobject_id') + ->from(ContentGateway::CONTENT_RELATION_TABLE, 'c_rel'); + + $subSelect->where( + $expr->andX( + $expr->eq( + 'c_rel.from_contentobject_version', + 'c.current_version' + ), + $expr->in( + 'c_rel.contentclassattribute_id', + $queryBuilder->createNamedParameter($fieldDefinitionIds, Connection::PARAM_INT_ARRAY) + ), + $expr->eq( + self::CONTENT_ITEM_REL_COLUMN, + $queryBuilder->createNamedParameter( + $value, + ParameterType::INTEGER + ) + ) + ) + ); + + $subRequest[] = $expr->in( + 'c.id', + $subSelect->getSQL() + ); + } + + return $subRequest; + } + + protected function buildQueryForInOperator( + QueryBuilder $queryBuilder, + array $criterionValue, + array $fieldDefinitionIds + ): QueryBuilder { + $subSelect = $this->connection->createQueryBuilder(); + $expr = $subSelect->expr(); + + $subSelect + ->select('from_contentobject_id') + ->from(ContentGateway::CONTENT_RELATION_TABLE, 'c_rel') + ->where( + $expr->eq( + 'c_rel.from_contentobject_version', + 'c.current_version' + ), + ) + ->andWhere( + $expr->in( + 'c_rel.contentclassattribute_id', + $queryBuilder->createNamedParameter( + $fieldDefinitionIds, + Connection::PARAM_INT_ARRAY + ) + ) + ) + ->andWhere( + $expr->in( + self::CONTENT_ITEM_REL_COLUMN, + $queryBuilder->createNamedParameter( + $criterionValue, + Connection::PARAM_INT_ARRAY + ) + ) + ); + + return $subSelect; + } +} + +class_alias(FieldRelation::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldRelation'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Converter.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Converter.php new file mode 100644 index 0000000000..4f3b9aa426 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Converter.php @@ -0,0 +1,80 @@ +registry = $registry; + $this->defaultHandler = $defaultHandler; + } + + /** + * Converts the criteria into query fragments. + * + * @param \Doctrine\DBAL\Query\QueryBuilder $outerQuery to be used only for parameter binding + * @param \Doctrine\DBAL\Query\QueryBuilder $subQuery to modify Field Value query constraints + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|string + * + * @throws \RuntimeException if Criterion is not applicable to its target + */ + public function convertCriteria( + string $fieldTypeIdentifier, + QueryBuilder $outerQuery, + QueryBuilder $subQuery, + Criterion $criterion, + string $column + ) { + if ($this->registry->has($fieldTypeIdentifier)) { + return $this->registry->get($fieldTypeIdentifier)->handle( + $outerQuery, + $subQuery, + $criterion, + $column + ); + } + + if ($this->defaultHandler === null) { + throw new RuntimeException( + "No conversion for a Field Type '$fieldTypeIdentifier' found." + ); + } + + return $this->defaultHandler->handle($outerQuery, $subQuery, $criterion, $column); + } +} + +class_alias(Converter::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Converter'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php new file mode 100644 index 0000000000..d061587f5e --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler.php @@ -0,0 +1,230 @@ + 'eq', + CriterionOperator::GT => 'gt', + CriterionOperator::GTE => 'gte', + CriterionOperator::LT => 'lt', + CriterionOperator::LTE => 'lte', + ]; + + /** + * Transformation processor. + * + * @var \Ibexa\Core\Persistence\TransformationProcessor + */ + protected $transformationProcessor; + + /** @var \Doctrine\DBAL\Platforms\AbstractPlatform|null */ + protected $dbPlatform; + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function __construct(Connection $connection, TransformationProcessor $transformationProcessor) + { + $this->connection = $connection; + $this->dbPlatform = $connection->getDatabasePlatform(); + $this->transformationProcessor = $transformationProcessor; + } + + /** + * Generates query expression for operator and value of a Field Criterion. + * + * @param \Doctrine\DBAL\Query\QueryBuilder $outerQuery to be used only for parameter binding + * @param \Doctrine\DBAL\Query\QueryBuilder $subQuery to modify Field Value query constraints + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion + * + * @return \Doctrine\DBAL\Query\Expression\CompositeExpression|string + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException If passed more than 1 argument to unary operator. + * @throws \RuntimeException If operator is not handled. + */ + public function handle( + QueryBuilder $outerQuery, + QueryBuilder $subQuery, + Criterion $criterion, + string $column + ) { + if (is_array($criterion->value) && Criterion\Operator::isUnary($criterion->operator)) { + if (count($criterion->value) > 1) { + throw new InvalidArgumentException('$criterion->value', "Too many arguments for unary operator '$criterion->operator'"); + } + + $criterion->value = reset($criterion->value); + } + + switch ($criterion->operator) { + case Criterion\Operator::IN: + $values = array_map([$this, 'prepareParameter'], $criterion->value); + $filter = $subQuery->expr()->in( + $column, + $outerQuery->createNamedParameter( + $values, + $this->getParamArrayType($values) + ) + ); + break; + + case Criterion\Operator::BETWEEN: + $filter = $this->dbPlatform->getBetweenExpression( + $column, + $outerQuery->createNamedParameter($this->lowerCase($criterion->value[0])), + $outerQuery->createNamedParameter($this->lowerCase($criterion->value[1])) + ); + break; + + case Criterion\Operator::EQ: + case Criterion\Operator::GT: + case Criterion\Operator::GTE: + case Criterion\Operator::LT: + case Criterion\Operator::LTE: + $operatorFunction = $this->comparatorMap[$criterion->operator]; + $filter = $subQuery->expr()->{$operatorFunction}( + $column, + $this->createNamedParameter($outerQuery, $column, $criterion->value) + ); + break; + + case Criterion\Operator::LIKE: + $value = str_replace('*', '%', $this->prepareLikeString($criterion->value)); + + $filter = $subQuery->expr()->like( + $column, + $outerQuery->createNamedParameter($value) + ); + break; + + case Criterion\Operator::CONTAINS: + $filter = $subQuery->expr()->like( + $column, + $outerQuery->createNamedParameter( + '%' . $this->prepareLikeString($criterion->value) . '%' + ) + ); + break; + + default: + throw new RuntimeException( + "Unknown operator '{$criterion->operator}' for Field Criterion handler." + ); + } + + return $filter; + } + + /** + * Returns the given $string prepared for use in SQL LIKE clause. + * + * LIKE clause wildcards '%' and '_' contained in the given $string will be escaped. + */ + protected function prepareLikeString(string $string): string + { + return addcslashes($this->lowerCase($string), '%_'); + } + + /** + * Downcases a given string using string transformation processor. + */ + protected function lowerCase(string $string): string + { + return $this->transformationProcessor->transformByGroup($string, 'lowercase'); + } + + /** + * @param scalar|array $value + * + * @return scalar|array + */ + private function prepareParameter($value) + { + if (is_string($value)) { + return $this->lowerCase($value); + } + + if (is_array($value)) { + return array_map([$this, 'prepareParameter'], $value); + } + + return $value; + } + + private function createNamedParameter(QueryBuilder $outerQuery, string $column, $value): ?string + { + switch ($column) { + case 'sort_key_string': + $parameterValue = $this->prepareParameter($value); + $parameterType = ParameterType::STRING; + break; + case 'sort_key_int': + $parameterValue = (int)$value; + $parameterType = ParameterType::INTEGER; + break; + default: + $parameterValue = $value; + $parameterType = null; + } + + return $outerQuery->createNamedParameter( + $parameterValue, + $parameterType + ); + } + + /** + * @param array $values + */ + private function getParamArrayType(array $values): int + { + if (empty($values)) { + throw new InvalidArgumentException('$values', 'Array cannot be empty'); + } + + $types = []; + foreach ($values as $value) { + if (is_bool($value) || ($value !== 0 && is_int($value))) { + // Ignore 0 as ambiguous (float vs int) + $types[] = Connection::PARAM_INT_ARRAY; + } else { + // Floats are considered strings + $types[] = Connection::PARAM_STR_ARRAY; + } + } + + $arrayValueTypes = array_unique($types); + + // Fallback to Connection::PARAM_STR_ARRAY + return $arrayValueTypes[0] ?? Connection::PARAM_STR_ARRAY; + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler'); diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Collection.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Collection.php similarity index 87% rename from eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Collection.php rename to src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Collection.php index 6b8cf9589f..e391e08a1e 100644 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Collection.php +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Collection.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler; +namespace Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler; use Doctrine\DBAL\Connection; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Core\Persistence\TransformationProcessor; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler; /** * Content locator gateway implementation using the DoctrineDatabase. @@ -95,3 +95,5 @@ public function handle( return $filter; } } + +class_alias(Collection::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler\Collection'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Composite.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Composite.php new file mode 100644 index 0000000000..22c8b1d01b --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FieldValue/Handler/Composite.php @@ -0,0 +1,21 @@ + $handler) { + $this->register($fieldTypeIdentifier, $handler); + } + } + + /** + * Register $handler for $fieldTypeIdentifier. + * + * @param string $fieldTypeIdentifier + * @param \Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler $handler + */ + public function register($fieldTypeIdentifier, $handler) + { + $this->map[$fieldTypeIdentifier] = $handler; + } + + /** + * Returns handler for given $fieldTypeIdentifier. + * + * @throws \OutOfBoundsException If handler is not registered for a given $fieldTypeIdentifier + * + * @param string $fieldTypeIdentifier + * + * @return \Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\Handler + */ + public function get($fieldTypeIdentifier) + { + if (!isset($this->map[$fieldTypeIdentifier])) { + throw new OutOfBoundsException("No handler registered for Field Type '{$fieldTypeIdentifier}'."); + } + + return $this->map[$fieldTypeIdentifier]; + } + + /** + * Checks if handler is registered for the given $fieldTypeIdentifier. + * + * @param string $fieldTypeIdentifier + * + * @return bool + */ + public function has($fieldTypeIdentifier) + { + return isset($this->map[$fieldTypeIdentifier]); + } +} + +class_alias(HandlerRegistry::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\HandlerRegistry'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php new file mode 100644 index 0000000000..2b17830ac3 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/FullText.php @@ -0,0 +1,283 @@ + 0.66, + 'enableWildcards' => true, + 'commands' => [ + 'apostrophe_normalize', + 'apostrophe_to_doublequote', + 'ascii_lowercase', + 'ascii_search_cleanup', + 'cyrillic_diacritical', + 'cyrillic_lowercase', + 'cyrillic_search_cleanup', + 'cyrillic_transliterate_ascii', + 'doublequote_normalize', + 'endline_search_normalize', + 'greek_diacritical', + 'greek_lowercase', + 'greek_normalize', + 'greek_transliterate_ascii', + 'hebrew_transliterate_ascii', + 'hyphen_normalize', + 'inverted_to_normal', + 'latin1_diacritical', + 'latin1_lowercase', + 'latin1_transliterate_ascii', + 'latin-exta_diacritical', + 'latin-exta_lowercase', + 'latin-exta_transliterate_ascii', + 'latin_lowercase', + 'latin_search_cleanup', + 'latin_search_decompose', + 'math_to_ascii', + 'punctuation_normalize', + 'space_normalize', + 'special_decompose', + 'specialwords_search_normalize', + 'tab_search_normalize', + ], + ]; + + /** + * @var int|null + * + * @see getStopWordThresholdValue() + */ + private $stopWordThresholdValue; + + /** + * Transformation processor to normalize search strings. + * + * @var \Ibexa\Core\Persistence\TransformationProcessor + */ + protected $processor; + + /** @var \Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator */ + private $languageMaskGenerator; + + /** + * @param array $configuration + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException On invalid $configuration values + * @throws \Doctrine\DBAL\DBALException + */ + public function __construct( + Connection $connection, + TransformationProcessor $processor, + MaskGenerator $languageMaskGenerator, + array $configuration = [] + ) { + parent::__construct($connection); + + $this->configuration = $configuration + $this->configuration; + $this->processor = $processor; + + if ( + $this->configuration['stopWordThresholdFactor'] < 0 || + $this->configuration['stopWordThresholdFactor'] > 1 + ) { + throw new InvalidArgumentException( + "\$configuration['stopWordThresholdFactor']", + 'Stop Word Threshold Factor needs to be between 0 and 1, got: ' . $this->configuration['stopWordThresholdFactor'] + ); + } + $this->languageMaskGenerator = $languageMaskGenerator; + } + + /** + * Check if this criterion handler accepts to handle the given criterion. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion + * + * @return bool + */ + public function accept(Criterion $criterion) + { + return $criterion instanceof Criterion\FullText; + } + + /** + * Tokenize String. + * + * @param string $string + * + * @return array + */ + protected function tokenizeString($string) + { + return preg_split('/[^\w|*]/u', $string, -1, PREG_SPLIT_NO_EMPTY); + } + + /** + * Get single word query expression. + * + * Depending on the configuration of the full text search criterion + * converter wildcards are either transformed into the respective LIKE + * queries, or everything is just compared using equal. + */ + protected function getWordExpression(QueryBuilder $query, string $token): string + { + if ($this->configuration['enableWildcards'] && $token[0] === '*') { + return $query->expr()->like( + 'word', + $query->createNamedParameter('%' . substr($token, 1)) + ); + } + + if ($this->configuration['enableWildcards'] && $token[strlen($token) - 1] === '*') { + return $query->expr()->like( + 'word', + $query->createNamedParameter(substr($token, 0, -1) . '%') + ); + } + + return $query->expr()->eq('word', $query->createNamedParameter($token)); + } + + /** + * Get sub query to select relevant word IDs. + * + * @uses getStopWordThresholdValue To get threshold for words we would like to ignore in query. + */ + protected function getWordIdSubquery(QueryBuilder $query, string $string): string + { + $subQuery = $this->connection->createQueryBuilder(); + $tokens = $this->tokenizeString( + $this->processor->transform($string, $this->configuration['commands']) + ); + $wordExpressions = []; + foreach ($tokens as $token) { + $wordExpressions[] = $this->getWordExpression($query, $token); + } + + // Search for provided string itself as well + $wordExpressions[] = $this->getWordExpression($query, $string); + + $whereCondition = $subQuery->expr()->orX(...$wordExpressions); + + // If stop word threshold is below 100%, make it part of $whereCondition + if ($this->configuration['stopWordThresholdFactor'] < 1) { + $whereCondition = $subQuery->expr()->andX( + $whereCondition, + $subQuery->expr()->lt( + 'object_count', + $query->createNamedParameter($this->getStopWordThresholdValue(), ParameterType::STRING) + ) + ); + } + + $subQuery + ->select('id') + ->from('ezsearch_word') + ->where($whereCondition); + + return $subQuery->getSQL(); + } + + public function handle( + CriteriaConverter $converter, + QueryBuilder $queryBuilder, + Criterion $criterion, + array $languageSettings + ) { + $subSelect = $this->connection->createQueryBuilder(); + $expr = $queryBuilder->expr(); + $subSelect + ->select( + 'contentobject_id' + )->from( + 'ezsearch_object_word_link' + )->where( + $expr->in( + 'word_id', + // pass main Query Builder to set query parameters + $this->getWordIdSubquery($queryBuilder, $criterion->value) + ) + ); + + if (!empty($languageSettings['languages'])) { + $languageMask = $this->languageMaskGenerator->generateLanguageMaskFromLanguageCodes( + $languageSettings['languages'], + $languageSettings['useAlwaysAvailable'] ?? true + ); + + $subSelect->andWhere( + $expr->gt( + $this->dbPlatform->getBitAndComparisonExpression( + 'ezsearch_object_word_link.language_mask', + $queryBuilder->createNamedParameter($languageMask, ParameterType::INTEGER) + ), + $queryBuilder->createNamedParameter(0, ParameterType::INTEGER) + ) + ); + } + + return $expr->in( + 'c.id', + $subSelect->getSQL() + ); + } + + /** + * Returns an exact content object count threshold to ignore common terms on. + * + * Common terms will be skipped if used in more then a given percentage of the total amount of content + * objects in the database. Percentage is defined by stopWordThresholdFactor configuration. + * + * Example: If stopWordThresholdFactor is 0.66 (66%), and a term like "the" exists in more then 66% of the content, it + * will ignore the phrase as it is assumed to not add any value ot the search. + * + * Caches the result for the instance used as we don't need this to be super accurate as it is based on percentage, + * set by stopWordThresholdFactor. + * + * @return int + */ + protected function getStopWordThresholdValue(): int + { + if ($this->stopWordThresholdValue !== null) { + return $this->stopWordThresholdValue; + } + + // Cached value does not exists, do a simple count query on ezcontentobject table + $query = $this->connection->createQueryBuilder(); + $query + ->select($this->dbPlatform->getCountExpression('id')) + ->from(ContentGateway::CONTENT_ITEM_TABLE); + + $count = (int)$query->execute()->fetchColumn(); + + // Calculate the int stopWordThresholdValue based on count (first column) * factor + return $this->stopWordThresholdValue = (int)($count * $this->configuration['stopWordThresholdFactor']); + } +} + +class_alias(FullText::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FullText'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsContainer.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsContainer.php new file mode 100644 index 0000000000..80189c186e --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsContainer.php @@ -0,0 +1,56 @@ +value; + $isContainer = reset($criterionValue); + + $subSelect = $this->connection->createQueryBuilder(); + $subSelect + ->select( + 'id' + )->from( + ContentTypeGateway::CONTENT_TYPE_TABLE + )->where( + $queryBuilder->expr()->eq( + 'is_container', + $queryBuilder->createNamedParameter((int)$isContainer, ParameterType::INTEGER) + ) + ); + + return $queryBuilder->expr()->in( + 'c.contentclass_id', + $subSelect->getSQL() + ); + } +} diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserBased.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserBased.php new file mode 100644 index 0000000000..9ac812bca2 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserBased.php @@ -0,0 +1,50 @@ +value); + + $subSelect = $this->connection->createQueryBuilder(); + $subSelect + ->select( + 'contentobject_id' + )->from( + 'ezuser' + ); + + $queryExpression = $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + + return $isUserBased + ? $queryExpression + : "NOT({$queryExpression})"; + } +} + +class_alias(IsUserBased::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\IsUserBased'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserEnabled.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserEnabled.php new file mode 100644 index 0000000000..8ed579f052 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/IsUserEnabled.php @@ -0,0 +1,54 @@ +connection->createQueryBuilder(); + $subSelect + ->select('t1.contentobject_id') + ->from(UserGateway::USER_TABLE, 't1') + ->leftJoin( + 't1', + 'ezuser_setting', + 't2', + 't1.contentobject_id = t2.user_id' + ) + ->where( + $queryBuilder->expr()->eq( + 't2.is_enabled', + $queryBuilder->createNamedParameter((int)reset($criterion->value)) + ) + ); + + return $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + } +} + +class_alias(IsUserEnabled::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\IsUserEnabled'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php new file mode 100644 index 0000000000..52d734ed49 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LanguageCode.php @@ -0,0 +1,63 @@ +maskGenerator = $maskGenerator; + } + + /** + * Check if this criterion handler accepts to handle the given criterion. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion + * + * @return bool + */ + public function accept(Criterion $criterion) + { + return $criterion instanceof Criterion\LanguageCode; + } + + public function handle( + CriteriaConverter $converter, + QueryBuilder $queryBuilder, + Criterion $criterion, + array $languageSettings + ) { + /* @var $criterion \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LanguageCode */ + return $queryBuilder->expr()->gt( + $this->dbPlatform->getBitAndComparisonExpression( + 'c.language_mask', + $this->maskGenerator->generateLanguageMaskFromLanguageCodes( + $criterion->value, + $criterion->matchAlwaysAvailable + ) + ), + 0 + ); + } +} + +class_alias(LanguageCode::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LanguageCode'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalAnd.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalAnd.php new file mode 100644 index 0000000000..bb751a7fff --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalAnd.php @@ -0,0 +1,50 @@ +criteria as $subCriterion) { + $subexpressions[] = $converter->convertCriteria( + $queryBuilder, + $subCriterion, + $languageSettings + ); + } + + return $queryBuilder->expr()->andX(...$subexpressions); + } +} + +class_alias(LogicalAnd::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LogicalAnd'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalNot.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalNot.php new file mode 100644 index 0000000000..3ee7879967 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalNot.php @@ -0,0 +1,44 @@ +convertCriteria($queryBuilder, $criterion->criteria[0], $languageSettings) + ); + } +} + +class_alias(LogicalNot::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LogicalNot'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalOr.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalOr.php new file mode 100644 index 0000000000..8d4ee794b7 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/LogicalOr.php @@ -0,0 +1,50 @@ +criteria as $subCriterion) { + $subexpressions[] = $converter->convertCriteria( + $queryBuilder, + $subCriterion, + $languageSettings + ); + } + + return $queryBuilder->expr()->orX(...$subexpressions); + } +} + +class_alias(LogicalOr::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\LogicalOr'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php new file mode 100644 index 0000000000..16dc37890a --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MapLocationDistance.php @@ -0,0 +1,288 @@ +contentTypeHandler->getSearchableFieldMap(); + + foreach ($fieldMap as $fieldIdentifierMap) { + // First check if field exists in the current ContentType, there is nothing to do if it doesn't + if ( + !( + isset($fieldIdentifierMap[$fieldIdentifier]) + && $fieldIdentifierMap[$fieldIdentifier]['field_type_identifier'] === 'ezgmaplocation' + ) + ) { + continue; + } + + $fieldDefinitionIdList[] = $fieldIdentifierMap[$fieldIdentifier]['field_definition_id']; + } + + if (empty($fieldDefinitionIdList)) { + throw new InvalidArgumentException( + '$criterion->target', + "No searchable Fields found for the provided Criterion target '{$fieldIdentifier}'." + ); + } + + return $fieldDefinitionIdList; + } + + protected function kilometersToDegrees($kilometers) + { + return $kilometers / self::DEGREE_KM; + } + + public function handle( + CriteriaConverter $converter, + QueryBuilder $queryBuilder, + Criterion $criterion, + array $languageSettings + ) { + $fieldDefinitionIds = $this->getFieldDefinitionIds($criterion->target); + $subSelect = $this->connection->createQueryBuilder(); + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Value\MapLocationValue $location */ + $location = $criterion->valueData; + + // note: avoid using literal names for parameters to account for multiple visits of the same Criterion + $latitudePlaceholder = $queryBuilder->createNamedParameter($location->latitude); + $longitudePlaceholder = $queryBuilder->createNamedParameter($location->longitude); + $correctionPlaceholder = $queryBuilder->createNamedParameter( + cos(deg2rad($location->latitude)) ** 2 + ); + + // build: (latitude1 - latitude2)^2 + (longitude2 - longitude2)^2 * longitude_correction) + $latitudeSubstrExpr = "(map.latitude - {$latitudePlaceholder})"; + $longitudeSubstrExpr = "(map.longitude - {$longitudePlaceholder})"; + $latitudeExpr = "{$latitudeSubstrExpr} * {$latitudeSubstrExpr}"; + $longitudeExpr = "{$longitudeSubstrExpr} * {$longitudeSubstrExpr} * {$correctionPlaceholder}"; + $distanceExpression = "{$latitudeExpr} + {$longitudeExpr}"; + + $expr = $subSelect->expr(); + switch ($criterion->operator) { + case Criterion\Operator::IN: + case Criterion\Operator::EQ: + case Criterion\Operator::GT: + case Criterion\Operator::GTE: + case Criterion\Operator::LT: + case Criterion\Operator::LTE: + $operatorFunction = $this->comparatorMap[$criterion->operator]; + $distanceInDegrees = $this->kilometersToDegrees($criterion->value) ** 2; + $distanceFilter = $expr->$operatorFunction( + $distanceExpression, + $this->dbPlatform->getRoundExpression( + $queryBuilder->createNamedParameter($distanceInDegrees), + 10 + ) + ); + break; + + case Criterion\Operator::BETWEEN: + $distanceInDegrees1 = $this->kilometersToDegrees($criterion->value[0]) ** 2; + $distanceInDegrees2 = $this->kilometersToDegrees($criterion->value[1]) ** 2; + $distanceFilter = $this->dbPlatform->getBetweenExpression( + $distanceExpression, + $this->dbPlatform->getRoundExpression( + $queryBuilder->createNamedParameter($distanceInDegrees1), + 10 + ), + $this->dbPlatform->getRoundExpression( + $queryBuilder->createNamedParameter($distanceInDegrees2), + 10 + ), + ); + break; + + default: + throw new RuntimeException('Unknown operator.'); + } + + // Calculate bounding box if possible + // @todo consider covering operators EQ and IN as well + $boundingConstraints = []; + switch ($criterion->operator) { + case Criterion\Operator::LT: + case Criterion\Operator::LTE: + $distanceUpper = $criterion->value; + break; + case Criterion\Operator::BETWEEN: + $distanceUpper = $criterion->value[0] > $criterion->value[1] ? + $criterion->value[0] : + $criterion->value[1]; + break; + default: + // Skip other operators + break; + } + if (isset($distanceUpper)) { + $boundingConstraints = $this->getBoundingConstraints( + $queryBuilder, + $location, + $distanceUpper + ); + } + + $subSelect + ->select('contentobject_id') + ->from('ezcontentobject_attribute', 'f_def') + ->innerJoin( + 'f_def', + 'ezgmaplocation', + 'map', + $expr->andX( + 'map.contentobject_version = f_def.version', + 'map.contentobject_attribute_id = f_def.id', + ...$boundingConstraints + ) + ) + // note: joining by correlation on outer table + ->where('f_def.version = c.current_version') + ->andWhere( + $expr->in( + 'f_def.contentclassattribute_id', + $queryBuilder->createNamedParameter($fieldDefinitionIds, Connection::PARAM_INT_ARRAY) + ) + ) + ->andWhere($distanceFilter) + ->andWhere($this->getFieldCondition($queryBuilder, $languageSettings)) + ; + + return $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + } + + /** + * Credit for the formula goes to http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates. + */ + protected function getBoundingConstraints( + QueryBuilder $query, + MapLocationValue $location, + float $distance + ): array { + $boundingCoordinates = $this->getBoundingCoordinates($location, $distance); + + $expr = $query->expr(); + + return [ + $expr->gte( + 'map.latitude', + $query->createNamedParameter($boundingCoordinates['lowLatitude']) + ), + $expr->gte( + 'map.longitude', + $query->createNamedParameter($boundingCoordinates['lowLongitude']) + ), + $expr->lte( + 'map.latitude', + $query->createNamedParameter($boundingCoordinates['highLatitude']) + ), + $expr->lte( + 'map.longitude', + $query->createNamedParameter($boundingCoordinates['highLongitude']) + ), + ]; + } + + /** + * Calculates and returns bounding box coordinates. + * + * Credits: http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Value\MapLocationValue $location + * @param float $distance + * + * @return array + * + * @todo it should also be possible to calculate inner bounding box, which could be applied for the + * operators GT, GTE and lower distance of the BETWEEN operator. + */ + protected function getBoundingCoordinates(MapLocationValue $location, $distance) + { + $radiansLatitude = deg2rad($location->latitude); + $radiansLongitude = deg2rad($location->longitude); + $angularDistance = $distance / self::EARTH_RADIUS; + $deltaLongitude = asin(sin($angularDistance) / cos($radiansLatitude)); + + $lowLatitudeRadians = $radiansLatitude - $angularDistance; + $highLatitudeRadians = $radiansLatitude + $angularDistance; + + // Check that bounding box does not include poles. + if ($lowLatitudeRadians > -M_PI_2 && $highLatitudeRadians < M_PI_2) { + $boundingCoordinates = [ + 'lowLatitude' => rad2deg($lowLatitudeRadians), + 'lowLongitude' => rad2deg($radiansLongitude - $deltaLongitude), + 'highLatitude' => rad2deg($highLatitudeRadians), + 'highLongitude' => rad2deg($radiansLongitude + $deltaLongitude), + ]; + } else { + // Handle the pole(s) being inside a bounding box, in this case we MUST cover + // full circle of Earth's longitude and one or both poles. + // Note that calculation for distances over the polar regions with flat Earth formula + // will be VERY imprecise. + $boundingCoordinates = [ + 'lowLatitude' => rad2deg(max($lowLatitudeRadians, -M_PI_2)), + 'lowLongitude' => rad2deg(-M_PI), + 'highLatitude' => rad2deg(min($highLatitudeRadians, M_PI_2)), + 'highLongitude' => rad2deg(M_PI), + ]; + } + + return $boundingCoordinates; + } +} + +class_alias(MapLocationDistance::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\MapLocationDistance'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MatchAll.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MatchAll.php new file mode 100644 index 0000000000..0c38f0ed7e --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/MatchAll.php @@ -0,0 +1,41 @@ +connection->createQueryBuilder(); + $value = (array)$criterion->value; + $subSelect + ->select( + 'contentobject_id' + )->from( + 'ezcobj_state_link' + )->where( + $queryBuilder->expr()->in( + 'contentobject_state_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ) + ); + + return $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + } +} + +class_alias(ObjectStateId::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ObjectStateId'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ObjectStateIdentifier.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ObjectStateIdentifier.php new file mode 100644 index 0000000000..a67ef2830c --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/ObjectStateIdentifier.php @@ -0,0 +1,77 @@ +value; + $matchStateIdentifier = $queryBuilder->expr()->in( + 't2.identifier', + $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) + ); + + if (null !== $criterion->target) { + $criterionTarget = (array)$criterion->target; + $constraints = $queryBuilder->expr()->andX( + $queryBuilder->expr()->in( + 't3.identifier', + $queryBuilder->createNamedParameter( + $criterionTarget, + Connection::PARAM_STR_ARRAY + ) + ), + $matchStateIdentifier + ); + } else { + $constraints = $matchStateIdentifier; + } + + $subSelect = $this->connection->createQueryBuilder(); + $subSelect + ->select('t1.contentobject_id') + ->from('ezcobj_state_link', 't1') + ->leftJoin( + 't1', + 'ezcobj_state', + 't2', + 't1.contentobject_state_id = t2.id', + ) + ->leftJoin( + 't2', + 'ezcobj_state_group', + 't3', + 't2.group_id = t3.id' + ) + ->where($constraints); + + return $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + } +} + +class_alias(ObjectStateIdentifier::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\ObjectStateIdentifier'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/RemoteId.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/RemoteId.php new file mode 100644 index 0000000000..c0293e280f --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/RemoteId.php @@ -0,0 +1,47 @@ +value; + + return $queryBuilder->expr()->in( + 'c.remote_id', + $queryBuilder->createNamedParameter($remoteIds, Connection::PARAM_STR_ARRAY) + ); + } +} + +class_alias(RemoteId::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\RemoteId'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionId.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionId.php new file mode 100644 index 0000000000..462d4096ef --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionId.php @@ -0,0 +1,47 @@ +value; + + return $queryBuilder->expr()->in( + 'c.section_id', + $queryBuilder->createNamedParameter($sectionIds, Connection::PARAM_INT_ARRAY) + ); + } +} + +class_alias(SectionId::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\SectionId'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionIdentifier.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionIdentifier.php new file mode 100644 index 0000000000..f33b424060 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/SectionIdentifier.php @@ -0,0 +1,55 @@ +connection->createQueryBuilder(); + $value = (array)$criterion->value; + $subSelect + ->select('t1.id') + ->from('ezcontentobject', 't1') + ->leftJoin( + 't1', + 'ezsection', + 't2', + 't1.section_id = t2.id' + ) + ->where( + $queryBuilder->expr()->in( + 't2.identifier', + $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) + ) + ); + + return $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + } +} + +class_alias(SectionIdentifier::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\SectionIdentifier'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserEmail.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserEmail.php new file mode 100644 index 0000000000..3142b34d7e --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserEmail.php @@ -0,0 +1,81 @@ +transformationProcessor = $transformationProcessor; + } + + public function accept(Criterion $criterion): bool + { + return $criterion instanceof Criterion\UserEmail; + } + + public function handle( + CriteriaConverter $converter, + QueryBuilder $queryBuilder, + Criterion $criterion, + array $languageSettings + ) { + if (Criterion\Operator::LIKE === $criterion->operator) { + $expression = $queryBuilder->expr()->like( + 't1.email', + $queryBuilder->createNamedParameter( + str_replace( + '*', + '%', + addcslashes( + $this->transformationProcessor->transformByGroup( + $criterion->value, + 'lowercase' + ), + '%_' + ) + ) + ) + ); + } else { + $value = (array)$criterion->value; + $expression = $queryBuilder->expr()->in( + 't1.email', + $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) + ); + } + + $subSelect = $this->connection->createQueryBuilder(); + $subSelect + ->select('t1.contentobject_id') + ->from('ezuser', 't1') + ->where($expression); + + return $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + } +} + +class_alias(UserEmail::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserEmail'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserId.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserId.php new file mode 100644 index 0000000000..c7c4e52e9c --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserId.php @@ -0,0 +1,50 @@ +connection->createQueryBuilder(); + $value = (array)$criterion->value; + $subSelect + ->select('t1.contentobject_id') + ->from(UserGateway::USER_TABLE, 't1') + ->where( + $queryBuilder->expr()->in( + 't1.contentobject_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ) + ); + + return $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + } +} + +class_alias(UserId::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserId'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserLogin.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserLogin.php new file mode 100644 index 0000000000..be2dffa25c --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserLogin.php @@ -0,0 +1,82 @@ +transformationProcessor = $transformationProcessor; + } + + public function accept(Criterion $criterion): bool + { + return $criterion instanceof Criterion\UserLogin; + } + + public function handle( + CriteriaConverter $converter, + QueryBuilder $queryBuilder, + Criterion $criterion, + array $languageSettings + ) { + $expr = $queryBuilder->expr(); + if (Criterion\Operator::LIKE === $criterion->operator) { + $expression = $expr->like( + 't1.login', + $queryBuilder->createNamedParameter( + str_replace( + '*', + '%', + addcslashes( + $this->transformationProcessor->transformByGroup( + $criterion->value, + 'lowercase' + ), + '%_' + ) + ) + ) + ); + } else { + $value = (array)$criterion->value; + $expression = $expr->in( + 't1.login', + $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) + ); + } + + $subSelect = $this->connection->createQueryBuilder(); + $subSelect + ->select('t1.contentobject_id') + ->from('ezuser', 't1') + ->where($expression); + + return $expr->in( + 'c.id', + $subSelect->getSQL() + ); + } +} + +class_alias(UserLogin::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserLogin'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserMetadata.php b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserMetadata.php new file mode 100644 index 0000000000..4da42b0668 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/CriterionHandler/UserMetadata.php @@ -0,0 +1,93 @@ +value; + switch ($criterion->target) { + case Criterion\UserMetadata::MODIFIER: + return $queryBuilder->expr()->in( + 'v.creator_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ); + + case Criterion\UserMetadata::GROUP: + $subSelect = $this->connection->createQueryBuilder(); + $subSelect + ->select( + 't1.contentobject_id' + )->from( + LocationGateway::CONTENT_TREE_TABLE, + 't1' + )->innerJoin( + 't1', + LocationGateway::CONTENT_TREE_TABLE, + 't2', + $queryBuilder->expr()->like( + 't1.path_string', + $this->dbPlatform->getConcatExpression( + 't2.path_string', + $queryBuilder->createNamedParameter('%', ParameterType::STRING) + ) + ) + )->where( + $queryBuilder->expr()->in( + 't2.contentobject_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ) + ); + + return $queryBuilder->expr()->in( + 'c.owner_id', + $subSelect->getSQL() + ); + + case Criterion\UserMetadata::OWNER: + return $queryBuilder->expr()->in( + 'c.owner_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ); + default: + break; + } + + throw new RuntimeException("Invalid target Criterion: '" . $criterion->target . "'"); + } +} + +class_alias(UserMetadata::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\UserMetadata'); diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseConverter.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseConverter.php similarity index 83% rename from eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseConverter.php rename to src/lib/Search/Legacy/Content/Common/Gateway/SortClauseConverter.php index 5de6c614b3..01450d0537 100644 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseConverter.php +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseConverter.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway; +namespace Ibexa\Core\Search\Legacy\Content\Common\Gateway; use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; use RuntimeException; /** @@ -18,7 +18,7 @@ class SortClauseConverter /** * Sort clause handlers. * - * @var \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler[] + * @var \Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler[] */ protected $handlers; @@ -32,7 +32,7 @@ class SortClauseConverter /** * Construct from an optional array of sort clause handlers. * - * @param \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler[] $handlers + * @param \Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler[] $handlers */ public function __construct(array $handlers = []) { @@ -42,7 +42,7 @@ public function __construct(array $handlers = []) /** * Adds handler. * - * @param \eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler $handler + * @param \Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler $handler */ public function addHandler(SortClauseHandler $handler) { @@ -53,7 +53,7 @@ public function addHandler(SortClauseHandler $handler) * Apply select parts of sort clauses to query. * * @param \Doctrine\DBAL\Query\QueryBuilder $query - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause[] $sortClauses * * @throws \RuntimeException If no handler is available for sort clause */ @@ -85,7 +85,7 @@ public function applySelect(QueryBuilder $query, array $sortClauses): void * * @throws \RuntimeException If no handler is available for sort clause * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $sortClauses + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause[] $sortClauses * @param array $languageSettings */ public function applyJoin(QueryBuilder $query, array $sortClauses, array $languageSettings): void @@ -121,3 +121,5 @@ public function applyOrderBy(QueryBuilder $query): void $this->sortColumns = []; } } + +class_alias(SortClauseConverter::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter'); diff --git a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler.php similarity index 83% rename from eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler.php rename to src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler.php index e0252117e2..b5a168bc95 100644 --- a/eZ/Publish/Core/Search/Legacy/Content/Common/Gateway/SortClauseHandler.php +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Legacy\Content\Common\Gateway; +namespace Ibexa\Core\Search\Legacy\Content\Common\Gateway; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; /** * Handler for a single sort clause. @@ -33,7 +33,7 @@ public function __construct(Connection $connection) /** * Check if this sort clause handler accepts to handle the given sort clause. * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause $sortClause * * @return bool */ @@ -46,7 +46,7 @@ abstract public function accept(SortClause $sortClause); * used for sorting. * * @param \Doctrine\DBAL\Query\QueryBuilder $query - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause $sortClause * @param int $number * * @return array column names to be used when sorting @@ -91,3 +91,5 @@ protected function getSortTableName($number, $externalTableName = null) return 'sort_table_' . ($externalTableName !== null ? $externalTableName . '_' : '') . $number; } } + +class_alias(SortClauseHandler::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/AbstractRandom.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/AbstractRandom.php new file mode 100644 index 0000000000..66cd7994d6 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/AbstractRandom.php @@ -0,0 +1,52 @@ +addSelect( + sprintf( + '%s AS %s', + $this->getRandomFunctionName($sortClause->targetData->seed), + $column = $this->getSortColumnName($number) + ) + ); + + return [$column]; + } + + abstract public function getRandomFunctionName(?int $seed): string; + + abstract public function getDriverName(): string; +} + +class_alias(AbstractRandom::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentId.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentId.php new file mode 100644 index 0000000000..12dbacdf12 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentId.php @@ -0,0 +1,47 @@ +addSelect( + sprintf( + 'c.id AS %s', + $column = $this->getSortColumnName($number) + ) + ); + + return [$column]; + } +} + +class_alias(ContentId::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\ContentId'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentName.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentName.php new file mode 100644 index 0000000000..177bb8101e --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/ContentName.php @@ -0,0 +1,47 @@ +addSelect( + sprintf( + 'c.name AS %s', + $column = $this->getSortColumnName($number) + ) + ); + + return (array)$column; + } +} + +class_alias(ContentName::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\ContentName'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DateModified.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DateModified.php new file mode 100644 index 0000000000..7e02069cf1 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DateModified.php @@ -0,0 +1,47 @@ +addSelect( + sprintf( + 'c.modified AS %s', + $column = $this->getSortColumnName($number) + ) + ); + + return [$column]; + } +} + +class_alias(DateModified::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\DateModified'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DatePublished.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DatePublished.php new file mode 100644 index 0000000000..7779e01e46 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/DatePublished.php @@ -0,0 +1,47 @@ +addSelect( + sprintf( + 'c.published AS %s', + $column = $this->getSortColumnName($number) + ) + ); + + return [$column]; + } +} + +class_alias(DatePublished::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\DatePublished'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Factory/RandomSortClauseHandlerFactory.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Factory/RandomSortClauseHandlerFactory.php new file mode 100644 index 0000000000..b893654830 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Factory/RandomSortClauseHandlerFactory.php @@ -0,0 +1,47 @@ +connection = $connection; + $this->randomSortClauseGateways = $randomSortClauseGateways; + } + + /** + * @throws \Doctrine\DBAL\DBALException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + */ + public function getGateway(): AbstractRandom + { + $driverName = $this->connection->getDatabasePlatform()->getName(); + + foreach ($this->randomSortClauseGateways as $gateway) { + if ($gateway->getDriverName() === $driverName) { + return $gateway; + } + } + + throw new InvalidArgumentException('$this->randomSortClauseGateways', 'No RandomSortClauseHandler found for driver ' . $driverName); + } +} + +class_alias(RandomSortClauseHandlerFactory::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Factory\RandomSortClauseHandlerFactory'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Field.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Field.php new file mode 100644 index 0000000000..8e231d518c --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Field.php @@ -0,0 +1,241 @@ +languageHandler = $languageHandler; + $this->contentTypeHandler = $contentTypeHandler; + } + + /** + * Check if this sort clause handler accepts to handle the given sort clause. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause $sortClause + * + * @return bool + */ + public function accept(SortClause $sortClause) + { + return $sortClause instanceof SortClause\Field; + } + + /** + * Apply selects to the query. + * + * Returns the name of the (aliased) column, which information should be + * used for sorting. + * + * @param \Doctrine\DBAL\Query\QueryBuilder $query + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause $sortClause + * @param int $number + * + * @return array + */ + public function applySelect( + QueryBuilder $query, + SortClause $sortClause, + int $number + ): array { + $query + ->addSelect( + sprintf( + '%s AS %s', + $query->expr()->isNotNull( + $this->getSortTableName($number) . '.sort_key_int' + ), + $column1 = $this->getSortColumnName($number . '_null') + ), + sprintf( + '%s AS %s', + $query->expr()->isNotNull( + $this->getSortTableName($number) . '.sort_key_string' + ), + $column2 = $this->getSortColumnName($number . '_bis_null') + ), + sprintf( + '%s AS %s', + $this->getSortTableName($number) . '.sort_key_int', + $column3 = $this->getSortColumnName($number) + ), + sprintf( + '%s AS %s', + $this->getSortTableName($number) . '.sort_key_string', + $column4 = $this->getSortColumnName($number . '_bis') + ) + ); + + return [$column1, $column2, $column3, $column4]; + } + + public function applyJoin( + QueryBuilder $query, + SortClause $sortClause, + int $number, + array $languageSettings + ): void { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Target\FieldTarget $fieldTarget */ + $fieldTarget = $sortClause->targetData; + $fieldMap = $this->contentTypeHandler->getSearchableFieldMap(); + + if (!isset($fieldMap[$fieldTarget->typeIdentifier][$fieldTarget->fieldIdentifier]['field_definition_id'])) { + throw new InvalidArgumentException( + '$sortClause->targetData', + 'No searchable Fields found for the provided Sort Clause target ' . + "'{$fieldTarget->fieldIdentifier}' on '{$fieldTarget->typeIdentifier}'." + ); + } + + $fieldDefinitionId = $fieldMap[$fieldTarget->typeIdentifier][$fieldTarget->fieldIdentifier]['field_definition_id']; + $table = $this->getSortTableName($number); + + $tableAlias = $this->connection->quoteIdentifier($table); + $query + ->leftJoin( + 'c', + Gateway::CONTENT_FIELD_TABLE, + $tableAlias, + $query->expr()->andX( + $query->expr()->eq( + $query->createNamedParameter( + $fieldDefinitionId, + ParameterType::INTEGER + ), + $tableAlias . '.contentclassattribute_id' + ), + $query->expr()->eq( + $tableAlias . '.contentobject_id', + 'c.id' + ), + $query->expr()->eq( + $tableAlias . '.version', + 'c.current_version' + ), + $this->getFieldCondition($query, $languageSettings, $table) + ) + ); + } + + protected function getFieldCondition( + QueryBuilder $query, + array $languageSettings, + $fieldTableName + ) { + // 1. Use main language(s) by default + if (empty($languageSettings['languages'])) { + return $query->expr()->gt( + $this->dbPlatform->getBitAndComparisonExpression( + 'c.initial_language_id', + $fieldTableName . '.language_id' + ), + $query->createNamedParameter(0, ParameterType::INTEGER) + ); + } + + // 2. Otherwise use prioritized languages + $leftSide = $this->dbPlatform->getBitAndComparisonExpression( + sprintf( + 'c.language_mask - %s', + $this->dbPlatform->getBitAndComparisonExpression( + 'c.language_mask', + $fieldTableName . '.language_id' + ) + ), + $query->createNamedParameter(1, ParameterType::INTEGER) + ); + $rightSide = $this->dbPlatform->getBitAndComparisonExpression( + $fieldTableName . '.language_id', + $query->createNamedParameter(1, ParameterType::INTEGER) + ); + + for ($index = count( + $languageSettings['languages'] + ) - 1, $multiplier = 2; $index >= 0; $index--, $multiplier *= 2) { + $languageId = $this->languageHandler + ->loadByLanguageCode($languageSettings['languages'][$index])->id; + + $addToLeftSide = $this->dbPlatform->getBitAndComparisonExpression( + sprintf( + 'c.language_mask - %s', + $this->dbPlatform->getBitAndComparisonExpression( + 'c.language_mask', + $fieldTableName . '.language_id' + ) + ), + $query->createNamedParameter($languageId, ParameterType::INTEGER) + ); + $addToRightSide = $this->dbPlatform->getBitAndComparisonExpression( + $fieldTableName . '.language_id', + $query->createNamedParameter($languageId, ParameterType::INTEGER) + ); + + if ($multiplier > $languageId) { + $factor = $multiplier / $languageId; + for ($shift = 0; $factor > 1; $factor = $factor / 2, $shift++); + $factorTerm = ' << ' . $shift; + $addToLeftSide .= $factorTerm; + $addToRightSide .= $factorTerm; + } elseif ($multiplier < $languageId) { + $factor = $languageId / $multiplier; + for ($shift = 0; $factor > 1; $factor = $factor / 2, $shift++); + $factorTerm = ' >> ' . $shift; + $addToLeftSide .= $factorTerm; + $addToRightSide .= $factorTerm; + } + + $leftSide = "$leftSide + ($addToLeftSide)"; + $rightSide = "$rightSide + ($addToRightSide)"; + } + + return $query->expr()->andX( + $query->expr()->gt( + $this->dbPlatform->getBitAndComparisonExpression( + 'c.language_mask', + $fieldTableName . '.language_id' + ), + $query->createNamedParameter(0, ParameterType::INTEGER) + ), + $query->expr()->lt($leftSide, $rightSide) + ); + } +} + +class_alias(Field::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Field'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/MapLocationDistance.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/MapLocationDistance.php new file mode 100644 index 0000000000..79c0888b38 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/MapLocationDistance.php @@ -0,0 +1,125 @@ +targetData; + $externalTable = $this->getSortTableName($number, 'ezgmaplocation'); + + // note: avoid using literal names for parameters to account for multiple visits of the same Criterion + $latitudePlaceholder = $query->createNamedParameter($target->latitude); + $longitudePlaceholder = $query->createNamedParameter($target->longitude); + + // note: can have literal name for all visits of this Criterion because it's constant + $query->setParameter(':longitude_correction', cos(deg2rad($target->latitude)) ** 2); + + // build: (latitude1 - latitude2)^2 + (longitude2 - longitude2)^2 * longitude_correction) + $latitudeSubstrExpr = "({$externalTable}.latitude - {$latitudePlaceholder})"; + $longitudeSubstrExpr = "({$externalTable}.longitude - {$longitudePlaceholder})"; + $latitudeExpr = "{$latitudeSubstrExpr} * {$latitudeSubstrExpr}"; + $longitudeExpr = "{$longitudeSubstrExpr} * {$longitudeSubstrExpr} * :longitude_correction"; + $distanceExpression = "{$latitudeExpr} + {$longitudeExpr}"; + + $query->addSelect( + sprintf('%s AS %s', $distanceExpression, $column1 = $this->getSortColumnName($number)) + ); + + return [$column1]; + } + + public function applyJoin( + QueryBuilder $query, + SortClause $sortClause, + int $number, + array $languageSettings + ): void { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Target\FieldTarget $fieldTarget */ + $fieldTarget = $sortClause->targetData; + $fieldMap = $this->contentTypeHandler->getSearchableFieldMap(); + + if (!isset($fieldMap[$fieldTarget->typeIdentifier][$fieldTarget->fieldIdentifier]['field_definition_id'])) { + throw new InvalidArgumentException( + '$sortClause->targetData', + 'No searchable Fields found for the provided Sort Clause target ' . + "'{$fieldTarget->fieldIdentifier}' on '{$fieldTarget->typeIdentifier}'." + ); + } + + $fieldDefinitionId = $fieldMap[$fieldTarget->typeIdentifier][$fieldTarget->fieldIdentifier]['field_definition_id']; + $table = $this->getSortTableName($number); + $externalTable = $this->getSortTableName($number, 'ezgmaplocation'); + + $tableAlias = $this->connection->quoteIdentifier($table); + $externalTableAlias = $this->connection->quoteIdentifier($externalTable); + $query + ->leftJoin( + 'c', + ContentGateway::CONTENT_FIELD_TABLE, + $tableAlias, + $query->expr()->andX( + $query->expr()->eq( + $query->createNamedParameter($fieldDefinitionId, ParameterType::INTEGER), + $tableAlias . '.contentclassattribute_id' + ), + $query->expr()->eq( + $tableAlias . '.contentobject_id', + 'c.id' + ), + $query->expr()->eq( + $tableAlias . '.version', + 'c.current_version' + ), + $this->getFieldCondition($query, $languageSettings, $tableAlias) + ) + ) + ->leftJoin( + $tableAlias, + 'ezgmaplocation', + $externalTableAlias, + $query->expr()->andX( + $query->expr()->eq( + $externalTableAlias . '.contentobject_version', + $tableAlias . '.version' + ), + $query->expr()->eq( + $externalTableAlias . '.contentobject_attribute_id', + $tableAlias . '.id' + ) + ) + ); + } +} + +class_alias(MapLocationDistance::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\MapLocationDistance'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/MySqlRandom.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/MySqlRandom.php new file mode 100644 index 0000000000..bb05687696 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Random/MySqlRandom.php @@ -0,0 +1,26 @@ +addSelect( + sprintf( + 'c.section_id AS %s', + $column = $this->getSortColumnName($number) + ) + ); + + return [$column]; + } +} + +class_alias(SectionIdentifier::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\SectionIdentifier'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/SectionName.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/SectionName.php new file mode 100644 index 0000000000..bb181ae479 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/SectionName.php @@ -0,0 +1,64 @@ +addSelect( + sprintf( + '%s AS %s', + $this->getSortTableName($number) . '.name', + $column = $this->getSortColumnName($number) + ) + ); + + return [$column]; + } + + public function applyJoin( + QueryBuilder $query, + SortClause $sortClause, + int $number, + array $languageSettings + ): void { + $table = $this->getSortTableName($number); + $query + ->leftJoin( + 'c', + 'ezsection', + $table, + "{$table}.id = c.section_id" + ); + } +} + +class_alias(SectionName::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\SectionName'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/ContentTypeName.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/ContentTypeName.php new file mode 100644 index 0000000000..9fb6c5375d --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/ContentTypeName.php @@ -0,0 +1,62 @@ +addSelect( + sprintf( + 'ctn.name AS %s', + $column = $this->getSortColumnName($number) + ) + ); + + return [$column]; + } + + public function applyJoin( + QueryBuilder $query, + SortClause $sortClause, + int $number, + array $languageSettings + ): void { + $query->innerJoin( + 'c', + ContentTypeGateway::CONTENT_TYPE_TABLE, + 'ct', + 'c.contentclass_id = ct.id' + )->innerJoin( + 'ct', + ContentTypeGateway::CONTENT_TYPE_NAME_TABLE, + 'ctn', + 'ctn.contentclass_id = ct.id' + ); + } +} + +class_alias(ContentTypeName::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Trash\ContentTypeName'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/DateTrashed.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/DateTrashed.php new file mode 100644 index 0000000000..fb94564971 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/DateTrashed.php @@ -0,0 +1,42 @@ +addSelect( + sprintf( + 't.trashed AS %s', + $column = $this->getSortColumnName($number) + ) + ); + + return (array)$column; + } +} + +class_alias(DateTrashed::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Trash\DateTrashed'); diff --git a/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/UserLogin.php b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/UserLogin.php new file mode 100644 index 0000000000..7612dace79 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Common/Gateway/SortClauseHandler/Trash/UserLogin.php @@ -0,0 +1,57 @@ +addSelect( + sprintf( + 'u.login AS %s', + $column = $this->getSortColumnName($number) + ) + ); + + return (array)$column; + } + + public function applyJoin( + QueryBuilder $query, + SortClause $sortClause, + int $number, + array $languageSettings + ): void { + $query->leftJoin( + 'c', + UserGateway::USER_TABLE, + 'u', + 'c.owner_id = u.contentobject_id' + ); + } +} + +class_alias(UserLogin::class, 'eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Trash\UserLogin'); diff --git a/eZ/Publish/Core/Search/Legacy/Content/FullTextData.php b/src/lib/Search/Legacy/Content/FullTextData.php similarity index 76% rename from eZ/Publish/Core/Search/Legacy/Content/FullTextData.php rename to src/lib/Search/Legacy/Content/FullTextData.php index 26c77c232f..4edffb6c47 100644 --- a/eZ/Publish/Core/Search/Legacy/Content/FullTextData.php +++ b/src/lib/Search/Legacy/Content/FullTextData.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Legacy\Content; +namespace Ibexa\Core\Search\Legacy\Content; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; /** * Represents full text data of FullTextValue(s) for a Content object. @@ -44,7 +44,9 @@ class FullTextData extends ValueObject /** * List of FullTextValue objects corresponding to content object fields (per translation). * - * @var \eZ\Publish\Core\Search\Legacy\Content\FullTextValue[] + * @var \Ibexa\Core\Search\Legacy\Content\FullTextValue[] */ public $values; } + +class_alias(FullTextData::class, 'eZ\Publish\Core\Search\Legacy\Content\FullTextData'); diff --git a/eZ/Publish/Core/Search/Legacy/Content/FullTextValue.php b/src/lib/Search/Legacy/Content/FullTextValue.php similarity index 86% rename from eZ/Publish/Core/Search/Legacy/Content/FullTextValue.php rename to src/lib/Search/Legacy/Content/FullTextValue.php index fed59525aa..eba5b4cfa2 100644 --- a/eZ/Publish/Core/Search/Legacy/Content/FullTextValue.php +++ b/src/lib/Search/Legacy/Content/FullTextValue.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Legacy\Content; +namespace Ibexa\Core\Search\Legacy\Content; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; /** * Represents full text searchable value of Content object field which can be indexed by the legacy search engine. @@ -65,3 +65,5 @@ class FullTextValue extends ValueObject */ public $splitFlag; } + +class_alias(FullTextValue::class, 'eZ\Publish\Core\Search\Legacy\Content\FullTextValue'); diff --git a/src/lib/Search/Legacy/Content/Gateway.php b/src/lib/Search/Legacy/Content/Gateway.php new file mode 100644 index 0000000000..a989baf18d --- /dev/null +++ b/src/lib/Search/Legacy/Content/Gateway.php @@ -0,0 +1,42 @@ +value as $value) { + foreach (explode('/', trim($value, '/')) as $id) { + $idSet[$id] = true; + } + } + + $subSelect = $this->connection->createQueryBuilder(); + $subSelect + ->select('contentobject_id') + ->from('ezcontentobject_tree') + ->where( + $queryBuilder->expr()->in( + 'node_id', + array_keys($idSet) + ) + ); + + return $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + } +} + +class_alias(Ancestor::class, 'eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\Ancestor'); diff --git a/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/LocationId.php b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/LocationId.php new file mode 100644 index 0000000000..1e317dc3c4 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/LocationId.php @@ -0,0 +1,59 @@ +value; + $subSelect = $this->connection->createQueryBuilder(); + $subSelect + ->select( + 'contentobject_id' + )->from( + 'ezcontentobject_tree' + )->where( + $queryBuilder->expr()->in( + 'node_id', + $queryBuilder->createNamedParameter($locationIds, Connection::PARAM_INT_ARRAY) + ) + ); + + return $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + } +} + +class_alias(LocationId::class, 'eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\LocationId'); diff --git a/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/LocationRemoteId.php b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/LocationRemoteId.php new file mode 100644 index 0000000000..2744257e40 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/LocationRemoteId.php @@ -0,0 +1,60 @@ +connection->createQueryBuilder(); + $value = (array)$criterion->value; + $subSelect + ->select( + 'contentobject_id' + )->from( + 'ezcontentobject_tree', + 'subquery_location' + )->where( + $queryBuilder->expr()->in( + 'subquery_location.remote_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) + ) + ); + + return $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + } +} + +class_alias(LocationRemoteId::class, 'eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\LocationRemoteId'); diff --git a/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/ParentLocationId.php b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/ParentLocationId.php new file mode 100644 index 0000000000..96eefe9578 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/ParentLocationId.php @@ -0,0 +1,62 @@ +connection->createQueryBuilder(); + $expr = $queryBuilder->expr(); + $subSelect + ->select( + 'contentobject_id' + )->from( + 'ezcontentobject_tree' + )->where( + $expr->in( + 'parent_node_id', + $queryBuilder->createNamedParameter( + $criterion->value, + Connection::PARAM_INT_ARRAY + ) + ) + ); + + return $expr->in( + 'c.id', + $subSelect->getSQL() + ); + } +} + +class_alias(ParentLocationId::class, 'eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\ParentLocationId'); diff --git a/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/PermissionSubtree.php b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/PermissionSubtree.php new file mode 100644 index 0000000000..61831618e8 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/PermissionSubtree.php @@ -0,0 +1,67 @@ +value as $pattern) { + $statements[] = $queryBuilder->expr()->like( + "{$table}.path_string", + $queryBuilder->createNamedParameter($pattern . '%') + ); + } + + $locationTableAlias = $this->connection->quoteIdentifier($table); + if (!$this->hasJoinedTableAs($queryBuilder, $locationTableAlias)) { + $queryBuilder + ->leftJoin( + 'c', + LocationGateway::CONTENT_TREE_TABLE, + $locationTableAlias, + $queryBuilder->expr()->eq( + "{$locationTableAlias}.contentobject_id", + 'c.id' + ) + ); + } + + return $queryBuilder->expr()->orX(...$statements); + } +} + +class_alias(PermissionSubtree::class, 'eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\PermissionSubtree'); diff --git a/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Subtree.php b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Subtree.php new file mode 100644 index 0000000000..618dd096f9 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Subtree.php @@ -0,0 +1,59 @@ +value as $pattern) { + $statements[] = $queryBuilder->expr()->like( + 'path_string', + $queryBuilder->createNamedParameter($pattern . '%', ParameterType::STRING) + ); + } + + $subSelect = $this->connection->createQueryBuilder(); + $subSelect + ->select('contentobject_id') + ->from('ezcontentobject_tree') + ->where($queryBuilder->expr()->orX(...$statements)); + + return $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + } +} + +class_alias(Subtree::class, 'eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\Subtree'); diff --git a/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Visibility.php b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Visibility.php new file mode 100644 index 0000000000..cebcca8901 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Gateway/CriterionHandler/Visibility.php @@ -0,0 +1,76 @@ +connection->createQueryBuilder(); + + if ($criterion->value[0] === Criterion\Visibility::VISIBLE) { + $expression = $queryBuilder->expr()->andX( + $queryBuilder->expr()->eq( + 'subquery_location.is_hidden', + 0 + ), + $queryBuilder->expr()->eq( + 'subquery_location.is_invisible', + 0 + ) + ); + } else { + $expression = $queryBuilder->expr()->orX( + $queryBuilder->expr()->eq( + 'subquery_location.is_hidden', + 1 + ), + $queryBuilder->expr()->eq( + 'subquery_location.is_invisible', + 1 + ) + ); + } + + $subSelect + ->select('contentobject_id') + ->from(LocationGateway::CONTENT_TREE_TABLE, 'subquery_location') + ->where($expression); + + return $queryBuilder->expr()->in( + 'c.id', + $subSelect->getSQL() + ); + } +} + +class_alias(Visibility::class, 'eZ\Publish\Core\Search\Legacy\Content\Gateway\CriterionHandler\Visibility'); diff --git a/src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php b/src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php new file mode 100644 index 0000000000..1fb0688ca2 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Gateway/DoctrineDatabase.php @@ -0,0 +1,266 @@ +connection = $connection; + $this->dbPlatform = $connection->getDatabasePlatform(); + $this->criteriaConverter = $criteriaConverter; + $this->sortClauseConverter = $sortClauseConverter; + $this->languageHandler = $languageHandler; + } + + public function find( + Criterion $criterion, + $offset, + $limit, + array $sort = null, + array $languageFilter = [], + $doCount = true + ): array { + $count = $doCount ? $this->getResultCount($criterion, $languageFilter) : null; + + if (!$doCount && $limit === 0) { + throw new RuntimeException('Invalid query. Cannot disable count and request 0 items at the same time.'); + } + + if ($limit === 0 || ($count !== null && $count <= $offset)) { + return ['count' => $count, 'rows' => []]; + } + + $contentInfoList = $this->getContentInfoList($criterion, $sort, $offset, $limit, $languageFilter); + + return [ + 'count' => $count, + 'rows' => $contentInfoList, + ]; + } + + /** + * Generates a language mask from the given $languageSettings. + * + * @param array $languageSettings + * + * @return int + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function getLanguageMask(array $languageSettings) + { + $mask = 0; + if ($languageSettings['useAlwaysAvailable']) { + $mask |= 1; + } + + foreach ($languageSettings['languages'] as $languageCode) { + $mask |= $this->languageHandler->loadByLanguageCode($languageCode)->id; + } + + return $mask; + } + + /** + * @param array $languageFilter + * + * @return string + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + private function getQueryCondition( + Criterion $filter, + QueryBuilder $query, + array $languageFilter + ) { + $expr = $query->expr(); + $condition = $expr->andX( + $this->criteriaConverter->convertCriteria($query, $filter, $languageFilter), + $expr->eq( + 'c.status', + ContentInfo::STATUS_PUBLISHED + ), + $expr->eq( + 'v.status', + VersionInfo::STATUS_PUBLISHED + ) + ); + + // If not main-languages query + if (!empty($languageFilter['languages'])) { + $condition = $expr->andX( + $condition, + $expr->gt( + $this->dbPlatform->getBitAndComparisonExpression( + 'c.language_mask', + $query->createNamedParameter( + $this->getLanguageMask($languageFilter), + ParameterType::INTEGER, + ':language_mask' + ) + ), + $query->createNamedParameter(0, ParameterType::INTEGER, ':zero') + ) + ); + } + + return $condition; + } + + /** + * Get result count. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $filter + * @param array $languageFilter + * + * @return int + */ + private function getResultCount(Criterion $filter, array $languageFilter) + { + $query = $this->connection->createQueryBuilder(); + + $columnName = 'c.id'; + $query + ->select("COUNT( DISTINCT $columnName )") + ->from(ContentGateway::CONTENT_ITEM_TABLE, 'c') + ->innerJoin( + 'c', + ContentGateway::CONTENT_VERSION_TABLE, + 'v', + 'c.id = v.contentobject_id', + ); + + $query->where( + $this->getQueryCondition($filter, $query, $languageFilter) + ); + + $statement = $query->execute(); + + return (int)$statement->fetchColumn(); + } + + /** + * Get sorted arrays of content IDs, which should be returned. + * + * @param array $sort + * @param mixed $offset + * @param mixed $limit + * @param array $languageFilter + * + * @return int[] + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + private function getContentInfoList( + Criterion $filter, + ?array $sort, + ?int $offset, + ?int $limit, + array $languageFilter + ): array { + $query = $this->connection->createQueryBuilder(); + $query->select( + 'DISTINCT c.*, main_tree.main_node_id AS main_tree_main_node_id', + ); + + if ($sort !== null) { + $this->sortClauseConverter->applySelect($query, $sort); + } + + $query + ->from(ContentGateway::CONTENT_ITEM_TABLE, 'c') + ->innerJoin( + 'c', + ContentGateway::CONTENT_VERSION_TABLE, + 'v', + 'c.id = v.contentobject_id' + ) + ->leftJoin( + 'c', + LocationGateway::CONTENT_TREE_TABLE, + 'main_tree', + $query->expr()->andX( + 'main_tree.contentobject_id = c.id', + 'main_tree.main_node_id = main_tree.node_id' + ) + ); + + if ($sort !== null) { + $this->sortClauseConverter->applyJoin($query, $sort, $languageFilter); + } + + $query->where( + $this->getQueryCondition($filter, $query, $languageFilter) + ); + + if ($sort !== null) { + $this->sortClauseConverter->applyOrderBy($query); + } + + $query->setMaxResults($limit); + $query->setFirstResult($offset); + + $statement = $query->execute(); + + return $statement->fetchAll(FetchMode::ASSOCIATIVE); + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Search\Legacy\Content\Gateway\DoctrineDatabase'); diff --git a/src/lib/Search/Legacy/Content/Gateway/ExceptionConversion.php b/src/lib/Search/Legacy/Content/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..d60dceb681 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Gateway/ExceptionConversion.php @@ -0,0 +1,47 @@ +innerGateway = $innerGateway; + } + + public function find( + Criterion $criterion, + $offset = 0, + $limit = null, + array $sort = null, + array $languageFilter = [], + $doCount = true + ): array { + try { + return $this->innerGateway->find($criterion, $offset, $limit, $sort, $languageFilter, $doCount); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Search\Legacy\Content\Gateway\ExceptionConversion'); diff --git a/src/lib/Search/Legacy/Content/Handler.php b/src/lib/Search/Legacy/Content/Handler.php new file mode 100644 index 0000000000..e00265b39b --- /dev/null +++ b/src/lib/Search/Legacy/Content/Handler.php @@ -0,0 +1,396 @@ +gateway = $gateway; + $this->locationGateway = $locationGateway; + $this->indexerGateway = $indexerGateway; + $this->contentMapper = $contentMapper; + $this->locationMapper = $locationMapper; + $this->languageHandler = $languageHandler; + $this->mapper = $mapper; + } + + /** + * Finds content objects for the given query. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if Query criterion is not applicable to its target + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query + * @param array $languageFilter - a map of language related filters specifying languages query will be performed on. + * Also used to define which field languages are loaded for the returned content. + * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + */ + public function findContent(Query $query, array $languageFilter = []) + { + if (!isset($languageFilter['languages'])) { + $languageFilter['languages'] = []; + } + + if (!isset($languageFilter['useAlwaysAvailable'])) { + $languageFilter['useAlwaysAvailable'] = true; + } + + $start = microtime(true); + $query->filter = $query->filter ?: new Criterion\MatchAll(); + $query->query = $query->query ?: new Criterion\MatchAll(); + + // The legacy search does not know about scores, so that we just + // combine the query with the filter + $filter = new Criterion\LogicalAnd([$query->query, $query->filter]); + + $data = $this->gateway->find( + $filter, + $query->offset, + $query->limit, + $query->sortClauses, + $languageFilter, + $query->performCount + ); + + $result = new SearchResult(); + $result->time = microtime(true) - $start; + $result->totalCount = $data['count']; + $contentInfoList = $this->contentMapper->extractContentInfoFromRows( + $data['rows'], + '', + 'main_tree_' + ); + + foreach ($contentInfoList as $index => $contentInfo) { + $searchHit = new SearchHit(); + $searchHit->valueObject = $contentInfo; + $searchHit->matchedTranslation = $this->extractMatchedLanguage( + $data['rows'][$index]['language_mask'], + $data['rows'][$index]['initial_language_id'], + $languageFilter + ); + + $result->searchHits[] = $searchHit; + } + + return $result; + } + + protected function extractMatchedLanguage($languageMask, $mainLanguageId, $languageSettings) + { + $languageList = !empty($languageSettings['languages']) ? + $this->languageHandler->loadListByLanguageCodes($languageSettings['languages']) : + []; + + foreach ($languageList as $language) { + if ($languageMask & $language->id) { + return $language->languageCode; + } + } + + if ($languageMask & 1 || empty($languageSettings['languages'])) { + return $this->languageHandler->load($mainLanguageId)->languageCode; + } + + return null; + } + + /** + * Performs a query for a single content object. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException if the object was not found by the query or due to permissions + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if Criterion is not applicable to its target + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException if there is more than than one result matching the criterions + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $filter + * @param array $languageFilter - a map of language related filters specifying languages query will be performed on. + * Also used to define which field languages are loaded for the returned content. + * Currently supports: array("languages" => array(,..), "useAlwaysAvailable" => bool) + * useAlwaysAvailable defaults to true to avoid exceptions on missing translations + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ContentInfo + */ + public function findSingle(Criterion $filter, array $languageFilter = []) + { + if (!isset($languageFilter['languages'])) { + $languageFilter['languages'] = []; + } + + if (!isset($languageFilter['useAlwaysAvailable'])) { + $languageFilter['useAlwaysAvailable'] = true; + } + + $searchQuery = new Query(); + $searchQuery->filter = $filter; + $searchQuery->query = new Criterion\MatchAll(); + $searchQuery->offset = 0; + $searchQuery->limit = 2; // Because we optimize away the count query below + $searchQuery->performCount = true; + $searchQuery->sortClauses = null; + $result = $this->findContent($searchQuery, $languageFilter); + + if (empty($result->searchHits)) { + throw new NotFoundException('Content', 'findSingle() found no content for the given $criterion'); + } elseif (isset($result->searchHits[1])) { + throw new InvalidArgumentException('totalCount', 'findSingle() found more then one item for the given $criterion'); + } + + $first = reset($result->searchHits); + + return $first->valueObject; + } + + public function findLocations(LocationQuery $query, array $languageFilter = []) + { + if (!isset($languageFilter['languages'])) { + $languageFilter['languages'] = []; + } + + if (!isset($languageFilter['useAlwaysAvailable'])) { + $languageFilter['useAlwaysAvailable'] = true; + } + + $start = microtime(true); + $query->filter = $query->filter ?: new Criterion\MatchAll(); + $query->query = $query->query ?: new Criterion\MatchAll(); + + // The legacy search does not know about scores, so we just + // combine the query with the filter + $data = $this->locationGateway->find( + new Criterion\LogicalAnd([$query->query, $query->filter]), + $query->offset, + $query->limit, + $query->sortClauses, + $languageFilter, + $query->performCount + ); + + $result = new SearchResult(); + $result->time = microtime(true) - $start; + $result->totalCount = $data['count']; + $locationList = $this->locationMapper->createLocationsFromRows($data['rows']); + + foreach ($locationList as $index => $location) { + $searchHit = new SearchHit(); + $searchHit->valueObject = $location; + $searchHit->matchedTranslation = $this->extractMatchedLanguage( + $data['rows'][$index]['language_mask'], + $data['rows'][$index]['initial_language_id'], + $languageFilter + ); + + $result->searchHits[] = $searchHit; + } + + return $result; + } + + /** + * Suggests a list of values for the given prefix. + * + * @param string $prefix + * @param string[] $fieldPaths + * @param int $limit + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion|null $filter + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function suggest($prefix, $fieldPaths = [], $limit = 10, Criterion $filter = null) + { + throw new NotImplementedException('Suggestions are not supported by Legacy search engine.'); + } + + /** + * Indexes a content object. + * + * @param \Ibexa\Contracts\Core\Persistence\Content $content + */ + public function indexContent(Content $content) + { + $fullTextValue = $this->mapper->mapContent($content); + + $this->indexerGateway->index($fullTextValue); + } + + /** + * Bulk index list of content objects. + * + * @param \Ibexa\Contracts\Core\Persistence\Content[] $contentList + * @param callable $errorCallback (Content $content, NotFoundException $e) + */ + public function bulkIndex(array $contentList, callable $errorCallback) + { + $fullTextBulkData = []; + foreach ($contentList as $content) { + try { + $fullTextBulkData[] = $this->mapper->mapContent($content); + } catch (NotFoundException $e) { + $errorCallback($content, $e); + } + } + + $this->indexerGateway->bulkIndex($fullTextBulkData); + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\Location $location + */ + public function indexLocation(Location $location) + { + // Not needed with Legacy Storage/Search Engine + } + + /** + * Deletes a content object from the index. + * + * @param int $contentId + * @param int|null $versionId + */ + public function deleteContent($contentId, $versionId = null) + { + $this->indexerGateway->remove($contentId, $versionId); + } + + public function deleteTranslation(int $contentId, string $languageCode): void + { + // Not needed with Legacy Storage/Search Engine + } + + /** + * Deletes a location from the index. + * + * @param mixed $locationId + * @param mixed $contentId + */ + public function deleteLocation($locationId, $contentId) + { + // Not needed with Legacy Storage/Search Engine + } + + /** + * Purges all contents from the index. + */ + public function purgeIndex() + { + $this->indexerGateway->purgeIndex(); + } + + /** + * Commits the data to the index, making it available for search. + * + * @param bool $flush + */ + public function commit($flush = false) + { + // Not needed with Legacy Storage/Search Engine + } + + public function supports(int $capabilityFlag): bool + { + return false; + } +} + +class_alias(Handler::class, 'eZ\Publish\Core\Search\Legacy\Content\Handler'); diff --git a/src/lib/Search/Legacy/Content/Indexer.php b/src/lib/Search/Legacy/Content/Indexer.php new file mode 100644 index 0000000000..63f3bcbc97 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Indexer.php @@ -0,0 +1,65 @@ +persistenceHandler->contentHandler(); + foreach ($contentIds as $contentId) { + try { + $info = $contentHandler->loadContentInfo($contentId); + if ($info->status === ContentInfo::STATUS_PUBLISHED) { + $this->searchHandler->indexContent( + $contentHandler->load($info->id, $info->currentVersionNo) + ); + } else { + $this->searchHandler->deleteContent($contentId); + } + } catch (NotFoundException $e) { + $this->searchHandler->deleteContent($contentId); + } catch (Exception $e) { + $context = [ + 'contentId' => $contentId, + 'error' => $e->getMessage(), + ]; + $this->logger->error('Unable to index the content', $context); + } + } + } + + public function purge() + { + $this->searchHandler->purgeIndex(); + } +} + +class_alias(Indexer::class, 'eZ\Publish\Core\Search\Legacy\Content\Indexer'); diff --git a/src/lib/Search/Legacy/Content/IndexerGateway.php b/src/lib/Search/Legacy/Content/IndexerGateway.php new file mode 100644 index 0000000000..18f82330e7 --- /dev/null +++ b/src/lib/Search/Legacy/Content/IndexerGateway.php @@ -0,0 +1,149 @@ +connection = $connection; + } + + public function getContentSince(DateTimeInterface $since, int $iterationCount): Generator + { + $query = $this->buildQueryForContentSince($since); + $query->orderBy('c.modified'); + + yield from $this->fetchIteration($query->execute(), $iterationCount); + } + + public function countContentSince(DateTimeInterface $since): int + { + $query = $this->buildCountingQuery( + $this->buildQueryForContentSince($since) + ); + + return (int)$query->execute()->fetchOne(); + } + + public function getContentInSubtree(string $locationPath, int $iterationCount): Generator + { + $query = $this->buildQueryForContentInSubtree($locationPath); + + yield from $this->fetchIteration($query->execute(), $iterationCount); + } + + public function countContentInSubtree(string $locationPath): int + { + $query = $this->buildCountingQuery( + $this->buildQueryForContentInSubtree($locationPath) + ); + + return (int)$query->execute()->fetchOne(); + } + + public function getAllContent(int $iterationCount): Generator + { + $query = $this->buildQueryForAllContent(); + + yield from $this->fetchIteration($query->execute(), $iterationCount); + } + + public function countAllContent(): int + { + $query = $this->buildCountingQuery( + $this->buildQueryForAllContent() + ); + + return (int)$query->execute()->fetchOne(); + } + + private function buildQueryForContentSince(DateTimeInterface $since): QueryBuilder + { + return $this->connection->createQueryBuilder() + ->select('c.id') + ->from('ezcontentobject', 'c') + ->where('c.status = :status')->andWhere('c.modified >= :since') + ->setParameter('status', ContentInfo::STATUS_PUBLISHED, ParameterType::INTEGER) + ->setParameter('since', $since->getTimestamp(), ParameterType::INTEGER); + } + + private function buildQueryForContentInSubtree(string $locationPath): QueryBuilder + { + return $this->connection->createQueryBuilder() + ->select('DISTINCT c.id') + ->from('ezcontentobject', 'c') + ->innerJoin('c', 'ezcontentobject_tree', 't', 't.contentobject_id = c.id') + ->where('c.status = :status') + ->andWhere('t.path_string LIKE :path') + ->setParameter('status', ContentInfo::STATUS_PUBLISHED, ParameterType::INTEGER) + ->setParameter('path', $locationPath . '%', ParameterType::STRING); + } + + private function buildQueryForAllContent(): QueryBuilder + { + return $this->connection->createQueryBuilder() + ->select('c.id') + ->from('ezcontentobject', 'c') + ->where('c.status = :status') + ->setParameter('status', ContentInfo::STATUS_PUBLISHED, ParameterType::INTEGER); + } + + /** + * @throws \Doctrine\DBAL\Exception + */ + private function buildCountingQuery(QueryBuilder $query): QueryBuilder + { + $databasePlatform = $this->connection->getDatabasePlatform(); + + // wrap existing select part in count expression + $query->select( + $databasePlatform->getCountExpression( + $query->getQueryPart('select')[0] + ) + ); + + return $query; + } + + private function fetchIteration(ResultStatement $statement, int $iterationCount): Generator + { + do { + $contentIds = []; + for ($i = 0; $i < $iterationCount; ++$i) { + if ($contentId = $statement->fetchOne()) { + $contentIds[] = $contentId; + } elseif (empty($contentIds)) { + return; + } else { + break; + } + } + + yield $contentIds; + } while (!empty($contentId)); + } +} + +class_alias(IndexerGateway::class, 'eZ\Publish\Core\Search\Legacy\Content\IndexerGateway'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway.php b/src/lib/Search/Legacy/Content/Location/Gateway.php new file mode 100644 index 0000000000..c693eb89d0 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway.php @@ -0,0 +1,41 @@ +value as $value) { + foreach (explode('/', trim($value, '/')) as $id) { + $idSet[$id] = true; + } + } + + return $queryBuilder->expr()->in( + 't.node_id', + $queryBuilder->createNamedParameter(array_keys($idSet), Connection::PARAM_INT_ARRAY) + ); + } +} + +class_alias(Ancestor::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Ancestor'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Depth.php b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Depth.php new file mode 100644 index 0000000000..4f7f5b6163 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Depth.php @@ -0,0 +1,75 @@ +operator) { + case Criterion\Operator::IN: + return $queryBuilder->expr()->in( + $column, + $criterion->value + ); + + case Criterion\Operator::BETWEEN: + return $this->dbPlatform->getBetweenExpression( + $column, + $queryBuilder->createNamedParameter($criterion->value[0], ParameterType::STRING), + $queryBuilder->createNamedParameter($criterion->value[1], ParameterType::STRING) + ); + + case Criterion\Operator::EQ: + case Criterion\Operator::GT: + case Criterion\Operator::GTE: + case Criterion\Operator::LT: + case Criterion\Operator::LTE: + $operatorFunction = $this->comparatorMap[$criterion->operator]; + + return $queryBuilder->expr()->$operatorFunction( + $column, + $queryBuilder->createNamedParameter(reset($criterion->value), ParameterType::STRING) + ); + + default: + throw new RuntimeException( + "Unknown operator '{$criterion->operator}' for Depth Criterion handler." + ); + } + } +} + +class_alias(Depth::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location\Depth'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/IsMainLocation.php b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/IsMainLocation.php new file mode 100644 index 0000000000..817a712a4e --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/IsMainLocation.php @@ -0,0 +1,62 @@ +value[0]) { + case Criterion\Location\IsMainLocation::MAIN: + return $queryBuilder->expr()->eq( + $idColumn, + $mainIdColumn + ); + + case Criterion\Location\IsMainLocation::NOT_MAIN: + return $queryBuilder->expr()->neq( + $idColumn, + $mainIdColumn + ); + + default: + throw new RuntimeException( + "Unknown value '{$criterion->value[0]}' for IsMainLocation Criterion handler." + ); + } + } +} + +class_alias(IsMainLocation::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location\IsMainLocation'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Priority.php b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Priority.php new file mode 100644 index 0000000000..649b7e2f76 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Location/Priority.php @@ -0,0 +1,68 @@ +operator) { + case Criterion\Operator::BETWEEN: + return $this->dbPlatform->getBetweenExpression( + $column, + $queryBuilder->createNamedParameter($criterion->value[0], ParameterType::STRING), + $queryBuilder->createNamedParameter($criterion->value[1], ParameterType::STRING) + ); + + case Criterion\Operator::GT: + case Criterion\Operator::GTE: + case Criterion\Operator::LT: + case Criterion\Operator::LTE: + $operatorFunction = $this->comparatorMap[$criterion->operator]; + + return $queryBuilder->expr()->$operatorFunction( + $column, + $queryBuilder->createNamedParameter(reset($criterion->value), ParameterType::STRING) + ); + + default: + throw new RuntimeException( + "Unknown operator '{$criterion->operator}' for Priority Criterion handler." + ); + } + } +} + +class_alias(Priority::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Location\Priority'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationId.php b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationId.php new file mode 100644 index 0000000000..71dc3883ba --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationId.php @@ -0,0 +1,47 @@ +value; + + return $queryBuilder->expr()->in( + 't.node_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ); + } +} + +class_alias(LocationId::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\LocationId'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationRemoteId.php b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationRemoteId.php new file mode 100644 index 0000000000..ac1c77558a --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/LocationRemoteId.php @@ -0,0 +1,47 @@ +value; + + return $queryBuilder->expr()->in( + 't.remote_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_STR_ARRAY) + ); + } +} + +class_alias(LocationRemoteId::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\LocationRemoteId'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/ParentLocationId.php b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/ParentLocationId.php new file mode 100644 index 0000000000..cd30b576d4 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/ParentLocationId.php @@ -0,0 +1,47 @@ +value; + + return $queryBuilder->expr()->in( + 't.parent_node_id', + $queryBuilder->createNamedParameter($value, Connection::PARAM_INT_ARRAY) + ); + } +} + +class_alias(ParentLocationId::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\ParentLocationId'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Subtree.php b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Subtree.php new file mode 100644 index 0000000000..64fb35fb5f --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Subtree.php @@ -0,0 +1,50 @@ +value as $pattern) { + $statements[] = $queryBuilder->expr()->like( + 't.path_string', + $queryBuilder->createNamedParameter($pattern . '%', ParameterType::STRING) + ); + } + + return $queryBuilder->expr()->orX(...$statements); + } + + /** + * Check if this criterion handler accepts to handle the given criterion. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion + * + * @return bool + */ + public function accept(Criterion $criterion) + { + return $criterion instanceof Criterion\Subtree; + } +} + +class_alias(Subtree::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Subtree'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Visibility.php b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Visibility.php new file mode 100644 index 0000000000..45fd8742a0 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/CriterionHandler/Visibility.php @@ -0,0 +1,62 @@ +value[0]) { + case Criterion\Visibility::VISIBLE: + return $queryBuilder->expr()->eq( + $column, + $queryBuilder->createNamedParameter(0, ParameterType::INTEGER) + ); + + case Criterion\Visibility::HIDDEN: + return $queryBuilder->expr()->eq( + $column, + $queryBuilder->createNamedParameter(1, ParameterType::INTEGER) + ); + + default: + throw new RuntimeException( + "Unknown value '{$criterion->value[0]}' for Visibility Criterion handler." + ); + } + } +} + +class_alias(Visibility::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler\Visibility'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/DoctrineDatabase.php b/src/lib/Search/Legacy/Content/Location/Gateway/DoctrineDatabase.php new file mode 100644 index 0000000000..553aeae3ad --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/DoctrineDatabase.php @@ -0,0 +1,262 @@ +connection = $connection; + $this->dbPlatform = $connection->getDatabasePlatform(); + $this->criteriaConverter = $criteriaConverter; + $this->sortClauseConverter = $sortClauseConverter; + $this->languageHandler = $languageHandler; + } + + public function find( + Criterion $criterion, + $offset, + $limit, + array $sortClauses = null, + array $languageFilter = [], + $doCount = true + ): array { + $count = $doCount ? $this->getTotalCount($criterion, $languageFilter) : null; + + if (!$doCount && $limit === 0) { + throw new RuntimeException('Invalid query. Cannot disable count and request 0 items at the same time.'); + } + + if ($limit === 0 || ($count !== null && $count <= $offset)) { + return ['count' => $count, 'rows' => []]; + } + + $selectQuery = $this->connection->createQueryBuilder(); + $selectQuery->select( + 't.*', + 'c.language_mask', + 'c.initial_language_id' + ); + + if ($sortClauses !== null) { + $this->sortClauseConverter->applySelect($selectQuery, $sortClauses); + } + + $selectQuery + ->from('ezcontentobject_tree', 't') + ->innerJoin( + 't', + 'ezcontentobject', + 'c', + 't.contentobject_id = c.id' + ) + ->innerJoin( + 'c', + 'ezcontentobject_version', + 'v', + 'c.id = v.contentobject_id', + ); + + if ($sortClauses !== null) { + $this->sortClauseConverter->applyJoin($selectQuery, $sortClauses, $languageFilter); + } + + $selectQuery->where( + $this->criteriaConverter->convertCriteria($selectQuery, $criterion, $languageFilter), + $selectQuery->expr()->eq( + 'c.status', + //ContentInfo::STATUS_PUBLISHED + $selectQuery->createNamedParameter(1, ParameterType::INTEGER) + ), + $selectQuery->expr()->eq( + 'v.status', + //VersionInfo::STATUS_PUBLISHED + $selectQuery->createNamedParameter(1, ParameterType::INTEGER) + ), + $selectQuery->expr()->neq( + 't.depth', + $selectQuery->createNamedParameter(0, ParameterType::INTEGER) + ) + ); + + // If not main-languages query + if (!empty($languageFilter['languages'])) { + $selectQuery->andWhere( + $selectQuery->expr()->gt( + $this->dbPlatform->getBitAndComparisonExpression( + 'c.language_mask', + $selectQuery->createNamedParameter( + $this->getLanguageMask($languageFilter), + ParameterType::INTEGER + ) + ), + $selectQuery->createNamedParameter(0, ParameterType::INTEGER) + ) + ); + } + + if ($sortClauses !== null) { + $this->sortClauseConverter->applyOrderBy($selectQuery); + } + + $selectQuery->setMaxResults($limit); + $selectQuery->setFirstResult($offset); + + $statement = $selectQuery->execute(); + + return [ + 'count' => $count, + 'rows' => $statement->fetchAll(FetchMode::ASSOCIATIVE), + ]; + } + + /** + * Returns total results count for $criterion and $sortClauses. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion + * @param array $languageFilter + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function getTotalCount(Criterion $criterion, array $languageFilter): int + { + $query = $this->connection->createQueryBuilder(); + $query + ->select($this->dbPlatform->getCountExpression('*')) + ->from('ezcontentobject_tree', 't') + ->innerJoin( + 't', + ContentGateway::CONTENT_ITEM_TABLE, + 'c', + 't.contentobject_id = c.id' + ) + ->innerJoin( + 'c', + ContentGateway::CONTENT_VERSION_TABLE, + 'v', + 'c.id = v.contentobject_id' + ); + + $query->where( + $this->criteriaConverter->convertCriteria($query, $criterion, $languageFilter), + $query->expr()->eq( + 'c.status', + //ContentInfo::STATUS_PUBLISHED + $query->createNamedParameter(1, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'v.status', + //VersionInfo::STATUS_PUBLISHED + $query->createNamedParameter(1, ParameterType::INTEGER) + ), + $query->expr()->neq( + 't.depth', + $query->createNamedParameter(0, ParameterType::INTEGER) + ) + ); + + // If not main-languages query + if (!empty($languageFilter['languages'])) { + $query->andWhere( + $query->expr()->gt( + $this->dbPlatform->getBitAndComparisonExpression( + 'c.language_mask', + $query->createNamedParameter( + $this->getLanguageMask($languageFilter), + ParameterType::INTEGER + ) + ), + $query->createNamedParameter(0, ParameterType::INTEGER) + ) + ); + } + + $statement = $query->execute(); + + return (int)$statement->fetchColumn(); + } + + /** + * Generates a language mask from the given $languageFilter. + * + * @param array $languageFilter + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function getLanguageMask(array $languageFilter): int + { + if (!isset($languageFilter['languages'])) { + $languageFilter['languages'] = []; + } + + if (!isset($languageFilter['useAlwaysAvailable'])) { + $languageFilter['useAlwaysAvailable'] = true; + } + + $mask = 0; + if ($languageFilter['useAlwaysAvailable']) { + $mask |= 1; + } + + foreach ($languageFilter['languages'] as $languageCode) { + $mask |= $this->languageHandler->loadByLanguageCode($languageCode)->id; + } + + return $mask; + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\DoctrineDatabase'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/ExceptionConversion.php b/src/lib/Search/Legacy/Content/Location/Gateway/ExceptionConversion.php new file mode 100644 index 0000000000..9af3219ee2 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/ExceptionConversion.php @@ -0,0 +1,53 @@ +innerGateway = $innerGateway; + } + + public function find( + Criterion $criterion, + $offset = 0, + $limit = null, + array $sortClauses = null, + array $languageFilter = [], + $doCount = true + ): array { + try { + return $this->innerGateway->find($criterion, $offset, $limit, $sortClauses, $languageFilter, $doCount); + } catch (DBALException | PDOException $e) { + throw DatabaseException::wrap($e); + } + } +} + +class_alias(ExceptionConversion::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\ExceptionConversion'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Depth.php b/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Depth.php new file mode 100644 index 0000000000..180ef47c0e --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Depth.php @@ -0,0 +1,44 @@ +addSelect( + sprintf('t.depth AS %s', $column = $this->getSortColumnName($number)) + ); + + return [$column]; + } +} + +class_alias(Depth::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Depth'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Id.php b/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Id.php new file mode 100644 index 0000000000..fa0384da07 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Id.php @@ -0,0 +1,47 @@ +addSelect( + sprintf( + 't.node_id AS %s', + $column = $this->getSortColumnName($number) + ) + ); + + return [$column]; + } +} + +class_alias(Id::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Id'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/IsMainLocation.php b/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/IsMainLocation.php new file mode 100644 index 0000000000..c56cbf6ccf --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/IsMainLocation.php @@ -0,0 +1,51 @@ +addSelect( + sprintf( + '%s AS %s', + $query->expr()->eq( + 't.node_id', + 't.main_node_id' + ), + $column = $this->getSortColumnName($number) + ) + ); + + return [$column]; + } +} + +class_alias(IsMainLocation::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\IsMainLocation'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Path.php b/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Path.php new file mode 100644 index 0000000000..0bcbfaa06c --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Path.php @@ -0,0 +1,47 @@ +addSelect( + sprintf( + 't.path_string AS %s', + $column = $this->getSortColumnName($number) + ) + ); + + return [$column]; + } +} + +class_alias(Path::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Path'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Priority.php b/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Priority.php new file mode 100644 index 0000000000..84b9ddeb95 --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Priority.php @@ -0,0 +1,47 @@ +addSelect( + sprintf( + 't.priority AS %s', + $column = $this->getSortColumnName($number) + ) + ); + + return [$column]; + } +} + +class_alias(Priority::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Priority'); diff --git a/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Visibility.php b/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Visibility.php new file mode 100644 index 0000000000..778b15d39e --- /dev/null +++ b/src/lib/Search/Legacy/Content/Location/Gateway/SortClauseHandler/Location/Visibility.php @@ -0,0 +1,47 @@ +addSelect( + sprintf( + 't.is_invisible AS %s', + $column = $this->getSortColumnName($number) + ) + ); + + return [$column]; + } +} + +class_alias(Visibility::class, 'eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler\Location\Visibility'); diff --git a/eZ/Publish/Core/Search/Legacy/Content/Mapper/FullTextMapper.php b/src/lib/Search/Legacy/Content/Mapper/FullTextMapper.php similarity index 76% rename from eZ/Publish/Core/Search/Legacy/Content/Mapper/FullTextMapper.php rename to src/lib/Search/Legacy/Content/Mapper/FullTextMapper.php index a99a453674..25b60009b2 100644 --- a/eZ/Publish/Core/Search/Legacy/Content/Mapper/FullTextMapper.php +++ b/src/lib/Search/Legacy/Content/Mapper/FullTextMapper.php @@ -4,16 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Legacy\Content\Mapper; +namespace Ibexa\Core\Search\Legacy\Content\Mapper; -use eZ\Publish\Core\Search\Common\FieldRegistry; -use eZ\Publish\Core\Search\Legacy\Content\FullTextData; -use eZ\Publish\Core\Search\Legacy\Content\FullTextValue; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as ContentTypeHandler; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType; +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\Type; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as ContentTypeHandler; +use Ibexa\Contracts\Core\Search\Field; +use Ibexa\Contracts\Core\Search\FieldType; +use Ibexa\Core\Search\Common\FieldRegistry; +use Ibexa\Core\Search\Legacy\Content\FullTextData; +use Ibexa\Core\Search\Legacy\Content\FullTextValue; /** * FullTextMapper maps Content object fields to FullTextValue objects which are searchable and @@ -24,20 +24,20 @@ class FullTextMapper /** * Field registry. * - * @var \eZ\Publish\Core\Search\Common\FieldRegistry + * @var \Ibexa\Core\Search\Common\FieldRegistry */ protected $fieldRegistry; /** * Content type handler. * - * @var \eZ\Publish\SPI\Persistence\Content\Type\Handler + * @var \Ibexa\Contracts\Core\Persistence\Content\Type\Handler */ protected $contentTypeHandler; /** - * @param \eZ\Publish\Core\Search\Common\FieldRegistry $fieldRegistry - * @param \eZ\Publish\SPI\Persistence\Content\Type\Handler $contentTypeHandler + * @param \Ibexa\Core\Search\Common\FieldRegistry $fieldRegistry + * @param \Ibexa\Contracts\Core\Persistence\Content\Type\Handler $contentTypeHandler */ public function __construct( FieldRegistry $fieldRegistry, @@ -50,9 +50,9 @@ public function __construct( /** * Map given Content to a FullTextValue. * - * @param \eZ\Publish\SPI\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content $content * - * @return \eZ\Publish\Core\Search\Legacy\Content\FullTextData + * @return \Ibexa\Core\Search\Legacy\Content\FullTextData */ public function mapContent(Content $content) { @@ -71,11 +71,11 @@ public function mapContent(Content $content) * Returns an array of FullTextValue object containing searchable values of content object * fields for the given $content. * - * @param \eZ\Publish\SPI\Persistence\Content $content + * @param \Ibexa\Contracts\Core\Persistence\Content $content * - * @return \eZ\Publish\Core\Search\Legacy\Content\FullTextValue[] + * @return \Ibexa\Core\Search\Legacy\Content\FullTextValue[] * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ protected function getFullTextValues(Content $content): array { @@ -132,3 +132,5 @@ static function ($indexField) { return !empty($fullTextFields) ? array_values($fullTextFields)[0] : null; } } + +class_alias(FullTextMapper::class, 'eZ\Publish\Core\Search\Legacy\Content\Mapper\FullTextMapper'); diff --git a/src/lib/Search/Legacy/Content/WordIndexer/Gateway.php b/src/lib/Search/Legacy/Content/WordIndexer/Gateway.php new file mode 100644 index 0000000000..2bac94a015 --- /dev/null +++ b/src/lib/Search/Legacy/Content/WordIndexer/Gateway.php @@ -0,0 +1,45 @@ +connection = $connection; + $this->typeHandler = $typeHandler; + $this->transformationProcessor = $transformationProcessor; + $this->searchIndex = $searchIndex; + $this->fullTextSearchConfiguration = $fullTextSearchConfiguration; + $this->languageMaskGenerator = $languageMaskGenerator; + } + + /** + * Index search engine full text data corresponding to content object field values. + * + * Ported from the legacy code + * + * @see https://github.com/ezsystems/ezpublish-legacy/blob/master/kernel/search/plugins/ezsearchengine/ezsearchengine.php#L45 + * + * @param \Ibexa\Core\Search\Legacy\Content\FullTextData $fullTextData + */ + public function index(FullTextData $fullTextData) + { + $indexArray = []; + $indexArrayOnlyWords = []; + $wordCount = 0; + $placement = 0; + + $this->connection->beginTransaction(); + // Remove previously indexed content if exists to avoid keeping in index removed field values + $this->remove($fullTextData->id); + foreach ($fullTextData->values as $fullTextValue) { + /** @var \Ibexa\Core\Search\Legacy\Content\FullTextValue $fullTextValue */ + if (is_numeric(trim($fullTextValue->value))) { + $integerValue = (int)$fullTextValue->value; + if ($integerValue > self::DB_INT_MAX) { + $integerValue = 0; + } + } else { + $integerValue = 0; + } + $text = $this->transformationProcessor->transform( + $fullTextValue->value, + !empty($fullTextValue->transformationRules) + ? $fullTextValue->transformationRules + : $this->fullTextSearchConfiguration['commands'] + ); + // split by non-words + $wordArray = $fullTextValue->splitFlag ? preg_split('/\W/u', $text, -1, PREG_SPLIT_NO_EMPTY) : [$text]; + foreach ($wordArray as $word) { + if (trim($word) === '') { + continue; + } + // words stored in search index are limited to 150 characters + if (mb_strlen($word) > 150) { + $word = mb_substr($word, 0, 150); + } + $indexArray[] = [ + 'Word' => $word, + 'ContentClassAttributeID' => $fullTextValue->fieldDefinitionId, + 'identifier' => $fullTextValue->fieldDefinitionIdentifier, + 'integer_value' => $integerValue, + 'language_code' => $fullTextValue->languageCode, + 'is_main_and_always_available' => $fullTextValue->isMainAndAlwaysAvailable, + ]; + $indexArrayOnlyWords[$word] = 1; + ++$wordCount; + // if we have "www." before word than + // treat it as url and add additional entry to the index + if (mb_strtolower(mb_substr($word, 0, 4)) === 'www.') { + $additionalUrlWord = substr($word, 4); + $indexArray[] = [ + 'Word' => $additionalUrlWord, + 'ContentClassAttributeID' => $fullTextValue->fieldDefinitionId, + 'identifier' => $fullTextValue->fieldDefinitionIdentifier, + 'integer_value' => $integerValue, + 'language_code' => $fullTextValue->languageCode, + 'is_main_and_always_available' => $fullTextValue->isMainAndAlwaysAvailable, + ]; + $indexArrayOnlyWords[$additionalUrlWord] = 1; + ++$wordCount; + } + } + } + + $wordIDArray = $this->buildWordIDArray(array_keys($indexArrayOnlyWords)); + for ($arrayCount = 0; $arrayCount < $wordCount; $arrayCount += 1000) { + $placement = $this->indexWords( + $fullTextData, + array_slice($indexArray, $arrayCount, 1000), + $wordIDArray, + $placement + ); + } + $this->connection->commit(); + } + + /** + * Indexes an array of FullTextData objects. + * + * Note: on large amounts of data make sure to iterate with several calls to this function with + * a limited set of FullTextData objects. Amount you have memory for depends on server, size + * of FullTextData objects & PHP version. + * + * @param \Ibexa\Core\Search\Legacy\Content\FullTextData[] $fullTextBulkData + */ + public function bulkIndex(array $fullTextBulkData) + { + foreach ($fullTextBulkData as $fullTextData) { + $this->index($fullTextData); + } + } + + /** + * Remove whole content or a specific version from index. + * + * Ported from the legacy code + * + * @see https://github.com/ezsystems/ezpublish-legacy/blob/master/kernel/search/plugins/ezsearchengine/ezsearchengine.php#L386 + * + * @param mixed $contentId + * @param mixed|null $versionId + * + * @return bool + */ + public function remove($contentId, $versionId = null) + { + $doDelete = false; + $this->connection->beginTransaction(); + // fetch all the words and decrease the object count on all the words + $wordIDList = $this->searchIndex->getContentObjectWords($contentId); + if (count($wordIDList) > 0) { + $this->searchIndex->decrementWordObjectCount($wordIDList); + $doDelete = true; + } + if ($doDelete) { + $this->searchIndex->deleteWordsWithoutObjects(); + $this->searchIndex->deleteObjectWordsLink($contentId); + } + $this->connection->commit(); + + return true; + } + + /** + * Remove entire search index. + */ + public function purgeIndex() + { + $this->searchIndex->purge(); + } + + /** + * Index wordIndex. + * + * Ported from the legacy code + * + * @see https://github.com/ezsystems/ezpublish-legacy/blob/master/kernel/search/plugins/ezsearchengine/ezsearchengine.php#L255 + * + * @param \Ibexa\Core\Search\Legacy\Content\FullTextData $fullTextData + * @param array $indexArray + * @param array $wordIDArray + * @param int $placement + * + * @return int last placement + */ + private function indexWords(FullTextData $fullTextData, array $indexArray, array $wordIDArray, $placement = 0) + { + $contentId = $fullTextData->id; + + $prevWordId = 0; + + for ($i = 0; $i < count($indexArray); ++$i) { + $indexWord = $indexArray[$i]['Word']; + $indexWord = $this->transformationProcessor->transformByGroup($indexWord, 'lowercase'); + $contentFieldId = $indexArray[$i]['ContentClassAttributeID']; + $identifier = $indexArray[$i]['identifier']; + $integerValue = $indexArray[$i]['integer_value']; + $languageCode = $indexArray[$i]['language_code']; + $wordId = $wordIDArray[$indexWord]; + $isMainAndAlwaysAvailable = $indexArray[$i]['is_main_and_always_available']; + $languageMask = $this->languageMaskGenerator->generateLanguageMaskFromLanguageCodes( + [$languageCode], + $isMainAndAlwaysAvailable + ); + + if (isset($indexArray[$i + 1])) { + $nextIndexWord = $indexArray[$i + 1]['Word']; + $nextIndexWord = $this->transformationProcessor->transformByGroup($nextIndexWord, 'lowercase'); + $nextWordId = $wordIDArray[$nextIndexWord]; + } else { + $nextWordId = 0; + } + $frequency = 0; + $this->searchIndex->addObjectWordLink( + $wordId, + $contentId, + $frequency, + $placement, + $nextWordId, + $prevWordId, + $fullTextData->contentTypeId, + $contentFieldId, + $fullTextData->published, + $fullTextData->sectionId, + $identifier, + $integerValue, + $languageMask + ); + $prevWordId = $wordId; + ++$placement; + } + + return $placement; + } + + /** + * Build WordIDArray and update ezsearch_word table. + * + * Ported from the legacy code + * + * @see https://github.com/ezsystems/ezpublish-legacy/blob/master/kernel/search/plugins/ezsearchengine/ezsearchengine.php#L155 + * + * @param array $indexArrayOnlyWords words for object to add + * + * @return array wordIDArray + */ + private function buildWordIDArray(array $indexArrayOnlyWords) + { + $wordCount = count($indexArrayOnlyWords); + $wordIDArray = []; + $wordArray = []; + + // store the words in the index and remember the ID + $this->connection->beginTransaction(); + for ($arrayCount = 0; $arrayCount < $wordCount; $arrayCount += 500) { + // Fetch already indexed words from database + $wordArrayChuck = array_slice($indexArrayOnlyWords, $arrayCount, 500); + $wordRes = $this->searchIndex->getWords($wordArrayChuck); + + // Build a has of the existing words + $wordResCount = count($wordRes); + $existingWordArray = []; + for ($i = 0; $i < $wordResCount; ++$i) { + $wordIDArray[] = $wordRes[$i]['id']; + $existingWordArray[] = $wordRes[$i]['word']; + $wordArray[$wordRes[$i]['word']] = $wordRes[$i]['id']; + } + + // Update the object count of existing words by one + if (count($wordIDArray) > 0) { + $this->searchIndex->incrementWordObjectCount($wordIDArray); + } + + // Insert if there is any news words + $newWordArray = array_diff($wordArrayChuck, $existingWordArray); + if (count($newWordArray) > 0) { + $this->searchIndex->addWords($newWordArray); + $newWordRes = $this->searchIndex->getWords($newWordArray); + $newWordCount = count($newWordRes); + for ($i = 0; $i < $newWordCount; ++$i) { + $wordLowercase = $this->transformationProcessor->transformByGroup($newWordRes[$i]['word'], 'lowercase'); + $wordArray[$wordLowercase] = $newWordRes[$i]['id']; + } + } + } + $this->connection->commit(); + + return $wordArray; + } +} + +class_alias(DoctrineDatabase::class, 'eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Gateway\DoctrineDatabase'); diff --git a/eZ/Publish/Core/Search/Legacy/Content/WordIndexer/Repository/SearchIndex.php b/src/lib/Search/Legacy/Content/WordIndexer/Repository/SearchIndex.php similarity index 97% rename from eZ/Publish/Core/Search/Legacy/Content/WordIndexer/Repository/SearchIndex.php rename to src/lib/Search/Legacy/Content/WordIndexer/Repository/SearchIndex.php index a04b2d45d3..7b3092af9a 100644 --- a/eZ/Publish/Core/Search/Legacy/Content/WordIndexer/Repository/SearchIndex.php +++ b/src/lib/Search/Legacy/Content/WordIndexer/Repository/SearchIndex.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Repository; +namespace Ibexa\Core\Search\Legacy\Content\WordIndexer\Repository; use Doctrine\DBAL\Connection; use Doctrine\DBAL\FetchMode; @@ -264,3 +264,5 @@ private function getWordUpdateQuery(array $wordIds): QueryBuilder return $query; } } + +class_alias(SearchIndex::class, 'eZ\Publish\Core\Search\Legacy\Content\WordIndexer\Repository\SearchIndex'); diff --git a/src/lib/Token/RandomBytesGenerator.php b/src/lib/Token/RandomBytesGenerator.php new file mode 100644 index 0000000000..341ef6deed --- /dev/null +++ b/src/lib/Token/RandomBytesGenerator.php @@ -0,0 +1,22 @@ +tokenGenerator = $tokenGenerator; + } + + /** + * @throws \Exception + */ + public function generateToken(int $length = 64): string + { + $token = $this->tokenGenerator->generateToken($length); + $encoded = base64_encode($token); + + return substr( + rtrim( + strtr($encoded, '+-', '/_'), + '=' + ), + 0, + $length + ); + } +} diff --git a/src/lib/Variation/VariationHandlerRegistry.php b/src/lib/Variation/VariationHandlerRegistry.php new file mode 100644 index 0000000000..6861af5559 --- /dev/null +++ b/src/lib/Variation/VariationHandlerRegistry.php @@ -0,0 +1,49 @@ + */ + private iterable $variationHandlers; + + public function __construct(iterable $variationHandlers) + { + $handlers = $variationHandlers instanceof Traversable + ? iterator_to_array($variationHandlers) + : $variationHandlers; + + foreach ($handlers as $identifier => $handler) { + $this->setVariationHandler($identifier, $handler); + } + } + + public function hasVariationHandler(string $identifier): bool + { + return isset($this->variationHandlers[$identifier]); + } + + public function getVariationHandler(string $identifier): VariationHandler + { + if (!$this->hasVariationHandler($identifier)) { + throw new InvalidArgumentException('identifier', 'VariationHandler is not registered'); + } + + return $this->variationHandlers[$identifier]; + } + + public function setVariationHandler(string $identifier, VariationHandler $variationHandler): void + { + $this->variationHandlers[$identifier] = $variationHandler; + } +} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/ApiLoader/CacheFactoryTest.php b/tests/bundle/Core/ApiLoader/CacheFactoryTest.php similarity index 87% rename from eZ/Bundle/EzPublishCoreBundle/Tests/ApiLoader/CacheFactoryTest.php rename to tests/bundle/Core/ApiLoader/CacheFactoryTest.php index a3bd751d1e..0d9b5d3922 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/ApiLoader/CacheFactoryTest.php +++ b/tests/bundle/Core/ApiLoader/CacheFactoryTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\ApiLoader; +namespace Ibexa\Tests\Bundle\Core\ApiLoader; -use eZ\Bundle\EzPublishCoreBundle\ApiLoader\CacheFactory; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Bundle\Core\ApiLoader\CacheFactory; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\Cache\Adapter\AdapterInterface; use Symfony\Component\Cache\Adapter\TagAwareAdapter; @@ -63,3 +63,5 @@ public function testGetService($name, $expected) $this->assertInstanceOf(TagAwareAdapter::class, $factory->getCachePool($this->configResolver)); } } + +class_alias(CacheFactoryTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\ApiLoader\CacheFactoryTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/ApiLoader/RepositoryConfigurationProviderTest.php b/tests/bundle/Core/ApiLoader/RepositoryConfigurationProviderTest.php similarity index 87% rename from eZ/Bundle/EzPublishCoreBundle/Tests/ApiLoader/RepositoryConfigurationProviderTest.php rename to tests/bundle/Core/ApiLoader/RepositoryConfigurationProviderTest.php index bfc7dc9e5e..76e4a8b2f2 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/ApiLoader/RepositoryConfigurationProviderTest.php +++ b/tests/bundle/Core/ApiLoader/RepositoryConfigurationProviderTest.php @@ -4,10 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\ApiLoader; +namespace Ibexa\Tests\Bundle\Core\ApiLoader; -use eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Bundle\Core\ApiLoader\Exception\InvalidRepositoryException; +use Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use PHPUnit\Framework\TestCase; class RepositoryConfigurationProviderTest extends TestCase @@ -73,7 +74,7 @@ public function testGetRepositoryConfigNotSpecifiedRepository() */ public function testGetRepositoryConfigUndefinedRepository(array $repositories): void { - $this->expectException(\eZ\Bundle\EzPublishCoreBundle\ApiLoader\Exception\InvalidRepositoryException::class); + $this->expectException(InvalidRepositoryException::class); $configResolver = $this->getConfigResolverMock(); @@ -130,10 +131,12 @@ public function providerForRepositories(): array } /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\MVC\ConfigResolverInterface + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ protected function getConfigResolverMock() { return $this->createMock(ConfigResolverInterface::class); } } + +class_alias(RepositoryConfigurationProviderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\ApiLoader\RepositoryConfigurationProviderTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/ApiLoader/StorageConnectionFactoryTest.php b/tests/bundle/Core/ApiLoader/StorageConnectionFactoryTest.php similarity index 90% rename from eZ/Bundle/EzPublishCoreBundle/Tests/ApiLoader/StorageConnectionFactoryTest.php rename to tests/bundle/Core/ApiLoader/StorageConnectionFactoryTest.php index aa777092c1..d49be25545 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/ApiLoader/StorageConnectionFactoryTest.php +++ b/tests/bundle/Core/ApiLoader/StorageConnectionFactoryTest.php @@ -4,11 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\ApiLoader; +namespace Ibexa\Tests\Bundle\Core\ApiLoader; -use eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider; -use eZ\Bundle\EzPublishCoreBundle\ApiLoader\StorageConnectionFactory; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Bundle\Core\ApiLoader\Exception\InvalidRepositoryException; +use Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider; +use Ibexa\Bundle\Core\ApiLoader\StorageConnectionFactory; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -68,7 +69,7 @@ public function getConnectionProvider() public function testGetConnectionInvalidRepository() { - $this->expectException(\eZ\Bundle\EzPublishCoreBundle\ApiLoader\Exception\InvalidRepositoryException::class); + $this->expectException(InvalidRepositoryException::class); $repositories = [ 'foo' => [ @@ -135,3 +136,5 @@ protected function getContainerMock() return $this->createMock(ContainerInterface::class); } } + +class_alias(StorageConnectionFactoryTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\ApiLoader\StorageConnectionFactoryTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/ApiLoader/StorageEngineFactoryTest.php b/tests/bundle/Core/ApiLoader/StorageEngineFactoryTest.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/Tests/ApiLoader/StorageEngineFactoryTest.php rename to tests/bundle/Core/ApiLoader/StorageEngineFactoryTest.php index 6a8d4b048f..aed319c78f 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/ApiLoader/StorageEngineFactoryTest.php +++ b/tests/bundle/Core/ApiLoader/StorageEngineFactoryTest.php @@ -4,19 +4,20 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\ApiLoader; +namespace Ibexa\Tests\Bundle\Core\ApiLoader; -use eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider; -use eZ\Bundle\EzPublishCoreBundle\ApiLoader\StorageEngineFactory; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\SPI\Persistence\Handler; +use Ibexa\Bundle\Core\ApiLoader\Exception\InvalidStorageEngine; +use Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider; +use Ibexa\Bundle\Core\ApiLoader\StorageEngineFactory; +use Ibexa\Contracts\Core\Persistence\Handler; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use PHPUnit\Framework\TestCase; class StorageEngineFactoryTest extends TestCase { public function testRegisterStorageEngine() { - /** @var \eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider $repositoryConfigurationProvider */ + /** @var \Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider $repositoryConfigurationProvider */ $repositoryConfigurationProvider = $this->createMock(RepositoryConfigurationProvider::class); $factory = new StorageEngineFactory($repositoryConfigurationProvider); @@ -72,7 +73,7 @@ public function testBuildStorageEngine() public function testBuildInvalidStorageEngine() { - $this->expectException(\eZ\Bundle\EzPublishCoreBundle\ApiLoader\Exception\InvalidStorageEngine::class); + $this->expectException(InvalidStorageEngine::class); $configResolver = $this->getConfigResolverMock(); $repositoryAlias = 'main'; @@ -111,7 +112,7 @@ public function testBuildInvalidStorageEngine() } /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\MVC\ConfigResolverInterface + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ protected function getConfigResolverMock() { @@ -123,3 +124,5 @@ protected function getPersistenceHandlerMock() return $this->createMock(Handler::class); } } + +class_alias(StorageEngineFactoryTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\ApiLoader\StorageEngineFactoryTest'); diff --git a/tests/bundle/Core/Cache/Warmer/ProxyCacheWarmerTest.php b/tests/bundle/Core/Cache/Warmer/ProxyCacheWarmerTest.php new file mode 100644 index 0000000000..19ab3183b3 --- /dev/null +++ b/tests/bundle/Core/Cache/Warmer/ProxyCacheWarmerTest.php @@ -0,0 +1,45 @@ +proxyGenerator = $this->createMock(ProxyGeneratorInterface::class); + $this->proxyCacheWarmer = new ProxyCacheWarmer($this->proxyGenerator); + } + + public function testIsOptional(): void + { + $this->assertFalse($this->proxyCacheWarmer->isOptional()); + } + + public function testWarmUp(): void + { + $this->proxyGenerator + ->expects($this->once()) + ->method('warmUp') + ->with(ProxyCacheWarmer::PROXY_CLASSES); + + $this->proxyCacheWarmer->warmUp('/cache/dir'); + } +} + +class_alias(ProxyCacheWarmerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Cache\Warmer\ProxyCacheWarmerTest'); diff --git a/tests/bundle/Core/ChainConfigResolverTest.php b/tests/bundle/Core/ChainConfigResolverTest.php new file mode 100644 index 0000000000..882ab18ceb --- /dev/null +++ b/tests/bundle/Core/ChainConfigResolverTest.php @@ -0,0 +1,263 @@ +chainResolver = new ChainConfigResolver(); + } + + public function testPriority() + { + $this->assertEquals([], $this->chainResolver->getAllResolvers()); + + list($low, $high) = $this->createResolverMocks(); + + $this->chainResolver->addResolver($low, 10); + $this->chainResolver->addResolver($high, 100); + + $this->assertEquals( + [ + $high, + $low, + ], + $this->chainResolver->getAllResolvers() + ); + } + + /** + * Resolvers are supposed to be sorted only once. + * This test will check that by trying to get all resolvers several times. + */ + public function testSortResolvers() + { + list($low, $medium, $high) = $this->createResolverMocks(); + // We're using a mock here and not $this->chainResolver because we need to ensure that the sorting operation is done only once. + $resolver = $this->buildMock( + ChainConfigResolver::class, + ['sortResolvers'] + ); + $resolver + ->expects($this->once()) + ->method('sortResolvers') + ->will( + $this->returnValue( + [$high, $medium, $low] + ) + ); + + $resolver->addResolver($low, 10); + $resolver->addResolver($medium, 50); + $resolver->addResolver($high, 100); + $expectedSortedRouters = [$high, $medium, $low]; + // Let's get all routers 5 times, we should only sort once. + for ($i = 0; $i < 5; ++$i) { + $this->assertSame($expectedSortedRouters, $resolver->getAllResolvers()); + } + } + + /** + * This test ensures that if a resolver is being added on the fly, the sorting is reset. + */ + public function testReSortResolvers() + { + list($low, $medium, $high) = $this->createResolverMocks(); + $highest = clone $high; + // We're using a mock here and not $this->chainResolver because we need to ensure that the sorting operation is done only once. + $resolver = $this->buildMock( + ChainConfigResolver::class, + ['sortResolvers'] + ); + $resolver + ->expects($this->at(0)) + ->method('sortResolvers') + ->will( + $this->returnValue( + [$high, $medium, $low] + ) + ); + // The second time sortResolvers() is called, we're supposed to get the newly added router ($highest) + $resolver + ->expects($this->at(1)) + ->method('sortResolvers') + ->will( + $this->returnValue( + [$highest, $high, $medium, $low] + ) + ); + + $resolver->addResolver($low, 10); + $resolver->addResolver($medium, 50); + $resolver->addResolver($high, 100); + $this->assertSame( + [$high, $medium, $low], + $resolver->getAllResolvers() + ); + + // Now adding another resolver on the fly, sorting must have been reset + $resolver->addResolver($highest, 101); + $this->assertSame( + [$highest, $high, $medium, $low], + $resolver->getAllResolvers() + ); + } + + public function testGetDefaultNamespace() + { + $this->expectException(\LogicException::class); + + $this->chainResolver->getDefaultNamespace(); + } + + public function testSetDefaultNamespace() + { + $namespace = 'foo'; + foreach ($this->createResolverMocks() as $i => $resolver) { + $resolver + ->expects($this->once()) + ->method('setDefaultNamespace') + ->with($namespace); + $this->chainResolver->addResolver($resolver, $i); + } + + $this->chainResolver->setDefaultNamespace($namespace); + } + + public function testGetParameterInvalid() + { + $this->expectException(ParameterNotFoundException::class); + + $paramName = 'foo'; + $namespace = 'namespace'; + $scope = 'scope'; + foreach ($this->createResolverMocks() as $resolver) { + $resolver + ->expects($this->once()) + ->method('getParameter') + ->with($paramName, $namespace, $scope) + ->will($this->throwException(new ParameterNotFoundException($paramName, $namespace))); + $this->chainResolver->addResolver($resolver); + } + + $this->chainResolver->getParameter($paramName, $namespace, $scope); + } + + /** + * @dataProvider getParameterProvider + * + * @param string $paramName + * @param string $namespace + * @param string $scope + * @param mixed $expectedValue + */ + public function testGetParameter($paramName, $namespace, $scope, $expectedValue) + { + $resolver = $this->createMock(ConfigResolverInterface::class); + $resolver + ->expects($this->once()) + ->method('getParameter') + ->with($paramName, $namespace, $scope) + ->will($this->returnValue($expectedValue)); + + $this->chainResolver->addResolver($resolver); + $this->assertSame($expectedValue, $this->chainResolver->getParameter($paramName, $namespace, $scope)); + } + + public function getParameterProvider() + { + return [ + ['foo', 'namespace', 'scope', 'someValue'], + ['some.parameter', 'wowNamespace', 'mySiteaccess', ['foo', 'bar']], + ['another.parameter.but.longer.name', 'yetAnotherNamespace', 'anotherSiteaccess', ['foo', ['fruit' => 'apple']]], + ['boolean.parameter', 'yetAnotherNamespace', 'admin', false], + ]; + } + + public function testHasParameterTrue() + { + $paramName = 'foo'; + $namespace = 'yetAnotherNamespace'; + $scope = 'mySiteaccess'; + + $resolver1 = $this->createMock(ConfigResolverInterface::class); + $resolver1 + ->expects($this->once()) + ->method('hasParameter') + ->with($paramName, $namespace, $scope) + ->will($this->returnValue(false)); + $this->chainResolver->addResolver($resolver1); + + $resolver2 = $this->createMock(ConfigResolverInterface::class); + $resolver2 + ->expects($this->once()) + ->method('hasParameter') + ->with($paramName, $namespace, $scope) + ->will($this->returnValue(true)); + $this->chainResolver->addResolver($resolver2); + + $resolver3 = $this->createMock(ConfigResolverInterface::class); + $resolver3 + ->expects($this->never()) + ->method('hasParameter'); + $this->chainResolver->addResolver($resolver3); + + $this->assertTrue($this->chainResolver->hasParameter($paramName, $namespace, $scope)); + } + + public function testHasParameterFalse() + { + $paramName = 'foo'; + $namespace = 'yetAnotherNamespace'; + $scope = 'mySiteaccess'; + + $resolver = $this->createMock(ConfigResolverInterface::class); + $resolver + ->expects($this->once()) + ->method('hasParameter') + ->with($paramName, $namespace, $scope) + ->will($this->returnValue(false)); + $this->chainResolver->addResolver($resolver); + + $this->assertFalse($this->chainResolver->hasParameter($paramName, $namespace, $scope)); + } + + /** + * @return \PHPUnit\Framework\MockObject\MockObject[] + */ + private function createResolverMocks() + { + return [ + $this->createMock(ConfigResolverInterface::class), + $this->createMock(ConfigResolverInterface::class), + $this->createMock(ConfigResolverInterface::class), + ]; + } + + private function buildMock($class, array $methods = []) + { + return $this + ->getMockBuilder($class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } +} + +class_alias(ChainConfigResolverTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\ChainConfigResolverTest'); diff --git a/tests/bundle/Core/Command/Indexer/ContentIdList/ContentTypeInputGeneratorStrategyTest.php b/tests/bundle/Core/Command/Indexer/ContentIdList/ContentTypeInputGeneratorStrategyTest.php new file mode 100644 index 0000000000..573243ba1d --- /dev/null +++ b/tests/bundle/Core/Command/Indexer/ContentIdList/ContentTypeInputGeneratorStrategyTest.php @@ -0,0 +1,115 @@ + $expectedBatches + */ + public function testGetGenerator(ContentList $contentList, int $batchSize, array $expectedBatches): void + { + $contentServiceMock = $this->createMock(ContentService::class); + $contentServiceMock->method('find')->willReturn($contentList); + + $inputMock = $this->createMock(InputInterface::class); + $inputMock->method('getOption')->with('content-type')->willReturn(uniqid('type', true)); + + $strategy = new ContentTypeInputGeneratorStrategy($contentServiceMock); + + self::assertSame( + $expectedBatches, + iterator_to_array($strategy->getBatchList($inputMock, $batchSize)) + ); + } + + /** + * @return iterable}> + */ + public function getDataForTestGetGenerator(): iterable + { + yield 'iteration count = 3, items = 10' => [ + $this->generateContentList(10), + 3, + [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [10], + ], + ]; + + yield 'iteration count = 6, items = 6' => [ + $this->generateContentList(6), + 6, + [ + [1, 2, 3, 4, 5, 6], + ], + ]; + + yield 'iteration count = 2, items = 4' => [ + $this->generateContentList(4), + 2, + [ + [1, 2], + [3, 4], + ], + ]; + + yield 'iteration count = 10, items = 5' => [ + $this->generateContentList(5), + 10, + [ + [1, 2, 3, 4, 5], + ], + ]; + + yield 'iteration count = 5, items = 0' => [ + $this->generateContentList(0), + 5, + [], + ]; + } + + private function generateContentList(int $totalCount): ContentList + { + $contentItems = []; + for ($i = 0; $i < $totalCount; ++$i) { + $contentItems[] = $this->createContentItemWithIdMock($i + 1); + } + + return new ContentList($totalCount, $contentItems); + } + + private function createContentItemWithIdMock(int $id): Content + { + $contentItem = $this->createMock(Content::class); + $contentInfoMock = $this->createMock(ContentInfo::class); + $contentInfoMock->method('getId')->willReturn($id); + $versionInfoMock = $this->createMock(VersionInfo::class); + $versionInfoMock->method('getContentInfo')->willReturn($contentInfoMock); + $contentItem->method('getVersionInfo')->willReturn($versionInfoMock); + + return $contentItem; + } +} diff --git a/tests/bundle/Core/ConfigResolverTest.php b/tests/bundle/Core/ConfigResolverTest.php new file mode 100644 index 0000000000..3b2985db40 --- /dev/null +++ b/tests/bundle/Core/ConfigResolverTest.php @@ -0,0 +1,298 @@ +siteAccess = new SiteAccess('test'); + $this->containerMock = $this->createMock(ContainerInterface::class); + } + + /** + * @param string $defaultNS + * @param int $undefinedStrategy + * @param array $groupsBySiteAccess + * + * @return \Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver + */ + private function getResolver($defaultNS = 'ibexa.site_access.config', $undefinedStrategy = ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION, array $groupsBySiteAccess = []) + { + $configResolver = new ConfigResolver( + null, + $groupsBySiteAccess, + $defaultNS, + $undefinedStrategy + ); + $configResolver->setSiteAccess($this->siteAccess); + $configResolver->setContainer($this->containerMock); + + return $configResolver; + } + + public function testGetSetUndefinedStrategy() + { + $strategy = ConfigResolver::UNDEFINED_STRATEGY_NULL; + $defaultNS = 'ibexa.site_access.config'; + $resolver = $this->getResolver($defaultNS, $strategy); + + $this->assertSame($strategy, $resolver->getUndefinedStrategy()); + $resolver->setUndefinedStrategy(ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION); + $this->assertSame(ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION, $resolver->getUndefinedStrategy()); + + $this->assertSame($defaultNS, $resolver->getDefaultNamespace()); + $resolver->setDefaultNamespace('anotherNamespace'); + $this->assertSame('anotherNamespace', $resolver->getDefaultNamespace()); + } + + public function testGetParameterFailedWithException() + { + $this->expectException(ParameterNotFoundException::class); + + $resolver = $this->getResolver('ibexa.site_access.config', ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION); + $resolver->getParameter('foo'); + } + + public function testGetParameterFailedNull() + { + $resolver = $this->getResolver('ibexa.site_access.config', ConfigResolver::UNDEFINED_STRATEGY_NULL); + $this->assertNull($resolver->getParameter('foo')); + } + + public function parameterProvider() + { + return [ + ['foo', 'bar'], + ['some.parameter', true], + ['some.other.parameter', ['foo', 'bar', 'baz']], + ['a.hash.parameter', ['foo' => 'bar', 'tata' => 'toto']], + [ + 'a.deep.hash', [ + 'foo' => 'bar', + 'tata' => 'toto', + 'deeper_hash' => [ + 'likeStarWars' => true, + 'jedi' => ['Obi-Wan Kenobi', 'Mace Windu', 'Luke Skywalker', 'Leïa Skywalker (yes! Read episodes 7-8-9!)'], + 'sith' => ['Darth Vader', 'Darth Maul', 'Palpatine'], + 'roles' => [ + 'Amidala' => ['Queen'], + 'Palpatine' => ['Senator', 'Emperor', 'Villain'], + 'C3PO' => ['Droid', 'Annoying guy'], + 'Jar-Jar' => ['Still wondering his role', 'Annoying guy'], + ], + ], + ], + ], + ]; + } + + /** + * @dataProvider parameterProvider + */ + public function testGetParameterGlobalScope($paramName, $expectedValue) + { + $globalScopeParameter = "ibexa.site_access.config.global.$paramName"; + $this->containerMock + ->expects($this->once()) + ->method('hasParameter') + ->with($globalScopeParameter) + ->will($this->returnValue(true)); + $this->containerMock + ->expects($this->once()) + ->method('getParameter') + ->with($globalScopeParameter) + ->will($this->returnValue($expectedValue)); + + $this->assertSame($expectedValue, $this->getResolver()->getParameter($paramName)); + } + + /** + * @dataProvider parameterProvider + */ + public function testGetParameterRelativeScope($paramName, $expectedValue) + { + $relativeScopeParameter = "ibexa.site_access.config.{$this->siteAccess->name}.$paramName"; + $this->containerMock + ->expects($this->exactly(2)) + ->method('hasParameter') + ->with( + $this->logicalOr( + "ibexa.site_access.config.global.$paramName", + $relativeScopeParameter + ) + ) + // First call is for "global" scope, second is the right one + ->will($this->onConsecutiveCalls(false, true)); + $this->containerMock + ->expects($this->once()) + ->method('getParameter') + ->with($relativeScopeParameter) + ->will($this->returnValue($expectedValue)); + + $this->assertSame($expectedValue, $this->getResolver()->getParameter($paramName)); + } + + /** + * @dataProvider parameterProvider + */ + public function testGetParameterSpecificScope($paramName, $expectedValue) + { + $scope = 'some_siteaccess'; + $relativeScopeParameter = "ibexa.site_access.config.$scope.$paramName"; + $this->containerMock + ->expects($this->exactly(2)) + ->method('hasParameter') + ->with( + $this->logicalOr( + "ibexa.site_access.config.global.$paramName", + $relativeScopeParameter + ) + ) + // First call is for "global" scope, second is the right one + ->will($this->onConsecutiveCalls(false, true)); + $this->containerMock + ->expects($this->once()) + ->method('getParameter') + ->with($relativeScopeParameter) + ->will($this->returnValue($expectedValue)); + + $this->assertSame( + $expectedValue, + $this->getResolver()->getParameter($paramName, 'ibexa.site_access.config', $scope) + ); + } + + /** + * @dataProvider parameterProvider + */ + public function testGetParameterDefaultScope($paramName, $expectedValue) + { + $defaultScopeParameter = "ibexa.site_access.config.default.$paramName"; + $relativeScopeParameter = "ibexa.site_access.config.{$this->siteAccess->name}.$paramName"; + $this->containerMock + ->expects($this->exactly(3)) + ->method('hasParameter') + ->with( + $this->logicalOr( + "ibexa.site_access.config.global.$paramName", + $relativeScopeParameter, + $defaultScopeParameter + ) + ) + // First call is for "global" scope, second is the right one + ->will($this->onConsecutiveCalls(false, false, true)); + $this->containerMock + ->expects($this->once()) + ->method('getParameter') + ->with($defaultScopeParameter) + ->will($this->returnValue($expectedValue)); + + $this->assertSame($expectedValue, $this->getResolver()->getParameter($paramName)); + } + + public function hasParameterProvider() + { + return [ + [true, true, true, true, true], + [true, true, true, false, true], + [true, true, false, false, true], + [false, false, false, false, false], + [false, false, true, false, true], + [false, false, false, true, true], + [false, false, true, true, true], + [false, true, false, false, true], + ]; + } + + /** + * @dataProvider hasParameterProvider + */ + public function testHasParameterNoNamespace($defaultMatch, $groupMatch, $scopeMatch, $globalMatch, $expectedResult) + { + $paramName = 'foo.bar'; + $groupName = 'my_group'; + $configResolver = $this->getResolver( + 'ibexa.site_access.config', + ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION, + [$this->siteAccess->name => [$groupName]] + ); + + $this->containerMock->expects($this->atLeastOnce()) + ->method('hasParameter') + ->will( + $this->returnValueMap( + [ + ["ibexa.site_access.config.default.$paramName", $defaultMatch], + ["ibexa.site_access.config.$groupName.$paramName", $groupMatch], + ["ibexa.site_access.config.{$this->siteAccess->name}.$paramName", $scopeMatch], + ["ibexa.site_access.config.global.$paramName", $globalMatch], + ] + ) + ); + + $this->assertSame($expectedResult, $configResolver->hasParameter($paramName)); + } + + /** + * @dataProvider hasParameterProvider + */ + public function testHasParameterWithNamespaceAndScope($defaultMatch, $groupMatch, $scopeMatch, $globalMatch, $expectedResult) + { + $paramName = 'foo.bar'; + $namespace = 'my.namespace'; + $scope = 'another_siteaccess'; + $groupName = 'my_group'; + $configResolver = $this->getResolver( + 'ibexa.site_access.config', + ConfigResolver::UNDEFINED_STRATEGY_EXCEPTION, + [ + $this->siteAccess->name => ['some_group'], + $scope => [$groupName], + ] + ); + + $this->containerMock->expects($this->atLeastOnce()) + ->method('hasParameter') + ->will( + $this->returnValueMap( + [ + ["$namespace.default.$paramName", $defaultMatch], + ["$namespace.$groupName.$paramName", $groupMatch], + ["$namespace.$scope.$paramName", $scopeMatch], + ["$namespace.global.$paramName", $globalMatch], + ] + ) + ); + + $this->assertSame($expectedResult, $configResolver->hasParameter($paramName, $namespace, $scope)); + } + + public function testGetSetDefaultScope() + { + $newDefaultScope = 'bar'; + $configResolver = $this->getResolver(); + $this->assertSame($this->siteAccess->name, $configResolver->getDefaultScope()); + $configResolver->setDefaultScope($newDefaultScope); + $this->assertSame($newDefaultScope, $configResolver->getDefaultScope()); + } +} + +class_alias(ConfigResolverTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\ConfigResolverTest'); diff --git a/tests/bundle/Core/ControllerArgumentResolver/LocationArgumentResolverTest.php b/tests/bundle/Core/ControllerArgumentResolver/LocationArgumentResolverTest.php new file mode 100644 index 0000000000..36a36c0bdc --- /dev/null +++ b/tests/bundle/Core/ControllerArgumentResolver/LocationArgumentResolverTest.php @@ -0,0 +1,153 @@ +createMock(LocationService::class); + $this->locationArgumentResolver = new LocationArgumentResolver($locationService); + } + + /** + * @dataProvider provideDataForTestSupports + */ + public function testSupports( + bool $expected, + Request $request, + ArgumentMetadata $argumentMetadata + ): void { + self::assertSame( + $expected, + $this->locationArgumentResolver->supports( + $request, + $argumentMetadata + ) + ); + } + + public function testResolveThrowsInvalidArgumentException(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'locationId\' is invalid: Expected numeric type, string given.'); + + $generator = $this->locationArgumentResolver->resolve( + new Request( + [ + 'locationId' => 'foo', + ] + ), + $this->createMock(ArgumentMetadata::class) + ); + + self::assertInstanceOf(Generator::class, $generator); + + $generator->getReturn(); + } + + public function testResolve(): void + { + $resolvedArgumentsGenerator = $this->locationArgumentResolver->resolve( + $this->createRequest(true, false, 1), + $this->createMock(ArgumentMetadata::class) + ); + + self::assertInstanceOf(Generator::class, $resolvedArgumentsGenerator); + $resolvedArguments = iterator_to_array($resolvedArgumentsGenerator); + + self::assertCount(1, $resolvedArguments); + + $value = current($resolvedArguments); + self::assertInstanceOf( + Location::class, + $value + ); + } + + /** + * @return iterable + */ + public function provideDataForTestSupports(): iterable + { + $locationBasedArgumentMetadata = $this->createArgumentMetadata(Location::class); + + yield 'Supported - locationId passed to request query' => [ + true, + $this->createRequest(true, false, 1), + $locationBasedArgumentMetadata, + ]; + + yield 'Not supported - type different than Ibexa\Contracts\Core\Repository\Values\Content\Location' => [ + false, + $this->createRequest(true, false, 1), + $this->createArgumentMetadata('foo'), + ]; + + yield 'Not supported - locationId passed to request attributes' => [ + false, + $this->createRequest(false, true, 1), + $locationBasedArgumentMetadata, + ]; + + yield 'Not supported - locationId passed to request attributes and query' => [ + false, + $this->createRequest(true, true, 1), + $locationBasedArgumentMetadata, + ]; + } + + private function createArgumentMetadata(string $type): ArgumentMetadata + { + $argumentMetadata = $this->createMock(ArgumentMetadata::class); + $argumentMetadata + ->method('getType') + ->willReturn($type); + + return $argumentMetadata; + } + + private function createRequest( + bool $addToQuery, + bool $addToAttributes, + ?int $locationId = null + ): Request { + $request = Request::create('/'); + + if ($addToQuery) { + $request->query->set(self::PARAMETER_LOCATION_ID, $locationId); + } + + if ($addToAttributes) { + $request->attributes->set(self::PARAMETER_LOCATION_ID, $locationId); + } + + return $request; + } +} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Converter/AbstractParamConverterTest.php b/tests/bundle/Core/Converter/AbstractParamConverterTest.php similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Converter/AbstractParamConverterTest.php rename to tests/bundle/Core/Converter/AbstractParamConverterTest.php index ec03157a9b..4a2053229e 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Converter/AbstractParamConverterTest.php +++ b/tests/bundle/Core/Converter/AbstractParamConverterTest.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Converter; +namespace Ibexa\Tests\Bundle\Core\Converter; use PHPUnit\Framework\TestCase; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; @@ -33,3 +33,5 @@ public function createConfiguration($class = null, $name = null) return $config; } } + +class_alias(AbstractParamConverterTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Converter\AbstractParamConverterTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Converter/ContentParamConverterTest.php b/tests/bundle/Core/Converter/ContentParamConverterTest.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Converter/ContentParamConverterTest.php rename to tests/bundle/Core/Converter/ContentParamConverterTest.php index 70774f4ce5..21cf408c6f 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Converter/ContentParamConverterTest.php +++ b/tests/bundle/Core/Converter/ContentParamConverterTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Converter; +namespace Ibexa\Tests\Bundle\Core\Converter; -use eZ\Bundle\EzPublishCoreBundle\Converter\ContentParamConverter; -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Content\Content; +use Ibexa\Bundle\Core\Converter\ContentParamConverter; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; use Symfony\Component\HttpFoundation\Request; class ContentParamConverterTest extends AbstractParamConverterTest @@ -17,7 +17,7 @@ class ContentParamConverterTest extends AbstractParamConverterTest public const CONTENT_CLASS = Content::class; - /** @var \eZ\Bundle\EzPublishCoreBundle\Converter\ContentParamConverter */ + /** @var \Ibexa\Bundle\Core\Converter\ContentParamConverter */ private $converter; private $contentServiceMock; @@ -72,3 +72,5 @@ public function testApplyContentOptionalWithEmptyAttribute() $this->assertNull($request->attributes->get('content')); } } + +class_alias(ContentParamConverterTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Converter\ContentParamConverterTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Converter/LocationParamConverterTest.php b/tests/bundle/Core/Converter/LocationParamConverterTest.php similarity index 77% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Converter/LocationParamConverterTest.php rename to tests/bundle/Core/Converter/LocationParamConverterTest.php index 58bb42e6e1..e2b69783f1 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Converter/LocationParamConverterTest.php +++ b/tests/bundle/Core/Converter/LocationParamConverterTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Converter; +namespace Ibexa\Tests\Bundle\Core\Converter; -use eZ\Bundle\EzPublishCoreBundle\Converter\LocationParamConverter; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\Helper\ContentPreviewHelper; +use Ibexa\Bundle\Core\Converter\LocationParamConverter; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Core\Helper\ContentPreviewHelper; use Symfony\Component\HttpFoundation\Request; class LocationParamConverterTest extends AbstractParamConverterTest @@ -18,20 +18,17 @@ class LocationParamConverterTest extends AbstractParamConverterTest public const LOCATION_CLASS = Location::class; - /** @var \eZ\Bundle\EzPublishCoreBundle\Converter\LocationParamConverter */ + /** @var \Ibexa\Bundle\Core\Converter\LocationParamConverter */ private $converter; private $locationServiceMock; - /** @var \eZ\Publish\Core\Helper\ContentPreviewHelper&\PHPUnit\Framework\MockObject\MockObject */ - private $contentPreviewHelperMock; - protected function setUp(): void { $this->locationServiceMock = $this->createMock(LocationService::class); - $this->contentPreviewHelperMock = $this->createMock(ContentPreviewHelper::class); + $contentPreviewHelper = $this->createMock(ContentPreviewHelper::class); - $this->converter = new LocationParamConverter($this->locationServiceMock, $this->contentPreviewHelperMock); + $this->converter = new LocationParamConverter($this->locationServiceMock, $contentPreviewHelper); } public function testSupports() @@ -78,3 +75,5 @@ public function testApplyLocationOptionalWithEmptyAttribute() $this->assertNull($request->attributes->get('location')); } } + +class_alias(LocationParamConverterTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Converter\LocationParamConverterTest'); diff --git a/tests/bundle/Core/DependencyInjection/Compiler/AggregateFieldValueMapperPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/AggregateFieldValueMapperPassTest.php index 35a7fab90c..54e227c678 100644 --- a/tests/bundle/Core/DependencyInjection/Compiler/AggregateFieldValueMapperPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/AggregateFieldValueMapperPassTest.php @@ -8,8 +8,9 @@ namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Publish\Core\Base\Container\Compiler\Search\AggregateFieldValueMapperPass; -use eZ\Publish\SPI\Search\FieldType\BooleanField; +use Ibexa\Contracts\Core\Search\FieldType\BooleanField; +use Ibexa\Core\Base\Container\Compiler\Search\AggregateFieldValueMapperPass; +use Ibexa\Core\Search\Common\FieldValueMapper\Aggregate; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -20,7 +21,7 @@ final class AggregateFieldValueMapperPassTest extends AbstractCompilerPassTestCa protected function setUp(): void { parent::setUp(); - $this->setDefinition(AggregateFieldValueMapperPass::SERVICE_ID, new Definition()); + $this->setDefinition(Aggregate::class, new Definition()); } protected function registerCompilerPass(ContainerBuilder $container): void @@ -41,7 +42,7 @@ public function testAddMapper(): void $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - AggregateFieldValueMapperPass::SERVICE_ID, + Aggregate::class, 'addMapper', [new Reference($fieldValueMapperServiceId), BooleanField::class] ); diff --git a/tests/bundle/Core/DependencyInjection/Compiler/ChainConfigResolverPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/ChainConfigResolverPassTest.php new file mode 100644 index 0000000000..c973128d31 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Compiler/ChainConfigResolverPassTest.php @@ -0,0 +1,82 @@ +setDefinition(ChainConfigResolver::class, new Definition()); + } + + /** + * Register the compiler pass under test, just like you would do inside a bundle's load() + * method:. + * + * $container->addCompilerPass(new MyCompilerPass()); + */ + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new ChainConfigResolverPass()); + } + + /** + * @param int|null $declaredPriority + * @param int $expectedPriority + * + * @dataProvider addResolverProvider + */ + public function testAddResolver($declaredPriority, $expectedPriority) + { + $resolverDef = new Definition(); + $serviceId = 'some_service_id'; + $resolverDef->addTag( + 'ibexa.site.config.resolver', + null !== $declaredPriority + ? ['priority' => $declaredPriority] + : [] + ); + + $this->setDefinition($serviceId, $resolverDef); + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + ChainConfigResolver::class, + 'addResolver', + [new Reference($serviceId), $expectedPriority] + ); + } + + public function addResolverProvider() + { + return [ + [null, 0], + [0, 0], + [57, 57], + [-23, -23], + [-255, -255], + [-256, -255], + [-1000, -255], + [255, 255], + [256, 255], + [1000, 255], + ]; + } +} + +class_alias(ChainConfigResolverPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\ChainConfigResolverPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ChainRoutingPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/ChainRoutingPassTest.php similarity index 77% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ChainRoutingPassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/ChainRoutingPassTest.php index 53dbbc4913..c9ce1f34e9 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ChainRoutingPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/ChainRoutingPassTest.php @@ -4,20 +4,26 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ChainRoutingPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\ChainRoutingPass; +use Ibexa\Core\MVC\Symfony\Routing\ChainRouter; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; +/** + * @covers \Ibexa\Bundle\Core\DependencyInjection\Compiler\ChainRoutingPass + */ class ChainRoutingPassTest extends AbstractCompilerPassTestCase { protected function setUp(): void { parent::setUp(); - $this->setDefinition('ezpublish.chain_router', new Definition()); + $this->setDefinition(ChainRouter::class, new Definition()); } /** @@ -35,7 +41,6 @@ protected function registerCompilerPass(ContainerBuilder $container): void * @param int|null $declaredPriority * @param int $expectedPriority * - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ChainRoutingPass::process * @dataProvider addRouterProvider */ public function testAddRouter($declaredPriority, $expectedPriority) @@ -52,7 +57,7 @@ public function testAddRouter($declaredPriority, $expectedPriority) $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.chain_router', + ChainRouter::class, 'add', [new Reference($serviceId), $expectedPriority] ); @@ -62,16 +67,15 @@ public function testAddRouter($declaredPriority, $expectedPriority) * @param int|null $declaredPriority * @param int $expectedPriority * - * @covers \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ChainRoutingPass::process * @dataProvider addRouterProvider */ public function testAddRouterWithDefaultRouter($declaredPriority, $expectedPriority) { $defaultRouter = new Definition(); $this->setDefinition('router.default', $defaultRouter); - $this->setDefinition('ezpublish.siteaccess', new Definition()); - $this->setDefinition('ezpublish.config.resolver', new Definition()); - $this->setDefinition('ezpublish.siteaccess_router', new Definition()); + $this->setDefinition(SiteAccess::class, new Definition()); + $this->setDefinition('ibexa.config.resolver', new Definition()); + $this->setDefinition(Router::class, new Definition()); $resolverDef = new Definition(); $serviceId = 'some_service_id'; @@ -88,32 +92,32 @@ public function testAddRouterWithDefaultRouter($declaredPriority, $expectedPrior $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'router.default', 'setSiteAccess', - [new Reference('ezpublish.siteaccess')] + [new Reference(SiteAccess::class)] ); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'router.default', 'setConfigResolver', - [new Reference('ezpublish.config.resolver')] + [new Reference('ibexa.config.resolver')] ); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'router.default', 'setNonSiteAccessAwareRoutes', - ['%ezpublish.default_router.non_siteaccess_aware_routes%'] + ['%ibexa.default_router.non_site_access_aware_routes%'] ); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( 'router.default', 'setSiteAccessRouter', - [new Reference('ezpublish.siteaccess_router')] + [new Reference(Router::class)] ); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.chain_router', + ChainRouter::class, 'add', [new Reference('router.default'), 255] ); // Assertion for all routers $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.chain_router', + ChainRouter::class, 'add', [new Reference($serviceId), $expectedPriority] ); @@ -135,3 +139,5 @@ public function addRouterProvider() ]; } } + +class_alias(ChainRoutingPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\ChainRoutingPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ConsoleCommandPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/ConsoleCommandPassTest.php similarity index 83% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ConsoleCommandPassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/ConsoleCommandPassTest.php index 9a48d15fd1..35346f0fd8 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ConsoleCommandPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/ConsoleCommandPassTest.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ConsoleCommandPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\ConsoleCommandPass; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -42,3 +42,5 @@ public function testAddSiteaccessOption(): void ); } } + +class_alias(ConsoleCommandPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\ConsoleCommandPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPassTest.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPassTest.php index 8db32a0194..de9d06b5a4 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/EntityManagerFactoryServiceLocatorPassTest.php @@ -6,9 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\EntityManagerFactoryServiceLocatorPass; +use Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\EntityManagerFactoryServiceLocatorPass; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\Argument\ServiceClosureArgument; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -24,7 +25,7 @@ protected function setUp(): void $this->setDefinition( 'ibexa.doctrine.orm.entity_manager_factory', new Definition(null, [ - '$repositoryConfigurationProvider' => new Reference('ezpublish.api.repository_configuration_provider'), + '$repositoryConfigurationProvider' => new Reference(RepositoryConfigurationProvider::class), '$defaultConnection' => '%doctrine.default_connection%', '$entityManagers' => '%doctrine.entity_managers%', ]) @@ -65,3 +66,5 @@ public function testAddServiceLocatorArgument(): void ); } } + +class_alias(EntityManagerFactoryServiceLocatorPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\EntityManagerFactoryServiceLocatorPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPassTest.php similarity index 80% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPassTest.php index 09782098ac..f034ae26a6 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/FieldTypeParameterProviderRegistryPassTest.php @@ -4,9 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\FieldTypeParameterProviderRegistryPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\FieldTypeParameterProviderRegistryPass; +use Ibexa\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -17,7 +18,7 @@ class FieldTypeParameterProviderRegistryPassTest extends AbstractCompilerPassTes protected function setUp(): void { parent::setUp(); - $this->setDefinition('ezpublish.fieldType.parameterProviderRegistry', new Definition()); + $this->setDefinition(ParameterProviderRegistry::class, new Definition()); } /** @@ -45,7 +46,7 @@ public function testRegisterFieldType(string $tag) $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.fieldType.parameterProviderRegistry', + ParameterProviderRegistry::class, 'setParameterProvider', [new Reference($serviceId), $fieldTypeIdentifier] ); @@ -69,7 +70,7 @@ public function testRegisterFieldTypeNoAlias(string $tag) $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.fieldType.parameterProviderRegistry', + ParameterProviderRegistry::class, 'setParameterProvider', [new Reference($serviceId), $fieldTypeIdentifier] ); @@ -78,8 +79,9 @@ public function testRegisterFieldTypeNoAlias(string $tag) public function tagsProvider(): array { return [ - [FieldTypeParameterProviderRegistryPass::DEPRECATED_FIELD_TYPE_PARAMETER_PROVIDER_SERVICE_TAG], [FieldTypeParameterProviderRegistryPass::FIELD_TYPE_PARAMETER_PROVIDER_SERVICE_TAG], ]; } } + +class_alias(FieldTypeParameterProviderRegistryPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\FieldTypeParameterProviderRegistryPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/FragmentPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/FragmentPassTest.php similarity index 81% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/FragmentPassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/FragmentPassTest.php index 61aa9a348f..f59baf694a 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/FragmentPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/FragmentPassTest.php @@ -4,10 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\FragmentPass; -use eZ\Bundle\EzPublishCoreBundle\Fragment\InlineFragmentRenderer; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\FragmentPass; +use Ibexa\Bundle\Core\Fragment\DecoratedFragmentRenderer; +use Ibexa\Bundle\Core\Fragment\FragmentListenerFactory; +use Ibexa\Bundle\Core\Fragment\InlineFragmentRenderer; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -36,8 +38,8 @@ public function testProcess() $this->setDefinition('fragment.renderer.inline', $inlineRendererDef); $this->setDefinition('fragment.renderer.esi', $esiRendererDef); $this->setDefinition('fragment.renderer.hinclude', $hincludeRendererDef); - $this->setDefinition('ezpublish.decorated_fragment_renderer', $decoratedFragmentRendererDef); - $this->setDefinition('ezpublish.fragment_listener.factory', new Definition()); + $this->setDefinition(DecoratedFragmentRenderer::class, $decoratedFragmentRendererDef); + $this->setDefinition(FragmentListenerFactory::class, new Definition()); $this->compile(); @@ -47,7 +49,7 @@ public function testProcess() $factoryArray = $fragmentListenerDef->getFactory(); $this->assertInstanceOf(Reference::class, $factoryArray[0]); $this->assertEquals('buildFragmentListener', $factoryArray[1]); - $this->assertEquals('ezpublish.fragment_listener.factory', $factoryArray[0]); + $this->assertEquals(FragmentListenerFactory::class, $factoryArray[0]); $this->assertTrue($this->container->hasDefinition('fragment.renderer.inline.inner')); $this->assertSame($inlineRendererDef, $this->container->getDefinition('fragment.renderer.inline.inner')); @@ -59,7 +61,7 @@ public function testProcess() $this->assertSame($hincludeRendererDef, $this->container->getDefinition('fragment.renderer.hinclude.inner')); $this->assertFalse($hincludeRendererDef->isPublic()); - $this->assertContainerBuilderHasServiceDefinitionWithParent('fragment.renderer.inline', 'ezpublish.decorated_fragment_renderer'); + $this->assertContainerBuilderHasServiceDefinitionWithParent('fragment.renderer.inline', DecoratedFragmentRenderer::class); $decoratedInlineDef = $this->container->getDefinition('fragment.renderer.inline'); $this->assertSame(['kernel.fragment_renderer' => [[]]], $decoratedInlineDef->getTags()); $this->assertEquals( @@ -68,7 +70,7 @@ public function testProcess() ); $this->assertSame(InlineFragmentRenderer::class, $decoratedInlineDef->getClass()); - $this->assertContainerBuilderHasServiceDefinitionWithParent('fragment.renderer.esi', 'ezpublish.decorated_fragment_renderer'); + $this->assertContainerBuilderHasServiceDefinitionWithParent('fragment.renderer.esi', DecoratedFragmentRenderer::class); $decoratedEsiDef = $this->container->getDefinition('fragment.renderer.esi'); $this->assertSame(['kernel.fragment_renderer' => [[]]], $decoratedEsiDef->getTags()); $this->assertEquals( @@ -76,7 +78,7 @@ public function testProcess() $decoratedEsiDef->getArguments() ); - $this->assertContainerBuilderHasServiceDefinitionWithParent('fragment.renderer.hinclude', 'ezpublish.decorated_fragment_renderer'); + $this->assertContainerBuilderHasServiceDefinitionWithParent('fragment.renderer.hinclude', DecoratedFragmentRenderer::class); $decoratedHincludeDef = $this->container->getDefinition('fragment.renderer.hinclude'); $this->assertSame(['kernel.fragment_renderer' => [[]]], $decoratedHincludeDef->getTags()); $this->assertEquals( @@ -85,3 +87,5 @@ public function testProcess() ); } } + +class_alias(FragmentPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\FragmentPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/InjectEntityManagerMappingPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingPassTest.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/InjectEntityManagerMappingPassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingPassTest.php index b6edde6c5b..4834b0f9a7 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/InjectEntityManagerMappingPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/InjectEntityManagerMappingPassTest.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\InjectEntityManagerMappingsPass; -use eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Stub\AnnotationEntityBundle\AnnotationEntityBundle; -use eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Stub\XmlEntityBundle\XmlEntityBundle; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\InjectEntityManagerMappingsPass; +use Ibexa\Tests\Bundle\Core\DependencyInjection\Stub\AnnotationEntityBundle\AnnotationEntityBundle; +use Ibexa\Tests\Bundle\Core\DependencyInjection\Stub\XmlEntityBundle\XmlEntityBundle; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -28,13 +28,13 @@ class InjectEntityManagerMappingPassTest extends AbstractCompilerPassTestCase 'is_bundle' => true, 'type' => 'annotation', 'dir' => 'Entity', - 'prefix' => '\eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Stub\AnnotationEntityBundle\Entity', + 'prefix' => '\Ibexa\Tests\Bundle\Core\DependencyInjection\Stub\AnnotationEntityBundle\Entity', ], 'XmlEntityBundle' => [ 'is_bundle' => true, 'type' => 'xml', 'dir' => 'config', - 'prefix' => '\eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Stub\XmlEntityBundle\XmlEntityBundle\Entity', + 'prefix' => '\Ibexa\Tests\Bundle\Core\DependencyInjection\Stub\XmlEntityBundle\XmlEntityBundle\Entity', ], ]; @@ -100,3 +100,5 @@ public function testInjectEntityMapping(): void } } } + +class_alias(InjectEntityManagerMappingPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\InjectEntityManagerMappingPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/LazyDoctrineRepositoriesPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/LazyDoctrineRepositoriesPassTest.php similarity index 87% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/LazyDoctrineRepositoriesPassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/LazyDoctrineRepositoriesPassTest.php index 4faa60ff41..1bca013572 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/LazyDoctrineRepositoriesPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/LazyDoctrineRepositoriesPassTest.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\LazyDoctrineRepositoriesPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\LazyDoctrineRepositoriesPass; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use RuntimeException; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -53,3 +53,5 @@ public function testNonLazyServices(): void $this->compile(); } } + +class_alias(LazyDoctrineRepositoriesPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\LazyDoctrineRepositoriesPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/NotificationRendererPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/NotificationRendererPassTest.php similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/NotificationRendererPassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/NotificationRendererPassTest.php index 9bff149f97..91a780db15 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/NotificationRendererPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/NotificationRendererPassTest.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\NotificationRendererPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\NotificationRendererPass; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -59,3 +59,5 @@ public function testAddRendererWithoutAliasThrowsLogicException() $this->compile(); } } + +class_alias(NotificationRendererPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\NotificationRendererPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/PlaceholderProviderPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/PlaceholderProviderPassTest.php similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/PlaceholderProviderPassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/PlaceholderProviderPassTest.php index 8baebaa683..fd54bb4f0d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/PlaceholderProviderPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/PlaceholderProviderPassTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\PlaceholderProviderPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\PlaceholderProviderPass; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -55,3 +55,5 @@ public function testAddProviderWithoutType() $this->compile(); } } + +class_alias(PlaceholderProviderPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\PlaceholderProviderPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/QueryTypePassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/QueryTypePassTest.php similarity index 82% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/QueryTypePassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/QueryTypePassTest.php index 821638d5df..223d1dec18 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/QueryTypePassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/QueryTypePassTest.php @@ -6,11 +6,12 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\QueryTypePass; -use eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Stub\QueryTypeBundle\QueryType\TestQueryType; -use eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Stub\QueryTypeBundle\QueryTypeBundle; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\QueryTypePass; +use Ibexa\Core\QueryType\ArrayQueryTypeRegistry; +use Ibexa\Tests\Bundle\Core\DependencyInjection\Stub\QueryTypeBundle\QueryType\TestQueryType; +use Ibexa\Tests\Bundle\Core\DependencyInjection\Stub\QueryTypeBundle\QueryTypeBundle; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -22,7 +23,7 @@ protected function setUp(): void { parent::setUp(); - $this->setDefinition('ezpublish.query_type.registry', new Definition()); + $this->setDefinition(ArrayQueryTypeRegistry::class, new Definition()); } protected function registerCompilerPass(ContainerBuilder $container): void @@ -43,7 +44,7 @@ public function testRegisterTaggedQueryType(string $tag): void $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.query_type.registry', + ArrayQueryTypeRegistry::class, 'addQueryTypes', [['Test:Test' => new Reference($serviceId)]] ); @@ -63,7 +64,7 @@ public function testRegisterTaggedQueryTypeWithClassAsParameter(string $tag): vo $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.query_type.registry', + ArrayQueryTypeRegistry::class, 'addQueryTypes', [['Test:Test' => new Reference($serviceId)]] ); @@ -95,7 +96,7 @@ public function testTaggedOverride(string $tag): void $this->assertContainerBuilderHasService('test.query_type_override'); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.query_type.registry', + ArrayQueryTypeRegistry::class, 'addQueryTypes', [ [ @@ -110,7 +111,8 @@ public function tagsProvider(): iterable { return [ [QueryTypePass::QUERY_TYPE_SERVICE_TAG], - [QueryTypePass::DEPRECATED_QUERY_TYPE_SERVICE_TAG], ]; } } + +class_alias(QueryTypePassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\QueryTypePassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/RegisterSearchEngineIndexerPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/RegisterSearchEngineIndexerPassTest.php similarity index 80% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/RegisterSearchEngineIndexerPassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/RegisterSearchEngineIndexerPassTest.php index dfafb67ea3..e7cfd926cc 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/RegisterSearchEngineIndexerPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/RegisterSearchEngineIndexerPassTest.php @@ -6,9 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\RegisterSearchEngineIndexerPass; +use Ibexa\Bundle\Core\ApiLoader\SearchEngineIndexerFactory; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\RegisterSearchEngineIndexerPass; use LogicException; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -24,7 +25,7 @@ protected function setUp(): void { parent::setUp(); - $this->setDefinition('ezpublish.api.search_engine.indexer.factory', new Definition()); + $this->setDefinition(SearchEngineIndexerFactory::class, new Definition()); } protected function registerCompilerPass(ContainerBuilder $container): void @@ -46,7 +47,7 @@ public function testRegisterSearchEngineIndexer(string $tag): void $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.api.search_engine.indexer.factory', + SearchEngineIndexerFactory::class, 'registerSearchEngineIndexer', [ new Reference(self::EXAMPLE_SERVICE_ID), @@ -73,7 +74,8 @@ public function tagsProvider(): iterable { return [ [RegisterSearchEngineIndexerPass::SEARCH_ENGINE_INDEXER_SERVICE_TAG], - [RegisterSearchEngineIndexerPass::DEPRECATED_SEARCH_ENGINE_INDEXER_SERVICE_TAG], ]; } } + +class_alias(RegisterSearchEngineIndexerPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\RegisterSearchEngineIndexerPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/RegisterSearchEnginePassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/RegisterSearchEnginePassTest.php similarity index 81% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/RegisterSearchEnginePassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/RegisterSearchEnginePassTest.php index 16243ebb91..198fde59a1 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/RegisterSearchEnginePassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/RegisterSearchEnginePassTest.php @@ -6,9 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\RegisterSearchEnginePass; +use Ibexa\Bundle\Core\ApiLoader\SearchEngineFactory; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\RegisterSearchEnginePass; use LogicException; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -24,7 +25,7 @@ protected function setUp(): void { parent::setUp(); - $this->setDefinition('ezpublish.api.search_engine.factory', new Definition()); + $this->setDefinition(SearchEngineFactory::class, new Definition()); } protected function registerCompilerPass(ContainerBuilder $container): void @@ -46,7 +47,7 @@ public function testRegisterSearchEngine(string $tag): void $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.api.search_engine.factory', + SearchEngineFactory::class, 'registerSearchEngine', [ new Reference(self::EXAMPLE_SERVICE_ID), @@ -73,7 +74,8 @@ public function tagsProvider(): iterable { return [ [RegisterSearchEnginePass::SEARCH_ENGINE_SERVICE_TAG], - [RegisterSearchEnginePass::DEPRECATED_SEATCH_ENGINE_SERVICE_TAG], ]; } } + +class_alias(RegisterSearchEnginePassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\RegisterSearchEnginePassTest'); diff --git a/tests/bundle/Core/DependencyInjection/Compiler/RegisterStorageEnginePassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/RegisterStorageEnginePassTest.php new file mode 100644 index 0000000000..97d6cfd0f0 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Compiler/RegisterStorageEnginePassTest.php @@ -0,0 +1,92 @@ +setDefinition(StorageEngineFactory::class, new Definition()); + $this->container->setParameter('ibexa.api.storage_engine.default', 'default_storage_engine'); + } + + /** + * Register the compiler pass under test, just like you would do inside a bundle's load() + * method:. + * + * $container->addCompilerPass(new MyCompilerPass()); + */ + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new RegisterStorageEnginePass()); + } + + public function testRegisterStorageEngine() + { + $storageEngineDef = new Definition(); + $storageEngineIdentifier = 'i_am_a_storage_engine'; + $storageEngineDef->addTag('ibexa.storage', ['alias' => $storageEngineIdentifier]); + $serviceId = 'storage_engine_service'; + $this->setDefinition($serviceId, $storageEngineDef); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + StorageEngineFactory::class, + 'registerStorageEngine', + [$serviceId, $storageEngineIdentifier] + ); + } + + public function testRegisterDefaultStorageEngine() + { + $storageEngineDef = new Definition(); + $storageEngineIdentifier = 'i_am_a_storage_engine'; + + $this->container->setParameter('ibexa.api.storage_engine.default', $storageEngineIdentifier); + $storageEngineDef->addTag('ibexa.storage', ['alias' => $storageEngineIdentifier]); + $serviceId = 'storage_engine_service'; + $this->setDefinition($serviceId, $storageEngineDef); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + StorageEngineFactory::class, + 'registerStorageEngine', + [new Reference($serviceId), $storageEngineIdentifier] + ); + } + + public function testRegisterStorageEngineNoAlias() + { + $this->expectException(\LogicException::class); + + $storageEngineDef = new Definition(); + $storageEngineIdentifier = 'i_am_a_storage_engine'; + $storageEngineDef->addTag('ibexa.storage'); + $serviceId = 'storage_engine_service'; + $this->setDefinition($serviceId, $storageEngineDef); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + StorageEngineFactory::class, + 'registerStorageEngine', + [$serviceId, $storageEngineIdentifier] + ); + } +} + +class_alias(RegisterStorageEnginePassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\RegisterStorageEnginePassTest'); diff --git a/tests/bundle/Core/DependencyInjection/Compiler/SecurityPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/SecurityPassTest.php new file mode 100644 index 0000000000..605e800554 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Compiler/SecurityPassTest.php @@ -0,0 +1,86 @@ +setDefinition('security.authentication.provider.dao', new Definition()); + $this->setDefinition('security.authentication.provider.rememberme', new Definition()); + $this->setDefinition('security.authentication.provider.guard', new Definition()); + $this->setDefinition('security.authentication.provider.anonymous', new Definition()); + $this->setDefinition('security.http_utils', new Definition()); + $this->setDefinition('security.authentication.success_handler', new Definition()); + $this->setDefinition('ibexa.config.resolver', new Definition()); + $this->setDefinition(SiteAccess::class, new Definition()); + $this->setDefinition(PermissionResolver::class, new Definition()); + $this->setDefinition(UserService::class, new Definition()); + } + + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new SecurityPass()); + } + + public function testAlteredDaoAuthenticationProvider() + { + $this->compile(); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'security.authentication.provider.dao', + 'setPermissionResolver', + [new Reference(PermissionResolver::class)] + ); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'security.authentication.provider.dao', + 'setUserService', + [new Reference(UserService::class)] + ); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'security.authentication.provider.rememberme', + 'setPermissionResolver', + [new Reference(PermissionResolver::class)] + ); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'security.authentication.provider.guard', + 'setPermissionResolver', + [new Reference(PermissionResolver::class)] + ); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'security.authentication.provider.anonymous', + 'setPermissionResolver', + [new Reference(PermissionResolver::class)] + ); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'security.authentication.provider.anonymous', + 'setConfigResolver', + [new Reference('ibexa.config.resolver')] + ); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'security.http_utils', + 'setSiteAccess', + [new Reference(SiteAccess::class)] + ); + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'security.authentication.success_handler', + 'setConfigResolver', + [new Reference('ibexa.config.resolver')] + ); + } +} + +class_alias(SecurityPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\SecurityPassTest'); diff --git a/tests/bundle/Core/DependencyInjection/Compiler/SessionConfigurationPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/SessionConfigurationPassTest.php new file mode 100644 index 0000000000..8a84c6165c --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Compiler/SessionConfigurationPassTest.php @@ -0,0 +1,131 @@ +addCompilerPass(new SessionConfigurationPass()); + } + + public function testCompilesWithoutStorageDefinitions(): void + { + $this->assertContainerBuilderNotHasService('session.storage.native'); + $this->assertContainerBuilderNotHasService('session.storage.php_bridge'); + $this->assertContainerBuilderNotHasService('session.storage.factory.native'); + $this->assertContainerBuilderNotHasService('session.storage.factory.php_bridge'); + + $this->doCompile(); + } + + public function testCompileUsingStorageFactory(): void + { + $this->container->setDefinition( + 'session.storage.factory.native', + (new Definition())->setArguments([null, null, null]) + ); + $this->container->setDefinition( + 'session.storage.factory.php_bridge', + (new Definition())->setArguments([null, null]) + ); + + $this->doCompile(); + + $this->assertContainerBuilderHasServiceDefinitionWithArgument( + 'session.storage.factory.native', + 1, + new Reference('session.handler') + ); + $this->assertContainerBuilderHasServiceDefinitionWithArgument( + 'session.storage.factory.php_bridge', + 0, + new Reference('session.handler') + ); + } + + public function testCompileUsingStorage(): void + { + $this->container->setDefinition( + 'session.storage.native', + (new Definition())->setArguments([null, null, null]) + ); + $this->container->setDefinition( + 'session.storage.php_bridge', + (new Definition())->setArguments([null, null]) + ); + + $this->doCompile(); + + $this->assertContainerBuilderHasServiceDefinitionWithArgument( + 'session.storage.native', + 1, + new Reference('session.handler') + ); + $this->assertContainerBuilderHasServiceDefinitionWithArgument( + 'session.storage.php_bridge', + 0, + new Reference('session.handler') + ); + } + + private function doCompile(): void + { + $this->container->setParameter('ibexa.session.handler_id', 'my_handler'); + $this->container->setParameter('ibexa.session.save_path', 'my_save_path'); + + $this->compile(); + + $this->assertContainerBuilderHasAlias('session.handler', 'my_handler'); + $this->assertContainerBuilderHasParameter('session.save_path', 'my_save_path'); + } + + public function testCompileWithDsn(): void + { + $dsn = 'redis://instance.local:1234'; + + $definition = new Definition(AbstractSessionHandler::class); + $definition->setFactory([SessionHandlerFactory::class, 'createHandler']); + $definition->setArguments([$dsn]); + + $this->container->setDefinition('session.abstract_handler', $definition); + $this->container->setParameter('ibexa.session.handler_id', $dsn); + $this->container->setDefinition( + 'session.storage.native', + (new Definition())->setArguments([null, null, null]) + ); + $this->container->setDefinition( + 'session.storage.php_bridge', + (new Definition())->setArguments([null, null]) + ); + + $this->compile(); + + $this->assertContainerBuilderHasAlias('session.handler', 'session.abstract_handler'); + } + + public function testCompileWithNullValues(): void + { + $this->container->setParameter('ibexa.session.handler_id', null); + $this->container->setParameter('ibexa.session.save_path', null); + + $this->compile(); + + $this->assertContainerBuilderNotHasService('session.handler'); + self::assertNotTrue($this->container->hasParameter('session.save_path')); + } +} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/SiteAccessMatcherRegistryPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/SiteAccessMatcherRegistryPassTest.php similarity index 79% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/SiteAccessMatcherRegistryPassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/SiteAccessMatcherRegistryPassTest.php index db917388b7..eff2f58d4b 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/SiteAccessMatcherRegistryPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/SiteAccessMatcherRegistryPassTest.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\SiteAccessMatcherRegistryPass; -use eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessMatcherRegistry; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\SiteAccessMatcherRegistryPass; +use Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistry; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -48,3 +48,5 @@ public function testSetMatcher(): void ); } } + +class_alias(SiteAccessMatcherRegistryPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\SiteAccessMatcherRegistryPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/SlugConverterConfigurationPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPassTest.php similarity index 81% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/SlugConverterConfigurationPassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPassTest.php index 374f2c3717..06f0dbf9fa 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/SlugConverterConfigurationPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/SlugConverterConfigurationPassTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\SlugConverterConfigurationPass; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; -use eZ\Publish\Core\Persistence\TransformationProcessor; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\SlugConverterConfigurationPass; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; +use Ibexa\Core\Persistence\TransformationProcessor; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use ReflectionClass; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -43,9 +43,9 @@ public function testMergeConfigurations( $definition->setArgument(1, $existingOldParameters); $definition->setPublic(true); - $this->setDefinition('ezpublish.persistence.slug_converter', $definition); + $this->setDefinition(\Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter::class, $definition); - $this->setParameter('ezpublish.url_alias.slug_converter', [ + $this->setParameter('ibexa.url_alias.slug_converter', [ 'transformation' => 'urlalias', 'separator' => 'underscore', 'transformation_groups' => [ @@ -57,11 +57,11 @@ public function testMergeConfigurations( ]); $this->compile(); - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter $slugConverter */ + /** @var \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter $slugConverter */ $slugConverterRef = new ReflectionClass(SlugConverter::class); $configurationPropertyRef = $slugConverterRef->getProperty('configuration'); $configurationPropertyRef->setAccessible(true); - $configuration = $configurationPropertyRef->getValue($this->container->get('ezpublish.persistence.slug_converter')); + $configuration = $configurationPropertyRef->getValue($this->container->get(\Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter::class)); $this->assertEquals('urlalias', $configuration['transformation']); $this->assertEquals('underscore', $configuration['wordSeparatorName']); @@ -109,3 +109,5 @@ public function configurationProvider() ]; } } + +class_alias(SlugConverterConfigurationPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\SlugConverterConfigurationPassTest'); diff --git a/tests/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPassTest.php new file mode 100644 index 0000000000..0493675f98 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Compiler/TranslationCollectorPassTest.php @@ -0,0 +1,64 @@ +addCompilerPass(new TranslationCollectorPass()); + } + + /** + * @dataProvider translationCollectorProvider + */ + public function testTranslationCollector( + bool $translationsEnabled, + array $availableTranslations + ): void { + $this->setDefinition('translator.default', new Definition()); + $this->setParameter('kernel.project_dir', __DIR__ . $this->normalizePath('/../Fixtures')); + $this->setParameter('ibexa.ui.translations.enabled', $translationsEnabled); + + $this->compile(); + + $this->assertContainerBuilderHasParameter('available_translations', $availableTranslations); + } + + /** + * @param $path + * + * @return mixed + */ + private function normalizePath($path) + { + return str_replace('/', \DIRECTORY_SEPARATOR, $path); + } + + /** + * @return iterable + */ + public function translationCollectorProvider(): iterable + { + yield 'translations enabled' => [ + true, + ['en', 'hi', 'nb'], + ]; + + yield 'translations disabled' => [ + false, + ['en'], + ]; + } +} + +class_alias(TranslationCollectorPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\TranslationCollectorPassTest'); diff --git a/tests/bundle/Core/DependencyInjection/Compiler/URLHandlerPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/URLHandlerPassTest.php new file mode 100644 index 0000000000..bb0b3f3644 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Compiler/URLHandlerPassTest.php @@ -0,0 +1,66 @@ +setDefinition(URLHandlerRegistry::class, new Definition()); + } + + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new URLHandlerPass()); + } + + public function testRegisterURLHandler() + { + $serviceId = 'service_id'; + $scheme = 'http'; + $definition = new Definition(); + $definition->addTag('ibexa.url_checker.handler', ['scheme' => $scheme]); + $this->setDefinition($serviceId, $definition); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + URLHandlerRegistry::class, + 'addHandler', + [$scheme, new Reference($serviceId)] + ); + } + + public function testRegisterURLHandlerNoScheme() + { + $this->expectException(\LogicException::class); + + $serviceId = 'service_id'; + $scheme = 'http'; + $definition = new Definition(); + $definition->addTag('ibexa.url_checker.handler'); + $this->setDefinition($serviceId, $definition); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + URLHandlerRegistry::class, + 'addHandler', + [$scheme, new Reference($serviceId)] + ); + } +} + +class_alias(URLHandlerPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\URLHandlerPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ViewProvidersPassTest.php b/tests/bundle/Core/DependencyInjection/Compiler/ViewProvidersPassTest.php similarity index 80% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ViewProvidersPassTest.php rename to tests/bundle/Core/DependencyInjection/Compiler/ViewProvidersPassTest.php index 7b36f22fe4..dd58d5da36 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Compiler/ViewProvidersPassTest.php +++ b/tests/bundle/Core/DependencyInjection/Compiler/ViewProvidersPassTest.php @@ -4,9 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Compiler; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\ViewProvidersPass; +use Ibexa\Bundle\Core\DependencyInjection\Compiler\ViewProvidersPass; +use Ibexa\Core\MVC\Symfony\View\Provider\Registry; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -17,7 +18,7 @@ class ViewProvidersPassTest extends AbstractCompilerPassTestCase protected function setUp(): void { parent::setUp(); - $this->setDefinition('ezpublish.view_provider.registry', new Definition()); + $this->setDefinition(Registry::class, new Definition()); } /** @@ -42,13 +43,13 @@ public function testAddViewProvider($declaredPriority, $expectedPriority) if ($declaredPriority !== null) { $attributes['priority'] = $declaredPriority; } - $def->addTag('ezpublish.view_provider', $attributes); + $def->addTag('ibexa.view.provider', $attributes); $serviceId = 'service_id'; $this->setDefinition($serviceId, $def); $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.view_provider.registry', + Registry::class, 'setViewProviders', [ ['Test\View' => [new Reference($serviceId)]], @@ -72,3 +73,5 @@ public function addViewProviderProvider() ]; } } + +class_alias(ViewProvidersPassTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Compiler\ViewProvidersPassTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserTest.php b/tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserTest.php similarity index 82% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserTest.php rename to tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserTest.php index dcfba21388..52fcf604d5 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserTest.php +++ b/tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingParserTest.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\ComplexSettings; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Configuration\ComplexSettings; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParser; use PHPUnit\Framework\TestCase; class ComplexSettingParserTest extends TestCase { - /** @var \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParser */ + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParser */ private $parser; protected function setUp(): void @@ -78,3 +78,5 @@ public function provideSettings() ]; } } + +class_alias(ComplexSettingParserTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\ComplexSettings\ComplexSettingParserTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolverTest.php b/tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolverTest.php new file mode 100644 index 0000000000..6005e51f81 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/ComplexSettings/ComplexSettingValueResolverTest.php @@ -0,0 +1,30 @@ +resolveSetting( + '/mnt/nfs/$var_dir$/$storage_dir$', + 'var_dir', + 'var/ibexa_demo_site', + 'storage_dir', + 'storage' + ) + ); + } +} + +class_alias(ComplexSettingValueResolverTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\ComplexSettings\ComplexSettingValueResolverTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigParserTest.php b/tests/bundle/Core/DependencyInjection/Configuration/ConfigParserTest.php similarity index 87% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigParserTest.php rename to tests/bundle/Core/DependencyInjection/Configuration/ConfigParserTest.php index 385ced73e3..d1c0332a92 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/ConfigParserTest.php +++ b/tests/bundle/Core/DependencyInjection/Configuration/ConfigParserTest.php @@ -4,11 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Configuration; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigParser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ParserInterface; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ParserInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; use PHPUnit\Framework\TestCase; use stdClass; use Symfony\Component\Config\Definition\Builder\NodeBuilder; @@ -17,7 +18,7 @@ class ConfigParserTest extends TestCase { public function testConstructWrongInnerParser() { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\InvalidArgumentType::class); + $this->expectException(InvalidArgumentType::class); new ConfigParser( [ @@ -134,3 +135,5 @@ protected function getConfigurationParserMock() return $this->createMock(ParserInterface::class); } } + +class_alias(ConfigParserTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\ConfigParserTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php b/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php new file mode 100644 index 0000000000..64add1fe17 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ChainConfigResolverTest.php @@ -0,0 +1,342 @@ +siteAccess = new SiteAccess(self::FIRST_SA_NAME); + $this->siteAccess->groups = [new SiteAccessGroup(self::SA_GROUP)]; + $this->containerMock = $this->createMock(ContainerInterface::class); + } + + /** + * @dataProvider parameterProvider + */ + public function testGetParameterDefaultScope(string $paramName, $expectedValue): void + { + $globalScopeParameter = $this->getParameter($paramName, self::SCOPE_GLOBAL); + $relativeScopeParameter = $this->getParameter($paramName, $this->siteAccess->name); + $saGroupScopeParameter = $this->getParameter($paramName, self::SA_GROUP); + $defaultScopeParameter = $this->getParameter($paramName, self::SCOPE_DEFAULT); + $this->containerMock + ->expects($this->exactly(4)) + ->method('hasParameter') + ->with( + $this->logicalOr( + $globalScopeParameter, + $relativeScopeParameter, + $saGroupScopeParameter, + $defaultScopeParameter + ) + ) + // First call is for "global" scope, second for SA scope, third fo SA group scope, last is the right one + ->will($this->onConsecutiveCalls(false, false, false, true)); + $this->containerMock + ->expects($this->once()) + ->method('getParameter') + ->with($defaultScopeParameter) + ->willReturn($expectedValue); + + $this->assertSame($expectedValue, $this->getChainConfigResolver()->getParameter($paramName)); + } + + /** + * @dataProvider parameterProvider + */ + public function testGetParameterRelativeScope(string $paramName, $expectedValue): void + { + $globalScopeParameter = $this->getParameter($paramName, self::SCOPE_GLOBAL); + $relativeScopeParameter = $this->getParameter($paramName, $this->siteAccess->name); + $this->containerMock + ->expects($this->exactly(2)) + ->method('hasParameter') + ->with( + $this->logicalOr( + $globalScopeParameter, + $relativeScopeParameter + ) + ) + // First call is for "global" scope, second is the right one + ->will($this->onConsecutiveCalls(false, true)); + $this->containerMock + ->expects($this->once()) + ->method('getParameter') + ->with($relativeScopeParameter) + ->willReturn($expectedValue); + + $this->assertSame($expectedValue, $this->getChainConfigResolver()->getParameter($paramName)); + } + + /** + * @dataProvider parameterProvider + */ + public function testGetParameterSpecificScope(string $paramName, $expectedValue): void + { + $specificScopeParameter = $this->getParameter($paramName, self::FIRST_SA_NAME); + $this->containerMock + ->expects($this->exactly(2)) + ->method('hasParameter') + ->with( + $this->logicalOr( + "ibexa.site_access.config.global.$paramName", + $specificScopeParameter + ) + ) + // First call is for "global" scope, second is the right one + ->will($this->onConsecutiveCalls(false, true)); + $this->containerMock + ->expects($this->once()) + ->method('getParameter') + ->with($specificScopeParameter) + ->willReturn($expectedValue); + + $this->assertSame( + $expectedValue, + $this->getChainConfigResolver()->getParameter($paramName, self::DEFAULT_NAMESPACE, self::FIRST_SA_NAME) + ); + } + + /** + * @dataProvider parameterProvider + */ + public function testGetParameterGlobalScope(string $paramName, $expectedValue): void + { + $globalScopeParameter = $this->getParameter($paramName, self::SCOPE_GLOBAL); + $this->containerMock + ->expects($this->once()) + ->method('hasParameter') + ->with($globalScopeParameter) + ->willReturn(true); + $this->containerMock + ->expects($this->once()) + ->method('getParameter') + ->with($globalScopeParameter) + ->willReturn($expectedValue); + + $this->assertSame($expectedValue, $this->getChainConfigResolver()->getParameter($paramName)); + } + + /** + * @dataProvider hasParameterProvider + */ + public function testHasParameterNoNamespace( + bool $defaultMatch, + bool $groupMatch, + bool $scopeMatch, + bool $globalMatch, + bool $expectedResult + ): void { + $paramName = 'foo.bar'; + $groupName = self::SA_GROUP; + + $chainConfigResolver = $this->getChainConfigResolver(); + + $this->containerMock->expects($this->atLeastOnce()) + ->method('hasParameter') + ->willReturnMap( + [ + ["ibexa.site_access.config.default.$paramName", $defaultMatch], + ["ibexa.site_access.config.$groupName.$paramName", $groupMatch], + ["ibexa.site_access.config.{$this->siteAccess->name}.$paramName", $scopeMatch], + ["ibexa.site_access.config.global.$paramName", $globalMatch], + ] + ); + + $this->assertSame($expectedResult, $chainConfigResolver->hasParameter($paramName)); + } + + /** + * @dataProvider hasParameterProvider + */ + public function testHasParameterWithNamespaceAndScope( + bool $defaultMatch, + bool $groupMatch, + bool $scopeMatch, + bool $globalMatch, + bool $expectedResult + ): void { + $paramName = 'foo.bar'; + $namespace = 'my.namespace'; + $scope = self::SECOND_SA_NAME; + $groupName = self::SA_GROUP; + + $chainConfigResolver = $this->getChainConfigResolver(); + + $this->containerMock->expects($this->atLeastOnce()) + ->method('hasParameter') + ->willReturnMap( + [ + ["$namespace.default.$paramName", $defaultMatch], + ["$namespace.$groupName.$paramName", $groupMatch], + ["$namespace.$scope.$paramName", $scopeMatch], + ["$namespace.global.$paramName", $globalMatch], + ] + ); + + $this->assertSame($expectedResult, $chainConfigResolver->hasParameter($paramName, $namespace, $scope)); + } + + private function getGlobalConfigResolver(string $defaultNamespace = self::DEFAULT_NAMESPACE): ConfigResolverInterface + { + $configResolver = new GlobalScopeConfigResolver( + $defaultNamespace + ); + $configResolver->setContainer($this->containerMock); + + return $configResolver; + } + + private function getDefaultConfigResolver(string $defaultNamespace = self::DEFAULT_NAMESPACE): ConfigResolverInterface + { + $configResolver = new DefaultScopeConfigResolver( + $defaultNamespace + ); + $configResolver->setContainer($this->containerMock); + + return $configResolver; + } + + protected function getSiteAccessGroupConfigResolver(string $defaultNamespace = self::DEFAULT_NAMESPACE): ConfigResolverInterface + { + $siteAccess = new SiteAccess( + self::FIRST_SA_NAME, + ); + $configResolver = new SiteAccessGroupConfigResolver( + $this->getStaticSiteAccessProvider(), + $defaultNamespace, + [] + ); + $configResolver->setContainer($this->containerMock); + $configResolver->setSiteAccess($siteAccess); + + return $configResolver; + } + + protected function getSiteAccessConfigResolver(string $defaultNamespace = self::DEFAULT_NAMESPACE): ConfigResolverInterface + { + $siteAccess = new SiteAccess( + self::FIRST_SA_NAME, + ); + $configResolver = new StaticSiteAccessConfigResolver( + $this->getStaticSiteAccessProvider(), + $defaultNamespace + ); + $configResolver->setContainer($this->containerMock); + $configResolver->setSiteAccess($siteAccess); + + return $configResolver; + } + + private function getStaticSiteAccessProvider(): StaticSiteAccessProvider + { + return new StaticSiteAccessProvider( + [ + self::FIRST_SA_NAME, + self::SECOND_SA_NAME, + ], + [ + self::FIRST_SA_NAME => [self::SA_GROUP], + self::SECOND_SA_NAME => [self::SA_GROUP], + ], + ); + } + + public function parameterProvider(): array + { + return [ + ['foo', 'bar'], + ['some.parameter', true], + ['some.other.parameter', ['foo', 'bar', 'baz']], + ['a.hash.parameter', ['foo' => 'bar', 'tata' => 'toto']], + [ + 'a.deep.hash', [ + 'foo' => 'bar', + 'tata' => 'toto', + 'deeper_hash' => [ + 'likeStarWars' => true, + 'jedi' => ['Obi-Wan Kenobi', 'Mace Windu', 'Luke Skywalker', 'Leïa Skywalker (yes! Read episodes 7-8-9!)'], + 'sith' => ['Darth Vader', 'Darth Maul', 'Palpatine'], + 'roles' => [ + 'Amidala' => ['Queen'], + 'Palpatine' => ['Senator', 'Emperor', 'Villain'], + 'C3PO' => ['Droid', 'Annoying guy'], + 'Jar-Jar' => ['Still wondering his role', 'Annoying guy'], + ], + ], + ], + ], + ]; + } + + public function hasParameterProvider(): array + { + return [ + [true, true, true, true, true], + [true, true, true, false, true], + [true, true, false, false, true], + [false, false, false, false, false], + [false, false, true, false, true], + [false, false, false, true, true], + [false, false, true, true, true], + [false, true, false, false, true], + ]; + } + + private function getChainConfigResolver(): ChainConfigResolver + { + $chainConfigResolver = new ChainConfigResolver(); + $chainConfigResolver->addResolver($this->getDefaultConfigResolver(), 0); + $chainConfigResolver->addResolver($this->getSiteAccessGroupConfigResolver(), 50); + $chainConfigResolver->addResolver($this->getSiteAccessConfigResolver(), 100); + $chainConfigResolver->addResolver($this->getGlobalConfigResolver(), 255); + + return $chainConfigResolver; + } + + private function getParameter( + string $paramName, + string $scope, + string $namespace = self::DEFAULT_NAMESPACE + ): string { + return sprintf('%s.%s.%s', $namespace, $scope, $paramName); + } +} + +class_alias(ChainConfigResolverTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\ConfigResolver\ChainConfigResolverTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ConfigResolverTest.php b/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ConfigResolverTest.php new file mode 100644 index 0000000000..a034848891 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/ConfigResolverTest.php @@ -0,0 +1,118 @@ +siteAccess = new SiteAccess('test'); + $this->containerMock = $this->createMock(ContainerInterface::class); + } + + abstract protected function getResolver(string $defaultNamespace = 'ibexa.site_access.config'): ConfigResolverInterface; + + abstract protected function getScope(): string; + + protected function getNamespace(): string + { + return self::DEFAULT_NAMESPACE; + } + + public function testGetParameterFailedWithException(): void + { + $resolver = $this->getResolver(self::DEFAULT_NAMESPACE); + $this->containerMock + ->expects($this->once()) + ->method('hasParameter') + ->with(sprintf('%s.%s.undefined', $this->getNamespace(), $this->getScope())) + ->willReturn(false); + + $this->expectException(ParameterNotFoundException::class); + + $resolver->getParameter('undefined'); + } + + /** + * @dataProvider parameterProvider + */ + public function testGetParameterGlobalScope(string $paramName, $expectedValue): void + { + $globalScopeParameter = sprintf('%s.%s.%s', $this->getNamespace(), $this->getScope(), $paramName); + $this->containerMock + ->expects($this->once()) + ->method('hasParameter') + ->with($globalScopeParameter) + ->willReturn(true); + $this->containerMock + ->expects($this->once()) + ->method('getParameter') + ->with($globalScopeParameter) + ->willReturn($expectedValue); + + $this->assertSame($expectedValue, $this->getResolver()->getParameter($paramName)); + } + + public function parameterProvider(): array + { + return [ + ['foo', 'bar'], + ['some.parameter', true], + ['some.other.parameter', ['foo', 'bar', 'baz']], + ['a.hash.parameter', ['foo' => 'bar', 'tata' => 'toto']], + [ + 'a.deep.hash', [ + 'foo' => 'bar', + 'tata' => 'toto', + 'deeper_hash' => [ + 'likeStarWars' => true, + 'jedi' => ['Obi-Wan Kenobi', 'Mace Windu', 'Luke Skywalker', 'Leïa Skywalker (yes! Read episodes 7-8-9!)'], + 'sith' => ['Darth Vader', 'Darth Maul', 'Palpatine'], + 'roles' => [ + 'Amidala' => ['Queen'], + 'Palpatine' => ['Senator', 'Emperor', 'Villain'], + 'C3PO' => ['Droid', 'Annoying guy'], + 'Jar-Jar' => ['Still wondering his role', 'Annoying guy'], + ], + ], + ], + ], + ]; + } + + public function testGetSetDefaultNamespace(): void + { + $newDefaultNamespace = 'new'; + $configResolver = $this->getResolver(); + $this->assertSame(self::DEFAULT_NAMESPACE, $configResolver->getDefaultNamespace()); + $configResolver->setDefaultNamespace($newDefaultNamespace); + $this->assertSame($newDefaultNamespace, $configResolver->getDefaultNamespace()); + } +} + +class_alias(ConfigResolverTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\ConfigResolver\ConfigResolverTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/DefaultScopeConfigResolverTest.php b/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/DefaultScopeConfigResolverTest.php new file mode 100644 index 0000000000..711ca8390c --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/DefaultScopeConfigResolverTest.php @@ -0,0 +1,32 @@ +setContainer($this->containerMock); + + return $configResolver; + } + + protected function getScope(): string + { + return 'default'; + } +} + +class_alias(DefaultScopeConfigResolverTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\ConfigResolver\DefaultScopeConfigResolverTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/GlobalScopeConfigResolverTest.php b/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/GlobalScopeConfigResolverTest.php new file mode 100644 index 0000000000..59433b5036 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/GlobalScopeConfigResolverTest.php @@ -0,0 +1,32 @@ +setContainer($this->containerMock); + + return $configResolver; + } + + protected function getScope(): string + { + return 'global'; + } +} + +class_alias(GlobalScopeConfigResolverTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\ConfigResolver\GlobalScopeConfigResolverTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolverTest.php b/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolverTest.php new file mode 100644 index 0000000000..0d0618d288 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/SiteAccessGroupConfigResolverTest.php @@ -0,0 +1,47 @@ + [self::SA_GROUP]], + ); + $siteAccess = new SiteAccess( + self::EXISTING_SA_NAME, + 'default', + $this->createMock(Matcher::class) + ); + $configResolver = new SiteAccessGroupConfigResolver( + $staticSiteAccessProvider, + $defaultNamespace, + [] + ); + $configResolver->setContainer($this->containerMock); + $configResolver->setSiteAccess($siteAccess); + + return $configResolver; + } + + protected function getScope(): string + { + return self::SA_GROUP; + } +} + +class_alias(SiteAccessGroupConfigResolverTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\ConfigResolver\SiteAccessGroupConfigResolverTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolverTest.php b/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolverTest.php new file mode 100644 index 0000000000..4dc96b72a8 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/ConfigResolver/StaticSiteAccessConfigResolverTest.php @@ -0,0 +1,46 @@ + [self::SA_GROUP]], + ); + $siteAccess = new SiteAccess( + self::EXISTING_SA_NAME, + 'default', + $this->createMock(Matcher::class) + ); + $configResolver = new StaticSiteAccessConfigResolver( + $staticSiteAccessProvider, + $defaultNamespace + ); + $configResolver->setContainer($this->containerMock); + $configResolver->setSiteAccess($siteAccess); + + return $configResolver; + } + + protected function getScope(): string + { + return self::EXISTING_SA_NAME; + } +} + +class_alias(StaticSiteAccessConfigResolverTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\ConfigResolver\StaticSiteAccessConfigResolverTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/Parser/AbstractParserTestCase.php b/tests/bundle/Core/DependencyInjection/Configuration/Parser/AbstractParserTestCase.php new file mode 100644 index 0000000000..c08bd7a501 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/Parser/AbstractParserTestCase.php @@ -0,0 +1,114 @@ +container, + new FileLocator(__DIR__ . '/../../Fixtures') + ); + + $loader->load('parameters.yml'); + } + + /** + * Asserts a parameter from ConfigResolver has expected value for given scope. + * + * @param string $parameterName + * @param mixed $expectedValue + * @param string $scope SiteAccess name, group, default or global + * @param bool $assertSame Set to false if you want to use assertEquals() instead of assertSame() + */ + protected function assertConfigResolverParameterValue($parameterName, $expectedValue, $scope, $assertSame = true) + { + $chainConfigResolver = $this->getConfigResolver(); + $assertMethod = $assertSame ? 'assertSame' : 'assertEquals'; + $this->$assertMethod($expectedValue, $chainConfigResolver->getParameter($parameterName, 'ibexa.site_access.config', $scope)); + } + + protected function getConfigResolver(): ConfigResolverInterface + { + $chainConfigResolver = new ChainConfigResolver(); + $siteAccessProvider = $this->getSiteAccessProviderMock(); + + $configResolvers = [ + new DefaultScopeConfigResolver('default'), + new SiteAccessGroupConfigResolver($siteAccessProvider, 'default', [self::EMPTY_SA_GROUP => []]), + new StaticSiteAccessConfigResolver($siteAccessProvider, 'default'), + new GlobalScopeConfigResolver('default'), + ]; + + foreach ($configResolvers as $priority => $configResolver) { + $configResolver->setContainer($this->container); + $chainConfigResolver->addResolver($configResolver, $priority); + } + + return $chainConfigResolver; + } + + protected function getSiteAccessProviderMock(): SiteAccessProviderInterface + { + $siteAccessProvider = $this->createMock(SiteAccessProviderInterface::class); + $siteAccessProvider + ->method('isDefined') + ->willReturnMap([ + ['ibexa_demo_site', true], + ['fre', true], + ['fre2', true], + ['ibexa_demo_site_admin', true], + ['empty_group', false], + ]); + $siteAccessProvider + ->method('getSiteAccess') + ->willReturnMap([ + ['ibexa_demo_site', $this->getSiteAccess('ibexa_demo_site', StaticSiteAccessProvider::class, ['ibexa_demo_group', 'ibexa_demo_frontend_group'])], + ['fre', $this->getSiteAccess('fre', StaticSiteAccessProvider::class, ['ibexa_demo_group', 'ibexa_demo_frontend_group'])], + ['fre2', $this->getSiteAccess('fre', StaticSiteAccessProvider::class, ['ibexa_demo_group', 'ibexa_demo_frontend_group'])], + ['ibexa_demo_site_admin', $this->getSiteAccess('ibexa_demo_site_admin', StaticSiteAccessProvider::class, ['ibexa_demo_group'])], + ]); + + return $siteAccessProvider; + } + + /** + * @param string[] $groupNames + */ + protected function getSiteAccess(string $name, string $provider, array $groupNames): SiteAccess + { + $siteAccess = new SiteAccess($name, SiteAccess::DEFAULT_MATCHING_TYPE, null, $provider); + $siteAccessGroups = []; + foreach ($groupNames as $groupName) { + $siteAccessGroups[] = new SiteAccessGroup($groupName); + } + $siteAccess->groups = $siteAccessGroups; + + return $siteAccess; + } +} + +class_alias(AbstractParserTestCase::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\Parser\AbstractParserTestCase'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/CommonTest.php b/tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php similarity index 80% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/CommonTest.php rename to tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php index 1cb67369cf..3831514945 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/Parser/CommonTest.php +++ b/tests/bundle/Core/DependencyInjection/Configuration/Parser/CommonTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\Parser; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Configuration\Parser; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Parser\Common; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\EzPublishCoreExtension; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Parser\Common; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Collector\SuggestionCollectorInterface; +use Ibexa\Bundle\Core\DependencyInjection\IbexaCoreExtension; use Symfony\Component\Yaml\Yaml; class CommonTest extends AbstractParserTestCase @@ -22,7 +22,7 @@ protected function getContainerExtensions(): array { $this->suggestionCollector = $this->createMock(SuggestionCollectorInterface::class); - return [new EzPublishCoreExtension([new Common()])]; + return [new IbexaCoreExtension([new Common()])]; } protected function getMinimalConfiguration(): array @@ -36,14 +36,14 @@ public function testIndexPage() $indexPage2 = '/Contact-Us'; $config = [ 'system' => [ - 'ezdemo_site' => ['index_page' => $indexPage1], - 'ezdemo_site_admin' => ['index_page' => $indexPage2], + 'ibexa_demo_site' => ['index_page' => $indexPage1], + 'ibexa_demo_site_admin' => ['index_page' => $indexPage2], ], ]; $this->load($config); - $this->assertConfigResolverParameterValue('index_page', $indexPage1, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('index_page', $indexPage2, 'ezdemo_site_admin'); + $this->assertConfigResolverParameterValue('index_page', $indexPage1, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('index_page', $indexPage2, 'ibexa_demo_site_admin'); $this->assertConfigResolverParameterValue('index_page', null, self::EMPTY_SA_GROUP); } @@ -53,14 +53,14 @@ public function testDefaultPage() $defaultPage2 = '/Foo/bar'; $config = [ 'system' => [ - 'ezdemo_site' => ['default_page' => $defaultPage1], - 'ezdemo_site_admin' => ['default_page' => $defaultPage2], + 'ibexa_demo_site' => ['default_page' => $defaultPage1], + 'ibexa_demo_site_admin' => ['default_page' => $defaultPage2], ], ]; $this->load($config); - $this->assertConfigResolverParameterValue('default_page', $defaultPage1, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('default_page', $defaultPage2, 'ezdemo_site_admin'); + $this->assertConfigResolverParameterValue('default_page', $defaultPage1, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('default_page', $defaultPage2, 'ibexa_demo_site_admin'); $this->assertConfigResolverParameterValue('index_page', null, self::EMPTY_SA_GROUP); } @@ -71,7 +71,7 @@ public function testDatabaseSingleSiteaccess() $this->load( [ 'system' => [ - 'ezdemo_site' => [ + 'ibexa_demo_site' => [ 'database' => [ 'type' => 'sqlite', 'server' => 'localhost', @@ -92,7 +92,7 @@ public function testDatabaseSiteaccessGroup() $this->load( [ 'system' => [ - 'ezdemo_group' => [ + 'ibexa_demo_group' => [ 'database' => [ 'type' => 'sqlite', 'server' => 'localhost', @@ -112,15 +112,15 @@ public function testDatabaseSiteaccessGroup() public function testNonExistentSettings() { $this->load(); - $this->assertConfigResolverParameterValue('url_alias_router', true, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('cache_service_name', 'cache.app', 'ezdemo_site'); - $this->assertConfigResolverParameterValue('var_dir', 'var', 'ezdemo_site'); - $this->assertConfigResolverParameterValue('storage_dir', 'storage', 'ezdemo_site'); - $this->assertConfigResolverParameterValue('binary_dir', 'original', 'ezdemo_site'); - $this->assertConfigResolverParameterValue('session_name', '%ezpublish.session_name.default%', 'ezdemo_site'); - $this->assertConfigResolverParameterValue('http_cache.purge_servers', [], 'ezdemo_site'); - $this->assertConfigResolverParameterValue('anonymous_user_id', 10, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('index_page', null, 'ezdemo_site'); + $this->assertConfigResolverParameterValue('url_alias_router', true, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('cache_service_name', 'cache.app', 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('var_dir', 'var', 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('storage_dir', 'storage', 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('binary_dir', 'original', 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('session_name', '%ibexa.session_name.default%', 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('http_cache.purge_servers', [], 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('anonymous_user_id', 10, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('index_page', null, 'ibexa_demo_site'); } public function testMiscSettings() @@ -140,7 +140,7 @@ public function testMiscSettings() $this->load( [ 'system' => [ - 'ezdemo_site' => [ + 'ibexa_demo_site' => [ 'cache_service_name' => $cachePoolName, 'var_dir' => $varDir, 'storage_dir' => $storageDir, @@ -156,14 +156,14 @@ public function testMiscSettings() ] ); - $this->assertConfigResolverParameterValue('cache_service_name', $cachePoolName, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('var_dir', $varDir, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('storage_dir', $storageDir, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('binary_dir', $binaryDir, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('session_name', $sessionName, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('index_page', $indexPage, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('http_cache.purge_servers', $cachePurgeServers, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('anonymous_user_id', $anonymousUserId, 'ezdemo_site'); + $this->assertConfigResolverParameterValue('cache_service_name', $cachePoolName, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('var_dir', $varDir, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('storage_dir', $storageDir, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('binary_dir', $binaryDir, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('session_name', $sessionName, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('index_page', $indexPage, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('http_cache.purge_servers', $cachePurgeServers, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('anonymous_user_id', $anonymousUserId, 'ibexa_demo_site'); } public function testApiKeysSettings() @@ -172,7 +172,7 @@ public function testApiKeysSettings() $this->load( [ 'system' => [ - 'ezdemo_group' => [ + 'ibexa_demo_group' => [ 'api_keys' => [ 'google_maps' => $key, ], @@ -181,8 +181,8 @@ public function testApiKeysSettings() ] ); - $this->assertConfigResolverParameterValue('api_keys', ['google_maps' => $key], 'ezdemo_site'); - $this->assertConfigResolverParameterValue('api_keys.google_maps', $key, 'ezdemo_site'); + $this->assertConfigResolverParameterValue('api_keys', ['google_maps' => $key], 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('api_keys.google_maps', $key, 'ibexa_demo_site'); } public function testUserSettings() @@ -192,7 +192,7 @@ public function testUserSettings() $this->load( [ 'system' => [ - 'ezdemo_site' => [ + 'ibexa_demo_site' => [ 'user' => [ 'layout' => $layout, 'login_template' => $loginTemplate, @@ -202,8 +202,8 @@ public function testUserSettings() ] ); - $this->assertConfigResolverParameterValue('security.base_layout', $layout, 'ezdemo_site'); - $this->assertConfigResolverParameterValue('security.login_template', $loginTemplate, 'ezdemo_site'); + $this->assertConfigResolverParameterValue('security.base_layout', $layout, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('security.login_template', $loginTemplate, 'ibexa_demo_site'); } public function testNoUserSettings() @@ -211,13 +211,13 @@ public function testNoUserSettings() $this->load(); $this->assertConfigResolverParameterValue( 'security.base_layout', - '%ezsettings.default.page_layout%', - 'ezdemo_site' + '%ibexa.site_access.config.default.page_layout%', + 'ibexa_demo_site' ); $this->assertConfigResolverParameterValue( 'security.login_template', - '@EzPublishCore/Security/login.html.twig', - 'ezdemo_site' + '@IbexaCore/Security/login.html.twig', + 'ibexa_demo_site' ); } @@ -229,13 +229,13 @@ public function testSessionSettings(array $inputParams, array $expected) $this->load( [ 'system' => [ - 'ezdemo_site' => $inputParams, + 'ibexa_demo_site' => $inputParams, ], ] ); - $this->assertConfigResolverParameterValue('session', $expected['session'], 'ezdemo_site'); - $this->assertConfigResolverParameterValue('session_name', $expected['session_name'], 'ezdemo_site'); + $this->assertConfigResolverParameterValue('session', $expected['session'], 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('session_name', $expected['session_name'], 'ibexa_demo_site'); } public function sessionSettingsProvider() @@ -302,3 +302,5 @@ public function sessionSettingsProvider() ]; } } + +class_alias(CommonTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\Parser\CommonTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/Parser/ContentTest.php b/tests/bundle/Core/DependencyInjection/Configuration/Parser/ContentTest.php new file mode 100644 index 0000000000..285a0cc2b3 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/Parser/ContentTest.php @@ -0,0 +1,131 @@ +load(); + + $this->assertConfigResolverParameterValue('content.view_cache', true, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('content.ttl_cache', true, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('content.default_ttl', 60, 'ibexa_demo_site'); + } + + /** + * @dataProvider contentSettingsProvider + */ + public function testContentSettings(array $config, array $expected) + { + $this->load( + [ + 'system' => [ + 'ibexa_demo_site' => $config, + ], + ] + ); + + foreach ($expected as $key => $val) { + $this->assertConfigResolverParameterValue($key, $val, 'ibexa_demo_site'); + } + } + + public function contentSettingsProvider() + { + return [ + [ + [ + 'content' => [ + 'view_cache' => true, + 'ttl_cache' => true, + 'default_ttl' => 100, + ], + ], + [ + 'content.view_cache' => true, + 'content.ttl_cache' => true, + 'content.default_ttl' => 100, + ], + ], + [ + [ + 'content' => [ + 'view_cache' => false, + 'ttl_cache' => false, + 'default_ttl' => 123, + ], + ], + [ + 'content.view_cache' => false, + 'content.ttl_cache' => false, + 'content.default_ttl' => 123, + ], + ], + [ + [ + 'content' => [ + 'view_cache' => false, + ], + ], + [ + 'content.view_cache' => false, + 'content.ttl_cache' => true, + 'content.default_ttl' => 60, + ], + ], + [ + [ + 'content' => [ + 'tree_root' => ['location_id' => 123], + ], + ], + [ + 'content.view_cache' => true, + 'content.ttl_cache' => true, + 'content.default_ttl' => 60, + 'content.tree_root.location_id' => 123, + ], + ], + [ + [ + 'content' => [ + 'tree_root' => [ + 'location_id' => 456, + 'excluded_uri_prefixes' => ['/media/images', '/products'], + ], + ], + ], + [ + 'content.view_cache' => true, + 'content.ttl_cache' => true, + 'content.default_ttl' => 60, + 'content.tree_root.location_id' => 456, + 'content.tree_root.excluded_uri_prefixes' => ['/media/images', '/products'], + ], + ], + ]; + } +} + +class_alias(ContentTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\Parser\ContentTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/Parser/FieldType/ImageAssetTest.php b/tests/bundle/Core/DependencyInjection/Configuration/Parser/FieldType/ImageAssetTest.php new file mode 100644 index 0000000000..8707c18001 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/Parser/FieldType/ImageAssetTest.php @@ -0,0 +1,88 @@ +load(); + + $this->assertConfigResolverParameterValue( + 'fieldtypes.ezimageasset.mappings', + [ + 'content_type_identifier' => 'image', + 'content_field_identifier' => 'image', + 'name_field_identifier' => 'name', + 'parent_location_id' => 51, + ], + 'ibexa_demo_site' + ); + } + + /** + * @dataProvider imageAssetSettingsProvider + */ + public function testImageAssetSettings(array $config, array $expected) + { + $this->load( + [ + 'system' => [ + 'ibexa_demo_site' => $config, + ], + ] + ); + + foreach ($expected as $key => $val) { + $this->assertConfigResolverParameterValue($key, $val, 'ibexa_demo_site'); + } + } + + public function imageAssetSettingsProvider(): array + { + return [ + [ + [ + 'fieldtypes' => [ + 'ezimageasset' => [ + 'content_type_identifier' => 'photo', + 'content_field_identifier' => 'file', + 'name_field_identifier' => 'title', + 'parent_location_id' => 68, + ], + ], + ], + [ + 'fieldtypes.ezimageasset.mappings' => [ + 'content_type_identifier' => 'photo', + 'content_field_identifier' => 'file', + 'name_field_identifier' => 'title', + 'parent_location_id' => 68, + ], + ], + ], + ]; + } +} + +class_alias(ImageAssetTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\Parser\FieldType\ImageAssetTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/Parser/IOTest.php b/tests/bundle/Core/DependencyInjection/Configuration/Parser/IOTest.php new file mode 100644 index 0000000000..bcec996124 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/Parser/IOTest.php @@ -0,0 +1,66 @@ +container->setParameter('ibexa.site_access.config.default.var_dir', 'var'); // PS: Does not seem to take effect + $this->container->setParameter('ibexa.site_access.config.default.storage_dir', 'storage'); + $this->container->setParameter('ibexa.site_access.config.ibexa_demo_site.var_dir', 'var/ibexa_demo_site'); + } + + protected function getContainerExtensions(): array + { + return [ + new IbexaCoreExtension([new IO(new ComplexSettingParser())]), + ]; + } + + protected function getMinimalConfiguration(): array + { + return $this->minimalConfig = Yaml::parse(file_get_contents(__DIR__ . '/../../Fixtures/ezpublish_minimal.yml')); + } + + public function testHandlersConfig() + { + $config = [ + 'system' => [ + 'ibexa_demo_site' => [ + 'io' => [ + 'binarydata_handler' => 'cluster', + 'metadata_handler' => 'cluster', + ], + ], + self::EMPTY_SA_GROUP => [ + 'io' => [ + 'binarydata_handler' => 'group_cluster', + 'metadata_handler' => 'group_cluster', + ], + ], + ], + ]; + + $this->load($config); + + $this->assertConfigResolverParameterValue('io.metadata_handler', 'cluster', 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('io.binarydata_handler', 'cluster', 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('io.metadata_handler', 'group_cluster', self::EMPTY_SA_GROUP); + $this->assertConfigResolverParameterValue('io.binarydata_handler', 'group_cluster', self::EMPTY_SA_GROUP); + } +} + +class_alias(IOTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\Parser\IOTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/Parser/ImageTest.php b/tests/bundle/Core/DependencyInjection/Configuration/Parser/ImageTest.php new file mode 100644 index 0000000000..360af7a6a1 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/Parser/ImageTest.php @@ -0,0 +1,93 @@ +markTestSkipped('Missing or mis-configured Imagemagick convert path.'); + } + } + + protected function getMinimalConfiguration(): array + { + $this->config = Yaml::parse(file_get_contents(__DIR__ . '/../../Fixtures/ezpublish_image.yml')); + $this->config += [ + 'imagemagick' => [ + 'enabled' => true, + 'path' => $_ENV['imagemagickConvertPath'], + ], + ]; + + return $this->config; + } + + protected function getContainerExtensions(): array + { + return [ + new IbexaCoreExtension([new Image()]), + ]; + } + + public function testVariations() + { + $this->load(); + + $expectedParsedVariations = []; + foreach ($this->config['system'] as $sa => $saConfig) { + $expectedParsedVariations[$sa] = []; + foreach ($saConfig['image_variations'] as $variationName => $imageVariationConfig) { + $imageVariationConfig['post_processors'] = []; + foreach ($imageVariationConfig['filters'] as $i => $filter) { + $imageVariationConfig['filters'][$filter['name']] = $filter['params']; + unset($imageVariationConfig['filters'][$i]); + } + $expectedParsedVariations[$sa][$variationName] = $imageVariationConfig; + } + } + + $expected = $expectedParsedVariations['ibexa_demo_group'] + $this->container->getParameter('ibexa.site_access.config.default.image_variations'); + $this->assertConfigResolverParameterValue('image_variations', $expected, 'ibexa_demo_site', false); + $this->assertConfigResolverParameterValue('image_variations', $expected, 'ibexa_demo_site_admin', false); + $this->assertConfigResolverParameterValue( + 'image_variations', + $expected + $expectedParsedVariations['fre'], + 'fre', + false + ); + } + + public function testPrePostParameters() + { + $this->expectException(\InvalidArgumentException::class); + + $this->load( + [ + 'system' => [ + 'ibexa_demo_site' => [ + 'imagemagick' => [ + 'pre_parameters' => '-foo -bar', + 'post_parameters' => '-baz', + ], + ], + ], + ] + ); + } +} + +class_alias(ImageTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\Parser\ImageTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/Parser/LanguagesTest.php b/tests/bundle/Core/DependencyInjection/Configuration/Parser/LanguagesTest.php new file mode 100644 index 0000000000..e7bb6a8aad --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/Parser/LanguagesTest.php @@ -0,0 +1,122 @@ +minimalConfig = Yaml::parse(file_get_contents(__DIR__ . '/../../Fixtures/ezpublish_minimal.yml')); + } + + public function testLanguagesSingleSiteaccess() + { + $langDemoSite = ['eng-GB']; + $langFre = ['fre-FR', 'eng-GB']; + $langEmptyGroup = ['pol-PL']; + $config = [ + 'siteaccess' => [ + 'list' => ['fre2'], + 'groups' => [self::EMPTY_SA_GROUP => []], + ], + 'system' => [ + 'ibexa_demo_site' => ['languages' => $langDemoSite], + 'fre' => ['languages' => $langFre], + 'fre2' => ['languages' => $langFre], + self::EMPTY_SA_GROUP => ['languages' => $langEmptyGroup], + ], + ]; + $this->load($config); + + $this->assertConfigResolverParameterValue('languages', $langDemoSite, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('languages', $langFre, 'fre'); + $this->assertConfigResolverParameterValue('languages', $langFre, 'fre2'); + $this->assertConfigResolverParameterValue('languages', $langEmptyGroup, self::EMPTY_SA_GROUP); + $this->assertSame( + [ + 'eng-GB' => ['ibexa_demo_site'], + 'fre-FR' => ['fre', 'fre2'], + 'pol-PL' => [self::EMPTY_SA_GROUP], + ], + $this->container->getParameter('ibexa.site_access.by_language') + ); + // languages for ibexa_demo_site_admin will take default value (empty array) + $this->assertConfigResolverParameterValue('languages', [], 'ibexa_demo_site_admin'); + } + + public function testLanguagesSiteaccessGroup() + { + $langDemoSite = ['eng-US', 'eng-GB']; + $config = [ + 'system' => [ + 'ibexa_demo_frontend_group' => ['languages' => $langDemoSite], + 'ibexa_demo_site' => [], + 'fre' => [], + ], + ]; + $this->load($config); + + $this->assertConfigResolverParameterValue('languages', $langDemoSite, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('languages', $langDemoSite, 'fre'); + $this->assertConfigResolverParameterValue('languages', [], self::EMPTY_SA_GROUP); + $this->assertSame( + [ + 'eng-US' => ['ibexa_demo_frontend_group', 'ibexa_demo_site', 'fre'], + ], + $this->container->getParameter('ibexa.site_access.by_language') + ); + // languages for ibexa_demo_site_admin will take default value (empty array) + $this->assertConfigResolverParameterValue('languages', [], 'ibexa_demo_site_admin'); + } + + public function testTranslationSiteAccesses() + { + $translationSAsDemoSite = ['foo', 'bar']; + $translationSAsFre = ['foo2', 'bar2']; + $config = [ + 'system' => [ + 'ibexa_demo_site' => ['translation_siteaccesses' => $translationSAsDemoSite], + 'fre' => ['translation_siteaccesses' => $translationSAsFre], + ], + ]; + $this->load($config); + + $this->assertConfigResolverParameterValue('translation_siteaccesses', $translationSAsDemoSite, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('translation_siteaccesses', $translationSAsFre, 'fre'); + $this->assertConfigResolverParameterValue('translation_siteaccesses', [], 'ibexa_demo_site_admin'); + $this->assertConfigResolverParameterValue('translation_siteaccesses', [], self::EMPTY_SA_GROUP); + } + + public function testTranslationSiteAccessesWithGroup() + { + $translationSAsDemoSite = ['ibexa_demo_site', 'fre']; + $config = [ + 'system' => [ + 'ibexa_demo_frontend_group' => ['translation_siteaccesses' => $translationSAsDemoSite], + 'ibexa_demo_site' => [], + 'fre' => [], + ], + ]; + $this->load($config); + + $this->assertConfigResolverParameterValue('translation_siteaccesses', $translationSAsDemoSite, 'ibexa_demo_site'); + $this->assertConfigResolverParameterValue('translation_siteaccesses', $translationSAsDemoSite, 'fre'); + $this->assertConfigResolverParameterValue('translation_siteaccesses', [], 'ibexa_demo_site_admin'); + $this->assertConfigResolverParameterValue('translation_siteaccesses', [], self::EMPTY_SA_GROUP); + } +} + +class_alias(LanguagesTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\Parser\LanguagesTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/Parser/TemplatesTest.php b/tests/bundle/Core/DependencyInjection/Configuration/Parser/TemplatesTest.php new file mode 100644 index 0000000000..2b5a3cfc38 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/Parser/TemplatesTest.php @@ -0,0 +1,163 @@ +config = Yaml::parse(file_get_contents(__DIR__ . '/../../Fixtures/ezpublish_templates.yml')); + } + + public function testFieldTemplates() + { + $this->load(); + $fixedUpConfig = $this->getExpectedConfigFieldTemplates($this->config); + $groupFieldTemplates = $fixedUpConfig['system']['ibexa_demo_frontend_group']['field_templates']; + $demoSiteFieldTemplates = $fixedUpConfig['system']['ibexa_demo_site']['field_templates']; + $this->assertConfigResolverParameterValue( + 'field_templates', + array_merge( + // Adding default kernel value. + [['template' => '%ibexa.default_templates.field_templates%', 'priority' => 0]], + $groupFieldTemplates, + $demoSiteFieldTemplates + ), + 'ibexa_demo_site', + false + ); + $this->assertConfigResolverParameterValue( + 'field_templates', + array_merge( + // Adding default kernel value. + [['template' => '%ibexa.default_templates.field_templates%', 'priority' => 0]], + $groupFieldTemplates + ), + 'fre', + false + ); + $this->assertConfigResolverParameterValue( + 'field_templates', + [['template' => '%ibexa.default_templates.field_templates%', 'priority' => 0]], + 'ibexa_demo_site_admin', + false + ); + } + + protected function getSiteAccessProviderMock(): SiteAccessProviderInterface + { + $siteAccessProvider = $this->createMock(SiteAccessProviderInterface::class); + $siteAccessProvider + ->method('isDefined') + ->willReturnMap([ + ['ibexa_demo_site', true], + ['fre', true], + ['ibexa_demo_site_admin', true], + ]); + $siteAccessProvider + ->method('getSiteAccess') + ->willReturnMap([ + ['ibexa_demo_site', $this->getSiteAccess('ibexa_demo_site', StaticSiteAccessProvider::class, ['ibexa_demo_group', 'ibexa_demo_frontend_group'])], + ['fre', $this->getSiteAccess('fre', StaticSiteAccessProvider::class, ['ibexa_demo_group', 'ibexa_demo_frontend_group'])], + ['ibexa_demo_site_admin', $this->getSiteAccess('ibexa_demo_site_admin', StaticSiteAccessProvider::class, ['ibexa_demo_group'])], + ]); + + return $siteAccessProvider; + } + + /** + * Fixes up input configuration for field_templates as semantic configuration parser does, adding a default priority of 0 when not set. + * + * @param array $config + * + * @return array + */ + private function getExpectedConfigFieldTemplates(array $config) + { + foreach ($config['system']['ibexa_demo_frontend_group']['field_templates'] as &$block) { + if (!isset($block['priority'])) { + $block['priority'] = 0; + } + } + + return $config; + } + + public function testFieldDefinitionSettingsTemplates() + { + $this->load(); + $fixedUpConfig = $this->getExpectedConfigFieldDefinitionSettingsTemplates($this->config); + $groupFieldTemplates = $fixedUpConfig['system']['ibexa_demo_frontend_group']['fielddefinition_settings_templates']; + $demoSiteFieldTemplates = $fixedUpConfig['system']['ibexa_demo_site']['fielddefinition_settings_templates']; + + $this->assertConfigResolverParameterValue( + 'fielddefinition_settings_templates', + array_merge( + // Adding default kernel value. + [['template' => '%ibexa.default_templates.fielddefinition_settings_templates%', 'priority' => 0]], + $groupFieldTemplates, + $demoSiteFieldTemplates + ), + 'ibexa_demo_site', + false + ); + $this->assertConfigResolverParameterValue( + 'fielddefinition_settings_templates', + array_merge( + // Adding default kernel value. + [['template' => '%ibexa.default_templates.fielddefinition_settings_templates%', 'priority' => 0]], + $groupFieldTemplates + ), + 'fre', + false + ); + $this->assertConfigResolverParameterValue( + 'fielddefinition_settings_templates', + [['template' => '%ibexa.default_templates.fielddefinition_settings_templates%', 'priority' => 0]], + 'ibexa_demo_site_admin', + false + ); + } + + /** + * Fixes up input configuration for fielddefinition_settings_templates as semantic configuration parser does, adding a default priority of 0 when not set. + * + * @param array $config + * + * @return array + */ + private function getExpectedConfigFieldDefinitionSettingsTemplates(array $config) + { + foreach ($config['system']['ibexa_demo_frontend_group']['fielddefinition_settings_templates'] as &$block) { + if (!isset($block['priority'])) { + $block['priority'] = 0; + } + } + + return $config; + } +} + +class_alias(TemplatesTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\Parser\TemplatesTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/Parser/ViewTest.php b/tests/bundle/Core/DependencyInjection/Configuration/Parser/ViewTest.php new file mode 100644 index 0000000000..48f67f5db3 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/Parser/ViewTest.php @@ -0,0 +1,70 @@ +config = Yaml::parse(file_get_contents(__DIR__ . '/../../Fixtures/ezpublish_view.yml')); + } + + public function testLocationView() + { + $this->load(); + $expectedLocationView = $this->config['system']['ibexa_demo_frontend_group']['location_view']; + + // Items that don't use a custom controller got converted to content view (location view depreciation) + unset($expectedLocationView['full']['frontpage']); + unset($expectedLocationView['line']['article']); + + foreach ($expectedLocationView as &$rulesets) { + foreach ($rulesets as &$config) { + if (!isset($config['params'])) { + $config['params'] = []; + } + } + } + + $this->assertConfigResolverParameterValue('location_view', $expectedLocationView, 'ibexa_demo_site', false); + $this->assertConfigResolverParameterValue('location_view', $expectedLocationView, 'fre', false); + $this->assertConfigResolverParameterValue('location_view', [], 'ibexa_demo_site_admin', false); + } + + public function testContentView() + { + $this->load(); + $expectedContentView = $this->config['system']['ibexa_demo_frontend_group']['content_view']; + foreach ($expectedContentView as &$rulesets) { + foreach ($rulesets as &$config) { + if (!isset($config['params'])) { + $config['params'] = []; + } + } + } + + $this->assertConfigResolverParameterValue('content_view', $expectedContentView, 'ibexa_demo_site', false); + $this->assertConfigResolverParameterValue('content_view', $expectedContentView, 'fre', false); + $this->assertConfigResolverParameterValue('content_view', [], 'ibexa_demo_site_admin', false); + } +} + +class_alias(ViewTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\Parser\ViewTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/RepositoryConfigParserTest.php b/tests/bundle/Core/DependencyInjection/Configuration/RepositoryConfigParserTest.php new file mode 100644 index 0000000000..fdcbc72920 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/RepositoryConfigParserTest.php @@ -0,0 +1,42 @@ +createMock(NodeBuilder::class); + + $repositoryConfigParser = new RepositoryConfigParser([ + $this->createRepositoryConfigParserMock($nodeBuilder), + $this->createRepositoryConfigParserMock($nodeBuilder), + $this->createRepositoryConfigParserMock($nodeBuilder), + ]); + + $repositoryConfigParser->addSemanticConfig($nodeBuilder); + } + + private function createRepositoryConfigParserMock( + NodeBuilder $nodeBuilder + ): RepositoryConfigParserInterface { + $configParser = $this->createMock(RepositoryConfigParserInterface::class); + $configParser + ->expects($this->once()) + ->method('addSemanticConfig') + ->with($nodeBuilder); + + return $configParser; + } +} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php b/tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php similarity index 90% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php rename to tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php index 7e57df3aab..6c42de8e00 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php +++ b/tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ConfigurationProcessorTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\SiteAccessAware; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ConfigurationMapperInterface; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ConfigurationProcessor; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\HookableConfigurationMapperInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ConfigurationMapperInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ConfigurationProcessor; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\HookableConfigurationMapperInterface; use PHPUnit\Framework\TestCase; use stdClass; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -18,7 +18,7 @@ class ConfigurationProcessorTest extends TestCase { public function testConstruct() { - $namespace = 'ez_test'; + $namespace = 'ibexa_test'; $siteAccessNodeName = 'foo'; $container = $this->getContainerMock(); $siteAccessList = ['test', 'bar']; @@ -44,7 +44,7 @@ public function testConstruct() public function testGetSetContextualizer() { - $namespace = 'ez_test'; + $namespace = 'ibexa_test'; $siteAccessNodeName = 'foo'; $container = $this->getContainerMock(); $processor = new ConfigurationProcessor($container, $namespace, $siteAccessNodeName); @@ -63,7 +63,7 @@ public function testMapConfigWrongMapper() { $this->expectException(\InvalidArgumentException::class); - $namespace = 'ez_test'; + $namespace = 'ibexa_test'; $siteAccessNodeName = 'foo'; $container = $this->getContainerMock(); $processor = new ConfigurationProcessor($container, $namespace, $siteAccessNodeName); @@ -73,7 +73,7 @@ public function testMapConfigWrongMapper() public function testMapConfigClosure() { - $namespace = 'ez_test'; + $namespace = 'ibexa_test'; $saNodeName = 'foo'; $container = $this->getContainerMock(); $processor = new ConfigurationProcessor($container, $namespace, $saNodeName); @@ -113,7 +113,7 @@ public function testMapConfigClosure() public function testMapConfigMapperObject() { - $namespace = 'ez_test'; + $namespace = 'ibexa_test'; $saNodeName = 'foo'; $container = $this->getContainerMock(); $processor = new ConfigurationProcessor($container, $namespace, $saNodeName); @@ -159,7 +159,7 @@ public function testMapConfigMapperObject() public function testMapConfigHookableMapperObject() { - $namespace = 'ez_test'; + $namespace = 'ibexa_test'; $saNodeName = 'foo'; $container = $this->getContainerMock(); $processor = new ConfigurationProcessor($container, $namespace, $saNodeName); @@ -213,7 +213,7 @@ public function testMapConfigHookableMapperObject() public function testMapSetting() { - $namespace = 'ez_test'; + $namespace = 'ibexa_test'; $saNodeName = 'foo'; $container = $this->getContainerMock(); $processor = new ConfigurationProcessor($container, $namespace, $saNodeName); @@ -249,7 +249,7 @@ public function testMapSetting() public function testMapConfigArray() { - $namespace = 'ez_test'; + $namespace = 'ibexa_test'; $saNodeName = 'foo'; $container = $this->getContainerMock(); $processor = new ConfigurationProcessor($container, $namespace, $saNodeName); @@ -293,3 +293,5 @@ protected function getContextualizerMock() return $this->createMock(ContextualizerInterface::class); } } + +class_alias(ConfigurationProcessorTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\SiteAccessAware\ConfigurationProcessorTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php b/tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php similarity index 98% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php rename to tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php index 2c24bf00fa..d620ed6f96 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php +++ b/tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/ContextualizerTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\SiteAccessAware; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\Contextualizer; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\Contextualizer; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\ContextualizerInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -19,7 +19,7 @@ class ContextualizerTest extends TestCase private $container; /** @var string */ - private $namespace = 'ez_test'; + private $namespace = 'ibexa_test'; /** @var string */ private $saNodeName = 'heyho'; @@ -40,7 +40,7 @@ class ContextualizerTest extends TestCase 'sa3' => ['sa_group1'], ]; - /** @var \eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\Contextualizer */ + /** @var \Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\Contextualizer */ private $contextualizer; protected function setUp(): void @@ -413,7 +413,7 @@ public function testGetSetSANodeName() public function testGetSetNamespace() { - $ns = 'ezpublish'; + $ns = 'ibexa'; $this->assertSame($this->namespace, $this->contextualizer->getNamespace()); $this->contextualizer->setNamespace($ns); $this->assertSame($ns, $this->contextualizer->getNamespace()); @@ -1414,3 +1414,5 @@ public function fullMapConfigArrayProvider() return $cases; } } + +class_alias(ContextualizerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\SiteAccessAware\ContextualizerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php b/tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php similarity index 88% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php rename to tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php index d1ba978657..d1629f385e 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php +++ b/tests/bundle/Core/DependencyInjection/Configuration/SiteAccessAware/DynamicSettingParserTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\SiteAccessAware; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\SiteAccessAware\DynamicSettingParser; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\SiteAccessAware\DynamicSettingParser; use PHPUnit\Framework\TestCase; class DynamicSettingParserTest extends TestCase @@ -94,3 +94,5 @@ public function parseDynamicSettingProvider() ]; } } + +class_alias(DynamicSettingParserTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Configuration\SiteAccessAware\DynamicSettingParserTest'); diff --git a/tests/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorTest.php b/tests/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorTest.php new file mode 100644 index 0000000000..93d8b17e5e --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Configuration/Suggestion/Collector/SuggestionCollectorTest.php @@ -0,0 +1,28 @@ +addSuggestion($suggestion); + } + + $this->assertTrue($collector->hasSuggestions()); + $this->assertSame($suggestions, $collector->getSuggestions()); + } +} + +class_alias(SuggestionCollectorTest::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Tests\Collector\SuggestionCollectorTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/Tests/ConfigSuggestionTest.php b/tests/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestionTest.php similarity index 78% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/Tests/ConfigSuggestionTest.php rename to tests/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestionTest.php index 83feaac944..90893fc5dc 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/Tests/ConfigSuggestionTest.php +++ b/tests/bundle/Core/DependencyInjection/Configuration/Suggestion/ConfigSuggestionTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Tests; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Configuration\Suggestion; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\ConfigSuggestion; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\ConfigSuggestion; use PHPUnit\Framework\TestCase; class ConfigSuggestionTest extends TestCase @@ -33,7 +33,7 @@ public function testConfigSuggestion() $suggestion->setMessage($newMessage); $this->assertSame($newMessage, $suggestion->getMessage()); - $newConfigArray = ['ez' => 'publish']; + $newConfigArray = ['ibexa' => 'publish']; $suggestion->setSuggestion($newConfigArray); $this->assertSame($newConfigArray, $suggestion->getSuggestion()); @@ -41,3 +41,5 @@ public function testConfigSuggestion() $this->assertTrue($suggestion->isMandatory()); } } + +class_alias(ConfigSuggestionTest::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Tests\ConfigSuggestionTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/Tests/Formatter/YamlSuggestionFormatterTest.php b/tests/bundle/Core/DependencyInjection/Configuration/Suggestion/Formatter/YamlSuggestionFormatterTest.php similarity index 75% rename from eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/Tests/Formatter/YamlSuggestionFormatterTest.php rename to tests/bundle/Core/DependencyInjection/Configuration/Suggestion/Formatter/YamlSuggestionFormatterTest.php index f24aa88dd3..e468aab9f2 100644 --- a/eZ/Bundle/EzPublishCoreBundle/DependencyInjection/Configuration/Suggestion/Tests/Formatter/YamlSuggestionFormatterTest.php +++ b/tests/bundle/Core/DependencyInjection/Configuration/Suggestion/Formatter/YamlSuggestionFormatterTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Tests\Formatter; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Configuration\Suggestion\Formatter; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\ConfigSuggestion; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Formatter\YamlSuggestionFormatter; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\ConfigSuggestion; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\Suggestion\Formatter\YamlSuggestionFormatter; use PHPUnit\Framework\TestCase; class YamlSuggestionFormatterTest extends TestCase @@ -15,11 +15,11 @@ class YamlSuggestionFormatterTest extends TestCase public function testFormat() { $message = <<setMandatory(true); @@ -38,7 +38,7 @@ public function testFormat() ], ], ], - 'ezpublish' => [ + 'ibexa' => [ 'repositories' => [ 'my_repository' => ['engine' => 'legacy', 'connection' => 'default'], ], @@ -52,11 +52,11 @@ public function testFormat() $suggestion->setSuggestion($suggestionArray); $expectedMessage = <<assertSame($message, $formatter->format($suggestion)); } } + +class_alias(YamlSuggestionFormatterTest::class, 'eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\Suggestion\Tests\Formatter\YamlSuggestionFormatterTest'); diff --git a/tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_image.yml b/tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_image.yml new file mode 100644 index 0000000000..d9b4156142 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_image.yml @@ -0,0 +1,52 @@ +siteaccess: + default_siteaccess: ibexa_demo_site + list: + - ibexa_demo_site + - fre + - ibexa_demo_site_admin + groups: + ibexa_demo_group: + - ibexa_demo_site + - fre + - ibexa_demo_site_admin + ibexa_demo_frontend_group: + - ibexa_demo_site + - fre + match: + URIElement: 1 + Map\URI: + the_front: ibexa_demo_site + the_back: ibexa_demo_site_admin + +system: + ibexa_demo_group: + image_variations: + small: + reference: null + filters: + - { name: geometry/scaledownonly, params: [100, 160] } + medium: + reference: null + filters: + - { name: geometry/scaledownonly, params: [200, 290] } + listitem: + reference: null + filters: + - { name: geometry/scaledownonly, params: [130, 190] } + + fre: + image_variations: + test_browse: + reference: reference + filters: + - { name: geometry/scaledownonly, params: [200, 200] } + +imagemagick: + enabled: false + +http_cache: + purge_type: local + +router: + default_router: + non_siteaccess_aware_routes: ['foo_route', 'my_prefix_'] diff --git a/tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_minimal.yml b/tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_minimal.yml new file mode 100644 index 0000000000..8475a2c72c --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_minimal.yml @@ -0,0 +1,30 @@ +siteaccess: + default_siteaccess: ibexa_demo_site + list: + - ibexa_demo_site + - fre + - ibexa_demo_site_admin + groups: + ibexa_demo_group: + - ibexa_demo_site + - fre + - ibexa_demo_site_admin + ibexa_demo_frontend_group: + - ibexa_demo_site + - fre + empty_group: [] + match: + URIElement: 1 + Map\URI: + the_front: ibexa_demo_site + the_back: ibexa_demo_site_admin + +imagemagick: + enabled: false + +http_cache: + purge_type: local + +router: + default_router: + non_siteaccess_aware_routes: ['foo_route', 'my_prefix_'] diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_minimal_no_siteaccess.yml b/tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_minimal_no_siteaccess.yml similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/ezpublish_minimal_no_siteaccess.yml rename to tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_minimal_no_siteaccess.yml diff --git a/tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_templates.yml b/tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_templates.yml new file mode 100644 index 0000000000..3824a9a6d0 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_templates.yml @@ -0,0 +1,45 @@ +siteaccess: + default_siteaccess: ibexa_demo_site + list: + - ibexa_demo_site + - fre + - ibexa_demo_site_admin + groups: + ibexa_demo_group: + - ibexa_demo_site + - fre + - ibexa_demo_site_admin + ibexa_demo_frontend_group: + - ibexa_demo_site + - fre + match: + URIElement: 1 + Map\URI: + the_front: ibexa_demo_site + the_back: ibexa_demo_site_admin + +system: + ibexa_demo_frontend_group: + field_templates: + - { template: "my_field_template.html.twig", priority: 123 } + - { template: "another_template.html.twig" } + + fielddefinition_settings_templates: + - { template: "my_field_template.html.twig", priority: 123 } + - { template: "another_template.html.twig" } + + ibexa_demo_site: + field_templates: + - { template: "ibexa_demo_site_template.html.twig", priority: 123 } + fielddefinition_settings_templates: + - { template: "ibexa_demo_site_template.html.twig", priority: 123 } + +imagemagick: + enabled: false + +http_cache: + purge_type: local + +router: + default_router: + non_siteaccess_aware_routes: ['foo_route', 'my_prefix_'] diff --git a/tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_view.yml b/tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_view.yml new file mode 100644 index 0000000000..91bd821a24 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Fixtures/ezpublish_view.yml @@ -0,0 +1,109 @@ +siteaccess: + default_siteaccess: ibexa_demo_site + list: + - ibexa_demo_site + - fre + - ibexa_demo_site_admin + groups: + ibexa_demo_group: + - ibexa_demo_site + - fre + - ibexa_demo_site_admin + ibexa_demo_frontend_group: + - ibexa_demo_site + - fre + match: + URIElement: 1 + Map\URI: + the_front: ibexa_demo_site + the_back: ibexa_demo_site_admin + +system: + ibexa_demo_frontend_group: + location_view: + full: + article: + controller: "IbexaDemoBundle:Demo:showArticle" + template: "IbexaDemoBundle:full:article.html.twig" + match: + Identifier\ContentType: [article] + # There are two ways to add extra information to your response using a custom controller + blog: + # Fully customized, handling everything yourself + controller: "IbexaDemoBundle:Demo:listBlogPosts" + match: + Identifier\ContentType: [blog] + blog_post: + # Enriched controller, only adding extra parameters + controller: "IbexaDemoBundle:Demo:showBlogPost" + # Overriding the template used by the default viewLocation + template: "IbexaDemoBundle:full:blog_post.html.twig" + match: + Identifier\ContentType: [blog_post] + frontpage: + template: "IbexaDemoBundle:full:landing_page.html.twig" + match: + Identifier\ContentType: "landing_page" + params: + foo: bar + zorglub: 123 + + line: + article: + template: "IbexaDemoBundle:line:article.html.twig" + match: + Identifier\ContentType: [article] + blog_post: + controller: "IbexaDemoBundle:Demo:showBlogPost" + template: "IbexaDemoBundle:line:blog_post.html.twig" + match: + Identifier\ContentType: [blog_post] + + content_view: + full: + article: + controller: "IbexaDemoBundle:Demo:showArticle" + template: "IbexaDemoBundle:full:article.html.twig" + match: + Identifier\ContentType: [article] + # There are two ways to add extra information to your response using a custom controller + blog: + # Fully customized, handling everything yourself + controller: "IbexaDemoBundle:Demo:listBlogPosts" + match: + Identifier\ContentType: [blog] + blog_post: + # Enriched controller, only adding extra parameters + controller: "IbexaDemoBundle:Demo:showBlogPost" + # Overriding the template used by the default viewLocation + template: "IbexaDemoBundle:full:blog_post.html.twig" + match: + Identifier\ContentType: [blog_post] + params: + foo: bar + zorglub: 123 + frontpage: + template: "IbexaDemoBundle:full:landing_page.html.twig" + match: + Identifier\ContentType: "landing_page" + + line: + article: + template: "IbexaDemoBundle:line:article.html.twig" + match: + Identifier\ContentType: [article] + blog_post: + controller: "IbexaDemoBundle:Demo:showBlogPost" + template: "IbexaDemoBundle:line:blog_post.html.twig" + match: + Identifier\ContentType: [blog_post] + +imagemagick: + enabled: false + +http_cache: + purge_type: local + +router: + default_router: + non_siteaccess_aware_routes: ['foo_route', 'my_prefix_'] diff --git a/tests/bundle/Core/DependencyInjection/Fixtures/parameters.yml b/tests/bundle/Core/DependencyInjection/Fixtures/parameters.yml new file mode 100644 index 0000000000..c172d080d9 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Fixtures/parameters.yml @@ -0,0 +1,3 @@ +parameters: + kernel.environment: unit + kernel.project_dir: '.' diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/policies1.yml b/tests/bundle/Core/DependencyInjection/Fixtures/policies1.yml similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/policies1.yml rename to tests/bundle/Core/DependencyInjection/Fixtures/policies1.yml diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/policies2.yml b/tests/bundle/Core/DependencyInjection/Fixtures/policies2.yml similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/policies2.yml rename to tests/bundle/Core/DependencyInjection/Fixtures/policies2.yml diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/vendor/ezplatform-i18n/ezplatform-i18n-hi_in/ezpublish-kernel/messages.hi_IN.xlf b/tests/bundle/Core/DependencyInjection/Fixtures/vendor/ibexa/i18n/translations/hi_IN/core/messages.hi_IN.xlf similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/vendor/ezplatform-i18n/ezplatform-i18n-hi_in/ezpublish-kernel/messages.hi_IN.xlf rename to tests/bundle/Core/DependencyInjection/Fixtures/vendor/ibexa/i18n/translations/hi_IN/core/messages.hi_IN.xlf diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/vendor/ezplatform-i18n/ezplatform-i18n-nb_no/ezpublish-kernel/messages.nb_NO.xlf b/tests/bundle/Core/DependencyInjection/Fixtures/vendor/ibexa/i18n/translations/nb_NO/core/messages.nb_NO.xlf similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Fixtures/vendor/ezplatform-i18n/ezplatform-i18n-nb_no/ezpublish-kernel/messages.nb_NO.xlf rename to tests/bundle/Core/DependencyInjection/Fixtures/vendor/ibexa/i18n/translations/nb_NO/core/messages.nb_NO.xlf diff --git a/tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php b/tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php new file mode 100644 index 0000000000..f71ba92824 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/IbexaCoreExtensionTest.php @@ -0,0 +1,962 @@ +container, + new FileLocator(__DIR__ . '/Fixtures') + ); + + $loader->load('parameters.yml'); + + $this->siteaccessConfig = [ + 'siteaccess' => [ + 'default_siteaccess' => 'ibexa_demo_site', + 'list' => ['ibexa_demo_site', 'eng', 'fre', 'ibexa_demo_site_admin'], + 'groups' => [ + 'ibexa_demo_group' => ['ibexa_demo_site', 'eng', 'fre', 'ibexa_demo_site_admin'], + 'ibexa_demo_frontend_group' => ['ibexa_demo_site', 'eng', 'fre'], + 'empty_group' => [], + ], + 'match' => [ + 'URILElement' => 1, + 'Map\URI' => ['the_front' => 'ibexa_demo_site', 'the_back' => 'ibexa_demo_site_admin'], + ], + ], + 'system' => [ + 'ibexa_demo_site' => [], + 'eng' => [], + 'fre' => [], + 'ibexa_demo_site_admin' => [], + 'empty_group' => ['var_dir' => 'foo'], + ], + ]; + + $_ENV['HTTPCACHE_PURGE_TYPE'] = $_SERVER['HTTPCACHE_PURGE_TYPE'] = 'http'; + } + + protected function getContainerExtensions(): array + { + return [IbexaCoreExtension::class => $this->getCoreExtension()]; + } + + protected function getMinimalConfiguration(): array + { + return $this->minimalConfig = Yaml::parse(file_get_contents(__DIR__ . '/Fixtures/ezpublish_minimal_no_siteaccess.yml')); + } + + public function testSiteAccessConfiguration() + { + $this->load($this->siteaccessConfig); + $this->assertContainerBuilderHasParameter( + 'ibexa.site_access.list', + $this->siteaccessConfig['siteaccess']['list'] + ); + $this->assertContainerBuilderHasParameter( + 'ibexa.site_access.default', + $this->siteaccessConfig['siteaccess']['default_siteaccess'] + ); + $this->assertContainerBuilderHasParameter('ibexa.site_access.groups', $this->siteaccessConfig['siteaccess']['groups']); + + $expectedMatchingConfig = []; + foreach ($this->siteaccessConfig['siteaccess']['match'] as $key => $val) { + // Value is expected to always be an array (transformed by semantic configuration parser). + $expectedMatchingConfig[$key] = is_array($val) ? $val : ['value' => $val]; + } + $this->assertContainerBuilderHasParameter('ibexa.site_access.match_config', $expectedMatchingConfig); + $this->assertContainerBuilderHasParameter('ibexa.site_access.config.empty_group.var_dir', 'foo'); + + $groupsBySiteaccess = []; + foreach ($this->siteaccessConfig['siteaccess']['groups'] as $groupName => $groupMembers) { + foreach ($groupMembers as $member) { + if (!isset($groupsBySiteaccess[$member])) { + $groupsBySiteaccess[$member] = []; + } + + $groupsBySiteaccess[$member][] = $groupName; + } + } + } + + public function testSiteAccessNoConfiguration() + { + $this->load(); + $this->assertContainerBuilderHasParameter('ibexa.site_access.list', ['setup']); + $this->assertContainerBuilderHasParameter('ibexa.site_access.default', 'setup'); + $this->assertContainerBuilderHasParameter('ibexa.site_access.groups', []); + $this->assertContainerBuilderHasParameter('ibexa.site_access.groups_by_site_access', []); + $this->assertContainerBuilderHasParameter('ibexa.site_access.match_config', null); + } + + public function testImageMagickConfigurationBasic() + { + if (!isset($_ENV['imagemagickConvertPath']) || !is_executable($_ENV['imagemagickConvertPath'])) { + $this->markTestSkipped('Missing or mis-configured Imagemagick convert path.'); + } + + $this->load( + [ + 'imagemagick' => [ + 'enabled' => true, + 'path' => $_ENV['imagemagickConvertPath'], + ], + ] + ); + $this->assertContainerBuilderHasParameter('ibexa.image.imagemagick.enabled', true); + $this->assertContainerBuilderHasParameter('ibexa.image.imagemagick.executable_path', dirname($_ENV['imagemagickConvertPath'])); + $this->assertContainerBuilderHasParameter('ibexa.image.imagemagick.executable', basename($_ENV['imagemagickConvertPath'])); + } + + /** + * @dataProvider translationsConfigurationProvider + */ + public function testUITranslationsConfiguration( + bool $enabled, + bool $expectedParameterValue + ): void { + if (is_bool($enabled)) { + $this->load( + [ + 'ui' => [ + 'translations' => [ + 'enabled' => $enabled, + ], + ], + ] + ); + } + + $this->assertContainerBuilderHasParameter('ibexa.ui.translations.enabled', $expectedParameterValue); + } + + /** + * @return iterable + */ + public function translationsConfigurationProvider(): iterable + { + yield 'translations enabled' => [ + true, + true, + ]; + + yield 'translations disabled' => [ + false, + false, + ]; + } + + public function testImageMagickConfigurationFilters() + { + if (!isset($_ENV['imagemagickConvertPath']) || !is_executable($_ENV['imagemagickConvertPath'])) { + $this->markTestSkipped('Missing or mis-configured Imagemagick convert path.'); + } + + $customFilters = [ + 'foobar' => '-foobar', + 'wow' => '-amazing', + ]; + $this->load( + [ + 'imagemagick' => [ + 'enabled' => true, + 'path' => $_ENV['imagemagickConvertPath'], + 'filters' => $customFilters, + ], + ] + ); + $this->assertTrue($this->container->hasParameter('ibexa.image.imagemagick.filters')); + $filters = $this->container->getParameter('ibexa.image.imagemagick.filters'); + $this->assertArrayHasKey('foobar', $filters); + $this->assertSame($customFilters['foobar'], $filters['foobar']); + $this->assertArrayHasKey('wow', $filters); + $this->assertSame($customFilters['wow'], $filters['wow']); + } + + public function testImagePlaceholderConfiguration() + { + $this->load([ + 'image_placeholder' => [ + 'default' => [ + 'provider' => 'generic', + 'options' => [ + 'foo' => 'Foo', + 'bar' => 'Bar', + ], + 'verify_binary_data_availability' => true, + ], + 'fancy' => [ + 'provider' => 'remote', + ], + ], + ]); + + $this->assertEquals([ + 'default' => [ + 'provider' => 'generic', + 'options' => [ + 'foo' => 'Foo', + 'bar' => 'Bar', + ], + 'verify_binary_data_availability' => true, + ], + 'fancy' => [ + 'provider' => 'remote', + 'options' => [], + 'verify_binary_data_availability' => false, + ], + ], $this->container->getParameter('ibexa.io.images.alias.placeholder_provider')); + } + + public function testRoutingConfiguration() + { + $this->load(); + $this->assertContainerBuilderHasAlias('router', ChainRouter::class); + + $this->assertTrue($this->container->hasParameter('ibexa.default_router.non_site_access_aware_routes')); + $nonSiteaccessAwareRoutes = $this->container->getParameter('ibexa.default_router.non_site_access_aware_routes'); + // See ezpublish_minimal_no_siteaccess.yml fixture + $this->assertContains('foo_route', $nonSiteaccessAwareRoutes); + $this->assertContains('my_prefix_', $nonSiteaccessAwareRoutes); + } + + /** + * @dataProvider cacheConfigurationProvider + * + * @param array $customCacheConfig + * @param string $expectedPurgeType + */ + public function testCacheConfiguration(array $customCacheConfig, $expectedPurgeType) + { + $this->load($customCacheConfig); + + $this->assertContainerBuilderHasParameter('ibexa.http_cache.purge_type', $expectedPurgeType); + } + + public function cacheConfigurationProvider() + { + return [ + [[], 'local'], + [ + [ + 'http_cache' => ['purge_type' => 'local'], + ], + 'local', + ], + [ + [ + 'http_cache' => ['purge_type' => 'multiple_http'], + ], + 'http', + ], + [ + [ + 'http_cache' => ['purge_type' => 'single_http'], + ], + 'http', + ], + [ + [ + 'http_cache' => ['purge_type' => 'http'], + ], + 'http', + ], + [ + [ + 'http_cache' => ['purge_type' => '%env(HTTPCACHE_PURGE_TYPE)%'], + ], + 'http', + ], + ]; + } + + public function testCacheConfigurationCustomPurgeService() + { + $serviceId = 'foobar'; + $this->setDefinition($serviceId, new Definition()); + $this->load( + [ + 'http_cache' => ['purge_type' => 'foobar', 'timeout' => 12], + ] + ); + + $this->assertContainerBuilderHasParameter('ibexa.http_cache.purge_type', 'foobar'); + } + + public function testLocaleConfiguration() + { + $this->load(['locale_conversion' => ['foo' => 'bar']]); + $conversionMap = $this->container->getParameter('ibexa.locale.conversion_map'); + $this->assertArrayHasKey('foo', $conversionMap); + $this->assertSame('bar', $conversionMap['foo']); + } + + public function testRepositoriesConfiguration() + { + $repositories = [ + 'main' => [ + 'storage' => [ + 'engine' => 'legacy', + 'connection' => 'default', + ], + 'search' => [ + 'engine' => 'legacy', + 'connection' => 'blabla', + ], + 'fields_groups' => [ + 'list' => ['content', 'metadata'], + 'default' => '%ibexa.site_access.config.default.content.field_groups.default%', + ], + 'options' => [ + 'default_version_archive_limit' => 5, + 'remove_archived_versions_on_publish' => true, + ], + ], + 'foo' => [ + 'storage' => [ + 'engine' => 'sqlng', + 'connection' => 'default', + ], + 'search' => [ + 'engine' => 'solr', + 'connection' => 'lalala', + ], + 'fields_groups' => [ + 'list' => ['content', 'metadata'], + 'default' => '%ibexa.site_access.config.default.content.field_groups.default%', + ], + 'options' => [ + 'default_version_archive_limit' => 5, + 'remove_archived_versions_on_publish' => true, + ], + ], + ]; + $this->load(['repositories' => $repositories]); + $this->assertTrue($this->container->hasParameter('ibexa.repositories')); + + foreach ($repositories as &$repositoryConfig) { + $repositoryConfig['storage']['config'] = []; + $repositoryConfig['search']['config'] = []; + } + $this->assertSame($repositories, $this->container->getParameter('ibexa.repositories')); + } + + /** + * @dataProvider repositoriesConfigurationFieldGroupsProvider + */ + public function testRepositoriesConfigurationFieldGroups($repositories, $expectedRepositories) + { + $this->load(['repositories' => $repositories]); + $this->assertTrue($this->container->hasParameter('ibexa.repositories')); + + $repositoriesPar = $this->container->getParameter('ibexa.repositories'); + $this->assertEquals(count($repositories), count($repositoriesPar)); + + foreach ($repositoriesPar as $key => $repo) { + $this->assertArrayHasKey($key, $expectedRepositories); + $this->assertArrayHasKey('fields_groups', $repo); + $this->assertEqualsCanonicalizing($expectedRepositories[$key]['fields_groups'], $repo['fields_groups'], 'Invalid fields groups element'); + } + } + + public function repositoriesConfigurationFieldGroupsProvider() + { + return [ + //empty config + [ + ['main' => null], + ['main' => [ + 'fields_groups' => [ + 'list' => ['content', 'metadata'], + 'default' => '%ibexa.site_access.config.default.content.field_groups.default%', + ], + ], + ], + ], + //single item with custom fields + [ + ['foo' => [ + 'fields_groups' => [ + 'list' => ['bar', 'baz', 'john'], + 'default' => 'bar', + ], + ], + ], + ['foo' => [ + 'fields_groups' => [ + 'list' => ['bar', 'baz', 'john'], + 'default' => 'bar', + ], + ], + ], + ], + //mixed item with custom config and empty item + [ + [ + 'foo' => [ + 'fields_groups' => [ + 'list' => ['bar', 'baz', 'john', 'doe'], + 'default' => 'bar', + ], + ], + 'anotherone' => null, + ], + [ + 'foo' => [ + 'fields_groups' => [ + 'list' => ['bar', 'baz', 'john', 'doe'], + 'default' => 'bar', + ], + ], + 'anotherone' => [ + 'fields_groups' => [ + 'list' => ['content', 'metadata'], + 'default' => '%ibexa.site_access.config.default.content.field_groups.default%', + ], + ], + ], + ], + //items with only one field configured + [ + [ + 'foo' => [ + 'fields_groups' => [ + 'list' => ['bar', 'baz', 'john'], + ], + ], + 'bar' => [ + 'fields_groups' => [ + 'default' => 'metadata', + ], + ], + ], + [ + 'foo' => [ + 'fields_groups' => [ + 'list' => ['bar', 'baz', 'john'], + 'default' => '%ibexa.site_access.config.default.content.field_groups.default%', + ], + ], + 'bar' => [ + 'fields_groups' => [ + 'list' => ['content', 'metadata'], + 'default' => 'metadata', + ], + ], + ], + ], + //two different repositories + [ + [ + 'foo' => [ + 'fields_groups' => [ + 'list' => ['bar', 'baz', 'john', 'doe'], + 'default' => 'bar', + ], + ], + 'bar' => [ + 'fields_groups' => [ + 'list' => ['lorem', 'ipsum'], + 'default' => 'lorem', + ], + ], + ], + [ + 'foo' => [ + 'fields_groups' => [ + 'list' => ['bar', 'baz', 'john', 'doe'], + 'default' => 'bar', + ], + ], + 'bar' => [ + 'fields_groups' => [ + 'list' => ['lorem', 'ipsum'], + 'default' => 'lorem', + ], + ], + ], + ], + ]; + } + + public function testRepositoriesConfigurationEmpty() + { + $repositories = [ + 'main' => null, + ]; + $expectedRepositories = [ + 'main' => [ + 'storage' => [ + 'engine' => '%ibexa.api.storage_engine.default%', + 'connection' => null, + 'config' => [], + ], + 'search' => [ + 'engine' => '%ibexa.api.search_engine.default%', + 'connection' => null, + 'config' => [], + ], + 'fields_groups' => [ + 'list' => ['content', 'metadata'], + 'default' => '%ibexa.site_access.config.default.content.field_groups.default%', + ], + 'options' => [ + 'default_version_archive_limit' => 5, + 'remove_archived_versions_on_publish' => true, + ], + ], + ]; + $this->load(['repositories' => $repositories]); + $this->assertTrue($this->container->hasParameter('ibexa.repositories')); + + $this->assertSame( + $expectedRepositories, + $this->container->getParameter('ibexa.repositories') + ); + } + + public function testRepositoriesConfigurationStorageEmpty() + { + $repositories = [ + 'main' => [ + 'search' => [ + 'engine' => 'fantasticfind', + 'connection' => 'french', + ], + ], + ]; + $expectedRepositories = [ + 'main' => [ + 'search' => [ + 'engine' => 'fantasticfind', + 'connection' => 'french', + 'config' => [], + ], + 'storage' => [ + 'engine' => '%ibexa.api.storage_engine.default%', + 'connection' => null, + 'config' => [], + ], + 'fields_groups' => [ + 'list' => ['content', 'metadata'], + 'default' => '%ibexa.site_access.config.default.content.field_groups.default%', + ], + 'options' => [ + 'default_version_archive_limit' => 5, + 'remove_archived_versions_on_publish' => true, + ], + ], + ]; + $this->load(['repositories' => $repositories]); + $this->assertTrue($this->container->hasParameter('ibexa.repositories')); + + $this->assertSame( + $expectedRepositories, + $this->container->getParameter('ibexa.repositories') + ); + } + + public function testRepositoriesConfigurationSearchEmpty() + { + $repositories = [ + 'main' => [ + 'storage' => [ + 'engine' => 'persistentprudence', + 'connection' => 'yes', + ], + ], + ]; + $expectedRepositories = [ + 'main' => [ + 'storage' => [ + 'engine' => 'persistentprudence', + 'connection' => 'yes', + 'config' => [], + ], + 'search' => [ + 'engine' => '%ibexa.api.search_engine.default%', + 'connection' => null, + 'config' => [], + ], + 'fields_groups' => [ + 'list' => ['content', 'metadata'], + 'default' => '%ibexa.site_access.config.default.content.field_groups.default%', + ], + 'options' => [ + 'default_version_archive_limit' => 5, + 'remove_archived_versions_on_publish' => true, + ], + ], + ]; + $this->load(['repositories' => $repositories]); + $this->assertTrue($this->container->hasParameter('ibexa.repositories')); + + $this->assertSame( + $expectedRepositories, + $this->container->getParameter('ibexa.repositories') + ); + } + + public function testRepositoriesConfigurationCompatibility() + { + $repositories = [ + 'main' => [ + 'engine' => 'legacy', + 'connection' => 'default', + 'search' => [ + 'engine' => 'legacy', + 'connection' => 'blabla', + ], + ], + 'foo' => [ + 'engine' => 'sqlng', + 'connection' => 'default', + 'search' => [ + 'engine' => 'solr', + 'connection' => 'lalala', + ], + ], + ]; + $expectedRepositories = [ + 'main' => [ + 'search' => [ + 'engine' => 'legacy', + 'connection' => 'blabla', + 'config' => [], + ], + 'storage' => [ + 'engine' => 'legacy', + 'connection' => 'default', + 'config' => [], + ], + 'fields_groups' => [ + 'list' => ['content', 'metadata'], + 'default' => '%ibexa.site_access.config.default.content.field_groups.default%', + ], + 'options' => [ + 'default_version_archive_limit' => 5, + 'remove_archived_versions_on_publish' => true, + ], + ], + 'foo' => [ + 'search' => [ + 'engine' => 'solr', + 'connection' => 'lalala', + 'config' => [], + ], + 'storage' => [ + 'engine' => 'sqlng', + 'connection' => 'default', + 'config' => [], + ], + 'fields_groups' => [ + 'list' => ['content', 'metadata'], + 'default' => '%ibexa.site_access.config.default.content.field_groups.default%', + ], + 'options' => [ + 'default_version_archive_limit' => 5, + 'remove_archived_versions_on_publish' => true, + ], + ], + ]; + $this->load(['repositories' => $repositories]); + $this->assertTrue($this->container->hasParameter('ibexa.repositories')); + + $this->assertSame( + $expectedRepositories, + $this->container->getParameter('ibexa.repositories') + ); + } + + public function testRepositoriesConfigurationCompatibility2() + { + $repositories = [ + 'main' => [ + 'engine' => 'legacy', + 'connection' => 'default', + ], + ]; + $expectedRepositories = [ + 'main' => [ + 'storage' => [ + 'engine' => 'legacy', + 'connection' => 'default', + 'config' => [], + ], + 'search' => [ + 'engine' => '%ibexa.api.search_engine.default%', + 'connection' => null, + 'config' => [], + ], + 'fields_groups' => [ + 'list' => ['content', 'metadata'], + 'default' => '%ibexa.site_access.config.default.content.field_groups.default%', + ], + 'options' => [ + 'default_version_archive_limit' => 5, + 'remove_archived_versions_on_publish' => true, + ], + ], + ]; + $this->load(['repositories' => $repositories]); + $this->assertTrue($this->container->hasParameter('ibexa.repositories')); + + $this->assertSame( + $expectedRepositories, + $this->container->getParameter('ibexa.repositories') + ); + } + + public function testRegisteredPolicies() + { + $this->load(); + $this->assertContainerBuilderHasParameter('ibexa.api.role.policy_map'); + $previousPolicyMap = $this->container->getParameter('ibexa.api.role.policy_map'); + + $policies1 = [ + 'custom_module' => [ + 'custom_function_1' => null, + 'custom_function_2' => ['CustomLimitation'], + ], + 'helloworld' => [ + 'foo' => ['bar'], + 'baz' => null, + ], + ]; + $this->extension->addPolicyProvider(new StubPolicyProvider($policies1)); + + $policies2 = [ + 'custom_module2' => [ + 'custom_function_3' => null, + 'custom_function_4' => ['CustomLimitation2', 'CustomLimitation3'], + ], + 'helloworld' => [ + 'foo' => ['additional_limitation'], + 'some' => ['thingy', 'thing', 'but', 'wait'], + ], + ]; + $this->extension->addPolicyProvider(new StubPolicyProvider($policies2)); + + $expectedPolicies = [ + 'custom_module' => [ + 'custom_function_1' => [], + 'custom_function_2' => ['CustomLimitation' => true], + ], + 'helloworld' => [ + 'foo' => ['bar' => true, 'additional_limitation' => true], + 'baz' => [], + 'some' => ['thingy' => true, 'thing' => true, 'but' => true, 'wait' => true], + ], + 'custom_module2' => [ + 'custom_function_3' => [], + 'custom_function_4' => ['CustomLimitation2' => true, 'CustomLimitation3' => true], + ], + ]; + + $this->load(); + $this->assertContainerBuilderHasParameter('ibexa.api.role.policy_map'); + $expectedPolicies = array_merge_recursive($expectedPolicies, $previousPolicyMap); + self::assertEquals($expectedPolicies, $this->container->getParameter('ibexa.api.role.policy_map')); + } + + public function testUrlAliasConfiguration() + { + $configuration = [ + 'transformation' => 'urlalias_lowercase', + 'separator' => 'dash', + 'transformation_groups' => [ + 'urlalias' => [ + 'commands' => [ + 'ascii_lowercase', + 'cyrillic_lowercase', + ], + 'cleanup_method' => 'url_cleanup', + ], + 'urlalias_compact' => [ + 'commands' => [ + 'greek_normalize', + 'exta_lowercase', + ], + 'cleanup_method' => 'compact_cleanup', + ], + ], + ]; + $this->load([ + 'url_alias' => [ + 'slug_converter' => $configuration, + ], + ]); + $parsedConfig = $this->container->getParameter('ibexa.url_alias.slug_converter'); + $this->assertSame( + $configuration, + $parsedConfig + ); + } + + /** + * Test automatic configuration of services implementing QueryType interface. + * + * @see \Ibexa\Core\QueryType\QueryType + */ + public function testQueryTypeAutomaticConfiguration(): void + { + $definition = new Definition(TestQueryType::class); + $definition->setAutoconfigured(true); + $this->setDefinition(TestQueryType::class, $definition); + + $this->load(); + + $this->compileCoreContainer(); + + $this->assertContainerBuilderHasServiceDefinitionWithTag( + TestQueryType::class, + QueryTypePass::QUERY_TYPE_SERVICE_TAG + ); + } + + /** + * Test automatic configuration of services implementing Criterion & SortClause Filtering Query + * Builders. + * + * @dataProvider getFilteringQueryBuilderData + * + * @see \Ibexa\Contracts\Core\Repository\Values\Filter\CriterionQueryBuilder + * @see \Ibexa\Contracts\Core\Repository\Values\Filter\SortClauseQueryBuilder + */ + public function testFilteringQueryBuildersAutomaticConfiguration( + string $classFQCN, + string $tagName + ): void { + $definition = new Definition($classFQCN); + $definition->setAutoconfigured(true); + $this->setDefinition($classFQCN, $definition); + + $this->load(); + + $this->compileCoreContainer(); + + $this->assertContainerBuilderHasServiceDefinitionWithTag( + $classFQCN, + $tagName + ); + } + + /** + * Data provider for {@see testFilteringQueryBuildersAutomaticConfiguration}. + */ + public function getFilteringQueryBuilderData(): iterable + { + yield Filter\CriterionQueryBuilder::class => [ + CustomCriterionQueryBuilder::class, + ServiceTags::FILTERING_CRITERION_QUERY_BUILDER, + ]; + + yield Filter\SortClauseQueryBuilder::class => [ + CustomSortClauseQueryBuilder::class, + ServiceTags::FILTERING_SORT_CLAUSE_QUERY_BUILDER, + ]; + } + + public function testDoesNotLoadTestServicesByDefault(): void + { + $this->load(); + $this->assertContainerBuilderNotHasService(QueryControllerContext::class); + } + + public function testLoadsTestServicesWhenParameterIsSpecified(): void + { + $this->container->setParameter('ibexa.behat.browser.enabled', true); + $this->load(); + $this->assertContainerBuilderHasService(QueryControllerContext::class); + } + + /** + * Prepare Core Container for compilation by mocking required parameters and compile it. + */ + private function compileCoreContainer(): void + { + $this->disableCheckExceptionOnInvalidReferenceBehaviorPass(); + $this->container->setParameter('webroot_dir', __DIR__); + $this->container->setParameter('kernel.project_dir', __DIR__); + $this->container->setParameter('kernel.cache_dir', __DIR__ . '/cache'); + $this->container->setParameter('kernel.debug', false); + $this->compile(); + } + + final public function disableCheckExceptionOnInvalidReferenceBehaviorPass(): void + { + $compilerPassConfig = $this->container->getCompilerPassConfig(); + $compilerPassConfig->setAfterRemovingPasses( + array_filter( + $compilerPassConfig->getAfterRemovingPasses(), + static function (CompilerPassInterface $pass) { + return !($pass instanceof CheckExceptionOnInvalidReferenceBehaviorPass); + } + ) + ); + } + + protected function getCoreExtension(): IbexaCoreExtension + { + if (null !== $this->extension) { + return $this->extension; + } + + $this->extension = new IbexaCoreExtension( + [ + new Common(), + new Content(), + ], + [ + new Storage(), + new Search(), + new FieldGroups(), + new Options(), + ], + ); + + return $this->extension; + } +} + +class_alias(IbexaCoreExtensionTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\EzPublishCoreExtensionTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilderTest.php b/tests/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilderTest.php similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilderTest.php rename to tests/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilderTest.php index 94229a895b..a8c7b11634 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilderTest.php +++ b/tests/bundle/Core/DependencyInjection/Security/PolicyProvider/PoliciesConfigBuilderTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Security\PolicyProvider; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Security\PolicyProvider; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Security\PolicyProvider\PoliciesConfigBuilder; +use Ibexa\Bundle\Core\DependencyInjection\Security\PolicyProvider\PoliciesConfigBuilder; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Resource\DirectoryResource; use Symfony\Component\Config\Resource\FileResource; @@ -25,7 +25,7 @@ public function testAddConfig(array $configOne, array $configTwo, array $expecte $configBuilder->addConfig($configOne); $configBuilder->addConfig($configTwo); - self::assertSame($expectedConfig, $containerBuilder->getParameter('ezpublish.api.role.policy_map')); + self::assertSame($expectedConfig, $containerBuilder->getParameter('ibexa.api.role.policy_map')); } public function policiesConfigProvider(): array @@ -68,3 +68,5 @@ public function testAddResource() self::assertSame([$resource1, $resource2], $containerBuilder->getResources()); } } + +class_alias(PoliciesConfigBuilderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Security\PolicyProvider\PoliciesConfigBuilderTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Security/PolicyProvider/YamlPolicyProviderTest.php b/tests/bundle/Core/DependencyInjection/Security/PolicyProvider/YamlPolicyProviderTest.php similarity index 87% rename from eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Security/PolicyProvider/YamlPolicyProviderTest.php rename to tests/bundle/Core/DependencyInjection/Security/PolicyProvider/YamlPolicyProviderTest.php index 47a5064f6c..29547bd69c 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/DependencyInjection/Security/PolicyProvider/YamlPolicyProviderTest.php +++ b/tests/bundle/Core/DependencyInjection/Security/PolicyProvider/YamlPolicyProviderTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Security\PolicyProvider; +namespace Ibexa\Tests\Bundle\Core\DependencyInjection\Security\PolicyProvider; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigBuilderInterface; -use eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Stub\StubYamlPolicyProvider; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigBuilderInterface; +use Ibexa\Tests\Bundle\Core\DependencyInjection\Stub\StubYamlPolicyProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\Config\Resource\FileResource; @@ -81,3 +81,5 @@ public function testMultipleYaml() $provider->addPolicies($configBuilder); } } + +class_alias(YamlPolicyProviderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Security\PolicyProvider\YamlPolicyProviderTest'); diff --git a/tests/bundle/Core/DependencyInjection/Stub/AnnotationEntityBundle/AnnotationEntityBundle.php b/tests/bundle/Core/DependencyInjection/Stub/AnnotationEntityBundle/AnnotationEntityBundle.php new file mode 100644 index 0000000000..04b1212db3 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Stub/AnnotationEntityBundle/AnnotationEntityBundle.php @@ -0,0 +1,17 @@ +policies = $policies; + } + + public function addPolicies(ConfigBuilderInterface $configBuilder) + { + $configBuilder->addConfig($this->policies); + } +} + +class_alias(StubPolicyProvider::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Stub\StubPolicyProvider'); diff --git a/tests/bundle/Core/DependencyInjection/Stub/StubYamlPolicyProvider.php b/tests/bundle/Core/DependencyInjection/Stub/StubYamlPolicyProvider.php new file mode 100644 index 0000000000..8057d5b2e3 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Stub/StubYamlPolicyProvider.php @@ -0,0 +1,27 @@ +files = $files; + } + + protected function getFiles() + { + return $this->files; + } +} + +class_alias(StubYamlPolicyProvider::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\DependencyInjection\Stub\StubYamlPolicyProvider'); diff --git a/tests/bundle/Core/DependencyInjection/Stub/XmlEntityBundle/XmlEntityBundle.php b/tests/bundle/Core/DependencyInjection/Stub/XmlEntityBundle/XmlEntityBundle.php new file mode 100644 index 0000000000..89e6341b91 --- /dev/null +++ b/tests/bundle/Core/DependencyInjection/Stub/XmlEntityBundle/XmlEntityBundle.php @@ -0,0 +1,17 @@ + self::INVALID_ENTITY_MANAGER, ]; - /** @var \eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider */ + /** @var \Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider */ private $repositoryConfigurationProvider; /** @var \Doctrine\ORM\EntityManagerInterface */ @@ -133,7 +133,7 @@ public function testGetEntityManagerInvalid(): void } /** - * @return \eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider|\PHPUnit\Framework\MockObject\MockObject */ protected function getRepositoryConfigurationProvider(): RepositoryConfigurationProvider { @@ -156,3 +156,5 @@ protected function getEntityManager(): EntityManagerInterface return $this->createMock(EntityManagerInterface::class); } } + +class_alias(EntityManagerFactoryTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Entity\EntityManagerFactoryTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/BackgroundIndexingTerminateListenerTest.php b/tests/bundle/Core/EventListener/BackgroundIndexingTerminateListenerTest.php similarity index 87% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/BackgroundIndexingTerminateListenerTest.php rename to tests/bundle/Core/EventListener/BackgroundIndexingTerminateListenerTest.php index 5e5fdd3457..c5cfc6ebbf 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/BackgroundIndexingTerminateListenerTest.php +++ b/tests/bundle/Core/EventListener/BackgroundIndexingTerminateListenerTest.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; - -use eZ\Bundle\EzPublishCoreBundle\EventListener\BackgroundIndexingTerminateListener; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Handler as PersistenceHandler; -use eZ\Publish\SPI\Search\Handler as SearchHandler; +namespace Ibexa\Tests\Bundle\Core\EventListener; + +use Ibexa\Bundle\Core\EventListener\BackgroundIndexingTerminateListener; +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Handler as PersistenceHandler; +use Ibexa\Contracts\Core\Search\Handler as SearchHandler; +use Ibexa\Core\Base\Exceptions\NotFoundException; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\Console\ConsoleEvents; @@ -20,13 +20,13 @@ class BackgroundIndexingTerminateListenerTest extends TestCase { - /** @var \eZ\Bundle\EzPublishCoreBundle\EventListener\BackgroundIndexingTerminateListener */ + /** @var \Ibexa\Bundle\Core\EventListener\BackgroundIndexingTerminateListener */ protected $listener; - /** @var \eZ\Publish\SPI\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject */ protected $persistenceMock; - /** @var \eZ\Publish\SPI\Search\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Search\Handler|\PHPUnit\Framework\MockObject\MockObject */ protected $searchMock; protected function setUp(): void @@ -162,7 +162,7 @@ public function indexDeleteProvider() /** * @dataProvider indexDeleteProvider * - * @param \eZ\Publish\SPI\Persistence\Content\ContentInfo|\eZ\Publish\SPI\Persistence\Content\Location $value + * @param \Ibexa\Contracts\Core\Persistence\Content\ContentInfo|\Ibexa\Contracts\Core\Persistence\Content\Location $value * @param \PHPUnit\Framework\MockObject\Stub $infoReturn * @param \PHPUnit\Framework\MockObject\Stub|null $contentReturn */ @@ -222,3 +222,5 @@ public function testIndexDelete($value, $infoReturn, $contentReturn = null) $this->listener->reindex(); } } + +class_alias(BackgroundIndexingTerminateListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\BackgroundIndexingTerminateListenerTest'); diff --git a/tests/bundle/Core/EventListener/BackwardCompatibleCommandListenerTest.php b/tests/bundle/Core/EventListener/BackwardCompatibleCommandListenerTest.php index d6fde3f437..56288b4132 100644 --- a/tests/bundle/Core/EventListener/BackwardCompatibleCommandListenerTest.php +++ b/tests/bundle/Core/EventListener/BackwardCompatibleCommandListenerTest.php @@ -8,8 +8,8 @@ namespace Ibexa\Tests\Bundle\Core\EventListener; -use eZ\Bundle\EzPublishCoreBundle\Command\BackwardCompatibleCommand; -use eZ\Bundle\EzPublishCoreBundle\EventListener\BackwardCompatibleCommandListener; +use Ibexa\Bundle\Core\Command\BackwardCompatibleCommand; +use Ibexa\Bundle\Core\EventListener\BackwardCompatibleCommandListener; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\ConsoleEvents; @@ -32,7 +32,7 @@ final class BackwardCompatibleCommandListenerTest extends TestCase 'ezpublish:command', ]; - /** @var \eZ\Bundle\EzPublishCoreBundle\EventListener\BackwardCompatibleCommandListener */ + /** @var \Ibexa\Bundle\Core\EventListener\BackwardCompatibleCommandListener */ private $listener; protected function setUp(): void @@ -109,7 +109,7 @@ private function assertOutputContainsDeprecationWarning(BufferedOutput $output): } /** - * @return \eZ\Bundle\EzPublishCoreBundle\Command\BackwardCompatibleCommand|\Symfony\Component\Console\Command\Command + * @return \Ibexa\Bundle\Core\Command\BackwardCompatibleCommand|\Symfony\Component\Console\Command\Command */ private function createBackwardCompatibleCommand(string $name, array $aliases = []): Command { diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ConfigScopeListenerTest.php b/tests/bundle/Core/EventListener/ConfigScopeListenerTest.php similarity index 79% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ConfigScopeListenerTest.php rename to tests/bundle/Core/EventListener/ConfigScopeListenerTest.php index a532092034..9fa06d9a31 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ConfigScopeListenerTest.php +++ b/tests/bundle/Core/EventListener/ConfigScopeListenerTest.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; +namespace Ibexa\Tests\Bundle\Core\EventListener; -use eZ\Bundle\EzPublishCoreBundle\EventListener\ConfigScopeListener; -use eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\Stubs\ViewManager; -use eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\Stubs\ViewProvider; -use eZ\Publish\Core\MVC\Symfony\Configuration\VersatileScopeInterface; -use eZ\Publish\Core\MVC\Symfony\Event\ScopeChangeEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Bundle\Core\EventListener\ConfigScopeListener; +use Ibexa\Core\MVC\Symfony\Configuration\VersatileScopeInterface; +use Ibexa\Core\MVC\Symfony\Event\ScopeChangeEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Tests\Bundle\Core\EventListener\Stubs\ViewManager; +use Ibexa\Tests\Bundle\Core\EventListener\Stubs\ViewProvider; use PHPUnit\Framework\TestCase; class ConfigScopeListenerTest extends TestCase @@ -74,3 +74,5 @@ public function testOnConfigScopeChange() $this->assertSame($siteAccess, $event->getSiteAccess()); } } + +class_alias(ConfigScopeListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\ConfigScopeListenerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ConsoleCommandListenerTest.php b/tests/bundle/Core/EventListener/ConsoleCommandListenerTest.php similarity index 87% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ConsoleCommandListenerTest.php rename to tests/bundle/Core/EventListener/ConsoleCommandListenerTest.php index f284dc237e..e622883af0 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ConsoleCommandListenerTest.php +++ b/tests/bundle/Core/EventListener/ConsoleCommandListenerTest.php @@ -4,11 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; +namespace Ibexa\Tests\Bundle\Core\EventListener; -use eZ\Bundle\EzPublishCoreBundle\EventListener\ConsoleCommandListener; -use eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\Stubs\TestOutput; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Bundle\Core\EventListener\ConsoleCommandListener; +use Ibexa\Core\MVC\Exception\InvalidSiteAccessException; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Tests\Bundle\Core\EventListener\Stubs\TestOutput; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\ConsoleEvents; @@ -23,13 +24,13 @@ class ConsoleCommandListenerTest extends TestCase { private const INVALID_SA_NAME = 'foo'; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ private $siteAccess; /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject */ private $dispatcher; - /** @var \eZ\Bundle\EzPublishCoreBundle\EventListener\ConsoleCommandListener */ + /** @var \Ibexa\Bundle\Core\EventListener\ConsoleCommandListener */ private $listener; /** @var \Symfony\Component\Console\Input\InputDefinition */ @@ -66,7 +67,7 @@ public function testGetSubscribedEvents() public function testInvalidSiteAccessDev() { - $this->expectException(\eZ\Publish\Core\MVC\Exception\InvalidSiteAccessException::class); + $this->expectException(InvalidSiteAccessException::class); $this->expectExceptionMessageMatches('/^Invalid SiteAccess \'foo\', matched by .+\\. Valid SiteAccesses are/'); $this->dispatcher->expects($this->never()) @@ -79,7 +80,7 @@ public function testInvalidSiteAccessDev() public function testInvalidSiteAccessProd() { - $this->expectException(\eZ\Publish\Core\MVC\Exception\InvalidSiteAccessException::class); + $this->expectException(InvalidSiteAccessException::class); $this->expectExceptionMessageMatches('/^Invalid SiteAccess \'foo\', matched by .+\\.$/'); $this->dispatcher->expects($this->never()) @@ -124,3 +125,5 @@ private function getSiteAccessProviderMock(): SiteAccess\SiteAccessProviderInter return $siteAccessProviderMock; } } + +class_alias(ConsoleCommandListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\ConsoleCommandListenerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ContentDownloadRouteReferenceListenerTest.php b/tests/bundle/Core/EventListener/ContentDownloadRouteReferenceListenerTest.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ContentDownloadRouteReferenceListenerTest.php rename to tests/bundle/Core/EventListener/ContentDownloadRouteReferenceListenerTest.php index 2f74f748c5..dbbcf820e7 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ContentDownloadRouteReferenceListenerTest.php +++ b/tests/bundle/Core/EventListener/ContentDownloadRouteReferenceListenerTest.php @@ -4,24 +4,24 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; - -use eZ\Bundle\EzPublishCoreBundle\EventListener\ContentDownloadRouteReferenceListener; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\BinaryFile\Value as BinaryFileValue; -use eZ\Publish\Core\Helper\TranslationHelper; -use eZ\Publish\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent; -use eZ\Publish\Core\MVC\Symfony\Routing\RouteReference; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; +namespace Ibexa\Tests\Bundle\Core\EventListener; + +use Ibexa\Bundle\Core\EventListener\ContentDownloadRouteReferenceListener; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\FieldType\BinaryFile\Value as BinaryFileValue; +use Ibexa\Core\Helper\TranslationHelper; +use Ibexa\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent; +use Ibexa\Core\MVC\Symfony\Routing\RouteReference; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; use PHPUnit\Framework\TestCase; use stdClass; use Symfony\Component\HttpFoundation\Request; class ContentDownloadRouteReferenceListenerTest extends TestCase { - /** @var \eZ\Publish\Core\Helper\TranslationHelper|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\Helper\TranslationHelper|\PHPUnit\Framework\MockObject\MockObject */ protected $translationHelperMock; protected function setUp(): void @@ -66,7 +66,7 @@ public function testThrowsExceptionOnBadFieldIdentifier() 'internalFields' => [], 'versionInfo' => new VersionInfo( [ - 'contentInfo' => new ContentInfo(['mainLanguageCode' => 'eng-GB']), + 'contentInfo' => new ContentInfo(['id' => 1, 'mainLanguageCode' => 'eng-GB']), ] ), ] @@ -132,7 +132,7 @@ public function testDownloadNameOverrideWorks() } /** - * @return \eZ\Publish\Core\Repository\Values\Content\Content + * @return \Ibexa\Core\Repository\Values\Content\Content */ protected function getCompleteContent() { @@ -161,3 +161,5 @@ protected function getListener() return new ContentDownloadRouteReferenceListener($this->translationHelperMock); } } + +class_alias(ContentDownloadRouteReferenceListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\ContentDownloadRouteReferenceListenerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ExceptionListenerTest.php b/tests/bundle/Core/EventListener/ExceptionListenerTest.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ExceptionListenerTest.php rename to tests/bundle/Core/EventListener/ExceptionListenerTest.php index d67671638d..b5588dbd2b 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/ExceptionListenerTest.php +++ b/tests/bundle/Core/EventListener/ExceptionListenerTest.php @@ -4,25 +4,25 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; +namespace Ibexa\Tests\Bundle\Core\EventListener; use Exception; -use eZ\Bundle\EzPublishCoreBundle\EventListener\ExceptionListener; -use eZ\Publish\Core\Base\Exceptions\BadStateException; -use eZ\Publish\Core\Base\Exceptions\ContentFieldValidationException; -use eZ\Publish\Core\Base\Exceptions\ContentTypeFieldDefinitionValidationException; -use eZ\Publish\Core\Base\Exceptions\ContentTypeValidationException; -use eZ\Publish\Core\Base\Exceptions\ContentValidationException; -use eZ\Publish\Core\Base\Exceptions\ForbiddenException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\Base\Exceptions\LimitationValidationException; -use eZ\Publish\Core\Base\Exceptions\MissingClass; -use eZ\Publish\Core\Base\Exceptions\NotFound\FieldTypeNotFoundException; -use eZ\Publish\Core\Base\Exceptions\NotFound\LimitationNotFoundException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; +use Ibexa\Bundle\Core\EventListener\ExceptionListener; +use Ibexa\Core\Base\Exceptions\BadStateException; +use Ibexa\Core\Base\Exceptions\ContentFieldValidationException; +use Ibexa\Core\Base\Exceptions\ContentTypeFieldDefinitionValidationException; +use Ibexa\Core\Base\Exceptions\ContentTypeValidationException; +use Ibexa\Core\Base\Exceptions\ContentValidationException; +use Ibexa\Core\Base\Exceptions\ForbiddenException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\Base\Exceptions\InvalidArgumentValue; +use Ibexa\Core\Base\Exceptions\LimitationValidationException; +use Ibexa\Core\Base\Exceptions\MissingClass; +use Ibexa\Core\Base\Exceptions\NotFound\FieldTypeNotFoundException; +use Ibexa\Core\Base\Exceptions\NotFound\LimitationNotFoundException; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Base\Exceptions\UnauthorizedException; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -40,7 +40,7 @@ class ExceptionListenerTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject|\Symfony\Contracts\Translation\TranslatorInterface */ private $translator; - /** @var \eZ\Bundle\EzPublishCoreBundle\EventListener\ExceptionListener */ + /** @var \Ibexa\Bundle\Core\EventListener\ExceptionListener */ private $listener; protected function setUp(): void @@ -122,7 +122,7 @@ public function testUnauthorizedException() /** * @dataProvider badRequestExceptionProvider * - * @param \Exception|\eZ\Publish\Core\Base\Translatable $exception + * @param \Exception|\Ibexa\Core\Base\Translatable $exception */ public function testBadRequestException(Exception $exception) { @@ -159,7 +159,7 @@ public function badRequestExceptionProvider() /** * @dataProvider otherExceptionProvider * - * @param \Exception|\eZ\Publish\Core\Base\Translatable $exception + * @param \Exception|\Ibexa\Core\Base\Translatable $exception */ public function testOtherRepositoryException(Exception $exception) { @@ -211,3 +211,5 @@ public function testUntouchedException() self::assertSame($exception, $event->getThrowable()); } } + +class_alias(ExceptionListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\ExceptionListenerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/IndexRequestListenerTest.php b/tests/bundle/Core/EventListener/IndexRequestListenerTest.php similarity index 89% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/IndexRequestListenerTest.php rename to tests/bundle/Core/EventListener/IndexRequestListenerTest.php index 443352e63d..9b7ea21ae8 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/IndexRequestListenerTest.php +++ b/tests/bundle/Core/EventListener/IndexRequestListenerTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; +namespace Ibexa\Tests\Bundle\Core\EventListener; -use eZ\Bundle\EzPublishCoreBundle\EventListener\IndexRequestListener; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Bundle\Core\EventListener\IndexRequestListener; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -19,7 +19,7 @@ class IndexRequestListenerTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $configResolver; - /** @var \eZ\Bundle\EzPublishCoreBundle\EventListener\IndexRequestListener */ + /** @var \Ibexa\Bundle\Core\EventListener\IndexRequestListener */ private $indexRequestEventListener; /** @var \Symfony\Component\HttpFoundation\Request */ @@ -100,3 +100,5 @@ public function testOnKernelRequestIndexNotOnIndexPage() $this->assertFalse($this->request->attributes->has('needsRedirect')); } } + +class_alias(IndexRequestListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\IndexRequestListenerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/LocaleListenerTest.php b/tests/bundle/Core/EventListener/LocaleListenerTest.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/LocaleListenerTest.php rename to tests/bundle/Core/EventListener/LocaleListenerTest.php index 7181598171..0f31f7a755 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/LocaleListenerTest.php +++ b/tests/bundle/Core/EventListener/LocaleListenerTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; +namespace Ibexa\Tests\Bundle\Core\EventListener; -use eZ\Bundle\EzPublishCoreBundle\EventListener\LocaleListener; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface; +use Ibexa\Bundle\Core\EventListener\LocaleListener; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Locale\LocaleConverterInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\Request; @@ -19,13 +19,13 @@ class LocaleListenerTest extends TestCase { - /** @var \eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\MVC\Symfony\Locale\LocaleConverterInterface|\PHPUnit\Framework\MockObject\MockObject */ private $localeConverter; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ private $configResolver; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ private $requestStack; protected function setUp(): void @@ -122,3 +122,5 @@ public function onKernelRequestProvider(): array ]; } } + +class_alias(LocaleListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\LocaleListenerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/OriginalRequestListenerTest.php b/tests/bundle/Core/EventListener/OriginalRequestListenerTest.php similarity index 92% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/OriginalRequestListenerTest.php rename to tests/bundle/Core/EventListener/OriginalRequestListenerTest.php index d9c7dc99e6..9f5fd12a12 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/OriginalRequestListenerTest.php +++ b/tests/bundle/Core/EventListener/OriginalRequestListenerTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; +namespace Ibexa\Tests\Bundle\Core\EventListener; -use eZ\Bundle\EzPublishCoreBundle\EventListener\OriginalRequestListener; +use Ibexa\Bundle\Core\EventListener\OriginalRequestListener; use PHPUnit\Framework\TestCase; use Symfony\Bridge\PhpUnit\ClockMock; use Symfony\Component\HttpFoundation\Request; @@ -84,3 +84,5 @@ public function testOnKernelRequestWithOriginalRequest() ClockMock::withClockMock(false); } } + +class_alias(OriginalRequestListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\OriginalRequestListenerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RejectExplicitFrontControllerRequestsListenerTest.php b/tests/bundle/Core/EventListener/RejectExplicitFrontControllerRequestsListenerTest.php similarity index 95% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RejectExplicitFrontControllerRequestsListenerTest.php rename to tests/bundle/Core/EventListener/RejectExplicitFrontControllerRequestsListenerTest.php index 52933b6f16..671d5aa046 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RejectExplicitFrontControllerRequestsListenerTest.php +++ b/tests/bundle/Core/EventListener/RejectExplicitFrontControllerRequestsListenerTest.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; +namespace Ibexa\Tests\Bundle\Core\EventListener; -use eZ\Bundle\EzPublishCoreBundle\EventListener\RejectExplicitFrontControllerRequestsListener; +use Ibexa\Bundle\Core\EventListener\RejectExplicitFrontControllerRequestsListener; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -18,7 +18,7 @@ class RejectExplicitFrontControllerRequestsListenerTest extends TestCase { - /** @var \eZ\Bundle\EzPublishCoreBundle\EventListener\RejectExplicitFrontControllerRequestsListener */ + /** @var \Ibexa\Bundle\Core\EventListener\RejectExplicitFrontControllerRequestsListener */ private $eventListener; /** @var \Symfony\Component\HttpKernel\HttpKernelInterface|\PHPUnit\Framework\MockObject\MockObject */ @@ -282,3 +282,5 @@ public function prohibitedRequestDataProvider(): array ]; } } + +class_alias(RejectExplicitFrontControllerRequestsListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\RejectExplicitFrontControllerRequestsListenerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RequestEventListenerTest.php b/tests/bundle/Core/EventListener/RequestEventListenerTest.php similarity index 95% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RequestEventListenerTest.php rename to tests/bundle/Core/EventListener/RequestEventListenerTest.php index c1895aded1..259c6e390d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RequestEventListenerTest.php +++ b/tests/bundle/Core/EventListener/RequestEventListenerTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; +namespace Ibexa\Tests\Bundle\Core\EventListener; -use eZ\Bundle\EzPublishCoreBundle\EventListener\RequestEventListener; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Bundle\Core\EventListener\RequestEventListener; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Bridge\PhpUnit\ClockMock; @@ -31,7 +31,7 @@ class RequestEventListenerTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject|\Psr\Log\LoggerInterface */ private $logger; - /** @var \eZ\Bundle\EzPublishCoreBundle\EventListener\RequestEventListener */ + /** @var \Ibexa\Bundle\Core\EventListener\RequestEventListener */ private $requestEventListener; /** @var \Symfony\Component\HttpFoundation\Request */ @@ -203,3 +203,5 @@ public function testOnKernelRequestRedirectPrependSiteaccess() $this->assertTrue($event->isPropagationStopped()); } } + +class_alias(RequestEventListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\RequestEventListenerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RoutingListenerTest.php b/tests/bundle/Core/EventListener/RoutingListenerTest.php similarity index 83% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RoutingListenerTest.php rename to tests/bundle/Core/EventListener/RoutingListenerTest.php index 8a62113b23..f171163834 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/RoutingListenerTest.php +++ b/tests/bundle/Core/EventListener/RoutingListenerTest.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; +namespace Ibexa\Tests\Bundle\Core\EventListener; -use eZ\Bundle\EzPublishCoreBundle\EventListener\RoutingListener; -use eZ\Bundle\EzPublishCoreBundle\Routing\UrlAliasRouter; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Bundle\Core\EventListener\RoutingListener; +use Ibexa\Bundle\Core\Routing\UrlAliasRouter; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator; +use Ibexa\Core\MVC\Symfony\SiteAccess; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -83,3 +83,5 @@ public function testOnSiteAccessMatch() $listener->onSiteAccessMatch($event); } } + +class_alias(RoutingListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\RoutingListenerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/SessionInitByPostListenerTest.php b/tests/bundle/Core/EventListener/SessionInitByPostListenerTest.php similarity index 89% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/SessionInitByPostListenerTest.php rename to tests/bundle/Core/EventListener/SessionInitByPostListenerTest.php index 76e3cbc50c..764624af71 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/SessionInitByPostListenerTest.php +++ b/tests/bundle/Core/EventListener/SessionInitByPostListenerTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; +namespace Ibexa\Tests\Bundle\Core\EventListener; -use eZ\Bundle\EzPublishCoreBundle\EventListener\SessionInitByPostListener; -use eZ\Publish\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Bundle\Core\EventListener\SessionInitByPostListener; +use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\SiteAccess; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\Session; @@ -19,7 +19,7 @@ class SessionInitByPostListenerTest extends TestCase { - /** @var \eZ\Bundle\EzPublishCoreBundle\EventListener\SessionInitByPostListener */ + /** @var \Ibexa\Bundle\Core\EventListener\SessionInitByPostListener */ private $listener; protected function setUp(): void @@ -126,3 +126,5 @@ public function testOnSiteAccessMatchNoSession(): void $this->listener->onSiteAccessMatch($event); } } + +class_alias(SessionInitByPostListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\SessionInitByPostListenerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/SessionSetDynamicNameListenerTest.php b/tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php similarity index 93% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/SessionSetDynamicNameListenerTest.php rename to tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php index 504332875d..e7c93d7a76 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/SessionSetDynamicNameListenerTest.php +++ b/tests/bundle/Core/EventListener/SessionSetDynamicNameListenerTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; +namespace Ibexa\Tests\Bundle\Core\EventListener; -use eZ\Bundle\EzPublishCoreBundle\EventListener\SessionSetDynamicNameListener; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Bundle\Core\EventListener\SessionSetDynamicNameListener; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\SiteAccess; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\Session; @@ -167,3 +167,5 @@ public function testOnSiteAccessMatchNoConfiguredSessionName() $listener->onSiteAccessMatch(new PostSiteAccessMatchEvent(new SiteAccess('test'), $request, HttpKernelInterface::MAIN_REQUEST)); } } + +class_alias(SessionSetDynamicNameListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\SessionSetDynamicNameListenerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/SiteAccessListenerTest.php b/tests/bundle/Core/EventListener/SiteAccessListenerTest.php similarity index 91% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/SiteAccessListenerTest.php rename to tests/bundle/Core/EventListener/SiteAccessListenerTest.php index b0a804a9cd..7f2156b7fd 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventListener/SiteAccessListenerTest.php +++ b/tests/bundle/Core/EventListener/SiteAccessListenerTest.php @@ -4,23 +4,23 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventListener; +namespace Ibexa\Tests\Bundle\Core\EventListener; -use eZ\Bundle\EzPublishCoreBundle\EventListener\SiteAccessListener; -use eZ\Publish\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccessGroup; +use Ibexa\Bundle\Core\EventListener\SiteAccessListener; +use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccessGroup; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\HttpKernelInterface; class SiteAccessListenerTest extends TestCase { - /** @var \eZ\Bundle\EzPublishCoreBundle\EventListener\SiteAccessListener */ + /** @var \Ibexa\Bundle\Core\EventListener\SiteAccessListener */ private $listener; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ private $defaultSiteaccess; protected function setUp(): void @@ -119,3 +119,5 @@ public function testOnSiteAccessMatchSubRequest($uri, $semanticPathinfo, $vpStri self::assertSame($this->defaultSiteaccess->groups, $siteAccess->groups); } } + +class_alias(SiteAccessListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\SiteAccessListenerTest'); diff --git a/tests/bundle/Core/EventListener/Stubs/FooServiceInterface.php b/tests/bundle/Core/EventListener/Stubs/FooServiceInterface.php new file mode 100644 index 0000000000..ae9a05f613 --- /dev/null +++ b/tests/bundle/Core/EventListener/Stubs/FooServiceInterface.php @@ -0,0 +1,14 @@ +output .= $message . ($newline ? "\n" : ''); } } + +class_alias(TestOutput::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\Stubs\TestOutput'); diff --git a/tests/bundle/Core/EventListener/Stubs/ViewManager.php b/tests/bundle/Core/EventListener/Stubs/ViewManager.php new file mode 100644 index 0000000000..5ce6a82cdf --- /dev/null +++ b/tests/bundle/Core/EventListener/Stubs/ViewManager.php @@ -0,0 +1,44 @@ +assertSame( [KernelEvents::CONTROLLER => ['getController', 10]], - $this->controllerListener->getSubscribedEvents() + $this->controllerListener::getSubscribedEvents() ); } @@ -120,7 +120,7 @@ public function testGetControllerMatchedView() $this->request->attributes->add( [ - '_controller' => 'ez_content:viewAction', + '_controller' => 'ibexa_content::viewAction', 'contentId' => $contentId, 'locationId' => $locationId, 'viewType' => $viewType, @@ -189,7 +189,7 @@ public function testGetControllerEmitsProperEvents(): void } /** - * @return \PHPUnit\Framework\MockObject\MockObject|\Symfony\Component\HttpKernel\Event\ControllerEvent + * @return \Symfony\Component\HttpKernel\Event\ControllerEvent */ protected function createEvent() { @@ -201,3 +201,5 @@ static function () {}, ); } } + +class_alias(ViewControllerListenerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventListener\ViewControllerListenerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/EventSubscriber/CrowdinRequestLocaleSubscriberTest.php b/tests/bundle/Core/EventSubscriber/CrowdinRequestLocaleSubscriberTest.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/Tests/EventSubscriber/CrowdinRequestLocaleSubscriberTest.php rename to tests/bundle/Core/EventSubscriber/CrowdinRequestLocaleSubscriberTest.php index 6ea670d895..e4b73994ee 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/EventSubscriber/CrowdinRequestLocaleSubscriberTest.php +++ b/tests/bundle/Core/EventSubscriber/CrowdinRequestLocaleSubscriberTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\EventSubscriber; +namespace Ibexa\Tests\Bundle\Core\EventSubscriber; -use eZ\Bundle\EzPublishCoreBundle\EventSubscriber\CrowdinRequestLocaleSubscriber; +use Ibexa\Bundle\Core\EventSubscriber\CrowdinRequestLocaleSubscriber; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -49,3 +49,5 @@ public function testSetRequestsProvider() ]; } } + +class_alias(CrowdinRequestLocaleSubscriberTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\EventSubscriber\CrowdinRequestLocaleSubscriberTest'); diff --git a/tests/bundle/Core/EventSubscriber/TrustedHeaderClientIpEventSubscriberTest.php b/tests/bundle/Core/EventSubscriber/TrustedHeaderClientIpEventSubscriberTest.php new file mode 100644 index 0000000000..55446a653d --- /dev/null +++ b/tests/bundle/Core/EventSubscriber/TrustedHeaderClientIpEventSubscriberTest.php @@ -0,0 +1,178 @@ + $data + */ + public function __construct(?string $name = null, array $data = [], string $dataName = '') + { + parent::__construct($name, $data, $dataName); + + $this->originalRemoteAddr = $_SERVER['REMOTE_ADDR'] ?? null; + } + + protected function setUp(): void + { + $_SERVER['REMOTE_ADDR'] = null; + Request::setTrustedProxies([], -1); + } + + protected function tearDown(): void + { + $_SERVER['REMOTE_ADDR'] = $this->originalRemoteAddr; + } + + public function getTrustedHeaderEventSubscriberTestData(): array + { + return [ + 'default behaviour' => [ + self::REAL_CLIENT_IP, + self::REAL_CLIENT_IP, + ], + 'use custom header name with valid value' => [ + self::REAL_CLIENT_IP, + self::PROXY_IP, + 'X-Custom-Header', + ['X-Custom-Header' => self::REAL_CLIENT_IP], + ], + 'use custom header name without valid value' => [ + self::PROXY_IP, + self::PROXY_IP, + 'X-Custom-Header', + ], + 'use custom header value without custom header name' => [ + self::PROXY_IP, + self::PROXY_IP, + null, + ['X-Custom-Header' => self::REAL_CLIENT_IP], + ], + 'default platform.sh behaviour' => [ + self::REAL_CLIENT_IP, + self::PROXY_IP, + null, + ['X-Client-IP' => self::REAL_CLIENT_IP], + ['PLATFORM_RELATIONSHIPS' => true], + ], + 'use custom header name without valid value on platform.sh' => [ + self::PROXY_IP, + self::PROXY_IP, + 'X-Custom-Header', + [self::PLATFORM_SH_TRUSTED_HEADER_CLIENT_IP => self::REAL_CLIENT_IP], + ['PLATFORM_RELATIONSHIPS' => true], + ], + 'use custom header with valid value on platform.sh' => [ + self::CUSTOM_CLIENT_IP, + self::PROXY_IP, + 'X-Custom-Header', + [ + self::PLATFORM_SH_TRUSTED_HEADER_CLIENT_IP => self::REAL_CLIENT_IP, + 'X-Custom-Header' => self::CUSTOM_CLIENT_IP, + ], + ['PLATFORM_RELATIONSHIPS' => true], + ], + 'use valid value without custom header name on platform.sh' => [ + self::REAL_CLIENT_IP, + self::PROXY_IP, + null, + [ + self::PLATFORM_SH_TRUSTED_HEADER_CLIENT_IP => self::REAL_CLIENT_IP, + 'X-Custom-Header' => self::CUSTOM_CLIENT_IP, + ], + ['PLATFORM_RELATIONSHIPS' => true], + ], + ]; + } + + public function testTrustedHeaderEventSubscriberWithoutTrustedProxy(): void + { + $_SERVER['REMOTE_ADDR'] = self::PROXY_IP; + + $eventDispatcher = new EventDispatcher(); + $eventDispatcher->addSubscriber( + new TrustedHeaderClientIpEventSubscriber('X-Custom-Header') + ); + + $request = Request::create('/', 'GET', [], [], [], array_merge( + $_SERVER, + ['PLATFORM_RELATIONSHIPS' => true], + )); + $request->headers->add([ + 'X-Custom-Header' => self::REAL_CLIENT_IP, + ]); + + $event = $eventDispatcher->dispatch(new RequestEvent( + self::createMock(KernelInterface::class), + $request, + HttpKernelInterface::MAIN_REQUEST + ), KernelEvents::REQUEST); + + /** @var \Symfony\Component\HttpFoundation\Request $request */ + $request = $event->getRequest(); + + self::assertEquals(self::PROXY_IP, $request->getClientIp()); + } + + /** + * @dataProvider getTrustedHeaderEventSubscriberTestData + */ + public function testTrustedHeaderEventSubscriberWithTrustedProxy( + string $expectedIp, + string $remoteAddrIp, + ?string $trustedHeaderName = null, + array $headers = [], + array $server = [] + ): void { + $_SERVER['REMOTE_ADDR'] = $remoteAddrIp; + Request::setTrustedProxies(['REMOTE_ADDR'], Request::getTrustedHeaderSet()); + + $eventDispatcher = new EventDispatcher(); + $eventDispatcher->addSubscriber( + new TrustedHeaderClientIpEventSubscriber($trustedHeaderName) + ); + + $request = Request::create('/', 'GET', [], [], [], array_merge( + $server, + ['REMOTE_ADDR' => $remoteAddrIp], + )); + $request->headers->add($headers); + + $event = $eventDispatcher->dispatch(new RequestEvent( + self::createMock(KernelInterface::class), + $request, + HttpKernelInterface::MAIN_REQUEST + ), KernelEvents::REQUEST); + + /** @var \Symfony\Component\HttpFoundation\Request $request */ + $request = $event->getRequest(); + + self::assertEquals($expectedIp, $request->getClientIp()); + } +} diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/DecoratedFragmentRendererTest.php b/tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php similarity index 94% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/DecoratedFragmentRendererTest.php rename to tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php index 4e05182bc8..f43f8e4cd9 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/DecoratedFragmentRendererTest.php +++ b/tests/bundle/Core/Fragment/DecoratedFragmentRendererTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Fragment; +namespace Ibexa\Tests\Bundle\Core\Fragment; -use eZ\Bundle\EzPublishCoreBundle\Fragment\DecoratedFragmentRenderer; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Bundle\Core\Fragment\DecoratedFragmentRenderer; +use Ibexa\Core\MVC\Symfony\SiteAccess; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; @@ -134,3 +134,5 @@ public function getRenderer(): FragmentRendererInterface return new DecoratedFragmentRenderer($this->innerRenderer); } } + +class_alias(DecoratedFragmentRendererTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Fragment\DecoratedFragmentRendererTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/DirectFragmentRendererTest.php b/tests/bundle/Core/Fragment/DirectFragmentRendererTest.php similarity index 91% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/DirectFragmentRendererTest.php rename to tests/bundle/Core/Fragment/DirectFragmentRendererTest.php index feca250576..8d18c1aacf 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/DirectFragmentRendererTest.php +++ b/tests/bundle/Core/Fragment/DirectFragmentRendererTest.php @@ -6,14 +6,14 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Fragment; - -use eZ\Bundle\EzPublishCoreBundle\EventListener\ViewControllerListener; -use eZ\Bundle\EzPublishCoreBundle\Fragment\DirectFragmentRenderer; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Templating\Exception\InvalidResponseException; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; -use eZ\Publish\Core\MVC\Symfony\View\Renderer\TemplateRenderer; +namespace Ibexa\Tests\Bundle\Core\Fragment; + +use Ibexa\Bundle\Core\EventListener\ViewControllerListener; +use Ibexa\Bundle\Core\Fragment\DirectFragmentRenderer; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\Templating\Exception\InvalidResponseException; +use Ibexa\Core\MVC\Symfony\View\ContentView; +use Ibexa\Core\MVC\Symfony\View\Renderer\TemplateRenderer; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -160,7 +160,7 @@ private function getControllerResolverInterfaceMock(): ControllerResolverInterfa } /** - * @return \eZ\Publish\Core\MVC\Symfony\View\Renderer\TemplateRenderer|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\MVC\Symfony\View\Renderer\TemplateRenderer|\PHPUnit\Framework\MockObject\MockObject */ private function getTemplateRendererMock(): TemplateRenderer { @@ -181,3 +181,5 @@ private function getDirectFragmentRenderer( ); } } + +class_alias(DirectFragmentRendererTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Fragment\DirectFragmentRendererTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/FragmentListenerFactoryTest.php b/tests/bundle/Core/Fragment/FragmentListenerFactoryTest.php similarity index 91% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/FragmentListenerFactoryTest.php rename to tests/bundle/Core/Fragment/FragmentListenerFactoryTest.php index 884fef9ac3..b9c9f276e9 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/FragmentListenerFactoryTest.php +++ b/tests/bundle/Core/Fragment/FragmentListenerFactoryTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Fragment; +namespace Ibexa\Tests\Bundle\Core\Fragment; -use eZ\Bundle\EzPublishCoreBundle\Fragment\FragmentListenerFactory; +use Ibexa\Bundle\Core\Fragment\FragmentListenerFactory; use PHPUnit\Framework\TestCase; use ReflectionObject; use Symfony\Component\HttpFoundation\Request; @@ -69,3 +69,5 @@ public function testBuildFragmentListenerNoRequest() $this->assertNull($listener); } } + +class_alias(FragmentListenerFactoryTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Fragment\FragmentListenerFactoryTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/FragmentRendererBaseTest.php b/tests/bundle/Core/Fragment/FragmentRendererBaseTest.php similarity index 91% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/FragmentRendererBaseTest.php rename to tests/bundle/Core/Fragment/FragmentRendererBaseTest.php index 3b309c7b4f..c62c68b6df 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/FragmentRendererBaseTest.php +++ b/tests/bundle/Core/Fragment/FragmentRendererBaseTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Fragment; +namespace Ibexa\Tests\Bundle\Core\Fragment; -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\SerializerTrait; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\Component\Serializer\SerializerTrait; +use Ibexa\Core\MVC\Symfony\SiteAccess; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ControllerReference; @@ -75,3 +75,5 @@ abstract public function getRequest(SiteAccess $siteAccess): Request; abstract public function getRenderer(): FragmentRendererInterface; } + +class_alias(FragmentRendererBaseTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Fragment\FragmentRendererBaseTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/InlineFragmentRendererTest.php b/tests/bundle/Core/Fragment/InlineFragmentRendererTest.php similarity index 93% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/InlineFragmentRendererTest.php rename to tests/bundle/Core/Fragment/InlineFragmentRendererTest.php index 43b754bfcd..461504678f 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Fragment/InlineFragmentRendererTest.php +++ b/tests/bundle/Core/Fragment/InlineFragmentRendererTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Fragment; +namespace Ibexa\Tests\Bundle\Core\Fragment; -use eZ\Bundle\EzPublishCoreBundle\Fragment\InlineFragmentRenderer; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Bundle\Core\Fragment\InlineFragmentRenderer; +use Ibexa\Core\MVC\Symfony\SiteAccess; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; @@ -83,3 +83,5 @@ public function getRenderer(): FragmentRendererInterface return new InlineFragmentRenderer($this->innerRenderer); } } + +class_alias(InlineFragmentRendererTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Fragment\InlineFragmentRendererTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/AliasCleanerTest.php b/tests/bundle/Core/Imagine/AliasCleanerTest.php similarity index 79% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/AliasCleanerTest.php rename to tests/bundle/Core/Imagine/AliasCleanerTest.php index 6ebf53b551..87f33fc186 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/AliasCleanerTest.php +++ b/tests/bundle/Core/Imagine/AliasCleanerTest.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine; +namespace Ibexa\Tests\Bundle\Core\Imagine; -use eZ\Bundle\EzPublishCoreBundle\Imagine\AliasCleaner; +use Ibexa\Bundle\Core\Imagine\AliasCleaner; use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface; use PHPUnit\Framework\TestCase; class AliasCleanerTest extends TestCase { - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\AliasCleaner */ + /** @var \Ibexa\Bundle\Core\Imagine\AliasCleaner */ private $aliasCleaner; /** @var \PHPUnit\Framework\MockObject\MockObject */ @@ -36,3 +36,5 @@ public function testRemoveAliases() $this->aliasCleaner->removeAliases($originalPath); } } + +class_alias(AliasCleanerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\AliasCleanerTest'); diff --git a/tests/bundle/Core/Imagine/AliasGeneratorTest.php b/tests/bundle/Core/Imagine/AliasGeneratorTest.php new file mode 100644 index 0000000000..4b7e278ade --- /dev/null +++ b/tests/bundle/Core/Imagine/AliasGeneratorTest.php @@ -0,0 +1,499 @@ +dataLoader = $this->createMock(LoaderInterface::class); + $this->filterManager = $this + ->getMockBuilder(FilterManager::class) + ->disableOriginalConstructor() + ->getMock(); + $this->ioResolver = $this->createMock(ResolverInterface::class); + $this->filterConfiguration = new FilterConfiguration(); + $this->logger = $this->createMock(LoggerInterface::class); + $this->imagine = $this->createMock(ImagineInterface::class); + $this->box = $this->createMock(BoxInterface::class); + $this->image = $this->createMock(ImageInterface::class); + $this->ioService = $this->createMock(IOServiceInterface::class); + $this->variationPathGenerator = $this->createMock(VariationPathGenerator::class); + $this->aliasGenerator = new AliasGenerator( + $this->dataLoader, + $this->filterManager, + $this->ioResolver, + $this->filterConfiguration, + $this->logger + ); + $this->decoratedAliasGenerator = new ImagineAwareAliasGenerator( + $this->aliasGenerator, + $this->variationPathGenerator, + $this->ioService, + $this->imagine + ); + } + + /** + * @dataProvider supportsValueProvider + * + * @param \Ibexa\Contracts\Core\FieldType\Value $value + * @param bool $isSupported + */ + public function testSupportsValue($value, $isSupported) + { + $this->assertSame($isSupported, $this->aliasGenerator->supportsValue($value)); + } + + /** + * Data provider for testSupportsValue. + * + * @see testSupportsValue + * + * @return array + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function supportsValueProvider() + { + return [ + [$this->createMock(FieldTypeValue::class), false], + [new TextLineValue(), false], + [new ImageValue(), true], + [$this->createMock(ImageValue::class), true], + ]; + } + + public function testGetVariationWrongValue() + { + $this->expectException(\InvalidArgumentException::class); + + $field = new Field(['value' => $this->createMock(FieldTypeValue::class)]); + $this->aliasGenerator->getVariation($field, new VersionInfo(), 'foo'); + } + + /** + * Test obtaining Image Variation that hasn't been stored yet. + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType + */ + public function testGetVariationNotStored() + { + $originalPath = 'foo/bar/image.jpg'; + $variationName = 'my_variation'; + $this->filterConfiguration->set($variationName, []); + $imageId = '123-45'; + $imageWidth = 300; + $imageHeight = 300; + $expectedUrl = "http://localhost/foo/bar/image_$variationName.jpg"; + + $this->ioResolver + ->expects($this->once()) + ->method('isStored') + ->with($originalPath, $variationName) + ->will($this->returnValue(false)); + + $this->logger + ->expects($this->once()) + ->method('debug'); + + $binary = $this->createMock(BinaryInterface::class); + $this->dataLoader + ->expects($this->once()) + ->method('find') + ->with($originalPath) + ->will($this->returnValue($binary)); + $this->filterManager + ->expects($this->once()) + ->method('applyFilter') + ->with($binary, $variationName) + ->will($this->returnValue($binary)); + $this->ioResolver + ->expects($this->once()) + ->method('store') + ->with($binary, $originalPath, $variationName); + + $this->assertImageVariationIsCorrect( + $expectedUrl, + $variationName, + $imageId, + $originalPath, + $imageWidth, + $imageHeight + ); + } + + public function testGetVariationOriginal() + { + $originalPath = 'foo/bar/image.jpg'; + $variationName = 'original'; + $imageId = '123-45'; + $imageWidth = 300; + $imageHeight = 300; + // original images already contain proper width and height + $imageValue = new ImageValue( + [ + 'id' => $originalPath, + 'imageId' => $imageId, + 'width' => $imageWidth, + 'height' => $imageHeight, + ] + ); + $field = new Field(['value' => $imageValue]); + $expectedUrl = 'http://localhost/foo/bar/image.jpg'; + + $this->ioResolver + ->expects($this->never()) + ->method('isStored') + ->with($originalPath, $variationName) + ->will($this->returnValue(false)); + + $this->logger + ->expects($this->once()) + ->method('debug'); + + $this->ioResolver + ->expects($this->once()) + ->method('resolve') + ->with($originalPath, $variationName) + ->will($this->returnValue($expectedUrl)); + + $expected = new ImageVariation( + [ + 'name' => $variationName, + 'fileName' => 'image.jpg', + 'dirPath' => 'http://localhost/foo/bar', + 'uri' => $expectedUrl, + 'imageId' => $imageId, + 'height' => $imageHeight, + 'width' => $imageWidth, + ] + ); + $this->assertEquals($expected, $this->decoratedAliasGenerator->getVariation($field, new VersionInfo(), $variationName)); + } + + /** + * Test obtaining Image Variation that hasn't been stored yet and has multiple references. + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType + */ + public function testGetVariationNotStoredHavingReferences() + { + $originalPath = 'foo/bar/image.jpg'; + $variationName = 'my_variation'; + $reference1 = 'reference1'; + $reference2 = 'reference2'; + $configVariation = ['reference' => $reference1]; + $configReference1 = ['reference' => $reference2]; + $configReference2 = []; + $this->filterConfiguration->set($variationName, $configVariation); + $this->filterConfiguration->set($reference1, $configReference1); + $this->filterConfiguration->set($reference2, $configReference2); + $imageId = '123-45'; + $imageWidth = 300; + $imageHeight = 300; + $expectedUrl = "http://localhost/foo/bar/image_$variationName.jpg"; + + $this->ioResolver + ->expects($this->once()) + ->method('isStored') + ->with($originalPath, $variationName) + ->will($this->returnValue(false)); + + $this->logger + ->expects($this->once()) + ->method('debug'); + + $binary = $this->createMock(BinaryInterface::class); + $this->dataLoader + ->expects($this->once()) + ->method('find') + ->with($originalPath) + ->will($this->returnValue($binary)); + + // Filter manager is supposed to be called 3 times to generate references, and then passed variation. + $this->filterManager + ->expects($this->at(0)) + ->method('applyFilter') + ->with($binary, $reference2) + ->will($this->returnValue($binary)); + $this->filterManager + ->expects($this->at(1)) + ->method('applyFilter') + ->with($binary, $reference1) + ->will($this->returnValue($binary)); + $this->filterManager + ->expects($this->at(2)) + ->method('applyFilter') + ->with($binary, $variationName) + ->will($this->returnValue($binary)); + + $this->ioResolver + ->expects($this->once()) + ->method('store') + ->with($binary, $originalPath, $variationName); + + $this->assertImageVariationIsCorrect( + $expectedUrl, + $variationName, + $imageId, + $originalPath, + $imageWidth, + $imageHeight + ); + } + + /** + * Test obtaining Image Variation that has been stored already. + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType + */ + public function testGetVariationAlreadyStored() + { + $originalPath = 'foo/bar/image.jpg'; + $variationName = 'my_variation'; + $imageId = '123-45'; + $imageWidth = 300; + $imageHeight = 300; + $expectedUrl = "http://localhost/foo/bar/image_$variationName.jpg"; + + $this->ioResolver + ->expects($this->once()) + ->method('isStored') + ->with($originalPath, $variationName) + ->will($this->returnValue(true)); + + $this->logger + ->expects($this->once()) + ->method('debug'); + + $this->dataLoader + ->expects($this->never()) + ->method('find'); + $this->filterManager + ->expects($this->never()) + ->method('applyFilter'); + $this->ioResolver + ->expects($this->never()) + ->method('store'); + + $this->assertImageVariationIsCorrect( + $expectedUrl, + $variationName, + $imageId, + $originalPath, + $imageWidth, + $imageHeight + ); + } + + public function testGetVariationOriginalNotFound() + { + $this->expectException(SourceImageNotFoundException::class); + + $this->dataLoader + ->expects($this->once()) + ->method('find') + ->will($this->throwException(new NotLoadableException())); + + $field = new Field(['value' => new ImageValue()]); + $this->aliasGenerator->getVariation($field, new VersionInfo(), 'foo'); + } + + public function testGetVariationInvalidVariation() + { + $this->expectException(InvalidVariationException::class); + + $originalPath = 'foo/bar/image.jpg'; + $variationName = 'my_variation'; + $imageId = '123-45'; + $imageValue = new ImageValue(['id' => $originalPath, 'imageId' => $imageId]); + $field = new Field(['value' => $imageValue]); + + $this->ioResolver + ->expects($this->once()) + ->method('isStored') + ->with($originalPath, $variationName) + ->will($this->returnValue(true)); + + $this->logger + ->expects($this->once()) + ->method('debug'); + + $this->dataLoader + ->expects($this->never()) + ->method('find'); + $this->filterManager + ->expects($this->never()) + ->method('applyFilter'); + $this->ioResolver + ->expects($this->never()) + ->method('store'); + + $this->ioResolver + ->expects($this->once()) + ->method('resolve') + ->with($originalPath, $variationName) + ->will($this->throwException(new NotResolvableException())); + + $this->aliasGenerator->getVariation($field, new VersionInfo(), $variationName); + } + + /** + * Prepare required Imagine-related mocks and assert that the Image Variation is as expected. + * + * @param string $expectedUrl + * @param string $variationName + * @param string $imageId + * @param string $originalPath + * @param int $imageWidth + * @param int $imageHeight + * + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentType + */ + protected function assertImageVariationIsCorrect( + $expectedUrl, + $variationName, + $imageId, + $originalPath, + $imageWidth, + $imageHeight + ) { + $imageValue = new ImageValue(['id' => $originalPath, 'imageId' => $imageId]); + $field = new Field(['value' => $imageValue]); + + $binaryFile = new BinaryFile( + [ + 'uri' => "_aliases/{$variationName}/foo/bar/image.jpg", + ] + ); + + $this->ioResolver + ->expects($this->once()) + ->method('resolve') + ->with($originalPath, $variationName) + ->will($this->returnValue($expectedUrl)); + + $this->variationPathGenerator + ->expects($this->once()) + ->method('getVariationPath') + ->with($originalPath, $variationName) + ->willReturn($binaryFile->uri); + + $this->ioService + ->expects($this->once()) + ->method('loadBinaryFile') + ->withAnyParameters() + ->willReturn($binaryFile); + + $this->ioService + ->expects($this->once()) + ->method('getFileContents') + ->with($binaryFile) + ->willReturn('file contents mock'); + + $this->imagine + ->expects($this->once()) + ->method('load') + ->with('file contents mock') + ->will($this->returnValue($this->image)); + $this->image + ->expects($this->once()) + ->method('getSize') + ->will($this->returnValue($this->box)); + + $this->box + ->expects($this->once()) + ->method('getWidth') + ->will($this->returnValue($imageWidth)); + $this->box + ->expects($this->once()) + ->method('getHeight') + ->will($this->returnValue($imageHeight)); + + $expected = new ImageVariation( + [ + 'name' => $variationName, + 'fileName' => "image_$variationName.jpg", + 'dirPath' => 'http://localhost/foo/bar', + 'uri' => $expectedUrl, + 'imageId' => $imageId, + 'height' => $imageHeight, + 'width' => $imageWidth, + ] + ); + $this->assertEquals( + $expected, + $this->decoratedAliasGenerator->getVariation($field, new VersionInfo(), $variationName) + ); + } +} + +class_alias(AliasGeneratorTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\AliasGeneratorTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/BinaryLoaderTest.php b/tests/bundle/Core/Imagine/BinaryLoaderTest.php similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/BinaryLoaderTest.php rename to tests/bundle/Core/Imagine/BinaryLoaderTest.php index 49a3a935a2..61feb41f99 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/BinaryLoaderTest.php +++ b/tests/bundle/Core/Imagine/BinaryLoaderTest.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine; - -use eZ\Bundle\EzPublishCoreBundle\Imagine\BinaryLoader; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\IO\Exception\InvalidBinaryFileIdException; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\IO\Values\MissingBinaryFile; +namespace Ibexa\Tests\Bundle\Core\Imagine; + +use Ibexa\Bundle\Core\Imagine\BinaryLoader; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\IO\Exception\InvalidBinaryFileIdException; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\BinaryFile; +use Ibexa\Core\IO\Values\MissingBinaryFile; use Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException; use Liip\ImagineBundle\Model\Binary; use PHPUnit\Framework\TestCase; @@ -22,7 +22,7 @@ class BinaryLoaderTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $ioService; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\BinaryLoader */ + /** @var \Ibexa\Bundle\Core\Imagine\BinaryLoader */ private $binaryLoader; protected function setUp(): void @@ -104,3 +104,5 @@ public function testFind(): void self::assertEquals($expected, $this->binaryLoader->find($path)); } } + +class_alias(BinaryLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\BinaryLoaderTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Cache/Resolver/ProxyResolverTest.php b/tests/bundle/Core/Imagine/Cache/Resolver/ProxyResolverTest.php similarity index 75% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Cache/Resolver/ProxyResolverTest.php rename to tests/bundle/Core/Imagine/Cache/Resolver/ProxyResolverTest.php index 57fccfb714..8eb840485a 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Cache/Resolver/ProxyResolverTest.php +++ b/tests/bundle/Core/Imagine/Cache/Resolver/ProxyResolverTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Cache\Resolver; +namespace Ibexa\Tests\Bundle\Core\Imagine\Cache\Resolver; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\Resolver\ProxyResolver; +use Ibexa\Bundle\Core\Imagine\Cache\Resolver\ProxyResolver; use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface; use PHPUnit\Framework\TestCase; @@ -34,7 +34,7 @@ public function testResolveUsingProxyHostWithTrailingSlash() $hosts = ['http://ezplatform.com/']; $proxyResolver = new ProxyResolver($this->resolver, $hosts); - $resolvedPath = 'http://ez.no/var/site/storage/images/_aliases/medium/7/4/2/0/247-1-eng-GB/img_0885.jpg'; + $resolvedPath = 'http://ibexa.co/var/site/storage/images/_aliases/medium/7/4/2/0/247-1-eng-GB/img_0885.jpg'; $this->resolver ->expects($this->once()) @@ -49,10 +49,10 @@ public function testResolveUsingProxyHostWithTrailingSlash() public function testResolveAndRemovePortUsingProxyHost() { - $hosts = ['http://ez.no']; + $hosts = ['http://ibexa.co']; $proxyResolver = new ProxyResolver($this->resolver, $hosts); - $resolvedPath = 'http://ez.no:8060/var/site/storage/images/_aliases/medium/7/4/2/0/247-1-eng-GB/img_0885.jpg'; + $resolvedPath = 'http://ibexa.co:8060/var/site/storage/images/_aliases/medium/7/4/2/0/247-1-eng-GB/img_0885.jpg'; $this->resolver ->expects($this->once()) @@ -60,14 +60,14 @@ public function testResolveAndRemovePortUsingProxyHost() ->with($this->path, $this->filter) ->willReturn($resolvedPath); - $expected = 'http://ez.no/var/site/storage/images/_aliases/medium/7/4/2/0/247-1-eng-GB/img_0885.jpg'; + $expected = 'http://ibexa.co/var/site/storage/images/_aliases/medium/7/4/2/0/247-1-eng-GB/img_0885.jpg'; $this->assertEquals($expected, $proxyResolver->resolve($this->path, $this->filter)); } public function testResolveAndRemovePortUsingProxyHostWithTrailingSlash() { - $hosts = ['http://ez.no']; + $hosts = ['http://ibexa.co']; $proxyResolver = new ProxyResolver($this->resolver, $hosts); $resolvedPath = 'http://ezplatform.com:8080/var/site/storage/images/_aliases/medium/7/4/2/0/247-1-eng-GB/img_0885.jpg'; @@ -78,8 +78,10 @@ public function testResolveAndRemovePortUsingProxyHostWithTrailingSlash() ->with($this->path, $this->filter) ->willReturn($resolvedPath); - $expected = 'http://ez.no/var/site/storage/images/_aliases/medium/7/4/2/0/247-1-eng-GB/img_0885.jpg'; + $expected = 'http://ibexa.co/var/site/storage/images/_aliases/medium/7/4/2/0/247-1-eng-GB/img_0885.jpg'; $this->assertEquals($expected, $proxyResolver->resolve($this->path, $this->filter)); } } + +class_alias(ProxyResolverTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Cache\Resolver\ProxyResolverTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Cache/Resolver/RelativeResolverTest.php b/tests/bundle/Core/Imagine/Cache/Resolver/RelativeResolverTest.php similarity index 76% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Cache/Resolver/RelativeResolverTest.php rename to tests/bundle/Core/Imagine/Cache/Resolver/RelativeResolverTest.php index 930e0b7d8c..daeb67a9c6 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Cache/Resolver/RelativeResolverTest.php +++ b/tests/bundle/Core/Imagine/Cache/Resolver/RelativeResolverTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Cache\Resolver; +namespace Ibexa\Tests\Bundle\Core\Imagine\Cache\Resolver; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\Resolver\RelativeResolver; +use Ibexa\Bundle\Core\Imagine\Cache\Resolver\RelativeResolver; use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface; use PHPUnit\Framework\TestCase; @@ -28,7 +28,7 @@ public function testResolve() $path = '7/4/2/0/247-1-eng-GB/test.png'; $filter = 'big'; - $absolute = 'http://ez.no/var/site/storage/images/_aliases/big/7/4/2/0/247-1-eng-GB/test.png'; + $absolute = 'http://ibexa.co/var/site/storage/images/_aliases/big/7/4/2/0/247-1-eng-GB/test.png'; $expected = '/var/site/storage/images/_aliases/big/7/4/2/0/247-1-eng-GB/test.png'; $this->liipResolver @@ -40,3 +40,5 @@ public function testResolve() $this->assertSame($expected, $resolver->resolve($path, $filter)); } } + +class_alias(RelativeResolverTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Cache\Resolver\RelativeResolverTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Cache/ResolverFactoryTest.php b/tests/bundle/Core/Imagine/Cache/ResolverFactoryTest.php similarity index 79% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Cache/ResolverFactoryTest.php rename to tests/bundle/Core/Imagine/Cache/ResolverFactoryTest.php index c295f84374..be4b5b7529 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Cache/ResolverFactoryTest.php +++ b/tests/bundle/Core/Imagine/Cache/ResolverFactoryTest.php @@ -4,24 +4,24 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Cache; +namespace Ibexa\Tests\Bundle\Core\Imagine\Cache; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\Resolver\RelativeResolver; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\ResolverFactory; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Bundle\Core\Imagine\Cache\Resolver\RelativeResolver; +use Ibexa\Bundle\Core\Imagine\Cache\ResolverFactory; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use Liip\ImagineBundle\Imagine\Cache\Resolver\ProxyResolver; use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface; use PHPUnit\Framework\TestCase; class ResolverFactoryTest extends TestCase { - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; /** @var \PHPUnit\Framework\MockObject\MockObject|\Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface */ private $resolver; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\ResolverFactory */ + /** @var \Ibexa\Bundle\Core\Imagine\Cache\ResolverFactory */ private $factory; protected function setUp(): void @@ -45,7 +45,7 @@ public function testCreateProxyCacheResolver() ->with('image_host') ->willReturn(true); - $host = 'http://ez.no'; + $host = 'http://ibexa.co'; $this->configResolver ->expects($this->at(1)) @@ -79,3 +79,5 @@ public function testCreateRelativeCacheResolver() $this->assertEquals($expected, $this->factory->createCacheResolver()); } } + +class_alias(ResolverFactoryTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Cache\ResolverFactoryTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/AbstractFilterTest.php b/tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php similarity index 89% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/AbstractFilterTest.php rename to tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php index c6063a3ac0..e9bef8727f 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/AbstractFilterTest.php +++ b/tests/bundle/Core/Imagine/Filter/AbstractFilterTest.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter; +namespace Ibexa\Tests\Bundle\Core\Imagine\Filter; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\AbstractFilter; +use Ibexa\Bundle\Core\Imagine\Filter\AbstractFilter; use PHPUnit\Framework\TestCase; class AbstractFilterTest extends TestCase { - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\AbstractFilter */ + /** @var \Ibexa\Bundle\Core\Imagine\Filter\AbstractFilter */ protected $filter; protected function setUp(): void @@ -81,3 +81,5 @@ public function getSetOptionWithDefaulValueProvider() ]; } } + +class_alias(AbstractFilterTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\AbstractFilterTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/FilterConfigurationTest.php b/tests/bundle/Core/Imagine/Filter/FilterConfigurationTest.php similarity index 86% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/FilterConfigurationTest.php rename to tests/bundle/Core/Imagine/Filter/FilterConfigurationTest.php index c1e923d059..9b4f3e0b6f 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/FilterConfigurationTest.php +++ b/tests/bundle/Core/Imagine/Filter/FilterConfigurationTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter; +namespace Ibexa\Tests\Bundle\Core\Imagine\Filter; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\FilterConfiguration; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Bundle\Core\Imagine\Filter\FilterConfiguration; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use PHPUnit\Framework\TestCase; class FilterConfigurationTest extends TestCase @@ -15,7 +15,7 @@ class FilterConfigurationTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $configResolver; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\FilterConfiguration */ + /** @var \Ibexa\Bundle\Core\Imagine\Filter\FilterConfiguration */ private $filterConfiguration; protected function setUp(): void @@ -61,17 +61,15 @@ public function testGetNoEzVariationInvalidImagineFilter() $this->filterConfiguration->get('foobar'); } - public function testGetWithEzVariationInvalidFilters() + public function testGetWithEzVariationNullConfiguration(): void { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidVariationException::class); - $fooConfig = ['fooconfig']; $barConfig = ['barconfig']; $this->filterConfiguration->set('foo', $fooConfig); $this->filterConfiguration->set('bar', $barConfig); $variations = [ - 'some_variation' => [], + 'some_variation' => null, ]; $this->configResolver ->expects($this->once()) @@ -79,7 +77,16 @@ public function testGetWithEzVariationInvalidFilters() ->with('image_variations') ->will($this->returnValue($variations)); - $this->filterConfiguration->get('some_variation'); + self::assertSame( + [ + 'cache' => 'ibexa', + 'data_loader' => 'ibexa', + 'reference' => null, + 'filters' => [], + 'post_processors' => [], + ], + $this->filterConfiguration->get('some_variation') + ); } public function testGetEzVariationNoReference() @@ -101,8 +108,8 @@ public function testGetEzVariationNoReference() $this->assertSame( [ - 'cache' => 'ezpublish', - 'data_loader' => 'ezpublish', + 'cache' => 'ibexa', + 'data_loader' => 'ibexa', 'reference' => null, 'filters' => $filters, 'post_processors' => [], @@ -131,8 +138,8 @@ public function testGetEzVariationWithReference() $this->assertSame( [ - 'cache' => 'ezpublish', - 'data_loader' => 'ezpublish', + 'cache' => 'ibexa', + 'data_loader' => 'ibexa', 'reference' => $reference, 'filters' => $filters, 'post_processors' => [], @@ -159,8 +166,8 @@ public function testGetEzVariationImagineFilters() $this->assertSame( [ - 'cache' => 'ezpublish', - 'data_loader' => 'ezpublish', + 'cache' => 'ibexa', + 'data_loader' => 'ibexa', 'reference' => $reference, 'filters' => $filters, 'post_processors' => [], @@ -190,8 +197,8 @@ public function testGetEzVariationImagineOptions() $this->assertSame( [ - 'cache' => 'ezpublish', - 'data_loader' => 'ezpublish', + 'cache' => 'ibexa', + 'data_loader' => 'ibexa', 'reference' => $reference, 'filters' => $filters, 'post_processors' => [], @@ -230,3 +237,5 @@ public function testAll() ); } } + +class_alias(FilterConfigurationTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\FilterConfigurationTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/BorderFilterLoaderTest.php b/tests/bundle/Core/Imagine/Filter/Loader/BorderFilterLoaderTest.php similarity index 91% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/BorderFilterLoaderTest.php rename to tests/bundle/Core/Imagine/Filter/Loader/BorderFilterLoaderTest.php index 944678db38..2c0e5c33cf 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/BorderFilterLoaderTest.php +++ b/tests/bundle/Core/Imagine/Filter/Loader/BorderFilterLoaderTest.php @@ -4,10 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader; +namespace Ibexa\Tests\Bundle\Core\Imagine\Filter\Loader; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\BorderFilterLoader; +use Ibexa\Bundle\Core\Imagine\Filter\Loader\BorderFilterLoader; use Imagine\Draw\DrawerInterface; +use Imagine\Exception\InvalidArgumentException; use Imagine\Image\BoxInterface; use Imagine\Image\ImageInterface; use Imagine\Image\Palette\Color\ColorInterface; @@ -21,7 +22,7 @@ class BorderFilterLoaderTest extends TestCase */ public function testLoadInvalidOptions(array $options) { - $this->expectException(\Imagine\Exception\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $loader = new BorderFilterLoader(); $loader->load($this->createMock(ImageInterface::class), $options); @@ -136,3 +137,5 @@ public function loadProvider() ]; } } + +class_alias(BorderFilterLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader\BorderFilterLoaderTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/CropFilterLoaderTest.php b/tests/bundle/Core/Imagine/Filter/Loader/CropFilterLoaderTest.php similarity index 79% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/CropFilterLoaderTest.php rename to tests/bundle/Core/Imagine/Filter/Loader/CropFilterLoaderTest.php index 731dcf8e77..304560dbb1 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/CropFilterLoaderTest.php +++ b/tests/bundle/Core/Imagine/Filter/Loader/CropFilterLoaderTest.php @@ -4,9 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader; +namespace Ibexa\Tests\Bundle\Core\Imagine\Filter\Loader; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\CropFilterLoader; +use Ibexa\Bundle\Core\Imagine\Filter\Loader\CropFilterLoader; +use Imagine\Exception\InvalidArgumentException; use Imagine\Image\ImageInterface; use Liip\ImagineBundle\Imagine\Filter\Loader\LoaderInterface; use PHPUnit\Framework\TestCase; @@ -16,7 +17,7 @@ class CropFilterLoaderTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $innerLoader; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\CropFilterLoader */ + /** @var \Ibexa\Bundle\Core\Imagine\Filter\Loader\CropFilterLoader */ private $loader; protected function setUp(): void @@ -32,7 +33,7 @@ protected function setUp(): void */ public function testLoadInvalidOptions(array $options) { - $this->expectException(\Imagine\Exception\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $this->loader->load($this->createMock(ImageInterface::class), $options); } @@ -65,3 +66,5 @@ public function testLoad() $this->assertSame($image, $this->loader->load($image, [$width, $height, $offsetX, $offsetY])); } } + +class_alias(CropFilterLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader\CropFilterLoaderTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/GrayscaleFilterLoaderTest.php b/tests/bundle/Core/Imagine/Filter/Loader/GrayscaleFilterLoaderTest.php similarity index 77% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/GrayscaleFilterLoaderTest.php rename to tests/bundle/Core/Imagine/Filter/Loader/GrayscaleFilterLoaderTest.php index a5cedd10d0..5dbb38fe79 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/GrayscaleFilterLoaderTest.php +++ b/tests/bundle/Core/Imagine/Filter/Loader/GrayscaleFilterLoaderTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader; +namespace Ibexa\Tests\Bundle\Core\Imagine\Filter\Loader; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\GrayscaleFilterLoader; +use Ibexa\Bundle\Core\Imagine\Filter\Loader\GrayscaleFilterLoader; use Imagine\Effects\EffectsInterface; use Imagine\Image\ImageInterface; use PHPUnit\Framework\TestCase; @@ -30,3 +30,5 @@ public function testLoad() $this->assertSame($image, $loader->load($image)); } } + +class_alias(GrayscaleFilterLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader\GrayscaleFilterLoaderTest'); diff --git a/tests/bundle/Core/Imagine/Filter/Loader/ReduceNoiseFilterLoaderTest.php b/tests/bundle/Core/Imagine/Filter/Loader/ReduceNoiseFilterLoaderTest.php new file mode 100644 index 0000000000..4a12e39d21 --- /dev/null +++ b/tests/bundle/Core/Imagine/Filter/Loader/ReduceNoiseFilterLoaderTest.php @@ -0,0 +1,38 @@ +filter = $this->createMock(FilterInterface::class); + $this->loader = new ReduceNoiseFilterLoader($this->filter); + } + + public function testLoadInvalidDriver() + { + $this->expectException(NotSupportedException::class); + + $this->loader->load($this->createMock(ImageInterface::class)); + } +} + +class_alias(ReduceNoiseFilterLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader\ReduceNoiseFilterLoaderTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleDownOnlyFilterLoaderTest.php b/tests/bundle/Core/Imagine/Filter/Loader/ScaleDownOnlyFilterLoaderTest.php similarity index 76% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleDownOnlyFilterLoaderTest.php rename to tests/bundle/Core/Imagine/Filter/Loader/ScaleDownOnlyFilterLoaderTest.php index ecfd68d01a..e831ea28a8 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleDownOnlyFilterLoaderTest.php +++ b/tests/bundle/Core/Imagine/Filter/Loader/ScaleDownOnlyFilterLoaderTest.php @@ -4,9 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader; +namespace Ibexa\Tests\Bundle\Core\Imagine\Filter\Loader; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleDownOnlyFilterLoader; +use Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleDownOnlyFilterLoader; +use Imagine\Exception\InvalidArgumentException; use Imagine\Image\ImageInterface; use Liip\ImagineBundle\Imagine\Filter\Loader\LoaderInterface; use PHPUnit\Framework\TestCase; @@ -16,7 +17,7 @@ class ScaleDownOnlyFilterLoaderTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $innerLoader; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleDownOnlyFilterLoader */ + /** @var \Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleDownOnlyFilterLoader */ private $loader; protected function setUp(): void @@ -32,7 +33,7 @@ protected function setUp(): void */ public function testLoadInvalidOptions(array $options) { - $this->expectException(\Imagine\Exception\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $this->loader->load($this->createMock(ImageInterface::class), $options); } @@ -59,3 +60,5 @@ public function testLoad() $this->assertSame($image, $this->loader->load($image, $options)); } } + +class_alias(ScaleDownOnlyFilterLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader\ScaleDownOnlyFilterLoaderTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleExactFilterLoaderTest.php b/tests/bundle/Core/Imagine/Filter/Loader/ScaleExactFilterLoaderTest.php similarity index 76% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleExactFilterLoaderTest.php rename to tests/bundle/Core/Imagine/Filter/Loader/ScaleExactFilterLoaderTest.php index 794c946154..c0b197c55d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleExactFilterLoaderTest.php +++ b/tests/bundle/Core/Imagine/Filter/Loader/ScaleExactFilterLoaderTest.php @@ -4,9 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader; +namespace Ibexa\Tests\Bundle\Core\Imagine\Filter\Loader; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleExactFilterLoader; +use Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleExactFilterLoader; +use Imagine\Exception\InvalidArgumentException; use Imagine\Image\ImageInterface; use Liip\ImagineBundle\Imagine\Filter\Loader\LoaderInterface; use PHPUnit\Framework\TestCase; @@ -16,7 +17,7 @@ class ScaleExactFilterLoaderTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $innerLoader; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleExactFilterLoader */ + /** @var \Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleExactFilterLoader */ private $loader; protected function setUp(): void @@ -32,7 +33,7 @@ protected function setUp(): void */ public function testLoadInvalidOptions(array $options) { - $this->expectException(\Imagine\Exception\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $this->loader->load($this->createMock(ImageInterface::class), $options); } @@ -59,3 +60,5 @@ public function testLoad() $this->assertSame($image, $this->loader->load($image, $options)); } } + +class_alias(ScaleExactFilterLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader\ScaleExactFilterLoaderTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleFilterLoaderTest.php b/tests/bundle/Core/Imagine/Filter/Loader/ScaleFilterLoaderTest.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleFilterLoaderTest.php rename to tests/bundle/Core/Imagine/Filter/Loader/ScaleFilterLoaderTest.php index 61d56163ef..2e1b0565d1 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScaleFilterLoaderTest.php +++ b/tests/bundle/Core/Imagine/Filter/Loader/ScaleFilterLoaderTest.php @@ -4,9 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader; +namespace Ibexa\Tests\Bundle\Core\Imagine\Filter\Loader; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleFilterLoader; +use Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleFilterLoader; +use Imagine\Exception\InvalidArgumentException; use Imagine\Image\Box; use Imagine\Image\ImageInterface; use Liip\ImagineBundle\Imagine\Filter\Loader\LoaderInterface; @@ -17,7 +18,7 @@ class ScaleFilterLoaderTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $innerLoader; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScaleFilterLoader */ + /** @var \Ibexa\Bundle\Core\Imagine\Filter\Loader\ScaleFilterLoader */ private $loader; protected function setUp(): void @@ -33,7 +34,7 @@ protected function setUp(): void */ public function testLoadInvalidOptions(array $options) { - $this->expectException(\Imagine\Exception\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $this->loader->load($this->createMock(ImageInterface::class), $options); } @@ -93,3 +94,5 @@ public function testLoadWiden() $this->assertSame($image, $this->loader->load($image, [$width, $height])); } } + +class_alias(ScaleFilterLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader\ScaleFilterLoaderTest'); diff --git a/tests/bundle/Core/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoaderTest.php b/tests/bundle/Core/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoaderTest.php new file mode 100644 index 0000000000..1ef1db37cc --- /dev/null +++ b/tests/bundle/Core/Imagine/Filter/Loader/ScaleHeightDownOnlyFilterLoaderTest.php @@ -0,0 +1,52 @@ +innerLoader = $this->createMock(LoaderInterface::class); + $this->loader = new ScaleHeightDownOnlyFilterLoader(); + $this->loader->setInnerLoader($this->innerLoader); + } + + public function testLoadInvalid() + { + $this->expectException(InvalidArgumentException::class); + + $this->loader->load($this->createMock(ImageInterface::class), []); + } + + public function testLoad() + { + $height = 123; + $image = $this->createMock(ImageInterface::class); + $this->innerLoader + ->expects($this->once()) + ->method('load') + ->with($image, $this->equalTo(['size' => [null, $height], 'mode' => 'inset'])) + ->will($this->returnValue($image)); + + $this->assertSame($image, $this->loader->load($image, [$height])); + } +} + +class_alias(ScaleHeightDownOnlyFilterLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader\ScaleHeightDownOnlyFilterLoaderTest'); diff --git a/tests/bundle/Core/Imagine/Filter/Loader/ScaleHeightFilterLoaderTest.php b/tests/bundle/Core/Imagine/Filter/Loader/ScaleHeightFilterLoaderTest.php new file mode 100644 index 0000000000..eb5c7a1c79 --- /dev/null +++ b/tests/bundle/Core/Imagine/Filter/Loader/ScaleHeightFilterLoaderTest.php @@ -0,0 +1,52 @@ +innerLoader = $this->createMock(LoaderInterface::class); + $this->loader = new ScaleHeightFilterLoader(); + $this->loader->setInnerLoader($this->innerLoader); + } + + public function testLoadFail() + { + $this->expectException(InvalidArgumentException::class); + + $this->loader->load($this->createMock(ImageInterface::class, [])); + } + + public function testLoad() + { + $height = 123; + $image = $this->createMock(ImageInterface::class); + $this->innerLoader + ->expects($this->once()) + ->method('load') + ->with($image, $this->equalTo(['heighten' => $height])) + ->will($this->returnValue($image)); + + $this->assertSame($image, $this->loader->load($image, [$height])); + } +} + +class_alias(ScaleHeightFilterLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader\ScaleHeightFilterLoaderTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScalePercentFilterLoaderTest.php b/tests/bundle/Core/Imagine/Filter/Loader/ScalePercentFilterLoaderTest.php similarity index 80% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScalePercentFilterLoaderTest.php rename to tests/bundle/Core/Imagine/Filter/Loader/ScalePercentFilterLoaderTest.php index 7ae5cf7f29..386701ad92 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/ScalePercentFilterLoaderTest.php +++ b/tests/bundle/Core/Imagine/Filter/Loader/ScalePercentFilterLoaderTest.php @@ -4,9 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader; +namespace Ibexa\Tests\Bundle\Core\Imagine\Filter\Loader; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScalePercentFilterLoader; +use Ibexa\Bundle\Core\Imagine\Filter\Loader\ScalePercentFilterLoader; +use Imagine\Exception\InvalidArgumentException; use Imagine\Image\Box; use Imagine\Image\ImageInterface; use Liip\ImagineBundle\Imagine\Filter\Loader\LoaderInterface; @@ -17,7 +18,7 @@ class ScalePercentFilterLoaderTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $innerLoader; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\ScalePercentFilterLoader */ + /** @var \Ibexa\Bundle\Core\Imagine\Filter\Loader\ScalePercentFilterLoader */ private $loader; protected function setUp(): void @@ -33,7 +34,7 @@ protected function setUp(): void */ public function testLoadInvalidOptions(array $options) { - $this->expectException(\Imagine\Exception\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $this->loader->load($this->createMock(ImageInterface::class), $options); } @@ -72,3 +73,5 @@ public function testLoad() $this->assertSame($image, $this->loader->load($image, [$widthPercent, $heightPercent])); } } + +class_alias(ScalePercentFilterLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader\ScalePercentFilterLoaderTest'); diff --git a/tests/bundle/Core/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoaderTest.php b/tests/bundle/Core/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoaderTest.php new file mode 100644 index 0000000000..215d771d03 --- /dev/null +++ b/tests/bundle/Core/Imagine/Filter/Loader/ScaleWidthDownOnlyFilterLoaderTest.php @@ -0,0 +1,52 @@ +innerLoader = $this->createMock(LoaderInterface::class); + $this->loader = new ScaleWidthDownOnlyFilterLoader(); + $this->loader->setInnerLoader($this->innerLoader); + } + + public function testLoadInvalid() + { + $this->expectException(InvalidArgumentException::class); + + $this->loader->load($this->createMock(ImageInterface::class), []); + } + + public function testLoad() + { + $width = 123; + $image = $this->createMock(ImageInterface::class); + $this->innerLoader + ->expects($this->once()) + ->method('load') + ->with($image, $this->equalTo(['size' => [$width, null], 'mode' => 'inset'])) + ->will($this->returnValue($image)); + + $this->assertSame($image, $this->loader->load($image, [$width])); + } +} + +class_alias(ScaleWidthDownOnlyFilterLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader\ScaleWidthDownOnlyFilterLoaderTest'); diff --git a/tests/bundle/Core/Imagine/Filter/Loader/ScaleWidthFilterLoaderTest.php b/tests/bundle/Core/Imagine/Filter/Loader/ScaleWidthFilterLoaderTest.php new file mode 100644 index 0000000000..5cfd9a4bd6 --- /dev/null +++ b/tests/bundle/Core/Imagine/Filter/Loader/ScaleWidthFilterLoaderTest.php @@ -0,0 +1,52 @@ +innerLoader = $this->createMock(LoaderInterface::class); + $this->loader = new ScaleWidthFilterLoader(); + $this->loader->setInnerLoader($this->innerLoader); + } + + public function testLoadFail() + { + $this->expectException(InvalidArgumentException::class); + + $this->loader->load($this->createMock(ImageInterface::class, [])); + } + + public function testLoad() + { + $width = 123; + $image = $this->createMock(ImageInterface::class); + $this->innerLoader + ->expects($this->once()) + ->method('load') + ->with($image, $this->equalTo(['widen' => $width])) + ->will($this->returnValue($image)); + + $this->assertSame($image, $this->loader->load($image, [$width])); + } +} + +class_alias(ScaleWidthFilterLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader\ScaleWidthFilterLoaderTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/SwirlFilterLoaderTest.php b/tests/bundle/Core/Imagine/Filter/Loader/SwirlFilterLoaderTest.php similarity index 82% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/SwirlFilterLoaderTest.php rename to tests/bundle/Core/Imagine/Filter/Loader/SwirlFilterLoaderTest.php index f174a118e7..e750a7d828 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/Filter/Loader/SwirlFilterLoaderTest.php +++ b/tests/bundle/Core/Imagine/Filter/Loader/SwirlFilterLoaderTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader; +namespace Ibexa\Tests\Bundle\Core\Imagine\Filter\Loader; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\FilterInterface; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\SwirlFilterLoader; +use Ibexa\Bundle\Core\Imagine\Filter\FilterInterface; +use Ibexa\Bundle\Core\Imagine\Filter\Loader\SwirlFilterLoader; use Imagine\Image\ImageInterface; use PHPUnit\Framework\TestCase; @@ -16,7 +16,7 @@ class SwirlFilterLoaderTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $filter; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\Loader\SwirlFilterLoader */ + /** @var \Ibexa\Bundle\Core\Imagine\Filter\Loader\SwirlFilterLoader */ private $loader; protected function setUp(): void @@ -72,3 +72,5 @@ public function loadWithOptionProvider() ]; } } + +class_alias(SwirlFilterLoaderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\Loader\SwirlFilterLoaderTest'); diff --git a/tests/bundle/Core/Imagine/Filter/UnsupportedFilterTest.php b/tests/bundle/Core/Imagine/Filter/UnsupportedFilterTest.php new file mode 100644 index 0000000000..3f77e33bee --- /dev/null +++ b/tests/bundle/Core/Imagine/Filter/UnsupportedFilterTest.php @@ -0,0 +1,24 @@ +expectException(NotSupportedException::class); + + $filter = new UnsupportedFilter(); + $filter->apply($this->createMock(ImageInterface::class)); + } +} + +class_alias(UnsupportedFilterTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\Filter\UnsupportedFilterTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/IORepositoryResolverTest.php b/tests/bundle/Core/Imagine/IORepositoryResolverTest.php similarity index 90% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/IORepositoryResolverTest.php rename to tests/bundle/Core/Imagine/IORepositoryResolverTest.php index ec0954dbec..aac66be802 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/IORepositoryResolverTest.php +++ b/tests/bundle/Core/Imagine/IORepositoryResolverTest.php @@ -4,18 +4,19 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine; - -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\FilterConfiguration; -use eZ\Bundle\EzPublishCoreBundle\Imagine\IORepositoryResolver; -use eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\IO\Values\BinaryFileCreateStruct; -use eZ\Publish\Core\IO\Values\MissingBinaryFile; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\SPI\Variation\VariationPurger; +namespace Ibexa\Tests\Bundle\Core\Imagine; + +use Ibexa\Bundle\Core\Imagine\Filter\FilterConfiguration; +use Ibexa\Bundle\Core\Imagine\IORepositoryResolver; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Contracts\Core\Variation\VariationPathGenerator; +use Ibexa\Contracts\Core\Variation\VariationPurger; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\BinaryFile; +use Ibexa\Core\IO\Values\BinaryFileCreateStruct; +use Ibexa\Core\IO\Values\MissingBinaryFile; +use Liip\ImagineBundle\Exception\Imagine\Cache\Resolver\NotResolvableException; use Liip\ImagineBundle\Model\Binary; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; @@ -32,16 +33,16 @@ class IORepositoryResolverTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $configResolver; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\IORepositoryResolver */ + /** @var \Ibexa\Bundle\Core\Imagine\IORepositoryResolver */ private $imageResolver; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\FilterConfiguration */ + /** @var \Ibexa\Bundle\Core\Imagine\Filter\FilterConfiguration */ private $filterConfiguration; - /** @var \eZ\Publish\SPI\Variation\VariationPurger|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Variation\VariationPurger|\PHPUnit\Framework\MockObject\MockObject */ protected $variationPurger; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Variation\VariationPathGenerator|\PHPUnit\Framework\MockObject\MockObject */ protected $variationPathGenerator; protected function setUp(): void @@ -153,7 +154,7 @@ public function testResolve($path, $filter, $variationPath, $requestUrl, $expect public function testResolveMissing() { - $this->expectException(\Liip\ImagineBundle\Exception\Imagine\Cache\Resolver\NotResolvableException::class); + $this->expectException(NotResolvableException::class); $path = 'foo/something.jpg'; $this->ioService @@ -167,7 +168,7 @@ public function testResolveMissing() public function testResolveNotFound() { - $this->expectException(\Liip\ImagineBundle\Exception\Imagine\Cache\Resolver\NotResolvableException::class); + $this->expectException(NotResolvableException::class); $path = 'foo/something.jpg'; $this->ioService @@ -360,3 +361,5 @@ public function testRemoveWithFilters() $this->imageResolver->remove([$originalPath], $filters); } } + +class_alias(IORepositoryResolverTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\IORepositoryResolverTest'); diff --git a/tests/bundle/Core/Imagine/ImageAsset/AliasGeneratorTest.php b/tests/bundle/Core/Imagine/ImageAsset/AliasGeneratorTest.php new file mode 100644 index 0000000000..2ac431b7e4 --- /dev/null +++ b/tests/bundle/Core/Imagine/ImageAsset/AliasGeneratorTest.php @@ -0,0 +1,144 @@ +innerAliasGenerator = $this->createMock(VariationHandler::class); + $this->contentService = $this->createMock(ContentService::class); + $this->assetMapper = $this->createMock(ImageAsset\AssetMapper::class); + + $this->aliasGenerator = new AliasGenerator( + $this->innerAliasGenerator, + $this->contentService, + $this->assetMapper + ); + } + + public function testGetVariationOfImageAsset() + { + $assetField = new Field([ + 'value' => new ImageAsset\Value(486), + ]); + $imageField = new Field([ + 'value' => new Image\Value([ + 'id' => 'images/6/8/4/0/486-10-eng-GB/photo.jpg', + ]), + ]); + + $assetVersionInfo = new VersionInfo(); + $imageVersionInfo = new VersionInfo(); + $imageContent = new Content([ + 'versionInfo' => $imageVersionInfo, + ]); + + $variationName = 'thumbnail'; + $parameters = []; + + $expectedVariation = new Variation(); + + $this->contentService + ->expects($this->once()) + ->method('loadContent') + ->with($assetField->value->destinationContentId) + ->willReturn($imageContent); + + $this->assetMapper + ->expects($this->once()) + ->method('getAssetField') + ->with($imageContent) + ->willReturn($imageField); + + $this->innerAliasGenerator + ->expects($this->once()) + ->method('getVariation') + ->with($imageField, $imageVersionInfo, $variationName, $parameters) + ->willReturn($expectedVariation); + + $actualVariation = $this->aliasGenerator->getVariation( + $assetField, + $assetVersionInfo, + $variationName, + $parameters + ); + + $this->assertEquals($expectedVariation, $actualVariation); + } + + public function testGetVariationOfNonImageAsset() + { + $imageField = new Field([ + 'value' => new Image\Value([ + 'id' => 'images/6/8/4/0/486-10-eng-GB/photo.jpg', + ]), + ]); + + $imageVersionInfo = new VersionInfo(); + $variationName = 'thumbnail'; + $parameters = []; + + $expectedVariation = new Variation(); + + $this->contentService + ->expects($this->never()) + ->method('loadContent'); + + $this->assetMapper + ->expects($this->never()) + ->method('getAssetField'); + + $this->innerAliasGenerator + ->expects($this->once()) + ->method('getVariation') + ->with($imageField, $imageVersionInfo, $variationName, $parameters) + ->willReturn($expectedVariation); + + $actualVariation = $this->aliasGenerator->getVariation( + $imageField, + $imageVersionInfo, + $variationName, + $parameters + ); + + $this->assertEquals($expectedVariation, $actualVariation); + } + + public function testSupport() + { + $this->assertTrue($this->aliasGenerator->supportsValue(new ImageAsset\Value())); + $this->assertFalse($this->aliasGenerator->supportsValue(new Image\Value())); + } +} + +class_alias(AliasGeneratorTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\ImageAsset\AliasGeneratorTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/PlaceholderAliasGeneratorConfiguratorTest.php b/tests/bundle/Core/Imagine/PlaceholderAliasGeneratorConfiguratorTest.php similarity index 78% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/PlaceholderAliasGeneratorConfiguratorTest.php rename to tests/bundle/Core/Imagine/PlaceholderAliasGeneratorConfiguratorTest.php index 6100bc352c..d23754057f 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/PlaceholderAliasGeneratorConfiguratorTest.php +++ b/tests/bundle/Core/Imagine/PlaceholderAliasGeneratorConfiguratorTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine; +namespace Ibexa\Tests\Bundle\Core\Imagine; -use eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderAliasGenerator; -use eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderAliasGeneratorConfigurator; -use eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider; -use eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProviderRegistry; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Bundle\Core\Imagine\PlaceholderAliasGenerator; +use Ibexa\Bundle\Core\Imagine\PlaceholderAliasGeneratorConfigurator; +use Ibexa\Bundle\Core\Imagine\PlaceholderProvider; +use Ibexa\Bundle\Core\Imagine\PlaceholderProviderRegistry; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; use PHPUnit\Framework\TestCase; class PlaceholderAliasGeneratorConfiguratorTest extends TestCase @@ -62,3 +62,5 @@ public function testConfigure() $configurator->configure($generator); } } + +class_alias(PlaceholderAliasGeneratorConfiguratorTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\PlaceholderAliasGeneratorConfiguratorTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/PlaceholderAliasGeneratorTest.php b/tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php similarity index 85% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/PlaceholderAliasGeneratorTest.php rename to tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php index 5d746a03a6..dbd3586c73 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/PlaceholderAliasGeneratorTest.php +++ b/tests/bundle/Core/Imagine/PlaceholderAliasGeneratorTest.php @@ -4,42 +4,42 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine; - -use eZ\Bundle\EzPublishCoreBundle\Imagine\IORepositoryResolver; -use eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderAliasGenerator; -use eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\Core\FieldType\Image\Value as ImageValue; -use eZ\Publish\Core\FieldType\Null\Value as NullValue; -use eZ\Publish\Core\FieldType\Value; -use eZ\Publish\Core\FieldType\Value as FieldTypeValue; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\IO\Values\BinaryFileCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\SPI\Variation\Values\ImageVariation; -use eZ\Publish\SPI\Variation\VariationHandler; +namespace Ibexa\Tests\Bundle\Core\Imagine; + +use Ibexa\Bundle\Core\Imagine\IORepositoryResolver; +use Ibexa\Bundle\Core\Imagine\PlaceholderAliasGenerator; +use Ibexa\Bundle\Core\Imagine\PlaceholderProvider; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Variation\Values\ImageVariation; +use Ibexa\Contracts\Core\Variation\VariationHandler; +use Ibexa\Core\FieldType\Image\Value as ImageValue; +use Ibexa\Core\FieldType\Null\Value as NullValue; +use Ibexa\Core\FieldType\Value; +use Ibexa\Core\FieldType\Value as FieldTypeValue; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\BinaryFile; +use Ibexa\Core\IO\Values\BinaryFileCreateStruct; +use Ibexa\Core\Repository\Values\Content\VersionInfo; use Liip\ImagineBundle\Exception\Imagine\Cache\Resolver\NotResolvableException; use PHPUnit\Framework\TestCase; class PlaceholderAliasGeneratorTest extends TestCase { - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderAliasGenerator */ + /** @var \Ibexa\Bundle\Core\Imagine\PlaceholderAliasGenerator */ private $aliasGenerator; - /** @var \eZ\Publish\SPI\Variation\VariationHandler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Variation\VariationHandler|\PHPUnit\Framework\MockObject\MockObject */ private $innerAliasGenerator; - /** @var \eZ\Publish\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ private $ioService; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\IORepositoryResolver|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Bundle\Core\Imagine\IORepositoryResolver|\PHPUnit\Framework\MockObject\MockObject */ private $ioResolver; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Bundle\Core\Imagine\PlaceholderProvider|\PHPUnit\Framework\MockObject\MockObject */ private $placeholderProvider; /** @var array */ @@ -298,3 +298,5 @@ public function getVariationProvider(): array ]; } } + +class_alias(PlaceholderAliasGeneratorTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\PlaceholderAliasGeneratorTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/PlaceholderProvider/GenericProviderTest.php b/tests/bundle/Core/Imagine/PlaceholderProvider/GenericProviderTest.php similarity index 91% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/PlaceholderProvider/GenericProviderTest.php rename to tests/bundle/Core/Imagine/PlaceholderProvider/GenericProviderTest.php index d960cf88f2..61a160d593 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/PlaceholderProvider/GenericProviderTest.php +++ b/tests/bundle/Core/Imagine/PlaceholderProvider/GenericProviderTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\PlaceholderProvider; +namespace Ibexa\Tests\Bundle\Core\Imagine\PlaceholderProvider; -use eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider\GenericProvider; -use eZ\Publish\Core\FieldType\Image\Value as ImageValue; +use Ibexa\Bundle\Core\Imagine\PlaceholderProvider\GenericProvider; +use Ibexa\Core\FieldType\Image\Value as ImageValue; use Imagine\Draw\DrawerInterface; use Imagine\Image\AbstractFont; use Imagine\Image\BoxInterface; @@ -101,3 +101,5 @@ private function assertColorEquals($expected, ColorInterface $actual) $this->assertEquals(strtolower($expected), strtolower((string)$actual)); } } + +class_alias(GenericProviderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\PlaceholderProvider\GenericProviderTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/PlaceholderProviderRegistryTest.php b/tests/bundle/Core/Imagine/PlaceholderProviderRegistryTest.php similarity index 78% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/PlaceholderProviderRegistryTest.php rename to tests/bundle/Core/Imagine/PlaceholderProviderRegistryTest.php index 0f8c556531..f42f68926e 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/PlaceholderProviderRegistryTest.php +++ b/tests/bundle/Core/Imagine/PlaceholderProviderRegistryTest.php @@ -4,21 +4,21 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine; +namespace Ibexa\Tests\Bundle\Core\Imagine; -use eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProvider; -use eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProviderRegistry; +use Ibexa\Bundle\Core\Imagine\PlaceholderProvider; +use Ibexa\Bundle\Core\Imagine\PlaceholderProviderRegistry; use PHPUnit\Framework\TestCase; +/** + * @covers \Ibexa\Bundle\Core\Imagine\PlaceholderProviderRegistry + */ class PlaceholderProviderRegistryTest extends TestCase { private const FOO = 'foo'; private const BAR = 'bar'; /** - * @covers \eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProviderRegistry::__construct - * - * @uses \eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProviderRegistry::getProvider * @depends testGetProviderKnown */ public function testConstructor() @@ -35,9 +35,6 @@ public function testConstructor() } /** - * @covers \eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProviderRegistry::addProvider - * - * @uses \eZ\Bundle\EzPublishCoreBundle\Imagine\PlaceholderProviderRegistry::getProvider * @depends testGetProviderKnown */ public function testAddProvider(): void @@ -87,3 +84,5 @@ private function getPlaceholderProviderMock(): PlaceholderProvider return $this->createMock(PlaceholderProvider::class); } } + +class_alias(PlaceholderProviderRegistryTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\PlaceholderProviderRegistryTest'); diff --git a/tests/bundle/Core/Imagine/VariationPathGenerator/AliasDirectoryVariationPathGeneratorTest.php b/tests/bundle/Core/Imagine/VariationPathGenerator/AliasDirectoryVariationPathGeneratorTest.php new file mode 100644 index 0000000000..59d2911a3b --- /dev/null +++ b/tests/bundle/Core/Imagine/VariationPathGenerator/AliasDirectoryVariationPathGeneratorTest.php @@ -0,0 +1,25 @@ +getVariationPath('path/to/original.png', 'large') + ); + } +} + +class_alias(AliasDirectoryVariationPathGeneratorTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\VariationPathGenerator\AliasDirectoryVariationPathGeneratorTest'); diff --git a/tests/bundle/Core/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGeneratorTest.php b/tests/bundle/Core/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGeneratorTest.php new file mode 100644 index 0000000000..010a1d46b2 --- /dev/null +++ b/tests/bundle/Core/Imagine/VariationPathGenerator/OriginalDirectoryVariationPathGeneratorTest.php @@ -0,0 +1,24 @@ +getVariationPath('path/to/original.png', 'large') + ); + } +} + +class_alias(OriginalDirectoryVariationPathGeneratorTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\VariationPathGenerator\OriginalDirectoryVariationPathGeneratorTest'); diff --git a/tests/bundle/Core/Imagine/VariationPathGenerator/WebpFormatVariationPathGeneratorTest.php b/tests/bundle/Core/Imagine/VariationPathGenerator/WebpFormatVariationPathGeneratorTest.php index ce36352d4d..adcbb83cd1 100644 --- a/tests/bundle/Core/Imagine/VariationPathGenerator/WebpFormatVariationPathGeneratorTest.php +++ b/tests/bundle/Core/Imagine/VariationPathGenerator/WebpFormatVariationPathGeneratorTest.php @@ -8,18 +8,18 @@ namespace Ibexa\Tests\Bundle\Core\Imagine\VariationPathGenerator; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\FilterConfiguration; -use eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator; +use Ibexa\Bundle\Core\Imagine\Filter\FilterConfiguration; +use Ibexa\Bundle\Core\Imagine\VariationPathGenerator; use Ibexa\Bundle\Core\Imagine\VariationPathGenerator\WebpFormatVariationPathGenerator; use PHPUnit\Framework\TestCase; final class WebpFormatVariationPathGeneratorTest extends TestCase { - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator|\PHPUnit\Framework\MockObject\MockObject */ - private $innerVariationPathGenerator; + /** @var \Ibexa\Bundle\Core\Imagine\VariationPathGenerator|\PHPUnit\Framework\MockObject\MockObject */ + private VariationPathGenerator $innerVariationPathGenerator; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\Filter\FilterConfiguration|\PHPUnit\Framework\MockObject\MockObject */ - private $filterConfiguration; + /** @var \Ibexa\Bundle\Core\Imagine\Filter\FilterConfiguration|\PHPUnit\Framework\MockObject\MockObject */ + private FilterConfiguration $filterConfiguration; protected function setUp(): void { diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPurger/IOVariationPurgerTest.php b/tests/bundle/Core/Imagine/VariationPurger/IOVariationPurgerTest.php similarity index 84% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPurger/IOVariationPurgerTest.php rename to tests/bundle/Core/Imagine/VariationPurger/IOVariationPurgerTest.php index 96feeebc53..c171551138 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPurger/IOVariationPurgerTest.php +++ b/tests/bundle/Core/Imagine/VariationPurger/IOVariationPurgerTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\VariationPurger; +namespace Ibexa\Tests\Bundle\Core\Imagine\VariationPurger; -use eZ\Bundle\EzPublishCoreBundle\Imagine\Cache\AliasGeneratorDecorator; -use eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\IOVariationPurger; -use eZ\Publish\Core\IO\IOServiceInterface; +use Ibexa\Bundle\Core\Imagine\Cache\AliasGeneratorDecorator; +use Ibexa\Bundle\Core\Imagine\VariationPurger\IOVariationPurger; +use Ibexa\Core\IO\IOServiceInterface; use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; @@ -59,3 +59,5 @@ public function testPurgesAliasList(): void $purger->purge(['medium', 'large']); } } + +class_alias(IOVariationPurgerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\VariationPurger\IOVariationPurgerTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPurger/ImageFileVariationPurgerTest.php b/tests/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurgerTest.php similarity index 81% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPurger/ImageFileVariationPurgerTest.php rename to tests/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurgerTest.php index 1c3f0ea1cb..5282d7d070 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Imagine/VariationPurger/ImageFileVariationPurgerTest.php +++ b/tests/bundle/Core/Imagine/VariationPurger/ImageFileVariationPurgerTest.php @@ -4,24 +4,24 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\VariationPurger; +namespace Ibexa\Tests\Bundle\Core\Imagine\VariationPurger; use ArrayIterator; -use eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator; -use eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\ImageFileVariationPurger; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\BinaryFile; +use Ibexa\Bundle\Core\Imagine\VariationPurger\ImageFileVariationPurger; +use Ibexa\Contracts\Core\Variation\VariationPathGenerator; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\BinaryFile; use PHPUnit\Framework\TestCase; class ImageFileVariationPurgerTest extends TestCase { - /** @var \eZ\Publish\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $ioServiceMock; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPathGenerator|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Variation\VariationPathGenerator|\PHPUnit\Framework\MockObject\MockObject */ protected $pathGeneratorMock; - /** @var \eZ\Bundle\EzPublishCoreBundle\Imagine\VariationPurger\ImageFileVariationPurger */ + /** @var \Ibexa\Bundle\Core\Imagine\VariationPurger\ImageFileVariationPurger */ protected $purger; protected function setUp(): void @@ -113,3 +113,5 @@ private function createPurger(array $fileList) return new ImageFileVariationPurger(new ArrayIterator($fileList), $this->ioServiceMock, $this->pathGeneratorMock); } } + +class_alias(ImageFileVariationPurgerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\VariationPurger\ImageFileVariationPurgerTest'); diff --git a/tests/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileListTest.php b/tests/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileListTest.php new file mode 100644 index 0000000000..83f76265cc --- /dev/null +++ b/tests/bundle/Core/Imagine/VariationPurger/LegacyStorageImageFileListTest.php @@ -0,0 +1,84 @@ +rowReaderMock = $this->createMock(ImageFileRowReader::class); + $this->ioConfigResolverMock = $this->createMock(IOConfigProvider::class); + $this->ioConfigResolverMock + ->method('getLegacyUrlPrefix') + ->willReturn('var/ibexa_demo_site/storage'); + $this->configResolverMock = $this->createMock(ConfigResolverInterface::class); + $this->configResolverMock + ->method('getParameter') + ->with('image.published_images_dir') + ->willReturn('images'); + + $this->fileList = new LegacyStorageImageFileList( + $this->rowReaderMock, + $this->ioConfigResolverMock, + $this->configResolverMock + ); + } + + public function testIterator() + { + $expected = [ + 'path/to/1st/image.jpg', + 'path/to/2nd/image.jpg', + ]; + $this->configureRowReaderMock($expected); + + foreach ($this->fileList as $index => $file) { + self::assertEquals($expected[$index], $file); + } + } + + /** + * Tests that the iterator transforms the ezimagefile value into a binaryfile id. + */ + public function testImageIdTransformation() + { + $this->configureRowReaderMock(['var/ibexa_demo_site/storage/images/path/to/1st/image.jpg']); + foreach ($this->fileList as $file) { + self::assertEquals('path/to/1st/image.jpg', $file); + } + } + + private function configureRowReaderMock(array $fileList) + { + $mockInvocator = $this->rowReaderMock->expects($this->any())->method('getRow'); + call_user_func_array([$mockInvocator, 'willReturnOnConsecutiveCalls'], $fileList); + + $this->rowReaderMock->expects($this->any())->method('getCount')->willReturn(count($fileList)); + } +} + +class_alias(LegacyStorageImageFileListTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Imagine\VariationPurger\LegacyStorageImageFileListTest'); diff --git a/tests/bundle/Core/Matcher/ServiceAwareMatcherFactoryTest.php b/tests/bundle/Core/Matcher/ServiceAwareMatcherFactoryTest.php new file mode 100644 index 0000000000..faf21dd3e1 --- /dev/null +++ b/tests/bundle/Core/Matcher/ServiceAwareMatcherFactoryTest.php @@ -0,0 +1,110 @@ +}>> + */ +final class ServiceAwareMatcherFactoryTest extends TestCase +{ + /** @var \Ibexa\Contracts\Core\MVC\View\ViewMatcherRegistryInterface&\PHPUnit\Framework\MockObject\MockObject */ + private ViewMatcherRegistryInterface $viewMatcherRegistryMock; + + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ViewMatcherInterface&\PHPUnit\Framework\MockObject\MockObject */ + private ViewMatcherInterface $matcherMock; + + protected function setUp(): void + { + $this->viewMatcherRegistryMock = $this->createMock(ViewMatcherRegistryInterface::class); + $this->viewMatcherRegistryMock->method('hasMatcher')->willReturnMap( + [ + ['App\Matcher', true], + ['IdentifierBasedMatcher', true], + ] + ); + + $this->matcherMock = $this->createMock(ViewMatcherInterface::class); + + $this->viewMatcherRegistryMock->method('getMatcher')->willReturnMap( + [ + ['App\Matcher', $this->matcherMock], + ['IdentifierBasedMatcher', $this->matcherMock], + ] + ); + } + + /** + * @phpstan-return iterable + */ + public function getDataForTestMatch(): iterable + { + yield 'full view service-based matcher' => [ + new ContentView(), + [ + 'full' => [ + 'my_view' => [ + 'match' => [ + '@App\Matcher' => 'service-based config', + ], + ], + ], + ], + 'service-based config', + ]; + + yield 'full view identifier-based matcher' => [ + new ContentView(), + [ + 'full' => [ + 'my_view' => [ + 'match' => [ + 'IdentifierBasedMatcher' => 'identifier-based config', + ], + ], + ], + ], + 'identifier-based config', + ]; + } + + /** + * @dataProvider getDataForTestMatch + * + * @phpstan-param TMatchConfigArray $matchConfig + */ + public function testMatch(View $view, array $matchConfig, string $matchedConfigValue): void + { + $serviceMatcherFactory = new ServiceAwareMatcherFactory( + $this->viewMatcherRegistryMock, + $this->createMock(Repository::class), + null, + $matchConfig + ); + $this->matcherMock->method('setMatchingConfig')->with(true); + $this->matcherMock->method('match')->with($view)->willReturn($matchedConfigValue); + + self::assertSame( + [ + 'match' => $matchConfig['full']['my_view']['match'], + 'matcher' => $this->matcherMock, + ], + $serviceMatcherFactory->match($view) + ); + } +} diff --git a/tests/bundle/Core/Matcher/ViewMatcherRegistryTest.php b/tests/bundle/Core/Matcher/ViewMatcherRegistryTest.php new file mode 100644 index 0000000000..93bb44aba6 --- /dev/null +++ b/tests/bundle/Core/Matcher/ViewMatcherRegistryTest.php @@ -0,0 +1,73 @@ +getMatcherMock(); + $registry = new ViewMatcherRegistry([self::MATCHER_NAME => $matcher]); + + self::assertSame($matcher, $registry->getMatcher(self::MATCHER_NAME)); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testSetMatcher(): void + { + $matcher = $this->getMatcherMock(); + $registry = new ViewMatcherRegistry(); + + $registry->setMatcher(self::MATCHER_NAME, $matcher); + + self::assertSame($matcher, $registry->getMatcher(self::MATCHER_NAME)); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testSetMatcherOverride(): void + { + $matcher = $this->getMatcherMock(); + $newMatcher = $this->getMatcherMock(); + $registry = new ViewMatcherRegistry([self::MATCHER_NAME => $matcher]); + + $registry->setMatcher(self::MATCHER_NAME, $newMatcher); + + self::assertSame($newMatcher, $registry->getMatcher(self::MATCHER_NAME)); + } + + public function testGetMatcherNotFound(): void + { + $this->expectException(NotFoundException::class); + $registry = new ViewMatcherRegistry(); + + $registry->getMatcher(self::MATCHER_NAME); + } + + protected function getMatcherMock(): ViewMatcherInterface + { + return $this->createMock(ViewMatcherInterface::class); + } +} + +class_alias(ViewMatcherRegistryTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Matcher\ViewMatcherRegistryTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/Translation/vendor/ezplatform-i18n/ezplatform-i18n-ach_UG/ezpublish-kernel/messages.ach_UG.xlf b/tests/bundle/Core/Resources/Translation/vendor/ibexa/i18n/translations/ach_UG/core/messages.ach_UG.xlf similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Resources/Translation/vendor/ezplatform-i18n/ezplatform-i18n-ach_UG/ezpublish-kernel/messages.ach_UG.xlf rename to tests/bundle/Core/Resources/Translation/vendor/ibexa/i18n/translations/ach_UG/core/messages.ach_UG.xlf diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/Translation/vendor/ezplatform-i18n/ezplatform-i18n-fr/platform-ui-bundle/dashboard.fr.xlf b/tests/bundle/Core/Resources/Translation/vendor/ibexa/i18n/translations/fr/admin-ui/dashboard.fr.xlf similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Resources/Translation/vendor/ezplatform-i18n/ezplatform-i18n-fr/platform-ui-bundle/dashboard.fr.xlf rename to tests/bundle/Core/Resources/Translation/vendor/ibexa/i18n/translations/fr/admin-ui/dashboard.fr.xlf diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/Translation/vendor/ezplatform-i18n/ezplatform-i18n-fr/ezpublish-kernel/messages.fr.xlf b/tests/bundle/Core/Resources/Translation/vendor/ibexa/i18n/translations/fr/core/messages.fr.xlf similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Resources/Translation/vendor/ezplatform-i18n/ezplatform-i18n-fr/ezpublish-kernel/messages.fr.xlf rename to tests/bundle/Core/Resources/Translation/vendor/ibexa/i18n/translations/fr/core/messages.fr.xlf diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/Translation/vendor/ezplatform-i18n/ezplatform-i18n-fr/ezpublish-kernel/wrongformat.fr.xml b/tests/bundle/Core/Resources/Translation/vendor/ibexa/i18n/translations/fr/core/wrongformat.fr.xml similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Resources/Translation/vendor/ezplatform-i18n/ezplatform-i18n-fr/ezpublish-kernel/wrongformat.fr.xml rename to tests/bundle/Core/Resources/Translation/vendor/ibexa/i18n/translations/fr/core/wrongformat.fr.xml diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/Translation/vendor/ezplatform-i18n/ezplatform-i18n-fr/wrongplace.fr.xml b/tests/bundle/Core/Resources/Translation/vendor/ibexa/i18n/translations/fr/wrongplace.fr.xml similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Resources/Translation/vendor/ezplatform-i18n/ezplatform-i18n-fr/wrongplace.fr.xml rename to tests/bundle/Core/Resources/Translation/vendor/ibexa/i18n/translations/fr/wrongplace.fr.xml diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/Translation/vendor/ezplatform-i18n/wrong-translation-folder-format/ezpublish-kernel/messages.fr.xlf b/tests/bundle/Core/Resources/Translation/vendor/ibexa/wrong-translation-folder-format/core/messages.fr.xlf similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Resources/Translation/vendor/ezplatform-i18n/wrong-translation-folder-format/ezpublish-kernel/messages.fr.xlf rename to tests/bundle/Core/Resources/Translation/vendor/ibexa/wrong-translation-folder-format/core/messages.fr.xlf diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/doctrine.php b/tests/bundle/Core/Resources/config/doctrine.php similarity index 89% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/doctrine.php rename to tests/bundle/Core/Resources/config/doctrine.php index b963ce1e7c..87267fc31d 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/doctrine.php +++ b/tests/bundle/Core/Resources/config/doctrine.php @@ -8,8 +8,8 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; -use EzSystems\DoctrineSchema\Database\DbPlatform\PostgreSqlDbPlatform; -use EzSystems\DoctrineSchema\Database\DbPlatform\SqliteDbPlatform; +use Ibexa\DoctrineSchema\Database\DbPlatform\PostgreSqlDbPlatform; +use Ibexa\DoctrineSchema\Database\DbPlatform\SqliteDbPlatform; use RuntimeException; return static function (ContainerConfigurator $container): void { diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/ezpublish.yaml b/tests/bundle/Core/Resources/config/ezpublish.yaml similarity index 97% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/ezpublish.yaml rename to tests/bundle/Core/Resources/config/ezpublish.yaml index 174793d0dd..af1126187e 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/ezpublish.yaml +++ b/tests/bundle/Core/Resources/config/ezpublish.yaml @@ -1,7 +1,7 @@ parameters: env(SEARCH_ENGINE): 'legacy' -ezpublish: +ibexa: siteaccess: default_siteaccess: '__default_site_access__' list: diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/framework.yaml b/tests/bundle/Core/Resources/config/framework.yaml similarity index 90% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/framework.yaml rename to tests/bundle/Core/Resources/config/framework.yaml index 2e420dbf87..f8b8e99feb 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/framework.yaml +++ b/tests/bundle/Core/Resources/config/framework.yaml @@ -1,5 +1,5 @@ parameters: - io_root_dir: '' + ibexa.io.dir.root: '' kernel.secret: 'foobar' framework: diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/routes.yaml b/tests/bundle/Core/Resources/config/routes.yaml similarity index 100% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Resources/config/routes.yaml rename to tests/bundle/Core/Resources/config/routes.yaml diff --git a/tests/bundle/Core/Resources/config/security.yaml b/tests/bundle/Core/Resources/config/security.yaml new file mode 100644 index 0000000000..abb4fa3d91 --- /dev/null +++ b/tests/bundle/Core/Resources/config/security.yaml @@ -0,0 +1,8 @@ +security: + providers: + default: + id: ibexa.security.user_provider + + firewalls: + main: + anonymous: ~ diff --git a/tests/bundle/Core/Resources/services/fixture-services.yaml b/tests/bundle/Core/Resources/services/fixture-services.yaml new file mode 100644 index 0000000000..6a2644a296 --- /dev/null +++ b/tests/bundle/Core/Resources/services/fixture-services.yaml @@ -0,0 +1,26 @@ +services: + Ibexa\Tests\Core\Repository\LegacySchemaImporter: + alias: 'test.ibexa.schema_importer' + + test.ibexa.schema_importer: + class: Ibexa\Tests\Core\Repository\LegacySchemaImporter + public: true + arguments: + - '@doctrine.dbal.default_connection' + + Ibexa\Contracts\Core\Test\Persistence\Fixture\FixtureImporter: + alias: 'test.ibexa.fixture_importer' + + test.ibexa.fixture_importer: + class: Ibexa\Contracts\Core\Test\Persistence\Fixture\FixtureImporter + public: true + arguments: + - '@doctrine.dbal.default_connection' + + Ibexa\DoctrineSchema\Database\DbPlatform\SqliteDbPlatform: + calls: + - [setEventManager, ['@doctrine.dbal.default_connection.event_manager']] + + Ibexa\DoctrineSchema\Database\DbPlatform\PostgreSqlDbPlatform: + calls: + - [ setEventManager, [ '@doctrine.dbal.default_connection.event_manager' ] ] diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Routing/DefaultRouterTest.php b/tests/bundle/Core/Routing/DefaultRouterTest.php similarity index 80% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Routing/DefaultRouterTest.php rename to tests/bundle/Core/Routing/DefaultRouterTest.php index 9d7727d285..8b288e069a 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Routing/DefaultRouterTest.php +++ b/tests/bundle/Core/Routing/DefaultRouterTest.php @@ -4,18 +4,19 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Routing; +namespace Ibexa\Tests\Bundle\Core\Routing; -use eZ\Bundle\EzPublishCoreBundle\Routing\DefaultRouter; -use eZ\Bundle\EzPublishCoreBundle\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Bundle\Core\Routing\DefaultRouter; +use Ibexa\Bundle\Core\SiteAccess\Matcher; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess; use PHPUnit\Framework\TestCase; use ReflectionObject; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\Matcher\UrlMatcherInterface; use Symfony\Component\Routing\RequestContext; class DefaultRouterTest extends TestCase @@ -23,7 +24,7 @@ class DefaultRouterTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject|\Symfony\Component\DependencyInjection\ContainerInterface */ protected $container; - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ protected $configResolver; /** @var \Symfony\Component\Routing\RequestContext */ @@ -37,19 +38,22 @@ protected function setUp(): void $this->requestContext = new RequestContext(); } + /** + * @return class-string<\Ibexa\Bundle\Core\Routing\DefaultRouter> + */ protected function getRouterClass() { return DefaultRouter::class; } /** - * @param array $mockedMethods + * @param array $mockedMethods * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Bundle\EzPublishCoreBundle\Routing\DefaultRouter + * @return \PHPUnit\Framework\MockObject\MockObject&\Ibexa\Bundle\Core\Routing\DefaultRouter */ protected function generateRouter(array $mockedMethods = []) { - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Bundle\EzPublishCoreBundle\Routing\DefaultRouter $router */ + /** @var \PHPUnit\Framework\MockObject\MockObject&\Ibexa\Bundle\Core\Routing\DefaultRouter $router */ $router = $this ->getMockBuilder($this->getRouterClass()) ->setConstructorArgs([$this->container, 'foo', [], $this->requestContext]) @@ -67,15 +71,21 @@ public function testMatchRequestWithSemanticPathinfo() $request = Request::create($pathinfo); $request->attributes->set('semanticPathinfo', $semanticPathinfo); - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Bundle\EzPublishCoreBundle\Routing\DefaultRouter $router */ - $router = $this->generateRouter(['match']); - + /** @var \PHPUnit\Framework\MockObject\MockObject&\Ibexa\Bundle\Core\Routing\DefaultRouter $router */ + $router = $this->generateRouter(['getMatcher']); $matchedParameters = ['_controller' => 'AcmeBundle:myAction']; - $router - ->expects($this->once()) + + $matcher = $this->createMock(UrlMatcherInterface::class); + $matcher->expects(self::once()) ->method('match') ->with($semanticPathinfo) - ->will($this->returnValue($matchedParameters)); + ->willReturn($matchedParameters); + + $router + ->expects(self::once()) + ->method('getMatcher') + ->willReturn($matcher); + $this->assertSame($matchedParameters, $router->matchRequest($request)); } @@ -88,13 +98,20 @@ public function testMatchRequestRegularPathinfo() $this->configResolver->expects($this->never())->method('getParameter'); - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Bundle\EzPublishCoreBundle\Routing\DefaultRouter $router */ - $router = $this->generateRouter(['match']); - $router - ->expects($this->once()) + /** @var \PHPUnit\Framework\MockObject\MockObject&\Ibexa\Bundle\Core\Routing\DefaultRouter $router */ + $router = $this->generateRouter(['getMatcher']); + + $matcher = $this->createMock(UrlMatcherInterface::class); + $matcher->expects(self::once()) ->method('match') ->with($pathinfo) - ->will($this->returnValue($matchedParameters)); + ->willReturn($matchedParameters); + + $router + ->expects(self::once()) + ->method('getMatcher') + ->willReturn($matcher); + $this->assertSame($matchedParameters, $router->matchRequest($request)); } @@ -105,17 +122,17 @@ public function testGenerateNoSiteAccess($url) { $generator = $this->createMock(UrlGeneratorInterface::class); $generator - ->expects($this->once()) + ->expects(self::once()) ->method('generate') ->with(__METHOD__) - ->will($this->returnValue($url)); + ->willReturn($url); - /** @var \eZ\Bundle\EzPublishCoreBundle\Routing\DefaultRouter|\PHPUnit\Framework\MockObject\MockObject $router */ + /** @var \Ibexa\Bundle\Core\Routing\DefaultRouter&\PHPUnit\Framework\MockObject\MockObject $router */ $router = $this->generateRouter(['getGenerator']); $router - ->expects($this->any()) + ->expects(self::any()) ->method('getGenerator') - ->will($this->returnValue($generator)); + ->willReturn($generator); $this->assertSame($url, $router->generate(__METHOD__)); } @@ -125,7 +142,7 @@ public function providerGenerateNoSiteAccess() return [ ['/foo/bar'], ['/foo/bar/baz?truc=muche&tata=toto'], - ['http://ez.no/Products/eZ-Publish-CMS'], + ['http://ibexa.co/Products/Ibexa-CMS'], ['http://www.metalfrance.net/decouvertes/edge-caress-inverse-ep'], ]; } @@ -147,17 +164,17 @@ public function testGenerateWithSiteAccess($urlGenerated, $relevantUri, $expecte $nonSiteAccessAwareRoutes = ['_dontwantsiteaccess']; $generator = $this->createMock(UrlGeneratorInterface::class); $generator - ->expects($this->once()) + ->expects(self::once()) ->method('generate') ->with($routeName) - ->will($this->returnValue($urlGenerated)); + ->willReturn($urlGenerated); - /** @var \eZ\Bundle\EzPublishCoreBundle\Routing\DefaultRouter|\PHPUnit\Framework\MockObject\MockObject $router */ + /** @var \Ibexa\Bundle\Core\Routing\DefaultRouter&\PHPUnit\Framework\MockObject\MockObject $router */ $router = $this->generateRouter(['getGenerator']); $router - ->expects($this->any()) + ->expects(self::any()) ->method('getGenerator') - ->will($this->returnValue($generator)); + ->willReturn($generator); // If matcher is URILexer, we make it act as it's supposed to, prepending the siteaccess. if ($isMatcherLexer) { @@ -165,10 +182,10 @@ public function testGenerateWithSiteAccess($urlGenerated, $relevantUri, $expecte // Route is siteaccess aware, we're expecting analyseLink() to be called if (!in_array($routeName, $nonSiteAccessAwareRoutes)) { $matcher - ->expects($this->once()) + ->expects(self::once()) ->method('analyseLink') ->with($relevantUri) - ->will($this->returnValue("/$saName$relevantUri")); + ->willReturn("/$saName$relevantUri"); } else { // Non-siteaccess aware route, it's not supposed to be analysed $matcher @@ -235,27 +252,27 @@ public function testGenerateReverseSiteAccessMatch() ] ); $versatileMatcher - ->expects($this->once()) + ->expects(self::once()) ->method('getRequest') - ->will($this->returnValue($simplifiedRequest)); + ->willReturn($simplifiedRequest); $siteAccessRouter - ->expects($this->once()) + ->expects(self::once()) ->method('matchByName') ->with($siteAccessName) - ->will($this->returnValue(new SiteAccess($siteAccessName, 'foo', $versatileMatcher))); + ->willReturn(new SiteAccess($siteAccessName, 'foo', $versatileMatcher)); $generator = $this->createMock(UrlGeneratorInterface::class); $generator - ->expects($this->at(0)) + ->expects(self::at(0)) ->method('setContext') - ->with($this->isInstanceOf(RequestContext::class)); + ->with(self::isInstanceOf(RequestContext::class)); $generator - ->expects($this->at(1)) + ->expects(self::at(1)) ->method('generate') ->with($routeName) - ->will($this->returnValue($urlGenerated)); + ->willReturn($urlGenerated); $generator - ->expects($this->at(2)) + ->expects(self::at(2)) ->method('setContext') ->with($this->requestContext); @@ -296,7 +313,7 @@ public function testGetContextBySimplifiedRequest($uri) * * @see testGetContextBySimplifiedRequest * - * @return array + * @phpstan-return array */ public function providerGetContextBySimplifiedRequest() { @@ -330,3 +347,5 @@ private function getExpectedRequestContext($uri) return $requestContext; } } + +class_alias(DefaultRouterTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Routing\DefaultRouterTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/Routing/JsRouting/ExposedRoutesExtractorTest.php b/tests/bundle/Core/Routing/JsRouting/ExposedRoutesExtractorTest.php similarity index 82% rename from eZ/Bundle/EzPublishCoreBundle/Tests/Routing/JsRouting/ExposedRoutesExtractorTest.php rename to tests/bundle/Core/Routing/JsRouting/ExposedRoutesExtractorTest.php index f2456f20c4..910f9db726 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/Routing/JsRouting/ExposedRoutesExtractorTest.php +++ b/tests/bundle/Core/Routing/JsRouting/ExposedRoutesExtractorTest.php @@ -6,17 +6,17 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Tests\Routing\JsRouting; +namespace Ibexa\Tests\Bundle\Core\Routing\JsRouting; -use eZ\Bundle\EzPublishCoreBundle\Routing\JsRouting\ExposedRoutesExtractor; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; use FOS\JsRoutingBundle\Extractor\ExposedRoutesExtractorInterface; +use Ibexa\Bundle\Core\Routing\JsRouting\ExposedRoutesExtractor; +use Ibexa\Core\MVC\Symfony\SiteAccess; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; /** - * @covers \eZ\Bundle\EzPublishCoreBundle\Routing\JsRouting\ExposedRoutesExtractor + * @covers \Ibexa\Bundle\Core\Routing\JsRouting\ExposedRoutesExtractor * * @internal */ @@ -67,10 +67,12 @@ public function testGetBaseUrl(?Request $masterRequest, string $expectedBaseUrl) $requestStack = $this->createMock(RequestStack::class); $innerExtractor->method('getBaseUrl')->willReturn(self::BASE_URL); - $requestStack->method('getMasterRequest')->willReturn($masterRequest); + $requestStack->method('getMainRequest')->willReturn($masterRequest); $extractor = new ExposedRoutesExtractor($innerExtractor, $requestStack); self::assertSame($expectedBaseUrl, $extractor->getBaseUrl()); } } + +class_alias(ExposedRoutesExtractorTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Routing\JsRouting\ExposedRoutesExtractorTest'); diff --git a/tests/bundle/Core/Routing/UrlAliasRouterTest.php b/tests/bundle/Core/Routing/UrlAliasRouterTest.php new file mode 100644 index 0000000000..97b86e88d4 --- /dev/null +++ b/tests/bundle/Core/Routing/UrlAliasRouterTest.php @@ -0,0 +1,405 @@ +configResolver = $this->createMock(ConfigResolverInterface::class); + $this->configResolver + ->expects($this->any()) + ->method('getParameter') + ->will( + $this->returnValueMap( + [ + ['url_alias_router', null, null, true], + ['content.tree_root.location_id', null, null, null], + ['content.tree_root.excluded_uri_prefixes', null, null, []], + ] + ) + ); + parent::setUp(); + } + + protected function getRouter(LocationService $locationService, URLAliasService $urlAliasService, ContentService $contentService, UrlAliasGenerator $urlAliasGenerator, RequestContext $requestContext) + { + $router = new UrlAliasRouter($locationService, $urlAliasService, $contentService, $urlAliasGenerator, $requestContext); + $router->setConfigResolver($this->configResolver); + + return $router; + } + + /** + * Resets container and configResolver mocks. + */ + protected function resetConfigResolver() + { + $this->configResolver = $this->createMock(ConfigResolverInterface::class); + $this->container = $this->createMock(ContainerInterface::class); + $this->router->setConfigResolver($this->configResolver); + } + + public function testMatchRequestDeactivatedUrlAlias() + { + $this->expectException(ResourceNotFoundException::class); + + $this->resetConfigResolver(); + $this->configResolver + ->expects($this->any()) + ->method('getParameter') + ->will( + $this->returnValueMap( + [ + ['url_alias_router', null, null, false], + ] + ) + ); + $this->router->matchRequest($this->getRequestByPathInfo('/foo')); + } + + public function testMatchRequestWithRootLocation() + { + $rootLocationId = 123; + $this->resetConfigResolver(); + $this->configResolver + ->expects($this->any()) + ->method('getParameter') + ->will( + $this->returnValueMap( + [ + ['url_alias_router', null, null, true], + ] + ) + ); + $this->router->setRootLocationId($rootLocationId); + + $prefix = '/root/prefix'; + $this->urlALiasGenerator + ->expects($this->exactly(2)) + ->method('getPathPrefixByRootLocationId') + ->with($rootLocationId) + ->will($this->returnValue($prefix)); + + $locationId = 789; + $path = '/foo/bar'; + $urlAlias = new URLAlias( + [ + 'destination' => $locationId, + 'path' => $prefix . $path, + 'type' => URLAlias::LOCATION, + 'isHistory' => false, + ] + ); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($prefix . $path) + ->will($this->returnValue($urlAlias)); + + $this->urlALiasGenerator + ->expects($this->once()) + ->method('loadLocation') + ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + '_controller' => UrlAliasRouter::VIEW_ACTION, + 'locationId' => $locationId, + 'contentId' => 456, + 'viewType' => ViewManager::VIEW_TYPE_FULL, + 'layout' => true, + ]; + $request = $this->getRequestByPathInfo($path); + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestLocationCaseRedirectWithRootLocation() + { + $rootLocationId = 123; + $this->resetConfigResolver(); + $this->configResolver + ->expects($this->any()) + ->method('getParameter') + ->will( + $this->returnValueMap( + [ + ['url_alias_router', null, null, true], + ] + ) + ); + $this->router->setRootLocationId($rootLocationId); + + $prefix = '/root/prefix'; + $this->urlALiasGenerator + ->expects($this->exactly(2)) + ->method('getPathPrefixByRootLocationId') + ->with($rootLocationId) + ->will($this->returnValue($prefix)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('loadLocation') + ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); + + $locationId = 789; + $path = '/foo/bar'; + $requestedPath = '/Foo/Bar'; + $urlAlias = new URLAlias( + [ + 'destination' => $locationId, + 'path' => $prefix . $path, + 'type' => URLAlias::LOCATION, + 'isHistory' => false, + ] + ); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($prefix . $requestedPath) + ->will($this->returnValue($urlAlias)); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + '_controller' => UrlAliasRouter::VIEW_ACTION, + 'locationId' => $locationId, + 'contentId' => 456, + 'viewType' => ViewManager::VIEW_TYPE_FULL, + 'layout' => true, + 'semanticPathinfo' => $path, + 'needsRedirect' => true, + ]; + $request = $this->getRequestByPathInfo($requestedPath); + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestLocationCaseRedirectWithRootRootLocation() + { + $rootLocationId = 123; + $this->resetConfigResolver(); + $this->configResolver + ->expects($this->any()) + ->method('getParameter') + ->will( + $this->returnValueMap( + [ + ['url_alias_router', null, null, true], + ] + ) + ); + $this->router->setRootLocationId($rootLocationId); + + $prefix = '/'; + $this->urlALiasGenerator + ->expects($this->exactly(2)) + ->method('getPathPrefixByRootLocationId') + ->with($rootLocationId) + ->will($this->returnValue($prefix)); + + $locationId = 789; + $path = '/foo/bar'; + $requestedPath = '/Foo/Bar'; + $urlAlias = new URLAlias( + [ + 'destination' => $locationId, + 'path' => $path, + 'type' => URLAlias::LOCATION, + 'isHistory' => false, + ] + ); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($requestedPath) + ->will($this->returnValue($urlAlias)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('loadLocation') + ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + '_controller' => UrlAliasRouter::VIEW_ACTION, + 'locationId' => $locationId, + 'contentId' => 456, + 'viewType' => ViewManager::VIEW_TYPE_FULL, + 'layout' => true, + 'semanticPathinfo' => $path, + 'needsRedirect' => true, + ]; + $request = $this->getRequestByPathInfo($requestedPath); + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestResourceCaseRedirectWithRootLocation() + { + $rootLocationId = 123; + $this->resetConfigResolver(); + $this->configResolver + ->expects($this->any()) + ->method('getParameter') + ->will( + $this->returnValueMap( + [ + ['url_alias_router', null, null, true], + ] + ) + ); + $this->router->setRootLocationId($rootLocationId); + + $prefix = '/root/prefix'; + $this->urlALiasGenerator + ->expects($this->exactly(2)) + ->method('getPathPrefixByRootLocationId') + ->with($rootLocationId) + ->will($this->returnValue($prefix)); + + $path = '/foo/bar'; + $requestedPath = '/Foo/Bar'; + $urlAlias = new URLAlias( + [ + 'destination' => '/content/search', + 'path' => $prefix . $path, + 'type' => URLAlias::RESOURCE, + 'isHistory' => false, + ] + ); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($prefix . $requestedPath) + ->will($this->returnValue($urlAlias)); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + 'semanticPathinfo' => $path, + 'needsRedirect' => true, + ]; + $request = $this->getRequestByPathInfo($requestedPath); + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestVirtualCaseRedirectWithRootLocation() + { + $rootLocationId = 123; + $this->resetConfigResolver(); + $this->configResolver + ->expects($this->any()) + ->method('getParameter') + ->will( + $this->returnValueMap( + [ + ['url_alias_router', null, null, true], + ] + ) + ); + $this->router->setRootLocationId($rootLocationId); + + $prefix = '/root/prefix'; + $this->urlALiasGenerator + ->expects($this->exactly(2)) + ->method('getPathPrefixByRootLocationId') + ->with($rootLocationId) + ->will($this->returnValue($prefix)); + + $path = '/foo/bar'; + $requestedPath = '/Foo/Bar'; + $urlAlias = new URLAlias( + [ + 'path' => $prefix . $path, + 'type' => URLAlias::VIRTUAL, + ] + ); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($prefix . $requestedPath) + ->will($this->returnValue($urlAlias)); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + 'semanticPathinfo' => $path, + 'needsRedirect' => true, + ]; + $request = $this->getRequestByPathInfo($requestedPath); + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestWithRootLocationAndExclusion() + { + $this->resetConfigResolver(); + $this->configResolver + ->expects($this->any()) + ->method('getParameter') + ->will( + $this->returnValueMap( + [ + ['url_alias_router', null, null, true], + ['content.tree_root.location_id', null, null, 123], + ['content.tree_root.excluded_uri_prefixes', null, null, ['/shared/content']], + ] + ) + ); + $this->router->setRootLocationId(123); + + $pathInfo = '/shared/content/foo-bar'; + $destinationId = 789; + $this->urlALiasGenerator + ->expects($this->any()) + ->method('isUriPrefixExcluded') + ->with($pathInfo) + ->will($this->returnValue(true)); + + $urlAlias = new URLAlias( + [ + 'path' => $pathInfo, + 'type' => UrlAlias::LOCATION, + 'destination' => $destinationId, + 'isHistory' => false, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('loadLocation') + ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + '_controller' => UrlAliasRouter::VIEW_ACTION, + 'locationId' => $destinationId, + 'contentId' => 456, + 'viewType' => ViewManager::VIEW_TYPE_FULL, + 'layout' => true, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + } +} + +class_alias(UrlAliasRouterTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Routing\UrlAliasRouterTest'); diff --git a/tests/bundle/Core/SiteAccess/Config/IOConfigResolverTest.php b/tests/bundle/Core/SiteAccess/Config/IOConfigResolverTest.php new file mode 100644 index 0000000000..127aa52df6 --- /dev/null +++ b/tests/bundle/Core/SiteAccess/Config/IOConfigResolverTest.php @@ -0,0 +1,129 @@ +configResolver = $this->createMock(ConfigResolverInterface::class); + $this->siteAccessService = $this->createMock(SiteAccessService::class); + } + + public function testGetUrlPrefix(): void + { + $this->siteAccessService + ->method('getCurrent') + ->willReturn(new SiteAccess('demo_site')); + + $this->configResolver + ->method('hasParameter') + ->with('io.url_prefix', null, 'demo_site') + ->willReturn(true); + $this->configResolver + ->method('getParameter') + ->willReturnMap([ + ['io.url_prefix', null, 'demo_site', '$var_dir$/demo_site/$storage_dir$'], + ['var_dir', self::DEFAULT_NAMESPACE, 'demo_site', 'var'], + ['storage_dir', self::DEFAULT_NAMESPACE, 'demo_site', 'storage'], + ]); + + $complexConfigProcessor = new ComplexConfigProcessor( + $this->configResolver, + $this->siteAccessService + ); + + $ioConfigResolver = new IOConfigResolver( + $complexConfigProcessor + ); + + $this->assertEquals('var/demo_site/storage', $ioConfigResolver->getUrlPrefix()); + } + + public function testGetLegacyUrlPrefix(): void + { + $this->siteAccessService + ->method('getCurrent') + ->willReturn(new SiteAccess('demo_site')); + + $this->configResolver + ->method('hasParameter') + ->with('io.legacy_url_prefix', null, 'demo_site') + ->willReturn(true); + $this->configResolver + ->method('getParameter') + ->willReturnMap([ + ['io.legacy_url_prefix', null, 'demo_site', '$var_dir$/demo_site/$storage_dir$'], + ['var_dir', self::DEFAULT_NAMESPACE, 'demo_site', 'var'], + ['storage_dir', self::DEFAULT_NAMESPACE, 'demo_site', 'legacy_storage'], + ]); + + $complexConfigProcessor = new ComplexConfigProcessor( + $this->configResolver, + $this->siteAccessService + ); + + $ioConfigResolver = new IOConfigResolver( + $complexConfigProcessor + ); + + $this->assertEquals('var/demo_site/legacy_storage', $ioConfigResolver->getLegacyUrlPrefix()); + } + + public function testGetRootDir(): void + { + $this->siteAccessService + ->method('getCurrent') + ->willReturn(new SiteAccess('demo_site')); + + $this->configResolver + ->method('hasParameter') + ->with('io.root_dir', null, 'demo_site') + ->willReturn(true); + $this->configResolver + ->method('getParameter') + ->willReturnMap([ + ['io.root_dir', null, 'demo_site', '/path/to/ibexa/web/$var_dir$/demo_site/$storage_dir$'], + ['var_dir', self::DEFAULT_NAMESPACE, 'demo_site', 'var'], + ['storage_dir', self::DEFAULT_NAMESPACE, 'demo_site', 'legacy_storage'], + ]); + + $complexConfigProcessor = new ComplexConfigProcessor( + $this->configResolver, + $this->siteAccessService + ); + + $ioConfigResolver = new IOConfigResolver( + $complexConfigProcessor + ); + + $this->assertEquals('/path/to/ibexa/web/var/demo_site/legacy_storage', $ioConfigResolver->getRootDir()); + } +} + +class_alias(IOConfigResolverTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\SiteAccess\Config\IOConfigResolverTest'); diff --git a/tests/bundle/Core/SiteAccess/MatcherBuilderTest.php b/tests/bundle/Core/SiteAccess/MatcherBuilderTest.php new file mode 100644 index 0000000000..bbbfa0ae4c --- /dev/null +++ b/tests/bundle/Core/SiteAccess/MatcherBuilderTest.php @@ -0,0 +1,81 @@ +siteAccessMatcherRegistry = $this->createMock(SiteAccessMatcherRegistryInterface::class); + } + + public function testBuildMatcherNoService() + { + $this->siteAccessMatcherRegistry + ->expects($this->never()) + ->method('getMatcher'); + $matcherBuilder = new MatcherBuilder($this->siteAccessMatcherRegistry); + $matcher = $this->createMock(Matcher::class); + $builtMatcher = $matcherBuilder->buildMatcher('\\' . get_class($matcher), [], new SimplifiedRequest()); + $this->assertInstanceOf(get_class($matcher), $builtMatcher); + } + + public function testBuildMatcherServiceWrongInterface() + { + $this->expectException(\TypeError::class); + + $serviceId = 'foo'; + $this->siteAccessMatcherRegistry + ->expects($this->once()) + ->method('getMatcher') + ->with($serviceId) + ->will($this->returnValue($this->createMock(Matcher::class))); + $matcherBuilder = new MatcherBuilder($this->siteAccessMatcherRegistry); + $matcherBuilder->buildMatcher("@$serviceId", [], new SimplifiedRequest()); + } + + public function testBuildMatcherService() + { + $serviceId = 'foo'; + $matcher = $this->createMock(CoreMatcher::class); + $this->siteAccessMatcherRegistry + ->expects($this->once()) + ->method('getMatcher') + ->with($serviceId) + ->will($this->returnValue($matcher)); + + $matchingConfig = ['foo' => 'bar']; + $request = new SimplifiedRequest(); + $matcher + ->expects($this->once()) + ->method('setMatchingConfiguration') + ->with($matchingConfig); + $matcher + ->expects($this->once()) + ->method('setRequest') + ->with($request); + + $matcherBuilder = new MatcherBuilder($this->siteAccessMatcherRegistry); + $matcherBuilder->buildMatcher("@$serviceId", $matchingConfig, $request); + } +} + +class_alias(MatcherBuilderTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\SiteAccess\MatcherBuilderTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/SiteAccess/SiteAccessMatcherRegistryTest.php b/tests/bundle/Core/SiteAccess/SiteAccessMatcherRegistryTest.php similarity index 82% rename from eZ/Bundle/EzPublishCoreBundle/Tests/SiteAccess/SiteAccessMatcherRegistryTest.php rename to tests/bundle/Core/SiteAccess/SiteAccessMatcherRegistryTest.php index 112323e778..167f30e62c 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/SiteAccess/SiteAccessMatcherRegistryTest.php +++ b/tests/bundle/Core/SiteAccess/SiteAccessMatcherRegistryTest.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Bundle\EzPublishCoreBundle\Tests\SiteAccess; +namespace Ibexa\Tests\Bundle\Core\SiteAccess; -use eZ\Bundle\EzPublishCoreBundle\SiteAccess\Matcher; -use eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessMatcherRegistry; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; +use Ibexa\Bundle\Core\SiteAccess\Matcher; +use Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistry; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; use PHPUnit\Framework\TestCase; class SiteAccessMatcherRegistryTest extends TestCase @@ -59,3 +59,5 @@ protected function getMatcherMock(): Matcher return $this->createMock(Matcher::class); } } + +class_alias(SiteAccessMatcherRegistryTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\SiteAccess\SiteAccessMatcherRegistryTest'); diff --git a/tests/bundle/Core/Translation/GlobCollectorTest.php b/tests/bundle/Core/Translation/GlobCollectorTest.php new file mode 100644 index 0000000000..059bc6c0f1 --- /dev/null +++ b/tests/bundle/Core/Translation/GlobCollectorTest.php @@ -0,0 +1,28 @@ +collect(); + self::assertCount(3, $files); + foreach ($files as $file) { + self::assertContains($file['domain'], ['messages', 'dashboard']); + self::assertContains($file['locale'], ['fr', 'ach_UG']); + self::assertEquals('xlf', $file['format']); + } + } +} + +class_alias(GlobCollectorTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\Translation\GlobCollectorTest'); diff --git a/eZ/Bundle/EzPublishCoreBundle/Tests/URLChecker/URLCheckerTest.php b/tests/bundle/Core/URLChecker/URLCheckerTest.php similarity index 83% rename from eZ/Bundle/EzPublishCoreBundle/Tests/URLChecker/URLCheckerTest.php rename to tests/bundle/Core/URLChecker/URLCheckerTest.php index 5468650720..3290c6e75c 100644 --- a/eZ/Bundle/EzPublishCoreBundle/Tests/URLChecker/URLCheckerTest.php +++ b/tests/bundle/Core/URLChecker/URLCheckerTest.php @@ -4,25 +4,25 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishCoreBundle\Tests\URLChecker; - -use eZ\Bundle\EzPublishCoreBundle\URLChecker\URLChecker; -use eZ\Bundle\EzPublishCoreBundle\URLChecker\URLHandlerInterface; -use eZ\Bundle\EzPublishCoreBundle\URLChecker\URLHandlerRegistryInterface; -use eZ\Publish\API\Repository\URLService; -use eZ\Publish\API\Repository\Values\URL\SearchResult; -use eZ\Publish\API\Repository\Values\URL\URL; -use eZ\Publish\API\Repository\Values\URL\URLQuery; -use eZ\Publish\API\Repository\Values\URL\URLUpdateStruct; +namespace Ibexa\Tests\Bundle\Core\URLChecker; + +use Ibexa\Bundle\Core\URLChecker\URLChecker; +use Ibexa\Bundle\Core\URLChecker\URLHandlerInterface; +use Ibexa\Bundle\Core\URLChecker\URLHandlerRegistryInterface; +use Ibexa\Contracts\Core\Repository\URLService; +use Ibexa\Contracts\Core\Repository\Values\URL\SearchResult; +use Ibexa\Contracts\Core\Repository\Values\URL\URL; +use Ibexa\Contracts\Core\Repository\Values\URL\URLQuery; +use Ibexa\Contracts\Core\Repository\Values\URL\URLUpdateStruct; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; class URLCheckerTest extends TestCase { - /** @var \eZ\Publish\API\Repository\URLService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\URLService|\PHPUnit\Framework\MockObject\MockObject */ private $urlService; - /** @var \eZ\Bundle\EzPublishCoreBundle\URLChecker\URLHandlerRegistryInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Bundle\Core\URLChecker\URLHandlerRegistryInterface|\PHPUnit\Framework\MockObject\MockObject */ private $handlerRegistry; /** @var \Psr\Log\LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */ @@ -153,7 +153,7 @@ private function createGroupedUrls(array $schemes, $n = 10) } /** - * @return \eZ\Bundle\EzPublishCoreBundle\URLChecker\URLChecker + * @return \Ibexa\Bundle\Core\URLChecker\URLChecker */ private function createUrlChecker() { @@ -166,3 +166,5 @@ private function createUrlChecker() return $urlChecker; } } + +class_alias(URLCheckerTest::class, 'eZ\Bundle\EzPublishCoreBundle\Tests\URLChecker\URLCheckerTest'); diff --git a/tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php b/tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php new file mode 100644 index 0000000000..23cc5756cc --- /dev/null +++ b/tests/bundle/Debug/Collector/IbexaCoreCollectorTest.php @@ -0,0 +1,170 @@ +mainCollector = new IbexaCoreCollector(); + } + + public function testAddGetCollector() + { + $collector = $this->getDataCollectorMock(); + $name = 'foobar'; + $collector + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue($name)); + + $this->mainCollector->addCollector($collector); + $this->assertSame($collector, $this->mainCollector->getCollector($name)); + } + + public function testGetInvalidCollector() + { + $this->expectException(\InvalidArgumentException::class); + + $collector = $this->getDataCollectorMock(); + $this->mainCollector->addCollector($collector); + $this->assertSame($collector, $this->mainCollector->getCollector('foo')); + } + + public function testGetAllCollectors() + { + $collector1 = $this->getDataCollectorMock(); + $nameCollector1 = 'collector1'; + $collector1 + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue($nameCollector1)); + $collector2 = $this->getDataCollectorMock(); + $nameCollector2 = 'collector2'; + $collector2 + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue($nameCollector2)); + + $allCollectors = [ + $nameCollector1 => $collector1, + $nameCollector2 => $collector2, + ]; + + foreach ($allCollectors as $name => $collector) { + $this->mainCollector->addCollector($collector); + } + + $this->assertSame($allCollectors, $this->mainCollector->getAllCollectors()); + } + + public function testGetToolbarTemplateNothing() + { + $collector = $this->getDataCollectorMock(); + $name = 'foobar'; + $collector + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue($name)); + $this->mainCollector->addCollector($collector); + $this->assertNull($this->mainCollector->getToolbarTemplate($name)); + } + + public function testGetToolbarTemplate() + { + $collector = $this->getDataCollectorMock(); + $name = 'foobar'; + $collector + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue($name)); + $toolbarTemplate = 'toolbar.html.twig'; + + $this->mainCollector->addCollector($collector, 'foo', $toolbarTemplate); + $this->assertSame($toolbarTemplate, $this->mainCollector->getToolbarTemplate($name)); + } + + public function testGetPanelTemplateNothing() + { + $collector = $this->getDataCollectorMock(); + $name = 'foobar'; + $collector + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue($name)); + $this->mainCollector->addCollector($collector); + $this->assertNull($this->mainCollector->getPanelTemplate($name)); + } + + public function testGetPanelTemplate() + { + $collector = $this->getDataCollectorMock(); + $name = 'foobar'; + $collector + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue($name)); + $panelTemplate = 'toolbar.html.twig'; + + $this->mainCollector->addCollector($collector, $panelTemplate, 'foo'); + $this->assertSame($panelTemplate, $this->mainCollector->getPanelTemplate($name)); + } + + public function testCollect() + { + $collector1 = $this->getDataCollectorMock(); + $nameCollector1 = 'collector1'; + $collector1 + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue($nameCollector1)); + $collector2 = $this->getDataCollectorMock(); + $nameCollector2 = 'collector2'; + $collector2 + ->expects($this->once()) + ->method('getName') + ->will($this->returnValue($nameCollector2)); + + $allCollectors = [ + $nameCollector1 => $collector1, + $nameCollector2 => $collector2, + ]; + + $request = new Request(); + $response = new Response(); + $exception = new Exception(); + + /** @var \PHPUnit\Framework\MockObject\MockObject */ + foreach ($allCollectors as $name => $collector) { + $this->mainCollector->addCollector($collector); + $collector + ->expects($this->once()) + ->method('collect') + ->with($request, $response, $exception); + } + + $this->mainCollector->collect($request, $response, $exception); + } + + protected function getDataCollectorMock() + { + return $this->createMock(DataCollectorInterface::class); + } +} + +class_alias(IbexaCoreCollectorTest::class, 'eZ\Bundle\EzPublishDebugBundle\Tests\Collector\EzPublishCoreCollectorTest'); diff --git a/tests/bundle/Debug/DependencyInjection/Compiler/DataCollectorPassTest.php b/tests/bundle/Debug/DependencyInjection/Compiler/DataCollectorPassTest.php new file mode 100644 index 0000000000..c42e41f2e2 --- /dev/null +++ b/tests/bundle/Debug/DependencyInjection/Compiler/DataCollectorPassTest.php @@ -0,0 +1,51 @@ +setDefinition(IbexaCoreCollector::class, new Definition()); + } + + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new DataCollectorPass()); + } + + public function testAddCollector() + { + $panelTemplate = 'panel.html.twig'; + $toolbarTemplate = 'toolbar.html.twig'; + $definition = new Definition(); + $definition->addTag( + 'ibexa.debug.data_collector', + ['panelTemplate' => $panelTemplate, 'toolbarTemplate' => $toolbarTemplate] + ); + + $serviceId = 'service_id'; + $this->setDefinition($serviceId, $definition); + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + IbexaCoreCollector::class, + 'addCollector', + [new Reference($serviceId), $panelTemplate, $toolbarTemplate] + ); + } +} + +class_alias(DataCollectorPassTest::class, 'eZ\Bundle\EzPublishDebugBundle\Tests\DependencyInjection\Compiler\DataCollectorPassTest'); diff --git a/tests/bundle/IO/DependencyInjection/Compiler/IOConfigurationPassTest.php b/tests/bundle/IO/DependencyInjection/Compiler/IOConfigurationPassTest.php new file mode 100644 index 0000000000..78d0c633e3 --- /dev/null +++ b/tests/bundle/IO/DependencyInjection/Compiler/IOConfigurationPassTest.php @@ -0,0 +1,141 @@ +container->setParameter('ibexa.io.metadata_handlers', []); + $this->container->setParameter('ibexa.io.binarydata_handlers', []); + + $this->container->setDefinition('ibexa.core.io.binarydata_handler.registry', new Definition()); + $this->container->setDefinition('ibexa.core.io.metadata_handler.registry', new Definition()); + $this->container->setDefinition('ibexa.core.io.binarydata_handler.flysystem.default', new Definition()); + $this->container->setDefinition('ibexa.core.io.metadata_handler.flysystem.default', new Definition()); + } + + protected function registerCompilerPass(ContainerBuilder $container): void + { + $this->metadataConfigurationFactoryMock = $this->createMock(ConfigurationFactory::class); + $this->binarydataConfigurationFactoryMock = $this->createMock(ConfigurationFactory::class); + + $container->addCompilerPass( + new IOConfigurationPass( + new ArrayObject( + ['test_handler' => $this->metadataConfigurationFactoryMock] + ), + new ArrayObject( + ['test_handler' => $this->binarydataConfigurationFactoryMock] + ) + ) + ); + } + + /** + * Tests that the default handlers are available when nothing is configured. + */ + public function testDefaultHandlers() + { + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'ibexa.core.io.binarydata_handler.registry', + 'setHandlersMap', + [['default' => 'ibexa.core.io.binarydata_handler.flysystem.default']] + ); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'ibexa.core.io.metadata_handler.registry', + 'setHandlersMap', + [['default' => 'ibexa.core.io.metadata_handler.flysystem.default']] + ); + } + + public function testBinarydataHandler() + { + $this->container->setParameter( + 'ibexa.io.binarydata_handlers', + ['my_handler' => ['name' => 'my_handler', 'type' => 'test_handler']] + ); + + $this->binarydataConfigurationFactoryMock + ->expects($this->once()) + ->method('getParentServiceId') + ->will($this->returnValue('test.io.binarydata_handler.test_handler')); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithParent( + 'test.io.binarydata_handler.test_handler.my_handler', + 'test.io.binarydata_handler.test_handler' + ); + } + + public function testMetadataHandler() + { + $this->container->setParameter( + 'ibexa.io.metadata_handlers', + ['my_handler' => ['name' => 'my_handler', 'type' => 'test_handler']] + ); + + $this->metadataConfigurationFactoryMock + ->expects($this->once()) + ->method('getParentServiceId') + ->will($this->returnValue('test.io.metadata_handler.test_handler')); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithParent( + 'test.io.metadata_handler.test_handler.my_handler', + 'test.io.metadata_handler.test_handler' + ); + } + + public function testUnknownMetadataHandler() + { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('Unknown handler'); + + $this->container->setParameter( + 'ibexa.io.metadata_handlers', + ['test' => ['type' => 'unknown']] + ); + + $this->compile(); + } + + public function testUnknownBinarydataHandler() + { + $this->expectException(InvalidConfigurationException::class); + $this->expectExceptionMessage('Unknown handler'); + + $this->container->setParameter( + 'ibexa.io.binarydata_handlers', + ['test' => ['type' => 'unknown']] + ); + + $this->compile(); + } +} + +class_alias(IOConfigurationPassTest::class, 'eZ\Bundle\EzPublishIOBundle\Tests\DependencyInjection\Compiler\IOConfigurationPassTest'); diff --git a/eZ/Bundle/EzPublishIOBundle/Tests/DependencyInjection/ConfigurationFactory/BaseFlysystemTest.php b/tests/bundle/IO/DependencyInjection/ConfigurationFactory/BaseFlysystemTest.php similarity index 84% rename from eZ/Bundle/EzPublishIOBundle/Tests/DependencyInjection/ConfigurationFactory/BaseFlysystemTest.php rename to tests/bundle/IO/DependencyInjection/ConfigurationFactory/BaseFlysystemTest.php index 45334d05d4..bb6236d146 100644 --- a/eZ/Bundle/EzPublishIOBundle/Tests/DependencyInjection/ConfigurationFactory/BaseFlysystemTest.php +++ b/tests/bundle/IO/DependencyInjection/ConfigurationFactory/BaseFlysystemTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishIOBundle\Tests\DependencyInjection\ConfigurationFactory; +namespace Ibexa\Tests\Bundle\IO\DependencyInjection\ConfigurationFactory; -use eZ\Bundle\EzPublishIOBundle\Tests\DependencyInjection\ConfigurationFactoryTest; +use Ibexa\Tests\Bundle\IO\DependencyInjection\ConfigurationFactoryTest; use Symfony\Component\DependencyInjection\Definition; use Symfony\Component\DependencyInjection\Reference; @@ -51,3 +51,5 @@ public function validateConfiguredContainer() ); } } + +class_alias(BaseFlysystemTest::class, 'eZ\Bundle\EzPublishIOBundle\Tests\DependencyInjection\ConfigurationFactory\BaseFlysystemTest'); diff --git a/tests/bundle/IO/DependencyInjection/ConfigurationFactory/BinarydataHandler/FlysystemTest.php b/tests/bundle/IO/DependencyInjection/ConfigurationFactory/BinarydataHandler/FlysystemTest.php new file mode 100644 index 0000000000..fbfea92f59 --- /dev/null +++ b/tests/bundle/IO/DependencyInjection/ConfigurationFactory/BinarydataHandler/FlysystemTest.php @@ -0,0 +1,28 @@ + 'doctrine.dbal.test_connection']; + } + + /** + * Lets you test the handler definition after it was configured. + * + * Use the assertContainer* methods from matthiasnoback/SymfonyDependencyInjectionTest. + * + * @param string $handlerServiceId id of the service that was registered by the compiler pass + */ + public function validateConfiguredHandler($handlerServiceId) + { + self::assertContainerBuilderHasServiceDefinitionWithArgument( + $handlerServiceId, + 0, + 'doctrine.dbal.test_connection' + ); + } +} + +class_alias(LegacyDFSClusterTest::class, 'eZ\Bundle\EzPublishIOBundle\Tests\DependencyInjection\ConfigurationFactory\MetadataHandler\LegacyDFSClusterTest'); diff --git a/eZ/Bundle/EzPublishIOBundle/Tests/DependencyInjection/ConfigurationFactoryTest.php b/tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php similarity index 92% rename from eZ/Bundle/EzPublishIOBundle/Tests/DependencyInjection/ConfigurationFactoryTest.php rename to tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php index e773d8b12c..fd7c0d0a1a 100644 --- a/eZ/Bundle/EzPublishIOBundle/Tests/DependencyInjection/ConfigurationFactoryTest.php +++ b/tests/bundle/IO/DependencyInjection/ConfigurationFactoryTest.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishIOBundle\Tests\DependencyInjection; +namespace Ibexa\Tests\Bundle\IO\DependencyInjection; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractContainerBuilderTestCase; use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; @@ -19,7 +19,7 @@ */ abstract class ConfigurationFactoryTest extends AbstractContainerBuilderTestCase { - /** @var \eZ\Bundle\EzPublishIOBundle\DependencyInjection\ConfigurationFactory */ + /** @var \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory */ protected $factory; protected function setUp(): void @@ -83,7 +83,7 @@ private function registerHandler($name) /** * Returns an instance of the tested factory. * - * @return \eZ\Bundle\EzPublishIOBundle\DependencyInjection\ConfigurationFactory + * @return \Ibexa\Bundle\IO\DependencyInjection\ConfigurationFactory */ abstract public function provideTestedFactory(); @@ -126,3 +126,5 @@ public function validateConfiguredContainer() { } } + +class_alias(ConfigurationFactoryTest::class, 'eZ\Bundle\EzPublishIOBundle\Tests\DependencyInjection\ConfigurationFactoryTest'); diff --git a/tests/bundle/IO/DependencyInjection/IbexaIOExtensionTest.php b/tests/bundle/IO/DependencyInjection/IbexaIOExtensionTest.php new file mode 100644 index 0000000000..42585c5fab --- /dev/null +++ b/tests/bundle/IO/DependencyInjection/IbexaIOExtensionTest.php @@ -0,0 +1,111 @@ +addMetadataHandlerFactory('flysystem', new ConfigurationFactory\MetadataHandler\Flysystem()); + $extension->addBinarydataHandlerFactory('flysystem', new ConfigurationFactory\BinarydataHandler\Flysystem()); + + return [$extension]; + } + + public function testParametersWithoutConfiguration() + { + $this->load(); + + $this->assertContainerBuilderHasParameter('ibexa.io.metadata_handlers', []); + $this->assertContainerBuilderHasParameter('ibexa.io.binarydata_handlers', []); + } + + public function testParametersWithMetadataHandler() + { + $config = [ + 'metadata_handlers' => [ + 'my_metadata_handler' => ['flysystem' => ['adapter' => 'my_adapter']], + ], + ]; + $this->load($config); + + $this->assertContainerBuilderHasParameter('ibexa.io.binarydata_handlers', []); + $this->assertContainerBuilderHasParameter( + 'ibexa.io.metadata_handlers', + ['my_metadata_handler' => ['name' => 'my_metadata_handler', 'type' => 'flysystem', 'adapter' => 'my_adapter']] + ); + } + + public function testParametersWithBinarydataHandler() + { + $config = [ + 'binarydata_handlers' => [ + 'my_binarydata_handler' => ['flysystem' => ['adapter' => 'my_adapter']], + ], + ]; + $this->load($config); + + $this->assertContainerBuilderHasParameter('ibexa.io.metadata_handlers', []); + $this->assertContainerBuilderHasParameter( + 'ibexa.io.binarydata_handlers', + ['my_binarydata_handler' => ['name' => 'my_binarydata_handler', 'type' => 'flysystem', 'adapter' => 'my_adapter']] + ); + } + + public function testUrlPrefixConfigurationIsUsedToDecorateUrl(): void + { + $this->container->registerExtension( + new IbexaCoreExtension( + [ + new Parser\IO(new ComplexSettingParser()), + ] + ) + ); + $this->container->prependExtensionConfig( + 'ibexa', + Yaml::parseFile(self::FIXTURES_DIR . '/url_prefix_test_config.yaml')['ibexa'] + ); + $this->buildMinimalContainerForUrlPrefixTest(); + + $decorator = $this->container->get(AbsolutePrefix::class); + + self::assertEquals( + 'http://static.example.com/my/image.png', + $decorator->decorate('my/image.png') + ); + } + + private function buildMinimalContainerForUrlPrefixTest(): void + { + // unrelated, but needed Container configuration + $this->container->setParameter('kernel.environment', 'dev'); + $this->container->setParameter('kernel.debug', true); + $this->container->setParameter('kernel.project_dir', self::FIXTURES_DIR); + $this->container->setParameter('kernel.cache_dir', self::FIXTURES_DIR . '/cache'); + + $this->container->addCompilerPass(new ChainConfigResolverPass()); + $this->container->addCompilerPass(new SetAllServicesPublicPass()); + + $this->container->compile(); + } +} + +class_alias(IbexaIOExtensionTest::class, 'eZ\Bundle\EzPublishIOBundle\Tests\DependencyInjection\EzPublishIOExtensionTest'); diff --git a/eZ/Bundle/EzPublishIOBundle/Tests/EventListener/StreamFileListenerTest.php b/tests/bundle/IO/EventListener/StreamFileListenerTest.php similarity index 88% rename from eZ/Bundle/EzPublishIOBundle/Tests/EventListener/StreamFileListenerTest.php rename to tests/bundle/IO/EventListener/StreamFileListenerTest.php index 19c2dff4ac..89f0072961 100644 --- a/eZ/Bundle/EzPublishIOBundle/Tests/EventListener/StreamFileListenerTest.php +++ b/tests/bundle/IO/EventListener/StreamFileListenerTest.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Bundle\EzPublishIOBundle\Tests\EventListener; +namespace Ibexa\Tests\Bundle\IO\EventListener; use DateTime; -use eZ\Bundle\EzPublishIOBundle\BinaryStreamResponse; -use eZ\Bundle\EzPublishIOBundle\EventListener\StreamFileListener; -use eZ\Publish\Core\IO\IOConfigProvider; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\BinaryFile; +use Ibexa\Bundle\IO\BinaryStreamResponse; +use Ibexa\Bundle\IO\EventListener\StreamFileListener; +use Ibexa\Core\IO\IOConfigProvider; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\BinaryFile; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Event\RequestEvent; @@ -19,13 +19,13 @@ class StreamFileListenerTest extends TestCase { - /** @var \eZ\Bundle\EzPublishIOBundle\EventListener\StreamFileListener */ + /** @var \Ibexa\Bundle\IO\EventListener\StreamFileListener */ private $eventListener; - /** @var \eZ\Publish\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ private $ioServiceMock; - /** @var \eZ\Publish\Core\IO\IOConfigProvider|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\IO\IOConfigProvider|\PHPUnit\Framework\MockObject\MockObject */ private $ioConfigResolverMock; protected function setUp(): void @@ -163,3 +163,5 @@ protected function createEvent($request) return $event; } } + +class_alias(StreamFileListenerTest::class, 'eZ\Bundle\EzPublishIOBundle\Tests\EventListener\StreamFileListenerTest'); diff --git a/tests/bundle/IO/Migration/FileMigratorTest.php b/tests/bundle/IO/Migration/FileMigratorTest.php index a8a6149b35..34e1dcb176 100644 --- a/tests/bundle/IO/Migration/FileMigratorTest.php +++ b/tests/bundle/IO/Migration/FileMigratorTest.php @@ -8,34 +8,35 @@ namespace Ibexa\Tests\Bundle\IO\Migration; -use eZ\Bundle\EzPublishIOBundle\ApiLoader\HandlerRegistry; -use eZ\Bundle\EzPublishIOBundle\Migration\FileMigrator\FileMigrator; -use eZ\Publish\Core\IO\IOBinarydataHandler; -use eZ\Publish\Core\IO\IOMetadataHandler; -use eZ\Publish\SPI\IO\BinaryFile; +use DateTime; +use Ibexa\Bundle\IO\ApiLoader\HandlerRegistry; +use Ibexa\Bundle\IO\Migration\FileMigrator\FileMigrator; +use Ibexa\Contracts\Core\IO\BinaryFile; +use Ibexa\Core\IO\IOBinarydataHandler; +use Ibexa\Core\IO\IOMetadataHandler; use PHPUnit\Framework\TestCase; final class FileMigratorTest extends TestCase { - /** @var \eZ\Bundle\EzPublishIOBundle\ApiLoader\HandlerRegistry|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Bundle\IO\ApiLoader\HandlerRegistry|\PHPUnit\Framework\MockObject\MockObject */ private $metadataHandlerRegistry; - /** @var \eZ\Bundle\EzPublishIOBundle\ApiLoader\HandlerRegistry|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Bundle\IO\ApiLoader\HandlerRegistry|\PHPUnit\Framework\MockObject\MockObject */ private $binaryHandlerRegistry; - /** @var \eZ\Bundle\EzPublishIOBundle\Migration\FileMigratorInterface */ + /** @var \Ibexa\Bundle\IO\Migration\FileMigratorInterface */ private $fileMigrator; - /** @var \eZ\Publish\Core\IO\IOMetadataHandler\Flysystem */ + /** @var \Ibexa\Core\IO\IOMetadataHandler\Flysystem */ private $metadataFlysystem; - /** @var \eZ\Publish\Core\IO\IOMetadataHandler\LegacyDFSCluster */ + /** @var \Ibexa\Core\IO\IOMetadataHandler\LegacyDFSCluster */ private $metadataLegacyDFSCluster; - /** @var \eZ\Publish\Core\IO\IOBinarydataHandler\Flysystem */ + /** @var \Ibexa\Core\IO\IOBinarydataHandler\Flysystem */ private $binaryFlysystemFrom; - /** @var \eZ\Publish\Core\IO\IOBinarydataHandler\Flysystem */ + /** @var \Ibexa\Core\IO\IOBinarydataHandler\Flysystem */ private $binaryFlysystemTo; protected function setUp(): void @@ -48,8 +49,8 @@ protected function setUp(): void $this->metadataFlysystem = $this->createMock(IOMetadataHandler\Flysystem::class); $this->metadataLegacyDFSCluster = $this->createMock(IOMetadataHandler\LegacyDFSCluster::class); - $this->binaryFlysystemFrom = $this->createMock(IOBinarydataHandler\Flysystem::class); - $this->binaryFlysystemTo = $this->createMock(IOBinarydataHandler\Flysystem::class); + $this->binaryFlysystemFrom = $this->createMock(IOBinarydataHandler::class); + $this->binaryFlysystemTo = $this->createMock(IOBinarydataHandler::class); $this->fileMigrator = new FileMigrator($this->metadataHandlerRegistry, $this->binaryHandlerRegistry); } @@ -84,7 +85,7 @@ public function testMigrateFile(): void $binaryFile = new BinaryFile(); $binaryFile->id = '1234.jpg'; - $binaryFile->mtime = new \DateTime(); + $binaryFile->mtime = new DateTime(); $binaryFile->size = 12345; $binaryFile->uri = '1/1234.jpg'; diff --git a/eZ/Bundle/EzPublishIOBundle/Tests/_fixtures/url_prefix_test_config.yaml b/tests/bundle/IO/_fixtures/url_prefix_test_config.yaml similarity index 94% rename from eZ/Bundle/EzPublishIOBundle/Tests/_fixtures/url_prefix_test_config.yaml rename to tests/bundle/IO/_fixtures/url_prefix_test_config.yaml index dd82dbb30c..10839c2a5e 100644 --- a/eZ/Bundle/EzPublishIOBundle/Tests/_fixtures/url_prefix_test_config.yaml +++ b/tests/bundle/IO/_fixtures/url_prefix_test_config.yaml @@ -1,4 +1,4 @@ -ezplatform: +ibexa: siteaccess: list: [site] default_siteaccess: site diff --git a/eZ/Publish/Core/FieldType/Tests/phppng.php b/tests/bundle/RepositoryInstaller/.gitkeep similarity index 100% rename from eZ/Publish/Core/FieldType/Tests/phppng.php rename to tests/bundle/RepositoryInstaller/.gitkeep diff --git a/tests/bundle/RepositoryInstaller/DependencyInjection/Compiler/InstallerTagPassTest.php b/tests/bundle/RepositoryInstaller/DependencyInjection/Compiler/InstallerTagPassTest.php new file mode 100644 index 0000000000..0582363709 --- /dev/null +++ b/tests/bundle/RepositoryInstaller/DependencyInjection/Compiler/InstallerTagPassTest.php @@ -0,0 +1,58 @@ +setDefinition( + InstallPlatformCommand::class, + new Definition(InstallPlatformCommand::class, ['$installers' => []]) + ); + $definition = new Definition(); + $definition->addTag( + InstallerTagPass::INSTALLER_TAG, + [ + 'type' => 'installer_type', + ] + ); + + $this->setDefinition('service_id', $definition); + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithArgument( + InstallPlatformCommand::class, + '$installers', + [ + 'installer_type' => new Reference('service_id'), + ] + ); + } + + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new InstallerTagPass()); + } +} + +class_alias(InstallerTagPassTest::class, 'EzSystems\PlatformInstallerBundleTests\DependencyInjection\Compiler\InstallerTagPassTest'); diff --git a/tests/bundle/RepositoryInstaller/DependencyInjection/IbexaInstallerExtensionTest.php b/tests/bundle/RepositoryInstaller/DependencyInjection/IbexaInstallerExtensionTest.php new file mode 100644 index 0000000000..19ff4e77d1 --- /dev/null +++ b/tests/bundle/RepositoryInstaller/DependencyInjection/IbexaInstallerExtensionTest.php @@ -0,0 +1,60 @@ +load(); + $this->assertContainerBuilderHasServiceDefinitionWithParent( + CoreInstaller::class, + DbBasedInstaller::class + ); + $this->assertContainerBuilderHasServiceDefinitionWithTag( + CoreInstaller::class, + InstallerTagPass::INSTALLER_TAG, + ['type' => 'clean'] + ); + } + + /** + * @covers \Ibexa\Bundle\RepositoryInstaller\DependencyInjection\IbexaRepositoryInstallerExtension::load + */ + public function testLoadLoadsTaggedInstallerCommand(): void + { + $this->load(); + $this->assertContainerBuilderHasServiceDefinitionWithTag( + InstallPlatformCommand::class, + 'console.command' + ); + } + + protected function getContainerExtensions(): array + { + return [ + new IbexaRepositoryInstallerExtension(), + ]; + } +} + +class_alias(IbexaInstallerExtensionTest::class, 'EzSystems\PlatformInstallerBundleTests\DependencyInjection\EzSystemsPlatformInstallerExtensionTest'); diff --git a/tests/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundleTest.php b/tests/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundleTest.php new file mode 100644 index 0000000000..323370c693 --- /dev/null +++ b/tests/bundle/RepositoryInstaller/IbexaRepositoryInstallerBundleTest.php @@ -0,0 +1,62 @@ +bundle = new IbexaRepositoryInstallerBundle(); + } + + /** + * @covers \Ibexa\Bundle\RepositoryInstaller\IbexaRepositoryInstallerBundle::build + */ + public function testBuild(): void + { + $container = new ContainerBuilder(); + $container->registerExtension(new DoctrineSchemaExtension()); + $this->bundle->build($container); + + // check if InstallerTagPass was added + self::assertNotEmpty( + array_filter( + $container->getCompilerPassConfig()->getPasses(), + static function (CompilerPassInterface $compilerPass) { + return $compilerPass instanceof InstallerTagPass; + } + ) + ); + } + + /** + * @covers \Ibexa\Bundle\RepositoryInstaller\IbexaRepositoryInstallerBundle::build + */ + public function testBuildFailsWithoutDoctrineSchemaBundle(): void + { + $container = new ContainerBuilder(); + + $this->expectException(RuntimeException::class); + $this->expectExceptionMessage('Ibexa Installer requires Doctrine Schema Bundle'); + $this->bundle->build($container); + } +} + +class_alias(IbexaRepositoryInstallerBundleTest::class, 'EzSystems\PlatformInstallerBundleTests\EzSystemsPlatformInstallerBundleTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/Integration/BaseCoreFieldTypeIntegrationTest.php b/tests/integration/Core/BaseCoreFieldTypeIntegrationTest.php similarity index 82% rename from eZ/Publish/Core/FieldType/Tests/Integration/BaseCoreFieldTypeIntegrationTest.php rename to tests/integration/Core/BaseCoreFieldTypeIntegrationTest.php index fa0184d6e5..f82d97aa64 100644 --- a/eZ/Publish/Core/FieldType/Tests/Integration/BaseCoreFieldTypeIntegrationTest.php +++ b/tests/integration/Core/BaseCoreFieldTypeIntegrationTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests\Integration; +namespace Ibexa\Tests\Integration\Core; use Doctrine\DBAL\Connection; use ErrorException; -use eZ\Publish\API\Repository\Tests\BaseTest as APIBaseTest; +use Ibexa\Tests\Integration\Core\Repository\BaseTest as APIBaseTest; /** * Base class for non-API Field Type integration tests (like Gateway w/ DBMS integration). @@ -36,7 +36,7 @@ protected function getDatabaseConnection(): Connection { try { return $this->getSetupFactory()->getServiceContainer()->get( - 'ezpublish.persistence.connection' + 'ibexa.persistence.connection' ); } catch (ErrorException $e) { self::fail( @@ -51,3 +51,5 @@ protected function getDatabaseConnection(): Connection } } } + +class_alias(BaseCoreFieldTypeIntegrationTest::class, 'eZ\Publish\Core\FieldType\Tests\Integration\BaseCoreFieldTypeIntegrationTest'); diff --git a/tests/integration/Core/BaseGatewayTest.php b/tests/integration/Core/BaseGatewayTest.php new file mode 100644 index 0000000000..22ab21975a --- /dev/null +++ b/tests/integration/Core/BaseGatewayTest.php @@ -0,0 +1,25 @@ +repository = (new Legacy())->getRepository(true); + } +} + +class_alias(BaseGatewayTest::class, 'eZ\Publish\SPI\Tests\BaseGatewayTest'); diff --git a/tests/integration/BasicKernelTest.php b/tests/integration/Core/BasicKernelTest.php similarity index 94% rename from tests/integration/BasicKernelTest.php rename to tests/integration/Core/BasicKernelTest.php index 98bc92ddc0..15ec36bbb7 100644 --- a/tests/integration/BasicKernelTest.php +++ b/tests/integration/Core/BasicKernelTest.php @@ -8,7 +8,7 @@ namespace Ibexa\Tests\Integration\Core; -use eZ\Publish\API\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Repository; use Ibexa\Contracts\Core\Test\IbexaKernelTestCase; /** diff --git a/tests/integration/Core/BinaryBase/BinaryBaseStorage/BinaryBaseStorageGatewayTest.php b/tests/integration/Core/BinaryBase/BinaryBaseStorage/BinaryBaseStorageGatewayTest.php new file mode 100644 index 0000000000..d4e8f0a2f3 --- /dev/null +++ b/tests/integration/Core/BinaryBase/BinaryBaseStorage/BinaryBaseStorageGatewayTest.php @@ -0,0 +1,95 @@ +getDatabaseConnection()); + $importer->import(new YamlFixture(__DIR__ . '/_fixtures/ezbinaryfile.yaml')); + } + + protected function getGateway(): BinaryBaseStorageGateway + { + return new DoctrineStorage($this->getDatabaseConnection()); + } + + public function testGetFileReferenceWithFixture(): void + { + $data = $this->getGateway()->getFileReferenceData(10, 1); + + $expected = [ + 'id' => 'image/a6bbf351175ad9c2f27e5b17c2c5d105.png', + 'mimeType' => 'image/png', + 'fileName' => 'test.png', + 'downloadCount' => 0, + ]; + + self::assertEquals($expected, $data); + } + + public function testStoreFileReference(): void + { + $field = new Field(); + $field->id = 123; + $field->fieldDefinitionId = 231; + $field->type = 'ezbinaryfile'; + $field->versionNo = 1; + $field->value = new FieldValue([ + 'externalData' => [ + 'id' => 'image/809c753a26e11f363cd8c14d824d162a.jpg', + 'path' => '/tmp/phpR4tNSI', + 'inputUri' => '/tmp/phpR4tNSI', + 'fileName' => '1.jpg', + 'fileSize' => '372949', + 'mimeType' => 'image/jpg', + 'uri' => '/admin/content/download/75/320?version=1', + 'downloadCount' => 0, + ], + ]); + + $versionInfo = new VersionInfo([ + 'contentInfo' => new ContentInfo([ + 'id' => 1, + ]), + 'versionNo' => 1, + ]); + + $this->getGateway()->storeFileReference($versionInfo, $field); + + $data = $this->getGateway()->getFileReferenceData(123, 1); + + $expected = [ + 'id' => 'image/809c753a26e11f363cd8c14d824d162a.jpg', + 'mimeType' => 'image/jpg', + 'fileName' => '1.jpg', + 'downloadCount' => 0, + ]; + + self::assertEquals($expected, $data); + } +} + +class_alias(BinaryBaseStorageGatewayTest::class, 'eZ\Publish\Core\FieldType\Tests\Integration\BinaryBase\BinaryBaseStorage\BinaryBaseStorageGatewayTest'); diff --git a/tests/integration/Core/BinaryBase/BinaryBaseStorage/BinaryBaseStorageTest.php b/tests/integration/Core/BinaryBase/BinaryBaseStorage/BinaryBaseStorageTest.php new file mode 100644 index 0000000000..468fcc2562 --- /dev/null +++ b/tests/integration/Core/BinaryBase/BinaryBaseStorage/BinaryBaseStorageTest.php @@ -0,0 +1,173 @@ +gateway = $this->getStorageGateway(); + $this->pathGeneratorMock = $this->createMock(PathGenerator::class); + $this->ioServiceMock = $this->createMock(IOServiceInterface::class); + $this->fileExtensionBlackListValidatorMock = $this->createMock( + FileExtensionBlackListValidator::class + ); + $this->storage = $this->getMockBuilder(BinaryBaseStorage::class) + ->onlyMethods([]) + ->setConstructorArgs( + [ + $this->gateway, + $this->ioServiceMock, + $this->pathGeneratorMock, + $this->createMock(MimeTypeDetector::class), + $this->fileExtensionBlackListValidatorMock, + ] + ) + ->getMock(); + } + + protected function getContext(): array + { + return ['context']; + } + + public function testHasFieldData(): void + { + self::assertTrue($this->storage->hasFieldData()); + } + + /** + * @dataProvider providerOfFieldData + */ + public function testStoreFieldData(VersionInfo $versionInfo, Field $field): void + { + $binaryFileCreateStruct = new BinaryFileCreateStruct([ + 'id' => 'qwerty12345', + 'size' => '372949', + 'mimeType' => 'image/jpeg', + ]); + + $this->ioServiceMock + ->expects(self::once()) + ->method('newBinaryCreateStructFromLocalFile') + ->will($this->returnValue($binaryFileCreateStruct)); + + $this->pathGeneratorMock + ->expects(self::once()) + ->method('getStoragePathForField') + ->with($field, $versionInfo) + ->willReturn('image/qwerty12345.jpg'); + + $this->ioServiceMock + ->expects(self::once()) + ->method('createBinaryFile') + ->with($binaryFileCreateStruct) + ->willReturn(new BinaryFile()); + + $this->storage->storeFieldData($versionInfo, $field, $this->getContext()); + + $this->expectNotToPerformAssertions(); + } + + /** + * @depends testStoreFieldData + * + * @dataProvider providerOfFieldData + */ + public function testCopyLegacyField(VersionInfo $versionInfo, Field $originalField): void + { + $field = clone $originalField; + $field->id = 124; + $field->versionNo = 2; + $field->value = new FieldValue([ + 'externalData' => [ + 'fileName' => '123.jpg', + 'downloadCount' => 0, + 'mimeType' => null, + 'uri' => null, + ], + ]); + + $flag = $this->storage->copyLegacyField($versionInfo, $field, $originalField, $this->getContext()); + + self::assertFalse($flag); + } + + public function providerOfFieldData(): array + { + $field = new Field(); + $field->id = 124; + $field->fieldDefinitionId = 231; + $field->type = 'ezbinaryfile'; + $field->versionNo = 1; + $field->value = new FieldValue([ + 'externalData' => [ + 'id' => 'image/aaac753a26e11f363cd8c14d824d162a.jpg', + 'path' => '/tmp/phpR4tNSV', + 'inputUri' => '/tmp/phpR4tNSV', + 'fileName' => '123.jpg', + 'fileSize' => '12345', + 'mimeType' => 'image/jpeg', + 'uri' => '/admin/content/download/75/320?version=1', + 'downloadCount' => 0, + ], + ]); + + $versionInfo = new VersionInfo([ + 'contentInfo' => new ContentInfo([ + 'id' => 235, + 'contentTypeId' => 24, + ]), + 'versionNo' => 1, + ]); + + return [ + [$versionInfo, $field], + ]; + } + + protected function getStorageGateway(): Gateway + { + return new DoctrineStorage($this->getDatabaseConnection()); + } +} + +class_alias(BinaryBaseStorageTest::class, 'eZ\Publish\Core\FieldType\Tests\Integration\BinaryBase\BinaryBaseStorage\BinaryBaseStorageTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/Integration/BinaryBase/BinaryBaseStorage/_fixtures/ezbinaryfile.yaml b/tests/integration/Core/BinaryBase/BinaryBaseStorage/_fixtures/ezbinaryfile.yaml similarity index 100% rename from eZ/Publish/Core/FieldType/Tests/Integration/BinaryBase/BinaryBaseStorage/_fixtures/ezbinaryfile.yaml rename to tests/integration/Core/BinaryBase/BinaryBaseStorage/_fixtures/ezbinaryfile.yaml diff --git a/tests/integration/Core/FieldType/FieldConstraintsStorage/FieldConstraintsStorageTest.php b/tests/integration/Core/FieldType/FieldConstraintsStorage/FieldConstraintsStorageTest.php new file mode 100644 index 0000000000..b3f26f964a --- /dev/null +++ b/tests/integration/Core/FieldType/FieldConstraintsStorage/FieldConstraintsStorageTest.php @@ -0,0 +1,169 @@ + 'AAA-000-99', + ]; + + private const EXAMPLE_FIELD_SETTINGS_UPDATED = [ + 'format' => 'aaa-999-00', + ]; + + private const EXAMPLE_VALIDATOR_CONFIGURATION = [ + 'StringLengthValidator' => [ + 'minStringLength' => 3, + 'maxStringLength' => 10, + ], + ]; + + private const EXAMPLE_VALIDATOR_CONFIGURATION_UPDATED = [ + 'StringLengthValidator' => [ + 'minStringLength' => 5, + 'maxStringLength' => 16, + ], + ]; + + public function testStorageDataIsCreatedOnContentTypeCreate(): ContentType + { + $repository = $this->getRepository(); + + $contentTypeService = $repository->getContentTypeService(); + $permissionResolver = $repository->getPermissionResolver(); + + $fieldDefCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( + self::EXAMPLE_FIELD_IDENTIFIER, + ExampleFieldType::FIELD_TYPE_IDENTIFIER + ); + + $fieldDefCreateStruct->names = ['eng-GB' => 'Example']; + $fieldDefCreateStruct->descriptions = [ + 'eng-GB' => 'Example field with external storage for field constraints', + ]; + $fieldDefCreateStruct->fieldSettings = self::EXAMPLE_FIELD_SETTINGS; + $fieldDefCreateStruct->validatorConfiguration = self::EXAMPLE_VALIDATOR_CONFIGURATION; + + $contentTypeCreateStruct = $this->createTypeCreateStruct($contentTypeService, $permissionResolver); + $contentTypeCreateStruct->addFieldDefinition($fieldDefCreateStruct); + + $contentType = $contentTypeService->createContentType($contentTypeCreateStruct, [ + $contentTypeService->loadContentTypeGroupByIdentifier('Content'), + ]); + + $contentTypeService->publishContentTypeDraft($contentType); + + $storage = $this->getExampleFieldConstraintsStorage(); + + self::assertTrue($storage->isPublished($contentType->fieldDefinitions->get(self::EXAMPLE_FIELD_IDENTIFIER)->id)); + + self::assertEquals( + new FieldTypeConstraints( + [ + 'fieldSettings' => self::EXAMPLE_FIELD_SETTINGS, + 'validators' => self::EXAMPLE_VALIDATOR_CONFIGURATION, + ] + ), + $storage->getFieldConstraintsDataIfAvailable( + $contentType->getFieldDefinition(self::EXAMPLE_FIELD_IDENTIFIER)->id + ) + ); + + return $contentTypeService->loadContentTypeByIdentifier($contentType->identifier); + } + + /** + * @depends Ibexa\Tests\Integration\Core\FieldType\FieldConstraintsStorage\FieldConstraintsStorageTest::testStorageDataIsCreatedOnContentTypeCreate + */ + public function testStorageDataIsUpdatedOnContentTypeUpdate(ContentType $contentType): ContentType + { + $repository = $this->getRepository(false); + + $contentTypeService = $repository->getContentTypeService(); + + $updateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); + $updateStruct->fieldSettings = self::EXAMPLE_FIELD_SETTINGS_UPDATED; + $updateStruct->validatorConfiguration = self::EXAMPLE_VALIDATOR_CONFIGURATION_UPDATED; + + $fieldDefinition = $contentType->getFieldDefinition(self::EXAMPLE_FIELD_IDENTIFIER); + + $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); + $contentTypeService->updateFieldDefinition($contentTypeDraft, $fieldDefinition, $updateStruct); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + $actualFieldTypeConstraints = $this + ->getExampleFieldConstraintsStorage() + ->getFieldConstraintsDataIfAvailable( + $contentType->getFieldDefinition(self::EXAMPLE_FIELD_IDENTIFIER)->id + ); + + self::assertEquals( + new FieldTypeConstraints( + [ + 'fieldSettings' => self::EXAMPLE_FIELD_SETTINGS_UPDATED, + 'validators' => self::EXAMPLE_VALIDATOR_CONFIGURATION_UPDATED, + ] + ), + $actualFieldTypeConstraints + ); + + return $contentTypeService->loadContentTypeByIdentifier($contentType->identifier); + } + + /** + * @depends Ibexa\Tests\Integration\Core\FieldType\FieldConstraintsStorage\FieldConstraintsStorageTest::testStorageDataIsUpdatedOnContentTypeUpdate + */ + public function testStorageDataIsDeletedOnContentTypeDelete(ContentType $contentType): void + { + $fieldDefinition = $contentType->getFieldDefinition(self::EXAMPLE_FIELD_IDENTIFIER); + + $storage = $this->getExampleFieldConstraintsStorage(); + self::assertTrue($storage->hasFieldConstraintsData($fieldDefinition->id)); + + $contentTypeService = $this->getRepository(false)->getContentTypeService(); + $contentTypeService->deleteContentType($contentType); + + self::assertFalse($storage->hasFieldConstraintsData($fieldDefinition->id)); + } + + private function createTypeCreateStruct( + ContentTypeService $contentTypeService, + PermissionResolver $permissionResolver + ): ContentTypeCreateStruct { + $creatorId = $this->generateId('user', $permissionResolver->getCurrentUserReference()->getUserId()); + + $typeCreateStruct = $contentTypeService->newContentTypeCreateStruct('field_constraints_storage_test'); + $typeCreateStruct->mainLanguageCode = 'eng-GB'; + $typeCreateStruct->names = ['eng-GB' => 'FieldConstraintsStorageTest']; + $typeCreateStruct->creatorId = $creatorId; + $typeCreateStruct->creationDate = $this->createDateTime(); + + return $typeCreateStruct; + } + + private function getExampleFieldConstraintsStorage(): ExampleFieldConstraintsStorage + { + /** @var \Ibexa\Tests\Integration\Core\FieldType\FieldConstraintsStorage\Stub\ExampleFieldConstraintsStorage $storage */ + $storage = $this->getSetupFactory()->getServiceContainer()->get(ExampleFieldConstraintsStorage::class); + + return $storage; + } +} diff --git a/tests/integration/Core/FieldType/FieldConstraintsStorage/Stub/ExampleFieldConstraintsStorage.php b/tests/integration/Core/FieldType/FieldConstraintsStorage/Stub/ExampleFieldConstraintsStorage.php new file mode 100644 index 0000000000..0a53c2ecc2 --- /dev/null +++ b/tests/integration/Core/FieldType/FieldConstraintsStorage/Stub/ExampleFieldConstraintsStorage.php @@ -0,0 +1,75 @@ +fieldConstraints = $fieldConstraints; + $this->published = []; + } + + public function publishFieldConstraintsData(int $fieldDefinitionId): void + { + $this->published[] = $fieldDefinitionId; + } + + public function storeFieldConstraintsData( + int $fieldDefinitionId, + int $status, + FieldTypeConstraints $fieldTypeConstraints + ): void { + $this->fieldConstraints[$fieldDefinitionId] = $fieldTypeConstraints; + } + + public function hasFieldConstraintsData( + int $fieldDefinitionId + ): bool { + return isset($this->fieldConstraints[$fieldDefinitionId]); + } + + public function isPublished(int $fieldDefinitionId): bool + { + return in_array($fieldDefinitionId, $this->published, true); + } + + public function getFieldConstraintsData( + int $fieldDefinitionId, + int $status + ): FieldTypeConstraints { + return $this->fieldConstraints[$fieldDefinitionId]; + } + + public function getFieldConstraintsDataIfAvailable( + int $fieldDefinitionId + ): ?FieldTypeConstraints { + return $this->fieldConstraints[$fieldDefinitionId] ?? null; + } + + public function deleteFieldConstraintsData(int $fieldDefinitionId, int $status): void + { + unset($this->fieldConstraints[$fieldDefinitionId]); + } +} diff --git a/tests/integration/Core/FieldType/FieldConstraintsStorage/Stub/ExampleFieldType.php b/tests/integration/Core/FieldType/FieldConstraintsStorage/Stub/ExampleFieldType.php new file mode 100644 index 0000000000..d786ac5fa9 --- /dev/null +++ b/tests/integration/Core/FieldType/FieldConstraintsStorage/Stub/ExampleFieldType.php @@ -0,0 +1,68 @@ +get(FlysystemTestAdapterInterface::class); + self::assertInstanceOf(FlysystemTestAdapterInterface::class, $adapter); + + return $adapter; + } + + protected function setUp(): void + { + parent::setUp(); + + $container = self::getContainer(); + // by default, we use InMemory "virtual" adapter for tests, + // but here we need to test real file system permissions + self::getAdapter()->useRealFileSystem(true); + + $this->binaryDataHandler = $this->getBinaryDataHandler($container); + $this->filesystem = $this->getFlysystemFilesystem($container); + } + + protected function tearDown(): void + { + $this->filesystem->deleteDirectory('/'); + } + + public function testCreateSetsCorrectPermissions(): void + { + $handle = fopen(dirname(__DIR__, 2) . '/Repository/FieldType/_fixtures/image.png', 'rob'); + try { + $binaryFileCreateStruct = new BinaryFileCreateStruct(); + $binaryFileCreateStruct->id = 'foo/image.png'; + $binaryFileCreateStruct->mimeType = 'image/png'; + $binaryFileCreateStruct->setInputStream($handle); + $this->binaryDataHandler->create($binaryFileCreateStruct); + foreach ($this->filesystem->listContents('/') as $storageAttributes) { + self::assertSame( + Visibility::PUBLIC, + $storageAttributes->visibility(), + sprintf( + 'Visibility of "%s" %s is expected to be %s', + $storageAttributes->path(), + $storageAttributes->type(), + Visibility::PUBLIC + ) + ); + } + } finally { + fclose($handle); + } + } + + private function getFlysystemFilesystem(ContainerInterface $container): FilesystemOperator + { + return $container->get('ibexa.core.io.flysystem.default_filesystem'); + } + + private function getBinaryDataHandler(ContainerInterface $container): IOBinarydataHandler + { + return $container->get('ibexa.core.io.binarydata_handler.flysystem'); + } +} diff --git a/tests/integration/Core/IO/FlysystemTestAdapter.php b/tests/integration/Core/IO/FlysystemTestAdapter.php new file mode 100644 index 0000000000..77b7cd3046 --- /dev/null +++ b/tests/integration/Core/IO/FlysystemTestAdapter.php @@ -0,0 +1,120 @@ +inMemoryAdapter = $inMemoryAdapter; + $this->localAdapter = $localAdapter; + $this->adapter = $this->inMemoryAdapter; + } + + public function useRealFileSystem(bool $useRealFilesystem): void + { + $this->adapter = $useRealFilesystem ? $this->localAdapter : $this->inMemoryAdapter; + } + + public function fileExists(string $path): bool + { + return $this->adapter->fileExists($path); + } + + public function write(string $path, string $contents, Config $config): void + { + $this->adapter->write($path, $contents, $config); + } + + public function writeStream(string $path, $contents, Config $config): void + { + $this->adapter->writeStream($path, $contents, $config); + } + + public function read(string $path): string + { + return $this->adapter->read($path); + } + + public function readStream(string $path) + { + return $this->adapter->readStream($path); + } + + public function delete(string $path): void + { + $this->adapter->delete($path); + } + + public function deleteDirectory(string $path): void + { + $this->adapter->deleteDirectory($path); + } + + public function createDirectory(string $path, Config $config): void + { + $this->adapter->createDirectory($path, $config); + } + + public function setVisibility(string $path, string $visibility): void + { + $this->adapter->setVisibility($path, $visibility); + } + + public function visibility(string $path): FileAttributes + { + return $this->adapter->visibility($path); + } + + public function mimeType(string $path): FileAttributes + { + return $this->adapter->mimeType($path); + } + + public function lastModified(string $path): FileAttributes + { + return $this->adapter->lastModified($path); + } + + public function fileSize(string $path): FileAttributes + { + return $this->adapter->fileSize($path); + } + + public function listContents(string $path, bool $deep): iterable + { + return $this->adapter->listContents($path, $deep); + } + + public function move(string $source, string $destination, Config $config): void + { + $this->adapter->move($source, $destination, $config); + } + + public function copy(string $source, string $destination, Config $config): void + { + $this->adapter->copy($source, $destination, $config); + } +} diff --git a/tests/integration/Core/IO/FlysystemTestAdapterInterface.php b/tests/integration/Core/IO/FlysystemTestAdapterInterface.php new file mode 100644 index 0000000000..b4d1d6d525 --- /dev/null +++ b/tests/integration/Core/IO/FlysystemTestAdapterInterface.php @@ -0,0 +1,14 @@ +gateway = new DoctrineStorage($this->redecorator, $this->getDatabaseConnection()); $this->imageSizeMetadataHandler = $this->createMock(MetadataHandler::class); $this->pathGenerator = $this->createMock(PathGenerator::class); - $this->deprecationWarner = $this->createMock(DeprecationWarnerInterface::class); $this->aliasCleaner = $this->createMock(AliasCleanerInterface::class); $this->filePathNormalizer = $this->createMock(FilePathNormalizerInterface::class); $this->ioService = $this->createMock(IOServiceInterface::class); @@ -76,7 +71,6 @@ protected function setUp(): void $this->ioService, $this->pathGenerator, $this->imageSizeMetadataHandler, - $this->deprecationWarner, $this->aliasCleaner, $this->filePathNormalizer, $this->fileExtensionBlackListValidator @@ -99,8 +93,8 @@ private function getContext(): array /** * @dataProvider providerOfFieldData * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\Core\IO\Exception\InvalidBinaryFileIdException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + * @throws \Ibexa\Core\IO\Exception\InvalidBinaryFileIdException */ public function testStoreFieldDataDuringCreate(VersionInfo $versionInfo, Field $field): void { @@ -214,8 +208,8 @@ private function runCommonStoreFieldDataMocks(Field $field): BinaryFile /** * @return iterable */ public function providerOfFieldData(): iterable diff --git a/tests/integration/Core/LegacyTestContainerBuilder.php b/tests/integration/Core/LegacyTestContainerBuilder.php new file mode 100644 index 0000000000..fcbff57704 --- /dev/null +++ b/tests/integration/Core/LegacyTestContainerBuilder.php @@ -0,0 +1,173 @@ +initCoreTestContainerBuilder(); + } + + /** + * @throws \Exception + */ + private function initCoreTestContainerBuilder(): void + { + $this->addResource(new FileResource(__FILE__)); + + $installDir = __DIR__ . '/../../..'; + $settingsPath = $installDir . '/src/lib/Resources/settings/'; + + $this->coreLoader = $this->loadCoreSettings($settingsPath); + + // Cache settings (takes same env variables as ezplatform does, only supports "singleredis" setup) + if (getenv('CUSTOM_CACHE_POOL') === 'singleredis') { + $this->configureRedis(); + } + + $this->setParameter('ibexa.kernel.root_dir', $installDir); + + $this->registerCompilerPasses(); + + $this->registerCoreBundleServices(); + } + + /** + * @throws \Exception + */ + private function loadCoreSettings(string $settingsPath): LoaderInterface + { + $loader = new YamlFileLoader( + $this, + new FileLocator([$settingsPath, __DIR__ . '/Resources/settings']) + ); + + $loader->load('fieldtype_constraints_storage.yaml'); + $loader->load('fieldtype_external_storages.yml'); + $loader->load('fieldtype_services.yml'); + $loader->load('fieldtypes.yml'); + $loader->load('indexable_fieldtypes.yml'); + $loader->load('io.yml'); + $loader->load('repository.yml'); + $loader->load('repository/inner.yml'); + $loader->load('repository/event.yml'); + $loader->load('repository/siteaccessaware.yml'); + $loader->load('repository/autowire.yml'); + $loader->load('roles.yml'); + $loader->load('storage_engines/common.yml'); + $loader->load('storage_engines/cache.yml'); + $loader->load('storage_engines/legacy.yml'); + $loader->load('storage_engines/shortcuts.yml'); + $loader->load('settings.yml'); + $loader->load('utils.yml'); + $loader->load('policies.yml'); + $loader->load('events.yml'); + $loader->load('thumbnails.yml'); + $loader->load('tokens.yml'); + $loader->load('content_location_mapper.yml'); + + // tests/integration/Core/Resources/settings/common.yml + $loader->load('common.yml'); + + return $loader; + } + + private function configureRedis(): void + { + $this + ->register('ezpublish.cache_pool.driver.redis', 'Redis') + ->addMethodCall('connect', [(getenv('CACHE_HOST') ?: '127.0.0.1'), 6379, 2.5]); + + $this + ->register('ibexa.cache_pool.driver', RedisAdapter::class) + ->setArguments([new Reference('ezpublish.cache_pool.driver.redis'), '', 120]); + } + + private function registerCompilerPasses(): void + { + $this->addCompilerPass(new Compiler\FieldTypeRegistryPass(), PassConfig::TYPE_OPTIMIZE); + $this->addCompilerPass( + new Compiler\Persistence\FieldTypeRegistryPass(), + PassConfig::TYPE_OPTIMIZE + ); + + $this->addCompilerPass(new Compiler\Storage\ExternalStorageRegistryPass()); + $this->addCompilerPass(new Compiler\Storage\Legacy\FieldValueConverterRegistryPass()); + $this->addCompilerPass(new Compiler\Storage\Legacy\RoleLimitationConverterPass()); + + $this->addCompilerPass(new Compiler\Search\Legacy\CriteriaConverterPass()); + $this->addCompilerPass(new Compiler\Search\Legacy\CriterionFieldValueHandlerRegistryPass()); + $this->addCompilerPass(new Compiler\Search\Legacy\SortClauseConverterPass()); + + $this->addCompilerPass(new ConsoleCommandPass()); + + // Symfony 4 makes services private by default. Test cases are not prepared for this. + // This is a simple workaround to override services as public. + $this->addCompilerPass(new SetAllServicesPublicPass()); + } + + /** + * @return \Symfony\Component\Config\Loader\LoaderInterface + */ + public function getCoreLoader(): LoaderInterface + { + if (null === $this->coreLoader) { + throw new \RuntimeException('Core loader is not initialized'); + } + + return $this->coreLoader; + } + + /** + * Register needed services from Core Bundle. + * + * Note: if a service from a bundle layer is required to be registered here, it most likely + * means that the service should be located in core lib layer in the first place. + */ + private function registerCoreBundleServices(): void + { + $this->registerSiteAccessConfigProcessingServices(); + } + + private function registerSiteAccessConfigProcessingServices(): void + { + if ($this->hasDefinition(ConfigProcessor::class)) { + return; + } + + $definition = new Definition(ComplexConfigProcessor::class); + $definition->setArgument('$configResolver', new Reference(ConfigResolverInterface::class)); + $definition->setArgument('$siteAccessService', new Reference(SiteAccessService::class)); + + $this->setDefinition(ConfigProcessor::class, $definition); + } +} diff --git a/tests/integration/Core/Limitation/MemberOfLimitationTest.php b/tests/integration/Core/Limitation/MemberOfLimitationTest.php index 732c901f3c..08318cb5cb 100644 --- a/tests/integration/Core/Limitation/MemberOfLimitationTest.php +++ b/tests/integration/Core/Limitation/MemberOfLimitationTest.php @@ -8,12 +8,12 @@ namespace Ibexa\Tests\Integration\Core\Limitation; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Tests\Limitation\PermissionResolver\BaseLimitationIntegrationTest; +use Ibexa\Contracts\Core\Repository\Repository; use Ibexa\Contracts\Core\Repository\Values\User\Limitation\MemberOfLimitation; use Ibexa\Core\Limitation\MemberOfLimitationType; +use Ibexa\Tests\Integration\Core\Repository\Limitation\PermissionResolver\BaseLimitationIntegrationTest; -class MemberOfLimitationTest extends BaseLimitationIntegrationTest +final class MemberOfLimitationTest extends BaseLimitationIntegrationTest { private const ADMIN_GROUP_ID = 14; private const USERS_GROUP_ID = 4; diff --git a/eZ/Publish/API/Repository/Tests/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php b/tests/integration/Core/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php similarity index 82% rename from eZ/Publish/API/Repository/Tests/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php rename to tests/integration/Core/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php index dffedbd5e7..17d77462f2 100644 --- a/eZ/Publish/API/Repository/Tests/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php +++ b/tests/integration/Core/Limitation/PermissionResolver/LanguageLimitationIntegrationTest.php @@ -6,10 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\Limitation\PermissionResolver; +namespace Ibexa\Tests\Integration\Core\Limitation\PermissionResolver; -use eZ\Publish\API\Repository\Values\User\Limitation\LanguageLimitation; -use eZ\Publish\SPI\Limitation\Target; +use Ibexa\Contracts\Core\Limitation\Target; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\LanguageLimitation; +use Ibexa\Tests\Integration\Core\Repository\Limitation\PermissionResolver\BaseLimitationIntegrationTest; /** * Integration test for chosen use cases of calls to PermissionResolver::canUser. @@ -53,9 +54,9 @@ public function providerForCanUserCreateContent(): array * @param array $limitations * @param bool $expectedResult * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testCanUserCreateContent(array $limitations, bool $expectedResult): void { @@ -118,9 +119,9 @@ public function providerForCanUserEditOrPublishContent(): array * @param array $limitations * @param bool $expectedResult * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testCanUserEditContent(array $limitations, bool $expectedResult): void { @@ -149,9 +150,9 @@ public function testCanUserEditContent(array $limitations, bool $expectedResult) * @param array $limitations * @param bool $expectedResult * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testCanUserPublishContent(array $limitations, bool $expectedResult): void { @@ -189,12 +190,12 @@ public function providerForCanUserDeleteContent(): array /** * @dataProvider providerForCanUserDeleteContent * - * @param \eZ\Publish\API\Repository\Values\User\Limitation[] $limitations + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation[] $limitations * @param bool $expectedResult * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testCanUserDeleteContent(array $limitations, bool $expectedResult): void { @@ -250,13 +251,13 @@ public function providerForCanUserDeleteContentTranslation(): iterable /** * @dataProvider providerForCanUserDeleteContentTranslation * - * @param \eZ\Publish\API\Repository\Values\User\Limitation[] $limitations + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation[] $limitations * @param string $translation * @param bool $expectedResult * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testCanUserDeleteContentTranslation(array $limitations, string $translation, bool $expectedResult): void { @@ -275,3 +276,5 @@ public function testCanUserDeleteContentTranslation(array $limitations, string $ $this->assertCanUser($expectedResult, 'content', 'remove', $limitations, $content, [$target]); } } + +class_alias(LanguageLimitationIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\Limitation\PermissionResolver\LanguageLimitationIntegrationTest'); diff --git a/tests/integration/Core/Limitation/RoleLimitationTest.php b/tests/integration/Core/Limitation/RoleLimitationTest.php index 4498ca9ce1..91035a9ad8 100644 --- a/tests/integration/Core/Limitation/RoleLimitationTest.php +++ b/tests/integration/Core/Limitation/RoleLimitationTest.php @@ -8,21 +8,21 @@ namespace Ibexa\Tests\Integration\Core\Limitation; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Tests\Limitation\PermissionResolver\BaseLimitationIntegrationTest; -use Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\UserRoleLimitation; +use Ibexa\Tests\Integration\Core\Repository\Limitation\PermissionResolver\BaseLimitationIntegrationTest; -class RoleLimitationTest extends BaseLimitationIntegrationTest +final class RoleLimitationTest extends BaseLimitationIntegrationTest { private const USERS_GROUP_ID = 4; public function userPermissionLimitationProvider(): array { - $allowEditorLimitation = new RoleLimitation(); + $allowEditorLimitation = new UserRoleLimitation(); $roleService = $this->getRepository()->getRoleService(); $allowEditorLimitation->limitationValues[] = $roleService->loadRoleByIdentifier('Editor')->id; - $allowAdministratorLimitation = new RoleLimitation(); + $allowAdministratorLimitation = new UserRoleLimitation(); $allowAdministratorLimitation->limitationValues[] = $roleService->loadRoleByIdentifier('Administrator')->id; return [ diff --git a/tests/integration/Core/Limitation/UserGroupLimitationTest.php b/tests/integration/Core/Limitation/UserGroupLimitationTest.php index 38fbaf1b74..77e64f772c 100644 --- a/tests/integration/Core/Limitation/UserGroupLimitationTest.php +++ b/tests/integration/Core/Limitation/UserGroupLimitationTest.php @@ -8,13 +8,13 @@ namespace Ibexa\Tests\Integration\Core\Limitation; -use eZ\Publish\API\Repository\Tests\Limitation\PermissionResolver\BaseLimitationIntegrationTest; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\UserGroupLimitation; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\LocationLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\UserGroupLimitation; +use Ibexa\Tests\Integration\Core\Repository\Limitation\PermissionResolver\BaseLimitationIntegrationTest; final class UserGroupLimitationTest extends BaseLimitationIntegrationTest { @@ -26,7 +26,10 @@ public function testHasUserWithUserGroupLimitationAccessToCreatedLocations(): vo $user = $this->createUserWithPolicies('test_user', $this->getPermissions()); $userGroups = $repository->getUserService()->loadUserGroupsOfUser($user); - $userGroupIds = array_column($userGroups, 'id'); + $userGroupIds = []; + foreach ($userGroups as $userGroup) { + $userGroupIds[] = $userGroup->id; + } $repository->getPermissionResolver()->setCurrentUserReference($user); @@ -49,7 +52,7 @@ public function testHasUserWithUserGroupLimitationAccessToCreatedLocations(): vo $results = $repository->getSearchService()->findLocations($query)->searchHits; $resultLocationIds = array_map(static function (SearchHit $hit): int { - /** @var \eZ\Publish\API\Repository\Values\Content\Location $location */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $location */ $location = $hit->valueObject; return $location->id; diff --git a/eZ/Publish/SPI/Tests/Persistence/Filter/Doctrine/FilteringQueryBuilderTest.php b/tests/integration/Core/Persistence/Filter/Doctrine/FilteringQueryBuilderTest.php similarity index 79% rename from eZ/Publish/SPI/Tests/Persistence/Filter/Doctrine/FilteringQueryBuilderTest.php rename to tests/integration/Core/Persistence/Filter/Doctrine/FilteringQueryBuilderTest.php index 3bf6e17be8..af2c9c5c88 100644 --- a/eZ/Publish/SPI/Tests/Persistence/Filter/Doctrine/FilteringQueryBuilderTest.php +++ b/tests/integration/Core/Persistence/Filter/Doctrine/FilteringQueryBuilderTest.php @@ -6,17 +6,17 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Tests\Persistence\Filter\Doctrine; +namespace Ibexa\Tests\Integration\Core\Persistence\Filter\Doctrine; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\Expression\ExpressionBuilder; -use eZ\Publish\Core\Base\Exceptions\DatabaseException; -use eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder; +use Ibexa\Contracts\Core\Persistence\Filter\Doctrine\FilteringQueryBuilder; +use Ibexa\Core\Base\Exceptions\DatabaseException; use PHPUnit\Framework\TestCase; class FilteringQueryBuilderTest extends TestCase { - /** @var \eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder */ + /** @var \Ibexa\Contracts\Core\Persistence\Filter\Doctrine\FilteringQueryBuilder */ private $queryBuilder; protected function setUp(): void @@ -29,7 +29,7 @@ protected function setUp(): void } /** - * @covers \eZ\Publish\SPI\Persistence\Filter\Doctrine\FilteringQueryBuilder::joinOnce + * @covers \Ibexa\Contracts\Core\Persistence\Filter\Doctrine\FilteringQueryBuilder::joinOnce */ public function testJoinOnce(): void { @@ -65,3 +65,5 @@ public function testJoinOnceThrowsDatabaseError(): void $this->queryBuilder->joinOnce('f', 'bar', 'b', 'f.bar_id = b.id'); } } + +class_alias(FilteringQueryBuilderTest::class, 'eZ\Publish\SPI\Tests\Persistence\Filter\Doctrine\FilteringQueryBuilderTest'); diff --git a/eZ/Publish/SPI/Tests/Search/Content/IndexerGatewayTest.php b/tests/integration/Core/Persistence/Search/Content/IndexerGatewayTest.php similarity index 88% rename from eZ/Publish/SPI/Tests/Search/Content/IndexerGatewayTest.php rename to tests/integration/Core/Persistence/Search/Content/IndexerGatewayTest.php index b7a48e40cf..06b29fd87f 100644 --- a/eZ/Publish/SPI/Tests/Search/Content/IndexerGatewayTest.php +++ b/tests/integration/Core/Persistence/Search/Content/IndexerGatewayTest.php @@ -6,20 +6,20 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Tests\Search\Content; +namespace Ibexa\Tests\Integration\Core\Persistence\Search\Content; use DateTimeImmutable; -use eZ\Publish\Core\Search\Legacy\Content\IndexerGateway; -use eZ\Publish\SPI\Tests\BaseGatewayTest; +use Ibexa\Core\Search\Legacy\Content\IndexerGateway; +use Ibexa\Tests\Integration\Core\BaseGatewayTest; /** * @internal * - * @covers \eZ\Publish\Core\Search\Legacy\Content\IndexerGateway + * @covers \Ibexa\Core\Search\Legacy\Content\IndexerGateway */ final class IndexerGatewayTest extends BaseGatewayTest { - /** @var \eZ\Publish\Core\Search\Legacy\Content\IndexerGateway */ + /** @var \Ibexa\Core\Search\Legacy\Content\IndexerGateway */ private $gateway; /** @@ -133,3 +133,5 @@ public function testGetAllContent(): void self::assertSame(18, $this->gateway->countAllContent()); } } + +class_alias(IndexerGatewayTest::class, 'eZ\Publish\SPI\Tests\Search\Content\IndexerGatewayTest'); diff --git a/tests/integration/Core/Persistence/Variation/InMemoryVariationHandler.php b/tests/integration/Core/Persistence/Variation/InMemoryVariationHandler.php new file mode 100644 index 0000000000..638084f9c8 --- /dev/null +++ b/tests/integration/Core/Persistence/Variation/InMemoryVariationHandler.php @@ -0,0 +1,30 @@ + $field->value . '-in-memory-test', + ]); + } +} + +class_alias(InMemoryVariationHandler::class, 'eZ\Publish\SPI\Tests\Variation\InMemoryVariationHandler'); diff --git a/eZ/Publish/API/Repository/Tests/BaseContentServiceTest.php b/tests/integration/Core/Repository/BaseContentServiceTest.php similarity index 91% rename from eZ/Publish/API/Repository/Tests/BaseContentServiceTest.php rename to tests/integration/Core/Repository/BaseContentServiceTest.php index b0b6c82182..ba8bb49f25 100644 --- a/eZ/Publish/API/Repository/Tests/BaseContentServiceTest.php +++ b/tests/integration/Core/Repository/BaseContentServiceTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\User\User; /** * Base class for content specific tests. @@ -17,7 +17,7 @@ abstract class BaseContentServiceTest extends BaseTest /** * Creates a fresh clean content draft. * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createContentVersion1EmptyBinaryField() { @@ -26,7 +26,7 @@ protected function createContentVersion1EmptyBinaryField() $parentLocationId = $this->generateId('location', 56); $sectionId = $this->generateId('section', 1); /* BEGIN: Inline */ - // $parentLocationId is the id of the /Design/eZ-publish node + // $parentLocationId is the id of the /Design/Ibexa node $contentService = $repository->getContentService(); $contentTypeService = $repository->getContentTypeService(); @@ -65,7 +65,7 @@ protected function createContentVersion1EmptyBinaryField() /** * Creates a fresh clean content draft. * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createContentDraftVersion1( $locationId = 56, @@ -78,7 +78,7 @@ protected function createContentDraftVersion1( $parentLocationId = $this->generateId('location', $locationId); $sectionId = $this->generateId('section', 1); /* BEGIN: Inline */ - // $parentLocationId is the id of the /Design/eZ-publish node + // $parentLocationId is the id of the /Design/Ibexa node $contentService = $repository->getContentService(); $contentTypeService = $repository->getContentTypeService(); @@ -119,7 +119,7 @@ protected function createContentDraftVersion1( /** * Creates a fresh clean published content instance. * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createContentVersion1() { @@ -141,7 +141,7 @@ protected function createContentVersion1() * Creates a new content draft named $draftVersion2 from a currently * published content object. * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createContentDraftVersion2() { @@ -163,7 +163,7 @@ protected function createContentDraftVersion2() * Creates an updated content draft named $draftVersion2 from * a currently published content object. * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createUpdatedDraftVersion2() { @@ -196,7 +196,7 @@ protected function createUpdatedDraftVersion2() * a currently published content object with a user different from the * creator. * - * @return array \eZ\Publish\API\Repository\Values\Content\Content, id + * @return array [Ibexa\Contracts\Core\Repository\Values\Content\Content, id] */ protected function createUpdatedDraftVersion2NotAdmin() { @@ -207,7 +207,7 @@ protected function createUpdatedDraftVersion2NotAdmin() $mainLanguageCode = 'eng-US'; // Create a new user that belongs to the Administrator users group - $newUserCreateStruct = $userService->newUserCreateStruct('admin2', 'admin2@ez.no', 'admin2', $mainLanguageCode); + $newUserCreateStruct = $userService->newUserCreateStruct('admin2', 'admin2@ibexa.co', 'admin2', $mainLanguageCode); $newUserCreateStruct->setField('first_name', 'Admin2', $mainLanguageCode); $newUserCreateStruct->setField('last_name', 'Admin2', $mainLanguageCode); @@ -240,7 +240,7 @@ protected function createUpdatedDraftVersion2NotAdmin() * Creates an updated content object named $contentVersion2 from * a currently published content object. * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createContentVersion2() { @@ -261,7 +261,7 @@ protected function createContentVersion2() /** * Creates an updated content draft named $draft. * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createMultipleLanguageDraftVersion1() { @@ -293,7 +293,7 @@ protected function createMultipleLanguageDraftVersion1() * Creates a published content object with versionNo=2 named * $contentVersion2. * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createMultipleLanguageContentVersion2() { @@ -341,7 +341,7 @@ protected function createMultipleLanguageContentVersion2() * @param string $mainLanguageCode * @param array $multilingualFieldValues map of ['fieldIdentifier' => ['languageCode' => 'field value']] * - * @return \eZ\Publish\API\Repository\Values\Content\Content Content Draft + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content Content Draft */ protected function createMultilingualContentDraft( $contentTypeIdentifier, @@ -393,7 +393,7 @@ protected function createMultilingualContentDraft( * @param int $parentLocationId * @param array $fieldValues map of ['fieldIdentifier' => 'field value'] * - * @return \eZ\Publish\API\Repository\Values\Content\Content Content Draft + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content Content Draft */ protected function createContentDraft( $contentTypeIdentifier, @@ -431,3 +431,5 @@ protected function createContentDraft( return $contentDraft; } } + +class_alias(BaseContentServiceTest::class, 'eZ\Publish\API\Repository\Tests\BaseContentServiceTest'); diff --git a/eZ/Publish/API/Repository/Tests/BaseContentTypeServiceTest.php b/tests/integration/Core/Repository/BaseContentTypeServiceTest.php similarity index 85% rename from eZ/Publish/API/Repository/Tests/BaseContentTypeServiceTest.php rename to tests/integration/Core/Repository/BaseContentTypeServiceTest.php index 104df220db..7c59c01cf9 100644 --- a/eZ/Publish/API/Repository/Tests/BaseContentTypeServiceTest.php +++ b/tests/integration/Core/Repository/BaseContentTypeServiceTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\API\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; /** * Base class for content type specific tests. @@ -16,14 +16,14 @@ abstract class BaseContentTypeServiceTest extends BaseTest /** * Creates a fully functional ContentTypeDraft and returns it. * - * @param \eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct[] $additionalFieldDefinitionsCreateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct[] $additionalFieldDefinitionsCreateStruct * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft * - * @throws \eZ\Publish\API\Repository\Exceptions\ContentTypeFieldDefinitionValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ protected function createContentTypeDraft(array $additionalFieldDefinitionsCreateStruct = []) { @@ -118,7 +118,7 @@ protected function createContentTypeDraft(array $additionalFieldDefinitionsCreat /** * Creates a fresh clean content draft. * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createContentDraft() { @@ -127,7 +127,7 @@ protected function createContentDraft() $parentLocationId = $this->generateId('location', 56); $sectionId = $this->generateId('section', 1); /* BEGIN: Inline */ - // $parentLocationId is the id of the "/Design/eZ-publish" Location + // $parentLocationId is the id of the "/Design/Ibexa" Location $contentService = $repository->getContentService(); $contentTypeService = $repository->getContentTypeService(); @@ -164,3 +164,5 @@ protected function createContentDraft() return $draft; } } + +class_alias(BaseContentTypeServiceTest::class, 'eZ\Publish\API\Repository\Tests\BaseContentTypeServiceTest'); diff --git a/eZ/Publish/API/Repository/Tests/BaseNonRedundantFieldSetTest.php b/tests/integration/Core/Repository/BaseNonRedundantFieldSetTest.php similarity index 95% rename from eZ/Publish/API/Repository/Tests/BaseNonRedundantFieldSetTest.php rename to tests/integration/Core/Repository/BaseNonRedundantFieldSetTest.php index f33d1041d0..857905bb5c 100644 --- a/eZ/Publish/API/Repository/Tests/BaseNonRedundantFieldSetTest.php +++ b/tests/integration/Core/Repository/BaseNonRedundantFieldSetTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\Core\FieldType\TextLine\Value as TextLineValue; +use Ibexa\Core\FieldType\TextLine\Value as TextLineValue; /** * Base class for for create and update Content operations in the ContentService with regard to @@ -17,7 +17,7 @@ abstract class BaseNonRedundantFieldSetTest extends BaseTest /** * Creates a fully functional ContentType and returns it. * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType */ protected function createContentType() { @@ -119,7 +119,7 @@ protected function createContentType() * @param string $mainLanguageCode * @param array $fieldValues * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createTestContent($mainLanguageCode, array $fieldValues) { @@ -199,3 +199,5 @@ protected function updateTestContent($initialLanguageCode, array $fieldValues) return $contentService->loadContent($content->id, null, $content->versionInfo->versionNo); } } + +class_alias(BaseNonRedundantFieldSetTest::class, 'eZ\Publish\API\Repository\Tests\BaseNonRedundantFieldSetTest'); diff --git a/tests/integration/Core/Repository/BaseTest.php b/tests/integration/Core/Repository/BaseTest.php new file mode 100644 index 0000000000..ccad7d3862 --- /dev/null +++ b/tests/integration/Core/Repository/BaseTest.php @@ -0,0 +1,889 @@ +getSetupFactory()->getRepository(false); + } catch (PDOException $e) { + $this->fail( + 'The communication with the database cannot be established. ' . + "This is required in order to perform the tests.\n\n" . + 'Exception: ' . $e + ); + } catch (Exception $e) { + $this->fail( + 'Cannot create a repository with predefined user. ' . + 'Check the UserService or RoleService implementation. ' . + PHP_EOL . PHP_EOL . + 'Exception: ' . $e + ); + } + } + + /** + * Resets the temporary used repository between each test run. + */ + protected function tearDown(): void + { + $this->repository = null; + parent::tearDown(); + } + + /** + * Returns the ID generator, fitting to the repository implementation. + * + * @return \Ibexa\Tests\Integration\Core\Repository\IdManager + */ + protected function getIdManager() + { + return $this->getSetupFactory()->getIdManager(); + } + + /** + * Generates a repository specific ID value. + * + * @param string $type + * @param mixed $rawId + * + * @return mixed + */ + protected function generateId($type, $rawId) + { + return $this->getIdManager()->generateId($type, $rawId); + } + + /** + * Parses a repository specific ID value. + * + * @param string $type + * @param mixed $id + * + * @return mixed + */ + protected function parseId($type, $id) + { + return $this->getIdManager()->parseId($type, $id); + } + + /** + * Returns a config setting provided by the setup factory. + * + * @param string $configKey + * + * @return mixed + */ + protected function getConfigValue($configKey) + { + return $this->getSetupFactory()->getConfigValue($configKey); + } + + /** + * Tests if the currently tested api is based on a V4 implementation. + * + * @deprecated Not in use, will be removed. + * + * @return bool + */ + protected function isVersion4() + { + return isset($_ENV['backendVersion']) && '4' === $_ENV['backendVersion']; + } + + /** + * @param bool $initialInitializeFromScratch Only has an effect if set in first call within a test + */ + protected function getRepository(bool $initialInitializeFromScratch = true): Repository + { + if (null === $this->repository) { + try { + $this->repository = $this->getSetupFactory()->getRepository( + $initialInitializeFromScratch + ); + } catch (ErrorException $e) { + self::fail( + sprintf( + '%s: %s in %s:%d', + __FUNCTION__, + $e->getMessage(), + $e->getFile(), + $e->getLine() + ) + ); + } + } + + return $this->repository; + } + + /** + * @throws \ErrorException + */ + protected function getSetupFactory(): SetupFactory + { + if (null === $this->setupFactory) { + if (false === ($setupClass = getenv('setupFactory'))) { + $setupClass = LegacySetupFactory::class; + putenv("setupFactory=$setupClass"); + } + + if (false === getenv('fixtureDir')) { + putenv('fixtureDir=Legacy'); + } + + if (false === class_exists($setupClass)) { + throw new ErrorException( + sprintf( + 'Environment variable "setupFactory" does not reference an existing class: %s. Did you forget to install a package dependency?', + $setupClass + ) + ); + } + + $this->setupFactory = new $setupClass(); + } + + return $this->setupFactory; + } + + /** + * Asserts that properties given in $expectedValues are correctly set in + * $actualObject. + * + * @param mixed[] $expectedValues + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $actualObject + */ + protected function assertPropertiesCorrect(array $expectedValues, ValueObject $actualObject) + { + foreach ($expectedValues as $propertyName => $propertyValue) { + if ($propertyValue instanceof ValueObject) { + $this->assertStructPropertiesCorrect($propertyValue, $actualObject->$propertyName); + } elseif (is_array($propertyValue)) { + foreach ($propertyValue as $key => $value) { + if ($value instanceof ValueObject) { + $this->assertStructPropertiesCorrect($value, $actualObject->$propertyName[$key]); + } else { + $this->assertPropertiesEqual("$propertyName\[$key\]", $value, $actualObject->$propertyName[$key]); + } + } + } else { + $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName); + } + } + } + + /** + * Asserts that properties given in $expectedValues are correctly set in + * $actualObject. + * + * If the property type is array, it will be sorted before comparison. + * + * @TODO: introduced because of randomly failing tests, ref: https://issues.ibexa.co/browse/EZP-21734 + * + * @param mixed[] $expectedValues + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $actualObject + */ + protected function assertPropertiesCorrectUnsorted(array $expectedValues, ValueObject $actualObject) + { + foreach ($expectedValues as $propertyName => $propertyValue) { + if ($propertyValue instanceof ValueObject) { + $this->assertStructPropertiesCorrect($propertyValue, $actualObject->$propertyName); + } else { + $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName, true); + } + } + } + + /** + * Asserts all properties from $expectedValues are correctly set in + * $actualObject. Additional (virtual) properties can be asserted using + * $additionalProperties. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $expectedValues + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $actualObject + * @param array $propertyNames + */ + protected function assertStructPropertiesCorrect(ValueObject $expectedValues, ValueObject $actualObject, array $additionalProperties = []) + { + foreach ($expectedValues as $propertyName => $propertyValue) { + if ($propertyValue instanceof ValueObject) { + $this->assertStructPropertiesCorrect($propertyValue, $actualObject->$propertyName); + } else { + $this->assertPropertiesEqual($propertyName, $propertyValue, $actualObject->$propertyName); + } + } + + foreach ($additionalProperties as $propertyName) { + $this->assertPropertiesEqual($propertyName, $expectedValues->$propertyName, $actualObject->$propertyName); + } + } + + /** + * @see \Ibexa\Tests\Integration\Core\Repository\BaseTest::assertPropertiesCorrectUnsorted + * + * @param array $items An array of scalar values + */ + private function sortItems(array &$items) + { + $sorter = function ($a, $b) { + if (!is_scalar($a) || !is_scalar($b)) { + $this->fail('Wrong usage: method ' . __METHOD__ . ' accepts only an array of scalar values'); + } + + return strcmp($a, $b); + }; + usort($items, $sorter); + } + + private function assertPropertiesEqual($propertyName, $expectedValue, $actualValue, $sortArray = false) + { + if ($expectedValue instanceof ArrayObject) { + $expectedValue = $expectedValue->getArrayCopy(); + } elseif ($expectedValue instanceof DateTime) { + $expectedValue = $expectedValue->format(DateTime::RFC850); + } + if ($actualValue instanceof ArrayObject) { + $actualValue = $actualValue->getArrayCopy(); + } elseif ($actualValue instanceof DateTime) { + $actualValue = $actualValue->format(DateTime::RFC850); + } + + if ($sortArray && is_array($actualValue) && is_array($expectedValue)) { + $this->sortItems($actualValue); + $this->sortItems($expectedValue); + } + + $this->assertEquals( + $expectedValue, + $actualValue, + sprintf('Object property "%s" incorrect.', $propertyName) + ); + } + + /** + * Create a user in editor user group. + */ + protected function createUserVersion1( + string $login = 'user', + ?string $email = null, + ContentType $contentType = null, + int $userGroupId = 13 + ): User { + $repository = $this->getRepository(); + + /* BEGIN: Inline */ + $userService = $repository->getUserService(); + + // Instantiate a create struct with mandatory properties + $email = $email ?? "{$login}@example.com"; + $userCreate = $userService->newUserCreateStruct( + $login, + $email, + 'VerySecret@Password.1234', + 'eng-US' + ); + $userCreate->enabled = true; + + // Set some fields required by the user ContentType + $userCreate->setField('first_name', 'Example'); + $userCreate->setField('last_name', 'User'); + + if (!empty($contentType)) { + $userCreate->contentType = $contentType; + } + + // Load parent group for the user + $group = $userService->loadUserGroup($userGroupId); + + // Create a new user instance. + $user = $userService->createUser($userCreate, [$group]); + /* END: Inline */ + + return $user; + } + + /** + * Create a user in new user group with editor rights limited to Media Library (/1/48/). + * + * @uses ::createCustomUserVersion1() + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + */ + protected function createMediaUserVersion1() + { + return $this->createCustomUserVersion1( + 'Media Editor', + 'Editor', + new SubtreeLimitation(['limitationValues' => ['/1/43/']]) + ); + } + + /** + * Create a user with new user group and assign a existing role (optionally with RoleLimitation). + * + * @param string $userGroupName Name of the new user group to create + * @param string $roleIdentifier Role identifier to assign to the new group + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation|null $roleLimitation + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + */ + protected function createCustomUserVersion1($userGroupName, $roleIdentifier, RoleLimitation $roleLimitation = null) + { + return $this->createCustomUserWithLogin( + 'user', + 'user@example.com', + $userGroupName, + $roleIdentifier, + $roleLimitation + ); + } + + /** + * Create a user with new user group and assign a existing role (optionally with RoleLimitation). + * + * @param string $login User login + * @param string $email User e-mail + * @param string $userGroupName Name of the new user group to create + * @param string $roleIdentifier Role identifier to assign to the new group + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation|null $roleLimitation + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + */ + protected function createCustomUserWithLogin( + $login, + $email, + $userGroupName, + $roleIdentifier, + RoleLimitation $roleLimitation = null + ) { + $repository = $this->getRepository(); + + /* BEGIN: Inline */ + // ID of the "Users" user group in an Ibexa demo installation + $rootUsersGroupId = $this->generateId('location', 4); + + $roleService = $repository->getRoleService(); + $userService = $repository->getUserService(); + + // Get a group create struct + $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); + $userGroupCreate->setField('name', $userGroupName); + + // Create new group with media editor rights + $userGroup = $userService->createUserGroup( + $userGroupCreate, + $userService->loadUserGroup($rootUsersGroupId) + ); + $roleService->assignRoleToUserGroup( + $roleService->loadRoleByIdentifier($roleIdentifier), + $userGroup, + $roleLimitation + ); + + // Instantiate a create struct with mandatory properties + $userCreate = $userService->newUserCreateStruct( + $login, + $email, + 'secret', + 'eng-US' + ); + $userCreate->enabled = true; + + // Set some fields required by the user ContentType + $userCreate->setField('first_name', 'Example'); + $userCreate->setField('last_name', ucfirst($login)); + + // Create a new user instance. + $user = $userService->createUser($userCreate, [$userGroup]); + /* END: Inline */ + + return $user; + } + + /** + * Create a user using given data. + * + * @param string $login + * @param string $firstName + * @param string $lastName + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup|null $userGroup optional user group, Editor by default + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + */ + protected function createUser($login, $firstName, $lastName, UserGroup $userGroup = null) + { + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + if (null === $userGroup) { + $userGroup = $userService->loadUserGroup(13); + } + + // Instantiate a create struct with mandatory properties + $userCreate = $userService->newUserCreateStruct( + $login, + "{$login}@example.com", + 'secret', + 'eng-US' + ); + $userCreate->enabled = true; + + // Set some fields required by the user ContentType + $userCreate->setField('first_name', $firstName); + $userCreate->setField('last_name', $lastName); + + // Create a new user instance. + $user = $userService->createUser($userCreate, [$userGroup]); + + return $user; + } + + /** + * Only for internal use. + * + * Creates a \DateTime object for $timestamp in the current time zone + * + * @param int $timestamp + * + * @return \DateTime + */ + public function createDateTime($timestamp = null) + { + $dateTime = new \DateTime(); + if ($timestamp !== null) { + $dateTime->setTimestamp($timestamp); + } + + return $dateTime; + } + + /** + * Calls given Repository's aggregated SearchHandler::refresh(). + * + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + */ + protected function refreshSearch(Repository $repository) + { + if ($this->isLegacySearchEngineSetup()) { + return; + } + + while (true) { + $repositoryReflection = new \ReflectionObject($repository); + // If the repository is decorated, we need to recurse in the "repository" property + if (!$repositoryReflection->hasProperty('repository')) { + break; + } + + $repositoryProperty = $repositoryReflection->getProperty('repository'); + $repositoryProperty->setAccessible(true); + $repository = $repositoryProperty->getValue($repository); + } + + $searchHandlerProperty = new \ReflectionProperty($repository, 'searchHandler'); + $searchHandlerProperty->setAccessible(true); + + /** @var \Ibexa\Solr\Handler $searchHandler */ + $searchHandler = $searchHandlerProperty->getValue($repository); + + $searchHandler->commit(); + } + + protected function isLegacySearchEngineSetup(): bool + { + return get_class($this->getSetupFactory()) === LegacySetupFactory::class; + } + + /** + * Create role of a given name with the given policies described by an array. + * + * @param $roleName + * @param array $policiesData [['module' => 'content', 'function' => 'read', 'limitations' => []] + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\Role + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function createRoleWithPolicies($roleName, array $policiesData) + { + $repository = $this->getRepository(false); + $roleService = $repository->getRoleService(); + + $roleCreateStruct = $roleService->newRoleCreateStruct($roleName); + foreach ($policiesData as $policyData) { + $policyCreateStruct = $roleService->newPolicyCreateStruct( + $policyData['module'], + $policyData['function'] + ); + + if (isset($policyData['limitations'])) { + foreach ($policyData['limitations'] as $limitation) { + $policyCreateStruct->addLimitation($limitation); + } + } + + $roleCreateStruct->addPolicy($policyCreateStruct); + } + + $roleDraft = $roleService->createRole($roleCreateStruct); + + $roleService->publishRoleDraft($roleDraft); + + return $roleService->loadRole($roleDraft->id); + } + + /** + * Create user and assign new role with the given policies. + * + * @param string $login + * @param array $policiesData list of policies in the form of [ [ 'module' => 'name', 'function' => 'name'] ] + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation|null $roleLimitation + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function createUserWithPolicies($login, array $policiesData, RoleLimitation $roleLimitation = null) + { + $repository = $this->getRepository(false); + $roleService = $repository->getRoleService(); + $userService = $repository->getUserService(); + + $repository->beginTransaction(); + try { + $userCreateStruct = $userService->newUserCreateStruct( + $login, + "{$login}@test.local", + $login, + 'eng-GB' + ); + $userCreateStruct->setField('first_name', $login); + $userCreateStruct->setField('last_name', $login); + $user = $userService->createUser($userCreateStruct, [$userService->loadUserGroup(4)]); + + $role = $this->createRoleWithPolicies(uniqid('role_for_' . $login . '_', true), $policiesData); + $roleService->assignRoleToUser($role, $user, $roleLimitation); + + $repository->commit(); + + return $user; + } catch (ForbiddenException | NotFoundException | UnauthorizedException $ex) { + $repository->rollback(); + throw $ex; + } + } + + /** + * @throws \ErrorException + */ + protected function getRawDatabaseConnection(): Connection + { + $connection = $this + ->getSetupFactory() + ->getServiceContainer()->get('ibexa.persistence.connection'); + + if (!$connection instanceof Connection) { + throw new \RuntimeException( + sprintf('Found %s instead of %s', get_class($connection), Connection::class) + ); + } + + return $connection; + } + + /** + * Executes the given callback passing raw Database Connection (\Doctrine\DBAL\Connection). + * Returns the result returned by the given callback. + * + * **Note**: The method clears the entire persistence cache pool. + * + * @throws \Exception + * + * @param callable $callback + * + * @return mixed the return result of the given callback + */ + public function performRawDatabaseOperation(callable $callback) + { + $repository = $this->getRepository(false); + $repository->beginTransaction(); + try { + $callback( + $this->getRawDatabaseConnection() + ); + $repository->commit(); + } catch (Exception $e) { + $repository->rollback(); + throw $e; + } + + /** @var \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface $cachePool */ + $cachePool = $this + ->getSetupFactory() + ->getServiceContainer()->get('ibexa.cache_pool'); + + $cachePool->clear(); + } + + /** + * Traverse all errors for all fields in all Translations to find expected one. + * + * @param \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException $exception + * @param string $expectedValidationErrorMessage + */ + protected function assertValidationErrorOccurs( + ContentFieldValidationException $exception, + string $expectedValidationErrorMessage + ): void { + $constraint = new PHPUnitConstraint\ValidationErrorOccurs($expectedValidationErrorMessage); + + self::assertThat($exception, $constraint); + } + + /** + * Traverse all errors for all fields in all Translations to find if all expected ones occurred. + * + * @param \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException $exception + * @param string[] $expectedValidationErrorMessages + */ + protected function assertAllValidationErrorsOccur( + ContentFieldValidationException $exception, + array $expectedValidationErrorMessages + ): void { + $constraint = new PHPUnitConstraint\AllValidationErrorsOccur( + $expectedValidationErrorMessages + ); + + self::assertThat($exception, $constraint); + } + + protected function assertContentItemEquals( + Content $expected, + Content $actual, + string $message + ): void { + $constraint = new PHPUnitConstraint\ContentItemEquals($expected); + + self::assertThat($actual, $constraint, $message); + } + + /** + * Create 'folder' Content. + * + * @param array $names Folder names in the form of ['<language_code>' => '<name>'] + * @param int|null $parentLocationId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content published Content + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function createFolder( + array $names, + ?int $parentLocationId = null, + ?string $remoteId = null, + bool $alwaysAvailable = true + ): Content { + $repository = $this->getRepository(false); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + + if (empty($names)) { + throw new \RuntimeException(sprintf('%s expects a non-empty names list', __METHOD__)); + } + $mainLanguageCode = array_keys($names)[0]; + + $struct = $contentService->newContentCreateStruct( + $contentTypeService->loadContentTypeByIdentifier('folder'), + $mainLanguageCode + ); + $struct->remoteId = $remoteId; + $struct->alwaysAvailable = $alwaysAvailable; + foreach ($names as $languageCode => $translatedName) { + $struct->setField('name', $translatedName, $languageCode); + } + + $locationCreateStructList = []; + if (null !== $parentLocationId) { + $locationCreateStructList[] = $locationService->newLocationCreateStruct( + $parentLocationId + ); + } + + $contentDraft = $contentService->createContent( + $struct, + $locationCreateStructList + ); + + return $contentService->publishVersion($contentDraft->versionInfo); + } + + /** + * Update 'folder' Content. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * @param array $names Folder names in the form of ['<language_code>' => '<name>'] + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + protected function updateFolder(Content $content, array $names) + { + $repository = $this->getRepository(false); + $contentService = $repository->getContentService(); + + $draft = $contentService->createContentDraft($content->contentInfo); + $struct = $contentService->newContentUpdateStruct(); + $struct->initialLanguageCode = array_keys($names)[0]; + + foreach ($names as $languageCode => $translatedName) { + $struct->setField('name', $translatedName, $languageCode); + } + + return $contentService->updateContent($draft->versionInfo, $struct); + } + + /** + * Add new Language to the Repository. + * + * @param string $languageCode + * @param string $name + * @param bool $enabled + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Language + */ + protected function createLanguage(string $languageCode, string $name, bool $enabled = true): Language + { + $repository = $this->getRepository(false); + + $languageService = $repository->getContentLanguageService(); + $languageStruct = $languageService->newLanguageCreateStruct(); + $languageStruct->name = $name; + $languageStruct->languageCode = $languageCode; + $languageStruct->enabled = $enabled; + + return $languageService->createLanguage($languageStruct); + } + + protected function createLanguageIfNotExists( + string $languageCode, + string $name, + bool $enabled = true + ): Language { + $repository = $this->getRepository(false); + + try { + return $repository->getContentLanguageService()->loadLanguage($languageCode); + } catch (NotFoundException $e) { + return $this->createLanguage($languageCode, $name, $enabled); + } + } + + /** + * @param string $identifier content type identifier + * @param string $mainTranslation main translation language code + * @param array $fieldsToDefine a map of field definition identifiers to their field type identifiers + * @param bool $alwaysAvailable default always available + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + protected function createSimpleContentType( + string $identifier, + string $mainTranslation, + array $fieldsToDefine, + bool $alwaysAvailable = true + ): ContentType { + $contentTypeService = $this->getRepository(false)->getContentTypeService(); + $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct($identifier); + $contentTypeCreateStruct->mainLanguageCode = $mainTranslation; + $contentTypeCreateStruct->names = [$mainTranslation => $identifier]; + $contentTypeCreateStruct->defaultAlwaysAvailable = $alwaysAvailable; + foreach ($fieldsToDefine as $fieldDefinitionIdentifier => $fieldTypeIdentifier) { + $fieldDefinitionCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( + $fieldDefinitionIdentifier, + $fieldTypeIdentifier + ); + $contentTypeCreateStruct->addFieldDefinition($fieldDefinitionCreateStruct); + } + $contentTypeService->publishContentTypeDraft( + $contentTypeService->createContentType( + $contentTypeCreateStruct, + [$contentTypeService->loadContentTypeGroupByIdentifier('Content')] + ) + ); + + return $contentTypeService->loadContentTypeByIdentifier($identifier); + } + + protected function loginAsUser(UserReference $user): void + { + $this->getRepository(false)->getPermissionResolver()->setCurrentUserReference($user); + } +} + +class_alias(BaseTest::class, 'eZ\Publish\API\Repository\Tests\BaseTest'); diff --git a/eZ/Publish/API/Repository/Tests/BaseTrashServiceTest.php b/tests/integration/Core/Repository/BaseTrashServiceTest.php similarity index 88% rename from eZ/Publish/API/Repository/Tests/BaseTrashServiceTest.php rename to tests/integration/Core/Repository/BaseTrashServiceTest.php index 8e3862179a..ecdad092a0 100644 --- a/eZ/Publish/API/Repository/Tests/BaseTrashServiceTest.php +++ b/tests/integration/Core/Repository/BaseTrashServiceTest.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; use Doctrine\DBAL\ParameterType; @@ -17,7 +17,7 @@ abstract class BaseTrashServiceTest extends BaseTest * Creates a trashed item from the Community page location and stores * this item in a location variable named $trashItem. * - * @return \eZ\Publish\API\Repository\Values\Content\TrashItem + * @return \Ibexa\Contracts\Core\Repository\Values\Content\TrashItem */ protected function createTrashItem() { @@ -58,3 +58,5 @@ protected function updateTrashedDate(int $locationId, int $newTimestamp): void $query->execute(); } } + +class_alias(BaseTrashServiceTest::class, 'eZ\Publish\API\Repository\Tests\BaseTrashServiceTest'); diff --git a/eZ/Publish/API/Repository/Tests/BaseURLServiceTest.php b/tests/integration/Core/Repository/BaseURLServiceTest.php similarity index 90% rename from eZ/Publish/API/Repository/Tests/BaseURLServiceTest.php rename to tests/integration/Core/Repository/BaseURLServiceTest.php index 09ef41255c..bd98cd5201 100644 --- a/eZ/Publish/API/Repository/Tests/BaseURLServiceTest.php +++ b/tests/integration/Core/Repository/BaseURLServiceTest.php @@ -4,16 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; -use eZ\Publish\API\Repository\Values\URL\SearchResult; -use eZ\Publish\API\Repository\Values\URL\URLQuery; -use eZ\Publish\API\Repository\Values\URL\UsageSearchResult; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\URL\SearchResult; +use Ibexa\Contracts\Core\Repository\Values\URL\URLQuery; +use Ibexa\Contracts\Core\Repository\Values\URL\UsageSearchResult; /** * Base class for URLService tests. @@ -179,3 +179,5 @@ private function createUrlFieldDefinitionCreateStruct(ContentTypeService $conten return $urlFieldCreate; } } + +class_alias(BaseURLServiceTest::class, 'eZ\Publish\API\Repository\Tests\BaseURLServiceTest'); diff --git a/tests/integration/Core/Repository/BookmarkServiceTest.php b/tests/integration/Core/Repository/BookmarkServiceTest.php new file mode 100644 index 0000000000..b4d1542d59 --- /dev/null +++ b/tests/integration/Core/Repository/BookmarkServiceTest.php @@ -0,0 +1,155 @@ +getRepository(); + + /* BEGIN: Use Case */ + $location = $repository->getLocationService()->loadLocation($this->generateId('location', self::LOCATION_ID_BOOKMARKED)); + $isBookmarked = $repository->getBookmarkService()->isBookmarked($location); + /* END: Use Case */ + + $this->assertTrue($isBookmarked); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::isBookmarked + */ + public function testIsNotBookmarked() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $location = $repository->getLocationService()->loadLocation($this->generateId('location', self::LOCATION_ID_NOT_BOOKMARKED)); + $isBookmarked = $repository->getBookmarkService()->isBookmarked($location); + /* END: Use Case */ + + $this->assertFalse($isBookmarked); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::createBookmark + */ + public function testCreateBookmark() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $bookmarkService = $repository->getBookmarkService(); + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation($this->generateId('location', self::LOCATION_ID_NOT_BOOKMARKED)); + $beforeCreateBookmark = $bookmarkService->isBookmarked($location); + $bookmarkService->createBookmark($location); + $afterCreateBookmark = $bookmarkService->isBookmarked($location); + /* END: Use Case */ + + $this->assertFalse($beforeCreateBookmark); + $this->assertTrue($afterCreateBookmark); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::createBookmark + * @depends testCreateBookmark + */ + public function testCreateBookmarkThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $bookmarkService = $repository->getBookmarkService(); + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation($this->generateId('location', self::LOCATION_ID_BOOKMARKED)); + $bookmarkService->createBookmark($location); + /* END: Use Case */ + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::deleteBookmark + */ + public function testDeleteBookmark() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $bookmarkService = $repository->getBookmarkService(); + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation($this->generateId('location', self::LOCATION_ID_BOOKMARKED)); + + $beforeDeleteBookmark = $bookmarkService->isBookmarked($location); + $bookmarkService->deleteBookmark($location); + $afterDeleteBookmark = $bookmarkService->isBookmarked($location); + /* END: Use Case */ + + $this->assertTrue($beforeDeleteBookmark); + $this->assertFalse($afterDeleteBookmark); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::deleteBookmark + * @depends testDeleteBookmark + */ + public function testDeleteBookmarkThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $bookmarkService = $repository->getBookmarkService(); + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation($this->generateId('location', self::LOCATION_ID_NOT_BOOKMARKED)); + $bookmarkService->deleteBookmark($location); + /* END: Use Case */ + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::loadBookmarks + */ + public function testLoadBookmarks() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $bookmarks = $repository->getBookmarkService()->loadBookmarks(1, 3); + /* END: Use Case */ + + $this->assertInstanceOf(BookmarkList::class, $bookmarks); + $this->assertEquals($bookmarks->totalCount, 5); + // Assert bookmarks order: recently added should be first + $this->assertEquals([15, 13, 12], array_map(static function ($location) { + return $location->id; + }, $bookmarks->items)); + } +} + +class_alias(BookmarkServiceTest::class, 'eZ\Publish\API\Repository\Tests\BookmarkServiceTest'); diff --git a/tests/integration/Core/Repository/Common/SlugConverter.php b/tests/integration/Core/Repository/Common/SlugConverter.php new file mode 100644 index 0000000000..0a84515e43 --- /dev/null +++ b/tests/integration/Core/Repository/Common/SlugConverter.php @@ -0,0 +1,28 @@ +configuration[$key] = $value; + } +} + +class_alias(SlugConverter::class, 'eZ\Publish\API\Repository\Tests\Common\SlugConverter'); diff --git a/eZ/Publish/API/Repository/Tests/Container/Compiler/SetAllServicesPublicPass.php b/tests/integration/Core/Repository/Container/Compiler/SetAllServicesPublicPass.php similarity index 90% rename from eZ/Publish/API/Repository/Tests/Container/Compiler/SetAllServicesPublicPass.php rename to tests/integration/Core/Repository/Container/Compiler/SetAllServicesPublicPass.php index 4806cb4049..268d7c4ae3 100644 --- a/eZ/Publish/API/Repository/Tests/Container/Compiler/SetAllServicesPublicPass.php +++ b/tests/integration/Core/Repository/Container/Compiler/SetAllServicesPublicPass.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Container\Compiler; +namespace Ibexa\Tests\Integration\Core\Repository\Container\Compiler; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -54,3 +54,5 @@ public function process(ContainerBuilder $containerBuilder): void } } } + +class_alias(SetAllServicesPublicPass::class, 'eZ\Publish\API\Repository\Tests\Container\Compiler\SetAllServicesPublicPass'); diff --git a/tests/integration/Core/Repository/ContentService/CopyNonTranslatableFieldsFromPublishedVersionTest.php b/tests/integration/Core/Repository/ContentService/CopyNonTranslatableFieldsFromPublishedVersionTest.php index a38d527797..92cd4b8e06 100644 --- a/tests/integration/Core/Repository/ContentService/CopyNonTranslatableFieldsFromPublishedVersionTest.php +++ b/tests/integration/Core/Repository/ContentService/CopyNonTranslatableFieldsFromPublishedVersionTest.php @@ -9,13 +9,13 @@ namespace Ibexa\Tests\Integration\Core\Repository\ContentService; use DateTime; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct; +use Ibexa\Core\Repository\Values\Content\ContentUpdateStruct; use Ibexa\Tests\Integration\Core\RepositoryTestCase; /** - * @covers \eZ\Publish\API\Repository\ContentService + * @covers \Ibexa\Contracts\Core\Repository\ContentService */ final class CopyNonTranslatableFieldsFromPublishedVersionTest extends RepositoryTestCase { @@ -25,7 +25,7 @@ final class CopyNonTranslatableFieldsFromPublishedVersionTest extends Repository private const TEXT_LINE_FIELD_TYPE_IDENTIFIER = 'ezstring'; /** - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception */ public function testCopyNonTranslatableFieldsFromPublishedVersionToDraft(): void { @@ -68,13 +68,14 @@ public function testCopyNonTranslatableFieldsFromPublishedVersionToDraft(): void // Loading main content $mainPublishedContent = $contentService->loadContent($engContent->id); - $bodyFieldValue = $mainPublishedContent->getField('body')->getValue(); + $bodyField = $mainPublishedContent->getField('body'); + $bodyFieldValue = $bodyField !== null ? $bodyField->getValue() : null; self::assertSame($expectedBodyValue, $bodyFieldValue->text); } /** - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception */ public function testCopyNonTranslatableFieldsTwoParallelDrafts(): void { @@ -117,7 +118,7 @@ public function testCopyNonTranslatableFieldsTwoParallelDrafts(): void } /** - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception */ public function testCopyNonTranslatableFieldsOverridesNonMainLanguageDrafts(): void { @@ -158,7 +159,8 @@ public function testCopyNonTranslatableFieldsOverridesNonMainLanguageDrafts(): v // Loading content in ger-DE language $mainPublishedContent = $contentService->loadContent($engContent->id, ['ger-DE']); - $bodyFieldValue = $mainPublishedContent->getField('body')->getValue(); + $bodyField = $mainPublishedContent->getField('body'); + $bodyFieldValue = $bodyField !== null ? $bodyField->getValue() : null; self::assertSame($expectedBodyValue, $bodyFieldValue->text); } diff --git a/tests/integration/Core/Repository/ContentService/CopyTranslationsFromPublishedVersionTest.php b/tests/integration/Core/Repository/ContentService/CopyTranslationsFromPublishedVersionTest.php index 8ba05c9867..da95b1dee9 100644 --- a/tests/integration/Core/Repository/ContentService/CopyTranslationsFromPublishedVersionTest.php +++ b/tests/integration/Core/Repository/ContentService/CopyTranslationsFromPublishedVersionTest.php @@ -9,13 +9,14 @@ namespace Ibexa\Tests\Integration\Core\Repository\ContentService; use DateTime; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; -use eZ\Publish\Core\FieldType\TextLine; -use eZ\Publish\Core\Repository\Values\Content\ContentUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct; +use Ibexa\Core\FieldType\TextLine; +use Ibexa\Core\Repository\Values\Content\ContentUpdateStruct; use Ibexa\Tests\Integration\Core\RepositoryTestCase; /** - * @covers \eZ\Publish\API\Repository\ContentService + * @covers \Ibexa\Contracts\Core\Repository\ContentService */ final class CopyTranslationsFromPublishedVersionTest extends RepositoryTestCase { @@ -25,7 +26,7 @@ final class CopyTranslationsFromPublishedVersionTest extends RepositoryTestCase private const CONTENT_TYPE_IDENTIFIER = 'custom'; /** - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception */ public function testCopyTranslationsFromPublishedVersionCopiesEmptyValues(): void { @@ -68,11 +69,17 @@ public function testCopyTranslationsFromPublishedVersionCopiesEmptyValues(): voi $usContent = $contentService->updateContent($usDraft->getVersionInfo(), $contentUpdateStruct); $publishedUsContent = $contentService->publishVersion($usContent->getVersionInfo(), [self::US_LANGUAGE_CODE]); - $usFieldValueInUsContent = $publishedUsContent->getField('title', self::US_LANGUAGE_CODE)->getValue(); + $usFieldInUsContent = $publishedUsContent->getField('title', self::US_LANGUAGE_CODE); + self::assertInstanceOf(Field::class, $usFieldInUsContent); + + $usFieldValueInUsContent = $usFieldInUsContent->getValue(); self::assertInstanceOf(TextLine\Value::class, $usFieldValueInUsContent); self::assertSame('', $usFieldValueInUsContent->text); - $gerFieldValueInUsContent = $publishedUsContent->getField('title', self::GER_LANGUAGE_CODE)->getValue(); + $gerFieldInUsContent = $publishedUsContent->getField('title', self::GER_LANGUAGE_CODE); + self::assertInstanceOf(Field::class, $gerFieldInUsContent); + + $gerFieldValueInUsContent = $gerFieldInUsContent->getValue(); self::assertInstanceOf(TextLine\Value::class, $gerFieldValueInUsContent); self::assertSame('', $gerFieldValueInUsContent->text); } diff --git a/tests/integration/Core/Repository/ContentService/LoadVersionInfoTest.php b/tests/integration/Core/Repository/ContentService/LoadVersionInfoTest.php index 5c8a7b5771..026d6f0e08 100644 --- a/tests/integration/Core/Repository/ContentService/LoadVersionInfoTest.php +++ b/tests/integration/Core/Repository/ContentService/LoadVersionInfoTest.php @@ -11,12 +11,12 @@ use Ibexa\Tests\Integration\Core\RepositoryTestCase; /** - * @covers \eZ\Publish\API\Repository\ContentService + * @covers \Ibexa\Contracts\Core\Repository\ContentService */ final class LoadVersionInfoTest extends RepositoryTestCase { /** - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception */ public function testLoadVersionInfoListByContentInfo(): void { diff --git a/tests/integration/Core/Repository/ContentService/UpdateContentTest.php b/tests/integration/Core/Repository/ContentService/UpdateContentTest.php new file mode 100644 index 0000000000..0dd3f900f6 --- /dev/null +++ b/tests/integration/Core/Repository/ContentService/UpdateContentTest.php @@ -0,0 +1,158 @@ +addRelationFieldToFolderContentType(); + + $privateSection = $this->createPrivateSection(); + + $folderPrivate = $this->createFolder(['eng-GB' => 'Private Folder'], 2); + $sectionService->assignSection($folderPrivate->getContentInfo(), $privateSection); + + // Create folder with relation to 'Private Folder' + $folder = $this->createFolderWithRelations([$folderPrivate->getId()]); + + $userWithRoleLimitation = $this->createUserWithNoAccessToPrivateSection(); + + // Create & publish new $folder version as $editor + $permissionResolver->setCurrentUserReference($userWithRoleLimitation); + $folder = $this->publishVersionWithoutChanges($folder->getContentInfo()); + + // Read relations & check if count($relations) is unchanged + self::setAdministratorUser(); + $relations = $contentService->loadRelations($folder->getVersionInfo()); + if ($relations instanceof \Traversable) { + $relations = iterator_to_array($relations); + } + self::assertCount(1, $relations); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + private function addRelationFieldToFolderContentType(): void + { + $contentTypeService = self::getContentTypeService(); + $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); + $folderTypeDraft = $contentTypeService->createContentTypeDraft($folderType); + + $relationsFieldCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( + 'relations', + 'ezobjectrelationlist' + ); + $relationsFieldCreateStruct->names = ['eng-GB' => 'Relations']; + $contentTypeService->addFieldDefinition($folderTypeDraft, $relationsFieldCreateStruct); + $contentTypeService->publishContentTypeDraft($folderTypeDraft); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function createPrivateSection(): Section + { + $sectionService = self::getSectionService(); + + $sectionCreateStruct = $sectionService->newSectionCreateStruct(); + $sectionCreateStruct->identifier = 'private'; + $sectionCreateStruct->name = 'Private Section'; + + return $sectionService->createSection($sectionCreateStruct); + } + + /** + * @param int[] $relationListTarget + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + private function createFolderWithRelations(array $relationListTarget): Content + { + $contentService = self::getContentService(); + + $folder = $this->createFolder(['eng-GB' => 'Folder with private relation'], 2); + $folderDraft = $contentService->createContentDraft($folder->getContentInfo()); + $folderUpdateStruct = $contentService->newContentUpdateStruct(); + $folderUpdateStruct->setField('relations', $relationListTarget); + + $folder = $contentService->updateContent($folderDraft->getVersionInfo(), $folderUpdateStruct); + + return $contentService->publishVersion($folder->getVersionInfo()); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException + */ + private function assignToUserRoleWithStandardSectionLimitation(User $user): void + { + $sectionService = self::getSectionService(); + $roleService = self::getRoleService(); + + $roleCreateStruct = $roleService->newRoleCreateStruct('limited_access'); + $roleCreateStruct->addPolicy($roleService->newPolicyCreateStruct('*', '*')); + $role = $roleService->createRole($roleCreateStruct); + $roleService->publishRoleDraft($role); + + // limit access to standard section only on the role assignment level + $standardSection = $sectionService->loadSectionByIdentifier('standard'); + $roleService->assignRoleToUser( + $role, + $user, + new SectionLimitation(['limitationValues' => [$standardSection->id]]) + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + private function createUserWithNoAccessToPrivateSection(): User + { + $user = $this->createUser('test.editor', 'Editor', 'Test'); + $this->assignToUserRoleWithStandardSectionLimitation($user); + + return $user; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + private function publishVersionWithoutChanges(ContentInfo $contentInfo): Content + { + $contentService = self::getContentService(); + + $folderDraft = $contentService->createContentDraft($contentInfo); + $folderUpdateStruct = $contentService->newContentUpdateStruct(); + $folder = $contentService->updateContent($folderDraft->getVersionInfo(), $folderUpdateStruct); + + return $contentService->publishVersion($folder->getVersionInfo()); + } +} diff --git a/tests/integration/Core/Repository/ContentService/VersionValidatorTest.php b/tests/integration/Core/Repository/ContentService/VersionValidatorTest.php new file mode 100644 index 0000000000..97e2e13383 --- /dev/null +++ b/tests/integration/Core/Repository/ContentService/VersionValidatorTest.php @@ -0,0 +1,130 @@ +validator = new VersionValidator( + $this->createMock(FieldTypeRegistry::class) + ); + + $repository = $this->getRepository(); + + $this->contentService = $repository->getContentService(); + $this->contentTypeService = $repository->getContentTypeService(); + } + + public function testSupportsKnownValidationObject(): void + { + self::assertTrue($this->validator->supports(new VersionInfo())); + } + + public function testSupportsUnknownValidationObject(): void + { + self::assertFalse($this->validator->supports(new TrashItem())); + } + + public function testValidateMultilingualContent(): void + { + $contentType = $this->createSimpleContentType( + self::CONTENT_TYPE_IDENTIFIER, + self::ENG_GB, + [ + self::FIELD_IDENTIFIER => 'ezstring', + ], + ); + + $content = $this->createAndPublishMultilingualContent($contentType); + $this->makeContentTypeFieldRequired($contentType); + + try { + $this->updateContentTranslation($content); + } catch (ContentFieldValidationException $e) { + // since we updated only one translation, we expect one field error to be thrown (related to null value for eng-GB version) + self::assertCount(1, $e->getFieldErrors()); + } + } + + private function createAndPublishMultilingualContent(ContentType $contentType): Content + { + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_GB); + $contentCreate->setField(self::FIELD_IDENTIFIER, null, self::ENG_GB); + $contentCreate->setField(self::FIELD_IDENTIFIER, 'Name DE', self::GER_DE); + + $contentDraft = $this->contentService->createContent($contentCreate); + + return $this->contentService->publishVersion($contentDraft->versionInfo); + } + + private function makeContentTypeFieldRequired(ContentType $contentType): void + { + $contentTypeDraft = $this->contentTypeService->createContentTypeDraft($contentType); + $nameField = $contentTypeDraft->getFieldDefinition(self::FIELD_IDENTIFIER); + + $fieldDefinitionUpdate = $this->contentTypeService->newFieldDefinitionUpdateStruct(); + $fieldDefinitionUpdate->isRequired = true; + + $this->contentTypeService->updateFieldDefinition( + $contentTypeDraft, + $nameField, + $fieldDefinitionUpdate + ); + + $this->contentTypeService->publishContentTypeDraft($contentTypeDraft); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function updateContentTranslation(Content $content): void + { + $contentDraft = $this->contentService->createContentDraft($content->contentInfo); + $contentUpdate = $this->contentService->newContentUpdateStruct(); + $contentUpdate->setField(self::FIELD_IDENTIFIER, 'Updated name DE', self::GER_DE); + + $this->contentService->updateContent($contentDraft->getVersionInfo(), $contentUpdate); + $this->contentService->publishVersion($contentDraft->versionInfo); + } +} + +class_alias(VersionValidatorTest::class, 'eZ\Publish\API\Repository\Tests\ContentService\VersionValidatorTest'); diff --git a/eZ/Publish/API/Repository/Tests/ContentServiceAuthorizationTest.php b/tests/integration/Core/Repository/ContentServiceAuthorizationTest.php similarity index 80% rename from eZ/Publish/API/Repository/Tests/ContentServiceAuthorizationTest.php rename to tests/integration/Core/Repository/ContentServiceAuthorizationTest.php index 62bce38c11..37b17d95c3 100644 --- a/eZ/Publish/API/Repository/Tests/ContentServiceAuthorizationTest.php +++ b/tests/integration/Core/Repository/ContentServiceAuthorizationTest.php @@ -4,42 +4,42 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\User\Limitation\LanguageLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\LanguageLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\LocationLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; /** * Test case for operations in the ContentServiceAuthorization using in memory storage. * - * @see eZ\Publish\API\Repository\ContentService - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUser + * @covers \Ibexa\Contracts\Core\Repository\ContentService + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUser * @group integration * @group authorization */ class ContentServiceAuthorizationTest extends BaseContentServiceTest { - /** @var \eZ\Publish\API\Repository\Values\User\User */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\User */ private $administratorUser; - /** @var \eZ\Publish\API\Repository\Values\User\User */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\User */ private $anonymousUser; - /** @var \eZ\Publish\API\Repository\Repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository */ private $repository; - /** @var \eZ\Publish\API\Repository\PermissionResolver */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ private $permissionResolver; - /** @var \eZ\Publish\API\Repository\UserService */ + /** @var \Ibexa\Contracts\Core\Repository\UserService */ private $userService; - /** @var \eZ\Publish\API\Repository\ContentService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentService */ private $contentService; public function setUp(): void @@ -61,8 +61,8 @@ public function setUp(): void /** * Test for the createContent() method. * - * @see \eZ\Publish\API\Repository\ContentService::createContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent */ public function testCreateContentThrowsUnauthorizedException() { @@ -87,8 +87,8 @@ public function testCreateContentThrowsUnauthorizedException() /** * Test for the createContent() method. * - * @see \eZ\Publish\API\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent */ public function testCreateContentThrowsUnauthorizedExceptionWithSecondParameter() { @@ -103,8 +103,8 @@ public function testCreateContentThrowsUnauthorizedExceptionWithSecondParameter( /** * Test for the loadContentInfo() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContentInfo() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfo + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfo() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentInfo */ public function testLoadContentInfoThrowsUnauthorizedException() { @@ -121,7 +121,7 @@ public function testLoadContentInfoThrowsUnauthorizedException() /** * Test for the sudo() method. * - * @see \eZ\Publish\API\Repository\Repository::sudo() + * @covers \Ibexa\Contracts\Core\Repository\Repository::sudo() * @depends testLoadContentInfoThrowsUnauthorizedException */ public function testSudo() @@ -143,8 +143,8 @@ public function testSudo() /** * Test for the loadContentInfoList() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoList() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfoList + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfoList() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentInfoList */ public function testLoadContentInfoListSkipsUnauthorizedItems() { @@ -157,8 +157,8 @@ public function testLoadContentInfoListSkipsUnauthorizedItems() /** * Test for the loadContentInfoByRemoteId() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContentInfoByRemoteId() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentInfoByRemoteId + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfoByRemoteId() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentInfoByRemoteId */ public function testLoadContentInfoByRemoteIdThrowsUnauthorizedException() { @@ -175,8 +175,8 @@ public function testLoadContentInfoByRemoteIdThrowsUnauthorizedException() /** * Test for the loadVersionInfo() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfo() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfo + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfo() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadVersionInfo */ public function testLoadVersionInfoThrowsUnauthorizedException() { @@ -193,8 +193,8 @@ public function testLoadVersionInfoThrowsUnauthorizedException() /** * Test for the loadVersionInfo() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfo($contentInfo, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoWithSecondParameter + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfo($contentInfo, $versionNo) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadVersionInfoWithSecondParameter */ public function testLoadVersionInfoThrowsUnauthorizedExceptionWithSecondParameter() { @@ -211,8 +211,8 @@ public function testLoadVersionInfoThrowsUnauthorizedExceptionWithSecondParamete /** * Test for the loadVersionInfoById() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoById + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadVersionInfoById */ public function testLoadVersionInfoByIdThrowsUnauthorizedException() { @@ -228,8 +228,8 @@ public function testLoadVersionInfoByIdThrowsUnauthorizedException() /** * Test for the loadVersionInfoById() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById($contentId, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoByIdWithSecondParameter + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById($contentId, $versionNo) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadVersionInfoByIdWithSecondParameter */ public function testLoadVersionInfoByIdThrowsUnauthorizedExceptionWithSecondParameter() { @@ -245,8 +245,8 @@ public function testLoadVersionInfoByIdThrowsUnauthorizedExceptionWithSecondPara /** * Test for the loadVersionInfoById() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadVersionInfoById($contentId, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersionInfoById + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById($contentId, $versionNo) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadVersionInfoById */ public function testLoadVersionInfoByIdThrowsUnauthorizedExceptionForFirstDraft() { @@ -267,8 +267,8 @@ public function testLoadVersionInfoByIdThrowsUnauthorizedExceptionForFirstDraft( /** * Test for the loadContentByContentInfo() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfo + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByContentInfo() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentByContentInfo */ public function testLoadContentByContentInfoThrowsUnauthorizedException() { @@ -285,8 +285,8 @@ public function testLoadContentByContentInfoThrowsUnauthorizedException() /** * Test for the loadContentByContentInfo() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfoWithLanguageParameters + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentByContentInfoWithLanguageParameters */ public function testLoadContentByContentInfoThrowsUnauthorizedExceptionWithSecondParameter() { @@ -303,8 +303,8 @@ public function testLoadContentByContentInfoThrowsUnauthorizedExceptionWithSecon /** * Test for the loadContentByContentInfo() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByContentInfoWithVersionNumberParameter + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages, $versionNo) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentByContentInfoWithVersionNumberParameter */ public function testLoadContentByContentInfoThrowsUnauthorizedExceptionWithThirdParameter() { @@ -321,8 +321,8 @@ public function testLoadContentByContentInfoThrowsUnauthorizedExceptionWithThird /** * Test for the loadContentByVersionInfo() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByVersionInfo() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByVersionInfo + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByVersionInfo() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentByVersionInfo */ public function testLoadContentByVersionInfoThrowsUnauthorizedException() { @@ -341,8 +341,8 @@ public function testLoadContentByVersionInfoThrowsUnauthorizedException() /** * Test for the loadContentByVersionInfo() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByVersionInfo($versionInfo, $languages) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByVersionInfoWithSecondParameter + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByVersionInfo($versionInfo, $languages) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentByVersionInfoWithSecondParameter */ public function testLoadContentByVersionInfoThrowsUnauthorizedExceptionWithSecondParameter() { @@ -361,8 +361,8 @@ public function testLoadContentByVersionInfoThrowsUnauthorizedExceptionWithSecon /** * Test for the loadContent() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContent */ public function testLoadContentThrowsUnauthorizedException() { @@ -378,8 +378,8 @@ public function testLoadContentThrowsUnauthorizedException() /** * Test for the loadContent() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContent($contentId, $languages) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentWithPrioritizedLanguages + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContent($contentId, $languages) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentWithPrioritizedLanguages */ public function testLoadContentThrowsUnauthorizedExceptionWithSecondParameter() { @@ -395,8 +395,8 @@ public function testLoadContentThrowsUnauthorizedExceptionWithSecondParameter() /** * Test for the loadContent() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContent($contentId, $languages, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentWithThirdParameter + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContent($contentId, $languages, $versionNo) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentWithThirdParameter */ public function testLoadContentThrowsUnauthorizedExceptionWithThirdParameter() { @@ -412,8 +412,8 @@ public function testLoadContentThrowsUnauthorizedExceptionWithThirdParameter() /** * Test for the loadContent() method on a draft. * - * @see \eZ\Publish\API\Repository\ContentService::loadContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContent */ public function testLoadContentThrowsUnauthorizedExceptionOnDrafts() { @@ -439,8 +439,8 @@ public function testLoadContentThrowsUnauthorizedExceptionOnDrafts() * * This test the version permission on loading archived versions * - * @see \eZ\Publish\API\Repository\ContentService::loadContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContent */ public function testLoadContentThrowsUnauthorizedExceptionsOnArchives() { @@ -483,8 +483,8 @@ public function testLoadContentThrowsUnauthorizedExceptionsOnArchives() /** * Test for the loadContentByRemoteId() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByRemoteId + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByRemoteId() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentByRemoteId */ public function testLoadContentByRemoteIdThrowsUnauthorizedException() { @@ -501,8 +501,8 @@ public function testLoadContentByRemoteIdThrowsUnauthorizedException() /** * Test for the loadContentByRemoteId() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId($remoteId, $languages) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByRemoteIdWithSecondParameter + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByRemoteId($remoteId, $languages) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentByRemoteIdWithSecondParameter */ public function testLoadContentByRemoteIdThrowsUnauthorizedExceptionWithSecondParameter() { @@ -519,8 +519,8 @@ public function testLoadContentByRemoteIdThrowsUnauthorizedExceptionWithSecondPa /** * Test for the loadContentByRemoteId() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContentByRemoteId($remoteId, $languages, $versionNo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentByRemoteIdWithThirdParameter + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByRemoteId($remoteId, $languages, $versionNo) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentByRemoteIdWithThirdParameter */ public function testLoadContentByRemoteIdThrowsUnauthorizedExceptionWithThirdParameter() { @@ -537,8 +537,8 @@ public function testLoadContentByRemoteIdThrowsUnauthorizedExceptionWithThirdPar /** * Test for the updateContentMetadata() method. * - * @see \eZ\Publish\API\Repository\ContentService::updateContentMetadata() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContentMetadata + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContentMetadata() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testUpdateContentMetadata */ public function testUpdateContentMetadataThrowsUnauthorizedException() { @@ -568,8 +568,8 @@ public function testUpdateContentMetadataThrowsUnauthorizedException() /** * Test for the deleteContent() method. * - * @see \eZ\Publish\API\Repository\ContentService::deleteContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testDeleteContent */ public function testDeleteContentThrowsUnauthorizedException() { @@ -586,7 +586,7 @@ public function testDeleteContentThrowsUnauthorizedException() } /** - * @covers \eZ\Publish\API\Repository\ContentService::deleteContent() + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteContent() */ public function testDeleteContentThrowsUnauthorizedExceptionWithLanguageLimitation(): void { @@ -612,7 +612,7 @@ public function testDeleteContentThrowsUnauthorizedExceptionWithLanguageLimitati } /** - * @covers \eZ\Publish\API\Repository\ContentService::deleteContent() + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteContent() */ public function testDeleteContentWithLanguageLimitation(): void { @@ -638,8 +638,8 @@ public function testDeleteContentWithLanguageLimitation(): void /** * Test for the createContentDraft() method. * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraft + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContentDraft */ public function testCreateContentDraftThrowsUnauthorizedException() { @@ -658,8 +658,8 @@ public function testCreateContentDraftThrowsUnauthorizedException() /** * Test for the createContentDraft() method. * - * @see \eZ\Publish\API\Repository\ContentService::createContentDraft($contentInfo, $versionInfo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCreateContentDraftWithSecondParameter + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft($contentInfo, $versionInfo) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContentDraftWithSecondParameter */ public function testCreateContentDraftThrowsUnauthorizedExceptionWithSecondParameter() { @@ -679,7 +679,7 @@ public function testCreateContentDraftThrowsUnauthorizedExceptionWithSecondParam /** * Test for the countContentDrafts() method. * - * @see \eZ\Publish\API\Repository\ContentService::countContentDrafts() + * @covers \Ibexa\Contracts\Core\Repository\ContentService::countContentDrafts() */ public function testCountContentDraftsReturnZero() { @@ -691,9 +691,9 @@ public function testCountContentDraftsReturnZero() /** * Test for the loadContentDrafts() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentDrafts - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentDrafts + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentDrafts() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentDrafts + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentDrafts */ public function testLoadContentDraftsThrowsUnauthorizedException() { @@ -708,8 +708,8 @@ public function testLoadContentDraftsThrowsUnauthorizedException() /** * Test for the loadContentDrafts() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadContentDrafts($user) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadContentDrafts + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentDrafts($user) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentDrafts */ public function testLoadContentDraftsThrowsUnauthorizedExceptionWithUser() { @@ -724,8 +724,8 @@ public function testLoadContentDraftsThrowsUnauthorizedExceptionWithUser() /** * Test for the updateContent() method. * - * @see \eZ\Publish\API\Repository\ContentService::updateContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testUpdateContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testUpdateContent */ public function testUpdateContentThrowsUnauthorizedException() { @@ -752,8 +752,8 @@ public function testUpdateContentThrowsUnauthorizedException() /** * Test for the publishVersion() method. * - * @see \eZ\Publish\API\Repository\ContentService::publishVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testPublishVersion + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testPublishVersion */ public function testPublishVersionThrowsUnauthorizedException() { @@ -770,8 +770,8 @@ public function testPublishVersionThrowsUnauthorizedException() /** * Test for the deleteVersion() method. * - * @see \eZ\Publish\API\Repository\ContentService::deleteVersion() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteVersion + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteVersion() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testDeleteVersion */ public function testDeleteVersionThrowsUnauthorizedException() { @@ -788,8 +788,8 @@ public function testDeleteVersionThrowsUnauthorizedException() /** * Test for the loadVersions() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadVersions() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadVersions + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersions() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadVersions */ public function testLoadVersionsThrowsUnauthorizedException() { @@ -808,8 +808,8 @@ public function testLoadVersionsThrowsUnauthorizedException() /** * Test for the copyContent() method. * - * @see \eZ\Publish\API\Repository\ContentService::copyContent() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCopyContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::copyContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCopyContent */ public function testCopyContentThrowsUnauthorizedException() { @@ -844,8 +844,8 @@ public function testCopyContentThrowsUnauthorizedException() /** * Test for the copyContent() method. * - * @see \eZ\Publish\API\Repository\ContentService::copyContent($contentInfo, $destinationLocationCreateStruct, $versionInfo) - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testCopyContentWithGivenVersion + * @covers \Ibexa\Contracts\Core\Repository\ContentService::copyContent($contentInfo, $destinationLocationCreateStruct, $versionInfo) + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCopyContentWithGivenVersion */ public function testCopyContentThrowsUnauthorizedExceptionWithGivenVersion() { @@ -877,8 +877,8 @@ public function testCopyContentThrowsUnauthorizedExceptionWithGivenVersion() /** * Test for the loadRelations() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadRelations() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadRelations + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadRelations() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadRelations */ public function testLoadRelationsThrowsUnauthorizedException() { @@ -903,8 +903,8 @@ public function testLoadRelationsThrowsUnauthorizedException() /** * Test for the loadRelations() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadRelations() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadRelations + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadRelations() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadRelations */ public function testLoadRelationsForDraftVersionThrowsUnauthorizedException() { @@ -921,8 +921,8 @@ public function testLoadRelationsForDraftVersionThrowsUnauthorizedException() /** * Test for the loadReverseRelations() method. * - * @see \eZ\Publish\API\Repository\ContentService::loadReverseRelations() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testLoadReverseRelations + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadReverseRelations() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadReverseRelations */ public function testLoadReverseRelationsThrowsUnauthorizedException() { @@ -943,8 +943,8 @@ public function testLoadReverseRelationsThrowsUnauthorizedException() /** * Test for the addRelation() method. * - * @see \eZ\Publish\API\Repository\ContentService::addRelation() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation + * @covers \Ibexa\Contracts\Core\Repository\ContentService::addRelation() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testAddRelation */ public function testAddRelationThrowsUnauthorizedException() { @@ -970,8 +970,8 @@ public function testAddRelationThrowsUnauthorizedException() /** * Test for the deleteRelation() method. * - * @see \eZ\Publish\API\Repository\ContentService::deleteRelation() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testDeleteRelation + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteRelation() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testDeleteRelation */ public function testDeleteRelationThrowsUnauthorizedException() { @@ -1001,7 +1001,7 @@ public function testDeleteRelationThrowsUnauthorizedException() * Creates a pseudo editor with a limitation to objects in the "Media/Images" * subtree. * - * @return \eZ\Publish\API\Repository\Values\User\User + * @return \Ibexa\Contracts\Core\Repository\Values\User\User */ private function createAnonymousWithEditorRole() { @@ -1014,7 +1014,7 @@ private function createAnonymousWithEditorRole() $roleService->assignRoleToUser( $role, $user, - new \eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation( + new SubtreeLimitation( [ 'limitationValues' => ['/1/43/51/'], ] @@ -1029,8 +1029,8 @@ private function createAnonymousWithEditorRole() * related object, executing loadRelations() would not throw any exception, * only that the non-readable related object(s) won't be loaded. * - * @see \eZ\Publish\API\Repository\ContentService::loadRelations() - * @depends eZ\Publish\API\Repository\Tests\ContentServiceTest::testAddRelation + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadRelations() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testAddRelation */ public function testLoadRelationsWithUnauthorizedRelations() { @@ -1282,10 +1282,10 @@ public function testCopyContentToAuthorizedLocationWithSubtreeLimitation() } /** - * @return \eZ\Publish\API\Repository\Values\Content\ContentInfo + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ private function getContentInfoForAnonymousUser(): ContentInfo { @@ -1299,3 +1299,5 @@ private function setRestrictedEditorUser(): void $this->permissionResolver->setCurrentUserReference($this->createAnonymousWithEditorRole()); } } + +class_alias(ContentServiceAuthorizationTest::class, 'eZ\Publish\API\Repository\Tests\ContentServiceAuthorizationTest'); diff --git a/tests/integration/Core/Repository/ContentServiceTest.php b/tests/integration/Core/Repository/ContentServiceTest.php new file mode 100644 index 0000000000..91f42c9438 --- /dev/null +++ b/tests/integration/Core/Repository/ContentServiceTest.php @@ -0,0 +1,6756 @@ +getRepository(); + $this->permissionResolver = $repository->getPermissionResolver(); + $this->contentService = $repository->getContentService(); + $this->locationService = $repository->getLocationService(); + } + + /** + * Test for the newContentCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::newContentCreateStruct() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testLoadContentTypeByIdentifier + * @group user + * @group field-type + */ + public function testNewContentCreateStruct() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); + + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + + $this->assertInstanceOf(ContentCreateStruct::class, $contentCreate); + } + + /** + * Test for the createContent() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testNewContentCreateStruct + * @group user + * @group field-type + */ + public function testCreateContent() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); + + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + $contentCreate->setField('name', 'My awesome forum'); + + $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789'; + $contentCreate->alwaysAvailable = true; + + $content = $this->contentService->createContent($contentCreate); + + $this->assertInstanceOf(Content::class, $content); + + return $content; + } + + /** + * Test for the createContent() method. + * + * Tests made for issue #EZP-20955 where Anonymous user is granted access to create content + * and should have access to do that. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + * @group user + * @group field-type + */ + public function testCreateContentAndPublishWithPrivilegedAnonymousUser(): void + { + $anonymousUserId = $this->generateId('user', 10); + + $repository = $this->getRepository(); + $contentTypeService = $this->getRepository()->getContentTypeService(); + $roleService = $repository->getRoleService(); + + // Give Anonymous user role additional rights + $role = $roleService->loadRoleByIdentifier('Anonymous'); + $roleDraft = $roleService->createRoleDraft($role); + $policyCreateStruct = $roleService->newPolicyCreateStruct('content', 'create'); + $policyCreateStruct->addLimitation(new SectionLimitation(['limitationValues' => [1]])); + $policyCreateStruct->addLimitation(new LocationLimitation(['limitationValues' => [2]])); + $policyCreateStruct->addLimitation(new ContentTypeLimitation(['limitationValues' => [1]])); + $roleDraft = $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct); + + $policyCreateStruct = $roleService->newPolicyCreateStruct('content', 'publish'); + $policyCreateStruct->addLimitation(new SectionLimitation(['limitationValues' => [1]])); + $policyCreateStruct->addLimitation(new LocationLimitation(['limitationValues' => [2]])); + $policyCreateStruct->addLimitation(new ContentTypeLimitation(['limitationValues' => [1]])); + $roleDraft = $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct); + $roleService->publishRoleDraft($roleDraft); + + // Set Anonymous user as current + $repository->getPermissionResolver()->setCurrentUserReference($repository->getUserService()->loadUser($anonymousUserId)); + + // Create a new content object: + $contentCreate = $this->contentService->newContentCreateStruct( + $contentTypeService->loadContentTypeByIdentifier('folder'), + self::ENG_GB + ); + + $contentCreate->setField('name', 'Folder 1'); + + $content = $this->contentService->createContent( + $contentCreate, + [$this->locationService->newLocationCreateStruct(2)] + ); + + $this->contentService->publishVersion( + $content->getVersionInfo() + ); + } + + /** + * Test for the createContent() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContent + */ + public function testCreateContentSetsContentInfo($content) + { + $this->assertInstanceOf(ContentInfo::class, $content->contentInfo); + + return $content; + } + + /** + * Test for the createContent() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContentSetsContentInfo + */ + public function testCreateContentSetsExpectedContentInfo($content) + { + $permissionResolver = $this->getRepository()->getPermissionResolver(); + + $this->assertEquals( + [ + $content->id, + 28, // id of content type "forum" + true, + 1, + 'abcdef0123456789abcdef0123456789', + self::ENG_US, + $permissionResolver->getCurrentUserReference()->getUserId(), + false, + null, + // Main Location id for unpublished Content should be null + null, + ], + [ + $content->contentInfo->id, + $content->contentInfo->contentTypeId, + $content->contentInfo->alwaysAvailable, + $content->contentInfo->currentVersionNo, + $content->contentInfo->remoteId, + $content->contentInfo->mainLanguageCode, + $content->contentInfo->ownerId, + $content->contentInfo->published, + $content->contentInfo->publishedDate, + $content->contentInfo->mainLocationId, + ] + ); + } + + /** + * Test for the createContent() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContent + */ + public function testCreateContentSetsVersionInfo($content) + { + $this->assertInstanceOf(VersionInfo::class, $content->getVersionInfo()); + + return $content; + } + + /** + * Test for the createContent() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContentSetsVersionInfo + */ + public function testCreateContentSetsExpectedVersionInfo($content) + { + $currentUserReference = $this->getRepository()->getPermissionResolver()->getCurrentUserReference(); + + $this->assertEquals( + [ + 'status' => VersionInfo::STATUS_DRAFT, + 'versionNo' => 1, + 'creatorId' => $currentUserReference->getUserId(), + 'initialLanguageCode' => self::ENG_US, + ], + [ + 'status' => $content->getVersionInfo()->status, + 'versionNo' => $content->getVersionInfo()->versionNo, + 'creatorId' => $content->getVersionInfo()->creatorId, + 'initialLanguageCode' => $content->getVersionInfo()->initialLanguageCode, + ] + ); + $this->assertTrue($content->getVersionInfo()->isDraft()); + $this->assertFalse($content->getVersionInfo()->isPublished()); + $this->assertFalse($content->getVersionInfo()->isArchived()); + } + + /** + * Test for the createContent() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContent + */ + public function testCreateContentSetsExpectedContentType($content) + { + $contentType = $content->getContentType(); + + $this->assertEquals( + [ + $contentType->id, + // Won't match as it's set to true in createContentDraftVersion1() + //$contentType->defaultAlwaysAvailable, + //$contentType->defaultSortField, + //$contentType->defaultSortOrder, + ], + [ + $content->contentInfo->contentTypeId, + //$content->contentInfo->alwaysAvailable, + //$location->sortField, + //$location->sortOrder, + ] + ); + } + + /** + * Test for the createContent() method with utilizing ContentType default options. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + */ + public function testCreateContentWithContentTypeDefaultOptions(): void + { + $contentType = $this->getRepository()->getContentTypeService() + ->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); + + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_GB); + $contentCreate->setField('name', 'Sorting Test'); + + $content = $this->contentService->createContent( + $contentCreate, + [$this->locationService->newLocationCreateStruct(2)] + ); + $publishedContent = $this->contentService->publishVersion($content->getVersionInfo()); + + $location = $publishedContent->contentInfo->getMainLocation(); + + $this->assertEquals($contentType->defaultSortField, $location->sortField); + $this->assertEquals($contentType->defaultSortOrder, $location->sortOrder); + $this->assertEquals($contentType->defaultAlwaysAvailable, $publishedContent->contentInfo->alwaysAvailable); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContent + */ + public function testCreateContentThrowsInvalidArgumentException() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); + + $contentCreate1 = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + $contentCreate1->setField('name', 'An awesome Sidelfingen forum'); + + $contentCreate1->remoteId = 'abcdef0123456789abcdef0123456789'; + $contentCreate1->alwaysAvailable = true; + + $draft = $this->contentService->createContent($contentCreate1); + $this->contentService->publishVersion($draft->versionInfo); + + $contentCreate2 = $this->contentService->newContentCreateStruct($contentType, self::ENG_GB); + $contentCreate2->setField('name', 'An awesome Bielefeld forum'); + + $contentCreate2->remoteId = 'abcdef0123456789abcdef0123456789'; + $contentCreate2->alwaysAvailable = false; + + $this->expectException(APIInvalidArgumentException::class); + $this->contentService->createContent($contentCreate2); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContent + */ + public function testCreateContentThrowsInvalidArgumentExceptionOnFieldTypeNotAccept() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); + + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + // The name field does only accept strings and null as its values + $contentCreate->setField('name', new \stdClass()); + + $this->expectException(APIInvalidArgumentException::class); + $this->contentService->createContent($contentCreate); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContent + */ + public function testCreateContentThrowsContentFieldValidationException() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + + $contentCreate1 = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + $contentCreate1->setField('name', 'An awesome Sidelfingen folder'); + // Violates string length constraint + $contentCreate1->setField('short_name', str_repeat('a', 200)); + + $this->expectException(ContentFieldValidationException::class); + + // Throws ContentFieldValidationException, since short_name does not pass validation of the string length validator + $this->contentService->createContent($contentCreate1); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContent + */ + public function testCreateContentRequiredFieldMissing() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); + + $contentCreate1 = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + // Required field "name" is not set + + $this->expectException(ContentFieldValidationException::class); + + // Throws a ContentFieldValidationException, since a required field is missing + $this->contentService->createContent($contentCreate1); + } + + /** + * Test for the createContent() method. + * + * NOTE: We have bidirectional dependencies between the ContentService and + * the LocationService, so that we cannot use PHPUnit's test dependencies + * here. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + * @depends testCreateContent + * @group user + */ + public function testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately() + { + $this->createContentDraftVersion1(); + + $this->expectException(NotFoundException::class); + + // The location will not have been created, yet, so this throws an exception + $this->locationService->loadLocationByRemoteId('0123456789abcdef0123456789abcdef'); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs) + * @depends testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately + */ + public function testCreateContentThrowsInvalidArgumentExceptionWithLocationCreateParameter() + { + $parentLocationId = $this->generateId('location', 56); + // $parentLocationId is a valid location ID + + $contentTypeService = $this->getRepository()->getContentTypeService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); + + // Configure new locations + $locationCreate1 = $this->locationService->newLocationCreateStruct($parentLocationId); + + $locationCreate1->priority = 23; + $locationCreate1->hidden = true; + $locationCreate1->remoteId = '0123456789abcdef0123456789aaaaaa'; + $locationCreate1->sortField = Location::SORT_FIELD_NODE_ID; + $locationCreate1->sortOrder = Location::SORT_ORDER_DESC; + + $locationCreate2 = $this->locationService->newLocationCreateStruct($parentLocationId); + + $locationCreate2->priority = 42; + $locationCreate2->hidden = true; + $locationCreate2->remoteId = '0123456789abcdef0123456789bbbbbb'; + $locationCreate2->sortField = Location::SORT_FIELD_NODE_ID; + $locationCreate2->sortOrder = Location::SORT_ORDER_DESC; + + // Configure new content object + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + + $contentCreate->setField('name', 'A awesome Sindelfingen forum'); + $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789'; + $contentCreate->alwaysAvailable = true; + + // Create new content object under the specified location + $draft = $this->contentService->createContent( + $contentCreate, + [$locationCreate1] + ); + $this->contentService->publishVersion($draft->versionInfo); + + $this->expectException(APIInvalidArgumentException::class); + // Content remoteId already exists, + $this->contentService->createContent( + $contentCreate, + [$locationCreate2] + ); + } + + /** + * Test for the loadContentInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfo() + * @group user + */ + public function testLoadContentInfo() + { + $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); + + // Load the ContentInfo for "Media" folder + $contentInfo = $this->contentService->loadContentInfo($mediaFolderId); + + $this->assertInstanceOf(ContentInfo::class, $contentInfo); + + return $contentInfo; + } + + /** + * Test for the returned value of the loadContentInfo() method. + * + * @depends testLoadContentInfo + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfo + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + */ + public function testLoadContentInfoSetsExpectedContentInfo(ContentInfo $contentInfo) + { + $this->assertPropertiesCorrectUnsorted( + $this->getExpectedMediaContentInfoProperties(), + $contentInfo + ); + } + + /** + * @depends testLoadContentInfo + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfo + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + */ + public function testLoadContentInfoGetContentType(ContentInfo $contentInfo): void + { + $contentType = $contentInfo->getContentType(); + + $this->assertInstanceOf(ContentType::class, $contentType); + $this->assertEquals('folder', $contentType->identifier); + } + + /** + * @depends testLoadContentInfo + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfo + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + */ + public function testLoadContentInfoGetSection(ContentInfo $contentInfo): void + { + $section = $contentInfo->getSection(); + + $this->assertInstanceOf(Section::class, $section); + $this->assertEquals('media', $section->identifier); + } + + /** + * @depends testLoadContentInfo + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfo + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + */ + public function testLoadContentInfoGetMainLanguage(ContentInfo $contentInfo): void + { + $language = $contentInfo->getMainLanguage(); + + $this->assertInstanceOf(Language::class, $language); + $this->assertEquals('eng-US', $language->languageCode); + } + + /** + * @depends testLoadContentInfo + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfo + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + */ + public function testLoadContentInfoGetMainLocation(ContentInfo $contentInfo): void + { + $mainLocation = $contentInfo->getMainLocation(); + + $this->assertInstanceOf(Location::class, $mainLocation); + $this->assertEquals('75c715a51699d2d309a924eca6a95145', $mainLocation->remoteId); + } + + /** + * @depends testLoadContentInfo + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfo + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + */ + public function testLoadContentInfoSetsExpectedOwnerProxy(ContentInfo $contentInfo): void + { + $owner = $contentInfo->getOwner(); + + $this->assertInstanceOf(User::class, $owner); + $this->assertEquals('Administrator User', $owner->getName()); + } + + /** + * Test for the loadContentInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfo() + * @depends testLoadContentInfo + */ + public function testLoadContentInfoThrowsNotFoundException() + { + $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX); + + $this->expectException(NotFoundException::class); + + $this->contentService->loadContentInfo($nonExistentContentId); + } + + /** + * Test for the loadContentInfoList() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfoList() + */ + public function testLoadContentInfoList() + { + $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); + $list = $this->contentService->loadContentInfoList([$mediaFolderId]); + + $this->assertCount(1, $list); + $this->assertEquals([$mediaFolderId], array_keys($list), 'Array key was not content id'); + $this->assertInstanceOf( + ContentInfo::class, + $list[$mediaFolderId] + ); + } + + /** + * Test for the loadContentInfoList() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfoList() + * @depends testLoadContentInfoList + */ + public function testLoadContentInfoListSkipsNotFoundItems() + { + $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX); + $list = $this->contentService->loadContentInfoList([$nonExistentContentId]); + + $this->assertCount(0, $list); + } + + /** + * Test for the loadContentInfoByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfoByRemoteId() + */ + public function testLoadContentInfoByRemoteId() + { + // Load the ContentInfo for "Media" folder + $contentInfo = $this->contentService->loadContentInfoByRemoteId('faaeb9be3bd98ed09f606fc16d144eca'); + + $this->assertInstanceOf(ContentInfo::class, $contentInfo); + + return $contentInfo; + } + + /** + * Test for the returned value of the loadContentInfoByRemoteId() method. + * + * @depends testLoadContentInfoByRemoteId + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfoByRemoteId + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + */ + public function testLoadContentInfoByRemoteIdSetsExpectedContentInfo(ContentInfo $contentInfo) + { + $this->assertPropertiesCorrectUnsorted( + [ + 'id' => 10, + 'contentTypeId' => 4, + 'name' => 'Anonymous User', + 'sectionId' => 2, + 'currentVersionNo' => 2, + 'published' => true, + 'ownerId' => 14, + 'modificationDate' => $this->createDateTime(1072180405), + 'publishedDate' => $this->createDateTime(1033920665), + 'alwaysAvailable' => 1, + 'remoteId' => 'faaeb9be3bd98ed09f606fc16d144eca', + 'mainLanguageCode' => self::ENG_US, + 'mainLocationId' => 45, + ], + $contentInfo + ); + } + + /** + * Test for the loadContentInfoByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentInfoByRemoteId() + * @depends testLoadContentInfoByRemoteId + */ + public function testLoadContentInfoByRemoteIdThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $this->contentService->loadContentInfoByRemoteId('abcdefghijklmnopqrstuvwxyz0123456789'); + } + + /** + * Test for the loadVersionInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfo() + * @depends testLoadContentInfo + * @group user + */ + public function testLoadVersionInfo() + { + $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); + // $mediaFolderId contains the ID of the "Media" folder + + // Load the ContentInfo for "Media" folder + $contentInfo = $this->contentService->loadContentInfo($mediaFolderId); + + // Now load the current version info of the "Media" folder + $versionInfo = $this->contentService->loadVersionInfo($contentInfo); + + $this->assertInstanceOf( + VersionInfo::class, + $versionInfo + ); + } + + /** + * Test for the loadVersionInfoById() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById() + */ + public function testLoadVersionInfoById() + { + $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); + // $mediaFolderId contains the ID of the "Media" folder + + // Load the VersionInfo for "Media" folder + $versionInfo = $this->contentService->loadVersionInfoById($mediaFolderId); + + $this->assertInstanceOf( + VersionInfo::class, + $versionInfo + ); + + return $versionInfo; + } + + /** + * Test for the returned value of the loadVersionInfoById() method. + * + * @depends testLoadVersionInfoById + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + */ + public function testLoadVersionInfoByIdSetsExpectedVersionInfo(VersionInfo $versionInfo) + { + $this->assertPropertiesCorrect( + [ + 'names' => [ + self::ENG_US => 'Media', + ], + 'contentInfo' => new ContentInfo($this->getExpectedMediaContentInfoProperties()), + 'id' => 472, + 'versionNo' => 1, + 'modificationDate' => $this->createDateTime(1060695457), + 'creatorId' => 14, + 'creationDate' => $this->createDateTime(1060695450), + 'status' => VersionInfo::STATUS_PUBLISHED, + 'initialLanguageCode' => self::ENG_US, + 'languageCodes' => [ + self::ENG_US, + ], + ], + $versionInfo + ); + $this->assertTrue($versionInfo->isPublished()); + $this->assertFalse($versionInfo->isDraft()); + $this->assertFalse($versionInfo->isArchived()); + } + + /** + * @depends testLoadVersionInfoById + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + */ + public function testLoadVersionInfoByIdGetCreator(VersionInfo $versionInfo): void + { + $creator = $versionInfo->getCreator(); + + $this->assertInstanceOf(User::class, $creator); + $this->assertEquals('Administrator User', $creator->getName()); + } + + /** + * @depends testLoadVersionInfoById + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + */ + public function testLoadVersionInfoByIdGetInitialLanguage(VersionInfo $versionInfo): void + { + $initialLanguage = $versionInfo->getInitialLanguage(); + + $this->assertInstanceOf(Language::class, $initialLanguage); + $this->assertEquals('eng-US', $initialLanguage->languageCode); + } + + /** + * @depends testLoadVersionInfoById + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo + */ + public function testLoadVersionInfoByIdGetLanguages(VersionInfo $versionInfo): void + { + $actualLanguages = $versionInfo->getLanguages(); + + $expectedLanguages = ['eng-US']; + foreach ($expectedLanguages as $i => $expectedLanguage) { + $this->assertEquals($expectedLanguage, $actualLanguages[$i]->languageCode); + } + } + + /** + * Test for the loadVersionInfoById() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById() + * @depends testLoadVersionInfoById + */ + public function testLoadVersionInfoByIdThrowsNotFoundException() + { + $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX); + + $this->expectException(NotFoundException::class); + + $this->contentService->loadVersionInfoById($nonExistentContentId); + } + + /** + * Test for the loadContentByContentInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByContentInfo() + * @depends testLoadContentInfo + */ + public function testLoadContentByContentInfo() + { + $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); + // $mediaFolderId contains the ID of the "Media" folder + + // Load the ContentInfo for "Media" folder + $contentInfo = $this->contentService->loadContentInfo($mediaFolderId); + + // Now load the current content version for the info instance + $content = $this->contentService->loadContentByContentInfo($contentInfo); + + $this->assertInstanceOf( + Content::class, + $content + ); + } + + /** + * Test for the loadContentByVersionInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByVersionInfo() + * @depends testLoadVersionInfo + */ + public function testLoadContentByVersionInfo() + { + $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); + // $mediaFolderId contains the ID of the "Media" folder + + // Load the ContentInfo for "Media" folder + $contentInfo = $this->contentService->loadContentInfo($mediaFolderId); + + // Load the current VersionInfo + $versionInfo = $this->contentService->loadVersionInfo($contentInfo); + + // Now load the current content version for the info instance + $content = $this->contentService->loadContentByVersionInfo($versionInfo); + + $this->assertInstanceOf( + Content::class, + $content + ); + } + + /** + * Test for the loadContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContent() + * @group user + * @group field-type + */ + public function testLoadContent() + { + $mediaFolderId = $this->generateId('object', self::MEDIA_CONTENT_ID); + // $mediaFolderId contains the ID of the "Media" folder + + // Load the Content for "Media" folder, any language and current version + $content = $this->contentService->loadContent($mediaFolderId); + + $this->assertInstanceOf( + Content::class, + $content + ); + } + + /** + * Test for the loadContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContent() + * @depends testLoadContent + */ + public function testLoadContentThrowsNotFoundException() + { + $nonExistentContentId = $this->generateId('object', self::DB_INT_MAX); + + $this->expectException(NotFoundException::class); + + $this->contentService->loadContent($nonExistentContentId); + } + + /** + * Data provider for testLoadContentByRemoteId(). + * + * @return array + */ + public function contentRemoteIdVersionLanguageProvider() + { + return [ + ['f5c88a2209584891056f987fd965b0ba', null, null], + ['f5c88a2209584891056f987fd965b0ba', [self::ENG_US], null], + ['f5c88a2209584891056f987fd965b0ba', null, 1], + ['f5c88a2209584891056f987fd965b0ba', [self::ENG_US], 1], + [self::MEDIA_REMOTE_ID, null, null], + [self::MEDIA_REMOTE_ID, [self::ENG_US], null], + [self::MEDIA_REMOTE_ID, null, 1], + [self::MEDIA_REMOTE_ID, [self::ENG_US], 1], + ]; + } + + /** + * Test for the loadContentByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByRemoteId + * @dataProvider contentRemoteIdVersionLanguageProvider + * + * @param string $remoteId + * @param array|null $languages + * @param int $versionNo + */ + public function testLoadContentByRemoteId($remoteId, $languages, $versionNo) + { + $content = $this->contentService->loadContentByRemoteId($remoteId, $languages, $versionNo); + + $this->assertInstanceOf( + Content::class, + $content + ); + + $this->assertEquals($remoteId, $content->contentInfo->remoteId); + if ($languages !== null) { + $this->assertEquals($languages, $content->getVersionInfo()->languageCodes); + } + $this->assertEquals($versionNo ?: 1, $content->getVersionInfo()->versionNo); + } + + /** + * Test for the loadContentByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByRemoteId() + * @depends testLoadContentByRemoteId + */ + public function testLoadContentByRemoteIdThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + // This call will fail with a "NotFoundException", because no content object exists for the given remoteId + $this->contentService->loadContentByRemoteId('a1b1c1d1e1f1a2b2c2d2e2f2a3b3c3d3'); + } + + /** + * Test for the publishVersion() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends testLoadContent + * @depends testLoadContentInfo + * @depends testLoadVersionInfo + * @depends testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately + * @group user + * @group field-type + */ + public function testPublishVersion() + { + $time = time(); + $content = $this->createContentVersion1(); + + $this->assertInstanceOf(Content::class, $content); + $this->assertTrue($content->contentInfo->published); + $this->assertEquals(VersionInfo::STATUS_PUBLISHED, $content->versionInfo->status); + $this->assertGreaterThanOrEqual($time, $content->contentInfo->publishedDate->getTimestamp()); + $this->assertGreaterThanOrEqual($time, $content->contentInfo->modificationDate->getTimestamp()); + $this->assertTrue($content->versionInfo->isPublished()); + $this->assertFalse($content->versionInfo->isDraft()); + $this->assertFalse($content->versionInfo->isArchived()); + + return $content; + } + + /** + * Test for the publishVersion() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends testPublishVersion + */ + public function testPublishVersionSetsExpectedContentInfo($content) + { + $userReference = $this->getRepository()->getPermissionResolver()->getCurrentUserReference(); + + $this->assertEquals( + [ + $content->id, + true, + 1, + 'abcdef0123456789abcdef0123456789', + self::ENG_US, + $userReference->getUserId(), + true, + ], + [ + $content->contentInfo->id, + $content->contentInfo->alwaysAvailable, + $content->contentInfo->currentVersionNo, + $content->contentInfo->remoteId, + $content->contentInfo->mainLanguageCode, + $content->contentInfo->ownerId, + $content->contentInfo->published, + ] + ); + + $this->assertNotNull($content->contentInfo->mainLocationId); + $date = new \DateTime('1984/01/01'); + $this->assertGreaterThan( + $date->getTimestamp(), + $content->contentInfo->publishedDate->getTimestamp() + ); + } + + /** + * Test for the publishVersion() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends testPublishVersion + */ + public function testPublishVersionSetsExpectedVersionInfo($content) + { + $currentUserReference = $this->getRepository()->getPermissionResolver()->getCurrentUserReference(); + + $this->assertEquals( + [ + $currentUserReference->getUserId(), + self::ENG_US, + VersionInfo::STATUS_PUBLISHED, + 1, + ], + [ + $content->getVersionInfo()->creatorId, + $content->getVersionInfo()->initialLanguageCode, + $content->getVersionInfo()->status, + $content->getVersionInfo()->versionNo, + ] + ); + + $date = new \DateTime('1984/01/01'); + $this->assertGreaterThan( + $date->getTimestamp(), + $content->getVersionInfo()->modificationDate->getTimestamp() + ); + + $this->assertNotNull($content->getVersionInfo()->modificationDate); + $this->assertTrue($content->getVersionInfo()->isPublished()); + $this->assertFalse($content->getVersionInfo()->isDraft()); + $this->assertFalse($content->getVersionInfo()->isArchived()); + } + + /** + * Test for the publishVersion() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends testPublishVersion + */ + public function testPublishVersionSetsExpectedContentType($content) + { + $contentType = $content->getContentType(); + + $this->assertEquals( + [ + $contentType->id, + // won't be a match as it's set to true in createContentDraftVersion1() + //$contentType->defaultAlwaysAvailable, + //$contentType->defaultSortField, + //$contentType->defaultSortOrder, + ], + [ + $content->contentInfo->contentTypeId, + //$content->contentInfo->alwaysAvailable, + //$location->sortField, + //$location->sortOrder, + ] + ); + } + + /** + * Test for the publishVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion + */ + public function testPublishVersionCreatesLocationsDefinedOnCreate(): array + { + $content = $this->createContentVersion1(); + + $location = $this->locationService->loadLocationByRemoteId( + '0123456789abcdef0123456789abcdef' + ); + + $this->assertEquals( + $location->getContentInfo(), + $content->getVersionInfo()->getContentInfo() + ); + + return [$content, $location]; + } + + /** + * Test for the publishVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends testPublishVersionCreatesLocationsDefinedOnCreate + */ + public function testCreateContentWithLocationCreateParameterCreatesExpectedLocation(array $testData) + { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $content */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $location */ + list($content, $location) = $testData; + + $parentLocationId = $this->generateId('location', 56); + $parentLocation = $this->getRepository()->getLocationService()->loadLocation($parentLocationId); + $mainLocationId = $content->getVersionInfo()->getContentInfo()->mainLocationId; + + $this->assertPropertiesCorrect( + [ + 'id' => $mainLocationId, + 'priority' => 23, + 'hidden' => true, + 'invisible' => true, + 'remoteId' => '0123456789abcdef0123456789abcdef', + 'parentLocationId' => $parentLocationId, + 'pathString' => $parentLocation->pathString . $mainLocationId . '/', + 'depth' => $parentLocation->depth + 1, + 'sortField' => Location::SORT_FIELD_NODE_ID, + 'sortOrder' => Location::SORT_ORDER_DESC, + ], + $location + ); + } + + /** + * Test for the publishVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends testPublishVersion + */ + public function testPublishVersionThrowsBadStateException() + { + $draft = $this->createContentDraftVersion1(); + + // Publish the content draft + $this->contentService->publishVersion($draft->getVersionInfo()); + + $this->expectException(BadStateException::class); + + // This call will fail with a "BadStateException", because the version is already published. + $this->contentService->publishVersion($draft->getVersionInfo()); + } + + /** + * Test that publishVersion() does not affect publishedDate (assuming previous version exists). + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion + */ + public function testPublishVersionDoesNotChangePublishedDate() + { + $publishedContent = $this->createContentVersion1(); + + // force timestamps to differ + sleep(1); + + $contentDraft = $this->contentService->createContentDraft($publishedContent->contentInfo); + $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); + $contentUpdateStruct->setField('name', 'New name'); + $contentDraft = $this->contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct); + $republishedContent = $this->contentService->publishVersion($contentDraft->versionInfo); + + $this->assertEquals( + $publishedContent->contentInfo->publishedDate->getTimestamp(), + $republishedContent->contentInfo->publishedDate->getTimestamp() + ); + $this->assertGreaterThan( + $publishedContent->contentInfo->modificationDate->getTimestamp(), + $republishedContent->contentInfo->modificationDate->getTimestamp() + ); + } + + /** + * Test for the createContentDraft() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft() + * @depends testPublishVersion + * @group user + */ + public function testCreateContentDraft() + { + $content = $this->createContentVersion1(); + + // Now we create a new draft from the published content + $draftedContent = $this->contentService->createContentDraft($content->contentInfo); + + $this->assertInstanceOf( + Content::class, + $draftedContent + ); + + return $draftedContent; + } + + /** + * Test for the createContentDraft() method with given language for new draft. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft + */ + public function testCreateContentDraftInOtherLanguage() + { + $content = $this->createContentVersion1(); + + $language = $this->getRepository()->getContentLanguageService()->loadLanguage('eng-GB'); + + // Now we create a new draft from the published content + $draftedContent = $this->contentService->createContentDraft( + $content->contentInfo, + null, + null, + $language + ); + + $this->assertEquals('eng-US', $content->versionInfo->initialLanguageCode); + $this->assertEquals('eng-GB', $draftedContent->versionInfo->initialLanguageCode); + } + + /** + * Test for the createContentDraft() method. + * + * Test that editor has access to edit own draft. + * Note: Editors have access to version_read, which is needed to load content drafts. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft() + * @depends testPublishVersion + * @group user + */ + public function testCreateContentDraftAndLoadAccess() + { + $user = $this->createUserVersion1(); + + // Set new editor as user + $this->permissionResolver->setCurrentUserReference($user); + + // Create draft + $draft = $this->createContentDraftVersion1(2, 'folder'); + + // Try to load the draft + $loadedDraft = $this->contentService->loadContent($draft->id); + + $this->assertEquals($draft->id, $loadedDraft->id); + } + + /** + * Test for the createContentDraft() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $draft + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft() + * @depends testCreateContentDraft + */ + public function testCreateContentDraftSetsExpectedProperties($draft) + { + $this->assertEquals( + [ + 'fieldCount' => 1, + 'relationCount' => 0, + ], + [ + 'fieldCount' => count($draft->getFields()), + 'relationCount' => count($this->getRepository()->getContentService()->loadRelations($draft->getVersionInfo())), + ] + ); + } + + /** + * Test for the createContentDraft() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $draft + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft() + * @depends testCreateContentDraft + */ + public function testCreateContentDraftSetsContentInfo($draft) + { + $contentInfo = $draft->contentInfo; + $currentUserReference = $this->getRepository()->getPermissionResolver()->getCurrentUserReference(); + + $this->assertEquals( + [ + $draft->id, + true, + 1, + self::ENG_US, + $currentUserReference->getUserId(), + 'abcdef0123456789abcdef0123456789', + 1, + ], + [ + $contentInfo->id, + $contentInfo->alwaysAvailable, + $contentInfo->currentVersionNo, + $contentInfo->mainLanguageCode, + $contentInfo->ownerId, + $contentInfo->remoteId, + $contentInfo->getSectionId(), + ] + ); + } + + /** + * Test for the createContentDraft() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $draft + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft() + * @depends testCreateContentDraft + */ + public function testCreateContentDraftSetsVersionInfo($draft) + { + $versionInfo = $draft->getVersionInfo(); + $currentUserReference = $this->getRepository()->getPermissionResolver()->getCurrentUserReference(); + + $this->assertEquals( + [ + 'creatorId' => $currentUserReference->getUserId(), + 'initialLanguageCode' => self::ENG_US, + 'languageCodes' => [0 => self::ENG_US], + 'status' => VersionInfo::STATUS_DRAFT, + 'versionNo' => 2, + ], + [ + 'creatorId' => $versionInfo->creatorId, + 'initialLanguageCode' => $versionInfo->initialLanguageCode, + 'languageCodes' => $versionInfo->languageCodes, + 'status' => $versionInfo->status, + 'versionNo' => $versionInfo->versionNo, + ] + ); + $this->assertTrue($versionInfo->isDraft()); + $this->assertFalse($versionInfo->isPublished()); + $this->assertFalse($versionInfo->isArchived()); + } + + /** + * Test for the createContentDraft() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $draft + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft() + * @depends testCreateContentDraft + * @depends testLoadVersionInfo + */ + public function testCreateContentDraftLoadVersionInfoStillLoadsPublishedVersion($draft) + { + $content = $this->createContentVersion1(); + + // Now we create a new draft from the published content + $this->contentService->createContentDraft($content->contentInfo); + + // This call will still load the published version + $versionInfoPublished = $this->contentService->loadVersionInfo($content->contentInfo); + + $this->assertEquals(1, $versionInfoPublished->versionNo); + } + + /** + * Test for the createContentDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft() + * @depends testLoadContent + * @depends testCreateContentDraft + */ + public function testCreateContentDraftLoadContentStillLoadsPublishedVersion() + { + $content = $this->createContentVersion1(); + + // Now we create a new draft from the published content + $this->contentService->createContentDraft($content->contentInfo); + + // This call will still load the published content version + $contentPublished = $this->contentService->loadContent($content->id); + + $this->assertEquals(1, $contentPublished->getVersionInfo()->versionNo); + } + + /** + * Test for the createContentDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft() + * @depends testLoadContentByRemoteId + * @depends testCreateContentDraft + */ + public function testCreateContentDraftLoadContentByRemoteIdStillLoadsPublishedVersion() + { + $content = $this->createContentVersion1(); + + // Now we create a new draft from the published content + $this->contentService->createContentDraft($content->contentInfo); + + // This call will still load the published content version + $contentPublished = $this->contentService->loadContentByRemoteId('abcdef0123456789abcdef0123456789'); + + $this->assertEquals(1, $contentPublished->getVersionInfo()->versionNo); + } + + /** + * Test for the createContentDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft() + * @depends testLoadContentByContentInfo + * @depends testCreateContentDraft + */ + public function testCreateContentDraftLoadContentByContentInfoStillLoadsPublishedVersion() + { + $content = $this->createContentVersion1(); + + // Now we create a new draft from the published content + $this->contentService->createContentDraft($content->contentInfo); + + // This call will still load the published content version + $contentPublished = $this->contentService->loadContentByContentInfo($content->contentInfo); + + $this->assertEquals(1, $contentPublished->getVersionInfo()->versionNo); + } + + /** + * Test for the newContentUpdateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::newContentUpdateStruct + * @group user + */ + public function testNewContentUpdateStruct() + { + $updateStruct = $this->contentService->newContentUpdateStruct(); + + $this->assertInstanceOf( + ContentUpdateStruct::class, + $updateStruct + ); + + $this->assertPropertiesCorrect( + [ + 'initialLanguageCode' => null, + 'fields' => [], + ], + $updateStruct + ); + } + + /** + * Test for the updateContent() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testNewContentUpdateStruct + * @depends testCreateContentDraft + * @group user + * @group field-type + */ + public function testUpdateContent() + { + $draftVersion2 = $this->createUpdatedDraftVersion2(); + + $this->assertInstanceOf( + Content::class, + $draftVersion2 + ); + + $this->assertEquals( + $this->generateId('user', 10), + $draftVersion2->versionInfo->creatorId, + 'creatorId is not properly set on new Version' + ); + + return $draftVersion2; + } + + /** + * Test for the updateContent_WithDifferentUser() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testNewContentUpdateStruct + * @depends testCreateContentDraft + * @group user + * @group field-type + */ + public function testUpdateContentWithDifferentUser() + { + $arrayWithDraftVersion2 = $this->createUpdatedDraftVersion2NotAdmin(); + + $this->assertInstanceOf( + Content::class, + $arrayWithDraftVersion2[0] + ); + + $this->assertEquals( + $this->generateId('user', $arrayWithDraftVersion2[1]), + $arrayWithDraftVersion2[0]->versionInfo->creatorId, + 'creatorId is not properly set on new Version' + ); + + return $arrayWithDraftVersion2[0]; + } + + /** + * Test for the updateContent() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContent + */ + public function testUpdateContentSetsExpectedFields($content) + { + $actual = $this->normalizeFields($content->getFields()); + + $expected = [ + new Field( + [ + 'id' => 0, + 'value' => true, + 'languageCode' => self::ENG_GB, + 'fieldDefIdentifier' => 'name', + 'fieldTypeIdentifier' => 'ezstring', + ] + ), + new Field( + [ + 'id' => 0, + 'value' => true, + 'languageCode' => self::ENG_US, + 'fieldDefIdentifier' => 'name', + 'fieldTypeIdentifier' => 'ezstring', + ] + ), + ]; + + $this->assertEquals($expected, $actual); + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContent + */ + public function testUpdateContentThrowsBadStateException() + { + $content = $this->createContentVersion1(); + + // Now create an update struct and modify some fields + $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); + $contentUpdateStruct->setField('name', 'An awesome² story about ezp.'); + $contentUpdateStruct->setField('name', 'An awesome²³ story about ezp.', self::ENG_GB); + + $contentUpdateStruct->initialLanguageCode = self::ENG_US; + + $this->expectException(BadStateException::class); + + // This call will fail with a "BadStateException", because $publishedContent is not a draft. + $this->contentService->updateContent( + $content->getVersionInfo(), + $contentUpdateStruct + ); + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContent + */ + public function testUpdateContentThrowsInvalidArgumentExceptionWhenFieldTypeDoesNotAccept() + { + $draft = $this->createContentDraftVersion1(); + + // Now create an update struct and modify some fields + $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); + // The name field does not accept a stdClass object as its input + $contentUpdateStruct->setField('name', new \stdClass(), self::ENG_US); + + $this->expectException(APIInvalidArgumentException::class); + // is not accepted + $this->contentService->updateContent( + $draft->getVersionInfo(), + $contentUpdateStruct + ); + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContent + */ + public function testUpdateContentWhenMandatoryFieldIsEmpty() + { + $draft = $this->createContentDraftVersion1(); + + // Now create an update struct and set a mandatory field to null + $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); + $contentUpdateStruct->setField('name', null); + + // Don't set this, then the above call without languageCode will fail + $contentUpdateStruct->initialLanguageCode = self::ENG_US; + + $this->expectException(ContentFieldValidationException::class); + + // This call will fail with a "ContentFieldValidationException", because the mandatory "name" field is empty. + $this->contentService->updateContent( + $draft->getVersionInfo(), + $contentUpdateStruct + ); + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContent + */ + public function testUpdateContentThrowsContentFieldValidationException() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + $contentCreate->setField('name', 'An awesome Sidelfingen folder'); + + $draft = $this->contentService->createContent($contentCreate); + + $contentUpdate = $this->contentService->newContentUpdateStruct(); + // Violates string length constraint + $contentUpdate->setField('short_name', str_repeat('a', 200), self::ENG_US); + + $this->expectException(ContentFieldValidationException::class); + + // Throws ContentFieldValidationException because the string length validation of the field "short_name" fails + $this->contentService->updateContent($draft->getVersionInfo(), $contentUpdate); + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContent + */ + public function testUpdateContentValidatorIgnoresRequiredFieldsOfNotUpdatedLanguages() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + + // Create multilangual content + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + $contentCreate->setField('name', 'An awesome Sidelfingen folder', self::ENG_US); + $contentCreate->setField('name', 'An awesome Sidelfingen folder', self::ENG_GB); + + $contentDraft = $this->contentService->createContent($contentCreate); + + // 2. Update content type definition + $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); + + $fieldDefinition = $contentType->getFieldDefinition('short_name'); + $fieldDefinitionUpdate = $contentTypeService->newFieldDefinitionUpdateStruct(); + $fieldDefinitionUpdate->identifier = 'short_name'; + $fieldDefinitionUpdate->isRequired = true; + + $contentTypeService->updateFieldDefinition( + $contentTypeDraft, + $fieldDefinition, + $fieldDefinitionUpdate + ); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + // 3. Update only eng-US translation + $contentUpdate = $this->contentService->newContentUpdateStruct(); + $contentUpdate->setField('name', 'An awesome Sidelfingen folder (updated)', self::ENG_US); + $contentUpdate->setField('short_name', 'Lorem ipsum dolor'); + + $this->contentService->updateContent($contentDraft->getVersionInfo(), $contentUpdate); + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContent + */ + public function testUpdateContentWithNotUpdatingMandatoryField() + { + $draft = $this->createContentDraftVersion1(); + + // Now create an update struct which does not overwrite mandatory + // fields + $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); + + // Don't set this, then the above call without languageCode will fail + $contentUpdateStruct->initialLanguageCode = self::ENG_US; + + // This will only update the "description" field in the "eng-US" language + $updatedDraft = $this->contentService->updateContent( + $draft->getVersionInfo(), + $contentUpdateStruct + ); + + foreach ($updatedDraft->getFields() as $field) { + if ($field->languageCode === self::ENG_US && $field->fieldDefIdentifier === 'name' && $field->value !== null) { + // Found field + return; + } + } + $this->fail( + 'Field with identifier "name" in language "eng-US" could not be found or has empty value.' + ); + } + + /** + * Test for the createContentDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft($contentInfo, $versionInfo) + * @depends testUpdateContent + */ + public function testCreateContentDraftWithSecondParameter() + { + $contentVersion2 = $this->createContentVersion2(); + + // Now we create a new draft from the initial version + $draftedContentReloaded = $this->contentService->createContentDraft( + $contentVersion2->contentInfo, + $contentVersion2->getVersionInfo() + ); + + $this->assertEquals(3, $draftedContentReloaded->getVersionInfo()->versionNo); + } + + /** + * Test for the createContentDraft() method with third parameter. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft + */ + public function testCreateContentDraftWithThirdParameter() + { + $content = $this->contentService->loadContent(4); + $user = $this->createUserVersion1(); + + $draftContent = $this->contentService->createContentDraft( + $content->contentInfo, + $content->getVersionInfo(), + $user + ); + + $this->assertInstanceOf( + Content::class, + $draftContent + ); + } + + /** + * Test for the publishVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends testPublishVersion + * @depends testUpdateContent + */ + public function testPublishVersionFromContentDraft() + { + $contentVersion2 = $this->createContentVersion2(); + + $versionInfo = $this->contentService->loadVersionInfo($contentVersion2->contentInfo); + + $this->assertEquals( + [ + 'status' => VersionInfo::STATUS_PUBLISHED, + 'versionNo' => 2, + ], + [ + 'status' => $versionInfo->status, + 'versionNo' => $versionInfo->versionNo, + ] + ); + $this->assertTrue($versionInfo->isPublished()); + $this->assertFalse($versionInfo->isDraft()); + $this->assertFalse($versionInfo->isArchived()); + } + + /** + * Test for the publishVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends testPublishVersionFromContentDraft + */ + public function testPublishVersionFromContentDraftArchivesOldVersion() + { + $contentVersion2 = $this->createContentVersion2(); + + $versionInfo = $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 1); + + $this->assertEquals( + [ + 'status' => VersionInfo::STATUS_ARCHIVED, + 'versionNo' => 1, + ], + [ + 'status' => $versionInfo->status, + 'versionNo' => $versionInfo->versionNo, + ] + ); + $this->assertTrue($versionInfo->isArchived()); + $this->assertFalse($versionInfo->isDraft()); + $this->assertFalse($versionInfo->isPublished()); + } + + /** + * Test for the publishVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends testPublishVersionFromContentDraft + */ + public function testPublishVersionFromContentDraftUpdatesContentInfoCurrentVersion() + { + $contentVersion2 = $this->createContentVersion2(); + + $this->assertEquals(2, $contentVersion2->contentInfo->currentVersionNo); + } + + /** + * Test for the publishVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends testPublishVersionFromContentDraft + */ + public function testPublishVersionFromOldContentDraftArchivesNewerVersionNo() + { + $content = $this->createContentVersion1(); + + // Create a new draft with versionNo = 2 + $draftedContentVersion2 = $this->contentService->createContentDraft($content->contentInfo); + + // Create another new draft with versionNo = 3 + $draftedContentVersion3 = $this->contentService->createContentDraft($content->contentInfo); + + // Publish draft with versionNo = 3 + $this->contentService->publishVersion($draftedContentVersion3->getVersionInfo()); + + // Publish the first draft with versionNo = 2 + // currentVersionNo is now 2, versionNo 3 will be archived + $publishedDraft = $this->contentService->publishVersion($draftedContentVersion2->getVersionInfo()); + + $this->assertEquals(2, $publishedDraft->contentInfo->currentVersionNo); + } + + /** + * Test for the publishVersion() method, and that it creates limited archives. + * + * @todo Adapt this when per content type archive limited is added on repository content type model. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends testPublishVersionFromContentDraft + */ + public function testPublishVersionNotCreatingUnlimitedArchives() + { + $content = $this->createContentVersion1(); + + // load first to make sure list gets updated also (cache) + $versionInfoList = $this->contentService->loadVersions($content->contentInfo); + $this->assertCount(1, $versionInfoList); + $this->assertEquals(1, $versionInfoList[0]->versionNo); + + // Create a new draft with versionNo = 2 + $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo); + $this->contentService->publishVersion($draftedContentVersion->getVersionInfo()); + + // Create a new draft with versionNo = 3 + $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo); + $this->contentService->publishVersion($draftedContentVersion->getVersionInfo()); + + // Create a new draft with versionNo = 4 + $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo); + $this->contentService->publishVersion($draftedContentVersion->getVersionInfo()); + + // Create a new draft with versionNo = 5 + $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo); + $this->contentService->publishVersion($draftedContentVersion->getVersionInfo()); + + // Create a new draft with versionNo = 6 + $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo); + $this->contentService->publishVersion($draftedContentVersion->getVersionInfo()); + + // Create a new draft with versionNo = 7 + $draftedContentVersion = $this->contentService->createContentDraft($content->contentInfo); + $this->contentService->publishVersion($draftedContentVersion->getVersionInfo()); + + $versionInfoList = $this->contentService->loadVersions($content->contentInfo); + + $this->assertCount(6, $versionInfoList); + $this->assertEquals(2, $versionInfoList[0]->versionNo); + $this->assertEquals(7, $versionInfoList[5]->versionNo); + + $this->assertEquals( + [ + VersionInfo::STATUS_ARCHIVED, + VersionInfo::STATUS_ARCHIVED, + VersionInfo::STATUS_ARCHIVED, + VersionInfo::STATUS_ARCHIVED, + VersionInfo::STATUS_ARCHIVED, + VersionInfo::STATUS_PUBLISHED, + ], + [ + $versionInfoList[0]->status, + $versionInfoList[1]->status, + $versionInfoList[2]->status, + $versionInfoList[3]->status, + $versionInfoList[4]->status, + $versionInfoList[5]->status, + ] + ); + } + + /** + * Test for the newContentMetadataUpdateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::newContentMetadataUpdateStruct + * @group user + */ + public function testNewContentMetadataUpdateStruct() + { + // Creates a new metadata update struct + $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct(); + + foreach ($metadataUpdate as $propertyName => $propertyValue) { + $this->assertNull($propertyValue, "Property '{$propertyName}' initial value should be null'"); + } + + $metadataUpdate->remoteId = 'aaaabbbbccccddddeeeeffff11112222'; + $metadataUpdate->mainLanguageCode = self::ENG_GB; + $metadataUpdate->alwaysAvailable = false; + + $this->assertInstanceOf( + ContentMetadataUpdateStruct::class, + $metadataUpdate + ); + } + + /** + * Test for the updateContentMetadata() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContentMetadata() + * @depends testPublishVersion + * @depends testNewContentMetadataUpdateStruct + * @group user + */ + public function testUpdateContentMetadata() + { + $content = $this->createContentVersion1(); + + // Creates a metadata update struct + $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct(); + + $metadataUpdate->remoteId = 'aaaabbbbccccddddeeeeffff11112222'; + $metadataUpdate->mainLanguageCode = self::ENG_GB; + $metadataUpdate->alwaysAvailable = false; + $metadataUpdate->publishedDate = $this->createDateTime(441759600); // 1984/01/01 + $metadataUpdate->modificationDate = $this->createDateTime(441759600); // 1984/01/01 + + // Update the metadata of the published content object + $content = $this->contentService->updateContentMetadata( + $content->contentInfo, + $metadataUpdate + ); + + $this->assertInstanceOf( + Content::class, + $content + ); + + return $content; + } + + /** + * Test for the updateContentMetadata() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContentMetadata() + * @depends testUpdateContentMetadata + */ + public function testUpdateContentMetadataSetsExpectedProperties($content) + { + $contentInfo = $content->contentInfo; + $currentUserReference = $this->getRepository()->getPermissionResolver()->getCurrentUserReference(); + + $this->assertEquals( + [ + 'remoteId' => 'aaaabbbbccccddddeeeeffff11112222', + 'sectionId' => $this->generateId('section', 1), + 'alwaysAvailable' => false, + 'currentVersionNo' => 1, + 'mainLanguageCode' => self::ENG_GB, + 'modificationDate' => $this->createDateTime(441759600), + 'ownerId' => $currentUserReference->getUserId(), + 'published' => true, + 'publishedDate' => $this->createDateTime(441759600), + ], + [ + 'remoteId' => $contentInfo->remoteId, + 'sectionId' => $contentInfo->getSectionId(), + 'alwaysAvailable' => $contentInfo->alwaysAvailable, + 'currentVersionNo' => $contentInfo->currentVersionNo, + 'mainLanguageCode' => $contentInfo->mainLanguageCode, + 'modificationDate' => $contentInfo->modificationDate, + 'ownerId' => $contentInfo->ownerId, + 'published' => $contentInfo->published, + 'publishedDate' => $contentInfo->publishedDate, + ] + ); + } + + /** + * Test for the updateContentMetadata() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContentMetadata() + * @depends testUpdateContentMetadata + */ + public function testUpdateContentMetadataNotUpdatesContentVersion($content) + { + $this->assertEquals(1, $content->getVersionInfo()->versionNo); + } + + /** + * Test for the updateContentMetadata() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContentMetadata() + * @depends testUpdateContentMetadata + */ + public function testUpdateContentMetadataThrowsInvalidArgumentExceptionOnDuplicateRemoteId() + { + $content = $this->createContentVersion1(); + + // Creates a metadata update struct + $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct(); + $metadataUpdate->remoteId = self::MEDIA_REMOTE_ID; + + $this->expectException(APIInvalidArgumentException::class); + // specified remoteId is already used by the "Media" page. + $this->contentService->updateContentMetadata( + $content->contentInfo, + $metadataUpdate + ); + } + + /** + * Test for the updateContentMetadata() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContentMetadata + */ + public function testUpdateContentMetadataThrowsInvalidArgumentExceptionOnNoMetadataPropertiesSet() + { + $contentInfo = $this->contentService->loadContentInfo(4); + $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct(); + + $this->expectException(APIInvalidArgumentException::class); + $this->contentService->updateContentMetadata($contentInfo, $contentMetadataUpdateStruct); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContentMetadata + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testUpdateContentAlwaysAvailable(): void + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $folder = $this->createFolder(['eng-GB' => 'Folder'], 2); + + $contentMetadataUpdate = $contentService->newContentMetadataUpdateStruct(); + $contentMetadataUpdate->alwaysAvailable = !$folder->contentInfo->alwaysAvailable; + $contentService->updateContentMetadata($folder->contentInfo, $contentMetadataUpdate); + + $reloadedFolder = $contentService->loadContent($folder->id); + self::assertEquals( + $contentMetadataUpdate->alwaysAvailable, + $reloadedFolder->contentInfo->alwaysAvailable + ); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContentMetadata + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testUpdateContentMainTranslation(): void + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + + // create a content type which is not always available by default + $contentType = $this->createSimpleContentType( + 'test_t', + self::ENG_GB, + [ + 'name' => 'ezstring', + ], + false + ); + + $contentCreate = $contentService->newContentCreateStruct( + $contentType, + self::ENG_US + ); + $contentCreate->setField('name', 'My content'); + $content = $contentService->publishVersion( + $contentService->createContent( + $contentCreate, + [$locationService->newLocationCreateStruct(2)] + )->getVersionInfo() + ); + // perform sanity check + self::assertFalse($content->contentInfo->alwaysAvailable); + + $updateStruct = $contentService->newContentMetadataUpdateStruct(); + $updateStruct->mainLanguageCode = self::ENG_GB; + + $contentService->updateContentMetadata($content->contentInfo, $updateStruct); + + $reloadedContent = $contentService->loadContent($content->id); + self::assertEquals(self::ENG_GB, $reloadedContent->contentInfo->mainLanguageCode); + + // check that other properties remained unchanged + self::assertStructPropertiesCorrect( + $content->contentInfo, + $reloadedContent->contentInfo, + [ + 'id', + 'contentTypeId', + 'name', + 'sectionId', + 'currentVersionNo', + 'published', + 'ownerId', + 'alwaysAvailable', + 'remoteId', + 'mainLocationId', + 'status', + ] + ); + } + + /** + * Test for the deleteContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteContent() + * @depends testPublishVersionFromContentDraft + */ + public function testDeleteContent() + { + $contentVersion2 = $this->createContentVersion2(); + + // Load the locations for this content object + $locations = $this->locationService->loadLocations($contentVersion2->contentInfo); + + // This will delete the content, all versions and the associated locations + $this->contentService->deleteContent($contentVersion2->contentInfo); + + $this->expectException(NotFoundException::class); + + foreach ($locations as $location) { + $this->locationService->loadLocation($location->id); + } + } + + /** + * Test for the deleteContent() method. + * + * Test for issue EZP-21057: + * "contentService: Unable to delete a content with an empty file attribute" + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteContent() + * @depends testPublishVersionFromContentDraft + */ + public function testDeleteContentWithEmptyBinaryField() + { + $contentVersion = $this->createContentVersion1EmptyBinaryField(); + + // Load the locations for this content object + $locations = $this->locationService->loadLocations($contentVersion->contentInfo); + + // This will delete the content, all versions and the associated locations + $this->contentService->deleteContent($contentVersion->contentInfo); + + $this->expectException(NotFoundException::class); + + foreach ($locations as $location) { + $this->locationService->loadLocation($location->id); + } + } + + public function testCountContentDraftsReturnsZeroByDefault(): void + { + $this->assertSame(0, $this->contentService->countContentDrafts()); + } + + public function testCountContentDrafts(): void + { + // Create 5 drafts + $this->createContentDrafts(5); + + $this->assertSame(5, $this->contentService->countContentDrafts()); + } + + public function testCountContentDraftsForUsers(): void + { + $newUser = $this->createUserWithPolicies( + 'new_user', + [ + ['module' => 'content', 'function' => 'create'], + ['module' => 'content', 'function' => 'read'], + ['module' => 'content', 'function' => 'publish'], + ['module' => 'content', 'function' => 'edit'], + ] + ); + + $previousUser = $this->permissionResolver->getCurrentUserReference(); + + // Set new editor as user + $this->permissionResolver->setCurrentUserReference($newUser); + + // Create a content draft as newUser + $publishedContent = $this->createContentVersion1(); + $this->contentService->createContentDraft($publishedContent->contentInfo); + + // Reset to previous current user + $this->permissionResolver->setCurrentUserReference($previousUser); + + // Now $contentDrafts for the previous current user and the new user + $newUserDrafts = $this->contentService->countContentDrafts($newUser); + $previousUserDrafts = $this->contentService->countContentDrafts(); + + $this->assertSame(1, $newUserDrafts); + $this->assertSame(0, $previousUserDrafts); + } + + /** + * Test for the loadContentDrafts() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentDrafts() + */ + public function testLoadContentDraftsReturnsEmptyArrayByDefault() + { + $contentDrafts = $this->contentService->loadContentDrafts(); + + $this->assertSame([], $contentDrafts); + } + + /** + * Test for the loadContentDrafts() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentDrafts() + * @depends testCreateContentDraft + */ + public function testLoadContentDrafts() + { + // "Media" content object + $mediaContentInfo = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); + + // "Ibexa Demo Design ..." content object + $demoDesignContentInfo = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID); + + // Create some drafts + $this->contentService->createContentDraft($mediaContentInfo); + $this->contentService->createContentDraft($demoDesignContentInfo); + + // Now $contentDrafts should contain two drafted versions + $draftedVersions = $this->contentService->loadContentDrafts(); + + $actual = [ + $draftedVersions[0]->status, + $draftedVersions[0]->getContentInfo()->remoteId, + $draftedVersions[1]->status, + $draftedVersions[1]->getContentInfo()->remoteId, + ]; + sort($actual, SORT_STRING); + + $this->assertEquals( + [ + VersionInfo::STATUS_DRAFT, + VersionInfo::STATUS_DRAFT, + self::DEMO_DESIGN_REMOTE_ID, + self::MEDIA_REMOTE_ID, + ], + $actual + ); + } + + /** + * Test for the loadContentDrafts() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentDrafts($user) + */ + public function testLoadContentDraftsWithFirstParameter() + { + $user = $this->createUserVersion1(); + + // Get current user + $oldCurrentUser = $this->permissionResolver->getCurrentUserReference(); + + // Set new editor as user + $this->permissionResolver->setCurrentUserReference($user); + + // "Media" content object + $mediaContentInfo = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); + + // Create a content draft + $this->contentService->createContentDraft($mediaContentInfo); + + // Reset to previous current user + $this->permissionResolver->setCurrentUserReference($oldCurrentUser); + + // Now $contentDrafts for the previous current user and the new user + $newCurrentUserDrafts = $this->contentService->loadContentDrafts($user); + $oldCurrentUserDrafts = $this->contentService->loadContentDrafts(); + + $this->assertSame([], $oldCurrentUserDrafts); + + $this->assertEquals( + [ + VersionInfo::STATUS_DRAFT, + self::MEDIA_REMOTE_ID, + ], + [ + $newCurrentUserDrafts[0]->status, + $newCurrentUserDrafts[0]->getContentInfo()->remoteId, + ] + ); + $this->assertTrue($newCurrentUserDrafts[0]->isDraft()); + $this->assertFalse($newCurrentUserDrafts[0]->isArchived()); + $this->assertFalse($newCurrentUserDrafts[0]->isPublished()); + } + + /** + * Test for the loadContentDraftList() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentDrafts() + */ + public function testLoadContentDraftListWithPaginationParameters() + { + // Create some drafts + $publishedContent = $this->createContentVersion1(); + $draftContentA = $this->contentService->createContentDraft($publishedContent->contentInfo); + $draftContentB = $this->contentService->createContentDraft($draftContentA->contentInfo); + $draftContentC = $this->contentService->createContentDraft($draftContentB->contentInfo); + $draftContentD = $this->contentService->createContentDraft($draftContentC->contentInfo); + $draftContentE = $this->contentService->createContentDraft($draftContentD->contentInfo); + + $draftsOnPage1 = $this->contentService->loadContentDraftList(null, 0, 2); + $draftsOnPage2 = $this->contentService->loadContentDraftList(null, 2, 2); + + $this->assertSame(5, $draftsOnPage1->totalCount); + $this->assertSame(5, $draftsOnPage2->totalCount); + $this->assertEquals($draftContentE->getVersionInfo(), $draftsOnPage1->items[0]->getVersionInfo()); + $this->assertEquals($draftContentD->getVersionInfo(), $draftsOnPage1->items[1]->getVersionInfo()); + $this->assertEquals($draftContentC->getVersionInfo(), $draftsOnPage2->items[0]->getVersionInfo()); + $this->assertEquals($draftContentB->getVersionInfo(), $draftsOnPage2->items[1]->getVersionInfo()); + } + + /** + * Test for the loadContentDraftList() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentDrafts($user) + */ + public function testLoadContentDraftListWithForUserWithLimitation() + { + $oldUser = $this->permissionResolver->getCurrentUserReference(); + + $parentContent = $this->createFolder(['eng-US' => 'parentFolder'], 2); + $content = $this->createFolder(['eng-US' => 'parentFolder'], $parentContent->contentInfo->mainLocationId); + + // User has limitation to read versions only for `$content`, not for `$parentContent` + $newUser = $this->createUserWithVersionReadLimitations([$content->contentInfo->mainLocationId]); + + $this->permissionResolver->setCurrentUserReference($newUser); + + $contentDraftUnauthorized = $this->contentService->createContentDraft($parentContent->contentInfo); + $contentDraftA = $this->contentService->createContentDraft($content->contentInfo); + $contentDraftB = $this->contentService->createContentDraft($content->contentInfo); + + $newUserDraftList = $this->contentService->loadContentDraftList($newUser, 0); + $this->assertSame(3, $newUserDraftList->totalCount); + $this->assertEquals($contentDraftB->getVersionInfo(), $newUserDraftList->items[0]->getVersionInfo()); + $this->assertEquals($contentDraftA->getVersionInfo(), $newUserDraftList->items[1]->getVersionInfo()); + $this->assertEquals( + new UnauthorizedContentDraftListItem('content', 'versionread', ['contentId' => $contentDraftUnauthorized->id]), + $newUserDraftList->items[2] + ); + + // Reset to previous user + $this->permissionResolver->setCurrentUserReference($oldUser); + + $oldUserDraftList = $this->contentService->loadContentDraftList(); + + $this->assertSame(0, $oldUserDraftList->totalCount); + $this->assertSame([], $oldUserDraftList->items); + } + + /** + * Test for the loadContentDraftList() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentDrafts() + */ + public function testLoadAllContentDrafts() + { + // Create more drafts then default pagination limit + $this->createContentDrafts(12); + + $this->assertCount(12, $this->contentService->loadContentDraftList()); + } + + /** + * Test for the loadVersionInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfo($contentInfo, $versionNo) + * @depends testPublishVersionFromContentDraft + */ + public function testLoadVersionInfoWithSecondParameter() + { + $publishedContent = $this->createContentVersion1(); + + $this->contentService->createContentDraft($publishedContent->contentInfo); + + // Will return the VersionInfo of the $draftContent + $versionInfo = $this->contentService->loadVersionInfoById($publishedContent->id, 2); + + $this->assertEquals(2, $versionInfo->versionNo); + + // Check that ContentInfo contained in VersionInfo has correct main Location id set + $this->assertEquals( + $publishedContent->getVersionInfo()->getContentInfo()->mainLocationId, + $versionInfo->getContentInfo()->mainLocationId + ); + } + + /** + * Test for the loadVersionInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfo($contentInfo, $versionNo) + * @depends testLoadVersionInfoWithSecondParameter + */ + public function testLoadVersionInfoThrowsNotFoundExceptionWithSecondParameter() + { + $draft = $this->createContentDraftVersion1(); + + $this->expectException(NotFoundException::class); + + // This call will fail with a "NotFoundException", because not versionNo 2 exists for this content object. + $this->contentService->loadVersionInfo($draft->contentInfo, 2); + } + + /** + * Test for the loadVersionInfoById() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById($contentId, $versionNo) + * @depends testLoadVersionInfoWithSecondParameter + */ + public function testLoadVersionInfoByIdWithSecondParameter() + { + $publishedContent = $this->createContentVersion1(); + + $draftContent = $this->contentService->createContentDraft($publishedContent->contentInfo); + + // Will return the VersionInfo of the $draftContent + $versionInfo = $this->contentService->loadVersionInfoById($publishedContent->id, 2); + + $this->assertEquals(2, $versionInfo->versionNo); + + // Check that ContentInfo contained in VersionInfo has correct main Location id set + $this->assertEquals( + $publishedContent->getVersionInfo()->getContentInfo()->mainLocationId, + $versionInfo->getContentInfo()->mainLocationId + ); + + return [ + 'versionInfo' => $versionInfo, + 'draftContent' => $draftContent, + ]; + } + + /** + * Test for the returned value of the loadVersionInfoById() method. + * + * @depends testLoadVersionInfoByIdWithSecondParameter + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById + * + * @param array $data + */ + public function testLoadVersionInfoByIdWithSecondParameterSetsExpectedVersionInfo(array $data) + { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo */ + $versionInfo = $data['versionInfo']; + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $draftContent */ + $draftContent = $data['draftContent']; + + $this->assertPropertiesCorrect( + [ + 'names' => [ + self::ENG_US => 'An awesome forum', + ], + 'contentInfo' => new ContentInfo([ + 'id' => $draftContent->contentInfo->id, + 'contentTypeId' => 28, + 'name' => 'An awesome forum', + 'sectionId' => 1, + 'currentVersionNo' => 1, + 'published' => true, + 'ownerId' => 14, + // this Content Object is created at the test runtime + 'modificationDate' => $versionInfo->contentInfo->modificationDate, + 'publishedDate' => $versionInfo->contentInfo->publishedDate, + 'alwaysAvailable' => 1, + 'remoteId' => 'abcdef0123456789abcdef0123456789', + 'mainLanguageCode' => self::ENG_US, + 'mainLocationId' => $draftContent->contentInfo->mainLocationId, + 'status' => ContentInfo::STATUS_PUBLISHED, + ]), + 'id' => $draftContent->versionInfo->id, + 'versionNo' => 2, + 'creatorId' => 14, + 'status' => 0, + 'initialLanguageCode' => self::ENG_US, + 'languageCodes' => [ + self::ENG_US, + ], + ], + $versionInfo + ); + } + + /** + * Test for the loadVersionInfoById() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById($contentId, $versionNo) + */ + public function testLoadVersionInfoByIdThrowsNotFoundExceptionWithSecondParameter() + { + $content = $this->createContentVersion1(); + + $this->expectException(NotFoundException::class); + + // This call will fail with a "NotFoundException", because not versionNo 2 exists for this content object. + $this->contentService->loadVersionInfoById($content->id, 2); + } + + /** + * Test for the loadContentByVersionInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByVersionInfo($versionInfo, $languages) + * @depends testCreateContent + * @depends testLoadContentByVersionInfo + */ + public function testLoadContentByVersionInfoWithSecondParameter() + { + $sectionId = $this->generateId('section', 1); + $contentTypeService = $this->getRepository()->getContentTypeService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); + + $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + + $contentCreateStruct->setField('name', 'Sindelfingen forum²'); + + $contentCreateStruct->setField('name', 'Sindelfingen forum²³', self::ENG_GB); + + $contentCreateStruct->remoteId = 'abcdef0123456789abcdef0123456789'; + // $sectionId contains the ID of section 1 + $contentCreateStruct->sectionId = $sectionId; + $contentCreateStruct->alwaysAvailable = true; + + // Create a new content draft + $content = $this->contentService->createContent($contentCreateStruct); + + // Now publish this draft + $publishedContent = $this->contentService->publishVersion($content->getVersionInfo()); + + // Will return a content instance with fields in "eng-US" + $reloadedContent = $this->contentService->loadContentByVersionInfo( + $publishedContent->getVersionInfo(), + [ + self::ENG_GB, + ], + false + ); + + $actual = []; + foreach ($reloadedContent->getFields() as $field) { + $actual[] = new Field( + [ + 'id' => 0, + 'value' => $field->value !== null, // Actual value tested by FieldType integration tests + 'languageCode' => $field->languageCode, + 'fieldDefIdentifier' => $field->fieldDefIdentifier, + ] + ); + } + usort( + $actual, + static function ($field1, $field2) { + if (0 === ($return = strcasecmp($field1->fieldDefIdentifier, $field2->fieldDefIdentifier))) { + return strcasecmp($field1->languageCode, $field2->languageCode); + } + + return $return; + } + ); + + $expected = [ + new Field( + [ + 'id' => 0, + 'value' => true, + 'languageCode' => self::ENG_GB, + 'fieldDefIdentifier' => 'name', + ] + ), + ]; + + $this->assertEquals($expected, $actual); + } + + /** + * Test for the loadContentByContentInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages) + * @depends testLoadContentByContentInfo + */ + public function testLoadContentByContentInfoWithLanguageParameters() + { + $sectionId = $this->generateId('section', 1); + $contentTypeService = $this->getRepository()->getContentTypeService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); + + $contentCreateStruct = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + + $contentCreateStruct->setField('name', 'Sindelfingen forum²'); + + $contentCreateStruct->setField('name', 'Sindelfingen forum²³', self::ENG_GB); + + $contentCreateStruct->remoteId = 'abcdef0123456789abcdef0123456789'; + // $sectionId contains the ID of section 1 + $contentCreateStruct->sectionId = $sectionId; + $contentCreateStruct->alwaysAvailable = true; + + // Create a new content draft + $content = $this->contentService->createContent($contentCreateStruct); + + // Now publish this draft + $publishedContent = $this->contentService->publishVersion($content->getVersionInfo()); + + // Will return a content instance with fields in "eng-US" + $reloadedContent = $this->contentService->loadContentByContentInfo( + $publishedContent->contentInfo, + [ + self::ENG_US, + ], + null, + false + ); + + $actual = $this->normalizeFields($reloadedContent->getFields()); + + $expected = [ + new Field( + [ + 'id' => 0, + 'value' => true, + 'languageCode' => self::ENG_US, + 'fieldDefIdentifier' => 'name', + 'fieldTypeIdentifier' => 'ezstring', + ] + ), + ]; + + $this->assertEquals($expected, $actual); + + // Will return a content instance with fields in "eng-GB" (versions prior to 6.0.0-beta9 returned "eng-US" also) + $reloadedContent = $this->contentService->loadContentByContentInfo( + $publishedContent->contentInfo, + [ + self::ENG_GB, + ], + null, + true + ); + + $actual = $this->normalizeFields($reloadedContent->getFields()); + + $expected = [ + new Field( + [ + 'id' => 0, + 'value' => true, + 'languageCode' => self::ENG_GB, + 'fieldDefIdentifier' => 'name', + 'fieldTypeIdentifier' => 'ezstring', + ] + ), + ]; + + $this->assertEquals($expected, $actual); + + // Will return a content instance with fields in main language "eng-US", as "fre-FR" does not exists + $reloadedContent = $this->contentService->loadContentByContentInfo( + $publishedContent->contentInfo, + [ + 'fre-FR', + ], + null, + true + ); + + $actual = $this->normalizeFields($reloadedContent->getFields()); + + $expected = [ + new Field( + [ + 'id' => 0, + 'value' => true, + 'languageCode' => self::ENG_US, + 'fieldDefIdentifier' => 'name', + 'fieldTypeIdentifier' => 'ezstring', + ] + ), + ]; + + $this->assertEquals($expected, $actual); + } + + /** + * Test for the loadContentByContentInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages, $versionNo) + * @depends testLoadContentByContentInfo + */ + public function testLoadContentByContentInfoWithVersionNumberParameter() + { + $publishedContent = $this->createContentVersion1(); + + $this->contentService->createContentDraft($publishedContent->contentInfo); + + // This content instance is identical to $draftContent + $draftContentReloaded = $this->contentService->loadContentByContentInfo( + $publishedContent->contentInfo, + null, + 2 + ); + + $this->assertEquals( + 2, + $draftContentReloaded->getVersionInfo()->versionNo + ); + + // Check that ContentInfo contained in reloaded draft Content has correct main Location id set + $this->assertEquals( + $publishedContent->versionInfo->contentInfo->mainLocationId, + $draftContentReloaded->versionInfo->contentInfo->mainLocationId + ); + } + + /** + * Test for the loadContentByContentInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByContentInfo($contentInfo, $languages, $versionNo) + * @depends testLoadContentByContentInfoWithVersionNumberParameter + */ + public function testLoadContentByContentInfoThrowsNotFoundExceptionWithVersionNumberParameter() + { + $content = $this->createContentVersion1(); + + $this->expectException(NotFoundException::class); + + // This call will fail with a "NotFoundException", because no content with versionNo = 2 exists. + $this->contentService->loadContentByContentInfo($content->contentInfo, null, 2); + } + + /** + * Test for the loadContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContent($contentId, $languages) + * @depends testPublishVersionFromContentDraft + */ + public function testLoadContentWithPrioritizedLanguages() + { + $draft = $this->createMultipleLanguageDraftVersion1(); + + // This draft contains those fields localized with "eng-GB" + $draftLocalized = $this->contentService->loadContent($draft->id, [self::ENG_GB], null, false); + + $this->assertLocaleFieldsEquals($draftLocalized->getFields(), self::ENG_GB); + + return $draftLocalized; + } + + /** + * Test for the loadContent() method using undefined translation. + * + * @depends testLoadContentWithPrioritizedLanguages + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $contentDraft + */ + public function testLoadContentWithPrioritizedLanguagesThrowsNotFoundException(Content $contentDraft) + { + $this->expectException(NotFoundException::class); + + $this->contentService->loadContent($contentDraft->id, [self::GER_DE], null, false); + } + + /** + * Test for the loadContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContent + * @depends testLoadContentWithPrioritizedLanguages + */ + public function testLoadContentPassTroughPrioritizedLanguagesToContentType(Content $content): void + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + + $contentType = $contentTypeService->loadContentType( + $content->contentInfo->contentTypeId, + [self::ENG_GB] + ); + + $this->assertEquals($contentType, $content->getContentType()); + } + + /** + * Test for the loadContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContent($contentId, $languages, $versionNo) + * @depends testPublishVersionFromContentDraft + */ + public function testLoadContentWithThirdParameter() + { + $publishedContent = $this->createContentVersion1(); + + $this->contentService->createContentDraft($publishedContent->contentInfo); + + // This content instance is identical to $draftContent + $draftContentReloaded = $this->contentService->loadContent($publishedContent->id, null, 2); + + $this->assertEquals(2, $draftContentReloaded->getVersionInfo()->versionNo); + + // Check that ContentInfo contained in reloaded draft Content has correct main Location id set + $this->assertEquals( + $publishedContent->versionInfo->contentInfo->mainLocationId, + $draftContentReloaded->versionInfo->contentInfo->mainLocationId + ); + } + + /** + * Test for the loadContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContent($contentId, $languages, $versionNo) + * @depends testLoadContentWithThirdParameter + */ + public function testLoadContentThrowsNotFoundExceptionWithThirdParameter() + { + $content = $this->createContentVersion1(); + + $this->expectException(NotFoundException::class); + + // This call will fail with a "NotFoundException", because for this content object no versionNo=2 exists. + $this->contentService->loadContent($content->id, null, 2); + } + + /** + * Test for the loadContentByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByRemoteId($remoteId, $languages) + * @depends testPublishVersionFromContentDraft + */ + public function testLoadContentByRemoteIdWithSecondParameter() + { + $draft = $this->createMultipleLanguageDraftVersion1(); + + $this->contentService->publishVersion($draft->versionInfo); + + // This draft contains those fields localized with "eng-GB" + $draftLocalized = $this->contentService->loadContentByRemoteId( + $draft->contentInfo->remoteId, + [self::ENG_GB], + null, + false + ); + + $this->assertLocaleFieldsEquals($draftLocalized->getFields(), self::ENG_GB); + } + + /** + * Test for the loadContentByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByRemoteId($remoteId, $languages, $versionNo) + * @depends testPublishVersionFromContentDraft + */ + public function testLoadContentByRemoteIdWithThirdParameter() + { + $publishedContent = $this->createContentVersion1(); + + $this->contentService->createContentDraft($publishedContent->contentInfo); + + // This content instance is identical to $draftContent + $draftContentReloaded = $this->contentService->loadContentByRemoteId( + $publishedContent->contentInfo->remoteId, + null, + 2 + ); + + $this->assertEquals(2, $draftContentReloaded->getVersionInfo()->versionNo); + + // Check that ContentInfo contained in reloaded draft Content has correct main Location id set + $this->assertEquals( + $publishedContent->versionInfo->contentInfo->mainLocationId, + $draftContentReloaded->versionInfo->contentInfo->mainLocationId + ); + } + + /** + * Test for the loadContentByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByRemoteId($remoteId, $languages, $versionNo) + * @depends testLoadContentByRemoteIdWithThirdParameter + */ + public function testLoadContentByRemoteIdThrowsNotFoundExceptionWithThirdParameter() + { + $content = $this->createContentVersion1(); + + $this->expectException(NotFoundException::class); + + // This call will fail with a "NotFoundException", because for this content object no versionNo=2 exists. + $this->contentService->loadContentByRemoteId( + $content->contentInfo->remoteId, + null, + 2 + ); + } + + /** + * Test that retrieval of translated name field respects prioritized language list. + * + * @dataProvider getPrioritizedLanguageList + * + * @param string[]|null $languageCodes + */ + public function testLoadContentWithPrioritizedLanguagesList($languageCodes) + { + $content = $this->createContentVersion2(); + + $content = $this->contentService->loadContent($content->id, $languageCodes); + + $expectedName = $content->getVersionInfo()->getName( + isset($languageCodes[0]) ? $languageCodes[0] : null + ); + $nameValue = $content->getFieldValue('name'); + /** @var \Ibexa\Core\FieldType\TextLine\Value $nameValue */ + self::assertEquals($expectedName, $nameValue->text); + self::assertEquals($expectedName, $content->getVersionInfo()->getName()); + // Also check value on shortcut method on content + self::assertEquals($expectedName, $content->getName()); + } + + /** + * @return array + */ + public function getPrioritizedLanguageList() + { + return [ + [[self::ENG_US]], + [[self::ENG_GB]], + [[self::ENG_GB, self::ENG_US]], + [[self::ENG_US, self::ENG_GB]], + ]; + } + + /** + * Test for the deleteVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteVersion() + * @depends testLoadContent + * @depends testCreateContent + * @depends testPublishVersion + * @depends testCreateContentDraft + */ + public function testDeleteVersion() + { + $content = $this->createContentVersion1(); + + // Create new draft, because published or last version of the Content can't be deleted + $draft = $this->contentService->createContentDraft( + $content->getVersionInfo()->getContentInfo() + ); + + // Delete the previously created draft + $this->contentService->deleteVersion($draft->getVersionInfo()); + + $versions = $this->contentService->loadVersions($content->getVersionInfo()->getContentInfo()); + + $this->assertCount(1, $versions); + $this->assertEquals( + $content->getVersionInfo()->id, + $versions[0]->id + ); + } + + /** + * Test for the deleteVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteVersion() + * @depends testLoadContent + * @depends testCreateContent + * @depends testPublishVersion + */ + public function testDeleteVersionThrowsBadStateExceptionOnPublishedVersion() + { + $content = $this->createContentVersion1(); + + $this->expectException(BadStateException::class); + + // This call will fail with a "BadStateException", because the content version is currently published. + $this->contentService->deleteVersion($content->getVersionInfo()); + } + + /** + * Test for the deleteVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteVersion() + * @depends testLoadContent + * @depends testCreateContent + * @depends testPublishVersion + */ + public function testDeleteVersionWorksIfOnlyVersionIsDraft() + { + $draft = $this->createContentDraftVersion1(); + + $this->contentService->deleteVersion($draft->getVersionInfo()); + + $this->expectException(NotFoundException::class); + + // This call will fail with a "NotFound", because we allow to delete content if remaining version is draft. + // Can normally only happen if there where always only a draft to begin with, simplifies UI edit API usage. + $this->contentService->loadContentInfo($draft->id); + } + + /** + * Test for the loadVersions() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersions() + * @depends testPublishVersion + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo[] + */ + public function testLoadVersions() + { + $contentVersion2 = $this->createContentVersion2(); + + // Load versions of this ContentInfo instance + $versions = $this->contentService->loadVersions($contentVersion2->contentInfo); + + $expectedVersionsOrder = [ + $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 1), + $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 2), + ]; + + $this->assertEquals($expectedVersionsOrder, $versions); + + return $versions; + } + + /** + * Test for the loadVersions() method. + * + * @depends testLoadVersions + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersions + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo[] $versions + */ + public function testLoadVersionsSetsExpectedVersionInfo(array $versions) + { + $this->assertCount(2, $versions); + + $expectedVersions = [ + [ + 'versionNo' => 1, + 'creatorId' => 14, + 'status' => VersionInfo::STATUS_ARCHIVED, + 'initialLanguageCode' => self::ENG_US, + 'languageCodes' => [self::ENG_US], + ], + [ + 'versionNo' => 2, + 'creatorId' => 10, + 'status' => VersionInfo::STATUS_PUBLISHED, + 'initialLanguageCode' => self::ENG_US, + 'languageCodes' => [self::ENG_US, self::ENG_GB], + ], + ]; + + $this->assertPropertiesCorrect($expectedVersions[0], $versions[0]); + $this->assertPropertiesCorrect($expectedVersions[1], $versions[1]); + $this->assertEqualsWithDelta( + $versions[0]->creationDate->getTimestamp(), + $versions[1]->creationDate->getTimestamp(), + 2, + 'Creation time did not match within delta of 2 seconds', + ); + $this->assertEqualsWithDelta( + $versions[0]->modificationDate->getTimestamp(), + $versions[1]->modificationDate->getTimestamp(), + 2, + 'Creation time did not match within delta of 2 seconds', + ); + $this->assertTrue($versions[0]->isArchived()); + $this->assertFalse($versions[0]->isDraft()); + $this->assertFalse($versions[0]->isPublished()); + + $this->assertTrue($versions[1]->isPublished()); + $this->assertFalse($versions[1]->isDraft()); + $this->assertFalse($versions[1]->isArchived()); + } + + /** + * Test for the copyContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::copyContent() + * @depends testPublishVersionFromContentDraft + * @group field-type + */ + public function testCopyContent() + { + $parentLocationId = $this->generateId('location', 56); + + $contentVersion2 = $this->createMultipleLanguageContentVersion2(); + + // Configure new target location + $targetLocationCreate = $this->locationService->newLocationCreateStruct($parentLocationId); + + $targetLocationCreate->priority = 42; + $targetLocationCreate->hidden = true; + $targetLocationCreate->remoteId = '01234abcdef5678901234abcdef56789'; + $targetLocationCreate->sortField = Location::SORT_FIELD_NODE_ID; + $targetLocationCreate->sortOrder = Location::SORT_ORDER_DESC; + + // Copy content with all versions and drafts + $contentCopied = $this->contentService->copyContent( + $contentVersion2->contentInfo, + $targetLocationCreate + ); + + $this->assertInstanceOf( + Content::class, + $contentCopied + ); + + $this->assertNotEquals( + $contentVersion2->contentInfo->remoteId, + $contentCopied->contentInfo->remoteId + ); + + $this->assertNotEquals( + $contentVersion2->id, + $contentCopied->id + ); + + $this->assertCount( + 2, + $this->contentService->loadVersions($contentCopied->contentInfo) + ); + + $this->assertEquals(2, $contentCopied->getVersionInfo()->versionNo); + + $this->assertAllFieldsEquals($contentCopied->getFields()); + + $this->assertDefaultContentStates($contentCopied->contentInfo); + + $this->assertNotNull( + $contentCopied->contentInfo->mainLocationId, + 'Expected main location to be set given we provided a LocationCreateStruct' + ); + } + + /** + * Test for the copyContent() method with ibexa.site_access.config.default.content.retain_owner_on_copy set to false + * See settings/test/integration_legacy.yml for service override. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::copyContent() + * @depends testPublishVersionFromContentDraft + * @group field-type + */ + public function testCopyContentWithNewOwner() + { + $parentLocationId = $this->generateId('location', 56); + + $userService = $this->getRepository()->getUserService(); + + $owner = $this->createUser('new_owner', 'foo', 'bar'); + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $contentVersion2 */ + $contentVersion2 = $this->createContentDraftVersion1( + $parentLocationId, + self::FORUM_IDENTIFIER, + 'name', + $owner + ); + + // Configure new target location + $targetLocationCreate = $this->locationService->newLocationCreateStruct($parentLocationId); + + $targetLocationCreate->priority = 42; + $targetLocationCreate->hidden = true; + $targetLocationCreate->remoteId = '01234abcdef5678901234abcdef56789'; + $targetLocationCreate->sortField = Location::SORT_FIELD_NODE_ID; + $targetLocationCreate->sortOrder = Location::SORT_ORDER_DESC; + + $this->contentService->publishVersion($contentVersion2->versionInfo); + $this->contentService->createContentDraft($contentVersion2->contentInfo); + + // Copy content with all versions and drafts + $contentCopied = $this->contentService->copyContent( + $contentVersion2->contentInfo, + $targetLocationCreate + ); + + $this->assertEquals( + $owner->id, + $contentVersion2->contentInfo->ownerId + ); + $newOwnerId = $userService->loadUserByLogin('admin')->getUserId(); + $this->assertEquals( + $newOwnerId, + $contentCopied->contentInfo->ownerId + ); + $versions = $this->contentService->loadVersions($contentCopied->contentInfo); + $this->assertCount(2, $versions); + + foreach ($versions as $version) { + $this->assertEquals( + $newOwnerId, + $version->creatorId + ); + } + } + + /** + * Test for the copyContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::copyContent($contentInfo, $destinationLocationCreateStruct, $versionInfo) + * @depends testCopyContent + */ + public function testCopyContentWithGivenVersion() + { + $parentLocationId = $this->generateId('location', 56); + + $contentVersion2 = $this->createContentVersion2(); + + // Configure new target location + $targetLocationCreate = $this->locationService->newLocationCreateStruct($parentLocationId); + + $targetLocationCreate->priority = 42; + $targetLocationCreate->hidden = true; + $targetLocationCreate->remoteId = '01234abcdef5678901234abcdef56789'; + $targetLocationCreate->sortField = Location::SORT_FIELD_NODE_ID; + $targetLocationCreate->sortOrder = Location::SORT_ORDER_DESC; + + // Copy only the initial version + $contentCopied = $this->contentService->copyContent( + $contentVersion2->contentInfo, + $targetLocationCreate, + $this->contentService->loadVersionInfo($contentVersion2->contentInfo, 1) + ); + + $this->assertInstanceOf( + Content::class, + $contentCopied + ); + + $this->assertNotEquals( + $contentVersion2->contentInfo->remoteId, + $contentCopied->contentInfo->remoteId + ); + + $this->assertNotEquals( + $contentVersion2->id, + $contentCopied->id + ); + + $this->assertCount( + 1, + $this->contentService->loadVersions($contentCopied->contentInfo) + ); + + $this->assertEquals(1, $contentCopied->getVersionInfo()->versionNo); + + $this->assertNotNull( + $contentCopied->contentInfo->mainLocationId, + 'Expected main location to be set given we provided a LocationCreateStruct' + ); + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::addRelation + * + * @depends testPublishVersionFromContentDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + public function testAddRelation(): array + { + $draft = $this->createContentDraftVersion1(); + + $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); + + // Create relation between new content object and "Media" page + $relation = $this->contentService->addRelation( + $draft->getVersionInfo(), + $media + ); + + self::assertInstanceOf(Relation::class, $relation); + + return $this->contentService->loadRelations($draft->getVersionInfo()); + } + + /** + * Test for the addRelation() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] $relations + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::addRelation() + * @depends testAddRelation + */ + public function testAddRelationAddsRelationToContent($relations) + { + $this->assertCount( + 1, + $relations + ); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] $relations + */ + protected function assertExpectedRelations($relations) + { + $this->assertEquals( + [ + 'type' => Relation::COMMON, + 'sourceFieldDefinitionIdentifier' => null, + 'sourceContentInfo' => 'abcdef0123456789abcdef0123456789', + 'destinationContentInfo' => self::MEDIA_REMOTE_ID, + ], + [ + 'type' => $relations[0]->type, + 'sourceFieldDefinitionIdentifier' => $relations[0]->sourceFieldDefinitionIdentifier, + 'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId, + 'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId, + ] + ); + } + + /** + * Test for the addRelation() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] $relations + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::addRelation() + * @depends testAddRelation + */ + public function testAddRelationSetsExpectedRelations($relations) + { + $this->assertExpectedRelations($relations); + } + + /** + * Test for the createContentDraft() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft + * @depends testAddRelationSetsExpectedRelations + */ + public function testCreateContentDraftWithRelations() + { + $draft = $this->createContentDraftVersion1(); + $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); + + // Create relation between new content object and "Media" page + $this->contentService->addRelation( + $draft->getVersionInfo(), + $media + ); + + $content = $this->contentService->publishVersion($draft->versionInfo); + $newDraft = $this->contentService->createContentDraft($content->contentInfo); + + return $this->contentService->loadRelations($newDraft->getVersionInfo()); + } + + /** + * Test for the createContentDraft() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] $relations + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] + * @depends testCreateContentDraftWithRelations + */ + public function testCreateContentDraftWithRelationsCreatesRelations(array $relations): array + { + $this->assertCount( + 1, + $relations + ); + + return $relations; + } + + /** + * Test for the createContentDraft() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] $relations + * + * @depends testCreateContentDraftWithRelationsCreatesRelations + */ + public function testCreateContentDraftWithRelationsCreatesExpectedRelations($relations) + { + $this->assertExpectedRelations($relations); + } + + /** + * Test for the addRelation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::addRelation() + * @depends testAddRelation + */ + public function testAddRelationThrowsBadStateException() + { + $content = $this->createContentVersion1(); + + $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); + + $this->expectException(BadStateException::class); + + // This call will fail with a "BadStateException", because content is published and not a draft. + $this->contentService->addRelation( + $content->getVersionInfo(), + $media + ); + } + + /** + * Test for the loadRelations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadRelations() + * @depends testAddRelation + */ + public function testLoadRelations() + { + $draft = $this->createContentWithRelations(); + + $relations = $this->contentService->loadRelations($draft->getVersionInfo()); + + usort( + $relations, + static function ($rel1, $rel2) { + return strcasecmp( + $rel2->getDestinationContentInfo()->remoteId, + $rel1->getDestinationContentInfo()->remoteId + ); + } + ); + + $this->assertEquals( + [ + [ + 'sourceContentInfo' => 'abcdef0123456789abcdef0123456789', + 'destinationContentInfo' => self::MEDIA_REMOTE_ID, + ], + [ + 'sourceContentInfo' => 'abcdef0123456789abcdef0123456789', + 'destinationContentInfo' => self::DEMO_DESIGN_REMOTE_ID, + ], + ], + [ + [ + 'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId, + 'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId, + ], + [ + 'sourceContentInfo' => $relations[1]->sourceContentInfo->remoteId, + 'destinationContentInfo' => $relations[1]->destinationContentInfo->remoteId, + ], + ] + ); + } + + /** + * Test for the loadRelations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadRelations() + * @depends testAddRelation + * @depends testLoadRelations + */ + public function testLoadRelationsSkipsArchivedContent() + { + $trashService = $this->getRepository()->getTrashService(); + + $draft = $this->createContentDraftVersion1(); + + // Load other content objects + $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); + $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID); + + // Create relation between new content object and "Media" page + $this->contentService->addRelation( + $draft->getVersionInfo(), + $media + ); + + // Create another relation with the "Demo Design" page + $this->contentService->addRelation( + $draft->getVersionInfo(), + $demoDesign + ); + + $demoDesignLocation = $this->locationService->loadLocation($demoDesign->mainLocationId); + + // Trashing Content's last Location will change its status to archived, + // in this case relation towards it will not be loaded. + $trashService->trash($demoDesignLocation); + + // Load all relations + $relations = $this->contentService->loadRelations($draft->getVersionInfo()); + + $this->assertCount(1, $relations); + $this->assertEquals( + [ + [ + 'sourceContentInfo' => 'abcdef0123456789abcdef0123456789', + 'destinationContentInfo' => self::MEDIA_REMOTE_ID, + ], + ], + [ + [ + 'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId, + 'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId, + ], + ] + ); + } + + /** + * Test for the loadRelations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadRelations() + * @depends testAddRelation + * @depends testLoadRelations + */ + public function testLoadRelationsSkipsDraftContent() + { + $draft = $this->createContentDraftVersion1(); + + // Load other content objects + $media = $this->contentService->loadContentByRemoteId(self::MEDIA_REMOTE_ID); + $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID); + + // Create draft of "Media" page + $mediaDraft = $this->contentService->createContentDraft($media->contentInfo); + + // Create relation between "Media" page and new content object draft. + // This relation will not be loaded before the draft is published. + $this->contentService->addRelation( + $mediaDraft->getVersionInfo(), + $draft->getVersionInfo()->getContentInfo() + ); + + // Create another relation with the "Demo Design" page + $this->contentService->addRelation( + $mediaDraft->getVersionInfo(), + $demoDesign + ); + + $relations = $this->contentService->loadRelations($mediaDraft->getVersionInfo()); + + $this->assertCount(1, $relations); + $this->assertEquals( + [ + [ + 'sourceContentInfo' => self::MEDIA_REMOTE_ID, + 'destinationContentInfo' => self::DEMO_DESIGN_REMOTE_ID, + ], + ], + [ + [ + 'sourceContentInfo' => $relations[0]->sourceContentInfo->remoteId, + 'destinationContentInfo' => $relations[0]->destinationContentInfo->remoteId, + ], + ] + ); + } + + public function testCountRelations(): void + { + $draft = $this->createContentWithRelations(); + + self::assertEquals(2, $this->contentService->countRelations($draft->getVersionInfo())); + } + + public function testCountRelationsReturnsZeroByDefault(): void + { + $draft = $this->createContentDraftVersion1(); + + self::assertSame(0, $this->contentService->countRelations($draft->getVersionInfo())); + } + + public function testCountRelationsForUnauthorizedUser(): void + { + $draft = $this->createContentWithRelations(); + $mediaUser = $this->createMediaUserVersion1(); + $this->permissionResolver->setCurrentUserReference($mediaUser); + + self::assertSame(0, $this->contentService->countRelations($draft->getVersionInfo())); + } + + public function testLoadRelationList(): void + { + $draft = $this->createContentWithRelations(); + $relationList = $this->contentService->loadRelationList($draft->getVersionInfo()); + $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); + $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID); + + self::assertSame(2, $relationList->totalCount); + + $relation1 = $relationList->items[0]->getRelation(); + $relation2 = $relationList->items[1]->getRelation(); + + self::assertNotNull($relation1); + self::assertNotNull($relation2); + + self::assertEquals( + $demoDesign, + $relation1->getDestinationContentInfo() + ); + + self::assertEquals( + $media, + $relation2->getDestinationContentInfo() + ); + } + + public function testLoadRelationListWithPagination(): void + { + $draft = $this->createContentWithRelations(); + $versionInfo = $draft->getVersionInfo(); + $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); + $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID); + + $relationPage1 = $this->contentService->loadRelationList($versionInfo, 0, 1); + $relationPage2 = $this->contentService->loadRelationList($versionInfo, 1, 2); + + self::assertSame(2, $relationPage1->totalCount); + self::assertSame(2, $relationPage2->totalCount); + + $relation1 = $relationPage1->items[0]->getRelation(); + $relation2 = $relationPage2->items[0]->getRelation(); + + self::assertNotNull($relation1); + self::assertNotNull($relation2); + + self::assertEquals( + $demoDesign, + $relation1->getDestinationContentInfo() + ); + self::assertEquals( + $media, + $relation2->getDestinationContentInfo() + ); + } + + /** + * Test for the countReverseRelations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::countReverseRelations + */ + public function testCountReverseRelations(): void + { + $contentWithReverseRelations = $this->createContentWithReverseRelations([ + $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo + ), + $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo + ), + ]); + + $contentInfo = $contentWithReverseRelations->content->getVersionInfo()->getContentInfo(); + + $this->assertEquals(2, $this->contentService->countReverseRelations($contentInfo)); + } + + /** + * Test for the countReverseRelations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::countReverseRelations + */ + public function testCountReverseRelationsReturnsZeroByDefault(): void + { + $draft = $this->createContentDraftVersion1(); + + $this->assertSame(0, $this->contentService->countReverseRelations($draft->getVersionInfo()->getContentInfo())); + } + + /** + * Test for the countReverseRelations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::countReverseRelations + */ + public function testCountReverseRelationsForUnauthorizedUser(): void + { + $contentWithReverseRelations = $this->createContentWithReverseRelations([ + $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo + ), + ]); + $mediaUser = $this->createMediaUserVersion1(); + $this->permissionResolver->setCurrentUserReference($mediaUser); + + $contentInfo = $contentWithReverseRelations->content->contentInfo; + + $this->assertSame(0, $this->contentService->countReverseRelations($contentInfo)); + } + + /** + * Test for the loadReverseRelations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadReverseRelations() + * @depends testAddRelation + */ + public function testLoadReverseRelations() + { + $versionInfo = $this->createContentVersion1()->getVersionInfo(); + $contentInfo = $versionInfo->getContentInfo(); + + // Create some drafts + $mediaDraft = $this->contentService->createContentDraft( + $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID) + ); + $demoDesignDraft = $this->contentService->createContentDraft( + $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID) + ); + + // Create relation between new content object and "Media" page + $relation1 = $this->contentService->addRelation( + $mediaDraft->getVersionInfo(), + $contentInfo + ); + + // Create another relation with the "Demo Design" page + $relation2 = $this->contentService->addRelation( + $demoDesignDraft->getVersionInfo(), + $contentInfo + ); + + // Publish drafts, so relations become active + $this->contentService->publishVersion($mediaDraft->getVersionInfo()); + $this->contentService->publishVersion($demoDesignDraft->getVersionInfo()); + + $relations = $this->contentService->loadRelations($versionInfo); + $reverseRelations = $this->contentService->loadReverseRelations($contentInfo); + + $this->assertEquals($contentInfo->id, $relation1->getDestinationContentInfo()->id); + $this->assertEquals($mediaDraft->id, $relation1->getSourceContentInfo()->id); + + $this->assertEquals($contentInfo->id, $relation2->getDestinationContentInfo()->id); + $this->assertEquals($demoDesignDraft->id, $relation2->getSourceContentInfo()->id); + + $this->assertCount(0, $relations); + $this->assertCount(2, $reverseRelations); + + usort( + $reverseRelations, + static function ($rel1, $rel2) { + return strcasecmp( + $rel2->getSourceContentInfo()->remoteId, + $rel1->getSourceContentInfo()->remoteId + ); + } + ); + + $this->assertEquals( + [ + [ + 'sourceContentInfo' => self::MEDIA_REMOTE_ID, + 'destinationContentInfo' => 'abcdef0123456789abcdef0123456789', + ], + [ + 'sourceContentInfo' => self::DEMO_DESIGN_REMOTE_ID, + 'destinationContentInfo' => 'abcdef0123456789abcdef0123456789', + ], + ], + [ + [ + 'sourceContentInfo' => $reverseRelations[0]->sourceContentInfo->remoteId, + 'destinationContentInfo' => $reverseRelations[0]->destinationContentInfo->remoteId, + ], + [ + 'sourceContentInfo' => $reverseRelations[1]->sourceContentInfo->remoteId, + 'destinationContentInfo' => $reverseRelations[1]->destinationContentInfo->remoteId, + ], + ] + ); + } + + /** + * Test for the loadReverseRelations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadReverseRelations() + * @depends testAddRelation + * @depends testLoadReverseRelations + */ + public function testLoadReverseRelationsSkipsArchivedContent() + { + $trashService = $this->getRepository()->getTrashService(); + + $versionInfo = $this->createContentVersion1()->getVersionInfo(); + $contentInfo = $versionInfo->getContentInfo(); + + // Create some drafts + $mediaDraft = $this->contentService->createContentDraft( + $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID) + ); + $demoDesignDraft = $this->contentService->createContentDraft( + $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID) + ); + + // Create relation between new content object and "Media" page + $relation1 = $this->contentService->addRelation( + $mediaDraft->getVersionInfo(), + $contentInfo + ); + + // Create another relation with the "Demo Design" page + $relation2 = $this->contentService->addRelation( + $demoDesignDraft->getVersionInfo(), + $contentInfo + ); + + // Publish drafts, so relations become active + $this->contentService->publishVersion($mediaDraft->getVersionInfo()); + $this->contentService->publishVersion($demoDesignDraft->getVersionInfo()); + + $demoDesignLocation = $this->locationService->loadLocation($demoDesignDraft->contentInfo->mainLocationId); + + // Trashing Content's last Location will change its status to archived, + // in this case relation from it will not be loaded. + $trashService->trash($demoDesignLocation); + + // Load all relations + $relations = $this->contentService->loadRelations($versionInfo); + $reverseRelations = $this->contentService->loadReverseRelations($contentInfo); + + $this->assertEquals($contentInfo->id, $relation1->getDestinationContentInfo()->id); + $this->assertEquals($mediaDraft->id, $relation1->getSourceContentInfo()->id); + + $this->assertEquals($contentInfo->id, $relation2->getDestinationContentInfo()->id); + $this->assertEquals($demoDesignDraft->id, $relation2->getSourceContentInfo()->id); + + $this->assertCount(0, $relations); + $this->assertCount(1, $reverseRelations); + + $this->assertEquals( + [ + [ + 'sourceContentInfo' => self::MEDIA_REMOTE_ID, + 'destinationContentInfo' => 'abcdef0123456789abcdef0123456789', + ], + ], + [ + [ + 'sourceContentInfo' => $reverseRelations[0]->sourceContentInfo->remoteId, + 'destinationContentInfo' => $reverseRelations[0]->destinationContentInfo->remoteId, + ], + ] + ); + } + + /** + * Test for the loadReverseRelations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadReverseRelations() + * @depends testAddRelation + * @depends testLoadReverseRelations + */ + public function testLoadReverseRelationsSkipsDraftContent() + { + // Load "Media" page Content + $media = $this->contentService->loadContentByRemoteId(self::MEDIA_REMOTE_ID); + + // Create some drafts + $newDraftVersionInfo = $this->createContentDraftVersion1()->getVersionInfo(); + $demoDesignDraft = $this->contentService->createContentDraft( + $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID) + ); + + // Create relation between "Media" page and new content object + $relation1 = $this->contentService->addRelation( + $newDraftVersionInfo, + $media->contentInfo + ); + + // Create another relation with the "Demo Design" page + $relation2 = $this->contentService->addRelation( + $demoDesignDraft->getVersionInfo(), + $media->contentInfo + ); + + // Publish drafts, so relations become active + $this->contentService->publishVersion($demoDesignDraft->getVersionInfo()); + // We will not publish new Content draft, therefore relation from it + // will not be loaded as reverse relation for "Media" page + + $relations = $this->contentService->loadRelations($media->versionInfo); + $reverseRelations = $this->contentService->loadReverseRelations($media->contentInfo); + + $this->assertEquals($media->contentInfo->id, $relation1->getDestinationContentInfo()->id); + $this->assertEquals($newDraftVersionInfo->contentInfo->id, $relation1->getSourceContentInfo()->id); + + $this->assertEquals($media->contentInfo->id, $relation2->getDestinationContentInfo()->id); + $this->assertEquals($demoDesignDraft->id, $relation2->getSourceContentInfo()->id); + + $this->assertCount(0, $relations); + $this->assertCount(1, $reverseRelations); + + $this->assertEquals( + [ + [ + 'sourceContentInfo' => self::DEMO_DESIGN_REMOTE_ID, + 'destinationContentInfo' => self::MEDIA_REMOTE_ID, + ], + ], + [ + [ + 'sourceContentInfo' => $reverseRelations[0]->sourceContentInfo->remoteId, + 'destinationContentInfo' => $reverseRelations[0]->destinationContentInfo->remoteId, + ], + ] + ); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadReverseRelationList + */ + public function testLoadReverseRelationList(): void + { + $draft1 = $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo + ); + $draft2 = $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo + ); + $draft3 = $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Baz'], 2)->contentInfo + ); + + $contentWithReverseRelations = $this->createContentWithReverseRelations([ + $draft1, + $draft2, + $draft3, + ]); + + $contentInfo = $contentWithReverseRelations->content->contentInfo; + + $reverseRelationList = $this->contentService->loadReverseRelationList($contentInfo); + + $this->assertSame(3, $reverseRelationList->totalCount); + $this->assertEquals( + $contentWithReverseRelations->reverseRelations[2]->contentInfo, + $reverseRelationList->items[0]->getRelation()->sourceContentInfo + ); + $this->assertEquals( + $contentWithReverseRelations->reverseRelations[1]->contentInfo, + $reverseRelationList->items[1]->getRelation()->sourceContentInfo + ); + $this->assertEquals( + $contentWithReverseRelations->reverseRelations[0]->contentInfo, + $reverseRelationList->items[2]->getRelation()->sourceContentInfo + ); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadReverseRelationList + */ + public function testLoadReverseRelationListWithPagination(): void + { + $draft1 = $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo + ); + $draft2 = $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo + ); + $draft3 = $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Baz'], 2)->contentInfo + ); + + $contentWithReverseRelations = $this->createContentWithReverseRelations([ + $draft1, + $draft2, + $draft3, + ]); + + $contentInfo = $contentWithReverseRelations->content->contentInfo; + + $reverseRelationPage1 = $this->contentService->loadReverseRelationList($contentInfo, 0, 2); + $reverseRelationPage2 = $this->contentService->loadReverseRelationList($contentInfo, 2, 2); + $this->assertSame(3, $reverseRelationPage1->totalCount); + $this->assertSame(3, $reverseRelationPage2->totalCount); + $this->assertEquals( + $contentWithReverseRelations->reverseRelations[2]->contentInfo, + $reverseRelationPage1->items[0]->getRelation()->sourceContentInfo + ); + $this->assertEquals( + $contentWithReverseRelations->reverseRelations[1]->contentInfo, + $reverseRelationPage1->items[1]->getRelation()->sourceContentInfo + ); + $this->assertEquals( + $contentWithReverseRelations->reverseRelations[0]->contentInfo, + $reverseRelationPage2->items[0]->getRelation()->sourceContentInfo + ); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadReverseRelationList + */ + public function testLoadReverseRelationListSkipsArchivedContent(): void + { + $trashService = $this->getRepository()->getTrashService(); + + $draft1 = $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo + ); + $draft2 = $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo + ); + $draft3 = $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Baz'], 2)->contentInfo + ); + + $contentWithReverseRelations = $this->createContentWithReverseRelations([ + $draft1, + $draft2, + $draft3, + ]); + + $locationToTrash = $this->locationService->loadLocation($draft3->contentInfo->mainLocationId); + + // Trashing Content's last Location will change its status to archived, in this case relation from it will not be loaded. + $trashService->trash($locationToTrash); + + $contentInfo = $contentWithReverseRelations->content->contentInfo; + $reverseRelationList = $this->contentService->loadReverseRelationList($contentInfo); + + $this->assertSame(2, $reverseRelationList->totalCount); + $this->assertEquals( + $contentWithReverseRelations->reverseRelations[1]->contentInfo, + $reverseRelationList->items[0]->getRelation()->sourceContentInfo + ); + $this->assertEquals( + $contentWithReverseRelations->reverseRelations[0]->contentInfo, + $reverseRelationList->items[1]->getRelation()->sourceContentInfo + ); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadReverseRelationList + */ + public function testLoadReverseRelationListSkipsDraftContent() + { + $draft1 = $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Foo'], 2)->contentInfo + ); + + $contentWithReverseRelations = $this->createContentWithReverseRelations([$draft1]); + + $contentInfo = $contentWithReverseRelations->content->contentInfo; + + // create a relation, but without publishing it + $draft2 = $this->contentService->createContentDraft( + $this->createFolder([self::ENG_GB => 'Bar'], 2)->contentInfo + ); + $this->contentService->addRelation( + $draft2->getVersionInfo(), + $contentInfo + ); + + $reverseRelationList = $this->contentService->loadReverseRelationList($contentInfo); + + $this->assertSame(1, $reverseRelationList->totalCount); + $this->assertEquals( + $contentWithReverseRelations->reverseRelations[0]->contentInfo, + $reverseRelationList->items[0]->getRelation()->sourceContentInfo + ); + } + + /** + * Test for the deleteRelation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteRelation() + * @depends testLoadRelations + */ + public function testDeleteRelation() + { + $draft = $this->createContentDraftVersion1(); + + $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); + $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID); + + // Establish some relations + $this->contentService->addRelation($draft->getVersionInfo(), $media); + $this->contentService->addRelation($draft->getVersionInfo(), $demoDesign); + + // Delete one of the currently created relations + $this->contentService->deleteRelation($draft->getVersionInfo(), $media); + + // The relations array now contains only one element + $relations = $this->contentService->loadRelations($draft->getVersionInfo()); + + $this->assertCount(1, $relations); + } + + /** + * Test for the deleteRelation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteRelation() + * @depends testDeleteRelation + */ + public function testDeleteRelationThrowsBadStateException() + { + $content = $this->createContentVersion1(); + + // Load the destination object + $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); + + // Create a new draft + $draftVersion2 = $this->contentService->createContentDraft($content->contentInfo); + + // Add a relation + $this->contentService->addRelation($draftVersion2->getVersionInfo(), $media); + + // Publish new version + $contentVersion2 = $this->contentService->publishVersion( + $draftVersion2->getVersionInfo() + ); + + $this->expectException(BadStateException::class); + + // This call will fail with a "BadStateException", because content is published and not a draft. + $this->contentService->deleteRelation( + $contentVersion2->getVersionInfo(), + $media + ); + } + + /** + * Test for the deleteRelation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteRelation() + * @depends testDeleteRelation + */ + public function testDeleteRelationThrowsInvalidArgumentException() + { + $draft = $this->createContentDraftVersion1(); + + // Load the destination object + $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); + + // This call will fail with a "InvalidArgumentException", because no relation exists between $draft and $media. + $this->expectException(APIInvalidArgumentException::class); + $this->contentService->deleteRelation( + $draft->getVersionInfo(), + $media + ); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContent + * @depends testLoadContent + */ + public function testCreateContentInTransactionWithRollback() + { + $repository = $this->getRepository(); + + $contentTypeService = $this->getRepository()->getContentTypeService(); + + // Start a transaction + $repository->beginTransaction(); + + try { + $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); + + // Get a content create struct and set mandatory properties + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + $contentCreate->setField('name', 'Sindelfingen forum'); + + $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789'; + $contentCreate->alwaysAvailable = true; + + // Create a new content object + $contentId = $this->contentService->createContent($contentCreate)->id; + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes + $repository->rollback(); + + try { + // This call will fail with a "NotFoundException" + $this->contentService->loadContent($contentId); + } catch (NotFoundException $e) { + // This is expected + return; + } + + $this->fail('Content object still exists after rollback.'); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContent + * @depends testLoadContent + */ + public function testCreateContentInTransactionWithCommit() + { + $repository = $this->getRepository(); + + $contentTypeService = $repository->getContentTypeService(); + + // Start a transaction + $repository->beginTransaction(); + + try { + $contentType = $contentTypeService->loadContentTypeByIdentifier(self::FORUM_IDENTIFIER); + + // Get a content create struct and set mandatory properties + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + $contentCreate->setField('name', 'Sindelfingen forum'); + + $contentCreate->remoteId = 'abcdef0123456789abcdef0123456789'; + $contentCreate->alwaysAvailable = true; + + // Create a new content object + $contentId = $this->contentService->createContent($contentCreate)->id; + + // Commit changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Load the new content object + $content = $this->contentService->loadContent($contentId); + + $this->assertEquals($contentId, $content->id); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs) + * @depends testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately + * @depends testLoadContentThrowsNotFoundException + */ + public function testCreateContentWithLocationCreateParameterInTransactionWithRollback() + { + $repository = $this->getRepository(); + + // Start a transaction + $repository->beginTransaction(); + + try { + $draft = $this->createContentDraftVersion1(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + $contentId = $draft->id; + + // Roleback the transaction + $repository->rollback(); + + try { + // This call will fail with a "NotFoundException" + $this->contentService->loadContent($contentId); + } catch (NotFoundException $e) { + return; + } + + $this->fail('Can still load content object after rollback.'); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent($contentCreateStruct, $locationCreateStructs) + * @depends testCreateContentWithLocationCreateParameterDoesNotCreateLocationImmediately + * @depends testLoadContentThrowsNotFoundException + */ + public function testCreateContentWithLocationCreateParameterInTransactionWithCommit() + { + $repository = $this->getRepository(); + + // Start a transaction + $repository->beginTransaction(); + + try { + $draft = $this->createContentDraftVersion1(); + + $contentId = $draft->id; + + // Roleback the transaction + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Load the new content object + $content = $this->contentService->loadContent($contentId); + + $this->assertEquals($contentId, $content->id); + } + + /** + * Test for the createContentDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft() + * @depends testCreateContentDraft + * @depends testLoadContent + */ + public function testCreateContentDraftInTransactionWithRollback() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); + + // Load the user group content object + $content = $this->contentService->loadContent($contentId); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Create a new draft + $drafted = $this->contentService->createContentDraft($content->contentInfo); + + // Store version number for later reuse + $versionNo = $drafted->versionInfo->versionNo; + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback + $repository->rollback(); + + try { + // This call will fail with a "NotFoundException" + $this->contentService->loadContent($contentId, null, $versionNo); + } catch (NotFoundException $e) { + return; + } + + $this->fail('Can still load content draft after rollback'); + } + + /** + * Test for the createContentDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft() + * @depends testCreateContentDraft + * @depends testLoadContent + */ + public function testCreateContentDraftInTransactionWithCommit() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); + + // Load the user group content object + $content = $this->contentService->loadContent($contentId); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Create a new draft + $drafted = $this->contentService->createContentDraft($content->contentInfo); + + // Store version number for later reuse + $versionNo = $drafted->versionInfo->versionNo; + + // Commit all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + $content = $this->contentService->loadContent($contentId, null, $versionNo); + + $this->assertEquals( + $versionNo, + $content->getVersionInfo()->versionNo + ); + } + + /** + * Test for the publishVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends testPublishVersion + * @depends testLoadContent + */ + public function testPublishVersionInTransactionWithRollback() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); + + // Load the user group content object + $content = $this->contentService->loadContent($contentId); + + // Start a new transaction + $repository->beginTransaction(); + + try { + $draftVersion = $this->contentService->createContentDraft($content->contentInfo)->getVersionInfo(); + + // Publish a new version + $content = $this->contentService->publishVersion($draftVersion); + + // Store version number for later reuse + $versionNo = $content->versionInfo->versionNo; + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback + $repository->rollback(); + + try { + // This call will fail with a "NotFoundException" + $this->contentService->loadContent($contentId, null, $versionNo); + } catch (NotFoundException $e) { + return; + } + + $this->fail('Can still load content draft after rollback'); + } + + /** + * Test for the publishVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion() + * @depends testPublishVersion + * @depends testLoadVersionInfo + */ + public function testPublishVersionInTransactionWithCommit() + { + $repository = $this->getRepository(); + + // Load the user group content object + $template = $this->contentService->loadContent(self::ADMINISTRATORS_USER_GROUP_ID); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Publish a new version + $content = $this->contentService->publishVersion( + $this->contentService->createContentDraft($template->contentInfo)->getVersionInfo() + ); + + // Store version number for later reuse + $versionNo = $content->versionInfo->versionNo; + + // Commit all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Load current version info + $versionInfo = $this->contentService->loadVersionInfo($content->contentInfo); + + $this->assertEquals($versionNo, $versionInfo->versionNo); + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContent + * @depends testLoadContent + * @depends testLoadContentInfo + */ + public function testUpdateContentInTransactionWithRollback() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); + + // Create a new user group draft + $draft = $this->contentService->createContentDraft( + $this->contentService->loadContentInfo($contentId) + ); + + // Get an update struct and change the group name + $contentUpdate = $this->contentService->newContentUpdateStruct(); + $contentUpdate->setField('name', self::ADMINISTRATORS_USER_GROUP_NAME, self::ENG_US); + + // Start a transaction + $repository->beginTransaction(); + + try { + // Update the group name + $draft = $this->contentService->updateContent( + $draft->getVersionInfo(), + $contentUpdate + ); + + // Publish updated version + $this->contentService->publishVersion($draft->getVersionInfo()); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes. + $repository->rollback(); + + // Name will still be "Administrator users" + $name = $this->contentService->loadContent($contentId)->getFieldValue('name'); + + $this->assertEquals('Administrator users', $name); + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContent + * @depends testLoadContent + * @depends testLoadContentInfo + */ + public function testUpdateContentInTransactionWithCommit() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); + + // Create a new user group draft + $draft = $this->contentService->createContentDraft( + $this->contentService->loadContentInfo($contentId) + ); + + // Get an update struct and change the group name + $contentUpdate = $this->contentService->newContentUpdateStruct(); + $contentUpdate->setField('name', self::ADMINISTRATORS_USER_GROUP_NAME, self::ENG_US); + + // Start a transaction + $repository->beginTransaction(); + + try { + // Update the group name + $draft = $this->contentService->updateContent( + $draft->getVersionInfo(), + $contentUpdate + ); + + // Publish updated version + $this->contentService->publishVersion($draft->getVersionInfo()); + + // Commit all changes. + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Name is now "Administrators" + $name = $this->contentService->loadContent($contentId)->getFieldValue('name', self::ENG_US); + + $this->assertEquals(self::ADMINISTRATORS_USER_GROUP_NAME, $name); + } + + /** + * Test for the updateContentMetadata() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContentMetadata() + * @depends testUpdateContentMetadata + * @depends testLoadContentInfo + */ + public function testUpdateContentMetadataInTransactionWithRollback() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); + + // Load a ContentInfo object + $contentInfo = $this->contentService->loadContentInfo($contentId); + + // Store remoteId for later testing + $remoteId = $contentInfo->remoteId; + + // Start a transaction + $repository->beginTransaction(); + + try { + // Get metadata update struct and change remoteId + $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct(); + $metadataUpdate->remoteId = md5(microtime(true)); + + // Update the metadata of the published content object + $this->contentService->updateContentMetadata( + $contentInfo, + $metadataUpdate + ); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes. + $repository->rollback(); + + // Load current remoteId + $remoteIdReloaded = $this->contentService->loadContentInfo($contentId)->remoteId; + + $this->assertEquals($remoteId, $remoteIdReloaded); + } + + /** + * Test for the updateContentMetadata() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContentMetadata() + * @depends testUpdateContentMetadata + * @depends testLoadContentInfo + */ + public function testUpdateContentMetadataInTransactionWithCommit() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); + + // Load a ContentInfo object + $contentInfo = $this->contentService->loadContentInfo($contentId); + + // Store remoteId for later testing + $remoteId = $contentInfo->remoteId; + + // Start a transaction + $repository->beginTransaction(); + + try { + // Get metadata update struct and change remoteId + $metadataUpdate = $this->contentService->newContentMetadataUpdateStruct(); + $metadataUpdate->remoteId = md5(microtime(true)); + + // Update the metadata of the published content object + $this->contentService->updateContentMetadata( + $contentInfo, + $metadataUpdate + ); + + // Commit all changes. + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Load current remoteId + $remoteIdReloaded = $this->contentService->loadContentInfo($contentId)->remoteId; + + $this->assertNotEquals($remoteId, $remoteIdReloaded); + } + + /** + * Test for the updateContentMetadata() method, and how cache + transactions play together. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContentMetadata() + * @depends testUpdateContentMetadata + * @depends testLoadContentInfo + */ + public function testUpdateContentMetadataCheckWithinTransaction() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentId = $this->generateId('object', 12); + + // Load a ContentInfo object, and warmup cache + $contentInfo = $contentService->loadContentInfo($contentId); + + // Store remoteId for later testing + $remoteId = $contentInfo->remoteId; + + // Start a transaction + $repository->beginTransaction(); + + try { + // Get metadata update struct and change remoteId + $metadataUpdate = $contentService->newContentMetadataUpdateStruct(); + $metadataUpdate->remoteId = md5(microtime(true)); + + // Update the metadata of the published content object + $contentService->updateContentMetadata( + $contentInfo, + $metadataUpdate + ); + + // Check that it's been updated + $remoteIdReloaded = $contentService->loadContentInfo($contentId)->remoteId; + $this->assertNotEquals($remoteId, $remoteIdReloaded); + + // Commit all changes. + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + } + + /** + * Test for the deleteVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteVersion() + * @depends testCreateContent + * @depends testLoadContentInfo + * @depends testLoadContentDrafts + */ + public function testDeleteVersionInTransactionWithRollback() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Create a new draft + $draft = $this->contentService->createContentDraft( + $this->contentService->loadContentInfo($contentId) + ); + + $this->contentService->deleteVersion($draft->getVersionInfo()); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes. + $repository->rollback(); + + // This array will be empty + $drafts = $this->contentService->loadContentDrafts(); + + $this->assertSame([], $drafts); + } + + /** + * Test for the deleteVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteVersion() + * @depends testCreateContent + * @depends testLoadContentInfo + * @depends testLoadContentDrafts + */ + public function testDeleteVersionInTransactionWithCommit() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', self::ADMINISTRATORS_USER_GROUP_ID); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Create a new draft + $draft = $this->contentService->createContentDraft( + $this->contentService->loadContentInfo($contentId) + ); + + $this->contentService->deleteVersion($draft->getVersionInfo()); + + // Commit all changes. + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // This array will contain no element + $drafts = $this->contentService->loadContentDrafts(); + + $this->assertSame([], $drafts); + } + + /** + * Test for the deleteContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteContent() + * @depends testDeleteContent + * @depends testLoadContentInfo + */ + public function testDeleteContentInTransactionWithRollback() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID); + + // Load a ContentInfo instance + $contentInfo = $this->contentService->loadContentInfo($contentId); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Delete content object + $this->contentService->deleteContent($contentInfo); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes + $repository->rollback(); + + // This call will return the original content object + $contentInfo = $this->contentService->loadContentInfo($contentId); + + $this->assertEquals($contentId, $contentInfo->id); + } + + /** + * Test for the deleteContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteContent() + * @depends testDeleteContent + * @depends testLoadContentInfo + */ + public function testDeleteContentInTransactionWithCommit() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID); + + // Load a ContentInfo instance + $contentInfo = $this->contentService->loadContentInfo($contentId); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Delete content object + $this->contentService->deleteContent($contentInfo); + + // Commit all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Deleted content info is not found anymore + try { + $this->contentService->loadContentInfo($contentId); + } catch (NotFoundException $e) { + return; + } + + $this->fail('Can still load ContentInfo after commit.'); + } + + /** + * Test for the copyContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::copyContent() + * @depends testCopyContent + */ + public function testCopyContentInTransactionWithRollback() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID); + $locationId = $this->generateId('location', self::ADMINISTRATORS_USER_GROUP_LOCATION_ID); + + // Load content object to copy + $content = $this->contentService->loadContent($contentId); + + // Create new target location + $locationCreate = $this->locationService->newLocationCreateStruct($locationId); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Copy content with all versions and drafts + $this->contentService->copyContent( + $content->contentInfo, + $locationCreate + ); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes + $repository->rollback(); + + $this->refreshSearch($repository); + + // This array will only contain a single admin user object + $locations = $this->locationService->loadLocationChildren( + $this->locationService->loadLocation($locationId) + )->locations; + + $this->assertCount(1, $locations); + } + + /** + * Test for the copyContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::copyContent() + * @depends testCopyContent + */ + public function testCopyContentInTransactionWithCommit() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', self::MEMBERS_USER_GROUP_ID); + $locationId = $this->generateId('location', self::ADMINISTRATORS_USER_GROUP_LOCATION_ID); + + // Load content object to copy + $content = $this->contentService->loadContent($contentId); + + // Create new target location + $locationCreate = $this->locationService->newLocationCreateStruct($locationId); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Copy content with all versions and drafts + $this->contentService->copyContent( + $content->contentInfo, + $locationCreate + ); + + // Commit all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + $this->refreshSearch($repository); + + // This will contain the admin user and the new child location + $locations = $this->locationService->loadLocationChildren( + $this->locationService->loadLocation($locationId) + )->locations; + + $this->assertCount(2, $locations); + } + + public function testURLAliasesCreatedForNewContent() + { + $urlAliasService = $this->getRepository()->getURLAliasService(); + + $draft = $this->createContentDraftVersion1(); + + // Automatically creates a new URLAlias for the content + $liveContent = $this->contentService->publishVersion($draft->getVersionInfo()); + + $location = $this->locationService->loadLocation( + $liveContent->getVersionInfo()->getContentInfo()->mainLocationId + ); + + $aliases = $urlAliasService->listLocationAliases($location, false); + + $this->assertAliasesCorrect( + [ + '/Design/Plain-site/An-awesome-forum' => [ + 'type' => URLAlias::LOCATION, + 'destination' => $location->id, + 'path' => '/Design/Plain-site/An-awesome-forum', + 'languageCodes' => [self::ENG_US], + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ], + ], + $aliases + ); + } + + public function testURLAliasesCreatedForUpdatedContent() + { + $urlAliasService = $this->getRepository()->getURLAliasService(); + + $draft = $this->createUpdatedDraftVersion2(); + + $location = $this->locationService->loadLocation( + $draft->getVersionInfo()->getContentInfo()->mainLocationId + ); + + // Load and assert URL aliases before publishing updated Content, so that + // SPI cache is warmed up and cache invalidation is also tested. + $aliases = $urlAliasService->listLocationAliases($location, false); + + $this->assertAliasesCorrect( + [ + '/Design/Plain-site/An-awesome-forum' => [ + 'type' => URLAlias::LOCATION, + 'destination' => $location->id, + 'path' => '/Design/Plain-site/An-awesome-forum', + 'languageCodes' => [self::ENG_US], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ], + ], + $aliases + ); + + // Automatically marks old aliases for the content as history + // and creates new aliases, based on the changes + $liveContent = $this->contentService->publishVersion($draft->getVersionInfo()); + + $location = $this->locationService->loadLocation( + $liveContent->getVersionInfo()->getContentInfo()->mainLocationId + ); + + $aliases = $urlAliasService->listLocationAliases($location, false); + + $this->assertAliasesCorrect( + [ + '/Design/Plain-site/An-awesome-forum2' => [ + 'type' => URLAlias::LOCATION, + 'destination' => $location->id, + 'path' => '/Design/Plain-site/An-awesome-forum2', + 'languageCodes' => [self::ENG_US], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ], + '/Design/Plain-site/An-awesome-forum23' => [ + 'type' => URLAlias::LOCATION, + 'destination' => $location->id, + 'path' => '/Design/Plain-site/An-awesome-forum23', + 'languageCodes' => [self::ENG_GB], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ], + ], + $aliases + ); + } + + public function testCustomURLAliasesNotHistorizedOnUpdatedContent() + { + $urlAliasService = $this->getRepository()->getURLAliasService(); + + $content = $this->createContentVersion1(); + + // Create a custom URL alias + $urlAliasService->createUrlAlias( + $this->locationService->loadLocation( + $content->getVersionInfo()->getContentInfo()->mainLocationId + ), + '/my/fancy/story-about-ibexa-dxp', + self::ENG_US + ); + + $draftVersion2 = $this->contentService->createContentDraft($content->contentInfo); + + $contentUpdate = $this->contentService->newContentUpdateStruct(); + $contentUpdate->initialLanguageCode = self::ENG_US; + $contentUpdate->setField('name', 'Amazing Bielefeld forum'); + + $draftVersion2 = $this->contentService->updateContent( + $draftVersion2->getVersionInfo(), + $contentUpdate + ); + + // Only marks auto-generated aliases as history + // the custom one is left untouched + $liveContent = $this->contentService->publishVersion($draftVersion2->getVersionInfo()); + + $location = $this->locationService->loadLocation( + $liveContent->getVersionInfo()->getContentInfo()->mainLocationId + ); + + $aliases = $urlAliasService->listLocationAliases($location); + + $this->assertAliasesCorrect( + [ + '/my/fancy/story-about-ibexa-dxp' => [ + 'type' => URLAlias::LOCATION, + 'destination' => $location->id, + 'path' => '/my/fancy/story-about-ibexa-dxp', + 'languageCodes' => [self::ENG_US], + 'isHistory' => false, + 'isCustom' => true, + 'forward' => false, + 'alwaysAvailable' => false, + ], + ], + $aliases + ); + } + + /** + * Test to ensure that old versions are not affected by updates to newer + * drafts. + */ + public function testUpdatingDraftDoesNotUpdateOldVersions() + { + $contentVersion2 = $this->createContentVersion2(); + + $loadedContent1 = $this->contentService->loadContent($contentVersion2->id, null, 1); + $loadedContent2 = $this->contentService->loadContent($contentVersion2->id, null, 2); + + $this->assertNotEquals( + $loadedContent1->getFieldValue('name', self::ENG_US), + $loadedContent2->getFieldValue('name', self::ENG_US) + ); + } + + /** + * Test scenario with writer and publisher users. + * Writer can only create content. Publisher can publish this content. + */ + public function testPublishWorkflow() + { + $this->createRoleWithPolicies('Publisher', [ + ['module' => 'content', 'function' => 'read'], + ['module' => 'content', 'function' => 'create'], + ['module' => 'content', 'function' => 'publish'], + ]); + + $this->createRoleWithPolicies('Writer', [ + ['module' => 'content', 'function' => 'read'], + ['module' => 'content', 'function' => 'create'], + ]); + + $writerUser = $this->createCustomUserWithLogin( + 'writer', + 'writer@example.com', + self::WRITERS_USER_GROUP_NAME, + 'Writer' + ); + + $publisherUser = $this->createCustomUserWithLogin( + 'publisher', + 'publisher@example.com', + 'Publishers', + 'Publisher' + ); + + $this->permissionResolver->setCurrentUserReference($writerUser); + $draft = $this->createContentDraftVersion1(); + + $this->permissionResolver->setCurrentUserReference($publisherUser); + $content = $this->contentService->publishVersion($draft->versionInfo); + + $this->contentService->loadContent($content->id); + } + + /** + * Test publish / content policy is required to be able to publish content. + */ + public function testPublishContentWithoutPublishPolicyThrowsException() + { + $this->createRoleWithPolicies('Writer', [ + ['module' => 'content', 'function' => 'read'], + ['module' => 'content', 'function' => 'create'], + ['module' => 'content', 'function' => 'edit'], + ]); + $writerUser = $this->createCustomUserWithLogin( + 'writer', + 'writer@example.com', + self::WRITERS_USER_GROUP_NAME, + 'Writer' + ); + $this->permissionResolver->setCurrentUserReference($writerUser); + + $this->expectException(CoreUnauthorizedException::class); + $this->expectExceptionMessageMatches('/The User does not have the \'publish\' \'content\' permission/'); + + $this->createContentVersion1(); + } + + /** + * Test removal of the specific translation from all the Versions of a Content Object. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslation + */ + public function testDeleteTranslation() + { + $content = $this->createContentVersion2(); + + // create multiple versions to exceed archive limit + for ($i = 0; $i < 5; ++$i) { + $contentDraft = $this->contentService->createContentDraft($content->contentInfo); + $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); + $contentDraft = $this->contentService->updateContent( + $contentDraft->versionInfo, + $contentUpdateStruct + ); + $this->contentService->publishVersion($contentDraft->versionInfo); + } + + $this->contentService->deleteTranslation($content->contentInfo, self::ENG_GB); + + $this->assertTranslationDoesNotExist(self::ENG_GB, $content->id); + } + + /** + * Test deleting a Translation which is initial for some Version, updates initialLanguageCode + * with mainLanguageCode (assuming they are different). + */ + public function testDeleteTranslationUpdatesInitialLanguageCodeVersion() + { + $content = $this->createContentVersion2(); + // create another, copied, version + $contentDraft = $this->contentService->updateContent( + $this->contentService->createContentDraft($content->contentInfo)->versionInfo, + $this->contentService->newContentUpdateStruct() + ); + $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo); + + // remove first version with only one translation as it is not the subject of this test + $this->contentService->deleteVersion( + $this->contentService->loadVersionInfo($publishedContent->contentInfo, 1) + ); + + // sanity check + self::assertEquals(self::ENG_US, $content->contentInfo->mainLanguageCode); + self::assertEquals(self::ENG_US, $content->versionInfo->initialLanguageCode); + + // update mainLanguageCode so it is different than initialLanguageCode for Version + $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct(); + $contentMetadataUpdateStruct->mainLanguageCode = self::ENG_GB; + $content = $this->contentService->updateContentMetadata($publishedContent->contentInfo, $contentMetadataUpdateStruct); + + $this->contentService->deleteTranslation($content->contentInfo, self::ENG_US); + + $this->assertTranslationDoesNotExist(self::ENG_US, $content->id); + } + + /** + * Test removal of the specific translation properly updates languages of the URL alias. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslation + */ + public function testDeleteTranslationUpdatesUrlAlias() + { + $urlAliasService = $this->getRepository()->getURLAliasService(); + + $content = $this->createContentVersion2(); + $mainLocation = $this->locationService->loadLocation($content->contentInfo->mainLocationId); + + // create custom URL alias for Content main Location + $urlAliasService->createUrlAlias($mainLocation, '/my-custom-url', self::ENG_GB); + + // create secondary Location for Content + $secondaryLocation = $this->locationService->createLocation( + $content->contentInfo, + $this->locationService->newLocationCreateStruct(2) + ); + + // create custom URL alias for Content secondary Location + $urlAliasService->createUrlAlias($secondaryLocation, '/my-secondary-url', self::ENG_GB); + + // delete Translation + $this->contentService->deleteTranslation($content->contentInfo, self::ENG_GB); + + foreach ([$mainLocation, $secondaryLocation] as $location) { + // check auto-generated URL aliases + foreach ($urlAliasService->listLocationAliases($location, false) as $alias) { + self::assertNotContains(self::ENG_GB, $alias->languageCodes); + } + + // check custom URL aliases + foreach ($urlAliasService->listLocationAliases($location) as $alias) { + self::assertNotContains(self::ENG_GB, $alias->languageCodes); + } + } + } + + /** + * Test removal of a main translation throws BadStateException. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslation + */ + public function testDeleteTranslationMainLanguageThrowsBadStateException() + { + $content = $this->createContentVersion2(); + + // delete first version which has only one translation + $this->contentService->deleteVersion($this->contentService->loadVersionInfo($content->contentInfo, 1)); + + // try to delete main translation + $this->expectException(BadStateException::class); + $this->expectExceptionMessage('The provided translation is the main translation of the Content item'); + + $this->contentService->deleteTranslation($content->contentInfo, $content->contentInfo->mainLanguageCode); + } + + /** + * Test removal of a Translation is possible when some archived Versions have only this Translation. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslation + */ + public function testDeleteTranslationDeletesSingleTranslationVersions() + { + // content created by the createContentVersion1 method has eng-US translation only. + $content = $this->createContentVersion1(); + + // create new version and add eng-GB translation + $contentDraft = $this->contentService->createContentDraft($content->contentInfo); + $contentUpdateStruct = $this->contentService->newContentUpdateStruct(); + $contentUpdateStruct->setField('name', 'Awesome Board', self::ENG_GB); + $contentDraft = $this->contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct); + $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo); + + // update mainLanguageCode to avoid exception related to that + $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct(); + $contentMetadataUpdateStruct->mainLanguageCode = self::ENG_GB; + + $content = $this->contentService->updateContentMetadata($publishedContent->contentInfo, $contentMetadataUpdateStruct); + + $this->contentService->deleteTranslation($content->contentInfo, self::ENG_US); + + $this->assertTranslationDoesNotExist(self::ENG_US, $content->id); + } + + /** + * Test removal of the translation by the user who is not allowed to delete a content + * throws UnauthorizedException. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslation + */ + public function testDeleteTranslationThrowsUnauthorizedException() + { + $content = $this->createContentVersion2(); + + // create user that can read/create/edit but cannot delete content + $this->createRoleWithPolicies('Writer', [ + ['module' => 'content', 'function' => 'read'], + ['module' => 'content', 'function' => 'versionread'], + ['module' => 'content', 'function' => 'create'], + ['module' => 'content', 'function' => 'edit'], + ]); + $writerUser = $this->createCustomUserWithLogin( + 'writer', + 'writer@example.com', + self::WRITERS_USER_GROUP_NAME, + 'Writer' + ); + $this->permissionResolver->setCurrentUserReference($writerUser); + + $this->expectException(UnauthorizedException::class); + $this->expectExceptionMessage('The User does not have the \'remove\' \'content\' permission'); + + $this->contentService->deleteTranslation($content->contentInfo, self::ENG_GB); + } + + /** + * Test removal of a non-existent translation throws InvalidArgumentException. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslation + */ + public function testDeleteTranslationThrowsInvalidArgumentException() + { + // content created by the createContentVersion1 method has eng-US translation only. + $content = $this->createContentVersion1(); + + $this->expectException(APIInvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$languageCode\' is invalid: ger-DE does not exist in the Content item'); + + $this->contentService->deleteTranslation($content->contentInfo, self::GER_DE); + } + + /** + * Test deleting a Translation from Draft. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslationFromDraft + */ + public function testDeleteTranslationFromDraft() + { + $languageCode = self::ENG_GB; + $content = $this->createMultipleLanguageContentVersion2(); + $draft = $this->contentService->createContentDraft($content->contentInfo); + $draft = $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode); + $content = $this->contentService->publishVersion($draft->versionInfo); + + $loadedContent = $this->contentService->loadContent($content->id); + self::assertNotContains($languageCode, $loadedContent->versionInfo->languageCodes); + self::assertEmpty($loadedContent->getFieldsByLanguage($languageCode)); + } + + /** + * Get values for multilingual field. + * + * @return array + */ + public function providerForDeleteTranslationFromDraftRemovesUrlAliasOnPublishing() + { + return [ + [ + [self::ENG_US => 'US Name', self::ENG_GB => 'GB Name'], + ], + [ + [self::ENG_US => 'Same Name', self::ENG_GB => 'Same Name'], + ], + ]; + } + + /** + * Test deleting a Translation from Draft removes previously stored URL aliases for published Content. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslationFromDraft + * + * @dataProvider providerForDeleteTranslationFromDraftRemovesUrlAliasOnPublishing + * + * @param string[] $fieldValues translated field values + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testDeleteTranslationFromDraftRemovesUrlAliasOnPublishing(array $fieldValues) + { + $urlAliasService = $this->getRepository()->getURLAliasService(); + + // set language code to be removed + $languageCode = self::ENG_GB; + $draft = $this->createMultilingualContentDraft( + 'folder', + 2, + self::ENG_US, + [ + 'name' => [ + self::ENG_GB => $fieldValues[self::ENG_GB], + self::ENG_US => $fieldValues[self::ENG_US], + ], + ] + ); + $content = $this->contentService->publishVersion($draft->versionInfo); + + // create secondary location + $this->locationService->createLocation( + $content->contentInfo, + $this->locationService->newLocationCreateStruct(5) + ); + + // sanity check + $locations = $this->locationService->loadLocations($content->contentInfo); + self::assertCount(2, $locations, 'Sanity check: Expected to find 2 Locations'); + foreach ($locations as $location) { + $urlAliasService->createUrlAlias($location, '/us-custom_' . $location->id, self::ENG_US); + $urlAliasService->createUrlAlias($location, '/gb-custom_' . $location->id, self::ENG_GB); + + // check default URL aliases + $aliases = $urlAliasService->listLocationAliases($location, false, $languageCode); + self::assertNotEmpty($aliases, 'Sanity check: URL alias for the translation does not exist'); + + // check custom URL aliases + $aliases = $urlAliasService->listLocationAliases($location, true, $languageCode); + self::assertNotEmpty($aliases, 'Sanity check: Custom URL alias for the translation does not exist'); + } + + // delete translation and publish new version + $draft = $this->contentService->createContentDraft($content->contentInfo); + $draft = $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode); + $this->contentService->publishVersion($draft->versionInfo); + + // check that aliases does not exist + foreach ($locations as $location) { + // check default URL aliases + $aliases = $urlAliasService->listLocationAliases($location, false, $languageCode); + self::assertEmpty($aliases, 'URL alias for the deleted translation still exists'); + + // check custom URL aliases + $aliases = $urlAliasService->listLocationAliases($location, true, $languageCode); + self::assertEmpty($aliases, 'Custom URL alias for the deleted translation still exists'); + } + } + + /** + * Test that URL aliases for deleted Translations are properly archived. + */ + public function testDeleteTranslationFromDraftArchivesUrlAliasOnPublishing() + { + $urlAliasService = $this->getRepository()->getURLAliasService(); + + $content = $this->contentService->publishVersion( + $this->createMultilingualContentDraft( + 'folder', + 2, + self::ENG_US, + [ + 'name' => [ + self::ENG_GB => 'BritishEnglishContent', + self::ENG_US => 'AmericanEnglishContent', + ], + ] + )->versionInfo + ); + + $unrelatedContent = $this->contentService->publishVersion( + $this->createMultilingualContentDraft( + 'folder', + 2, + self::ENG_US, + [ + 'name' => [ + self::ENG_GB => 'AnotherBritishContent', + self::ENG_US => 'AnotherAmericanContent', + ], + ] + )->versionInfo + ); + + $urlAlias = $urlAliasService->lookup('/BritishEnglishContent'); + self::assertFalse($urlAlias->isHistory); + self::assertEquals($urlAlias->path, '/BritishEnglishContent'); + self::assertEquals($urlAlias->destination, $content->contentInfo->mainLocationId); + + $draft = $this->contentService->deleteTranslationFromDraft( + $this->contentService->createContentDraft($content->contentInfo)->versionInfo, + self::ENG_GB + ); + $content = $this->contentService->publishVersion($draft->versionInfo); + + $urlAlias = $urlAliasService->lookup('/BritishEnglishContent'); + self::assertTrue($urlAlias->isHistory); + self::assertEquals($urlAlias->path, '/BritishEnglishContent'); + self::assertEquals($urlAlias->destination, $content->contentInfo->mainLocationId); + + $unrelatedUrlAlias = $urlAliasService->lookup('/AnotherBritishContent'); + self::assertFalse($unrelatedUrlAlias->isHistory); + self::assertEquals($unrelatedUrlAlias->path, '/AnotherBritishContent'); + self::assertEquals($unrelatedUrlAlias->destination, $unrelatedContent->contentInfo->mainLocationId); + } + + /** + * Test deleting a Translation from Draft which has single Translation throws BadStateException. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslationFromDraft + */ + public function testDeleteTranslationFromDraftThrowsBadStateExceptionOnSingleTranslation() + { + // create Content with single Translation + $publishedContent = $this->contentService->publishVersion( + $this->createContentDraft( + self::FORUM_IDENTIFIER, + 2, + ['name' => 'Eng-US Version name'] + )->versionInfo + ); + + // update mainLanguageCode to avoid exception related to trying to delete main Translation + $contentMetadataUpdateStruct = $this->contentService->newContentMetadataUpdateStruct(); + $contentMetadataUpdateStruct->mainLanguageCode = self::ENG_GB; + $publishedContent = $this->contentService->updateContentMetadata( + $publishedContent->contentInfo, + $contentMetadataUpdateStruct + ); + + // create single Translation Version from the first one + $draft = $this->contentService->createContentDraft( + $publishedContent->contentInfo, + $publishedContent->versionInfo + ); + + $this->expectException(BadStateException::class); + $this->expectExceptionMessage('The provided translation is the only translation in this version'); + + // attempt to delete Translation + $this->contentService->deleteTranslationFromDraft($draft->versionInfo, self::ENG_US); + } + + /** + * Test deleting the Main Translation from Draft throws BadStateException. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslationFromDraft + */ + public function testDeleteTranslationFromDraftThrowsBadStateExceptionOnMainTranslation() + { + $mainLanguageCode = self::ENG_US; + $draft = $this->createMultilingualContentDraft( + self::FORUM_IDENTIFIER, + 2, + $mainLanguageCode, + [ + 'name' => [ + self::ENG_US => 'An awesome eng-US forum', + self::ENG_GB => 'An awesome eng-GB forum', + ], + ] + ); + + $this->expectException(BadStateException::class); + $this->expectExceptionMessage('the specified translation is the main translation of the Content item'); + + $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $mainLanguageCode); + } + + /** + * Test deleting the Translation from Published Version throws BadStateException. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslationFromDraft + */ + public function testDeleteTranslationFromDraftThrowsBadStateExceptionOnPublishedVersion() + { + $languageCode = self::ENG_US; + $content = $this->createMultipleLanguageContentVersion2(); + $draft = $this->contentService->createContentDraft($content->contentInfo); + $publishedContent = $this->contentService->publishVersion($draft->versionInfo); + + $this->expectException(BadStateException::class); + $this->expectExceptionMessage('The version is not a draft'); + + $this->contentService->deleteTranslationFromDraft($publishedContent->versionInfo, $languageCode); + } + + /** + * Test deleting a Translation from Draft throws UnauthorizedException if user cannot edit Content. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslationFromDraft + */ + public function testDeleteTranslationFromDraftThrowsUnauthorizedException() + { + $languageCode = self::ENG_GB; + $content = $this->createMultipleLanguageContentVersion2(); + $draft = $this->contentService->createContentDraft($content->contentInfo); + + // create user that can read/create/delete but cannot edit or content + $this->createRoleWithPolicies('Writer', [ + ['module' => 'content', 'function' => 'read'], + ['module' => 'content', 'function' => 'versionread'], + ['module' => 'content', 'function' => 'create'], + ['module' => 'content', 'function' => 'delete'], + ]); + $writerUser = $this->createCustomUserWithLogin( + 'user', + 'user@example.com', + self::WRITERS_USER_GROUP_NAME, + 'Writer' + ); + $this->permissionResolver->setCurrentUserReference($writerUser); + + $this->expectException(UnauthorizedException::class); + $this->expectExceptionMessage('The User does not have the \'edit\' \'content\' permission'); + + $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode); + } + + /** + * Test deleting a non-existent Translation from Draft throws InvalidArgumentException. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslationFromDraft + */ + public function testDeleteTranslationFromDraftThrowsInvalidArgumentException() + { + $languageCode = self::GER_DE; + $content = $this->createMultipleLanguageContentVersion2(); + $draft = $this->contentService->createContentDraft($content->contentInfo); + $this->expectException(APIInvalidArgumentException::class); + $this->expectExceptionMessageMatches('/The version \(ContentId=\d+, VersionNo=\d+\) is not translated into ger-DE/'); + $this->contentService->deleteTranslationFromDraft($draft->versionInfo, $languageCode); + } + + /** + * Test loading list of Content items. + */ + public function testLoadContentListByContentInfo() + { + $allLocationsCount = $this->locationService->getAllLocationsCount(); + $contentInfoList = array_map( + static function (Location $location) { + return $location->contentInfo; + }, + $this->locationService->loadAllLocations(0, $allLocationsCount) + ); + + $contentList = $this->contentService->loadContentListByContentInfo($contentInfoList); + self::assertCount(count($contentInfoList), $contentList); + foreach ($contentList as $content) { + try { + $loadedContent = $this->contentService->loadContent($content->id); + self::assertEquals($loadedContent, $content, "Failed to properly bulk-load Content {$content->id}"); + } catch (NotFoundException $e) { + self::fail("Failed to load Content {$content->id}: {$e->getMessage()}"); + } catch (UnauthorizedException $e) { + self::fail("Failed to load Content {$content->id}: {$e->getMessage()}"); + } + } + } + + /** + * Test loading content versions after removing exactly two drafts. + * + * @see https://issues.ibexa.co/browse/EZP-30271 + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteVersion + */ + public function testLoadVersionsAfterDeletingTwoDrafts() + { + $content = $this->createFolder([self::ENG_GB => 'Foo'], 2); + + // First update and publish + $modifiedContent = $this->updateFolder($content, [self::ENG_GB => 'Foo1']); + $content = $this->contentService->publishVersion($modifiedContent->versionInfo); + + // Second update and publish + $modifiedContent = $this->updateFolder($content, [self::ENG_GB => 'Foo2']); + $content = $this->contentService->publishVersion($modifiedContent->versionInfo); + + // Create drafts + $this->updateFolder($content, [self::ENG_GB => 'Foo3']); + $this->updateFolder($content, [self::ENG_GB => 'Foo4']); + + $versions = $this->contentService->loadVersions($content->contentInfo); + + foreach ($versions as $key => $version) { + if ($version->isDraft()) { + $this->contentService->deleteVersion($version); + unset($versions[$key]); + } + } + + $this->assertEquals($versions, $this->contentService->loadVersions($content->contentInfo)); + } + + /** + * Tests loading list of content versions of status draft. + */ + public function testLoadVersionsOfStatusDraft() + { + $content = $this->createContentVersion1(); + + $this->contentService->createContentDraft($content->contentInfo); + $this->contentService->createContentDraft($content->contentInfo); + $this->contentService->createContentDraft($content->contentInfo); + + $versions = $this->contentService->loadVersions($content->contentInfo, VersionInfo::STATUS_DRAFT); + + $this->assertSame(\count($versions), 3); + } + + /** + * Tests loading list of content versions of status archived. + */ + public function testLoadVersionsOfStatusArchived() + { + $content = $this->createContentVersion1(); + + $draft1 = $this->contentService->createContentDraft($content->contentInfo); + $this->contentService->publishVersion($draft1->versionInfo); + + $draft2 = $this->contentService->createContentDraft($content->contentInfo); + $this->contentService->publishVersion($draft2->versionInfo); + + $versions = $this->contentService->loadVersions($content->contentInfo, VersionInfo::STATUS_ARCHIVED); + + $this->assertSame(\count($versions), 2); + } + + /** + * Asserts that all aliases defined in $expectedAliasProperties with the + * given properties are available in $actualAliases and not more. + * + * @param array $expectedAliasProperties + * @param array $actualAliases + */ + private function assertAliasesCorrect(array $expectedAliasProperties, array $actualAliases) + { + foreach ($actualAliases as $actualAlias) { + if (!isset($expectedAliasProperties[$actualAlias->path])) { + $this->fail( + sprintf( + 'Alias with path "%s" in languages "%s" not expected.', + $actualAlias->path, + implode(', ', $actualAlias->languageCodes) + ) + ); + } + + foreach ($expectedAliasProperties[$actualAlias->path] as $propertyName => $propertyValue) { + $this->assertEquals( + $propertyValue, + $actualAlias->$propertyName, + sprintf( + 'Property $%s incorrect for alias with path "%s" in languages "%s".', + $propertyName, + $actualAlias->path, + implode(', ', $actualAlias->languageCodes) + ) + ); + } + + unset($expectedAliasProperties[$actualAlias->path]); + } + + if (!empty($expectedAliasProperties)) { + $this->fail( + sprintf( + 'Missing expected aliases with paths "%s".', + implode('", "', array_keys($expectedAliasProperties)) + ) + ); + } + } + + /** + * Asserts that the given fields are equal to the default fields fixture. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $fields + */ + private function assertAllFieldsEquals(array $fields) + { + $actual = $this->normalizeFields($fields); + $expected = $this->normalizeFields($this->createFieldsFixture()); + + $this->assertEquals($expected, $actual); + } + + /** + * Asserts that the given fields are equal to a language filtered set of the + * default fields fixture. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $fields + * @param string $languageCode + */ + private function assertLocaleFieldsEquals(array $fields, $languageCode) + { + $actual = $this->normalizeFields($fields); + + $expected = []; + foreach ($this->normalizeFields($this->createFieldsFixture()) as $field) { + if ($field->languageCode !== $languageCode) { + continue; + } + $expected[] = $field; + } + + $this->assertEquals($expected, $actual); + } + + /** + * This method normalizes a set of fields and returns a normalized set. + * + * Normalization means it resets the storage specific field id to zero and + * it sorts the field by their identifier and their language code. In + * addition, the field value is removed, since this one depends on the + * specific FieldType, which is tested in a dedicated integration test. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $fields + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field[] + */ + private function normalizeFields(array $fields) + { + $normalized = []; + foreach ($fields as $field) { + $normalized[] = new Field( + [ + 'id' => 0, + 'value' => $field->value !== null, + 'languageCode' => $field->languageCode, + 'fieldDefIdentifier' => $field->fieldDefIdentifier, + 'fieldTypeIdentifier' => $field->fieldTypeIdentifier, + ] + ); + } + usort( + $normalized, + static function ($field1, $field2) { + if (0 === ($return = strcasecmp($field1->fieldDefIdentifier, $field2->fieldDefIdentifier))) { + return strcasecmp($field1->languageCode, $field2->languageCode); + } + + return $return; + } + ); + + return $normalized; + } + + /** + * Asserts that given Content has default ContentStates. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + */ + private function assertDefaultContentStates(ContentInfo $contentInfo) + { + $objectStateService = $this->getRepository()->getObjectStateService(); + + $objectStateGroups = $objectStateService->loadObjectStateGroups(); + + foreach ($objectStateGroups as $objectStateGroup) { + $contentState = $objectStateService->getContentState($contentInfo, $objectStateGroup); + foreach ($objectStateService->loadObjectStates($objectStateGroup, Language::ALL) as $objectState) { + // Only check the first object state which is the default one. + $this->assertEquals( + $objectState, + $contentState + ); + break; + } + } + } + + /** + * Assert that given Content has no references to a translation specified by the $languageCode. + * + * @param string $languageCode + * @param int $contentId + */ + private function assertTranslationDoesNotExist($languageCode, $contentId) + { + $content = $this->contentService->loadContent($contentId); + + foreach ($content->fields as $field) { + /** @var array $field */ + self::assertArrayNotHasKey($languageCode, $field); + self::assertNotEquals($languageCode, $content->contentInfo->mainLanguageCode); + self::assertArrayNotHasKey($languageCode, $content->versionInfo->getNames()); + self::assertNotEquals($languageCode, $content->versionInfo->initialLanguageCode); + self::assertNotContains($languageCode, $content->versionInfo->languageCodes); + } + foreach ($this->contentService->loadVersions($content->contentInfo) as $versionInfo) { + self::assertArrayNotHasKey($languageCode, $versionInfo->getNames()); + self::assertNotEquals($languageCode, $versionInfo->contentInfo->mainLanguageCode); + self::assertNotEquals($languageCode, $versionInfo->initialLanguageCode); + self::assertNotContains($languageCode, $versionInfo->languageCodes); + } + } + + /** + * Returns the default fixture of fields used in most tests. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field[] + */ + private function createFieldsFixture() + { + return [ + new Field( + [ + 'id' => 0, + 'value' => 'An awesome multi-lang forum²', + 'languageCode' => self::ENG_US, + 'fieldDefIdentifier' => 'name', + 'fieldTypeIdentifier' => 'ezstring', + ] + ), + new Field( + [ + 'id' => 0, + 'value' => 'An awesome multi-lang forum²³', + 'languageCode' => self::ENG_GB, + 'fieldDefIdentifier' => 'name', + 'fieldTypeIdentifier' => 'ezstring', + ] + ), + ]; + } + + /** + * Gets expected property values for the "Media" ContentInfo ValueObject. + * + * @return array + */ + private function getExpectedMediaContentInfoProperties() + { + return [ + 'id' => self::MEDIA_CONTENT_ID, + 'contentTypeId' => 1, + 'name' => 'Media', + 'sectionId' => 3, + 'currentVersionNo' => 1, + 'published' => true, + 'ownerId' => 14, + 'modificationDate' => $this->createDateTime(1060695457), + 'publishedDate' => $this->createDateTime(1060695457), + 'alwaysAvailable' => 1, + 'remoteId' => self::MEDIA_REMOTE_ID, + 'mainLanguageCode' => self::ENG_US, + 'mainLocationId' => 43, + 'status' => ContentInfo::STATUS_PUBLISHED, + ]; + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentService::hideContent + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testHideContent(): void + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + + $locationCreateStructs = array_map( + function (Location $parentLocation) { + return $this->locationService->newLocationCreateStruct($parentLocation->id); + }, + $this->createParentLocationsForHideReveal(2) + ); + + $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + $contentCreate->setField('name', 'Folder to hide'); + + $content = $this->contentService->createContent( + $contentCreate, + $locationCreateStructs + ); + + $publishedContent = $this->contentService->publishVersion($content->versionInfo); + $locations = $this->locationService->loadLocations($publishedContent->contentInfo); + + // Sanity check + $this->assertCount(3, $locations); + $this->assertCount(0, $this->filterHiddenLocations($locations)); + + $this->contentService->hideContent($publishedContent->contentInfo); + + $locations = $this->locationService->loadLocations($publishedContent->contentInfo); + $this->assertCount(3, $locations); + $this->assertCount(3, $this->filterHiddenLocations($locations)); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentService::revealContent + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testRevealContent() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + + $locationCreateStructs = array_map( + function (Location $parentLocation) { + return $this->locationService->newLocationCreateStruct($parentLocation->id); + }, + $this->createParentLocationsForHideReveal(2) + ); + + $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + $contentCreate->setField('name', 'Folder to hide'); + + $locationCreateStructs[0]->hidden = true; + + $content = $this->contentService->createContent( + $contentCreate, + $locationCreateStructs + ); + + $publishedContent = $this->contentService->publishVersion($content->versionInfo); + $locations = $this->locationService->loadLocations($publishedContent->contentInfo); + + // Sanity check + $hiddenLocations = $this->filterHiddenLocations($locations); + $this->assertCount(3, $locations); + $this->assertCount(1, $hiddenLocations); + + $this->contentService->hideContent($publishedContent->contentInfo); + $this->assertCount( + 3, + $this->filterHiddenLocations( + $this->locationService->loadLocations($publishedContent->contentInfo) + ) + ); + + $this->contentService->revealContent($publishedContent->contentInfo); + + $locations = $this->locationService->loadLocations($publishedContent->contentInfo); + $hiddenLocationsAfterReveal = $this->filterHiddenLocations($locations); + $this->assertCount(3, $locations); + $this->assertCount(1, $hiddenLocationsAfterReveal); + $this->assertEquals($hiddenLocations, $hiddenLocationsAfterReveal); + } + + /** + * @depends testRevealContent + */ + public function testRevealContentWithHiddenParent() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + + $contentNames = [ + 'Parent Content', + 'Child (Nesting 1)', + 'Child (Nesting 2)', + 'Child (Nesting 3)', + 'Child (Nesting 4)', + ]; + + $parentLocation = $this->locationService->newLocationCreateStruct( + $this->generateId('location', 2) + ); + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content[] $contents */ + $contents = []; + + foreach ($contentNames as $contentName) { + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + $contentCreate->setField('name', $contentName); + + $content = $this->contentService->createContent($contentCreate, [$parentLocation]); + $contents[] = $publishedContent = $this->contentService->publishVersion($content->versionInfo); + + $parentLocation = $this->locationService->newLocationCreateStruct( + $this->generateId('location', $publishedContent->contentInfo->mainLocationId) + ); + } + + $this->contentService->hideContent($contents[0]->contentInfo); + $this->contentService->hideContent($contents[2]->contentInfo); + $this->contentService->revealContent($contents[2]->contentInfo); + + $parentContent = $this->contentService->loadContent($contents[0]->id); + $parentLocation = $this->locationService->loadLocation($parentContent->contentInfo->mainLocationId); + $parentSublocations = $this->locationService->loadLocationList([ + $contents[1]->contentInfo->mainLocationId, + $contents[2]->contentInfo->mainLocationId, + $contents[3]->contentInfo->mainLocationId, + $contents[4]->contentInfo->mainLocationId, + ]); + + // Parent remains invisible + self::assertTrue($parentLocation->invisible); + + // All parent sublocations remain invisible as well + foreach ($parentSublocations as $parentSublocation) { + self::assertTrue($parentSublocation->invisible); + } + } + + /** + * @depends testRevealContent + */ + public function testRevealContentWithHiddenChildren() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + + $contentNames = [ + 'Parent Content', + 'Child (Nesting 1)', + 'Child (Nesting 2)', + 'Child (Nesting 3)', + 'Child (Nesting 4)', + ]; + + $parentLocation = $this->locationService->newLocationCreateStruct( + $this->generateId('location', 2) + ); + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content[] $contents */ + $contents = []; + + foreach ($contentNames as $contentName) { + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + $contentCreate->setField('name', $contentName); + + $content = $this->contentService->createContent($contentCreate, [$parentLocation]); + $contents[] = $publishedContent = $this->contentService->publishVersion($content->versionInfo); + + $parentLocation = $this->locationService->newLocationCreateStruct( + $this->generateId('location', $publishedContent->contentInfo->mainLocationId) + ); + } + + $this->contentService->hideContent($contents[0]->contentInfo); + $this->contentService->hideContent($contents[2]->contentInfo); + $this->contentService->revealContent($contents[0]->contentInfo); + + $directChildContent = $this->contentService->loadContent($contents[1]->id); + $directChildLocation = $this->locationService->loadLocation($directChildContent->contentInfo->mainLocationId); + + $childContent = $this->contentService->loadContent($contents[2]->id); + $childLocation = $this->locationService->loadLocation($childContent->contentInfo->mainLocationId); + $childSublocations = $this->locationService->loadLocationList([ + $contents[3]->contentInfo->mainLocationId, + $contents[4]->contentInfo->mainLocationId, + ]); + + // Direct child content is not hidden + self::assertFalse($directChildContent->contentInfo->isHidden); + + // Direct child content location is still invisible + self::assertFalse($directChildLocation->invisible); + + // Child content is still hidden + self::assertTrue($childContent->contentInfo->isHidden); + + // Child content location is still invisible + self::assertTrue($childLocation->invisible); + + // All childs sublocations remain invisible as well + foreach ($childSublocations as $childSublocation) { + self::assertTrue($childSublocation->invisible); + } + } + + public function testHideContentWithParentLocation() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + + $contentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + $contentCreate->setField('name', 'Parent'); + + $content = $this->contentService->createContent( + $contentCreate, + [ + $this->locationService->newLocationCreateStruct( + $this->generateId('location', 2) + ), + ] + ); + + $publishedContent = $this->contentService->publishVersion($content->versionInfo); + + $this->contentService->hideContent($publishedContent->contentInfo); + + $locations = $this->locationService->loadLocations($publishedContent->contentInfo); + + $childContentCreate = $this->contentService->newContentCreateStruct($contentType, self::ENG_US); + $childContentCreate->setField('name', 'Child'); + + $childContent = $this->contentService->createContent( + $childContentCreate, + [ + $this->locationService->newLocationCreateStruct( + $locations[0]->id + ), + ] + ); + + $publishedChildContent = $this->contentService->publishVersion($childContent->versionInfo); + + $childLocations = $this->locationService->loadLocations($publishedChildContent->contentInfo); + + $this->assertTrue($locations[0]->hidden); + $this->assertTrue($locations[0]->invisible); + + $this->assertFalse($childLocations[0]->hidden); + $this->assertTrue($childLocations[0]->invisible); + } + + public function testChangeContentName() + { + $contentDraft = $this->createContentDraft( + 'folder', + $this->generateId('location', 2), + [ + 'name' => 'Marco', + ] + ); + + $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo); + $contentMetadataUpdateStruct = new ContentMetadataUpdateStruct([ + 'name' => 'Polo', + ]); + $this->contentService->updateContentMetadata($publishedContent->contentInfo, $contentMetadataUpdateStruct); + + $updatedContent = $this->contentService->loadContent($publishedContent->id); + + $this->assertEquals('Marco', $publishedContent->contentInfo->name); + $this->assertEquals('Polo', $updatedContent->contentInfo->name); + } + + public function testCopyTranslationsFromPublishedToDraft() + { + $contentDraft = $this->createContentDraft( + 'folder', + $this->generateId('location', 2), + [ + 'name' => 'Folder US', + ] + ); + + $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo); + + $deDraft = $this->contentService->createContentDraft($publishedContent->contentInfo); + + $contentUpdateStruct = new ContentUpdateStruct([ + 'initialLanguageCode' => self::GER_DE, + 'fields' => $contentDraft->getFields(), + ]); + + $contentUpdateStruct->setField('name', 'Folder GER', self::GER_DE); + + $deContent = $this->contentService->updateContent($deDraft->versionInfo, $contentUpdateStruct); + + $updatedContent = $this->contentService->loadContent($deContent->id, null, $deContent->versionInfo->versionNo); + $this->assertEquals( + [ + self::ENG_US => 'Folder US', + self::GER_DE => 'Folder GER', + ], + $updatedContent->fields['name'] + ); + + $gbDraft = $this->contentService->createContentDraft($publishedContent->contentInfo); + + $contentUpdateStruct = new ContentUpdateStruct([ + 'initialLanguageCode' => self::ENG_GB, + 'fields' => $contentDraft->getFields(), + ]); + + $contentUpdateStruct->setField('name', 'Folder GB', self::ENG_GB); + + $gbContent = $this->contentService->updateContent($gbDraft->versionInfo, $contentUpdateStruct); + $this->contentService->publishVersion($gbDraft->versionInfo); + $updatedContent = $this->contentService->loadContent($gbContent->id, null, $gbContent->versionInfo->versionNo); + $this->assertEquals( + [ + self::ENG_US => 'Folder US', + self::ENG_GB => 'Folder GB', + ], + $updatedContent->fields['name'] + ); + + $dePublished = $this->contentService->publishVersion($deDraft->versionInfo); + $this->assertEquals( + [ + self::ENG_US => 'Folder US', + self::GER_DE => 'Folder GER', + self::ENG_GB => 'Folder GB', + ], + $dePublished->fields['name'] + ); + } + + public function testCopyTranslationsFromInvalidPublishedContentToDraft() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + + // Create content type for testing + $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct('test_copy_translation'); + $contentTypeCreateStruct->mainLanguageCode = 'eng-US'; + $contentTypeCreateStruct->names = ['eng-US' => 'Test content type for Copy Translations']; + $fieldDefinition = $contentTypeService->newFieldDefinitionCreateStruct('name', 'ezstring'); + $fieldDefinition->position = 1; + $contentTypeCreateStruct->addFieldDefinition($fieldDefinition); + $contentTypeService->publishContentTypeDraft( + $contentTypeService->createContentType( + $contentTypeCreateStruct, + [$contentTypeService->loadContentTypeGroupByIdentifier('Content')] + ) + ); + $contentType = $contentTypeService->loadContentTypeByIdentifier('test_copy_translation'); + + // Create entry content + $contentDraft = $this->createContentDraft( + 'test_copy_translation', + $this->generateId('location', 2), + [ + 'name' => 'Folder US', + ] + ); + $publishedContent = $this->contentService->publishVersion($contentDraft->versionInfo); + + // Create translation draft that would act as an OLD version + $deDraft = $this->contentService->createContentDraft($publishedContent->contentInfo); + $contentUpdateStruct = new ContentUpdateStruct([ + 'initialLanguageCode' => self::GER_DE, + 'fields' => $contentDraft->getFields(), + ]); + + $contentUpdateStruct->setField('name', 'Folder GER', self::GER_DE); + $deContent = $this->contentService->updateContent($deDraft->versionInfo, $contentUpdateStruct); + + // Update published version, as copying is only done when there is a diff between published and draft + $gbDraft = $this->contentService->createContentDraft($publishedContent->contentInfo); + $contentUpdateStruct = new ContentUpdateStruct([ + 'initialLanguageCode' => self::ENG_US, + ]); + $contentUpdateStruct->setField('name', 'Folder US 2', self::ENG_US); + + $gbContent = $this->contentService->updateContent($gbDraft->versionInfo, $contentUpdateStruct); + $this->contentService->publishVersion($gbContent->versionInfo); + + // Update content type with new required field + $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); + $fieldDefinition = $contentTypeService->newFieldDefinitionCreateStruct('req_field', 'ezstring'); + $fieldDefinition->position = 2; + $fieldDefinition->isRequired = true; + $contentTypeService->addFieldDefinition($contentTypeDraft, $fieldDefinition); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + // Reload previous german draft, it is now in invalid state for both ENG_US and GER_DE + $invalidContentDraft = $this->contentService->loadContent($deContent->id, null, $deContent->versionInfo->versionNo); + $contentUpdateStruct = new ContentUpdateStruct([ + 'initialLanguageCode' => self::GER_DE, + ]); + $contentUpdateStruct->setField('req_field', 'Required field DE', self::GER_DE); + + $this->contentService->updateContent($invalidContentDraft->versionInfo, $contentUpdateStruct); + $this->contentService->publishVersion($invalidContentDraft->versionInfo, [self::GER_DE]); + + $publishedContent = $this->contentService->loadContent($deContent->id, null, $deContent->versionInfo->versionNo); + + $this->assertEquals( + [ + self::GER_DE => 'Folder GER', + self::ENG_US => 'Folder US 2', + ], + $publishedContent->fields['name'] + ); + // Missing values were copied from last updated draft + $this->assertEquals( + [ + self::GER_DE => 'Required field DE', + self::ENG_US => 'Required field DE', + ], + $publishedContent->fields['req_field'] + ); + } + + /** + * Create structure of parent folders with Locations to be used for Content hide/reveal tests. + * + * @param int $parentLocationId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location[] A list of Locations aimed to be parents + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function createParentLocationsForHideReveal(int $parentLocationId): array + { + $parentFoldersLocationsIds = [ + $this->createFolder([self::ENG_US => 'P1'], $parentLocationId)->contentInfo->mainLocationId, + $this->createFolder([self::ENG_US => 'P2'], $parentLocationId)->contentInfo->mainLocationId, + $this->createFolder([self::ENG_US => 'P3'], $parentLocationId)->contentInfo->mainLocationId, + ]; + + return array_values($this->locationService->loadLocationList($parentFoldersLocationsIds)); + } + + /** + * Filter Locations list by hidden only. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location[] $locations + * + * @return array + */ + private function filterHiddenLocations(array $locations): array + { + return array_values( + array_filter( + $locations, + static function (Location $location) { + return $location->hidden; + } + ) + ); + } + + public function testPublishVersionWithSelectedLanguages() + { + $publishedContent = $this->createFolder( + [ + self::ENG_US => 'Published US', + self::GER_DE => 'Published DE', + ], + $this->generateId('location', 2) + ); + + $draft = $this->contentService->createContentDraft($publishedContent->contentInfo); + $contentUpdateStruct = new ContentUpdateStruct([ + 'initialLanguageCode' => self::ENG_US, + ]); + $contentUpdateStruct->setField('name', 'Draft 1 US', self::ENG_US); + $contentUpdateStruct->setField('name', 'Draft 1 DE', self::GER_DE); + + $this->contentService->updateContent($draft->versionInfo, $contentUpdateStruct); + $this->contentService->publishVersion($draft->versionInfo, ['ger-DE']); + $content = $this->contentService->loadContent($draft->contentInfo->id); + + $this->assertEquals( + [ + self::ENG_US => 'Published US', + self::GER_DE => 'Draft 1 DE', + ], + $content->fields['name'] + ); + } + + public function testCreateContentWithRomanianSpecialCharsInTitle() + { + $baseName = 'ȘșțȚdfdf'; + $expectedPath = '/SstTdfdf'; + + $this->createFolder([self::ENG_US => $baseName], 2); + + $urlAliasService = $this->getRepository()->getURLAliasService(); + $urlAlias = $urlAliasService->lookup($expectedPath); + $this->assertSame($expectedPath, $urlAlias->path); + } + + /** + * @param int $amountOfDrafts + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function createContentDrafts(int $amountOfDrafts): void + { + if (0 >= $amountOfDrafts) { + throw new InvalidArgumentException('$amountOfDrafts', 'Must be greater then 0'); + } + + $publishedContent = $this->createContentVersion1(); + + for ($i = 1; $i <= $amountOfDrafts; ++$i) { + $this->contentService->createContentDraft($publishedContent->contentInfo); + } + } + + /** + * @param array $limitationValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function createUserWithVersionReadLimitations(array $limitationValues = []): User + { + $limitations = [ + new LocationLimitation(['limitationValues' => $limitationValues]), + ]; + + return $this->createUserWithPolicies( + 'user', + [ + ['module' => 'content', 'function' => 'versionread', 'limitations' => $limitations], + ['module' => 'content', 'function' => 'create'], + ['module' => 'content', 'function' => 'read'], + ['module' => 'content', 'function' => 'edit'], + ] + ); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content[] $drafts + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * + * @return object + */ + private function createContentWithReverseRelations(array $drafts) + { + $contentWithReverseRelations = new class() { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content */ + public $content; + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content[] */ + public $reverseRelations; + }; + $content = $this->createContentVersion1(); + $versionInfo = $content->getVersionInfo(); + $contentInfo = $versionInfo->getContentInfo(); + $contentWithReverseRelations->content = $content; + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $draft */ + foreach ($drafts as $draft) { + $this->contentService->addRelation( + $draft->getVersionInfo(), + $contentInfo + ); + + $contentWithReverseRelations->reverseRelations[] = $this->contentService->publishVersion($draft->getVersionInfo()); + } + + return $contentWithReverseRelations; + } + + private function createContentWithRelations(): Content + { + $draft = $this->createContentDraftVersion1(); + $versionInfo = $draft->getVersionInfo(); + + $media = $this->contentService->loadContentInfoByRemoteId(self::MEDIA_REMOTE_ID); + $demoDesign = $this->contentService->loadContentInfoByRemoteId(self::DEMO_DESIGN_REMOTE_ID); + $this->contentService->addRelation( + $versionInfo, + $media + ); + + $this->contentService->addRelation( + $versionInfo, + $demoDesign + ); + + return $draft; + } +} + +class_alias(ContentServiceTest::class, 'eZ\Publish\API\Repository\Tests\ContentServiceTest'); diff --git a/eZ/Publish/API/Repository/Tests/ContentTypeServiceAuthorizationTest.php b/tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php similarity index 83% rename from eZ/Publish/API/Repository/Tests/ContentTypeServiceAuthorizationTest.php rename to tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php index 8be9e9c24c..7968a503b1 100644 --- a/eZ/Publish/API/Repository/Tests/ContentTypeServiceAuthorizationTest.php +++ b/tests/integration/Core/Repository/ContentTypeServiceAuthorizationTest.php @@ -4,13 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; /** * Test case for operations in the ContentTypeServiceAuthorization using in memory storage. * - * @see eZ\Publish\API\Repository\ContentTypeService - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUser + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUser * @group integration * @group authorization */ @@ -19,12 +21,12 @@ class ContentTypeServiceAuthorizationTest extends BaseContentTypeServiceTest /** * Test for the createContentTypeGroup() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentTypeGroup + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentTypeGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentTypeGroup */ public function testCreateContentTypeGroupThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -32,7 +34,7 @@ public function testCreateContentTypeGroupThrowsUnauthorizedException() $creatorId = $this->generateId('user', 14); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $contentTypeService = $repository->getContentTypeService(); @@ -60,12 +62,12 @@ public function testCreateContentTypeGroupThrowsUnauthorizedException() /** * Test for the updateContentTypeGroup() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUpdateContentTypeGroup + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testUpdateContentTypeGroup */ public function testUpdateContentTypeGroupThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -73,7 +75,7 @@ public function testUpdateContentTypeGroupThrowsUnauthorizedException() $modifierId = $this->generateId('user', 42); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $contentTypeService = $repository->getContentTypeService(); @@ -110,19 +112,19 @@ public function testUpdateContentTypeGroupThrowsUnauthorizedException() /** * Test for the deleteContentTypeGroup() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::deleteContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testDeleteContentTypeGroup + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::deleteContentTypeGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testDeleteContentTypeGroup */ public function testDeleteContentTypeGroupThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $contentTypeService = $repository->getContentTypeService(); @@ -147,12 +149,12 @@ public function testDeleteContentTypeGroupThrowsUnauthorizedException() /** * Test for the createContentType() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentType + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentType() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentType */ public function testCreateContentTypeThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -160,7 +162,7 @@ public function testCreateContentTypeThrowsUnauthorizedException() $creatorId = $this->generateId('user', 14); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $contentTypeService = $repository->getContentTypeService(); @@ -204,12 +206,12 @@ public function testCreateContentTypeThrowsUnauthorizedException() /** * Test for the updateContentTypeDraft() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUpdateContentTypeDraft + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeDraft() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testUpdateContentTypeDraft */ public function testUpdateContentTypeDraftThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $contentTypeService = $repository->getContentTypeService(); @@ -218,7 +220,7 @@ public function testUpdateContentTypeDraftThrowsUnauthorizedException() $modifierId = $this->generateId('user', 42); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $contentTypeDraft = $this->createContentTypeDraft(); @@ -256,12 +258,12 @@ public function testUpdateContentTypeDraftThrowsUnauthorizedException() /** * Test for the addFieldDefinition() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::addFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testAddFieldDefinition + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::addFieldDefinition() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testAddFieldDefinition */ public function testAddFieldDefinitionThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $contentTypeService = $repository->getContentTypeService(); @@ -269,7 +271,7 @@ public function testAddFieldDefinitionThrowsUnauthorizedException() $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $contentTypeDraft = $this->createContentTypeDraft(); @@ -312,12 +314,12 @@ public function testAddFieldDefinitionThrowsUnauthorizedException() /** * Test for the removeFieldDefinition() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::removeFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testRemoveFieldDefinition + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::removeFieldDefinition() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testRemoveFieldDefinition */ public function testRemoveFieldDefinitionThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $contentTypeService = $repository->getContentTypeService(); @@ -325,7 +327,7 @@ public function testRemoveFieldDefinitionThrowsUnauthorizedException() $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $contentTypeDraft = $this->createContentTypeDraft(); @@ -345,12 +347,12 @@ public function testRemoveFieldDefinitionThrowsUnauthorizedException() /** * Test for the updateFieldDefinition() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::updateFieldDefinition() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUpdateFieldDefinition + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateFieldDefinition() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testUpdateFieldDefinition */ public function testUpdateFieldDefinitionThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $contentTypeService = $repository->getContentTypeService(); @@ -358,7 +360,7 @@ public function testUpdateFieldDefinitionThrowsUnauthorizedException() $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $contentTypeDraft = $this->createContentTypeDraft(); @@ -403,12 +405,12 @@ public function testUpdateFieldDefinitionThrowsUnauthorizedException() /** * Test for the publishContentTypeDraft() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::publishContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testPublishContentTypeDraft + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::publishContentTypeDraft() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testPublishContentTypeDraft */ public function testPublishContentTypeDraftThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $contentTypeService = $repository->getContentTypeService(); @@ -416,7 +418,7 @@ public function testPublishContentTypeDraftThrowsUnauthorizedException() $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $contentTypeDraft = $this->createContentTypeDraft(); @@ -434,19 +436,19 @@ public function testPublishContentTypeDraftThrowsUnauthorizedException() /** * Test for the createContentTypeDraft() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::createContentTypeDraft() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCreateContentTypeDraft + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentTypeDraft() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentTypeDraft */ public function testCreateContentTypeDraftThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $contentTypeService = $repository->getContentTypeService(); @@ -466,19 +468,19 @@ public function testCreateContentTypeDraftThrowsUnauthorizedException() /** * Test for the deleteContentType() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::deleteContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testDeleteContentType + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::deleteContentType() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testDeleteContentType */ public function testDeleteContentTypeThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $contentTypeService = $repository->getContentTypeService(); @@ -498,19 +500,19 @@ public function testDeleteContentTypeThrowsUnauthorizedException() /** * Test for the copyContentType() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::copyContentType() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testCopyContentType + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::copyContentType() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCopyContentType */ public function testCopyContentTypeThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $contentTypeService = $repository->getContentTypeService(); @@ -530,19 +532,19 @@ public function testCopyContentTypeThrowsUnauthorizedException() /** * Test for the assignContentTypeGroup() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::assignContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testAssignContentTypeGroup + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::assignContentTypeGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testAssignContentTypeGroup */ public function testAssignContentTypeGroupThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $contentTypeService = $repository->getContentTypeService(); @@ -563,19 +565,19 @@ public function testAssignContentTypeGroupThrowsUnauthorizedException() /** * Test for the unassignContentTypeGroup() method. * - * @see \eZ\Publish\API\Repository\ContentTypeService::unassignContentTypeGroup() - * @depends eZ\Publish\API\Repository\Tests\ContentTypeServiceTest::testUnassignContentTypeGroup + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::unassignContentTypeGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testUnassignContentTypeGroup */ public function testUnassignContentTypeGroupThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $contentTypeService = $repository->getContentTypeService(); @@ -598,3 +600,5 @@ public function testUnassignContentTypeGroupThrowsUnauthorizedException() /* END: Use Case */ } } + +class_alias(ContentTypeServiceAuthorizationTest::class, 'eZ\Publish\API\Repository\Tests\ContentTypeServiceAuthorizationTest'); diff --git a/tests/integration/Core/Repository/ContentTypeServiceTest.php b/tests/integration/Core/Repository/ContentTypeServiceTest.php new file mode 100644 index 0000000000..d5b47f6b9a --- /dev/null +++ b/tests/integration/Core/Repository/ContentTypeServiceTest.php @@ -0,0 +1,4550 @@ +getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct( + 'new-group' + ); + /* END: Use Case */ + + $this->assertInstanceOf( + ContentTypeGroupCreateStruct::class, + $groupCreate + ); + + return $groupCreate; + } + + /** + * Test for the newContentTypeGroupCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::newContentTypeGroupCreateStruct() + * @depends testNewContentTypeGroupCreateStruct + */ + public function testNewContentTypeGroupCreateStructValues($createStruct) + { + $this->assertPropertiesCorrect( + [ + 'identifier' => 'new-group', + 'creatorId' => null, + 'creationDate' => null, + /* @todo uncomment when support for multilingual names and descriptions is added + 'mainLanguageCode' => null, + */ + ], + $createStruct + ); + } + + /** + * Test for the createContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentTypeGroup() + * @depends testNewContentTypeGroupCreateStruct + * @group user + */ + public function testCreateContentTypeGroup() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + $permissionResolver = $repository->getPermissionResolver(); + + $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct( + 'new-group' + ); + $groupCreate->creatorId = $this->generateId('user', $permissionResolver->getCurrentUserReference()->getUserId()); + $groupCreate->creationDate = $this->createDateTime(); + /* @todo uncomment when support for multilingual names and descriptions is added + $groupCreate->mainLanguageCode = 'ger-DE'; + $groupCreate->names = array( 'eng-GB' => 'A name.' ); + $groupCreate->descriptions = array( 'eng-GB' => 'A description.' ); + */ + + $groupCreate->isSystem = true; + $group = $contentTypeService->createContentTypeGroup($groupCreate); + /* END: Use Case */ + + $this->assertInstanceOf( + ContentTypeGroup::class, + $group + ); + + $reloadedGroup = $contentTypeService->loadContentTypeGroup($group->id); + + return [ + 'createStruct' => $groupCreate, + 'group' => $reloadedGroup, + ]; + } + + /** + * Test for the createContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentTypeGroup() + * @depends testCreateContentTypeGroup + */ + public function testCreateContentTypeGroupStructValues(array $data) + { + $createStruct = $data['createStruct']; + $group = $data['group']; + + $this->assertEquals( + [ + 'identifier' => $group->identifier, + 'creatorId' => $group->creatorId, + 'creationDate' => $group->creationDate->getTimestamp(), + 'isSystem' => $group->isSystem, + ], + [ + 'identifier' => $createStruct->identifier, + 'creatorId' => $createStruct->creatorId, + 'creationDate' => $createStruct->creationDate->getTimestamp(), + 'isSystem' => $createStruct->isSystem, + ] + ); + $this->assertNotNull( + $group->id + ); + + return $data; + } + + /** + * Test for the createContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentTypeGroup() + * @depends testCreateContentTypeGroupStructValues + */ + public function testCreateContentTypeGroupStructLanguageDependentValues(array $data) + { + $createStruct = $data['createStruct']; + $group = $data['group']; + + $this->assertStructPropertiesCorrect( + $createStruct, + $group, + /* @todo uncomment when support for multilingual names and descriptions is added + array( 'names', 'descriptions', 'mainLanguageCode' ) + */ + ); + } + + /** + * Test for the createContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentTypeGroup + * @depends testCreateContentTypeGroup + */ + public function testCreateContentTypeGroupThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$contentTypeGroupCreateStruct\' is invalid: A group with the identifier \'Content\' already exists'); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct( + 'Content' + ); + + // Throws an Exception, since group "Content" already exists + $contentTypeService->createContentTypeGroup($groupCreate); + /* END: Use Case */ + } + + /** + * Test for the loadContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeGroup() + * @depends testCreateContentTypeGroup + * @group user + */ + public function testLoadContentTypeGroup() + { + $repository = $this->getRepository(); + + $contentTypeGroupId = $this->generateId('typegroup', 2); + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Loads the "Users" group + // $contentTypeGroupId is the ID of an existing content type group + $loadedGroup = $contentTypeService->loadContentTypeGroup($contentTypeGroupId); + /* END: Use Case */ + + $this->assertInstanceOf( + ContentTypeGroup::class, + $loadedGroup + ); + + return $loadedGroup; + } + + public function testLoadSystemContentTypeGroup(): void + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + + // Loads the "System" group + $systemGroup = $contentTypeService->loadContentTypeGroup($this->generateId('typegroup', 5)); + + self::assertSame( + 'System', + $systemGroup->identifier + ); + self::assertTrue( + $systemGroup->isSystem + ); + } + + /** + * Test for the loadContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeGroup() + * @depends testLoadContentTypeGroup + */ + public function testLoadContentTypeGroupStructValues(ContentTypeGroup $group) + { + $this->assertPropertiesCorrect( + [ + 'id' => $this->generateId('typegroup', 2), + 'identifier' => 'Users', + 'creationDate' => $this->createDateTime(1031216941), + 'modificationDate' => $this->createDateTime(1033922113), + 'creatorId' => $this->generateId('user', 14), + 'modifierId' => $this->generateId('user', 14), + ], + $group + ); + } + + /** + * Test for the loadContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeGroup() + */ + public function testLoadContentTypeGroupThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $contentTypeService = $repository->getContentTypeService(); + $loadedGroup = $contentTypeService->loadContentTypeGroup($this->generateId('typegroup', 2342)); + } + + /** + * Test for the loadContentTypeGroupByIdentifier() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeGroupByIdentifier() + * @group user + * @group field-type + */ + public function testLoadContentTypeGroupByIdentifier() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $loadedGroup = $contentTypeService->loadContentTypeGroupByIdentifier( + 'Media' + ); + /* END: Use Case */ + + $this->assertInstanceOf( + ContentTypeGroup::class, + $loadedGroup + ); + + return $loadedGroup; + } + + /** + * Test for the loadContentTypeGroupByIdentifier() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeGroupByIdentifier() + * @depends testLoadContentTypeGroupByIdentifier + */ + public function testLoadContentTypeGroupByIdentifierStructValues(ContentTypeGroup $group) + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $this->assertEquals( + $contentTypeService->loadContentTypeGroup($this->generateId('typegroup', 3)), + $group + ); + } + + /** + * Test for the loadContentTypeGroupByIdentifier() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeGroupByIdentifier() + * @depends testLoadContentTypeGroupByIdentifier + */ + public function testLoadContentTypeGroupByIdentifierThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Throws exception + $loadedGroup = $contentTypeService->loadContentTypeGroupByIdentifier( + 'not-exists' + ); + /* END: Use Case */ + } + + /** + * Test for the loadContentTypeGroups() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeGroups() + * @depends testCreateContentTypeGroup + */ + public function testLoadContentTypeGroups() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Loads an array with all content type groups + $loadedGroups = $contentTypeService->loadContentTypeGroups(); + /* END: Use Case */ + + self::assertIsArray($loadedGroups); + + foreach ($loadedGroups as $loadedGroup) { + $this->assertStructPropertiesCorrect( + $contentTypeService->loadContentTypeGroup($loadedGroup->id), + $loadedGroup, + [ + 'id', + 'identifier', + 'creationDate', + 'modificationDate', + 'creatorId', + 'modifierId', + 'isSystem', + ] + ); + } + + return $loadedGroups; + } + + /** + * Test for the loadContentTypeGroups() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeGroups() + * @depends testLoadContentTypeGroups + */ + public function testLoadContentTypeGroupsIdentifiers($groups) + { + $this->assertCount(4, $groups); + + $expectedIdentifiers = [ + 'Content' => true, + 'Users' => true, + 'Media' => true, + 'Setup' => true, + ]; + + $actualIdentifiers = []; + foreach ($groups as $group) { + $actualIdentifiers[$group->identifier] = true; + } + + ksort($expectedIdentifiers); + ksort($actualIdentifiers); + + $this->assertEquals( + $expectedIdentifiers, + $actualIdentifiers, + 'Identifier missmatch in loaded groups.' + ); + } + + /** + * Test for the newContentTypeGroupUpdateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::newContentTypeGroupUpdateStruct() + */ + public function testNewContentTypeGroupUpdateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $groupUpdate = $contentTypeService->newContentTypeGroupUpdateStruct(); + /* END: Use Case */ + + self::assertInstanceOf( + ContentTypeGroupUpdateStruct::class, + $groupUpdate + ); + } + + /** + * Test for the updateContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeGroup() + * @depends testCreateContentTypeGroup + */ + public function testUpdateContentTypeGroup() + { + $repository = $this->getRepository(); + + $modifierId = $this->generateId('user', 42); + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $group = $contentTypeService->loadContentTypeGroupByIdentifier('Setup'); + + $groupUpdate = $contentTypeService->newContentTypeGroupUpdateStruct(); + + $groupUpdate->identifier = 'Teardown'; + $groupUpdate->modifierId = $modifierId; + $groupUpdate->modificationDate = $this->createDateTime(); + $groupUpdate->isSystem = true; + /* @todo uncomment when support for multilingual names and descriptions is added + $groupUpdate->mainLanguageCode = 'eng-GB'; + + $groupUpdate->names = array( + 'eng-GB' => 'A name', + 'eng-US' => 'A name', + ); + $groupUpdate->descriptions = array( + 'eng-GB' => 'A description', + 'eng-US' => 'A description', + ); + */ + + $contentTypeService->updateContentTypeGroup($group, $groupUpdate); + /* END: Use Case */ + + $updatedGroup = $contentTypeService->loadContentTypeGroup($group->id); + + self::assertInstanceOf( + ContentTypeGroupUpdateStruct::class, + $groupUpdate + ); + + return [ + 'originalGroup' => $group, + 'updateStruct' => $groupUpdate, + 'updatedGroup' => $updatedGroup, + ]; + } + + /** + * Test for the updateContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeGroup() + * @depends testUpdateContentTypeGroup + */ + public function testUpdateContentTypeGroupStructValues(array $data) + { + $expectedValues = [ + 'identifier' => $data['updateStruct']->identifier, + 'creationDate' => $data['originalGroup']->creationDate, + 'modificationDate' => $data['updateStruct']->modificationDate, + 'creatorId' => $data['originalGroup']->creatorId, + 'modifierId' => $data['updateStruct']->modifierId, + 'isSystem' => $data['updateStruct']->isSystem, + ]; + + $this->assertPropertiesCorrect($expectedValues, $data['updatedGroup']); + + return $data; + } + + /** + * Test for the updateContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeGroup() + * @depends testUpdateContentTypeGroupStructValues + */ + public function testUpdateContentTypeGroupStructLanguageDependentValues(array $data) + { + $expectedValues = [ + 'identifier' => $data['updateStruct']->identifier, + 'creationDate' => $data['originalGroup']->creationDate, + 'modificationDate' => $data['updateStruct']->modificationDate, + 'creatorId' => $data['originalGroup']->creatorId, + 'modifierId' => $data['updateStruct']->modifierId, + /* @todo uncomment when support for multilingual names and descriptions is added + 'mainLanguageCode' => $data['updateStruct']->mainLanguageCode, + 'names' => $data['updateStruct']->names, + 'descriptions' => $data['updateStruct']->descriptions, + */ + ]; + + $this->assertPropertiesCorrect($expectedValues, $data['updatedGroup']); + } + + /** + * Test for the updateContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeGroup + * @depends testUpdateContentTypeGroup + */ + public function testUpdateContentTypeGroupThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$contentTypeGroupUpdateStruct->identifier\' is invalid: given identifier already exists'); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $group = $contentTypeService->loadContentTypeGroupByIdentifier( + 'Media' + ); + + $groupUpdate = $contentTypeService->newContentTypeGroupUpdateStruct(); + $groupUpdate->identifier = 'Users'; + + // Exception, because group with identifier "Users" exists + $contentTypeService->updateContentTypeGroup($group, $groupUpdate); + /* END: Use Case */ + } + + /** + * Test for the deleteContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::deleteContentTypeGroup + * @depends testLoadContentTypeGroup + */ + public function testDeleteContentTypeGroup() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct( + 'new-group' + ); + $contentTypeService->createContentTypeGroup($groupCreate); + + $group = $contentTypeService->loadContentTypeGroupByIdentifier('new-group'); + + $contentTypeService->deleteContentTypeGroup($group); + /* END: Use Case */ + + // loadContentTypeGroup should throw NotFoundException + $contentTypeService->loadContentTypeGroup($group->id); + + $this->fail('Content type group not deleted.'); + } + + public function testDeleteContentTypeGroupWithOrphanedContentTypeDrafts(): void + { + $repository = $this->getRepository(); + + $contentTypeService = $repository->getContentTypeService(); + + $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct( + 'new-group' + ); + $contentTypeService->createContentTypeGroup($groupCreate); + + $group = $contentTypeService->loadContentTypeGroupByIdentifier('new-group'); + for ($i = 0; $i < 3; ++$i) { + $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct('content_type_draft_' . $i); + $contentTypeCreateStruct->mainLanguageCode = 'eng-GB'; + $contentTypeCreateStruct->names = [ + 'eng-GB' => 'content_type_draft_' . $i, + ]; + + $contentTypeService->createContentType($contentTypeCreateStruct, [$group]); + } + + $contentTypeService->deleteContentTypeGroup($group); + + // loadContentTypeGroup should throw NotFoundException + $this->expectException(NotFoundException::class); + + $contentTypeService->loadContentTypeGroup($group->id); + } + + /** + * Test for the newContentTypeCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::newContentTypeCreateStruct() + * @group user + * @group field-type + */ + public function testNewContentTypeCreateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $typeCreate = $contentTypeService->newContentTypeCreateStruct( + 'new-type' + ); + /* END: Use Case */ + + self::assertInstanceOf( + ContentTypeCreateStruct::class, + $typeCreate + ); + + return $typeCreate; + } + + /** + * Test for the newContentTypeCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::newContentTypeCreateStruct() + * @depends testNewContentTypeCreateStruct + */ + public function testNewContentTypeCreateStructValues($createStruct) + { + $this->assertPropertiesCorrect( + [ + 'identifier' => 'new-type', + 'mainLanguageCode' => null, + 'remoteId' => null, + 'urlAliasSchema' => null, + 'nameSchema' => null, + 'isContainer' => false, + 'defaultSortField' => Location::SORT_FIELD_PUBLISHED, + 'defaultSortOrder' => Location::SORT_ORDER_DESC, + 'defaultAlwaysAvailable' => true, + 'names' => null, + 'descriptions' => null, + 'creatorId' => null, + 'creationDate' => null, + ], + $createStruct + ); + } + + /** + * Test for the newFieldDefinitionCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::newFieldDefinitionCreateStruct() + * @group user + * @group field-type + */ + public function testNewFieldDefinitionCreateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $fieldDefinitionCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); + /* END: Use Case */ + + $this->assertInstanceOf( + FieldDefinitionCreateStruct::class, + $fieldDefinitionCreate + ); + + return $fieldDefinitionCreate; + } + + /** + * Test for the newFieldDefinitionCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::newFieldDefinitionCreateStruct() + * @depends testNewFieldDefinitionCreateStruct + */ + public function testNewFieldDefinitionCreateStructValues($createStruct) + { + $this->assertPropertiesCorrect( + [ + 'fieldTypeIdentifier' => 'ezstring', + 'identifier' => 'title', + 'names' => null, + 'descriptions' => null, + 'fieldGroup' => null, + 'position' => null, + 'isTranslatable' => null, + 'isRequired' => null, + 'isInfoCollector' => null, + 'validatorConfiguration' => null, + 'fieldSettings' => null, + 'defaultValue' => null, + 'isSearchable' => null, + ], + $createStruct + ); + } + + /** + * Test for the deleteContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::deleteContentTypeGroup() + * @depends testDeleteContentTypeGroup + */ + public function testDeleteContentTypeGroupThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); + + // Throws exception, since group contains types + $contentTypeService->deleteContentTypeGroup($contentGroup); + /* END: Use Case */ + } + + /** + * Test for the createContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentType() + * @depends testNewContentTypeCreateStruct + * @depends testNewFieldDefinitionCreateStruct + * @depends testLoadContentTypeGroupByIdentifier + * @group user + * @group field-type + */ + public function testCreateContentType() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + $permissionResolver = $repository->getPermissionResolver(); + + $typeCreate = $contentTypeService->newContentTypeCreateStruct('blog-post'); + $typeCreate->mainLanguageCode = 'eng-GB'; + $typeCreate->remoteId = '384b94a1bd6bc06826410e284dd9684887bf56fc'; + $typeCreate->urlAliasSchema = 'url|scheme'; + $typeCreate->nameSchema = 'name|scheme'; + $typeCreate->names = [ + 'eng-GB' => 'Blog post', + 'ger-DE' => 'Blog-Eintrag', + ]; + $typeCreate->descriptions = [ + 'eng-GB' => 'A blog post', + 'ger-DE' => 'Ein Blog-Eintrag', + ]; + $typeCreate->creatorId = $this->generateId('user', $permissionResolver->getCurrentUserReference()->getUserId()); + $typeCreate->creationDate = $this->createDateTime(); + + $titleFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); + $titleFieldCreate->names = [ + 'eng-GB' => 'Title', + 'ger-DE' => 'Titel', + ]; + $titleFieldCreate->descriptions = [ + 'eng-GB' => 'Title of the blog post', + 'ger-DE' => 'Titel des Blog-Eintrages', + ]; + $titleFieldCreate->fieldGroup = 'blog-content'; + $titleFieldCreate->position = 1; + $titleFieldCreate->isTranslatable = true; + $titleFieldCreate->isRequired = true; + $titleFieldCreate->isInfoCollector = false; + $titleFieldCreate->validatorConfiguration = [ + 'StringLengthValidator' => [ + 'minStringLength' => 0, + 'maxStringLength' => 0, + ], + ]; + $titleFieldCreate->fieldSettings = []; + $titleFieldCreate->isSearchable = true; + $titleFieldCreate->defaultValue = 'default title'; + + $typeCreate->addFieldDefinition($titleFieldCreate); + + $bodyFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('body', 'ezstring'); + $bodyFieldCreate->names = [ + 'eng-GB' => 'Body', + 'ger-DE' => 'Textkörper', + ]; + $bodyFieldCreate->descriptions = [ + 'eng-GB' => 'Body of the blog post', + 'ger-DE' => 'Textkörper des Blog-Eintrages', + ]; + $bodyFieldCreate->fieldGroup = 'blog-content'; + $bodyFieldCreate->position = 2; + $bodyFieldCreate->isTranslatable = true; + $bodyFieldCreate->isRequired = true; + $bodyFieldCreate->isInfoCollector = false; + $bodyFieldCreate->validatorConfiguration = [ + 'StringLengthValidator' => [ + 'minStringLength' => 0, + 'maxStringLength' => 0, + ], + ]; + $bodyFieldCreate->fieldSettings = []; + $bodyFieldCreate->isSearchable = true; + $bodyFieldCreate->defaultValue = 'default content'; + + $typeCreate->addFieldDefinition($bodyFieldCreate); + + $groups = [ + $contentTypeService->loadContentTypeGroupByIdentifier('Media'), + $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), + ]; + + $contentTypeDraft = $contentTypeService->createContentType( + $typeCreate, + $groups + ); + /* END: Use Case */ + + $this->assertInstanceOf( + ContentType::class, + $contentTypeDraft + ); + + return [ + 'typeCreate' => $typeCreate, + 'contentType' => $contentTypeDraft, + 'groups' => $groups, + ]; + } + + /** + * Test for the createContentType() method struct values. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentType + * @depends testCreateContentType + * + * @param array $data + */ + public function testCreateContentTypeStructValues(array $data) + { + $typeCreate = $data['typeCreate']; + $contentType = $data['contentType']; + $groups = $data['groups']; + + foreach ($typeCreate as $propertyName => $propertyValue) { + switch ($propertyName) { + case 'fieldDefinitions': + $this->assertFieldDefinitionsCorrect( + $typeCreate->fieldDefinitions, + $contentType->fieldDefinitions->toArray() + ); + break; + + case 'creationDate': + case 'modificationDate': + $this->assertEquals( + $typeCreate->$propertyName->getTimestamp(), + $contentType->$propertyName->getTimestamp() + ); + break; + + default: + $this->assertEquals( + $typeCreate->$propertyName, + $contentType->$propertyName, + "Did not assert that property '$propertyName' is equal on struct and resulting value object" + ); + break; + } + } + + $this->assertContentTypeGroupsCorrect( + $groups, + $contentType->contentTypeGroups + ); + + $this->assertNotNull( + $contentType->id + ); + } + + /** + * Asserts field definition creation. + * + * Asserts that all field definitions defined through created structs in + * $expectedDefinitionCreates have been correctly created in + * $actualDefinitions. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct[] $expectedDefinitionCreates + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition[] $actualDefinitions + */ + protected function assertFieldDefinitionsCorrect( + array $expectedDefinitionCreates, + array $actualDefinitions + ): void { + $this->assertSameSize( + $expectedDefinitionCreates, + $actualDefinitions, + 'Count of field definition creates did not match count of field definitions.' + ); + + $sorter = static function ($a, $b) { + return strcmp($a->identifier, $b->identifier); + }; + + usort($expectedDefinitionCreates, $sorter); + usort($actualDefinitions, $sorter); + + foreach ($expectedDefinitionCreates as $key => $expectedCreate) { + $this->assertFieldDefinitionsEqual( + $expectedCreate, + $actualDefinitions[$key] + ); + } + } + + /** + * Asserts that a field definition has been correctly created. + * + * Asserts that the given $actualDefinition is correctly created from the + * create struct in $expectedCreate. + */ + protected function assertFieldDefinitionsEqual( + FieldDefinitionCreateStruct $expectedCreate, + FieldDefinition $actualDefinition + ): void { + foreach ($expectedCreate as $propertyName => $propertyValue) { + $this->assertEquals( + $expectedCreate->$propertyName, + $actualDefinition->$propertyName + ); + } + } + + /** + * Asserts that two sets of ContentTypeGroups are equal. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup[] $expectedGroups + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup[] $actualGroups + */ + protected function assertContentTypeGroupsCorrect($expectedGroups, $actualGroups) + { + $sorter = static function ($a, $b) { + return strcmp($a->id, $b->id); + }; + + usort($expectedGroups, $sorter); + usort($actualGroups, $sorter); + + foreach ($expectedGroups as $key => $expectedGroup) { + $this->assertStructPropertiesCorrect( + $expectedGroup, + $actualGroups[$key], + [ + 'id', + 'identifier', + 'creationDate', + 'modificationDate', + 'creatorId', + 'modifierId', + ] + ); + } + } + + /** + * Test for the createContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentType() + * @depends testCreateContentType + */ + public function testCreateContentTypeThrowsInvalidArgumentExceptionDuplicateIdentifier() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$contentTypeCreateStruct\' is invalid: Another content type with identifier \'folder\' exists'); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $typeCreate = $contentTypeService->newContentTypeCreateStruct('folder'); + $typeCreate->mainLanguageCode = 'eng-GB'; + $typeCreate->names = ['eng-GB' => 'Article']; + + $firstFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); + $typeCreate->addFieldDefinition($firstFieldCreate); + + $groups = [ + $contentTypeService->loadContentTypeGroupByIdentifier('Media'), + $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), + ]; + + // Throws exception, since type "folder" exists + $contentTypeService->createContentType($typeCreate, $groups); + /* END: Use Case */ + } + + /** + * Test for the createContentType() method trying to create content type with already existing + * remoteId. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentType() + * @depends testCreateContentType + */ + public function testCreateContentTypeThrowsInvalidArgumentExceptionDuplicateRemoteId() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Another content type with remoteId \'a3d405b81be900468eb153d774f4f0d2\' exists'); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $typeCreate = $contentTypeService->newContentTypeCreateStruct('news-article'); + $typeCreate->remoteId = 'a3d405b81be900468eb153d774f4f0d2'; + $typeCreate->mainLanguageCode = 'eng-GB'; + $typeCreate->names = ['eng-GB' => 'Article']; + + $firstFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); + $typeCreate->addFieldDefinition($firstFieldCreate); + + $groups = [ + $contentTypeService->loadContentTypeGroupByIdentifier('Media'), + $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), + ]; + + // Throws exception, since "folder" type has this remote ID + $contentTypeService->createContentType($typeCreate, $groups); + /* END: Use Case */ + } + + /** + * Test for the createContentType() method creating content with duplicate field identifiers. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentType + * @depends testCreateContentType + */ + public function testCreateContentTypeThrowsInvalidArgumentExceptionDuplicateFieldIdentifier() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$contentTypeCreateStruct\' is invalid: The argument contains duplicate Field definition identifier \'title\''); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $typeCreate = $contentTypeService->newContentTypeCreateStruct('blog-post'); + $typeCreate->mainLanguageCode = 'eng-GB'; + $typeCreate->names = ['eng-GB' => 'Blog post']; + + $firstFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); + $typeCreate->addFieldDefinition($firstFieldCreate); + + $secondFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); + $typeCreate->addFieldDefinition($secondFieldCreate); + + $groups = [ + $contentTypeService->loadContentTypeGroupByIdentifier('Media'), + $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), + ]; + + // Throws exception, due to duplicate "title" field + $contentTypeService->createContentType($typeCreate, $groups); + /* END: Use Case */ + } + + /** + * Test for the createContentTypeGroup() method trying to create a content type with already + * existing identifier. + * + * @depends testCreateContentType + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentType + */ + public function testCreateContentTypeThrowsInvalidArgumentExceptionDuplicateContentTypeIdentifier() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Another content type with identifier \'blog-post\' exists'); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + // create published content type with identifier "blog-post" + $contentTypeDraft = $this->createContentTypeDraft(); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + $typeCreateStruct = $contentTypeService->newContentTypeCreateStruct('blog-post'); + $typeCreateStruct->remoteId = 'other-remote-id'; + $typeCreateStruct->creatorId = $repository->getPermissionResolver()->getCurrentUserReference()->getUserId(); + $typeCreateStruct->creationDate = new \DateTime(); + $typeCreateStruct->mainLanguageCode = 'eng-US'; + $typeCreateStruct->names = ['eng-US' => 'A name.']; + $typeCreateStruct->descriptions = ['eng-US' => 'A description.']; + + $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('test', 'eztext'); + $typeCreateStruct->addFieldDefinition($fieldCreate); + + // Throws an exception because content type with identifier "blog-post" already exists + $contentTypeService->createContentType( + $typeCreateStruct, + [ + $contentTypeService->loadContentTypeGroupByIdentifier('Content'), + ] + ); + } + + /** + * Test for the createContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentType() + * @depends testCreateContentType + */ + public function testCreateContentTypeThrowsContentTypeFieldDefinitionValidationException() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $typeCreate = $contentTypeService->newContentTypeCreateStruct('blog-post'); + $typeCreate->mainLanguageCode = 'eng-GB'; + $typeCreate->names = ['eng-GB' => 'Blog post']; + + $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('temperature', 'ezinteger'); + $fieldCreate->isSearchable = true; + $fieldCreate->validatorConfiguration = [ + 'IntegerValueValidator' => [ + 'minIntegerValue' => 'forty two point one', + 'maxIntegerValue' => 75, + ], + ]; + $typeCreate->addFieldDefinition($fieldCreate); + + $groups = [ + $contentTypeService->loadContentTypeGroupByIdentifier('Media'), + $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), + ]; + + try { + // Throws validation exception, because field's validator configuration is invalid + $contentType = $contentTypeService->createContentType($typeCreate, $groups); + } catch (ContentTypeFieldDefinitionValidationException $e) { + $validationErrors = $e->getFieldErrors(); + } + /* END: Use Case */ + + /* @var $validationErrors */ + $this->assertTrue(isset($validationErrors)); + $this->assertIsArray($validationErrors); + $this->assertCount(1, $validationErrors); + $this->assertArrayHasKey('temperature', $validationErrors); + $this->assertIsArray($validationErrors['temperature']); + $this->assertCount(1, $validationErrors['temperature']); + $this->assertInstanceOf(ValidationError::class, $validationErrors['temperature'][0]); + + $this->assertEquals( + new Message( + "Validator parameter '%parameter%' value must be of integer type", + ['%parameter%' => 'minIntegerValue'] + ), + $validationErrors['temperature'][0]->getTranslatableMessage() + ); + } + + /** + * Test for the createContentTypeGroup() method called with no groups. + * + * @depends testCreateContentType + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentTypeGroup + */ + public function testCreateContentTypeThrowsInvalidArgumentExceptionGroupsEmpty() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$contentTypeGroups\' is invalid: The argument must contain at least one content type group'); + + $repository = $this->getRepository(); + + $contentTypeService = $repository->getContentTypeService(); + + $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct( + 'new-type' + ); + $contentTypeCreateStruct->mainLanguageCode = 'eng-GB'; + $contentTypeCreateStruct->names = ['eng-GB' => 'Test type']; + + // Thrown an exception because array of content type groups is empty + $contentTypeService->createContentType($contentTypeCreateStruct, []); + } + + /** + * Test for the newContentTypeUpdateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::newContentTypeUpdateStruct() + */ + public function testNewContentTypeUpdateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $typeUpdate = $contentTypeService->newContentTypeUpdateStruct(); + /* END: Use Case */ + + $this->assertInstanceOf( + ContentTypeUpdateStruct::class, + $typeUpdate + ); + + return $typeUpdate; + } + + /** + * Test for the newContentTypeUpdateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::newContentTypeUpdateStruct() + * @depends testNewContentTypeUpdateStruct + */ + public function testNewContentTypeUpdateStructValues($typeUpdate) + { + foreach ($typeUpdate as $propertyName => $propertyValue) { + $this->assertNull( + $propertyValue, + "Property '$propertyName' is not null." + ); + } + } + + /** + * Test for the loadContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeDraft() + * @depends testCreateContentType + */ + public function testLoadContentTypeDraft() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $contentTypeDraftReloaded = $contentTypeService->loadContentTypeDraft( + $contentTypeDraft->id + ); + /* END: Use Case */ + + $this->assertEquals( + $contentTypeDraft, + $contentTypeDraftReloaded + ); + } + + /** + * Test for the loadContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeDraft() + * @depends testLoadContentTypeDraft + */ + public function testLoadContentTypeDraftThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $nonExistingContentTypeId = $this->generateId('type', 2342); + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Throws exception, since 2342 does not exist + $contentTypeDraft = $contentTypeService->loadContentTypeDraft($nonExistingContentTypeId); + /* END: Use Case */ + } + + /** + * Test for the loadContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeDraft() + */ + public function testLoadContentTypeDraftThrowsNotFoundExceptionIfDiffrentOwner() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + $permissionResolver = $repository->getPermissionResolver(); + $contentTypeService = $repository->getContentTypeService(); + + $draft = $this->createContentTypeDraft(); + + $anotherUser = $this->createUserVersion1('anotherUser'); + $permissionResolver->setCurrentUserReference($anotherUser); + + $contentTypeDraft = $contentTypeService->loadContentTypeDraft($draft->id); + } + + /** + * Test for the loadContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeDraft() + */ + public function testCanLoadContentTypeDraftEvenIfDiffrentOwner() + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + $permissionResolver = $repository->getPermissionResolver(); + $contentTypeService = $repository->getContentTypeService(); + + $draft = $this->createContentTypeDraft(); + + $anotherUser = $this->createUserVersion1('anotherUser'); + $permissionResolver->setCurrentUserReference($anotherUser); + + $loadedDraft = $contentTypeService->loadContentTypeDraft($draft->id, true); + + $this->assertSame((int)$loadedDraft->id, (int)$draft->id); + } + + /** + * Test for the updateContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeDraft() + */ + public function testUpdateContentTypeDraft() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $modifierId = $this->generateId('user', 14); + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $typeUpdate = $contentTypeService->newContentTypeUpdateStruct(); + $typeUpdate->identifier = 'news-article'; + $typeUpdate->remoteId = '4cf35f5166fd31bf0cda859dc837e095daee9833'; + $typeUpdate->urlAliasSchema = 'url@alias|scheme'; + $typeUpdate->nameSchema = '@name@scheme@'; + $typeUpdate->isContainer = true; + $typeUpdate->mainLanguageCode = 'eng-US'; + $typeUpdate->defaultAlwaysAvailable = false; + $typeUpdate->modifierId = $modifierId; + $typeUpdate->modificationDate = $this->createDateTime(); + $typeUpdate->names = [ + 'eng-GB' => 'News article', + 'ger-DE' => 'Nachrichten-Artikel', + ]; + $typeUpdate->descriptions = [ + 'eng-GB' => 'A news article', + 'ger-DE' => 'Ein Nachrichten-Artikel', + ]; + + $contentTypeService->updateContentTypeDraft($contentTypeDraft, $typeUpdate); + /* END: Use Case */ + + $updatedType = $contentTypeService->loadContentTypeDraft( + $contentTypeDraft->id + ); + + $this->assertInstanceOf( + ContentTypeDraft::class, + $updatedType + ); + + return [ + 'originalType' => $contentTypeDraft, + 'updateStruct' => $typeUpdate, + 'updatedType' => $updatedType, + ]; + } + + /** + * Test for the updateContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeDraft() + * @depends testUpdateContentTypeDraft + */ + public function testUpdateContentTypeDraftStructValues($data) + { + $originalType = $data['originalType']; + $updateStruct = $data['updateStruct']; + $updatedType = $data['updatedType']; + + $expectedValues = [ + 'id' => $originalType->id, + 'names' => $updateStruct->names, + 'descriptions' => $updateStruct->descriptions, + 'identifier' => $updateStruct->identifier, + 'creationDate' => $originalType->creationDate, + 'modificationDate' => $updateStruct->modificationDate, + 'creatorId' => $originalType->creatorId, + 'modifierId' => $updateStruct->modifierId, + 'urlAliasSchema' => $updateStruct->urlAliasSchema, + 'nameSchema' => $updateStruct->nameSchema, + 'isContainer' => $updateStruct->isContainer, + 'mainLanguageCode' => $updateStruct->mainLanguageCode, + 'contentTypeGroups' => $originalType->contentTypeGroups, + 'fieldDefinitions' => $originalType->fieldDefinitions, + ]; + + $this->assertPropertiesCorrect( + $expectedValues, + $updatedType + ); + + foreach ($originalType->fieldDefinitions as $index => $expectedFieldDefinition) { + $actualFieldDefinition = $updatedType->fieldDefinitions[$index]; + $this->assertInstanceOf( + FieldDefinition::class, + $actualFieldDefinition + ); + $this->assertEquals($expectedFieldDefinition, $actualFieldDefinition); + } + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testUpdateContentTypeDraftWithNewTranslation() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $contentTypeDraft = $this->createContentTypeDraft(); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); + // sanity check + self::assertEquals( + ['eng-US', 'ger-DE'], + array_keys($contentType->getNames()) + ); + + $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); + $updateStruct = $contentTypeService->newContentTypeUpdateStruct(); + $updateStruct->names = [ + 'eng-GB' => 'BrE blog post', + ]; + $contentTypeService->updateContentTypeDraft($contentTypeDraft, $updateStruct); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + self::assertEquals( + [ + 'eng-US' => 'Blog post', + 'ger-DE' => 'Blog-Eintrag', + 'eng-GB' => 'BrE blog post', + ], + $contentTypeService->loadContentType($contentType->id)->getNames() + ); + } + + /** + * Test for the updateContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeDraft() + * @depends testUpdateContentTypeDraft + */ + public function testUpdateContentTypeDraftThrowsInvalidArgumentExceptionDuplicateIdentifier() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $typeUpdate = $contentTypeService->newContentTypeUpdateStruct(); + $typeUpdate->identifier = 'folder'; + + // Throws exception, since type "folder" already exists + $contentTypeService->updateContentTypeDraft($contentTypeDraft, $typeUpdate); + /* END: Use Case */ + } + + /** + * Test for the updateContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeDraft() + * @depends testUpdateContentTypeDraft + */ + public function testUpdateContentTypeDraftThrowsInvalidArgumentExceptionDuplicateRemoteId() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $typeUpdate = $contentTypeService->newContentTypeUpdateStruct(); + $typeUpdate->remoteId = 'a3d405b81be900468eb153d774f4f0d2'; + + // Throws exception, since remote ID of type "folder" is used + $contentTypeService->updateContentTypeDraft($contentTypeDraft, $typeUpdate); + /* END: Use Case */ + } + + /** + * Test for the updateContentTypeDraft() method. + * + * @depends testUpdateContentTypeDraft + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeDraft + */ + public function testUpdateContentTypeDraftThrowsInvalidArgumentExceptionNoDraftForAuthenticatedUser() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$contentTypeDraft\' is invalid: There is no content type draft assigned to the authenticated user'); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $roleService = $repository->getRoleService(); + + $contentTypeDraft = $this->createContentTypeDraft(); + $typeUpdate = $contentTypeService->newContentTypeUpdateStruct(); + + // create Role allowing content type updates + $roleCreateStruct = $roleService->newRoleCreateStruct('ContentTypeUpdaters'); + $policyCreateStruct = $roleService->newPolicyCreateStruct('class', 'update'); + $roleDraft = $roleService->createRole($roleCreateStruct); + $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct); + $roleService->publishRoleDraft($roleDraft); + + $user = $this->createUserVersion1(); + $roleService->assignRoleToUser( + $roleService->loadRoleByIdentifier('ContentTypeUpdaters'), + $user + ); + $repository->getPermissionResolver()->setCurrentUserReference($user); + + // Throws exception, since draft belongs to another user + $contentTypeService->updateContentTypeDraft($contentTypeDraft, $typeUpdate); + } + + /** + * Test for the addFieldDefinition() method. + * + * @return array + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::addFieldDefinition() + * @depends testCreateContentType + */ + public function testAddFieldDefinition() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $fieldDefCreate = $contentTypeService->newFieldDefinitionCreateStruct('tags', 'ezstring'); + $fieldDefCreate->names = [ + 'eng-GB' => 'Tags', + 'ger-DE' => 'Schlagworte', + ]; + $fieldDefCreate->descriptions = [ + 'eng-GB' => 'Tags of the blog post', + 'ger-DE' => 'Schlagworte des Blog-Eintrages', + ]; + $fieldDefCreate->fieldGroup = 'blog-meta'; + $fieldDefCreate->position = 1; + $fieldDefCreate->isTranslatable = true; + $fieldDefCreate->isRequired = true; + $fieldDefCreate->isInfoCollector = false; + $fieldDefCreate->validatorConfiguration = [ + 'StringLengthValidator' => [ + 'minStringLength' => 0, + 'maxStringLength' => 0, + ], + ]; + $fieldDefCreate->fieldSettings = []; + $fieldDefCreate->isSearchable = true; + $fieldDefCreate->defaultValue = 'default tags'; + + $contentTypeService->addFieldDefinition($contentTypeDraft, $fieldDefCreate); + /* END: Use Case */ + + $loadedType = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); + + $this->assertInstanceOf( + ContentTypeDraft::class, + $loadedType + ); + + return [ + 'loadedType' => $loadedType, + 'fieldDefCreate' => $fieldDefCreate, + ]; + } + + /** + * Test for the addFieldDefinition() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::addFieldDefinition() + * @depends testAddFieldDefinition + */ + public function testAddFieldDefinitionStructValues(array $data) + { + $loadedType = $data['loadedType']; + $fieldDefCreate = $data['fieldDefCreate']; + + foreach ($loadedType->fieldDefinitions as $fieldDefinition) { + if ($fieldDefinition->identifier == $fieldDefCreate->identifier) { + $this->assertFieldDefinitionsEqual($fieldDefCreate, $fieldDefinition); + + return; + } + } + + $this->fail( + sprintf( + 'Could not create Field definition with identifier "%s".', + $fieldDefCreate->identifier + ) + ); + } + + /** + * Test for the addFieldDefinition() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::addFieldDefinition() + * @depends testAddFieldDefinition + */ + public function testAddFieldDefinitionThrowsInvalidArgumentExceptionDuplicateFieldIdentifier() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $fieldDefCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); + + // Throws an exception + $contentTypeService->addFieldDefinition($contentTypeDraft, $fieldDefCreate); + /* END: Use Case */ + } + + /** + * Test for the addFieldDefinition() method. + * + * Testing that field definition of non-repeatable field type can not be added multiple + * times to the same ContentType. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::addFieldDefinition() + * @depends testAddFieldDefinition + */ + public function testAddFieldDefinitionThrowsContentTypeFieldDefinitionValidationException() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $userContentType = $contentTypeService->loadContentTypeByIdentifier('user'); + $userContentTypeDraft = $contentTypeService->createContentTypeDraft($userContentType); + + $fieldDefCreate = $contentTypeService->newFieldDefinitionCreateStruct('temperature', 'ezinteger'); + $fieldDefCreate->isSearchable = true; + $fieldDefCreate->validatorConfiguration = [ + 'IntegerValueValidator' => [ + 'minIntegerValue' => 42, + 'maxIntegerValue' => 75.3, + ], + ]; + $fieldDefCreate->fieldGroup = 'blog-meta'; + $fieldDefCreate->position = 1; + $fieldDefCreate->isTranslatable = false; + $fieldDefCreate->isRequired = true; + $fieldDefCreate->isInfoCollector = false; + $fieldDefCreate->fieldSettings = []; + + try { + // Throws an exception because field's validator configuration is invalid + $contentTypeService->addFieldDefinition($userContentTypeDraft, $fieldDefCreate); + } catch (ContentTypeFieldDefinitionValidationException $e) { + $validationErrors = $e->getFieldErrors(); + } + /* END: Use Case */ + + /* @var $validationErrors */ + $this->assertTrue(isset($validationErrors)); + $this->assertIsArray($validationErrors); + $this->assertCount(1, $validationErrors); + $this->assertArrayHasKey('temperature', $validationErrors); + $this->assertIsArray($validationErrors['temperature']); + $this->assertCount(1, $validationErrors['temperature']); + $this->assertInstanceOf(ValidationError::class, $validationErrors['temperature'][0]); + + $this->assertEquals( + new Message( + "Validator parameter '%parameter%' value must be of integer type", + ['%parameter%' => 'maxIntegerValue'] + ), + $validationErrors['temperature'][0]->getTranslatableMessage() + ); + } + + /** + * Test for the addFieldDefinition() method. + * + * Testing that field definition of non-repeatable field type can not be added multiple + * times to the same ContentType. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::addFieldDefinition() + * @depends testAddFieldDefinition + */ + public function testAddFieldDefinitionThrowsBadStateExceptionNonRepeatableField() + { + $this->expectException(BadStateException::class); + $this->expectExceptionMessage('The content type already contains a Field definition of the singular Field Type \'ezuser\''); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $userContentType = $contentTypeService->loadContentTypeByIdentifier('user'); + $userContentTypeDraft = $contentTypeService->createContentTypeDraft($userContentType); + + $fieldDefCreate = $contentTypeService->newFieldDefinitionCreateStruct('second_user_account', 'ezuser'); + $fieldDefCreate->names = [ + 'eng-GB' => 'Second user account', + ]; + $fieldDefCreate->descriptions = [ + 'eng-GB' => 'Second user account for the ContentType', + ]; + $fieldDefCreate->fieldGroup = 'users'; + $fieldDefCreate->position = 1; + $fieldDefCreate->isTranslatable = false; + $fieldDefCreate->isRequired = true; + $fieldDefCreate->isInfoCollector = false; + $fieldDefCreate->validatorConfiguration = []; + $fieldDefCreate->fieldSettings = []; + $fieldDefCreate->isSearchable = false; + + // Throws an exception because $userContentTypeDraft already contains non-repeatable field type definition 'ezuser' + $contentTypeService->addFieldDefinition($userContentTypeDraft, $fieldDefCreate); + /* END: Use Case */ + } + + /** + * Test for the ContentTypeService::createContentType() method. + * + * Testing that field definition of non-repeatable field type can not be added multiple + * times to the same ContentTypeCreateStruct. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentType() + */ + public function testCreateContentThrowsContentTypeValidationException() + { + $this->expectException(ContentTypeValidationException::class); + $this->expectExceptionMessage('Field Type \'ezuser\' is singular and cannot be used more than once in a content type'); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + $contentTypeCreateStruct = $contentTypeService->newContentTypeCreateStruct('this_is_new'); + $contentTypeCreateStruct->names = ['eng-GB' => 'This is new']; + $contentTypeCreateStruct->mainLanguageCode = 'eng-GB'; + + // create first field definition + $firstFieldDefinition = $contentTypeService->newFieldDefinitionCreateStruct( + 'first_user', + 'ezuser' + ); + $firstFieldDefinition->names = [ + 'eng-GB' => 'First user account', + ]; + $firstFieldDefinition->position = 1; + + $contentTypeCreateStruct->addFieldDefinition($firstFieldDefinition); + + // create second field definition + $secondFieldDefinition = $contentTypeService->newFieldDefinitionCreateStruct( + 'second_user', + 'ezuser' + ); + $secondFieldDefinition->names = [ + 'eng-GB' => 'Second user account', + ]; + $secondFieldDefinition->position = 2; + + $contentTypeCreateStruct->addFieldDefinition($secondFieldDefinition); + + // Throws an exception because the ContentTypeCreateStruct has a singular field repeated + $contentTypeService->createContentType( + $contentTypeCreateStruct, + [$contentTypeService->loadContentTypeGroupByIdentifier('Content')] + ); + /* END: Use Case */ + } + + /** + * Test for the addFieldDefinition() method. + * + * Testing adding field definition of the field type that can not be added to the ContentType that + * already has Content instances. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::addFieldDefinition() + * @depends testAddFieldDefinition + */ + public function testAddFieldDefinitionThrowsBadStateExceptionContentInstances() + { + $this->expectException(BadStateException::class); + $this->expectExceptionMessage('A Field definition of the \'ezuser\' Field Type cannot be added because the content type already has Content items'); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $folderContentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + $folderContentTypeDraft = $contentTypeService->createContentTypeDraft($folderContentType); + + $fieldDefCreate = $contentTypeService->newFieldDefinitionCreateStruct('user_account', 'ezuser'); + $fieldDefCreate->names = [ + 'eng-GB' => 'User account', + ]; + $fieldDefCreate->descriptions = [ + 'eng-GB' => 'User account field definition for ContentType that has Content instances', + ]; + $fieldDefCreate->fieldGroup = 'users'; + $fieldDefCreate->position = 1; + $fieldDefCreate->isTranslatable = false; + $fieldDefCreate->isRequired = true; + $fieldDefCreate->isInfoCollector = false; + $fieldDefCreate->validatorConfiguration = []; + $fieldDefCreate->fieldSettings = []; + $fieldDefCreate->isSearchable = false; + + // Throws an exception because 'ezuser' type field definition can't be added to ContentType that already has Content instances + $contentTypeService->addFieldDefinition($folderContentTypeDraft, $fieldDefCreate); + /* END: Use Case */ + } + + /** + * Test for the removeFieldDefinition() method. + * + * @return array + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::removeFieldDefinition() + * @depends testCreateContentType + */ + public function testRemoveFieldDefinition() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $bodyField = $contentTypeDraft->getFieldDefinition('body'); + + $contentTypeService->removeFieldDefinition($contentTypeDraft, $bodyField); + /* END: Use Case */ + + $loadedType = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); + + $this->assertInstanceOf( + ContentTypeDraft::class, + $loadedType + ); + + return [ + 'removedFieldDefinition' => $bodyField, + 'loadedType' => $loadedType, + ]; + } + + /** + * Test for the removeFieldDefinition() method. + * + * @param array $data + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::removeFieldDefinition() + * @depends testRemoveFieldDefinition + */ + public function testRemoveFieldDefinitionRemoved(array $data) + { + $removedFieldDefinition = $data['removedFieldDefinition']; + $loadedType = $data['loadedType']; + + foreach ($loadedType->fieldDefinitions as $fieldDefinition) { + if ($fieldDefinition->identifier == $removedFieldDefinition->identifier) { + $this->fail( + sprintf( + 'Field definition with identifier "%s" not removed.', + $removedFieldDefinition->identifier + ) + ); + } + } + } + + /** + * Test for the removeFieldDefinition() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::removeFieldDefinition() + * @depends testRemoveFieldDefinition + */ + public function testRemoveFieldDefinitionThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $bodyField = $contentTypeDraft->getFieldDefinition('body'); + $contentTypeService->removeFieldDefinition($contentTypeDraft, $bodyField); + + $loadedDraft = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); + + // Throws exception, sine "body" has already been removed + $contentTypeService->removeFieldDefinition($loadedDraft, $bodyField); + /* END: Use Case */ + } + + /** + * Test removeFieldDefinition() method for field in a different draft throws an exception. + * + * @depends testRemoveFieldDefinition + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::removeFieldDefinition + */ + public function testRemoveFieldDefinitionThrowsInvalidArgumentExceptionOnWrongDraft() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $contentTypeDraft01 = $this->createContentTypeDraft(); + $contentTypeDraft02 = $this->createContentTypeDraft(); + + $bodyField = $contentTypeDraft02->getFieldDefinition('body'); + + // Throws an exception because $bodyField field belongs to another draft + $contentTypeService->removeFieldDefinition($contentTypeDraft01, $bodyField); + } + + /** + * Test for the removeFieldDefinition() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::removeFieldDefinition() + * @depends testRemoveFieldDefinition + */ + public function testRemoveFieldDefinitionRemovesFieldFromContent() + { + $repository = $this->getRepository(); + + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + + // Create ContentType + $contentTypeDraft = $this->createContentTypeDraft(); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + $publishedType = $contentTypeService->loadContentType($contentTypeDraft->id); + + // Create multi-language Content in all 3 possible versions + $contentDraft = $this->createContentDraft(); + $archivedContent = $contentService->publishVersion($contentDraft->versionInfo); + $contentDraft = $contentService->createContentDraft($archivedContent->contentInfo); + $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); + $draftContent = $contentService->createContentDraft($publishedContent->contentInfo); + + // Remove field definition from ContentType + $contentTypeDraft = $contentTypeService->createContentTypeDraft($publishedType); + $bodyField = $contentTypeDraft->getFieldDefinition('body'); + $contentTypeService->removeFieldDefinition($contentTypeDraft, $bodyField); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + // Reload all versions + $contentVersion1Archived = $contentService->loadContent( + $archivedContent->contentInfo->id, + null, + $archivedContent->versionInfo->versionNo + ); + $contentVersion2Published = $contentService->loadContent( + $publishedContent->contentInfo->id, + null, + $publishedContent->versionInfo->versionNo + ); + $contentVersion3Draft = $contentService->loadContent( + $draftContent->contentInfo->id, + null, + $draftContent->versionInfo->versionNo + ); + + $this->assertInstanceOf( + Content::class, + $contentVersion1Archived + ); + $this->assertInstanceOf( + Content::class, + $contentVersion2Published + ); + $this->assertInstanceOf( + Content::class, + $contentVersion3Draft + ); + + return [ + $contentVersion1Archived, + $contentVersion2Published, + $contentVersion3Draft, + ]; + } + + /** + * Test for the removeFieldDefinition() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content[] $data + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::removeFieldDefinition + * @depends testRemoveFieldDefinitionRemovesFieldFromContent + */ + public function testRemoveFieldDefinitionRemovesFieldFromContentRemoved($data) + { + list( + $contentVersion1Archived, + $contentVersion1Published, + $contentVersion2Draft + ) = $data; + + $this->assertFalse( + isset($contentVersion1Archived->fields['body']), + 'The field was not removed from archived version.' + ); + $this->assertFalse( + isset($contentVersion1Published->fields['body']), + 'The field was not removed from published version.' + ); + $this->assertFalse( + isset($contentVersion2Draft->fields['body']), + 'The field was not removed from draft version.' + ); + } + + /** + * Test for the addFieldDefinition() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::addFieldDefinition() + * @depends testAddFieldDefinition + */ + public function testAddFieldDefinitionAddsFieldToContent() + { + $repository = $this->getRepository(); + + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + + // Create ContentType + $contentTypeDraft = $this->createContentTypeDraft(); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + $publishedType = $contentTypeService->loadContentType($contentTypeDraft->id); + + // Create multi-language Content in all 3 possible versions + $contentDraft = $this->createContentDraft(); + $archivedContent = $contentService->publishVersion($contentDraft->versionInfo); + $contentDraft = $contentService->createContentDraft($archivedContent->contentInfo); + $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); + $draftContent = $contentService->createContentDraft($publishedContent->contentInfo); + + // Add field definition to ContentType + $contentTypeDraft = $contentTypeService->createContentTypeDraft($publishedType); + + $fieldDefinitionCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct('byline', 'ezstring'); + $fieldDefinitionCreateStruct->names = [ + 'eng-US' => 'Byline', + ]; + $fieldDefinitionCreateStruct->descriptions = [ + 'eng-US' => 'Byline of the blog post', + ]; + $fieldDefinitionCreateStruct->fieldGroup = 'blog-meta'; + $fieldDefinitionCreateStruct->position = 1; + $fieldDefinitionCreateStruct->isTranslatable = true; + $fieldDefinitionCreateStruct->isRequired = true; + $fieldDefinitionCreateStruct->isInfoCollector = false; + $fieldDefinitionCreateStruct->validatorConfiguration = [ + 'StringLengthValidator' => [ + 'minStringLength' => 0, + 'maxStringLength' => 0, + ], + ]; + $fieldDefinitionCreateStruct->fieldSettings = []; + $fieldDefinitionCreateStruct->isSearchable = true; + + $contentTypeService->addFieldDefinition($contentTypeDraft, $fieldDefinitionCreateStruct); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + // Reload all versions + $contentVersion1Archived = $contentService->loadContent( + $archivedContent->contentInfo->id, + null, + $archivedContent->versionInfo->versionNo + ); + $contentVersion2Published = $contentService->loadContent( + $publishedContent->contentInfo->id, + null, + $publishedContent->versionInfo->versionNo + ); + $contentVersion3Draft = $contentService->loadContent( + $draftContent->contentInfo->id, + null, + $draftContent->versionInfo->versionNo + ); + + $this->assertInstanceOf( + Content::class, + $contentVersion1Archived + ); + $this->assertInstanceOf( + Content::class, + $contentVersion2Published + ); + $this->assertInstanceOf( + Content::class, + $contentVersion3Draft + ); + + return [ + $contentVersion1Archived, + $contentVersion2Published, + $contentVersion3Draft, + ]; + } + + /** + * Test for the addFieldDefinition() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content[] $data + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::addFieldDefinition() + * @depends testAddFieldDefinitionAddsFieldToContent + */ + public function testAddFieldDefinitionAddsFieldToContentAdded(array $data) + { + list( + $contentVersion1Archived, + $contentVersion1Published, + $contentVersion2Draft + ) = $data; + + $this->assertTrue( + isset($contentVersion1Archived->fields['byline']), + 'New field was not added to archived version.' + ); + $this->assertTrue( + isset($contentVersion1Published->fields['byline']), + 'New field was not added to published version.' + ); + $this->assertTrue( + isset($contentVersion2Draft->fields['byline']), + 'New field was not added to draft version.' + ); + + $this->assertEquals( + $contentVersion1Archived->getField('byline')->id, + $contentVersion1Published->getField('byline')->id + ); + $this->assertEquals( + $contentVersion1Published->getField('byline')->id, + $contentVersion2Draft->getField('byline')->id + ); + } + + /** + * Test for the newFieldDefinitionUpdateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::newFieldDefinitionUpdateStruct() + */ + public function testNewFieldDefinitionUpdateStruct() + { + $repository = $this->getRepository(); + /* BEGIN: Use Case */ + // $draftId contains the ID of a content type draft + $contentTypeService = $repository->getContentTypeService(); + + $updateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); + /* END: Use Case */ + + self::assertInstanceOf( + FieldDefinitionUpdateStruct::class, + $updateStruct + ); + + return $updateStruct; + } + + /** + * Test for the newFieldDefinitionUpdateStruct() method. + * + * @depends testNewFieldDefinitionUpdateStruct + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::newContentTypeUpdateStruct + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionUpdateStruct $fieldDefinitionUpdateStruct + */ + public function testNewFieldDefinitionUpdateStructValues($fieldDefinitionUpdateStruct) + { + foreach ($fieldDefinitionUpdateStruct as $propertyName => $propertyValue) { + $this->assertNull( + $propertyValue, + "Property '$propertyName' is not null." + ); + } + } + + /** + * Test for the updateFieldDefinition() method. + * + * @return array + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateFieldDefinition() + * @depends testLoadContentTypeDraft + */ + public function testUpdateFieldDefinition() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $bodyField = $contentTypeDraft->getFieldDefinition('body'); + + $bodyUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); + $bodyUpdateStruct->identifier = 'blog-body'; + $bodyUpdateStruct->names = [ + 'eng-GB' => 'Blog post body', + 'ger-DE' => 'Blog-Eintrags-Textkörper', + ]; + $bodyUpdateStruct->descriptions = [ + 'eng-GB' => 'Blog post body of the blog post', + 'ger-DE' => 'Blog-Eintrags-Textkörper des Blog-Eintrages', + ]; + $bodyUpdateStruct->fieldGroup = 'updated-blog-content'; + $bodyUpdateStruct->position = 3; + $bodyUpdateStruct->isTranslatable = false; + $bodyUpdateStruct->isRequired = false; + $bodyUpdateStruct->isInfoCollector = true; + $bodyUpdateStruct->validatorConfiguration = []; + $bodyUpdateStruct->fieldSettings = [ + 'textRows' => 60, + ]; + $bodyUpdateStruct->isSearchable = false; + + $contentTypeService->updateFieldDefinition( + $contentTypeDraft, + $bodyField, + $bodyUpdateStruct + ); + /* END: Use Case */ + + $loadedDraft = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); + $this->assertInstanceOf( + FieldDefinition::class, + ($loadedField = $loadedDraft->getFieldDefinition('blog-body')) + ); + + return [ + 'originalField' => $bodyField, + 'updatedField' => $loadedField, + 'updateStruct' => $bodyUpdateStruct, + ]; + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateFieldDefinition + */ + public function testUpdateFieldDefinitionWithNewTranslation() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $bodyField = $contentTypeDraft->getFieldDefinition('body'); + + self::assertEquals( + ['eng-US', 'ger-DE'], + array_keys($bodyField->getNames()) + ); + + $bodyUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); + $bodyUpdateStruct->identifier = 'blog-body'; + $bodyUpdateStruct->names = [ + 'eng-GB' => 'New blog post body', + ]; + $bodyUpdateStruct->descriptions = [ + 'eng-GB' => null, + ]; + $bodyUpdateStruct->fieldGroup = 'updated-blog-content'; + $bodyUpdateStruct->position = 3; + $bodyUpdateStruct->isTranslatable = false; + $bodyUpdateStruct->isRequired = false; + $bodyUpdateStruct->isInfoCollector = true; + $bodyUpdateStruct->validatorConfiguration = []; + $bodyUpdateStruct->fieldSettings = [ + 'textRows' => 60, + ]; + $bodyUpdateStruct->isSearchable = false; + + $contentTypeService->updateFieldDefinition( + $contentTypeDraft, + $bodyField, + $bodyUpdateStruct + ); + /* END: Use Case */ + + $contentType = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); + + self::assertEquals( + [ + 'eng-GB' => 'New blog post body', + 'eng-US' => 'Body', + 'ger-DE' => 'Textkörper', + ], + $contentType->getFieldDefinition('blog-body')->getNames() + ); + self::assertEquals( + [ + 'eng-GB' => null, + 'eng-US' => 'Body of the blog post', + 'ger-DE' => 'Textkörper des Blog-Eintrages', + ], + $contentType->getFieldDefinition('blog-body')->getDescriptions() + ); + } + + /** + * Test for the updateFieldDefinition() method. + * + * @param array $data + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateFieldDefinition() + * @depends testUpdateFieldDefinition + */ + public function testUpdateFieldDefinitionStructValues(array $data) + { + $originalField = $data['originalField']; + $updatedField = $data['updatedField']; + $updateStruct = $data['updateStruct']; + + $this->assertPropertiesCorrect( + [ + 'id' => $originalField->id, + 'identifier' => $updateStruct->identifier, + 'names' => $updateStruct->names, + 'descriptions' => $updateStruct->descriptions, + 'fieldGroup' => $updateStruct->fieldGroup, + 'position' => $updateStruct->position, + 'fieldTypeIdentifier' => $originalField->fieldTypeIdentifier, + 'isTranslatable' => $updateStruct->isTranslatable, + 'isRequired' => $updateStruct->isRequired, + 'isInfoCollector' => $updateStruct->isInfoCollector, + 'validatorConfiguration' => $updateStruct->validatorConfiguration, + 'defaultValue' => $originalField->defaultValue, + 'isSearchable' => $updateStruct->isSearchable, + ], + $updatedField + ); + } + + /** + * Test for the updateFieldDefinition() method using an empty FieldDefinitionUpdateStruct. + * + * @covers \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionUpdateStruct + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateFieldDefinition + */ + public function testUpdateFieldDefinitionWithEmptyStruct() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $contentTypeDraft = $this->createContentTypeDraft(); + $fieldDefinition = $contentTypeDraft->getFieldDefinition('body'); + $fieldDefinitionUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); + + $contentTypeService->updateFieldDefinition( + $contentTypeDraft, + $fieldDefinition, + $fieldDefinitionUpdateStruct + ); + $contentTypeDraft = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); + $updatedFieldDefinition = $contentTypeDraft->getFieldDefinition('body'); + + self::assertEquals( + $fieldDefinition, + $updatedFieldDefinition + ); + } + + /** + * Test for the updateFieldDefinition() method with already defined field identifier. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateFieldDefinition + */ + public function testUpdateFieldDefinitionThrowsInvalidArgumentExceptionFieldIdentifierExists() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$fieldDefinitionUpdateStruct\' is invalid: Another Field definition with identifier \'title\' exists in the content type'); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $bodyField = $contentTypeDraft->getFieldDefinition('body'); + $titleField = $contentTypeDraft->getFieldDefinition('title'); + + $bodyUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); + $bodyUpdateStruct->identifier = 'title'; + + // Throws exception, since "title" field already exists + $contentTypeService->updateFieldDefinition( + $contentTypeDraft, + $bodyField, + $bodyUpdateStruct + ); + /* END: Use Case */ + } + + /** + * Test for the updateFieldDefinition() method trying to update non-existent field. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateFieldDefinition() + * @depends testLoadContentTypeDraft + */ + public function testUpdateFieldDefinitionThrowsInvalidArgumentExceptionForUndefinedField() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$fieldDefinition\' is invalid: The given Field definition does not belong to the content type'); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $bodyField = $contentTypeDraft->getFieldDefinition('body'); + $contentTypeService->removeFieldDefinition($contentTypeDraft, $bodyField); + + $loadedDraft = $contentTypeService->loadContentTypeDraft($contentTypeDraft->id); + + $bodyUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); + + // Throws exception, since field "body" is already deleted + $contentTypeService->updateFieldDefinition( + $loadedDraft, + $bodyField, + $bodyUpdateStruct + ); + /* END: Use Case */ + } + + /** + * Test for the publishContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::publishContentTypeDraft() + * @depends testLoadContentTypeDraft + */ + public function testPublishContentTypeDraft() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + /* END: Use Case */ + + $publishedType = $contentTypeService->loadContentType($contentTypeDraft->id); + + $this->assertInstanceOf( + ContentType::class, + $publishedType + ); + $this->assertNotInstanceOf( + ContentTypeDraft::class, + $publishedType + ); + } + + /** + * Test for the publishContentTypeDraft() method setting proper ContentType nameSchema. + * + * @depends testPublishContentTypeDraft + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::publishContentTypeDraft + */ + public function testPublishContentTypeDraftSetsNameSchema() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $typeCreateStruct = $contentTypeService->newContentTypeCreateStruct( + 'new-type' + ); + $typeCreateStruct->names = [ + 'eng-GB' => 'Type title', + ]; + $typeCreateStruct->mainLanguageCode = 'eng-GB'; + + $titleFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); + $titleFieldCreate->position = 1; + $typeCreateStruct->addFieldDefinition($titleFieldCreate); + + $type = $contentTypeService->createContentType( + $typeCreateStruct, + [ + $contentTypeService->loadContentTypeGroupByIdentifier('Content'), + ] + ); + + $contentTypeService->publishContentTypeDraft($type); + + $loadedContentType = $contentTypeService->loadContentType($type->id); + + $this->assertEquals('', $loadedContentType->nameSchema); + } + + /** + * Test that publishing content type Draft refreshes list of content types in content type groups. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::publishContentTypeDraft + */ + public function testPublishContentTypeDraftRefreshesContentTypesList() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $contentTypeDraft = $this->createContentTypeDraft(); + + // Make sure to 1. check draft is not part of lists, and 2. warm cache to make sure it invalidates + $contentTypes = $contentTypeService->loadContentTypeList([1, $contentTypeDraft->id]); + self::assertArrayNotHasKey($contentTypeDraft->id, $contentTypes); + self::assertCount(1, $contentTypes); + + $contentTypeGroups = $contentTypeDraft->getContentTypeGroups(); + foreach ($contentTypeGroups as $contentTypeGroup) { + $contentTypes = $contentTypeService->loadContentTypes($contentTypeGroup); + // check if not published content type does not exist on published content types list + self::assertNotContains( + $contentTypeDraft->id, + array_map( + static function (ContentType $contentType) { + return $contentType->id; + }, + $contentTypes + ) + ); + } + + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + // After publishing it should be part of lists + $contentTypes = $contentTypeService->loadContentTypeList([1, $contentTypeDraft->id]); + self::assertArrayHasKey($contentTypeDraft->id, $contentTypes); + self::assertCount(2, $contentTypes); + + foreach ($contentTypeGroups as $contentTypeGroup) { + $contentTypes = $contentTypeService->loadContentTypes($contentTypeGroup); + // check if published Content is available in published content types list + self::assertContains( + $contentTypeDraft->id, + array_map( + static function (ContentType $contentType) { + return $contentType->id; + }, + $contentTypes + ) + ); + } + } + + /** + * Test for the publishContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::publishContentTypeDraft() + * @depends testPublishContentTypeDraft + */ + public function testPublishContentTypeDraftThrowsBadStateException() + { + $this->expectException(BadStateException::class); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + /* BEGIN: Use Case */ + $contentTypeDraft = $this->createContentTypeDraft(); + + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + // Throws exception, since no draft exists anymore + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + /* END: Use Case */ + } + + /** + * Test for the createContentTypeGroup() method trying to create content type without any fields. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::publishContentTypeDraft() + * @depends testPublishContentTypeDraft + */ + public function testPublishContentTypeDraftThrowsInvalidArgumentExceptionWithoutFields() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$contentTypeDraft\' is invalid: The content type draft should have at least one Field definition'); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $typeCreateStruct = $contentTypeService->newContentTypeCreateStruct( + 'no-fields-type' + ); + $typeCreateStruct->remoteId = 'new-unique-remoteid'; + $typeCreateStruct->creatorId = $repository->getPermissionResolver()->getCurrentUserReference()->getUserId(); + $typeCreateStruct->creationDate = new \DateTime(); + $typeCreateStruct->mainLanguageCode = 'eng-US'; + $typeCreateStruct->names = ['eng-US' => 'A name.']; + $typeCreateStruct->descriptions = ['eng-US' => 'A description.']; + + $contentTypeDraft = $contentTypeService->createContentType( + $typeCreateStruct, + [ + $contentTypeService->loadContentTypeGroupByIdentifier('Content'), + ] + ); + // Throws an exception because content type draft should have at least one field definition. + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + } + + /** + * Test for the loadContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentType() + * @depends testCreateContentType + * @group user + * @group field-type + */ + public function testLoadContentType() + { + $repository = $this->getRepository(); + + $userGroupId = $this->generateId('type', 3); + /* BEGIN: Use Case */ + // $userGroupId is the ID of the "user_group" type + $contentTypeService = $repository->getContentTypeService(); + // Loads the standard "user_group" type + $userGroupType = $contentTypeService->loadContentType($userGroupId); + /* END: Use Case */ + + $this->assertInstanceOf( + ContentType::class, + $userGroupType + ); + + return $userGroupType; + } + + /** + * Test that multi-language logic respects prioritized language list. + * + * @dataProvider getPrioritizedLanguageList + * + * @param string[] $languageCodes + */ + public function testLoadContentTypeWithPrioritizedLanguagesList(array $languageCodes) + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $contentType = $this->createContentTypeDraft(); + $contentTypeService->publishContentTypeDraft($contentType); + $contentType = $contentTypeService->loadContentType($contentType->id, $languageCodes); + + $language = isset($languageCodes[0]) ? $languageCodes[0] : 'eng-US'; + /** @var \Ibexa\Core\FieldType\TextLine\Value $nameValue */ + self::assertEquals( + $contentType->getName($language), + $contentType->getName() + ); + self::assertEquals( + $contentType->getDescription($language), + $contentType->getDescription() + ); + + foreach ($contentType->getFieldDefinitions() as $fieldDefinition) { + self::assertEquals( + $fieldDefinition->getName($language), + $fieldDefinition->getName() + ); + self::assertEquals( + $fieldDefinition->getDescription($language), + $fieldDefinition->getDescription() + ); + } + } + + /** + * @return array + */ + public function getPrioritizedLanguageList() + { + return [ + [[]], + [['eng-US']], + [['ger-DE']], + [['eng-US', 'ger-DE']], + [['ger-DE', 'eng-US']], + ]; + } + + /** + * Test for the loadContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentType() + * @depends testLoadContentType + */ + public function testLoadContentTypeStructValues($userGroupType) + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $this->assertPropertiesCorrect( + [ + 'id' => $this->generateId('type', 3), + 'status' => 0, + 'identifier' => 'user_group', + 'creationDate' => $this->createDateTime(1024392098), + 'modificationDate' => $this->createDateTime(1048494743), + 'creatorId' => $this->generateId('user', 14), + 'modifierId' => $this->generateId('user', 14), + 'remoteId' => '25b4268cdcd01921b808a0d854b877ef', + 'names' => [ + 'eng-US' => 'User group', + ], + 'descriptions' => [], + 'nameSchema' => '<name>', + 'isContainer' => true, + 'mainLanguageCode' => 'eng-US', + 'defaultAlwaysAvailable' => true, + 'defaultSortField' => 1, + 'defaultSortOrder' => 1, + 'contentTypeGroups' => [ + 0 => $contentTypeService->loadContentTypeGroup($this->generateId('typegroup', 2)), + ], + ], + $userGroupType + ); + + return $userGroupType->fieldDefinitions; + } + + /** + * Test for the loadContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentType() + * @depends testLoadContentTypeStructValues + */ + public function testLoadContentTypeFieldDefinitions(APIFieldDefinitionCollection $fieldDefinitions) + { + $expectedFieldDefinitions = [ + 'name' => [ + 'identifier' => 'name', + 'fieldGroup' => '', + 'position' => 1, + 'fieldTypeIdentifier' => 'ezstring', + 'isTranslatable' => true, + 'isRequired' => true, + 'isInfoCollector' => false, + 'isSearchable' => true, + 'defaultValue' => new TextLineValue(), + 'names' => [ + 'eng-US' => 'Name', + ], + 'descriptions' => [], + ], + 'description' => [ + 'identifier' => 'description', + 'fieldGroup' => '', + 'position' => 2, + 'fieldTypeIdentifier' => 'ezstring', + 'isTranslatable' => true, + 'isRequired' => false, + 'isInfoCollector' => false, + 'isSearchable' => true, + 'defaultValue' => new TextLineValue(), + 'names' => [ + 'eng-US' => 'Description', + ], + 'descriptions' => [], + ], + ]; + + $fieldDefinitions = $fieldDefinitions->toArray(); + foreach ($fieldDefinitions as $index => $fieldDefinition) { + $this->assertInstanceOf( + FieldDefinition::class, + $fieldDefinition + ); + + $this->assertNotNull($fieldDefinition->id); + + if (!isset($expectedFieldDefinitions[$fieldDefinition->identifier])) { + $this->fail( + sprintf( + 'Unexpected Field definition loaded: "%s" (%s)', + $fieldDefinition->identifier, + $fieldDefinition->id + ) + ); + } + + $this->assertPropertiesCorrect( + $expectedFieldDefinitions[$fieldDefinition->identifier], + $fieldDefinition + ); + unset($expectedFieldDefinitions[$fieldDefinition->identifier]); + unset($fieldDefinitions[$index]); + } + + if (0 !== count($expectedFieldDefinitions)) { + $this->fail( + sprintf( + 'Missing expected Field definitions: %s', + implode(',', array_column($expectedFieldDefinitions, 'identifier')) + ) + ); + } + + if (0 !== count($fieldDefinitions)) { + $this->fail( + sprintf( + 'Loaded unexpected Field definitions: %s', + implode( + ',', + array_map( + static function ($fieldDefinition) { + return $fieldDefinition->identifier; + }, + $fieldDefinitions + ) + ) + ) + ); + } + } + + /** + * Test for the loadContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentType() + * @depends testLoadContentType + */ + public function testLoadContentTypeThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $nonExistentTypeId = $this->generateId('type', 2342); + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Throws exception, since type with ID 2342 does not exist + $contentTypeService->loadContentType($nonExistentTypeId); + /* END: Use Case */ + } + + /** + * Test for the loadContentTypeByIdentifier() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeByIdentifier() + * @depends testLoadContentType + * @group user + */ + public function testLoadContentTypeByIdentifier() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $articleType = $contentTypeService->loadContentTypeByIdentifier('article'); + /* END: Use Case */ + + $this->assertInstanceOf( + ContentType::class, + $articleType + ); + + return $articleType; + } + + /** + * Test for the loadContentTypeByIdentifier() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeByIdentifier() + * @depends testLoadContentTypeByIdentifier + */ + public function testLoadContentTypeByIdentifierReturnsCorrectInstance($contentType) + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $this->assertEquals( + $contentTypeService->loadContentType($contentType->id), + $contentType + ); + } + + /** + * Test for the loadContentTypeByIdentifier() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeByIdentifier() + * @depends testLoadContentTypeByIdentifier + */ + public function testLoadContentTypeByIdentifierThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Throws an exception, since no type with this identifier exists + $contentTypeService->loadContentTypeByIdentifier('sindelfingen'); + /* END: Use Case */ + } + + /** + * Test for the loadContentTypeByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeByRemoteId() + * @depends testLoadContentType + */ + public function testLoadContentTypeByRemoteId() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Loads the standard "user_group" type + $userGroupType = $contentTypeService->loadContentTypeByRemoteId( + '25b4268cdcd01921b808a0d854b877ef' + ); + /* END: Use Case */ + + $this->assertInstanceOf( + ContentType::class, + $userGroupType + ); + + return $userGroupType; + } + + /** + * Test for the loadContentTypeByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeByRemoteId() + * @depends testLoadContentTypeByRemoteId + */ + public function testLoadContentTypeByRemoteIdReturnsCorrectInstance($contentType) + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $this->assertEquals( + $contentTypeService->loadContentType($contentType->id), + $contentType + ); + } + + /** + * Test for the loadContentTypeByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeByRemoteId() + * @depends testLoadContentType + */ + public function testLoadContentTypeByRemoteIdThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Throws an exception, since no type with this remote ID exists + $contentTypeService->loadContentTypeByRemoteId('not-exists'); + /* END: Use Case */ + } + + /** + * Test for the loadContentTypeList() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypeList() + * @depends testLoadContentType + */ + public function testLoadContentTypeList() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $types = $contentTypeService->loadContentTypeList([3, 4]); + + $this->assertIsIterable($types); + + $this->assertEquals( + [ + 3 => $contentTypeService->loadContentType(3), + 4 => $contentTypeService->loadContentType(4), + ], + $types + ); + } + + /** + * Test for the loadContentTypes() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypes() + * @depends testLoadContentType + */ + public function testLoadContentTypes() + { + $repository = $this->getRepository(); + + $typeGroupId = $this->generateId('typegroup', 2); + /* BEGIN: Use Case */ + // $typeGroupId is a valid ID of a content type group + $contentTypeService = $repository->getContentTypeService(); + + $contentTypeGroup = $contentTypeService->loadContentTypeGroup($typeGroupId); + + // Loads all types from content type group "Users" + $types = $contentTypeService->loadContentTypes($contentTypeGroup); + /* END: Use Case */ + + $this->assertIsArray($types); + + return $types; + } + + /** + * Test for the loadContentTypes() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::loadContentTypes() + * @depends testLoadContentTypes + */ + public function testLoadContentTypesContent(array $types) + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + usort( + $types, + static function ($a, $b) { + if ($a->id == $b->id) { + return 0; + } + + return ($a->id < $b->id) ? -1 : 1; + } + ); + $this->assertEquals( + [ + $contentTypeService->loadContentType($this->generateId('type', 3)), + $contentTypeService->loadContentType($this->generateId('type', 4)), + ], + $types + ); + } + + /** + * Test for the createContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentTypeDraft() + * @depends testLoadContentType + */ + public function testCreateContentTypeDraft() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $commentType = $contentTypeService->loadContentTypeByIdentifier('comment', Language::ALL); + + $commentTypeDraft = $contentTypeService->createContentTypeDraft($commentType); + /* END: Use Case */ + + $this->assertInstanceOf( + ContentTypeDraft::class, + $commentTypeDraft + ); + + return [ + 'originalType' => $commentType, + 'typeDraft' => $commentTypeDraft, + ]; + } + + /** + * Test for the createContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentTypeDraft() + * @depends testCreateContentTypeDraft + */ + public function testCreateContentTypeDraftStructValues(array $data) + { + $originalType = $data['originalType']; + $typeDraft = $data['typeDraft']; + + // Names and descriptions tested in corresponding language test + $this->assertPropertiesCorrect( + [ + 'id' => $originalType->id, + 'names' => $originalType->names, + 'descriptions' => $originalType->descriptions, + 'identifier' => $originalType->identifier, + 'creatorId' => $originalType->creatorId, + 'modifierId' => $originalType->modifierId, + 'remoteId' => $originalType->remoteId, + 'urlAliasSchema' => $originalType->urlAliasSchema, + 'nameSchema' => $originalType->nameSchema, + 'isContainer' => $originalType->isContainer, + 'mainLanguageCode' => $originalType->mainLanguageCode, + 'defaultAlwaysAvailable' => $originalType->defaultAlwaysAvailable, + 'defaultSortField' => $originalType->defaultSortField, + 'defaultSortOrder' => $originalType->defaultSortOrder, + 'contentTypeGroups' => $originalType->contentTypeGroups, + 'fieldDefinitions' => $originalType->fieldDefinitions, + ], + $typeDraft + ); + + $this->assertInstanceOf( + 'DateTime', + $typeDraft->modificationDate + ); + $modificationDifference = $originalType->modificationDate->diff( + $typeDraft->modificationDate + ); + // No modification date is newer, interval is not inverted + $this->assertEquals(0, $modificationDifference->invert); + + $this->assertEquals( + ContentType::STATUS_DRAFT, + $typeDraft->status + ); + + return $data; + } + + /** + * Test for the createContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentTypeDraft() + * @depends testCreateContentTypeDraftStructValues + */ + public function testCreateContentTypeDraftStructLanguageDependentValues(array $data) + { + $originalType = $data['originalType']; + $typeDraft = $data['typeDraft']; + + $this->assertEquals( + [ + 'names' => $originalType->names, + 'descriptions' => $originalType->descriptions, + ], + [ + 'names' => $typeDraft->names, + 'descriptions' => $typeDraft->descriptions, + ] + ); + } + + /** + * Test for the createContentTypeDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentTypeDraft() + * @depends testCreateContentTypeDraft + */ + public function testCreateContentTypeDraftThrowsBadStateException() + { + $this->expectException(BadStateException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $commentType = $contentTypeService->loadContentTypeByIdentifier('comment'); + + $contentTypeService->createContentTypeDraft($commentType); + + // Throws exception, since type draft already exists + $contentTypeService->createContentTypeDraft($commentType); + /* END: Use Case */ + } + + /** + * Test for the deleteContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::deleteContentType() + * @depends testLoadContentTypeByIdentifier + */ + public function testDeleteContentType() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $commentType = $contentTypeService->loadContentTypeByIdentifier('comment'); + + $contentTypeService->deleteContentType($commentType); + /* END: Use Case */ + + $contentTypeService->loadContentType($commentType->id); + $this->fail('Content type could be loaded after delete.'); + } + + /** + * Test for the deleteContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::deleteContentType() + * @depends testDeleteContentType + */ + public function testDeleteContentTypeThrowsBadStateException() + { + $this->expectException(BadStateException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier('user'); + + // This call will fail with a "BadStateException" because there is at + // least on content object of type "user" in an Ibexa demo + $contentTypeService->deleteContentType($contentType); + /* END: Use Case */ + } + + /** + * Test for the copyContentType() method. + * + * @return array + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::copyContentType() + * @depends testLoadContentTypeByIdentifier + */ + public function testCopyContentType() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $commentType = $contentTypeService->loadContentTypeByIdentifier('comment'); + $contentTypeGroup = $commentType->contentTypeGroups[0]; + $contentTypes = $contentTypeService->loadContentTypes($contentTypeGroup); + $contentTypesCount = count($contentTypes); + + // Complete copy of the "comment" type + $copiedType = $contentTypeService->copyContentType($commentType); + + $contentTypes = $contentTypeService->loadContentTypes($contentTypeGroup); + $contentTypeIdentifiers = array_map(static function (ContentType $contentType) { + return $contentType->identifier; + }, $contentTypes); + /* END: Use Case */ + + $this->assertInstanceOf( + ContentType::class, + $copiedType + ); + + $this->assertContains($commentType->identifier, $contentTypeIdentifiers); + $this->assertContains($copiedType->identifier, $contentTypeIdentifiers); + $this->assertCount($contentTypesCount + 1, $contentTypes); + + $originalType = $contentTypeService->loadContentTypeByIdentifier('comment'); + + return [ + 'originalType' => $originalType, + 'copiedType' => $copiedType, + ]; + } + + /** + * Test for the copyContentType() method. + * + * @param array $data + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::copyContentType() + * @depends testCopyContentType + */ + public function testCopyContentTypeStructValues(array $data) + { + $originalType = $data['originalType']; + $copiedType = $data['copiedType']; + + $this->assertCopyContentTypeValues($originalType, $copiedType); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $originalType + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $copiedType + * @param array $excludedProperties + */ + private function assertCopyContentTypeValues($originalType, $copiedType, $excludedProperties = []) + { + $allProperties = [ + 'names', + 'descriptions', + 'creatorId', + 'modifierId', + 'urlAliasSchema', + 'nameSchema', + 'isContainer', + 'mainLanguageCode', + 'contentTypeGroups', + ]; + $properties = array_diff($allProperties, $excludedProperties); + $this->assertStructPropertiesCorrect( + $originalType, + $copiedType, + $properties + ); + + $this->assertNotEquals( + $originalType->id, + $copiedType->id + ); + $this->assertNotEquals( + $originalType->remoteId, + $copiedType->remoteId + ); + $this->assertNotEquals( + $originalType->identifier, + $copiedType->identifier + ); + $this->assertNotEquals( + $originalType->creationDate, + $copiedType->creationDate + ); + $this->assertNotEquals( + $originalType->modificationDate, + $copiedType->modificationDate + ); + + foreach ($originalType->fieldDefinitions as $originalFieldDefinition) { + $copiedFieldDefinition = $copiedType->getFieldDefinition( + $originalFieldDefinition->identifier + ); + + $this->assertStructPropertiesCorrect( + $originalFieldDefinition, + $copiedFieldDefinition, + [ + 'identifier', + 'names', + 'descriptions', + 'fieldGroup', + 'position', + 'fieldTypeIdentifier', + 'isTranslatable', + 'isRequired', + 'isInfoCollector', + 'validatorConfiguration', + 'defaultValue', + 'isSearchable', + ] + ); + $this->assertNotEquals( + $originalFieldDefinition->id, + $copiedFieldDefinition->id + ); + } + } + + /** + * Test for the copyContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::copyContentType($contentType, $user) + * @depends testCopyContentType + */ + public function testCopyContentTypeWithSecondParameter() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $user = $this->createUserVersion1(); + + $commentType = $contentTypeService->loadContentTypeByIdentifier('comment'); + + // Complete copy of the "comment" type + $copiedType = $contentTypeService->copyContentType($commentType, $user); + /* END: Use Case */ + + $this->assertPropertiesCorrect( + [ + 'creatorId' => $user->id, + 'modifierId' => $user->id, + ], + $copiedType + ); + $this->assertCopyContentTypeValues($commentType, $copiedType, ['creatorId', 'modifierId']); + } + + /** + * Test for the assignContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::assignContentTypeGroup() + * @depends testLoadContentTypeGroupByIdentifier + * @depends testLoadContentTypeByIdentifier + * @depends testLoadContentType + */ + public function testAssignContentTypeGroup() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $mediaGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Media'); + $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); + + $contentTypeService->assignContentTypeGroup($folderType, $mediaGroup); + /* END: Use Case */ + + $loadedType = $contentTypeService->loadContentType($folderType->id); + + foreach ($loadedType->contentTypeGroups as $loadedGroup) { + if ($mediaGroup->id == $loadedGroup->id) { + return; + } + } + $this->fail( + sprintf( + 'Group with ID "%s" not assigned to content type.', + $mediaGroup->id + ) + ); + } + + /** + * Test for the assignContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::assignContentTypeGroup() + * @depends testAssignContentTypeGroup + */ + public function testAssignContentTypeGroupThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); + $assignedGroups = $folderType->contentTypeGroups; + + foreach ($assignedGroups as $assignedGroup) { + // Throws an exception, since group is already assigned + $contentTypeService->assignContentTypeGroup($folderType, $assignedGroup); + } + /* END: Use Case */ + } + + /** + * Test for the unassignContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::unassignContentTypeGroup() + * @depends testAssignContentTypeGroup + */ + public function testUnassignContentTypeGroup() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); + + $mediaGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Media'); + $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); + + // May not unassign last group + $contentTypeService->assignContentTypeGroup($folderType, $mediaGroup); + + $contentTypeService->unassignContentTypeGroup($folderType, $contentGroup); + /* END: Use Case */ + + $loadedType = $contentTypeService->loadContentType($folderType->id); + + foreach ($loadedType->contentTypeGroups as $assignedGroup) { + if ($assignedGroup->id == $contentGroup->id) { + $this->fail( + sprintf( + 'Could not unassign group with ID "%s".', + $assignedGroup->id + ) + ); + } + } + } + + /** + * Test for the unassignContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::unassignContentTypeGroup() + * @depends testUnassignContentTypeGroup + */ + public function testUnassignContentTypeGroupThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); + $notAssignedGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Media'); + + // Throws an exception, since "Media" group is not assigned to "folder" + $contentTypeService->unassignContentTypeGroup($folderType, $notAssignedGroup); + /* END: Use Case */ + } + + /** + * Test for the unassignContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::unassignContentTypeGroup() + * @depends testUnassignContentTypeGroup + */ + public function testUnassignContentTypeGroupThrowsBadStateException() + { + $this->expectException(BadStateException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); + $assignedGroups = $folderType->contentTypeGroups; + + foreach ($assignedGroups as $assignedGroup) { + // Throws an exception, when last group is to be removed + $contentTypeService->unassignContentTypeGroup($folderType, $assignedGroup); + } + /* END: Use Case */ + } + + /** + * Test for the createContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentTypeGroup() + * @depends testLoadContentTypeGroup + * @depends testCreateContentTypeGroup + */ + public function testCreateContentTypeGroupInTransactionWithRollback() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Get create struct and set language property + $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct('new-group'); + /* @todo uncomment when support for multilingual names and descriptions is added + $groupCreate->mainLanguageCode = 'eng-GB'; + */ + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Create the new content type group + $groupId = $contentTypeService->createContentTypeGroup($groupCreate)->id; + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes + $repository->rollback(); + + try { + // This call will fail with a "NotFoundException" + $contentTypeService->loadContentTypeGroup($groupId); + } catch (NotFoundException $e) { + return; + } + /* END: Use Case */ + + $this->fail('Can still load content type group after rollback'); + } + + /** + * Test for the createContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentTypeGroup() + * @depends testLoadContentTypeGroup + * @depends testCreateContentTypeGroup + */ + public function testCreateContentTypeGroupInTransactionWithCommit() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Get create struct and set language property + $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct('new-group'); + /* @todo uncomment when support for multilingual names and descriptions is added + $groupCreate->mainLanguageCode = 'eng-GB'; + */ + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Create the new content type group + $groupId = $contentTypeService->createContentTypeGroup($groupCreate)->id; + + // Rollback all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Load created content type group + $group = $contentTypeService->loadContentTypeGroup($groupId); + /* END: Use Case */ + + $this->assertEquals($groupId, $group->id); + } + + /** + * Test for the updateContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeGroup() + * @depends testUpdateContentTypeGroup + * @depends testLoadContentTypeGroupByIdentifier + */ + public function testUpdateContentTypeGroupInTransactionWithRollback() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Load an existing group + $group = $contentTypeService->loadContentTypeGroupByIdentifier('Setup'); + + // Get an update struct and change the identifier + $groupUpdate = $contentTypeService->newContentTypeGroupUpdateStruct(); + $groupUpdate->identifier = 'Teardown'; + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Apply update to group + $contentTypeService->updateContentTypeGroup($group, $groupUpdate); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes + $repository->rollback(); + + // Load updated group, it will be unchanged + $updatedGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Setup'); + /* END: Use Case */ + + $this->assertEquals('Setup', $updatedGroup->identifier); + } + + /** + * Test for the updateContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeGroup() + * @depends testUpdateContentTypeGroup + * @depends testLoadContentTypeGroupByIdentifier + */ + public function testUpdateContentTypeGroupInTransactionWithCommit() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Load an existing group + $group = $contentTypeService->loadContentTypeGroupByIdentifier('Setup'); + + // Get an update struct and change the identifier + $groupUpdate = $contentTypeService->newContentTypeGroupUpdateStruct(); + $groupUpdate->identifier = 'Teardown'; + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Apply update to group + $contentTypeService->updateContentTypeGroup($group, $groupUpdate); + + // Commit all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Load updated group by it's new identifier "Teardown" + $updatedGroup = $contentTypeService->loadContentTypeGroupByIdentifier( + 'Teardown' + ); + /* END: Use Case */ + + $this->assertEquals('Teardown', $updatedGroup->identifier); + } + + /** + * Test for the deleteContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::deleteContentTypeGroup() + * @depends testDeleteContentTypeGroup + * @depends testLoadContentTypeGroupByIdentifierThrowsNotFoundException + */ + public function testDeleteContentTypeGroupWithRollback() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Get a group create struct + $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct( + 'new-group' + ); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Create the new group + $group = $contentTypeService->createContentTypeGroup($groupCreate); + + // Delete the currently created group + $contentTypeService->deleteContentTypeGroup($group); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes + $repository->rollback(); + + try { + // This call will fail with an "NotFoundException" + $contentTypeService->loadContentTypeGroupByIdentifier('new-group'); + } catch (NotFoundException $e) { + // Expected error path + } + /* END: Use Case */ + + $this->assertTrue(isset($e), 'Group not deleted after rollback'); + } + + /** + * Test for the deleteContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::deleteContentTypeGroup() + * @depends testDeleteContentTypeGroup + * @depends testLoadContentTypeGroupByIdentifierThrowsNotFoundException + */ + public function testDeleteContentTypeGroupWithCommit() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Get a group create struct + $groupCreate = $contentTypeService->newContentTypeGroupCreateStruct( + 'new-group' + ); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Create the new group + $group = $contentTypeService->createContentTypeGroup($groupCreate); + + // Delete the currently created group + $contentTypeService->deleteContentTypeGroup($group); + + // Commit all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + try { + // This call will fail with an "NotFoundException" + $contentTypeService->loadContentTypeGroupByIdentifier('new-group'); + } catch (NotFoundException $e) { + // Expected error path + } + /* END: Use Case */ + + $this->assertTrue(isset($e), 'Group not deleted after commit.'); + } + + /** + * Test for the createContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentType() + * @depends testCreateContentType + * @depends testLoadContentTypeByIdentifierThrowsNotFoundException + */ + public function testCreateContentTypeInTransactionWithRollback() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Get create struct and set some properties + $typeCreate = $contentTypeService->newContentTypeCreateStruct('blog-post'); + $typeCreate->mainLanguageCode = 'eng-GB'; + $typeCreate->names = ['eng-GB' => 'Blog post']; + + $titleFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); + $titleFieldCreate->names = ['eng-GB' => 'Title']; + $titleFieldCreate->position = 1; + $typeCreate->addFieldDefinition($titleFieldCreate); + + $groups = [ + $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), + ]; + + // Create content type + $contentTypeDraft = $contentTypeService->createContentType( + $typeCreate, + $groups + ); + + // Publish the content type draft + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes. + $repository->rollback(); + + try { + // This call will fail with a "NotFoundException" + $contentTypeService->loadContentTypeByIdentifier('blog-post'); + } catch (NotFoundException $e) { + // Expected execution path + } + /* END: Use Case */ + + $this->assertTrue(isset($e), 'Can still load content type after rollback.'); + } + + /** + * Test for the createContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::createContentType() + * @depends testCreateContentType + * @depends testLoadContentTypeByIdentifierThrowsNotFoundException + */ + public function testCreateContentTypeInTransactionWithCommit() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Get create struct and set some properties + $typeCreate = $contentTypeService->newContentTypeCreateStruct('blog-post'); + $typeCreate->mainLanguageCode = 'eng-GB'; + $typeCreate->names = ['eng-GB' => 'Blog post']; + + $titleFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); + $titleFieldCreate->names = ['eng-GB' => 'Title']; + $titleFieldCreate->position = 1; + $typeCreate->addFieldDefinition($titleFieldCreate); + + $groups = [ + $contentTypeService->loadContentTypeGroupByIdentifier('Setup'), + ]; + + // Create content type + $contentTypeDraft = $contentTypeService->createContentType( + $typeCreate, + $groups + ); + + // Publish the content type draft + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + // Commit all changes. + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Load the newly created content type + $contentType = $contentTypeService->loadContentTypeByIdentifier('blog-post'); + /* END: Use Case */ + + $this->assertEquals($contentTypeDraft->id, $contentType->id); + } + + /** + * Test for the copyContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::copyContentType() + * @depends testCopyContentType + * @depends testLoadContentTypeByIdentifier + * @depends testLoadContentTypeThrowsNotFoundException + */ + public function testCopyContentTypeInTransactionWithRollback() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Load content type to copy + $contentType = $contentTypeService->loadContentTypeByIdentifier('comment'); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Complete copy of the content type + $copiedType = $contentTypeService->copyContentType($contentType); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes + $repository->rollback(); + + try { + // This call will fail with a "NotFoundException" + $contentTypeService->loadContentType($copiedType->id); + } catch (NotFoundException $e) { + // Expected execution path + } + /* END: Use Case */ + + $this->assertTrue(isset($e), 'Can still load copied content type after rollback.'); + } + + /** + * Test for the copyContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::copyContentType() + * @depends testCopyContentType + * @depends testLoadContentTypeByIdentifier + * @depends testLoadContentTypeThrowsNotFoundException + */ + public function testCopyContentTypeInTransactionWithCommit() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Load content type to copy + $contentType = $contentTypeService->loadContentTypeByIdentifier('comment'); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Complete copy of the content type + $contentTypeId = $contentTypeService->copyContentType($contentType)->id; + + // Commit all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Load the new content type copy. + $copiedContentType = $contentTypeService->loadContentType($contentTypeId); + /* END: Use Case */ + + $this->assertEquals($contentTypeId, $copiedContentType->id); + } + + /** + * Test for the deleteContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::deleteContentType() + * @depends testCopyContentType + * @depends testLoadContentTypeByIdentifierThrowsNotFoundException + */ + public function testDeleteContentTypeInTransactionWithRollback() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Load content type to copy + $contentType = $contentTypeService->loadContentTypeByIdentifier('comment'); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Delete the "comment" content type. + $contentTypeService->deleteContentType($contentType); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes + $repository->rollback(); + + // Load currently deleted and rollbacked content type + $commentType = $contentTypeService->loadContentTypeByIdentifier('comment'); + /* END: Use Case */ + + $this->assertEquals('comment', $commentType->identifier); + } + + /** + * Test for the deleteContentType() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::deleteContentType() + * @depends testCopyContentType + * @depends testLoadContentTypeByIdentifierThrowsNotFoundException + */ + public function testDeleteContentTypeInTransactionWithCommit() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + // Load content type to copy + $contentType = $contentTypeService->loadContentTypeByIdentifier('comment'); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Delete the "comment" content type. + $contentTypeService->deleteContentType($contentType); + + // Commit all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + try { + // This call will fail with a "NotFoundException" + $contentTypeService->loadContentTypeByIdentifier('comment'); + } catch (NotFoundException $e) { + // Expected execution path + } + /* END: Use Case */ + + $this->assertTrue(isset($e), 'Can still load content type after rollback.'); + } + + /** + * Test for the assignContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::assignContentTypeGroup() + * @depends testAssignContentTypeGroup + */ + public function testAssignContentTypeGroupInTransactionWithRollback() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $mediaGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Media'); + $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Assign group to content type + $contentTypeService->assignContentTypeGroup($folderType, $mediaGroup); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes + $repository->rollback(); + + // Load all content types assigned to media group + $contentTypes = $contentTypeService->loadContentTypes($mediaGroup); + + $contentTypeIds = []; + foreach ($contentTypes as $contentType) { + $contentTypeIds[] = $contentType->id; + } + /* END: Use Case */ + + $this->assertFalse( + in_array($folderType->id, $contentTypeIds), + 'Folder content type is still in media group after rollback.' + ); + } + + /** + * Test for the assignContentTypeGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::assignContentTypeGroup() + * @depends testAssignContentTypeGroup + */ + public function testAssignContentTypeGroupInTransactionWithCommit() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $mediaGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Media'); + $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Assign group to content type + $contentTypeService->assignContentTypeGroup($folderType, $mediaGroup); + + // Commit all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Load all content types assigned to media group + $contentTypes = $contentTypeService->loadContentTypes($mediaGroup); + + $contentTypeIds = []; + foreach ($contentTypes as $contentType) { + $contentTypeIds[] = $contentType->id; + } + /* END: Use Case */ + + $this->assertTrue( + in_array($folderType->id, $contentTypeIds), + 'Folder content type not in media group after commit.' + ); + } + + /** + * Test for the isContentTypeUsed() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::isContentTypeUsed() + */ + public function testIsContentTypeUsed() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + + $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); + $eventType = $contentTypeService->loadContentTypeByIdentifier('event'); + + $isFolderUsed = $contentTypeService->isContentTypeUsed($folderType); + $isEventUsed = $contentTypeService->isContentTypeUsed($eventType); + /* END: Use Case */ + + $this->assertTrue($isFolderUsed); + $this->assertFalse($isEventUsed); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::removeContentTypeTranslation + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testRemoveContentTypeTranslation() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $contentTypeDraft = $this->createContentTypeDraft(); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); + + $this->assertEquals( + [ + 'eng-US' => 'Blog post', + 'ger-DE' => 'Blog-Eintrag', + ], + $contentType->getNames() + ); + + $contentTypeService->removeContentTypeTranslation( + $contentTypeService->createContentTypeDraft($contentType), + 'ger-DE' + ); + + $loadedContentTypeDraft = $contentTypeService->loadContentTypeDraft($contentType->id); + + $this->assertArrayNotHasKey('ger-DE', $loadedContentTypeDraft->getNames()); + $this->assertArrayNotHasKey('ger-DE', $loadedContentTypeDraft->getDescriptions()); + + foreach ($loadedContentTypeDraft->fieldDefinitions as $fieldDefinition) { + $this->assertArrayNotHasKey('ger-DE', $fieldDefinition->getNames()); + $this->assertArrayNotHasKey('ger-DE', $fieldDefinition->getDescriptions()); + } + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::removeContentTypeTranslation + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testRemoveContentTypeTranslationWithMultilingualData() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $selectionFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('selection', 'ezselection'); + + $selectionFieldCreate->names = [ + 'eng-US' => 'Selection', + 'ger-DE' => 'GER Selection', + ]; + + $selectionFieldCreate->fieldGroup = 'blog-content'; + $selectionFieldCreate->position = 3; + $selectionFieldCreate->isTranslatable = true; + $selectionFieldCreate->isRequired = true; + $selectionFieldCreate->isInfoCollector = false; + $selectionFieldCreate->validatorConfiguration = []; + $selectionFieldCreate->fieldSettings = [ + 'multilingualOptions' => [ + 'eng-US' => [ + 0 => 'A first', + 1 => 'Bielefeld', + 2 => 'Sindelfingen', + 3 => 'Turtles', + 4 => 'Zombies', + ], + 'ger-DE' => [ + 0 => 'Berlin', + 1 => 'Cologne', + 2 => 'Bonn', + 3 => 'Frankfurt', + 4 => 'Hamburg', + ], + ], + ]; + $selectionFieldCreate->isSearchable = false; + + $contentTypeDraft = $this->createContentTypeDraft([$selectionFieldCreate]); + + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); + + $contentTypeService->removeContentTypeTranslation( + $contentTypeService->createContentTypeDraft($contentType), + 'ger-DE' + ); + + $loadedContentTypeDraft = $contentTypeService->loadContentTypeDraft($contentType->id); + + $fieldDefinition = $loadedContentTypeDraft->getFieldDefinition('selection'); + $this->assertArrayNotHasKey('ger-DE', $fieldDefinition->fieldSettings['multilingualOptions']); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::updateContentTypeDraft + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testUpdateContentTypeDraftWithNewTranslationWithMultilingualData() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $selectionFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('selection', 'ezselection'); + + $selectionFieldCreate->names = [ + 'eng-US' => 'Selection', + 'ger-DE' => 'GER Selection', + ]; + + $selectionFieldCreate->fieldGroup = 'blog-content'; + $selectionFieldCreate->position = 3; + $selectionFieldCreate->isTranslatable = true; + $selectionFieldCreate->isRequired = true; + $selectionFieldCreate->isInfoCollector = false; + $selectionFieldCreate->validatorConfiguration = []; + $selectionFieldCreate->fieldSettings = [ + 'multilingualOptions' => [ + 'eng-US' => [ + 0 => 'A first', + 1 => 'Bielefeld', + 2 => 'Sindelfingen', + 3 => 'Turtles', + 4 => 'Zombies', + ], + 'ger-DE' => [ + 0 => 'Berlin', + 1 => 'Cologne', + 2 => 'Bonn', + 3 => 'Frankfurt', + 4 => 'Hamburg', + ], + ], + ]; + $selectionFieldCreate->isSearchable = false; + + $contentTypeDraft = $this->createContentTypeDraft([$selectionFieldCreate]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); + // sanity check + self::assertEquals( + ['eng-US', 'ger-DE'], + array_keys($contentType->getNames()) + ); + + $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); + $updateStruct = $contentTypeService->newContentTypeUpdateStruct(); + $updateStruct->names = [ + 'eng-GB' => 'BrE blog post', + ]; + + $selectionFieldUpdate = $contentTypeService->newFieldDefinitionUpdateStruct(); + + $selectionFieldUpdate->names = [ + 'eng-GB' => 'GB Selection', + ]; + + $selectionFieldUpdate->fieldGroup = 'blog-content'; + $selectionFieldUpdate->position = 3; + $selectionFieldUpdate->isTranslatable = true; + $selectionFieldUpdate->isRequired = true; + $selectionFieldUpdate->isInfoCollector = false; + $selectionFieldUpdate->validatorConfiguration = []; + $selectionFieldUpdate->fieldSettings = [ + 'multilingualOptions' => [ + 'eng-US' => [ + 0 => 'A first', + 1 => 'Bielefeld', + 2 => 'Sindelfingen', + 3 => 'Turtles', + 4 => 'Zombies', + ], + 'ger-DE' => [ + 0 => 'Berlin', + 1 => 'Cologne', + 2 => 'Bonn', + 3 => 'Frankfurt', + 4 => 'Hamburg', + ], + 'eng-GB' => [ + 0 => 'London', + 1 => 'Liverpool', + ], + ], + ]; + $selectionFieldUpdate->isSearchable = false; + + $contentTypeService->updateFieldDefinition( + $contentTypeDraft, + $contentType->getFieldDefinition('selection'), + $selectionFieldUpdate + ); + $contentTypeService->updateContentTypeDraft($contentTypeDraft, $updateStruct); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + $loadedFieldDefinition = $contentTypeService->loadContentType($contentType->id)->getFieldDefinition('selection'); + self::assertEquals( + [ + 'eng-US' => [ + 0 => 'A first', + 1 => 'Bielefeld', + 2 => 'Sindelfingen', + 3 => 'Turtles', + 4 => 'Zombies', + ], + 'ger-DE' => [ + 0 => 'Berlin', + 1 => 'Cologne', + 2 => 'Bonn', + 3 => 'Frankfurt', + 4 => 'Hamburg', + ], + 'eng-GB' => [ + 0 => 'London', + 1 => 'Liverpool', + ], + ], + $loadedFieldDefinition->fieldSettings['multilingualOptions'] + ); + } + + /** + * Test for the deleteUserDrafts() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentTypeService::deleteUserDrafts() + */ + public function testDeleteUserDrafts() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + $permissionResolver = $repository->getPermissionResolver(); + $contentTypeService = $repository->getContentTypeService(); + + $draft = $this->createContentTypeDraft(); + $user = $permissionResolver->getCurrentUserReference(); + + $contentTypeService->deleteUserDrafts($user->getUserId()); + $contentTypeDraft = $contentTypeService->loadContentTypeDraft($draft->id); + } +} + +class_alias(ContentTypeServiceTest::class, 'eZ\Publish\API\Repository\Tests\ContentTypeServiceTest'); diff --git a/tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php b/tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php new file mode 100644 index 0000000000..a2077819fb --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/AuthorIntegrationTest.php @@ -0,0 +1,536 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\FieldType\Author\Author; +use Ibexa\Core\FieldType\Author\AuthorCollection; +use Ibexa\Core\FieldType\Author\Type; +use Ibexa\Core\FieldType\Author\Value as AuthorValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class AuthorIntegrationTest extends SearchMultivaluedBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezauthor'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return [ + 'defaultAuthor' => [ + 'type' => 'choice', + 'default' => Type::DEFAULT_VALUE_EMPTY, + ], + ]; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return [ + 'defaultAuthor' => Type::DEFAULT_VALUE_EMPTY, + ]; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return []; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return []; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'unknown' => ['value' => 42], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return \Ibexa\Core\FieldType\Author\Value + */ + public function getValidCreationFieldData() + { + return new AuthorValue( + [ + new Author( + [ + 'id' => 23, + 'name' => 'Hans Mueller', + 'email' => 'hans@example.com', + ] + ), + ] + ); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'Hans Mueller'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + AuthorValue::class, + $field->value + ); + + $expectedData = [ + 'authors' => new AuthorCollection( + [ + new Author( + [ + 'id' => 23, + 'name' => 'Hans Mueller', + 'email' => 'hans@example.com', + ] + ), + ] + ), + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get field data which will result in errors during creation. + * + * This is a PHPUnit data provider. + * + * The returned records must contain of an error producing data value and + * the expected exception class (from the API or SPI, not implementation + * specific!) as the second element. For example: + * + * <code> + * [ + * [ + * new DoomedValue( true ), + * ContentValidationException::class + * ], + * // ... + * ]; + * </code> + * + * @return array[] + */ + public function provideInvalidCreationFieldData() + { + return [ + ['Sindelfingen', InvalidArgumentException::class], + ]; + } + + /** + * Get update field externals data. + * + * @return \Ibexa\Core\FieldType\Author\Value + */ + public function getValidUpdateFieldData() + { + return new AuthorValue( + [ + new Author( + [ + 'id' => 42, + 'name' => 'Lieschen Mueller', + 'email' => 'lieschen@example.com', + ] + ), + ] + ); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + AuthorValue::class, + $field->value + ); + + $expectedData = [ + 'authors' => new AuthorCollection( + [ + new Author( + [ + 'id' => 42, + 'name' => 'Lieschen Mueller', + 'email' => 'lieschen@example.com', + ] + ), + ] + ), + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + AuthorValue::class, + $field->value + ); + + $expectedData = [ + 'authors' => new AuthorCollection( + [ + new Author( + [ + 'id' => 23, + 'name' => 'Hans Mueller', + 'email' => 'hans@example.com', + ] + ), + ] + ), + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new AuthorValue( + [ + new Author( + [ + 'id' => 23, + 'name' => 'Hans Mueller', + 'email' => 'hans@example.com', + ] + ), + ] + ), + [ + [ + 'id' => 23, + 'name' => 'Hans Mueller', + 'email' => 'hans@example.com', + ], + ], + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + [ + [ + 'id' => 23, + 'name' => 'Hans Mueller', + 'email' => 'hans@example.com', + ], + ], + new AuthorValue( + [ + new Author( + [ + 'id' => 23, + 'name' => 'Hans Mueller', + 'email' => 'hans@example.com', + ] + ), + ] + ), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new AuthorValue()], + [new AuthorValue([])], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + [ + new AuthorValue( + [ + new Author( + [ + 'id' => 23, + 'name' => 'Hans Mueller', + 'email' => 'hans@example.com', + ] + ), + ] + ), + ], + ]; + } + + protected function getValidSearchValueOne() + { + return [ + new Author( + [ + 'id' => 2, + 'name' => 'Ferdinand', + 'email' => 'ferdinand@example.com', + ] + ), + ]; + } + + protected function getValidSearchValueTwo() + { + return [ + new Author( + [ + 'id' => 3, + 'name' => 'Greta', + 'email' => 'greta@example.com', + ] + ), + ]; + } + + protected function getSearchTargetValueOne() + { + return 'Ferdinand'; + } + + protected function getSearchTargetValueTwo() + { + return 'Greta'; + } + + protected function getAdditionallyIndexedFieldData() + { + return [ + [ + 'id', + 2, + 3, + ], + [ + 'email', + 'ferdinand@example.com', + 'greta@example.com', + ], + [ + 'sort_value', + 'Ferdinand', + 'Greta', + ], + ]; + } + + protected function getValidMultivaluedSearchValuesOne() + { + return [ + new Author( + [ + 'id' => 1, + 'name' => 'Antoinette', + 'email' => 'antoinette@example.com', + ] + ), + new Author( + [ + 'id' => 2, + 'name' => 'Ferdinand', + 'email' => 'ferdinand@example.com', + ] + ), + ]; + } + + protected function getValidMultivaluedSearchValuesTwo() + { + return [ + new Author( + [ + 'id' => 3, + 'name' => 'Greta', + 'email' => 'greta@example.com', + ] + ), + new Author( + [ + 'id' => 4, + 'name' => 'Leopold', + 'email' => 'leopold@example.com', + ] + ), + new Author( + [ + 'id' => 5, + 'name' => 'Maximilian', + 'email' => 'maximilian@example.com', + ] + ), + ]; + } + + protected function getMultivaluedSearchTargetValuesOne() + { + return ['Antoinette', 'Ferdinand']; + } + + protected function getMultivaluedSearchTargetValuesTwo() + { + return ['Greta', 'Leopold', 'Maximilian']; + } + + protected function getAdditionallyIndexedMultivaluedFieldData() + { + return [ + [ + 'id', + [1, 2], + [3, 4, 5], + ], + [ + 'email', + ['antoinette@example.com', 'ferdinand@example.com'], + ['greta@example.com', 'leopold@example.com', 'maximilian@example.com'], + ], + ]; + } + + protected function getFullTextIndexedFieldData() + { + return [ + ['Ferdinand', 'Greta'], + ]; + } +} + +class_alias(AuthorIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\AuthorIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php b/tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php new file mode 100644 index 0000000000..8e3af2ead9 --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/BaseIntegrationTest.php @@ -0,0 +1,1220 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository; +use Ibexa\Contracts\Core\Repository\Exceptions\ContentTypeFieldDefinitionValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; + +/** + * Integration test for legacy storage field types. + * + * This abstract base test case is supposed to be the base for field type + * integration tests. It basically calls all involved methods in the field type + * ``Converter`` and ``Storage`` implementations. Fo get it working implement + * the abstract methods in a sensible way. + * + * The following actions are performed by this test using the custom field + * type: + * + * - Create a new content type with the given field type + * - Load created content type + * - Create content object of new content type + * - Load created content + * - Publish created content + * - Update content + * - Copy created content + * - Remove copied content + * - Test toHash + * - Test fromHash + * + * @group integration + * @group field-type + * + * @todo Finalize dependencies to other tests (including groups!) + */ +abstract class BaseIntegrationTest extends BaseTest +{ + /** + * Content version archive limit (default). + * Note: currently there is no way to retrieve this setting from the ContentService. + */ + public const VERSION_ARCHIVE_LIMIT = 5; + + /** + * Identifier of the custom field. + * + * @var string + */ + protected $customFieldIdentifier = 'data'; + + /** + * Get name of tested field type. + * + * @return string + */ + abstract public function getTypeName(); + + /** + * Get expected settings schema. + * + * @return array + */ + abstract public function getSettingsSchema(); + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + abstract public function getValidFieldSettings(); + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + abstract public function getInvalidFieldSettings(); + + /** + * Get expected validator schema. + * + * @return array + */ + abstract public function getValidatorSchema(); + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + abstract public function getValidValidatorConfiguration(); + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + abstract public function getInvalidValidatorConfiguration(); + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + abstract public function getValidCreationFieldData(); + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + abstract public function getFieldName(); + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + abstract public function assertFieldDataLoadedCorrect(Field $field); + + /** + * Get field data which will result in errors during creation. + * + * This is a PHPUnit data provider. + * + * The returned records must contain of an error producing data value and + * the expected exception class (from the API or SPI, not implementation + * specific!) as the second element. For example: + * + * <code> + * [ + * [ + * new DoomedValue( true ), + * ContentValidationException::class + * ], + * // ... + * ]; + * </code> + * + * @return array[] + */ + abstract public function provideInvalidCreationFieldData(); + + /** + * Get valid field data for updating content. + * + * @return mixed + */ + abstract public function getValidUpdateFieldData(); + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidUpdateFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + abstract public function assertUpdatedFieldDataLoadedCorrect(Field $field); + + /** + * Get field data which will result in errors during update. + * + * This is a PHPUnit data provider. + * + * The returned records must contain of an error producing data value and + * the expected exception class (from the API or SPI, not implementation + * specific!) as the second element. For example: + * + * <code> + * [ + * [ + * new DoomedValue( true ), + * ContentValidationException::class + * ], + * // ... + * ]; + * </code> + * + * @return array[] + */ + abstract public function provideInvalidUpdateFieldData(); + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + abstract public function assertCopiedFieldDataLoadedCorrectly(Field $field); + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + abstract public function provideToHashData(); + + /** + * Get hashes and their respective converted values. + * + * This is a PHPUnit data provider + * + * The returned records must have the the input hash assigned to the + * first index and the expected value result to the second. For example: + * + * <code> + * array( + * array( + * array( 'myValue' => true ), + * new MyValue( true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + abstract public function provideFromHashData(); + + /** + * Method called after content creation. + * + * Useful, if additional stuff should be executed (like creating the actual + * user). + * + * We cannot just overwrite the testCreateContent method, since this messes + * up PHPUnits @depends sorting of tests, so everything will be skipped. + * + * @param \Ibexa\Contracts\Core\Repository $repository + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function postCreationHook(Repository\Repository $repository, Repository\Values\Content\Content $content) + { + // Do nothing by default + } + + public function getValidContentTypeConfiguration(): array + { + return []; + } + + public function getValidFieldConfiguration(): array + { + return []; + } + + public function testCreateContentType() + { + $contentType = $this->createContentType( + $this->getValidFieldSettings(), + $this->getValidValidatorConfiguration(), + $this->getValidContentTypeConfiguration(), + $this->getValidFieldConfiguration() + ); + + $this->assertNotNull($contentType->id); + + return $contentType; + } + + /** + * For checking if field type can be used in name/url schema (pattern). + * + * @return bool + */ + protected function checkSupportGetName() + { + return true; + } + + /** + * Creates a content type under test with $fieldSettings and + * $validatorConfiguration. + * + * $typeCreateOverride and $fieldCreateOverride can be used to selectively + * override settings on the type create struct and field create struct. + * + * @param mixed $fieldSettings + * @param mixed $validatorConfiguration + * @param array $typeCreateOverride + * @param array $fieldCreateOverride + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + */ + protected function createContentType($fieldSettings, $validatorConfiguration, array $typeCreateOverride = [], array $fieldCreateOverride = []) + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $contentTypeIdentifier = 'test-' . $this->getTypeName(); + + try { + return $contentTypeService->loadContentTypeByIdentifier($contentTypeIdentifier); + } catch (NotFoundException $e) { + // Move on to creating content type + } + + $createStruct = $contentTypeService->newContentTypeCreateStruct( + $contentTypeIdentifier + ); + $createStruct->mainLanguageCode = $this->getOverride('mainLanguageCode', $typeCreateOverride, 'eng-GB'); + $createStruct->remoteId = $this->getTypeName(); + $createStruct->names = $this->getOverride('names', $typeCreateOverride, ['eng-GB' => 'Test']); + $createStruct->creatorId = 14; + $createStruct->creationDate = $this->createDateTime(); + + if ($this->checkSupportGetName()) { + $createStruct->nameSchema = '<name> <data>'; + $createStruct->urlAliasSchema = '<data>'; + } + + $nameFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('name', 'ezstring'); + $nameFieldCreate->names = ['eng-GB' => 'Title']; + $nameFieldCreate->fieldGroup = 'main'; + $nameFieldCreate->position = 1; + $nameFieldCreate->isTranslatable = true; + $createStruct->addFieldDefinition($nameFieldCreate); + + $dataFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('data', $this->getTypeName()); + $dataFieldCreate->names = $this->getOverride('names', $fieldCreateOverride, ['eng-GB' => 'Title']); + $dataFieldCreate->fieldGroup = 'main'; + $dataFieldCreate->position = 2; + $dataFieldCreate->isTranslatable = $this->getOverride('isTranslatable', $fieldCreateOverride, false); + + // Custom settings + $dataFieldCreate->fieldSettings = $fieldSettings; + $dataFieldCreate->validatorConfiguration = $validatorConfiguration; + + $createStruct->addFieldDefinition($dataFieldCreate); + + $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); + $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); + + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); + + return $contentType; + } + + /** + * Retrieves a value for $key from $overrideValues, falling back to + * $default. + * + * @param string $key + * @param array $overrideValues + * @param mixed $default + * + * @return mixed + */ + protected function getOverride($key, array $overrideValues, $default) + { + return isset($overrideValues[$key]) ? $overrideValues[$key] : $default; + } + + /** + * @covers \Ibexa\Core\FieldType\FieldType::isEmptyValue + * @dataProvider providerForTestIsEmptyValue + */ + public function testIsEmptyValue($value) + { + $this->assertTrue($this->getRepository()->getFieldTypeService()->getFieldType($this->getTypeName())->isEmptyValue($value)); + } + + abstract public function providerForTestIsEmptyValue(); + + /** + * @covers \Ibexa\Core\FieldType\FieldType::isEmptyValue + * @dataProvider providerForTestIsNotEmptyValue + */ + public function testIsNotEmptyValue($value) + { + $this->assertFalse($this->getRepository()->getFieldTypeService()->getFieldType($this->getTypeName())->isEmptyValue($value)); + } + + abstract public function providerForTestIsNotEmptyValue(); + + /** + * @depends testCreateContentType + */ + public function testContentTypeField($contentType) + { + $this->assertSame( + $this->getTypeName(), + $contentType->fieldDefinitions[1]->fieldTypeIdentifier + ); + } + + /** + * @depends testCreateContentType + */ + public function testLoadContentTypeField() + { + $contentType = $this->testCreateContentType(); + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + return $contentTypeService->loadContentType($contentType->id); + } + + /** + * @depends testLoadContentTypeField + */ + public function testLoadContentTypeFieldType($contentType) + { + $this->assertSame( + $this->getTypeName(), + $contentType->fieldDefinitions[1]->fieldTypeIdentifier + ); + + return $contentType->fieldDefinitions[1]; + } + + public function testSettingsSchema() + { + $repository = $this->getRepository(); + $fieldTypeService = $repository->getFieldTypeService(); + $fieldType = $fieldTypeService->getFieldType($this->getTypeName()); + + $this->assertEquals( + $this->getSettingsSchema(), + $fieldType->getSettingsSchema() + ); + } + + /** + * @depends testLoadContentTypeFieldType + */ + public function testLoadContentTypeFieldData(FieldDefinition $fieldDefinition) + { + $this->assertEquals( + $this->getTypeName(), + $fieldDefinition->fieldTypeIdentifier, + 'Loaded fieldTypeIdentifier does not match.' + ); + $this->assertEquals( + $this->getValidFieldSettings(), + $fieldDefinition->fieldSettings, + 'Loaded fieldSettings do not match.' + ); + $this->assertEquals( + $this->getValidValidatorConfiguration(), + $fieldDefinition->validatorConfiguration, + 'Loaded validatorConfiguration does not match.' + ); + } + + /** + * @depends testCreateContentType + */ + public function testCreateContentTypeFailsWithInvalidFieldSettings() + { + $this->expectException(ContentTypeFieldDefinitionValidationException::class); + + $this->createContentType( + $this->getInvalidFieldSettings(), + $this->getValidValidatorConfiguration() + ); + } + + public function testValidatorSchema() + { + $repository = $this->getRepository(); + $fieldTypeService = $repository->getFieldTypeService(); + $fieldType = $fieldTypeService->getFieldType($this->getTypeName()); + + $this->assertEquals( + $this->getValidatorSchema(), + $fieldType->getValidatorConfigurationSchema() + ); + } + + /** + * @depends testCreateContentType + */ + public function testCreateContentTypeFailsWithInvalidValidatorConfiguration() + { + $this->expectException(ContentTypeFieldDefinitionValidationException::class); + + $this->createContentType( + $this->getValidFieldSettings(), + $this->getInvalidValidatorConfiguration() + ); + } + + /** + * @depends testLoadContentTypeField + */ + public function testCreateContent() + { + return $this->createContent($this->getValidCreationFieldData()); + } + + /** + * Creates content with $fieldData. + * + * @param mixed $fieldData + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + protected function createContent($fieldData, $contentType = null) + { + if ($contentType === null) { + $contentType = $this->testCreateContentType(); + } + + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-US'); + $createStruct->setField('name', 'Test object'); + $createStruct->setField( + 'data', + $fieldData + ); + + $createStruct->remoteId = 'abcdef0123456789abcdef0123456789'; + $createStruct->alwaysAvailable = true; + + return $contentService->createContent($createStruct); + } + + /** + * Create multilingual content of given name and FT-specific data. + * + * @param array $names Content names in the form of <code>[languageCode => name]</code> + * @param array $fieldData FT-specific data in the form of <code>[languageCode => data]</code> + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + protected function createMultilingualContent(array $names, array $fieldData, array $locationCreateStructs = []) + { + self::assertEquals(array_keys($names), array_keys($fieldData), 'Languages passed to names and data differ'); + + $contentType = $this->createContentType( + $this->getValidFieldSettings(), + $this->getValidValidatorConfiguration(), + $this->getValidContentTypeConfiguration(), + array_merge( + $this->getValidFieldConfiguration(), + ['isTranslatable' => true] + ) + ); + + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-US'); + foreach ($names as $languageCode => $name) { + $createStruct->setField('name', $name, $languageCode); + } + foreach ($fieldData as $languageCode => $value) { + $createStruct->setField('data', $value, $languageCode); + } + + $createStruct->remoteId = md5(uniqid('', true) . microtime()); + $createStruct->alwaysAvailable = true; + + return $contentService->createContent($createStruct, $locationCreateStructs); + } + + /** + * @depends testCreateContent + */ + public function testCreatedFieldType($content) + { + foreach ($content->getFields() as $field) { + if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { + return $field; + } + } + + $this->fail('Custom field not found.'); + } + + /** + * @depends testCreateContent + */ + public function testPublishContent() + { + $draft = $this->testCreateContent(); + + if (!$draft->getVersionInfo()->isDraft()) { + $this->markTestSkipped('Provided content object is not a draft.'); + } + + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + return $contentService->publishVersion($draft->getVersionInfo()); + } + + /** + * @depends testPublishContent + */ + public function testPublishedFieldType($content) + { + foreach ($content->getFields() as $field) { + if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { + return $field; + } + } + + $this->fail('Custom field not found.'); + } + + /** + * @depends testPublishContent + */ + public function testPublishedName(Content $content) + { + $this->assertEquals( + $content->getFieldValue('name') . ' ' . $this->getFieldName(), + $content->contentInfo->name + ); + } + + /** + * @depends testCreateContent + */ + public function testLoadField() + { + $content = $this->testCreateContent(); + + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + return $contentService->loadContent($content->contentInfo->id); + } + + /** + * @depends testLoadField + */ + public function testLoadFieldType() + { + $content = $this->testCreateContent(); + + foreach ($content->getFields() as $field) { + if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { + return $field; + } + } + + $this->fail('Custom field not found.'); + } + + /** + * @depends testLoadFieldType + */ + public function testLoadExternalData() + { + $this->assertFieldDataLoadedCorrect($this->testLoadFieldType()); + } + + public function testCreateContentWithEmptyFieldValue() + { + /** @var \Ibexa\Core\FieldType\FieldType $fieldType */ + $fieldType = $this->getRepository()->getFieldTypeService()->getFieldType($this->getTypeName()); + + return $this->createContent($fieldType->getEmptyValue()); + } + + /** + * Test that publishing (and thus indexing) content with an empty field value does not fail. + * + * @depends testCreateContentWithEmptyFieldValue + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $contentDraft + */ + public function testPublishContentWithEmptyFieldValue(Content $contentDraft) + { + $this->getRepository(false)->getContentService()->publishVersion( + $contentDraft->versionInfo + ); + } + + /** + * @depends testCreateContentWithEmptyFieldValue + */ + public function testCreatedEmptyFieldValue($content) + { + foreach ($content->getFields() as $field) { + if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { + return $field; + } + } + + $this->fail('Custom field not found.'); + } + + /** + * @depends testCreateContentWithEmptyFieldValue + * @group xx + */ + public function testLoadEmptyFieldValue() + { + $content = $this->testCreateContentWithEmptyFieldValue(); + + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + return $contentService->loadContent($content->contentInfo->id); + } + + /** + * @depends testLoadEmptyFieldValue + */ + public function testLoadEmptyFieldValueType($content) + { + foreach ($content->getFields() as $field) { + if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { + return $field; + } + } + + $this->fail('Custom field not found.'); + } + + /** + * @depends testLoadEmptyFieldValueType + */ + public function testLoadEmptyFieldValueData($field) + { + /** @var \Ibexa\Core\FieldType\FieldType $fieldType */ + $fieldType = $this->getRepository()->getFieldTypeService()->getFieldType($this->getTypeName()); + + // @todo either test this not using acceptValue, or add to API (but is not meant for high level API, so..) + $refObject = new \ReflectionObject($fieldType); + $refProperty = $refObject->getProperty('internalFieldType'); + $refProperty->setAccessible(true); + $spiFieldType = $refProperty->getValue($fieldType); + + $this->assertEquals( + $fieldType->getEmptyValue(), + $spiFieldType->acceptValue($field->value) + ); + } + + /** + * @depends testLoadFieldType + */ + public function testUpdateField() + { + return $this->updateContent($this->getValidUpdateFieldData()); + } + + /** + * Updates the standard published content object with $fieldData. + * + * @param mixed $fieldData + * @param bool $setField If false the update struct will be empty (field value will not be set) + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function updateContent($fieldData, $setField = true) + { + $content = $this->testPublishContent(); + + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $draft = $contentService->createContentDraft($content->contentInfo); + + $updateStruct = $contentService->newContentUpdateStruct(); + if ($setField) { + $updateStruct->setField( + $this->customFieldIdentifier, + $fieldData + ); + } + + return $contentService->updateContent($draft->versionInfo, $updateStruct); + } + + /** + * @depends testUpdateField + */ + public function testUpdateTypeFieldStillAvailable($content) + { + foreach ($content->getFields() as $field) { + if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { + return $field; + } + } + + $this->fail('Custom field not found.'); + } + + /** + * @depends testUpdateTypeFieldStillAvailable + */ + public function testUpdatedDataCorrect(Field $field) + { + $this->assertUpdatedFieldDataLoadedCorrect($field); + } + + /** + * Tests creating a new Version keeps the existing value. + */ + public function testUpdateFieldNoNewContent() + { + return $this->updateContent(null, false); + } + + /** + * @depends testUpdateFieldNoNewContent + */ + public function testUpdateNoNewContentTypeFieldStillAvailable($content) + { + foreach ($content->getFields() as $field) { + if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { + return $field; + } + } + + $this->fail('Custom field not found.'); + } + + /** + * @depends testUpdateNoNewContentTypeFieldStillAvailable + */ + public function testUpdatedNoNewContentDataCorrect(Field $field) + { + $this->assertFieldDataLoadedCorrect($field); + } + + /** + * @depends testCreateContent + */ + public function testCopyField($content) + { + $content = $this->testCreateContent(); + + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $locationService = $repository->getLocationService(); + $parentLocationId = $this->generateId('location', 2); + $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); + + $copied = $contentService->copyContent($content->contentInfo, $locationCreate); + + $this->assertNotSame( + $content->contentInfo->id, + $copied->contentInfo->id + ); + + return $contentService->loadContent($copied->id); + } + + /** + * @depends testCopyField + */ + public function testCopiedFieldType($content) + { + foreach ($content->getFields() as $field) { + if ($field->fieldDefIdentifier === $this->customFieldIdentifier) { + return $field; + } + } + + $this->fail('Custom field not found.'); + } + + /** + * @depends testCopiedFieldType + */ + public function testCopiedExternalData(Field $field) + { + $this->assertCopiedFieldDataLoadedCorrectly($field); + } + + /** + * @depends testCopyField + */ + public function testDeleteContent($content) + { + $this->expectException(NotFoundException::class); + + $content = $this->testPublishContent(); + + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $contentService->deleteContent($content->contentInfo); + + $contentService->loadContent($content->contentInfo->id); + } + + /** + * Tests failing content creation. + * + * @param mixed $failingValue + * + * @dataProvider provideInvalidCreationFieldData + */ + public function testCreateContentFails($failingValue, ?string $expectedException): void + { + $this->expectException($expectedException); + $this->createContent($failingValue); + } + + /** + * Tests failing content update. + * + * @param mixed $failingValue + * @param string $expectedException + * + * @dataProvider provideInvalidUpdateFieldData + */ + public function testUpdateContentFails($failingValue, $expectedException) + { + $this->expectException($expectedException); + $this->updateContent($failingValue); + } + + protected function removeFieldDefinition() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $content = $this->testPublishContent(); + + $contentType = $contentTypeService->loadContentType($content->contentInfo->contentTypeId); + $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); + $fieldDefinition = $contentTypeDraft->getFieldDefinition('data'); + + $contentTypeService->removeFieldDefinition($contentTypeDraft, $fieldDefinition); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + return $contentService->loadContent($content->id); + } + + /** + * Tests removal of field definition from the ContentType of the Content. + */ + public function testRemoveFieldDefinition() + { + $content = $this->removeFieldDefinition(); + + $this->assertCount(1, $content->getFields()); + $this->assertNull($content->getFieldValue('data')); + } + + protected function addFieldDefinition() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $content = $this->removeFieldDefinition(); + + $contentType = $contentTypeService->loadContentType($content->contentInfo->contentTypeId); + $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); + + $fieldDefinitionCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct( + 'data', + $this->getTypeName() + ); + + $fieldDefinitionCreateStruct->names = $this->getOverride('names', $this->getValidFieldConfiguration(), [$contentType->mainLanguageCode => $this->getTypeName()]); + $fieldDefinitionCreateStruct->validatorConfiguration = $this->getValidValidatorConfiguration(); + $fieldDefinitionCreateStruct->fieldSettings = $this->getValidFieldSettings(); + $fieldDefinitionCreateStruct->defaultValue = null; + + $contentTypeService->addFieldDefinition($contentTypeDraft, $fieldDefinitionCreateStruct); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + return $contentService->loadContent($content->id); + } + + /** + * Tests addition of field definition from the ContentType of the Content. + */ + public function testAddFieldDefinition() + { + $content = $this->addFieldDefinition(); + + $this->assertCount(2, $content->getFields()); + + $this->assertTrue( + $this->getRepository()->getFieldTypeService()->getFieldType( + $this->getTypeName() + )->isEmptyValue( + $content->getFieldValue('data') + ) + ); + } + + /** + * @dataProvider provideToHashData + */ + public function testToHash($value, $expectedHash) + { + $repository = $this->getRepository(); + $fieldTypeService = $repository->getFieldTypeService(); + $fieldType = $fieldTypeService->getFieldType($this->getTypeName()); + + $this->assertEquals( + $expectedHash, + $fieldType->toHash($value) + ); + } + + /** + * @depends testCreateContent + * @dataProvider provideFromHashData + * @todo: Requires correct registered FieldTypeService, needs to be + * maintained! + */ + public function testFromHash($hash, $expectedValue) + { + $repository = $this->getRepository(); + $fieldTypeService = $repository->getFieldTypeService(); + $fieldType = $fieldTypeService->getFieldType($this->getTypeName()); + + $this->assertEquals( + $expectedValue, + $fieldType->fromHash($hash) + ); + } + + /** + * Test that exceeding default version archive limit has no effect on a published content. + */ + public function testExceededVersionArchiveLimitHasNoEffectOnContent() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentDraft = $this->createContent($this->getValidCreationFieldData()); + $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); + // update and publish content to exceed version archive limit + $contentUpdateStruct = $contentService->newContentUpdateStruct(); + $contentUpdateStruct->setField('data', $this->getValidUpdateFieldData()); + for ($i = 0; $i < static::VERSION_ARCHIVE_LIMIT + 1; ++$i) { + $contentDraft = $contentService->createContentDraft($publishedContent->contentInfo); + $contentService->updateContent($contentDraft->versionInfo, $contentUpdateStruct); + $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); + } + + $loadedContent = $contentService->loadContent( + $publishedContent->contentInfo->id, + ['eng-US'] + ); + $this->assertUpdatedFieldDataLoadedCorrect($loadedContent->getField('data')); + } + + /** + * Test that deleting new draft does not affect data of published version. + */ + public function testDeleteDraftOfPublishedContentDoesNotDeleteData() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $fieldType = $repository->getFieldTypeService()->getFieldType($this->getTypeName()); + + $contentDraft = $this->testCreateContent(); + $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); + + $contentDraft = $contentService->createContentDraft($publishedContent->contentInfo); + + $contentService->deleteVersion($contentDraft->versionInfo); + $loadedContent = $contentService->loadContent($publishedContent->contentInfo->id, ['eng-US']); + + self::assertFalse( + $fieldType->isEmptyValue($loadedContent->getField('data')->value) + ); + } + + /** + * Test creating new translation from existing content with empty field. + */ + public function testUpdateContentWithNewTranslationOnEmptyField() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $content = $this->testCreateContentWithEmptyFieldValue(); + $publishedContent = $contentService->publishVersion($content->versionInfo); + + $contentDraft = $contentService->createContentDraft($publishedContent->contentInfo); + $updateStruct = $contentService->newContentUpdateStruct(); + $updateStruct->setField( + 'data', + $publishedContent->getFieldValue('data', 'eng-US'), + 'eng-US' + ); + $updateStruct->initialLanguageCode = 'eng-GB'; + $updatedContentDraft = $contentService->updateContent($contentDraft->versionInfo, $updateStruct); + $contentService->publishVersion($updatedContentDraft->versionInfo); + } + + /** + * Get proper multilingual FT-specific Values. It Can be overridden by a Field Type test case. + * + * @param string[] $languageCodes List of languages to create data for + * + * @return array an array in the form of <code>[languageCode => data]</code> + */ + public function getValidMultilingualFieldData(array $languageCodes) + { + $data = []; + foreach ($languageCodes as $languageCode) { + $data[$languageCode] = $this->getValidCreationFieldData(); + } + + return $data; + } + + /** + * Test that removing Translation from all Versions works for data from a Field Type. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteTranslation + */ + public function testDeleteTranslation() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $languageCodes = ['eng-US', 'ger-DE']; + + $fieldName = $this->getFieldName(); + $names = []; + foreach ($languageCodes as $languageCode) { + $names[$languageCode] = "{$languageCode} {$fieldName}"; + } + + $fieldData = $this->getValidMultilingualFieldData($languageCodes); + + $content = $contentService->publishVersion( + $this->createMultilingualContent($names, $fieldData)->versionInfo + ); + + // create one more Version + $publishedContent = $contentService->publishVersion( + $contentService->createContentDraft($content->contentInfo)->versionInfo + ); + + // create Draft + $contentService->createContentDraft($content->contentInfo); + + // create copy of content in all Versions to use it for comparision later on + $contentByVersion = []; + foreach ($contentService->loadVersions($content->contentInfo) as $versionInfo) { + $contentByVersion[$versionInfo->versionNo] = $contentService->loadContent( + $content->id, + null, + $versionInfo->versionNo + ); + } + + // delete Translation from all available Versions + $contentService->deleteTranslation($publishedContent->contentInfo, 'ger-DE'); + + // check if are Versions have valid Translation + foreach ($contentService->loadVersions($publishedContent->contentInfo) as $versionInfo) { + // check if deleted Translation does not exist + self::assertEquals(['eng-US'], array_keys($versionInfo->getNames())); + self::assertEquals(['eng-US'], $versionInfo->languageCodes); + + // load Content of a Version to access other fields data + $versionContent = $contentService->loadContent( + $content->id, + null, + $versionInfo->versionNo + ); + // check if deleted Translation for Field Type data does not exist + self::assertEmpty($versionContent->getFieldsByLanguage('ger-DE')); + self::assertEmpty($versionContent->getField('data', 'ger-DE')); + + // check if the remaining Translation is still valid + $expectedContent = $contentByVersion[$versionContent->versionInfo->versionNo]; + self::assertNotEmpty($versionContent->getFieldsByLanguage('eng-US')); + self::assertEquals( + $expectedContent->getField('name', 'eng-US'), + $versionContent->getField('name', 'eng-US') + ); + self::assertEquals( + $expectedContent->getField('data', 'eng-US'), + $versionContent->getField('data', 'eng-US') + ); + } + } +} + +class_alias(BaseIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\BaseIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php b/tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php new file mode 100644 index 0000000000..a50acfec0f --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/BinaryFileIntegrationTest.php @@ -0,0 +1,446 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; +use Ibexa\Core\Base\Exceptions\InvalidArgumentValue; +use Ibexa\Core\FieldType\BinaryFile\Value as BinaryFileValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class BinaryFileIntegrationTest extends FileSearchBaseIntegrationTest +{ + /** + * Stores the loaded image path for copy test. + */ + protected static $loadedBinaryFilePath; + + /** + * IOService storage prefix for the tested Type's files. + * + * @var string + */ + protected static $storagePrefixConfigKey = 'ibexa.io.binary_file.storage.prefix'; + + protected function getStoragePrefix() + { + return $this->getConfigValue(self::$storagePrefixConfigKey); + } + + /** + * Sets up fixture data. + * + * @return array + */ + protected function getFixtureData() + { + return [ + 'create' => [ + 'id' => null, + 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), + 'fileName' => 'Icy-Night-Flower-Binary.jpg', + 'fileSize' => filesize($path), + 'mimeType' => 'image/jpeg', + // Left out'downloadCount' by intention (will be set to 0) + ], + 'update' => [ + 'id' => null, + 'inputUri' => ($path = __DIR__ . '/_fixtures/image.png'), + 'fileName' => 'Blue-Blue-Blue-Sindelfingen.png', + 'fileSize' => filesize($path), + 'downloadCount' => 23, + // Left out 'mimeType' by intention (will be auto-detected) + ], + ]; + } + + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezbinaryfile'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return []; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return []; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return [ + 'FileSizeValidator' => [ + 'maxFileSize' => [ + 'type' => 'int', + 'default' => false, + ], + ], + ]; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return [ + 'FileSizeValidator' => [ + 'maxFileSize' => 2 * 1024 * 1024, // 2 MB + ], + ]; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'StringLengthValidator' => [ + 'minStringLength' => new \stdClass(), + ], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + $fixtureData = $this->getFixtureData(); + + return new BinaryFileValue($fixtureData['create']); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'Icy-Night-Flower-Binary.jpg'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + BinaryFileValue::class, + $field->value + ); + + $fixtureData = $this->getFixtureData(); + $expectedData = $fixtureData['create']; + + // Will change during storage + unset($expectedData['id']); + $expectedData['inputUri'] = null; + + $this->assertNotEmpty($field->value->id); + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + + $this->assertTrue( + $this->uriExistsOnIO($field->value->uri), + "File {$field->value->uri} doesn't exist" + ); + + self::$loadedBinaryFilePath = $field->value->id; + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + [ + 'id' => '/foo/bar/sindelfingen.pdf', + ], + InvalidArgumentValue::class, + ], + [ + new BinaryFileValue( + [ + 'id' => '/foo/bar/sindelfingen.pdf', + ] + ), + InvalidArgumentValue::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + $fixtureData = $this->getFixtureData(); + + return new BinaryFileValue($fixtureData['update']); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + BinaryFileValue::class, + $field->value + ); + + $fixtureData = $this->getFixtureData(); + $expectedData = $fixtureData['update']; + + // Will change during storage + unset($expectedData['id']); + $expectedData['inputUri'] = null; + + $this->assertNotEmpty($field->value->id); + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + + $this->assertTrue( + $this->uriExistsOnIO($field->value->uri), + "File {$field->value->uri} doesn't exist." + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertFieldDataLoadedCorrect($field); + + $this->assertEquals( + self::$loadedBinaryFilePath, + $field->value->id + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + $fixture = $this->getFixtureData(); + $expected = $fixture['create']; + $expected['downloadCount'] = 0; + $expected['uri'] = $expected['inputUri']; + $expected['path'] = $expected['inputUri']; + + $fieldValue = $this->getValidCreationFieldData(); + $fieldValue->uri = $expected['uri']; + + return [ + [ + $fieldValue, + $expected, + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + $fixture = $this->getFixtureData(); + $fixture['create']['downloadCount'] = 0; + $fixture['create']['uri'] = $fixture['create']['inputUri']; + + $fieldValue = $this->getValidCreationFieldData(); + $fieldValue->uri = $fixture['create']['uri']; + + return [ + [ + $fixture['create'], + $fieldValue, + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new BinaryFileValue()], + [new BinaryFileValue([])], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + ]; + } + + protected function getValidSearchValueOne() + { + return new BinaryFileValue( + [ + 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), + 'fileName' => 'blue-blue-blue-sindelfingen.jpg', + 'fileSize' => filesize($path), + ] + ); + } + + /** + * BinaryFile field type is not searchable with Field criterion + * and sort clause in Legacy search engine. + */ + protected function checkSearchEngineSupport() + { + if ($this->getSetupFactory() instanceof Legacy) { + $this->markTestSkipped( + "'ezbinaryfile' field type is not searchable with Legacy Search Engine" + ); + } + } + + protected function getValidSearchValueTwo() + { + return new BinaryFileValue( + [ + 'inputUri' => ($path = __DIR__ . '/_fixtures/image.png'), + 'fileName' => 'icy-night-flower-binary.png', + 'fileSize' => filesize($path), + ] + ); + } + + protected function getSearchTargetValueOne() + { + $value = $this->getValidSearchValueOne(); + // ensure case-insensitivity + return strtoupper($value->fileName); + } + + protected function getSearchTargetValueTwo() + { + $value = $this->getValidSearchValueTwo(); + // ensure case-insensitivity + return strtoupper($value->fileName); + } + + protected function getAdditionallyIndexedFieldData() + { + return [ + [ + 'file_size', + $this->getValidSearchValueOne()->fileSize, + $this->getValidSearchValueTwo()->fileSize, + ], + [ + 'mime_type', + // ensure case-insensitivity + 'IMAGE/JPEG', + 'IMAGE/PNG', + ], + ]; + } +} + +class_alias(BinaryFileIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\BinaryFileIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php b/tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php new file mode 100644 index 0000000000..dcdf55d297 --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/CheckboxIntegrationTest.php @@ -0,0 +1,428 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\Checkbox\Value as CheckboxValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class CheckboxIntegrationTest extends SearchBaseIntegrationTest +{ + private const IS_ACTIVE_FIELD_DEF_IDENTIFIER = 'is_active'; + + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezboolean'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return []; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return []; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return []; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return []; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'unknown' => ['value' => 42], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new CheckboxValue(true); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return '1'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + self::assertInstanceOf( + CheckboxValue::class, + $field->value + ); + + $expectedData = [ + 'bool' => true, + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + new CheckboxValue(new \stdClass()), + InvalidArgumentType::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + return new CheckboxValue(false); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + self::assertInstanceOf( + CheckboxValue::class, + $field->value + ); + + $expectedData = [ + 'bool' => false, + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + self::assertInstanceOf( + CheckboxValue::class, + $field->value + ); + + $expectedData = [ + 'bool' => true, + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new CheckboxValue(true), + '1', + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + '1', + new CheckboxValue(true), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return []; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [$this->getValidCreationFieldData()], + [new CheckboxValue(true)], + [new CheckboxValue()], + [new CheckboxValue(null)], + [new CheckboxValue(false)], + ]; + } + + protected function getValidSearchValueOne() + { + return false; + } + + protected function getValidSearchValueTwo() + { + return true; + } + + protected function getSearchTargetValueOne() + { + // Handling Legacy Search Engine, which stores Checkbox value as integer + if ($this->getSetupFactory() instanceof Legacy) { + return (int)$this->getValidSearchValueOne(); + } + + return parent::getSearchTargetValueOne(); + } + + protected function getSearchTargetValueTwo() + { + // Handling Legacy Search Engine, which stores Checkbox value as integer + if ($this->getSetupFactory() instanceof Legacy) { + return (int)$this->getValidSearchValueTwo(); + } + + return parent::getSearchTargetValueTwo(); + } + + /** + * Data corresponds to Content items created by {@see createCheckboxContentItems}. + */ + public function getDataForTestFindContentFieldCriterion(): iterable + { + // there are 2 Content items created, one with is_active = true, the other one with is_active = false + yield 'active' => [true]; + yield 'not active' => [false]; + } + + /** + * @dataProvider getDataForTestFindContentFieldCriterion + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testFindContentFieldCriterion(bool $isActive): void + { + $repository = $this->getRepository(); + $this->createCheckboxContentItems($repository); + + $criterion = new Criterion\Field( + self::IS_ACTIVE_FIELD_DEF_IDENTIFIER, + Criterion\Operator::EQ, + $isActive + ); + $query = new Query(['query' => $criterion]); + + $searchService = $repository->getSearchService(); + $searchResult = $searchService->findContent($query); + + self::assertEquals(1, $searchResult->totalCount); + $contentItem = $searchResult->searchHits[0]->valueObject; + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $contentItem */ + $value = $contentItem->getField('is_active')->value; + /** @var \Ibexa\Core\FieldType\Checkbox\Value $value */ + self::assertSame($isActive, $value->bool); + } + + public function testAddFieldDefinition(): void + { + $fieldTypeService = $this->getRepository()->getFieldTypeService(); + $content = $this->addFieldDefinition(); + + self::assertCount(2, $content->getFields()); + self::assertFalse( + $fieldTypeService + ->getFieldType($this->getTypeName()) + ->isEmptyValue($content->getFieldValue('data')) + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + protected function createCheckboxContentItems(Repository $repository): void + { + $contentType = $this->createContentTypeWithCheckboxField($repository); + + $contentService = $repository->getContentService(); + + $toCreate = [ + 'content-checkbox-active' => true, + 'content-checkbox-not-active' => false, + ]; + foreach ($toCreate as $remoteId => $isActive) { + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->remoteId = $remoteId; + $createStruct->alwaysAvailable = false; + $createStruct->setField(self::IS_ACTIVE_FIELD_DEF_IDENTIFIER, $isActive); + + $contentService->publishVersion( + $contentService->createContent($createStruct)->getVersionInfo() + ); + } + + $this->refreshSearch($repository); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function createContentTypeWithCheckboxField(Repository $repository): ContentType + { + $contentTypeService = $repository->getContentTypeService(); + + $createStruct = $contentTypeService->newContentTypeCreateStruct('content-checkbox'); + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->names = ['eng-GB' => 'Checkboxes']; + + $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct( + self::IS_ACTIVE_FIELD_DEF_IDENTIFIER, + 'ezboolean' + ); + $fieldCreate->names = ['eng-GB' => 'Active']; + $fieldCreate->position = 1; + $fieldCreate->isTranslatable = false; + $fieldCreate->isSearchable = true; + + $createStruct->addFieldDefinition($fieldCreate); + + $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); + $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + return $contentTypeService->loadContentType($contentTypeDraft->id); + } +} + +class_alias(CheckboxIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\CheckboxIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php b/tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php new file mode 100644 index 0000000000..3af6da08a0 --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/CountryIntegrationTest.php @@ -0,0 +1,481 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\FieldType\Country\Value as CountryValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class CountryIntegrationTest extends SearchMultivaluedBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezcountry'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return [ + 'isMultiple' => [ + 'type' => 'boolean', + 'default' => false, + ], + ]; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return [ + 'isMultiple' => false, + ]; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return []; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return []; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'unknown' => ['value' => 42], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + ] + ); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'Belgium'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + CountryValue::class, + $field->value + ); + + $expectedData = [ + 'countries' => [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + ], + ]; + + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + 'Sindelfingen', + InvalidArgumentException::class, + ], + [ + ['NON_VALID_ALPHA2_CODE'], + InvalidArgumentException::class, + ], + [ + ['BE', 'FR'], + ContentFieldValidationException::class, + ], + [ + new CountryValue( + [ + 'NON_VALID_ALPHA2_CODE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + ] + ), + ContentFieldValidationException::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + return new CountryValue( + [ + 'FR' => [ + 'Name' => 'France', + 'Alpha2' => 'FR', + 'Alpha3' => 'FRA', + 'IDC' => 33, + ], + ] + ); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + CountryValue::class, + $field->value + ); + + $expectedData = [ + 'countries' => [ + 'FR' => [ + 'Name' => 'France', + 'Alpha2' => 'FR', + 'Alpha3' => 'FRA', + 'IDC' => 33, + ], + ], + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + CountryValue::class, + $field->value + ); + + $expectedData = [ + 'countries' => [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + ], + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + 'FR' => [ + 'Name' => 'France', + 'Alpha2' => 'FR', + 'Alpha3' => 'FRA', + 'IDC' => 33, + ], + ] + ), + ['BE', 'FR'], + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + ['BE', 'FR'], + new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + 'FR' => [ + 'Name' => 'France', + 'Alpha2' => 'FR', + 'Alpha3' => 'FRA', + 'IDC' => 33, + ], + ] + ), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new CountryValue()], + [new CountryValue([])], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + ]; + } + + protected function getValidSearchValueOne() + { + return ['Andorra']; + } + + protected function getValidSearchValueTwo() + { + return ['Trinidad and Tobago']; + } + + protected function getSearchTargetValueOne() + { + return 'Andorra'; + } + + protected function getSearchTargetValueTwo() + { + return 'Trinidad and Tobago'; + } + + protected function getAdditionallyIndexedFieldData() + { + return [ + [ + 'idc', + '376', + '1868', + ], + [ + 'alpha2', + 'AD', + 'TT', + ], + [ + 'alpha3', + 'AND', + 'TTO', + ], + [ + 'name', + 'Andorra', + 'Trinidad and Tobago', + ], + [ + 'sort_value', + 'andorra', + 'trinidad and tobago', + ], + ]; + } + + protected function getValidMultivaluedSearchValuesOne() + { + return ['Andorra', 'Bolivia']; + } + + protected function getValidMultivaluedSearchValuesTwo() + { + return ['Syrian Arab Republic', 'Trinidad and Tobago']; + } + + protected function getAdditionallyIndexedMultivaluedFieldData() + { + return [ + [ + 'idc', + [376, 591], + [963, 1868], + ], + [ + 'alpha2', + ['AD', 'BO'], + ['SY', 'TT'], + ], + [ + 'alpha3', + ['AND', 'BOL'], + ['SYR', 'TTO'], + ], + [ + 'name', + ['Andorra', 'Bolivia'], + ['Syrian Arab Republic', 'Trinidad and Tobago'], + ], + ]; + } + + protected function getFullTextIndexedFieldData() + { + return [ + ['Andorra', 'Tobago'], + ]; + } + + protected function createTestContentType() + { + $contentType = $this->createContentType( + [ + 'isMultiple' => true, + ], + $this->getValidValidatorConfiguration() + ); + + return $contentType; + } +} + +class_alias(CountryIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\CountryIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php b/tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php new file mode 100644 index 0000000000..8b733ee841 --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/DateAndTimeIntegrationTest.php @@ -0,0 +1,362 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use DateTime; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; +use Ibexa\Core\FieldType\DateAndTime\Value as DateAndTimeValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class DateAndTimeIntegrationTest extends SearchBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezdatetime'; + } + + /** + * {@inheritdoc} + */ + protected function supportsLikeWildcard($value) + { + parent::supportsLikeWildcard($value); + + return false; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return [ + 'useSeconds' => [ + 'type' => 'bool', + 'default' => false, + ], + 'defaultType' => [ + 'type' => 'choice', + 'default' => 0, + ], + 'dateInterval' => [ + 'type' => 'dateInterval', + 'default' => null, + ], + ]; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return [ + 'useSeconds' => false, + 'defaultType' => 0, + 'dateInterval' => null, + ]; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return []; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return []; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'unknown' => ['value' => 42], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + // We may only create times from timestamps here, since storing will + // loose information about the timezone. + return DateAndTimeValue::fromTimestamp(123456); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'Fri 1970-02-01 10:17:36'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + DateAndTimeValue::class, + $field->value + ); + + $expectedData = [ + 'value' => new \DateTime('@123456'), + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + 'Some unknown date format', InvalidArgumentException::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + return DateAndTimeValue::fromTimestamp(12345678); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + DateAndTimeValue::class, + $field->value + ); + + $expectedData = [ + 'value' => new \DateTime('@12345678'), + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Tests failing content update. + * + * @param mixed $failingValue + * @param string $expectedException + * + * @dataProvider provideInvalidUpdateFieldData + */ + public function testUpdateContentFails($failingValue, $expectedException) + { + return [ + [ + 'Some unknown date format', InvalidArgumentException::class, + ], + ]; + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + DateAndTimeValue::class, + $field->value + ); + + $expectedData = [ + 'value' => new \DateTime('@123456'), + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + DateAndTimeValue::fromTimestamp(123456), + [ + 'timestamp' => 123456, + 'rfc850' => 'Friday, 02-Jan-70 10:17:36 GMT+0000', + ], + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + [ + 'timestamp' => 123456, + 'rfc850' => 'Friday, 02-Jan-70 10:17:36 GMT+0000', + ], + DateAndTimeValue::fromTimestamp(123456), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new DateAndTimeValue()], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + ]; + } + + protected function getValidSearchValueOne() + { + return '2012-04-15T15:43:56Z'; + } + + protected function getValidSearchValueTwo() + { + return '2015-04-15T15:43:56Z'; + } + + protected function getSearchTargetValueOne() + { + // Handling Legacy Search Engine, which stores DateAndTime value as integer timestamp + if ($this->getSetupFactory() instanceof Legacy) { + $dateTime = new DateTime($this->getValidSearchValueOne()); + + return $dateTime->getTimestamp(); + } + + return parent::getSearchTargetValueOne(); + } + + protected function getSearchTargetValueTwo() + { + // Handling Legacy Search Engine, which stores DateAndTime value as integer timestamp + if ($this->getSetupFactory() instanceof Legacy) { + $dateTime = new DateTime($this->getValidSearchValueTwo()); + + return $dateTime->getTimestamp(); + } + + return parent::getSearchTargetValueTwo(); + } +} + +class_alias(DateAndTimeIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\DateAndTimeIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/DateIntegrationTest.php b/tests/integration/Core/Repository/FieldType/DateIntegrationTest.php new file mode 100644 index 0000000000..2fc2508f44 --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/DateIntegrationTest.php @@ -0,0 +1,350 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use DateTime; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; +use Ibexa\Core\FieldType\Date\Type; +use Ibexa\Core\FieldType\Date\Value as DateValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class DateIntegrationTest extends SearchBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezdate'; + } + + /** + * {@inheritdoc} + */ + protected function supportsLikeWildcard($value) + { + parent::supportsLikeWildcard($value); + + return false; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return [ + 'defaultType' => [ + 'type' => 'choice', + 'default' => Type::DEFAULT_EMPTY, + ], + ]; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return [ + 'defaultType' => Type::DEFAULT_EMPTY, + ]; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return []; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return []; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'unknown' => ['value' => 42], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return DateValue::fromTimestamp(86400); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'Friday 02 January 1970'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + DateValue::class, + $field->value + ); + + $expectedData = [ + 'date' => new DateTime('@86400'), + ]; + + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + 'Some unknown date format', + InvalidArgumentException::class, + ], + ]; + } + + /** + * Get valid field data for updating content. + * + * @return mixed + */ + public function getValidUpdateFieldData() + { + return DateValue::fromTimestamp(86400); + } + + /** + * Asserts the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidUpdateFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + DateValue::class, + $field->value + ); + + $expectedData = [ + 'date' => new DateTime('@86400'), + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertFieldDataLoadedCorrect($field); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + $timestamp = 186401; + $dateTime = new DateTime("@{$timestamp}"); + + return [ + [ + DateValue::fromTimestamp($timestamp), + [ + 'timestamp' => $dateTime->setTime(0, 0, 0)->getTimestamp(), + 'rfc850' => $dateTime->format(DateTime::RFC850), + ], + ], + ]; + } + + /** + * Get hashes and their respective converted values. + * + * This is a PHPUnit data provider + * + * The returned records must have the the input hash assigned to the + * first index and the expected value result to the second. For example: + * + * <code> + * array( + * array( + * array( 'myValue' => true ), + * new MyValue( true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideFromHashData() + { + $timestamp = 123456; + + $dateTime = new DateTime("@{$timestamp}"); + $dateTime->setTime(0, 0, 0); + + return [ + [ + [ + 'timestamp' => $timestamp, + 'rfc850' => ($rfc850 = $dateTime->format(DateTime::RFC850)), + ], + DateValue::fromString($rfc850), + ], + [ + [ + 'timestamp' => $dateTime->getTimestamp(), + 'rfc850' => null, + ], + DateValue::fromTimestamp($timestamp), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new DateValue()], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + ]; + } + + protected function getValidSearchValueOne() + { + return 86400; + } + + protected function getValidSearchValueTwo() + { + return 172800; + } + + protected function getSearchTargetValueOne() + { + // Handling Legacy Search Engine, which stores Date value as timestamp + if ($this->getSetupFactory() instanceof Legacy) { + return $this->getValidSearchValueOne(); + } + + return '1970-01-02T00:00:00Z'; + } + + protected function getSearchTargetValueTwo() + { + // Handling Legacy Search Engine, which stores Date value as timestamp + if ($this->getSetupFactory() instanceof Legacy) { + return $this->getValidSearchValueTwo(); + } + + return '1970-01-03T00:00:00Z'; + } +} + +class_alias(DateIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\DateIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php b/tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php new file mode 100644 index 0000000000..1d4a9b3c89 --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/EmailAddressIntegrationTest.php @@ -0,0 +1,333 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\Base\Exceptions\ContentFieldValidationException; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\EmailAddress\Value as EmailAddressValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class EmailAddressIntegrationTest extends SearchBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezemail'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return []; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return []; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return [ + 'EmailAddressValidator' => [], + ]; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return [ + 'EmailAddressValidator' => [], + ]; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'StringLengthValidator' => [ + 'minStringLength' => new \stdClass(), + ], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new EmailAddressValue('spam@ibexa.co'); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'spam@ibexa.co'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + EmailAddressValue::class, + $field->value + ); + + $expectedData = [ + 'email' => 'spam@ibexa.co', + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + new \stdClass(), + InvalidArgumentException::class, + ], + [ + 42, + InvalidArgumentException::class, + ], + [ + new EmailAddressValue(str_repeat('.', 64)), + ContentFieldValidationException::class, + ], + [ + new EmailAddressValue('spam@'), + ContentFieldValidationException::class, + ], + [ + new EmailAddressValue('@ibexa.co'), + ContentFieldValidationException::class, + ], + [ + new EmailAddressValue('spam@ibexa'), + ContentFieldValidationException::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + return new EmailAddressValue('spam_name@ibexa-some-thing.co'); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + EmailAddressValue::class, + $field->value + ); + + $expectedData = [ + 'email' => 'spam_name@ibexa-some-thing.co', + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + EmailAddressValue::class, + $field->value + ); + + $expectedData = [ + 'email' => 'spam@ibexa.co', + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new EmailAddressValue('spam@example.no'), + 'spam@example.no', + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + 'spam@example.no', + new EmailAddressValue('spam@example.no'), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new EmailAddressValue()], + [new EmailAddressValue(null)], + [new EmailAddressValue('')], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + ]; + } + + protected function getValidSearchValueOne() + { + return 'holmes4@ibexa.co'; + } + + protected function getSearchTargetValueOne() + { + // ensure case-insensitivity + return strtoupper($this->getValidSearchValueOne()); + } + + protected function getValidSearchValueTwo() + { + return 'wyoming.knott@o2.ru'; + } + + protected function getSearchTargetValueTwo() + { + // ensure case-insensitivity + return strtoupper($this->getValidSearchValueTwo()); + } + + protected function getFullTextIndexedFieldData() + { + return [ + ['holmes4@ibexa.co', 'wyoming.knott@o2.ru'], + ]; + } +} + +class_alias(EmailAddressIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\EmailAddressIntegrationTest'); diff --git a/eZ/Publish/API/Repository/Tests/FieldType/FileSearchBaseIntegrationTest.php b/tests/integration/Core/Repository/FieldType/FileSearchBaseIntegrationTest.php similarity index 93% rename from eZ/Publish/API/Repository/Tests/FieldType/FileSearchBaseIntegrationTest.php rename to tests/integration/Core/Repository/FieldType/FileSearchBaseIntegrationTest.php index c1f4c54154..17058af66e 100644 --- a/eZ/Publish/API/Repository/Tests/FieldType/FileSearchBaseIntegrationTest.php +++ b/tests/integration/Core/Repository/FieldType/FileSearchBaseIntegrationTest.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\FieldType; +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; use FilesystemIterator; use RecursiveDirectoryIterator; @@ -43,7 +43,7 @@ abstract class FileSearchBaseIntegrationTest extends SearchBaseIntegrationTest /** * Storage dir settings key. */ - protected static $storageDirConfigKey = 'storage_dir'; + protected static $storageDirConfigKey = 'ibexa.io.dir.storage'; /** * If storage data should not be cleaned up. @@ -89,9 +89,9 @@ protected function setUp(): void parent::setUp(); if (!isset(self::$installDir)) { - self::$installDir = $this->getConfigValue('ezpublish.kernel.root_dir'); + self::$installDir = $this->getConfigValue('ibexa.kernel.root_dir'); self::$storageDir = $this->getConfigValue(static::$storageDirConfigKey); - self::$ioRootDir = $this->getConfigValue('io_root_dir'); + self::$ioRootDir = $this->getConfigValue('ibexa.io.dir.root'); self::setUpIgnoredPath($this->getConfigValue('ignored_storage_files')); } @@ -220,3 +220,5 @@ public function testUpdateWithRemove() $this->testIsEmptyValue($updatedContent->getFieldValue('data')); } } + +class_alias(FileSearchBaseIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\FileSearchBaseIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php b/tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php new file mode 100644 index 0000000000..47b0c1b337 --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/FloatIntegrationTest.php @@ -0,0 +1,317 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\FieldType\Float\Value as FloatValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class FloatIntegrationTest extends SearchBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezfloat'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return []; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return []; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return [ + 'FloatValueValidator' => [ + 'minFloatValue' => [ + 'type' => 'float', + 'default' => false, + ], + 'maxFloatValue' => [ + 'type' => 'float', + 'default' => false, + ], + ], + ]; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return [ + 'FloatValueValidator' => [ + 'minFloatValue' => 23., + 'maxFloatValue' => 43., + ], + ]; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'FloatValueValidator' => [ + 'minStringLength' => new \stdClass(), + ], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new FloatValue(23.5); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return '23.5'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + FloatValue::class, + $field->value + ); + + $expectedData = [ + 'value' => 23.5, + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + new \stdClass(), + InvalidArgumentException::class, + ], + [ + new FloatValue(5.5), + ContentFieldValidationException::class, + ], + [ + new FloatValue(127.5), + ContentFieldValidationException::class, + ], + ]; + } + + /** + * Get update field externals data. + */ + public function getValidUpdateFieldData() + { + return new FloatValue(42.5); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + FloatValue::class, + $field->value + ); + + $expectedData = [ + 'value' => 42.5, + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + FloatValue::class, + $field->value + ); + + $expectedData = [ + 'value' => 23.5, + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new FloatValue(23.5), + 23.5, + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + 42.5, + new FloatValue(42.5), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new FloatValue()], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + [new FloatValue(0)], + [new FloatValue(0.0)], + ]; + } + + protected function getValidSearchValueOne() + { + return 25.519; + } + + protected function getValidSearchValueTwo() + { + return 25.59; + } + + public function checkFullTextSupport(): bool + { + return false; + } +} + +class_alias(FloatIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\FloatIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php b/tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php new file mode 100644 index 0000000000..f98418b0cf --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/ISBNIntegrationTest.php @@ -0,0 +1,330 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\FieldType\ISBN\Value as ISBNValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class ISBNIntegrationTest extends SearchBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezisbn'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return [ + 'isISBN13' => [ + 'type' => 'boolean', + 'default' => true, + ], + ]; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return [ + 'isISBN13' => true, + ]; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return []; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return []; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'unknown' => ['value' => 42], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new ISBNValue('9789722514095'); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return '9789722514095'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + ISBNValue::class, + $field->value + ); + + $expectedData = '9789722514095'; + + $this->assertEquals( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + '9789722', + ContentFieldValidationException::class, + ], + [ + 'NON_VALID_ISBN_CODE', + ContentFieldValidationException::class, + ], + [ + new \stdClass(), + InvalidArgumentException::class, + ], + [ + new ISBNValue('97897225'), + ContentFieldValidationException::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + return new ISBNValue('978-972-25-1409-5'); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + ISBNValue::class, + $field->value + ); + + $expectedData = '978-972-25-1409-5'; + $this->assertEquals( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + ISBNValue::class, + $field->value + ); + + $expectedData = '9789722514095'; + + $this->assertEquals( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new ISBNValue('9789722514095'), + '9789722514095', + ], + [ + new ISBNValue('978-972-25-1409-5'), + '978-972-25-1409-5', + ], + [ + new ISBNValue('0-9752298-0-X'), + '0-9752298-0-X', + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + '9789722514095', + new ISBNValue('9789722514095'), + ], + [ + '978-972-25-1409-5', + new ISBNValue('978-972-25-1409-5'), + ], + [ + '0-9752298-0-X', + new ISBNValue('0-9752298-0-X'), + ], + [ + '097522980X', + new ISBNValue('097522980X'), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new ISBNValue()], + [new ISBNValue(null)], + [new ISBNValue('')], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + ]; + } + + protected function getValidSearchValueOne() + { + return '9780099067504'; + } + + protected function getValidSearchValueTwo() + { + return '9780380448340'; + } + + protected function getFullTextIndexedFieldData() + { + return [ + ['9780099067504', '9780380448340'], + ]; + } +} + +class_alias(ISBNIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\ISBNIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php b/tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php new file mode 100644 index 0000000000..b11c4eaa23 --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/ImageIntegrationTest.php @@ -0,0 +1,992 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Doctrine\DBAL\ParameterType; +use DOMDocument; +use DOMElement; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; +use Ibexa\Core\FieldType\Image\IO\Legacy as LegacyIOService; +use Ibexa\Core\FieldType\Image\Value as ImageValue; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\Persistence\Legacy\Content\Gateway; +use stdClass; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class ImageIntegrationTest extends FileSearchBaseIntegrationTest +{ + /** + * Stores the loaded image path for copy test. + */ + protected static $loadedImagePath; + + /** + * IOService storage prefix for the tested Type's files. + * + * @var string + */ + protected static $storagePrefixConfigKey = 'ibexa.io.images.storage.prefix'; + + protected function getStoragePrefix() + { + return $this->getConfigValue(self::$storagePrefixConfigKey); + } + + /** + * Sets up fixture data. + * + * @return array + */ + protected function getFixtureData(): array + { + return [ + 'create' => [ + 'fileName' => 'Icy-Night-Flower.jpg', + 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), + 'alternativeText' => 'My icy flower at night', + 'fileSize' => filesize($path), + ], + 'update' => [ + 'fileName' => 'Blue-Blue-Blue.png', + 'inputUri' => ($path = __DIR__ . '/_fixtures/image.png'), + 'alternativeText' => 'Such a blue …', + 'fileSize' => filesize($path), + ], + ]; + } + + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName(): string + { + return 'ezimage'; + } + + /** + * @return array{ + * mimeTypes: array{ + * type: string, + * default: array{}, + * } + * } + */ + public function getSettingsSchema(): array + { + return [ + 'mimeTypes' => [ + 'type' => 'choice', + 'default' => [], + ], + ]; + } + + /** + * Get a valid $fieldSettings value. + * + * @return array{ + * mimeTypes: array<string>, + * } + */ + public function getValidFieldSettings(): array + { + return [ + 'mimeTypes' => [ + 'image/jpeg', + 'image/png', + ], + ]; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return array{ + * somethingUnknown: int, + * } + */ + public function getInvalidFieldSettings(): array + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array{ + * FileSizeValidator: array{ + * maxFileSize: array{ + * type: string, + * default: null, + * } + * }, + * AlternativeTextValidator: array{ + * required: array{ + * type: string, + * default: bool, + * } + * }, + * } + */ + public function getValidatorSchema(): array + { + return [ + 'FileSizeValidator' => [ + 'maxFileSize' => [ + 'type' => 'numeric', + 'default' => null, + ], + ], + 'AlternativeTextValidator' => [ + 'required' => [ + 'type' => 'bool', + 'default' => false, + ], + ], + ]; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return array{ + * FileSizeValidator: array{ + * maxFileSize: numeric, + * }, + * AlternativeTextValidator: array{ + * required: bool, + * }, + * } + */ + public function getValidValidatorConfiguration(): array + { + return [ + 'FileSizeValidator' => [ + 'maxFileSize' => 2.0, + ], + 'AlternativeTextValidator' => [ + 'required' => true, + ], + ]; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return array{ + * StringLengthValidator: array{ + * minStringLength: \stdClass, + * }, + * } + */ + public function getInvalidValidatorConfiguration(): array + { + return [ + 'StringLengthValidator' => [ + 'minStringLength' => new stdClass(), + ], + ]; + } + + /** + * Get initial field data for valid object creation. + */ + public function getValidCreationFieldData(): ImageValue + { + $fixtureData = $this->getFixtureData(); + + return new ImageValue($fixtureData['create']); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName(): string + { + return 'My icy flower at night'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + */ + public function assertFieldDataLoadedCorrect(Field $field): void + { + self::assertInstanceOf( + ImageValue::class, + $field->value + ); + + $fixtureData = $this->getFixtureData(); + $expectedData = $fixtureData['create']; + + // Will be nullified by external storage + $expectedData['inputUri'] = null; + + // Will be changed by external storage as fileName will be decorated with a hash + $expectedData['fileName'] = $field->value->fileName; + + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + + self::assertTrue( + $this->uriExistsOnIO($field->value->uri), + "Asserting that {$field->value->uri} exists." + ); + + self::$loadedImagePath = $field->value->id; + } + + public function provideInvalidCreationFieldData(): array + { + return [ + // will fail because the provided file doesn't exist, and fileSize/fileName won't be set + [ + new ImageValue( + [ + 'inputUri' => __DIR__ . '/_fixtures/nofile.png', + ] + ), + InvalidArgumentException::class, + ], + ]; + } + + /** + * Get update field externals data. + */ + public function getValidUpdateFieldData(): ImageValue + { + $fixtureData = $this->getFixtureData(); + + return new ImageValue($fixtureData['update']); + } + + public function assertUpdatedFieldDataLoadedCorrect(Field $field): void + { + self::assertInstanceOf( + ImageValue::class, + $field->value + ); + + $fixtureData = $this->getFixtureData(); + $expectedData = $fixtureData['update']; + + // Will change during storage + $expectedData['inputUri'] = null; + + // Will change during storage as fileName will be decorated with a hash + $expectedData['fileName'] = $field->value->fileName; + + $expectedData['uri'] = $field->value->uri; + + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + + self::assertTrue( + $this->uriExistsOnIO($field->value->uri), + "Asserting that file {$field->value->uri} exists" + ); + } + + public function provideInvalidUpdateFieldData(): array + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field): void + { + $this->assertFieldDataLoadedCorrect($field); + + $this->assertEquals( + self::$loadedImagePath, + $field->value->id + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData(): array + { + return [ + [ + new ImageValue( + [ + 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), + 'fileName' => 'Icy-Night-Flower.jpg', + 'alternativeText' => 'My icy flower at night', + ] + ), + [ + 'inputUri' => $path, + 'path' => $path, + 'fileName' => 'Icy-Night-Flower.jpg', + 'alternativeText' => 'My icy flower at night', + 'fileSize' => null, + 'id' => null, + 'imageId' => null, + 'uri' => null, + 'width' => null, + 'height' => null, + 'additionalData' => [], + 'mime' => null, + ], + ], + [ + new ImageValue( + [ + 'id' => $path = 'var/test/storage/images/file.png', + 'fileName' => 'Icy-Night-Flower.jpg', + 'alternativeText' => 'My icy flower at night', + 'fileSize' => 23, + 'imageId' => '1-2', + 'uri' => "/$path", + 'width' => 123, + 'height' => 456, + 'mime' => 'image/png', + ] + ), + [ + 'id' => $path, + 'path' => $path, + 'fileName' => 'Icy-Night-Flower.jpg', + 'alternativeText' => 'My icy flower at night', + 'fileSize' => 23, + 'inputUri' => null, + 'imageId' => '1-2', + 'uri' => "/$path", + 'width' => 123, + 'height' => 456, + 'additionalData' => [], + 'mime' => 'image/png', + ], + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData(): array + { + $fixture = $this->getFixtureData(); + + return [ + [ + $fixture['create'], + $this->getValidCreationFieldData(), + ], + ]; + } + + public function testInherentCopyForNewLanguage(): void + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $type = $this->createContentType( + $this->getValidFieldSettings(), + $this->getValidValidatorConfiguration(), + [], + // Causes a copy of the image value for each language in legacy + // storage + ['isTranslatable' => false] + ); + + $draft = $this->createContent($this->getValidCreationFieldData(), $type); + + $updateStruct = $contentService->newContentUpdateStruct(); + $updateStruct->initialLanguageCode = 'ger-DE'; + $updateStruct->setField('name', 'Sindelfingen'); + + // Automatically creates a copy of the image field in the back ground + $updatedDraft = $contentService->updateContent($draft->versionInfo, $updateStruct); + + $paths = []; + foreach ($updatedDraft->getFields() as $field) { + if ($field->fieldDefIdentifier === 'data') { + $paths[$field->languageCode] = $field->value->uri; + } + } + + $this->assertTrue( + isset($paths['eng-US']) && isset($paths['ger-DE']), + 'Failed asserting that file path for all languages were found in draft' + ); + + $this->assertEquals( + $paths['eng-US'], + $paths['ger-DE'] + ); + + $contentService->deleteContent($updatedDraft->contentInfo); + + foreach ($paths as $uri) { + self::assertFalse( + $this->uriExistsOnIO($uri), + "$uri has not been removed" + ); + } + } + + /** + * @return array<array{ + * \Ibexa\Core\FieldType\Image\Value + * }> + */ + public function providerForTestIsEmptyValue(): array + { + return [ + [new ImageValue()], + ]; + } + + /** + * @return array<array{ + * \Ibexa\Core\FieldType\Image\Value + * }> + */ + public function providerForTestIsNotEmptyValue(): array + { + return [ + [ + $this->getValidCreationFieldData(), + ], + ]; + } + + /** + * Covers EZP-23080. + */ + public function testUpdatingImageMetadataOnlyWorks(): void + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $type = $this->createContentType( + $this->getValidFieldSettings(), + $this->getValidValidatorConfiguration(), + [] + ); + + $draft = $this->createContent($this->getValidCreationFieldData(), $type); + + /** @var \Ibexa\Core\FieldType\Image\Value $imageFieldValue */ + $imageFieldValue = $draft->getFieldValue('data'); + $initialValueImageUri = $imageFieldValue->uri; + + // update alternative text + $imageFieldValue->alternativeText = __METHOD__; + $updateStruct = $contentService->newContentUpdateStruct(); + $updateStruct->setField('data', $imageFieldValue); + $updatedDraft = $contentService->updateContent($draft->versionInfo, $updateStruct); + + /** @var \Ibexa\Core\FieldType\Image\Value $updatedImageValue */ + $updatedImageValue = $updatedDraft->getFieldValue('data'); + + self::assertEquals($initialValueImageUri, $updatedImageValue->uri); + self::assertEquals(__METHOD__, $updatedImageValue->alternativeText); + } + + /** + * @see https://issues.ibexa.co/browse/EZP-23152 + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testThatRemovingDraftDoesntRemovePublishedImages(): void + { + $repository = $this->getRepository(); + + // Load services + $contentService = $repository->getContentService(); + + // create content and publish image + $content = $this->publishNewImage( + 'EZP23152_1', + $this->getValidCreationFieldData(), + [2] + ); + $originalFileUri = $this->getImageURI($content); + + self::assertTrue( + $this->uriExistsOnIO($originalFileUri), + "Asserting image file $originalFileUri exists." + ); + + // Create a new draft and update it + $updatedDraft = $contentService->createContentDraft($content->contentInfo); + $contentUpdateStruct = $contentService->newContentUpdateStruct(); + $contentUpdateStruct->initialLanguageCode = 'eng-GB'; + $contentUpdateStruct->setField('name', 'EZP23152_2'); + $updatedDraft = $contentService->updateContent($updatedDraft->versionInfo, $contentUpdateStruct); + + // remove the newly published content version, verify that the original file exists + $contentService->deleteVersion($updatedDraft->versionInfo); + self::assertTrue( + $this->uriExistsOnIO($originalFileUri), + "Asserting original image file $originalFileUri exists." + ); + + // delete content + $contentService->deleteContent($content->contentInfo); + self::assertFalse( + $this->uriExistsOnIO($originalFileUri), + "Asserting image file $originalFileUri has been removed." + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testUpdateImageAltTextOnly(): void + { + $content = $this->publishNewImage( + __METHOD__, + new ImageValue( + [ + 'inputUri' => __DIR__ . '/_fixtures/image.jpg', + 'fileName' => 'image.jpg', + 'fileSize' => filesize(__DIR__ . '/_fixtures/image.jpg'), + 'alternativeText' => 'Initial alternative text', + ] + ), + [2] + ); + + /** @var \Ibexa\Core\FieldType\Image\Value $imageField */ + $imageField = $content->getFieldValue('image'); + $updatedAlternativeText = 'Updated alternative text'; + $imageField->alternativeText = $updatedAlternativeText; + + $content = $this->updateImage($content, $imageField); + + self::assertSame( + $updatedAlternativeText, + $content->getFieldValue('image')->alternativeText + ); + } + + protected function checkSearchEngineSupport(): void + { + if ($this->getSetupFactory() instanceof Legacy) { + $this->markTestSkipped( + "'ezimage' field type is not searchable with Legacy Search Engine" + ); + } + } + + protected function getValidSearchValueOne(): ImageValue + { + return new ImageValue( + [ + 'fileName' => '1234eeee1234-cafe-terrace-at-night.jpg', + 'inputUri' => ($path = __DIR__ . '/_fixtures/1234eeee1234-image.jpg'), + 'alternativeText' => 'café terrace at night, also known as the cafe terrace on the place du forum', + 'fileSize' => filesize($path), + ] + ); + } + + protected function getValidSearchValueTwo(): ImageValue + { + return new ImageValue( + [ + 'fileName' => '2222eeee1111-thatched-cottages-at-cordeville.png', + 'inputUri' => ($path = __DIR__ . '/_fixtures/2222eeee1111-image.png'), + 'alternativeText' => 'chaumes de cordeville à auvers-sur-oise', + 'fileSize' => filesize($path), + ] + ); + } + + protected function getSearchTargetValueOne(): string + { + $value = $this->getValidSearchValueOne(); + + /** + * ensure case-insensitivity. + * + * @phpstan-ignore-next-line + */ + return strtoupper($value->fileName); + } + + protected function getSearchTargetValueTwo(): string + { + $value = $this->getValidSearchValueTwo(); + + /** + * ensure case-insensitivity. + * + * @phpstan-ignore-next-line + */ + return strtoupper($value->fileName); + } + + protected function getAdditionallyIndexedFieldData(): array + { + return [ + [ + 'alternative_text', + $this->getValidSearchValueOne()->alternativeText, + $this->getValidSearchValueTwo()->alternativeText, + ], + [ + 'file_size', + $this->getValidSearchValueOne()->fileSize, + $this->getValidSearchValueTwo()->fileSize, + ], + [ + 'mime_type', + // ensure case-insensitivity + 'IMAGE/JPEG', + 'IMAGE/PNG', + ], + ]; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testRemovingContentRemovesImages(): void + { + $repository = $this->getRepository(); + + // Load services + $contentService = $repository->getContentService(); + + $content = $this->publishNewImage('My Image', $this->getValidCreationFieldData()); + $originalFileUri = $this->getImageURI($content); + + // sanity check + self::assertTrue( + $this->uriExistsOnIO($originalFileUri), + "Asserting image file $originalFileUri exists" + ); + + $content = $this->updateImage($content, $this->getValidUpdateFieldData()); + $updatedFileUri = $this->getImageURI($content); + + // sanity check + self::assertNotEquals($originalFileUri, $updatedFileUri); + + $contentService->deleteContent($content->contentInfo); + + self::assertFalse( + $this->uriExistsOnIO($updatedFileUri), + "Asserting updated image file $updatedFileUri has been removed" + ); + + self::assertFalse( + $this->uriExistsOnIO($originalFileUri), + "Asserting original image file $originalFileUri has been removed" + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testRemovingDraftRemovesOldImage(): void + { + $repository = $this->getRepository(); + + // Load services + $contentService = $repository->getContentService(); + + $contentVersion1 = $this->publishNewImage('My Image', $this->getValidCreationFieldData()); + $originalFileUri = $this->getImageURI($contentVersion1); + + // sanity check + self::assertTrue( + $this->uriExistsOnIO($originalFileUri), + "Asserting image file $originalFileUri exists" + ); + + $contentVersion2 = $this->updateImage($contentVersion1, $this->getValidUpdateFieldData()); + $updatedFileUri = $this->getImageURI($contentVersion2); + + // delete 1st version with original image + $contentService->deleteVersion( + // reload 1st version (its state changed) to delete + $contentService->loadVersionInfo( + $contentVersion1->contentInfo, + $contentVersion1->getVersionInfo()->versionNo + ) + ); + + // updated image should be available, but original image should be gone now + self::assertTrue( + $this->uriExistsOnIO($updatedFileUri), + "Asserting image file {$updatedFileUri} exists" + ); + + self::assertFalse( + $this->uriExistsOnIO($originalFileUri), + "Asserting image file {$originalFileUri} has been removed" + ); + } + + /** + * @throws \Doctrine\DBAL\Driver\Exception + * @throws \Doctrine\DBAL\Exception + * @throws \ErrorException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + public function testDeleteImageWithCorruptedName(): void + { + $ioService = $this->getSetupFactory()->getServiceContainer()->get(LegacyIOService::class); + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + self::assertInstanceOf(IOServiceInterface::class, $ioService); + + $content = $this->publishNewImage( + __METHOD__, + new ImageValue( + [ + 'inputUri' => __DIR__ . '/_fixtures/image.jpg', + 'fileName' => 'image.jpg', + 'fileSize' => filesize(__DIR__ . '/_fixtures/image.jpg'), + 'alternativeText' => 'Alternative', + ] + ), + [2] + ); + + $imageFieldDefinition = $content->getContentType()->getFieldDefinition('image'); + self::assertNotNull($imageFieldDefinition); + + // sanity check + $this->assertImageExists(true, $ioService, $content); + + $record = $this->fetchXML( + $content->id, + $content->getVersionInfo()->versionNo, + $imageFieldDefinition->id + ); + + $document = $this->corruptImageFieldXML($record); + + $this->updateXML( + $content->id, + $content->getVersionInfo()->versionNo, + $imageFieldDefinition->id, + $document + ); + + // reload content to get the field value with the corrupted path + $content = $contentService->loadContent($content->id); + $this->assertImageExists(false, $ioService, $content); + + $contentService->deleteContent($content->getVersionInfo()->getContentInfo()); + + // Expect no League\Flysystem\CorruptedPathDetected thrown + } + + /** + * @return array<string,mixed> + * + * @throws \Doctrine\DBAL\Exception + * @throws \ErrorException + * @throws \Doctrine\DBAL\Driver\Exception + */ + private function fetchXML(int $contentId, int $versionNo, int $fieldDefinitionId): array + { + $connection = $this->getRawDatabaseConnection(); + + $query = $connection->createQueryBuilder(); + $query + ->select('data_text') + ->from(Gateway::CONTENT_FIELD_TABLE) + ->andWhere('contentclassattribute_id = :contentclassattribute_id') + ->andWhere('version = :version') + ->andWhere('contentobject_id = :contentobject_id') + ->setParameter('contentclassattribute_id', $fieldDefinitionId, ParameterType::INTEGER) + ->setParameter('version', $versionNo, ParameterType::INTEGER) + ->setParameter('contentobject_id', $contentId, ParameterType::INTEGER); + + $result = $query->execute()->fetchAssociative(); + self::assertNotFalse($result); + + return $result; + } + + /** + * @param array<string,mixed> $row + */ + private function corruptImageFieldXML(array $row): DOMDocument + { + $corruptedChar = '­'; + + $document = new DOMDocument('1.0', 'utf-8'); + $document->loadXML($row['data_text']); + $elements = $document->getElementsByTagName('ezimage'); + $element = $elements->item(0); + self::assertInstanceOf(DOMElement::class, $element); + $element->setAttribute('filename', $element->getAttribute('filename') . $corruptedChar); + $element->setAttribute('url', $element->getAttribute('url') . $corruptedChar); + + return $document; + } + + /** + * @throws \Doctrine\DBAL\Exception + * @throws \ErrorException + */ + private function updateXML( + int $contentId, + int $versionNo, + int $fieldDefinitionId, + DOMDocument $document + ): void { + $connection = $this->getRawDatabaseConnection(); + + $query = $connection->createQueryBuilder(); + $query + ->update(Gateway::CONTENT_FIELD_TABLE) + ->set('data_text', ':data_text') + ->setParameter('data_text', $document->saveXML(), ParameterType::STRING) + ->andWhere('contentclassattribute_id = :contentclassattribute_id') + ->andWhere('version = :version') + ->andWhere('contentobject_id = :contentobject_id') + ->setParameter('contentclassattribute_id', $fieldDefinitionId, ParameterType::INTEGER) + ->setParameter('version', $versionNo, ParameterType::INTEGER) + ->setParameter('contentobject_id', $contentId, ParameterType::INTEGER); + + $query->execute(); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function publishNewImage( + string $name, + ImageValue $imageValue, + array $parentLocationIDs = [] + ): Content { + $repository = $this->getRepository(false); + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + $contentTypeService = $repository->getContentTypeService(); + + $contentCreateStruct = $contentService->newContentCreateStruct( + $contentTypeService->loadContentTypeByIdentifier('image'), + 'eng-GB' + ); + $contentCreateStruct->setField('name', $name); + $contentCreateStruct->setField('image', $imageValue); + + $locationCreateStructList = []; + foreach ($parentLocationIDs as $parentLocationID) { + $locationCreateStructList[] = $locationService->newLocationCreateStruct( + $parentLocationID + ); + } + + return $contentService->publishVersion( + $contentService + ->createContent($contentCreateStruct, $locationCreateStructList) + ->getVersionInfo() + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function updateImage(Content $publishedImageContent, ImageValue $newImageValue): Content + { + $repository = $this->getRepository(false); + $contentService = $repository->getContentService(); + + $contentDraft = $contentService->createContentDraft($publishedImageContent->contentInfo); + $contentUpdateStruct = $contentService->newContentUpdateStruct(); + $contentUpdateStruct->setField('image', $newImageValue); + $contentService->updateContent($contentDraft->getVersionInfo(), $contentUpdateStruct); + + $content = $contentService->publishVersion($contentDraft->getVersionInfo()); + + // reload Content to make sure proper data has been persisted + return $contentService->loadContentByContentInfo($content->contentInfo); + } + + private function getImageURI(Content $content): string + { + return $content->getFieldValue('image')->uri; + } + + private function assertImageExists(bool $expectExists, IOServiceInterface $ioService, Content $content): void + { + $imageField = $content->getField('image'); + self::assertNotNull($imageField, 'Image field not found'); + + /** @var \Ibexa\Core\FieldType\Image\Value $imageFieldValue */ + $imageFieldValue = $imageField->value; + self::assertSame($expectExists, $ioService->exists($imageFieldValue->id)); + } +} + +class_alias(ImageIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\ImageIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php b/tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php new file mode 100644 index 0000000000..2e33f371c8 --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/IntegerIntegrationTest.php @@ -0,0 +1,321 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\Integer\Value as IntegerValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class IntegerIntegrationTest extends SearchBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezinteger'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return []; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return []; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return [ + 'IntegerValueValidator' => [ + 'minIntegerValue' => [ + 'type' => 'int', + 'default' => null, + ], + 'maxIntegerValue' => [ + 'type' => 'int', + 'default' => null, + ], + ], + ]; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return [ + 'IntegerValueValidator' => [ + 'minIntegerValue' => 23, + 'maxIntegerValue' => 42, + ], + ]; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'IntegerValueValidator' => [ + 'minStringLength' => new \stdClass(), + ], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new IntegerValue(23); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return '23'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + IntegerValue::class, + $field->value + ); + + $expectedData = [ + 'value' => 23, + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + new \stdClass(), + InvalidArgumentType::class, + ], + [ + new IntegerValue(5), + ContentFieldValidationException::class, + ], + [ + new IntegerValue(127), + ContentFieldValidationException::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + return new IntegerValue(42); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + IntegerValue::class, + $field->value + ); + + $expectedData = [ + 'value' => 42, + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + IntegerValue::class, + $field->value + ); + + $expectedData = [ + 'value' => 23, + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new IntegerValue(23), + 23, + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + 42, + new IntegerValue(42), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new IntegerValue()], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + [new IntegerValue(0)], + [new IntegerValue(0.0)], + ]; + } + + protected function getValidSearchValueOne() + { + return 25; + } + + protected function getValidSearchValueTwo() + { + return 26; + } + + protected function getFullTextIndexedFieldData() + { + return [ + ['25', '26'], + ]; + } +} + +class_alias(IntegerIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\IntegerIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php b/tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php new file mode 100644 index 0000000000..aa0cc736dd --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/KeywordIntegrationTest.php @@ -0,0 +1,595 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\Keyword\Value as KeywordValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class KeywordIntegrationTest extends SearchMultivaluedBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezkeyword'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return []; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return []; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return []; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return []; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'unknown' => ['value' => 23], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new KeywordValue(['foo', 'bar', 'sindelfingen']); + } + + /** + * {@inheritdoc} + */ + public function getValidMultilingualFieldData(array $languageCodes) + { + $data = []; + foreach ($languageCodes as $languageCode) { + $data[$languageCode] = new KeywordValue( + [ + "{$languageCode} bar", + "{$languageCode} foo", + "$languageCode sindelfingen", + ] + ); + } + + return $data; + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'foo, bar, sindelfingen'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + KeywordValue::class, + $field->value + ); + + $this->assertEquals( + ['foo' => true, 'bar' => true, 'sindelfingen' => true], + array_fill_keys($field->value->values, true) + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + new \stdClass(), + InvalidArgumentType::class, + ], + [ + 23, + InvalidArgumentType::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + return new KeywordValue(['bielefeld']); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + KeywordValue::class, + $field->value + ); + + $this->assertEquals( + ['bielefeld' => true], + array_fill_keys($field->value->values, true) + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + KeywordValue::class, + $field->value + ); + + $this->assertEquals( + ['foo' => true, 'bar' => true, 'sindelfingen' => true], + array_fill_keys($field->value->values, true) + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new KeywordValue(['bielefeld', 'sindelfingen']), + ['bielefeld', 'sindelfingen'], + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + ['sindelfeld', 'bielefingen'], + new KeywordValue(['sindelfeld', 'bielefingen']), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new KeywordValue()], + [new KeywordValue(null)], + [new KeywordValue([])], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + [ + new KeywordValue(['0']), + ], + ]; + } + + /** + * Test updating multiple contents with ezkeyword field preserves proper fields values. + */ + public function testUpdateContentKeywords() + { + $contentType = $this->testCreateContentType(); + $contentService = $this->getRepository()->getContentService(); + + $value01 = new KeywordValue(['foo', 'FOO', 'bar', 'baz']); + $contentDraft = $this->createContent($value01, $contentType); + $publishedContent01 = $contentService->publishVersion($contentDraft->versionInfo); + $this->assertContentFieldHasCorrectData($publishedContent01->contentInfo->id, $value01); + + // create another content with the same value + $value02 = $value01; + $contentDraft = $this->createContent($value02, $contentType); + $publishedContent02 = $contentService->publishVersion($contentDraft->versionInfo); + $this->assertContentFieldHasCorrectData($publishedContent02->contentInfo->id, $value02); + + // for the first content, create draft, remove one keyword and publish new version + $contentDraft = $contentService->createContentDraft($publishedContent01->contentInfo); + $updateStruct = $contentService->newContentUpdateStruct(); + $value01 = new KeywordValue(['foo', 'FOO', 'bar']); + $updateStruct->setField('data', $value01); + $contentDraft = $contentService->updateContent($contentDraft->versionInfo, $updateStruct); + $publishedContent01 = $contentService->publishVersion($contentDraft->versionInfo); + $this->assertContentFieldHasCorrectData($publishedContent01->contentInfo->id, $value01); + // reload and check the second content value01 + $this->assertContentFieldHasCorrectData($publishedContent02->contentInfo->id, $value02); + + // delete the second content + $contentService->deleteContent($publishedContent02->contentInfo); + // check if the first content was not affected + $this->assertContentFieldHasCorrectData($publishedContent01->contentInfo->id, $value01); + } + + /** + * {@inheritdoc} + */ + protected function createContent($fieldData, $contentType = null) + { + if ($contentType === null) { + $contentType = $this->testCreateContentType(); + } + + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-US'); + $createStruct->setField('name', 'Test object'); + $createStruct->setField( + 'data', + $fieldData + ); + + $createStruct->remoteId = md5(uniqid('', true) . microtime()); + $createStruct->alwaysAvailable = true; + + return $contentService->createContent($createStruct); + } + + /** + * Check that the given Content Object contains proper Keywords. + * + * @param int $contentId + * @param \Ibexa\Core\FieldType\Keyword\Value $value + */ + private function assertContentFieldHasCorrectData($contentId, KeywordValue $value) + { + $contentService = $this->getRepository()->getContentService(); + $loadedContent = $contentService->loadContent($contentId, ['eng-US']); + $dataField = $loadedContent->getField('data'); + sort($dataField->value->values); + sort($value->values); + $this->assertEquals($value, $dataField->value); + } + + /** + * Test going back to different version which contains different keywords than the other version. + */ + public function testGoBackToDifferentVersionWithDifferentKeywords(): void + { + $contentType = $this->testCreateContentType(); + $contentService = $this->getRepository()->getContentService(); + + $value01 = new KeywordValue(['foo', 'FOO', 'bar', 'baz']); + $contentDraft01 = $this->createContent($value01, $contentType); + $publishedContent01 = $contentService->publishVersion($contentDraft01->versionInfo); + + // for the first content, create draft, remove one keyword, add new keyword, and publish new version + $contentDraft = $contentService->createContentDraft($publishedContent01->contentInfo); + $updateStruct = $contentService->newContentUpdateStruct(); + $value02 = new KeywordValue(['foo', 'FOO', 'bar', 'far']); + $updateStruct->setField('data', $value02); + $contentDraft02 = $contentService->updateContent($contentDraft->versionInfo, $updateStruct); + $publishedContent01 = $contentService->publishVersion($contentDraft02->versionInfo); + + // go back to the first version and check whether keywords are correct + $contentDraft03 = $contentService->createContentDraft($publishedContent01->contentInfo, $contentDraft01->versionInfo); + $contentService->deleteContent($publishedContent01->contentInfo); + $this->assertEqualsCanonicalizing($contentDraft03->getFieldValue('data'), $value01); + } + + public function testKeywordsAreCaseSensitive() + { + $contentType = $this->testCreateContentType(); + $publishedContent01 = $this->createAndPublishContent('Foo', $contentType, md5(uniqid(__METHOD__, true))); + $publishedContent02 = $this->createAndPublishContent('foo', $contentType, md5(uniqid(__METHOD__, true))); + + $data = $publishedContent01->getField('data')->value; + $this->assertCount(1, $data->values); + $this->assertEquals('Foo', $data->values[0]); + + $data = $publishedContent02->getField('data')->value; + $this->assertCount(1, $data->values); + $this->assertEquals('foo', $data->values[0]); + } + + /** + * Create and publish content of $contentType with $fieldData. + * + * @param mixed $fieldData + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param string $remoteId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + protected function createAndPublishContent($fieldData, ContentType $contentType, $remoteId) + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-US'); + $createStruct->setField('name', 'Test object'); + $createStruct->setField( + 'data', + $fieldData + ); + + $createStruct->remoteId = $remoteId; + $createStruct->alwaysAvailable = true; + + $contentDraft = $contentService->createContent($createStruct); + + return $contentService->publishVersion($contentDraft->versionInfo); + } + + protected function getValidSearchValueOne() + { + return 'add'; + } + + protected function getValidSearchValueTwo() + { + return 'branch'; + } + + protected function getValidMultivaluedSearchValuesOne() + { + return ['add', 'branch']; + } + + protected function getValidMultivaluedSearchValuesTwo() + { + return ['commit', 'delete']; + } + + public function checkFullTextSupport() + { + // Does nothing + } + + protected function getFullTextIndexedFieldData() + { + return [ + ['add', 'branch'], + ]; + } + + public function providerForTestTruncateField() + { + return [ + [new KeywordValue()], + [new KeywordValue(null)], + [new KeywordValue([])], + // an empty array is what actually REST API sets when field should be truncated + [[]], + ]; + } + + /** + * Test that setting an empty value truncates field data. + * + * @dataProvider providerForTestTruncateField + * + * @param mixed $emptyValue data representing an empty value + * + * @todo Move this method to BaseIntegrationTest when fixed for all field types. + */ + public function testTruncateField($emptyValue) + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $fieldType = $repository->getFieldTypeService()->getFieldType($this->getTypeName()); + + $contentDraft = $this->testCreateContent(); + $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); + + $contentDraft = $contentService->createContentDraft($publishedContent->contentInfo); + $updateStruct = $contentService->newContentUpdateStruct(); + $updateStruct->setField('data', $emptyValue); + $contentDraft = $contentService->updateContent($contentDraft->versionInfo, $updateStruct); + $publishedContent = $contentService->publishVersion($contentDraft->versionInfo); + + $content = $contentService->loadContent($publishedContent->contentInfo->id, ['eng-US']); + + $fieldValue = $content->getField('data')->value; + self::assertTrue( + $fieldType->isEmptyValue($fieldValue), + 'Field value is not empty: ' . var_export($fieldValue, true) + ); + } + + /** + * Create test Content with ezkeyword type. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content[] + */ + protected function createKeywordContent() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + + $createStruct = $contentTypeService->newContentTypeCreateStruct('content-keyword'); + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->remoteId = 'content-keyword-123'; + $createStruct->names = ['eng-GB' => 'Keywords']; + $createStruct->creatorId = 14; + $createStruct->creationDate = new \DateTime(); + + $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('tags', 'ezkeyword'); + $fieldCreate->names = ['eng-GB' => 'Tags']; + $fieldCreate->fieldGroup = 'main'; + $fieldCreate->position = 1; + $fieldCreate->isTranslatable = false; + $fieldCreate->isSearchable = true; + + $createStruct->addFieldDefinition($fieldCreate); + + $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); + $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + + $toCreate = [ + 'content-keyword-456' => ['foo', 'bar'], + 'content-keyword-789' => ['bar', 'foobar'], + ]; + $createdContent = []; + foreach ($toCreate as $remoteId => $tagsString) { + $createStruct->remoteId = $remoteId; + $createStruct->alwaysAvailable = false; + $createStruct->setField( + 'tags', + $tagsString + ); + + $draft = $contentService->createContent($createStruct); + $createdContent[] = $contentService->publishVersion($draft->getVersionInfo()); + } + + $this->refreshSearch($repository); + + return $createdContent; + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testFindContentFieldCriterion() + { + $this->createKeywordContent(); + $repository = $this->getRepository(); + + $criterion = new Criterion\Field('tags', Criterion\Operator::IN, ['foo']); + $query = new Query(['query' => $criterion]); + + $searchService = $repository->getSearchService(); + $searchResult = $searchService->findContent($query); + + $this->assertEquals(1, $searchResult->totalCount); + } +} + +class_alias(KeywordIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\KeywordIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php b/tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php new file mode 100644 index 0000000000..36541ff5de --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/MapLocationIntegrationTest.php @@ -0,0 +1,331 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\MapLocation\Value as MapLocationValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class MapLocationIntegrationTest extends BaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezgmaplocation'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return []; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return []; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return []; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return []; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'unknown' => ['value' => 23], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new MapLocationValue( + [ + 'latitude' => 51.559997, + 'longitude' => 6.767921, + 'address' => 'Bielefeld', + ] + ); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'Bielefeld'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertEquals( + $this->getValidCreationFieldData(), + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + new \stdClass(), + InvalidArgumentType::class, + ], + [ + 23, + InvalidArgumentType::class, + ], + [ + new MapLocationValue( + [ + 'latitude' => 'string', + ] + ), + InvalidArgumentType::class, + ], + [ + new MapLocationValue( + [ + 'latitude' => 23.42, + 'longitude' => 'invalid', + ] + ), + InvalidArgumentType::class, + ], + [ + new MapLocationValue( + [ + 'latitude' => 23.42, + 'longitude' => 42.23, + 'address' => true, + ] + ), + InvalidArgumentType::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + // https://maps.google.de/maps?qll=,&spn=0.139491,0.209942&sll=51.983611,8.574829&sspn=0.36242,0.839767&oq=Punta+Cana&t=h&hnear=Punta+Cana,+La+Altagracia,+Dominikanische+Republik&z=13 + return new MapLocationValue( + [ + 'latitude' => 18.524701, + 'longitude' => -68.363113, + 'address' => 'Punta Cana', + ] + ); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertEquals( + $this->getValidUpdateFieldData(), + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertEquals( + $this->getValidCreationFieldData(), + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new MapLocationValue( + [ + 'latitude' => 51.559997, + 'longitude' => 6.767921, + 'address' => 'Bielefeld', + ] + ), + [ + 'latitude' => 51.559997, + 'longitude' => 6.767921, + 'address' => 'Bielefeld', + ], + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + [ + 'latitude' => 51.559997, + 'longitude' => 6.767921, + 'address' => 'Bielefeld', + ], + new MapLocationValue( + [ + 'latitude' => 51.559997, + 'longitude' => 6.767921, + 'address' => 'Bielefeld', + ] + ), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new MapLocationValue()], + [ + new MapLocationValue( + [ + 'latitude' => null, + 'longitude' => null, + ] + ), + ], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + [ + new MapLocationValue( + [ + 'latitude' => 0, + 'longitude' => 0, + ] + ), + ], + ]; + } +} + +class_alias(MapLocationIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\MapLocationIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php b/tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php new file mode 100644 index 0000000000..b8b1d7a23e --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/MediaIntegrationTest.php @@ -0,0 +1,450 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\Base\Exceptions\InvalidArgumentValue; +use Ibexa\Core\FieldType\Media\Type as MediaType; +use Ibexa\Core\FieldType\Media\Value as MediaValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class MediaIntegrationTest extends FileSearchBaseIntegrationTest +{ + /** + * Stores the loaded image path for copy test. + */ + protected static $loadedMediaPath; + + /** + * IOService storage prefix for the tested Type's files. + * + * @var string + */ + protected static $storagePrefixConfigKey = 'ibexa.io.binary_file.storage.prefix'; + + protected function getStoragePrefix() + { + return $this->getConfigValue(self::$storagePrefixConfigKey); + } + + /** + * Sets up fixture data. + * + * @return array + */ + protected function getFixtureData() + { + return [ + 'create' => [ + 'id' => null, + 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), + 'fileName' => 'Icy-Night-Flower-Binary.jpg', + 'fileSize' => filesize($path), + 'mimeType' => 'image/jpeg', + // Left out 'hasControlls', 'autoplay', 'loop', 'height' and + // 'width' by intention (will be set to defaults) + ], + 'update' => [ + 'id' => null, + 'inputUri' => ($path = __DIR__ . '/_fixtures/image.png'), + 'fileName' => 'Blue-Blue-Blue-Sindelfingen.png', + 'fileSize' => filesize($path), + // Left out 'mimeType' by intention (will be auto-detected) + 'hasController' => true, + 'autoplay' => true, + 'loop' => true, + 'width' => 23, + 'height' => 42, + ], + ]; + } + + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezmedia'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return [ + 'mediaType' => [ + 'type' => 'choice', + 'default' => MediaType::TYPE_HTML5_VIDEO, + ], + ]; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return [ + 'mediaType' => MediaType::TYPE_FLASH, + ]; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return [ + 'FileSizeValidator' => [ + 'maxFileSize' => [ + 'type' => 'int', + 'default' => false, + ], + ], + ]; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return [ + 'FileSizeValidator' => [ + 'maxFileSize' => 2 * 1024 * 1024, // 2 MB + ], + ]; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'StringLengthValidator' => [ + 'minStringLength' => new \stdClass(), + ], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + $fixtureData = $this->getFixtureData(); + + return new MediaValue($fixtureData['create']); + } + + /** + * Get name generated by the given field type (via or fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'Icy-Night-Flower-Binary.jpg'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + MediaValue::class, + $field->value + ); + + $fixtureData = $this->getFixtureData(); + $expectedData = $fixtureData['create']; + + // Will change during storage + unset($expectedData['id']); + $expectedData['inputUri'] = null; + + $this->assertNotEmpty($field->value->id); + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + + $this->assertTrue( + $this->uriExistsOnIO($field->value->uri), + "File {$field->value->uri} doesn't exist." + ); + + self::$loadedMediaPath = $field->value->id; + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + [ + 'id' => '/foo/bar/sindelfingen.pdf', + ], + InvalidArgumentValue::class, + ], + [ + new MediaValue( + [ + 'id' => '/foo/bar/sindelfingen.pdf', + ] + ), + InvalidArgumentValue::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + $fixtureData = $this->getFixtureData(); + + return new MediaValue($fixtureData['update']); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + MediaValue::class, + $field->value + ); + + $fixtureData = $this->getFixtureData(); + $expectedData = $fixtureData['update']; + + // Will change during storage + unset($expectedData['id']); + $expectedData['inputUri'] = null; + + $this->assertNotEmpty($field->value->id); + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + + $this->assertTrue( + $this->uriExistsOnIO($field->value->uri), + "File {$field->value->uri} doesn't exist." + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertFieldDataLoadedCorrect($field); + + $this->assertEquals( + self::$loadedMediaPath, + $field->value->id + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + $fixture = $this->getFixtureData(); + $expected = $fixture['create']; + + $expected['uri'] = $expected['inputUri']; + $expected['path'] = $expected['inputUri']; + + // Defaults set by type + $expected['hasController'] = false; + $expected['autoplay'] = false; + $expected['loop'] = false; + $expected['width'] = 0; + $expected['height'] = 0; + + $fieldValue = $this->getValidCreationFieldData(); + $fieldValue->uri = $expected['uri']; + + return [ + [ + $fieldValue, + $expected, + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + $fixture = $this->getFixtureData(); + $fixture['create']['uri'] = $fixture['create']['id']; + + $fieldValue = $this->getValidCreationFieldData(); + $fieldValue->uri = $fixture['create']['uri']; + + return [ + [ + $fixture['create'], + $fieldValue, + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new MediaValue()], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + ]; + } + + protected function getValidSearchValueOne() + { + return new MediaValue( + [ + 'inputUri' => ($path = __DIR__ . '/_fixtures/image.jpg'), + 'fileName' => 'blue-blue-blue-sindelfingen.jpg', + 'fileSize' => filesize($path), + ] + ); + } + + protected function getValidSearchValueTwo() + { + return new MediaValue( + [ + 'inputUri' => ($path = __DIR__ . '/_fixtures/image.png'), + 'fileName' => 'icy-night-flower-binary.png', + 'fileSize' => filesize($path), + ] + ); + } + + protected function getSearchTargetValueOne() + { + $value = $this->getValidSearchValueOne(); + // ensure case-insensitivity + return strtoupper($value->fileName); + } + + protected function getSearchTargetValueTwo() + { + $value = $this->getValidSearchValueTwo(); + // ensure case-insensitivity + return strtoupper($value->fileName); + } + + protected function getAdditionallyIndexedFieldData() + { + return [ + [ + 'file_size', + $this->getValidSearchValueOne()->fileSize, + $this->getValidSearchValueTwo()->fileSize, + ], + [ + 'mime_type', + // ensure case-insensitivity + 'IMAGE/JPEG', + 'IMAGE/PNG', + ], + ]; + } +} + +class_alias(MediaIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\MediaIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php b/tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php new file mode 100644 index 0000000000..a47546c4a7 --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/RelationIntegrationTest.php @@ -0,0 +1,380 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\Relation\Value as RelationValue; +use Ibexa\Core\Repository\Values\Content\Relation; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class RelationIntegrationTest extends SearchBaseIntegrationTest +{ + use RelationSearchBaseIntegrationTestTrait; + + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezobjectrelation'; + } + + /** + * {@inheritdoc} + */ + protected function supportsLikeWildcard($value) + { + parent::supportsLikeWildcard($value); + + return false; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @return array|\Ibexa\Contracts\Core\Repository\Values\Content\Relation[] + */ + public function getCreateExpectedRelations(Content $content) + { + $contentService = $this->getRepository()->getContentService(); + + return [ + new Relation( + [ + 'sourceFieldDefinitionIdentifier' => 'data', + 'type' => Relation::FIELD, + 'sourceContentInfo' => $content->contentInfo, + 'destinationContentInfo' => $contentService->loadContentInfo(4), + ] + ), + ]; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @return array|\Ibexa\Contracts\Core\Repository\Values\Content\Relation[] + */ + public function getUpdateExpectedRelations(Content $content) + { + $contentService = $this->getRepository()->getContentService(); + + return [ + new Relation( + [ + 'sourceFieldDefinitionIdentifier' => 'data', + 'type' => Relation::FIELD, + 'sourceContentInfo' => $content->contentInfo, + 'destinationContentInfo' => $contentService->loadContentInfo(49), + ] + ), + ]; + } + + public function getSettingsSchema() + { + return [ + 'selectionMethod' => [ + 'type' => 'int', + 'default' => 0, + ], + 'selectionRoot' => [ + 'type' => 'string', + 'default' => null, + ], + 'selectionContentTypes' => [ + 'type' => 'array', + 'default' => [], + ], + 'rootDefaultLocation' => [ + 'type' => 'bool', + 'default' => false, + ], + ]; + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\Tests\FieldType\BaseIntegrationTest::getValidatorSchema() + */ + public function getValidatorSchema() + { + return []; + } + + /** + * Get a valid $fieldSettings value. + * + * @todo Implement correctly + * + * @return mixed + */ + public function getValidFieldSettings() + { + return [ + 'selectionMethod' => 0, + 'selectionRoot' => 1, + 'selectionContentTypes' => [], + 'rootDefaultLocation' => false, + ]; + } + + /** + * Get a valid $validatorConfiguration. + * + * @todo Implement correctly + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return []; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @todo Implement correctly + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'selectionMethod' => 'a', + 'selectionRoot' => true, + 'unknownSetting' => false, + 'selectionContentTypes' => true, + ]; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @todo Implement correctly + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return ['noValidator' => true]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new RelationValue(4); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'Users'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + RelationValue::class, + $field->value + ); + + $expectedData = [ + 'destinationContentId' => 4, + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + new RelationValue([]), + InvalidArgumentType::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + return new RelationValue(49); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + self::assertInstanceOf(RelationValue::class, $field->value); + + $expectedData = [ + 'destinationContentId' => 49, + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + RelationValue::class, + $field->value + ); + + $expectedData = [ + 'destinationContentId' => 4, + ]; + + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new RelationValue(4), + [ + 'destinationContentId' => 4, + ], + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + ['destinationContentId' => 4], + new RelationValue(4), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new RelationValue()], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + ]; + } + + protected function getValidSearchValueOne() + { + // Using different values for Legacy Search Engine, in order to demonstrate that sort will + // depend on how search engine stores field type's value. Legacy stores it as integer, while + // other engines store it as string. + if ($this->getSetupFactory() instanceof Legacy) { + return 4; + } + + return 10; + } + + protected function getValidSearchValueTwo() + { + // Using different values for Legacy Search Engine, in order to demonstrate that sort will + // depend on how search engine stores field type's value. Legacy stores it as integer, while + // other engines store it as string. + if ($this->getSetupFactory() instanceof Legacy) { + return 49; + } + + return 4; + } +} + +class_alias(RelationIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\RelationIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php b/tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php new file mode 100644 index 0000000000..6f9f861d1b --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/RelationListIntegrationTest.php @@ -0,0 +1,415 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\RelationList\Type as RelationListType; +use Ibexa\Core\FieldType\RelationList\Value as RelationListValue; +use Ibexa\Core\Repository\Values\Content\Relation; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class RelationListIntegrationTest extends SearchMultivaluedBaseIntegrationTest +{ + use RelationSearchBaseIntegrationTestTrait; + + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezobjectrelationlist'; + } + + /** + * {@inheritdoc} + */ + protected function supportsLikeWildcard($value) + { + parent::supportsLikeWildcard($value); + + return false; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @return array|\Ibexa\Contracts\Core\Repository\Values\Content\Relation[] + */ + public function getCreateExpectedRelations(Content $content) + { + $contentService = $this->getRepository()->getContentService(); + + return [ + new Relation( + [ + 'sourceFieldDefinitionIdentifier' => 'data', + 'type' => Relation::FIELD, + 'sourceContentInfo' => $content->contentInfo, + 'destinationContentInfo' => $contentService->loadContentInfo(4), + ] + ), + new Relation( + [ + 'sourceFieldDefinitionIdentifier' => 'data', + 'type' => Relation::FIELD, + 'sourceContentInfo' => $content->contentInfo, + 'destinationContentInfo' => $contentService->loadContentInfo(49), + ] + ), + ]; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @return array|\Ibexa\Contracts\Core\Repository\Values\Content\Relation[] + */ + public function getUpdateExpectedRelations(Content $content) + { + $contentService = $this->getRepository()->getContentService(); + + return [ + new Relation( + [ + 'id' => null, + 'sourceFieldDefinitionIdentifier' => 'data', + 'type' => Relation::FIELD, + 'sourceContentInfo' => $content->contentInfo, + 'destinationContentInfo' => $contentService->loadContentInfo(4), + ] + ), + new Relation( + [ + 'sourceFieldDefinitionIdentifier' => 'data', + 'type' => Relation::FIELD, + 'sourceContentInfo' => $content->contentInfo, + 'destinationContentInfo' => $contentService->loadContentInfo(49), + ] + ), + new Relation( + [ + 'id' => null, + 'sourceFieldDefinitionIdentifier' => 'data', + 'type' => Relation::FIELD, + 'sourceContentInfo' => $content->contentInfo, + 'destinationContentInfo' => $contentService->loadContentInfo(54), + ] + ), + ]; + } + + public function getSettingsSchema() + { + return [ + 'selectionMethod' => [ + 'type' => 'int', + 'default' => RelationListType::SELECTION_BROWSE, + ], + 'selectionDefaultLocation' => [ + 'type' => 'string', + 'default' => null, + ], + 'selectionContentTypes' => [ + 'type' => 'array', + 'default' => [], + ], + 'rootDefaultLocation' => [ + 'type' => 'bool', + 'default' => false, + ], + ]; + } + + public function getValidatorSchema() + { + return [ + 'RelationListValueValidator' => [ + 'selectionLimit' => [ + 'type' => 'int', + 'default' => 0, + ], + ], + ]; + } + + /** + * Get a valid $fieldSettings value. + * + * @todo Implement correctly + * + * @return mixed + */ + public function getValidFieldSettings() + { + return [ + 'selectionMethod' => 1, + 'selectionDefaultLocation' => 2, + 'selectionContentTypes' => [], + 'rootDefaultLocation' => false, + ]; + } + + /** + * Get a valid $validatorConfiguration. + * + * @todo Implement correctly + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return [ + 'RelationListValueValidator' => [ + 'selectionLimit' => 0, + ], + ]; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @todo Implement correctly + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return ['selectionMethod' => 'a', 'selectionDefaultLocation' => true, 'unknownSetting' => false]; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @todo Implement correctly + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return ['noValidator' => true]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new RelationListValue([4, 49]); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'Users' . ' ' . 'Images'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + RelationListValue::class, + $field->value + ); + + $expectedData = [ + 'destinationContentIds' => [4, 49], + ]; + $this->assertPropertiesCorrectUnsorted( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + new RelationListValue([null]), + InvalidArgumentType::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + return new RelationListValue([49, 54, 4]); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + self::assertInstanceOf(RelationListValue::class, $field->value); + + $expectedData = [ + 'destinationContentIds' => [49, 54, 4], + ]; + $this->assertPropertiesCorrectUnsorted( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + RelationListValue::class, + $field->value + ); + + $expectedData = [ + 'destinationContentIds' => [4, 49], + ]; + $this->assertPropertiesCorrectUnsorted( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new RelationListValue([4, 49]), + [ + 'destinationContentIds' => [4, 49], + ], + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + ['destinationContentIds' => [4, 49]], + new RelationListValue([4, 49]), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new RelationListValue()], + [new RelationListValue([])], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + ]; + } + + protected function getValidSearchValueOne() + { + return [11]; + } + + protected function getValidSearchValueTwo() + { + return [12]; + } + + protected function getSearchTargetValueOne() + { + return 11; + } + + protected function getSearchTargetValueTwo() + { + return 12; + } + + protected function getValidMultivaluedSearchValuesOne() + { + return [11, 12]; + } + + protected function getValidMultivaluedSearchValuesTwo() + { + return [13, 14]; + } +} + +class_alias(RelationListIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\RelationListIntegrationTest'); diff --git a/eZ/Publish/API/Repository/Tests/FieldType/RelationSearchBaseIntegrationTestTrait.php b/tests/integration/Core/Repository/FieldType/RelationSearchBaseIntegrationTestTrait.php similarity index 85% rename from eZ/Publish/API/Repository/Tests/FieldType/RelationSearchBaseIntegrationTestTrait.php rename to tests/integration/Core/Repository/FieldType/RelationSearchBaseIntegrationTestTrait.php index a17e5ccdfe..161799afb9 100644 --- a/eZ/Publish/API/Repository/Tests/FieldType/RelationSearchBaseIntegrationTestTrait.php +++ b/tests/integration/Core/Repository/FieldType/RelationSearchBaseIntegrationTestTrait.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\FieldType; +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\Relation; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Core\Repository\Values\Content\Relation; /** * Base integration test for field types handling content relations. @@ -22,16 +22,16 @@ trait RelationSearchBaseIntegrationTestTrait { /** - * @param \eZ\Publish\API\Repository\Values\Content\Content $content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content * - * @return \eZ\Publish\API\Repository\Values\Content\Relation[] + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] */ abstract public function getCreateExpectedRelations(Content $content); /** - * @param \eZ\Publish\API\Repository\Values\Content\Content $content + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content * - * @return \eZ\Publish\API\Repository\Values\Content\Relation[] + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Relation[] */ abstract public function getUpdateExpectedRelations(Content $content); @@ -72,9 +72,9 @@ public function testUpdateContentRelationsProcessedCorrect() /** * Normalizes given $relations for easier comparison. * - * @param \eZ\Publish\Core\Repository\Values\Content\Relation[] $relations + * @param \Ibexa\Core\Repository\Values\Content\Relation[] $relations * - * @return \eZ\Publish\Core\Repository\Values\Content\Relation[] + * @return \Ibexa\Core\Repository\Values\Content\Relation[] */ protected function normalizeRelations(array $relations) { @@ -166,3 +166,5 @@ public function testSubtreeCopyContentCopiesFieldRelations() ); } } + +class_alias(RelationSearchBaseIntegrationTestTrait::class, 'eZ\Publish\API\Repository\Tests\FieldType\RelationSearchBaseIntegrationTestTrait'); diff --git a/eZ/Publish/API/Repository/Tests/FieldType/SearchBaseIntegrationTest.php b/tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php similarity index 92% rename from eZ/Publish/API/Repository/Tests/FieldType/SearchBaseIntegrationTest.php rename to tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php index 6d0112baa2..a9a647b06e 100644 --- a/eZ/Publish/API/Repository/Tests/FieldType/SearchBaseIntegrationTest.php +++ b/tests/integration/Core/Repository/FieldType/SearchBaseIntegrationTest.php @@ -4,23 +4,24 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\FieldType; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Tests\SetupFactory\Legacy; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Field; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalOperator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\CustomFieldInterface; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Field as FieldSortClause; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalNot; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalOperator; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\CustomFieldInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Field as FieldSortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; +use Ibexa\Core\Search\Common\FieldNameResolver; /** * Integration test for searching and sorting with Field criterion and Field sort clause. @@ -255,10 +256,10 @@ protected function checkCustomFieldsSupport() * Creates and returns content with given $fieldData. * * @param mixed $fieldData - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createTestSearchContent($fieldData, Repository $repository, $contentType) { @@ -288,10 +289,8 @@ protected function createTestSearchContent($fieldData, Repository $repository, $ * Creates test Content and Locations and returns the context for subsequent testing. * * Context consists of repository instance and created Content IDs. - * - * @return \eZ\Publish\API\Repository\Repository */ - public function testCreateTestContent() + public function testCreateTestContent(): array { $repository = $this->getRepository(); $fieldTypeService = $repository->getFieldTypeService(); @@ -1138,7 +1137,7 @@ public function testFindNotLikeTwo($valueOne, $valueTwo, $filter, $content, $mod * $fieldName refers to additional field (to the default field) defined in Indexable definition, * and is resolved using FieldNameResolver. * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion * @param string $fieldName */ protected function modifyFieldCriterion(Criterion $criterion, $fieldName) @@ -1147,8 +1146,8 @@ protected function modifyFieldCriterion(Criterion $criterion, $fieldName) /** @var \Symfony\Component\DependencyInjection\ContainerBuilder $container */ $container = $setupFactory->getServiceContainer()->getInnerContainer(); - /** @var \eZ\Publish\Core\Search\Common\FieldNameResolver $fieldNameResolver */ - $fieldNameResolver = $container->get('ezpublish.search.common.field_name_resolver'); + /** @var \Ibexa\Core\Search\Common\FieldNameResolver $fieldNameResolver */ + $fieldNameResolver = $container->get(FieldNameResolver::class); $resolvedFieldNames = $fieldNameResolver->getFieldNames( $criterion, 'data', @@ -1167,7 +1166,7 @@ protected function modifyFieldCriterion(Criterion $criterion, $fieldName) * $fieldName refers to additional field (to the default field) defined in Indexable definition, * and is resolved using FieldNameResolver. * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause $sortClause * @param string $fieldName */ protected function modifyFieldSortClause(SortClause $sortClause, $fieldName) @@ -1176,8 +1175,8 @@ protected function modifyFieldSortClause(SortClause $sortClause, $fieldName) /** @var \Symfony\Component\DependencyInjection\ContainerBuilder $container */ $container = $setupFactory->getServiceContainer()->getInnerContainer(); - /** @var \eZ\Publish\Core\Search\Common\FieldNameResolver $fieldNameResolver */ - $fieldNameResolver = $container->get('ezpublish.search.common.field_name_resolver'); + /** @var \Ibexa\Core\Search\Common\FieldNameResolver $fieldNameResolver */ + $fieldNameResolver = $container->get(FieldNameResolver::class); $resolvedFieldName = $fieldNameResolver->getSortFieldName( $sortClause, 'test-' . $this->getTypeName(), @@ -1194,7 +1193,7 @@ protected function modifyFieldSortClause(SortClause $sortClause, $fieldName) * * Implemented separately to utilize recursion. * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion[]|\eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $criteriaOrSortClauses + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion[]|\Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause[] $criteriaOrSortClauses * @param string $fieldName */ protected function doModifyField(array $criteriaOrSortClauses, $fieldName) @@ -1243,7 +1242,7 @@ public function sortProvider() */ public function testSort($ascending, $content, $modifyField, array $context) { - list($repository, $contentOneId, $contentTwoId) = $context; + [$repository, $contentOneId, $contentTwoId] = $context; $sortClause = new FieldSortClause( 'test-' . $this->getTypeName(), 'data', @@ -1316,11 +1315,11 @@ public function testFullTextFindTwo($valueOne, $valueTwo, $filter, $content, arr /** * Returns SearchResult of the tested Content for the given $criterion. * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion * @param bool $filter Denotes search by filtering if true, search by querying if false * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult */ protected function findContent(Repository $repository, Criterion $criterion, $filter) { @@ -1349,10 +1348,10 @@ protected function findContent(Repository $repository, Criterion $criterion, $fi /** * Returns SearchResult of the tested Content for the given $sortClause. * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause $sortClause * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult */ protected function sortContent(Repository $repository, SortClause $sortClause) { @@ -1373,11 +1372,11 @@ protected function sortContent(Repository $repository, SortClause $sortClause) /** * Returns SearchResult of the tested Locations for the given $criterion. * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion * @param bool $filter Denotes search by filtering if true, search by querying if false * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult */ protected function findLocations(Repository $repository, Criterion $criterion, $filter) { @@ -1406,10 +1405,10 @@ protected function findLocations(Repository $repository, Criterion $criterion, $ /** * Returns SearchResult of the tested Locations for the given $sortClause. * - * @param \eZ\Publish\API\Repository\Repository $repository - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause $sortClause + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause $sortClause * - * @return \eZ\Publish\API\Repository\Values\Content\Search\SearchResult + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult */ protected function sortLocations(Repository $repository, SortClause $sortClause) { @@ -1430,7 +1429,7 @@ protected function sortLocations(Repository $repository, SortClause $sortClause) /** * Returns a list of Content IDs from given $searchResult, with order preserved. * - * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $searchResult + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult $searchResult * * @return array */ @@ -1466,7 +1465,7 @@ protected function getResultContentIdList(SearchResult $searchResult) * Search result can be empty, contain both Content One and Content Two or only one of them. * * @param array $context - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion * @param bool $includesOne * @param bool $includesTwo * @param bool $filter @@ -1482,7 +1481,7 @@ protected function assertFindResult( $content, $modifyField ) { - list($repository, $contentOneId, $contentTwoId) = $context; + [$repository, $contentOneId, $contentTwoId] = $context; if ($modifyField !== null) { $this->checkCustomFieldsSupport(); @@ -1528,7 +1527,7 @@ protected function assertFindResult( /** * Asserts order of the given $searchResult, both Content One and Two are always expected. * - * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $searchResult + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult $searchResult * @param bool $ascending Denotes ascending order if true, descending order if false * @param string|int $contentOneId * @param string|int $contentTwoId @@ -1554,3 +1553,5 @@ protected function assertSortResult( $this->assertEquals($contentTwoId, $contentIdList[$indexTwo]); } } + +class_alias(SearchBaseIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\SearchBaseIntegrationTest'); diff --git a/eZ/Publish/API/Repository/Tests/FieldType/SearchMultivaluedBaseIntegrationTest.php b/tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php similarity index 95% rename from eZ/Publish/API/Repository/Tests/FieldType/SearchMultivaluedBaseIntegrationTest.php rename to tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php index a536559a22..7a5f0997e9 100644 --- a/eZ/Publish/API/Repository/Tests/FieldType/SearchMultivaluedBaseIntegrationTest.php +++ b/tests/integration/Core/Repository/FieldType/SearchMultivaluedBaseIntegrationTest.php @@ -4,11 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\FieldType; +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Field; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalNot; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; /** * Integration test for searching and sorting with Field criterion and Field sort clause. @@ -105,25 +106,29 @@ protected function getAdditionallyIndexedMultivaluedFieldData() Operator::BETWEEN => 'BETWEEN', ]; - protected function checkOperatorSupport($operator) + /** + * @throws \ErrorException + */ + protected function checkOperatorSupport($operator): void { - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\\Publish\\API\\Repository\\Tests\\SetupFactory\\Legacy') { - if (isset($this->legacyUnsupportedOperators[$operator])) { - $this->markTestSkipped( - 'Legacy Search Engine does not properly support multivalued fields ' . - "with '{$this->legacyUnsupportedOperators[$operator]}' operator" - ); - } + if ( + isset($this->legacyUnsupportedOperators[$operator]) + && $this->getSetupFactory() instanceof Legacy + ) { + $this->markTestSkipped( + 'Legacy Search Engine does not properly support multivalued fields ' . + "with '{$this->legacyUnsupportedOperators[$operator]}' operator" + ); } } /** - * Proxy method for creating test Content Type. + * Proxy method for creating test content type. * * Defaults to the testCreateContentType() in the base field type test, * override in the concrete test as needed. * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType */ protected function createTestContentType() { @@ -134,10 +139,8 @@ protected function createTestContentType() * Creates test Content and Locations and returns the context for subsequent testing. * * Context consists of repository instance and created Content IDs. - * - * @return \eZ\Publish\API\Repository\Repository */ - public function testCreateMultivaluedTestContent() + public function testCreateMultivaluedTestContent(): array { $repository = $this->getRepository(); $fieldTypeService = $repository->getFieldTypeService(); @@ -779,3 +782,5 @@ public function testFindMultivaluedNotBetweenTwoOne($valuesOne, $valuesTwo, $fil } } } + +class_alias(SearchMultivaluedBaseIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\SearchMultivaluedBaseIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php b/tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php new file mode 100644 index 0000000000..a8395a6f0b --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/SelectionIntegrationTest.php @@ -0,0 +1,393 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\Selection\Value as SelectionValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class SelectionIntegrationTest extends SearchMultivaluedBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezselection'; + } + + /** + * {@inheritdoc} + * + * If Selection is improved to be able to index + search for string also with LegacySearch, then adapt this too. + */ + protected function supportsLikeWildcard($value) + { + parent::supportsLikeWildcard($value); + + return false; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return [ + 'isMultiple' => [ + 'type' => 'bool', + 'default' => false, + ], + 'options' => [ + 'type' => 'hash', + 'default' => [], + ], + 'multilingualOptions' => [ + 'type' => 'hash', + 'default' => [], + ], + ]; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return [ + 'isMultiple' => true, + 'options' => [ + 0 => 'A first', + 1 => 'Bielefeld', + 2 => 'Sindelfingen', + 3 => 'Turtles', + 4 => 'Zombies', + ], + 'multilingualOptions' => [ + 'eng-GB' => [ + 0 => 'A first', + 1 => 'Bielefeld', + 2 => 'Sindelfingen', + 3 => 'Turtles', + 4 => 'Zombies', + ], + ], + ]; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + 'isMultiple' => [], + 'options' => new \stdClass(), + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return []; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return []; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'unknown' => ['value' => 23], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new SelectionValue([0, 2]); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'A first' . ' ' . 'Sindelfingen'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + SelectionValue::class, + $field->value + ); + + $expectedData = [ + 'selection' => [0, 2], + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + new \stdClass(), + InvalidArgumentType::class, + ], + [ + new SelectionValue([7]), + ContentFieldValidationException::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + return new SelectionValue([1]); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + SelectionValue::class, + $field->value + ); + + $expectedData = [ + 'selection' => [1], + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + SelectionValue::class, + $field->value + ); + + $expectedData = [ + 'selection' => [0, 2], + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new SelectionValue([0, 2]), + [0, 2], + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + [0, 2], + new SelectionValue([0, 2]), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new SelectionValue()], + [new SelectionValue([])], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + [ + new SelectionValue([0]), + ], + ]; + } + + protected function getValidSearchValueOne() + { + return [1]; + } + + protected function getValidSearchValueTwo() + { + return [2]; + } + + protected function getSearchTargetValueOne() + { + return 1; + } + + protected function getSearchTargetValueTwo() + { + return 2; + } + + protected function getAdditionallyIndexedFieldData() + { + return [ + [ + 'selected_option_value', + 'Bielefeld', + 'Sindelfingen', + ], + [ + 'sort_value', + '1', + '2', + ], + ]; + } + + protected function getValidMultivaluedSearchValuesOne() + { + return [0, 1]; + } + + protected function getValidMultivaluedSearchValuesTwo() + { + return [2, 3, 4]; + } + + protected function getAdditionallyIndexedMultivaluedFieldData() + { + return [ + [ + 'selected_option_value', + ['A first', 'Bielefeld'], + ['Sindelfingen', 'Turtles', 'Zombies'], + ], + ]; + } + + protected function getFullTextIndexedFieldData() + { + return [ + ['Bielefeld', 'Sindelfingen'], + ]; + } +} + +class_alias(SelectionIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\SelectionIntegrationTest'); diff --git a/eZ/Publish/API/Repository/Tests/FieldType/SelectionMultilingualIntegrationTest.php b/tests/integration/Core/Repository/FieldType/SelectionMultilingualIntegrationTest.php similarity index 93% rename from eZ/Publish/API/Repository/Tests/FieldType/SelectionMultilingualIntegrationTest.php rename to tests/integration/Core/Repository/FieldType/SelectionMultilingualIntegrationTest.php index aad2b1d3ea..c062d374f6 100644 --- a/eZ/Publish/API/Repository/Tests/FieldType/SelectionMultilingualIntegrationTest.php +++ b/tests/integration/Core/Repository/FieldType/SelectionMultilingualIntegrationTest.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\FieldType; +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; /** * Integration test for use field type. @@ -123,3 +123,5 @@ protected function getFullTextIndexedFieldData() ]; } } + +class_alias(SelectionMultilingualIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\SelectionMultilingualIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php b/tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php new file mode 100644 index 0000000000..5c26890faf --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/TextBlockIntegrationTest.php @@ -0,0 +1,318 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\TextBlock\Value as TextBlockValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class TextBlockIntegrationTest extends SearchBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'eztext'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return [ + 'textRows' => [ + 'type' => 'int', + 'default' => 10, + ], + ]; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return [ + 'textRows' => 0, + ]; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return []; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return []; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'unknown' => ['value' => 23], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new TextBlockValue('Example'); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'Example'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + TextBlockValue::class, + $field->value + ); + + $expectedData = [ + 'text' => 'Example', + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + new \stdClass(), + InvalidArgumentType::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + return new TextBlockValue('Example 2'); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + TextBlockValue::class, + $field->value + ); + + $expectedData = [ + 'text' => 'Example 2', + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + TextBlockValue::class, + $field->value + ); + + $expectedData = [ + 'text' => 'Example', + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new TextBlockValue('Simple value'), + 'Simple value', + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + 'Foobar', + new TextBlockValue('Foobar'), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new TextBlockValue()], + [new TextBlockValue(null)], + [new TextBlockValue('')], + [new TextBlockValue("\n\n\n")], + [new TextBlockValue("\r\r\r")], + [new TextBlockValue(' ')], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + [new TextBlockValue(0)], + [new TextBlockValue('0')], + ]; + } + + protected function getValidSearchValueOne() + { + return 'caution is the " path to mediocrity' . PHP_EOL . 'something completely different'; + } + + protected function getSearchTargetValueOne() + { + // ensure case-insensitivity + return strtoupper('caution is the " path to mediocrity'); + } + + protected function getValidSearchValueTwo() + { + return "truth suffers from ' too much analysis\n hello and goodbye"; + } + + protected function getSearchTargetValueTwo() + { + // ensure case-insensitivity + return strtoupper("truth suffers from ' too much analysis"); + } + + protected function getFullTextIndexedFieldData() + { + return [ + ['path', 'analysis'], + ]; + } +} + +class_alias(TextBlockIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\TextBlockIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php b/tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php new file mode 100644 index 0000000000..ee493d3a52 --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/TextLineIntegrationTest.php @@ -0,0 +1,336 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\TextLine\Value as TextLineValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class TextLineIntegrationTest extends SearchBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezstring'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return []; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return []; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return [ + 'StringLengthValidator' => [ + 'minStringLength' => [ + 'type' => 'int', + 'default' => null, + ], + 'maxStringLength' => [ + 'type' => 'int', + 'default' => null, + ], + ], + ]; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return [ + 'StringLengthValidator' => [ + 'minStringLength' => 1, + 'maxStringLength' => 42, + ], + ]; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'StringLengthValidator' => [ + 'minStringLength' => new \stdClass(), + ], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new TextLineValue('Example'); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'Example'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + TextLineValue::class, + $field->value + ); + + $expectedData = [ + 'text' => 'Example', + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + new \stdClass(), + InvalidArgumentType::class, + ], + [ + 42, + InvalidArgumentType::class, + ], + [ + new TextLineValue(str_repeat('.', 64)), + ContentFieldValidationException::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + return new TextLineValue('Example 2'); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + TextLineValue::class, + $field->value + ); + + $expectedData = [ + 'text' => 'Example 2', + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + TextLineValue::class, + $field->value + ); + + $expectedData = [ + 'text' => 'Example', + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new TextLineValue('Simple value'), + 'Simple value', + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + 'Foobar', + new TextLineValue('Foobar'), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new TextLineValue()], + [new TextLineValue(null)], + [new TextLineValue('')], + [new TextLineValue(' ')], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + [new TextLineValue(0)], + [new TextLineValue('0')], + ]; + } + + protected function getValidSearchValueOne() + { + return 'aaa'; + } + + protected function getSearchTargetValueOne() + { + // ensure case-insensitivity + return strtoupper($this->getValidSearchValueOne()); + } + + protected function getValidSearchValueTwo() + { + return 'bbb'; + } + + protected function getSearchTargetValueTwo() + { + // ensure case-insensitivity + return strtoupper($this->getValidSearchValueTwo()); + } + + protected function getFullTextIndexedFieldData() + { + return [ + ['aaa', 'bbb'], + ]; + } +} + +class_alias(TextLineIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\TextLineIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php b/tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php new file mode 100644 index 0000000000..87132350bf --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/TimeIntegrationTest.php @@ -0,0 +1,340 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use DateTime; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\FieldType\Time\Value as TimeValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class TimeIntegrationTest extends SearchBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'eztime'; + } + + /** + * {@inheritdoc} + */ + protected function supportsLikeWildcard($value) + { + parent::supportsLikeWildcard($value); + + return false; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return [ + 'useSeconds' => [ + 'type' => 'bool', + 'default' => false, + ], + 'defaultType' => [ + 'type' => 'choice', + 'default' => 0, + ], + ]; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return [ + 'useSeconds' => false, + 'defaultType' => 0, + ]; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return []; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return []; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'unknown' => ['value' => 42], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + // We may only create times from timestamps here, since storing will + // loose information about the timezone. + return new TimeValue(3661); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return '1:01:01 am'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + TimeValue::class, + $field->value + ); + + $expectedData = [ + 'time' => 3661, + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + 'Some unknown date format', InvalidArgumentException::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return \Ibexa\Core\FieldType\Time\Value + */ + public function getValidUpdateFieldData() + { + return TimeValue::fromTimestamp(12345678); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + TimeValue::class, + $field->value + ); + + $dateTime = new DateTime('@12345678'); + $expectedData = [ + 'time' => $dateTime->getTimestamp() - $dateTime->setTime(0, 0, 0)->getTimestamp(), + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Tests failing content update. + * + * @param mixed $failingValue + * @param string $expectedException + * + * @dataProvider provideInvalidUpdateFieldData + */ + public function testUpdateContentFails($failingValue, $expectedException) + { + return [ + [ + 'Some unknown date format', InvalidArgumentException::class, + ], + ]; + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + TimeValue::class, + $field->value + ); + + $expectedData = [ + 'time' => 3661, + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + $timestamp = 123456; + $dateTime = new DateTime("@{$timestamp}"); + + return [ + [ + TimeValue::fromTimestamp($timestamp), + $dateTime->getTimestamp() - $dateTime->setTime(0, 0, 0)->getTimestamp(), + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + 3661, + new TimeValue(3661), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new TimeValue()], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + ]; + } + + protected function getValidSearchValueOne() + { + return new TimeValue($this->getSearchTargetValueOne()); + } + + protected function getValidSearchValueTwo() + { + return new TimeValue($this->getSearchTargetValueTwo()); + } + + protected function getSearchTargetValueOne() + { + return 9600; + } + + protected function getSearchTargetValueTwo() + { + return 14400; + } +} + +class_alias(TimeIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\TimeIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php b/tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php new file mode 100644 index 0000000000..08616ef6a8 --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/UrlIntegrationTest.php @@ -0,0 +1,341 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\FieldType\Url\Value as UrlValue; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class UrlIntegrationTest extends SearchBaseIntegrationTest +{ + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezurl'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return []; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return []; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return []; + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return []; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'unknown' => ['value' => 23], + ]; + } + + /** + * Get initial field data for valid object creation. + * + * @return mixed + */ + public function getValidCreationFieldData() + { + return new UrlValue('http://example.com', 'Example'); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return 'Example'; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + UrlValue::class, + $field->value + ); + + $expectedData = [ + 'link' => 'http://example.com', + 'text' => 'Example', + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidCreationFieldData() + { + return [ + [ + new UrlValue(23), + InvalidArgumentType::class, + ], + [ + new UrlValue('http://example.com', 23), + InvalidArgumentType::class, + ], + ]; + } + + /** + * Get update field externals data. + * + * @return array + */ + public function getValidUpdateFieldData() + { + return new UrlValue('http://example.com/2', 'Example 2'); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + UrlValue::class, + $field->value + ); + + $expectedData = [ + 'link' => 'http://example.com/2', + 'text' => 'Example 2', + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + public function provideInvalidUpdateFieldData() + { + return $this->provideInvalidCreationFieldData(); + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + UrlValue::class, + $field->value + ); + + $expectedData = [ + 'link' => 'http://example.com', + 'text' => 'Example', + ]; + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new UrlValue('http://example.com'), + [ + 'link' => 'http://example.com', + 'text' => null, + ], + ], + [ + new UrlValue('http://example.com', 'Link text'), + [ + 'link' => 'http://example.com', + 'text' => 'Link text', + ], + ], + ]; + } + + /** + * Get expectations for the fromHash call on our field value. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + ['link' => 'http://example.com/sindelfingen'], + new UrlValue('http://example.com/sindelfingen'), + ], + [ + ['link' => 'http://example.com/sindelfingen', 'text' => 'Foo'], + new UrlValue('http://example.com/sindelfingen', 'Foo'), + ], + ]; + } + + public function providerForTestIsEmptyValue() + { + return [ + [new UrlValue()], + [new UrlValue(null)], + [new UrlValue('')], + [new UrlValue('', '')], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + [ + new UrlValue('http://example.com'), + ], + ]; + } + + protected function getValidSearchValueOne() + { + return new UrlValue('http://ample.com', 'Ample'); + } + + protected function getValidSearchValueTwo() + { + return new UrlValue('http://example.com', 'Example'); + } + + protected function getSearchTargetValueOne() + { + return 'http://ample.com'; + } + + protected function getSearchTargetValueTwo() + { + return 'http://example.com'; + } + + protected function getAdditionallyIndexedFieldData() + { + return [ + [ + 'value_text', + // ensure case-insensitivity + 'AMPLE', + 'EXAMPLE', + ], + ]; + } + + protected function getFullTextIndexedFieldData() + { + return [ + ['ample', 'example'], + ]; + } +} + +class_alias(UrlIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\UrlIntegrationTest'); diff --git a/tests/integration/Core/Repository/FieldType/UserIntegrationTest.php b/tests/integration/Core/Repository/FieldType/UserIntegrationTest.php new file mode 100644 index 0000000000..94578eac33 --- /dev/null +++ b/tests/integration/Core/Repository/FieldType/UserIntegrationTest.php @@ -0,0 +1,532 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\FieldType; + +use Doctrine\DBAL\Exception\NotNullConstraintViolationException; +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\FieldType\User\Type; +use Ibexa\Core\FieldType\User\Value as UserValue; +use Ibexa\Core\Repository\Values\User\User; +use Ibexa\Tests\Core\FieldType\DataProvider\UserValidatorConfigurationSchemaProvider; + +/** + * Integration test for use field type. + * + * @group integration + * @group field-type + */ +class UserIntegrationTest extends BaseIntegrationTest +{ + private const TEST_LOGIN = 'hans'; + + /** + * Get name of tested field type. + * + * @return string + */ + public function getTypeName() + { + return 'ezuser'; + } + + /** + * Get expected settings schema. + * + * @return array + */ + public function getSettingsSchema() + { + return [ + 'PasswordTTL' => [ + 'type' => 'int', + 'default' => null, + ], + 'PasswordTTLWarning' => [ + 'type' => 'int', + 'default' => null, + ], + 'RequireUniqueEmail' => [ + 'type' => 'bool', + 'default' => true, + ], + 'UsernamePattern' => [ + 'type' => 'string', + 'default' => '^[^@]+$', + ], + ]; + } + + /** + * Get a valid $fieldSettings value. + * + * @return mixed + */ + public function getValidFieldSettings() + { + return [ + 'PasswordTTL' => null, + 'PasswordTTLWarning' => null, + 'RequireUniqueEmail' => false, + 'UsernamePattern' => '.*', + ]; + } + + /** + * Get $fieldSettings value not accepted by the field type. + * + * @return mixed + */ + public function getInvalidFieldSettings() + { + return [ + 'somethingUnknown' => 0, + ]; + } + + /** + * Get expected validator schema. + * + * @return array + */ + public function getValidatorSchema() + { + return (new UserValidatorConfigurationSchemaProvider()) + ->getExpectedValidatorConfigurationSchema(); + } + + /** + * Get a valid $validatorConfiguration. + * + * @return mixed + */ + public function getValidValidatorConfiguration() + { + return [ + 'PasswordValueValidator' => [ + 'requireAtLeastOneUpperCaseCharacter' => true, + 'requireAtLeastOneLowerCaseCharacter' => true, + 'requireAtLeastOneNumericCharacter' => true, + 'requireAtLeastOneNonAlphanumericCharacter' => false, + 'requireNewPassword' => false, + 'requireNotCompromisedPassword' => false, + 'minLength' => 10, + ], + ]; + } + + /** + * Get $validatorConfiguration not accepted by the field type. + * + * @return mixed + */ + public function getInvalidValidatorConfiguration() + { + return [ + 'unknown' => ['value' => 23], + ]; + } + + /** + * Get initial field externals data. + * + * @return \Ibexa\Core\FieldType\User\Value + */ + public function getValidCreationFieldData(): UserValue + { + return new UserValue([ + 'login' => self::TEST_LOGIN, + 'email' => sprintf('%s@example.com', self::TEST_LOGIN), + 'enabled' => true, + 'plainPassword' => 'PassWord42', + ]); + } + + /** + * Get name generated by the given field type (via fieldType->getName()). + * + * @return string + */ + public function getFieldName() + { + return self::TEST_LOGIN; + } + + /** + * Asserts that the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()} + * was stored and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + UserValue::class, + $field->value + ); + + $expectedData = [ + 'hasStoredLogin' => true, + 'login' => self::TEST_LOGIN, + 'email' => 'hans@example.com', + 'passwordHashType' => User::PASSWORD_HASH_PHP_DEFAULT, + 'enabled' => true, + ]; + + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + + $this->assertNotNull($field->value->contentId); + } + + public function provideInvalidCreationFieldData() + { + return []; + } + + public function testCreateContentFails( + $failingValue = null, + ?string $expectedException = null + ): void { + $this->markTestSkipped('Values are ignored on creation.'); + } + + /** + * Get update field externals data. + * + * @return \Ibexa\Core\FieldType\User\Value + */ + public function getValidUpdateFieldData() + { + return new UserValue( + [ + 'hasStoredLogin' => true, + 'login' => 'changeLogin', + 'email' => 'changeEmail@ibexa.co', + 'passwordHash' => '*2', + 'passwordHashType' => User::DEFAULT_PASSWORD_HASH, + 'enabled' => false, + ] + ); + } + + /** + * Get externals updated field data values. + * + * This is a PHPUnit data provider + * + * @return array + */ + public function assertUpdatedFieldDataLoadedCorrect(Field $field) + { + $this->assertInstanceOf( + UserValue::class, + $field->value + ); + + $expectedData = [ + 'hasStoredLogin' => true, + 'login' => 'changeLogin', + 'email' => 'changeEmail@ibexa.co', + 'passwordHashType' => User::DEFAULT_PASSWORD_HASH, + 'enabled' => false, + ]; + + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + + $this->assertNotNull($field->value->contentId); + } + + public function provideInvalidUpdateFieldData() + { + return [ + [ + null, + NotNullConstraintViolationException::class, + ], + // @todo: Define more failure cases ... + ]; + } + + /** + * Asserts the the field data was loaded correctly. + * + * Asserts that the data provided by {@link getValidCreationFieldData()}; + * was copied and loaded correctly. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field $field + */ + public function assertCopiedFieldDataLoadedCorrectly(Field $field) + { + $this->assertInstanceOf( + UserValue::class, + $field->value + ); + + $expectedData = [ + 'hasStoredLogin' => false, + 'contentId' => null, + 'login' => null, + 'email' => null, + 'passwordHash' => null, + 'passwordHashType' => null, + 'enabled' => false, + 'maxLogin' => null, + ]; + + $this->assertPropertiesCorrect( + $expectedData, + $field->value + ); + } + + /** + * Get data to test to hash method. + * + * This is a PHPUnit data provider + * + * The returned records must have the the original value assigned to the + * first index and the expected hash result to the second. For example: + * + * <code> + * array( + * array( + * new MyValue( true ), + * array( 'myValue' => true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideToHashData() + { + return [ + [ + new UserValue(['login' => self::TEST_LOGIN]), + [ + 'login' => self::TEST_LOGIN, + 'hasStoredLogin' => null, + 'contentId' => null, + 'email' => null, + 'passwordHash' => null, + 'passwordHashType' => null, + 'enabled' => null, + 'maxLogin' => null, + 'plainPassword' => null, + 'passwordUpdatedAt' => null, + ], + ], + ]; + } + + /** + * Get hashes and their respective converted values. + * + * This is a PHPUnit data provider + * + * The returned records must have the the input hash assigned to the + * first index and the expected value result to the second. For example: + * + * <code> + * array( + * array( + * array( 'myValue' => true ), + * new MyValue( true ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideFromHashData() + { + return [ + [ + ['login' => self::TEST_LOGIN], + new UserValue(['login' => self::TEST_LOGIN]), + ], + ]; + } + + /** + * Overwrite normal content creation. + * + * @param mixed $fieldData + */ + protected function createContent($fieldData, $contentType = null) + { + if ($contentType === null) { + $contentType = $this->testCreateContentType(); + } + + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + // Instantiate a create struct with mandatory properties + $userCreate = $userService->newUserCreateStruct( + self::TEST_LOGIN, + 'hans@example.com', + 'PassWord42', + 'eng-US', + $contentType + ); + $userCreate->enabled = true; + + // Set some fields required by the user ContentType + $userCreate->setField('name', 'Example User'); + + // ID of the "Editors" user group in an Ibexa demo installation + $group = $userService->loadUserGroup(13); + + // Create a new user instance. + $user = $userService->createUser($userCreate, [$group]); + + // Create draft from user content object + $contentService = $repository->getContentService(); + + return $contentService->createContentDraft($user->content->contentInfo, $user->content->versionInfo); + } + + public function testCreateContentWithEmptyFieldValue() + { + $this->markTestSkipped('User field will never be created empty'); + } + + public function providerForTestIsEmptyValue() + { + return [ + [new UserValue()], + [new UserValue([])], + ]; + } + + public function providerForTestIsNotEmptyValue() + { + return [ + [ + $this->getValidCreationFieldData(), + ], + ]; + } + + public function testRemoveFieldDefinition() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $content = $this->testPublishContent(); + $countBeforeRemoval = count($content->getFields()); + + $contentType = $contentTypeService->loadContentType($content->contentInfo->contentTypeId); + $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); + + $userFieldDefinition = $this->getUserFieldDefinition($contentType); + + $contentTypeService->removeFieldDefinition($contentTypeDraft, $userFieldDefinition); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + $content = $contentService->loadContent($content->id); + + $this->assertCount($countBeforeRemoval - 1, $content->getFields()); + $this->assertNull($content->getFieldValue($userFieldDefinition->identifier)); + } + + public function testAddFieldDefinition() + { + // Field cannot be added to ContentType with existing content. + $this->expectException(BadStateException::class); + + return parent::testAddFieldDefinition(); + } + + /** + * @depends testCreateContent + */ + public function testCopyField($content) + { + // Users cannot be copied. + $this->expectException(ForbiddenException::class); + $this->expectExceptionMessage(sprintf('User "%s" already exists', self::TEST_LOGIN)); + + return parent::testCopyField($content); + } + + /** + * @depends testCopyField + */ + public function testCopiedFieldType($content) + { + $this->markTestSkipped('Users cannot be copied, content is not passed to test.'); + } + + /** + * @depends testCopiedFieldType + */ + public function testCopiedExternalData(Field $field) + { + $this->markTestSkipped('Users cannot be copied, field is not passed to test.'); + } + + /** + * @see https://issues.ibexa.co/browse/EZP-30966 + */ + public function testUpdateFieldDefinitionWithIncompleteSettingsSchema() + { + $contentTypeService = $this->getRepository()->getContentTypeService(); + $contentType = $this->testCreateContentType(); + $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); + + $userFieldDefinition = $this->getUserFieldDefinition($contentType); + $userFieldDefinitionUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); + $userFieldDefinitionUpdateStruct->fieldSettings = [ + Type::PASSWORD_TTL_WARNING_SETTING => null, + Type::REQUIRE_UNIQUE_EMAIL => false, + Type::USERNAME_PATTERN => '.*', + ]; + + $contentTypeService->updateFieldDefinition($contentTypeDraft, $userFieldDefinition, $userFieldDefinitionUpdateStruct); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + $contentType = $contentTypeService->loadContentType($contentType->id); + $userFieldDefinition = $this->getUserFieldDefinition($contentType); + + $this->assertNull($userFieldDefinition->fieldSettings[Type::PASSWORD_TTL_WARNING_SETTING]); + } + + /** + * Finds ezuser field definition in given $contentType or mark test as failed if it doens't exists. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition + */ + private function getUserFieldDefinition(ContentType $contentType): FieldDefinition + { + $fieldDefinition = $contentType->getFirstFieldDefinitionOfType('ezuser'); + + if ($fieldDefinition === null) { + $this->fail("'ezuser' field definition was not found"); + } + + return $fieldDefinition; + } +} + +class_alias(UserIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\FieldType\UserIntegrationTest'); diff --git a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/1234eeee1234-image.jpg b/tests/integration/Core/Repository/FieldType/_fixtures/1234eeee1234-image.jpg similarity index 100% rename from eZ/Publish/API/Repository/Tests/FieldType/_fixtures/1234eeee1234-image.jpg rename to tests/integration/Core/Repository/FieldType/_fixtures/1234eeee1234-image.jpg diff --git a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/2222eeee1111-image.png b/tests/integration/Core/Repository/FieldType/_fixtures/2222eeee1111-image.png similarity index 100% rename from eZ/Publish/API/Repository/Tests/FieldType/_fixtures/2222eeee1111-image.png rename to tests/integration/Core/Repository/FieldType/_fixtures/2222eeee1111-image.png diff --git a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/image.jpg b/tests/integration/Core/Repository/FieldType/_fixtures/image.jpg similarity index 100% rename from eZ/Publish/API/Repository/Tests/FieldType/_fixtures/image.jpg rename to tests/integration/Core/Repository/FieldType/_fixtures/image.jpg diff --git a/eZ/Publish/API/Repository/Tests/FieldType/_fixtures/image.png b/tests/integration/Core/Repository/FieldType/_fixtures/image.png similarity index 100% rename from eZ/Publish/API/Repository/Tests/FieldType/_fixtures/image.png rename to tests/integration/Core/Repository/FieldType/_fixtures/image.png diff --git a/eZ/Publish/API/Repository/Tests/FieldTypeServiceTest.php b/tests/integration/Core/Repository/FieldTypeServiceTest.php similarity index 80% rename from eZ/Publish/API/Repository/Tests/FieldTypeServiceTest.php rename to tests/integration/Core/Repository/FieldTypeServiceTest.php index 32903eea08..488114238d 100644 --- a/eZ/Publish/API/Repository/Tests/FieldTypeServiceTest.php +++ b/tests/integration/Core/Repository/FieldTypeServiceTest.php @@ -4,12 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\FieldType; /** * Test case for operations in the FieldTypeService using in memory storage. * - * @see eZ\Publish\API\Repository\FieldTypeService + * @covers \Ibexa\Contracts\Core\Repository\FieldTypeService * @group field-type */ class FieldTypeServiceTest extends BaseTest @@ -17,7 +19,7 @@ class FieldTypeServiceTest extends BaseTest /** * Test for the getFieldTypes() method. * - * @see \eZ\Publish\API\Repository\FieldTypeService::getFieldTypes() + * @covers \Ibexa\Contracts\Core\Repository\FieldTypeService::getFieldTypes() */ public function testGetFieldTypes() { @@ -35,7 +37,7 @@ public function testGetFieldTypes() foreach ($fieldTypes as $fieldType) { $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\FieldType', + FieldType::class, $fieldType ); } @@ -46,7 +48,7 @@ public function testGetFieldTypes() * * Expects FieldType "ezurl" to be available! * - * @see \eZ\Publish\API\Repository\FieldTypeService::getFieldType() + * @covers \Ibexa\Contracts\Core\Repository\FieldTypeService::getFieldType() */ public function testGetFieldType() { @@ -60,7 +62,7 @@ public function testGetFieldType() /* END: Use Case */ $this->assertInstanceof( - 'eZ\\Publish\\API\\Repository\\FieldType', + FieldType::class, $fieldType ); $this->assertEquals( @@ -72,7 +74,7 @@ public function testGetFieldType() /** * Test for the getFieldType() method. * - * @see \eZ\Publish\API\Repository\FieldTypeService::getFieldType() + * @covers \Ibexa\Contracts\Core\Repository\FieldTypeService::getFieldType() */ public function testGetFieldTypeThrowsNotFoundException() { @@ -91,7 +93,7 @@ public function testGetFieldTypeThrowsNotFoundException() /** * Test for the hasFieldType() method. * - * @see \eZ\Publish\API\Repository\FieldTypeService::hasFieldType() + * @covers \Ibexa\Contracts\Core\Repository\FieldTypeService::hasFieldType() */ public function testHasFieldTypeReturnsTrue() { @@ -110,7 +112,7 @@ public function testHasFieldTypeReturnsTrue() /** * Test for the hasFieldType() method. * - * @see \eZ\Publish\API\Repository\FieldTypeService::hasFieldType() + * @covers \Ibexa\Contracts\Core\Repository\FieldTypeService::hasFieldType() */ public function testHasFieldTypeReturnsFalse() { @@ -126,3 +128,5 @@ public function testHasFieldTypeReturnsFalse() $this->assertFalse($typeExists); } } + +class_alias(FieldTypeServiceTest::class, 'eZ\Publish\API\Repository\Tests\FieldTypeServiceTest'); diff --git a/eZ/Publish/API/Repository/Tests/Filtering/BaseRepositoryFilteringTestCase.php b/tests/integration/Core/Repository/Filtering/BaseRepositoryFilteringTestCase.php similarity index 85% rename from eZ/Publish/API/Repository/Tests/Filtering/BaseRepositoryFilteringTestCase.php rename to tests/integration/Core/Repository/Filtering/BaseRepositoryFilteringTestCase.php index be039381de..6118035a9a 100644 --- a/eZ/Publish/API/Repository/Tests/Filtering/BaseRepositoryFilteringTestCase.php +++ b/tests/integration/Core/Repository/Filtering/BaseRepositoryFilteringTestCase.php @@ -6,23 +6,24 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\Filtering; - -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; +namespace Ibexa\Tests\Integration\Core\Repository\Filtering; + use Ibexa\Contracts\Core\Repository\Collections\TotalCountAwareInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ParentLocationId; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use Ibexa\Contracts\Core\Repository\Values\Filter\FilteringCriterion; +use Ibexa\Contracts\Core\Repository\Values\Filter\FilteringSortClause; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Tests\Core\Repository\Filtering\TestContentProvider; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; use IteratorAggregate; /** - * @internal for internal use by eZ Platform Kernel test cases + * @internal for internal use by Ibexa Kernel test cases */ abstract class BaseRepositoryFilteringTestCase extends BaseTest { @@ -30,7 +31,7 @@ abstract class BaseRepositoryFilteringTestCase extends BaseTest private const PAGINATION_LIMIT = 3; private const PAGINATION_OFFSET = 2; - /** @var \eZ\Publish\API\Repository\Tests\Filtering\TestContentProvider */ + /** @var \Ibexa\Tests\Core\Repository\Filtering\TestContentProvider */ protected $contentProvider; abstract protected function find(Filter $filter, ?array $contextLanguages = null): iterable; @@ -47,12 +48,12 @@ abstract protected function getDefaultSortClause(): FilteringSortClause; /** * @dataProvider getFilterFactories * - * @covers \eZ\Publish\API\Repository\ContentService::find - * @covers \eZ\Publish\API\Repository\LocationService::find + * @covers \Ibexa\Contracts\Core\Repository\ContentService::find + * @covers \Ibexa\Contracts\Core\Repository\LocationService::find * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testFind(callable $filterFactory): void { @@ -78,9 +79,9 @@ protected function setUp(): void } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testFindDoesNotFindDrafts(): void { @@ -99,7 +100,7 @@ public function testFindDoesNotFindDrafts(): void } /** - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception */ public function testFindPaginated(): void { @@ -129,16 +130,16 @@ public function testFindPaginated(): void } /** - * @covers \eZ\Publish\API\Repository\ContentService::find + * @covers \Ibexa\Contracts\Core\Repository\ContentService::find * * @dataProvider getUserLimitationData * - * @param \eZ\Publish\API\Repository\Values\User\Limitation[] $limitations + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation[] $limitations * @param string[] $expectedContentRemoteIds * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testFindByUserWithLimitations( array $limitations, @@ -222,7 +223,7 @@ static function (Content $parentFolder) use ($filter): Filter { * Note: this is a quick attempt to cover all supported Filtering Criteria. In the future it * should be refactored to rely on shared data structure created at runtime. * - * @return \eZ\Publish\SPI\Repository\Values\Filter\FilteringCriterion[] + * @return \Ibexa\Contracts\Core\Repository\Values\Filter\FilteringCriterion[] * * @see getFilterFactories */ @@ -238,6 +239,8 @@ public function getCriteriaForInitialData(): iterable Criterion\Operator::BETWEEN, [1080220197, 1448889046] ); + yield 'IsContainer=true' => new Criterion\IsContainer(true); + yield 'IsContainer=false' => new Criterion\IsContainer(false); yield 'IsUserBased=true' => new Criterion\IsUserBased(true); yield 'IsUserBased=false' => new Criterion\IsUserBased(false); yield 'IsUserEnabled=true' => new Criterion\IsUserEnabled(); @@ -309,14 +312,14 @@ protected function assertTotalCount(FilteringCriterion $criterion, int $searchTo } /** - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function getUserLimitationData(): iterable { $repository = $this->getRepository(false); - // Content Type Limitations + // Content type Limitations $contentTypeService = $repository->getContentTypeService(); $articleType = $contentTypeService->loadContentTypeByIdentifier('article'); $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); @@ -420,3 +423,5 @@ protected function buildFilter(callable $filterFactory, Content $parentFolder): return $filterFactory($parentFolder); } } + +class_alias(BaseRepositoryFilteringTestCase::class, 'eZ\Publish\API\Repository\Tests\Filtering\BaseRepositoryFilteringTestCase'); diff --git a/eZ/Publish/API/Repository/Tests/Filtering/ContentFilteringTest.php b/tests/integration/Core/Repository/Filtering/ContentFilteringTest.php similarity index 83% rename from eZ/Publish/API/Repository/Tests/Filtering/ContentFilteringTest.php rename to tests/integration/Core/Repository/Filtering/ContentFilteringTest.php index 36d4b3a015..5a26a80163 100644 --- a/eZ/Publish/API/Repository/Tests/Filtering/ContentFilteringTest.php +++ b/tests/integration/Core/Repository/Filtering/ContentFilteringTest.php @@ -6,22 +6,22 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\Filtering; +namespace Ibexa\Tests\Integration\Core\Repository\Filtering; use function array_map; use function count; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentList; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; -use eZ\Publish\Core\FieldType\Keyword; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentList; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use Ibexa\Contracts\Core\Repository\Values\Filter\FilteringSortClause; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; +use Ibexa\Core\FieldType\Keyword; +use Ibexa\Tests\Core\Repository\Filtering\TestContentProvider; use IteratorAggregate; use function sprintf; @@ -36,9 +36,9 @@ final class ContentFilteringTest extends BaseRepositoryFilteringTestCase * Content can have multiple Locations, so we need to check if the list of results * doesn't contain duplicates. * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testFindWithLocationSortClauses(): void { @@ -82,7 +82,7 @@ public function testFindWithLocationSortClauses(): void } /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException * @throws \Exception */ protected function compareWithSearchResults(Filter $filter, IteratorAggregate $filteredContentList): void @@ -101,7 +101,7 @@ protected function compareWithSearchResults(Filter $filter, IteratorAggregate $f } /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ protected function findUsingContentSearch(Query $query): ContentList { @@ -112,7 +112,9 @@ protected function findUsingContentSearch(Query $query): ContentList return new ContentList( $searchResults->totalCount, array_map( - static function (SearchHit $searchHit) { + static function (SearchHit $searchHit): Content { + self::assertInstanceOf(Content::class, $searchHit->valueObject); + return $searchHit->valueObject; }, $searchResults->searchHits @@ -149,9 +151,9 @@ static function (Content $parentFolder): Filter { * * @return int parent Folder Location ID * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ private function createMultiplePagesOfContentItems(int $pageSize, int $noOfPages): int { @@ -167,9 +169,9 @@ private function createMultiplePagesOfContentItems(int $pageSize, int $noOfPages } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testPagination(): void { @@ -202,9 +204,9 @@ public function testPagination(): void } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testFindContentWithExternalStorageFields(): void { @@ -236,9 +238,9 @@ public function testFindContentWithExternalStorageFields(): void * * @param string[] $expectedContentRemoteIds * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testFindContentUsingLocationCriterion( callable $filterFactory, @@ -296,7 +298,7 @@ protected function assertFoundContentItemsByRemoteIds( ): void { self::assertCount(count($expectedContentRemoteIds), $list); foreach ($list as $content) { - /** @var \eZ\Publish\API\Repository\Values\Content\Content $content */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $content */ self::assertContainsEquals( $content->contentInfo->remoteId, $expectedContentRemoteIds, @@ -312,7 +314,7 @@ protected function assertFoundContentItemsByRemoteIds( /** * @dataProvider getListOfSupportedSortClauses * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException */ public function testFindWithSortClauses(string $sortClauseFQCN): void { @@ -325,18 +327,20 @@ public function testFindWithSortClauses(string $sortClauseFQCN): void * * Note: It should be expanded in the future to check validity of the sorting logic itself * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException */ private function performAndAssertSimpleSortClauseQuery(FilteringSortClause $sortClause): void { $filter = new Filter(new Criterion\ContentId(57), [$sortClause]); $contentList = $this->find($filter, []); self::assertCount(1, $contentList); - self::assertSame(57, $contentList->getIterator()[0]->id); + $contentItem = $contentList->getIterator()[0]; + self::assertNotNull($contentItem); + self::assertSame(57, $contentItem->id); } /** - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception */ public function testObjectStateIdCriterionOnMultipleObjectStates(): void { @@ -420,7 +424,7 @@ public function getListOfSupportedSortClauses(): iterable } /** - * @return \eZ\Publish\API\Repository\Values\Content\ContentList + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentList */ protected function find(Filter $filter, ?array $contextLanguages = null): iterable { @@ -444,3 +448,5 @@ private function buildSearchQueryFromFilter(Filter $filter): Query ); } } + +class_alias(ContentFilteringTest::class, 'eZ\Publish\API\Repository\Tests\Filtering\ContentFilteringTest'); diff --git a/eZ/Publish/API/Repository/Tests/Filtering/LocationFilteringTest.php b/tests/integration/Core/Repository/Filtering/LocationFilteringTest.php similarity index 77% rename from eZ/Publish/API/Repository/Tests/Filtering/LocationFilteringTest.php rename to tests/integration/Core/Repository/Filtering/LocationFilteringTest.php index 5281838757..f5d165f34d 100644 --- a/eZ/Publish/API/Repository/Tests/Filtering/LocationFilteringTest.php +++ b/tests/integration/Core/Repository/Filtering/LocationFilteringTest.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\Filtering; +namespace Ibexa\Tests\Integration\Core\Repository\Filtering; -use eZ\Publish\API\Repository\Values\Content\LocationList; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationList; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use Ibexa\Contracts\Core\Repository\Values\Filter\FilteringSortClause; use IteratorAggregate; /** @@ -23,7 +23,7 @@ final class LocationFilteringTest extends BaseRepositoryFilteringTestCase { /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ protected function compareWithSearchResults( Filter $filter, @@ -35,7 +35,7 @@ protected function compareWithSearchResults( } /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ private function findUsingLocationSearch(LocationQuery $query): LocationList { @@ -73,7 +73,7 @@ public function getCriteriaForInitialData(): iterable } /** - * @return \eZ\Publish\API\Repository\Values\Content\LocationList + * @return \Ibexa\Contracts\Core\Repository\Values\Content\LocationList */ protected function find(Filter $filter, ?array $contextLanguages = null): iterable { @@ -88,7 +88,7 @@ protected function assertFoundContentItemsByRemoteIds( array $expectedContentRemoteIds ): void { foreach ($list as $location) { - /** @var \eZ\Publish\API\Repository\Values\Content\Location $location */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $location */ $contentInfo = $location->getContentInfo(); self::assertContainsEquals( $contentInfo->remoteId, @@ -117,3 +117,5 @@ private function buildSearchQueryFromFilter(Filter $filter): LocationQuery ); } } + +class_alias(LocationFilteringTest::class, 'eZ\Publish\API\Repository\Tests\Filtering\LocationFilteringTest'); diff --git a/eZ/Publish/API/Repository/Tests/IdManager.php b/tests/integration/Core/Repository/IdManager.php similarity index 87% rename from eZ/Publish/API/Repository/Tests/IdManager.php rename to tests/integration/Core/Repository/IdManager.php index fe448df098..e0811347e3 100644 --- a/eZ/Publish/API/Repository/Tests/IdManager.php +++ b/tests/integration/Core/Repository/IdManager.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; /** * Base class for ID manager used in the tests suite. @@ -37,3 +37,5 @@ abstract public function generateId($type, $rawId); */ abstract public function parseId($type, $id); } + +class_alias(IdManager::class, 'eZ\Publish\API\Repository\Tests\IdManager'); diff --git a/eZ/Publish/API/Repository/Tests/LanguageServiceAuthorizationTest.php b/tests/integration/Core/Repository/LanguageServiceAuthorizationTest.php similarity index 80% rename from eZ/Publish/API/Repository/Tests/LanguageServiceAuthorizationTest.php rename to tests/integration/Core/Repository/LanguageServiceAuthorizationTest.php index 24d3c91e84..6994389d0b 100644 --- a/eZ/Publish/API/Repository/Tests/LanguageServiceAuthorizationTest.php +++ b/tests/integration/Core/Repository/LanguageServiceAuthorizationTest.php @@ -4,13 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; /** * Test case for operations in the LanguageService using in memory storage. * - * @see \eZ\Publish\API\Repository\LanguageService - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUser + * @covers \Ibexa\Contracts\Core\Repository\LanguageService + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUser * @group integration * @group authorization */ @@ -19,19 +21,19 @@ class LanguageServiceAuthorizationTest extends BaseTest /** * Test for the createLanguage() method. * - * @see \eZ\Publish\API\Repository\LanguageService::createLanguage() - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testCreateLanguage + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::createLanguage() + * @depends testCreateLanguage */ public function testCreateLanguageThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $languageService = $repository->getContentLanguageService(); @@ -52,19 +54,19 @@ public function testCreateLanguageThrowsUnauthorizedException() /** * Test for the updateLanguageName() method. * - * @see \eZ\Publish\API\Repository\LanguageService::updateLanguageName() - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testUpdateLanguageName + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::updateLanguageName() + * @depends testUpdateLanguageName */ public function testUpdateLanguageNameThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $languageService = $repository->getContentLanguageService(); @@ -89,19 +91,19 @@ public function testUpdateLanguageNameThrowsUnauthorizedException() /** * Test for the enableLanguage() method. * - * @see \eZ\Publish\API\Repository\LanguageService::enableLanguage() - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testEnableLanguage + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::enableLanguage() + * @depends testEnableLanguage */ public function testEnableLanguageThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $languageService = $repository->getContentLanguageService(); @@ -124,19 +126,19 @@ public function testEnableLanguageThrowsUnauthorizedException() /** * Test for the disableLanguage() method. * - * @see \eZ\Publish\API\Repository\LanguageService::disableLanguage() - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testDisableLanguage + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::disableLanguage() + * @depends testDisableLanguage */ public function testDisableLanguageThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $languageService = $repository->getContentLanguageService(); @@ -159,19 +161,19 @@ public function testDisableLanguageThrowsUnauthorizedException() /** * Test for the deleteLanguage() method. * - * @see \eZ\Publish\API\Repository\LanguageService::deleteLanguage() - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testDeleteLanguage + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::deleteLanguage() + * @depends testDeleteLanguage */ public function testDeleteLanguageThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $languageService = $repository->getContentLanguageService(); @@ -191,3 +193,5 @@ public function testDeleteLanguageThrowsUnauthorizedException() /* END: Use Case */ } } + +class_alias(LanguageServiceAuthorizationTest::class, 'eZ\Publish\API\Repository\Tests\LanguageServiceAuthorizationTest'); diff --git a/eZ/Publish/API/Repository/Tests/LanguageServiceMaximumSupportedLanguagesTest.php b/tests/integration/Core/Repository/LanguageServiceMaximumSupportedLanguagesTest.php similarity index 82% rename from eZ/Publish/API/Repository/Tests/LanguageServiceMaximumSupportedLanguagesTest.php rename to tests/integration/Core/Repository/LanguageServiceMaximumSupportedLanguagesTest.php index cdc760ba67..4fad195312 100644 --- a/eZ/Publish/API/Repository/Tests/LanguageServiceMaximumSupportedLanguagesTest.php +++ b/tests/integration/Core/Repository/LanguageServiceMaximumSupportedLanguagesTest.php @@ -4,20 +4,20 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\API\Repository\Tests\SetupFactory\Legacy as LegacySetupFactory; +use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy as LegacySetupFactory; /** * Test case for maximum number of languages supported in the LanguageService. * - * @see eZ\Publish\API\Repository\LanguageService + * @see \Ibexa\Contracts\Core\Repository\LanguageService * @group integration * @group language */ class LanguageServiceMaximumSupportedLanguagesTest extends BaseTest { - /** @var \eZ\Publish\API\Repository\LanguageService */ + /** @var \Ibexa\Contracts\Core\Repository\LanguageService */ private $languageService; /** @var array */ @@ -72,9 +72,9 @@ protected function tearDown(): void /** * Test for the number of maximum language that can be created. * - * @see \eZ\Publish\API\Repository\LanguageService::createLanguage() + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::createLanguage() * - * @depends eZ\Publish\API\Repository\Tests\LanguageServiceTest::testNewLanguageCreateStruct + * @depends Ibexa\Tests\Integration\Core\Repository\LanguageServiceTest::testNewLanguageCreateStruct */ public function testCreateMaximumLanguageLimit() { @@ -90,3 +90,5 @@ public function testCreateMaximumLanguageLimit() $this->languageService->createLanguage($languageCreate); } } + +class_alias(LanguageServiceMaximumSupportedLanguagesTest::class, 'eZ\Publish\API\Repository\Tests\LanguageServiceMaximumSupportedLanguagesTest'); diff --git a/tests/integration/Core/Repository/LanguageServiceTest.php b/tests/integration/Core/Repository/LanguageServiceTest.php new file mode 100644 index 0000000000..a0698f4682 --- /dev/null +++ b/tests/integration/Core/Repository/LanguageServiceTest.php @@ -0,0 +1,748 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use Exception; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\LanguageCreateStruct; + +/** + * Test case for operations in the LanguageService using in memory storage. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService + * @group integration + * @group language + */ +class LanguageServiceTest extends BaseTest +{ + /** + * Test for the newLanguageCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::newLanguageCreateStruct + */ + public function testNewLanguageCreateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + $languageCreate = $languageService->newLanguageCreateStruct(); + /* END: Use Case */ + + $this->assertInstanceOf( + LanguageCreateStruct::class, + $languageCreate + ); + + $this->assertPropertiesCorrect( + [ + 'languageCode' => null, + 'name' => null, + 'enabled' => true, + ], + $languageCreate + ); + } + + /** + * Test for the createLanguage() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Language + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::createLanguage + * @depends testNewLanguageCreateStruct + */ + public function testCreateLanguage() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + $languageCreate = $languageService->newLanguageCreateStruct(); + $languageCreate->enabled = true; + $languageCreate->name = 'English (New Zealand)'; + $languageCreate->languageCode = 'eng-NZ'; + + $language = $languageService->createLanguage($languageCreate); + /* END: Use Case */ + + $this->assertInstanceOf( + Language::class, + $language + ); + + return $language; + } + + /** + * Test for the createLanguage() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Language $language + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::createLanguage + * @depends testCreateLanguage + */ + public function testCreateLanguageSetsIdPropertyOnReturnedLanguage($language) + { + $this->assertNotNull($language->id); + } + + /** + * Test for the createLanguage() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Language $language + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::createLanguage + * @depends testCreateLanguage + */ + public function testCreateLanguageSetsExpectedProperties($language) + { + $this->assertEquals( + [ + true, + 'English (New Zealand)', + 'eng-NZ', + ], + [ + $language->enabled, + $language->name, + $language->languageCode, + ] + ); + } + + /** + * Test for the createLanguage() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::createLanguage + * @depends testCreateLanguage + */ + public function testCreateLanguageThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'languageCreateStruct\' is invalid: language with the "nor-NO" language code already exists'); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + $languageCreate = $languageService->newLanguageCreateStruct(); + $languageCreate->enabled = true; + $languageCreate->name = 'Norwegian'; + $languageCreate->languageCode = 'nor-NO'; + + $languageService->createLanguage($languageCreate); + + // This call should fail with an InvalidArgumentException, because + // the language code "nor-NO" already exists. + $languageService->createLanguage($languageCreate); + /* END: Use Case */ + } + + /** + * Test for the loadLanguageById() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::loadLanguageById + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::loadLanguageListById + * @depends testCreateLanguage + */ + public function testLoadLanguageById() + { + $repository = $this->getRepository(); + + $languageService = $repository->getContentLanguageService(); + + $languageCreate = $languageService->newLanguageCreateStruct(); + $languageCreate->enabled = false; + $languageCreate->name = 'English'; + $languageCreate->languageCode = 'eng-NZ'; + + $languageId = $languageService->createLanguage($languageCreate)->id; + + $language = $languageService->loadLanguageById($languageId); + + $this->assertInstanceOf( + Language::class, + $language + ); + + $languages = $languageService->loadLanguageListById([$languageId]); + + $this->assertIsIterable($languages); + $this->assertCount(1, $languages); + $this->assertInstanceOf(Language::class, $languages[$languageId]); + } + + /** + * Test for the loadLanguageById() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::loadLanguageById + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::loadLanguageListById + * @depends testLoadLanguageById + */ + public function testLoadLanguageByIdThrowsNotFoundException() + { + $repository = $this->getRepository(); + + $nonExistentLanguageId = $this->generateId('language', 2342); + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + $languages = $languageService->loadLanguageListById([$nonExistentLanguageId]); + + $this->assertIsIterable($languages); + $this->assertCount(0, $languages); + + $this->expectException(NotFoundException::class); + + $languageService->loadLanguageById($nonExistentLanguageId); + /* END: Use Case */ + } + + /** + * Test for the updateLanguageName() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::updateLanguageName + * @depends testLoadLanguageById + */ + public function testUpdateLanguageName() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + $languageCreate = $languageService->newLanguageCreateStruct(); + $languageCreate->enabled = false; + $languageCreate->name = 'English'; + $languageCreate->languageCode = 'eng-NZ'; + + $languageId = $languageService->createLanguage($languageCreate)->id; + + $language = $languageService->loadLanguageById($languageId); + + $updatedLanguage = $languageService->updateLanguageName( + $language, + 'New language name.' + ); + /* END: Use Case */ + + // Verify that the service returns an updated language instance. + $this->assertInstanceOf( + Language::class, + $updatedLanguage + ); + + // Verify that the service also persists the changes + $updatedLanguage = $languageService->loadLanguageById($languageId); + $this->assertPropertiesCorrect( + [ + 'id' => $language->id, + 'name' => 'New language name.', + 'languageCode' => $language->languageCode, + 'enabled' => $language->enabled, + ], + $updatedLanguage + ); + } + + /** + * Test service method for updating language name throwing InvalidArgumentException. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::updateLanguageName + */ + public function testUpdateLanguageNameThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'newName\' is invalid: \'\' is incorrect value'); + + $repository = $this->getRepository(); + $languageService = $repository->getContentLanguageService(); + + $language = $languageService->loadLanguage('eng-GB'); + $languageService->updateLanguageName($language, ''); + } + + /** + * Test for the enableLanguage() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::enableLanguage + * @depends testLoadLanguageById + */ + public function testEnableLanguage() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + $languageCreate = $languageService->newLanguageCreateStruct(); + $languageCreate->enabled = false; + $languageCreate->name = 'English'; + $languageCreate->languageCode = 'eng-NZ'; + + $language = $languageService->createLanguage($languageCreate); + + // Now lets enable the newly created language + $languageService->enableLanguage($language); + + $enabledLanguage = $languageService->loadLanguageById($language->id); + /* END: Use Case */ + + $this->assertTrue($enabledLanguage->enabled); + } + + /** + * Test for the disableLanguage() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::disableLanguage + * @depends testLoadLanguageById + */ + public function testDisableLanguage() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + $languageCreate = $languageService->newLanguageCreateStruct(); + $languageCreate->enabled = true; + $languageCreate->name = 'English'; + $languageCreate->languageCode = 'eng-NZ'; + + $language = $languageService->createLanguage($languageCreate); + + // Now lets disable the newly created language + $languageService->disableLanguage($language); + + $enabledLanguage = $languageService->loadLanguageById($language->id); + /* END: Use Case */ + + $this->assertFalse($enabledLanguage->enabled); + } + + /** + * Test for the loadLanguage() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::loadLanguage + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::loadLanguageListByCode + * @depends testCreateLanguage + */ + public function testLoadLanguage() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + $languageCreate = $languageService->newLanguageCreateStruct(); + $languageCreate->enabled = true; + $languageCreate->name = 'English'; + $languageCreate->languageCode = 'eng-NZ'; + + $languageId = $languageService->createLanguage($languageCreate)->id; + + // Now load the newly created language by it's language code + $language = $languageService->loadLanguage('eng-NZ'); + /* END: Use Case */ + + $this->assertPropertiesCorrect( + [ + 'id' => $languageId, + 'languageCode' => 'eng-NZ', + 'name' => 'English', + 'enabled' => true, + ], + $language + ); + + $languages = $languageService->loadLanguageListByCode(['eng-NZ']); + + $this->assertIsIterable($languages); + $this->assertCount(1, $languages); + + $this->assertPropertiesCorrect( + [ + 'id' => $languageId, + 'languageCode' => 'eng-NZ', + 'name' => 'English', + 'enabled' => true, + ], + $languages['eng-NZ'] + ); + } + + /** + * Test for the loadLanguage() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::loadLanguage + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::loadLanguageListByCode + * @depends testLoadLanguage + */ + public function testLoadLanguageThrowsNotFoundException() + { + $repository = $this->getRepository(); + + $languageService = $repository->getContentLanguageService(); + + $languages = $languageService->loadLanguageListByCode(['fre-FR']); + + $this->assertIsIterable($languages); + $this->assertCount(0, $languages); + + $this->expectException(NotFoundException::class); + + $languageService->loadLanguage('fre-FR'); + } + + /** + * Test service method for loading language throwing InvalidArgumentException. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::loadLanguage + */ + public function testLoadLanguageThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'languageCode\' is invalid: language code has an invalid value'); + + $repository = $this->getRepository(); + + $repository->getContentLanguageService()->loadLanguage(''); + } + + /** + * Test for the loadLanguages() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::loadLanguages + * @depends testCreateLanguage + * @depends testLoadLanguage + */ + public function testLoadLanguages() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + // Create some languages + $languageCreateEnglish = $languageService->newLanguageCreateStruct(); + $languageCreateEnglish->enabled = false; + $languageCreateEnglish->name = 'English'; + $languageCreateEnglish->languageCode = 'eng-NZ'; + + $languageCreateFrench = $languageService->newLanguageCreateStruct(); + $languageCreateFrench->enabled = false; + $languageCreateFrench->name = 'French'; + $languageCreateFrench->languageCode = 'fre-FR'; + + $languageService->createLanguage($languageCreateEnglish); + $languageService->createLanguage($languageCreateFrench); + + $languages = $languageService->loadLanguages(); + self::assertIsArray($languages); + foreach ($languages as $language) { + self::assertInstanceOf(Language::class, $language); + $singleLanguage = $languageService->loadLanguage($language->languageCode); + $this->assertStructPropertiesCorrect( + $singleLanguage, + $language, + ['id', 'languageCode', 'name', 'enabled'] + ); + } + /* END: Use Case */ + + // eng-US, eng-GB, ger-DE + 2 newly created + $this->assertCount(5, $languages); + } + + /** + * Test for the loadLanguages() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::loadLanguages + * @depends testCreateLanguage + */ + public function loadLanguagesReturnsAnEmptyArrayByDefault() + { + $repository = $this->getRepository(); + + $languageService = $repository->getContentLanguageService(); + + $this->assertSame([], $languageService->loadLanguages()); + } + + /** + * Test for the deleteLanguage() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::deleteLanguage + * @depends testLoadLanguages + */ + public function testDeleteLanguage() + { + $repository = $this->getRepository(); + $languageService = $repository->getContentLanguageService(); + + $beforeCount = count($languageService->loadLanguages()); + + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + $languageCreateEnglish = $languageService->newLanguageCreateStruct(); + $languageCreateEnglish->enabled = false; + $languageCreateEnglish->name = 'English'; + $languageCreateEnglish->languageCode = 'eng-NZ'; + + $language = $languageService->createLanguage($languageCreateEnglish); + + // Delete the newly created language + $languageService->deleteLanguage($language); + /* END: Use Case */ + + // +1 -1 + $this->assertEquals($beforeCount, count($languageService->loadLanguages())); + + $this->expectException(NotFoundException::class); + $this->expectExceptionMessage('Could not find \'Language\' with identifier \'eng-NZ\''); + + // ensure just created & deleted language doesn't exist + $languageService->loadLanguage($languageCreateEnglish->languageCode); + self::fail('Language is still returned after being deleted'); + } + + /** + * Test for the deleteLanguage() method. + * + * NOTE: This test has a dependency against several methods in the content + * service, but because there is no topological sort for test dependencies + * we cannot declare them here. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::deleteLanguage + * @depends testDeleteLanguage + */ + public function testDeleteLanguageThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'language\' is invalid: Cannot delete language: some content still references the language'); + + $repository = $this->getRepository(); + + $editorsGroupId = $this->generateId('group', 13); + /* BEGIN: Use Case */ + // $editorsGroupId is the ID of the "Editors" user group in an Ibexa + // Publish demo installation + + $languageService = $repository->getContentLanguageService(); + + $languageCreateEnglish = $languageService->newLanguageCreateStruct(); + $languageCreateEnglish->enabled = true; + $languageCreateEnglish->name = 'English'; + $languageCreateEnglish->languageCode = 'eng-NZ'; + + $language = $languageService->createLanguage($languageCreateEnglish); + + $contentService = $repository->getContentService(); + + // Get metadata update struct and set new language as main language. + $metadataUpdate = $contentService->newContentMetadataUpdateStruct(); + $metadataUpdate->mainLanguageCode = 'eng-NZ'; + + // Update content object + $contentService->updateContentMetadata( + $contentService->loadContentInfo($editorsGroupId), + $metadataUpdate + ); + + // This call will fail with an "InvalidArgumentException", because the + // new language is used by a content object. + $languageService->deleteLanguage($language); + /* END: Use Case */ + } + + /** + * Test for the getDefaultLanguageCode() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::getDefaultLanguageCode + */ + public function testGetDefaultLanguageCode() + { + $repository = $this->getRepository(); + $languageService = $repository->getContentLanguageService(); + + $this->assertRegExp( + '(^[a-z]{3}\-[A-Z]{2}$)', + $languageService->getDefaultLanguageCode() + ); + } + + /** + * Test for the createLanguage() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::createLanguage + * @depends testCreateLanguage + */ + public function testCreateLanguageInTransactionWithRollback() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Get create struct and set properties + $languageCreate = $languageService->newLanguageCreateStruct(); + $languageCreate->enabled = true; + $languageCreate->name = 'English (New Zealand)'; + $languageCreate->languageCode = 'eng-NZ'; + + // Create new language + $languageService->createLanguage($languageCreate); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes + $repository->rollback(); + + try { + // This call will fail with a "NotFoundException" + $languageService->loadLanguage('eng-NZ'); + } catch (NotFoundException $e) { + // Expected execution path + } + /* END: Use Case */ + + $this->assertTrue(isset($e), 'Can still load language after rollback'); + } + + /** + * Test for the createLanguage() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::createLanguage + * @depends testCreateLanguage + */ + public function testCreateLanguageInTransactionWithCommit() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Get create struct and set properties + $languageCreate = $languageService->newLanguageCreateStruct(); + $languageCreate->enabled = true; + $languageCreate->name = 'English (New Zealand)'; + $languageCreate->languageCode = 'eng-NZ'; + + // Create new language + $languageService->createLanguage($languageCreate); + + // Commit all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Load new language + $language = $languageService->loadLanguage('eng-NZ'); + /* END: Use Case */ + + $this->assertEquals('eng-NZ', $language->languageCode); + } + + /** + * Test for the updateLanguageName() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::updateLanguageName + * @depends testUpdateLanguageName + */ + public function testUpdateLanguageNameInTransactionWithRollback() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Load an existing language + $language = $languageService->loadLanguage('eng-US'); + + // Update the language name + $languageService->updateLanguageName($language, 'My English'); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes + $repository->rollback(); + + // Load updated version, name will still be "English (American)" + $updatedLanguage = $languageService->loadLanguage('eng-US'); + /* END: Use Case */ + + $this->assertEquals('English (American)', $updatedLanguage->name); + } + + /** + * Test for the updateLanguageName() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LanguageService::updateLanguageName + * @depends testUpdateLanguageName + */ + public function testUpdateLanguageNameInTransactionWithCommit() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $languageService = $repository->getContentLanguageService(); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Load an existing language + $language = $languageService->loadLanguage('eng-US'); + + // Update the language name + $languageService->updateLanguageName($language, 'My English'); + + // Commit all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Load updated version, name will be "My English" + $updatedLanguage = $languageService->loadLanguage('eng-US'); + /* END: Use Case */ + + $this->assertEquals('My English', $updatedLanguage->name); + } +} + +class_alias(LanguageServiceTest::class, 'eZ\Publish\API\Repository\Tests\LanguageServiceTest'); diff --git a/tests/integration/Core/Repository/Limitation/PermissionResolver/BaseLimitationIntegrationTest.php b/tests/integration/Core/Repository/Limitation/PermissionResolver/BaseLimitationIntegrationTest.php new file mode 100644 index 0000000000..2b88fa6d15 --- /dev/null +++ b/tests/integration/Core/Repository/Limitation/PermissionResolver/BaseLimitationIntegrationTest.php @@ -0,0 +1,104 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\Limitation\PermissionResolver; + +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; + +/** + * Base class for all Limitation integration tests. + */ +abstract class BaseLimitationIntegrationTest extends BaseTest +{ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ + protected $permissionResolver; + + protected function setUp(): void + { + $repository = $this->getRepository(false); + $this->permissionResolver = $repository->getPermissionResolver(); + } + + /** + * Map Limitations list to readable string for debugging purposes. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation[] $limitations + * + * @return string + */ + protected function getLimitationsListAsString(array $limitations): string + { + $str = ''; + foreach ($limitations as $limitation) { + $str .= sprintf( + '%s[%s]', + get_class($limitation), + implode(', ', $limitation->limitationValues) + ); + } + + return $str; + } + + /** + * Create Editor user with the given Policy and Limitations and set it as current user. + * + * @param string $module + * @param string $function + * @param array $limitations + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + protected function loginAsEditorUserWithLimitations(string $module, string $function, array $limitations = []): void + { + $user = $this->createUserWithPolicies( + uniqid('editor'), + [ + ['module' => $module, 'function' => $function, 'limitations' => $limitations], + ] + ); + + $this->permissionResolver->setCurrentUserReference($user); + } + + /** + * @param bool $expectedResult + * @param string $module + * @param string $function + * @param array $limitations + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object + * @param array $targets + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + protected function assertCanUser( + bool $expectedResult, + string $module, + string $function, + array $limitations, + ValueObject $object, + array $targets = [] + ): void { + self::assertEquals( + $expectedResult, + $this->permissionResolver->canUser($module, $function, $object, $targets), + sprintf( + 'Failure for %s/%s with Limitations: %s', + $module, + $function, + $this->getLimitationsListAsString($limitations) + ) + ); + } +} + +class_alias(BaseLimitationIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\Limitation\PermissionResolver\BaseLimitationIntegrationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Limitation/PermissionResolver/ContentLimitationsMixIntegrationTest.php b/tests/integration/Core/Repository/Limitation/PermissionResolver/ContentLimitationsMixIntegrationTest.php similarity index 80% rename from eZ/Publish/API/Repository/Tests/Limitation/PermissionResolver/ContentLimitationsMixIntegrationTest.php rename to tests/integration/Core/Repository/Limitation/PermissionResolver/ContentLimitationsMixIntegrationTest.php index 030e8d5155..33606bbfc7 100644 --- a/eZ/Publish/API/Repository/Tests/Limitation/PermissionResolver/ContentLimitationsMixIntegrationTest.php +++ b/tests/integration/Core/Repository/Limitation/PermissionResolver/ContentLimitationsMixIntegrationTest.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\Limitation\PermissionResolver; +namespace Ibexa\Tests\Integration\Core\Repository\Limitation\PermissionResolver; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; /** * Test mix of chosen core Content Limitations. @@ -72,11 +72,11 @@ public function providerForCanUser(): array * @param array $limitations * @param bool $expectedResult * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testCanUser( string $module, @@ -105,7 +105,7 @@ public function testCanUser( /** * Get a list of Limitations common to all test cases. * - * @return \eZ\Publish\API\Repository\Values\User\Limitation[] + * @return \Ibexa\Contracts\Core\Repository\Values\User\Limitation[] */ private function getCommonLimitations(): array { @@ -116,3 +116,5 @@ private function getCommonLimitations(): array ]; } } + +class_alias(ContentLimitationsMixIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\Limitation\PermissionResolver\ContentLimitationsMixIntegrationTest'); diff --git a/tests/integration/Core/Repository/Limitation/PermissionResolver/LocationLimitationIntegrationTest.php b/tests/integration/Core/Repository/Limitation/PermissionResolver/LocationLimitationIntegrationTest.php new file mode 100644 index 0000000000..f1eceeb6fe --- /dev/null +++ b/tests/integration/Core/Repository/Limitation/PermissionResolver/LocationLimitationIntegrationTest.php @@ -0,0 +1,99 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\Limitation\PermissionResolver; + +use Ibexa\Contracts\Core\Limitation\Target\Version; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\LocationLimitation; + +class LocationLimitationIntegrationTest extends BaseLimitationIntegrationTest +{ + private const LOCATION_ID = 2; + + public function providerForCanUserEditOrPublishContent(): array + { + $limitationRoot = new LocationLimitation(); + $limitationRoot->limitationValues = [self::LOCATION_ID]; + + return [ + [[$limitationRoot], true], + ]; + } + + /** + * @dataProvider providerForCanUserEditOrPublishContent + * + * @param array $limitations + * @param bool $expectedResult + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testCanUserEditContent(array $limitations, bool $expectedResult): void + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation(2); + + $this->loginAsEditorUserWithLimitations('content', 'edit', $limitations); + + $this->assertCanUser( + $expectedResult, + 'content', + 'edit', + $limitations, + $location->contentInfo, + [$location] + ); + + $this->assertCanUser( + $expectedResult, + 'content', + 'edit', + $limitations, + $location->contentInfo, + [$location, new Version(['allLanguageCodesList' => 'eng-GB'])] + ); + } + + /** + * @dataProvider providerForCanUserEditOrPublishContent + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testCanUserReadTrashedContent(array $limitations, bool $expectedResult): void + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation(2); + + $this->loginAsEditorUserWithLimitations('content', 'read', $limitations); + + $trashItem = $repository->sudo( + static function (Repository $repository) use ($location) { + return $repository->getTrashService()->trash($location); + } + ); + + $this->assertCanUser( + $expectedResult, + 'content', + 'read', + $limitations, + $trashItem->contentInfo + ); + } +} + +class_alias(LocationLimitationIntegrationTest::class, 'eZ\Publish\API\Repository\Tests\Limitation\PermissionResolver\LocationLimitationIntegrationTest'); diff --git a/eZ/Publish/API/Repository/Tests/LocationServiceAuthorizationTest.php b/tests/integration/Core/Repository/LocationServiceAuthorizationTest.php similarity index 82% rename from eZ/Publish/API/Repository/Tests/LocationServiceAuthorizationTest.php rename to tests/integration/Core/Repository/LocationServiceAuthorizationTest.php index 479c692080..f6d49e9257 100644 --- a/eZ/Publish/API/Repository/Tests/LocationServiceAuthorizationTest.php +++ b/tests/integration/Core/Repository/LocationServiceAuthorizationTest.php @@ -4,19 +4,19 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\User\Limitation\LanguageLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\OwnerLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\LanguageLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\OwnerLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; /** * Test case for operations in the LocationService using in memory storage. * - * @see eZ\Publish\API\Repository\LocationService - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\LocationService + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser * @group integration * @group authorization */ @@ -25,12 +25,12 @@ class LocationServiceAuthorizationTest extends BaseTest /** * Test for the createLocation() method. * - * @see \eZ\Publish\API\Repository\LocationService::createLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testCreateLocation + * @covers \Ibexa\Contracts\Core\Repository\LocationService::createLocation() + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testCreateLocation */ public function testCreateLocationThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -67,12 +67,12 @@ public function testCreateLocationThrowsUnauthorizedException() /** * Test for the createLocation() method. Tests a case when user doesn't have content/manage_locations policy for the new location ID. * - * @see \eZ\Publish\API\Repository\LocationService::createLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testCreateLocation + * @covers \Ibexa\Contracts\Core\Repository\LocationService::createLocation() + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testCreateLocation */ public function testCreateLocationThrowsUnauthorizedExceptionDueToLackOfContentManageLocationsPolicy() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -127,12 +127,12 @@ public function testCreateLocationThrowsUnauthorizedExceptionDueToLackOfContentM /** * Test for the loadLocation() method. * - * @see \eZ\Publish\API\Repository\LocationService::loadLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocation + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocation() + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testLoadLocation */ public function testLoadLocationThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -155,7 +155,7 @@ public function testLoadLocationThrowsUnauthorizedException() /** * Test for the loadLocationList() method. * - * @covers \eZ\Publish\API\Repository\LocationService::loadLocationList + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationList */ public function testLoadLocationListFiltersUnauthorizedLocations(): void { @@ -176,18 +176,18 @@ public function testLoadLocationListFiltersUnauthorizedLocations(): void /** * Test for the loadLocationByRemoteId() method. * - * @see \eZ\Publish\API\Repository\LocationService::loadLocationByRemoteId() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocationByRemoteId + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationByRemoteId() + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testLoadLocationByRemoteId */ public function testLoadLocationByRemoteIdThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); /* BEGIN: Use Case */ - // remoteId of the "Editors" location in an eZ Publish demo installation + // remoteId of the "Editors" location in an Ibexa demo installation $editorsRemoteId = 'f7dda2854fc68f7c8455d9cb14bd04a9'; $locationService = $repository->getLocationService(); @@ -205,8 +205,8 @@ public function testLoadLocationByRemoteIdThrowsUnauthorizedException() /** * Test for the loadLocations() method. * - * @see \eZ\Publish\API\Repository\LocationService::loadLocations() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testLoadLocations + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocations() + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testLoadLocations */ public function testLoadLocationsNoAccess() { @@ -234,12 +234,12 @@ public function testLoadLocationsNoAccess() /** * Test for the updateLocation() method. * - * @see \eZ\Publish\API\Repository\LocationService::updateLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testUpdateLocation + * @covers \Ibexa\Contracts\Core\Repository\LocationService::updateLocation() + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testUpdateLocation */ public function testUpdateLocationThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -273,12 +273,12 @@ public function testUpdateLocationThrowsUnauthorizedException() /** * Test for the swapLocation() method. * - * @see \eZ\Publish\API\Repository\LocationService::swapLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testSwapLocation + * @covers \Ibexa\Contracts\Core\Repository\LocationService::swapLocation() + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testSwapLocation */ public function testSwapLocationThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -287,9 +287,9 @@ public function testSwapLocationThrowsUnauthorizedException() $demoDesignLocationId = $this->generateId('location', 56); /* BEGIN: Use Case */ // $mediaLocationId is the ID of the "Media" Location in - // an eZ Publish demo installation + // an Ibexa demo installation - // $demoDesignLocationId is the ID of the "Demo Design" Location in an eZ + // $demoDesignLocationId is the ID of the "Demo Design" Location in an Ibexa // Publish demo installation // Load the location service @@ -314,12 +314,12 @@ public function testSwapLocationThrowsUnauthorizedException() /** * Test for the hideLocation() method. * - * @see \eZ\Publish\API\Repository\LocationService::hideLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testHideLocation + * @covers \Ibexa\Contracts\Core\Repository\LocationService::hideLocation() + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testHideLocation */ public function testHideLocationThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -344,12 +344,12 @@ public function testHideLocationThrowsUnauthorizedException() /** * Test for the unhideLocation() method. * - * @see \eZ\Publish\API\Repository\LocationService::unhideLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testUnhideLocation + * @covers \Ibexa\Contracts\Core\Repository\LocationService::unhideLocation() + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testUnhideLocation */ public function testUnhideLocationThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -377,12 +377,12 @@ public function testUnhideLocationThrowsUnauthorizedException() /** * Test for the deleteLocation() method. * - * @see \eZ\Publish\API\Repository\LocationService::deleteLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testDeleteLocation + * @covers \Ibexa\Contracts\Core\Repository\LocationService::deleteLocation() + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testDeleteLocation */ public function testDeleteLocationThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -405,7 +405,7 @@ public function testDeleteLocationThrowsUnauthorizedException() } /** - * @covers \eZ\Publish\API\Repository\LocationService::deleteLocation() + * @covers \Ibexa\Contracts\Core\Repository\LocationService::deleteLocation() */ public function testDeleteLocationThrowsUnauthorizedExceptionWithLanguageLimitation(): void { @@ -440,12 +440,12 @@ public function testDeleteLocationThrowsUnauthorizedExceptionWithLanguageLimitat /** * Test for the deleteLocation() method. * - * @see \eZ\Publish\API\Repository\LocationService::deleteLocation() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testDeleteLocation + * @covers \Ibexa\Contracts\Core\Repository\LocationService::deleteLocation() + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testDeleteLocation */ public function testDeleteLocationWithSubtreeThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $this->expectExceptionMessage('The User does not have the \'remove\' \'content\' permission'); $repository = $this->getRepository(); @@ -463,7 +463,7 @@ public function testDeleteLocationWithSubtreeThrowsUnauthorizedException() $roleDraft = $roleService->createRoleDraft($role); $removePolicy = null; - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy */ foreach ($roleDraft->getPolicies() as $policy) { if ('content' != $policy->module || 'remove' != $policy->function) { continue; @@ -538,12 +538,12 @@ public function testDeleteLocationWithSubtreeThrowsUnauthorizedException() /** * Test for the copySubtree() method. * - * @see \eZ\Publish\API\Repository\LocationService::copySubtree() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testCopySubtree + * @covers \Ibexa\Contracts\Core\Repository\LocationService::copySubtree() + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testCopySubtree */ public function testCopySubtreeThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -554,9 +554,9 @@ public function testCopySubtreeThrowsUnauthorizedException() $user = $this->createMediaUserVersion1(); // $mediaLocationId is the ID of the "Media" Location in - // an eZ Publish demo installation + // an Ibexa demo installation - // $demoDesignLocationId is the ID of the "Demo Design" Location in an eZ + // $demoDesignLocationId is the ID of the "Demo Design" Location in an Ibexa // Publish demo installation // Load the location service @@ -582,12 +582,12 @@ public function testCopySubtreeThrowsUnauthorizedException() /** * Test for the moveSubtree() method. * - * @see \eZ\Publish\API\Repository\LocationService::moveSubtree() - * @depends eZ\Publish\API\Repository\Tests\LocationServiceTest::testMoveSubtree + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree() + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testMoveSubtree */ public function testMoveSubtreeThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -598,9 +598,9 @@ public function testMoveSubtreeThrowsUnauthorizedException() $user = $this->createMediaUserVersion1(); // $mediaLocationId is the ID of the "Media" page location in - // an eZ Publish demo installation + // an Ibexa demo installation - // $demoDesignLocationId is the ID of the "Demo Design" page location in an eZ + // $demoDesignLocationId is the ID of the "Demo Design" page location in an Ibexa // Publish demo installation // Load the location service @@ -623,3 +623,5 @@ public function testMoveSubtreeThrowsUnauthorizedException() /* END: Use Case */ } } + +class_alias(LocationServiceAuthorizationTest::class, 'eZ\Publish\API\Repository\Tests\LocationServiceAuthorizationTest'); diff --git a/tests/integration/Core/Repository/LocationServiceTest.php b/tests/integration/Core/Repository/LocationServiceTest.php new file mode 100644 index 0000000000..350347bda4 --- /dev/null +++ b/tests/integration/Core/Repository/LocationServiceTest.php @@ -0,0 +1,3705 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use Exception; +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\URLAliasService as URLAliasServiceInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationList; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; +use Ibexa\Core\Repository\Values\Content\ContentUpdateStruct; + +/** + * Test case for operations in the LocationService using in memory storage. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService + * @group location + */ +class LocationServiceTest extends BaseTest +{ + /** + * Test for the newLocationCreateStruct() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::newLocationCreateStruct() + */ + public function testNewLocationCreateStruct() + { + $repository = $this->getRepository(); + + $parentLocationId = $this->generateId('location', 1); + /* BEGIN: Use Case */ + // $parentLocationId is the ID of an existing location + $locationService = $repository->getLocationService(); + + $locationCreate = $locationService->newLocationCreateStruct( + $parentLocationId + ); + /* END: Use Case */ + + $this->assertInstanceOf( + LocationCreateStruct::class, + $locationCreate + ); + + return $locationCreate; + } + + /** + * Test for the newLocationCreateStruct() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct $locationCreate + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::newLocationCreateStruct() + * @depends testNewLocationCreateStruct + */ + public function testNewLocationCreateStructValues(LocationCreateStruct $locationCreate) + { + $this->assertPropertiesCorrect( + [ + 'priority' => 0, + 'hidden' => false, + // remoteId should be initialized with a default value + //'remoteId' => null, + 'sortField' => null, + 'sortOrder' => null, + 'parentLocationId' => $this->generateId('location', 1), + ], + $locationCreate + ); + } + + /** + * Test for the createLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::createLocation() + * @depends testNewLocationCreateStruct + */ + public function testCreateLocation() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', 41); + $parentLocationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $contentId is the ID of an existing content object + // $parentLocationId is the ID of an existing location + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + + // ContentInfo for "How to use Ibexa" + $contentInfo = $contentService->loadContentInfo($contentId); + + $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); + $locationCreate->priority = 23; + $locationCreate->hidden = true; + $locationCreate->remoteId = 'sindelfingen'; + $locationCreate->sortField = Location::SORT_FIELD_NODE_ID; + $locationCreate->sortOrder = Location::SORT_ORDER_DESC; + + $location = $locationService->createLocation( + $contentInfo, + $locationCreate + ); + /* END: Use Case */ + + $this->assertInstanceOf( + Location::class, + $location + ); + + return [ + 'locationCreate' => $locationCreate, + 'createdLocation' => $location, + 'contentInfo' => $contentInfo, + 'parentLocation' => $locationService->loadLocation($this->generateId('location', 5)), + ]; + } + + /** + * Test for the createLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::createLocation + * @depends testCreateLocation + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testHideContent + */ + public function testCreateLocationChecksContentVisibility(): void + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', 41); + $parentLocationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $contentId is the ID of an existing content object + // $parentLocationId is the ID of an existing location + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + + // ContentInfo for "How to use Ibexa" + $contentInfo = $contentService->loadContentInfo($contentId); + $contentService->hideContent($contentInfo); + + $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); + $locationCreate->priority = 23; + $locationCreate->hidden = false; + $locationCreate->remoteId = 'sindelfingen'; + $locationCreate->sortField = Location::SORT_FIELD_NODE_ID; + $locationCreate->sortOrder = Location::SORT_ORDER_DESC; + + $location = $locationService->createLocation( + $contentInfo, + $locationCreate + ); + /* END: Use Case */ + + self::assertInstanceOf(Location::class, $location); + + self::assertTrue($location->invisible); + } + + /** + * Test for the createLocation() method with utilizing default ContentType sorting options. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::createLocation + */ + public function testCreateLocationWithContentTypeSortingOptions(): void + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', 41); + $parentLocationId = $this->generateId('location', 5); + // $contentId is the ID of an existing content object + // $parentLocationId is the ID of an existing location + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + + // ContentInfo for "How to use Ibexa" + $contentInfo = $contentService->loadContentInfo($contentId); + + // ContentType loading + $contentType = $contentTypeService->loadContentType($contentInfo->contentTypeId); + + $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); + $locationCreate->priority = 23; + $locationCreate->hidden = true; + $locationCreate->remoteId = 'sindelfingen'; + + $location = $locationService->createLocation( + $contentInfo, + $locationCreate + ); + + $this->assertEquals($contentType->defaultSortField, $location->sortField); + $this->assertEquals($contentType->defaultSortOrder, $location->sortOrder); + } + + /** + * Test for the createLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::createLocation() + * @depends testCreateLocation + */ + public function testCreateLocationStructValues(array $data) + { + $locationCreate = $data['locationCreate']; + $createdLocation = $data['createdLocation']; + $contentInfo = $data['contentInfo']; + + $this->assertPropertiesCorrect( + [ + 'priority' => $locationCreate->priority, + 'hidden' => $locationCreate->hidden, + 'invisible' => $locationCreate->hidden, + 'remoteId' => $locationCreate->remoteId, + 'contentInfo' => $contentInfo, + 'parentLocationId' => $locationCreate->parentLocationId, + 'pathString' => '/1/5/' . $this->parseId('location', $createdLocation->id) . '/', + 'depth' => 2, + 'sortField' => $locationCreate->sortField, + 'sortOrder' => $locationCreate->sortOrder, + ], + $createdLocation + ); + + $this->assertNotNull($createdLocation->id); + } + + /** + * Test for the createLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::createLocation() + * @depends testNewLocationCreateStruct + */ + public function testCreateLocationThrowsInvalidArgumentExceptionContentAlreadyBelowParent() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', 11); + $parentLocationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $contentId is the ID of an existing content object + // $parentLocationId is the ID of an existing location which already + // has the content assigned to one of its descendant locations + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + + // ContentInfo for "How to use Ibexa" + $contentInfo = $contentService->loadContentInfo($contentId); + + $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); + + // Throws exception, since content is already located at "/1/2/107/110/" + $locationService->createLocation( + $contentInfo, + $locationCreate + ); + /* END: Use Case */ + } + + /** + * Test for the createLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::createLocation() + * @depends testNewLocationCreateStruct + */ + public function testCreateLocationThrowsInvalidArgumentExceptionParentIsSubLocationOfContent() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', 4); + $parentLocationId = $this->generateId('location', 12); + /* BEGIN: Use Case */ + // $contentId is the ID of an existing content object + // $parentLocationId is the ID of an existing location which is below a + // location that is assigned to the content + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + + // ContentInfo for "How to use Ibexa" + $contentInfo = $contentService->loadContentInfo($contentId); + + $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); + + // Throws exception, since content is already located at "/1/2/" + $locationService->createLocation( + $contentInfo, + $locationCreate + ); + /* END: Use Case */ + } + + /** + * Test for the createLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::createLocation() + * @depends testNewLocationCreateStruct + */ + public function testCreateLocationThrowsInvalidArgumentExceptionRemoteIdExists() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', 41); + $parentLocationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $contentId is the ID of an existing content object + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + + // ContentInfo for "How to use Ibexa" + $contentInfo = $contentService->loadContentInfo($contentId); + + $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); + // This remote ID already exists + $locationCreate->remoteId = 'f3e90596361e31d496d4026eb624c983'; + + // Throws exception, since remote ID is already in use + $locationService->createLocation( + $contentInfo, + $locationCreate + ); + /* END: Use Case */ + } + + /** + * Test for the createLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::createLocation() + * @depends testNewLocationCreateStruct + * @dataProvider dataProviderForOutOfRangeLocationPriority + */ + public function testCreateLocationThrowsInvalidArgumentExceptionPriorityIsOutOfRange($priority) + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', 41); + $parentLocationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $contentId is the ID of an existing content object + // $parentLocationId is the ID of an existing location + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + + // ContentInfo for "How to use Ibexa" + $contentInfo = $contentService->loadContentInfo($contentId); + + $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); + $locationCreate->priority = $priority; + $locationCreate->hidden = true; + $locationCreate->remoteId = 'sindelfingen'; + $locationCreate->sortField = Location::SORT_FIELD_NODE_ID; + $locationCreate->sortOrder = Location::SORT_ORDER_DESC; + + // Throws exception, since priority is out of range + $locationService->createLocation( + $contentInfo, + $locationCreate + ); + /* END: Use Case */ + } + + public function dataProviderForOutOfRangeLocationPriority() + { + return [[-2147483649], [2147483648]]; + } + + /** + * Test for the createLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::createLocation() + * @depends testCreateLocation + */ + public function testCreateLocationInTransactionWithRollback() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', 41); + $parentLocationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $contentId is the ID of an existing content object + // $parentLocationId is the ID of an existing location + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + + $repository->beginTransaction(); + + try { + // ContentInfo for "How to use Ibexa" + $contentInfo = $contentService->loadContentInfo($contentId); + + $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); + $locationCreate->remoteId = 'sindelfingen'; + + $createdLocationId = $locationService->createLocation( + $contentInfo, + $locationCreate + )->id; + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + $repository->rollback(); + + try { + // Throws exception since creation of location was rolled back + $location = $locationService->loadLocation($createdLocationId); + } catch (NotFoundException $e) { + return; + } + /* END: Use Case */ + + $this->fail('Objects still exists after rollback.'); + } + + /** + * Test for the loadLocation() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocation + * @depends testCreateLocation + */ + public function testLoadLocation() + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $locationId is the ID of an existing location + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation($locationId); + /* END: Use Case */ + + $this->assertInstanceOf( + Location::class, + $location + ); + self::assertEquals(5, $location->id); + + return $location; + } + + /** + * Test for the loadLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocation() + * @depends testLoadLocation + */ + public function testLoadLocationRootStructValues() + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $location = $locationService->loadLocation($this->generateId('location', 1)); + + $this->assertRootLocationStructValues($location); + } + + public function testLoadLocationRootStructValuesWithPrioritizedLanguages(): void + { + $repository = $this->getRepository(); + + $rootLocation = $repository + ->getLocationService() + ->loadLocation( + $this->generateId('location', 1), + [ + 'eng-GB', + 'ger-DE', + ] + ); + + $this->assertRootLocationStructValues($rootLocation); + } + + private function assertRootLocationStructValues(Location $location): void + { + $legacyDateTime = new \DateTime(); + $legacyDateTime->setTimestamp(1030968000); + + $this->assertInstanceOf(Location::class, $location); + $this->assertPropertiesCorrect( + [ + 'id' => $this->generateId('location', 1), + 'status' => 1, + 'priority' => 0, + 'hidden' => false, + 'invisible' => false, + 'remoteId' => '629709ba256fe317c3ddcee35453a96a', + 'parentLocationId' => $this->generateId('location', 1), + 'pathString' => '/1/', + 'depth' => 0, + 'sortField' => 1, + 'sortOrder' => 1, + ], + $location + ); + + $this->assertInstanceOf(ContentInfo::class, $location->contentInfo); + $this->assertPropertiesCorrect( + [ + 'id' => $this->generateId('content', 0), + 'name' => 'Top Level Nodes', + 'sectionId' => 1, + 'mainLocationId' => 1, + 'contentTypeId' => 1, + 'currentVersionNo' => 1, + 'published' => 1, + 'ownerId' => 14, + 'modificationDate' => $legacyDateTime, + 'publishedDate' => $legacyDateTime, + 'alwaysAvailable' => 1, + 'remoteId' => null, + 'mainLanguageCode' => 'eng-GB', + ], + $location->contentInfo + ); + } + + /** + * Test for the loadLocation() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocation() + * @depends testLoadLocation + */ + public function testLoadLocationStructValues(Location $location) + { + $this->assertPropertiesCorrect( + [ + 'id' => $this->generateId('location', 5), + 'priority' => 0, + 'hidden' => false, + 'invisible' => false, + 'remoteId' => '3f6d92f8044aed134f32153517850f5a', + 'parentLocationId' => $this->generateId('location', 1), + 'pathString' => '/1/5/', + 'depth' => 1, + 'sortField' => 1, + 'sortOrder' => 1, + ], + $location + ); + + $this->assertInstanceOf(ContentInfo::class, $location->contentInfo); + $this->assertEquals($this->generateId('object', 4), $location->contentInfo->id); + + $this->assertInstanceOf(Location::class, $location->getParentLocation()); + $this->assertEquals($this->generateId('location', 1), $location->getParentLocation()->id); + + // Check lazy loaded proxy on ->content + $this->assertInstanceOf( + Content::class, + $content = $location->getContent() + ); + $this->assertEquals(4, $content->contentInfo->id); + } + + public function testLoadLocationPrioritizedLanguagesFallback() + { + $repository = $this->getRepository(); + + // Add a language + $this->createLanguage('nor-NO', 'Norsk'); + + $locationService = $repository->getLocationService(); + $contentService = $repository->getContentService(); + $location = $locationService->loadLocation(5); + + // Translate "Users" + $draft = $contentService->createContentDraft($location->contentInfo); + $struct = $contentService->newContentUpdateStruct(); + $struct->setField('name', 'Brukere', 'nor-NO'); + $draft = $contentService->updateContent($draft->getVersionInfo(), $struct); + $contentService->publishVersion($draft->getVersionInfo()); + + // Load with priority language (fallback will be the old one) + $location = $locationService->loadLocation(5, ['nor-NO']); + + $this->assertInstanceOf( + Location::class, + $location + ); + self::assertEquals(5, $location->id); + $this->assertInstanceOf( + Content::class, + $content = $location->getContent() + ); + $this->assertEquals(4, $content->contentInfo->id); + + $this->assertEquals($content->getVersionInfo()->getName(), 'Brukere'); + $this->assertEquals($content->getVersionInfo()->getName('eng-US'), 'Users'); + } + + /** + * Test that accessing lazy-loaded Content without a translation in the specific + * not available language throws NotFoundException. + */ + public function testLoadLocationThrowsNotFoundExceptionForNotAvailableContent(): void + { + $repository = $this->getRepository(); + + $locationService = $repository->getLocationService(); + + $this->createLanguage('pol-PL', 'Polski'); + + $this->expectException(NotFoundException::class); + + // Note: relying on existing database fixtures to make test case more readable + $locationService->loadLocation(60, ['pol-PL']); + } + + /** + * Test for the loadLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocation() + * @depends testCreateLocation + */ + public function testLoadLocationThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $nonExistentLocationId = $this->generateId('location', 2342); + /* BEGIN: Use Case */ + $locationService = $repository->getLocationService(); + + // Throws exception, if Location with $nonExistentLocationId does not + // exist + $location = $locationService->loadLocation($nonExistentLocationId); + /* END: Use Case */ + } + + /** + * Test for the loadLocationList() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationList + */ + public function testLoadLocationList(): void + { + $repository = $this->getRepository(); + + // 5 is the ID of an existing location, 442 is a non-existing id + $locationService = $repository->getLocationService(); + $locations = $locationService->loadLocationList([5, 442]); + + self::assertIsIterable($locations); + self::assertCount(1, $locations); + self::assertEquals([5], array_keys($locations)); + self::assertInstanceOf(Location::class, $locations[5]); + self::assertEquals(5, $locations[5]->id); + } + + /** + * Test for the loadLocationList() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationList + * @depends testLoadLocationList + */ + public function testLoadLocationListPrioritizedLanguagesFallback(): void + { + $repository = $this->getRepository(); + + $this->createLanguage('pol-PL', 'Polski'); + + // 5 is the ID of an existing location, 442 is a non-existing id + $locationService = $repository->getLocationService(); + $locations = $locationService->loadLocationList([5, 442], ['pol-PL'], false); + + self::assertIsIterable($locations); + self::assertCount(0, $locations); + } + + /** + * Test for the loadLocationList() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationList + * @depends testLoadLocationListPrioritizedLanguagesFallback + */ + public function testLoadLocationListPrioritizedLanguagesFallbackAndAlwaysAvailable(): void + { + $repository = $this->getRepository(); + + $this->createLanguage('pol-PL', 'Polski'); + + // 5 is the ID of an existing location, 442 is a non-existing id + $locationService = $repository->getLocationService(); + $locations = $locationService->loadLocationList([5, 442], ['pol-PL'], true); + + self::assertIsIterable($locations); + self::assertCount(1, $locations); + self::assertEquals([5], array_keys($locations)); + self::assertInstanceOf(Location::class, $locations[5]); + self::assertEquals(5, $locations[5]->id); + } + + /** + * Test for the loadLocationList() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationList + */ + public function testLoadLocationListWithRootLocationId() + { + $repository = $this->getRepository(); + + // 1 is the ID of an root location + $locationService = $repository->getLocationService(); + $locations = $locationService->loadLocationList([1]); + + self::assertIsIterable($locations); + self::assertCount(1, $locations); + self::assertEquals([1], array_keys($locations)); + self::assertInstanceOf(Location::class, $locations[1]); + self::assertEquals(1, $locations[1]->id); + } + + /** + * Test for the loadLocationList() method. + * + * Ensures the list is returned in the same order as passed IDs array. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationList + */ + public function testLoadLocationListInCorrectOrder() + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $cachedLocationId = 2; + $locationIdsToLoad = [43, $cachedLocationId, 5]; + + // Call loadLocation to cache it in memory as it might possibly affect list order + $locationService->loadLocation($cachedLocationId); + + $locations = $locationService->loadLocationList($locationIdsToLoad); + $locationIds = array_column($locations, 'id'); + + self::assertEquals($locationIdsToLoad, $locationIds); + } + + /** + * Test for the loadLocationByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationByRemoteId() + * @depends testLoadLocation + */ + public function testLoadLocationByRemoteId() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocationByRemoteId( + '3f6d92f8044aed134f32153517850f5a' + ); + /* END: Use Case */ + + $this->assertEquals( + $locationService->loadLocation($this->generateId('location', 5)), + $location + ); + } + + /** + * Test for the loadLocationByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationByRemoteId() + * @depends testLoadLocation + */ + public function testLoadLocationByRemoteIdThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $locationService = $repository->getLocationService(); + + // Throws exception, since Location with remote ID does not exist + $location = $locationService->loadLocationByRemoteId( + 'not-exists' + ); + /* END: Use Case */ + } + + /** + * Test for the loadLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocations() + * @depends testCreateLocation + */ + public function testLoadLocations() + { + $repository = $this->getRepository(); + + $contentId = $this->generateId('object', 4); + /* BEGIN: Use Case */ + // $contentId contains the ID of an existing content object + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + + $contentInfo = $contentService->loadContentInfo($contentId); + + $locations = $locationService->loadLocations($contentInfo); + /* END: Use Case */ + + $this->assertIsArray($locations); + self::assertNotEmpty($locations); + + foreach ($locations as $location) { + self::assertInstanceOf(Location::class, $location); + self::assertEquals($contentInfo->id, $location->getContentInfo()->id); + } + + return $locations; + } + + /** + * Test for the loadLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocations() + * @depends testLoadLocations + */ + public function testLoadLocationsContent(array $locations) + { + $this->assertCount(1, $locations); + foreach ($locations as $loadedLocation) { + self::assertInstanceOf(Location::class, $loadedLocation); + } + + usort( + $locations, + static function ($a, $b) { + return strcmp($a->id, $b->id); + } + ); + + $this->assertEquals( + [$this->generateId('location', 5)], + array_map( + static function (Location $location) { + return $location->id; + }, + $locations + ) + ); + } + + /** + * Test for the loadLocations() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location[] + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocations($contentInfo, $rootLocation) + * @depends testLoadLocations + */ + public function testLoadLocationsLimitedSubtree() + { + $repository = $this->getRepository(); + + $originalLocationId = $this->generateId('location', 54); + $originalParentLocationId = $this->generateId('location', 48); + $newParentLocationId = $this->generateId('location', 43); + /* BEGIN: Use Case */ + // $originalLocationId is the ID of an existing location + // $originalParentLocationId is the ID of the parent location of + // $originalLocationId + // $newParentLocationId is the ID of an existing location outside the tree + // of $originalLocationId and $originalParentLocationId + $locationService = $repository->getLocationService(); + + // Location at "/1/48/54" + $originalLocation = $locationService->loadLocation($originalLocationId); + + // Create location under "/1/43/" + $locationCreate = $locationService->newLocationCreateStruct($newParentLocationId); + $locationService->createLocation( + $originalLocation->contentInfo, + $locationCreate + ); + + $findRootLocation = $locationService->loadLocation($originalParentLocationId); + + // Returns an array with only $originalLocation + $locations = $locationService->loadLocations( + $originalLocation->contentInfo, + $findRootLocation + ); + /* END: Use Case */ + + $this->assertIsArray($locations); + + return $locations; + } + + /** + * Test for the loadLocations() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location[] $locations + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocations() + * @depends testLoadLocationsLimitedSubtree + */ + public function testLoadLocationsLimitedSubtreeContent(array $locations) + { + $this->assertCount(1, $locations); + + $this->assertEquals( + $this->generateId('location', 54), + reset($locations)->id + ); + } + + /** + * Test for the loadLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocations() + * @depends testLoadLocations + */ + public function testLoadLocationsThrowsBadStateException() + { + $this->expectException(BadStateException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + + // Create new content, which is not published + $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); + $contentCreate = $contentService->newContentCreateStruct($folderType, 'eng-US'); + $contentCreate->setField('name', 'New Folder'); + $content = $contentService->createContent($contentCreate); + + // Throws Exception, since $content has no published version, yet + $locationService->loadLocations( + $content->contentInfo + ); + /* END: Use Case */ + } + + /** + * Test for the loadLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocations($contentInfo, $rootLocation) + * @depends testLoadLocations + */ + public function testLoadLocationsThrowsBadStateExceptionLimitedSubtree() + { + $this->expectException(BadStateException::class); + + $repository = $this->getRepository(); + + $someLocationId = $this->generateId('location', 2); + /* BEGIN: Use Case */ + // $someLocationId is the ID of an existing location + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + + // Create new content, which is not published + $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); + $contentCreate = $contentService->newContentCreateStruct($folderType, 'eng-US'); + $contentCreate->setField('name', 'New Folder'); + $content = $contentService->createContent($contentCreate); + + $findRootLocation = $locationService->loadLocation($someLocationId); + + // Throws Exception, since $content has no published version, yet + $locationService->loadLocations( + $content->contentInfo, + $findRootLocation + ); + /* END: Use Case */ + } + + /** + * Test for the loadLocationChildren() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationChildren + * @depends testLoadLocation + */ + public function testLoadLocationChildren() + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $locationId is the ID of an existing location + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation($locationId); + + $childLocations = $locationService->loadLocationChildren($location); + /* END: Use Case */ + + $this->assertInstanceOf(LocationList::class, $childLocations); + $this->assertIsArray($childLocations->locations); + $this->assertNotEmpty($childLocations->locations); + $this->assertIsInt($childLocations->totalCount); + + foreach ($childLocations->locations as $childLocation) { + $this->assertInstanceOf(Location::class, $childLocation); + $this->assertEquals($location->id, $childLocation->parentLocationId); + } + + return $childLocations; + } + + /** + * Test loading parent Locations for draft Content. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadParentLocationsForDraftContent + */ + public function testLoadParentLocationsForDraftContent() + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + + // prepare locations + $locationCreateStructs = [ + $locationService->newLocationCreateStruct(2), + $locationService->newLocationCreateStruct(5), + ]; + + // Create new content + $folderType = $contentTypeService->loadContentTypeByIdentifier('folder'); + $contentCreate = $contentService->newContentCreateStruct($folderType, 'eng-US'); + $contentCreate->setField('name', 'New Folder'); + $contentDraft = $contentService->createContent($contentCreate, $locationCreateStructs); + + // Test loading parent Locations + $locations = $locationService->loadParentLocationsForDraftContent($contentDraft->versionInfo); + + self::assertCount(2, $locations); + foreach ($locations as $location) { + // test it is one of the given parent locations + self::assertTrue($location->id === 2 || $location->id === 5); + } + + return $contentDraft; + } + + /** + * Test that trying to load parent Locations throws Exception if Content is not a draft. + * + * @depends testLoadParentLocationsForDraftContent + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $contentDraft + */ + public function testLoadParentLocationsForDraftContentThrowsBadStateException(Content $contentDraft) + { + $this->expectException(BadStateException::class); + $this->expectExceptionMessageMatches('/is already published/'); + + $repository = $this->getRepository(false); + $locationService = $repository->getLocationService(); + $contentService = $repository->getContentService(); + + $content = $contentService->publishVersion($contentDraft->versionInfo); + + $locationService->loadParentLocationsForDraftContent($content->versionInfo); + } + + /** + * Test for the getLocationChildCount() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::getLocationChildCount() + * @depends testLoadLocation + */ + public function testGetLocationChildCount() + { + // $locationId is the ID of an existing location + $locationService = $this->getRepository()->getLocationService(); + + $this->assertSame( + 5, + $locationService->getLocationChildCount( + $locationService->loadLocation($this->generateId('location', 5)) + ) + ); + } + + /** + * Test for the loadLocationChildren() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationChildren() + * @depends testLoadLocationChildren + */ + public function testLoadLocationChildrenData(LocationList $locations) + { + $this->assertCount(5, $locations->locations); + $this->assertEquals(5, $locations->getTotalCount()); + + foreach ($locations->locations as $location) { + $this->assertInstanceOf( + Location::class, + $location + ); + } + + $this->assertEquals( + [ + $this->generateId('location', 12), + $this->generateId('location', 13), + $this->generateId('location', 14), + $this->generateId('location', 44), + $this->generateId('location', 61), + ], + array_map( + static function (Location $location) { + return $location->id; + }, + $locations->locations + ) + ); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationChildren + */ + public function testLoadLocationChildrenWithOffset(): LocationList + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + // $locationId is the ID of an existing location + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation($locationId); + + $childLocations = $locationService->loadLocationChildren($location, 2); + + self::assertIsIterable($childLocations); + self::assertIsInt($childLocations->totalCount); + + return $childLocations; + } + + /** + * Test for the loadLocationChildren() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationList $locations + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationChildren + * + * @depends testLoadLocationChildrenWithOffset + */ + public function testLoadLocationChildrenDataWithOffset(LocationList $locations): void + { + self::assertCount(3, $locations->locations); + self::assertEquals(5, $locations->getTotalCount()); + + $actualLocationIds = []; + foreach ($locations->locations as $location) { + self::assertInstanceOf(Location::class, $location); + $actualLocationIds[] = $location->id; + } + + self::assertEquals( + [ + $this->generateId('location', 14), + $this->generateId('location', 44), + $this->generateId('location', 61), + ], + $actualLocationIds + ); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationChildren + * + * @depends testLoadLocationChildren + */ + public function testLoadLocationChildrenWithOffsetAndLimit(): LocationList + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + // $locationId is the ID of an existing location + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation($locationId); + + $childLocations = $locationService->loadLocationChildren($location, 2, 2); + + $this->assertIsArray($childLocations->locations); + $this->assertIsInt($childLocations->totalCount); + + return $childLocations; + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationChildren + * + * @depends testLoadLocationChildrenWithOffsetAndLimit + */ + public function testLoadLocationChildrenDataWithOffsetAndLimit(LocationList $locations): void + { + $this->assertCount(2, $locations->locations); + $this->assertEquals(5, $locations->getTotalCount()); + + $actualLocationIds = []; + foreach ($locations->locations as $location) { + self::assertInstanceOf(Location::class, $location); + $actualLocationIds[] = $location->id; + } + + $this->assertEquals( + [ + $this->generateId('location', 14), + $this->generateId('location', 44), + ], + $actualLocationIds + ); + } + + public function providerForLoadLocationChildrenRespectsParentSortingClauses(): iterable + { + yield 'Name_ASC' => [ + Location::SORT_FIELD_NAME, + Location::SORT_ORDER_ASC, + ['A', 'B', 'C', 'Test'], + ]; + + yield 'Name_DESC' => [ + Location::SORT_FIELD_NAME, + Location::SORT_ORDER_DESC, + ['Test', 'C', 'B', 'A'], + ]; + + yield 'Priority_ASC' => [ + Location::SORT_FIELD_PRIORITY, + Location::SORT_ORDER_ASC, + ['A', 'C', 'B', 'Test'], + ]; + + yield 'Priority_DESC' => [ + Location::SORT_FIELD_PRIORITY, + Location::SORT_ORDER_DESC, + ['Test', 'B', 'C', 'A'], + ]; + + yield 'Path_ASC' => [ + Location::SORT_FIELD_PATH, + Location::SORT_ORDER_ASC, + ['A', 'C', 'B', 'Test'], + ]; + + yield 'Path_DESC' => [ + Location::SORT_FIELD_PATH, + Location::SORT_ORDER_DESC, + ['Test', 'B', 'C', 'A'], + ]; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + private function createStructureForTestLoadLocationChildrenRespectsParentSortingClauses(): Location + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + + // Firstly, create a container folder + $rootLocation = $locationService->loadLocation(1); + $createStruct = $contentService->newContentCreateStruct( + $contentTypeService->loadContentTypeByIdentifier('folder'), + 'eng-GB' + ); + $createStruct->setField('name', 'Parent folder'); + $content = $contentService->publishVersion( + $contentService->createContent( + $createStruct, + [$locationService->newLocationCreateStruct($rootLocation->id)] + )->versionInfo + ); + + // Secondly, create child folders that would be sorted later on + $contentNames = ['A', 'C', 'B', 'Test']; + $priority = 1; + foreach ($contentNames as $contentName) { + $rootLocation = $locationService->loadLocation($content->contentInfo->mainLocationId); + $createStruct = $contentService->newContentCreateStruct( + $contentTypeService->loadContentTypeByIdentifier('folder'), + 'eng-GB' + ); + $createStruct->setField('name', $contentName); + + $locationCreateStruct = $locationService->newLocationCreateStruct($rootLocation->id); + $locationCreateStruct->priority = $priority; + $contentService->publishVersion( + $contentService->createContent( + $createStruct, + [$locationCreateStruct] + )->versionInfo + ); + + ++$priority; + } + + $location = $locationService->loadLocation($content->contentInfo->mainLocationId); + $childrenLocations = $locationService->loadLocationChildren($location); + + self::assertCount(count($contentNames), $childrenLocations); + + return $location; + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\LocationService::loadLocationChildren + * + * @dataProvider providerForLoadLocationChildrenRespectsParentSortingClauses + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + public function testLoadLocationChildrenRespectsParentSortingClauses( + int $sortField, + int $sortOrder, + array $expectedChildrenNames + ): void { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $location = $this->createStructureForTestLoadLocationChildrenRespectsParentSortingClauses(); + + // Update Location in order to change sort clause + $locationUpdateStruct = $locationService->newLocationUpdateStruct(); + $locationUpdateStruct->sortField = $sortField; + $locationUpdateStruct->sortOrder = $sortOrder; + $location = $locationService->updateLocation( + $location, + $locationUpdateStruct + ); + + $childrenNames = array_map( + static function (Location $location) { + return $location->getContentInfo()->name; + }, + iterator_to_array($locationService->loadLocationChildren($location)) + ); + + self::assertSame($expectedChildrenNames, $childrenNames); + } + + /** + * Test for the newLocationUpdateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::newLocationUpdateStruct + */ + public function testNewLocationUpdateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $locationService = $repository->getLocationService(); + + $updateStruct = $locationService->newLocationUpdateStruct(); + /* END: Use Case */ + + $this->assertInstanceOf( + LocationUpdateStruct::class, + $updateStruct + ); + + $this->assertPropertiesCorrect( + [ + 'priority' => null, + 'remoteId' => null, + 'sortField' => null, + 'sortOrder' => null, + ], + $updateStruct + ); + } + + /** + * Test for the updateLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::updateLocation() + * @depends testLoadLocation + */ + public function testUpdateLocation() + { + $repository = $this->getRepository(); + + $originalLocationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $originalLocationId is the ID of an existing location + $locationService = $repository->getLocationService(); + + $originalLocation = $locationService->loadLocation($originalLocationId); + + $updateStruct = $locationService->newLocationUpdateStruct(); + $updateStruct->priority = 3; + $updateStruct->remoteId = 'c7adcbf1e96bc29bca28c2d809d0c7ef69272651'; + $updateStruct->sortField = Location::SORT_FIELD_PRIORITY; + $updateStruct->sortOrder = Location::SORT_ORDER_DESC; + + $updatedLocation = $locationService->updateLocation($originalLocation, $updateStruct); + /* END: Use Case */ + + $this->assertInstanceOf( + Location::class, + $updatedLocation + ); + + return [ + 'originalLocation' => $originalLocation, + 'updateStruct' => $updateStruct, + 'updatedLocation' => $updatedLocation, + ]; + } + + /** + * Test for the updateLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::updateLocation() + * @depends testUpdateLocation + */ + public function testUpdateLocationStructValues(array $data) + { + $originalLocation = $data['originalLocation']; + $updateStruct = $data['updateStruct']; + $updatedLocation = $data['updatedLocation']; + + $this->assertPropertiesCorrect( + [ + 'id' => $originalLocation->id, + 'priority' => $updateStruct->priority, + 'hidden' => $originalLocation->hidden, + 'invisible' => $originalLocation->invisible, + 'remoteId' => $updateStruct->remoteId, + 'contentInfo' => $originalLocation->contentInfo, + 'parentLocationId' => $originalLocation->parentLocationId, + 'pathString' => $originalLocation->pathString, + 'depth' => $originalLocation->depth, + 'sortField' => $updateStruct->sortField, + 'sortOrder' => $updateStruct->sortOrder, + ], + $updatedLocation + ); + } + + /** + * Test for the updateLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::updateLocation() + * @depends testLoadLocation + */ + public function testUpdateLocationWithSameRemoteId() + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $locationId and remote ID is the IDs of the same, existing location + $locationService = $repository->getLocationService(); + + $originalLocation = $locationService->loadLocation($locationId); + + $updateStruct = $locationService->newLocationUpdateStruct(); + + // Remote ID of an existing location with the same locationId + $updateStruct->remoteId = $originalLocation->remoteId; + + // Sets one of the properties to be able to confirm location gets updated, here: priority + $updateStruct->priority = 2; + + $location = $locationService->updateLocation($originalLocation, $updateStruct); + + // Checks that the location was updated + $this->assertEquals(2, $location->priority); + + // Checks that remoteId remains the same + $this->assertEquals($originalLocation->remoteId, $location->remoteId); + /* END: Use Case */ + } + + /** + * Test for the updateLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::updateLocation() + * @depends testLoadLocation + */ + public function testUpdateLocationThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $locationId and remoteId is the IDs of an existing, but not the same, location + $locationService = $repository->getLocationService(); + + $originalLocation = $locationService->loadLocation($locationId); + + $updateStruct = $locationService->newLocationUpdateStruct(); + + // Remote ID of an existing location with a different locationId + $updateStruct->remoteId = 'f3e90596361e31d496d4026eb624c983'; + + // Throws exception, since remote ID is already taken + $locationService->updateLocation($originalLocation, $updateStruct); + /* END: Use Case */ + } + + /** + * Test for the updateLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::updateLocation() + * @depends testLoadLocation + * @dataProvider dataProviderForOutOfRangeLocationPriority + */ + public function testUpdateLocationThrowsInvalidArgumentExceptionPriorityIsOutOfRange($priority) + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $locationId and remoteId is the IDs of an existing, but not the same, location + $locationService = $repository->getLocationService(); + + $originalLocation = $locationService->loadLocation($locationId); + + $updateStruct = $locationService->newLocationUpdateStruct(); + + // Priority value is out of range + $updateStruct->priority = $priority; + + // Throws exception, since remote ID is already taken + $locationService->updateLocation($originalLocation, $updateStruct); + /* END: Use Case */ + } + + /** + * Test for the updateLocation() method. + * Ref EZP-23302: Update Location fails if no change is performed with the update. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::updateLocation() + * @depends testLoadLocation + */ + public function testUpdateLocationTwice() + { + $repository = $this->getRepository(); + $permissionResolver = $repository->getPermissionResolver(); + + $locationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + $locationService = $repository->getLocationService(); + $permissionResolver->setCurrentUserReference($repository->getUserService()->loadUser(14)); + + $originalLocation = $locationService->loadLocation($locationId); + + $updateStruct = $locationService->newLocationUpdateStruct(); + $updateStruct->priority = 42; + + $updatedLocation = $locationService->updateLocation($originalLocation, $updateStruct); + + // Repeated update with the same, unchanged struct + $secondUpdatedLocation = $locationService->updateLocation($updatedLocation, $updateStruct); + /* END: Use Case */ + + $this->assertEquals($updatedLocation->priority, 42); + $this->assertEquals($secondUpdatedLocation->priority, 42); + } + + /** + * Test for the swapLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::swapLocation() + * @depends testLoadLocation + */ + public function testSwapLocation() + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $mediaLocationId = $this->generateId('location', 43); + $demoDesignLocationId = $this->generateId('location', 56); + + $mediaContentInfo = $locationService->loadLocation($mediaLocationId)->getContentInfo(); + $demoDesignContentInfo = $locationService->loadLocation($demoDesignLocationId)->getContentInfo(); + + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the "Media" page location in + // an Ibexa demo installation + + // $demoDesignLocationId is the ID of the "Demo Design" page location in an Ibexa + // Publish demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + $mediaLocation = $locationService->loadLocation($mediaLocationId); + $demoDesignLocation = $locationService->loadLocation($demoDesignLocationId); + + // Swaps the content referred to by the locations + $locationService->swapLocation($mediaLocation, $demoDesignLocation); + /* END: Use Case */ + + // Reload Locations, IDs swapped + $demoDesignLocation = $locationService->loadLocation($mediaLocationId); + $mediaLocation = $locationService->loadLocation($demoDesignLocationId); + + // Assert Location's Content is updated + $this->assertEquals( + $mediaContentInfo->id, + $mediaLocation->getContentInfo()->id + ); + $this->assertEquals( + $demoDesignContentInfo->id, + $demoDesignLocation->getContentInfo()->id + ); + + // Assert URL aliases are updated + $this->assertEquals( + $mediaLocation->id, + $repository->getURLAliasService()->lookup('/Design/Media')->destination + ); + $this->assertEquals( + $demoDesignLocation->id, + $repository->getURLAliasService()->lookup('/Ibexa-Demo-Design-without-demo-content')->destination + ); + } + + /** + * Test for the swapLocation() method with custom aliases. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::swapLocation + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testSwapLocationForContentWithCustomUrlAliases(): void + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $urlAliasService = $repository->getURLAliasService(); + $this->createLanguage('pol-PL', 'Polski'); + + $folder1 = $this->createFolder(['eng-GB' => 'Folder1', 'pol-PL' => 'Folder1'], 2); + $folder2 = $this->createFolder(['eng-GB' => 'Folder2'], 2); + $location1 = $locationService->loadLocation($folder1->contentInfo->mainLocationId); + $location2 = $locationService->loadLocation($folder2->contentInfo->mainLocationId); + + $urlAlias = $urlAliasService->createUrlAlias($location1, '/custom-location1', 'eng-GB', false, true); + $urlAliasService->createUrlAlias($location1, '/custom-location1', 'pol-PL', false, true); + $urlAliasService->createUrlAlias($location2, '/custom-location2', 'eng-GB', false, true); + $location1UrlAliases = $urlAliasService->listLocationAliases($location1); + $location2UrlAliases = $urlAliasService->listLocationAliases($location2); + + $locationService->swapLocation($location1, $location2); + $location1 = $locationService->loadLocation($location1->contentInfo->mainLocationId); + $location2 = $locationService->loadLocation($location2->contentInfo->mainLocationId); + + $location1UrlAliasesAfterSwap = $urlAliasService->listLocationAliases($location1); + $location2UrlAliasesAfterSwap = $urlAliasService->listLocationAliases($location2); + + $keyUrlAlias = array_search($urlAlias->id, array_column($location1UrlAliasesAfterSwap, 'id')); + + self::assertEquals($folder1->id, $location2->contentInfo->id); + self::assertEquals($folder2->id, $location1->contentInfo->id); + self::assertNotEquals($location1UrlAliases, $location1UrlAliasesAfterSwap); + self::assertEquals($location2UrlAliases, $location2UrlAliasesAfterSwap); + self::assertEquals(['eng-GB'], $location1UrlAliasesAfterSwap[$keyUrlAlias]->languageCodes); + } + + /** + * Test swapping secondary Location with main Location. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::swapLocation + * + * @see https://issues.ibexa.co/browse/EZP-28663 + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * + * @return int[] + */ + public function testSwapLocationForMainAndSecondaryLocation(): array + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $contentService = $repository->getContentService(); + + $folder1 = $this->createFolder(['eng-GB' => 'Folder1'], 2); + $folder2 = $this->createFolder(['eng-GB' => 'Folder2'], 2); + $folder3 = $this->createFolder(['eng-GB' => 'Folder3'], 2); + + $primaryLocation = $folder1->getVersionInfo()->getContentInfo()->getMainLocation(); + $parentLocation = $folder2->getVersionInfo()->getContentInfo()->getMainLocation(); + $secondaryLocation = $locationService->createLocation( + $folder1->getVersionInfo()->getContentInfo(), + $locationService->newLocationCreateStruct($parentLocation->id) + ); + + $targetLocation = $folder3->getVersionInfo()->getContentInfo()->getMainLocation(); + + // perform sanity checks + $this->assertContentHasExpectedLocations([$primaryLocation, $secondaryLocation], $folder1); + + // begin use case + $locationService->swapLocation($secondaryLocation, $targetLocation); + + // test results + $primaryLocation = $locationService->loadLocation($primaryLocation->id); + $secondaryLocation = $locationService->loadLocation($secondaryLocation->id); + $targetLocation = $locationService->loadLocation($targetLocation->id); + + self::assertEquals($folder1->id, $primaryLocation->getContentInfo()->getId()); + self::assertEquals($folder1->id, $targetLocation->getContentInfo()->getId()); + self::assertEquals($folder3->id, $secondaryLocation->getContentInfo()->getId()); + + $this->assertContentHasExpectedLocations([$primaryLocation, $targetLocation], $folder1); + + self::assertEquals( + $primaryLocation->id, + $contentService->loadContent($folder1->id)->getVersionInfo()->getContentInfo()->getMainLocationId() + ); + + self::assertEquals( + $parentLocation->id, + $contentService->loadContent($folder2->id)->getVersionInfo()->getContentInfo()->getMainLocationId() + ); + + // only in case of Folder 3, main location id changed due to swap + self::assertEquals( + $secondaryLocation->id, + $contentService->loadContent($folder3->id)->getVersionInfo()->getContentInfo()->getMainLocation()->id + ); + + return [$folder1, $folder2, $folder3]; + } + + /** + * Compare Ids of expected and loaded Locations for the given Content. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location[] $expectedLocations + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + private function assertContentHasExpectedLocations(array $expectedLocations, Content $content): void + { + $repository = $this->getRepository(false); + $locationService = $repository->getLocationService(); + + $expectedLocationIds = array_map( + static function (Location $location) { + return (int)$location->id; + }, + $expectedLocations + ); + + $actualLocationsIds = array_map( + static function (Location $location) { + return $location->id; + }, + $locationService->loadLocations($content->contentInfo) + ); + self::assertCount(count($expectedLocations), $actualLocationsIds); + + // perform unordered equality assertion + self::assertEqualsCanonicalizing( + $expectedLocationIds, + $actualLocationsIds, + sprintf( + 'Content %d contains Locations %s, not expected Locations: %s', + $content->id, + implode(', ', $actualLocationsIds), + implode(', ', $expectedLocationIds) + ) + ); + } + + /** + * @depends testSwapLocationForMainAndSecondaryLocation + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content[] $contentItems Content items created by testSwapLocationForSecondaryLocation + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testSwapLocationDoesNotCorruptSearchResults(array $contentItems) + { + $repository = $this->getRepository(false); + $searchService = $repository->getSearchService(); + + $this->refreshSearch($repository); + + $contentIds = array_map( + static function (Content $content) { + return $content->id; + }, + $contentItems + ); + + $query = new Query(); + $query->filter = new Query\Criterion\ContentId($contentIds); + + $searchResult = $searchService->findContent($query); + + self::assertEquals(count($contentItems), $searchResult->totalCount); + self::assertEquals( + $searchResult->totalCount, + count($searchResult->searchHits), + 'Total count of search result hits does not match the actual number of found results' + ); + $foundContentIds = array_map( + static function (SearchHit $searchHit) { + return $searchHit->valueObject->id; + }, + $searchResult->searchHits + ); + sort($contentIds); + sort($foundContentIds); + self::assertSame( + $contentIds, + $foundContentIds, + 'Got different than expected Content item Ids' + ); + } + + /** + * Test swapping two secondary (non-main) Locations. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::swapLocation + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testSwapLocationForSecondaryLocations(): void + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $folder1 = $this->createFolder(['eng-GB' => 'Folder1'], 2); + $folder2 = $this->createFolder(['eng-GB' => 'Folder2'], 2); + $parentFolder1 = $this->createFolder(['eng-GB' => 'Parent1'], 2); + $parentFolder2 = $this->createFolder(['eng-GB' => 'Parent2'], 2); + + $parentLocation1 = $parentFolder1->getVersionInfo()->getContentInfo()->getMainLocation(); + $parentLocation2 = $parentFolder2->getVersionInfo()->getContentInfo()->getMainLocation(); + $secondaryLocation1 = $locationService->createLocation( + $folder1->getVersionInfo()->getContentInfo(), + $locationService->newLocationCreateStruct($parentLocation1->id) + ); + $secondaryLocation2 = $locationService->createLocation( + $folder2->getVersionInfo()->getContentInfo(), + $locationService->newLocationCreateStruct($parentLocation2->id) + ); + + // begin use case + $locationService->swapLocation($secondaryLocation1, $secondaryLocation2); + + // test results + $secondaryLocation1 = $locationService->loadLocation($secondaryLocation1->id); + $secondaryLocation2 = $locationService->loadLocation($secondaryLocation2->id); + + self::assertEquals($folder2->id, $secondaryLocation1->getContentInfo()->getId()); + self::assertEquals($folder1->id, $secondaryLocation2->getContentInfo()->getId()); + + self::assertEqualsCanonicalizing( + [$folder1->getVersionInfo()->getContentInfo()->getMainLocationId(), $secondaryLocation2->id], + array_map( + static fn (Location $location): int => $location->id, + $locationService->loadLocations($folder1->getVersionInfo()->getContentInfo()) + ) + ); + + self::assertEqualsCanonicalizing( + [$folder2->getVersionInfo()->getContentInfo()->getMainLocationId(), $secondaryLocation1->id], + array_map( + static fn (Location $location): int => $location->id, + $locationService->loadLocations($folder2->getVersionInfo()->getContentInfo()) + ) + ); + } + + /** + * Test swapping Main Location of a Content with another one updates Content item Main Location. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::swapLocation + */ + public function testSwapLocationUpdatesMainLocation() + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $contentService = $repository->getContentService(); + + $mainLocationParentId = 60; + $secondaryLocationId = 43; + + $publishedContent = $this->publishContentWithParentLocation( + 'Content for Swap Location Test', + $mainLocationParentId + ); + + // sanity check + $mainLocation = $locationService->loadLocation($publishedContent->contentInfo->mainLocationId); + self::assertEquals($mainLocationParentId, $mainLocation->parentLocationId); + + // load another pre-existing location + $secondaryLocation = $locationService->loadLocation($secondaryLocationId); + + // swap the Main Location with a secondary one + $locationService->swapLocation($mainLocation, $secondaryLocation); + + // check if Main Location has been updated + $mainLocation = $locationService->loadLocation($secondaryLocation->id); + self::assertEquals($publishedContent->contentInfo->id, $mainLocation->contentInfo->id); + self::assertEquals($mainLocation->id, $mainLocation->contentInfo->mainLocationId); + + $reloadedContent = $contentService->loadContentByContentInfo($publishedContent->contentInfo); + self::assertEquals($mainLocation->id, $reloadedContent->contentInfo->mainLocationId); + } + + /** + * Test if location swap affects related bookmarks. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::swapLocation + */ + public function testBookmarksAreSwappedAfterSwapLocation() + { + $repository = $this->getRepository(); + + $mediaLocationId = $this->generateId('location', 43); + $demoDesignLocationId = $this->generateId('location', 56); + + /* BEGIN: Use Case */ + $locationService = $repository->getLocationService(); + $bookmarkService = $repository->getBookmarkService(); + + $mediaLocation = $locationService->loadLocation($mediaLocationId); + $demoDesignLocation = $locationService->loadLocation($demoDesignLocationId); + + // Bookmark locations + $bookmarkService->createBookmark($mediaLocation); + $bookmarkService->createBookmark($demoDesignLocation); + + $beforeSwap = $bookmarkService->loadBookmarks(); + + // Swaps the content referred to by the locations + $locationService->swapLocation($mediaLocation, $demoDesignLocation); + + $afterSwap = $bookmarkService->loadBookmarks(); + /* END: Use Case */ + + $this->assertEquals($beforeSwap->items[0]->id, $afterSwap->items[1]->id); + $this->assertEquals($beforeSwap->items[1]->id, $afterSwap->items[0]->id); + } + + /** + * Test for the hideLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::hideLocation() + * @depends testLoadLocation + */ + public function testHideLocation() + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $locationId is the ID of an existing location + $locationService = $repository->getLocationService(); + + $visibleLocation = $locationService->loadLocation($locationId); + + $hiddenLocation = $locationService->hideLocation($visibleLocation); + /* END: Use Case */ + + $this->assertInstanceOf( + Location::class, + $hiddenLocation + ); + + $this->assertTrue( + $hiddenLocation->hidden, + sprintf( + 'Location with ID "%s" is not hidden.', + $hiddenLocation->id + ) + ); + + $this->refreshSearch($repository); + + foreach ($locationService->loadLocationChildren($hiddenLocation)->locations as $child) { + $this->assertSubtreeProperties( + ['invisible' => true], + $child + ); + } + } + + /** + * Assert that $expectedValues are set in the subtree starting at $location. + * + * @param array $expectedValues + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + */ + protected function assertSubtreeProperties(array $expectedValues, Location $location, $stopId = null) + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + if ($location->id === $stopId) { + return; + } + + foreach ($expectedValues as $propertyName => $propertyValue) { + $this->assertEquals( + $propertyValue, + $location->$propertyName + ); + + foreach ($locationService->loadLocationChildren($location)->locations as $child) { + $this->assertSubtreeProperties($expectedValues, $child); + } + } + } + + /** + * Test for the unhideLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::unhideLocation() + * @depends testHideLocation + */ + public function testUnhideLocation() + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $locationId is the ID of an existing location + $locationService = $repository->getLocationService(); + + $visibleLocation = $locationService->loadLocation($locationId); + $hiddenLocation = $locationService->hideLocation($visibleLocation); + + $unHiddenLocation = $locationService->unhideLocation($hiddenLocation); + /* END: Use Case */ + + $this->assertInstanceOf( + Location::class, + $unHiddenLocation + ); + + $this->assertFalse( + $unHiddenLocation->hidden, + sprintf( + 'Location with ID "%s" is hidden.', + $unHiddenLocation->id + ) + ); + + $this->refreshSearch($repository); + + foreach ($locationService->loadLocationChildren($unHiddenLocation)->locations as $child) { + $this->assertSubtreeProperties( + ['invisible' => false], + $child + ); + } + } + + /** + * Test for the unhideLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::unhideLocation() + * @depends testUnhideLocation + */ + public function testUnhideLocationNotUnhidesHiddenSubtree() + { + $repository = $this->getRepository(); + + $higherLocationId = $this->generateId('location', 5); + $lowerLocationId = $this->generateId('location', 13); + /* BEGIN: Use Case */ + // $higherLocationId is the ID of a location + // $lowerLocationId is the ID of a location below $higherLocationId + $locationService = $repository->getLocationService(); + + $higherLocation = $locationService->loadLocation($higherLocationId); + $hiddenHigherLocation = $locationService->hideLocation($higherLocation); + + $lowerLocation = $locationService->loadLocation($lowerLocationId); + $hiddenLowerLocation = $locationService->hideLocation($lowerLocation); + + $unHiddenHigherLocation = $locationService->unhideLocation($hiddenHigherLocation); + /* END: Use Case */ + + $this->assertInstanceOf( + Location::class, + $unHiddenHigherLocation + ); + + $this->assertFalse( + $unHiddenHigherLocation->hidden, + sprintf( + 'Location with ID "%s" is hidden.', + $unHiddenHigherLocation->id + ) + ); + + $this->refreshSearch($repository); + + foreach ($locationService->loadLocationChildren($unHiddenHigherLocation)->locations as $child) { + $this->assertSubtreeProperties( + ['invisible' => false], + $child, + $this->generateId('location', 13) + ); + } + + $stillHiddenLocation = $locationService->loadLocation($this->generateId('location', 13)); + $this->assertTrue( + $stillHiddenLocation->hidden, + sprintf( + 'Hidden sub-location with ID %s unhidden unexpectedly.', + $stillHiddenLocation->id + ) + ); + foreach ($locationService->loadLocationChildren($stillHiddenLocation)->locations as $child) { + $this->assertSubtreeProperties( + ['invisible' => true], + $child + ); + } + } + + /** + * Test for the deleteLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::deleteLocation() + * @depends testLoadLocation + */ + public function testDeleteLocation() + { + $repository = $this->getRepository(); + + $mediaLocationId = $this->generateId('location', 43); + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the location of the + // "Media" location in an Ibexa demo installation + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation($mediaLocationId); + + $locationService->deleteLocation($location); + /* END: Use Case */ + + try { + $locationService->loadLocation($mediaLocationId); + $this->fail("Location $mediaLocationId not deleted."); + } catch (NotFoundException $e) { + } + + // The following IDs are IDs of child locations of $mediaLocationId location + // ( Media/Images, Media/Files, Media/Multimedia respectively ) + foreach ([51, 52, 53] as $childLocationId) { + try { + $locationService->loadLocation($this->generateId('location', $childLocationId)); + $this->fail("Location $childLocationId not deleted."); + } catch (NotFoundException $e) { + } + } + + // The following IDs are IDs of content below $mediaLocationId location + // ( Media/Images, Media/Files, Media/Multimedia respectively ) + $contentService = $this->getRepository()->getContentService(); + foreach ([49, 50, 51] as $childContentId) { + try { + $contentService->loadContentInfo($this->generateId('object', $childContentId)); + $this->fail("Content $childContentId not deleted."); + } catch (NotFoundException $e) { + } + } + } + + /** + * Test for the deleteLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::deleteLocation() + * @depends testDeleteLocation + */ + public function testDeleteLocationDecrementsChildCountOnParent() + { + $repository = $this->getRepository(); + + $mediaLocationId = $this->generateId('location', 43); + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the location of the + // "Media" location in an Ibexa demo installation + + $locationService = $repository->getLocationService(); + + // Load the current the user group location + $location = $locationService->loadLocation($mediaLocationId); + + // Load the parent location + $parentLocation = $locationService->loadLocation( + $location->parentLocationId + ); + + // Get child count + $childCountBefore = $locationService->getLocationChildCount($parentLocation); + + // Delete the user group location + $locationService->deleteLocation($location); + + $this->refreshSearch($repository); + + // Reload parent location + $parentLocation = $locationService->loadLocation( + $location->parentLocationId + ); + + // This will be $childCountBefore - 1 + $childCountAfter = $locationService->getLocationChildCount($parentLocation); + /* END: Use Case */ + + $this->assertEquals($childCountBefore - 1, $childCountAfter); + } + + /** + * Test for the deleteLocation() method. + * + * Related issue: EZP-21904 + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::deleteLocation() + */ + public function testDeleteContentObjectLastLocation() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use case */ + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + $contentTypeService = $repository->getContentTypeService(); + $urlAliasService = $repository->getURLAliasService(); + + // prepare Content object + $createStruct = $contentService->newContentCreateStruct( + $contentTypeService->loadContentTypeByIdentifier('folder'), + 'eng-GB' + ); + $createStruct->setField('name', 'Test folder'); + + // creata Content object + $content = $contentService->publishVersion( + $contentService->createContent( + $createStruct, + [$locationService->newLocationCreateStruct(2)] + )->versionInfo + ); + + // delete location + $locationService->deleteLocation( + $locationService->loadLocation( + $urlAliasService->lookup('/Test-folder')->destination + ) + ); + + // this should throw a not found exception + $contentService->loadContent($content->versionInfo->contentInfo->id); + /* END: Use case*/ + } + + /** + * Test for the deleteLocation() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::deleteLocation + * @depends testDeleteLocation + */ + public function testDeleteLocationDeletesRelatedBookmarks() + { + $repository = $this->getRepository(); + + $parentLocationId = $this->generateId('location', 43); + $childLocationId = $this->generateId('location', 53); + + /* BEGIN: Use Case */ + $locationService = $repository->getLocationService(); + $bookmarkService = $repository->getBookmarkService(); + + // Load location + $childLocation = $locationService->loadLocation($childLocationId); + // Add location to bookmarks + $bookmarkService->createBookmark($childLocation); + // Load parent location + $parentLocation = $locationService->loadLocation($parentLocationId); + // Delete parent location + $locationService->deleteLocation($parentLocation); + /* END: Use Case */ + + // Location isn't bookmarked anymore + foreach ($bookmarkService->loadBookmarks(0, 9999) as $bookmarkedLocation) { + $this->assertNotEquals($childLocation->id, $bookmarkedLocation->id); + } + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\LocationService::deleteLocation + */ + public function testDeleteUnusedLocationWhichPreviousHadContentWithRelativeAlias(): void + { + $repository = $this->getRepository(false); + + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + $urlAliasService = $repository->getURLAliasService(); + + $originalFolder = $this->createFolder(['eng-GB' => 'Original folder'], 2); + $newFolder = $this->createFolder(['eng-GB' => 'New folder'], 2); + $originalFolderLocationId = $originalFolder->contentInfo->mainLocationId; + + $forum = $contentService->publishVersion( + $contentService->createContent( + $this->createForumStruct('Some forum'), + [ + $locationService->newLocationCreateStruct($originalFolderLocationId), + ] + )->versionInfo + ); + + $forumMainLocation = $locationService->loadLocation( + $forum->contentInfo->mainLocationId + ); + + $customRelativeAliasPath = '/Original-folder/some-forum-alias'; + + $urlAliasService->createUrlAlias( + $forumMainLocation, + $customRelativeAliasPath, + 'eng-GB', + true, + true + ); + + $locationService->moveSubtree( + $forumMainLocation, + $locationService->loadLocation( + $newFolder->contentInfo->mainLocationId + ) + ); + + $this->assertAliasExists( + $customRelativeAliasPath, + $forumMainLocation, + $urlAliasService + ); + + $urlAliasService->lookup($customRelativeAliasPath); + + $locationService->deleteLocation( + $locationService->loadLocation( + $originalFolder->contentInfo->mainLocationId + ) + ); + + $this->assertAliasExists( + $customRelativeAliasPath, + $forumMainLocation, + $urlAliasService + ); + + $urlAliasService->lookup($customRelativeAliasPath); + } + + /** + * Test for the copySubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::copySubtree() + * @depends testLoadLocation + */ + public function testCopySubtree() + { + $repository = $this->getRepository(); + + $mediaLocationId = $this->generateId('location', 43); + $demoDesignLocationId = $this->generateId('location', 56); + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the "Media" page location in + // an Ibexa demo installation + + // $demoDesignLocationId is the ID of the "Demo Design" page location in an Ibexa + // Publish demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + // Load location to copy + $locationToCopy = $locationService->loadLocation($mediaLocationId); + + // Load new parent location + $newParentLocation = $locationService->loadLocation($demoDesignLocationId); + + // Copy location "Media" to "Demo Design" + $copiedLocation = $locationService->copySubtree( + $locationToCopy, + $newParentLocation + ); + /* END: Use Case */ + + $this->assertInstanceOf( + Location::class, + $copiedLocation + ); + + $this->assertPropertiesCorrect( + [ + 'depth' => $newParentLocation->depth + 1, + 'parentLocationId' => $newParentLocation->id, + 'pathString' => $newParentLocation->pathString . $this->parseId('location', $copiedLocation->id) . '/', + ], + $copiedLocation + ); + + $this->assertDefaultContentStates($copiedLocation->contentInfo); + } + + /** + * Test for the copySubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::copySubtree() + * @depends testLoadLocation + */ + public function testCopySubtreeWithAliases() + { + $repository = $this->getRepository(); + $urlAliasService = $repository->getURLAliasService(); + + // $mediaLocationId is the ID of the "Media" page location in + // an Ibexa demo installation + + // $demoDesignLocationId is the ID of the "Demo Design" page location in an Ibexa + // Publish demo installation + $mediaLocationId = $this->generateId('location', 43); + $demoDesignLocationId = $this->generateId('location', 56); + + $locationService = $repository->getLocationService(); + $locationToCopy = $locationService->loadLocation($mediaLocationId); + $newParentLocation = $locationService->loadLocation($demoDesignLocationId); + + $expectedSubItemAliases = [ + '/Design/Plain-site/Media/Multimedia', + '/Design/Plain-site/Media/Images', + '/Design/Plain-site/Media/Files', + ]; + + $this->assertAliasesBeforeCopy($urlAliasService, $expectedSubItemAliases); + + // Copy location "Media" to "Design" + $locationService->copySubtree( + $locationToCopy, + $newParentLocation + ); + + $this->assertGeneratedAliases($urlAliasService, $expectedSubItemAliases); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\LocationService::copySubtree + */ + public function testCopySubtreeWithTranslatedContent(): void + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + $urlAliasService = $repository->getURLAliasService(); + + $mediaLocationId = $this->generateId('location', 43); + $filesLocationId = $this->generateId('location', 52); + $demoDesignLocationId = $this->generateId('location', 56); + + $locationToCopy = $locationService->loadLocation($mediaLocationId); + $filesLocation = $locationService->loadLocation($filesLocationId); + $newParentLocation = $locationService->loadLocation($demoDesignLocationId); + + // translating the 'middle' folder + $translatedDraft = $contentService->createContentDraft($filesLocation->contentInfo); + $contentUpdateStruct = new ContentUpdateStruct([ + 'initialLanguageCode' => 'ger-DE', + 'fields' => $translatedDraft->getFields(), + ]); + $contentUpdateStruct->setField('short_name', 'FilesGER', 'ger-DE'); + $translatedContent = $contentService->updateContent($translatedDraft->versionInfo, $contentUpdateStruct); + $contentService->publishVersion($translatedContent->versionInfo); + + // creating additional content under translated folder + $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + $contentCreate = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $contentCreate->setField('name', 'My folder'); + $content = $contentService->createContent( + $contentCreate, + [new LocationCreateStruct(['parentLocationId' => $filesLocationId])] + ); + $contentService->publishVersion($content->versionInfo); + + $expectedSubItemAliases = [ + '/Design/Plain-site/Media/Multimedia', + '/Design/Plain-site/Media/Images', + '/Design/Plain-site/Media/Files', + '/Design/Plain-site/Media/Files/my-folder', + ]; + + $this->assertAliasesBeforeCopy($urlAliasService, $expectedSubItemAliases); + + $locationService->copySubtree( + $locationToCopy, + $newParentLocation + ); + + $this->assertGeneratedAliases($urlAliasService, $expectedSubItemAliases); + } + + /** + * Asserts that given Content has default ContentStates. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + */ + private function assertDefaultContentStates(ContentInfo $contentInfo) + { + $repository = $this->getRepository(); + $objectStateService = $repository->getObjectStateService(); + + $objectStateGroups = $objectStateService->loadObjectStateGroups(); + + foreach ($objectStateGroups as $objectStateGroup) { + $contentState = $objectStateService->getContentState($contentInfo, $objectStateGroup); + foreach ($objectStateService->loadObjectStates($objectStateGroup, Language::ALL) as $objectState) { + // Only check the first object state which is the default one. + $this->assertEquals( + $objectState, + $contentState + ); + break; + } + } + } + + /** + * Test for the copySubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::copySubtree() + * @depends testCopySubtree + */ + public function testCopySubtreeUpdatesSubtreeProperties() + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $locationToCopy = $locationService->loadLocation($this->generateId('location', 43)); + + // Load Subtree properties before copy + $expected = $this->loadSubtreeProperties($locationToCopy); + + $mediaLocationId = $this->generateId('location', 43); + $demoDesignLocationId = $this->generateId('location', 56); + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the "Media" page location in + // an Ibexa demo installation + + // $demoDesignLocationId is the ID of the "Demo Design" page location in an Ibexa + // Publish demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + // Load location to copy + $locationToCopy = $locationService->loadLocation($mediaLocationId); + + // Load new parent location + $newParentLocation = $locationService->loadLocation($demoDesignLocationId); + + // Copy location "Media" to "Demo Design" + $copiedLocation = $locationService->copySubtree( + $locationToCopy, + $newParentLocation + ); + /* END: Use Case */ + + $beforeIds = []; + foreach ($expected as $properties) { + $beforeIds[] = $properties['id']; + } + + $this->refreshSearch($repository); + + // Load Subtree properties after copy + $actual = $this->loadSubtreeProperties($copiedLocation); + + $this->assertEquals(count($expected), count($actual)); + + foreach ($actual as $properties) { + $this->assertNotContains($properties['id'], $beforeIds); + $this->assertStringStartsWith( + $newParentLocation->pathString . $this->parseId('location', $copiedLocation->id) . '/', + $properties['pathString'] + ); + $this->assertStringEndsWith( + '/' . $this->parseId('location', $properties['id']) . '/', + $properties['pathString'] + ); + } + } + + /** + * Test for the copySubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::copySubtree() + * @depends testCopySubtree + */ + public function testCopySubtreeIncrementsChildCountOfNewParent() + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $childCountBefore = $locationService->getLocationChildCount($locationService->loadLocation(56)); + + $mediaLocationId = $this->generateId('location', 43); + $demoDesignLocationId = $this->generateId('location', 56); + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the "Media" page location in + // an Ibexa demo installation + + // $demoDesignLocationId is the ID of the "Demo Design" page location in an Ibexa + // Publish demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + // Load location to copy + $locationToCopy = $locationService->loadLocation($mediaLocationId); + + // Load new parent location + $newParentLocation = $locationService->loadLocation($demoDesignLocationId); + + // Copy location "Media" to "Demo Design" + $copiedLocation = $locationService->copySubtree( + $locationToCopy, + $newParentLocation + ); + /* END: Use Case */ + + $this->refreshSearch($repository); + + $childCountAfter = $locationService->getLocationChildCount($locationService->loadLocation($demoDesignLocationId)); + + $this->assertEquals($childCountBefore + 1, $childCountAfter); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\LocationService::copySubtree() + */ + public function testCopySubtreeWithInvisibleChild(): void + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + // Hide child Location + $locationService->hideLocation($locationService->loadLocation($this->generateId('location', 53))); + + $this->refreshSearch($repository); + + $locationToCopy = $locationService->loadLocation($this->generateId('location', 43)); + + $expected = $this->loadSubtreeProperties($locationToCopy); + + $mediaLocationId = $this->generateId('location', 43); + $demoDesignLocationId = $this->generateId('location', 56); + $locationService = $repository->getLocationService(); + + $locationToCopy = $locationService->loadLocation($mediaLocationId); + + $newParentLocation = $locationService->loadLocation($demoDesignLocationId); + + $copiedLocation = $locationService->copySubtree( + $locationToCopy, + $newParentLocation + ); + + $this->refreshSearch($repository); + + // Load Subtree properties after copy + $actual = $this->loadSubtreeProperties($copiedLocation); + + self::assertEquals(count($expected), count($actual)); + + foreach ($actual as $key => $properties) { + self::assertEquals($expected[$key]['hidden'], $properties['hidden']); + self::assertEquals($expected[$key]['invisible'], $properties['invisible']); + } + } + + /** + * Test for the copySubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::copySubtree() + * @depends testCopySubtree + */ + public function testCopySubtreeThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $communityLocationId = $this->generateId('location', 5); + /* BEGIN: Use Case */ + // $communityLocationId is the ID of the "Community" page location in + // an Ibexa demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + // Load location to copy + $locationToCopy = $locationService->loadLocation($communityLocationId); + + // Use a child as new parent + $childLocations = $locationService->loadLocationChildren($locationToCopy)->locations; + $newParentLocation = end($childLocations); + + // This call will fail with an "InvalidArgumentException", because the + // new parent is a child location of the subtree to copy. + $locationService->copySubtree( + $locationToCopy, + $newParentLocation + ); + /* END: Use Case */ + } + + /** + * Test for the moveSubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree + * @depends testLoadLocation + */ + public function testMoveSubtree(): void + { + $repository = $this->getRepository(); + + $mediaLocationId = $this->generateId('location', 43); + $demoDesignLocationId = $this->generateId('location', 56); + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the "Media" page location in + // an Ibexa demo installation + + // $demoDesignLocationId is the ID of the "Demo Design" page location in an Ibexa + // Publish demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + // Load location to move + $locationToMove = $locationService->loadLocation($demoDesignLocationId); + + // Load new parent location + $newParentLocation = $locationService->loadLocation($mediaLocationId); + + // Move location from "Home" to "Media" + $locationService->moveSubtree( + $locationToMove, + $newParentLocation + ); + + // Load moved location + $movedLocation = $locationService->loadLocation($demoDesignLocationId); + /* END: Use Case */ + + $this->assertPropertiesCorrect( + [ + 'hidden' => false, + 'invisible' => false, + 'depth' => $newParentLocation->depth + 1, + 'parentLocationId' => $newParentLocation->id, + 'pathString' => $newParentLocation->pathString . $this->parseId('location', $movedLocation->id) . '/', + ], + $movedLocation + ); + } + + /** + * Test for the moveSubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree + * @depends testLoadLocation + */ + public function testMoveSubtreeToLocationWithoutContent(): void + { + $repository = $this->getRepository(); + + $rootLocationId = $this->generateId('location', 1); + $demoDesignLocationId = $this->generateId('location', 56); + $locationService = $repository->getLocationService(); + $locationToMove = $locationService->loadLocation($demoDesignLocationId); + $newParentLocation = $locationService->loadLocation($rootLocationId); + + $locationService->moveSubtree( + $locationToMove, + $newParentLocation + ); + + $movedLocation = $locationService->loadLocation($demoDesignLocationId); + + $this->assertPropertiesCorrect( + [ + 'hidden' => false, + 'invisible' => false, + 'depth' => $newParentLocation->depth + 1, + 'parentLocationId' => $newParentLocation->id, + 'pathString' => $newParentLocation->pathString . $this->parseId('location', $movedLocation->id) . '/', + ], + $movedLocation + ); + } + + /** + * Test for the moveSubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree + * @depends testLoadLocation + */ + public function testMoveSubtreeThrowsExceptionOnMoveNotIntoContainer(): void + { + $repository = $this->getRepository(); + + $mediaLocationId = $this->generateId('location', 43); + $demoDesignLocationId = $this->generateId('location', 56); + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the "Media" page location in + // an Ibexa demo installation + + // $demoDesignLocationId is the ID of the "Demo Design" page location in an Ibexa + // Publish demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + // Load location to move + $locationToMove = $locationService->loadLocation($mediaLocationId); + + // Load new parent location + $newParentLocation = $locationService->loadLocation($demoDesignLocationId); + + // Move location from "Home" to "Demo Design" (not container) + $this->expectException(InvalidArgumentException::class); + $locationService->moveSubtree($locationToMove, $newParentLocation); + } + + /** + * Test for the moveSubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree + * @depends testLoadLocation + */ + public function testMoveSubtreeThrowsExceptionOnMoveToSame(): void + { + $repository = $this->getRepository(); + + $mediaLocationId = $this->generateId('location', 43); + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the "Media" page location in + // an Ibexa demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + // Load location to move + $locationToMove = $locationService->loadLocation($mediaLocationId); + + // Load parent location + $newParentLocation = $locationService->loadLocation($locationToMove->parentLocationId); + + // Move location from "Home" to "Home" + $this->expectException(InvalidArgumentException::class); + $locationService->moveSubtree($locationToMove, $newParentLocation); + } + + /** + * Test for the moveSubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree + * @depends testMoveSubtree + */ + public function testMoveSubtreeHidden(): void + { + $repository = $this->getRepository(); + + $mediaLocationId = $this->generateId('location', 43); + $demoDesignLocationId = $this->generateId('location', 56); + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the "Media" page location in + // an Ibexa demo installation + + // $demoDesignLocationId is the ID of the "Demo Design" page location in an Ibexa + // Publish demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + // Load location to move + $locationToMove = $locationService->loadLocation($demoDesignLocationId); + + // Load new parent location + $newParentLocation = $locationService->loadLocation($mediaLocationId); + + // Hide the target location before we move + $newParentLocation = $locationService->hideLocation($newParentLocation); + + // Move location from "Demo Design" to "Home" + $locationService->moveSubtree( + $locationToMove, + $newParentLocation + ); + + // Load moved location + $movedLocation = $locationService->loadLocation($demoDesignLocationId); + /* END: Use Case */ + + $this->assertPropertiesCorrect( + [ + 'hidden' => false, + 'invisible' => true, + 'depth' => $newParentLocation->depth + 1, + 'parentLocationId' => $newParentLocation->id, + 'pathString' => $newParentLocation->pathString . $this->parseId('location', $movedLocation->id) . '/', + ], + $movedLocation + ); + } + + /** + * Test for the moveSubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree() + * @depends testMoveSubtree + */ + public function testMoveSubtreeUpdatesSubtreeProperties() + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $locationToMove = $locationService->loadLocation($this->generateId('location', 56)); + $newParentLocation = $locationService->loadLocation($this->generateId('location', 43)); + + // Load Subtree properties before move + $expected = $this->loadSubtreeProperties($locationToMove); + foreach ($expected as $id => $properties) { + $expected[$id]['depth'] = $properties['depth'] + 2; + $expected[$id]['pathString'] = str_replace( + $locationToMove->pathString, + $newParentLocation->pathString . $this->parseId('location', $locationToMove->id) . '/', + $properties['pathString'] + ); + } + + $mediaLocationId = $this->generateId('location', 43); + $demoDesignLocationId = $this->generateId('location', 56); + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the "Media" page location in + // an Ibexa demo installation + + // $demoDesignLocationId is the ID of the "Demo Design" page location in an Ibexa + // Publish demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + // Load location to move + $locationToMove = $locationService->loadLocation($demoDesignLocationId); + + // Load new parent location + $newParentLocation = $locationService->loadLocation($mediaLocationId); + + // Move location from "Demo Design" to "Home" + $locationService->moveSubtree( + $locationToMove, + $newParentLocation + ); + + // Load moved location + $movedLocation = $locationService->loadLocation($demoDesignLocationId); + /* END: Use Case */ + + $this->refreshSearch($repository); + + // Load Subtree properties after move + $actual = $this->loadSubtreeProperties($movedLocation); + + $this->assertEquals($expected, $actual); + } + + /** + * Test for the moveSubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree() + * @depends testMoveSubtreeUpdatesSubtreeProperties + */ + public function testMoveSubtreeUpdatesSubtreePropertiesHidden() + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $locationToMove = $locationService->loadLocation($this->generateId('location', 2)); + $newParentLocation = $locationService->loadLocation($this->generateId('location', 43)); + + // Hide the target location before we move + $newParentLocation = $locationService->hideLocation($newParentLocation); + + // Load Subtree properties before move + $expected = $this->loadSubtreeProperties($locationToMove); + foreach ($expected as $id => $properties) { + $expected[$id]['invisible'] = true; + $expected[$id]['depth'] = $properties['depth'] + 1; + $expected[$id]['pathString'] = str_replace( + $locationToMove->pathString, + $newParentLocation->pathString . $this->parseId('location', $locationToMove->id) . '/', + $properties['pathString'] + ); + } + + $homeLocationId = $this->generateId('location', 2); + $mediaLocationId = $this->generateId('location', 43); + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the "Media" page location in + // an Ibexa demo installation + + // $homeLocationId is the ID of the "Home" page location in an Ibexa + // Publish demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + // Load location to move + $locationToMove = $locationService->loadLocation($homeLocationId); + + // Load new parent location + $newParentLocation = $locationService->loadLocation($mediaLocationId); + + // Move location from "Home" to "Demo Design" + $locationService->moveSubtree( + $locationToMove, + $newParentLocation + ); + + // Load moved location + $movedLocation = $locationService->loadLocation($homeLocationId); + /* END: Use Case */ + + $this->refreshSearch($repository); + + // Load Subtree properties after move + $actual = $this->loadSubtreeProperties($movedLocation); + + $this->assertEquals($expected, $actual); + } + + /** + * Test for the moveSubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree() + * @depends testMoveSubtree + */ + public function testMoveSubtreeIncrementsChildCountOfNewParent() + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $newParentLocation = $locationService->loadLocation($this->generateId('location', 43)); + + // Load expected properties before move + $expected = $this->loadLocationProperties($newParentLocation); + $childCountBefore = $locationService->getLocationChildCount($newParentLocation); + + $mediaLocationId = $this->generateId('location', 43); + $demoDesignLocationId = $this->generateId('location', 56); + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the "Media" page location in + // an Ibexa demo installation + + // $demoDesignLocationId is the ID of the "Demo Design" page location in an Ibexa + // Publish demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + // Load location to move + $locationToMove = $locationService->loadLocation($demoDesignLocationId); + + // Load new parent location + $newParentLocation = $locationService->loadLocation($mediaLocationId); + + // Move location from "Demo Design" to "Home" + $locationService->moveSubtree( + $locationToMove, + $newParentLocation + ); + + // Load moved location + $movedLocation = $locationService->loadLocation($demoDesignLocationId); + + // Reload new parent location + $newParentLocation = $locationService->loadLocation($mediaLocationId); + /* END: Use Case */ + + $this->refreshSearch($repository); + + // Load Subtree properties after move + $actual = $this->loadLocationProperties($newParentLocation); + $childCountAfter = $locationService->getLocationChildCount($newParentLocation); + + $this->assertEquals($expected, $actual); + $this->assertEquals($childCountBefore + 1, $childCountAfter); + } + + /** + * Test for the moveSubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree() + * @depends testMoveSubtree + */ + public function testMoveSubtreeDecrementsChildCountOfOldParent() + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $oldParentLocation = $locationService->loadLocation($this->generateId('location', 1)); + + // Load expected properties before move + $expected = $this->loadLocationProperties($oldParentLocation); + $childCountBefore = $locationService->getLocationChildCount($oldParentLocation); + + $homeLocationId = $this->generateId('location', 2); + $mediaLocationId = $this->generateId('location', 43); + /* BEGIN: Use Case */ + // $homeLocationId is the ID of the "Home" page location in + // an Ibexa demo installation + + // $mediaLocationId is the ID of the "Media" page location in an Ibexa + // Publish demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + // Load location to move + $locationToMove = $locationService->loadLocation($mediaLocationId); + + // Get the location id of the old parent + $oldParentLocationId = $locationToMove->parentLocationId; + + // Load new parent location + $newParentLocation = $locationService->loadLocation($homeLocationId); + + // Move location from "Demo Design" to "Home" + $locationService->moveSubtree( + $locationToMove, + $newParentLocation + ); + + // Reload old parent location + $oldParentLocation = $locationService->loadLocation($oldParentLocationId); + /* END: Use Case */ + + $this->refreshSearch($repository); + + // Load Subtree properties after move + $actual = $this->loadLocationProperties($oldParentLocation); + $childCountAfter = $locationService->getLocationChildCount($oldParentLocation); + + $this->assertEquals($expected, $actual); + $this->assertEquals($childCountBefore - 1, $childCountAfter); + } + + /** + * Test moving invisible (hidden by parent) subtree. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testMoveInvisibleSubtree() + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $rootLocationId = 2; + + $folder = $this->createFolder(['eng-GB' => 'Folder'], $rootLocationId); + $child = $this->createFolder(['eng-GB' => 'Child'], $folder->contentInfo->mainLocationId); + $locationService->hideLocation( + $locationService->loadLocation($folder->contentInfo->mainLocationId) + ); + // sanity check + $childLocation = $locationService->loadLocation($child->contentInfo->mainLocationId); + self::assertFalse($childLocation->hidden); + self::assertTrue($childLocation->invisible); + self::assertEquals($folder->contentInfo->mainLocationId, $childLocation->parentLocationId); + + $destination = $this->createFolder(['eng-GB' => 'Destination'], $rootLocationId); + $destinationLocation = $locationService->loadLocation( + $destination->contentInfo->mainLocationId + ); + + $locationService->moveSubtree($childLocation, $destinationLocation); + + $childLocation = $locationService->loadLocation($child->contentInfo->mainLocationId); + // Business logic - Location moved to visible parent becomes visible + self::assertFalse($childLocation->hidden); + self::assertFalse($childLocation->invisible); + self::assertEquals($destinationLocation->id, $childLocation->parentLocationId); + } + + /** + * Test for the moveSubtree() method. + * + * @depends testMoveSubtree + */ + public function testMoveSubtreeThrowsInvalidArgumentException(): void + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + $mediaLocationId = $this->generateId('location', 43); + $multimediaLocationId = $this->generateId('location', 53); + + /* BEGIN: Use Case */ + // $mediaLocationId is the ID of the "Media" page location in + // an Ibexa demo installation + + // $multimediaLocationId is the ID of the "Multimedia" page location in an Ibexa + // Publish demo installation + + // Load the location service + $locationService = $repository->getLocationService(); + + // Load location to move + $locationToMove = $locationService->loadLocation($mediaLocationId); + + // Load new parent location + $newParentLocation = $locationService->loadLocation($multimediaLocationId); + + // Throws an exception because new parent location is placed below location to move + $this->expectException(InvalidArgumentException::class); + $locationService->moveSubtree( + $locationToMove, + $newParentLocation + ); + /* END: Use Case */ + } + + /** + * Test that Legacy ezcontentobject_tree.path_identification_string field is correctly updated + * after moving subtree. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree + * + * @throws \ErrorException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testMoveSubtreeUpdatesPathIdentificationString(): void + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $topNode = $this->createFolder(['eng-US' => 'top_node'], 2); + + $newParentLocation = $locationService->loadLocation( + $this + ->createFolder(['eng-US' => 'Parent'], $topNode->contentInfo->mainLocationId) + ->contentInfo + ->mainLocationId + ); + $location = $locationService->loadLocation( + $this + ->createFolder(['eng-US' => 'Move Me'], $topNode->contentInfo->mainLocationId) + ->contentInfo + ->mainLocationId + ); + + $locationService->moveSubtree($location, $newParentLocation); + + // path location string is not present on API level, so we need to query database + $serviceContainer = $this->getSetupFactory()->getServiceContainer(); + /** @var \Doctrine\DBAL\Connection $connection */ + $connection = $serviceContainer->get('ibexa.persistence.connection'); + $query = $connection->createQueryBuilder(); + $query + ->select('path_identification_string') + ->from('ezcontentobject_tree') + ->where('node_id = :nodeId') + ->setParameter('nodeId', $location->id); + + self::assertEquals( + 'top_node/parent/move_me', + $query->execute()->fetchColumn() + ); + } + + /** + * Test that is_visible is set correct for children when moving a location where a child is hidden by content (not by location). + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree + */ + public function testMoveSubtreeKeepsContentHiddenOnChildrenAndParent(): void + { + $repository = $this->getRepository(); + + $mediaLocationId = $this->generateId('location', 43); + + $locationService = $repository->getLocationService(); + $contentService = $repository->getContentService(); + + $sourceFolderContent = $this->publishContentWithParentLocation('SourceFolder', $mediaLocationId); // media/SourceFolder + $subFolderContent1 = $this->publishContentWithParentLocation('subFolderContent1', $sourceFolderContent->contentInfo->mainLocationId); + $subFolderContent2 = $this->publishContentWithParentLocation('subFolderContent2', $sourceFolderContent->contentInfo->mainLocationId); + $targetFolderContent = $this->publishContentWithParentLocation('targetFolder', $mediaLocationId); // media/TargetFolder + + $contentService->hideContent($subFolderContent1->contentInfo); + + // Move media/SourceFolder to media/TargetFolder/ + $locationService->moveSubtree( + $sourceFolderContent->contentInfo->getMainLocation(), + $targetFolderContent->contentInfo->getMainLocation() + ); + + $movedLocation = $locationService->loadLocation($sourceFolderContent->contentInfo->mainLocationId); + $newParentLocation = $locationService->loadLocation($targetFolderContent->contentInfo->mainLocationId); + + // Assert Moved Location remains visible ( only child is hidden ) + $this->assertPropertiesCorrect( + [ + 'hidden' => false, + 'invisible' => false, + 'depth' => $newParentLocation->depth + 1, + 'parentLocationId' => $newParentLocation->id, + 'pathString' => $newParentLocation->pathString . $this->parseId('location', $movedLocation->id) . '/', + ], + $movedLocation + ); + self::assertFalse($movedLocation->getContentInfo()->isHidden); + + // Assert children of Moved location + $childrenLocations = [ + $subFolderContent1->contentInfo->getMainLocation(), + $subFolderContent2->contentInfo->getMainLocation(), + ]; + foreach ($childrenLocations as $childLocation) { + $this->assertPropertiesCorrect( + [ + 'hidden' => $childLocation === $subFolderContent1->contentInfo->getMainLocation(), // Only SubFolderContent1 should be hidden, + 'invisible' => $childLocation === $subFolderContent1->contentInfo->getMainLocation(), // Only SubFolderContent1 should be hidden + 'depth' => $movedLocation->depth + 1, + 'parentLocationId' => $movedLocation->id, + 'pathString' => $movedLocation->pathString . $this->parseId('location', $childLocation->id) . '/', + ], + $childLocation + ); + self::assertEquals($childLocation === $subFolderContent1->contentInfo->getMainLocation(), $childLocation->getContentInfo()->isHidden); + } + } + + /** + * Test that is_visible is set correct for children when moving a content which is hidden (location is not hidden). + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree + */ + public function testMoveSubtreeKeepsContentHiddenOnChildren(): void + { + $repository = $this->getRepository(); + + $sourceLocationId = $this->createFolder( + [ + 'eng-GB' => 'SourceParentFolder', + ], + 2 + )->getVersionInfo()->getContentInfo()->mainLocationId; + + $locationService = $repository->getLocationService(); + $contentService = $repository->getContentService(); + + $sourceFolderContent = $this->publishContentWithParentLocation('SourceFolder', $sourceLocationId); // media/SourceFolder + $subFolderContent1 = $this->publishContentWithParentLocation('subFolderContent1', $sourceFolderContent->contentInfo->mainLocationId); + $subFolderContent2 = $this->publishContentWithParentLocation('subFolderContent2', $sourceFolderContent->contentInfo->mainLocationId); + $targetFolderContent = $this->publishContentWithParentLocation('targetFolder', $sourceLocationId); // media/TargetFolder + + $contentService->hideContent($sourceFolderContent->contentInfo); + + // Move media/SourceFolder to media/TargetFolder/ + $locationService->moveSubtree( + $sourceFolderContent->contentInfo->getMainLocation(), + $targetFolderContent->contentInfo->getMainLocation() + ); + + $movedLocation = $locationService->loadLocation($sourceFolderContent->contentInfo->mainLocationId); + $newParentLocation = $locationService->loadLocation($targetFolderContent->contentInfo->mainLocationId); + + // Assert Moved Location + $this->assertPropertiesCorrect( + [ + 'hidden' => true, + 'invisible' => true, + 'depth' => $newParentLocation->depth + 1, + 'parentLocationId' => $newParentLocation->id, + 'pathString' => $newParentLocation->pathString . $this->parseId('location', $movedLocation->id) . '/', + ], + $movedLocation + ); + + self::assertTrue($movedLocation->getContentInfo()->isHidden); + + // Assert children of Moved location + $childLocations = [$subFolderContent1->contentInfo->getMainLocation(), $subFolderContent2->contentInfo->getMainLocation()]; + foreach ($childLocations as $childLocation) { + $this->assertPropertiesCorrect( + [ + 'hidden' => false, + 'invisible' => true, + 'depth' => $movedLocation->depth + 1, + 'parentLocationId' => $movedLocation->id, + 'pathString' => $movedLocation->pathString . $this->parseId('location', $childLocation->id) . '/', + ], + $childLocation + ); + self::assertFalse($childLocation->getContentInfo()->isHidden); + } + } + + /** + * Test validating whether content that is being moved is still allowed to be moved when one of its locations + * is inaccessible by a current user, however, when moved location is accessible. + * + * @covers \Ibexa\Contracts\Core\Repository\LocationService::moveSubtree + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + public function testMoveSubtreeContentWithMultipleLocationsAndOneOfThemInaccessible(): void + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $permissionResolver = $repository->getPermissionResolver(); + + $folder = $this->publishContentWithParentLocation('Parent folder', 2); + $accessibleFolder = $this->publishContentWithParentLocation('Accessible folder', 2); + $subFolder = $this->publishContentWithParentLocation( + 'Sub folder', + $folder->contentInfo->mainLocationId + ); + $contentToBeMoved = $this->publishContentWithParentLocation( + 'Target folder', + $subFolder->contentInfo->mainLocationId + ); + $forbiddenContent = $this->publishContentWithParentLocation('Forbidden folder', 2); + + // Add second location (parent 'Forbidden folder') to 'Target content' in folder that user won't have access to + $locationService->createLocation( + $contentToBeMoved->contentInfo, + $locationService->newLocationCreateStruct($forbiddenContent->contentInfo->mainLocationId) + ); + + $folderLocation = $locationService->loadLocation($folder->contentInfo->mainLocationId); + $accessibleFolderLocation = $locationService->loadLocation($accessibleFolder->contentInfo->mainLocationId); + + // Set user that cannot access 'Forbidden folder' + $user = $this->createUserWithPolicies( + 'user', + [ + ['module' => 'content', 'function' => 'read'], + ['module' => 'content', 'function' => 'create'], + ], + new SubtreeLimitation( + [ + 'limitationValues' => [ + $folderLocation->getPathString(), + $accessibleFolderLocation->getPathString(), + ], + ] + ) + ); + $permissionResolver->setCurrentUserReference($user); + + // Move Parent folder/Sub folder/Target folder to location of ID = 2 + $locationService->moveSubtree( + $contentToBeMoved->contentInfo->getMainLocation(), + $accessibleFolderLocation + ); + + $targetContentMainLocation = $locationService->loadLocation($contentToBeMoved->contentInfo->mainLocationId); + + self::assertSame($targetContentMainLocation->parentLocationId, $accessibleFolderLocation->id); + } + + public function testGetSubtreeSize(): Location + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $folder = $this->createFolder(['eng-GB' => 'Parent Folder'], 2); + $location = $folder->getVersionInfo()->getContentInfo()->getMainLocation(); + self::assertSame(1, $locationService->getSubtreeSize($location)); + + $this->createFolder(['eng-GB' => 'Child 1'], $location->id); + $this->createFolder(['eng-GB' => 'Child 2'], $location->id); + + self::assertSame(3, $locationService->getSubtreeSize($location)); + + return $location; + } + + /** + * Loads properties from all locations in the $location's subtree. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param array $properties + * + * @return array + */ + private function loadSubtreeProperties(Location $location, array $properties = []) + { + $locationService = $this->getRepository()->getLocationService(); + + foreach ($locationService->loadLocationChildren($location)->locations as $childLocation) { + $properties[] = $this->loadLocationProperties($childLocation); + + $properties = $this->loadSubtreeProperties($childLocation, $properties); + } + + return $properties; + } + + /** + * Loads assertable properties from the given location. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param mixed[] $overwrite + * + * @return array + */ + private function loadLocationProperties(Location $location, array $overwrite = []) + { + return array_merge( + [ + 'id' => $location->id, + 'depth' => $location->depth, + 'parentLocationId' => $location->parentLocationId, + 'pathString' => $location->pathString, + 'remoteId' => $location->remoteId, + 'hidden' => $location->hidden, + 'invisible' => $location->invisible, + 'priority' => $location->priority, + 'sortField' => $location->sortField, + 'sortOrder' => $location->sortOrder, + ], + $overwrite + ); + } + + /** + * Assert generated aliases to expected alias return. + * + * @param \Ibexa\Contracts\Core\Repository\URLAliasService $urlAliasService + * @param array $expectedAliases + */ + protected function assertGeneratedAliases($urlAliasService, array $expectedAliases) + { + foreach ($expectedAliases as $expectedAlias) { + $urlAlias = $urlAliasService->lookup($expectedAlias); + $this->assertPropertiesCorrect(['type' => 0], $urlAlias); + } + } + + /** + * @param \Ibexa\Contracts\Core\Repository\URLAliasService $urlAliasService + * @param array $expectedSubItemAliases + */ + private function assertAliasesBeforeCopy($urlAliasService, array $expectedSubItemAliases) + { + foreach ($expectedSubItemAliases as $aliasUrl) { + try { + $urlAliasService->lookup($aliasUrl); + $this->fail('We didn\'t expect to find alias, but it was found'); + } catch (\Exception $e) { + $this->assertTrue(true); // OK - alias was not found + } + } + } + + /** + * Create and publish Content with the given parent Location. + * + * @param string $contentName + * @param int $parentLocationId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content published Content + */ + private function publishContentWithParentLocation($contentName, $parentLocationId) + { + $repository = $this->getRepository(false); + $locationService = $repository->getLocationService(); + + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + + $contentCreateStruct = $contentService->newContentCreateStruct( + $contentTypeService->loadContentTypeByIdentifier('folder'), + 'eng-US' + ); + $contentCreateStruct->setField('name', $contentName); + $contentDraft = $contentService->createContent( + $contentCreateStruct, + [ + $locationService->newLocationCreateStruct($parentLocationId), + ] + ); + + return $contentService->publishVersion($contentDraft->versionInfo); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function createForumStruct(string $name): ContentCreateStruct + { + $repository = $this->getRepository(false); + + $contentTypeForum = $repository->getContentTypeService() + ->loadContentTypeByIdentifier('forum'); + + $forum = $repository->getContentService() + ->newContentCreateStruct($contentTypeForum, 'eng-GB'); + + $forum->setField('name', $name); + + return $forum; + } + + private function assertAliasExists( + string $expectedAliasPath, + Location $location, + URLAliasServiceInterface $urlAliasService + ): void { + $articleAliasesBeforeDelete = $urlAliasService + ->listLocationAliases($location); + + $this->assertNotEmpty( + array_filter( + $articleAliasesBeforeDelete, + static function (URLAlias $alias) use ($expectedAliasPath) { + return $alias->path === $expectedAliasPath; + } + ) + ); + } +} + +class_alias(LocationServiceTest::class, 'eZ\Publish\API\Repository\Tests\LocationServiceTest'); diff --git a/tests/integration/Core/Repository/NonRedundantFieldSetTest.php b/tests/integration/Core/Repository/NonRedundantFieldSetTest.php new file mode 100644 index 0000000000..141ec36a26 --- /dev/null +++ b/tests/integration/Core/Repository/NonRedundantFieldSetTest.php @@ -0,0 +1,791 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; + +/** + * Test case for create and update Content operations in the ContentService with regard to + * non-redundant set of fields being passed to the storage. + * + * These tests depends on TextLine field type being functional. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService + * @group content + */ +class NonRedundantFieldSetTest extends BaseNonRedundantFieldSetTest +{ + /** + * Test for the createContent() method. + * + * Default values are stored. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function testCreateContentDefaultValues() + { + $mainLanguageCode = 'eng-US'; + $fieldValues = [ + 'field1' => ['eng-US' => 'new value 1'], + 'field3' => ['eng-US' => 'new value 3'], + ]; + + $content = $this->createTestContent($mainLanguageCode, $fieldValues); + + $this->assertInstanceOf(Content::class, $content); + + return $content; + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContentDefaultValues + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function testCreateContentDefaultValuesFields(Content $content) + { + $this->assertCount(1, $content->versionInfo->languageCodes); + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertCount(4, $content->getFields()); + + // eng-US + $this->assertEquals('new value 1', $content->getFieldValue('field1', 'eng-US')); + $this->assertEquals('default value 2', $content->getFieldValue('field2', 'eng-US')); + $this->assertEquals('new value 3', $content->getFieldValue('field3', 'eng-US')); + $this->assertEquals('default value 4', $content->getFieldValue('field4', 'eng-US')); + } + + /** + * Test for the createContent() method. + * + * Creating fields with empty values, no values being passed to storage. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function testCreateContentEmptyValues() + { + $mainLanguageCode = 'eng-US'; + $fieldValues = [ + 'field2' => ['eng-US' => null], + 'field4' => ['eng-US' => null], + ]; + + $content = $this->createTestContent($mainLanguageCode, $fieldValues); + + $this->assertInstanceOf(Content::class, $content); + + return $content; + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContentEmptyValues + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function testCreateContentEmptyValuesFields(Content $content) + { + $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); + + $this->assertCount(1, $content->versionInfo->languageCodes); + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertCount(4, $content->getFields()); + + // eng-US + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field4', 'eng-US')); + } + + /** + * Test for the createContent() method. + * + * Creating fields with empty values, no values being passed to storage. + * Case where additional language is not stored. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function testCreateContentEmptyValuesTranslationNotStored() + { + $mainLanguageCode = 'eng-US'; + $fieldValues = [ + 'field2' => ['eng-US' => null], + 'field4' => ['eng-US' => null, 'ger-DE' => null], + ]; + + $content = $this->createTestContent($mainLanguageCode, $fieldValues); + + $this->assertInstanceOf(Content::class, $content); + + return $content; + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContentEmptyValuesTranslationNotStored + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function testCreateContentEmptyValuesTranslationNotStoredFields(Content $content) + { + $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); + + $this->assertCount(1, $content->versionInfo->languageCodes); + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertCount(4, $content->getFields()); + + // eng-US + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field4', 'eng-US')); + + // ger-DE is not stored! + $this->assertNotContains('ger-DE', $content->versionInfo->languageCodes); + } + + /** + * Test for the createContent() method. + * + * Creating with two languages, main language is always stored (even with all values being empty). + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function testCreateContentTwoLanguagesMainTranslationStored() + { + $mainLanguageCode = 'eng-US'; + $fieldValues = [ + 'field2' => ['eng-US' => null], + 'field4' => ['eng-US' => null, 'ger-DE' => 'new ger-DE value 4'], + ]; + + $content = $this->createTestContent($mainLanguageCode, $fieldValues); + + $this->assertInstanceOf(Content::class, $content); + + return $content; + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContentTwoLanguagesMainTranslationStored + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function testCreateContentTwoLanguagesMainTranslationStoredFields(Content $content) + { + $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); + + $this->assertCount(2, $content->versionInfo->languageCodes); + $this->assertContains('ger-DE', $content->versionInfo->languageCodes); + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertCount(8, $content->getFields()); + + // eng-US + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field4', 'eng-US')); + + // ger-DE + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'ger-DE')); + $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'ger-DE')); + $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'ger-DE')); + $this->assertEquals('new ger-DE value 4', $content->getFieldValue('field4', 'ger-DE')); + } + + /** + * Test for the createContent() method. + * + * Creating with two languages, second (not main one) language with empty values, causing no fields + * for it being passed to the storage. Second language will not be stored. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function testCreateContentTwoLanguagesSecondTranslationNotStored() + { + $mainLanguageCode = 'eng-US'; + $fieldValues = [ + 'field4' => ['ger-DE' => null], + ]; + + $content = $this->createTestContent($mainLanguageCode, $fieldValues); + + $this->assertInstanceOf(Content::class, $content); + + return $content; + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContentTwoLanguagesSecondTranslationNotStored + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function testCreateContentTwoLanguagesSecondTranslationNotStoredFields(Content $content) + { + $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); + + $this->assertCount(1, $content->versionInfo->languageCodes); + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertCount(4, $content->getFields()); + + // eng-US + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); + $this->assertEquals('default value 2', $content->getFieldValue('field2', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'eng-US')); + $this->assertEquals('default value 4', $content->getFieldValue('field4', 'eng-US')); + + // ger-DE is not stored! + $this->assertNotContains('ger-DE', $content->versionInfo->languageCodes); + } + + /** + * Test for the createContent() method. + * + * Creating with no fields in struct, using only default values. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function testCreateContentDefaultValuesNoStructFields() + { + $mainLanguageCode = 'eng-US'; + $fieldValues = []; + + $content = $this->createTestContent($mainLanguageCode, $fieldValues); + + $this->assertInstanceOf(Content::class, $content); + + return $content; + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContentDefaultValuesNoStructFields + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function testCreateContentDefaultValuesNoStructFieldsFields(Content $content) + { + $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); + + $this->assertCount(1, $content->versionInfo->languageCodes); + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertCount(4, $content->getFields()); + + // eng-US + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); + $this->assertEquals('default value 2', $content->getFieldValue('field2', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'eng-US')); + $this->assertEquals('default value 4', $content->getFieldValue('field4', 'eng-US')); + } + + /** + * Test for the createContent() method. + * + * Creating in two languages with no given field values for main language. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function testCreateContentTwoLanguagesNoValuesForMainLanguage() + { + $mainLanguageCode = 'eng-US'; + $fieldValues = [ + 'field4' => ['ger-DE' => 'new value 4'], + ]; + + $content = $this->createTestContent($mainLanguageCode, $fieldValues); + + $this->assertInstanceOf(Content::class, $content); + + return $content; + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends testCreateContentTwoLanguagesNoValuesForMainLanguage + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function testCreateContentTwoLanguagesNoValuesForMainLanguageFields(Content $content) + { + $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); + + $this->assertCount(2, $content->versionInfo->languageCodes); + $this->assertContains('ger-DE', $content->versionInfo->languageCodes); + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertCount(8, $content->getFields()); + + // eng-US + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); + $this->assertEquals('default value 2', $content->getFieldValue('field2', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'eng-US')); + $this->assertEquals('default value 4', $content->getFieldValue('field4', 'eng-US')); + + // ger-DE + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'ger-DE')); + $this->assertEquals('default value 2', $content->getFieldValue('field2', 'ger-DE')); + $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'ger-DE')); + $this->assertEquals('new value 4', $content->getFieldValue('field4', 'ger-DE')); + } + + /** + * Test for the createContentDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft() + * @depends testCreateContentTwoLanguagesMainTranslationStoredFields + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content[] + */ + public function testCreateContentDraft() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $draft = $this->createMultilingualTestContent(); + $published = $contentService->publishVersion($draft->versionInfo); + $newDraft = $contentService->createContentDraft($published->contentInfo); + + $newDraft = $contentService->loadContent($newDraft->id, null, $newDraft->versionInfo->versionNo); + + return [$published, $newDraft]; + } + + /** + * Test for the createContentDraft() method. + * + * @depends testCreateContentDraft + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content[] $data + */ + public function testCreateContentDraftFields(array $data) + { + $content = $data[1]; + + $this->assertEquals(VersionInfo::STATUS_DRAFT, $content->versionInfo->status); + $this->assertEquals(2, $content->versionInfo->versionNo); + $this->assertCount(2, $content->versionInfo->languageCodes); + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertContains('eng-GB', $content->versionInfo->languageCodes); + $this->assertCount(8, $content->getFields()); + + // eng-US + $this->assertEquals('value 1', $content->getFieldValue('field1', 'eng-US')); + $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-US')); + $this->assertEquals('value 3', $content->getFieldValue('field3', 'eng-US')); + $this->assertEquals('value 4', $content->getFieldValue('field4', 'eng-US')); + + // eng-GB + $this->assertEquals('value 1', $content->getFieldValue('field1', 'eng-GB')); + $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-GB')); + $this->assertEquals('value 3 eng-GB', $content->getFieldValue('field3', 'eng-GB')); + $this->assertEquals('value 4 eng-GB', $content->getFieldValue('field4', 'eng-GB')); + } + + /** + * Test for the createContentDraft() method. + * + * @depends testCreateContentDraft + * @depends testCreateContentDraftFields + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content[] $data + */ + public function testCreateContentDraftFieldsRetainsIds(array $data) + { + $this->assertFieldIds($data[0], $data[1]); + } + + /** + * Test for the updateContent() method. + * + * Testing update with new language: + * - value for new language is copied from value in main language + * - value for new language is empty + * - value for new language is not empty + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function testUpdateContentWithNewLanguage() + { + $initialLanguageCode = 'ger-DE'; + $fieldValues = [ + 'field4' => ['ger-DE' => 'new value 4'], + ]; + + $content = $this->updateTestContent($initialLanguageCode, $fieldValues); + $this->assertInstanceOf(Content::class, $content); + + return $content; + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContentWithNewLanguage + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function testUpdateContentWithNewLanguageFields(Content $content) + { + $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); + + $this->assertCount(3, $content->versionInfo->languageCodes); + $this->assertContains('ger-DE', $content->versionInfo->languageCodes); + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertContains('eng-GB', $content->versionInfo->languageCodes); + $this->assertCount(12, $content->getFields()); + + // eng-US + $this->assertEquals('value 1', $content->getFieldValue('field1', 'eng-US')); + $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-US')); + $this->assertEquals('value 3', $content->getFieldValue('field3', 'eng-US')); + $this->assertEquals('value 4', $content->getFieldValue('field4', 'eng-US')); + + // eng-GB + $this->assertEquals('value 1', $content->getFieldValue('field1', 'eng-GB')); + $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-GB')); + $this->assertEquals('value 3 eng-GB', $content->getFieldValue('field3', 'eng-GB')); + $this->assertEquals('value 4 eng-GB', $content->getFieldValue('field4', 'eng-GB')); + + // ger-DE + $this->assertEquals('value 1', $content->getFieldValue('field1', 'ger-DE')); + $this->assertEquals('value 2', $content->getFieldValue('field2', 'ger-DE')); + $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'ger-DE')); + $this->assertEquals('new value 4', $content->getFieldValue('field4', 'ger-DE')); + } + + /** + * Test for the updateContent() method. + * + * Testing update of existing language and adding a new language: + * - value for new language is copied from value in main language + * - value for new language is empty + * - value for new language is not empty + * - existing language value updated with empty value + * - existing language value not changed + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function testUpdateContentWithNewLanguageVariant() + { + $initialLanguageCode = 'ger-DE'; + $fieldValues = [ + 'field1' => ['eng-US' => null], + 'field4' => ['ger-DE' => 'new value 4'], + ]; + + $content = $this->updateTestContent($initialLanguageCode, $fieldValues); + $this->assertInstanceOf(Content::class, $content); + + return $content; + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContentWithNewLanguageVariant + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function testUpdateContentWithNewLanguageVariantFields(Content $content) + { + $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); + + $this->assertCount(3, $content->versionInfo->languageCodes); + $this->assertContains('ger-DE', $content->versionInfo->languageCodes); + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertContains('eng-GB', $content->versionInfo->languageCodes); + $this->assertCount(12, $content->getFields()); + + // eng-US + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); + $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-US')); + $this->assertEquals('value 3', $content->getFieldValue('field3', 'eng-US')); + $this->assertEquals('value 4', $content->getFieldValue('field4', 'eng-US')); + + // eng-GB + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-GB')); + $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-GB')); + $this->assertEquals('value 3 eng-GB', $content->getFieldValue('field3', 'eng-GB')); + $this->assertEquals('value 4 eng-GB', $content->getFieldValue('field4', 'eng-GB')); + + // ger-DE + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'ger-DE')); + $this->assertEquals('value 2', $content->getFieldValue('field2', 'ger-DE')); + $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'ger-DE')); + $this->assertEquals('new value 4', $content->getFieldValue('field4', 'ger-DE')); + } + + /** + * Test for the updateContent() method. + * + * Updating with with new language and no field values given in the update struct. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function testUpdateContentWithNewLanguageNoValues() + { + $initialLanguageCode = 'ger-DE'; + $fieldValues = []; + + $content = $this->updateTestContent($initialLanguageCode, $fieldValues); + $this->assertInstanceOf(Content::class, $content); + + return $content; + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContentWithNewLanguageNoValues + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function testUpdateContentWithNewLanguageNoValuesFields(Content $content) + { + $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); + + $this->assertCount(3, $content->versionInfo->languageCodes); + $this->assertContains('ger-DE', $content->versionInfo->languageCodes); + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertContains('eng-GB', $content->versionInfo->languageCodes); + $this->assertCount(12, $content->getFields()); + + // eng-US + $this->assertEquals('value 1', $content->getFieldValue('field1', 'eng-US')); + $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-US')); + $this->assertEquals('value 3', $content->getFieldValue('field3', 'eng-US')); + $this->assertEquals('value 4', $content->getFieldValue('field4', 'eng-US')); + + // eng-GB + $this->assertEquals('value 1', $content->getFieldValue('field1', 'eng-GB')); + $this->assertEquals('value 2', $content->getFieldValue('field2', 'eng-GB')); + $this->assertEquals('value 3 eng-GB', $content->getFieldValue('field3', 'eng-GB')); + $this->assertEquals('value 4 eng-GB', $content->getFieldValue('field4', 'eng-GB')); + + // ger-DE + $this->assertEquals('value 1', $content->getFieldValue('field1', 'ger-DE')); + $this->assertEquals('value 2', $content->getFieldValue('field2', 'ger-DE')); + $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'ger-DE')); + $this->assertEquals('default value 4', $content->getFieldValue('field4', 'ger-DE')); + } + + /** + * Test for the updateContent() method. + * + * When updating Content with two languages, updating non-translatable field will also update it's value + * for non-main language. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function testUpdateContentUpdatingNonTranslatableFieldUpdatesFieldCopy() + { + $initialLanguageCode = 'eng-US'; + $fieldValues = [ + 'field1' => ['eng-US' => 'new value 1'], + 'field2' => ['eng-US' => null], + ]; + + $content = $this->updateTestContent($initialLanguageCode, $fieldValues); + $this->assertInstanceOf(Content::class, $content); + + return $content; + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContentUpdatingNonTranslatableFieldUpdatesFieldCopy + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function testUpdateContentUpdatingNonTranslatableFieldUpdatesFieldCopyFields(Content $content) + { + $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); + + $this->assertCount(2, $content->versionInfo->languageCodes); + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertContains('eng-GB', $content->versionInfo->languageCodes); + $this->assertCount(8, $content->getFields()); + + // eng-US + $this->assertEquals('new value 1', $content->getFieldValue('field1', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-US')); + $this->assertEquals('value 3', $content->getFieldValue('field3', 'eng-US')); + $this->assertEquals('value 4', $content->getFieldValue('field4', 'eng-US')); + + // eng-GB + $this->assertEquals('new value 1', $content->getFieldValue('field1', 'eng-GB')); + $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-GB')); + $this->assertEquals('value 3 eng-GB', $content->getFieldValue('field3', 'eng-GB')); + $this->assertEquals('value 4 eng-GB', $content->getFieldValue('field4', 'eng-GB')); + } + + /** + * Test for the updateContent() method. + * + * Updating with two languages, initial language is always stored (even with all values being empty). + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testCreateContent + * @depends Ibexa\Tests\Integration\Core\Repository\ContentTypeServiceTest::testCreateContentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + public function testUpdateContentWithTwoLanguagesInitialLanguageTranslationNotCreated() + { + $initialLanguageCode = 'ger-DE'; + $fieldValues = [ + 'field1' => ['eng-US' => null], + 'field2' => ['eng-US' => null], + 'field4' => ['ger-DE' => null], + ]; + + $content = $this->updateTestContent($initialLanguageCode, $fieldValues); + $this->assertInstanceOf(Content::class, $content); + + return $content; + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent() + * @depends testUpdateContentWithTwoLanguagesInitialLanguageTranslationNotCreated + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + public function testUpdateContentWithTwoLanguagesInitialLanguageTranslationNotCreatedFields(Content $content) + { + $emptyValue = $this->getRepository()->getFieldTypeService()->getFieldType('ezstring')->getEmptyValue(); + + $this->assertCount(3, $content->versionInfo->languageCodes); + $this->assertContains('ger-DE', $content->versionInfo->languageCodes); + $this->assertContains('eng-US', $content->versionInfo->languageCodes); + $this->assertContains('eng-GB', $content->versionInfo->languageCodes); + $this->assertCount(12, $content->getFields()); + + // eng-US + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-US')); + $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-US')); + $this->assertEquals('value 3', $content->getFieldValue('field3', 'eng-US')); + $this->assertEquals('value 4', $content->getFieldValue('field4', 'eng-US')); + + // eng-GB + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'eng-GB')); + $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'eng-GB')); + $this->assertEquals('value 3 eng-GB', $content->getFieldValue('field3', 'eng-GB')); + $this->assertEquals('value 4 eng-GB', $content->getFieldValue('field4', 'eng-GB')); + + // ger-DE + $this->assertEquals($emptyValue, $content->getFieldValue('field1', 'ger-DE')); + $this->assertEquals($emptyValue, $content->getFieldValue('field2', 'ger-DE')); + $this->assertEquals($emptyValue, $content->getFieldValue('field3', 'ger-DE')); + $this->assertEquals($emptyValue, $content->getFieldValue('field4', 'ger-DE')); + } + + protected function assertFieldIds(Content $content1, Content $content2) + { + $fields1 = $this->mapFields($content1->getFields()); + $fields2 = $this->mapFields($content2->getFields()); + + foreach ($fields1 as $fieldDefinitionIdentifier => $languageFieldIds) { + foreach ($languageFieldIds as $languageCode => $fieldId) { + $this->assertEquals( + $fields2[$fieldDefinitionIdentifier][$languageCode], + $fieldId + ); + } + } + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $fields + * + * @return array + */ + protected function mapFields(array $fields) + { + $mappedFields = []; + + foreach ($fields as $field) { + $mappedFields[$field->fieldDefIdentifier][$field->languageCode] = $field->id; + } + + return $mappedFields; + } +} + +class_alias(NonRedundantFieldSetTest::class, 'eZ\Publish\API\Repository\Tests\NonRedundantFieldSetTest'); diff --git a/tests/integration/Core/Repository/NotificationServiceTest.php b/tests/integration/Core/Repository/NotificationServiceTest.php new file mode 100644 index 0000000000..7b13a4c6ff --- /dev/null +++ b/tests/integration/Core/Repository/NotificationServiceTest.php @@ -0,0 +1,204 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Notification\CreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Notification\Notification; +use Ibexa\Contracts\Core\Repository\Values\Notification\NotificationList; + +/** + * Test case for the NotificationService. + * + * @covers \Ibexa\Contracts\Core\Repository\NotificationService + */ +class NotificationServiceTest extends BaseTest +{ + /** + * @covers \Ibexa\Contracts\Core\Repository\NotificationService::loadNotifications() + */ + public function testLoadNotifications() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $notificationService = $repository->getNotificationService(); + $notificationList = $notificationService->loadNotifications(0, 25); + /* END: Use Case */ + + $this->assertInstanceOf(NotificationList::class, $notificationList); + $this->assertIsArray($notificationList->items); + $this->assertIsInt($notificationList->totalCount); + $this->assertEquals(5, $notificationList->totalCount); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\NotificationService::getNotification() + */ + public function testGetNotification() + { + $repository = $this->getRepository(); + + $notificationId = $this->generateId('notification', 5); + + /* BEGIN: Use Case */ + $notificationService = $repository->getNotificationService(); + // $notificationId is the ID of an existing notification + $notification = $notificationService->getNotification($notificationId); + /* END: Use Case */ + + $this->assertInstanceOf(Notification::class, $notification); + $this->assertEquals($notificationId, $notification->id); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\NotificationService::markNotificationAsRead() + */ + public function testMarkNotificationAsRead() + { + $repository = $this->getRepository(); + + $notificationId = $this->generateId('notification', 5); + /* BEGIN: Use Case */ + $notificationService = $repository->getNotificationService(); + + $notification = $notificationService->getNotification($notificationId); + $notificationService->markNotificationAsRead($notification); + $notification = $notificationService->getNotification($notificationId); + /* END: Use Case */ + + $this->assertFalse($notification->isPending); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\NotificationService::getPendingNotificationCount() + */ + public function testGetPendingNotificationCount() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $notificationService = $repository->getNotificationService(); + $notificationPendingCount = $notificationService->getPendingNotificationCount(); + /* END: Use Case */ + + $this->assertEquals(3, $notificationPendingCount); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\NotificationService::getNotificationCount() + */ + public function testGetNotificationCount() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $notificationService = $repository->getNotificationService(); + $notificationCount = $notificationService->getNotificationCount(); + /* END: Use Case */ + + $this->assertEquals(5, $notificationCount); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\NotificationService::deleteNotification() + */ + public function testDeleteNotification() + { + $repository = $this->getRepository(); + + $notificationId = $this->generateId('notification', 5); + /* BEGIN: Use Case */ + $notificationService = $repository->getNotificationService(); + $notification = $notificationService->getNotification($notificationId); + $notificationService->deleteNotification($notification); + /* END: Use Case */ + + try { + $notificationService->getNotification($notificationId); + $this->fail('Notification ' . $notificationId . ' not deleted.'); + } catch (NotFoundException $e) { + } + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\NotificationService::createNotification() + */ + public function testCreateNotification() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $notificationService = $repository->getNotificationService(); + $user = $repository->getUserService()->loadUser(14); + + $createStruct = new CreateStruct([ + 'ownerId' => $user->id, + 'type' => 'TEST', + 'data' => [ + 'foo' => 'Foo', + 'bar' => 'Bar', + 'baz' => 'Baz', + ], + ]); + + $notification = $notificationService->createNotification($createStruct); + /* END: Use Case */ + + $this->assertInstanceOf(Notification::class, $notification); + $this->assertGreaterThan(0, $notification->id); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\NotificationService::createNotification() + * @depends testCreateNotification + */ + public function testCreateNotificationThrowsInvalidArgumentExceptionOnMissingOwner() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $notificationService = $repository->getNotificationService(); + + $createStruct = new CreateStruct([ + 'type' => 'TEST', + ]); + + // This call will fail because notification owner is not specified + $notificationService->createNotification($createStruct); + /* END: Use Case */ + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\NotificationService::createNotification() + * @depends testCreateNotification + */ + public function testCreateNotificationThrowsInvalidArgumentExceptionOnMissingType() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $notificationService = $repository->getNotificationService(); + + $createStruct = new CreateStruct([ + 'ownerId' => 14, + ]); + + // This call will fail because notification type is not specified + $notificationService->createNotification($createStruct); + /* END: Use Case */ + } +} + +class_alias(NotificationServiceTest::class, 'eZ\Publish\API\Repository\Tests\NotificationServiceTest'); diff --git a/eZ/Publish/API/Repository/Tests/ObjectStateServiceAuthorizationTest.php b/tests/integration/Core/Repository/ObjectStateServiceAuthorizationTest.php similarity index 83% rename from eZ/Publish/API/Repository/Tests/ObjectStateServiceAuthorizationTest.php rename to tests/integration/Core/Repository/ObjectStateServiceAuthorizationTest.php index 71281f0f35..1a7ade9c56 100644 --- a/eZ/Publish/API/Repository/Tests/ObjectStateServiceAuthorizationTest.php +++ b/tests/integration/Core/Repository/ObjectStateServiceAuthorizationTest.php @@ -4,12 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; /** * Test case for operations in the ObjectStateService using in memory storage. * - * @see eZ\Publish\API\Repository\ObjectStateService + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService * @group integration * @group authorization */ @@ -18,19 +20,19 @@ class ObjectStateServiceAuthorizationTest extends BaseTest /** * Test for the createObjectStateGroup() method. * - * @see \eZ\Publish\API\Repository\ObjectStateService::createObjectStateGroup() - * @depends eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testCreateObjectStateGroup + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::createObjectStateGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\ObjectStateServiceTest::testCreateObjectStateGroup */ public function testCreateObjectStateGroupThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. // Set anonymous user $userService = $repository->getUserService(); @@ -62,12 +64,12 @@ public function testCreateObjectStateGroupThrowsUnauthorizedException() /** * Test for the updateObjectStateGroup() method. * - * @see \eZ\Publish\API\Repository\ObjectStateService::updateObjectStateGroup() - * @depends eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testUpdateObjectStateGroup + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::updateObjectStateGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\ObjectStateServiceTest::testUpdateObjectStateGroup */ public function testUpdateObjectStateGroupThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -75,7 +77,7 @@ public function testUpdateObjectStateGroupThrowsUnauthorizedException() $objectStateGroupId = $this->generateId('objectstategroup', 2); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. // Set anonymous user $userService = $repository->getUserService(); @@ -111,12 +113,12 @@ public function testUpdateObjectStateGroupThrowsUnauthorizedException() /** * Test for the deleteObjectStateGroup() method. * - * @see \eZ\Publish\API\Repository\ObjectStateService::deleteObjectStateGroup() - * @depends eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testDeleteObjectStateGroup + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::deleteObjectStateGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\ObjectStateServiceTest::testDeleteObjectStateGroup */ public function testDeleteObjectStateGroupThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -124,7 +126,7 @@ public function testDeleteObjectStateGroupThrowsUnauthorizedException() $objectStateGroupId = $this->generateId('objectstategroup', 2); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. // Set anonymous user $userService = $repository->getUserService(); @@ -147,12 +149,12 @@ public function testDeleteObjectStateGroupThrowsUnauthorizedException() /** * Test for the createObjectState() method. * - * @see \eZ\Publish\API\Repository\ObjectStateService::createObjectState() - * @depends eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testCreateObjectState + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::createObjectState() + * @depends Ibexa\Tests\Integration\Core\Repository\ObjectStateServiceTest::testCreateObjectState */ public function testCreateObjectStateThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -160,7 +162,7 @@ public function testCreateObjectStateThrowsUnauthorizedException() $objectStateGroupId = $this->generateId('objectstategroup', 2); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. // Set anonymous user $userService = $repository->getUserService(); @@ -198,12 +200,12 @@ public function testCreateObjectStateThrowsUnauthorizedException() /** * Test for the updateObjectState() method. * - * @see \eZ\Publish\API\Repository\ObjectStateService::updateObjectState() - * @depends eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testUpdateObjectState + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::updateObjectState() + * @depends Ibexa\Tests\Integration\Core\Repository\ObjectStateServiceTest::testUpdateObjectState */ public function testUpdateObjectStateThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -211,7 +213,7 @@ public function testUpdateObjectStateThrowsUnauthorizedException() $objectStateId = $this->generateId('objectstate', 2); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. // Set anonymous user $userService = $repository->getUserService(); @@ -248,12 +250,12 @@ public function testUpdateObjectStateThrowsUnauthorizedException() /** * Test for the setPriorityOfObjectState() method. * - * @see \eZ\Publish\API\Repository\ObjectStateService::setPriorityOfObjectState() - * @depends eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testSetPriorityOfObjectState + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::setPriorityOfObjectState() + * @depends Ibexa\Tests\Integration\Core\Repository\ObjectStateServiceTest::testSetPriorityOfObjectState */ public function testSetPriorityOfObjectStateThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -261,7 +263,7 @@ public function testSetPriorityOfObjectStateThrowsUnauthorizedException() $objectStateId = $this->generateId('objectstate', 2); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. // Set anonymous user $userService = $repository->getUserService(); @@ -286,12 +288,12 @@ public function testSetPriorityOfObjectStateThrowsUnauthorizedException() /** * Test for the deleteObjectState() method. * - * @see \eZ\Publish\API\Repository\ObjectStateService::deleteObjectState() - * @depends eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testDeleteObjectState + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::deleteObjectState() + * @depends Ibexa\Tests\Integration\Core\Repository\ObjectStateServiceTest::testDeleteObjectState */ public function testDeleteObjectStateThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -300,7 +302,7 @@ public function testDeleteObjectStateThrowsUnauthorizedException() $lockedObjectStateId = $this->generateId('objectstate', 2); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. // Set anonymous user $userService = $repository->getUserService(); @@ -320,12 +322,12 @@ public function testDeleteObjectStateThrowsUnauthorizedException() /** * Test for the setContentState() method. * - * @see \eZ\Publish\API\Repository\ObjectStateService::setContentState() - * @depends eZ\Publish\API\Repository\Tests\ObjectStateServiceTest::testSetContentState + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::setContentState() + * @depends Ibexa\Tests\Integration\Core\Repository\ObjectStateServiceTest::testSetContentState */ public function testSetContentStateThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -334,7 +336,7 @@ public function testSetContentStateThrowsUnauthorizedException() $ezLockObjectStateGroupId = $this->generateId('objectstategroup', 2); $lockedObjectStateId = $this->generateId('objectstate', 2); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. // Set anonymous user $userService = $repository->getUserService(); @@ -364,3 +366,5 @@ public function testSetContentStateThrowsUnauthorizedException() /* END: Use Case */ } } + +class_alias(ObjectStateServiceAuthorizationTest::class, 'eZ\Publish\API\Repository\Tests\ObjectStateServiceAuthorizationTest'); diff --git a/tests/integration/Core/Repository/ObjectStateServiceTest.php b/tests/integration/Core/Repository/ObjectStateServiceTest.php new file mode 100644 index 0000000000..b67b08d23c --- /dev/null +++ b/tests/integration/Core/Repository/ObjectStateServiceTest.php @@ -0,0 +1,1907 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateUpdateStruct; + +/** + * Test case for operations in the ObjectStateService using in memory storage. + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService + * @group object-state + */ +class ObjectStateServiceTest extends BaseTest +{ + private const EXISTING_OBJECT_STATE_GROUP_IDENTIFIER = 'ez_lock'; + private const EXISTING_OBJECT_STATE_IDENTIFIER = 'locked'; + + private const NON_EXISTING_OBJECT_STATE_GROUP_IDENTIFIER = 'non-existing'; + private const NON_EXISTING_OBJECT_STATE_IDENTIFIER = 'non-existing'; + + /** + * Test for the newObjectStateGroupCreateStruct() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::newObjectStateGroupCreateStruct() + */ + public function testNewObjectStateGroupCreateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $objectStateService = $repository->getObjectStateService(); + + $objectStateGroupCreate = $objectStateService->newObjectStateGroupCreateStruct( + 'publishing' + ); + /* END: Use Case */ + + $this->assertInstanceOf( + ObjectStateGroupCreateStruct::class, + $objectStateGroupCreate + ); + + return $objectStateGroupCreate; + } + + /** + * testNewObjectStateGroupCreateStructValues. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupCreateStruct $objectStateGroupCreate + * + * + * @depends testNewObjectStateGroupCreateStruct + */ + public function testNewObjectStateGroupCreateStructValues(ObjectStateGroupCreateStruct $objectStateGroupCreate) + { + $this->assertPropertiesCorrect( + [ + 'identifier' => 'publishing', + 'defaultLanguageCode' => null, + 'names' => null, + 'descriptions' => null, + ], + $objectStateGroupCreate + ); + } + + /** + * Test for the newObjectStateGroupUpdateStruct() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::newObjectStateGroupUpdateStruct() + */ + public function testNewObjectStateGroupUpdateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $objectStateService = $repository->getObjectStateService(); + + $objectStateGroupUpdate = $objectStateService->newObjectStateGroupUpdateStruct(); + /* END: Use Case */ + + $this->assertInstanceOf( + ObjectStateGroupUpdateStruct::class, + $objectStateGroupUpdate + ); + + return $objectStateGroupUpdate; + } + + /** + * testNewObjectStateGroupUpdateStructValues. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct $objectStateGroupUpdate + * + * + * @depends testNewObjectStateGroupUpdateStruct + */ + public function testNewObjectStateGroupUpdateStructValues(ObjectStateGroupUpdateStruct $objectStateGroupUpdate) + { + $this->assertPropertiesCorrect( + [ + 'identifier' => null, + 'defaultLanguageCode' => null, + 'names' => null, + 'descriptions' => null, + ], + $objectStateGroupUpdate + ); + } + + /** + * Test for the newObjectStateCreateStruct() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::newObjectStateCreateStruct() + */ + public function testNewObjectStateCreateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $objectStateService = $repository->getObjectStateService(); + + $objectStateCreate = $objectStateService->newObjectStateCreateStruct( + 'pending' + ); + /* END: Use Case */ + + $this->assertInstanceOf( + ObjectStateCreateStruct::class, + $objectStateCreate + ); + + return $objectStateCreate; + } + + /** + * testNewObjectStateCreateStructValues. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateCreateStruct $objectStateCreate + * + * + * @depends testNewObjectStateCreateStruct + */ + public function testNewObjectStateCreateStructValues(ObjectStateCreateStruct $objectStateCreate) + { + $this->assertPropertiesCorrect( + [ + 'identifier' => 'pending', + 'priority' => false, + 'defaultLanguageCode' => null, + 'names' => null, + 'descriptions' => null, + ], + $objectStateCreate + ); + } + + /** + * Test for the newObjectStateUpdateStruct() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::newObjectStateUpdateStruct() + */ + public function testNewObjectStateUpdateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $objectStateService = $repository->getObjectStateService(); + + $objectStateUpdate = $objectStateService->newObjectStateUpdateStruct(); + /* END: Use Case */ + + $this->assertInstanceOf( + ObjectStateUpdateStruct::class, + $objectStateUpdate + ); + + return $objectStateUpdate; + } + + /** + * testNewObjectStateUpdateStructValues. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateUpdateStruct $objectStateUpdate + * + * + * @depends testNewObjectStateUpdateStruct + */ + public function testNewObjectStateUpdateStructValues(ObjectStateUpdateStruct $objectStateUpdate) + { + $this->assertPropertiesCorrect( + [ + 'identifier' => null, + 'defaultLanguageCode' => null, + 'names' => null, + 'descriptions' => null, + ], + $objectStateUpdate + ); + } + + /** + * Test for the createObjectStateGroup() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::createObjectStateGroup() + * @depends testNewObjectStateGroupCreateStructValues + */ + public function testCreateObjectStateGroup() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $objectStateService = $repository->getObjectStateService(); + + $objectStateGroupCreate = $objectStateService->newObjectStateGroupCreateStruct( + 'publishing' + ); + $objectStateGroupCreate->defaultLanguageCode = 'eng-US'; + $objectStateGroupCreate->names = [ + 'eng-US' => 'Publishing', + 'ger-DE' => 'Sindelfingen', + ]; + $objectStateGroupCreate->descriptions = [ + 'eng-US' => 'Put something online', + 'ger-DE' => 'Put something ton Sindelfingen.', + ]; + + $createdObjectStateGroup = $objectStateService->createObjectStateGroup( + $objectStateGroupCreate + ); + /* END: Use Case */ + + $this->assertInstanceOf( + ObjectStateGroup::class, + $createdObjectStateGroup + ); + + return $createdObjectStateGroup; + } + + /** + * testCreateObjectStateGroupStructValues. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup $createdObjectStateGroup + * + * + * @depends testCreateObjectStateGroup + */ + public function testCreateObjectStateGroupStructValues(ObjectStateGroup $createdObjectStateGroup) + { + $this->assertPropertiesCorrect( + [ + 'identifier' => 'publishing', + 'mainLanguageCode' => 'eng-US', + 'languageCodes' => ['eng-US', 'ger-DE'], + 'names' => [ + 'eng-US' => 'Publishing', + 'ger-DE' => 'Sindelfingen', + ], + 'descriptions' => [ + 'eng-US' => 'Put something online', + 'ger-DE' => 'Put something ton Sindelfingen.', + ], + ], + $createdObjectStateGroup + ); + $this->assertNotNull($createdObjectStateGroup->id); + } + + /** + * Test for the createObjectStateGroup() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::createObjectStateGroup() + * @depends testCreateObjectStateGroup + */ + public function testCreateObjectStateGroupThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $objectStateService = $repository->getObjectStateService(); + + $objectStateGroupCreate = $objectStateService->newObjectStateGroupCreateStruct( + // 'ez_lock' is already existing identifier + 'ez_lock' + ); + $objectStateGroupCreate->defaultLanguageCode = 'eng-US'; + $objectStateGroupCreate->names = [ + 'eng-US' => 'Publishing', + 'eng-GB' => 'Sindelfingen', + ]; + $objectStateGroupCreate->descriptions = [ + 'eng-US' => 'Put something online', + 'eng-GB' => 'Put something ton Sindelfingen.', + ]; + + // This call will fail because group with 'ez_lock' identifier already exists + $objectStateService->createObjectStateGroup( + $objectStateGroupCreate + ); + } + + /** + * Test for the loadObjectStateGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::loadObjectStateGroup + */ + public function testLoadObjectStateGroup() + { + $repository = $this->getRepository(); + + $objectStateGroupId = $this->generateId('objectstategroup', 2); + /* BEGIN: Use Case */ + // $objectStateGroupId contains the ID of the standard object state + // group ez_lock. + $objectStateService = $repository->getObjectStateService(); + + $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( + $objectStateGroupId + ); + /* END: Use Case */ + + $this->assertInstanceOf( + ObjectStateGroup::class, + $loadedObjectStateGroup + ); + + $this->assertPropertiesCorrect( + [ + 'id' => 2, + 'identifier' => 'ez_lock', + 'mainLanguageCode' => 'eng-US', + 'languageCodes' => ['eng-US'], + 'names' => ['eng-US' => 'Lock'], + 'descriptions' => ['eng-US' => ''], + ], + $loadedObjectStateGroup + ); + } + + /** + * Test for the loadObjectStateGroup() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::loadObjectStateGroup() + * @depends testLoadObjectStateGroup + */ + public function testLoadObjectStateGroupThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $nonExistentObjectStateGroupId = $this->generateId('objectstategroup', self::DB_INT_MAX); + /* BEGIN: Use Case */ + // $nonExistentObjectStateGroupId contains an ID for an object state + // that does not exist + $objectStateService = $repository->getObjectStateService(); + + // Throws a not found exception + $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( + $nonExistentObjectStateGroupId + ); + /* END: Use Case */ + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::loadGroupByIdentifier + */ + public function testLoadObjectStateGroupByIdentifier(): void + { + $repository = $this->getRepository(); + + $objectStateService = $repository->getObjectStateService(); + + $loadedObjectStateGroup = $objectStateService->loadObjectStateGroupByIdentifier( + self::EXISTING_OBJECT_STATE_GROUP_IDENTIFIER + ); + + $this->assertInstanceOf( + ObjectStateGroup::class, + $loadedObjectStateGroup + ); + + $this->assertPropertiesCorrect( + [ + 'id' => 2, + 'identifier' => 'ez_lock', + 'mainLanguageCode' => 'eng-US', + 'languageCodes' => ['eng-US'], + 'names' => ['eng-US' => 'Lock'], + 'descriptions' => ['eng-US' => ''], + ], + $loadedObjectStateGroup + ); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::loadGroupByIdentifier + */ + public function testLoadObjectStateGroupByIdentifierThrowsNotFoundException(): void + { + $repository = $this->getRepository(); + + $objectStateService = $repository->getObjectStateService(); + + $this->expectException(NotFoundException::class); + $this->expectExceptionMessage(sprintf( + "Could not find 'ObjectStateGroup' with identifier '%s'", + self::NON_EXISTING_OBJECT_STATE_IDENTIFIER + )); + + $objectStateService->loadObjectStateGroupByIdentifier( + self::NON_EXISTING_OBJECT_STATE_GROUP_IDENTIFIER + ); + } + + /** + * Test for the loadObjectStateGroups() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::loadObjectStateGroups() + * @depends testLoadObjectStateGroup + */ + public function testLoadObjectStateGroups() + { + $repository = $this->getRepository(); + + $expectedGroupIdentifiers = $this->getGroupIdentifierMap($this->createObjectStateGroups()); + $expectedGroupIdentifiers['ez_lock'] = true; + + /* BEGIN: Use Case */ + $objectStateService = $repository->getObjectStateService(); + + $loadedObjectStateGroups = $objectStateService->loadObjectStateGroups(); + /* END: Use Case */ + + $this->assertIsArray($loadedObjectStateGroups); + + $this->assertObjectsLoadedByIdentifiers( + $expectedGroupIdentifiers, + $loadedObjectStateGroups, + 'ObjectStateGroup' + ); + } + + /** + * Creates a set of object state groups and returns an array of all + * existing group identifiers after creation. + * + * @return bool[] + */ + protected function createObjectStateGroups() + { + $repository = $this->getRepository(); + $objectStateService = $repository->getObjectStateService(); + + $identifiersToCreate = [ + 'first', + 'second', + 'third', + ]; + + $createdStateGroups = []; + + $groupCreateStruct = $objectStateService->newObjectStateGroupCreateStruct('dummy'); + + $groupCreateStruct->defaultLanguageCode = 'eng-US'; + $groupCreateStruct->names = [ + 'eng-US' => 'Foo', + 'ger-DE' => 'GerFoo', + ]; + $groupCreateStruct->descriptions = [ + 'eng-US' => 'Foo Bar', + 'ger-DE' => 'GerBar', + ]; + + foreach ($identifiersToCreate as $identifier) { + $groupCreateStruct->identifier = $identifier; + $createdStateGroups[] = $objectStateService->createObjectStateGroup($groupCreateStruct); + } + + return $createdStateGroups; + } + + /** + * Assert object identifiers. + * + * @param array $expectedIdentifiers + * @param array $loadedObjects + * @param string $class + */ + protected function assertObjectsLoadedByIdentifiers(array $expectedIdentifiers, array $loadedObjects, $class) + { + foreach ($loadedObjects as $loadedObject) { + if (!isset($expectedIdentifiers[$loadedObject->identifier])) { + $this->fail( + sprintf( + 'Loaded unexpected %s with identifier "%s"', + $class, + $loadedObject->identifier + ) + ); + } + unset($expectedIdentifiers[$loadedObject->identifier]); + } + + if (!empty($expectedIdentifiers)) { + $this->fail( + sprintf( + 'Expected %ss with identifiers "%s" not loaded.', + $class, + implode('", "', $expectedIdentifiers) + ) + ); + } + } + + /** + * Test for the loadObjectStateGroups() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::loadObjectStateGroups($offset) + * @depends testLoadObjectStateGroups + */ + public function testLoadObjectStateGroupsWithOffset() + { + $repository = $this->getRepository(); + $objectStateService = $repository->getObjectStateService(); + + $this->createObjectStateGroups(); + + $allObjectStateGroups = $objectStateService->loadObjectStateGroups(); + + $existingGroupIdentifiers = $this->getGroupIdentifierMap($allObjectStateGroups); + + /* BEGIN: Use Case */ + $objectStateService = $repository->getObjectStateService(); + + $loadedObjectStateGroups = $objectStateService->loadObjectStateGroups(2); + /* END: Use Case */ + + $this->assertIsArray($loadedObjectStateGroups); + + $this->assertObjectsLoadedByIdentifiers( + array_slice($existingGroupIdentifiers, 2), + $loadedObjectStateGroups, + 'ObjectStateGroup' + ); + } + + /** + * Returns a map of the given object state groups. + * + * @param array $groups + * + * @return array + */ + protected function getGroupIdentifierMap(array $groups) + { + $existingGroupIdentifiers = array_map( + static function ($group) { + return $group->identifier; + }, + $groups + ); + + return array_fill_keys($existingGroupIdentifiers, true); + } + + /** + * Test for the loadObjectStateGroups() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::loadObjectStateGroups($offset, $limit) + * @depends testLoadObjectStateGroupsWithOffset + */ + public function testLoadObjectStateGroupsWithOffsetAndLimit() + { + $repository = $this->getRepository(); + $objectStateService = $repository->getObjectStateService(); + + $allObjectStateGroups = $objectStateService->loadObjectStateGroups(); + + $existingGroupIdentifiers = $this->getGroupIdentifierMap($allObjectStateGroups); + + /* BEGIN: Use Case */ + $objectStateService = $repository->getObjectStateService(); + + $loadedObjectStateGroups = $objectStateService->loadObjectStateGroups(1, 2); + /* END: Use Case */ + + $this->assertIsArray($loadedObjectStateGroups); + + $this->assertObjectsLoadedByIdentifiers( + array_slice($existingGroupIdentifiers, 1, 2), + $loadedObjectStateGroups, + 'ObjectStateGroup' + ); + } + + /** + * Test for the loadObjectStates() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::loadObjectStates() + * @depends testLoadObjectStateGroup + */ + public function testLoadObjectStates() + { + $repository = $this->getRepository(); + + $objectStateGroupId = $this->generateId('objectstategroup', 2); + /* BEGIN: Use Case */ + // $objectStateGroupId contains the ID of the standard object state + // group ez_lock. + $objectStateService = $repository->getObjectStateService(); + + $objectStateGroup = $objectStateService->loadObjectStateGroup( + $objectStateGroupId + ); + + // Loads all object states in $objectStateGroup + $loadedObjectStates = $objectStateService->loadObjectStates($objectStateGroup); + /* END: Use Case */ + + $this->assertIsArray( + $loadedObjectStates + ); + $this->assertObjectsLoadedByIdentifiers( + ['not_locked' => true, 'locked' => true], + $loadedObjectStates, + 'ObjectState' + ); + } + + /** + * Test for the updateObjectStateGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::updateObjectStateGroup + * @depends testLoadObjectStateGroup + */ + public function testUpdateObjectStateGroup() + { + $repository = $this->getRepository(); + + $objectStateGroupId = $this->generateId('objectstategroup', 2); + /* BEGIN: Use Case */ + // $objectStateGroupId contains the ID of the standard object state + // group ez_lock. + $objectStateService = $repository->getObjectStateService(); + + $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( + $objectStateGroupId + ); + + // pre populate any kind of cache for all + $objectStateService->loadObjectStateGroups(); + + $groupUpdateStruct = $objectStateService->newObjectStateGroupUpdateStruct(); + $groupUpdateStruct->identifier = 'sindelfingen'; + $groupUpdateStruct->defaultLanguageCode = 'ger-DE'; + $groupUpdateStruct->names = [ + 'ger-DE' => 'Sindelfingen', + ]; + $groupUpdateStruct->descriptions = [ + 'ger-DE' => 'Sindelfingen ist nicht nur eine Stadt', + ]; + + // Updates the $loadObjectStateGroup with the data from + // $groupUpdateStruct and returns the updated group + $updatedObjectStateGroup = $objectStateService->updateObjectStateGroup( + $loadedObjectStateGroup, + $groupUpdateStruct + ); + + $allObjectGroups = $objectStateService->loadObjectStateGroups(); + /* END: Use Case */ + + $this->assertInstanceOf( + ObjectStateGroup::class, + $updatedObjectStateGroup + ); + + return [ + $loadedObjectStateGroup, + $groupUpdateStruct, + $updatedObjectStateGroup, + $allObjectGroups, + ]; + } + + /** + * Test service method for partially updating object state group. + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::updateObjectStateGroup + * @depends testLoadObjectStateGroup + */ + public function testUpdateObjectStateGroupChosenFieldsOnly() + { + $repository = $this->getRepository(); + $objectStateService = $repository->getObjectStateService(); + + $groupUpdateStruct = $objectStateService->newObjectStateGroupUpdateStruct(); + $groupUpdateStruct->defaultLanguageCode = 'eng-GB'; + $groupUpdateStruct->names = ['eng-GB' => 'Test']; + + $group = $objectStateService->loadObjectStateGroup(2); + + $updatedGroup = $objectStateService->updateObjectStateGroup($group, $groupUpdateStruct); + + $this->assertInstanceOf( + ObjectStateGroup::class, + $updatedGroup + ); + + $this->assertPropertiesCorrect( + [ + 'id' => 2, + 'identifier' => 'ez_lock', + 'mainLanguageCode' => 'eng-GB', + 'languageCodes' => ['eng-GB'], + 'names' => ['eng-GB' => 'Test'], + // descriptions array should have an empty value for eng-GB + // without the original descriptions + // since the descriptions were not in the update struct and we're changing default language + 'descriptions' => ['eng-GB' => ''], + ], + $updatedGroup + ); + } + + /** + * Test for the updateObjectStateGroup() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::updateObjectStateGroup() + * @depends testUpdateObjectStateGroup + */ + public function testUpdateObjectStateGroupThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $objectStateService = $repository->getObjectStateService(); + + // Create object state group which we will later update + $objectStateGroupCreate = $objectStateService->newObjectStateGroupCreateStruct( + 'publishing' + ); + $objectStateGroupCreate->defaultLanguageCode = 'eng-US'; + $objectStateGroupCreate->names = [ + 'eng-US' => 'Publishing', + 'eng-GB' => 'Sindelfingen', + ]; + $objectStateGroupCreate->descriptions = [ + 'eng-US' => 'Put something online', + 'eng-GB' => 'Put something ton Sindelfingen.', + ]; + + $createdObjectStateGroup = $objectStateService->createObjectStateGroup( + $objectStateGroupCreate + ); + + $groupUpdateStruct = $objectStateService->newObjectStateGroupUpdateStruct(); + // 'ez_lock' is the identifier of already existing group + $groupUpdateStruct->identifier = 'ez_lock'; + $groupUpdateStruct->defaultLanguageCode = 'ger-DE'; + $groupUpdateStruct->names = [ + 'ger-DE' => 'Sindelfingen', + ]; + $groupUpdateStruct->descriptions = [ + 'ger-DE' => 'Sindelfingen ist nicht nur eine Stadt', + ]; + + // This call will fail since state group with 'ez_lock' identifier already exists + $objectStateService->updateObjectStateGroup( + $createdObjectStateGroup, + $groupUpdateStruct + ); + } + + /** + * testUpdateObjectStateGroupStructValues. + * + * @param array $testData + * + * + * @depends testUpdateObjectStateGroup + */ + public function testUpdateObjectStateGroupStructValues(array $testData) + { + list( + $loadedObjectStateGroup, + $groupUpdateStruct, + $updatedObjectStateGroup, + $allObjectGroups + ) = $testData; + + $this->assertStructPropertiesCorrect( + $groupUpdateStruct, + $updatedObjectStateGroup + ); + + $this->assertContainsEquals($updatedObjectStateGroup, $allObjectGroups, ''); + $this->assertNotContainsEquals($loadedObjectStateGroup, $allObjectGroups, ''); + } + + /** + * Test for the createObjectState() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::createObjectState() + * @depends testLoadObjectStateGroup + * @depends testNewObjectStateCreateStruct + */ + public function testCreateObjectState() + { + $repository = $this->getRepository(); + + $objectStateGroupId = $this->generateId('objectstategroup', 2); + /* BEGIN: Use Case */ + // $objectStateGroupId contains the ID of the standard object state + // group ez_lock. + $objectStateService = $repository->getObjectStateService(); + + $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( + $objectStateGroupId, + Language::ALL + ); + + $objectStateCreateStruct = $objectStateService->newObjectStateCreateStruct( + 'locked_and_unlocked' + ); + $objectStateCreateStruct->priority = 23; + $objectStateCreateStruct->defaultLanguageCode = 'eng-US'; + $objectStateCreateStruct->names = [ + 'eng-US' => 'Locked and Unlocked', + 'ger-DE' => 'geschlossen und ungeschlossen', + ]; + $objectStateCreateStruct->descriptions = [ + 'eng-US' => 'A state between locked and unlocked.', + 'ger-DE' => 'ein Zustand zwischen geschlossen und ungeschlossen.', + ]; + + // Creates a new object state in the $loadObjectStateGroup with the + // data from $objectStateCreateStruct + $createdObjectState = $objectStateService->createObjectState( + $loadedObjectStateGroup, + $objectStateCreateStruct + ); + /* END: Use Case */ + + $this->assertInstanceOf(ObjectState::class, $createdObjectState); + // Object sequences are renumbered + $objectStateCreateStruct->priority = 2; + + return [ + $loadedObjectStateGroup, + $objectStateCreateStruct, + $createdObjectState, + ]; + } + + /** + * Test service method for creating object state in empty group. + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::createObjectState + */ + public function testCreateObjectStateInEmptyGroup() + { + $repository = $this->getRepository(); + $objectStateService = $repository->getObjectStateService(); + + $groupCreateStruct = $objectStateService->newObjectStateGroupCreateStruct('test'); + $groupCreateStruct->defaultLanguageCode = 'eng-GB'; + $groupCreateStruct->names = ['eng-GB' => 'Test']; + $groupCreateStruct->descriptions = ['eng-GB' => 'Test description']; + + $createdGroup = $objectStateService->createObjectStateGroup($groupCreateStruct); + + $stateCreateStruct = $objectStateService->newObjectStateCreateStruct('test'); + $stateCreateStruct->priority = 2; + $stateCreateStruct->defaultLanguageCode = 'eng-GB'; + $stateCreateStruct->names = ['eng-GB' => 'Test']; + $stateCreateStruct->descriptions = ['eng-GB' => 'Test description']; + + $createdState = $objectStateService->createObjectState( + $createdGroup, + $stateCreateStruct + ); + + $this->assertInstanceOf( + ObjectState::class, + $createdState + ); + + $this->assertNotNull($createdState->id); + $this->assertPropertiesCorrect( + [ + 'identifier' => 'test', + 'priority' => 0, + 'mainLanguageCode' => 'eng-GB', + 'languageCodes' => ['eng-GB'], + 'names' => ['eng-GB' => 'Test'], + 'descriptions' => ['eng-GB' => 'Test description'], + ], + $createdState + ); + + $objectStateGroup = $createdState->getObjectStateGroup(); + $this->assertInstanceOf( + ObjectStateGroup::class, + $objectStateGroup + ); + + $this->assertEquals($createdGroup->id, $objectStateGroup->id); + $this->assertGreaterThan(0, $objectStateService->getContentCount($createdState)); + } + + /** + * Test for the createObjectState() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::createObjectState() + * @depends testLoadObjectStateGroup + * @depends testCreateObjectState + */ + public function testCreateObjectStateThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $objectStateGroupId = $this->generateId('objectstategroup', 2); + // $objectStateGroupId contains the ID of the standard object state + // group ez_lock. + $objectStateService = $repository->getObjectStateService(); + + $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( + $objectStateGroupId + ); + + $objectStateCreateStruct = $objectStateService->newObjectStateCreateStruct( + // 'not_locked' is the identifier of already existing state + 'not_locked' + ); + $objectStateCreateStruct->priority = 23; + $objectStateCreateStruct->defaultLanguageCode = 'eng-US'; + $objectStateCreateStruct->names = [ + 'eng-US' => 'Locked and Unlocked', + ]; + $objectStateCreateStruct->descriptions = [ + 'eng-US' => 'A state between locked and unlocked.', + ]; + + // This call will fail because object state with + // 'not_locked' identifier already exists + $objectStateService->createObjectState( + $loadedObjectStateGroup, + $objectStateCreateStruct + ); + } + + /** + * testCreateObjectStateStructValues. + * + * @param array $testData + * + * + * @depends testCreateObjectState + */ + public function testCreateObjectStateStructValues(array $testData) + { + list( + $loadedObjectStateGroup, + $objectStateCreateStruct, + $createdObjectState + ) = $testData; + + $this->assertStructPropertiesCorrect( + $objectStateCreateStruct, + $createdObjectState + ); + + $this->assertNotNull($createdObjectState->id); + + $this->assertEquals( + $loadedObjectStateGroup, + $createdObjectState->getObjectStateGroup() + ); + } + + /** + * Test for the loadObjectState() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::loadObjectState() + * @depends testLoadObjectStateGroup + */ + public function testLoadObjectState() + { + $repository = $this->getRepository(); + + $objectStateId = $this->generateId('objectstate', 2); + /* BEGIN: Use Case */ + // $objectStateId contains the ID of the "locked" state + $objectStateService = $repository->getObjectStateService(); + + $loadedObjectState = $objectStateService->loadObjectState( + $objectStateId + ); + /* END: Use Case */ + + $this->assertInstanceOf( + ObjectState::class, + $loadedObjectState + ); + + return $loadedObjectState; + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::loadObjectStateByIdentifier + */ + public function testLoadObjectStateByIdentifier(): void + { + $repository = $this->getRepository(); + + $objectStateService = $repository->getObjectStateService(); + + $objectStateGroup = $objectStateService->loadObjectStateGroupByIdentifier( + self::EXISTING_OBJECT_STATE_GROUP_IDENTIFIER + ); + + $loadedObjectState = $objectStateService->loadObjectStateByIdentifier( + $objectStateGroup, + self::EXISTING_OBJECT_STATE_IDENTIFIER + ); + + $this->assertInstanceOf( + ObjectState::class, + $loadedObjectState + ); + + $this->assertPropertiesCorrect( + [ + 'id' => 2, + 'identifier' => self::EXISTING_OBJECT_STATE_IDENTIFIER, + 'priority' => 1, + 'languageCodes' => ['eng-US'], + ], + $loadedObjectState + ); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::loadObjectStateByIdentifier + */ + public function testLoadObjectStateByIdentifierThrowsNotFoundException(): void + { + $repository = $this->getRepository(); + + $objectStateService = $repository->getObjectStateService(); + + $loadedObjectStateGroup = $objectStateService->loadObjectStateGroupByIdentifier( + self::EXISTING_OBJECT_STATE_GROUP_IDENTIFIER + ); + + $this->expectException(NotFoundException::class); + $this->expectExceptionMessage(sprintf( + "Could not find 'ObjectState' with identifier '%s'", + var_export([ + 'identifier' => self::NON_EXISTING_OBJECT_STATE_IDENTIFIER, + 'groupId' => $loadedObjectStateGroup->id, + ], true) + )); + + $objectStateService->loadObjectStateByIdentifier( + $loadedObjectStateGroup, + self::NON_EXISTING_OBJECT_STATE_IDENTIFIER + ); + } + + /** + * testLoadObjectStateStructValues. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState $loadedObjectState + * + * + * @depends testLoadObjectState + */ + public function testLoadObjectStateStructValues(ObjectState $loadedObjectState) + { + $this->assertPropertiesCorrect( + [ + 'id' => 2, + 'identifier' => 'locked', + 'priority' => 1, + 'mainLanguageCode' => 'eng-US', + 'languageCodes' => [0 => 'eng-US'], + 'prioritizedLanguages' => [ + 0 => 'eng-US', + 1 => 'eng-GB', + 2 => 'ger-DE', + ], + 'names' => ['eng-US' => 'Locked'], + 'descriptions' => ['eng-US' => ''], + ], + $loadedObjectState + ); + + $this->assertEquals( + $this->getRepository()->getObjectStateService()->loadObjectStateGroup(2), + $loadedObjectState->getObjectStateGroup() + ); + } + + /** + * Test for the loadObjectState() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::loadObjectState() + * @depends testLoadObjectState + */ + public function testLoadObjectStateThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $nonExistingObjectStateId = $this->generateId('objectstate', self::DB_INT_MAX); + /* BEGIN: Use Case */ + // $nonExistingObjectStateId contains the ID of a non existing state + $objectStateService = $repository->getObjectStateService(); + + // Throws not found exception + $loadedObjectState = $objectStateService->loadObjectState( + $nonExistingObjectStateId + ); + /* END: Use Case */ + } + + /** + * Data provider for PrioritizedLanguageList tests. + * + * @return array + */ + public function getPrioritizedLanguagesList() + { + return [ + [[], null], + [['eng-GB'], null], + [['eng-US'], 'eng-US'], + [['ger-DE'], 'ger-DE'], + [['eng-US', 'ger-DE'], 'eng-US'], + [['ger-DE', 'eng-US'], 'ger-DE'], + [['eng-GB', 'ger-DE', 'eng-US'], 'ger-DE'], + ]; + } + + /** + * Test that multi-language logic for loadObjectStateGroups respects prioritized language list. + * + * @dataProvider getPrioritizedLanguagesList + * + * @param string[] $prioritizedLanguages + * @param string|null $expectedLanguageCode + */ + public function testLoadObjectStateGroupsWithPrioritizedLanguagesList( + array $prioritizedLanguages, + $expectedLanguageCode + ) { + // cleanup before the actual test + $this->deleteExistingObjectStateGroups(); + + $repository = $this->getRepository(false); + $objectStateService = $repository->getObjectStateService(); + + $this->createObjectStateGroups(); + + $objectStateGroups = $objectStateService->loadObjectStateGroups( + 0, + -1, + $prioritizedLanguages + ); + + foreach ($objectStateGroups as $objectStateGroup) { + $languageCode = $expectedLanguageCode === null ? $objectStateGroup->defaultLanguageCode : $expectedLanguageCode; + + self::assertEquals( + $objectStateGroup->getName($languageCode), + $objectStateGroup->getName() + ); + + self::assertEquals( + $objectStateGroup->getDescription($languageCode), + $objectStateGroup->getDescription() + ); + } + } + + /** + * Test that multi-language logic for loadObjectStateGroup respects prioritized language list. + * + * @dataProvider getPrioritizedLanguagesList + * + * @param string[] $prioritizedLanguages + * @param string|null $expectedLanguageCode + */ + public function testLoadObjectStateGroupWithPrioritizedLanguagesList( + array $prioritizedLanguages, + $expectedLanguageCode + ) { + $repository = $this->getRepository(); + $objectStateService = $repository->getObjectStateService(); + + $objectStateGroup = $this->testCreateObjectStateGroup(); + $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( + $objectStateGroup->id, + $prioritizedLanguages + ); + + if ($expectedLanguageCode === null) { + $expectedLanguageCode = $loadedObjectStateGroup->defaultLanguageCode; + } + + self::assertEquals( + $loadedObjectStateGroup->getName($expectedLanguageCode), + $loadedObjectStateGroup->getName() + ); + + self::assertEquals( + $loadedObjectStateGroup->getDescription($expectedLanguageCode), + $loadedObjectStateGroup->getDescription() + ); + } + + /** + * Test that multi-language logic for loadObjectState respects prioritized language list. + * + * @dataProvider getPrioritizedLanguagesList + * + * @param string[] $prioritizedLanguages + * @param string|null $expectedLanguageCode + */ + public function testLoadObjectStateWithPrioritizedLanguagesList( + array $prioritizedLanguages, + $expectedLanguageCode + ) { + $repository = $this->getRepository(); + $objectStateService = $repository->getObjectStateService(); + + $objectStateData = $this->testCreateObjectState(); + /** @see \Ibexa\Tests\Integration\Core\Repository\ObjectStateServiceTest::testCreateObjectState */ + $objectState = $objectStateData[2]; + /** @var \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState $objectState */ + $loadedObjectState = $objectStateService->loadObjectState($objectState->id, $prioritizedLanguages); + + if ($expectedLanguageCode === null) { + $expectedLanguageCode = $objectState->defaultLanguageCode; + } + + self::assertEquals( + $loadedObjectState->getName($expectedLanguageCode), + $loadedObjectState->getName() + ); + + self::assertEquals( + $loadedObjectState->getDescription($expectedLanguageCode), + $loadedObjectState->getDescription() + ); + } + + /** + * Test that multi-language logic for loadObjectStates respects prioritized language list. + * + * @dataProvider getPrioritizedLanguagesList + * + * @param string[] $languageCodes + * @param string|null $expectedLanguageCode + */ + public function testLoadObjectStatesWithPrioritizedLanguagesList($languageCodes, $expectedLanguageCode) + { + $repository = $this->getRepository(); + $objectStateService = $repository->getObjectStateService(); + + $objectStateGroup = $this->testCreateObjectStateGroup(); + $this->createObjectState( + $objectStateGroup, + 'state_1', + [ + 'eng-US' => 'One', + 'ger-DE' => 'ein', + ], + [ + 'eng-US' => 'State one', + 'ger-DE' => 'ein Zustand', + ] + ); + $this->createObjectState( + $objectStateGroup, + 'state_2', + [ + 'eng-US' => 'Two', + 'ger-DE' => 'zwei', + ], + [ + 'eng-US' => 'State two', + 'ger-DE' => 'zwei Zustand', + ] + ); + + // Loads all object states in $objectStateGroup + $loadedObjectStates = $objectStateService->loadObjectStates($objectStateGroup, $languageCodes); + + foreach ($loadedObjectStates as $objectState) { + self::assertEquals( + $objectState->getName($expectedLanguageCode), + $objectState->getName() + ); + + self::assertEquals( + $objectState->getDescription($expectedLanguageCode), + $objectState->getDescription() + ); + } + } + + /** + * Test for the updateObjectState() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::updateObjectState + * @depends testLoadObjectState + */ + public function testUpdateObjectState() + { + $repository = $this->getRepository(); + + $objectStateId = $this->generateId('objectstate', 2); + /* BEGIN: Use Case */ + // $objectStateId contains the ID of the "locked" state + $objectStateService = $repository->getObjectStateService(); + + $loadedObjectState = $objectStateService->loadObjectState( + $objectStateId + ); + + // pre load any possile cache loading all + $objectStateService->loadObjectStates($loadedObjectState->getObjectStateGroup()); + + $updateStateStruct = $objectStateService->newObjectStateUpdateStruct(); + $updateStateStruct->identifier = 'somehow_locked'; + $updateStateStruct->defaultLanguageCode = 'ger-DE'; + $updateStateStruct->names = [ + 'eng-US' => 'Somehow locked', + 'ger-DE' => 'Irgendwie gelockt', + ]; + $updateStateStruct->descriptions = [ + 'eng-US' => 'The object is somehow locked', + 'ger-DE' => 'Sindelfingen', + ]; + + $updatedObjectState = $objectStateService->updateObjectState( + $loadedObjectState, + $updateStateStruct + ); + + $allObjectStates = $objectStateService->loadObjectStates($loadedObjectState->getObjectStateGroup()); + /* END: Use Case */ + + $this->assertInstanceOf( + ObjectState::class, + $updatedObjectState + ); + + return [ + $loadedObjectState, + $updateStateStruct, + $updatedObjectState, + $allObjectStates, + ]; + } + + /** + * Test service method for partially updating object state. + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::updateObjectState + * @depends testLoadObjectState + */ + public function testUpdateObjectStateChosenFieldsOnly() + { + $repository = $this->getRepository(); + $objectStateService = $repository->getObjectStateService(); + + $stateUpdateStruct = $objectStateService->newObjectStateUpdateStruct(); + $stateUpdateStruct->identifier = 'test'; + $stateUpdateStruct->names = ['eng-US' => 'Test']; + + $state = $objectStateService->loadObjectState(1); + + $updatedState = $objectStateService->updateObjectState($state, $stateUpdateStruct); + + $this->assertInstanceOf( + ObjectState::class, + $updatedState + ); + + $this->assertPropertiesCorrect( + [ + 'id' => 1, + 'identifier' => 'test', + 'priority' => 0, + 'mainLanguageCode' => 'eng-US', + 'languageCodes' => ['eng-US'], + 'names' => ['eng-US' => 'Test'], + // Original value of empty description for eng-US should be kept + 'descriptions' => ['eng-US' => ''], + ], + $updatedState + ); + + $this->assertInstanceOf( + ObjectStateGroup::class, + $updatedState->getObjectStateGroup() + ); + + $this->assertEquals($state->getObjectStateGroup()->id, $updatedState->getObjectStateGroup()->id); + } + + /** + * Test for the updateObjectState() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::updateObjectState() + * @depends testUpdateObjectState + */ + public function testUpdateObjectStateThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $objectStateId = $this->generateId('objectstate', 2); + // $objectStateId contains the ID of the "locked" state + $objectStateService = $repository->getObjectStateService(); + + $loadedObjectState = $objectStateService->loadObjectState( + $objectStateId + ); + + $updateStateStruct = $objectStateService->newObjectStateUpdateStruct(); + // 'not_locked' is the identifier of already existing state + $updateStateStruct->identifier = 'not_locked'; + $updateStateStruct->defaultLanguageCode = 'ger-DE'; + $updateStateStruct->names = [ + 'eng-US' => 'Somehow locked', + 'ger-DE' => 'Irgendwie gelockt', + ]; + $updateStateStruct->descriptions = [ + 'eng-US' => 'The object is somehow locked', + 'ger-DE' => 'Sindelfingen', + ]; + + // This call will fail because state with + // 'not_locked' identifier already exists + $objectStateService->updateObjectState( + $loadedObjectState, + $updateStateStruct + ); + } + + /** + * testUpdateObjectStateStructValues. + * + * @param array $testData + * + * + * @depends testUpdateObjectState + */ + public function testUpdateObjectStateStructValues(array $testData) + { + list( + $loadedObjectState, + $updateStateStruct, + $updatedObjectState, + $allObjectStates + ) = $testData; + + $this->assertPropertiesCorrect( + [ + 'id' => $loadedObjectState->id, + 'identifier' => $updateStateStruct->identifier, + 'priority' => $loadedObjectState->priority, + 'mainLanguageCode' => $updateStateStruct->defaultLanguageCode, + 'languageCodes' => ['eng-US', 'ger-DE'], + 'names' => $updateStateStruct->names, + 'descriptions' => $updateStateStruct->descriptions, + ], + $updatedObjectState + ); + + $this->assertEquals( + $loadedObjectState->getObjectStateGroup(), + $updatedObjectState->getObjectStateGroup() + ); + + $this->assertContainsEquals($updatedObjectState, $allObjectStates, ''); + $this->assertNotContainsEquals($loadedObjectState, $allObjectStates, ''); + } + + /** + * Test for the setPriorityOfObjectState() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::setPriorityOfObjectState + * @depends testLoadObjectState + */ + public function testSetPriorityOfObjectState() + { + $repository = $this->getRepository(); + + $objectStateId = $this->generateId('objectstate', 1); + /* BEGIN: Use Case */ + // $objectStateId contains the ID of the "not_locked" state + $objectStateService = $repository->getObjectStateService(); + + $initiallyLoadedObjectState = $objectStateService->loadObjectState( + $objectStateId + ); + + // Sets the given priority on $initiallyLoadedObjectState + $objectStateService->setPriorityOfObjectState( + $initiallyLoadedObjectState, + 23 + ); + // $loadObjectState now has the priority 1, since object state + // priorities are always made sequential + $loadedObjectState = $objectStateService->loadObjectState( + $objectStateId + ); + /* END: Use Case */ + + $this->assertInstanceOf( + ObjectState::class, + $loadedObjectState + ); + $this->assertEquals(1, $loadedObjectState->priority); + } + + /** + * Test for the getContentState() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::getContentState() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentInfo + * @depends testLoadObjectState + */ + public function testGetContentState() + { + $repository = $this->getRepository(); + + $anonymousUserId = $this->generateId('user', 10); + $ezLockObjectStateGroupId = $this->generateId('objectstategroup', 2); + /* BEGIN: Use Case */ + // $anonymousUserId is the content ID of "Anonymous User" + $contentService = $repository->getContentService(); + $objectStateService = $repository->getObjectStateService(); + + $contentInfo = $contentService->loadContentInfo($anonymousUserId); + + $ezLockObjectStateGroup = $objectStateService->loadObjectStateGroup( + $ezLockObjectStateGroupId + ); + + // Loads the state of $contentInfo in the "ez_lock" object state group + $ezLockObjectState = $objectStateService->getContentState( + $contentInfo, + $ezLockObjectStateGroup + ); + /* END: Use Case */ + + $this->assertInstanceOf( + ObjectState::class, + $ezLockObjectState + ); + $this->assertEquals('not_locked', $ezLockObjectState->identifier); + } + + /** + * testGetInitialObjectState. + * + * + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentInfo + * @depends testLoadObjectState + */ + public function testGetInitialObjectState() + { + $repository = $this->getRepository(); + $objectStateService = $repository->getObjectStateService(); + + // Create object state group with custom state + $createdStateGroups = $this->createObjectStateGroups(); + + $customObjectStateGroupId = $createdStateGroups[1]->id; + $anonymousUserId = $this->generateId('user', 10); + + $customGroup = $objectStateService->loadObjectStateGroup( + $customObjectStateGroupId + ); + + $objectStateCreateStruct = $objectStateService->newObjectStateCreateStruct( + 'sindelfingen' + ); + $objectStateCreateStruct->priority = 1; + $objectStateCreateStruct->defaultLanguageCode = 'eng-US'; + $objectStateCreateStruct->names = ['eng-US' => 'Sindelfingen']; + + $createdState = $objectStateService->createObjectState( + $customGroup, + $objectStateCreateStruct + ); + + // Store state ID to be used + $customObjectStateId = $createdState->id; + + /* BEGIN: Use Case */ + // $anonymousUserId is the content ID of "Anonymous User" + // $customObjectStateGroupId is the ID of a state group, from which no + // state has been assigned to $anonymousUserId, yet + $contentService = $repository->getContentService(); + $objectStateService = $repository->getObjectStateService(); + + $contentInfo = $contentService->loadContentInfo($anonymousUserId); + + $customObjectStateGroup = $objectStateService->loadObjectStateGroup( + $customObjectStateGroupId + ); + + // Loads the initial state of the custom state group + $initialObjectState = $objectStateService->getContentState( + $contentInfo, + $customObjectStateGroup + ); + /* END: Use Case */ + + $this->assertInstanceOf( + ObjectState::class, + $initialObjectState + ); + $this->assertEquals('sindelfingen', $initialObjectState->identifier); + $this->assertEquals(['eng-US' => 'Sindelfingen'], $initialObjectState->names); + $this->assertEquals('eng-US', $initialObjectState->defaultLanguageCode); + } + + /** + * Test for the setContentState() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::setContentState() + * @depends Ibexa\Tests\Integration\Core\Repository\ContentServiceTest::testLoadContentInfo + * @depends testLoadObjectState + */ + public function testSetContentState() + { + $repository = $this->getRepository(); + + $anonymousUserId = $this->generateId('user', 10); + $ezLockObjectStateGroupId = $this->generateId('objectstategroup', 2); + $lockedObjectStateId = $this->generateId('objectstate', 2); + /* BEGIN: Use Case */ + // $anonymousUserId is the content ID of "Anonymous User" + // $ezLockObjectStateGroupId contains the ID of the "ez_lock" object + // state group + // $lockedObjectStateId is the ID of the state "locked" + $contentService = $repository->getContentService(); + $objectStateService = $repository->getObjectStateService(); + + $contentInfo = $contentService->loadContentInfo($anonymousUserId); + + $ezLockObjectStateGroup = $objectStateService->loadObjectStateGroup( + $ezLockObjectStateGroupId + ); + $lockedObjectState = $objectStateService->loadObjectState($lockedObjectStateId); + + // Sets the state of $contentInfo from "not_locked" to "locked" + $objectStateService->setContentState( + $contentInfo, + $ezLockObjectStateGroup, + $lockedObjectState + ); + /* END: Use Case */ + + $ezLockObjectState = $objectStateService->getContentState( + $contentInfo, + $ezLockObjectStateGroup + ); + + $this->assertEquals('locked', $ezLockObjectState->identifier); + } + + /** + * Test for the setContentState() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::setContentState + * @depends testSetContentState + */ + public function testSetContentStateThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $createdStateGroups = $this->createObjectStateGroups(); + + $anonymousUserId = $this->generateId('user', 10); + $differentObjectStateGroupId = $createdStateGroups[1]->id; + $lockedObjectStateId = $this->generateId('objectstate', 2); + + /* BEGIN: Use Case */ + // $anonymousUserId is the content ID of "Anonymous User" + // $differentObjectStateGroupId contains the ID of an object state + // group which does not contain $lockedObjectStateId + // $lockedObjectStateId is the ID of the state "locked" + $contentService = $repository->getContentService(); + $objectStateService = $repository->getObjectStateService(); + + $contentInfo = $contentService->loadContentInfo($anonymousUserId); + + $differentObjectStateGroup = $objectStateService->loadObjectStateGroup( + $differentObjectStateGroupId + ); + $lockedObjectState = $objectStateService->loadObjectState($lockedObjectStateId); + + // Throws an invalid argument exception since $lockedObjectState does + // not belong to $differentObjectStateGroup + $objectStateService->setContentState( + $contentInfo, + $differentObjectStateGroup, + $lockedObjectState + ); + /* END: Use Case */ + } + + /** + * Test for the getContentCount() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::getContentCount() + * @depends testLoadObjectState + */ + public function testGetContentCount() + { + $repository = $this->getRepository(); + + $notLockedObjectStateId = $this->generateId('objectstate', 1); + /* BEGIN: Use Case */ + // $notLockedObjectStateId is the ID of the state "not_locked" + $objectStateService = $repository->getObjectStateService(); + + $notLockedObjectState = $objectStateService->loadObjectState($notLockedObjectStateId); + + $objectCount = $objectStateService->getContentCount($notLockedObjectState); + /* END: Use Case */ + + $this->assertEquals(18, $objectCount); + } + + /** + * Test for the deleteObjectState() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::deleteObjectState() + * @depends testLoadObjectState + */ + public function testDeleteObjectState() + { + $repository = $this->getRepository(); + + $notLockedObjectStateId = $this->generateId('objectstate', 1); + $lockedObjectStateId = $this->generateId('objectstate', 2); + /* BEGIN: Use Case */ + // $notLockedObjectStateId is the ID of the state "not_locked" + $objectStateService = $repository->getObjectStateService(); + + $notLockedObjectState = $objectStateService->loadObjectState($notLockedObjectStateId); + + // Deletes the object state and sets all objects, which where in that + // state, to the first state of the same object state group + $objectStateService->deleteObjectState($notLockedObjectState); + /* END: Use Case */ + + $lockedObjectState = $objectStateService->loadObjectState($lockedObjectStateId); + + // All objects transferred + $this->assertEquals( + 18, + $objectStateService->getContentCount($lockedObjectState) + ); + } + + /** + * Test for the deleteObjectStateGroup() method. + * + * + * @covers \Ibexa\Contracts\Core\Repository\ObjectStateService::deleteObjectStateGroup() + * @depends testLoadObjectStateGroup + */ + public function testDeleteObjectStateGroup() + { + $repository = $this->getRepository(); + + $objectStateGroupId = $this->generateId('objectstategroup', 2); + /* BEGIN: Use Case */ + // $objectStateGroupId contains the ID of the standard object state + // group ez_lock. + $objectStateService = $repository->getObjectStateService(); + + $loadedObjectStateGroup = $objectStateService->loadObjectStateGroup( + $objectStateGroupId + ); + + $objectStateService->deleteObjectStateGroup($loadedObjectStateGroup); + /* END: Use Case */ + + try { + $objectStateService->loadObjectStateGroup($objectStateGroupId); + $this->fail( + sprintf( + 'Object state group with ID "%s" not deleted.', + $objectStateGroupId + ) + ); + } catch (NotFoundException $e) { + } + } + + /** + * Delete existing (e.g. initial) object state groups. + */ + private function deleteExistingObjectStateGroups() + { + $repository = $this->getRepository(); + $objectStateService = $repository->getObjectStateService(); + + $objectStateGroups = $objectStateService->loadObjectStateGroups(); + + foreach ($objectStateGroups as $objectStateGroup) { + $objectStateService->deleteObjectStateGroup($objectStateGroup); + } + } + + /** + * Create Object State within the given Object State Group. + * + * @param \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup $objectStateGroup + * @param string $identifier + * @param array $names multi-language names + * @param array $descriptions multi-language descriptions + * + * @return \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState + */ + private function createObjectState( + ObjectStateGroup $objectStateGroup, + $identifier, + array $names, + array $descriptions + ) { + $objectStateService = $this->getRepository(false)->getObjectStateService(); + $objectStateCreateStruct = $objectStateService->newObjectStateCreateStruct( + $identifier + ); + $objectStateCreateStruct->priority = 23; + $objectStateCreateStruct->defaultLanguageCode = array_keys($names)[0]; + $objectStateCreateStruct->names = $names; + $objectStateCreateStruct->descriptions = $descriptions; + + // Create a new object state in the $objectStateGroup with the + // data from $objectStateCreateStruct + return $objectStateService->createObjectState( + $objectStateGroup, + $objectStateCreateStruct + ); + } +} + +class_alias(ObjectStateServiceTest::class, 'eZ\Publish\API\Repository\Tests\ObjectStateServiceTest'); diff --git a/eZ/Publish/API/Repository/Tests/Parallel/BaseParallelTestCase.php b/tests/integration/Core/Repository/Parallel/BaseParallelTestCase.php similarity index 83% rename from eZ/Publish/API/Repository/Tests/Parallel/BaseParallelTestCase.php rename to tests/integration/Core/Repository/Parallel/BaseParallelTestCase.php index 4a23faeca5..6bf573cbe3 100644 --- a/eZ/Publish/API/Repository/Tests/Parallel/BaseParallelTestCase.php +++ b/tests/integration/Core/Repository/Parallel/BaseParallelTestCase.php @@ -6,9 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\Parallel; +namespace Ibexa\Tests\Integration\Core\Repository\Parallel; -use eZ\Publish\API\Repository\Tests\BaseTest; +use Ibexa\Tests\Core\Repository\Parallel\ParallelProcessList; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; use Jenner\SimpleFork\Process; abstract class BaseParallelTestCase extends BaseTest @@ -55,3 +56,5 @@ protected function runParallelProcesses(ParallelProcessList $list): void $connection->connect(); } } + +class_alias(BaseParallelTestCase::class, 'eZ\Publish\API\Repository\Tests\Parallel\BaseParallelTestCase'); diff --git a/tests/integration/Core/Repository/Parallel/ContentServiceTest.php b/tests/integration/Core/Repository/Parallel/ContentServiceTest.php new file mode 100644 index 0000000000..08d0ec0830 --- /dev/null +++ b/tests/integration/Core/Repository/Parallel/ContentServiceTest.php @@ -0,0 +1,58 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\Parallel; + +use Ibexa\Core\Base\Exceptions\BadStateException; +use Ibexa\Tests\Core\Repository\Parallel\ParallelProcessList; + +final class ContentServiceTest extends BaseParallelTestCase +{ + public function testPublishMultipleVersions(): void + { + $repository = $this->getRepository(); + + $contentService = $repository->getContentService(); + $content = $this->createFolder( + [ + 'eng-US' => 'Content', + ], + $this->generateId('location', 2) + ); + + $version1 = $contentService->createContentDraft($content->contentInfo, $content->versionInfo); + $version2 = $contentService->createContentDraft($content->contentInfo, $content->versionInfo); + + $processList = new ParallelProcessList(); + $this->addParallelProcess($processList, static function () use ($version1, $contentService) { + try { + $contentService->publishVersion($version1->versionInfo); + } catch (BadStateException $e) { + } + }); + + $this->addParallelProcess($processList, static function () use ($version2, $contentService) { + try { + $contentService->publishVersion($version2->versionInfo); + } catch (BadStateException $e) { + } + }); + + $this->runParallelProcesses($processList); + + $version1 = $contentService->loadVersionInfo($version1->contentInfo, 2); + $version2 = $contentService->loadVersionInfo($version2->contentInfo, 3); + + $this->assertTrue( + $version1->isPublished() && $version2->isDraft() || $version1->isDraft() && $version2->isPublished(), + 'One of the versions should be published and the other should be draft' + ); + } +} + +class_alias(ContentServiceTest::class, 'eZ\Publish\API\Repository\Tests\Parallel\ContentServiceTest'); diff --git a/eZ/Publish/API/Repository/Tests/PermissionResolverTest.php b/tests/integration/Core/Repository/PermissionResolverTest.php similarity index 79% rename from eZ/Publish/API/Repository/Tests/PermissionResolverTest.php rename to tests/integration/Core/Repository/PermissionResolverTest.php index 660e84c2d1..3cfeb6d70b 100644 --- a/eZ/Publish/API/Repository/Tests/PermissionResolverTest.php +++ b/tests/integration/Core/Repository/PermissionResolverTest.php @@ -4,22 +4,26 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; use function array_filter; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\LookupLimitationResult; -use eZ\Publish\API\Repository\Values\User\LookupPolicyLimitations; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Repository\Values\User\UserReference; -use eZ\Publish\SPI\Limitation\Target\Builder\VersionBuilder; +use Ibexa\Contracts\Core\Limitation\Target\Builder\VersionBuilder; +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\LookupLimitationResult; +use Ibexa\Contracts\Core\Repository\Values\User\LookupPolicyLimitations; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Repository\Values\User\UserReference; /** * Test case for operations in the PermissionResolver. * - * @see \eZ\Publish\API\Repository\PermissionResolver + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver * @group integration * @group permission */ @@ -28,7 +32,7 @@ class PermissionResolverTest extends BaseTest /** * Test for the getCurrentUserReference() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::getCurrentUserReference() + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::getCurrentUserReference() */ public function testGetCurrentUserReferenceReturnsAnonymousUserReference() { @@ -39,7 +43,7 @@ public function testGetCurrentUserReferenceReturnsAnonymousUserReference() ); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. // Only a UserReference has previously been set to the $repository @@ -47,11 +51,7 @@ public function testGetCurrentUserReferenceReturnsAnonymousUserReference() $anonymousUserReference = $permissionResolver->getCurrentUserReference(); /* END: Use Case */ - $this->assertInstanceOf( - 'eZ\Publish\API\Repository\Values\User\UserReference', - $anonymousUserReference - ); - $this->assertEquals( + self::assertEquals( $anonymousUserReference->getUserId(), $repository->getUserService()->loadUser($anonymousUserId)->id ); @@ -60,8 +60,8 @@ public function testGetCurrentUserReferenceReturnsAnonymousUserReference() /** * Test for the setCurrentUserReference() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::setCurrentUserReference() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::setCurrentUserReference() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService */ public function testSetCurrentUserReference() { @@ -102,8 +102,8 @@ public function testSetCurrentUserReference() /** * Test for the hasAccess() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::hasAccess() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::hasAccess() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService */ public function testHasAccessWithAnonymousUserNo() { @@ -112,7 +112,7 @@ public function testHasAccessWithAnonymousUserNo() $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); @@ -132,9 +132,9 @@ public function testHasAccessWithAnonymousUserNo() /** * Test for the hasAccess() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::hasAccess() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessWithAnonymousUserNo + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::hasAccess() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends testHasAccessWithAnonymousUserNo */ public function testHasAccessForCurrentUserNo() { @@ -143,7 +143,7 @@ public function testHasAccessForCurrentUserNo() $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); @@ -166,8 +166,8 @@ public function testHasAccessForCurrentUserNo() /** * Test for the hasAccess() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::hasAccess() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::hasAccess() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService */ public function testHasAccessWithAdministratorUser() { @@ -194,10 +194,10 @@ public function testHasAccessWithAdministratorUser() /** * Test for the hasAccess() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::hasAccess() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testSetCurrentUserReference - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessWithAdministratorUser + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::hasAccess() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends testSetCurrentUserReference + * @depends testHasAccessWithAdministratorUser */ public function testHasAccessForCurrentUserYes() { @@ -227,9 +227,9 @@ public function testHasAccessForCurrentUserYes() /** * Test for the hasAccess() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::hasAccess() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testSetCurrentUserReference + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::hasAccess() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends testSetCurrentUserReference */ public function testHasAccessLimited() { @@ -257,14 +257,14 @@ public function testHasAccessLimited() /** * Test for the canUser() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::canUser() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentService - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessForCurrentUserNo + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentService + * @depends testHasAccessForCurrentUserNo */ public function testCanUserForAnonymousUserNo() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); @@ -272,7 +272,7 @@ public function testCanUserForAnonymousUserNo() $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. // $homeId contains the ID of the "Home" frontpage @@ -303,14 +303,14 @@ public function testCanUserForAnonymousUserNo() /** * Test for the canUser() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::canUser() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentService - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessForCurrentUserYes + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentService + * @depends testHasAccessForCurrentUserYes */ public function testCanUserForAdministratorUser() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $repository = $this->getRepository(); @@ -348,10 +348,10 @@ public function testCanUserForAdministratorUser() /** * Test for the canUser() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::canUser() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentService - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessLimited + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentService + * @depends testHasAccessLimited */ public function testCanUserWithLimitationYes() { @@ -384,14 +384,14 @@ public function testCanUserWithLimitationYes() /** * Test for the canUser() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::canUser() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentService - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessLimited + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentService + * @depends testHasAccessLimited */ public function testCanUserWithLimitationNo() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); @@ -428,15 +428,15 @@ public function testCanUserWithLimitationNo() /** * Test for the canUser() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::canUser() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentTypeService - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testSetCurrentUserReference - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessLimited + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentTypeService + * @depends testSetCurrentUserReference + * @depends testHasAccessLimited */ public function testCanUserThrowsInvalidArgumentException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $repository = $this->getRepository(); @@ -458,20 +458,20 @@ public function testCanUserThrowsInvalidArgumentException() $userGroupContentType = $contentTypeService->loadContentType($userGroupContentTypeId); // This call will throw "InvalidArgumentException" because $userGroupContentType - // is an instance of \eZ\Publish\API\Repository\Values\ContentType\ContentType, + // is an instance of \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType, // which can not be checked for user access - $canUser = $permissionResolver->canUser('content', 'create', $userGroupContentType); + $permissionResolver->canUser('content', 'create', $userGroupContentType); /* END: Use Case */ } /** * Test for the canUser() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::canUser() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentTypeService - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessLimited + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentTypeService + * @depends testHasAccessLimited */ public function testCanUserWithTargetYes() { @@ -528,15 +528,15 @@ public function testCanUserWithTargetYes() /** * Test for the canUser() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::canUser() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentTypeService - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessLimited + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentTypeService + * @depends testHasAccessLimited */ public function testCanUserWithTargetNo() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); @@ -588,11 +588,11 @@ public function testCanUserWithTargetNo() /** * Test for the canUser() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::canUser() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentTypeService - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessLimited + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentTypeService + * @depends testHasAccessLimited */ public function testCanUserWithMultipleTargetsYes() { @@ -650,15 +650,15 @@ public function testCanUserWithMultipleTargetsYes() /** * Test for the canUser() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::canUser() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentTypeService - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessLimited + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentTypeService + * @depends testHasAccessLimited */ public function testCanUserWithMultipleTargetsNo() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); @@ -711,17 +711,17 @@ public function testCanUserWithMultipleTargetsNo() /** * Test for the canUser() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::canUser() - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentTypeService - * @depends eZ\Publish\API\Repository\Tests\RepositoryTest::testGetURLAliasService - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testSetCurrentUserReference - * @depends eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessLimited + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentTypeService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetURLAliasService + * @depends testSetCurrentUserReference + * @depends testHasAccessLimited */ public function testCanUserWithTargetThrowsInvalidArgumentException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $repository = $this->getRepository(); @@ -760,11 +760,11 @@ public function testCanUserWithTargetThrowsInvalidArgumentException() /** * Test for the canUser() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::canUser() + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser() */ public function testCanUserThrowsBadStateException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\BadStateException::class); + $this->expectException(BadStateException::class); $this->markTestIncomplete( 'Cannot be tested on current fixture since policy with unsupported limitation value is not available.' @@ -774,19 +774,19 @@ public function testCanUserThrowsBadStateException() /** * Test PermissionResolver::canUser for Users with different Limitations. * - * @covers \eZ\Publish\API\Repository\PermissionResolver::canUser + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser * * @dataProvider getDataForTestCanUserWithLimitations * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation * @param string $module * @param string $function - * @param \eZ\Publish\API\Repository\Values\ValueObject $object + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $object * @param array $targets * @param bool $expectedResult expected result of canUser check * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function testCanUserWithLimitations( Limitation $limitation, @@ -827,7 +827,7 @@ public function testCanUserWithLimitations( * * @return array * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function getDataForTestCanUserWithLimitations() { @@ -880,10 +880,10 @@ public function getDataForTestCanUserWithLimitations() /** * Test for the lookupLimitations() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::lookupLimitations() - * @depends \eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends \eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentService - * @depends \eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessForCurrentUserNo + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::lookupLimitations() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentService + * @depends Ibexa\Tests\Integration\Core\Repository\PermissionResolverTest::testHasAccessForCurrentUserNo */ public function testLookupLimitationsForAnonymousUserHasNoAccess(): void { @@ -893,7 +893,7 @@ public function testLookupLimitationsForAnonymousUserHasNoAccess(): void $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. // $homeId contains the ID of the "Home" frontpage @@ -924,10 +924,10 @@ public function testLookupLimitationsForAnonymousUserHasNoAccess(): void /** * Test for the lookupLimitations() method. * - * @see \eZ\Publish\API\Repository\PermissionResolver::lookupLimitations() - * @depends \eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends \eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentService - * @depends \eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessForCurrentUserYes + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::lookupLimitations() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentService + * @depends Ibexa\Tests\Integration\Core\Repository\PermissionResolverTest::testHasAccessForCurrentUserYes */ public function testLookupLimitationsForAdministratorUser(): void { @@ -965,16 +965,16 @@ public function testLookupLimitationsForAdministratorUser(): void /** * When one of policy pass then all limitation should be returned. * - * @see \eZ\Publish\API\Repository\PermissionResolver::lookupLimitations() - * @depends \eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends \eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentService - * @depends \eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessForCurrentUserYes + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::lookupLimitations() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentService + * @depends Ibexa\Tests\Integration\Core\Repository\PermissionResolverTest::testHasAccessForCurrentUserYes * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testLookupLimitationsWithLimitations(): void { @@ -1026,16 +1026,16 @@ public function testLookupLimitationsWithLimitations(): void /** * When one of policy pass then only filtered limitation should be returned. * - * @see \eZ\Publish\API\Repository\PermissionResolver::lookupLimitations() - * @depends \eZ\Publish\API\Repository\Tests\RepositoryTest::testGetUserService - * @depends \eZ\Publish\API\Repository\Tests\RepositoryTest::testGetContentService - * @depends \eZ\Publish\API\Repository\Tests\PermissionResolverTest::testHasAccessForCurrentUserYes + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::lookupLimitations() + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetUserService + * @depends Ibexa\Tests\Integration\Core\Repository\RepositoryTest::testGetContentService + * @depends Ibexa\Tests\Integration\Core\Repository\PermissionResolverTest::testHasAccessForCurrentUserYes * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testLookupLimitationsWithFilteredLimitations(): void { @@ -1087,11 +1087,11 @@ public function testLookupLimitationsWithFilteredLimitations(): void * If the role limitation is set it should be taken into account. In this case, role limitation * will pass and ContentTypeLimitation should be returned. * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testLookupLimitationsWithRoleLimitationsHasAccess(): void { @@ -1142,13 +1142,13 @@ public function testLookupLimitationsWithRoleLimitationsHasAccess(): void * In this case, role limitation will pass and SectionLimitation should be returned as role limitation * and limitations in LookupPolicyLimitations should be an empty array. * - * @see https://jira.ez.no/browse/EZP-30728 + * @see https://issues.ibexa.co/browse/EZP-30728 * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testLookupLimitationsWithRoleLimitationsWithoutPolicyLimitationsHasAccess(): void { @@ -1201,11 +1201,11 @@ public function testLookupLimitationsWithRoleLimitationsWithoutPolicyLimitations * If the role limitation is set it should be taken into account. In this case, role limitation * will not pass and ContentTypeLimitation should not be returned. * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\LimitationValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testLookupLimitationsWithRoleLimitationsHasNoAccess(): void { @@ -1336,14 +1336,14 @@ public function testLookupLimitationsWithMixedTargets(): void } /** - * @param \eZ\Publish\API\Repository\Repository $repository + * @param \Ibexa\Contracts\Core\Repository\Repository $repository * @param string $contentTypeIdentifier * @param string $mainLanguageCode * @param int $sectionId * - * @return \eZ\Publish\API\Repository\Values\Content\ContentCreateStruct + * @return \Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ private function getContentCreateStruct( Repository $repository, @@ -1360,3 +1360,5 @@ private function getContentCreateStruct( return $contentCreateStruct; } } + +class_alias(PermissionResolverTest::class, 'eZ\Publish\API\Repository\Tests\PermissionResolverTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP20018LanguageTest.php b/tests/integration/Core/Repository/Regression/EZP20018LanguageTest.php similarity index 78% rename from eZ/Publish/API/Repository/Tests/Regression/EZP20018LanguageTest.php rename to tests/integration/Core/Repository/Regression/EZP20018LanguageTest.php index 777cfe297b..d5b46d5c10 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP20018LanguageTest.php +++ b/tests/integration/Core/Repository/Regression/EZP20018LanguageTest.php @@ -4,12 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LanguageCode; -use EzSystems\EzPlatformSolrSearchEngine\Tests\SetupFactory\LegacySetupFactory; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LanguageCode; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; +use Ibexa\Tests\Solr\SetupFactory\LegacySetupFactory; /** * Test case for language issues in EZP-20018. @@ -57,11 +58,11 @@ protected function setUp(): void } /** - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LanguageCode + * @covers \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LanguageCode */ public function testSearchOnNotExistingLanguageGivesException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $setupFactory = $this->getSetupFactory(); if ($setupFactory instanceof LegacySetupFactory) { @@ -74,7 +75,7 @@ public function testSearchOnNotExistingLanguageGivesException() } /** - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LanguageCode + * @covers \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LanguageCode */ public function testSearchOnUsedLanguageGivesOneResult() { @@ -87,7 +88,7 @@ public function testSearchOnUsedLanguageGivesOneResult() } /** - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LanguageCode + * @covers \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LanguageCode */ public function testSearchOnStandardLanguageGivesManyResult() { @@ -101,7 +102,7 @@ public function testSearchOnStandardLanguageGivesManyResult() } /** - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\LanguageCode + * @covers \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LanguageCode */ public function testSearchOnNotUsedInstalledLanguageGivesNoResult() { @@ -113,3 +114,5 @@ public function testSearchOnNotUsedInstalledLanguageGivesNoResult() $this->assertEquals($results->totalCount, count($results->searchHits)); } } + +class_alias(EZP20018LanguageTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP20018LanguageTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP20018ObjectStateTest.php b/tests/integration/Core/Repository/Regression/EZP20018ObjectStateTest.php similarity index 82% rename from eZ/Publish/API/Repository/Tests/Regression/EZP20018ObjectStateTest.php rename to tests/integration/Core/Repository/Regression/EZP20018ObjectStateTest.php index 0cf489bf74..c6782fc312 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP20018ObjectStateTest.php +++ b/tests/integration/Core/Repository/Regression/EZP20018ObjectStateTest.php @@ -4,22 +4,21 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ObjectStateId; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ObjectStateId; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Test case for ObjectState issues in EZP-20018. * + * @covers \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ObjectStateId + * * Issue EZP-20018 */ class EZP20018ObjectStateTest extends BaseTest { - /** - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ObjectStateId - */ public function testSearchForNonUsedObjectState() { $repository = $this->getRepository(); @@ -48,9 +47,6 @@ public function testSearchForNonUsedObjectState() $this->assertCount($results2->totalCount, $results2->searchHits); } - /** - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\ObjectStateId - */ public function testSearchForUsedObjectState() { $repository = $this->getRepository(); @@ -80,3 +76,5 @@ public function testSearchForUsedObjectState() $this->assertCount($results2->totalCount, $results2->searchHits); } } + +class_alias(EZP20018ObjectStateTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP20018ObjectStateTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP20018VisibilityTest.php b/tests/integration/Core/Repository/Regression/EZP20018VisibilityTest.php similarity index 81% rename from eZ/Publish/API/Repository/Tests/Regression/EZP20018VisibilityTest.php rename to tests/integration/Core/Repository/Regression/EZP20018VisibilityTest.php index f2e9119e09..d184c09f9c 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP20018VisibilityTest.php +++ b/tests/integration/Core/Repository/Regression/EZP20018VisibilityTest.php @@ -4,22 +4,21 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Visibility; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Visibility; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Test case for Visibility issues in EZP-20018. * + * @covers \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Visibility + * * Issue EZP-20018 */ class EZP20018VisibilityTest extends BaseTest { - /** - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Visibility - */ public function testSearchForHiddenContent() { $repository = $this->getRepository(); @@ -44,9 +43,6 @@ public function testSearchForHiddenContent() $this->assertCount(1, $results2->searchHits); } - /** - * @see \eZ\Publish\API\Repository\Values\Content\Query\Criterion\Visibility - */ public function testSearchForVisibleContent() { $repository = $this->getRepository(); @@ -72,3 +68,5 @@ public function testSearchForVisibleContent() $this->assertEquals($results2->totalCount, count($results2->searchHits)); } } + +class_alias(EZP20018VisibilityTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP20018VisibilityTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP21069Test.php b/tests/integration/Core/Repository/Regression/EZP21069Test.php similarity index 90% rename from eZ/Publish/API/Repository/Tests/Regression/EZP21069Test.php rename to tests/integration/Core/Repository/Regression/EZP21069Test.php index bf71162ba9..851ee3b591 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP21069Test.php +++ b/tests/integration/Core/Repository/Regression/EZP21069Test.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Field; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Test case for issue EZP-21069. @@ -113,3 +113,5 @@ public function testSearchOnDraftAttributeContentGivesNoResult() $this->assertEmpty($results->searchHits); } } + +class_alias(EZP21069Test::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP21069Test'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP21089Test.php b/tests/integration/Core/Repository/Regression/EZP21089Test.php similarity index 93% rename from eZ/Publish/API/Repository/Tests/Regression/EZP21089Test.php rename to tests/integration/Core/Repository/Regression/EZP21089Test.php index b5e3cc6c22..70b6d9a826 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP21089Test.php +++ b/tests/integration/Core/Repository/Regression/EZP21089Test.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; use DateTime; -use eZ\Publish\API\Repository\Tests\BaseTest; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Test case for issue EZP-21089. @@ -21,7 +21,7 @@ */ class EZP21089Test extends BaseTest { - /** @var \eZ\Publish\Core\Repository\Values\ContentType\ContentType */ + /** @var \Ibexa\Core\Repository\Values\ContentType\ContentType */ private $contentType; protected function setUp(): void @@ -123,3 +123,5 @@ public function testCreateContent() ); } } + +class_alias(EZP21089Test::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP21089Test'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP21109EzIntegerTest.php b/tests/integration/Core/Repository/Regression/EZP21109EzIntegerTest.php similarity index 85% rename from eZ/Publish/API/Repository/Tests/Regression/EZP21109EzIntegerTest.php rename to tests/integration/Core/Repository/Regression/EZP21109EzIntegerTest.php index 1158c64c2f..420c2a2046 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP21109EzIntegerTest.php +++ b/tests/integration/Core/Repository/Regression/EZP21109EzIntegerTest.php @@ -4,11 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Persistence\Legacy\Exception\TypeNotFound as TypeNotFoundException; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\FieldType\Integer\Value; +use Ibexa\Core\Persistence\Legacy\Exception\TypeNotFound as TypeNotFoundException; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Regression tests for the issue EZP-21109. @@ -22,7 +23,7 @@ class EZP21109EzIntegerTest extends BaseTest */ protected $classShortName; - /** @var \eZ\Publish\API\Repository\Values\ContentType\ContentType */ + /** @var \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType */ protected $contentType; protected function setUp(): void @@ -63,10 +64,10 @@ public function testEzIntegerWithDefaultValues($integerValue) $content = $contentService->loadContent($draft->versionInfo->contentInfo->id); - /** @var \eZ\Publish\Core\FieldType\Integer\Value $fieldValue */ + /** @var \Ibexa\Core\FieldType\Integer\Value $fieldValue */ $fieldValue = $content->getFieldValue('test'); - $this->assertInstanceOf('eZ\Publish\Core\FieldType\Integer\Value', $fieldValue); + $this->assertInstanceOf(Value::class, $fieldValue); $this->assertEquals($integerValue, $fieldValue->value); @@ -87,7 +88,7 @@ public function validIntegerValues() /** * Creates a Test ContentType for this test holding an ezintegerfield. * - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType */ protected function createTestContentType() { @@ -140,3 +141,5 @@ protected function deleteTestContentType() } } } + +class_alias(EZP21109EzIntegerTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP21109EzIntegerTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP21771EzStringTest.php b/tests/integration/Core/Repository/Regression/EZP21771EzStringTest.php similarity index 87% rename from eZ/Publish/API/Repository/Tests/Regression/EZP21771EzStringTest.php rename to tests/integration/Core/Repository/Regression/EZP21771EzStringTest.php index 0e3229cae3..605a4087d3 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP21771EzStringTest.php +++ b/tests/integration/Core/Repository/Regression/EZP21771EzStringTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Test case for 11+ string issue in EZP-21771. @@ -54,3 +54,5 @@ public function test11NumbersOnEzString() ); } } + +class_alias(EZP21771EzStringTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP21771EzStringTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP21798Test.php b/tests/integration/Core/Repository/Regression/EZP21798Test.php similarity index 91% rename from eZ/Publish/API/Repository/Tests/Regression/EZP21798Test.php rename to tests/integration/Core/Repository/Regression/EZP21798Test.php index 7a273ea5ba..b19588a7c5 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP21798Test.php +++ b/tests/integration/Core/Repository/Regression/EZP21798Test.php @@ -4,9 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Test case for language issues in EZP-21798. @@ -77,7 +78,7 @@ public function testRoleChanges() $roleDraft = $roleService->createRoleDraft($role); $numPolicies = count($roleDraft->getPolicies()); - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft[] $policies */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft[] $policies */ $policies = $roleDraft->getPolicies(); $found = false; @@ -91,7 +92,7 @@ public function testRoleChanges() $this->assertTrue($found, "Couldn't find policy with module 'content' and function 'read'"); $newPolicy = $roleService->newPolicyUpdateStruct(); - $newLimitation = new \eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation(); + $newLimitation = new SectionLimitation(); $section = $sectionService->loadSectionByIdentifier('private'); $newLimitation->limitationValues = [1, $section->id]; $newPolicy->addLimitation($newLimitation); @@ -110,3 +111,5 @@ public function testRoleChanges() $contentService->loadContent($contentInfoarticle->id); } } + +class_alias(EZP21798Test::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP21798Test'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP21906SearchOneContentMultipleLocationsTest.php b/tests/integration/Core/Repository/Regression/EZP21906SearchOneContentMultipleLocationsTest.php similarity index 94% rename from eZ/Publish/API/Repository/Tests/Regression/EZP21906SearchOneContentMultipleLocationsTest.php rename to tests/integration/Core/Repository/Regression/EZP21906SearchOneContentMultipleLocationsTest.php index 8092a10227..4b18b4ec55 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP21906SearchOneContentMultipleLocationsTest.php +++ b/tests/integration/Core/Repository/Regression/EZP21906SearchOneContentMultipleLocationsTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Issue EZP-21906. @@ -220,3 +220,5 @@ public function searchContentQueryProvider() ]; } } + +class_alias(EZP21906SearchOneContentMultipleLocationsTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP21906SearchOneContentMultipleLocationsTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP22408DeleteRelatedObjectTest.php b/tests/integration/Core/Repository/Regression/EZP22408DeleteRelatedObjectTest.php similarity index 86% rename from eZ/Publish/API/Repository/Tests/Regression/EZP22408DeleteRelatedObjectTest.php rename to tests/integration/Core/Repository/Regression/EZP22408DeleteRelatedObjectTest.php index 6dd55e7d4f..f6fcb51f43 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP22408DeleteRelatedObjectTest.php +++ b/tests/integration/Core/Repository/Regression/EZP22408DeleteRelatedObjectTest.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; class EZP22408DeleteRelatedObjectTest extends BaseTest { - /** @var \eZ\Publish\API\Repository\Values\ContentType\ContentType */ + /** @var \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType */ private $testContentType; protected function setUp(): void @@ -37,7 +37,7 @@ public function testRelationListIsUpdatedWhenRelatedObjectIsDeleted() $contentService->deleteContent($targetObject1->contentInfo); $reloadedReferenceObject = $contentService->loadContent($referenceObject->id); - /** @var \eZ\Publish\Core\FieldType\RelationList\Value */ + /** @var \Ibexa\Core\FieldType\RelationList\Value */ $relationListValue = $reloadedReferenceObject->getFieldValue('relation_list'); $this->assertSame([$targetObject2->id], $relationListValue->destinationContentIds); } @@ -55,7 +55,7 @@ public function testSingleRelationIsUpdatedWhenRelatedObjectIsDeleted() $contentService->deleteContent($targetObject->contentInfo); $reloadedReferenceObject = $contentService->loadContent($referenceObject->id); - /** @var \eZ\Publish\Core\FieldType\Relation\Value */ + /** @var \Ibexa\Core\FieldType\Relation\Value */ $relationValue = $reloadedReferenceObject->getFieldValue('single_relation'); $this->assertEmpty($relationValue->destinationContentId); } @@ -67,7 +67,7 @@ private function createTestContentType() $createStruct = $contentTypeService->newContentTypeCreateStruct('test_content_type'); $createStruct->mainLanguageCode = $languageCode; - $createStruct->names = [$languageCode => 'Test Content Type']; + $createStruct->names = [$languageCode => 'Test content type']; $createStruct->nameSchema = '<name>'; $createStruct->urlAliasSchema = '<name>'; @@ -117,7 +117,7 @@ private function getMainLanguageCode() /** * @param string $name * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ private function createTargetObject($name) { @@ -141,9 +141,9 @@ private function createTargetObject($name) /** * @param string $name * @param array $relationListTarget Array of destination content ids - * @param id $singleRelationTarget Content id + * @param int $singleRelationTarget Content id * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ private function createReferenceObject($name, array $relationListTarget = [], $singleRelationTarget = null) { @@ -173,10 +173,12 @@ private function createReferenceObject($name, array $relationListTarget = [], $s } /** - * @return \eZ\Publish\API\Repository\Values\Content\LocationCreateStruct + * @return \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct */ private function getLocationCreateStruct() { return $this->getRepository()->getLocationService()->newLocationCreateStruct(2); } } + +class_alias(EZP22408DeleteRelatedObjectTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP22408DeleteRelatedObjectTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP22409RelationListTypeStateTest.php b/tests/integration/Core/Repository/Regression/EZP22409RelationListTypeStateTest.php similarity index 95% rename from eZ/Publish/API/Repository/Tests/Regression/EZP22409RelationListTypeStateTest.php rename to tests/integration/Core/Repository/Regression/EZP22409RelationListTypeStateTest.php index bb5f082de0..1b71d055a3 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP22409RelationListTypeStateTest.php +++ b/tests/integration/Core/Repository/Regression/EZP22409RelationListTypeStateTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; use DateTime; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\Core\FieldType\RelationList; +use Ibexa\Core\FieldType\RelationList; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Test case for RelationList using alterate ContentType states issue in EZP-22409. @@ -155,3 +155,5 @@ private function createContentWithRelationList() $content2 = $contentService->publishVersion($draft2->versionInfo); } } + +class_alias(EZP22409RelationListTypeStateTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP22409RelationListTypeStateTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP22612URLAliasTranslations.php b/tests/integration/Core/Repository/Regression/EZP22612URLAliasTranslations.php similarity index 84% rename from eZ/Publish/API/Repository/Tests/Regression/EZP22612URLAliasTranslations.php rename to tests/integration/Core/Repository/Regression/EZP22612URLAliasTranslations.php index b2e550ec03..837f92c35f 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP22612URLAliasTranslations.php +++ b/tests/integration/Core/Repository/Regression/EZP22612URLAliasTranslations.php @@ -4,9 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; class EZP22612URLAliasTranslations extends BaseTest { @@ -54,8 +55,10 @@ public function testURLAliasLoadedInRightLanguage() $alias = $aliasService->lookup('common/alias'); $this->assertInstanceOf( - 'eZ\\Publish\\API\\Repository\\Values\\Content\\URLAlias', + URLAlias::class, $alias ); } } + +class_alias(EZP22612URLAliasTranslations::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP22612URLAliasTranslations'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP22840RoleLimitations.php b/tests/integration/Core/Repository/Regression/EZP22840RoleLimitations.php similarity index 86% rename from eZ/Publish/API/Repository/Tests/Regression/EZP22840RoleLimitations.php rename to tests/integration/Core/Repository/Regression/EZP22840RoleLimitations.php index 4d89ab12ba..66611cf4b5 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP22840RoleLimitations.php +++ b/tests/integration/Core/Repository/Regression/EZP22840RoleLimitations.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\User\Limitation\NewObjectStateLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\NewObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Issue EZP-22840. @@ -87,3 +87,5 @@ public function testSectionRoleAssignLimitation() ); } } + +class_alias(EZP22840RoleLimitations::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP22840RoleLimitations'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP22958SearchSubtreePathstringFormatTest.php b/tests/integration/Core/Repository/Regression/EZP22958SearchSubtreePathstringFormatTest.php similarity index 82% rename from eZ/Publish/API/Repository/Tests/Regression/EZP22958SearchSubtreePathstringFormatTest.php rename to tests/integration/Core/Repository/Regression/EZP22958SearchSubtreePathstringFormatTest.php index b8ec981898..721a21600c 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP22958SearchSubtreePathstringFormatTest.php +++ b/tests/integration/Core/Repository/Regression/EZP22958SearchSubtreePathstringFormatTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Issue EZP-21906. @@ -84,3 +84,5 @@ public function searchContentQueryWithInvalidDataProvider() ]; } } + +class_alias(EZP22958SearchSubtreePathstringFormatTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP22958SearchSubtreePathstringFormatTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP26327UrlAliasHistorizationTest.php b/tests/integration/Core/Repository/Regression/EZP26327UrlAliasHistorizationTest.php similarity index 86% rename from eZ/Publish/API/Repository/Tests/Regression/EZP26327UrlAliasHistorizationTest.php rename to tests/integration/Core/Repository/Regression/EZP26327UrlAliasHistorizationTest.php index 0d762a907a..ee9513e659 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP26327UrlAliasHistorizationTest.php +++ b/tests/integration/Core/Repository/Regression/EZP26327UrlAliasHistorizationTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** - * Issue https://jira.ez.no/browse/EZP-26327. + * Issue https://issues.ibexa.co/browse/EZP-26327. * * @group ezp26327 */ @@ -52,3 +52,5 @@ public function testHistorization() $this->assertTrue($historyAlias->isHistory); } } + +class_alias(EZP26327UrlAliasHistorizationTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP26327UrlAliasHistorizationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP26367UrlAliasHistoryRedirectLoopTest.php b/tests/integration/Core/Repository/Regression/EZP26367UrlAliasHistoryRedirectLoopTest.php similarity index 95% rename from eZ/Publish/API/Repository/Tests/Regression/EZP26367UrlAliasHistoryRedirectLoopTest.php rename to tests/integration/Core/Repository/Regression/EZP26367UrlAliasHistoryRedirectLoopTest.php index 6c5f3afef6..b2b5d28aec 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP26367UrlAliasHistoryRedirectLoopTest.php +++ b/tests/integration/Core/Repository/Regression/EZP26367UrlAliasHistoryRedirectLoopTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** - * Issue https://jira.ez.no/browse/EZP-26367. + * Issue https://issues.ibexa.co/browse/EZP-26367. * * @group regression * @group ezp26367 @@ -160,3 +160,5 @@ public function testLookupHistoryUrlReturnsActiveAlias() $this->assertTrue($urlAliasHistorized->isHistory); } } + +class_alias(EZP26367UrlAliasHistoryRedirectLoopTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP26367UrlAliasHistoryRedirectLoopTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP26551DeleteContentTypeDraftTest.php b/tests/integration/Core/Repository/Regression/EZP26551DeleteContentTypeDraftTest.php similarity index 83% rename from eZ/Publish/API/Repository/Tests/Regression/EZP26551DeleteContentTypeDraftTest.php rename to tests/integration/Core/Repository/Regression/EZP26551DeleteContentTypeDraftTest.php index 6e7443791b..c2f3ad147e 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP26551DeleteContentTypeDraftTest.php +++ b/tests/integration/Core/Repository/Regression/EZP26551DeleteContentTypeDraftTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Tests\BaseTest; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** - * @issue https://jira.ez.no/browse/EZP-26551 + * @issue https://issues.ibexa.co/browse/EZP-26551 * @group regression * @group ezp26551 */ @@ -49,3 +49,5 @@ public function testDeleteContentTypeGroup() } } } + +class_alias(EZP26551DeleteContentTypeDraftTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP26551DeleteContentTypeDraftTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EZP28799SubtreeSearchTest.php b/tests/integration/Core/Repository/Regression/EZP28799SubtreeSearchTest.php similarity index 90% rename from eZ/Publish/API/Repository/Tests/Regression/EZP28799SubtreeSearchTest.php rename to tests/integration/Core/Repository/Regression/EZP28799SubtreeSearchTest.php index 78af6b49dc..5d28154928 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EZP28799SubtreeSearchTest.php +++ b/tests/integration/Core/Repository/Regression/EZP28799SubtreeSearchTest.php @@ -4,18 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** - * @see https://jira.ez.no/browse/EZP-28799 + * @see https://issues.ibexa.co/browse/EZP-28799 */ class EZP28799SubtreeSearchTest extends BaseTest { /** - * @return \eZ\Publish\API\Repository\Values\Content\Content[] + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content[] */ public function createTestContent() { @@ -103,3 +103,5 @@ public function testNegativeSubtree() $this->assertSame(0, $result->totalCount); } } + +class_alias(EZP28799SubtreeSearchTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EZP28799SubtreeSearchTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/EnvTest.php b/tests/integration/Core/Repository/Regression/EnvTest.php similarity index 82% rename from eZ/Publish/API/Repository/Tests/Regression/EnvTest.php rename to tests/integration/Core/Repository/Regression/EnvTest.php index c34d829862..ec7a08559d 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/EnvTest.php +++ b/tests/integration/Core/Repository/Regression/EnvTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter; +use Ibexa\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Cache\Adapter\RedisAdapter; use Symfony\Component\Cache\Adapter\TagAwareAdapter; @@ -22,7 +22,7 @@ class EnvTest extends BaseTest */ public function testVerifyCacheDriver() { - $pool = $this->getSetupFactory()->getServiceContainer()->get('ezpublish.cache_pool'); + $pool = $this->getSetupFactory()->getServiceContainer()->get('ibexa.cache_pool'); $this->assertInstanceOf(TransactionalInMemoryCacheAdapter::class, $pool); @@ -43,3 +43,5 @@ public function testVerifyCacheDriver() } } } + +class_alias(EnvTest::class, 'eZ\Publish\API\Repository\Tests\Regression\EnvTest'); diff --git a/eZ/Publish/API/Repository/Tests/Regression/PureNegativeQueryTest.php b/tests/integration/Core/Repository/Regression/PureNegativeQueryTest.php similarity index 91% rename from eZ/Publish/API/Repository/Tests/Regression/PureNegativeQueryTest.php rename to tests/integration/Core/Repository/Regression/PureNegativeQueryTest.php index edbac2d9fb..5e507e5b15 100644 --- a/eZ/Publish/API/Repository/Tests/Regression/PureNegativeQueryTest.php +++ b/tests/integration/Core/Repository/Regression/PureNegativeQueryTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Regression; +namespace Ibexa\Tests\Integration\Core\Repository\Regression; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * This test will try to execute search queries that might be interpreted as "pure negative" @@ -215,7 +215,7 @@ public function providerForTestMatchAll() /** * @dataProvider providerForTestMatchAll * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion * @param int $totalCount */ public function testMatchAllContentInfoQuery($criterion, $totalCount) @@ -234,7 +234,7 @@ public function testMatchAllContentInfoQuery($criterion, $totalCount) /** * @dataProvider providerForTestMatchAll * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion * @param int $totalCount */ public function testMatchAllContentInfoFilter($criterion, $totalCount) @@ -253,7 +253,7 @@ public function testMatchAllContentInfoFilter($criterion, $totalCount) /** * @dataProvider providerForTestMatchAll * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion * @param int $totalCount */ public function testMatchAllLocationQuery($criterion, $totalCount) @@ -272,7 +272,7 @@ public function testMatchAllLocationQuery($criterion, $totalCount) /** * @dataProvider providerForTestMatchAll * - * @param \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterion + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion * @param int $totalCount */ public function testMatchAllLocationFilter($criterion, $totalCount) @@ -288,3 +288,5 @@ public function testMatchAllLocationFilter($criterion, $totalCount) $this->assertEquals($totalCount, $result->totalCount); } } + +class_alias(PureNegativeQueryTest::class, 'eZ\Publish\API\Repository\Tests\Regression\PureNegativeQueryTest'); diff --git a/tests/integration/Core/Repository/RepositoryTest.php b/tests/integration/Core/Repository/RepositoryTest.php new file mode 100644 index 0000000000..8b66866183 --- /dev/null +++ b/tests/integration/Core/Repository/RepositoryTest.php @@ -0,0 +1,310 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use Exception; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\FieldTypeService; +use Ibexa\Contracts\Core\Repository\LanguageService; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\NotificationService; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\RoleService; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\SectionService; +use Ibexa\Contracts\Core\Repository\TrashService; +use Ibexa\Contracts\Core\Repository\URLAliasService; +use Ibexa\Contracts\Core\Repository\URLWildcardService; +use Ibexa\Contracts\Core\Repository\UserService; + +/** + * Test case for operations in the Repository using in memory storage. + * + * @covers \Ibexa\Contracts\Core\Repository\Repository + * @group integration + */ +class RepositoryTest extends BaseTest +{ + /** + * Test for the getRepository() method. + */ + public function testGetRepository() + { + $this->assertInstanceOf(Repository::class, $this->getSetupFactory()->getRepository(true)); + } + + /** + * Test for the getContentService() method. + * + * @group content + * @group user + */ + public function testGetContentService() + { + $repository = $this->getRepository(); + self::assertInstanceOf( + ContentService::class, + $repository->getContentService() + ); + } + + /** + * Test for the getContentLanguageService() method. + * + * @group language + * + * @covers \Ibexa\Contracts\Core\Repository\Repository::getContentLanguageService() + */ + public function testGetContentLanguageService() + { + $repository = $this->getRepository(); + self::assertInstanceOf( + LanguageService::class, + $repository->getContentLanguageService() + ); + } + + /** + * Test for the getContentTypeService() method. + * + * @group content-type + * @group field-type + * @group user + */ + public function testGetContentTypeService() + { + $repository = $this->getRepository(); + self::assertInstanceOf( + ContentTypeService::class, + $repository->getContentTypeService() + ); + } + + /** + * Test for the getLocationService() method. + * + * @group location + */ + public function testGetLocationService() + { + $repository = $this->getRepository(); + self::assertInstanceOf( + LocationService::class, + $repository->getLocationService() + ); + } + + /** + * Test for the getSectionService() method. + * + * @group section + */ + public function testGetSectionService() + { + $repository = $this->getRepository(); + self::assertInstanceOf( + SectionService::class, + $repository->getSectionService() + ); + } + + /** + * Test for the getUserService() method. + * + * @group user + */ + public function testGetUserService() + { + $repository = $this->getRepository(); + self::assertInstanceOf( + UserService::class, + $repository->getUserService() + ); + } + + /** + * Test for the getNotificationService() method. + * + * @group user + */ + public function testGetNotificationService() + { + $repository = $this->getRepository(); + $this->assertInstanceOf( + NotificationService::class, + $repository->getNotificationService() + ); + } + + /** + * Test for the getTrashService() method. + * + * @group trash + */ + public function testGetTrashService() + { + $repository = $this->getRepository(); + self::assertInstanceOf( + TrashService::class, + $repository->getTrashService() + ); + } + + /** + * Test for the getRoleService() method. + * + * @group role + */ + public function testGetRoleService() + { + $repository = $this->getRepository(); + self::assertInstanceOf( + RoleService::class, + $repository->getRoleService() + ); + } + + /** + * Test for the getURLAliasService() method. + * + * @group url-alias + */ + public function testGetURLAliasService() + { + $repository = $this->getRepository(); + self::assertInstanceOf( + URLAliasService::class, + $repository->getURLAliasService() + ); + } + + /** + * Test for the getUrlWildcardService() method. + * + * @group url-wildcard + */ + public function testGetURLWildcardService() + { + $repository = $this->getRepository(); + self::assertInstanceOf( + URLWildcardService::class, + $repository->getURLWildcardService() + ); + } + + /** + * Test for the getObjectStateService(). + * + * @group object-state + */ + public function testGetObjectStateService() + { + $repository = $this->getRepository(); + self::assertInstanceOf( + ObjectStateService::class, + $repository->getObjectStateService() + ); + } + + /** + * Test for the getFieldTypeService(). + * + * @group object-state + */ + public function testGetFieldTypeService() + { + $repository = $this->getRepository(); + self::assertInstanceOf( + FieldTypeService::class, + $repository->getFieldTypeService() + ); + } + + /** + * Test for the getSearchService() method. + * + * @group search + */ + public function testGetSearchService() + { + $repository = $this->getRepository(); + + self::assertInstanceOf( + SearchService::class, + $repository->getSearchService() + ); + } + + /** + * Test for the getSearchService() method. + * + * @group permission + */ + public function testGetPermissionResolver() + { + $repository = $this->getRepository(); + + self::assertInstanceOf( + PermissionResolver::class, + $repository->getPermissionResolver() + ); + } + + /** + * Test for the commit() method. + */ + public function testCommit() + { + $repository = $this->getRepository(); + + try { + $repository->beginTransaction(); + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + } + + /** + * Test for the commit() method. + */ + public function testCommitThrowsRuntimeException() + { + $this->expectException(\RuntimeException::class); + + $repository = $this->getRepository(); + $repository->commit(); + } + + /** + * Test for the rollback() method. + */ + public function testRollback() + { + $repository = $this->getRepository(); + $repository->beginTransaction(); + $repository->rollback(); + } + + /** + * Test for the rollback() method. + */ + public function testRollbackThrowsRuntimeException() + { + $this->expectException(\RuntimeException::class); + + $repository = $this->getRepository(); + $repository->rollback(); + } +} + +class_alias(RepositoryTest::class, 'eZ\Publish\API\Repository\Tests\RepositoryTest'); diff --git a/eZ/Publish/API/Repository/Tests/RoleServiceAuthorizationTest.php b/tests/integration/Core/Repository/RoleServiceAuthorizationTest.php similarity index 76% rename from eZ/Publish/API/Repository/Tests/RoleServiceAuthorizationTest.php rename to tests/integration/Core/Repository/RoleServiceAuthorizationTest.php index fc5e475129..7f6ff1873b 100644 --- a/eZ/Publish/API/Repository/Tests/RoleServiceAuthorizationTest.php +++ b/tests/integration/Core/Repository/RoleServiceAuthorizationTest.php @@ -4,14 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; /** * Test case for operations in the RoleService using in memory storage. * - * @see eZ\Publish\API\Repository\RoleService + * @covers \Ibexa\Contracts\Core\Repository\RoleService * @group integration * @group authorization */ @@ -20,13 +21,13 @@ class RoleServiceAuthorizationTest extends BaseTest /** * Test for the createRole() method. * - * @see \eZ\Publish\API\Repository\RoleService::createRole() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testCreateRole - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRole() + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testCreateRole + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testCreateRoleThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -51,13 +52,13 @@ public function testCreateRoleThrowsUnauthorizedException() /** * Test for the loadRole() method. * - * @see \eZ\Publish\API\Repository\RoleService::loadRole() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRole - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::loadRole() + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testLoadRole + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testLoadRoleThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $roleService = $repository->getRoleService(); @@ -79,13 +80,13 @@ public function testLoadRoleThrowsUnauthorizedException() /** * Test for the loadRoleByIdentifier() method. * - * @see \eZ\Publish\API\Repository\RoleService::loadRoleByIdentifier() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testLoadRoleByIdentifier - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::loadRoleByIdentifier() + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testLoadRoleByIdentifier + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testLoadRoleByIdentifierThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $roleService = $repository->getRoleService(); @@ -107,7 +108,7 @@ public function testLoadRoleByIdentifierThrowsUnauthorizedException() /** * Test for the loadRoles() method. * - * @see \eZ\Publish\API\Repository\RoleService::loadRoles() + * @covers \Ibexa\Contracts\Core\Repository\RoleService::loadRoles() */ public function testLoadRolesLoadsEmptyListForAnonymousUser() { @@ -130,7 +131,7 @@ public function testLoadRolesLoadsEmptyListForAnonymousUser() /** * Test for the loadRoles() method. * - * @see \eZ\Publish\API\Repository\RoleService::loadRoles() + * @covers \Ibexa\Contracts\Core\Repository\RoleService::loadRoles() */ public function testLoadRolesForUserWithSubtreeLimitation() { @@ -160,13 +161,13 @@ public function testLoadRolesForUserWithSubtreeLimitation() /** * Test for the deleteRole() method. * - * @see \eZ\Publish\API\Repository\RoleService::deleteRole() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testDeleteRole - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::deleteRole() + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testDeleteRole + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testDeleteRoleThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $roleService = $repository->getRoleService(); @@ -188,13 +189,13 @@ public function testDeleteRoleThrowsUnauthorizedException() /** * Test for the updatePolicy() method. * - * @see \eZ\Publish\API\Repository\RoleService::updatePolicyByRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testUpdatePolicyByRoleDraft - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::updatePolicyByRoleDraft() + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testUpdatePolicyByRoleDraft + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testUpdatePolicyByRoleDraftThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $roleService = $repository->getRoleService(); @@ -235,13 +236,13 @@ public function testUpdatePolicyByRoleDraftThrowsUnauthorizedException() /** * Test for the removePolicy() method. * - * @see \eZ\Publish\API\Repository\RoleService::removePolicy() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testRemovePolicyByRoleDraft - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::removePolicy() + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testRemovePolicyByRoleDraft + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testRemovePolicyThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $roleService = $repository->getRoleService(); @@ -277,13 +278,13 @@ public function testRemovePolicyThrowsUnauthorizedException() /** * Test for the removePolicyByRoleDraft() method. * - * @see \eZ\Publish\API\Repository\RoleService::removePolicyByRoleDraft() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testRemovePolicyByRoleDraft - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::removePolicyByRoleDraft() + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testRemovePolicyByRoleDraft + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testDeletePolicyThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $roleService = $repository->getRoleService(); @@ -310,13 +311,13 @@ public function testDeletePolicyThrowsUnauthorizedException() /** * Test for the assignRoleToUserGroup() method. * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUserGroup() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUserGroup - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUserGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testAssignRoleToUserGroup + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testAssignRoleToUserGroupThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $userService = $repository->getUserService(); @@ -344,13 +345,13 @@ public function testAssignRoleToUserGroupThrowsUnauthorizedException() /** * Test for the assignRoleToUserGroup() method. * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUserGroup($role, $userGroup, $roleLimitation) - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUserGroup - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUserGroup($role, $userGroup, $roleLimitation) + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testAssignRoleToUserGroup + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testAssignRoleToUserGroupThrowsUnauthorizedExceptionWithRoleLimitationParameter() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $userService = $repository->getUserService(); @@ -385,13 +386,13 @@ public function testAssignRoleToUserGroupThrowsUnauthorizedExceptionWithRoleLimi /** * Test for the removeRoleAssignment() method. * - * @see \eZ\Publish\API\Repository\RoleService::removeRoleAssignment() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testRemoveRoleAssignmentFromUserGroup - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::removeRoleAssignment() + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testRemoveRoleAssignmentFromUserGroup + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testRemoveRoleAssignmentFromUserGroupThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $userService = $repository->getUserService(); @@ -428,13 +429,13 @@ public function testRemoveRoleAssignmentFromUserGroupThrowsUnauthorizedException /** * Test for the assignRoleToUser() method. * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUser() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUser - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUser() + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testAssignRoleToUser + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testAssignRoleToUserThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $roleService = $repository->getRoleService(); @@ -456,13 +457,13 @@ public function testAssignRoleToUserThrowsUnauthorizedException() /** * Test for the assignRoleToUser() method. * - * @see \eZ\Publish\API\Repository\RoleService::assignRoleToUser($role, $user, $roleLimitation) - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testAssignRoleToUser - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUser($role, $user, $roleLimitation) + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testAssignRoleToUser + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testAssignRoleToUserThrowsUnauthorizedExceptionWithRoleLimitationParameter() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $roleService = $repository->getRoleService(); @@ -491,13 +492,13 @@ public function testAssignRoleToUserThrowsUnauthorizedExceptionWithRoleLimitatio /** * Test for the removeRoleAssignment() method. * - * @see \eZ\Publish\API\Repository\RoleService::removeRoleAssignment() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testRemoveRoleAssignment - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::removeRoleAssignment() + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testRemoveRoleAssignment + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testRemoveRoleAssignmentThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $roleService = $repository->getRoleService(); @@ -527,13 +528,13 @@ public function testRemoveRoleAssignmentThrowsUnauthorizedException() /** * Test for the getRoleAssignments() method. * - * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignments() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testGetRoleAssignments - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::getRoleAssignments() + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testGetRoleAssignments + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testGetRoleAssignmentsThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $roleService = $repository->getRoleService(); @@ -555,7 +556,7 @@ public function testGetRoleAssignmentsThrowsUnauthorizedException() /** * Test for the getRoleAssignmentsForUser() method. * - * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignmentsForUser() + * @covers \Ibexa\Contracts\Core\Repository\RoleService::getRoleAssignmentsForUser() */ public function testGetRoleAssignmentsForUserLoadsEmptyListForAnonymousUser() { @@ -577,7 +578,7 @@ public function testGetRoleAssignmentsForUserLoadsEmptyListForAnonymousUser() /** * Test for the getRoleAssignmentsForUser() method. * - * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignmentsForUser() + * @covers \Ibexa\Contracts\Core\Repository\RoleService::getRoleAssignmentsForUser() */ public function testGetRoleAssignmentsForUserWithSubtreeLimitation() { @@ -606,13 +607,13 @@ public function testGetRoleAssignmentsForUserWithSubtreeLimitation() /** * Test for the getRoleAssignmentsForUserGroup() method. * - * @see \eZ\Publish\API\Repository\RoleService::getRoleAssignmentsForUserGroup() - * @depends eZ\Publish\API\Repository\Tests\RoleServiceTest::testGetRoleAssignmentsForUserGroup - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\RoleService::getRoleAssignmentsForUserGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\RoleServiceTest::testGetRoleAssignmentsForUserGroup + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testGetRoleAssignmentsForUserGroupThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $roleService = $repository->getRoleService(); @@ -640,7 +641,7 @@ public function testGetRoleAssignmentsForUserGroupThrowsUnauthorizedException() /** * Create a role fixture in a variable named <b>$role</b>,. * - * @return \eZ\Publish\API\Repository\Values\User\Role + * @return \Ibexa\Contracts\Core\Repository\Values\User\Role */ private function createRole() { @@ -670,3 +671,5 @@ private function createRole() return $role; } } + +class_alias(RoleServiceAuthorizationTest::class, 'eZ\Publish\API\Repository\Tests\RoleServiceAuthorizationTest'); diff --git a/tests/integration/Core/Repository/RoleServiceTest.php b/tests/integration/Core/Repository/RoleServiceTest.php new file mode 100644 index 0000000000..09d1aebd5c --- /dev/null +++ b/tests/integration/Core/Repository/RoleServiceTest.php @@ -0,0 +1,3081 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use Exception; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\RoleService; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\LanguageLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Policy; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\Role; +use Ibexa\Contracts\Core\Repository\Values\User\RoleAssignment; +use Ibexa\Contracts\Core\Repository\Values\User\RoleCopyStruct; +use Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\RoleDraft; +use Ibexa\Contracts\Core\Repository\Values\User\RoleUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroupRoleAssignment; +use Ibexa\Contracts\Core\Repository\Values\User\UserRoleAssignment; + +/** + * Test case for operations in the RoleService using in memory storage. + * + * The following IDs from the default Ibexa community edition database are used in + * this test: + * + * <ul> + * <li> + * ContentType + * <ul> + * <li><strong>28</strong>: File</li> + * <li><strong>29</strong>: Flash</li> + * <li><strong>30</strong>: Image</li> + * </ul> + * </li> + * <ul> + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService + * @group role + */ +class RoleServiceTest extends BaseTest +{ + /** + * Test for the newRoleCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::newRoleCreateStruct() + */ + public function testNewRoleCreateStruct() + { + $repository = $this->getRepository(); + + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('roleName'); + + $this->assertInstanceOf(RoleCreateStruct::class, $roleCreate); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\RoleService::newRoleCopyStruct + */ + public function testNewRoleCopyStruct(): void + { + $repository = $this->getRepository(); + + $roleService = $repository->getRoleService(); + $roleCopy = $roleService->newRoleCopyStruct('copiedRole'); + + $this->assertSame('copiedRole', $roleCopy->newIdentifier); + $this->assertSame([], $roleCopy->getPolicies()); + } + + /** + * Test for the newRoleCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::newRoleCreateStruct() + * @depends testNewRoleCreateStruct + */ + public function testNewRoleCreateStructSetsNamePropertyOnStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('roleName'); + + /* END: Use Case */ + + $this->assertEquals('roleName', $roleCreate->identifier); + } + + /** + * Test for the createRole() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRole() + * @depends testNewRoleCreateStruct + */ + public function testCreateRole() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('roleName'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $role = $roleService->createRole($roleCreate); + + /* END: Use Case */ + + self::assertInstanceOf( + RoleDraft::class, + $role + ); + + return [ + 'createStruct' => $roleCreate, + 'role' => $role, + ]; + } + + /** + * Test for the createRole() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRole() + * @depends testCreateRole + */ + public function testRoleCreateStructValues(array $data) + { + $createStruct = $data['createStruct']; + $role = $data['role']; + + $this->assertEquals( + [ + 'identifier' => $createStruct->identifier, + 'policies' => $createStruct->policies, + ], + [ + 'identifier' => $role->identifier, + 'policies' => $role->policies, + ] + ); + $this->assertNotNull($role->id); + + return $data; + } + + /** + * Test for the createRole() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRole() + * @depends testNewRoleCreateStruct + */ + public function testCreateRoleWithPolicy() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('roleName'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + // Create new subtree limitation + $limitation = new SubtreeLimitation( + [ + 'limitationValues' => ['/1/2/'], + ] + ); + + // Create policy create struct and add limitation to it + $policyCreate = $roleService->newPolicyCreateStruct('content', 'read'); + $policyCreate->addLimitation($limitation); + + // Add policy create struct to role create struct + $roleCreate->addPolicy($policyCreate); + + $role = $roleService->createRole($roleCreate); + + /* END: Use Case */ + + $this->assertInstanceOf( + RoleDraft::class, + $role + ); + + return [ + 'createStruct' => $roleCreate, + 'role' => $role, + ]; + } + + /** + * Test for the createRole() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRole() + * @depends testCreateRoleWithPolicy + */ + public function testRoleCreateStructValuesWithPolicy(array $data) + { + $createStruct = $data['createStruct']; + $role = $data['role']; + + $this->assertEquals( + [ + 'identifier' => $createStruct->identifier, + 'policy_module' => $createStruct->policies[0]->module, + 'policy_function' => $createStruct->policies[0]->function, + 'policy_limitation' => array_values($createStruct->policies[0]->limitations), + ], + [ + 'identifier' => $role->identifier, + 'policy_module' => $role->policies[0]->module, + 'policy_function' => $role->policies[0]->function, + 'policy_limitation' => array_values($role->policies[0]->limitations), + ] + ); + $this->assertNotNull($role->id); + + return $data; + } + + /** + * Test creating a role with multiple policies. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRole + */ + public function testCreateRoleWithMultiplePolicies() + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + $limitation1 = new Limitation\ContentTypeLimitation(); + $limitation1->limitationValues = ['1', '3', '13']; + + $limitation2 = new Limitation\SectionLimitation(); + $limitation2->limitationValues = ['2', '3']; + + $limitation3 = new Limitation\OwnerLimitation(); + $limitation3->limitationValues = ['1', '2']; + + $limitation4 = new Limitation\UserGroupLimitation(); + $limitation4->limitationValues = ['1']; + + $policyCreateStruct1 = $roleService->newPolicyCreateStruct('content', 'read'); + $policyCreateStruct1->addLimitation($limitation1); + $policyCreateStruct1->addLimitation($limitation2); + + $policyCreateStruct2 = $roleService->newPolicyCreateStruct('content', 'edit'); + $policyCreateStruct2->addLimitation($limitation3); + $policyCreateStruct2->addLimitation($limitation4); + + $roleCreateStruct = $roleService->newRoleCreateStruct('ultimate_permissions'); + $roleCreateStruct->addPolicy($policyCreateStruct1); + $roleCreateStruct->addPolicy($policyCreateStruct2); + + $createdRole = $roleService->createRole($roleCreateStruct); + + self::assertInstanceOf(Role::class, $createdRole); + self::assertGreaterThan(0, $createdRole->id); + + $this->assertPropertiesCorrect( + [ + 'identifier' => $roleCreateStruct->identifier, + ], + $createdRole + ); + + self::assertCount(2, $createdRole->getPolicies()); + + foreach ($createdRole->getPolicies() as $policy) { + self::assertInstanceOf(Policy::class, $policy); + self::assertGreaterThan(0, $policy->id); + self::assertEquals($createdRole->id, $policy->roleId); + + self::assertCount(2, $policy->getLimitations()); + + foreach ($policy->getLimitations() as $limitation) { + self::assertInstanceOf(Limitation::class, $limitation); + + if ($policy->module == 'content' && $policy->function == 'read') { + switch ($limitation->getIdentifier()) { + case Limitation::CONTENTTYPE: + self::assertEquals($limitation1->limitationValues, $limitation->limitationValues); + break; + + case Limitation::SECTION: + self::assertEquals($limitation2->limitationValues, $limitation->limitationValues); + break; + + default: + self::fail('Created role contains limitations not defined with create struct'); + } + } elseif ($policy->module == 'content' && $policy->function == 'edit') { + switch ($limitation->getIdentifier()) { + case Limitation::OWNER: + self::assertEquals($limitation3->limitationValues, $limitation->limitationValues); + break; + + case Limitation::USERGROUP: + self::assertEquals($limitation4->limitationValues, $limitation->limitationValues); + break; + + default: + self::fail('Created role contains limitations not defined with create struct'); + } + } else { + self::fail('Created role contains policy not defined with create struct'); + } + } + } + } + + /** + * Test for the createRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRoleDraft() + * @depends testNewRoleCreateStruct + */ + public function testCreateRoleDraft() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('roleName'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRole($roleDraft->id); + $newRoleDraft = $roleService->createRoleDraft($role); + + /* END: Use Case */ + + $this->assertInstanceOf( + RoleDraft::class, + $newRoleDraft + ); + } + + /** + * Test for the createRole() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRole() + * @depends testCreateRole + */ + public function testCreateRoleThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('Editor'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + // This call will fail with an InvalidArgumentException, because Editor exists + $roleService->createRole($roleCreate); + + /* END: Use Case */ + } + + /** + * Test for the createRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRoleDraft() + * @depends testCreateRoleDraft + */ + public function testCreateRoleDraftThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('Editor'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRole($roleDraft->id); + $roleService->createRoleDraft($role); // First role draft + + // This call will fail with an InvalidArgumentException, because there is already a draft + $roleService->createRoleDraft($role); + + /* END: Use Case */ + } + + /** + * Test for the createRole() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRole() + */ + public function testCreateRoleThrowsLimitationValidationException() + { + $this->expectException(LimitationValidationException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + // Create new role create struct + $roleCreate = $roleService->newRoleCreateStruct('Lumberjack'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + // Create new subtree limitation + $limitation = new SubtreeLimitation( + [ + 'limitationValues' => ['/mountain/forest/tree/42/'], + ] + ); + + // Create policy create struct and add limitation to it + $policyCreate = $roleService->newPolicyCreateStruct('content', 'remove'); + $policyCreate->addLimitation($limitation); + + // Add policy create struct to role create struct + $roleCreate->addPolicy($policyCreate); + + // This call will fail with an LimitationValidationException, because subtree + // "/mountain/forest/tree/42/" does not exist + $roleService->createRole($roleCreate); + /* END: Use Case */ + } + + /** + * Test for the createRole() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRole() + * @depends testNewRoleCreateStruct + */ + public function testCreateRoleInTransactionWithRollback() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + + $repository->beginTransaction(); + + $roleCreate = $roleService->newRoleCreateStruct('roleName'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $createdRoleId = $roleService->createRole($roleCreate)->id; + + $repository->rollback(); + + try { + // This call will fail with a "NotFoundException" + $role = $roleService->loadRole($createdRoleId); + } catch (NotFoundException $e) { + return; + } + /* END: Use Case */ + + $this->fail('Role object still exists after rollback.'); + } + + /** + * Test for the createRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRoleDraft() + * @depends testNewRoleCreateStruct + */ + public function testCreateRoleDraftInTransactionWithRollback() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + + $repository->beginTransaction(); + + $roleCreate = $roleService->newRoleCreateStruct('roleName'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $createdRoleId = $roleService->createRole($roleCreate)->id; + + $repository->rollback(); + + try { + // This call will fail with a "NotFoundException" + $role = $roleService->loadRoleDraft($createdRoleId); + } catch (NotFoundException $e) { + return; + } + /* END: Use Case */ + + $this->fail('Role draft object still exists after rollback.'); + } + + public function providerForCopyRoleTests(): array + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + $roleCreateStruct = $roleService->newRoleCreateStruct('newRole'); + $roleCopyStruct = $roleService->newRoleCopyStruct('copiedRole'); + + $policyCreateStruct1 = $roleService->newPolicyCreateStruct('content', 'read'); + $policyCreateStruct2 = $roleService->newPolicyCreateStruct('content', 'edit'); + + $roleLimitations = [ + new SectionLimitation(['limitationValues' => [2]]), + new SubtreeLimitation(['limitationValues' => ['/1/2/']]), + ]; + + $policyCreateStruct1WithLimitations = $roleService->newPolicyCreateStruct('content', 'read'); + foreach ($roleLimitations as $roleLimitation) { + $policyCreateStruct1WithLimitations->addLimitation($roleLimitation); + } + + return [ + 'without-policies' => [ + $roleCreateStruct, + $roleCopyStruct, + [], + ], + 'with-policies' => [ + $roleCreateStruct, + $roleCopyStruct, + [$policyCreateStruct1, $policyCreateStruct2], + ], + 'with-limitations' => [ + $roleCreateStruct, + $roleCopyStruct, + [$policyCreateStruct1WithLimitations, $policyCreateStruct2], + ], + ]; + } + + /** + * @dataProvider providerForCopyRoleTests + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::copyRole + * @depends testNewRoleCopyStruct + * @depends testLoadRoleByIdentifier + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException + */ + public function testCopyRole(RoleCreateStruct $roleCreateStruct, RoleCopyStruct $roleCopyStruct): void + { + $repository = $this->getRepository(); + + $roleService = $repository->getRoleService(); + + $roleDraft = $roleService->createRole($roleCreateStruct); + + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRole($roleDraft->id); + + $copiedRole = $roleService->copyRole($role, $roleCopyStruct); + + // Now verify that our change was saved + $role = $roleService->loadRoleByIdentifier('copiedRole'); + + $this->assertEquals($role->id, $copiedRole->id); + $this->assertEquals('copiedRole', $role->identifier); + $this->assertEmpty($role->getPolicies()); + } + + /** + * Test for the copyRole() method with added policies. + * + * @dataProvider providerForCopyRoleTests + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::copyRole + * @depends testNewRoleCopyStruct + * @depends testLoadRoleByIdentifier + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct[] $policies + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException + */ + public function testCopyRoleWithPolicies( + RoleCreateStruct $roleCreateStruct, + RoleCopyStruct $roleCopyStruct, + array $policies + ): void { + $repository = $this->getRepository(); + + $roleService = $repository->getRoleService(); + + foreach ($policies as $policy) { + $roleCreateStruct->addPolicy($policy); + } + + $roleDraft = $roleService->createRole($roleCreateStruct); + + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRole($roleDraft->id); + + $copiedRole = $roleService->copyRole($role, $roleCopyStruct); + + // Now verify that our change was saved + $role = $roleService->loadRoleByIdentifier('copiedRole'); + + $this->assertEquals($role->getPolicies(), $copiedRole->getPolicies()); + } + + /** + * Test for the copyRole() method with added policies and limitations. + * + * @dataProvider providerForCopyRoleTests + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::copyRole + * @depends testNewRoleCopyStruct + * @depends testLoadRoleByIdentifier + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct[] $policies + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException + */ + public function testCopyRoleWithPoliciesAndLimitations( + RoleCreateStruct $roleCreateStruct, + RoleCopyStruct $roleCopyStruct, + array $policies + ): void { + $repository = $this->getRepository(); + + $roleService = $repository->getRoleService(); + + foreach ($policies as $policy) { + $roleCreateStruct->addPolicy($policy); + } + + $roleDraft = $roleService->createRole($roleCreateStruct); + + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRole($roleDraft->id); + + $copiedRole = $roleService->copyRole($role, $roleCopyStruct); + + // Now verify that our change was saved + $role = $roleService->loadRoleByIdentifier('copiedRole'); + + $limitations = []; + foreach ($role->getPolicies() as $policy) { + $limitations[$policy->function] = $policy->getLimitations(); + } + + $limitationsCopied = []; + foreach ($copiedRole->getPolicies() as $policy) { + $limitationsCopied[$policy->function] = $policy->getLimitations(); + } + + $this->assertEquals($role->getPolicies(), $copiedRole->getPolicies()); + foreach ($limitations as $policy => $limitation) { + $this->assertEquals($limitation, $limitationsCopied[$policy]); + } + } + + /** + * Test for the loadRole() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::loadRole() + * @depends testCreateRole + */ + public function testLoadRole() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('roleName'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + + // Load the newly created role by its ID + $role = $roleService->loadRole($roleDraft->id); + + /* END: Use Case */ + + $this->assertEquals('roleName', $role->identifier); + } + + /** + * Test for the loadRoleDraft() method. + * + * @depends testCreateRoleDraft + */ + public function testLoadRoleDraft() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('roleName'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + + // Load the newly created role by its ID + $role = $roleService->loadRoleDraft($roleDraft->id); + + /* END: Use Case */ + + $this->assertEquals('roleName', $role->identifier); + } + + public function testLoadRoleDraftByRoleId() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('roleName'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $role = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($role); + + // Now create a new draft based on the role + $newDraft = $roleService->createRoleDraft($role); + $loadedRoleDraft = $roleService->loadRoleDraftByRoleId($role->id); + + /* END: Use Case */ + + self::assertEquals('roleName', $role->identifier); + self::assertInstanceOf(RoleDraft::class, $loadedRoleDraft); + self::assertEquals($newDraft, $loadedRoleDraft); + } + + /** + * Test for the loadRole() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::loadRole() + * @depends testLoadRole + */ + public function testLoadRoleThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $nonExistingRoleId = $this->generateId('role', self::DB_INT_MAX); + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + + // This call will fail with a NotFoundException, because no such role exists. + $roleService->loadRole($nonExistingRoleId); + + /* END: Use Case */ + } + + /** + * Test for the loadRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::loadRoleDraft() + * @depends testLoadRoleDraft + */ + public function testLoadRoleDraftThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $nonExistingRoleId = $this->generateId('role', self::DB_INT_MAX); + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + + // This call will fail with a NotFoundException, because no such role exists. + $roleService->loadRoleDraft($nonExistingRoleId); + + /* END: Use Case */ + } + + public function testLoadRoleDraftByRoleIdThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $nonExistingRoleId = $this->generateId('role', self::DB_INT_MAX); + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + + // This call will fail with a NotFoundException, because no such role exists. + $roleService->loadRoleDraftByRoleId($nonExistingRoleId); + + /* END: Use Case */ + } + + /** + * Test for the loadRoleByIdentifier() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::loadRoleByIdentifier() + * @depends testCreateRole + */ + public function testLoadRoleByIdentifier() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('roleName'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + + // Load the newly created role by its identifier + $role = $roleService->loadRoleByIdentifier('roleName'); + + /* END: Use Case */ + + $this->assertEquals('roleName', $role->identifier); + } + + /** + * Test for the loadRoleByIdentifier() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::loadRoleByIdentifier() + * @depends testLoadRoleByIdentifier + */ + public function testLoadRoleByIdentifierThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + $roleService = $repository->getRoleService(); + + // This call will fail with a NotFoundException, because no such role exists. + $roleService->loadRoleByIdentifier('MissingRole'); + + /* END: Use Case */ + } + + /** + * Test for the loadRoles() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::loadRoles() + * @depends testCreateRole + */ + public function testLoadRoles() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + + // First create a custom role + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('roleName'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + + // Now load all available roles + $roles = $roleService->loadRoles(); + + foreach ($roles as $role) { + if ($role->identifier === 'roleName') { + break; + } + } + + /* END: Use Case */ + + $this->assertEquals('roleName', $role->identifier); + } + + /** + * Test for the loadRoles() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::loadRoles() + * @depends testLoadRoles + */ + public function testLoadRolesReturnsExpectedSetOfDefaultRoles() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + $roles = $roleService->loadRoles(); + + $roleNames = []; + foreach ($roles as $role) { + $roleNames[] = $role->identifier; + } + /* END: Use Case */ + + $this->assertEqualsCanonicalizing( + [ + 'Administrator', + 'Anonymous', + 'Editor', + 'Member', + 'Partner', + ], + $roleNames + ); + } + + /** + * Test for the newRoleUpdateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::newRoleUpdateStruct() + */ + public function testNewRoleUpdateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + $roleUpdate = $roleService->newRoleUpdateStruct('newRole'); + /* END: Use Case */ + + self::assertInstanceOf(RoleUpdateStruct::class, $roleUpdate); + } + + /** + * Test for the updateRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::updateRoleDraft() + * @depends testNewRoleUpdateStruct + * @depends testLoadRoleDraft + */ + public function testUpdateRoleDraft() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('newRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + + $roleUpdate = $roleService->newRoleUpdateStruct(); + $roleUpdate->identifier = 'updatedRole'; + + $updatedRole = $roleService->updateRoleDraft($roleDraft, $roleUpdate); + /* END: Use Case */ + + // Now verify that our change was saved + $role = $roleService->loadRoleDraft($updatedRole->id); + + $this->assertEquals($role->identifier, 'updatedRole'); + } + + /** + * Test for the updateRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::updateRoleDraft() + * @depends testUpdateRoleDraft + */ + public function testUpdateRoleDraftThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('newRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + + $roleUpdate = $roleService->newRoleUpdateStruct(); + $roleUpdate->identifier = 'Editor'; + + // This call will fail with an InvalidArgumentException, because Editor is a predefined role + $roleService->updateRoleDraft($roleDraft, $roleUpdate); + /* END: Use Case */ + } + + /** + * Test for the deleteRole() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::deleteRole() + * @depends testCreateRole + * @depends testLoadRoles + */ + public function testDeleteRole() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('newRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRole($roleDraft->id); + + $roleService->deleteRole($role); + /* END: Use Case */ + + $this->assertCount(5, $roleService->loadRoles()); + } + + /** + * Test for the deleteRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::deleteRoleDraft() + * @depends testLoadRoleDraft + */ + public function testDeleteRoleDraft() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('newRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + $roleID = $roleDraft->id; + $roleService->deleteRoleDraft($roleDraft); + + // This call will fail with a NotFoundException, because the draft no longer exists + $roleService->loadRoleDraft($roleID); + /* END: Use Case */ + } + + /** + * Test for the newPolicyCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::newPolicyCreateStruct() + */ + public function testNewPolicyCreateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + $policyCreate = $roleService->newPolicyCreateStruct('content', 'create'); + /* END: Use Case */ + + self::assertInstanceOf(PolicyCreateStruct::class, $policyCreate); + } + + /** + * Test for the newPolicyCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::newPolicyCreateStruct() + * @depends testNewPolicyCreateStruct + */ + public function testNewPolicyCreateStructSetsStructProperties() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + $policyCreate = $roleService->newPolicyCreateStruct('content', 'create'); + /* END: Use Case */ + + $this->assertEquals( + ['content', 'create'], + [$policyCreate->module, $policyCreate->function] + ); + } + + /** + * Test for the addPolicyByRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::addPolicyByRoleDraft() + * @depends testCreateRoleDraft + * @depends testNewPolicyCreateStruct + */ + public function testAddPolicyByRoleDraft() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + $roleCreate = $roleService->newRoleCreateStruct('newRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + + $roleDraft = $roleService->addPolicyByRoleDraft( + $roleDraft, + $roleService->newPolicyCreateStruct('content', 'delete') + ); + $roleDraft = $roleService->addPolicyByRoleDraft( + $roleDraft, + $roleService->newPolicyCreateStruct('content', 'create') + ); + /* END: Use Case */ + + $actual = []; + foreach ($roleDraft->getPolicies() as $policy) { + $actual[] = [ + 'module' => $policy->module, + 'function' => $policy->function, + ]; + } + usort( + $actual, + static function ($p1, $p2) { + return strcasecmp($p1['function'], $p2['function']); + } + ); + + $this->assertEquals( + [ + [ + 'module' => 'content', + 'function' => 'create', + ], + [ + 'module' => 'content', + 'function' => 'delete', + ], + ], + $actual + ); + } + + /** + * Test for the addPolicyByRoleDraft() method. + * + * @return array [\Ibexa\Contracts\Core\Repository\Values\User\RoleDraft, \Ibexa\Contracts\Core\Repository\Values\User\Policy] + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::addPolicyByRoleDraft() + * @depends testAddPolicyByRoleDraft + */ + public function testAddPolicyByRoleDraftUpdatesRole() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + $roleCreate = $roleService->newRoleCreateStruct('newRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + + $policyCreate = $roleService->newPolicyCreateStruct('content', 'create'); + $roleDraft = $roleService->addPolicyByRoleDraft($roleDraft, $policyCreate); + + $policy = null; + foreach ($roleDraft->getPolicies() as $policy) { + if ($policy->module === 'content' && $policy->function === 'create') { + break; + } + } + /* END: Use Case */ + + $this->assertInstanceOf( + Policy::class, + $policy + ); + + return [$roleDraft, $policy]; + } + + /** + * Test for the addPolicyByRoleDraft() method. + * + * @param array $roleAndPolicy + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::addPolicyByRoleDraft() + * @depends testAddPolicyByRoleDraftUpdatesRole + */ + public function testAddPolicyByRoleDraftSetsPolicyProperties($roleAndPolicy) + { + list($role, $policy) = $roleAndPolicy; + + $this->assertEquals( + [$role->id, 'content', 'create'], + [$policy->roleId, $policy->module, $policy->function] + ); + } + + /** + * Test for the addPolicyByRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::addPolicyByRoleDraft() + * @depends testNewPolicyCreateStruct + * @depends testCreateRoleDraft + */ + public function testAddPolicyByRoleDraftThrowsLimitationValidationException() + { + $this->expectException(LimitationValidationException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + $roleCreate = $roleService->newRoleCreateStruct('Lumberjack'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + + // Create new subtree limitation + $limitation = new SubtreeLimitation( + [ + 'limitationValues' => ['/mountain/forest/tree/42/'], + ] + ); + + // Create policy create struct and add limitation to it + $policyCreateStruct = $roleService->newPolicyCreateStruct('content', 'remove'); + $policyCreateStruct->addLimitation($limitation); + + // This call will fail with an LimitationValidationException, because subtree + // "/mountain/forest/tree/42/" does not exist + $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct); + /* END: Use Case */ + } + + /** + * Test for the createRole() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRole() + * @depends testAddPolicyByRoleDraftUpdatesRole + */ + public function testCreateRoleWithAddPolicy() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + // Instantiate a new create struct + $roleCreate = $roleService->newRoleCreateStruct('newRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + // Add some role policies + $roleCreate->addPolicy( + $roleService->newPolicyCreateStruct( + 'content', + 'read' + ) + ); + $roleCreate->addPolicy( + $roleService->newPolicyCreateStruct( + 'content', + 'translate' + ) + ); + + // Create new role instance + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRole($roleDraft->id); + + $policies = []; + foreach ($role->getPolicies() as $policy) { + $policies[] = ['module' => $policy->module, 'function' => $policy->function]; + } + /* END: Use Case */ + array_multisort($policies); + + $this->assertEquals( + [ + [ + 'module' => 'content', + 'function' => 'read', + ], + [ + 'module' => 'content', + 'function' => 'translate', + ], + ], + $policies + ); + } + + /** + * Test for the createRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::createRoleDraft() + * @depends testAddPolicyByRoleDraftUpdatesRole + */ + public function testCreateRoleDraftWithAddPolicy() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + // Instantiate a new create struct + $roleCreate = $roleService->newRoleCreateStruct('newRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + // Add some role policies + $roleCreate->addPolicy( + $roleService->newPolicyCreateStruct( + 'content', + 'read' + ) + ); + $roleCreate->addPolicy( + $roleService->newPolicyCreateStruct( + 'content', + 'translate' + ) + ); + + // Create new role instance + $roleDraft = $roleService->createRole($roleCreate); + + $policies = []; + foreach ($roleDraft->getPolicies() as $policy) { + $policies[] = ['module' => $policy->module, 'function' => $policy->function]; + } + /* END: Use Case */ + + $this->assertEquals( + [ + [ + 'module' => 'content', + 'function' => 'read', + ], + [ + 'module' => 'content', + 'function' => 'translate', + ], + ], + $policies + ); + } + + /** + * Test for the newPolicyUpdateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::newPolicyUpdateStruct() + */ + public function testNewPolicyUpdateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + $policyUpdate = $roleService->newPolicyUpdateStruct(); + /* END: Use Case */ + + self::assertInstanceOf( + PolicyUpdateStruct::class, + $policyUpdate + ); + } + + public function testUpdatePolicyByRoleDraftNoLimitation() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + // Instantiate new policy create + $policyCreate = $roleService->newPolicyCreateStruct('foo', 'bar'); + + // Instantiate a role create and add the policy create + $roleCreate = $roleService->newRoleCreateStruct('myRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleCreate->addPolicy($policyCreate); + + // Create a new role instance. + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRole($roleDraft->id); + + $roleDraft = $roleService->createRoleDraft($role); + // Search for the new policy instance + $policy = null; + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy */ + foreach ($roleDraft->getPolicies() as $policy) { + if ($policy->module === 'foo' && $policy->function === 'bar') { + break; + } + } + + // Create an update struct + $policyUpdate = $roleService->newPolicyUpdateStruct(); + + // Update the the policy + $policy = $roleService->updatePolicyByRoleDraft( + $roleDraft, + $policy, + $policyUpdate + ); + $roleService->publishRoleDraft($roleDraft); + + /* END: Use Case */ + + $this->assertInstanceOf( + Policy::class, + $policy + ); + + self::assertEquals([], $policy->getLimitations()); + } + + /** + * @return array + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::updatePolicyByRoleDraft() + * @depends testAddPolicyByRoleDraft + * @depends testNewPolicyUpdateStruct + */ + public function testUpdatePolicyByRoleDraft() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + // Instantiate new policy create + $policyCreate = $roleService->newPolicyCreateStruct('content', 'translate'); + + // Add some limitations for the new policy + $policyCreate->addLimitation( + new LanguageLimitation( + [ + 'limitationValues' => ['eng-US', 'eng-GB'], + ] + ) + ); + + // Instantiate a role create and add the policy create + $roleCreate = $roleService->newRoleCreateStruct('myRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleCreate->addPolicy($policyCreate); + + // Create a new role instance. + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRole($roleDraft->id); + + $roleDraft = $roleService->createRoleDraft($role); + // Search for the new policy instance + $policy = null; + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy */ + foreach ($roleDraft->getPolicies() as $policy) { + if ($policy->module === 'content' && $policy->function === 'translate') { + break; + } + } + + // Create an update struct and set a modified limitation + $policyUpdate = $roleService->newPolicyUpdateStruct(); + $policyUpdate->addLimitation( + new ContentTypeLimitation( + [ + 'limitationValues' => [29, 30], + ] + ) + ); + + // Update the the policy + $policy = $roleService->updatePolicyByRoleDraft( + $roleDraft, + $policy, + $policyUpdate + ); + $roleService->publishRoleDraft($roleDraft); + + /* END: Use Case */ + + $this->assertInstanceOf( + Policy::class, + $policy + ); + + return [$roleService->loadRole($role->id), $policy]; + } + + /** + * @param array $roleAndPolicy + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::testUpdatePolicyByRoleDraft() + * @depends testUpdatePolicyByRoleDraft + */ + public function testUpdatePolicyUpdatesLimitations($roleAndPolicy) + { + list($role, $policy) = $roleAndPolicy; + + $this->assertEquals( + [ + new ContentTypeLimitation( + [ + 'limitationValues' => [29, 30], + ] + ), + ], + $policy->getLimitations() + ); + + return $role; + } + + /** + * Test for the updatePolicy() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\Role $role + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::updatePolicyByRoleDraft() + * @depends testUpdatePolicyUpdatesLimitations + */ + public function testUpdatePolicyUpdatesRole($role) + { + $limitations = []; + foreach ($role->getPolicies() as $policy) { + foreach ($policy->getLimitations() as $limitation) { + $limitations[] = $limitation; + } + } + + $this->assertCount(1, $limitations); + $this->assertInstanceOf( + Limitation::class, + $limitations[0] + ); + + $expectedData = [ + 'limitationValues' => [29, 30], + ]; + $this->assertPropertiesCorrectUnsorted( + $expectedData, + $limitations[0] + ); + } + + /** + * Test for the updatePolicy() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::updatePolicyByRoleDraft() + * @depends testAddPolicyByRoleDraft + * @depends testNewPolicyCreateStruct + * @depends testNewPolicyUpdateStruct + * @depends testNewRoleCreateStruct + * @depends testCreateRole + */ + public function testUpdatePolicyByRoleDraftThrowsLimitationValidationException() + { + $this->expectException(LimitationValidationException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + // Instantiate new policy create + $policyCreate = $roleService->newPolicyCreateStruct('content', 'remove'); + + // Add some limitations for the new policy + $policyCreate->addLimitation( + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/2/'], + ] + ) + ); + + // Instantiate a role create and add the policy create + $roleCreate = $roleService->newRoleCreateStruct('myRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleCreate->addPolicy($policyCreate); + + // Create a new role instance. + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRole($roleDraft->id); + $roleDraft = $roleService->createRoleDraft($role); + // Search for the new policy instance + $policy = null; + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy */ + foreach ($roleDraft->getPolicies() as $policy) { + if ($policy->module === 'content' && $policy->function === 'remove') { + break; + } + } + + // Create an update struct and set a modified limitation + $policyUpdate = $roleService->newPolicyUpdateStruct(); + $policyUpdate->addLimitation( + new SubtreeLimitation( + [ + 'limitationValues' => ['/mountain/forest/tree/42/'], + ] + ) + ); + + // This call will fail with an LimitationValidationException, because subtree + // "/mountain/forest/tree/42/" does not exist + $policy = $roleService->updatePolicyByRoleDraft( + $roleDraft, + $policy, + $policyUpdate + ); + /* END: Use Case */ + } + + /** + * Test for the removePolicyByRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::removePolicyByRoleDraft() + * @depends testAddPolicyByRoleDraft + */ + public function testRemovePolicyByRoleDraft() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + // Instantiate a new role create + $roleCreate = $roleService->newRoleCreateStruct('newRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + // Create a new role with two policies + $roleDraft = $roleService->createRole($roleCreate); + $roleService->addPolicyByRoleDraft( + $roleDraft, + $roleService->newPolicyCreateStruct('content', 'create') + ); + $roleService->addPolicyByRoleDraft( + $roleDraft, + $roleService->newPolicyCreateStruct('content', 'delete') + ); + + // Delete all policies from the new role + foreach ($roleDraft->getPolicies() as $policy) { + $roleDraft = $roleService->removePolicyByRoleDraft($roleDraft, $policy); + } + /* END: Use Case */ + + $this->assertSame([], $roleDraft->getPolicies()); + } + + /** + * Test for the addPolicyByRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::addPolicyByRoleDraft() + */ + public function testAddPolicyWithRoleAssignment() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + $userService = $repository->getUserService(); + + /* Create new user group */ + $mainGroupId = $this->generateId('group', 4); + $parentUserGroup = $userService->loadUserGroup($mainGroupId); + $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); + $userGroupCreate->setField('name', 'newUserGroup'); + $userGroup = $userService->createUserGroup($userGroupCreate, $parentUserGroup); + + /* Create Role */ + $roleCreate = $roleService->newRoleCreateStruct('newRole'); + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + + $role = $roleService->loadRole($roleDraft->id); + $roleService->assignRoleToUserGroup($role, $userGroup); + + $roleAssignmentsBeforeNewPolicy = $roleService->getRoleAssignments($role)[0]; + + /* Add new policy to existing role */ + $roleUpdateDraft = $roleService->createRoleDraft($role); + $roleUpdateDraft = $roleService->addPolicyByRoleDraft( + $roleUpdateDraft, + $roleService->newPolicyCreateStruct('content', 'create') + ); + $roleService->publishRoleDraft($roleUpdateDraft); + + $roleAfterUpdate = $roleService->loadRole($role->id); + $roleAssignmentsAfterNewPolicy = $roleService->getRoleAssignments($roleAfterUpdate)[0]; + /* END: Use Case */ + + $this->assertNotEquals($roleAssignmentsBeforeNewPolicy->id, $roleAssignmentsAfterNewPolicy->id); + } + + /** + * Test loading user/group role assignments. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroupRoleAssignment + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::loadRoleAssignment + */ + public function testLoadRoleAssignment() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + $user = $repository->getUserService()->loadUser(14); + + // Check initial empty assignments (also warms up potential cache to validate it is correct below) + $this->assertCount(0, $roleService->getRoleAssignmentsForUser($user)); + + // Assignment to user group + $groupRoleAssignment = $roleService->loadRoleAssignment(25); + + // Assignment to user + $role = $roleService->loadRole(2); + $roleService->assignRoleToUser($role, $user); + $userRoleAssignments = $roleService->getRoleAssignmentsForUser($user); + + $userRoleAssignment = $roleService->loadRoleAssignment($userRoleAssignments[0]->id); + /* END: Use Case */ + + self::assertInstanceOf(UserGroupRoleAssignment::class, $groupRoleAssignment); + + $this->assertEquals( + [ + 12, + 2, + 25, + ], + [ + $groupRoleAssignment->userGroup->id, + $groupRoleAssignment->role->id, + $groupRoleAssignment->id, + ] + ); + + self::assertInstanceOf(UserRoleAssignment::class, $userRoleAssignment); + self::assertEquals(14, $userRoleAssignment->user->id); + + return $groupRoleAssignment; + } + + /** + * Test for the getRoleAssignments() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\RoleAssignment[] + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::getRoleAssignments() + * @depends testLoadRoleByIdentifier + */ + public function testGetRoleAssignments() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + // Load the editor role + $role = $roleService->loadRoleByIdentifier('Editor'); + + // Load all assigned users and user groups + $roleAssignments = $roleService->getRoleAssignments($role); + + /* END: Use Case */ + + $this->assertCount(2, $roleAssignments); + $this->assertInstanceOf( + UserGroupRoleAssignment::class, + $roleAssignments[0] + ); + $this->assertInstanceOf( + UserGroupRoleAssignment::class, + $roleAssignments[1] + ); + + return $roleAssignments; + } + + /** + * Test for the getRoleAssignments() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleAssignment[] $roleAssignments + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::getRoleAssignments + * @depends testGetRoleAssignments + */ + public function testGetRoleAssignmentsContainExpectedLimitation(array $roleAssignments) + { + $this->assertEquals( + 'Subtree', + reset($roleAssignments)->limitation->getIdentifier() + ); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\RoleService::loadRoleAssignments() + */ + public function testLoadRoleAssignments(): void + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + $role = $this->createRoleWithPolicies('testLoadRoleAssignments', []); + $user = $this->createUser('test', 'Test', 'Test'); + $user2 = $this->createUser('test2', 'Test2', 'Test2'); + + $roleService->assignRoleToUser($role, $user); + $roleService->assignRoleToUser($role, $user2); + + $loadedRole = $roleService->loadRole($role->id); + + $roleAssignments = $roleService->loadRoleAssignments($loadedRole, 0, 1); + + self::assertCount(1, $roleAssignments); + self::assertInstanceOf(UserRoleAssignment::class, $roleAssignments[0]); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\RoleService::countRoleAssignments() + */ + public function testLoadRoleAssignmentsWithDeletedUser(): void + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + $userService = $repository->getUserService(); + + $role = $roleService->loadRoleByIdentifier('Editor'); + $roleAssignments = $roleService->loadRoleAssignments($role); + $expectedCount = count($roleAssignments); + + // Adding user should add '1' to the assignments count + $newUser = $this->createUser('login', 'Test', 'Test'); + $roleService->assignRoleToUser($role, $newUser); + ++$expectedCount; + + $roleAssignments = $roleService->loadRoleAssignments($role); + + self::assertCount($expectedCount, $roleAssignments); + + // Removing user should subtract '1' from the assignments count + $userService->deleteUser($newUser); + --$expectedCount; + + $roleAssignments = $roleService->loadRoleAssignments($role); + + self::assertCount($expectedCount, $roleAssignments); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + public function testCountRoleAssignmentsAfterRemovingRoleAssignment(): void + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + $role = $roleService->loadRoleByIdentifier('Editor'); + $newUser = $this->createUser('login', 'Test', 'Test'); + $roleService->assignRoleToUser($role, $newUser); + $roleAssignmentsCount = $roleService->countRoleAssignments($role); + + $userRoleAssignment = $this->loadRoleAssignmentForUser($roleService, $role, $newUser); + $roleService->removeRoleAssignment($userRoleAssignment); + + self::assertEquals($roleAssignmentsCount - 1, $roleService->countRoleAssignments($role)); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + public function testCountRoleAssignmentsAfterDeletingUser(): void + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + $userService = $repository->getUserService(); + + $role = $roleService->loadRoleByIdentifier('Editor'); + $newUser = $this->createUser('login', 'Test', 'Test'); + $roleService->assignRoleToUser($role, $newUser); + + $roleAssignmentsCount = $roleService->countRoleAssignments($role); + + $userService->deleteUser($newUser); + $afterUserDeleteCount = $roleService->countRoleAssignments($role); + + self::assertEquals($roleAssignmentsCount - 1, $afterUserDeleteCount); + } + + /** + * Test for the assignRoleToUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUser() + * @depends testGetRoleAssignments + */ + public function testAssignRoleToUser() + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Load the existing "Administrator" role + $role = $roleService->loadRoleByIdentifier('Administrator'); + + // Assign the "Administrator" role to the newly created user + $roleService->assignRoleToUser($role, $user); + + // The assignments array will contain the new role<->user assignment + $roleAssignments = $roleService->getRoleAssignments($role); + /* END: Use Case */ + + // Administrator + Example User + $this->assertCount(2, $roleAssignments); + } + + /** + * Test for the assignRoleToUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUser($role, $user, $roleLimitation) + * @depends testAssignRoleToUser + */ + public function testAssignRoleToUserWithRoleLimitation() + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Load the existing "Anonymous" role + $role = $roleService->loadRoleByIdentifier('Anonymous'); + + // Assign the "Anonymous" role to the newly created user + $roleService->assignRoleToUser( + $role, + $user, + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/43/'], + ] + ) + ); + + // The assignments array will contain the new role<->user assignment + $roleAssignments = $roleService->getRoleAssignments($role); + /* END: Use Case */ + + // Members + Partners + Anonymous + Example User + $this->assertCount(4, $roleAssignments); + + // Get the role limitation + $roleLimitation = null; + foreach ($roleAssignments as $roleAssignment) { + $roleLimitation = $roleAssignment->getRoleLimitation(); + if ($roleLimitation) { + $this->assertInstanceOf( + UserRoleAssignment::class, + $roleAssignment + ); + break; + } + } + + $this->assertEquals( + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/43/'], + ] + ), + $roleLimitation + ); + + // Test again to see values being merged + $roleService->assignRoleToUser( + $role, + $user, + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/43/', '/1/2/'], + ] + ) + ); + + // The assignments array will contain the new role<->user assignment + $roleAssignments = $roleService->getRoleAssignments($role); + + // Members + Partners + Anonymous + Example User + $this->assertCount(5, $roleAssignments); + + // Get the role limitation + $roleLimitations = []; + foreach ($roleAssignments as $roleAssignment) { + $roleLimitation = $roleAssignment->getRoleLimitation(); + if ($roleLimitation) { + $this->assertInstanceOf( + UserRoleAssignment::class, + $roleAssignment + ); + $roleLimitations[] = $roleLimitation; + } + } + array_multisort($roleLimitations); + + $this->assertEquals( + [ + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/2/'], + ] + ), + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/43/'], + ] + ), + ], + $roleLimitations + ); + } + + /** + * Test for the assignRoleToUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUser($role, $user, $roleLimitation) + * @depends testAssignRoleToUser + * @depends testLoadRoleByIdentifier + */ + public function testAssignRoleToUserWithRoleLimitationThrowsLimitationValidationException() + { + $this->expectException(LimitationValidationException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + // Load the existing "Anonymous" role + $role = $roleService->loadRoleByIdentifier('Anonymous'); + + // Get current user + $permissionResolver = $this->getRepository()->getPermissionResolver(); + $userService = $repository->getUserService(); + $currentUser = $userService->loadUser($permissionResolver->getCurrentUserReference()->getUserId()); + + // Assign the "Anonymous" role to the current user + // This call will fail with an LimitationValidationException, because subtree "/lorem/ipsum/42/" + // does not exists + $roleService->assignRoleToUser( + $role, + $currentUser, + new SubtreeLimitation( + [ + 'limitationValues' => ['/lorem/ipsum/42/'], + ] + ) + ); + /* END: Use Case */ + } + + /** + * Test for the assignRoleToUser() method. + * + * Makes sure assigning role several times throws. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUser($role, $user, $roleLimitation) + * @depends testAssignRoleToUser + * @depends testLoadRoleByIdentifier + */ + public function testAssignRoleToUserThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + // Load the existing "Anonymous" role + $role = $roleService->loadRoleByIdentifier('Anonymous'); + + // Get current user + $permissionResolver = $this->getRepository()->getPermissionResolver(); + $userService = $repository->getUserService(); + $currentUser = $userService->loadUser($permissionResolver->getCurrentUserReference()->getUserId()); + + // Assign the "Anonymous" role to the current user + try { + $roleService->assignRoleToUser( + $role, + $currentUser + ); + } catch (Exception $e) { + $this->fail('Got exception at first valid attempt to assign role'); + } + + // Re-Assign the "Anonymous" role to the current user + // This call will fail with an InvalidArgumentException, because limitation is already assigned + $roleService->assignRoleToUser( + $role, + $currentUser + ); + /* END: Use Case */ + } + + /** + * Test for the assignRoleToUser() method. + * + * Makes sure assigning role several times with same limitations throws. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUser($role, $user, $roleLimitation) + * @depends testAssignRoleToUser + * @depends testLoadRoleByIdentifier + */ + public function testAssignRoleToUserWithRoleLimitationThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + + // Load the existing "Anonymous" role + $role = $roleService->loadRoleByIdentifier('Anonymous'); + + // Get current user + $permissionResolver = $this->getRepository()->getPermissionResolver(); + $userService = $repository->getUserService(); + $currentUser = $userService->loadUser($permissionResolver->getCurrentUserReference()->getUserId()); + + // Assign the "Anonymous" role to the current user + try { + $roleService->assignRoleToUser( + $role, + $currentUser, + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/43/', '/1/2/'], + ] + ) + ); + } catch (Exception $e) { + $this->fail('Got exception at first valid attempt to assign role'); + } + + // Re-Assign the "Anonymous" role to the current user + // This call will fail with an InvalidArgumentException, because limitation is already assigned + $roleService->assignRoleToUser( + $role, + $currentUser, + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/43/'], + ] + ) + ); + /* END: Use Case */ + } + + /** + * Test for the removeRoleAssignment() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::removeRoleAssignment() + * @depends testAssignRoleToUser + */ + public function testRemoveRoleAssignment() + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Load the existing "Member" role + $role = $roleService->loadRoleByIdentifier('Member'); + + // Assign the "Member" role to the newly created user + $roleService->assignRoleToUser($role, $user); + + // Unassign user from role + $roleAssignments = $roleService->getRoleAssignmentsForUser($user); + foreach ($roleAssignments as $roleAssignment) { + if ($roleAssignment->role->id === $role->id) { + $roleService->removeRoleAssignment($roleAssignment); + } + } + // The assignments array will not contain the new role<->user assignment + $roleAssignments = $roleService->getRoleAssignments($role); + /* END: Use Case */ + + // Members + Editors + Partners + $this->assertCount(3, $roleAssignments); + } + + /** + * Test for the getRoleAssignmentsForUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::getRoleAssignmentsForUser() + * @depends testAssignRoleToUser + * @depends testCreateRoleWithAddPolicy + */ + public function testGetRoleAssignmentsForUserDirect() + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Instantiate a role create and add some policies + $roleCreate = $roleService->newRoleCreateStruct('Example Role'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleCreate->addPolicy( + $roleService->newPolicyCreateStruct('user', 'login') + ); + $roleCreate->addPolicy( + $roleService->newPolicyCreateStruct('content', 'read') + ); + $roleCreate->addPolicy( + $roleService->newPolicyCreateStruct('content', 'edit') + ); + + // Create the new role instance + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRole($roleDraft->id); + + // Check inital empty assigments (also warms up potential cache to validate it is correct below) + $this->assertCount(0, $roleService->getRoleAssignmentsForUser($user)); + $this->assertCount(0, $roleService->getRoleAssignments($role)); + + // Assign role to new user + $roleService->assignRoleToUser($role, $user); + + // Load the currently assigned role + $roleAssignments = $roleService->getRoleAssignmentsForUser($user); + /* END: Use Case */ + + $this->assertCount(1, $roleAssignments); + $this->assertInstanceOf( + UserRoleAssignment::class, + reset($roleAssignments) + ); + $this->assertCount(1, $roleService->getRoleAssignments($role)); + } + + /** + * Test for the getRoleAssignmentsForUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::getRoleAssignmentsForUser() + * @depends testAssignRoleToUser + * @depends testCreateRoleWithAddPolicy + */ + public function testGetRoleAssignmentsForUserEmpty() + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + /* BEGIN: Use Case */ + $permissionResolver = $this->getRepository()->getPermissionResolver(); + $userService = $repository->getUserService(); + $adminUser = $userService->loadUser($permissionResolver->getCurrentUserReference()->getUserId()); + + // Load the currently assigned role + $roleAssignments = $roleService->getRoleAssignmentsForUser($adminUser); + /* END: Use Case */ + + $this->assertCount(0, $roleAssignments); + } + + /** + * Test for the getRoleAssignmentsForUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::getRoleAssignmentsForUser() + * @depends testAssignRoleToUser + * @depends testCreateRoleWithAddPolicy + */ + public function testGetRoleAssignmentsForUserInherited() + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + /* BEGIN: Use Case */ + $permissionResolver = $this->getRepository()->getPermissionResolver(); + $userService = $repository->getUserService(); + $adminUser = $userService->loadUser($permissionResolver->getCurrentUserReference()->getUserId()); + + // Load the currently assigned role + inherited role assignments + $roleAssignments = $roleService->getRoleAssignmentsForUser($adminUser, true); + /* END: Use Case */ + + $this->assertCount(1, $roleAssignments); + $this->assertInstanceOf( + UserGroupRoleAssignment::class, + reset($roleAssignments) + ); + } + + /** + * Test for the assignRoleToUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUserGroup() + * @depends testGetRoleAssignments + */ + public function testAssignRoleToUserGroup() + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + /* BEGIN: Use Case */ + $userGroup = $this->createUserGroupVersion1(); + + // Load the existing "Administrator" role + $role = $roleService->loadRoleByIdentifier('Administrator'); + + // Assign the "Administrator" role to the newly created user group + $roleService->assignRoleToUserGroup($role, $userGroup); + + // The assignments array will contain the new role<->group assignment + $roleAssignments = $roleService->getRoleAssignments($role); + /* END: Use Case */ + + // Administrator + Example Group + $this->assertCount(2, $roleAssignments); + } + + /** + * Test for the assignRoleToUserGroup() method. + * + * Related issue: EZP-29113 + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUserGroup() + */ + public function testAssignRoleToUserGroupAffectsRoleAssignmentsForUser() + { + $roleService = $this->getRepository()->getRoleService(); + + /* BEGIN: Use Case */ + $userGroup = $this->createUserGroupVersion1(); + $user = $this->createUser('user', 'John', 'Doe', $userGroup); + + $initRoleAssignments = $roleService->getRoleAssignmentsForUser($user, true); + + // Load the existing "Administrator" role + $role = $roleService->loadRoleByIdentifier('Administrator'); + + // Assign the "Administrator" role to the newly created user group + $roleService->assignRoleToUserGroup($role, $userGroup); + + $updatedRoleAssignments = $roleService->getRoleAssignmentsForUser($user, true); + /* END: Use Case */ + + $this->assertEmpty($initRoleAssignments); + $this->assertCount(1, $updatedRoleAssignments); + } + + /** + * Test for the assignRoleToUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUserGroup($role, $userGroup, $roleLimitation) + * @depends testAssignRoleToUserGroup + */ + public function testAssignRoleToUserGroupWithRoleLimitation() + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + /* BEGIN: Use Case */ + $userGroup = $this->createUserGroupVersion1(); + + // Load the existing "Anonymous" role + $role = $roleService->loadRoleByIdentifier('Anonymous'); + + // Assign the "Anonymous" role to the newly created user group + $roleService->assignRoleToUserGroup( + $role, + $userGroup, + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/43/'], + ] + ) + ); + + // The assignments array will contain the new role<->group assignment + $roleAssignments = $roleService->getRoleAssignments($role); + /* END: Use Case */ + + // Members + Partners + Anonymous + Example Group + $this->assertCount(4, $roleAssignments); + + // Get the role limitation + $roleLimitation = null; + foreach ($roleAssignments as $roleAssignment) { + $roleLimitation = $roleAssignment->getRoleLimitation(); + if ($roleLimitation) { + break; + } + } + + $this->assertEquals( + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/43/'], + ] + ), + $roleLimitation + ); + + // Test again to see values being merged + $roleService->assignRoleToUserGroup( + $role, + $userGroup, + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/43/', '/1/2/'], + ] + ) + ); + + // The assignments array will contain the new role<->user assignment + $roleAssignments = $roleService->getRoleAssignments($role); + + // Members + Partners + Anonymous + Example User + $this->assertCount(5, $roleAssignments); + + // Get the role limitation + $roleLimitations = []; + foreach ($roleAssignments as $roleAssignment) { + $roleLimitation = $roleAssignment->getRoleLimitation(); + if ($roleLimitation) { + $this->assertInstanceOf( + UserGroupRoleAssignment::class, + $roleAssignment + ); + $roleLimitations[] = $roleLimitation; + } + } + array_multisort($roleLimitations); + + $this->assertEquals( + [ + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/2/'], + ] + ), + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/43/'], + ] + ), + ], + $roleLimitations + ); + } + + /** + * Test for the assignRoleToUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUserGroup($role, $userGroup, $roleLimitation) + * @depends testLoadRoleByIdentifier + * @depends testAssignRoleToUserGroup + */ + public function testAssignRoleToUserGroupWithRoleLimitationThrowsLimitationValidationException() + { + $this->expectException(LimitationValidationException::class); + + $repository = $this->getRepository(); + + $mainGroupId = $this->generateId('group', 4); + /* BEGIN: Use Case */ + // $mainGroupId is the ID of the main "Users" group + + $userService = $repository->getUserService(); + $roleService = $repository->getRoleService(); + + $userGroup = $userService->loadUserGroup($mainGroupId); + + // Load the existing "Anonymous" role + $role = $roleService->loadRoleByIdentifier('Anonymous'); + + // Assign the "Anonymous" role to the newly created user group + // This call will fail with an LimitationValidationException, because subtree "/lorem/ipsum/42/" + // does not exists + $roleService->assignRoleToUserGroup( + $role, + $userGroup, + new SubtreeLimitation( + [ + 'limitationValues' => ['/lorem/ipsum/42/'], + ] + ) + ); + /* END: Use Case */ + } + + /** + * Test for the assignRoleToUserGroup() method. + * + * Makes sure assigning role several times throws. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUserGroup($role, $userGroup, $roleLimitation) + * @depends testLoadRoleByIdentifier + * @depends testAssignRoleToUserGroup + */ + public function testAssignRoleToUserGroupThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $mainGroupId = $this->generateId('group', 4); + /* BEGIN: Use Case */ + // $mainGroupId is the ID of the main "Users" group + + $userService = $repository->getUserService(); + $roleService = $repository->getRoleService(); + + $userGroup = $userService->loadUserGroup($mainGroupId); + + // Load the existing "Anonymous" role + $role = $roleService->loadRoleByIdentifier('Anonymous'); + + // Assign the "Anonymous" role to the newly created user group + try { + $roleService->assignRoleToUserGroup( + $role, + $userGroup + ); + } catch (Exception $e) { + $this->fail('Got exception at first valid attempt to assign role'); + } + + // Re-Assign the "Anonymous" role to the newly created user group + // This call will fail with an InvalidArgumentException, because role is already assigned + $roleService->assignRoleToUserGroup( + $role, + $userGroup + ); + /* END: Use Case */ + } + + /** + * Test for the assignRoleToUserGroup() method. + * + * Makes sure assigning role several times with same limitations throws. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::assignRoleToUserGroup($role, $userGroup, $roleLimitation) + * @depends testLoadRoleByIdentifier + * @depends testAssignRoleToUserGroup + */ + public function testAssignRoleToUserGroupWithRoleLimitationThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $mainGroupId = $this->generateId('group', 4); + /* BEGIN: Use Case */ + // $mainGroupId is the ID of the main "Users" group + + $userService = $repository->getUserService(); + $roleService = $repository->getRoleService(); + + $userGroup = $userService->loadUserGroup($mainGroupId); + + // Load the existing "Anonymous" role + $role = $roleService->loadRoleByIdentifier('Anonymous'); + + // Assign the "Anonymous" role to the newly created user group + try { + $roleService->assignRoleToUserGroup( + $role, + $userGroup, + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/43/', '/1/2/'], + ] + ) + ); + } catch (Exception $e) { + $this->fail('Got exception at first valid attempt to assign role'); + } + + // Re-Assign the "Anonymous" role to the newly created user group + // This call will fail with an InvalidArgumentException, because limitation is already assigned + $roleService->assignRoleToUserGroup( + $role, + $userGroup, + new SubtreeLimitation( + [ + 'limitationValues' => ['/1/43/'], + ] + ) + ); + /* END: Use Case */ + } + + /** + * Test for the removeRoleAssignment() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::removeRoleAssignment() + * @depends testAssignRoleToUserGroup + */ + public function testRemoveRoleAssignmentFromUserGroup() + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + /* BEGIN: Use Case */ + $userGroup = $this->createUserGroupVersion1(); + + // Load the existing "Member" role + $role = $roleService->loadRoleByIdentifier('Member'); + + // Assign the "Member" role to the newly created user group + $roleService->assignRoleToUserGroup($role, $userGroup); + + // Unassign group from role + $roleAssignments = $roleService->getRoleAssignmentsForUserGroup($userGroup); + + // This call will fail with an "UnauthorizedException" + foreach ($roleAssignments as $roleAssignment) { + if ($roleAssignment->role->id === $role->id) { + $roleService->removeRoleAssignment($roleAssignment); + } + } + // The assignments array will not contain the new role<->group assignment + $roleAssignments = $roleService->getRoleAssignments($role); + /* END: Use Case */ + + // Members + Editors + Partners + $this->assertCount(3, $roleAssignments); + } + + /** + * Test unassigning role by assignment. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::removeRoleAssignment + */ + public function testUnassignRoleByAssignment() + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + $role = $roleService->loadRole(2); + $user = $repository->getUserService()->loadUser(14); + + $originalAssignmentCount = count($roleService->getRoleAssignmentsForUser($user)); + + $roleService->assignRoleToUser($role, $user); + $newAssignmentCount = count($roleService->getRoleAssignmentsForUser($user)); + self::assertEquals($originalAssignmentCount + 1, $newAssignmentCount); + + $assignments = $roleService->getRoleAssignmentsForUser($user); + $roleService->removeRoleAssignment($assignments[0]); + $finalAssignmentCount = count($roleService->getRoleAssignmentsForUser($user)); + self::assertEquals($newAssignmentCount - 1, $finalAssignmentCount); + } + + /** + * Test unassigning role by assignment. + * + * But on current admin user so he lacks access to read roles. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::removeRoleAssignment + */ + public function testUnassignRoleByAssignmentThrowsUnauthorizedException() + { + $this->expectException(UnauthorizedException::class); + + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + try { + $adminUserGroup = $repository->getUserService()->loadUserGroup(12); + $assignments = $roleService->getRoleAssignmentsForUserGroup($adminUserGroup); + $roleService->removeRoleAssignment($assignments[0]); + } catch (Exception $e) { + self::fail( + 'Unexpected exception: ' . $e->getMessage() . " \n[" . $e->getFile() . ' (' . $e->getLine() . ')]' + ); + } + + $roleService->removeRoleAssignment($assignments[0]); + } + + /** + * Test unassigning role by non-existing assignment. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::removeRoleAssignment + */ + public function testUnassignRoleByAssignmentThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + try { + $editorsUserGroup = $repository->getUserService()->loadUserGroup(13); + $assignments = $roleService->getRoleAssignmentsForUserGroup($editorsUserGroup); + $roleService->removeRoleAssignment($assignments[0]); + } catch (Exception $e) { + self::fail( + 'Unexpected exception: ' . $e->getMessage() . " \n[" . $e->getFile() . ' (' . $e->getLine() . ')]' + ); + } + + $roleService->removeRoleAssignment($assignments[0]); + } + + /** + * Test for the getRoleAssignmentsForUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::getRoleAssignmentsForUserGroup() + * @depends testAssignRoleToUserGroup + * @depends testCreateRoleWithAddPolicy + */ + public function testGetRoleAssignmentsForUserGroup() + { + $repository = $this->getRepository(); + $roleService = $repository->getRoleService(); + + /* BEGIN: Use Case */ + $userGroup = $this->createUserGroupVersion1(); + + // Instantiate a role create and add some policies + $roleCreate = $roleService->newRoleCreateStruct('Example Role'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleCreate->addPolicy( + $roleService->newPolicyCreateStruct('user', 'login') + ); + $roleCreate->addPolicy( + $roleService->newPolicyCreateStruct('content', 'read') + ); + $roleCreate->addPolicy( + $roleService->newPolicyCreateStruct('content', 'edit') + ); + + // Create the new role instance + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRole($roleDraft->id); + + // Assign role to new user group + $roleService->assignRoleToUserGroup($role, $userGroup); + + // Load the currently assigned role + $roleAssignments = $roleService->getRoleAssignmentsForUserGroup($userGroup); + /* END: Use Case */ + + $this->assertCount(1, $roleAssignments); + $this->assertInstanceOf( + UserGroupRoleAssignment::class, + reset($roleAssignments) + ); + } + + /** + * Test for the getRoleAssignmentsForUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::getRoleAssignmentsForUser() + * @depends testAssignRoleToUser + * @depends testAssignRoleToUserGroup + */ + public function testLoadPoliciesByUserId() + { + $repository = $this->getRepository(); + + $anonUserId = $this->generateId('user', 10); + /* BEGIN: Use Case */ + // $anonUserId is the ID of the "Anonymous" user. + + $userService = $repository->getUserService(); + $roleService = $repository->getRoleService(); + + // Load "Anonymous" user + $user = $userService->loadUser($anonUserId); + + // Instantiate a role create and add some policies + $roleCreate = $roleService->newRoleCreateStruct('User Role'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleCreate->addPolicy( + $roleService->newPolicyCreateStruct('notification', 'use') + ); + $roleCreate->addPolicy( + $roleService->newPolicyCreateStruct('user', 'password') + ); + $roleCreate->addPolicy( + $roleService->newPolicyCreateStruct('user', 'selfedit') + ); + + // Create the new role instance + $roleDraft = $roleService->createRole($roleCreate); + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRole($roleDraft->id); + + // Assign role to anon user + $roleService->assignRoleToUser($role, $user); + $roleAssignments = $roleService->getRoleAssignmentsForUser($user, true); + + $policies = []; + foreach ($roleAssignments as $roleAssignment) { + $policies[] = $roleAssignment->getRole()->getPolicies(); + } + $policies = array_merge(...$policies); + + $simplePolicyList = []; + foreach ($policies as $simplePolicy) { + $simplePolicyList[] = [$simplePolicy->roleId, $simplePolicy->module, $simplePolicy->function]; + } + /* END: Use Case */ + array_multisort($simplePolicyList); + + $this->assertEquals( + [ + [1, 'content', 'pdf'], + [1, 'content', 'read'], + [1, 'content', 'read'], + [1, 'rss', 'feed'], + [1, 'user', 'login'], + [1, 'user', 'login'], + [1, 'user', 'login'], + [1, 'user', 'login'], + [$role->id, 'notification', 'use'], + [$role->id, 'user', 'password'], + [$role->id, 'user', 'selfedit'], + ], + $simplePolicyList + ); + } + + /** + * Test for the publishRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::publishRoleDraft() + * @depends testCreateRoleDraft + */ + public function testPublishRoleDraft() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('newRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + + $roleDraft = $roleService->addPolicyByRoleDraft( + $roleDraft, + $roleService->newPolicyCreateStruct('content', 'delete') + ); + $roleDraft = $roleService->addPolicyByRoleDraft( + $roleDraft, + $roleService->newPolicyCreateStruct('content', 'create') + ); + + $roleService->publishRoleDraft($roleDraft); + /* END: Use Case */ + + $this->assertInstanceOf( + Role::class, + $roleService->loadRoleByIdentifier($roleCreate->identifier) + ); + } + + /** + * Test for the publishRoleDraft() method. + * + * @covers \Ibexa\Contracts\Core\Repository\RoleService::publishRoleDraft() + * @depends testCreateRoleDraft + * @depends testAddPolicyByRoleDraft + */ + public function testPublishRoleDraftAddPolicies() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $roleService = $repository->getRoleService(); + $roleCreate = $roleService->newRoleCreateStruct('newRole'); + + // @todo uncomment when support for multilingual names and descriptions is added EZP-24776 + // $roleCreate->mainLanguageCode = 'eng-US'; + + $roleDraft = $roleService->createRole($roleCreate); + + $roleDraft = $roleService->addPolicyByRoleDraft( + $roleDraft, + $roleService->newPolicyCreateStruct('content', 'delete') + ); + $roleDraft = $roleService->addPolicyByRoleDraft( + $roleDraft, + $roleService->newPolicyCreateStruct('content', 'create') + ); + + $roleService->publishRoleDraft($roleDraft); + $role = $roleService->loadRoleByIdentifier($roleCreate->identifier); + /* END: Use Case */ + + $actual = []; + foreach ($role->getPolicies() as $policy) { + $actual[] = [ + 'module' => $policy->module, + 'function' => $policy->function, + ]; + } + usort( + $actual, + static function ($p1, $p2) { + return strcasecmp($p1['function'], $p2['function']); + } + ); + + $this->assertEquals( + [ + [ + 'module' => 'content', + 'function' => 'create', + ], + [ + 'module' => 'content', + 'function' => 'delete', + ], + ], + $actual + ); + } + + /** + * Create a user group fixture in a variable named <b>$userGroup</b>,. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup + */ + private function createUserGroupVersion1() + { + $repository = $this->getRepository(); + + $mainGroupId = $this->generateId('group', 4); + /* BEGIN: Inline */ + // $mainGroupId is the ID of the main "Users" group + + $roleService = $repository->getRoleService(); + $userService = $repository->getUserService(); + + // Load main group + $parentUserGroup = $userService->loadUserGroup($mainGroupId); + + // Instantiate a new create struct + $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); + $userGroupCreate->setField('name', 'Example Group'); + + // Create the new user group + $userGroup = $userService->createUserGroup( + $userGroupCreate, + $parentUserGroup + ); + /* END: Inline */ + + return $userGroup; + } + + private function loadRoleAssignmentForUser(RoleService $roleService, Role $role, User $newUser): UserRoleAssignment + { + [$userRoleAssignment] = array_values( + array_filter( + (array)$roleService->getRoleAssignments($role), + static function (RoleAssignment $roleAssignment) use ($newUser): bool { + return $roleAssignment instanceof UserRoleAssignment + && $roleAssignment->getUser()->login === $newUser->login; + } + ) + ); + + return $userRoleAssignment; + } +} + +class_alias(RoleServiceTest::class, 'eZ\Publish\API\Repository\Tests\RoleServiceTest'); diff --git a/eZ/Publish/API/Repository/Tests/SearchEngineIndexingTest.php b/tests/integration/Core/Repository/SearchEngineIndexingTest.php similarity index 96% rename from eZ/Publish/API/Repository/Tests/SearchEngineIndexingTest.php rename to tests/integration/Core/Repository/SearchEngineIndexingTest.php index 63b0d21c5c..8fe3092741 100644 --- a/eZ/Publish/API/Repository/Tests/SearchEngineIndexingTest.php +++ b/tests/integration/Core/Repository/SearchEngineIndexingTest.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; use DateTime; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; /** * Test case for indexing operations with a search engine. @@ -56,7 +56,7 @@ public function testFindContentInfoFullTextIsSearchable() * * @depends testFindContentInfoFullTextIsSearchable * - * @param \eZ\Publish\API\Repository\Values\Content\ContentInfo $contentInfo + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo */ public function testFindLocationsFullTextIsSearchable(ContentInfo $contentInfo) { @@ -138,7 +138,7 @@ public function testFindLocationsFullTextIsNotSearchable() * @param string $searchText * @param bool $isSearchable * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createFullTextIsSearchableContent($searchText, $isSearchable) { @@ -914,7 +914,7 @@ public function getSpecialFullTextCases() /** * Test FullText search on user first name and last name. * - * @see https://jira.ez.no/browse/EZP-27250 + * @see https://issues.ibexa.co/browse/EZP-27250 */ public function testUserFullTextSearch() { @@ -969,7 +969,7 @@ public function testRemovedContentFieldValueIsNotFound() /** * Check if children locations are/are not ivisible. * - * @param \eZ\Publish\API\Repository\SearchService $searchService + * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService * @param int $parentLocationId parent location Id * @param bool $expected expected value of {invisible} property in subtree */ @@ -1055,7 +1055,7 @@ public function testUpdateContentMetadata() $results = $searchService->findContent($query); $this->assertEquals(1, $results->totalCount); $foundContentInfo = $results->searchHits[0]->valueObject->contentInfo; - /** @var \eZ\Publish\Core\Repository\Values\Content\Content $foundContentInfo */ + /** @var \Ibexa\Core\Repository\Values\Content\Content $foundContentInfo */ $this->assertEquals($publishedContent->id, $foundContentInfo->id); $this->assertEquals($newContentMetadataUpdateStruct->publishedDate->getTimestamp(), $foundContentInfo->publishedDate->getTimestamp()); $this->assertEquals($newLocation->id, $foundContentInfo->mainLocationId); @@ -1112,7 +1112,10 @@ public function testAssignSection() $criterion = new Criterion\ContentId($content->id); $query = new Query(['filter' => $criterion]); $results = $searchService->findContentInfo($query); - $this->assertEquals($section->id, $results->searchHits[0]->valueObject->sectionId); + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo */ + $contentInfo = $results->searchHits[0]->valueObject; + self::assertEquals($section->id, $contentInfo->getSectionId()); } /** @@ -1189,7 +1192,7 @@ protected function createTestContentType( $contentTypeStruct->mainLanguageCode = 'eng-GB'; $contentTypeStruct->creatorId = 14; $contentTypeStruct->creationDate = new DateTime(); - $contentTypeStruct->names = ['eng-GB' => 'Test Content Type']; + $contentTypeStruct->names = ['eng-GB' => 'Test content type']; $contentTypeStruct->addFieldDefinition($nameField); $contentTypeGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); @@ -1220,11 +1223,11 @@ protected function createContentWithName(string $contentName, array $parentLocat * @param string $address * @param int[] $parentLocationIdList * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\ContentValidationException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ protected function createContentEmailWithAddress(string $address, array $parentLocationIdList = []): Content { @@ -1263,7 +1266,7 @@ protected function createContent( * @param $contentDescription * @param array $parentLocationIdList * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createContentWithNameAndDescription($contentName, $contentDescription, array $parentLocationIdList = []) { @@ -1296,7 +1299,7 @@ protected function createContentWithNameAndDescription($contentName, $contentDes * @param int $parentLocationId * @param bool $alwaysAvailable * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createMultiLanguageContent(array $names, $parentLocationId, $alwaysAvailable) { @@ -1356,7 +1359,7 @@ protected function assertContentIdSearch($contentId, $expectedCount) /** * Create & get new Location for tests. * - * @return \eZ\Publish\API\Repository\Values\Content\Location + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Location */ protected function createNewTestLocation() { @@ -1373,3 +1376,5 @@ protected function createNewTestLocation() return $locationService->createLocation($membersContentInfo, $locationCreateStruct); } } + +class_alias(SearchEngineIndexingTest::class, 'eZ\Publish\API\Repository\Tests\SearchEngineIndexingTest'); diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/AbstractAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/AbstractAggregationTest.php similarity index 78% rename from eZ/Publish/API/Repository/Tests/SearchService/Aggregation/AbstractAggregationTest.php rename to tests/integration/Core/Repository/SearchService/Aggregation/AbstractAggregationTest.php index 50fb75b746..b6ece8bbbf 100644 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/AbstractAggregationTest.php +++ b/tests/integration/Core/Repository/SearchService/Aggregation/AbstractAggregationTest.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchAll; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\MatchAll; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; abstract class AbstractAggregationTest extends BaseTest { @@ -48,7 +48,7 @@ public function testFindContentWithAggregation( $expectedResult, $searchService->findContent( $this->createContentQuery($aggregation) - )->aggregations->first() + )->getAggregations()->first() ); } @@ -67,7 +67,7 @@ public function testFindLocationWithAggregation( $expectedResult, $searchService->findLocations( $this->createLocationQuery($aggregation) - )->aggregations->first() + )->getAggregations()->first() ); } @@ -107,3 +107,5 @@ protected function createLocationQuery(Aggregation $aggregation): LocationQuery return $query; } } + +class_alias(AbstractAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\AbstractAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/ContentTypeGroupTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/ContentTypeGroupTermAggregationTest.php new file mode 100644 index 0000000000..bafd5b803e --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/ContentTypeGroupTermAggregationTest.php @@ -0,0 +1,36 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\ContentTypeGroupTermAggregation; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; + +final class ContentTypeGroupTermAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + $aggregation = new ContentTypeGroupTermAggregation('content_type_group'); + + $builder = new TermAggregationDataSetBuilder($aggregation); + $builder->setExpectedEntries([ + 'Content' => 8, + 'Users' => 8, + 'Setup' => 2, + ]); + + $builder->setEntryMapper([ + $this->getRepository()->getContentTypeService(), + 'loadContentTypeGroupByIdentifier', + ]); + + yield $builder->build(); + } +} + +class_alias(ContentTypeGroupTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\ContentTypeGroupTermAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/ContentTypeTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/ContentTypeTermAggregationTest.php new file mode 100644 index 0000000000..c295300141 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/ContentTypeTermAggregationTest.php @@ -0,0 +1,40 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\ContentTypeTermAggregation; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; + +final class ContentTypeTermAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + $aggregation = new ContentTypeTermAggregation('content_type'); + + $builder = new TermAggregationDataSetBuilder($aggregation); + $builder->setExpectedEntries([ + 'folder' => 6, + 'user_group' => 6, + 'user' => 2, + 'common_ini_settings' => 1, + 'template_look' => 1, + 'feedback_form' => 1, + 'landing_page' => 1, + ]); + + $builder->setEntryMapper([ + $this->getRepository()->getContentTypeService(), + 'loadContentTypeByIdentifier', + ]); + + yield $builder->build(); + } +} + +class_alias(ContentTypeTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\ContentTypeTermAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/DataSetBuilder/TermAggregationDataSetBuilder.php b/tests/integration/Core/Repository/SearchService/Aggregation/DataSetBuilder/TermAggregationDataSetBuilder.php new file mode 100644 index 0000000000..35ae0ffb04 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/DataSetBuilder/TermAggregationDataSetBuilder.php @@ -0,0 +1,73 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\DataSetBuilder; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; + +/** + * @internal + */ +final class TermAggregationDataSetBuilder +{ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation */ + private $aggregation; + + /** @var array */ + private $entries; + + /** @var callable|null */ + private $mapper; + + public function __construct(Aggregation $aggregation) + { + $this->aggregation = $aggregation; + $this->entries = []; + $this->mapper = null; + } + + public function setExpectedEntries(array $entries): self + { + $this->entries = $entries; + + return $this; + } + + public function setEntryMapper(callable $mapper): self + { + $this->mapper = $mapper; + + return $this; + } + + public function build(): array + { + return [ + $this->aggregation, + $this->buildExpectedTermAggregationResult(), + ]; + } + + private function buildExpectedTermAggregationResult(): TermAggregationResult + { + $entries = []; + foreach ($this->entries as $key => $count) { + if ($this->mapper !== null) { + $key = ($this->mapper)($key); + } + + $entries[] = new TermAggregationResultEntry($key, $count); + } + + return TermAggregationResult::createForAggregation($this->aggregation, $entries); + } +} + +class_alias(TermAggregationDataSetBuilder::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder'); diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/DateMetadataRangeAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/DateMetadataRangeAggregationTest.php similarity index 86% rename from eZ/Publish/API/Repository/Tests/SearchService/Aggregation/DateMetadataRangeAggregationTest.php rename to tests/integration/Core/Repository/SearchService/Aggregation/DateMetadataRangeAggregationTest.php index e8b3c90b57..14d2470b97 100644 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/DateMetadataRangeAggregationTest.php +++ b/tests/integration/Core/Repository/SearchService/Aggregation/DateMetadataRangeAggregationTest.php @@ -6,14 +6,14 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; use DateTime; use DateTimeZone; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\DateMetadataRangeAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Range; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\DateMetadataRangeAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; final class DateMetadataRangeAggregationTest extends AbstractAggregationTest { @@ -116,3 +116,5 @@ public function dataProviderForTestFindContentWithAggregation(): iterable ]; } } + +class_alias(DateMetadataRangeAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\DateMetadataRangeAggregationTest'); diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/AuthorTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/Field/AuthorTermAggregationTest.php similarity index 79% rename from eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/AuthorTermAggregationTest.php rename to tests/integration/Core/Repository/SearchService/Aggregation/Field/AuthorTermAggregationTest.php index 2cd1968720..74a5b5df99 100644 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/AuthorTermAggregationTest.php +++ b/tests/integration/Core/Repository/SearchService/Aggregation/Field/AuthorTermAggregationTest.php @@ -6,16 +6,16 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field; +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\Field; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\AbstractAggregationTest; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\AuthorTermAggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; -use eZ\Publish\Core\FieldType\Author\Author; -use eZ\Publish\Core\FieldType\Author\Value as AuthorValue; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Field\AuthorTermAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; +use Ibexa\Core\FieldType\Author\Author; +use Ibexa\Core\FieldType\Author\Value as AuthorValue; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\AbstractAggregationTest; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; final class AuthorTermAggregationTest extends AbstractAggregationTest { @@ -111,3 +111,5 @@ protected function createFixturesForAggregation(Aggregation $aggregation): void $this->refreshSearch($this->getRepository()); } } + +class_alias(AuthorTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field\AuthorTermAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/Field/CheckboxTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/Field/CheckboxTermAggregationTest.php new file mode 100644 index 0000000000..f2700bbd3b --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/Field/CheckboxTermAggregationTest.php @@ -0,0 +1,55 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\Field; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Field\CheckboxTermAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; +use Ibexa\Core\FieldType\Checkbox\Value as CheckboxValue; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\AbstractAggregationTest; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; + +final class CheckboxTermAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + yield [ + new CheckboxTermAggregation('checkbox_term', 'content_type', 'boolean'), + new TermAggregationResult( + 'checkbox_term', + [ + new TermAggregationResultEntry(true, 3), + new TermAggregationResultEntry(false, 2), + ] + ), + ]; + } + + protected function createFixturesForAggregation(Aggregation $aggregation): void + { + $generator = new FieldAggregationFixtureGenerator($this->getRepository()); + $generator->setContentTypeIdentifier('content_type'); + $generator->setFieldDefinitionIdentifier('boolean'); + $generator->setFieldTypeIdentifier('ezboolean'); + $generator->setValues([ + new CheckboxValue(true), + new CheckboxValue(true), + new CheckboxValue(true), + new CheckboxValue(false), + new CheckboxValue(false), + ]); + + $generator->execute(); + + $this->refreshSearch($this->getRepository()); + } +} + +class_alias(CheckboxTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field\CheckboxTermAggregationTest'); diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/CountryTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/Field/CountryTermAggregationTest.php similarity index 81% rename from eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/CountryTermAggregationTest.php rename to tests/integration/Core/Repository/SearchService/Aggregation/Field/CountryTermAggregationTest.php index cb35811222..d195c49f41 100644 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/CountryTermAggregationTest.php +++ b/tests/integration/Core/Repository/SearchService/Aggregation/Field/CountryTermAggregationTest.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field; +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\Field; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\AbstractAggregationTest; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\CountryTermAggregation; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Field\CountryTermAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\AbstractAggregationTest; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; final class CountryTermAggregationTest extends AbstractAggregationTest { @@ -124,3 +124,5 @@ static function (FieldDefinitionCreateStruct $createStruct): void { $this->refreshSearch($this->getRepository()); } } + +class_alias(CountryTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field\CountryTermAggregationTest'); diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/DateRangeAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/Field/DateRangeAggregationTest.php similarity index 78% rename from eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/DateRangeAggregationTest.php rename to tests/integration/Core/Repository/SearchService/Aggregation/Field/DateRangeAggregationTest.php index 7dd60abaa6..159fd377c5 100644 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/DateRangeAggregationTest.php +++ b/tests/integration/Core/Repository/SearchService/Aggregation/Field/DateRangeAggregationTest.php @@ -6,17 +6,17 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field; +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\Field; use DateTime; use DateTimeZone; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\AbstractAggregationTest; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\DateRangeAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Range; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Field\DateRangeAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\AbstractAggregationTest; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; final class DateRangeAggregationTest extends AbstractAggregationTest { @@ -98,3 +98,5 @@ protected function createFixturesForAggregation(Aggregation $aggregation): void $this->refreshSearch($this->getRepository()); } } + +class_alias(DateRangeAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field\DateRangeAggregationTest'); diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/DateTimeRangeAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/Field/DateTimeRangeAggregationTest.php similarity index 79% rename from eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/DateTimeRangeAggregationTest.php rename to tests/integration/Core/Repository/SearchService/Aggregation/Field/DateTimeRangeAggregationTest.php index 56e0d03fb9..366d48cad9 100644 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/Field/DateTimeRangeAggregationTest.php +++ b/tests/integration/Core/Repository/SearchService/Aggregation/Field/DateTimeRangeAggregationTest.php @@ -6,17 +6,17 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field; +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\Field; use DateTime; use DateTimeZone; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\AbstractAggregationTest; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Field\DateTimeRangeAggregation; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\Range; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Field\DateTimeRangeAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\AbstractAggregationTest; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; final class DateTimeRangeAggregationTest extends AbstractAggregationTest { @@ -99,3 +99,5 @@ protected function createFixturesForAggregation(Aggregation $aggregation): void $this->refreshSearch($this->getRepository()); } } + +class_alias(DateTimeRangeAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field\DateTimeRangeAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/Field/FloatRangeAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/Field/FloatRangeAggregationTest.php new file mode 100644 index 0000000000..41916ffe11 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/Field/FloatRangeAggregationTest.php @@ -0,0 +1,56 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\Field; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Field\FloatRangeAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\AbstractAggregationTest; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; + +final class FloatRangeAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + yield [ + new FloatRangeAggregation('float_range', 'content_type', 'float_field', [ + new Range(null, 10.0), + new Range(10.0, 25.0), + new Range(25.0, 50.0), + new Range(50.0, null), + ]), + new RangeAggregationResult( + 'float_range', + [ + new RangeAggregationResultEntry(new Range(null, 10.0), 4), + new RangeAggregationResultEntry(new Range(10.0, 25.0), 6), + new RangeAggregationResultEntry(new Range(25, 50), 10), + new RangeAggregationResultEntry(new Range(50, null), 20), + ] + ), + ]; + } + + protected function createFixturesForAggregation(Aggregation $aggregation): void + { + $generator = new FieldAggregationFixtureGenerator($this->getRepository()); + $generator->setContentTypeIdentifier('content_type'); + $generator->setFieldDefinitionIdentifier('float_field'); + $generator->setFieldTypeIdentifier('ezfloat'); + $generator->setValues(range(1.0, 100.0, 2.5)); + + $generator->execute(); + + $this->refreshSearch($this->getRepository()); + } +} + +class_alias(FloatRangeAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field\FloatRangeAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/Field/FloatStatsAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/Field/FloatStatsAggregationTest.php new file mode 100644 index 0000000000..300f6e8909 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/Field/FloatStatsAggregationTest.php @@ -0,0 +1,48 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\Field; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Field\FloatStatsAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\StatsAggregationResult; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\AbstractAggregationTest; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; + +final class FloatStatsAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + yield [ + new FloatStatsAggregation('float_stats', 'content_type', 'float_field_2'), + new StatsAggregationResult( + 'float_stats', + 5, + 1.0, + 7.75, + 3.8, + 19.0 + ), + ]; + } + + protected function createFixturesForAggregation(Aggregation $aggregation): void + { + $generator = new FieldAggregationFixtureGenerator($this->getRepository()); + $generator->setContentTypeIdentifier('content_type'); + $generator->setFieldDefinitionIdentifier('float_field_2'); + $generator->setFieldTypeIdentifier('ezfloat'); + $generator->setValues([1.0, 2.5, 2.5, 5.25, 7.75]); + + $generator->execute(); + + $this->refreshSearch($this->getRepository()); + } +} + +class_alias(FloatStatsAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field\FloatStatsAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/Field/IntegerRangeAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/Field/IntegerRangeAggregationTest.php new file mode 100644 index 0000000000..53cdcef485 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/Field/IntegerRangeAggregationTest.php @@ -0,0 +1,56 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\Field; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Field\IntegerRangeAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\AbstractAggregationTest; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; + +final class IntegerRangeAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + yield [ + new IntegerRangeAggregation('integer_range', 'content_type', 'integer_field', [ + new Range(null, 10), + new Range(10, 25), + new Range(25, 50), + new Range(50, null), + ]), + new RangeAggregationResult( + 'integer_range', + [ + new RangeAggregationResultEntry(new Range(null, 10), 9), + new RangeAggregationResultEntry(new Range(10, 25), 15), + new RangeAggregationResultEntry(new Range(25, 50), 25), + new RangeAggregationResultEntry(new Range(50, null), 51), + ] + ), + ]; + } + + protected function createFixturesForAggregation(Aggregation $aggregation): void + { + $generator = new FieldAggregationFixtureGenerator($this->getRepository()); + $generator->setContentTypeIdentifier('content_type'); + $generator->setFieldDefinitionIdentifier('integer_field'); + $generator->setFieldTypeIdentifier('ezinteger'); + $generator->setValues(range(1, 100)); + + $generator->execute(); + + $this->refreshSearch($this->getRepository()); + } +} + +class_alias(IntegerRangeAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field\IntegerRangeAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/Field/IntegerStatsAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/Field/IntegerStatsAggregationTest.php new file mode 100644 index 0000000000..833f82d2ce --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/Field/IntegerStatsAggregationTest.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\Field; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Field\IntegerStatsAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\StatsAggregationResult; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\AbstractAggregationTest; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; + +final class IntegerStatsAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + yield [ + new IntegerStatsAggregation('integer_stats', 'content_type', 'integer_field'), + new StatsAggregationResult( + 'integer_stats', + 7, + 1, + 21, + 7.571428571428571, + 53 + ), + ]; + } + + protected function createFixturesForAggregation(Aggregation $aggregation): void + { + $generator = new FieldAggregationFixtureGenerator($this->getRepository()); + $generator->setContentTypeIdentifier('content_type'); + $generator->setFieldDefinitionIdentifier('integer_field'); + $generator->setFieldTypeIdentifier('ezinteger'); + $generator->setValues([1, 2, 3, 5, 8, 13, 21]); + $generator->execute(); + + $this->refreshSearch($this->getRepository()); + } +} + +class_alias(IntegerStatsAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field\IntegerStatsAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/Field/KeywordTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/Field/KeywordTermAggregationTest.php new file mode 100644 index 0000000000..51210e328f --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/Field/KeywordTermAggregationTest.php @@ -0,0 +1,57 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\Field; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Field\KeywordTermAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\AbstractAggregationTest; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; + +final class KeywordTermAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + yield [ + new KeywordTermAggregation( + 'keyword_term', + 'content_type', + 'keyword_field' + ), + new TermAggregationResult( + 'keyword_term', + [ + new TermAggregationResultEntry('foo', 3), + new TermAggregationResultEntry('bar', 2), + new TermAggregationResultEntry('baz', 1), + ] + ), + ]; + } + + protected function createFixturesForAggregation(Aggregation $aggregation): void + { + $generator = new FieldAggregationFixtureGenerator($this->getRepository()); + $generator->setContentTypeIdentifier('content_type'); + $generator->setFieldDefinitionIdentifier('keyword_field'); + $generator->setFieldTypeIdentifier('ezkeyword'); + $generator->setValues([ + ['foo'], + ['foo', 'bar'], + ['foo', 'bar', 'baz'], + ]); + + $generator->execute(); + + $this->refreshSearch($this->getRepository()); + } +} + +class_alias(KeywordTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field\KeywordTermAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/Field/SelectionTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/Field/SelectionTermAggregationTest.php new file mode 100644 index 0000000000..8ccb1f7045 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/Field/SelectionTermAggregationTest.php @@ -0,0 +1,71 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\Field; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Field\SelectionTermAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\AbstractAggregationTest; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; + +final class SelectionTermAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + yield [ + new SelectionTermAggregation( + 'selection_term', + 'content_type', + 'selection_field' + ), + new TermAggregationResult( + 'selection_term', + [ + new TermAggregationResultEntry('foo', 3), + new TermAggregationResultEntry('bar', 2), + new TermAggregationResultEntry('baz', 1), + ] + ), + ]; + } + + protected function createFixturesForAggregation(Aggregation $aggregation): void + { + $generator = new FieldAggregationFixtureGenerator($this->getRepository()); + $generator->setContentTypeIdentifier('content_type'); + $generator->setFieldDefinitionIdentifier('selection_field'); + $generator->setFieldTypeIdentifier('ezselection'); + $generator->setValues([ + [0], + [0, 1], + [0, 1, 2], + ]); + + $generator->setFieldDefinitionCreateStructConfigurator( + static function (FieldDefinitionCreateStruct $createStruct): void { + $createStruct->fieldSettings = [ + 'isMultiple' => true, + 'options' => [ + 0 => 'foo', + 1 => 'bar', + 2 => 'baz', + ], + ]; + }, + ); + + $generator->execute(); + + $this->refreshSearch($this->getRepository()); + } +} + +class_alias(SelectionTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field\SelectionTermAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/Field/TimeRangeAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/Field/TimeRangeAggregationTest.php new file mode 100644 index 0000000000..161b6bb46d --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/Field/TimeRangeAggregationTest.php @@ -0,0 +1,83 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\Field; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Field\TimeRangeAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; +use Ibexa\Core\FieldType\Time\Value as TimeValue; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\AbstractAggregationTest; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator; + +final class TimeRangeAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + yield [ + new TimeRangeAggregation( + 'time_term', + 'content_type', + 'time_field', + [ + new Range(null, mktime(7, 0, 0, 0, 0, 0)), + new Range( + mktime(7, 0, 0, 0, 0, 0), + mktime(12, 0, 0, 0, 0, 0) + ), + new Range(mktime(12, 0, 0, 0, 0, 0), null), + ] + ), + new RangeAggregationResult( + 'time_term', + [ + new RangeAggregationResultEntry( + new Range(null, mktime(7, 0, 0, 0, 0, 0)), + 2 + ), + new RangeAggregationResultEntry( + new Range( + mktime(7, 0, 0, 0, 0, 0), + mktime(12, 0, 0, 0, 0, 0) + ), + 2 + ), + new RangeAggregationResultEntry( + new Range(mktime(12, 0, 0, 0, 0, 0), null), + 3 + ), + ] + ), + ]; + } + + protected function createFixturesForAggregation(Aggregation $aggregation): void + { + $generator = new FieldAggregationFixtureGenerator($this->getRepository()); + $generator->setContentTypeIdentifier('content_type'); + $generator->setFieldDefinitionIdentifier('time_field'); + $generator->setFieldTypeIdentifier('eztime'); + $generator->setValues([ + new TimeValue(mktime(6, 45, 0, 0, 0, 0)), + new TimeValue(mktime(7, 0, 0, 0, 0, 0)), + new TimeValue(mktime(6, 30, 0, 0, 0, 0)), + new TimeValue(mktime(11, 45, 0, 0, 0, 0)), + new TimeValue(mktime(16, 00, 0, 0, 0, 0)), + new TimeValue(mktime(17, 00, 0, 0, 0, 0)), + new TimeValue(mktime(17, 30, 0, 0, 0, 0)), + ]); + + $generator->execute(); + + $this->refreshSearch($this->getRepository()); + } +} + +class_alias(TimeRangeAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\Field\TimeRangeAggregationTest'); diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php b/tests/integration/Core/Repository/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php similarity index 90% rename from eZ/Publish/API/Repository/Tests/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php rename to tests/integration/Core/Repository/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php index 3230ff0759..2fb9af61af 100644 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php +++ b/tests/integration/Core/Repository/SearchService/Aggregation/FixtureGenerator/FieldAggregationFixtureGenerator.php @@ -6,18 +6,18 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator; +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\FixtureGenerator; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct; /** * @internal */ final class FieldAggregationFixtureGenerator { - /** @var \eZ\Publish\API\Repository\Repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository */ private $repository; /** @var string|null */ @@ -174,3 +174,5 @@ private function createFieldDefinitionCreateStruct( return $fieldDefinitionCreateStruct; } } + +class_alias(FieldAggregationFixtureGenerator::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\FixtureGenerator\FieldAggregationFixtureGenerator'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/LanguageTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/LanguageTermAggregationTest.php new file mode 100644 index 0000000000..e688b01219 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/LanguageTermAggregationTest.php @@ -0,0 +1,35 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\LanguageTermAggregation; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; + +final class LanguageTermAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + $aggregation = new LanguageTermAggregation('language'); + + $builder = new TermAggregationDataSetBuilder($aggregation); + $builder->setExpectedEntries([ + 'eng-US' => 16, + 'eng-GB' => 2, + ]); + + $builder->setEntryMapper([ + $this->getRepository()->getContentLanguageService(), + 'loadLanguage', + ]); + + yield $builder->build(); + } +} + +class_alias(LanguageTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\LanguageTermAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/LocationChildrenTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/LocationChildrenTermAggregationTest.php new file mode 100644 index 0000000000..b5b79155c7 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/LocationChildrenTermAggregationTest.php @@ -0,0 +1,53 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Location\LocationChildrenTermAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; + +final class LocationChildrenTermAggregationTest extends AbstractAggregationTest +{ + /** + * @dataProvider dataProviderForTestFindContentWithAggregation + */ + public function testFindContentWithAggregation( + Aggregation $aggregation, + AggregationResult $expectedResult + ): void { + self::markTestSkipped('LocationChildrenTermAggregation is only available for Location search'); + } + + public function dataProviderForTestFindContentWithAggregation(): iterable + { + $aggregation = new LocationChildrenTermAggregation('children'); + + $builder = new TermAggregationDataSetBuilder($aggregation); + $builder->setExpectedEntries([ + 1 => 5, + 5 => 5, + 43 => 3, + 13 => 1, + 2 => 1, + 44 => 1, + 48 => 1, + 58 => 1, + ]); + + $builder->setEntryMapper([ + $this->getRepository()->getLocationService(), + 'loadLocation', + ]); + + yield $builder->build(); + } +} + +class_alias(LocationChildrenTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\LocationChildrenTermAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/ObjectStateTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/ObjectStateTermAggregationTest.php new file mode 100644 index 0000000000..f4ff71b3ba --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/ObjectStateTermAggregationTest.php @@ -0,0 +1,44 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\ObjectStateTermAggregation; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; + +final class ObjectStateTermAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + $aggregation = new ObjectStateTermAggregation('object_state', 'ez_lock'); + + $builder = new TermAggregationDataSetBuilder($aggregation); + $builder->setExpectedEntries([ + // TODO: Change the state of some content objects to have better test data + 'not_locked' => 18, + ]); + + $builder->setEntryMapper( + function (string $identifier): ObjectState { + $objectStateService = $this->getRepository()->getObjectStateService(); + + static $objectStateGroup = null; + if ($objectStateGroup === null) { + $objectStateGroup = $objectStateService->loadObjectStateGroupByIdentifier('ez_lock'); + } + + return $objectStateService->loadObjectStateByIdentifier($objectStateGroup, $identifier); + } + ); + + yield $builder->build(); + } +} + +class_alias(ObjectStateTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\ObjectStateTermAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/RawRangeAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/RawRangeAggregationTest.php new file mode 100644 index 0000000000..fe6d134313 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/RawRangeAggregationTest.php @@ -0,0 +1,42 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\RawRangeAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\RangeAggregationResultEntry; + +final class RawRangeAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + yield [ + new RawRangeAggregation( + 'raw_range', + 'content_version_no_i', + [ + new Range(null, 2), + new Range(2, 3), + new Range(3, null), + ] + ), + new RangeAggregationResult( + 'raw_range', + [ + new RangeAggregationResultEntry(new Range(null, 2), 14), + new RangeAggregationResultEntry(new Range(2, 3), 3), + new RangeAggregationResultEntry(new Range(3, null), 1), + ] + ), + ]; + } +} + +class_alias(RawRangeAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\RawRangeAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/RawStatsAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/RawStatsAggregationTest.php new file mode 100644 index 0000000000..93ab243fc4 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/RawStatsAggregationTest.php @@ -0,0 +1,35 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\RawStatsAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\StatsAggregationResult; + +final class RawStatsAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + yield [ + new RawStatsAggregation( + 'raw_stats', + 'content_version_no_i' + ), + new StatsAggregationResult( + 'raw_stats', + 18, + 1.0, + 4.0, + 1.3333333333333333, + 24.0 + ), + ]; + } +} + +class_alias(RawStatsAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\RawStatsAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/RawTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/RawTermAggregationTest.php new file mode 100644 index 0000000000..3c1c65c744 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/RawTermAggregationTest.php @@ -0,0 +1,35 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\RawTermAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResult\TermAggregationResultEntry; + +final class RawTermAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + yield [ + new RawTermAggregation( + 'raw_term', + 'content_section_identifier_id' + ), + new TermAggregationResult('raw_term', [ + new TermAggregationResultEntry('users', 8), + new TermAggregationResultEntry('media', 4), + new TermAggregationResultEntry('design', 2), + new TermAggregationResultEntry('setup', 2), + new TermAggregationResultEntry('standard', 2), + ]), + ]; + } +} + +class_alias(RawTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\RawTermAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/SectionTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/SectionTermAggregationTest.php new file mode 100644 index 0000000000..18042b3028 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/SectionTermAggregationTest.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\SectionTermAggregation; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; + +final class SectionTermAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + $aggregation = new SectionTermAggregation('section'); + + $builder = new TermAggregationDataSetBuilder($aggregation); + $builder->setExpectedEntries([ + 'users' => 8, + 'media' => 4, + 'standard' => 2, + 'setup' => 2, + 'design' => 2, + ]); + + $builder->setEntryMapper([ + $this->getRepository()->getSectionService(), + 'loadSectionByIdentifier', + ]); + + yield $builder->build(); + } +} + +class_alias(SectionTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\SectionTermAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/SubtreeTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/SubtreeTermAggregationTest.php new file mode 100644 index 0000000000..ee577d2445 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/SubtreeTermAggregationTest.php @@ -0,0 +1,36 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Location\SubtreeTermAggregation; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; + +final class SubtreeTermAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + $aggregation = new SubtreeTermAggregation('subtree', '/1/5/'); + + $builder = new TermAggregationDataSetBuilder($aggregation); + $builder->setExpectedEntries([ + 5 => 7, + 13 => 1, + 44 => 1, + ]); + + $builder->setEntryMapper([ + $this->getRepository()->getLocationService(), + 'loadLocation', + ]); + + yield $builder->build(); + } +} + +class_alias(SubtreeTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\SubtreeTermAggregationTest'); diff --git a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/UserMetadataTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/UserMetadataTermAggregationTest.php similarity index 83% rename from eZ/Publish/API/Repository/Tests/SearchService/Aggregation/UserMetadataTermAggregationTest.php rename to tests/integration/Core/Repository/SearchService/Aggregation/UserMetadataTermAggregationTest.php index ae402b32cc..559ec7636a 100644 --- a/eZ/Publish/API/Repository/Tests/SearchService/Aggregation/UserMetadataTermAggregationTest.php +++ b/tests/integration/Core/Repository/SearchService/Aggregation/UserMetadataTermAggregationTest.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\SearchService\Aggregation; +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; -use eZ\Publish\API\Repository\Tests\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation\UserMetadataTermAggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\UserMetadataTermAggregation; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; final class UserMetadataTermAggregationTest extends AbstractAggregationTest { @@ -62,3 +62,5 @@ private function createModifierTermAggregationDataSet(): array return $builder->build(); } } + +class_alias(UserMetadataTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\UserMetadataTermAggregationTest'); diff --git a/tests/integration/Core/Repository/SearchService/Aggregation/VisibilityTermAggregationTest.php b/tests/integration/Core/Repository/SearchService/Aggregation/VisibilityTermAggregationTest.php new file mode 100644 index 0000000000..818e1c4b32 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/Aggregation/VisibilityTermAggregationTest.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\VisibilityTermAggregation; +use Ibexa\Tests\Integration\Core\Repository\SearchService\Aggregation\DataSetBuilder\TermAggregationDataSetBuilder; + +final class VisibilityTermAggregationTest extends AbstractAggregationTest +{ + public function dataProviderForTestFindContentWithAggregation(): iterable + { + $aggregation = new VisibilityTermAggregation('visibility'); + + $builder = new TermAggregationDataSetBuilder($aggregation); + $builder->setExpectedEntries([ + true => 18, + ]); + + yield $builder->build(); + } +} + +class_alias(VisibilityTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\Aggregation\VisibilityTermAggregationTest'); diff --git a/eZ/Publish/API/Repository/Tests/SearchService/DeleteTranslationTest.php b/tests/integration/Core/Repository/SearchService/DeleteTranslationTest.php similarity index 75% rename from eZ/Publish/API/Repository/Tests/SearchService/DeleteTranslationTest.php rename to tests/integration/Core/Repository/SearchService/DeleteTranslationTest.php index 7528d9062e..acbf0b80d0 100644 --- a/eZ/Publish/API/Repository/Tests/SearchService/DeleteTranslationTest.php +++ b/tests/integration/Core/Repository/SearchService/DeleteTranslationTest.php @@ -6,21 +6,21 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\SearchService; +namespace Ibexa\Tests\Integration\Core\Repository\SearchService; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use eZ\Publish\API\Repository\Values\User\Limitation\LanguageLimitation; -use eZ\Publish\API\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\LanguageLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Test case for delete content translation with the SearchService. * - * @see \eZ\Publish\API\Repository\SearchService + * @covers \Ibexa\Contracts\Core\Repository\SearchService * @group integration * @group search */ @@ -29,9 +29,9 @@ final class DeleteTranslationTest extends BaseTest /** * @param array $languages * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ protected function createTestContentWithLanguages(array $languages): Content { @@ -62,7 +62,7 @@ protected function createTestContentWithLanguages(array $languages): Content } /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ protected function findContent(string $text, string $languageCode): SearchResult { @@ -81,9 +81,9 @@ protected function findContent(string $text, string $languageCode): SearchResult } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testDeleteContentTranslation(): void { @@ -112,9 +112,9 @@ public function testDeleteContentTranslation(): void } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testDeleteContentTranslationWithContentRemovePolicy(): void { @@ -138,9 +138,9 @@ public function testDeleteContentTranslationWithContentRemovePolicy(): void } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testPreventTranslationDeletionIfNoAccess(): void { @@ -180,3 +180,5 @@ public function provideUserWithContentRemovePolicies(): User ); } } + +class_alias(DeleteTranslationTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\DeleteTranslationTest'); diff --git a/tests/integration/Core/Repository/SearchService/MultilingualContentSearchIndexingTest.php b/tests/integration/Core/Repository/SearchService/MultilingualContentSearchIndexingTest.php index 6cbb03ca54..4a1d564756 100644 --- a/tests/integration/Core/Repository/SearchService/MultilingualContentSearchIndexingTest.php +++ b/tests/integration/Core/Repository/SearchService/MultilingualContentSearchIndexingTest.php @@ -8,11 +8,11 @@ namespace Ibexa\Tests\Integration\Core\Repository\SearchService; -use eZ\Publish\API\Repository\LanguageService; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\LanguageCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\LanguageService; +use Ibexa\Contracts\Core\Repository\Values\Content\LanguageCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; final class MultilingualContentSearchIndexingTest extends BaseTest { @@ -28,9 +28,9 @@ final class MultilingualContentSearchIndexingTest extends BaseTest ]; /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testPublishingSingleTranslationKeepsSearchIndexConsistent(): void { @@ -75,7 +75,7 @@ public function testPublishingSingleTranslationKeepsSearchIndexConsistent(): voi ]); $searchResult = $searchService->findContent($query, ['languages' => $translations]); self::assertSame(1, $searchResult->totalCount); - /** @var \eZ\Publish\API\Repository\Values\Content\Content $foundContent */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $foundContent */ $foundContent = $searchResult->searchHits[0]->valueObject; $expectedContentInfo = $foundContent->contentInfo; $expectedVersionNo = $foundContent->getVersionInfo()->versionNo; @@ -85,7 +85,7 @@ public function testPublishingSingleTranslationKeepsSearchIndexConsistent(): voi ['languages' => [self::MODIFIED_TRANSLATION]] ); self::assertSame(1, $searchResult->totalCount); - /** @var \eZ\Publish\API\Repository\Values\Content\Content $foundContent */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $foundContent */ $foundContent = $searchResult->searchHits[0]->valueObject; self::assertEquals($expectedContentInfo, $foundContent->contentInfo); self::assertEquals($expectedVersionNo, $foundContent->getVersionInfo()->versionNo); @@ -94,8 +94,8 @@ public function testPublishingSingleTranslationKeepsSearchIndexConsistent(): voi /** * Create required languages which are not pre-defined by Repository test setup. * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ private function createMissingLanguages(LanguageService $languageService): void { diff --git a/eZ/Publish/API/Repository/Tests/SearchService/RemoteIdIndexingTest.php b/tests/integration/Core/Repository/SearchService/RemoteIdIndexingTest.php similarity index 80% rename from eZ/Publish/API/Repository/Tests/SearchService/RemoteIdIndexingTest.php rename to tests/integration/Core/Repository/SearchService/RemoteIdIndexingTest.php index 539287a4ad..0d5338be04 100644 --- a/eZ/Publish/API/Repository/Tests/SearchService/RemoteIdIndexingTest.php +++ b/tests/integration/Core/Repository/SearchService/RemoteIdIndexingTest.php @@ -6,12 +6,12 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\SearchService; +namespace Ibexa\Tests\Integration\Core\Repository\SearchService; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; final class RemoteIdIndexingTest extends BaseTest { @@ -19,9 +19,9 @@ final class RemoteIdIndexingTest extends BaseTest private static $contentIdByRemoteIdIndex = []; /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ protected function setUp(): void { @@ -41,7 +41,7 @@ protected function setUp(): void /** * @dataProvider providerForTestIndexingRemoteId * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException */ public function testIndexingRemoteId(Criterion $criterion): void { @@ -104,9 +104,9 @@ public function providerForTestIndexingRemoteId(): iterable } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ private function createTestFolder(string $remoteId): int { @@ -130,3 +130,5 @@ private function createTestFolder(string $remoteId): int return $folder->id; } } + +class_alias(RemoteIdIndexingTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\RemoteIdIndexingTest'); diff --git a/tests/integration/Core/Repository/SearchService/SortClause/AbstractSortClauseTest.php b/tests/integration/Core/Repository/SearchService/SortClause/AbstractSortClauseTest.php new file mode 100644 index 0000000000..8f5e84f3d4 --- /dev/null +++ b/tests/integration/Core/Repository/SearchService/SortClause/AbstractSortClauseTest.php @@ -0,0 +1,37 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\SortClause; + +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; + +abstract class AbstractSortClauseTest extends BaseTest +{ + protected function assertSearchResultOrderByRemoteId( + array $expectedOrderedIds, + SearchResult $actualSearchResults + ): void { + self::assertEquals( + count($expectedOrderedIds), + $actualSearchResults->totalCount + ); + + $actualIds = array_map( + static function (SearchHit $searchHit): string { + return $searchHit->valueObject->remoteId; + }, + $actualSearchResults->searchHits + ); + + self::assertEquals($expectedOrderedIds, $actualIds); + } +} + +class_alias(AbstractSortClauseTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\SortClause\AbstractSortClauseTest'); diff --git a/eZ/Publish/API/Repository/Tests/SearchService/SortClause/ContentTranslatedNameTest.php b/tests/integration/Core/Repository/SearchService/SortClause/ContentTranslatedNameTest.php similarity index 92% rename from eZ/Publish/API/Repository/Tests/SearchService/SortClause/ContentTranslatedNameTest.php rename to tests/integration/Core/Repository/SearchService/SortClause/ContentTranslatedNameTest.php index 09b59b134a..fa9ea9d50a 100644 --- a/eZ/Publish/API/Repository/Tests/SearchService/SortClause/ContentTranslatedNameTest.php +++ b/tests/integration/Core/Repository/SearchService/SortClause/ContentTranslatedNameTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\SearchService\SortClause; +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\SortClause; use DateTime; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; final class ContentTranslatedNameTest extends AbstractSortClauseTest { @@ -28,7 +28,7 @@ protected function setUp(): void /** * @param string[] $values * - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception * * @dataProvider dataProviderForTestSortingByContentTranslatedName */ @@ -57,7 +57,7 @@ public function testContentSortingByContentTranslatedName( /** * @param string[] $values * - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception * * @dataProvider dataProviderForTestSortingByContentTranslatedName */ @@ -229,3 +229,5 @@ private function createContentForContentTranslatedNameTesting(iterable $values): $this->refreshSearch($repository); } } + +class_alias(ContentTranslatedNameTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\SortClause\ContentTranslatedNameTest'); diff --git a/eZ/Publish/API/Repository/Tests/SearchService/SortClause/ScoreTest.php b/tests/integration/Core/Repository/SearchService/SortClause/ScoreTest.php similarity index 89% rename from eZ/Publish/API/Repository/Tests/SearchService/SortClause/ScoreTest.php rename to tests/integration/Core/Repository/SearchService/SortClause/ScoreTest.php index aa42ffd16e..cb6c4cca35 100644 --- a/eZ/Publish/API/Repository/Tests/SearchService/SortClause/ScoreTest.php +++ b/tests/integration/Core/Repository/SearchService/SortClause/ScoreTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\SearchService\SortClause; +namespace Ibexa\Tests\Integration\Core\Repository\SearchService\SortClause; -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; final class ScoreTest extends AbstractSortClauseTest { @@ -29,7 +29,7 @@ protected function setUp(): void /** * @param string[] $values * - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception * * @dataProvider dataProviderForTestSortingByScore */ @@ -104,7 +104,7 @@ public function dataProviderForTestSortingByScore(): iterable /** * @param string[] $values * - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception */ private function createContentForScoreSortTesting(iterable $values): void { @@ -153,3 +153,5 @@ private function createContentForScoreSortTesting(iterable $values): void $this->refreshSearch($repository); } } + +class_alias(ScoreTest::class, 'eZ\Publish\API\Repository\Tests\SearchService\SortClause\ScoreTest'); diff --git a/eZ/Publish/API/Repository/Tests/SearchServiceAuthorizationTest.php b/tests/integration/Core/Repository/SearchServiceAuthorizationTest.php similarity index 76% rename from eZ/Publish/API/Repository/Tests/SearchServiceAuthorizationTest.php rename to tests/integration/Core/Repository/SearchServiceAuthorizationTest.php index 6ee16e6821..43fc1fb583 100644 --- a/eZ/Publish/API/Repository/Tests/SearchServiceAuthorizationTest.php +++ b/tests/integration/Core/Repository/SearchServiceAuthorizationTest.php @@ -4,16 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Core\Repository\Values\Content\Content; /** * Test case for operations in the SearchService. * - * @see eZ\Publish\API\Repository\SearchService - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUser + * @covers \Ibexa\Contracts\Core\Repository\SearchService + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUser * @group integration * @group authorization */ @@ -22,8 +24,8 @@ class SearchServiceAuthorizationTest extends BaseTest /** * Test for the findContent() method but with anonymous user. * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testFindContentFiltered + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * @depends Ibexa\Tests\Integration\Core\Repository\SearchServiceTest::testFindContentFiltered */ public function testFindContent() { @@ -32,7 +34,7 @@ public function testFindContent() $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $searchService = $repository->getSearchService(); $userService = $repository->getUserService(); @@ -52,8 +54,8 @@ public function testFindContent() /** * Test for the findContent() method. * - * @see \eZ\Publish\API\Repository\SearchService::findContent() - * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testFindContentFiltered + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * @depends Ibexa\Tests\Integration\Core\Repository\SearchServiceTest::testFindContentFiltered */ public function testFindContentEmptyResult() { @@ -62,7 +64,7 @@ public function testFindContentEmptyResult() $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $searchService = $repository->getSearchService(); $userService = $repository->getUserService(); @@ -85,19 +87,19 @@ public function testFindContentEmptyResult() /** * Test for the findSingle() method. * - * @see \eZ\Publish\API\Repository\SearchService::findSingle() - * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testFindSingle + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findSingle() + * @depends Ibexa\Tests\Integration\Core\Repository\SearchServiceTest::testFindSingle */ public function testFindSingleThrowsNotFoundException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $searchService = $repository->getSearchService(); $userService = $repository->getUserService(); @@ -117,8 +119,8 @@ public function testFindSingleThrowsNotFoundException() /** * Test for the findContent() method, verifying disabling permissions. * - * @see \eZ\Publish\API\Repository\ContentService::findContent($query, $languageFilter, $filterOnUserPermissions) - * @depends eZ\Publish\API\Repository\Tests\SearchServiceAuthorizationTest::testFindContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::findContent($query, $languageFilter, $filterOnUserPermissions) + * @depends Ibexa\Tests\Integration\Core\Repository\SearchServiceAuthorizationTest::testFindContent */ public function testFindContentWithUserPermissionFilter() { @@ -155,8 +157,8 @@ public function testFindContentWithUserPermissionFilter() /** * Test for the findSingle() method disabling permission filtering. * - * @see \eZ\Publish\API\Repository\ContentService::findSingle($query, $languageFilter, $filterOnUserPermissions) - * @depends eZ\Publish\API\Repository\Tests\SearchServiceAuthorizationTest::testFindContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::findSingle($query, $languageFilter, $filterOnUserPermissions) + * @depends Ibexa\Tests\Integration\Core\Repository\SearchServiceAuthorizationTest::testFindContent */ public function testFindSingleWithUserPermissionFilter() { @@ -177,8 +179,8 @@ public function testFindSingleWithUserPermissionFilter() ); /* END: Use Case */ - $this->assertInstanceOf( - '\\eZ\\Publish\\API\\Repository\\Values\\Content\\Content', + self::assertInstanceOf( + Content::class, $content ); } @@ -186,12 +188,12 @@ public function testFindSingleWithUserPermissionFilter() /** * Test for the findSingle() method. * - * @see \eZ\Publish\API\Repository\ContentService::findSingle($query, $languageFilter, $filterOnUserPermissions) - * @depends eZ\Publish\API\Repository\Tests\SearchServiceAuthorizationTest::testFindContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::findSingle($query, $languageFilter, $filterOnUserPermissions) + * @depends Ibexa\Tests\Integration\Core\Repository\SearchServiceAuthorizationTest::testFindContent */ public function testFindSingleThrowsNotFoundExceptionWithUserPermissionFilter() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -212,3 +214,5 @@ public function testFindSingleThrowsNotFoundExceptionWithUserPermissionFilter() /* END: Use Case */ } } + +class_alias(SearchServiceAuthorizationTest::class, 'eZ\Publish\API\Repository\Tests\SearchServiceAuthorizationTest'); diff --git a/tests/integration/Core/Repository/SearchServiceContentNameTest.php b/tests/integration/Core/Repository/SearchServiceContentNameTest.php new file mode 100644 index 0000000000..b333212898 --- /dev/null +++ b/tests/integration/Core/Repository/SearchServiceContentNameTest.php @@ -0,0 +1,301 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\FieldType\TextLine\Value; +use Ibexa\Tests\Integration\Core\RepositorySearchTestCase; + +final class SearchServiceContentNameTest extends RepositorySearchTestCase +{ + private const TOTAL_COUNT = 20; + + private const LANGUAGE_CODE_ENG = 'eng-GB'; + private const LANGUAGE_CODE_GER = 'ger-DE'; + + private const CAR_ENG = 'Car'; + private const SPORTS_CAR_ENG = 'Sports car'; + private const TRUCK_ENG = 'TRUCK'; + + private const CAR_GER = 'auto'; + private const SPORTS_CAR_GER = 'Sportwagen'; + private const TRUCK_GER = 'LASTWAGEN'; + + private const CONTENT_ITEMS_MAP = [ + [ + 'mainLanguageCode' => self::LANGUAGE_CODE_ENG, + 'name' => self::CAR_ENG, + 'translations' => [ + self::LANGUAGE_CODE_GER => self::CAR_GER, + ], + ], + [ + 'mainLanguageCode' => self::LANGUAGE_CODE_ENG, + 'name' => self::SPORTS_CAR_ENG, + 'translations' => [ + self::LANGUAGE_CODE_GER => self::SPORTS_CAR_GER, + ], + ], + [ + 'mainLanguageCode' => self::LANGUAGE_CODE_ENG, + 'name' => self::TRUCK_ENG, + 'translations' => [ + self::LANGUAGE_CODE_GER => self::TRUCK_GER, + ], + ], + ]; + + protected function setUp(): void + { + parent::setUp(); + + $this->createTestContentItems(); + + $this->refreshSearch(); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testCriterionFindAllContentItems(): void + { + $query = $this->createQuery( + $this->createContentNameCriterion('*') + ); + + self::assertSame( + self::TOTAL_COUNT, + self::getSearchService()->findContent($query)->totalCount + ); + } + + /** + * @dataProvider provideDataForTestCriterion + * + * @param array<string> $expectedContentItemTitles + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + */ + public function testCriterion( + Criterion $criterion, + ?string $languageCode, + array $expectedContentItemTitles, + int $expectedCount + ): void { + $result = self::getSearchService()->findContent( + $this->createQuery($criterion), + $this->getLanguageFilter($languageCode) + ); + + self::assertEquals( + $expectedContentItemTitles, + array_map( + static function (SearchHit $searchHit) use ($languageCode): ?string { + $content = $searchHit->valueObject; + if ($content instanceof Content) { + return $content->getName($languageCode); + } + + return null; + }, + $result->searchHits + ) + ); + + self::assertSame( + $expectedCount, + $result->totalCount + ); + } + + /** + * @return iterable<array{ + * \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion, + * ?string, + * array<string>, + * int, + * }> + */ + public function provideDataForTestCriterion(): iterable + { + yield 'Content items not found' => [ + $this->createContentNameCriterion('foo'), + self::LANGUAGE_CODE_ENG, + [], + 0, + ]; + + yield 'Return content items in default language (English) that contain "car" in name' => [ + $this->createContentNameCriterion('*car*'), + null, + [ + self::CAR_ENG, + self::SPORTS_CAR_ENG, + ], + 2, + ]; + + yield 'Return content item in default language (English) whose name starts with "car"' => [ + $this->createContentNameCriterion('Car*'), + null, + [ + self::CAR_ENG, + ], + 1, + ]; + + yield 'Return content item in English that contain "Spo*t*" in name' => [ + $this->createContentNameCriterion('Spo*t*'), + self::LANGUAGE_CODE_ENG, + [ + self::SPORTS_CAR_ENG, + ], + 1, + ]; + + yield 'Return content item in English with name "sports car"' => [ + $this->createContentNameCriterion('sports car'), + self::LANGUAGE_CODE_ENG, + [ + self::SPORTS_CAR_ENG, + ], + 1, + ]; + + yield 'Return content item in English that contain "**ruc*" in name' => [ + $this->createContentNameCriterion('**ruc*'), + self::LANGUAGE_CODE_ENG, + [ + self::TRUCK_ENG, + ], + 1, + ]; + + yield 'Return content item in German that contain "aut*" in name' => [ + $this->createContentNameCriterion('aut*'), + self::LANGUAGE_CODE_GER, + [ + self::CAR_GER, + ], + 1, + ]; + + yield 'Return content items in German that contain "*wagen" in name' => [ + $this->createContentNameCriterion('*wagen'), + self::LANGUAGE_CODE_GER, + [ + self::SPORTS_CAR_GER, + self::TRUCK_GER, + ], + 2, + ]; + + yield 'Return content item in German with name "lastwagen"' => [ + $this->createContentNameCriterion('lastwagen'), + self::LANGUAGE_CODE_GER, + [ + self::TRUCK_GER, + ], + 1, + ]; + } + + private function createTestContentItems(): void + { + foreach (self::CONTENT_ITEMS_MAP as $contentItem) { + $this->createContent( + $contentItem['name'], + $contentItem['mainLanguageCode'], + $contentItem['translations'] + ); + } + } + + /** + * @param array<string> $translations + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function createContent( + string $title, + string $mainLanguageCode, + array $translations + ): Content { + $contentService = self::getContentService(); + $createStruct = $contentService->newContentCreateStruct( + $this->loadContentType('article'), + $mainLanguageCode + ); + + $createStruct->setField('title', new Value($title)); + + if (!empty($translations)) { + foreach ($translations as $languageCode => $translatedName) { + $createStruct->setField('title', new Value($translatedName), $languageCode); + } + } + + $content = $contentService->createContent($createStruct); + + $contentService->publishVersion($content->getVersionInfo()); + + return $content; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function loadContentType(string $contentTypeIdentifier): ContentType + { + return self::getContentTypeService() + ->loadContentTypeByIdentifier($contentTypeIdentifier); + } + + private function createContentNameCriterion(string $value): Criterion + { + return new Criterion\ContentName($value); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException + */ + private function createQuery(Criterion $criterion): Query + { + $query = new Query(); + $query->filter = new Criterion\LogicalAnd( + [$criterion] + ); + + return $query; + } + + /** + * @return array{}|array{ + * languages: array<string> + * } + */ + public function getLanguageFilter(?string $languageCode): array + { + return null !== $languageCode + ? ['languages' => [$languageCode]] + : []; + } +} diff --git a/eZ/Publish/API/Repository/Tests/SearchServiceFulltextTest.php b/tests/integration/Core/Repository/SearchServiceFulltextTest.php similarity index 91% rename from eZ/Publish/API/Repository/Tests/SearchServiceFulltextTest.php rename to tests/integration/Core/Repository/SearchServiceFulltextTest.php index 5085bc8329..a1af8b37a8 100644 --- a/eZ/Publish/API/Repository/Tests/SearchServiceFulltextTest.php +++ b/tests/integration/Core/Repository/SearchServiceFulltextTest.php @@ -4,22 +4,22 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; - -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; use RuntimeException; /** * Test case for full text search in the SearchService. * - * @see \eZ\Publish\API\Repository\SearchService + * @covers \Ibexa\Contracts\Core\Repository\SearchService * @group integration * @group search * @group fulltext @@ -235,7 +235,7 @@ private function doTestFulltextLocationSearch($searchString, array $expectedKeys /** * Assert given $searchResult using $expectedKeys and $idMap. * - * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $searchResult + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult $searchResult * @param array $expectedKeys * @param array $idMap */ @@ -297,7 +297,7 @@ private function mapKeysToIds(array $expectedKeys, array $idMap) /** * Map given $searchResult to an array of Content IDs, ordered and grouped by relevancy score. * - * @param \eZ\Publish\API\Repository\Values\Content\Search\SearchResult $searchResult + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult $searchResult * * @return array */ @@ -349,3 +349,5 @@ private function isSolrMajorVersionInRange(string $minVersion, string $maxVersio return false; } } + +class_alias(SearchServiceFulltextTest::class, 'eZ\Publish\API\Repository\Tests\SearchServiceFulltextTest'); diff --git a/tests/integration/Core/Repository/SearchServiceImageTest.php b/tests/integration/Core/Repository/SearchServiceImageTest.php new file mode 100644 index 0000000000..0d6c0f6731 --- /dev/null +++ b/tests/integration/Core/Repository/SearchServiceImageTest.php @@ -0,0 +1,389 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\FieldType\Image\Orientation; +use Ibexa\Core\FieldType\Image\Value as ImageValue; +use Ibexa\Core\FieldType\TextLine\Value as TextValue; +use Ibexa\Tests\Integration\Core\RepositorySearchTestCase; + +final class SearchServiceImageTest extends RepositorySearchTestCase +{ + private const IMAGE_CONTENT_TYPE = 'image'; + private const IMAGE_FIELD_DEF_IDENTIFIER = 'image'; + private const IMAGE_FILES = [ + 'landscape.jpg', + 'portrait.jpg', + 'square.png', + ]; + + private const IMAGE_FIXTURES_DIR_PATH = __DIR__ . '/_fixtures/image/'; + + protected function setUp(): void + { + parent::setUp(); + + $this->createImages(); + + $this->refreshSearch(); + } + + /** + * @dataProvider provideDataForTestCriterion + * @dataProvider provideInvalidDataForTestCriterion + */ + public function testCriterion( + int $expectedCount, + Query\Criterion $imageCriterion + ): void { + if (getenv('SEARCH_ENGINE') === 'legacy') { + self::markTestSkipped('Image criteria are not supported in Legacy Search Engine'); + } + + $query = new Query(); + $query->filter = new Query\Criterion\LogicalAnd( + [ + new Query\Criterion\ContentTypeIdentifier(self::IMAGE_CONTENT_TYPE), + $imageCriterion, + ] + ); + + $searchHits = self::getSearchService()->findContent($query); + + self::assertSame( + $expectedCount, + $searchHits->totalCount + ); + } + + /** + * @return iterable<array{ + * int, + * \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion + * }> + */ + public function provideDataForTestCriterion(): iterable + { + yield 'Dimensions' => [ + 3, + $this->createDimensionsCriterion( + 0, + 100, + 0, + 100 + ), + ]; + + yield 'FileSize - default values min 0 and max 1' => [ + 3, + $this->createFileSizeCriterion(), + ]; + + yield 'FileSize' => [ + 3, + $this->createFileSizeCriterion(0, 2), + ]; + + yield 'Width' => [ + 3, + $this->createWidthCriterion(0, 100), + ]; + + yield 'Height' => [ + 3, + $this->createHeightCriterion(0, 100), + ]; + + yield 'MimeType - single' => [ + 2, + $this->createMimeTypeCriterion('image/jpeg'), + ]; + + yield 'MimeType - multiple' => [ + 3, + $this->createMimeTypeCriterion( + [ + 'image/jpeg', + 'image/png', + ], + ), + ]; + + yield 'Orientation - landscape' => [ + 1, + $this->createOrientationCriterion(Orientation::LANDSCAPE), + ]; + + yield 'Orientation - portrait' => [ + 1, + $this->createOrientationCriterion(Orientation::PORTRAIT), + ]; + + yield 'Orientation - square' => [ + 1, + $this->createOrientationCriterion(Orientation::SQUARE), + ]; + + yield 'Orientation - multiple' => [ + 3, + $this->createOrientationCriterion( + [ + Orientation::LANDSCAPE, + Orientation::PORTRAIT, + Orientation::SQUARE, + ] + ), + ]; + + yield 'Image' => [ + 2, + new Query\Criterion\Image( + self::IMAGE_FIELD_DEF_IDENTIFIER, + [ + 'mimeTypes' => [ + 'image/jpeg', + 'image/png', + ], + 'size' => [ + 'min' => 0, + 'max' => 1, + ], + 'width' => [ + 'min' => 0, + 'max' => 100, + ], + 'height' => [ + 'min' => 0, + 'max' => 100, + ], + 'orientation' => [ + Orientation::LANDSCAPE, + Orientation::PORTRAIT, + ], + ] + ), + ]; + } + + /** + * @return iterable<array{ + * int, + * \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion + * }> + */ + public function provideInvalidDataForTestCriterion(): iterable + { + yield 'Dimensions - width and height values too large' => [ + 0, + $this->createDimensionsCriterion( + 101, + 200, + 101, + 300 + ), + ]; + + yield 'FileSize - size value too large' => [ + 0, + $this->createFileSizeCriterion( + 1, + 2 + ), + ]; + + yield 'Width - width value to large' => [ + 0, + $this->createWidthCriterion(101, 200), + ]; + + yield 'Height - height value to large' => [ + 0, + $this->createHeightCriterion(101, 300), + ]; + + yield 'MimeType - invalid single mime type' => [ + 0, + $this->createMimeTypeCriterion('image/invalid'), + ]; + + yield 'MimeType - invalid multiple mime types' => [ + 0, + $this->createMimeTypeCriterion( + [ + 'image/invalid', + 'image/gif', + ] + ), + ]; + } + + /** + * @param string|array<string> $value + */ + private function createMimeTypeCriterion($value): Query\Criterion\Image\MimeType + { + return new Query\Criterion\Image\MimeType( + self::IMAGE_FIELD_DEF_IDENTIFIER, + $value + ); + } + + private function createFileSizeCriterion( + int $min = 0, + ?int $max = null + ): Query\Criterion\Image\FileSize { + return new Query\Criterion\Image\FileSize( + self::IMAGE_FIELD_DEF_IDENTIFIER, + $min, + $max + ); + } + + private function createWidthCriterion( + int $min = 0, + ?int $max = null + ): Query\Criterion\Image\Width { + return new Query\Criterion\Image\Width( + self::IMAGE_FIELD_DEF_IDENTIFIER, + $min, + $max + ); + } + + private function createHeightCriterion( + int $min = 0, + ?int $max = null + ): Query\Criterion\Image\Height { + return new Query\Criterion\Image\Height( + self::IMAGE_FIELD_DEF_IDENTIFIER, + $min, + $max + ); + } + + private function createDimensionsCriterion( + int $minWidth, + int $maxWidth, + int $minHeight, + int $maxHeight + ): Query\Criterion\Image\Dimensions { + return new Query\Criterion\Image\Dimensions( + self::IMAGE_FIELD_DEF_IDENTIFIER, + [ + 'width' => [ + 'min' => $minWidth, + 'max' => $maxWidth, + ], + 'height' => [ + 'min' => $minHeight, + 'max' => $maxHeight, + ], + ] + ); + } + + /** + * @param string|array<string> $value + */ + private function createOrientationCriterion($value): Query\Criterion\Image\Orientation + { + return new Query\Criterion\Image\Orientation( + self::IMAGE_FIELD_DEF_IDENTIFIER, + $value + ); + } + + private function createImages(): void + { + $contentType = $this->loadContentTypeImage(); + foreach (self::IMAGE_FILES as $image) { + $this->createContentImage( + $contentType, + self::IMAGE_FIXTURES_DIR_PATH . $image, + $image + ); + } + } + + private function createContentImage( + ContentType $contentType, + string $path, + string $fileName + ): void { + $contentCreateStruct = self::getContentService()->newContentCreateStruct( + $contentType, + 'eng-GB' + ); + + $imageValue = new ImageValue(); + $imageValue->fileName = $fileName; + $imageValue->path = $path; + + $contentCreateStruct->setField('name', new TextValue('Image'), 'eng-GB'); + $contentCreateStruct->setField('image', $imageValue, 'eng-GB'); + + $contentService = self::getContentService(); + $contentService->publishVersion( + $contentService + ->createContent($contentCreateStruct) + ->getVersionInfo() + ); + } + + private function loadContentTypeImage(): ContentType + { + $imageContentType = self::getContentTypeService()->loadContentTypeByIdentifier(self::IMAGE_CONTENT_TYPE); + + $this->ensureImageFieldTypeIsSearchable($imageContentType); + + return $imageContentType; + } + + private function ensureImageFieldTypeIsSearchable(ContentType $contentType): void + { + $fieldDefinition = $contentType->getFieldDefinition(self::IMAGE_FIELD_DEF_IDENTIFIER); + if ( + null === $fieldDefinition + || $fieldDefinition->isSearchable + ) { + return; + } + + $this->setFieldTypeAsSearchable( + self::getContentTypeService()->createContentTypeDraft($contentType), + $fieldDefinition + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function setFieldTypeAsSearchable( + ContentTypeDraft $contentTypeDraft, + FieldDefinition $fieldDefinition + ): void { + $contentTypeService = self::getContentTypeService(); + $fieldDefinitionUpdateStruct = $contentTypeService->newFieldDefinitionUpdateStruct(); + $fieldDefinitionUpdateStruct->isSearchable = true; + + $contentTypeService = self::getContentTypeService(); + $contentTypeService->updateFieldDefinition( + $contentTypeDraft, + $fieldDefinition, + $fieldDefinitionUpdateStruct + ); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + } +} diff --git a/eZ/Publish/API/Repository/Tests/SearchServiceLocationTest.php b/tests/integration/Core/Repository/SearchServiceLocationTest.php similarity index 93% rename from eZ/Publish/API/Repository/Tests/SearchServiceLocationTest.php rename to tests/integration/Core/Repository/SearchServiceLocationTest.php index fbf9d69e4e..374ae91784 100644 --- a/eZ/Publish/API/Repository/Tests/SearchServiceLocationTest.php +++ b/tests/integration/Core/Repository/SearchServiceLocationTest.php @@ -4,23 +4,25 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; - -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use eZ\Publish\Core\Repository\Values\Content\Location; -use EzSystems\EzPlatformSolrSearchEngine\Tests\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory; +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Tests\Core\Repository\Common; +use Ibexa\Tests\Solr\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory; /** * Test case for Location operations in the SearchService. * - * @see eZ\Publish\API\Repository\SearchService + * @covers \Ibexa\Contracts\Core\Repository\SearchService * @group integration * @group search */ @@ -35,7 +37,7 @@ class SearchServiceLocationTest extends BaseTest * * @dataProvider getFacetedSearches * - * @see \eZ\Publish\API\Repository\SearchService::findLoctions() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations */ public function testFindFacetedLocation(LocationQuery $query, $fixture) { @@ -45,7 +47,7 @@ public function testFindFacetedLocation(LocationQuery $query, $fixture) /** * Create movie Content with subtitle field set to null. * - * @return \eZ\Publish\API\Repository\Values\Content\Content[] + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content[] */ protected function createMovieContent(): array { @@ -123,7 +125,7 @@ protected function createMovieContent(): array /** * Create test Content with ezcountry field having multiple countries selected. * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content */ protected function createMultipleCountriesContent() { @@ -171,9 +173,9 @@ protected function createMultipleCountriesContent() } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ protected function createFolderWithNonPrintableUtf8Characters(): Content { @@ -202,7 +204,7 @@ protected function createFolderWithNonPrintableUtf8Characters(): Content /** * Test for the findLocations() method. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() */ public function testFieldIsEmptyInLocation() { @@ -235,7 +237,7 @@ public function testFieldIsEmptyInLocation() /** * Test for the findLocations() method. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() */ public function testFieldIsNotEmptyInLocation() { @@ -265,7 +267,7 @@ public function testFieldIsNotEmptyInLocation() /** * Test for the findLocations() method. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() */ public function testFieldCollectionContains() { @@ -295,8 +297,8 @@ public function testFieldCollectionContains() /** * Test for the findLocations() method. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() - * @depends eZ\Publish\API\Repository\Tests\SearchServiceTest::testFieldCollectionContains + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + * @depends Ibexa\Tests\Integration\Core\Repository\SearchServiceTest::testFieldCollectionContains */ public function testFieldCollectionContainsNoMatch() { @@ -319,11 +321,11 @@ public function testFieldCollectionContainsNoMatch() } /** - * @covers \eZ\Publish\API\Repository\SearchService::findLocations + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testNonPrintableUtf8Characters(): void { @@ -350,12 +352,12 @@ public function testNonPrintableUtf8Characters(): void } /** - * @covers \eZ\Publish\API\Repository\SearchService::findLocations + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations * * @throws \ErrorException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testEscapedNonPrintableUtf8Characters(): void { @@ -464,7 +466,7 @@ static function (SearchHit $searchHit) { /** * Test for the findLocations() method. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() */ public function testQueryCustomField() { @@ -495,12 +497,12 @@ public function testQueryCustomField() * the last_name of admin and anonymous. This is done to show the custom * copy field working. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() */ public function testQueryModifiedField() { // Check using get_class since the others extend SetupFactory\Legacy - if (ltrim(get_class($this->getSetupFactory()), '\\') === 'eZ\Publish\API\Repository\Tests\SetupFactory\Legacy') { + if ($this->getSetupFactory() instanceof Legacy) { $this->markTestIncomplete( 'Custom fields not supported by LegacySE ' . '(@todo: Legacy should fallback to just querying normal field so this should be tested here)' @@ -530,7 +532,7 @@ public function testQueryModifiedField() } /** - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType */ protected function createTestPlaceContentType() { @@ -563,7 +565,7 @@ protected function createTestPlaceContentType() /** * Test for the findLocations() method. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() * @group maplocation */ public function testMapLocationDistanceLessThanOrEqual() @@ -644,7 +646,7 @@ public function testMapLocationDistanceLessThanOrEqual() /** * Test for the findLocations() method. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() * @group maplocation */ public function testMapLocationDistanceGreaterThanOrEqual() @@ -725,7 +727,7 @@ public function testMapLocationDistanceGreaterThanOrEqual() /** * Test for the findLocations() method. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() * @group maplocation */ public function testMapLocationDistanceBetween() @@ -822,7 +824,7 @@ public function testMapLocationDistanceBetween() /** * Test for the findLocations() method. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() * @group maplocation */ public function testMapLocationDistanceSortAscending() @@ -940,7 +942,7 @@ public function testMapLocationDistanceSortAscending() /** * Test for the findLocations() method. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() * @group maplocation */ public function testMapLocationDistanceSortDescending() @@ -1058,7 +1060,7 @@ public function testMapLocationDistanceSortDescending() /** * Test for the findLocations() method. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() * @group maplocation */ public function testMapLocationDistanceWithCustomField() @@ -1142,7 +1144,7 @@ public function testMapLocationDistanceWithCustomField() /** * Test for the findLocations() method. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() * @group maplocation */ public function testMapLocationDistanceWithCustomFieldSort() @@ -1263,7 +1265,7 @@ public function testMapLocationDistanceWithCustomFieldSort() /** * Test for the findLocations() method. * - * @see \eZ\Publish\API\Repository\SearchService::findLocations() + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() */ public function testVisibilityCriterionWithHiddenContent() { @@ -1377,14 +1379,14 @@ protected function assertQueryFixture(LocationQuery $query, $fixture, $closure = } if (!is_file($fixture)) { - if (isset($_ENV['ez_tests_record'])) { + if (isset($_ENV['ibexa_tests_record'])) { file_put_contents( $record = $fixture . '.recording', "<?php\n\nreturn " . var_export($result, true) . ";\n\n" ); $this->markTestIncomplete("No fixture available. Result recorded at $record. Result: \n" . $this->printResult($result)); } else { - $this->markTestIncomplete("No fixture available. Set \$_ENV['ez_tests_record'] to generate:\n " . $fixture); + $this->markTestIncomplete("No fixture available. Set \$_ENV['ibexa_tests_record'] to generate:\n " . $fixture); } } @@ -1478,3 +1480,5 @@ protected function getFixtureDir() return __DIR__ . '/_fixtures/' . getenv('fixtureDir') . '/'; } } + +class_alias(SearchServiceLocationTest::class, 'eZ\Publish\API\Repository\Tests\SearchServiceLocationTest'); diff --git a/tests/integration/Core/Repository/SearchServiceTest.php b/tests/integration/Core/Repository/SearchServiceTest.php new file mode 100644 index 0000000000..87f59cc671 --- /dev/null +++ b/tests/integration/Core/Repository/SearchServiceTest.php @@ -0,0 +1,5315 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use function count; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; +use Ibexa\Tests\Core\Repository\Common; +use Ibexa\Tests\Solr\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory; + +/** + * Test case for operations in the SearchService. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService + * @group integration + * @group search + */ +class SearchServiceTest extends BaseTest +{ + public const QUERY_CLASS = Query::class; + + public const FIND_CONTENT_METHOD = 'findContent'; + public const FIND_LOCATION_METHOD = 'findLocations'; + + public const AVAILABLE_FIND_METHODS = [ + self::FIND_CONTENT_METHOD, + self::FIND_LOCATION_METHOD, + ]; + + use Common\FacetedSearchProvider; + + public function getFilterContentSearches() + { + $fixtureDir = $this->getFixtureDir(); + + return [ + 0 => [ + [ + 'filter' => new Criterion\ContentId( + [1, 4, 10] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'ContentId.php', + ], + 1 => [ + [ + 'filter' => new Criterion\LogicalAnd( + [ + new Criterion\ContentId( + [1, 4, 10] + ), + new Criterion\ContentId( + [4, 12] + ), + ] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'LogicalAnd.php', + ], + 2 => [ + [ + 'filter' => new Criterion\LogicalOr( + [ + new Criterion\ContentId( + [1, 4, 10] + ), + new Criterion\ContentId( + [4, 12] + ), + ] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'LogicalOr.php', + ], + 3 => [ + [ + 'filter' => new Criterion\LogicalAnd( + [ + new Criterion\ContentId( + [1, 4, 10] + ), + new Criterion\LogicalNot( + new Criterion\ContentId( + [10, 12] + ) + ), + ] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'LogicalNot.php', + ], + 4 => [ + [ + 'filter' => new Criterion\LogicalAnd( + [ + new Criterion\ContentId( + [1, 4, 10] + ), + new Criterion\LogicalAnd( + [ + new Criterion\LogicalNot( + new Criterion\ContentId( + [10, 12] + ) + ), + ] + ), + ] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'LogicalNot.php', + ], + 5 => [ + [ + 'filter' => new Criterion\ContentTypeId( + 4 + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'ContentTypeId.php', + ], + 6 => [ + [ + 'filter' => new Criterion\ContentTypeIdentifier( + 'user' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'ContentTypeId.php', + ], + 7 => [ + [ + 'filter' => new Criterion\ContentTypeIdentifier( + 'user', + 'invalid' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'ContentTypeId.php', + ], + 8 => [ + [ + 'filter' => new Criterion\ContentTypeIdentifier( + 'invalid1', + 'invalid2' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'MatchNone.php', + ], + 9 => [ + [ + 'filter' => new Criterion\MatchNone(), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'MatchNone.php', + ], + 10 => [ + [ + 'filter' => new Criterion\ContentTypeGroupId( + 2 + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'ContentTypeGroupId.php', + ], + 11 => [ + [ + 'filter' => new Criterion\DateMetadata( + Criterion\DateMetadata::MODIFIED, + Criterion\Operator::GT, + 1343140540 + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'DateMetadataGt.php', + ], + 12 => [ + [ + 'filter' => new Criterion\DateMetadata( + Criterion\DateMetadata::MODIFIED, + Criterion\Operator::GTE, + 1311154215 + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'DateMetadataGte.php', + ], + 13 => [ + [ + 'filter' => new Criterion\DateMetadata( + Criterion\DateMetadata::MODIFIED, + Criterion\Operator::LTE, + 1311154215 + ), + 'limit' => 10, + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'DateMetadataLte.php', + ], + 14 => [ + [ + 'filter' => new Criterion\DateMetadata( + Criterion\DateMetadata::MODIFIED, + Criterion\Operator::IN, + [1033920794, 1060695457, 1343140540] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'DateMetadataIn.php', + ], + 15 => [ + [ + 'filter' => new Criterion\DateMetadata( + Criterion\DateMetadata::MODIFIED, + Criterion\Operator::BETWEEN, + [1033920776, 1072180276] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'DateMetadataBetween.php', + ], + 16 => [ + [ + 'filter' => new Criterion\DateMetadata( + Criterion\DateMetadata::CREATED, + Criterion\Operator::BETWEEN, + [1033920776, 1072180278] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'DateMetadataCreated.php', + ], + 17 => [ + [ + 'filter' => new Criterion\CustomField( + 'user_group_name_value_s', + Criterion\Operator::EQ, + 'Members' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'Field.php', + ], + 18 => [ + [ + 'filter' => new Criterion\CustomField( + 'user_group_name_value_s', + Criterion\Operator::CONTAINS, + 'Members' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'Field.php', + ], + 19 => [ + [ + 'filter' => new Criterion\CustomField( + 'user_group_name_value_s', + Criterion\Operator::LT, + 'Members' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'CustomFieldLt.php', + ], + 20 => [ + [ + 'filter' => new Criterion\CustomField( + 'user_group_name_value_s', + Criterion\Operator::LTE, + 'Members' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'CustomFieldLte.php', + ], + 21 => [ + [ + 'filter' => new Criterion\CustomField( + 'user_group_name_value_s', + Criterion\Operator::GT, + 'Members' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'CustomFieldGt.php', + ], + 22 => [ + [ + 'filter' => new Criterion\CustomField( + 'user_group_name_value_s', + Criterion\Operator::GTE, + 'Members' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'CustomFieldGte.php', + ], + 23 => [ + [ + 'filter' => new Criterion\CustomField( + 'user_group_name_value_s', + Criterion\Operator::BETWEEN, + ['Administrator users', 'Members'] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'CustomFieldBetween.php', + ], + 24 => [ + [ + 'filter' => new Criterion\RemoteId( + ['f5c88a2209584891056f987fd965b0ba', 'faaeb9be3bd98ed09f606fc16d144eca'] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'RemoteId.php', + ], + 25 => [ + [ + 'filter' => new Criterion\SectionId( + [2] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'SectionId.php', + ], + 26 => [ + [ + 'filter' => new Criterion\Field( + 'name', + Criterion\Operator::EQ, + 'Members' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'Field.php', + ], + 27 => [ + [ + 'filter' => new Criterion\Field( + 'name', + Criterion\Operator::IN, + ['Members', 'Anonymous users'] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'FieldIn.php', + ], + 28 => [ + [ + 'filter' => new Criterion\DateMetadata( + Criterion\DateMetadata::MODIFIED, + Criterion\Operator::BETWEEN, + [1033920275, 1033920794] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'FieldBetween.php', + ], + 29 => [ + [ + 'filter' => new Criterion\LogicalOr( + [ + new Criterion\Field( + 'name', + Criterion\Operator::EQ, + 'Members' + ), + new Criterion\DateMetadata( + Criterion\DateMetadata::MODIFIED, + Criterion\Operator::BETWEEN, + [1033920275, 1033920794] + ), + ] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'FieldOr.php', + ], + 30 => [ + [ + 'filter' => new Criterion\Subtree( + '/1/5/' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'Subtree.php', + ], + 31 => [ + [ + 'filter' => new Criterion\LocationId( + [1, 2, 5] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'LocationId.php', + ], + 32 => [ + [ + 'filter' => new Criterion\ParentLocationId( + [1] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'ParentLocationId.php', + ], + 33 => [ + [ + 'filter' => new Criterion\LocationRemoteId( + ['3f6d92f8044aed134f32153517850f5a', 'f3e90596361e31d496d4026eb624c983'] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'LocationRemoteId.php', + ], + 34 => [ + [ + // There is no Status Criterion anymore, this should match all published as well + 'filter' => new Criterion\Subtree( + '/1/' + ), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'Status.php', + // Result having the same sort level should be sorted between them to be system independent + static function (&$data) { + usort( + $data->searchHits, + static function ($a, $b) { + if ($a->score == $b->score) { + if ($a->valueObject['id'] == $b->valueObject['id']) { + return 0; + } + + // Order by ascending ID + return ($a->valueObject['id'] < $b->valueObject['id']) ? -1 : 1; + } + + // Order by descending score + return ($a->score > $b->score) ? -1 : 1; + } + ); + }, + ], + 35 => [ + [ + 'filter' => new Criterion\UserMetadata( + Criterion\UserMetadata::MODIFIER, + Criterion\Operator::EQ, + 14 + ), + 'sortClauses' => [ + new SortClause\ContentId(), + ], + 'limit' => 50, + ], + $fixtureDir . 'UserMetadata.php', + ], + 36 => [ + [ + 'filter' => new Criterion\UserMetadata( + Criterion\UserMetadata::MODIFIER, + Criterion\Operator::IN, + [14] + ), + 'sortClauses' => [ + new SortClause\ContentId(), + ], + 'limit' => 50, + ], + $fixtureDir . 'UserMetadata.php', + ], + 37 => [ + [ + 'filter' => new Criterion\UserMetadata( + Criterion\UserMetadata::OWNER, + Criterion\Operator::EQ, + 14 + ), + 'sortClauses' => [ + new SortClause\ContentId(), + ], + 'limit' => 50, + ], + $fixtureDir . 'UserMetadata.php', + ], + 38 => [ + [ + 'filter' => new Criterion\UserMetadata( + Criterion\UserMetadata::OWNER, + Criterion\Operator::IN, + [14] + ), + 'sortClauses' => [ + new SortClause\ContentId(), + ], + 'limit' => 50, + ], + $fixtureDir . 'UserMetadata.php', + ], + 39 => [ + [ + 'filter' => new Criterion\UserMetadata( + Criterion\UserMetadata::GROUP, + Criterion\Operator::EQ, + 12 + ), + 'sortClauses' => [ + new SortClause\ContentId(), + ], + 'limit' => 50, + ], + $fixtureDir . 'UserMetadata.php', + ], + 40 => [ + [ + 'filter' => new Criterion\UserMetadata( + Criterion\UserMetadata::GROUP, + Criterion\Operator::IN, + [12] + ), + 'sortClauses' => [ + new SortClause\ContentId(), + ], + 'limit' => 50, + ], + $fixtureDir . 'UserMetadata.php', + ], + 41 => [ + [ + 'filter' => new Criterion\UserMetadata( + Criterion\UserMetadata::GROUP, + Criterion\Operator::EQ, + 4 + ), + 'sortClauses' => [ + new SortClause\ContentId(), + ], + 'limit' => 50, + ], + $fixtureDir . 'UserMetadata.php', + ], + 42 => [ + [ + 'filter' => new Criterion\UserMetadata( + Criterion\UserMetadata::GROUP, + Criterion\Operator::IN, + [4] + ), + 'sortClauses' => [ + new SortClause\ContentId(), + ], + 'limit' => 50, + ], + $fixtureDir . 'UserMetadata.php', + ], + 43 => [ + [ + 'filter' => new Criterion\UserMetadata( + Criterion\UserMetadata::GROUP, + null, + [4] + ), + 'sortClauses' => [ + new SortClause\ContentId(), + ], + 'limit' => 50, + ], + $fixtureDir . 'UserMetadata.php', + ], + 44 => [ + [ + 'filter' => new Criterion\Ancestor( + [ + '/1/5/44/', + '/1/5/44/45/', + ] + ), + 'sortClauses' => [ + new SortClause\ContentId(), + ], + 'limit' => 50, + ], + $fixtureDir . 'AncestorContent.php', + ], + ]; + } + + public function getContentQuerySearches() + { + $fixtureDir = $this->getFixtureDir(); + + return [ + [ + [ + 'filter' => new Criterion\ContentId( + [58, 10] + ), + 'query' => new Criterion\FullText('contact'), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'FullTextFiltered.php', + ], + [ + [ + 'query' => new Criterion\FullText( + 'contact', + [ + 'boost' => [ + 'title' => 2, + ], + 'fuzziness' => .5, + ] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'FullText.php', + ], + [ + [ + 'query' => new Criterion\FullText( + 'Contact*' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'FullTextWildcard.php', + ], + [ + [ + 'query' => new Criterion\LanguageCode('eng-GB', false), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'LanguageCode.php', + ], + [ + [ + 'query' => new Criterion\LanguageCode(['eng-US', 'eng-GB']), + 'offset' => 10, + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'LanguageCodeIn.php', + ], + [ + [ + 'query' => new Criterion\LanguageCode('eng-GB'), + 'offset' => 10, + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'LanguageCodeAlwaysAvailable.php', + ], + [ + [ + 'query' => new Criterion\Visibility( + Criterion\Visibility::VISIBLE + ), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'Visibility.php', + ], + [ + [ + 'query' => new Criterion\IsContainer(true), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 5, + ], + $fixtureDir . 'IsContainerTrue.php', + ], + [ + [ + 'query' => new Criterion\IsContainer(false), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 5, + ], + $fixtureDir . 'IsContainerFalse.php', + ], + [ + [ + 'query' => new Criterion\IsUserEnabled(true), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 5, + ], + $fixtureDir . 'IsUserEnabledTrue.php', + ], + [ + [ + 'query' => new Criterion\IsUserEnabled(false), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 5, + ], + $fixtureDir . 'IsUserEnabledFalse.php', + ], + [ + [ + 'query' => new Criterion\IsUserBased(true), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'IsUserBasedTrue.php', + ], + [ + [ + 'query' => new Criterion\IsUserBased(false), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 1, + ], + $fixtureDir . 'IsUserBasedFalse.php', + ], + [ + [ + 'query' => new Criterion\UserId(14), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'UserIdEq.php', + ], + [ + [ + 'query' => new Criterion\UserId([14, 10]), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'UserIdIn.php', + ], + [ + [ + 'query' => new Criterion\UserLogin('*adm*', Operator::LIKE), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'UserLogin.php', + ], + [ + [ + 'query' => new Criterion\UserLogin('admin', Operator::EQ), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'UserLogin.php', + ], + [ + [ + 'query' => new Criterion\UserLogin(['admin'], Operator::IN), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'UserLogin.php', + ], + [ + [ + 'query' => new Criterion\UserEmail('*anonymous*', Operator::LIKE), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'UserEmail.php', + ], + [ + [ + 'query' => new Criterion\UserEmail('anonymous@link.invalid', Operator::EQ), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'UserEmail.php', + ], + [ + [ + 'query' => new Criterion\UserEmail(['anonymous@link.invalid'], Operator::IN), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'UserEmail.php', + ], + [ + [ + 'query' => new Criterion\SectionIdentifier('users'), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'SectionIdentifier.php', + ], + [ + [ + 'query' => new Criterion\SectionIdentifier(['users']), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'SectionIdentifier.php', + ], + [ + [ + 'query' => new Criterion\ObjectStateIdentifier('not_locked'), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'ObjectStateIdentifier.php', + ], + [ + [ + 'query' => new Criterion\ObjectStateIdentifier(['not_locked']), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'ObjectStateIdentifier.php', + ], + [ + [ + 'query' => new Criterion\ObjectStateIdentifier(['not_locked'], 'ez_lock'), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'ObjectStateIdentifier.php', + ], + [ + [ + 'query' => new Criterion\Sibling(58, 1), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'Sibling.php', + ], + ]; + } + + public function getLocationQuerySearches() + { + $fixtureDir = $this->getFixtureDir(); + + return [ + [ + [ + 'query' => new Criterion\Location\Depth(Criterion\Operator::EQ, 1), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'Depth.php', + ], + [ + [ + 'query' => new Criterion\Location\Depth(Criterion\Operator::IN, [1, 3]), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'DepthIn.php', + ], + [ + [ + 'query' => new Criterion\Location\Depth(Criterion\Operator::GT, 2), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'DepthGt.php', + ], + [ + [ + 'query' => new Criterion\Location\Depth(Criterion\Operator::GTE, 2), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'DepthGte.php', + ], + [ + [ + 'query' => new Criterion\Location\Depth(Criterion\Operator::LT, 2), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'Depth.php', + ], + [ + [ + 'query' => new Criterion\Location\Depth(Criterion\Operator::LTE, 2), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'DepthLte.php', + ], + [ + [ + 'query' => new Criterion\Location\Depth(Criterion\Operator::BETWEEN, [1, 2]), + 'sortClauses' => [new SortClause\ContentId()], + 'limit' => 50, + ], + $fixtureDir . 'DepthLte.php', + ], + [ + [ + 'filter' => new Criterion\Ancestor('/1/5/44/45/'), + 'sortClauses' => [ + new SortClause\Location\Depth(), + ], + 'limit' => 50, + ], + $fixtureDir . 'AncestorLocation.php', + ], + ]; + } + + /** + * Test for the findContent() method. + * + * @dataProvider getFilterContentSearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testFindContentFiltered($queryData, $fixture, $closure = null) + { + $query = new Query($queryData); + $this->assertQueryFixture($query, $fixture, $closure); + } + + /** + * Test for the findContentInfo() method. + * + * @dataProvider getFilterContentSearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContentInfo() + */ + public function testFindContentInfoFiltered($queryData, $fixture, $closure = null) + { + $query = new Query($queryData); + $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true); + } + + /** + * Test for the findLocations() method. + * + * @dataProvider getFilterContentSearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + */ + public function testFindLocationsContentFiltered($queryData, $fixture, $closure = null) + { + $query = new LocationQuery($queryData); + $this->assertQueryFixture($query, $fixture, $closure); + } + + /** + * Test for deprecated $criterion property on query object. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * + * @deprecated + */ + public function testDeprecatedCriteriaProperty() + { + $this->assertQueryFixture( + new Query( + [ + 'query' => new Criterion\ContentId( + [1, 4, 10] + ), + 'sortClauses' => [new SortClause\ContentId()], + ] + ), + $this->getFixtureDir() . 'DeprecatedContentIdQuery.php' + ); + } + + /** + * Test for the findContent() method. + * + * @dataProvider getContentQuerySearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testQueryContent($queryData, $fixture, $closure = null) + { + $query = new Query($queryData); + $this->assertQueryFixture($query, $fixture, $closure); + } + + /** + * Test for the findContentInfo() method. + * + * @dataProvider getContentQuerySearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testQueryContentInfo($queryData, $fixture, $closure = null) + { + $query = new Query($queryData); + $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true); + } + + /** + * Test for the findLocations() method. + * + * @dataProvider getContentQuerySearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + */ + public function testQueryContentLocations($queryData, $fixture, $closure = null) + { + $query = new LocationQuery($queryData); + $this->assertQueryFixture($query, $fixture, $closure); + } + + /** + * Test for the findLocations() method. + * + * @dataProvider getLocationQuerySearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + */ + public function testQueryLocations($queryData, $fixture, $closure = null) + { + $query = new LocationQuery($queryData); + $this->assertQueryFixture($query, $fixture, $closure); + } + + public function getCaseInsensitiveSearches() + { + return [ + [ + [ + 'filter' => new Criterion\Field( + 'name', + Criterion\Operator::EQ, + 'Members' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + ], + [ + [ + 'filter' => new Criterion\Field( + 'name', + Criterion\Operator::EQ, + 'members' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + ], + [ + [ + 'filter' => new Criterion\Field( + 'name', + Criterion\Operator::EQ, + 'MEMBERS' + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + ], + ]; + } + + /** + * Test for the findContent() method. + * + * @dataProvider getCaseInsensitiveSearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testFindContentFieldFiltersCaseSensitivity($queryData) + { + $query = new Query($queryData); + $this->assertQueryFixture( + $query, + $this->getFixtureDir() . 'Field.php' + ); + } + + /** + * Test for the findLocations() method. + * + * @dataProvider getCaseInsensitiveSearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + */ + public function testFindLocationsFieldFiltersCaseSensitivity($queryData) + { + $query = new LocationQuery($queryData); + $this->assertQueryFixture( + $query, + $this->getFixtureDir() . 'Field.php' + ); + } + + public function getRelationFieldFilterSearches() + { + $fixtureDir = $this->getFixtureDir(); + + return [ + 0 => [ + [ + 'filter' => new Criterion\FieldRelation( + 'image', + Criterion\Operator::IN, + [1, 4, 10] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'FieldRelation.php', + ], + 1 => [ + [ + 'filter' => new Criterion\FieldRelation( + 'image', + Criterion\Operator::IN, + [4, 49] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'FieldRelationAll.php', + ], + 2 => [ + [ + 'filter' => new Criterion\FieldRelation( + 'image', + Criterion\Operator::IN, + [4] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'FieldRelation.php', + ], + 3 => [ + [ + 'filter' => new Criterion\FieldRelation( + 'image', + Criterion\Operator::CONTAINS, + [1, 4, 10] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'MatchNone.php', + ], + 4 => [ + [ + 'filter' => new Criterion\FieldRelation( + 'image', + Criterion\Operator::CONTAINS, + [4, 49] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'MatchNone.php', + ], + 5 => [ + [ + 'filter' => new Criterion\FieldRelation( + 'image', + Criterion\Operator::CONTAINS, + [4] + ), + 'sortClauses' => [new SortClause\ContentId()], + ], + $fixtureDir . 'FieldRelation.php', + ], + ]; + } + + /** + * Purely for creating relation data needed for testFindRelationFieldContentInfoFiltered() + * and testFindRelationFieldLocationsFiltered(). + */ + public function testRelationContentCreation() + { + $repository = $this->getRepository(); + $galleryType = $repository->getContentTypeService()->loadContentTypeByIdentifier('gallery'); + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + + $locationCreateStruct = $locationService->newLocationCreateStruct(2); // Home + + $createStruct = $contentService->newContentCreateStruct($galleryType, 'eng-GB'); + $createStruct->setField('name', 'Image gallery'); + $createStruct->setField('image', 49); // Images folder + $draft = $contentService->createContent($createStruct, [$locationCreateStruct]); + $contentService->publishVersion($draft->getVersionInfo()); + + $createStruct = $contentService->newContentCreateStruct($galleryType, 'eng-GB'); + $createStruct->setField('name', 'User gallery'); + $createStruct->setField('image', 4); // User folder + $draft = $contentService->createContent($createStruct, [$locationCreateStruct]); + $contentService->publishVersion($draft->getVersionInfo()); + + $this->refreshSearch($repository); + } + + /** + * Test for FieldRelation using findContentInfo() method. + * + * @dataProvider getRelationFieldFilterSearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContentInfo() + * @depends testRelationContentCreation + */ + public function testFindRelationFieldContentInfoFiltered($queryData, $fixture) + { + $this->getRepository(false); // To make sure repo is setup w/o removing data from getRelationFieldFilterContentSearches + $query = new Query($queryData); + $this->assertQueryFixture($query, $fixture, null, true, true, false); + } + + /** + * Test for FieldRelation using findLocations() method. + * + * @dataProvider getRelationFieldFilterSearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + * @depends testRelationContentCreation + */ + public function testFindRelationFieldLocationsFiltered($queryData, $fixture) + { + $this->getRepository(false); // To make sure repo is setup w/o removing data from getRelationFieldFilterContentSearches + $query = new LocationQuery($queryData); + $this->assertQueryFixture($query, $fixture, null, true, false, false); + } + + public function testFindSingle() + { + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $content = $searchService->findSingle( + new Criterion\ContentId( + [4] + ) + ); + + $this->assertEquals( + 4, + $content->id + ); + } + + public function testFindNoPerformCount() + { + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $query = new Query(); + $query->performCount = false; + $query->query = new Criterion\ContentTypeId( + [4] + ); + + $searchHit = $searchService->findContent($query); + + if ($this->isRunningOnLegacySetup()) { + $this->assertNull( + $searchHit->totalCount + ); + } else { + $this->assertEquals( + 2, + $searchHit->totalCount + ); + } + } + + public function testFindNoPerformCountException() + { + $this->expectException(\RuntimeException::class); + + if (!$this->isRunningOnLegacySetup()) { + $this->markTestSkipped('Only applicable to Legacy/DB based search'); + } + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $query = new Query(); + $query->performCount = false; + $query->limit = 0; + $query->query = new Criterion\ContentTypeId( + [4] + ); + + $searchService->findContent($query); + } + + public function testFindLocationsNoPerformCount() + { + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $query = new LocationQuery(); + $query->performCount = false; + $query->query = new Criterion\ContentTypeId( + [4] + ); + + $searchHit = $searchService->findLocations($query); + + if ($this->isRunningOnLegacySetup()) { + $this->assertNull( + $searchHit->totalCount + ); + } else { + $this->assertEquals( + 2, + $searchHit->totalCount + ); + } + } + + public function testFindLocationsNoPerformCountException() + { + $this->expectException(\RuntimeException::class); + + if (!$this->isRunningOnLegacySetup()) { + $this->markTestSkipped('Only applicable to Legacy/DB based search'); + } + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $query = new LocationQuery(); + $query->performCount = false; + $query->limit = 0; + $query->query = new Criterion\ContentTypeId( + [4] + ); + + $searchService->findLocations($query); + } + + /** + * Create movie Content with subtitle field set to null. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content[] + */ + protected function createMovieContent() + { + $movies = []; + + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + + $createStruct = $contentTypeService->newContentTypeCreateStruct('movie'); + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->remoteId = 'movie-123'; + $createStruct->names = ['eng-GB' => 'Movie']; + $createStruct->creatorId = 14; + $createStruct->creationDate = new \DateTime(); + + $fieldTitle = $contentTypeService->newFieldDefinitionCreateStruct('title', 'ezstring'); + $fieldTitle->names = ['eng-GB' => 'Title']; + $fieldTitle->fieldGroup = 'main'; + $fieldTitle->position = 1; + $fieldTitle->isTranslatable = false; + $fieldTitle->isSearchable = true; + $fieldTitle->isRequired = true; + $createStruct->addFieldDefinition($fieldTitle); + + $fieldSubtitle = $contentTypeService->newFieldDefinitionCreateStruct('subtitle', 'ezstring'); + $fieldSubtitle->names = ['eng-GB' => 'Subtitle']; + $fieldSubtitle->fieldGroup = 'main'; + $fieldSubtitle->position = 2; + $fieldSubtitle->isTranslatable = false; + $fieldSubtitle->isSearchable = true; + $fieldSubtitle->isRequired = false; + $createStruct->addFieldDefinition($fieldSubtitle); + + $contentTypeGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); + $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentTypeGroup]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); + + $createStructRambo = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStructRambo->remoteId = 'movie-456'; + $createStructRambo->alwaysAvailable = false; + $createStructRambo->setField('title', 'Rambo'); + + $ramboDraft = $contentService->createContent($createStructRambo); + $movies[] = $contentService->publishVersion($ramboDraft->getVersionInfo()); + $this->refreshSearch($repository); + $createStructRobocop = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStructRobocop->remoteId = 'movie-789'; + $createStructRobocop->alwaysAvailable = false; + $createStructRobocop->setField('title', 'Robocop'); + $createStructRobocop->setField('subtitle', ''); + + $robocopDraft = $contentService->createContent($createStructRobocop); + $movies[] = $contentService->publishVersion($robocopDraft->getVersionInfo()); + $this->refreshSearch($repository); + $createStructLastHope = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStructLastHope->remoteId = 'movie-101112'; + $createStructLastHope->alwaysAvailable = false; + $createStructLastHope->setField('title', 'Star Wars'); + $createStructLastHope->setField('subtitle', 'Last Hope'); + + $lastHopeDraft = $contentService->createContent($createStructLastHope); + $movies[] = $contentService->publishVersion($lastHopeDraft->getVersionInfo()); + + $this->refreshSearch($repository); + + return $movies; + } + + /** + * Create test Content with ezcountry field having multiple countries selected. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + protected function createMultipleCountriesContent() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + + $createStruct = $contentTypeService->newContentTypeCreateStruct('countries-multiple'); + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->remoteId = 'countries-multiple-123'; + $createStruct->names = ['eng-GB' => 'Multiple countries']; + $createStruct->creatorId = 14; + $createStruct->creationDate = new \DateTime(); + + $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('countries', 'ezcountry'); + $fieldCreate->names = ['eng-GB' => 'Countries']; + $fieldCreate->fieldGroup = 'main'; + $fieldCreate->position = 1; + $fieldCreate->isTranslatable = false; + $fieldCreate->isSearchable = true; + $fieldCreate->fieldSettings = ['isMultiple' => true]; + + $createStruct->addFieldDefinition($fieldCreate); + + $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); + $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->remoteId = 'countries-multiple-456'; + $createStruct->alwaysAvailable = false; + $createStruct->setField( + 'countries', + ['BE', 'DE', 'FR', 'HR', 'NO', 'PT', 'RU'] + ); + + $draft = $contentService->createContent($createStruct); + $content = $contentService->publishVersion($draft->getVersionInfo()); + + $this->refreshSearch($repository); + + return $content; + } + + /** + * Test for the findContent() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content[] + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testFieldIsEmpty() + { + $testContents = $this->createMovieContent(); + + $query = new Query( + [ + 'query' => new Criterion\IsFieldEmpty('subtitle'), + ] + ); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + $result = $searchService->findContent($query, ['eng-GB']); + + $this->assertEquals(2, $result->totalCount); + + $this->assertEquals( + $testContents[0]->id, + $result->searchHits[0]->valueObject->id + ); + $this->assertEquals( + $testContents[1]->id, + $result->searchHits[1]->valueObject->id + ); + + return $testContents; + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testFieldIsNotEmpty() + { + $testContents = $this->createMovieContent(); + + $query = new Query( + [ + 'query' => new Criterion\IsFieldEmpty( + 'subtitle', + false + ), + ] + ); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + $result = $searchService->findContent($query, ['eng-GB']); + + $this->assertEquals(1, $result->totalCount); + $this->assertEquals( + $testContents[2]->id, + $result->searchHits[0]->valueObject->id + ); + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testFieldCollectionContains() + { + $testContent = $this->createMultipleCountriesContent(); + + $query = new Query( + [ + 'query' => new Criterion\Field( + 'countries', + Criterion\Operator::CONTAINS, + 'Belgium' + ), + ] + ); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + $result = $searchService->findContent($query); + + $this->assertEquals(1, $result->totalCount); + $this->assertEquals( + $testContent->id, + $result->searchHits[0]->valueObject->id + ); + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * @depends testFieldCollectionContains + */ + public function testFieldCollectionContainsNoMatch() + { + $this->createMultipleCountriesContent(); + $query = new Query( + [ + 'query' => new Criterion\Field( + 'countries', + Criterion\Operator::CONTAINS, + 'Netherlands Antilles' + ), + ] + ); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + $result = $searchService->findContent($query); + + $this->assertEquals(0, $result->totalCount); + } + + public function testInvalidFieldIdentifierRange() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$criterion->target\' is invalid: No searchable Fields found for the provided Criterion target \'some_hopefully_unknown_field\''); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $searchService->findContent( + new Query( + [ + 'filter' => new Criterion\Field( + 'some_hopefully_unknown_field', + Criterion\Operator::BETWEEN, + [10, 1000] + ), + 'sortClauses' => [new SortClause\ContentId()], + ] + ) + ); + } + + public function testInvalidFieldIdentifierIn() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$criterion->target\' is invalid: No searchable Fields found for the provided Criterion target \'some_hopefully_unknown_field\''); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $searchService->findContent( + new Query( + [ + 'filter' => new Criterion\Field( + 'some_hopefully_unknown_field', + Criterion\Operator::EQ, + 1000 + ), + 'sortClauses' => [new SortClause\ContentId()], + ] + ) + ); + } + + public function testFindContentWithNonSearchableField() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$criterion->target\' is invalid: No searchable Fields found for the provided Criterion target \'tag_cloud_url\''); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $searchService->findContent( + new Query( + [ + 'filter' => new Criterion\Field( + 'tag_cloud_url', + Criterion\Operator::EQ, + 'http://nimbus.com' + ), + 'sortClauses' => [new SortClause\ContentId()], + ] + ) + ); + } + + public function testSortFieldWithNonSearchableField() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$sortClause->targetData\' is invalid: No searchable Fields found for the provided Sort Clause target \'title\' on \'template_look\''); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $searchService->findContent( + new Query( + [ + 'sortClauses' => [new SortClause\Field('template_look', 'title')], + ] + ) + ); + } + + public function testSortMapLocationDistanceWithNonSearchableField() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$sortClause->targetData\' is invalid: No searchable Fields found for the provided Sort Clause target \'title\' on \'template_look\''); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $searchService->findContent( + new Query( + [ + 'sortClauses' => [ + new SortClause\MapLocationDistance( + 'template_look', + 'title', + 1, + 2 + ), + ], + ] + ) + ); + } + + public function testFindSingleFailMultiple() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $searchService->findSingle( + new Criterion\ContentId( + [4, 10] + ) + ); + } + + public function testFindSingleWithNonSearchableField() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $searchService->findSingle( + new Criterion\Field( + 'tag_cloud_url', + Criterion\Operator::EQ, + 'http://nimbus.com' + ) + ); + } + + public function getSortedContentSearches() + { + $fixtureDir = $this->getFixtureDir(); + + yield [ + [ + 'filter' => new Criterion\SectionId([2]), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [], + ], + $fixtureDir . 'SortNone.php', + // Result having the same sort level should be sorted between them to be system independent + static function (&$data): void { + usort( + $data->searchHits, + static function (SearchHit $a, SearchHit $b): int { + return $a->valueObject['id'] <=> $b->valueObject['id']; + } + ); + }, + ]; + + yield [ + [ + 'filter' => new Criterion\SectionId([2]), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [ + new SortClause\DatePublished(), + new SortClause\ContentId(), + ], + ], + $fixtureDir . 'SortDatePublished.php', + ]; + + yield [ + [ + 'filter' => new Criterion\SectionId([2]), + 'offset' => 0, + 'limit' => 50, + 'sortClauses' => [ + new SortClause\DateModified(), + new SortClause\ContentId(), + ], + ], + $fixtureDir . 'SortDateModified.php', + ]; + + yield [ + [ + 'filter' => new Criterion\SectionId([4, 2, 6, 3]), + 'offset' => 0, + 'limit' => 50, + 'sortClauses' => [ + new SortClause\SectionIdentifier(), + new SortClause\ContentId(), + ], + ], + $fixtureDir . 'SortSectionIdentifier.php', + ]; + + yield [ + [ + 'filter' => new Criterion\SectionId([4, 2, 6, 3]), + 'offset' => 0, + 'limit' => 50, + 'sortClauses' => [ + new SortClause\SectionName(), + new SortClause\ContentId(), + ], + ], + $fixtureDir . 'SortSectionName.php', + ]; + + yield [ + [ + 'filter' => new Criterion\SectionId([2, 3]), + 'offset' => 0, + 'limit' => 50, + 'sortClauses' => [ + new SortClause\ContentName(), + new SortClause\ContentId(), + ], + ], + $fixtureDir . 'SortContentName.php', + ]; + + yield [ + [ + 'filter' => new Criterion\ContentTypeId(1), + 'offset' => 0, + 'limit' => 50, + 'sortClauses' => [ + new SortClause\Field('folder', 'name', Query::SORT_ASC), + new SortClause\ContentId(), + ], + ], + $fixtureDir . 'SortFolderName.php', + ]; + + yield [ + [ + 'filter' => new Criterion\ContentTypeId([1, 3]), + 'offset' => 0, + 'limit' => 50, + 'sortClauses' => [ + new SortClause\Field('folder', 'name', Query::SORT_ASC), + new SortClause\ContentId(), + ], + ], + $fixtureDir . 'SortFieldMultipleTypes.php', + ]; + + yield [ + [ + 'filter' => new Criterion\ContentTypeId([1, 3]), + 'offset' => 0, + 'limit' => 50, + 'sortClauses' => [ + new SortClause\Field('folder', 'name', Query::SORT_DESC), + new SortClause\ContentId(), + ], + ], + $fixtureDir . 'SortFieldMultipleTypesReverse.php', + ]; + + yield [ + [ + 'filter' => new Criterion\ContentTypeId([1, 3]), + 'offset' => 3, + 'limit' => 5, + 'sortClauses' => [ + new SortClause\Field('folder', 'name', Query::SORT_ASC), + new SortClause\Field('user', 'first_name', Query::SORT_ASC), + new SortClause\ContentId(), + ], + ], + $fixtureDir . 'SortFieldMultipleTypesSlice.php', + ]; + + yield [ + [ + 'filter' => new Criterion\ContentTypeId([1, 3]), + 'offset' => 3, + 'limit' => 5, + 'sortClauses' => [ + new SortClause\Field('folder', 'name', Query::SORT_DESC), + new SortClause\Field('user', 'first_name', Query::SORT_ASC), + new SortClause\ContentId(), + ], + ], + $fixtureDir . 'SortFieldMultipleTypesSliceReverse.php', + ]; + + if (!$this->isLegacySearchEngineSetup()) { + yield [ + [ + 'filter' => new Criterion\ContentTypeId(1), + 'offset' => 0, + 'limit' => 50, + 'sortClauses' => [ + new SortClause\CustomField('folder_name_value_s', Query::SORT_ASC), + new SortClause\ContentId(), + ], + ], + $fixtureDir . 'SortFolderName.php', + ]; + } + } + + public function getSortedLocationSearches() + { + $fixtureDir = $this->getFixtureDir(); + + return [ + [ + [ + 'filter' => new Criterion\SectionId([2]), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [new SortClause\Location\Path(Query::SORT_DESC)], + ], + $fixtureDir . 'SortPathString.php', + ], + [ + [ + 'filter' => new Criterion\SectionId([2]), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [new SortClause\Location\Depth(Query::SORT_ASC)], + ], + $fixtureDir . 'SortLocationDepth.php', + // Result having the same sort level should be sorted between them to be system independent + static function (&$data) { + // Result with ids: + // 4 has depth = 1 + // 11, 12, 13, 42, 59 have depth = 2 + // 10, 14 have depth = 3 + $map = [ + 4 => 0, + 11 => 1, + 12 => 2, + 13 => 3, + 42 => 4, + 59 => 5, + 10 => 6, + 14 => 7, + ]; + usort( + $data->searchHits, + static function ($a, $b) use ($map) { + return ($map[$a->valueObject['id']] < $map[$b->valueObject['id']]) ? -1 : 1; + } + ); + }, + ], + [ + [ + 'filter' => new Criterion\SectionId([3]), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [ + new SortClause\Location\Path(Query::SORT_DESC), + new SortClause\ContentName(Query::SORT_ASC), + ], + ], + $fixtureDir . 'SortMultiple.php', + ], + [ + [ + 'filter' => new Criterion\SectionId([2]), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [ + new SortClause\Location\Priority(Query::SORT_DESC), + new SortClause\ContentId(), + ], + ], + $fixtureDir . 'SortDesc.php', + ], + ]; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + */ + protected function createTestContentType() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $createStruct = $contentTypeService->newContentTypeCreateStruct('test-type'); + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->names = ['eng-GB' => 'Test type']; + $createStruct->creatorId = 14; + $createStruct->creationDate = new \DateTime(); + + $translatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('integer', 'ezinteger'); + $translatableFieldCreate->names = ['eng-GB' => 'Simple translatable integer field']; + $translatableFieldCreate->fieldGroup = 'main'; + $translatableFieldCreate->position = 1; + $translatableFieldCreate->isTranslatable = true; + $translatableFieldCreate->isSearchable = true; + + $createStruct->addFieldDefinition($translatableFieldCreate); + + $nonTranslatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('integer2', 'ezinteger'); + $nonTranslatableFieldCreate->names = ['eng-GB' => 'Simple non-translatable integer field']; + $nonTranslatableFieldCreate->fieldGroup = 'main'; + $nonTranslatableFieldCreate->position = 2; + $nonTranslatableFieldCreate->isTranslatable = false; + $nonTranslatableFieldCreate->isSearchable = true; + + $createStruct->addFieldDefinition($nonTranslatableFieldCreate); + + $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); + $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); + + return $contentType; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * @param int $fieldValue11 Value for translatable field in first language + * @param int $fieldValue12 Value for translatable field in second language + * @param int $fieldValue2 Value for non translatable field + * @param string $mainLanguageCode + * @param bool $alwaysAvailable + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + protected function createMultilingualContent( + $contentType, + $fieldValue11 = null, + $fieldValue12 = null, + $fieldValue2 = null, + $mainLanguageCode = 'eng-GB', + $alwaysAvailable = false + ) { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = $alwaysAvailable; + $createStruct->mainLanguageCode = $mainLanguageCode; + if ($fieldValue11) { + $createStruct->setField('integer', $fieldValue11, 'eng-GB'); + } + if ($fieldValue12) { + $createStruct->setField('integer', $fieldValue12, 'ger-DE'); + } + $createStruct->setField('integer2', $fieldValue2, $mainLanguageCode); + + $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(2); + $draft = $contentService->createContent($createStruct, [$locationCreateStruct]); + $content = $contentService->publishVersion($draft->getVersionInfo()); + + $this->refreshSearch($repository); + + return $content; + } + + public function providerForTestMultilingualFieldSort() + { + return [ + 0 => [ + [ + 1 => [1, 2, 1], + 2 => [2, 1, 2], + 3 => [2, 1, 3], + 4 => [1, 2, 4], + ], + [ + 'languages' => [ + 'eng-GB', + 'ger-DE', + ], + ], + [ + new SortClause\Field('test-type', 'integer', Query::SORT_ASC), + new SortClause\Field('test-type', 'integer2', Query::SORT_DESC), + ], + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 4, 1, 2, 4 + * Content 1, 1, 2, 1 + * Content 3, 2, 1, 3 + * Content 2, 2, 1, 2 + */ + [4, 1, 3, 2], + ], + 1 => [ + [ + 1 => [1, 2, 1], + 2 => [2, 1, 2], + 3 => [2, 1, 3], + 4 => [1, 2, 4], + ], + [ + 'languages' => [ + 'ger-DE', + 'eng-GB', + ], + ], + [ + new SortClause\Field('test-type', 'integer', Query::SORT_ASC), + new SortClause\Field('test-type', 'integer2', Query::SORT_DESC), + ], + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 3, 2, 1, 3 + * Content 2, 2, 1, 2 + * Content 4, 1, 2, 4 + * Content 1, 1, 2, 1 + */ + [3, 2, 4, 1], + ], + 2 => [ + [ + 1 => [null, 2, null, 'ger-DE'], + 2 => [3, null, null, 'eng-GB'], + 3 => [4, null, null, 'eng-GB'], + 4 => [null, 1, null, 'ger-DE'], + ], + [ + 'languages' => [ + 'eng-GB', + ], + ], + [ + new SortClause\Field('test-type', 'integer', Query::SORT_DESC), + ], + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 3, 4, - + * Content 2, 3, - + */ + [3, 2], + ], + 3 => [ + [ + 1 => [null, 2, null, 'ger-DE'], + 2 => [3, null, null, 'eng-GB'], + 3 => [4, null, null, 'eng-GB'], + 4 => [null, 1, null, 'ger-DE'], + ], + [ + 'languages' => [ + 'ger-DE', + ], + ], + [ + new SortClause\Field('test-type', 'integer', Query::SORT_DESC), + ], + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 1, -, 2 + * Content 4, -, 1 + */ + [1, 4], + ], + 4 => [ + [ + 1 => [null, 2, null, 'ger-DE'], + 2 => [3, null, null, 'eng-GB'], + 3 => [4, null, null, 'eng-GB'], + 4 => [null, 1, null, 'ger-DE'], + ], + [ + 'languages' => [ + 'eng-GB', + 'ger-DE', + ], + ], + [ + new SortClause\Field('test-type', 'integer', Query::SORT_DESC), + ], + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 3, 4, - + * Content 2, 3, - + * Content 1, -, 2 + * Content 4, -, 1 + */ + [3, 2, 1, 4], + ], + 5 => [ + [ + 1 => [null, 2, null, 'ger-DE'], + 2 => [3, null, null, 'eng-GB'], + 3 => [4, null, null, 'eng-GB'], + 4 => [null, 1, null, 'ger-DE'], + ], + [ + 'languages' => [ + 'ger-DE', + 'eng-GB', + ], + ], + [ + new SortClause\Field('test-type', 'integer', Query::SORT_DESC), + ], + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 3, 4, - + * Content 2, 3, - + * Content 1, -, 2 + * Content 4, -, 1 + */ + [3, 2, 1, 4], + ], + 6 => [ + [ + 1 => [null, 2, null, 'ger-DE'], + 2 => [3, 4, null, 'eng-GB'], + 3 => [4, 3, null, 'eng-GB'], + 4 => [null, 1, null, 'ger-DE'], + ], + [ + 'languages' => [ + 'eng-GB', + 'ger-DE', + ], + ], + [ + new SortClause\Field('test-type', 'integer', Query::SORT_DESC), + ], + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 3, 4, 3 + * Content 2, 3, 4 + * Content 1, -, 2 + * Content 4, -, 1 + */ + [3, 2, 1, 4], + ], + 7 => [ + [ + 1 => [null, 2, null, 'ger-DE'], + 2 => [3, 4, null, 'eng-GB'], + 3 => [4, 3, null, 'eng-GB'], + 4 => [null, 1, null, 'ger-DE'], + ], + [ + 'languages' => [ + 'ger-DE', + 'eng-GB', + ], + ], + [ + new SortClause\Field('test-type', 'integer', Query::SORT_DESC), + ], + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 2, 3, 4 + * Content 3, 4, 3 + * Content 1, -, 2 + * Content 4, -, 1 + */ + [2, 3, 1, 4], + ], + 8 => [ + [ + 1 => [null, 1, null, 'ger-DE', true], + 2 => [4, null, null, 'eng-GB', true], + 3 => [3, null, null, 'eng-GB', false], + 4 => [null, 2, null, 'ger-DE', false], + ], + [ + 'languages' => [ + 'eng-GB', + ], + ], + [ + new SortClause\Field('test-type', 'integer', Query::SORT_ASC), + ], + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 1, -, 1 + * Content 3, 3, - + * Content 2, 4, - + */ + [1, 3, 2], + ], + 9 => [ + [ + 1 => [null, 1, null, 'ger-DE', true], + 2 => [4, null, null, 'eng-GB', true], + 3 => [3, null, null, 'eng-GB', false], + 4 => [null, 2, null, 'ger-DE', false], + ], + [ + 'languages' => [ + 'ger-DE', + ], + ], + [ + new SortClause\Field('test-type', 'integer', Query::SORT_DESC), + ], + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 2, 4, - + * Content 4, -, 2 + * Content 1, -, 1 + */ + [2, 4, 1], + ], + 10 => [ + [ + 1 => [null, 1, null, 'ger-DE', true], + 2 => [4, null, null, 'eng-GB', true], + 3 => [3, null, null, 'eng-GB', false], + 4 => [null, 2, null, 'ger-DE', false], + ], + [ + 'languages' => [ + 'eng-GB', + ], + 'useAlwaysAvailable' => false, + ], + [ + new SortClause\Field('test-type', 'integer', Query::SORT_ASC), + ], + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 3, 3, - + * Content 2, 4, - + */ + [3, 2], + ], + 11 => [ + [ + 1 => [null, 1, null, 'ger-DE', true], + 2 => [4, null, null, 'eng-GB', true], + 3 => [3, null, null, 'eng-GB', false], + 4 => [null, 2, null, 'ger-DE', false], + ], + [ + 'languages' => [ + 'ger-DE', + ], + 'useAlwaysAvailable' => false, + ], + [ + new SortClause\Field('test-type', 'integer', Query::SORT_DESC), + ], + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 4, -, 2 + * Content 1, -, 1 + */ + [4, 1], + ], + ]; + } + + /** + * Test for the findContent() method. + * + * @group rrr + * @dataProvider providerForTestMultilingualFieldSort + * + * @param array $contentDataList + * @param array $languageSettings + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause[] $sortClauses + * @param array $expected + */ + public function testMultilingualFieldSortContent( + array $contentDataList, + $languageSettings, + array $sortClauses, + $expected + ) { + $this->assertMultilingualFieldSort( + $contentDataList, + $languageSettings, + $sortClauses, + $expected + ); + } + + /** + * Test for the findLocations() method. + * + * @group rrr + * @dataProvider providerForTestMultilingualFieldSort + * + * @param array $contentDataList + * @param array $languageSettings + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause[] $sortClauses + * @param array $expected + */ + public function testMultilingualFieldSortLocation( + array $contentDataList, + $languageSettings, + array $sortClauses, + $expected + ) { + $this->assertMultilingualFieldSort( + $contentDataList, + $languageSettings, + $sortClauses, + $expected, + false + ); + } + + /** + * @param array $contentDataList + * @param array $languageSettings + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause[] $sortClauses + * @param array $expected + * @param bool $contentSearch + */ + protected function assertMultilingualFieldSort( + array $contentDataList, + $languageSettings, + array $sortClauses, + $expected, + $contentSearch = true + ) { + $contentType = $this->createTestContentType(); + + // Create a draft to account for behaviour with ContentType in different states + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentTypeService->createContentTypeDraft($contentType); + + $defaults = [null, null, null, 'eng-GB', false]; + $contentIdList = []; + foreach ($contentDataList as $key => $contentData) { + $contentData = $contentData + $defaults; + list( + $fieldValue11, + $fieldValue12, + $fieldValue2, + $mainLanguageCode, + $alwaysAvailable + ) = $contentData; + + $contentIdList[$key] = $this->createMultilingualContent( + $contentType, + $fieldValue11, + $fieldValue12, + $fieldValue2, + $mainLanguageCode, + $alwaysAvailable + )->id; + } + + // "article" type Content is not matched, this ensures that non-matched + // field does not affect sort + $dummySortClause = new SortClause\Field('article', 'title', Query::SORT_ASC); + array_unshift($sortClauses, $dummySortClause); + $sortClauses[] = $dummySortClause; + + $searchService = $repository->getSearchService(); + if ($contentSearch) { + $query = new Query( + [ + 'query' => new Criterion\ContentTypeId($contentType->id), + 'sortClauses' => $sortClauses, + ] + ); + $result = $searchService->findContent($query, $languageSettings); + } else { + $query = new LocationQuery( + [ + 'query' => new Criterion\ContentTypeId($contentType->id), + 'sortClauses' => $sortClauses, + ] + ); + $result = $searchService->findLocations($query, $languageSettings); + } + + $this->assertEquals(count($expected), $result->totalCount); + + $expectedIdList = []; + foreach ($expected as $contentNumber) { + $expectedIdList[] = $contentIdList[$contentNumber]; + } + + $this->assertEquals($expectedIdList, $this->mapResultContentIds($result)); + } + + public function providerForTestMultilingualFieldFilter() + { + return [ + 0 => [ + $fixture = [ + 1 => [null, 1, null, 'ger-DE', true], + 2 => [4, null, null, 'eng-GB', true], + 3 => [3, null, null, 'eng-GB', false], + 4 => [null, 2, null, 'ger-DE', false], + 5 => [5, null, null, 'eng-GB', true], + ], + $languageSettings = [ + 'languages' => [ + 'ger-DE', + ], + ], + new Criterion\Field('integer', Criterion\Operator::LT, 5), + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 2, 4, - + * Content 4, -, 2 + * Content 1, -, 1 + */ + [2, 4, 1], + ], + 1 => [ + $fixture, + [ + 'languages' => [ + 'ger-DE', + ], + 'useAlwaysAvailable' => false, + ], + new Criterion\Field('integer', Criterion\Operator::LT, 2), + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 1, -, 1 + */ + [1], + ], + 2 => [ + $fixture, + [ + 'languages' => [ + 'eng-GB', + ], + ], + new Criterion\Field('integer', Criterion\Operator::LTE, 4), + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 5, 5, - + * Content 2, 4, - + * Content 3, 3, - + * Content 1, -, 1 + */ + [2, 3, 1], + ], + 3 => [ + $fixture, + [ + 'languages' => [ + 'eng-GB', + ], + 'useAlwaysAvailable' => false, + ], + new Criterion\Field('integer', Criterion\Operator::LTE, 4), + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 2, 4, - + * Content 3, 3, - + */ + [2, 3], + ], + 4 => [ + $fixture, + $languageSettings, + new Criterion\Field('integer', Criterion\Operator::LTE, 4), + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 2, 4, - + * Content 4, -, 2 + * Content 1, -, 1 + */ + [2, 4, 1], + ], + 5 => [ + $fixture, + $languageSettings, + new Criterion\Field('integer', Criterion\Operator::GT, 1), + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 5, 5, - + * Content 2, 4, - + * Content 4, -, 2 + */ + [5, 2, 4], + ], + 6 => [ + $fixture, + $languageSettings, + new Criterion\Field('integer', Criterion\Operator::GTE, 2), + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 5, 5, - + * Content 2, 4, - + * Content 4, -, 2 + */ + [5, 2, 4], + ], + 7 => [ + $fixture, + $languageSettings, + new Criterion\Field('integer', Criterion\Operator::BETWEEN, [2, 4]), + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 2, 4, - + * Content 4, -, 2 + */ + [2, 4], + ], + 8 => [ + $fixture, + $languageSettings, + new Criterion\Field('integer', Criterion\Operator::BETWEEN, [4, 2]), + [], + ], + 9 => [ + $fixture, + $languageSettings, + new Criterion\Field('integer', Criterion\Operator::EQ, 4), + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 4, -, 2 + */ + [2], + ], + 10 => [ + $fixture, + $languageSettings, + new Criterion\Field('integer', Criterion\Operator::EQ, 2), + /** + * Expected order, Value eng-GB, Value ger-DE. + * + * Content 2, 4, - + */ + [4], + ], + ]; + } + + /** + * Test for the findContent() method. + * + * @group ttt + * @dataProvider providerForTestMultilingualFieldFilter + * + * @param array $contentDataList + * @param array $languageSettings + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion + * @param array $expected + */ + public function testMultilingualFieldFilterContent( + array $contentDataList, + $languageSettings, + Criterion $criterion, + $expected + ) { + $this->assertMultilingualFieldFilter( + $contentDataList, + $languageSettings, + $criterion, + $expected + ); + } + + /** + * Test for the findLocations() method. + * + * @group ttt + * @dataProvider providerForTestMultilingualFieldFilter + * + * @param array $contentDataList + * @param array $languageSettings + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion + * @param array $expected + */ + public function testMultilingualFieldFilterLocation( + array $contentDataList, + $languageSettings, + Criterion $criterion, + $expected + ) { + $this->assertMultilingualFieldFilter( + $contentDataList, + $languageSettings, + $criterion, + $expected, + false + ); + } + + /** + * @param array $contentDataList + * @param array $languageSettings + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterion + * @param array $expected + * @param bool $contentSearch + */ + protected function assertMultilingualFieldFilter( + array $contentDataList, + $languageSettings, + Criterion $criterion, + $expected, + $contentSearch = true + ) { + $contentType = $this->createTestContentType(); + + // Create a draft to account for behaviour with ContentType in different states + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentTypeService->createContentTypeDraft($contentType); + + $defaults = [null, null, null, 'eng-GB', false]; + $contentIdList = []; + foreach ($contentDataList as $key => $contentData) { + $contentData = $contentData + $defaults; + list( + $fieldValue11, + $fieldValue12, + $fieldValue2, + $mainLanguageCode, + $alwaysAvailable + ) = $contentData; + + $contentIdList[$key] = $this->createMultilingualContent( + $contentType, + $fieldValue11, + $fieldValue12, + $fieldValue2, + $mainLanguageCode, + $alwaysAvailable + )->id; + } + + $sortClause = new SortClause\Field('test-type', 'integer', Query::SORT_DESC); + $searchService = $repository->getSearchService(); + if ($contentSearch) { + $query = new Query( + [ + 'query' => new Criterion\LogicalAnd( + [ + new Criterion\ContentTypeId($contentType->id), + $criterion, + ] + ), + 'sortClauses' => [$sortClause], + ] + ); + $result = $searchService->findContent($query, $languageSettings); + } else { + $query = new LocationQuery( + [ + 'query' => new Criterion\LogicalAnd( + [ + new Criterion\ContentTypeId($contentType->id), + $criterion, + ] + ), + 'sortClauses' => [$sortClause], + ] + ); + $result = $searchService->findLocations($query, $languageSettings); + } + + $this->assertEquals(count($expected), $result->totalCount); + + $expectedIdList = []; + foreach ($expected as $contentNumber) { + $expectedIdList[] = $contentIdList[$contentNumber]; + } + + $this->assertEquals($expectedIdList, $this->mapResultContentIds($result)); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult $result + * + * @return array + */ + protected function mapResultContentIds(SearchResult $result) + { + return array_map( + static function (SearchHit $searchHit) { + if ($searchHit->valueObject instanceof Location) { + return $searchHit->valueObject->contentInfo->id; + } + + return $searchHit->valueObject->id; + }, + $result->searchHits + ); + } + + /** + * Test for the findContent() method. + * + * @dataProvider getSortedContentSearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testFindAndSortContent($queryData, $fixture, $closure = null) + { + $query = new Query($queryData); + $this->assertQueryFixture($query, $fixture, $closure); + } + + /** + * Test for the findContentInfo() method. + * + * @dataProvider getSortedContentSearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContentInfo() + */ + public function testFindAndSortContentInfo($queryData, $fixture, $closure = null) + { + $query = new Query($queryData); + $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure($closure), true); + } + + /** + * Test for the findLocations() method. + * + * @dataProvider getSortedContentSearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + */ + public function testFindAndSortContentLocations($queryData, $fixture, $closure = null) + { + $query = new LocationQuery($queryData); + $this->assertQueryFixture($query, $fixture, $closure); + } + + /** + * Test for the findLocations() method. + * + * @dataProvider getSortedLocationSearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + */ + public function testFindAndSortLocations($queryData, $fixture, $closure = null) + { + $query = new LocationQuery($queryData); + $this->assertQueryFixture($query, $fixture, $closure); + } + + /** + * Test for the findContent() method. + * + * @dataProvider getFacetedSearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testFindFacetedContent(Query $query, $fixture) + { + $this->assertQueryFixture($query, $fixture); + } + + /** + * Test for the findContentInfo() method. + * + * @dataProvider getFacetedSearches + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContentInfo() + */ + public function testFindFacetedContentInfo(Query $query, $fixture) + { + $this->assertQueryFixture($query, $fixture, $this->getContentInfoFixtureClosure(), true); + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testQueryCustomField() + { + $query = new Query( + [ + 'query' => new Criterion\CustomField( + 'custom_field', + Criterion\Operator::EQ, + 'AdMiNiStRaToR' + ), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [new SortClause\ContentId()], + ] + ); + $this->assertQueryFixture( + $query, + $this->getFixtureDir() . '/QueryCustomField.php' + ); + } + + /** + * Test for the findContent() method. + * + * This tests explicitely queries the first_name while user is contained in + * the last_name of admin and anonymous. This is done to show the custom + * copy field working. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testQueryModifiedField() + { + // Check using get_class since the others extend SetupFactory\Legacy + if ($this->getSetupFactory() instanceof Legacy) { + $this->markTestIncomplete( + 'Custom fields not supported by LegacySE ' . + '(@todo: Legacy should fallback to just querying normal field so this should be tested here)' + ); + } + + $query = new Query( + [ + 'query' => new Criterion\Field( + 'first_name', + Criterion\Operator::EQ, + 'User' + ), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [new SortClause\ContentId()], + ] + ); + $query->query->setCustomField('user', 'first_name', 'custom_field'); + + $this->assertQueryFixture( + $query, + $this->getFixtureDir() . '/QueryModifiedField.php' + ); + } + + /** + * Test for the findContent() method. + * + * This tests first explicitly creates sort clause on the 'short_name' which is empty + * for all Content instances of 'folder' ContentType. Custom sort field is then set + * to the index storage name of folder's 'name' field, in order to show the custom + * sort field working. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testSortModifiedField() + { + // Check using get_class since the others extend SetupFactory\Legacy + if ($this->getSetupFactory() instanceof Legacy) { + $this->markTestIncomplete( + 'Custom field sort not supported by LegacySE ' . + '(@todo: Legacy should fallback to just querying normal field so this should be tested here)' + ); + } + + $sortClause = new SortClause\Field('folder', 'short_name', Query::SORT_ASC); + $sortClause->setCustomField('folder', 'short_name', 'folder_name_value_s'); + + $query = new Query( + [ + 'filter' => new Criterion\ContentTypeId(1), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [ + $sortClause, + new SortClause\ContentId(), + ], + ] + ); + + $this->assertQueryFixture( + $query, + $this->getFixtureDir() . '/SortFolderName.php' + ); + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + */ + protected function createTestPlaceContentType() + { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + + $createStruct = $contentTypeService->newContentTypeCreateStruct('testtype'); + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->names = ['eng-GB' => 'Test type']; + $createStruct->creatorId = 14; + $createStruct->creationDate = new \DateTime(); + + $translatableFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('maplocation', 'ezgmaplocation'); + $translatableFieldCreate->names = ['eng-GB' => 'Map location field']; + $translatableFieldCreate->fieldGroup = 'main'; + $translatableFieldCreate->position = 1; + $translatableFieldCreate->isTranslatable = false; + $translatableFieldCreate->isSearchable = true; + + $createStruct->addFieldDefinition($translatableFieldCreate); + + $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); + $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); + + return $contentType; + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * @group maplocation + */ + public function testMapLocationDistanceLessThanOrEqual() + { + $contentType = $this->createTestPlaceContentType(); + + // Create a draft to account for behaviour with ContentType in different states + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + $contentTypeService->createContentTypeDraft($contentType); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.894877, + 'longitude' => 15.972699, + 'address' => 'Here be wild boars', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.927334, + 'longitude' => 15.934847, + 'address' => 'A lone tree', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $tree = $contentService->publishVersion($draft->getVersionInfo()); + + $this->refreshSearch($repository); + + $query = new Query( + [ + 'filter' => new Criterion\LogicalAnd( + [ + new Criterion\ContentTypeId($contentType->id), + new Criterion\MapLocationDistance( + 'maplocation', + Criterion\Operator::LTE, + 240, + 43.756825, + 15.775074 + ), + ] + ), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [], + ] + ); + + $searchService = $repository->getSearchService(); + $result = $searchService->findContent($query); + + $this->assertEquals(1, $result->totalCount); + $this->assertEquals( + $wildBoars->id, + $result->searchHits[0]->valueObject->id + ); + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * @group maplocation + */ + public function testMapLocationDistanceGreaterThanOrEqual() + { + $contentType = $this->createTestPlaceContentType(); + + // Create a draft to account for behaviour with ContentType in different states + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + $contentTypeService->createContentTypeDraft($contentType); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.894877, + 'longitude' => 15.972699, + 'address' => 'Here be wild boars', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.927334, + 'longitude' => 15.934847, + 'address' => 'A lone tree', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $tree = $contentService->publishVersion($draft->getVersionInfo()); + + $this->refreshSearch($repository); + + $query = new Query( + [ + 'filter' => new Criterion\LogicalAnd( + [ + new Criterion\ContentTypeId($contentType->id), + new Criterion\MapLocationDistance( + 'maplocation', + Criterion\Operator::GTE, + 240, + 43.756825, + 15.775074 + ), + ] + ), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [], + ] + ); + + $searchService = $repository->getSearchService(); + $result = $searchService->findContent($query); + + $this->assertEquals(1, $result->totalCount); + $this->assertEquals( + $tree->id, + $result->searchHits[0]->valueObject->id + ); + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * @group maplocation + */ + public function testMapLocationDistanceBetween() + { + $contentType = $this->createTestPlaceContentType(); + + // Create a draft to account for behaviour with ContentType in different states + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + $contentTypeService->createContentTypeDraft($contentType); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.894877, + 'longitude' => 15.972699, + 'address' => 'Here be wild boars', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.927334, + 'longitude' => 15.934847, + 'address' => 'A lone tree', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $tree = $contentService->publishVersion($draft->getVersionInfo()); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.903777, + 'longitude' => 15.958788, + 'address' => 'Meadow with mushrooms', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $mushrooms = $contentService->publishVersion($draft->getVersionInfo()); + + $this->refreshSearch($repository); + + $query = new Query( + [ + 'filter' => new Criterion\LogicalAnd( + [ + new Criterion\ContentTypeId($contentType->id), + new Criterion\MapLocationDistance( + 'maplocation', + Criterion\Operator::BETWEEN, + [239, 241], + 43.756825, + 15.775074 + ), + ] + ), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [], + ] + ); + + $searchService = $repository->getSearchService(); + $result = $searchService->findContent($query); + + $this->assertEquals(1, $result->totalCount); + $this->assertEquals( + $mushrooms->id, + $result->searchHits[0]->valueObject->id + ); + } + + /** + * Test for the findContent() method. + * + * This tests the distance over the pole. The tests intentionally uses large range, + * as the flat Earth model used in Legacy Storage Search is not precise for the use case. + * What is tested here is that outer bounding box is correctly calculated, so that + * location is not excluded. + * + * Range between 222km and 350km shows the magnitude of error between great-circle + * (always very precise) and flat Earth (very imprecise for this use case) models. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * @group maplocation + */ + public function testMapLocationDistanceBetweenPolar() + { + $contentType = $this->createTestPlaceContentType(); + + // Create a draft to account for behaviour with ContentType in different states + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + $contentTypeService->createContentTypeDraft($contentType); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 89, + 'longitude' => -164, + 'address' => 'Polar bear media tower', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $polarBear = $contentService->publishVersion($draft->getVersionInfo()); + + $this->refreshSearch($repository); + + $query = new Query( + [ + 'filter' => new Criterion\LogicalAnd( + [ + new Criterion\ContentTypeId($contentType->id), + new Criterion\MapLocationDistance( + 'maplocation', + Criterion\Operator::BETWEEN, + [221, 350], + 89, + 16 + ), + ] + ), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [], + ] + ); + + $searchService = $repository->getSearchService(); + $result = $searchService->findContent($query); + + $this->assertEquals(1, $result->totalCount); + $this->assertEquals( + $polarBear->id, + $result->searchHits[0]->valueObject->id + ); + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * @group maplocation + */ + public function testMapLocationDistanceSortAscending() + { + $contentType = $this->createTestPlaceContentType(); + + // Create a draft to account for behaviour with ContentType in different states + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + $contentTypeService->createContentTypeDraft($contentType); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.894877, + 'longitude' => 15.972699, + 'address' => 'Here be wild boars', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.927334, + 'longitude' => 15.934847, + 'address' => 'A lone tree', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $tree = $contentService->publishVersion($draft->getVersionInfo()); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.903777, + 'longitude' => 15.958788, + 'address' => 'Meadow with mushrooms', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $mushrooms = $contentService->publishVersion($draft->getVersionInfo()); + + $this->refreshSearch($repository); + + $wellInVodice = [ + 'latitude' => 43.756825, + 'longitude' => 15.775074, + ]; + + $query = new Query( + [ + 'filter' => new Criterion\LogicalAnd( + [ + new Criterion\ContentTypeId($contentType->id), + new Criterion\MapLocationDistance( + 'maplocation', + Criterion\Operator::GTE, + 235, + $wellInVodice['latitude'], + $wellInVodice['longitude'] + ), + ] + ), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [ + new SortClause\MapLocationDistance( + 'testtype', + 'maplocation', + $wellInVodice['latitude'], + $wellInVodice['longitude'], + Query::SORT_ASC + ), + ], + ] + ); + + $searchService = $repository->getSearchService(); + $result = $searchService->findContent($query); + + $this->assertEquals(3, $result->totalCount); + $this->assertEquals( + $wildBoars->id, + $result->searchHits[0]->valueObject->id + ); + $this->assertEquals( + $mushrooms->id, + $result->searchHits[1]->valueObject->id + ); + $this->assertEquals( + $tree->id, + $result->searchHits[2]->valueObject->id + ); + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * @group maplocation + */ + public function testMapLocationDistanceSortDescending() + { + $contentType = $this->createTestPlaceContentType(); + + // Create a draft to account for behaviour with ContentType in different states + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + $contentTypeService->createContentTypeDraft($contentType); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.894877, + 'longitude' => 15.972699, + 'address' => 'Here be wild boars', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.927334, + 'longitude' => 15.934847, + 'address' => 'A lone tree', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $tree = $contentService->publishVersion($draft->getVersionInfo()); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.903777, + 'longitude' => 15.958788, + 'address' => 'Meadow with mushrooms', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $mushrooms = $contentService->publishVersion($draft->getVersionInfo()); + + $this->refreshSearch($repository); + + $well = [ + 'latitude' => 43.756825, + 'longitude' => 15.775074, + ]; + + $query = new Query( + [ + 'filter' => new Criterion\LogicalAnd( + [ + new Criterion\ContentTypeId($contentType->id), + new Criterion\MapLocationDistance( + 'maplocation', + Criterion\Operator::GTE, + 235, + $well['latitude'], + $well['longitude'] + ), + ] + ), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [ + new SortClause\MapLocationDistance( + 'testtype', + 'maplocation', + $well['latitude'], + $well['longitude'], + Query::SORT_DESC + ), + ], + ] + ); + + $searchService = $repository->getSearchService(); + $result = $searchService->findContent($query); + + $this->assertEquals(3, $result->totalCount); + $this->assertEquals( + $wildBoars->id, + $result->searchHits[2]->valueObject->id + ); + $this->assertEquals( + $mushrooms->id, + $result->searchHits[1]->valueObject->id + ); + $this->assertEquals( + $tree->id, + $result->searchHits[0]->valueObject->id + ); + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * @group maplocation + */ + public function testMapLocationDistanceWithCustomField() + { + $contentType = $this->createTestPlaceContentType(); + + // Create a draft to account for behaviour with ContentType in different states + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + $contentTypeService->createContentTypeDraft($contentType); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.894877, + 'longitude' => 15.972699, + 'address' => 'Here be wild boars', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.927334, + 'longitude' => 15.934847, + 'address' => 'A lone tree', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $tree = $contentService->publishVersion($draft->getVersionInfo()); + + $this->refreshSearch($repository); + + $distanceCriterion = new Criterion\MapLocationDistance( + 'maplocation', + Criterion\Operator::LTE, + 240, + 43.756825, + 15.775074 + ); + $distanceCriterion->setCustomField('testtype', 'maplocation', 'custom_geolocation_field'); + + $query = new Query( + [ + 'filter' => new Criterion\LogicalAnd( + [ + new Criterion\ContentTypeId($contentType->id), + $distanceCriterion, + ] + ), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [], + ] + ); + + $searchService = $repository->getSearchService(); + $result = $searchService->findContent($query); + + $this->assertEquals(1, $result->totalCount); + $this->assertEquals( + $wildBoars->id, + $result->searchHits[0]->valueObject->id + ); + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * @group maplocation + */ + public function testMapLocationDistanceWithCustomFieldSort() + { + $contentType = $this->createTestPlaceContentType(); + + // Create a draft to account for behaviour with ContentType in different states + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + $contentTypeService->createContentTypeDraft($contentType); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.894877, + 'longitude' => 15.972699, + 'address' => 'Here be wild boars', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $wildBoars = $contentService->publishVersion($draft->getVersionInfo()); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.927334, + 'longitude' => 15.934847, + 'address' => 'A lone tree', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $tree = $contentService->publishVersion($draft->getVersionInfo()); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->setField( + 'maplocation', + [ + 'latitude' => 45.903777, + 'longitude' => 15.958788, + 'address' => 'Meadow with mushrooms', + ], + 'eng-GB' + ); + + $draft = $contentService->createContent($createStruct); + $mushrooms = $contentService->publishVersion($draft->getVersionInfo()); + + $this->refreshSearch($repository); + + $well = [ + 'latitude' => 43.756825, + 'longitude' => 15.775074, + ]; + + $sortClause = new SortClause\MapLocationDistance( + 'testtype', + 'maplocation', + $well['latitude'], + $well['longitude'], + Query::SORT_DESC + ); + $sortClause->setCustomField('testtype', 'maplocation', 'custom_geolocation_field'); + + $query = new Query( + [ + 'filter' => new Criterion\LogicalAnd( + [ + new Criterion\ContentTypeId($contentType->id), + new Criterion\MapLocationDistance( + 'maplocation', + Criterion\Operator::GTE, + 235, + $well['latitude'], + $well['longitude'] + ), + ] + ), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [ + $sortClause, + ], + ] + ); + + $searchService = $repository->getSearchService(); + $result = $searchService->findContent($query); + + $this->assertEquals(3, $result->totalCount); + $this->assertEquals( + $wildBoars->id, + $result->searchHits[2]->valueObject->id + ); + $this->assertEquals( + $mushrooms->id, + $result->searchHits[1]->valueObject->id + ); + $this->assertEquals( + $tree->id, + $result->searchHits[0]->valueObject->id + ); + } + + /** + * Test for the findLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + */ + public function testFindMainLocation() + { + $plainSiteLocationId = 56; + $designLocationId = 58; + $partnersContentId = 59; + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $contentService = $repository->getContentService(); + + // Add secondary Location for "Partners" user group, under "Design" page + $locationService->createLocation( + $contentService->loadContentInfo($partnersContentId), + $locationService->newLocationCreateStruct($designLocationId) + ); + + $this->refreshSearch($repository); + + $query = new LocationQuery( + [ + 'filter' => new Criterion\LogicalAnd( + [ + new Criterion\ParentLocationId($designLocationId), + new Criterion\Location\IsMainLocation( + Criterion\Location\IsMainLocation::MAIN + ), + ] + ), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [], + ] + ); + + $searchService = $repository->getSearchService(); + $result = $searchService->findLocations($query); + + $this->assertEquals(1, $result->totalCount); + $this->assertEquals($plainSiteLocationId, $result->searchHits[0]->valueObject->id); + } + + /** + * Test for the findLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + */ + public function testFindNonMainLocation() + { + $designLocationId = 58; + $partnersContentId = 59; + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $contentService = $repository->getContentService(); + + // Add secondary Location for "Partners" user group, under "Design" page + $newLocation = $locationService->createLocation( + $contentService->loadContentInfo($partnersContentId), + $locationService->newLocationCreateStruct($designLocationId) + ); + + $this->refreshSearch($repository); + + $query = new LocationQuery( + [ + 'filter' => new Criterion\LogicalAnd( + [ + new Criterion\ParentLocationId($designLocationId), + new Criterion\Location\IsMainLocation( + Criterion\Location\IsMainLocation::NOT_MAIN + ), + ] + ), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [], + ] + ); + + $searchService = $repository->getSearchService(); + $result = $searchService->findLocations($query); + + $this->assertEquals(1, $result->totalCount); + $this->assertEquals($newLocation->id, $result->searchHits[0]->valueObject->id); + } + + /** + * Test for the findLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + */ + public function testSortMainLocationAscending() + { + $plainSiteLocationId = 56; + $designLocationId = 58; + $partnersContentId = 59; + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $contentService = $repository->getContentService(); + + // Add secondary Location for "Partners" user group, under "Design" page + $newLocation = $locationService->createLocation( + $contentService->loadContentInfo($partnersContentId), + $locationService->newLocationCreateStruct($designLocationId) + ); + + $this->refreshSearch($repository); + + $query = new LocationQuery( + [ + 'filter' => new Criterion\ParentLocationId($designLocationId), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [ + new SortClause\Location\IsMainLocation( + LocationQuery::SORT_ASC + ), + ], + ] + ); + + $searchService = $repository->getSearchService(); + $result = $searchService->findLocations($query); + + $this->assertEquals(2, $result->totalCount); + $this->assertEquals($newLocation->id, $result->searchHits[0]->valueObject->id); + $this->assertEquals($plainSiteLocationId, $result->searchHits[1]->valueObject->id); + } + + /** + * Test for the findLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + */ + public function testSortMainLocationDescending() + { + $plainSiteLocationId = 56; + $designLocationId = 58; + $partnersContentId = 59; + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + $contentService = $repository->getContentService(); + + // Add secondary Location for "Partners" user group, under "Design" page + $newLocation = $locationService->createLocation( + $contentService->loadContentInfo($partnersContentId), + $locationService->newLocationCreateStruct($designLocationId) + ); + + $this->refreshSearch($repository); + + $query = new LocationQuery( + [ + 'filter' => new Criterion\ParentLocationId($designLocationId), + 'offset' => 0, + 'limit' => 10, + 'sortClauses' => [ + new SortClause\Location\IsMainLocation( + LocationQuery::SORT_DESC + ), + ], + ] + ); + + $searchService = $repository->getSearchService(); + $result = $searchService->findLocations($query); + + $this->assertEquals(2, $result->totalCount); + $this->assertEquals($plainSiteLocationId, $result->searchHits[0]->valueObject->id); + $this->assertEquals($newLocation->id, $result->searchHits[1]->valueObject->id); + } + + /** + * Test for the findLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + */ + public function testContentWithMultipleLocations() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + + $forumType = $contentTypeService->loadContentTypeByIdentifier('forum'); + + $createStruct = $contentService->newContentCreateStruct($forumType, 'eng-GB'); + $createStruct->alwaysAvailable = false; + $createStruct->setField('name', 'An awesome duplicate forum'); + + $draft = $contentService->createContent($createStruct); + $content = $contentService->publishVersion($draft->getVersionInfo()); + + $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(2); + $location1 = $locationService->createLocation($content->contentInfo, $locationCreateStruct); + $locationCreateStruct = $repository->getLocationService()->newLocationCreateStruct(5); + $location2 = $locationService->createLocation($content->contentInfo, $locationCreateStruct); + + $this->refreshSearch($repository); + + $query = new LocationQuery( + [ + 'filter' => new Criterion\ContentId($content->id), + ] + ); + + $searchService = $repository->getSearchService(); + $result = $searchService->findLocations($query); + + $this->assertEquals(2, $result->totalCount); + $locationIds = array_map( + static function (SearchHit $searchHit): int { + return $searchHit->valueObject->id; + }, + $result->searchHits + ); + $this->assertContains($location1->id, $locationIds); + $this->assertContains($location2->id, $locationIds); + } + + protected function createContentForTestUserMetadataGroupHorizontal() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + $userService = $repository->getUserService(); + $permissionResolver = $repository->getPermissionResolver(); + + $administratorUser = $userService->loadUser($permissionResolver->getCurrentUserReference()->getUserId()); + // ID of the "Administrators" user group in an Ibexa demo installation + $administratorsUserGroupId = 12; + // ID of the "Editors" user group in an Ibexa demo installation + $editorsUserGroupId = 13; + + $administratorsUserGroup = $userService->loadUserGroup($administratorsUserGroupId); + $editorsUserGroup = $userService->loadUserGroup($editorsUserGroupId); + + // Add additional Location for Administrators UserGroup under Editors UserGroup Location + $locationCreateStruct = $locationService->newLocationCreateStruct( + $editorsUserGroup->contentInfo->mainLocationId + ); + $newAdministratorsUserGroupLocation = $locationService->createLocation( + $administratorsUserGroup->contentInfo, + $locationCreateStruct + ); + + // Add additional Location for administrator user under newly created UserGroup Location + $locationCreateStruct = $locationService->newLocationCreateStruct( + $newAdministratorsUserGroupLocation->id + ); + $locationService->createLocation( + $administratorUser->contentInfo, + $locationCreateStruct + ); + + // Create a Content to be found through Editors UserGroup id. + // This ensures data is indexed, it could also be done by updating metadata of + // an existing Content, but listener would need to reindex Content and that should + // be tested elsewhere (dedicated indexing integration tests, missing ATM). + $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->setField('name', 'test'); + + $locationCreateStruct = $locationService->newLocationCreateStruct(2); + $draft = $contentService->createContent($createStruct, [$locationCreateStruct]); + $content = $contentService->publishVersion($draft->getVersionInfo()); + $contentTypeService->createContentTypeDraft($contentType); + + $this->refreshSearch($repository); + + return $content; + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testUserMetadataGroupHorizontalFilterContent($queryType = null) + { + if ($queryType === null) { + $queryType = 'filter'; + } + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + $editorsUserGroupId = 13; + + $content = $this->createContentForTestUserMetadataGroupHorizontal(); + + $criteria = []; + $setupFactory = $this->getSetupFactory(); + + // Do not limit for LSE, as it does not not require reindexing. + // See explanation below. + if ($setupFactory instanceof LegacySolrSetupFactory) { + $criteria[] = new Criterion\ContentTypeIdentifier('folder'); + } + + $criteria[] = new Criterion\UserMetadata( + Criterion\UserMetadata::GROUP, + Criterion\Operator::EQ, + $editorsUserGroupId + ); + + $query = new Query( + [ + $queryType => new Criterion\LogicalAnd($criteria), + 'sortClauses' => [ + new SortClause\ContentId(), + ], + 'limit' => 50, + ] + ); + + if ($setupFactory instanceof LegacySolrSetupFactory) { + $result = $searchService->findContent($query); + + // Administrator User is owned by itself, when additional Locations are added + // it should be reindexed and its UserGroups will updated, which means it should + // also be found as a Content of Editors UserGroup. However we do not handle this + // in listeners yet, and also miss SPI methods to do it without using Search (also + // needed to decouple services), because as indexing is asynchronous Search + // should not eat its own dog food for reindexing. + $this->assertEquals(1, $result->totalCount); + + $this->assertEquals( + $content->id, + $result->searchHits[0]->valueObject->id + ); + } else { + // This is how it should eventually work for all search engines, + // with required reindexing listeners properly implemented. + + $result = $searchService->findContent($query); + + // Assert last hit manually, as id will change because it is created in test + // and not present it base fixture. + $foundContent1 = array_pop($result->searchHits); + $result->totalCount = $result->totalCount - 1; + $this->assertEquals($content->id, $foundContent1->valueObject->id); + + $this->simplifySearchResult($result); + $this->assertEqualsWithDelta( + include $this->getFixtureDir() . '/UserMetadata.php', + $result, + .1, // Be quite generous regarding delay -- most important for scores + 'Search results do not match.', + ); + } + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testUserMetadataGroupHorizontalQueryContent() + { + $this->testUserMetadataGroupHorizontalFilterContent('query'); + } + + /** + * Test for the findLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + */ + public function testUserMetadataGroupHorizontalFilterLocation($queryType = null) + { + if ($queryType === null) { + $queryType = 'filter'; + } + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + $permissionResolver = $repository->getPermissionResolver(); + $editorsUserGroupId = 13; + + $content = $this->createContentForTestUserMetadataGroupHorizontal(); + + $criteria = []; + $setupFactory = $this->getSetupFactory(); + + // Do not limit for LSE, as it does not not require reindexing. + // See explanation below. + if ($setupFactory instanceof LegacySolrSetupFactory) { + $criteria[] = new Criterion\ContentTypeIdentifier('folder'); + } + + $criteria[] = new Criterion\UserMetadata( + Criterion\UserMetadata::GROUP, + Criterion\Operator::EQ, + $editorsUserGroupId + ); + + $query = new LocationQuery( + [ + $queryType => new Criterion\LogicalAnd($criteria), + 'sortClauses' => [ + new SortClause\Location\Id(), + ], + 'limit' => 50, + ] + ); + + if ($setupFactory instanceof LegacySolrSetupFactory) { + $result = $searchService->findLocations($query); + + // Administrator User is owned by itself, when additional Locations are added + // it should be reindexed and its UserGroups will updated, which means it should + // also be found as a Content of Editors UserGroup. However we do not handle this + // in listeners yet, and also miss SPI methods to do it without using Search (also + // needed to decouple services), because as indexing is asynchronous Search + // should not eat its own dog food for reindexing. + $this->assertEquals(1, $result->totalCount); + + $this->assertEquals( + $content->contentInfo->mainLocationId, + $result->searchHits[0]->valueObject->id + ); + } else { + // This is how it should eventually work for all search engines, + // with required reindexing listeners properly implemented. + + $result = $searchService->findLocations($query); + + // Assert last two hits manually, as ids will change because they are created + // in test and not present in base fixture. + $foundLocation1 = array_pop($result->searchHits); + $foundLocation2 = array_pop($result->searchHits); + // Remove additional Administrators UserGroup Location + array_pop($result->searchHits); + $result->totalCount = $result->totalCount - 2; + $this->assertEquals( + $content->versionInfo->contentInfo->mainLocationId, + $foundLocation1->valueObject->id + ); + $this->assertEquals( + $permissionResolver->getCurrentUserReference()->getUserId(), + $foundLocation2->valueObject->contentId + ); + + $this->simplifySearchResult($result); + $this->assertEqualsWithDelta( + include $this->getFixtureDir() . '/UserMetadataLocation.php', + $result, + .1, // Be quite generous regarding delay -- most important for scores + 'Search results do not match.', + ); + } + } + + /** + * Test for the findLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + */ + public function testUserMetadataGroupHorizontalQueryLocation() + { + $this->testUserMetadataGroupHorizontalFilterLocation('query'); + } + + /** + * Test for FullText on the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testFullTextOnNewContent() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + $searchService = $repository->getSearchService(); + + $contentCreateStruct = $contentService->newContentCreateStruct( + $contentTypeService->loadContentTypeByIdentifier('folder'), + 'eng-GB' + ); + + $contentCreateStruct->setField('name', 'foxes'); + + $englishContent = $contentService->publishVersion( + $contentService->createContent( + $contentCreateStruct, + [$locationService->newLocationCreateStruct(2)] + )->versionInfo + ); + + $this->refreshSearch($repository); + + $query = new Query( + [ + 'query' => new Criterion\FullText('foxes'), + ] + ); + + $searchResult = $searchService->findContentInfo($query); + + $this->assertEquals(1, $searchResult->totalCount); + $this->assertEquals($englishContent->id, $searchResult->searchHits[0]->valueObject->id); + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testLanguageAnalysisSeparateContent() + { + $this->markTestSkipped('Language analysis is currently not supported'); + + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + $searchService = $repository->getSearchService(); + $languageService = $repository->getContentLanguageService(); + + $languageCreateStruct = $languageService->newLanguageCreateStruct(); + $languageCreateStruct->languageCode = 'rus-RU'; + $languageCreateStruct->name = 'Russian'; + + $languageService->createLanguage($languageCreateStruct); + + $contentCreateStruct = $contentService->newContentCreateStruct( + $contentTypeService->loadContentTypeByIdentifier('folder'), + 'eng-GB' + ); + + $contentCreateStruct->setField('name', 'foxes'); + + $englishContent = $contentService->publishVersion( + $contentService->createContent( + $contentCreateStruct, + [$locationService->newLocationCreateStruct(2)] + )->versionInfo + ); + + $contentCreateStruct = $contentService->newContentCreateStruct( + $contentTypeService->loadContentTypeByIdentifier('folder'), + 'rus-RU' + ); + + $contentCreateStruct->setField('name', 'foxes'); + + $russianContent = $contentService->publishVersion( + $contentService->createContent( + $contentCreateStruct, + [$locationService->newLocationCreateStruct(2)] + )->versionInfo + ); + + // Only Content in English should be found, because Content in Russian + // will not be correctly stemmed + $query = new Query( + [ + 'query' => new Criterion\FullText('foxing'), + ] + ); + + $searchResult = $searchService->findContent($query); + + $this->assertEquals(1, $searchResult->totalCount); + $this->assertEquals($englishContent->id, $searchResult->searchHits[0]->valueObject->id); + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testLanguageAnalysisSameContent() + { + $this->markTestSkipped('Language analysis is currently not supported'); + + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + $searchService = $repository->getSearchService(); + $languageService = $repository->getContentLanguageService(); + + $languageCreateStruct = $languageService->newLanguageCreateStruct(); + $languageCreateStruct->languageCode = 'rus-RU'; + $languageCreateStruct->name = 'Russian'; + + $languageService->createLanguage($languageCreateStruct); + + $contentCreateStruct = $contentService->newContentCreateStruct( + $contentTypeService->loadContentTypeByIdentifier('folder'), + 'eng-GB' + ); + + $contentCreateStruct->setField('name', 'foxes важнейшими', 'eng-GB'); + $contentCreateStruct->setField('name', 'foxes важнейшими', 'rus-RU'); + + $mixedContent = $contentService->publishVersion( + $contentService->createContent( + $contentCreateStruct, + [$locationService->newLocationCreateStruct(2)] + )->versionInfo + ); + + // Content will be found because translation in Russian will be correctly stemmed + $query = new Query( + [ + 'query' => new Criterion\FullText('важнее'), + ] + ); + + $searchResult = $searchService->findContent($query); + + $this->assertEquals(1, $searchResult->totalCount); + $this->assertEquals($mixedContent->id, $searchResult->searchHits[0]->valueObject->id); + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testLanguageAnalysisSameContentNotFound() + { + $this->markTestSkipped('Language analysis is currently not supported'); + + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + $searchService = $repository->getSearchService(); + $languageService = $repository->getContentLanguageService(); + + $languageCreateStruct = $languageService->newLanguageCreateStruct(); + $languageCreateStruct->languageCode = 'rus-RU'; + $languageCreateStruct->name = 'Russian'; + + $languageService->createLanguage($languageCreateStruct); + + $contentCreateStruct = $contentService->newContentCreateStruct( + $contentTypeService->loadContentTypeByIdentifier('folder'), + 'eng-GB' + ); + + $contentCreateStruct->setField('name', 'foxes важнейшими', 'eng-GB'); + $contentCreateStruct->setField('name', 'foxes важнейшими', 'rus-RU'); + + $mixedContent = $contentService->publishVersion( + $contentService->createContent( + $contentCreateStruct, + [$locationService->newLocationCreateStruct(2)] + )->versionInfo + ); + + // Content should be found because translation in Russian will be correctly stemmed + $query = new Query( + [ + 'query' => new Criterion\FullText('важнее'), + ] + ); + + // Filtering fields for only English will cause no match because the term will + // not be correctly stemmed + $searchResult = $searchService->findContent($query, ['languages' => ['eng-GB']]); + + $this->assertEquals(0, $searchResult->totalCount); + } + + /** + * Test for the findContent() method searching for content filtered by languages. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent + */ + public function testFindContentWithLanguageFilter() + { + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $query = new Query( + [ + 'filter' => new Criterion\ContentId([4]), + 'offset' => 0, + ] + ); + $searchResult = $searchService->findContent( + $query, + ['languages' => ['eng-US']], + false + ); + /* END: Use Case */ + + $this->assertInstanceOf( + SearchResult::class, + $searchResult + ); + + $this->assertEquals(1, $searchResult->totalCount); + $this->assertCount($searchResult->totalCount, $searchResult->searchHits); + foreach ($searchResult->searchHits as $searchHit) { + $this->assertInstanceOf( + SearchHit::class, + $searchHit + ); + } + } + + /** + * This test prepares data for other tests. + * + * @see testFulltextContentSearchComplex + * @see testFulltextLocationSearchComplex + * + * @return array + */ + public function testFulltextComplex() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $contentTypeService = $repository->getContentTypeService(); + $locationService = $repository->getLocationService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + $contentCreateStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + + $contentCreateStruct->setField('name', 'red'); + $contentCreateStruct->setField('short_name', 'red apple'); + $content1 = $contentService->publishVersion( + $contentService->createContent( + $contentCreateStruct, + [$locationService->newLocationCreateStruct(2)] + )->versionInfo + ); + + $contentCreateStruct->setField('name', 'apple'); + $contentCreateStruct->setField('short_name', 'two'); + $content2 = $contentService->publishVersion( + $contentService->createContent( + $contentCreateStruct, + [$locationService->newLocationCreateStruct(2)] + )->versionInfo + ); + + $contentCreateStruct->setField('name', 'red apple'); + $contentCreateStruct->setField('short_name', 'three'); + $content3 = $contentService->publishVersion( + $contentService->createContent( + $contentCreateStruct, + [$locationService->newLocationCreateStruct(2)] + )->versionInfo + ); + + $contentCreateStruct->setField('name', 'four'); + $contentCreateStruct->setField('name', 'german red apple', 'ger-DE'); + $contentCreateStruct->setField('short_name', 'four'); + $contentCreateStruct->setField('short_name', 'german red apple', 'ger-DE'); + $contentService->publishVersion( + $contentService->createContent( + $contentCreateStruct, + [$locationService->newLocationCreateStruct(2)] + )->versionInfo + ); + + $this->refreshSearch($repository); + + $criterion = new Criterion\FullText( + 'red apple', + [ + 'boost' => [ + 'short_name' => 2, + ], + 'fuzziness' => .1, + ] + ); + + return [$criterion, $content1, $content2, $content3]; + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * @depends testFulltextComplex + * + * @param array $data + */ + public function testFulltextContentSearchComplex(array $data) + { + // Do not initialize from scratch + $repository = $this->getRepository(false); + $searchService = $repository->getSearchService(); + list($criterion, $content1, $content2, $content3) = $data; + + $searchResult = $searchService->findContent( + new Query(['query' => $criterion]), + ['languages' => ['eng-GB']] + ); + $searchHits = $searchResult->searchHits; + + $this->assertEquals(3, $searchResult->totalCount); + + // Legacy search engine does have scoring, sorting the results by ID in that case + $setupFactory = $this->getSetupFactory(); + if ($setupFactory instanceof Legacy) { + $this->sortSearchHitsById($searchHits); + + $this->assertEquals($content1->id, $searchHits[0]->valueObject->id); + $this->assertEquals($content2->id, $searchHits[1]->valueObject->id); + $this->assertEquals($content3->id, $searchHits[2]->valueObject->id); + + return; + } + + // Assert scores are descending + $this->assertGreaterThan($searchHits[1]->score, $searchHits[0]->score); + $this->assertGreaterThan($searchHits[2]->score, $searchHits[1]->score); + + // Assert order + $this->assertEquals($content1->id, $searchHits[0]->valueObject->id); + $this->assertEquals($content3->id, $searchHits[1]->valueObject->id); + $this->assertEquals($content2->id, $searchHits[2]->valueObject->id); + } + + /** + * Test for the findContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * @depends testFulltextComplex + * + * @param array $data + */ + public function testFulltextContentTranslationSearch(array $data) + { + $criterion = $data[0]; + $query = new Query(['query' => $criterion]); + + $this->assertFulltextSearchForTranslations(self::FIND_CONTENT_METHOD, $query); + } + + /** + * Test for the findLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + * @depends testFulltextComplex + * + * @param array $data + */ + public function testFulltextLocationSearchComplex(array $data) + { + $setupFactory = $this->getSetupFactory(); + if ($setupFactory instanceof LegacySolrSetupFactory && getenv('SOLR_VERSION') === '4.10.4') { + $this->markTestSkipped('Skipping location search score test on Solr 4.10, you need Solr 6 for this!'); + } + + // Do not initialize from scratch + $repository = $this->getRepository(false); + list($criterion, $content1, $content2, $content3) = $data; + $searchService = $repository->getSearchService(); + + $searchResult = $searchService->findLocations( + new LocationQuery(['query' => $criterion]), + ['languages' => ['eng-GB']] + ); + $searchHits = $searchResult->searchHits; + + $this->assertEquals(3, $searchResult->totalCount); + + // Legacy search engine does have scoring, sorting the results by ID in that case + $setupFactory = $this->getSetupFactory(); + if ($setupFactory instanceof Legacy) { + $this->sortSearchHitsById($searchHits); + + $this->assertEquals($content1->id, $searchHits[0]->valueObject->contentId); + $this->assertEquals($content2->id, $searchHits[1]->valueObject->contentId); + $this->assertEquals($content3->id, $searchHits[2]->valueObject->contentId); + + return; + } + + // Assert scores are descending + $this->assertGreaterThan($searchHits[1]->score, $searchHits[0]->score); + $this->assertGreaterThan($searchHits[2]->score, $searchHits[1]->score); + + // Assert order + $this->assertEquals($content1->id, $searchHits[0]->valueObject->contentId); + $this->assertEquals($content3->id, $searchHits[1]->valueObject->contentId); + $this->assertEquals($content2->id, $searchHits[2]->valueObject->contentId); + } + + /** + * Test for the findLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + * @depends testFulltextComplex + * + * @param array $data + */ + public function testFulltextLocationTranslationSearch(array $data): void + { + $criterion = $data[0]; + $query = new LocationQuery(['query' => $criterion]); + + $this->assertFulltextSearchForTranslations(self::FIND_LOCATION_METHOD, $query); + } + + public function testSpellcheckWithIncorrectQuery(): void + { + $searchService = $this->getRepository()->getSearchService(); + + if (!$searchService->supports(SearchService::CAPABILITY_SPELLCHECK)) { + self::markTestSkipped("Search engine doesn't support spellchecking"); + } + + $query = new Query(); + // Search phrase with typo: "Contatc Us" instead of "Contact Us": + $query->spellcheck = new Query\Spellcheck('Contatc Us'); + + $results = $searchService->findContent($query); + + self::assertNotNull($results->spellcheck); + self::assertTrue($results->spellcheck->isIncorrect()); + self::assertEqualsIgnoringCase('Contact Us', $results->spellcheck->getQuery()); + } + + public function testSpellcheckWithCorrectQuery(): void + { + $searchService = $this->getRepository()->getSearchService(); + + if (!$searchService->supports(SearchService::CAPABILITY_SPELLCHECK)) { + self::markTestSkipped("Search engine doesn't support spellchecking"); + } + + $query = new Query(); + // Search phrase without typo + $query->spellcheck = new Query\Spellcheck('Ibexa Platform'); + + $results = $searchService->findContent($query); + + self::assertNotNull($results->spellcheck); + self::assertFalse($results->spellcheck->isIncorrect()); + self::assertEqualsIgnoringCase('Ibexa Platform', $results->spellcheck->getQuery()); + } + + /** + * Assert that query result matches the given fixture. + * + * @throws \ReflectionException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + protected function assertQueryFixture( + Query $query, + string $fixtureFilePath, + ?callable $closure = null, + bool $ignoreScore = true, + bool $info = false, + bool $id = true + ): void { + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + try { + if ($query instanceof LocationQuery) { + $result = $searchService->findLocations($query); + } elseif ($query instanceof Query) { + if ($info) { + $result = $searchService->findContentInfo($query); + } else { + $result = $searchService->findContent($query); + } + } else { + self::fail('Expected instance of LocationQuery or Query, got: ' . gettype($query)); + } + $this->simplifySearchResult($result); + } catch (NotImplementedException $e) { + self::markTestSkipped( + 'This feature is not supported by the current search backend: ' . $e->getMessage() + ); + } + + if (!is_file($fixtureFilePath)) { + if (isset($_ENV['ibexa_tests_record'])) { + file_put_contents( + $record = $fixtureFilePath . '.recording', + "<?php\n\nreturn " . var_export($result, true) . ";\n\n" + ); + self::markTestIncomplete("No fixture available. Result recorded at $record. Result: \n" . $this->printResult($result)); + } else { + self::markTestIncomplete("No fixture available. Set \$_ENV['ibexa_tests_record'] to generate:\n " . $fixtureFilePath); + } + } + + $fixture = require $fixtureFilePath; + + if ($closure !== null) { + $closure($fixture); + $closure($result); + } + + if ($ignoreScore) { + foreach ([$fixture, $result] as $set) { + $property = new \ReflectionProperty(get_class($set), 'maxScore'); + $property->setAccessible(true); + $property->setValue($set, 0.0); + + foreach ($set->searchHits as $hit) { + $property = new \ReflectionProperty(get_class($hit), 'score'); + $property->setAccessible(true); + $property->setValue($hit, 0.0); + } + } + } + + foreach ([$fixture, $result] as $set) { + foreach ($set->searchHits as $hit) { + $property = new \ReflectionProperty(get_class($hit), 'index'); + $property->setAccessible(true); + $property->setValue($hit, null); + + $property = new \ReflectionProperty(get_class($hit), 'matchedTranslation'); + $property->setAccessible(true); + $property->setValue($hit, null); + + if (!$id) { + $hit->valueObject['id'] = null; + } + } + } + + self::assertEqualsWithDelta( + $fixture, + $result, + .99, // Be quite generous regarding delay -- most important for scores + 'Search results do not match the fixture: ' . $fixtureFilePath + ); + } + + /** + * Show a simplified view of the search result for manual introspection. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult $result + * + * @return string + */ + protected function printResult(SearchResult $result) + { + $printed = ''; + foreach ($result->searchHits as $hit) { + $printed .= sprintf(" - %s (%s)\n", $hit->valueObject['title'], $hit->valueObject['id']); + } + + return $printed; + } + + /** + * Simplify search result. + * + * This leads to saner comparisons of results, since we do not get the full + * content objects every time. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult $result + */ + protected function simplifySearchResult(SearchResult $result) + { + $result->time = 1; + + foreach ($result->searchHits as $hit) { + switch (true) { + case $hit->valueObject instanceof Content: + case $hit->valueObject instanceof Location: + $hit->valueObject = [ + 'id' => $hit->valueObject->contentInfo->id, + 'title' => $hit->valueObject->contentInfo->name, + ]; + break; + + case $hit->valueObject instanceof ContentInfo: + $hit->valueObject = [ + 'id' => $hit->valueObject->id, + 'title' => $hit->valueObject->name, + ]; + break; + + default: + throw new \RuntimeException('Unknown search result hit type: ' . get_class($hit->valueObject)); + } + } + } + + /** + * Get fixture directory. + * + * @return string + */ + protected function getFixtureDir() + { + return __DIR__ . '/_fixtures/' . getenv('fixtureDir') . '/'; + } + + /** + * For findContentInfo tests, to reuse fixtures for findContent tests. + * + * @param callable|null $closure + * + * @return callable + */ + private function getContentInfoFixtureClosure($closure = null) + { + /** @var $data \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult */ + return static function (&$data) use ($closure) { + foreach ($data->searchHits as $searchHit) { + if ($searchHit->valueObject instanceof Content) { + $searchHit->valueObject = $searchHit->valueObject->getVersionInfo()->getContentInfo(); + } + } + + if (isset($closure)) { + $closure($data); + } + }; + } + + /** + * Test searching using Field Criterion where the given Field Identifier exists in + * both searchable and non-searchable Fields. + * Number of returned results depends on used storage. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + */ + public function testFieldCriterionForContentsWithIdenticalFieldIdentifiers() + { + $this->createContentWithFieldType( + 'url', + 'title', + 'foo' + ); + $this->createContentWithFieldType( + 'string', + 'title', + 'foo' + ); + $query = new Query( + [ + 'query' => new Criterion\Field( + 'title', + Criterion\Operator::EQ, + 'foo' + ), + ] + ); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + $result = $searchService->findContent($query); + + $this->assertTrue(($result->totalCount === 1 || $result->totalCount === 2)); + } + + private function createContentWithFieldType( + string $fieldType, + string $fieldName, + string $fieldValue + ) { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + + $createStruct = $contentTypeService->newContentTypeCreateStruct($fieldType . uniqid()); + $createStruct->mainLanguageCode = 'eng-GB'; + $createStruct->remoteId = $fieldType . '-123'; + $createStruct->names = ['eng-GB' => $fieldType]; + $createStruct->creatorId = 14; + $createStruct->creationDate = new \DateTime(); + + $fieldCreate = $contentTypeService->newFieldDefinitionCreateStruct($fieldName, 'ez' . $fieldType); + $fieldCreate->names = ['eng-GB' => $fieldName]; + $fieldCreate->fieldGroup = 'main'; + $fieldCreate->position = 1; + + $createStruct->addFieldDefinition($fieldCreate); + + $contentGroup = $contentTypeService->loadContentTypeGroupByIdentifier('Content'); + $contentTypeDraft = $contentTypeService->createContentType($createStruct, [$contentGroup]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + $contentType = $contentTypeService->loadContentType($contentTypeDraft->id); + + $createStruct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $createStruct->remoteId = $fieldType . '-456'; + $createStruct->alwaysAvailable = false; + $createStruct->setField( + $fieldName, + $fieldValue + ); + + $draft = $contentService->createContent($createStruct); + $content = $contentService->publishVersion($draft->getVersionInfo()); + + $this->refreshSearch($repository); + + return $content; + } + + /** + * Test for the findContent() method with random sort clause. + * + * There is a slight chance when this test could fail, if by some reason, + * we got to same _random_ results, or mt_rand() provides same seed for seed-supported DB implementation. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent() + * + * @dataProvider getSeedsForRandomSortClause + */ + public function testRandomSortContent(?int $firstSeed, ?int $secondSeed) + { + if ($firstSeed || $secondSeed) { + $this->skipIfSeedNotImplemented(); + } + + $firstQuery = new Query([ + 'sortClauses' => [ + new SortClause\Random($firstSeed), + ], + ]); + + $secondQuery = new Query([ + 'sortClauses' => [ + new SortClause\Random($secondSeed), + ], + ]); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $method = 'assertNotEquals'; + + if ($firstSeed !== null && $secondSeed !== null && ($firstSeed === $secondSeed)) { + $method = 'assertEquals'; + } + + try { + $this->$method( + $searchService->findContent($firstQuery)->searchHits, + $searchService->findContent($secondQuery)->searchHits + ); + } catch (NotImplementedException $e) { + $this->markTestSkipped( + 'This feature is not supported by the current search backend: ' . $e->getMessage() + ); + } + } + + /** + * Test for the findLocations() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations() + * + * @dataProvider getSeedsForRandomSortClause + */ + public function testRandomSortLocation(?int $firstSeed, ?int $secondSeed) + { + if ($firstSeed || $secondSeed) { + $this->skipIfSeedNotImplemented(); + } + + $firstQuery = new LocationQuery([ + 'sortClauses' => [ + new SortClause\Random($firstSeed), + ], + ]); + + $secondQuery = new LocationQuery([ + 'sortClauses' => [ + new SortClause\Random($secondSeed), + ], + ]); + + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $method = 'assertNotEquals'; + + if ($firstSeed !== null && $secondSeed !== null && ($firstSeed === $secondSeed)) { + $method = 'assertEquals'; + } + + try { + $this->$method( + $searchService->findLocations($firstQuery)->searchHits, + $searchService->findLocations($secondQuery)->searchHits + ); + } catch (NotImplementedException $e) { + $this->markTestSkipped( + 'This feature is not supported by the current search backend: ' . $e->getMessage() + ); + } + } + + public function getSeedsForRandomSortClause() + { + $randomSeed = mt_rand(); + + return [ + [ + null, + null, + ], + [ + 123456, + 123456, + ], + [ + $randomSeed, + 2 * $randomSeed, + ], + ]; + } + + private function skipIfSeedNotImplemented() + { + /** @var \Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy $setupFactory */ + $setupFactory = $this->getSetupFactory(); + + $db = $setupFactory->getDB(); + + if (in_array($db, ['sqlite', 'pgsql'])) { + $this->markTestSkipped( + 'Seed function is not implemented in ' . $db . '.' + ); + } + } + + /** + * @param string $findMethod + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query + * @param array $languages + * @param bool $useAlwaysAvailable + * + * @throws \InvalidArgumentException + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult + */ + private function find( + string $findMethod, + Query $query, + array $languages, + bool $useAlwaysAvailable + ): SearchResult { + if (false === in_array($findMethod, self::AVAILABLE_FIND_METHODS, true)) { + throw new \InvalidArgumentException( + 'Allowed find methods are: ' + . implode(',', self::AVAILABLE_FIND_METHODS) + ); + } + + $repository = $this->getRepository(false); + $searchService = $repository->getSearchService(); + + return $searchService->{$findMethod}( + $query, + [ + 'languages' => $languages, + 'useAlwaysAvailable' => $useAlwaysAvailable, + ] + ); + } + + /** + * @param string $findMethod + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query + */ + private function assertFulltextSearchForTranslations(string $findMethod, Query $query): void + { + /* + * Search in German translations without always available + */ + $searchResult = $this->find($findMethod, $query, ['ger-DE'], false); + $this->assertEquals(1, $searchResult->totalCount); + $this->assertSearchResultMatchTranslations($searchResult, ['ger-DE']); + + /* + * Search in German translations with always available + */ + $searchResult = $this->find($findMethod, $query, ['ger-DE'], true); + $this->assertEquals(4, $searchResult->totalCount); + $this->assertSearchResultMatchTranslations($searchResult, ['eng-GB', 'eng-GB', 'eng-GB', 'ger-DE']); + + /* + * Search in multiple (ger-DE, eng-GB) translations without always available + */ + $searchResult = $this->find($findMethod, $query, ['ger-DE', 'eng-GB'], false); + $this->assertEquals(4, $searchResult->totalCount); + $this->assertSearchResultMatchTranslations($searchResult, ['eng-GB', 'eng-GB', 'eng-GB', 'ger-DE']); + + /* + * Search in multiple (eng-US, ger-DE) translations without always available + */ + $searchResult = $this->find($findMethod, $query, ['eng-US', 'ger-DE'], false); + $this->assertEquals(1, $searchResult->totalCount); + $this->assertSearchResultMatchTranslations($searchResult, ['ger-DE']); + + /* + * Search in eng-US translations without always available + */ + $searchResult = $this->find($findMethod, $query, ['eng-US'], false); + $this->assertEquals(0, $searchResult->totalCount); + + /* + * Search in eng-US translations with always available + */ + $searchResult = $this->find($findMethod, $query, ['eng-US'], true); + $this->assertEquals(3, $searchResult->totalCount); + $this->assertSearchResultMatchTranslations($searchResult, ['eng-GB', 'eng-GB', 'eng-GB']); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult $searchResult + * @param string[] $translationsToMatch + * + * @throws \InvalidArgumentException + */ + private function assertSearchResultMatchTranslations( + SearchResult $searchResult, + array $translationsToMatch + ): void { + $translationsToMatchCount = count($translationsToMatch); + + if ($searchResult->totalCount < $translationsToMatchCount + || $searchResult->totalCount > $translationsToMatchCount + ) { + throw new \InvalidArgumentException( + 'Argument `translationsToMatch` must be equal to the search result total count!' + ); + } + + $searchHits = $searchResult->searchHits; + $this->sortSearchHitsById($searchHits); + + for ($i = 0; $i < $searchResult->totalCount; ++$i) { + $this->assertEquals( + $translationsToMatch[$i], + $searchHits[$i]->matchedTranslation + ); + } + } + + private function sortSearchHitsById(array &$searchHits): void + { + usort( + $searchHits, + static function (SearchHit $a, SearchHit $b): int { + return $a->valueObject->id <=> $b->valueObject->id; + } + ); + } + + /** + * @dataProvider providerForTestSortingByNumericFieldsWithValuesOfDifferentLength + * + * @param int[] $expectedOrderedIds + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testSortingByNumericFieldsWithValuesOfDifferentLength( + LocationQuery $query, + array $expectedOrderedIds + ): void { + $repository = $this->getRepository(); + $searchService = $repository->getSearchService(); + + $result = $searchService->findLocations($query); + + self::assertEquals(count($expectedOrderedIds), $result->totalCount); + $actualIds = array_map( + static function (SearchHit $searchHit) { + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $location */ + $location = $searchHit->valueObject; + + return $location->id; + }, + $result->searchHits + ); + self::assertEquals($expectedOrderedIds, $actualIds); + } + + public function providerForTestSortingByNumericFieldsWithValuesOfDifferentLength(): iterable + { + yield 'Location ID ASC' => [ + new LocationQuery( + [ + 'filter' => new Criterion\LocationId([43, 5]), + 'sortClauses' => [ + new SortClause\Location\Id(LocationQuery::SORT_ASC), + ], + ] + ), + [5, 43], + ]; + + yield 'Location ID DESC' => [ + new LocationQuery( + [ + 'filter' => new Criterion\LocationId([5, 43]), + 'sortClauses' => [ + new SortClause\Location\Id(LocationQuery::SORT_DESC), + ], + ] + ), + [43, 5], + ]; + + yield 'Content ID ASC' => [ + new LocationQuery( + [ + 'filter' => new Criterion\ContentId([14, 4]), + 'sortClauses' => [ + new SortClause\ContentId(LocationQuery::SORT_ASC), + ], + ] + ), + // those are still Location IDs as it's LocationQuery + [5, 15], + ]; + + yield 'Content ID DESC' => [ + new LocationQuery( + [ + 'filter' => new Criterion\ContentId([4, 14]), + 'sortClauses' => [ + new SortClause\ContentId(LocationQuery::SORT_DESC), + ], + ] + ), + // those are still Location IDs as it's LocationQuery + [15, 5], + ]; + } + + private function isRunningOnLegacySetup(): bool + { + return get_class($this->getSetupFactory()) === Legacy::class; + } +} + +class_alias(SearchServiceTest::class, 'eZ\Publish\API\Repository\Tests\SearchServiceTest'); diff --git a/eZ/Publish/API/Repository/Tests/SearchServiceTranslationLanguageFallbackTest.php b/tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php similarity index 98% rename from eZ/Publish/API/Repository/Tests/SearchServiceTranslationLanguageFallbackTest.php rename to tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php index 13b513ac08..f9a919e276 100644 --- a/eZ/Publish/API/Repository/Tests/SearchServiceTranslationLanguageFallbackTest.php +++ b/tests/integration/Core/Repository/SearchServiceTranslationLanguageFallbackTest.php @@ -4,21 +4,21 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; use DateTime; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use EzSystems\EzPlatformSolrSearchEngine\Tests\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Tests\Solr\SetupFactory\LegacySetupFactory as LegacySolrSetupFactory; use RuntimeException; /** * Test case for field filtering operations in the SearchService. * - * @see eZ\Publish\API\Repository\SearchService + * @covers \Ibexa\Contracts\Core\Repository\SearchService * @group integration * @group search * @group language_fallback @@ -31,7 +31,7 @@ class SearchServiceTranslationLanguageFallbackTest extends BaseTest public const SETUP_CLOUD = 'cloud'; /** - * @return \eZ\Publish\API\Repository\Values\ContentType\ContentType + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType */ protected function createTestContentType() { @@ -71,7 +71,7 @@ protected function createTestContentType() } /** - * @param \eZ\Publish\API\Repository\Values\ContentType\ContentType $contentType + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType * @param array $searchValuesMap * @param string $mainLanguageCode * @param bool $alwaysAvailable @@ -1718,7 +1718,7 @@ public function testFindContent( array $contentDataList, array $context ) { - /** @var \eZ\Publish\Api\Repository\Repository $repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository $repository */ list($repository, $data) = $context; $queryProperties = [ @@ -1739,7 +1739,7 @@ public function testFindContent( list($contentNo, $translationLanguageCode, $indexMap) = $contentData; list($index, $contentNo) = $this->getIndexesToMatchData($contentData, $index, $contentNo); - /** @var \eZ\Publish\Api\Repository\Values\Content\Content $content */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $content */ $content = $searchResult->searchHits[$index]->valueObject; $this->assertEquals( @@ -1767,7 +1767,7 @@ public function testFindLocationsSingle( array $contentDataList, array $context ) { - /** @var \eZ\Publish\Api\Repository\Repository $repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository $repository */ list($repository, $data) = $context; $queryProperties = [ @@ -1793,7 +1793,7 @@ public function testFindLocationsSingle( list($contentNo, $translationLanguageCode, $indexMap) = $contentData; list($index, $contentNo) = $this->getIndexesToMatchData($contentData, $index, $contentNo); - /** @var \eZ\Publish\Api\Repository\Values\Content\Location $location */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $location */ $location = $searchResult->searchHits[$index]->valueObject; $this->assertEquals( @@ -1821,7 +1821,7 @@ public function testFindLocationsMultiple( array $contentDataList, array $context ) { - /** @var \eZ\Publish\Api\Repository\Repository $repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository $repository */ list($repository, $data) = $context; $queryProperties = [ @@ -1843,7 +1843,7 @@ public function testFindLocationsMultiple( list($contentNo, $translationLanguageCode, $indexMap) = $contentData; list($index, $contentNo) = $this->getIndexesToMatchData($contentData, $index, $contentNo); - /** @var \eZ\Publish\Api\Repository\Values\Content\Location $location */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $location */ $location = $searchResult->searchHits[$index]->valueObject; $this->assertEquals( @@ -1862,7 +1862,7 @@ public function testFindLocationsMultiple( list($index, $contentNo) = $this->getIndexesToMatchData($contentData, $index, $contentNo); $realIndex = $index + count($contentDataList); - /** @var \eZ\Publish\Api\Repository\Values\Content\Location $location */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $location */ $location = $searchResult->searchHits[$realIndex]->valueObject; $this->assertEquals( @@ -1913,3 +1913,5 @@ private function getIndexesToMatchData( return $indexesToMatchData; } } + +class_alias(SearchServiceTranslationLanguageFallbackTest::class, 'eZ\Publish\API\Repository\Tests\SearchServiceTranslationLanguageFallbackTest'); diff --git a/eZ/Publish/API/Repository/Tests/SectionServiceAuthorizationTest.php b/tests/integration/Core/Repository/SectionServiceAuthorizationTest.php similarity index 88% rename from eZ/Publish/API/Repository/Tests/SectionServiceAuthorizationTest.php rename to tests/integration/Core/Repository/SectionServiceAuthorizationTest.php index 63a028891e..29baeae5fb 100644 --- a/eZ/Publish/API/Repository/Tests/SectionServiceAuthorizationTest.php +++ b/tests/integration/Core/Repository/SectionServiceAuthorizationTest.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; /** * Test case for operations in the SectionService using in memory storage. * - * @see eZ\Publish\API\Repository\SectionService + * @covers \Ibexa\Contracts\Core\Repository\SectionService * @group integration * @group authorization */ @@ -21,7 +21,7 @@ class SectionServiceAuthorizationTest extends BaseTest /** * Test for the createSection() method. * - * @see \eZ\Publish\API\Repository\SectionService::createSection() + * @covers \Ibexa\Contracts\Core\Repository\SectionService::createSection() */ public function testCreateSectionThrowsUnauthorizedException() { @@ -29,7 +29,7 @@ public function testCreateSectionThrowsUnauthorizedException() $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $sectionService = $repository->getSectionService(); @@ -51,7 +51,7 @@ public function testCreateSectionThrowsUnauthorizedException() /** * Test for the loadSection() method. * - * @see \eZ\Publish\API\Repository\SectionService::loadSection() + * @covers \Ibexa\Contracts\Core\Repository\SectionService::loadSection() */ public function testLoadSectionThrowsUnauthorizedException() { @@ -59,7 +59,7 @@ public function testLoadSectionThrowsUnauthorizedException() $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $sectionService = $repository->getSectionService(); @@ -83,7 +83,7 @@ public function testLoadSectionThrowsUnauthorizedException() /** * Test for the updateSection() method. * - * @see \eZ\Publish\API\Repository\SectionService::updateSection() + * @covers \Ibexa\Contracts\Core\Repository\SectionService::updateSection() */ public function testUpdateSectionThrowsUnauthorizedException() { @@ -92,9 +92,9 @@ public function testUpdateSectionThrowsUnauthorizedException() $standardSectionId = $this->generateId('section', 1); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. - // $standardSectionId is the ID of the "Standard" section in a eZ + // $standardSectionId is the ID of the "Standard" section in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); @@ -119,7 +119,7 @@ public function testUpdateSectionThrowsUnauthorizedException() /** * Test for the loadSections() method. * - * @see \eZ\Publish\API\Repository\SectionService::loadSections() + * @covers \Ibexa\Contracts\Core\Repository\SectionService::loadSections() */ public function testLoadSectionsLoadsEmptyListForAnonymousUser() { @@ -127,7 +127,7 @@ public function testLoadSectionsLoadsEmptyListForAnonymousUser() $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $sectionService = $repository->getSectionService(); @@ -156,7 +156,7 @@ public function testLoadSectionsLoadsEmptyListForAnonymousUser() /** * Test for the loadSections() method. * - * @see \eZ\Publish\API\Repository\SectionService::loadSections() + * @covers \Ibexa\Contracts\Core\Repository\SectionService::loadSections() */ public function testLoadSectionFiltersSections() { @@ -201,7 +201,7 @@ public function testLoadSectionFiltersSections() /** * Test for the loadSectionByIdentifier() method. * - * @see \eZ\Publish\API\Repository\SectionService::loadSectionByIdentifier() + * @covers \Ibexa\Contracts\Core\Repository\SectionService::loadSectionByIdentifier() */ public function testLoadSectionByIdentifierThrowsUnauthorizedException() { @@ -209,7 +209,7 @@ public function testLoadSectionByIdentifierThrowsUnauthorizedException() $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $sectionService = $repository->getSectionService(); @@ -233,23 +233,23 @@ public function testLoadSectionByIdentifierThrowsUnauthorizedException() /** * Test for the assignSection() method. * - * @see \eZ\Publish\API\Repository\SectionService::assignSection() + * @covers \Ibexa\Contracts\Core\Repository\SectionService::assignSection() */ public function testAssignSectionThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $standardSectionId = $this->generateId('section', 1); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. - // $standardSectionId is the ID of the "Standard" section in a eZ + // $standardSectionId is the ID of the "Standard" section in a Ibexa // Publish demo installation. - // RemoteId of the "Media" page of an eZ Publish demo installation + // RemoteId of the "Media" page of an Ibexa demo installation $mediaRemoteId = 'a6e35cbcb7cd6ae4b691f3eee30cd262'; $userService = $repository->getUserService(); @@ -275,17 +275,17 @@ public function testAssignSectionThrowsUnauthorizedException() /** * Test for the deleteSection() method. * - * @see \eZ\Publish\API\Repository\SectionService::deleteSection() + * @covers \Ibexa\Contracts\Core\Repository\SectionService::deleteSection() */ public function testDeleteSectionThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $sectionService = $repository->getSectionService(); @@ -304,3 +304,5 @@ public function testDeleteSectionThrowsUnauthorizedException() /* END: Use Case */ } } + +class_alias(SectionServiceAuthorizationTest::class, 'eZ\Publish\API\Repository\Tests\SectionServiceAuthorizationTest'); diff --git a/tests/integration/Core/Repository/SectionServiceTest.php b/tests/integration/Core/Repository/SectionServiceTest.php new file mode 100644 index 0000000000..8ff940b9ca --- /dev/null +++ b/tests/integration/Core/Repository/SectionServiceTest.php @@ -0,0 +1,1070 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use Exception; +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Section; +use Ibexa\Contracts\Core\Repository\Values\Content\SectionCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\SectionUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation; + +/** + * Test case for operations in the SectionService using in memory storage. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService + * @group integration + * @group section + */ +class SectionServiceTest extends BaseTest +{ + private const SECTION_UNIQUE_KEY = 'uniqueKey'; + + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ + protected $permissionResolver; + + /** + * Tests that the required <b>ContentService::loadContentInfoByRemoteId()</b> + * at least returns an object, because this method is utilized in several + * tests,. + */ + protected function setUp(): void + { + parent::setUp(); + + try { + // RemoteId of the "Media" page of an Ibexa demo installation + $mediaRemoteId = 'a6e35cbcb7cd6ae4b691f3eee30cd262'; + + // Load the ContentService + $contentService = $this->getRepository()->getContentService(); + + // Load a content info instance + $contentInfo = $contentService->loadContentInfoByRemoteId( + $mediaRemoteId + ); + + if (false === is_object($contentInfo)) { + $this->markTestSkipped( + 'This test cannot be executed, because the utilized ' . + 'ContentService::loadContentInfoByRemoteId() does not ' . + 'return an object.' + ); + } + } catch (Exception $e) { + $this->markTestSkipped( + 'This test cannot be executed, because the utilized ' . + 'ContentService::loadContentInfoByRemoteId() failed with ' . + PHP_EOL . PHP_EOL . + $e + ); + } + + $repository = $this->getRepository(false); + $this->permissionResolver = $repository->getPermissionResolver(); + } + + /** + * Test for the newSectionCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::newSectionCreateStruct() + */ + public function testNewSectionCreateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + $sectionCreate = $sectionService->newSectionCreateStruct(); + /* END: Use Case */ + + $this->assertInstanceOf(SectionCreateStruct::class, $sectionCreate); + } + + /** + * Test for the createSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::createSection() + * @depends testNewSectionCreateStruct + */ + public function testCreateSection() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + $sectionCreate = $sectionService->newSectionCreateStruct(); + $sectionCreate->name = 'Test Section'; + $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; + + $section = $sectionService->createSection($sectionCreate); + /* END: Use Case */ + + $this->assertInstanceOf(Section::class, $section); + } + + /** + * Test for the createSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::createSection() + * @depends testNewSectionCreateStruct + */ + public function testCreateSectionForUserWithSectionLimitation() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + $sectionCreate = $sectionService->newSectionCreateStruct(); + $sectionCreate->name = 'Test Section'; + $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; + + $this->createRoleWithPolicies('sectionCreator', [ + ['module' => 'section', 'function' => 'edit'], + ]); + + $user = $this->createCustomUserWithLogin( + 'user', + 'user@example.com', + 'sectionCreators', + 'sectionCreator', + new SectionLimitation(['limitationValues' => [1]]) + ); + + $repository->getPermissionResolver()->setCurrentUserReference($user); + + $section = $sectionService->createSection($sectionCreate); + /* END: Use Case */ + + $this->assertInstanceOf(Section::class, $section); + $this->assertSame(self::SECTION_UNIQUE_KEY, $section->identifier); + } + + /** + * Test for the createSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::createSection() + * @depends testCreateSection + */ + public function testCreateSectionThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + $sectionCreateOne = $sectionService->newSectionCreateStruct(); + $sectionCreateOne->name = 'Test section one'; + $sectionCreateOne->identifier = self::SECTION_UNIQUE_KEY; + + $sectionService->createSection($sectionCreateOne); + + $sectionCreateTwo = $sectionService->newSectionCreateStruct(); + $sectionCreateTwo->name = 'Test section two'; + $sectionCreateTwo->identifier = self::SECTION_UNIQUE_KEY; + + // This will fail, because identifier uniqueKey already exists. + $sectionService->createSection($sectionCreateTwo); + /* END: Use Case */ + } + + /** + * Test for the loadSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::loadSection() + * @depends testCreateSection + */ + public function testLoadSection() + { + $repository = $this->getRepository(); + + $sectionId = $this->generateId('section', 2); + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + // Loads user section + // $sectionId contains the corresponding ID + $section = $sectionService->loadSection($sectionId); + /* END: Use Case */ + + $this->assertEquals('users', $section->identifier); + } + + /** + * Test for the loadSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::loadSection() + */ + public function testLoadSectionThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $nonExistentSectionId = $this->generateId('section', self::DB_INT_MAX); + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + // This call should fail with a NotFoundException + // $nonExistentSectionId contains a section ID that is not known + $sectionService->loadSection($nonExistentSectionId); + /* END: Use Case */ + } + + /** + * Test for the newSectionUpdateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::newSectionUpdateStruct() + */ + public function testNewSectionUpdateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + $sectionUpdate = $sectionService->newSectionUpdateStruct(); + /* END: Use Case */ + + $this->assertInstanceOf(SectionUpdateStruct::class, $sectionUpdate); + } + + /** + * Test for the updateSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::updateSection() + * @depends testCreateSection + * @depends testLoadSection + * @depends testNewSectionUpdateStruct + */ + public function testUpdateSection() + { + $repository = $this->getRepository(); + + $standardSectionId = $this->generateId('section', 1); + /* BEGIN: Use Case */ + // $standardSectionId contains the ID of the "Standard" section in a Ibexa + // Publish demo installation. + + $sectionService = $repository->getSectionService(); + + $section = $sectionService->loadSection($standardSectionId); + + $sectionUpdate = $sectionService->newSectionUpdateStruct(); + $sectionUpdate->name = 'New section name'; + $sectionUpdate->identifier = 'newUniqueKey'; + + $updatedSection = $sectionService->updateSection($section, $sectionUpdate); + /* END: Use Case */ + + // Verify that service returns an instance of Section + $this->assertInstanceOf(Section::class, $updatedSection); + + // Verify that the service also persists the changes + $updatedSection = $sectionService->loadSection($standardSectionId); + + $this->assertEquals('New section name', $updatedSection->name); + } + + /** + * Test for the updateSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::updateSection() + * @depends testCreateSection + * @depends testLoadSection + * @depends testNewSectionUpdateStruct + */ + public function testUpdateSectionForUserWithSectionLimitation() + { + $repository = $this->getRepository(); + $administratorUserId = $this->generateId('user', 14); + /* BEGIN: Use Case */ + // $standardSectionId contains the ID of the "Standard" section in a Ibexa + // Publish demo installation. + + $sectionService = $repository->getSectionService(); + $userService = $repository->getUserService(); + + $sectionCreate = $sectionService->newSectionCreateStruct(); + $sectionCreate->name = 'Test Section'; + $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; + $section = $sectionService->createSection($sectionCreate); + + $sectionUpdate = $sectionService->newSectionUpdateStruct(); + $sectionUpdate->name = 'New section name'; + $sectionUpdate->identifier = 'newUniqueKey'; + + $this->createRoleWithPolicies('sectionCreator', [ + ['module' => 'section', 'function' => 'edit'], + ]); + $user = $this->createCustomUserWithLogin( + 'user', + 'user@example.com', + 'sectionCreators', + 'sectionCreator', + new SectionLimitation(['limitationValues' => [$section->id]]) + ); + $this->permissionResolver->setCurrentUserReference($user); + + $updatedSection = $sectionService->updateSection($section, $sectionUpdate); + /* END: Use Case */ + + // Verify that service returns an instance of Section + $this->assertInstanceOf(Section::class, $updatedSection); + + // Load section as an administrator + $administratorUser = $userService->loadUser($administratorUserId); + $this->permissionResolver->setCurrentUserReference($administratorUser); + + // Verify that the service also persists the changes + $updatedSection = $sectionService->loadSection($section->id); + + $this->assertEquals('New section name', $updatedSection->name); + $this->assertEquals('newUniqueKey', $updatedSection->identifier); + } + + /** + * Test for the updateSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::updateSection() + * @depends testUpdateSection + */ + public function testUpdateSectionKeepsSectionIdentifierOnNameUpdate() + { + $repository = $this->getRepository(); + + $standardSectionId = $this->generateId('section', 1); + /* BEGIN: Use Case */ + // $standardSectionId contains the ID of the "Standard" section in a Ibexa + // Publish demo installation. + + $sectionService = $repository->getSectionService(); + + $section = $sectionService->loadSection($standardSectionId); + $sectionUpdate = $sectionService->newSectionUpdateStruct(); + $sectionUpdate->name = 'New section name'; + + $updatedSection = $sectionService->updateSection($section, $sectionUpdate); + /* END: Use Case */ + + $this->assertEquals('standard', $updatedSection->identifier); + } + + /** + * Test for the updateSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::updateSection() + * @depends testUpdateSection + */ + public function testUpdateSectionWithSectionIdentifierOnNameUpdate() + { + $repository = $this->getRepository(); + + $standardSectionId = $this->generateId('section', 1); + /* BEGIN: Use Case */ + // $standardSectionId contains the ID of the "Standard" section in a Ibexa + // Publish demo installation. + + $sectionService = $repository->getSectionService(); + + $section = $sectionService->loadSection($standardSectionId); + $sectionUpdate = $sectionService->newSectionUpdateStruct(); + $sectionUpdate->name = 'New section name'; + + // section identifier remains the same + $sectionUpdate->identifier = $section->identifier; + + $updatedSection = $sectionService->updateSection($section, $sectionUpdate); + /* END: Use Case */ + + $this->assertEquals('standard', $updatedSection->identifier); + } + + /** + * Test for the updateSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::updateSection() + * @depends testUpdateSection + */ + public function testUpdateSectionKeepsSectionNameOnIdentifierUpdate() + { + $repository = $this->getRepository(); + + $standardSectionId = $this->generateId('section', 1); + /* BEGIN: Use Case */ + // $standardSectionId contains the ID of the "Standard" section in a Ibexa + // Publish demo installation. + + $sectionService = $repository->getSectionService(); + + $section = $sectionService->loadSection($standardSectionId); + + $sectionUpdate = $sectionService->newSectionUpdateStruct(); + $sectionUpdate->identifier = 'newUniqueKey'; + + $updatedSection = $sectionService->updateSection($section, $sectionUpdate); + /* END: Use Case */ + + $this->assertEquals('Standard', $updatedSection->name); + } + + /** + * Test for the updateSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::updateSection() + * @depends testUpdateSection + */ + public function testUpdateSectionThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $standardSectionId = $this->generateId('section', 1); + /* BEGIN: Use Case */ + // $standardSectionId contains the ID of the "Standard" section in a Ibexa + // Publish demo installation. + + $sectionService = $repository->getSectionService(); + + // Create section with conflict identifier + $sectionCreate = $sectionService->newSectionCreateStruct(); + $sectionCreate->name = 'Conflict section'; + $sectionCreate->identifier = 'conflictKey'; + + $sectionService->createSection($sectionCreate); + + // Load an existing section and update to an existing identifier + $section = $sectionService->loadSection($standardSectionId); + + $sectionUpdate = $sectionService->newSectionUpdateStruct(); + $sectionUpdate->identifier = 'conflictKey'; + + // This call should fail with an InvalidArgumentException + $sectionService->updateSection($section, $sectionUpdate); + /* END: Use Case */ + } + + /** + * Test for the loadSections() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::loadSections() + * @depends testCreateSection + */ + public function testLoadSections() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + $sections = $sectionService->loadSections(); + foreach ($sections as $section) { + // Operate on all sections. + } + /* END: Use Case */ + + $this->assertCount(6, $sections); + } + + /** + * Test for the loadSections() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::loadSections() + * @depends testCreateSection + */ + public function testLoadSectionsReturnsDefaultSectionsByDefault() + { + $repository = $this->getRepository(); + + $sectionService = $repository->getSectionService(); + + $this->assertEquals( + [ + new Section( + [ + 'id' => $this->generateId('section', 1), + 'name' => 'Standard', + 'identifier' => 'standard', + ] + ), + new Section( + [ + 'id' => $this->generateId('section', 2), + 'name' => 'Users', + 'identifier' => 'users', + ] + ), + new Section( + [ + 'id' => $this->generateId('section', 3), + 'name' => 'Media', + 'identifier' => 'media', + ] + ), + new Section( + [ + 'id' => $this->generateId('section', 4), + 'name' => 'Setup', + 'identifier' => 'setup', + ] + ), + new Section( + [ + 'id' => $this->generateId('section', 5), + 'name' => 'Design', + 'identifier' => 'design', + ] + ), + new Section( + [ + 'id' => $this->generateId('section', 6), + 'name' => 'Restricted', + 'identifier' => '', + ] + ), + ], + $sectionService->loadSections() + ); + } + + /** + * Test for the loadSectionByIdentifier() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::loadSectionByIdentifier() + * @depends testCreateSection + */ + public function testLoadSectionByIdentifier() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + $sectionCreate = $sectionService->newSectionCreateStruct(); + $sectionCreate->name = 'Test Section'; + $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; + + $sectionId = $sectionService->createSection($sectionCreate)->id; + + $section = $sectionService->loadSectionByIdentifier(self::SECTION_UNIQUE_KEY); + /* END: Use Case */ + + $this->assertEquals($sectionId, $section->id); + } + + /** + * Test for the loadSectionByIdentifier() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::loadSectionByIdentifier() + */ + public function testLoadSectionByIdentifierThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + // This call should fail with a NotFoundException + $sectionService->loadSectionByIdentifier('someUnknownSectionIdentifier'); + /* END: Use Case */ + } + + /** + * Test for the countAssignedContents() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::countAssignedContents() + */ + public function testCountAssignedContents() + { + $repository = $this->getRepository(); + + $sectionService = $repository->getSectionService(); + + $standardSectionId = $this->generateId('section', 1); + /* BEGIN: Use Case */ + // $standardSectionId contains the ID of the "Standard" section in a Ibexa + // Publish demo installation. + + $standardSection = $sectionService->loadSection($standardSectionId); + + $numberOfAssignedContent = $sectionService->countAssignedContents( + $standardSection + ); + /* END: Use Case */ + + $this->assertEquals( + 2, // Taken from the fixture + $numberOfAssignedContent + ); + } + + /** + * Test for the isSectionUsed() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::isSectionUsed() + */ + public function testIsSectionUsed() + { + $repository = $this->getRepository(); + + $sectionService = $repository->getSectionService(); + + $standardSectionId = $this->generateId('section', 1); + /* BEGIN: Use Case */ + // $standardSectionId contains the ID of the "Standard" section in a Ibexa + // Publish demo installation. + + $standardSection = $sectionService->loadSection($standardSectionId); + + $isSectionUsed = $sectionService->isSectionUsed( + $standardSection + ); + /* END: Use Case */ + + $this->assertTrue( + // Taken from the fixture + $isSectionUsed + ); + } + + /** + * Test for the assignSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::assignSection() + * @depends testCountAssignedContents + */ + public function testAssignSection() + { + $repository = $this->getRepository(); + $sectionService = $repository->getSectionService(); + + $standardSectionId = $this->generateId('section', 1); + $mediaSectionId = $this->generateId('section', 3); + + $beforeStandardCount = $sectionService->countAssignedContents( + $sectionService->loadSection($standardSectionId) + ); + $beforeMediaCount = $sectionService->countAssignedContents( + $sectionService->loadSection($mediaSectionId) + ); + + /* BEGIN: Use Case */ + // $mediaSectionId contains the ID of the "Media" section in a Ibexa + // Publish demo installation. + + // RemoteId of the "Media" page of an Ibexa demo installation + $mediaRemoteId = 'a6e35cbcb7cd6ae4b691f3eee30cd262'; + + $contentService = $repository->getContentService(); + $sectionService = $repository->getSectionService(); + + // Load a content info instance + $contentInfo = $contentService->loadContentInfoByRemoteId( + $mediaRemoteId + ); + + // Load the "Standard" section + $section = $sectionService->loadSection($standardSectionId); + + // Assign Section to ContentInfo + $sectionService->assignSection($contentInfo, $section); + /* END: Use Case */ + + $this->assertEquals( + $beforeStandardCount + 1, + $sectionService->countAssignedContents( + $sectionService->loadSection($standardSectionId) + ) + ); + $this->assertEquals( + $beforeMediaCount - 1, + $sectionService->countAssignedContents( + $sectionService->loadSection($mediaSectionId) + ) + ); + } + + /** + * Test for the assignSectionToSubtree() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::assignSectionToSubtree() + * @depends testCreateSection + */ + public function testAssignSectionToSubtree() + { + $repository = $this->getRepository(); + $sectionService = $repository->getSectionService(); + + $standardSectionId = $this->generateId('section', 1); + $mediaSectionId = $this->generateId('section', 3); + + $beforeStandardCount = $sectionService->countAssignedContents( + $sectionService->loadSection($standardSectionId) + ); + + $beforeMediaCount = $sectionService->countAssignedContents( + $sectionService->loadSection($mediaSectionId) + ); + + // RemoteId of the "Media" page of an Ibexa demo installation + $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; + + /* BEGIN: Use Case */ + $locationService = $repository->getLocationService(); + + // Load a location instance + $location = $locationService->loadLocationByRemoteId($mediaRemoteId); + + // Load the "Standard" section + $section = $sectionService->loadSection($standardSectionId); + + // Assign Section to ContentInfo + $sectionService->assignSectionToSubtree($location, $section); + + /* END: Use Case */ + $this->assertEquals( + $beforeStandardCount + 4, + $sectionService->countAssignedContents( + $sectionService->loadSection($standardSectionId) + ) + ); + $this->assertEquals( + $beforeMediaCount - 4, + $sectionService->countAssignedContents( + $sectionService->loadSection($mediaSectionId) + ) + ); + } + + /** + * Test for the countAssignedContents() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::countAssignedContents() + * @depends testCreateSection + */ + public function testCountAssignedContentsReturnsZeroByDefault() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + $sectionCreate = $sectionService->newSectionCreateStruct(); + $sectionCreate->name = 'Test Section'; + $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; + + $section = $sectionService->createSection($sectionCreate); + + // The number of assigned contents should be zero + $assignedContents = $sectionService->countAssignedContents($section); + /* END: Use Case */ + + $this->assertSame(0, $assignedContents); + } + + /** + * Test for the isSectionUsed() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::isSectionUsed() + * @depends testCreateSection + */ + public function testIsSectionUsedReturnsZeroByDefault() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + $sectionCreate = $sectionService->newSectionCreateStruct(); + $sectionCreate->name = 'Test Section'; + $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; + + $section = $sectionService->createSection($sectionCreate); + + // The number of assigned contents should be zero + $isSectionUsed = $sectionService->isSectionUsed($section); + /* END: Use Case */ + + $this->assertFalse($isSectionUsed); + } + + /** + * Test for the deleteSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::deleteSection() + * @depends testLoadSections + */ + public function testDeleteSection() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + $sectionCreate = $sectionService->newSectionCreateStruct(); + $sectionCreate->name = 'Test Section'; + $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; + + $section = $sectionService->createSection($sectionCreate); + + // Delete the newly created section + $sectionService->deleteSection($section); + /* END: Use Case */ + + $this->assertCount(6, $sectionService->loadSections()); + } + + /** + * Test for the deleteSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::deleteSection() + * @depends testDeleteSection + */ + public function testDeleteSectionThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + $sectionCreate = $sectionService->newSectionCreateStruct(); + $sectionCreate->name = 'Test Section'; + $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; + + $section = $sectionService->createSection($sectionCreate); + + // Delete the newly created section + $sectionService->deleteSection($section); + + // This call should fail with a NotFoundException + $sectionService->deleteSection($section); + /* END: Use Case */ + } + + /** + * Test for the deleteSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::deleteSection() + * @depends testAssignSection + */ + public function testDeleteSectionThrowsBadStateException() + { + $this->expectException(BadStateException::class); + + $repository = $this->getRepository(); + + $standardSectionId = $this->generateId('section', 1); + /* BEGIN: Use Case */ + // $standardSectionId contains the ID of the "Standard" section in a Ibexa + // Publish demo installation. + + // RemoteId of the "Media" page of an Ibexa demo installation + $mediaRemoteId = 'a6e35cbcb7cd6ae4b691f3eee30cd262'; + + $contentService = $repository->getContentService(); + $sectionService = $repository->getSectionService(); + + // Load the "Media" ContentInfo + $contentInfo = $contentService->loadContentInfoByRemoteId($mediaRemoteId); + + // Load the "Standard" section + $section = $sectionService->loadSection($standardSectionId); + + // Assign "Media" to "Standard" section + $sectionService->assignSection($contentInfo, $section); + + // This call should fail with a BadStateException, because there are assigned contents + $sectionService->deleteSection($section); + /* END: Use Case */ + } + + /** + * Test for the createSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::createSection() + * @depends testCreateSection + * @depends testLoadSectionByIdentifier + */ + public function testCreateSectionInTransactionWithRollback() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Get a create struct and set some properties + $sectionCreate = $sectionService->newSectionCreateStruct(); + $sectionCreate->name = 'Test Section'; + $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; + + // Create a new section + $sectionService->createSection($sectionCreate); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes + $repository->rollback(); + + try { + // This call will fail with a not found exception + $sectionService->loadSectionByIdentifier(self::SECTION_UNIQUE_KEY); + } catch (NotFoundException $e) { + // Expected execution path + } + /* END: Use Case */ + + $this->assertTrue(isset($e), 'Can still load section after rollback.'); + } + + /** + * Test for the createSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::createSection() + * @depends testCreateSection + * @depends testLoadSectionByIdentifier + */ + public function testCreateSectionInTransactionWithCommit() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Get a create struct and set some properties + $sectionCreate = $sectionService->newSectionCreateStruct(); + $sectionCreate->name = 'Test Section'; + $sectionCreate->identifier = self::SECTION_UNIQUE_KEY; + + // Create a new section + $sectionService->createSection($sectionCreate); + + // Commit all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Load new section + $section = $sectionService->loadSectionByIdentifier(self::SECTION_UNIQUE_KEY); + /* END: Use Case */ + + $this->assertEquals(self::SECTION_UNIQUE_KEY, $section->identifier); + } + + /** + * Test for the createSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::createSection() + * @depends testUpdateSection + * @depends testLoadSectionByIdentifier + */ + public function testUpdateSectionInTransactionWithRollback() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Load standard section + $section = $sectionService->loadSectionByIdentifier('standard'); + + // Get an update struct and change section name + $sectionUpdate = $sectionService->newSectionUpdateStruct(); + $sectionUpdate->name = 'My Standard'; + + // Update section + $sectionService->updateSection($section, $sectionUpdate); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Rollback all changes + $repository->rollback(); + + // Load updated section, name will still be "Standard" + $updatedStandard = $sectionService->loadSectionByIdentifier('standard'); + /* END: Use Case */ + + $this->assertEquals('Standard', $updatedStandard->name); + } + + /** + * Test for the createSection() method. + * + * @covers \Ibexa\Contracts\Core\Repository\SectionService::createSection() + * @depends testUpdateSection + * @depends testLoadSectionByIdentifier + */ + public function testUpdateSectionInTransactionWithCommit() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $sectionService = $repository->getSectionService(); + + // Start a new transaction + $repository->beginTransaction(); + + try { + // Load standard section + $section = $sectionService->loadSectionByIdentifier('standard'); + + // Get an update struct and change section name + $sectionUpdate = $sectionService->newSectionUpdateStruct(); + $sectionUpdate->name = 'My Standard'; + + // Update section + $sectionService->updateSection($section, $sectionUpdate); + + // Commit all changes + $repository->commit(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + // Load updated section, name will now be "My Standard" + $updatedStandard = $sectionService->loadSectionByIdentifier('standard'); + /* END: Use Case */ + + $this->assertEquals('My Standard', $updatedStandard->name); + } +} + +class_alias(SectionServiceTest::class, 'eZ\Publish\API\Repository\Tests\SectionServiceTest'); diff --git a/tests/integration/Core/Repository/SettingServiceTest.php b/tests/integration/Core/Repository/SettingServiceTest.php new file mode 100644 index 0000000000..e19688b9c4 --- /dev/null +++ b/tests/integration/Core/Repository/SettingServiceTest.php @@ -0,0 +1,230 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\SettingService; +use Ibexa\Contracts\Core\Repository\Values\Setting\Setting; + +/** + * Test case for operations in the SettingService using in memory storage. + * + * @covers \Ibexa\Contracts\Core\Repository\SettingService + * @group integration + * @group setting + */ +final class SettingServiceTest extends BaseTest +{ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver */ + protected $permissionResolver; + + /** @var \Ibexa\Contracts\Core\Repository\SettingService */ + protected $settingService; + + protected function getSettingService(): SettingService + { + $container = $this->getSetupFactory()->getServiceContainer(); + /** @var \Ibexa\Contracts\Core\Repository\SettingService $settingService */ + $settingService = $container->get(SettingService::class); + + return $settingService; + } + + protected function setUp(): void + { + parent::setUp(); + + $repository = $this->getRepository(false); + $this->permissionResolver = $repository->getPermissionResolver(); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\SettingService::createSetting() + * + * @dataProvider dataProviderForCreateSetting + */ + public function testCreateSetting(string $identifier, $value): void + { + $settingService = $this->getSettingService(); + + $settingCreate = $settingService->newSettingCreateStruct(); + $settingCreate->setGroup('test_group'); + $settingCreate->setIdentifier($identifier); + $settingCreate->setValue($value); + + $setting = $settingService->createSetting($settingCreate); + + self::assertEquals(new Setting([ + 'group' => 'test_group', + 'identifier' => $identifier, + 'value' => $value, + ]), $setting); + } + + public function dataProviderForCreateSetting(): iterable + { + yield 'null' => [ + 'example_null', + null, + ]; + + yield 'boolean' => [ + 'example_boolean', + true, + ]; + + yield 'string' => [ + 'example_string', + 'string', + ]; + + yield 'int' => [ + 'example_int', + 2, + ]; + + yield 'float' => [ + 'example_number', + 3.14, + ]; + + yield 'array' => [ + 'example_hash', + [ + 'foo' => 'foo', + 'bar' => 2, + 'baz' => 3.14, + 'foobar' => range(1, 10), + ], + ]; + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\SettingService::createSetting() + */ + public function testCreateSettingThrowsInvalidArgumentException(): void + { + $settingService = $this->getSettingService(); + + $settingCreateFirst = $settingService->newSettingCreateStruct(); + $settingCreateFirst->setGroup('test_group2'); + $settingCreateFirst->setIdentifier('test_identifier2'); + $settingCreateFirst->setValue('test_value'); + + $settingService->createSetting($settingCreateFirst); + + $settingCreateSecond = $settingService->newSettingCreateStruct(); + $settingCreateSecond->setGroup('test_group2'); + $settingCreateSecond->setIdentifier('test_identifier2'); + $settingCreateSecond->setValue('another_value'); + + $this->expectException(InvalidArgumentException::class); + + $settingService->createSetting($settingCreateSecond); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\SettingService::loadSetting() + */ + public function testLoadSetting(): void + { + $settingService = $this->getSettingService(); + + $settingCreate = $settingService->newSettingCreateStruct(); + $settingCreate->setGroup('another_group'); + $settingCreate->setIdentifier('another_identifier'); + $settingCreate->setValue('test_value'); + + $settingService->createSetting($settingCreate); + $setting = $settingService->loadSetting('another_group', 'another_identifier'); + + self::assertEquals('test_value', $setting->value); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\SettingService::loadSetting() + */ + public function testLoadSettingThrowsNotFoundException(): void + { + $settingService = $this->getSettingService(); + + $this->expectException(NotFoundException::class); + + $settingService->loadSetting('unknown_group', 'unknown_identifier'); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\SettingService::updateSetting() + */ + public function testUpdateSetting(): void + { + $settingService = $this->getSettingService(); + + $settingCreate = $settingService->newSettingCreateStruct(); + $settingCreate->setGroup('update_group'); + $settingCreate->setIdentifier('update_identifier'); + $settingCreate->setValue('some_value'); + + $setting = $settingService->createSetting($settingCreate); + + $settingUpdate = $settingService->newSettingUpdateStruct(); + $settingUpdate->setValue('updated_value'); + + $settingService->updateSetting($setting, $settingUpdate); + $updatedSetting = $settingService->loadSetting('update_group', 'update_identifier'); + + self::assertEquals('updated_value', $updatedSetting->value); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\SettingService::deleteSetting() + */ + public function testDeleteSetting(): void + { + $settingService = $this->getSettingService(); + + $settingCreate = $settingService->newSettingCreateStruct(); + $settingCreate->setGroup('delete_group'); + $settingCreate->setIdentifier('delete_identifier'); + $settingCreate->setValue('some_value'); + + $setting = $settingService->createSetting($settingCreate); + $settingService->deleteSetting($setting); + + $this->expectException(NotFoundException::class); + + $settingService->loadSetting('delete_group', 'delete_identifier'); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\SettingService::deleteSetting() + */ + public function testDeleteSettingThrowsNotFoundException(): void + { + $settingService = $this->getSettingService(); + + $settingCreate = $settingService->newSettingCreateStruct(); + $settingCreate->setGroup('delete_twice_group'); + $settingCreate->setIdentifier('delete_twice_identifier'); + $settingCreate->setValue('some_value'); + + $setting = $settingService->createSetting($settingCreate); + + // Delete the newly created setting + $settingService->deleteSetting($setting); + + $this->expectException(NotFoundException::class); + + // This call should fail with a NotFoundException + $settingService->deleteSetting($setting); + } +} + +class_alias(SettingServiceTest::class, 'eZ\Publish\API\Repository\Tests\SettingServiceTest'); diff --git a/tests/integration/Core/Repository/TokenServiceTest.php b/tests/integration/Core/Repository/TokenServiceTest.php new file mode 100644 index 0000000000..0b723c36e4 --- /dev/null +++ b/tests/integration/Core/Repository/TokenServiceTest.php @@ -0,0 +1,294 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\TokenService; +use Ibexa\Contracts\Core\Repository\Values\Token\Token; +use Ibexa\Contracts\Core\Test\IbexaKernelTestCase; +use Ibexa\Core\Base\Exceptions\TokenLengthException; + +/** + * @covers \Ibexa\Core\Repository\TokenService + */ +final class TokenServiceTest extends IbexaKernelTestCase +{ + private const TOKEN_TYPE = 'foo'; + private const TOKEN_TTL = 100; + private const TOKEN_IDENTIFIER = 'test'; + + private TokenService $tokenService; + + protected function setUp(): void + { + parent::setUp(); + + self::loadSchema(); + self::loadFixtures(); + + $this->tokenService = self::getServiceByClassName(TokenService::class); + } + + /** + * @dataProvider provideTokenData + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testGenerateToken( + string $type, + int $tll, + ?string $identifier, + int $length = 64 + ): void { + $token = $this->tokenService->generateToken($type, $tll, $identifier, $length); + + self::assertSame($type, $token->getType()); + self::assertSame($identifier, $token->getIdentifier()); + self::assertSame($length, strlen($token->getToken())); + self::assertFalse($token->isRevoked()); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testGenerateTokenThrowsTokenLengthException(): void + { + $length = 300; + + $this->expectException(TokenLengthException::class); + $this->expectExceptionMessage('Token length is too long: 300 characters. Max length is 255.'); + + $this->tokenService->generateToken( + self::TOKEN_TYPE, + self::TOKEN_TTL, + self::TOKEN_IDENTIFIER, + $length + ); + } + + /** + * @dataProvider provideDataForTestCheckToken + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testCheckExistingToken( + string $type, + int $tll, + ?string $identifier + ): void { + $token = $this->tokenService->generateToken($type, $tll, $identifier); + + self::assertTrue( + $this->tokenService->checkToken( + $token->getType(), + $token->getToken(), + $token->getIdentifier() + ) + ); + } + + public function testCheckNotExistentToken(): void + { + self::assertFalse( + $this->tokenService->checkToken( + 'bar', + '1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik,', + 'test' + ) + ); + } + + /** + * @dataProvider provideTokenData + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testGetToken( + string $type, + int $tll, + ?string $identifier, + int $length = 64 + ): void { + $token = $this->tokenService->generateToken($type, $tll, $identifier, $length); + + self::assertEquals( + $token, + $this->tokenService->getToken($type, $token->getToken(), $identifier) + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testRevokeToken(): void + { + $token = $this->tokenService->generateToken( + self::TOKEN_TYPE, + self::TOKEN_TTL, + self::TOKEN_IDENTIFIER + ); + + $this->tokenService->revokeToken($token); + + $this->assertRevokedToken($token); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testRevokeAllTokensByIdentifier(): void + { + $tokens = [ + $this->tokenService->generateToken( + self::TOKEN_TYPE, + self::TOKEN_TTL, + self::TOKEN_IDENTIFIER + ), + $this->tokenService->generateToken( + self::TOKEN_TYPE, + self::TOKEN_TTL, + self::TOKEN_IDENTIFIER + ), + $this->tokenService->generateToken( + self::TOKEN_TYPE, + self::TOKEN_TTL, + self::TOKEN_IDENTIFIER + ), + ]; + + $differentToken = $this->tokenService->generateToken( + self::TOKEN_TYPE, + self::TOKEN_TTL, + 'different' + ); + + $tokenWithoutIdentifier = $this->tokenService->generateToken( + self::TOKEN_TYPE, + self::TOKEN_TTL + ); + + $this->tokenService->revokeTokenByIdentifier(self::TOKEN_TYPE, self::TOKEN_IDENTIFIER); + + foreach ($tokens as $token) { + $this->assertRevokedToken($token); + } + + self::assertFalse( + $this->tokenService->getToken( + $differentToken->getType(), + $differentToken->getToken(), + $differentToken->getIdentifier() + )->isRevoked() + ); + + self::assertFalse( + $this->tokenService->getToken( + $tokenWithoutIdentifier->getType(), + $tokenWithoutIdentifier->getToken(), + $tokenWithoutIdentifier->getIdentifier() + )->isRevoked() + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testDeleteToken(): void + { + $token = $this->tokenService->generateToken( + self::TOKEN_TYPE, + self::TOKEN_TTL, + self::TOKEN_IDENTIFIER + ); + + $this->tokenService->deleteToken($token); + + self::assertFalse( + $this->tokenService->checkToken( + $token->getType(), + $token->getToken(), + $token->getIdentifier() + ) + ); + } + + /** + * @return iterable<array{ + * string, + * int, + * ?string, + * ?int + * }> + */ + public function provideTokenData(): iterable + { + yield 'Token with default length 64 and custom identifier' => [ + self::TOKEN_TYPE, + self::TOKEN_TTL, + self::TOKEN_IDENTIFIER, + 64, + ]; + + yield 'Token with length 200 and custom identifier' => [ + self::TOKEN_TYPE, + self::TOKEN_TTL, + self::TOKEN_IDENTIFIER, + 200, + ]; + + yield 'Token without identifier' => [ + self::TOKEN_TYPE, + self::TOKEN_TTL, + null, + ]; + } + + /** + * @return iterable<array{ + * string, + * int, + * ?string + * }> + */ + public function provideDataForTestCheckToken(): iterable + { + yield 'Token with identifier' => [ + self::TOKEN_TYPE, + self::TOKEN_TTL, + self::TOKEN_IDENTIFIER, + ]; + + yield 'Token without identifier' => [ + self::TOKEN_TYPE, + self::TOKEN_TTL, + null, + ]; + } + + private function assertRevokedToken(Token $token): void + { + $revokedToken = $this->tokenService->getToken( + $token->getType(), + $token->getToken(), + $token->getIdentifier() + ); + + self::assertSame($token->getType(), $revokedToken->getType()); + self::assertSame($token->getToken(), $revokedToken->getToken()); + self::assertSame($token->getIdentifier(), $revokedToken->getIdentifier()); + self::assertTrue($revokedToken->isRevoked()); + + self::assertFalse( + $this->tokenService->checkToken( + $token->getType(), + $token->getToken(), + $token->getIdentifier() + ) + ); + } +} diff --git a/eZ/Publish/API/Repository/Tests/TrashServiceAuthorizationTest.php b/tests/integration/Core/Repository/TrashServiceAuthorizationTest.php similarity index 79% rename from eZ/Publish/API/Repository/Tests/TrashServiceAuthorizationTest.php rename to tests/integration/Core/Repository/TrashServiceAuthorizationTest.php index f07d84f8a2..c05865e438 100644 --- a/eZ/Publish/API/Repository/Tests/TrashServiceAuthorizationTest.php +++ b/tests/integration/Core/Repository/TrashServiceAuthorizationTest.php @@ -4,18 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\User\Limitation\LanguageLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation; -use eZ\Publish\Core\Repository\TrashService; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\LanguageLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation; +use Ibexa\Core\Repository\TrashService; /** * Test case for operations in the TrashService using in memory storage. * - * @see eZ\Publish\API\Repository\TrashService + * @covers \Ibexa\Contracts\Core\Repository\TrashService * @group integration * @group authorization */ @@ -24,13 +24,13 @@ class TrashServiceAuthorizationTest extends BaseTrashServiceTest /** * Test for the loadTrashItem() method. * - * @see \eZ\Publish\API\Repository\TrashService::loadTrashItem() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testLoadTrashItem - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUser + * @covers \Ibexa\Contracts\Core\Repository\TrashService::loadTrashItem() + * @depends Ibexa\Tests\Integration\Core\Repository\TrashServiceTest::testLoadTrashItem + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUser */ public function testLoadTrashItemThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $trashService = $repository->getTrashService(); @@ -55,11 +55,11 @@ public function testLoadTrashItemThrowsUnauthorizedException() /** * Test for the trash() method without proper permissions. * - * @covers \eZ\Publish\API\Repository\TrashService::trash + * @covers \Ibexa\Contracts\Core\Repository\TrashService::trash */ public function testTrashThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $this->expectExceptionMessage('The User does not have the \'remove\' \'content\' permission'); $repository = $this->getRepository(); @@ -81,7 +81,7 @@ public function testTrashThrowsUnauthorizedException() /** * Test for the trash() method without proper permissions. * - * @covers \eZ\Publish\API\Repository\TrashService::trash + * @covers \Ibexa\Contracts\Core\Repository\TrashService::trash */ public function testTrashThrowsUnauthorizedExceptionWithLanguageLimitation(): void { @@ -118,7 +118,7 @@ public function testTrashThrowsUnauthorizedExceptionWithLanguageLimitation(): vo * * @depends testTrashThrowsUnauthorizedException * - * @covers \eZ\Publish\API\Repository\TrashService::trash + * @covers \Ibexa\Contracts\Core\Repository\TrashService::trash */ public function testTrashRequiresContentRemovePolicy() { @@ -145,13 +145,13 @@ public function testTrashRequiresContentRemovePolicy() /** * Test for the recover() method. * - * @see \eZ\Publish\API\Repository\TrashService::recover() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testRecover - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUser + * @covers \Ibexa\Contracts\Core\Repository\TrashService::recover() + * @depends Ibexa\Tests\Integration\Core\Repository\TrashServiceTest::testRecover + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUser */ public function testRecoverThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $trashService = $repository->getTrashService(); @@ -176,13 +176,13 @@ public function testRecoverThrowsUnauthorizedException() /** * Test for the recover() method. * - * @see \eZ\Publish\API\Repository\TrashService::recover($trashItem, $newParentLocation) - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testRecover - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUser + * @covers \Ibexa\Contracts\Core\Repository\TrashService::recover($trashItem, $newParentLocation) + * @depends Ibexa\Tests\Integration\Core\Repository\TrashServiceTest::testRecover + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUser */ public function testRecoverThrowsUnauthorizedExceptionWithNewParentLocationParameter() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $trashService = $repository->getTrashService(); @@ -193,7 +193,7 @@ public function testRecoverThrowsUnauthorizedExceptionWithNewParentLocationParam $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ // $anonymousUserId is the ID of the "Anonymous" user - // $homeLocationId is the ID of the "Home" location in an eZ Publish + // $homeLocationId is the ID of the "Home" location in an Ibexa // demo installation $trashItem = $this->createTrashItem(); @@ -215,13 +215,13 @@ public function testRecoverThrowsUnauthorizedExceptionWithNewParentLocationParam /** * Test for the emptyTrash() method. * - * @see \eZ\Publish\API\Repository\TrashService::emptyTrash() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testEmptyTrash - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUser + * @covers \Ibexa\Contracts\Core\Repository\TrashService::emptyTrash() + * @depends Ibexa\Tests\Integration\Core\Repository\TrashServiceTest::testEmptyTrash + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUser */ public function testEmptyTrashThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $trashService = $repository->getTrashService(); @@ -246,13 +246,13 @@ public function testEmptyTrashThrowsUnauthorizedException() /** * Test for the deleteTrashItem() method. * - * @see \eZ\Publish\API\Repository\TrashService::deleteTrashItem() - * @depends eZ\Publish\API\Repository\Tests\TrashServiceTest::testDeleteTrashItem - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUser + * @covers \Ibexa\Contracts\Core\Repository\TrashService::deleteTrashItem() + * @depends Ibexa\Tests\Integration\Core\Repository\TrashServiceTest::testDeleteTrashItem + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUser */ public function testDeleteTrashItemThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $trashService = $repository->getTrashService(); @@ -293,7 +293,7 @@ public function testTrashRequiresPremissionsToRemoveAllSubitems() 'Publishers', 'Publisher' ); - /** @var \eZ\Publish\Core\Repository\Repository $repository */ + /** @var \Ibexa\Core\Repository\Repository $repository */ $repository = $this->getRepository(); $repository->getPermissionResolver()->setCurrentUserReference($publisherUser); $trashService = $repository->getTrashService(); @@ -309,7 +309,9 @@ public function testTrashRequiresPremissionsToRemoveAllSubitems() $childContent = $this->createFolder(['eng-US' => 'Child Folder'], $parentLocation->id); $this->refreshSearch($repository); - $this->expectException(\eZ\Publish\Core\Base\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $trashService->trash($parentLocation); } } + +class_alias(TrashServiceAuthorizationTest::class, 'eZ\Publish\API\Repository\Tests\TrashServiceAuthorizationTest'); diff --git a/tests/integration/Core/Repository/TrashServiceTest.php b/tests/integration/Core/Repository/TrashServiceTest.php new file mode 100644 index 0000000000..ffb2bc5446 --- /dev/null +++ b/tests/integration/Core/Repository/TrashServiceTest.php @@ -0,0 +1,1342 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use DateTime; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\URLAliasService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location as APILocation; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Trash\SearchResult; +use Ibexa\Contracts\Core\Repository\Values\Content\TrashItem as APITrashItem; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Repository\Values\Content\TrashItem; + +/** + * Test case for operations in the TrashService using in memory storage. + * + * @covers \Ibexa\Contracts\Core\Repository\TrashService + * @group integration + * @group trash + */ +class TrashServiceTest extends BaseTrashServiceTest +{ + /** + * Test for the trash() method. + * + * @depends Ibexa\Tests\Integration\Core\Repository\LocationServiceTest::testLoadLocationByRemoteId + */ + public function testTrash() + { + /* BEGIN: Use Case */ + $trashItem = $this->createTrashItem(); + /* END: Use Case */ + + $this->assertInstanceOf( + TrashItem::class, + $trashItem + ); + } + + /** + * Test for the trash() method. + * + * @depends testTrash + */ + public function testTrashSetsExpectedTrashItemProperties() + { + $repository = $this->getRepository(); + + $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; + + // Load the location that will be trashed + $location = $repository->getLocationService() + ->loadLocationByRemoteId($mediaRemoteId); + + $expected = [ + 'id' => $location->id, + 'depth' => $location->depth, + 'hidden' => $location->hidden, + 'invisible' => $location->invisible, + 'parentLocationId' => $location->parentLocationId, + 'pathString' => $location->pathString, + 'priority' => $location->priority, + 'remoteId' => $location->remoteId, + 'sortField' => $location->sortField, + 'sortOrder' => $location->sortOrder, + ]; + + $trashItem = $this->createTrashItem(); + + $this->assertPropertiesCorrect($expected, $trashItem); + } + + /** + * Test for the trash() method. + * + * @depends testTrash + */ + public function testTrashRemovesLocationFromMainStorage() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; + + /* BEGIN: Use Case */ + $this->createTrashItem(); + + // Load the location service + $locationService = $repository->getLocationService(); + + // This call will fail with a "NotFoundException", because the media + // location was marked as trashed in the main storage + $locationService->loadLocationByRemoteId($mediaRemoteId); + /* END: Use Case */ + } + + /** + * Test for the trash() method. + * + * @depends testTrash + */ + public function testTrashRemovesChildLocationsFromMainStorage() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $remoteIds = $this->createRemoteIdList(); + + $this->createTrashItem(); + + // All invocations to loadLocationByRemoteId() to one of the above + // collected remoteIds will return in an "NotFoundException" + /* END: Use Case */ + + $locationService = $repository->getLocationService(); + foreach ($remoteIds as $remoteId) { + try { + $locationService->loadLocationByRemoteId($remoteId); + $this->fail("Location '{$remoteId}' should exist.'"); + } catch (NotFoundException $e) { + // echo $e->getFile(), ' +', $e->getLine(), PHP_EOL; + } + } + + $this->assertGreaterThan( + 0, + count($remoteIds), + "There should be at least one 'Community' child location." + ); + } + + /** + * Test for the trash() method. + * + * @depends testTrash + */ + public function testTrashDecrementsChildCountOnParentLocation() + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $baseLocationId = $this->generateId('location', 1); + + $location = $locationService->loadLocation($baseLocationId); + + $childCount = $locationService->getLocationChildCount($location); + + $this->createTrashItem(); + + $this->refreshSearch($repository); + + $this->assertEquals( + $childCount - 1, + $locationService->getLocationChildCount($location) + ); + } + + /** + * Test sending a location to trash updates Content mainLocation. + */ + public function testTrashUpdatesMainLocation() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + $trashService = $repository->getTrashService(); + + $contentInfo = $contentService->loadContentInfo(42); + + // Create additional location that will become new main location + $location = $locationService->createLocation( + $contentInfo, + new LocationCreateStruct(['parentLocationId' => 2]) + ); + + $trashService->trash( + $locationService->loadLocation($contentInfo->mainLocationId) + ); + + self::assertEquals( + $location->id, + $contentService->loadContentInfo(42)->mainLocationId + ); + } + + /** + * Test sending a location to trash. + */ + public function testTrashReturnsNull() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + $trashService = $repository->getTrashService(); + + // Create additional location to trash + $location = $locationService->createLocation( + $contentService->loadContentInfo(42), + new LocationCreateStruct(['parentLocationId' => 2]) + ); + + $trashItem = $trashService->trash($location); + + self::assertNull($trashItem); + } + + /** + * Test for the loadTrashItem() method. + * + * @depends testTrash + */ + public function testLoadTrashItem() + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + + /* BEGIN: Use Case */ + $trashItem = $this->createTrashItem(); + + // Reload the trash item + $trashItemReloaded = $trashService->loadTrashItem($trashItem->id); + /* END: Use Case */ + + $this->assertInstanceOf( + APITrashItem::class, + $trashItemReloaded + ); + + $this->assertEquals( + $trashItem->pathString, + $trashItemReloaded->pathString + ); + + $this->assertInstanceOf( + DateTime::class, + $trashItemReloaded->trashed + ); + + $this->assertEquals( + $trashItem->trashed->getTimestamp(), + $trashItemReloaded->trashed->getTimestamp() + ); + + $this->assertGreaterThan( + 0, + $trashItemReloaded->trashed->getTimestamp() + ); + + $this->assertInstanceOf( + Content::class, + $content = $trashItemReloaded->getContent() + ); + $this->assertEquals($trashItem->contentId, $content->contentInfo->id); + } + + /** + * Test for the loadTrashItem() method. + * + * @depends testLoadTrashItem + */ + public function testLoadTrashItemThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $nonExistingTrashId = $this->generateId('trash', 2342); + /* BEGIN: Use Case */ + $trashService = $repository->getTrashService(); + + // This call will fail with a "NotFoundException", because no trash item + // with the ID 1342 should exist in an Ibexa demo installation + $trashService->loadTrashItem($nonExistingTrashId); + /* END: Use Case */ + } + + /** + * Test for the recover() method. + * + * @depends testTrash + */ + public function testRecover() + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + $locationService = $repository->getLocationService(); + + $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; + + /* BEGIN: Use Case */ + $trashItem = $this->createTrashItem(); + + // Recover the trashed item + $location = $trashService->recover($trashItem); + + // Load the recovered location + $locationReloaded = $locationService->loadLocationByRemoteId( + $mediaRemoteId + ); + /* END: Use Case */ + + $this->assertInstanceOf( + APILocation::class, + $location + ); + + $this->assertEquals( + $location, + $locationReloaded + ); + + try { + $trashService->loadTrashItem($trashItem->id); + $this->fail('Trash item was not removed after being recovered.'); + } catch (NotFoundException $e) { + // All well + } + } + + /** + * Test recovering a non existing trash item results in a NotFoundException. + */ + public function testRecoverThrowsNotFoundExceptionForNonExistingTrashItem() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + + $trashService->recover( + $this->getTrashItemDouble( + 12364, + 12345, + 12363 + ) + ); + } + + /** + * Test for the trash() method. + * + * @depends testTrash + */ + public function testNotFoundAliasAfterRemoveIt() + { + $this->expectException(NotFoundException::class); + + $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; + + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + $urlAliasService = $repository->getURLAliasService(); + $locationService = $repository->getLocationService(); + + // Double ->lookup() call because there where issue that one call was not enough to spot bug + $urlAliasService->lookup('/Media'); + $urlAliasService->lookup('/Media'); + + $mediaLocation = $locationService->loadLocationByRemoteId($mediaRemoteId); + $trashService->trash($mediaLocation); + + $urlAliasService->lookup('/Media'); + } + + /** + * Test for the recover() method. + * + * @depends testTrash + */ + public function testAliasesForRemovedItems() + { + $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; + + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + $urlAliasService = $repository->getURLAliasService(); + $locationService = $repository->getLocationService(); + + // Double ->lookup() call because there where issue that one call was not enough to spot bug + $urlAliasService->lookup('/Media'); + $trashedLocationAlias = $urlAliasService->lookup('/Media'); + + $mediaLocation = $locationService->loadLocationByRemoteId($mediaRemoteId); + $trashItem = $trashService->trash($mediaLocation); + $this->assertAliasNotExists($urlAliasService, '/Media'); + + $this->createNewContentInPlaceTrashedOne($repository, $mediaLocation->parentLocationId); + + $createdLocationAlias = $urlAliasService->lookup('/Media'); + + $this->assertNotEquals( + $trashedLocationAlias->destination, + $createdLocationAlias->destination, + 'Destination for /media url should changed' + ); + + $recoveredLocation = $trashService->recover($trashItem); + $recoveredLocationAlias = $urlAliasService->lookup('/Media2'); + $recoveredLocationAliasReverse = $urlAliasService->reverseLookup($recoveredLocation); + + $this->assertEquals($recoveredLocationAlias->destination, $recoveredLocationAliasReverse->destination); + + $this->assertNotEquals($recoveredLocationAliasReverse->destination, $trashedLocationAlias->destination); + $this->assertNotEquals($recoveredLocationAliasReverse->destination, $createdLocationAlias->destination); + } + + /** + * Test for the recover() method. + * + * @depends testRecover + */ + public function testRecoverDoesNotRestoreChildLocations() + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + $locationService = $repository->getLocationService(); + + $remoteIds = $this->createRemoteIdList(); + + // Unset remote ID of actually restored location + unset($remoteIds[array_search('3f6d92f8044aed134f32153517850f5a', $remoteIds)]); + + $trashItem = $this->createTrashItem(); + + $trashService->recover($trashItem); + + $this->assertGreaterThan( + 0, + count($remoteIds), + "There should be at least one 'Community' child location." + ); + + // None of the child locations will be available again + foreach ($remoteIds as $remoteId) { + try { + $locationService->loadLocationByRemoteId($remoteId); + $this->fail( + sprintf( + 'Location with remote ID "%s" unexpectedly restored.', + $remoteId + ) + ); + } catch (NotFoundException $e) { + // All well + } + } + + try { + $trashService->loadTrashItem($trashItem->id); + $this->fail('Trash item was not removed after being recovered.'); + } catch (NotFoundException $e) { + // All well + } + } + + /** + * Test for the recover() method. + * + * @depends testRecover + * + * @todo Fix naming + */ + public function testRecoverWithLocationCreateStructParameter() + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + $locationService = $repository->getLocationService(); + + $homeLocationId = $this->generateId('location', 2); + /* BEGIN: Use Case */ + // $homeLocationId is the ID of the "Home" location in an Ibexa + // demo installation + + $trashItem = $this->createTrashItem(); + + // Get the new parent location + $newParentLocation = $locationService->loadLocation($homeLocationId); + + // Recover location with new location + $location = $trashService->recover($trashItem, $newParentLocation); + /* END: Use Case */ + + $this->assertPropertiesCorrect( + [ + 'remoteId' => $trashItem->remoteId, + 'parentLocationId' => $homeLocationId, + // Not the full sub tree is restored + 'depth' => $newParentLocation->depth + 1, + 'hidden' => false, + 'invisible' => $trashItem->invisible, + 'pathString' => $newParentLocation->pathString . $this->parseId('location', $location->id) . '/', + 'priority' => 0, + 'sortField' => APILocation::SORT_FIELD_NAME, + 'sortOrder' => APILocation::SORT_ORDER_ASC, + ], + $location + ); + + try { + $trashService->loadTrashItem($trashItem->id); + $this->fail('Trash item was not removed after being recovered.'); + } catch (NotFoundException $e) { + // All well + } + } + + /** + * Test for the recover() method. + * + * @depends testRecover + */ + public function testRecoverIncrementsChildCountOnOriginalParent() + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation($this->generateId('location', 1)); + + $trashItem = $this->createTrashItem(); + + $this->refreshSearch($repository); + + /* BEGIN: Use Case */ + $childCount = $locationService->getLocationChildCount($location); + + // Recover location with new location + $trashService->recover($trashItem); + /* END: Use Case */ + + $this->refreshSearch($repository); + + $this->assertEquals( + $childCount + 1, + $locationService->getLocationChildCount($location) + ); + + try { + $trashService->loadTrashItem($trashItem->id); + $this->fail('Trash item was not removed after being recovered.'); + } catch (NotFoundException $e) { + // All well + } + } + + /** + * Test for the recover() method. + * + * @depends testRecoverWithLocationCreateStructParameter + */ + public function testRecoverWithLocationCreateStructParameterIncrementsChildCountOnNewParent() + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + $locationService = $repository->getLocationService(); + + $homeLocationId = $this->generateId('location', 2); + + $location = $locationService->loadLocation($homeLocationId); + + $childCount = $locationService->getLocationChildCount($location); + + /* BEGIN: Use Case */ + // $homeLocationId is the ID of the "Home" location in an Ibexa + // demo installation + + $trashItem = $this->createTrashItem(); + + // Get the new parent location + $newParentLocation = $locationService->loadLocation($homeLocationId); + + // Recover location with new location + $trashService->recover($trashItem, $newParentLocation); + /* END: Use Case */ + + $this->refreshSearch($repository); + + $this->assertEquals( + $childCount + 1, + $locationService->getLocationChildCount($location) + ); + + try { + $trashService->loadTrashItem($trashItem->id); + $this->fail('Trash item was not removed after being recovered.'); + } catch (NotFoundException $e) { + // All well + } + } + + /** + * Test recovering a location from trash to non existing location. + */ + public function testRecoverToNonExistingLocation() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation(44); + $trashItem = $trashService->trash($location); + + $newParentLocation = new Location( + [ + 'id' => 123456, + 'parentLocationId' => 123455, + ] + ); + $trashService->recover($trashItem, $newParentLocation); + } + + /** + * @dataProvider trashFiltersProvider + */ + public function testFindTrashItems( + array $filters, + int $expectedCount + ): void { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + + $this->trashDifferentContentItems(); + + $query = new Query(); + $filtersCount = count($filters); + + if ($filtersCount === 1) { + $query->filter = $filters[0]; + } elseif ($filtersCount > 1) { + $query->filter = new Criterion\LogicalAnd( + $filters + ); + } + + $searchResult = $trashService->findTrashItems($query); + + $this->assertEquals($expectedCount, $searchResult->totalCount); + } + + /** + * @throws \ErrorException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testFindTrashItemsSortedByDateTrashed(): void + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + $locationService = $repository->getLocationService(); + + $folder1 = $this->createFolder(['eng-GB' => 'Folder1'], 2); + $folder2 = $this->createFolder(['eng-GB' => 'Folder2'], 2); + + $firstTrashedItem = $trashService->trash( + $locationService->loadLocation($folder1->contentInfo->mainLocationId) + ); + $this->updateTrashedDate($firstTrashedItem->id, \time() - 100); + $latestTrashItem = $trashService->trash( + $locationService->loadLocation($folder2->contentInfo->mainLocationId) + ); + + $query = new Query(); + + // Load all trashed locations, sorted by trashed date ASC + $query->sortClauses = [new SortClause\Trash\DateTrashed(Query::SORT_ASC)]; + $searchResult = $trashService->findTrashItems($query); + self::assertEquals(2, $searchResult->totalCount); + self::assertEquals($firstTrashedItem->remoteId, $searchResult->items[0]->remoteId); + self::assertEquals($latestTrashItem->remoteId, $searchResult->items[1]->remoteId); + + // Load all trashed locations, sorted by trashed date DESC + $query->sortClauses = [new SortClause\Trash\DateTrashed(Query::SORT_DESC)]; + $searchResult = $trashService->findTrashItems($query); + self::assertEquals(2, $searchResult->totalCount); + self::assertEquals($latestTrashItem->remoteId, $searchResult->items[0]->remoteId); + self::assertEquals($firstTrashedItem->remoteId, $searchResult->items[1]->remoteId); + } + + /** + * @dataProvider trashSortClausesProvider + */ + public function testFindTrashItemsSort(array $sortClausesClasses): void + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + + $expectedCount = 2; + $ascQuery = new Query(); + $ascQuery->limit = $expectedCount; + $descQuery = clone $ascQuery; + + $this->trashDifferentContentItems(); + + foreach ($sortClausesClasses as $sortClauseClass) { + $ascQuery->sortClauses[] = new $sortClauseClass(Query::SORT_ASC); + } + + $ascResultsIds = []; + foreach ($trashService->findTrashItems($ascQuery) as $result) { + $ascResultsIds[] = $result->contentInfo->id; + } + + $this->assertGreaterThanOrEqual($expectedCount, count($ascResultsIds)); + + foreach ($sortClausesClasses as $sortClauseClass) { + $descQuery->sortClauses[] = new $sortClauseClass(Query::SORT_DESC); + } + + $descResultsIds = []; + foreach ($trashService->findTrashItems($descQuery) as $result) { + $descResultsIds[] = $result->contentInfo->id; + } + + $this->assertNotSame($descResultsIds, $ascResultsIds); + + krsort($descResultsIds); + $descResultsIds = array_values($descResultsIds); + + $this->assertSame($descResultsIds, $ascResultsIds); + } + + /** + * Test for the findTrashItems() method for it's result structure. + * + * @depends testTrash + */ + public function testFindTrashItemsLimits() + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + + $this->createTrashItem(); + + // Create a search query for all trashed items + $query = new Query(); + $query->limit = 2; + + // Load all trashed locations + $searchResult = $trashService->findTrashItems($query); + + $this->assertInstanceOf( + SearchResult::class, + $searchResult + ); + + // 4 trashed locations from the sub tree, but only 2 in results + $this->assertCount(2, $searchResult->items); + $this->assertEquals(4, $searchResult->count); + $this->assertEquals(4, $searchResult->totalCount); + } + + /** + * Test for the findTrashItems() method. + * + * @depends Ibexa\Tests\Integration\Core\Repository\TrashServiceTest::testFindTrashItems + */ + public function testFindTrashItemsLimitedAccess() + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + + /* BEGIN: Use Case */ + $this->createTrashItem(); + + // Create a search query for all trashed items + $query = new Query(); + $query->filter = new Criterion\LogicalAnd( + [ + new Criterion\Field('title', Criterion\Operator::LIKE, '*'), + ] + ); + + // Create a user in the Editor user group. + $user = $this->createUserVersion1(); + + // Set the Editor user as current user, these users have no access to Trash by default. + $repository->getPermissionResolver()->setCurrentUserReference($user); + + // Finding trash items with Field Criterion is not supported yet + $this->expectException(NotImplementedException::class); + $this->expectExceptionMessage( + 'Intentionally not implemented: No visitor available for: ' . + 'Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Field' + ); + // Load all trashed locations + $searchResult = $trashService->findTrashItems($query); + /* END: Use Case */ + + $this->assertInstanceOf( + SearchResult::class, + $searchResult + ); + + // 0 trashed locations found, though 4 exist + $this->assertEquals(0, $searchResult->count); + } + + /** + * Test Section Role Assignment Limitation against user/login. + */ + public function testFindTrashItemsSubtreeLimitation() + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + $contentTypeService = $repository->getContentTypeService(); + $trashService = $repository->getTrashService(); + + $folder1 = $this->createFolder(['eng-GB' => 'Folder1'], 2); + $folderLocationId = $folder1->contentInfo->mainLocationId; + $contentType = $contentTypeService->loadContentTypeByIdentifier('forum'); + $newContent = $contentService->newContentCreateStruct($contentType, 'eng-US'); + $newContent->setField('name', 'Media'); + $draftContent = $contentService->createContent($newContent, [new LocationCreateStruct(['parentLocationId' => $folderLocationId])]); + $published = $contentService->publishVersion($draftContent->versionInfo); + $location = $locationService->loadLocation($published->contentInfo->mainLocationId); + $trashService->trash($location); + + $this->createRoleWithPolicies('roleTrashCleaner', [ + [ + 'module' => 'content', + 'function' => 'cleantrash', + ], + [ + 'module' => 'content', + 'function' => 'read', + 'limitations' => [ + new SubtreeLimitation(['limitationValues' => [sprintf('/1/2/%d/', $folderLocationId)]]), + ], + ], + ]); + $user = $this->createCustomUserWithLogin( + 'user', + 'user@example.com', + 'roleTrashCleaners', + 'roleTrashCleaner' + ); + $repository->getPermissionResolver()->setCurrentUserReference($user); + + $query = new Query(); + + // Load all trashed locations + $searchResult = $trashService->findTrashItems($query); + /* END: Use Case */ + $this->assertInstanceOf( + SearchResult::class, + $searchResult + ); + + $this->assertEquals(1, count($searchResult->items)); + } + + /** + * Test for the emptyTrash() method. + * + * @depends testFindTrashItems + */ + public function testEmptyTrash() + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + $contentService = $repository->getContentService(); + + /* BEGIN: Use Case */ + $trashItem = $this->createTrashItem(); + + // Empty the trash + $trashService->emptyTrash(); + + // Create a search query for all trashed items + $query = new Query(); + + // Load all trashed locations, search result should be empty + $searchResult = $trashService->findTrashItems($query); + /* END: Use Case */ + + $this->assertEquals(0, $searchResult->count); + + // Try to load content + $this->expectException(NotFoundException::class); + $contentService->loadContent($trashItem->contentId); + } + + /** + * Test for the emptyTrash() method with user which has subtree limitations. + * + * @depends testFindTrashItems + */ + public function testEmptyTrashForUserWithSubtreeLimitation() + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + $contentService = $repository->getContentService(); + + /* BEGIN: Use Case */ + $trashItem = $this->createTrashItem(); + + $this->createRoleWithPolicies('roleTrashCleaner', [ + ['module' => 'content', 'function' => 'cleantrash'], + ['module' => 'content', 'function' => 'read'], + ]); + $user = $this->createCustomUserWithLogin( + 'user', + 'user@example.com', + 'roleTrashCleaners', + 'roleTrashCleaner', + new SubtreeLimitation(['limitationValues' => ['/1/2/']]) + ); + $repository->getPermissionResolver()->setCurrentUserReference($user); + + // Empty the trash + $trashService->emptyTrash(); + + // Create a search query for all trashed items + $query = new Query(); + + // Load all trashed locations, search result should be empty + $searchResult = $trashService->findTrashItems($query); + /* END: Use Case */ + + $this->assertEquals(0, $searchResult->totalCount); + + // Try to load content + $this->expectException(NotFoundException::class); + $contentService->loadContent($trashItem->contentId); + } + + /** + * Test for the deleteTrashItem() method. + * + * @depends testFindTrashItems + */ + public function testDeleteTrashItem() + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + $locationService = $repository->getLocationService(); + $contentService = $repository->getContentService(); + + $demoDesignLocationId = $this->generateId('location', 56); + /* BEGIN: Use Case */ + // $demoDesignLocationId is the ID of the "Demo Design" location in an Ibexa + // Publish demo installation + + $trashItem = $this->createTrashItem(); + + // Trash one more location + $trashService->trash( + $locationService->loadLocation($demoDesignLocationId) + ); + + // Empty the trash + $trashService->deleteTrashItem($trashItem); + + // Create a search query for all trashed items + $query = new Query(); + + // Load all trashed locations, should only contain the Demo Design location + $searchResult = $trashService->findTrashItems($query); + /* END: Use Case */ + + $foundIds = array_map( + static function ($trashItem) { + return $trashItem->id; + }, + $searchResult->items + ); + + $this->assertEquals(4, $searchResult->count); + $this->assertTrue( + in_array($demoDesignLocationId, $foundIds) + ); + + // Try to load Content + $this->expectException(NotFoundException::class); + $contentService->loadContent($trashItem->contentId); + } + + /** + * Test deleting a non existing trash item. + */ + public function testDeleteThrowsNotFoundExceptionForNonExistingTrashItem() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + + $trashService->deleteTrashItem($this->getTrashItemDouble( + 12364, + 12345, + 12363 + )); + } + + public function testTrashProperlyAssignsRemovedLocationContentMapToTrashItem(): void + { + $repository = $this->getRepository(); + $trashService = $repository->getTrashService(); + $locationService = $repository->getLocationService(); + + $folder1 = $this->createFolder(['eng-GB' => 'Folder1'], 2); + $folder2 = $this->createFolder(['eng-GB' => 'Folder2'], $folder1->contentInfo->getMainLocationId()); + $folder3 = $this->createFolder(['eng-GB' => 'Folder2'], $folder2->contentInfo->getMainLocationId()); + + $folderLocation = $locationService->loadLocation($folder1->contentInfo->getMainLocationId()); + + $trashItem = $trashService->trash($folderLocation); + $removedLocationContentMap = $trashItem->getRemovedLocationContentIdMap(); + + self::assertSame( + [ + $folderLocation->id => $folder1->id, + $folder2->contentInfo->getMainLocationId() => $folder2->id, + $folder3->contentInfo->getMainLocationId() => $folder3->id, + ], + $removedLocationContentMap, + ); + } + + /** + * @return array + */ + public function trashFiltersProvider(): array + { + return [ + [ + [], + 2, + ], + [ + [ + new Criterion\ContentTypeId(4), + ], + 1, + ], + [ + [ + new Criterion\ContentTypeId(999), + ], + 0, + ], + [ + [ + new Criterion\SectionId(2), + ], + 1, + ], + [ + [ + new Criterion\SectionId(999), + ], + 0, + ], + [ + [ + new Criterion\UserMetadata( + Criterion\UserMetadata::OWNER, + Criterion\Operator::EQ, + 14 + ), + ], + 1, + ], + [ + [ + new Criterion\UserMetadata( + Criterion\UserMetadata::OWNER, + Criterion\Operator::EQ, + 999 + ), + ], + 0, + ], + [ + [ + new Criterion\DateMetadata( + Criterion\DateMetadata::TRASHED, + Criterion\Operator::BETWEEN, + [time(), time() + 86400] + ), + ], + 2, + ], + [ + [ + new Criterion\DateMetadata( + Criterion\DateMetadata::TRASHED, + Criterion\Operator::BETWEEN, + [time() - 90, time()] + ), + ], + 0, + ], + [ + [ + new Criterion\ContentTypeId(1), + new Criterion\SectionId(1), + new Criterion\UserMetadata( + Criterion\UserMetadata::OWNER, + Criterion\Operator::EQ, + 14 + ), + new Criterion\DateMetadata( + Criterion\DateMetadata::TRASHED, + Criterion\Operator::BETWEEN, + [time(), time() + 86400] + ), + ], + 1, + ], + [ + [ + new Criterion\ContentTypeId(999), + new Criterion\SectionId(1), + new Criterion\UserMetadata( + Criterion\UserMetadata::OWNER, + Criterion\Operator::EQ, + 10 + ), + new Criterion\DateMetadata( + Criterion\DateMetadata::TRASHED, + Criterion\Operator::BETWEEN, + [time() - 90, time() + 90] + ), + ], + 0, + ], + [ + [ + new Criterion\MatchNone(), + ], + 0, + ], + [ + [ + new Criterion\MatchAll(), + ], + 2, + ], + [ + [ + new Criterion\LogicalNot(new Criterion\SectionId(2)), + ], + 1, + ], + [ + [ + new Criterion\LogicalOr([ + new Criterion\SectionId(1), + new Criterion\ContentTypeId(4), + ]), + ], + 2, + ], + [ + [ + new Criterion\LogicalAnd([ + new Criterion\SectionId(2), + new Criterion\ContentTypeId(4), + ]), + ], + 1, + ], + ]; + } + + public function trashSortClausesProvider(): array + { + return [ + [ + [ + SortClause\SectionName::class, + ], + ], + [ + [ + SortClause\ContentName::class, + ], + ], + [ + [ + SortClause\Trash\ContentTypeName::class, + ], + ], + [ + [ + SortClause\Trash\UserLogin::class, + ], + ], + [ + [ + SortClause\SectionName::class, + SortClause\ContentName::class, + SortClause\Trash\ContentTypeName::class, + SortClause\Trash\UserLogin::class, + ], + ], + ]; + } + + /** + * Returns an array with the remoteIds of all child locations of the + * <b>Community</b> location. It is stored in a local variable named + * <b>$remoteIds</b>. + * + * @return string[] + */ + private function createRemoteIdList() + { + $repository = $this->getRepository(); + + /* BEGIN: Inline */ + // remoteId of the "Community" location in an Ibexa demo installation + $mediaRemoteId = '75c715a51699d2d309a924eca6a95145'; + + // Load the location service + $locationService = $repository->getLocationService(); + + $remoteIds = []; + $children = $locationService->loadLocationChildren($locationService->loadLocationByRemoteId($mediaRemoteId)); + foreach ($children->locations as $child) { + $remoteIds[] = $child->remoteId; + foreach ($locationService->loadLocationChildren($child)->locations as $grandChild) { + $remoteIds[] = $grandChild->remoteId; + } + } + /* END: Inline */ + + return $remoteIds; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param int $parentLocationId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + protected function createNewContentInPlaceTrashedOne(Repository $repository, $parentLocationId) + { + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + $contentTypeService = $repository->getContentTypeService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier('forum'); + $newContent = $contentService->newContentCreateStruct($contentType, 'eng-US'); + $newContent->setField('name', 'Media'); + + $location = $locationService->newLocationCreateStruct($parentLocationId); + + $draftContent = $contentService->createContent($newContent, [$location]); + + return $contentService->publishVersion($draftContent->versionInfo); + } + + /** + * @param string $urlPath Url alias path + */ + private function assertAliasNotExists(URLAliasService $urlAliasService, $urlPath) + { + try { + $this->getRepository()->getURLAliasService()->lookup($urlPath); + $this->fail(sprintf('Alias [%s] should not exist', $urlPath)); + } catch (NotFoundException $e) { + $this->assertTrue(true); + } + } + + /** + * Get Test Double for TrashItem for exception testing and similar. + * + * @param int $trashId + * @param int $contentId + * @param int $parentLocationId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\TrashItem + */ + private function getTrashItemDouble(int $trashId, int $contentId = 44, int $parentLocationId = 2): APITrashItem + { + return new TrashItem([ + 'id' => $trashId, + 'parentLocationId' => $parentLocationId, + 'contentInfo' => new ContentInfo(['id' => $contentId]), + ]); + } + + private function trashDifferentContentItems(): void + { + $repository = $this->getRepository(false); + $permissionResolver = $repository->getPermissionResolver(); + $trashService = $repository->getTrashService(); + $locationService = $repository->getLocationService(); + $currentUser = $permissionResolver->getCurrentUserReference(); + + $folderContent = $this->createFolder(['eng-GB' => 'Folder'], 2); + + $newCreator = $this->createUserWithPolicies( + 'test_user', + [ + ['module' => 'content', 'function' => 'create'], + ['module' => 'content', 'function' => 'read'], + ['module' => 'content', 'function' => 'publish'], + ] + ); + + $permissionResolver->setCurrentUserReference($newCreator); + + $userContent = $this->createUser('test_user2', 'Some2', 'User2'); + + $permissionResolver->setCurrentUserReference($currentUser); + + $locationIds = [ + $userContent->contentInfo->mainLocationId, + $folderContent->contentInfo->mainLocationId, + ]; + + foreach ($locationIds as $locationId) { + $trashService->trash( + $locationService->loadLocation($locationId) + ); + } + } +} + +class_alias(TrashServiceTest::class, 'eZ\Publish\API\Repository\Tests\TrashServiceTest'); diff --git a/tests/integration/Core/Repository/URLAliasService/CustomUrlAliasForMultilingualContentTest.php b/tests/integration/Core/Repository/URLAliasService/CustomUrlAliasForMultilingualContentTest.php new file mode 100644 index 0000000000..b81f8fa9b4 --- /dev/null +++ b/tests/integration/Core/Repository/URLAliasService/CustomUrlAliasForMultilingualContentTest.php @@ -0,0 +1,61 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\URLAliasService; + +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; + +final class CustomUrlAliasForMultilingualContentTest extends BaseTest +{ + /** + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createUrlAlias + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testCreateCustomUrlAliasWithTheSamePathThrowsException(): void + { + $repository = $this->getRepository(); + $urlAliasService = $repository->getURLAliasService(); + $locationService = $repository->getLocationService(); + $language = 'ger-DE'; + + $names = [ + 'eng-GB' => 'Contact', + 'ger-DE' => 'Kontakt', + 'eng-US' => 'Contact', + ]; + $contactFolder = $this->createFolder( + $names, + 2, + null, + false // not always available, so the created content behaves the same as "article" + ); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage( + 'Argument \'$path\' is invalid: Path \'Contact\' already exists for the given context' + ); + // attempt to create custom alias for German translation while a system one + // for a different translation already exists + $urlAliasService->createUrlAlias( + $locationService->loadLocation( + $contactFolder->contentInfo->mainLocationId + ), + 'Contact', + $language, + true, // forwarding + true // always available + ); + } +} + +class_alias(CustomUrlAliasForMultilingualContentTest::class, 'eZ\Publish\API\Repository\Tests\URLAliasService\CustomUrlAliasForMultilingualContentTest'); diff --git a/tests/integration/Core/Repository/URLAliasService/UrlAliasLookupTest.php b/tests/integration/Core/Repository/URLAliasService/UrlAliasLookupTest.php new file mode 100644 index 0000000000..d61140be32 --- /dev/null +++ b/tests/integration/Core/Repository/URLAliasService/UrlAliasLookupTest.php @@ -0,0 +1,35 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\URLAliasService; + +use Ibexa\Tests\Integration\Core\RepositoryTestCase; + +/** + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService + */ +final class UrlAliasLookupTest extends RepositoryTestCase +{ + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + public function testLookup(): void + { + $urlAliasService = self::getUrlAliasService(); + $folder = $this->createFolder(['eng-GB' => 'Foo']); + $folderMainLocation = $folder->getVersionInfo()->getContentInfo()->getMainLocation(); + $urlAlias = $urlAliasService->lookup('/Foo'); + self::assertSame( + $folderMainLocation->id, + $urlAlias->destination + ); + $systemUrlAliasList = $urlAliasService->listLocationAliases($folderMainLocation, false); + self::assertCount(1, $systemUrlAliasList); + self::assertEquals($urlAlias, $systemUrlAliasList[0]); + } +} diff --git a/eZ/Publish/API/Repository/Tests/URLAliasServiceAuthorizationTest.php b/tests/integration/Core/Repository/URLAliasServiceAuthorizationTest.php similarity index 76% rename from eZ/Publish/API/Repository/Tests/URLAliasServiceAuthorizationTest.php rename to tests/integration/Core/Repository/URLAliasServiceAuthorizationTest.php index 8b3a7d4e05..c4bed8b4dd 100644 --- a/eZ/Publish/API/Repository/Tests/URLAliasServiceAuthorizationTest.php +++ b/tests/integration/Core/Repository/URLAliasServiceAuthorizationTest.php @@ -4,28 +4,28 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; class URLAliasServiceAuthorizationTest extends BaseTest { /** * Test for the createUrlAlias() method. * - * @covers \eZ\Publish\API\Repository\URLAliasService::createUrlAlias() - * @depends \eZ\Publish\API\Repository\Tests\URLAliasServiceTest::testCreateUrlAlias + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createUrlAlias() + * @depends Ibexa\Tests\Integration\Core\Repository\URLAliasServiceTest::testCreateUrlAlias */ public function testCreateUrlAliasThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $anonymousUserId = $this->generateId('user', 10); $parentLocationId = $this->generateId('location', 2); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. // $locationId is the ID of an existing location $userService = $repository->getUserService(); @@ -46,18 +46,18 @@ public function testCreateUrlAliasThrowsUnauthorizedException() /** * Test for the createGlobalUrlAlias() method. * - * @covers \eZ\Publish\API\Repository\URLAliasService::createGlobalUrlAlias() - * @depends \eZ\Publish\API\Repository\Tests\URLAliasServiceTest::testCreateGlobalUrlAlias + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createGlobalUrlAlias() + * @depends Ibexa\Tests\Integration\Core\Repository\URLAliasServiceTest::testCreateGlobalUrlAlias */ public function testCreateGlobalUrlAliasThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); $urlAliasService = $repository->getURLAliasService(); @@ -66,19 +66,19 @@ public function testCreateGlobalUrlAliasThrowsUnauthorizedException() $repository->getPermissionResolver()->setCurrentUserReference($anonymousUser); // This call will fail with an UnauthorizedException - $urlAliasService->createGlobalUrlAlias('module:content/search?SearchText=eZ', '/Home/My-New-Site', 'eng-US'); + $urlAliasService->createGlobalUrlAlias('module:content/search?SearchText=Ibexa', '/Home/My-New-Site', 'eng-US'); /* END: Use Case */ } /** * Test for the removeAliases() method. * - * @covers \eZ\Publish\API\Repository\URLAliasService::removeAliases() - * @depends \eZ\Publish\API\Repository\Tests\URLAliasServiceTest::testRemoveAliases + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::removeAliases() + * @depends Ibexa\Tests\Integration\Core\Repository\URLAliasServiceTest::testRemoveAliases */ public function testRemoveAliasesThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $anonymousUserId = $this->generateId('user', 10); @@ -91,7 +91,7 @@ public function testRemoveAliasesThrowsUnauthorizedException() /* BEGIN: Use Case */ // $someLocation contains a location with automatically generated // aliases assigned - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa $urlAliasService = $repository->getURLAliasService(); $userService = $repository->getUserService(); @@ -105,3 +105,5 @@ public function testRemoveAliasesThrowsUnauthorizedException() /* END: Use Case */ } } + +class_alias(URLAliasServiceAuthorizationTest::class, 'eZ\Publish\API\Repository\Tests\URLAliasServiceAuthorizationTest'); diff --git a/tests/integration/Core/Repository/URLAliasServiceTest.php b/tests/integration/Core/Repository/URLAliasServiceTest.php new file mode 100644 index 0000000000..c4284521f2 --- /dev/null +++ b/tests/integration/Core/Repository/URLAliasServiceTest.php @@ -0,0 +1,1768 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use Doctrine\DBAL\Connection; +use Exception; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; +use Ibexa\Tests\Integration\Core\Repository\Common\SlugConverter as TestSlugConverter; +use PDO; +use RuntimeException; + +/** + * Test case for operations in the URLAliasService using in memory storage. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService + * @group url-alias + */ +class URLAliasServiceTest extends BaseTest +{ + /** + * Tests that the required <b>LocationService::loadLocation()</b> + * at least returns an object, because this method is utilized in several + * tests. + */ + protected function setUp(): void + { + parent::setUp(); + + try { + // Load the LocationService + $locationService = $this->getRepository()->getLocationService(); + + $membersUserGroupLocationId = 12; + + // Load a location instance + $location = $locationService->loadLocation( + $membersUserGroupLocationId + ); + + if (false === is_object($location)) { + $this->markTestSkipped( + 'This test cannot be executed, because the utilized ' . + 'LocationService::loadLocation() does not ' . + 'return an object.' + ); + } + } catch (Exception $e) { + $this->markTestSkipped( + 'This test cannot be executed, because the utilized ' . + 'LocationService::loadLocation() failed with ' . + PHP_EOL . PHP_EOL . + $e->getTraceAsString() + ); + } + } + + /** + * Test for the createUrlAlias() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createUrlAlias() + */ + public function testCreateUrlAlias() + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + + /* BEGIN: Use Case */ + // $locationId is the ID of an existing location + + $locationService = $repository->getLocationService(); + $urlAliasService = $repository->getURLAliasService(); + + $location = $locationService->loadLocation($locationId); + + $createdUrlAlias = $urlAliasService->createUrlAlias($location, '/Home/My-New-Site', 'eng-US'); + /* END: Use Case */ + + $this->assertInstanceOf( + URLAlias::class, + $createdUrlAlias + ); + + return [$createdUrlAlias, $location->id]; + } + + /** + * Test for the createUrlAlias() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createUrlAlias + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testCreateSameAliasForDifferentLanguage() + { + $repository = $this->getRepository(); + $locationId = $this->generateId('location', 5); + $locationService = $repository->getLocationService(); + $urlAliasService = $repository->getURLAliasService(); + $location = $locationService->loadLocation($locationId); + + $urlAliasService->createUrlAlias($location, '/alias', 'eng-US'); + $updatedAlias = $urlAliasService->createUrlAlias($location, '/alias', 'eng-GB'); + + $this->assertPropertiesCorrect( + [ + 'languageCodes' => ['eng-US', 'eng-GB'], + ], + $updatedAlias + ); + } + + public function testLoad(): void + { + $repository = $this->getRepository(); + + $urlAliasService = $repository->getURLAliasService(); + + // Load URL alias for root location + $loadedUrlAlias = $urlAliasService->load('0-d41d8cd98f00b204e9800998ecf8427e'); + + $this->assertUrlAliasPropertiesSame( + [ + 'type' => URLAlias::LOCATION, + 'destination' => 2, + 'path' => '/', + 'languageCodes' => ['eng-US', 'eng-GB'], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ], + $loadedUrlAlias + ); + } + + /** + * @param array $testData + * + * @depends testCreateUrlAlias + */ + public function testCreateUrlAliasPropertyValues(array $testData) + { + [$createdUrlAlias, $locationId] = $testData; + + $this->assertNotNull($createdUrlAlias->id); + + $this->assertPropertiesCorrect( + [ + 'type' => URLAlias::LOCATION, + 'destination' => $locationId, + 'path' => '/Home/My-New-Site', + 'languageCodes' => ['eng-US'], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => false, + ], + $createdUrlAlias + ); + } + + /** + * Test for the createUrlAlias() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createUrlAlias($location, $path, $languageCode, $forwarding) + * @depends testCreateUrlAliasPropertyValues + */ + public function testCreateUrlAliasWithForwarding() + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + + /* BEGIN: Use Case */ + // $locationId is the ID of an existing location + + $locationService = $repository->getLocationService(); + $urlAliasService = $repository->getURLAliasService(); + + $location = $locationService->loadLocation($locationId); + + $createdUrlAlias = $urlAliasService->createUrlAlias($location, '/Home/My-New-Site', 'eng-US', true); + /* END: Use Case */ + + $this->assertInstanceOf( + URLAlias::class, + $createdUrlAlias + ); + + return [$createdUrlAlias, $location->id]; + } + + /** + * @param array $testData + * + * @depends testCreateUrlAliasWithForwarding + */ + public function testCreateUrlAliasPropertyValuesWithForwarding(array $testData) + { + [$createdUrlAlias, $locationId] = $testData; + + $this->assertNotNull($createdUrlAlias->id); + + $this->assertPropertiesCorrect( + [ + 'type' => URLAlias::LOCATION, + 'destination' => $locationId, + 'path' => '/Home/My-New-Site', + 'languageCodes' => ['eng-US'], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => true, + ], + $createdUrlAlias + ); + } + + /** + * Test for the createUrlAlias() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createUrlAlias($location, $path, $languageCode, $forwarding, $alwaysAvailable) + */ + public function testCreateUrlAliasWithAlwaysAvailable() + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + + /* BEGIN: Use Case */ + // $locationId is the ID of an existing location + + $locationService = $repository->getLocationService(); + $urlAliasService = $repository->getURLAliasService(); + + $location = $locationService->loadLocation($locationId); + + $createdUrlAlias = $urlAliasService->createUrlAlias($location, '/Home/My-New-Site', 'eng-US', false, true); + /* END: Use Case */ + + $this->assertInstanceOf( + URLAlias::class, + $createdUrlAlias + ); + + return [$createdUrlAlias, $location->id]; + } + + /** + * @param array $testData + * + * @depends testCreateUrlAliasWithAlwaysAvailable + */ + public function testCreateUrlAliasPropertyValuesWithAlwaysAvailable(array $testData) + { + [$createdUrlAlias, $locationId] = $testData; + + $this->assertNotNull($createdUrlAlias->id); + + $this->assertPropertiesCorrect( + [ + 'type' => URLAlias::LOCATION, + 'destination' => $locationId, + 'path' => '/Home/My-New-Site', + 'languageCodes' => ['eng-US'], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => false, + ], + $createdUrlAlias + ); + } + + /** + * Test for the createUrlAlias() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createUrlAlias() + */ + public function testCreateUrlAliasThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + + /* BEGIN: Use Case */ + // $locationId is the ID of an existing location + + $locationService = $repository->getLocationService(); + $urlAliasService = $repository->getURLAliasService(); + + $location = $locationService->loadLocation($locationId); + + // Throws InvalidArgumentException, since this path already exists for the + // language + $createdUrlAlias = $urlAliasService->createUrlAlias($location, '/Design/Plain-site', 'eng-US'); + /* END: Use Case */ + } + + /** + * Test for the createGlobalUrlAlias() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createGlobalUrlAlias() + */ + public function testCreateGlobalUrlAlias() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlAliasService = $repository->getURLAliasService(); + + $createdUrlAlias = $urlAliasService->createGlobalUrlAlias( + 'module:content/search?SearchText=Ibexa', + '/Home/My-New-Site', + 'eng-US' + ); + /* END: Use Case */ + + $this->assertInstanceOf( + URLAlias::class, + $createdUrlAlias + ); + + return $createdUrlAlias; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + * + * @depends testCreateGlobalUrlAlias + */ + public function testCreateGlobalUrlAliasPropertyValues(URLAlias $createdUrlAlias) + { + $this->assertNotNull($createdUrlAlias->id); + + $this->assertPropertiesCorrect( + [ + 'type' => URLAlias::RESOURCE, + 'destination' => 'content/search?SearchText=Ibexa', + 'path' => '/Home/My-New-Site', + 'languageCodes' => ['eng-US'], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => false, + ], + $createdUrlAlias + ); + } + + /** + * Test for the createGlobalUrlAlias() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createGlobalUrlAlias($resource, $path, $languageCode, $forward) + */ + public function testCreateGlobalUrlAliasWithForward() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlAliasService = $repository->getURLAliasService(); + + $createdUrlAlias = $urlAliasService->createGlobalUrlAlias( + 'module:content/search?SearchText=Ibexa', + '/Home/My-New-Site', + 'eng-US', + true + ); + /* END: Use Case */ + + $this->assertInstanceOf( + URLAlias::class, + $createdUrlAlias + ); + + return $createdUrlAlias; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + * + * @depends testCreateGlobalUrlAliasWithForward + */ + public function testCreateGlobalUrlAliasWithForwardPropertyValues(URLAlias $createdUrlAlias) + { + $this->assertNotNull($createdUrlAlias->id); + + $this->assertPropertiesCorrect( + [ + 'type' => URLAlias::RESOURCE, + 'destination' => 'content/search?SearchText=Ibexa', + 'path' => '/Home/My-New-Site', + 'languageCodes' => ['eng-US'], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => true, + ], + $createdUrlAlias + ); + } + + /** + * Test for the createGlobalUrlAlias() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createGlobalUrlAlias($resource, $path, $languageCode, $forwarding, $alwaysAvailable) + */ + public function testCreateGlobalUrlAliasWithAlwaysAvailable() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlAliasService = $repository->getURLAliasService(); + + $createdUrlAlias = $urlAliasService->createGlobalUrlAlias( + 'module:content/search?SearchText=Ibexa', + '/Home/My-New-Site', + 'eng-US', + false, + true + ); + /* END: Use Case */ + + $this->assertInstanceOf( + URLAlias::class, + $createdUrlAlias + ); + + return $createdUrlAlias; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + * + * @depends testCreateGlobalUrlAliasWithAlwaysAvailable + */ + public function testCreateGlobalUrlAliasWithAlwaysAvailablePropertyValues(URLAlias $createdUrlAlias) + { + $this->assertNotNull($createdUrlAlias->id); + + $this->assertPropertiesCorrect( + [ + 'type' => URLAlias::RESOURCE, + 'destination' => 'content/search?SearchText=Ibexa', + 'path' => '/Home/My-New-Site', + 'languageCodes' => ['eng-US'], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => false, + ], + $createdUrlAlias + ); + } + + /** + * Test for the createUrlAlias() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createGlobalUrlAlias($resource, $path, $languageCode, $forwarding, $alwaysAvailable) + */ + public function testCreateGlobalUrlAliasForLocation() + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + $locationService = $repository->getLocationService(); + $location = $locationService->loadLocation($locationId); + + /* BEGIN: Use Case */ + // $locationId is the ID of an existing location + + $urlAliasService = $repository->getURLAliasService(); + + $createdUrlAlias = $urlAliasService->createGlobalUrlAlias( + 'module:content/view/full/' . $locationId, + '/Home/My-New-Site-global', + 'eng-US', + false, + true + ); + /* END: Use Case */ + + $this->assertInstanceOf( + URLAlias::class, + $createdUrlAlias + ); + + return [$createdUrlAlias, $location->id]; + } + + /** + * Test for the createUrlAlias() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createGlobalUrlAlias($resource, $path, $languageCode, $forwarding, $alwaysAvailable) + */ + public function testCreateGlobalUrlAliasForLocationVariation() + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 5); + $locationService = $repository->getLocationService(); + $location = $locationService->loadLocation($locationId); + + /* BEGIN: Use Case */ + // $locationId is the ID of an existing location + + $urlAliasService = $repository->getURLAliasService(); + + $createdUrlAlias = $urlAliasService->createGlobalUrlAlias( + 'eznode:' . $locationId, + '/Home/My-New-Site-global', + 'eng-US', + false, + true + ); + /* END: Use Case */ + + $this->assertInstanceOf( + URLAlias::class, + $createdUrlAlias + ); + + return [$createdUrlAlias, $location->id]; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + * + * @depends testCreateGlobalUrlAliasForLocation + */ + public function testCreateGlobalUrlAliasForLocationPropertyValues($testData) + { + [$createdUrlAlias, $locationId] = $testData; + + $this->assertNotNull($createdUrlAlias->id); + + $this->assertPropertiesCorrect( + [ + 'type' => URLAlias::LOCATION, + 'destination' => $locationId, + 'path' => '/Home/My-New-Site-global', + 'languageCodes' => ['eng-US'], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => false, + ], + $createdUrlAlias + ); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLAlias + * + * @depends testCreateGlobalUrlAliasForLocationVariation + */ + public function testCreateGlobalUrlAliasForLocationVariationPropertyValues($testData) + { + $this->testCreateGlobalUrlAliasForLocationPropertyValues($testData); + } + + /** + * Test for the createGlobalUrlAlias() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createGlobalUrlAlias() + */ + public function testCreateGlobalUrlAliasThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlAliasService = $repository->getURLAliasService(); + + // Throws InvalidArgumentException, since this path already exists for the + // language + $createdUrlAlias = $urlAliasService->createGlobalUrlAlias( + 'module:content/search?SearchText=Ibexa', + '/Design/Plain-site', + 'eng-US' + ); + /* END: Use Case */ + } + + /** + * Test for the listLocationAliases() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::listLocationAliases() + */ + public function testListLocationAliases() + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 12); + + /* BEGIN: Use Case */ + // $locationId contains the ID of an existing Location + $urlAliasService = $repository->getURLAliasService(); + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation($locationId); + + // Create a custom URL alias for $location + $urlAliasService->createUrlAlias($location, '/My/Great-new-Site', 'eng-US'); + + // $loadedAliases will contain an array of custom URLAlias objects + $loadedAliases = $urlAliasService->listLocationAliases($location); + /* END: Use Case */ + + $this->assertIsArray($loadedAliases); + + // Only 1 non-history alias + $this->assertCount(1, $loadedAliases); + + return [$loadedAliases, $location]; + } + + /** + * @param array $testData + * + * @depends testListLocationAliases + */ + public function testListLocationAliasesLoadsCorrectly(array $testData) + { + [$loadedAliases, $location] = $testData; + + foreach ($loadedAliases as $loadedAlias) { + $this->assertInstanceOf( + URLAlias::class, + $loadedAlias + ); + $this->assertEquals( + $location->id, + $loadedAlias->destination + ); + } + } + + /** + * Test for the listLocationAliases() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::listLocationAliases($location, $custom, $languageCode) + */ + public function testListLocationAliasesWithCustomFilter() + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 12); + + /* BEGIN: Use Case */ + // $locationId contains the ID of an existing Location + $urlAliasService = $repository->getURLAliasService(); + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation($locationId); + + // Create a second URL alias for $location, this is a "custom" one + $urlAliasService->createUrlAlias($location, '/My/Great-new-Site', 'ger-DE'); + + // $loadedAliases will contain 1 aliases in eng-US only + $loadedAliases = $urlAliasService->listLocationAliases($location, false, 'eng-US'); + /* END: Use Case */ + + $this->assertIsArray($loadedAliases); + $this->assertCount(1, $loadedAliases); + } + + /** + * Test for the listLocationAliases() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::listLocationAliases($location, $custom) + */ + public function testListLocationAliasesWithLanguageCodeFilter() + { + $repository = $this->getRepository(); + + $locationId = $this->generateId('location', 12); + + /* BEGIN: Use Case */ + // $locationId contains the ID of an existing Location + $urlAliasService = $repository->getURLAliasService(); + $locationService = $repository->getLocationService(); + + $location = $locationService->loadLocation($locationId); + // Create a custom URL alias for $location + $urlAliasService->createUrlAlias($location, '/My/Great-new-Site', 'eng-US'); + + // $loadedAliases will contain only 1 of 3 aliases (custom in eng-US) + $loadedAliases = $urlAliasService->listLocationAliases($location, true, 'eng-US'); + /* END: Use Case */ + + $this->assertIsArray($loadedAliases); + $this->assertCount(1, $loadedAliases); + } + + /** + * Test for the listGlobalAliases() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::listGlobalAliases() + */ + public function testListGlobalAliases() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlAliasService = $repository->getURLAliasService(); + + // Create some global aliases + $this->createGlobalAliases(); + + // $loadedAliases will contain all 3 global aliases + $loadedAliases = $urlAliasService->listGlobalAliases(); + /* END: Use Case */ + + $this->assertIsArray($loadedAliases); + $this->assertCount(3, $loadedAliases); + } + + /** + * Creates 3 global aliases. + */ + private function createGlobalAliases() + { + $repository = $this->getRepository(); + $urlAliasService = $repository->getURLAliasService(); + + /* BEGIN: Inline */ + $urlAliasService->createGlobalUrlAlias( + 'module:content/search?SearchText=Ibexa', + '/My/Special-Support', + 'eng-US' + ); + $urlAliasService->createGlobalUrlAlias( + 'module:content/search?SearchText=Ibexa', + '/My/London-Office', + 'eng-GB' + ); + $urlAliasService->createGlobalUrlAlias( + 'module:content/search?SearchText=Sindelfingen', + '/My/Fancy-Site', + 'eng-US' + ); + /* END: Inline */ + } + + /** + * Test for the listGlobalAliases() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::listGlobalAliases($languageCode) + */ + public function testListGlobalAliasesWithLanguageFilter() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlAliasService = $repository->getURLAliasService(); + + // Create some global aliases + $this->createGlobalAliases(); + + // $loadedAliases will contain only 2 of 3 global aliases + $loadedAliases = $urlAliasService->listGlobalAliases('eng-US'); + /* END: Use Case */ + + $this->assertIsArray($loadedAliases); + $this->assertCount(2, $loadedAliases); + } + + /** + * Test for the listGlobalAliases() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::listGlobalAliases($languageCode, $offset) + */ + public function testListGlobalAliasesWithOffset() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlAliasService = $repository->getURLAliasService(); + + // Create some global aliases + $this->createGlobalAliases(); + + // $loadedAliases will contain only 2 of 3 global aliases + $loadedAliases = $urlAliasService->listGlobalAliases(null, 1); + /* END: Use Case */ + + $this->assertIsArray($loadedAliases); + $this->assertCount(2, $loadedAliases); + } + + /** + * Test for the listGlobalAliases() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::listGlobalAliases($languageCode, $offset, $limit) + */ + public function testListGlobalAliasesWithLimit() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlAliasService = $repository->getURLAliasService(); + + // Create some global aliases + $this->createGlobalAliases(); + + // $loadedAliases will contain only 1 of 3 global aliases + $loadedAliases = $urlAliasService->listGlobalAliases(null, 0, 1); + /* END: Use Case */ + + $this->assertIsArray($loadedAliases); + $this->assertCount(1, $loadedAliases); + } + + /** + * Test for the removeAliases() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::removeAliases() + */ + public function testRemoveAliases() + { + $repository = $this->getRepository(); + + $locationService = $repository->getLocationService(); + $someLocation = $locationService->loadLocation( + $this->generateId('location', 12) + ); + + /* BEGIN: Use Case */ + // $someLocation contains a location with automatically generated + // aliases assigned + $urlAliasService = $repository->getURLAliasService(); + + $initialAliases = $urlAliasService->listLocationAliases($someLocation); + + // Creates a custom alias for $someLocation + $urlAliasService->createUrlAlias( + $someLocation, + '/my/fancy/url/alias/sindelfingen', + 'eng-US' + ); + + $customAliases = $urlAliasService->listLocationAliases($someLocation); + + // The custom alias just created will be removed + // the automatic aliases stay in tact + $urlAliasService->removeAliases($customAliases); + /* END: Use Case */ + + $this->assertEquals( + $initialAliases, + $urlAliasService->listLocationAliases($someLocation) + ); + } + + /** + * Test for the removeAliases() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::removeAliases() + */ + public function testRemoveAliasesThrowsInvalidArgumentExceptionIfAutogeneratedAliasesAreToBeRemoved() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $locationService = $repository->getLocationService(); + $someLocation = $locationService->loadLocation( + $this->generateId('location', 12) + ); + + /* BEGIN: Use Case */ + // $someLocation contains a location with automatically generated + // aliases assigned + $urlAliasService = $repository->getURLAliasService(); + + $autogeneratedAliases = $urlAliasService->listLocationAliases($someLocation, false); + + // Throws an InvalidArgumentException, since autogeneratedAliases + // cannot be removed with this method + $urlAliasService->removeAliases($autogeneratedAliases); + /* END: Use Case */ + } + + /** + * Test for the lookUp() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::lookUp() + */ + public function testLookUp() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlAliasService = $repository->getURLAliasService(); + + $loadedAlias = $urlAliasService->lookup('/Setup2'); + /* END: Use Case */ + + $this->assertInstanceOf( + URLAlias::class, + $loadedAlias + ); + + return $loadedAlias; + } + + /** + * Test for the lookUp() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::lookUp($url, $languageCode) + */ + public function testLookUpWithLanguageFilter() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlAliasService = $repository->getURLAliasService(); + + // Create aliases in multiple languages + $this->createGlobalAliases(); + + $loadedAlias = $urlAliasService->lookup('/My/Special-Support', 'eng-US'); + /* END: Use Case */ + + $this->assertInstanceOf( + URLAlias::class, + $loadedAlias + ); + $this->assertEquals( + 'content/search?SearchText=Ibexa', + $loadedAlias->destination + ); + } + + /** + * Test for the lookUp() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::lookUp() + */ + public function testLookUpThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlAliasService = $repository->getURLAliasService(); + + // Throws NotFoundException + $urlAliasService->lookup('/non-existent-url'); + /* END: Use Case */ + } + + /** + * Test for the lookUp() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::lookUp($url, $languageCode) + */ + public function testLookUpThrowsNotFoundExceptionWithLanguageFilter() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlAliasService = $repository->getURLAliasService(); + + // Throws NotFoundException + $urlAliasService->lookup('/Contact-Us', 'ger-DE'); + /* END: Use Case */ + } + + /** + * Test for the lookUp() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::lookUp($url, $languageCode) + */ + public function testLookUpThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlAliasService = $repository->getURLAliasService(); + + // Throws InvalidArgumentException + $loadedAlias = $urlAliasService->lookup(str_repeat('/1', 99), 'ger-DE'); + /* END: Use Case */ + } + + /** + * Test for the lookUp() method after renaming parent which is a part of the lookup path. + * + * @see https://issues.ibexa.co/browse/EZP-28046 + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::lookUp + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::listLocationAliases + */ + public function testLookupOnRenamedParent() + { + $urlAliasService = $this->getRepository()->getURLAliasService(); + $locationService = $this->getRepository()->getLocationService(); + $contentTypeService = $this->getRepository()->getContentTypeService(); + $contentService = $this->getRepository()->getContentService(); + + // 1. Create new container object (e.g. Folder "My Folder"). + $folderContentType = $contentTypeService->loadContentTypeByIdentifier('folder'); + $folderCreateStruct = $contentService->newContentCreateStruct($folderContentType, 'eng-GB'); + $folderCreateStruct->setField('name', 'My-Folder'); + + $folderDraft = $contentService->createContent($folderCreateStruct, [ + $locationService->newLocationCreateStruct(2), + ]); + + $folder = $contentService->publishVersion($folderDraft->versionInfo); + + // 2. Create new object inside this container (e.g. article "My Article"). + $folderLocation = $locationService->loadLocation($folder->contentInfo->mainLocationId); + + $articleContentType = $contentTypeService->loadContentTypeByIdentifier('article'); + $articleCreateStruct = $contentService->newContentCreateStruct($articleContentType, 'eng-GB'); + $articleCreateStruct->setField('title', 'My Article'); + $article = $contentService->publishVersion( + $contentService->createContent($articleCreateStruct, [ + $locationService->newLocationCreateStruct($folderLocation->id), + ])->versionInfo + ); + $articleLocation = $locationService->loadLocation($article->contentInfo->mainLocationId); + + // 3. Navigate to both of them + $urlAliasService->lookup('/My-Folder'); + $urlAliasService->listLocationAliases($folderLocation, false); + $urlAliasService->lookup('/My-Folder/My-Article'); + $urlAliasService->listLocationAliases($articleLocation, false); + + // 4. Rename "My Folder" to "My Folder Modified". + $folderDraft = $contentService->createContentDraft($folder->contentInfo); + $folderUpdateStruct = $contentService->newContentUpdateStruct(); + $folderUpdateStruct->setField('name', 'My Folder Modified'); + + $contentService->publishVersion( + $contentService->updateContent($folderDraft->versionInfo, $folderUpdateStruct)->versionInfo + ); + + // 5. Navigate to "Article" + $urlAliasService->lookup('/My-Folder/My-Article'); + $aliases = $urlAliasService->listLocationAliases($articleLocation, false); + + $this->assertEquals('/My-Folder-Modified/My-Article', $aliases[0]->path); + } + + /** + * Test lookup on multilingual nested Locations returns proper UrlAlias Value. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testLookupOnMultilingualNestedLocations() + { + $urlAliasService = $this->getRepository()->getURLAliasService(); + $locationService = $this->getRepository()->getLocationService(); + + $topFolderNames = [ + 'eng-GB' => 'My folder Name', + 'ger-DE' => 'Ger folder Name', + 'eng-US' => 'My folder Name', + ]; + $nestedFolderNames = [ + 'eng-GB' => 'nested Folder name', + 'ger-DE' => 'Ger Nested folder Name', + 'eng-US' => 'nested Folder name', + ]; + $topFolderLocation = $locationService->loadLocation( + $this->createFolder($topFolderNames, 2)->contentInfo->mainLocationId + ); + $nestedFolderLocation = $locationService->loadLocation( + $this->createFolder( + $nestedFolderNames, + $topFolderLocation->id + )->contentInfo->mainLocationId + ); + $urlAlias = $urlAliasService->lookup('/My-Folder-Name/Nested-Folder-Name'); + self::assertPropertiesCorrect( + [ + 'destination' => $nestedFolderLocation->id, + 'path' => '/My-folder-Name/nested-Folder-name', + 'languageCodes' => ['eng-US', 'eng-GB'], + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ], + $urlAlias + ); + $urlAlias = $urlAliasService->lookup('/Ger-Folder-Name/Ger-Nested-Folder-Name'); + self::assertPropertiesCorrect( + [ + 'destination' => $nestedFolderLocation->id, + 'path' => '/Ger-folder-Name/Ger-Nested-folder-Name', + 'languageCodes' => ['ger-DE'], + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ], + $urlAlias + ); + + return [$topFolderLocation, $nestedFolderLocation]; + } + + /** + * Test refreshSystemUrlAliasesForLocation historizes and changes current URL alias after + * changing SlugConverter configuration. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \ErrorException + */ + public function testRefreshSystemUrlAliasesForLocationWithChangedSlugConverterConfiguration() + { + [$topFolderLocation, $nestedFolderLocation] = $this->testLookupOnMultilingualNestedLocations(); + + $urlAliasService = $this->getRepository(false)->getURLAliasService(); + + $this->changeSlugConverterConfiguration('transformation', 'urlalias_compat'); + $this->changeSlugConverterConfiguration('wordSeparatorName', 'underscore'); + + try { + $urlAliasService->refreshSystemUrlAliasesForLocation($topFolderLocation); + $urlAliasService->refreshSystemUrlAliasesForLocation($nestedFolderLocation); + + $urlAlias = $urlAliasService->lookup('/My-Folder-Name/Nested-Folder-Name'); + $this->assertUrlAliasPropertiesCorrect( + $nestedFolderLocation, + '/My-folder-Name/nested-Folder-name', + ['eng-US', 'eng-GB'], + true, + $urlAlias + ); + + $urlAlias = $urlAliasService->lookup('/my_folder_name/nested_folder_name'); + $this->assertUrlAliasPropertiesCorrect( + $nestedFolderLocation, + '/my_folder_name/nested_folder_name', + ['eng-US', 'eng-GB'], + false, + $urlAlias + ); + + $urlAlias = $urlAliasService->lookup('/Ger-Folder-Name/Ger-Nested-Folder-Name'); + $this->assertUrlAliasPropertiesCorrect( + $nestedFolderLocation, + '/Ger-folder-Name/Ger-Nested-folder-Name', + ['ger-DE'], + true, + $urlAlias + ); + + $urlAlias = $urlAliasService->lookup('/ger_folder_name/ger_nested_folder_name'); + $this->assertUrlAliasPropertiesCorrect( + $nestedFolderLocation, + '/ger_folder_name/ger_nested_folder_name', + ['ger-DE'], + false, + $urlAlias + ); + } finally { + // restore configuration + $this->changeSlugConverterConfiguration('transformation', 'urlalias'); + $this->changeSlugConverterConfiguration('wordSeparatorName', 'dash'); + } + } + + /** + * Test that URL aliases are refreshed after changing URL alias schema Field name of a content type. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testRefreshSystemUrlAliasesForContentsWithUpdatedContentTypes() + { + [$topFolderLocation, $nestedFolderLocation] = $this->testLookupOnMultilingualNestedLocations(); + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $topFolderLocation */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Location $nestedFolderLocation */ + // Default URL Alias schema is <short_name|name> which messes up this test, so: + $this->changeContentTypeUrlAliasSchema('folder', '<name>'); + + $urlAliasService = $this->getRepository(false)->getURLAliasService(); + + $this->updateContentField( + $topFolderLocation->getContentInfo(), + 'short_name', + ['eng-GB' => 'EN Short Name', 'ger-DE' => 'DE Short Name'] + ); + $this->updateContentField( + $nestedFolderLocation->getContentInfo(), + 'short_name', + ['eng-GB' => 'EN Nested Short Name', 'ger-DE' => 'DE Nested Short Name'] + ); + + $this->changeContentTypeUrlAliasSchema('folder', '<short_name>'); + + // sanity test, done after updating CT, because it does not update existing entries by design + $this->assertUrlIsCurrent('/My-folder-Name', $topFolderLocation->id); + $this->assertUrlIsCurrent('/Ger-folder-Name', $topFolderLocation->id); + $this->assertUrlIsCurrent('/My-folder-Name/nested-Folder-name', $nestedFolderLocation->id); + $this->assertUrlIsCurrent('/Ger-folder-Name/Ger-Nested-folder-Name', $nestedFolderLocation->id); + + // Call API being tested + $urlAliasService->refreshSystemUrlAliasesForLocation($topFolderLocation); + $urlAliasService->refreshSystemUrlAliasesForLocation($nestedFolderLocation); + + // check archived aliases + $this->assertUrlIsHistory('/My-folder-Name', $topFolderLocation->id); + $this->assertUrlIsHistory('/Ger-folder-Name', $topFolderLocation->id); + $this->assertUrlIsHistory('/My-folder-Name/nested-Folder-name', $nestedFolderLocation->id); + $this->assertUrlIsHistory('/Ger-folder-Name/Ger-Nested-folder-Name', $nestedFolderLocation->id); + + // check new current aliases + $this->assertUrlIsCurrent('/EN-Short-Name', $topFolderLocation->id); + $this->assertUrlIsCurrent('/DE-Short-Name', $topFolderLocation->id); + $this->assertUrlIsCurrent('/EN-Short-Name/EN-Nested-Short-Name', $nestedFolderLocation->id); + $this->assertUrlIsCurrent('/DE-Short-Name/DE-Nested-Short-Name', $nestedFolderLocation->id); + } + + /** + * Test that created non-latin aliases are non-empty and unique. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testCreateNonLatinNonEmptyUniqueAliases() + { + $repository = $this->getRepository(); + $urlAliasService = $repository->getURLAliasService(); + $locationService = $repository->getLocationService(); + + $folderNames = [ + 'eng-GB' => 'ひらがな', + ]; + + $folderLocation1 = $locationService->loadLocation( + $this->createFolder($folderNames, 2)->contentInfo->mainLocationId + ); + $urlAlias1 = $urlAliasService->lookup('/1'); + self::assertPropertiesCorrect( + [ + 'destination' => $folderLocation1->id, + 'path' => '/1', + 'languageCodes' => ['eng-GB'], + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ], + $urlAlias1 + ); + + $folderLocation2 = $locationService->loadLocation( + $this->createFolder($folderNames, 2)->contentInfo->mainLocationId + ); + $urlAlias2 = $urlAliasService->lookup('/2'); + self::assertPropertiesCorrect( + [ + 'destination' => $folderLocation2->id, + 'path' => '/2', + 'languageCodes' => ['eng-GB'], + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ], + $urlAlias2 + ); + } + + /** + * Test restoring missing current URL which has existing history. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Exception + */ + public function testRefreshSystemUrlAliasesForMissingUrlWithHistory() + { + $repository = $this->getRepository(); + $urlAliasService = $repository->getURLAliasService(); + $locationService = $repository->getLocationService(); + + $folderNames = ['eng-GB' => 'My folder Name']; + $folder = $this->createFolder($folderNames, 2); + $folderLocation = $locationService->loadLocation($folder->contentInfo->mainLocationId); + $nestedFolder = $this->createFolder(['eng-GB' => 'Nested folder'], $folderLocation->id); + $nestedFolderLocation = $locationService->loadLocation($nestedFolder->contentInfo->mainLocationId); + + $folder = $this->updateContentField( + $folder->contentInfo, + 'name', + ['eng-GB' => 'Updated Name'] + ); + // create more historical entries + $this->updateContentField( + $folder->contentInfo, + 'name', + ['eng-GB' => 'Updated Again Name'] + ); + // create historical entry for nested folder + $this->updateContentField( + $nestedFolder->contentInfo, + 'name', + ['eng-GB' => 'Updated Nested folder'] + ); + + // perform sanity check + $this->assertUrlIsHistory('/My-folder-Name', $folderLocation->id); + $this->assertUrlIsHistory('/Updated-Name', $folderLocation->id); + $this->assertUrlIsHistory('/My-folder-Name/Nested-folder', $nestedFolderLocation->id); + $this->assertUrlIsHistory('/Updated-Name/Nested-folder', $nestedFolderLocation->id); + $this->assertUrlIsHistory('/Updated-Again-Name/Nested-folder', $nestedFolderLocation->id); + + $this->assertUrlIsCurrent('/Updated-Again-Name', $folderLocation->id); + $this->assertUrlIsCurrent('/Updated-Again-Name/Updated-Nested-folder', $nestedFolderLocation->id); + + self::assertNotEmpty($urlAliasService->listLocationAliases($folderLocation, false)); + + // corrupt database by removing original entry, keeping its history + $this->performRawDatabaseOperation( + static function (Connection $connection) use ($folderLocation) { + $queryBuilder = $connection->createQueryBuilder(); + $expr = $queryBuilder->expr(); + $queryBuilder + ->delete('ezurlalias_ml') + ->where( + $expr->andX( + $expr->eq( + 'action', + $queryBuilder->createPositionalParameter( + "eznode:{$folderLocation->id}" + ) + ), + $expr->eq( + 'is_original', + $queryBuilder->createPositionalParameter(1) + ) + ) + ); + + return $queryBuilder->execute(); + } + ); + + // perform sanity check + self::assertEmpty($urlAliasService->listLocationAliases($folderLocation, false)); + + // Begin the actual test + $urlAliasService->refreshSystemUrlAliasesForLocation($folderLocation); + $urlAliasService->refreshSystemUrlAliasesForLocation($nestedFolderLocation); + + // make sure there is no corrupted data that could affect the test + $urlAliasService->deleteCorruptedUrlAliases(); + + // test if history was restored + $this->assertUrlIsHistory('/My-folder-Name', $folderLocation->id); + $this->assertUrlIsHistory('/Updated-Name', $folderLocation->id); + $this->assertUrlIsHistory('/My-folder-Name/Nested-folder', $nestedFolderLocation->id); + $this->assertUrlIsHistory('/Updated-Name/Nested-folder', $nestedFolderLocation->id); + $this->assertUrlIsHistory('/Updated-Again-Name/Nested-folder', $nestedFolderLocation->id); + + $this->assertUrlIsCurrent('/Updated-Again-Name', $folderLocation->id); + $this->assertUrlIsCurrent('/Updated-Again-Name/Updated-Nested-folder', $nestedFolderLocation->id); + } + + /** + * Test edge case when updated and archived entry gets moved to another subtree. + * + * @see https://issues.ibexa.co/browse/EZP-30004 + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Exception + */ + public function testRefreshSystemUrlAliasesForMovedLocation() + { + $repository = $this->getRepository(); + $urlAliasService = $repository->getURLAliasService(); + $locationService = $repository->getLocationService(); + + $folderNames = ['eng-GB' => 'folder']; + $folder = $this->createFolder($folderNames, 2); + $nestedFolder = $this->createFolder($folderNames, $folder->contentInfo->mainLocationId); + + $nestedFolder = $this->updateContentField( + $nestedFolder->contentInfo, + 'name', + ['eng-GB' => 'folder2'] + ); + + $nestedFolderLocation = $locationService->loadLocation( + $nestedFolder->contentInfo->mainLocationId + ); + $rootLocation = $locationService->loadLocation(2); + + $locationService->moveSubtree($nestedFolderLocation, $rootLocation); + // reload nested Location to get proper parent information + $nestedFolderLocation = $locationService->loadLocation($nestedFolderLocation->id); + + // corrupt database by breaking link to the original URL alias + $this->performRawDatabaseOperation( + static function (Connection $connection) use ($nestedFolderLocation) { + $queryBuilder = $connection->createQueryBuilder(); + $expr = $queryBuilder->expr(); + $queryBuilder + ->update('ezurlalias_ml') + ->set('link', $queryBuilder->createPositionalParameter(666, \PDO::PARAM_INT)) + ->where( + $expr->eq( + 'action', + $queryBuilder->createPositionalParameter( + "eznode:{$nestedFolderLocation->id}" + ) + ) + ) + ->andWhere( + $expr->eq( + 'is_original', + $queryBuilder->createPositionalParameter(0, \PDO::PARAM_INT) + ) + ) + ->andWhere( + $expr->eq('text', $queryBuilder->createPositionalParameter('folder')) + ) + ; + + return $queryBuilder->execute(); + } + ); + + $urlAliasService->refreshSystemUrlAliasesForLocation($nestedFolderLocation); + } + + public function testOverrideHistoryUrlAliasAtTheSameLocation(): void + { + $repository = $this->getRepository(); + $urlAliasService = $repository->getURLAliasService(); + $locationService = $repository->getLocationService(); + + $folderNames = ['eng-GB' => 'foo']; + $folder = $this->createFolder($folderNames, 2); + $destinationFolder = $this->createFolder($folderNames, 2); + + $location = $locationService->loadLocation($folder->contentInfo->mainLocationId); + $destinationFolderLocation = $locationService->loadLocation($destinationFolder->contentInfo->mainLocationId); + + $locationService->moveSubtree($location, $destinationFolderLocation); + + $urlAliasService->lookup('foo'); + $urlAliasService->lookup('foo2/foo'); + + $newFolder = ['eng-GB' => 'foo']; + $this->createFolder($newFolder, 2); + + $newAlias = $urlAliasService->lookup('foo'); + + self::assertFalse($newAlias->isHistory); + } + + /** + * Lookup given URL and check if it is archived and points to the given Location Id. + * + * @param string $lookupUrl + * @param int $expectedDestination Expected Location ID + */ + protected function assertUrlIsHistory($lookupUrl, $expectedDestination) + { + $this->assertLookupHistory(true, $expectedDestination, $lookupUrl); + } + + /** + * Lookup given URL and check if it is current (not archived) and points to the given Location Id. + * + * @param string $lookupUrl + * @param int $expectedDestination Expected Location ID + */ + protected function assertUrlIsCurrent($lookupUrl, $expectedDestination) + { + $this->assertLookupHistory(false, $expectedDestination, $lookupUrl); + } + + /** + * Lookup and URLAlias VO history and destination properties. + * + * @see assertUrlIsHistory + * @see assertUrlIsCurrent + * + * @param bool $expectedIsHistory + * @param int $expectedDestination Expected Location ID + * @param string $lookupUrl + */ + protected function assertLookupHistory($expectedIsHistory, $expectedDestination, $lookupUrl) + { + $urlAliasService = $this->getRepository(false)->getURLAliasService(); + + try { + $urlAlias = $urlAliasService->lookup($lookupUrl); + self::assertPropertiesCorrect( + [ + 'destination' => $expectedDestination, + 'path' => $lookupUrl, + 'isHistory' => $expectedIsHistory, + ], + $urlAlias + ); + } catch (InvalidArgumentException $e) { + self::fail("Failed to lookup {$lookupUrl}: $e"); + } catch (NotFoundException $e) { + self::fail("Failed to lookup {$lookupUrl}: $e"); + } + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param $fieldDefinitionIdentifier + * @param array $fieldValues + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + protected function updateContentField(ContentInfo $contentInfo, $fieldDefinitionIdentifier, array $fieldValues) + { + $contentService = $this->getRepository(false)->getContentService(); + + $contentUpdateStruct = $contentService->newContentUpdateStruct(); + foreach ($fieldValues as $languageCode => $fieldValue) { + $contentUpdateStruct->setField($fieldDefinitionIdentifier, $fieldValue, $languageCode); + } + $contentDraft = $contentService->updateContent( + $contentService->createContentDraft($contentInfo)->versionInfo, + $contentUpdateStruct + ); + + return $contentService->publishVersion($contentDraft->versionInfo); + } + + /** + * Test deleting corrupted URL aliases. + * + * Note: this test will not be needed once we introduce Improved Storage with Foreign keys support. + * + * Note: test depends on already broken URL aliases: eznode:59, eznode:59, eznode:60. + * + * @throws \ErrorException + */ + public function testDeleteCorruptedUrlAliases() + { + $repository = $this->getRepository(); + $urlAliasService = $repository->getURLAliasService(); + $connection = $this->getRawDatabaseConnection(); + + $query = $connection->createQueryBuilder()->select('*')->from('ezurlalias_ml'); + $originalRows = $query->execute()->fetchAll(PDO::FETCH_ASSOC); + + $expectedCount = count($originalRows); + $expectedCount += $this->insertBrokenUrlAliasTableFixtures($connection); + + // sanity check + $updatedRows = $query->execute()->fetchAll(PDO::FETCH_ASSOC); + self::assertCount($expectedCount, $updatedRows, 'Found unexpected number of new rows'); + + // BEGIN API use case + $urlAliasService->deleteCorruptedUrlAliases(); + // END API use case + + $updatedRows = $query->execute()->fetchAll(PDO::FETCH_ASSOC); + self::assertCount( + // API should also remove already broken pre-existing URL aliases + count($originalRows) - 4, + $updatedRows, + 'Number of rows after cleanup is not the same as the original number of rows' + ); + } + + /** + * Mutate \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter::class Service configuration. + * + * @param string $key + * @param string $value + * + * @throws \ErrorException + * @throws \Exception + */ + protected function changeSlugConverterConfiguration($key, $value) + { + $testSlugConverter = $this + ->getSetupFactory() + ->getServiceContainer() + ->getInnerContainer() + ->get(SlugConverter::class); + + if (!$testSlugConverter instanceof TestSlugConverter) { + throw new RuntimeException( + sprintf( + '%s: expected instance of %s, got %s', + __METHOD__, + TestSlugConverter::class, + get_class($testSlugConverter) + ) + ); + } + + $testSlugConverter->setConfigurationValue($key, $value); + } + + /** + * Update content type URL alias schema pattern. + * + * @param string $contentTypeIdentifier + * @param string $newUrlAliasSchema + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + protected function changeContentTypeUrlAliasSchema($contentTypeIdentifier, $newUrlAliasSchema) + { + $contentTypeService = $this->getRepository(false)->getContentTypeService(); + + $contentType = $contentTypeService->loadContentTypeByIdentifier($contentTypeIdentifier); + + $contentTypeDraft = $contentTypeService->createContentTypeDraft($contentType); + $contentTypeUpdateStruct = $contentTypeService->newContentTypeUpdateStruct(); + $contentTypeUpdateStruct->urlAliasSchema = $newUrlAliasSchema; + + $contentTypeService->updateContentTypeDraft($contentTypeDraft, $contentTypeUpdateStruct); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + } + + private function assertUrlAliasPropertiesSame(array $expectedValues, URLAlias $urlAlias): void + { + $this->assertSame( + $expectedValues, + [ + 'type' => $urlAlias->type, + 'destination' => $urlAlias->destination, + 'path' => $urlAlias->path, + 'languageCodes' => $urlAlias->languageCodes, + 'alwaysAvailable' => $urlAlias->alwaysAvailable, + 'isHistory' => $urlAlias->isHistory, + 'isCustom' => $urlAlias->isCustom, + 'forward' => $urlAlias->forward, + ] + ); + } + + private function assertUrlAliasPropertiesCorrect( + Location $expectedDestinationLocation, + $expectedPath, + array $expectedLanguageCodes, + $expectedIsHistory, + URLAlias $actualUrlAliasValue + ) { + self::assertPropertiesCorrect( + [ + 'destination' => $expectedDestinationLocation->id, + 'path' => $expectedPath, + // @todo uncomment after fixing EZP-27124 + //'languageCodes' => $expectedLanguageCodes, + 'isHistory' => $expectedIsHistory, + 'isCustom' => false, + 'forward' => false, + ], + $actualUrlAliasValue + ); + } + + /** + * Insert intentionally broken rows into ezurlalias_ml table to test cleanup API. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::deleteCorruptedUrlAliases + * + * @see testDeleteCorruptedUrlAliases + * + * @param \Doctrine\DBAL\Connection $connection + * + * @return int Number of new rows + */ + private function insertBrokenUrlAliasTableFixtures(Connection $connection) + { + $rows = [ + // link to non-existent location + [ + 'action' => 'eznode:9999', + 'action_type' => 'eznode', + 'alias_redirects' => 0, + 'id' => 9997, + 'is_alias' => 0, + 'is_original' => 1, + 'lang_mask' => 3, + 'link' => 9997, + 'parent' => 0, + 'text' => 'my-location', + 'text_md5' => '19d12b1b9994619cd8e90f00a6f5834e', + ], + // link to non-existent target URL alias (`link` column) + [ + 'action' => 'nop:', + 'action_type' => 'nop', + 'alias_redirects' => 0, + 'id' => 9998, + 'is_alias' => 1, + 'is_original' => 1, + 'lang_mask' => 2, + 'link' => 9995, + 'parent' => 0, + 'text' => 'my-alias1', + 'text_md5' => 'a29dd95ccf4c1bc7ebbd61086863b632', + ], + // link to non-existent parent URL alias + [ + 'action' => 'nop:', + 'action_type' => 'nop', + 'alias_redirects' => 0, + 'id' => 9999, + 'is_alias' => 0, + 'is_original' => 1, + 'lang_mask' => 3, + 'link' => 9999, + 'parent' => 9995, + 'text' => 'my-alias2', + 'text_md5' => 'e5dea18481e4f86857865d9fc94e4ce9', + ], + ]; + + $query = $connection->createQueryBuilder()->insert('ezurlalias_ml'); + + foreach ($rows as $row) { + foreach ($row as $columnName => $value) { + $row[$columnName] = $query->createNamedParameter($value); + } + $query->values($row); + $query->execute(); + } + + return count($rows); + } +} + +class_alias(URLAliasServiceTest::class, 'eZ\Publish\API\Repository\Tests\URLAliasServiceTest'); diff --git a/eZ/Publish/API/Repository/Tests/URLServiceAuthorizationTest.php b/tests/integration/Core/Repository/URLServiceAuthorizationTest.php similarity index 79% rename from eZ/Publish/API/Repository/Tests/URLServiceAuthorizationTest.php rename to tests/integration/Core/Repository/URLServiceAuthorizationTest.php index 8f1ed7509e..578e79a484 100644 --- a/eZ/Publish/API/Repository/Tests/URLServiceAuthorizationTest.php +++ b/tests/integration/Core/Repository/URLServiceAuthorizationTest.php @@ -4,28 +4,28 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\API\Repository\Values\URL\URLQuery; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\URL\URLQuery; class URLServiceAuthorizationTest extends BaseURLServiceTest { /** * Test for the findUrls() method. * - * @see \eZ\Publish\API\Repository\URLService::findUrls + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls */ public function testFindUrlsThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); @@ -44,18 +44,18 @@ public function testFindUrlsThrowsUnauthorizedException() /** * Test for the updateUrl() method. * - * @see \eZ\Publish\API\Repository\URLService::updateUrl + * @covers \Ibexa\Contracts\Core\Repository\URLService::updateUrl */ public function testUpdateUrlThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $anonymousUserId = $this->generateId('user', 10); $urlId = $this->generateId('url', 23); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); @@ -75,18 +75,18 @@ public function testUpdateUrlThrowsUnauthorizedException() /** * Test for the loadById() method. * - * @see \eZ\Publish\API\Repository\URLService::loadById + * @covers \Ibexa\Contracts\Core\Repository\URLService::loadById */ public function testLoadByIdThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $anonymousUserId = $this->generateId('user', 10); $urlId = $this->generateId('url', 23); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); @@ -102,11 +102,11 @@ public function testLoadByIdThrowsUnauthorizedException() /** * Test for the loadByUrl() method. * - * @see \eZ\Publish\API\Repository\URLService::loadById + * @covers \Ibexa\Contracts\Core\Repository\URLService::loadById */ public function testLoadByUrlThrowsUnauthorizedException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); @@ -114,7 +114,7 @@ public function testLoadByUrlThrowsUnauthorizedException() $url = '/content/view/sitemap/2'; /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. $userService = $repository->getUserService(); @@ -127,3 +127,5 @@ public function testLoadByUrlThrowsUnauthorizedException() /* END: Use Case */ } } + +class_alias(URLServiceAuthorizationTest::class, 'eZ\Publish\API\Repository\Tests\URLServiceAuthorizationTest'); diff --git a/tests/integration/Core/Repository/URLServiceTest.php b/tests/integration/Core/Repository/URLServiceTest.php new file mode 100644 index 0000000000..e9dc181353 --- /dev/null +++ b/tests/integration/Core/Repository/URLServiceTest.php @@ -0,0 +1,947 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use DateTime; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\URL\URL; +use Ibexa\Contracts\Core\Repository\Values\URL\URLQuery; +use Ibexa\Contracts\Core\Repository\Values\URL\URLUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\URL\UsageSearchResult; +use Ibexa\Core\Base\Exceptions\InvalidArgumentValue; + +/** + * Test case for operations in the UserService using in memory storage. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService + * @group integration + * @group url + */ +class URLServiceTest extends BaseURLServiceTest +{ + private const TOTAL_URLS_COUNT = 20; + + protected function setUp(): void + { + parent::setUp(); + + $urls = [ + [ + 'name' => 'Twitter', + 'url' => 'https://twitter.com/', + 'published' => true, + 'sectionId' => 1, + ], + [ + 'name' => 'Facebook', + 'url' => 'https://www.facebook.com/', + 'published' => true, + 'sectionId' => 1, + ], + [ + 'name' => 'Google', + 'url' => 'https://www.google.com/', + 'published' => true, + 'sectionId' => 1, + ], + [ + 'name' => 'Vimeo', + 'url' => 'https://vimeo.com/', + 'published' => true, + 'sectionId' => 1, + ], + [ + 'name' => 'Facebook Sharer', + 'url' => 'https://www.facebook.com/sharer.php', + 'published' => true, + 'sectionId' => 1, + ], + [ + 'name' => 'Youtube', + 'url' => 'https://www.youtube.com/', + 'published' => true, + 'sectionId' => 1, + ], + [ + 'name' => 'Googel support', + 'url' => 'https://support.google.com/chrome/answer/95647?hl=es', + 'published' => true, + 'sectionId' => 1, + ], + [ + 'name' => 'Instagram', + 'url' => 'https://instagram.com/', + 'published' => true, + 'sectionId' => 1, + ], + [ + 'name' => 'Discuz', + 'url' => 'https://www.discuz.net/forum.php', + 'published' => true, + 'sectionId' => 1, + ], + [ + 'name' => 'Google calendar', + 'url' => 'https://calendar.google.com/calendar/render', + 'published' => true, + 'sectionId' => 1, + ], + [ + 'name' => 'Wikipedia', + 'url' => 'https://www.wikipedia.org/', + 'published' => true, + 'sectionId' => 1, + ], + [ + 'name' => 'Google Analytics', + 'url' => 'https://www.google.com/analytics/', + 'published' => true, + 'sectionId' => 1, + ], + [ + 'name' => 'nazwa.pl', + 'url' => 'https://www.nazwa.pl/', + 'published' => true, + 'sectionId' => 1, + ], + [ + 'name' => 'Apache', + 'url' => 'https://www.apache.org/', + 'published' => true, + 'sectionId' => 2, + ], + [ + 'name' => 'Nginx', + 'url' => 'https://www.nginx.com/', + 'published' => true, + 'sectionId' => 2, + ], + [ + 'name' => 'Microsoft.com', + 'url' => 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', + 'published' => true, + 'sectionId' => 3, + ], + [ + 'name' => 'Dropbox', + 'url' => 'https://www.dropbox.com/', + 'published' => false, + 'sectionId' => 3, + ], + [ + 'name' => 'Google [DE]', + 'url' => 'https://www.google.de/', + 'published' => true, + 'sectionId' => 3, + ], + ]; + + $repository = $this->getRepository(); + + $parentLocationId = $this->generateId('location', 2); + + $contentService = $repository->getContentService(); + $locationService = $repository->getLocationService(); + + $contentType = $repository->getContentTypeService()->loadContentTypeByIdentifier('url'); + foreach ($urls as $data) { + $struct = $contentService->newContentCreateStruct($contentType, 'eng-GB'); + $struct->setField('name', $data['name']); + $struct->setField('url', $data['url']); + $struct->sectionId = $data['sectionId']; + + $location = $locationService->newLocationCreateStruct($parentLocationId); + + $draft = $contentService->createContent($struct, [$location]); + if ($data['published']) { + $contentService->publishVersion($draft->versionInfo); + } + } + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + */ + public function testFindUrls() + { + $expectedUrls = [ + 'https://www.apache.org/', + 'https://calendar.google.com/calendar/render', + 'https://www.dropbox.com/', + '/content/view/sitemap/2', + 'https://support.google.com/chrome/answer/95647?hl=es', + 'https://www.nazwa.pl/', + 'https://www.facebook.com/sharer.php', + 'https://www.wikipedia.org/', + 'https://www.google.de/', + 'https://www.google.com/', + 'https://www.nginx.com/', + '/content/view/tagcloud/2', + 'https://www.youtube.com/', + 'https://vimeo.com/', + 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', + 'https://twitter.com/', + 'https://www.google.com/analytics/', + 'https://www.facebook.com/', + 'https://www.discuz.net/forum.php', + 'https://instagram.com/', + ]; + + $query = new URLQuery(); + $query->filter = new Criterion\MatchAll(); + + $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + */ + public function testFindUrlsWithoutCounting() + { + $expectedUrls = [ + 'https://www.apache.org/', + 'https://calendar.google.com/calendar/render', + 'https://www.dropbox.com/', + '/content/view/sitemap/2', + 'https://support.google.com/chrome/answer/95647?hl=es', + 'https://www.nazwa.pl/', + 'https://www.facebook.com/sharer.php', + 'https://www.wikipedia.org/', + 'https://www.google.de/', + 'https://www.google.com/', + 'https://www.nginx.com/', + '/content/view/tagcloud/2', + 'https://www.youtube.com/', + 'https://vimeo.com/', + 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', + 'https://twitter.com/', + 'https://www.google.com/analytics/', + 'https://www.facebook.com/', + 'https://www.discuz.net/forum.php', + 'https://instagram.com/', + ]; + + $query = new URLQuery(); + $query->filter = new Criterion\MatchAll(); + $query->performCount = false; + + $this->doTestFindUrls($query, $expectedUrls, null); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + * @depends testFindUrls + */ + public function testFindUrlsUsingMatchNone() + { + $query = new URLQuery(); + $query->filter = new Criterion\MatchNone(); + + $this->doTestFindUrls($query, [], 0); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + * @depends testFindUrls + */ + public function testFindUrlsUsingPatternCriterion() + { + $expectedUrls = [ + 'https://www.google.de/', + 'https://www.google.com/', + 'https://support.google.com/chrome/answer/95647?hl=es', + 'https://calendar.google.com/calendar/render', + 'https://www.google.com/analytics/', + ]; + + $query = new URLQuery(); + $query->filter = new Criterion\Pattern('google'); + + $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + * @depends testFindUrls + */ + public function testFindUrlsUsingValidityCriterionValid() + { + $expectedUrls = [ + 'https://www.google.com/', + '/content/view/sitemap/2', + 'https://support.google.com/chrome/answer/95647?hl=es', + 'https://www.google.de/', + 'https://www.nginx.com/', + 'https://www.google.com/analytics/', + 'https://www.discuz.net/forum.php', + 'https://www.wikipedia.org/', + 'https://www.facebook.com/sharer.php', + 'https://twitter.com/', + 'https://www.nazwa.pl/', + 'https://instagram.com/', + 'https://www.apache.org/', + 'https://www.dropbox.com/', + 'https://www.facebook.com/', + 'https://www.youtube.com/', + 'https://calendar.google.com/calendar/render', + 'https://vimeo.com/', + 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', + ]; + + $query = new URLQuery(); + $query->filter = new Criterion\Validity(true); + + $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls + * @depends testFindUrls + */ + public function testFindUrlsUsingSectionIdCriterion(): void + { + $expectedUrls = [ + 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', + 'https://www.dropbox.com/', + 'https://www.google.de/', + ]; + + $query = new URLQuery(); + $query->filter = new Criterion\SectionId([3]); + + $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + * @depends testFindUrls + */ + public function testFindUrlsUsingSectionIdAndValidityCriterionValid(): void + { + $expectedUrls = [ + 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', + 'https://www.dropbox.com/', + 'https://www.google.de/', + ]; + + $query = new URLQuery(); + $query->filter = new Criterion\LogicalAnd([ + new Criterion\SectionId([3]), + new Criterion\Validity(true), + ]); + + $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls + * @depends testFindUrls + */ + public function testFindUrlsUsingSectionIdentifierCriterion(): void + { + $expectedUrls = [ + 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', + 'https://www.dropbox.com/', + 'https://www.google.de/', + ]; + + $query = new URLQuery(); + $query->filter = new Criterion\SectionIdentifier(['media']); + + $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + * @depends testFindUrls + */ + public function testFindUrlsUsingSectionIdentifierAndValidityCriterionValid(): void + { + $expectedUrls = [ + 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', + 'https://www.dropbox.com/', + 'https://www.google.de/', + 'https://www.apache.org/', + 'https://www.nginx.com/', + ]; + + $query = new URLQuery(); + $query->filter = new Criterion\LogicalAnd([ + new Criterion\SectionIdentifier(['media', 'users']), + new Criterion\Validity(true), + ]); + + $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + * @depends testFindUrls + */ + public function testFindUrlsUsingSectionIdentifierOrSectionIdCriterion(): void + { + $expectedUrls = [ + 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', + 'https://www.dropbox.com/', + 'https://www.google.de/', + 'https://www.apache.org/', + 'https://www.nginx.com/', + ]; + + $query = new URLQuery(); + $query->filter = new Criterion\LogicalOr([ + new Criterion\SectionIdentifier(['media']), + new Criterion\SectionId([2]), + ]); + + $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + * @depends testFindUrls + */ + public function testFindUrlsUsingValidityCriterionInvalid() + { + $expectedUrls = [ + '/content/view/tagcloud/2', + ]; + + $query = new URLQuery(); + $query->filter = new Criterion\Validity(false); + + $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + * @depends testFindUrls + */ + public function testFindUrlsUsingVisibleOnlyCriterion() + { + $expectedUrls = [ + 'https://vimeo.com/', + 'https://calendar.google.com/calendar/render', + 'https://www.facebook.com/', + 'https://www.google.com/', + 'https://www.google.com/analytics/', + 'https://www.facebook.com/sharer.php', + 'https://www.apache.org/', + 'https://www.nginx.com/', + 'https://www.wikipedia.org/', + 'https://www.youtube.com/', + 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', + 'https://www.google.de/', + 'https://instagram.com/', + 'https://www.nazwa.pl/', + '/content/view/tagcloud/2', + 'https://www.discuz.net/forum.php', + 'https://support.google.com/chrome/answer/95647?hl=es', + 'https://twitter.com/', + '/content/view/sitemap/2', + ]; + + $query = new URLQuery(); + $query->filter = new Criterion\VisibleOnly(); + + $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls)); + } + + /** + * @see https://issues.ibexa.co/browse/EZP-31059 + */ + public function testFindUrlsUsingVisibleOnlyCriterionReturnsUniqueItems(): void + { + $exampleUrl = 'https://ezplatform.com'; + + $this->createContentWithLink('A', $exampleUrl); + $this->createContentWithLink('B', $exampleUrl); + + $urlService = $this->getRepository()->getURLService(); + + $query = new URLQuery(); + $query->filter = new Criterion\VisibleOnly(); + $query->limit = -1; + + $results = $urlService->findUrls($query); + + $this->assertSearchResultItemsAreUnique($results); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + */ + public function testFindUrlsWithInvalidOffsetThrowsInvalidArgumentException() + { + $query = new URLQuery(); + $query->filter = new Criterion\MatchAll(); + $query->offset = 'invalid!'; + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlService = $repository->getURLService(); + + $this->expectException(InvalidArgumentValue::class); + $urlService->findUrls($query); + /* END: Use Case */ + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + */ + public function testFindUrlsWithInvalidLimitThrowsInvalidArgumentException() + { + $query = new URLQuery(); + $query->filter = new Criterion\MatchAll(); + $query->limit = 'invalid!'; + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlService = $repository->getURLService(); + + $this->expectException(InvalidArgumentValue::class); + $urlService->findUrls($query); + /* END: Use Case */ + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + * @depends testFindUrls + */ + public function testFindUrlsWithOffset() + { + $expectedUrls = [ + 'https://www.discuz.net/forum.php', + 'https://calendar.google.com/calendar/render', + 'https://www.wikipedia.org/', + 'https://www.google.com/analytics/', + 'https://www.nazwa.pl/', + 'https://www.apache.org/', + 'https://www.nginx.com/', + 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', + 'https://www.dropbox.com/', + 'https://www.google.de/', + ]; + + $query = new URLQuery(); + $query->filter = new Criterion\MatchAll(); + $query->offset = 10; + $query->sortClauses = [new SortClause\Id()]; + + $this->doTestFindUrls($query, $expectedUrls, self::TOTAL_URLS_COUNT); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + * @depends testFindUrls + */ + public function testFindUrlsWithOffsetAndLimit() + { + $expectedUrls = [ + 'https://www.discuz.net/forum.php', + 'https://calendar.google.com/calendar/render', + 'https://www.wikipedia.org/', + ]; + + $query = new URLQuery(); + $query->filter = new Criterion\MatchAll(); + $query->offset = 10; + $query->limit = 3; + $query->sortClauses = [new SortClause\Id()]; + + $this->doTestFindUrls($query, $expectedUrls, self::TOTAL_URLS_COUNT); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + * @depends testFindUrls + */ + public function testFindUrlsWithLimitZero() + { + $query = new URLQuery(); + $query->filter = new Criterion\MatchAll(); + $query->limit = 0; + + $this->doTestFindUrls($query, [], self::TOTAL_URLS_COUNT); + } + + /** + * Test for URLService::findUrls() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::findUrls() + * @depends testFindUrls + * @dataProvider dataProviderForFindUrlsWithSorting + */ + public function testFindUrlsWithSorting(SortClause $sortClause, array $expectedUrls) + { + $query = new URLQuery(); + $query->filter = new Criterion\MatchAll(); + $query->sortClauses = [$sortClause]; + + $this->doTestFindUrls($query, $expectedUrls, count($expectedUrls), false); + } + + public function dataProviderForFindUrlsWithSorting() + { + $urlsSortedById = [ + '/content/view/sitemap/2', + '/content/view/tagcloud/2', + 'https://twitter.com/', + 'https://www.facebook.com/', + 'https://www.google.com/', + 'https://vimeo.com/', + 'https://www.facebook.com/sharer.php', + 'https://www.youtube.com/', + 'https://support.google.com/chrome/answer/95647?hl=es', + 'https://instagram.com/', + 'https://www.discuz.net/forum.php', + 'https://calendar.google.com/calendar/render', + 'https://www.wikipedia.org/', + 'https://www.google.com/analytics/', + 'https://www.nazwa.pl/', + 'https://www.apache.org/', + 'https://www.nginx.com/', + 'https://windows.microsoft.com/en-US/internet-explorer/products/ie/home', + 'https://www.dropbox.com/', + 'https://www.google.de/', + ]; + + $urlsSortedByURL = $urlsSortedById; + sort($urlsSortedByURL); + + return [ + [new SortClause\Id(SortClause::SORT_ASC), $urlsSortedById], + [new SortClause\Id(SortClause::SORT_DESC), array_reverse($urlsSortedById)], + [new SortClause\URL(SortClause::SORT_ASC), $urlsSortedByURL], + [new SortClause\URL(SortClause::SORT_DESC), array_reverse($urlsSortedByURL)], + ]; + } + + /** + * Test for URLService::updateUrl() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::updateUrl() + */ + public function testUpdateUrl() + { + $repository = $this->getRepository(); + + $id = $this->generateId('url', 23); + + /* BEGIN: Use Case */ + $urlService = $repository->getURLService(); + + $urlBeforeUpdate = $urlService->loadById($id); + $updateStruct = $urlService->createUpdateStruct(); + $updateStruct->url = 'https://someurl.com/'; + + $urlAfterUpdate = $urlService->updateUrl($urlBeforeUpdate, $updateStruct); + /* END: Use Case */ + + $this->assertInstanceOf(URL::class, $urlAfterUpdate); + $this->assertPropertiesCorrect([ + 'id' => 23, + 'url' => 'https://someurl.com/', + // (!) URL status should be reset to valid nad never checked + 'isValid' => true, + 'lastChecked' => null, + 'created' => new DateTime('@1343140541'), + ], $urlAfterUpdate); + $this->assertGreaterThanOrEqual($urlBeforeUpdate->modified, $urlAfterUpdate->modified); + } + + /** + * Test for URLService::updateUrl() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::updateUrl() + */ + public function testUpdateUrlStatus() + { + $repository = $this->getRepository(); + + $id = $this->generateId('url', 23); + $checked = new DateTime('@' . time()); + + /* BEGIN: Use Case */ + $urlService = $repository->getURLService(); + + $urlBeforeUpdate = $urlService->loadById($id); + + $updateStruct = $urlService->createUpdateStruct(); + $updateStruct->isValid = false; + $updateStruct->lastChecked = $checked; + + $urlAfterUpdate = $urlService->updateUrl($urlBeforeUpdate, $updateStruct); + /* END: Use Case */ + + $this->assertInstanceOf(URL::class, $urlAfterUpdate); + $this->assertPropertiesCorrect([ + 'id' => $id, + 'url' => '/content/view/sitemap/2', + // (!) URL status should be reset to valid nad never checked + 'isValid' => false, + 'lastChecked' => $checked, + 'created' => new DateTime('@1343140541'), + ], $urlAfterUpdate); + $this->assertGreaterThanOrEqual($urlBeforeUpdate->modified, $urlAfterUpdate->modified); + } + + /** + * Test for URLService::updateUrl() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::updateUrl() + * @depends testUpdateUrl + */ + public function testUpdateUrlWithNonUniqueUrl() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $id = $this->generateId('url', 23); + + /* BEGIN: Use Case */ + $urlService = $repository->getURLService(); + + $urlBeforeUpdate = $urlService->loadById($id); + $updateStruct = $urlService->createUpdateStruct(); + $updateStruct->url = 'https://www.youtube.com/'; + + // This call will fail with a InvalidArgumentException + $urlService->updateUrl($urlBeforeUpdate, $updateStruct); + /* END: Use Case */ + } + + /** + * Test for URLService::loadById() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::loadById + */ + public function testLoadById() + { + $repository = $this->getRepository(); + + $id = $this->generateId('url', 23); + + /* BEGIN: Use Case */ + $urlService = $repository->getURLService(); + + $url = $urlService->loadById($id); + /* END: Use Case */ + + $this->assertInstanceOf(URL::class, $url); + $this->assertPropertiesCorrect([ + 'id' => 23, + 'url' => '/content/view/sitemap/2', + 'isValid' => true, + 'lastChecked' => null, + 'created' => new DateTime('@1343140541'), + 'modified' => new DateTime('@1343140541'), + ], $url); + } + + /** + * Test for URLService::loadById() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::loadById + * @depends testLoadById + */ + public function testLoadByIdThrowsNotFoundException() + { + $repository = $this->getRepository(); + + $nonExistentUrlId = $this->generateId('url', self::DB_INT_MAX); + /* BEGIN: Use Case */ + $urlService = $repository->getURLService(); + + $this->expectException(NotFoundException::class); + $urlService->loadById($nonExistentUrlId); + /* END: Use Case */ + } + + /** + * Test for URLService::loadByUrl() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::loadByUrl + */ + public function testLoadByUrl() + { + $repository = $this->getRepository(); + + $urlAddr = '/content/view/sitemap/2'; + /* BEGIN: Use Case */ + $urlService = $repository->getURLService(); + + $url = $urlService->loadByUrl($urlAddr); + + /* END: Use Case */ + + $this->assertInstanceOf(URL::class, $url); + $this->assertPropertiesCorrect([ + 'id' => 23, + 'url' => '/content/view/sitemap/2', + 'isValid' => true, + 'lastChecked' => null, + 'created' => new DateTime('@1343140541'), + 'modified' => new DateTime('@1343140541'), + ], $url); + } + + /** + * Test for URLService::loadByUrl() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::loadByUrl + * @depends testLoadByUrl + */ + public function testLoadByUrlThrowsNotFoundException() + { + $repository = $this->getRepository(); + + $nonExistentUrl = 'https://laravel.com/'; + /* BEGIN: Use Case */ + $urlService = $repository->getURLService(); + + $this->expectException(NotFoundException::class); + $urlService->loadByUrl($nonExistentUrl); + /* END: Use Case */ + } + + /** + * Test for URLService::createUpdateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLService::createUpdateStruct + * + * @return \Ibexa\Contracts\Core\Repository\Values\URL\URLUpdateStruct + */ + public function testCreateUpdateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlService = $repository->getURLService(); + $updateStruct = $urlService->createUpdateStruct(); + /* END: Use Case */ + + $this->assertInstanceOf(URLUpdateStruct::class, $updateStruct); + + return $updateStruct; + } + + /** + * Test for URLService::createUpdateStruct() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\URL\URLUpdateStruct $updateStruct + * @depends testCreateUpdateStruct + */ + public function testCreateUpdateStructValues(URLUpdateStruct $updateStruct) + { + $this->assertPropertiesCorrect([ + 'url' => null, + 'isValid' => null, + 'lastChecked' => null, + ], $updateStruct); + } + + /** + * Test for URLService::testFindUsages() method. + * + * @depends testLoadById + * @dataProvider dataProviderForFindUsages + */ + public function testFindUsages($urlId, $offset, $limit, array $expectedContentInfos, $expectedTotalCount = null) + { + $repository = $this->getRepository(); + + $id = $this->generateId('url', $urlId); + /* BEGIN: Use Case */ + $urlService = $repository->getURLService(); + + $loadedUrl = $urlService->loadById($id); + + $usagesSearchResults = $urlService->findUsages($loadedUrl, $offset, $limit); + /* END: Use Case */ + + $this->assertInstanceOf(UsageSearchResult::class, $usagesSearchResults); + $this->assertEquals($expectedTotalCount, $usagesSearchResults->totalCount); + $this->assertUsagesSearchResultItems($usagesSearchResults, $expectedContentInfos); + } + + public function dataProviderForFindUsages() + { + return [ + // findUsages($url, 0, -1) + [23, 0, -1, [54], 1], + // findUsages($url, 0, $limit) + [23, 0, 1, [54], 1], + ]; + } + + /** + * Test for URLService::testFindUsages() method. + * + * @depends testFindUsages + */ + public function testFindUsagesReturnsEmptySearchResults() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlService = $repository->getURLService(); + + $loadedUrl = $urlService->loadByUrl('https://www.dropbox.com/'); + + $usagesSearchResults = $urlService->findUsages($loadedUrl); + /* END: Use Case */ + + $this->assertInstanceOf(UsageSearchResult::class, $usagesSearchResults); + $this->assertPropertiesCorrect([ + 'totalCount' => 0, + 'items' => [], + ], $usagesSearchResults); + } +} + +class_alias(URLServiceTest::class, 'eZ\Publish\API\Repository\Tests\URLServiceTest'); diff --git a/tests/integration/Core/Repository/URLWildcardService/CriterionTest.php b/tests/integration/Core/Repository/URLWildcardService/CriterionTest.php index 0b194d5031..f44887ac4d 100644 --- a/tests/integration/Core/Repository/URLWildcardService/CriterionTest.php +++ b/tests/integration/Core/Repository/URLWildcardService/CriterionTest.php @@ -8,18 +8,17 @@ namespace Ibexa\Tests\Integration\Core\Repository\URLWildcardService; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion as CriterionURL; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; use Ibexa\Contracts\Core\Repository\Exceptions\InvalidCriterionArgumentException; use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\Criterion; use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\SearchResult; use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\URLWildcardQuery; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion as CriterionURL; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Test case criterion for URLWildcard. * - * @covers \eZ\Publish\API\Repository\URLWildcardService + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService * @group url-wildcard */ class CriterionTest extends BaseTest @@ -207,32 +206,6 @@ public function testTypeNoForward(): void $this->checkWildcardUrl($searchResult->items, $expectedWildcardUrls); } - public function testInvalidLimitThrowsInvalidArgumentException(): void - { - $query = new URLWildcardQuery(); - $query->filter = new Criterion\MatchAll(); - $query->limit = 'invalid!'; - - $repository = $this->getRepository(); - $urlWildcardService = $repository->getURLWildcardService(); - - $this->expectException(InvalidArgumentValue::class); - $urlWildcardService->findUrlWildcards($query); - } - - public function testInvalidOffsetThrowsInvalidArgumentException(): void - { - $query = new URLWildcardQuery(); - $query->filter = new Criterion\MatchAll(); - $query->offset = 'invalid!'; - - $repository = $this->getRepository(); - $urlWildcardService = $repository->getURLWildcardService(); - - $this->expectException(InvalidArgumentValue::class); - $urlWildcardService->findUrlWildcards($query); - } - public function testSourceAndDestination(): void { $search = 'test'; @@ -264,7 +237,7 @@ public function testLogicalInvalidCriterion(): void { $this->expectException(InvalidCriterionArgumentException::class); $this->expectExceptionMessage( - 'You provided eZ\Publish\API\Repository\Values\URL\Query\Criterion\VisibleOnly ' . + 'You provided Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion\VisibleOnly ' . "at index '1', but only instances of " . "'Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard\Query\Criterion' are accepted" ); @@ -276,7 +249,7 @@ public function testLogicalInvalidCriterion(): void } /** - * @param \eZ\Publish\API\Repository\Values\Content\URLWildcard[] $items + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard[] $items * @param string[] $expectedWildcardUrls */ protected function checkWildcardUrl(array $items, array $expectedWildcardUrls, bool $sourceUrl = true): void diff --git a/tests/integration/Core/Repository/URLWildcardServiceAuthorizationTest.php b/tests/integration/Core/Repository/URLWildcardServiceAuthorizationTest.php new file mode 100644 index 0000000000..dd0f093b90 --- /dev/null +++ b/tests/integration/Core/Repository/URLWildcardServiceAuthorizationTest.php @@ -0,0 +1,78 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; + +/** + * Test case for operations in the URLWildcardService. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService + * @group integration + * @group authorization + */ +class URLWildcardServiceAuthorizationTest extends BaseTest +{ + /** + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::create + * @depends Ibexa\Tests\Integration\Core\Repository\URLWildcardServiceTest::testCreate + */ + public function testCreateThrowsUnauthorizedException(): void + { + $this->expectException(UnauthorizedException::class); + + $repository = $this->getRepository(); + + $anonymousUserId = $this->generateId('user', 10); + /* BEGIN: Use Case */ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa + // Publish demo installation. + + $userService = $repository->getUserService(); + $urlWildcardService = $repository->getURLWildcardService(); + + $repository->getPermissionResolver()->setCurrentUserReference($userService->loadUser($anonymousUserId)); + + $this->expectException(UnauthorizedException::class); + $urlWildcardService->create('/articles/*', '/content/{1}'); + /* END: Use Case */ + } + + /** + * Test for the remove() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::remove() + * @depends Ibexa\Tests\Integration\Core\Repository\URLWildcardServiceTest::testRemove + */ + public function testRemoveThrowsUnauthorizedException() + { + $this->expectException(UnauthorizedException::class); + + $repository = $this->getRepository(); + + $anonymousUserId = $this->generateId('user', 10); + /* BEGIN: Use Case */ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa + // Publish demo installation. + $userService = $repository->getUserService(); + $urlWildcardService = $repository->getURLWildcardService(); + + // Create a new url wildcard + $urlWildcardId = $urlWildcardService->create('/articles/*', '/content/{1}')->id; + + $repository->getPermissionResolver()->setCurrentUserReference($userService->loadUser($anonymousUserId)); + + // Load newly created url wildcard + $urlWildcard = $urlWildcardService->load($urlWildcardId); + + $this->expectException(UnauthorizedException::class); + $urlWildcardService->remove($urlWildcard); + /* END: Use Case */ + } +} + +class_alias(URLWildcardServiceAuthorizationTest::class, 'eZ\Publish\API\Repository\Tests\URLWildcardServiceAuthorizationTest'); diff --git a/tests/integration/Core/Repository/URLWildcardServiceTest.php b/tests/integration/Core/Repository/URLWildcardServiceTest.php new file mode 100644 index 0000000000..2e6e6871ad --- /dev/null +++ b/tests/integration/Core/Repository/URLWildcardServiceTest.php @@ -0,0 +1,561 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcardTranslationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcardUpdateStruct; + +/** + * Test case for operations in the URLWildcardService. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService + * @group url-wildcard + */ +class URLWildcardServiceTest extends BaseTest +{ + /** + * Test for the create() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::create() + */ + public function testCreate() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // Create a new url wildcard + $urlWildcard = $urlWildcardService->create('/articles/*', '/content/{1}'); + /* END: Use Case */ + + $this->assertInstanceOf( + URLWildcard::class, + $urlWildcard + ); + + return $urlWildcard; + } + + /** + * Test for the create() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard $urlWildcard + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::create() + * @depends testCreate + */ + public function testCreateSetsIdPropertyOnURLWildcard(URLWildcard $urlWildcard) + { + $this->assertNotNull($urlWildcard->id); + } + + /** + * Test for the create() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard $urlWildcard + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::create() + * @depends testCreate + */ + public function testCreateSetsPropertiesOnURLWildcard(URLWildcard $urlWildcard) + { + $this->assertPropertiesCorrect( + [ + 'sourceUrl' => '/articles/*', + 'destinationUrl' => '/content/{1}', + 'forward' => false, + ], + $urlWildcard + ); + } + + /** + * Test for the create() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::create() + * @depends testCreate + */ + public function testCreateWithOptionalForwardParameter() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // Create a new url wildcard + $urlWildcard = $urlWildcardService->create('/articles/*', '/content/{1}', true); + /* END: Use Case */ + + $this->assertPropertiesCorrect( + [ + 'sourceUrl' => '/articles/*', + 'destinationUrl' => '/content/{1}', + 'forward' => true, + ], + $urlWildcard + ); + } + + /** + * Test for the create() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::create() + * @depends testCreate + */ + public function testCreateThrowsInvalidArgumentExceptionOnDuplicateSourceUrl() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // Create a new url wildcard + $urlWildcardService->create('/articles/*', '/content/{1}', true); + + // This call will fail with an InvalidArgumentException because the + // sourceUrl '/articles/*' already exists. + $urlWildcardService->create('/articles/*', '/content/data/{1}'); + /* END: Use Case */ + } + + /** + * Test for the create() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::create() + * @depends testCreate + */ + public function testCreateThrowsContentValidationExceptionWhenPatternsAndPlaceholdersNotMatch() + { + $this->expectException(ContentValidationException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // This call will fail with a ContentValidationException because the + // number of patterns '*' does not match the number of {\d} placeholders + $urlWildcardService->create('/articles/*', '/content/{1}/year{2}'); + /* END: Use Case */ + } + + /** + * Test for the create() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::create() + * @depends testCreate + */ + public function testCreateThrowsContentValidationExceptionWhenPlaceholdersNotValidNumberSequence() + { + $this->expectException(ContentValidationException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // This call will fail with a ContentValidationException because the + // number of patterns '*' does not match the number of {\d} placeholders + $urlWildcardService->create('/articles/*/*/*', '/content/{1}/year/{2}/{4}'); + /* END: Use Case */ + } + + /** + * Test for the load() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::load() + * @depends testCreate + */ + public function testLoad() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // Create a new url wildcard + $urlWildcardId = $urlWildcardService->create('/articles/*', '/content/{1}', true)->id; + + // Load newly created url wildcard + $urlWildcard = $urlWildcardService->load($urlWildcardId); + /* END: Use Case */ + + $this->assertInstanceOf( + URLWildcard::class, + $urlWildcard + ); + + return $urlWildcard; + } + + /** + * Test for the load() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard $urlWildcard + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::load() + * @depends testLoad + */ + public function testLoadSetsPropertiesOnURLWildcard(URLWildcard $urlWildcard) + { + $this->assertPropertiesCorrect( + [ + 'sourceUrl' => '/articles/*', + 'destinationUrl' => '/content/{1}', + 'forward' => true, + ], + $urlWildcard + ); + } + + /** + * Test for the load() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard $urlWildcard + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::load() + * @depends testLoad + */ + public function testLoadThrowsNotFoundException(URLWildcard $urlWildcard) + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // This call will fail with a NotFoundException + $urlWildcardService->load(42); + /* END: Use Case */ + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::update + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testUpdate(): void + { + $repository = $this->getRepository(); + + $urlWildcardService = $repository->getURLWildcardService(); + + $urlWildcard = $urlWildcardService->create( + '/articles/*', + '/content/{1}', + true + ); + + $updateStruct = new URLWildcardUpdateStruct(); + $updateStruct->sourceUrl = '/articles/new/*'; + $updateStruct->destinationUrl = '/content/new/*'; + $updateStruct->forward = false; + + $urlWildcardService->update($urlWildcard, $updateStruct); + + $urlWildcardUpdated = $urlWildcardService->load($urlWildcard->id); + + $this->assertEquals( + [ + $urlWildcard->id, + $updateStruct->sourceUrl, + $updateStruct->destinationUrl, + $updateStruct->forward, + ], + [ + $urlWildcardUpdated->id, + $urlWildcardUpdated->sourceUrl, + $urlWildcardUpdated->destinationUrl, + $urlWildcardUpdated->forward, + ] + ); + } + + /** + * Test for the remove() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::remove() + * @depends testLoad + */ + public function testRemove() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // Create a new url wildcard + $urlWildcard = $urlWildcardService->create('/articles/*', '/content/{1}', true); + + // Store wildcard url for later reuse + $urlWildcardId = $urlWildcard->id; + + // Remove the newly created url wildcard + $urlWildcardService->remove($urlWildcard); + + // This call will fail with a NotFoundException + $urlWildcardService->load($urlWildcardId); + /* END: Use Case */ + } + + /** + * Test for the loadAll() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::loadAll() + * @depends testCreate + */ + public function testLoadAll() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // Create new url wildcards + $urlWildcardOne = $urlWildcardService->create('/articles/*', '/content/{1}', true); + $urlWildcardTwo = $urlWildcardService->create('/news/*', '/content/{1}', true); + + // Load all available url wildcards + $allUrlWildcards = $urlWildcardService->loadAll(); + /* END: Use Case */ + + $this->assertEquals( + [ + $urlWildcardOne, + $urlWildcardTwo, + ], + $allUrlWildcards + ); + } + + /** + * Test for the loadAll() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::loadAll() + * @depends testLoadAll + */ + public function testLoadAllWithOffsetParameter() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // Create new url wildcards + $urlWildcardOne = $urlWildcardService->create('/articles/*', '/content/{1}', true); + $urlWildcardTwo = $urlWildcardService->create('/news/*', '/content/{1}', true); + + // Load all available url wildcards + $allUrlWildcards = $urlWildcardService->loadAll(1); + /* END: Use Case */ + + $this->assertEquals([$urlWildcardTwo], $allUrlWildcards); + } + + /** + * Test for the loadAll() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::loadAll() + * @depends testLoadAll + */ + public function testLoadAllWithOffsetAndLimitParameter() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // Create new url wildcards + $urlWildcardOne = $urlWildcardService->create('/articles/*', '/content/{1}'); + $urlWildcardTwo = $urlWildcardService->create('/news/*', '/content/{1}'); + + // Load all available url wildcards + $allUrlWildcards = $urlWildcardService->loadAll(0, 1); + /* END: Use Case */ + + $this->assertEquals([$urlWildcardOne], $allUrlWildcards); + } + + /** + * Test for the loadAll() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::loadAll() + * @depends testLoadAll + */ + public function testLoadAllReturnsEmptyArrayByDefault() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // Load all available url wildcards + $allUrlWildcards = $urlWildcardService->loadAll(); + /* END: Use Case */ + + $this->assertSame([], $allUrlWildcards); + } + + /** + * Test for the translate() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcardTranslationResult + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::translate() + * @depends testCreate + */ + public function testTranslate() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // Create a new url wildcard + $urlWildcardService->create('/articles/*', '/content/{1}'); + + // Translate a given url + $result = $urlWildcardService->translate('/articles/2012/05/sindelfingen'); + /* END: Use Case */ + + $this->assertInstanceOf( + URLWildcardTranslationResult::class, + $result + ); + + return $result; + } + + /** + * Test for the translate() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\URLWildcardTranslationResult $result + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::translate() + * @depends testTranslate + */ + public function testTranslateSetsPropertiesOnTranslationResult(URLWildcardTranslationResult $result) + { + $this->assertPropertiesCorrect( + [ + 'uri' => '/content/2012/05/sindelfingen', + 'forward' => false, + ], + $result + ); + } + + /** + * Test for the translate() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::translate() + * @depends testTranslate + */ + public function testTranslateWithForwardSetToTrue() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // Create a new url wildcard + $urlWildcardService->create('/articles/*/05/*', '/content/{2}/year/{1}', true); + + // Translate a given url + $result = $urlWildcardService->translate('/articles/2012/05/sindelfingen'); + /* END: Use Case */ + + $this->assertPropertiesCorrect( + [ + 'uri' => '/content/sindelfingen/year/2012', + 'forward' => true, + ], + $result + ); + } + + /** + * Test for the translate() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::translate() + * @depends testTranslate + */ + public function testTranslateReturnsLongestMatchingWildcard() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // Create new url wildcards + $urlWildcardService->create('/articles/*/05/*', '/content/{2}/year/{1}'); + $urlWildcardService->create('/articles/*/05/sindelfingen/*', '/content/{2}/bar/{1}'); + + // Translate a given url + $result = $urlWildcardService->translate('/articles/2012/05/sindelfingen/42'); + /* END: Use Case */ + + $this->assertEquals('/content/42/bar/2012', $result->uri); + } + + /** + * Test for the translate() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::translate() + * @depends testTranslate + */ + public function testTranslateThrowsNotFoundExceptionWhenNotAliasOrWildcardMatches() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $urlWildcardService = $repository->getURLWildcardService(); + + // This call will fail with a NotFoundException because no wildcard or + // url alias matches against the given url. + $urlWildcardService->translate('/sindelfingen'); + /* END: Use Case */ + } + + public function testCountAllReturnsZeroByDefault(): void + { + $repository = $this->getRepository(); + $urlWildcardService = $repository->getURLWildcardService(); + + $this->assertSame(0, $urlWildcardService->countAll()); + } + + public function testCountAll(): void + { + $repository = $this->getRepository(); + $urlWildcardService = $repository->getURLWildcardService(); + + $urlWildcardService->create('/articles/*', '/content/{1}'); + + $this->assertSame(1, $urlWildcardService->countAll()); + } +} + +class_alias(URLWildcardServiceTest::class, 'eZ\Publish\API\Repository\Tests\URLWildcardServiceTest'); diff --git a/tests/integration/Core/Repository/UserPreferenceServiceTest.php b/tests/integration/Core/Repository/UserPreferenceServiceTest.php new file mode 100644 index 0000000000..808c9ba2b2 --- /dev/null +++ b/tests/integration/Core/Repository/UserPreferenceServiceTest.php @@ -0,0 +1,147 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository; + +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\UserPreference\UserPreference; +use Ibexa\Contracts\Core\Repository\Values\UserPreference\UserPreferenceList; +use Ibexa\Contracts\Core\Repository\Values\UserPreference\UserPreferenceSetStruct; + +/** + * Test case for the UserPreferenceService. + * + * @covers \Ibexa\Contracts\Core\Repository\UserPreferenceService + */ +class UserPreferenceServiceTest extends BaseTest +{ + /** + * @covers \Ibexa\Contracts\Core\Repository\UserPreferenceService::loadUserPreferences() + */ + public function testLoadUserPreferences() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $userPreferenceService = $repository->getUserPreferenceService(); + $userPreferenceList = $userPreferenceService->loadUserPreferences(0, 25); + /* END: Use Case */ + + $this->assertInstanceOf(UserPreferenceList::class, $userPreferenceList); + $this->assertIsArray($userPreferenceList->items); + $this->assertIsInt($userPreferenceList->totalCount); + $this->assertEquals(5, $userPreferenceList->totalCount); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserPreferenceService::getUserPreference() + */ + public function testGetUserPreference() + { + $repository = $this->getRepository(); + + $userPreferenceName = 'setting_1'; + + /* BEGIN: Use Case */ + $userPreferenceService = $repository->getUserPreferenceService(); + // $userPreferenceName is the name of an existing preference + $userPreference = $userPreferenceService->getUserPreference($userPreferenceName); + /* END: Use Case */ + + $this->assertInstanceOf(UserPreference::class, $userPreference); + $this->assertEquals($userPreferenceName, $userPreference->name); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserPreferenceService::setUserPreference() + * @depends testGetUserPreference + */ + public function testSetUserPreference() + { + $repository = $this->getRepository(); + + $userPreferenceName = 'timezone'; + + /* BEGIN: Use Case */ + $userPreferenceService = $repository->getUserPreferenceService(); + + $setStruct = new UserPreferenceSetStruct([ + 'name' => $userPreferenceName, + 'value' => 'America/New_York', + ]); + + $userPreferenceService->setUserPreference([$setStruct]); + $userPreference = $userPreferenceService->getUserPreference($userPreferenceName); + /* END: Use Case */ + + $this->assertInstanceOf(UserPreference::class, $userPreference); + $this->assertEquals($userPreferenceName, $userPreference->name); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserPreferenceService::setUserPreference() + * @depends testSetUserPreference + */ + public function testSetUserPreferenceThrowsInvalidArgumentExceptionOnInvalidValue() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $userPreferenceService = $repository->getUserPreferenceService(); + + $setStruct = new UserPreferenceSetStruct([ + 'name' => 'setting', + 'value' => new \stdClass(), + ]); + + // This call will fail because value is not specified + $userPreferenceService->setUserPreference([$setStruct]); + /* END: Use Case */ + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserPreferenceService::setUserPreference() + * @depends testSetUserPreference + */ + public function testSetUserPreferenceThrowsInvalidArgumentExceptionOnEmptyName() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $userPreferenceService = $repository->getUserPreferenceService(); + + $setStruct = new UserPreferenceSetStruct([ + 'value' => 'value', + ]); + + // This call will fail because value is not specified + $userPreferenceService->setUserPreference([$setStruct]); + /* END: Use Case */ + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserPreferenceService::getUserPreferenceCount() + */ + public function testGetUserPreferenceCount() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $userPreferenceService = $repository->getUserPreferenceService(); + $userPreferenceCount = $userPreferenceService->getUserPreferenceCount(); + /* END: Use Case */ + + $this->assertEquals(5, $userPreferenceCount); + } +} + +class_alias(UserPreferenceServiceTest::class, 'eZ\Publish\API\Repository\Tests\UserPreferenceServiceTest'); diff --git a/eZ/Publish/API/Repository/Tests/UserServiceAuthorizationTest.php b/tests/integration/Core/Repository/UserServiceAuthorizationTest.php similarity index 84% rename from eZ/Publish/API/Repository/Tests/UserServiceAuthorizationTest.php rename to tests/integration/Core/Repository/UserServiceAuthorizationTest.php index a40dec8bbb..865301f4e3 100644 --- a/eZ/Publish/API/Repository/Tests/UserServiceAuthorizationTest.php +++ b/tests/integration/Core/Repository/UserServiceAuthorizationTest.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Integration\Core\Repository; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; /** * Test case for operations in the UserService using in memory storage. * - * @see eZ\Publish\API\Repository\UserService + * @covers \Ibexa\Contracts\Core\Repository\UserService * @group integration * @group authorization */ @@ -20,8 +20,8 @@ class UserServiceAuthorizationTest extends BaseTest /** * Test for the loadUserGroup() method. * - * @see \eZ\Publish\API\Repository\UserService::loadUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserGroup + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUserGroup */ public function testLoadUserGroupThrowsUnauthorizedException() { @@ -47,8 +47,8 @@ public function testLoadUserGroupThrowsUnauthorizedException() /** * Test for the loadUserGroupByRemoteId() method. * - * @covers \eZ\Publish\API\Repository\UserService::loadUserGroupByRemoteId() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserGroupByRemoteId + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserGroupByRemoteId() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUserGroupByRemoteId */ public function testLoadUserGroupByRemoteIdThrowsUnauthorizedException(): void { @@ -72,8 +72,8 @@ public function testLoadUserGroupByRemoteIdThrowsUnauthorizedException(): void /** * Test for the loadSubUserGroups() method. * - * @see \eZ\Publish\API\Repository\UserService::loadSubUserGroups() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadSubUserGroups + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadSubUserGroups() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadSubUserGroups */ public function testLoadSubUserGroupsThrowsUnauthorizedException() { @@ -99,8 +99,8 @@ public function testLoadSubUserGroupsThrowsUnauthorizedException() /** * Test for the createUserGroup() method. * - * @see \eZ\Publish\API\Repository\UserService::createUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUserGroup + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUserGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUserGroup */ public function testCreateUserGroupThrowsUnauthorizedException() { @@ -133,8 +133,8 @@ public function testCreateUserGroupThrowsUnauthorizedException() /** * Test for the deleteUserGroup() method. * - * @see \eZ\Publish\API\Repository\UserService::deleteUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testDeleteUserGroup + * @covers \Ibexa\Contracts\Core\Repository\UserService::deleteUserGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testDeleteUserGroup */ public function testDeleteUserGroupThrowsUnauthorizedException() { @@ -160,8 +160,8 @@ public function testDeleteUserGroupThrowsUnauthorizedException() /** * Test for the moveUserGroup() method. * - * @see \eZ\Publish\API\Repository\UserService::moveUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testMoveUserGroup + * @covers \Ibexa\Contracts\Core\Repository\UserService::moveUserGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testMoveUserGroup */ public function testMoveUserGroupThrowsUnauthorizedException() { @@ -173,7 +173,7 @@ public function testMoveUserGroupThrowsUnauthorizedException() $memberGroupId = $this->generateId('group', 11); /* BEGIN: Use Case */ - // $memberGroupId is the ID of the "Members" group in an eZ Publish + // $memberGroupId is the ID of the "Members" group in an Ibexa // demo installation // $user = $this->createUserVersion1(); @@ -194,8 +194,8 @@ public function testMoveUserGroupThrowsUnauthorizedException() /** * Test for the updateUserGroup() method. * - * @see \eZ\Publish\API\Repository\UserService::updateUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testUpdateUserGroup + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUserGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testUpdateUserGroup */ public function testUpdateUserGroupThrowsUnauthorizedException() { @@ -231,8 +231,8 @@ public function testUpdateUserGroupThrowsUnauthorizedException() /** * Test for the createUser() method. * - * @see \eZ\Publish\API\Repository\UserService::createUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testCreateUser + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUser() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testCreateUser */ public function testCreateUserThrowsUnauthorizedException() { @@ -274,8 +274,8 @@ public function testCreateUserThrowsUnauthorizedException() /** * Test for the deleteUser() method. * - * @see \eZ\Publish\API\Repository\UserService::deleteUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testDeleteUser + * @covers \Ibexa\Contracts\Core\Repository\UserService::deleteUser() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testDeleteUser */ public function testDeleteUserThrowsUnauthorizedException() { @@ -299,7 +299,7 @@ public function testDeleteUserThrowsUnauthorizedException() /** * Test for the updateUser() method. * - * @see \eZ\Publish\API\Repository\UserService::updateUser() + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUser() */ public function testUpdateUserThrowsUnauthorizedException() { @@ -325,7 +325,7 @@ public function testUpdateUserThrowsUnauthorizedException() } /** - * @covers \eZ\Publish\API\Repository\UserService::updateUserPassword + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUserPassword */ public function testUpdateUserPasswordThrowsUnauthorizedException(): void { @@ -352,8 +352,8 @@ public function testUpdateUserPasswordThrowsUnauthorizedException(): void /** * Test for the assignUserToUserGroup() method. * - * @see \eZ\Publish\API\Repository\UserService::assignUserToUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testAssignUserToUserGroup + * @covers \Ibexa\Contracts\Core\Repository\UserService::assignUserToUserGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testAssignUserToUserGroup */ public function testAssignUserToUserGroupThrowsUnauthorizedException() { @@ -366,7 +366,7 @@ public function testAssignUserToUserGroupThrowsUnauthorizedException() $administratorGroupId = $this->generateId('group', 12); /* BEGIN: Use Case */ // $administratorGroupId is the ID of the "Administrator" group in an - // eZ Publish demo installation + // Ibexa demo installation $user = $this->createUserVersion1(); @@ -384,8 +384,8 @@ public function testAssignUserToUserGroupThrowsUnauthorizedException() /** * Test for the unAssignUssrFromUserGroup() method. * - * @see \eZ\Publish\API\Repository\UserService::unAssignUssrFromUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testUnAssignUserFromUserGroup + * @covers \Ibexa\Contracts\Core\Repository\UserService::unAssignUssrFromUserGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testUnAssignUserFromUserGroup */ public function testUnAssignUserFromUserGroupThrowsUnauthorizedException() { @@ -399,7 +399,7 @@ public function testUnAssignUserFromUserGroupThrowsUnauthorizedException() $memberGroupId = $this->generateId('group', 11); /* BEGIN: Use Case */ - // $memberGroupId is the ID of the "Members" group in an eZ Publish + // $memberGroupId is the ID of the "Members" group in an Ibexa // demo installation $user = $this->createUserVersion1(); @@ -424,8 +424,8 @@ public function testUnAssignUserFromUserGroupThrowsUnauthorizedException() /** * Test for the loadUserGroupsOfUser() method. * - * @see \eZ\Publish\API\Repository\UserService::loadUserGroupsOfUser() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUserGroupsOfUser + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserGroupsOfUser() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUserGroupsOfUser */ public function testLoadUserGroupsOfUserThrowsUnauthorizedException() { @@ -450,8 +450,8 @@ public function testLoadUserGroupsOfUserThrowsUnauthorizedException() /** * Test for the loadUsersOfUserGroup() method. * - * @see \eZ\Publish\API\Repository\UserService::loadUsersOfUserGroup() - * @depends eZ\Publish\API\Repository\Tests\UserServiceTest::testLoadUsersOfUserGroup + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUsersOfUserGroup() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUsersOfUserGroup */ public function testLoadUsersOfUserGroupThrowsUnauthorizedException() { @@ -477,7 +477,7 @@ public function testLoadUsersOfUserGroupThrowsUnauthorizedException() /** * Create a user group fixture in a variable named <b>$userGroup</b>,. * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup */ private function createUserGroupVersion1() { @@ -485,7 +485,7 @@ private function createUserGroupVersion1() $mainGroupId = $this->generateId('group', 4); /* BEGIN: Inline */ - // $mainGroupId is the ID of the main "Users" group in an eZ Publish + // $mainGroupId is the ID of the main "Users" group in an Ibexa // demo installation $userService = $repository->getUserService(); @@ -507,3 +507,5 @@ private function createUserGroupVersion1() return $userGroup; } } + +class_alias(UserServiceAuthorizationTest::class, 'eZ\Publish\API\Repository\Tests\UserServiceAuthorizationTest'); diff --git a/tests/integration/Core/Repository/UserServiceTest.php b/tests/integration/Core/Repository/UserServiceTest.php new file mode 100644 index 0000000000..d1d02f8fdd --- /dev/null +++ b/tests/integration/Core/Repository/UserServiceTest.php @@ -0,0 +1,3443 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository; + +use DateInterval; +use DateTime; +use DateTimeImmutable; +use Doctrine\DBAL\ParameterType; +use Exception; +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\PasswordInfo; +use Ibexa\Contracts\Core\Repository\Values\User\PasswordValidationContext; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserTokenUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserUpdateStruct; +use Ibexa\Core\FieldType\User\Type; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\Persistence\Legacy\User\Gateway; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\User\UserGroup; + +/** + * Test case for operations in the UserService using in memory storage. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService + * @group integration + * @group user + */ +class UserServiceTest extends BaseTest +{ + // Example password matching default rules + private const EXAMPLE_PASSWORD = 'P@ssword123!'; + + private const EXAMPLE_PASSWORD_TTL = 30; + private const EXAMPLE_PASSWORD_TTL_WARNING = 14; + + /** + * Test for the loadUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserGroup() + */ + public function testLoadUserGroup() + { + $repository = $this->getRepository(); + + $mainGroupId = $this->generateId('group', 4); + /* BEGIN: Use Case */ + // $mainGroupId is the ID of the main "Users" group + + $userService = $repository->getUserService(); + + $userGroup = $userService->loadUserGroup($mainGroupId); + /* END: Use Case */ + + $this->assertInstanceOf(UserGroup::class, $userGroup); + + // User group happens to also be a Content; isUserGroup() should be true and isUser() should be false + $this->assertTrue($userService->isUserGroup($userGroup), 'isUserGroup() => false on a user group'); + $this->assertFalse($userService->isUser($userGroup), 'isUser() => true on a user group'); + $this->assertSame(0, $userGroup->parentId, 'parentId should be equal `0` because it is top level node'); + } + + /** + * Test for the loadUserGroupByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserGroupByRemoteId() + */ + public function testLoadUserGroupByRemoteId(): void + { + $existingRemoteId = 'f5c88a2209584891056f987fd965b0ba'; + + $userService = $this->getRepository()->getUserService(); + $userGroup = $userService->loadUserGroupByRemoteId($existingRemoteId); + + $this->assertInstanceOf(UserGroup::class, $userGroup); + $this->assertEquals($existingRemoteId, $userGroup->contentInfo->remoteId); + // User group happens to also be a Content; isUserGroup() should be true and isUser() should be false + $this->assertTrue($userService->isUserGroup($userGroup), 'isUserGroup() => false on a user group'); + $this->assertFalse($userService->isUser($userGroup), 'isUser() => true on a user group'); + } + + /** + * Test for the loadUserGroup() method to ensure that DomainUserGroupObject is created properly even if a user + * has no access to parent of UserGroup. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserGroup() + */ + public function testLoadUserGroupWithNoAccessToParent() + { + $repository = $this->getRepository(); + + $mainGroupId = $this->generateId('group', 4); + /* BEGIN: Use Case */ + // $mainGroupId is the ID of the main "Users" group + + $userService = $repository->getUserService(); + + $user = $this->createUserWithPolicies( + 'user', + [ + ['module' => 'content', 'function' => 'read'], + ], + new SubtreeLimitation(['limitationValues' => ['/1/5']]) + ); + $repository->getPermissionResolver()->setCurrentUserReference($user); + + $userGroup = $userService->loadUserGroup($mainGroupId); + /* END: Use Case */ + + $this->assertInstanceOf(UserGroup::class, $userGroup); + + // User group happens to also be a Content; isUserGroup() should be true and isUser() should be false + $this->assertTrue($userService->isUserGroup($userGroup), 'isUserGroup() => false on a user group'); + $this->assertFalse($userService->isUser($userGroup), 'isUser() => true on a user group'); + $this->assertSame(0, $userGroup->parentId, 'parentId should be equal `0` because it is top level node'); + } + + /** + * Test for the loadUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserGroup() + * @depends testLoadUserGroup + */ + public function testLoadUserGroupThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $nonExistingGroupId = $this->generateId('group', self::DB_INT_MAX); + /* BEGIN: Use Case */ + $userService = $repository->getUserService(); + + // This call will fail with a NotFoundException + $userService->loadUserGroup($nonExistingGroupId); + /* END: Use Case */ + } + + /** + * Test for the loadUserGroupByRemoteId() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserGroupByRemoteId() + * @depends Ibexa\Tests\Integration\Core\Repository\UserServiceTest::testLoadUserGroupByRemoteId + */ + public function testLoadUserGroupByRemoteIdThrowsNotFoundException(): void + { + $this->expectException(NotFoundException::class); + + $nonExistingGroupRemoteId = 'non-existing'; + + $userService = $this->getRepository()->getUserService(); + $userService->loadUserGroupByRemoteId($nonExistingGroupRemoteId); + } + + /** + * Test for the loadSubUserGroups() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadSubUserGroups() + * @depends testLoadUserGroup + */ + public function testLoadSubUserGroups() + { + $repository = $this->getRepository(); + + $mainGroupId = $this->generateId('group', 4); + /* BEGIN: Use Case */ + // $mainGroupId is the ID of the main "Users" group + + $userService = $repository->getUserService(); + + $userGroup = $userService->loadUserGroup($mainGroupId); + + $subUserGroups = $userService->loadSubUserGroups($userGroup); + foreach ($subUserGroups as $subUserGroup) { + // Do something with the $subUserGroup + $this->assertInstanceOf(UserGroup::class, $subUserGroup); + } + /* END: Use Case */ + } + + /** + * Test loading sub groups throwing NotFoundException. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadSubUserGroups + */ + public function testLoadSubUserGroupsThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $parentGroup = new UserGroup( + [ + 'content' => new Content( + [ + 'versionInfo' => new VersionInfo( + [ + 'contentInfo' => new ContentInfo( + ['id' => 123456] + ), + ] + ), + 'internalFields' => [], + ] + ), + ] + ); + $userService->loadSubUserGroups($parentGroup); + } + + /** + * Test for the newUserGroupCreateStruct() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroupCreateStruct + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::newUserGroupCreateStruct() + */ + public function testNewUserGroupCreateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $userService = $repository->getUserService(); + + $groupCreate = $userService->newUserGroupCreateStruct('eng-US'); + /* END: Use Case */ + + self::assertInstanceOf( + UserGroupCreateStruct::class, + $groupCreate + ); + + return $groupCreate; + } + + /** + * Test for the newUserGroupCreateStruct() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroupCreateStruct $groupCreate + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::newUserGroupCreateStruct() + * @depends testNewUserGroupCreateStruct + */ + public function testNewUserGroupCreateStructSetsMainLanguageCode($groupCreate) + { + $this->assertEquals('eng-US', $groupCreate->mainLanguageCode); + } + + /** + * Test for the newUserGroupCreateStruct() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroupCreateStruct $groupCreate + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::newUserGroupCreateStruct() + * @depends testNewUserGroupCreateStruct + */ + public function testNewUserGroupCreateStructSetsContentType($groupCreate) + { + $this->assertInstanceOf( + ContentType::class, + $groupCreate->contentType + ); + } + + /** + * Test for the newUserGroupCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::newUserGroupCreateStruct($mainLanguageCode, $contentType) + * @depends testNewUserGroupCreateStruct + */ + public function testNewUserGroupCreateStructWithSecondParameter() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + $userService = $repository->getUserService(); + + // Load the default ContentType for user groups + $groupType = $contentTypeService->loadContentTypeByIdentifier('user_group'); + + // Instantiate a new group create struct + $groupCreate = $userService->newUserGroupCreateStruct( + 'eng-US', + $groupType + ); + /* END: Use Case */ + + $this->assertSame($groupType, $groupCreate->contentType); + } + + /** + * Test for the createUserGroup() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUserGroup() + * @depends testNewUserGroupCreateStruct + * @depends testLoadUserGroup + */ + public function testCreateUserGroup() + { + /* BEGIN: Use Case */ + $userGroup = $this->createUserGroupVersion1(); + /* END: Use Case */ + + $this->assertInstanceOf( + UserGroup::class, + $userGroup + ); + + $versionInfo = $userGroup->getVersionInfo(); + + $this->assertEquals(APIVersionInfo::STATUS_PUBLISHED, $versionInfo->status); + $this->assertEquals(1, $versionInfo->versionNo); + + return $userGroup; + } + + /** + * Test for the createUserGroup() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroup + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUserGroup() + * @depends testCreateUserGroup + */ + public function testCreateUserGroupSetsExpectedProperties($userGroup) + { + $this->assertEquals( + [ + 'parentId' => $this->generateId('group', 4), + ], + [ + 'parentId' => $userGroup->parentId, + ] + ); + } + + /** + * Test for the createUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUserGroup() + * @depends testCreateUserGroup + */ + public function testCreateUserGroupThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $mainGroupId = $this->generateId('group', 4); + /* BEGIN: Use Case */ + // $mainGroupId is the ID of the main "Users" group + + $userService = $repository->getUserService(); + + // Load main group + $parentUserGroup = $userService->loadUserGroup($mainGroupId); + + // Instantiate a new create struct + $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); + $userGroupCreate->setField('name', 'Example Group'); + $userGroupCreate->remoteId = '5f7f0bdb3381d6a461d8c29ff53d908f'; + + // This call will fail with an "InvalidArgumentException", because the + // specified remoteId is already used for the "Members" user group. + $userService->createUserGroup( + $userGroupCreate, + $parentUserGroup + ); + /* END: Use Case */ + } + + /** + * Test for the createUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUserGroup() + * @depends testCreateUserGroup + */ + public function testCreateUserGroupThrowsInvalidArgumentExceptionFieldTypeNotAccept() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $mainGroupId = $this->generateId('group', 4); + /* BEGIN: Use Case */ + // $mainGroupId is the ID of the main "Users" group + + $userService = $repository->getUserService(); + + // Load main group + $parentUserGroup = $userService->loadUserGroup($mainGroupId); + + // Instantiate a new create struct + $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); + $userGroupCreate->setField('name', new \stdClass()); + + // This call will fail with an "InvalidArgumentException", because the + // specified remoteId is already used for the "Members" user group. + $userService->createUserGroup( + $userGroupCreate, + $parentUserGroup + ); + /* END: Use Case */ + } + + /** + * Test for the createUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUserGroup() + * @depends testCreateUserGroup + */ + public function testCreateUserGroupWhenMissingField() + { + $this->expectException(ContentFieldValidationException::class); + + $repository = $this->getRepository(); + + $mainGroupId = $this->generateId('group', 4); + /* BEGIN: Use Case */ + // $mainGroupId is the ID of the main "Users" group + + $userService = $repository->getUserService(); + + // Load main group + $parentUserGroup = $userService->loadUserGroup($mainGroupId); + + // Instantiate a new create struct + $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); + + // This call will fail with a "ContentFieldValidationException", because the + // only mandatory field "name" is not set. + $userService->createUserGroup($userGroupCreate, $parentUserGroup); + /* END: Use Case */ + } + + /** + * Test for the createUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUserGroup + * @depends testNewUserGroupCreateStruct + * @depends testLoadUserGroup + */ + public function testCreateUserGroupInTransactionWithRollback(): void + { + $repository = $this->getRepository(); + + $mainGroupId = $this->generateId('group', 4); + /* BEGIN: Use Case */ + // $mainGroupId is the ID of the main "Users" group + + $userService = $repository->getUserService(); + + $repository->beginTransaction(); + + try { + // Load main group + $parentUserGroup = $userService->loadUserGroup($mainGroupId); + + // Instantiate a new create struct + $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); + $userGroupCreate->setField('name', 'Example Group'); + + // Create the new user group + $createdUserGroupId = $userService->createUserGroup( + $userGroupCreate, + $parentUserGroup + )->id; + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + $repository->rollback(); + + try { + // Throws exception since creation of user group was rolled back + $loadedGroup = $userService->loadUserGroup($createdUserGroupId); + } catch (NotFoundException $e) { + return; + } + /* END: Use Case */ + + $this->fail('User group object still exists after rollback.'); + } + + /** + * Test for the deleteUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::deleteUserGroup() + * @depends testCreateUserGroup + */ + public function testDeleteUserGroup() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $userGroup = $this->createUserGroupVersion1(); + + // Delete the currently created user group again + $userService->deleteUserGroup($userGroup); + /* END: Use Case */ + + // We use the NotFoundException here for verification + $userService->loadUserGroup($userGroup->id); + } + + /** + * Test deleting user group throwing NotFoundException. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::deleteUserGroup + */ + public function testDeleteUserGroupThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $userGroup = new UserGroup( + [ + 'content' => new Content( + [ + 'versionInfo' => new VersionInfo( + ['contentInfo' => new ContentInfo(['id' => 123456])] + ), + 'internalFields' => [], + ] + ), + ] + ); + $userService->deleteUserGroup($userGroup); + } + + /** + * Test for the moveUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::moveUserGroup() + * @depends testCreateUserGroup + * @depends testLoadSubUserGroups + */ + public function testMoveUserGroup() + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $membersGroupId = $this->generateId('group', 13); + /* BEGIN: Use Case */ + // $membersGroupId is the ID of the "Members" user group in an Ibexa + // Publish demo installation + + $userGroup = $this->createUserGroupVersion1(); + + // Load the new parent group + $membersUserGroup = $userService->loadUserGroup($membersGroupId); + + // Move user group from "Users" to "Members" + $userService->moveUserGroup($userGroup, $membersUserGroup); + + // Reload the user group to get an updated $parentId + $userGroup = $userService->loadUserGroup($userGroup->id); + + $this->refreshSearch($repository); + + // The returned array will no contain $userGroup + $subUserGroups = $userService->loadSubUserGroups( + $membersUserGroup + ); + /* END: Use Case */ + + $subUserGroupIds = array_map( + static function ($content) { + return $content->id; + }, + $subUserGroups + ); + + $this->assertEquals($membersGroupId, $userGroup->parentId); + $this->assertEquals([$userGroup->id], $subUserGroupIds); + } + + /** + * Test moving a user group below another group throws NotFoundException. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::moveUserGroup + */ + public function testMoveUserGroupThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $userGroupToMove = new UserGroup( + [ + 'content' => new Content( + [ + 'versionInfo' => new VersionInfo( + ['contentInfo' => new ContentInfo(['id' => 123456])] + ), + 'internalFields' => [], + ] + ), + ] + ); + $parentUserGroup = new UserGroup( + [ + 'content' => new Content( + [ + 'versionInfo' => new VersionInfo( + ['contentInfo' => new ContentInfo(['id' => 123455])] + ), + 'internalFields' => [], + ] + ), + ] + ); + $userService->moveUserGroup($userGroupToMove, $parentUserGroup); + } + + /** + * Test for the newUserGroupUpdateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::newUserGroupUpdateStruct + */ + public function testNewUserGroupUpdateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $userService = $repository->getUserService(); + + $groupUpdate = $userService->newUserGroupUpdateStruct(); + /* END: Use Case */ + + $this->assertInstanceOf( + UserGroupUpdateStruct::class, + $groupUpdate + ); + + $this->assertNull($groupUpdate->contentUpdateStruct); + $this->assertNull($groupUpdate->contentMetadataUpdateStruct); + } + + /** + * Test for the updateUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUserGroup() + * @depends testCreateUserGroup + * @depends testNewUserGroupUpdateStruct + */ + public function testUpdateUserGroup() + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $userGroup = $this->createUserGroupVersion1(); + + // Create a group update struct and change nothing + $groupUpdate = $userService->newUserGroupUpdateStruct(); + + // This update will do nothing + $userGroup = $userService->updateUserGroup( + $userGroup, + $groupUpdate + ); + /* END: Use Case */ + + $this->assertInstanceOf( + UserGroup::class, + $userGroup + ); + + $this->assertEquals(1, $userGroup->getVersionInfo()->versionNo); + } + + /** + * Test for the updateUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUserGroup() + * @depends testUpdateUserGroup + */ + public function testUpdateUserGroupWithSubContentUpdateStruct() + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $userGroup = $this->createUserGroupVersion1(); + + // Load the content service + $contentService = $repository->getContentService(); + + // Create a content update struct and update the group name + $contentUpdate = $contentService->newContentUpdateStruct(); + $contentUpdate->setField('name', 'Sindelfingen', 'eng-US'); + + // Create a group update struct and set content update struct + $groupUpdate = $userService->newUserGroupUpdateStruct(); + $groupUpdate->contentUpdateStruct = $contentUpdate; + + // This will update the name and the increment the group version number + $userGroup = $userService->updateUserGroup( + $userGroup, + $groupUpdate + ); + /* END: Use Case */ + + $this->assertEquals('Sindelfingen', $userGroup->getFieldValue('name', 'eng-US')); + + $versionInfo = $userGroup->getVersionInfo(); + + $this->assertEquals(APIVersionInfo::STATUS_PUBLISHED, $versionInfo->status); + $this->assertEquals(2, $versionInfo->versionNo); + } + + /** + * Test for the updateUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUserGroup() + * @depends testUpdateUserGroup + */ + public function testUpdateUserGroupWithSubContentMetadataUpdateStruct() + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $userGroup = $this->createUserGroupVersion1(); + + // Load the content service + $contentService = $repository->getContentService(); + + // Create a metadata update struct and change the remoteId + $metadataUpdate = $contentService->newContentMetadataUpdateStruct(); + $metadataUpdate->remoteId = '3c61299780663bafa3af2101e52125da'; + + // Create a group update struct and set content update struct + $groupUpdate = $userService->newUserGroupUpdateStruct(); + $groupUpdate->contentMetadataUpdateStruct = $metadataUpdate; + + // This will update the name and the increment the group version number + $userGroup = $userService->updateUserGroup( + $userGroup, + $groupUpdate + ); + /* END: Use Case */ + + $this->assertEquals( + '3c61299780663bafa3af2101e52125da', + $userGroup->contentInfo->remoteId + ); + + $versionInfo = $userGroup->getVersionInfo(); + + $this->assertEquals(APIVersionInfo::STATUS_PUBLISHED, $versionInfo->status); + $this->assertEquals(1, $versionInfo->versionNo); + } + + /** + * Test for the updateUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUserGroup() + * @depends testUpdateUserGroup + */ + public function testUpdateUserGroupThrowsInvalidArgumentExceptionOnFieldTypeNotAccept() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $userGroup = $this->createUserGroupVersion1(); + + // Load the content service + $contentService = $repository->getContentService(); + + // Create a content update struct and update the group name + $contentUpdate = $contentService->newContentUpdateStruct(); + // An object of stdClass is not accepted as a value by the field "name" + $contentUpdate->setField('name', new \stdClass(), 'eng-US'); + + // Create a group update struct and set content update struct + $groupUpdate = $userService->newUserGroupUpdateStruct(); + $groupUpdate->contentUpdateStruct = $contentUpdate; + + // This call will fail with an InvalidArgumentException, because the + // field "name" does not accept the given value + $userService->updateUserGroup($userGroup, $groupUpdate); + /* END: Use Case */ + } + + /** + * Test for the newUserCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::newUserCreateStruct() + */ + public function testNewUserCreateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $userService = $repository->getUserService(); + + $userCreate = $userService->newUserCreateStruct( + 'user', + 'user@example.com', + 'secret', + 'eng-US' + ); + /* END: Use Case */ + + self::assertInstanceOf( + UserCreateStruct::class, + $userCreate + ); + + return $userCreate; + } + + /** + * Test updating a user group throws ContentFieldValidationException. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUserGroup + */ + public function testUpdateUserGroupThrowsContentFieldValidationExceptionOnRequiredFieldEmpty() + { + $this->expectException(ContentFieldValidationException::class); + + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + $contentService = $repository->getContentService(); + + $userGroup = $userService->loadUserGroup(42); + $userGroupUpdateStruct = $userService->newUserGroupUpdateStruct(); + $userGroupUpdateStruct->contentUpdateStruct = $contentService->newContentUpdateStruct(); + $userGroupUpdateStruct->contentUpdateStruct->setField('name', '', 'eng-US'); + + $userService->updateUserGroup($userGroup, $userGroupUpdateStruct); + } + + /** + * Test for the newUserCreateStruct() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserCreateStruct $userCreate + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::newUserCreateStruct() + * @depends testNewUserCreateStruct + */ + public function testNewUserCreateStructSetsExpectedProperties($userCreate) + { + $this->assertEquals( + [ + 'login' => 'user', + 'email' => 'user@example.com', + 'password' => 'secret', + 'mainLanguageCode' => 'eng-US', + ], + [ + 'login' => $userCreate->login, + 'email' => $userCreate->email, + 'password' => $userCreate->password, + 'mainLanguageCode' => $userCreate->mainLanguageCode, + ] + ); + } + + /** + * Test for the newUserCreateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::newUserCreateStruct($login, $email, $password, $mainLanguageCode, $contentType) + * @depends testNewUserCreateStruct + */ + public function testNewUserCreateStructWithFifthParameter() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $contentTypeService = $repository->getContentTypeService(); + $userService = $repository->getUserService(); + + $userType = $contentTypeService->loadContentTypeByIdentifier('user'); + + $userCreate = $userService->newUserCreateStruct( + 'user', + 'user@example.com', + 'secret', + 'eng-US', + $userType + ); + /* END: Use Case */ + + $this->assertSame($userType, $userCreate->contentType); + } + + public function testNewUserWithDomainName(): void + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + $createdUser = $this->createUserVersion1( + 'ibexa-user-Domain\username-by-login', + 'username-by-login@ibexa-user-Domain.com' + ); + $loadedUser = $userService->loadUserByLogin('ibexa-user-Domain\username-by-login', Language::ALL); + + $this->assertIsSameUser($createdUser, $loadedUser); + } + + /** + * Test for the createUser() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUser() + * @depends testLoadUserGroup + * @depends testNewUserCreateStruct + */ + public function testCreateUser() + { + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + /* END: Use Case */ + + $this->assertInstanceOf( + User::class, + $user + ); + + return $user; + } + + /** + * Test for the createUser() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUser() + * @depends testCreateUser + */ + public function testCreateUserSetsExpectedProperties(User $user) + { + $this->assertEquals( + [ + 'login' => 'user', + 'email' => 'user@example.com', + 'mainLanguageCode' => 'eng-US', + ], + [ + 'login' => $user->login, + 'email' => $user->email, + 'mainLanguageCode' => $user->contentInfo->mainLanguageCode, + ] + ); + } + + /** + * Test for the createUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUser() + * @depends testCreateUser + */ + public function testCreateUserWhenMissingField() + { + $this->expectException(ContentFieldValidationException::class); + + $repository = $this->getRepository(); + + $editorsGroupId = $this->generateId('group', 13); + /* BEGIN: Use Case */ + // $editorsGroupId is the ID of the "Editors" user group in an Ibexa + // Publish demo installation + + $userService = $repository->getUserService(); + + // Instantiate a create struct with mandatory properties + $userCreate = $userService->newUserCreateStruct( + 'user', + 'user@example.com', + 'secret', + 'eng-US' + ); + + // Do not set the mandatory fields "first_name" and "last_name" + //$userCreate->setField( 'first_name', 'Example' ); + //$userCreate->setField( 'last_name', 'User' ); + + // Load parent group for the user + $group = $userService->loadUserGroup($editorsGroupId); + + // This call will fail with a "ContentFieldValidationException", because the + // mandatory fields "first_name" and "last_name" are not set. + $userService->createUser($userCreate, [$group]); + /* END: Use Case */ + } + + /** + * Test for the createUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUser() + * @depends testCreateUser + */ + public function testCreateUserThrowsInvalidArgumentExceptionOnFieldTypeNotAccept() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $editorsGroupId = $this->generateId('group', 13); + /* BEGIN: Use Case */ + // $editorsGroupId is the ID of the "Editors" user group in an Ibexa + // Publish demo installation + + $userService = $repository->getUserService(); + + // Instantiate a create struct with mandatory properties + $userCreate = $userService->newUserCreateStruct( + 'user', + 'user@example.com', + 'secret', + 'eng-US' + ); + + // An object of stdClass is not a valid value for the field first_name + $userCreate->setField('first_name', new \stdClass()); + $userCreate->setField('last_name', 'User'); + + // Load parent group for the user + $group = $userService->loadUserGroup($editorsGroupId); + + // This call will fail with an "InvalidArgumentException", because the + // value for the firled "first_name" is not accepted by the field type. + $userService->createUser($userCreate, [$group]); + /* END: Use Case */ + } + + /** + * Test for the createUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUser + * @depends testCreateUser + */ + public function testCreateUserThrowsInvalidArgumentException() + { + $repository = $this->getRepository(); + + $editorsGroupId = $this->generateId('group', 13); + /* BEGIN: Use Case */ + // $editorsGroupId is the ID of the "Editors" user group in an Ibexa + // Publish demo installation + + $userService = $repository->getUserService(); + + // Instantiate a create struct with mandatory properties + $userCreate = $userService->newUserCreateStruct( + // admin is an existing login + 'admin', + 'user@example.com', + 'secret', + 'eng-US' + ); + + $userCreate->setField('first_name', 'Example'); + $userCreate->setField('last_name', 'User'); + + // Load parent group for the user + $group = $userService->loadUserGroup($editorsGroupId); + + try { + // This call will fail with a "InvalidArgumentException", because the + // user with "admin" login already exists. + $userService->createUser($userCreate, [$group]); + /* END: Use Case */ + } catch (ContentFieldValidationException $e) { + // Exception is caught, as there is no other way to check exception properties. + $this->assertValidationErrorOccurs($e, 'The user login \'admin\' is used by another user. You must enter a unique login.'); + + /* END: Use Case */ + return; + } + + $this->fail('Expected ValidationError messages did not occur.'); + } + + /** + * Test for the createUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUser + * @depends testCreateUser + */ + public function testCreateUserWithEmailAlreadyTaken(): void + { + $repository = $this->getRepository(); + + $userContentType = $this->createUserContentTypeWithAccountSettings('user_email_unique', [ + Type::REQUIRE_UNIQUE_EMAIL => true, + ]); + + $existingUser = $this->createUserVersion1( + 'existing_user', + 'unique@email.com', + $userContentType, + ); + + $editorsGroupId = $this->generateId('group', 13); + /* BEGIN: Use Case */ + // $editorsGroupId is the ID of the "Editors" user group in an Ibexa + // Publish demo installation + + $userService = $repository->getUserService(); + + // Instantiate a create struct with mandatory properties + $userCreate = $userService->newUserCreateStruct( + 'another_user', + // email is already taken + 'unique@email.com', + 'VerySecure@Password.1234', + 'eng-US', + $userContentType + ); + + $userCreate->setField('first_name', 'Example'); + $userCreate->setField('last_name', 'User'); + + // Load parent group for the user + $group = $userService->loadUserGroup($editorsGroupId); + + try { + // This call will fail with a "ContentFieldValidationException", because the + // user with "unique@email.com" email already exists in database. + $userService->createUser($userCreate, [$group]); + } catch (ContentFieldValidationException $e) { + // Exception is caught, as there is no other way to check exception properties. + $this->assertValidationErrorOccurs($e, 'Email \'unique@email.com\' is used by another user. You must enter a unique email.'); + + return; + } + + $this->fail('Expected ValidationError messages did not occur.'); + } + + /** + * Test for the createUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUser + * @depends testCreateUser + */ + public function testCreateInvalidFormatUsername(): void + { + $repository = $this->getRepository(); + + $userContentType = $this->createUserContentTypeWithAccountSettings('username_format', [ + Type::USERNAME_PATTERN => '^[^@]$', + ]); + + $editorsGroupId = $this->generateId('group', 13); + /* BEGIN: Use Case */ + // $editorsGroupId is the ID of the "Editors" user group in an Ibexa + // Publish demo installation + + $userService = $repository->getUserService(); + + // Instantiate a create struct with mandatory properties + $userCreate = $userService->newUserCreateStruct( + // login contains @ + 'invalid@user', + 'unique@email.com', + 'VerySecure@Password.1234', + 'eng-US', + $userContentType + ); + + $userCreate->setField('first_name', 'Example'); + $userCreate->setField('last_name', 'User'); + + // Load parent group for the user + $group = $userService->loadUserGroup($editorsGroupId); + + try { + // This call will fail with a "ContentFieldValidationException", because the + // user with "invalid@user" login does not match "^[^@]$" pattern. + $userService->createUser($userCreate, [$group]); + } catch (ContentFieldValidationException $e) { + // Exception is caught, as there is no other way to check exception properties. + $this->assertValidationErrorOccurs($e, 'Invalid login format'); + + return; + } + + $this->fail('Expected ValidationError messages did not occur.'); + } + + /** + * Test for the createUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUser + * @depends testLoadUserGroup + * @depends testNewUserCreateStruct + */ + public function testCreateUserInTransactionWithRollback(): void + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $repository->beginTransaction(); + + try { + $user = $this->createUserVersion1(); + } catch (Exception $e) { + // Cleanup hanging transaction on error + $repository->rollback(); + throw $e; + } + + $repository->rollback(); + + try { + // Throws exception since creation of user was rolled back + $loadedUser = $userService->loadUser($user->id); + } catch (NotFoundException $e) { + return; + } + /* END: Use Case */ + + $this->fail('User object still exists after rollback.'); + } + + /** + * Test creating a user throwing NotFoundException. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUser + */ + public function testCreateUserThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $userCreateStruct = $userService->newUserCreateStruct('new_user', 'new_user@ibexa.co', 'password', 'eng-GB'); + $userCreateStruct->setField('first_name', 'New'); + $userCreateStruct->setField('last_name', 'User'); + + $parentGroup = new UserGroup( + [ + 'content' => new Content( + [ + 'versionInfo' => new VersionInfo( + [ + 'contentInfo' => new ContentInfo(['id' => 123456]), + ] + ), + 'internalFields' => [], + ] + ), + ] + ); + $userService->createUser($userCreateStruct, [$parentGroup]); + } + + /** + * Test creating a user throwing UserPasswordValidationException when password doesn't follow specific rules. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUser + */ + public function testCreateUserWithWeakPasswordThrowsUserPasswordValidationException() + { + $userContentType = $this->createUserContentTypeWithStrongPassword(); + + try { + // This call will fail with a "UserPasswordValidationException" because the + // the password does not follow specified rules. + $this->createTestUserWithPassword('pass', $userContentType); + } catch (ContentFieldValidationException $e) { + // Exception is caught, as there is no other way to check exception properties. + $this->assertAllValidationErrorsOccur( + $e, + [ + 'User password must include at least one special character', + 'User password must be at least 8 characters long', + 'User password must include at least one upper case letter', + 'User password must include at least one number', + ] + ); + + return; + } + + $this->fail('Expected ValidationError messages did not occur.'); + } + + /** + * Opposite test case for testCreateUserWithWeakPasswordThrowsUserPasswordValidationException. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUser + */ + public function testCreateUserWithStrongPassword() + { + $userContentType = $this->createUserContentTypeWithStrongPassword(); + + /* BEGIN: Use Case */ + $user = $this->createTestUserWithPassword('H@xxi0r!', $userContentType); + /* END: Use Case */ + + $this->assertInstanceOf(User::class, $user); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUser() + * + * @depends testCreateUser + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testLoadUser(): void + { + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Load the newly created user + $userReloaded = $userService->loadUser($user->id, Language::ALL); + /* END: Use Case */ + + $this->assertIsSameUser($user, $userReloaded); + + // User happens to also be a Content; isUser() should be true and isUserGroup() should be false + self::assertTrue($userService->isUser($user), 'isUser() => false on a user'); + self::assertFalse($userService->isUserGroup($user), 'isUserGroup() => true on a user group'); + } + + /** + * Test for the loadUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUser() + * @depends testLoadUser + */ + public function testLoadUserThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $nonExistingUserId = $this->generateId('user', self::DB_INT_MAX); + /* BEGIN: Use Case */ + $userService = $repository->getUserService(); + + // This call will fail with a "NotFoundException", because no user with + // an id equal to self::DB_INT_MAX should exist. + $userService->loadUser($nonExistingUserId); + /* END: Use Case */ + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserService::checkUserCredentials() + * @depends testCreateUser + */ + public function testCheckUserCredentialsValid(): void + { + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Load the newly created user credentials + $credentialsValid = $userService->checkUserCredentials($user, 'VerySecret@Password.1234'); + /* END: Use Case */ + + $this->assertTrue($credentialsValid); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserService::checkUserCredentials() + * @depends testCreateUser + */ + public function testCheckUserCredentialsInvalid(): void + { + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Load the newly created user credentials + $credentialsValid = $userService->checkUserCredentials($user, 'NotSoSecretPassword'); + /* END: Use Case */ + + $this->assertFalse($credentialsValid); + } + + /** + * Test for the loadUserByLogin() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserByLogin() + * @depends testCreateUser + */ + public function testLoadUserByLogin() + { + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1('User'); + + // Load the newly created user + $userReloaded = $userService->loadUserByLogin('User'); + /* END: Use Case */ + + $this->assertPropertiesCorrect( + [ + 'login' => $user->login, + 'email' => $user->email, + 'passwordHash' => $user->passwordHash, + 'hashAlgorithm' => $user->hashAlgorithm, + 'enabled' => $user->enabled, + 'maxLogin' => $user->maxLogin, + 'id' => $user->id, + 'contentInfo' => $user->contentInfo, + 'versionInfo' => $user->versionInfo, + 'fields' => $user->fields, + ], + $userReloaded + ); + } + + /** + * Test for the loadUserByLogin() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserByLogin() + * @depends testLoadUserByLogin + */ + public function testLoadUserByLoginThrowsNotFoundExceptionForUnknownLogin() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $this->createUserVersion1(); + + // This call will fail with a "NotFoundException", because the given + // login/password combination does not exist. + $userService->loadUserByLogin('user42'); + /* END: Use Case */ + } + + /** + * Test for the loadUserByLogin() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserByLogin() + * @depends testLoadUserByLogin + */ + public function testLoadUserByLoginWorksForLoginWithWrongCase() + { + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Lookup by user login should ignore casing + $userReloaded = $userService->loadUserByLogin('USER'); + /* END: Use Case */ + + $this->assertPropertiesCorrect( + [ + 'login' => $user->login, + 'email' => $user->email, + 'passwordHash' => $user->passwordHash, + 'hashAlgorithm' => $user->hashAlgorithm, + 'enabled' => $user->enabled, + 'maxLogin' => $user->maxLogin, + 'id' => $user->id, + 'contentInfo' => $user->contentInfo, + 'versionInfo' => $user->versionInfo, + 'fields' => $user->fields, + ], + $userReloaded + ); + } + + /** + * Test for the loadUserByLogin() method. + * + * In some cases people use email as login name, make sure system works as exepcted when asking for user by email. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserByLogin() + * @depends testLoadUserByLogin + */ + public function testLoadUserByLoginThrowsNotFoundExceptionForUnknownLoginByEmail() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Lookup by user login by email should behave as normal + $userService->loadUserByLogin('user@example.com'); + /* END: Use Case */ + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUsersByEmail() + * + * @depends testCreateUser + */ + public function testLoadUserByEmail(): void + { + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + $user = $this->createUserVersion1(); + + // Load the newly created user + $usersReloaded = $userService->loadUsersByEmail('user@example.com', Language::ALL); + + self::assertCount(1, $usersReloaded); + $this->assertIsSameUser($user, $usersReloaded[0]); + } + + /** + * Test for the loadUsersByEmail() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUsersByEmail() + * @depends testLoadUserByEmail + */ + public function testLoadUserByEmailReturnsEmptyInUnknownEmail() + { + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $this->createUserVersion1(); + + // This call will return empty array, because the given + // login/password combination does not exist. + $emptyUserList = $userService->loadUsersByEmail('user42@example.com'); + /* END: Use Case */ + + $this->assertEquals([], $emptyUserList); + } + + /** + * Test for the deleteUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::deleteUser() + * @depends testCreateUser + * @depends testLoadUser + */ + public function testDeleteUser() + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Delete the currently created user + $userService->deleteUser($user); + /* END: Use Case */ + + // We use the NotFoundException here to verify that the user not exists + $userService->loadUser($user->id); + } + + /** + * Test for the deleteUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::deleteUser() + * @depends testCreateUser + * @depends testLoadUser + */ + public function testDeleteUserDeletesRelatedBookmarks() + { + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + $locationService = $repository->getLocationService(); + $bookmarkService = $repository->getBookmarkService(); + /* BEGIN: Use Case */ + $admin = $repository->getPermissionResolver()->getCurrentUserReference(); + + $user = $this->createUserVersion1(); + + $repository->getPermissionResolver()->setCurrentUserReference($user); + + $bookmarkService->createBookmark( + $locationService->loadLocation($this->generateId('location', 43)) + ); + + $repository->getPermissionResolver()->setCurrentUserReference($admin); + // Delete the currently created user + $userService->deleteUser($user); + + $repository->getPermissionResolver()->setCurrentUserReference($user); + /* END: Use Case */ + + $this->assertEquals(0, $bookmarkService->loadBookmarks(0, 9999)->totalCount); + } + + /** + * Test for the newUserUpdateStruct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::newUserUpdateStruct() + */ + public function testNewUserUpdateStruct() + { + $repository = $this->getRepository(); + + /* BEGIN: Use Case */ + $userService = $repository->getUserService(); + + // Create a new update struct instance + $userUpdate = $userService->newUserUpdateStruct(); + /* END: Use Case */ + + $this->assertInstanceOf( + UserUpdateStruct::class, + $userUpdate + ); + + $this->assertNull($userUpdate->contentUpdateStruct); + $this->assertNull($userUpdate->contentMetadataUpdateStruct); + + $this->assertPropertiesCorrect( + [ + 'email' => null, + 'password' => null, + 'enabled' => null, + 'maxLogin' => null, + ], + $userUpdate + ); + } + + /** + * Test for the updateUser() method. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUser() + * @depends testCreateUser + * @depends testNewUserUpdateStruct + */ + public function testUpdateUser() + { + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Create a new update struct instance + $userUpdate = $userService->newUserUpdateStruct(); + + // Set new values for password and maxLogin + $userUpdate->password = 'my-new-password'; + $userUpdate->maxLogin = 42; + $userUpdate->enabled = false; + + // Updated the user record. + $userVersion2 = $userService->updateUser($user, $userUpdate); + /* END: Use Case */ + + $this->assertInstanceOf(User::class, $userVersion2); + + return $userVersion2; + } + + /** + * Test for the updateUser() and loadUsersByEmail() method on change to email. + */ + public function testUpdateUserEmail(): void + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + // Create a user + $user = $this->createUserVersion1(); + + // Check we get what we expect (and implicit warmup any kind of cache) + $users = $userService->loadUsersByEmail('user2@example.com'); + $this->assertCount(0, $users); + + // Update user with the given email address + $userUpdate = $userService->newUserUpdateStruct(); + $userUpdate->email = 'user2@example.com'; + $updatedUser = $userService->updateUser($user, $userUpdate); + $this->assertInstanceOf(User::class, $updatedUser); + + // Check that we can load user by email + $users = $userService->loadUsersByEmail('user2@example.com'); + $this->assertCount(1, $users); + $this->assertInstanceOf(User::class, $users[0]); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUser + * @depends testCreateUser + * @depends testNewUserUpdateStruct + */ + public function testUpdateUserNoPassword(): void + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Create a new update struct instance + $userUpdate = $userService->newUserUpdateStruct(); + + // Set new values for maxLogin, don't change password + $userUpdate->maxLogin = 43; + $userUpdate->enabled = false; + + // Updated the user record. + $userVersion2 = $userService->updateUser($user, $userUpdate); + /* END: Use Case */ + + $this->assertInstanceOf(User::class, $user); + + $this->assertEquals( + [ + 'login' => $user->login, + 'email' => $user->email, + 'passwordHash' => $user->passwordHash, + 'hashAlgorithm' => $user->hashAlgorithm, + 'maxLogin' => 43, + 'enabled' => false, + ], + [ + 'login' => $userVersion2->login, + 'email' => $userVersion2->email, + 'passwordHash' => $userVersion2->passwordHash, + 'hashAlgorithm' => $userVersion2->hashAlgorithm, + 'maxLogin' => $userVersion2->maxLogin, + 'enabled' => $userVersion2->enabled, + ] + ); + } + + /** + * Test for the updateUser() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUser() + * @depends testUpdateUser + */ + public function testUpdateUserUpdatesExpectedProperties(User $user) + { + $this->assertEquals( + [ + 'login' => 'user', + 'email' => 'user@example.com', + 'maxLogin' => 42, + 'enabled' => false, + ], + [ + 'login' => $user->login, + 'email' => $user->email, + 'maxLogin' => $user->maxLogin, + 'enabled' => $user->enabled, + ] + ); + + // Make sure passwordUpdatedAt field has been updated together with password + $this->assertNotNull($user->passwordUpdatedAt); + $this->assertEqualsWithDelta( + $user->getVersionInfo()->modificationDate->getTimestamp(), + $user->passwordUpdatedAt->getTimestamp(), + 2.0 + ); + } + + /** + * Test for the updateUser() method. + * + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUser() + * @depends testUpdateUser + */ + public function testUpdateUserReturnsPublishedVersion(User $user) + { + $this->assertEquals( + APIVersionInfo::STATUS_PUBLISHED, + $user->getVersionInfo()->status + ); + } + + /** + * Test for the updateUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUser() + * @depends testUpdateUser + */ + public function testUpdateUserWithContentMetadataUpdateStruct() + { + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Get the ContentService implementation + $contentService = $repository->getContentService(); + + // Create a metadata update struct and change the remote id. + $metadataUpdate = $contentService->newContentMetadataUpdateStruct(); + $metadataUpdate->remoteId = '85e10037d1ac0a00aa75443ced483e08'; + + // Create a new update struct instance + $userUpdate = $userService->newUserUpdateStruct(); + + // Set the metadata update struct. + $userUpdate->contentMetadataUpdateStruct = $metadataUpdate; + + // Updated the user record. + $userVersion2 = $userService->updateUser($user, $userUpdate); + + // The contentInfo->remoteId will be changed now. + $remoteId = $userVersion2->contentInfo->remoteId; + /* END: Use Case */ + + $this->assertEquals('85e10037d1ac0a00aa75443ced483e08', $remoteId); + } + + /** + * Test for the updateUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUser() + * @depends testUpdateUser + */ + public function testUpdateUserWithContentUpdateStruct() + { + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Get the ContentService implementation + $contentService = $repository->getContentService(); + + // Create a content update struct and change the remote id. + $contentUpdate = $contentService->newContentUpdateStruct(); + $contentUpdate->setField('first_name', 'Hello', 'eng-US'); + $contentUpdate->setField('last_name', 'World', 'eng-US'); + + // Create a new update struct instance + $userUpdate = $userService->newUserUpdateStruct(); + + // Set the content update struct. + $userUpdate->contentUpdateStruct = $contentUpdate; + + // Updated the user record. + $userVersion2 = $userService->updateUser($user, $userUpdate); + + $name = sprintf( + '%s %s', + $userVersion2->getFieldValue('first_name'), + $userVersion2->getFieldValue('last_name') + ); + /* END: Use Case */ + + $this->assertEquals('Hello World', $name); + } + + /** + * Test for the updateUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUser() + * @depends testUpdateUser + */ + public function testUpdateUserWhenMissingField() + { + $this->expectException(ContentFieldValidationException::class); + + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Get the ContentService implementation + $contentService = $repository->getContentService(); + + // Create a content update struct and change the remote id. + $contentUpdate = $contentService->newContentUpdateStruct(); + $contentUpdate->setField('first_name', null, 'eng-US'); + + // Create a new update struct instance + $userUpdate = $userService->newUserUpdateStruct(); + + // Set the content update struct. + $userUpdate->contentUpdateStruct = $contentUpdate; + + // This call will fail with a "ContentFieldValidationException" because the + // mandatory field "first_name" is set to an empty value. + $userService->updateUser($user, $userUpdate); + + /* END: Use Case */ + } + + /** + * Test for the updateUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUser() + * @depends testUpdateUser + */ + public function testUpdateUserThrowsInvalidArgumentExceptionOnFieldTypeNotAccept() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // Get the ContentService implementation + $contentService = $repository->getContentService(); + + $contentUpdate = $contentService->newContentUpdateStruct(); + // An object of stdClass is not valid for the field first_name + $contentUpdate->setField('first_name', new \stdClass(), 'eng-US'); + + // Create a new update struct instance + $userUpdate = $userService->newUserUpdateStruct(); + + // Set the content update struct. + $userUpdate->contentUpdateStruct = $contentUpdate; + + // This call will fail with a "InvalidArgumentException" because the + // the field "first_name" does not accept the given value. + $userService->updateUser($user, $userUpdate); + + /* END: Use Case */ + } + + /** + * Test updating a user throwing UserPasswordValidationException when password doesn't follow specified rules. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUser + */ + public function testUpdateUserWithWeakPasswordThrowsUserPasswordValidationException() + { + $userService = $this->getRepository()->getUserService(); + + $user = $this->createTestUserWithPassword('H@xxxiR!_1', $this->createUserContentTypeWithStrongPassword()); + + /* BEGIN: Use Case */ + // Create a new update struct instance + $userUpdate = $userService->newUserUpdateStruct(); + $userUpdate->password = 'pass'; + + try { + // This call will fail with a "UserPasswordValidationException" because the + // the password does not follow specified rules + $userService->updateUser($user, $userUpdate); + /* END: Use Case */ + } catch (ContentFieldValidationException $e) { + // Exception is caught, as there is no other way to check exception properties. + $this->assertValidationErrorOccurs($e, 'User password must include at least one special character'); + $this->assertValidationErrorOccurs($e, 'User password must be at least 8 characters long'); + $this->assertValidationErrorOccurs($e, 'User password must include at least one upper case letter'); + $this->assertValidationErrorOccurs($e, 'User password must include at least one number'); + + /* END: Use Case */ + return; + } + + $this->fail('Expected ValidationError messages did not occur.'); + } + + /** + * Opposite test case for testUpdateUserWithWeakPasswordThrowsUserPasswordValidationException. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUser + */ + public function testUpdateUserWithStrongPassword() + { + $userService = $this->getRepository()->getUserService(); + + $user = $this->createTestUserWithPassword('H@xxxiR!_1', $this->createUserContentTypeWithStrongPassword()); + + /* BEGIN: Use Case */ + // Create a new update struct instance + $userUpdate = $userService->newUserUpdateStruct(); + $userUpdate->password = 'H@xxxiR!_2'; + + $user = $userService->updateUser($user, $userUpdate); + /* END: Use Case */ + + $this->assertInstanceOf(User::class, $user); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUser + */ + public function testUpdateUserByUserWithLimitations(): void + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $user = $this->createTestUserWithPassword('H@xxxiR!_1', $this->createUserContentTypeWithStrongPassword()); + + $currentUser = $this->createUserWithPolicies( + 'user', + [ + ['module' => 'content', 'function' => 'edit'], + ['module' => 'content', 'function' => 'read'], + ['module' => 'content', 'function' => 'versionread'], + ['module' => 'content', 'function' => 'publish'], + ['module' => 'user', 'function' => 'password'], + ], + new SubtreeLimitation(['limitationValues' => ['/1/5']]) + ); + $repository->getPermissionResolver()->setCurrentUserReference($currentUser); + + // Create a new update struct instance + $userUpdate = $userService->newUserUpdateStruct(); + $userUpdate->password = 'H@xxxiR!_2'; + + $user = $userService->updateUser($user, $userUpdate); + + self::assertInstanceOf(User::class, $user); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUserPassword + */ + public function testUpdateUserPasswordWorksWithUserPasswordRole(): void + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + $permissionResolver = $repository->getPermissionResolver(); + + $this->createRoleWithPolicies('CanChangePassword', [ + ['module' => 'user', 'function' => 'password'], + ]); + + $user = $this->createCustomUserWithLogin( + 'with_role_password', + 'with_role_password@example.com', + 'Anons', + 'CanChangePassword' + ); + $previousHash = $user->passwordHash; + + $permissionResolver->setCurrentUserReference($user); + + $userService->updateUserPassword($user, 'new password'); + + $user = $userService->loadUserByLogin('with_role_password'); + $this->assertNotEquals($previousHash, $user->passwordHash); + } + + /** + * @throws \Doctrine\DBAL\Exception + * @throws \ErrorException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testUpdateUserPasswordWithUnsupportedHashType(): void + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $user = $this->createUser('john.doe', 'John', 'Doe'); + $oldPasswordHash = $user->passwordHash; + + $wrongHashType = 1; + $this->updateRawPasswordHash($user->getUserId(), $wrongHashType); + $newPassword = 'new_secret123'; + // no need to invalidate cache since there was no load between create & raw database update + $user = $userService->updateUserPassword($user, $newPassword); + + self::assertTrue($userService->checkUserCredentials($user, $newPassword)); + self::assertNotEquals($oldPasswordHash, $user->passwordHash); + } + + /** + * Test for the loadUserGroupsOfUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserGroupsOfUser + * @depends testCreateUser + */ + public function testLoadUserGroupsOfUser() + { + $repository = $this->getRepository(); + + $userService = $repository->getUserService(); + + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // This array will contain the "Editors" user group name + $userGroupNames = []; + foreach ($userService->loadUserGroupsOfUser($user) as $userGroup) { + $this->assertInstanceOf(UserGroup::class, $userGroup); + $userGroupNames[] = $userGroup->getFieldValue('name'); + } + /* END: Use Case */ + + $this->assertEquals(['Editors'], $userGroupNames); + } + + /** + * Test for the loadUsersOfUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUsersOfUserGroup + * @depends testCreateUser + */ + public function testLoadUsersOfUserGroup() + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $group = $userService->loadUserGroup($this->generateId('group', 13)); + + /* BEGIN: Use Case */ + $this->createUserVersion1(); + + $this->refreshSearch($repository); + + // This array will contain the email of the newly created "Editor" user + $email = []; + foreach ($userService->loadUsersOfUserGroup($group) as $user) { + $this->assertInstanceOf(User::class, $user); + $email[] = $user->email; + } + /* END: Use Case */ + $this->assertEquals(['user@example.com'], $email); + } + + /** + * Test for the assignUserToUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::assignUserToUserGroup() + * @depends testLoadUserGroupsOfUser + */ + public function testAssignUserToUserGroup() + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $administratorGroupId = $this->generateId('group', 12); + /* BEGIN: Use Case */ + // $administratorGroupId is the ID of the "Administrator" group in an + // Ibexa demo installation + + $user = $this->createUserVersion1(); + + // Assign group to newly created user + $userService->assignUserToUserGroup( + $user, + $userService->loadUserGroup($administratorGroupId) + ); + + // This array will contain "Editors" and "Administrator users" + $userGroupNames = []; + foreach ($userService->loadUserGroupsOfUser($user) as $userGroup) { + $userGroupNames[] = $userGroup->getFieldValue('name'); + } + /* END: Use Case */ + + sort($userGroupNames, SORT_STRING); + + $this->assertEquals( + [ + 'Administrator users', + 'Editors', + ], + $userGroupNames + ); + } + + /** + * Test for the assignUserToUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::assignUserToUserGroup + * @depends testAssignUserToUserGroup + */ + public function testAssignUserToUserGroupThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'user\' is invalid: User is already in the given User Group'); + + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $editorsGroupId = $this->generateId('group', 13); + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + // $editorsGroupId is the ID of the "Editors" group in an + // Ibexa demo installation + + // This call will fail with an "InvalidArgumentException", because the + // user is already assigned to the "Editors" group + $userService->assignUserToUserGroup( + $user, + $userService->loadUserGroup($editorsGroupId) + ); + /* END: Use Case */ + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserService::assignUserToUserGroup + */ + public function testAssignUserToGroupWithLocationsValidation(): void + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + $locationService = $repository->getLocationService(); + + $administratorGroupId = $this->generateId('group', 12); + + $user = $this->createUserVersion1(); + + $group = $userService->loadUserGroup($administratorGroupId); + $groupLocation = $locationService->loadLocation($group->contentInfo->mainLocationId); + + // Count number of child locations before assigning user to group + $count = $locationService->getLocationChildCount($groupLocation); + $expectedCount = $count + 1; + + $userService->assignUserToUserGroup( + $user, + $group + ); + + $this->refreshSearch($repository); + + // Count number of child locations after assigning the user to a group + $actualCount = $locationService->getLocationChildCount($groupLocation); + + self::assertEquals($expectedCount, $actualCount); + } + + /** + * Test for the unAssignUssrFromUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::unAssignUssrFromUserGroup() + * @depends testLoadUserGroupsOfUser + */ + public function testUnAssignUserFromUserGroup() + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $editorsGroupId = $this->generateId('group', 13); + $anonymousGroupId = $this->generateId('group', 42); + + /* BEGIN: Use Case */ + // $anonymousGroupId is the ID of the "Anonymous users" group in an Ibexa + // Publish demo installation + + $user = $this->createUserVersion1(); + + // Assign group to newly created user + $userService->assignUserToUserGroup( + $user, + $userService->loadUserGroup($anonymousGroupId) + ); + + // Unassign user from "Editors" group + $userService->unAssignUserFromUserGroup( + $user, + $userService->loadUserGroup($editorsGroupId) + ); + + // This array will contain "Anonymous users" + $userGroupNames = []; + foreach ($userService->loadUserGroupsOfUser($user) as $userGroup) { + $userGroupNames[] = $userGroup->getFieldValue('name'); + } + /* END: Use Case */ + + $this->assertEquals(['Anonymous users'], $userGroupNames); + } + + /** + * Test for the unAssignUserFromUserGroup() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::unAssignUserFromUserGroup() + * @depends testUnAssignUserFromUserGroup + */ + public function testUnAssignUserFromUserGroupThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $administratorGroupId = $this->generateId('group', 12); + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + // $administratorGroupId is the ID of the "Administrator" group in an + // Ibexa demo installation + + // This call will fail with an "InvalidArgumentException", because the + // user is not assigned to the "Administrator" group + $userService->unAssignUserFromUserGroup( + $user, + $userService->loadUserGroup($administratorGroupId) + ); + /* END: Use Case */ + } + + /** + * Test for the unAssignUserFromUserGroup() method removing user from the last group. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::unAssignUserFromUserGroup + * @depends testUnAssignUserFromUserGroup + */ + public function testUnAssignUserFromUserGroupThrowsBadStateArgumentException() + { + $this->expectException(BadStateException::class); + $this->expectExceptionMessage('Argument \'user\' has a bad state: User only has one User Group, cannot unassign from last group'); + + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $editorsGroupId = $this->generateId('group', 13); + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + // This call will fail with an "BadStateException", because the + // user has to be assigned to at least one group + $userService->unAssignUserFromUserGroup( + $user, + $userService->loadUserGroup($editorsGroupId) + ); + /* END: Use Case */ + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserService::unAssignUserFromUserGroup + */ + public function testUnAssignUserToGroupWithLocationValidation(): void + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + $locationService = $repository->getLocationService(); + + $editorsGroupId = $this->generateId('group', 13); + $anonymousGroupId = $this->generateId('group', 42); + + $user = $this->createUserVersion1(); + + $this->refreshSearch($repository); + + $group = $userService->loadUserGroup($editorsGroupId); + $groupLocation = $locationService->loadLocation($group->contentInfo->mainLocationId); + + // Count number of child locations before unassigning the user from a group + $count = $locationService->getLocationChildCount($groupLocation); + $expectedCount = $count - 1; + + // Assigning user to a different group to avoid removing all groups from the user + $userService->assignUserToUserGroup( + $user, + $userService->loadUserGroup($anonymousGroupId) + ); + + $userService->unAssignUserFromUserGroup( + $user, + $userService->loadUserGroup($editorsGroupId) + ); + + $this->refreshSearch($repository); + + // Count number of child locations after unassigning the user from a group + $actualCount = $locationService->getLocationChildCount($groupLocation); + + self::assertEquals($expectedCount, $actualCount); + } + + /** + * Test that multi-language logic for the loadUserGroup method respects prioritized language list. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserGroup + * @dataProvider getPrioritizedLanguageList + * + * @param string[] $prioritizedLanguages + * @param string|null $expectedLanguageCode language code of expected translation + */ + public function testLoadUserGroupWithPrioritizedLanguagesList( + array $prioritizedLanguages, + $expectedLanguageCode + ) { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $userGroup = $this->createMultiLanguageUserGroup(); + if ($expectedLanguageCode === null) { + $expectedLanguageCode = $userGroup->contentInfo->mainLanguageCode; + } + + $loadedUserGroup = $userService->loadUserGroup($userGroup->id, $prioritizedLanguages); + + self::assertEquals( + $loadedUserGroup->getName($expectedLanguageCode), + $loadedUserGroup->getName() + ); + self::assertEquals( + $loadedUserGroup->getFieldValue('description', $expectedLanguageCode), + $loadedUserGroup->getFieldValue('description') + ); + } + + /** + * Test that multi-language logic works correctly after updating user group main language. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserGroup + * @dataProvider getPrioritizedLanguageList + * + * @param string[] $prioritizedLanguages + * @param string|null $expectedLanguageCode language code of expected translation + */ + public function testLoadUserGroupWithPrioritizedLanguagesListAfterMainLanguageUpdate( + array $prioritizedLanguages, + $expectedLanguageCode + ) { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + $contentService = $repository->getContentService(); + + $userGroup = $this->createMultiLanguageUserGroup(); + + $userGroupUpdateStruct = $userService->newUserGroupUpdateStruct(); + $userGroupUpdateStruct->contentMetadataUpdateStruct = $contentService->newContentMetadataUpdateStruct(); + $userGroupUpdateStruct->contentMetadataUpdateStruct->mainLanguageCode = 'eng-GB'; + $userService->updateUserGroup($userGroup, $userGroupUpdateStruct); + + if ($expectedLanguageCode === null) { + $expectedLanguageCode = 'eng-GB'; + } + + $loadedUserGroup = $userService->loadUserGroup($userGroup->id, $prioritizedLanguages); + + self::assertEquals( + $loadedUserGroup->getName($expectedLanguageCode), + $loadedUserGroup->getName() + ); + self::assertEquals( + $loadedUserGroup->getFieldValue('description', $expectedLanguageCode), + $loadedUserGroup->getFieldValue('description') + ); + } + + /** + * Test that multi-language logic for the loadSubUserGroups method respects prioritized language list. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadSubUserGroups + * @dataProvider getPrioritizedLanguageList + * + * @param string[] $prioritizedLanguages + * @param string|null $expectedLanguageCode language code of expected translation + */ + public function testLoadSubUserGroupsWithPrioritizedLanguagesList( + array $prioritizedLanguages, + $expectedLanguageCode + ) { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + // create main group for subgroups + $userGroup = $this->createMultiLanguageUserGroup(4); + if ($expectedLanguageCode === null) { + $expectedLanguageCode = $userGroup->contentInfo->mainLanguageCode; + } + + // create subgroups + $this->createMultiLanguageUserGroup($userGroup->id); + $this->createMultiLanguageUserGroup($userGroup->id); + + $userGroup = $userService->loadUserGroup($userGroup->id, $prioritizedLanguages); + + $subUserGroups = $userService->loadSubUserGroups($userGroup, 0, 2, $prioritizedLanguages); + foreach ($subUserGroups as $subUserGroup) { + self::assertEquals( + $subUserGroup->getName($expectedLanguageCode), + $subUserGroup->getName() + ); + self::assertEquals( + $subUserGroup->getFieldValue('description', $expectedLanguageCode), + $subUserGroup->getFieldValue('description') + ); + } + } + + /** + * Test that multi-language logic for the loadUser method respects prioritized language list. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUser + * @dataProvider getPrioritizedLanguageList + * + * @param string[] $prioritizedLanguages + * @param string|null $expectedLanguageCode language code of expected translation + */ + public function testLoadUserWithPrioritizedLanguagesList( + array $prioritizedLanguages, + $expectedLanguageCode + ) { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $user = $this->createMultiLanguageUser(); + if ($expectedLanguageCode === null) { + $expectedLanguageCode = $user->contentInfo->mainLanguageCode; + } + + $loadedUser = $userService->loadUser($user->id, $prioritizedLanguages); + + self::assertEquals( + $loadedUser->getName($expectedLanguageCode), + $loadedUser->getName() + ); + + foreach (['fist_name', 'last_name', 'signature'] as $fieldIdentifier) { + self::assertEquals( + $loadedUser->getFieldValue($fieldIdentifier, $expectedLanguageCode), + $loadedUser->getFieldValue($fieldIdentifier) + ); + } + } + + /** + * Test that multi-language logic for the loadUser method works correctly after updating + * user content main language. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserGroup + * @dataProvider getPrioritizedLanguageList + * + * @param string[] $prioritizedLanguages + * @param string|null $expectedLanguageCode language code of expected translation + */ + public function testLoadUserWithPrioritizedLanguagesListAfterMainLanguageUpdate( + array $prioritizedLanguages, + $expectedLanguageCode + ) { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + $contentService = $repository->getContentService(); + + $user = $this->createMultiLanguageUser(); + // sanity check + self::assertEquals($user->contentInfo->mainLanguageCode, 'eng-US'); + + $userUpdateStruct = $userService->newUserUpdateStruct(); + $userUpdateStruct->contentMetadataUpdateStruct = $contentService->newContentMetadataUpdateStruct(); + $userUpdateStruct->contentMetadataUpdateStruct->mainLanguageCode = 'eng-GB'; + $userService->updateUser($user, $userUpdateStruct); + if ($expectedLanguageCode === null) { + $expectedLanguageCode = 'eng-GB'; + } + + $loadedUser = $userService->loadUser($user->id, $prioritizedLanguages); + + self::assertEquals( + $loadedUser->getName($expectedLanguageCode), + $loadedUser->getName() + ); + + foreach (['fist_name', 'last_name', 'signature'] as $fieldIdentifier) { + self::assertEquals( + $loadedUser->getFieldValue($fieldIdentifier, $expectedLanguageCode), + $loadedUser->getFieldValue($fieldIdentifier) + ); + } + } + + /** + * Test that multi-language logic for the loadUserByLogin method respects prioritized language list. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserByLogin + * @dataProvider getPrioritizedLanguageList + * + * @param string[] $prioritizedLanguages + * @param string|null $expectedLanguageCode language code of expected translation + */ + public function testLoadUserByLoginWithPrioritizedLanguagesList( + array $prioritizedLanguages, + $expectedLanguageCode + ) { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + $user = $this->createMultiLanguageUser(); + + // load, with prioritized languages, the newly created user + $loadedUser = $userService->loadUserByLogin($user->login, $prioritizedLanguages); + if ($expectedLanguageCode === null) { + $expectedLanguageCode = $loadedUser->contentInfo->mainLanguageCode; + } + + self::assertEquals( + $loadedUser->getName($expectedLanguageCode), + $loadedUser->getName() + ); + + foreach (['first_name', 'last_name', 'signature'] as $fieldIdentifier) { + self::assertEquals( + $loadedUser->getFieldValue($fieldIdentifier, $expectedLanguageCode), + $loadedUser->getFieldValue($fieldIdentifier) + ); + } + } + + /** + * Test that multi-language logic for the loadUsersByEmail method respects + * prioritized language list. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUsersByEmail + * @dataProvider getPrioritizedLanguageList + * + * @param string[] $prioritizedLanguages + * @param string|null $expectedLanguageCode language code of expected translation + */ + public function testLoadUsersByEmailWithPrioritizedLanguagesList( + array $prioritizedLanguages, + $expectedLanguageCode + ) { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + $user = $this->createMultiLanguageUser(); + + // load, with prioritized languages, users by email + $loadedUsers = $userService->loadUsersByEmail($user->email, $prioritizedLanguages); + + foreach ($loadedUsers as $loadedUser) { + if ($expectedLanguageCode === null) { + $expectedLanguageCode = $loadedUser->contentInfo->mainLanguageCode; + } + self::assertEquals( + $loadedUser->getName($expectedLanguageCode), + $loadedUser->getName() + ); + + foreach (['first_name', 'last_name', 'signature'] as $fieldIdentifier) { + self::assertEquals( + $loadedUser->getFieldValue($fieldIdentifier, $expectedLanguageCode), + $loadedUser->getFieldValue($fieldIdentifier) + ); + } + } + } + + /** + * Test that multi-language logic for the loadUserGroupsOfUser method respects + * prioritized language list. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserGroupsOfUser + * @dataProvider getPrioritizedLanguageList + * + * @param string[] $prioritizedLanguages + * @param string|null $expectedLanguageCode language code of expected translation + */ + public function testLoadUserGroupsOfUserWithPrioritizedLanguagesList( + array $prioritizedLanguages, + $expectedLanguageCode + ) { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + $userGroup = $this->createMultiLanguageUserGroup(); + $user = $this->createMultiLanguageUser($userGroup->id); + + $userGroups = $userService->loadUserGroupsOfUser($user, 0, 25, $prioritizedLanguages); + foreach ($userGroups as $userGroup) { + self::assertEquals( + $userGroup->getName($expectedLanguageCode), + $userGroup->getName() + ); + self::assertEquals( + $userGroup->getFieldValue('description', $expectedLanguageCode), + $userGroup->getFieldValue('description') + ); + } + } + + /** + * Test that multi-language logic for the loadUsersOfUserGroup method respects + * prioritized language list. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUsersOfUserGroup + * @dataProvider getPrioritizedLanguageList + * + * @param string[] $prioritizedLanguages + * @param string|null $expectedLanguageCode language code of expected translation + */ + public function testLoadUsersOfUserGroupWithPrioritizedLanguagesList( + array $prioritizedLanguages, + $expectedLanguageCode + ) { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + // create parent user group + $userGroup = $this->createMultiLanguageUserGroup(); + // add two users to the created parent user group + $this->createMultiLanguageUser($userGroup->id); + $this->createMultiLanguageUser($userGroup->id); + + // test loading of users via user group with prioritized languages list + $users = $userService->loadUsersOfUserGroup($userGroup, 0, 25, $prioritizedLanguages); + foreach ($users as $user) { + if ($expectedLanguageCode === null) { + $expectedLanguageCode = $user->contentInfo->mainLanguageCode; + } + self::assertEquals( + $user->getName($expectedLanguageCode), + $user->getName() + ); + + foreach (['first_name', 'last_name', 'signature'] as $fieldIdentifier) { + self::assertEquals( + $user->getFieldValue($fieldIdentifier, $expectedLanguageCode), + $user->getFieldValue($fieldIdentifier) + ); + } + } + } + + /** + * Get prioritized languages list data. + * + * Test cases using this data provider should expect the following arguments: + * <code> + * array $prioritizedLanguagesList + * string $expectedLanguage (if null - use main language) + * </code> + * + * @return array + */ + public function getPrioritizedLanguageList() + { + return [ + [[], null], + [['eng-US'], 'eng-US'], + [['eng-GB'], 'eng-GB'], + [['eng-US', 'eng-GB'], 'eng-US'], + [['eng-GB', 'eng-US'], 'eng-GB'], + // use non-existent group as the first one + [['ger-DE'], null], + [['ger-DE', 'eng-GB'], 'eng-GB'], + ]; + } + + /** + * @param int $parentGroupId + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup + */ + private function createMultiLanguageUserGroup($parentGroupId = 4) + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + // create user group with multiple translations + $parentGroupId = $this->generateId('group', $parentGroupId); + $parentGroup = $userService->loadUserGroup($parentGroupId); + + $userGroupCreateStruct = $userService->newUserGroupCreateStruct('eng-US'); + $userGroupCreateStruct->setField('name', 'US user group', 'eng-US'); + $userGroupCreateStruct->setField('name', 'GB user group', 'eng-GB'); + $userGroupCreateStruct->setField('description', 'US user group description', 'eng-US'); + $userGroupCreateStruct->setField('description', 'GB user group description', 'eng-GB'); + $userGroupCreateStruct->alwaysAvailable = true; + + return $userService->createUserGroup($userGroupCreateStruct, $parentGroup); + } + + /** + * Create a user group fixture in a variable named <b>$userGroup</b>,. + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup + */ + private function createUserGroupVersion1() + { + $repository = $this->getRepository(); + + $mainGroupId = $this->generateId('group', 4); + /* BEGIN: Inline */ + // $mainGroupId is the ID of the main "Users" group + + $userService = $repository->getUserService(); + + // Load main group + $parentUserGroup = $userService->loadUserGroup($mainGroupId); + + // Instantiate a new create struct + $userGroupCreate = $userService->newUserGroupCreateStruct('eng-US'); + $userGroupCreate->setField('name', 'Example Group'); + + // Create the new user group + $userGroup = $userService->createUserGroup( + $userGroupCreate, + $parentUserGroup + ); + /* END: Inline */ + + return $userGroup; + } + + /** + * Create user with multiple translations of User Content fields. + * + * @param int $userGroupId User group ID (default 13 - Editors) + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + */ + private function createMultiLanguageUser($userGroupId = 13) + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + // Instantiate a create struct with mandatory properties + $randomLogin = md5(mt_rand() . time()); + $userCreateStruct = $userService->newUserCreateStruct( + $randomLogin, + "{$randomLogin}@example.com", + 'secret', + 'eng-US' + ); + $userCreateStruct->enabled = true; + $userCreateStruct->alwaysAvailable = true; + + // set field for each language + foreach (['eng-US', 'eng-GB'] as $languageCode) { + $userCreateStruct->setField('first_name', "{$languageCode} Example", $languageCode); + $userCreateStruct->setField('last_name', "{$languageCode} User", $languageCode); + $userCreateStruct->setField('signature', "{$languageCode} signature", $languageCode); + } + + // Load parent group for the user + $group = $userService->loadUserGroup($userGroupId); + + // Create a new user + return $userService->createUser($userCreateStruct, [$group]); + } + + /** + * Test for the createUser() method. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::createUser() + */ + public function testCreateUserWithDefaultPasswordHashTypeWhenHashTypeIsUnsupported(): void + { + $repository = $this->getRepository(); + $eventUserService = $repository->getUserService(); + + // Instantiate a create struct with mandatory properties. + $createStruct = $eventUserService->newUserCreateStruct( + 'user', + 'user@example.com', + 'secret', + 'eng-US' + ); + + // Set some fields required by the user ContentType. + $createStruct->setField('first_name', 'Example'); + $createStruct->setField('last_name', 'User'); + + // Get User fieldType. + $userFieldDef = null; + foreach ($createStruct->fields as $field) { + if ($field->fieldTypeIdentifier === 'ezuser') { + $userFieldDef = $field; + break; + } + } + + if (!$userFieldDef) { + $this->fail('User FieldType not found in userCreateStruct!'); + } + + /** @var \Ibexa\Core\FieldType\User\Value $userValue */ + $userValue = $userFieldDef->value; + + // Set not supported hash type. + $userValue->passwordHashType = 42424242; + + // Create a new user instance. + // 13 is ID of the "Editors" user group in an Ibexa demo installation. + $createdUser = $eventUserService->createUser($createStruct, [$eventUserService->loadUserGroup(13)]); + + self::assertEquals(User::DEFAULT_PASSWORD_HASH, $createdUser->hashAlgorithm); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserByToken + */ + public function testLoadUserByToken(): string + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $user = $this->createUserVersion1(); + + $userTokenUpdateStruct = new UserTokenUpdateStruct(); + $userTokenUpdateStruct->hashKey = md5('hash'); + $userTokenUpdateStruct->time = (new DateTime())->add(new DateInterval('PT1H')); + + $userService->updateUserToken($user, $userTokenUpdateStruct); + + $loadedUser = $userService->loadUserByToken($userTokenUpdateStruct->hashKey, Language::ALL); + $this->assertIsSameUser($user, $loadedUser); + + return $userTokenUpdateStruct->hashKey; + } + + /** + * Test updating User Token. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::updateUserToken() + * + * @depends testLoadUserByToken + * + * @param string $originalUserToken + */ + public function testUpdateUserToken($originalUserToken) + { + $repository = $this->getRepository(false); + $userService = $repository->getUserService(); + + $user = $userService->loadUserByToken($originalUserToken); + + $userTokenUpdateStruct = new UserTokenUpdateStruct(); + $userTokenUpdateStruct->hashKey = md5('my_updated_hash'); + $userTokenUpdateStruct->time = (new DateTime())->add(new DateInterval('PT1H')); + + $userService->updateUserToken($user, $userTokenUpdateStruct); + + $loadedUser = $userService->loadUserByToken($userTokenUpdateStruct->hashKey); + self::assertEquals($user, $loadedUser); + } + + /** + * Test invalidating (expiring) User Token. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::expireUserToken() + * + * @depends testLoadUserByToken + * + * @param string $userToken + */ + public function testExpireUserToken($userToken) + { + $this->expectException(NotFoundException::class); + + $repository = $this->getRepository(false); + $userService = $repository->getUserService(); + + // sanity check + $userService->loadUserByToken($userToken); + + $userService->expireUserToken($userToken); + + // should throw NotFoundException now + $userService->loadUserByToken($userToken); + } + + /** + * Test trying to load User by invalid Token. + * + * @covers \Ibexa\Contracts\Core\Repository\UserService::loadUserByToken + */ + public function testLoadUserByTokenThrowsNotFoundException(): void + { + $repository = $this->getRepository(); + $userService = $repository->getUserService(); + + $this->expectException(NotFoundException::class); + $userService->loadUserByToken('not_existing_token'); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserService::validatePassword() + */ + public function testValidatePasswordWithDefaultContext() + { + $userService = $this->getRepository()->getUserService(); + + /* BEGIN: Use Case */ + $errors = $userService->validatePassword('pass'); + /* END: Use Case */ + + $this->assertEmpty($errors); + } + + /** + * @covers \Ibexa\Contracts\Core\Repository\UserService::validatePassword() + * @dataProvider dataProviderForValidatePassword + */ + public function testValidatePassword(string $password, array $expectedErrors) + { + $userService = $this->getRepository()->getUserService(); + $contentType = $this->createUserContentTypeWithStrongPassword(); + + /* BEGIN: Use Case */ + $context = new PasswordValidationContext([ + 'contentType' => $contentType, + ]); + + $actualErrors = $userService->validatePassword($password, $context); + /* END: Use Case */ + + $this->assertEquals($expectedErrors, $actualErrors); + } + + public function testValidatePasswordReturnsErrorWhenOldPasswordIsReused(): void + { + $password = 'P@blish123!'; + + $userService = $this->getRepository()->getUserService(); + // Password expiration needs to be enabled + $contentType = $this->createUserContentTypeWithPasswordExpirationDate(); + + $user = $this->createTestUserWithPassword($password, $contentType); + + $context = new PasswordValidationContext([ + 'contentType' => $contentType, + 'user' => $user, + ]); + + $actualErrors = $userService->validatePassword($password, $context); + + $this->assertEquals( + [new ValidationError('New password cannot be the same as old password', null, [], 'password')], + $actualErrors + ); + } + + public function getDataForTestPasswordUpdateRespectsAllValidationSettings(): iterable + { + $oldPassword = 'P@blish123!'; + + yield 'require at least one upper case character' => [ + $oldPassword, + 'p@blish123!', + 'User password must include at least one upper case letter', + ]; + + yield 'require at least one lower case character' => [ + $oldPassword, + 'P@BLISH123!', + 'User password must include at least one lower case letter', + ]; + + yield 'require at least one numeric character' => [ + $oldPassword, + 'P@blishONETWOTHREE!', + 'User password must include at least one number', + ]; + + yield 'require at least one non-alphanumeric character' => [ + $oldPassword, + 'Publish123', + 'User password must include at least one special character', + ]; + + yield 'require min. length >= 8 chars' => [ + $oldPassword, + 'P@b123!', + 'User password must be at least 8 characters long', + ]; + + yield 'require new password' => [ + $oldPassword, + $oldPassword, + 'New password cannot be the same as old password', + ]; + } + + /** + * @dataProvider getDataForTestPasswordUpdateRespectsAllValidationSettings + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + * @throws \Exception + */ + public function testUpdateUserPasswordPerformsValidation( + string $oldPassword, + string $newPassword, + string $expectedExceptionMessage + ): void { + $userService = $this->getRepository()->getUserService(); + + $contentType = $this->createUserContentTypeWithStrongPassword(); + $user = $this->createTestUserWithPassword($oldPassword, $contentType); + + try { + $userService->updateUserPassword($user, $newPassword); + + self::fail( + sprintf( + 'Failed to get validation exception with message "%s"', + $expectedExceptionMessage + ) + ); + } catch (ContentFieldValidationException $e) { + $this->assertValidationErrorOccurs($e, $expectedExceptionMessage); + } + } + + /** + * Data provider for testValidatePassword. + * + * @return array + */ + public function dataProviderForValidatePassword(): array + { + return [ + [ + 'pass', + [ + new ValidationError('User password must be at least %length% characters long', null, [ + '%length%' => 8, + ], 'password'), + new ValidationError('User password must include at least one upper case letter', null, [], 'password'), + new ValidationError('User password must include at least one number', null, [], 'password'), + new ValidationError('User password must include at least one special character', null, [], 'password'), + ], + ], + [ + 'H@xxxi0R!!!', + [], + ], + ]; + } + + public function testGetPasswordInfo(): void + { + $userService = $this->getRepository()->getUserService(); + $contentType = $this->createUserContentTypeWithPasswordExpirationDate( + self::EXAMPLE_PASSWORD_TTL, + self::EXAMPLE_PASSWORD_TTL_WARNING + ); + + $user = $this->createTestUser($contentType); + + /* BEGIN: Use Case */ + $passwordInfo = $userService->getPasswordInfo($user); + /* END: Use Case */ + + $passwordUpdatedAt = $user->passwordUpdatedAt; + if ($passwordUpdatedAt instanceof DateTime) { + $passwordUpdatedAt = DateTimeImmutable::createFromFormat(DateTime::ATOM, $passwordUpdatedAt->format(DateTime::ATOM)); + } + + $expectedPasswordExpirationDate = $passwordUpdatedAt->add( + new DateInterval(sprintf('P%dD', self::EXAMPLE_PASSWORD_TTL)) + ); + + $expectedPasswordExpirationWarningDate = $passwordUpdatedAt->add( + new DateInterval(sprintf('P%dD', self::EXAMPLE_PASSWORD_TTL - self::EXAMPLE_PASSWORD_TTL_WARNING)) + ); + + $this->assertEquals(new PasswordInfo( + $expectedPasswordExpirationDate, + $expectedPasswordExpirationWarningDate + ), $passwordInfo); + } + + public function testGetPasswordInfoIfExpirationIsDisabled(): void + { + $userService = $this->getRepository()->getUserService(); + $contentType = $this->createUserContentTypeWithPasswordExpirationDate(null, null); + + $user = $this->createTestUser($contentType); + + /* BEGIN: Use Case */ + $passwordInfo = $userService->getPasswordInfo($user); + /* END: Use Case */ + + $this->assertEquals(new PasswordInfo(), $passwordInfo); + } + + public function testGetPasswordInfoIfExpirationWarningIsDisabled(): void + { + $userService = $this->getRepository()->getUserService(); + $contentType = $this->createUserContentTypeWithPasswordExpirationDate(self::EXAMPLE_PASSWORD_TTL, null); + + $user = $this->createTestUser($contentType); + + /* BEGIN: Use Case */ + $passwordInfo = $userService->getPasswordInfo($user); + /* END: Use Case */ + + $passwordUpdatedAt = $user->passwordUpdatedAt; + if ($passwordUpdatedAt instanceof DateTime) { + $passwordUpdatedAt = DateTimeImmutable::createFromFormat(DateTime::ATOM, $passwordUpdatedAt->format(DateTime::ATOM)); + } + + $expectedPasswordExpirationDate = $passwordUpdatedAt->add( + new DateInterval(sprintf('P%dD', self::EXAMPLE_PASSWORD_TTL)) + ); + + $this->assertEquals(new PasswordInfo($expectedPasswordExpirationDate, null), $passwordInfo); + } + + public function createTestUser(ContentType $contentType): User + { + return $this->createTestUserWithPassword(self::EXAMPLE_PASSWORD, $contentType); + } + + /** + * Creates a user with given password. + * + * @param string $password + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType $contentType + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + */ + private function createTestUserWithPassword(string $password, ContentType $contentType): User + { + $userService = $this->getRepository()->getUserService(); + // ID of the "Editors" user group in an Ibexa demo installation + $editorsGroupId = 13; + + // Instantiate a create struct with mandatory properties + $userCreate = $userService->newUserCreateStruct( + 'johndoe', + 'johndoe@example.com', + $password, + 'eng-US', + $contentType + ); + $userCreate->enabled = true; + $userCreate->setField('first_name', 'John'); + $userCreate->setField('last_name', 'Doe'); + + return $userService->createUser($userCreate, [ + $userService->loadUserGroup($editorsGroupId), + ]); + } + + /** + * Creates the User content type with password constraints. + * + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType + */ + private function createUserContentTypeWithStrongPassword(): ContentType + { + return $this->createUserContentTypeWithAccountSettings('user-with-strong-password', null, [ + 'PasswordValueValidator' => [ + 'requireAtLeastOneUpperCaseCharacter' => 1, + 'requireAtLeastOneLowerCaseCharacter' => 1, + 'requireAtLeastOneNumericCharacter' => 1, + 'requireAtLeastOneNonAlphanumericCharacter' => 1, + 'requireNewPassword' => 1, + 'minLength' => 8, + ], + ]); + } + + private function createUserContentTypeWithPasswordExpirationDate( + ?int $passwordTTL = self::EXAMPLE_PASSWORD_TTL, + ?int $passwordTTLWarning = self::EXAMPLE_PASSWORD_TTL_WARNING + ): ContentType { + return $this->createUserContentTypeWithAccountSettings('password-expiration', [ + 'PasswordTTL' => $passwordTTL, + 'PasswordTTLWarning' => $passwordTTLWarning, + ]); + } + + private function createUserContentTypeWithAccountSettings( + string $identifier, + ?array $fieldSetting = null, + ?array $validatorConfiguration = null + ): ContentType { + $repository = $this->getRepository(); + + $contentTypeService = $repository->getContentTypeService(); + $permissionResolver = $repository->getPermissionResolver(); + + $typeCreate = $contentTypeService->newContentTypeCreateStruct($identifier); + $typeCreate->mainLanguageCode = 'eng-GB'; + $typeCreate->urlAliasSchema = 'url|scheme'; + $typeCreate->nameSchema = 'name|scheme'; + $typeCreate->names = [ + 'eng-GB' => 'User: ' . $identifier, + ]; + $typeCreate->descriptions = [ + 'eng-GB' => '', + ]; + $typeCreate->creatorId = $this->generateId('user', $permissionResolver->getCurrentUserReference()->getUserId()); + $typeCreate->creationDate = $this->createDateTime(); + + $firstNameFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('first_name', 'ezstring'); + $firstNameFieldCreate->names = [ + 'eng-GB' => 'First name', + ]; + $firstNameFieldCreate->descriptions = [ + 'eng-GB' => '', + ]; + $firstNameFieldCreate->fieldGroup = 'default'; + $firstNameFieldCreate->position = 1; + $firstNameFieldCreate->isTranslatable = false; + $firstNameFieldCreate->isRequired = true; + $firstNameFieldCreate->isInfoCollector = false; + $firstNameFieldCreate->validatorConfiguration = [ + 'StringLengthValidator' => [ + 'minStringLength' => 0, + 'maxStringLength' => 0, + ], + ]; + $firstNameFieldCreate->fieldSettings = []; + $firstNameFieldCreate->isSearchable = true; + $firstNameFieldCreate->defaultValue = ''; + + $typeCreate->addFieldDefinition($firstNameFieldCreate); + + $lastNameFieldCreate = $contentTypeService->newFieldDefinitionCreateStruct('last_name', 'ezstring'); + $lastNameFieldCreate->names = [ + 'eng-GB' => 'Last name', + ]; + $lastNameFieldCreate->descriptions = [ + 'eng-GB' => '', + ]; + $lastNameFieldCreate->fieldGroup = 'default'; + $lastNameFieldCreate->position = 2; + $lastNameFieldCreate->isTranslatable = false; + $lastNameFieldCreate->isRequired = true; + $lastNameFieldCreate->isInfoCollector = false; + $lastNameFieldCreate->validatorConfiguration = [ + 'StringLengthValidator' => [ + 'minStringLength' => 0, + 'maxStringLength' => 0, + ], + ]; + $lastNameFieldCreate->fieldSettings = []; + $lastNameFieldCreate->isSearchable = true; + $lastNameFieldCreate->defaultValue = ''; + + $typeCreate->addFieldDefinition($lastNameFieldCreate); + + $accountFieldCreateStruct = $contentTypeService->newFieldDefinitionCreateStruct('user_account', 'ezuser'); + $accountFieldCreateStruct->names = [ + 'eng-GB' => 'User account', + ]; + $accountFieldCreateStruct->descriptions = [ + 'eng-GB' => '', + ]; + $accountFieldCreateStruct->fieldGroup = 'default'; + $accountFieldCreateStruct->position = 3; + $accountFieldCreateStruct->isTranslatable = false; + $accountFieldCreateStruct->isRequired = true; + $accountFieldCreateStruct->isInfoCollector = false; + $accountFieldCreateStruct->validatorConfiguration = $validatorConfiguration; + $accountFieldCreateStruct->fieldSettings = $fieldSetting; + $accountFieldCreateStruct->isSearchable = false; + $accountFieldCreateStruct->defaultValue = null; + + $typeCreate->addFieldDefinition($accountFieldCreateStruct); + + $contentTypeDraft = $contentTypeService->createContentType($typeCreate, [ + $contentTypeService->loadContentTypeGroupByIdentifier('Users'), + ]); + $contentTypeService->publishContentTypeDraft($contentTypeDraft); + + return $contentTypeService->loadContentTypeByIdentifier($identifier); + } + + /** + * @throws \Doctrine\DBAL\Exception + * @throws \ErrorException + */ + protected function updateRawPasswordHash(int $userId, int $newHashType): void + { + $connection = $this->getRawDatabaseConnection(); + $queryBuilder = $connection->createQueryBuilder(); + $queryBuilder + ->update(Gateway::USER_TABLE) + ->set('password_hash_type', ':wrong_hash_type') + ->where('contentobject_id = :user_id') + ->setParameter('wrong_hash_type', $newHashType, ParameterType::INTEGER) + ->setParameter('user_id', $userId); + + $queryBuilder->execute(); + } + + private function assertIsSameUser(User $expectedUser, User $actualUser): void + { + self::assertSame($expectedUser->getUserId(), $actualUser->getUserId()); + self::assertSame($expectedUser->getName(), $actualUser->getName()); + self::assertSame($expectedUser->login, $actualUser->login); + self::assertSame($expectedUser->email, $actualUser->email); + } +} + +class_alias(UserServiceTest::class, 'eZ\Publish\API\Repository\Tests\UserServiceTest'); diff --git a/tests/integration/Core/Repository/Values/Content/DefaultLanguageCodeForContentTest.php b/tests/integration/Core/Repository/Values/Content/DefaultLanguageCodeForContentTest.php index a614437f2f..2a1397b1e1 100644 --- a/tests/integration/Core/Repository/Values/Content/DefaultLanguageCodeForContentTest.php +++ b/tests/integration/Core/Repository/Values/Content/DefaultLanguageCodeForContentTest.php @@ -8,14 +8,14 @@ namespace Ibexa\Tests\Integration\Core\Repository\Values\Content; -use eZ\Publish\API\Repository\Tests\BaseTest; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; final class DefaultLanguageCodeForContentTest extends BaseTest { /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testDefaultLanguageCodeForCreatedContentWithoutPrioritizedLanguage(): void { @@ -35,9 +35,9 @@ public function testDefaultLanguageCodeForCreatedContentWithoutPrioritizedLangua } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testDefaultLanguageCodeForCreatedContentWithPrioritizedLanguage(): void { diff --git a/tests/integration/Core/Repository/Values/Content/Query/Aggregation/Ranges/DateTimeStepRangesGeneratorTest.php b/tests/integration/Core/Repository/Values/Content/Query/Aggregation/Ranges/DateTimeStepRangesGeneratorTest.php new file mode 100644 index 0000000000..6c662f5483 --- /dev/null +++ b/tests/integration/Core/Repository/Values/Content/Query/Aggregation/Ranges/DateTimeStepRangesGeneratorTest.php @@ -0,0 +1,114 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\Values\Content\Query\Aggregation\Ranges; + +use DateInterval; +use DateTimeImmutable; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Ranges\DateTimeStepRangesGenerator; +use PHPUnit\Framework\TestCase; + +final class DateTimeStepRangesGeneratorTest extends TestCase +{ + public function testGenerateOpenRangesSequence(): void + { + self::assertGeneratorResults( + [ + $this->createRange(Range::INF, '01-01-2023'), + $this->createRange('01-01-2023', '02-01-2023'), + $this->createRange('02-01-2023', '03-01-2023'), + $this->createRange('03-01-2023', Range::INF), + ], + new DateTimeStepRangesGenerator( + new DateTimeImmutable('01-01-2023 00:00:00'), + new DateTimeImmutable('03-01-2023 00:00:00') + ) + ); + } + + public function testGenerateCloseRangesSequence(): void + { + $generator = new DateTimeStepRangesGenerator( + new DateTimeImmutable('01-01-2023 00:00:00'), + new DateTimeImmutable('03-01-2023 00:00:00') + ); + $generator->setLeftOpen(false); + $generator->setRightOpen(false); + + self::assertGeneratorResults( + [ + $this->createRange('01-01-2023', '02-01-2023'), + $this->createRange('02-01-2023', '03-01-2023'), + ], + $generator + ); + } + + public function testGenerateRangesWithCustomStep(): void + { + $generator = new DateTimeStepRangesGenerator( + new DateTimeImmutable('01-01-2023 00:00:00'), + new DateTimeImmutable('05-01-2023 00:00:00') + ); + $generator->setStep(new DateInterval('P2D')); + + self::assertGeneratorResults( + [ + $this->createRange(Range::INF, '01-01-2023'), + $this->createRange('01-01-2023', '03-01-2023'), + $this->createRange('03-01-2023', '05-01-2023'), + $this->createRange('05-01-2023', Range::INF), + ], + $generator + ); + } + + public function testGenerateInfRangesSequence(): void + { + $generator = new DateTimeStepRangesGenerator( + new DateTimeImmutable('01-01-1970 00:00:00'), + new DateTimeImmutable('01-01-1970 00:00:00'), + ); + + self::assertGeneratorResults( + [ + Range::ofDateTime(Range::INF, Range::INF), + ], + $generator + ); + } + + public function testGenerateEmptyRangesSequence(): void + { + $generator = new DateTimeStepRangesGenerator( + new DateTimeImmutable('01-01-1970 00:00:00'), + new DateTimeImmutable('01-01-1970 00:00:00'), + ); + $generator->setLeftOpen(false); + $generator->setRightOpen(false); + + self::assertGeneratorResults([], $generator); + } + + private function createRange(?string $start, ?string $end): Range + { + return Range::ofDateTime( + $start !== null ? new DateTimeImmutable($start . ' 00:00:00') : null, + $end !== null ? new DateTimeImmutable($end . ' 00:00:00') : null + ); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range[] $expectedResult + */ + private static function assertGeneratorResults(array $expectedResult, DateTimeStepRangesGenerator $generator): void + { + self::assertEquals($expectedResult, $generator->generate()); + } +} diff --git a/tests/integration/Core/Repository/Values/Content/Query/Aggregation/Ranges/FloatStepRangesGeneratorTest.php b/tests/integration/Core/Repository/Values/Content/Query/Aggregation/Ranges/FloatStepRangesGeneratorTest.php new file mode 100644 index 0000000000..f41391acab --- /dev/null +++ b/tests/integration/Core/Repository/Values/Content/Query/Aggregation/Ranges/FloatStepRangesGeneratorTest.php @@ -0,0 +1,91 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\Values\Content\Query\Aggregation\Ranges; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Ranges\FloatStepRangesGenerator; +use PHPUnit\Framework\TestCase; + +final class FloatStepRangesGeneratorTest extends TestCase +{ + public function testGenerateOpenRangesSequence(): void + { + self::assertGeneratorResults( + [ + Range::ofFloat(Range::INF, 1.0), + Range::ofFloat(1.0, 2.0), + Range::ofFloat(2.0, 3.0), + Range::ofFloat(3.0, Range::INF), + ], + new FloatStepRangesGenerator(1.0, 3.0) + ); + } + + public function testGenerateCloseRangesSequence(): void + { + $generator = new FloatStepRangesGenerator(1.0, 3.0); + $generator->setLeftOpen(false); + $generator->setRightOpen(false); + + self::assertGeneratorResults( + [ + Range::ofFloat(1.0, 2.0), + Range::ofFloat(2.0, 3.0), + ], + $generator + ); + } + + public function testGenerateRangesWithCustomStep(): void + { + $generator = new FloatStepRangesGenerator(1.0, 10.0); + $generator->setStep(2.0); + + self::assertGeneratorResults( + [ + Range::ofFloat(Range::INF, 1.0), + Range::ofFloat(1.0, 3.0), + Range::ofFloat(3.0, 5.0), + Range::ofFloat(5.0, 7.0), + Range::ofFloat(7.0, 9.0), + Range::ofFloat(10.0, Range::INF), + ], + $generator + ); + } + + public function testGenerateInfRangesSequence(): void + { + $generator = new FloatStepRangesGenerator(0.0, 0.0); + + self::assertGeneratorResults( + [ + Range::ofFloat(Range::INF, Range::INF), + ], + $generator + ); + } + + public function testGenerateEmptyRangesSequence(): void + { + $generator = new FloatStepRangesGenerator(0.0, 0.0); + $generator->setLeftOpen(false); + $generator->setRightOpen(false); + + self::assertGeneratorResults([], $generator); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range[] $expectedResult + */ + private static function assertGeneratorResults(array $expectedResult, FloatStepRangesGenerator $generator): void + { + self::assertEquals($expectedResult, $generator->generate()); + } +} diff --git a/tests/integration/Core/Repository/Values/Content/Query/Aggregation/Ranges/IntegerStepRangesGeneratorTest.php b/tests/integration/Core/Repository/Values/Content/Query/Aggregation/Ranges/IntegerStepRangesGeneratorTest.php new file mode 100644 index 0000000000..272e2e095b --- /dev/null +++ b/tests/integration/Core/Repository/Values/Content/Query/Aggregation/Ranges/IntegerStepRangesGeneratorTest.php @@ -0,0 +1,94 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\Values\Content\Query\Aggregation\Ranges; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Ranges\IntegerStepRangesGenerator; +use PHPUnit\Framework\TestCase; + +final class IntegerStepRangesGeneratorTest extends TestCase +{ + public function testGenerateOpenRangesSequence(): void + { + self::assertGeneratorResults( + [ + Range::ofInt(Range::INF, 1), + Range::ofInt(1, 2), + Range::ofInt(2, 3), + Range::ofInt(3, Range::INF), + ], + new IntegerStepRangesGenerator(1, 3) + ); + } + + public function testGenerateCloseRangesSequence(): void + { + $generator = new IntegerStepRangesGenerator(1, 3); + $generator->setLeftOpen(false); + $generator->setRightOpen(false); + + self::assertGeneratorResults( + [ + Range::ofInt(1, 2), + Range::ofInt(2, 3), + ], + $generator + ); + } + + public function testGenerateRangesWithCustomStep(): void + { + $generator = new IntegerStepRangesGenerator(1, 10); + $generator->setStep(2); + + self::assertGeneratorResults( + [ + Range::ofInt(Range::INF, 1), + Range::ofInt(1, 3), + Range::ofInt(3, 5), + Range::ofInt(5, 7), + Range::ofInt(7, 9), + Range::ofInt(10, Range::INF), + ], + $generator + ); + } + + public function testGenerateInfRangesSequence(): void + { + $generator = new IntegerStepRangesGenerator(0, 0); + + self::assertGeneratorResults( + [ + Range::ofInt(Range::INF, Range::INF), + ], + $generator + ); + } + + public function testGenerateEmptyRangesSequence(): void + { + $generator = new IntegerStepRangesGenerator(0, 0); + $generator->setLeftOpen(false); + $generator->setRightOpen(false); + + self::assertGeneratorResults([], $generator); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range[] $expectedResult + */ + private static function assertGeneratorResults(array $expectedResult, IntegerStepRangesGenerator $generator): void + { + self::assertEquals( + $expectedResult, + iterator_to_array($generator->generate()) + ); + } +} diff --git a/tests/integration/Core/Repository/Values/User/Limitation/BaseLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/BaseLimitationTest.php new file mode 100644 index 0000000000..0dc2300afd --- /dev/null +++ b/tests/integration/Core/Repository/Values/User/Limitation/BaseLimitationTest.php @@ -0,0 +1,135 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; + +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\Role; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; + +/** + * Abstract base class for limitation tests. + * + * @group integration + * @group limitation + */ +abstract class BaseLimitationTest extends BaseTest +{ + /** + * Creates a published wiki page. + */ + protected function createWikiPage(): Content + { + $repository = $this->getRepository(); + + $contentService = $repository->getContentService(); + /* BEGIN: Inline */ + $draft = $this->createWikiPageDraft(); + + $content = $contentService->publishVersion($draft->versionInfo); + /* END: Inline */ + + return $content; + } + + /** + * Creates a fresh clean content draft. + */ + protected function createWikiPageDraft(): Content + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + // $parentLocationId is the id of the /Home/Contact-Us node + $parentLocationId = $this->generateId('location', 60); + + $locationCreate = $this->createWikiPageLocationCreateStruct($parentLocationId); + $wikiPageCreate = $this->createWikiPageContentCreateStruct(); + + // Create a draft + $draft = $contentService->createContent( + $wikiPageCreate, + [$locationCreate] + ); + + return $draft; + } + + /** + * Creates a basic LocationCreateStruct. + */ + protected function createWikiPageLocationCreateStruct(int $parentLocationId): LocationCreateStruct + { + $repository = $this->getRepository(); + $locationService = $repository->getLocationService(); + + $locationCreate = $locationService->newLocationCreateStruct($parentLocationId); + + $locationCreate->priority = 23; + $locationCreate->hidden = true; + $locationCreate->remoteId = '0123456789abcdef0123456789abcdef'; + $locationCreate->sortField = Location::SORT_FIELD_NODE_ID; + $locationCreate->sortOrder = Location::SORT_ORDER_DESC; + + return $locationCreate; + } + + /** + * Creates a basic ContentCreateStruct. + */ + protected function createWikiPageContentCreateStruct( + ?int $ownerId = null, + ?string $remoteId = 'abcdef0123456789abcdef0123456789' + ): ContentCreateStruct { + $repository = $this->getRepository(); + $contentTypeService = $repository->getContentTypeService(); + $contentService = $repository->getContentService(); + + $sectionId = $this->generateId('section', 1); + + // Load content type + $wikiPageType = $contentTypeService->loadContentTypeByIdentifier('wiki_page'); + + // Configure new content object + $wikiPageCreate = $contentService->newContentCreateStruct($wikiPageType, 'eng-US'); + $wikiPageCreate->setField('title', 'An awesome wiki page'); + + if (null === $remoteId) { + $remoteId = md5(time()); + } + + $wikiPageCreate->remoteId = $remoteId; + + // $sectionId is the ID of section 1 + $wikiPageCreate->sectionId = $sectionId; + $wikiPageCreate->alwaysAvailable = true; + + // Optional: Configure owner + if ($ownerId !== null) { + $wikiPageCreate->ownerId = $ownerId; + } + + return $wikiPageCreate; + } + + protected function addPolicyToRole(string $roleIdentifier, PolicyCreateStruct $policyCreateStruct): Role + { + $roleService = $this->getRepository()->getRoleService(); + + $role = $roleService->loadRoleByIdentifier($roleIdentifier); + $roleDraft = $roleService->createRoleDraft($role); + $roleService->addPolicyByRoleDraft($roleDraft, $policyCreateStruct); + $roleService->publishRoleDraft($roleDraft); + + return $roleService->loadRoleByIdentifier($roleIdentifier); + } +} + +class_alias(BaseLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\BaseLimitationTest'); diff --git a/tests/integration/Core/Repository/Values/User/Limitation/ChangeOwnerLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/ChangeOwnerLimitationTest.php new file mode 100644 index 0000000000..81f1ff6e97 --- /dev/null +++ b/tests/integration/Core/Repository/Values/User/Limitation/ChangeOwnerLimitationTest.php @@ -0,0 +1,190 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\RoleService; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ChangeOwnerLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft; +use Ibexa\Contracts\Core\Repository\Values\User\Role; +use Ibexa\Contracts\Core\Repository\Values\User\RoleDraft; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use RuntimeException; + +/** + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ChangeOwnerLimitation + * @group integration + * @group limitation + */ +final class ChangeOwnerLimitationTest extends BaseLimitationTest +{ + private PermissionResolver $permissionResolver; + + private RoleService $roleService; + + private ContentService $contentService; + + protected function setUp(): void + { + $repository = $this->getRepository(); + + $this->permissionResolver = $repository->getPermissionResolver(); + $this->roleService = $repository->getRoleService(); + $this->contentService = $repository->getContentService(); + } + + /** + * @return array<array{ + * 0: \Ibexa\Contracts\Core\Repository\Values\User\User, + * 1: int, + * 2: int[] + * }> + */ + public function getDataForGrantedAccess(): array + { + $otherUserId1 = 123; + $otherUserId2 = 456; + + return [ + [null, []], + [null, [-1]], + [null, [$otherUserId1, $otherUserId2, -1]], + [$otherUserId1, [$otherUserId1, $otherUserId2, -1]], + [$otherUserId1, []], + ]; + } + + /** + * @return array<array{ + * 0: \Ibexa\Contracts\Core\Repository\Values\User\User, + * 1: int, + * 2: int[] + * }> + */ + public function getDataForDeniedAccess(): array + { + $otherUserId1 = 123; + $otherUserId2 = 456; + $otherUserId3 = 789; + + return [ + [$otherUserId1, [-1]], + [$otherUserId1, [$otherUserId2, $otherUserId3]], + ]; + } + + /** + * @dataProvider getDataForGrantedAccess + * + * @param int[] $limitationValues + */ + public function testChangeOwnerLimitationAllowed(?int $ownerId, array $limitationValues): void + { + $currentUser = $this->createUserVersion1('current_user', null, null, 42); + $ownerId = $ownerId ?? $currentUser->id; + + $this->createTestCaseDraft($currentUser, $ownerId, $limitationValues); + + $this->expectNotToPerformAssertions(); + } + + /** + * @dataProvider getDataForDeniedAccess + * + * @param int[] $limitationValues + */ + public function testChangeOwnerLimitationDenied(?int $ownerId, array $limitationValues): void + { + $currentUser = $this->createUserVersion1('current_user', null, null, 42); + $ownerId = $ownerId ?? $currentUser->id; + + $this->expectException(UnauthorizedException::class); + $this->createTestCaseDraft($currentUser, $ownerId, $limitationValues); + } + + /** + * @param int[] $limitationValues + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + private function createTestCaseDraft( + User $currentUser, + int $ownerId, + array $limitationValues + ): void { + $role = $this->updateRoleWithChangeOwnerLimitation( + $this->roleService->loadRoleByIdentifier('Anonymous'), + $limitationValues + ); + $this->roleService->assignRoleToUser($role, $currentUser); + $this->permissionResolver->setCurrentUserReference($currentUser); + + $contentCreateStruct = $this->createWikiPageContentCreateStruct($ownerId); + $locationCreateStruct = $this->createWikiPageLocationCreateStruct($this->generateId('location', 2)); + + $this->contentService->createContent($contentCreateStruct, [$locationCreateStruct]); + } + + /** + * @throws \RuntimeException + */ + private function getContentCreatePolicyDraft(RoleDraft $role): PolicyDraft + { + foreach ($role->getPolicies() as $policy) { + if ($policy->module === 'content' && $policy->function === 'create') { + return $policy; + } + } + + throw new RuntimeException('No content:create policy found.'); + } + + private function hasContentCreatePolicy(RoleDraft $role): bool + { + foreach ($role->getPolicies() as $policy) { + if ($policy->module === 'content' && $policy->function === 'create') { + return true; + } + } + + return false; + } + + /** + * @param int[] $limitationValues + */ + private function updateRoleWithChangeOwnerLimitation( + Role $role, + array $limitationValues + ): Role { + $roleDraft = $this->roleService->createRoleDraft($role); + + if (!$this->hasContentCreatePolicy($roleDraft)) { + $createPolicyStruct = $this->roleService->newPolicyCreateStruct('content', 'create'); + $roleDraft = $this->roleService->addPolicyByRoleDraft($roleDraft, $createPolicyStruct); + } + + $contentCreatePolicyDraft = $this->getContentCreatePolicyDraft($roleDraft); + + $policyUpdate = $this->roleService->newPolicyUpdateStruct(); + $policyUpdate->addLimitation( + new ChangeOwnerLimitation($limitationValues) + ); + $this->roleService->updatePolicyByRoleDraft( + $roleDraft, + $contentCreatePolicyDraft, + $policyUpdate + ); + $this->roleService->publishRoleDraft($roleDraft); + + return $this->roleService->loadRoleByIdentifier($role->identifier); + } +} diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ContentTypeLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/ContentTypeLimitationTest.php similarity index 84% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/ContentTypeLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/ContentTypeLimitationTest.php index 188ae38b4a..e98cfc7e6c 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ContentTypeLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/ContentTypeLimitationTest.php @@ -4,16 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation; /** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation} - * class. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation * @group integration * @group limitation */ @@ -22,8 +19,6 @@ class ContentTypeLimitationTest extends BaseLimitationTest /** * Test for the ContentTypeLimitation. * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation - * * @throws \ErrorException */ public function testContentTypeLimitationAllow() @@ -42,7 +37,7 @@ public function testContentTypeLimitationAllow() $role = $roleService->loadRoleByIdentifier('Editor'); $roleDraft = $roleService->createRoleDraft($role); $editPolicy = null; - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy */ foreach ($roleDraft->getPolicies() as $policy) { if ('content' != $policy->module || 'edit' != $policy->function) { continue; @@ -94,13 +89,11 @@ public function testContentTypeLimitationAllow() /** * Test for the ContentTypeLimitation. * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation - * * @throws \ErrorException */ public function testContentTypeLimitationForbid() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -116,7 +109,7 @@ public function testContentTypeLimitationForbid() $role = $roleService->loadRoleByIdentifier('Editor'); $roleDraft = $roleService->createRoleDraft($role); $editPolicy = null; - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy */ foreach ($roleDraft->getPolicies() as $policy) { if ('content' != $policy->module || 'edit' != $policy->function) { continue; @@ -154,15 +147,11 @@ public function testContentTypeLimitationForbid() } /** - * Test for the ContentTypeLimitation. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation - * * @throws \ErrorException */ public function testContentTypeLimitationForbidVariant() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -179,7 +168,7 @@ public function testContentTypeLimitationForbidVariant() $roleDraft = $roleService->createRoleDraft($role); // Search for the new policy instance $policy = null; - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy */ $editPolicy = null; foreach ($roleDraft->getPolicies() as $policy) { if ('content' != $policy->module || 'edit' != $policy->function) { @@ -225,3 +214,5 @@ public function testContentTypeLimitationForbidVariant() /* END: Use Case */ } } + +class_alias(ContentTypeLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\ContentTypeLimitationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/LanguageLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php similarity index 79% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/LanguageLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php index 840bd17b5d..ccad8afbfc 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/LanguageLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/LanguageLimitationTest.php @@ -6,22 +6,22 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct; -use eZ\Publish\API\Repository\Values\User\Limitation\LanguageLimitation; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\SPI\Limitation\Target\Builder\VersionBuilder; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; + +use Ibexa\Contracts\Core\Limitation\Target\Builder\VersionBuilder; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\LanguageLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; /** * Test cases for ContentService APIs calls made by user with LanguageLimitation on chosen policies. * - * @uses \eZ\Publish\API\Repository\Values\User\Limitation\LanguageLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\LanguageLimitation * * @group integration * @group authorization @@ -44,11 +44,11 @@ class LanguageLimitationTest extends BaseTest * @param array $allowedTranslationsList list of translations (language codes) which editor can modify. * @param string $login * - * @return \eZ\Publish\API\Repository\Values\User\User + * @return \Ibexa\Contracts\Core\Repository\Values\User\User * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ private function createEditorUserWithLanguageLimitation( array $allowedTranslationsList, @@ -102,9 +102,9 @@ public function providerForCreateAndPublishContent(): array * * @dataProvider providerForCreateAndPublishContent * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testCreateAndPublishContent(array $names, array $allowedTranslationsList): void { @@ -124,18 +124,18 @@ public function testCreateAndPublishContent(array $names, array $allowedTranslat } /** - * @covers \eZ\Publish\API\Repository\PermissionResolver::canUser + * @covers \Ibexa\Contracts\Core\Repository\PermissionResolver::canUser * * @dataProvider providerForCanUserWithLimitationTargets * * @param array $folderNames names of a folder to create as test content * @param array $allowedTranslationsList a list of language codes of translations a user is allowed to edit - * @param \eZ\Publish\SPI\Limitation\Target[] $targets + * @param \Ibexa\Contracts\Core\Limitation\Target[] $targets * @param bool $expectedCanUserResult * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testCanUserWithLimitationTargets( string $policyModule, @@ -174,7 +174,7 @@ public function testCanUserWithLimitationTargets( * * @return array * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function providerForCanUserWithLimitationTargets(): array { @@ -278,13 +278,13 @@ public function providerForPublishVersionWithLanguageLimitation(): array * * @dataProvider providerForPublishVersionWithLanguageLimitation * - * @covers \eZ\Publish\API\Repository\ContentService::createContentDraft - * @covers \eZ\Publish\API\Repository\ContentService::updateContent - * @covers \eZ\Publish\API\Repository\ContentService::publishVersion + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException * @throws \Exception */ public function testPublishVersion( @@ -332,13 +332,13 @@ public function testPublishVersion( * * @dataProvider providerForPublishVersionWithLanguageLimitation * - * @covers \eZ\Publish\API\Repository\ContentService::createContentDraft - * @covers \eZ\Publish\API\Repository\ContentService::updateContent - * @covers \eZ\Publish\API\Repository\ContentService::publishVersion + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContentDraft + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::publishVersion * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testPublishVersionIsNotAllowedIfModifiedOtherTranslations(array $names): void { @@ -364,9 +364,9 @@ public function testPublishVersionIsNotAllowedIfModifiedOtherTranslations(array } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testPublishVersionTranslation(): void { @@ -399,9 +399,9 @@ public function testPublishVersionTranslation(): void } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testPublishVersionTranslationIsNotAllowed(): void { @@ -424,9 +424,9 @@ public function testPublishVersionTranslationIsNotAllowed(): void } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testPublishVersionTranslationIsNotAllowedWithTwoEditors(): void { @@ -467,9 +467,9 @@ public function testPublishVersionTranslationIsNotAllowedWithTwoEditors(): void } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testPublishVersionTranslationWhenUserHasNoAccessToAllLanguages(): void { @@ -497,10 +497,10 @@ public function testPublishVersionTranslationWhenUserHasNoAccessToAllLanguages() /** * @dataProvider providerForPrepareDataForTestsWithLanguageLimitationAndDifferentContentTranslations * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function testCopyContentWithLanguageLimitationAndDifferentContentTranslations( array $limitationValues, @@ -530,11 +530,11 @@ public function testCopyContentWithLanguageLimitationAndDifferentContentTranslat /** * @dataProvider providerForPrepareDataForTestsWithLanguageLimitationAndDifferentContentTranslations * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function testCopySubtreeWithLanguageLimitationAndDifferentContentTranslations( array $limitationValues, @@ -568,11 +568,11 @@ public function testCopySubtreeWithLanguageLimitationAndDifferentContentTranslat /** * @dataProvider providerForPrepareDataForTestsWithLanguageLimitationAndDifferentContentTranslations * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function testMoveSubtreeWithLanguageLimitationAndDifferentContentTranslations( array $limitationValues, @@ -609,10 +609,10 @@ public function testMoveSubtreeWithLanguageLimitationAndDifferentContentTranslat /** * @dataProvider providerForPrepareDataForTestsWithLanguageLimitationAndDifferentContentTranslations * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function testSwapLocationWithLanguageLimitationAndDifferentContentTranslations( array $limitationValues, @@ -648,10 +648,10 @@ public function testSwapLocationWithLanguageLimitationAndDifferentContentTransla /** * @dataProvider providerForPrepareDataForTestsWithLanguageLimitationAndDifferentContentTranslations * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function testHideLocationWithLanguageLimitationAndDifferentContentTranslations( array $limitationValues, @@ -685,10 +685,10 @@ public function testHideLocationWithLanguageLimitationAndDifferentContentTransla /** * @dataProvider providerForPrepareDataForTestsWithLanguageLimitationAndDifferentContentTranslations * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function testUnhideLocationWithLanguageLimitationAndDifferentContentTranslations( array $limitationValues, @@ -722,10 +722,10 @@ public function testUnhideLocationWithLanguageLimitationAndDifferentContentTrans /** * @dataProvider providerForPrepareDataForTestsWithLanguageLimitationAndDifferentContentTranslations * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function testHideContentWithLanguageLimitationAndDifferentContentTranslations( array $limitationValues, @@ -755,10 +755,10 @@ public function testHideContentWithLanguageLimitationAndDifferentContentTranslat /** * @dataProvider providerForPrepareDataForTestsWithLanguageLimitationAndDifferentContentTranslations * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function testRevealContentWithLanguageLimitationAndDifferentContentTranslations( array $limitationValues, @@ -788,10 +788,10 @@ public function testRevealContentWithLanguageLimitationAndDifferentContentTransl /** * @dataProvider providerForPrepareDataForTestsWithLanguageLimitationAndDifferentContentTranslations * - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException */ public function testUpdateLocationWithLanguageLimitationAndDifferentContentTranslations( array $limitationValues, @@ -825,10 +825,10 @@ public function testUpdateLocationWithLanguageLimitationAndDifferentContentTrans /** * @dataProvider providerForPrepareDataForTestsWithLanguageLimitationAndDifferentContentTranslations * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException */ public function testPrepareDataForTestsWithLanguageLimitationAndDifferentContentTranslations( array $limitationValues, @@ -864,13 +864,13 @@ public function providerForPrepareDataForTestsWithLanguageLimitationAndDifferent } /** - * @param \eZ\Publish\API\Repository\ContentService $contentService + * @param \Ibexa\Contracts\Core\Repository\ContentService $contentService * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ private function createMultilingualFolderDraft(ContentService $contentService): Content { @@ -885,3 +885,5 @@ private function createMultilingualFolderDraft(ContentService $contentService): return $contentService->createContentDraft($publishedContent->contentInfo); } } + +class_alias(LanguageLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\LanguageLimitationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/LocationLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/LocationLimitationTest.php similarity index 77% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/LocationLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/LocationLimitationTest.php index 67131d0563..b7089c4248 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/LocationLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/LocationLimitationTest.php @@ -4,16 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\LocationLimitation; /** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation} - * class. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\LocationLimitation * @group integration * @group limitation */ @@ -22,7 +19,7 @@ class LocationLimitationTest extends BaseLimitationTest /** * Tests a LocationLimitation. * - * @see eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\LocationLimitation */ public function testLocationLimitationAllow() { @@ -57,14 +54,9 @@ public function testLocationLimitationAllow() ); } - /** - * Tests a LocationLimitation. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation - */ public function testLocationLimitationForbid() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -96,3 +88,5 @@ public function testLocationLimitationForbid() /* END: Use Case */ } } + +class_alias(LocationLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\LocationLimitationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/NewObjectStateLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/NewObjectStateLimitationTest.php similarity index 79% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/NewObjectStateLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/NewObjectStateLimitationTest.php index f8f2aeda8f..32cb00d697 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/NewObjectStateLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/NewObjectStateLimitationTest.php @@ -4,28 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\NewObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\NewObjectStateLimitation; /** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\NewObjectStateLimitation} - * class. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\NewObjectStateLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\NewObjectStateLimitation * @group integration * @group limitation */ class NewObjectStateLimitationTest extends BaseLimitationTest { - /** - * Tests a NewObjectStateLimitation. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation\NewObjectStateLimitation - * - * @throws \ErrorException if a mandatory test fixture not exists. - */ public function testNewObjectStateLimitationAllow() { $repository = $this->getRepository(); @@ -66,13 +56,13 @@ public function testNewObjectStateLimitationAllow() /** * Tests a NewObjectStateLimitation. * - * @see eZ\Publish\API\Repository\Values\User\Limitation\NewObjectStateLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\NewObjectStateLimitation * * @throws \ErrorException if a mandatory test fixture not exists. */ public function testNewObjectStateLimitationForbid() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $lockedState = $this->generateId('objectstate', 1); @@ -113,3 +103,5 @@ public function testNewObjectStateLimitationForbid() /* END: Use Case */ } } + +class_alias(NewObjectStateLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\NewObjectStateLimitationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/NewSectionLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/NewSectionLimitationTest.php similarity index 80% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/NewSectionLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/NewSectionLimitationTest.php index 755948f70e..e5f4d1e6a4 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/NewSectionLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/NewSectionLimitationTest.php @@ -4,26 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\NewSectionLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\NewSectionLimitation; /** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\NewSectionLimitation} - * class. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\NewSectionLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\NewSectionLimitation * @group integration * @group limitation */ class NewSectionLimitationTest extends BaseLimitationTest { - /** - * Tests the NewSectionLimitation. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\NewSectionLimitation - */ public function testNewSectionLimitationAllow() { $repository = $this->getRepository(); @@ -71,18 +63,13 @@ public function testNewSectionLimitationAllow() $this->assertSame( $sectionId, - $contentService->loadContentInfo($contentId)->sectionId + $contentService->loadContentInfo($contentId)->getSectionId() ); } - /** - * Tests the NewSectionLimitation. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\NewSectionLimitation - */ public function testNewSectionLimitationForbid() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -129,3 +116,5 @@ public function testNewSectionLimitationForbid() /* END: Use Case */ } } + +class_alias(NewSectionLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\NewSectionLimitationTest'); diff --git a/tests/integration/Core/Repository/Values/User/Limitation/ObjectStateLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/ObjectStateLimitationTest.php new file mode 100644 index 0000000000..512cc06a47 --- /dev/null +++ b/tests/integration/Core/Repository/Values/User/Limitation/ObjectStateLimitationTest.php @@ -0,0 +1,310 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; + +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\User; + +/** + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation + * @covers \Ibexa\Core\Limitation\ObjectStateLimitationType + * + * @group integration + * @group limitation + */ +class ObjectStateLimitationTest extends BaseLimitationTest +{ + public const OBJECT_STATE_LOCK_GROUP_ID = 2; + public const OBJECT_STATE_NOT_LOCKED_STATE_ID = 1; + public const OBJECT_STATE_LOCKED_STATE_ID = 2; + public const EDITOR_ROLE_IDENTIFIER = 'Editor'; + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testObjectStateLimitationAllow(): void + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $this->loginAsUser( + $this->createUserWithObjectStateLimitation([self::OBJECT_STATE_NOT_LOCKED_STATE_ID]) + ); + + $draft = $this->createWikiPageDraft(); + + $contentService->deleteContent($draft->contentInfo); + + $this->expectException(NotFoundException::class); + $contentService->loadContent($draft->id); + } + + /** + * @throws \ErrorException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testObjectStateLimitationForbid(): void + { + $this->expectException(UnauthorizedException::class); + + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + + $this->loginAsUser( + $this->createUserWithObjectStateLimitation([self::OBJECT_STATE_LOCKED_STATE_ID]) + ); + + $draft = $this->createWikiPageDraft(); + + $this->expectException(UnauthorizedException::class); + $contentService->deleteContent($draft->contentInfo); + } + + /** + * Checks if the action is correctly forbidden when using ObjectStateLimitation + * with limitation values from two different StateGroups. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testObjectStateLimitationForbidVariant(): void + { + $this->expectException(UnauthorizedException::class); + $this->expectExceptionMessage('\'remove\' \'content\''); + + $repository = $this->getRepository(); + $objectStateGroup = $this->createObjectStateGroup(); + $objectState = $this->createObjectState($objectStateGroup); + + $contentService = $repository->getContentService(); + + $this->loginAsUser( + $this->createUserWithObjectStateLimitation( + [ + self::OBJECT_STATE_LOCKED_STATE_ID, + $objectState->id, + ] + ) + ); + + $draft = $this->createWikiPageDraft(); + + $this->expectException(UnauthorizedException::class); + $this->expectExceptionMessage("'remove' 'content'"); + + $contentService->deleteContent($draft->contentInfo); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function createObjectStateGroup(): ObjectStateGroup + { + $objectStateService = $this->getRepository()->getObjectStateService(); + + $objectStateGroupCreateStruct = $objectStateService->newObjectStateGroupCreateStruct('second_group'); + $objectStateGroupCreateStruct->defaultLanguageCode = 'eng-US'; + $objectStateGroupCreateStruct->names = ['eng-US' => 'Second Group']; + + return $objectStateService->createObjectStateGroup($objectStateGroupCreateStruct); + } + + /** + * Create new State and assign it to the $objectStateGroup. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function createObjectState(ObjectStateGroup $objectStateGroup): ObjectState + { + $objectStateService = $this->getRepository()->getObjectStateService(); + + $objectStateCreateStruct = $objectStateService->newObjectStateCreateStruct('default_state'); + $objectStateCreateStruct->defaultLanguageCode = 'eng-US'; + $objectStateCreateStruct->names = ['eng-US' => 'Default state']; + + return $objectStateService->createObjectState($objectStateGroup, $objectStateCreateStruct); + } + + /** + * Checks if the search results are correctly filtered when using ObjectStateLimitation + * with limitation values from two different StateGroups. + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testObjectStateLimitationSearch(): void + { + $repository = $this->getRepository(); + $permissionResolver = $repository->getPermissionResolver(); + + $objectStateGroup = $this->createObjectStateGroup(); + $objectState = $this->createObjectState($objectStateGroup); + + $user = $this->createUserWithObjectStateLimitationOnContentRead( + [ + self::OBJECT_STATE_NOT_LOCKED_STATE_ID, + $objectState->id, + ] + ); + $adminUser = $permissionResolver->getCurrentUserReference(); + + $wikiPage = $this->createWikiPage(); + + $this->loginAsUser($user); + + $query = new Query(); + $query->filter = new Criterion\MatchAll(); + $query->limit = 50; + + $this->refreshSearch($repository); + $searchResultsBefore = $repository->getSearchService()->findContent($query); + + $this->loginAsUser($adminUser); + + //change the Object State to the one that doesn't match the Limitation + $stateService = $repository->getObjectStateService(); + $stateService->setContentState( + $wikiPage->contentInfo, + $stateService->loadObjectStateGroup(2), + $stateService->loadObjectState(2) + ); + + $this->loginAsUser($user); + + $this->refreshSearch($repository); + $searchResultsAfter = $repository->getSearchService()->findContent($query); + + self::assertEquals($searchResultsBefore->totalCount - 1, $searchResultsAfter->totalCount); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testUserWithNotLockedLimitationCanEditNotLockedContent(): void + { + $repository = $this->getRepository(); + $contentService = $repository->getContentService(); + $objectStateService = $repository->getObjectStateService(); + $lockGroup = $objectStateService->loadObjectStateGroup(self::OBJECT_STATE_LOCK_GROUP_ID); + $notLockedState = $objectStateService->loadObjectState(self::OBJECT_STATE_NOT_LOCKED_STATE_ID); + + // sanity check + self::assertSame('not_locked', $notLockedState->identifier); + + $this->loginAsUser( + $this->createUserWithObjectStateLimitation([self::OBJECT_STATE_NOT_LOCKED_STATE_ID]) + ); + $draft = $this->createWikiPageDraft(); + + $this->assertContentHasState( + $objectStateService, + $draft->contentInfo, + $lockGroup, + $notLockedState + ); + + $contentUpdate = $contentService->newContentUpdateStruct(); + $contentUpdate->setField('title', 'Updated test folder'); + $updatedDraft = $contentService->updateContent($draft->versionInfo, $contentUpdate); + + $this->assertContentHasState( + $objectStateService, + $updatedDraft->contentInfo, + $lockGroup, + $notLockedState + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + protected function createUserWithObjectStateLimitation(array $objectStateIDs): User + { + return $this->createUserWithPolicies( + uniqid('test', true), + [ + ['module' => 'content', 'function' => 'read'], + ['module' => 'content', 'function' => 'versionread'], + ['module' => 'content', 'function' => 'create'], + ['module' => 'content', 'function' => 'publish'], + [ + 'module' => 'content', + 'function' => 'edit', + 'limitations' => [ + new ObjectStateLimitation(['limitationValues' => $objectStateIDs]), + ], + ], + [ + 'module' => 'content', + 'function' => 'remove', + 'limitations' => [ + new ObjectStateLimitation(['limitationValues' => $objectStateIDs]), + ], + ], + ] + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + private function createUserWithObjectStateLimitationOnContentRead(array $values): User + { + return $this->createUserWithPolicies( + uniqid('test', true), + [ + [ + 'module' => 'content', + 'function' => 'read', + 'limitations' => [ + new ObjectStateLimitation( + [ + 'limitationValues' => $values, + ] + ), + ], + ], + ] + ); + } + + private function assertContentHasState( + ObjectStateService $objectStateService, + ContentInfo $contentInfo, + ObjectStateGroup $lockGroup, + ObjectState $objectState + ): void { + self::assertSame( + $objectState->identifier, + $objectStateService->getContentState($contentInfo, $lockGroup)->identifier + ); + } +} + +class_alias(ObjectStateLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\ObjectStateLimitationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/OwnerLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/OwnerLimitationTest.php similarity index 79% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/OwnerLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/OwnerLimitationTest.php index b71a665977..30a35ee928 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/OwnerLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/OwnerLimitationTest.php @@ -4,31 +4,22 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\OwnerLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\OwnerLimitation; /** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\OwnerLimitation} - * class. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\OwnerLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\OwnerLimitation * @group integration * @group limitation */ class OwnerLimitationTest extends BaseLimitationTest { - /** - * Test for the OwnerLimitation. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\OwnerLimitation - * - * @throws \ErrorException - */ public function testOwnerLimitationAllow() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -43,7 +34,7 @@ public function testOwnerLimitationAllow() $role = $roleService->loadRoleByIdentifier('Editor'); $roleDraft = $roleService->createRoleDraft($role); // Search for the new policy instance - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy */ $removePolicy = null; foreach ($roleDraft->getPolicies() as $policy) { if ('content' != $policy->module || 'remove' != $policy->function) { @@ -93,16 +84,9 @@ public function testOwnerLimitationAllow() $contentService->loadContent($content->id); } - /** - * Test for the OwnerLimitation. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\OwnerLimitation - * - * @throws \ErrorException - */ public function testOwnerLimitationForbid() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -117,7 +101,7 @@ public function testOwnerLimitationForbid() $role = $roleService->loadRoleByIdentifier('Editor'); $roleDraft = $roleService->createRoleDraft($role); // Search for the new policy instance - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy */ $removePolicy = null; foreach ($roleDraft->getPolicies() as $policy) { if ('content' != $policy->module || 'remove' != $policy->function) { @@ -159,3 +143,5 @@ public function testOwnerLimitationForbid() /* END: Use Case */ } } + +class_alias(OwnerLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\OwnerLimitationTest'); diff --git a/tests/integration/Core/Repository/Values/User/Limitation/ParentContentTypeLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/ParentContentTypeLimitationTest.php new file mode 100644 index 0000000000..998cff0a1d --- /dev/null +++ b/tests/integration/Core/Repository/Values/User/Limitation/ParentContentTypeLimitationTest.php @@ -0,0 +1,111 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; + +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentContentTypeLimitation; + +/** + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentContentTypeLimitation + * @group integration + * @group limitation + */ +class ParentContentTypeLimitationTest extends BaseLimitationTest +{ + public function testParentContentTypeLimitationAllow() + { + $repository = $this->getRepository(); + $permissionResolver = $repository->getPermissionResolver(); + + $parentContentTypeId = $this->generateId('contentType', 20); + $contentTypeId = $this->generateId('contentType', 22); + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + $roleService = $repository->getRoleService(); + $contentService = $repository->getContentService(); + + $role = $roleService->loadRoleByIdentifier('Editor'); + + $policyCreate = $roleService->newPolicyCreateStruct('content', 'create'); + $policyCreate->addLimitation( + new ParentContentTypeLimitation( + ['limitationValues' => [$parentContentTypeId]] + ) + ); + $policyCreate->addLimitation( + new ContentTypeLimitation( + ['limitationValues' => [$contentTypeId]] + ) + ); + + $roleDraft = $roleService->createRoleDraft($role); + $roleService->addPolicyByRoleDraft( + $roleDraft, + $policyCreate + ); + $roleService->publishRoleDraft($roleDraft); + + $roleService->assignRoleToUser($role, $user); + + $permissionResolver->setCurrentUserReference($user); + + $draft = $this->createWikiPageDraft(); + $content = $contentService->publishVersion($draft->versionInfo); + /* END: Use Case */ + + $this->assertEquals( + 'An awesome wiki page', + $content->getFieldValue('title')->text + ); + } + + public function testParentContentTypeLimitationForbid() + { + $this->expectException(UnauthorizedException::class); + + $repository = $this->getRepository(); + $permissionResolver = $repository->getPermissionResolver(); + + $parentContentTypeId = $this->generateId('contentType', 20); + $contentTypeId = $this->generateId('contentType', 33); + /* BEGIN: Use Case */ + $user = $this->createUserVersion1(); + + $roleService = $repository->getRoleService(); + + $role = $roleService->loadRoleByIdentifier('Editor'); + + $policyCreate = $roleService->newPolicyCreateStruct('content', 'create'); + $policyCreate->addLimitation( + new ParentContentTypeLimitation( + ['limitationValues' => [$parentContentTypeId]] + ) + ); + $policyCreate->addLimitation( + new ContentTypeLimitation( + ['limitationValues' => [$contentTypeId]] + ) + ); + + $roleDraft = $roleService->createRoleDraft($role); + $roleService->addPolicyByRoleDraft( + $roleDraft, + $policyCreate + ); + + $roleService->publishRoleDraft($roleDraft); + + $permissionResolver->setCurrentUserReference($user); + + $this->createWikiPageDraft(); + /* END: Use Case */ + } +} + +class_alias(ParentContentTypeLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\ParentContentTypeLimitationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ParentDepthLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/ParentDepthLimitationTest.php similarity index 76% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/ParentDepthLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/ParentDepthLimitationTest.php index 28ce3ebdad..46a83de63b 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ParentDepthLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/ParentDepthLimitationTest.php @@ -4,31 +4,22 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ParentDepthLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentDepthLimitation; /** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\ParentDepthLimitation} - * class. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\ParentDepthLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentDepthLimitation * @group integration * @group limitation */ class ParentDepthLimitationTest extends BaseLimitationTest { - /** - * Tests a combination of ParentDepthLimitation and ContentTypeLimitation. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ParentDepthLimitation - */ public function testParentDepthLimitationForbid() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -66,12 +57,6 @@ public function testParentDepthLimitationForbid() ); } - /** - * Tests a combination of ParentDepthLimitation and ContentTypeLimitation. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ParentDepthLimitation - */ public function testParentDepthLimitationAllow() { $repository = $this->getRepository(); @@ -116,9 +101,6 @@ public function testParentDepthLimitationAllow() * Tests a combination of ParentDepthLimitation and ContentTypeLimitation. * * @depends testParentDepthLimitationAllow - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ParentDepthLimitation */ public function testParentDepthLimitationAllowPublish() { @@ -164,3 +146,5 @@ public function testParentDepthLimitationAllowPublish() /* END: Use Case */ } } + +class_alias(ParentDepthLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\ParentDepthLimitationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ParentOwnerLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/ParentOwnerLimitationTest.php similarity index 76% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/ParentOwnerLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/ParentOwnerLimitationTest.php index 83a3208e14..6ddc4b6c2d 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ParentOwnerLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/ParentOwnerLimitationTest.php @@ -4,26 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ParentOwnerLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentOwnerLimitation; /** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\ParentOwnerLimitation} - * class. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\ParentOwnerLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentOwnerLimitation * @group integration * @group limitation */ class ParentOwnerLimitationTest extends BaseLimitationTest { - /** - * Tests the ParentOwnerLimitation. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ParentOwnerLimitation - */ public function testParentOwnerLimitationAllow() { $repository = $this->getRepository(); @@ -67,14 +59,9 @@ public function testParentOwnerLimitationAllow() ); } - /** - * Tests the ParentOwnerLimitation. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\ParentOwnerLimitation - */ public function testParentOwnerLimitationForbid() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -108,3 +95,5 @@ public function testParentOwnerLimitationForbid() /* END: Use Case */ } } + +class_alias(ParentOwnerLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\ParentOwnerLimitationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ParentUserGroupLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/ParentUserGroupLimitationTest.php similarity index 83% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/ParentUserGroupLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/ParentUserGroupLimitationTest.php index eadd850214..142e85dac9 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/ParentUserGroupLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/ParentUserGroupLimitationTest.php @@ -4,26 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ParentUserGroupLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentUserGroupLimitation; /** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\ParentUserGroupLimitation} - * class. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\ParentUserGroupLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentUserGroupLimitation * @group integration * @group limitation */ class ParentUserGroupLimitationTest extends BaseLimitationTest { - /** - * Tests a ParentUserGroupLimitation. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation\ParentUserGroupLimitation - */ public function testParentUserGroupLimitationAllow() { $repository = $this->getRepository(); @@ -86,14 +78,9 @@ public function testParentUserGroupLimitationAllow() ); } - /** - * Tests a ParentUserGroupLimitation. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation\ParentUserGroupLimitation - */ public function testParentUserGroupLimitationForbid() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $userService = $repository->getUserService(); @@ -147,3 +134,5 @@ public function testParentUserGroupLimitationForbid() /* END: Use Case */ } } + +class_alias(ParentUserGroupLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\ParentUserGroupLimitationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/RolePolicyLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/RolePolicyLimitationTest.php similarity index 84% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/RolePolicyLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/RolePolicyLimitationTest.php index bee749fac5..909bf0be17 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/RolePolicyLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/RolePolicyLimitationTest.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation; -use eZ\Publish\API\Repository\Values\User\RoleCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroup; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroup; class RolePolicyLimitationTest extends BaseLimitationTest { @@ -116,9 +116,9 @@ protected function getSubtreeLocationsCount($subtreePathString) /** * Create test User in the given User Group. * - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $group + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $group * - * @return \eZ\Publish\API\Repository\Values\User\User + * @return \Ibexa\Contracts\Core\Repository\Values\User\User */ protected function createUserInGroup(UserGroup $group) { @@ -146,10 +146,10 @@ protected function createUserInGroup(UserGroup $group) /** * Add policy to a new role. * - * @param \eZ\Publish\API\Repository\Values\User\RoleCreateStruct $roleCreateStruct + * @param \Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct $roleCreateStruct * @param string $module * @param string $function - * @param \eZ\Publish\API\Repository\Values\User\Limitation[] $limitations + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation[] $limitations */ protected function addPolicyToNewRole(RoleCreateStruct $roleCreateStruct, $module, $function, array $limitations) { @@ -168,7 +168,7 @@ protected function addPolicyToNewRole(RoleCreateStruct $roleCreateStruct, $modul * @param string $mainLanguageCode * @param int $parentGroupId * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup */ protected function createGroup($groupName, $mainLanguageCode, $parentGroupId) { @@ -181,3 +181,5 @@ protected function createGroup($groupName, $mainLanguageCode, $parentGroupId) return $userService->createUserGroup($userGroupCreateStruct, $usersGroup); } } + +class_alias(RolePolicyLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\RolePolicyLimitationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/SectionLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/SectionLimitationTest.php similarity index 79% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/SectionLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/SectionLimitationTest.php index 50dd8b2e30..e3f6e48068 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/SectionLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/SectionLimitationTest.php @@ -4,28 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation; /** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation} - * class. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation * @group integration * @group limitation */ class SectionLimitationTest extends BaseLimitationTest { - /** - * Test for the SectionLimitation. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation - * - * @throws \ErrorException - */ public function testSectionLimitationAllow() { $repository = $this->getRepository(); @@ -39,7 +29,7 @@ public function testSectionLimitationAllow() $role = $roleService->loadRoleByIdentifier('Editor'); $roleDraft = $roleService->createRoleDraft($role); // Search for the new policy instance - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy */ $readPolicy = null; foreach ($roleDraft->getPolicies() as $policy) { if ('content' != $policy->module || 'read' != $policy->function) { @@ -84,16 +74,9 @@ public function testSectionLimitationAllow() ); } - /** - * Test for the SectionLimitation. - * - * @see \eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation - * - * @throws \ErrorException - */ public function testSectionLimitationForbid() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); @@ -106,7 +89,7 @@ public function testSectionLimitationForbid() $role = $roleService->loadRoleByIdentifier('Editor'); $roleDraft = $roleService->createRoleDraft($role); // Search for the new policy instance - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy */ $readPolicy = null; foreach ($roleDraft->getPolicies() as $policy) { if ('content' != $policy->module || 'read' != $policy->function) { @@ -147,3 +130,5 @@ public function testSectionLimitationForbid() /* END: Use Case */ } } + +class_alias(SectionLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\SectionLimitationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/StatusLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/StatusLimitationTest.php similarity index 85% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/StatusLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/StatusLimitationTest.php index 4a1b09539e..a2b5481b71 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/StatusLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/StatusLimitationTest.php @@ -4,27 +4,19 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation\StatusLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\StatusLimitation; /** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\StatusLimitation} - * class. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\StatusLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\StatusLimitation * @group integration * @group limitation */ class StatusLimitationTest extends BaseLimitationTest { - /** - * Tests a StatusLimitation. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation\StatusLimitation - */ public function testStatusLimitationAllow() { $repository = $this->getRepository(); @@ -33,9 +25,9 @@ public function testStatusLimitationAllow() $administratorUserId = $this->generateId('user', 14); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $administratorUserId is the ID of the "Administrator" user in a eZ + // $administratorUserId is the ID of the "Administrator" user in a Ibexa // Publish demo installation. - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. // Load the user service @@ -86,14 +78,9 @@ public function testStatusLimitationAllow() ); } - /** - * Tests a StatusLimitation. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation\StatusLimitation - */ public function testStatusLimitationForbid() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); $permissionResolver = $repository->getPermissionResolver(); @@ -101,9 +88,9 @@ public function testStatusLimitationForbid() $administratorUserId = $this->generateId('user', 14); $anonymousUserId = $this->generateId('user', 10); /* BEGIN: Use Case */ - // $anonymousUserId is the ID of the "Anonymous" user in a eZ + // $anonymousUserId is the ID of the "Anonymous" user in a Ibexa // Publish demo installation. - // $administratorUserId is the ID of the "Administrator" user in a eZ + // $administratorUserId is the ID of the "Administrator" user in a Ibexa // Publish demo installation. // Load the user service @@ -157,3 +144,5 @@ public function testStatusLimitationForbid() /* END: Use Case */ } } + +class_alias(StatusLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\StatusLimitationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/SubtreeLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/SubtreeLimitationTest.php similarity index 75% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/SubtreeLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/SubtreeLimitationTest.php index 1a558f7ae3..0fd2fa41d4 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/SubtreeLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/SubtreeLimitationTest.php @@ -4,20 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; /** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation} - * class. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation * @group integration * @group limitation */ @@ -27,9 +22,9 @@ class SubtreeLimitationTest extends BaseLimitationTest * Tests a combination of SubtreeLimitation, SectionLimitation and * the ContentTypeLimitation. * - * @see eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation + * @see \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation + * @see \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation + * @see \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation */ public function testSubtreeLimitationAllow() { @@ -45,7 +40,7 @@ public function testSubtreeLimitationAllow() $contentService = $repository->getContentService(); $contentUpdate = $contentService->newContentUpdateStruct(); - $contentUpdate->setField('name', 'eZ Editors'); + $contentUpdate->setField('name', 'Ibexa Editors'); $userGroup = $userService->loadUserGroup($userGroupId); @@ -56,7 +51,7 @@ public function testSubtreeLimitationAllow() /* END: Use Case */ $this->assertEquals( - 'eZ Editors', + 'Ibexa Editors', $userService->loadUserGroup($userGroupId) ->getFieldValue('name') ->text @@ -67,13 +62,13 @@ public function testSubtreeLimitationAllow() * Tests a combination of SubtreeLimitation, SectionLimitation and * the ContentTypeLimitation. * - * @see eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation + * @see \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation + * @see \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation + * @see \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation */ public function testSubtreeLimitationForbid() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); @@ -117,7 +112,7 @@ protected function prepareLimitation($subtree) $role = $roleService->loadRoleByIdentifier('Editor'); $roleDraft = $roleService->createRoleDraft($role); // Search for the new policy instance - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy */ $editPolicy = null; foreach ($roleDraft->getPolicies() as $policy) { if ('content' != $policy->module || 'read' != $policy->function) { @@ -174,3 +169,5 @@ protected function prepareLimitation($subtree) /* END: Inline */ } } + +class_alias(SubtreeLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\SubtreeLimitationTest'); diff --git a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/UserGroupLimitationTest.php b/tests/integration/Core/Repository/Values/User/Limitation/UserGroupLimitationTest.php similarity index 76% rename from eZ/Publish/API/Repository/Tests/Values/User/Limitation/UserGroupLimitationTest.php rename to tests/integration/Core/Repository/Values/User/Limitation/UserGroupLimitationTest.php index bfdcf5101f..8f7fe568b5 100644 --- a/eZ/Publish/API/Repository/Tests/Values/User/Limitation/UserGroupLimitationTest.php +++ b/tests/integration/Core/Repository/Values/User/Limitation/UserGroupLimitationTest.php @@ -4,30 +4,20 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Values\User\Limitation; +namespace Ibexa\Tests\Integration\Core\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\UserGroupLimitation; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserGroup; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\UserGroupLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroup; /** - * Test case for the {@link \eZ\Publish\API\Repository\Values\User\Limitation\UserGroupLimitation} - * class. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation - * @see eZ\Publish\API\Repository\Values\User\Limitation\UserGroupLimitation + * @covers \Ibexa\Contracts\Core\Repository\Values\User\Limitation\UserGroupLimitation * @group integration * @group limitation */ class UserGroupLimitationTest extends BaseLimitationTest { - /** - * Tests a UserGroupLimitation. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation\UserGroupLimitation - * - * @throws \ErrorException if a mandatory test fixture not exists. - */ public function testUserGroupLimitationAllow() { $repository = $this->getRepository(); @@ -52,16 +42,9 @@ public function testUserGroupLimitationAllow() ); } - /** - * Tests a UserGroupLimitation. - * - * @see eZ\Publish\API\Repository\Values\User\Limitation\UserGroupLimitation - * - * @throws \ErrorException if a mandatory test fixture not exists. - */ public function testUserGroupLimitationForbid() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\UnauthorizedException::class); + $this->expectException(UnauthorizedException::class); $repository = $this->getRepository(); @@ -82,7 +65,7 @@ public function testUserGroupLimitationForbid() /** * Prepares the UserGroup fixture. * - * @return \eZ\Publish\API\Repository\Values\User\UserGroup + * @return \Ibexa\Contracts\Core\Repository\Values\User\UserGroup */ protected function prepareUserGroup() { @@ -108,10 +91,10 @@ protected function prepareUserGroup() /** * Prepares the limitation fixture. * - * @param \eZ\Publish\API\Repository\Values\User\User $user - * @param \eZ\Publish\API\Repository\Values\User\UserGroup $userGroup + * @param \Ibexa\Contracts\Core\Repository\Values\User\User $user + * @param \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroup * - * @return \eZ\Publish\API\Repository\Values\Content\Content + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content * * @throws \ErrorException */ @@ -128,7 +111,7 @@ protected function prepareLimitationAndContent(User $user, UserGroup $userGroup) $role = $roleService->loadRoleByIdentifier('Editor'); $roleDraft = $roleService->createRoleDraft($role); // Search for the new policy instance - /** @var \eZ\Publish\API\Repository\Values\User\PolicyDraft $policy */ + /** @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policy */ $editPolicy = null; foreach ($roleDraft->getPolicies() as $policy) { if ('content' != $policy->module || 'edit' != $policy->function) { @@ -175,3 +158,5 @@ protected function prepareLimitationAndContent(User $user, UserGroup $userGroup) return $draft; } } + +class_alias(UserGroupLimitationTest::class, 'eZ\Publish\API\Repository\Tests\Values\User\Limitation\UserGroupLimitationTest'); diff --git a/tests/integration/Core/Repository/_fixtures/LanguageFixture.php b/tests/integration/Core/Repository/_fixtures/LanguageFixture.php new file mode 100644 index 0000000000..dda29be8ef --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/LanguageFixture.php @@ -0,0 +1,42 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Language; + +return [ + [ + 2 => new Language( + [ + 'id' => 2, + 'name' => 'English (American)', + 'enabled' => true, + 'languageCode' => 'eng-US', + ] + ), + 4 => new Language( + [ + 'id' => 4, + 'name' => 'German', + 'enabled' => true, + 'languageCode' => 'ger-DE', + ] + ), + 8 => new Language( + [ + 'id' => 8, + 'name' => 'English (United Kingdom)', + 'enabled' => true, + 'languageCode' => 'eng-GB', + ] + ), + ], + [ + 'eng-US' => 2, + 'ger-DE' => 4, + 'eng-GB' => 8, + ], + 8, +]; diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/AncestorContent.php b/tests/integration/Core/Repository/_fixtures/Legacy/AncestorContent.php new file mode 100644 index 0000000000..6b64349a6c --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/AncestorContent.php @@ -0,0 +1,50 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/AncestorLocation.php b/tests/integration/Core/Repository/_fixtures/Legacy/AncestorLocation.php new file mode 100644 index 0000000000..12e03d3a83 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/AncestorLocation.php @@ -0,0 +1,50 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/ContentId.php b/tests/integration/Core/Repository/_fixtures/Legacy/ContentId.php new file mode 100644 index 0000000000..6975cb2811 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/ContentId.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/ContentTypeGroupId.php b/tests/integration/Core/Repository/_fixtures/Legacy/ContentTypeGroupId.php new file mode 100644 index 0000000000..a677afcfb5 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/ContentTypeGroupId.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/ContentTypeId.php b/tests/integration/Core/Repository/_fixtures/Legacy/ContentTypeId.php new file mode 100644 index 0000000000..d9ecdf09b5 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/ContentTypeId.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataBetween.php b/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataBetween.php new file mode 100644 index 0000000000..cf57292657 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataBetween.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataCreated.php b/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataCreated.php new file mode 100644 index 0000000000..bdbf411fdc --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataCreated.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataGt.php b/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataGt.php new file mode 100644 index 0000000000..1d3c4d2b4d --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataGt.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataGte.php b/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataGte.php new file mode 100644 index 0000000000..ee4b6c3f33 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataGte.php @@ -0,0 +1,56 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 4, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataIn.php b/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataIn.php new file mode 100644 index 0000000000..bdbf411fdc --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataIn.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataLte.php b/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataLte.php new file mode 100644 index 0000000000..df223bb4d3 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/DateMetadataLte.php @@ -0,0 +1,110 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 14, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/DeprecatedContentIdQuery.php b/tests/integration/Core/Repository/_fixtures/Legacy/DeprecatedContentIdQuery.php new file mode 100644 index 0000000000..6975cb2811 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/DeprecatedContentIdQuery.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/Depth.php b/tests/integration/Core/Repository/_fixtures/Legacy/Depth.php new file mode 100644 index 0000000000..2e13359911 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/Depth.php @@ -0,0 +1,76 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 5, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/DepthGt.php b/tests/integration/Core/Repository/_fixtures/Legacy/DepthGt.php new file mode 100644 index 0000000000..91e559c16b --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/DepthGt.php @@ -0,0 +1,43 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/DepthGte.php b/tests/integration/Core/Repository/_fixtures/Legacy/DepthGte.php new file mode 100644 index 0000000000..46679f2417 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/DepthGte.php @@ -0,0 +1,164 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 6 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 7 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 8 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 9 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 10 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 11 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 12 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 13, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/DepthIn.php b/tests/integration/Core/Repository/_fixtures/Legacy/DepthIn.php new file mode 100644 index 0000000000..9ebca90405 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/DepthIn.php @@ -0,0 +1,98 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 6 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 7, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/DepthLte.php b/tests/integration/Core/Repository/_fixtures/Legacy/DepthLte.php new file mode 100644 index 0000000000..c9a65a4632 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/DepthLte.php @@ -0,0 +1,197 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 6 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 7 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 8 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 9 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 10 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 11 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 12 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 13 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 14 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 15 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 16, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FacetContentType.php b/tests/integration/Core/Repository/_fixtures/Legacy/FacetContentType.php new file mode 100644 index 0000000000..b8a2e25b10 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FacetContentType.php @@ -0,0 +1,40 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FacetContentTypeMinCount.php b/tests/integration/Core/Repository/_fixtures/Legacy/FacetContentTypeMinCount.php new file mode 100644 index 0000000000..b8a2e25b10 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FacetContentTypeMinCount.php @@ -0,0 +1,40 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FacetContentTypeMinLimit.php b/tests/integration/Core/Repository/_fixtures/Legacy/FacetContentTypeMinLimit.php new file mode 100644 index 0000000000..b8a2e25b10 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FacetContentTypeMinLimit.php @@ -0,0 +1,40 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FacetFieldRegexp.php b/tests/integration/Core/Repository/_fixtures/Legacy/FacetFieldRegexp.php new file mode 100644 index 0000000000..b8a2e25b10 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FacetFieldRegexp.php @@ -0,0 +1,40 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FacetFieldRegexpSortCount.php b/tests/integration/Core/Repository/_fixtures/Legacy/FacetFieldRegexpSortCount.php new file mode 100644 index 0000000000..b8a2e25b10 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FacetFieldRegexpSortCount.php @@ -0,0 +1,40 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FacetFieldRegexpSortTerm.php b/tests/integration/Core/Repository/_fixtures/Legacy/FacetFieldRegexpSortTerm.php new file mode 100644 index 0000000000..b8a2e25b10 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FacetFieldRegexpSortTerm.php @@ -0,0 +1,40 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FacetFieldSimple.php b/tests/integration/Core/Repository/_fixtures/Legacy/FacetFieldSimple.php new file mode 100644 index 0000000000..b8a2e25b10 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FacetFieldSimple.php @@ -0,0 +1,40 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FacetSection.php b/tests/integration/Core/Repository/_fixtures/Legacy/FacetSection.php new file mode 100644 index 0000000000..b8a2e25b10 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FacetSection.php @@ -0,0 +1,40 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FacetTerm.php b/tests/integration/Core/Repository/_fixtures/Legacy/FacetTerm.php new file mode 100644 index 0000000000..b8a2e25b10 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FacetTerm.php @@ -0,0 +1,40 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FacetUser.php b/tests/integration/Core/Repository/_fixtures/Legacy/FacetUser.php new file mode 100644 index 0000000000..b8a2e25b10 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FacetUser.php @@ -0,0 +1,40 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'matchedTranslation' => 'eng-GB', + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/Field.php b/tests/integration/Core/Repository/_fixtures/Legacy/Field.php new file mode 100644 index 0000000000..62f4970ff1 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/Field.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FieldBetween.php b/tests/integration/Core/Repository/_fixtures/Legacy/FieldBetween.php new file mode 100644 index 0000000000..d16077c430 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FieldBetween.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FieldIn.php b/tests/integration/Core/Repository/_fixtures/Legacy/FieldIn.php new file mode 100644 index 0000000000..5aa1319874 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FieldIn.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FieldOr.php b/tests/integration/Core/Repository/_fixtures/Legacy/FieldOr.php new file mode 100644 index 0000000000..3da6c1d909 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FieldOr.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FieldRelation.php b/tests/integration/Core/Repository/_fixtures/Legacy/FieldRelation.php new file mode 100644 index 0000000000..bb787601bf --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FieldRelation.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 61, + 'title' => 'User gallery', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FieldRelationAll.php b/tests/integration/Core/Repository/_fixtures/Legacy/FieldRelationAll.php new file mode 100644 index 0000000000..71abe8d5c9 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FieldRelationAll.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 60, + 'title' => 'Image gallery', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 61, + 'title' => 'User gallery', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FullText.php b/tests/integration/Core/Repository/_fixtures/Legacy/FullText.php new file mode 100644 index 0000000000..1c8b609a3d --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FullText.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FullTextFiltered.php b/tests/integration/Core/Repository/_fixtures/Legacy/FullTextFiltered.php new file mode 100644 index 0000000000..4a6980df77 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FullTextFiltered.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/FullTextWildcard.php b/tests/integration/Core/Repository/_fixtures/Legacy/FullTextWildcard.php new file mode 100644 index 0000000000..4a6980df77 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/FullTextWildcard.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/IsContainerFalse.php b/tests/integration/Core/Repository/_fixtures/Legacy/IsContainerFalse.php new file mode 100644 index 0000000000..f2645e6790 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/IsContainerFalse.php @@ -0,0 +1,59 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return new SearchResult([ + 'facets' => [], + 'searchHits' => [ + new SearchHit([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + new SearchHit([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + new SearchHit([ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + new SearchHit([ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 4, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/IsContainerTrue.php b/tests/integration/Core/Repository/_fixtures/Legacy/IsContainerTrue.php new file mode 100644 index 0000000000..52f30b9826 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/IsContainerTrue.php @@ -0,0 +1,69 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return new SearchResult([ + 'facets' => [], + 'searchHits' => [ + new SearchHit([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + new SearchHit([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + new SearchHit([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + new SearchHit([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + new SearchHit([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 14, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/IsUserBasedFalse.php b/tests/integration/Core/Repository/_fixtures/Legacy/IsUserBasedFalse.php new file mode 100644 index 0000000000..453d9d0139 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/IsUserBasedFalse.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [], + 'searchHits' => [ + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 16, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/IsUserBasedTrue.php b/tests/integration/Core/Repository/_fixtures/Legacy/IsUserBasedTrue.php new file mode 100644 index 0000000000..e1460051ee --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/IsUserBasedTrue.php @@ -0,0 +1,39 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [], + 'searchHits' => [ + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/IsUserEnabledFalse.php b/tests/integration/Core/Repository/_fixtures/Legacy/IsUserEnabledFalse.php new file mode 100644 index 0000000000..dea87d12c8 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/IsUserEnabledFalse.php @@ -0,0 +1,17 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [], + 'searchHits' => [], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 0, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/IsUserEnabledTrue.php b/tests/integration/Core/Repository/_fixtures/Legacy/IsUserEnabledTrue.php new file mode 100644 index 0000000000..e1460051ee --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/IsUserEnabledTrue.php @@ -0,0 +1,39 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [], + 'searchHits' => [ + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/LanguageCode.php b/tests/integration/Core/Repository/_fixtures/Legacy/LanguageCode.php new file mode 100644 index 0000000000..9ca04c29bf --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/LanguageCode.php @@ -0,0 +1,43 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/LanguageCodeAlwaysAvailable.php b/tests/integration/Core/Repository/_fixtures/Legacy/LanguageCodeAlwaysAvailable.php new file mode 100644 index 0000000000..e2cbceb99f --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/LanguageCodeAlwaysAvailable.php @@ -0,0 +1,87 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 16, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/LanguageCodeIn.php b/tests/integration/Core/Repository/_fixtures/Legacy/LanguageCodeIn.php new file mode 100644 index 0000000000..c0628fde25 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/LanguageCodeIn.php @@ -0,0 +1,109 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 6 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 7 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 18, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/LocationId.php b/tests/integration/Core/Repository/_fixtures/Legacy/LocationId.php new file mode 100644 index 0000000000..836d7916f7 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/LocationId.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/LocationRemoteId.php b/tests/integration/Core/Repository/_fixtures/Legacy/LocationRemoteId.php new file mode 100644 index 0000000000..836d7916f7 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/LocationRemoteId.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/LogicalAnd.php b/tests/integration/Core/Repository/_fixtures/Legacy/LogicalAnd.php new file mode 100644 index 0000000000..bbe1019c31 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/LogicalAnd.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/LogicalNot.php b/tests/integration/Core/Repository/_fixtures/Legacy/LogicalNot.php new file mode 100644 index 0000000000..bbe1019c31 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/LogicalNot.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/LogicalOr.php b/tests/integration/Core/Repository/_fixtures/Legacy/LogicalOr.php new file mode 100644 index 0000000000..9fb28ad33a --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/LogicalOr.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/MatchNone.php b/tests/integration/Core/Repository/_fixtures/Legacy/MatchNone.php new file mode 100644 index 0000000000..c1b1d19bcd --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/MatchNone.php @@ -0,0 +1,19 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 0, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/ObjectStateIdentifier.php b/tests/integration/Core/Repository/_fixtures/Legacy/ObjectStateIdentifier.php new file mode 100644 index 0000000000..087c80c374 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/ObjectStateIdentifier.php @@ -0,0 +1,199 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 12 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 13 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 14 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 15 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 16 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 17 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 0.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 18, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/ParentLocationId.php b/tests/integration/Core/Repository/_fixtures/Legacy/ParentLocationId.php new file mode 100644 index 0000000000..3498c06503 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/ParentLocationId.php @@ -0,0 +1,65 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 5, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/QueryModifiedField.php b/tests/integration/Core/Repository/_fixtures/Legacy/QueryModifiedField.php new file mode 100644 index 0000000000..a2e1eab5a2 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/QueryModifiedField.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/RemoteId.php b/tests/integration/Core/Repository/_fixtures/Legacy/RemoteId.php new file mode 100644 index 0000000000..6975cb2811 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/RemoteId.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SectionId.php b/tests/integration/Core/Repository/_fixtures/Legacy/SectionId.php new file mode 100644 index 0000000000..a677afcfb5 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SectionId.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SectionIdentifier.php b/tests/integration/Core/Repository/_fixtures/Legacy/SectionIdentifier.php new file mode 100644 index 0000000000..2537c135e6 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SectionIdentifier.php @@ -0,0 +1,91 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [], + 'searchHits' => [ + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/Sibling.php b/tests/integration/Core/Repository/_fixtures/Legacy/Sibling.php new file mode 100644 index 0000000000..4c79896099 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/Sibling.php @@ -0,0 +1,56 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 4, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortContentName.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortContentName.php new file mode 100644 index 0000000000..18db48e4cb --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortContentName.php @@ -0,0 +1,128 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 12, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortDateModified.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortDateModified.php new file mode 100644 index 0000000000..62835f2917 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortDateModified.php @@ -0,0 +1,109 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 6 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 7 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 8, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortDatePublished.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortDatePublished.php new file mode 100644 index 0000000000..a677afcfb5 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortDatePublished.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortDesc.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortDesc.php new file mode 100644 index 0000000000..ae713a15a8 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortDesc.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortFieldMultipleTypes.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortFieldMultipleTypes.php new file mode 100644 index 0000000000..1305378773 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortFieldMultipleTypes.php @@ -0,0 +1,128 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 12, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortFieldMultipleTypesReverse.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortFieldMultipleTypesReverse.php new file mode 100644 index 0000000000..04f8f07acf --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortFieldMultipleTypesReverse.php @@ -0,0 +1,128 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 12, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortFieldMultipleTypesSlice.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortFieldMultipleTypesSlice.php new file mode 100644 index 0000000000..430ee4e57f --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortFieldMultipleTypesSlice.php @@ -0,0 +1,65 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 12, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortFieldMultipleTypesSliceReverse.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortFieldMultipleTypesSliceReverse.php new file mode 100644 index 0000000000..a45838e8ce --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortFieldMultipleTypesSliceReverse.php @@ -0,0 +1,65 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 12, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortFolderName.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortFolderName.php new file mode 100644 index 0000000000..decb145e9e --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortFolderName.php @@ -0,0 +1,74 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 6, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortLocationDepth.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortLocationDepth.php new file mode 100644 index 0000000000..0b983ae799 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortLocationDepth.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortMultiple.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortMultiple.php new file mode 100644 index 0000000000..b671cca203 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortMultiple.php @@ -0,0 +1,56 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 4, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortNone.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortNone.php new file mode 100644 index 0000000000..a677afcfb5 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortNone.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortPathString.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortPathString.php new file mode 100644 index 0000000000..01787dc6e4 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortPathString.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortSectionIdentifier.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortSectionIdentifier.php new file mode 100644 index 0000000000..182cce6a42 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortSectionIdentifier.php @@ -0,0 +1,146 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 12 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 13 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 14, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortSectionName.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortSectionName.php new file mode 100644 index 0000000000..25b9e87cde --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortSectionName.php @@ -0,0 +1,146 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 12 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 13 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 14, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/SortTemplateTitle.php b/tests/integration/Core/Repository/_fixtures/Legacy/SortTemplateTitle.php new file mode 100644 index 0000000000..2ed325d7b4 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/SortTemplateTitle.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/Status.php b/tests/integration/Core/Repository/_fixtures/Legacy/Status.php new file mode 100644 index 0000000000..fff1c0bb32 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/Status.php @@ -0,0 +1,182 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 12 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 13 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 14 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 15 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 16 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 17 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 18, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/Subtree.php b/tests/integration/Core/Repository/_fixtures/Legacy/Subtree.php new file mode 100644 index 0000000000..a677afcfb5 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/Subtree.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/UserEmail.php b/tests/integration/Core/Repository/_fixtures/Legacy/UserEmail.php new file mode 100644 index 0000000000..6955338104 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/UserEmail.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [], + 'searchHits' => [ + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/UserIdEq.php b/tests/integration/Core/Repository/_fixtures/Legacy/UserIdEq.php new file mode 100644 index 0000000000..730fec977c --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/UserIdEq.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [], + 'searchHits' => [ + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/UserIdIn.php b/tests/integration/Core/Repository/_fixtures/Legacy/UserIdIn.php new file mode 100644 index 0000000000..e1460051ee --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/UserIdIn.php @@ -0,0 +1,39 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [], + 'searchHits' => [ + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/UserLogin.php b/tests/integration/Core/Repository/_fixtures/Legacy/UserLogin.php new file mode 100644 index 0000000000..730fec977c --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/UserLogin.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [], + 'searchHits' => [ + SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/UserMetadata.php b/tests/integration/Core/Repository/_fixtures/Legacy/UserMetadata.php new file mode 100644 index 0000000000..69f07c4917 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/UserMetadata.php @@ -0,0 +1,200 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 12 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 13 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 14 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 15 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-GB', + ]), + 16 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-GB', + ]), + 17 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 18, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/UserMetadataLocation.php b/tests/integration/Core/Repository/_fixtures/Legacy/UserMetadataLocation.php new file mode 100644 index 0000000000..6fb1af45ca --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/UserMetadataLocation.php @@ -0,0 +1,200 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-GB', + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 12 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 13 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 14 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 15 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + 16 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-GB', + ]), + 17 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + 'matchedTranslation' => 'eng-US', + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 19, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Legacy/Visibility.php b/tests/integration/Core/Repository/_fixtures/Legacy/Visibility.php new file mode 100644 index 0000000000..02df53fabc --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Legacy/Visibility.php @@ -0,0 +1,219 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 6 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 7 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 8 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 9 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 10 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 11 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 12 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 13 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 14 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 15 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 16 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + 17 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 18, + ] +); diff --git a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/data/test_data.yaml b/tests/integration/Core/Repository/_fixtures/Legacy/data/test_data.yaml similarity index 97% rename from eZ/Publish/API/Repository/Tests/_fixtures/Legacy/data/test_data.yaml rename to tests/integration/Core/Repository/_fixtures/Legacy/data/test_data.yaml index d8cef45ea3..62e092cdcd 100644 --- a/eZ/Publish/API/Repository/Tests/_fixtures/Legacy/data/test_data.yaml +++ b/tests/integration/Core/Repository/_fixtures/Legacy/data/test_data.yaml @@ -87,16 +87,16 @@ ezcontentclass_attribute: - { can_translate: 0, category: '', contentclass_id: 14, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 6, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: image.ini, data_text2: large, data_text3: Filters, data_text4: '', data_text5: override;user;admin;demo, data_type_string: ezinisetting, id: 171, identifier: imagelarge, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 13, serialized_data_text: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_description_list: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_name_list: 'a:2:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:16:"Image Large Size";}', version: 0 } - { can_translate: 0, category: '', contentclass_id: 15, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 1, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: site.ini, data_text2: SiteSettings, data_text3: SiteName, data_text4: '', data_text5: override;user;admin;demo, data_type_string: ezinisetting, id: 172, identifier: title, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 1, serialized_data_text: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_description_list: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_name_list: 'a:2:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:5:"Title";}', version: 0 } - { can_translate: 0, category: '', contentclass_id: 15, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 6, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: site.ini, data_text2: SiteSettings, data_text3: MetaDataArray, data_text4: '', data_text5: override;user;admin;demo, data_type_string: ezinisetting, id: 173, identifier: meta_data, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 2, serialized_data_text: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_description_list: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_name_list: 'a:2:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:9:"Meta data";}', version: 0 } - - { can_translate: 1, category: '', contentclass_id: 15, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezimage, id: 174, identifier: image, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 3, serialized_data_text: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_description_list: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_name_list: 'a:2:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:5:"Image";}', version: 0 } + - { can_translate: 1, category: '', contentclass_id: 15, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: 'KB', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezimage, id: 174, identifier: image, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 3, serialized_data_text: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_description_list: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_name_list: 'a:2:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:5:"Image";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 15, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: sitestyle, data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezpackage, id: 175, identifier: sitestyle, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 4, serialized_data_text: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_description_list: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_name_list: 'a:2:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:9:"Sitestyle";}', version: 0 } - { can_translate: 0, category: '', contentclass_id: 15, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 1, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: site.ini, data_text2: MailSettings, data_text3: AdminEmail, data_text4: '', data_text5: override;user;admin;demo, data_type_string: ezinisetting, id: 177, identifier: email, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 6, serialized_data_text: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_description_list: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_name_list: 'a:2:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:5:"Email";}', version: 0 } - { can_translate: 0, category: '', contentclass_id: 15, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 1, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: site.ini, data_text2: SiteSettings, data_text3: SiteURL, data_text4: '', data_text5: override;user;admin;demo, data_type_string: ezinisetting, id: 178, identifier: siteurl, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 7, serialized_data_text: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_description_list: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_name_list: 'a:2:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:8:"Site URL";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 4, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 10, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: eztext, id: 179, identifier: signature, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 4, serialized_data_text: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_description_list: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_name_list: 'a:2:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:9:"Signature";}', version: 0 } - - { can_translate: 1, category: '', contentclass_id: 4, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 1, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezimage, id: 180, identifier: image, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 5, serialized_data_text: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_description_list: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_name_list: 'a:2:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:5:"Image";}', version: 0 } + - { can_translate: 1, category: '', contentclass_id: 4, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 1024, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: 'KB', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezimage, id: 180, identifier: image, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 5, serialized_data_text: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_description_list: 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}', serialized_name_list: 'a:2:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:5:"Image";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 16, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 255, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: 'New article', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezstring, id: 181, identifier: title, is_information_collector: 0, is_required: 1, is_searchable: 1, placement: 1, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:5:"Title";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 16, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 255, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezstring, id: 182, identifier: short_title, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 2, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:11:"Short title";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 16, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezauthor, id: 183, identifier: author, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 3, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:6:"Author";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - - { can_translate: 1, category: '', contentclass_id: 16, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezimage, id: 186, identifier: image, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 6, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}', version: 0 } + - { can_translate: 1, category: '', contentclass_id: 16, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: 'KB', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezimage, id: 186, identifier: image, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 6, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 16, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezdatetime, id: 188, identifier: publish_date, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 8, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:12:"Publish date";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 16, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezkeyword, id: 190, identifier: tags, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 10, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:2:{s:6:"eng-GB";s:0:"";s:16:"always-available";s:6:"eng-GB";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:4:"Tags";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 16, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezgmaplocation, id: 191, identifier: location, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 11, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:2:{s:6:"eng-GB";s:0:"";s:16:"always-available";s:6:"eng-GB";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:8:"Location";s:16:"always-available";s:6:"eng-GB";}', version: 0 } @@ -110,7 +110,7 @@ ezcontentclass_attribute: - { can_translate: 1, category: '', contentclass_id: 19, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezstring, id: 201, identifier: name, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 1, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:4:"Name";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 19, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezstring, id: 202, identifier: product_number, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 2, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:14:"Product number";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 19, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezfloat, id: 205, identifier: price, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 5, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:5:"Price";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - - { can_translate: 1, category: '', contentclass_id: 19, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezimage, id: 206, identifier: image, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 6, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}', version: 0 } + - { can_translate: 1, category: '', contentclass_id: 19, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: 'KB', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezimage, id: 206, identifier: image, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 6, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 19, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezmultioption, id: 208, identifier: additional_options, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 8, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:18:"Additional options";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 19, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezkeyword, id: 210, identifier: tags, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 10, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:2:{s:6:"eng-GB";s:0:"";s:16:"always-available";s:6:"eng-GB";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:4:"Tags";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 19, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezcomcomments, id: 211, identifier: comments, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 11, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:2:{s:6:"eng-GB";s:0:"";s:16:"always-available";s:6:"eng-GB";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:8:"Comments";s:16:"always-available";s:6:"eng-GB";}', version: 0 } @@ -132,7 +132,7 @@ ezcontentclass_attribute: - { can_translate: 1, category: '', contentclass_id: 24, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezkeyword, id: 234, identifier: tags, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 5, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:2:{s:6:"eng-GB";s:0:"";s:16:"always-available";s:6:"eng-GB";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:4:"Tags";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 24, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezcomcomments, id: 235, identifier: comments, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 6, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:2:{s:6:"eng-GB";s:0:"";s:16:"always-available";s:6:"eng-GB";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:8:"Comments";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 25, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 150, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezstring, id: 236, identifier: name, is_information_collector: 0, is_required: 1, is_searchable: 1, placement: 1, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:4:"Name";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - - { can_translate: 1, category: '', contentclass_id: 25, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 2, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezimage, id: 238, identifier: image, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 3, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}', version: 0 } + - { can_translate: 1, category: '', contentclass_id: 25, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 2048, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: 'KB', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezimage, id: 238, identifier: image, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 3, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 25, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezkeyword, id: 240, identifier: tags, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 5, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:2:{s:6:"eng-GB";s:0:"";s:16:"always-available";s:6:"eng-GB";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:4:"Tags";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 26, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 255, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezstring, id: 241, identifier: name, is_information_collector: 0, is_required: 1, is_searchable: 1, placement: 1, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:0:{}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:4:"Name";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 26, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezurl, id: 243, identifier: location, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 3, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:0:{}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:8:"Location";s:16:"always-available";s:6:"eng-GB";}', version: 0 } @@ -157,7 +157,7 @@ ezcontentclass_attribute: - { can_translate: 0, category: '', contentclass_id: 32, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ezselection><options><option id=\"0\" name=\"Calendar\"/><option id=\"1\" name=\"Program\"/></options></ezselection>\n", data_type_string: ezselection, id: 266, identifier: view, is_information_collector: 0, is_required: 1, is_searchable: 0, placement: 2, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:1:{s:6:"eng-GB";s:0:"";}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:4:"View";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 33, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezstring, id: 267, identifier: name, is_information_collector: 0, is_required: 1, is_searchable: 0, placement: 1, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:0:{}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:4:"Name";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 33, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezstring, id: 268, identifier: url, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 2, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:0:{}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:3:"URL";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - - { can_translate: 1, category: '', contentclass_id: 33, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezimage, id: 269, identifier: image, is_information_collector: 0, is_required: 1, is_searchable: 0, placement: 3, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:0:{}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}', version: 0 } + - { can_translate: 1, category: '', contentclass_id: 33, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: 'KB', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezimage, id: 269, identifier: image, is_information_collector: 0, is_required: 1, is_searchable: 0, placement: 3, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:0:{}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:5:"Image";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 33, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 10, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: eztext, id: 270, identifier: image_map, is_information_collector: 0, is_required: 0, is_searchable: 0, placement: 4, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:0:{}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:9:"Image map";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 33, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezkeyword, id: 271, identifier: tags, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 5, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:0:{}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:4:"Tags";s:16:"always-available";s:6:"eng-GB";}', version: 0 } - { can_translate: 1, category: '', contentclass_id: 34, data_float1: 0, data_float2: 0, data_float3: 0, data_float4: 0, data_int1: 0, data_int2: 0, data_int3: 0, data_int4: 0, data_text1: '', data_text2: '', data_text3: '', data_text4: '', data_text5: '', data_type_string: ezstring, id: 272, identifier: title, is_information_collector: 0, is_required: 0, is_searchable: 1, placement: 1, serialized_data_text: 'a:0:{}', serialized_description_list: 'a:0:{}', serialized_name_list: 'a:2:{s:6:"eng-GB";s:5:"Title";s:16:"always-available";s:6:"eng-GB";}', version: 0 } @@ -235,10 +235,11 @@ ezcontentclass_name: - { contentclass_id: 34, contentclass_version: 0, language_id: 9, language_locale: eng-GB, name: Forums } - { contentclass_id: 35, contentclass_version: 0, language_id: 9, language_locale: eng-GB, name: Video } ezcontentclassgroup: - - { created: 1031216928, creator_id: 14, id: 1, modified: 1033922106, modifier_id: 14, name: Content } - - { created: 1031216941, creator_id: 14, id: 2, modified: 1033922113, modifier_id: 14, name: Users } - - { created: 1032009743, creator_id: 14, id: 3, modified: 1033922120, modifier_id: 14, name: Media } - - { created: 1081858024, creator_id: 14, id: 4, modified: 1081858024, modifier_id: 14, name: Setup } + - { created: 1031216928, creator_id: 14, id: 1, modified: 1033922106, modifier_id: 14, name: Content, is_system: 0 } + - { created: 1031216941, creator_id: 14, id: 2, modified: 1033922113, modifier_id: 14, name: Users, is_system: 0 } + - { created: 1032009743, creator_id: 14, id: 3, modified: 1033922120, modifier_id: 14, name: Media, is_system: 0 } + - { created: 1081858024, creator_id: 14, id: 4, modified: 1081858024, modifier_id: 14, name: Setup, is_system: 0 } + - { created: 1634729305, creator_id: 14, id: 5, modified: 1634729305, modifier_id: 14, name: System, is_system: 1 } ezcontentobject: - { contentclass_id: 3, current_version: 1, id: 4, initial_language_id: 2, language_mask: 3, modified: 1033917596, name: Users, owner_id: 14, published: 1033917596, remote_id: f5c88a2209584891056f987fd965b0ba, section_id: 2, status: 1 } - { contentclass_id: 4, current_version: 2, id: 10, initial_language_id: 2, language_mask: 3, modified: 1072180405, name: 'Anonymous User', owner_id: 14, published: 1033920665, remote_id: faaeb9be3bd98ed09f606fc16d144eca, section_id: 2, status: 1 } @@ -247,13 +248,13 @@ ezcontentobject: - { contentclass_id: 3, current_version: 1, id: 13, initial_language_id: 2, language_mask: 3, modified: 1033920794, name: Editors, owner_id: 14, published: 1033920794, remote_id: 3c160cca19fb135f83bd02d911f04db2, section_id: 2, status: 1 } - { contentclass_id: 4, current_version: 4, id: 14, initial_language_id: 2, language_mask: 3, modified: 1343140540, name: 'Administrator User', owner_id: 14, published: 1033920830, remote_id: 1bb4fe25487f05527efa8bfd394cecc7, section_id: 2, status: 1 } - { contentclass_id: 1, current_version: 1, id: 41, initial_language_id: 2, language_mask: 3, modified: 1060695457, name: Media, owner_id: 14, published: 1060695457, remote_id: a6e35cbcb7cd6ae4b691f3eee30cd262, section_id: 3, status: 1 } - - { contentclass_id: 3, current_version: 1, id: 42, initial_language_id: 2, language_mask: 3, modified: 1072180330, name: 'Anonymous Users', owner_id: 14, published: 1072180330, remote_id: 15b256dbea2ae72418ff5facc999e8f9, section_id: 2, status: 1 } + - { contentclass_id: 3, current_version: 1, id: 42, initial_language_id: 2, language_mask: 3, modified: 1072180330, name: 'Anonymous users', owner_id: 14, published: 1072180330, remote_id: 15b256dbea2ae72418ff5facc999e8f9, section_id: 2, status: 1 } - { contentclass_id: 1, current_version: 1, id: 45, initial_language_id: 2, language_mask: 3, modified: 1079684190, name: Setup, owner_id: 14, published: 1079684190, remote_id: 241d538ce310074e602f29f49e44e938, section_id: 4, status: 1 } - { contentclass_id: 1, current_version: 1, id: 49, initial_language_id: 2, language_mask: 3, modified: 1080220197, name: Images, owner_id: 14, published: 1080220197, remote_id: e7ff633c6b8e0fd3531e74c6e712bead, section_id: 3, status: 1 } - { contentclass_id: 1, current_version: 1, id: 50, initial_language_id: 2, language_mask: 3, modified: 1080220220, name: Files, owner_id: 14, published: 1080220220, remote_id: 732a5acd01b51a6fe6eab448ad4138a9, section_id: 3, status: 1 } - { contentclass_id: 1, current_version: 1, id: 51, initial_language_id: 2, language_mask: 3, modified: 1080220233, name: Multimedia, owner_id: 14, published: 1080220233, remote_id: 09082deb98662a104f325aaa8c4933d3, section_id: 3, status: 1 } - { contentclass_id: 14, current_version: 1, id: 52, initial_language_id: 2, language_mask: 2, modified: 1082016591, name: 'Common INI settings', owner_id: 14, published: 1082016591, remote_id: 27437f3547db19cf81a33c92578b2c89, section_id: 4, status: 1 } - - { contentclass_id: 15, current_version: 2, id: 54, initial_language_id: 2, language_mask: 2, modified: 1301062376, name: 'eZ Publish Demo Design (without demo content)', owner_id: 14, published: 1082016652, remote_id: 8b8b22fe3c6061ed500fbd2b377b885f, section_id: 5, status: 1 } + - { contentclass_id: 15, current_version: 2, id: 54, initial_language_id: 2, language_mask: 2, modified: 1301062376, name: 'Ibexa Demo Design (without demo content)', owner_id: 14, published: 1082016652, remote_id: 8b8b22fe3c6061ed500fbd2b377b885f, section_id: 5, status: 1 } - { contentclass_id: 1, current_version: 1, id: 56, initial_language_id: 2, language_mask: 3, modified: 1103023132, name: Design, owner_id: 14, published: 1103023132, remote_id: 08799e609893f7aba22f10cb466d9cc8, section_id: 5, status: 1 } - { contentclass_id: 21, current_version: 1, id: 57, initial_language_id: 8, language_mask: 9, modified: 1196268696, name: Home, owner_id: 14, published: 1195480486, remote_id: 8a9c9c761004866fb458d89910f52bee, section_id: 1, status: 1 } - { contentclass_id: 20, current_version: 1, id: 58, initial_language_id: 8, language_mask: 8, modified: 1332927282, name: 'Contact Us', owner_id: 14, published: 1332927205, remote_id: f8cc7a4cf8a964a1a0ea6666f5da7d0d, section_id: 1, status: 1 } @@ -279,7 +280,7 @@ ezcontentobject_attribute: - { attribute_original_id: 30, contentclassattribute_id: 12, contentobject_id: 14, data_float: 0, data_int: 0, data_text: '', data_type_string: ezuser, id: 30, language_code: eng-US, language_id: 3, sort_key_int: 0, sort_key_string: '', version: 3 } - { attribute_original_id: 30, contentclassattribute_id: 12, contentobject_id: 14, data_float: 0, data_int: 0, data_text: '', data_type_string: ezuser, id: 30, language_code: eng-US, language_id: 3, sort_key_int: 0, sort_key_string: '', version: 4 } - { attribute_original_id: 0, contentclassattribute_id: 4, contentobject_id: 41, data_float: 0, data_int: 0, data_text: Media, data_type_string: ezstring, id: 98, language_code: eng-US, language_id: 3, sort_key_int: 0, sort_key_string: media, version: 1 } - - { attribute_original_id: 0, contentclassattribute_id: 6, contentobject_id: 42, data_float: 0, data_int: 0, data_text: 'Anonymous Users', data_type_string: ezstring, id: 100, language_code: eng-US, language_id: 3, sort_key_int: 0, sort_key_string: 'anonymous users', version: 1 } + - { attribute_original_id: 0, contentclassattribute_id: 6, contentobject_id: 42, data_float: 0, data_int: 0, data_text: 'Anonymous users', data_type_string: ezstring, id: 100, language_code: eng-US, language_id: 3, sort_key_int: 0, sort_key_string: 'anonymous users', version: 1 } - { attribute_original_id: 0, contentclassattribute_id: 7, contentobject_id: 42, data_float: 0, data_int: 0, data_text: 'User group for the anonymous user', data_type_string: ezstring, id: 101, language_code: eng-US, language_id: 3, sort_key_int: 0, sort_key_string: 'user group for the anonymous user', version: 1 } - { attribute_original_id: 0, contentclassattribute_id: 155, contentobject_id: 41, data_float: 0, data_int: 0, data_text: '', data_type_string: ezstring, id: 103, language_code: eng-US, language_id: 3, sort_key_int: 0, sort_key_string: '', version: 1 } - { attribute_original_id: 0, contentclassattribute_id: 158, contentobject_id: 41, data_float: 0, data_int: 0, data_text: '', data_type_string: ezboolean, id: 109, language_code: eng-US, language_id: 3, sort_key_int: 0, sort_key_string: '', version: 1 } @@ -308,11 +309,11 @@ ezcontentobject_attribute: - { attribute_original_id: 0, contentclassattribute_id: 169, contentobject_id: 52, data_float: 0, data_int: 0, data_text: '=geometry/scale=100;100', data_type_string: ezinisetting, id: 167, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 1 } - { attribute_original_id: 0, contentclassattribute_id: 170, contentobject_id: 52, data_float: 0, data_int: 0, data_text: '=geometry/scale=200;200', data_type_string: ezinisetting, id: 168, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 1 } - { attribute_original_id: 0, contentclassattribute_id: 171, contentobject_id: 52, data_float: 0, data_int: 0, data_text: '=geometry/scale=300;300', data_type_string: ezinisetting, id: 169, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 1 } - - { attribute_original_id: 0, contentclassattribute_id: 172, contentobject_id: 54, data_float: 0, data_int: 0, data_text: 'eZ Publish Demo Design (without demo content)', data_type_string: ezinisetting, id: 170, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 2 } + - { attribute_original_id: 0, contentclassattribute_id: 172, contentobject_id: 54, data_float: 0, data_int: 0, data_text: 'Ibexa Demo Design (without demo content)', data_type_string: ezinisetting, id: 170, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 2 } - { attribute_original_id: 0, contentclassattribute_id: 173, contentobject_id: 54, data_float: 0, data_int: 0, data_text: "author=eZ Systems\ncopyright=eZ Systems\ndescription=Content Management System\nkeywords=cms, publish, e-commerce, content management, development framework", data_type_string: ezinisetting, id: 171, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 2 } - - { attribute_original_id: 0, contentclassattribute_id: 174, contentobject_id: 54, data_float: 0, data_int: 0, data_text: "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ezimage serial_number=\"1\" is_valid=\"1\" filename=\"eZ-Publish-Demo-Design-without-demo-content1.png\" suffix=\"png\" basename=\"eZ-Publish-Demo-Design-without-demo-content1\" dirpath=\"var/ezdemo_site/storage/images/design/plain-site/172-2-eng-US\" url=\"var/ezdemo_site/storage/images/design/plain-site/172-2-eng-US/eZ-Publish-Demo-Design-without-demo-content1.png\" original_filename=\"logo.png\" mime_type=\"image/png\" width=\"138\" height=\"46\" alternative_text=\"\" alias_key=\"1293033771\" timestamp=\"1343140541\"><original attribute_id=\"172\" attribute_version=\"2\" attribute_language=\"eng-US\"/></ezimage>\n", data_type_string: ezimage, id: 172, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 2 } + - { attribute_original_id: 0, contentclassattribute_id: 174, contentobject_id: 54, data_float: 0, data_int: 0, data_text: "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ezimage serial_number=\"1\" is_valid=\"1\" filename=\"Ibexa-Demo-Design-without-demo-content1.png\" suffix=\"png\" basename=\"Ibexa-Demo-Design-without-demo-content1\" dirpath=\"var/ibexa_demo_site/storage/images/design/plain-site/172-2-eng-US\" url=\"var/ibexa_demo_site/storage/images/design/plain-site/172-2-eng-US/Ibexa-Demo-Design-without-demo-content1.png\" original_filename=\"logo.png\" mime_type=\"image/png\" width=\"138\" height=\"46\" alternative_text=\"\" alias_key=\"1293033771\" timestamp=\"1343140541\"><original attribute_id=\"172\" attribute_version=\"2\" attribute_language=\"eng-US\"/></ezimage>\n", data_type_string: ezimage, id: 172, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 2 } - { attribute_original_id: 0, contentclassattribute_id: 175, contentobject_id: 54, data_float: 0, data_int: 0, data_text: 0, data_type_string: ezpackage, id: 173, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: 0, version: 2 } - - { attribute_original_id: 0, contentclassattribute_id: 177, contentobject_id: 54, data_float: 0, data_int: 0, data_text: spam@ez.no, data_type_string: ezinisetting, id: 175, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 2 } + - { attribute_original_id: 0, contentclassattribute_id: 177, contentobject_id: 54, data_float: 0, data_int: 0, data_text: spam@ibexa.co, data_type_string: ezinisetting, id: 175, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 2 } - { attribute_original_id: 0, contentclassattribute_id: 178, contentobject_id: 54, data_float: 0, data_int: 0, data_text: ws2/dump_47_demo/index.php, data_type_string: ezinisetting, id: 176, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 2 } - { attribute_original_id: 0, contentclassattribute_id: 179, contentobject_id: 10, data_float: 0, data_int: 0, data_text: '', data_type_string: eztext, id: 177, language_code: eng-US, language_id: 3, sort_key_int: 0, sort_key_string: '', version: 2 } - { attribute_original_id: 0, contentclassattribute_id: 179, contentobject_id: 14, data_float: 0, data_int: 0, data_text: '', data_type_string: eztext, id: 178, language_code: eng-US, language_id: 3, sort_key_int: 0, sort_key_string: '', version: 3 } @@ -339,7 +340,7 @@ ezcontentobject_attribute: - { attribute_original_id: 0, contentclassattribute_id: 285, contentobject_id: 54, data_float: 0, data_int: null, data_text: /rss/feed/my_feed, data_type_string: ezstring, id: 201, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: /rss/feed/my_feed, version: 2 } - { attribute_original_id: 0, contentclassattribute_id: 286, contentobject_id: 54, data_float: 0, data_int: null, data_text: 'Shopping basket content', data_type_string: ezstring, id: 202, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: 'shopping basket', version: 2 } - { attribute_original_id: 0, contentclassattribute_id: 287, contentobject_id: 54, data_float: 0, data_int: null, data_text: 'Site settings', data_type_string: ezstring, id: 203, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: 'site settings', version: 2 } - - { attribute_original_id: 0, contentclassattribute_id: 288, contentobject_id: 54, data_float: 0, data_int: null, data_text: 'Copyright © 2012 <a href="http://ez.no" title="eZ Systems">eZ Systems AS</a> (except where otherwise noted). All rights reserved.', data_type_string: eztext, id: 204, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 2 } + - { attribute_original_id: 0, contentclassattribute_id: 288, contentobject_id: 54, data_float: 0, data_int: null, data_text: 'Copyright © 2012 <a href="http://ibexa.co" title="eZ Systems">eZ Systems AS</a> (except where otherwise noted). All rights reserved.', data_type_string: eztext, id: 204, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 2 } - { attribute_original_id: 0, contentclassattribute_id: 289, contentobject_id: 54, data_float: 0, data_int: 0, data_text: '', data_type_string: ezboolean, id: 205, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 2 } - { attribute_original_id: 0, contentclassattribute_id: 290, contentobject_id: 54, data_float: 0, data_int: null, data_text: '', data_type_string: eztext, id: 206, language_code: eng-US, language_id: 2, sort_key_int: 0, sort_key_string: '', version: 2 } - { attribute_original_id: 0, contentclassattribute_id: 6, contentobject_id: 59, data_float: 0, data_int: null, data_text: Partners, data_type_string: ezstring, id: 207, language_code: eng-US, language_id: 3, sort_key_int: 0, sort_key_string: partners, version: 1 } @@ -361,13 +362,13 @@ ezcontentobject_name: - { content_translation: eng-US, content_version: 3, contentobject_id: 14, language_id: 3, name: 'Administrator User', real_translation: eng-US } - { content_translation: eng-US, content_version: 4, contentobject_id: 14, language_id: 3, name: 'Administrator User', real_translation: eng-US } - { content_translation: eng-US, content_version: 1, contentobject_id: 41, language_id: 3, name: Media, real_translation: eng-US } - - { content_translation: eng-US, content_version: 1, contentobject_id: 42, language_id: 3, name: 'Anonymous Users', real_translation: eng-US } + - { content_translation: eng-US, content_version: 1, contentobject_id: 42, language_id: 3, name: 'Anonymous users', real_translation: eng-US } - { content_translation: eng-US, content_version: 1, contentobject_id: 45, language_id: 3, name: Setup, real_translation: eng-US } - { content_translation: eng-US, content_version: 1, contentobject_id: 49, language_id: 3, name: Images, real_translation: eng-US } - { content_translation: eng-US, content_version: 1, contentobject_id: 50, language_id: 3, name: Files, real_translation: eng-US } - { content_translation: eng-US, content_version: 1, contentobject_id: 51, language_id: 3, name: Multimedia, real_translation: eng-US } - { content_translation: eng-US, content_version: 1, contentobject_id: 52, language_id: 2, name: 'Common INI settings', real_translation: eng-US } - - { content_translation: eng-US, content_version: 2, contentobject_id: 54, language_id: 2, name: 'eZ Publish Demo Design (without demo content)', real_translation: eng-US } + - { content_translation: eng-US, content_version: 2, contentobject_id: 54, language_id: 2, name: 'Ibexa Demo Design (without demo content)', real_translation: eng-US } - { content_translation: eng-US, content_version: 1, contentobject_id: 56, language_id: 3, name: Design, real_translation: eng-US } - { content_translation: eng-GB, content_version: 1, contentobject_id: 57, language_id: 9, name: Home, real_translation: eng-GB } - { content_translation: eng-GB, content_version: 1, contentobject_id: 58, language_id: 8, name: 'Contact Us', real_translation: eng-GB } @@ -416,7 +417,7 @@ ezcontentobject_version: - { contentobject_id: 11, created: 1343140541, creator_id: 14, id: 508, initial_language_id: 2, language_mask: 3, modified: 1343140541, status: 1, user_id: 0, version: 2, workflow_event_pos: 0 } ezimagefile: - { contentobject_attribute_id: 172, filepath: var/storage/images/setup/ez_publish/172-1-eng-GB/ez_publish., id: 1 } - - { contentobject_attribute_id: 172, filepath: var/ezdemo_site/storage/images/design/plain-site/172-2-eng-US/eZ-Publish-Demo-Design-without-demo-content1.png, id: 2 } + - { contentobject_attribute_id: 172, filepath: var/ibexa_demo_site/storage/images/design/plain-site/172-2-eng-US/Ibexa-Demo-Design-without-demo-content1.png, id: 2 } eznode_assignment: - { contentobject_id: 8, contentobject_version: 2, from_node_id: 0, id: 4, is_main: 1, op_code: 2, parent_node: 5, parent_remote_id: '', remote_id: 0, sort_field: 1, sort_order: 1, priority: 0, is_hidden: 0 } - { contentobject_id: 42, contentobject_version: 1, from_node_id: 0, id: 5, is_main: 1, op_code: 2, parent_node: 5, parent_remote_id: '', remote_id: 0, sort_field: 9, sort_order: 1, priority: 0, is_hidden: 0 } diff --git a/tests/integration/Core/Repository/_fixtures/SectionFixture.php b/tests/integration/Core/Repository/_fixtures/SectionFixture.php new file mode 100644 index 0000000000..2bf853b5b8 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/SectionFixture.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Section; + +return [ + [ + 1 => new Section( + [ + 'id' => 1, + 'name' => 'Standard', + 'identifier' => 'standard', + ] + ), + 2 => new Section( + [ + 'id' => 2, + 'name' => 'Users', + 'identifier' => 'users', + ] + ), + 3 => new Section( + [ + 'id' => 3, + 'name' => 'Media', + 'identifier' => 'media', + ] + ), + 4 => new Section( + [ + 'id' => 4, + 'name' => 'Setup', + 'identifier' => 'setup', + ] + ), + 5 => new Section( + [ + 'id' => 5, + 'name' => 'Design', + 'identifier' => 'design', + ] + ), + 6 => new Section( + [ + 'id' => 6, + 'name' => 'Restricted', + 'identifier' => '', + ] + ), + ], + [ + 'standard' => 1, + 'users' => 2, + 'media' => 3, + 'setup' => 4, + 'design' => 5, + ], + [ + '2' => [ + 4 => true, + 10 => true, + 11 => true, + 12 => true, + 13 => true, + 14 => true, + 42 => true, + 59 => true, + ], + '3' => [ + 41 => true, + 49 => true, + 50 => true, + 51 => true, + ], + '4' => [ + 45 => true, + 52 => true, + ], + '5' => [ + 54 => true, + 56 => true, + ], + '1' => [ + 57 => true, + 58 => true, + ], + ], + 6, +]; diff --git a/tests/integration/Core/Repository/_fixtures/Solr/AncestorContent.php b/tests/integration/Core/Repository/_fixtures/Solr/AncestorContent.php new file mode 100644 index 0000000000..7c649c2753 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/AncestorContent.php @@ -0,0 +1,50 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/AncestorLocation.php b/tests/integration/Core/Repository/_fixtures/Solr/AncestorLocation.php new file mode 100644 index 0000000000..a78d3ddf0c --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/AncestorLocation.php @@ -0,0 +1,50 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/ContentId.php b/tests/integration/Core/Repository/_fixtures/Solr/ContentId.php new file mode 100644 index 0000000000..91a1c0d95c --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/ContentId.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/ContentTypeGroupId.php b/tests/integration/Core/Repository/_fixtures/Solr/ContentTypeGroupId.php new file mode 100644 index 0000000000..bec1e0dd26 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/ContentTypeGroupId.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/ContentTypeId.php b/tests/integration/Core/Repository/_fixtures/Solr/ContentTypeId.php new file mode 100644 index 0000000000..5ea3f86ffa --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/ContentTypeId.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldBetween.php b/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldBetween.php new file mode 100644 index 0000000000..3b286e5bdd --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldBetween.php @@ -0,0 +1,60 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1, + 'index' => null, + 'matchedTranslation' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1, + 'totalCount' => 4, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldGt.php b/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldGt.php new file mode 100644 index 0000000000..599eba1b34 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldGt.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldGte.php b/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldGte.php new file mode 100644 index 0000000000..75d1d9eccf --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldGte.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldLt.php b/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldLt.php new file mode 100644 index 0000000000..1d17a9fcf4 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldLt.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldLte.php b/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldLte.php new file mode 100644 index 0000000000..01a3410de2 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/CustomFieldLte.php @@ -0,0 +1,56 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1, + 'totalCount' => 4, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataBetween.php b/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataBetween.php new file mode 100644 index 0000000000..1a59c2d2d3 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataBetween.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataCreated.php b/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataCreated.php new file mode 100644 index 0000000000..1b06bea89a --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataCreated.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataGt.php b/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataGt.php new file mode 100644 index 0000000000..239ae48041 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataGt.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataGte.php b/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataGte.php new file mode 100644 index 0000000000..b56e8984ae --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataGte.php @@ -0,0 +1,56 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 4, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataIn.php b/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataIn.php new file mode 100644 index 0000000000..c569ed3962 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataIn.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataLte.php b/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataLte.php new file mode 100644 index 0000000000..9b57ee2e18 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/DateMetadataLte.php @@ -0,0 +1,110 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 14, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/DeprecatedContentIdQuery.php b/tests/integration/Core/Repository/_fixtures/Solr/DeprecatedContentIdQuery.php new file mode 100644 index 0000000000..ce7a4730d9 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/DeprecatedContentIdQuery.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 0.57124877, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 0.57124877, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 0.57124877, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/Depth.php b/tests/integration/Core/Repository/_fixtures/Solr/Depth.php new file mode 100644 index 0000000000..68233a8cf2 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/Depth.php @@ -0,0 +1,76 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.9200476, + 'totalCount' => 5, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/DepthGt.php b/tests/integration/Core/Repository/_fixtures/Solr/DepthGt.php new file mode 100644 index 0000000000..92fc3be50a --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/DepthGt.php @@ -0,0 +1,43 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.9200476, + 'totalCount' => 2, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/DepthGte.php b/tests/integration/Core/Repository/_fixtures/Solr/DepthGte.php new file mode 100644 index 0000000000..9f8034d913 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/DepthGte.php @@ -0,0 +1,164 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 6 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 7 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 8 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 9 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 10 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 11 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 12 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.9200476, + 'totalCount' => 13, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/DepthIn.php b/tests/integration/Core/Repository/_fixtures/Solr/DepthIn.php new file mode 100644 index 0000000000..c485f4b213 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/DepthIn.php @@ -0,0 +1,98 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.8414208999999999, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.8414208999999999, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.384091, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1.384091, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 1.384091, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 1.384091, + 'index' => null, + 'highlight' => null, + ] + ), + 6 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1.384091, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.8414208999999999, + 'totalCount' => 7, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/DepthLte.php b/tests/integration/Core/Repository/_fixtures/Solr/DepthLte.php new file mode 100644 index 0000000000..f7cc2b7d6e --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/DepthLte.php @@ -0,0 +1,197 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 6 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 7 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 8 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 9 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 10 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 11 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 12 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 13 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 14 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 15 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.9200476, + 'totalCount' => 16, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FacetContentType.php b/tests/integration/Core/Repository/_fixtures/Solr/FacetContentType.php new file mode 100644 index 0000000000..6fd40b9f5d --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FacetContentType.php @@ -0,0 +1,46 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\Facet\ContentTypeFacet; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + 0 => ContentTypeFacet::__set_state([ + 'entries' => [ + 20 => 1, + 21 => 1, + ], + 'name' => 'type', + ]), + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FacetContentTypeMinCount.php b/tests/integration/Core/Repository/_fixtures/Solr/FacetContentTypeMinCount.php new file mode 100644 index 0000000000..620e056d50 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FacetContentTypeMinCount.php @@ -0,0 +1,43 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\Facet\ContentTypeFacet; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + 0 => ContentTypeFacet::__set_state([ + 'entries' => [], + 'name' => 'type', + ]), + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FacetContentTypeMinLimit.php b/tests/integration/Core/Repository/_fixtures/Solr/FacetContentTypeMinLimit.php new file mode 100644 index 0000000000..6fd40b9f5d --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FacetContentTypeMinLimit.php @@ -0,0 +1,46 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\Facet\ContentTypeFacet; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + 0 => ContentTypeFacet::__set_state([ + 'entries' => [ + 20 => 1, + 21 => 1, + ], + 'name' => 'type', + ]), + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FacetSection.php b/tests/integration/Core/Repository/_fixtures/Solr/FacetSection.php new file mode 100644 index 0000000000..36e2a864a9 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FacetSection.php @@ -0,0 +1,45 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\Facet\SectionFacet; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + 0 => SectionFacet::__set_state([ + 'entries' => [ + 1 => 2, + ], + 'name' => 'section', + ]), + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FacetUser.php b/tests/integration/Core/Repository/_fixtures/Solr/FacetUser.php new file mode 100644 index 0000000000..1e7a663994 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FacetUser.php @@ -0,0 +1,45 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\Facet\UserFacet; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + 0 => UserFacet::__set_state([ + 'entries' => [ + 14 => 2, + ], + 'name' => 'creator', + ]), + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/Field.php b/tests/integration/Core/Repository/_fixtures/Solr/Field.php new file mode 100644 index 0000000000..664d54322a --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/Field.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FieldBetween.php b/tests/integration/Core/Repository/_fixtures/Solr/FieldBetween.php new file mode 100644 index 0000000000..d532c3b884 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FieldBetween.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FieldIn.php b/tests/integration/Core/Repository/_fixtures/Solr/FieldIn.php new file mode 100644 index 0000000000..b7ddb65eb9 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FieldIn.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FieldOr.php b/tests/integration/Core/Repository/_fixtures/Solr/FieldOr.php new file mode 100644 index 0000000000..f0fbf87c25 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FieldOr.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FieldRelation.php b/tests/integration/Core/Repository/_fixtures/Solr/FieldRelation.php new file mode 100644 index 0000000000..bb787601bf --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FieldRelation.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 61, + 'title' => 'User gallery', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FieldRelationAll.php b/tests/integration/Core/Repository/_fixtures/Solr/FieldRelationAll.php new file mode 100644 index 0000000000..71abe8d5c9 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FieldRelationAll.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 60, + 'title' => 'Image gallery', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 61, + 'title' => 'User gallery', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FieldUnknown.php b/tests/integration/Core/Repository/_fixtures/Solr/FieldUnknown.php new file mode 100644 index 0000000000..5ef9d008c9 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FieldUnknown.php @@ -0,0 +1,19 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 0, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FullText.php b/tests/integration/Core/Repository/_fixtures/Solr/FullText.php new file mode 100644 index 0000000000..312d3f099b --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FullText.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => 0.05622538, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 0.20990808, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 0.20990808, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FullTextFiltered.php b/tests/integration/Core/Repository/_fixtures/Solr/FullTextFiltered.php new file mode 100644 index 0000000000..0a3d54713d --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FullTextFiltered.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 1.3987858, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.3987858, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/FullTextWildcard.php b/tests/integration/Core/Repository/_fixtures/Solr/FullTextWildcard.php new file mode 100644 index 0000000000..244c9714d2 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/FullTextWildcard.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/LanguageCode.php b/tests/integration/Core/Repository/_fixtures/Solr/LanguageCode.php new file mode 100644 index 0000000000..9d574e4a43 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/LanguageCode.php @@ -0,0 +1,43 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 2.7917593, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 2.7917593, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 2.7917593, + 'totalCount' => 2, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/LanguageCodeAlwaysAvailable.php b/tests/integration/Core/Repository/_fixtures/Solr/LanguageCodeAlwaysAvailable.php new file mode 100644 index 0000000000..f2c412d1b5 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/LanguageCodeAlwaysAvailable.php @@ -0,0 +1,87 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 0.20774001, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 0.20774001, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 0.20774001, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 3.007218, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 1.295869, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 0.20774001, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 3.007218, + 'totalCount' => 16, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/LanguageCodeIn.php b/tests/integration/Core/Repository/_fixtures/Solr/LanguageCodeIn.php new file mode 100644 index 0000000000..b5962b5f14 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/LanguageCodeIn.php @@ -0,0 +1,109 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 0.49504447000000001, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 0.49504447000000001, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => 0.18718657, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => 0.18718657, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 0.49504447000000001, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1.8913484, + 'index' => null, + 'highlight' => null, + ] + ), + 6 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 0.81501900000000005, + 'index' => null, + 'highlight' => null, + ] + ), + 7 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 0.49504447000000001, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.8913484, + 'totalCount' => 18, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/LocationId.php b/tests/integration/Core/Repository/_fixtures/Solr/LocationId.php new file mode 100644 index 0000000000..bbedfb0f3b --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/LocationId.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/LocationRemoteId.php b/tests/integration/Core/Repository/_fixtures/Solr/LocationRemoteId.php new file mode 100644 index 0000000000..bbedfb0f3b --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/LocationRemoteId.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/LogicalAnd.php b/tests/integration/Core/Repository/_fixtures/Solr/LogicalAnd.php new file mode 100644 index 0000000000..9d79a0a416 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/LogicalAnd.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/LogicalNot.php b/tests/integration/Core/Repository/_fixtures/Solr/LogicalNot.php new file mode 100644 index 0000000000..9d79a0a416 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/LogicalNot.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/LogicalOr.php b/tests/integration/Core/Repository/_fixtures/Solr/LogicalOr.php new file mode 100644 index 0000000000..9c7dae08e5 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/LogicalOr.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 3, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/MatchNone.php b/tests/integration/Core/Repository/_fixtures/Solr/MatchNone.php new file mode 100644 index 0000000000..c1b1d19bcd --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/MatchNone.php @@ -0,0 +1,19 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 0, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/ParentLocationId.php b/tests/integration/Core/Repository/_fixtures/Solr/ParentLocationId.php new file mode 100644 index 0000000000..dde9ad02eb --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/ParentLocationId.php @@ -0,0 +1,65 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 5, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/QueryCustomField.php b/tests/integration/Core/Repository/_fixtures/Solr/QueryCustomField.php new file mode 100644 index 0000000000..e64b071ffa --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/QueryCustomField.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.9982654, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.9982654, + 'totalCount' => 1, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/QueryModifiedField.php b/tests/integration/Core/Repository/_fixtures/Solr/QueryModifiedField.php new file mode 100644 index 0000000000..966b804db5 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/QueryModifiedField.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.7448496, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.7448496, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.7448496, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/RemoteId.php b/tests/integration/Core/Repository/_fixtures/Solr/RemoteId.php new file mode 100644 index 0000000000..91a1c0d95c --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/RemoteId.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 2, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SectionId.php b/tests/integration/Core/Repository/_fixtures/Solr/SectionId.php new file mode 100644 index 0000000000..bec1e0dd26 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SectionId.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/Sibling.php b/tests/integration/Core/Repository/_fixtures/Solr/Sibling.php new file mode 100644 index 0000000000..4c79896099 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/Sibling.php @@ -0,0 +1,56 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => null, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => null, + 'totalCount' => 4, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortContentName.php b/tests/integration/Core/Repository/_fixtures/Solr/SortContentName.php new file mode 100644 index 0000000000..dafb7c1132 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortContentName.php @@ -0,0 +1,128 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 12, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortDateModified.php b/tests/integration/Core/Repository/_fixtures/Solr/SortDateModified.php new file mode 100644 index 0000000000..04d1ddd895 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortDateModified.php @@ -0,0 +1,109 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 6 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + 7 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.9200476, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.9200476, + 'totalCount' => 8, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortDatePublished.php b/tests/integration/Core/Repository/_fixtures/Solr/SortDatePublished.php new file mode 100644 index 0000000000..bec1e0dd26 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortDatePublished.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortDesc.php b/tests/integration/Core/Repository/_fixtures/Solr/SortDesc.php new file mode 100644 index 0000000000..e697140314 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortDesc.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortFieldMultipleTypes.php b/tests/integration/Core/Repository/_fixtures/Solr/SortFieldMultipleTypes.php new file mode 100644 index 0000000000..72a025f8bc --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortFieldMultipleTypes.php @@ -0,0 +1,128 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1, + 'totalCount' => 12, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortFieldMultipleTypesReverse.php b/tests/integration/Core/Repository/_fixtures/Solr/SortFieldMultipleTypesReverse.php new file mode 100644 index 0000000000..603c49d972 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortFieldMultipleTypesReverse.php @@ -0,0 +1,128 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1, + 'totalCount' => 12, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortFieldMultipleTypesSlice.php b/tests/integration/Core/Repository/_fixtures/Solr/SortFieldMultipleTypesSlice.php new file mode 100644 index 0000000000..9eb99a5e7e --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortFieldMultipleTypesSlice.php @@ -0,0 +1,65 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1, + 'totalCount' => 12, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortFieldMultipleTypesSliceReverse.php b/tests/integration/Core/Repository/_fixtures/Solr/SortFieldMultipleTypesSliceReverse.php new file mode 100644 index 0000000000..29f24a201d --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortFieldMultipleTypesSliceReverse.php @@ -0,0 +1,65 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1, + 'totalCount' => 12, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortFolderName.php b/tests/integration/Core/Repository/_fixtures/Solr/SortFolderName.php new file mode 100644 index 0000000000..f862251f8b --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortFolderName.php @@ -0,0 +1,74 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1, + 'totalCount' => 6, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortLocationDepth.php b/tests/integration/Core/Repository/_fixtures/Solr/SortLocationDepth.php new file mode 100644 index 0000000000..e2514e2a5b --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortLocationDepth.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortMultiple.php b/tests/integration/Core/Repository/_fixtures/Solr/SortMultiple.php new file mode 100644 index 0000000000..ef18e5cdbf --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortMultiple.php @@ -0,0 +1,56 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 4, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortNone.php b/tests/integration/Core/Repository/_fixtures/Solr/SortNone.php new file mode 100644 index 0000000000..bec1e0dd26 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortNone.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortPathString.php b/tests/integration/Core/Repository/_fixtures/Solr/SortPathString.php new file mode 100644 index 0000000000..9223efa242 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortPathString.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortSectionIdentifier.php b/tests/integration/Core/Repository/_fixtures/Solr/SortSectionIdentifier.php new file mode 100644 index 0000000000..40e005b9bc --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortSectionIdentifier.php @@ -0,0 +1,146 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 12 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 13 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 14, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/SortSectionName.php b/tests/integration/Core/Repository/_fixtures/Solr/SortSectionName.php new file mode 100644 index 0000000000..f897e4c624 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/SortSectionName.php @@ -0,0 +1,146 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 12 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 13 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 14, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/Status.php b/tests/integration/Core/Repository/_fixtures/Solr/Status.php new file mode 100644 index 0000000000..6daabfb8ae --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/Status.php @@ -0,0 +1,182 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 12 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 13 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 14 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 15 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 16 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 17 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 18, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/Subtree.php b/tests/integration/Core/Repository/_fixtures/Solr/Subtree.php new file mode 100644 index 0000000000..bec1e0dd26 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/Subtree.php @@ -0,0 +1,92 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1.0, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1.0, + 'totalCount' => 8, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/UserMetadata.php b/tests/integration/Core/Repository/_fixtures/Solr/UserMetadata.php new file mode 100644 index 0000000000..c3733d3cc7 --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/UserMetadata.php @@ -0,0 +1,182 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state([ + 'facets' => [ + ], + 'searchHits' => [ + 0 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 1 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 2 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 3 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 4 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 5 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 6 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 7 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 8 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 9 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 10 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 11 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 12 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 13 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 14 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 15 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 16 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + 17 => SearchHit::__set_state([ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 1, + 'index' => null, + 'highlight' => null, + ]), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 1, + 'totalCount' => 18, +]); diff --git a/tests/integration/Core/Repository/_fixtures/Solr/Visibility.php b/tests/integration/Core/Repository/_fixtures/Solr/Visibility.php new file mode 100644 index 0000000000..02cb22d25c --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/Solr/Visibility.php @@ -0,0 +1,219 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; + +return SearchResult::__set_state( + [ + 'facets' => [], + 'searchHits' => [ + 0 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 4, + 'title' => 'Users', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 1 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 10, + 'title' => 'Anonymous User', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 2 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 11, + 'title' => 'Members', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 3 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 12, + 'title' => 'Administrator users', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 4 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 13, + 'title' => 'Editors', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 5 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 14, + 'title' => 'Administrator User', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 6 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 41, + 'title' => 'Media', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 7 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 42, + 'title' => 'Anonymous users', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 8 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 45, + 'title' => 'Setup', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 9 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 49, + 'title' => 'Images', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 10 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 50, + 'title' => 'Files', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 11 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 51, + 'title' => 'Multimedia', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 12 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 52, + 'title' => 'Common INI settings', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 13 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 54, + 'title' => 'Ibexa Demo Design (without demo content)', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 14 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 56, + 'title' => 'Design', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 15 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 57, + 'title' => 'Home', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 16 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 58, + 'title' => 'Contact Us', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + 17 => SearchHit::__set_state( + [ + 'valueObject' => [ + 'id' => 59, + 'title' => 'Partners', + ], + 'score' => 0.9459328, + 'index' => null, + 'highlight' => null, + ] + ), + ], + 'spellSuggestion' => null, + 'time' => 1, + 'timedOut' => null, + 'maxScore' => 0.9459328, + 'totalCount' => 18, + ] +); diff --git a/tests/integration/Core/Repository/_fixtures/URLAliasFixture.php b/tests/integration/Core/Repository/_fixtures/URLAliasFixture.php new file mode 100644 index 0000000000..8e12fa449f --- /dev/null +++ b/tests/integration/Core/Repository/_fixtures/URLAliasFixture.php @@ -0,0 +1,552 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; + +return [ + [ + 14 => new URLAlias( + [ + 'id' => 14, + 'type' => 2, + 'destination' => null, + 'path' => '/foo_bar_folder', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 39 => new URLAlias( + [ + 'id' => 39, + 'type' => 0, + 'destination' => 60, + 'path' => '/Contact-Us', + 'languageCodes' => [ + 0 => 'eng-GB', + ], + 'alwaysAvailable' => 0, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 25 => new URLAlias( + [ + 'id' => 25, + 'type' => 0, + 'destination' => 58, + 'path' => '/Design', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 13 => new URLAlias( + [ + 'id' => 13, + 'type' => 0, + 'destination' => 48, + 'path' => '/Setup2', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 17 => new URLAlias( + [ + 'id' => 17, + 'type' => 2, + 'destination' => null, + 'path' => '/media2', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 9 => new URLAlias( + [ + 'id' => 9, + 'type' => 0, + 'destination' => 43, + 'path' => '/Media', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 21 => new URLAlias( + [ + 'id' => 21, + 'type' => 2, + 'destination' => null, + 'path' => '/setup3', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 3 => new URLAlias( + [ + 'id' => 3, + 'type' => 2, + 'destination' => null, + 'path' => '/users2', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 2 => new URLAlias( + [ + 'id' => 2, + 'type' => 0, + 'destination' => 5, + 'path' => '/Users', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 1 => new URLAlias( + [ + 'id' => 1, + 'type' => 0, + 'destination' => 2, + 'path' => '/', + 'languageCodes' => [ + 0 => 'eng-US', + 1 => 'eng-GB', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 40 => new URLAlias( + [ + 'id' => 40, + 'type' => 0, + 'destination' => 61, + 'path' => '/Users/Partners', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 6 => new URLAlias( + [ + 'id' => 6, + 'type' => 0, + 'destination' => 14, + 'path' => '/Users/Editors', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 10 => new URLAlias( + [ + 'id' => 10, + 'type' => 0, + 'destination' => 44, + 'path' => '/Users/Anonymous-Users', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 4 => new URLAlias( + [ + 'id' => 4, + 'type' => 0, + 'destination' => 12, + 'path' => '/Users/Members', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 41 => new URLAlias( + [ + 'id' => 41, + 'type' => 0, + 'destination' => 12, + 'path' => '/Users/Guest-accounts', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 5 => new URLAlias( + [ + 'id' => 5, + 'type' => 0, + 'destination' => 13, + 'path' => '/Users/Administrator-users', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 11 => new URLAlias( + [ + 'id' => 11, + 'type' => 2, + 'destination' => null, + 'path' => '/users2/anonymous_users2', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 26 => new URLAlias( + [ + 'id' => 26, + 'type' => 0, + 'destination' => 12, + 'path' => '/users2/guest_accounts', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 0, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 29 => new URLAlias( + [ + 'id' => 29, + 'type' => 0, + 'destination' => 14, + 'path' => '/users2/editors', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 7 => new URLAlias( + [ + 'id' => 7, + 'type' => 2, + 'destination' => null, + 'path' => '/users2/administrator_users2', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 27 => new URLAlias( + [ + 'id' => 27, + 'type' => 0, + 'destination' => 13, + 'path' => '/users2/administrator_users', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 30 => new URLAlias( + [ + 'id' => 30, + 'type' => 0, + 'destination' => 44, + 'path' => '/users2/anonymous_users', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 8 => new URLAlias( + [ + 'id' => 8, + 'type' => 0, + 'destination' => 15, + 'path' => '/Users/Administrator-users/Administrator-User', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 28 => new URLAlias( + [ + 'id' => 28, + 'type' => 0, + 'destination' => 15, + 'path' => '/users2/administrator_users2/administrator_user', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 0, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 20 => new URLAlias( + [ + 'id' => 20, + 'type' => 0, + 'destination' => 53, + 'path' => '/Media/Multimedia', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 19 => new URLAlias( + [ + 'id' => 19, + 'type' => 0, + 'destination' => 52, + 'path' => '/Media/Files', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 18 => new URLAlias( + [ + 'id' => 18, + 'type' => 0, + 'destination' => 51, + 'path' => '/Media/Images', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 12 => new URLAlias( + [ + 'id' => 12, + 'type' => 0, + 'destination' => 45, + 'path' => '/Users/Anonymous-Users/Anonymous-User', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 1, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 31 => new URLAlias( + [ + 'id' => 31, + 'type' => 0, + 'destination' => 45, + 'path' => '/users2/anonymous_users2/anonymous_user', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 22 => new URLAlias( + [ + 'id' => 22, + 'type' => 0, + 'destination' => 54, + 'path' => '/Setup2/Common-INI-settings', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 0, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + 15 => new URLAlias( + [ + 'id' => 15, + 'type' => 2, + 'destination' => null, + 'path' => '/foo_bar_folder/images', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 34 => new URLAlias( + [ + 'id' => 34, + 'type' => 0, + 'destination' => 53, + 'path' => '/media2/multimedia', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 33 => new URLAlias( + [ + 'id' => 33, + 'type' => 0, + 'destination' => 52, + 'path' => '/media2/files', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 32 => new URLAlias( + [ + 'id' => 32, + 'type' => 0, + 'destination' => 51, + 'path' => '/media2/images', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 35 => new URLAlias( + [ + 'id' => 35, + 'type' => 0, + 'destination' => 54, + 'path' => '/setup3/common_ini_settings', + 'languageCodes' => [ + ], + 'alwaysAvailable' => 1, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 37 => new URLAlias( + [ + 'id' => 37, + 'type' => 0, + 'destination' => 56, + 'path' => '/Design/Ibexa', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 0, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => true, + ] + ), + 24 => new URLAlias( + [ + 'id' => 24, + 'type' => 0, + 'destination' => 56, + 'path' => '/Design/Plain-site', + 'languageCodes' => [ + 0 => 'eng-US', + ], + 'alwaysAvailable' => 0, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => true, + ] + ), + ], + 41, +]; diff --git a/tests/integration/Core/Repository/_fixtures/image/landscape.jpg b/tests/integration/Core/Repository/_fixtures/image/landscape.jpg new file mode 100644 index 0000000000..1be0750b3b Binary files /dev/null and b/tests/integration/Core/Repository/_fixtures/image/landscape.jpg differ diff --git a/eZ/Publish/SPI/Tests/FieldType/_fixtures/image.jpg b/tests/integration/Core/Repository/_fixtures/image/portrait.jpg similarity index 100% rename from eZ/Publish/SPI/Tests/FieldType/_fixtures/image.jpg rename to tests/integration/Core/Repository/_fixtures/image/portrait.jpg diff --git a/eZ/Publish/SPI/Tests/FieldType/_fixtures/image.png b/tests/integration/Core/Repository/_fixtures/image/square.png similarity index 100% rename from eZ/Publish/SPI/Tests/FieldType/_fixtures/image.png rename to tests/integration/Core/Repository/_fixtures/image/square.png diff --git a/tests/integration/Core/RepositorySearchTestCase.php b/tests/integration/Core/RepositorySearchTestCase.php new file mode 100644 index 0000000000..4f97374a74 --- /dev/null +++ b/tests/integration/Core/RepositorySearchTestCase.php @@ -0,0 +1,25 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Integration\Core; + +use Ibexa\Solr\Handler as SolrHandler; + +abstract class RepositorySearchTestCase extends RepositoryTestCase +{ + protected function refreshSearch(): void + { + $handler = self::getContainer()->get('ibexa.spi.search'); + if ( + class_exists(SolrHandler::class) + && $handler instanceof SolrHandler + ) { + $handler->commit(); + } + } +} diff --git a/tests/integration/Core/RepositoryTestCase.php b/tests/integration/Core/RepositoryTestCase.php index 4a2b9ff13e..97de4dc0d1 100644 --- a/tests/integration/Core/RepositoryTestCase.php +++ b/tests/integration/Core/RepositoryTestCase.php @@ -8,7 +8,9 @@ namespace Ibexa\Tests\Integration\Core; -use eZ\Publish\API\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroup; use Ibexa\Contracts\Core\Test\IbexaKernelTestCase; use InvalidArgumentException; @@ -17,6 +19,7 @@ abstract class RepositoryTestCase extends IbexaKernelTestCase public const CONTENT_TREE_ROOT_ID = 2; private const CONTENT_TYPE_FOLDER_IDENTIFIER = 'folder'; + private const MAIN_USER_GROUP_REMOTE_ID = 'f5c88a2209584891056f987fd965b0ba'; protected function setUp(): void { @@ -31,7 +34,7 @@ protected function setUp(): void /** * @param array<string, string> $names * - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception */ public function createFolder(array $names, int $parentLocationId = self::CONTENT_TREE_ROOT_ID): Content { @@ -41,10 +44,41 @@ public function createFolder(array $names, int $parentLocationId = self::CONTENT return $contentService->publishVersion($draft->getVersionInfo()); } + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + final protected function createUser(string $login, string $firstName, string $lastName, UserGroup $userGroup = null): User + { + $userService = self::getUserService(); + + if (null === $userGroup) { + $userGroup = $userService->loadUserGroupByRemoteId(self::MAIN_USER_GROUP_REMOTE_ID); + } + + $userCreateStruct = $userService->newUserCreateStruct( + $login, + "$login@mail.invalid", + 'secret', + 'eng-US' + ); + $userCreateStruct->enabled = true; + + // Set some fields required by the user ContentType + $userCreateStruct->setField('first_name', $firstName); + $userCreateStruct->setField('last_name', $lastName); + + // Create a new user instance. + return $userService->createUser($userCreateStruct, [$userGroup]); + } + /** * @param array<string, string> $names * - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception */ public function createFolderDraft(array $names, int $parentLocationId = self::CONTENT_TREE_ROOT_ID): Content { diff --git a/tests/integration/Core/Resources/settings/common.yml b/tests/integration/Core/Resources/settings/common.yml new file mode 100644 index 0000000000..2bedb7dd96 --- /dev/null +++ b/tests/integration/Core/Resources/settings/common.yml @@ -0,0 +1,91 @@ +parameters: + ibexa.site_access.config.default.io.file_storage.file_type_blacklist: + - php + - php3 + - phar + - phpt + - pht + - phtml + - pgif + +services: + logger: + class: Psr\Log\NullLogger + + Symfony\Component\EventDispatcher\EventDispatcher: + calls: + - [ 'addSubscriber', [ '@Ibexa\Core\Search\Common\EventSubscriber\ContentEventSubscriber' ] ] + - [ 'addSubscriber', [ '@Ibexa\Core\Search\Common\EventSubscriber\LocationEventSubscriber' ] ] + - [ 'addSubscriber', [ '@Ibexa\Core\Search\Common\EventSubscriber\ObjectStateEventSubscriber' ] ] + - [ 'addSubscriber', [ '@Ibexa\Core\Search\Common\EventSubscriber\SectionEventSubscriber' ] ] + - [ 'addSubscriber', [ '@Ibexa\Core\Search\Common\EventSubscriber\TrashEventSubscriber' ] ] + - [ 'addSubscriber', [ '@Ibexa\Core\Search\Common\EventSubscriber\UserEventSubscriber' ] ] + - [ 'addSubscriber', [ '@Ibexa\Core\Repository\EventSubscriber\NameSchemaSubscriber' ] ] + - [ 'addSubscriber', [ '@Ibexa\Core\Persistence\Legacy\Content\Mapper\ResolveVirtualFieldSubscriber' ] ] + + Symfony\Contracts\EventDispatcher\EventDispatcherInterface: '@Symfony\Component\EventDispatcher\EventDispatcher' + + # By default use in-memory cache for tests to avoid disk IO but still make sure we tests cache clearing works + ibexa.cache_pool.driver: + class: Symfony\Component\Cache\Adapter\ArrayAdapter + arguments: [120, false] + + # Override Slug Converter service to expose mutating Service configuration + Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter: + class: Ibexa\Tests\Integration\Core\Repository\Common\SlugConverter + arguments: + - '@Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased' + - [] + + # Configure serializer required Generic Field Type + ezpublish.field_type.ezgeneric.value_serializer.symfony.normalizer: + class: Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer + + ezpublish.field_type.ezgeneric.value_serializer.symfony.encoder: + class: Symfony\Component\Serializer\Encoder\JsonEncoder + + ezpublish.field_type.ezgeneric.value_serializer.symfony.serializer: + class: Symfony\Component\Serializer\Serializer + arguments: + - [ '@ezpublish.field_type.ezgeneric.value_serializer.symfony.normalizer' ] + - [ '@ezpublish.field_type.ezgeneric.value_serializer.symfony.encoder' ] + + serializer: + alias: ezpublish.field_type.ezgeneric.value_serializer.symfony.serializer + + Ibexa\Tests\Integration\Core\Persistence\Variation\InMemoryVariationHandler: ~ + Ibexa\Contracts\Core\Variation\VariationHandler: '@Ibexa\Tests\Integration\Core\Persistence\Variation\InMemoryVariationHandler' + + ibexa.config.resolver: + class: Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver + arguments: + - '@logger' + - [] + - 'ibexa.site_access.config' + calls: + - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']] + - [setContainer, ['@service_container']] + - [setDefaultScope, ['default']] + + Ibexa\Core\MVC\Symfony\SiteAccess: + class: Ibexa\Core\MVC\Symfony\SiteAccess + arguments: ['default', 'uninitialized'] + + Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService: + class: Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService + arguments: + - '@ibexa.siteaccess.provider' + - '@ibexa.config.resolver' + calls: + - [setSiteAccess, ['@Ibexa\Core\MVC\Symfony\SiteAccess']] + + Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface: + alias: 'Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService' + + Ibexa\Core\MVC\Symfony\SiteAccess\Provider\ChainSiteAccessProvider: + class: Ibexa\Core\MVC\Symfony\SiteAccess\Provider\ChainSiteAccessProvider + arguments: + $providers: !tagged ibexa.site_access.provider + + ibexa.siteaccess.provider: + alias: Ibexa\Core\MVC\Symfony\SiteAccess\Provider\ChainSiteAccessProvider diff --git a/tests/integration/Core/Resources/settings/fieldtype_constraints_storage.yaml b/tests/integration/Core/Resources/settings/fieldtype_constraints_storage.yaml new file mode 100644 index 0000000000..10793c8b26 --- /dev/null +++ b/tests/integration/Core/Resources/settings/fieldtype_constraints_storage.yaml @@ -0,0 +1,14 @@ +services: + Ibexa\Tests\Integration\Core\FieldType\FieldConstraintsStorage\Stub\ExampleFieldType: + tags: + - { name: ibexa.field_type, alias: example } + + Ibexa\Tests\Integration\Core\FieldType\FieldConstraintsStorage\Stub\ExampleFieldConstraintsStorage: + public: true + tags: + - { name: ibexa.field_type.storage.external.constraints.handler, alias: example } + + ibexa.test.field_type.example.converter: + class: Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\NullConverter + tags: + - { name: ibexa.field_type.storage.legacy.converter, alias: example } diff --git a/tests/integration/Core/Resources/settings/integration_legacy.yml b/tests/integration/Core/Resources/settings/integration_legacy.yml new file mode 100644 index 0000000000..d7caf0c52d --- /dev/null +++ b/tests/integration/Core/Resources/settings/integration_legacy.yml @@ -0,0 +1,51 @@ +parameters: + languages: + - eng-US + - eng-GB + ignored_storage_files: + - + var/ibexa_demo_site/storage/images/design/plain-site/172-2-eng-US/Ibexa-Demo-Design-without-demo-content1.png + # Image Asset mappings + ibexa.site_access.config.default.fieldtypes.ezimageasset.mappings: + content_type_identifier: image + content_field_identifier: image + name_field_identifier: name + parent_location_id: 51 + + ibexa.site_access.config.default.user_content_type_identifier: ['user'] + +services: + Ibexa\Core\FieldType\ImageAsset\AssetMapper: + arguments: + $contentService: '@ibexa.api.service.content' + $locationService: '@ibexa.api.service.location' + $contentTypeService: '@ibexa.api.service.content_type' + $configResolver: '@ibexa.config.resolver' + + # repeat part of DIC setup to avoid loading DoctrineSchemaBundle + _instanceof: + Ibexa\DoctrineSchema\Database\DbPlatform\DbPlatformInterface: + tags: [ ibexa.doctrine.db.platform ] + + Doctrine\Common\EventManager: ~ + + Ibexa\DoctrineSchema\Database\DbPlatform\SqliteDbPlatform: + autowire: true + + Ibexa\Tests\Core\Persistence\DatabaseConnectionFactory: + autowire: true + arguments: + $databasePlatforms: !tagged ibexa.doctrine.db.platform + + # build ezpublish.api.storage_engine.legacy.connection for test purposes + ibexa.api.storage_engine.legacy.connection: + class: Doctrine\DBAL\Connection + factory: ['@Ibexa\Tests\Core\Persistence\DatabaseConnectionFactory', 'createConnection'] + arguments: + $databaseURL: '%ibexa.persistence.legacy.dsn%' + + Ibexa\Contracts\Core\Repository\SettingService: + public: true + alias: Ibexa\Core\Event\SettingService + + Ibexa\Bundle\Core\Imagine\Cache\AliasGeneratorDecorator: '@Ibexa\Tests\Integration\Core\Persistence\Variation\InMemoryVariationHandler' diff --git a/tests/integration/Core/Resources/settings/integration_legacy_core.yml b/tests/integration/Core/Resources/settings/integration_legacy_core.yml new file mode 100644 index 0000000000..5ce2cbe292 --- /dev/null +++ b/tests/integration/Core/Resources/settings/integration_legacy_core.yml @@ -0,0 +1,4 @@ +parameters: + ignored_storage_files: + - + var/ibexa_demo_site/storage/images/design/plain-site/172-2-eng-US/Ibexa-Demo-Design-without-demo-content1.png diff --git a/tests/integration/Core/Resources/settings/override.yml b/tests/integration/Core/Resources/settings/override.yml new file mode 100644 index 0000000000..20e6580113 --- /dev/null +++ b/tests/integration/Core/Resources/settings/override.yml @@ -0,0 +1,14 @@ +# Config loaded as the last one just before creating the test container. +# Effectively overrides all other settings + +parameters: + # for tests of features using prioritized languages list + languages: + - eng-US + - eng-GB + - ger-DE + + ibexa.spi.persistence.cache.inmemory.ttl: 0 + ibexa.io.dir.storage: var/ibexa_demo_site/storage + ibexa.url_prefix: var/ibexa_demo_site/storage + ibexa.legacy.url_prefix: var/ibexa_demo_site/storage diff --git a/eZ/Publish/Core/FieldType/Tests/Integration/User/UserStorage/UserStorageGatewayTest.php b/tests/integration/Core/User/UserStorage/UserStorageGatewayTest.php similarity index 85% rename from eZ/Publish/Core/FieldType/Tests/Integration/User/UserStorage/UserStorageGatewayTest.php rename to tests/integration/Core/User/UserStorage/UserStorageGatewayTest.php index 27a9bade50..4725712a56 100644 --- a/eZ/Publish/Core/FieldType/Tests/Integration/User/UserStorage/UserStorageGatewayTest.php +++ b/tests/integration/Core/User/UserStorage/UserStorageGatewayTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests\Integration\User\UserStorage; +namespace Ibexa\Tests\Integration\Core\User\UserStorage; -use eZ\Publish\Core\FieldType\Tests\Integration\BaseCoreFieldTypeIntegrationTest; -use eZ\Publish\Core\FieldType\User\UserStorage\Gateway; -use eZ\Publish\Core\Repository\Values\User\User; -use eZ\Publish\SPI\Tests\Persistence\FixtureImporter; -use eZ\Publish\SPI\Tests\Persistence\YamlFixture; +use Ibexa\Contracts\Core\Test\Persistence\Fixture\FixtureImporter; +use Ibexa\Contracts\Core\Test\Persistence\Fixture\YamlFixture; +use Ibexa\Core\FieldType\User\UserStorage\Gateway; +use Ibexa\Core\Repository\Values\User\User; +use Ibexa\Tests\Integration\Core\BaseCoreFieldTypeIntegrationTest; /** * User Field Type external storage gateway tests. @@ -92,3 +92,5 @@ public function getDataForTestCountUsersWithUnsupportedHashType(): iterable ]; } } + +class_alias(UserStorageGatewayTest::class, 'eZ\Publish\Core\FieldType\Tests\Integration\User\UserStorage\UserStorageGatewayTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/Integration/User/UserStorage/_fixtures/unsupported_hash.yaml b/tests/integration/Core/User/UserStorage/_fixtures/unsupported_hash.yaml similarity index 100% rename from eZ/Publish/Core/FieldType/Tests/Integration/User/UserStorage/_fixtures/unsupported_hash.yaml rename to tests/integration/Core/User/UserStorage/_fixtures/unsupported_hash.yaml diff --git a/tests/integration/RepositoryInstaller/Installer/CoreInstallerTest.php b/tests/integration/RepositoryInstaller/Installer/CoreInstallerTest.php index f3c76d6898..28f8750700 100644 --- a/tests/integration/RepositoryInstaller/Installer/CoreInstallerTest.php +++ b/tests/integration/RepositoryInstaller/Installer/CoreInstallerTest.php @@ -4,16 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ +declare(strict_types=1); + namespace Ibexa\Tests\Integration\Installer\RepositoryInstaller; -use EzSystems\PlatformInstallerBundle\Installer\CoreInstaller; +use Ibexa\Bundle\RepositoryInstaller\Installer\CoreInstaller; use Ibexa\Tests\Integration\RepositoryInstaller\TestCase; use Symfony\Component\Console\Output\NullOutput; final class CoreInstallerTest extends TestCase { - /** @var \EzSystems\PlatformInstallerBundle\Installer\CoreInstaller */ - private $installer; + private CoreInstaller $installer; protected function setUp(): void { diff --git a/tests/integration/RepositoryInstaller/TestKernel.php b/tests/integration/RepositoryInstaller/TestKernel.php index 175b51dee8..6cee2d1315 100644 --- a/tests/integration/RepositoryInstaller/TestKernel.php +++ b/tests/integration/RepositoryInstaller/TestKernel.php @@ -8,9 +8,9 @@ namespace Ibexa\Tests\Integration\RepositoryInstaller; -use EzSystems\DoctrineSchemaBundle\DoctrineSchemaBundle; -use EzSystems\PlatformInstallerBundle\EzSystemsPlatformInstallerBundle; -use EzSystems\PlatformInstallerBundle\Installer\CoreInstaller; +use Ibexa\Bundle\DoctrineSchema\DoctrineSchemaBundle; +use Ibexa\Bundle\RepositoryInstaller\IbexaRepositoryInstallerBundle; +use Ibexa\Bundle\RepositoryInstaller\Installer\CoreInstaller; use Ibexa\Contracts\Core\Test\IbexaTestKernel; final class TestKernel extends IbexaTestKernel @@ -20,7 +20,7 @@ public function registerBundles(): iterable yield from parent::registerBundles(); yield new DoctrineSchemaBundle(); - yield new EzSystemsPlatformInstallerBundle(); + yield new IbexaRepositoryInstallerBundle(); } protected static function getExposedServicesByClass(): iterable diff --git a/tests/lib/Base/Container/Compiler/FieldTypeRegistryPassTest.php b/tests/lib/Base/Container/Compiler/FieldTypeRegistryPassTest.php new file mode 100644 index 0000000000..9eb8e855bc --- /dev/null +++ b/tests/lib/Base/Container/Compiler/FieldTypeRegistryPassTest.php @@ -0,0 +1,87 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Base\Container\Compiler; + +use Ibexa\Core\Base\Container\Compiler\FieldTypeRegistryPass; +use Ibexa\Core\FieldType\FieldTypeRegistry; +use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class FieldTypeRegistryPassTest extends AbstractCompilerPassTestCase +{ + protected function setUp(): void + { + parent::setUp(); + $this->setDefinition(FieldTypeRegistry::class, new Definition()); + } + + /** + * Register the compiler pass under test, just like you would do inside a bundle's load() + * method:. + * + * $container->addCompilerPass(new MyCompilerPass()); + */ + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new FieldTypeRegistryPass()); + } + + /** + * @dataProvider tagsProvider + */ + public function testRegisterFieldType(string $tag) + { + $fieldTypeIdentifier = 'field_type_identifier'; + $serviceId = 'service_id'; + $def = new Definition(); + $def->addTag($tag, ['alias' => $fieldTypeIdentifier]); + $this->setDefinition($serviceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + FieldTypeRegistry::class, + 'registerFieldType', + [$fieldTypeIdentifier, new Reference($serviceId)] + ); + } + + /** + * @dataProvider tagsProvider + * + * @param string $tag + */ + public function testRegisterFieldTypeNoAlias(string $tag) + { + $this->expectException(\LogicException::class); + + $fieldTypeIdentifier = 'field_type_identifier'; + $serviceId = 'service_id'; + $def = new Definition(); + $def->addTag($tag); + $this->setDefinition($serviceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + FieldTypeRegistry::class, + 'registerFieldType', + [$serviceId, $fieldTypeIdentifier] + ); + } + + public function tagsProvider(): array + { + return [ + [FieldTypeRegistryPass::FIELD_TYPE_SERVICE_TAG], + ]; + } +} + +class_alias(FieldTypeRegistryPassTest::class, 'eZ\Publish\Core\Base\Tests\Container\Compiler\FieldTypeRegistryPassTest'); diff --git a/eZ/Publish/Core/Base/Tests/Container/Compiler/GenericFieldTypeConverterPassTest.php b/tests/lib/Base/Container/Compiler/GenericFieldTypeConverterPassTest.php similarity index 90% rename from eZ/Publish/Core/Base/Tests/Container/Compiler/GenericFieldTypeConverterPassTest.php rename to tests/lib/Base/Container/Compiler/GenericFieldTypeConverterPassTest.php index 649ab9a8b2..4c49e18864 100644 --- a/eZ/Publish/Core/Base/Tests/Container/Compiler/GenericFieldTypeConverterPassTest.php +++ b/tests/lib/Base/Container/Compiler/GenericFieldTypeConverterPassTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Base\Tests\Container\Compiler; +namespace Ibexa\Tests\Core\Base\Container\Compiler; -use eZ\Publish\Core\Base\Container\Compiler\FieldTypeRegistryPass; -use eZ\Publish\Core\Base\Container\Compiler\GenericFieldTypeConverterPass; -use eZ\Publish\Core\Base\Container\Compiler\Storage\Legacy\FieldValueConverterRegistryPass; -use eZ\Publish\Core\Base\Tests\Container\Compiler\Stubs\GenericFieldType; -use eZ\Publish\SPI\FieldType\Generic\Type; +use Ibexa\Contracts\Core\FieldType\Generic\Type; +use Ibexa\Core\Base\Container\Compiler\FieldTypeRegistryPass; +use Ibexa\Core\Base\Container\Compiler\GenericFieldTypeConverterPass; +use Ibexa\Core\Base\Container\Compiler\Storage\Legacy\FieldValueConverterRegistryPass; +use Ibexa\Tests\Core\Base\Container\Compiler\Stubs\GenericFieldType; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\DefinitionHasMethodCallConstraint; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -140,3 +140,5 @@ private function assertContainerBuilderHasNoServiceDefinitionWithMethodCall( ); } } + +class_alias(GenericFieldTypeConverterPassTest::class, 'eZ\Publish\Core\Base\Tests\Container\Compiler\GenericFieldTypeConverterPassTest'); diff --git a/tests/lib/Base/Container/Compiler/Search/FieldTypeRegistryPassTest.php b/tests/lib/Base/Container/Compiler/Search/FieldTypeRegistryPassTest.php new file mode 100644 index 0000000000..f3aa1f4720 --- /dev/null +++ b/tests/lib/Base/Container/Compiler/Search/FieldTypeRegistryPassTest.php @@ -0,0 +1,72 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Base\Container\Compiler\Search; + +use Ibexa\Core\Base\Container\Compiler\Search\FieldRegistryPass; +use Ibexa\Core\Search\Common\FieldRegistry; +use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class FieldTypeRegistryPassTest extends AbstractCompilerPassTestCase +{ + protected function setUp(): void + { + parent::setUp(); + $this->setDefinition(FieldRegistry::class, new Definition()); + } + + /** + * Register the compiler pass under test, just like you would do inside a bundle's load() + * method:. + * + * $container->addCompilerPass(new MyCompilerPass()); + */ + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new FieldRegistryPass()); + } + + public function testRegisterFieldType() + { + $fieldTypeIdentifier = 'field_type_identifier'; + $serviceId = 'service_id'; + $def = new Definition(); + $def->addTag('ibexa.field_type.indexable', ['alias' => $fieldTypeIdentifier]); + $this->setDefinition($serviceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + FieldRegistry::class, + 'registerType', + [$fieldTypeIdentifier, new Reference($serviceId)] + ); + } + + public function testRegisterFieldTypeNoAlias() + { + $this->expectException(\LogicException::class); + + $fieldTypeIdentifier = 'field_type_identifier'; + $serviceId = 'service_id'; + $def = new Definition(); + $def->addTag('ibexa.field_type.indexable'); + $this->setDefinition($serviceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + FieldRegistry::class, + 'registerType', + [$fieldTypeIdentifier, new Reference($serviceId)] + ); + } +} + +class_alias(FieldTypeRegistryPassTest::class, 'eZ\Publish\Core\Base\Tests\Container\Compiler\Search\FieldTypeRegistryPassTest'); diff --git a/tests/lib/Base/Container/Compiler/Search/Legacy/CriteriaConverterPassTest.php b/tests/lib/Base/Container/Compiler/Search/Legacy/CriteriaConverterPassTest.php new file mode 100644 index 0000000000..e0baf23b1c --- /dev/null +++ b/tests/lib/Base/Container/Compiler/Search/Legacy/CriteriaConverterPassTest.php @@ -0,0 +1,135 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Base\Container\Compiler\Search\Legacy; + +use Ibexa\Core\Base\Container\Compiler\Search\Legacy\CriteriaConverterPass; +use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class CriteriaConverterPassTest extends AbstractCompilerPassTestCase +{ + /** + * Register the compiler pass under test, just like you would do inside a bundle's load() + * method:. + * + * $container->addCompilerPass(new MyCompilerPass()); + */ + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new CriteriaConverterPass()); + } + + public function testAddContentHandlers() + { + $this->setDefinition( + 'ibexa.search.legacy.gateway.criteria_converter.content', + new Definition() + ); + + $serviceId = 'service_id'; + $def = new Definition(); + $def->addTag('ibexa.search.legacy.gateway.criterion_handler.content'); + $this->setDefinition($serviceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'ibexa.search.legacy.gateway.criteria_converter.content', + 'addHandler', + [new Reference($serviceId)] + ); + } + + public function testAddLocationHandlers() + { + $this->setDefinition( + 'ibexa.search.legacy.gateway.criteria_converter.location', + new Definition() + ); + + $serviceId = 'service_id'; + $def = new Definition(); + $def->addTag('ibexa.search.legacy.gateway.criterion_handler.location'); + $this->setDefinition($serviceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'ibexa.search.legacy.gateway.criteria_converter.location', + 'addHandler', + [new Reference($serviceId)] + ); + } + + public function testAddTrashHandlers(): void + { + $this->setDefinition( + 'ibexa.core.trash.search.legacy.gateway.criteria_converter', + new Definition() + ); + + $serviceId = 'service_id'; + $def = new Definition(); + $def->addTag('ibexa.search.legacy.trash.gateway.criterion.handler'); + $this->setDefinition($serviceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'ibexa.core.trash.search.legacy.gateway.criteria_converter', + 'addHandler', + [new Reference($serviceId)] + ); + } + + public function testAddMultipleHandlers(): void + { + $this->setDefinition( + 'ibexa.search.legacy.gateway.criteria_converter.content', + new Definition() + ); + $this->setDefinition( + 'ibexa.search.legacy.gateway.criteria_converter.location', + new Definition() + ); + $this->setDefinition( + 'ibexa.core.trash.search.legacy.gateway.criteria_converter', + new Definition() + ); + + $commonServiceId = 'common_service_id'; + $def = new Definition(); + $def->addTag('ibexa.search.legacy.gateway.criterion_handler.content'); + $def->addTag('ibexa.search.legacy.gateway.criterion_handler.location'); + $def->addTag('ibexa.search.legacy.trash.gateway.criterion.handler'); + $this->setDefinition($commonServiceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'ibexa.search.legacy.gateway.criteria_converter.content', + 'addHandler', + [new Reference($commonServiceId)] + ); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'ibexa.search.legacy.gateway.criteria_converter.location', + 'addHandler', + [new Reference($commonServiceId)] + ); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'ibexa.core.trash.search.legacy.gateway.criteria_converter', + 'addHandler', + [new Reference($commonServiceId)] + ); + } +} + +class_alias(CriteriaConverterPassTest::class, 'eZ\Publish\Core\Base\Tests\Container\Compiler\Search\Legacy\CriteriaConverterPassTest'); diff --git a/tests/lib/Base/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPassTest.php b/tests/lib/Base/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPassTest.php new file mode 100644 index 0000000000..e7a62c26bd --- /dev/null +++ b/tests/lib/Base/Container/Compiler/Search/Legacy/CriterionFieldValueHandlerRegistryPassTest.php @@ -0,0 +1,78 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Base\Container\Compiler\Search\Legacy; + +use Ibexa\Core\Base\Container\Compiler\Search\Legacy\CriterionFieldValueHandlerRegistryPass; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler\FieldValue\HandlerRegistry; +use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class CriterionFieldValueHandlerRegistryPassTest extends AbstractCompilerPassTestCase +{ + protected function setUp(): void + { + parent::setUp(); + $this->setDefinition( + HandlerRegistry::class, + new Definition() + ); + } + + /** + * Register the compiler pass under test, just like you would do inside a bundle's load() + * method:. + * + * $container->addCompilerPass(new MyCompilerPass()); + */ + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new CriterionFieldValueHandlerRegistryPass()); + } + + public function testRegisterValueHandler() + { + $fieldTypeIdentifier = 'field_type_identifier'; + $serviceId = 'service_id'; + $def = new Definition(); + $def->addTag( + 'ibexa.search.legacy.gateway.criterion_handler.field_value', + ['alias' => $fieldTypeIdentifier] + ); + $this->setDefinition($serviceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + HandlerRegistry::class, + 'register', + [$fieldTypeIdentifier, new Reference($serviceId)] + ); + } + + public function testRegisterValueHandlerNoAlias() + { + $this->expectException(\LogicException::class); + + $fieldTypeIdentifier = 'field_type_identifier'; + $serviceId = 'service_id'; + $def = new Definition(); + $def->addTag('ibexa.search.legacy.gateway.criterion_handler.field_value'); + $this->setDefinition($serviceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + HandlerRegistry::class, + 'register', + [$fieldTypeIdentifier, new Reference($serviceId)] + ); + } +} + +class_alias(CriterionFieldValueHandlerRegistryPassTest::class, 'eZ\Publish\Core\Base\Tests\Container\Compiler\Search\Legacy\CriterionFieldValueHandlerRegistryPassTest'); diff --git a/tests/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPassTest.php b/tests/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPassTest.php new file mode 100644 index 0000000000..d4f5ca5e27 --- /dev/null +++ b/tests/lib/Base/Container/Compiler/Search/Legacy/SortClauseConverterPassTest.php @@ -0,0 +1,103 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Base\Container\Compiler\Search\Legacy; + +use Ibexa\Core\Base\Container\Compiler\Search\Legacy\SortClauseConverterPass; +use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class SortClauseConverterPassTest extends AbstractCompilerPassTestCase +{ + /** + * Register the compiler pass under test, just like you would do inside a bundle's load() + * method:. + * + * $container->addCompilerPass(new MyCompilerPass()); + */ + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new SortClauseConverterPass()); + } + + public function testAddContentHandlers() + { + $this->setDefinition( + 'ibexa.search.legacy.gateway.sort_clause_converter.content', + new Definition() + ); + + $serviceId = 'service_id'; + $def = new Definition(); + $def->addTag('ibexa.search.legacy.gateway.sort_clause_handler.content'); + $this->setDefinition($serviceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'ibexa.search.legacy.gateway.sort_clause_converter.content', + 'addHandler', + [new Reference($serviceId)] + ); + } + + public function testAddLocationHandlers() + { + $this->setDefinition( + 'ibexa.search.legacy.gateway.sort_clause_converter.location', + new Definition() + ); + + $serviceId = 'service_id'; + $def = new Definition(); + $def->addTag('ibexa.search.legacy.gateway.sort_clause_handler.location'); + $this->setDefinition($serviceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'ibexa.search.legacy.gateway.sort_clause_converter.location', + 'addHandler', + [new Reference($serviceId)] + ); + } + + public function testAddLocationAndContentHandlers() + { + $this->setDefinition( + 'ibexa.search.legacy.gateway.sort_clause_converter.content', + new Definition() + ); + $this->setDefinition( + 'ibexa.search.legacy.gateway.sort_clause_converter.location', + new Definition() + ); + + $commonServiceId = 'common_service_id'; + $def = new Definition(); + $def->addTag('ibexa.search.legacy.gateway.sort_clause_handler.content'); + $def->addTag('ibexa.search.legacy.gateway.sort_clause_handler.location'); + $this->setDefinition($commonServiceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'ibexa.search.legacy.gateway.sort_clause_converter.content', + 'addHandler', + [new Reference($commonServiceId)] + ); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + 'ibexa.search.legacy.gateway.sort_clause_converter.location', + 'addHandler', + [new Reference($commonServiceId)] + ); + } +} + +class_alias(SortClauseConverterPassTest::class, 'eZ\Publish\Core\Base\Tests\Container\Compiler\Search\Legacy\SortClauseConverterPassTest'); diff --git a/eZ/Publish/Core/Base/Tests/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php b/tests/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php similarity index 88% rename from eZ/Publish/Core/Base/Tests/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php rename to tests/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php index 6a3531f218..8e22e13311 100644 --- a/eZ/Publish/Core/Base/Tests/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php +++ b/tests/lib/Base/Container/Compiler/Storage/ExternalStorageRegistryPassTest.php @@ -4,10 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Base\Tests\Container\Compiler\Storage; +namespace Ibexa\Tests\Core\Base\Container\Compiler\Storage; -use eZ\Publish\Core\Base\Container\Compiler\Storage\ExternalStorageRegistryPass; -use eZ\Publish\Core\Base\Tests\Container\Compiler\Stubs\GatewayBasedStorageHandler; +use Ibexa\Core\Base\Container\Compiler\Storage\ExternalStorageRegistryPass; +use Ibexa\Core\Persistence\Legacy\Content\StorageRegistry; +use Ibexa\Tests\Core\Base\Container\Compiler\Stubs\GatewayBasedStorageHandler; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -18,7 +19,7 @@ class ExternalStorageRegistryPassTest extends AbstractCompilerPassTestCase protected function setUp(): void { parent::setUp(); - $this->setDefinition('ezpublish.persistence.external_storage_registry', new Definition()); + $this->setDefinition(StorageRegistry::class, new Definition()); } protected function registerCompilerPass(ContainerBuilder $container): void @@ -40,7 +41,7 @@ public function testRegisterExternalStorageHandler(string $tag) $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.persistence.external_storage_registry', + StorageRegistry::class, 'register', [$fieldTypeIdentifier, new Reference($serviceId)] ); @@ -62,7 +63,7 @@ public function testRegisterExternalStorageHandlerNoAlias(string $tag) $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.persistence.external_storage_registry', + StorageRegistry::class, 'register', [$fieldTypeIdentifier, new Reference($serviceId)] ); @@ -94,7 +95,7 @@ public function testRegisterExternalStorageHandlerWithGateway(string $tag) $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.persistence.external_storage_registry', + StorageRegistry::class, 'register', [$fieldTypeIdentifier, new Reference($storageHandlerServiceId)] ); @@ -125,7 +126,7 @@ public function testRegisterExternalStorageHandlerWithoutRegisteredGateway(strin $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.persistence.external_storage_registry', + StorageRegistry::class, 'register', [$fieldTypeIdentifier, new Reference($storageHandlerServiceId)] ); @@ -156,7 +157,7 @@ public function testRegisterExternalStorageHandlerWithGatewayNoAlias(string $tag $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.persistence.external_storage_registry', + StorageRegistry::class, 'register', [$fieldTypeIdentifier, new Reference($storageHandlerServiceId)] ); @@ -187,7 +188,7 @@ public function testRegisterExternalStorageHandlerWithGatewayNoIdentifier(string $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.persistence.external_storage_registry', + StorageRegistry::class, 'register', [$fieldTypeIdentifier, new Reference($storageHandlerServiceId)] ); @@ -196,7 +197,6 @@ public function testRegisterExternalStorageHandlerWithGatewayNoIdentifier(string public function externalStorageHandlerTagsProvider(): array { return [ - [ExternalStorageRegistryPass::DEPRECATED_EXTERNAL_STORAGE_HANDLER_SERVICE_TAG], [ExternalStorageRegistryPass::EXTERNAL_STORAGE_HANDLER_SERVICE_TAG], ]; } @@ -204,8 +204,9 @@ public function externalStorageHandlerTagsProvider(): array public function externalStorageHandlerGatewayTagsProvider(): array { return [ - [ExternalStorageRegistryPass::DEPRECATED_EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG], [ExternalStorageRegistryPass::EXTERNAL_STORAGE_HANDLER_GATEWAY_SERVICE_TAG], ]; } } + +class_alias(ExternalStorageRegistryPassTest::class, 'eZ\Publish\Core\Base\Tests\Container\Compiler\Storage\ExternalStorageRegistryPassTest'); diff --git a/eZ/Publish/Core/Base/Tests/Container/Compiler/Storage/Legacy/FieldValueConverterRegistryPassTest.php b/tests/lib/Base/Container/Compiler/Storage/Legacy/FieldValueConverterRegistryPassTest.php similarity index 76% rename from eZ/Publish/Core/Base/Tests/Container/Compiler/Storage/Legacy/FieldValueConverterRegistryPassTest.php rename to tests/lib/Base/Container/Compiler/Storage/Legacy/FieldValueConverterRegistryPassTest.php index 50aa1fa4db..5fb61ebc96 100644 --- a/eZ/Publish/Core/Base/Tests/Container/Compiler/Storage/Legacy/FieldValueConverterRegistryPassTest.php +++ b/tests/lib/Base/Container/Compiler/Storage/Legacy/FieldValueConverterRegistryPassTest.php @@ -4,9 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Base\Tests\Container\Compiler\Storage\Legacy; +namespace Ibexa\Tests\Core\Base\Container\Compiler\Storage\Legacy; -use eZ\Publish\Core\Base\Container\Compiler\Storage\Legacy\FieldValueConverterRegistryPass; +use Ibexa\Core\Base\Container\Compiler\Storage\Legacy\FieldValueConverterRegistryPass; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -17,7 +18,7 @@ class FieldValueConverterRegistryPassTest extends AbstractCompilerPassTestCase protected function setUp(): void { parent::setUp(); - $this->setDefinition('ezpublish.persistence.legacy.field_value_converter.registry', new Definition()); + $this->setDefinition(ConverterRegistry::class, new Definition()); } /** @@ -48,9 +49,11 @@ public function testRegisterConverterNoLazy() $this->compile(); $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( - 'ezpublish.persistence.legacy.field_value_converter.registry', + ConverterRegistry::class, 'register', [$fieldTypeIdentifier, new Reference($serviceId)] ); } } + +class_alias(FieldValueConverterRegistryPassTest::class, 'eZ\Publish\Core\Base\Tests\Container\Compiler\Storage\Legacy\FieldValueConverterRegistryPassTest'); diff --git a/tests/lib/Base/Container/Compiler/Storage/Legacy/RoleLimitationConverterPassTest.php b/tests/lib/Base/Container/Compiler/Storage/Legacy/RoleLimitationConverterPassTest.php new file mode 100644 index 0000000000..95f9e1db5c --- /dev/null +++ b/tests/lib/Base/Container/Compiler/Storage/Legacy/RoleLimitationConverterPassTest.php @@ -0,0 +1,55 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Base\Container\Compiler\Storage\Legacy; + +use Ibexa\Core\Base\Container\Compiler\Storage\Legacy\RoleLimitationConverterPass; +use Ibexa\Core\Persistence\Legacy\User\Role\LimitationConverter; +use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractCompilerPassTestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +class RoleLimitationConverterPassTest extends AbstractCompilerPassTestCase +{ + protected function setUp(): void + { + parent::setUp(); + $this->setDefinition( + LimitationConverter::class, + new Definition() + ); + } + + /** + * Register the compiler pass under test, just like you would do inside a bundle's load() + * method:. + * + * $container->addCompilerPass(new MyCompilerPass()); + */ + protected function registerCompilerPass(ContainerBuilder $container): void + { + $container->addCompilerPass(new RoleLimitationConverterPass()); + } + + public function testRegisterRoleLimitationConverter() + { + $serviceId = 'service_id'; + $def = new Definition(); + $def->addTag('ibexa.storage.legacy.role.limitation.handler'); + $this->setDefinition($serviceId, $def); + + $this->compile(); + + $this->assertContainerBuilderHasServiceDefinitionWithMethodCall( + LimitationConverter::class, + 'addHandler', + [new Reference($serviceId)] + ); + } +} + +class_alias(RoleLimitationConverterPassTest::class, 'eZ\Publish\Core\Base\Tests\Container\Compiler\Storage\Legacy\RoleLimitationConverterPassTest'); diff --git a/tests/lib/Base/Container/Compiler/Stubs/GatewayBasedStorageHandler.php b/tests/lib/Base/Container/Compiler/Stubs/GatewayBasedStorageHandler.php new file mode 100644 index 0000000000..f25b98a7a5 --- /dev/null +++ b/tests/lib/Base/Container/Compiler/Stubs/GatewayBasedStorageHandler.php @@ -0,0 +1,39 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Base\Container\Compiler\Stubs; + +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Core\FieldType\GatewayBasedStorage; + +/** + * Stub implementation of GatewayBasedStorage. + */ +class GatewayBasedStorageHandler extends GatewayBasedStorage +{ + public function storeFieldData(VersionInfo $versionInfo, Field $field, array $context) + { + } + + public function getFieldData(VersionInfo $versionInfo, Field $field, array $context) + { + } + + public function deleteFieldData(VersionInfo $versionInfo, array $fieldIds, array $context) + { + } + + public function hasFieldData() + { + } + + public function getIndexData(VersionInfo $versionInfo, Field $field, array $context) + { + } +} + +class_alias(GatewayBasedStorageHandler::class, 'eZ\Publish\Core\Base\Tests\Container\Compiler\Stubs\GatewayBasedStorageHandler'); diff --git a/tests/lib/Base/Container/Compiler/Stubs/GenericFieldType.php b/tests/lib/Base/Container/Compiler/Stubs/GenericFieldType.php new file mode 100644 index 0000000000..e5cdf48ecd --- /dev/null +++ b/tests/lib/Base/Container/Compiler/Stubs/GenericFieldType.php @@ -0,0 +1,21 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Base\Container\Compiler\Stubs; + +use Ibexa\Contracts\Core\FieldType\Generic\Type; + +final class GenericFieldType extends Type +{ + public function getFieldTypeIdentifier(): string + { + return 'field_type_identifier'; + } +} + +class_alias(GenericFieldType::class, 'eZ\Publish\Core\Base\Tests\Container\Compiler\Stubs\GenericFieldType'); diff --git a/eZ/Publish/Core/Base/Tests/Container/Compiler/TaggedServiceIdsIterator/BackwardCompatibleIteratorTest.php b/tests/lib/Base/Container/Compiler/TaggedServiceIdsIterator/BackwardCompatibleIteratorTest.php similarity index 86% rename from eZ/Publish/Core/Base/Tests/Container/Compiler/TaggedServiceIdsIterator/BackwardCompatibleIteratorTest.php rename to tests/lib/Base/Container/Compiler/TaggedServiceIdsIterator/BackwardCompatibleIteratorTest.php index b49aac0ae4..936d947ce7 100644 --- a/eZ/Publish/Core/Base/Tests/Container/Compiler/TaggedServiceIdsIterator/BackwardCompatibleIteratorTest.php +++ b/tests/lib/Base/Container/Compiler/TaggedServiceIdsIterator/BackwardCompatibleIteratorTest.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Base\Tests\Container\Compiler\TaggedServiceIdsIterator; +namespace Ibexa\Tests\Core\Base\Container\Compiler\TaggedServiceIdsIterator; -use eZ\Publish\Core\Base\Container\Compiler\TaggedServiceIdsIterator\BackwardCompatibleIterator; +use Ibexa\Core\Base\Container\Compiler\TaggedServiceIdsIterator\BackwardCompatibleIterator; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\TaggedContainerInterface; @@ -17,7 +17,7 @@ final class BackwardCompatibleIteratorTest extends TestCase private const EXAMPLE_SERVICE_TAG = 'current_tag'; private const EXAMPLE_DEPRECATED_SERVICE_TAG = 'deprecated_tag'; - /** @var \eZ\Publish\Core\Base\Tests\Container\Compiler\TaggedServiceIdsIterator\DeprecationErrorCollector */ + /** @var \Ibexa\Tests\Core\Base\Container\Compiler\TaggedServiceIdsIterator\DeprecationErrorCollector */ private $deprecationErrorCollector; /** @var \Symfony\Component\DependencyInjection\TaggedContainerInterface */ @@ -80,7 +80,7 @@ public function testGetIterator(): void ], iterator_to_array($iterator)); $this->assertDeprecationError(sprintf( - 'Service tag `%s` is deprecated and will be removed in eZ Platform 4.0. Tag %s with `%s` instead.', + 'Service tag `%s` is deprecated and will be removed in Ibexa 4.0. Tag %s with `%s` instead.', self::EXAMPLE_DEPRECATED_SERVICE_TAG, 'app.service.foo', self::EXAMPLE_SERVICE_TAG @@ -101,3 +101,5 @@ private function assertDeprecationError(string $expectedMessage): void )); } } + +class_alias(BackwardCompatibleIteratorTest::class, 'eZ\Publish\Core\Base\Tests\Container\Compiler\TaggedServiceIdsIterator\BackwardCompatibleIteratorTest'); diff --git a/eZ/Publish/Core/Base/Tests/Container/Compiler/TaggedServiceIdsIterator/DeprecationErrorCollector.php b/tests/lib/Base/Container/Compiler/TaggedServiceIdsIterator/DeprecationErrorCollector.php similarity index 82% rename from eZ/Publish/Core/Base/Tests/Container/Compiler/TaggedServiceIdsIterator/DeprecationErrorCollector.php rename to tests/lib/Base/Container/Compiler/TaggedServiceIdsIterator/DeprecationErrorCollector.php index 7c7baeaf2e..ce53558d98 100644 --- a/eZ/Publish/Core/Base/Tests/Container/Compiler/TaggedServiceIdsIterator/DeprecationErrorCollector.php +++ b/tests/lib/Base/Container/Compiler/TaggedServiceIdsIterator/DeprecationErrorCollector.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Base\Tests\Container\Compiler\TaggedServiceIdsIterator; +namespace Ibexa\Tests\Core\Base\Container\Compiler\TaggedServiceIdsIterator; /** * Captures user deprecation warnings emitted using trigger_error function. @@ -48,3 +48,5 @@ public function __invoke(int $code, string $message, string $file, int $line): b return true; } } + +class_alias(DeprecationErrorCollector::class, 'eZ\Publish\Core\Base\Tests\Container\Compiler\TaggedServiceIdsIterator\DeprecationErrorCollector'); diff --git a/tests/lib/Collection/AbstractCollectionTest.php b/tests/lib/Collection/AbstractCollectionTest.php new file mode 100644 index 0000000000..c2823274b3 --- /dev/null +++ b/tests/lib/Collection/AbstractCollectionTest.php @@ -0,0 +1,128 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Collection; + +use Closure; +use Ibexa\Contracts\Core\Collection\CollectionInterface; +use Ibexa\Contracts\Core\Collection\StreamableInterface; +use IteratorAggregate; +use PHPUnit\Framework\TestCase; + +/** + * @template TCollection of \Ibexa\Contracts\Core\Repository\Collection\CollectionInterfaces + */ +abstract class AbstractCollectionTest extends TestCase +{ + public function testIsEmptyReturnsTrue(): void + { + self::assertTrue($this->createEmptyCollection()->isEmpty()); + } + + public function testIsEmptyReturnsFalse(): void + { + self::assertFalse($this->createCollectionWithExampleData()->isEmpty()); + } + + public function testToArray(): void + { + self::assertEquals($this->getExampleData(), $this->createCollectionWithExampleData()->toArray()); + } + + public function testCount(): void + { + self::assertCount( + count($this->getExampleData()), + $this->createCollectionWithExampleData() + ); + } + + public function testIsIterable(): void + { + $collection = $this->createCollectionWithExampleData(); + + self::assertInstanceOf(IteratorAggregate::class, $collection); + self::assertEquals($this->getExampleData(), iterator_to_array($collection->getIterator())); + } + + public function testFilterEdgeCases(): void + { + $input = $this->createCollection($this->getExampleData()); + + self::assertEquals( + $this->createEmptyCollection(), + $input->filter($this->getContradiction()) + ); + + self::assertEquals( + $this->createCollectionWithExampleData(), + $input->filter($this->getTautology()) + ); + } + + public function testExistsEdgeCases(): void + { + $collection = $this->createCollectionWithExampleData(); + if (!($collection instanceof \Ibexa\Contracts\Core\Collection\StreamableInterface)) { + self::markTestSkipped(sprintf('%s collection is not streamable', get_class($collection))); + } + + self::assertTrue($collection->exists($this->getTautology())); + self::assertFalse($collection->exists($this->getContradiction())); + } + + public function testForAllEdgeCases(): void + { + $collection = $this->createCollectionWithExampleData(); + if (!($collection instanceof StreamableInterface)) { + self::markTestSkipped(sprintf('%s collection is not streamable', get_class($collection))); + } + + self::assertTrue($collection->forAll($this->getTautology())); + self::assertFalse($collection->forAll($this->getContradiction())); + } + + abstract protected function getExampleData(): array; + + /** + * @return TCollection + */ + abstract protected function createCollection(array $data): CollectionInterface; + + /** + * @return TCollection + */ + protected function createEmptyCollection(): CollectionInterface + { + return $this->createCollection([]); + } + + /** + * @return TCollection + */ + protected function createCollectionWithExampleData(): CollectionInterface + { + return $this->createCollection($this->getExampleData()); + } + + /** + * Returns a predicate which is always true. + */ + protected function getTautology(): Closure + { + return static fn (): bool => true; + } + + /** + * Returns a predicate which is always false. + */ + protected function getContradiction(): Closure + { + return static fn (): bool => false; + } +} diff --git a/tests/lib/Collection/ArrayListTest.php b/tests/lib/Collection/ArrayListTest.php new file mode 100644 index 0000000000..297c3cd20c --- /dev/null +++ b/tests/lib/Collection/ArrayListTest.php @@ -0,0 +1,91 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Collection; + +use Ibexa\Contracts\Core\Collection\ArrayList; +use Ibexa\Contracts\Core\Exception\OutOfBoundsException; + +/** + * @template-extends \Ibexa\Tests\Core\Collection\AbstractCollectionTest< + * \Ibexa\Contracts\Core\Collection\ArrayList + * > + */ +class ArrayListTest extends AbstractCollectionTest +{ + public function testFirst(): void + { + self::assertEquals('A', ($this->createCollection(['A', 'B', 'C']))->first()); + } + + public function testLast(): void + { + self::assertEquals('C', ($this->createCollection(['A', 'B', 'C']))->last()); + } + + public function testFirstThrowsOutOfBoundsException(): void + { + $this->expectException(OutOfBoundsException::class); + $this->expectExceptionMessage('Collection is empty'); + + /** @var \Ibexa\Contracts\Core\Collection\ArrayList $list */ + $list = $this->createEmptyCollection(); + $list->first(); + } + + public function testLastThrowsOutOfBoundsException(): void + { + $this->expectException(OutOfBoundsException::class); + $this->expectExceptionMessage('Collection is empty'); + + /** @var \Ibexa\Contracts\Core\Collection\ArrayList $list */ + $list = $this->createEmptyCollection(); + $list->last(); + } + + public function testContains(): void + { + $list = $this->createCollection(['a', 'b', 'c']); + + self::assertTrue($list->contains('a')); + self::assertFalse($list->contains('z')); + } + + public function testMap(): void + { + $list = $this->createCollection(['a', 'b', 'c']); + + self::assertEquals( + $this->createCollection(['A', 'B', 'C']), + $list->map(static fn (string $value) => strtoupper($value)) + ); + } + + public function testFilter(): void + { + $list = $this->createCollection(['A', '7', 'B', 'C', '9', '10']); + + self::assertEquals( + $this->createCollection(['7', '9', '10']), + $list->filter(static fn (string $item) => ctype_digit($item)) + ); + } + + /** + * @return string[] + */ + protected function getExampleData(): array + { + return ['A', 'B', 'C']; + } + + protected function createCollection(array $data): ArrayList + { + return new ArrayList($data); + } +} diff --git a/tests/lib/Collection/ArrayMapTest.php b/tests/lib/Collection/ArrayMapTest.php new file mode 100644 index 0000000000..33d86ac2b8 --- /dev/null +++ b/tests/lib/Collection/ArrayMapTest.php @@ -0,0 +1,109 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Collection; + +use Ibexa\Contracts\Core\Collection\ArrayMap; +use Ibexa\Contracts\Core\Collection\MapInterface; +use Ibexa\Contracts\Core\Exception\OutOfBoundsException; + +/** + * @template-extends \Ibexa\Tests\Core\Collection\AbstractCollectionTest< + * \Ibexa\Contracts\Core\Collection\ArrayMap + * > + */ +class ArrayMapTest extends AbstractCollectionTest +{ + protected const EXAMPLE_DATA = [ + 'A' => 'foo', + 'B' => 'bar', + 'C' => 'baz', + ]; + + public function testGet(): void + { + $map = $this->createCollection(self::EXAMPLE_DATA); + + self::assertEquals('foo', $map->get('A')); + self::assertEquals('bar', $map->get('B')); + self::assertEquals('baz', $map->get('C')); + } + + public function testGetThrowsOutOfBoundsExceptionForNonExistingKey(): void + { + $this->expectException(OutOfBoundsException::class); + $this->expectExceptionMessage("Collection does not contain element with key 'non-exiting'"); + + /** @var \Ibexa\Contracts\Core\Collection\ArrayMap $map */ + $map = $this->createEmptyCollection(); + $map->get('non-exiting'); + } + + public function testHas(): void + { + $map = $this->createCollection([ + 'existing' => 'value', + ]); + + self::assertTrue($map->has('existing')); + self::assertFalse($map->has('non-existing')); + } + + public function testFilter(): void + { + $input = $this->createCollection(self::EXAMPLE_DATA); + + self::assertEquals( + $this->createCollection(['A' => 'foo']), + $input->filter(static fn ($value, $key): bool => $value === 'foo') + ); + } + + public function testMap(): void + { + $input = $this->createCollection(self::EXAMPLE_DATA); + + self::assertEquals( + $this->createCollection([ + 'A' => 'FOO', + 'B' => 'BAR', + 'C' => 'BAZ', + ]), + $input->map(static fn ($value): string => strtoupper($value)) + ); + } + + public function testExists(): void + { + $map = $this->createCollection(self::EXAMPLE_DATA); + + self::assertTrue($map->exists(static fn ($value, $key) => $value === 'foo')); + self::assertFalse($map->exists(static fn ($value, $key) => $value === 'non-existing')); + } + + public function testForAll(): void + { + $map = $this->createCollection(self::EXAMPLE_DATA); + + self::assertTrue($map->forAll(static fn ($value, $key) => strlen($value) > 2)); + self::assertFalse($map->forAll(static fn ($value, $key) => $value === 'foo')); + } + + protected function getExampleData(): array + { + return self::EXAMPLE_DATA; + } + + /** + * @return \Ibexa\Contracts\Core\Collection\MapInterface|\Ibexa\Contracts\Core\Collection\StreamableInterface + */ + protected function createCollection(array $data): MapInterface + { + return new ArrayMap($data); + } +} diff --git a/tests/lib/Collection/MutableArrayListTest.php b/tests/lib/Collection/MutableArrayListTest.php new file mode 100644 index 0000000000..444cfe73c4 --- /dev/null +++ b/tests/lib/Collection/MutableArrayListTest.php @@ -0,0 +1,59 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Collection; + +use Ibexa\Contracts\Core\Collection\MutableArrayList; + +final class MutableArrayListTest extends ArrayListTest +{ + public function testAppend(): void + { + /** @var \Ibexa\Contracts\Core\Collection\MutableArrayList $list */ + $list = $this->createEmptyCollection(); + $list->append('A'); + $list->append('B'); + $list->append('C'); + + self::assertEquals(['A', 'B', 'C'], $list->toArray()); + } + + public function testPrepend(): void + { + /** @var \Ibexa\Contracts\Core\Collection\MutableArrayList $list */ + $list = $this->createEmptyCollection(); + $list->prepend('A'); + $list->prepend('B'); + $list->prepend('C'); + + self::assertEquals(['C', 'B', 'A'], $list->toArray()); + } + + public function testRemove(): void + { + /** @var \Ibexa\Contracts\Core\Collection\MutableArrayList $list */ + $list = $this->createCollectionWithExampleData(); + $list->remove('B'); + + self::assertEquals(['A', 'C'], $list->toArray()); + } + + public function testClear(): void + { + /** @var \Ibexa\Contracts\Core\Collection\MutableArrayList $list */ + $list = $this->createCollectionWithExampleData(); + self::assertFalse($list->isEmpty()); + $list->clear(); + self::assertTrue($list->isEmpty()); + } + + protected function createCollection(array $data): MutableArrayList + { + return new MutableArrayList($data); + } +} diff --git a/tests/lib/Collection/MutableArrayMapTest.php b/tests/lib/Collection/MutableArrayMapTest.php new file mode 100644 index 0000000000..2fdbd5c9fa --- /dev/null +++ b/tests/lib/Collection/MutableArrayMapTest.php @@ -0,0 +1,59 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Collection; + +use Ibexa\Contracts\Core\Collection\MutableArrayMap; + +final class MutableArrayMapTest extends ArrayMapTest +{ + public function testSetWithExistingKey(): void + { + $map = $this->createCollection(['key' => 'value']); + self::assertEquals('value', $map->get('key')); + $map->set('key', 'updated_value'); + self::assertEquals('updated_value', $map->get('key')); + } + + public function testSetWithNewKey(): void + { + $map = $this->createCollection([]); + self::assertFalse($map->has('key')); + $map->set('key', 'new_value'); + self::assertEquals('new_value', $map->get('key')); + } + + public function testUnsetExistingKey(): void + { + $map = $this->createCollection(['key' => 'value']); + self::assertTrue($map->has('key')); + $map->unset('key'); + self::assertFalse($map->has('key')); + } + + public function testUnsetNonExistingKey(): void + { + $map = $this->createCollection([]); + self::assertFalse($map->has('non-existing')); + $map->unset('non-existing'); + self::assertFalse($map->has('non-existing')); + } + + public function testClear(): void + { + $map = $this->createCollection(self::EXAMPLE_DATA); + self::assertFalse($map->isEmpty()); + $map->clear(); + self::assertTrue($map->isEmpty()); + } + + protected function createCollection(array $data): MutableArrayMap + { + return new MutableArrayMap($data); + } +} diff --git a/tests/lib/Container/Encore/ConfigurationDumperTest.php b/tests/lib/Container/Encore/ConfigurationDumperTest.php new file mode 100644 index 0000000000..6e337c9d6a --- /dev/null +++ b/tests/lib/Container/Encore/ConfigurationDumperTest.php @@ -0,0 +1,81 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Container\Encore; + +use Ibexa\Contracts\Core\Container\Encore\ConfigurationDumper; +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Filesystem\Filesystem; + +/** + * @covers \Ibexa\Contracts\Core\Container\Encore\ConfigurationDumper + */ +final class ConfigurationDumperTest extends TestCase +{ + private const PROJECT_DIR = '/var/io-tests/'; + private const FOO_BAR_BUNDLE_DIR = 'foo-bar'; + + private Filesystem $filesystem; + + private string $projectDir; + + private string $fooBarBundlePath; + + protected function setUp(): void + { + $this->projectDir = dirname(__DIR__, 4) . self::PROJECT_DIR; + $this->fooBarBundlePath = $this->projectDir . self::FOO_BAR_BUNDLE_DIR; + $this->filesystem = new Filesystem(); + $this->filesystem->mkdir($this->fooBarBundlePath); + $this->filesystem->dumpFile( + $this->fooBarBundlePath . '/Resources/encore/foo-bar.js', + 'console.log("Hello, Foo Bar!");' + ); + $this->filesystem->dumpFile( + $this->projectDir . 'encore/foo-bar.js', + 'console.log("Hello, world!");' + ); + } + + /** + * @throws \JsonException + */ + public function testDumpCustomConfiguration(): void + { + $containerMock = $this->createMock(ContainerInterface::class); + $containerMock->method('getParameter')->willReturnMap( + [ + [ + 'kernel.bundles_metadata', + ['FooBar' => ['path' => $this->fooBarBundlePath]], + ], + ['kernel.project_dir', $this->projectDir], + ], + ); + $configurationDumper = new ConfigurationDumper($containerMock); + $configurationDumper->dumpCustomConfiguration(['foo-bar.js' => ['foo-bar.js' => []]]); + + $compiledFilePath = $this->projectDir . '/var/encore/foo-bar.js'; + self::assertFileExists($compiledFilePath); + $compiledFileContents = file_get_contents($compiledFilePath); + self::assertRegExp( + '@^module\.exports = \[.*io-tests\\\/foo-bar\\\/Resources\\\/encore\\\/foo-bar\.js@', + $compiledFileContents + ); + self::assertRegExp( + '@^module\.exports = \[.*io-tests\\\/encore\\\/foo-bar\.js@', + $compiledFileContents + ); + } + + protected function tearDown(): void + { + $this->filesystem->remove($this->projectDir); + } +} diff --git a/tests/lib/Event/AbstractServiceTest.php b/tests/lib/Event/AbstractServiceTest.php new file mode 100644 index 0000000000..8dd461c73c --- /dev/null +++ b/tests/lib/Event/AbstractServiceTest.php @@ -0,0 +1,42 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Event\AfterEvent; +use Ibexa\Contracts\Core\Repository\Event\BeforeEvent; +use PHPUnit\Framework\TestCase; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\Stopwatch\Stopwatch; + +abstract class AbstractServiceTest extends TestCase +{ + public function getEventDispatcher(string $beforeEventName, string $eventName): TraceableEventDispatcher + { + $eventDispatcher = new EventDispatcher(); + $eventDispatcher->addListener($beforeEventName, static function (BeforeEvent $event) {}); + $eventDispatcher->addListener($eventName, static function (AfterEvent $event) {}); + + return new TraceableEventDispatcher( + $eventDispatcher, + new Stopwatch() + ); + } + + public function getListenersStack(array $listeners): array + { + $stack = []; + + foreach ($listeners as $listener) { + $stack[] = [$listener['event'], $listener['priority']]; + } + + return $stack; + } +} + +class_alias(AbstractServiceTest::class, 'eZ\Publish\Core\Event\Tests\AbstractServiceTest'); diff --git a/tests/lib/Event/BookmarkServiceTest.php b/tests/lib/Event/BookmarkServiceTest.php new file mode 100644 index 0000000000..e052c50b13 --- /dev/null +++ b/tests/lib/Event/BookmarkServiceTest.php @@ -0,0 +1,134 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\BookmarkService as BookmarkServiceInterface; +use Ibexa\Contracts\Core\Repository\Events\Bookmark\BeforeCreateBookmarkEvent; +use Ibexa\Contracts\Core\Repository\Events\Bookmark\BeforeDeleteBookmarkEvent; +use Ibexa\Contracts\Core\Repository\Events\Bookmark\CreateBookmarkEvent; +use Ibexa\Contracts\Core\Repository\Events\Bookmark\DeleteBookmarkEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Core\Event\BookmarkService; + +class BookmarkServiceTest extends AbstractServiceTest +{ + public function testCreateBookmarkEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateBookmarkEvent::class, + CreateBookmarkEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $innerServiceMock = $this->createMock(BookmarkServiceInterface::class); + + $service = new BookmarkService($innerServiceMock, $traceableEventDispatcher); + $service->createBookmark(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeCreateBookmarkEvent::class, 0], + [CreateBookmarkEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateBookmarkStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateBookmarkEvent::class, + CreateBookmarkEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $innerServiceMock = $this->createMock(BookmarkServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeCreateBookmarkEvent::class, static function (BeforeCreateBookmarkEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new BookmarkService($innerServiceMock, $traceableEventDispatcher); + $service->createBookmark(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeCreateBookmarkEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateBookmarkEvent::class, 0], + [CreateBookmarkEvent::class, 0], + ]); + } + + public function testDeleteBookmarkEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteBookmarkEvent::class, + DeleteBookmarkEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $innerServiceMock = $this->createMock(BookmarkServiceInterface::class); + + $service = new BookmarkService($innerServiceMock, $traceableEventDispatcher); + $service->deleteBookmark(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteBookmarkEvent::class, 0], + [DeleteBookmarkEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteBookmarkStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteBookmarkEvent::class, + DeleteBookmarkEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $innerServiceMock = $this->createMock(BookmarkServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteBookmarkEvent::class, static function (BeforeDeleteBookmarkEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new BookmarkService($innerServiceMock, $traceableEventDispatcher); + $service->deleteBookmark(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteBookmarkEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteBookmarkEvent::class, 0], + [DeleteBookmarkEvent::class, 0], + ]); + } +} + +class_alias(BookmarkServiceTest::class, 'eZ\Publish\Core\Event\Tests\BookmarkServiceTest'); diff --git a/tests/lib/Event/ContentServiceTest.php b/tests/lib/Event/ContentServiceTest.php new file mode 100644 index 0000000000..3a5761249b --- /dev/null +++ b/tests/lib/Event/ContentServiceTest.php @@ -0,0 +1,1158 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\ContentService as ContentServiceInterface; +use Ibexa\Contracts\Core\Repository\Events\Content\AddRelationEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\BeforeAddRelationEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\BeforeCopyContentEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\BeforeCreateContentDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\BeforeCreateContentEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\BeforeDeleteContentEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\BeforeDeleteRelationEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\BeforeDeleteTranslationEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\BeforeDeleteVersionEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\BeforeHideContentEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\BeforePublishVersionEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\BeforeRevealContentEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\BeforeUpdateContentEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\BeforeUpdateContentMetadataEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\CopyContentEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\CreateContentDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\CreateContentEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\DeleteContentEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\DeleteRelationEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\DeleteTranslationEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\DeleteVersionEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\HideContentEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\PublishVersionEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\RevealContentEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\UpdateContentEvent; +use Ibexa\Contracts\Core\Repository\Events\Content\UpdateContentMetadataEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentMetadataUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Core\Event\ContentService; + +class ContentServiceTest extends AbstractServiceTest +{ + public function testDeleteContentEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteContentEvent::class, + DeleteContentEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + ]; + + $locations = []; + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('deleteContent')->willReturn($locations); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->deleteContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($locations, $result); + $this->assertSame($calledListeners, [ + [BeforeDeleteContentEvent::class, 0], + [DeleteContentEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnDeleteContentResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteContentEvent::class, + DeleteContentEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + ]; + + $locations = []; + $eventLocations = []; + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('deleteContent')->willReturn($locations); + + $traceableEventDispatcher->addListener(BeforeDeleteContentEvent::class, static function (BeforeDeleteContentEvent $event) use ($eventLocations) { + $event->setLocations($eventLocations); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->deleteContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventLocations, $result); + $this->assertSame($calledListeners, [ + [BeforeDeleteContentEvent::class, 10], + [BeforeDeleteContentEvent::class, 0], + [DeleteContentEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteContentStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteContentEvent::class, + DeleteContentEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + ]; + + $locations = []; + $eventLocations = []; + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('deleteContent')->willReturn($locations); + + $traceableEventDispatcher->addListener(BeforeDeleteContentEvent::class, static function (BeforeDeleteContentEvent $event) use ($eventLocations) { + $event->setLocations($eventLocations); + $event->stopPropagation(); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->deleteContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventLocations, $result); + $this->assertSame($calledListeners, [ + [BeforeDeleteContentEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteContentEvent::class, 0], + [DeleteContentEvent::class, 0], + ]); + } + + public function testCopyContentEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCopyContentEvent::class, + CopyContentEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(LocationCreateStruct::class), + $this->createMock(VersionInfo::class), + ]; + + $content = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('copyContent')->willReturn($content); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->copyContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($content, $result); + $this->assertSame($calledListeners, [ + [BeforeCopyContentEvent::class, 0], + [CopyContentEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCopyContentResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCopyContentEvent::class, + CopyContentEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(LocationCreateStruct::class), + $this->createMock(VersionInfo::class), + ]; + + $content = $this->createMock(Content::class); + $eventContent = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('copyContent')->willReturn($content); + + $traceableEventDispatcher->addListener(BeforeCopyContentEvent::class, static function (BeforeCopyContentEvent $event) use ($eventContent) { + $event->setContent($eventContent); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->copyContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventContent, $result); + $this->assertSame($calledListeners, [ + [BeforeCopyContentEvent::class, 10], + [BeforeCopyContentEvent::class, 0], + [CopyContentEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCopyContentStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCopyContentEvent::class, + CopyContentEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(LocationCreateStruct::class), + $this->createMock(VersionInfo::class), + ]; + + $content = $this->createMock(Content::class); + $eventContent = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('copyContent')->willReturn($content); + + $traceableEventDispatcher->addListener(BeforeCopyContentEvent::class, static function (BeforeCopyContentEvent $event) use ($eventContent) { + $event->setContent($eventContent); + $event->stopPropagation(); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->copyContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventContent, $result); + $this->assertSame($calledListeners, [ + [BeforeCopyContentEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCopyContentEvent::class, 0], + [CopyContentEvent::class, 0], + ]); + } + + public function testUpdateContentEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateContentEvent::class, + UpdateContentEvent::class + ); + + $parameters = [ + $this->createMock(VersionInfo::class), + $this->createMock(ContentUpdateStruct::class), + ]; + + $content = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('updateContent')->willReturn($content); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($content, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateContentEvent::class, 0], + [UpdateContentEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdateContentResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateContentEvent::class, + UpdateContentEvent::class + ); + + $parameters = [ + $this->createMock(VersionInfo::class), + $this->createMock(ContentUpdateStruct::class), + ]; + + $content = $this->createMock(Content::class); + $eventContent = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('updateContent')->willReturn($content); + + $traceableEventDispatcher->addListener(BeforeUpdateContentEvent::class, static function (BeforeUpdateContentEvent $event) use ($eventContent) { + $event->setContent($eventContent); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventContent, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateContentEvent::class, 10], + [BeforeUpdateContentEvent::class, 0], + [UpdateContentEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateContentStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateContentEvent::class, + UpdateContentEvent::class + ); + + $parameters = [ + $this->createMock(VersionInfo::class), + $this->createMock(ContentUpdateStruct::class), + ]; + + $content = $this->createMock(Content::class); + $eventContent = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('updateContent')->willReturn($content); + + $traceableEventDispatcher->addListener(BeforeUpdateContentEvent::class, static function (BeforeUpdateContentEvent $event) use ($eventContent) { + $event->setContent($eventContent); + $event->stopPropagation(); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventContent, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateContentEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateContentEvent::class, 0], + [UpdateContentEvent::class, 0], + ]); + } + + public function testDeleteRelationEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteRelationEvent::class, + DeleteRelationEvent::class + ); + + $parameters = [ + $this->createMock(VersionInfo::class), + $this->createMock(ContentInfo::class), + ]; + + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $service->deleteRelation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteRelationEvent::class, 0], + [DeleteRelationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteRelationStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteRelationEvent::class, + DeleteRelationEvent::class + ); + + $parameters = [ + $this->createMock(VersionInfo::class), + $this->createMock(ContentInfo::class), + ]; + + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteRelationEvent::class, static function (BeforeDeleteRelationEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $service->deleteRelation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteRelationEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteRelationEvent::class, 0], + [DeleteRelationEvent::class, 0], + ]); + } + + public function testCreateContentEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentEvent::class, + CreateContentEvent::class + ); + + $parameters = [ + $this->createMock(ContentCreateStruct::class), + [], + ]; + + $content = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('createContent')->willReturn($content); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($content, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentEvent::class, 0], + [CreateContentEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateContentResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentEvent::class, + CreateContentEvent::class + ); + + $parameters = [ + $this->createMock(ContentCreateStruct::class), + [], + ]; + + $content = $this->createMock(Content::class); + $eventContent = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('createContent')->willReturn($content); + + $traceableEventDispatcher->addListener(BeforeCreateContentEvent::class, static function (BeforeCreateContentEvent $event) use ($eventContent) { + $event->setContent($eventContent); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventContent, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentEvent::class, 10], + [BeforeCreateContentEvent::class, 0], + [CreateContentEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateContentStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentEvent::class, + CreateContentEvent::class + ); + + $parameters = [ + $this->createMock(ContentCreateStruct::class), + [], + ]; + + $content = $this->createMock(Content::class); + $eventContent = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('createContent')->willReturn($content); + + $traceableEventDispatcher->addListener(BeforeCreateContentEvent::class, static function (BeforeCreateContentEvent $event) use ($eventContent) { + $event->setContent($eventContent); + $event->stopPropagation(); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventContent, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateContentEvent::class, 0], + [CreateContentEvent::class, 0], + ]); + } + + public function testHideContentEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeHideContentEvent::class, + HideContentEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + ]; + + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $service->hideContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeHideContentEvent::class, 0], + [HideContentEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testHideContentStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeHideContentEvent::class, + HideContentEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + ]; + + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeHideContentEvent::class, static function (BeforeHideContentEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $service->hideContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeHideContentEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeHideContentEvent::class, 0], + [HideContentEvent::class, 0], + ]); + } + + public function testDeleteVersionEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteVersionEvent::class, + DeleteVersionEvent::class + ); + + $parameters = [ + $this->createMock(VersionInfo::class), + ]; + + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $service->deleteVersion(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteVersionEvent::class, 0], + [DeleteVersionEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteVersionStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteVersionEvent::class, + DeleteVersionEvent::class + ); + + $parameters = [ + $this->createMock(VersionInfo::class), + ]; + + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteVersionEvent::class, static function (BeforeDeleteVersionEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $service->deleteVersion(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteVersionEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteVersionEvent::class, 0], + [DeleteVersionEvent::class, 0], + ]); + } + + public function testAddRelationEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAddRelationEvent::class, + AddRelationEvent::class + ); + + $parameters = [ + $this->createMock(VersionInfo::class), + $this->createMock(ContentInfo::class), + ]; + + $relation = $this->createMock(Relation::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('addRelation')->willReturn($relation); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->addRelation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($relation, $result); + $this->assertSame($calledListeners, [ + [BeforeAddRelationEvent::class, 0], + [AddRelationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnAddRelationResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAddRelationEvent::class, + AddRelationEvent::class + ); + + $parameters = [ + $this->createMock(VersionInfo::class), + $this->createMock(ContentInfo::class), + ]; + + $relation = $this->createMock(Relation::class); + $eventRelation = $this->createMock(Relation::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('addRelation')->willReturn($relation); + + $traceableEventDispatcher->addListener(BeforeAddRelationEvent::class, static function (BeforeAddRelationEvent $event) use ($eventRelation) { + $event->setRelation($eventRelation); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->addRelation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventRelation, $result); + $this->assertSame($calledListeners, [ + [BeforeAddRelationEvent::class, 10], + [BeforeAddRelationEvent::class, 0], + [AddRelationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testAddRelationStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAddRelationEvent::class, + AddRelationEvent::class + ); + + $parameters = [ + $this->createMock(VersionInfo::class), + $this->createMock(ContentInfo::class), + ]; + + $relation = $this->createMock(Relation::class); + $eventRelation = $this->createMock(Relation::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('addRelation')->willReturn($relation); + + $traceableEventDispatcher->addListener(BeforeAddRelationEvent::class, static function (BeforeAddRelationEvent $event) use ($eventRelation) { + $event->setRelation($eventRelation); + $event->stopPropagation(); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->addRelation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventRelation, $result); + $this->assertSame($calledListeners, [ + [BeforeAddRelationEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [AddRelationEvent::class, 0], + [BeforeAddRelationEvent::class, 0], + ]); + } + + public function testUpdateContentMetadataEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateContentMetadataEvent::class, + UpdateContentMetadataEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(ContentMetadataUpdateStruct::class), + ]; + + $content = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('updateContentMetadata')->willReturn($content); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateContentMetadata(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($content, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateContentMetadataEvent::class, 0], + [UpdateContentMetadataEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdateContentMetadataResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateContentMetadataEvent::class, + UpdateContentMetadataEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(ContentMetadataUpdateStruct::class), + ]; + + $content = $this->createMock(Content::class); + $eventContent = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('updateContentMetadata')->willReturn($content); + + $traceableEventDispatcher->addListener(BeforeUpdateContentMetadataEvent::class, static function (BeforeUpdateContentMetadataEvent $event) use ($eventContent) { + $event->setContent($eventContent); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateContentMetadata(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventContent, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateContentMetadataEvent::class, 10], + [BeforeUpdateContentMetadataEvent::class, 0], + [UpdateContentMetadataEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateContentMetadataStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateContentMetadataEvent::class, + UpdateContentMetadataEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(ContentMetadataUpdateStruct::class), + ]; + + $content = $this->createMock(Content::class); + $eventContent = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('updateContentMetadata')->willReturn($content); + + $traceableEventDispatcher->addListener(BeforeUpdateContentMetadataEvent::class, static function (BeforeUpdateContentMetadataEvent $event) use ($eventContent) { + $event->setContent($eventContent); + $event->stopPropagation(); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateContentMetadata(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventContent, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateContentMetadataEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateContentMetadataEvent::class, 0], + [UpdateContentMetadataEvent::class, 0], + ]); + } + + public function testDeleteTranslationEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteTranslationEvent::class, + DeleteTranslationEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + 'random_value_5cff79c31a2f31.74205767', + ]; + + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $service->deleteTranslation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteTranslationEvent::class, 0], + [DeleteTranslationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteTranslationStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteTranslationEvent::class, + DeleteTranslationEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + 'random_value_5cff79c31a2fc0.71971617', + ]; + + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteTranslationEvent::class, static function (BeforeDeleteTranslationEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $service->deleteTranslation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteTranslationEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteTranslationEvent::class, 0], + [DeleteTranslationEvent::class, 0], + ]); + } + + public function testPublishVersionEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforePublishVersionEvent::class, + PublishVersionEvent::class + ); + + $parameters = [ + $this->createMock(VersionInfo::class), + [], + ]; + + $content = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('publishVersion')->willReturn($content); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->publishVersion(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($content, $result); + $this->assertSame($calledListeners, [ + [BeforePublishVersionEvent::class, 0], + [PublishVersionEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnPublishVersionResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforePublishVersionEvent::class, + PublishVersionEvent::class + ); + + $parameters = [ + $this->createMock(VersionInfo::class), + [], + ]; + + $content = $this->createMock(Content::class); + $eventContent = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('publishVersion')->willReturn($content); + + $traceableEventDispatcher->addListener(BeforePublishVersionEvent::class, static function (BeforePublishVersionEvent $event) use ($eventContent) { + $event->setContent($eventContent); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->publishVersion(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventContent, $result); + $this->assertSame($calledListeners, [ + [BeforePublishVersionEvent::class, 10], + [BeforePublishVersionEvent::class, 0], + [PublishVersionEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testPublishVersionStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforePublishVersionEvent::class, + PublishVersionEvent::class + ); + + $parameters = [ + $this->createMock(VersionInfo::class), + [], + ]; + + $content = $this->createMock(Content::class); + $eventContent = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('publishVersion')->willReturn($content); + + $traceableEventDispatcher->addListener(BeforePublishVersionEvent::class, static function (BeforePublishVersionEvent $event) use ($eventContent) { + $event->setContent($eventContent); + $event->stopPropagation(); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->publishVersion(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventContent, $result); + $this->assertSame($calledListeners, [ + [BeforePublishVersionEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforePublishVersionEvent::class, 0], + [PublishVersionEvent::class, 0], + ]); + } + + public function testCreateContentDraftEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentDraftEvent::class, + CreateContentDraftEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(VersionInfo::class), + $this->createMock(User::class), + ]; + + $contentDraft = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('createContentDraft')->willReturn($contentDraft); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContentDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($contentDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentDraftEvent::class, 0], + [CreateContentDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateContentDraftResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentDraftEvent::class, + CreateContentDraftEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(VersionInfo::class), + $this->createMock(User::class), + ]; + + $contentDraft = $this->createMock(Content::class); + $eventContentDraft = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('createContentDraft')->willReturn($contentDraft); + + $traceableEventDispatcher->addListener(BeforeCreateContentDraftEvent::class, static function (BeforeCreateContentDraftEvent $event) use ($eventContentDraft) { + $event->setContentDraft($eventContentDraft); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContentDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventContentDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentDraftEvent::class, 10], + [BeforeCreateContentDraftEvent::class, 0], + [CreateContentDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateContentDraftStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentDraftEvent::class, + CreateContentDraftEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(VersionInfo::class), + $this->createMock(User::class), + ]; + + $contentDraft = $this->createMock(Content::class); + $eventContentDraft = $this->createMock(Content::class); + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + $innerServiceMock->method('createContentDraft')->willReturn($contentDraft); + + $traceableEventDispatcher->addListener(BeforeCreateContentDraftEvent::class, static function (BeforeCreateContentDraftEvent $event) use ($eventContentDraft) { + $event->setContentDraft($eventContentDraft); + $event->stopPropagation(); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContentDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventContentDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentDraftEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateContentDraftEvent::class, 0], + [CreateContentDraftEvent::class, 0], + ]); + } + + public function testRevealContentEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRevealContentEvent::class, + RevealContentEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + ]; + + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $service->revealContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeRevealContentEvent::class, 0], + [RevealContentEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testRevealContentStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRevealContentEvent::class, + RevealContentEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + ]; + + $innerServiceMock = $this->createMock(ContentServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeRevealContentEvent::class, static function (BeforeRevealContentEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentService($innerServiceMock, $traceableEventDispatcher); + $service->revealContent(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeRevealContentEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeRevealContentEvent::class, 0], + [RevealContentEvent::class, 0], + ]); + } +} + +class_alias(ContentServiceTest::class, 'eZ\Publish\Core\Event\Tests\ContentServiceTest'); diff --git a/tests/lib/Event/ContentTypeServiceTest.php b/tests/lib/Event/ContentTypeServiceTest.php new file mode 100644 index 0000000000..baac131341 --- /dev/null +++ b/tests/lib/Event/ContentTypeServiceTest.php @@ -0,0 +1,1146 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\ContentTypeService as ContentTypeServiceInterface; +use Ibexa\Contracts\Core\Repository\Events\ContentType\AddFieldDefinitionEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\AssignContentTypeGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeAddFieldDefinitionEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeAssignContentTypeGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeCopyContentTypeEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeCreateContentTypeDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeCreateContentTypeEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeCreateContentTypeGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeDeleteContentTypeEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeDeleteContentTypeGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforePublishContentTypeDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeRemoveContentTypeTranslationEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeRemoveFieldDefinitionEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeUnassignContentTypeGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeUpdateContentTypeDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeUpdateContentTypeGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\BeforeUpdateFieldDefinitionEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\CopyContentTypeEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\CreateContentTypeDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\CreateContentTypeEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\CreateContentTypeGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\DeleteContentTypeEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\DeleteContentTypeGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\PublishContentTypeDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\RemoveContentTypeTranslationEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\RemoveFieldDefinitionEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\UnassignContentTypeGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\UpdateContentTypeDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\UpdateContentTypeGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ContentType\UpdateFieldDefinitionEvent; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Core\Event\ContentTypeService; + +class ContentTypeServiceTest extends AbstractServiceTest +{ + public function testAddFieldDefinitionEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAddFieldDefinitionEvent::class, + AddFieldDefinitionEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeDraft::class), + $this->createMock(FieldDefinitionCreateStruct::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->addFieldDefinition(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAddFieldDefinitionEvent::class, 0], + [AddFieldDefinitionEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testAddFieldDefinitionStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAddFieldDefinitionEvent::class, + AddFieldDefinitionEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeDraft::class), + $this->createMock(FieldDefinitionCreateStruct::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeAddFieldDefinitionEvent::class, static function (BeforeAddFieldDefinitionEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->addFieldDefinition(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAddFieldDefinitionEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [AddFieldDefinitionEvent::class, 0], + [BeforeAddFieldDefinitionEvent::class, 0], + ]); + } + + public function testDeleteContentTypeGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteContentTypeGroupEvent::class, + DeleteContentTypeGroupEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeGroup::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->deleteContentTypeGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteContentTypeGroupEvent::class, 0], + [DeleteContentTypeGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteContentTypeGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteContentTypeGroupEvent::class, + DeleteContentTypeGroupEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeGroup::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteContentTypeGroupEvent::class, static function (BeforeDeleteContentTypeGroupEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->deleteContentTypeGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteContentTypeGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteContentTypeGroupEvent::class, 0], + [DeleteContentTypeGroupEvent::class, 0], + ]); + } + + public function testCreateContentTypeDraftEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentTypeDraftEvent::class, + CreateContentTypeDraftEvent::class + ); + + $parameters = [ + $this->createMock(ContentType::class), + ]; + + $contentTypeDraft = $this->createMock(ContentTypeDraft::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('createContentTypeDraft')->willReturn($contentTypeDraft); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContentTypeDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($contentTypeDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentTypeDraftEvent::class, 0], + [CreateContentTypeDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateContentTypeDraftResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentTypeDraftEvent::class, + CreateContentTypeDraftEvent::class + ); + + $parameters = [ + $this->createMock(ContentType::class), + ]; + + $contentTypeDraft = $this->createMock(ContentTypeDraft::class); + $eventContentTypeDraft = $this->createMock(ContentTypeDraft::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('createContentTypeDraft')->willReturn($contentTypeDraft); + + $traceableEventDispatcher->addListener(BeforeCreateContentTypeDraftEvent::class, static function (BeforeCreateContentTypeDraftEvent $event) use ($eventContentTypeDraft) { + $event->setContentTypeDraft($eventContentTypeDraft); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContentTypeDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventContentTypeDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentTypeDraftEvent::class, 10], + [BeforeCreateContentTypeDraftEvent::class, 0], + [CreateContentTypeDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateContentTypeDraftStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentTypeDraftEvent::class, + CreateContentTypeDraftEvent::class + ); + + $parameters = [ + $this->createMock(ContentType::class), + ]; + + $contentTypeDraft = $this->createMock(ContentTypeDraft::class); + $eventContentTypeDraft = $this->createMock(ContentTypeDraft::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('createContentTypeDraft')->willReturn($contentTypeDraft); + + $traceableEventDispatcher->addListener(BeforeCreateContentTypeDraftEvent::class, static function (BeforeCreateContentTypeDraftEvent $event) use ($eventContentTypeDraft) { + $event->setContentTypeDraft($eventContentTypeDraft); + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContentTypeDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventContentTypeDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentTypeDraftEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateContentTypeDraftEvent::class, 0], + [CreateContentTypeDraftEvent::class, 0], + ]); + } + + public function testCreateContentTypeGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentTypeGroupEvent::class, + CreateContentTypeGroupEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeGroupCreateStruct::class), + ]; + + $contentTypeGroup = $this->createMock(ContentTypeGroup::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('createContentTypeGroup')->willReturn($contentTypeGroup); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContentTypeGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($contentTypeGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentTypeGroupEvent::class, 0], + [CreateContentTypeGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateContentTypeGroupResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentTypeGroupEvent::class, + CreateContentTypeGroupEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeGroupCreateStruct::class), + ]; + + $contentTypeGroup = $this->createMock(ContentTypeGroup::class); + $eventContentTypeGroup = $this->createMock(ContentTypeGroup::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('createContentTypeGroup')->willReturn($contentTypeGroup); + + $traceableEventDispatcher->addListener(BeforeCreateContentTypeGroupEvent::class, static function (BeforeCreateContentTypeGroupEvent $event) use ($eventContentTypeGroup) { + $event->setContentTypeGroup($eventContentTypeGroup); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContentTypeGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventContentTypeGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentTypeGroupEvent::class, 10], + [BeforeCreateContentTypeGroupEvent::class, 0], + [CreateContentTypeGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateContentTypeGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentTypeGroupEvent::class, + CreateContentTypeGroupEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeGroupCreateStruct::class), + ]; + + $contentTypeGroup = $this->createMock(ContentTypeGroup::class); + $eventContentTypeGroup = $this->createMock(ContentTypeGroup::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('createContentTypeGroup')->willReturn($contentTypeGroup); + + $traceableEventDispatcher->addListener(BeforeCreateContentTypeGroupEvent::class, static function (BeforeCreateContentTypeGroupEvent $event) use ($eventContentTypeGroup) { + $event->setContentTypeGroup($eventContentTypeGroup); + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContentTypeGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventContentTypeGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentTypeGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateContentTypeGroupEvent::class, 0], + [CreateContentTypeGroupEvent::class, 0], + ]); + } + + public function testUpdateContentTypeGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateContentTypeGroupEvent::class, + UpdateContentTypeGroupEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeGroup::class), + $this->createMock(ContentTypeGroupUpdateStruct::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->updateContentTypeGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeUpdateContentTypeGroupEvent::class, 0], + [UpdateContentTypeGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateContentTypeGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateContentTypeGroupEvent::class, + UpdateContentTypeGroupEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeGroup::class), + $this->createMock(ContentTypeGroupUpdateStruct::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeUpdateContentTypeGroupEvent::class, static function (BeforeUpdateContentTypeGroupEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->updateContentTypeGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeUpdateContentTypeGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateContentTypeGroupEvent::class, 0], + [UpdateContentTypeGroupEvent::class, 0], + ]); + } + + public function testCreateContentTypeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentTypeEvent::class, + CreateContentTypeEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeCreateStruct::class), + [], + ]; + + $contentTypeDraft = $this->createMock(ContentTypeDraft::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('createContentType')->willReturn($contentTypeDraft); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContentType(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($contentTypeDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentTypeEvent::class, 0], + [CreateContentTypeEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateContentTypeResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentTypeEvent::class, + CreateContentTypeEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeCreateStruct::class), + [], + ]; + + $contentTypeDraft = $this->createMock(ContentTypeDraft::class); + $eventContentTypeDraft = $this->createMock(ContentTypeDraft::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('createContentType')->willReturn($contentTypeDraft); + + $traceableEventDispatcher->addListener(BeforeCreateContentTypeEvent::class, static function (BeforeCreateContentTypeEvent $event) use ($eventContentTypeDraft) { + $event->setContentTypeDraft($eventContentTypeDraft); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContentType(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventContentTypeDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentTypeEvent::class, 10], + [BeforeCreateContentTypeEvent::class, 0], + [CreateContentTypeEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateContentTypeStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateContentTypeEvent::class, + CreateContentTypeEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeCreateStruct::class), + [], + ]; + + $contentTypeDraft = $this->createMock(ContentTypeDraft::class); + $eventContentTypeDraft = $this->createMock(ContentTypeDraft::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('createContentType')->willReturn($contentTypeDraft); + + $traceableEventDispatcher->addListener(BeforeCreateContentTypeEvent::class, static function (BeforeCreateContentTypeEvent $event) use ($eventContentTypeDraft) { + $event->setContentTypeDraft($eventContentTypeDraft); + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createContentType(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventContentTypeDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateContentTypeEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateContentTypeEvent::class, 0], + [CreateContentTypeEvent::class, 0], + ]); + } + + public function testRemoveContentTypeTranslationEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemoveContentTypeTranslationEvent::class, + RemoveContentTypeTranslationEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeDraft::class), + 'random_value_5cff79c318f864.57583321', + ]; + + $newContentTypeDraft = $this->createMock(ContentTypeDraft::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('removeContentTypeTranslation')->willReturn($newContentTypeDraft); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->removeContentTypeTranslation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($newContentTypeDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeRemoveContentTypeTranslationEvent::class, 0], + [RemoveContentTypeTranslationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnRemoveContentTypeTranslationResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemoveContentTypeTranslationEvent::class, + RemoveContentTypeTranslationEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeDraft::class), + 'random_value_5cff79c318f913.11826610', + ]; + + $newContentTypeDraft = $this->createMock(ContentTypeDraft::class); + $eventNewContentTypeDraft = $this->createMock(ContentTypeDraft::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('removeContentTypeTranslation')->willReturn($newContentTypeDraft); + + $traceableEventDispatcher->addListener(BeforeRemoveContentTypeTranslationEvent::class, static function (BeforeRemoveContentTypeTranslationEvent $event) use ($eventNewContentTypeDraft) { + $event->setNewContentTypeDraft($eventNewContentTypeDraft); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->removeContentTypeTranslation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventNewContentTypeDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeRemoveContentTypeTranslationEvent::class, 10], + [BeforeRemoveContentTypeTranslationEvent::class, 0], + [RemoveContentTypeTranslationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testRemoveContentTypeTranslationStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemoveContentTypeTranslationEvent::class, + RemoveContentTypeTranslationEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeDraft::class), + 'random_value_5cff79c318f983.61112462', + ]; + + $newContentTypeDraft = $this->createMock(ContentTypeDraft::class); + $eventNewContentTypeDraft = $this->createMock(ContentTypeDraft::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('removeContentTypeTranslation')->willReturn($newContentTypeDraft); + + $traceableEventDispatcher->addListener(BeforeRemoveContentTypeTranslationEvent::class, static function (BeforeRemoveContentTypeTranslationEvent $event) use ($eventNewContentTypeDraft) { + $event->setNewContentTypeDraft($eventNewContentTypeDraft); + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->removeContentTypeTranslation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventNewContentTypeDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeRemoveContentTypeTranslationEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeRemoveContentTypeTranslationEvent::class, 0], + [RemoveContentTypeTranslationEvent::class, 0], + ]); + } + + public function testUnassignContentTypeGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUnassignContentTypeGroupEvent::class, + UnassignContentTypeGroupEvent::class + ); + + $parameters = [ + $this->createMock(ContentType::class), + $this->createMock(ContentTypeGroup::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->unassignContentTypeGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeUnassignContentTypeGroupEvent::class, 0], + [UnassignContentTypeGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUnassignContentTypeGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUnassignContentTypeGroupEvent::class, + UnassignContentTypeGroupEvent::class + ); + + $parameters = [ + $this->createMock(ContentType::class), + $this->createMock(ContentTypeGroup::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeUnassignContentTypeGroupEvent::class, static function (BeforeUnassignContentTypeGroupEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->unassignContentTypeGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeUnassignContentTypeGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUnassignContentTypeGroupEvent::class, 0], + [UnassignContentTypeGroupEvent::class, 0], + ]); + } + + public function testPublishContentTypeDraftEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforePublishContentTypeDraftEvent::class, + PublishContentTypeDraftEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeDraft::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->publishContentTypeDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforePublishContentTypeDraftEvent::class, 0], + [PublishContentTypeDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testPublishContentTypeDraftStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforePublishContentTypeDraftEvent::class, + PublishContentTypeDraftEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeDraft::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforePublishContentTypeDraftEvent::class, static function (BeforePublishContentTypeDraftEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->publishContentTypeDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforePublishContentTypeDraftEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforePublishContentTypeDraftEvent::class, 0], + [PublishContentTypeDraftEvent::class, 0], + ]); + } + + public function testUpdateFieldDefinitionEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateFieldDefinitionEvent::class, + UpdateFieldDefinitionEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeDraft::class), + $this->createMock(FieldDefinition::class), + $this->createMock(FieldDefinitionUpdateStruct::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->updateFieldDefinition(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeUpdateFieldDefinitionEvent::class, 0], + [UpdateFieldDefinitionEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateFieldDefinitionStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateFieldDefinitionEvent::class, + UpdateFieldDefinitionEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeDraft::class), + $this->createMock(FieldDefinition::class), + $this->createMock(FieldDefinitionUpdateStruct::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeUpdateFieldDefinitionEvent::class, static function (BeforeUpdateFieldDefinitionEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->updateFieldDefinition(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeUpdateFieldDefinitionEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateFieldDefinitionEvent::class, 0], + [UpdateFieldDefinitionEvent::class, 0], + ]); + } + + public function testRemoveFieldDefinitionEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemoveFieldDefinitionEvent::class, + RemoveFieldDefinitionEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeDraft::class), + $this->createMock(FieldDefinition::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->removeFieldDefinition(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeRemoveFieldDefinitionEvent::class, 0], + [RemoveFieldDefinitionEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testRemoveFieldDefinitionStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemoveFieldDefinitionEvent::class, + RemoveFieldDefinitionEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeDraft::class), + $this->createMock(FieldDefinition::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeRemoveFieldDefinitionEvent::class, static function (BeforeRemoveFieldDefinitionEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->removeFieldDefinition(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeRemoveFieldDefinitionEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeRemoveFieldDefinitionEvent::class, 0], + [RemoveFieldDefinitionEvent::class, 0], + ]); + } + + public function testAssignContentTypeGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAssignContentTypeGroupEvent::class, + AssignContentTypeGroupEvent::class + ); + + $parameters = [ + $this->createMock(ContentType::class), + $this->createMock(ContentTypeGroup::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->assignContentTypeGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAssignContentTypeGroupEvent::class, 0], + [AssignContentTypeGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testAssignContentTypeGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAssignContentTypeGroupEvent::class, + AssignContentTypeGroupEvent::class + ); + + $parameters = [ + $this->createMock(ContentType::class), + $this->createMock(ContentTypeGroup::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeAssignContentTypeGroupEvent::class, static function (BeforeAssignContentTypeGroupEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->assignContentTypeGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAssignContentTypeGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [AssignContentTypeGroupEvent::class, 0], + [BeforeAssignContentTypeGroupEvent::class, 0], + ]); + } + + public function testUpdateContentTypeDraftEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateContentTypeDraftEvent::class, + UpdateContentTypeDraftEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeDraft::class), + $this->createMock(ContentTypeUpdateStruct::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->updateContentTypeDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeUpdateContentTypeDraftEvent::class, 0], + [UpdateContentTypeDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateContentTypeDraftStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateContentTypeDraftEvent::class, + UpdateContentTypeDraftEvent::class + ); + + $parameters = [ + $this->createMock(ContentTypeDraft::class), + $this->createMock(ContentTypeUpdateStruct::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeUpdateContentTypeDraftEvent::class, static function (BeforeUpdateContentTypeDraftEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->updateContentTypeDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeUpdateContentTypeDraftEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateContentTypeDraftEvent::class, 0], + [UpdateContentTypeDraftEvent::class, 0], + ]); + } + + public function testDeleteContentTypeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteContentTypeEvent::class, + DeleteContentTypeEvent::class + ); + + $parameters = [ + $this->createMock(ContentType::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->deleteContentType(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteContentTypeEvent::class, 0], + [DeleteContentTypeEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteContentTypeStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteContentTypeEvent::class, + DeleteContentTypeEvent::class + ); + + $parameters = [ + $this->createMock(ContentType::class), + ]; + + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteContentTypeEvent::class, static function (BeforeDeleteContentTypeEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $service->deleteContentType(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteContentTypeEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteContentTypeEvent::class, 0], + [DeleteContentTypeEvent::class, 0], + ]); + } + + public function testCopyContentTypeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCopyContentTypeEvent::class, + CopyContentTypeEvent::class + ); + + $parameters = [ + $this->createMock(ContentType::class), + $this->createMock(User::class), + ]; + + $contentTypeCopy = $this->createMock(ContentType::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('copyContentType')->willReturn($contentTypeCopy); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->copyContentType(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($contentTypeCopy, $result); + $this->assertSame($calledListeners, [ + [BeforeCopyContentTypeEvent::class, 0], + [CopyContentTypeEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCopyContentTypeResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCopyContentTypeEvent::class, + CopyContentTypeEvent::class + ); + + $parameters = [ + $this->createMock(ContentType::class), + $this->createMock(User::class), + ]; + + $contentTypeCopy = $this->createMock(ContentType::class); + $eventContentTypeCopy = $this->createMock(ContentType::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('copyContentType')->willReturn($contentTypeCopy); + + $traceableEventDispatcher->addListener(BeforeCopyContentTypeEvent::class, static function (BeforeCopyContentTypeEvent $event) use ($eventContentTypeCopy) { + $event->setContentTypeCopy($eventContentTypeCopy); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->copyContentType(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventContentTypeCopy, $result); + $this->assertSame($calledListeners, [ + [BeforeCopyContentTypeEvent::class, 10], + [BeforeCopyContentTypeEvent::class, 0], + [CopyContentTypeEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCopyContentTypeStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCopyContentTypeEvent::class, + CopyContentTypeEvent::class + ); + + $parameters = [ + $this->createMock(ContentType::class), + $this->createMock(User::class), + ]; + + $contentTypeCopy = $this->createMock(ContentType::class); + $eventContentTypeCopy = $this->createMock(ContentType::class); + $innerServiceMock = $this->createMock(ContentTypeServiceInterface::class); + $innerServiceMock->method('copyContentType')->willReturn($contentTypeCopy); + + $traceableEventDispatcher->addListener(BeforeCopyContentTypeEvent::class, static function (BeforeCopyContentTypeEvent $event) use ($eventContentTypeCopy) { + $event->setContentTypeCopy($eventContentTypeCopy); + $event->stopPropagation(); + }, 10); + + $service = new ContentTypeService($innerServiceMock, $traceableEventDispatcher); + $result = $service->copyContentType(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventContentTypeCopy, $result); + $this->assertSame($calledListeners, [ + [BeforeCopyContentTypeEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCopyContentTypeEvent::class, 0], + [CopyContentTypeEvent::class, 0], + ]); + } +} + +class_alias(ContentTypeServiceTest::class, 'eZ\Publish\Core\Event\Tests\ContentTypeServiceTest'); diff --git a/tests/lib/Event/LanguageServiceTest.php b/tests/lib/Event/LanguageServiceTest.php new file mode 100644 index 0000000000..0995fca95a --- /dev/null +++ b/tests/lib/Event/LanguageServiceTest.php @@ -0,0 +1,483 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Events\Language\BeforeCreateLanguageEvent; +use Ibexa\Contracts\Core\Repository\Events\Language\BeforeDeleteLanguageEvent; +use Ibexa\Contracts\Core\Repository\Events\Language\BeforeDisableLanguageEvent; +use Ibexa\Contracts\Core\Repository\Events\Language\BeforeEnableLanguageEvent; +use Ibexa\Contracts\Core\Repository\Events\Language\BeforeUpdateLanguageNameEvent; +use Ibexa\Contracts\Core\Repository\Events\Language\CreateLanguageEvent; +use Ibexa\Contracts\Core\Repository\Events\Language\DeleteLanguageEvent; +use Ibexa\Contracts\Core\Repository\Events\Language\DisableLanguageEvent; +use Ibexa\Contracts\Core\Repository\Events\Language\EnableLanguageEvent; +use Ibexa\Contracts\Core\Repository\Events\Language\UpdateLanguageNameEvent; +use Ibexa\Contracts\Core\Repository\LanguageService as LanguageServiceInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\LanguageCreateStruct; +use Ibexa\Core\Event\LanguageService; + +class LanguageServiceTest extends AbstractServiceTest +{ + public function testDeleteLanguageEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteLanguageEvent::class, + DeleteLanguageEvent::class + ); + + $parameters = [ + $this->createMock(Language::class), + ]; + + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $service->deleteLanguage(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteLanguageEvent::class, 0], + [DeleteLanguageEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteLanguageStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteLanguageEvent::class, + DeleteLanguageEvent::class + ); + + $parameters = [ + $this->createMock(Language::class), + ]; + + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteLanguageEvent::class, static function (BeforeDeleteLanguageEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $service->deleteLanguage(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteLanguageEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteLanguageEvent::class, 0], + [DeleteLanguageEvent::class, 0], + ]); + } + + public function testCreateLanguageEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateLanguageEvent::class, + CreateLanguageEvent::class + ); + + $parameters = [ + $this->createMock(LanguageCreateStruct::class), + ]; + + $language = $this->createMock(Language::class); + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + $innerServiceMock->method('createLanguage')->willReturn($language); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createLanguage(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($language, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateLanguageEvent::class, 0], + [CreateLanguageEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateLanguageResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateLanguageEvent::class, + CreateLanguageEvent::class + ); + + $parameters = [ + $this->createMock(LanguageCreateStruct::class), + ]; + + $language = $this->createMock(Language::class); + $eventLanguage = $this->createMock(Language::class); + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + $innerServiceMock->method('createLanguage')->willReturn($language); + + $traceableEventDispatcher->addListener(BeforeCreateLanguageEvent::class, static function (BeforeCreateLanguageEvent $event) use ($eventLanguage) { + $event->setLanguage($eventLanguage); + }, 10); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createLanguage(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventLanguage, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateLanguageEvent::class, 10], + [BeforeCreateLanguageEvent::class, 0], + [CreateLanguageEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateLanguageStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateLanguageEvent::class, + CreateLanguageEvent::class + ); + + $parameters = [ + $this->createMock(LanguageCreateStruct::class), + ]; + + $language = $this->createMock(Language::class); + $eventLanguage = $this->createMock(Language::class); + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + $innerServiceMock->method('createLanguage')->willReturn($language); + + $traceableEventDispatcher->addListener(BeforeCreateLanguageEvent::class, static function (BeforeCreateLanguageEvent $event) use ($eventLanguage) { + $event->setLanguage($eventLanguage); + $event->stopPropagation(); + }, 10); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createLanguage(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventLanguage, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateLanguageEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateLanguageEvent::class, 0], + [CreateLanguageEvent::class, 0], + ]); + } + + public function testUpdateLanguageNameEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateLanguageNameEvent::class, + UpdateLanguageNameEvent::class + ); + + $parameters = [ + $this->createMock(Language::class), + 'random_value_5cff79c3161276.87987683', + ]; + + $updatedLanguage = $this->createMock(Language::class); + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + $innerServiceMock->method('updateLanguageName')->willReturn($updatedLanguage); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateLanguageName(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($updatedLanguage, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateLanguageNameEvent::class, 0], + [UpdateLanguageNameEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdateLanguageNameResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateLanguageNameEvent::class, + UpdateLanguageNameEvent::class + ); + + $parameters = [ + $this->createMock(Language::class), + 'random_value_5cff79c3161312.94068030', + ]; + + $updatedLanguage = $this->createMock(Language::class); + $eventUpdatedLanguage = $this->createMock(Language::class); + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + $innerServiceMock->method('updateLanguageName')->willReturn($updatedLanguage); + + $traceableEventDispatcher->addListener(BeforeUpdateLanguageNameEvent::class, static function (BeforeUpdateLanguageNameEvent $event) use ($eventUpdatedLanguage) { + $event->setUpdatedLanguage($eventUpdatedLanguage); + }, 10); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateLanguageName(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUpdatedLanguage, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateLanguageNameEvent::class, 10], + [BeforeUpdateLanguageNameEvent::class, 0], + [UpdateLanguageNameEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateLanguageNameStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateLanguageNameEvent::class, + UpdateLanguageNameEvent::class + ); + + $parameters = [ + $this->createMock(Language::class), + 'random_value_5cff79c3161386.01414999', + ]; + + $updatedLanguage = $this->createMock(Language::class); + $eventUpdatedLanguage = $this->createMock(Language::class); + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + $innerServiceMock->method('updateLanguageName')->willReturn($updatedLanguage); + + $traceableEventDispatcher->addListener(BeforeUpdateLanguageNameEvent::class, static function (BeforeUpdateLanguageNameEvent $event) use ($eventUpdatedLanguage) { + $event->setUpdatedLanguage($eventUpdatedLanguage); + $event->stopPropagation(); + }, 10); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateLanguageName(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUpdatedLanguage, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateLanguageNameEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateLanguageNameEvent::class, 0], + [UpdateLanguageNameEvent::class, 0], + ]); + } + + public function testDisableLanguageEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDisableLanguageEvent::class, + DisableLanguageEvent::class + ); + + $parameters = [ + $this->createMock(Language::class), + ]; + + $disabledLanguage = $this->createMock(Language::class); + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + $innerServiceMock->method('disableLanguage')->willReturn($disabledLanguage); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $result = $service->disableLanguage(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($disabledLanguage, $result); + $this->assertSame($calledListeners, [ + [BeforeDisableLanguageEvent::class, 0], + [DisableLanguageEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnDisableLanguageResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDisableLanguageEvent::class, + DisableLanguageEvent::class + ); + + $parameters = [ + $this->createMock(Language::class), + ]; + + $disabledLanguage = $this->createMock(Language::class); + $eventDisabledLanguage = $this->createMock(Language::class); + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + $innerServiceMock->method('disableLanguage')->willReturn($disabledLanguage); + + $traceableEventDispatcher->addListener(BeforeDisableLanguageEvent::class, static function (BeforeDisableLanguageEvent $event) use ($eventDisabledLanguage) { + $event->setDisabledLanguage($eventDisabledLanguage); + }, 10); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $result = $service->disableLanguage(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventDisabledLanguage, $result); + $this->assertSame($calledListeners, [ + [BeforeDisableLanguageEvent::class, 10], + [BeforeDisableLanguageEvent::class, 0], + [DisableLanguageEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDisableLanguageStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDisableLanguageEvent::class, + DisableLanguageEvent::class + ); + + $parameters = [ + $this->createMock(Language::class), + ]; + + $disabledLanguage = $this->createMock(Language::class); + $eventDisabledLanguage = $this->createMock(Language::class); + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + $innerServiceMock->method('disableLanguage')->willReturn($disabledLanguage); + + $traceableEventDispatcher->addListener(BeforeDisableLanguageEvent::class, static function (BeforeDisableLanguageEvent $event) use ($eventDisabledLanguage) { + $event->setDisabledLanguage($eventDisabledLanguage); + $event->stopPropagation(); + }, 10); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $result = $service->disableLanguage(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventDisabledLanguage, $result); + $this->assertSame($calledListeners, [ + [BeforeDisableLanguageEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDisableLanguageEvent::class, 0], + [DisableLanguageEvent::class, 0], + ]); + } + + public function testEnableLanguageEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeEnableLanguageEvent::class, + EnableLanguageEvent::class + ); + + $parameters = [ + $this->createMock(Language::class), + ]; + + $enabledLanguage = $this->createMock(Language::class); + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + $innerServiceMock->method('enableLanguage')->willReturn($enabledLanguage); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $result = $service->enableLanguage(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($enabledLanguage, $result); + $this->assertSame($calledListeners, [ + [BeforeEnableLanguageEvent::class, 0], + [EnableLanguageEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnEnableLanguageResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeEnableLanguageEvent::class, + EnableLanguageEvent::class + ); + + $parameters = [ + $this->createMock(Language::class), + ]; + + $enabledLanguage = $this->createMock(Language::class); + $eventEnabledLanguage = $this->createMock(Language::class); + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + $innerServiceMock->method('enableLanguage')->willReturn($enabledLanguage); + + $traceableEventDispatcher->addListener(BeforeEnableLanguageEvent::class, static function (BeforeEnableLanguageEvent $event) use ($eventEnabledLanguage) { + $event->setEnabledLanguage($eventEnabledLanguage); + }, 10); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $result = $service->enableLanguage(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventEnabledLanguage, $result); + $this->assertSame($calledListeners, [ + [BeforeEnableLanguageEvent::class, 10], + [BeforeEnableLanguageEvent::class, 0], + [EnableLanguageEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testEnableLanguageStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeEnableLanguageEvent::class, + EnableLanguageEvent::class + ); + + $parameters = [ + $this->createMock(Language::class), + ]; + + $enabledLanguage = $this->createMock(Language::class); + $eventEnabledLanguage = $this->createMock(Language::class); + $innerServiceMock = $this->createMock(LanguageServiceInterface::class); + $innerServiceMock->method('enableLanguage')->willReturn($enabledLanguage); + + $traceableEventDispatcher->addListener(BeforeEnableLanguageEvent::class, static function (BeforeEnableLanguageEvent $event) use ($eventEnabledLanguage) { + $event->setEnabledLanguage($eventEnabledLanguage); + $event->stopPropagation(); + }, 10); + + $service = new LanguageService($innerServiceMock, $traceableEventDispatcher); + $result = $service->enableLanguage(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventEnabledLanguage, $result); + $this->assertSame($calledListeners, [ + [BeforeEnableLanguageEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeEnableLanguageEvent::class, 0], + [EnableLanguageEvent::class, 0], + ]); + } +} + +class_alias(LanguageServiceTest::class, 'eZ\Publish\Core\Event\Tests\LanguageServiceTest'); diff --git a/tests/lib/Event/LocationServiceTest.php b/tests/lib/Event/LocationServiceTest.php new file mode 100644 index 0000000000..277fbe6b19 --- /dev/null +++ b/tests/lib/Event/LocationServiceTest.php @@ -0,0 +1,714 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Events\Location\BeforeCopySubtreeEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\BeforeCreateLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\BeforeDeleteLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\BeforeHideLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\BeforeMoveSubtreeEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\BeforeSwapLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\BeforeUnhideLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\BeforeUpdateLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\CopySubtreeEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\CreateLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\DeleteLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\HideLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\MoveSubtreeEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\SwapLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\UnhideLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\Location\UpdateLocationEvent; +use Ibexa\Contracts\Core\Repository\LocationService as LocationServiceInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationUpdateStruct; +use Ibexa\Core\Event\LocationService; + +class LocationServiceTest extends AbstractServiceTest +{ + public function testCopySubtreeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCopySubtreeEvent::class, + CopySubtreeEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + $this->createMock(Location::class), + ]; + + $location = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('copySubtree')->willReturn($location); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->copySubtree(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($location, $result); + $this->assertSame($calledListeners, [ + [BeforeCopySubtreeEvent::class, 0], + [CopySubtreeEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCopySubtreeResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCopySubtreeEvent::class, + CopySubtreeEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + $this->createMock(Location::class), + ]; + + $location = $this->createMock(Location::class); + $eventLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('copySubtree')->willReturn($location); + + $traceableEventDispatcher->addListener(BeforeCopySubtreeEvent::class, static function (BeforeCopySubtreeEvent $event) use ($eventLocation) { + $event->setLocation($eventLocation); + }, 10); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->copySubtree(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeCopySubtreeEvent::class, 10], + [BeforeCopySubtreeEvent::class, 0], + [CopySubtreeEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCopySubtreeStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCopySubtreeEvent::class, + CopySubtreeEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + $this->createMock(Location::class), + ]; + + $location = $this->createMock(Location::class); + $eventLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('copySubtree')->willReturn($location); + + $traceableEventDispatcher->addListener(BeforeCopySubtreeEvent::class, static function (BeforeCopySubtreeEvent $event) use ($eventLocation) { + $event->setLocation($eventLocation); + $event->stopPropagation(); + }, 10); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->copySubtree(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeCopySubtreeEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCopySubtreeEvent::class, 0], + [CopySubtreeEvent::class, 0], + ]); + } + + public function testDeleteLocationEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteLocationEvent::class, + DeleteLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $service->deleteLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteLocationEvent::class, 0], + [DeleteLocationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteLocationStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteLocationEvent::class, + DeleteLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteLocationEvent::class, static function (BeforeDeleteLocationEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $service->deleteLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteLocationEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteLocationEvent::class, 0], + [DeleteLocationEvent::class, 0], + ]); + } + + public function testUnhideLocationEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUnhideLocationEvent::class, + UnhideLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $revealedLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('unhideLocation')->willReturn($revealedLocation); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->unhideLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($revealedLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeUnhideLocationEvent::class, 0], + [UnhideLocationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUnhideLocationResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUnhideLocationEvent::class, + UnhideLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $revealedLocation = $this->createMock(Location::class); + $eventRevealedLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('unhideLocation')->willReturn($revealedLocation); + + $traceableEventDispatcher->addListener(BeforeUnhideLocationEvent::class, static function (BeforeUnhideLocationEvent $event) use ($eventRevealedLocation) { + $event->setRevealedLocation($eventRevealedLocation); + }, 10); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->unhideLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventRevealedLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeUnhideLocationEvent::class, 10], + [BeforeUnhideLocationEvent::class, 0], + [UnhideLocationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUnhideLocationStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUnhideLocationEvent::class, + UnhideLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $revealedLocation = $this->createMock(Location::class); + $eventRevealedLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('unhideLocation')->willReturn($revealedLocation); + + $traceableEventDispatcher->addListener(BeforeUnhideLocationEvent::class, static function (BeforeUnhideLocationEvent $event) use ($eventRevealedLocation) { + $event->setRevealedLocation($eventRevealedLocation); + $event->stopPropagation(); + }, 10); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->unhideLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventRevealedLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeUnhideLocationEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUnhideLocationEvent::class, 0], + [UnhideLocationEvent::class, 0], + ]); + } + + public function testHideLocationEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeHideLocationEvent::class, + HideLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $hiddenLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('hideLocation')->willReturn($hiddenLocation); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->hideLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($hiddenLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeHideLocationEvent::class, 0], + [HideLocationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnHideLocationResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeHideLocationEvent::class, + HideLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $hiddenLocation = $this->createMock(Location::class); + $eventHiddenLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('hideLocation')->willReturn($hiddenLocation); + + $traceableEventDispatcher->addListener(BeforeHideLocationEvent::class, static function (BeforeHideLocationEvent $event) use ($eventHiddenLocation) { + $event->setHiddenLocation($eventHiddenLocation); + }, 10); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->hideLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventHiddenLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeHideLocationEvent::class, 10], + [BeforeHideLocationEvent::class, 0], + [HideLocationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testHideLocationStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeHideLocationEvent::class, + HideLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $hiddenLocation = $this->createMock(Location::class); + $eventHiddenLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('hideLocation')->willReturn($hiddenLocation); + + $traceableEventDispatcher->addListener(BeforeHideLocationEvent::class, static function (BeforeHideLocationEvent $event) use ($eventHiddenLocation) { + $event->setHiddenLocation($eventHiddenLocation); + $event->stopPropagation(); + }, 10); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->hideLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventHiddenLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeHideLocationEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeHideLocationEvent::class, 0], + [HideLocationEvent::class, 0], + ]); + } + + public function testSwapLocationEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeSwapLocationEvent::class, + SwapLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + $this->createMock(Location::class), + ]; + + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $service->swapLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeSwapLocationEvent::class, 0], + [SwapLocationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testSwapLocationStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeSwapLocationEvent::class, + SwapLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + $this->createMock(Location::class), + ]; + + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeSwapLocationEvent::class, static function (BeforeSwapLocationEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $service->swapLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeSwapLocationEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeSwapLocationEvent::class, 0], + [SwapLocationEvent::class, 0], + ]); + } + + public function testMoveSubtreeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeMoveSubtreeEvent::class, + MoveSubtreeEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + $this->createMock(Location::class), + ]; + + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $service->moveSubtree(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeMoveSubtreeEvent::class, 0], + [MoveSubtreeEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testMoveSubtreeStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeMoveSubtreeEvent::class, + MoveSubtreeEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + $this->createMock(Location::class), + ]; + + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeMoveSubtreeEvent::class, static function (BeforeMoveSubtreeEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $service->moveSubtree(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeMoveSubtreeEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeMoveSubtreeEvent::class, 0], + [MoveSubtreeEvent::class, 0], + ]); + } + + public function testUpdateLocationEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateLocationEvent::class, + UpdateLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + $this->createMock(LocationUpdateStruct::class), + ]; + + $updatedLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('updateLocation')->willReturn($updatedLocation); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($updatedLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateLocationEvent::class, 0], + [UpdateLocationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdateLocationResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateLocationEvent::class, + UpdateLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + $this->createMock(LocationUpdateStruct::class), + ]; + + $updatedLocation = $this->createMock(Location::class); + $eventUpdatedLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('updateLocation')->willReturn($updatedLocation); + + $traceableEventDispatcher->addListener(BeforeUpdateLocationEvent::class, static function (BeforeUpdateLocationEvent $event) use ($eventUpdatedLocation) { + $event->setUpdatedLocation($eventUpdatedLocation); + }, 10); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUpdatedLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateLocationEvent::class, 10], + [BeforeUpdateLocationEvent::class, 0], + [UpdateLocationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateLocationStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateLocationEvent::class, + UpdateLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + $this->createMock(LocationUpdateStruct::class), + ]; + + $updatedLocation = $this->createMock(Location::class); + $eventUpdatedLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('updateLocation')->willReturn($updatedLocation); + + $traceableEventDispatcher->addListener(BeforeUpdateLocationEvent::class, static function (BeforeUpdateLocationEvent $event) use ($eventUpdatedLocation) { + $event->setUpdatedLocation($eventUpdatedLocation); + $event->stopPropagation(); + }, 10); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUpdatedLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateLocationEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateLocationEvent::class, 0], + [UpdateLocationEvent::class, 0], + ]); + } + + public function testCreateLocationEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateLocationEvent::class, + CreateLocationEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(LocationCreateStruct::class), + ]; + + $location = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('createLocation')->willReturn($location); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($location, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateLocationEvent::class, 0], + [CreateLocationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateLocationResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateLocationEvent::class, + CreateLocationEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(LocationCreateStruct::class), + ]; + + $location = $this->createMock(Location::class); + $eventLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('createLocation')->willReturn($location); + + $traceableEventDispatcher->addListener(BeforeCreateLocationEvent::class, static function (BeforeCreateLocationEvent $event) use ($eventLocation) { + $event->setLocation($eventLocation); + }, 10); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateLocationEvent::class, 10], + [BeforeCreateLocationEvent::class, 0], + [CreateLocationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateLocationStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateLocationEvent::class, + CreateLocationEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(LocationCreateStruct::class), + ]; + + $location = $this->createMock(Location::class); + $eventLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(LocationServiceInterface::class); + $innerServiceMock->method('createLocation')->willReturn($location); + + $traceableEventDispatcher->addListener(BeforeCreateLocationEvent::class, static function (BeforeCreateLocationEvent $event) use ($eventLocation) { + $event->setLocation($eventLocation); + $event->stopPropagation(); + }, 10); + + $service = new LocationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateLocationEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateLocationEvent::class, 0], + [CreateLocationEvent::class, 0], + ]); + } +} + +class_alias(LocationServiceTest::class, 'eZ\Publish\Core\Event\Tests\LocationServiceTest'); diff --git a/tests/lib/Event/NotificationServiceTest.php b/tests/lib/Event/NotificationServiceTest.php new file mode 100644 index 0000000000..eb032d16a6 --- /dev/null +++ b/tests/lib/Event/NotificationServiceTest.php @@ -0,0 +1,236 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Events\Notification\BeforeCreateNotificationEvent; +use Ibexa\Contracts\Core\Repository\Events\Notification\BeforeDeleteNotificationEvent; +use Ibexa\Contracts\Core\Repository\Events\Notification\BeforeMarkNotificationAsReadEvent; +use Ibexa\Contracts\Core\Repository\Events\Notification\CreateNotificationEvent; +use Ibexa\Contracts\Core\Repository\Events\Notification\DeleteNotificationEvent; +use Ibexa\Contracts\Core\Repository\Events\Notification\MarkNotificationAsReadEvent; +use Ibexa\Contracts\Core\Repository\NotificationService as NotificationServiceInterface; +use Ibexa\Contracts\Core\Repository\Values\Notification\CreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Notification\Notification; +use Ibexa\Core\Event\NotificationService; + +class NotificationServiceTest extends AbstractServiceTest +{ + public function testCreateNotificationEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateNotificationEvent::class, + CreateNotificationEvent::class + ); + + $parameters = [ + $this->createMock(CreateStruct::class), + ]; + + $notification = $this->createMock(Notification::class); + $innerServiceMock = $this->createMock(NotificationServiceInterface::class); + $innerServiceMock->method('createNotification')->willReturn($notification); + + $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createNotification(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($notification, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateNotificationEvent::class, 0], + [CreateNotificationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateNotificationResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateNotificationEvent::class, + CreateNotificationEvent::class + ); + + $parameters = [ + $this->createMock(CreateStruct::class), + ]; + + $notification = $this->createMock(Notification::class); + $eventNotification = $this->createMock(Notification::class); + $innerServiceMock = $this->createMock(NotificationServiceInterface::class); + $innerServiceMock->method('createNotification')->willReturn($notification); + + $traceableEventDispatcher->addListener(BeforeCreateNotificationEvent::class, static function (BeforeCreateNotificationEvent $event) use ($eventNotification) { + $event->setNotification($eventNotification); + }, 10); + + $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createNotification(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventNotification, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateNotificationEvent::class, 10], + [BeforeCreateNotificationEvent::class, 0], + [CreateNotificationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateNotificationStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateNotificationEvent::class, + CreateNotificationEvent::class + ); + + $parameters = [ + $this->createMock(CreateStruct::class), + ]; + + $notification = $this->createMock(Notification::class); + $eventNotification = $this->createMock(Notification::class); + $innerServiceMock = $this->createMock(NotificationServiceInterface::class); + $innerServiceMock->method('createNotification')->willReturn($notification); + + $traceableEventDispatcher->addListener(BeforeCreateNotificationEvent::class, static function (BeforeCreateNotificationEvent $event) use ($eventNotification) { + $event->setNotification($eventNotification); + $event->stopPropagation(); + }, 10); + + $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createNotification(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventNotification, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateNotificationEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateNotificationEvent::class, 0], + [CreateNotificationEvent::class, 0], + ]); + } + + public function testDeleteNotificationEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteNotificationEvent::class, + DeleteNotificationEvent::class + ); + + $parameters = [ + $this->createMock(Notification::class), + ]; + + $innerServiceMock = $this->createMock(NotificationServiceInterface::class); + + $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); + $service->deleteNotification(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteNotificationEvent::class, 0], + [DeleteNotificationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteNotificationStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteNotificationEvent::class, + DeleteNotificationEvent::class + ); + + $parameters = [ + $this->createMock(Notification::class), + ]; + + $innerServiceMock = $this->createMock(NotificationServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteNotificationEvent::class, static function (BeforeDeleteNotificationEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); + $service->deleteNotification(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteNotificationEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteNotificationEvent::class, 0], + [DeleteNotificationEvent::class, 0], + ]); + } + + public function testMarkNotificationAsReadEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeMarkNotificationAsReadEvent::class, + MarkNotificationAsReadEvent::class + ); + + $parameters = [ + $this->createMock(Notification::class), + ]; + + $innerServiceMock = $this->createMock(NotificationServiceInterface::class); + + $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); + $service->markNotificationAsRead(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeMarkNotificationAsReadEvent::class, 0], + [MarkNotificationAsReadEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testMarkNotificationAsReadStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeMarkNotificationAsReadEvent::class, + MarkNotificationAsReadEvent::class + ); + + $parameters = [ + $this->createMock(Notification::class), + ]; + + $innerServiceMock = $this->createMock(NotificationServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeMarkNotificationAsReadEvent::class, static function (BeforeMarkNotificationAsReadEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new NotificationService($innerServiceMock, $traceableEventDispatcher); + $service->markNotificationAsRead(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeMarkNotificationAsReadEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeMarkNotificationAsReadEvent::class, 0], + [MarkNotificationAsReadEvent::class, 0], + ]); + } +} + +class_alias(NotificationServiceTest::class, 'eZ\Publish\Core\Event\Tests\NotificationServiceTest'); diff --git a/tests/lib/Event/ObjectStateServiceTest.php b/tests/lib/Event/ObjectStateServiceTest.php new file mode 100644 index 0000000000..3efb09df1d --- /dev/null +++ b/tests/lib/Event/ObjectStateServiceTest.php @@ -0,0 +1,677 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Events\ObjectState\BeforeCreateObjectStateEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\BeforeCreateObjectStateGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\BeforeDeleteObjectStateEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\BeforeDeleteObjectStateGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\BeforeSetContentStateEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\BeforeSetPriorityOfObjectStateEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\BeforeUpdateObjectStateEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\BeforeUpdateObjectStateGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\CreateObjectStateEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\CreateObjectStateGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\DeleteObjectStateEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\DeleteObjectStateGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\SetContentStateEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\SetPriorityOfObjectStateEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\UpdateObjectStateEvent; +use Ibexa\Contracts\Core\Repository\Events\ObjectState\UpdateObjectStateGroupEvent; +use Ibexa\Contracts\Core\Repository\ObjectStateService as ObjectStateServiceInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateUpdateStruct; +use Ibexa\Core\Event\ObjectStateService; + +class ObjectStateServiceTest extends AbstractServiceTest +{ + public function testSetContentStateEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeSetContentStateEvent::class, + SetContentStateEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(ObjectStateGroup::class), + $this->createMock(ObjectState::class), + ]; + + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $service->setContentState(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeSetContentStateEvent::class, 0], + [SetContentStateEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testSetContentStateStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeSetContentStateEvent::class, + SetContentStateEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(ObjectStateGroup::class), + $this->createMock(ObjectState::class), + ]; + + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeSetContentStateEvent::class, static function (BeforeSetContentStateEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $service->setContentState(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeSetContentStateEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeSetContentStateEvent::class, 0], + [SetContentStateEvent::class, 0], + ]); + } + + public function testCreateObjectStateGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateObjectStateGroupEvent::class, + CreateObjectStateGroupEvent::class + ); + + $parameters = [ + $this->createMock(ObjectStateGroupCreateStruct::class), + ]; + + $objectStateGroup = $this->createMock(ObjectStateGroup::class); + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + $innerServiceMock->method('createObjectStateGroup')->willReturn($objectStateGroup); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createObjectStateGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($objectStateGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateObjectStateGroupEvent::class, 0], + [CreateObjectStateGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateObjectStateGroupResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateObjectStateGroupEvent::class, + CreateObjectStateGroupEvent::class + ); + + $parameters = [ + $this->createMock(ObjectStateGroupCreateStruct::class), + ]; + + $objectStateGroup = $this->createMock(ObjectStateGroup::class); + $eventObjectStateGroup = $this->createMock(ObjectStateGroup::class); + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + $innerServiceMock->method('createObjectStateGroup')->willReturn($objectStateGroup); + + $traceableEventDispatcher->addListener(BeforeCreateObjectStateGroupEvent::class, static function (BeforeCreateObjectStateGroupEvent $event) use ($eventObjectStateGroup) { + $event->setObjectStateGroup($eventObjectStateGroup); + }, 10); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createObjectStateGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventObjectStateGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateObjectStateGroupEvent::class, 10], + [BeforeCreateObjectStateGroupEvent::class, 0], + [CreateObjectStateGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateObjectStateGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateObjectStateGroupEvent::class, + CreateObjectStateGroupEvent::class + ); + + $parameters = [ + $this->createMock(ObjectStateGroupCreateStruct::class), + ]; + + $objectStateGroup = $this->createMock(ObjectStateGroup::class); + $eventObjectStateGroup = $this->createMock(ObjectStateGroup::class); + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + $innerServiceMock->method('createObjectStateGroup')->willReturn($objectStateGroup); + + $traceableEventDispatcher->addListener(BeforeCreateObjectStateGroupEvent::class, static function (BeforeCreateObjectStateGroupEvent $event) use ($eventObjectStateGroup) { + $event->setObjectStateGroup($eventObjectStateGroup); + $event->stopPropagation(); + }, 10); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createObjectStateGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventObjectStateGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateObjectStateGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateObjectStateGroupEvent::class, 0], + [CreateObjectStateGroupEvent::class, 0], + ]); + } + + public function testUpdateObjectStateEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateObjectStateEvent::class, + UpdateObjectStateEvent::class + ); + + $parameters = [ + $this->createMock(ObjectState::class), + $this->createMock(ObjectStateUpdateStruct::class), + ]; + + $updatedObjectState = $this->createMock(ObjectState::class); + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + $innerServiceMock->method('updateObjectState')->willReturn($updatedObjectState); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateObjectState(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($updatedObjectState, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateObjectStateEvent::class, 0], + [UpdateObjectStateEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdateObjectStateResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateObjectStateEvent::class, + UpdateObjectStateEvent::class + ); + + $parameters = [ + $this->createMock(ObjectState::class), + $this->createMock(ObjectStateUpdateStruct::class), + ]; + + $updatedObjectState = $this->createMock(ObjectState::class); + $eventUpdatedObjectState = $this->createMock(ObjectState::class); + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + $innerServiceMock->method('updateObjectState')->willReturn($updatedObjectState); + + $traceableEventDispatcher->addListener(BeforeUpdateObjectStateEvent::class, static function (BeforeUpdateObjectStateEvent $event) use ($eventUpdatedObjectState) { + $event->setUpdatedObjectState($eventUpdatedObjectState); + }, 10); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateObjectState(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUpdatedObjectState, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateObjectStateEvent::class, 10], + [BeforeUpdateObjectStateEvent::class, 0], + [UpdateObjectStateEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateObjectStateStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateObjectStateEvent::class, + UpdateObjectStateEvent::class + ); + + $parameters = [ + $this->createMock(ObjectState::class), + $this->createMock(ObjectStateUpdateStruct::class), + ]; + + $updatedObjectState = $this->createMock(ObjectState::class); + $eventUpdatedObjectState = $this->createMock(ObjectState::class); + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + $innerServiceMock->method('updateObjectState')->willReturn($updatedObjectState); + + $traceableEventDispatcher->addListener(BeforeUpdateObjectStateEvent::class, static function (BeforeUpdateObjectStateEvent $event) use ($eventUpdatedObjectState) { + $event->setUpdatedObjectState($eventUpdatedObjectState); + $event->stopPropagation(); + }, 10); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateObjectState(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUpdatedObjectState, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateObjectStateEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateObjectStateEvent::class, 0], + [UpdateObjectStateEvent::class, 0], + ]); + } + + public function testCreateObjectStateEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateObjectStateEvent::class, + CreateObjectStateEvent::class + ); + + $parameters = [ + $this->createMock(ObjectStateGroup::class), + $this->createMock(ObjectStateCreateStruct::class), + ]; + + $objectState = $this->createMock(ObjectState::class); + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + $innerServiceMock->method('createObjectState')->willReturn($objectState); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createObjectState(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($objectState, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateObjectStateEvent::class, 0], + [CreateObjectStateEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateObjectStateResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateObjectStateEvent::class, + CreateObjectStateEvent::class + ); + + $parameters = [ + $this->createMock(ObjectStateGroup::class), + $this->createMock(ObjectStateCreateStruct::class), + ]; + + $objectState = $this->createMock(ObjectState::class); + $eventObjectState = $this->createMock(ObjectState::class); + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + $innerServiceMock->method('createObjectState')->willReturn($objectState); + + $traceableEventDispatcher->addListener(BeforeCreateObjectStateEvent::class, static function (BeforeCreateObjectStateEvent $event) use ($eventObjectState) { + $event->setObjectState($eventObjectState); + }, 10); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createObjectState(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventObjectState, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateObjectStateEvent::class, 10], + [BeforeCreateObjectStateEvent::class, 0], + [CreateObjectStateEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateObjectStateStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateObjectStateEvent::class, + CreateObjectStateEvent::class + ); + + $parameters = [ + $this->createMock(ObjectStateGroup::class), + $this->createMock(ObjectStateCreateStruct::class), + ]; + + $objectState = $this->createMock(ObjectState::class); + $eventObjectState = $this->createMock(ObjectState::class); + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + $innerServiceMock->method('createObjectState')->willReturn($objectState); + + $traceableEventDispatcher->addListener(BeforeCreateObjectStateEvent::class, static function (BeforeCreateObjectStateEvent $event) use ($eventObjectState) { + $event->setObjectState($eventObjectState); + $event->stopPropagation(); + }, 10); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createObjectState(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventObjectState, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateObjectStateEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateObjectStateEvent::class, 0], + [CreateObjectStateEvent::class, 0], + ]); + } + + public function testUpdateObjectStateGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateObjectStateGroupEvent::class, + UpdateObjectStateGroupEvent::class + ); + + $parameters = [ + $this->createMock(ObjectStateGroup::class), + $this->createMock(ObjectStateGroupUpdateStruct::class), + ]; + + $updatedObjectStateGroup = $this->createMock(ObjectStateGroup::class); + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + $innerServiceMock->method('updateObjectStateGroup')->willReturn($updatedObjectStateGroup); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateObjectStateGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($updatedObjectStateGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateObjectStateGroupEvent::class, 0], + [UpdateObjectStateGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdateObjectStateGroupResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateObjectStateGroupEvent::class, + UpdateObjectStateGroupEvent::class + ); + + $parameters = [ + $this->createMock(ObjectStateGroup::class), + $this->createMock(ObjectStateGroupUpdateStruct::class), + ]; + + $updatedObjectStateGroup = $this->createMock(ObjectStateGroup::class); + $eventUpdatedObjectStateGroup = $this->createMock(ObjectStateGroup::class); + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + $innerServiceMock->method('updateObjectStateGroup')->willReturn($updatedObjectStateGroup); + + $traceableEventDispatcher->addListener(BeforeUpdateObjectStateGroupEvent::class, static function (BeforeUpdateObjectStateGroupEvent $event) use ($eventUpdatedObjectStateGroup) { + $event->setUpdatedObjectStateGroup($eventUpdatedObjectStateGroup); + }, 10); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateObjectStateGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUpdatedObjectStateGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateObjectStateGroupEvent::class, 10], + [BeforeUpdateObjectStateGroupEvent::class, 0], + [UpdateObjectStateGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateObjectStateGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateObjectStateGroupEvent::class, + UpdateObjectStateGroupEvent::class + ); + + $parameters = [ + $this->createMock(ObjectStateGroup::class), + $this->createMock(ObjectStateGroupUpdateStruct::class), + ]; + + $updatedObjectStateGroup = $this->createMock(ObjectStateGroup::class); + $eventUpdatedObjectStateGroup = $this->createMock(ObjectStateGroup::class); + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + $innerServiceMock->method('updateObjectStateGroup')->willReturn($updatedObjectStateGroup); + + $traceableEventDispatcher->addListener(BeforeUpdateObjectStateGroupEvent::class, static function (BeforeUpdateObjectStateGroupEvent $event) use ($eventUpdatedObjectStateGroup) { + $event->setUpdatedObjectStateGroup($eventUpdatedObjectStateGroup); + $event->stopPropagation(); + }, 10); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateObjectStateGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUpdatedObjectStateGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateObjectStateGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateObjectStateGroupEvent::class, 0], + [UpdateObjectStateGroupEvent::class, 0], + ]); + } + + public function testSetPriorityOfObjectStateEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeSetPriorityOfObjectStateEvent::class, + SetPriorityOfObjectStateEvent::class + ); + + $parameters = [ + $this->createMock(ObjectState::class), + 100, + ]; + + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $service->setPriorityOfObjectState(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeSetPriorityOfObjectStateEvent::class, 0], + [SetPriorityOfObjectStateEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testSetPriorityOfObjectStateStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeSetPriorityOfObjectStateEvent::class, + SetPriorityOfObjectStateEvent::class + ); + + $parameters = [ + $this->createMock(ObjectState::class), + 100, + ]; + + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeSetPriorityOfObjectStateEvent::class, static function (BeforeSetPriorityOfObjectStateEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $service->setPriorityOfObjectState(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeSetPriorityOfObjectStateEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeSetPriorityOfObjectStateEvent::class, 0], + [SetPriorityOfObjectStateEvent::class, 0], + ]); + } + + public function testDeleteObjectStateGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteObjectStateGroupEvent::class, + DeleteObjectStateGroupEvent::class + ); + + $parameters = [ + $this->createMock(ObjectStateGroup::class), + ]; + + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $service->deleteObjectStateGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteObjectStateGroupEvent::class, 0], + [DeleteObjectStateGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteObjectStateGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteObjectStateGroupEvent::class, + DeleteObjectStateGroupEvent::class + ); + + $parameters = [ + $this->createMock(ObjectStateGroup::class), + ]; + + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteObjectStateGroupEvent::class, static function (BeforeDeleteObjectStateGroupEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $service->deleteObjectStateGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteObjectStateGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteObjectStateGroupEvent::class, 0], + [DeleteObjectStateGroupEvent::class, 0], + ]); + } + + public function testDeleteObjectStateEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteObjectStateEvent::class, + DeleteObjectStateEvent::class + ); + + $parameters = [ + $this->createMock(ObjectState::class), + ]; + + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $service->deleteObjectState(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteObjectStateEvent::class, 0], + [DeleteObjectStateEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteObjectStateStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteObjectStateEvent::class, + DeleteObjectStateEvent::class + ); + + $parameters = [ + $this->createMock(ObjectState::class), + ]; + + $innerServiceMock = $this->createMock(ObjectStateServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteObjectStateEvent::class, static function (BeforeDeleteObjectStateEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new ObjectStateService($innerServiceMock, $traceableEventDispatcher); + $service->deleteObjectState(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteObjectStateEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteObjectStateEvent::class, 0], + [DeleteObjectStateEvent::class, 0], + ]); + } +} + +class_alias(ObjectStateServiceTest::class, 'eZ\Publish\Core\Event\Tests\ObjectStateServiceTest'); diff --git a/tests/lib/Event/RoleServiceTest.php b/tests/lib/Event/RoleServiceTest.php new file mode 100644 index 0000000000..a4a33e1439 --- /dev/null +++ b/tests/lib/Event/RoleServiceTest.php @@ -0,0 +1,1009 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Events\Role\AddPolicyByRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\AssignRoleToUserEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\AssignRoleToUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\BeforeAddPolicyByRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\BeforeAssignRoleToUserEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\BeforeAssignRoleToUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\BeforeCreateRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\BeforeCreateRoleEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\BeforeDeleteRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\BeforeDeleteRoleEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\BeforePublishRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\BeforeRemovePolicyByRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\BeforeRemoveRoleAssignmentEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\BeforeUpdatePolicyByRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\BeforeUpdateRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\CreateRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\CreateRoleEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\DeleteRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\DeleteRoleEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\PublishRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\RemovePolicyByRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\RemoveRoleAssignmentEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\UpdatePolicyByRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\Events\Role\UpdateRoleDraftEvent; +use Ibexa\Contracts\Core\Repository\RoleService as RoleServiceInterface; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\Role; +use Ibexa\Contracts\Core\Repository\Values\User\RoleAssignment; +use Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\RoleDraft; +use Ibexa\Contracts\Core\Repository\Values\User\RoleUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroup; +use Ibexa\Core\Event\RoleService; + +class RoleServiceTest extends AbstractServiceTest +{ + public function testPublishRoleDraftEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforePublishRoleDraftEvent::class, + PublishRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + ]; + + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $service->publishRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforePublishRoleDraftEvent::class, 0], + [PublishRoleDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testPublishRoleDraftStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforePublishRoleDraftEvent::class, + PublishRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + ]; + + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforePublishRoleDraftEvent::class, static function (BeforePublishRoleDraftEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $service->publishRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforePublishRoleDraftEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforePublishRoleDraftEvent::class, 0], + [PublishRoleDraftEvent::class, 0], + ]); + } + + public function testAssignRoleToUserEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAssignRoleToUserEvent::class, + AssignRoleToUserEvent::class + ); + + $parameters = [ + $this->createMock(Role::class), + $this->createMock(User::class), + $this->createMock(RoleLimitation::class), + ]; + + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $service->assignRoleToUser(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAssignRoleToUserEvent::class, 0], + [AssignRoleToUserEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testAssignRoleToUserStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAssignRoleToUserEvent::class, + AssignRoleToUserEvent::class + ); + + $parameters = [ + $this->createMock(Role::class), + $this->createMock(User::class), + $this->createMock(RoleLimitation::class), + ]; + + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeAssignRoleToUserEvent::class, static function (BeforeAssignRoleToUserEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $service->assignRoleToUser(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAssignRoleToUserEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [AssignRoleToUserEvent::class, 0], + [BeforeAssignRoleToUserEvent::class, 0], + ]); + } + + public function testUpdateRoleDraftEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateRoleDraftEvent::class, + UpdateRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + $this->createMock(RoleUpdateStruct::class), + ]; + + $updatedRoleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('updateRoleDraft')->willReturn($updatedRoleDraft); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($updatedRoleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateRoleDraftEvent::class, 0], + [UpdateRoleDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdateRoleDraftResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateRoleDraftEvent::class, + UpdateRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + $this->createMock(RoleUpdateStruct::class), + ]; + + $updatedRoleDraft = $this->createMock(RoleDraft::class); + $eventUpdatedRoleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('updateRoleDraft')->willReturn($updatedRoleDraft); + + $traceableEventDispatcher->addListener(BeforeUpdateRoleDraftEvent::class, static function (BeforeUpdateRoleDraftEvent $event) use ($eventUpdatedRoleDraft) { + $event->setUpdatedRoleDraft($eventUpdatedRoleDraft); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUpdatedRoleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateRoleDraftEvent::class, 10], + [BeforeUpdateRoleDraftEvent::class, 0], + [UpdateRoleDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateRoleDraftStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateRoleDraftEvent::class, + UpdateRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + $this->createMock(RoleUpdateStruct::class), + ]; + + $updatedRoleDraft = $this->createMock(RoleDraft::class); + $eventUpdatedRoleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('updateRoleDraft')->willReturn($updatedRoleDraft); + + $traceableEventDispatcher->addListener(BeforeUpdateRoleDraftEvent::class, static function (BeforeUpdateRoleDraftEvent $event) use ($eventUpdatedRoleDraft) { + $event->setUpdatedRoleDraft($eventUpdatedRoleDraft); + $event->stopPropagation(); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUpdatedRoleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateRoleDraftEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateRoleDraftEvent::class, 0], + [UpdateRoleDraftEvent::class, 0], + ]); + } + + public function testAssignRoleToUserGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAssignRoleToUserGroupEvent::class, + AssignRoleToUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(Role::class), + $this->createMock(UserGroup::class), + $this->createMock(RoleLimitation::class), + ]; + + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $service->assignRoleToUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAssignRoleToUserGroupEvent::class, 0], + [AssignRoleToUserGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testAssignRoleToUserGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAssignRoleToUserGroupEvent::class, + AssignRoleToUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(Role::class), + $this->createMock(UserGroup::class), + $this->createMock(RoleLimitation::class), + ]; + + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeAssignRoleToUserGroupEvent::class, static function (BeforeAssignRoleToUserGroupEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $service->assignRoleToUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAssignRoleToUserGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [AssignRoleToUserGroupEvent::class, 0], + [BeforeAssignRoleToUserGroupEvent::class, 0], + ]); + } + + public function testUpdatePolicyByRoleDraftEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdatePolicyByRoleDraftEvent::class, + UpdatePolicyByRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + $this->createMock(PolicyDraft::class), + $this->createMock(PolicyUpdateStruct::class), + ]; + + $updatedPolicyDraft = $this->createMock(PolicyDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('updatePolicyByRoleDraft')->willReturn($updatedPolicyDraft); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updatePolicyByRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($updatedPolicyDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdatePolicyByRoleDraftEvent::class, 0], + [UpdatePolicyByRoleDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdatePolicyByRoleDraftResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdatePolicyByRoleDraftEvent::class, + UpdatePolicyByRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + $this->createMock(PolicyDraft::class), + $this->createMock(PolicyUpdateStruct::class), + ]; + + $updatedPolicyDraft = $this->createMock(PolicyDraft::class); + $eventUpdatedPolicyDraft = $this->createMock(PolicyDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('updatePolicyByRoleDraft')->willReturn($updatedPolicyDraft); + + $traceableEventDispatcher->addListener(BeforeUpdatePolicyByRoleDraftEvent::class, static function (BeforeUpdatePolicyByRoleDraftEvent $event) use ($eventUpdatedPolicyDraft) { + $event->setUpdatedPolicyDraft($eventUpdatedPolicyDraft); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updatePolicyByRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUpdatedPolicyDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdatePolicyByRoleDraftEvent::class, 10], + [BeforeUpdatePolicyByRoleDraftEvent::class, 0], + [UpdatePolicyByRoleDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdatePolicyByRoleDraftStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdatePolicyByRoleDraftEvent::class, + UpdatePolicyByRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + $this->createMock(PolicyDraft::class), + $this->createMock(PolicyUpdateStruct::class), + ]; + + $updatedPolicyDraft = $this->createMock(PolicyDraft::class); + $eventUpdatedPolicyDraft = $this->createMock(PolicyDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('updatePolicyByRoleDraft')->willReturn($updatedPolicyDraft); + + $traceableEventDispatcher->addListener(BeforeUpdatePolicyByRoleDraftEvent::class, static function (BeforeUpdatePolicyByRoleDraftEvent $event) use ($eventUpdatedPolicyDraft) { + $event->setUpdatedPolicyDraft($eventUpdatedPolicyDraft); + $event->stopPropagation(); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updatePolicyByRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUpdatedPolicyDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdatePolicyByRoleDraftEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdatePolicyByRoleDraftEvent::class, 0], + [UpdatePolicyByRoleDraftEvent::class, 0], + ]); + } + + public function testCreateRoleEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateRoleEvent::class, + CreateRoleEvent::class + ); + + $parameters = [ + $this->createMock(RoleCreateStruct::class), + ]; + + $roleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('createRole')->willReturn($roleDraft); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createRole(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($roleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateRoleEvent::class, 0], + [CreateRoleEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateRoleResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateRoleEvent::class, + CreateRoleEvent::class + ); + + $parameters = [ + $this->createMock(RoleCreateStruct::class), + ]; + + $roleDraft = $this->createMock(RoleDraft::class); + $eventRoleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('createRole')->willReturn($roleDraft); + + $traceableEventDispatcher->addListener(BeforeCreateRoleEvent::class, static function (BeforeCreateRoleEvent $event) use ($eventRoleDraft) { + $event->setRoleDraft($eventRoleDraft); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createRole(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventRoleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateRoleEvent::class, 10], + [BeforeCreateRoleEvent::class, 0], + [CreateRoleEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateRoleStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateRoleEvent::class, + CreateRoleEvent::class + ); + + $parameters = [ + $this->createMock(RoleCreateStruct::class), + ]; + + $roleDraft = $this->createMock(RoleDraft::class); + $eventRoleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('createRole')->willReturn($roleDraft); + + $traceableEventDispatcher->addListener(BeforeCreateRoleEvent::class, static function (BeforeCreateRoleEvent $event) use ($eventRoleDraft) { + $event->setRoleDraft($eventRoleDraft); + $event->stopPropagation(); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createRole(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventRoleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateRoleEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateRoleEvent::class, 0], + [CreateRoleEvent::class, 0], + ]); + } + + public function testRemovePolicyByRoleDraftEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemovePolicyByRoleDraftEvent::class, + RemovePolicyByRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + $this->createMock(PolicyDraft::class), + ]; + + $updatedRoleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('removePolicyByRoleDraft')->willReturn($updatedRoleDraft); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->removePolicyByRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($updatedRoleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeRemovePolicyByRoleDraftEvent::class, 0], + [RemovePolicyByRoleDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnRemovePolicyByRoleDraftResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemovePolicyByRoleDraftEvent::class, + RemovePolicyByRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + $this->createMock(PolicyDraft::class), + ]; + + $updatedRoleDraft = $this->createMock(RoleDraft::class); + $eventUpdatedRoleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('removePolicyByRoleDraft')->willReturn($updatedRoleDraft); + + $traceableEventDispatcher->addListener(BeforeRemovePolicyByRoleDraftEvent::class, static function (BeforeRemovePolicyByRoleDraftEvent $event) use ($eventUpdatedRoleDraft) { + $event->setUpdatedRoleDraft($eventUpdatedRoleDraft); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->removePolicyByRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUpdatedRoleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeRemovePolicyByRoleDraftEvent::class, 10], + [BeforeRemovePolicyByRoleDraftEvent::class, 0], + [RemovePolicyByRoleDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testRemovePolicyByRoleDraftStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemovePolicyByRoleDraftEvent::class, + RemovePolicyByRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + $this->createMock(PolicyDraft::class), + ]; + + $updatedRoleDraft = $this->createMock(RoleDraft::class); + $eventUpdatedRoleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('removePolicyByRoleDraft')->willReturn($updatedRoleDraft); + + $traceableEventDispatcher->addListener(BeforeRemovePolicyByRoleDraftEvent::class, static function (BeforeRemovePolicyByRoleDraftEvent $event) use ($eventUpdatedRoleDraft) { + $event->setUpdatedRoleDraft($eventUpdatedRoleDraft); + $event->stopPropagation(); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->removePolicyByRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUpdatedRoleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeRemovePolicyByRoleDraftEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeRemovePolicyByRoleDraftEvent::class, 0], + [RemovePolicyByRoleDraftEvent::class, 0], + ]); + } + + public function testAddPolicyByRoleDraftEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAddPolicyByRoleDraftEvent::class, + AddPolicyByRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + $this->createMock(PolicyCreateStruct::class), + ]; + + $updatedRoleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('addPolicyByRoleDraft')->willReturn($updatedRoleDraft); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->addPolicyByRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($updatedRoleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeAddPolicyByRoleDraftEvent::class, 0], + [AddPolicyByRoleDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnAddPolicyByRoleDraftResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAddPolicyByRoleDraftEvent::class, + AddPolicyByRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + $this->createMock(PolicyCreateStruct::class), + ]; + + $updatedRoleDraft = $this->createMock(RoleDraft::class); + $eventUpdatedRoleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('addPolicyByRoleDraft')->willReturn($updatedRoleDraft); + + $traceableEventDispatcher->addListener(BeforeAddPolicyByRoleDraftEvent::class, static function (BeforeAddPolicyByRoleDraftEvent $event) use ($eventUpdatedRoleDraft) { + $event->setUpdatedRoleDraft($eventUpdatedRoleDraft); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->addPolicyByRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUpdatedRoleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeAddPolicyByRoleDraftEvent::class, 10], + [BeforeAddPolicyByRoleDraftEvent::class, 0], + [AddPolicyByRoleDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testAddPolicyByRoleDraftStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAddPolicyByRoleDraftEvent::class, + AddPolicyByRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + $this->createMock(PolicyCreateStruct::class), + ]; + + $updatedRoleDraft = $this->createMock(RoleDraft::class); + $eventUpdatedRoleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('addPolicyByRoleDraft')->willReturn($updatedRoleDraft); + + $traceableEventDispatcher->addListener(BeforeAddPolicyByRoleDraftEvent::class, static function (BeforeAddPolicyByRoleDraftEvent $event) use ($eventUpdatedRoleDraft) { + $event->setUpdatedRoleDraft($eventUpdatedRoleDraft); + $event->stopPropagation(); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->addPolicyByRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUpdatedRoleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeAddPolicyByRoleDraftEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [AddPolicyByRoleDraftEvent::class, 0], + [BeforeAddPolicyByRoleDraftEvent::class, 0], + ]); + } + + public function testDeleteRoleEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteRoleEvent::class, + DeleteRoleEvent::class + ); + + $parameters = [ + $this->createMock(Role::class), + ]; + + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $service->deleteRole(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteRoleEvent::class, 0], + [DeleteRoleEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteRoleStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteRoleEvent::class, + DeleteRoleEvent::class + ); + + $parameters = [ + $this->createMock(Role::class), + ]; + + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteRoleEvent::class, static function (BeforeDeleteRoleEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $service->deleteRole(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteRoleEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteRoleEvent::class, 0], + [DeleteRoleEvent::class, 0], + ]); + } + + public function testDeleteRoleDraftEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteRoleDraftEvent::class, + DeleteRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + ]; + + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $service->deleteRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteRoleDraftEvent::class, 0], + [DeleteRoleDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteRoleDraftStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteRoleDraftEvent::class, + DeleteRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(RoleDraft::class), + ]; + + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteRoleDraftEvent::class, static function (BeforeDeleteRoleDraftEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $service->deleteRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteRoleDraftEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteRoleDraftEvent::class, 0], + [DeleteRoleDraftEvent::class, 0], + ]); + } + + public function testRemoveRoleAssignmentEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemoveRoleAssignmentEvent::class, + RemoveRoleAssignmentEvent::class + ); + + $parameters = [ + $this->createMock(RoleAssignment::class), + ]; + + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $service->removeRoleAssignment(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeRemoveRoleAssignmentEvent::class, 0], + [RemoveRoleAssignmentEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testRemoveRoleAssignmentStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemoveRoleAssignmentEvent::class, + RemoveRoleAssignmentEvent::class + ); + + $parameters = [ + $this->createMock(RoleAssignment::class), + ]; + + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeRemoveRoleAssignmentEvent::class, static function (BeforeRemoveRoleAssignmentEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $service->removeRoleAssignment(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeRemoveRoleAssignmentEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeRemoveRoleAssignmentEvent::class, 0], + [RemoveRoleAssignmentEvent::class, 0], + ]); + } + + public function testCreateRoleDraftEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateRoleDraftEvent::class, + CreateRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(Role::class), + ]; + + $roleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('createRoleDraft')->willReturn($roleDraft); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($roleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateRoleDraftEvent::class, 0], + [CreateRoleDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateRoleDraftResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateRoleDraftEvent::class, + CreateRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(Role::class), + ]; + + $roleDraft = $this->createMock(RoleDraft::class); + $eventRoleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('createRoleDraft')->willReturn($roleDraft); + + $traceableEventDispatcher->addListener(BeforeCreateRoleDraftEvent::class, static function (BeforeCreateRoleDraftEvent $event) use ($eventRoleDraft) { + $event->setRoleDraft($eventRoleDraft); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventRoleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateRoleDraftEvent::class, 10], + [BeforeCreateRoleDraftEvent::class, 0], + [CreateRoleDraftEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateRoleDraftStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateRoleDraftEvent::class, + CreateRoleDraftEvent::class + ); + + $parameters = [ + $this->createMock(Role::class), + ]; + + $roleDraft = $this->createMock(RoleDraft::class); + $eventRoleDraft = $this->createMock(RoleDraft::class); + $innerServiceMock = $this->createMock(RoleServiceInterface::class); + $innerServiceMock->method('createRoleDraft')->willReturn($roleDraft); + + $traceableEventDispatcher->addListener(BeforeCreateRoleDraftEvent::class, static function (BeforeCreateRoleDraftEvent $event) use ($eventRoleDraft) { + $event->setRoleDraft($eventRoleDraft); + $event->stopPropagation(); + }, 10); + + $service = new RoleService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createRoleDraft(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventRoleDraft, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateRoleDraftEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateRoleDraftEvent::class, 0], + [CreateRoleDraftEvent::class, 0], + ]); + } +} + +class_alias(RoleServiceTest::class, 'eZ\Publish\Core\Event\Tests\RoleServiceTest'); diff --git a/tests/lib/Event/SectionServiceTest.php b/tests/lib/Event/SectionServiceTest.php new file mode 100644 index 0000000000..e988fe6c00 --- /dev/null +++ b/tests/lib/Event/SectionServiceTest.php @@ -0,0 +1,406 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Events\Section\AssignSectionEvent; +use Ibexa\Contracts\Core\Repository\Events\Section\AssignSectionToSubtreeEvent; +use Ibexa\Contracts\Core\Repository\Events\Section\BeforeAssignSectionEvent; +use Ibexa\Contracts\Core\Repository\Events\Section\BeforeAssignSectionToSubtreeEvent; +use Ibexa\Contracts\Core\Repository\Events\Section\BeforeCreateSectionEvent; +use Ibexa\Contracts\Core\Repository\Events\Section\BeforeDeleteSectionEvent; +use Ibexa\Contracts\Core\Repository\Events\Section\BeforeUpdateSectionEvent; +use Ibexa\Contracts\Core\Repository\Events\Section\CreateSectionEvent; +use Ibexa\Contracts\Core\Repository\Events\Section\DeleteSectionEvent; +use Ibexa\Contracts\Core\Repository\Events\Section\UpdateSectionEvent; +use Ibexa\Contracts\Core\Repository\SectionService as SectionServiceInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\Section; +use Ibexa\Contracts\Core\Repository\Values\Content\SectionCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\SectionUpdateStruct; +use Ibexa\Core\Event\SectionService; + +class SectionServiceTest extends AbstractServiceTest +{ + public function testAssignSectionEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAssignSectionEvent::class, + AssignSectionEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(Section::class), + ]; + + $innerServiceMock = $this->createMock(SectionServiceInterface::class); + + $service = new SectionService($innerServiceMock, $traceableEventDispatcher); + $service->assignSection(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAssignSectionEvent::class, 0], + [AssignSectionEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testAssignSectionStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAssignSectionEvent::class, + AssignSectionEvent::class + ); + + $parameters = [ + $this->createMock(ContentInfo::class), + $this->createMock(Section::class), + ]; + + $innerServiceMock = $this->createMock(SectionServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeAssignSectionEvent::class, static function (BeforeAssignSectionEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new SectionService($innerServiceMock, $traceableEventDispatcher); + $service->assignSection(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAssignSectionEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [AssignSectionEvent::class, 0], + [BeforeAssignSectionEvent::class, 0], + ]); + } + + public function testUpdateSectionEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateSectionEvent::class, + UpdateSectionEvent::class + ); + + $parameters = [ + $this->createMock(Section::class), + $this->createMock(SectionUpdateStruct::class), + ]; + + $updatedSection = $this->createMock(Section::class); + $innerServiceMock = $this->createMock(SectionServiceInterface::class); + $innerServiceMock->method('updateSection')->willReturn($updatedSection); + + $service = new SectionService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateSection(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($updatedSection, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateSectionEvent::class, 0], + [UpdateSectionEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdateSectionResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateSectionEvent::class, + UpdateSectionEvent::class + ); + + $parameters = [ + $this->createMock(Section::class), + $this->createMock(SectionUpdateStruct::class), + ]; + + $updatedSection = $this->createMock(Section::class); + $eventUpdatedSection = $this->createMock(Section::class); + $innerServiceMock = $this->createMock(SectionServiceInterface::class); + $innerServiceMock->method('updateSection')->willReturn($updatedSection); + + $traceableEventDispatcher->addListener(BeforeUpdateSectionEvent::class, static function (BeforeUpdateSectionEvent $event) use ($eventUpdatedSection) { + $event->setUpdatedSection($eventUpdatedSection); + }, 10); + + $service = new SectionService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateSection(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUpdatedSection, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateSectionEvent::class, 10], + [BeforeUpdateSectionEvent::class, 0], + [UpdateSectionEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateSectionStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateSectionEvent::class, + UpdateSectionEvent::class + ); + + $parameters = [ + $this->createMock(Section::class), + $this->createMock(SectionUpdateStruct::class), + ]; + + $updatedSection = $this->createMock(Section::class); + $eventUpdatedSection = $this->createMock(Section::class); + $innerServiceMock = $this->createMock(SectionServiceInterface::class); + $innerServiceMock->method('updateSection')->willReturn($updatedSection); + + $traceableEventDispatcher->addListener(BeforeUpdateSectionEvent::class, static function (BeforeUpdateSectionEvent $event) use ($eventUpdatedSection) { + $event->setUpdatedSection($eventUpdatedSection); + $event->stopPropagation(); + }, 10); + + $service = new SectionService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateSection(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUpdatedSection, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateSectionEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateSectionEvent::class, 0], + [UpdateSectionEvent::class, 0], + ]); + } + + public function testAssignSectionToSubtreeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAssignSectionToSubtreeEvent::class, + AssignSectionToSubtreeEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + $this->createMock(Section::class), + ]; + + $innerServiceMock = $this->createMock(SectionServiceInterface::class); + + $service = new SectionService($innerServiceMock, $traceableEventDispatcher); + $service->assignSectionToSubtree(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAssignSectionToSubtreeEvent::class, 0], + [AssignSectionToSubtreeEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testAssignSectionToSubtreeStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAssignSectionToSubtreeEvent::class, + AssignSectionToSubtreeEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + $this->createMock(Section::class), + ]; + + $innerServiceMock = $this->createMock(SectionServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeAssignSectionToSubtreeEvent::class, static function (BeforeAssignSectionToSubtreeEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new SectionService($innerServiceMock, $traceableEventDispatcher); + $service->assignSectionToSubtree(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAssignSectionToSubtreeEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [AssignSectionToSubtreeEvent::class, 0], + [BeforeAssignSectionToSubtreeEvent::class, 0], + ]); + } + + public function testDeleteSectionEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteSectionEvent::class, + DeleteSectionEvent::class + ); + + $parameters = [ + $this->createMock(Section::class), + ]; + + $innerServiceMock = $this->createMock(SectionServiceInterface::class); + + $service = new SectionService($innerServiceMock, $traceableEventDispatcher); + $service->deleteSection(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteSectionEvent::class, 0], + [DeleteSectionEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteSectionStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteSectionEvent::class, + DeleteSectionEvent::class + ); + + $parameters = [ + $this->createMock(Section::class), + ]; + + $innerServiceMock = $this->createMock(SectionServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeDeleteSectionEvent::class, static function (BeforeDeleteSectionEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new SectionService($innerServiceMock, $traceableEventDispatcher); + $service->deleteSection(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeDeleteSectionEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteSectionEvent::class, 0], + [DeleteSectionEvent::class, 0], + ]); + } + + public function testCreateSectionEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateSectionEvent::class, + CreateSectionEvent::class + ); + + $parameters = [ + $this->createMock(SectionCreateStruct::class), + ]; + + $section = $this->createMock(Section::class); + $innerServiceMock = $this->createMock(SectionServiceInterface::class); + $innerServiceMock->method('createSection')->willReturn($section); + + $service = new SectionService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createSection(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($section, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateSectionEvent::class, 0], + [CreateSectionEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateSectionResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateSectionEvent::class, + CreateSectionEvent::class + ); + + $parameters = [ + $this->createMock(SectionCreateStruct::class), + ]; + + $section = $this->createMock(Section::class); + $eventSection = $this->createMock(Section::class); + $innerServiceMock = $this->createMock(SectionServiceInterface::class); + $innerServiceMock->method('createSection')->willReturn($section); + + $traceableEventDispatcher->addListener(BeforeCreateSectionEvent::class, static function (BeforeCreateSectionEvent $event) use ($eventSection) { + $event->setSection($eventSection); + }, 10); + + $service = new SectionService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createSection(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventSection, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateSectionEvent::class, 10], + [BeforeCreateSectionEvent::class, 0], + [CreateSectionEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateSectionStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateSectionEvent::class, + CreateSectionEvent::class + ); + + $parameters = [ + $this->createMock(SectionCreateStruct::class), + ]; + + $section = $this->createMock(Section::class); + $eventSection = $this->createMock(Section::class); + $innerServiceMock = $this->createMock(SectionServiceInterface::class); + $innerServiceMock->method('createSection')->willReturn($section); + + $traceableEventDispatcher->addListener(BeforeCreateSectionEvent::class, static function (BeforeCreateSectionEvent $event) use ($eventSection) { + $event->setSection($eventSection); + $event->stopPropagation(); + }, 10); + + $service = new SectionService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createSection(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventSection, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateSectionEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateSectionEvent::class, 0], + [CreateSectionEvent::class, 0], + ]); + } +} + +class_alias(SectionServiceTest::class, 'eZ\Publish\Core\Event\Tests\SectionServiceTest'); diff --git a/tests/lib/Event/SettingServiceTest.php b/tests/lib/Event/SettingServiceTest.php new file mode 100644 index 0000000000..6e0779a0a6 --- /dev/null +++ b/tests/lib/Event/SettingServiceTest.php @@ -0,0 +1,334 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Events\Setting\BeforeCreateSettingEvent; +use Ibexa\Contracts\Core\Repository\Events\Setting\BeforeDeleteSettingEvent; +use Ibexa\Contracts\Core\Repository\Events\Setting\BeforeUpdateSettingEvent; +use Ibexa\Contracts\Core\Repository\Events\Setting\CreateSettingEvent; +use Ibexa\Contracts\Core\Repository\Events\Setting\DeleteSettingEvent; +use Ibexa\Contracts\Core\Repository\Events\Setting\UpdateSettingEvent; +use Ibexa\Contracts\Core\Repository\SettingService as SettingServiceInterface; +use Ibexa\Contracts\Core\Repository\Values\Setting\Setting; +use Ibexa\Contracts\Core\Repository\Values\Setting\SettingCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Setting\SettingUpdateStruct; +use Ibexa\Core\Event\SettingService; +use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; + +class SettingServiceTest extends AbstractServiceTest +{ + public function testUpdateSettingEvents(): void + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateSettingEvent::class, + UpdateSettingEvent::class + ); + $updatedSetting = $this->createMock(Setting::class); + + $result = $this->updateSetting($updatedSetting, $traceableEventDispatcher); + + $calledListeners = $this->getListenersStack( + $traceableEventDispatcher->getCalledListeners() + ); + + self::assertSame($updatedSetting, $result); + self::assertSame( + $calledListeners, + [ + [BeforeUpdateSettingEvent::class, 0], + [UpdateSettingEvent::class, 0], + ] + ); + self::assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdateSettingResultInBeforeEvents(): void + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateSettingEvent::class, + UpdateSettingEvent::class + ); + $eventUpdatedSetting = $this->createMock(Setting::class); + + $traceableEventDispatcher->addListener( + BeforeUpdateSettingEvent::class, + static function (BeforeUpdateSettingEvent $event) use ($eventUpdatedSetting) { + $event->setUpdatedSetting($eventUpdatedSetting); + }, + 10 + ); + $updatedSetting = $this->createMock(Setting::class); + + $result = $this->updateSetting($updatedSetting, $traceableEventDispatcher); + + $calledListeners = $this->getListenersStack( + $traceableEventDispatcher->getCalledListeners() + ); + + self::assertSame($eventUpdatedSetting, $result); + self::assertSame( + $calledListeners, + [ + [BeforeUpdateSettingEvent::class, 10], + [BeforeUpdateSettingEvent::class, 0], + [UpdateSettingEvent::class, 0], + ] + ); + self::assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateSettingStopPropagationInBeforeEvents(): void + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateSettingEvent::class, + UpdateSettingEvent::class + ); + $eventUpdatedSetting = $this->createMock(Setting::class); + $traceableEventDispatcher->addListener( + BeforeUpdateSettingEvent::class, + static function (BeforeUpdateSettingEvent $event) use ($eventUpdatedSetting) { + $event->setUpdatedSetting($eventUpdatedSetting); + $event->stopPropagation(); + }, + 10 + ); + $updatedSetting = $this->createMock(Setting::class); + + $result = $this->updateSetting($updatedSetting, $traceableEventDispatcher); + + $calledListeners = $this->getListenersStack( + $traceableEventDispatcher->getCalledListeners() + ); + $notCalledListeners = $this->getListenersStack( + $traceableEventDispatcher->getNotCalledListeners() + ); + + self::assertSame($eventUpdatedSetting, $result); + self::assertSame( + $calledListeners, + [ + [BeforeUpdateSettingEvent::class, 10], + ] + ); + self::assertSame( + $notCalledListeners, + [ + [BeforeUpdateSettingEvent::class, 0], + [UpdateSettingEvent::class, 0], + ] + ); + } + + public function testDeleteSettingEvents(): void + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteSettingEvent::class, + DeleteSettingEvent::class + ); + + $parameters = [ + $this->createMock(Setting::class), + ]; + + $innerServiceMock = $this->createMock(SettingServiceInterface::class); + + $service = new SettingService($innerServiceMock, $traceableEventDispatcher); + $service->deleteSetting(...$parameters); + + $calledListeners = $this->getListenersStack( + $traceableEventDispatcher->getCalledListeners() + ); + + self::assertSame( + $calledListeners, + [ + [BeforeDeleteSettingEvent::class, 0], + [DeleteSettingEvent::class, 0], + ] + ); + self::assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteSettingStopPropagationInBeforeEvents(): void + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteSettingEvent::class, + DeleteSettingEvent::class + ); + + $parameters = [ + $this->createMock(Setting::class), + ]; + + $innerServiceMock = $this->createMock(SettingServiceInterface::class); + + $traceableEventDispatcher->addListener( + BeforeDeleteSettingEvent::class, + static function (BeforeDeleteSettingEvent $event) { + $event->stopPropagation(); + }, + 10 + ); + + $service = new SettingService($innerServiceMock, $traceableEventDispatcher); + $service->deleteSetting(...$parameters); + + $calledListeners = $this->getListenersStack( + $traceableEventDispatcher->getCalledListeners() + ); + $notCalledListeners = $this->getListenersStack( + $traceableEventDispatcher->getNotCalledListeners() + ); + + self::assertSame( + $calledListeners, + [ + [BeforeDeleteSettingEvent::class, 10], + ] + ); + self::assertSame( + $notCalledListeners, + [ + [BeforeDeleteSettingEvent::class, 0], + [DeleteSettingEvent::class, 0], + ] + ); + } + + public function testCreateSettingEvents(): void + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateSettingEvent::class, + CreateSettingEvent::class + ); + $setting = $this->createMock(Setting::class); + + $result = $this->createSetting($setting, $traceableEventDispatcher); + + $calledListeners = $this->getListenersStack( + $traceableEventDispatcher->getCalledListeners() + ); + + self::assertSame($setting, $result); + self::assertSame( + $calledListeners, + [ + [BeforeCreateSettingEvent::class, 0], + [CreateSettingEvent::class, 0], + ] + ); + self::assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateSettingResultInBeforeEvents(): void + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateSettingEvent::class, + CreateSettingEvent::class + ); + $eventSetting = $this->createMock(Setting::class); + $setting = $this->createMock(Setting::class); + $traceableEventDispatcher->addListener( + BeforeCreateSettingEvent::class, + static function (BeforeCreateSettingEvent $event) use ($eventSetting) { + $event->setSetting($eventSetting); + }, + 10 + ); + + $result = $this->createSetting($setting, $traceableEventDispatcher); + + $calledListeners = $this->getListenersStack( + $traceableEventDispatcher->getCalledListeners() + ); + + self::assertSame($eventSetting, $result); + self::assertSame( + $calledListeners, + [ + [BeforeCreateSettingEvent::class, 10], + [BeforeCreateSettingEvent::class, 0], + [CreateSettingEvent::class, 0], + ] + ); + self::assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateSettingStopPropagationInBeforeEvents(): void + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateSettingEvent::class, + CreateSettingEvent::class + ); + $eventSetting = $this->createMock(Setting::class); + $setting = $this->createMock(Setting::class); + $traceableEventDispatcher->addListener( + BeforeCreateSettingEvent::class, + static function (BeforeCreateSettingEvent $event) use ($eventSetting) { + $event->setSetting($eventSetting); + $event->stopPropagation(); + }, + 10 + ); + + $result = $this->createSetting($setting, $traceableEventDispatcher); + + $calledListeners = $this->getListenersStack( + $traceableEventDispatcher->getCalledListeners() + ); + $notCalledListeners = $this->getListenersStack( + $traceableEventDispatcher->getNotCalledListeners() + ); + + self::assertSame($eventSetting, $result); + self::assertSame( + $calledListeners, + [ + [BeforeCreateSettingEvent::class, 10], + ] + ); + self::assertSame( + $notCalledListeners, + [ + [BeforeCreateSettingEvent::class, 0], + [CreateSettingEvent::class, 0], + ] + ); + } + + private function createSetting( + Setting $setting, + TraceableEventDispatcher $traceableEventDispatcher + ): Setting { + $parameters = [ + $this->createMock(SettingCreateStruct::class), + ]; + $innerServiceMock = $this->createMock(SettingServiceInterface::class); + $innerServiceMock->method('createSetting')->willReturn($setting); + $service = new SettingService($innerServiceMock, $traceableEventDispatcher); + + return $service->createSetting(...$parameters); + } + + private function updateSetting( + Setting $updatedSetting, + TraceableEventDispatcher $traceableEventDispatcher + ): Setting { + $parameters = [ + $this->createMock(Setting::class), + $this->createMock(SettingUpdateStruct::class), + ]; + $innerServiceMock = $this->createMock(SettingServiceInterface::class); + $innerServiceMock->method('updateSetting')->willReturn($updatedSetting); + + $service = new SettingService($innerServiceMock, $traceableEventDispatcher); + + return $service->updateSetting(...$parameters); + } +} + +class_alias(SettingServiceTest::class, 'eZ\Publish\Core\Event\Tests\SettingServiceTest'); diff --git a/tests/lib/Event/TrashServiceTest.php b/tests/lib/Event/TrashServiceTest.php new file mode 100644 index 0000000000..34fddd915a --- /dev/null +++ b/tests/lib/Event/TrashServiceTest.php @@ -0,0 +1,458 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Events\Trash\BeforeDeleteTrashItemEvent; +use Ibexa\Contracts\Core\Repository\Events\Trash\BeforeEmptyTrashEvent; +use Ibexa\Contracts\Core\Repository\Events\Trash\BeforeRecoverEvent; +use Ibexa\Contracts\Core\Repository\Events\Trash\BeforeTrashEvent; +use Ibexa\Contracts\Core\Repository\Events\Trash\DeleteTrashItemEvent; +use Ibexa\Contracts\Core\Repository\Events\Trash\EmptyTrashEvent; +use Ibexa\Contracts\Core\Repository\Events\Trash\RecoverEvent; +use Ibexa\Contracts\Core\Repository\Events\Trash\TrashEvent; +use Ibexa\Contracts\Core\Repository\TrashService as TrashServiceInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResultList; +use Ibexa\Contracts\Core\Repository\Values\Content\TrashItem; +use Ibexa\Core\Event\TrashService; + +class TrashServiceTest extends AbstractServiceTest +{ + public function testEmptyTrashEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeEmptyTrashEvent::class, + EmptyTrashEvent::class + ); + + $parameters = [ + ]; + + $resultList = $this->createMock(TrashItemDeleteResultList::class); + $innerServiceMock = $this->createMock(TrashServiceInterface::class); + $innerServiceMock->method('emptyTrash')->willReturn($resultList); + + $service = new TrashService($innerServiceMock, $traceableEventDispatcher); + $result = $service->emptyTrash(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($resultList, $result); + $this->assertSame($calledListeners, [ + [BeforeEmptyTrashEvent::class, 0], + [EmptyTrashEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnEmptyTrashResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeEmptyTrashEvent::class, + EmptyTrashEvent::class + ); + + $parameters = [ + ]; + + $resultList = $this->createMock(TrashItemDeleteResultList::class); + $eventResultList = $this->createMock(TrashItemDeleteResultList::class); + $innerServiceMock = $this->createMock(TrashServiceInterface::class); + $innerServiceMock->method('emptyTrash')->willReturn($resultList); + + $traceableEventDispatcher->addListener(BeforeEmptyTrashEvent::class, static function (BeforeEmptyTrashEvent $event) use ($eventResultList) { + $event->setResultList($eventResultList); + }, 10); + + $service = new TrashService($innerServiceMock, $traceableEventDispatcher); + $result = $service->emptyTrash(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventResultList, $result); + $this->assertSame($calledListeners, [ + [BeforeEmptyTrashEvent::class, 10], + [BeforeEmptyTrashEvent::class, 0], + [EmptyTrashEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testEmptyTrashStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeEmptyTrashEvent::class, + EmptyTrashEvent::class + ); + + $parameters = [ + ]; + + $resultList = $this->createMock(TrashItemDeleteResultList::class); + $eventResultList = $this->createMock(TrashItemDeleteResultList::class); + $innerServiceMock = $this->createMock(TrashServiceInterface::class); + $innerServiceMock->method('emptyTrash')->willReturn($resultList); + + $traceableEventDispatcher->addListener(BeforeEmptyTrashEvent::class, static function (BeforeEmptyTrashEvent $event) use ($eventResultList) { + $event->setResultList($eventResultList); + $event->stopPropagation(); + }, 10); + + $service = new TrashService($innerServiceMock, $traceableEventDispatcher); + $result = $service->emptyTrash(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventResultList, $result); + $this->assertSame($calledListeners, [ + [BeforeEmptyTrashEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeEmptyTrashEvent::class, 0], + [EmptyTrashEvent::class, 0], + ]); + } + + public function testTrashEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeTrashEvent::class, + TrashEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $trashItem = $this->createMock(TrashItem::class); + $innerServiceMock = $this->createMock(TrashServiceInterface::class); + $innerServiceMock->method('trash')->willReturn($trashItem); + + $service = new TrashService($innerServiceMock, $traceableEventDispatcher); + $result = $service->trash(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($trashItem, $result); + $this->assertSame($calledListeners, [ + [BeforeTrashEvent::class, 0], + [TrashEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnTrashResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeTrashEvent::class, + TrashEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $trashItem = $this->createMock(TrashItem::class); + $eventTrashItem = $this->createMock(TrashItem::class); + $innerServiceMock = $this->createMock(TrashServiceInterface::class); + $innerServiceMock->method('trash')->willReturn($trashItem); + + $traceableEventDispatcher->addListener(BeforeTrashEvent::class, static function (BeforeTrashEvent $event) use ($eventTrashItem) { + $event->setResult($eventTrashItem); + }, 10); + + $service = new TrashService($innerServiceMock, $traceableEventDispatcher); + $result = $service->trash(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventTrashItem, $result); + $this->assertSame($calledListeners, [ + [BeforeTrashEvent::class, 10], + [BeforeTrashEvent::class, 0], + [TrashEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testTrashStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeTrashEvent::class, + TrashEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $trashItem = $this->createMock(TrashItem::class); + $eventTrashItem = $this->createMock(TrashItem::class); + $innerServiceMock = $this->createMock(TrashServiceInterface::class); + $innerServiceMock->method('trash')->willReturn($trashItem); + + $traceableEventDispatcher->addListener(BeforeTrashEvent::class, static function (BeforeTrashEvent $event) use ($eventTrashItem) { + $event->setResult($eventTrashItem); + $event->stopPropagation(); + }, 10); + + $service = new TrashService($innerServiceMock, $traceableEventDispatcher); + $result = $service->trash(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventTrashItem, $result); + $this->assertSame($calledListeners, [ + [BeforeTrashEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeTrashEvent::class, 0], + [TrashEvent::class, 0], + ]); + } + + public function testTrashStopPropagationInBeforeEventsSetsNullResult(): void + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeTrashEvent::class, + TrashEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $innerServiceMock = $this->createMock(TrashServiceInterface::class); + $innerServiceMock->expects(self::never())->method('trash'); + + $traceableEventDispatcher->addListener(BeforeTrashEvent::class, static function (BeforeTrashEvent $event) { + $event->setResult(null); + $event->stopPropagation(); + }, 10); + + $service = new TrashService($innerServiceMock, $traceableEventDispatcher); + $result = $service->trash(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + self::assertNull($result); + self::assertSame($calledListeners, [ + [BeforeTrashEvent::class, 10], + ]); + self::assertSame($notCalledListeners, [ + [BeforeTrashEvent::class, 0], + [TrashEvent::class, 0], + ]); + } + + public function testRecoverEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRecoverEvent::class, + RecoverEvent::class + ); + + $parameters = [ + $this->createMock(TrashItem::class), + $this->createMock(Location::class), + ]; + + $location = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(TrashServiceInterface::class); + $innerServiceMock->method('recover')->willReturn($location); + + $service = new TrashService($innerServiceMock, $traceableEventDispatcher); + $result = $service->recover(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($location, $result); + $this->assertSame($calledListeners, [ + [BeforeRecoverEvent::class, 0], + [RecoverEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnRecoverResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRecoverEvent::class, + RecoverEvent::class + ); + + $parameters = [ + $this->createMock(TrashItem::class), + $this->createMock(Location::class), + ]; + + $location = $this->createMock(Location::class); + $eventLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(TrashServiceInterface::class); + $innerServiceMock->method('recover')->willReturn($location); + + $traceableEventDispatcher->addListener(BeforeRecoverEvent::class, static function (BeforeRecoverEvent $event) use ($eventLocation) { + $event->setLocation($eventLocation); + }, 10); + + $service = new TrashService($innerServiceMock, $traceableEventDispatcher); + $result = $service->recover(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeRecoverEvent::class, 10], + [BeforeRecoverEvent::class, 0], + [RecoverEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testRecoverStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRecoverEvent::class, + RecoverEvent::class + ); + + $parameters = [ + $this->createMock(TrashItem::class), + $this->createMock(Location::class), + ]; + + $location = $this->createMock(Location::class); + $eventLocation = $this->createMock(Location::class); + $innerServiceMock = $this->createMock(TrashServiceInterface::class); + $innerServiceMock->method('recover')->willReturn($location); + + $traceableEventDispatcher->addListener(BeforeRecoverEvent::class, static function (BeforeRecoverEvent $event) use ($eventLocation) { + $event->setLocation($eventLocation); + $event->stopPropagation(); + }, 10); + + $service = new TrashService($innerServiceMock, $traceableEventDispatcher); + $result = $service->recover(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventLocation, $result); + $this->assertSame($calledListeners, [ + [BeforeRecoverEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeRecoverEvent::class, 0], + [RecoverEvent::class, 0], + ]); + } + + public function testDeleteTrashItemEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteTrashItemEvent::class, + DeleteTrashItemEvent::class + ); + + $parameters = [ + $this->createMock(TrashItem::class), + ]; + + $result = $this->createMock(TrashItemDeleteResult::class); + $innerServiceMock = $this->createMock(TrashServiceInterface::class); + $innerServiceMock->method('deleteTrashItem')->willReturn($result); + + $service = new TrashService($innerServiceMock, $traceableEventDispatcher); + $result = $service->deleteTrashItem(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($result, $result); + $this->assertSame($calledListeners, [ + [BeforeDeleteTrashItemEvent::class, 0], + [DeleteTrashItemEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnDeleteTrashItemResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteTrashItemEvent::class, + DeleteTrashItemEvent::class + ); + + $parameters = [ + $this->createMock(TrashItem::class), + ]; + + $result = $this->createMock(TrashItemDeleteResult::class); + $eventResult = $this->createMock(TrashItemDeleteResult::class); + $innerServiceMock = $this->createMock(TrashServiceInterface::class); + $innerServiceMock->method('deleteTrashItem')->willReturn($result); + + $traceableEventDispatcher->addListener(BeforeDeleteTrashItemEvent::class, static function (BeforeDeleteTrashItemEvent $event) use ($eventResult) { + $event->setResult($eventResult); + }, 10); + + $service = new TrashService($innerServiceMock, $traceableEventDispatcher); + $result = $service->deleteTrashItem(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventResult, $result); + $this->assertSame($calledListeners, [ + [BeforeDeleteTrashItemEvent::class, 10], + [BeforeDeleteTrashItemEvent::class, 0], + [DeleteTrashItemEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteTrashItemStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteTrashItemEvent::class, + DeleteTrashItemEvent::class + ); + + $parameters = [ + $this->createMock(TrashItem::class), + ]; + + $result = $this->createMock(TrashItemDeleteResult::class); + $eventResult = $this->createMock(TrashItemDeleteResult::class); + $innerServiceMock = $this->createMock(TrashServiceInterface::class); + $innerServiceMock->method('deleteTrashItem')->willReturn($result); + + $traceableEventDispatcher->addListener(BeforeDeleteTrashItemEvent::class, static function (BeforeDeleteTrashItemEvent $event) use ($eventResult) { + $event->setResult($eventResult); + $event->stopPropagation(); + }, 10); + + $service = new TrashService($innerServiceMock, $traceableEventDispatcher); + $result = $service->deleteTrashItem(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventResult, $result); + $this->assertSame($calledListeners, [ + [BeforeDeleteTrashItemEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteTrashItemEvent::class, 0], + [DeleteTrashItemEvent::class, 0], + ]); + } +} + +class_alias(TrashServiceTest::class, 'eZ\Publish\Core\Event\Tests\TrashServiceTest'); diff --git a/tests/lib/Event/URLAliasServiceTest.php b/tests/lib/Event/URLAliasServiceTest.php new file mode 100644 index 0000000000..65f450bf65 --- /dev/null +++ b/tests/lib/Event/URLAliasServiceTest.php @@ -0,0 +1,361 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Events\URLAlias\BeforeCreateGlobalUrlAliasEvent; +use Ibexa\Contracts\Core\Repository\Events\URLAlias\BeforeCreateUrlAliasEvent; +use Ibexa\Contracts\Core\Repository\Events\URLAlias\BeforeRefreshSystemUrlAliasesForLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\URLAlias\BeforeRemoveAliasesEvent; +use Ibexa\Contracts\Core\Repository\Events\URLAlias\CreateGlobalUrlAliasEvent; +use Ibexa\Contracts\Core\Repository\Events\URLAlias\CreateUrlAliasEvent; +use Ibexa\Contracts\Core\Repository\Events\URLAlias\RefreshSystemUrlAliasesForLocationEvent; +use Ibexa\Contracts\Core\Repository\Events\URLAlias\RemoveAliasesEvent; +use Ibexa\Contracts\Core\Repository\URLAliasService as URLAliasServiceInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; +use Ibexa\Core\Event\URLAliasService; + +class URLAliasServiceTest extends AbstractServiceTest +{ + public function testCreateGlobalUrlAliasEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateGlobalUrlAliasEvent::class, + CreateGlobalUrlAliasEvent::class + ); + + $parameters = [ + 'random_value_5cff79c3183471.48198669', + 'random_value_5cff79c3183491.90712521', + 'random_value_5cff79c31834a2.27245619', + 'random_value_5cff79c31834b7.17763784', + 'random_value_5cff79c31834c3.69513526', + ]; + + $urlAlias = $this->createMock(URLAlias::class); + $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); + $innerServiceMock->method('createGlobalUrlAlias')->willReturn($urlAlias); + + $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createGlobalUrlAlias(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($urlAlias, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateGlobalUrlAliasEvent::class, 0], + [CreateGlobalUrlAliasEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateGlobalUrlAliasResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateGlobalUrlAliasEvent::class, + CreateGlobalUrlAliasEvent::class + ); + + $parameters = [ + 'random_value_5cff79c3183999.45723962', + 'random_value_5cff79c31839a0.16919746', + 'random_value_5cff79c31839b6.04657069', + 'random_value_5cff79c31839c8.99027893', + 'random_value_5cff79c31839d9.22502123', + ]; + + $urlAlias = $this->createMock(URLAlias::class); + $eventUrlAlias = $this->createMock(URLAlias::class); + $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); + $innerServiceMock->method('createGlobalUrlAlias')->willReturn($urlAlias); + + $traceableEventDispatcher->addListener(BeforeCreateGlobalUrlAliasEvent::class, static function (BeforeCreateGlobalUrlAliasEvent $event) use ($eventUrlAlias) { + $event->setUrlAlias($eventUrlAlias); + }, 10); + + $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createGlobalUrlAlias(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUrlAlias, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateGlobalUrlAliasEvent::class, 10], + [BeforeCreateGlobalUrlAliasEvent::class, 0], + [CreateGlobalUrlAliasEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateGlobalUrlAliasStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateGlobalUrlAliasEvent::class, + CreateGlobalUrlAliasEvent::class + ); + + $parameters = [ + 'random_value_5cff79c3183a40.78467503', + 'random_value_5cff79c3183a52.60688594', + 'random_value_5cff79c3183a62.37338343', + 'random_value_5cff79c3183a74.31062414', + 'random_value_5cff79c3183a85.16422549', + ]; + + $urlAlias = $this->createMock(URLAlias::class); + $eventUrlAlias = $this->createMock(URLAlias::class); + $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); + $innerServiceMock->method('createGlobalUrlAlias')->willReturn($urlAlias); + + $traceableEventDispatcher->addListener(BeforeCreateGlobalUrlAliasEvent::class, static function (BeforeCreateGlobalUrlAliasEvent $event) use ($eventUrlAlias) { + $event->setUrlAlias($eventUrlAlias); + $event->stopPropagation(); + }, 10); + + $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createGlobalUrlAlias(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUrlAlias, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateGlobalUrlAliasEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateGlobalUrlAliasEvent::class, 0], + [CreateGlobalUrlAliasEvent::class, 0], + ]); + } + + public function testRefreshSystemUrlAliasesForLocationEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRefreshSystemUrlAliasesForLocationEvent::class, + RefreshSystemUrlAliasesForLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); + + $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); + $service->refreshSystemUrlAliasesForLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeRefreshSystemUrlAliasesForLocationEvent::class, 0], + [RefreshSystemUrlAliasesForLocationEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testRefreshSystemUrlAliasesForLocationStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRefreshSystemUrlAliasesForLocationEvent::class, + RefreshSystemUrlAliasesForLocationEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + ]; + + $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeRefreshSystemUrlAliasesForLocationEvent::class, static function (BeforeRefreshSystemUrlAliasesForLocationEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); + $service->refreshSystemUrlAliasesForLocation(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeRefreshSystemUrlAliasesForLocationEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeRefreshSystemUrlAliasesForLocationEvent::class, 0], + [RefreshSystemUrlAliasesForLocationEvent::class, 0], + ]); + } + + public function testCreateUrlAliasEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateUrlAliasEvent::class, + CreateUrlAliasEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + 'random_value_5cff79c3184f05.03459159', + 'random_value_5cff79c3184f14.18292216', + 'random_value_5cff79c3184f24.01158164', + 'random_value_5cff79c3184f32.03833593', + ]; + + $urlAlias = $this->createMock(URLAlias::class); + $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); + $innerServiceMock->method('createUrlAlias')->willReturn($urlAlias); + + $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createUrlAlias(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($urlAlias, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateUrlAliasEvent::class, 0], + [CreateUrlAliasEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateUrlAliasResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateUrlAliasEvent::class, + CreateUrlAliasEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + 'random_value_5cff79c3184fd7.07408772', + 'random_value_5cff79c3184fe2.98616568', + 'random_value_5cff79c3184ff0.62652505', + 'random_value_5cff79c3185003.87499400', + ]; + + $urlAlias = $this->createMock(URLAlias::class); + $eventUrlAlias = $this->createMock(URLAlias::class); + $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); + $innerServiceMock->method('createUrlAlias')->willReturn($urlAlias); + + $traceableEventDispatcher->addListener(BeforeCreateUrlAliasEvent::class, static function (BeforeCreateUrlAliasEvent $event) use ($eventUrlAlias) { + $event->setUrlAlias($eventUrlAlias); + }, 10); + + $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createUrlAlias(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUrlAlias, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateUrlAliasEvent::class, 10], + [BeforeCreateUrlAliasEvent::class, 0], + [CreateUrlAliasEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateUrlAliasStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateUrlAliasEvent::class, + CreateUrlAliasEvent::class + ); + + $parameters = [ + $this->createMock(Location::class), + 'random_value_5cff79c3185072.24449261', + 'random_value_5cff79c3185080.62311461', + 'random_value_5cff79c3185095.31877612', + 'random_value_5cff79c31850a4.20254218', + ]; + + $urlAlias = $this->createMock(URLAlias::class); + $eventUrlAlias = $this->createMock(URLAlias::class); + $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); + $innerServiceMock->method('createUrlAlias')->willReturn($urlAlias); + + $traceableEventDispatcher->addListener(BeforeCreateUrlAliasEvent::class, static function (BeforeCreateUrlAliasEvent $event) use ($eventUrlAlias) { + $event->setUrlAlias($eventUrlAlias); + $event->stopPropagation(); + }, 10); + + $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createUrlAlias(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUrlAlias, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateUrlAliasEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateUrlAliasEvent::class, 0], + [CreateUrlAliasEvent::class, 0], + ]); + } + + public function testRemoveAliasesEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemoveAliasesEvent::class, + RemoveAliasesEvent::class + ); + + $parameters = [ + [], + ]; + + $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); + + $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); + $service->removeAliases(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeRemoveAliasesEvent::class, 0], + [RemoveAliasesEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testRemoveAliasesStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemoveAliasesEvent::class, + RemoveAliasesEvent::class + ); + + $parameters = [ + [], + ]; + + $innerServiceMock = $this->createMock(URLAliasServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeRemoveAliasesEvent::class, static function (BeforeRemoveAliasesEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new URLAliasService($innerServiceMock, $traceableEventDispatcher); + $service->removeAliases(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeRemoveAliasesEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeRemoveAliasesEvent::class, 0], + [RemoveAliasesEvent::class, 0], + ]); + } +} + +class_alias(URLAliasServiceTest::class, 'eZ\Publish\Core\Event\Tests\URLAliasServiceTest'); diff --git a/tests/lib/Event/URLServiceTest.php b/tests/lib/Event/URLServiceTest.php new file mode 100644 index 0000000000..de76868972 --- /dev/null +++ b/tests/lib/Event/URLServiceTest.php @@ -0,0 +1,121 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Events\URL\BeforeUpdateUrlEvent; +use Ibexa\Contracts\Core\Repository\Events\URL\UpdateUrlEvent; +use Ibexa\Contracts\Core\Repository\URLService as URLServiceInterface; +use Ibexa\Contracts\Core\Repository\Values\URL\URL; +use Ibexa\Contracts\Core\Repository\Values\URL\URLUpdateStruct; +use Ibexa\Core\Event\URLService; + +class URLServiceTest extends AbstractServiceTest +{ + public function testUpdateUrlEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateUrlEvent::class, + UpdateUrlEvent::class + ); + + $parameters = [ + $this->createMock(URL::class), + $this->createMock(URLUpdateStruct::class), + ]; + + $updatedUrl = $this->createMock(URL::class); + $innerServiceMock = $this->createMock(URLServiceInterface::class); + $innerServiceMock->method('updateUrl')->willReturn($updatedUrl); + + $service = new URLService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateUrl(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($updatedUrl, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateUrlEvent::class, 0], + [UpdateUrlEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdateUrlResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateUrlEvent::class, + UpdateUrlEvent::class + ); + + $parameters = [ + $this->createMock(URL::class), + $this->createMock(URLUpdateStruct::class), + ]; + + $updatedUrl = $this->createMock(URL::class); + $eventUpdatedUrl = $this->createMock(URL::class); + $innerServiceMock = $this->createMock(URLServiceInterface::class); + $innerServiceMock->method('updateUrl')->willReturn($updatedUrl); + + $traceableEventDispatcher->addListener(BeforeUpdateUrlEvent::class, static function (BeforeUpdateUrlEvent $event) use ($eventUpdatedUrl) { + $event->setUpdatedUrl($eventUpdatedUrl); + }, 10); + + $service = new URLService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateUrl(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUpdatedUrl, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateUrlEvent::class, 10], + [BeforeUpdateUrlEvent::class, 0], + [UpdateUrlEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateUrlStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateUrlEvent::class, + UpdateUrlEvent::class + ); + + $parameters = [ + $this->createMock(URL::class), + $this->createMock(URLUpdateStruct::class), + ]; + + $updatedUrl = $this->createMock(URL::class); + $eventUpdatedUrl = $this->createMock(URL::class); + $innerServiceMock = $this->createMock(URLServiceInterface::class); + $innerServiceMock->method('updateUrl')->willReturn($updatedUrl); + + $traceableEventDispatcher->addListener(BeforeUpdateUrlEvent::class, static function (BeforeUpdateUrlEvent $event) use ($eventUpdatedUrl) { + $event->setUpdatedUrl($eventUpdatedUrl); + $event->stopPropagation(); + }, 10); + + $service = new URLService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateUrl(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUpdatedUrl, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateUrlEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateUrlEvent::class, 0], + [UpdateUrlEvent::class, 0], + ]); + } +} + +class_alias(URLServiceTest::class, 'eZ\Publish\Core\Event\Tests\URLServiceTest'); diff --git a/tests/lib/Event/URLWildcardServiceTest.php b/tests/lib/Event/URLWildcardServiceTest.php new file mode 100644 index 0000000000..56d5e1c3ad --- /dev/null +++ b/tests/lib/Event/URLWildcardServiceTest.php @@ -0,0 +1,366 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Events\URLWildcard\BeforeCreateEvent; +use Ibexa\Contracts\Core\Repository\Events\URLWildcard\BeforeRemoveEvent; +use Ibexa\Contracts\Core\Repository\Events\URLWildcard\BeforeTranslateEvent; +use Ibexa\Contracts\Core\Repository\Events\URLWildcard\BeforeUpdateEvent; +use Ibexa\Contracts\Core\Repository\Events\URLWildcard\CreateEvent; +use Ibexa\Contracts\Core\Repository\Events\URLWildcard\RemoveEvent; +use Ibexa\Contracts\Core\Repository\Events\URLWildcard\TranslateEvent; +use Ibexa\Contracts\Core\Repository\Events\URLWildcard\UpdateEvent; +use Ibexa\Contracts\Core\Repository\URLWildcardService as URLWildcardServiceInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcardTranslationResult; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcardUpdateStruct; +use Ibexa\Core\Event\URLWildcardService; + +class URLWildcardServiceTest extends AbstractServiceTest +{ + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testRemoveEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemoveEvent::class, + RemoveEvent::class + ); + + $parameters = [ + $this->createMock(URLWildcard::class), + ]; + + $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); + + $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); + $service->remove(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeRemoveEvent::class, 0], + [RemoveEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testRemoveStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeRemoveEvent::class, + RemoveEvent::class + ); + + $parameters = [ + $this->createMock(URLWildcard::class), + ]; + + $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeRemoveEvent::class, static function (BeforeRemoveEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); + $service->remove(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeRemoveEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeRemoveEvent::class, 0], + [RemoveEvent::class, 0], + ]); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testUpdateEvents(): void + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateEvent::class, + UpdateEvent::class + ); + + $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); + + $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); + $service->update( + $this->createMock(URLWildcard::class), + new URLWildcardUpdateStruct() + ); + + $calledListeners = $this->getListenersStack( + $traceableEventDispatcher->getCalledListeners() + ); + + $this->assertSame($calledListeners, [ + [BeforeUpdateEvent::class, 0], + [UpdateEvent::class, 0], + ]); + + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateStopPropagationInBeforeEvents(): void + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateEvent::class, + UpdateEvent::class + ); + + $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); + + $traceableEventDispatcher->addListener( + BeforeUpdateEvent::class, + static function (BeforeUpdateEvent $event) { + $event->stopPropagation(); + }, + 10 + ); + + $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); + $service->update( + $this->createMock(URLWildcard::class), + new URLWildcardUpdateStruct() + ); + + $calledListeners = $this->getListenersStack( + $traceableEventDispatcher->getCalledListeners() + ); + $notCalledListeners = $this->getListenersStack( + $traceableEventDispatcher->getNotCalledListeners() + ); + + $this->assertSame($calledListeners, [ + [BeforeUpdateEvent::class, 10], + ]); + + $this->assertSame($notCalledListeners, [ + [BeforeUpdateEvent::class, 0], + [UpdateEvent::class, 0], + ]); + } + + public function testCreateEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateEvent::class, + CreateEvent::class + ); + + $parameters = [ + 'random_value_5cff79c316c1f5.58580131', + 'random_value_5cff79c316c223.93334332', + 'random_value_5cff79c316c237.08397355', + ]; + + $urlWildcard = $this->createMock(URLWildcard::class); + $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); + $innerServiceMock->method('create')->willReturn($urlWildcard); + + $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); + $result = $service->create(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($urlWildcard, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateEvent::class, 0], + [CreateEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateEvent::class, + CreateEvent::class + ); + + $parameters = [ + 'random_value_5cff79c316c2d5.26653678', + 'random_value_5cff79c316c2e7.55400833', + 'random_value_5cff79c316c2f8.59874187', + ]; + + $urlWildcard = $this->createMock(URLWildcard::class); + $eventUrlWildcard = $this->createMock(URLWildcard::class); + $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); + $innerServiceMock->method('create')->willReturn($urlWildcard); + + $traceableEventDispatcher->addListener(BeforeCreateEvent::class, static function (BeforeCreateEvent $event) use ($eventUrlWildcard) { + $event->setUrlWildcard($eventUrlWildcard); + }, 10); + + $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); + $result = $service->create(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUrlWildcard, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateEvent::class, 10], + [BeforeCreateEvent::class, 0], + [CreateEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateEvent::class, + CreateEvent::class + ); + + $parameters = [ + 'random_value_5cff79c316c359.46056769', + 'random_value_5cff79c316c361.53134429', + 'random_value_5cff79c316c374.82657815', + ]; + + $urlWildcard = $this->createMock(URLWildcard::class); + $eventUrlWildcard = $this->createMock(URLWildcard::class); + $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); + $innerServiceMock->method('create')->willReturn($urlWildcard); + + $traceableEventDispatcher->addListener(BeforeCreateEvent::class, static function (BeforeCreateEvent $event) use ($eventUrlWildcard) { + $event->setUrlWildcard($eventUrlWildcard); + $event->stopPropagation(); + }, 10); + + $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); + $result = $service->create(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUrlWildcard, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateEvent::class, 0], + [CreateEvent::class, 0], + ]); + } + + public function testTranslateEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeTranslateEvent::class, + TranslateEvent::class + ); + + $parameters = [ + 'random_value_5cff79c316cfa7.72466150', + ]; + + $result = $this->createMock(URLWildcardTranslationResult::class); + $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); + $innerServiceMock->method('translate')->willReturn($result); + + $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); + $result = $service->translate(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($result, $result); + $this->assertSame($calledListeners, [ + [BeforeTranslateEvent::class, 0], + [TranslateEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnTranslateResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeTranslateEvent::class, + TranslateEvent::class + ); + + $parameters = [ + 'random_value_5cff79c316d370.25863709', + ]; + + $result = $this->createMock(URLWildcardTranslationResult::class); + $eventResult = $this->createMock(URLWildcardTranslationResult::class); + $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); + $innerServiceMock->method('translate')->willReturn($result); + + $traceableEventDispatcher->addListener(BeforeTranslateEvent::class, static function (BeforeTranslateEvent $event) use ($eventResult) { + $event->setResult($eventResult); + }, 10); + + $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); + $result = $service->translate(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventResult, $result); + $this->assertSame($calledListeners, [ + [BeforeTranslateEvent::class, 10], + [BeforeTranslateEvent::class, 0], + [TranslateEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testTranslateStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeTranslateEvent::class, + TranslateEvent::class + ); + + $parameters = [ + 'random_value_5cff79c316d3f9.73226122', + ]; + + $result = $this->createMock(URLWildcardTranslationResult::class); + $eventResult = $this->createMock(URLWildcardTranslationResult::class); + $innerServiceMock = $this->createMock(URLWildcardServiceInterface::class); + $innerServiceMock->method('translate')->willReturn($result); + + $traceableEventDispatcher->addListener(BeforeTranslateEvent::class, static function (BeforeTranslateEvent $event) use ($eventResult) { + $event->setResult($eventResult); + $event->stopPropagation(); + }, 10); + + $service = new URLWildcardService($innerServiceMock, $traceableEventDispatcher); + $result = $service->translate(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventResult, $result); + $this->assertSame($calledListeners, [ + [BeforeTranslateEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeTranslateEvent::class, 0], + [TranslateEvent::class, 0], + ]); + } +} + +class_alias(URLWildcardServiceTest::class, 'eZ\Publish\Core\Event\Tests\URLWildcardServiceTest'); diff --git a/tests/lib/Event/UserPreferenceServiceTest.php b/tests/lib/Event/UserPreferenceServiceTest.php new file mode 100644 index 0000000000..a40317bb0b --- /dev/null +++ b/tests/lib/Event/UserPreferenceServiceTest.php @@ -0,0 +1,74 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Events\UserPreference\BeforeSetUserPreferenceEvent; +use Ibexa\Contracts\Core\Repository\Events\UserPreference\SetUserPreferenceEvent; +use Ibexa\Contracts\Core\Repository\UserPreferenceService as UserPreferenceServiceInterface; +use Ibexa\Core\Event\UserPreferenceService; + +class UserPreferenceServiceTest extends AbstractServiceTest +{ + public function testSetUserPreferenceEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeSetUserPreferenceEvent::class, + SetUserPreferenceEvent::class + ); + + $parameters = [ + [], + ]; + + $innerServiceMock = $this->createMock(UserPreferenceServiceInterface::class); + + $service = new UserPreferenceService($innerServiceMock, $traceableEventDispatcher); + $service->setUserPreference(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeSetUserPreferenceEvent::class, 0], + [SetUserPreferenceEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testSetUserPreferenceStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeSetUserPreferenceEvent::class, + SetUserPreferenceEvent::class + ); + + $parameters = [ + [], + ]; + + $innerServiceMock = $this->createMock(UserPreferenceServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeSetUserPreferenceEvent::class, static function (BeforeSetUserPreferenceEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new UserPreferenceService($innerServiceMock, $traceableEventDispatcher); + $service->setUserPreference(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeSetUserPreferenceEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeSetUserPreferenceEvent::class, 0], + [SetUserPreferenceEvent::class, 0], + ]); + } +} + +class_alias(UserPreferenceServiceTest::class, 'eZ\Publish\Core\Event\Tests\UserPreferenceServiceTest'); diff --git a/tests/lib/Event/UserServiceTest.php b/tests/lib/Event/UserServiceTest.php new file mode 100644 index 0000000000..1f80d6537f --- /dev/null +++ b/tests/lib/Event/UserServiceTest.php @@ -0,0 +1,927 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Event; + +use Ibexa\Contracts\Core\Repository\Events\User\AssignUserToUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\BeforeAssignUserToUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\BeforeCreateUserEvent; +use Ibexa\Contracts\Core\Repository\Events\User\BeforeCreateUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\BeforeDeleteUserEvent; +use Ibexa\Contracts\Core\Repository\Events\User\BeforeDeleteUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\BeforeMoveUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\BeforeUnAssignUserFromUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\BeforeUpdateUserEvent; +use Ibexa\Contracts\Core\Repository\Events\User\BeforeUpdateUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\BeforeUpdateUserTokenEvent; +use Ibexa\Contracts\Core\Repository\Events\User\CreateUserEvent; +use Ibexa\Contracts\Core\Repository\Events\User\CreateUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\DeleteUserEvent; +use Ibexa\Contracts\Core\Repository\Events\User\DeleteUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\MoveUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\UnAssignUserFromUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\UpdateUserEvent; +use Ibexa\Contracts\Core\Repository\Events\User\UpdateUserGroupEvent; +use Ibexa\Contracts\Core\Repository\Events\User\UpdateUserTokenEvent; +use Ibexa\Contracts\Core\Repository\UserService as UserServiceInterface; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroup; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserTokenUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserUpdateStruct; +use Ibexa\Core\Event\UserService; + +class UserServiceTest extends AbstractServiceTest +{ + public function testUpdateUserGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateUserGroupEvent::class, + UpdateUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(UserGroup::class), + $this->createMock(UserGroupUpdateStruct::class), + ]; + + $updatedUserGroup = $this->createMock(UserGroup::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('updateUserGroup')->willReturn($updatedUserGroup); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($updatedUserGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateUserGroupEvent::class, 0], + [UpdateUserGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdateUserGroupResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateUserGroupEvent::class, + UpdateUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(UserGroup::class), + $this->createMock(UserGroupUpdateStruct::class), + ]; + + $updatedUserGroup = $this->createMock(UserGroup::class); + $eventUpdatedUserGroup = $this->createMock(UserGroup::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('updateUserGroup')->willReturn($updatedUserGroup); + + $traceableEventDispatcher->addListener(BeforeUpdateUserGroupEvent::class, static function (BeforeUpdateUserGroupEvent $event) use ($eventUpdatedUserGroup) { + $event->setUpdatedUserGroup($eventUpdatedUserGroup); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUpdatedUserGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateUserGroupEvent::class, 10], + [BeforeUpdateUserGroupEvent::class, 0], + [UpdateUserGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateUserGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateUserGroupEvent::class, + UpdateUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(UserGroup::class), + $this->createMock(UserGroupUpdateStruct::class), + ]; + + $updatedUserGroup = $this->createMock(UserGroup::class); + $eventUpdatedUserGroup = $this->createMock(UserGroup::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('updateUserGroup')->willReturn($updatedUserGroup); + + $traceableEventDispatcher->addListener(BeforeUpdateUserGroupEvent::class, static function (BeforeUpdateUserGroupEvent $event) use ($eventUpdatedUserGroup) { + $event->setUpdatedUserGroup($eventUpdatedUserGroup); + $event->stopPropagation(); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUpdatedUserGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateUserGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateUserGroupEvent::class, 0], + [UpdateUserGroupEvent::class, 0], + ]); + } + + public function testUpdateUserEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateUserEvent::class, + UpdateUserEvent::class + ); + + $parameters = [ + $this->createMock(User::class), + $this->createMock(UserUpdateStruct::class), + ]; + + $updatedUser = $this->createMock(User::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('updateUser')->willReturn($updatedUser); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateUser(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($updatedUser, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateUserEvent::class, 0], + [UpdateUserEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdateUserResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateUserEvent::class, + UpdateUserEvent::class + ); + + $parameters = [ + $this->createMock(User::class), + $this->createMock(UserUpdateStruct::class), + ]; + + $updatedUser = $this->createMock(User::class); + $eventUpdatedUser = $this->createMock(User::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('updateUser')->willReturn($updatedUser); + + $traceableEventDispatcher->addListener(BeforeUpdateUserEvent::class, static function (BeforeUpdateUserEvent $event) use ($eventUpdatedUser) { + $event->setUpdatedUser($eventUpdatedUser); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateUser(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUpdatedUser, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateUserEvent::class, 10], + [BeforeUpdateUserEvent::class, 0], + [UpdateUserEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateUserStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateUserEvent::class, + UpdateUserEvent::class + ); + + $parameters = [ + $this->createMock(User::class), + $this->createMock(UserUpdateStruct::class), + ]; + + $updatedUser = $this->createMock(User::class); + $eventUpdatedUser = $this->createMock(User::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('updateUser')->willReturn($updatedUser); + + $traceableEventDispatcher->addListener(BeforeUpdateUserEvent::class, static function (BeforeUpdateUserEvent $event) use ($eventUpdatedUser) { + $event->setUpdatedUser($eventUpdatedUser); + $event->stopPropagation(); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateUser(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUpdatedUser, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateUserEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateUserEvent::class, 0], + [UpdateUserEvent::class, 0], + ]); + } + + public function testUnAssignUserFromUserGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUnAssignUserFromUserGroupEvent::class, + UnAssignUserFromUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(User::class), + $this->createMock(UserGroup::class), + ]; + + $innerServiceMock = $this->createMock(UserServiceInterface::class); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $service->unAssignUserFromUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeUnAssignUserFromUserGroupEvent::class, 0], + [UnAssignUserFromUserGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUnAssignUserFromUserGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUnAssignUserFromUserGroupEvent::class, + UnAssignUserFromUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(User::class), + $this->createMock(UserGroup::class), + ]; + + $innerServiceMock = $this->createMock(UserServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeUnAssignUserFromUserGroupEvent::class, static function (BeforeUnAssignUserFromUserGroupEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $service->unAssignUserFromUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeUnAssignUserFromUserGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUnAssignUserFromUserGroupEvent::class, 0], + [UnAssignUserFromUserGroupEvent::class, 0], + ]); + } + + public function testDeleteUserGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteUserGroupEvent::class, + DeleteUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(UserGroup::class), + ]; + + $locations = []; + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('deleteUserGroup')->willReturn($locations); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->deleteUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($locations, $result); + $this->assertSame($calledListeners, [ + [BeforeDeleteUserGroupEvent::class, 0], + [DeleteUserGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnDeleteUserGroupResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteUserGroupEvent::class, + DeleteUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(UserGroup::class), + ]; + + $locations = []; + $eventLocations = []; + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('deleteUserGroup')->willReturn($locations); + + $traceableEventDispatcher->addListener(BeforeDeleteUserGroupEvent::class, static function (BeforeDeleteUserGroupEvent $event) use ($eventLocations) { + $event->setLocations($eventLocations); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->deleteUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventLocations, $result); + $this->assertSame($calledListeners, [ + [BeforeDeleteUserGroupEvent::class, 10], + [BeforeDeleteUserGroupEvent::class, 0], + [DeleteUserGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteUserGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteUserGroupEvent::class, + DeleteUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(UserGroup::class), + ]; + + $locations = []; + $eventLocations = []; + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('deleteUserGroup')->willReturn($locations); + + $traceableEventDispatcher->addListener(BeforeDeleteUserGroupEvent::class, static function (BeforeDeleteUserGroupEvent $event) use ($eventLocations) { + $event->setLocations($eventLocations); + $event->stopPropagation(); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->deleteUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventLocations, $result); + $this->assertSame($calledListeners, [ + [BeforeDeleteUserGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteUserGroupEvent::class, 0], + [DeleteUserGroupEvent::class, 0], + ]); + } + + public function testAssignUserToUserGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAssignUserToUserGroupEvent::class, + AssignUserToUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(User::class), + $this->createMock(UserGroup::class), + ]; + + $innerServiceMock = $this->createMock(UserServiceInterface::class); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $service->assignUserToUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAssignUserToUserGroupEvent::class, 0], + [AssignUserToUserGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testAssignUserToUserGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeAssignUserToUserGroupEvent::class, + AssignUserToUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(User::class), + $this->createMock(UserGroup::class), + ]; + + $innerServiceMock = $this->createMock(UserServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeAssignUserToUserGroupEvent::class, static function (BeforeAssignUserToUserGroupEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $service->assignUserToUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeAssignUserToUserGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [AssignUserToUserGroupEvent::class, 0], + [BeforeAssignUserToUserGroupEvent::class, 0], + ]); + } + + public function testDeleteUserEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteUserEvent::class, + DeleteUserEvent::class + ); + + $parameters = [ + $this->createMock(User::class), + ]; + + $locations = []; + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('deleteUser')->willReturn($locations); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->deleteUser(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($locations, $result); + $this->assertSame($calledListeners, [ + [BeforeDeleteUserEvent::class, 0], + [DeleteUserEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnDeleteUserResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteUserEvent::class, + DeleteUserEvent::class + ); + + $parameters = [ + $this->createMock(User::class), + ]; + + $locations = []; + $eventLocations = []; + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('deleteUser')->willReturn($locations); + + $traceableEventDispatcher->addListener(BeforeDeleteUserEvent::class, static function (BeforeDeleteUserEvent $event) use ($eventLocations) { + $event->setLocations($eventLocations); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->deleteUser(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventLocations, $result); + $this->assertSame($calledListeners, [ + [BeforeDeleteUserEvent::class, 10], + [BeforeDeleteUserEvent::class, 0], + [DeleteUserEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testDeleteUserStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeDeleteUserEvent::class, + DeleteUserEvent::class + ); + + $parameters = [ + $this->createMock(User::class), + ]; + + $locations = []; + $eventLocations = []; + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('deleteUser')->willReturn($locations); + + $traceableEventDispatcher->addListener(BeforeDeleteUserEvent::class, static function (BeforeDeleteUserEvent $event) use ($eventLocations) { + $event->setLocations($eventLocations); + $event->stopPropagation(); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->deleteUser(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventLocations, $result); + $this->assertSame($calledListeners, [ + [BeforeDeleteUserEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeDeleteUserEvent::class, 0], + [DeleteUserEvent::class, 0], + ]); + } + + public function testMoveUserGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeMoveUserGroupEvent::class, + MoveUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(UserGroup::class), + $this->createMock(UserGroup::class), + ]; + + $innerServiceMock = $this->createMock(UserServiceInterface::class); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $service->moveUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeMoveUserGroupEvent::class, 0], + [MoveUserGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testMoveUserGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeMoveUserGroupEvent::class, + MoveUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(UserGroup::class), + $this->createMock(UserGroup::class), + ]; + + $innerServiceMock = $this->createMock(UserServiceInterface::class); + + $traceableEventDispatcher->addListener(BeforeMoveUserGroupEvent::class, static function (BeforeMoveUserGroupEvent $event) { + $event->stopPropagation(); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $service->moveUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($calledListeners, [ + [BeforeMoveUserGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeMoveUserGroupEvent::class, 0], + [MoveUserGroupEvent::class, 0], + ]); + } + + public function testCreateUserEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateUserEvent::class, + CreateUserEvent::class + ); + + $parameters = [ + $this->createMock(UserCreateStruct::class), + [], + ]; + + $user = $this->createMock(User::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('createUser')->willReturn($user); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createUser(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($user, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateUserEvent::class, 0], + [CreateUserEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateUserResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateUserEvent::class, + CreateUserEvent::class + ); + + $parameters = [ + $this->createMock(UserCreateStruct::class), + [], + ]; + + $user = $this->createMock(User::class); + $eventUser = $this->createMock(User::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('createUser')->willReturn($user); + + $traceableEventDispatcher->addListener(BeforeCreateUserEvent::class, static function (BeforeCreateUserEvent $event) use ($eventUser) { + $event->setUser($eventUser); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createUser(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUser, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateUserEvent::class, 10], + [BeforeCreateUserEvent::class, 0], + [CreateUserEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateUserStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateUserEvent::class, + CreateUserEvent::class + ); + + $parameters = [ + $this->createMock(UserCreateStruct::class), + [], + ]; + + $user = $this->createMock(User::class); + $eventUser = $this->createMock(User::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('createUser')->willReturn($user); + + $traceableEventDispatcher->addListener(BeforeCreateUserEvent::class, static function (BeforeCreateUserEvent $event) use ($eventUser) { + $event->setUser($eventUser); + $event->stopPropagation(); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createUser(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUser, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateUserEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateUserEvent::class, 0], + [CreateUserEvent::class, 0], + ]); + } + + public function testCreateUserGroupEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateUserGroupEvent::class, + CreateUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(UserGroupCreateStruct::class), + $this->createMock(UserGroup::class), + ]; + + $userGroup = $this->createMock(UserGroup::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('createUserGroup')->willReturn($userGroup); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($userGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateUserGroupEvent::class, 0], + [CreateUserGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnCreateUserGroupResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateUserGroupEvent::class, + CreateUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(UserGroupCreateStruct::class), + $this->createMock(UserGroup::class), + ]; + + $userGroup = $this->createMock(UserGroup::class); + $eventUserGroup = $this->createMock(UserGroup::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('createUserGroup')->willReturn($userGroup); + + $traceableEventDispatcher->addListener(BeforeCreateUserGroupEvent::class, static function (BeforeCreateUserGroupEvent $event) use ($eventUserGroup) { + $event->setUserGroup($eventUserGroup); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUserGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateUserGroupEvent::class, 10], + [BeforeCreateUserGroupEvent::class, 0], + [CreateUserGroupEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testCreateUserGroupStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeCreateUserGroupEvent::class, + CreateUserGroupEvent::class + ); + + $parameters = [ + $this->createMock(UserGroupCreateStruct::class), + $this->createMock(UserGroup::class), + ]; + + $userGroup = $this->createMock(UserGroup::class); + $eventUserGroup = $this->createMock(UserGroup::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('createUserGroup')->willReturn($userGroup); + + $traceableEventDispatcher->addListener(BeforeCreateUserGroupEvent::class, static function (BeforeCreateUserGroupEvent $event) use ($eventUserGroup) { + $event->setUserGroup($eventUserGroup); + $event->stopPropagation(); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->createUserGroup(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUserGroup, $result); + $this->assertSame($calledListeners, [ + [BeforeCreateUserGroupEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeCreateUserGroupEvent::class, 0], + [CreateUserGroupEvent::class, 0], + ]); + } + + public function testUpdateUserTokenEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateUserTokenEvent::class, + UpdateUserTokenEvent::class + ); + + $parameters = [ + $this->createMock(User::class), + $this->createMock(UserTokenUpdateStruct::class), + ]; + + $updatedUser = $this->createMock(User::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('updateUserToken')->willReturn($updatedUser); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateUserToken(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($updatedUser, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateUserTokenEvent::class, 0], + [UpdateUserTokenEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testReturnUpdateUserTokenResultInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateUserTokenEvent::class, + UpdateUserTokenEvent::class + ); + + $parameters = [ + $this->createMock(User::class), + $this->createMock(UserTokenUpdateStruct::class), + ]; + + $updatedUser = $this->createMock(User::class); + $eventUpdatedUser = $this->createMock(User::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('updateUserToken')->willReturn($updatedUser); + + $traceableEventDispatcher->addListener(BeforeUpdateUserTokenEvent::class, static function (BeforeUpdateUserTokenEvent $event) use ($eventUpdatedUser) { + $event->setUpdatedUser($eventUpdatedUser); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateUserToken(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + + $this->assertSame($eventUpdatedUser, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateUserTokenEvent::class, 10], + [BeforeUpdateUserTokenEvent::class, 0], + [UpdateUserTokenEvent::class, 0], + ]); + $this->assertSame([], $traceableEventDispatcher->getNotCalledListeners()); + } + + public function testUpdateUserTokenStopPropagationInBeforeEvents() + { + $traceableEventDispatcher = $this->getEventDispatcher( + BeforeUpdateUserTokenEvent::class, + UpdateUserTokenEvent::class + ); + + $parameters = [ + $this->createMock(User::class), + $this->createMock(UserTokenUpdateStruct::class), + ]; + + $updatedUser = $this->createMock(User::class); + $eventUpdatedUser = $this->createMock(User::class); + $innerServiceMock = $this->createMock(UserServiceInterface::class); + $innerServiceMock->method('updateUserToken')->willReturn($updatedUser); + + $traceableEventDispatcher->addListener(BeforeUpdateUserTokenEvent::class, static function (BeforeUpdateUserTokenEvent $event) use ($eventUpdatedUser) { + $event->setUpdatedUser($eventUpdatedUser); + $event->stopPropagation(); + }, 10); + + $service = new UserService($innerServiceMock, $traceableEventDispatcher); + $result = $service->updateUserToken(...$parameters); + + $calledListeners = $this->getListenersStack($traceableEventDispatcher->getCalledListeners()); + $notCalledListeners = $this->getListenersStack($traceableEventDispatcher->getNotCalledListeners()); + + $this->assertSame($eventUpdatedUser, $result); + $this->assertSame($calledListeners, [ + [BeforeUpdateUserTokenEvent::class, 10], + ]); + $this->assertSame($notCalledListeners, [ + [BeforeUpdateUserTokenEvent::class, 0], + [UpdateUserTokenEvent::class, 0], + ]); + } +} + +class_alias(UserServiceTest::class, 'eZ\Publish\Core\Event\Tests\UserServiceTest'); diff --git a/tests/lib/Event/View/PostBuildViewEventTest.php b/tests/lib/Event/View/PostBuildViewEventTest.php index fc3e8d97a8..7e8002ef0e 100644 --- a/tests/lib/Event/View/PostBuildViewEventTest.php +++ b/tests/lib/Event/View/PostBuildViewEventTest.php @@ -8,8 +8,8 @@ namespace Ibexa\Tests\Core\Event\View; -use eZ\Publish\Core\MVC\Symfony\View\BaseView; use Ibexa\Contracts\Core\Event\View\PostBuildViewEvent; +use Ibexa\Core\MVC\Symfony\View\BaseView; use PHPUnit\Framework\TestCase; final class PostBuildViewEventTest extends TestCase diff --git a/eZ/Publish/Core/FieldType/Tests/APIFieldTypeTest.php b/tests/lib/FieldType/APIFieldTypeTest.php similarity index 88% rename from eZ/Publish/Core/FieldType/Tests/APIFieldTypeTest.php rename to tests/lib/FieldType/APIFieldTypeTest.php index 68b941f7e8..6575577aa1 100644 --- a/eZ/Publish/Core/FieldType/Tests/APIFieldTypeTest.php +++ b/tests/lib/FieldType/APIFieldTypeTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests; +namespace Ibexa\Tests\Core\FieldType; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition; -use eZ\Publish\Core\FieldType\Value; -use eZ\Publish\Core\Repository\Values\ContentType\FieldType; -use eZ\Publish\SPI\FieldType\FieldType as SPIFieldType; -use eZ\Publish\SPI\FieldType\ValidationError; +use Ibexa\Contracts\Core\FieldType\FieldType as SPIFieldType; +use Ibexa\Contracts\Core\FieldType\ValidationError; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition; +use Ibexa\Core\FieldType\Value; +use Ibexa\Core\Repository\Values\ContentType\FieldType; use PHPUnit\Framework\TestCase; class APIFieldTypeTest extends TestCase @@ -18,7 +18,7 @@ class APIFieldTypeTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $innerFieldType; - /** @var \eZ\Publish\Core\Repository\Values\ContentType\FieldType */ + /** @var \Ibexa\Core\Repository\Values\ContentType\FieldType */ private $fieldType; protected function setUp(): void @@ -120,3 +120,5 @@ public function testValidateValue() self::assertSame($validationErrors, $this->fieldType->validateValue($fieldDefinition, $value)); } } + +class_alias(APIFieldTypeTest::class, 'eZ\Publish\Core\FieldType\Tests\APIFieldTypeTest'); diff --git a/tests/lib/FieldType/AuthorTest.php b/tests/lib/FieldType/AuthorTest.php new file mode 100644 index 0000000000..4e98d24e44 --- /dev/null +++ b/tests/lib/FieldType/AuthorTest.php @@ -0,0 +1,547 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Author\Author; +use Ibexa\Core\FieldType\Author\AuthorCollection; +use Ibexa\Core\FieldType\Author\Type as AuthorType; +use Ibexa\Core\FieldType\Author\Value as AuthorValue; +use Ibexa\Core\FieldType\Value; + +/** + * @group fieldType + * @group ezauthor + */ +class AuthorTest extends FieldTypeTest +{ + /** @var \Ibexa\Core\FieldType\Author\Author[] */ + private $authors; + + protected function setUp(): void + { + parent::setUp(); + $this->authors = [ + new Author(['name' => 'Boba Fett', 'email' => 'boba.fett@bountyhunters.com']), + new Author(['name' => 'Darth Vader', 'email' => 'darth.vader@evilempire.biz']), + new Author(['name' => 'Luke Skywalker', 'email' => 'luke@imtheone.net']), + ]; + } + + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Contracts\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new AuthorType(); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return []; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return [ + 'defaultAuthor' => [ + 'type' => 'choice', + 'default' => AuthorType::DEFAULT_VALUE_EMPTY, + ], + ]; + } + + /** + * Returns the empty value expected from the field type. + * + * @return \Ibexa\Core\FieldType\Author\Value + */ + protected function getEmptyValueExpectation() + { + return new AuthorValue(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + 'My name', + InvalidArgumentException::class, + ], + [ + 23, + InvalidArgumentException::class, + ], + [ + ['foo'], + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + [], + new AuthorValue([]), + ], + [ + [ + new Author(['name' => 'Boba Fett', 'email' => 'boba.fett@example.com']), + ], + new AuthorValue( + [ + new Author(['id' => 1, 'name' => 'Boba Fett', 'email' => 'boba.fett@example.com']), + ] + ), + ], + [ + [ + new Author(['name' => 'Boba Fett', 'email' => 'boba.fett@example.com']), + new Author(['name' => 'Darth Vader', 'email' => 'darth.vader@example.com']), + ], + new AuthorValue( + [ + new Author(['id' => 1, 'name' => 'Boba Fett', 'email' => 'boba.fett@example.com']), + new Author(['id' => 2, 'name' => 'Darth Vader', 'email' => 'darth.vader@example.com']), + ] + ), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new AuthorValue([]), + [], + ], + [ + new AuthorValue( + [ + new Author(['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com']), + ] + ), + [ + ['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com'], + ], + ], + [ + new AuthorValue( + [ + new Author(['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com']), + new Author(['id' => 2, 'name' => 'Joe Bielefeld', 'email' => 'bielefeld@example.com']), + ] + ), + [ + ['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com'], + ['id' => 2, 'name' => 'Joe Bielefeld', 'email' => 'bielefeld@example.com'], + ], + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + [], + new AuthorValue([]), + ], + [ + [ + ['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com'], + ], + new AuthorValue( + [ + new Author(['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com']), + ] + ), + ], + [ + [ + ['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com'], + ['id' => 2, 'name' => 'Joe Bielefeld', 'email' => 'bielefeld@example.com'], + ], + new AuthorValue( + [ + new Author(['id' => 1, 'name' => 'Joe Sindelfingen', 'email' => 'sindelfingen@example.com']), + new Author(['id' => 2, 'name' => 'Joe Bielefeld', 'email' => 'bielefeld@example.com']), + ] + ), + ], + ]; + } + + /** + * Provide data sets with field settings which are considered valid by the + * {@link validateFieldSettings()} method. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( 'rows' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidFieldSettings() + { + return [ + [ + [], + ], + [ + [ + 'defaultAuthor' => AuthorType::DEFAULT_VALUE_EMPTY, + ], + ], + [ + [ + 'defaultAuthor' => AuthorType::DEFAULT_CURRENT_USER, + ], + ], + ]; + } + + /** + * Provide data sets with field settings which are considered invalid by the + * {@link validateFieldSettings()} method. The method must return a + * non-empty array of validation error when receiving such field settings. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * true, + * ), + * array( + * array( 'nonExistentKey' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInValidFieldSettings() + { + return [ + [ + [ + // non-existent setting + 'useSeconds' => 23, + ], + ], + [ + [ + //defaultAuthor must be constant + 'defaultAuthor' => 42, + ], + ], + ]; + } + + protected function tearDown(): void + { + unset($this->authors); + parent::tearDown(); + } + + /** + * @covers \Ibexa\Core\FieldType\FieldType::getValidatorConfigurationSchema + */ + public function testValidatorConfigurationSchema() + { + $ft = $this->createFieldTypeUnderTest(); + self::assertEmpty( + $ft->getValidatorConfigurationSchema(), + 'The validator configuration schema does not match what is expected.' + ); + } + + /** + * @covers \Ibexa\Core\FieldType\Author\Type::acceptValue + */ + public function testAcceptValueInvalidType() + { + $this->expectException(InvalidArgumentException::class); + + $ft = $this->createFieldTypeUnderTest(); + $ft->acceptValue($this->createMock(Value::class)); + } + + /** + * @covers \Ibexa\Core\FieldType\Author\Type::acceptValue + */ + public function testAcceptValueInvalidFormat() + { + $this->expectException(InvalidArgumentException::class); + + $ft = $this->createFieldTypeUnderTest(); + $value = new AuthorValue(); + $value->authors = 'This is not a valid author collection'; + $ft->acceptValue($value); + } + + /** + * @covers \Ibexa\Core\FieldType\Author\Type::acceptValue + */ + public function testAcceptValueValidFormat() + { + $ft = $this->createFieldTypeUnderTest(); + $author = new Author(); + $author->name = 'Boba Fett'; + $author->email = 'boba.fett@bountyhunters.com'; + $value = new AuthorValue([$author]); + $newValue = $ft->acceptValue($value); + self::assertSame($value, $newValue); + } + + /** + * @covers \Ibexa\Core\FieldType\Author\Value::__construct + */ + public function testBuildFieldValueWithoutParam() + { + $value = new AuthorValue(); + self::assertInstanceOf(AuthorCollection::class, $value->authors); + self::assertSame([], $value->authors->getArrayCopy()); + } + + /** + * @covers \Ibexa\Core\FieldType\Author\Value::__construct + */ + public function testBuildFieldValueWithParam() + { + $value = new AuthorValue($this->authors); + self::assertInstanceOf(AuthorCollection::class, $value->authors); + self::assertSame($this->authors, $value->authors->getArrayCopy()); + } + + /** + * @covers \Ibexa\Core\FieldType\Author\Value::__toString + */ + public function testFieldValueToString() + { + $value = new AuthorValue($this->authors); + + $authorsName = []; + foreach ($this->authors as $author) { + $authorsName[] = $author->name; + } + + self::assertSame(implode(', ', $authorsName), $value->__toString()); + } + + /** + * @covers \Ibexa\Core\FieldType\Author\AuthorCollection::offsetSet + */ + public function testAddAuthor() + { + $value = new AuthorValue(); + $value->authors[] = $this->authors[0]; + self::assertSame(1, $this->authors[0]->id); + self::assertCount(1, $value->authors); + + $this->authors[1]->id = 10; + $value->authors[] = $this->authors[1]; + self::assertSame(10, $this->authors[1]->id); + + $this->authors[2]->id = -1; + $value->authors[] = $this->authors[2]; + self::assertSame($this->authors[1]->id + 1, $this->authors[2]->id); + self::assertCount(3, $value->authors); + } + + /** + * @covers \Ibexa\Core\FieldType\Author\AuthorCollection::removeAuthorsById + */ + public function testRemoveAuthors() + { + $existingIds = []; + foreach ($this->authors as $author) { + $id = random_int(1, 100); + if (in_array($id, $existingIds)) { + continue; + } + $author->id = $id; + $existingIds[] = $id; + } + + $value = new AuthorValue($this->authors); + $value->authors->removeAuthorsById([$this->authors[1]->id, $this->authors[2]->id]); + self::assertSame(count($this->authors) - 2, count($value->authors)); + self::assertSame([$this->authors[0]], $value->authors->getArrayCopy()); + } + + /** + * Returns the identifier of the field type under test. + * + * @return string + */ + protected function provideFieldTypeIdentifier() + { + return 'ezauthor'; + } + + /** + * Provides data for the getName() test. + * + * @return array + */ + public function provideDataForGetName(): array + { + $authorList = new AuthorValue( + [ + new Author(['id' => 1, 'name' => 'Boba Fett', 'email' => 'boba.fett@example.com']), + new Author(['id' => 2, 'name' => 'Luke Skywalker', 'email' => 'luke.skywalker@example.com']), + ] + ); + + return [ + [$this->getEmptyValueExpectation(), '', [], 'en_GB'], + [$authorList, 'Boba Fett', [], 'en_GB'], + ]; + } +} + +class_alias(AuthorTest::class, 'eZ\Publish\Core\FieldType\Tests\AuthorTest'); diff --git a/tests/lib/FieldType/BaseFieldTypeTest.php b/tests/lib/FieldType/BaseFieldTypeTest.php new file mode 100644 index 0000000000..c36e00fdb5 --- /dev/null +++ b/tests/lib/FieldType/BaseFieldTypeTest.php @@ -0,0 +1,957 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Contracts\Core\FieldType\FieldType; +use Ibexa\Contracts\Core\FieldType\ValidationError; +use Ibexa\Contracts\Core\FieldType\Value as SPIValue; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition; +use PHPUnit\Framework\TestCase; + +abstract class BaseFieldTypeTest extends TestCase +{ + /** + * Generic cache for the getFieldTypeUnderTest() method. + * + * @var \Ibexa\Contracts\Core\FieldType\FieldType + */ + private $fieldTypeUnderTest; + + /** + * Returns the identifier of the field type under test. + * + * @return string + */ + abstract protected function provideFieldTypeIdentifier(); + + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\FieldType + */ + abstract protected function createFieldTypeUnderTest(); + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + abstract protected function getValidatorConfigurationSchemaExpectation(); + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + abstract protected function getSettingsSchemaExpectation(); + + /** + * Returns the empty value expected from the field type. + * + * @return mixed + */ + abstract protected function getEmptyValueExpectation(); + + /** + * Data provider for invalid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The invalid + * input to acceptValue(), 2. The expected exception type as a string. For + * example: + * + * <code> + * return array( + * array( + * new \stdClass(), + * InvalidArgumentException::class, + * ), + * array( + * array(), + * InvalidArgumentException::class, + * ), + * // ... + * ); + * </code> + * + * @return array + */ + abstract public function provideInvalidInputForAcceptValue(); + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + abstract public function provideValidInputForAcceptValue(); + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + abstract public function provideInputForToHash(); + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + abstract public function provideInputForFromHash(); + + /** + * Provides data for the getName() test. + * + * @return array + */ + abstract public function provideDataForGetName(): array; + + /** + * Provide data sets with field settings which are considered valid by the + * {@link validateFieldSettings()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten + * if a FieldType supports field settings! + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( 'rows' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidFieldSettings() + { + return [ + [ + [], + ], + ]; + } + + /** + * Provide data sets with field settings which are considered invalid by the + * {@link validateFieldSettings()} method. The method must return a + * non-empty array of validation error when receiving such field settings. + * + * ATTENTION: This is a default implementation, which must be overwritten + * if a FieldType supports field settings! + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * true, + * ), + * array( + * array( 'nonExistentKey' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInValidFieldSettings() + { + return [ + [ + ['nonempty'], + ], + ]; + } + + /** + * Provide data sets with validator configurations which are considered + * valid by the {@link validateValidatorConfiguration()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten + * if a FieldType supports validators! + * + * Returns an array of data provider sets with a single argument: A valid + * set of validator configurations. + * + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( + * 'IntegerValueValidator' => array( + * 'minIntegerValue' => 0, + * 'maxIntegerValue' => 23, + * ) + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidValidatorConfiguration() + { + return [ + [ + [], + ], + ]; + } + + /** + * Provide data sets with validator configurations which are considered + * invalid by the {@link validateValidatorConfiguration()} method. The + * method must return a non-empty array of valiation errors when receiving + * one of the provided values. + * + * ATTENTION: This is a default implementation, which must be overwritten + * if a FieldType supports validators! + * + * Returns an array of data provider sets with a single argument: A valid + * set of validator configurations. + * + * For example: + * + * <code> + * return array( + * array( + * array( + * 'NonExistentValidator' => array(), + * ), + * ), + * array( + * array( + * // Typos + * 'InTEgervALUeVALIdator' => array( + * 'minIntegerValue' => 0, + * 'maxIntegerValue' => 23, + * ) + * ) + * ), + * array( + * array( + * 'IntegerValueValidator' => array( + * // Incorrect value types + * 'minIntegerValue' => true, + * 'maxIntegerValue' => false, + * ) + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInvalidValidatorConfiguration() + { + return [ + [ + [ + 'NonExistentValidator' => [], + ], + ], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings and + * field value which are considered valid by the {@link validate()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten if + * a FieldType supports validation! + * + * For example: + * + * <code> + * return array( + * array( + * array( + * "validatorConfiguration" => array( + * "StringLengthValidator" => array( + * "minStringLength" => 2, + * "maxStringLength" => 10, + * ), + * ), + * ), + * new TextLineValue( "lalalala" ), + * ), + * array( + * array( + * "fieldSettings" => array( + * 'isMultiple' => true + * ), + * ), + * new CountryValue( + * array( + * "BE" => array( + * "Name" => "Belgium", + * "Alpha2" => "BE", + * "Alpha3" => "BEL", + * "IDC" => 32, + * ), + * ), + * ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidDataForValidate() + { + return [ + [ + [], + $this->createMock(SPIValue::class), + ], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings, + * field value and corresponding validation errors returned by + * the {@link validate()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten + * if a FieldType supports validation! + * + * For example: + * + * <code> + * return array( + * array( + * array( + * "validatorConfiguration" => array( + * "IntegerValueValidator" => array( + * "minIntegerValue" => 5, + * "maxIntegerValue" => 10 + * ), + * ), + * ), + * new IntegerValue( 3 ), + * array( + * new ValidationError( + * "The value can not be lower than %size%.", + * null, + * array( + * "%size%" => 5 + * ), + * ), + * ), + * ), + * array( + * array( + * "fieldSettings" => array( + * "isMultiple" => false + * ), + * ), + * new CountryValue( + * "BE" => array( + * "Name" => "Belgium", + * "Alpha2" => "BE", + * "Alpha3" => "BEL", + * "IDC" => 32, + * ), + * "FR" => array( + * "Name" => "France", + * "Alpha2" => "FR", + * "Alpha3" => "FRA", + * "IDC" => 33, + * ), + * ) + * ), + * array( + * new ValidationError( + * "Field definition does not allow multiple countries to be selected." + * ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInvalidDataForValidate() + { + return [ + [ + [], + $this->createMock(SPIValue::class), + [], + ], + ]; + } + + /** + * Retrieves a test wide cached version of the field type under test. + * + * Uses {@link createFieldTypeUnderTest()} to create the instance + * initially. + * + * @return \Ibexa\Contracts\Core\FieldType\FieldType + */ + protected function getFieldTypeUnderTest() + { + if (!isset($this->fieldTypeUnderTest)) { + $this->fieldTypeUnderTest = $this->createFieldTypeUnderTest(); + } + + return $this->fieldTypeUnderTest; + } + + public function testGetFieldTypeIdentifier() + { + self::assertSame( + $this->provideFieldTypeIdentifier(), + $this->getFieldTypeUnderTest()->getFieldTypeIdentifier() + ); + } + + /** + * @dataProvider provideDataForGetName + */ + public function testGetName( + SPIValue $value, + string $expected, + array $fieldSettings = [], + string $languageCode = 'en_GB' + ): void { + $fieldDefinitionMock = $this->getFieldDefinitionMock($fieldSettings); + + self::assertSame( + $expected, + $this->getFieldTypeUnderTest()->getName($value, $fieldDefinitionMock, $languageCode) + ); + } + + public function testValidatorConfigurationSchema() + { + $fieldType = $this->getFieldTypeUnderTest(); + + self::assertSame( + $this->getValidatorConfigurationSchemaExpectation(), + $fieldType->getValidatorConfigurationSchema(), + 'Validator configuration schema not returned correctly.' + ); + } + + public function testSettingsSchema() + { + $fieldType = $this->getFieldTypeUnderTest(); + + self::assertSame( + $this->getSettingsSchemaExpectation(), + $fieldType->getSettingsSchema(), + 'Settings schema not returned correctly.' + ); + } + + public function testEmptyValue() + { + $fieldType = $this->getFieldTypeUnderTest(); + + $this->assertEquals( + $this->getEmptyValueExpectation(), + $fieldType->getEmptyValue() + ); + } + + /** + * @param mixed $inputValue + * @param mixed $expectedOutputValue + * + * @dataProvider provideValidInputForAcceptValue + */ + public function testAcceptValue($inputValue, $expectedOutputValue) + { + $fieldType = $this->getFieldTypeUnderTest(); + + $outputValue = $fieldType->acceptValue($inputValue); + + $this->assertEquals( + $expectedOutputValue, + $outputValue, + 'acceptValue() did not convert properly.' + ); + } + + /** + * Tests that default empty value is unchanged by acceptValue() method. + */ + public function testAcceptGetEmptyValue() + { + $fieldType = $this->getFieldTypeUnderTest(); + $emptyValue = $fieldType->getEmptyValue(); + + $acceptedEmptyValue = $fieldType->acceptValue($emptyValue); + + $this->assertEquals( + $emptyValue, + $acceptedEmptyValue, + 'acceptValue() did not convert properly.' + ); + } + + /** + * @param mixed $inputValue + * + * @dataProvider provideInvalidInputForAcceptValue + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testAcceptValueFailsOnInvalidValues( + $inputValue, + string $expectedException + ): void { + $fieldType = $this->getFieldTypeUnderTest(); + + $this->expectException($expectedException); + $fieldType->acceptValue($inputValue); + } + + /** + * @param mixed $inputValue + * @param array $expectedResult + * + * @dataProvider provideInputForToHash + */ + public function testToHash($inputValue, $expectedResult) + { + $fieldType = $this->getFieldTypeUnderTest(); + + $actualResult = $fieldType->toHash($inputValue); + + $this->assertIsValidHashValue($actualResult); + + if (is_object($expectedResult) || is_array($expectedResult)) { + $this->assertEquals( + $expectedResult, + $actualResult, + 'toHash() method did not create expected result.' + ); + } else { + $this->assertSame( + $expectedResult, + $actualResult, + 'toHash() method did not create expected result.' + ); + } + } + + /** + * @param mixed $inputValue + * @param array $expectedResult + * + * @dataProvider provideInputForFromHash + */ + public function testFromHash($inputHash, $expectedResult) + { + $this->assertIsValidHashValue($inputHash); + + $fieldType = $this->getFieldTypeUnderTest(); + + $actualResult = $fieldType->fromHash($inputHash); + + if (is_object($expectedResult) || is_array($expectedResult)) { + $this->assertEquals( + $expectedResult, + $actualResult, + 'fromHash() method did not create expected result.' + ); + } else { + $this->assertSame( + $expectedResult, + $actualResult, + 'fromHash() method did not create expected result.' + ); + } + } + + public function testEmptyValueIsEmpty() + { + $fieldType = $this->getFieldTypeUnderTest(); + + $this->assertTrue( + $fieldType->isEmptyValue($fieldType->getEmptyValue()) + ); + } + + /** + * @param mixed $inputSettings + * + * @dataProvider provideValidFieldSettings + */ + public function testValidateFieldSettingsValid($inputSettings) + { + $fieldType = $this->getFieldTypeUnderTest(); + + $validationResult = $fieldType->validateFieldSettings($inputSettings); + + $this->assertIsArray( + $validationResult, + 'The method validateFieldSettings() must return an array.' + ); + $this->assertEquals( + [], + $validationResult, + 'validateFieldSettings() did not consider the input settings valid.' + ); + } + + /** + * @param mixed $inputSettings + * + * @dataProvider provideInvalidFieldSettings + */ + public function testValidateFieldSettingsInvalid($inputSettings) + { + $fieldType = $this->getFieldTypeUnderTest(); + + $validationResult = $fieldType->validateFieldSettings($inputSettings); + + $this->assertIsArray( + $validationResult, + 'The method validateFieldSettings() must return an array.' + ); + + $this->assertNotEquals( + [], + $validationResult, + 'validateFieldSettings() did consider the input settings valid, which should be invalid.' + ); + + foreach ($validationResult as $actualResultElement) { + $this->assertInstanceOf( + ValidationError::class, + $actualResultElement, + 'Validation result of incorrect type.' + ); + } + } + + /** + * @param mixed $inputConfiguration + * + * @dataProvider provideValidValidatorConfiguration + */ + public function testValidateValidatorConfigurationValid($inputConfiguration) + { + $fieldType = $this->getFieldTypeUnderTest(); + + $validationResult = $fieldType->validateValidatorConfiguration($inputConfiguration); + + $this->assertIsArray( + $validationResult, + 'The method validateValidatorConfiguration() must return an array.' + ); + $this->assertEquals( + [], + $validationResult, + 'validateValidatorConfiguration() did not consider the input configuration valid.' + ); + } + + /** + * @param mixed $inputConfiguration + * + * @dataProvider provideInvalidValidatorConfiguration + */ + public function testValidateValidatorConfigurationInvalid($inputConfiguration) + { + $fieldType = $this->getFieldTypeUnderTest(); + + $validationResult = $fieldType->validateValidatorConfiguration($inputConfiguration); + + $this->assertIsArray( + $validationResult, + 'The method validateValidatorConfiguration() must return an array.' + ); + + $this->assertNotEquals( + [], + $validationResult, + 'validateValidatorConfiguration() did consider the input settings valid, which should be invalid.' + ); + + foreach ($validationResult as $actualResultElement) { + $this->assertInstanceOf( + ValidationError::class, + $actualResultElement, + 'Validation result of incorrect type.' + ); + } + } + + /** + * @param mixed $inputConfiguration + * + * @dataProvider provideValidFieldSettings + */ + public function testFieldSettingsToHash($inputSettings) + { + $fieldType = $this->getFieldTypeUnderTest(); + + $hash = $fieldType->fieldSettingsToHash($inputSettings); + + $this->assertIsValidHashValue($hash); + } + + /** + * @param mixed $inputConfiguration + * + * @dataProvider provideValidValidatorConfiguration + */ + public function testValidatorConfigurationToHash($inputConfiguration) + { + $fieldType = $this->getFieldTypeUnderTest(); + + $hash = $fieldType->validatorConfigurationToHash($inputConfiguration); + + $this->assertIsValidHashValue($hash); + } + + /** + * @param mixed $inputConfiguration + * + * @dataProvider provideValidFieldSettings + */ + public function testFieldSettingsFromHash($inputSettings) + { + $fieldType = $this->getFieldTypeUnderTest(); + + $hash = $fieldType->fieldSettingsToHash($inputSettings); + $restoredSettings = $fieldType->fieldSettingsFromHash($hash); + + $this->assertEquals($inputSettings, $restoredSettings); + } + + /** + * @param mixed $inputConfiguration + * + * @dataProvider provideValidValidatorConfiguration + */ + public function testValidatorConfigurationFromHash($inputConfiguration) + { + $fieldType = $this->getFieldTypeUnderTest(); + + $hash = $fieldType->validatorConfigurationToHash($inputConfiguration); + $restoredConfiguration = $fieldType->validatorConfigurationFromHash($hash); + + $this->assertEquals($inputConfiguration, $restoredConfiguration); + } + + /** + * Asserts that the given $actualHash complies to the rules for hashes. + * + * @param mixed $actualHash + * @param array $keyChain + */ + protected function assertIsValidHashValue($actualHash, $keyChain = []) + { + switch ($actualHashType = gettype($actualHash)) { + case 'boolean': + case 'integer': + case 'double': + case 'string': + case 'NULL': + // All valid, just return + return; + + case 'array': + foreach ($actualHash as $key => $childHash) { + $this->assertIsValidHashValue( + $childHash, + array_merge($keyChain, [$key]) + ); + } + + return; + + case 'resource': + case 'object': + $this->fail( + sprintf( + 'Value for $hash[%s] is of invalid type "%s".', + implode('][', $keyChain), + $actualHashType + ) + ); + } + } + + /** + * @dataProvider provideValidDataForValidate + */ + public function testValidateValid($fieldDefinitionData, $value) + { + $validationErrors = $this->doValidate($fieldDefinitionData, $value); + + $this->assertIsArray($validationErrors); + $this->assertEmpty($validationErrors, "Got value:\n" . var_export($validationErrors, true)); + } + + /** + * @dataProvider provideInvalidDataForValidate + */ + public function testValidateInvalid($fieldDefinitionData, $value, $errors) + { + $validationErrors = $this->doValidate($fieldDefinitionData, $value); + + $this->assertIsArray($validationErrors); + $this->assertEquals($errors, $validationErrors); + } + + protected function doValidate($fieldDefinitionData, $value) + { + $fieldType = $this->getFieldTypeUnderTest(); + + /** @var \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition|\PHPUnit\Framework\MockObject\MockObject $fieldDefinitionMock */ + $fieldDefinitionMock = $this->createMock(APIFieldDefinition::class); + + foreach ($fieldDefinitionData as $method => $data) { + if ($method === 'validatorConfiguration') { + $fieldDefinitionMock + ->method('getValidatorConfiguration') + ->willReturn($data); + } + + if ($method === 'fieldSettings') { + $fieldDefinitionMock + ->method('getFieldSettings') + ->willReturn($data); + } + } + + return $fieldType->validate($fieldDefinitionMock, $value); + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getFieldDefinitionMock(array $fieldSettings) + { + /** @var |\PHPUnit\Framework\MockObject\MockObject $fieldDefinitionMock */ + $fieldDefinitionMock = $this->createMock(APIFieldDefinition::class); + $fieldDefinitionMock + ->method('getFieldSettings') + ->willReturn($fieldSettings); + + return $fieldDefinitionMock; + } + + // @todo: More test methods … +} + +class_alias(BaseFieldTypeTest::class, 'eZ\Publish\SPI\FieldType\Tests\FieldTypeTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/BinaryBaseTest.php b/tests/lib/FieldType/BinaryBaseTest.php similarity index 93% rename from eZ/Publish/Core/FieldType/Tests/BinaryBaseTest.php rename to tests/lib/FieldType/BinaryBaseTest.php index 8fb187106a..436ead5880 100644 --- a/eZ/Publish/Core/FieldType/Tests/BinaryBaseTest.php +++ b/tests/lib/FieldType/BinaryBaseTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests; +namespace Ibexa\Tests\Core\FieldType; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Validator\FileExtensionBlackListValidator; -use eZ\Publish\Core\FieldType\Value; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator; +use Ibexa\Core\FieldType\Value; /** * Base class for binary field types. @@ -201,3 +201,5 @@ public function provideInvalidValidatorConfiguration() ]; } } + +class_alias(BinaryBaseTest::class, 'eZ\Publish\Core\FieldType\Tests\BinaryBaseTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/BinaryFileTest.php b/tests/lib/FieldType/BinaryFileTest.php similarity index 97% rename from eZ/Publish/Core/FieldType/Tests/BinaryFileTest.php rename to tests/lib/FieldType/BinaryFileTest.php index a88283f8a1..b8cf9bfcfe 100644 --- a/eZ/Publish/Core/FieldType/Tests/BinaryFileTest.php +++ b/tests/lib/FieldType/BinaryFileTest.php @@ -4,20 +4,20 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests; +namespace Ibexa\Tests\Core\FieldType; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue; -use eZ\Publish\Core\FieldType\BinaryFile\Type as BinaryFileType; -use eZ\Publish\Core\FieldType\BinaryFile\Value as BinaryFileValue; -use eZ\Publish\Core\FieldType\FieldType; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\FieldType\BinaryBase\RouteAwarePathGenerator; +use Ibexa\Contracts\Core\FieldType\BinaryBase\RouteAwarePathGenerator; +use Ibexa\Core\Base\Exceptions\InvalidArgumentValue; +use Ibexa\Core\FieldType\BinaryFile\Type as BinaryFileType; +use Ibexa\Core\FieldType\BinaryFile\Value as BinaryFileValue; +use Ibexa\Core\FieldType\FieldType; +use Ibexa\Core\FieldType\ValidationError; /** * @group fieldType * @group ezbinaryfile * - * @covers \eZ\Publish\Core\FieldType\BinaryFile\Type + * @covers \Ibexa\Core\FieldType\BinaryFile\Type */ class BinaryFileTest extends BinaryBaseTest { @@ -30,7 +30,7 @@ class BinaryFileTest extends BinaryBaseTest * NOT take care for test case wide caching of the field type, just return * a new instance from this method! * - * @return \eZ\Publish\Core\FieldType\FieldType + * @return \Ibexa\Core\FieldType\FieldType */ protected function createFieldTypeUnderTest(): FieldType { @@ -648,3 +648,5 @@ private function getRouteAwarePathGenerator(): RouteAwarePathGenerator return $mock; } } + +class_alias(BinaryFileTest::class, 'eZ\Publish\Core\FieldType\Tests\BinaryFileTest'); diff --git a/tests/lib/FieldType/CheckboxTest.php b/tests/lib/FieldType/CheckboxTest.php new file mode 100644 index 0000000000..284c9bcb33 --- /dev/null +++ b/tests/lib/FieldType/CheckboxTest.php @@ -0,0 +1,305 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Checkbox\Type as Checkbox; +use Ibexa\Core\FieldType\Checkbox\Value as CheckboxValue; + +/** + * @group fieldType + * @group ezboolean + */ +class CheckboxTest extends FieldTypeTest +{ + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Contracts\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new Checkbox(); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return []; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return []; + } + + /** + * Returns the empty value expected from the field type. + * + * @return \Ibexa\Core\FieldType\Checkbox\Value + */ + protected function getEmptyValueExpectation() + { + return new CheckboxValue(false); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + 23, + InvalidArgumentException::class, + ], + [ + new CheckboxValue(42), + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + false, + new CheckboxValue(false), + ], + [ + true, + new CheckboxValue(true), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new CheckboxValue(true), + true, + ], + [ + new CheckboxValue(false), + false, + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + true, + new CheckboxValue(true), + ], + [ + false, + new CheckboxValue(false), + ], + ]; + } + + /** + * @covers \Ibexa\Core\FieldType\Checkbox\Type::toPersistenceValue + */ + public function testToPersistenceValue() + { + $ft = $this->createFieldTypeUnderTest(); + $fieldValue = $ft->toPersistenceValue(new CheckboxValue(true)); + + self::assertTrue($fieldValue->data); + self::assertSame(1, $fieldValue->sortKey); + } + + /** + * @covers \Ibexa\Core\FieldType\Checkbox\Value::__construct + */ + public function testBuildFieldValueWithParam() + { + $bool = true; + $value = new CheckboxValue($bool); + self::assertSame($bool, $value->bool); + } + + /** + * @covers \Ibexa\Core\FieldType\Checkbox\Value::__construct + */ + public function testBuildFieldValueWithoutParam() + { + $value = new CheckboxValue(); + self::assertFalse($value->bool); + } + + /** + * @covers \Ibexa\Core\FieldType\Checkbox\Value::__toString + */ + public function testFieldValueToString() + { + $valueTrue = new CheckboxValue(true); + $valueFalse = new CheckboxValue(false); + self::assertSame('1', (string)$valueTrue); + self::assertSame('0', (string)$valueFalse); + } + + protected function provideFieldTypeIdentifier() + { + return 'ezboolean'; + } + + public function provideDataForGetName(): array + { + return [ + [new CheckboxValue(true), '1', [], 'en_GB'], + [new CheckboxValue(false), '0', [], 'en_GB'], + ]; + } + + /** + * @dataProvider provideForValueIsNeverEmpty + */ + public function testValueIsNeverEmpty(CheckboxValue $value): void + { + $fieldType = $this->getFieldTypeUnderTest(); + + self::assertFalse($fieldType->isEmptyValue($value)); + } + + /** + * @return iterable<array{ + * \Ibexa\Core\FieldType\Checkbox\Value, + * }> + */ + public function provideForValueIsNeverEmpty(): iterable + { + yield [new CheckboxValue(true)]; + yield [new CheckboxValue(false)]; + } + + public function testEmptyValueIsEmpty(): void + { + self::markTestSkipped('Value of Checkbox fieldtype is never considered empty'); + } +} + +class_alias(CheckboxTest::class, 'eZ\Publish\Core\FieldType\Tests\CheckboxTest'); diff --git a/tests/lib/FieldType/CountryTest.php b/tests/lib/FieldType/CountryTest.php new file mode 100644 index 0000000000..d20c28ebda --- /dev/null +++ b/tests/lib/FieldType/CountryTest.php @@ -0,0 +1,622 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Country\Exception\InvalidValue; +use Ibexa\Core\FieldType\Country\Type as Country; +use Ibexa\Core\FieldType\Country\Value as CountryValue; +use Ibexa\Core\FieldType\ValidationError; + +/** + * @group fieldType + * @group ezcountry + */ +class CountryTest extends FieldTypeTest +{ + protected function provideFieldTypeIdentifier() + { + return 'ezcountry'; + } + + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new Country( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + 'FR' => [ + 'Name' => 'France', + 'Alpha2' => 'FR', + 'Alpha3' => 'FRA', + 'IDC' => 33, + ], + 'NO' => [ + 'Name' => 'Norway', + 'Alpha2' => 'NO', + 'Alpha3' => 'NOR', + 'IDC' => 47, + ], + 'KP' => [ + 'Name' => "Korea, Democratic People's Republic of", + 'Alpha2' => 'KP', + 'Alpha3' => 'PRK', + 'IDC' => 850, + ], + 'TF' => [ + 'Name' => 'French Southern Territories', + 'Alpha2' => 'TF', + 'Alpha3' => 'ATF', + 'IDC' => 0, + ], + 'CF' => [ + 'Name' => 'Central African Republic', + 'Alpha2' => 'CF', + 'Alpha3' => 'CAF', + 'IDC' => 236, + ], + ] + ); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return []; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return [ + 'isMultiple' => [ + 'type' => 'boolean', + 'default' => false, + ], + ]; + } + + /** + * Returns the empty value expected from the field type. + * + * @return \Ibexa\Core\FieldType\Country\Value + */ + protected function getEmptyValueExpectation() + { + return new CountryValue(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + 'LegoLand', + InvalidArgumentException::class, + ], + [ + ['Norway', 'France', 'LegoLand'], + InvalidValue::class, + ], + [ + ['FR', 'BE', 'LE'], + InvalidValue::class, + ], + [ + ['FRE', 'BEL', 'LEG'], + InvalidValue::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + ['BE', 'FR'], + new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + 'FR' => [ + 'Name' => 'France', + 'Alpha2' => 'FR', + 'Alpha3' => 'FRA', + 'IDC' => 33, + ], + ] + ), + ], + [ + ['Belgium'], + new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + ] + ), + ], + [ + ['BE'], + new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + ] + ), + ], + [ + ['BEL'], + new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + ] + ), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + ] + ), + ['BE'], + ], + [ + new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + 'FR' => [ + 'Name' => 'France', + 'Alpha2' => 'FR', + 'Alpha3' => 'FRA', + 'IDC' => 33, + ], + ] + ), + ['BE', 'FR'], + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + ['BE'], + new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + ] + ), + ], + [ + ['BE', 'FR'], + new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + 'FR' => [ + 'Name' => 'France', + 'Alpha2' => 'FR', + 'Alpha3' => 'FRA', + 'IDC' => 33, + ], + ] + ), + ], + ]; + } + + public function provideDataForGetName(): array + { + return [ + [new CountryValue(), '', [], 'en_GB'], + [new CountryValue(['FR' => ['Name' => 'France']]), 'France', [], 'en_GB'], + [ + new CountryValue(['FR' => ['Name' => 'France'], 'DE' => ['Name' => 'Deutschland']]), + 'France, Deutschland', + [], + 'en_GB', + ], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings and + * field value which are considered valid by the {@link validate()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten if + * a FieldType supports validation! + * + * For example: + * + * <code> + * return array( + * array( + * array( + * "validatorConfiguration" => array( + * "StringLengthValidator" => array( + * "minStringLength" => 2, + * "maxStringLength" => 10, + * ), + * ), + * ), + * new TextLineValue( "lalalala" ), + * ), + * array( + * array( + * "fieldSettings" => array( + * 'isMultiple' => true + * ), + * ), + * new CountryValue( + * array( + * "BE" => array( + * "Name" => "Belgium", + * "Alpha2" => "BE", + * "Alpha3" => "BEL", + * "IDC" => 32, + * ), + * ), + * ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidDataForValidate() + { + return [ + [ + [ + 'fieldSettings' => [ + 'isMultiple' => true, + ], + ], + new CountryValue(), + ], + [ + [ + 'fieldSettings' => [ + 'isMultiple' => false, + ], + ], + new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + ] + ), + ], + [ + [ + 'fieldSettings' => [ + 'isMultiple' => true, + ], + ], + new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + 'FR' => [ + 'Name' => 'France', + 'Alpha2' => 'FR', + 'Alpha3' => 'FRA', + 'IDC' => 33, + ], + ] + ), + ], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings, + * field value and corresponding validation errors returned by + * the {@link validate()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten + * if a FieldType supports validation! + * + * For example: + * + * <code> + * return array( + * array( + * array( + * "validatorConfiguration" => array( + * "IntegerValueValidator" => array( + * "minIntegerValue" => 5, + * "maxIntegerValue" => 10 + * ), + * ), + * ), + * new IntegerValue( 3 ), + * array( + * new ValidationError( + * "The value can not be lower than %size%.", + * null, + * array( + * "size" => 5 + * ), + * ), + * ), + * ), + * array( + * array( + * "fieldSettings" => array( + * "isMultiple" => false + * ), + * ), + * new CountryValue( + * "BE" => array( + * "Name" => "Belgium", + * "Alpha2" => "BE", + * "Alpha3" => "BEL", + * "IDC" => 32, + * ), + * "FR" => array( + * "Name" => "France", + * "Alpha2" => "FR", + * "Alpha3" => "FRA", + * "IDC" => 33, + * ), + * ) + * ), + * array( + * new ValidationError( + * "Field definition does not allow multiple countries to be selected." + * ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInvalidDataForValidate() + { + return [ + [ + [ + 'fieldSettings' => [ + 'isMultiple' => false, + ], + ], + new CountryValue( + [ + 'BE' => [ + 'Name' => 'Belgium', + 'Alpha2' => 'BE', + 'Alpha3' => 'BEL', + 'IDC' => 32, + ], + 'FR' => [ + 'Name' => 'France', + 'Alpha2' => 'FR', + 'Alpha3' => 'FRA', + 'IDC' => 33, + ], + ] + ), + [ + new ValidationError( + 'Field definition does not allow multiple countries to be selected.', + null, + [], + 'countries' + ), + ], + ], + [ + [ + 'fieldSettings' => [ + 'isMultiple' => true, + ], + ], + new CountryValue( + [ + 'LE' => [ + 'Name' => 'LegoLand', + 'Alpha2' => 'LE', + 'Alpha3' => 'LEG', + 'IDC' => 888, + ], + ] + ), + [ + new ValidationError( + "Country with Alpha2 code '%alpha2%' is not defined in FieldType settings.", + null, + [ + '%alpha2%' => 'LE', + ], + 'countries' + ), + ], + ], + ]; + } +} + +class_alias(CountryTest::class, 'eZ\Publish\Core\FieldType\Tests\CountryTest'); diff --git a/tests/lib/FieldType/DataProvider/UserValidatorConfigurationSchemaProvider.php b/tests/lib/FieldType/DataProvider/UserValidatorConfigurationSchemaProvider.php new file mode 100644 index 0000000000..e99de374be --- /dev/null +++ b/tests/lib/FieldType/DataProvider/UserValidatorConfigurationSchemaProvider.php @@ -0,0 +1,54 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\FieldType\DataProvider; + +/** + * @internal + */ +final class UserValidatorConfigurationSchemaProvider +{ + /** + * @return array<string, array<string, array{type: string, default: ?scalar}>> + */ + public function getExpectedValidatorConfigurationSchema(): array + { + return [ + 'PasswordValueValidator' => [ + 'requireAtLeastOneUpperCaseCharacter' => [ + 'type' => 'int', + 'default' => 1, + ], + 'requireAtLeastOneLowerCaseCharacter' => [ + 'type' => 'int', + 'default' => 1, + ], + 'requireAtLeastOneNumericCharacter' => [ + 'type' => 'int', + 'default' => 1, + ], + 'requireAtLeastOneNonAlphanumericCharacter' => [ + 'type' => 'int', + 'default' => null, + ], + 'requireNewPassword' => [ + 'type' => 'int', + 'default' => null, + ], + 'requireNotCompromisedPassword' => [ + 'type' => 'bool', + 'default' => false, + ], + 'minLength' => [ + 'type' => 'int', + 'default' => 10, + ], + ], + ]; + } +} diff --git a/tests/lib/FieldType/DateAndTimeTest.php b/tests/lib/FieldType/DateAndTimeTest.php new file mode 100644 index 0000000000..61a3c9a6e9 --- /dev/null +++ b/tests/lib/FieldType/DateAndTimeTest.php @@ -0,0 +1,442 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use DateInterval; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\DateAndTime\Type as DateAndTime; +use Ibexa\Core\FieldType\DateAndTime\Value as DateAndTimeValue; +use stdClass; + +/** + * @group fieldType + * @group ezdatetime + */ +class DateAndTimeTest extends FieldTypeTest +{ + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new DateAndTime(); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return []; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return [ + 'useSeconds' => [ + 'type' => 'bool', + 'default' => false, + ], + 'defaultType' => [ + 'type' => 'choice', + 'default' => DateAndTime::DEFAULT_EMPTY, + ], + 'dateInterval' => [ + 'type' => 'dateInterval', + 'default' => null, + ], + ]; + } + + /** + * Returns the empty value expected from the field type. + */ + protected function getEmptyValueExpectation() + { + return new DateAndTimeValue(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + [], + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + null, + new DateAndTimeValue(), + ], + [ + '2012-08-28 12:20 Europe/Berlin', + DateAndTimeValue::fromString('2012-08-28 12:20 Europe/Berlin'), + ], + [ + 1346149200, + DateAndTimeValue::fromTimestamp(1346149200), + ], + [ + ($dateTime = new \DateTime()), + new DateAndTimeValue($dateTime), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new DateAndTimeValue(), + null, + ], + [ + new DateAndTimeValue($date = new \DateTime('Tue, 28 Aug 2012 12:20:00 +0200')), + [ + 'rfc850' => $date->format(\DateTime::RFC850), + 'timestamp' => $date->getTimeStamp(), + ], + ], + ]; + } + + /** + * @param mixed $inputValue + * @param array $expectedResult + * + * @dataProvider provideInputForFromHash + */ + public function testFromHash($inputHash, $expectedResult) + { + $this->assertIsValidHashValue($inputHash); + + $fieldType = $this->getFieldTypeUnderTest(); + + $actualResult = $fieldType->fromHash($inputHash); + + // Tests may run slowly. Allow 20 seconds margin of error. + $this->assertGreaterThanOrEqual( + $expectedResult, + $actualResult, + 'fromHash() method did not create expected result.' + ); + if ($expectedResult->value !== null) { + $this->assertLessThan( + $expectedResult->value->add(new DateInterval('PT20S')), + $actualResult->value, + 'fromHash() method did not create expected result.' + ); + } + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * + * @return array + */ + public function provideInputForFromHash() + { + $date = new \DateTime('Tue, 28 Aug 2012 12:20:00 +0200'); + + return [ + [ + null, + new DateAndTimeValue(), + ], + [ + [ + 'rfc850' => $date->format(\DateTime::RFC850), + 'timestamp' => $date->getTimeStamp(), + ], + new DateAndTimeValue($date), + ], + [ + [ + 'timestamp' => $date->getTimeStamp(), + ], + DateAndTimeValue::fromTimestamp($date->getTimeStamp()), + ], + ]; + } + + /** + * @param mixed $inputValue + * @param string $intervalSpec + * + * @dataProvider provideInputForTimeStringFromHash + */ + public function testTimeStringFromHash($inputHash, $intervalSpec) + { + $this->assertIsValidHashValue($inputHash); + + $fieldType = $this->getFieldTypeUnderTest(); + + $expectedResult = new DateAndTimeValue(new \DateTime()); + $expectedResult->value->add(new DateInterval($intervalSpec)); + + $actualResult = $fieldType->fromHash($inputHash); + + // Tests may run slowly. Allow 20 seconds margin of error. + $this->assertGreaterThanOrEqual( + $expectedResult, + $actualResult, + 'fromHash() method did not create expected result.' + ); + if ($expectedResult->value !== null) { + $this->assertLessThan( + $expectedResult->value->add(new DateInterval('PT20S')), + $actualResult->value, + 'fromHash() method did not create expected result.' + ); + } + } + + /** + * Provide input to testTimeStringFromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. A valid + * timestring input to fromHash(), 2. An interval specification string, + * from which can be created a DateInterval which can be added to the + * current DateTime, to be compared with the expected return value from + * fromHash(). + * + * @return array + */ + public function provideInputForTimeStringFromHash() + { + return [ + [ + [ + 'timestring' => 'now', + ], + 'P0Y', + ], + [ + [ + 'timestring' => '+42 seconds', + ], + 'PT42S', + ], + [ + [ + 'timestring' => '+3 months 2 days 5 hours', + ], + 'P3M2DT5H', + ], + ]; + } + + /** + * Provide data sets with field settings which are considered valid by the + * {@link validateFieldSettings()} method. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( 'rows' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidFieldSettings() + { + return [ + [ + [], + ], + [ + [ + 'useSeconds' => true, + 'defaultType' => DateAndTime::DEFAULT_EMPTY, + ], + ], + [ + [ + 'useSeconds' => false, + 'defaultType' => DateAndTime::DEFAULT_CURRENT_DATE, + ], + ], + [ + [ + 'useSeconds' => false, + 'defaultType' => DateAndTime::DEFAULT_CURRENT_DATE_ADJUSTED, + 'dateInterval' => new DateInterval('P2Y'), + ], + ], + ]; + } + + /** + * Provide data sets with field settings which are considered invalid by the + * {@link validateFieldSettings()} method. The method must return a + * non-empty array of validation error when receiving such field settings. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * true, + * ), + * array( + * array( 'nonExistentKey' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInValidFieldSettings() + { + return [ + [ + [ + // useSeconds must be bool + 'useSeconds' => 23, + ], + ], + [ + [ + // defaultType must be constant + 'defaultType' => 42, + ], + ], + [ + [ + // No dateInterval allowed with this defaultType + 'defaultType' => DateAndTime::DEFAULT_EMPTY, + 'dateInterval' => new DateInterval('P2Y'), + ], + ], + [ + [ + // dateInterval must be a \DateInterval + 'defaultType' => DateAndTime::DEFAULT_CURRENT_DATE_ADJUSTED, + 'dateInterval' => new stdClass(), + ], + ], + ]; + } + + protected function provideFieldTypeIdentifier() + { + return 'ezdatetime'; + } + + public function provideDataForGetName(): array + { + return [ + [$this->getEmptyValueExpectation(), '', [], 'en_GB'], + [DateAndTimeValue::fromTimestamp(438512400), 'Thu 1983-24-11 09:00:00', [], 'en_GB'], + ]; + } +} + +class_alias(DateAndTimeTest::class, 'eZ\Publish\Core\FieldType\Tests\DateAndTimeTest'); diff --git a/tests/lib/FieldType/DateTest.php b/tests/lib/FieldType/DateTest.php new file mode 100644 index 0000000000..45b9072d43 --- /dev/null +++ b/tests/lib/FieldType/DateTest.php @@ -0,0 +1,353 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use DateTime; +use DateTimeZone; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Date\Type as Date; +use Ibexa\Core\FieldType\Date\Value as DateValue; + +/** + * @group fieldType + * @group ezdate + */ +class DateTest extends FieldTypeTest +{ + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new Date(); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return []; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return [ + 'defaultType' => [ + 'type' => 'choice', + 'default' => Date::DEFAULT_EMPTY, + ], + ]; + } + + /** + * Returns the empty value expected from the field type. + */ + protected function getEmptyValueExpectation() + { + return new DateValue(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + [], + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + null, + new DateValue(), + ], + [ + ($dateString = '2012-08-28 12:20 EST'), + new DateValue(new DateTime($dateString)), + ], + [ + ($timestamp = 1346149200), + new DateValue( + new DateTime("@{$timestamp}") + ), + ], + [ + DateValue::fromTimestamp($timestamp = 1372895999), + new DateValue(new DateTime("@{$timestamp}")), + ], + [ + ($dateTime = new DateTime()), + new DateValue($dateTime), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new DateValue(), + null, + ], + [ + new DateValue($dateTime = new DateTime()), + [ + 'timestamp' => $dateTime->setTime(0, 0, 0)->getTimestamp(), + 'rfc850' => $dateTime->format(DateTime::RFC850), + ], + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + $dateTime = new DateTime(); + + return [ + [ + null, + new DateValue(), + ], + [ + [ + 'timestamp' => ($timestamp = 1362614400), + ], + new DateValue(new DateTime("@{$timestamp}")), + ], + [ + [ + // Timezone is not abbreviated because PHP converts it to non-abbreviated local name, + // but this is sufficient to test conversion + 'rfc850' => 'Thursday, 04-Jul-13 23:59:59 Europe/Zagreb', + ], + new DateValue( + $dateTime + ->setTimeZone(new DateTimeZone('Europe/Zagreb')) + ->setTimestamp(1372896000) + ), + ], + ]; + } + + /** + * Provide data sets with field settings which are considered valid by the + * {@link validateFieldSettings()} method. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( 'rows' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidFieldSettings() + { + return [ + [ + [], + ], + [ + [ + 'defaultType' => Date::DEFAULT_EMPTY, + ], + ], + [ + [ + 'defaultType' => Date::DEFAULT_CURRENT_DATE, + ], + ], + ]; + } + + /** + * Provide data sets with field settings which are considered invalid by the + * {@link validateFieldSettings()} method. The method must return a + * non-empty array of validation error when receiving such field settings. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * true, + * ), + * array( + * array( 'nonExistentKey' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInValidFieldSettings() + { + return [ + [ + [ + // non-existent setting + 'useSeconds' => 23, + ], + ], + [ + [ + // defaultType must be constant + 'defaultType' => 42, + ], + ], + ]; + } + + protected function provideFieldTypeIdentifier() + { + return 'ezdate'; + } + + public function provideDataForGetName(): array + { + return [ + [$this->getEmptyValueExpectation(), '', [], 'en_GB'], + [new DateValue(new DateTime('11/24/1983')), 'Thursday 24 November 1983', [], 'en_GB'], + ]; + } +} + +class_alias(DateTest::class, 'eZ\Publish\Core\FieldType\Tests\DateTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/EmailAddressTest.php b/tests/lib/FieldType/EmailAddressTest.php similarity index 93% rename from eZ/Publish/Core/FieldType/Tests/EmailAddressTest.php rename to tests/lib/FieldType/EmailAddressTest.php index 01c32e78c0..dd536abb3e 100644 --- a/eZ/Publish/Core/FieldType/Tests/EmailAddressTest.php +++ b/tests/lib/FieldType/EmailAddressTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests; +namespace Ibexa\Tests\Core\FieldType; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\EmailAddress\Type as EmailAddressType; -use eZ\Publish\Core\FieldType\EmailAddress\Value as EmailAddressValue; -use eZ\Publish\Core\FieldType\ValidationError; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\EmailAddress\Type as EmailAddressType; +use Ibexa\Core\FieldType\EmailAddress\Value as EmailAddressValue; +use Ibexa\Core\FieldType\ValidationError; /** * @group fieldType @@ -26,7 +26,7 @@ class EmailAddressTest extends FieldTypeTest * NOT take care for test case wide caching of the field type, just return * a new instance from this method! * - * @return FieldType + * @return \Ibexa\Core\FieldType\FieldType */ protected function createFieldTypeUnderTest() { @@ -79,29 +79,6 @@ protected function getEmptyValueExpectation() return new EmailAddressValue(); } - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ public function provideInvalidInputForAcceptValue() { return [ @@ -537,3 +514,5 @@ public function provideInvalidDataForValidate() ]; } } + +class_alias(EmailAddressTest::class, 'eZ\Publish\Core\FieldType\Tests\EmailAddressTest'); diff --git a/tests/lib/FieldType/EmailAddressValidatorTest.php b/tests/lib/FieldType/EmailAddressValidatorTest.php new file mode 100644 index 0000000000..d9f56d5ae5 --- /dev/null +++ b/tests/lib/FieldType/EmailAddressValidatorTest.php @@ -0,0 +1,110 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Core\FieldType\EmailAddress\Value as EmailAddressValue; +use Ibexa\Core\FieldType\Validator; +use Ibexa\Core\FieldType\Validator\EmailAddressValidator; +use PHPUnit\Framework\TestCase; + +/** + * @todo add more tests, like on validateConstraints method + * @group fieldType + * @group validator + */ +class EmailAddressValidatorTest extends TestCase +{ + /** + * This test ensure an EmailAddressValidator can be created. + */ + public function testConstructor() + { + $this->assertInstanceOf( + Validator::class, + new EmailAddressValidator() + ); + } + + /** + * Tests setting and getting constraints. + * + * @covers \Ibexa\Core\FieldType\Validator::initializeWithConstraints + * @covers \Ibexa\Core\FieldType\Validator::__get + */ + public function testConstraintsInitializeGet() + { + $constraints = [ + 'Extent' => 'regex', + ]; + $validator = new EmailAddressValidator(); + $validator->initializeWithConstraints( + $constraints + ); + $this->assertSame($constraints['Extent'], $validator->Extent); + } + + /** + * Test getting constraints schema. + * + * @covers \Ibexa\Core\FieldType\Validator::getConstraintsSchema + */ + public function testGetConstraintsSchema() + { + $constraintsSchema = [ + 'Extent' => [ + 'type' => 'string', + 'default' => 'regex', + ], + ]; + $validator = new EmailAddressValidator(); + $this->assertSame($constraintsSchema, $validator->getConstraintsSchema()); + } + + /** + * Tests setting and getting constraints. + * + * @covers \Ibexa\Core\FieldType\Validator::__set + * @covers \Ibexa\Core\FieldType\Validator::__get + */ + public function testConstraintsSetGet() + { + $constraints = [ + 'Extent' => 'regex', + ]; + $validator = new EmailAddressValidator(); + $validator->Extent = $constraints['Extent']; + $this->assertSame($constraints['Extent'], $validator->Extent); + } + + public function testValidateCorrectEmailAddresses() + { + $validator = new EmailAddressValidator(); + $validator->Extent = 'regex'; + $emailAddresses = ['john.doe@example.com', 'Info@Ibexa.Co']; + foreach ($emailAddresses as $value) { + $this->assertTrue($validator->validate(new EmailAddressValue($value))); + $this->assertSame([], $validator->getMessage()); + } + } + + /** + * Tests validating a wrong value. + * + * @covers \Ibexa\Core\FieldType\Validator\EmailAddressValidator::validate + */ + public function testValidateWrongEmailAddresses() + { + $validator = new EmailAddressValidator(); + $validator->Extent = 'regex'; + $emailAddresses = ['.john.doe@example.com', 'Info-at-Ibexa.Co']; + foreach ($emailAddresses as $value) { + $this->assertFalse($validator->validate(new EmailAddressValue($value))); + } + } +} + +class_alias(EmailAddressValidatorTest::class, 'eZ\Publish\Core\FieldType\Tests\EmailAddressValidatorTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/FieldTypeMockTest.php b/tests/lib/FieldType/FieldTypeMockTest.php similarity index 88% rename from eZ/Publish/Core/FieldType/Tests/FieldTypeMockTest.php rename to tests/lib/FieldType/FieldTypeMockTest.php index e112ca5541..424eda9b85 100644 --- a/eZ/Publish/Core/FieldType/Tests/FieldTypeMockTest.php +++ b/tests/lib/FieldType/FieldTypeMockTest.php @@ -4,18 +4,19 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests; +namespace Ibexa\Tests\Core\FieldType; -use eZ\Publish\Core\FieldType\FieldType; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\FieldType; use PHPUnit\Framework\TestCase; class FieldTypeMockTest extends TestCase { public function testApplyDefaultSettingsThrowsInvalidArgumentException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); - /** @var \eZ\Publish\Core\FieldType\FieldType|\PHPUnit\Framework\MockObject\MockObject $stub */ + /** @var \Ibexa\Core\FieldType\FieldType|\PHPUnit\Framework\MockObject\MockObject $stub */ $stub = $this->getMockForAbstractClass( FieldType::class, [], @@ -31,11 +32,11 @@ public function testApplyDefaultSettingsThrowsInvalidArgumentException() /** * @dataProvider providerForTestApplyDefaultSettings * - * @covers \eZ\Publish\Core\FieldType\FieldType::applyDefaultSettings + * @covers \Ibexa\Core\FieldType\FieldType::applyDefaultSettings */ public function testApplyDefaultSettings($initialSettings, $expectedSettings) { - /** @var \eZ\Publish\Core\FieldType\FieldType|\PHPUnit\Framework\MockObject\MockObject $stub */ + /** @var \Ibexa\Core\FieldType\FieldType|\PHPUnit\Framework\MockObject\MockObject $stub */ $stub = $this->getMockForAbstractClass( FieldType::class, [], @@ -161,9 +162,9 @@ public function providerForTestApplyDefaultSettings() public function testApplyDefaultValidatorConfigurationEmptyThrowsInvalidArgumentException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); - /** @var \eZ\Publish\Core\FieldType\FieldType|\PHPUnit\Framework\MockObject\MockObject $stub */ + /** @var \Ibexa\Core\FieldType\FieldType|\PHPUnit\Framework\MockObject\MockObject $stub */ $stub = $this->getMockForAbstractClass( FieldType::class, [], @@ -178,7 +179,7 @@ public function testApplyDefaultValidatorConfigurationEmptyThrowsInvalidArgument public function testApplyDefaultValidatorConfigurationEmpty() { - /** @var \eZ\Publish\Core\FieldType\FieldType|\PHPUnit\Framework\MockObject\MockObject $stub */ + /** @var \Ibexa\Core\FieldType\FieldType|\PHPUnit\Framework\MockObject\MockObject $stub */ $stub = $this->getMockForAbstractClass( FieldType::class, [], @@ -208,7 +209,7 @@ public function testApplyDefaultValidatorConfigurationEmpty() */ public function testApplyDefaultValidatorConfiguration($initialConfiguration, $expectedConfiguration) { - /** @var \eZ\Publish\Core\FieldType\FieldType|\PHPUnit\Framework\MockObject\MockObject $stub */ + /** @var \Ibexa\Core\FieldType\FieldType|\PHPUnit\Framework\MockObject\MockObject $stub */ $stub = $this->getMockForAbstractClass( FieldType::class, [], @@ -279,3 +280,5 @@ public function providerForTestApplyDefaultValidatorConfiguration() ]; } } + +class_alias(FieldTypeMockTest::class, 'eZ\Publish\Core\FieldType\Tests\FieldTypeMockTest'); diff --git a/tests/lib/FieldType/FieldTypeTest.php b/tests/lib/FieldType/FieldTypeTest.php new file mode 100644 index 0000000000..1950cfdbe9 --- /dev/null +++ b/tests/lib/FieldType/FieldTypeTest.php @@ -0,0 +1,54 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Contracts\Core\FieldType\Value as SPIValue; +use Ibexa\Core\Persistence\TransformationProcessor; +use Ibexa\Tests\Core\FieldType\BaseFieldTypeTest as BaseFieldTypeTest; + +abstract class FieldTypeTest extends BaseFieldTypeTest +{ + /** + * @return \Ibexa\Core\Persistence\TransformationProcessor|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getTransformationProcessorMock() + { + return $this->getMockForAbstractClass( + TransformationProcessor::class, + [], + '', + false, + true, + true, + ['transform', 'transformByGroup'] + ); + } + + public function provideInputForValuesEqual(): array + { + return $this->provideInputForFromHash(); + } + + /** + * @dataProvider provideInputForValuesEqual + * + * @param mixed $inputValue1Hash + */ + public function testValuesEqual($inputValue1Hash, SPIValue $inputValue2): void + { + $fieldType = $this->getFieldTypeUnderTest(); + + $inputValue1 = $fieldType->fromHash($inputValue1Hash); + + self::assertTrue( + $fieldType->valuesEqual($inputValue1, $inputValue2), + 'valuesEqual() method did not create expected result.' + ); + } +} + +class_alias(FieldTypeTest::class, 'eZ\Publish\Core\FieldType\Tests\FieldTypeTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/FileSizeValidatorTest.php b/tests/lib/FieldType/FileSizeValidatorTest.php similarity index 80% rename from eZ/Publish/Core/FieldType/Tests/FileSizeValidatorTest.php rename to tests/lib/FieldType/FileSizeValidatorTest.php index cc28178129..a8f65652a7 100644 --- a/eZ/Publish/Core/FieldType/Tests/FileSizeValidatorTest.php +++ b/tests/lib/FieldType/FileSizeValidatorTest.php @@ -4,16 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests; +namespace Ibexa\Tests\Core\FieldType; -use eZ\Publish\API\Repository\IOServiceInterface; -use eZ\Publish\API\Repository\Values\Translation\Message; -use eZ\Publish\API\Repository\Values\Translation\Plural; -use eZ\Publish\Core\FieldType\BinaryFile\Value as BinaryFileValue; -use eZ\Publish\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\Validator\FileSizeValidator; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\SPI\FieldType\ValidationError; +use Ibexa\Contracts\Core\FieldType\ValidationError; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Translation\Message; +use Ibexa\Contracts\Core\Repository\Values\Translation\Plural; +use Ibexa\Core\FieldType\BinaryFile\Value as BinaryFileValue; +use Ibexa\Core\FieldType\Validator; +use Ibexa\Core\FieldType\Validator\FileSizeValidator; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\BinaryFile; use PHPUnit\Framework\TestCase; /** @@ -44,8 +45,8 @@ public function testConstructor() /** * Tests setting and getting constraints. * - * @covers \eZ\Publish\Core\FieldType\Validator::initializeWithConstraints - * @covers \eZ\Publish\Core\FieldType\Validator::__get + * @covers \Ibexa\Core\FieldType\Validator::initializeWithConstraints + * @covers \Ibexa\Core\FieldType\Validator::__get */ public function testConstraintsInitializeGet() { @@ -62,7 +63,7 @@ public function testConstraintsInitializeGet() /** * Test getting constraints schema. * - * @covers \eZ\Publish\Core\FieldType\Validator::getConstraintsSchema + * @covers \Ibexa\Core\FieldType\Validator::getConstraintsSchema */ public function testGetConstraintsSchema() { @@ -79,8 +80,8 @@ public function testGetConstraintsSchema() /** * Tests setting and getting constraints. * - * @covers \eZ\Publish\Core\FieldType\Validator::__set - * @covers \eZ\Publish\Core\FieldType\Validator::__get + * @covers \Ibexa\Core\FieldType\Validator::__set + * @covers \Ibexa\Core\FieldType\Validator::__get */ public function testConstraintsSetGet() { @@ -95,11 +96,11 @@ public function testConstraintsSetGet() /** * Tests initializing with a wrong constraint. * - * @covers \eZ\Publish\Core\FieldType\Validator::initializeWithConstraints + * @covers \Ibexa\Core\FieldType\Validator::initializeWithConstraints */ public function testInitializeBadConstraint() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); + $this->expectException(PropertyNotFoundException::class); $constraints = [ 'unexisting' => 0, @@ -113,11 +114,11 @@ public function testInitializeBadConstraint() /** * Tests setting a wrong constraint. * - * @covers \eZ\Publish\Core\FieldType\Validator::__set + * @covers \Ibexa\Core\FieldType\Validator::__set */ public function testSetBadConstraint() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); + $this->expectException(PropertyNotFoundException::class); $validator = new FileSizeValidator(); $validator->unexisting = 0; @@ -126,11 +127,11 @@ public function testSetBadConstraint() /** * Tests getting a wrong constraint. * - * @covers \eZ\Publish\Core\FieldType\Validator::__get + * @covers \Ibexa\Core\FieldType\Validator::__get */ public function testGetBadConstraint() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); + $this->expectException(PropertyNotFoundException::class); $validator = new FileSizeValidator(); $null = $validator->unexisting; @@ -142,8 +143,8 @@ public function testGetBadConstraint() * @param int $size * * @dataProvider providerForValidateOK - * @covers \eZ\Publish\Core\FieldType\Validator\FileSizeValidator::validate - * @covers \eZ\Publish\Core\FieldType\Validator::getMessage + * @covers \Ibexa\Core\FieldType\Validator\FileSizeValidator::validate + * @covers \Ibexa\Core\FieldType\Validator::getMessage */ public function testValidateCorrectValues($size) { @@ -157,7 +158,7 @@ public function testValidateCorrectValues($size) /** * @param int $size * - * @return \eZ\Publish\Core\FieldType\BinaryFile\Value + * @return \Ibexa\Core\FieldType\BinaryFile\Value */ protected function getBinaryFileValue($size) { @@ -181,7 +182,7 @@ public function providerForValidateOK() * Tests validating a wrong value. * * @dataProvider providerForValidateKO - * @covers \eZ\Publish\Core\FieldType\Validator\FileSizeValidator::validate + * @covers \Ibexa\Core\FieldType\Validator\FileSizeValidator::validate */ public function testValidateWrongValues($size, $message, $values) { @@ -231,7 +232,7 @@ public function providerForValidateKO() * Tests validation of constraints. * * @dataProvider providerForValidateConstraintsOK - * @covers \eZ\Publish\Core\FieldType\Validator\FileSizeValidator::validateConstraints + * @covers \Ibexa\Core\FieldType\Validator\FileSizeValidator::validateConstraints */ public function testValidateConstraintsCorrectValues($constraints) { @@ -259,7 +260,7 @@ public function providerForValidateConstraintsOK() * Tests validation of constraints. * * @dataProvider providerForValidateConstraintsKO - * @covers \eZ\Publish\Core\FieldType\Validator\FileSizeValidator::validateConstraints + * @covers \Ibexa\Core\FieldType\Validator\FileSizeValidator::validateConstraints */ public function testValidateConstraintsWrongValues($constraints, $expectedMessages, $values) { @@ -306,3 +307,5 @@ public function providerForValidateConstraintsKO() ]; } } + +class_alias(FileSizeValidatorTest::class, 'eZ\Publish\Core\FieldType\Tests\FileSizeValidatorTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/FloatTest.php b/tests/lib/FieldType/FloatTest.php similarity index 94% rename from eZ/Publish/Core/FieldType/Tests/FloatTest.php rename to tests/lib/FieldType/FloatTest.php index f040efed13..907e4a6a95 100644 --- a/eZ/Publish/Core/FieldType/Tests/FloatTest.php +++ b/tests/lib/FieldType/FloatTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests; +namespace Ibexa\Tests\Core\FieldType; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Float\Type as FloatType; -use eZ\Publish\Core\FieldType\Float\Value as FloatValue; -use eZ\Publish\Core\FieldType\ValidationError; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Float\Type as FloatType; +use Ibexa\Core\FieldType\Float\Value as FloatValue; +use Ibexa\Core\FieldType\ValidationError; /** * @group fieldType @@ -26,7 +26,7 @@ class FloatTest extends FieldTypeTest * NOT take care for test case wide caching of the field type, just return * a new instance from this method! * - * @return FieldType + * @return \Ibexa\Core\FieldType\FieldType */ protected function createFieldTypeUnderTest() { @@ -70,36 +70,13 @@ protected function getSettingsSchemaExpectation() /** * Returns the empty value expected from the field type. * - * @return \eZ\Publish\Core\FieldType\Float\Value + * @return \Ibexa\Core\FieldType\Float\Value */ protected function getEmptyValueExpectation() { return new FloatValue(); } - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ public function provideInvalidInputForAcceptValue() { return [ @@ -638,3 +615,5 @@ public function provideInvalidDataForValidate() ]; } } + +class_alias(FloatTest::class, 'eZ\Publish\Core\FieldType\Tests\FloatTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/FloatValueValidatorTest.php b/tests/lib/FieldType/FloatValueValidatorTest.php similarity index 86% rename from eZ/Publish/Core/FieldType/Tests/FloatValueValidatorTest.php rename to tests/lib/FieldType/FloatValueValidatorTest.php index 6b9ade23e2..b566148841 100644 --- a/eZ/Publish/Core/FieldType/Tests/FloatValueValidatorTest.php +++ b/tests/lib/FieldType/FloatValueValidatorTest.php @@ -4,13 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests; +namespace Ibexa\Tests\Core\FieldType; -use eZ\Publish\API\Repository\Values\Translation\Message; -use eZ\Publish\Core\FieldType\Float\Value as FloatValue; -use eZ\Publish\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\Validator\FloatValueValidator; -use eZ\Publish\SPI\FieldType\ValidationError; +use Ibexa\Contracts\Core\FieldType\ValidationError; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Translation\Message; +use Ibexa\Core\FieldType\Float\Value as FloatValue; +use Ibexa\Core\FieldType\Validator; +use Ibexa\Core\FieldType\Validator\FloatValueValidator; use PHPUnit\Framework\TestCase; /** @@ -49,8 +50,8 @@ public function testConstructor() /** * Tests setting and getting constraints. * - * @covers \eZ\Publish\Core\FieldType\Validator::initializeWithConstraints - * @covers \eZ\Publish\Core\FieldType\Validator::__get + * @covers \Ibexa\Core\FieldType\Validator::initializeWithConstraints + * @covers \Ibexa\Core\FieldType\Validator::__get */ public function testConstraintsInitializeGet() { @@ -69,7 +70,7 @@ public function testConstraintsInitializeGet() /** * Test getting constraints schema. * - * @covers \eZ\Publish\Core\FieldType\Validator::getConstraintsSchema + * @covers \Ibexa\Core\FieldType\Validator::getConstraintsSchema */ public function testGetConstraintsSchema() { @@ -90,8 +91,8 @@ public function testGetConstraintsSchema() /** * Tests setting and getting constraints. * - * @covers \eZ\Publish\Core\FieldType\Validator::__set - * @covers \eZ\Publish\Core\FieldType\Validator::__get + * @covers \Ibexa\Core\FieldType\Validator::__set + * @covers \Ibexa\Core\FieldType\Validator::__get */ public function testConstraintsSetGet() { @@ -109,11 +110,11 @@ public function testConstraintsSetGet() /** * Tests initializing with a wrong constraint. * - * @covers \eZ\Publish\Core\FieldType\Validator::initializeWithConstraints + * @covers \Ibexa\Core\FieldType\Validator::initializeWithConstraints */ public function testInitializeBadConstraint() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); + $this->expectException(PropertyNotFoundException::class); $constraints = [ 'unexisting' => 0, @@ -127,11 +128,11 @@ public function testInitializeBadConstraint() /** * Tests setting a wrong constraint. * - * @covers \eZ\Publish\Core\FieldType\Validator::__set + * @covers \Ibexa\Core\FieldType\Validator::__set */ public function testSetBadConstraint() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); + $this->expectException(PropertyNotFoundException::class); $validator = new FloatValueValidator(); $validator->unexisting = 0; @@ -140,11 +141,11 @@ public function testSetBadConstraint() /** * Tests getting a wrong constraint. * - * @covers \eZ\Publish\Core\FieldType\Validator::__get + * @covers \Ibexa\Core\FieldType\Validator::__get */ public function testGetBadConstraint() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); + $this->expectException(PropertyNotFoundException::class); $validator = new FloatValueValidator(); $null = $validator->unexisting; @@ -154,8 +155,8 @@ public function testGetBadConstraint() * Tests validating a correct value. * * @dataProvider providerForValidateOK - * @covers \eZ\Publish\Core\FieldType\Validator\FloatValueValidator::validate - * @covers \eZ\Publish\Core\FieldType\Validator::getMessage + * @covers \Ibexa\Core\FieldType\Validator\FloatValueValidator::validate + * @covers \Ibexa\Core\FieldType\Validator::getMessage */ public function testValidateCorrectValues($value) { @@ -181,7 +182,7 @@ public function providerForValidateOK() * Tests validating a wrong value. * * @dataProvider providerForValidateKO - * @covers \eZ\Publish\Core\FieldType\Validator\FloatValueValidator::validate + * @covers \Ibexa\Core\FieldType\Validator\FloatValueValidator::validate */ public function testValidateWrongValues($value, $message, $values) { @@ -223,7 +224,7 @@ public function providerForValidateKO() * Tests validation of constraints. * * @dataProvider providerForValidateConstraintsOK - * @covers \eZ\Publish\Core\FieldType\Validator\FileSizeValidator::validateConstraints + * @covers \Ibexa\Core\FieldType\Validator\FileSizeValidator::validateConstraints */ public function testValidateConstraintsCorrectValues($constraints) { @@ -269,7 +270,7 @@ public function providerForValidateConstraintsOK() * Tests validation of constraints. * * @dataProvider providerForValidateConstraintsKO - * @covers \eZ\Publish\Core\FieldType\Validator\FileSizeValidator::validateConstraints + * @covers \Ibexa\Core\FieldType\Validator\FileSizeValidator::validateConstraints */ public function testValidateConstraintsWrongValues($constraints, $expectedMessages, $values) { @@ -379,3 +380,5 @@ public function providerForValidateConstraintsKO() ]; } } + +class_alias(FloatValueValidatorTest::class, 'eZ\Publish\Core\FieldType\Tests\FloatValueValidatorTest'); diff --git a/eZ/Publish/SPI/FieldType/Generic/Tests/GenericTest.php b/tests/lib/FieldType/Generic/GenericTest.php similarity index 87% rename from eZ/Publish/SPI/FieldType/Generic/Tests/GenericTest.php rename to tests/lib/FieldType/Generic/GenericTest.php index 80018a1378..54be845462 100644 --- a/eZ/Publish/SPI/FieldType/Generic/Tests/GenericTest.php +++ b/tests/lib/FieldType/Generic/GenericTest.php @@ -6,22 +6,22 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\FieldType\Generic\Tests; - -use eZ\Publish\SPI\Exception\InvalidArgumentException; -use eZ\Publish\SPI\FieldType\Generic\Tests\Stubs\Type as GenericFieldTypeStub; -use eZ\Publish\SPI\FieldType\Generic\Tests\Stubs\Value as GenericFieldValueStub; -use eZ\Publish\SPI\FieldType\Tests\FieldTypeTest; -use eZ\Publish\SPI\FieldType\ValidationError; -use eZ\Publish\SPI\FieldType\ValueSerializerInterface; +namespace Ibexa\Tests\Core\FieldType\Generic; + +use Ibexa\Contracts\Core\FieldType\ValidationError; +use Ibexa\Contracts\Core\FieldType\ValueSerializerInterface; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Tests\Core\FieldType\BaseFieldTypeTest; +use Ibexa\Tests\Core\FieldType\Generic\Stubs\Type as GenericFieldTypeStub; +use Ibexa\Tests\Core\FieldType\Generic\Stubs\Value as GenericFieldValueStub; use Symfony\Component\Validator\ConstraintViolation; use Symfony\Component\Validator\ConstraintViolationList; use Symfony\Component\Validator\ConstraintViolationListInterface; use Symfony\Component\Validator\Validator\ValidatorInterface; -class GenericTest extends FieldTypeTest +class GenericTest extends BaseFieldTypeTest { - /** @var \eZ\Publish\SPI\FieldType\ValueSerializerInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\FieldType\ValueSerializerInterface|\PHPUnit\Framework\MockObject\MockObject */ private $serializer; /** @var \Symfony\Component\Validator\Validator\ValidatorInterface|\PHPUnit\Framework\MockObject\MockObject */ @@ -182,3 +182,5 @@ private function createSerializerMock(): ValueSerializerInterface return $serializer; } } + +class_alias(GenericTest::class, 'eZ\Publish\SPI\FieldType\Generic\Tests\GenericTest'); diff --git a/tests/lib/FieldType/Generic/Stubs/Type.php b/tests/lib/FieldType/Generic/Stubs/Type.php new file mode 100644 index 0000000000..81200550f8 --- /dev/null +++ b/tests/lib/FieldType/Generic/Stubs/Type.php @@ -0,0 +1,21 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\FieldType\Generic\Stubs; + +use Ibexa\Contracts\Core\FieldType\Generic\Type as BaseType; + +final class Type extends BaseType +{ + public function getFieldTypeIdentifier(): string + { + return 'generic'; + } +} + +class_alias(Type::class, 'eZ\Publish\SPI\FieldType\Generic\Tests\Stubs\Type'); diff --git a/tests/lib/FieldType/Generic/Stubs/Value.php b/tests/lib/FieldType/Generic/Stubs/Value.php new file mode 100644 index 0000000000..808ce812ed --- /dev/null +++ b/tests/lib/FieldType/Generic/Stubs/Value.php @@ -0,0 +1,33 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\FieldType\Generic\Stubs; + +use Ibexa\Contracts\Core\FieldType\Value as ValueInterface; + +final class Value implements ValueInterface +{ + private $value; + + public function __construct($value = null) + { + $this->value = $value; + } + + public function getValue() + { + return $this->value; + } + + public function __toString() + { + return (string)$this->value; + } +} + +class_alias(Value::class, 'eZ\Publish\SPI\FieldType\Generic\Tests\Stubs\Value'); diff --git a/eZ/Publish/Core/FieldType/Tests/Generic/ValueSerializer/SymfonySerializerAdapterTest.php b/tests/lib/FieldType/Generic/ValueSerializer/SymfonySerializerAdapterTest.php similarity index 88% rename from eZ/Publish/Core/FieldType/Tests/Generic/ValueSerializer/SymfonySerializerAdapterTest.php rename to tests/lib/FieldType/Generic/ValueSerializer/SymfonySerializerAdapterTest.php index 86767c8061..58f8f40def 100644 --- a/eZ/Publish/Core/FieldType/Tests/Generic/ValueSerializer/SymfonySerializerAdapterTest.php +++ b/tests/lib/FieldType/Generic/ValueSerializer/SymfonySerializerAdapterTest.php @@ -6,9 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\FieldType\Tests\Generic\ValueSerializer; +namespace Ibexa\Tests\Core\FieldType\Generic\ValueSerializer; -use eZ\Publish\SPI\FieldType\Value; +use Ibexa\Contracts\Core\FieldType\Value; +use Ibexa\Core\FieldType\ValueSerializer\SymfonySerializerAdapter; use PHPUnit\Framework\TestCase; use Symfony\Component\Serializer\Encoder\DecoderInterface; use Symfony\Component\Serializer\Encoder\EncoderInterface; @@ -32,7 +33,7 @@ class SymfonySerializerAdapterTest extends TestCase /** @var \Symfony\Component\Serializer\Encoder\DecoderInterface|\PHPUnit\Framework\MockObject\MockObject */ private $decoder; - /** @var \eZ\Publish\Core\FieldType\ValueSerializer\SymfonySerializerAdapter */ + /** @var \Ibexa\Core\FieldType\ValueSerializer\SymfonySerializerAdapter */ private $adapter; protected function setUp(): void @@ -42,7 +43,7 @@ protected function setUp(): void $this->encoder = $this->createMock(EncoderInterface::class); $this->decoder = $this->createMock(DecoderInterface::class); - $this->adapter = new \eZ\Publish\Core\FieldType\ValueSerializer\SymfonySerializerAdapter( + $this->adapter = new SymfonySerializerAdapter( $this->normalizer, $this->denomalizer, $this->encoder, @@ -107,3 +108,5 @@ public function testDecode(): void $this->assertEquals($data, $this->adapter->decode($json, self::TEST_CONTEXT)); } } + +class_alias(SymfonySerializerAdapterTest::class, 'eZ\Publish\Core\FieldType\Tests\Generic\ValueSerializer\SymfonySerializerAdapterTest'); diff --git a/tests/lib/FieldType/ISBNTest.php b/tests/lib/FieldType/ISBNTest.php new file mode 100644 index 0000000000..811c2e0673 --- /dev/null +++ b/tests/lib/FieldType/ISBNTest.php @@ -0,0 +1,310 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\ISBN\Type as ISBN; +use Ibexa\Core\FieldType\ISBN\Value as ISBNValue; +use Ibexa\Core\FieldType\ValidationError; + +/** + * @group fieldType + * @group ezisbn + */ +class ISBNTest extends FieldTypeTest +{ + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new ISBN('9789722514095'); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return []; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return [ + 'isISBN13' => [ + 'type' => 'boolean', + 'default' => true, + ], + ]; + } + + protected function getEmptyValueExpectation(): ISBNValue + { + return new ISBNValue(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + 1234567890, + InvalidArgumentException::class, + ], + [ + [], + InvalidArgumentException::class, + ], + [ + new \stdClass(), + InvalidArgumentException::class, + ], + [ + 44.55, + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + '9789722514095', + new ISBNValue('9789722514095'), + ], + [ + '978-972-25-1409-5', + new ISBNValue('978-972-25-1409-5'), + ], + [ + '0-9752298-0-X', + new ISBNValue('0-9752298-0-X'), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new ISBNValue('9789722514095'), + '9789722514095', + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + '9789722514095', + new ISBNValue('9789722514095'), + ], + ]; + } + + protected function provideFieldTypeIdentifier() + { + return 'ezisbn'; + } + + public function provideDataForGetName(): array + { + return [ + [$this->getEmptyValueExpectation(), '', [], 'en_GB'], + [new ISBNValue('9789722514095'), '9789722514095', [], 'en_GB'], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings and + * field value which are considered valid by the {@link validate()} method. + * + * @return array + */ + public function provideValidDataForValidate() + { + return [ + [ + [ + 'fieldSettings' => [ + 'isISBN13' => true, + ], + ], + new ISBNValue(), + ], + [ + [ + 'fieldSettings' => [ + 'isISBN13' => false, + ], + ], + new ISBNValue(), + ], + [ + [ + 'fieldSettings' => [ + 'isISBN13' => true, + ], + ], + new ISBNValue('9789722514095'), + ], + [ + [ + 'fieldSettings' => [ + 'isISBN13' => false, + ], + ], + new ISBNValue('0-9752298-0-X'), + ], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings, + * field value and corresponding validation errors returned by + * the {@link validate()} method. + * + * @return array + */ + public function provideInvalidDataForValidate() + { + return [ + [ + [ + 'fieldSettings' => [ + 'isISBN13' => false, + ], + ], + new ISBNValue('9789722514095'), + [ + new ValidationError('ISBN-10 must be 10 character length', null, [], 'isbn'), + ], + ], + ]; + } +} + +class_alias(ISBNTest::class, 'eZ\Publish\Core\FieldType\Tests\ISBNTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/Image/IO/LegacyTest.php b/tests/lib/FieldType/Image/IO/LegacyTest.php similarity index 94% rename from eZ/Publish/Core/FieldType/Tests/Image/IO/LegacyTest.php rename to tests/lib/FieldType/Image/IO/LegacyTest.php index b07f19bfbe..dbe7c3f981 100644 --- a/eZ/Publish/Core/FieldType/Tests/Image/IO/LegacyTest.php +++ b/tests/lib/FieldType/Image/IO/LegacyTest.php @@ -4,33 +4,33 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests\Image\IO; - -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Image\IO\Legacy as LegacyIOService; -use eZ\Publish\Core\FieldType\Image\IO\OptionsProvider; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\IO\Values\BinaryFileCreateStruct; -use eZ\Publish\Core\MVC\ConfigResolverInterface; +namespace Ibexa\Tests\Core\FieldType\Image\IO; + +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Image\IO\Legacy as LegacyIOService; +use Ibexa\Core\FieldType\Image\IO\OptionsProvider; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\BinaryFile; +use Ibexa\Core\IO\Values\BinaryFileCreateStruct; use PHPUnit\Framework\TestCase; class LegacyTest extends TestCase { - /** @var \eZ\Publish\Core\FieldType\Image\IO\Legacy */ + /** @var \Ibexa\Core\FieldType\Image\IO\Legacy */ protected $service; /** * Internal IOService instance for published images. * - * @var \eZ\Publish\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject + * @var \Ibexa\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $publishedIoServiceMock; /** * Internal IOService instance for draft images. * - * @var \eZ\Publish\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject + * @var \Ibexa\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $draftIoServiceMock; @@ -447,7 +447,7 @@ public function testNewBinaryCreateStructFromUploadedFile() } /** - * @return \eZ\Publish\Core\MVC\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ protected function createConfigResolverMock(): ConfigResolverInterface { @@ -475,3 +475,5 @@ protected function createConfigResolverMock(): ConfigResolverInterface return $mock; } } + +class_alias(LegacyTest::class, 'eZ\Publish\Core\FieldType\Tests\Image\IO\LegacyTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/Image/PathGenerator/LegacyPathGeneratorTest.php b/tests/lib/FieldType/Image/PathGenerator/LegacyPathGeneratorTest.php similarity index 86% rename from eZ/Publish/Core/FieldType/Tests/Image/PathGenerator/LegacyPathGeneratorTest.php rename to tests/lib/FieldType/Image/PathGenerator/LegacyPathGeneratorTest.php index 358397aebb..f9fcec89fd 100644 --- a/eZ/Publish/Core/FieldType/Tests/Image/PathGenerator/LegacyPathGeneratorTest.php +++ b/tests/lib/FieldType/Image/PathGenerator/LegacyPathGeneratorTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests\Image\PathGenerator; +namespace Ibexa\Tests\Core\FieldType\Image\PathGenerator; -use eZ\Publish\Core\FieldType\Image\PathGenerator\LegacyPathGenerator; +use Ibexa\Core\FieldType\Image\PathGenerator\LegacyPathGenerator; use PHPUnit\Framework\TestCase; /** @@ -65,3 +65,5 @@ public function provideStoragePathForFieldData() ]; } } + +class_alias(LegacyPathGeneratorTest::class, 'eZ\Publish\Core\FieldType\Tests\Image\PathGenerator\LegacyPathGeneratorTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/ImageAsset/AssetMapperTest.php b/tests/lib/FieldType/ImageAsset/AssetMapperTest.php similarity index 84% rename from eZ/Publish/Core/FieldType/Tests/ImageAsset/AssetMapperTest.php rename to tests/lib/FieldType/ImageAsset/AssetMapperTest.php index 392c8e949a..9b21c73f8f 100644 --- a/eZ/Publish/Core/FieldType/Tests/ImageAsset/AssetMapperTest.php +++ b/tests/lib/FieldType/ImageAsset/AssetMapperTest.php @@ -6,38 +6,39 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\FieldType\Tests\ImageAsset; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\Core\FieldType\Image; -use eZ\Publish\Core\FieldType\ImageAsset\AssetMapper; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\Repository\ContentTypeService; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition; +namespace Ibexa\Tests\Core\FieldType\ImageAsset; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\FieldType\Image; +use Ibexa\Core\FieldType\ImageAsset\AssetMapper; +use Ibexa\Core\Repository\ContentTypeService; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinition; use PHPUnit\Framework\TestCase; class AssetMapperTest extends TestCase { public const EXAMPLE_CONTENT_ID = 487; - /** @var \eZ\Publish\API\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject */ private $contentService; - /** @var \eZ\Publish\API\Repository\LocationService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\LocationService|\PHPUnit\Framework\MockObject\MockObject */ private $locationService; - /** @var \eZ\Publish\API\Repository\ContentTypeService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService|\PHPUnit\Framework\MockObject\MockObject */ private $contentTypeService; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ private $configResolver; /** @var array */ @@ -136,7 +137,7 @@ public function testGetAssetField(): void public function testGetAssetFieldThrowsInvalidArgumentException(): void { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $content = $this->createContentWithId(self::EXAMPLE_CONTENT_ID); @@ -193,7 +194,7 @@ public function testGetAssetValue(): void public function testGetAssetValueThrowsInvalidArgumentException(): void { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $content = $this->createContentWithId(self::EXAMPLE_CONTENT_ID); @@ -323,7 +324,7 @@ private function createContentWithContentType(int $contentTypeId): Content } /** - * @return \eZ\Publish\Core\MVC\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ private function mockConfigResolver(): ConfigResolverInterface { @@ -337,3 +338,5 @@ private function mockConfigResolver(): ConfigResolverInterface return $mock; } } + +class_alias(AssetMapperTest::class, 'eZ\Publish\Core\FieldType\Tests\ImageAsset\AssetMapperTest'); diff --git a/tests/lib/FieldType/ImageAssetTest.php b/tests/lib/FieldType/ImageAssetTest.php new file mode 100644 index 0000000000..fe6d5c32fe --- /dev/null +++ b/tests/lib/FieldType/ImageAssetTest.php @@ -0,0 +1,437 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Contracts\Core\FieldType\Value as SPIValue; +use Ibexa\Contracts\Core\Persistence\Content\Handler as SPIContentHandler; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Image\Value; +use Ibexa\Core\FieldType\ImageAsset; +use Ibexa\Core\FieldType\ValidationError; + +/** + * @group fieldType + * @group ezimageasset + */ +class ImageAssetTest extends FieldTypeTest +{ + private const DESTINATION_CONTENT_ID = 14; + + /** @var \Ibexa\Contracts\Core\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject */ + private $contentServiceMock; + + /** @var \Ibexa\Core\FieldType\ImageAsset\AssetMapper|\PHPUnit\Framework\MockObject\MockObject */ + private $assetMapperMock; + + /** @var \Ibexa\Contracts\Core\Persistence\Content\Handler|\PHPUnit\Framework\MockObject\MockObject */ + private $contentHandlerMock; + + /** + * {@inheritdoc} + */ + protected function setUp(): void + { + parent::setUp(); + + $this->contentServiceMock = $this->createMock(ContentService::class); + $this->assetMapperMock = $this->createMock(ImageAsset\AssetMapper::class); + $this->contentHandlerMock = $this->createMock(SPIContentHandler::class); + $versionInfo = new VersionInfo([ + 'versionNo' => 24, + 'names' => [ + 'en_GB' => 'name_en_GB', + 'de_DE' => 'Name_de_DE', + ], + ]); + $currentVersionNo = 28; + $destinationContentInfo = $this->createMock(ContentInfo::class); + $destinationContentInfo + ->method('__get') + ->willReturnMap([ + ['currentVersionNo', $currentVersionNo], + ['mainLanguageCode', 'en_GB'], + ]); + + $this->contentHandlerMock + ->method('loadContentInfo') + ->with(self::DESTINATION_CONTENT_ID) + ->willReturn($destinationContentInfo); + + $this->contentHandlerMock + ->method('loadVersionInfo') + ->with(self::DESTINATION_CONTENT_ID, $currentVersionNo) + ->willReturn($versionInfo); + } + + /** + * {@inheritdoc} + */ + protected function provideFieldTypeIdentifier(): string + { + return ImageAsset\Type::FIELD_TYPE_IDENTIFIER; + } + + /** + * {@inheritdoc} + */ + protected function createFieldTypeUnderTest() + { + return new ImageAsset\Type( + $this->contentServiceMock, + $this->assetMapperMock, + $this->contentHandlerMock + ); + } + + /** + * {@inheritdoc} + */ + protected function getValidatorConfigurationSchemaExpectation(): array + { + return []; + } + + /** + * {@inheritdoc} + */ + protected function getSettingsSchemaExpectation(): array + { + return []; + } + + /** + * {@inheritdoc} + */ + protected function getEmptyValueExpectation() + { + return new ImageAsset\Value(); + } + + /** + * {@inheritdoc} + */ + public function provideInvalidInputForAcceptValue(): array + { + return [ + [ + true, + InvalidArgumentException::class, + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function provideValidInputForAcceptValue(): array + { + $destinationContentId = 7; + + return [ + [ + null, + $this->getEmptyValueExpectation(), + ], + [ + $destinationContentId, + new ImageAsset\Value($destinationContentId), + ], + [ + new ContentInfo([ + 'id' => $destinationContentId, + ]), + new ImageAsset\Value($destinationContentId), + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function provideInputForToHash(): array + { + $destinationContentId = 7; + $alternativeText = 'The alternative text for image'; + + return [ + [ + new ImageAsset\Value(), + [ + 'destinationContentId' => null, + 'alternativeText' => null, + ], + ], + [ + new ImageAsset\Value($destinationContentId), + [ + 'destinationContentId' => $destinationContentId, + 'alternativeText' => null, + ], + ], + [ + new ImageAsset\Value($destinationContentId, $alternativeText), + [ + 'destinationContentId' => $destinationContentId, + 'alternativeText' => $alternativeText, + ], + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function provideInputForFromHash(): array + { + $destinationContentId = 7; + $alternativeText = 'The alternative text for image'; + + return [ + [ + null, + new ImageAsset\Value(), + ], + [ + [ + 'destinationContentId' => $destinationContentId, + 'alternativeText' => null, + ], + new ImageAsset\Value($destinationContentId), + ], + [ + [ + 'destinationContentId' => $destinationContentId, + 'alternativeText' => $alternativeText, + ], + new ImageAsset\Value($destinationContentId, $alternativeText), + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function provideInvalidDataForValidate(): array + { + return []; + } + + /** + * {@inheritdoc} + */ + public function testValidateNonAsset() + { + $destinationContentId = 7; + $destinationContent = $this->createMock(Content::class); + $invalidContentTypeIdentifier = 'article'; + $invalidContentType = $this->createMock(ContentType::class); + $invalidContentType + ->expects($this->once()) + ->method('__get') + ->with('identifier') + ->willReturn($invalidContentTypeIdentifier); + + $destinationContent + ->method('getContentType') + ->willReturn($invalidContentType); + + $this->contentServiceMock + ->expects($this->once()) + ->method('loadContent') + ->with($destinationContentId) + ->willReturn($destinationContent); + + $this->assetMapperMock + ->expects($this->once()) + ->method('isAsset') + ->with($destinationContent) + ->willReturn(false); + + $validationErrors = $this->doValidate([], new ImageAsset\Value($destinationContentId)); + + $this->assertIsArray($validationErrors); + $this->assertEquals([ + new ValidationError( + 'Content %type% is not a valid asset target', + null, + [ + '%type%' => $invalidContentTypeIdentifier, + ], + 'destinationContentId' + ), + ], $validationErrors); + } + + /** + * {@inheritdoc} + */ + public function provideValidDataForValidate(): array + { + return [ + [ + [], + $this->getEmptyValueExpectation(), + ], + ]; + } + + /** + * @dataProvider provideDataForTestValidateValidNonEmptyAssetValue + * + * @param array<\Ibexa\Core\FieldType\ValidationError> $expectedValidationErrors + */ + public function testValidateValidNonEmptyAssetValue( + int $fileSize, + array $expectedValidationErrors + ): void { + $destinationContentId = 7; + $destinationContent = $this->createMock(Content::class); + + $this->contentServiceMock + ->expects($this->once()) + ->method('loadContent') + ->with($destinationContentId) + ->willReturn($destinationContent); + + $this->assetMapperMock + ->expects($this->once()) + ->method('isAsset') + ->with($destinationContent) + ->willReturn(true); + + $assetValueMock = $this->createMock(Value::class); + $assetValueMock + ->method('getFileSize') + ->willReturn($fileSize); + + $this->assetMapperMock + ->expects($this->once()) + ->method('getAssetValue') + ->with($destinationContent) + ->willReturn($assetValueMock); + + $fieldDefinitionMock = $this->createMock(FieldDefinition::class); + $fieldDefinitionMock + ->method('getValidatorConfiguration') + ->willReturn( + [ + 'FileSizeValidator' => [ + 'maxFileSize' => 1.4, + ], + ] + ); + + $this->assetMapperMock + ->method('getAssetFieldDefinition') + ->willReturn($fieldDefinitionMock); + + $validationErrors = $this->doValidate([], new ImageAsset\Value($destinationContentId)); + $this->assertEquals( + $expectedValidationErrors, + $validationErrors + ); + } + + /** + * @return iterable<array{ + * int, + * array<\Ibexa\Core\FieldType\ValidationError>, + * }> + */ + public function provideDataForTestValidateValidNonEmptyAssetValue(): iterable + { + yield 'No validation errors' => [ + 123456, + [], + ]; + + yield 'Maximum file size exceeded' => [ + 12345678912356, + [ + new ValidationError( + 'The file size cannot exceed %size% megabyte.', + 'The file size cannot exceed %size% megabytes.', + [ + '%size%' => 1.4, + ], + 'fileSize' + ), + ], + ]; + } + + /** + * {@inheritdoc} + */ + public function provideDataForGetName(): array + { + return [ + 'empty_destination_content_id' => [ + $this->getEmptyValueExpectation(), + '', + [], + 'en_GB', + ], + 'destination_content_id' => [ + new ImageAsset\Value(self::DESTINATION_CONTENT_ID), 'name_en_GB', [], 'en_GB', + ], + 'destination_content_id_de_DE' => [ + new ImageAsset\Value(self::DESTINATION_CONTENT_ID), 'Name_de_DE', [], 'de_DE', + ], + ]; + } + + /** + * @dataProvider provideDataForGetName + */ + public function testGetName( + SPIValue $value, + string $expected, + array $fieldSettings = [], + string $languageCode = 'en_GB' + ): void { + /** @var \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition|\PHPUnit\Framework\MockObject\MockObject $fieldDefinitionMock */ + $fieldDefinitionMock = $this->createMock(FieldDefinition::class); + $fieldDefinitionMock->method('getFieldSettings')->willReturn($fieldSettings); + + $name = $this->getFieldTypeUnderTest()->getName($value, $fieldDefinitionMock, $languageCode); + + self::assertSame($expected, $name); + } + + public function testIsSearchable() + { + $this->assertTrue($this->getFieldTypeUnderTest()->isSearchable()); + } + + /** + * @covers \Ibexa\Core\FieldType\Relation\Type::getRelations + */ + public function testGetRelations() + { + $destinationContentId = 7; + $fieldType = $this->createFieldTypeUnderTest(); + + $this->assertEquals( + [ + Relation::ASSET => [$destinationContentId], + ], + $fieldType->getRelations($fieldType->acceptValue($destinationContentId)) + ); + } +} + +class_alias(ImageAssetTest::class, 'eZ\Publish\Core\FieldType\Tests\ImageAssetTest'); diff --git a/tests/lib/FieldType/ImageTest.php b/tests/lib/FieldType/ImageTest.php new file mode 100644 index 0000000000..bcf4b96c2e --- /dev/null +++ b/tests/lib/FieldType/ImageTest.php @@ -0,0 +1,942 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Contracts\Core\IO\MimeTypeDetector; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Image\Type as ImageType; +use Ibexa\Core\FieldType\Image\Value as ImageValue; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\FieldType\Validator\FileExtensionBlackListValidator; +use Ibexa\Core\FieldType\Validator\ImageValidator; + +/** + * @group fieldType + * @group ezfloat + */ +class ImageTest extends FieldTypeTest +{ + private const MIME_TYPES = [ + 'image/png', + 'image/jpeg', + 'image/gif', + ]; + + protected $blackListedExtensions = [ + 'php', + 'php3', + 'phar', + 'phpt', + 'pht', + 'phtml', + 'pgif', + ]; + + public function getImageInputPath() + { + return __DIR__ . '/../_fixtures/squirrel-developers.jpg'; + } + + /** + * @return \Ibexa\Contracts\Core\IO\MimeTypeDetector + */ + protected function getMimeTypeDetectorMock() + { + if (!isset($this->mimeTypeDetectorMock)) { + $this->mimeTypeDetectorMock = $this->createMock(MimeTypeDetector::class); + } + + return $this->mimeTypeDetectorMock; + } + + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new ImageType( + [ + $this->getBlackListValidatorMock(), + $this->getImageValidatorMock(), + ], + self::MIME_TYPES + ); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + private function getBlackListValidatorMock() + { + return $this + ->getMockBuilder(FileExtensionBlackListValidator::class) + ->setConstructorArgs([ + $this->getConfigResolverMock(), + ]) + ->setMethods(null) + ->getMock(); + } + + private function getImageValidatorMock() + { + return $this + ->getMockBuilder(ImageValidator::class) + ->setMethods(null) + ->getMock(); + } + + private function getConfigResolverMock() + { + $configResolver = $this + ->createMock(ConfigResolverInterface::class); + + $configResolver + ->method('getParameter') + ->with('io.file_storage.file_type_blacklist') + ->willReturn($this->blackListedExtensions); + + return $configResolver; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return [ + 'FileSizeValidator' => [ + 'maxFileSize' => [ + 'type' => 'numeric', + 'default' => null, + ], + ], + 'AlternativeTextValidator' => [ + 'required' => [ + 'type' => 'bool', + 'default' => false, + ], + ], + ]; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return [ + 'mimeTypes' => [ + 'type' => 'choice', + 'default' => [], + ], + ]; + } + + /** + * Returns the empty value expected from the field type. + * + * @return \Ibexa\Core\FieldType\Image\Value + */ + protected function getEmptyValueExpectation() + { + return new ImageValue(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + 'foo', + InvalidArgumentException::class, + ], + [ + new ImageValue( + [ + 'id' => 'non/existent/path', + ] + ), + InvalidArgumentException::class, + ], + [ + new ImageValue( + [ + 'id' => __FILE__, + 'fileName' => [], + ] + ), + InvalidArgumentException::class, + ], + [ + new ImageValue( + [ + 'id' => __FILE__, + 'fileName' => 'ImageTest.php', + 'fileSize' => 'truebar', + ] + ), + InvalidArgumentException::class, + ], + [ + new ImageValue( + [ + 'id' => __FILE__, + 'fileName' => 'ImageTest.php', + 'fileSize' => 23, + 'alternativeText' => [], + ] + ), + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'id' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + null, + new ImageValue(), + ], + [ + [], + new ImageValue(), + ], + [ + new ImageValue(), + new ImageValue(), + ], + [ + $this->getImageInputPath(), + new ImageValue( + [ + 'inputUri' => $this->getImageInputPath(), + 'fileName' => basename($this->getImageInputPath()), + 'fileSize' => filesize($this->getImageInputPath()), + 'alternativeText' => null, + ] + ), + ], + [ + [ + 'id' => $this->getImageInputPath(), + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'fileSize' => 23, + 'alternativeText' => 'This is so Sindelfingen!', + 'uri' => 'http://' . $this->getImageInputPath(), + ], + new ImageValue( + [ + 'id' => $this->getImageInputPath(), + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'fileSize' => 23, + 'alternativeText' => 'This is so Sindelfingen!', + 'uri' => 'http://' . $this->getImageInputPath(), + ] + ), + ], + [ + [ + 'inputUri' => $this->getImageInputPath(), + 'fileName' => 'My Fancy Filename', + 'fileSize' => 123, + ], + new ImageValue( + [ + 'inputUri' => $this->getImageInputPath(), + 'fileName' => 'My Fancy Filename', + 'fileSize' => filesize($this->getImageInputPath()), + ] + ), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new ImageValue(), + null, + ], + [ + new ImageValue( + [ + 'id' => $this->getImageInputPath(), + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'fileSize' => 23, + 'alternativeText' => 'This is so Sindelfingen!', + 'imageId' => '123-12345', + 'uri' => 'http://' . $this->getImageInputPath(), + 'width' => 123, + 'height' => 456, + 'mime' => 'image/jpeg', + ] + ), + [ + 'id' => $this->getImageInputPath(), + 'path' => $this->getImageInputPath(), + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'fileSize' => 23, + 'alternativeText' => 'This is so Sindelfingen!', + 'imageId' => '123-12345', + 'uri' => 'http://' . $this->getImageInputPath(), + 'inputUri' => null, + 'width' => 123, + 'height' => 456, + 'additionalData' => [], + 'mime' => 'image/jpeg', + ], + ], + // BC with 5.0 (EZP-20948). Path can be used as input instead of $inputUri. + [ + new ImageValue( + [ + 'path' => $this->getImageInputPath(), + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'fileSize' => 23, + 'alternativeText' => 'This is so Sindelfingen!', + 'imageId' => '123-12345', + 'uri' => 'http://' . $this->getImageInputPath(), + 'mime' => null, + ] + ), + [ + 'id' => null, + 'path' => $this->getImageInputPath(), + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'fileSize' => 23, + 'alternativeText' => 'This is so Sindelfingen!', + 'imageId' => '123-12345', + 'uri' => 'http://' . $this->getImageInputPath(), + 'inputUri' => $this->getImageInputPath(), + 'width' => null, + 'height' => null, + 'additionalData' => [], + 'mime' => null, + ], + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + null, + new ImageValue(), + ], + [ + [ + 'id' => $this->getImageInputPath(), + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'fileSize' => 23, + 'alternativeText' => 'This is so Sindelfingen!', + 'uri' => 'http://' . $this->getImageInputPath(), + ], + new ImageValue( + [ + 'id' => $this->getImageInputPath(), + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'fileSize' => 23, + 'alternativeText' => 'This is so Sindelfingen!', + 'uri' => 'http://' . $this->getImageInputPath(), + ] + ), + ], + // BC with 5.0 (EZP-20948). Path can be used as input instead of ID. + [ + [ + 'path' => $this->getImageInputPath(), + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'fileSize' => 23, + 'alternativeText' => 'This is so Sindelfingen!', + 'uri' => 'http://' . $this->getImageInputPath(), + ], + new ImageValue( + [ + 'inputUri' => $this->getImageInputPath(), + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'fileSize' => 23, + 'alternativeText' => 'This is so Sindelfingen!', + 'uri' => 'http://' . $this->getImageInputPath(), + ] + ), + ], + // @todo: Provide REST upload tests + ]; + } + + protected function provideFieldTypeIdentifier() + { + return 'ezimage'; + } + + public function provideDataForGetName(): array + { + return [ + [$this->getEmptyValueExpectation(), '', [], 'en_GB'], + [ + new ImageValue(['fileName' => 'Sindelfingen-Squirrels.jpg']), + 'Sindelfingen-Squirrels.jpg', + [], + 'en_GB', + ], + // Alternative text has priority + [ + new ImageValue( + [ + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'alternativeText' => 'This is so Sindelfingen!', + ] + ), + 'This is so Sindelfingen!', + [], + 'en_GB', + ], + [ + new ImageValue( + [ + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'alternativeText' => 'This is so Sindelfingen!', + ] + ), + 'This is so Sindelfingen!', + [], + 'en_GB', + ], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings and + * field value which are considered valid by the {@link validate()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten if + * a FieldType supports validation! + * + * For example: + * + * <code> + * return array( + * array( + * array( + * "validatorConfiguration" => array( + * "StringLengthValidator" => array( + * "minStringLength" => 2, + * "maxStringLength" => 10, + * ), + * ), + * ), + * new TextLineValue( "lalalala" ), + * ), + * array( + * array( + * "fieldSettings" => array( + * 'isMultiple' => true + * ), + * ), + * new CountryValue( + * array( + * "BE" => array( + * "Name" => "Belgium", + * "Alpha2" => "BE", + * "Alpha3" => "BEL", + * "IDC" => 32, + * ), + * ), + * ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidDataForValidate() + { + return [ + [ + [ + 'validatorConfiguration' => [ + 'FileSizeValidator' => [ + 'maxFileSize' => 1.0, + ], + ], + ], + new ImageValue( + [ + 'id' => $this->getImageInputPath(), + 'fileName' => basename($this->getImageInputPath()), + 'fileSize' => filesize($this->getImageInputPath()), + 'alternativeText' => null, + 'uri' => '', + ] + ), + ], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings, + * field value and corresponding validation errors returned by + * the {@link validate()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten + * if a FieldType supports validation! + * + * For example: + * + * <code> + * return array( + * array( + * array( + * "validatorConfiguration" => array( + * "IntegerValueValidator" => array( + * "minIntegerValue" => 5, + * "maxIntegerValue" => 10 + * ), + * ), + * ), + * new IntegerValue( 3 ), + * array( + * new ValidationError( + * "The value can not be lower than %size%.", + * null, + * array( + * "size" => 5 + * ), + * ), + * ), + * ), + * array( + * array( + * "fieldSettings" => array( + * "isMultiple" => false + * ), + * ), + * new CountryValue( + * "BE" => array( + * "Name" => "Belgium", + * "Alpha2" => "BE", + * "Alpha3" => "BEL", + * "IDC" => 32, + * ), + * "FR" => array( + * "Name" => "France", + * "Alpha2" => "FR", + * "Alpha3" => "FRA", + * "IDC" => 33, + * ), + * ) + * ), + * array( + * new ValidationError( + * "Field definition does not allow multiple countries to be selected." + * ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInvalidDataForValidate() + { + return [ + 'file is too large' => [ + [ + 'validatorConfiguration' => [ + 'FileSizeValidator' => [ + 'maxFileSize' => 0.01, + ], + ], + ], + new ImageValue( + [ + 'id' => $this->getImageInputPath(), + 'fileName' => basename($this->getImageInputPath()), + 'fileSize' => filesize($this->getImageInputPath()), + 'alternativeText' => null, + 'uri' => '', + ] + ), + [ + new ValidationError( + 'The file size cannot exceed %size% megabyte.', + 'The file size cannot exceed %size% megabytes.', + [ + '%size%' => 0.01, + ], + 'fileSize' + ), + ], + ], + 'file is not an image file' => [ + [ + 'validatorConfiguration' => [ + 'FileSizeValidator' => [ + 'maxFileSize' => 1, + ], + ], + ], + new ImageValue( + [ + 'id' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'alternativeText' => null, + 'uri' => '', + ] + ), + [ + new ValidationError( + 'A valid file is required. The following file extensions are not allowed: %extensionsBlackList%', + null, + ['%extensionsBlackList%' => implode(', ', $this->blackListedExtensions)], + 'fileExtensionBlackList' + ), + new ValidationError( + 'A valid image file is required.', + null, + [], + 'id' + ), + ], + ], + 'file is too large and invalid' => [ + [ + 'validatorConfiguration' => [ + 'FileSizeValidator' => [ + 'maxFileSize' => 0.01, + ], + ], + ], + new ImageValue( + [ + 'id' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'alternativeText' => null, + 'uri' => '', + ] + ), + [ + new ValidationError( + 'A valid file is required. The following file extensions are not allowed: %extensionsBlackList%', + null, + ['%extensionsBlackList%' => implode(', ', $this->blackListedExtensions)], + 'fileExtensionBlackList' + ), + new ValidationError('A valid image file is required.', null, [], 'id'), + new ValidationError( + 'The file size cannot exceed %size% megabyte.', + 'The file size cannot exceed %size% megabytes.', + [ + '%size%' => 0.01, + ], + 'fileSize' + ), + ], + ], + 'file is an image file but filename ends with .php' => [ + [ + 'validatorConfiguration' => [ + 'FileSizeValidator' => [ + 'maxFileSize' => 1, + ], + ], + ], + new ImageValue( + [ + 'id' => __DIR__ . '/../_fixtures/phppng.php', + 'fileName' => basename(__DIR__ . '/../_fixtures/phppng.php'), + 'fileSize' => filesize(__DIR__ . '/../_fixtures/phppng.php'), + 'alternativeText' => null, + 'uri' => '', + ] + ), + [ + new ValidationError( + 'A valid file is required. The following file extensions are not allowed: %extensionsBlackList%', + null, + ['%extensionsBlackList%' => implode(', ', $this->blackListedExtensions)], + 'fileExtensionBlackList' + ), + new ValidationError( + 'A valid image file is required.', + null, + [], + 'id' + ), + ], + ], + 'file is an image file but filename ends with .PHP (upper case)' => [ + [ + 'validatorConfiguration' => [ + 'FileSizeValidator' => [ + 'maxFileSize' => 1, + ], + ], + ], + new ImageValue( + [ + 'id' => __DIR__ . '/../_fixtures/phppng2.PHP', + 'fileName' => basename(__DIR__ . '/../_fixtures/phppng2.PHP'), + 'fileSize' => filesize(__DIR__ . '/../_fixtures/phppng2.PHP'), + 'alternativeText' => null, + 'uri' => '', + ] + ), + [ + new ValidationError( + 'A valid file is required. The following file extensions are not allowed: %extensionsBlackList%', + null, + ['%extensionsBlackList%' => implode(', ', $this->blackListedExtensions)], + 'fileExtensionBlackList' + ), + new ValidationError( + 'A valid image file is required.', + null, + [], + 'id' + ), + ], + ], + 'alternative text is null' => [ + [ + 'validatorConfiguration' => [ + 'AlternativeTextValidator' => [ + 'required' => true, + ], + ], + ], + new ImageValue( + [ + 'id' => $this->getImageInputPath(), + 'fileName' => basename($this->getImageInputPath()), + 'fileSize' => filesize($this->getImageInputPath()), + 'alternativeText' => null, + 'uri' => '', + ] + ), + [ + new ValidationError( + 'Alternative text is required.', + null, + [], + 'alternativeText' + ), + ], + ], + 'alternative text is empty string' => [ + [ + 'validatorConfiguration' => [ + 'AlternativeTextValidator' => [ + 'required' => true, + ], + ], + ], + new ImageValue( + [ + 'id' => $this->getImageInputPath(), + 'fileName' => basename($this->getImageInputPath()), + 'fileSize' => filesize($this->getImageInputPath()), + 'alternativeText' => '', + 'uri' => '', + ] + ), + [ + new ValidationError( + 'Alternative text is required.', + null, + [], + 'alternativeText' + ), + ], + ], + 'Image with not allowed mime type' => [ + [ + 'fieldSettings' => [ + 'mimeTypes' => [ + 'image/png', + 'image/gif', + ], + ], + ], + new ImageValue( + [ + 'id' => $this->getImageInputPath(), + 'fileName' => basename($this->getImageInputPath()), + 'fileSize' => filesize($this->getImageInputPath()), + 'alternativeText' => '', + 'uri' => '', + ] + ), + [ + new ValidationError( + 'The mime type of the file is invalid (%mimeType%). Allowed mime types are %mimeTypes%.', + null, + [ + '%mimeType%' => 'image/jpeg', + '%mimeTypes%' => 'image/png, image/gif', + ], + 'id' + ), + ], + ], + ]; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function provideInputForValuesEqual(): array + { + return [ + [ + [ + 'id' => $this->getImageInputPath(), + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'fileSize' => 23, + 'alternativeText' => 'This is so Sindelfingen!', + 'imageId' => '123-12345', + 'uri' => 'http://' . $this->getImageInputPath(), + 'width' => 123, + 'height' => 456, + ], + new ImageValue( + [ + 'id' => $this->getImageInputPath(), + 'path' => $this->getImageInputPath(), + 'fileName' => 'Sindelfingen-Squirrels.jpg', + 'fileSize' => 23, + 'alternativeText' => 'This is so Sindelfingen!', + 'imageId' => '123-12317', + 'uri' => 'http://' . $this->getImageInputPath(), + 'inputUri' => null, + 'width' => 123, + 'height' => 456, + ] + ), + ], + ]; + } +} + +class_alias(ImageTest::class, 'eZ\Publish\Core\FieldType\Tests\ImageTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/IntegerTest.php b/tests/lib/FieldType/IntegerTest.php similarity index 94% rename from eZ/Publish/Core/FieldType/Tests/IntegerTest.php rename to tests/lib/FieldType/IntegerTest.php index ae450dcaaa..a75f4c8bd9 100644 --- a/eZ/Publish/Core/FieldType/Tests/IntegerTest.php +++ b/tests/lib/FieldType/IntegerTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests; +namespace Ibexa\Tests\Core\FieldType; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\Integer\Type as Integer; -use eZ\Publish\Core\FieldType\Integer\Value as IntegerValue; -use eZ\Publish\Core\FieldType\ValidationError; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Integer\Type as Integer; +use Ibexa\Core\FieldType\Integer\Value as IntegerValue; +use Ibexa\Core\FieldType\ValidationError; /** * @group fieldType @@ -26,7 +26,7 @@ class IntegerTest extends FieldTypeTest * NOT take care for test case wide caching of the field type, just return * a new instance from this method! * - * @return FieldType + * @return \Ibexa\Core\FieldType\FieldType */ protected function createFieldTypeUnderTest() { @@ -75,29 +75,6 @@ protected function getEmptyValueExpectation() return new IntegerValue(); } - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ public function provideInvalidInputForAcceptValue() { return [ @@ -628,3 +605,5 @@ public function provideInvalidDataForValidate() ]; } } + +class_alias(IntegerTest::class, 'eZ\Publish\Core\FieldType\Tests\IntegerTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/IntegerValueValidatorTest.php b/tests/lib/FieldType/IntegerValueValidatorTest.php similarity index 86% rename from eZ/Publish/Core/FieldType/Tests/IntegerValueValidatorTest.php rename to tests/lib/FieldType/IntegerValueValidatorTest.php index bf50127665..8f1cf841a3 100644 --- a/eZ/Publish/Core/FieldType/Tests/IntegerValueValidatorTest.php +++ b/tests/lib/FieldType/IntegerValueValidatorTest.php @@ -4,13 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests; +namespace Ibexa\Tests\Core\FieldType; -use eZ\Publish\API\Repository\Values\Translation\Message; -use eZ\Publish\Core\FieldType\Integer\Value as IntegerValue; -use eZ\Publish\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\Validator\IntegerValueValidator; -use eZ\Publish\SPI\FieldType\ValidationError; +use Ibexa\Contracts\Core\FieldType\ValidationError; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Translation\Message; +use Ibexa\Core\FieldType\Integer\Value as IntegerValue; +use Ibexa\Core\FieldType\Validator; +use Ibexa\Core\FieldType\Validator\IntegerValueValidator; use PHPUnit\Framework\TestCase; /** @@ -49,8 +50,8 @@ public function testConstructor() /** * Tests setting and getting constraints. * - * @covers \eZ\Publish\Core\FieldType\Validator::initializeWithConstraints - * @covers \eZ\Publish\Core\FieldType\Validator::__get + * @covers \Ibexa\Core\FieldType\Validator::initializeWithConstraints + * @covers \Ibexa\Core\FieldType\Validator::__get */ public function testConstraintsInitializeGet() { @@ -69,7 +70,7 @@ public function testConstraintsInitializeGet() /** * Test getting constraints schema. * - * @covers \eZ\Publish\Core\FieldType\Validator::getConstraintsSchema + * @covers \Ibexa\Core\FieldType\Validator::getConstraintsSchema */ public function testGetConstraintsSchema() { @@ -90,8 +91,8 @@ public function testGetConstraintsSchema() /** * Tests setting and getting constraints. * - * @covers \eZ\Publish\Core\FieldType\Validator::__set - * @covers \eZ\Publish\Core\FieldType\Validator::__get + * @covers \Ibexa\Core\FieldType\Validator::__set + * @covers \Ibexa\Core\FieldType\Validator::__get */ public function testConstraintsSetGet() { @@ -109,11 +110,11 @@ public function testConstraintsSetGet() /** * Tests initializing with a wrong constraint. * - * @covers \eZ\Publish\Core\FieldType\Validator::initializeWithConstraints + * @covers \Ibexa\Core\FieldType\Validator::initializeWithConstraints */ public function testInitializeBadConstraint() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); + $this->expectException(PropertyNotFoundException::class); $constraints = [ 'unexisting' => 0, @@ -127,11 +128,11 @@ public function testInitializeBadConstraint() /** * Tests setting a wrong constraint. * - * @covers \eZ\Publish\Core\FieldType\Validator::__set + * @covers \Ibexa\Core\FieldType\Validator::__set */ public function testSetBadConstraint() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); + $this->expectException(PropertyNotFoundException::class); $validator = new IntegerValueValidator(); $validator->unexisting = 0; @@ -140,11 +141,11 @@ public function testSetBadConstraint() /** * Tests getting a wrong constraint. * - * @covers \eZ\Publish\Core\FieldType\Validator::__get + * @covers \Ibexa\Core\FieldType\Validator::__get */ public function testGetBadConstraint() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); + $this->expectException(PropertyNotFoundException::class); $validator = new IntegerValueValidator(); $null = $validator->unexisting; @@ -154,8 +155,8 @@ public function testGetBadConstraint() * Tests validating a correct value. * * @dataProvider providerForValidateOK - * @covers \eZ\Publish\Core\FieldType\Validator\IntegerValueValidator::validate - * @covers \eZ\Publish\Core\FieldType\Validator::getMessage + * @covers \Ibexa\Core\FieldType\Validator\IntegerValueValidator::validate + * @covers \Ibexa\Core\FieldType\Validator::getMessage */ public function testValidateCorrectValues($value) { @@ -183,7 +184,7 @@ public function providerForValidateOK() * Tests validating a wrong value. * * @dataProvider providerForValidateKO - * @covers \eZ\Publish\Core\FieldType\Validator\IntegerValueValidator::validate + * @covers \Ibexa\Core\FieldType\Validator\IntegerValueValidator::validate */ public function testValidateWrongValues($value, $message, $values) { @@ -225,7 +226,7 @@ public function providerForValidateKO() * Tests validation of constraints. * * @dataProvider providerForValidateConstraintsOK - * @covers \eZ\Publish\Core\FieldType\Validator\FileSizeValidator::validateConstraints + * @covers \Ibexa\Core\FieldType\Validator\FileSizeValidator::validateConstraints */ public function testValidateConstraintsCorrectValues($constraints) { @@ -271,7 +272,7 @@ public function providerForValidateConstraintsOK() * Tests validation of constraints. * * @dataProvider providerForValidateConstraintsKO - * @covers \eZ\Publish\Core\FieldType\Validator\FileSizeValidator::validateConstraints + * @covers \Ibexa\Core\FieldType\Validator\FileSizeValidator::validateConstraints */ public function testValidateConstraintsWrongValues($constraints, $expectedMessages, $values) { @@ -381,3 +382,5 @@ public function providerForValidateConstraintsKO() ]; } } + +class_alias(IntegerValueValidatorTest::class, 'eZ\Publish\Core\FieldType\Tests\IntegerValueValidatorTest'); diff --git a/tests/lib/FieldType/KeywordTest.php b/tests/lib/FieldType/KeywordTest.php new file mode 100644 index 0000000000..61a1646137 --- /dev/null +++ b/tests/lib/FieldType/KeywordTest.php @@ -0,0 +1,243 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Keyword\Type as KeywordType; +use Ibexa\Core\FieldType\Keyword\Value as KeywordValue; + +/** + * @group fieldType + * @group ezinteger + */ +class KeywordTest extends FieldTypeTest +{ + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new KeywordType(); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return []; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return []; + } + + /** + * Returns the empty value expected from the field type. + */ + protected function getEmptyValueExpectation() + { + return new KeywordValue([]); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + 23, + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + null, + new KeywordValue([]), + ], + [ + [], + new KeywordValue([]), + ], + [ + 'foo', + new KeywordValue(['foo']), + ], + [ + ['foo'], + new KeywordValue(['foo']), + ], + [ + new KeywordValue(['foo']), + new KeywordValue(['foo']), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new KeywordValue([]), + [], + ], + [ + new KeywordValue(['foo', 'bar']), + ['foo', 'bar'], + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + [], + new KeywordValue([]), + ], + [ + ['foo', 'bar'], + new KeywordValue(['foo', 'bar']), + ], + ]; + } + + protected function provideFieldTypeIdentifier() + { + return 'ezkeyword'; + } + + public function provideDataForGetName(): array + { + return [ + [$this->getEmptyValueExpectation(), '', [], 'en_GB'], + [new KeywordValue(['foo', 'bar']), 'foo, bar', [], 'en_GB'], + ]; + } +} + +class_alias(KeywordTest::class, 'eZ\Publish\Core\FieldType\Tests\KeywordTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/MapLocationTest.php b/tests/lib/FieldType/MapLocationTest.php similarity index 91% rename from eZ/Publish/Core/FieldType/Tests/MapLocationTest.php rename to tests/lib/FieldType/MapLocationTest.php index 7aab09733f..bde9e351d6 100644 --- a/eZ/Publish/Core/FieldType/Tests/MapLocationTest.php +++ b/tests/lib/FieldType/MapLocationTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests; +namespace Ibexa\Tests\Core\FieldType; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\FieldType\MapLocation; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\MapLocation; class MapLocationTest extends FieldTypeTest { @@ -20,7 +20,7 @@ class MapLocationTest extends FieldTypeTest * NOT take care for test case wide caching of the field type, just return * a new instance from this method! * - * @return FieldType + * @return \Ibexa\Core\FieldType\FieldType */ protected function createFieldTypeUnderTest() { @@ -58,29 +58,6 @@ protected function getEmptyValueExpectation() return new MapLocation\Value(); } - /** - * Data provider for invalid input to acceptValue(). - * - * Returns an array of data provider sets with 2 arguments: 1. The invalid - * input to acceptValue(), 2. The expected exception type as a string. For - * example: - * - * <code> - * return array( - * array( - * new \stdClass(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * array( - * array(), - * 'eZ\\Publish\\Core\\Base\\Exceptions\\InvalidArgumentException', - * ), - * // ... - * ); - * </code> - * - * @return array - */ public function provideInvalidInputForAcceptValue() { return [ @@ -342,3 +319,5 @@ public function provideDataForGetName(): array ]; } } + +class_alias(MapLocationTest::class, 'eZ\Publish\Core\FieldType\Tests\MapLocationTest'); diff --git a/tests/lib/FieldType/MediaTest.php b/tests/lib/FieldType/MediaTest.php new file mode 100644 index 0000000000..d76f2f684e --- /dev/null +++ b/tests/lib/FieldType/MediaTest.php @@ -0,0 +1,775 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\BinaryFile\Value as BinaryFileValue; +use Ibexa\Core\FieldType\Media\Type as MediaType; +use Ibexa\Core\FieldType\Media\Value as MediaValue; +use Ibexa\Core\FieldType\ValidationError; + +/** + * @group fieldType + * @group ezbinaryfile + */ +class MediaTest extends BinaryBaseTest +{ + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new MediaType([$this->getBlackListValidatorMock()]); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + protected function getEmptyValueExpectation() + { + return new MediaValue(); + } + + protected function getSettingsSchemaExpectation() + { + return [ + 'mediaType' => [ + 'type' => 'choice', + 'default' => MediaType::TYPE_HTML5_VIDEO, + ], + ]; + } + + public function provideInvalidInputForAcceptValue() + { + $baseInput = parent::provideInvalidInputForAcceptValue(); + $binaryFileInput = [ + [ + new MediaValue(['id' => '/foo/bar']), + InvalidArgumentException::class, + ], + [ + new MediaValue(['hasController' => 'yes']), + InvalidArgumentException::class, + ], + [ + new MediaValue(['autoplay' => 'yes']), + InvalidArgumentException::class, + ], + [ + new MediaValue(['loop' => 'yes']), + InvalidArgumentException::class, + ], + [ + new MediaValue(['height' => []]), + InvalidArgumentException::class, + ], + [ + new MediaValue(['width' => []]), + InvalidArgumentException::class, + ], + ]; + + return array_merge($baseInput, $binaryFileInput); + } + + public function provideValidInputForAcceptValue() + { + return [ + [ + null, + new MediaValue(), + ], + [ + new MediaValue(), + new MediaValue(), + ], + [ + __FILE__, + new MediaValue( + [ + 'inputUri' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'hasController' => false, + 'autoplay' => false, + 'loop' => false, + 'width' => 0, + 'height' => 0, + 'uri' => '', + ] + ), + ], + [ + ['inputUri' => __FILE__], + new MediaValue( + [ + 'inputUri' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'hasController' => false, + 'autoplay' => false, + 'loop' => false, + 'width' => 0, + 'height' => 0, + 'uri' => '', + ] + ), + ], + [ + [ + 'inputUri' => __FILE__, + 'fileSize' => 23, + ], + new MediaValue( + [ + 'inputUri' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => 23, + 'hasController' => false, + 'autoplay' => false, + 'loop' => false, + 'width' => 0, + 'height' => 0, + 'uri' => '', + ] + ), + ], + [ + [ + 'inputUri' => __FILE__, + 'mimeType' => 'application/text+php', + ], + new MediaValue( + [ + 'inputUri' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'mimeType' => 'application/text+php', + 'hasController' => false, + 'autoplay' => false, + 'loop' => false, + 'width' => 0, + 'height' => 0, + 'uri' => '', + ] + ), + ], + [ + [ + 'inputUri' => __FILE__, + 'hasController' => true, + ], + new MediaValue( + [ + 'inputUri' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'hasController' => true, + 'autoplay' => false, + 'loop' => false, + 'width' => 0, + 'height' => 0, + 'uri' => '', + ] + ), + ], + [ + [ + 'inputUri' => __FILE__, + 'autoplay' => true, + ], + new MediaValue( + [ + 'inputUri' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'hasController' => false, + 'autoplay' => true, + 'loop' => false, + 'width' => 0, + 'height' => 0, + 'uri' => '', + ] + ), + ], + [ + [ + 'inputUri' => __FILE__, + 'loop' => true, + ], + new MediaValue( + [ + 'inputUri' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + 'uri' => '', + ] + ), + ], + [ + [ + 'inputUri' => __FILE__, + 'width' => 23, + ], + new MediaValue( + [ + 'inputUri' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'hasController' => false, + 'autoplay' => false, + 'loop' => false, + 'width' => 23, + 'height' => 0, + 'uri' => '', + ] + ), + ], + [ + [ + 'inputUri' => __FILE__, + 'height' => 42, + ], + new MediaValue( + [ + 'inputUri' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'hasController' => false, + 'autoplay' => false, + 'loop' => false, + 'width' => 0, + 'height' => 42, + 'uri' => '', + ] + ), + ], + // BC with 5.2 (EZP-22808). Id can be used as input instead of inputUri. + [ + ['id' => __FILE__], + new MediaValue( + [ + 'inputUri' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'hasController' => false, + 'autoplay' => false, + 'loop' => false, + 'width' => 0, + 'height' => 0, + 'uri' => '', + ] + ), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( + * array( + * 'id' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * array( + * 'id' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new MediaValue(), + null, + ], + [ + new MediaValue( + [ + 'inputUri' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + 'uri' => 'http://' . basename(__FILE__), + ] + ), + [ + 'id' => null, + 'inputUri' => __FILE__, + 'path' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + 'uri' => 'http://' . basename(__FILE__), + ], + ], + // BC with 5.0 (EZP-20948). Path can be used as input instead of inputUri. + [ + new MediaValue( + [ + 'path' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + 'uri' => 'http://' . basename(__FILE__), + ] + ), + [ + 'id' => null, + 'inputUri' => __FILE__, + 'path' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + 'uri' => 'http://' . basename(__FILE__), + ], + ], + // BC with 5.2 (EZP-22808). Id can be used as input instead of inputUri. + [ + new MediaValue( + [ + 'id' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + 'uri' => 'http://' . basename(__FILE__), + ] + ), + [ + 'id' => null, + 'inputUri' => __FILE__, + 'path' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + 'uri' => 'http://' . basename(__FILE__), + ], + ], + // BC with 5.2 (EZP-22808). Id is recognized as such if not pointing to existing file. + [ + new MediaValue( + [ + 'id' => 'application/asdf1234.pdf', + 'fileName' => 'asdf1234.pdf', + 'fileSize' => 12345, + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + 'uri' => 'http://asdf1234.pdf', + ] + ), + [ + 'id' => 'application/asdf1234.pdf', + 'inputUri' => null, + 'path' => null, + 'fileName' => 'asdf1234.pdf', + 'fileSize' => 12345, + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + 'uri' => 'http://asdf1234.pdf', + ], + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'id' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( + * array( + * 'id' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + null, + new MediaValue(), + ], + [ + [ + 'id' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + ], + new MediaValue( + [ + 'id' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + ] + ), + ], + // BC with 5.0 (EZP-20948). Path can be used as input instead of ID. + [ + [ + 'path' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + ], + new MediaValue( + [ + 'id' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + ] + ), + ], + // BC with 5.2 (EZP-22808). Id can be used as input instead of inputUri. + [ + [ + 'id' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + ], + new MediaValue( + [ + 'id' => null, + 'inputUri' => __FILE__, + 'path' => __FILE__, + 'fileName' => basename(__FILE__), + 'fileSize' => filesize(__FILE__), + 'mimeType' => 'text/plain', + 'hasController' => false, + 'autoplay' => false, + 'loop' => true, + 'width' => 0, + 'height' => 0, + ] + ), + ], + // @todo: Test for REST upload hash + ]; + } + + /** + * Provide data sets with field settings which are considered valid by the + * {@link validateFieldSettings()} method. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( 'rows' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidFieldSettings() + { + return [ + [ + [], + ], + [ + [ + 'mediaType' => MediaType::TYPE_FLASH, + ], + ], + [ + [ + 'mediaType' => MediaType::TYPE_REALPLAYER, + ], + ], + ]; + } + + /** + * Provide data sets with field settings which are considered invalid by the + * {@link validateFieldSettings()} method. The method must return a + * non-empty array of validation error when receiving such field settings. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * true, + * ), + * array( + * array( 'nonExistentKey' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInValidFieldSettings() + { + return [ + [ + [ + 'not-existing' => 23, + ], + ], + [ + // mediaType must be constant + [ + 'mediaType' => 23, + ], + ], + ]; + } + + protected function provideFieldTypeIdentifier() + { + return 'ezmedia'; + } + + public function provideDataForGetName(): array + { + return [ + [ + new MediaValue(), + '', + [], + 'en_GB', + ], + [ + new MediaValue(['fileName' => 'sindelfingen.jpg']), + 'sindelfingen.jpg', + [], + 'en_GB', + ], + ]; + } + + public function provideValidDataForValidate() + { + return [ + [ + [ + 'validatorConfiguration' => [ + 'FileSizeValidator' => [ + 'maxFileSize' => 1, + ], + ], + ], + new BinaryFileValue( + [ + 'id' => 'some/file/here', + 'fileName' => 'sindelfingen.mp4', + 'fileSize' => 15000, + 'downloadCount' => 0, + 'mimeType' => 'video/mp4', + ] + ), + ], + ]; + } + + public function provideInvalidDataForValidate() + { + return [ + // File is too large + [ + [ + 'validatorConfiguration' => [ + 'FileSizeValidator' => [ + 'maxFileSize' => 0.01, + ], + ], + ], + new MediaValue( + [ + 'id' => 'some/file/here', + 'fileName' => 'sindelfingen.mp4', + 'fileSize' => 150000, + 'mimeType' => 'video/mp4', + ] + ), + [ + new ValidationError( + 'The file size cannot exceed %size% megabyte.', + 'The file size cannot exceed %size% megabytes.', + [ + '%size%' => 0.01, + ], + 'fileSize' + ), + ], + ], + + // file extension is in blacklist + [ + [ + 'validatorConfiguration' => [ + 'FileSizeValidator' => [ + 'maxFileSize' => 1, + ], + ], + ], + new MediaValue( + [ + 'id' => 'phppng.php', + 'fileName' => 'phppng.php', + 'fileSize' => 0.01, + 'mimeType' => 'video/mp4', + ] + ), + [ + new ValidationError( + 'A valid file is required. The following file extensions are not allowed: %extensionsBlackList%', + null, + ['%extensionsBlackList%' => implode(', ', $this->blackListedExtensions)], + 'fileExtensionBlackList' + ), + ], + ], + ]; + } +} + +class_alias(MediaTest::class, 'eZ\Publish\Core\FieldType\Tests\MediaTest'); diff --git a/tests/lib/FieldType/RelationListTest.php b/tests/lib/FieldType/RelationListTest.php new file mode 100644 index 0000000000..117972f437 --- /dev/null +++ b/tests/lib/FieldType/RelationListTest.php @@ -0,0 +1,883 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Contracts\Core\FieldType\Value as SPIValue; +use Ibexa\Contracts\Core\Persistence\Content\Handler as SPIContentHandler; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\RelationList\Type as RelationList; +use Ibexa\Core\FieldType\RelationList\Value; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\Repository\Validator\TargetContentValidatorInterface; + +class RelationListTest extends FieldTypeTest +{ + private const DESTINATION_CONTENT_ID_14 = 14; + private const DESTINATION_CONTENT_ID_22 = 22; + + /** @var \Ibexa\Contracts\Core\Persistence\Content\Handler */ + private $contentHandler; + + /** @var \Ibexa\Core\Repository\Validator\TargetContentValidatorInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $targetContentValidator; + + protected function setUp(): void + { + parent::setUp(); + + $this->targetContentValidator = $this->createMock(TargetContentValidatorInterface::class); + + $versionInfo14 = new VersionInfo([ + 'versionNo' => 1, + 'names' => [ + 'en_GB' => 'name_14_en_GB', + 'de_DE' => 'Name_14_de_DE', + ], + ]); + $versionInfo22 = new VersionInfo([ + 'versionNo' => 1, + 'names' => [ + 'en_GB' => 'name_22_en_GB', + 'de_DE' => 'Name_22_de_DE', + ], + ]); + $currentVersionNoFor14 = 44; + $destinationContentInfo14 = $this->createMock(ContentInfo::class); + $destinationContentInfo14 + ->method('__get') + ->willReturnMap([ + ['currentVersionNo', $currentVersionNoFor14], + ['mainLanguageCode', 'en_GB'], + ]); + $currentVersionNoFor22 = 22; + $destinationContentInfo22 = $this->createMock(ContentInfo::class); + $destinationContentInfo22 + ->method('__get') + ->willReturnMap([ + ['currentVersionNo', $currentVersionNoFor22], + ['mainLanguageCode', 'en_GB'], + ]); + + $this->contentHandler = $this->createMock(SPIContentHandler::class); + $this->contentHandler + ->method('loadContentInfo') + ->willReturnMap([ + [self::DESTINATION_CONTENT_ID_14, $destinationContentInfo14], + [self::DESTINATION_CONTENT_ID_22, $destinationContentInfo22], + ]); + + $this->contentHandler + ->method('loadVersionInfo') + ->willReturnMap([ + [self::DESTINATION_CONTENT_ID_14, $currentVersionNoFor14, $versionInfo14], + [self::DESTINATION_CONTENT_ID_22, $currentVersionNoFor22, $versionInfo22], + ]); + } + + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + */ + protected function createFieldTypeUnderTest(): RelationList + { + $fieldType = new RelationList( + $this->contentHandler, + $this->targetContentValidator + ); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return [ + 'RelationListValueValidator' => [ + 'selectionLimit' => [ + 'type' => 'int', + 'default' => 0, + ], + ], + ]; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return [ + 'selectionMethod' => [ + 'type' => 'int', + 'default' => RelationList::SELECTION_BROWSE, + ], + 'selectionDefaultLocation' => [ + 'type' => 'string', + 'default' => null, + ], + 'rootDefaultLocation' => [ + 'type' => 'bool', + 'default' => false, + ], + 'selectionContentTypes' => [ + 'type' => 'array', + 'default' => [], + ], + ]; + } + + /** + * Returns the empty value expected from the field type. + * + * @return \Ibexa\Core\FieldType\RelationList\Value + */ + protected function getEmptyValueExpectation() + { + // @todo FIXME: Is this correct? + return new Value(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + true, + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + new Value(), + new Value(), + ], + [ + 23, + new Value([23]), + ], + [ + new ContentInfo(['id' => 23]), + new Value([23]), + ], + [ + [23, 42], + new Value([23, 42]), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new Value([23, 42]), + ['destinationContentIds' => [23, 42]], + ], + [ + new Value(), + ['destinationContentIds' => []], + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + ['destinationContentIds' => [23, 42]], + new Value([23, 42]), + ], + [ + ['destinationContentIds' => []], + new Value(), + ], + ]; + } + + /** + * Provide data sets with field settings which are considered valid by the + * {@link validateFieldSettings()} method. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( 'rows' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidFieldSettings() + { + return [ + [ + [ + 'selectionMethod' => RelationList::SELECTION_BROWSE, + 'selectionDefaultLocation' => 23, + ], + ], + [ + [ + 'selectionMethod' => RelationList::SELECTION_DROPDOWN, + 'selectionDefaultLocation' => 'foo', + ], + ], + [ + [ + 'selectionMethod' => RelationList::SELECTION_DROPDOWN, + 'selectionDefaultLocation' => 'foo', + 'selectionContentTypes' => [1, 2, 3], + ], + ], + [ + [ + 'selectionMethod' => RelationList::SELECTION_LIST_WITH_RADIO_BUTTONS, + 'selectionDefaultLocation' => 'foo', + 'selectionContentTypes' => [1, 2, 3], + ], + ], + [ + [ + 'selectionMethod' => RelationList::SELECTION_LIST_WITH_CHECKBOXES, + 'selectionDefaultLocation' => 'foo', + 'selectionContentTypes' => [1, 2, 3], + ], + ], + [ + [ + 'selectionMethod' => RelationList::SELECTION_MULTIPLE_SELECTION_LIST, + 'selectionDefaultLocation' => 'foo', + 'selectionContentTypes' => [1, 2, 3], + ], + ], + [ + [ + 'selectionMethod' => RelationList::SELECTION_TEMPLATE_BASED_MULTIPLE, + 'selectionDefaultLocation' => 'foo', + 'selectionContentTypes' => [1, 2, 3], + ], + ], + [ + [ + 'selectionMethod' => RelationList::SELECTION_TEMPLATE_BASED_SINGLE, + 'selectionDefaultLocation' => 'foo', + 'selectionContentTypes' => [1, 2, 3], + ], + ], + ]; + } + + /** + * Provide data sets with field settings which are considered invalid by the + * {@link validateFieldSettings()} method. The method must return a + * non-empty array of validation error when receiving such field settings. + * + * ATTENTION: This is a default implementation, which must be overwritten + * if a FieldType supports field settings! + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * true, + * ), + * array( + * array( 'nonExistentKey' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInValidFieldSettings() + { + return [ + [ + // Invalid value for 'selectionMethod' + [ + 'selectionMethod' => true, + 'selectionDefaultLocation' => 23, + ], + ], + [ + // Invalid value for 'selectionDefaultLocation' + [ + 'selectionMethod' => RelationList::SELECTION_DROPDOWN, + 'selectionDefaultLocation' => [], + ], + ], + [ + // Invalid value for 'selectionContentTypes' + [ + 'selectionMethod' => RelationList::SELECTION_DROPDOWN, + 'selectionDefaultLocation' => 23, + 'selectionContentTypes' => true, + ], + ], + [ + // Invalid value for 'selectionMethod' + [ + 'selectionMethod' => 9, + 'selectionDefaultLocation' => 23, + 'selectionContentTypes' => true, + ], + ], + ]; + } + + /** + * Provide data sets with validator configurations which are considered + * valid by the {@link validateValidatorConfiguration()} method. + * + * Returns an array of data provider sets with a single argument: A valid + * set of validator configurations. + * + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( + * 'IntegerValueValidator' => array( + * 'minIntegerValue' => 0, + * 'maxIntegerValue' => 23, + * ) + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidValidatorConfiguration() + { + return [ + [ + [], + ], + [ + [ + 'RelationListValueValidator' => [ + 'selectionLimit' => 0, + ], + ], + ], + [ + [ + 'RelationListValueValidator' => [ + 'selectionLimit' => 14, + ], + ], + ], + ]; + } + + /** + * Provide data sets with validator configurations which are considered + * invalid by the {@link validateValidatorConfiguration()} method. The + * method must return a non-empty array of validation errors when receiving + * one of the provided values. + * + * Returns an array of data provider sets with a single argument: A valid + * set of validator configurations. + * + * For example: + * + * <code> + * return array( + * array( + * array( + * 'NonExistentValidator' => array(), + * ), + * ), + * array( + * array( + * // Typos + * 'InTEgervALUeVALIdator' => array( + * 'iinIntegerValue' => 0, + * 'maxIntegerValue' => 23, + * ) + * ) + * ), + * array( + * array( + * 'IntegerValueValidator' => array( + * // Incorrect value types + * 'minIntegerValue' => true, + * 'maxIntegerValue' => false, + * ) + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInvalidValidatorConfiguration() + { + return [ + [ + [ + 'NonExistentValidator' => [], + ], + ], + [ + [ + 'RelationListValueValidator' => [ + 'nonExistentValue' => 14, + ], + ], + ], + [ + [ + 'RelationListValueValidator' => [ + 'selectionLimit' => 'foo', + ], + ], + ], + [ + [ + 'RelationListValueValidator' => [ + 'selectionLimit' => -10, + ], + ], + ], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings and + * field value which are considered valid by the {@link validate()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten if + * a FieldType supports validation! + * + * For example: + * + * <code> + * return array( + * array( + * array( + * "validatorConfiguration" => array( + * "StringLengthValidator" => array( + * "minStringLength" => 2, + * "maxStringLength" => 10, + * ), + * ), + * ), + * new TextLineValue( "lalalala" ), + * ), + * array( + * array( + * "fieldSettings" => array( + * 'isMultiple' => true + * ), + * ), + * new CountryValue( + * array( + * "BE" => array( + * "Name" => "Belgium", + * "Alpha2" => "BE", + * "Alpha3" => "BEL", + * "IDC" => 32, + * ), + * ), + * ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidDataForValidate() + { + return [ + [ + [ + 'validatorConfiguration' => [ + 'RelationListValueValidator' => [ + 'selectionLimit' => 0, + ], + ], + ], + new Value([5, 6, 7]), + ], + [ + [ + 'validatorConfiguration' => [ + 'RelationListValueValidator' => [ + 'selectionLimit' => 1, + ], + ], + ], + new Value([5]), + ], + [ + [ + 'validatorConfiguration' => [ + 'RelationListValueValidator' => [ + 'selectionLimit' => 3, + ], + ], + ], + new Value([5, 6]), + ], + [ + [ + 'validatorConfiguration' => [ + 'RelationListValueValidator' => [ + 'selectionLimit' => 3, + ], + ], + ], + new Value([]), + ], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings, + * field value and corresponding validation errors returned by + * the {@link validate()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten + * if a FieldType supports validation! + * + * For example: + * + * <code> + * return array( + * array( + * array( + * "validatorConfiguration" => array( + * "IntegerValueValidator" => array( + * "minIntegerValue" => 5, + * "maxIntegerValue" => 10 + * ), + * ), + * ), + * new IntegerValue( 3 ), + * array( + * new ValidationError( + * "The value can not be lower than %size%.", + * null, + * array( + * "%size%" => 5 + * ), + * ), + * ), + * ), + * array( + * array( + * "fieldSettings" => array( + * "isMultiple" => false + * ), + * ), + * new CountryValue( + * "BE" => array( + * "Name" => "Belgium", + * "Alpha2" => "BE", + * "Alpha3" => "BEL", + * "IDC" => 32, + * ), + * "FR" => array( + * "Name" => "France", + * "Alpha2" => "FR", + * "Alpha3" => "FRA", + * "IDC" => 33, + * ), + * ) + * ), + * array( + * new ValidationError( + * "Field definition does not allow multiple countries to be selected." + * ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInvalidDataForValidate() + { + return [ + [ + [ + 'validatorConfiguration' => [ + 'RelationListValueValidator' => [ + 'selectionLimit' => 3, + ], + ], + ], + new Value([1, 2, 3, 4]), + [ + new ValidationError( + 'The selected content items number cannot be higher than %limit%.', + null, + [ + '%limit%' => 3, + ], + 'destinationContentIds' + ), + ], + ], + ]; + } + + public function testValidateNotExistingContentRelations(): void + { + $invalidDestinationContentId = (int) 'invalid'; + $invalidDestinationContentId2 = (int) 'invalid-second'; + + $this->targetContentValidator + ->expects(self::exactly(2)) + ->method('validate') + ->withConsecutive([$invalidDestinationContentId], [$invalidDestinationContentId2]) + ->willReturnOnConsecutiveCalls( + $this->generateValidationError($invalidDestinationContentId), + $this->generateValidationError($invalidDestinationContentId2) + ); + + $validationErrors = $this->doValidate([], new Value([$invalidDestinationContentId, $invalidDestinationContentId2])); + + self::assertIsArray($validationErrors); + self::assertCount(2, $validationErrors); + } + + public function testValidateInvalidContentType(): void + { + $destinationContentId = 12; + $destinationContentId2 = 13; + $allowedContentTypes = ['article', 'folder']; + + $this->targetContentValidator + ->method('validate') + ->withConsecutive( + [$destinationContentId, $allowedContentTypes], + [$destinationContentId2, $allowedContentTypes] + ) + ->willReturnOnConsecutiveCalls( + $this->generateContentTypeValidationError('test'), + $this->generateContentTypeValidationError('test') + ); + + $validationErrors = $this->doValidate( + ['fieldSettings' => ['selectionContentTypes' => $allowedContentTypes]], + new Value([$destinationContentId, $destinationContentId2]) + ); + + self::assertIsArray($validationErrors); + self::assertCount(2, $validationErrors); + } + + private function generateValidationError(string $contentId): ValidationError + { + return new ValidationError( + 'Content with identifier %contentId% is not a valid relation target', + null, + [ + '%contentId%' => $contentId, + ], + 'targetContentId' + ); + } + + private function generateContentTypeValidationError(string $contentTypeIdentifier): ValidationError + { + return new ValidationError( + 'Content type %contentTypeIdentifier% is not a valid relation target', + null, + [ + '%contentTypeIdentifier%' => $contentTypeIdentifier, + ], + 'targetContentId' + ); + } + + /** + * @covers \Ibexa\Core\FieldType\Relation\Type::getRelations + */ + public function testGetRelations() + { + $ft = $this->createFieldTypeUnderTest(); + $this->assertEquals( + [ + Relation::FIELD => [70, 72], + ], + $ft->getRelations($ft->acceptValue([70, 72])) + ); + } + + protected function provideFieldTypeIdentifier() + { + return 'ezobjectrelationlist'; + } + + /** + * @dataProvider provideDataForGetName + */ + public function testGetName( + SPIValue $value, + string $expected, + array $fieldSettings = [], + string $languageCode = 'en_GB' + ): void { + $fieldDefinitionMock = $this->getFieldDefinitionMock($fieldSettings); + + $name = $this->getFieldTypeUnderTest()->getName($value, $fieldDefinitionMock, $languageCode); + + self::assertSame($expected, $name); + } + + public function provideDataForGetName(): array + { + return [ + [$this->getEmptyValueExpectation(), '', [], 'en_GB'], + [new Value([self::DESTINATION_CONTENT_ID_14, self::DESTINATION_CONTENT_ID_22]), 'name_14_en_GB name_22_en_GB', [], 'en_GB'], + [new Value([self::DESTINATION_CONTENT_ID_14, self::DESTINATION_CONTENT_ID_22]), 'Name_14_de_DE Name_22_de_DE', [], 'de_DE'], + ]; + } +} + +class_alias(RelationListTest::class, 'eZ\Publish\Core\FieldType\Tests\RelationListTest'); diff --git a/tests/lib/FieldType/RelationTest.php b/tests/lib/FieldType/RelationTest.php new file mode 100644 index 0000000000..86e4bf177e --- /dev/null +++ b/tests/lib/FieldType/RelationTest.php @@ -0,0 +1,506 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Contracts\Core\FieldType\Value as SPIValue; +use Ibexa\Contracts\Core\Persistence\Content\Handler as SPIContentHandler; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Relation\Type as RelationType; +use Ibexa\Core\FieldType\Relation\Value; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\Repository\Validator\TargetContentValidatorInterface; + +class RelationTest extends FieldTypeTest +{ + private const DESTINATION_CONTENT_ID = 14; + + private $contentHandler; + + /** @var \Ibexa\Core\Repository\Validator\TargetContentValidatorInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $targetContentValidator; + + protected function setUp(): void + { + parent::setUp(); + + $versionInfo = new VersionInfo([ + 'versionNo' => 24, + 'names' => [ + 'en_GB' => 'name_en_GB', + 'de_DE' => 'Name_de_DE', + ], + ]); + $currentVersionNo = 28; + $destinationContentInfo = $this->createMock(ContentInfo::class); + $destinationContentInfo + ->method('__get') + ->willReturnMap([ + ['currentVersionNo', $currentVersionNo], + ['mainLanguageCode', 'en_GB'], + ]); + + $this->contentHandler = $this->createMock(SPIContentHandler::class); + $this->contentHandler + ->method('loadContentInfo') + ->with(self::DESTINATION_CONTENT_ID) + ->willReturn($destinationContentInfo); + + $this->contentHandler + ->method('loadVersionInfo') + ->with(self::DESTINATION_CONTENT_ID, $currentVersionNo) + ->willReturn($versionInfo); + + $this->targetContentValidator = $this->createMock(TargetContentValidatorInterface::class); + } + + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\Relation\Type + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new RelationType( + $this->contentHandler, + $this->targetContentValidator + ); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return []; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return [ + 'selectionMethod' => [ + 'type' => 'int', + 'default' => RelationType::SELECTION_BROWSE, + ], + 'selectionRoot' => [ + 'type' => 'string', + 'default' => null, + ], + 'rootDefaultLocation' => [ + 'type' => 'bool', + 'default' => false, + ], + 'selectionContentTypes' => [ + 'type' => 'array', + 'default' => [], + ], + ]; + } + + /** + * Returns the empty value expected from the field type. + * + * @return \Ibexa\Core\FieldType\Relation\Value + */ + protected function getEmptyValueExpectation() + { + return new Value(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + true, + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + new Value(), + new Value(), + ], + [ + 23, + new Value(23), + ], + [ + new ContentInfo(['id' => 23]), + new Value(23), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new Value(23), + ['destinationContentId' => 23], + ], + [ + new Value(), + ['destinationContentId' => null], + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + ['destinationContentId' => 23], + new Value(23), + ], + [ + ['destinationContentId' => null], + new Value(), + ], + ]; + } + + /** + * Provide data sets with field settings which are considered valid by the + * {@link validateFieldSettings()} method. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( 'rows' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidFieldSettings() + { + return [ + [ + [ + 'selectionMethod' => RelationType::SELECTION_BROWSE, + 'selectionRoot' => 42, + ], + ], + [ + [ + 'selectionMethod' => RelationType::SELECTION_DROPDOWN, + 'selectionRoot' => 'some-key', + ], + ], + ]; + } + + /** + * Provide data sets with field settings which are considered invalid by the + * {@link validateFieldSettings()} method. The method must return a + * non-empty array of validation error when receiving such field settings. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * true, + * ), + * array( + * array( 'nonExistentKey' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInValidFieldSettings() + { + return [ + [ + // Unknown key + [ + 'unknownKey' => 23, + 'selectionMethod' => RelationType::SELECTION_BROWSE, + 'selectionRoot' => 42, + ], + ], + [ + // Invalid selectionMethod + [ + 'selectionMethod' => 2342, + 'selectionRoot' => 42, + ], + ], + [ + // Invalid selectionRoot + [ + 'selectionMethod' => RelationType::SELECTION_DROPDOWN, + 'selectionRoot' => [], + ], + ], + ]; + } + + /** + * @covers \Ibexa\Core\FieldType\Relation\Type::getRelations + */ + public function testGetRelations() + { + $ft = $this->createFieldTypeUnderTest(); + $this->assertEquals( + [ + Relation::FIELD => [70], + ], + $ft->getRelations($ft->acceptValue(70)) + ); + } + + public function testValidateNotExistingContentRelation(): void + { + $destinationContentId = 'invalid'; + + $this->targetContentValidator + ->expects(self::once()) + ->method('validate') + ->with((int) $destinationContentId) + ->willReturn($this->generateValidationError($destinationContentId)); + + $validationErrors = $this->doValidate([], new Value($destinationContentId)); + + self::assertIsArray($validationErrors); + self::assertEquals([$this->generateValidationError($destinationContentId)], $validationErrors); + } + + public function testValidateInvalidContentType(): void + { + $destinationContentId = 12; + $allowedContentTypes = ['article', 'folder']; + + $this->targetContentValidator + ->expects(self::once()) + ->method('validate') + ->with($destinationContentId, $allowedContentTypes) + ->willReturn($this->generateContentTypeValidationError('test')); + + $validationErrors = $this->doValidate( + ['fieldSettings' => ['selectionContentTypes' => $allowedContentTypes]], + new Value($destinationContentId) + ); + + self::assertIsArray($validationErrors); + self::assertEquals([$this->generateContentTypeValidationError('test')], $validationErrors); + } + + private function generateValidationError(string $contentId): ValidationError + { + return new ValidationError( + 'Content with identifier %contentId% is not a valid relation target', + null, + [ + '%contentId%' => $contentId, + ], + 'targetContentId' + ); + } + + private function generateContentTypeValidationError(string $contentTypeIdentifier): ValidationError + { + return new ValidationError( + 'Content type %contentTypeIdentifier% is not a valid relation target', + null, + [ + '%contentTypeIdentifier%' => $contentTypeIdentifier, + ], + 'targetContentId' + ); + } + + protected function provideFieldTypeIdentifier() + { + return 'ezobjectrelation'; + } + + /** + * @dataProvider provideDataForGetName + */ + public function testGetName( + SPIValue $value, + string $expected, + array $fieldSettings = [], + string $languageCode = 'en_GB' + ): void { + /** @var \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition|\PHPUnit\Framework\MockObject\MockObject $fieldDefinitionMock */ + $fieldDefinitionMock = $this->createMock(FieldDefinition::class); + $fieldDefinitionMock->method('getFieldSettings')->willReturn($fieldSettings); + + $name = $this->getFieldTypeUnderTest()->getName($value, $fieldDefinitionMock, $languageCode); + + self::assertSame($expected, $name); + } + + public function provideDataForGetName(): array + { + return [ + 'empty_destination_content_id' => [ + $this->getEmptyValueExpectation(), '', [], 'en_GB', + ], + 'destination_content_id' => [ + new Value(self::DESTINATION_CONTENT_ID), 'name_en_GB', [], 'en_GB', + ], + 'destination_content_id_de_DE' => [ + new Value(self::DESTINATION_CONTENT_ID), 'Name_de_DE', [], 'de_DE', + ], + ]; + } + + public function provideValidDataForValidate(): array + { + return [ + [[], new Value(5)], + ]; + } + + public function provideInvalidDataForValidate(): array + { + return [ + [[], new Value('invalid'), []], + ]; + } +} + +class_alias(RelationTest::class, 'eZ\Publish\Core\FieldType\Tests\RelationTest'); diff --git a/tests/lib/FieldType/SelectionTest.php b/tests/lib/FieldType/SelectionTest.php new file mode 100644 index 0000000000..05300261b3 --- /dev/null +++ b/tests/lib/FieldType/SelectionTest.php @@ -0,0 +1,623 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Contracts\Core\FieldType\Value as SPIValue; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Selection\Type as Selection; +use Ibexa\Core\FieldType\Selection\Value as SelectionValue; +use Ibexa\Core\FieldType\ValidationError; + +/** + * @group fieldType + * @group ezselection + */ +class SelectionTest extends FieldTypeTest +{ + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new Selection(); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return []; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return [ + 'isMultiple' => [ + 'type' => 'bool', + 'default' => false, + ], + 'options' => [ + 'type' => 'hash', + 'default' => [], + ], + 'multilingualOptions' => [ + 'type' => 'hash', + 'default' => [], + ], + ]; + } + + /** + * Returns the empty value expected from the field type. + * + * @return \Ibexa\Core\FieldType\Selection\Value + */ + protected function getEmptyValueExpectation() + { + return new SelectionValue(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + 23, + InvalidArgumentException::class, + ], + [ + 'sindelfingen', + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + [], + new SelectionValue(), + ], + [ + [23], + new SelectionValue([23]), + ], + [ + [23, 42], + new SelectionValue([23, 42]), + ], + [ + new SelectionValue([23, 42]), + new SelectionValue([23, 42]), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new SelectionValue(), + [], + ], + [ + new SelectionValue([23, 42]), + [23, 42], + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + [], + new SelectionValue(), + ], + [ + [23, 42], + new SelectionValue([23, 42]), + ], + ]; + } + + /** + * Provide data sets with field settings which are considered valid by the + * {@link validateFieldSettings()} method. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( 'rows' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidFieldSettings() + { + return [ + [ + [], + ], + [ + [ + 'isMultiple' => true, + 'options' => ['foo', 'bar'], + ], + ], + [ + [ + 'isMultiple' => false, + 'options' => [23, 42], + ], + ], + ]; + } + + /** + * Provide data sets with field settings which are considered invalid by the + * {@link validateFieldSettings()} method. The method must return a + * non-empty array of validation error when receiving such field settings. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * true, + * ), + * array( + * array( 'nonExistentKey' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInValidFieldSettings() + { + return [ + [ + [ + // isMultiple must be bool + 'isMultiple' => 23, + ], + ], + [ + [ + // options must be array + 'options' => 23, + ], + ], + ]; + } + + protected function provideFieldTypeIdentifier() + { + return 'ezselection'; + } + + /** + * @dataProvider provideDataForGetName + */ + public function testGetName( + SPIValue $value, + string $expected, + array $fieldSettings = [], + string $languageCode = 'en_GB' + ): void { + $fieldDefinitionMock = $this->getFieldDefinitionMock($fieldSettings); + $fieldDefinitionMock + ->method('__get') + ->with('mainLanguageCode') + ->willReturn('de_DE'); + + $name = $this->getFieldTypeUnderTest()->getName($value, $fieldDefinitionMock, $languageCode); + + self::assertSame($expected, $name); + } + + public function provideDataForGetName(): array + { + return [ + 'empty_value_and_field_settings' => [$this->getEmptyValueExpectation(), '', [], 'en_GB'], + 'one_option' => [ + new SelectionValue(['optionIndex1']), + 'option_1', + ['options' => ['optionIndex1' => 'option_1']], + 'en_GB', + ], + 'two_options' => [ + new SelectionValue(['optionIndex1', 'optionIndex2']), + 'option_1 option_2', + ['options' => ['optionIndex1' => 'option_1', 'optionIndex2' => 'option_2']], + 'en_GB', + ], + 'multilingual_options' => [ + new SelectionValue(['optionIndex1', 'optionIndex2']), + 'option_1 option_2', + ['multilingualOptions' => ['en_GB' => ['optionIndex1' => 'option_1', 'optionIndex2' => 'option_2']]], + 'en_GB', + ], + 'multilingual_options_with_main_language_code' => [ + new SelectionValue(['optionIndex3', 'optionIndex4']), + 'option_3 option_4', + ['multilingualOptions' => [ + 'en_GB' => ['optionIndex1' => 'option_1', 'optionIndex2' => 'option_2'], + 'de_DE' => ['optionIndex3' => 'option_3', 'optionIndex4' => 'option_4'], + ]], + 'de_DE', + ], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings and + * field value which are considered valid by the {@link validate()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten if + * a FieldType supports validation! + * + * For example: + * + * <code> + * return array( + * array( + * array( + * "validatorConfiguration" => array( + * "StringLengthValidator" => array( + * "minStringLength" => 2, + * "maxStringLength" => 10, + * ), + * ), + * ), + * new TextLineValue( "lalalala" ), + * ), + * array( + * array( + * "fieldSettings" => array( + * 'isMultiple' => true + * ), + * ), + * new CountryValue( + * array( + * "BE" => array( + * "Name" => "Belgium", + * "Alpha2" => "BE", + * "Alpha3" => "BEL", + * "IDC" => 32, + * ), + * ), + * ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidDataForValidate() + { + return [ + [ + [ + 'fieldSettings' => [ + 'isMultiple' => true, + 'options' => [0 => 1, 1 => 2], + ], + ], + new SelectionValue([0, 1]), + ], + [ + [ + 'fieldSettings' => [ + 'isMultiple' => false, + 'options' => [0 => 1, 1 => 2], + ], + ], + new SelectionValue([1]), + ], + [ + [ + 'fieldSettings' => [ + 'isMultiple' => false, + 'options' => [0 => 1, 1 => 2], + ], + ], + new SelectionValue(), + ], + [ + [ + 'fieldSettings' => [ + 'isMultiple' => false, + 'options' => [0 => 1, 1 => 2], + 'multilingualOptions' => [ + 'en_GB' => [0 => 1, 1 => 2], + 'de_DE' => [0 => 1, 1 => 2], + ], + ], + ], + new SelectionValue([1]), + ], + [ + [ + 'fieldSettings' => [ + 'isMultiple' => false, + 'options' => [0 => 1, 1 => 2], + 'multilingualOptions' => [ + 'en_GB' => [0 => 1, 1 => 2], + 'de_DE' => [0 => 1], + ], + ], + ], + new SelectionValue([1]), + ], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings, + * field value and corresponding validation errors returned by + * the {@link validate()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten + * if a FieldType supports validation! + * + * For example: + * + * <code> + * return array( + * array( + * array( + * "validatorConfiguration" => array( + * "IntegerValueValidator" => array( + * "minIntegerValue" => 5, + * "maxIntegerValue" => 10 + * ), + * ), + * ), + * new IntegerValue( 3 ), + * array( + * new ValidationError( + * "The value can not be lower than %size%.", + * null, + * array( + * "size" => 5 + * ), + * ), + * ), + * ), + * array( + * array( + * "fieldSettings" => array( + * "isMultiple" => false + * ), + * ), + * new CountryValue( + * "BE" => array( + * "Name" => "Belgium", + * "Alpha2" => "BE", + * "Alpha3" => "BEL", + * "IDC" => 32, + * ), + * "FR" => array( + * "Name" => "France", + * "Alpha2" => "FR", + * "Alpha3" => "FRA", + * "IDC" => 33, + * ), + * ) + * ), + * array( + * new ValidationError( + * "Field definition does not allow multiple countries to be selected." + * ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInvalidDataForValidate() + { + return [ + [ + [ + 'fieldSettings' => [ + 'isMultiple' => false, + 'options' => [0 => 1, 1 => 2], + ], + ], + new SelectionValue([0, 1]), + [ + new ValidationError( + 'Field definition does not allow multiple options to be selected.', + null, + [], + 'selection' + ), + ], + ], + [ + [ + 'fieldSettings' => [ + 'isMultiple' => false, + 'options' => [0 => 1, 1 => 2], + ], + ], + new SelectionValue([3]), + [ + new ValidationError( + 'Option with index %index% does not exist in the field definition.', + null, + [ + '%index%' => 3, + ], + 'selection' + ), + ], + ], + [ + [ + 'fieldSettings' => [ + 'isMultiple' => false, + 'options' => [0 => 1, 1 => 2], + 'multilingualOptions' => [ + 'en_GB' => [0 => 1, 1 => 2], + 'de_DE' => [0 => 1], + ], + ], + ], + new SelectionValue([3]), + [ + new ValidationError( + 'Option with index %index% does not exist in the field definition.', + null, + [ + '%index%' => 3, + ], + 'selection' + ), + ], + ], + ]; + } +} + +class_alias(SelectionTest::class, 'eZ\Publish\Core\FieldType\Tests\SelectionTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/StringLengthValidatorTest.php b/tests/lib/FieldType/StringLengthValidatorTest.php similarity index 87% rename from eZ/Publish/Core/FieldType/Tests/StringLengthValidatorTest.php rename to tests/lib/FieldType/StringLengthValidatorTest.php index 9f94c780cb..26bfc39b84 100644 --- a/eZ/Publish/Core/FieldType/Tests/StringLengthValidatorTest.php +++ b/tests/lib/FieldType/StringLengthValidatorTest.php @@ -4,14 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests; +namespace Ibexa\Tests\Core\FieldType; -use eZ\Publish\API\Repository\Values\Translation\Message; -use eZ\Publish\API\Repository\Values\Translation\Plural; -use eZ\Publish\Core\FieldType\TextLine\Value as TextLineValue; -use eZ\Publish\Core\FieldType\Validator; -use eZ\Publish\Core\FieldType\Validator\StringLengthValidator; -use eZ\Publish\SPI\FieldType\ValidationError; +use Ibexa\Contracts\Core\FieldType\ValidationError; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Translation\Message; +use Ibexa\Contracts\Core\Repository\Values\Translation\Plural; +use Ibexa\Core\FieldType\TextLine\Value as TextLineValue; +use Ibexa\Core\FieldType\Validator; +use Ibexa\Core\FieldType\Validator\StringLengthValidator; use PHPUnit\Framework\TestCase; /** @@ -50,8 +51,8 @@ public function testConstructor() /** * Tests setting and getting constraints. * - * @covers \eZ\Publish\Core\FieldType\Validator::initializeWithConstraints - * @covers \eZ\Publish\Core\FieldType\Validator::__get + * @covers \Ibexa\Core\FieldType\Validator::initializeWithConstraints + * @covers \Ibexa\Core\FieldType\Validator::__get */ public function testConstraintsInitializeGet() { @@ -70,7 +71,7 @@ public function testConstraintsInitializeGet() /** * Test getting constraints schema. * - * @covers \eZ\Publish\Core\FieldType\Validator::getConstraintsSchema + * @covers \Ibexa\Core\FieldType\Validator::getConstraintsSchema */ public function testGetConstraintsSchema() { @@ -91,8 +92,8 @@ public function testGetConstraintsSchema() /** * Tests setting and getting constraints. * - * @covers \eZ\Publish\Core\FieldType\Validator::__set - * @covers \eZ\Publish\Core\FieldType\Validator::__get + * @covers \Ibexa\Core\FieldType\Validator::__set + * @covers \Ibexa\Core\FieldType\Validator::__get */ public function testConstraintsSetGet() { @@ -110,11 +111,11 @@ public function testConstraintsSetGet() /** * Tests initializing with a wrong constraint. * - * @covers \eZ\Publish\Core\FieldType\Validator::initializeWithConstraints + * @covers \Ibexa\Core\FieldType\Validator::initializeWithConstraints */ public function testInitializeBadConstraint() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); + $this->expectException(PropertyNotFoundException::class); $constraints = [ 'unexisting' => 0, @@ -128,11 +129,11 @@ public function testInitializeBadConstraint() /** * Tests setting a wrong constraint. * - * @covers \eZ\Publish\Core\FieldType\Validator::__set + * @covers \Ibexa\Core\FieldType\Validator::__set */ public function testSetBadConstraint() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); + $this->expectException(PropertyNotFoundException::class); $validator = new StringLengthValidator(); $validator->unexisting = 0; @@ -141,11 +142,11 @@ public function testSetBadConstraint() /** * Tests getting a wrong constraint. * - * @covers \eZ\Publish\Core\FieldType\Validator::__get + * @covers \Ibexa\Core\FieldType\Validator::__get */ public function testGetBadConstraint() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\PropertyNotFoundException::class); + $this->expectException(PropertyNotFoundException::class); $validator = new StringLengthValidator(); $null = $validator->unexisting; @@ -155,8 +156,8 @@ public function testGetBadConstraint() * Tests validating a correct value. * * @dataProvider providerForValidateOK - * @covers \eZ\Publish\Core\FieldType\Validator\StringLengthValidator::validate - * @covers \eZ\Publish\Core\FieldType\Validator::getMessage + * @covers \Ibexa\Core\FieldType\Validator\StringLengthValidator::validate + * @covers \Ibexa\Core\FieldType\Validator::getMessage */ public function testValidateCorrectValues($value) { @@ -181,7 +182,7 @@ public function providerForValidateOK() * Tests validating a wrong value. * * @dataProvider providerForValidateKO - * @covers \eZ\Publish\Core\FieldType\Validator\StringLengthValidator::validate + * @covers \Ibexa\Core\FieldType\Validator\StringLengthValidator::validate */ public function testValidateWrongValues($value, $messageSingular, $messagePlural, $values) { @@ -247,7 +248,7 @@ public function providerForValidateKO() * Tests validation of constraints. * * @dataProvider providerForValidateConstraintsOK - * @covers \eZ\Publish\Core\FieldType\Validator\FileSizeValidator::validateConstraints + * @covers \Ibexa\Core\FieldType\Validator\FileSizeValidator::validateConstraints */ public function testValidateConstraintsCorrectValues($constraints) { @@ -293,7 +294,7 @@ public function providerForValidateConstraintsOK() * Tests validation of constraints. * * @dataProvider providerForValidateConstraintsKO - * @covers \eZ\Publish\Core\FieldType\Validator\FileSizeValidator::validateConstraints + * @covers \Ibexa\Core\FieldType\Validator\FileSizeValidator::validateConstraints */ public function testValidateConstraintsWrongValues($constraints, $expectedMessages, $values) { @@ -403,3 +404,5 @@ public function providerForValidateConstraintsKO() ]; } } + +class_alias(StringLengthValidatorTest::class, 'eZ\Publish\Core\FieldType\Tests\StringLengthValidatorTest'); diff --git a/tests/lib/FieldType/TextBlockTest.php b/tests/lib/FieldType/TextBlockTest.php new file mode 100644 index 0000000000..c2fc16a2d5 --- /dev/null +++ b/tests/lib/FieldType/TextBlockTest.php @@ -0,0 +1,334 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\TextBlock\Type as TextBlockType; +use Ibexa\Core\FieldType\TextBlock\Value as TextBlockValue; + +/** + * @group fieldType + * @group ezselection + */ +class TextBlockTest extends FieldTypeTest +{ + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Contracts\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new TextBlockType(); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return []; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return [ + 'textRows' => [ + 'type' => 'int', + 'default' => 10, + ], + ]; + } + + /** + * Returns the empty value expected from the field type. + * + * @return \Ibexa\Core\FieldType\TextLine\Value + */ + protected function getEmptyValueExpectation() + { + return new TextBlockValue(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + 23, + InvalidArgumentException::class, + ], + [ + new TextBlockValue(23), + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + null, + new TextBlockValue(), + ], + [ + '', + new TextBlockValue(), + ], + [ + 'sindelfingen', + new TextBlockValue('sindelfingen'), + ], + [ + new TextBlockValue('sindelfingen'), + new TextBlockValue('sindelfingen'), + ], + [ + new TextBlockValue(''), + new TextBlockValue(), + ], + [ + new TextBlockValue(null), + new TextBlockValue(), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new TextBlockValue(), + null, + ], + [ + new TextBlockValue('sindelfingen'), + 'sindelfingen', + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + '', + new TextBlockValue(), + ], + [ + 'sindelfingen', + new TextBlockValue('sindelfingen'), + ], + ]; + } + + /** + * Provide data sets with field settings which are considered valid by the + * {@link validateFieldSettings()} method. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( 'rows' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidFieldSettings() + { + return [ + [ + [], + ], + [ + [ + 'textRows' => 23, + ], + ], + ]; + } + + /** + * Provide data sets with field settings which are considered invalid by the + * {@link validateFieldSettings()} method. The method must return a + * non-empty array of validation error when receiving such field settings. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * true, + * ), + * array( + * array( 'nonExistentKey' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInValidFieldSettings() + { + return [ + [ + [ + 'non-existent' => 'foo', + ], + ], + [ + [ + // textRows must be integer + 'textRows' => 'foo', + ], + ], + ]; + } + + protected function provideFieldTypeIdentifier() + { + return 'eztext'; + } + + public function provideDataForGetName(): array + { + return [ + [$this->getEmptyValueExpectation(), '', [], 'en_GB'], + [new TextBlockValue('This is a piece of text'), 'This is a piece of text', [], 'en_GB'], + ]; + } +} + +class_alias(TextBlockTest::class, 'eZ\Publish\Core\FieldType\Tests\TextBlockTest'); diff --git a/tests/lib/FieldType/TextLineTest.php b/tests/lib/FieldType/TextLineTest.php new file mode 100644 index 0000000000..0a903c134c --- /dev/null +++ b/tests/lib/FieldType/TextLineTest.php @@ -0,0 +1,699 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\TextLine\Type as TextLineType; +use Ibexa\Core\FieldType\TextLine\Value as TextLineValue; +use Ibexa\Core\FieldType\ValidationError; + +/** + * @group fieldType + * @group ezstring + */ +class TextLineTest extends FieldTypeTest +{ + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new TextLineType(); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return [ + 'StringLengthValidator' => [ + 'minStringLength' => [ + 'type' => 'int', + 'default' => 0, + ], + 'maxStringLength' => [ + 'type' => 'int', + 'default' => null, + ], + ], + ]; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return []; + } + + /** + * Returns the empty value expected from the field type. + * + * @return \Ibexa\Core\FieldType\TextLine\Value + */ + protected function getEmptyValueExpectation() + { + return new TextLineValue(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + 23, + InvalidArgumentException::class, + ], + [ + new TextLineValue(23), + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + null, + new TextLineValue(), + ], + [ + '', + new TextLineValue(), + ], + [ + ' ', + new TextLineValue(), + ], + [ + ' sindelfingen ', + new TextLineValue(' sindelfingen '), + ], + [ + new TextLineValue(' sindelfingen '), + new TextLineValue(' sindelfingen '), + ], + [ + // 11+ numbers - EZP-21771 + '12345678901', + new TextLineValue('12345678901'), + ], + [ + new TextLineValue(''), + new TextLineValue(), + ], + [ + new TextLineValue(' '), + new TextLineValue(), + ], + [ + new TextLineValue(null), + new TextLineValue(), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new TextLineValue(), + null, + ], + [ + new TextLineValue(''), + null, + ], + [ + new TextLineValue('sindelfingen'), + 'sindelfingen', + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + null, + new TextLineValue(), + ], + [ + '', + new TextLineValue(), + ], + [ + 'sindelfingen', + new TextLineValue('sindelfingen'), + ], + ]; + } + + /** + * Provide data sets with validator configurations which are considered + * valid by the {@link validateValidatorConfiguration()} method. + * + * Returns an array of data provider sets with a single argument: A valid + * set of validator configurations. + * + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( + * 'StringLengthValidator' => array( + * 'minStringLength' => 0, + * 'maxStringLength' => 23, + * ) + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidValidatorConfiguration() + { + return [ + [ + [], + ], + [ + [ + 'StringLengthValidator' => [ + 'minStringLength' => null, + ], + ], + ], + [ + [ + 'StringLengthValidator' => [ + 'minStringLength' => 23, + ], + ], + ], + [ + [ + 'StringLengthValidator' => [ + 'maxStringLength' => null, + ], + ], + ], + [ + [ + 'StringLengthValidator' => [ + 'maxStringLength' => 23, + ], + ], + ], + [ + [ + 'StringLengthValidator' => [ + 'minStringLength' => 23, + 'maxStringLength' => 42, + ], + ], + ], + ]; + } + + /** + * Provide data sets with validator configurations which are considered + * invalid by the {@link validateValidatorConfiguration()} method. The + * method must return a non-empty array of valiation errors when receiving + * one of the provided values. + * + * Returns an array of data provider sets with a single argument: A valid + * set of validator configurations. + * + * For example: + * + * <code> + * return array( + * array( + * array( + * 'NonExistentValidator' => array(), + * ), + * ), + * array( + * array( + * // Typos + * 'InTEgervALUeVALIdator' => array( + * 'iinStringLength' => 0, + * 'maxStringLength' => 23, + * ) + * ) + * ), + * array( + * array( + * 'StringLengthValidator' => array( + * // Incorrect value types + * 'minStringLength' => true, + * 'maxStringLength' => false, + * ) + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInvalidValidatorConfiguration() + { + return [ + [ + [ + 'NonExistentValidator' => [], + ], + ], + [ + [ + 'StringLengthValidator' => [ + 'nonExistentValue' => 23, + ], + ], + ], + [ + [ + 'StringLengthValidator' => [ + 'minStringLength' => .23, + ], + ], + ], + [ + [ + 'StringLengthValidator' => [ + 'maxStringLength' => .42, + ], + ], + ], + [ + [ + 'StringLengthValidator' => [ + 'minStringLength' => -23, + ], + ], + ], + [ + [ + 'StringLengthValidator' => [ + 'maxStringLength' => -42, + ], + ], + ], + [ + [ + 'StringLengthValidator' => [ + 'maxStringLength' => 23, + 'minStringLength' => 42, + ], + ], + ], + ]; + } + + protected function provideFieldTypeIdentifier() + { + return 'ezstring'; + } + + public function provideDataForGetName(): array + { + return [ + [$this->getEmptyValueExpectation(), '', [], 'en_GB'], + [new TextLineValue('This is a line of text'), 'This is a line of text', [], 'en_GB'], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings and + * field value which are considered valid by the {@link validate()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten if + * a FieldType supports validation! + * + * For example: + * + * <code> + * return array( + * array( + * array( + * "validatorConfiguration" => array( + * "StringLengthValidator" => array( + * "minStringLength" => 2, + * "maxStringLength" => 10, + * ), + * ), + * ), + * new TextLineValue( "lalalala" ), + * ), + * array( + * array( + * "fieldSettings" => array( + * 'isMultiple' => true + * ), + * ), + * new CountryValue( + * array( + * "BE" => array( + * "Name" => "Belgium", + * "Alpha2" => "BE", + * "Alpha3" => "BEL", + * "IDC" => 32, + * ), + * ), + * ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidDataForValidate() + { + return [ + [ + [ + 'validatorConfiguration' => [ + 'StringLengthValidator' => [ + 'minStringLength' => 2, + 'maxStringLength' => 10, + ], + ], + ], + new TextLineValue('lalalala'), + ], + [ + [ + 'validatorConfiguration' => [ + 'StringLengthValidator' => [ + 'maxStringLength' => 10, + ], + ], + ], + new TextLineValue('lililili'), + ], + [ + [ + 'validatorConfiguration' => [ + 'StringLengthValidator' => [ + 'maxStringLength' => 10, + ], + ], + ], + new TextLineValue('♔♕♖♗♘♙♚♛♜♝'), + ], + ]; + } + + /** + * Provides data sets with validator configuration and/or field settings, + * field value and corresponding validation errors returned by + * the {@link validate()} method. + * + * ATTENTION: This is a default implementation, which must be overwritten + * if a FieldType supports validation! + * + * For example: + * + * <code> + * return array( + * array( + * array( + * "validatorConfiguration" => array( + * "IntegerValueValidator" => array( + * "minIntegerValue" => 5, + * "maxIntegerValue" => 10 + * ), + * ), + * ), + * new IntegerValue( 3 ), + * array( + * new ValidationError( + * "The value can not be lower than %size%.", + * null, + * array( + * "size" => 5 + * ), + * ), + * ), + * ), + * array( + * array( + * "fieldSettings" => array( + * "isMultiple" => false + * ), + * ), + * new CountryValue( + * "BE" => array( + * "Name" => "Belgium", + * "Alpha2" => "BE", + * "Alpha3" => "BEL", + * "IDC" => 32, + * ), + * "FR" => array( + * "Name" => "France", + * "Alpha2" => "FR", + * "Alpha3" => "FRA", + * "IDC" => 33, + * ), + * ) + * ), + * array( + * new ValidationError( + * "Field definition does not allow multiple countries to be selected." + * ), + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInvalidDataForValidate() + { + return [ + [ + [ + 'validatorConfiguration' => [ + 'StringLengthValidator' => [ + 'minStringLength' => 5, + 'maxStringLength' => 10, + ], + ], + ], + new TextLineValue('aaa'), + [ + new ValidationError( + 'The string cannot be shorter than %size% character.', + 'The string cannot be shorter than %size% characters.', + [ + '%size%' => 5, + ], + 'text' + ), + ], + ], + [ + [ + 'validatorConfiguration' => [ + 'StringLengthValidator' => [ + 'minStringLength' => 5, + 'maxStringLength' => 10, + ], + ], + ], + new TextLineValue('0123456789012345'), + [ + new ValidationError( + 'The string can not exceed %size% character.', + 'The string can not exceed %size% characters.', + [ + '%size%' => 10, + ], + 'text' + ), + ], + ], + [ + [ + 'validatorConfiguration' => [ + 'StringLengthValidator' => [ + 'minStringLength' => 10, + 'maxStringLength' => 5, + ], + ], + ], + new TextLineValue('1234567'), + [ + new ValidationError( + 'The string can not exceed %size% character.', + 'The string can not exceed %size% characters.', + [ + '%size%' => 5, + ], + 'text' + ), + new ValidationError( + 'The string cannot be shorter than %size% character.', + 'The string cannot be shorter than %size% characters.', + [ + '%size%' => 10, + ], + 'text' + ), + ], + ], + [ + [ + 'validatorConfiguration' => [ + 'StringLengthValidator' => [ + 'minStringLength' => 5, + 'maxStringLength' => 10, + ], + ], + ], + new TextLineValue('ABC♔'), + [ + new ValidationError( + 'The string cannot be shorter than %size% character.', + 'The string cannot be shorter than %size% characters.', + [ + '%size%' => 5, + ], + 'text' + ), + ], + ], + ]; + } +} + +class_alias(TextLineTest::class, 'eZ\Publish\Core\FieldType\Tests\TextLineTest'); diff --git a/tests/lib/FieldType/TimeTest.php b/tests/lib/FieldType/TimeTest.php new file mode 100644 index 0000000000..816baa4607 --- /dev/null +++ b/tests/lib/FieldType/TimeTest.php @@ -0,0 +1,362 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use DateTime; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Time\Type as Time; +use Ibexa\Core\FieldType\Time\Value as TimeValue; + +/** + * @group fieldType + * @group eztime + */ +class TimeTest extends FieldTypeTest +{ + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new Time(); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return []; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return [ + 'useSeconds' => [ + 'type' => 'bool', + 'default' => false, + ], + 'defaultType' => [ + 'type' => 'choice', + 'default' => Time::DEFAULT_EMPTY, + ], + ]; + } + + /** + * Returns the empty value expected from the field type. + */ + protected function getEmptyValueExpectation() + { + return new TimeValue(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + [], + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + $dateTime = new DateTime(); + // change timezone to UTC (+00:00) to be able to calculate proper TimeValue + $timestamp = $dateTime->setTimezone(new \DateTimeZone('UTC'))->getTimestamp(); + + return [ + [ + null, + new TimeValue(), + ], + [ + '2012-08-28 12:20', + new TimeValue(44400), + ], + [ + '2012-08-28 12:20 Europe/Berlin', + new TimeValue(44400), + ], + [ + '2012-08-28 12:20 Asia/Sakhalin', + new TimeValue(44400), + ], + [ + // create new DateTime object from timestamp w/o taking into account server timezone + (new DateTime('@1372896001'))->getTimestamp(), + new TimeValue(1), + ], + [ + TimeValue::fromTimestamp($timestamp), + new TimeValue( + $timestamp - $dateTime->setTime(0, 0, 0)->getTimestamp() + ), + ], + [ + clone $dateTime, + new TimeValue( + $dateTime->getTimestamp() - $dateTime->setTime(0, 0, 0)->getTimestamp() + ), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new TimeValue(), + null, + ], + [ + new TimeValue(0), + 0, + ], + [ + new TimeValue(200), + 200, + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + null, + new TimeValue(), + ], + [ + 0, + new TimeValue(0), + ], + [ + 200, + new TimeValue(200), + ], + ]; + } + + /** + * Provide data sets with field settings which are considered valid by the + * {@link validateFieldSettings()} method. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( 'rows' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidFieldSettings() + { + return [ + [ + [], + ], + [ + [ + 'useSeconds' => true, + 'defaultType' => Time::DEFAULT_EMPTY, + ], + ], + [ + [ + 'useSeconds' => false, + 'defaultType' => Time::DEFAULT_CURRENT_TIME, + ], + ], + ]; + } + + /** + * Provide data sets with field settings which are considered invalid by the + * {@link validateFieldSettings()} method. The method must return a + * non-empty array of validation error when receiving such field settings. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * true, + * ), + * array( + * array( 'nonExistentKey' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInValidFieldSettings() + { + return [ + [ + [ + // useSeconds must be bool + 'useSeconds' => 23, + ], + ], + [ + [ + // defaultType must be constant + 'defaultType' => 42, + ], + ], + ]; + } + + protected function provideFieldTypeIdentifier() + { + return 'eztime'; + } + + public function provideDataForGetName(): array + { + return [ + [$this->getEmptyValueExpectation(), '', [], 'en_GB'], + [new TimeValue(200), '12:03:20 am', [], 'en_GB'], + ]; + } +} + +class_alias(TimeTest::class, 'eZ\Publish\Core\FieldType\Tests\TimeTest'); diff --git a/eZ/Publish/Core/FieldType/Tests/Url/Gateway/DoctrineStorageTest.php b/tests/lib/FieldType/Url/Gateway/DoctrineStorageTest.php similarity index 85% rename from eZ/Publish/Core/FieldType/Tests/Url/Gateway/DoctrineStorageTest.php rename to tests/lib/FieldType/Url/Gateway/DoctrineStorageTest.php index 793f9d0dc7..508b892a58 100644 --- a/eZ/Publish/Core/FieldType/Tests/Url/Gateway/DoctrineStorageTest.php +++ b/tests/lib/FieldType/Url/Gateway/DoctrineStorageTest.php @@ -4,19 +4,19 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests\Url\Gateway; +namespace Ibexa\Tests\Core\FieldType\Url\Gateway; -use eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway; -use eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; +use Ibexa\Core\FieldType\Url\UrlStorage\Gateway; +use Ibexa\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; /** - * @covers \eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage + * @covers \Ibexa\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage */ class DoctrineStorageTest extends TestCase { /** - * @covers \eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage::getIdUrlMap + * @covers \Ibexa\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage::getIdUrlMap */ public function testGetIdUrlMap() { @@ -36,7 +36,7 @@ public function testGetIdUrlMap() } /** - * @covers \eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage::getUrlIdMap + * @covers \Ibexa\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage::getUrlIdMap */ public function testGetUrlIdMap() { @@ -60,7 +60,7 @@ public function testGetUrlIdMap() } /** - * @covers \eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage::insertUrl + * @covers \Ibexa\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage::insertUrl */ public function testInsertUrl() { @@ -106,7 +106,7 @@ public function testInsertUrl() } /** - * @covers \eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage::linkUrl + * @covers \Ibexa\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage::linkUrl */ public function testLinkUrl() { @@ -143,7 +143,7 @@ public function testLinkUrl() } /** - * @covers \eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage::unlinkUrl + * @covers \Ibexa\Core\FieldType\Url\UrlStorage\Gateway\DoctrineStorage::unlinkUrl */ public function testUnlinkUrl() { @@ -203,3 +203,5 @@ protected function getStorageGateway(): Gateway return $this->storageGateway; } } + +class_alias(DoctrineStorageTest::class, 'eZ\Publish\Core\FieldType\Tests\Url\Gateway\DoctrineStorageTest'); diff --git a/tests/lib/FieldType/Url/Gateway/_fixtures/urls.php b/tests/lib/FieldType/Url/Gateway/_fixtures/urls.php new file mode 100644 index 0000000000..4c0907cef2 --- /dev/null +++ b/tests/lib/FieldType/Url/Gateway/_fixtures/urls.php @@ -0,0 +1,40 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurl' => [ + 0 => [ + 'created' => '1343140541', + 'id' => '23', + 'is_valid' => '1', + 'last_checked' => '0', + 'modified' => '1343140541', + 'original_url_md5' => '9b492048041e95b32de08bafc86d759b', + 'url' => '/content/view/sitemap/2', + ], + 1 => [ + 'created' => '1343140541', + 'id' => '24', + 'is_valid' => '1', + 'last_checked' => '0', + 'modified' => '1343140541', + 'original_url_md5' => 'c86bcb109d8e70f9db65c803baafd550', + 'url' => '/content/view/tagcloud/2', + ], + ], + 'ezurl_object_link' => [ + 0 => [ + 'contentobject_attribute_id' => 42, + 'contentobject_attribute_version' => 5, + 'url_id' => 23, + ], + 1 => [ + 'contentobject_attribute_id' => 43, + 'contentobject_attribute_version' => 6, + 'url_id' => 24, + ], + ], +]; diff --git a/eZ/Publish/Core/FieldType/Tests/Url/UrlStorageTest.php b/tests/lib/FieldType/Url/UrlStorageTest.php similarity index 85% rename from eZ/Publish/Core/FieldType/Tests/Url/UrlStorageTest.php rename to tests/lib/FieldType/Url/UrlStorageTest.php index 69bdf9f1b7..84e52ed981 100644 --- a/eZ/Publish/Core/FieldType/Tests/Url/UrlStorageTest.php +++ b/tests/lib/FieldType/Url/UrlStorageTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\FieldType\Tests\Url; +namespace Ibexa\Tests\Core\FieldType\Url; -use eZ\Publish\Core\FieldType\Url\UrlStorage; -use eZ\Publish\SPI\FieldType\StorageGateway; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; +use Ibexa\Contracts\Core\FieldType\StorageGatewayInterface; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Core\FieldType\Url\UrlStorage; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; @@ -19,15 +19,15 @@ class UrlStorageTest extends TestCase public function testStoreFieldDataWithExistingUrl() { $versionInfo = new VersionInfo(['versionNo' => 24]); - $fieldValue = new FieldValue(['externalData' => 'http://ez.no']); + $fieldValue = new FieldValue(['externalData' => 'http://ibexa.co']); $field = new Field(['id' => 42, 'value' => $fieldValue]); $gateway = $this->getGatewayMock(); $gateway ->expects($this->once()) ->method('getUrlIdMap') - ->with(['http://ez.no']) - ->will($this->returnValue(['http://ez.no' => 12])); + ->with(['http://ibexa.co']) + ->will($this->returnValue(['http://ibexa.co' => 12])); $gateway ->expects($this->once()) @@ -53,20 +53,20 @@ public function testStoreFieldDataWithExistingUrl() public function testStoreFieldDataWithNewUrl() { $versionInfo = new VersionInfo(['versionNo' => 24]); - $fieldValue = new FieldValue(['externalData' => 'http://ez.no']); + $fieldValue = new FieldValue(['externalData' => 'http://ibexa.co']); $field = new Field(['id' => 42, 'value' => $fieldValue]); $gateway = $this->getGatewayMock(); $gateway ->expects($this->once()) ->method('getUrlIdMap') - ->with(['http://ez.no']) + ->with(['http://ibexa.co']) ->will($this->returnValue([])); $gateway ->expects($this->once()) ->method('insertUrl') - ->with('http://ez.no') + ->with('http://ibexa.co') ->will($this->returnValue(12)); $gateway @@ -131,12 +131,12 @@ public function testGetFieldData() ->expects($this->once()) ->method('getIdUrlMap') ->with([12]) - ->will($this->returnValue([12 => 'http://ez.no'])); + ->will($this->returnValue([12 => 'http://ibexa.co'])); $storage = $this->getPartlyMockedStorage($gateway); $storage->getFieldData($versionInfo, $field, $this->getContext()); - $this->assertEquals('http://ez.no', $field->value->externalData); + $this->assertEquals('http://ibexa.co', $field->value->externalData); } public function testGetFieldDataNotFound() @@ -211,11 +211,11 @@ public function testHasFieldData() } /** - * @param \eZ\Publish\SPI\FieldType\StorageGateway $gateway + * @param \Ibexa\Contracts\Core\FieldType\StorageGatewayInterface $gateway * - * @return \eZ\Publish\Core\FieldType\Url\UrlStorage|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\FieldType\Url\UrlStorage|\PHPUnit\Framework\MockObject\MockObject */ - protected function getPartlyMockedStorage(StorageGateway $gateway) + protected function getPartlyMockedStorage(StorageGatewayInterface $gateway) { return $this->getMockBuilder(UrlStorage::class) ->setMethods(null) @@ -253,11 +253,11 @@ protected function getLoggerMock() return $this->loggerMock; } - /** @var \eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\FieldType\Url\UrlStorage\Gateway|\PHPUnit\Framework\MockObject\MockObject */ protected $gatewayMock; /** - * @return \eZ\Publish\Core\FieldType\Url\UrlStorage\Gateway|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\FieldType\Url\UrlStorage\Gateway|\PHPUnit\Framework\MockObject\MockObject */ protected function getGatewayMock() { @@ -268,3 +268,5 @@ protected function getGatewayMock() return $this->gatewayMock; } } + +class_alias(UrlStorageTest::class, 'eZ\Publish\Core\FieldType\Tests\Url\UrlStorageTest'); diff --git a/tests/lib/FieldType/UrlTest.php b/tests/lib/FieldType/UrlTest.php new file mode 100644 index 0000000000..4545043abd --- /dev/null +++ b/tests/lib/FieldType/UrlTest.php @@ -0,0 +1,259 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\FieldType\Url\Type as UrlType; +use Ibexa\Core\FieldType\Url\Value as UrlValue; + +/** + * @group fieldType + * @group ezurl + */ +class UrlTest extends FieldTypeTest +{ + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\FieldType + */ + protected function createFieldTypeUnderTest() + { + $fieldType = new UrlType(); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return []; + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return []; + } + + /** + * Returns the empty value expected from the field type. + */ + protected function getEmptyValueExpectation() + { + return new UrlValue(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + 23, + InvalidArgumentException::class, + ], + [ + new UrlValue(23), + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + null, + new UrlValue(), + ], + [ + 'http://example.com/sindelfingen', + new UrlValue('http://example.com/sindelfingen'), + ], + [ + new UrlValue('http://example.com/sindelfingen', 'Sindelfingen!'), + new UrlValue('http://example.com/sindelfingen', 'Sindelfingen!'), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + return [ + [ + new UrlValue(), + null, + ], + [ + new UrlValue('http://example.com/sindelfingen'), + [ + 'link' => 'http://example.com/sindelfingen', + 'text' => '', + ], + ], + [ + new UrlValue('http://example.com/sindelfingen', 'Sindelfingen!'), + [ + 'link' => 'http://example.com/sindelfingen', + 'text' => 'Sindelfingen!', + ], + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + null, + new UrlValue(), + ], + [ + [ + 'link' => 'http://example.com/sindelfingen', + 'text' => null, + ], + new UrlValue('http://example.com/sindelfingen'), + ], + [ + [ + 'link' => 'http://example.com/sindelfingen', + 'text' => 'Sindelfingen!', + ], + new UrlValue('http://example.com/sindelfingen', 'Sindelfingen!'), + ], + ]; + } + + protected function provideFieldTypeIdentifier() + { + return 'ezurl'; + } + + public function provideDataForGetName(): array + { + return [ + [$this->getEmptyValueExpectation(), '', [], 'en_GB'], + [new UrlValue('', 'Url text'), 'Url text', [], 'en_GB'], + ]; + } +} + +class_alias(UrlTest::class, 'eZ\Publish\Core\FieldType\Tests\UrlTest'); diff --git a/tests/lib/FieldType/UserTest.php b/tests/lib/FieldType/UserTest.php new file mode 100644 index 0000000000..7381fde02e --- /dev/null +++ b/tests/lib/FieldType/UserTest.php @@ -0,0 +1,851 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\FieldType; + +use DateTimeImmutable; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\User; +use Ibexa\Contracts\Core\Repository\PasswordHashService; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\FieldType\User\Type; +use Ibexa\Core\FieldType\User\Type as UserType; +use Ibexa\Core\FieldType\User\Value as UserValue; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\Persistence\Cache\UserHandler; +use Ibexa\Core\Repository\User\PasswordValidatorInterface; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinition as CoreFieldDefinition; +use Ibexa\Core\Repository\Values\User\User as RepositoryUser; +use Ibexa\Tests\Core\FieldType\DataProvider\UserValidatorConfigurationSchemaProvider; +use PHPUnit\Framework\MockObject\Builder\InvocationMocker; + +/** + * @group fieldType + * @group ezurl + */ +class UserTest extends FieldTypeTest +{ + private const UNSUPPORTED_HASH_TYPE = 0xDEADBEEF; + + /** + * Returns the field type under test. + * + * This method is used by all test cases to retrieve the field type under + * test. Just create the FieldType instance using mocks from the provided + * get*Mock() methods and/or custom get*Mock() implementations. You MUST + * NOT take care for test case wide caching of the field type, just return + * a new instance from this method! + * + * @return \Ibexa\Core\FieldType\User\Type + */ + protected function createFieldTypeUnderTest(): UserType + { + $fieldType = new UserType( + $this->createMock(UserHandler::class), + $this->createMock(PasswordHashService::class), + $this->createMock(PasswordValidatorInterface::class) + ); + $fieldType->setTransformationProcessor($this->getTransformationProcessorMock()); + + return $fieldType; + } + + /** + * Returns the validator configuration schema expected from the field type. + * + * @return array + */ + protected function getValidatorConfigurationSchemaExpectation() + { + return (new UserValidatorConfigurationSchemaProvider()) + ->getExpectedValidatorConfigurationSchema(); + } + + /** + * Returns the settings schema expected from the field type. + * + * @return array + */ + protected function getSettingsSchemaExpectation() + { + return [ + UserType::PASSWORD_TTL_SETTING => [ + 'type' => 'int', + 'default' => null, + ], + UserType::PASSWORD_TTL_WARNING_SETTING => [ + 'type' => 'int', + 'default' => null, + ], + UserType::REQUIRE_UNIQUE_EMAIL => [ + 'type' => 'bool', + 'default' => true, + ], + UserType::USERNAME_PATTERN => [ + 'type' => 'string', + 'default' => '^[^@]+$', + ], + ]; + } + + /** + * Returns the empty value expected from the field type. + */ + protected function getEmptyValueExpectation() + { + return new UserValue(); + } + + public function provideInvalidInputForAcceptValue() + { + return [ + [ + 23, + InvalidArgumentException::class, + ], + ]; + } + + /** + * Data provider for valid input to acceptValue(). + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to acceptValue(), 2. The expected return value from acceptValue(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * __FILE__, + * new BinaryFileValue( array( + * 'path' => __FILE__, + * 'fileName' => basename( __FILE__ ), + * 'fileSize' => filesize( __FILE__ ), + * 'downloadCount' => 0, + * 'mimeType' => 'text/plain', + * ) ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidInputForAcceptValue() + { + return [ + [ + null, + new UserValue(), + ], + [ + [], + new UserValue([]), + ], + [ + new UserValue(['login' => 'sindelfingen']), + new UserValue(['login' => 'sindelfingen']), + ], + [ + $userData = [ + 'hasStoredLogin' => true, + 'contentId' => 23, + 'login' => 'sindelfingen', + 'email' => 'sindelfingen@example.com', + 'passwordHash' => '1234567890abcdef', + 'passwordHashType' => 'md5', + 'enabled' => true, + 'maxLogin' => 1000, + ], + new UserValue($userData), + ], + [ + new UserValue( + $userData = [ + 'hasStoredLogin' => true, + 'contentId' => 23, + 'login' => 'sindelfingen', + 'email' => 'sindelfingen@example.com', + 'passwordHash' => '1234567890abcdef', + 'passwordHashType' => 'md5', + 'enabled' => true, + 'maxLogin' => 1000, + ] + ), + new UserValue($userData), + ], + ]; + } + + /** + * Provide input for the toHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to toHash(), 2. The expected return value from toHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * new BinaryFileValue( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForToHash() + { + $passwordUpdatedAt = new DateTimeImmutable(); + + return [ + [ + new UserValue(), + null, + ], + [ + new UserValue( + $userData = [ + 'hasStoredLogin' => true, + 'contentId' => 23, + 'login' => 'sindelfingen', + 'email' => 'sindelfingen@example.com', + 'passwordHash' => '1234567890abcdef', + 'passwordHashType' => 'md5', + 'passwordUpdatedAt' => $passwordUpdatedAt, + 'enabled' => true, + 'maxLogin' => 1000, + 'plainPassword' => null, + ] + ), + [ + 'passwordUpdatedAt' => $passwordUpdatedAt->getTimestamp(), + ] + $userData, + ], + ]; + } + + /** + * Provide input to fromHash() method. + * + * Returns an array of data provider sets with 2 arguments: 1. The valid + * input to fromHash(), 2. The expected return value from fromHash(). + * For example: + * + * <code> + * return array( + * array( + * null, + * null + * ), + * array( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ), + * new BinaryFileValue( + * array( + * 'path' => 'some/file/here', + * 'fileName' => 'sindelfingen.jpg', + * 'fileSize' => 2342, + * 'downloadCount' => 0, + * 'mimeType' => 'image/jpeg', + * ) + * ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInputForFromHash() + { + return [ + [ + null, + new UserValue(), + ], + [ + $userData = [ + 'hasStoredLogin' => true, + 'contentId' => 23, + 'login' => 'sindelfingen', + 'email' => 'sindelfingen@example.com', + 'passwordHash' => '1234567890abcdef', + 'passwordHashType' => 'md5', + 'passwordUpdatedAt' => 1567071092, + 'enabled' => true, + 'maxLogin' => 1000, + ], + new UserValue([ + 'passwordUpdatedAt' => new DateTimeImmutable('@1567071092'), + ] + $userData), + ], + ]; + } + + /** + * Returns empty data set. Validation tests were moved to testValidate method. + * + * @return array + */ + public function provideValidDataForValidate(): array + { + return []; + } + + /** + * Returns empty data set. Validation tests were moved to testValidate method. + * + * @see testValidate + * @see providerForTestValidate + * + * @return array + */ + public function provideInvalidDataForValidate(): array + { + return []; + } + + /** + * @covers \Ibexa\Core\FieldType\User\Type::validate + * + * @dataProvider providerForTestValidate + * + * @param \Ibexa\Core\FieldType\User\Value $userValue + * @param array $expectedValidationErrors + * @param callable|null $loadByLoginBehaviorCallback + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testValidate( + UserValue $userValue, + array $expectedValidationErrors, + ?callable $loadByLoginBehaviorCallback + ): void { + $userHandlerMock = $this->createMock(UserHandler::class); + + if (null !== $loadByLoginBehaviorCallback) { + $loadByLoginBehaviorCallback( + $userHandlerMock + ->expects($this->once()) + ->method('loadByLogin') + ->with($userValue->login) + ); + } + + $userType = new UserType( + $userHandlerMock, + $this->createMock(PasswordHashService::class), + $this->createMock(PasswordValidatorInterface::class) + ); + + $fieldSettings = [ + Type::USERNAME_PATTERN => '.*', + Type::REQUIRE_UNIQUE_EMAIL => false, + ]; + + $fieldDefinitionMock = $this->createMock(FieldDefinition::class); + $fieldDefinitionMock->method('__get')->with('fieldSettings')->willReturn($fieldSettings); + $fieldDefinitionMock->method('getFieldSettings')->willReturn($fieldSettings); + + $validationErrors = $userType->validate($fieldDefinitionMock, $userValue); + + self::assertEquals($expectedValidationErrors, $validationErrors); + } + + public function testInvalidLoginFormat(): void + { + $validateUserValue = new UserValue([ + 'hasStoredLogin' => false, + 'contentId' => 46, + 'login' => 'validate@user', + 'email' => 'example@test.ibexa.co', + 'passwordHash' => '1234567890abcdef', + 'passwordHashType' => 'md5', + 'enabled' => true, + 'maxLogin' => 1000, + 'plainPassword' => 'testPassword', + ]); + + $userHandlerMock = $this->createMock(UserHandler::class); + + $userHandlerMock + ->expects($this->once()) + ->method('loadByLogin') + ->with($validateUserValue->login) + ->willThrowException(new NotFoundException('', '')); + + $userType = new UserType( + $userHandlerMock, + $this->createMock(PasswordHashService::class), + $this->createMock(PasswordValidatorInterface::class) + ); + + $fieldSettings = [ + UserType::REQUIRE_UNIQUE_EMAIL => false, + UserType::USERNAME_PATTERN => '^[^@]+$', + ]; + + $fieldDefinition = new CoreFieldDefinition(['fieldSettings' => $fieldSettings]); + + $validationErrors = $userType->validate($fieldDefinition, $validateUserValue); + + self::assertEquals([ + new ValidationError( + 'Invalid login format', + null, + [], + 'username' + ), + ], $validationErrors); + } + + public function testValidLoginFormat(): void + { + $validateUserValue = new UserValue([ + 'hasStoredLogin' => false, + 'contentId' => 46, + 'login' => 'validate_user', + 'email' => 'example@test.ibexa.co', + 'passwordHash' => '1234567890abcdef', + 'passwordHashType' => 'md5', + 'enabled' => true, + 'maxLogin' => 1000, + 'plainPassword' => 'testPassword', + ]); + + $userHandlerMock = $this->createMock(UserHandler::class); + + $userHandlerMock + ->expects($this->once()) + ->method('loadByLogin') + ->with($validateUserValue->login) + ->willThrowException(new NotFoundException('', '')); + + $userType = new UserType( + $userHandlerMock, + $this->createMock(PasswordHashService::class), + $this->createMock(PasswordValidatorInterface::class) + ); + + $fieldSettings = [ + UserType::REQUIRE_UNIQUE_EMAIL => false, + UserType::USERNAME_PATTERN => '^[^@]+$', + ]; + + $fieldDefinition = new CoreFieldDefinition(['fieldSettings' => $fieldSettings]); + + $validationErrors = $userType->validate($fieldDefinition, $validateUserValue); + + self::assertEquals([], $validationErrors); + } + + public function testEmailAlreadyTaken(): void + { + $existingUser = new User([ + 'id' => 23, + 'login' => 'existing_user', + 'email' => 'test@test.ibexa.co', + ]); + + $validateUserValue = new UserValue([ + 'hasStoredLogin' => false, + 'contentId' => 46, + 'login' => 'validate_user', + 'email' => 'test@test.ibexa.co', + 'passwordHash' => '1234567890abcdef', + 'passwordHashType' => 'md5', + 'enabled' => true, + 'maxLogin' => 1000, + 'plainPassword' => 'testPassword', + ]); + + $userHandlerMock = $this->createMock(UserHandler::class); + + $userHandlerMock + ->expects($this->once()) + ->method('loadByLogin') + ->with($validateUserValue->login) + ->willThrowException(new NotFoundException('', '')); + + $userHandlerMock + ->expects($this->once()) + ->method('loadByEmail') + ->with($validateUserValue->email) + ->willReturn($existingUser); + + $userType = new UserType( + $userHandlerMock, + $this->createMock(PasswordHashService::class), + $this->createMock(PasswordValidatorInterface::class) + ); + + $fieldSettings = [ + UserType::REQUIRE_UNIQUE_EMAIL => true, + UserType::USERNAME_PATTERN => '^[^@]+$', + ]; + + $fieldDefinition = new CoreFieldDefinition(['fieldSettings' => $fieldSettings]); + + $validationErrors = $userType->validate($fieldDefinition, $validateUserValue); + + self::assertEquals([ + new ValidationError( + "Email '%email%' is used by another user. You must enter a unique email.", + null, + [ + '%email%' => $validateUserValue->email, + ], + 'email' + ), + ], $validationErrors); + } + + /** + * @covers \Ibexa\Core\FieldType\User\Type::toPersistenceValue + * + * @dataProvider providerForTestCreatePersistenceValue + */ + public function testCreatePersistenceValue(array $userValueDate, array $expectedFieldValueExternalData): void + { + $passwordHashServiceMock = $this->createMock(PasswordHashService::class); + $passwordHashServiceMock->method('getDefaultHashType')->willReturn(RepositoryUser::DEFAULT_PASSWORD_HASH); + $userType = new UserType( + $this->createMock(UserHandler::class), + $passwordHashServiceMock, + $this->createMock(PasswordValidatorInterface::class) + ); + + $value = new UserValue($userValueDate); + $fieldValue = $userType->toPersistenceValue($value); + + $expected = new FieldValue( + [ + 'data' => null, + 'externalData' => $expectedFieldValueExternalData, + 'sortKey' => null, + ] + ); + self::assertEquals($expected, $fieldValue); + } + + public function providerForTestCreatePersistenceValue(): iterable + { + $passwordUpdatedAt = new DateTimeImmutable(); + $userData = [ + 'hasStoredLogin' => false, + 'contentId' => 46, + 'login' => 'validate_user', + 'email' => 'test@test.ibexa.co', + 'passwordHash' => '1234567890abcdef', + 'enabled' => true, + 'maxLogin' => 1000, + 'plainPassword' => '', + 'passwordUpdatedAt' => $passwordUpdatedAt, + ]; + + yield 'when password hash type is given' => [ + $userValueData = [ + 'passwordHashType' => RepositoryUser::PASSWORD_HASH_PHP_DEFAULT, + ] + $userData, + $expectedFieldValueExternalData = [ + 'passwordHashType' => RepositoryUser::PASSWORD_HASH_PHP_DEFAULT, + 'passwordUpdatedAt' => $passwordUpdatedAt->getTimestamp(), + ] + $userData, + ]; + yield 'when password hash type is null' => [ + $userValueData = [ + 'passwordHashType' => null, + ] + $userData, + $expectedFieldValueExternalData = [ + 'passwordHashType' => RepositoryUser::DEFAULT_PASSWORD_HASH, + 'passwordUpdatedAt' => $passwordUpdatedAt->getTimestamp(), + ] + $userData, + ]; + yield 'when password hash type is unsupported' => [ + $userValueData = [ + 'passwordHashType' => self::UNSUPPORTED_HASH_TYPE, + ] + $userData, + $expectedFieldValueExternalData = [ + 'passwordHashType' => RepositoryUser::DEFAULT_PASSWORD_HASH, + 'passwordUpdatedAt' => $passwordUpdatedAt->getTimestamp(), + ] + $userData, + ]; + } + + public function testEmailFreeToUse(): void + { + $validateUserValue = new UserValue([ + 'hasStoredLogin' => false, + 'contentId' => 46, + 'login' => 'validate_user', + 'email' => 'test@test.ibexa.co', + 'passwordHash' => '1234567890abcdef', + 'passwordHashType' => 'md5', + 'enabled' => true, + 'maxLogin' => 1000, + 'plainPassword' => 'testPassword', + ]); + + $userHandlerMock = $this->createMock(UserHandler::class); + + $userHandlerMock + ->expects($this->once()) + ->method('loadByLogin') + ->with($validateUserValue->login) + ->willThrowException(new NotFoundException('', '')); + + $userHandlerMock + ->expects($this->once()) + ->method('loadByEmail') + ->with($validateUserValue->email) + ->willThrowException(new NotFoundException('', '')); + + $userType = new UserType( + $userHandlerMock, + $this->createMock(PasswordHashService::class), + $this->createMock(PasswordValidatorInterface::class) + ); + + $fieldSettings = [ + UserType::REQUIRE_UNIQUE_EMAIL => true, + UserType::USERNAME_PATTERN => '^[^@]+$', + ]; + + $fieldDefinition = new CoreFieldDefinition(['fieldSettings' => $fieldSettings]); + + $validationErrors = $userType->validate($fieldDefinition, $validateUserValue); + + self::assertEquals([], $validationErrors); + } + + /** + * Data provider for testValidate test. + * + * @see testValidate + * + * @return array data sets for testValidate method (<code>$userValue, $expectedValidationErrors, $loadByLoginBehaviorCallback</code>) + */ + public function providerForTestValidate(): array + { + return [ + [ + new UserValue( + [ + 'hasStoredLogin' => false, + 'contentId' => 23, + 'login' => 'user', + 'email' => 'invalid', + 'passwordHash' => '1234567890abcdef', + 'passwordHashType' => 'md5', + 'enabled' => true, + 'maxLogin' => 1000, + 'plainPassword' => 'testPassword', + ] + ), + [ + new ValidationError( + "The given e-mail '%email%' is invalid", + null, + [ + '%email%' => 'invalid', + ], + 'email' + ), + ], + static function (InvocationMocker $loadByLoginInvocationMocker) { + $loadByLoginInvocationMocker->willThrowException( + new NotFoundException('user', 'user') + ); + }, + ], + [ + new UserValue([ + 'hasStoredLogin' => false, + 'contentId' => 23, + 'login' => 'sindelfingen', + 'email' => 'sindelfingen@example.com', + 'passwordHash' => '1234567890abcdef', + 'passwordHashType' => 'md5', + 'enabled' => true, + 'maxLogin' => 1000, + 'plainPassword' => 'testPassword', + ]), + [ + new ValidationError( + "The user login '%login%' is used by another user. You must enter a unique login.", + null, + [ + '%login%' => 'sindelfingen', + ], + 'username' + ), + ], + function (InvocationMocker $loadByLoginInvocationMocker) { + $loadByLoginInvocationMocker->willReturn( + $this->createMock(UserValue::class) + ); + }, + ], + [ + new UserValue([ + 'hasStoredLogin' => true, + 'contentId' => 23, + 'login' => 'sindelfingen', + 'email' => 'sindelfingen@example.com', + 'passwordHash' => '1234567890abcdef', + 'passwordHashType' => 'md5', + 'enabled' => true, + 'maxLogin' => 1000, + ]), + [], + null, + ], + ]; + } + + /** + * Provide data sets with field settings which are considered valid by the + * {@link validateFieldSettings()} method. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * array(), + * ), + * array( + * array( 'rows' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideValidFieldSettings(): array + { + return [ + [ + [], + ], + [ + [ + UserType::PASSWORD_TTL_SETTING => 30, + ], + ], + [ + [ + UserType::PASSWORD_TTL_SETTING => 30, + UserType::PASSWORD_TTL_WARNING_SETTING => null, + ], + ], + [ + [ + UserType::PASSWORD_TTL_SETTING => 30, + UserType::PASSWORD_TTL_WARNING_SETTING => 14, + UserType::REQUIRE_UNIQUE_EMAIL => true, + UserType::USERNAME_PATTERN => '^[^!]+$', + ], + ], + ]; + } + + /** + * Provide data sets with field settings which are considered invalid by the + * {@link validateFieldSettings()} method. The method must return a + * non-empty array of validation error when receiving such field settings. + * + * Returns an array of data provider sets with a single argument: A valid + * set of field settings. + * For example: + * + * <code> + * return array( + * array( + * true, + * ), + * array( + * array( 'nonExistentKey' => 2 ) + * ), + * // ... + * ); + * </code> + * + * @return array + */ + public function provideInValidFieldSettings(): array + { + return [ + [ + [ + UserType::PASSWORD_TTL_WARNING_SETTING => 30, + ], + ], + [ + [ + UserType::PASSWORD_TTL_SETTING => null, + UserType::PASSWORD_TTL_WARNING_SETTING => 60, + ], + ], + [ + [ + UserType::PASSWORD_TTL_SETTING => 30, + UserType::PASSWORD_TTL_WARNING_SETTING => 60, + ], + ], + ]; + } + + protected function provideFieldTypeIdentifier() + { + return 'ezuser'; + } + + public function provideDataForGetName(): array + { + return [ + [$this->getEmptyValueExpectation(), '', [], 'en_GB'], + [new UserValue(['login' => 'johndoe']), 'johndoe', [], 'en_GB'], + ]; + } +} + +class_alias(UserTest::class, 'eZ\Publish\Core\FieldType\Tests\UserTest'); diff --git a/eZ/Publish/Core/Helper/Tests/ContentInfoLocationLoader/SudoMainLocationLoaderTest.php b/tests/lib/Helper/ContentInfoLocationLoader/SudoMainLocationLoaderTest.php similarity index 76% rename from eZ/Publish/Core/Helper/Tests/ContentInfoLocationLoader/SudoMainLocationLoaderTest.php rename to tests/lib/Helper/ContentInfoLocationLoader/SudoMainLocationLoaderTest.php index f80dfff417..8600adae39 100644 --- a/eZ/Publish/Core/Helper/Tests/ContentInfoLocationLoader/SudoMainLocationLoaderTest.php +++ b/tests/lib/Helper/ContentInfoLocationLoader/SudoMainLocationLoaderTest.php @@ -4,24 +4,24 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Helper\Tests\ContentInfoLocationLoader; - -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Helper\ContentInfoLocationLoader\SudoMainLocationLoader; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\Repository\Mapper\RoleDomainMapper; -use eZ\Publish\Core\Repository\Permission\LimitationService; -use eZ\Publish\Core\Repository\Permission\PermissionResolver; -use eZ\Publish\Core\Repository\Repository; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\SPI\Persistence\User\Handler as SPIUserHandler; +namespace Ibexa\Tests\Core\Helper\ContentInfoLocationLoader; + +use Ibexa\Contracts\Core\Persistence\User\Handler as SPIUserHandler; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Helper\ContentInfoLocationLoader\SudoMainLocationLoader; +use Ibexa\Core\Repository\Mapper\RoleDomainMapper; +use Ibexa\Core\Repository\Permission\LimitationService; +use Ibexa\Core\Repository\Permission\PermissionResolver; +use Ibexa\Core\Repository\Repository; +use Ibexa\Core\Repository\Values\Content\Location; use PHPUnit\Framework\TestCase; class SudoMainLocationLoaderTest extends TestCase { - /** @var \eZ\Publish\Core\Helper\ContentInfoLocationLoader\SudoMainLocationLoader */ + /** @var \Ibexa\Core\Helper\ContentInfoLocationLoader\SudoMainLocationLoader */ private $loader; protected function setUp(): void @@ -31,7 +31,7 @@ protected function setUp(): void public function testLoadLocationNoMainLocation() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $contentInfo = new ContentInfo(); @@ -68,7 +68,7 @@ public function testLoadLocation() public function testLoadLocationError() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $contentInfo = new ContentInfo(['mainLocationId' => 42]); $location = new Location(); @@ -95,7 +95,7 @@ public function testLoadLocationError() } /** - * @return \eZ\Publish\Core\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject */ private function getRepositoryMock() { @@ -120,7 +120,7 @@ private function getRepositoryMock() } /** - * @return \eZ\Publish\API\Repository\LocationService|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\Repository\LocationService|\PHPUnit\Framework\MockObject\MockObject */ private function getLocationServiceMock() { @@ -136,7 +136,7 @@ private function getLocationServiceMock() } /** - * @return \eZ\Publish\Core\Repository\Permission\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Repository\Permission\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ private function getPermissionResolverMock() { @@ -168,3 +168,5 @@ private function getPermissionResolverMock() ->getMock(); } } + +class_alias(SudoMainLocationLoaderTest::class, 'eZ\Publish\Core\Helper\Tests\ContentInfoLocationLoader\SudoMainLocationLoaderTest'); diff --git a/eZ/Publish/Core/Helper/Tests/ContentPreviewHelperTest.php b/tests/lib/Helper/ContentPreviewHelperTest.php similarity index 80% rename from eZ/Publish/Core/Helper/Tests/ContentPreviewHelperTest.php rename to tests/lib/Helper/ContentPreviewHelperTest.php index 0d84f0c064..c177ece44e 100644 --- a/eZ/Publish/Core/Helper/Tests/ContentPreviewHelperTest.php +++ b/tests/lib/Helper/ContentPreviewHelperTest.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Helper\Tests; - -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\Helper\ContentPreviewHelper; -use eZ\Publish\Core\MVC\Symfony\Event\ScopeChangeEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface; +namespace Ibexa\Tests\Core\Helper; + +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\Location as APILocation; +use Ibexa\Core\Helper\ContentPreviewHelper; +use Ibexa\Core\MVC\Symfony\Event\ScopeChangeEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -24,7 +24,7 @@ class ContentPreviewHelperTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $siteAccessRouter; - /** @var \eZ\Publish\Core\Helper\ContentPreviewHelper */ + /** @var \Ibexa\Core\Helper\ContentPreviewHelper */ private $previewHelper; protected function setUp(): void @@ -78,11 +78,16 @@ public function testRestoreConfigScope() public function testPreviewActive() { + $originalSiteAccess = new SiteAccess('foo', 'bar'); + $this->previewHelper->setSiteAccess($originalSiteAccess); + $this->assertFalse($this->previewHelper->isPreviewActive()); $this->previewHelper->setPreviewActive(true); $this->assertTrue($this->previewHelper->isPreviewActive()); $this->previewHelper->setPreviewActive(false); $this->assertFalse($this->previewHelper->isPreviewActive()); + + self::assertNotSame($originalSiteAccess, $this->previewHelper->getOriginalSiteAccess()); } public function testPreviewedContent() @@ -101,3 +106,5 @@ public function testPreviewedLocation() $this->assertSame($location, $this->previewHelper->getPreviewedLocation()); } } + +class_alias(ContentPreviewHelperTest::class, 'eZ\Publish\Core\Helper\Tests\ContentPreviewHelperTest'); diff --git a/eZ/Publish/Core/Helper/Tests/FieldHelperTest.php b/tests/lib/Helper/FieldHelperTest.php similarity index 78% rename from eZ/Publish/Core/Helper/Tests/FieldHelperTest.php rename to tests/lib/Helper/FieldHelperTest.php index c4e64facf4..6fa0830a6d 100644 --- a/eZ/Publish/Core/Helper/Tests/FieldHelperTest.php +++ b/tests/lib/Helper/FieldHelperTest.php @@ -4,34 +4,34 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Helper\Tests; - -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\FieldTypeService; -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\FieldType\TextLine\Type as TextLineType; -use eZ\Publish\Core\FieldType\TextLine\Value; -use eZ\Publish\Core\Helper\FieldHelper; -use eZ\Publish\Core\Helper\TranslationHelper; -use eZ\Publish\Core\Repository\Values\ContentType\FieldType; +namespace Ibexa\Tests\Core\Helper; + +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\FieldTypeService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\FieldType\TextLine\Type as TextLineType; +use Ibexa\Core\FieldType\TextLine\Value; +use Ibexa\Core\Helper\FieldHelper; +use Ibexa\Core\Helper\TranslationHelper; +use Ibexa\Core\Repository\Values\ContentType\FieldType; use PHPUnit\Framework\TestCase; class FieldHelperTest extends TestCase { - /** @var \eZ\Publish\Core\Helper\FieldHelper */ + /** @var \Ibexa\Core\Helper\FieldHelper */ private $fieldHelper; - /** @var \eZ\Publish\API\Repository\FieldTypeService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\FieldTypeService|\PHPUnit\Framework\MockObject\MockObject */ private $fieldTypeServiceMock; - /** @var \eZ\Publish\API\Repository\ContentTypeService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService|\PHPUnit\Framework\MockObject\MockObject */ private $contentTypeServiceMock; - /** @var \eZ\Publish\Core\Helper\TranslationHelper|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\Helper\TranslationHelper|\PHPUnit\Framework\MockObject\MockObject */ private $translationHelper; protected function setUp(): void @@ -135,3 +135,5 @@ public function testIsFieldNotEmpty() $this->assertFalse($this->fieldHelper->isFieldEmpty($content, $fieldDefIdentifier)); } } + +class_alias(FieldHelperTest::class, 'eZ\Publish\Core\Helper\Tests\FieldHelperTest'); diff --git a/eZ/Publish/Core/Helper/Tests/FieldsGroups/ArrayTranslatorFieldsGroupsListTest.php b/tests/lib/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsListTest.php similarity index 80% rename from eZ/Publish/Core/Helper/Tests/FieldsGroups/ArrayTranslatorFieldsGroupsListTest.php rename to tests/lib/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsListTest.php index 3fe4fd2988..18fa357960 100644 --- a/eZ/Publish/Core/Helper/Tests/FieldsGroups/ArrayTranslatorFieldsGroupsListTest.php +++ b/tests/lib/Helper/FieldsGroups/ArrayTranslatorFieldsGroupsListTest.php @@ -4,14 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Helper\Tests\FieldsGroups; +namespace Ibexa\Tests\Core\Helper\FieldsGroups; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Helper\FieldsGroups\ArrayTranslatorFieldsGroupsList; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Helper\FieldsGroups\ArrayTranslatorFieldsGroupsList; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Symfony\Contracts\Translation\TranslatorInterface; +/** + * @covers \Ibexa\Core\Helper\FieldsGroups\ArrayTranslatorFieldsGroupsList + */ class ArrayTranslatorFieldsGroupsListTest extends TestCase { private const FIRST_GROUP_ID = 'slayer'; @@ -25,9 +28,6 @@ class ArrayTranslatorFieldsGroupsListTest extends TestCase private $translatorMock; - /** - * @covers \eZ\Publish\Core\Helper\FieldsGroups\ArrayTranslatorFieldsGroupsList::getGroups - */ public function testTranslatesGroups(): void { $this->applyTranslationsForTranslationsMock(); @@ -43,9 +43,6 @@ public function testTranslatesGroups(): void ); } - /** - * @covers \eZ\Publish\Core\Helper\FieldsGroups\ArrayTranslatorFieldsGroupsList::getDefaultGroup - */ public function testReturnsDefault(): void { $list = $this->getArrayTranslatorFieldsGroupsList([]); @@ -53,9 +50,6 @@ public function testReturnsDefault(): void self::assertEquals(self::DEFAULT_GROUP_ID, $list->getDefaultGroup()); } - /** - * @covers \eZ\Publish\Core\Helper\FieldsGroups\ArrayTranslatorFieldsGroupsList::getFieldGroup - */ public function testGetFieldGroupWhenFieldDefinitionHasGroup(): void { $fieldDefinitionMock = $this->getFieldDefinitionMock( @@ -70,9 +64,6 @@ public function testGetFieldGroupWhenFieldDefinitionHasGroup(): void ); } - /** - * @covers \eZ\Publish\Core\Helper\FieldsGroups\ArrayTranslatorFieldsGroupsList::getFieldGroup - */ public function testGetFieldGroupWhenFieldDefinitionMissingGroup(): void { $fieldDefinitionMock = $this->getFieldDefinitionMock(); @@ -133,8 +124,8 @@ private function applyTranslationsForTranslationsMock(): void ->method('trans') ->will( $this->returnValueMap([ - [self::FIRST_GROUP_ID, [], 'ezplatform_fields_groups', null, self::FIRST_GROUP_NAME], - [self::SECOND_GROUP_ID, [], 'ezplatform_fields_groups', null, self::SECOND_GROUP_NAME], + [self::FIRST_GROUP_ID, [], 'ibexa_fields_groups', null, self::FIRST_GROUP_NAME], + [self::SECOND_GROUP_ID, [], 'ibexa_fields_groups', null, self::SECOND_GROUP_NAME], ]) ); } @@ -142,7 +133,7 @@ private function applyTranslationsForTranslationsMock(): void /** * @param array $constructorArgs * - * @return \eZ\Publish\API\Repository\Values\ContentType\FieldDefinition|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition|\PHPUnit\Framework\MockObject\MockObject */ private function getFieldDefinitionMock(array $constructorArgs = []): MockObject { @@ -152,3 +143,5 @@ private function getFieldDefinitionMock(array $constructorArgs = []): MockObject ->getMockForAbstractClass(); } } + +class_alias(ArrayTranslatorFieldsGroupsListTest::class, 'eZ\Publish\Core\Helper\Tests\FieldsGroups\ArrayTranslatorFieldsGroupsListTest'); diff --git a/eZ/Publish/Core/Helper/Tests/FieldsGroups/RepositoryConfigFieldsGroupsListFactoryTest.php b/tests/lib/Helper/FieldsGroups/RepositoryConfigFieldsGroupsListFactoryTest.php similarity index 79% rename from eZ/Publish/Core/Helper/Tests/FieldsGroups/RepositoryConfigFieldsGroupsListFactoryTest.php rename to tests/lib/Helper/FieldsGroups/RepositoryConfigFieldsGroupsListFactoryTest.php index 9ff5292ffe..89b6aa7b4c 100644 --- a/eZ/Publish/Core/Helper/Tests/FieldsGroups/RepositoryConfigFieldsGroupsListFactoryTest.php +++ b/tests/lib/Helper/FieldsGroups/RepositoryConfigFieldsGroupsListFactoryTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Helper\Tests\FieldsGroups; +namespace Ibexa\Tests\Core\Helper\FieldsGroups; -use eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider; -use eZ\Publish\Core\Helper\FieldsGroups\RepositoryConfigFieldsGroupsListFactory; +use Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider; +use Ibexa\Core\Helper\FieldsGroups\RepositoryConfigFieldsGroupsListFactory; use PHPUnit\Framework\TestCase; use Symfony\Contracts\Translation\TranslatorInterface; @@ -37,7 +37,7 @@ public function testBuild() } /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Bundle\EzPublishCoreBundle\ApiLoader\RepositoryConfigurationProvider + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Bundle\Core\ApiLoader\RepositoryConfigurationProvider */ protected function getRepositoryConfigMock() { @@ -60,3 +60,5 @@ protected function getTranslatorMock() return $this->translatorMock; } } + +class_alias(RepositoryConfigFieldsGroupsListFactoryTest::class, 'eZ\Publish\Core\Helper\Tests\FieldsGroups\RepositoryConfigFieldsGroupsListFactoryTest'); diff --git a/eZ/Publish/Core/Helper/Tests/PreviewLocationProviderTest.php b/tests/lib/Helper/PreviewLocationProviderTest.php similarity index 79% rename from eZ/Publish/Core/Helper/Tests/PreviewLocationProviderTest.php rename to tests/lib/Helper/PreviewLocationProviderTest.php index 4273f3f62d..fb8f05d18e 100644 --- a/eZ/Publish/Core/Helper/Tests/PreviewLocationProviderTest.php +++ b/tests/lib/Helper/PreviewLocationProviderTest.php @@ -4,31 +4,31 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Helper\Tests; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo as APIContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\Helper\PreviewLocationProvider; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as SPILocationHandler; +namespace Ibexa\Tests\Core\Helper; + +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as SPILocationHandler; +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo as APIContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location as APILocation; +use Ibexa\Core\Helper\PreviewLocationProvider; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Repository\Values\Content\VersionInfo; use PHPUnit\Framework\TestCase; class PreviewLocationProviderTest extends TestCase { - /** @var \eZ\Publish\API\Repository\LocationService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\LocationService|\PHPUnit\Framework\MockObject\MockObject */ private $contentService; - /** @var \eZ\Publish\API\Repository\LocationService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\LocationService|\PHPUnit\Framework\MockObject\MockObject */ private $locationService; - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Location\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $locationHandler; - /** @var \eZ\Publish\Core\Helper\PreviewLocationProvider */ + /** @var \Ibexa\Core\Helper\PreviewLocationProvider */ private $provider; protected function setUp(): void @@ -143,3 +143,5 @@ private function getContentMock(int $contentId, ?int $mainLocationId = null, boo return $content; } } + +class_alias(PreviewLocationProviderTest::class, 'eZ\Publish\Core\Helper\Tests\PreviewLocationProviderTest'); diff --git a/eZ/Publish/Core/Helper/Tests/TranslationHelperTest.php b/tests/lib/Helper/TranslationHelperTest.php similarity index 92% rename from eZ/Publish/Core/Helper/Tests/TranslationHelperTest.php rename to tests/lib/Helper/TranslationHelperTest.php index cf804dde9f..812f286504 100644 --- a/eZ/Publish/Core/Helper/Tests/TranslationHelperTest.php +++ b/tests/lib/Helper/TranslationHelperTest.php @@ -4,35 +4,35 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Helper\Tests; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\Helper\TranslationHelper; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; +namespace Ibexa\Tests\Core\Helper; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\Helper\TranslationHelper; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; class TranslationHelperTest extends TestCase { - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\ContentService */ + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Core\Repository\ContentService */ private $contentService; /** @var \PHPUnit\Framework\MockObject\MockObject */ private $logger; - /** @var \eZ\Publish\Core\Helper\TranslationHelper */ + /** @var \Ibexa\Core\Helper\TranslationHelper */ private $translationHelper; private $siteAccessByLanguages; - /** @var \eZ\Publish\API\Repository\Values\Content\Field[] */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Field[] */ private $translatedFields; /** @var string[] */ @@ -71,7 +71,7 @@ protected function setUp(): void } /** - * @return \eZ\Publish\Core\Repository\Values\Content\Content + * @return \Ibexa\Core\Repository\Values\Content\Content */ private function generateContent() { @@ -84,7 +84,7 @@ private function generateContent() } /** - * @return \eZ\Publish\API\Repository\Values\Content\VersionInfo + * @return \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo */ private function generateVersionInfo() { @@ -335,3 +335,5 @@ public function testGetAvailableLanguagesWithoutTranslationSiteAccesses() $this->assertSame($expectedLanguages, $this->translationHelper->getAvailableLanguages()); } } + +class_alias(TranslationHelperTest::class, 'eZ\Publish\Core\Helper\Tests\TranslationHelperTest'); diff --git a/eZ/Publish/Core/IO/Tests/ConfigScopeChangeAwareIOServiceTest.php b/tests/lib/IO/ConfigScopeChangeAwareIOServiceTest.php similarity index 92% rename from eZ/Publish/Core/IO/Tests/ConfigScopeChangeAwareIOServiceTest.php rename to tests/lib/IO/ConfigScopeChangeAwareIOServiceTest.php index 051cb68bd7..0426a3aa51 100644 --- a/eZ/Publish/Core/IO/Tests/ConfigScopeChangeAwareIOServiceTest.php +++ b/tests/lib/IO/ConfigScopeChangeAwareIOServiceTest.php @@ -6,14 +6,14 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\IO\Tests; - -use eZ\Publish\Core\IO\ConfigScopeChangeAwareIOService; -use eZ\Publish\Core\IO\IOServiceInterface; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\IO\Values\BinaryFileCreateStruct; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Event\ScopeChangeEvent; +namespace Ibexa\Tests\Core\IO; + +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\IO\ConfigScopeChangeAwareIOService; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\Values\BinaryFile; +use Ibexa\Core\IO\Values\BinaryFileCreateStruct; +use Ibexa\Core\MVC\Symfony\Event\ScopeChangeEvent; use PHPUnit\Framework\TestCase; final class ConfigScopeChangeAwareIOServiceTest extends TestCase @@ -21,13 +21,13 @@ final class ConfigScopeChangeAwareIOServiceTest extends TestCase protected const PREFIX = 'test-prefix'; protected const PREFIX_PARAMETER_NAME = 'param'; - /** @var \eZ\Publish\Core\IO\ConfigScopeChangeAwareIOService */ + /** @var \Ibexa\Core\IO\ConfigScopeChangeAwareIOService */ protected $ioService; - /** @var \eZ\Publish\Core\IO\ConfigScopeChangeAwareIOService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\IO\ConfigScopeChangeAwareIOService|\PHPUnit\Framework\MockObject\MockObject */ protected $innerIOService; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $configResolver; protected function setUp(): void @@ -318,3 +318,5 @@ public function testOnConfigScopeChange(): void $this->ioService->onConfigScopeChange($event); } } + +class_alias(ConfigScopeChangeAwareIOServiceTest::class, 'eZ\Publish\Core\IO\Tests\ConfigScopeChangeAwareIOServiceTest'); diff --git a/tests/lib/IO/FilePathNormalizer/FlysystemTest.php b/tests/lib/IO/FilePathNormalizer/FlysystemTest.php new file mode 100644 index 0000000000..35e933257c --- /dev/null +++ b/tests/lib/IO/FilePathNormalizer/FlysystemTest.php @@ -0,0 +1,115 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\IO\FilePathNormalizer; + +use Ibexa\Core\IO\FilePathNormalizer\Flysystem; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; +use League\Flysystem\WhitespacePathNormalizer; +use PHPUnit\Framework\TestCase; + +final class FlysystemTest extends TestCase +{ + /** @var \Ibexa\Core\IO\FilePathNormalizer\Flysystem */ + private $filePathNormalizer; + + /** @var \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter|\PHPUnit\Framework\MockObject\MockObject */ + private $slugConverter; + + public function setUp(): void + { + $this->slugConverter = $this->createMock(SlugConverter::class); + $this->filePathNormalizer = new Flysystem($this->slugConverter, new WhitespacePathNormalizer()); + } + + /** + * @dataProvider providerForTestNormalizePath + */ + public function testNormalizePath( + string $originalPath, + string $fileName, + string $sluggedFileName, + string $regex, + bool $doHash + ): void { + $this->slugConverter + ->expects(self::once()) + ->method('convert') + ->with($fileName) + ->willReturn($sluggedFileName); + + $normalizedPath = $this->filePathNormalizer->normalizePath($originalPath, $doHash); + + self::assertStringEndsWith($sluggedFileName, $normalizedPath); + self::assertRegExp($regex, $normalizedPath); + } + + public function providerForTestNormalizePath(): array + { + $defaultPattern = '/\/[0-9a-f]{12}-'; + + return [ + 'No special chars' => [ + '4/3/2/234/1/image.jpg', + 'image.jpg', + 'image.jpg', + $defaultPattern . 'image.jpg/', + true, + ], + 'Spaces in the filename' => [ + '4/3/2/234/1/image with spaces.jpg', + 'image with spaces.jpg', + 'image-with-spaces.jpg', + $defaultPattern . 'image-with-spaces.jpg/', + true, + ], + 'Encoded spaces in the name' => [ + '4/3/2/234/1/image%20+no+spaces.jpg', + 'image%20+no+spaces.jpg', + 'image-20-nospaces.jpg', + $defaultPattern . 'image-20-nospaces.jpg/', + true, + ], + 'Special chars in the name' => [ + '4/3/2/234/1/image%20+no+spaces?.jpg', + 'image%20+no+spaces?.jpg', + 'image-20-nospaces.jpg', + $defaultPattern . 'image-20-nospaces.jpg/', + true, + ], + 'Already hashed name' => [ + '4/3/2/234/1/14ff44718877-hashed.jpg', + '14ff44718877-hashed.jpg', + '14ff44718877-hashed.jpg', + '/^4\/3\/2\/234\/1\/14ff44718877-hashed.jpg$/', + true, + ], + 'No special chars and no hashing' => [ + '4/3/2/234/1/image.jpg', + 'image.jpg', + 'image.jpg', + '/\/image.jpg/', + false, + ], + 'Special chars and no hashing' => [ + '4/3/2/234/1/image%20+no+spaces.jpg', + 'image%20+no+spaces.jpg', + 'image-20-nospaces.jpg', + '/\/image-20-nospaces.jpg/', + false, + ], + 'Already hashed name and no hashing' => [ + '4/3/2/234/1/14ff44718877-hashed.jpg', + '14ff44718877-hashed.jpg', + '14ff44718877-hashed.jpg', + '/^4\/3\/2\/234\/1\/14ff44718877-hashed.jpg$/', + false, + ], + ]; + } +} diff --git a/tests/lib/IO/Flysystem/Adapter/DynamicPathFilesystemAdapterDecoratorTest.php b/tests/lib/IO/Flysystem/Adapter/DynamicPathFilesystemAdapterDecoratorTest.php new file mode 100644 index 0000000000..98e0aad30f --- /dev/null +++ b/tests/lib/IO/Flysystem/Adapter/DynamicPathFilesystemAdapterDecoratorTest.php @@ -0,0 +1,389 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\IO\Flysystem\Adapter; + +use Ibexa\Core\IO\Flysystem\Adapter\DynamicPathFilesystemAdapterDecorator; +use Ibexa\Core\IO\Flysystem\PathPrefixer\PathPrefixerInterface; +use League\Flysystem\Config; +use League\Flysystem\FileAttributes; +use League\Flysystem\FilesystemAdapter; +use League\Flysystem\Visibility; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\IO\Flysystem\Adapter\DynamicPathFilesystemAdapterDecorator + * + * Note: SiteAccess-aware dynamic settings resolving aspect has been tested via PathPrefixer + * and Visibility converter test cases: + * {@see \Ibexa\Tests\Core\IO\Flysystem\VisibilityConverter\BaseVisibilityConverterTest} + * {@see \Ibexa\Tests\Core\IO\Flysystem\PathPrefixer\DFSSiteAccessAwarePathPrefixerTest} + */ +final class DynamicPathFilesystemAdapterDecoratorTest extends TestCase +{ + private const FLYSYSTEM_TEST_DIR = __DIR__; + private const FILE_CONTENTS = 'FOO BAR'; + private const FOO_BAR_FILE = 'foo/bar.file'; + private const BAR_COPY_FILE = '/bar.copy'; + public const MY_DIR_NAME = '/my_dir'; + + private FilesystemAdapter $adapter; + + private Config $config; + + /** @var \League\Flysystem\FilesystemAdapter&\PHPUnit\Framework\MockObject\MockObject */ + private FilesystemAdapter $innerAdapterMock; + + protected function setUp(): void + { + $this->innerAdapterMock = $this->createMock(FilesystemAdapter::class); + $this->adapter = new DynamicPathFilesystemAdapterDecorator( + $this->innerAdapterMock, + $this->createPathPrefixerMock() + ); + + $this->config = new Config( + [ + Config::OPTION_VISIBILITY => Visibility::PUBLIC, + Config::OPTION_DIRECTORY_VISIBILITY => Visibility::PUBLIC, + ] + ); + } + + /** + * @throws \League\Flysystem\FilesystemException + */ + public function testWrite(): void + { + $this + ->innerAdapterMock + ->expects(self::once()) + ->method('write') + ->with( + $this->buildAbsolutePath(self::FOO_BAR_FILE), + self::FILE_CONTENTS, + $this->config + ); + + $this->adapter->write(self::FOO_BAR_FILE, self::FILE_CONTENTS, $this->config); + } + + /** + * @throws \League\Flysystem\FilesystemException + */ + public function testWriteStream(): void + { + $resource = tmpfile(); + + $this + ->innerAdapterMock + ->expects(self::once()) + ->method('writeStream') + ->with( + $this->buildAbsolutePath(self::FOO_BAR_FILE), + $resource, + $this->config + ); + + $this->adapter->writeStream( + self::FOO_BAR_FILE, + $resource, + $this->config + ); + + fclose($resource); + } + + /** + * @throws \League\Flysystem\FilesystemException + */ + public function testFileSize(): void + { + $fileSize = 123; + $fileAttributesMock = $this->createFileAttributesMock('fileSize', $fileSize); + + $this + ->innerAdapterMock + ->expects(self::once()) + ->method('fileSize') + ->with( + $this->buildAbsolutePath(self::FOO_BAR_FILE) + ) + ->willReturn($fileAttributesMock); + + self::assertSame( + $fileSize, + $this->adapter->fileSize(self::FOO_BAR_FILE)->fileSize() + ); + } + + /** + * @throws \League\Flysystem\FilesystemException + */ + public function testFileExists(): void + { + $this + ->innerAdapterMock + ->expects(self::once()) + ->method('fileExists') + ->with( + $this->buildAbsolutePath(self::FOO_BAR_FILE) + ) + ->willReturn(true); + + self::assertTrue($this->adapter->fileExists(self::FOO_BAR_FILE)); + } + + /** + * @throws \League\Flysystem\FilesystemException + */ + public function testLastModified(): void + { + $lastModified = 1673264474; + $fileAttributesMock = $this->createFileAttributesMock('lastModified', $lastModified); + + $this + ->innerAdapterMock + ->expects(self::once()) + ->method('lastModified') + ->with( + $this->buildAbsolutePath(self::FOO_BAR_FILE) + ) + ->willReturn($fileAttributesMock); + + self::assertSame( + $lastModified, + $this->adapter->lastModified(self::FOO_BAR_FILE)->lastModified() + ); + } + + /** + * @throws \League\Flysystem\FilesystemException + */ + public function testCopy(): void + { + $relativeCopiedFilePath = self::BAR_COPY_FILE; + + $this + ->innerAdapterMock + ->expects(self::once()) + ->method('copy') + ->with( + $this->buildAbsolutePath(self::FOO_BAR_FILE), + $this->buildAbsolutePath($relativeCopiedFilePath), + $this->config + ); + + $this->adapter->copy(self::FOO_BAR_FILE, $relativeCopiedFilePath, $this->config); + } + + /** + * @throws \League\Flysystem\FilesystemException + */ + public function testMove(): void + { + $this + ->innerAdapterMock + ->expects(self::once()) + ->method('move') + ->with( + $this->buildAbsolutePath(self::FOO_BAR_FILE), + $this->buildAbsolutePath(self::BAR_COPY_FILE), + $this->config + ); + + $this->adapter->move(self::FOO_BAR_FILE, self::BAR_COPY_FILE, $this->config); + } + + /** + * @throws \League\Flysystem\FilesystemException + */ + public function testListContents(): void + { + $contents = [ + self::FOO_BAR_FILE, + self::BAR_COPY_FILE, + ]; + + $fileAttributesMocks = []; + foreach ($contents as $relativeFilePath) { + $fileAttributesMock = $this->createFileAttributesPathMock( + $this->buildAbsolutePath($relativeFilePath) + ); + $fileAttributesMock + ->expects(self::once()) + ->method('withPath') + ->willReturn($this->createFileAttributesPathMock($relativeFilePath)); + + $fileAttributesMocks[] = $fileAttributesMock; + } + + $this + ->innerAdapterMock + ->expects(self::once()) + ->method('listContents') + ->with( + $this->buildAbsolutePath('/'), + true + )->willReturn($fileAttributesMocks); + + $rootDirectoryContents = $this->adapter->listContents('/', true); + foreach ($rootDirectoryContents as $storageAttributes) { + self::assertContains($storageAttributes->path(), $contents); + } + } + + /** + * @throws \League\Flysystem\FilesystemException + */ + public function testRead(): void + { + $this + ->innerAdapterMock + ->expects(self::once()) + ->method('read') + ->with( + $this->buildAbsolutePath(self::FOO_BAR_FILE) + ) + ->willReturn(self::FILE_CONTENTS); + + self::assertSame(self::FILE_CONTENTS, $this->adapter->read(self::FOO_BAR_FILE)); + } + + /** + * @depends testWrite + * + * @throws \League\Flysystem\FilesystemException + */ + public function testReadStream(): void + { + $fileHandle = tmpfile(); + $this + ->innerAdapterMock + ->expects(self::once()) + ->method('readStream') + ->with( + $this->buildAbsolutePath(self::FOO_BAR_FILE) + ) + ->willReturn($fileHandle); + + self::assertSame($fileHandle, $this->adapter->readStream(self::FOO_BAR_FILE)); + + fclose($fileHandle); + } + + /** + * @throws \League\Flysystem\FilesystemException + */ + public function testCreateDirectory(): void + { + $this + ->innerAdapterMock + ->expects(self::once()) + ->method('createDirectory') + ->with( + $this->buildAbsolutePath(self::MY_DIR_NAME), + $this->config + ); + + $this->adapter->createDirectory(self::MY_DIR_NAME, $this->config); + } + + /** + * @throws \League\Flysystem\FilesystemException + */ + public function testDeleteDirectory(): void + { + $this + ->innerAdapterMock + ->expects(self::once()) + ->method('deleteDirectory') + ->with( + $this->buildAbsolutePath(self::MY_DIR_NAME) + ); + + $this->adapter->deleteDirectory(self::MY_DIR_NAME); + } + + /** + * @throws \League\Flysystem\FilesystemException + */ + public function testVisibility(): void + { + $relativeFilePath = self::FOO_BAR_FILE; + $fileAttributesMock = $this->createFileAttributesMock( + 'visibility', + Visibility::PUBLIC + ); + + $this + ->innerAdapterMock + ->expects(self::once()) + ->method('visibility') + ->with( + $this->buildAbsolutePath($relativeFilePath) + ) + ->willReturn($fileAttributesMock); + + self::assertSame( + Visibility::PUBLIC, + $this->adapter->visibility($relativeFilePath)->visibility() + ); + } + + private function createPathPrefixerMock(): PathPrefixerInterface + { + $prefixerMock = $this->createMock(PathPrefixerInterface::class); + $prefixerMock + ->method('prefixPath') + ->willReturnCallback( + static function (string $relativePath): string { + return self::FLYSYSTEM_TEST_DIR . '/' . $relativePath; + } + ); + + $prefixerMock + ->method('stripPrefix') + ->willReturnCallback( + static function (string $absolutePath): string { + return str_replace( + ltrim(self::FLYSYSTEM_TEST_DIR, '/') . '/', + '', + $absolutePath + ); + } + ); + + return $prefixerMock; + } + + private function buildAbsolutePath(string $relativeFilePath): string + { + return self::FLYSYSTEM_TEST_DIR . '/' . $relativeFilePath; + } + + private function createFileAttributesPathMock(string $path): MockObject + { + return $this->createFileAttributesMock('path', $path); + } + + /** + * @param string|int $returnValue + */ + private function createFileAttributesMock(string $methodName, $returnValue): MockObject + { + $fileAttributesMock = $this->createMock(FileAttributes::class); + $fileAttributesMock + ->expects(self::once()) + ->method($methodName) + ->willReturn($returnValue); + + return $fileAttributesMock; + } +} diff --git a/tests/lib/IO/Flysystem/PathPrefixer/BaseSiteAccessAwarePathPrefixerTest.php b/tests/lib/IO/Flysystem/PathPrefixer/BaseSiteAccessAwarePathPrefixerTest.php new file mode 100644 index 0000000000..6a1ce536dd --- /dev/null +++ b/tests/lib/IO/Flysystem/PathPrefixer/BaseSiteAccessAwarePathPrefixerTest.php @@ -0,0 +1,88 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\IO\Flysystem\PathPrefixer; + +use Ibexa\Core\IO\Flysystem\PathPrefixer\PathPrefixerInterface; +use Ibexa\Tests\Core\Search\TestCase; + +/** + * @covers \Ibexa\Core\IO\Flysystem\PathPrefixer\PathPrefixerInterface + */ +abstract class BaseSiteAccessAwarePathPrefixerTest extends TestCase +{ + abstract protected function getPrefixer(): PathPrefixerInterface; + + /** + * @return iterable<string, array{string, string}> + */ + abstract public function getDataForTestPrefixPath(): iterable; + + /** + * @return iterable<string, array{string, string}> + */ + abstract public function getDataForTestPrefixDirectoryPath(): iterable; + + /** + * @return iterable<string, array{string, string}> + */ + abstract public function getDataForTestStripPrefixPath(): iterable; + + /** + * @return iterable<string, array{string, string}> + */ + public function getDataForTestStripDirectoryPrefix(): iterable + { + // treat file names as directories + yield from $this->getDataForTestStripPrefixPath(); + } + + /** + * @dataProvider getDataForTestPrefixPath + */ + final public function testPrefixPath(string $expectedPrefixedPath, string $path): void + { + self::assertSame( + $expectedPrefixedPath, + $this->getPrefixer()->prefixPath($path) + ); + } + + /** + * @dataProvider getDataForTestPrefixDirectoryPath + */ + final public function testPrefixDirectoryPath(string $expectedPrefixedPath, string $path): void + { + self::assertSame( + $expectedPrefixedPath, + $this->getPrefixer()->prefixDirectoryPath($path) + ); + } + + /** + * @dataProvider getDataForTestStripPrefixPath + */ + final public function testStripPrefix(string $expectedStrippedPath, string $path): void + { + self::assertSame( + $expectedStrippedPath, + $this->getPrefixer()->stripPrefix($path) + ); + } + + /** + * @dataProvider getDataForTestStripDirectoryPrefix + */ + final public function testStripDirectoryPrefix(string $expectedStrippedPath, string $path): void + { + self::assertSame( + $expectedStrippedPath, + $this->getPrefixer()->stripDirectoryPrefix($path) + ); + } +} diff --git a/tests/lib/IO/Flysystem/PathPrefixer/DFSSiteAccessAwarePathPrefixerTest.php b/tests/lib/IO/Flysystem/PathPrefixer/DFSSiteAccessAwarePathPrefixerTest.php new file mode 100644 index 0000000000..b3bc0b8f42 --- /dev/null +++ b/tests/lib/IO/Flysystem/PathPrefixer/DFSSiteAccessAwarePathPrefixerTest.php @@ -0,0 +1,82 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\IO\Flysystem\PathPrefixer; + +use Ibexa\Contracts\Core\SiteAccess\ConfigProcessor; +use Ibexa\Core\IO\Flysystem\PathPrefixer\DFSSiteAccessAwarePathPrefixer; +use Ibexa\Core\IO\Flysystem\PathPrefixer\PathPrefixerInterface; + +/** + * @covers \Ibexa\Core\IO\Flysystem\PathPrefixer\DFSSiteAccessAwarePathPrefixer + */ +final class DFSSiteAccessAwarePathPrefixerTest extends BaseSiteAccessAwarePathPrefixerTest +{ + public function getDataForTestPrefixPath(): iterable + { + $dfsRootDir = $this->getDFSRootDir(); + + yield 'dynamic path to relative file name' => [$dfsRootDir . '/var/storage/foo', 'foo']; + yield 'dynamic path to relative file path name' => [ + $dfsRootDir . '/var/storage/foo/bar', + 'foo/bar', + ]; + yield 'dynamic path to absolute file name' => [$dfsRootDir . '/var/storage/foo', '/foo']; + } + + public function getDataForTestPrefixDirectoryPath(): iterable + { + $dfsRootDir = $this->getDFSRootDir(); + + yield 'dynamic path to relative directory' => [ + $dfsRootDir . '/var/storage/foo/', + 'foo/', + ]; + yield 'dynamic path to absolute directory' => [ + $dfsRootDir . '/var/storage/foo/', + '/foo/', + ]; + yield 'dynamic path to relative directory, no trailing separator' => [ + $dfsRootDir . '/var/storage/foo/', + 'foo', + ]; + yield 'dynamic path to absolute directory, no trailing separator' => [ + $dfsRootDir . '/var/storage/foo/', + '/foo', + ]; + } + + public function getDataForTestStripPrefixPath(): iterable + { + $dfsRootDir = $this->getDFSRootDir(); + + yield 'relative single file name' => ['/foo', $dfsRootDir . '/var/storage/foo']; + yield 'relative file path' => ['/foo/bar', $dfsRootDir . '/var/storage/foo/bar']; + } + + protected function getPrefixer(): PathPrefixerInterface + { + $dynamicPath = '$var_dir$/$storage_dir$/'; + $configProcessor = $this->createMock(ConfigProcessor::class); + $configProcessor + ->method('processSettingValue') + ->with($dynamicPath) + ->willReturn('var/storage'); + + return new DFSSiteAccessAwarePathPrefixer( + $configProcessor, + $this->getDFSRootDir(), + $dynamicPath + ); + } + + private function getDFSRootDir(): string + { + return sys_get_temp_dir() . '/dfs'; + } +} diff --git a/tests/lib/IO/Flysystem/PathPrefixer/LocalSiteAccessAwarePathPrefixerTest.php b/tests/lib/IO/Flysystem/PathPrefixer/LocalSiteAccessAwarePathPrefixerTest.php new file mode 100644 index 0000000000..b27d547460 --- /dev/null +++ b/tests/lib/IO/Flysystem/PathPrefixer/LocalSiteAccessAwarePathPrefixerTest.php @@ -0,0 +1,61 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\IO\Flysystem\PathPrefixer; + +use Ibexa\Core\IO\Flysystem\PathPrefixer\LocalSiteAccessAwarePathPrefixer; +use Ibexa\Core\IO\Flysystem\PathPrefixer\PathPrefixerInterface; +use Ibexa\Core\IO\IOConfigProvider; + +/** + * @covers \Ibexa\Core\IO\Flysystem\PathPrefixer\LocalSiteAccessAwarePathPrefixer + */ +final class LocalSiteAccessAwarePathPrefixerTest extends BaseSiteAccessAwarePathPrefixerTest +{ + public function getDataForTestPrefixPath(): iterable + { + yield 'dynamic path to relative file name' => [__DIR__ . '/var/storage/foo', 'foo']; + yield 'dynamic path to relative file path name' => [ + __DIR__ . '/var/storage/foo/bar', + 'foo/bar', + ]; + yield 'dynamic path to absolute file name' => [__DIR__ . '/var/storage/foo', '/foo']; + } + + public function getDataForTestPrefixDirectoryPath(): iterable + { + yield 'dynamic path to relative directory' => [__DIR__ . '/var/storage/foo/', 'foo/']; + yield 'dynamic path to absolute directory' => [__DIR__ . '/var/storage/foo/', '/foo/']; + yield 'dynamic path to relative directory, no trailing separator' => [ + __DIR__ . '/var/storage/foo/', + 'foo', + ]; + yield 'dynamic path to absolute directory, no trailing separator' => [ + __DIR__ . '/var/storage/foo/', + '/foo', + ]; + } + + public function getDataForTestStripPrefixPath(): iterable + { + yield 'relative single file name' => ['/foo', __DIR__ . '/var/storage/foo']; + yield 'relative file path' => ['/foo/bar', __DIR__ . '/var/storage/foo/bar']; + } + + protected function getPrefixer(): PathPrefixerInterface + { + $configProviderMock = $this->createMock(IOConfigProvider::class); + $configProviderMock + ->method('getRootDir') + ->willReturn(__DIR__ . '/var/storage'); + + return new LocalSiteAccessAwarePathPrefixer( + $configProviderMock + ); + } +} diff --git a/tests/lib/IO/Flysystem/VisibilityConverter/BaseVisibilityConverterTest.php b/tests/lib/IO/Flysystem/VisibilityConverter/BaseVisibilityConverterTest.php new file mode 100644 index 0000000000..fb11de1ddc --- /dev/null +++ b/tests/lib/IO/Flysystem/VisibilityConverter/BaseVisibilityConverterTest.php @@ -0,0 +1,114 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\IO\Flysystem\VisibilityConverter; + +use Ibexa\Core\IO\Flysystem\VisibilityConverter\BaseVisibilityConverter; +use League\Flysystem\UnixVisibility\VisibilityConverter as FlysystemVisibilityConverter; +use League\Flysystem\Visibility; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\IO\Flysystem\VisibilityConverter\BaseVisibilityConverter + */ +abstract class BaseVisibilityConverterTest extends TestCase +{ + protected const FLYSYSTEM_FILE_FLAGS = 0600; + protected const FLYSYSTEM_DIRECTORY_FLAGS = 0700; + protected const DIFFERENT_FILE_FLAGS = 0604; + protected const DIFFERENT_DIRECTORY_FLAGS = 0704; + + protected BaseVisibilityConverter $visibilityConverter; + + /** @var \League\Flysystem\UnixVisibility\VisibilityConverter&\PHPUnit\Framework\MockObject\MockObject */ + protected FlysystemVisibilityConverter $innerVisibilityConverterMock; + + abstract protected function buildVisibilityConverter(): BaseVisibilityConverter; + + abstract public function getDataForTestForFile(): iterable; + + abstract public function getDataForTestForDirectory(): iterable; + + abstract public function getDataForTestInverseForFile(): iterable; + + abstract public function getDataForTestInverseForDirectory(): iterable; + + final protected function setUp(): void + { + $this->innerVisibilityConverterMock = $this->createMock(FlysystemVisibilityConverter::class); + $this->innerVisibilityConverterMock + ->method('forFile') + ->with(Visibility::PRIVATE) + ->willReturn(self::FLYSYSTEM_FILE_FLAGS); + $this->innerVisibilityConverterMock + ->method('forDirectory') + ->with(Visibility::PRIVATE) + ->willReturn(self::FLYSYSTEM_DIRECTORY_FLAGS); + + $this->visibilityConverter = $this->buildVisibilityConverter(); + } + + /** + * @dataProvider getDataForTestForFile + */ + final public function testForFile(string $visibility, int $expectedVisibilityFlags): void + { + self::assertSame( + $expectedVisibilityFlags, + $this->visibilityConverter->forFile($visibility) + ); + } + + /** + * @dataProvider getDataForTestForDirectory + */ + final public function testForDirectory(string $visibility, int $expectedVisibilityFlags): void + { + self::assertSame( + $expectedVisibilityFlags, + $this->visibilityConverter->forDirectory($visibility) + ); + } + + /** + * @dataProvider getDataForTestInverseForFile + */ + final public function testInverseForFile(int $fileVisibilityFlags, string $expectedVisibility): void + { + self::assertSame( + $expectedVisibility, + $this->visibilityConverter->inverseForFile($fileVisibilityFlags) + ); + } + + /** + * @dataProvider getDataForTestInverseForDirectory + */ + final public function testInverseForDirectory( + int $directoryVisibilityFlags, + string $expectedVisibility + ): void { + self::assertSame( + $expectedVisibility, + $this->visibilityConverter->inverseForDirectory($directoryVisibilityFlags) + ); + } + + final public function testDefaultForDirectories(): void + { + $this->innerVisibilityConverterMock + ->expects(self::once()) + ->method('defaultForDirectories') + ->willReturn(self::FLYSYSTEM_DIRECTORY_FLAGS); + + self::assertSame( + self::FLYSYSTEM_DIRECTORY_FLAGS, + $this->visibilityConverter->defaultForDirectories() + ); + } +} diff --git a/tests/lib/IO/Flysystem/VisibilityConverter/DFSVisibilityConverterTest.php b/tests/lib/IO/Flysystem/VisibilityConverter/DFSVisibilityConverterTest.php new file mode 100644 index 0000000000..85b1a3a830 --- /dev/null +++ b/tests/lib/IO/Flysystem/VisibilityConverter/DFSVisibilityConverterTest.php @@ -0,0 +1,89 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\IO\Flysystem\VisibilityConverter; + +use Ibexa\Core\IO\Flysystem\VisibilityConverter\BaseVisibilityConverter; +use Ibexa\Core\IO\Flysystem\VisibilityConverter\DFSVisibilityConverter; +use League\Flysystem\Visibility; + +/** + * @covers \Ibexa\Core\IO\Flysystem\VisibilityConverter\DFSVisibilityConverter + */ +final class DFSVisibilityConverterTest extends BaseVisibilityConverterTest +{ + private const DFS_FILE_FLAGS = 0640; + private const DFS_DIRECTORY_FLAGS = 0750; + + protected function buildVisibilityConverter(): BaseVisibilityConverter + { + return new DFSVisibilityConverter( + $this->innerVisibilityConverterMock, + [ + 'files' => self::DFS_FILE_FLAGS, + 'directories' => self::DFS_DIRECTORY_FLAGS, + ] + ); + } + + public function getDataForTestForFile(): iterable + { + yield 'public visibility (from DFS config)' => [ + Visibility::PUBLIC, + self::DFS_FILE_FLAGS, + ]; + yield 'private visibility (from Flysystem fallback)' => [ + Visibility::PRIVATE, + self::FLYSYSTEM_FILE_FLAGS, + ]; + } + + public function getDataForTestForDirectory(): iterable + { + yield 'public visibility (from DFS config)' => [ + Visibility::PUBLIC, + self::DFS_DIRECTORY_FLAGS, + ]; + yield 'private visibility (from Flysystem fallback)' => [ + Visibility::PRIVATE, + self::FLYSYSTEM_DIRECTORY_FLAGS, + ]; + } + + public function getDataForTestInverseForFile(): iterable + { + yield self::DFS_FILE_FLAGS . ' (DFS config) is public' => [ + self::DFS_FILE_FLAGS, + Visibility::PUBLIC, + ]; + yield self::FLYSYSTEM_FILE_FLAGS . ' (Flysystem fallback) is private' => [ + self::FLYSYSTEM_FILE_FLAGS, + Visibility::PRIVATE, + ]; + yield self::DIFFERENT_FILE_FLAGS . ' (Flysystem fallback default) is public' => [ + self::DIFFERENT_FILE_FLAGS, + Visibility::PUBLIC, + ]; + } + + public function getDataForTestInverseForDirectory(): iterable + { + yield self::DFS_DIRECTORY_FLAGS . ' (DFS config) is public' => [ + self::DFS_DIRECTORY_FLAGS, + Visibility::PUBLIC, + ]; + yield self::FLYSYSTEM_DIRECTORY_FLAGS . ' (Flysystem fallback) is private' => [ + self::FLYSYSTEM_DIRECTORY_FLAGS, + Visibility::PRIVATE, + ]; + yield self::DIFFERENT_DIRECTORY_FLAGS . ' (Flysystem fallback default) is public' => [ + self::DIFFERENT_DIRECTORY_FLAGS, + Visibility::PUBLIC, + ]; + } +} diff --git a/tests/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverterTest.php b/tests/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverterTest.php new file mode 100644 index 0000000000..3c9da4733b --- /dev/null +++ b/tests/lib/IO/Flysystem/VisibilityConverter/SiteAccessAwareVisibilityConverterTest.php @@ -0,0 +1,97 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\IO\Flysystem\VisibilityConverter; + +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\IO\Flysystem\VisibilityConverter\BaseVisibilityConverter; +use Ibexa\Core\IO\Flysystem\VisibilityConverter\SiteAccessAwareVisibilityConverter; +use League\Flysystem\Visibility; + +/** + * @covers \Ibexa\Core\IO\Flysystem\VisibilityConverter\SiteAccessAwareVisibilityConverter + */ +final class SiteAccessAwareVisibilityConverterTest extends BaseVisibilityConverterTest +{ + private const SITE_FILE_FLAGS = 0644; + private const SITE_DIRECTORY_FLAGS = 0755; + + protected function buildVisibilityConverter(): BaseVisibilityConverter + { + $configResolverMock = $this->createMock(ConfigResolverInterface::class); + $configResolverMock + ->method('getParameter') + ->willReturnMap( + [ + ['io.permissions.files', null, null, self::SITE_FILE_FLAGS], + ['io.permissions.directories', null, null, self::SITE_DIRECTORY_FLAGS], + ] + ); + + return new SiteAccessAwareVisibilityConverter( + $this->innerVisibilityConverterMock, + $configResolverMock + ); + } + + public function getDataForTestForFile(): iterable + { + yield 'public visibility (from SiteAccess config)' => [ + Visibility::PUBLIC, + self::SITE_FILE_FLAGS, + ]; + yield 'private visibility (from Flysystem fallback)' => [ + Visibility::PRIVATE, + self::FLYSYSTEM_FILE_FLAGS, + ]; + } + + public function getDataForTestForDirectory(): iterable + { + yield 'public visibility (from SiteAccess config)' => [ + Visibility::PUBLIC, + self::SITE_DIRECTORY_FLAGS, + ]; + yield 'private visibility (from Flysystem fallback)' => [ + Visibility::PRIVATE, + self::FLYSYSTEM_DIRECTORY_FLAGS, + ]; + } + + public function getDataForTestInverseForFile(): iterable + { + yield self::SITE_FILE_FLAGS . ' (SiteAccess config) is public' => [ + self::SITE_FILE_FLAGS, + Visibility::PUBLIC, + ]; + yield self::FLYSYSTEM_FILE_FLAGS . ' (Flysystem fallback) is private' => [ + self::FLYSYSTEM_FILE_FLAGS, + Visibility::PRIVATE, + ]; + yield self::DIFFERENT_FILE_FLAGS . ' (Flysystem fallback default) is public' => [ + self::DIFFERENT_FILE_FLAGS, + Visibility::PUBLIC, + ]; + } + + public function getDataForTestInverseForDirectory(): iterable + { + yield self::SITE_DIRECTORY_FLAGS . ' (SiteAccess config) is public' => [ + self::SITE_DIRECTORY_FLAGS, + Visibility::PUBLIC, + ]; + yield self::FLYSYSTEM_DIRECTORY_FLAGS . ' (Flysystem fallback) is private' => [ + self::FLYSYSTEM_DIRECTORY_FLAGS, + Visibility::PRIVATE, + ]; + yield self::DIFFERENT_DIRECTORY_FLAGS . ' (Flysystem fallback default) is public' => [ + self::DIFFERENT_DIRECTORY_FLAGS, + Visibility::PUBLIC, + ]; + } +} diff --git a/tests/lib/IO/IOBinarydataHandler/FlysystemTest.php b/tests/lib/IO/IOBinarydataHandler/FlysystemTest.php new file mode 100644 index 0000000000..8a790a4cae --- /dev/null +++ b/tests/lib/IO/IOBinarydataHandler/FlysystemTest.php @@ -0,0 +1,174 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\IO\IOBinarydataHandler; + +use Ibexa\Contracts\Core\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct; +use Ibexa\Core\IO\Exception\BinaryFileNotFoundException; +use Ibexa\Core\IO\IOBinarydataHandler; +use Ibexa\Core\IO\IOBinarydataHandler\Flysystem; +use League\Flysystem\FilesystemOperator; +use League\Flysystem\UnableToDeleteFile; +use League\Flysystem\UnableToReadFile; +use PHPUnit\Framework\TestCase; + +class FlysystemTest extends TestCase +{ + /** @var \Ibexa\Core\IO\IOBinarydataHandler|\PHPUnit\Framework\MockObject\MockObject */ + private IOBinarydataHandler $handler; + + /** @var \League\Flysystem\FilesystemOperator|\PHPUnit\Framework\MockObject\MockObject */ + private FilesystemOperator $filesystem; + + protected function setUp(): void + { + $this->filesystem = $this->createMock(FilesystemOperator::class); + $this->handler = new Flysystem($this->filesystem); + } + + public function testCreate(): void + { + $stream = fopen('php://memory', 'rb'); + $spiBinaryFileCreateStruct = new SPIBinaryFileCreateStruct(); + $spiBinaryFileCreateStruct->id = 'prefix/my/file.png'; + $spiBinaryFileCreateStruct->mimeType = 'image/png'; + $spiBinaryFileCreateStruct->size = 123; + $spiBinaryFileCreateStruct->mtime = 1307155200; + $spiBinaryFileCreateStruct->setInputStream($stream); + + $this->filesystem + ->expects($this->once()) + ->method('writeStream') + ->with( + $this->equalTo($spiBinaryFileCreateStruct->id), + $this->equalTo($stream), + $this->equalTo( + [ + 'mimetype' => 'image/png', + 'visibility' => 'public', + 'directory_visibility' => 'public', + ] + ) + ); + + $this->handler->create($spiBinaryFileCreateStruct); + } + + public function testDelete() + { + $this->filesystem + ->expects($this->once()) + ->method('delete') + ->with('prefix/my/file.png'); + + $this->handler->delete('prefix/my/file.png'); + } + + public function testDeleteNotFound(): void + { + // Note: technically Flysystem's v2+ Local Adapter silently skips non-existent file + $filePath = 'prefix/my/file.png'; + $this->filesystem + ->expects($this->once()) + ->method('delete') + ->with($filePath) + ->willThrowException(UnableToDeleteFile::atLocation($filePath)); + + $this->expectException(BinaryFileNotFoundException::class); + + $this->handler->delete($filePath); + } + + /** + * @throws \Ibexa\Core\IO\Exception\BinaryFileNotFoundException + */ + public function testGetContents(): void + { + $filePath = 'prefix/my/file.png'; + $fileContents = 'This is my contents'; + $this->filesystem + ->expects($this->once()) + ->method('read') + ->with($filePath) + ->willReturn($fileContents); + + self::assertEquals( + $fileContents, + $this->handler->getContents($filePath) + ); + } + + public function testGetContentsNotFound(): void + { + $filePath = 'prefix/my/file.png'; + $this->filesystem + ->expects($this->once()) + ->method('read') + ->with($filePath) + ->willThrowException(UnableToReadFile::fromLocation($filePath)); + + $this->expectException(BinaryFileNotFoundException::class); + $this->handler->getContents($filePath); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testGetResource(): void + { + $resource = fopen('php://temp', 'rb'); + + $filePath = 'prefix/my/file.png'; + $this->filesystem + ->expects($this->once()) + ->method('readStream') + ->with($filePath) + ->willReturn($resource); + + self::assertEquals( + $resource, + $this->handler->getResource($filePath) + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testGetResourceNotFound(): void + { + $filePath = 'prefix/my/file.png'; + $this->filesystem + ->expects($this->once()) + ->method('readStream') + ->with($filePath) + ->willThrowException(UnableToReadFile::fromLocation($filePath)); + + $this->expectException(BinaryFileNotFoundException::class); + $this->handler->getResource($filePath); + } + + public function testGetUri(): void + { + self::assertEquals( + '/prefix/my/file.png', + $this->handler->getUri('prefix/my/file.png') + ); + } + + public function testDeleteDirectory(): void + { + $this->filesystem + ->expects($this->once()) + ->method('deleteDirectory') + ->with('some/path'); + + $this->handler->deleteDirectory('some/path'); + } +} + +class_alias(FlysystemTest::class, 'eZ\Publish\Core\IO\Tests\IOBinarydataHandler\FlysystemTest'); diff --git a/tests/lib/IO/IOMetadataHandler/FlysystemTest.php b/tests/lib/IO/IOMetadataHandler/FlysystemTest.php new file mode 100644 index 0000000000..9c21d97d74 --- /dev/null +++ b/tests/lib/IO/IOMetadataHandler/FlysystemTest.php @@ -0,0 +1,159 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\IO\IOMetadataHandler; + +use DateTime; +use Ibexa\Contracts\Core\IO\BinaryFile as SPIBinaryFile; +use Ibexa\Contracts\Core\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct; +use Ibexa\Core\IO\Exception\BinaryFileNotFoundException; +use Ibexa\Core\IO\IOMetadataHandler\Flysystem; +use League\Flysystem\UnableToRetrieveMetadata; +use PHPUnit\Framework\TestCase; + +class FlysystemTest extends TestCase +{ + /** @var \Ibexa\Core\IO\IOMetadataHandler|\PHPUnit\Framework\MockObject\MockObject */ + private $handler; + + /** @var \League\Flysystem\FilesystemOperator|\PHPUnit\Framework\MockObject\MockObject */ + private $filesystem; + + protected function setUp(): void + { + $this->filesystem = $this->createMock(\League\Flysystem\FilesystemOperator::class); + $this->handler = new Flysystem($this->filesystem); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testCreate(): void + { + // good example of bad responsibilities... since create also loads, we test the same thing twice + $spiCreateStruct = new SPIBinaryFileCreateStruct(); + $spiCreateStruct->id = 'prefix/my/file.png'; + $spiCreateStruct->size = 123; + $spiCreateStruct->mtime = new DateTime('@1307155200'); + + $expectedSpiBinaryFile = new SPIBinaryFile(); + $expectedSpiBinaryFile->id = 'prefix/my/file.png'; + $expectedSpiBinaryFile->size = 123; + $expectedSpiBinaryFile->mtime = new DateTime('@1307155200'); + + $this->filesystem + ->expects(self::once()) + ->method('fileSize') + ->with($spiCreateStruct->id) + ->willReturn(123); + + $this->filesystem + ->expects(self::once()) + ->method('lastModified') + ->with($spiCreateStruct->id) + ->willReturn(1307155200); + + $spiBinaryFile = $this->handler->create($spiCreateStruct); + + $this->assertInstanceOf(SPIBinaryFile::class, $spiBinaryFile); + $this->assertEquals($expectedSpiBinaryFile, $spiBinaryFile); + } + + public function testDelete() + { + $this->filesystem->expects($this->never())->method('delete'); + $this->handler->delete('prefix/my/file.png'); + } + + public function testLoad(): void + { + $expectedSpiBinaryFile = new SPIBinaryFile(); + $filePath = 'prefix/my/file.png'; + $expectedSpiBinaryFile->id = $filePath; + $expectedSpiBinaryFile->size = 123; + $expectedSpiBinaryFile->mtime = new DateTime('@1307155200'); + + $this->filesystem + ->expects(self::once()) + ->method('fileSize') + ->with($filePath) + ->willReturn(123); + + $this->filesystem + ->expects(self::once()) + ->method('lastModified') + ->with($filePath) + ->willReturn(1307155200); + + $spiBinaryFile = $this->handler->load($filePath); + + $this->assertInstanceOf(SPIBinaryFile::class, $spiBinaryFile); + $this->assertEquals($expectedSpiBinaryFile, $spiBinaryFile); + } + + public function testLoadNotFound(): void + { + $notExistentPath = 'prefix/my/file.png'; + $this->filesystem + ->expects(self::once()) + ->method('fileSize') + ->with($notExistentPath) + ->willThrowException(UnableToRetrieveMetadata::fileSize($notExistentPath)); + + $this->expectException(BinaryFileNotFoundException::class); + + $this->handler->load($notExistentPath); + } + + /** + * @dataProvider getDataForFileExists + */ + public function testExists(string $filePath, bool $exists): void + { + $this->filesystem + ->expects($this->once()) + ->method('fileExists') + ->with($filePath) + // Note: test proper proxying of Flysystem call as this is a unit test + ->willReturn($exists); + + self::assertSame($exists, $this->handler->exists($filePath)); + } + + public function getDataForFileExists(): iterable + { + $filePath = 'prefix/my/file.png'; + + yield 'exists' => [$filePath, true]; + yield 'does not exist' => [$filePath, false]; + } + + public function testGetMimeType(): void + { + $fileName = 'file.txt'; + $this->filesystem + ->expects(self::once()) + ->method('mimeType') + ->with($fileName) + // Note: test proper proxying of Flysystem call as this is a unit test + ->willReturn('text/plain'); + + self::assertEquals('text/plain', $this->handler->getMimeType($fileName)); + } + + public function testDeleteDirectory(): void + { + // test this actually doesn't call Flysystem's FilesystemOperator::deleteDirectory + // (see \Ibexa\Core\IO\IOMetadataHandler\Flysystem::deleteDirectory for more details) + $this->filesystem + ->expects(self::never()) + ->method('deleteDirectory'); + + $this->handler->deleteDirectory('some/path'); + } +} + +class_alias(FlysystemTest::class, 'eZ\Publish\Core\IO\Tests\IOMetadataHandler\FlysystemTest'); diff --git a/tests/lib/IO/IOMetadataHandler/LegacyDFSClusterTest.php b/tests/lib/IO/IOMetadataHandler/LegacyDFSClusterTest.php new file mode 100644 index 0000000000..eac55e27d6 --- /dev/null +++ b/tests/lib/IO/IOMetadataHandler/LegacyDFSClusterTest.php @@ -0,0 +1,252 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\IO\IOMetadataHandler; + +use DateTime; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\Query\QueryBuilder; +use Doctrine\DBAL\Result; +use Ibexa\Contracts\Core\IO\BinaryFile as SPIBinaryFile; +use Ibexa\Contracts\Core\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Core\IO\Exception\BinaryFileNotFoundException; +use Ibexa\Core\IO\IOMetadataHandler\LegacyDFSCluster; +use Ibexa\Core\IO\UrlDecorator; +use PHPUnit\Framework\TestCase; + +class LegacyDFSClusterTest extends TestCase +{ + /** @var \Ibexa\Core\IO\IOMetadataHandler&\PHPUnit\Framework\MockObject\MockObject */ + private $handler; + + /** @var \Doctrine\DBAL\Connection&\PHPUnit\Framework\MockObject\MockObject */ + private $dbalMock; + + /** @var \Doctrine\DBAL\Query\QueryBuilder&\PHPUnit\Framework\MockObject\MockObject */ + private $qbMock; + + /** @var \Ibexa\Core\IO\UrlDecorator&\PHPUnit\Framework\MockObject\MockObject */ + private $urlDecoratorMock; + + protected function setUp(): void + { + $this->dbalMock = $this->createMock(Connection::class); + + $this->qbMock = $this->getMockBuilder(QueryBuilder::class) + ->disableOriginalConstructor() + ->getMock(); + + $this->dbalMock->method('createQueryBuilder')->willReturn($this->qbMock); + $this->urlDecoratorMock = $this->createMock(UrlDecorator::class); + + $this->handler = new LegacyDFSCluster( + $this->dbalMock, + $this->urlDecoratorMock + ); + } + + /** + * @return iterable<array{string, string, int, \DateTime, \DateTime}> + */ + public function providerCreate(): iterable + { + return [ + ['prefix/my/file.png', 'image/png', 123, new DateTime('@1307155200'), new DateTime('@1307155200')], + ['prefix/my/file.png', 'image/png', 123, new DateTime('@1307155200'), new DateTime('@1307155200')], // Duplicate, should not fail + ['prefix/my/file.png', 'image/png', 123, new DateTime('@1307155242'), new DateTime('@1307155242')], + ]; + } + + /** + * @dataProvider providerCreate + */ + public function testCreate(string $id, string $mimeType, int $size, \DateTime $mtime, \DateTime $mtimeExpected): void + { + $this->dbalMock + ->expects(self::once()) + ->method('insert') + ->with('ezdfsfile'); + + $spiCreateStruct = new SPIBinaryFileCreateStruct(); + $spiCreateStruct->id = $id; + $spiCreateStruct->mimeType = $mimeType; + $spiCreateStruct->size = $size; + $spiCreateStruct->mtime = $mtime; + + $spiBinary = $this->handler->create($spiCreateStruct); + + self::assertInstanceOf(SPIBinaryFile::class, $spiBinary); + self::assertEquals($mtimeExpected, $spiBinary->mtime); + } + + public function testCreateInvalidArgument(): void + { + $this->dbalMock + ->expects(self::never()) + ->method('insert'); + + $spiCreateStruct = new SPIBinaryFileCreateStruct(); + $spiCreateStruct->id = 'prefix/my/file.png'; + $spiCreateStruct->mimeType = 'image/png'; + $spiCreateStruct->size = 123; + $spiCreateStruct->mtime = 1307155242; // Invalid, should be a DateTime + + $this->expectException(InvalidArgumentException::class); + $this->handler->create($spiCreateStruct); + } + + public function testDelete(): void + { + $this->dbalMock + ->expects(self::once()) + ->method('delete') + ->with('ezdfsfile') + ->willReturn(1); + + $this->handler->delete('prefix/my/file.png'); + } + + public function testDeleteNotFound(): void + { + $this->dbalMock + ->expects(self::once()) + ->method('delete') + ->with('ezdfsfile') + ->willReturn(0); + + $this->expectException(BinaryFileNotFoundException::class); + $this->handler->delete('prefix/my/file.png'); + } + + public function testLoad(): void + { + $this->setupQueryBuilderLoad(1, ['size' => 123, 'datatype' => 'image/png', 'mtime' => 1307155200]); + + $expectedSpiBinaryFile = new SPIBinaryFile(); + $expectedSpiBinaryFile->id = 'prefix/my/file.png'; + $expectedSpiBinaryFile->size = 123; + $expectedSpiBinaryFile->mtime = new DateTime('@1307155200'); + $expectedSpiBinaryFile->mimeType = 'image/png'; + + self::assertEquals( + $expectedSpiBinaryFile, + $this->handler->load('prefix/my/file.png') + ); + } + + public function testLoadNotFound(): void + { + $this->setupQueryBuilderLoad(0, null); + + $this->expectException(BinaryFileNotFoundException::class); + $this->handler->load('prefix/my/file.png'); + } + + public function testExists(): void + { + $this->setupQueryBuilderLoad(1, null); + + self::assertTrue($this->handler->exists('prefix/my/file.png')); + } + + public function testExistsNot(): void + { + $this->setupQueryBuilderLoad(0, null); + + self::assertFalse($this->handler->exists('prefix/my/file.png')); + } + + public function testDeletedirectory(): void + { + $this->urlDecoratorMock + ->expects(self::once()) + ->method('decorate') + ->willReturn('prefix/images/_alias/subfolder'); + + $this->qbMock + ->expects(self::once()) + ->method('delete') + ->with('ezdfsfile') + ->willReturnSelf(); + + $this->qbMock + ->expects(self::once()) + ->method('where') + ->with('name LIKE :spiPath ESCAPE :esc') + ->willReturnSelf(); + + $this->qbMock + ->expects(self::exactly(2)) + ->method('setParameter') + ->withConsecutive( + [':esc', '\\'], + [':spiPath', 'prefix/images/\_alias/subfolder/%'], + ) + ->willReturnSelf(); + + $this->qbMock + ->expects(self::once()) + ->method('execute') + ->willReturn(1); + + $this->handler->deleteDirectory('images/_alias/subfolder/'); + } + + /** + * @return \PHPUnit\Framework\MockObject\MockObject + */ + protected function createDbalStatementMock() + { + return $this->createMock(Statement::class); + } + + /** + * @param array<mixed>|null $result + */ + private function setupQueryBuilderLoad(int $rowCount, ?array $result): void + { + $resultMock = $this->createMock(Result::class); + $resultMock + ->expects(self::once()) + ->method('rowCount') + ->willReturn($rowCount); + + if ($result === null) { + $resultMock + ->expects(self::never()) + ->method('fetchAssociative'); + } else { + $resultMock + ->expects(self::once()) + ->method('fetchAssociative') + ->willReturn($result); + } + + $this->qbMock + ->expects(self::once()) + ->method('select') + ->willReturnSelf(); + + $this->qbMock + ->expects(self::once()) + ->method('from') + ->willReturnSelf(); + + $this->qbMock + ->method('andWhere') + ->willReturnSelf(); + + $this->qbMock + ->method('setParameter') + ->willReturnSelf(); + + $this->qbMock + ->method('execute') + ->willReturn($resultMock); + } +} diff --git a/eZ/Publish/Core/IO/Tests/IOServiceTest.php b/tests/lib/IO/IOServiceTest.php similarity index 83% rename from eZ/Publish/Core/IO/Tests/IOServiceTest.php rename to tests/lib/IO/IOServiceTest.php index 8229172ce1..9035c0f86d 100644 --- a/eZ/Publish/Core/IO/Tests/IOServiceTest.php +++ b/tests/lib/IO/IOServiceTest.php @@ -4,35 +4,36 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\IO\Tests; - -use eZ\Publish\Core\IO\Exception\BinaryFileNotFoundException; -use eZ\Publish\Core\IO\IOBinarydataHandler; -use eZ\Publish\Core\IO\IOMetadataHandler; -use eZ\Publish\Core\IO\IOService; -use eZ\Publish\Core\IO\Values\BinaryFile; -use eZ\Publish\Core\IO\Values\BinaryFileCreateStruct; -use eZ\Publish\SPI\IO\BinaryFile as SPIBinaryFile; -use eZ\Publish\SPI\IO\MimeTypeDetector; +namespace Ibexa\Tests\Core\IO; + +use Ibexa\Contracts\Core\IO\BinaryFile as SPIBinaryFile; +use Ibexa\Contracts\Core\IO\BinaryFileCreateStruct as SPIBinaryFileCreateStruct; +use Ibexa\Contracts\Core\IO\MimeTypeDetector; +use Ibexa\Core\IO\Exception\BinaryFileNotFoundException; +use Ibexa\Core\IO\IOBinarydataHandler; +use Ibexa\Core\IO\IOMetadataHandler; +use Ibexa\Core\IO\IOService; +use Ibexa\Core\IO\Values\BinaryFile; +use Ibexa\Core\IO\Values\BinaryFileCreateStruct; use PHPUnit\Framework\TestCase; /** - * Test case for IO Service. + * @covers \Ibexa\Core\IO\IOService */ class IOServiceTest extends TestCase { public const PREFIX = 'test-prefix'; - /** @var \eZ\Publish\Core\IO\IOService */ + /** @var \Ibexa\Core\IO\IOService */ protected $IOService; - /** @var \eZ\Publish\Core\IO\IOMetadataHandler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\IO\IOMetadataHandler|\PHPUnit\Framework\MockObject\MockObject */ protected $metadataHandlerMock; - /** @var \eZ\Publish\Core\IO\IOBinarydataHandler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\IO\IOBinarydataHandler|\PHPUnit\Framework\MockObject\MockObject */ protected $binarydataHandlerMock; - /** @var \eZ\Publish\SPI\IO\MimeTypeDetector|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\IO\MimeTypeDetector|\PHPUnit\Framework\MockObject\MockObject */ protected $mimeTypeDetectorMock; protected function setUp(): void @@ -53,8 +54,6 @@ protected function setUp(): void /** * Test creating new BinaryCreateStruct from uploaded file. - * - * @covers \eZ\Publish\Core\IO\IOService::newBinaryCreateStructFromUploadedFile */ public function testNewBinaryCreateStructFromUploadedFile() { @@ -86,9 +85,6 @@ public function testNewBinaryCreateStructFromUploadedFile() } } - /** - * @covers \eZ\Publish\Core\IO\IOService::newBinaryCreateStructFromUploadedFile - */ public function testNewBinaryCreateStructFromLocalFile() { $file = __FILE__; @@ -113,9 +109,6 @@ public function testNewBinaryCreateStructFromLocalFile() } /** - * @covers \eZ\Publish\Core\IO\IOService::createBinaryFile - * @covers \eZ\Publish\Core\IO\IOService::buildSPIBinaryFileCreateStructObject - * @covers \eZ\Publish\Core\IO\IOService::buildDomainBinaryFileObject * @depends testNewBinaryCreateStructFromLocalFile */ public function testCreateBinaryFile(BinaryFileCreateStruct $createStruct) @@ -135,7 +128,7 @@ public function testCreateBinaryFile(BinaryFileCreateStruct $createStruct) ->with( $this->callback( static function ($subject) use ($id) { - if (!$subject instanceof \eZ\Publish\SPI\IO\BinaryFileCreateStruct) { + if (!$subject instanceof SPIBinaryFileCreateStruct) { return false; } @@ -158,9 +151,6 @@ static function ($subject) use ($id) { return $binaryFile; } - /** - * @covers \eZ\Publish\Core\IO\IOService::loadBinaryFile - */ public function testLoadBinaryFile() { $id = 'my/path.png'; @@ -183,9 +173,6 @@ public function testLoadBinaryFile() return $binaryFile; } - /** - * @covers \eZ\Publish\Core\IO\IOService::loadBinaryFile - */ public function testLoadBinaryFileNoMetadataUri() { $id = 'my/path.png'; @@ -216,8 +203,6 @@ public function testLoadBinaryFileNoMetadataUri() } /** - * @covers \eZ\Publish\Core\IO\IOService::loadBinaryFile - * * @return mixed Whatever loadBinaryFile returns */ public function testLoadBinaryFileNotFound() @@ -266,7 +251,6 @@ public function testLoadBinaryFileByUriNotFound() } /** - * @covers \eZ\Publish\Core\IO\IOService::getFileInputStream * @depends testCreateBinaryFile */ public function testGetFileInputStream(BinaryFile $binaryFile) @@ -276,7 +260,6 @@ public function testGetFileInputStream(BinaryFile $binaryFile) /** * @depends testLoadBinaryFile - * @covers \eZ\Publish\Core\IO\IOService::getFileContents */ public function testGetFileContents(BinaryFile $binaryFile) { @@ -296,7 +279,6 @@ public function testGetFileContents(BinaryFile $binaryFile) /** * @depends testCreateBinaryFile - * @covers \eZ\Publish\Core\IO\IOService::exists() */ public function testExists(BinaryFile $binaryFile) { @@ -313,9 +295,6 @@ public function testExists(BinaryFile $binaryFile) ); } - /** - * @covers \eZ\Publish\Core\IO\IOService::exists() - */ public function testExistsNot() { $this->metadataHandlerMock @@ -333,7 +312,6 @@ public function testExistsNot() /** * @depends testCreateBinaryFile - * @covers \eZ\Publish\Core\IO\IOService::getMimeType() */ public function testGetMimeType(BinaryFile $binaryFile) { @@ -352,7 +330,6 @@ public function testGetMimeType(BinaryFile $binaryFile) } /** - * @covers \eZ\Publish\Core\IO\IOService::deleteBinaryFile * @depends testCreateBinaryFile */ public function testDeleteBinaryFile(BinaryFile $binaryFile) @@ -370,9 +347,6 @@ public function testDeleteBinaryFile(BinaryFile $binaryFile) $this->getIOService()->deleteBinaryFile($binaryFile); } - /** - * @covers \eZ\Publish\Core\IO\IOService::deleteDirectory() - */ public function testDeleteDirectory() { $id = 'some/directory'; @@ -392,8 +366,6 @@ public function testDeleteDirectory() } /** - * @covers \eZ\Publish\Core\IO\IOService::deleteBinaryFile - * * @return mixed Whatever deleteBinaryFile returned */ public function testDeleteBinaryFileNotFound() @@ -409,7 +381,7 @@ public function getPrefixedUri($uri) } /** - * @return \eZ\Publish\Core\IO\IOService + * @return \Ibexa\Core\IO\IOService */ protected function getIOService() { @@ -419,14 +391,12 @@ protected function getIOService() /** * Asserts that the given $ioCreateStruct is of the right type and that id matches the expected value. * - * @param $ioCreateStruct - * - * @return bool + * @param int $spiId */ - private function getSPIBinaryFileCreateStructCallback($spiId) + private function getSPIBinaryFileCreateStructCallback($spiId): \Closure { return static function ($subject) use ($spiId) { - if (!$subject instanceof \eZ\Publish\SPI\IO\BinaryFileCreateStruct) { + if (!$subject instanceof SPIBinaryFileCreateStruct) { return false; } @@ -435,10 +405,10 @@ private function getSPIBinaryFileCreateStructCallback($spiId) } /** - * @return bool|\eZ\Publish\Core\IO\Values\BinaryFile + * @return bool|\Ibexa\Core\IO\Values\BinaryFile * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentValue + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException */ protected function loadBinaryFileNotFound() { @@ -470,10 +440,10 @@ protected function deleteBinaryFileNotFound(): void } /** - * @return bool|\eZ\Publish\Core\IO\Values\BinaryFile + * @return bool|\Ibexa\Core\IO\Values\BinaryFile * - * @throws \eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentValue + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException */ protected function loadBinaryFileByUriNotFound() { @@ -495,3 +465,5 @@ protected function loadBinaryFileByUriNotFound() return $this->getIOService()->loadBinaryFileByUri($spiId); } } + +class_alias(IOServiceTest::class, 'eZ\Publish\Core\IO\Tests\IOServiceTest'); diff --git a/tests/lib/IO/MetadataHandler/ImageSizeTest.php b/tests/lib/IO/MetadataHandler/ImageSizeTest.php new file mode 100644 index 0000000000..69d113660b --- /dev/null +++ b/tests/lib/IO/MetadataHandler/ImageSizeTest.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\IO\MetadataHandler; + +use Ibexa\Core\IO\MetadataHandler\ImageSize as ImageSizeMetadataHandler; +use PHPUnit\Framework\TestCase; + +/** + * @group fieldType + * @group ezimage + */ +class ImageSizeTest extends TestCase +{ + public function testExtract() + { + $metadataHandler = new ImageSizeMetadataHandler(); + $file = __DIR__ . '/ezplogo.png'; + self::assertEquals( + ['width' => 189, 'height' => 200, 'mime' => 'image/png'], + $metadataHandler->extract($file) + ); + } +} + +class_alias(ImageSizeTest::class, 'eZ\Publish\Core\IO\Tests\MetadataHandler\ImageSizeTest'); diff --git a/eZ/Publish/Core/Repository/Tests/Service/Integration/ezplogo.png b/tests/lib/IO/MetadataHandler/ezplogo.png similarity index 100% rename from eZ/Publish/Core/Repository/Tests/Service/Integration/ezplogo.png rename to tests/lib/IO/MetadataHandler/ezplogo.png diff --git a/tests/lib/IO/MimeTypeDetector/FileInfoTest.php b/tests/lib/IO/MimeTypeDetector/FileInfoTest.php new file mode 100644 index 0000000000..57d5d46434 --- /dev/null +++ b/tests/lib/IO/MimeTypeDetector/FileInfoTest.php @@ -0,0 +1,48 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\IO\MimeTypeDetector; + +use Ibexa\Core\IO\MimeTypeDetector\FileInfo as MimeTypeDetector; +use PHPUnit\Framework\TestCase; + +class FileInfoTest extends TestCase +{ + /** @var \Ibexa\Core\IO\MimeTypeDetector\FileInfo */ + protected $mimeTypeDetector; + + protected function setUp(): void + { + $this->mimeTypeDetector = new MimeTypeDetector(); + } + + protected function getFixture() + { + return __DIR__ . '/../../_fixtures/squirrel-developers.jpg'; + } + + public function testGetFromPath() + { + self::assertEquals( + $this->mimeTypeDetector->getFromPath( + $this->getFixture() + ), + 'image/jpeg' + ); + } + + public function testGetFromBuffer() + { + self::assertEquals( + $this->mimeTypeDetector->getFromBuffer( + file_get_contents($this->getFixture()) + ), + 'image/jpeg' + ); + } +} + +class_alias(FileInfoTest::class, 'eZ\Publish\Core\IO\Tests\MimeTypeDetector\FileInfoTest'); diff --git a/eZ/Publish/Core/IO/Tests/TolerantIOServiceTest.php b/tests/lib/IO/TolerantIOServiceTest.php similarity index 76% rename from eZ/Publish/Core/IO/Tests/TolerantIOServiceTest.php rename to tests/lib/IO/TolerantIOServiceTest.php index 76627264b3..18cf68efce 100644 --- a/eZ/Publish/Core/IO/Tests/TolerantIOServiceTest.php +++ b/tests/lib/IO/TolerantIOServiceTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\IO\Tests; +namespace Ibexa\Tests\Core\IO; -use eZ\Publish\Core\IO\TolerantIOService; -use eZ\Publish\Core\IO\Values\MissingBinaryFile; +use Ibexa\Core\IO\TolerantIOService; +use Ibexa\Core\IO\Values\MissingBinaryFile; /** - * Test case for the TolerantIOService. + * @covers \Ibexa\Core\IO\IOService */ class TolerantIOServiceTest extends IOServiceTest { @@ -26,9 +26,6 @@ protected function setUp(): void ); } - /** - * @covers \eZ\Publish\Core\IO\IOService::loadBinaryFile - */ public function testLoadBinaryFileNotFound() { $binaryFile = parent::loadBinaryFileNotFound(); @@ -39,9 +36,6 @@ public function testLoadBinaryFileNotFound() ); } - /** - * @covers \eZ\Publish\Core\IO\TolerantIOService::createMissingBinaryFile - */ public function testCreateMissingBinaryFile() { $id = 'id.ext'; @@ -62,19 +56,19 @@ public function testCreateMissingBinaryFile() /** * Overridden to change the expected exception (none). - * - * @covers \eZ\Publish\Core\IO\IOService::deleteBinaryFile */ public function testDeleteBinaryFileNotFound() { - parent::deleteBinaryFileNotFound(); + $this->deleteBinaryFileNotFound(); } public function testLoadBinaryFileByUriNotFound() { self::assertEquals( new MissingBinaryFile(['id' => 'my/path.png']), - parent::loadBinaryFileByUriNotFound() + $this->loadBinaryFileByUriNotFound() ); } } + +class_alias(TolerantIOServiceTest::class, 'eZ\Publish\Core\IO\Tests\TolerantIOServiceTest'); diff --git a/eZ/Publish/Core/IO/Tests/UrlDecorator/AbsolutePrefixTest.php b/tests/lib/IO/UrlDecorator/AbsolutePrefixTest.php similarity index 86% rename from eZ/Publish/Core/IO/Tests/UrlDecorator/AbsolutePrefixTest.php rename to tests/lib/IO/UrlDecorator/AbsolutePrefixTest.php index 95c33fb825..0581fb6dfc 100644 --- a/eZ/Publish/Core/IO/Tests/UrlDecorator/AbsolutePrefixTest.php +++ b/tests/lib/IO/UrlDecorator/AbsolutePrefixTest.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\IO\Tests\UrlDecorator; +namespace Ibexa\Tests\Core\IO\UrlDecorator; -use eZ\Publish\Core\IO\IOConfigProvider; -use eZ\Publish\Core\IO\UrlDecorator; -use eZ\Publish\Core\IO\UrlDecorator\AbsolutePrefix; +use Ibexa\Core\IO\IOConfigProvider; +use Ibexa\Core\IO\UrlDecorator; +use Ibexa\Core\IO\UrlDecorator\AbsolutePrefix; /** * Test case for IO Service. @@ -63,3 +63,5 @@ public function provideData(): array ]; } } + +class_alias(AbsolutePrefixTest::class, 'eZ\Publish\Core\IO\Tests\UrlDecorator\AbsolutePrefixTest'); diff --git a/eZ/Publish/Core/IO/Tests/UrlDecorator/PrefixTest.php b/tests/lib/IO/UrlDecorator/PrefixTest.php similarity index 87% rename from eZ/Publish/Core/IO/Tests/UrlDecorator/PrefixTest.php rename to tests/lib/IO/UrlDecorator/PrefixTest.php index 23eda652b8..0aaa633615 100644 --- a/eZ/Publish/Core/IO/Tests/UrlDecorator/PrefixTest.php +++ b/tests/lib/IO/UrlDecorator/PrefixTest.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\IO\Tests\UrlDecorator; +namespace Ibexa\Tests\Core\IO\UrlDecorator; -use eZ\Publish\Core\IO\IOConfigProvider; -use eZ\Publish\Core\IO\UrlDecorator; -use eZ\Publish\Core\IO\UrlDecorator\Prefix; +use Ibexa\Core\IO\IOConfigProvider; +use Ibexa\Core\IO\UrlDecorator; +use Ibexa\Core\IO\UrlDecorator\Prefix; use PHPUnit\Framework\TestCase; class PrefixTest extends TestCase @@ -72,3 +72,5 @@ public function provideData() ]; } } + +class_alias(PrefixTest::class, 'eZ\Publish\Core\IO\Tests\UrlDecorator\PrefixTest'); diff --git a/eZ/Publish/Core/IO/Tests/UrlRedecoratorTest.php b/tests/lib/IO/UrlRedecoratorTest.php similarity index 82% rename from eZ/Publish/Core/IO/Tests/UrlRedecoratorTest.php rename to tests/lib/IO/UrlRedecoratorTest.php index 25038d4e0d..fe5a278df0 100644 --- a/eZ/Publish/Core/IO/Tests/UrlRedecoratorTest.php +++ b/tests/lib/IO/UrlRedecoratorTest.php @@ -4,21 +4,21 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\IO\Tests; +namespace Ibexa\Tests\Core\IO; -use eZ\Publish\Core\IO\UrlDecorator; -use eZ\Publish\Core\IO\UrlRedecorator; +use Ibexa\Core\IO\UrlDecorator; +use Ibexa\Core\IO\UrlRedecorator; use PHPUnit\Framework\TestCase; class UrlRedecoratorTest extends TestCase { - /** @var \eZ\Publish\Core\IO\UrlRedecorator|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\IO\UrlRedecorator|\PHPUnit\Framework\MockObject\MockObject */ private $redecorator; - /** @var \eZ\Publish\Core\IO\UrlDecorator|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\IO\UrlDecorator|\PHPUnit\Framework\MockObject\MockObject */ private $sourceDecoratorMock; - /** @var \eZ\Publish\Core\IO\UrlDecorator|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\IO\UrlDecorator|\PHPUnit\Framework\MockObject\MockObject */ private $targetDecoratorMock; protected function setUp(): void @@ -69,3 +69,5 @@ public function testRedecorateFromTarget() ); } } + +class_alias(UrlRedecoratorTest::class, 'eZ\Publish\Core\IO\Tests\UrlRedecoratorTest'); diff --git a/tests/lib/Limitation/Base.php b/tests/lib/Limitation/Base.php new file mode 100644 index 0000000000..56c6a15434 --- /dev/null +++ b/tests/lib/Limitation/Base.php @@ -0,0 +1,69 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Limitation; + +use Ibexa\Contracts\Core\Persistence\Handler as SPIHandler; +use Ibexa\Contracts\Core\Repository\Values\User\User as APIUser; +use PHPUnit\Framework\TestCase; + +abstract class Base extends TestCase +{ + /** @var \Ibexa\Contracts\Core\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject */ + private $persistenceHandlerMock; + + /** @var \Ibexa\Contracts\Core\Repository\Values\User\User|\PHPUnit\Framework\MockObject\MockObject */ + private $userMock; + + /** + * @param array $mockMethods For specifying the methods to mock, all by default + * + * @return \Ibexa\Contracts\Core\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject + */ + public function getPersistenceMock(array $mockMethods = []) + { + if ($this->persistenceHandlerMock !== null) { + return $this->persistenceHandlerMock; + } + + return $this->persistenceHandlerMock = $this->createMock(SPIHandler::class); + } + + /** + * @param array $mockMethods For specifying the methods to mock, all by default + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User|\PHPUnit\Framework\MockObject\MockObject + */ + public function getUserMock(array $mockMethods = []) + { + if ($this->userMock !== null) { + return $this->userMock; + } + + return $this->userMock = $this->getMockBuilder(APIUser::class) + ->setConstructorArgs([]) + ->setMethods($mockMethods) + ->getMock(); + } + + /** + * unset properties. + */ + protected function tearDown(): void + { + if ($this->persistenceHandlerMock !== null) { + unset($this->persistenceHandlerMock); + } + + if ($this->userMock !== null) { + unset($this->userMock); + } + + parent::tearDown(); + } +} + +class_alias(Base::class, 'eZ\Publish\Core\Limitation\Tests\Base'); diff --git a/eZ/Publish/Core/Limitation/Tests/BlockingLimitationTypeTest.php b/tests/lib/Limitation/BlockingLimitationTypeTest.php similarity index 80% rename from eZ/Publish/Core/Limitation/Tests/BlockingLimitationTypeTest.php rename to tests/lib/Limitation/BlockingLimitationTypeTest.php index d97874640b..61d8343fe1 100644 --- a/eZ/Publish/Core/Limitation/Tests/BlockingLimitationTypeTest.php +++ b/tests/lib/Limitation/BlockingLimitationTypeTest.php @@ -4,17 +4,19 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Limitation\Tests; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchNone; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\BlockingLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Limitation\BlockingLimitationType; -use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\Location; +namespace Ibexa\Tests\Core\Limitation; + +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\MatchNone; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\BlockingLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Limitation\BlockingLimitationType; +use Ibexa\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Core\Repository\Values\Content\Location; /** * Test Case for LimitationType. @@ -22,7 +24,7 @@ class BlockingLimitationTypeTest extends Base { /** - * @return \eZ\Publish\Core\Limitation\BlockingLimitationType + * @return \Ibexa\Core\Limitation\BlockingLimitationType */ public function testConstruct() { @@ -44,8 +46,8 @@ public function providerForTestAcceptValue() * @dataProvider providerForTestAcceptValue * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\BlockingLimitation $limitation - * @param \eZ\Publish\Core\Limitation\BlockingLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\BlockingLimitation $limitation + * @param \Ibexa\Core\Limitation\BlockingLimitationType $limitationType */ public function testAcceptValue(BlockingLimitation $limitation, BlockingLimitationType $limitationType) { @@ -66,12 +68,12 @@ public function providerForTestAcceptValueException() * @dataProvider providerForTestAcceptValueException * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - * @param \eZ\Publish\Core\Limitation\BlockingLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + * @param \Ibexa\Core\Limitation\BlockingLimitationType $limitationType */ public function testAcceptValueException(Limitation $limitation, BlockingLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $limitationType->acceptValue($limitation); } @@ -90,7 +92,7 @@ public function providerForTestValidatePass() /** * @dataProvider providerForTestValidatePass * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\BlockingLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\BlockingLimitation $limitation */ public function testValidatePass(BlockingLimitation $limitation) { @@ -116,7 +118,7 @@ public function providerForTestValidateError() /** * @dataProvider providerForTestValidateError * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\BlockingLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\BlockingLimitation $limitation * @param int $errorCount */ public function testValidateError(BlockingLimitation $limitation, $errorCount) @@ -135,7 +137,7 @@ public function testValidateError(BlockingLimitation $limitation, $errorCount) /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\BlockingLimitationType $limitationType + * @param \Ibexa\Core\Limitation\BlockingLimitationType $limitationType */ public function testBuildValue(BlockingLimitationType $limitationType) { @@ -240,7 +242,7 @@ public function testEvaluateInvalidArgument( ValueObject $object, array $targets ) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); // Need to create inline instead of depending on testConstruct() to get correct mock instance $limitationType = $this->testConstruct(); @@ -267,7 +269,7 @@ public function testEvaluateInvalidArgument( /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\BlockingLimitationType $limitationType + * @param \Ibexa\Core\Limitation\BlockingLimitationType $limitationType */ public function testGetCriterion(BlockingLimitationType $limitationType) { @@ -284,11 +286,11 @@ public function testGetCriterion(BlockingLimitationType $limitationType) /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\BlockingLimitationType $limitationType + * @param \Ibexa\Core\Limitation\BlockingLimitationType $limitationType */ public function testValueSchema(BlockingLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotImplementedException::class); + $this->expectException(NotImplementedException::class); self::assertEquals( [], @@ -296,3 +298,5 @@ public function testValueSchema(BlockingLimitationType $limitationType) ); } } + +class_alias(BlockingLimitationTypeTest::class, 'eZ\Publish\Core\Limitation\Tests\BlockingLimitationTypeTest'); diff --git a/eZ/Publish/Core/Limitation/Tests/ContentTypeLimitationTypeTest.php b/tests/lib/Limitation/ContentTypeLimitationTypeTest.php similarity index 83% rename from eZ/Publish/Core/Limitation/Tests/ContentTypeLimitationTypeTest.php rename to tests/lib/Limitation/ContentTypeLimitationTypeTest.php index 4c6693a659..5d3666d391 100644 --- a/eZ/Publish/Core/Limitation/Tests/ContentTypeLimitationTypeTest.php +++ b/tests/lib/Limitation/ContentTypeLimitationTypeTest.php @@ -4,30 +4,32 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Limitation\Tests; - -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeId; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Limitation\ContentTypeLimitationType; -use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\SPI\Limitation\Target\Builder\VersionBuilder; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as SPIHandler; +namespace Ibexa\Tests\Core\Limitation; + +use Ibexa\Contracts\Core\Limitation\Target\Builder\VersionBuilder; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as SPIHandler; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ContentTypeId; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Limitation\ContentTypeLimitationType; +use Ibexa\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Core\Repository\Values\Content\Location; /** * Test Case for LimitationType. */ class ContentTypeLimitationTypeTest extends Base { - /** @var \eZ\Publish\SPI\Persistence\Content\Type\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Type\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $contentTypeHandlerMock; /** @@ -49,7 +51,7 @@ protected function tearDown(): void } /** - * @return \eZ\Publish\Core\Limitation\ContentTypeLimitationType + * @return \Ibexa\Core\Limitation\ContentTypeLimitationType */ public function testConstruct() { @@ -72,8 +74,8 @@ public function providerForTestAcceptValue() * @dataProvider providerForTestAcceptValue * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation $limitation - * @param \eZ\Publish\Core\Limitation\ContentTypeLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation $limitation + * @param \Ibexa\Core\Limitation\ContentTypeLimitationType $limitationType */ public function testAcceptValue(ContentTypeLimitation $limitation, ContentTypeLimitationType $limitationType) { @@ -95,12 +97,12 @@ public function providerForTestAcceptValueException() * @dataProvider providerForTestAcceptValueException * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - * @param \eZ\Publish\Core\Limitation\ContentTypeLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + * @param \Ibexa\Core\Limitation\ContentTypeLimitationType $limitationType */ public function testAcceptValueException(Limitation $limitation, ContentTypeLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $limitationType->acceptValue($limitation); } @@ -120,7 +122,7 @@ public function providerForTestValidatePass() /** * @dataProvider providerForTestValidatePass * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation $limitation */ public function testValidatePass(ContentTypeLimitation $limitation) { @@ -160,7 +162,7 @@ public function providerForTestValidateError() /** * @dataProvider providerForTestValidateError * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\ContentTypeLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ContentTypeLimitation $limitation * @param int $errorCount */ public function testValidateError(ContentTypeLimitation $limitation, $errorCount) @@ -194,7 +196,7 @@ public function testValidateError(ContentTypeLimitation $limitation, $errorCount /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\ContentTypeLimitationType $limitationType + * @param \Ibexa\Core\Limitation\ContentTypeLimitationType $limitationType */ public function testBuildValue(ContentTypeLimitationType $limitationType) { @@ -361,7 +363,7 @@ public function testEvaluateInvalidArgument( ValueObject $object, array $targets ) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); // Need to create inline instead of depending on testConstruct() to get correct mock instance $limitationType = $this->testConstruct(); @@ -388,7 +390,7 @@ public function testEvaluateInvalidArgument( /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\ContentTypeLimitationType $limitationType + * @param \Ibexa\Core\Limitation\ContentTypeLimitationType $limitationType */ public function testGetCriterionInvalidValue(ContentTypeLimitationType $limitationType) { @@ -403,7 +405,7 @@ public function testGetCriterionInvalidValue(ContentTypeLimitationType $limitati /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\ContentTypeLimitationType $limitationType + * @param \Ibexa\Core\Limitation\ContentTypeLimitationType $limitationType */ public function testGetCriterionSingleValue(ContentTypeLimitationType $limitationType) { @@ -422,7 +424,7 @@ public function testGetCriterionSingleValue(ContentTypeLimitationType $limitatio /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\ContentTypeLimitationType $limitationType + * @param \Ibexa\Core\Limitation\ContentTypeLimitationType $limitationType */ public function testGetCriterionMultipleValues(ContentTypeLimitationType $limitationType) { @@ -441,11 +443,11 @@ public function testGetCriterionMultipleValues(ContentTypeLimitationType $limita /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\ContentTypeLimitationType $limitationType + * @param \Ibexa\Core\Limitation\ContentTypeLimitationType $limitationType */ public function testValueSchema(ContentTypeLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotImplementedException::class); + $this->expectException(NotImplementedException::class); self::assertEquals( [], @@ -453,3 +455,5 @@ public function testValueSchema(ContentTypeLimitationType $limitationType) ); } } + +class_alias(ContentTypeLimitationTypeTest::class, 'eZ\Publish\Core\Limitation\Tests\ContentTypeLimitationTypeTest'); diff --git a/eZ/Publish/Core/Limitation/Tests/LanguageLimitation/ContentDeleteEvaluatorTest.php b/tests/lib/Limitation/LanguageLimitation/ContentDeleteEvaluatorTest.php similarity index 87% rename from eZ/Publish/Core/Limitation/Tests/LanguageLimitation/ContentDeleteEvaluatorTest.php rename to tests/lib/Limitation/LanguageLimitation/ContentDeleteEvaluatorTest.php index 4778381f90..4738d0dd8c 100644 --- a/eZ/Publish/Core/Limitation/Tests/LanguageLimitation/ContentDeleteEvaluatorTest.php +++ b/tests/lib/Limitation/LanguageLimitation/ContentDeleteEvaluatorTest.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Limitation\Tests\LanguageLimitation; +namespace Ibexa\Tests\Core\Limitation\LanguageLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\Core\Limitation\LanguageLimitation\ContentDeleteEvaluator; -use eZ\Publish\SPI\Limitation\Target; +use Ibexa\Contracts\Core\Limitation\Target; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Core\Limitation\LanguageLimitation\ContentDeleteEvaluator; use PHPUnit\Framework\TestCase; final class ContentDeleteEvaluatorTest extends TestCase @@ -92,3 +92,5 @@ private function getLanguageLimitation(array $languageCodes): Limitation\Languag return new Limitation\LanguageLimitation(['limitationValues' => $languageCodes]); } } + +class_alias(ContentDeleteEvaluatorTest::class, 'eZ\Publish\Core\Limitation\Tests\LanguageLimitation\ContentDeleteEvaluatorTest'); diff --git a/tests/lib/Limitation/LimitationIdentifierToLabelConverterTest.php b/tests/lib/Limitation/LimitationIdentifierToLabelConverterTest.php new file mode 100644 index 0000000000..40fb7807a5 --- /dev/null +++ b/tests/lib/Limitation/LimitationIdentifierToLabelConverterTest.php @@ -0,0 +1,22 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Limitation; + +use Ibexa\Core\Limitation\LimitationIdentifierToLabelConverter; +use PHPUnit\Framework\TestCase; + +final class LimitationIdentifierToLabelConverterTest extends TestCase +{ + public function testConvert(): void + { + $label = LimitationIdentifierToLabelConverter::convert('Some_Identifier'); + + self::assertSame('policy.limitation.identifier.some_identifier', $label); + } +} diff --git a/eZ/Publish/Core/Limitation/Tests/LocationLimitationTypeTest.php b/tests/lib/Limitation/LocationLimitationTypeTest.php similarity index 86% rename from eZ/Publish/Core/Limitation/Tests/LocationLimitationTypeTest.php rename to tests/lib/Limitation/LocationLimitationTypeTest.php index 2293a06f35..0f130c4727 100644 --- a/eZ/Publish/Core/Limitation/Tests/LocationLimitationTypeTest.php +++ b/tests/lib/Limitation/LocationLimitationTypeTest.php @@ -4,30 +4,31 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Limitation\Tests; - -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LocationId; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Limitation\LocationLimitationType; -use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as SPIHandler; +namespace Ibexa\Tests\Core\Limitation; + +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as SPIHandler; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LocationId; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\LocationLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Limitation\LocationLimitationType; +use Ibexa\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Core\Repository\Values\Content\Location; /** * Test Case for LimitationType. */ class LocationLimitationTypeTest extends Base { - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Location\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $locationHandlerMock; /** @@ -49,7 +50,7 @@ protected function tearDown(): void } /** - * @return \eZ\Publish\Core\Limitation\LocationLimitationType + * @return \Ibexa\Core\Limitation\LocationLimitationType */ public function testConstruct() { @@ -72,8 +73,8 @@ public function providerForTestAcceptValue() * @dataProvider providerForTestAcceptValue * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation $limitation - * @param \eZ\Publish\Core\Limitation\LocationLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\LocationLimitation $limitation + * @param \Ibexa\Core\Limitation\LocationLimitationType $limitationType */ public function testAcceptValue(LocationLimitation $limitation, LocationLimitationType $limitationType) { @@ -95,12 +96,12 @@ public function providerForTestAcceptValueException() * @dataProvider providerForTestAcceptValueException * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - * @param \eZ\Publish\Core\Limitation\LocationLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + * @param \Ibexa\Core\Limitation\LocationLimitationType $limitationType */ public function testAcceptValueException(Limitation $limitation, LocationLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $limitationType->acceptValue($limitation); } @@ -120,7 +121,7 @@ public function providerForTestValidatePass() /** * @dataProvider providerForTestValidatePass * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\LocationLimitation $limitation */ public function testValidatePass(LocationLimitation $limitation) { @@ -160,7 +161,7 @@ public function providerForTestValidateError() /** * @dataProvider providerForTestValidateError * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\LocationLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\LocationLimitation $limitation * @param int $errorCount */ public function testValidateError(LocationLimitation $limitation, $errorCount) @@ -194,7 +195,7 @@ public function testValidateError(LocationLimitation $limitation, $errorCount) /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\LocationLimitationType $limitationType + * @param \Ibexa\Core\Limitation\LocationLimitationType $limitationType */ public function testBuildValue(LocationLimitationType $limitationType) { @@ -425,7 +426,7 @@ public function testEvaluateInvalidArgument( $targets, array $persistenceLocations ) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); // Need to create inline instead of depending on testConstruct() to get correct mock instance $limitationType = $this->testConstruct(); @@ -452,7 +453,7 @@ public function testEvaluateInvalidArgument( /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\LocationLimitationType $limitationType + * @param \Ibexa\Core\Limitation\LocationLimitationType $limitationType */ public function testGetCriterionInvalidValue(LocationLimitationType $limitationType) { @@ -467,7 +468,7 @@ public function testGetCriterionInvalidValue(LocationLimitationType $limitationT /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\LocationLimitationType $limitationType + * @param \Ibexa\Core\Limitation\LocationLimitationType $limitationType */ public function testGetCriterionSingleValue(LocationLimitationType $limitationType) { @@ -486,7 +487,7 @@ public function testGetCriterionSingleValue(LocationLimitationType $limitationTy /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\LocationLimitationType $limitationType + * @param \Ibexa\Core\Limitation\LocationLimitationType $limitationType */ public function testGetCriterionMultipleValues(LocationLimitationType $limitationType) { @@ -505,7 +506,7 @@ public function testGetCriterionMultipleValues(LocationLimitationType $limitatio /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\LocationLimitationType $limitationType + * @param \Ibexa\Core\Limitation\LocationLimitationType $limitationType */ public function testValueSchema(LocationLimitationType $limitationType) { @@ -515,3 +516,5 @@ public function testValueSchema(LocationLimitationType $limitationType) ); } } + +class_alias(LocationLimitationTypeTest::class, 'eZ\Publish\Core\Limitation\Tests\LocationLimitationTypeTest'); diff --git a/tests/lib/Limitation/MemberOfLimitationTypeTest.php b/tests/lib/Limitation/MemberOfLimitationTypeTest.php index 98929efd8c..eaad7bbf5b 100644 --- a/tests/lib/Limitation/MemberOfLimitationTypeTest.php +++ b/tests/lib/Limitation/MemberOfLimitationTypeTest.php @@ -8,28 +8,26 @@ namespace Ibexa\Tests\Core\Limitation; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Limitation\Tests\Base; -use eZ\Publish\Core\Persistence\Legacy\Content\Handler as ContentHandlerInterface; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\User\Role; -use eZ\Publish\Core\Repository\Values\User\User; -use eZ\Publish\Core\Repository\Values\User\UserGroup; -use eZ\Publish\Core\Repository\Values\User\UserGroupRoleAssignment; -use eZ\Publish\Core\Repository\Values\User\UserRoleAssignment; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as LocationHandlerInterface; +use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandlerInterface; +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandlerInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; use Ibexa\Contracts\Core\Repository\Values\User\Limitation\MemberOfLimitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\Base\Exceptions\NotFoundException; use Ibexa\Core\Limitation\MemberOfLimitationType; - -class MemberOfLimitationTypeTest extends Base +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\User\Role; +use Ibexa\Core\Repository\Values\User\User; +use Ibexa\Core\Repository\Values\User\UserGroup; +use Ibexa\Core\Repository\Values\User\UserGroupRoleAssignment; +use Ibexa\Core\Repository\Values\User\UserRoleAssignment; + +final class MemberOfLimitationTypeTest extends Base { - /** @var \Ibexa\Core\Limitation\MemberOfLimitationType */ - private $limitationType; + private MemberOfLimitationType $limitationType; protected function setUp(): void { @@ -145,7 +143,7 @@ public function testValidateError(MemberOfLimitation $limitation, int $errorCoun self::assertCount($errorCount, $validationErrors); } - public function providerForTestValidateError() + public function providerForTestValidateError(): array { return [ [ @@ -201,7 +199,7 @@ public function testEvaluate( self::assertEquals($expected, $value); } - public function providerForTestEvaluate() + public function providerForTestEvaluate(): array { return [ 'valid_group_limitation' => [ @@ -350,7 +348,7 @@ public function providerForTestEvaluate() } /** - * @param \eZ\Publish\API\Repository\Values\User\User|\eZ\Publish\API\Repository\Values\User\UserRoleAssignment $object + * @param \Ibexa\Core\Repository\Values\User\User|\Ibexa\Core\Repository\Values\User\UserRoleAssignment $object * @dataProvider providerForTestEvaluateSelfGroup */ public function testEvaluateSelfGroup( diff --git a/eZ/Publish/Core/Limitation/Tests/NewObjectStateLimitationTypeTest.php b/tests/lib/Limitation/NewObjectStateLimitationTypeTest.php similarity index 81% rename from eZ/Publish/Core/Limitation/Tests/NewObjectStateLimitationTypeTest.php rename to tests/lib/Limitation/NewObjectStateLimitationTypeTest.php index 236f8aa374..616646f92d 100644 --- a/eZ/Publish/Core/Limitation/Tests/NewObjectStateLimitationTypeTest.php +++ b/tests/lib/Limitation/NewObjectStateLimitationTypeTest.php @@ -4,27 +4,29 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Limitation\Tests; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\NewObjectStateLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Limitation\NewObjectStateLimitationType; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\ObjectState\ObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Handler as SPIHandler; +namespace Ibexa\Tests\Core\Limitation; + +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\Handler as SPIHandler; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\NewObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Limitation\NewObjectStateLimitationType; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\ObjectState\ObjectState; /** * Test Case for LimitationType. */ class NewObjectStateLimitationTypeTest extends Base { - /** @var \eZ\Publish\SPI\Persistence\Content\ObjectState\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $objectStateHandlerMock; /** @@ -46,7 +48,7 @@ protected function tearDown(): void } /** - * @return \eZ\Publish\Core\Limitation\NewObjectStateLimitationType + * @return \Ibexa\Core\Limitation\NewObjectStateLimitationType */ public function testConstruct() { @@ -69,8 +71,8 @@ public function providerForTestAcceptValue() * @dataProvider providerForTestAcceptValue * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\NewObjectStateLimitation $limitation - * @param \eZ\Publish\Core\Limitation\NewObjectStateLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\NewObjectStateLimitation $limitation + * @param \Ibexa\Core\Limitation\NewObjectStateLimitationType $limitationType */ public function testAcceptValue(NewObjectStateLimitation $limitation, NewObjectStateLimitationType $limitationType) { @@ -92,12 +94,12 @@ public function providerForTestAcceptValueException() * @dataProvider providerForTestAcceptValueException * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - * @param \eZ\Publish\Core\Limitation\NewObjectStateLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + * @param \Ibexa\Core\Limitation\NewObjectStateLimitationType $limitationType */ public function testAcceptValueException(Limitation $limitation, NewObjectStateLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $limitationType->acceptValue($limitation); } @@ -117,7 +119,7 @@ public function providerForTestValidatePass() /** * @dataProvider providerForTestValidatePass * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\NewObjectStateLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\NewObjectStateLimitation $limitation */ public function testValidatePass(NewObjectStateLimitation $limitation) { @@ -157,7 +159,7 @@ public function providerForTestValidateError() /** * @dataProvider providerForTestValidateError * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\NewObjectStateLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\NewObjectStateLimitation $limitation * @param int $errorCount */ public function testValidateError(NewObjectStateLimitation $limitation, $errorCount) @@ -191,7 +193,7 @@ public function testValidateError(NewObjectStateLimitation $limitation, $errorCo /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\NewObjectStateLimitationType $limitationType + * @param \Ibexa\Core\Limitation\NewObjectStateLimitationType $limitationType */ public function testBuildValue(NewObjectStateLimitationType $limitationType) { @@ -322,7 +324,7 @@ public function testEvaluateInvalidArgument( ValueObject $object, array $targets ) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); // Need to create inline instead of depending on testConstruct() to get correct mock instance $limitationType = $this->testConstruct(); @@ -349,11 +351,11 @@ public function testEvaluateInvalidArgument( /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\NewObjectStateLimitationType $limitationType + * @param \Ibexa\Core\Limitation\NewObjectStateLimitationType $limitationType */ public function testGetCriterion(NewObjectStateLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotImplementedException::class); + $this->expectException(NotImplementedException::class); $limitationType->getCriterion( new NewObjectStateLimitation([]), @@ -364,11 +366,11 @@ public function testGetCriterion(NewObjectStateLimitationType $limitationType) /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\NewObjectStateLimitationType $limitationType + * @param \Ibexa\Core\Limitation\NewObjectStateLimitationType $limitationType */ public function testValueSchema(NewObjectStateLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotImplementedException::class); + $this->expectException(NotImplementedException::class); self::assertEquals( [], @@ -376,3 +378,5 @@ public function testValueSchema(NewObjectStateLimitationType $limitationType) ); } } + +class_alias(NewObjectStateLimitationTypeTest::class, 'eZ\Publish\Core\Limitation\Tests\NewObjectStateLimitationTypeTest'); diff --git a/eZ/Publish/Core/Limitation/Tests/ObjectStateLimitationTypeTest.php b/tests/lib/Limitation/ObjectStateLimitationTypeTest.php similarity index 88% rename from eZ/Publish/Core/Limitation/Tests/ObjectStateLimitationTypeTest.php rename to tests/lib/Limitation/ObjectStateLimitationTypeTest.php index 78b9d5a745..3b1273ed90 100644 --- a/eZ/Publish/Core/Limitation/Tests/ObjectStateLimitationTypeTest.php +++ b/tests/lib/Limitation/ObjectStateLimitationTypeTest.php @@ -4,29 +4,29 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Limitation\Tests; - -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ObjectStateId; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Limitation\ObjectStateLimitationType; -use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\SPI\Persistence\Content\ObjectState; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Group; -use eZ\Publish\SPI\Persistence\Content\ObjectState\Handler as SPIHandler; +namespace Ibexa\Tests\Core\Limitation; + +use Ibexa\Contracts\Core\Persistence\Content\ObjectState; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\Handler as SPIHandler; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalAnd; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ObjectStateId; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Limitation\ObjectStateLimitationType; +use Ibexa\Core\Repository\Values\Content\ContentCreateStruct; /** * Test Case for LimitationType. */ class ObjectStateLimitationTypeTest extends Base { - /** @var \eZ\Publish\SPI\Persistence\Content\ObjectState\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $objectStateHandlerMock; - /** @var \eZ\Publish\SPI\Persistence\Content\ObjectState\Group[] */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group[] */ private $allObjectStateGroups; /** @var array */ @@ -75,7 +75,7 @@ protected function tearDown(): void } /** - * @return \eZ\Publish\Core\Limitation\ObjectStateLimitationType + * @return \Ibexa\Core\Limitation\ObjectStateLimitationType */ public function testConstruct() { @@ -209,7 +209,7 @@ public function testEvaluate( /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\ObjectStateLimitationType $limitationType + * @param \Ibexa\Core\Limitation\ObjectStateLimitationType $limitationType */ public function testGetCriterionInvalidValue(ObjectStateLimitationType $limitationType) { @@ -224,7 +224,7 @@ public function testGetCriterionInvalidValue(ObjectStateLimitationType $limitati /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\ObjectStateLimitationType $limitationType + * @param \Ibexa\Core\Limitation\ObjectStateLimitationType $limitationType */ public function testGetCriterionSingleValue(ObjectStateLimitationType $limitationType) { @@ -305,3 +305,5 @@ public function testGetCriterionMultipleValuesFromMultipleGroups() self::assertEquals([3], $criterion->criteria[1]->value); } } + +class_alias(ObjectStateLimitationTypeTest::class, 'eZ\Publish\Core\Limitation\Tests\ObjectStateLimitationTypeTest'); diff --git a/eZ/Publish/Core/Limitation/Tests/ParentContentTypeLimitationTypeTest.php b/tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php similarity index 89% rename from eZ/Publish/Core/Limitation/Tests/ParentContentTypeLimitationTypeTest.php rename to tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php index b4f400b41f..f58c3ebfba 100644 --- a/eZ/Publish/Core/Limitation/Tests/ParentContentTypeLimitationTypeTest.php +++ b/tests/lib/Limitation/ParentContentTypeLimitationTypeTest.php @@ -4,37 +4,39 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Limitation\Tests; - -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ParentContentTypeLimitation; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Limitation\ParentContentTypeLimitationType; -use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\SPI\Persistence\Content\ContentInfo as SPIContentInfo; -use eZ\Publish\SPI\Persistence\Content\Handler as SPIContentHandler; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as SPIContentTypeHandler; +namespace Ibexa\Tests\Core\Limitation; + +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo as SPIContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\Handler as SPIContentHandler; +use Ibexa\Contracts\Core\Persistence\Content\Location as SPILocation; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as SPIContentTypeHandler; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentContentTypeLimitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Limitation\ParentContentTypeLimitationType; +use Ibexa\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Core\Repository\Values\Content\Location; /** * Test Case for LimitationType. */ class ParentContentTypeLimitationTypeTest extends Base { - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Location\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $locationHandlerMock; - /** @var \eZ\Publish\SPI\Persistence\Content\Type\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Type\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $contentTypeHandlerMock; - /** @var \eZ\Publish\SPI\Persistence\Content\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $contentHandlerMock; /** @@ -60,7 +62,7 @@ protected function tearDown(): void } /** - * @return \eZ\Publish\Core\Limitation\ParentContentTypeLimitationType + * @return \Ibexa\Core\Limitation\ParentContentTypeLimitationType */ public function testConstruct() { @@ -83,8 +85,8 @@ public function providerForTestAcceptValue() * @dataProvider providerForTestAcceptValue * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\ParentContentTypeLimitation $limitation - * @param \eZ\Publish\Core\Limitation\ParentContentTypeLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentContentTypeLimitation $limitation + * @param \Ibexa\Core\Limitation\ParentContentTypeLimitationType $limitationType */ public function testAcceptValue(ParentContentTypeLimitation $limitation, ParentContentTypeLimitationType $limitationType) { @@ -107,12 +109,12 @@ public function providerForTestAcceptValueException() * @dataProvider providerForTestAcceptValueException * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - * @param \eZ\Publish\Core\Limitation\ParentContentTypeLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + * @param \Ibexa\Core\Limitation\ParentContentTypeLimitationType $limitationType */ public function testAcceptValueException(Limitation $limitation, ParentContentTypeLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $limitationType->acceptValue($limitation); } @@ -132,7 +134,7 @@ public function providerForTestValidatePass() /** * @dataProvider providerForTestValidatePass * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\ParentContentTypeLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentContentTypeLimitation $limitation */ public function testValidatePass(ParentContentTypeLimitation $limitation) { @@ -173,7 +175,7 @@ public function providerForTestValidateError() /** * @dataProvider providerForTestValidateError * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\ParentContentTypeLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentContentTypeLimitation $limitation * @param int $errorCount */ public function testValidateError(ParentContentTypeLimitation $limitation, $errorCount) @@ -207,7 +209,7 @@ public function testValidateError(ParentContentTypeLimitation $limitation, $erro /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\ParentContentTypeLimitationType $limitationType + * @param \Ibexa\Core\Limitation\ParentContentTypeLimitationType $limitationType */ public function testBuildValue(ParentContentTypeLimitationType $limitationType) { @@ -606,7 +608,7 @@ public function providerForTestEvaluateInvalidArgument() */ public function testEvaluateInvalidArgument(Limitation $limitation, ValueObject $object, $targets) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); // Need to create inline instead of depending on testConstruct() to get correct mock instance $limitationType = $this->testConstruct(); @@ -632,11 +634,11 @@ public function testEvaluateInvalidArgument(Limitation $limitation, ValueObject /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\ParentContentTypeLimitationType $limitationType + * @param \Ibexa\Core\Limitation\ParentContentTypeLimitationType $limitationType */ public function testGetCriterionInvalidValue(ParentContentTypeLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotImplementedException::class); + $this->expectException(NotImplementedException::class); $limitationType->getCriterion( new ParentContentTypeLimitation([]), @@ -647,10 +649,12 @@ public function testGetCriterionInvalidValue(ParentContentTypeLimitationType $li /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\ParentContentTypeLimitationType $limitationType + * @param \Ibexa\Core\Limitation\ParentContentTypeLimitationType $limitationType */ public function testValueSchema(ParentContentTypeLimitationType $limitationType) { $this->markTestIncomplete('Method is not implemented yet: ' . __METHOD__); } } + +class_alias(ParentContentTypeLimitationTypeTest::class, 'eZ\Publish\Core\Limitation\Tests\ParentContentTypeLimitationTypeTest'); diff --git a/eZ/Publish/Core/Limitation/Tests/ParentDepthLimitationTypeTest.php b/tests/lib/Limitation/ParentDepthLimitationTypeTest.php similarity index 87% rename from eZ/Publish/Core/Limitation/Tests/ParentDepthLimitationTypeTest.php rename to tests/lib/Limitation/ParentDepthLimitationTypeTest.php index 836689e83f..6dc9111334 100644 --- a/eZ/Publish/Core/Limitation/Tests/ParentDepthLimitationTypeTest.php +++ b/tests/lib/Limitation/ParentDepthLimitationTypeTest.php @@ -4,27 +4,28 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Limitation\Tests; - -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ParentDepthLimitation; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Limitation\ParentDepthLimitationType; -use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as SPILocationHandler; +namespace Ibexa\Tests\Core\Limitation; + +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as SPILocationHandler; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentDepthLimitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Limitation\ParentDepthLimitationType; +use Ibexa\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Core\Repository\Values\Content\Location; /** * Test Case for LimitationType. */ class ParentDepthLimitationTypeTest extends Base { - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Location\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $locationHandlerMock; /** @@ -46,7 +47,7 @@ protected function tearDown(): void } /** - * @return \eZ\Publish\Core\Limitation\ParentDepthLimitationType + * @return \Ibexa\Core\Limitation\ParentDepthLimitationType */ public function testConstruct() { @@ -69,8 +70,8 @@ public function providerForTestAcceptValue() * @dataProvider providerForTestAcceptValue * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\ParentDepthLimitation $limitation - * @param \eZ\Publish\Core\Limitation\ParentDepthLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentDepthLimitation $limitation + * @param \Ibexa\Core\Limitation\ParentDepthLimitationType $limitationType */ public function testAcceptValue(ParentDepthLimitation $limitation, ParentDepthLimitationType $limitationType) { @@ -92,12 +93,12 @@ public function providerForTestAcceptValueException() * @dataProvider providerForTestAcceptValueException * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - * @param \eZ\Publish\Core\Limitation\ParentDepthLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + * @param \Ibexa\Core\Limitation\ParentDepthLimitationType $limitationType */ public function testAcceptValueException(Limitation $limitation, ParentDepthLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $limitationType->acceptValue($limitation); } @@ -118,7 +119,7 @@ public function providerForTestValidatePass() * @dataProvider providerForTestValidatePass * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\ParentDepthLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\ParentDepthLimitation $limitation */ public function testValidatePass(ParentDepthLimitation $limitation, ParentDepthLimitationType $limitationType) { @@ -129,7 +130,7 @@ public function testValidatePass(ParentDepthLimitation $limitation, ParentDepthL /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\ParentDepthLimitationType $limitationType + * @param \Ibexa\Core\Limitation\ParentDepthLimitationType $limitationType */ public function testBuildValue(ParentDepthLimitationType $limitationType) { @@ -384,7 +385,7 @@ public function testEvaluateInvalidArgument( $targets, array $persistenceLocations ) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); // Need to create inline instead of depending on testConstruct() to get correct mock instance $limitationType = $this->testConstruct(); @@ -408,3 +409,5 @@ public function testEvaluateInvalidArgument( var_dump($v); // intentional, debug in case no exception above } } + +class_alias(ParentDepthLimitationTypeTest::class, 'eZ\Publish\Core\Limitation\Tests\ParentDepthLimitationTypeTest'); diff --git a/tests/lib/Limitation/RoleLimitationTypeTest.php b/tests/lib/Limitation/RoleLimitationTypeTest.php index 9f0dd38898..1ee992769f 100644 --- a/tests/lib/Limitation/RoleLimitationTypeTest.php +++ b/tests/lib/Limitation/RoleLimitationTypeTest.php @@ -8,29 +8,27 @@ namespace Ibexa\Tests\Core\Limitation; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Limitation\Tests\Base; -use eZ\Publish\Core\Persistence\Legacy\Content\Handler as ContentHandlerInterface; -use eZ\Publish\Core\Persistence\Legacy\User\Handler as UserHandlerInterface; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\User\Role; -use eZ\Publish\Core\Repository\Values\User\User; -use eZ\Publish\Core\Repository\Values\User\UserGroup; -use eZ\Publish\Core\Repository\Values\User\UserGroupRoleAssignment; -use eZ\Publish\Core\Repository\Values\User\UserRoleAssignment; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as LocationHandlerInterface; -use Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation; +use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandlerInterface; +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandlerInterface; +use Ibexa\Contracts\Core\Persistence\User\Handler as UserHandlerInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\UserRoleLimitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\Base\Exceptions\NotFoundException; use Ibexa\Core\Limitation\RoleLimitationType; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\User\Role; +use Ibexa\Core\Repository\Values\User\User; +use Ibexa\Core\Repository\Values\User\UserGroup; +use Ibexa\Core\Repository\Values\User\UserGroupRoleAssignment; +use Ibexa\Core\Repository\Values\User\UserRoleAssignment; -class RoleLimitationTypeTest extends Base +final class RoleLimitationTypeTest extends Base { - /** @var \Ibexa\Core\Limitation\RoleLimitationType */ - private $limitationType; + private RoleLimitationType $limitationType; protected function setUp(): void { @@ -44,7 +42,7 @@ protected function setUp(): void /** * @dataProvider providerForTestAcceptValue */ - public function testAcceptValue(RoleLimitation $limitation): void + public function testAcceptValue(UserRoleLimitation $limitation): void { $this->expectNotToPerformAssertions(); $this->limitationType->acceptValue($limitation); @@ -54,12 +52,12 @@ public function providerForTestAcceptValue(): array { return [ [ - new RoleLimitation([ + new UserRoleLimitation([ 'limitationValues' => [], ]), ], [ - new RoleLimitation([ + new UserRoleLimitation([ 'limitationValues' => [4, 8], ]), ], @@ -69,7 +67,7 @@ public function providerForTestAcceptValue(): array /** * @dataProvider providerForTestAcceptValueException */ - public function testAcceptValueException(RoleLimitation $limitation): void + public function testAcceptValueException(UserRoleLimitation $limitation): void { $this->expectException(InvalidArgumentType::class); $this->limitationType->acceptValue($limitation); @@ -79,22 +77,22 @@ public function providerForTestAcceptValueException(): array { return [ [ - new RoleLimitation([ + new UserRoleLimitation([ 'limitationValues' => 1, ]), ], [ - new RoleLimitation([ + new UserRoleLimitation([ 'limitationValues' => null, ]), ], [ - new RoleLimitation([ + new UserRoleLimitation([ 'limitationValues' => 'string', ]), ], [ - new RoleLimitation([ + new UserRoleLimitation([ 'limitationValues' => ['string'], ]), ], @@ -104,7 +102,7 @@ public function providerForTestAcceptValueException(): array /** * @dataProvider providerForTestAcceptValue */ - public function testValidatePass(RoleLimitation $limitation): void + public function testValidatePass(UserRoleLimitation $limitation): void { $userHandlerMock = $this->createMock(UserHandlerInterface::class); $contentHandlerMock = $this->createMock(ContentHandlerInterface::class); @@ -137,7 +135,7 @@ public function testValidatePass(RoleLimitation $limitation): void /** * @dataProvider providerForTestValidateError */ - public function testValidateError(RoleLimitation $limitation, int $errorCount): void + public function testValidateError(UserRoleLimitation $limitation, int $errorCount): void { $userHandlerMock = $this->createMock(UserHandlerInterface::class); @@ -163,7 +161,7 @@ public function providerForTestValidateError() { return [ [ - new RoleLimitation([ + new UserRoleLimitation([ 'limitationValues' => [4, 8], ]), 1, @@ -175,7 +173,7 @@ public function providerForTestValidateError() * @dataProvider providerForTestEvaluate */ public function testEvaluate( - RoleLimitation $limitation, + UserRoleLimitation $limitation, ValueObject $object, ?array $targets, ?bool $expected @@ -210,7 +208,7 @@ public function providerForTestEvaluate() { return [ 'valid_role_limitation' => [ - 'limitation' => new RoleLimitation([ + 'limitation' => new UserRoleLimitation([ 'limitationValues' => [4, 8], ]), 'object' => new Role(['id' => 4]), @@ -218,7 +216,7 @@ public function providerForTestEvaluate() 'expected' => true, ], 'allow_non_role_limitation' => [ - 'limitation' => new RoleLimitation([ + 'limitation' => new UserRoleLimitation([ 'limitationValues' => [], ]), 'object' => new Role(['id' => 4]), @@ -226,7 +224,7 @@ public function providerForTestEvaluate() 'expected' => false, ], 'pass_to_next_limitation' => [ - 'limitation' => new RoleLimitation([ + 'limitation' => new UserRoleLimitation([ 'limitationValues' => [4, 8], ]), 'object' => new VersionInfo([ @@ -238,7 +236,7 @@ public function providerForTestEvaluate() 'expected' => null, ], 'user_role_assigment_valid' => [ - 'limitation' => new RoleLimitation([ + 'limitation' => new UserRoleLimitation([ 'limitationValues' => [4, 8], ]), 'object' => new UserRoleAssignment([ @@ -257,7 +255,7 @@ public function providerForTestEvaluate() 'expected' => true, ], 'user_role_assigment_invalid_role' => [ - 'limitation' => new RoleLimitation([ + 'limitation' => new UserRoleLimitation([ 'limitationValues' => [4, 8], ]), 'object' => new UserRoleAssignment([ @@ -276,7 +274,7 @@ public function providerForTestEvaluate() 'expected' => false, ], 'user_group_role_assigment_valid' => [ - 'limitation' => new RoleLimitation([ + 'limitation' => new UserRoleLimitation([ 'limitationValues' => [4, 8], ]), 'object' => new UserGroupRoleAssignment([ @@ -295,7 +293,7 @@ public function providerForTestEvaluate() 'expected' => true, ], 'user_group_role_assigment_invalid_role' => [ - 'limitation' => new RoleLimitation([ + 'limitation' => new UserRoleLimitation([ 'limitationValues' => [4, 8], ]), 'object' => new UserGroupRoleAssignment([ diff --git a/eZ/Publish/Core/Limitation/Tests/SectionLimitationTypeTest.php b/tests/lib/Limitation/SectionLimitationTypeTest.php similarity index 84% rename from eZ/Publish/Core/Limitation/Tests/SectionLimitationTypeTest.php rename to tests/lib/Limitation/SectionLimitationTypeTest.php index d2ea392a26..ef0967c57d 100644 --- a/eZ/Publish/Core/Limitation/Tests/SectionLimitationTypeTest.php +++ b/tests/lib/Limitation/SectionLimitationTypeTest.php @@ -4,32 +4,34 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Limitation\Tests; - -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\SectionId; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Limitation\SectionLimitationType; -use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\SPI\Limitation\Type as LimitationType; -use eZ\Publish\SPI\Persistence\Content\Section as SPISection; -use eZ\Publish\SPI\Persistence\Content\Section\Handler as SPISectionHandler; +namespace Ibexa\Tests\Core\Limitation; + +use Ibexa\Contracts\Core\Limitation\Type as LimitationType; +use Ibexa\Contracts\Core\Persistence\Content\Section as SPISection; +use Ibexa\Contracts\Core\Persistence\Content\Section\Handler as SPISectionHandler; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\SectionId; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Limitation\SectionLimitationType; +use Ibexa\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Core\Repository\Values\Content\Location; /** * Test Case for LimitationType. */ class SectionLimitationTypeTest extends Base { - /** @var \eZ\Publish\SPI\Persistence\Content\Section\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Section\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $sectionHandlerMock; /** @@ -51,7 +53,7 @@ protected function tearDown(): void } /** - * @return \eZ\Publish\Core\Limitation\SectionLimitationType + * @return \Ibexa\Core\Limitation\SectionLimitationType */ public function testConstruct() { @@ -74,8 +76,8 @@ public function providerForTestAcceptValue() * @dataProvider providerForTestAcceptValue * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation $limitation - * @param \eZ\Publish\Core\Limitation\SectionLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation $limitation + * @param \Ibexa\Core\Limitation\SectionLimitationType $limitationType */ public function testAcceptValue(SectionLimitation $limitation, SectionLimitationType $limitationType) { @@ -100,12 +102,12 @@ public function providerForTestAcceptValueException() * @dataProvider providerForTestAcceptValueException * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - * @param \eZ\Publish\Core\Limitation\SectionLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + * @param \Ibexa\Core\Limitation\SectionLimitationType $limitationType */ public function testAcceptValueException(Limitation $limitation, SectionLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $limitationType->acceptValue($limitation); } @@ -125,7 +127,7 @@ public function providerForTestValidatePass() /** * @dataProvider providerForTestValidatePass * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation $limitation */ public function testValidatePass(SectionLimitation $limitation) { @@ -170,7 +172,7 @@ public function providerForTestValidateError() /** * @dataProvider providerForTestValidateError * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\SectionLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SectionLimitation $limitation * @param int $errorCount */ public function testValidateError(SectionLimitation $limitation, $errorCount) @@ -204,7 +206,7 @@ public function testValidateError(SectionLimitation $limitation, $errorCount) /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\SectionLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SectionLimitationType $limitationType */ public function testBuildValue(SectionLimitationType $limitationType) { @@ -398,7 +400,7 @@ public function testEvaluateInvalidArgument( ValueObject $object, $targets ) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); // Need to create inline instead of depending on testConstruct() to get correct mock instance $limitationType = $this->testConstruct(); @@ -424,7 +426,7 @@ public function testEvaluateInvalidArgument( /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\SectionLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SectionLimitationType $limitationType */ public function testGetCriterionInvalidValue(SectionLimitationType $limitationType) { @@ -439,7 +441,7 @@ public function testGetCriterionInvalidValue(SectionLimitationType $limitationTy /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\SectionLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SectionLimitationType $limitationType */ public function testGetCriterionSingleValue(SectionLimitationType $limitationType) { @@ -458,7 +460,7 @@ public function testGetCriterionSingleValue(SectionLimitationType $limitationTyp /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\SectionLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SectionLimitationType $limitationType */ public function testGetCriterionMultipleValues(SectionLimitationType $limitationType) { @@ -477,12 +479,14 @@ public function testGetCriterionMultipleValues(SectionLimitationType $limitation /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\SectionLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SectionLimitationType $limitationType */ public function testValueSchema(SectionLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotImplementedException::class); + $this->expectException(NotImplementedException::class); $limitationType->valueSchema(); } } + +class_alias(SectionLimitationTypeTest::class, 'eZ\Publish\Core\Limitation\Tests\SectionLimitationTypeTest'); diff --git a/eZ/Publish/Core/Limitation/Tests/SiteAccessLimitationTypeTest.php b/tests/lib/Limitation/SiteAccessLimitationTypeTest.php similarity index 78% rename from eZ/Publish/Core/Limitation/Tests/SiteAccessLimitationTypeTest.php rename to tests/lib/Limitation/SiteAccessLimitationTypeTest.php index ae1b95fa36..c6d65fb3a8 100644 --- a/eZ/Publish/Core/Limitation/Tests/SiteAccessLimitationTypeTest.php +++ b/tests/lib/Limitation/SiteAccessLimitationTypeTest.php @@ -4,21 +4,23 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Limitation\Tests; +namespace Ibexa\Tests\Core\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\SiteAccessLimitation; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Limitation\SiteAccessLimitationType; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SiteAccessLimitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Limitation\SiteAccessLimitationType; +use Ibexa\Core\MVC\Symfony\SiteAccess; /** * Test Case for LimitationType. */ class SiteAccessLimitationTypeTest extends Base { - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ private $siteAccessServiceMock; protected function setUp(): void @@ -28,14 +30,14 @@ protected function setUp(): void $this->siteAccessServiceMock ->method('getAll') ->willReturn([ - new SiteAccess('ezdemo_site'), + new SiteAccess('ibexa_demo_site'), new SiteAccess('eng'), new SiteAccess('fre'), ]); } /** - * @return \eZ\Publish\Core\Limitation\SiteAccessLimitationType + * @return \Ibexa\Core\Limitation\SiteAccessLimitationType */ public function testConstruct() { @@ -56,7 +58,7 @@ public function providerForTestAcceptValue() new SiteAccessLimitation( [ 'limitationValues' => [ - sprintf('%u', crc32('ezdemo_site')), + sprintf('%u', crc32('ibexa_demo_site')), sprintf('%u', crc32('eng')), sprintf('%u', crc32('fre')), ], @@ -67,7 +69,7 @@ public function providerForTestAcceptValue() new SiteAccessLimitation( [ 'limitationValues' => [ - crc32('ezdemo_site'), + crc32('ibexa_demo_site'), crc32('eng'), crc32('fre'), ], @@ -81,8 +83,8 @@ public function providerForTestAcceptValue() * @depends testConstruct * @dataProvider providerForTestAcceptValue * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\SiteAccessLimitation $limitation - * @param \eZ\Publish\Core\Limitation\SiteAccessLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SiteAccessLimitation $limitation + * @param \Ibexa\Core\Limitation\SiteAccessLimitationType $limitationType */ public function testAcceptValue(SiteAccessLimitation $limitation, SiteAccessLimitationType $limitationType) { @@ -104,12 +106,12 @@ public function providerForTestAcceptValueException() * @depends testConstruct * @dataProvider providerForTestAcceptValueException * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - * @param \eZ\Publish\Core\Limitation\SiteAccessLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + * @param \Ibexa\Core\Limitation\SiteAccessLimitationType $limitationType */ public function testAcceptValueException(Limitation $limitation, SiteAccessLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $limitationType->acceptValue($limitation); } @@ -146,7 +148,7 @@ public function providerForTestValidateError() new SiteAccessLimitation( [ 'limitationValues' => [ - sprintf('%u', crc32('ezdemo_site')), + sprintf('%u', crc32('ibexa_demo_site')), sprintf('%u', crc32('eng')), sprintf('%u', crc32('fre')), ], @@ -161,9 +163,9 @@ public function providerForTestValidateError() * @dataProvider providerForTestValidateError * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\SiteAccessLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SiteAccessLimitation $limitation * @param int $errorCount - * @param \eZ\Publish\Core\Limitation\SiteAccessLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SiteAccessLimitationType $limitationType */ public function testValidateError(SiteAccessLimitation $limitation, $errorCount, SiteAccessLimitationType $limitationType) { @@ -174,14 +176,14 @@ public function testValidateError(SiteAccessLimitation $limitation, $errorCount, /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\SiteAccessLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SiteAccessLimitationType $limitationType */ public function testBuildValue(SiteAccessLimitationType $limitationType) { $expected = ['test', 'test' => 9]; $value = $limitationType->buildValue($expected); - self::assertInstanceOf('\eZ\Publish\API\Repository\Values\User\Limitation\SiteAccessLimitation', $value); + self::assertInstanceOf(SiteAccessLimitation::class, $value); self::assertIsArray($value->limitationValues); self::assertEquals($expected, $value->limitationValues); } @@ -264,7 +266,7 @@ public function testEvaluateInvalidArgument( ValueObject $object, SiteAccessLimitationType $limitationType ) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $userMock = $this->getUserMock(); $userMock->expects($this->never())->method($this->anything()); @@ -279,11 +281,11 @@ public function testEvaluateInvalidArgument( /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\SiteAccessLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SiteAccessLimitationType $limitationType */ public function testGetCriterion(SiteAccessLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotImplementedException::class); + $this->expectException(NotImplementedException::class); $limitationType->getCriterion(new SiteAccessLimitation(), $this->getUserMock()); } @@ -291,7 +293,7 @@ public function testGetCriterion(SiteAccessLimitationType $limitationType) /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\SiteAccessLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SiteAccessLimitationType $limitationType */ public function testValueSchema(SiteAccessLimitationType $limitationType) { @@ -308,3 +310,5 @@ public function testGenerateSiteAccessValue(SiteAccessLimitationType $limitation self::assertSame('1817462202', $limitationType->generateSiteAccessValue('behat_site')); } } + +class_alias(SiteAccessLimitationTypeTest::class, 'eZ\Publish\Core\Limitation\Tests\SiteAccessLimitationTypeTest'); diff --git a/eZ/Publish/Core/Limitation/Tests/StatusLimitationTypeTest.php b/tests/lib/Limitation/StatusLimitationTypeTest.php similarity index 82% rename from eZ/Publish/Core/Limitation/Tests/StatusLimitationTypeTest.php rename to tests/lib/Limitation/StatusLimitationTypeTest.php index f46e31311f..e4ba35d9ee 100644 --- a/eZ/Publish/Core/Limitation/Tests/StatusLimitationTypeTest.php +++ b/tests/lib/Limitation/StatusLimitationTypeTest.php @@ -4,18 +4,20 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Limitation\Tests; +namespace Ibexa\Tests\Core\Limitation; -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\StatusLimitation; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Limitation\StatusLimitationType; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\User\User; -use eZ\Publish\SPI\Persistence\Content\VersionInfo as SPIVersionInfo; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo as SPIVersionInfo; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\StatusLimitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Limitation\StatusLimitationType; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\User\User; /** * Test Case for LimitationType. @@ -23,7 +25,7 @@ class StatusLimitationTypeTest extends Base { /** - * @return \eZ\Publish\Core\Limitation\StatusLimitationType + * @return \Ibexa\Core\Limitation\StatusLimitationType */ public function testConstruct() { @@ -56,8 +58,8 @@ public function providerForTestAcceptValue() * @depends testConstruct * @dataProvider providerForTestAcceptValue * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\StatusLimitation $limitation - * @param \eZ\Publish\Core\Limitation\StatusLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\StatusLimitation $limitation + * @param \Ibexa\Core\Limitation\StatusLimitationType $limitationType */ public function testAcceptValue(StatusLimitation $limitation, StatusLimitationType $limitationType) { @@ -79,12 +81,12 @@ public function providerForTestAcceptValueException() * @depends testConstruct * @dataProvider providerForTestAcceptValueException * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - * @param \eZ\Publish\Core\Limitation\StatusLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + * @param \Ibexa\Core\Limitation\StatusLimitationType $limitationType */ public function testAcceptValueException(Limitation $limitation, StatusLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $limitationType->acceptValue($limitation); } @@ -147,9 +149,9 @@ public function providerForTestValidateError() * @dataProvider providerForTestValidateError * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\StatusLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\StatusLimitation $limitation * @param int $errorCount - * @param \eZ\Publish\Core\Limitation\StatusLimitationType $limitationType + * @param \Ibexa\Core\Limitation\StatusLimitationType $limitationType */ public function testValidateError(StatusLimitation $limitation, $errorCount, StatusLimitationType $limitationType) { @@ -160,7 +162,7 @@ public function testValidateError(StatusLimitation $limitation, $errorCount, Sta /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\StatusLimitationType $limitationType + * @param \Ibexa\Core\Limitation\StatusLimitationType $limitationType */ public function testBuildValue(StatusLimitationType $limitationType) { @@ -313,7 +315,7 @@ public function testEvaluateInvalidArgument( ValueObject $object, StatusLimitationType $limitationType ) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $userMock = $this->getUserMock(); $userMock->expects($this->never())->method($this->anything()); @@ -329,11 +331,11 @@ public function testEvaluateInvalidArgument( /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\StatusLimitationType $limitationType + * @param \Ibexa\Core\Limitation\StatusLimitationType $limitationType */ public function testGetCriterion(StatusLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotImplementedException::class); + $this->expectException(NotImplementedException::class); $limitationType->getCriterion(new StatusLimitation(), $this->getUserMock()); } @@ -341,10 +343,12 @@ public function testGetCriterion(StatusLimitationType $limitationType) /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\StatusLimitationType $limitationType + * @param \Ibexa\Core\Limitation\StatusLimitationType $limitationType */ public function testValueSchema(StatusLimitationType $limitationType) { self::markTestSkipped('Method valueSchema() is not implemented'); } } + +class_alias(StatusLimitationTypeTest::class, 'eZ\Publish\Core\Limitation\Tests\StatusLimitationTypeTest'); diff --git a/eZ/Publish/Core/Limitation/Tests/SubtreeLimitationTypeTest.php b/tests/lib/Limitation/SubtreeLimitationTypeTest.php similarity index 88% rename from eZ/Publish/Core/Limitation/Tests/SubtreeLimitationTypeTest.php rename to tests/lib/Limitation/SubtreeLimitationTypeTest.php index 265aec1d40..a75e17330a 100644 --- a/eZ/Publish/Core/Limitation/Tests/SubtreeLimitationTypeTest.php +++ b/tests/lib/Limitation/SubtreeLimitationTypeTest.php @@ -4,33 +4,34 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Limitation\Tests; - -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\Limitation\ObjectStateLimitation; -use eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Limitation\SubtreeLimitationType; -use eZ\Publish\Core\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\Query\Criterion\PermissionSubtree; -use eZ\Publish\SPI\Limitation\Type as LimitationType; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as SPILocationHandler; +namespace Ibexa\Tests\Core\Limitation; + +use Ibexa\Contracts\Core\Limitation\Type as LimitationType; +use Ibexa\Contracts\Core\Persistence\Content\Location as SPILocation; +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as SPILocationHandler; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Subtree; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\ObjectStateLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Limitation\SubtreeLimitationType; +use Ibexa\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Repository\Values\Content\Query\Criterion\PermissionSubtree; /** * Test Case for LimitationType. */ class SubtreeLimitationTypeTest extends Base { - /** @var \eZ\Publish\SPI\Persistence\Content\Location\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Location\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $locationHandlerMock; /** @@ -52,7 +53,7 @@ protected function tearDown(): void } /** - * @return \eZ\Publish\Core\Limitation\SubtreeLimitationType + * @return \Ibexa\Core\Limitation\SubtreeLimitationType */ public function testConstruct() { @@ -75,8 +76,8 @@ public function providerForTestAcceptValue() * @dataProvider providerForTestAcceptValue * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation $limitation - * @param \eZ\Publish\Core\Limitation\SubtreeLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation $limitation + * @param \Ibexa\Core\Limitation\SubtreeLimitationType $limitationType */ public function testAcceptValue(SubtreeLimitation $limitation, SubtreeLimitationType $limitationType) { @@ -101,12 +102,12 @@ public function providerForTestAcceptValueException() * @dataProvider providerForTestAcceptValueException * @depends testConstruct * - * @param \eZ\Publish\API\Repository\Values\User\Limitation $limitation - * @param \eZ\Publish\Core\Limitation\SubtreeLimitationType $limitationType + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation $limitation + * @param \Ibexa\Core\Limitation\SubtreeLimitationType $limitationType */ public function testAcceptValueException(Limitation $limitation, SubtreeLimitationType $limitationType) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $limitationType->acceptValue($limitation); } @@ -126,7 +127,7 @@ public function providerForTestValidatePass() /** * @dataProvider providerForTestValidatePass * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation $limitation */ public function testValidatePass(SubtreeLimitation $limitation) { @@ -172,7 +173,7 @@ public function providerForTestValidateError() /** * @dataProvider providerForTestValidateError * - * @param \eZ\Publish\API\Repository\Values\User\Limitation\SubtreeLimitation $limitation + * @param \Ibexa\Contracts\Core\Repository\Values\User\Limitation\SubtreeLimitation $limitation * @param int $errorCount */ public function testValidateError(SubtreeLimitation $limitation, $errorCount) @@ -236,7 +237,7 @@ public function testValidateErrorWrongPath() /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\SubtreeLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SubtreeLimitationType $limitationType */ public function testBuildValue(SubtreeLimitationType $limitationType) { @@ -481,7 +482,7 @@ public function testEvaluateInvalidArgument( $targets, array $persistenceLocations ) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); // Need to create inline instead of depending on testConstruct() to get correct mock instance $limitationType = $this->testConstruct(); @@ -508,7 +509,7 @@ public function testEvaluateInvalidArgument( /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\SubtreeLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SubtreeLimitationType $limitationType */ public function testGetCriterionInvalidValue(SubtreeLimitationType $limitationType) { @@ -523,7 +524,7 @@ public function testGetCriterionInvalidValue(SubtreeLimitationType $limitationTy /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\SubtreeLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SubtreeLimitationType $limitationType */ public function testGetCriterionSingleValue(SubtreeLimitationType $limitationType) { @@ -544,7 +545,7 @@ public function testGetCriterionSingleValue(SubtreeLimitationType $limitationTyp /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\SubtreeLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SubtreeLimitationType $limitationType */ public function testGetCriterionMultipleValues(SubtreeLimitationType $limitationType) { @@ -565,7 +566,7 @@ public function testGetCriterionMultipleValues(SubtreeLimitationType $limitation /** * @depends testConstruct * - * @param \eZ\Publish\Core\Limitation\SubtreeLimitationType $limitationType + * @param \Ibexa\Core\Limitation\SubtreeLimitationType $limitationType */ public function testValueSchema(SubtreeLimitationType $limitationType) { @@ -575,3 +576,5 @@ public function testValueSchema(SubtreeLimitationType $limitationType) ); } } + +class_alias(SubtreeLimitationTypeTest::class, 'eZ\Publish\Core\Limitation\Tests\SubtreeLimitationTypeTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/CompoundMatcherNormalizerTest.php b/tests/lib/MVC/Symfony/Component/Serializer/CompoundMatcherNormalizerTest.php similarity index 76% rename from eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/CompoundMatcherNormalizerTest.php rename to tests/lib/MVC/Symfony/Component/Serializer/CompoundMatcherNormalizerTest.php index bb6035a767..49d56afb87 100644 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/CompoundMatcherNormalizerTest.php +++ b/tests/lib/MVC/Symfony/Component/Serializer/CompoundMatcherNormalizerTest.php @@ -6,14 +6,14 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer; +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer; -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\CompoundMatcherNormalizer; -use eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\CompoundStub; -use eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\MatcherStub; -use eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\SerializerStub; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound; +use Ibexa\Core\MVC\Symfony\Component\Serializer\CompoundMatcherNormalizer; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Compound; +use Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs\CompoundStub; +use Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs\MatcherStub; +use Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs\SerializerStub; use PHPUnit\Framework\TestCase; final class CompoundMatcherNormalizerTest extends TestCase @@ -70,3 +70,5 @@ public function testSupportsDenormalization(): void $this->assertFalse($normalizer->supportsDenormalization($data, Matcher::class, 'json')); } } + +class_alias(CompoundMatcherNormalizerTest::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\CompoundMatcherNormalizerTest'); diff --git a/tests/lib/MVC/Symfony/Component/Serializer/HostElementNormalizerTest.php b/tests/lib/MVC/Symfony/Component/Serializer/HostElementNormalizerTest.php new file mode 100644 index 0000000000..e409df5a27 --- /dev/null +++ b/tests/lib/MVC/Symfony/Component/Serializer/HostElementNormalizerTest.php @@ -0,0 +1,51 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer; + +use Ibexa\Core\MVC\Symfony\Component\Serializer\HostElementNormalizer; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\HostElement; +use Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs\SerializerStub; +use PHPUnit\Framework\TestCase; + +final class HostElementNormalizerTest extends TestCase +{ + public function testNormalization(): void + { + $normalizer = new HostElementNormalizer(); + $normalizer->setSerializer(new SerializerStub()); + + $matcher = new HostElement(2); + // Set request and invoke match to initialize HostElement::$hostElements + $matcher->setRequest(SimplifiedRequest::fromUrl('http://ibexa.dev/foo/bar')); + $matcher->match(); + + $this->assertEquals( + [ + 'elementNumber' => 2, + 'hostElements' => [ + 'ibexa', + 'dev', + ], + ], + $normalizer->normalize($matcher) + ); + } + + public function testSupportsNormalization(): void + { + $normalizer = new HostElementNormalizer(); + + $this->assertTrue($normalizer->supportsNormalization($this->createMock(HostElement::class))); + $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); + } +} + +class_alias(HostElementNormalizerTest::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\HostElementNormalizerTest'); diff --git a/tests/lib/MVC/Symfony/Component/Serializer/HostTextNormalizerTest.php b/tests/lib/MVC/Symfony/Component/Serializer/HostTextNormalizerTest.php new file mode 100644 index 0000000000..ad91c6160b --- /dev/null +++ b/tests/lib/MVC/Symfony/Component/Serializer/HostTextNormalizerTest.php @@ -0,0 +1,49 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer; + +use Ibexa\Core\MVC\Symfony\Component\Serializer\HostTextNormalizer; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\HostText; +use Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs\SerializerStub; +use Ibexa\Tests\Core\Search\TestCase; + +final class HostTextNormalizerTest extends TestCase +{ + public function testNormalize(): void + { + $normalizer = new HostTextNormalizer(); + $normalizer->setSerializer(new SerializerStub()); + + $matcher = new HostText([ + 'prefix' => 'foo', + 'suffix' => 'bar', + ]); + + $this->assertEquals( + [ + 'siteAccessesConfiguration' => [ + 'prefix' => 'foo', + 'suffix' => 'bar', + ], + ], + $normalizer->normalize($matcher) + ); + } + + public function testSupportsNormalization(): void + { + $normalizer = new HostTextNormalizer(); + + $this->assertTrue($normalizer->supportsNormalization($this->createMock(HostText::class))); + $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); + } +} + +class_alias(HostTextNormalizerTest::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\HostTextNormalizerTest'); diff --git a/tests/lib/MVC/Symfony/Component/Serializer/MapNormalizerTest.php b/tests/lib/MVC/Symfony/Component/Serializer/MapNormalizerTest.php new file mode 100644 index 0000000000..17ab5c2961 --- /dev/null +++ b/tests/lib/MVC/Symfony/Component/Serializer/MapNormalizerTest.php @@ -0,0 +1,44 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer; + +use Ibexa\Core\MVC\Symfony\Component\Serializer\MapNormalizer; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Map as MapMatcher; +use PHPUnit\Framework\TestCase; + +final class MapNormalizerTest extends TestCase +{ + public function testNormalization(): void + { + $normalizer = new MapNormalizer(); + + $matcher = $this->createMock(MapMatcher::class); + $matcher->method('getMapKey')->willReturn('foo'); + + $this->assertEquals( + [ + 'key' => 'foo', + 'map' => [], + 'reverseMap' => [], + ], + $normalizer->normalize($matcher) + ); + } + + public function testSupportsNormalization(): void + { + $normalizer = new MapNormalizer(); + + $this->assertTrue($normalizer->supportsNormalization($this->createMock(MapMatcher::class))); + $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); + } +} + +class_alias(MapNormalizerTest::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\MapNormalizerTest'); diff --git a/tests/lib/MVC/Symfony/Component/Serializer/RegexHostNormalizerTest.php b/tests/lib/MVC/Symfony/Component/Serializer/RegexHostNormalizerTest.php new file mode 100644 index 0000000000..eff32ba5cd --- /dev/null +++ b/tests/lib/MVC/Symfony/Component/Serializer/RegexHostNormalizerTest.php @@ -0,0 +1,49 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer; + +use Ibexa\Core\MVC\Symfony\Component\Serializer\RegexHostNormalizer; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Regex\Host; +use Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs\SerializerStub; +use Ibexa\Tests\Core\Search\TestCase; + +final class RegexHostNormalizerTest extends TestCase +{ + public function testNormalize(): void + { + $normalizer = new RegexHostNormalizer(); + $normalizer->setSerializer(new SerializerStub()); + + $matcher = new Host([ + 'regex' => '/^Foo(.*)/(.*)/', + 'itemNumber' => 2, + ]); + + $this->assertEquals( + [ + 'siteAccessesConfiguration' => [ + 'regex' => '/^Foo(.*)/(.*)/', + 'itemNumber' => 2, + ], + ], + $normalizer->normalize($matcher) + ); + } + + public function testSupportsNormalization(): void + { + $normalizer = new RegexHostNormalizer(); + + $this->assertTrue($normalizer->supportsNormalization($this->createMock(Host::class))); + $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); + } +} + +class_alias(RegexHostNormalizerTest::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\RegexHostNormalizerTest'); diff --git a/tests/lib/MVC/Symfony/Component/Serializer/RegexNormalizerTest.php b/tests/lib/MVC/Symfony/Component/Serializer/RegexNormalizerTest.php new file mode 100644 index 0000000000..9fbe7c7f23 --- /dev/null +++ b/tests/lib/MVC/Symfony/Component/Serializer/RegexNormalizerTest.php @@ -0,0 +1,43 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer; + +use Ibexa\Core\MVC\Symfony\Component\Serializer\RegexNormalizer; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Regex as RegexMatcher; +use Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs\RegexMatcher as RegexMatcherStub; +use PHPUnit\Framework\TestCase; + +final class RegexNormalizerTest extends TestCase +{ + public function testNormalize(): void + { + $normalizer = new RegexNormalizer(); + $matcher = new RegexMatcherStub('/^Foo(.*)/(.*)/', 2); + + $this->assertEquals( + [ + 'regex' => '/^Foo(.*)/(.*)/', + 'itemNumber' => 2, + 'matchedSiteAccess' => null, + ], + $normalizer->normalize($matcher) + ); + } + + public function testSupportsNormalization(): void + { + $normalizer = new RegexNormalizer(); + + $this->assertTrue($normalizer->supportsNormalization($this->createMock(RegexMatcher::class))); + $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); + } +} + +class_alias(RegexNormalizerTest::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\RegexNormalizerTest'); diff --git a/tests/lib/MVC/Symfony/Component/Serializer/RegexURINormalizerTest.php b/tests/lib/MVC/Symfony/Component/Serializer/RegexURINormalizerTest.php new file mode 100644 index 0000000000..f9a45dbd8c --- /dev/null +++ b/tests/lib/MVC/Symfony/Component/Serializer/RegexURINormalizerTest.php @@ -0,0 +1,49 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer; + +use Ibexa\Core\MVC\Symfony\Component\Serializer\RegexURINormalizer; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Regex\URI; +use Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs\SerializerStub; +use Ibexa\Tests\Core\Search\TestCase; + +final class RegexURINormalizerTest extends TestCase +{ + public function testNormalize(): void + { + $normalizer = new RegexURINormalizer(); + $normalizer->setSerializer(new SerializerStub()); + + $matcher = new URI([ + 'regex' => '/^Foo(.*)/(.*)/', + 'itemNumber' => 2, + ]); + + $this->assertEquals( + [ + 'siteAccessesConfiguration' => [ + 'regex' => '/^Foo(.*)/(.*)/', + 'itemNumber' => 2, + ], + ], + $normalizer->normalize($matcher) + ); + } + + public function testSupportsNormalization(): void + { + $normalizer = new RegexURINormalizer(); + + $this->assertTrue($normalizer->supportsNormalization($this->createMock(URI::class))); + $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); + } +} + +class_alias(RegexURINormalizerTest::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\RegexURINormalizerTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/SimplifiedRequestNormalizerTest.php b/tests/lib/MVC/Symfony/Component/Serializer/SimplifiedRequestNormalizerTest.php similarity index 84% rename from eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/SimplifiedRequestNormalizerTest.php rename to tests/lib/MVC/Symfony/Component/Serializer/SimplifiedRequestNormalizerTest.php index 6d5c63897f..2c7d02b775 100644 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/SimplifiedRequestNormalizerTest.php +++ b/tests/lib/MVC/Symfony/Component/Serializer/SimplifiedRequestNormalizerTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer; +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer; -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\SimplifiedRequestNormalizer; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\Component\Serializer\SimplifiedRequestNormalizer; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; use PHPUnit\Framework\TestCase; use stdClass; @@ -53,3 +53,5 @@ public function testSupportsNormalization() $this->assertFalse($normalizer->supportsNormalization(new stdClass())); } } + +class_alias(SimplifiedRequestNormalizerTest::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\SimplifiedRequestNormalizerTest'); diff --git a/tests/lib/MVC/Symfony/Component/Serializer/Stubs/CompoundStub.php b/tests/lib/MVC/Symfony/Component/Serializer/Stubs/CompoundStub.php new file mode 100644 index 0000000000..7799567c97 --- /dev/null +++ b/tests/lib/MVC/Symfony/Component/Serializer/Stubs/CompoundStub.php @@ -0,0 +1,33 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs; + +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Compound; + +final class CompoundStub extends Compound +{ + public function __construct(array $subMatchers) + { + parent::__construct([]); + $this->subMatchers = $subMatchers; + } + + public function match() + { + throw new NotImplementedException(__METHOD__); + } + + public function reverseMatch($siteAccessName) + { + throw new NotImplementedException(__METHOD__); + } +} + +class_alias(CompoundStub::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\CompoundStub'); diff --git a/tests/lib/MVC/Symfony/Component/Serializer/Stubs/MatcherStub.php b/tests/lib/MVC/Symfony/Component/Serializer/Stubs/MatcherStub.php new file mode 100644 index 0000000000..50d9be135f --- /dev/null +++ b/tests/lib/MVC/Symfony/Component/Serializer/Stubs/MatcherStub.php @@ -0,0 +1,46 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs; + +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; + +final class MatcherStub implements Matcher +{ + /** @var mixed */ + private $data; + + public function __construct($data = null) + { + $this->data = $data; + } + + public function setRequest(SimplifiedRequest $request) + { + throw new NotImplementedException(__METHOD__); + } + + public function match() + { + throw new NotImplementedException(__METHOD__); + } + + public function getName() + { + throw new NotImplementedException(__METHOD__); + } + + public function getData() + { + return $this->data; + } +} + +class_alias(MatcherStub::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\MatcherStub'); diff --git a/tests/lib/MVC/Symfony/Component/Serializer/Stubs/RegexMatcher.php b/tests/lib/MVC/Symfony/Component/Serializer/Stubs/RegexMatcher.php new file mode 100644 index 0000000000..d00375db5e --- /dev/null +++ b/tests/lib/MVC/Symfony/Component/Serializer/Stubs/RegexMatcher.php @@ -0,0 +1,22 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs; + +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Regex as BaseRegex; + +final class RegexMatcher extends BaseRegex +{ + public function getName() + { + throw new NotImplementedException(__METHOD__); + } +} + +class_alias(RegexMatcher::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\RegexMatcher'); diff --git a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/Stubs/SerializerStub.php b/tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php similarity index 83% rename from eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/Stubs/SerializerStub.php rename to tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php index 3538aca129..d76cf9f15b 100644 --- a/eZ/Publish/Core/MVC/Symfony/Component/Tests/Serializer/Stubs/SerializerStub.php +++ b/tests/lib/MVC/Symfony/Component/Serializer/Stubs/SerializerStub.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs; +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; use Symfony\Component\Serializer\Normalizer\NormalizerInterface; use Symfony\Component\Serializer\SerializerInterface; @@ -47,3 +47,5 @@ public function supportsNormalization($data, string $format = null) return true; } } + +class_alias(SerializerStub::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\Stubs\SerializerStub'); diff --git a/tests/lib/MVC/Symfony/Component/Serializer/URIElementNormalizerTest.php b/tests/lib/MVC/Symfony/Component/Serializer/URIElementNormalizerTest.php new file mode 100644 index 0000000000..3bf5477672 --- /dev/null +++ b/tests/lib/MVC/Symfony/Component/Serializer/URIElementNormalizerTest.php @@ -0,0 +1,48 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer; + +use Ibexa\Core\MVC\Symfony\Component\Serializer\URIElementNormalizer; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\URIElement; +use Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs\SerializerStub; +use PHPUnit\Framework\TestCase; + +final class URIElementNormalizerTest extends TestCase +{ + public function testNormalization(): void + { + $normalizer = new URIElementNormalizer(); + $normalizer->setSerializer(new SerializerStub()); + + $matcher = new URIElement(2); + // Set request and invoke match to initialize HostElement::$hostElements + $matcher->setRequest(SimplifiedRequest::fromUrl('http://ezpublish.dev/foo/bar')); + $matcher->match(); + + $this->assertEquals( + [ + 'elementNumber' => 2, + 'uriElements' => ['foo', 'bar'], + ], + $normalizer->normalize($matcher) + ); + } + + public function testSupportsNormalization(): void + { + $normalizer = new URIElementNormalizer(); + + $this->assertTrue($normalizer->supportsNormalization($this->createMock(URIElement::class))); + $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); + } +} + +class_alias(URIElementNormalizerTest::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\URIElementNormalizerTest'); diff --git a/tests/lib/MVC/Symfony/Component/Serializer/URITextNormalizerTest.php b/tests/lib/MVC/Symfony/Component/Serializer/URITextNormalizerTest.php new file mode 100644 index 0000000000..d1a4b77786 --- /dev/null +++ b/tests/lib/MVC/Symfony/Component/Serializer/URITextNormalizerTest.php @@ -0,0 +1,49 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Component\Serializer; + +use Ibexa\Core\MVC\Symfony\Component\Serializer\URITextNormalizer; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\URIText; +use Ibexa\Tests\Core\MVC\Symfony\Component\Serializer\Stubs\SerializerStub; +use Ibexa\Tests\Core\Search\TestCase; + +final class URITextNormalizerTest extends TestCase +{ + public function testNormalize(): void + { + $normalizer = new URITextNormalizer(); + $normalizer->setSerializer(new SerializerStub()); + + $matcher = new URIText([ + 'prefix' => 'foo', + 'suffix' => 'bar', + ]); + + $this->assertEquals( + [ + 'siteAccessesConfiguration' => [ + 'prefix' => 'foo', + 'suffix' => 'bar', + ], + ], + $normalizer->normalize($matcher) + ); + } + + public function testSupportsNormalization(): void + { + $normalizer = new URITextNormalizer(); + + $this->assertTrue($normalizer->supportsNormalization($this->createMock(URIText::class))); + $this->assertFalse($normalizer->supportsNormalization($this->createMock(Matcher::class))); + } +} + +class_alias(URITextNormalizerTest::class, 'eZ\Publish\Core\MVC\Symfony\Component\Tests\Serializer\URITextNormalizerTest'); diff --git a/tests/lib/MVC/Symfony/Controller/Controller/Content/PreviewControllerTest.php b/tests/lib/MVC/Symfony/Controller/Controller/Content/PreviewControllerTest.php new file mode 100644 index 0000000000..04eee4c2c1 --- /dev/null +++ b/tests/lib/MVC/Symfony/Controller/Controller/Content/PreviewControllerTest.php @@ -0,0 +1,328 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Controller\Controller\Content; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Core\Base\Exceptions\UnauthorizedException; +use Ibexa\Core\Helper\ContentPreviewHelper; +use Ibexa\Core\Helper\PreviewLocationProvider; +use Ibexa\Core\MVC\Symfony\Controller\Content\PreviewController; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\View\CustomLocationControllerChecker; +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; +use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; +use Symfony\Component\Security\Core\Exception\AccessDeniedException; + +/** + * @covers \Ibexa\Core\MVC\Symfony\Controller\Content\PreviewController + */ +final class PreviewControllerTest extends TestCase +{ + /** @var \Ibexa\Contracts\Core\Repository\ContentService&\PHPUnit\Framework\MockObject\MockObject */ + protected ContentService $contentService; + + /** @var \Ibexa\Contracts\Core\Repository\LocationService&\PHPUnit\Framework\MockObject\MockObject */ + protected LocationService $locationService; + + /** @var \Symfony\Component\HttpKernel\HttpKernelInterface&\PHPUnit\Framework\MockObject\MockObject */ + protected HttpKernelInterface $httpKernel; + + /** @var \Ibexa\Core\Helper\ContentPreviewHelper&\PHPUnit\Framework\MockObject\MockObject */ + protected ContentPreviewHelper $previewHelper; + + /** @var \Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface&\PHPUnit\Framework\MockObject\MockObject */ + protected AuthorizationCheckerInterface $authorizationChecker; + + /** @var \Ibexa\Core\Helper\PreviewLocationProvider&\PHPUnit\Framework\MockObject\MockObject */ + protected PreviewLocationProvider $locationProvider; + + /** @var \Ibexa\Core\MVC\Symfony\View\CustomLocationControllerChecker&\PHPUnit\Framework\MockObject\MockObject */ + protected CustomLocationControllerChecker $controllerChecker; + + protected function setUp(): void + { + parent::setUp(); + + $this->contentService = $this->createMock(ContentService::class); + $this->locationService = $this->createMock(LocationService::class); + $this->httpKernel = $this->createMock(HttpKernelInterface::class); + $this->previewHelper = $this->createMock(ContentPreviewHelper::class); + $this->authorizationChecker = $this->createMock(AuthorizationCheckerInterface::class); + $this->locationProvider = $this->createMock(PreviewLocationProvider::class); + $this->controllerChecker = $this->createMock(CustomLocationControllerChecker::class); + } + + protected function getPreviewController(): PreviewController + { + return new PreviewController( + $this->contentService, + $this->locationService, + $this->httpKernel, + $this->previewHelper, + $this->authorizationChecker, + $this->locationProvider, + $this->controllerChecker + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + public function testPreviewUnauthorized(): void + { + $this->expectException(AccessDeniedException::class); + + $controller = $this->getPreviewController(); + $contentId = 123; + $lang = 'eng-GB'; + $versionNo = 3; + $this->contentService + ->expects(self::once()) + ->method('loadContent') + ->with($contentId, [$lang], $versionNo) + ->willThrowException(new UnauthorizedException('foo', 'bar')) + ; + + $controller->previewContentAction(new Request(), $contentId, $versionNo, $lang, 'test'); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + public function testPreviewCanUserFail(): void + { + $controller = $this->getPreviewController(); + $contentId = 123; + $lang = 'eng-GB'; + $versionNo = 3; + $content = $this->createMock(Content::class); + + $location = $this->createMock(Location::class); + $this->locationProvider + ->method('loadMainLocationByContent') + ->with($content) + ->willReturn($location) + ; + $this->contentService + ->method('loadContent') + ->with($contentId, [$lang], $versionNo) + ->willReturn($content) + ; + + $this->authorizationChecker->method('isGranted')->willReturn(false); + + $this->expectException(AccessDeniedException::class); + + $controller->previewContentAction(new Request(), $contentId, $versionNo, $lang, 'test'); + } + + /** + * @return iterable<string, array{SiteAccess|null, int, string, int, int|null, string|null}> + */ + public static function getDataForTestPreview(): iterable + { + yield 'with different SiteAccess, main Location' => [ + new SiteAccess('test', 'preview'), + 123, // contentId + 'eng-GB', + 3, // versionNo + null, // secondary Location Id + null, + ]; + + yield 'with default SiteAccess, main Location' => [ + null, + 234, // contentId + 'ger-DE', + 1, // versionNo + null, // secondary Location Id + null, + ]; + + yield 'with different SiteAccess, secondary Location' => [ + new SiteAccess('test', 'preview'), + 567, // contentId + 'eng-GB', + 11, // versionNo + 220, // secondary Location Id + null, + ]; + + yield 'with default SiteAccess, secondary Location' => [ + null, + 234, // contentId + 'ger-DE', + 1, // versionNo + 221, // secondary Location Id + null, + ]; + + yield 'with different SiteAccess and different viewType' => [ + new SiteAccess('test', 'preview'), + 789, // contentId + 'eng-GB', + 9, // versionNo + null, + 'foo_view_type', + ]; + } + + /** + * @dataProvider getDataForTestPreview + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + */ + public function testPreview( + ?SiteAccess $previewSiteAccess, + int $contentId, + string $language, + int $versionNo, + ?int $locationId, + ?string $viewType = null + ): void { + $content = $this->createMock(Content::class); + $location = $this->getMockBuilder(Location::class) + ->setConstructorArgs([['id' => $locationId ?? 456]]) + ->getMockForAbstractClass(); + + if (null === $locationId) { + $this->locationProvider->method('loadMainLocationByContent')->with($content)->willReturn($location); + } else { + $this->locationService->method('loadLocation')->with($locationId)->willReturn($location); + } + + $this->contentService + ->method('loadContent') + ->with($contentId, [$language], $versionNo) + ->willReturn($content); + + $this->authorizationChecker->method('isGranted')->willReturn(true); + + $originalSiteAccess = new SiteAccess('foo'); + + $request = new Request(); + $request->attributes->set('semanticPathinfo', '/foo/bar'); + if (null !== $viewType) { + $request->query->set('viewType', $viewType); + } + + $this->configurePreviewHelper( + $content, + $location, + $originalSiteAccess, + $previewSiteAccess + ); + + $forwardRequestParameters = $this->getExpectedForwardRequestParameters( + $location, + $content, + $previewSiteAccess ?? $originalSiteAccess, + $language, + $viewType + ); + + // the actual assertion happens here, checking if the forward request params are correct + $this->httpKernel + ->method('handle') + ->with($request->duplicate(null, null, $forwardRequestParameters), HttpKernelInterface::SUB_REQUEST) + ->willReturn(new Response()) + ; + + $controller = $this->getPreviewController(); + $controller->previewContentAction( + $request, + $contentId, + $versionNo, + $language, + $previewSiteAccess !== null ? $previewSiteAccess->name : null, + $locationId + ); + } + + /** + * @return array<string, mixed> + */ + private function getExpectedForwardRequestParameters( + Location $location, + Content $content, + SiteAccess $previewSiteAccess, + string $language, + ?string $viewType + ): array { + return [ + '_controller' => 'ibexa_content::viewAction', + '_route' => 'ibexa.content.view', + '_route_params' => [ + 'contentId' => $content->id, + 'locationId' => $location->id, + ], + 'location' => $location, + 'content' => $content, + 'viewType' => $viewType ?? 'full', + 'layout' => true, + 'params' => [ + 'content' => $content, + 'location' => $location, + 'isPreview' => true, + 'language' => $language, + ], + 'siteaccess' => $previewSiteAccess, + 'semanticPathinfo' => '/foo/bar', + ]; + } + + private function configurePreviewHelper( + Content $content, + Location $location, + SiteAccess $originalSiteAccess, + ?SiteAccess $previewSiteAccess = null + ): void { + $this->previewHelper + ->expects(self::exactly(2)) + ->method('setPreviewActive') + ->withConsecutive([true], [false]) + ; + + $this->previewHelper + ->expects(self::once()) + ->method('setPreviewedContent') + ->with($content) + ; + $this->previewHelper + ->expects(self::once()) + ->method('setPreviewedLocation') + ->with($location) + ; + $this->previewHelper + ->expects(self::once()) + ->method('getOriginalSiteAccess') + ->willReturn($originalSiteAccess) + ; + + if ($previewSiteAccess !== null) { + $this->previewHelper + ->expects(self::once()) + ->method('changeConfigScope') + ->with($previewSiteAccess->name) + ->willReturn($previewSiteAccess) + ; + } + + $this->previewHelper + ->expects(self::once()) + ->method('restoreConfigScope') + ; + } +} diff --git a/eZ/Publish/Core/MVC/Symfony/Controller/Tests/ControllerTest.php b/tests/lib/MVC/Symfony/Controller/ControllerTest.php similarity index 87% rename from eZ/Publish/Core/MVC/Symfony/Controller/Tests/ControllerTest.php rename to tests/lib/MVC/Symfony/Controller/ControllerTest.php index 8c94d63525..7b552e7fc6 100644 --- a/eZ/Publish/Core/MVC/Symfony/Controller/Tests/ControllerTest.php +++ b/tests/lib/MVC/Symfony/Controller/ControllerTest.php @@ -4,20 +4,22 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Controller\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Controller; -use eZ\Publish\Core\MVC\Symfony\Controller\Controller; +use Ibexa\Core\MVC\Symfony\Controller\Controller; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Templating\EngineInterface; /** + * @covers \Ibexa\Core\MVC\Symfony\Controller\Controller::render + * * @mvc */ class ControllerTest extends TestCase { - /** @var \eZ\Publish\Core\MVC\Symfony\Controller\Controller */ + /** @var \Ibexa\Core\MVC\Symfony\Controller\Controller */ protected $controller; /** @var \PHPUnit\Framework\MockObject\MockObject */ @@ -39,9 +41,6 @@ protected function setUp(): void ->will($this->returnValue($this->templateEngineMock)); } - /** - * @covers \eZ\Publish\Core\MVC\Symfony\Controller\Controller::render - */ public function testRender() { $view = 'some:valid:view.html.twig'; @@ -73,3 +72,5 @@ public function testRenderWithResponse() self::assertSame($tplResult, $response->getContent()); } } + +class_alias(ControllerTest::class, 'eZ\Publish\Core\MVC\Symfony\Controller\Tests\ControllerTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Controller/Tests/QueryRenderControllerTest.php b/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php similarity index 80% rename from eZ/Publish/Core/MVC/Symfony/Controller/Tests/QueryRenderControllerTest.php rename to tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php index 0991af3262..1e82471efe 100644 --- a/eZ/Publish/Core/MVC/Symfony/Controller/Tests/QueryRenderControllerTest.php +++ b/tests/lib/MVC/Symfony/Controller/QueryRenderControllerTest.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Controller\Tests; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\Core\MVC\Symfony\Controller\QueryRenderController; -use eZ\Publish\Core\MVC\Symfony\View\QueryView; -use eZ\Publish\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface; -use eZ\Publish\Core\Pagination\Pagerfanta\Pagerfanta; -use eZ\Publish\Core\Pagination\Pagerfanta\SearchResultAdapter; -use eZ\Publish\Core\Query\QueryFactoryInterface; +namespace Ibexa\Tests\Core\MVC\Symfony\Controller; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Core\MVC\Symfony\Controller\QueryRenderController; +use Ibexa\Core\MVC\Symfony\View\QueryView; +use Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface; +use Ibexa\Core\Pagination\Pagerfanta\Pagerfanta; +use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; +use Ibexa\Core\Query\QueryFactoryInterface; use Pagerfanta\Adapter\AdapterInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; @@ -49,13 +49,13 @@ final class QueryRenderControllerTest extends TestCase ], ]; - /** @var \eZ\Publish\Core\Query\QueryFactoryInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\Query\QueryFactoryInterface|\PHPUnit\Framework\MockObject\MockObject */ private $queryFactory; - /** @var \eZ\Publish\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactoryInterface|\PHPUnit\Framework\MockObject\MockObject */ private $searchHitAdapterFactory; - /** @var \eZ\Publish\Core\MVC\Symfony\Controller\QueryRenderController */ + /** @var \Ibexa\Core\MVC\Symfony\Controller\QueryRenderController */ private $controller; protected function setUp(): void @@ -138,3 +138,5 @@ private function assertRenderQueryResult( ); } } + +class_alias(QueryRenderControllerTest::class, 'eZ\Publish\Core\MVC\Symfony\Controller\Tests\QueryRenderControllerTest'); diff --git a/tests/lib/MVC/Symfony/ErrorHandler/Php82HideDeprecationsErrorHandlerTest.php b/tests/lib/MVC/Symfony/ErrorHandler/Php82HideDeprecationsErrorHandlerTest.php index a4eadebe86..d66552b029 100644 --- a/tests/lib/MVC/Symfony/ErrorHandler/Php82HideDeprecationsErrorHandlerTest.php +++ b/tests/lib/MVC/Symfony/ErrorHandler/Php82HideDeprecationsErrorHandlerTest.php @@ -16,8 +16,7 @@ */ final class Php82HideDeprecationsErrorHandlerTest extends TestCase { - /** @var int */ - private $originalErrorReporting; + private int $originalErrorReporting; protected function setUp(): void { diff --git a/eZ/Publish/Core/MVC/Symfony/Event/Tests/ContentCacheClearEventTest.php b/tests/lib/MVC/Symfony/Event/ContentCacheClearEventTest.php similarity index 81% rename from eZ/Publish/Core/MVC/Symfony/Event/Tests/ContentCacheClearEventTest.php rename to tests/lib/MVC/Symfony/Event/ContentCacheClearEventTest.php index bc9bb85ea4..55a973dfb6 100644 --- a/eZ/Publish/Core/MVC/Symfony/Event/Tests/ContentCacheClearEventTest.php +++ b/tests/lib/MVC/Symfony/Event/ContentCacheClearEventTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Event\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Event; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\Core\MVC\Symfony\Event\ContentCacheClearEvent; -use eZ\Publish\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Core\MVC\Symfony\Event\ContentCacheClearEvent; +use Ibexa\Core\Repository\Values\Content\Location; use PHPUnit\Framework\TestCase; class ContentCacheClearEventTest extends TestCase @@ -45,3 +45,5 @@ public function setLocationsToClear() $this->assertSame($otherLocations, $event->getLocationsToClear()); } } + +class_alias(ContentCacheClearEventTest::class, 'eZ\Publish\Core\MVC\Symfony\Event\Tests\ContentCacheClearEventTest'); diff --git a/tests/lib/MVC/Symfony/Event/InteractiveLoginEventTest.php b/tests/lib/MVC/Symfony/Event/InteractiveLoginEventTest.php new file mode 100644 index 0000000000..4dc8adadf2 --- /dev/null +++ b/tests/lib/MVC/Symfony/Event/InteractiveLoginEventTest.php @@ -0,0 +1,28 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Event; + +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Core\MVC\Symfony\Event\InteractiveLoginEvent; +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +class InteractiveLoginEventTest extends TestCase +{ + public function testGetSetAPIUser() + { + $event = new InteractiveLoginEvent(new Request(), $this->createMock(TokenInterface::class)); + $this->assertFalse($event->hasAPIUser()); + $apiUser = $this->createMock(User::class); + $event->setApiUser($apiUser); + $this->assertTrue($event->hasAPIUser()); + $this->assertSame($apiUser, $event->getAPIUser()); + } +} + +class_alias(InteractiveLoginEventTest::class, 'eZ\Publish\Core\MVC\Symfony\Event\Tests\InteractiveLoginEventTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Event/Tests/RouteReferenceGenerationEventTest.php b/tests/lib/MVC/Symfony/Event/RouteReferenceGenerationEventTest.php similarity index 80% rename from eZ/Publish/Core/MVC/Symfony/Event/Tests/RouteReferenceGenerationEventTest.php rename to tests/lib/MVC/Symfony/Event/RouteReferenceGenerationEventTest.php index 79e7027799..d9f3dda9f1 100644 --- a/eZ/Publish/Core/MVC/Symfony/Event/Tests/RouteReferenceGenerationEventTest.php +++ b/tests/lib/MVC/Symfony/Event/RouteReferenceGenerationEventTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Event\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Event; -use eZ\Publish\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent; -use eZ\Publish\Core\MVC\Symfony\Routing\RouteReference; +use Ibexa\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent; +use Ibexa\Core\MVC\Symfony\Routing\RouteReference; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; @@ -36,3 +36,5 @@ public function testGetSet() $this->assertSame($newRouteReference, $event->getRouteReference()); } } + +class_alias(RouteReferenceGenerationEventTest::class, 'eZ\Publish\Core\MVC\Symfony\Event\Tests\RouteReferenceGenerationEventTest'); diff --git a/tests/lib/MVC/Symfony/Event/ScopeChangeEventTest.php b/tests/lib/MVC/Symfony/Event/ScopeChangeEventTest.php new file mode 100644 index 0000000000..704c0d3aa3 --- /dev/null +++ b/tests/lib/MVC/Symfony/Event/ScopeChangeEventTest.php @@ -0,0 +1,23 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Event; + +use Ibexa\Core\MVC\Symfony\Event\ScopeChangeEvent; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use PHPUnit\Framework\TestCase; + +class ScopeChangeEventTest extends TestCase +{ + public function testGetSiteAccess() + { + $siteAccess = new SiteAccess('foo', 'test'); + $event = new ScopeChangeEvent($siteAccess); + $this->assertSame($siteAccess, $event->getSiteAccess()); + } +} + +class_alias(ScopeChangeEventTest::class, 'eZ\Publish\Core\MVC\Symfony\Event\Tests\ScopeChangeEventTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/EventListener/Tests/ContentViewTwigVariablesSubscriberTest.php b/tests/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriberTest.php similarity index 89% rename from eZ/Publish/Core/MVC/Symfony/EventListener/Tests/ContentViewTwigVariablesSubscriberTest.php rename to tests/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriberTest.php index 7c728a9b64..c7e16f4d6e 100644 --- a/eZ/Publish/Core/MVC/Symfony/EventListener/Tests/ContentViewTwigVariablesSubscriberTest.php +++ b/tests/lib/MVC/Symfony/EventListener/ContentViewTwigVariablesSubscriberTest.php @@ -6,24 +6,24 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\EventListener\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\EventListener; use ArrayIterator; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Event\PreContentViewEvent; -use eZ\Publish\Core\MVC\Symfony\EventListener\ContentViewTwigVariablesSubscriber; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; -use eZ\Publish\Core\MVC\Symfony\View\GenericVariableProviderRegistry; -use eZ\Publish\Core\MVC\Symfony\View\View; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\SPI\MVC\View\VariableProvider; +use Ibexa\Contracts\Core\MVC\View\VariableProvider; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Event\PreContentViewEvent; +use Ibexa\Core\MVC\Symfony\EventListener\ContentViewTwigVariablesSubscriber; +use Ibexa\Core\MVC\Symfony\View\ContentView; +use Ibexa\Core\MVC\Symfony\View\GenericVariableProviderRegistry; +use Ibexa\Core\MVC\Symfony\View\View; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\Location; use PHPUnit\Framework\TestCase; final class ContentViewTwigVariablesSubscriberTest extends TestCase { /** - * @return \eZ\Publish\Core\MVC\Symfony\View\ContentView|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\MVC\Symfony\View\ContentView|\PHPUnit\Framework\MockObject\MockObject */ private function getContentViewMock(): ContentView { @@ -246,3 +246,5 @@ public function testWithProviderExpression(): void $subscriber->onPreContentView($event); } } + +class_alias(ContentViewTwigVariablesSubscriberTest::class, 'eZ\Publish\Core\MVC\Symfony\EventListener\Tests\ContentViewTwigVariablesSubscriberTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/EventListener/Tests/LanguageSwitchListenerTest.php b/tests/lib/MVC/Symfony/EventListener/LanguageSwitchListenerTest.php similarity index 86% rename from eZ/Publish/Core/MVC/Symfony/EventListener/Tests/LanguageSwitchListenerTest.php rename to tests/lib/MVC/Symfony/EventListener/LanguageSwitchListenerTest.php index 0f47c4f79b..dcadba49f0 100644 --- a/eZ/Publish/Core/MVC/Symfony/EventListener/Tests/LanguageSwitchListenerTest.php +++ b/tests/lib/MVC/Symfony/EventListener/LanguageSwitchListenerTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\EventListener\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\EventListener; -use eZ\Publish\Core\Helper\TranslationHelper; -use eZ\Publish\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent; -use eZ\Publish\Core\MVC\Symfony\EventListener\LanguageSwitchListener; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\Routing\RouteReference; +use Ibexa\Core\Helper\TranslationHelper; +use Ibexa\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent; +use Ibexa\Core\MVC\Symfony\EventListener\LanguageSwitchListener; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\Routing\RouteReference; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; @@ -82,3 +82,5 @@ public function testOnRouteReferenceGenerationNoTranslationSiteAccess() $this->assertFalse($routeReference->has('siteaccess')); } } + +class_alias(LanguageSwitchListenerTest::class, 'eZ\Publish\Core\MVC\Symfony\EventListener\Tests\LanguageSwitchListenerTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/EventListener/Tests/SiteAccessMatchListenerTest.php b/tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/EventListener/Tests/SiteAccessMatchListenerTest.php rename to tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php index 0c538f89a2..402cbd0c9d 100644 --- a/eZ/Publish/Core/MVC/Symfony/EventListener/Tests/SiteAccessMatchListenerTest.php +++ b/tests/lib/MVC/Symfony/EventListener/SiteAccessMatchListenerTest.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\EventListener\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\EventListener; -use eZ\Bundle\EzPublishCoreBundle\SiteAccess\SiteAccessMatcherRegistryInterface; -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\SerializerTrait; -use eZ\Publish\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; -use eZ\Publish\Core\MVC\Symfony\EventListener\SiteAccessMatchListener; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router; -use eZ\Publish\Core\MVC\Symfony\SiteAccessGroup; +use Ibexa\Bundle\Core\SiteAccess\SiteAccessMatcherRegistryInterface; +use Ibexa\Core\MVC\Symfony\Component\Serializer\SerializerTrait; +use Ibexa\Core\MVC\Symfony\Event\PostSiteAccessMatchEvent; +use Ibexa\Core\MVC\Symfony\EventListener\SiteAccessMatchListener; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; +use Ibexa\Core\MVC\Symfony\SiteAccessGroup; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; @@ -33,7 +33,7 @@ class SiteAccessMatchListenerTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $eventDispatcher; - /** @var \eZ\Publish\Core\MVC\Symfony\EventListener\SiteAccessMatchListener */ + /** @var \Ibexa\Core\MVC\Symfony\EventListener\SiteAccessMatchListener */ private $listener; protected function setUp(): void @@ -266,3 +266,5 @@ public function testOnKernelRequestUserHashWithOriginalRequest() $this->assertSame($siteAccess, $request->attributes->get('siteaccess')); } } + +class_alias(SiteAccessMatchListenerTest::class, 'eZ\Publish\Core\MVC\Symfony\EventListener\Tests\SiteAccessMatchListenerTest'); diff --git a/tests/lib/MVC/Symfony/FieldType/ImageAsset/ParameterProviderTest.php b/tests/lib/MVC/Symfony/FieldType/ImageAsset/ParameterProviderTest.php new file mode 100644 index 0000000000..3fb2a5ad16 --- /dev/null +++ b/tests/lib/MVC/Symfony/FieldType/ImageAsset/ParameterProviderTest.php @@ -0,0 +1,202 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\FieldType\ImageAsset; + +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\FieldType; +use Ibexa\Contracts\Core\Repository\FieldTypeService; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\FieldType\ImageAsset\Value as ImageAssetValue; +use Ibexa\Core\MVC\Symfony\FieldType\ImageAsset\ParameterProvider; +use Ibexa\Core\Repository\SiteAccessAware\Repository; +use PHPUnit\Framework\TestCase; + +class ParameterProviderTest extends TestCase +{ + /** @var \Ibexa\Core\Repository\SiteAccessAware\Repository|\PHPUnit\Framework\MockObject\MockObject */ + private $repository; + + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ + private $permissionsResolver; + + /** @var \Ibexa\Core\MVC\Symfony\FieldType\ImageAsset\ParameterProvider */ + private $parameterProvider; + + /** @var \Ibexa\Contracts\Core\Repository\FieldType|\PHPUnit\Framework\MockObject\MockObject */ + private $fieldType; + + protected function setUp(): void + { + $this->repository = $this->createMock(Repository::class); + $this->permissionsResolver = $this->createMock(PermissionResolver::class); + $this->fieldType = $this->createMock(FieldType::class); + + $this->repository + ->method('getPermissionResolver') + ->willReturn($this->permissionsResolver); + + $fieldTypeService = $this->createMock(FieldTypeService::class); + + $this->repository + ->method('getFieldTypeService') + ->willReturn($fieldTypeService); + + $fieldTypeService + ->method('getFieldType') + ->with('ezimageasset') + ->willReturn($this->fieldType); + + $this->parameterProvider = new ParameterProvider($this->repository); + } + + public function dataProviderForTestGetViewParameters(): array + { + return [ + [ContentInfo::STATUS_PUBLISHED, ['available' => true]], + [ContentInfo::STATUS_TRASHED, ['available' => false]], + ]; + } + + /** + * @dataProvider dataProviderForTestGetViewParameters + */ + public function testGetViewParameters($status, array $expected): void + { + $destinationContentId = 1; + + $this->fieldType + ->method('isEmptyValue') + ->willReturn(false); + + $closure = static function (Repository $repository) use ($destinationContentId) { + return $repository->getContentService()->loadContentInfo($destinationContentId); + }; + + $this->repository + ->method('sudo') + ->with($closure) + ->willReturn(new ContentInfo([ + 'status' => $status, + ])); + + $this->permissionsResolver + ->method('canUser') + ->willReturn(true); + + $actual = $this->parameterProvider->getViewParameters($this->createField($destinationContentId)); + + $this->assertEquals($expected, $actual); + } + + public function testGetViewParametersHandleNotFoundException(): void + { + $destinationContentId = 1; + + $this->fieldType + ->method('isEmptyValue') + ->willReturn(false); + + $closure = static function (Repository $repository) use ($destinationContentId) { + return $repository->getContentService()->loadContentInfo($destinationContentId); + }; + + $this->repository + ->expects($this->once()) + ->method('sudo') + ->with($closure) + ->willThrowException($this->createMock(NotFoundException::class)); + + $actual = $this->parameterProvider->getViewParameters( + $this->createField($destinationContentId) + ); + + $this->assertEquals([ + 'available' => false, + ], $actual); + } + + public function testGetViewParametersHandleUnauthorizedAccess(): void + { + $destinationContentId = 1; + + $this->fieldType + ->method('isEmptyValue') + ->willReturn(false); + + $contentInfo = $this->createMock(ContentInfo::class); + + $this->repository + ->method('sudo') + ->willReturn($contentInfo) + ; + + $this->permissionsResolver + ->expects($this->at(0)) + ->method('canUser') + ->with('content', 'read', $contentInfo) + ->willReturn(false) + ; + + $this->permissionsResolver + ->expects($this->at(1)) + ->method('canUser') + ->with('content', 'view_embed', $contentInfo) + ->willReturn(false) + ; + + $actual = $this->parameterProvider->getViewParameters( + $this->createField($destinationContentId) + ); + + $this->assertEquals([ + 'available' => false, + ], $actual); + } + + public function testGetViewParametersHandleEmptyValue(): void + { + $destinationContentId = 1; + + $this->fieldType + ->method('isEmptyValue') + ->willReturn(true); + + $contentInfo = $this->createMock(ContentInfo::class); + + $this->repository + ->method('sudo') + ->willReturn($contentInfo) + ; + + $actual = $this->parameterProvider->getViewParameters( + $this->createField($destinationContentId) + ); + + $this->assertEquals([ + 'available' => null, + ], $actual); + } + + /** + * @param int $destinationContentId + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Field + */ + private function createField(int $destinationContentId): Field + { + return new Field([ + 'value' => new ImageAssetValue($destinationContentId), + 'fieldTypeIdentifier' => 'ezimageasset', + ]); + } +} + +class_alias(ParameterProviderTest::class, 'eZ\Publish\Core\MVC\Symfony\FieldType\Tests\ImageAsset\ParameterProviderTest'); diff --git a/tests/lib/MVC/Symfony/FieldType/Relation/ParameterProviderTest.php b/tests/lib/MVC/Symfony/FieldType/Relation/ParameterProviderTest.php new file mode 100644 index 0000000000..6b89e5646e --- /dev/null +++ b/tests/lib/MVC/Symfony/FieldType/Relation/ParameterProviderTest.php @@ -0,0 +1,84 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\FieldType\Relation; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Base\Exceptions\UnauthorizedException; +use Ibexa\Core\FieldType\Relation\Value; +use Ibexa\Core\MVC\Symfony\FieldType\Relation\ParameterProvider; +use PHPUnit\Framework\TestCase; + +class ParameterProviderTest extends TestCase +{ + public function providerForTestGetViewParameters() + { + return [ + [ContentInfo::STATUS_DRAFT, ['available' => true]], + [ContentInfo::STATUS_PUBLISHED, ['available' => true]], + [ContentInfo::STATUS_TRASHED, ['available' => false]], + ]; + } + + /** + * @dataProvider providerForTestGetViewParameters + */ + public function testGetViewParameters($status, array $expected) + { + $contentServiceMock = $this->createMock(ContentService::class); + $contentServiceMock + ->method('loadContentInfo') + ->will(TestCase::returnValue( + new ContentInfo(['status' => $status]) + )); + + $parameterProvider = new ParameterProvider($contentServiceMock); + $parameters = $parameterProvider->getViewParameters(new Field([ + 'value' => new Value(123), + ])); + + TestCase::assertSame($parameters, $expected); + } + + public function testNotFoundGetViewParameters() + { + $contentId = 123; + + $contentServiceMock = $this->createMock(ContentService::class); + $contentServiceMock + ->method('loadContentInfo') + ->will(TestCase::throwException(new NotFoundException('ContentInfo', $contentId))); + + $parameterProvider = new ParameterProvider($contentServiceMock); + $parameters = $parameterProvider->getViewParameters(new Field([ + 'value' => new Value($contentId), + ])); + + TestCase::assertSame($parameters, ['available' => false]); + } + + public function testUnauthorizedGetViewParameters() + { + $contentId = 123; + + $contentServiceMock = $this->createMock(ContentService::class); + $contentServiceMock + ->method('loadContentInfo') + ->will(TestCase::throwException(new UnauthorizedException('content', 'read'))); + + $parameterProvider = new ParameterProvider($contentServiceMock); + $parameters = $parameterProvider->getViewParameters(new Field([ + 'value' => new Value($contentId), + ])); + + TestCase::assertSame($parameters, ['available' => false]); + } +} + +class_alias(ParameterProviderTest::class, 'eZ\Publish\Core\MVC\Symfony\FieldType\Tests\Relation\ParameterProviderTest'); diff --git a/tests/lib/MVC/Symfony/FieldType/RelationList/ParameterProviderTest.php b/tests/lib/MVC/Symfony/FieldType/RelationList/ParameterProviderTest.php new file mode 100644 index 0000000000..552668f84a --- /dev/null +++ b/tests/lib/MVC/Symfony/FieldType/RelationList/ParameterProviderTest.php @@ -0,0 +1,99 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\FieldType\RelationList; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\FieldType\RelationList\Value; +use Ibexa\Core\MVC\Symfony\FieldType\RelationList\ParameterProvider; +use PHPUnit\Framework\TestCase; + +class ParameterProviderTest extends TestCase +{ + public function providerForTestGetViewParameters() + { + return [ + [[123, 456, 789], ['available' => [123 => true, 456 => true, 789 => false]]], + [[123, 456], ['available' => [123 => true, 456 => true]]], + [[789], ['available' => [789 => false]]], + [[], ['available' => []]], + ]; + } + + /** + * @dataProvider providerForTestGetViewParameters + */ + public function testGetViewParameters(array $desinationContentIds, array $expected) + { + $contentServiceMock = $this->createMock(ContentService::class); + $contentServiceMock + ->method('loadContentInfoList') + ->with($desinationContentIds) + ->will($this->returnCallback(static function ($arg) { + $return = []; + if (in_array(123, $arg)) { + $return[123] = new ContentInfo(['status' => ContentInfo::STATUS_DRAFT]); + } + + if (in_array(456, $arg)) { + $return[456] = new ContentInfo(['status' => ContentInfo::STATUS_PUBLISHED]); + } + + if (in_array(789, $arg)) { + $return[789] = new ContentInfo(['status' => ContentInfo::STATUS_TRASHED]); + } + + return $return; + })); + + $parameterProvider = new ParameterProvider($contentServiceMock); + $parameters = $parameterProvider->getViewParameters(new Field([ + 'value' => new Value($desinationContentIds), + ])); + + TestCase::assertSame($parameters, $expected); + } + + public function testNotFoundGetViewParameters() + { + $contentId = 123; + + $contentServiceMock = $this->createMock(ContentService::class); + $contentServiceMock + ->method('loadContentInfoList') + ->with([$contentId]) + ->willReturn([]); + + $parameterProvider = new ParameterProvider($contentServiceMock); + $parameters = $parameterProvider->getViewParameters(new Field([ + 'value' => new Value([$contentId]), + ])); + + TestCase::assertSame($parameters, ['available' => [$contentId => false]]); + } + + public function testUnauthorizedGetViewParameters() + { + $contentId = 123; + + $contentServiceMock = $this->createMock(ContentService::class); + $contentServiceMock + ->method('loadContentInfoList') + ->with([$contentId]) + ->willReturn([]); + + $parameterProvider = new ParameterProvider($contentServiceMock); + $parameters = $parameterProvider->getViewParameters(new Field([ + 'value' => new Value([$contentId]), + ])); + + TestCase::assertSame($parameters, ['available' => [$contentId => false]]); + } +} + +class_alias(ParameterProviderTest::class, 'eZ\Publish\Core\MVC\Symfony\FieldType\Tests\RelationList\ParameterProviderTest'); diff --git a/tests/lib/MVC/Symfony/FieldType/User/ParameterProviderTest.php b/tests/lib/MVC/Symfony/FieldType/User/ParameterProviderTest.php new file mode 100644 index 0000000000..6f47f029fa --- /dev/null +++ b/tests/lib/MVC/Symfony/FieldType/User/ParameterProviderTest.php @@ -0,0 +1,96 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\FieldType\User; + +use DateInterval; +use DateTimeImmutable; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\User\PasswordInfo; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Core\FieldType\User\Value; +use Ibexa\Core\MVC\Symfony\FieldType\User\ParameterProvider; +use PHPUnit\Framework\TestCase; + +class ParameterProviderTest extends TestCase +{ + private const EXAMPLE_USER_ID = 1; + + /** @var \Ibexa\Contracts\Core\Repository\UserService|\PHPUnit\Framework\MockObject\MockObject */ + private $userService; + + /** @var \Ibexa\Contracts\Core\Repository\Values\User\User|\PHPUnit\Framework\MockObject\MockObject */ + private $user; + + /** @var \Ibexa\Core\MVC\Symfony\FieldType\User\ParameterProvider */ + private $parameterProvider; + + protected function setUp(): void + { + $this->user = $this->createMock(User::class); + + $this->userService = $this->createMock(UserService::class); + $this->userService + ->method('loadUser') + ->with(self::EXAMPLE_USER_ID, []) + ->willReturn($this->user); + + $this->parameterProvider = new ParameterProvider($this->userService); + } + + /** + * @requires PHP < 8.1 + */ + public function testGetViewParameters(): void + { + $passwordExpiresIn = 14; + $passwordExpiresAt = (new DateTimeImmutable())->add(new DateInterval('P14D')); + + $this->userService + ->method('getPasswordInfo') + ->with($this->user) + ->willReturn(new PasswordInfo($passwordExpiresAt)); + + $parameters = $this->parameterProvider->getViewParameters( + $this->createFieldMock(self::EXAMPLE_USER_ID) + ); + + $this->assertFalse($parameters['is_password_expired']); + $this->assertEquals($passwordExpiresAt, $parameters['password_expires_at']); + $this->assertEquals($passwordExpiresIn, $parameters['password_expires_in']->days); + } + + public function testGetViewParametersWhenPasswordExpirationDateIsNull(): void + { + $field = $this->createFieldMock(self::EXAMPLE_USER_ID); + + $this->userService + ->method('getPasswordInfo') + ->with($this->user) + ->willReturn(new PasswordInfo()); + + $this->assertEquals([ + 'is_password_expired' => false, + 'password_expires_at' => null, + 'password_expires_in' => null, + ], $this->parameterProvider->getViewParameters($field)); + } + + private function createFieldMock(int $userId): Field + { + $field = $this->createMock(Field::class); + $field->method('__get')->with('value')->willReturn(new Value([ + 'contentId' => $userId, + ])); + + return $field; + } +} + +class_alias(ParameterProviderTest::class, 'eZ\Publish\Core\MVC\Symfony\FieldType\Tests\User\ParameterProviderTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/View/ParameterProvider/LocaleParameterProviderTest.php b/tests/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProviderTest.php similarity index 83% rename from eZ/Publish/Core/MVC/Symfony/FieldType/Tests/View/ParameterProvider/LocaleParameterProviderTest.php rename to tests/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProviderTest.php index 06383cf02a..c545bba678 100644 --- a/eZ/Publish/Core/MVC/Symfony/FieldType/Tests/View/ParameterProvider/LocaleParameterProviderTest.php +++ b/tests/lib/MVC/Symfony/FieldType/View/ParameterProvider/LocaleParameterProviderTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\FieldType\Tests\View\ParameterProvider; +namespace Ibexa\Tests\Core\MVC\Symfony\FieldType\View\ParameterProvider; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProvider\LocaleParameterProvider; -use eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\MVC\Symfony\FieldType\View\ParameterProvider\LocaleParameterProvider; +use Ibexa\Core\MVC\Symfony\Locale\LocaleConverterInterface; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\ParameterBag; use Symfony\Component\HttpFoundation\Request; @@ -73,3 +73,5 @@ protected function getLocaleConverterMock() return $mock; } } + +class_alias(LocaleParameterProviderTest::class, 'eZ\Publish\Core\MVC\Symfony\FieldType\Tests\View\ParameterProvider\LocaleParameterProviderTest'); diff --git a/tests/lib/MVC/Symfony/FieldType/View/ParameterProviderRegistryTest.php b/tests/lib/MVC/Symfony/FieldType/View/ParameterProviderRegistryTest.php new file mode 100644 index 0000000000..13060d4e16 --- /dev/null +++ b/tests/lib/MVC/Symfony/FieldType/View/ParameterProviderRegistryTest.php @@ -0,0 +1,46 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\FieldType\View; + +use Ibexa\Core\MVC\Symfony\FieldType\View\ParameterProviderInterface; +use Ibexa\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistry::setParameterProvider + */ +class ParameterProviderRegistryTest extends TestCase +{ + public function testSetHasParameterProvider() + { + $registry = new ParameterProviderRegistry(); + $this->assertFalse($registry->hasParameterProvider('foo')); + $registry->setParameterProvider( + $this->createMock(ParameterProviderInterface::class), + 'foo' + ); + $this->assertTrue($registry->hasParameterProvider('foo')); + } + + public function testGetParameterProviderFail() + { + $this->expectException(\InvalidArgumentException::class); + + $registry = new ParameterProviderRegistry(); + $registry->getParameterProvider('foo'); + } + + public function testGetParameterProvider() + { + $provider = $this->createMock(ParameterProviderInterface::class); + $registry = new ParameterProviderRegistry(); + $registry->setParameterProvider($provider, 'foo'); + $this->assertSame($provider, $registry->getParameterProvider('foo')); + } +} + +class_alias(ParameterProviderRegistryTest::class, 'eZ\Publish\Core\MVC\Symfony\FieldType\Tests\View\ParameterProviderRegistryTest'); diff --git a/tests/lib/MVC/Symfony/Locale/LocaleConverterTest.php b/tests/lib/MVC/Symfony/Locale/LocaleConverterTest.php new file mode 100644 index 0000000000..826db2d144 --- /dev/null +++ b/tests/lib/MVC/Symfony/Locale/LocaleConverterTest.php @@ -0,0 +1,125 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Locale; + +use Ibexa\Core\MVC\Symfony\Locale\LocaleConverter; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; + +/** + * @covers \Ibexa\Core\MVC\Symfony\Locale\LocaleConverter + */ +final class LocaleConverterTest extends TestCase +{ + private LocaleConverter $localeConverter; + + /** @var \Psr\Log\LoggerInterface&\PHPUnit\Framework\MockObject\MockObject */ + private LoggerInterface $logger; + + /** + * @var array{ + * array{ + * string, + * string|null, + * } + * } + */ + private array $conversionMap; + + protected function setUp(): void + { + parent::setUp(); + + $this->conversionMap = [ + 'eng-GB' => 'en_GB', + 'eng-US' => 'en_US', + 'fre-FR' => 'fr_FR', + 'ger-DE' => 'de_DE', + 'nor-NO' => 'no_NO', + 'cro-HR' => 'hr_HR', + ]; + + $this->logger = $this->createMock(LoggerInterface::class); + $this->localeConverter = new LocaleConverter($this->conversionMap, $this->logger); + } + + /** + * @dataProvider convertToPOSIXProvider + */ + public function testConvertToPOSIX(string $repositoryLocale, ?string $expected): void + { + if ($expected === null) { + $this->logger + ->expects($this->once()) + ->method('warning'); + } + + self::assertSame($expected, $this->localeConverter->convertToPOSIX($repositoryLocale)); + } + + public function convertToPOSIXProvider(): array + { + return [ + ['eng-GB', 'en_GB'], + ['eng-US', 'en_US'], + ['fre-FR', 'fr_FR'], + ['chi-CN', null], + ['epo-EO', null], + ['nor-NO', 'no_NO'], + ]; + } + + /** + * @dataProvider convertToRepositoryProvider + */ + public function testConvertToEz(string $posixLocale, ?string $expected): void + { + if ($expected === null) { + $this->logger + ->expects($this->once()) + ->method('warning'); + } + + self::assertSame($expected, $this->localeConverter->convertToEz($posixLocale)); + } + + /** + * @dataProvider convertToRepositoryProvider + */ + public function testConvertToRepository(string $posixLocale, ?string $expected): void + { + if ($expected === null) { + $this->logger + ->expects($this->once()) + ->method('warning'); + } + + self::assertSame($expected, $this->localeConverter->convertToRepository($posixLocale)); + } + + /** + * @return array{ + * array{ + * string, + * string|null, + * } + * } + */ + public function convertToRepositoryProvider(): array + { + return [ + ['en_GB', 'eng-GB'], + ['en_US', 'eng-US'], + ['fr_FR', 'fre-FR'], + ['zh-CN', null], + ['eo', null], + ['no_NO', 'nor-NO'], + ]; + } +} + +class_alias(LocaleConverterTest::class, 'eZ\Publish\Core\MVC\Symfony\Locale\Tests\LocaleConverterTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Locale/Tests/UserLanguagePreferenceProviderTest.php b/tests/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderTest.php similarity index 87% rename from eZ/Publish/Core/MVC/Symfony/Locale/Tests/UserLanguagePreferenceProviderTest.php rename to tests/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderTest.php index 5a6508efd6..cd58c29164 100644 --- a/eZ/Publish/Core/MVC/Symfony/Locale/Tests/UserLanguagePreferenceProviderTest.php +++ b/tests/lib/MVC/Symfony/Locale/UserLanguagePreferenceProviderTest.php @@ -6,12 +6,12 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Locale\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Locale; -use eZ\Publish\API\Repository\UserPreferenceService; -use eZ\Publish\API\Repository\Values\UserPreference\UserPreference; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\MVC\Symfony\Locale\UserLanguagePreferenceProvider; +use Ibexa\Contracts\Core\Repository\UserPreferenceService; +use Ibexa\Contracts\Core\Repository\Values\UserPreference\UserPreference; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\MVC\Symfony\Locale\UserLanguagePreferenceProvider; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\HeaderBag; use Symfony\Component\HttpFoundation\Request; @@ -24,13 +24,13 @@ class UserLanguagePreferenceProviderTest extends TestCase private const LANGUAGE_PREFERENCE_NAME = 'language'; private const LANGUAGE_PREFERENCE_VALUE = 'no'; - /** @var \eZ\Publish\Core\MVC\Symfony\Locale\UserLanguagePreferenceProviderInterface */ + /** @var \Ibexa\Core\MVC\Symfony\Locale\UserLanguagePreferenceProviderInterface */ private $userLanguagePreferenceProvider; /** @var \PHPUnit\Framework\MockObject\MockObject|\Symfony\Component\HttpFoundation\RequestStack */ private $requestStackMock; - /** @var \eZ\Publish\API\Repository\UserPreferenceService */ + /** @var \Ibexa\Contracts\Core\Repository\UserPreferenceService */ private $userPreferenceServiceMock; protected function setUp(): void @@ -163,9 +163,11 @@ public function providerForTestGetPreferredLanguagesWithUserPreferredLanguage(): private function getLanguageCodesMap(): array { $config = Yaml::parseFile( - realpath(dirname(__DIR__, 6) . '/Bundle/EzPublishCoreBundle/Resources/config/locale.yml') + realpath(dirname(__DIR__, 5) . '/src/bundle/Core/Resources/config/locale.yml') ); - return $config['parameters']['ezpublish.locale.browser_map']; + return $config['parameters']['ibexa.locale.browser_map']; } } + +class_alias(UserLanguagePreferenceProviderTest::class, 'eZ\Publish\Core\MVC\Symfony\Locale\Tests\UserLanguagePreferenceProviderTest'); diff --git a/tests/lib/MVC/Symfony/Matcher/ContentBased/BaseTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/BaseTest.php new file mode 100644 index 0000000000..cd25aa307f --- /dev/null +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/BaseTest.php @@ -0,0 +1,119 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased; + +use Ibexa\Contracts\Core\Persistence\User\Handler as SPIUserHandler; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\View\Provider\Configured; +use Ibexa\Core\Repository\Mapper\RoleDomainMapper; +use Ibexa\Core\Repository\Permission\LimitationService; +use Ibexa\Core\Repository\Permission\PermissionResolver; +use Ibexa\Core\Repository\Repository; +use PHPUnit\Framework\TestCase; + +abstract class BaseTest extends TestCase +{ + /** @var \PHPUnit\Framework\MockObject\MockObject */ + protected $repositoryMock; + + protected function setUp(): void + { + parent::setUp(); + $this->repositoryMock = $this->getRepositoryMock(); + } + + /** + * @param array $matchingConfig + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + protected function getPartiallyMockedViewProvider(array $matchingConfig = []) + { + return $this + ->getMockBuilder(Configured::class) + ->setConstructorArgs( + [ + $this->repositoryMock, + $matchingConfig, + ] + ) + ->setMethods(['getMatcher']) + ->getMock(); + } + + /** + * @return \PHPUnit\Framework\MockObject\MockObject + */ + protected function getRepositoryMock() + { + $repositoryClass = Repository::class; + + return $this + ->getMockBuilder($repositoryClass) + ->disableOriginalConstructor() + ->setMethods( + array_diff( + get_class_methods($repositoryClass), + ['sudo'] + ) + ) + ->getMock(); + } + + /** + * @param array $properties + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + protected function getLocationMock(array $properties = []) + { + return $this + ->getMockBuilder(Location::class) + ->setConstructorArgs([$properties]) + ->getMockForAbstractClass(); + } + + /** + * @param array $properties + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + protected function getContentInfoMock(array $properties = []) + { + return $this-> + getMockBuilder(ContentInfo::class) + ->setConstructorArgs([$properties]) + ->getMockForAbstractClass(); + } + + protected function getPermissionResolverMock() + { + $configResolverMock = $this->createMock(ConfigResolverInterface::class); + $configResolverMock + ->method('getParameter') + ->with('anonymous_user_id') + ->willReturn(10); + + return $this + ->getMockBuilder(PermissionResolver::class) + ->setMethods(null) + ->setConstructorArgs( + [ + $this->createMock(RoleDomainMapper::class), + $this->createMock(LimitationService::class), + $this->createMock(SPIUserHandler::class), + $configResolverMock, + [], + ] + ) + ->getMock(); + } +} + +class_alias(BaseTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\BaseTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/DepthTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/DepthTest.php similarity index 78% rename from eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/DepthTest.php rename to tests/lib/MVC/Symfony/Matcher/ContentBased/DepthTest.php index 5d0df08f5c..33694b809b 100644 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/DepthTest.php +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/DepthTest.php @@ -4,16 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased; +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Depth as DepthMatcher; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Depth as DepthMatcher; class DepthTest extends BaseTest { - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Depth */ + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Depth */ private $matcher; protected function setUp(): void @@ -24,11 +24,11 @@ protected function setUp(): void /** * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Depth::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Depth::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig * * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Values\Content\Location $location + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location * @param bool $expectedResult */ public function testMatchLocation($matchingConfig, Location $location, $expectedResult) @@ -75,12 +75,12 @@ public function matchLocationProvider() /** * @dataProvider matchContentInfoProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Depth::matchContentInfo - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * @covers \eZ\Publish\Core\MVC\RepositoryAware::setRepository + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Depth::matchContentInfo + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\RepositoryAware::setRepository * * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Repository $repository + * @param \Ibexa\Contracts\Core\Repository\Repository $repository * @param bool $expectedResult */ public function testMatchContentInfo($matchingConfig, Repository $repository, $expectedResult) @@ -151,3 +151,5 @@ private function generateRepositoryMockForDepth($depth) return $repository; } } + +class_alias(DepthTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\DepthTest'); diff --git a/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTest.php new file mode 100644 index 0000000000..82a5efe9f5 --- /dev/null +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTest.php @@ -0,0 +1,130 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\Id; + +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Content as ContentIdMatcher; +use Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\BaseTest; + +class ContentTest extends BaseTest +{ + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Content */ + private $matcher; + + protected function setUp(): void + { + parent::setUp(); + $this->matcher = new ContentIdMatcher(); + } + + /** + * @dataProvider matchLocationProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Content::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * + * @param int|int[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param bool $expectedResult + */ + public function testMatchLocation($matchingConfig, Location $location, $expectedResult) + { + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame($expectedResult, $this->matcher->matchLocation($location)); + } + + public function matchLocationProvider() + { + return [ + [ + 123, + $this->generateLocationForContentId(123), + true, + ], + [ + 123, + $this->generateLocationForContentId(456), + false, + ], + [ + [123, 789], + $this->generateLocationForContentId(456), + false, + ], + [ + [123, 789], + $this->generateLocationForContentId(789), + true, + ], + ]; + } + + /** + * Generates a Location mock in respect of a given content Id. + * + * @param int $contentId + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + private function generateLocationForContentId($contentId) + { + $location = $this->getLocationMock(); + $location + ->expects($this->any()) + ->method('getContentInfo') + ->will( + $this->returnValue( + $this->getContentInfoMock(['id' => $contentId]) + ) + ); + + return $location; + } + + /** + * @dataProvider matchContentInfoProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Content::matchContentInfo + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * + * @param int|int[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param bool $expectedResult + */ + public function testMatchContentInfo($matchingConfig, ContentInfo $contentInfo, $expectedResult) + { + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame($expectedResult, $this->matcher->matchContentInfo($contentInfo)); + } + + public function matchContentInfoProvider() + { + return [ + [ + 123, + $this->getContentInfoMock(['id' => 123]), + true, + ], + [ + 123, + $this->getContentInfoMock(['id' => 456]), + false, + ], + [ + [123, 789], + $this->getContentInfoMock(['id' => 456]), + false, + ], + [ + [123, 789], + $this->getContentInfoMock(['id' => 789]), + true, + ], + ]; + } +} + +class_alias(ContentTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id\ContentTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ContentTypeGroupTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroupTest.php similarity index 80% rename from eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ContentTypeGroupTest.php rename to tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroupTest.php index c0c87f25c1..7ee473a299 100644 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ContentTypeGroupTest.php +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeGroupTest.php @@ -4,18 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id; +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\Id; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentTypeGroup as ContentTypeGroupIdMatcher; -use eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\BaseTest; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentTypeGroup as ContentTypeGroupIdMatcher; +use Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\BaseTest; class ContentTypeGroupTest extends BaseTest { - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentTypeGroup */ + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentTypeGroup */ private $matcher; protected function setUp(): void @@ -26,11 +26,11 @@ protected function setUp(): void /** * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentTypeGroup::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentTypeGroup::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig * * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Repository $repository + * @param \Ibexa\Contracts\Core\Repository\Repository $repository * @param bool $expectedResult */ public function testMatchLocation($matchingConfig, Repository $repository, $expectedResult) @@ -97,11 +97,11 @@ private function generateLocationMock() /** * @dataProvider matchContentInfoProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentTypeGroup::matchContentInfo - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentTypeGroup::matchContentInfo + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig * * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Repository $repository + * @param \Ibexa\Contracts\Core\Repository\Repository $repository * @param bool $expectedResult */ public function testMatchContentInfo($matchingConfig, Repository $repository, $expectedResult) @@ -186,3 +186,5 @@ private function generateRepositoryMockForContentTypeGroupId($contentTypeGroupId return $repository; } } + +class_alias(ContentTypeGroupTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id\ContentTypeGroupTest'); diff --git a/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeTest.php new file mode 100644 index 0000000000..2b5cb9e275 --- /dev/null +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ContentTypeTest.php @@ -0,0 +1,152 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\Id; + +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentType as ContentTypeIdMatcher; +use Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\BaseTest; + +class ContentTypeTest extends BaseTest +{ + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentType */ + private $matcher; + + protected function setUp(): void + { + parent::setUp(); + $this->matcher = new ContentTypeIdMatcher(); + } + + /** + * @dataProvider matchLocationProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentType::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * + * @param int|int[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param bool $expectedResult + */ + public function testMatchLocation($matchingConfig, Location $location, $expectedResult) + { + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame($expectedResult, $this->matcher->matchLocation($location)); + } + + public function matchLocationProvider() + { + $data = []; + + $data[] = [ + 123, + $this->generateLocationForContentType(123), + true, + ]; + + $data[] = [ + 123, + $this->generateLocationForContentType(456), + false, + ]; + + $data[] = [ + [123, 789], + $this->generateLocationForContentType(456), + false, + ]; + + $data[] = [ + [123, 789], + $this->generateLocationForContentType(789), + true, + ]; + + return $data; + } + + /** + * Generates a Location object in respect of a given content type identifier. + * + * @param int $contentTypeId + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + private function generateLocationForContentType($contentTypeId) + { + $location = $this->getLocationMock(); + $location + ->expects($this->any()) + ->method('getContentInfo') + ->will( + $this->returnValue( + $this->generateContentInfoForContentType($contentTypeId) + ) + ); + + return $location; + } + + /** + * Generates a ContentInfo object in respect of a given content type identifier. + * + * @param int $contentTypeId + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + private function generateContentInfoForContentType($contentTypeId) + { + return $this->getContentInfoMock(['contentTypeId' => $contentTypeId]); + } + + /** + * @dataProvider matchContentInfoProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ContentType::matchContentInfo + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * + * @param int|int[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param bool $expectedResult + */ + public function testMatchContentInfo($matchingConfig, ContentInfo $contentInfo, $expectedResult) + { + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame($expectedResult, $this->matcher->matchContentInfo($contentInfo)); + } + + public function matchContentInfoProvider() + { + $data = []; + + $data[] = [ + 123, + $this->generateContentInfoForContentType(123), + true, + ]; + + $data[] = [ + 123, + $this->generateContentInfoForContentType(456), + false, + ]; + + $data[] = [ + [123, 789], + $this->generateContentInfoForContentType(456), + false, + ]; + + $data[] = [ + [123, 789], + $this->generateContentInfoForContentType(789), + true, + ]; + + return $data; + } +} + +class_alias(ContentTypeTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id\ContentTypeTest'); diff --git a/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/LocationTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/LocationTest.php new file mode 100644 index 0000000000..00015a98cd --- /dev/null +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/LocationTest.php @@ -0,0 +1,108 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\Id; + +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Location as LocationIdMatcher; +use Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\BaseTest; + +class LocationTest extends BaseTest +{ + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Location */ + private $matcher; + + protected function setUp(): void + { + parent::setUp(); + $this->matcher = new LocationIdMatcher(); + } + + /** + * @dataProvider matchLocationProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Location::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * + * @param int|int[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param bool $expectedResult + */ + public function testMatchLocation($matchingConfig, Location $location, $expectedResult) + { + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame($expectedResult, $this->matcher->matchLocation($location)); + } + + public function matchLocationProvider() + { + return [ + [ + 123, + $this->getLocationMock(['id' => 123]), + true, + ], + [ + 123, + $this->getLocationMock(['id' => 456]), + false, + ], + [ + [123, 789], + $this->getLocationMock(['id' => 456]), + false, + ], + [ + [123, 789], + $this->getLocationMock(['id' => 789]), + true, + ], + ]; + } + + /** + * @dataProvider matchContentInfoProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Location::matchContentInfo + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * + * @param int|int[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param bool $expectedResult + */ + public function testMatchContentInfo($matchingConfig, ContentInfo $contentInfo, $expectedResult) + { + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame($expectedResult, $this->matcher->matchContentInfo($contentInfo)); + } + + public function matchContentInfoProvider() + { + return [ + [ + 123, + $this->getContentInfoMock(['mainLocationId' => 123]), + true, + ], + [ + 123, + $this->getContentInfoMock(['mainLocationId' => 456]), + false, + ], + [ + [123, 789], + $this->getContentInfoMock(['mainLocationId' => 456]), + false, + ], + [ + [123, 789], + $this->getContentInfoMock(['mainLocationId' => 789]), + true, + ], + ]; + } +} + +class_alias(LocationTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id\LocationTest'); diff --git a/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentTypeTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentTypeTest.php new file mode 100644 index 0000000000..9c356b4820 --- /dev/null +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentContentTypeTest.php @@ -0,0 +1,148 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\Id; + +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentContentType as ParentContentTypeMatcher; +use Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\BaseTest; + +class ParentContentTypeTest extends BaseTest +{ + private const EXAMPLE_LOCATION_ID = 54; + private const EXAMPLE_PARENT_LOCATION_ID = 2; + + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentContentType */ + private $matcher; + + protected function setUp(): void + { + parent::setUp(); + $this->matcher = new ParentContentTypeMatcher(); + } + + /** + * Returns a Repository mock configured to return the appropriate ContentType object with given id. + * + * @param int $contentTypeId + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + private function generateRepositoryMockForContentTypeId($contentTypeId) + { + $parentContentInfo = $this->getContentInfoMock([ + 'contentTypeId' => $contentTypeId, + 'mainLocationId' => self::EXAMPLE_LOCATION_ID, + ]); + + $parentLocation = $this->getLocationMock([ + 'parentLocationId' => self::EXAMPLE_PARENT_LOCATION_ID, + ]); + $parentLocation->expects($this->once()) + ->method('getContentInfo') + ->will( + $this->returnValue($parentContentInfo) + ); + + $locationServiceMock = $this->createMock(LocationService::class); + + $locationServiceMock->expects($this->atLeastOnce()) + ->method('loadLocation') + ->will( + $this->returnValue($parentLocation) + ); + // The following is used in the case of a match by contentInfo + $locationServiceMock->expects($this->any()) + ->method('loadLocation') + ->will( + $this->returnValue($this->getLocationMock()) + ); + + $repository = $this->getRepositoryMock(); + $repository + ->expects($this->any()) + ->method('getLocationService') + ->will($this->returnValue($locationServiceMock)); + $repository + ->expects($this->any()) + ->method('getPermissionResolver') + ->will($this->returnValue($this->getPermissionResolverMock())); + + return $repository; + } + + /** + * @dataProvider matchLocationProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentContentType::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\RepositoryAware::setRepository + * + * @param int|int[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param bool $expectedResult + */ + public function testMatchLocation($matchingConfig, Repository $repository, $expectedResult) + { + $this->matcher->setRepository($repository); + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame( + $expectedResult, + $this->matcher->matchLocation($this->getLocationMock(['parentLocationId' => self::EXAMPLE_LOCATION_ID])) + ); + } + + public function matchLocationProvider() + { + return [ + [ + 123, + $this->generateRepositoryMockForContentTypeId(123), + true, + ], + [ + 123, + $this->generateRepositoryMockForContentTypeId(456), + false, + ], + [ + [123, 789], + $this->generateRepositoryMockForContentTypeId(456), + false, + ], + [ + [123, 789], + $this->generateRepositoryMockForContentTypeId(789), + true, + ], + ]; + } + + /** + * @dataProvider matchLocationProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentContentType::matchContentInfo + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\RepositoryAware::setRepository + * + * @param int|int[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param bool $expectedResult + */ + public function testMatchContentInfo($matchingConfig, Repository $repository, $expectedResult) + { + $this->matcher->setRepository($repository); + $this->matcher->setMatchingConfig($matchingConfig); + + $this->assertSame( + $expectedResult, + $this->matcher->matchContentInfo($this->getContentInfoMock([ + 'mainLocationId' => self::EXAMPLE_LOCATION_ID, + ])) + ); + } +} + +class_alias(ParentContentTypeTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id\ParentContentTypeTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ParentLocationTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentLocationTest.php similarity index 76% rename from eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ParentLocationTest.php rename to tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentLocationTest.php index 8975b30794..cf1d0434f0 100644 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/ContentBased/Id/ParentLocationTest.php +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/ParentLocationTest.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id; +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\Id; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentLocation as ParentLocationIdMatcher; -use eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\BaseTest; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentLocation as ParentLocationIdMatcher; +use Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\BaseTest; class ParentLocationTest extends BaseTest { - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentLocation */ + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentLocation */ private $matcher; protected function setUp(): void @@ -25,11 +25,11 @@ protected function setUp(): void /** * @dataProvider matchLocationProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentLocation::matchLocation - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentLocation::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig * * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Values\Content\Location $location + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location * @param bool $expectedResult */ public function testMatchLocation($matchingConfig, Location $location, $expectedResult) @@ -66,12 +66,12 @@ public function matchLocationProvider() /** * @dataProvider matchContentInfoProvider - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentLocation::matchContentInfo - * @covers \eZ\Publish\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig - * @covers \eZ\Publish\Core\MVC\RepositoryAware::setRepository + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\ParentLocation::matchContentInfo + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\RepositoryAware::setRepository * * @param int|int[] $matchingConfig - * @param \eZ\Publish\API\Repository\Repository $repository + * @param \Ibexa\Contracts\Core\Repository\Repository $repository * @param bool $expectedResult */ public function testMatchContentInfo($matchingConfig, Repository $repository, $expectedResult) @@ -142,3 +142,5 @@ private function generateRepositoryMockForParentLocationId($parentLocationId) return $repository; } } + +class_alias(ParentLocationTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id\ParentLocationTest'); diff --git a/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/RemoteTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/RemoteTest.php new file mode 100644 index 0000000000..7c9211c354 --- /dev/null +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/RemoteTest.php @@ -0,0 +1,108 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\Id; + +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Remote as RemoteIdMatcher; +use Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\BaseTest; + +class RemoteTest extends BaseTest +{ + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Remote */ + private $matcher; + + protected function setUp(): void + { + parent::setUp(); + $this->matcher = new RemoteIdMatcher(); + } + + /** + * @dataProvider matchLocationProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Remote::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * + * @param string|string[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param bool $expectedResult + */ + public function testMatchLocation($matchingConfig, Location $location, $expectedResult) + { + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame($expectedResult, $this->matcher->matchLocation($location)); + } + + public function matchLocationProvider() + { + return [ + [ + 'foo', + $this->getLocationMock(['remoteId' => 'foo']), + true, + ], + [ + 'foo', + $this->getLocationMock(['remoteId' => 'bar']), + false, + ], + [ + ['foo', 'baz'], + $this->getLocationMock(['remoteId' => 'bar']), + false, + ], + [ + ['foo', 'baz'], + $this->getLocationMock(['remoteId' => 'baz']), + true, + ], + ]; + } + + /** + * @dataProvider matchContentInfoProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Remote::matchContentInfo + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * + * @param string|string[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param bool $expectedResult + */ + public function testMatchContentInfo($matchingConfig, ContentInfo $contentInfo, $expectedResult) + { + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame($expectedResult, $this->matcher->matchContentInfo($contentInfo)); + } + + public function matchContentInfoProvider() + { + return [ + [ + 'foo', + $this->getContentInfoMock(['remoteId' => 'foo']), + true, + ], + [ + 'foo', + $this->getContentInfoMock(['remoteId' => 'bar']), + false, + ], + [ + ['foo', 'baz'], + $this->getContentInfoMock(['remoteId' => 'bar']), + false, + ], + [ + ['foo', 'baz'], + $this->getContentInfoMock(['remoteId' => 'baz']), + true, + ], + ]; + } +} + +class_alias(RemoteTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id\RemoteTest'); diff --git a/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/SectionTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/SectionTest.php new file mode 100644 index 0000000000..30dc941dd9 --- /dev/null +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/Id/SectionTest.php @@ -0,0 +1,130 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\Id; + +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Section as SectionIdMatcher; +use Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\BaseTest; + +class SectionTest extends BaseTest +{ + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Section */ + private $matcher; + + protected function setUp(): void + { + parent::setUp(); + $this->matcher = new SectionIdMatcher(); + } + + /** + * @dataProvider matchLocationProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * + * @param int|int[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Location $location + * @param bool $expectedResult + */ + public function testMatchLocation($matchingConfig, Location $location, $expectedResult) + { + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame($expectedResult, $this->matcher->matchLocation($location)); + } + + public function matchLocationProvider() + { + return [ + [ + 123, + $this->generateLocationForSectionId(123), + true, + ], + [ + 123, + $this->generateLocationForSectionId(456), + false, + ], + [ + [123, 789], + $this->generateLocationForSectionId(456), + false, + ], + [ + [123, 789], + $this->generateLocationForSectionId(789), + true, + ], + ]; + } + + /** + * Generates a Location mock in respect of a given content Id. + * + * @param int $sectionId + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + private function generateLocationForSectionId($sectionId) + { + $location = $this->getLocationMock(); + $location + ->expects($this->any()) + ->method('getContentInfo') + ->will( + $this->returnValue( + $this->getContentInfoMock(['sectionId' => $sectionId]) + ) + ); + + return $location; + } + + /** + * @dataProvider matchContentInfoProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Id\Section::matchContentInfo + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * + * @param int|int[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo + * @param bool $expectedResult + */ + public function testMatchContentInfo($matchingConfig, ContentInfo $contentInfo, $expectedResult) + { + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame($expectedResult, $this->matcher->matchContentInfo($contentInfo)); + } + + public function matchContentInfoProvider() + { + return [ + [ + 123, + $this->getContentInfoMock(['sectionId' => 123]), + true, + ], + [ + 123, + $this->getContentInfoMock(['sectionId' => 456]), + false, + ], + [ + [123, 789], + $this->getContentInfoMock(['sectionId' => 456]), + false, + ], + [ + [123, 789], + $this->getContentInfoMock(['sectionId' => 789]), + true, + ], + ]; + } +} + +class_alias(SectionTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Id\SectionTest'); diff --git a/tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ContentTypeTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ContentTypeTest.php new file mode 100644 index 0000000000..085fe2d57e --- /dev/null +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ContentTypeTest.php @@ -0,0 +1,183 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\Identifier; + +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ContentType as ContentTypeIdentifierMatcher; +use Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\BaseTest; + +class ContentTypeTest extends BaseTest +{ + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ContentType */ + private $matcher; + + protected function setUp(): void + { + parent::setUp(); + $this->matcher = new ContentTypeIdentifierMatcher(); + } + + /** + * @dataProvider matchLocationProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ContentType::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * + * @param string|string[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param bool $expectedResult + */ + public function testMatchLocation($matchingConfig, Repository $repository, $expectedResult) + { + $this->matcher->setRepository($repository); + $this->matcher->setMatchingConfig($matchingConfig); + + $this->assertSame( + $expectedResult, + $this->matcher->matchLocation($this->generateLocationMock()) + ); + } + + public function matchLocationProvider() + { + $data = []; + + $data[] = [ + 'foo', + $this->generateRepositoryMockForContentTypeIdentifier('foo'), + true, + ]; + + $data[] = [ + 'foo', + $this->generateRepositoryMockForContentTypeIdentifier('bar'), + false, + ]; + + $data[] = [ + ['foo', 'baz'], + $this->generateRepositoryMockForContentTypeIdentifier('bar'), + false, + ]; + + $data[] = [ + ['foo', 'baz'], + $this->generateRepositoryMockForContentTypeIdentifier('baz'), + true, + ]; + + return $data; + } + + /** + * Generates a Location object in respect of a given content type identifier. + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + private function generateLocationMock() + { + $location = $this->getLocationMock(); + $location + ->expects($this->any()) + ->method('getContentInfo') + ->will( + $this->returnValue( + $this->getContentInfoMock(['contentTypeId' => 42]) + ) + ); + + return $location; + } + + /** + * @dataProvider matchContentInfoProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ContentType::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * + * @param string|string[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param bool $expectedResult + */ + public function testMatchContentInfo($matchingConfig, Repository $repository, $expectedResult) + { + $this->matcher->setRepository($repository); + $this->matcher->setMatchingConfig($matchingConfig); + + $this->assertSame( + $expectedResult, + $this->matcher->matchContentInfo( + $this->getContentInfoMock(['contentTypeId' => 42]) + ) + ); + } + + public function matchContentInfoProvider() + { + $data = []; + + $data[] = [ + 'foo', + $this->generateRepositoryMockForContentTypeIdentifier('foo'), + true, + ]; + + $data[] = [ + 'foo', + $this->generateRepositoryMockForContentTypeIdentifier('bar'), + false, + ]; + + $data[] = [ + ['foo', 'baz'], + $this->generateRepositoryMockForContentTypeIdentifier('bar'), + false, + ]; + + $data[] = [ + ['foo', 'baz'], + $this->generateRepositoryMockForContentTypeIdentifier('baz'), + true, + ]; + + return $data; + } + + /** + * Returns a Repository mock configured to return the appropriate ContentType object with given identifier. + * + * @param int $contentTypeIdentifier + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + private function generateRepositoryMockForContentTypeIdentifier($contentTypeIdentifier) + { + $contentTypeMock = $this + ->getMockBuilder(ContentType::class) + ->setConstructorArgs( + [['identifier' => $contentTypeIdentifier]] + ) + ->getMockForAbstractClass(); + $contentTypeServiceMock = $this->createMock(ContentTypeService::class); + $contentTypeServiceMock->expects($this->once()) + ->method('loadContentType') + ->with(42) + ->will( + $this->returnValue($contentTypeMock) + ); + + $repository = $this->getRepositoryMock(); + $repository + ->expects($this->any()) + ->method('getContentTypeService') + ->will($this->returnValue($contentTypeServiceMock)); + + return $repository; + } +} + +class_alias(ContentTypeTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Identifier\ContentTypeTest'); diff --git a/tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentTypeTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentTypeTest.php new file mode 100644 index 0000000000..ee20362a26 --- /dev/null +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/ParentContentTypeTest.php @@ -0,0 +1,170 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\Identifier; + +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ParentContentType as ParentContentTypeMatcher; +use Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\BaseTest; + +class ParentContentTypeTest extends BaseTest +{ + private const EXAMPLE_LOCATION_ID = 54; + private const EXAMPLE_PARENT_LOCATION_ID = 2; + + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ParentContentType */ + private $matcher; + + protected function setUp(): void + { + parent::setUp(); + $this->matcher = new ParentContentTypeMatcher(); + } + + /** + * Returns a Repository mock configured to return the appropriate Section object with given section identifier. + * + * @param string $contentTypeIdentifier + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + private function generateRepositoryMockForContentTypeIdentifier($contentTypeIdentifier) + { + $parentContentInfo = $this->getContentInfoMock([ + 'mainLocationId' => self::EXAMPLE_LOCATION_ID, + 'contentTypeId' => 42, + ]); + $parentLocation = $this->getLocationMock([ + 'parentLocationId' => self::EXAMPLE_PARENT_LOCATION_ID, + ]); + $parentLocation->expects($this->once()) + ->method('getContentInfo') + ->will( + $this->returnValue($parentContentInfo) + ); + + $locationServiceMock = $this->createMock(LocationService::class); + $locationServiceMock->expects($this->atLeastOnce()) + ->method('loadLocation') + ->will( + $this->returnValue($parentLocation) + ); + // The following is used in the case of a match by contentInfo + $locationServiceMock->expects($this->any()) + ->method('loadLocation') + ->will( + $this->returnValue($this->getLocationMock()) + ); + + $contentTypeServiceMock = $this->createMock(ContentTypeService::class); + $contentTypeServiceMock->expects($this->once()) + ->method('loadContentType') + ->with(42) + ->will( + $this->returnValue( + $this + ->getMockBuilder(ContentType::class) + ->setConstructorArgs( + [ + ['identifier' => $contentTypeIdentifier], + ] + ) + ->getMockForAbstractClass() + ) + ); + + $repository = $this->getRepositoryMock(); + $repository + ->expects($this->any()) + ->method('getLocationService') + ->will($this->returnValue($locationServiceMock)); + $repository + ->expects($this->once()) + ->method('getContentTypeService') + ->will($this->returnValue($contentTypeServiceMock)); + $repository + ->expects($this->any()) + ->method('getPermissionResolver') + ->will($this->returnValue($this->getPermissionResolverMock())); + + return $repository; + } + + /** + * @dataProvider matchLocationProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ParentContentType::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\RepositoryAware::setRepository + * + * @param string|string[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param bool $expectedResult + */ + public function testMatchLocation($matchingConfig, Repository $repository, $expectedResult) + { + $this->matcher->setRepository($repository); + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame( + $expectedResult, + $this->matcher->matchLocation($this->getLocationMock([ + 'parentLocationId' => self::EXAMPLE_LOCATION_ID, + ])) + ); + } + + public function matchLocationProvider() + { + return [ + [ + 'foo', + $this->generateRepositoryMockForContentTypeIdentifier('foo'), + true, + ], + [ + 'foo', + $this->generateRepositoryMockForContentTypeIdentifier('bar'), + false, + ], + [ + ['foo', 'baz'], + $this->generateRepositoryMockForContentTypeIdentifier('bar'), + false, + ], + [ + ['foo', 'baz'], + $this->generateRepositoryMockForContentTypeIdentifier('baz'), + true, + ], + ]; + } + + /** + * @dataProvider matchLocationProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Identifier\ParentContentType::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\RepositoryAware::setRepository + * + * @param string|string[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param bool $expectedResult + */ + public function testMatchContentInfo($matchingConfig, Repository $repository, $expectedResult) + { + $this->matcher->setRepository($repository); + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame( + $expectedResult, + $this->matcher->matchContentInfo($this->getContentInfoMock([ + 'mainLocationId' => self::EXAMPLE_LOCATION_ID, + ])) + ); + } +} + +class_alias(ParentContentTypeTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Identifier\ParentContentTypeTest'); diff --git a/tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/SectionTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/SectionTest.php new file mode 100644 index 0000000000..cd59972f87 --- /dev/null +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/Identifier/SectionTest.php @@ -0,0 +1,144 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\Identifier; + +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\SectionService; +use Ibexa\Contracts\Core\Repository\Values\Content\Section; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Identifier\Section as SectionIdentifierMatcher; +use Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased\BaseTest; + +class SectionTest extends BaseTest +{ + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Identifier\Section */ + private $matcher; + + protected function setUp(): void + { + parent::setUp(); + $this->matcher = new SectionIdentifierMatcher(); + } + + /** + * Returns a Repository mock configured to return the appropriate Section object with given section identifier. + * + * @param string $sectionIdentifier + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + private function generateRepositoryMockForSectionIdentifier($sectionIdentifier) + { + $sectionServiceMock = $this->createMock(SectionService::class); + $sectionServiceMock->expects($this->once()) + ->method('loadSection') + ->will( + $this->returnValue( + $this + ->getMockBuilder(Section::class) + ->setConstructorArgs( + [ + ['identifier' => $sectionIdentifier], + ] + ) + ->getMockForAbstractClass() + ) + ); + + $repository = $this->getRepositoryMock(); + $repository + ->expects($this->once()) + ->method('getSectionService') + ->will($this->returnValue($sectionServiceMock)); + $repository + ->expects($this->any()) + ->method('getPermissionResolver') + ->will($this->returnValue($this->getPermissionResolverMock())); + + return $repository; + } + + /** + * @dataProvider matchSectionProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Identifier\Section::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\RepositoryAware::setRepository + * + * @param string|string[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param bool $expectedResult + */ + public function testMatchLocation($matchingConfig, Repository $repository, $expectedResult) + { + $this->matcher->setRepository($repository); + $this->matcher->setMatchingConfig($matchingConfig); + + $location = $this->getLocationMock(); + $location + ->expects($this->once()) + ->method('getContentInfo') + ->will( + $this->returnValue( + $this->getContentInfoMock(['sectionId' => 1]) + ) + ); + + $this->assertSame( + $expectedResult, + $this->matcher->matchLocation($location) + ); + } + + public function matchSectionProvider() + { + return [ + [ + 'foo', + $this->generateRepositoryMockForSectionIdentifier('foo'), + true, + ], + [ + 'foo', + $this->generateRepositoryMockForSectionIdentifier('bar'), + false, + ], + [ + ['foo', 'baz'], + $this->generateRepositoryMockForSectionIdentifier('bar'), + false, + ], + [ + ['foo', 'baz'], + $this->generateRepositoryMockForSectionIdentifier('baz'), + true, + ], + ]; + } + + /** + * @dataProvider matchSectionProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\Identifier\Section::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\RepositoryAware::setRepository + * + * @param string|string[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param bool $expectedResult + */ + public function testMatchContentInfo($matchingConfig, Repository $repository, $expectedResult) + { + $this->matcher->setRepository($repository); + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame( + $expectedResult, + $this->matcher->matchContentInfo($this->getContentInfoMock(['sectionId' => 1])) + ); + } +} + +class_alias(SectionTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\Identifier\SectionTest'); diff --git a/tests/lib/MVC/Symfony/Matcher/ContentBased/IsPreviewTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/IsPreviewTest.php new file mode 100644 index 0000000000..114ce35369 --- /dev/null +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/IsPreviewTest.php @@ -0,0 +1,96 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased; + +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\IsPreview; +use Ibexa\Core\MVC\Symfony\View\ContentView; +use Ibexa\Core\MVC\Symfony\View\View; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\IsPreview + */ +final class IsPreviewTest extends TestCase +{ + private IsPreview $isPreviewMatcher; + + protected function setUp(): void + { + $this->isPreviewMatcher = new IsPreview(); + } + + /** + * @return iterable<string, array{\Ibexa\Core\MVC\Symfony\View\View, boolean, boolean}> + */ + public static function getDataForTestMatch(): iterable + { + $previewContentView = new ContentView(); + $previewContentView->setParameters(['isPreview' => true]); + + $notPreviewContentView = new ContentView(); + $notPreviewContentView->setParameters(['isPreview' => false]); + + $viewContentView = new ContentView(); + yield 'match for preview content view' => [ + $previewContentView, + true, // IsPreview: true + true, // matches the view + ]; + + yield 'do not match for preview content view' => [ + $previewContentView, + false, // IsPreview: false + false, // doesn't match the view + ]; + + yield 'match for view content view' => [ + $notPreviewContentView, + false, // IsPreview: false + true, // matches since it's not a preview content view + ]; + + yield 'do not match for view content view' => [ + $notPreviewContentView, + true, // IsPreview: true + false, // doesn't match since it's not a preview content view + ]; + + yield 'not match for not set isPreview parameter' => [ + $viewContentView, + true, // IsPreview: true + false, // by default, it's not a preview view if parameter is not set + ]; + + yield 'do not match for not set isPreview parameter' => [ + $viewContentView, + false, + true, // matches not a preview view, when parameter is not set + ]; + } + + /** + * @dataProvider getDataForTestMatch + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testMatch(View $view, bool $matchConfig, bool $expectedIsPreview): void + { + $this->isPreviewMatcher->setMatchingConfig($matchConfig); + + self::assertSame($expectedIsPreview, $this->isPreviewMatcher->match($view)); + } + + public function testSetMatchConfigThrowsInvalidArgumentException(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('IsPreview matcher expects true or false value, got a value of integer type'); + $this->isPreviewMatcher->setMatchingConfig(123); + } +} diff --git a/tests/lib/MVC/Symfony/Matcher/ContentBased/MultipleValuedTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/MultipleValuedTest.php new file mode 100644 index 0000000000..f1f5e820e8 --- /dev/null +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/MultipleValuedTest.php @@ -0,0 +1,65 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased; + +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued; + +class MultipleValuedTest extends BaseTest +{ + /** + * @dataProvider matchingConfigProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::getValues + */ + public function testSetMatchingConfig($matchingConfig) + { + $matcher = $this->getMultipleValuedMatcherMock(); + $matcher->setMatchingConfig($matchingConfig); + $values = $matcher->getValues(); + $this->assertIsArray($values); + + $matchingConfig = is_array($matchingConfig) ? $matchingConfig : [$matchingConfig]; + foreach ($matchingConfig as $val) { + $this->assertContains($val, $values); + } + } + + /** + * Returns a set of matching values, either single or multiple. + * + * @return array + */ + public function matchingConfigProvider() + { + return [ + [ + 'singleValue', + ['one', 'two', 'three'], + [123, 'nous irons au bois'], + 456, + ], + ]; + } + + /** + * @covers \Ibexa\Core\MVC\RepositoryAware::setRepository + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::getRepository + */ + public function testInjectRepository() + { + $matcher = $this->getMultipleValuedMatcherMock(); + $matcher->setRepository($this->repositoryMock); + $this->assertSame($this->repositoryMock, $matcher->getRepository()); + } + + private function getMultipleValuedMatcherMock() + { + return $this->getMockForAbstractClass(MultipleValued::class); + } +} + +class_alias(MultipleValuedTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\MultipleValuedTest'); diff --git a/tests/lib/MVC/Symfony/Matcher/ContentBased/UrlAliasTest.php b/tests/lib/MVC/Symfony/Matcher/ContentBased/UrlAliasTest.php new file mode 100644 index 0000000000..e729b67dda --- /dev/null +++ b/tests/lib/MVC/Symfony/Matcher/ContentBased/UrlAliasTest.php @@ -0,0 +1,162 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher\ContentBased; + +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\URLAliasService; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; +use Ibexa\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias as UrlAliasMatcher; + +class UrlAliasTest extends BaseTest +{ + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias */ + private $matcher; + + protected function setUp(): void + { + parent::setUp(); + $this->matcher = new UrlAliasMatcher(); + } + + /** + * @dataProvider setMatchingConfigProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias::setMatchingConfig + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\MultipleValued::setMatchingConfig + * + * @param string $matchingConfig + * @param string[] $expectedValues + */ + public function testSetMatchingConfig($matchingConfig, $expectedValues) + { + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame( + $this->matcher->getValues(), + $expectedValues + ); + } + + public function setMatchingConfigProvider() + { + return [ + ['/foo/bar/', ['foo/bar']], + ['/foo/bar/', ['foo/bar']], + ['/foo/bar', ['foo/bar']], + [['/foo/bar/', 'baz/biz/'], ['foo/bar', 'baz/biz']], + [['foo/bar', 'baz/biz'], ['foo/bar', 'baz/biz']], + ]; + } + + /** + * Returns a Repository mock configured to return the appropriate Section object with given section identifier. + * + * @param string $path + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + private function generateRepositoryMockForUrlAlias($path) + { + // First an url alias that will never match, then the right url alias. + // This ensures to test even if the location has several url aliases. + $urlAliasList = [ + $this->createMock(URLAlias::class), + $this + ->getMockBuilder(URLAlias::class) + ->setConstructorArgs([['path' => $path]]) + ->getMockForAbstractClass(), + ]; + + $urlAliasServiceMock = $this->createMock(URLAliasService::class); + $urlAliasServiceMock->expects($this->at(0)) + ->method('listLocationAliases') + ->with( + $this->isInstanceOf(Location::class), + true + ) + ->will($this->returnValue([])); + $urlAliasServiceMock->expects($this->at(1)) + ->method('listLocationAliases') + ->with( + $this->isInstanceOf(Location::class), + false + ) + ->will($this->returnValue($urlAliasList)); + + $repository = $this->getRepositoryMock(); + $repository + ->expects($this->once()) + ->method('getURLAliasService') + ->will($this->returnValue($urlAliasServiceMock)); + + return $repository; + } + + /** + * @dataProvider matchLocationProvider + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias::matchLocation + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias::setMatchingConfig + * @covers \Ibexa\Core\MVC\RepositoryAware::setRepository + * + * @param string|string[] $matchingConfig + * @param \Ibexa\Contracts\Core\Repository\Repository $repository + * @param bool $expectedResult + */ + public function testMatchLocation($matchingConfig, Repository $repository, $expectedResult) + { + $this->matcher->setRepository($repository); + $this->matcher->setMatchingConfig($matchingConfig); + $this->assertSame( + $expectedResult, + $this->matcher->matchLocation($this->getLocationMock()) + ); + } + + public function matchLocationProvider() + { + return [ + [ + 'foo/url', + $this->generateRepositoryMockForUrlAlias('/foo/url'), + true, + ], + [ + '/foo/url', + $this->generateRepositoryMockForUrlAlias('/foo/url'), + true, + ], + [ + 'foo/url', + $this->generateRepositoryMockForUrlAlias('/bar/url'), + false, + ], + [ + ['foo/url', 'baz'], + $this->generateRepositoryMockForUrlAlias('/bar/url'), + false, + ], + [ + ['foo/url ', 'baz '], + $this->generateRepositoryMockForUrlAlias('/baz'), + true, + ], + ]; + } + + /** + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias::matchContentInfo + * @covers \Ibexa\Core\MVC\Symfony\Matcher\ContentBased\UrlAlias::setMatchingConfig + */ + public function testMatchContentInfo() + { + $this->expectException(\RuntimeException::class); + + $this->matcher->setMatchingConfig('foo/bar'); + $this->matcher->matchContentInfo($this->getContentInfoMock()); + } +} + +class_alias(UrlAliasTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\ContentBased\UrlAliasTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/DynamicallyConfiguredMatcherFactoryDecoratorTest.php b/tests/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecoratorTest.php similarity index 78% rename from eZ/Publish/Core/MVC/Symfony/Matcher/Tests/DynamicallyConfiguredMatcherFactoryDecoratorTest.php rename to tests/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecoratorTest.php index 72477d4b94..3f944bbb25 100644 --- a/eZ/Publish/Core/MVC/Symfony/Matcher/Tests/DynamicallyConfiguredMatcherFactoryDecoratorTest.php +++ b/tests/lib/MVC/Symfony/Matcher/DynamicallyConfiguredMatcherFactoryDecoratorTest.php @@ -4,20 +4,20 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Matcher\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Matcher; -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Configuration\ConfigResolver; -use eZ\Publish\Core\MVC\Symfony\Matcher\ClassNameMatcherFactory; -use eZ\Publish\Core\MVC\Symfony\Matcher\DynamicallyConfiguredMatcherFactoryDecorator; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; +use Ibexa\Bundle\Core\DependencyInjection\Configuration\ConfigResolver; +use Ibexa\Core\MVC\Symfony\Matcher\ClassNameMatcherFactory; +use Ibexa\Core\MVC\Symfony\Matcher\DynamicallyConfiguredMatcherFactoryDecorator; +use Ibexa\Core\MVC\Symfony\View\ContentView; use PHPUnit\Framework\TestCase; class DynamicallyConfiguredMatcherFactoryDecoratorTest extends TestCase { - /** @var \eZ\Publish\Core\MVC\Symfony\Matcher\ConfigurableMatcherFactoryInterface */ + /** @var \Ibexa\Core\MVC\Symfony\Matcher\ConfigurableMatcherFactoryInterface */ private $innerMatcherFactory; - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolver; public function setUp(): void @@ -80,3 +80,5 @@ public function matchConfigProvider(): array ]; } } + +class_alias(DynamicallyConfiguredMatcherFactoryDecoratorTest::class, 'eZ\Publish\Core\MVC\Symfony\Matcher\Tests\DynamicallyConfiguredMatcherFactoryDecoratorTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/Tests/GeneratorTest.php b/tests/lib/MVC/Symfony/Routing/GeneratorTest.php similarity index 90% rename from eZ/Publish/Core/MVC/Symfony/Routing/Tests/GeneratorTest.php rename to tests/lib/MVC/Symfony/Routing/GeneratorTest.php index 986e38068f..28e4290837 100644 --- a/eZ/Publish/Core/MVC/Symfony/Routing/Tests/GeneratorTest.php +++ b/tests/lib/MVC/Symfony/Routing/GeneratorTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Routing\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Routing; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\URILexer; -use eZ\Publish\Core\Repository\Values\Content\Location; +use Ibexa\Core\MVC\Symfony\Routing\Generator; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess\URILexer; +use Ibexa\Core\Repository\Values\Content\Location; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; @@ -18,7 +18,7 @@ class GeneratorTest extends TestCase { - /** @var \eZ\Publish\Core\MVC\Symfony\Routing\Generator|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\MVC\Symfony\Routing\Generator|\PHPUnit\Framework\MockObject\MockObject */ private $generator; /** @var \PHPUnit\Framework\MockObject\MockObject */ @@ -127,3 +127,5 @@ public function testGenerateWithSiteAccessNoReverseMatch($urlResource, array $pa $this->assertSame($fullUri, $this->generator->generate($urlResource, $parameters + ['siteaccess' => $siteAccessName], $referenceType)); } } + +class_alias(GeneratorTest::class, 'eZ\Publish\Core\MVC\Symfony\Routing\Tests\GeneratorTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/Tests/RouteReferenceGeneratorTest.php b/tests/lib/MVC/Symfony/Routing/RouteReferenceGeneratorTest.php similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/Routing/Tests/RouteReferenceGeneratorTest.php rename to tests/lib/MVC/Symfony/Routing/RouteReferenceGeneratorTest.php index 93df95580e..c4cdb0205e 100644 --- a/eZ/Publish/Core/MVC/Symfony/Routing/Tests/RouteReferenceGeneratorTest.php +++ b/tests/lib/MVC/Symfony/Routing/RouteReferenceGeneratorTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Routing\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Routing; -use eZ\Publish\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator; -use eZ\Publish\Core\MVC\Symfony\Routing\RouteReference; -use eZ\Publish\Core\Repository\Values\Content\Location; +use Ibexa\Core\MVC\Symfony\Event\RouteReferenceGenerationEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator; +use Ibexa\Core\MVC\Symfony\Routing\RouteReference; +use Ibexa\Core\Repository\Values\Content\Location; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; @@ -138,3 +138,5 @@ public function generateGenerator() ]; } } + +class_alias(RouteReferenceGeneratorTest::class, 'eZ\Publish\Core\MVC\Symfony\Routing\Tests\RouteReferenceGeneratorTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/Tests/RouteReferenceTest.php b/tests/lib/MVC/Symfony/Routing/RouteReferenceTest.php similarity index 90% rename from eZ/Publish/Core/MVC/Symfony/Routing/Tests/RouteReferenceTest.php rename to tests/lib/MVC/Symfony/Routing/RouteReferenceTest.php index 5e9cb1c584..60c8249500 100644 --- a/eZ/Publish/Core/MVC/Symfony/Routing/Tests/RouteReferenceTest.php +++ b/tests/lib/MVC/Symfony/Routing/RouteReferenceTest.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Routing\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Routing; -use eZ\Publish\Core\MVC\Symfony\Routing\RouteReference; +use Ibexa\Core\MVC\Symfony\Routing\RouteReference; use PHPUnit\Framework\TestCase; class RouteReferenceTest extends TestCase @@ -63,3 +63,5 @@ public function testRemoveParam() $this->assertFalse($reference->has('foo')); } } + +class_alias(RouteReferenceTest::class, 'eZ\Publish\Core\MVC\Symfony\Routing\Tests\RouteReferenceTest'); diff --git a/tests/lib/MVC/Symfony/Routing/SimplifiedRequestTest.php b/tests/lib/MVC/Symfony/Routing/SimplifiedRequestTest.php new file mode 100644 index 0000000000..57d06dcab9 --- /dev/null +++ b/tests/lib/MVC/Symfony/Routing/SimplifiedRequestTest.php @@ -0,0 +1,89 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Routing; + +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest + */ +class SimplifiedRequestTest extends TestCase +{ + /** + * @param string $url + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $expectedRequest + * + * @dataProvider fromUrlProvider + */ + public function testFromUrl($url, $expectedRequest) + { + self::assertEquals( + $expectedRequest, + SimplifiedRequest::fromUrl($url) + ); + } + + public function testStrictGetters(): void + { + $headers = ['Cookie' => ['abc', 'def']]; + $languages = ['en', 'pl']; + $url = 'https://host.invalid:8080/foo?param=bar¶m2=bar2'; + + $request = SimplifiedRequest::fromUrl($url); + $request->setHeaders($headers); + $request->setLanguages($languages); + + self::assertSame($headers['Cookie'], $request->getHeader('Cookie')); + self::assertSame($headers, $request->getHeaders()); + self::assertSame($languages, $request->getLanguages()); + self::assertSame('https', $request->getScheme()); + self::assertSame('host.invalid', $request->getHost()); + self::assertSame('8080', $request->getPort()); + self::assertSame('/foo', $request->getPathInfo()); + self::assertSame(['param' => 'bar', 'param2' => 'bar2'], $request->getQueryParams()); + } + + public function fromUrlProvider() + { + return [ + [ + 'http://www.example.com/foo/bar', + new SimplifiedRequest( + [ + 'scheme' => 'http', + 'host' => 'www.example.com', + 'pathinfo' => '/foo/bar', + ] + ), + ], + [ + 'https://www.example.com/', + new SimplifiedRequest( + [ + 'scheme' => 'https', + 'host' => 'www.example.com', + 'pathinfo' => '/', + ] + ), + ], + [ + 'http://www.example.com/foo?param=value&this=that', + new SimplifiedRequest( + [ + 'scheme' => 'http', + 'host' => 'www.example.com', + 'pathinfo' => '/foo', + 'queryParams' => ['param' => 'value', 'this' => 'that'], + ] + ), + ], + ]; + } +} + +class_alias(SimplifiedRequestTest::class, 'eZ\Publish\Core\MVC\Symfony\Routing\Tests\SimplifiedRequestTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Routing/Tests/UrlAliasGeneratorTest.php b/tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php similarity index 78% rename from eZ/Publish/Core/MVC/Symfony/Routing/Tests/UrlAliasGeneratorTest.php rename to tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php index 901faca008..c7d8d2e00f 100644 --- a/eZ/Publish/Core/MVC/Symfony/Routing/Tests/UrlAliasGeneratorTest.php +++ b/tests/lib/MVC/Symfony/Routing/UrlAliasGeneratorTest.php @@ -4,22 +4,22 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Routing\Tests; - -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\URLAliasService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface; -use eZ\Publish\Core\Repository\Mapper\RoleDomainMapper; -use eZ\Publish\Core\Repository\Permission\LimitationService; -use eZ\Publish\Core\Repository\Permission\PermissionResolver; -use eZ\Publish\Core\Repository\Repository; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\SPI\Persistence\User\Handler as SPIUserHandler; +namespace Ibexa\Tests\Core\MVC\Symfony\Routing; + +use Ibexa\Contracts\Core\Persistence\User\Handler as SPIUserHandler; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\URLAliasService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessRouterInterface; +use Ibexa\Core\Repository\Mapper\RoleDomainMapper; +use Ibexa\Core\Repository\Permission\LimitationService; +use Ibexa\Core\Repository\Permission\PermissionResolver; +use Ibexa\Core\Repository\Repository; +use Ibexa\Core\Repository\Values\Content\Location; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\Routing\RouterInterface; @@ -41,7 +41,7 @@ class UrlAliasGeneratorTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $logger; - /** @var \eZ\Publish\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator */ + /** @var \Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator */ private $urlAliasGenerator; /** @var \PHPUnit\Framework\MockObject\MockObject */ @@ -139,14 +139,14 @@ public function providerTestIsPrefixExcluded() return [ ['/foo/bar', false], ['/products/bar', true], - ['/ProDUctS/eZ-Publish', true], - ['/ProductsFoo/eZ-Publish', true], + ['/ProDUctS/Ibexa', true], + ['/ProductsFoo/Ibexa', true], ['/shared/foo', false], ['/SHARED/contenT/bar', true], ['/SomeThing/bidule/chose', false], ['/SomeThing/in-the-way/truc/', true], ['/SomeThing/in-the-way-suffixed/', false], - ['/CMS/eZ-Publish', false], + ['/CMS/Ibexa', false], ['/Lyon/Best/city', false], ]; } @@ -319,6 +319,84 @@ public function providerTestDoGenerateWithSiteaccess() ]; } + public function testDoGenerateWithSiteAccessLoadsLocationWithLanguages(): void + { + $siteSiteAccess = 'site'; + $gerSiteAccess = 'ger'; + $parameters = ['siteaccess' => $gerSiteAccess]; + + $saRootLocations = [ + $siteSiteAccess => $siteSiteAccessLocationId = 2, + $gerSiteAccess => $gerSiteAccessLocationId = 71, + ]; + $treeRootUrlAliases = [ + $siteSiteAccessLocationId => new URLAlias(['path' => '/']), + $gerSiteAccessLocationId => new URLAlias(['path' => '/ger']), + ]; + + $this->configResolver + ->expects(self::any()) + ->method('getParameter') + ->will( + self::returnValueMap( + [ + ['languages', null, $siteSiteAccess, ['eng-GB']], + ['languages', null, $gerSiteAccess, ['ger-DE']], + [ + 'content.tree_root.location_id', + null, + $siteSiteAccess, + $saRootLocations[$siteSiteAccess], + ], + [ + 'content.tree_root.location_id', + null, + $gerSiteAccess, + $saRootLocations[$gerSiteAccess], + ], + ] + ) + ); + + $location = new Location(['id' => $gerSiteAccessLocationId]); + + $this->urlAliasService + ->expects(self::once()) + ->method('listLocationAliases') + ->with($location, false, null, null, ['ger-DE']) + ->willReturn( + [ + new URLAlias( + ['path' => $gerRootLocationAlias = '/ger-folder'], + ), + ], + ); + + $this->locationService + ->expects(self::once()) + ->method('loadLocation') + ->with($gerSiteAccessLocationId, ['ger-DE']) + ->willReturn($location); + + $this->urlAliasService + ->expects(self::once()) + ->method('reverseLookup') + ->with($location, null, false, ['ger-DE']) + ->willReturn($treeRootUrlAliases[$location->id]); + + $this->urlAliasGenerator->setSiteAccess( + new SiteAccess( + $gerSiteAccess, + 'default', + ) + ); + + self::assertSame( + $gerRootLocationAlias, + $this->urlAliasGenerator->doGenerate($location, $parameters) + ); + } + public function testDoGenerateNoUrlAlias() { $location = new Location(['id' => 123, 'contentInfo' => new ContentInfo(['id' => 456])]); @@ -425,9 +503,9 @@ public function providerTestDoGenerateRootLocation() '/my/root-folder', ], [ - new UrlAlias(['path' => '/products/ez-publish']), + new UrlAlias(['path' => '/products/ibexa-dxp']), false, - '/products/ez-publish', + '/products/ibexa-dxp', '/my/root-folder', ], [ @@ -437,9 +515,9 @@ public function providerTestDoGenerateRootLocation() '/my/root-folder', ], [ - new UrlAlias(['path' => '/products/ez-publish']), + new UrlAlias(['path' => '/products/ibexa-dxp']), false, - '/products/ez-publish', + '/products/ibexa-dxp', '/prod', ], ]; @@ -468,3 +546,5 @@ protected function getPermissionResolverMock() ->getMock(); } } + +class_alias(UrlAliasGeneratorTest::class, 'eZ\Publish\Core\MVC\Symfony\Routing\Tests\UrlAliasGeneratorTest'); diff --git a/tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php b/tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php new file mode 100644 index 0000000000..2a6f85f483 --- /dev/null +++ b/tests/lib/MVC/Symfony/Routing/UrlAliasRouterTest.php @@ -0,0 +1,802 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Routing; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\URLAliasService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator; +use Ibexa\Core\MVC\Symfony\Routing\UrlAliasRouter; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\View\Manager as ViewManager; +use Ibexa\Core\Repository\Repository; +use Ibexa\Core\Repository\Values\Content\Location; +use PHPUnit\Framework\TestCase; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\Exception\RouteNotFoundException; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RouterInterface; + +class UrlAliasRouterTest extends TestCase +{ + /** @var \PHPUnit\Framework\MockObject\MockObject */ + protected $repository; + + /** @var \PHPUnit\Framework\MockObject\MockObject */ + protected $urlAliasService; + + /** @var \PHPUnit\Framework\MockObject\MockObject */ + protected $locationService; + + /** @var \PHPUnit\Framework\MockObject\MockObject */ + protected $contentService; + + /** @var \PHPUnit\Framework\MockObject\MockObject */ + protected $urlALiasGenerator; + + protected $requestContext; + + /** @var \Ibexa\Core\MVC\Symfony\Routing\UrlAliasRouter */ + protected $router; + + protected function setUp(): void + { + parent::setUp(); + $repositoryClass = Repository::class; + $this->repository = $repository = $this + ->getMockBuilder($repositoryClass) + ->disableOriginalConstructor() + ->setMethods( + array_diff( + get_class_methods($repositoryClass), + ['sudo'] + ) + ) + ->getMock(); + $this->urlAliasService = $this->createMock(URLAliasService::class); + $this->locationService = $this->createMock(LocationService::class); + $this->contentService = $this->createMock(ContentService::class); + $this->urlALiasGenerator = $this + ->getMockBuilder(UrlAliasGenerator::class) + ->setConstructorArgs( + [ + $repository, + $this->createMock(RouterInterface::class), + $this->createMock(ConfigResolverInterface::class), + ] + ) + ->getMock(); + $this->requestContext = new RequestContext(); + + $this->router = $this->getRouter($this->locationService, $this->urlAliasService, $this->contentService, $this->urlALiasGenerator, $this->requestContext); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\LocationService $locationService + * @param \Ibexa\Contracts\Core\Repository\URLAliasService $urlAliasService + * @param \Ibexa\Contracts\Core\Repository\ContentService $contentService + * @param \Ibexa\Core\MVC\Symfony\Routing\Generator\UrlAliasGenerator $urlAliasGenerator + * @param \Symfony\Component\Routing\RequestContext $requestContext + * + * @return \Ibexa\Core\MVC\Symfony\Routing\UrlAliasRouter + */ + protected function getRouter(LocationService $locationService, URLAliasService $urlAliasService, ContentService $contentService, UrlAliasGenerator $urlAliasGenerator, RequestContext $requestContext) + { + return new UrlAliasRouter($locationService, $urlAliasService, $contentService, $urlAliasGenerator, $requestContext); + } + + public function testRequestContext() + { + $this->assertSame($this->requestContext, $this->router->getContext()); + $newContext = new RequestContext(); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('setRequestContext') + ->with($newContext); + $this->router->setContext($newContext); + $this->assertSame($newContext, $this->router->getContext()); + } + + public function testMatch() + { + $this->expectException(\RuntimeException::class); + + $this->router->match('/foo'); + } + + /** + * @dataProvider providerTestSupports + */ + public function testSupports($routeReference, $isSupported) + { + $this->assertSame($isSupported, $this->router->supports($routeReference)); + } + + public function providerTestSupports() + { + return [ + [new Location(), true], + [new \stdClass(), false], + [UrlAliasRouter::URL_ALIAS_ROUTE_NAME, true], + ['some_route_name', false], + ]; + } + + public function testGetRouteCollection() + { + $this->assertInstanceOf(RouteCollection::class, $this->router->getRouteCollection()); + } + + /** + * @param $pathInfo + * + * @return \Symfony\Component\HttpFoundation\Request + */ + protected function getRequestByPathInfo($pathInfo) + { + $request = Request::create($pathInfo); + $request->attributes->set('semanticPathinfo', $pathInfo); + $request->attributes->set( + 'siteaccess', + new SiteAccess( + 'test', + 'fake', + $this->createMock(Matcher::class) + ) + ); + + return $request; + } + + public function testMatchRequestLocation() + { + $pathInfo = '/foo/bar'; + $destinationId = 123; + $urlAlias = new URLAlias( + [ + 'path' => $pathInfo, + 'type' => UrlAlias::LOCATION, + 'destination' => $destinationId, + 'isHistory' => false, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('loadLocation') + ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + '_controller' => UrlAliasRouter::VIEW_ACTION, + 'locationId' => $destinationId, + 'contentId' => 456, + 'viewType' => ViewManager::VIEW_TYPE_FULL, + 'layout' => true, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestLocationWithCaseRedirect() + { + $pathInfo = '/Foo/bAR'; + $urlAliasPath = '/foo/bar'; + $destinationId = 123; + $urlAlias = new URLAlias( + [ + 'path' => $urlAliasPath, + 'type' => UrlAlias::LOCATION, + 'destination' => $destinationId, + 'isHistory' => false, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('isUriPrefixExcluded') + ->with($pathInfo) + ->will($this->returnValue(false)); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('loadLocation') + ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + '_controller' => UrlAliasRouter::VIEW_ACTION, + 'locationId' => $destinationId, + 'contentId' => 456, + 'viewType' => ViewManager::VIEW_TYPE_FULL, + 'layout' => true, + 'needsRedirect' => true, + 'semanticPathinfo' => $urlAliasPath, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestLocationWrongCaseUriPrefixExcluded() + { + $pathInfo = '/Foo/bAR'; + $urlAliasPath = '/foo/bar'; + $destinationId = 123; + $urlAlias = new URLAlias( + [ + 'path' => $urlAliasPath, + 'type' => UrlAlias::LOCATION, + 'destination' => $destinationId, + 'isHistory' => false, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('isUriPrefixExcluded') + ->with($pathInfo) + ->will($this->returnValue(true)); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('loadLocation') + ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + '_controller' => UrlAliasRouter::VIEW_ACTION, + 'contentId' => 456, + 'locationId' => $destinationId, + 'viewType' => ViewManager::VIEW_TYPE_FULL, + 'layout' => true, + 'needsRedirect' => true, + 'semanticPathinfo' => $urlAliasPath, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestLocationCorrectCaseUriPrefixExcluded() + { + $pathInfo = $urlAliasPath = '/foo/bar'; + $destinationId = 123; + $urlAlias = new URLAlias( + [ + 'path' => $urlAliasPath, + 'type' => UrlAlias::LOCATION, + 'destination' => $destinationId, + 'isHistory' => false, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('isUriPrefixExcluded') + ->with($pathInfo) + ->will($this->returnValue(true)); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('loadLocation') + ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + '_controller' => UrlAliasRouter::VIEW_ACTION, + 'locationId' => $destinationId, + 'contentId' => 456, + 'viewType' => ViewManager::VIEW_TYPE_FULL, + 'layout' => true, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + $this->assertFalse($request->attributes->has('needsRedirect')); + $this->assertSame($pathInfo, $request->attributes->get('semanticPathinfo')); + } + + public function testMatchRequestLocationHistory() + { + $pathInfo = '/foo/bar'; + $newPathInfo = '/foo/bar-new'; + $destinationId = 123; + $destinationLocation = new Location([ + 'id' => $destinationId, + 'contentInfo' => new ContentInfo(['id' => 456]), + ]); + $urlAlias = new URLAlias( + [ + 'path' => $pathInfo, + 'type' => UrlAlias::LOCATION, + 'destination' => $destinationId, + 'isHistory' => true, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('generate') + ->with($destinationLocation) + ->will($this->returnValue($newPathInfo)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('loadLocation') + ->will($this->returnValue($destinationLocation)); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + '_controller' => UrlAliasRouter::VIEW_ACTION, + 'locationId' => $destinationId, + 'contentId' => 456, + 'viewType' => ViewManager::VIEW_TYPE_FULL, + 'layout' => true, + 'needsRedirect' => true, + 'semanticPathinfo' => $newPathInfo, + 'prependSiteaccessOnRedirect' => false, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestLocationCustom() + { + $pathInfo = '/foo/bar'; + $destinationId = 123; + $urlAlias = new URLAlias( + [ + 'path' => $pathInfo, + 'type' => UrlAlias::LOCATION, + 'destination' => $destinationId, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => false, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('loadLocation') + ->will($this->returnValue(new Location(['contentInfo' => new ContentInfo(['id' => 456])]))); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + '_controller' => UrlAliasRouter::VIEW_ACTION, + 'locationId' => $destinationId, + 'contentId' => 456, + 'viewType' => ViewManager::VIEW_TYPE_FULL, + 'layout' => true, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestLocationCustomForward() + { + $pathInfo = '/foo/bar'; + $newPathInfo = '/foo/bar-new'; + $destinationId = 123; + $destinationLocation = new Location([ + 'id' => $destinationId, + 'contentInfo' => new ContentInfo(['id' => 456]), + ]); + $urlAlias = new URLAlias( + [ + 'path' => $pathInfo, + 'type' => UrlAlias::LOCATION, + 'destination' => $destinationId, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => true, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('loadLocation') + ->with($destinationId) + ->will( + $this->returnValue($destinationLocation) + ); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('generate') + ->with($destinationLocation) + ->will($this->returnValue($newPathInfo)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('loadLocation') + ->will($this->returnValue($destinationLocation)); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + '_controller' => UrlAliasRouter::VIEW_ACTION, + 'locationId' => $destinationId, + 'contentId' => 456, + 'viewType' => ViewManager::VIEW_TYPE_FULL, + 'layout' => true, + 'needsRedirect' => true, + 'semanticPathinfo' => $newPathInfo, + 'prependSiteaccessOnRedirect' => false, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestFail() + { + $this->expectException(ResourceNotFoundException::class); + + $pathInfo = '/foo/bar'; + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->throwException(new NotFoundException('URLAlias', $pathInfo))); + $this->router->matchRequest($request); + } + + public function testMatchRequestResource() + { + $pathInfo = '/hello_content/hello_search'; + $destination = '/content/search'; + $urlAlias = new URLAlias( + [ + 'destination' => $destination, + 'path' => $pathInfo, + 'type' => UrlAlias::RESOURCE, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + 'semanticPathinfo' => $destination, + 'needsForward' => true, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestResourceWithRedirect() + { + $pathInfo = '/hello_content/hello_search'; + $destination = '/content/search'; + $urlAlias = new URLAlias( + [ + 'destination' => $destination, + 'path' => $pathInfo, + 'type' => UrlAlias::RESOURCE, + 'forward' => true, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + 'needsRedirect' => true, + 'semanticPathinfo' => $destination, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestResourceWithCaseRedirect() + { + $pathInfo = '/heLLo_contEnt/hEllo_SEarch'; + $urlAliasPath = '/hello_content/hello_search'; + $destination = '/content/search'; + $urlAlias = new URLAlias( + [ + 'destination' => $destination, + 'path' => $urlAliasPath, + 'type' => UrlAlias::RESOURCE, + 'forward' => false, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('isUriPrefixExcluded') + ->with($pathInfo) + ->will($this->returnValue(false)); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + 'semanticPathinfo' => $urlAliasPath, + 'needsRedirect' => true, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + /** + * Tests that forwarding custom alias will redirect to the resource destination rather than + * to the case-corrected alias. + */ + public function testMatchRequestResourceCaseIncorrectWithForwardRedirect() + { + $pathInfo = '/heLLo_contEnt/hEllo_SEarch'; + $urlAliasPath = '/hello_content/hello_search'; + $destination = '/content/search'; + $urlAlias = new URLAlias( + [ + 'destination' => $destination, + 'path' => $urlAliasPath, + 'type' => UrlAlias::RESOURCE, + 'forward' => true, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + 'semanticPathinfo' => $destination, + 'needsRedirect' => true, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestVirtual() + { + $pathInfo = '/foo/bar'; + $urlAlias = new URLAlias( + [ + 'path' => $pathInfo, + 'type' => UrlAlias::VIRTUAL, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + 'semanticPathinfo' => '/', + 'needsForward' => true, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testMatchRequestVirtualWithCaseRedirect() + { + $pathInfo = '/Foo/bAR'; + $urlAliasPath = '/foo/bar'; + $urlAlias = new URLAlias( + [ + 'path' => $urlAliasPath, + 'type' => UrlAlias::VIRTUAL, + ] + ); + $request = $this->getRequestByPathInfo($pathInfo); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('isUriPrefixExcluded') + ->with($pathInfo) + ->will($this->returnValue(false)); + $this->urlAliasService + ->expects($this->once()) + ->method('lookup') + ->with($pathInfo) + ->will($this->returnValue($urlAlias)); + + $expected = [ + '_route' => UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + 'semanticPathinfo' => $urlAliasPath, + 'needsRedirect' => true, + ]; + $this->assertEquals($expected, $this->router->matchRequest($request)); + } + + public function testGenerateFail() + { + $this->expectException(RouteNotFoundException::class); + + $this->router->generate('invalidRoute'); + } + + public function testGenerateWithRouteObject(): void + { + $location = new Location(['id' => 54]); + $parameters = [ + 'some' => 'thing', + ]; + + $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH; + $generatedLink = '/foo/bar'; + + $this->urlALiasGenerator + ->expects($this->once()) + ->method('generate') + ->with($location, $parameters, $referenceType) + ->will($this->returnValue($generatedLink)); + + $this->assertSame( + $generatedLink, + $this->router->generate( + '', + $parameters + [ + RouteObjectInterface::ROUTE_OBJECT => $location, + ], + $referenceType + ) + ); + } + + public function testGenerateNoLocation() + { + $this->expectException(\InvalidArgumentException::class); + + $this->router->generate(UrlAliasRouter::URL_ALIAS_ROUTE_NAME, ['foo' => 'bar']); + } + + public function testGenerateInvalidLocation() + { + $this->expectException(\LogicException::class); + + $this->router->generate(UrlAliasRouter::URL_ALIAS_ROUTE_NAME, ['location' => new \stdClass()]); + } + + public function testGenerateWithLocationId() + { + $locationId = 123; + $location = new Location(['id' => $locationId]); + $parameters = ['some' => 'thing']; + $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH; + $generatedLink = '/foo/bar'; + $this->locationService + ->expects($this->once()) + ->method('loadLocation') + ->with($locationId) + ->will($this->returnValue($location)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('generate') + ->with($location, $parameters, $referenceType) + ->will($this->returnValue($generatedLink)); + $this->assertSame( + $generatedLink, + $this->router->generate( + UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + $parameters + ['locationId' => $locationId], + $referenceType + ) + ); + } + + public function testGenerateWithLocationAsParameter() + { + $locationId = 123; + $location = new Location(['id' => $locationId]); + $parameters = ['some' => 'thing']; + $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH; + $generatedLink = '/foo/bar'; + $this->urlALiasGenerator + ->expects($this->once()) + ->method('generate') + ->with($location, $parameters, $referenceType) + ->will($this->returnValue($generatedLink)); + $this->assertSame( + $generatedLink, + $this->router->generate( + UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + $parameters + ['location' => $location], + $referenceType + ) + ); + } + + public function testGenerateWithContentId() + { + $locationId = 123; + $contentId = 456; + $location = new Location(['id' => $locationId]); + $contentInfo = new ContentInfo(['id' => $contentId, 'mainLocationId' => $locationId]); + $parameters = ['some' => 'thing']; + $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH; + $generatedLink = '/foo/bar'; + $this->contentService + ->expects($this->once()) + ->method('loadContentInfo') + ->with($contentId) + ->will($this->returnValue($contentInfo)); + $this->locationService + ->expects($this->once()) + ->method('loadLocation') + ->with($contentInfo->mainLocationId) + ->will($this->returnValue($location)); + $this->urlALiasGenerator + ->expects($this->once()) + ->method('generate') + ->with($location, $parameters, $referenceType) + ->will($this->returnValue($generatedLink)); + $this->assertSame( + $generatedLink, + $this->router->generate( + UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + $parameters + ['contentId' => $contentId], + $referenceType + ) + ); + } + + public function testGenerateWithContentIdWithMissingMainLocation() + { + $this->expectException(\LogicException::class); + + $contentId = 456; + $contentInfo = new ContentInfo(['id' => $contentId, 'mainLocationId' => null]); + $parameters = ['some' => 'thing']; + $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH; + $this->contentService + ->expects($this->once()) + ->method('loadContentInfo') + ->with($contentId) + ->will($this->returnValue($contentInfo)); + + $this->router->generate( + UrlAliasRouter::URL_ALIAS_ROUTE_NAME, + $parameters + ['contentId' => $contentId], + $referenceType + ); + } +} + +class_alias(UrlAliasRouterTest::class, 'eZ\Publish\Core\MVC\Symfony\Routing\Tests\UrlAliasRouterTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/AnonymousAuthenticationProviderTest.php b/tests/lib/MVC/Symfony/Security/Authentication/AnonymousAuthenticationProviderTest.php similarity index 75% rename from eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/AnonymousAuthenticationProviderTest.php rename to tests/lib/MVC/Symfony/Security/Authentication/AnonymousAuthenticationProviderTest.php index a48d6a1a3a..f9a4a26a3f 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/AnonymousAuthenticationProviderTest.php +++ b/tests/lib/MVC/Symfony/Security/Authentication/AnonymousAuthenticationProviderTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests\Authentication; +namespace Ibexa\Tests\Core\MVC\Symfony\Security\Authentication; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Security\Authentication\AnonymousAuthenticationProvider; -use eZ\Publish\Core\Repository\Values\User\UserReference; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Security\Authentication\AnonymousAuthenticationProvider; +use Ibexa\Core\Repository\Values\User\UserReference; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\User\UserInterface; @@ -19,7 +19,7 @@ class AnonymousAuthenticationProviderTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $configResolver; - /** @var \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ private $permissionResolver; protected function setUp(): void @@ -54,3 +54,5 @@ public function testAuthenticate() $this->assertSame($anonymousToken, $authProvider->authenticate($anonymousToken)); } } + +class_alias(AnonymousAuthenticationProviderTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\Authentication\AnonymousAuthenticationProviderTest'); diff --git a/tests/lib/MVC/Symfony/Security/Authentication/DefaultAuthenticationSuccessHandlerTest.php b/tests/lib/MVC/Symfony/Security/Authentication/DefaultAuthenticationSuccessHandlerTest.php new file mode 100644 index 0000000000..d410ada7dc --- /dev/null +++ b/tests/lib/MVC/Symfony/Security/Authentication/DefaultAuthenticationSuccessHandlerTest.php @@ -0,0 +1,66 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Security\Authentication; + +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Security\Authentication\DefaultAuthenticationSuccessHandler; +use Ibexa\Core\MVC\Symfony\Security\HttpUtils; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use PHPUnit\Framework\TestCase; +use ReflectionObject; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\Session; +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + +class DefaultAuthenticationSuccessHandlerTest extends TestCase +{ + public function testSetConfigResolver() + { + $siteAccess = new SiteAccess( + 'test', + SiteAccess::DEFAULT_MATCHING_TYPE, + $this->createMock(Matcher::class) + ); + $httpUtils = new HttpUtils(); + $httpUtils->setSiteAccess($siteAccess); + $successHandler = new DefaultAuthenticationSuccessHandler($httpUtils, []); + $successHandler->setFirewallName('test_firewall'); + + $refHandler = new ReflectionObject($successHandler); + $refOptions = $refHandler->getProperty('options'); + $refOptions->setAccessible(true); + $options = $refOptions->getValue($successHandler); + $this->assertSame('/', $options['default_target_path']); + + $defaultPage = '/foo/bar'; + $configResolver = $this->createMock(ConfigResolverInterface::class); + $configResolver + ->expects($this->once()) + ->method('getParameter') + ->with('default_page') + ->will($this->returnValue($defaultPage)); + $successHandler->setConfigResolver($configResolver); + $successHandler->setEventDispatcher($this->createMock(EventDispatcherInterface::class)); + + $request = $this->createMock(Request::class); + $request + ->method('getSession') + ->willReturn($this->createMock(Session::class)); + + $request + ->method('getUriForPath') + ->willReturn($defaultPage); + + $successHandler->onAuthenticationSuccess($request, $this->createMock(TokenInterface::class)); + $options = $refOptions->getValue($successHandler); + $this->assertSame($defaultPage, $options['default_target_path']); + } +} + +class_alias(DefaultAuthenticationSuccessHandlerTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\Authentication\DefaultAuthenticationSuccessHandlerTest'); diff --git a/tests/lib/MVC/Symfony/Security/Authentication/GuardRepositoryAuthenticationProviderTest.php b/tests/lib/MVC/Symfony/Security/Authentication/GuardRepositoryAuthenticationProviderTest.php new file mode 100644 index 0000000000..c108fc9c0f --- /dev/null +++ b/tests/lib/MVC/Symfony/Security/Authentication/GuardRepositoryAuthenticationProviderTest.php @@ -0,0 +1,101 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Security\Authentication; + +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Core\MVC\Symfony\Security\Authentication\GuardRepositoryAuthenticationProvider; +use InvalidArgumentException; +use PHPUnit\Framework\TestCase; +use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; +use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; +use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\User\UserCheckerInterface; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Component\Security\Core\User\UserProviderInterface; +use Symfony\Component\Security\Guard\AuthenticatorInterface; +use Symfony\Component\Security\Guard\Token\PostAuthenticationGuardToken; +use Symfony\Component\Security\Guard\Token\PreAuthenticationGuardToken; + +final class GuardRepositoryAuthenticationProviderTest extends TestCase +{ + private GuardRepositoryAuthenticationProvider $authProvider; + + protected function setUp(): void + { + parent::setUp(); + + $user = self::createMock(UserInterface::class); + + $guardAuthenticator = self::createMock(AuthenticatorInterface::class); + $guardAuthenticator + ->method('getUser') + ->willReturn($user); + + $guardAuthenticator + ->method('checkCredentials') + ->willReturn(true); + + $guardAuthenticator + ->method('createAuthenticatedToken') + ->willReturn(new PostAuthenticationGuardToken( + $user, + 'provider-key_authenticator', + [] + )); + + $this->authProvider = new GuardRepositoryAuthenticationProvider( + ['authenticator' => $guardAuthenticator], + self::createMock(UserProviderInterface::class), + 'provider-key', + self::createMock(UserCheckerInterface::class), + self::createMock(UserPasswordHasherInterface::class) + ); + + $this->authProvider->setPermissionResolver( + self::createMock(PermissionResolver::class) + ); + } + + public function testAuthenticateUnsupportedToken() + { + self::expectException(InvalidArgumentException::class); + self::expectExceptionMessage('GuardAuthenticationProvider only supports GuardTokenInterface.'); + + $anonymousToken = self::getMockBuilder(AnonymousToken::class) + ->setConstructorArgs(['secret', self::createMock(UserInterface::class)]) + ->getMock(); + + $this->authProvider->authenticate($anonymousToken); + } + + public function testAuthenticateWrongGuardProviderKey() + { + self::expectException(AuthenticationException::class); + self::expectExceptionMessage(sprintf( + 'Token with provider key "%s" did not originate from any of the guard authenticators of provider "%s".', + 'wrong-key_authenticator', + 'provider-key' + )); + + $guardToken = new PreAuthenticationGuardToken(['test' => 'credentials'], 'wrong-key_authenticator'); + + $this->authProvider->authenticate($guardToken); + } + + public function testAuthenticate() + { + $guardToken = new PreAuthenticationGuardToken(['test' => 'credentials'], 'provider-key_authenticator'); + + $authenticatedToken = $this->authProvider->authenticate($guardToken); + self::assertEquals( + [$guardToken->getGuardProviderKey(), []], + [$authenticatedToken->getProviderKey(), $authenticatedToken->getCredentials()] + ); + } +} diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/RememberMeRepositoryAuthenticationProviderTest.php b/tests/lib/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProviderTest.php similarity index 79% rename from eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/RememberMeRepositoryAuthenticationProviderTest.php rename to tests/lib/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProviderTest.php index f8581669c3..53aaee14e4 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/RememberMeRepositoryAuthenticationProviderTest.php +++ b/tests/lib/MVC/Symfony/Security/Authentication/RememberMeRepositoryAuthenticationProviderTest.php @@ -4,24 +4,25 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests\Authentication; +namespace Ibexa\Tests\Core\MVC\Symfony\Security\Authentication; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Values\User\User as ApiUser; -use eZ\Publish\Core\MVC\Symfony\Security\Authentication\RememberMeRepositoryAuthenticationProvider; -use eZ\Publish\Core\MVC\Symfony\Security\User; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\Values\User\User as ApiUser; +use Ibexa\Core\MVC\Symfony\Security\Authentication\RememberMeRepositoryAuthenticationProvider; +use Ibexa\Core\MVC\Symfony\Security\User; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken; use Symfony\Component\Security\Core\Authentication\Token\RememberMeToken; +use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; class RememberMeRepositoryAuthenticationProviderTest extends TestCase { - /** @var \eZ\Publish\Core\MVC\Symfony\Security\Authentication\RememberMeRepositoryAuthenticationProvider */ + /** @var \Ibexa\Core\MVC\Symfony\Security\Authentication\RememberMeRepositoryAuthenticationProvider */ private $authProvider; - /** @var \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ private $permissionResolver; protected function setUp(): void @@ -39,7 +40,7 @@ protected function setUp(): void public function testAuthenticateUnsupportedToken() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); + $this->expectException(AuthenticationException::class); $this->expectExceptionMessage('The token is not supported by this authentication provider.'); $anonymousToken = $this @@ -51,7 +52,7 @@ public function testAuthenticateUnsupportedToken() public function testAuthenticateWrongProviderKey() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); + $this->expectException(AuthenticationException::class); $this->expectExceptionMessage('The token is not supported by this authentication provider.'); $user = $this->createMock(UserInterface::class); @@ -74,7 +75,7 @@ public function testAuthenticateWrongProviderKey() public function testAuthenticateWrongSecret() { - $this->expectException(\Symfony\Component\Security\Core\Exception\AuthenticationException::class); + $this->expectException(AuthenticationException::class); $user = $this->createMock(UserInterface::class); $user @@ -116,3 +117,5 @@ public function testAuthenticate() ); } } + +class_alias(RememberMeRepositoryAuthenticationProviderTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\Authentication\RememberMeRepositoryAuthenticationProviderTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/RepositoryAuthenticationProviderTest.php b/tests/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProviderTest.php similarity index 88% rename from eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/RepositoryAuthenticationProviderTest.php rename to tests/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProviderTest.php index 13245f7347..2fc7f3f009 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/Authentication/RepositoryAuthenticationProviderTest.php +++ b/tests/lib/MVC/Symfony/Security/Authentication/RepositoryAuthenticationProviderTest.php @@ -4,21 +4,22 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests\Authentication; - -use eZ\Bundle\EzPublishCoreBundle\DependencyInjection\Compiler\SecurityPass; -use eZ\Publish\API\Repository\Exceptions\PasswordInUnsupportedFormatException; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\Core\MVC\Symfony\Security\Authentication\RepositoryAuthenticationProvider; -use eZ\Publish\Core\MVC\Symfony\Security\User; -use eZ\Publish\Core\Repository\User\Exception\UnsupportedPasswordHashType; +namespace Ibexa\Tests\Core\MVC\Symfony\Security\Authentication; + +use Ibexa\Bundle\Core\DependencyInjection\Compiler\SecurityPass; +use Ibexa\Contracts\Core\Repository\Exceptions\PasswordInUnsupportedFormatException; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\User\User as APIUser; +use Ibexa\Core\MVC\Symfony\Security\Authentication\RepositoryAuthenticationProvider; +use Ibexa\Core\MVC\Symfony\Security\User; +use Ibexa\Core\Repository\User\Exception\UnsupportedPasswordHashType; use PHPUnit\Framework\TestCase; use Psr\Log\LoggerInterface; use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken; use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface; use Symfony\Component\Security\Core\Exception\AuthenticationException; +use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\User\UserCheckerInterface; use Symfony\Component\Security\Core\User\UserInterface; use Symfony\Component\Security\Core\User\UserProviderInterface; @@ -31,10 +32,10 @@ class RepositoryAuthenticationProviderTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject|\Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface */ private $encoderFactory; - /** @var \eZ\Publish\Core\MVC\Symfony\Security\Authentication\RepositoryAuthenticationProvider */ + /** @var \Ibexa\Core\MVC\Symfony\Security\Authentication\RepositoryAuthenticationProvider */ private $authProvider; - /** @var \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ private $permissionResolver; /** @@ -42,7 +43,7 @@ class RepositoryAuthenticationProviderTest extends TestCase */ private $userProvider; - /** @var \eZ\Publish\API\Repository\UserService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\UserService|\PHPUnit\Framework\MockObject\MockObject */ private $userService; /** @var \Psr\Log\LoggerInterface|\PHPUnit\Framework\MockObject\MockObject */ @@ -91,7 +92,7 @@ public function testAuthenticationNotEzUser() public function testCheckAuthenticationCredentialsChanged() { - $this->expectException(\Symfony\Component\Security\Core\Exception\BadCredentialsException::class); + $this->expectException(BadCredentialsException::class); $apiUser = $this->getMockBuilder(APIUser::class) ->setConstructorArgs([['passwordHash' => 'some_encoded_password']]) @@ -148,7 +149,7 @@ public function testCheckAuthenticationAlreadyLoggedIn() public function testCheckAuthenticationFailed() { - $this->expectException(\Symfony\Component\Security\Core\Exception\BadCredentialsException::class); + $this->expectException(BadCredentialsException::class); $apiUser = $this->createMock(APIUser::class); $user = $this->createMock(User::class); @@ -268,3 +269,5 @@ public function testAuthenticateConstantTimeDisabled(): void $this->authProvider->authenticate($token); } } + +class_alias(RepositoryAuthenticationProviderTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\Authentication\RepositoryAuthenticationProviderTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/EventListener/SecurityListenerTest.php b/tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/Security/Tests/EventListener/SecurityListenerTest.php rename to tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php index 295daceaeb..1cf8624b4b 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/EventListener/SecurityListenerTest.php +++ b/tests/lib/MVC/Symfony/Security/EventListener/SecurityListenerTest.php @@ -4,18 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests\EventListener; - -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute; -use eZ\Publish\Core\MVC\Symfony\Security\EventListener\SecurityListener; -use eZ\Publish\Core\MVC\Symfony\Security\Exception\UnauthorizedSiteAccessException; -use eZ\Publish\Core\MVC\Symfony\Security\InteractiveLoginToken; -use eZ\Publish\Core\MVC\Symfony\Security\UserInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +namespace Ibexa\Tests\Core\MVC\Symfony\Security\EventListener; + +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\User\User as APIUser; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute; +use Ibexa\Core\MVC\Symfony\Security\EventListener\SecurityListener; +use Ibexa\Core\MVC\Symfony\Security\Exception\UnauthorizedSiteAccessException; +use Ibexa\Core\MVC\Symfony\Security\InteractiveLoginToken; +use Ibexa\Core\MVC\Symfony\Security\UserInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; @@ -43,13 +43,13 @@ class SecurityListenerTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ protected $authChecker; - /** @var \eZ\Publish\Core\MVC\Symfony\Security\EventListener\SecurityListener */ + /** @var \Ibexa\Core\MVC\Symfony\Security\EventListener\SecurityListener */ protected $listener; - /** @var \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ private $permissionResolver; - /** @var \eZ\Publish\API\Repository\UserService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\UserService|\PHPUnit\Framework\MockObject\MockObject */ private $userService; protected function setUp(): void @@ -172,7 +172,7 @@ public function testOnInteractiveLogin() public function testCheckSiteAccessPermissionDenied() { - $this->expectException(\eZ\Publish\Core\MVC\Symfony\Security\Exception\UnauthorizedSiteAccessException::class); + $this->expectException(UnauthorizedSiteAccessException::class); $user = $this->createMock(UserInterface::class); $token = $this->createMock(TokenInterface::class); @@ -412,3 +412,5 @@ public function testOnKernelRequestAccessGranted() $this->listener->onKernelRequest($event); } } + +class_alias(SecurityListenerTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\EventListener\SecurityListenerTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/HttpUtilsTest.php b/tests/lib/MVC/Symfony/Security/HttpUtilsTest.php similarity index 96% rename from eZ/Publish/Core/MVC/Symfony/Security/Tests/HttpUtilsTest.php rename to tests/lib/MVC/Symfony/Security/HttpUtilsTest.php index dc5ddc6bb7..ccf6d99b17 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/HttpUtilsTest.php +++ b/tests/lib/MVC/Symfony/Security/HttpUtilsTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Security; -use eZ\Publish\Core\MVC\Symfony\Security\HttpUtils; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\Security\HttpUtils; +use Ibexa\Core\MVC\Symfony\SiteAccess; use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; @@ -138,3 +138,5 @@ public function checkRequestPathProvider() ]; } } + +class_alias(HttpUtilsTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\HttpUtilsTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/InteractiveLoginTokenTest.php b/tests/lib/MVC/Symfony/Security/InteractiveLoginTokenTest.php similarity index 86% rename from eZ/Publish/Core/MVC/Symfony/Security/Tests/InteractiveLoginTokenTest.php rename to tests/lib/MVC/Symfony/Security/InteractiveLoginTokenTest.php index 9a6af81e5d..5e9b35cf8d 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/InteractiveLoginTokenTest.php +++ b/tests/lib/MVC/Symfony/Security/InteractiveLoginTokenTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Security; -use eZ\Publish\Core\MVC\Symfony\Security\InteractiveLoginToken; -use eZ\Publish\Core\MVC\Symfony\Security\UserInterface; +use Ibexa\Core\MVC\Symfony\Security\InteractiveLoginToken; +use Ibexa\Core\MVC\Symfony\Security\UserInterface; use PHPUnit\Framework\TestCase; class InteractiveLoginTokenTest extends TestCase @@ -51,3 +51,5 @@ public function testSerialize() $this->assertEquals($token, $unserializedToken); } } + +class_alias(InteractiveLoginTokenTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\InteractiveLoginTokenTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/User/EmailProviderTest.php b/tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php similarity index 77% rename from eZ/Publish/Core/MVC/Symfony/Security/Tests/User/EmailProviderTest.php rename to tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php index ab2b966589..a99edb2a58 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/User/EmailProviderTest.php +++ b/tests/lib/MVC/Symfony/Security/User/EmailProviderTest.php @@ -6,32 +6,34 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests\User; - -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\MVC\Symfony\Security\User as MVCUser; -use eZ\Publish\Core\MVC\Symfony\Security\User\EmailProvider; -use eZ\Publish\Core\MVC\Symfony\Security\UserInterface; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\User\User; -use eZ\Publish\Core\Repository\Values\User\UserReference; +namespace Ibexa\Tests\Core\MVC\Symfony\Security\User; + +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\User\User as APIUser; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\MVC\Symfony\Security\User as MVCUser; +use Ibexa\Core\MVC\Symfony\Security\User\EmailProvider; +use Ibexa\Core\MVC\Symfony\Security\UserInterface; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\User\User; +use Ibexa\Core\Repository\Values\User\UserReference; use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\Exception\UnsupportedUserException; +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\User\UserInterface as SymfonyUserInterface; class EmailProviderTest extends TestCase { - /** @var \eZ\Publish\API\Repository\UserService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\UserService|\PHPUnit\Framework\MockObject\MockObject */ private $userService; - /** @var \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ private $permissionResolver; - /** @var \eZ\Publish\Core\MVC\Symfony\Security\User\EmailProvider */ + /** @var \Ibexa\Core\MVC\Symfony\Security\User\EmailProvider */ private $userProvider; protected function setUp(): void @@ -50,7 +52,7 @@ public function testLoadUserByUsernameAlreadyUserObject() public function testLoadUserByUsernameUserNotFound() { - $this->expectException(\Symfony\Component\Security\Core\Exception\UsernameNotFoundException::class); + $this->expectException(UsernameNotFoundException::class); $username = 'foobar@example.org'; $this->userService @@ -80,7 +82,7 @@ public function testLoadUserByUsername() public function testRefreshUserNotSupported() { - $this->expectException(\Symfony\Component\Security\Core\Exception\UnsupportedUserException::class); + $this->expectException(UnsupportedUserException::class); $user = $this->createMock(SymfonyUserInterface::class); $this->userProvider->refreshUser($user); @@ -127,7 +129,7 @@ public function testRefreshUser() public function testRefreshUserNotFound() { - $this->expectException(\Symfony\Component\Security\Core\Exception\UsernameNotFoundException::class); + $this->expectException(UsernameNotFoundException::class); $userId = 123; $apiUser = new User( @@ -184,3 +186,5 @@ public function testLoadUserByAPIUser() $this->assertSame(['ROLE_USER'], $user->getRoles()); } } + +class_alias(EmailProviderTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\User\EmailProviderTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/User/UsernameProviderTest.php b/tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php similarity index 77% rename from eZ/Publish/Core/MVC/Symfony/Security/Tests/User/UsernameProviderTest.php rename to tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php index cb505f208e..f03117f9d5 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/User/UsernameProviderTest.php +++ b/tests/lib/MVC/Symfony/Security/User/UsernameProviderTest.php @@ -6,32 +6,34 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests\User; - -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\MVC\Symfony\Security\User as MVCUser; -use eZ\Publish\Core\MVC\Symfony\Security\User\UsernameProvider; -use eZ\Publish\Core\MVC\Symfony\Security\UserInterface; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\User\User; -use eZ\Publish\Core\Repository\Values\User\UserReference; +namespace Ibexa\Tests\Core\MVC\Symfony\Security\User; + +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\User\User as APIUser; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\MVC\Symfony\Security\User as MVCUser; +use Ibexa\Core\MVC\Symfony\Security\User\UsernameProvider; +use Ibexa\Core\MVC\Symfony\Security\UserInterface; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\User\User; +use Ibexa\Core\Repository\Values\User\UserReference; use PHPUnit\Framework\TestCase; +use Symfony\Component\Security\Core\Exception\UnsupportedUserException; +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Core\User\UserInterface as SymfonyUserInterface; class UsernameProviderTest extends TestCase { - /** @var \eZ\Publish\API\Repository\UserService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\UserService|\PHPUnit\Framework\MockObject\MockObject */ private $userService; - /** @var \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ private $permissionResolver; - /** @var \eZ\Publish\Core\MVC\Symfony\Security\User\UsernameProvider */ + /** @var \Ibexa\Core\MVC\Symfony\Security\User\UsernameProvider */ private $userProvider; protected function setUp(): void @@ -50,7 +52,7 @@ public function testLoadUserByUsernameAlreadyUserObject() public function testLoadUserByUsernameUserNotFound() { - $this->expectException(\Symfony\Component\Security\Core\Exception\UsernameNotFoundException::class); + $this->expectException(UsernameNotFoundException::class); $username = 'foobar'; $this->userService @@ -80,7 +82,7 @@ public function testLoadUserByUsername() public function testRefreshUserNotSupported() { - $this->expectException(\Symfony\Component\Security\Core\Exception\UnsupportedUserException::class); + $this->expectException(UnsupportedUserException::class); $user = $this->createMock(SymfonyUserInterface::class); $this->userProvider->refreshUser($user); @@ -127,7 +129,7 @@ public function testRefreshUser() public function testRefreshUserNotFound() { - $this->expectException(\Symfony\Component\Security\Core\Exception\UsernameNotFoundException::class); + $this->expectException(UsernameNotFoundException::class); $userId = 123; $apiUser = new User( @@ -184,3 +186,5 @@ public function testLoadUserByAPIUser() $this->assertSame(['ROLE_USER'], $user->getRoles()); } } + +class_alias(UsernameProviderTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\User\UsernameProviderTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/UserCheckerTest.php b/tests/lib/MVC/Symfony/Security/UserCheckerTest.php similarity index 88% rename from eZ/Publish/Core/MVC/Symfony/Security/Tests/UserCheckerTest.php rename to tests/lib/MVC/Symfony/Security/UserCheckerTest.php index ea74a93a9e..1d3c6b6e77 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/UserCheckerTest.php +++ b/tests/lib/MVC/Symfony/Security/UserCheckerTest.php @@ -6,18 +6,18 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Security; use DateTimeImmutable; -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\User\PasswordInfo; -use eZ\Publish\Core\MVC\Symfony\Security\User; -use eZ\Publish\Core\MVC\Symfony\Security\UserChecker; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\User\User as APIUser; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\User\PasswordInfo; use Ibexa\Core\MVC\Symfony\Security\Exception\PasswordExpiredException; +use Ibexa\Core\MVC\Symfony\Security\User; +use Ibexa\Core\MVC\Symfony\Security\UserChecker; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\User\User as APIUser; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Exception\DisabledException; use Throwable; @@ -29,7 +29,7 @@ final class UserCheckerTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject */ private $userServiceMock; - /** @var \eZ\Publish\Core\MVC\Symfony\Security\UserChecker */ + /** @var \Ibexa\Core\MVC\Symfony\Security\UserChecker */ private $userChecker; protected function setUp(): void @@ -162,3 +162,5 @@ public function testCheckPostAuthWithExpiredUser(): void $this->userChecker->checkPostAuth(new User($apiUser)); } } + +class_alias(UserCheckerTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\UserCheckerTest'); diff --git a/tests/lib/MVC/Symfony/Security/UserTest.php b/tests/lib/MVC/Symfony/Security/UserTest.php new file mode 100644 index 0000000000..da74c5f5f5 --- /dev/null +++ b/tests/lib/MVC/Symfony/Security/UserTest.php @@ -0,0 +1,133 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\MVC\Symfony\Security; + +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\User\User as APIUser; +use Ibexa\Core\MVC\Symfony\Security\ReferenceUserInterface; +use Ibexa\Core\MVC\Symfony\Security\User; +use Ibexa\Core\Repository\Values\User\UserReference; +use PHPUnit\Framework\TestCase; + +class UserTest extends TestCase +{ + public function testConstruct() + { + $login = 'my_username'; + $passwordHash = 'encoded_password'; + $apiUser = $this + ->getMockBuilder(APIUser::class) + ->setConstructorArgs( + [ + [ + 'login' => $login, + 'passwordHash' => $passwordHash, + 'enabled' => true, + ], + ] + ) + ->setMethods(['getUserId']) + ->getMockForAbstractClass(); + + $roles = ['ROLE_USER']; + $apiUser + ->expects($this->once()) + ->method('getUserId') + ->will($this->returnValue(42)); + + $user = new User($apiUser, $roles); + $this->assertSame($apiUser, $user->getAPIUser()); + $this->assertSame($login, $user->getUsername()); + $this->assertSame($passwordHash, $user->getPassword()); + $this->assertSame($roles, $user->getRoles()); + $this->assertNull($user->getSalt()); + } + + public function testIsEqualTo() + { + $userId = 123; + $apiUser = $this->createMock(APIUser::class); + $apiUser + ->expects($this->once()) + ->method('getUserId') + ->will($this->returnValue($userId)); + $roles = ['ROLE_USER']; + + $user = new User($apiUser, $roles); + + $apiUser2 = $this->createMock(APIUser::class); + $apiUser2 + ->expects($this->once()) + ->method('getUserId') + ->will($this->returnValue($userId)); + $user2 = new User($apiUser2, []); + + $this->assertTrue($user->isEqualTo($user2)); + } + + public function testIsNotEqualTo() + { + $apiUser = $this->createMock(APIUser::class); + $apiUser + ->expects($this->once()) + ->method('getUserId') + ->will($this->returnValue(123)); + $roles = ['ROLE_USER']; + + $user = new User($apiUser, $roles); + + $apiUser2 = $this->createMock(APIUser::class); + $apiUser2 + ->expects($this->once()) + ->method('getUserId') + ->will($this->returnValue(456)); + $user2 = new User($apiUser2, []); + + $this->assertFalse($user->isEqualTo($user2)); + } + + public function testIsEqualToNotSameUserType() + { + $user = new User($this->createMock(APIUser::class)); + $user2 = $this->createMock(ReferenceUserInterface::class); + $user2 + ->expects($this->once()) + ->method('getAPIUserReference') + ->willReturn(new UserReference(456)); + $this->assertFalse($user->isEqualTo($user2)); + } + + public function testSetAPIUser() + { + $apiUserA = $this->createMock(APIUser::class); + $apiUserB = $this->createMock(APIUser::class); + + $user = new User($apiUserA); + $user->setAPIUser($apiUserB); + $this->assertSame($apiUserB, $user->getAPIUser()); + } + + public function testToString(): void + { + $fullName = 'My full name'; + $userContentInfo = $this->createMock(ContentInfo::class); + + $userContentInfo + ->method('getName') + ->willReturn($fullName); + + $apiUser = $this->createMock(APIUser::class); + $apiUser + ->method('getContentInfo') + ->willReturn($userContentInfo); + + $user = new User($apiUser); + self::assertSame($fullName, (string)$user); + } +} + +class_alias(UserTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\UserTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/UserWrappedTest.php b/tests/lib/MVC/Symfony/Security/UserWrappedTest.php similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/Security/Tests/UserWrappedTest.php rename to tests/lib/MVC/Symfony/Security/UserWrappedTest.php index 6ec1e5b1ec..e9c3075730 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/UserWrappedTest.php +++ b/tests/lib/MVC/Symfony/Security/UserWrappedTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Security; -use eZ\Publish\API\Repository\Values\User\User as APIUser; -use eZ\Publish\Core\MVC\Symfony\Security\UserInterface; -use eZ\Publish\Core\MVC\Symfony\Security\UserWrapped; +use Ibexa\Contracts\Core\Repository\Values\User\User as APIUser; +use Ibexa\Core\MVC\Symfony\Security\UserInterface; +use Ibexa\Core\MVC\Symfony\Security\UserWrapped; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\User\EquatableInterface; use Symfony\Component\Security\Core\User\UserInterface as SymfonyUserInterface; @@ -117,3 +117,5 @@ public function testNotSerializeApiUser(): void interface UserEquatableInterface extends UserInterface, EquatableInterface { } + +class_alias(UserWrappedTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\UserWrappedTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/Voter/CoreVoterTest.php b/tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php similarity index 91% rename from eZ/Publish/Core/MVC/Symfony/Security/Tests/Voter/CoreVoterTest.php rename to tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php index ec2ded7282..7a0c8cd572 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/Voter/CoreVoterTest.php +++ b/tests/lib/MVC/Symfony/Security/Voter/CoreVoterTest.php @@ -4,20 +4,20 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests\Voter; +namespace Ibexa\Tests\Core\MVC\Symfony\Security\Voter; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\MVC\Symfony\Controller\Content\ViewController; -use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute; -use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Voter\CoreVoter; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\MVC\Symfony\Controller\Content\ViewController; +use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute; +use Ibexa\Core\MVC\Symfony\Security\Authorization\Voter\CoreVoter; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; class CoreVoterTest extends TestCase { - /** @var \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ private $permissionResolver; protected function setUp(): void @@ -211,3 +211,5 @@ public function voteProvider() ]; } } + +class_alias(CoreVoterTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\Voter\CoreVoterTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Security/Tests/Voter/ValueObjectVoterTest.php b/tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php similarity index 84% rename from eZ/Publish/Core/MVC/Symfony/Security/Tests/Voter/ValueObjectVoterTest.php rename to tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php index 1ced4e8aae..fd6ff413b3 100644 --- a/eZ/Publish/Core/MVC/Symfony/Security/Tests/Voter/ValueObjectVoterTest.php +++ b/tests/lib/MVC/Symfony/Security/Voter/ValueObjectVoterTest.php @@ -4,20 +4,20 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Security\Tests\Voter; +namespace Ibexa\Tests\Core\MVC\Symfony\Security\Voter; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\MVC\Symfony\Controller\Content\ViewController; -use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Attribute; -use eZ\Publish\Core\MVC\Symfony\Security\Authorization\Voter\ValueObjectVoter; -use eZ\Publish\Core\Repository\Permission\PermissionResolver; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\MVC\Symfony\Controller\Content\ViewController; +use Ibexa\Core\MVC\Symfony\Security\Authorization\Attribute; +use Ibexa\Core\MVC\Symfony\Security\Authorization\Voter\ValueObjectVoter; +use Ibexa\Core\Repository\Permission\PermissionResolver; use PHPUnit\Framework\TestCase; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface; class ValueObjectVoterTest extends TestCase { - /** @var \eZ\Publish\Core\Repository\Permission\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\Repository\Permission\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ private $permissionResolver; protected function setUp(): void @@ -127,12 +127,20 @@ public function voteProvider() { return [ [ - new Attribute('content', 'read', ['valueObject' => $this->getMockForAbstractClass('eZ\Publish\API\Repository\Values\ValueObject')]), + new Attribute('content', 'read', [ + 'valueObject' => $this->getMockForAbstractClass( + ValueObject::class + ), + ]), true, VoterInterface::ACCESS_GRANTED, ], [ - new Attribute('content', 'read', ['valueObject' => $this->getMockForAbstractClass('eZ\Publish\API\Repository\Values\ValueObject')]), + new Attribute('content', 'read', [ + 'valueObject' => $this->getMockForAbstractClass( + ValueObject::class + ), + ]), false, VoterInterface::ACCESS_DENIED, ], @@ -187,3 +195,5 @@ public function voteProvider() ]; } } + +class_alias(ValueObjectVoterTest::class, 'eZ\Publish\Core\MVC\Symfony\Security\Tests\Voter\ValueObjectVoterTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/Compound/CompoundAndTest.php b/tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/Compound/CompoundAndTest.php rename to tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php index 7883e20b65..5d33b936e0 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/Compound/CompoundAndTest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundAndTest.php @@ -4,16 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\Compound; - -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound\LogicalAnd; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilder; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\VersatileMatcher; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess\Compound; + +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Compound; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Compound\LogicalAnd; +use Ibexa\Core\MVC\Symfony\SiteAccess\MatcherBuilder; +use Ibexa\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess\VersatileMatcher; use PHPUnit\Framework\TestCase; class CompoundAndTest extends TestCase @@ -28,7 +28,7 @@ protected function setUp(): void } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound\LogicalAnd + * @return \Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Compound\LogicalAnd */ public function testConstruct() { @@ -36,7 +36,7 @@ public function testConstruct() } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound\LogicalAnd + * @return \Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Compound\LogicalAnd */ private function buildMatcher() { @@ -90,7 +90,7 @@ public function testSetMatcherBuilder(Compound $compoundMatcher) /** * @dataProvider matchProvider * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $request * @param $expectedMatch */ public function testMatch(SimplifiedRequest $request, $expectedMatch) @@ -329,3 +329,5 @@ public function testSerialize() $this->assertSame($serializedSA1, $serializedSA2); } } + +class_alias(CompoundAndTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\Compound\CompoundAndTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/Compound/CompoundOrTest.php b/tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundOrTest.php similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/Compound/CompoundOrTest.php rename to tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundOrTest.php index bdaef05b6b..bf83f7eb91 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/Compound/CompoundOrTest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/Compound/CompoundOrTest.php @@ -4,16 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\Compound; - -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound\LogicalOr; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilder; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\VersatileMatcher; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess\Compound; + +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Compound; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Compound\LogicalOr; +use Ibexa\Core\MVC\Symfony\SiteAccess\MatcherBuilder; +use Ibexa\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess\VersatileMatcher; use PHPUnit\Framework\TestCase; class CompoundOrTest extends TestCase @@ -27,18 +27,12 @@ protected function setUp(): void $this->matcherBuilder = $this->createMock(MatcherBuilderInterface::class); } - /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound\LogicalAnd - */ - public function testConstruct() + public function testConstruct(): LogicalOr { return $this->buildMatcher(); } - /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Compound\LogicalOr - */ - private function buildMatcher() + private function buildMatcher(): LogicalOr { return new LogicalOr( [ @@ -82,7 +76,7 @@ public function testSetMatcherBuilder(Compound $compoundMatcher) /** * @dataProvider matchProvider * - * @param \eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest $request + * @param \Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest $request * @param string $expectedMatch */ public function testMatch(SimplifiedRequest $request, $expectedMatch) @@ -337,3 +331,5 @@ public function testSerialize() $this->assertSame($serializedSA1, $serializedSA2); } } + +class_alias(CompoundOrTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\Compound\CompoundOrTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/MatcherSerializationTest.php b/tests/lib/MVC/Symfony/SiteAccess/MatcherSerializationTest.php similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/MatcherSerializationTest.php rename to tests/lib/MVC/Symfony/SiteAccess/MatcherSerializationTest.php index 277e9888d3..05ebdf65a8 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/MatcherSerializationTest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/MatcherSerializationTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Component\Serializer\SerializerTrait; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\Component\Serializer\SerializerTrait; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; use PHPUnit\Framework\TestCase; class MatcherSerializationTest extends TestCase @@ -15,7 +15,7 @@ class MatcherSerializationTest extends TestCase use SerializerTrait; /** - * @param \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher|null $expected + * @param \Ibexa\Core\MVC\Symfony\SiteAccess\Matcher|null $expected * * @dataProvider matcherProvider */ @@ -43,7 +43,7 @@ private function serializeMatcher(Matcher $matcher) * @param string $serializedMatcher * @param string $matcherFQCN * - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher|object + * @return \Ibexa\Core\MVC\Symfony\SiteAccess\Matcher|object */ private function deserializeMatcher($serializedMatcher, $matcherFQCN) { @@ -171,3 +171,5 @@ private function getMapURIMatcherTestCase(): array return [$matcherBeforeSerialization, $matcherAfterDeserialization]; } } + +class_alias(MatcherSerializationTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\MatcherSerializationTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/Provider/ChainSiteAccessProviderTest.php b/tests/lib/MVC/Symfony/SiteAccess/Provider/ChainSiteAccessProviderTest.php similarity index 89% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/Provider/ChainSiteAccessProviderTest.php rename to tests/lib/MVC/Symfony/SiteAccess/Provider/ChainSiteAccessProviderTest.php index f86a686baf..bb0eaf014b 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/Provider/ChainSiteAccessProviderTest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/Provider/ChainSiteAccessProviderTest.php @@ -6,14 +6,14 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\Provider; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess\Provider; use function array_map; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Provider\ChainSiteAccessProvider; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Provider\StaticSiteAccessProvider; -use eZ\Publish\Core\MVC\Symfony\SiteAccessGroup; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Provider\ChainSiteAccessProvider; +use Ibexa\Core\MVC\Symfony\SiteAccess\Provider\StaticSiteAccessProvider; +use Ibexa\Core\MVC\Symfony\SiteAccessGroup; use PHPUnit\Framework\TestCase; final class ChainSiteAccessProviderTest extends TestCase @@ -23,7 +23,7 @@ final class ChainSiteAccessProviderTest extends TestCase private const WITHOUT_GROUP_SA_NAME = 'without_group_sa'; private const SA_GROUP = 'group'; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface[] */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface[] */ private $providers; /** @var array */ @@ -117,7 +117,7 @@ public function getExistingSiteProvider(): array * * @param string[] $expectedGroups * - * @throws \eZ\Publish\Core\Base\Exceptions\NotFoundException + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException */ public function testGetExistingSiteAccess(string $siteAccessName, array $expectedGroups): void { @@ -172,3 +172,5 @@ static function (string $groupName) { return $undefinedSiteAccess; } } + +class_alias(ChainSiteAccessProviderTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\Provider\ChainSiteAccessProviderTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterBaseTest.php b/tests/lib/MVC/Symfony/SiteAccess/RouterBaseTest.php similarity index 80% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterBaseTest.php rename to tests/lib/MVC/Symfony/SiteAccess/RouterBaseTest.php index c07abb577b..0760b6284e 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterBaseTest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/RouterBaseTest.php @@ -6,12 +6,12 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilder; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\MatcherBuilder; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; use PHPUnit\Framework\TestCase; abstract class RouterBaseTest extends TestCase @@ -22,10 +22,10 @@ abstract class RouterBaseTest extends TestCase protected const DEFAULT_SA_NAME = 'default_sa'; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilder */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\MatcherBuilder */ protected $matcherBuilder; - /** @var \eZ\Publish\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface */ + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface */ protected $siteAccessProvider; protected function setUp(): void @@ -49,7 +49,7 @@ public function testMatch(SimplifiedRequest $request, string $siteAccess) $sa = $router->match($request); $this->assertInstanceOf(SiteAccess::class, $sa); $this->assertSame($siteAccess, $sa->name); - // SiteAccess must be serializable as a whole. See https://jira.ez.no/browse/EZP-21613 + // SiteAccess must be serializable as a whole. See https://issues.ibexa.co/browse/EZP-21613 $this->assertIsString(serialize($sa)); $router->setSiteAccess(); } @@ -84,7 +84,9 @@ private function createSiteAccessProviderMock(): SiteAccess\SiteAccessProviderIn } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessSetting[] + * @return \Ibexa\Tests\Core\MVC\Symfony\SiteAccess\SiteAccessSetting[] */ abstract public function getSiteAccessProviderSettings(): array; } + +class_alias(RouterBaseTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\RouterBaseTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterHostElementTest.php b/tests/lib/MVC/Symfony/SiteAccess/RouterHostElementTest.php similarity index 88% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterHostElementTest.php rename to tests/lib/MVC/Symfony/SiteAccess/RouterHostElementTest.php index 3545270ae2..b4ef57fb06 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterHostElementTest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/RouterHostElementTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\HostElement; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map\Host as HostMapMatcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\HostElement; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Map\Host as HostMapMatcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; use Psr\Log\LoggerInterface; class RouterHostElementTest extends RouterBaseTest @@ -97,7 +97,7 @@ public function reverseMatchProvider() { return [ ['foo', 1, SimplifiedRequest::fromUrl('http://bar.example.com/'), 'foo.example.com'], - ['ezdemo_site', 1, SimplifiedRequest::fromUrl('http://ezflow_site.ez.no/'), 'ezdemo_site.ez.no'], + ['ibexa_demo_site', 1, SimplifiedRequest::fromUrl('http://ezflow_site.ibexa.co/'), 'ibexa_demo_site.ibexa.co'], ['metalfrance', 2, SimplifiedRequest::fromUrl('http://www.lolart.net/'), 'www.metalfrance.net'], ['fm', 3, SimplifiedRequest::fromUrl('http://www.phoenix-rises.fr/'), 'www.phoenix-rises.fm'], ]; @@ -106,18 +106,18 @@ public function reverseMatchProvider() public function testReverseMatchFail() { $matcher = new HostElement([3]); - $matcher->setRequest(new SimplifiedRequest(['host' => 'ez.no'])); + $matcher->setRequest(new SimplifiedRequest(['host' => 'ibexa.co'])); $this->assertNull($matcher->reverseMatch('foo')); } public function testSerialize() { $matcher = new HostElement([1]); - $matcher->setRequest(new SimplifiedRequest(['host' => 'ez.no', 'pathinfo' => '/foo/bar'])); + $matcher->setRequest(new SimplifiedRequest(['host' => 'ibexa.co', 'pathinfo' => '/foo/bar'])); $sa = new SiteAccess('test', 'test', $matcher); $serializedSA1 = serialize($sa); - $matcher->setRequest(new SimplifiedRequest(['host' => 'ez.no', 'pathinfo' => '/foo/bar/baz'])); + $matcher->setRequest(new SimplifiedRequest(['host' => 'ibexa.co', 'pathinfo' => '/foo/bar/baz'])); $serializedSA2 = serialize($sa); $this->assertSame($serializedSA1, $serializedSA2); @@ -148,7 +148,7 @@ protected function createRouter(): Router } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessSetting[] + * @return \Ibexa\Tests\Core\MVC\Symfony\SiteAccess\SiteAccessSetting[] */ public function getSiteAccessProviderSettings(): array { @@ -163,3 +163,5 @@ public function getSiteAccessProviderSettings(): array ]; } } + +class_alias(RouterHostElementTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\RouterHostElementTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterHostPortURITest.php b/tests/lib/MVC/Symfony/SiteAccess/RouterHostPortURITest.php similarity index 91% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterHostPortURITest.php rename to tests/lib/MVC/Symfony/SiteAccess/RouterHostPortURITest.php index 10de06f22a..691bab36ff 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterHostPortURITest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/RouterHostPortURITest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map\Host; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map\Port; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Map\Host; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Map\Port; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; use Psr\Log\LoggerInterface; class RouterHostPortURITest extends RouterBaseTest @@ -102,16 +102,16 @@ public function testReverseHostMatchFail() public function testReverseMatchHost() { $config = [ - 'ez.no' => 'some_siteaccess', + 'ibexa.co' => 'some_siteaccess', 'something_else' => 'another_siteaccess', - 'phoenix-rises.fm' => 'ezdemo_site', + 'phoenix-rises.fm' => 'ibexa_demo_site', ]; - $request = new SimplifiedRequest(['host' => 'ez.no']); + $request = new SimplifiedRequest(['host' => 'ibexa.co']); $matcher = new Host($config); $matcher->setRequest($request); - $this->assertSame('ez.no', $matcher->getMapKey()); + $this->assertSame('ibexa.co', $matcher->getMapKey()); - $result = $matcher->reverseMatch('ezdemo_site'); + $result = $matcher->reverseMatch('ibexa_demo_site'); $this->assertInstanceOf(Host::class, $result); $this->assertSame($request, $matcher->getRequest()); $this->assertSame('phoenix-rises.fm', $result->getMapKey()); @@ -140,14 +140,14 @@ public function testReverseMatchPort() $config = [ '80' => 'some_siteaccess', '443' => 'another_siteaccess', - 8000 => 'ezdemo_site', + 8000 => 'ibexa_demo_site', ]; - $request = new SimplifiedRequest(['scheme' => 'http', 'host' => 'ez.no']); + $request = new SimplifiedRequest(['scheme' => 'http', 'host' => 'ibexa.co']); $matcher = new Port($config); $matcher->setRequest($request); $this->assertSame(80, $matcher->getMapKey()); - $result = $matcher->reverseMatch('ezdemo_site'); + $result = $matcher->reverseMatch('ibexa_demo_site'); $this->assertInstanceOf(Port::class, $result); $this->assertSame($request, $matcher->getRequest()); $this->assertSame(8000, $result->getMapKey()); @@ -185,7 +185,7 @@ protected function createRouter(): Router } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessSetting[] + * @return \Ibexa\Tests\Core\MVC\Symfony\SiteAccess\SiteAccessSetting[] */ public function getSiteAccessProviderSettings(): array { @@ -199,3 +199,5 @@ public function getSiteAccessProviderSettings(): array ]; } } + +class_alias(RouterHostPortURITest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\RouterHostPortURITest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterHostRegexTest.php b/tests/lib/MVC/Symfony/SiteAccess/RouterHostRegexTest.php similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterHostRegexTest.php rename to tests/lib/MVC/Symfony/SiteAccess/RouterHostRegexTest.php index aa731e86d1..cab1732c82 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterHostRegexTest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/RouterHostRegexTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex\Host as HostRegexMatcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Regex\Host as HostRegexMatcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; use Psr\Log\LoggerInterface; class RouterHostRegexTest extends RouterBaseTest @@ -78,7 +78,7 @@ public function testGetName() } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Router + * @return \Ibexa\Core\MVC\Symfony\SiteAccess\Router */ protected function createRouter(): Router { @@ -104,7 +104,7 @@ protected function createRouter(): Router } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessSetting[] + * @return \Ibexa\Tests\Core\MVC\Symfony\SiteAccess\SiteAccessSetting[] */ public function getSiteAccessProviderSettings(): array { @@ -119,3 +119,5 @@ public function getSiteAccessProviderSettings(): array ]; } } + +class_alias(RouterHostRegexTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\RouterHostRegexTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterHostTextTest.php b/tests/lib/MVC/Symfony/SiteAccess/RouterHostTextTest.php similarity index 94% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterHostTextTest.php rename to tests/lib/MVC/Symfony/SiteAccess/RouterHostTextTest.php index 0e86af7410..fbd06ca7b1 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterHostTextTest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/RouterHostTextTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\HostText as HostTextMatcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\HostText as HostTextMatcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; use Psr\Log\LoggerInterface; class RouterHostTextTest extends RouterBaseTest @@ -119,7 +119,7 @@ protected function createRouter(): Router } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessSetting[] + * @return \Ibexa\Tests\Core\MVC\Symfony\SiteAccess\SiteAccessSetting[] */ public function getSiteAccessProviderSettings(): array { @@ -134,3 +134,5 @@ public function getSiteAccessProviderSettings(): array ]; } } + +class_alias(RouterHostTextTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\RouterHostTextTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterMapURITest.php b/tests/lib/MVC/Symfony/SiteAccess/RouterMapURITest.php similarity index 87% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterMapURITest.php rename to tests/lib/MVC/Symfony/SiteAccess/RouterMapURITest.php index 9d8eb929a1..4be7cfdc89 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterMapURITest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/RouterMapURITest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map\URI as URIMapMatcher; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Map\URI as URIMapMatcher; use PHPUnit\Framework\TestCase; class RouterMapURITest extends TestCase @@ -81,7 +81,7 @@ public function fixupURIProvider() ['/foo/foo/bar', '/foo/bar'], ['/foo/foo/bar?something=foo&bar=toto', '/foo/bar?something=foo&bar=toto'], ['/vive/le/sucre', '/le/sucre'], - ['/ezdemo_site/some/thing?foo=ezdemo_site&bar=toto', '/some/thing?foo=ezdemo_site&bar=toto'], + ['/ibexa_demo_site/some/thing?foo=ibexa_demo_site&bar=toto', '/some/thing?foo=ibexa_demo_site&bar=toto'], ]; } @@ -97,16 +97,18 @@ public function testReverseMatch() $config = [ 'some_uri' => 'some_siteaccess', 'something_else' => 'another_siteaccess', - 'toutouyoutou' => 'ezdemo_site', + 'toutouyoutou' => 'ibexa_demo_site', ]; $request = new SimplifiedRequest(['pathinfo' => '/foo']); $matcher = new URIMapMatcher($config); $matcher->setRequest($request); - $result = $matcher->reverseMatch('ezdemo_site'); + $result = $matcher->reverseMatch('ibexa_demo_site'); $this->assertInstanceOf(URIMapMatcher::class, $result); $this->assertSame($request, $matcher->getRequest()); $this->assertSame('toutouyoutou', $result->getMapKey()); $this->assertSame('/toutouyoutou/foo', $result->getRequest()->pathinfo); } } + +class_alias(RouterMapURITest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\RouterMapURITest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterPortHostURITest.php b/tests/lib/MVC/Symfony/SiteAccess/RouterPortHostURITest.php similarity index 95% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterPortHostURITest.php rename to tests/lib/MVC/Symfony/SiteAccess/RouterPortHostURITest.php index a5a85789ed..958ba1cd18 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterPortHostURITest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/RouterPortHostURITest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; use Psr\Log\LoggerInterface; class RouterPortHostURITest extends RouterBaseTest @@ -110,7 +110,7 @@ protected function createRouter(): Router } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessSetting[] + * @return \Ibexa\Tests\Core\MVC\Symfony\SiteAccess\SiteAccessSetting[] */ public function getSiteAccessProviderSettings(): array { @@ -124,3 +124,5 @@ public function getSiteAccessProviderSettings(): array ]; } } + +class_alias(RouterPortHostURITest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\RouterPortHostURITest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterSpecialPortsTest.php b/tests/lib/MVC/Symfony/SiteAccess/RouterSpecialPortsTest.php similarity index 93% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterSpecialPortsTest.php rename to tests/lib/MVC/Symfony/SiteAccess/RouterSpecialPortsTest.php index c4c00b448e..60710c9353 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterSpecialPortsTest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/RouterSpecialPortsTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Map\Port as PortMatcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Map\Port as PortMatcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; use Psr\Log\LoggerInterface; class RouterSpecialPortsTest extends RouterBaseTest @@ -110,7 +110,7 @@ protected function createRouter(): Router } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessSetting[] + * @return \Ibexa\Tests\Core\MVC\Symfony\SiteAccess\SiteAccessSetting[] */ public function getSiteAccessProviderSettings(): array { @@ -124,3 +124,5 @@ public function getSiteAccessProviderSettings(): array ]; } } + +class_alias(RouterSpecialPortsTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\RouterSpecialPortsTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterTest.php b/tests/lib/MVC/Symfony/SiteAccess/RouterTest.php similarity index 95% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterTest.php rename to tests/lib/MVC/Symfony/SiteAccess/RouterTest.php index 411d512fce..3ee493507f 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterTest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/RouterTest.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Exception\InvalidSiteAccessException; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\VersatileMatcher; +use Ibexa\Core\MVC\Exception\InvalidSiteAccessException; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\MatcherBuilderInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; +use Ibexa\Core\MVC\Symfony\SiteAccess\VersatileMatcher; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\Request; @@ -38,7 +38,7 @@ public function testMatch(SimplifiedRequest $request, $siteAccess) $sa = $router->match($request); $this->assertInstanceOf(SiteAccess::class, $sa); $this->assertSame($siteAccess, $sa->name); - // SiteAccess must be serializable as a whole. See https://jira.ez.no/browse/EZP-21613 + // SiteAccess must be serializable as a whole. See https://issues.ibexa.co/browse/EZP-21613 $this->assertIsString(serialize($sa)); $router->setSiteAccess(); } @@ -306,7 +306,7 @@ protected function createRouter($debug = false): Router } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessSetting[] + * @return \Ibexa\Tests\Core\MVC\Symfony\SiteAccess\SiteAccessSetting[] */ public function getSiteAccessProviderSettings(): array { @@ -324,3 +324,5 @@ public function getSiteAccessProviderSettings(): array ]; } } + +class_alias(RouterTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\RouterTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterURIElement2Test.php b/tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php similarity index 94% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterURIElement2Test.php rename to tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php index 66550bf578..18110c4be1 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterURIElement2Test.php +++ b/tests/lib/MVC/Symfony/SiteAccess/RouterURIElement2Test.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIElement; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIElement as URIElementMatcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\URIElement; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\URIElement as URIElementMatcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; use Psr\Log\LoggerInterface; class RouterURIElement2Test extends RouterBaseTest @@ -119,7 +119,7 @@ public function analyseProvider() return [ [2, '/my/siteaccess/foo/bar', '/foo/bar'], [2, '/vive/le/sucre/en-poudre', '/sucre/en-poudre'], - // Issue https://jira.ez.no/browse/EZP-20125 + // Issue https://issues.ibexa.co/browse/EZP-20125 [1, '/fre/content/edit/104/1/fre-FR', '/content/edit/104/1/fre-FR'], [1, '/fre/utf8-with-accent/é/fre/à/à/fre/é', '/utf8-with-accent/é/fre/à/à/fre/é'], [2, '/é/fre/utf8-with-accent/é/fre/à/à/fre/é', '/utf8-with-accent/é/fre/à/à/fre/é'], @@ -200,7 +200,7 @@ protected function createRouter(): Router } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessSetting[] + * @return \Ibexa\Tests\Core\MVC\Symfony\SiteAccess\SiteAccessSetting[] */ public function getSiteAccessProviderSettings(): array { @@ -218,3 +218,5 @@ public function getSiteAccessProviderSettings(): array ]; } } + +class_alias(RouterURIElement2Test::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\RouterURIElement2Test'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterURIElementTest.php b/tests/lib/MVC/Symfony/SiteAccess/RouterURIElementTest.php similarity index 93% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterURIElementTest.php rename to tests/lib/MVC/Symfony/SiteAccess/RouterURIElementTest.php index 8a3dfcb42a..5e80dcadec 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterURIElementTest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/RouterURIElementTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIElement; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIElement as URIElementMatcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\URIElement; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\URIElement as URIElementMatcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; use Psr\Log\LoggerInterface; class RouterURIElementTest extends RouterBaseTest @@ -172,7 +172,7 @@ protected function createRouter(): Router } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessSetting[] + * @return \Ibexa\Tests\Core\MVC\Symfony\SiteAccess\SiteAccessSetting[] */ public function getSiteAccessProviderSettings(): array { @@ -188,3 +188,5 @@ public function getSiteAccessProviderSettings(): array ]; } } + +class_alias(RouterURIElementTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\RouterURIElementTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterURIRegexTest.php b/tests/lib/MVC/Symfony/SiteAccess/RouterURIRegexTest.php similarity index 93% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterURIRegexTest.php rename to tests/lib/MVC/Symfony/SiteAccess/RouterURIRegexTest.php index 94f05dc6b1..e4f3f3ca8b 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterURIRegexTest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/RouterURIRegexTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\Regex\URI as RegexMatcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\Regex\URI as RegexMatcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; use Psr\Log\LoggerInterface; class RouterURIRegexTest extends RouterBaseTest @@ -120,7 +120,7 @@ protected function createRouter(): Router } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessSetting[] + * @return \Ibexa\Tests\Core\MVC\Symfony\SiteAccess\SiteAccessSetting[] */ public function getSiteAccessProviderSettings(): array { @@ -135,3 +135,5 @@ public function getSiteAccessProviderSettings(): array ]; } } + +class_alias(RouterURIRegexTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\RouterURIRegexTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterURITextTest.php b/tests/lib/MVC/Symfony/SiteAccess/RouterURITextTest.php similarity index 94% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterURITextTest.php rename to tests/lib/MVC/Symfony/SiteAccess/RouterURITextTest.php index b187829d76..aeac59795f 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/RouterURITextTest.php +++ b/tests/lib/MVC/Symfony/SiteAccess/RouterURITextTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Routing\SimplifiedRequest; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIText; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Matcher\URIText as URITextMatcher; -use eZ\Publish\Core\MVC\Symfony\SiteAccess\Router; +use Ibexa\Core\MVC\Symfony\Routing\SimplifiedRequest; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\URIText; +use Ibexa\Core\MVC\Symfony\SiteAccess\Matcher\URIText as URITextMatcher; +use Ibexa\Core\MVC\Symfony\SiteAccess\Router; use Psr\Log\LoggerInterface; class RouterURITextTest extends RouterBaseTest @@ -151,7 +151,7 @@ protected function createRouter(): Router } /** - * @return \eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessSetting[] + * @return \Ibexa\Tests\Core\MVC\Symfony\SiteAccess\SiteAccessSetting[] */ public function getSiteAccessProviderSettings(): array { @@ -166,3 +166,5 @@ public function getSiteAccessProviderSettings(): array ]; } } + +class_alias(RouterURITextTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\RouterURITextTest'); diff --git a/tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php b/tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php new file mode 100644 index 0000000000..6ba29301eb --- /dev/null +++ b/tests/lib/MVC/Symfony/SiteAccess/SiteAccessServiceTest.php @@ -0,0 +1,165 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; + +use ArrayIterator; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess\Provider\StaticSiteAccessProvider; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface; +use Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessService; +use PHPUnit\Framework\TestCase; + +class SiteAccessServiceTest extends TestCase +{ + private const EXISTING_SA_NAME = 'existing_sa'; + private const UNDEFINED_SA_NAME = 'undefined_sa'; + private const SA_GROUP = 'group'; + + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess\SiteAccessProviderInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $provider; + + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $configResolver; + + /** @var \Ibexa\Core\MVC\Symfony\SiteAccess */ + private $siteAccess; + + /** @var \ArrayIterator */ + private $availableSiteAccesses; + + /** @var array */ + private $configResolverParameters; + + protected function setUp(): void + { + parent::setUp(); + $this->provider = $this->createMock(SiteAccessProviderInterface::class); + $this->configResolver = $this->createMock(ConfigResolverInterface::class); + $this->siteAccess = new SiteAccess('current'); + $this->availableSiteAccesses = $this->getAvailableSitAccesses(['current', 'first_sa', 'second_sa', 'default']); + $this->configResolverParameters = $this->getConfigResolverParameters(); + } + + public function testGetCurrentSiteAccess(): void + { + $service = new SiteAccessService( + $this->createMock(SiteAccessProviderInterface::class), + $this->createMock(ConfigResolverInterface::class) + ); + + self::assertNull($service->getCurrent()); + + $siteAccess = new SiteAccess('default'); + $service->setSiteAccess($siteAccess); + self::assertSame($siteAccess, $service->getCurrent()); + + $service->setSiteAccess(null); + self::assertNull($service->getCurrent()); + } + + public function testGetSiteAccess(): void + { + $staticSiteAccessProvider = new StaticSiteAccessProvider( + [self::EXISTING_SA_NAME], + [self::EXISTING_SA_NAME => [self::SA_GROUP]], + ); + $service = new SiteAccessService( + $staticSiteAccessProvider, + $this->createMock(ConfigResolverInterface::class) + ); + + self::assertEquals( + self::EXISTING_SA_NAME, + $service->get(self::EXISTING_SA_NAME)->name + ); + } + + public function testGetSiteAccessThrowsNotFoundException(): void + { + $staticSiteAccessProvider = new StaticSiteAccessProvider( + [self::EXISTING_SA_NAME], + [self::EXISTING_SA_NAME => [self::SA_GROUP]], + ); + $service = new SiteAccessService( + $staticSiteAccessProvider, + $this->createMock(ConfigResolverInterface::class) + ); + + $this->expectException(NotFoundException::class); + $service->get(self::UNDEFINED_SA_NAME); + } + + public function testGetCurrentSiteAccessesRelation(): void + { + $this->configResolver + ->method('getParameter') + ->willReturnMap($this->configResolverParameters); + + $this->provider + ->method('getSiteAccesses') + ->willReturn($this->availableSiteAccesses); + + $this->assertSame(['current', 'first_sa'], $this->getSiteAccessService()->getSiteAccessesRelation()); + } + + public function testGetFirstSiteAccessesRelation(): void + { + $this->configResolver + ->method('getParameter') + ->willReturnMap($this->configResolverParameters); + + $this->provider + ->method('getSiteAccesses') + ->willReturn($this->availableSiteAccesses); + + $this->assertSame( + ['current', 'first_sa'], + $this->getSiteAccessService()->getSiteAccessesRelation(new SiteAccess('first_sa')) + ); + } + + private function getSiteAccessService(): SiteAccessService + { + $siteAccessService = new SiteAccessService($this->provider, $this->configResolver); + $siteAccessService->setSiteAccess($this->siteAccess); + + return $siteAccessService; + } + + /** + * @param string[] $siteAccessNames + */ + private function getAvailableSitAccesses(array $siteAccessNames): ArrayIterator + { + $availableSitAccesses = []; + foreach ($siteAccessNames as $siteAccessName) { + $availableSitAccesses[] = new SiteAccess($siteAccessName); + } + + return new ArrayIterator($availableSitAccesses); + } + + private function getConfigResolverParameters(): array + { + return [ + ['repository', 'ibexa.site_access.config', 'current', 'repository_1'], + ['content.tree_root.location_id', 'ibexa.site_access.config', 'current', 1], + ['repository', 'ibexa.site_access.config', 'first_sa', 'repository_1'], + ['content.tree_root.location_id', 'ibexa.site_access.config', 'first_sa', 1], + ['repository', 'ibexa.site_access.config', 'second_sa', 'repository_1'], + ['content.tree_root.location_id', 'ibexa.site_access.config', 'second_sa', 2], + ['repository', 'ibexa.site_access.config', 'default', ''], + ['content.tree_root.location_id', 'ibexa.site_access.config', 'default', 3], + ]; + } +} + +class_alias(SiteAccessServiceTest::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessServiceTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/SiteAccessSetting.php b/tests/lib/MVC/Symfony/SiteAccess/SiteAccessSetting.php similarity index 79% rename from eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/SiteAccessSetting.php rename to tests/lib/MVC/Symfony/SiteAccess/SiteAccessSetting.php index b33d7d5692..7a77d2e35e 100644 --- a/eZ/Publish/Core/MVC/Symfony/SiteAccess/Tests/SiteAccessSetting.php +++ b/tests/lib/MVC/Symfony/SiteAccess/SiteAccessSetting.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\SiteAccess; /** * This class represents settings which will be used to construct SiteAccessProvider mock. @@ -34,3 +34,5 @@ public function __construct( $this->matchingType = $matchingType; } } + +class_alias(SiteAccessSetting::class, 'eZ\Publish\Core\MVC\Symfony\SiteAccess\Tests\SiteAccessSetting'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/BaseRenderStrategyTest.php b/tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php similarity index 79% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/BaseRenderStrategyTest.php rename to tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php index 29e8a2d16f..5e847d6eed 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/BaseRenderStrategyTest.php +++ b/tests/lib/MVC/Symfony/Templating/BaseRenderStrategyTest.php @@ -6,17 +6,17 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Templating; -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Search\Tests\TestCase; -use eZ\Publish\SPI\MVC\Templating\RenderStrategy; +use Ibexa\Contracts\Core\MVC\Templating\RenderStrategy; +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location as APILocation; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Tests\Core\Search\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; @@ -98,3 +98,5 @@ public function createContent(int $id): APIContent ]); } } + +class_alias(BaseRenderStrategyTest::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\BaseRenderStrategyTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/GlobalHelperTest.php b/tests/lib/MVC/Symfony/Templating/GlobalHelperTest.php similarity index 88% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/GlobalHelperTest.php rename to tests/lib/MVC/Symfony/Templating/GlobalHelperTest.php index 670a0aee44..1f989ed964 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/GlobalHelperTest.php +++ b/tests/lib/MVC/Symfony/Templating/GlobalHelperTest.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests; - -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\Core\Helper\TranslationHelper; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Routing\UrlAliasRouter; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Templating\GlobalHelper; +namespace Ibexa\Tests\Core\MVC\Symfony\Templating; + +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\Helper\TranslationHelper; +use Ibexa\Core\MVC\Symfony\Routing\UrlAliasRouter; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\Templating\GlobalHelper; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; @@ -21,7 +21,7 @@ class GlobalHelperTest extends TestCase { - /** @var \eZ\Publish\Core\MVC\Symfony\Templating\GlobalHelper */ + /** @var \Ibexa\Core\MVC\Symfony\Templating\GlobalHelper */ protected $helper; /** @var \PHPUnit\Framework\MockObject\MockObject */ @@ -93,7 +93,7 @@ public function testGetViewParametersString() public function testGetRequestedUriString() { - $request = Request::create('/ezdemo_site/foo/bar'); + $request = Request::create('/ibexa_demo_site/foo/bar'); $semanticPathinfo = '/foo/bar'; $request->attributes->set('semanticPathinfo', $semanticPathinfo); $requestStack = new RequestStack(); @@ -105,7 +105,7 @@ public function testGetRequestedUriString() public function testGetSystemUriStringNoUrlAlias() { - $request = Request::create('/ezdemo_site/foo/bar'); + $request = Request::create('/ibexa_demo_site/foo/bar'); $semanticPathinfo = '/foo/bar'; $request->attributes->set('semanticPathinfo', $semanticPathinfo); $request->attributes->set('_route', 'someRouteName'); @@ -121,7 +121,7 @@ public function testGetSystemUriString() $contentId = 456; $viewType = 'full'; $expectedSystemUriString = '/view/content/456/full/1/123'; - $request = Request::create('/ezdemo_site/foo/bar'); + $request = Request::create('/ibexa_demo_site/foo/bar'); $request->attributes->set('_route', UrlAliasRouter::URL_ALIAS_ROUTE_NAME); $request->attributes->set('contentId', $contentId); $request->attributes->set('locationId', $locationId); @@ -132,7 +132,7 @@ public function testGetSystemUriString() $this->router ->expects($this->once()) ->method('generate') - ->with('_ez_content_view', [ + ->with('ibexa.content.view', [ 'contentId' => $contentId, 'locationId' => $locationId, 'viewType' => $viewType, @@ -196,3 +196,5 @@ public function testGetAvailableLanguages() $this->assertSame($languages, $this->helper->getAvailableLanguages()); } } + +class_alias(GlobalHelperTest::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\GlobalHelperTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/RenderContentStrategyTest.php b/tests/lib/MVC/Symfony/Templating/RenderContentStrategyTest.php similarity index 88% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/RenderContentStrategyTest.php rename to tests/lib/MVC/Symfony/Templating/RenderContentStrategyTest.php index 12d9ad4599..0d3cc48c2b 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/RenderContentStrategyTest.php +++ b/tests/lib/MVC/Symfony/Templating/RenderContentStrategyTest.php @@ -6,14 +6,14 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests; - -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Templating\RenderContentStrategy; -use eZ\Publish\Core\MVC\Symfony\Templating\RenderOptions; +namespace Ibexa\Tests\Core\MVC\Symfony\Templating; + +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\Templating\RenderContentStrategy; +use Ibexa\Core\MVC\Symfony\Templating\RenderOptions; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Controller\ControllerReference; @@ -108,7 +108,7 @@ public function testExpectedMethodRenderArgumentsFormat(): void $controllerReferenceCallback = $this->callback(function (ControllerReference $controllerReference) { $this->assertInstanceOf(ControllerReference::class, $controllerReference); - $this->assertEquals('ez_content::viewAction', $controllerReference->controller); + $this->assertEquals('ibexa_content::viewAction', $controllerReference->controller); $this->assertSame([ 'contentId' => 123, 'viewType' => 'awesome', @@ -150,3 +150,5 @@ public function testExpectedMethodRenderArgumentsFormat(): void )); } } + +class_alias(RenderContentStrategyTest::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\RenderContentStrategyTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/RenderLocationStrategyTest.php b/tests/lib/MVC/Symfony/Templating/RenderLocationStrategyTest.php similarity index 88% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/RenderLocationStrategyTest.php rename to tests/lib/MVC/Symfony/Templating/RenderLocationStrategyTest.php index 58a30839bd..94e28a4f64 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/RenderLocationStrategyTest.php +++ b/tests/lib/MVC/Symfony/Templating/RenderLocationStrategyTest.php @@ -6,14 +6,14 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests; - -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\MVC\Symfony\SiteAccess; -use eZ\Publish\Core\MVC\Symfony\Templating\RenderLocationStrategy; -use eZ\Publish\Core\MVC\Symfony\Templating\RenderOptions; +namespace Ibexa\Tests\Core\MVC\Symfony\Templating; + +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\MVC\Symfony\SiteAccess; +use Ibexa\Core\MVC\Symfony\Templating\RenderLocationStrategy; +use Ibexa\Core\MVC\Symfony\Templating\RenderOptions; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Controller\ControllerReference; @@ -109,7 +109,7 @@ public function testExpectedMethodRenderRequestFormat(): void $controllerReferenceCallback = $this->callback(function (ControllerReference $controllerReference) { $this->assertInstanceOf(ControllerReference::class, $controllerReference); - $this->assertEquals('ez_content::viewAction', $controllerReference->controller); + $this->assertEquals('ibexa_content::viewAction', $controllerReference->controller); $this->assertSame([ 'contentId' => 234, 'locationId' => 345, @@ -152,3 +152,5 @@ public function testExpectedMethodRenderRequestFormat(): void )); } } + +class_alias(RenderLocationStrategyTest::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\RenderLocationStrategyTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/RenderOptionsTest.php b/tests/lib/MVC/Symfony/Templating/RenderOptionsTest.php similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/RenderOptionsTest.php rename to tests/lib/MVC/Symfony/Templating/RenderOptionsTest.php index 57b07499e4..089538c0a4 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/RenderOptionsTest.php +++ b/tests/lib/MVC/Symfony/Templating/RenderOptionsTest.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Templating; -use eZ\Publish\Core\MVC\Symfony\Templating\RenderOptions; +use Ibexa\Core\MVC\Symfony\Templating\RenderOptions; use PHPUnit\Framework\TestCase; class RenderOptionsTest extends TestCase @@ -80,3 +80,5 @@ public function testUnsettingOptions(): void $this->assertTrue($renderOptions->has('c')); } } + +class_alias(RenderOptionsTest::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\RenderOptionsTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/RenderStrategyTest.php b/tests/lib/MVC/Symfony/Templating/RenderStrategyTest.php similarity index 88% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/RenderStrategyTest.php rename to tests/lib/MVC/Symfony/Templating/RenderStrategyTest.php index 13455276b9..47533b5174 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/RenderStrategyTest.php +++ b/tests/lib/MVC/Symfony/Templating/RenderStrategyTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\Templating; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\MVC\Symfony\Templating\RenderOptions; -use eZ\Publish\Core\MVC\Symfony\Templating\RenderStrategy; -use eZ\Publish\SPI\MVC\Templating\RenderStrategy as SPIRenderStrategy; +use Ibexa\Contracts\Core\MVC\Templating\RenderStrategy as SPIRenderStrategy; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\MVC\Symfony\Templating\RenderOptions; +use Ibexa\Core\MVC\Symfony\Templating\RenderStrategy; use PHPUnit\Framework\TestCase; class RenderStrategyTest extends TestCase @@ -115,3 +115,5 @@ public function testMultipleStrategies(): void $this->assertSame('other_rendered_content', $renderStrategy->render($valueObject, new RenderOptions())); } } + +class_alias(RenderStrategyTest::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\RenderStrategyTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/ContentExtensionTest.php b/tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php similarity index 76% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/ContentExtensionTest.php rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php index fb60a0e1a8..7696b0ab8d 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/ContentExtensionTest.php +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/ContentExtensionTest.php @@ -4,22 +4,23 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\Helper\FieldHelper; -use eZ\Publish\Core\Helper\TranslationHelper; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\ContentExtension; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection; +namespace Ibexa\Tests\Core\MVC\Symfony\Templating\Twig\Extension; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\Helper\FieldHelper; +use Ibexa\Core\Helper\FieldsGroups\FieldsGroupsList; +use Ibexa\Core\Helper\TranslationHelper; +use Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\ContentExtension; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinitionCollection; use Psr\Log\LoggerInterface; /** @@ -29,10 +30,10 @@ */ class ContentExtensionTest extends FileSystemTwigIntegrationTestCase { - /** @var \eZ\Publish\API\Repository\ContentTypeService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\ContentTypeService|\PHPUnit\Framework\MockObject\MockObject */ private $fieldHelperMock; - /** @var \eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition[] */ + /** @var array<int, \Ibexa\Core\Repository\Values\ContentType\FieldDefinition[]> */ private $fieldDefinitions = []; /** @var int[] */ @@ -52,7 +53,8 @@ public function getExtensions() [], $this->createMock(LoggerInterface::class) ), - $this->fieldHelperMock + $this->fieldHelperMock, + $this->getFieldsGroupsListMock() ), ]; } @@ -69,7 +71,7 @@ public function getFixturesDir() * @param array $fieldsData * @param array $namesData * - * @return \eZ\Publish\Core\Repository\Values\Content\Content + * @return \Ibexa\Core\Repository\Values\Content\Content */ protected function getContent(string $contentTypeIdentifier, array $fieldsData, array $namesData = []) { @@ -115,6 +117,9 @@ protected function getContent(string $contentTypeIdentifier, array $fieldsData, ), ] ), + 'contentType' => new ContentType([ + 'fieldDefinitions' => new FieldDefinitionCollection($this->fieldDefinitions[$contentTypeId] ?? []), + ]), ] ); @@ -143,6 +148,16 @@ private function getConfigResolverMock() return $mock; } + private function getFieldsGroupsListMock(): FieldsGroupsList + { + $fieldsGroupsList = $this->createMock(FieldsGroupsList::class); + $fieldsGroupsList->method('getGroups')->willReturn([ + 'content' => 'Content', + ]); + + return $fieldsGroupsList; + } + protected function getField($isEmpty) { $field = new Field(['fieldDefIdentifier' => 'testfield', 'value' => null]); @@ -197,3 +212,5 @@ function ($contentTypeId) { return $mock; } } + +class_alias(ContentExtensionTest::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension\ContentExtensionTest'); diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/DataAttributesExtensionTest.php b/tests/lib/MVC/Symfony/Templating/Twig/Extension/DataAttributesExtensionTest.php new file mode 100644 index 0000000000..6311fdaf57 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/DataAttributesExtensionTest.php @@ -0,0 +1,29 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Templating\Twig\Extension; + +use Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\DataAttributesExtension; +use Twig\Test\IntegrationTestCase; + +class DataAttributesExtensionTest extends IntegrationTestCase +{ + public function getExtensions(): array + { + return [ + new DataAttributesExtension(), + ]; + } + + protected function getFixturesDir(): string + { + return __DIR__ . '/_fixtures/filters'; + } +} + +class_alias(DataAttributesExtensionTest::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension\DataAttributesExtensionTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/FieldRenderingExtensionIntegrationTest.php b/tests/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtensionIntegrationTest.php similarity index 83% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/FieldRenderingExtensionIntegrationTest.php rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtensionIntegrationTest.php index 49c502d9c0..96646006f7 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/FieldRenderingExtensionIntegrationTest.php +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/FieldRenderingExtensionIntegrationTest.php @@ -4,22 +4,22 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\Helper\TranslationHelper; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistryInterface; -use eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\FieldRenderingExtension; -use eZ\Publish\Core\MVC\Symfony\Templating\Twig\FieldBlockRenderer; -use eZ\Publish\Core\MVC\Symfony\Templating\Twig\ResourceProviderInterface; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection; +namespace Ibexa\Tests\Core\MVC\Symfony\Templating\Twig\Extension; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\Helper\TranslationHelper; +use Ibexa\Core\MVC\Symfony\FieldType\View\ParameterProviderRegistryInterface; +use Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\FieldRenderingExtension; +use Ibexa\Core\MVC\Symfony\Templating\Twig\FieldBlockRenderer; +use Ibexa\Core\MVC\Symfony\Templating\Twig\ResourceProviderInterface; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinitionCollection; use Psr\Log\LoggerInterface; use Twig\Environment; @@ -76,7 +76,7 @@ public function getFieldDefinition($typeIdentifier, $id = null, $settings = []) * @param array $fieldsData * @param array $namesData * - * @return \eZ\Publish\Core\Repository\Values\Content\Content + * @return \Ibexa\Core\Repository\Values\Content\Content */ protected function getContent($contentTypeIdentifier, array $fieldsData, array $namesData = []) { @@ -159,7 +159,7 @@ private function getConfigResolverMock() } /** - * @return \eZ\Publish\Core\MVC\Symfony\Templating\Twig\ResourceProviderInterface|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\MVC\Symfony\Templating\Twig\ResourceProviderInterface|\PHPUnit\Framework\MockObject\MockObject */ private function getResourceProviderMock(): ResourceProviderInterface { @@ -204,3 +204,5 @@ private function getResourceProviderMock(): ResourceProviderInterface return $mock; } } + +class_alias(FieldRenderingExtensionIntegrationTest::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension\FieldRenderingExtensionIntegrationTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/FileSizeExtensionTest.php b/tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php similarity index 81% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/FileSizeExtensionTest.php rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php index 8344be05b1..b526e49d6f 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/FileSizeExtensionTest.php +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSizeExtensionTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension; +namespace Ibexa\Tests\Core\MVC\Symfony\Templating\Twig\Extension; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface; -use eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\FileSizeExtension; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Locale\LocaleConverterInterface; +use Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\FileSizeExtension; use Symfony\Contracts\Translation\TranslatorInterface; use Twig\Test\IntegrationTestCase; @@ -33,12 +33,12 @@ class FileSizeExtensionTest extends IntegrationTestCase protected $translatorMock; /** - * @param \eZ\Publish\Core\MVC\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject + * @param \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $configResolverInterfaceMock; /** - * @param \eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface|\PHPUnit\Framework\MockObject\MockObject + * @param \Ibexa\Core\MVC\Symfony\Locale\LocaleConverterInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $localeConverterInterfaceMock; @@ -79,7 +79,7 @@ protected function getFixturesDir() } /** - * @return \eZ\Publish\Core\MVC\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ protected function getConfigResolverInterfaceMock() { @@ -93,7 +93,7 @@ protected function getConfigResolverInterfaceMock() } /** - * @return \eZ\Publish\Core\MVC\Symfony\Locale\LocaleConverterInterface|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\MVC\Symfony\Locale\LocaleConverterInterface|\PHPUnit\Framework\MockObject\MockObject */ protected function getLocaleConverterInterfaceMock() { @@ -139,3 +139,5 @@ static function ($suffixes) use ($that) { return $this->translatorMock; } } + +class_alias(FileSizeExtensionTest::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension\FileSizeExtensionTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/FileSystemTwigIntegrationTestCase.php b/tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php similarity index 95% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/FileSystemTwigIntegrationTestCase.php rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php index 8f2f4b8603..cca6652886 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/FileSystemTwigIntegrationTestCase.php +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/FileSystemTwigIntegrationTestCase.php @@ -4,7 +4,7 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension; +namespace Ibexa\Tests\Core\MVC\Symfony\Templating\Twig\Extension; use Exception; use PHPUnit\Framework\Constraint\Exception as PHPUnitException; @@ -132,3 +132,5 @@ protected function doIntegrationTest($file, $message, $condition, $templates, $e } } } + +class_alias(FileSystemTwigIntegrationTestCase::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension\FileSystemTwigIntegrationTestCase'); diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/QueryRenderingExtensionTest.php b/tests/lib/MVC/Symfony/Templating/Twig/Extension/QueryRenderingExtensionTest.php similarity index 75% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/QueryRenderingExtensionTest.php rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/QueryRenderingExtensionTest.php index c3db8bc5fc..4d09fef55b 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/QueryRenderingExtensionTest.php +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/QueryRenderingExtensionTest.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension; +namespace Ibexa\Tests\Core\MVC\Symfony\Templating\Twig\Extension; -use eZ\Publish\Core\MVC\Symfony\Templating\Twig\Extension\QueryRenderingExtension; +use Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\QueryRenderingExtension; use Symfony\Component\HttpKernel\Fragment\FragmentHandler; final class QueryRenderingExtensionTest extends FileSystemTwigIntegrationTestCase @@ -32,3 +32,5 @@ protected function getFixturesDir(): string return __DIR__ . '/_fixtures/query_rendering_functions/'; } } + +class_alias(QueryRenderingExtensionTest::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension\QueryRenderingExtensionTest'); diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtensionTest.php b/tests/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtensionTest.php new file mode 100644 index 0000000000..a5b6324f71 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/RoutingExtensionTest.php @@ -0,0 +1,119 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Templating\Twig\Extension; + +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentAwareInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location as APILocation; +use Ibexa\Core\MVC\Symfony\Routing\Generator\RouteReferenceGenerator; +use Ibexa\Core\MVC\Symfony\Routing\Generator\RouteReferenceGeneratorInterface; +use Ibexa\Core\MVC\Symfony\Routing\RouteReference; +use Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\RoutingExtension; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use stdClass; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Twig\Test\IntegrationTestCase; + +final class RoutingExtensionTest extends IntegrationTestCase +{ + protected function getExtensions(): array + { + return [ + new RoutingExtension( + $this->getRouteReferenceGenerator(), + $this->getUrlGenerator() + ), + ]; + } + + protected function getFixturesDir(): string + { + return __DIR__ . '/_fixtures/routing_functions'; + } + + protected function getExampleContent(int $id): APIContent + { + return new Content([ + 'versionInfo' => new VersionInfo([ + 'contentInfo' => $this->getExampleContentInfo($id), + ]), + ]); + } + + protected function getExampleContentAware(int $id): ContentAwareInterface + { + $contentAware = $this->createMock(ContentAwareInterface::class); + $contentAware->method('getContent')->willReturn($this->getExampleContent($id)); + + return $contentAware; + } + + protected function getExampleContentInfo(int $id): ContentInfo + { + return new ContentInfo([ + 'id' => $id, + ]); + } + + protected function getExampleLocation(int $id): APILocation + { + return new Location(['id' => $id]); + } + + protected function getExampleRouteReference($name, array $parameters = []): RouteReference + { + return new RouteReference($name, $parameters); + } + + protected function getExampleUnsupportedObject(): object + { + $object = new stdClass(); + $object->foo = 'foo'; + $object->bar = 'bar'; + + return $object; + } + + private function getRouteReferenceGenerator(): RouteReferenceGeneratorInterface + { + $generator = new RouteReferenceGenerator( + $this->createMock(EventDispatcherInterface::class) + ); + $request = new Request(); + $requestStack = new RequestStack(); + $requestStack->push($request); + $generator->setRequestStack($requestStack); + + return $generator; + } + + private function getUrlGenerator(): UrlGeneratorInterface + { + $generator = $this->createMock(UrlGeneratorInterface::class); + $generator + ->method('generate') + ->willReturnCallback(static function ($name, $parameters, $referenceType): string { + return json_encode([ + '$name' => $name, + '$parameters' => $parameters, + '$referenceType' => $referenceType, + ]); + }); + + return $generator; + } +} + +class_alias(RoutingExtensionTest::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\Extension\RoutingExtensionTest'); diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/UserExtensionTest.php b/tests/lib/MVC/Symfony/Templating/Twig/Extension/UserExtensionTest.php new file mode 100644 index 0000000000..4c7aeef798 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/UserExtensionTest.php @@ -0,0 +1,83 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Templating\Twig\Extension; + +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserReference; +use Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\UserExtension; +use Twig\Test\IntegrationTestCase; + +final class UserExtensionTest extends IntegrationTestCase +{ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver&\PHPUnit\Framework\MockObject\MockObject */ + private PermissionResolver $permissionResolver; + + /** @var \Ibexa\Contracts\Core\Repository\UserService&\PHPUnit\Framework\MockObject\MockObject */ + private UserService $userService; + + /** @var array<int, \Ibexa\Contracts\Core\Repository\Values\User\User> */ + private array $users = []; + + private int $currentUserId; + + protected function setUp(): void + { + parent::setUp(); + + $this->userService = $this->createMock(UserService::class); + $this->userService + ->method('loadUser') + ->willReturnCallback(fn (int $id): User => $this->users[$id]); + + $this->permissionResolver = $this->createMock(PermissionResolver::class); + $this->permissionResolver + ->method('getCurrentUserReference') + ->willReturnCallback(function (): UserReference { + $reference = $this->createMock(UserReference::class); + $reference->method('getUserId')->willReturn($this->currentUserId); + + return $reference; + }); + + $this->getUser(10, true); + } + + protected function getExtensions(): array + { + return [ + new UserExtension( + $this->userService, + $this->permissionResolver + ), + ]; + } + + public function getUser(int $id, bool $isCurrent = false): User + { + if (!isset($this->users[$id])) { + $user = $this->createMock(User::class); + $user->method('getUserId')->willReturn($id); + + $this->users[$id] = $user; + + if ($isCurrent) { + $this->currentUserId = $id; + } + } + + return $this->users[$id]; + } + + protected function getFixturesDir(): string + { + return __DIR__ . '/_fixtures/user_functions'; + } +} diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/UserPreferenceExtensionTest.php b/tests/lib/MVC/Symfony/Templating/Twig/Extension/UserPreferenceExtensionTest.php new file mode 100644 index 0000000000..4430dd47df --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/UserPreferenceExtensionTest.php @@ -0,0 +1,82 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Templating\Twig\Extension; + +use Ibexa\Contracts\Core\Repository\UserPreferenceService; +use Ibexa\Contracts\Core\Repository\Values\UserPreference\UserPreference; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\UserPreferenceExtension; +use Ibexa\Core\MVC\Symfony\Templating\Twig\Extension\UserPreferenceRuntime; +use Twig\Extension\RuntimeExtensionInterface; +use Twig\RuntimeLoader\RuntimeLoaderInterface; +use Twig\Test\IntegrationTestCase; + +final class UserPreferenceExtensionTest extends IntegrationTestCase +{ + protected function getRuntimeLoaders(): array + { + $userPreferenceService = $this->createUserPreferenceService(); + + return [ + new class($userPreferenceService) implements RuntimeLoaderInterface { + private UserPreferenceService $userPreferenceService; + + public function __construct( + UserPreferenceService $userPreferenceService + ) { + $this->userPreferenceService = $userPreferenceService; + } + + public function load(string $class): ?RuntimeExtensionInterface + { + if ($class === UserPreferenceRuntime::class) { + return new UserPreferenceRuntime($this->userPreferenceService); + } + + return null; + } + }, + ]; + } + + protected function getFixturesDir(): string + { + return __DIR__ . '/_fixtures/user_preference_functions'; + } + + /** + * @return \Twig\Extension\ExtensionInterface[] + */ + protected function getExtensions(): array + { + return [ + new UserPreferenceExtension(), + ]; + } + + private function createUserPreferenceService(): UserPreferenceService + { + $callback = static function ($identifier): UserPreference { + if ($identifier === 'baz') { + throw new NotFoundException('User Preference', 14); + } + + return new UserPreference([ + 'value' => 'bar', + ]); + }; + + $userPreferenceService = $this->createMock(UserPreferenceService::class); + $userPreferenceService + ->method('getUserPreference') + ->willReturnCallback($callback); + + return $userPreferenceService; + } +} diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/content_functions/ez_content_name.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ez_content_name.test similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/content_functions/ez_content_name.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ez_content_name.test diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/content_functions/ez_field.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ez_field.test similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/content_functions/ez_field.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ez_field.test diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/content_functions/ez_field_description.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ez_field_description.test similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/content_functions/ez_field_description.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ez_field_description.test diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/content_functions/ez_field_name.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ez_field_name.test similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/content_functions/ez_field_name.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ez_field_name.test diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/content_functions/ez_field_value.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ez_field_value.test similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/content_functions/ez_field_value.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ez_field_value.test diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/content_functions/ez_is_field_empty.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ez_is_field_empty.test similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/content_functions/ez_is_field_empty.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ez_is_field_empty.test diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_content_name.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_content_name.test new file mode 100644 index 0000000000..40ebad8c52 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_content_name.test @@ -0,0 +1,35 @@ +--TEST-- +"ibexa_content_name" function +--TEMPLATE-- +{{ ibexa_content_name( content ) }} +{{ ibexa_content_name( content, "eng-GB" ) }} + +--DATA-- +return array( + 'content' => $this->getContent( + 'article', + array(), + array( + 'eng-US' => 'American', + 'fre-FR' => 'French' + ) + ) +) +--EXPECT-- +French +French + +--DATA-- +return array( + 'content' => $this->getContent( + 'article', + array(), + array( + 'eng-GB' => 'British', + 'eng-US' => 'American' + ) + ) +) +--EXPECT-- +American +British diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field.test new file mode 100644 index 0000000000..e11d68be38 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field.test @@ -0,0 +1,49 @@ +--TEST-- +"ibexa_field" function +--TEMPLATE-- +{{ ibexa_field( content, "testfield" ).value }} +{{ ibexa_field( content, "testfield", "eng-GB" ).value }} + +--DATA-- +return array( + 'content' => $this->getContent( + 'article', + array( + 'ezstring' => array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR' + ) + ) + ) +) +--EXPECT-- +foo2 +foo2 + +--DATA-- +return array( + 'content' => $this->getContent( + 'article', + array( + 'ezstring' => array( + array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'bar3', + 'languageCode' => 'eng-GB' + ), + array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR' + ), + ) + ) + ) +) +--EXPECT-- +foo2 +bar3 diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_description.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_description.test new file mode 100644 index 0000000000..67ffe1a861 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_description.test @@ -0,0 +1,49 @@ +--TEST-- +"ibexa_field_description" function +--TEMPLATE-- +{{ ibexa_field_description( content, 'testfield' ) }} +{{ ibexa_field_description( content, 'testfield', "eng-GB" ) }} + +--DATA-- +return array( + 'content' => $this->getContent( + 'article', + array( + 'ezstring' => array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR', + 'fieldDefDescriptions' => array( + 'eng-US' => 'American description', + 'fre-FR' => 'French description', + ) + ) + ) + ) +) +--EXPECT-- +French description +French description + +--DATA-- +return array( + 'content' => $this->getContent( + 'article', + array( + 'ezstring' => array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR', + 'fieldDefDescriptions' => array( + 'eng-GB' => 'British description', + 'eng-US' => 'American description', + ) + ) + ) + ) +) +--EXPECT-- +American description +British description diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_group_name.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_group_name.test new file mode 100644 index 0000000000..d90488afc7 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_group_name.test @@ -0,0 +1,8 @@ +--TEST-- +"ibexa_field_group_name" function +--TEMPLATE-- +{{ ibexa_field_group_name('content') }} +--DATA-- +return []; +--EXPECT-- +Content diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_name.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_name.test new file mode 100644 index 0000000000..3d2e2f4c98 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_name.test @@ -0,0 +1,49 @@ +--TEST-- +"ibexa_field_name" function +--TEMPLATE-- +{{ ibexa_field_name( content, 'testfield' ) }} +{{ ibexa_field_name( content, 'testfield', "eng-GB" ) }} + +--DATA-- +return array( + 'content' => $this->getContent( + 'article', + array( + 'ezstring' => array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR', + 'fieldDefNames' => array( + 'eng-US' => 'American name', + 'fre-FR' => 'French name', + ) + ) + ) + ) +) +--EXPECT-- +French name +French name + +--DATA-- +return array( + 'content' => $this->getContent( + 'article', + array( + 'ezstring' => array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR', + 'fieldDefNames' => array( + 'eng-GB' => 'British name', + 'eng-US' => 'American name', + ) + ) + ) + ) +) +--EXPECT-- +American name +British name diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_value.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_value.test new file mode 100644 index 0000000000..0041060de7 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_field_value.test @@ -0,0 +1,49 @@ +--TEST-- +"ibexa_field_value" function +--TEMPLATE-- +{{ ibexa_field_value( content, "testfield" ) }} +{{ ibexa_field_value( content, "testfield", "eng-GB" ) }} + +--DATA-- +return array( + 'content' => $this->getContent( + 'article', + array( + 'ezstring' => array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR' + ) + ) + ) +) +--EXPECT-- +foo2 +foo2 + +--DATA-- +return array( + 'content' => $this->getContent( + 'article', + array( + 'ezstring' => array( + array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'bar3', + 'languageCode' => 'eng-GB' + ), + array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR' + ), + ) + ) + ) +) +--EXPECT-- +foo2 +bar3 diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_has_field.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_has_field.test new file mode 100644 index 0000000000..bd9964c30a --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_has_field.test @@ -0,0 +1,23 @@ +--TEST-- +"ibexa_has_field" function +--TEMPLATE-- +{{ ibexa_has_field(content, 'existing') ? 'YES' : 'NO' }} +{{ ibexa_has_field(content, 'non-existing') ? 'YES' : 'NO' }} + +--DATA-- +return [ + 'content' => $this->getContent( + 'test_content', + [ + 'ezstring' => [ + 'id' => 125, + 'fieldDefIdentifier' => 'existing', + 'value' => 'value', + 'languageCode' => 'eng-GB', + ], + ] + ), +]; +--EXPECT-- +YES +NO diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_is_field_empty.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_is_field_empty.test new file mode 100644 index 0000000000..2a8ee2bc65 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/content_functions/ibexa_is_field_empty.test @@ -0,0 +1,80 @@ +--TEST-- +"ibexa_field_is_empty" function +--TEMPLATE-- +{% if ibexa_field_is_empty( content, field ) %} + empty +{% else %} + not empty +{% endif %} + +--DATA-- +return array( + 'content' => $this->getContent( + 'data', + array( + 'ezdata' => array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR' + ) + ) + ), + 'field' => $this->getField( false )->fieldDefIdentifier +) +--EXPECT-- +not empty + +--DATA-- +return array( + 'content' => $this->getContent( + 'data', + array( + 'ezdata' => array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR' + ) + ) + ), + 'field' => $this->getField( true )->fieldDefIdentifier +) +--EXPECT-- +empty + +--DATA-- +return array( + 'content' => $this->getContent( + 'data', + array( + 'ezdata' => array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR' + ) + ) + ), + 'field' => $this->getField( false ) +) +--EXPECT-- +not empty + +--DATA-- +return array( + 'content' => $this->getContent( + 'data', + array( + 'ezdata' => array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR' + ) + ) + ), + 'field' => $this->getField( true ) +) +--EXPECT-- +empty diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/ez_render_field.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ez_render_field.test similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/ez_render_field.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ez_render_field.test diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/ez_render_field_exception.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ez_render_field_exception.test similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/ez_render_field_exception.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ez_render_field_exception.test diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/ez_render_fielddefinition_settings.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ez_render_fielddefinition_settings.test similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/ez_render_fielddefinition_settings.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ez_render_fielddefinition_settings.test diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ibexa_render_field.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ibexa_render_field.test new file mode 100644 index 0000000000..24642e0b9f --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ibexa_render_field.test @@ -0,0 +1,73 @@ +--TEST-- +"ibexa_render_field" function +--TEMPLATE-- +{% extends 'templates/base.html.twig' %} +{% block content %} +{{ ibexa_render_field( nooverride, 'testfield' ) }} +{{ ibexa_render_field( overrides, 'testfield' ) }} +{{ ibexa_render_field( notdefault, 'testfield' ) }} +{{ ibexa_render_field( data, 'testfield' ) }} +{{ ibexa_render_field( data, 'testfield', {'attr': {'class': 'added'}} ) }} +{{ ibexa_render_field( data, 'testfield', {'parameters': { 'key1' : 1, 'key2': 2, 'key3': 3 }} ) }} +{{ ibexa_render_field( data, 'testfield', {'parameters': [ 3, 2, 1]} ) }} +{{ ibexa_render_field( data, 'testfield', {'parameters': [ 3, 2, 1], 'template': "templates/fields_localoverride.html.twig"} ) }} +{{ ibexa_render_field( data, 'testfield', {'parameters': [ 3, 2, 1], 'template': _self} ) }}{% endblock %} +{% block ezdata_field %}SELF OVERRIDE field id: 5 contentInfo id: 42 versionInfo versionNo: 64 attr class: ezdata-field parameters:3, 2, 1{% endblock %} +--DATA-- +return array( + 'nooverride' => $this->getContent( + 'nooverride', + array( + 'eznooverride' => array( + 'id' => 2, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR' + ), + ) + ), + 'overrides' => $this->getContent( + 'overrides', + array( + 'ezoverride' => array( + 'id' => 3, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR' + ), + ) + ), + 'notdefault' => $this->getContent( + 'notdefault', + array( + 'eznotdefault' => array( + 'id' => 4, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR' + ), + ) + ), + 'data' => $this->getContent( + 'data', + array( + 'ezdata' => array( + 'id' => 5, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR' + ), + ) + ) + +) +--EXPECT-- +default (no override) +override2 +not default +field id: 5 contentInfo id: 42 versionInfo versionNo: 64 attr class: ezdata-field parameters:empty +field id: 5 contentInfo id: 42 versionInfo versionNo: 64 attr class: added ezdata-field parameters:empty +field id: 5 contentInfo id: 42 versionInfo versionNo: 64 attr class: ezdata-field parameters:1, 2, 3 +field id: 5 contentInfo id: 42 versionInfo versionNo: 64 attr class: ezdata-field parameters:3, 2, 1 +LOCAL OVERRIDE field id: 5 contentInfo id: 42 versionInfo versionNo: 64 attr class: ezdata-field parameters:3, 2, 1 +SELF OVERRIDE field id: 5 contentInfo id: 42 versionInfo versionNo: 64 attr class: ezdata-field parameters:3, 2, 1 diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ibexa_render_field_exception.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ibexa_render_field_exception.test new file mode 100644 index 0000000000..3d195973a2 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ibexa_render_field_exception.test @@ -0,0 +1,23 @@ +--TEST-- +Exception for "ibexa_render_field" function +--TEMPLATE-- +{% extends 'templates/base.html.twig' %} +{% block content %} +{{ ibexa_render_field( notexisting, 'testfield' ) }} +{% endblock %} +--DATA-- +return array( + 'notexisting' => $this->getContent( + 'notexisting', + array( + 'notexisting' => array( + 'id' => 2, + 'fieldDefIdentifier' => 'testfield', + 'value' => 'foo2', + 'languageCode' => 'fre-FR' + ), + ) + ) +) +--EXCEPTION-- +An exception has been thrown during the rendering of a template ("Cannot find 'notexisting_field' template block.") in "index.twig" at line 4. diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ibexa_render_fielddefinition_settings.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ibexa_render_fielddefinition_settings.test new file mode 100644 index 0000000000..caae879f94 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/ibexa_render_fielddefinition_settings.test @@ -0,0 +1,25 @@ +--TEST-- +"ibexa_render_field_definition_settings" function +--TEMPLATE-- +{{ ibexa_render_field_definition_settings( nooverride ) }} +{{ ibexa_render_field_definition_settings( overrides ) }} +{{ ibexa_render_field_definition_settings( notdefault ) }} +{{ ibexa_render_field_definition_settings( withdata ) }} +{{ ibexa_render_field_definition_settings( notexisting ) }} +--DATA-- +return array( + 'nooverride' => $this->getFieldDefinition( 'eznooverride' ), + 'overrides' => $this->getFieldDefinition( 'ezoverride' ), + 'notdefault' => $this->getFieldDefinition( 'eznotdefaulttemplate' ), + 'withdata' => $this->getFieldDefinition( + 'ezwithdata', 42, + array( 'frameworks' => array( 'YUI3', 'jQuery' ) ) + ), + 'notexisting' => $this->getFieldDefinition( 'eznotexisting' ) +) +--EXPECT-- +default (no override) +override2 +not default +42 ezwithdata YUI3, jQuery + diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/base.html.twig b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/base.html.twig similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/base.html.twig rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/base.html.twig diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_default.html.twig b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_default.html.twig similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_default.html.twig rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_default.html.twig diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_localoverride.html.twig b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_localoverride.html.twig similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_localoverride.html.twig rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_localoverride.html.twig diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_override1.html.twig b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_override1.html.twig similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_override1.html.twig rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_override1.html.twig diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_override2.html.twig b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_override2.html.twig similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_override2.html.twig rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/fields_override2.html.twig diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/settings_default.html.twig b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/settings_default.html.twig similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/settings_default.html.twig rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/settings_default.html.twig diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/settings_override1.html.twig b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/settings_override1.html.twig similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/settings_override1.html.twig rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/settings_override1.html.twig diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/settings_override2.html.twig b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/settings_override2.html.twig similarity index 100% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/field_rendering_functions/templates/settings_override2.html.twig rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/field_rendering_functions/templates/settings_override2.html.twig diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/filters/ez_data_attributes_serialize.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/filters/ez_data_attributes_serialize.test similarity index 85% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/filters/ez_data_attributes_serialize.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/filters/ez_data_attributes_serialize.test index a3d47879ad..f6355c5a4c 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/filters/ez_data_attributes_serialize.test +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/filters/ez_data_attributes_serialize.test @@ -1,5 +1,7 @@ --TEST-- "ez_data_attributes_serialize" filter +--DEPRECATION-- +Twig Filter "ez_data_attributes_serialize" is deprecated since version 4.0. Use "ibexa_data_attributes_serialize" instead in index.twig at line 2. --TEMPLATE-- <a href="/article" {{ data_attributes|ez_data_attributes_serialize }}>Article</a> --DATA-- diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/filters/ibexa_data_attributes_serialize.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/filters/ibexa_data_attributes_serialize.test new file mode 100644 index 0000000000..6681e0066d --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/filters/ibexa_data_attributes_serialize.test @@ -0,0 +1,37 @@ +--TEST-- +"ibexa_data_attributes_serialize" filter +--TEMPLATE-- +<a href="/article" {{ data_attributes|ibexa_data_attributes_serialize }}>Article</a> +--DATA-- +return [ + 'data_attributes' => [ + 'my-attr1' => 'value1', + 'my-attr2' => 'value2,value3', + ] +]; +--EXPECT-- +<a href="/article" data-my-attr1="value1" data-my-attr2="value2,value3">Article</a> +--DATA-- +return [ + 'data_attributes' => [ + 'attr' => 'foo" style="background: red', + ] +]; +--EXPECT-- +<a href="/article" data-attr="foo" style="background: red">Article</a> +--DATA-- +return [ + 'data_attributes' => [ + 'attr' => true, + ] +]; +--EXPECT-- +<a href="/article" data-attr="true">Article</a> +--DATA-- +return [ + 'data_attributes' => [ + 'attr' => ['key1' => 'value1', 'key2' => 'value2'], + ] +]; +--EXPECT-- +<a href="/article" data-attr="{"key1":"value1","key2":"value2"}">Article</a> diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/functions/ez_file_size/ez_file_size.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/functions/ez_file_size/ez_file_size.test new file mode 100644 index 0000000000..109f9ef365 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/functions/ez_file_size/ez_file_size.test @@ -0,0 +1,78 @@ +--TEST-- +"twig" filter +--DEPRECATION-- +Twig Filter "ez_file_size" is deprecated since version 4.0. Use "ibexa_file_size" instead in index.twig at line 2. +Twig Filter "ez_file_size" is deprecated since version 4.0. Use "ibexa_file_size" instead in index.twig at line 3. +Twig Filter "ez_file_size" is deprecated since version 4.0. Use "ibexa_file_size" instead in index.twig at line 4. +Twig Filter "ez_file_size" is deprecated since version 4.0. Use "ibexa_file_size" instead in index.twig at line 5. +Twig Filter "ez_file_size" is deprecated since version 4.0. Use "ibexa_file_size" instead in index.twig at line 6. +Twig Filter "ez_file_size" is deprecated since version 4.0. Use "ibexa_file_size" instead in index.twig at line 7. +Twig Filter "ez_file_size" is deprecated since version 4.0. Use "ibexa_file_size" instead in index.twig at line 8. +Twig Filter "ez_file_size" is deprecated since version 4.0. Use "ibexa_file_size" instead in index.twig at line 9. +Twig Filter "ez_file_size" is deprecated since version 4.0. Use "ibexa_file_size" instead in index.twig at line 10. +Twig Filter "ez_file_size" is deprecated since version 4.0. Use "ibexa_file_size" instead in index.twig at line 11. +Twig Filter "ez_file_size" is deprecated since version 4.0. Use "ibexa_file_size" instead in index.twig at line 12. +Twig Filter "ez_file_size" is deprecated since version 4.0. Use "ibexa_file_size" instead in index.twig at line 13. +--TEMPLATE-- +{{ 10|ez_file_size( 2 ) }} +{{ 1024|ez_file_size( 0 ) }} +{{ 5120|ez_file_size( 3 ) }} +{{ 12288|ez_file_size( 1 ) }} +{{ 155648|ez_file_size( 0 ) }} +{{ 27421583|ez_file_size( 5 ) }} +{{ 129103927|ez_file_size( 4 ) }} +{{ 490163142656|ez_file_size( 1 ) }} +{{ 868383057603765|ez_file_size( 6 ) }} +{{ 889224250749591400|ez_file_size( 10 ) }} +{{ 910565875123441600000|ez_file_size( 2 ) }} +{{ 910565632767581700000000000|ez_file_size( 4 ) }} +--DATA-- +$this->setConfigurationLocale( array( 'wrong local' ), 'eng-GB' ); +return array() +--EXPECT-- +10 B wrong local so we take the default one which is en-GB here +1 kB wrong local so we take the default one which is en-GB here +5 kB wrong local so we take the default one which is en-GB here +12 kB wrong local so we take the default one which is en-GB here +152 kB wrong local so we take the default one which is en-GB here +26.15126 MB wrong local so we take the default one which is en-GB here +123.1231 MB wrong local so we take the default one which is en-GB here +456.5 GB wrong local so we take the default one which is en-GB here +789.78979 TB wrong local so we take the default one which is en-GB here +789.7897897898 PB wrong local so we take the default one which is en-GB here +789.79 EB wrong local so we take the default one which is en-GB here +789789789.7898 EB wrong local so we take the default one which is en-GB here +--DATA-- +return array() +--CONFIG-- +$this->locale = "fre-FR"; return array(); +--EXPECT-- +10 B French version +1 kB French version +5 kB French version +12 kB French version +152 kB French version +26,15126 MB French version +123,1231 MB French version +456,5 GB French version +789,78979 TB French version +789,7897897898 PB French version +789,79 EB French version +789789789,7898 EB French version +--DATA-- +return array() +--CONFIG-- +$this->locale = "eng-GB"; return array(); +--EXPECT-- +10 B English version +1 kB English version +5 kB English version +12 kB English version +152 kB English version +26.15126 MB English version +123.1231 MB English version +456.5 GB English version +789.78979 TB English version +789.7897897898 PB English version +789.79 EB English version +789789789.7898 EB English version diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/functions/ez_file_size/ibexa_file_size.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/functions/ez_file_size/ibexa_file_size.test new file mode 100644 index 0000000000..e8000014ec --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/functions/ez_file_size/ibexa_file_size.test @@ -0,0 +1,65 @@ +--TEST-- +"twig" filter +--TEMPLATE-- +{{ 10|ibexa_file_size( 2 ) }} +{{ 1024|ibexa_file_size( 0 ) }} +{{ 5120|ibexa_file_size( 3 ) }} +{{ 12288|ibexa_file_size( 1 ) }} +{{ 155648|ibexa_file_size( 0 ) }} +{{ 27421583|ibexa_file_size( 5 ) }} +{{ 129103927|ibexa_file_size( 4 ) }} +{{ 490163142656|ibexa_file_size( 1 ) }} +{{ 868383057603765|ibexa_file_size( 6 ) }} +{{ 889224250749591400|ibexa_file_size( 10 ) }} +{{ 910565875123441600000|ibexa_file_size( 2 ) }} +{{ 910565632767581700000000000|ibexa_file_size( 4 ) }} +--DATA-- +$this->setConfigurationLocale( array( 'wrong local' ), 'eng-GB' ); +return array() +--EXPECT-- +10 B wrong local so we take the default one which is en-GB here +1 kB wrong local so we take the default one which is en-GB here +5 kB wrong local so we take the default one which is en-GB here +12 kB wrong local so we take the default one which is en-GB here +152 kB wrong local so we take the default one which is en-GB here +26.15126 MB wrong local so we take the default one which is en-GB here +123.1231 MB wrong local so we take the default one which is en-GB here +456.5 GB wrong local so we take the default one which is en-GB here +789.78979 TB wrong local so we take the default one which is en-GB here +789.7897897898 PB wrong local so we take the default one which is en-GB here +789.79 EB wrong local so we take the default one which is en-GB here +789789789.7898 EB wrong local so we take the default one which is en-GB here +--DATA-- +return array() +--CONFIG-- +$this->locale = "fre-FR"; return array(); +--EXPECT-- +10 B French version +1 kB French version +5 kB French version +12 kB French version +152 kB French version +26,15126 MB French version +123,1231 MB French version +456,5 GB French version +789,78979 TB French version +789,7897897898 PB French version +789,79 EB French version +789789789,7898 EB French version +--DATA-- +return array() +--CONFIG-- +$this->locale = "eng-GB"; return array(); +--EXPECT-- +10 B English version +1 kB English version +5 kB English version +12 kB English version +152 kB English version +26.15126 MB English version +123.1231 MB English version +456.5 GB English version +789.78979 TB English version +789.7897897898 PB English version +789.79 EB English version +789789789.7898 EB English version diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/query_rendering_functions/ez_render_content_query.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ez_render_content_query.test similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/query_rendering_functions/ez_render_content_query.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ez_render_content_query.test index c351361ec4..50d4150144 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/query_rendering_functions/ez_render_content_query.test +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ez_render_content_query.test @@ -13,7 +13,7 @@ return array() array ( 0 => Symfony\Component\HttpKernel\Controller\ControllerReference::__set_state(array( - 'controller' => 'ez_query_render::renderQuery', + 'controller' => 'ibexa_query_render::renderQuery', 'attributes' => array ( 'options' => diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/query_rendering_functions/ez_render_content_query_esi.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ez_render_content_query_esi.test similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/query_rendering_functions/ez_render_content_query_esi.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ez_render_content_query_esi.test index bd58936295..deea30cbe0 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/query_rendering_functions/ez_render_content_query_esi.test +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ez_render_content_query_esi.test @@ -13,7 +13,7 @@ return array() array ( 0 => Symfony\Component\HttpKernel\Controller\ControllerReference::__set_state(array( - 'controller' => 'ez_query_render::renderQuery', + 'controller' => 'ibexa_query_render::renderQuery', 'attributes' => array ( 'options' => diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/query_rendering_functions/ez_render_location_query.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ez_render_location_query.test similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/query_rendering_functions/ez_render_location_query.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ez_render_location_query.test index 8b0fc22650..87d38f00c8 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/query_rendering_functions/ez_render_location_query.test +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ez_render_location_query.test @@ -13,7 +13,7 @@ return array() array ( 0 => Symfony\Component\HttpKernel\Controller\ControllerReference::__set_state(array( - 'controller' => 'ez_query_render::renderQuery', + 'controller' => 'ibexa_query_render::renderQuery', 'attributes' => array ( 'options' => diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/query_rendering_functions/ez_render_location_query_esi.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ez_render_location_query_esi.test similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/query_rendering_functions/ez_render_location_query_esi.test rename to tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ez_render_location_query_esi.test index 5c17042360..47138cd606 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/Extension/_fixtures/query_rendering_functions/ez_render_location_query_esi.test +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ez_render_location_query_esi.test @@ -13,7 +13,7 @@ return array() array ( 0 => Symfony\Component\HttpKernel\Controller\ControllerReference::__set_state(array( - 'controller' => 'ez_query_render::renderQuery', + 'controller' => 'ibexa_query_render::renderQuery', 'attributes' => array ( 'options' => diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ibexa_render_content_query.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ibexa_render_content_query.test new file mode 100644 index 0000000000..b827277a24 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ibexa_render_content_query.test @@ -0,0 +1,36 @@ +--TEST-- +"ibexa_render_content_query" function +--TEMPLATE-- +{{ ibexa_render_content_query({ + 'query': { + 'query_type': 'LatestBlogPost', + }, + 'template': 'latest_blog_post.html.twig', +}) }} +--DATA-- +return array() +--EXPECT-- +array ( + 0 => + Symfony\Component\HttpKernel\Controller\ControllerReference::__set_state(array( + 'controller' => 'ibexa_query_render::renderQuery', + 'attributes' => + array ( + 'options' => + array ( + 'query' => + array ( + 'query_type' => 'LatestBlogPost', + ), + 'template' => 'latest_blog_post.html.twig', + ), + ), + 'query' => + array ( + ), + )), + 1 => 'inline', + 2 => + array ( + ), +) diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ibexa_render_content_query_esi.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ibexa_render_content_query_esi.test new file mode 100644 index 0000000000..f4781eb192 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ibexa_render_content_query_esi.test @@ -0,0 +1,36 @@ +--TEST-- +"ibexa_render_content_query_esi" function +--TEMPLATE-- +{{ ibexa_render_content_query_esi({ + 'query': { + 'query_type': 'LatestBlogPost', + }, + 'template': 'latest_blog_post.html.twig', +}) }} +--DATA-- +return array() +--EXPECT-- +array ( + 0 => + Symfony\Component\HttpKernel\Controller\ControllerReference::__set_state(array( + 'controller' => 'ibexa_query_render::renderQuery', + 'attributes' => + array ( + 'options' => + array ( + 'query' => + array ( + 'query_type' => 'LatestBlogPost', + ), + 'template' => 'latest_blog_post.html.twig', + ), + ), + 'query' => + array ( + ), + )), + 1 => 'esi', + 2 => + array ( + ), +) diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ibexa_render_location_query.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ibexa_render_location_query.test new file mode 100644 index 0000000000..bc1ff47d6c --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ibexa_render_location_query.test @@ -0,0 +1,36 @@ +--TEST-- +"ibexa_render_location_query" function +--TEMPLATE-- +{{ ibexa_render_location_query({ + 'query': { + 'query_type': 'LatestBlogPost', + }, + 'template': 'latest_blog_post.html.twig', +}) }} +--DATA-- +return array() +--EXPECT-- +array ( + 0 => + Symfony\Component\HttpKernel\Controller\ControllerReference::__set_state(array( + 'controller' => 'ibexa_query_render::renderQuery', + 'attributes' => + array ( + 'options' => + array ( + 'query' => + array ( + 'query_type' => 'LatestBlogPost', + ), + 'template' => 'latest_blog_post.html.twig', + ), + ), + 'query' => + array ( + ), + )), + 1 => 'inline', + 2 => + array ( + ), +) diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ibexa_render_location_query_esi.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ibexa_render_location_query_esi.test new file mode 100644 index 0000000000..c7e7596d36 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/query_rendering_functions/ibexa_render_location_query_esi.test @@ -0,0 +1,36 @@ +--TEST-- +"ibexa_render_location_query_esi" function +--TEMPLATE-- +{{ ibexa_render_location_query_esi({ + 'query': { + 'query_type': 'LatestBlogPost', + }, + 'template': 'latest_blog_post.html.twig', +}) }} +--DATA-- +return array() +--EXPECT-- +array ( + 0 => + Symfony\Component\HttpKernel\Controller\ControllerReference::__set_state(array( + 'controller' => 'ibexa_query_render::renderQuery', + 'attributes' => + array ( + 'options' => + array ( + 'query' => + array ( + 'query_type' => 'LatestBlogPost', + ), + 'template' => 'latest_blog_post.html.twig', + ), + ), + 'query' => + array ( + ), + )), + 1 => 'esi', + 2 => + array ( + ), +) diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ez_path.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ez_path.test new file mode 100644 index 0000000000..c139633803 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ez_path.test @@ -0,0 +1,74 @@ +--TEST-- +"ez_path" function +--DEPRECATION-- +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 2. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 3. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 4. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 5. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 6. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 7. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 8. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 9. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 10. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 11. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 12. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 13. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 14. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 15. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 16. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 17. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 18. +Twig Function "ez_path" is deprecated since version 4.0. Use "ibexa_path" instead in index.twig at line 19. +--TEMPLATE-- +{{ ez_path(location) }} +{{ ez_path(location, {}, true) }} +{{ ez_path(location, {'foo': 'foo'}) }} +{{ ez_path(content) }} +{{ ez_path(content, {}, true) }} +{{ ez_path(content, {'foo': 'foo'}) }} +{{ ez_path(content_info) }} +{{ ez_path(content_info, {}, true) }} +{{ ez_path(content_info, {'foo': 'foo'}) }} +{{ ez_path(content_aware) }} +{{ ez_path(content_aware, {}, true) }} +{{ ez_path(content_aware, {'foo': 'foo'}) }} +{{ ez_path(route_ref) }} +{{ ez_path(route_ref, {}, true) }} +{{ ez_path(route_ref, {'baz': 'baz'}) }} +{{ ez_path(unsupported_object) }} +{{ ez_path(unsupported_object, {}, true) }} +{{ ez_path(unsupported_object, {'baz': 'baz'}) }} +--DATA-- +return [ + 'location' => $this->getExampleLocation(54), + 'content' => $this->getExampleContent(2), + 'content_info' => $this->getExampleContentInfo(2), + 'content_aware' => $this->getExampleContentAware(64), + 'route_ref' => $this->getExampleRouteReference( + 'example_route', + [ + 'foo' => 'foo', + 'bar' => 'bar' + ] + ), + 'unsupported_object' => $this->getExampleUnsupportedObject(), +]; +--EXPECT-- +{"$name":"ibexa.url.alias","$parameters":{"locationId":54},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"locationId":54},"$referenceType":2} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","locationId":54},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":2} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","contentId":2},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":2} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","contentId":2},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"contentId":64},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"contentId":64},"$referenceType":2} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","contentId":64},"$referenceType":1} +{"$name":"example_route","$parameters":{"foo":"foo","bar":"bar"},"$referenceType":1} +{"$name":"example_route","$parameters":{"foo":"foo","bar":"bar"},"$referenceType":2} +{"$name":"example_route","$parameters":{"baz":"baz","foo":"foo","bar":"bar"},"$referenceType":1} +{"$name":"","$parameters":{"_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":1} +{"$name":"","$parameters":{"_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":2} +{"$name":"","$parameters":{"baz":"baz","_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":1} diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ez_route.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ez_route.test new file mode 100644 index 0000000000..20e6a5aba1 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ez_route.test @@ -0,0 +1,25 @@ +--TEST-- +"twig" filter +--DEPRECATION-- +Twig Function "ez_route" is deprecated since version 4.0. Use "ibexa_route" instead in index.twig at line 2. +Twig Function "ez_route" is deprecated since version 4.0. Use "ibexa_route" instead in index.twig at line 3. +Twig Function "ez_route" is deprecated since version 4.0. Use "ibexa_route" instead in index.twig at line 4. +--TEMPLATE-- +{% set route_ref1 = ez_route( "foo_route" ) %} +{% set route_ref2 = ez_route( "bar_route", {"some": "thing"} ) %} +{% set route_ref3 = ez_route( "route_66", {"direction": "highway to hell"} ) %} +{{ route_ref1.route }} +{{ route_ref1.get( "param", "test" ) }} +{{ route_ref2.route }} +{{ route_ref2.get( "some" ) }} +{{ route_ref3.route }} +{{ route_ref3.get( "direction", "highway to hell" ) }} +--DATA-- +return array(); +--EXPECT-- +foo_route +test +bar_route +thing +route_66 +highway to hell diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ez_url.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ez_url.test new file mode 100644 index 0000000000..11fc817e47 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ez_url.test @@ -0,0 +1,74 @@ +--TEST-- +"ez_url" function +--DEPRECATION-- +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 2. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 3. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 4. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 5. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 6. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 7. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 8. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 9. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 10. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 11. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 12. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 13. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 14. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 15. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 16. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 17. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 18. +Twig Function "ez_url" is deprecated since version 4.0. Use "ibexa_url" instead in index.twig at line 19. +--TEMPLATE-- +{{ ez_url(location) }} +{{ ez_url(location, {}, true) }} +{{ ez_url(location, {'foo': 'foo'}) }} +{{ ez_url(content) }} +{{ ez_url(content, {}, true) }} +{{ ez_url(content, {'foo': 'foo'}) }} +{{ ez_url(content_info) }} +{{ ez_url(content_info, {}, true) }} +{{ ez_url(content_info, {'foo': 'foo'}) }} +{{ ez_url(content_aware) }} +{{ ez_url(content_aware, {}, true) }} +{{ ez_url(content_aware, {'foo': 'foo'}) }} +{{ ez_url(route_ref) }} +{{ ez_url(route_ref, {}, true) }} +{{ ez_url(route_ref, {'baz': 'baz'}) }} +{{ ez_url(unsupported_object) }} +{{ ez_url(unsupported_object, {}, true) }} +{{ ez_url(unsupported_object, {'baz': 'baz'}) }} +--DATA-- +return [ + 'location' => $this->getExampleLocation(54), + 'content' => $this->getExampleContent(2), + 'content_info' => $this->getExampleContentInfo(2), + 'content_aware' => $this->getExampleContentAware(64), + 'route_ref' => $this->getExampleRouteReference( + 'example_route', + [ + 'foo' => 'foo', + 'bar' => 'bar' + ] + ), + 'unsupported_object' => $this->getExampleUnsupportedObject(), +]; +--EXPECT-- +{"$name":"ibexa.url.alias","$parameters":{"locationId":54},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"locationId":54},"$referenceType":3} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","locationId":54},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":3} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","contentId":2},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":3} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","contentId":2},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"contentId":64},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"contentId":64},"$referenceType":3} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","contentId":64},"$referenceType":0} +{"$name":"example_route","$parameters":{"foo":"foo","bar":"bar"},"$referenceType":0} +{"$name":"example_route","$parameters":{"foo":"foo","bar":"bar"},"$referenceType":3} +{"$name":"example_route","$parameters":{"baz":"baz","foo":"foo","bar":"bar"},"$referenceType":0} +{"$name":"","$parameters":{"_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":0} +{"$name":"","$parameters":{"_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":3} +{"$name":"","$parameters":{"baz":"baz","_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":0} diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ibexa_path.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ibexa_path.test new file mode 100644 index 0000000000..c23a9a9676 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ibexa_path.test @@ -0,0 +1,55 @@ +--TEST-- +"ibexa_path" function +--TEMPLATE-- +{{ ibexa_path(location) }} +{{ ibexa_path(location, {}, true) }} +{{ ibexa_path(location, {'foo': 'foo'}) }} +{{ ibexa_path(content) }} +{{ ibexa_path(content, {}, true) }} +{{ ibexa_path(content, {'foo': 'foo'}) }} +{{ ibexa_path(content_info) }} +{{ ibexa_path(content_info, {}, true) }} +{{ ibexa_path(content_info, {'foo': 'foo'}) }} +{{ ibexa_path(content_aware) }} +{{ ibexa_path(content_aware, {}, true) }} +{{ ibexa_path(content_aware, {'foo': 'foo'}) }} +{{ ibexa_path(route_ref) }} +{{ ibexa_path(route_ref, {}, true) }} +{{ ibexa_path(route_ref, {'baz': 'baz'}) }} +{{ ibexa_path(unsupported_object) }} +{{ ibexa_path(unsupported_object, {}, true) }} +{{ ibexa_path(unsupported_object, {'baz': 'baz'}) }} +--DATA-- +return [ + 'location' => $this->getExampleLocation(54), + 'content' => $this->getExampleContent(2), + 'content_info' => $this->getExampleContentInfo(2), + 'content_aware' => $this->getExampleContentAware(64), + 'route_ref' => $this->getExampleRouteReference( + 'example_route', + [ + 'foo' => 'foo', + 'bar' => 'bar' + ] + ), + 'unsupported_object' => $this->getExampleUnsupportedObject(), +]; +--EXPECT-- +{"$name":"ibexa.url.alias","$parameters":{"locationId":54},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"locationId":54},"$referenceType":2} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","locationId":54},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":2} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","contentId":2},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":2} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","contentId":2},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"contentId":64},"$referenceType":1} +{"$name":"ibexa.url.alias","$parameters":{"contentId":64},"$referenceType":2} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","contentId":64},"$referenceType":1} +{"$name":"example_route","$parameters":{"foo":"foo","bar":"bar"},"$referenceType":1} +{"$name":"example_route","$parameters":{"foo":"foo","bar":"bar"},"$referenceType":2} +{"$name":"example_route","$parameters":{"baz":"baz","foo":"foo","bar":"bar"},"$referenceType":1} +{"$name":"","$parameters":{"_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":1} +{"$name":"","$parameters":{"_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":2} +{"$name":"","$parameters":{"baz":"baz","_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":1} diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ibexa_route.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ibexa_route.test new file mode 100644 index 0000000000..f03cc5af17 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ibexa_route.test @@ -0,0 +1,21 @@ +--TEST-- +"twig" filter +--TEMPLATE-- +{% set route_ref1 = ibexa_route( "foo_route" ) %} +{% set route_ref2 = ibexa_route( "bar_route", {"some": "thing"} ) %} +{% set route_ref3 = ibexa_route( "route_66", {"direction": "highway to hell"} ) %} +{{ route_ref1.route }} +{{ route_ref1.get( "param", "test" ) }} +{{ route_ref2.route }} +{{ route_ref2.get( "some" ) }} +{{ route_ref3.route }} +{{ route_ref3.get( "direction", "highway to hell" ) }} +--DATA-- +return array(); +--EXPECT-- +foo_route +test +bar_route +thing +route_66 +highway to hell diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ibexa_url.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ibexa_url.test new file mode 100644 index 0000000000..f737c8d3ab --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/routing_functions/ibexa_url.test @@ -0,0 +1,55 @@ +--TEST-- +"ibexa_url" function +--TEMPLATE-- +{{ ibexa_url(location) }} +{{ ibexa_url(location, {}, true) }} +{{ ibexa_url(location, {'foo': 'foo'}) }} +{{ ibexa_url(content) }} +{{ ibexa_url(content, {}, true) }} +{{ ibexa_url(content, {'foo': 'foo'}) }} +{{ ibexa_url(content_info) }} +{{ ibexa_url(content_info, {}, true) }} +{{ ibexa_url(content_info, {'foo': 'foo'}) }} +{{ ibexa_url(content_aware) }} +{{ ibexa_url(content_aware, {}, true) }} +{{ ibexa_url(content_aware, {'foo': 'foo'}) }} +{{ ibexa_url(route_ref) }} +{{ ibexa_url(route_ref, {}, true) }} +{{ ibexa_url(route_ref, {'baz': 'baz'}) }} +{{ ibexa_url(unsupported_object) }} +{{ ibexa_url(unsupported_object, {}, true) }} +{{ ibexa_url(unsupported_object, {'baz': 'baz'}) }} +--DATA-- +return [ + 'location' => $this->getExampleLocation(54), + 'content' => $this->getExampleContent(2), + 'content_info' => $this->getExampleContentInfo(2), + 'content_aware' => $this->getExampleContentAware(64), + 'route_ref' => $this->getExampleRouteReference( + 'example_route', + [ + 'foo' => 'foo', + 'bar' => 'bar' + ] + ), + 'unsupported_object' => $this->getExampleUnsupportedObject(), +]; +--EXPECT-- +{"$name":"ibexa.url.alias","$parameters":{"locationId":54},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"locationId":54},"$referenceType":3} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","locationId":54},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":3} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","contentId":2},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"contentId":2},"$referenceType":3} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","contentId":2},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"contentId":64},"$referenceType":0} +{"$name":"ibexa.url.alias","$parameters":{"contentId":64},"$referenceType":3} +{"$name":"ibexa.url.alias","$parameters":{"foo":"foo","contentId":64},"$referenceType":0} +{"$name":"example_route","$parameters":{"foo":"foo","bar":"bar"},"$referenceType":0} +{"$name":"example_route","$parameters":{"foo":"foo","bar":"bar"},"$referenceType":3} +{"$name":"example_route","$parameters":{"baz":"baz","foo":"foo","bar":"bar"},"$referenceType":0} +{"$name":"","$parameters":{"_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":0} +{"$name":"","$parameters":{"_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":3} +{"$name":"","$parameters":{"baz":"baz","_route_object":{"foo":"foo","bar":"bar"}},"$referenceType":0} diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_functions/ibexa_current_user.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_functions/ibexa_current_user.test new file mode 100644 index 0000000000..4eb566cf9d --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_functions/ibexa_current_user.test @@ -0,0 +1,8 @@ +--TEST-- +"ibexa_current_user" function +--TEMPLATE-- +{{ ibexa_current_user().getUserId() }} +--DATA-- +return []; +--EXPECT-- +10 diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_functions/ibexa_is_current_user.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_functions/ibexa_is_current_user.test new file mode 100644 index 0000000000..6e5c5fb905 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_functions/ibexa_is_current_user.test @@ -0,0 +1,13 @@ +--TEST-- +"ibexa_is_current_user" function +--TEMPLATE-- +{{ ibexa_is_current_user(user_foo) ? 'YES' : 'NO' }} +{{ ibexa_is_current_user(user_bar) ? 'YES' : 'NO' }} +--DATA-- +return [ + 'user_foo' => $this->getUser(10, true), + 'user_bar' => $this->getUser(11, false), +]; +--EXPECT-- +YES +NO diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_preference_functions/get_user_preference_value.function.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_preference_functions/get_user_preference_value.function.test new file mode 100644 index 0000000000..3372d752b6 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_preference_functions/get_user_preference_value.function.test @@ -0,0 +1,10 @@ +--TEST-- +"ibexa_get_user_preference_value" function +--TEMPLATE-- +{{ ibexa_get_user_preference_value('foo', 'default') }} +{{ ibexa_get_user_preference_value('baz', 'default') }} +--DATA-- +return []; +--EXPECT-- +bar +default diff --git a/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_preference_functions/has_user_preference.function.test b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_preference_functions/has_user_preference.function.test new file mode 100644 index 0000000000..5db23fe020 --- /dev/null +++ b/tests/lib/MVC/Symfony/Templating/Twig/Extension/_fixtures/user_preference_functions/has_user_preference.function.test @@ -0,0 +1,10 @@ +--TEST-- +"ibexa_has_user_preference" function +--TEMPLATE-- +{{ ibexa_has_user_preference('foo') is same as(true) }} +{{ ibexa_has_user_preference('baz') is same as(false) }} +--DATA-- +return []; +--EXPECT-- +1 +1 diff --git a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/ResourceProviderTest.php b/tests/lib/MVC/Symfony/Templating/Twig/ResourceProviderTest.php similarity index 92% rename from eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/ResourceProviderTest.php rename to tests/lib/MVC/Symfony/Templating/Twig/ResourceProviderTest.php index 148785da4a..ef0b521e09 100644 --- a/eZ/Publish/Core/MVC/Symfony/Templating/Tests/Twig/ResourceProviderTest.php +++ b/tests/lib/MVC/Symfony/Templating/Twig/ResourceProviderTest.php @@ -6,18 +6,18 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig; +namespace Ibexa\Tests\Core\MVC\Symfony\Templating\Twig; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\Templating\Twig\ResourceProvider; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\Templating\Twig\ResourceProvider; use PHPUnit\Framework\TestCase; class ResourceProviderTest extends TestCase { - /** @var \eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ protected $configResolver; - /** @var \eZ\Publish\Core\MVC\Symfony\Templating\Twig\ResourceProvider */ + /** @var \Ibexa\Core\MVC\Symfony\Templating\Twig\ResourceProvider */ protected $resourceProvider; protected function setUp(): void @@ -93,7 +93,7 @@ public function testGetFieldDefinitionEditResources(): void * * Make sure returned resource lists are not sorted as ResourceProvider is sorting them * - * @return \eZ\Publish\Core\MVC\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ protected function getConfigResolverMock(): ConfigResolverInterface { @@ -183,3 +183,5 @@ protected function getConfigResolverMock(): ConfigResolverInterface return $mock; } } + +class_alias(ResourceProviderTest::class, 'eZ\Publish\Core\MVC\Symfony\Templating\Tests\Twig\ResourceProviderTest'); diff --git a/tests/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitorTest.php b/tests/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitorTest.php new file mode 100644 index 0000000000..b151a1915d --- /dev/null +++ b/tests/lib/MVC/Symfony/Translation/ExceptionMessageTemplateFileVisitorTest.php @@ -0,0 +1,102 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Translation; + +use Doctrine\Common\Annotations\DocParser; +use Ibexa\Core\MVC\Symfony\Translation\ExceptionMessageTemplateFileVisitor; +use JMS\TranslationBundle\Model\Message; +use JMS\TranslationBundle\Model\MessageCatalogue; +use JMS\TranslationBundle\Translation\FileSourceFactory; +use PhpParser\Lexer; +use PhpParser\Parser; +use PhpParser\ParserFactory; +use PHPUnit\Framework\TestCase; +use Psr\Log\LoggerInterface; +use SplFileInfo; + +final class ExceptionMessageTemplateFileVisitorTest extends TestCase +{ + private const FIXTURES_DIR = __DIR__ . '/fixtures/'; + + private Parser $phpParser; + + private ExceptionMessageTemplateFileVisitor $exceptionMessageTemplateFileVisitor; + + protected function setUp(): void + { + $docParser = new DocParser(); + $fileSourceFactory = new FileSourceFactory( + self::FIXTURES_DIR, + ); + $lexer = new Lexer(); + $factory = new ParserFactory(); + $this->phpParser = $factory->create(ParserFactory::PREFER_PHP7, $lexer); + $this->exceptionMessageTemplateFileVisitor = new ExceptionMessageTemplateFileVisitor( + $docParser, + $fileSourceFactory + ); + } + + public function testExtractTranslation(): void + { + $messageCatalogue = new MessageCatalogue(); + $file = self::FIXTURES_DIR . 'SetMessageTemplate.php'; + $fileInfo = new SplFileInfo($file); + + $ast = $this->phpParser->parse(file_get_contents($file)); + $this->exceptionMessageTemplateFileVisitor->visitPhpFile( + $fileInfo, + $messageCatalogue, + $ast + ); + + $expectedMessage = new Message('Foo exception', 'ibexa_repository_exceptions'); + + self::assertTrue( + $messageCatalogue->has($expectedMessage) + ); + } + + public function testNoTranslationToExtract(): void + { + $messageCatalogue = new MessageCatalogue(); + $file = self::FIXTURES_DIR . 'NoTranslationToExtract.php'; + $fileInfo = new SplFileInfo($file); + + $ast = $this->phpParser->parse(file_get_contents($file)); + $this->exceptionMessageTemplateFileVisitor->visitPhpFile( + $fileInfo, + $messageCatalogue, + $ast + ); + + self::assertEmpty($messageCatalogue->getDomains()); + } + + public function testWrongTranslationId(): void + { + $messageCatalogue = new MessageCatalogue(); + $file = self::FIXTURES_DIR . 'WrongTranslationId.php'; + $fileInfo = new SplFileInfo($file); + + $ast = $this->phpParser->parse(file_get_contents($file)); + + $logger = $this->createMock(LoggerInterface::class); + $logger + ->expects($this->once()) + ->method('error'); + + $this->exceptionMessageTemplateFileVisitor->setLogger($logger); + $this->exceptionMessageTemplateFileVisitor->visitPhpFile( + $fileInfo, + $messageCatalogue, + $ast + ); + } +} diff --git a/tests/lib/MVC/Symfony/Translation/fixtures/NoTranslationToExtract.php b/tests/lib/MVC/Symfony/Translation/fixtures/NoTranslationToExtract.php new file mode 100644 index 0000000000..cfdd87f9e6 --- /dev/null +++ b/tests/lib/MVC/Symfony/Translation/fixtures/NoTranslationToExtract.php @@ -0,0 +1,24 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Translation\fixtures; + +use Exception; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException as APIInvalidArgumentException; +use Ibexa\Core\Base\Translatable; +use Ibexa\Core\Base\TranslatableBase; + +final class NoTranslationToExtract extends APIInvalidArgumentException implements Translatable +{ + use TranslatableBase; + + public function __construct(?Exception $previous = null) + { + parent::__construct($this->getBaseTranslation(), 0, $previous); + } +} diff --git a/tests/lib/MVC/Symfony/Translation/fixtures/SetMessageTemplate.php b/tests/lib/MVC/Symfony/Translation/fixtures/SetMessageTemplate.php new file mode 100644 index 0000000000..deea961bf6 --- /dev/null +++ b/tests/lib/MVC/Symfony/Translation/fixtures/SetMessageTemplate.php @@ -0,0 +1,26 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Translation\fixtures; + +use Exception; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException as APIInvalidArgumentException; +use Ibexa\Core\Base\Translatable; +use Ibexa\Core\Base\TranslatableBase; + +final class SetMessageTemplate extends APIInvalidArgumentException implements Translatable +{ + use TranslatableBase; + + public function __construct(?Exception $previous = null) + { + $this->setMessageTemplate('Foo exception'); + + parent::__construct($this->getBaseTranslation(), 0, $previous); + } +} diff --git a/tests/lib/MVC/Symfony/Translation/fixtures/WrongTranslationId.php b/tests/lib/MVC/Symfony/Translation/fixtures/WrongTranslationId.php new file mode 100644 index 0000000000..73a54e46e1 --- /dev/null +++ b/tests/lib/MVC/Symfony/Translation/fixtures/WrongTranslationId.php @@ -0,0 +1,26 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\MVC\Symfony\Translation\fixtures; + +use Exception; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException as APIInvalidArgumentException; +use Ibexa\Core\Base\Translatable; +use Ibexa\Core\Base\TranslatableBase; + +final class WrongTranslationId extends APIInvalidArgumentException implements Translatable +{ + use TranslatableBase; + + public function __construct(?Exception $previous = null) + { + $this->setMessageTemplate(['foo']); + + parent::__construct($this->getBaseTranslation(), 0, $previous); + } +} diff --git a/eZ/Publish/Core/MVC/Symfony/View/Tests/AbstractViewTest.php b/tests/lib/MVC/Symfony/View/AbstractViewTest.php similarity index 76% rename from eZ/Publish/Core/MVC/Symfony/View/Tests/AbstractViewTest.php rename to tests/lib/MVC/Symfony/View/AbstractViewTest.php index 695eaca6dd..3ba7a50f34 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/Tests/AbstractViewTest.php +++ b/tests/lib/MVC/Symfony/View/AbstractViewTest.php @@ -6,13 +6,16 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\View\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\View; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentType; -use eZ\Publish\Core\MVC\Symfony\View\View; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\MVC\Symfony\View\View; use InvalidArgumentException; use PHPUnit\Framework\TestCase; +/** + * @covers \Ibexa\Core\MVC\Symfony\View\View + */ abstract class AbstractViewTest extends TestCase { abstract protected function createViewUnderTest($template = null, array $parameters = [], $viewType = 'full'): View; @@ -27,10 +30,6 @@ protected function getAlwaysAvailableParams(): array return []; } - /** - * @covers \eZ\Publish\Core\MVC\Symfony\View\View::setParameters - * @covers \eZ\Publish\Core\MVC\Symfony\View\View::getParameters - */ public function testGetSetParameters(): void { $params = [ @@ -44,10 +43,6 @@ public function testGetSetParameters(): void self::assertSame($this->getAlwaysAvailableParams() + $params, $view->getParameters()); } - /** - * @covers \eZ\Publish\Core\MVC\Symfony\View\View::setParameters - * @covers \eZ\Publish\Core\MVC\Symfony\View\View::getParameters - */ public function testAddParameters(): void { $params = ['bar' => 'baz', 'fruit' => 'apple']; @@ -59,10 +54,6 @@ public function testAddParameters(): void $this->assertSame($this->getAlwaysAvailableParams() + $params + $additionalParams, $view->getParameters()); } - /** - * @covers \eZ\Publish\Core\MVC\Symfony\View\View::setParameters - * @covers \eZ\Publish\Core\MVC\Symfony\View\View::getParameters - */ public function testHasParameter(): View { $view = $this->createViewUnderTest(__METHOD__, ['foo' => 'bar']); @@ -75,12 +66,6 @@ public function testHasParameter(): View /** * @depends testHasParameter - * @covers \eZ\Publish\Core\MVC\Symfony\View\View::setParameters - * @covers \eZ\Publish\Core\MVC\Symfony\View\View::getParameters - * - * @param \eZ\Publish\Core\MVC\Symfony\View\View $view - * - * @return \eZ\Publish\Core\MVC\Symfony\View\View */ public function testGetParameter(View $view): View { @@ -91,9 +76,6 @@ public function testGetParameter(View $view): View /** * @depends testGetParameter - * - * @covers \eZ\Publish\Core\MVC\Symfony\View\View::setParameters - * @covers \eZ\Publish\Core\MVC\Symfony\View\View::getParameters */ public function testGetParameterFail(View $view): void { @@ -150,3 +132,5 @@ public function badTemplateIdentifierProvider(): array ]; } } + +class_alias(AbstractViewTest::class, 'eZ\Publish\Core\MVC\Symfony\View\Tests\AbstractViewTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/Tests/Builder/ContentViewBuilderTest.php b/tests/lib/MVC/Symfony/View/Builder/ContentViewBuilderTest.php similarity index 83% rename from eZ/Publish/Core/MVC/Symfony/View/Tests/Builder/ContentViewBuilderTest.php rename to tests/lib/MVC/Symfony/View/Builder/ContentViewBuilderTest.php index 56efa9b146..f1348661f4 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/Tests/Builder/ContentViewBuilderTest.php +++ b/tests/lib/MVC/Symfony/View/Builder/ContentViewBuilderTest.php @@ -6,25 +6,25 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\View\Tests\Builder; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Base\Exceptions\UnauthorizedException; -use eZ\Publish\Core\Helper\ContentInfoLocationLoader; -use eZ\Publish\Core\MVC\Exception\HiddenLocationException; -use eZ\Publish\Core\MVC\Symfony\View\Builder\ContentViewBuilder; -use eZ\Publish\Core\MVC\Symfony\View\Configurator; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; -use eZ\Publish\Core\MVC\Symfony\View\ParametersInjector; -use eZ\Publish\Core\Repository\Repository; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; +namespace Ibexa\Tests\Core\MVC\Symfony\View\Builder; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as APINotFoundException; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Base\Exceptions\UnauthorizedException; +use Ibexa\Core\Helper\ContentInfoLocationLoader; +use Ibexa\Core\MVC\Exception\HiddenLocationException; +use Ibexa\Core\MVC\Symfony\View\Builder\ContentViewBuilder; +use Ibexa\Core\MVC\Symfony\View\Configurator; +use Ibexa\Core\MVC\Symfony\View\ContentView; +use Ibexa\Core\MVC\Symfony\View\ParametersInjector; +use Ibexa\Core\Repository\Repository; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Repository\Values\Content\VersionInfo; use PHPUnit\Framework\TestCase; use ReflectionClass; use Symfony\Component\HttpFoundation\ParameterBag; @@ -36,22 +36,22 @@ */ class ContentViewBuilderTest extends TestCase { - /** @var \eZ\Publish\API\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject */ private $repository; - /** @var \eZ\Publish\Core\MVC\Symfony\View\Configurator|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\MVC\Symfony\View\Configurator|\PHPUnit\Framework\MockObject\MockObject */ private $viewConfigurator; - /** @var \eZ\Publish\Core\MVC\Symfony\View\ParametersInjector|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\MVC\Symfony\View\ParametersInjector|\PHPUnit\Framework\MockObject\MockObject */ private $parametersInjector; - /** @var \eZ\Publish\Core\Helper\ContentInfoLocationLoader|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\Helper\ContentInfoLocationLoader|\PHPUnit\Framework\MockObject\MockObject */ private $contentInfoLocationLoader; - /** @var \eZ\Publish\Core\MVC\Symfony\View\Builder\ContentViewBuilder|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\MVC\Symfony\View\Builder\ContentViewBuilder|\PHPUnit\Framework\MockObject\MockObject */ private $contentViewBuilder; - /** @var \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ private $permissionResolver; /** @var \Symfony\Component\HttpFoundation\RequestStack|\PHPUnit\Framework\MockObject\MockObject */ @@ -90,7 +90,7 @@ protected function setUp(): void public function testMatches(): void { - $this->assertTrue($this->contentViewBuilder->matches('ez_content:55')); + $this->assertTrue($this->contentViewBuilder->matches('ibexa_content:55')); $this->assertFalse($this->contentViewBuilder->matches('dummy_value')); } @@ -98,7 +98,7 @@ public function testBuildViewWithoutLocationIdAndContentId(): void { $parameters = [ 'viewType' => 'full', - '_controller' => 'ez_content:viewContent', + '_controller' => 'ibexa_content:viewContent', ]; $this->expectException(InvalidArgumentException::class); @@ -110,7 +110,7 @@ public function testBuildViewWithInvalidLocationId(): void { $parameters = [ 'viewType' => 'full', - '_controller' => 'ez_content:viewContent', + '_controller' => 'ibexa_content:viewContent', 'locationId' => 865, ]; @@ -128,7 +128,7 @@ public function testBuildViewWithHiddenLocation(): void { $parameters = [ 'viewType' => 'full', - '_controller' => 'ez_content:viewContent', + '_controller' => 'ibexa_content:viewContent', 'locationId' => 2, ]; @@ -151,7 +151,8 @@ public function testBuildViewWithoutContentReadPermission(): void 'invisible' => false, 'content' => new Content([ 'versionInfo' => new VersionInfo([ - 'contentInfo' => new ContentInfo(), + 'id' => 2, + 'contentInfo' => new ContentInfo(['id' => 1]), ]), ]), ] @@ -159,7 +160,7 @@ public function testBuildViewWithoutContentReadPermission(): void $parameters = [ 'viewType' => 'full', - '_controller' => 'ez_content:viewContent', + '_controller' => 'ibexa_content:viewContent', 'locationId' => 2, ]; @@ -199,7 +200,7 @@ public function testBuildEmbedViewWithoutContentViewEmbedPermission(): void $parameters = [ 'viewType' => 'embed', - '_controller' => 'ez_content:viewContent', + '_controller' => 'ibexa_content:viewContent', 'locationId' => 2, ]; @@ -236,7 +237,7 @@ public function testBuildEmbedViewWithNullMainRequest(): void $parameters = [ 'viewType' => 'embed', - '_controller' => 'ez_content:embedAction', + '_controller' => 'ibexa_content:embedAction', 'contentId' => $contentId, ]; @@ -326,7 +327,7 @@ public function testBuildViewWithContentWhichDoesNotBelongToLocation(): void $parameters = [ 'viewType' => 'full', - '_controller' => 'ez_content:viewContent', + '_controller' => 'ibexa_content:viewContent', 'locationId' => 2, ]; @@ -357,7 +358,7 @@ public function testBuildViewWithTranslatedContentWithoutLocation(): void $parameters = [ 'viewType' => 'full', - '_controller' => 'ez_content:viewContent', + '_controller' => 'ibexa_content:viewContent', 'contentId' => 120, 'languageCode' => 'eng-GB', ]; @@ -409,7 +410,7 @@ public function testBuildView(): void $parameters = [ 'viewType' => 'full', - '_controller' => 'ez_content:viewAction', + '_controller' => 'ibexa_content::viewAction', 'locationId' => 2, ]; @@ -427,8 +428,8 @@ public function testBuildView(): void } /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function testBuildViewInsertsDoNotGenerateEmbedUrlParameter(): void { @@ -478,3 +479,5 @@ public function testBuildViewInsertsDoNotGenerateEmbedUrlParameter(): void ); } } + +class_alias(ContentViewBuilderTest::class, 'eZ\Publish\Core\MVC\Symfony\View\Tests\Builder\ContentViewBuilderTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/Tests/ContentViewTest.php b/tests/lib/MVC/Symfony/View/ContentViewTest.php similarity index 78% rename from eZ/Publish/Core/MVC/Symfony/View/Tests/ContentViewTest.php rename to tests/lib/MVC/Symfony/View/ContentViewTest.php index 171ee9967a..ee039dd1a2 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/Tests/ContentViewTest.php +++ b/tests/lib/MVC/Symfony/View/ContentViewTest.php @@ -6,13 +6,16 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\View\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\View; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; -use eZ\Publish\Core\MVC\Symfony\View\View; +use Ibexa\Core\Base\Exceptions\InvalidArgumentType; +use Ibexa\Core\MVC\Symfony\View\ContentView; +use Ibexa\Core\MVC\Symfony\View\View; /** * @group mvc + * + * @covers \Ibexa\Core\MVC\Symfony\View\ContentView */ class ContentViewTest extends AbstractViewTest { @@ -25,9 +28,6 @@ class ContentViewTest extends AbstractViewTest /** * @dataProvider constructProvider - * @covers \eZ\Publish\Core\MVC\Symfony\View\ContentView::__construct - * @covers \eZ\Publish\Core\MVC\Symfony\View\ContentView::getTemplateIdentifier - * @covers \eZ\Publish\Core\MVC\Symfony\View\ContentView::getParameters */ public function testConstruct($templateIdentifier, array $params) { @@ -59,11 +59,10 @@ static function () { /** * @dataProvider constructFailProvider - * @covers \eZ\Publish\Core\MVC\Symfony\View\ContentView::__construct */ public function testConstructFail($templateIdentifier) { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\InvalidArgumentType::class); + $this->expectException(InvalidArgumentType::class); new ContentView($templateIdentifier); } @@ -87,3 +86,5 @@ protected function getAlwaysAvailableParams(): array return $this->valueParams; } } + +class_alias(ContentViewTest::class, 'eZ\Publish\Core\MVC\Symfony\View\Tests\ContentViewTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/Tests/LoginFormViewTest.php b/tests/lib/MVC/Symfony/View/LoginFormViewTest.php similarity index 77% rename from eZ/Publish/Core/MVC/Symfony/View/Tests/LoginFormViewTest.php rename to tests/lib/MVC/Symfony/View/LoginFormViewTest.php index 9e65c9aacd..7f60891914 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/Tests/LoginFormViewTest.php +++ b/tests/lib/MVC/Symfony/View/LoginFormViewTest.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\View\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\View; -use eZ\Publish\Core\MVC\Symfony\View\LoginFormView; -use eZ\Publish\Core\MVC\Symfony\View\View; +use Ibexa\Core\MVC\Symfony\View\LoginFormView; +use Ibexa\Core\MVC\Symfony\View\View; use Symfony\Component\Security\Core\Exception\AuthenticationException; /** @@ -19,7 +19,7 @@ final class LoginFormViewTest extends AbstractViewTest { public function testSetLastUsername(): void { - /** @var \eZ\Publish\Core\MVC\Symfony\View\LoginFormView $view */ + /** @var \Ibexa\Core\MVC\Symfony\View\LoginFormView $view */ $view = $this->createViewUnderTest(); $view->setLastUsername('johndoe'); @@ -30,7 +30,7 @@ public function testSetLastAuthenticationError(): void { $exception = $this->createMock(AuthenticationException::class); - /** @var \eZ\Publish\Core\MVC\Symfony\View\LoginFormView $view */ + /** @var \Ibexa\Core\MVC\Symfony\View\LoginFormView $view */ $view = $this->createViewUnderTest(); $view->setLastAuthenticationError($exception); @@ -50,3 +50,5 @@ protected function getAlwaysAvailableParams(): array ]; } } + +class_alias(LoginFormViewTest::class, 'eZ\Publish\Core\MVC\Symfony\View\Tests\LoginFormViewTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/Tests/Renderer/TemplateRendererTest.php b/tests/lib/MVC/Symfony/View/Renderer/TemplateRendererTest.php similarity index 75% rename from eZ/Publish/Core/MVC/Symfony/View/Tests/Renderer/TemplateRendererTest.php rename to tests/lib/MVC/Symfony/View/Renderer/TemplateRendererTest.php index ec87a89372..b24bfa5da4 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/Tests/Renderer/TemplateRendererTest.php +++ b/tests/lib/MVC/Symfony/View/Renderer/TemplateRendererTest.php @@ -4,19 +4,20 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\View\Tests\Renderer; +namespace Ibexa\Tests\Core\MVC\Symfony\View\Renderer; -use eZ\Publish\Core\MVC\Symfony\Event\PreContentViewEvent; -use eZ\Publish\Core\MVC\Symfony\MVCEvents; -use eZ\Publish\Core\MVC\Symfony\View\ContentView; -use eZ\Publish\Core\MVC\Symfony\View\Renderer\TemplateRenderer; +use Ibexa\Core\MVC\Exception\NoViewTemplateException; +use Ibexa\Core\MVC\Symfony\Event\PreContentViewEvent; +use Ibexa\Core\MVC\Symfony\MVCEvents; +use Ibexa\Core\MVC\Symfony\View\ContentView; +use Ibexa\Core\MVC\Symfony\View\Renderer\TemplateRenderer; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Twig\Environment; class TemplateRendererTest extends TestCase { - /** @var \eZ\Publish\Core\MVC\Symfony\View\Renderer\TemplateRenderer */ + /** @var \Ibexa\Core\MVC\Symfony\View\Renderer\TemplateRenderer */ private $renderer; /** @var \Twig\Environment|\PHPUnit\Framework\MockObject\MockObject */ @@ -61,13 +62,13 @@ public function testRender() public function testRenderNoViewTemplate() { - $this->expectException(\eZ\Publish\Core\MVC\Exception\NoViewTemplateException::class); + $this->expectException(NoViewTemplateException::class); $this->renderer->render($this->createView()); } /** - * @return \eZ\Publish\Core\MVC\Symfony\View\View + * @return \Ibexa\Core\MVC\Symfony\View\View */ protected function createView() { @@ -76,3 +77,5 @@ protected function createView() return $view; } } + +class_alias(TemplateRendererTest::class, 'eZ\Publish\Core\MVC\Symfony\View\Tests\Renderer\TemplateRendererTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/Tests/VariableProviderRegistryTest.php b/tests/lib/MVC/Symfony/View/VariableProviderRegistryTest.php similarity index 89% rename from eZ/Publish/Core/MVC/Symfony/View/Tests/VariableProviderRegistryTest.php rename to tests/lib/MVC/Symfony/View/VariableProviderRegistryTest.php index 9dac75f1b5..629c184d4f 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/Tests/VariableProviderRegistryTest.php +++ b/tests/lib/MVC/Symfony/View/VariableProviderRegistryTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\MVC\Symfony\View\Tests; +namespace Ibexa\Tests\Core\MVC\Symfony\View; use ArrayIterator; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\MVC\Symfony\View\GenericVariableProviderRegistry; -use eZ\Publish\Core\MVC\Symfony\View\View; -use eZ\Publish\SPI\MVC\View\VariableProvider; +use Ibexa\Contracts\Core\MVC\View\VariableProvider; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\MVC\Symfony\View\GenericVariableProviderRegistry; +use Ibexa\Core\MVC\Symfony\View\View; use PHPUnit\Framework\TestCase; final class VariableProviderRegistryTest extends TestCase @@ -103,3 +103,5 @@ public function testParameterProviderChecker(): void $this->assertFalse($registry->hasTwigVariableProvider('provider_c')); } } + +class_alias(VariableProviderRegistryTest::class, 'eZ\Publish\Core\MVC\Symfony\View\Tests\VariableProviderRegistryTest'); diff --git a/eZ/Publish/Core/MVC/Symfony/View/Tests/ViewManagerTest.php b/tests/lib/MVC/Symfony/View/ViewManagerTest.php similarity index 89% rename from eZ/Publish/Core/MVC/Symfony/View/Tests/ViewManagerTest.php rename to tests/lib/MVC/Symfony/View/ViewManagerTest.php index ef9d560329..8f18d913a5 100644 --- a/eZ/Publish/Core/MVC/Symfony/View/Tests/ViewManagerTest.php +++ b/tests/lib/MVC/Symfony/View/ViewManagerTest.php @@ -4,20 +4,20 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\MVC\Symfony\View\Tests; - -use eZ\Publish\API\Repository\ContentService as APIContentService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\MVC\Symfony\View\Configurator; -use eZ\Publish\Core\MVC\Symfony\View\Manager; -use eZ\Publish\Core\MVC\Symfony\View\View; -use eZ\Publish\Core\MVC\Symfony\View\ViewProvider; -use eZ\Publish\Core\Repository\ContentService; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; +namespace Ibexa\Tests\Core\MVC\Symfony\View; + +use Ibexa\Contracts\Core\Repository\ContentService as APIContentService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\MVC\Symfony\View\Configurator; +use Ibexa\Core\MVC\Symfony\View\Manager; +use Ibexa\Core\MVC\Symfony\View\View; +use Ibexa\Core\MVC\Symfony\View\ViewProvider; +use Ibexa\Core\Repository\ContentService; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Repository\Values\Content\VersionInfo; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Twig\Environment; @@ -27,7 +27,7 @@ */ class ViewManagerTest extends TestCase { - /** @var \eZ\Publish\Core\MVC\Symfony\View\Manager */ + /** @var \Ibexa\Core\MVC\Symfony\View\Manager */ private $viewManager; /** @var \PHPUnit\Framework\MockObject\MockObject|\Twig\Environment */ @@ -36,16 +36,16 @@ class ViewManagerTest extends TestCase /** @var \PHPUnit\Framework\MockObject\MockObject|\Symfony\Component\EventDispatcher\EventDispatcherInterface */ private $eventDispatcherMock; - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\Repository */ + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Core\Repository\Repository */ private $repositoryMock; - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\MVC\ConfigResolverInterface */ + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface */ private $configResolverMock; - /** @var \eZ\Publish\Core\MVC\Symfony\View\Configurator|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\MVC\Symfony\View\Configurator|\PHPUnit\Framework\MockObject\MockObject */ private $viewConfigurator; - private $viewBaseLayout = 'EzPublishCoreBundle::viewbase.html.twig'; + private $viewBaseLayout = 'IbexaCoreBundle::viewbase.html.twig'; protected function setUp(): void { @@ -83,7 +83,7 @@ public function testAddLocationViewProvider() public function testContentViewProvidersPriority() { - list($high, $medium, $low) = $this->createContentViewProviderMocks(); + [$high, $medium, $low] = $this->createContentViewProviderMocks(); $this->viewManager->addContentViewProvider($medium, 33); $this->viewManager->addContentViewProvider($high, 100); $this->viewManager->addContentViewProvider($low, -100); @@ -95,7 +95,7 @@ public function testContentViewProvidersPriority() public function testLocationViewProvidersPriority() { - list($high, $medium, $low) = $this->createLocationViewProviderMocks(); + [$high, $medium, $low] = $this->createLocationViewProviderMocks(); $this->viewManager->addLocationViewProvider($medium, 33); $this->viewManager->addLocationViewProvider($high, 100); $this->viewManager->addLocationViewProvider($low, -100); @@ -343,3 +343,5 @@ private function createLocationViewProviderMocks() ]; } } + +class_alias(ViewManagerTest::class, 'eZ\Publish\Core\MVC\Symfony\View\Tests\ViewManagerTest'); diff --git a/tests/lib/Pagination/AdapterFactory/SearchHitAdapterFactoryTest.php b/tests/lib/Pagination/AdapterFactory/SearchHitAdapterFactoryTest.php new file mode 100644 index 0000000000..cca74ca853 --- /dev/null +++ b/tests/lib/Pagination/AdapterFactory/SearchHitAdapterFactoryTest.php @@ -0,0 +1,110 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Pagination\AdapterFactory; + +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactory; +use Ibexa\Core\Pagination\Pagerfanta\ContentSearchHitAdapter; +use Ibexa\Core\Pagination\Pagerfanta\FixedSearchResultHitAdapter; +use Ibexa\Core\Pagination\Pagerfanta\LocationSearchHitAdapter; +use PHPUnit\Framework\TestCase; + +final class SearchHitAdapterFactoryTest extends TestCase +{ + private const EXAMPLE_LANGUAGE_FILTER = [ + 'language' => 'eng-GB', + ]; + + /** @var \Ibexa\Contracts\Core\Repository\SearchService|\PHPUnit\Framework\MockObject\MockObject */ + private $searchService; + + /** @var \Ibexa\Core\Pagination\Pagerfanta\AdapterFactory\SearchHitAdapterFactory */ + private $searchHitAdapterFactory; + + protected function setUp(): void + { + $this->searchService = $this->createMock(SearchService::class); + $this->searchHitAdapterFactory = new SearchHitAdapterFactory($this->searchService); + } + + public function testCreateAdapterForContentQuery(): void + { + $query = new Query(); + + $this->assertEquals( + new ContentSearchHitAdapter( + $query, + $this->searchService, + self::EXAMPLE_LANGUAGE_FILTER + ), + $this->searchHitAdapterFactory->createAdapter($query, self::EXAMPLE_LANGUAGE_FILTER) + ); + } + + public function testCreateAdapterForLocationQuery(): void + { + $query = new LocationQuery(); + + $this->assertEquals( + new LocationSearchHitAdapter( + $query, + $this->searchService, + self::EXAMPLE_LANGUAGE_FILTER + ), + $this->searchHitAdapterFactory->createAdapter($query, self::EXAMPLE_LANGUAGE_FILTER) + ); + } + + /** + * @dataProvider dataProviderForCreateFixedAdapter + */ + public function testCreateFixedAdapter(Query $query, string $expectedSearchMethod): void + { + $hits = [ + new SearchHit(), + new SearchHit(), + new SearchHit(), + ]; + + $searchResult = new SearchResult([ + 'searchHits' => $hits, + 'totalCount' => count($hits), + ]); + + $this->searchService + ->expects($this->once()) + ->method($expectedSearchMethod) + ->with($query, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($searchResult); + + $this->assertEquals( + new FixedSearchResultHitAdapter($searchResult), + $this->searchHitAdapterFactory->createFixedAdapter($query, self::EXAMPLE_LANGUAGE_FILTER) + ); + } + + public function dataProviderForCreateFixedAdapter(): iterable + { + yield 'content query' => [ + new Query(), + 'findContent', + ]; + + yield 'location query' => [ + new LocationQuery(), + 'findLocations', + ]; + } +} + +class_alias(SearchHitAdapterFactoryTest::class, 'eZ\Publish\Core\Pagination\Tests\AdapterFactory\SearchHitAdapterFactoryTest'); diff --git a/tests/lib/Pagination/ContentFilteringAdapterTest.php b/tests/lib/Pagination/ContentFilteringAdapterTest.php new file mode 100644 index 0000000000..94164893bf --- /dev/null +++ b/tests/lib/Pagination/ContentFilteringAdapterTest.php @@ -0,0 +1,100 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Pagination; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentList; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use Ibexa\Core\Pagination\Pagerfanta\ContentFilteringAdapter; +use Ibexa\Tests\Core\Search\TestCase; + +final class ContentFilteringAdapterTest extends TestCase +{ + private const EXAMPLE_LANGUAGE_FILTER = [ + 'languages' => ['eng-GB', 'pol-PL'], + 'useAlwaysAvailable' => true, + ]; + + /** @var \Ibexa\Contracts\Core\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject */ + private $contentService; + + protected function setUp(): void + { + $this->contentService = $this->createMock(ContentService::class); + } + + public function testGetNbResults(): void + { + $expectedNumberOfItems = 10; + + $filter = new Filter(); + $filter->sliceBy(5, 0); + + // Make sure that count query doesn't fetch results + $this->contentService + ->expects(self::never()) + ->method('find'); + + $this->contentService + ->method('count') + ->with( + $filter, + self::EXAMPLE_LANGUAGE_FILTER + ) + ->willReturn($expectedNumberOfItems); + + $adapter = new ContentFilteringAdapter( + $this->contentService, + $filter, + self::EXAMPLE_LANGUAGE_FILTER + ); + + self::assertEquals( + $expectedNumberOfItems, + $adapter->getNbResults() + ); + } + + public function testGetSlice(): void + { + $expectedContentList = $this->createExpectedContentList(10); + + $filter = new Filter(); + $filter->sliceBy(20, 10); + + $this->contentService + ->method('find') + ->with($filter, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($expectedContentList); + + $adapter = new ContentFilteringAdapter( + $this->contentService, + $filter, + self::EXAMPLE_LANGUAGE_FILTER + ); + + $this->assertEquals( + $expectedContentList, + $adapter->getSlice(10, 20) + ); + } + + private function createExpectedContentList(int $numberOfItems): ContentList + { + $items = []; + for ($i = 0; $i < $numberOfItems; ++$i) { + $items[] = $this->createMock(Content::class); + } + + return new ContentList($numberOfItems, $items); + } +} + +class_alias(ContentFilteringAdapterTest::class, 'eZ\Publish\Core\Pagination\Tests\ContentFilteringAdapterTest'); diff --git a/tests/lib/Pagination/ContentSearchAdapterTest.php b/tests/lib/Pagination/ContentSearchAdapterTest.php new file mode 100644 index 0000000000..1edec52e9a --- /dev/null +++ b/tests/lib/Pagination/ContentSearchAdapterTest.php @@ -0,0 +1,47 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Pagination; + +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Core\Pagination\Pagerfanta\ContentSearchAdapter; + +class ContentSearchAdapterTest extends ContentSearchHitAdapterTest +{ + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query + * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService + * @param array $languageFilter + * + * @return \Ibexa\Core\Pagination\Pagerfanta\ContentSearchAdapter + */ + protected function getAdapter(Query $query, SearchService $searchService, array $languageFilter = []) + { + return new ContentSearchAdapter($query, $searchService, $languageFilter); + } + + /** + * Returns expected result from adapter from search hits. + * + * @param $hits + * + * @return mixed + */ + protected function getExpectedFinalResultFromHits($hits) + { + $expectedResult = []; + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] $hits */ + foreach ($hits as $hit) { + $expectedResult[] = $hit->valueObject; + } + + return $expectedResult; + } +} + +class_alias(ContentSearchAdapterTest::class, 'eZ\Publish\Core\Pagination\Tests\ContentSearchAdapterTest'); diff --git a/eZ/Publish/Core/Pagination/Tests/ContentSearchHitAdapterTest.php b/tests/lib/Pagination/ContentSearchHitAdapterTest.php similarity index 83% rename from eZ/Publish/Core/Pagination/Tests/ContentSearchHitAdapterTest.php rename to tests/lib/Pagination/ContentSearchHitAdapterTest.php index 758614b106..08d887faa5 100644 --- a/eZ/Publish/Core/Pagination/Tests/ContentSearchHitAdapterTest.php +++ b/tests/lib/Pagination/ContentSearchHitAdapterTest.php @@ -4,18 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Pagination\Tests; - -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\Content as APIContent; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResultCollection; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use eZ\Publish\Core\Pagination\Pagerfanta\ContentSearchHitAdapter; +namespace Ibexa\Tests\Core\Pagination; + +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Core\Pagination\Pagerfanta\ContentSearchHitAdapter; use PHPUnit\Framework\TestCase; class ContentSearchHitAdapterTest extends TestCase @@ -30,7 +30,7 @@ class ContentSearchHitAdapterTest extends TestCase private const EXAMPLE_RESULT_MAX_SCORE = 5.123; private const EXAMPLE_RESULT_TIME = 30.0; - /** @var \eZ\Publish\API\Repository\SearchService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\SearchService|\PHPUnit\Framework\MockObject\MockObject */ protected $searchService; protected function setUp(): void @@ -42,11 +42,11 @@ protected function setUp(): void /** * Returns the adapter to test. * - * @param \eZ\Publish\API\Repository\Values\Content\Query $query - * @param \eZ\Publish\API\Repository\SearchService $searchService + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query $query + * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService * @param array $languageFilter * - * @return \eZ\Publish\Core\Pagination\Pagerfanta\ContentSearchHitAdapter + * @return \Ibexa\Core\Pagination\Pagerfanta\ContentSearchHitAdapter */ protected function getAdapter(Query $query, SearchService $searchService, array $languageFilter = []) { @@ -184,3 +184,5 @@ private function createTestQuery(int $limit = 25, int $offset = 0): Query return $query; } } + +class_alias(ContentSearchHitAdapterTest::class, 'eZ\Publish\Core\Pagination\Tests\ContentSearchHitAdapterTest'); diff --git a/tests/lib/Pagination/FixedSearchResultHitAdapterTest.php b/tests/lib/Pagination/FixedSearchResultHitAdapterTest.php new file mode 100644 index 0000000000..370b5c046a --- /dev/null +++ b/tests/lib/Pagination/FixedSearchResultHitAdapterTest.php @@ -0,0 +1,49 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Pagination; + +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Core\Pagination\Pagerfanta\FixedSearchResultHitAdapter; +use PHPUnit\Framework\TestCase; + +final class FixedSearchResultHitAdapterTest extends TestCase +{ + public function testFixedSearchResultHitAdapter(): void + { + $searchResult = $this->createExampleSearchResult(); + + $adapter = new FixedSearchResultHitAdapter($searchResult); + + $this->assertEquals($searchResult->totalCount, $adapter->getNbResults()); + $this->assertEquals($searchResult->searchHits, $adapter->getSlice(0, 10)); + $this->assertSame($searchResult->getAggregations(), $adapter->getAggregations()); + $this->assertEquals($searchResult->maxScore, $adapter->getMaxScore()); + $this->assertEquals($searchResult->time, $adapter->getTime()); + $this->assertEquals($searchResult->timedOut, $adapter->getTimedOut()); + } + + private function createExampleSearchResult(): SearchResult + { + $searchResult = new SearchResult(); + $searchResult->totalCount = 3; + $searchResult->searchHits = [ + new SearchHit(), + new SearchHit(), + new SearchHit(), + ]; + $searchResult->timedOut = true; + $searchResult->time = 30; + $searchResult->maxScore = 5.234; + + return $searchResult; + } +} + +class_alias(FixedSearchResultHitAdapterTest::class, 'eZ\Publish\Core\Pagination\Tests\FixedSearchResultHitAdapterTest'); diff --git a/tests/lib/Pagination/LocationFilteringAdapterTest.php b/tests/lib/Pagination/LocationFilteringAdapterTest.php new file mode 100644 index 0000000000..50acd16af9 --- /dev/null +++ b/tests/lib/Pagination/LocationFilteringAdapterTest.php @@ -0,0 +1,103 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Pagination; + +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationList; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use Ibexa\Core\Pagination\Pagerfanta\LocationFilteringAdapter; +use Ibexa\Tests\Core\Search\TestCase; + +final class LocationFilteringAdapterTest extends TestCase +{ + private const EXAMPLE_LANGUAGE_FILTER = [ + 'languages' => ['eng-GB', 'pol-PL'], + 'useAlwaysAvailable' => true, + ]; + + /** @var \Ibexa\Contracts\Core\Repository\LocationService|\PHPUnit\Framework\MockObject\MockObject */ + private $locationService; + + protected function setUp(): void + { + $this->locationService = $this->createMock(LocationService::class); + } + + public function testGetNbResults(): void + { + $expectedNumberOfItems = 10; + + $filter = new Filter(); + $filter->sliceBy(5, 0); + + // Make sure that count query doesn't fetch results + $this->locationService + ->expects(self::never()) + ->method('find'); + + $this->locationService + ->method('count') + ->with( + $filter, + self::EXAMPLE_LANGUAGE_FILTER + ) + ->willReturn($expectedNumberOfItems); + + $adapter = new LocationFilteringAdapter( + $this->locationService, + $filter, + self::EXAMPLE_LANGUAGE_FILTER + ); + + self::assertEquals( + $expectedNumberOfItems, + $adapter->getNbResults() + ); + } + + public function testGetSlice(): void + { + $expectedContentList = $this->createExpectedLocationList(10); + + $filter = new Filter(); + $filter->sliceBy(20, 10); + + $this->locationService + ->method('find') + ->with($filter, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($expectedContentList); + + $adapter = new LocationFilteringAdapter( + $this->locationService, + $filter, + self::EXAMPLE_LANGUAGE_FILTER + ); + + $this->assertEquals( + $expectedContentList, + $adapter->getSlice(10, 20) + ); + } + + private function createExpectedLocationList(int $numberOfItems): LocationList + { + $items = []; + for ($i = 0; $i < $numberOfItems; ++$i) { + $items[] = $this->createMock(Location::class); + } + + return new LocationList([ + 'totalCount' => $numberOfItems, + 'locations' => $items, + ]); + } +} + +class_alias(LocationFilteringAdapterTest::class, 'eZ\Publish\Core\Pagination\Tests\LocationFilteringAdapterTest'); diff --git a/tests/lib/Pagination/LocationSearchAdapterTest.php b/tests/lib/Pagination/LocationSearchAdapterTest.php new file mode 100644 index 0000000000..e9c282c6db --- /dev/null +++ b/tests/lib/Pagination/LocationSearchAdapterTest.php @@ -0,0 +1,46 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Pagination; + +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Core\Pagination\Pagerfanta\LocationSearchAdapter; + +class LocationSearchAdapterTest extends LocationSearchHitAdapterTest +{ + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query + * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService + * + * @return \Ibexa\Core\Pagination\Pagerfanta\LocationSearchAdapter + */ + protected function getAdapter(LocationQuery $query, SearchService $searchService, array $languageFilter = []) + { + return new LocationSearchAdapter($query, $searchService, $languageFilter); + } + + /** + * Returns expected result from adapter from search hits. + * + * @param $hits + * + * @return mixed + */ + protected function getExpectedFinalResultFromHits($hits) + { + $expectedResult = []; + + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit[] $hits */ + foreach ($hits as $hit) { + $expectedResult[] = $hit->valueObject; + } + + return $expectedResult; + } +} + +class_alias(LocationSearchAdapterTest::class, 'eZ\Publish\Core\Pagination\Tests\LocationSearchAdapterTest'); diff --git a/eZ/Publish/Core/Pagination/Tests/LocationSearchHitAdapterTest.php b/tests/lib/Pagination/LocationSearchHitAdapterTest.php similarity index 82% rename from eZ/Publish/Core/Pagination/Tests/LocationSearchHitAdapterTest.php rename to tests/lib/Pagination/LocationSearchHitAdapterTest.php index 35969f2b1e..9a6a8381f7 100644 --- a/eZ/Publish/Core/Pagination/Tests/LocationSearchHitAdapterTest.php +++ b/tests/lib/Pagination/LocationSearchHitAdapterTest.php @@ -4,19 +4,19 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Pagination\Tests; - -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\Location as APILocation; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Aggregation; -use eZ\Publish\API\Repository\Values\Content\Query\CriterionInterface; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResultCollection; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use eZ\Publish\Core\Pagination\Pagerfanta\LocationSearchHitAdapter; +namespace Ibexa\Tests\Core\Pagination; + +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\Location as APILocation; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\CriterionInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Core\Pagination\Pagerfanta\LocationSearchHitAdapter; use PHPUnit\Framework\TestCase; class LocationSearchHitAdapterTest extends TestCase @@ -31,7 +31,7 @@ class LocationSearchHitAdapterTest extends TestCase private const EXAMPLE_RESULT_MAX_SCORE = 5.123; private const EXAMPLE_RESULT_TIME = 30.0; - /** @var \eZ\Publish\API\Repository\SearchService|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\SearchService|\PHPUnit\Framework\MockObject\MockObject */ protected $searchService; protected function setUp(): void @@ -43,11 +43,11 @@ protected function setUp(): void /** * Returns the adapter to test. * - * @param \eZ\Publish\API\Repository\Values\Content\LocationQuery $query - * @param \eZ\Publish\API\Repository\SearchService $searchService + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery $query + * @param \Ibexa\Contracts\Core\Repository\SearchService $searchService * @param array $languageFilter * - * @return \eZ\Publish\Core\Pagination\Pagerfanta\LocationSearchHitAdapter + * @return \Ibexa\Core\Pagination\Pagerfanta\LocationSearchHitAdapter */ protected function getAdapter(LocationQuery $query, SearchService $searchService, array $languageFilter = []) { @@ -188,3 +188,5 @@ private function createTestQuery(int $limit = 25, int $offset = 0): LocationQuer return $query; } } + +class_alias(LocationSearchHitAdapterTest::class, 'eZ\Publish\Core\Pagination\Tests\LocationSearchHitAdapterTest'); diff --git a/eZ/Publish/Core/Pagination/Tests/PagerfantaTest.php b/tests/lib/Pagination/PagerfantaTest.php similarity index 77% rename from eZ/Publish/Core/Pagination/Tests/PagerfantaTest.php rename to tests/lib/Pagination/PagerfantaTest.php index 3dd499bd92..f990370b76 100644 --- a/eZ/Publish/Core/Pagination/Tests/PagerfantaTest.php +++ b/tests/lib/Pagination/PagerfantaTest.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Pagination\Tests; +namespace Ibexa\Tests\Core\Pagination; -use eZ\Publish\API\Repository\Values\Content\Search\AggregationResultCollection; -use eZ\Publish\Core\Pagination\Pagerfanta\Pagerfanta; -use eZ\Publish\Core\Pagination\Pagerfanta\SearchResultAdapter; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\AggregationResultCollection; +use Ibexa\Core\Pagination\Pagerfanta\Pagerfanta; +use Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter; use PHPUnit\Framework\TestCase; final class PagerfantaTest extends TestCase @@ -18,10 +18,10 @@ final class PagerfantaTest extends TestCase private const EXAMPLE_TIME_RESULT = 30.0; private const EXAMPLE_MAX_SCORE_RESULT = 5.12354; - /** @var \eZ\Publish\Core\Pagination\Pagerfanta\SearchResultAdapter|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\Pagination\Pagerfanta\SearchResultAdapter|\PHPUnit\Framework\MockObject\MockObject */ private $adapter; - /** @var \eZ\Publish\Core\Pagination\Pagerfanta\Pagerfanta */ + /** @var \Ibexa\Core\Pagination\Pagerfanta\Pagerfanta */ private $pagerfanta; protected function setUp(): void @@ -71,3 +71,5 @@ public function testGetMaxScore(): void ); } } + +class_alias(PagerfantaTest::class, 'eZ\Publish\Core\Pagination\Tests\PagerfantaTest'); diff --git a/tests/lib/Persistence/Cache/AbstractBaseHandlerTest.php b/tests/lib/Persistence/Cache/AbstractBaseHandlerTest.php new file mode 100644 index 0000000000..8577ef7117 --- /dev/null +++ b/tests/lib/Persistence/Cache/AbstractBaseHandlerTest.php @@ -0,0 +1,191 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence\Handler; +use Ibexa\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter; +use Ibexa\Core\Persistence\Cache\BookmarkHandler as CacheBookmarkHandler; +use Ibexa\Core\Persistence\Cache\CacheIndicesValidatorInterface; +use Ibexa\Core\Persistence\Cache\ContentHandler as CacheContentHandler; +use Ibexa\Core\Persistence\Cache\ContentLanguageHandler as CacheContentLanguageHandler; +use Ibexa\Core\Persistence\Cache\ContentTypeHandler as CacheContentTypeHandler; +use Ibexa\Core\Persistence\Cache\Handler as CacheHandler; +use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface; +use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierSanitizer; +use Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache; +use Ibexa\Core\Persistence\Cache\LocationHandler as CacheLocationHandler; +use Ibexa\Core\Persistence\Cache\LocationPathConverter; +use Ibexa\Core\Persistence\Cache\NotificationHandler as CacheNotificationHandler; +use Ibexa\Core\Persistence\Cache\ObjectStateHandler as CacheObjectStateHandler; +use Ibexa\Core\Persistence\Cache\PersistenceLogger; +use Ibexa\Core\Persistence\Cache\SectionHandler as CacheSectionHandler; +use Ibexa\Core\Persistence\Cache\SettingHandler as CacheSettingHandler; +use Ibexa\Core\Persistence\Cache\TransactionHandler as CacheTransactionHandler; +use Ibexa\Core\Persistence\Cache\TrashHandler as CacheTrashHandler; +use Ibexa\Core\Persistence\Cache\UrlAliasHandler as CacheUrlAliasHandler; +use Ibexa\Core\Persistence\Cache\URLHandler as CacheUrlHandler; +use Ibexa\Core\Persistence\Cache\UrlWildcardHandler as CacheUrlWildcardHandler; +use Ibexa\Core\Persistence\Cache\UserHandler as CacheUserHandler; +use Ibexa\Core\Persistence\Cache\UserPreferenceHandler as CacheUserPreferenceHandler; +use PHPUnit\Framework\TestCase; +use Symfony\Component\Cache\CacheItem; + +/** + * Abstract test case for spi cache impl. + */ +abstract class AbstractBaseHandlerTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter|\PHPUnit\Framework\MockObject\MockObject */ + protected $cacheMock; + + /** @var \Ibexa\Contracts\Core\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject */ + protected $persistenceHandlerMock; + + /** @var \Ibexa\Core\Persistence\Cache\Handler */ + protected $persistenceCacheHandler; + + /** @var \Ibexa\Core\Persistence\Cache\PersistenceLogger|\PHPUnit\Framework\MockObject\MockObject */ + protected $loggerMock; + + /** @var \Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache|\PHPUnit\Framework\MockObject\MockObject */ + protected $inMemoryMock; + + /** @var \Closure */ + protected $cacheItemsClosure; + + /** @var \Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface|\PHPUnit\Framework\MockObject\MockObject */ + protected $cacheIdentifierGeneratorMock; + + /** @var \Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierSanitizer */ + protected $cacheIdentifierSanitizer; + + /** @var \Ibexa\Core\Persistence\Cache\LocationPathConverter */ + protected $locationPathConverter; + + /** @var \Ibexa\Core\Persistence\Cache\CacheIndicesValidatorInterface */ + protected $cacheIndicesValidator; + + /** + * Setup the HandlerTest. + */ + protected function setUp(): void + { + parent::setUp(); + + $this->persistenceHandlerMock = $this->createMock(Handler::class); + $this->cacheMock = $this->createMock(TransactionalInMemoryCacheAdapter::class); + $this->loggerMock = $this->createMock(PersistenceLogger::class); + $this->inMemoryMock = $this->createMock(InMemoryCache::class); + $this->cacheIdentifierGeneratorMock = $this->createMock(CacheIdentifierGeneratorInterface::class); + $this->cacheIdentifierSanitizer = new CacheIdentifierSanitizer(); + $this->locationPathConverter = new LocationPathConverter(); + $this->cacheIndicesValidator = $this->createMock(CacheIndicesValidatorInterface::class); + + $cacheAbstractHandlerArguments = $this->provideAbstractCacheHandlerArguments(); + $cacheInMemoryHandlerArguments = $this->provideInMemoryCacheHandlerArguments(); + + $this->persistenceCacheHandler = new CacheHandler( + $this->persistenceHandlerMock, + new CacheSectionHandler(...$cacheAbstractHandlerArguments), + new CacheLocationHandler(...$cacheInMemoryHandlerArguments), + new CacheContentHandler(...$cacheInMemoryHandlerArguments), + new CacheContentLanguageHandler(...$cacheInMemoryHandlerArguments), + new CacheContentTypeHandler(...$cacheInMemoryHandlerArguments), + new CacheUserHandler(...$cacheInMemoryHandlerArguments), + new CacheTransactionHandler(...$cacheInMemoryHandlerArguments), + new CacheTrashHandler(...$cacheAbstractHandlerArguments), + new CacheUrlAliasHandler(...$cacheInMemoryHandlerArguments), + new CacheObjectStateHandler(...$cacheInMemoryHandlerArguments), + new CacheUrlHandler(...$cacheAbstractHandlerArguments), + new CacheBookmarkHandler(...$cacheAbstractHandlerArguments), + new CacheNotificationHandler(...$cacheAbstractHandlerArguments), + new CacheUserPreferenceHandler(...$cacheInMemoryHandlerArguments), + new CacheUrlWildcardHandler(...$cacheAbstractHandlerArguments), + new CacheSettingHandler(...$cacheInMemoryHandlerArguments), + $this->loggerMock + ); + + $this->cacheItemsClosure = \Closure::bind( + static function ($key, $value, $isHit, $defaultLifetime = 0) { + $item = new CacheItem(); + $item->key = $key; + $item->value = $value; + $item->isHit = $isHit; + $item->defaultLifetime = $defaultLifetime; + $item->isTaggable = true; + + return $item; + }, + null, + CacheItem::class + ); + } + + /** + * Tear down test (properties). + */ + protected function tearDown(): void + { + unset( + $this->cacheMock, + $this->persistenceHandlerMock, + $this->persistenceCacheHandler, + $this->loggerMock, + $this->cacheItemsClosure, + $this->inMemoryMock, + $this->cacheIdentifierGeneratorMock, + $this->cacheIdentifierSanitizer, + $this->locationPathConverter + ); + + parent::tearDown(); + } + + /** + * @param $key + * @param null $value If null the cache item will be assumed to be a cache miss here. + * @param int $defaultLifetime + * + * @return \Symfony\Component\Cache\CacheItem + */ + final protected function getCacheItem($key, $value = null, $defaultLifetime = 0) + { + $cacheItemsClosure = $this->cacheItemsClosure; + + return $cacheItemsClosure($key, $value, (bool)$value, $defaultLifetime); + } + + private function provideAbstractCacheHandlerArguments(): array + { + return [ + $this->cacheMock, + $this->persistenceHandlerMock, + $this->loggerMock, + $this->cacheIdentifierGeneratorMock, + $this->cacheIdentifierSanitizer, + $this->locationPathConverter, + ]; + } + + private function provideInMemoryCacheHandlerArguments(): array + { + return [ + $this->cacheMock, + $this->loggerMock, + $this->inMemoryMock, + $this->persistenceHandlerMock, + $this->cacheIdentifierGeneratorMock, + $this->cacheIdentifierSanitizer, + $this->locationPathConverter, + $this->cacheIndicesValidator, + ]; + } +} + +class_alias(AbstractBaseHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\AbstractBaseHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/AbstractCacheHandlerTest.php b/tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php similarity index 96% rename from eZ/Publish/Core/Persistence/Cache/Tests/AbstractCacheHandlerTest.php rename to tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php index b357e62d33..4bdd590fb6 100644 --- a/eZ/Publish/Core/Persistence/Cache/Tests/AbstractCacheHandlerTest.php +++ b/tests/lib/Persistence/Cache/AbstractCacheHandlerTest.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache\Tests; +namespace Ibexa\Tests\Core\Persistence\Cache; /** * Abstract test case for spi cache impl. @@ -219,7 +219,11 @@ final public function testLoadMethodsCacheMiss( $cacheItem = $this->getCacheItem($key, null); $handlerMethodName = $this->getHandlerMethodName(); - $this->loggerMock->expects($this->once())->method('logCall'); + $handler = $this->persistenceCacheHandler->$handlerMethodName(); + $this->loggerMock + ->expects(self::once()) + ->method('logCall') + ->with(get_class($handler) . '::' . $method, self::isType('array')); if ($tagGeneratingArguments) { $this->cacheIdentifierGeneratorMock @@ -281,7 +285,6 @@ final public function testLoadMethodsCacheMiss( ->method('save') ->with($cacheItem); - $handler = $this->persistenceCacheHandler->$handlerMethodName(); $return = call_user_func_array([$handler, $method], $arguments); $this->assertEquals($data, $return); @@ -290,3 +293,5 @@ final public function testLoadMethodsCacheMiss( //$this->assertAttributeEquals([], 'tags', $cacheItem); } } + +class_alias(AbstractCacheHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\AbstractCacheHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/AbstractInMemoryCacheHandlerTest.php b/tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php similarity index 98% rename from eZ/Publish/Core/Persistence/Cache/Tests/AbstractInMemoryCacheHandlerTest.php rename to tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php index a0e4d24809..87083bf4ec 100644 --- a/eZ/Publish/Core/Persistence/Cache/Tests/AbstractInMemoryCacheHandlerTest.php +++ b/tests/lib/Persistence/Cache/AbstractInMemoryCacheHandlerTest.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache\Tests; +namespace Ibexa\Tests\Core\Persistence\Cache; /** * Abstract test case for spi cache impl, with in-memory handling. @@ -294,3 +294,5 @@ final public function testLoadMethodsCacheMiss( //$this->assertAttributeEquals([], 'tags', $cacheItem); } } + +class_alias(AbstractInMemoryCacheHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\AbstractInMemoryCacheHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/Adapter/InMemoryClearingProxyAdapterTest.php b/tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php similarity index 92% rename from eZ/Publish/Core/Persistence/Cache/Tests/Adapter/InMemoryClearingProxyAdapterTest.php rename to tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php index 75b842e301..9fe7ef6d52 100644 --- a/eZ/Publish/Core/Persistence/Cache/Tests/Adapter/InMemoryClearingProxyAdapterTest.php +++ b/tests/lib/Persistence/Cache/Adapter/InMemoryClearingProxyAdapterTest.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache\Tests\Adapter; +namespace Ibexa\Tests\Core\Persistence\Cache\Adapter; -use eZ\Publish\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter; -use eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache; +use Ibexa\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter; +use Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache; use PHPUnit\Framework\TestCase; use Symfony\Component\Cache\Adapter\TagAwareAdapterInterface; use Symfony\Component\Cache\CacheItem; @@ -19,13 +19,13 @@ */ class InMemoryClearingProxyAdapterTest extends TestCase { - /** @var \eZ\Publish\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter */ + /** @var \Ibexa\Core\Persistence\Cache\Adapter\TransactionalInMemoryCacheAdapter */ protected $cache; /** @var \Symfony\Component\Cache\Adapter\TagAwareAdapterInterface|\PHPUnit\Framework\MockObject\MockObject */ protected $innerPool; - /** @var \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache|\PHPUnit\Framework\MockObject\MockObject */ protected $inMemory; /** @var \Closure */ @@ -236,3 +236,5 @@ private function arrayAsGenerator(array $array) } } } + +class_alias(InMemoryClearingProxyAdapterTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\Adapter\InMemoryClearingProxyAdapterTest'); diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/BookmarkHandlerTest.php b/tests/lib/Persistence/Cache/BookmarkHandlerTest.php similarity index 87% rename from eZ/Publish/Core/Persistence/Cache/Tests/BookmarkHandlerTest.php rename to tests/lib/Persistence/Cache/BookmarkHandlerTest.php index cbcb692108..1a195ca41f 100644 --- a/eZ/Publish/Core/Persistence/Cache/Tests/BookmarkHandlerTest.php +++ b/tests/lib/Persistence/Cache/BookmarkHandlerTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache\Tests; +namespace Ibexa\Tests\Core\Persistence\Cache; -use eZ\Publish\SPI\Persistence\Bookmark\Bookmark; -use eZ\Publish\SPI\Persistence\Bookmark\CreateStruct; -use eZ\Publish\SPI\Persistence\Bookmark\Handler as SPIBookmarkHandler; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Location\Handler as SPILocationHandler; +use Ibexa\Contracts\Core\Persistence\Bookmark\Bookmark; +use Ibexa\Contracts\Core\Persistence\Bookmark\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Bookmark\Handler as SPIBookmarkHandler; +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as SPILocationHandler; /** * Test case for Persistence\Cache\BookmarkHandler. @@ -103,3 +103,5 @@ public function providerForCachedLoadMethodsMiss(): array ]; } } + +class_alias(BookmarkHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\BookmarkHandlerTest'); diff --git a/tests/lib/Persistence/Cache/ContentHandlerTest.php b/tests/lib/Persistence/Cache/ContentHandlerTest.php new file mode 100644 index 0000000000..c7c43308fa --- /dev/null +++ b/tests/lib/Persistence/Cache/ContentHandlerTest.php @@ -0,0 +1,474 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Handler as SPIContentHandler; +use Ibexa\Contracts\Core\Persistence\Content\MetadataUpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Relation; +use Ibexa\Contracts\Core\Persistence\Content\Relation as SPIRelation; +use Ibexa\Contracts\Core\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\UpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation as APIRelation; +use Ibexa\Core\Persistence\Cache\ContentHandler; + +/** + * @covers \Ibexa\Core\Persistence\Cache\ContentHandler + */ +class ContentHandlerTest extends AbstractInMemoryCacheHandlerTest +{ + public function getHandlerMethodName(): string + { + return 'contentHandler'; + } + + public function getHandlerClassName(): string + { + return SPIContentHandler::class; + } + + /** + * @return array + */ + public function providerForUnCachedMethods(): array + { + // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue + return [ + ['create', [new CreateStruct()]], + ['createDraftFromVersion', [2, 1, 14], null, [['content_version_list', [2], true]], [], ['ibx-c-2-vl']], + ['copy', [2, 1]], + ['loadDraftsForUser', [14]], + ['setStatus', [2, 0, 1], [['content_version', [2, 1], false]], null, ['c-2-v-1']], + ['setStatus', [2, 1, 1], [['content', [2], false]], null, ['c-2']], + ['updateMetadata', [2, new MetadataUpdateStruct()], [['content', [2], false]], null, ['c-2']], + ['updateContent', [2, 1, new UpdateStruct()], [['content_version', [2, 1], false]], null, ['c-2-v-1']], + //['deleteContent', [2]], own tests for relations complexity + ['deleteVersion', [2, 1], [['content_version', [2, 1], false]], null, ['c-2-v-1']], + ['addRelation', [new RelationCreateStruct(['destinationContentId' => 2])], [['content', [2], false]], null, ['c-2']], + ['removeRelation', [66, APIRelation::COMMON, 2], [['content', [2], false], ['relation', [66], false]], null, ['c-2', 're-66']], + ['loadRelations', [2, 1, 3]], + ['loadReverseRelations', [2, 3]], + ['publish', [2, 3, new MetadataUpdateStruct()], [['content', [2], false]], null, ['c-2']], + [ + 'listVersions', + [2, 1], + [['content', [2], false]], + [['content_version_list', [2], true]], + [], + [], + [ + new VersionInfo([ + 'versionNo' => 1, + 'contentInfo' => new ContentInfo([ + 'id' => 2, + ]), + ]), + ], + ], + ]; + } + + /** + * @return array + */ + public function providerForCachedLoadMethodsHit(): array + { + $info = new ContentInfo(['id' => 2]); + $version = new VersionInfo(['versionNo' => 1, 'contentInfo' => $info]); + $content = new Content(['fields' => [], 'versionInfo' => $version]); + $relation = new Relation(); + $relation->id = 1; + $relation->sourceContentId = 2; + $relation->sourceContentVersionNo = 2; + $relation->destinationContentId = 1; + $relation->type = 1; + $relationList[1] = $relation; + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi = false, array $additionalCalls + return [ + ['countReverseRelations', [2], 'ibx-crrc-2', null, null, [['content_reverse_relations_count', [2], true]], ['ibx-crrc-2'], 10], + ['countRelations', [2], 'ibx-crc-2-v--t-', null, null, [['content_relations_count_with_by_version_type_suffix', [2, null, null], true]], ['ibx-crc-2-v--t-'], 10], + ['countRelations', [2, 2], 'ibx-crc-2-v-2-t-', null, null, [['content_relations_count_with_by_version_type_suffix', [2, 2, null], true]], ['ibx-crc-2-v-2-t-'], 10], + ['countRelations', [2, null, 1], 'ibx-crc-2-v--t-1', null, null, [['content_relations_count_with_by_version_type_suffix', [2, null, 1], true]], ['ibx-crc-2-v--t-1'], 10], + ['countRelations', [2, 2, 1], 'ibx-crc-2-v-2-t-1', null, null, [['content_relations_count_with_by_version_type_suffix', [2, 2, 1], true]], ['ibx-crc-2-v-2-t-1'], 10], + ['loadRelationList', [2, 1, 0], 'ibx-crl-2-l-1-o-0-v--t-', null, null, [['content_relations_list_with_by_version_type_suffix', [2, 1, 0, null, null], true]], ['ibx-crl-2-l-1-o-0-v--t-'], $relationList], + ['loadRelationList', [2, 1, 0, 2], 'ibx-crl-2-l-1-o-0-v-2-t-', null, null, [['content_relations_list_with_by_version_type_suffix', [2, 1, 0, 2, null], true]], ['ibx-crl-2-l-1-o-0-v-2-t-'], $relationList], + ['loadRelationList', [2, 1, 0, null, 1], 'ibx-crl-2-l-1-o-0-v--t-1', null, null, [['content_relations_list_with_by_version_type_suffix', [2, 1, 0, null, 1], true]], ['ibx-crl-2-l-1-o-0-v--t-1'], $relationList], + ['loadRelationList', [2, 1, 0, 2, 1], 'ibx-crl-2-l-1-o-0-v-2-t-1', null, null, [['content_relations_list_with_by_version_type_suffix', [2, 1, 0, 2, 1], true]], ['ibx-crl-2-l-1-o-0-v-2-t-1'], $relationList], + ['load', [2, 1], 'ibx-c-2-1-' . ContentHandler::ALL_TRANSLATIONS_KEY, null, null, [['content', [], true]], ['ibx-c'], $content], + ['load', [2, 1, ['eng-GB', 'eng-US']], 'ibx-c-2-1-eng-GB|eng-US', null, null, [['content', [], true]], ['ibx-c'], $content], + ['load', [2], 'ibx-c-2-' . ContentHandler::ALL_TRANSLATIONS_KEY, null, null, [['content', [], true]], ['ibx-c'], $content], + ['load', [2, null, ['eng-GB', 'eng-US']], 'ibx-c-2-eng-GB|eng-US', null, null, [['content', [], true]], ['ibx-c'], $content], + ['loadContentList', [[2]], 'ibx-c-2-' . ContentHandler::ALL_TRANSLATIONS_KEY, null, null, [['content', [], true]], ['ibx-c'], [2 => $content], true], + ['loadContentList', [[5], ['eng-GB', 'eng-US']], 'ibx-c-5-eng-GB|eng-US', null, null, [['content', [], true]], ['ibx-c'], [5 => $content], true], + ['loadContentInfo', [2], 'ibx-ci-2', null, null, [['content_info', [], true]], ['ibx-ci'], $info], + ['loadContentInfoList', [[2]], 'ibx-ci-2', null, null, [['content_info', [], true]], ['ibx-ci'], [2 => $info], true], + ['loadContentInfoByRemoteId', ['3d8jrj'], 'ibx-cibri-3d8jrj', null, null, [['content_info_by_remote_id', [], true]], ['ibx-cibri'], $info], + ['loadRelation', [66], 'ibx-re-66', null, null, [['relation', [66], true]], ['ibx-re-66'], new SPIRelation()], + ['loadVersionInfo', [2, 1], 'ibx-cvi-2-1', null, null, [['content_version_info', [2], true]], ['ibx-cvi-2'], $version], + ['loadVersionInfo', [2], 'ibx-cvi-2', null, null, [['content_version_info', [2], true]], ['ibx-cvi-2'], $version], + ['listVersions', [2], 'ibx-c-2-vl', null, null, [['content_version_list', [2], true]], ['ibx-c-2-vl'], [$version]], + ['loadVersionInfoList', [[2]], 'ibx-cvi-2', null, null, [['content_version_info', [], true]], ['ibx-cvi'], [2 => $version], true], + ]; + } + + /** + * @return array + */ + public function providerForCachedLoadMethodsMiss(): array + { + $info = new ContentInfo([ + 'id' => 2, + 'contentTypeId' => 3, + ]); + $version = new VersionInfo(['versionNo' => 1, 'contentInfo' => $info]); + $content = new Content(['fields' => [], 'versionInfo' => $version]); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi = false, array $additionalCalls + return [ + [ + 'countReverseRelations', + [2], + 'ibx-crrc-2', + [ + ['content', [2], false], + ], + ['c-2'], + [ + ['content_reverse_relations_count', [2], true], + ], + ['ibx-crrc-2'], + 10, + ], + [ + 'countRelations', + [2], + 'ibx-crc-2-v--t-', + [ + ['content', [2], false], + ], + ['c-2'], + [ + ['content_relations_count_with_by_version_type_suffix', [2, null, null], true], + ], + ['ibx-crc-2-v--t-'], + 10, + ], + [ + 'countRelations', + [2, 3], + 'ibx-crc-2-v-3-t-', + [ + ['content', [2], false], + ], + ['c-2'], + [ + ['content_relations_count_with_by_version_type_suffix', [2, 3, null], true], + ], + ['ibx-crc-2-v-3-t-'], + 10, + ], + [ + 'countRelations', + [2, null, 1], + 'ibx-crc-2-v--t-1', + [ + ['content', [2], false], + ], + ['c-2'], + [ + ['content_relations_count_with_by_version_type_suffix', [2, null, 1], true], + ], + ['ibx-crc-2-v--t-1'], + 10, + ], + [ + 'countRelations', + [2, 3, 1], + 'ibx-crc-2-v-3-t-', + [ + ['content', [2], false], + ], + ['c-2'], + [ + ['content_relations_count_with_by_version_type_suffix', [2, 3, 1], true], + ], + ['ibx-crc-2-v-3-t-'], + 10, + ], + [ + 'load', + [2, 1], + 'ibx-c-2-1-' . ContentHandler::ALL_TRANSLATIONS_KEY, + [ + ['content_fields_type', [3], false], + ['content_version', [2, 1], false], + ['content', [2], false], + ], + ['cft-3', 'c-2-v-1', 'c-2'], + [ + ['content', [], true], + ], + ['ibx-c'], + $content, + ], + [ + 'load', + [2, 1, ['eng-GB', 'eng-US']], + 'ibx-c-2-1-eng-GB|eng-US', + [ + ['content_fields_type', [3], false], + ['content_version', [2, 1], false], + ['content', [2], false], + ], + ['cft-2', 'c-2-v-1', 'c-2'], + [ + ['content', [], true], + ], + ['ibx-c'], + $content, + ], + [ + 'load', + [2], + 'ibx-c-2-' . ContentHandler::ALL_TRANSLATIONS_KEY, + [ + ['content_fields_type', [3], false], + ['content_version', [2, 1], false], + ['content', [2], false], + ], + ['cft-2', 'c-2-v-1', 'c-2'], + [ + ['content', [], true], + ], + ['ibx-c'], + $content, + ], + [ + 'load', + [2, null, ['eng-GB', 'eng-US']], + 'ibx-c-2-eng-GB|eng-US', + [ + ['content_fields_type', [3], false], + ['content_version', [2, 1], false], + ['content', [2], false], + ], + ['cft-2', 'c-2-v-1', 'c-2'], + [ + ['content', [], true], + ], + ['ibx-c'], + $content, + ], + [ + 'loadContentList', + [[2]], + 'ibx-c-2-' . ContentHandler::ALL_TRANSLATIONS_KEY, + [ + ['content_fields_type', [3], false], + ['content_version', [2, 1], false], + ['content', [2], false], + ], + ['cft-2', 'c-2-v-1', 'c-2'], + [ + ['content', [], true], + ], + ['ibx-c'], + [2 => $content], + true, + ], + [ + 'loadContentList', + [[5], ['eng-GB', 'eng-US']], + 'ibx-c-5-eng-GB|eng-US', + [ + ['content_fields_type', [3], false], + ['content_version', [2, 1], false], + ['content', [2], false], + ], + ['cft-2', 'c-2-v-1', 'c-2'], + [ + ['content', [], true], + ], + ['ibx-c'], + [5 => $content], + true, + ], + [ + 'loadContentInfo', + [2], + 'ibx-ci-2', + [ + ['content', [2], false], + ], + ['c-2'], + [ + ['content_info', [], true], + ], + ['ibx-ci'], + $info, + ], + [ + 'loadContentInfoList', + [[2]], + 'ibx-ci-2', + [ + ['content', [2], false], + ], + ['c-2'], + [ + ['content_info', [], true], + ], + ['ibx-ci'], + [2 => $info], + true, + ], + [ + 'loadContentInfoByRemoteId', + ['3d8jrj'], 'ibx-cibri-3d8jrj', + [ + ['content', [2], false], + ], + ['c-2'], + [ + ['content_info_by_remote_id', [], true], + ], + ['ibx-cibri'], + $info, + ], + [ + 'loadVersionInfo', + [2, 1], + 'ibx-cvi-2-1', + [ + ['content_version', [2, 1], false], + ['content', [2], false], + ], + ['c-2-v-1', 'c-2'], + [ + ['content_version_info', [2], true], + ], + ['ibx-cvi-2'], + $version, + ], + [ + 'loadVersionInfo', + [2], + 'ibx-cvi-2', + [ + ['content_version', [2, 1], false], + ['content', [2], false], + ], + ['c-2-v-1', 'c-2'], + [ + ['content_version_info', [2], true], + ], + ['ibx-cvi-2'], + $version, + ], + [ + 'listVersions', + [2], + 'ibx-c-2-vl', + [ + ['content', [2], false], + ['content_version', [2, 1], false], + ['content', [2], false], + ], + ['c-2', 'c-2-v-1', 'c-2'], + [ + ['content_version_list', [2], true], + ], + ['ibx-c-2-vl'], + [$version], + ], + [ + 'loadRelation', + [66], + 'ibx-re-66', + [ + ['relation', [66], false], + ], + ['re-66'], + [ + ['relation', [66], true], + ], + ['ibx-re-66'], + new SPIRelation(), + ], + [ + 'loadVersionInfoList', + [[2]], + 'ibx-cvi-2', + [ + ['content_version', [2, 1], false], + ['content', [2], false], + ], + ['c-2-v-1', 'c-2'], + [ + ['content_version_info', [], true], + ], + ['ibx-cvi'], + [2 => $version], + true, + ], + ]; + } + + public function testDeleteContent() + { + $this->loggerMock->expects($this->once())->method('logCall'); + + $innerHandlerMock = $this->createMock(SPIContentHandler::class); + $this->persistenceHandlerMock + ->expects($this->exactly(2)) + ->method('contentHandler') + ->willReturn($innerHandlerMock); + + $innerHandlerMock + ->expects($this->once()) + ->method('loadReverseRelations') + ->with(2, APIRelation::FIELD | APIRelation::ASSET) + ->willReturn( + [ + new SPIRelation(['sourceContentId' => 42]), + ] + ); + + $innerHandlerMock + ->expects($this->once()) + ->method('deleteContent') + ->with(2) + ->willReturn(true); + + $this->cacheMock + ->expects($this->never()) + ->method('deleteItem'); + + $this->cacheIdentifierGeneratorMock + ->expects($this->exactly(2)) + ->method('generateTag') + ->withConsecutive( + ['content', [42], false], + ['content', [2], false] + ) + ->willReturnOnConsecutiveCalls('c-42', 'c-2'); + + $this->cacheMock + ->expects($this->once()) + ->method('invalidateTags') + ->with(['c-42', 'c-2']); + + $handler = $this->persistenceCacheHandler->contentHandler(); + $handler->deleteContent(2); + } +} + +class_alias(ContentHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\ContentHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/ContentLanguageHandlerTest.php b/tests/lib/Persistence/Cache/ContentLanguageHandlerTest.php similarity index 92% rename from eZ/Publish/Core/Persistence/Cache/Tests/ContentLanguageHandlerTest.php rename to tests/lib/Persistence/Cache/ContentLanguageHandlerTest.php index 5247d50b56..c618f6d6ed 100644 --- a/eZ/Publish/Core/Persistence/Cache/Tests/ContentLanguageHandlerTest.php +++ b/tests/lib/Persistence/Cache/ContentLanguageHandlerTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; +namespace Ibexa\Tests\Core\Persistence\Cache; -use eZ\Publish\SPI\Persistence\Content\Language as SPILanguage; -use eZ\Publish\SPI\Persistence\Content\Language\CreateStruct as SPILanguageCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Language\Handler; +use Ibexa\Contracts\Core\Persistence\Content\Language as SPILanguage; +use Ibexa\Contracts\Core\Persistence\Content\Language\CreateStruct as SPILanguageCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler; /** * Test case for Persistence\Cache\ContentLanguageHandler. @@ -161,3 +161,5 @@ public function providerForCachedLoadMethodsMiss(): array ]; } } + +class_alias(ContentLanguageHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\ContentLanguageHandlerTest'); diff --git a/tests/lib/Persistence/Cache/ContentTypeHandlerTest.php b/tests/lib/Persistence/Cache/ContentTypeHandlerTest.php new file mode 100644 index 0000000000..6b113e3fb8 --- /dev/null +++ b/tests/lib/Persistence/Cache/ContentTypeHandlerTest.php @@ -0,0 +1,478 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence\Content\Type as SPIType; +use Ibexa\Contracts\Core\Persistence\Content\Type\CreateStruct as SPITypeCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as SPITypeFieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\Type\Group as SPITypeGroup; +use Ibexa\Contracts\Core\Persistence\Content\Type\Group\CreateStruct as SPITypeGroupCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Type\Group\UpdateStruct as SPITypeGroupUpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as SPIContentTypeHandler; +use Ibexa\Contracts\Core\Persistence\Content\Type\UpdateStruct as SPITypeUpdateStruct; + +/** + * Test case for Persistence\Cache\ContentTypeHandler. + */ +class ContentTypeHandlerTest extends AbstractInMemoryCacheHandlerTest +{ + public function getHandlerMethodName(): string + { + return 'contentTypeHandler'; + } + + public function getHandlerClassName(): string + { + return SPIContentTypeHandler::class; + } + + /** + * @return array + */ + public function providerForUnCachedMethods(): array + { + $groupUpdate = new SPITypeGroupUpdateStruct(['id' => 3, 'identifier' => 'media']); + $typeUpdate = new SPITypeUpdateStruct(['identifier' => 'article', 'remoteId' => '34o9tj8394t']); + + // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue + return [ + ['createGroup', [new SPITypeGroupCreateStruct()], null, [['content_type_group_list', [], true]], null, ['ibx-ctgl']], + [ + 'updateGroup', + [$groupUpdate], + null, + [ + ['content_type_group_list', [], true], + ['content_type_group', [3], true], + ['content_type_group_with_id_suffix', ['media'], true], + ], + null, + ['ibx-ctgl', 'ibx-ctg-3', 'ibx-ctg-media-bi'], + ], + ['deleteGroup', [3], [['type_group', [3], false]], null, ['tg-3']], + ['loadContentTypes', [3, 1]], // also listed for cached cases in providerForCachedLoadMethods + ['load', [5, 1]], // also listed for cached case in providerForCachedLoadMethods + [ + 'create', + [new SPITypeCreateStruct(['groupIds' => [2, 3]])], + null, + [ + ['content_type_list_by_group', [2], true], + ['content_type_list_by_group', [3], true], + ], + null, + ['ibx-ctlbg-2', 'ibx-ctlbg-3'], + ], + [ + 'update', + [5, 0, $typeUpdate], + [ + ['type', [5], false], + ['type_map', [], false], + ['content_fields_type', [5], false], + ], + null, + ['t-5', 'tm', 'cft-5'], + ], + ['update', [5, 1, $typeUpdate]], + [ + 'delete', + [5, 0], + [ + ['type', [5], false], + ['type_map', [], false], + ['content_fields_type', [5], false], + ], + null, + ['t-5', 'tm', 'cft-5'], + ], + ['delete', [5, 1]], + ['createDraft', [10, 5]], + [ + 'copy', + [10, 5, 0], + null, + [ + ['content_type_list_by_group', [1], true], + ['content_type_list_by_group', [2], true], + ], + null, + ['ibx-ctlbg-1', 'ibx-ctlbg-2'], + new SPIType(['groupIds' => [1, 2]]), + ], + ['copy', [10, 5, 1], null, [['content_type_list_by_group', [3], true]], null, ['ibx-ctlbg-3'], new SPIType(['groupIds' => [3]])], + ['unlink', [3, 5, 0], [['type', [5], false]], null, ['t-5']], + ['unlink', [3, 5, 1]], + [ + 'link', + [3, 5, 0], + [ + ['type', [5], false], + ], + [ + ['content_type_list_by_group', [3], true], + ], + ['t-5'], + ['ibx-ctlbg-3'], + ], + ['link', [3, 5, 1]], + ['getFieldDefinition', [7, 1]], + ['getFieldDefinition', [7, 0]], + ['getContentCount', [5]], + [ + 'addFieldDefinition', + [5, 0, new SPITypeFieldDefinition()], + [ + ['type', [5], false], + ['type_map', [], false], + ['content_fields_type', [5], false], + ], + null, + ['t-5', 'tm', 'cft-5'], + ], + ['addFieldDefinition', [5, 1, new SPITypeFieldDefinition()]], + [ + 'removeFieldDefinition', + [5, 0, new SPITypeFieldDefinition(['id' => 7])], + [ + ['type', [5], false], + ['type_map', [], false], + ['content_fields_type', [5], false], + ], + null, + ['t-5', 'tm', 'cft-5'], + ], + ['removeFieldDefinition', [5, 1, new SPITypeFieldDefinition(['id' => 7])]], + [ + 'updateFieldDefinition', + [5, 0, new SPITypeFieldDefinition()], + [ + ['type', [5], false], + ['type_map', [], false], + ['content_fields_type', [5], false], + ], + null, + ['t-5', 'tm', 'cft-5'], + ], + ['updateFieldDefinition', [5, 1, new SPITypeFieldDefinition()]], + [ + 'removeContentTypeTranslation', + [5, 'eng-GB'], + [ + ['type', [5], false], + ['type_map', [], false], + ['content_fields_type', [5], false], + ], + null, + ['t-5', 'tm', 'cft-5'], + null, + new SPIType(), + ], + ['deleteByUserAndStatus', [12, 0], [['type_without_value', [], false]], null, ['t']], + ['deleteByUserAndStatus', [12, 1]], + ]; + } + + /** + * @return array + */ + public function providerForCachedLoadMethodsHit(): array + { + $group = new SPITypeGroup(['id' => 3, 'identifier' => 'media']); + $type = new SPIType(['id' => 5, 'identifier' => 'article', 'remoteId' => '34o9tj8394t']); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + ['loadGroup', [3], 'ibx-ctg-3', null, null, [['content_type_group', [], true]], ['ibx-ctg'], $group], + ['loadGroups', [[3]], 'ibx-ctg-3', null, null, [['content_type_group', [], true]], ['ibx-ctg'], [3 => $group], true], + [ + 'loadGroupByIdentifier', + ['content'], + 'ibx-ctg-content-bi', + null, + null, + [ + ['content_type_group', [], true], + ['by_identifier_suffix', [], false], + ], + ['ibx-ctg', 'bi'], + $group, + ], + ['loadAllGroups', [], 'ibx-ctgl', null, null, [['content_type_group_list', [], true]], ['ibx-ctgl'], [3 => $group]], + ['loadContentTypes', [3, 0], 'ibx-ctlbg-3', null, null, [['content_type_list_by_group', [3], true]], ['ibx-ctlbg-3'], [$type]], + ['loadContentTypesByFieldDefinitionIdentifier', [3, 0], 'ibx-ctlbfdi-3', null, null, [['content_type_list_by_field_definition_identifier', [3], true]], ['ibx-ctlbfdi-3'], [$type]], + ['loadContentTypeList', [[5]], 'ibx-ct-5', null, null, [['content_type', [], true]], ['ibx-ct'], [5 => $type], true], + ['load', [5, 0], 'ibx-ct-5', null, null, [['content_type', [], true]], ['ibx-ct'], $type], + [ + 'loadByIdentifier', + ['article'], + 'ibx-ct-article-bi', + null, + null, + [ + ['content_type', [], true], + ['by_identifier_suffix', [], false], + ], + ['ibx-ct', 'bi'], + $type, + ], + [ + 'loadByRemoteId', + ['f34tg45gf'], + 'ibx-ct-f34tg45gf-br', + null, + null, + [ + ['content_type', [], true], + ['by_remote_suffix', [], false], + ], + ['ibx-ct', 'br'], + $type, + ], + ['getSearchableFieldMap', [], 'ibx-ctfm', null, null, [['content_type_field_map', [], true]], ['ibx-ctfm'], [$type]], + ]; + } + + public function providerForCachedLoadMethodsMiss(): array + { + $group = new SPITypeGroup(['id' => 3, 'identifier' => 'media']); + $type = new SPIType(['id' => 5, 'identifier' => 'article', 'remoteId' => '34o9tj8394t']); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + [ + 'loadGroup', + [3], + 'ibx-ctg-3', + [ + ['type_group', [3], false], + ], + ['tg-3'], + [ + ['content_type_group', [], true], + ], + ['ibx-ctg'], + $group, + ], + [ + 'loadGroups', + [[3]], + 'ibx-ctg-3', + [ + ['type_group', [3], false], + ], + ['tg-3'], + [ + ['content_type_group', [], true], + ], + ['ibx-ctg'], + [3 => $group], + true, + ], + [ + 'loadGroupByIdentifier', + ['content'], + 'ibx-ctg-content-bi', + [ + ['type_group', [3], false], + ], + ['tg-3'], + [ + ['content_type_group', [], true], + ['by_identifier_suffix', [], false], + ], + ['ibx-ctg', 'bi'], + $group, + ], + [ + 'loadAllGroups', + [], + 'ibx-ctgl', + [ + ['type_group', [3], false], + ], + ['tg-3'], + [ + ['content_type_group_list', [], true], + ], + ['ibx-ctgl'], + [3 => $group], + ], + [ + 'loadContentTypes', + [3, 0], + 'ibx-ctlbg-3', + [ + ['type_group', [3], false], + ['type', [], false], + ['type', [5], false], + ], + ['tg-3', 't', 't-5'], + [ + ['content_type_list_by_group', [3], true], + ], + ['ibx-ctlbg-3'], + [$type], + ], + [ + 'loadContentTypeList', + [[5]], + 'ibx-ct-5', + [ + ['type', [], false], + ['type', [5], false], + ], + ['t-3', 't-5'], + [ + ['content_type', [], true], + ], + ['ibx-ct'], + [5 => $type], + true, + ], + [ + 'load', + [5, 0], + 'ibx-ct-5', + [ + ['type', [], false], + ['type', [5], false], + ], + ['t-3', 't-5'], + [ + ['content_type', [], true], + ], + ['ibx-ct'], + $type, + ], + [ + 'loadByIdentifier', + ['article'], + 'ibx-ct-article-bi', + [ + ['type', [], false], + ['type', [5], false], + ], + ['t', 't-5'], + [ + ['content_type', [], true], + ['by_identifier_suffix', [], false], + ], + ['ibx-ct', 'bi'], + $type, + ], + [ + 'loadByRemoteId', + ['f34tg45gf'], + 'ibx-ct-f34tg45gf-br', + [ + ['type', [], false], + ['type', [5], false], + ], + ['t', 't-5'], + [ + ['content_type', [], true], + ['by_remote_suffix', [], false], + ], + ['ibx-ct', 'br'], + $type, + ], + [ + 'getSearchableFieldMap', + [], + 'ibx-ctfm', + [ + ['type_map', [], false], + ], + ['tm'], + [ + ['content_type_field_map', [], true], + ], + ['ibx-ctfm'], + [$type], + ], + ]; + } + + /** + * Test cache invalidation when publishing Content type. + */ + public function testPublish() + { + $tags = ['t-5', 'tm', 'cft-5']; + $method = 'publish'; + $arguments = [5]; + $type = new SPIType(['id' => 5, 'groupIds' => [3, 4]]); + $cacheItem = $this->getCacheItem('ibx-ct-5', $type); + + $handlerMethodName = $this->getHandlerMethodName(); + + $this->loggerMock->expects($this->once())->method('logCall'); + + $innerHandler = $this->createMock($this->getHandlerClassName()); + $this->persistenceHandlerMock + ->expects($this->once()) + ->method($handlerMethodName) + ->willReturn($innerHandler); + + $innerHandler + ->expects($this->once()) + ->method($method) + ->with(...$arguments) + ->willReturn(null); + + $this->cacheIdentifierGeneratorMock + ->expects($this->exactly(3)) + ->method('generateTag') + ->withConsecutive( + ['type', [5], false], + ['type_map', [], false], + ['content_fields_type', [5], false] + ) + ->willReturnOnConsecutiveCalls( + 't-5', + 'tm', + 'cft-5' + ); + + $this->cacheIdentifierGeneratorMock + ->expects($this->exactly(3)) + ->method('generateKey') + ->withConsecutive( + ['content_type', [], true], + ['content_type_list_by_group', [3], true], + ['content_type_list_by_group', [4], true] + ) + ->willReturnOnConsecutiveCalls( + 'ibx-ct', + 'ibx-ctlbg-3', + 'ibx-ctlbg-4' + ); + + $this->cacheMock + ->expects(!empty($tags) ? $this->once() : $this->never()) + ->method('invalidateTags') + ->with($tags); + + $this->cacheMock + ->expects($this->once()) + ->method('getItem') + ->with($cacheItem->getKey()) + ->willReturn($cacheItem); + + $this->cacheMock + ->expects($this->once()) + ->method('deleteItems') + ->with(['ibx-ctlbg-3', 'ibx-ctlbg-4']) + ->willReturn(true); + + $handler = $this->persistenceCacheHandler->$handlerMethodName(); + call_user_func_array([$handler, $method], $arguments); + } +} + +class_alias(ContentTypeHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\ContentTypeHandlerTest'); diff --git a/tests/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorTest.php b/tests/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorTest.php index d9912c0523..895a7c9cc8 100644 --- a/tests/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorTest.php +++ b/tests/lib/Persistence/Cache/Identifier/CacheIdentifierGeneratorTest.php @@ -8,7 +8,7 @@ namespace Ibexa\Tests\Core\Persistence\Cache\Identifier; -use eZ\Publish\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGenerator; use PHPUnit\Framework\TestCase; diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/InMemory/InMemoryCacheTest.php b/tests/lib/Persistence/Cache/InMemory/InMemoryCacheTest.php similarity index 95% rename from eZ/Publish/Core/Persistence/Cache/Tests/InMemory/InMemoryCacheTest.php rename to tests/lib/Persistence/Cache/InMemory/InMemoryCacheTest.php index 9cf83632bb..2f9d26e936 100644 --- a/eZ/Publish/Core/Persistence/Cache/Tests/InMemory/InMemoryCacheTest.php +++ b/tests/lib/Persistence/Cache/InMemory/InMemoryCacheTest.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache\Tests\InMemory; +namespace Ibexa\Tests\Core\Persistence\Cache\InMemory; -use eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache; +use Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache; use PHPUnit\Framework\TestCase; /** @@ -16,7 +16,7 @@ */ class InMemoryCacheTest extends TestCase { - /** @var \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache */ + /** @var \Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache */ protected $cache; /** @@ -193,8 +193,9 @@ public function testAccessCountsWhenReachingTotalLimit(): void $this->assertSame($obj, $this->cache->get('eight')); } } +class_alias(InMemoryCacheTest::class, 'eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCacheTest'); -namespace eZ\Publish\Core\Persistence\Cache\InMemory; +namespace Ibexa\Core\Persistence\Cache\InMemory; /** * Overloads microtime(true) calls in InMemoryCache in order to be able to test expiry. diff --git a/tests/lib/Persistence/Cache/LocationHandlerTest.php b/tests/lib/Persistence/Cache/LocationHandlerTest.php new file mode 100644 index 0000000000..ed498aa495 --- /dev/null +++ b/tests/lib/Persistence/Cache/LocationHandlerTest.php @@ -0,0 +1,323 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\Location\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as SPILocationHandler; +use Ibexa\Contracts\Core\Persistence\Content\Location\UpdateStruct; + +/** + * Test case for Persistence\Cache\LocationHandler. + */ +class LocationHandlerTest extends AbstractInMemoryCacheHandlerTest +{ + public function getHandlerMethodName(): string + { + return 'locationHandler'; + } + + public function getHandlerClassName(): string + { + return SPILocationHandler::class; + } + + public function providerForUnCachedMethods(): array + { + // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue + return [ + ['copySubtree', [12, 45]], + ['move', [12, 45], [['location_path', [12], false]], null, ['lp-12']], + ['markSubtreeModified', [12]], + ['hide', [12], [['location_path', [12], false]], null, ['lp-12']], + ['unHide', [12], [['location_path', [12], false]], null, ['lp-12']], + [ + 'swap', + [12, 45], + [ + ['location', [12], false], + ['location', [45], false], + ], + null, + ['l-12', 'l-45'], + ], + ['update', [new UpdateStruct(), 12], [['location', [12], false]], null, ['l-12']], + [ + 'create', + [new CreateStruct(['contentId' => 4, 'mainLocationId' => true])], + [ + ['content', [4], false], + ['role_assignment_group_list', [4], false], + ], + null, + ['c-4', 'ragl-4'], + ], + [ + 'create', + [new CreateStruct(['contentId' => 4, 'mainLocationId' => false])], + [ + ['content', [4], false], + ['role_assignment_group_list', [4], false], + ], + null, + ['c-4', 'ragl-4'], + ], + ['removeSubtree', [12], [['location_path', [12], false]], null, ['lp-12']], + ['setSectionForSubtree', [12, 2], [['location_path', [12], false]], null, ['lp-12']], + ['changeMainLocation', [4, 12], [['content', [4], false]], null, ['c-4']], + ['countLocationsByContent', [4]], + ]; + } + + public function providerForCachedLoadMethodsHit(): array + { + $location = new Location(['id' => 12]); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + ['load', [12], 'ibx-l-12-1', null, null, [['location', [], true]], ['ibx-l'], $location], + ['load', [12, ['eng-GB', 'bra-PG'], false], 'ibx-l-12-bra-PG|eng-GB|0', null, null, [['location', [], true]], ['ibx-l'], $location], + ['loadList', [[12]], 'ibx-l-12-1', null, null, [['location', [], true]], ['ibx-l'], [12 => $location], true], + ['loadList', [[12], ['eng-GB', 'bra-PG'], false], 'ibx-l-12-bra-PG|eng-GB|0', null, null, [['location', [], true]], ['ibx-l'], [12 => $location], true], + ['loadSubtreeIds', [12], 'ibx-ls-12', null, null, [['location_subtree', [], true]], ['ibx-ls'], [33, 44]], + [ + 'loadLocationsByContent', + [4, 12], + 'ibx-cl-4-root-12', + [ + ['content', [4], false], + ['location', [12], false], + ['location_path', [12], false], + ], + ['c-4', 'l-12', 'lp-12'], + [ + ['content_locations', [], true], + ], + ['ibx-cl'], + [$location], + ], + [ + 'loadLocationsByContent', + [4], + 'ibx-cl-4', + [ + ['content', [4], false], + ], + ['c-4'], + [ + ['content_locations', [], true], + ], + ['ibx-cl'], + [$location], + ], + [ + 'loadParentLocationsForDraftContent', + [4], + 'ibx-cl-4-pfd', + null, + null, + [ + ['content_locations', [], true], + ['parent_for_draft_suffix', [], false], + ], + ['ibx-cl', '-pfd'], + [$location], + ], + ['loadByRemoteId', ['34fe5y4'], 'ibx-lri-34fe5y4-1', null, null, [['location_remote_id', [], true]], ['ibx-lri'], $location], + ['loadByRemoteId', ['34fe5y4', ['eng-GB', 'arg-ES']], 'ibx-lri-34fe5y4-arg-ES|eng-GB|1', null, null, [['location_remote_id', [], true]], ['ibx-lri'], $location], + ]; + } + + public function providerForCachedLoadMethodsMiss(): array + { + $location = new Location( + [ + 'id' => 12, + 'contentId' => 15, + 'pathString' => '/1/2', + ] + ); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + [ + 'load', + [12], + 'ibx-l-12-1', + [ + ['content', [15], false], + ['location', [12], false], + ['location_path', ['2'], false], + ], + ['c-15', 'l-12', 'lp-2'], + [ + ['location', [], true], + ], + ['ibx-l'], + $location, + ], + [ + 'load', + [12, ['eng-GB', 'bra-PG'], false], + 'ibx-l-12-bra-PG|eng-GB|0', + [ + ['content', [15], false], + ['location', [12], false], + ['location_path', ['2'], false], + ], + ['c-15', 'l-12', 'lp-2'], + [ + ['location', [], true], + ], + ['ibx-l'], + $location, + ], + [ + 'loadList', + [[12]], + 'ibx-l-12-1', + [ + ['content', [15], false], + ['location', [12], false], + ['location_path', ['2'], false], + ], + ['c-15', 'l-12', 'lp-2'], + [ + ['location', [], true], + ], + ['ibx-l'], + [12 => $location], + true, + ], + [ + 'loadList', + [[12], + ['eng-GB', 'bra-PG'], false, ], + 'ibx-l-12-bra-PG|eng-GB|0', + [ + ['content', [15], false], + ['location', [12], false], + ['location_path', ['2'], false], + ], + ['c-15', 'l-12', 'lp-2'], + [ + ['location', [], true], + ], + ['ibx-l'], + [12 => $location], + true, + ], + [ + 'loadSubtreeIds', + [12], + 'ibx-ls-12', + [ + ['location', [12], false], + ['location_path', [12], false], + ['location', [33], false], + ['location_path', [33], false], + ['location', [44], false], + ['location_path', [44], false], + ], + ['l-12', 'lp-12', 'l-33', 'lp-33', 'l-44', 'lp-44'], + [ + ['location_subtree', [], true], + ], + ['ibx-ls'], + [33, 44], + ], + [ + 'loadLocationsByContent', + [4, 12], + 'ibx-cl-4-root-12', + [ + ['content', [4], false], + ['location', [12], false], + ['location_path', [12], false], + ['content', [15], false], + ['location', [12], false], + ['location_path', ['2'], false], + ], + ['c-4', 'l-12', 'lp-12', 'c-15', 'l-12', 'lp-2'], + [ + ['content_locations', [], true], + ], + ['ibx-cl'], + [$location], + ], + [ + 'loadLocationsByContent', + [4], + 'ibx-cl-4', + [ + ['content', [4], false], + ['content', [15], false], + ['location', [12], false], + ['location_path', ['2'], false], + ], + ['c-4', 'c-15', 'l-12', 'lp-2'], + [ + ['content_locations', [], true], + ], + ['ibx-cl'], + [$location], + ], + [ + 'loadParentLocationsForDraftContent', + [4], + 'ibx-cl-4-pfd', + [ + ['content', [4], false], + ['content', [15], false], + ['location', [12], false], + ['location_path', ['2'], false], + ], + ['c-4', 'c-15', 'l-12', 'lp-2'], + [ + ['content_locations', [], true], + ['parent_for_draft_suffix', [], false], + ], + ['ibx-cl', '-pfd'], + [$location], + ], + [ + 'loadByRemoteId', + ['34fe5y4'], + 'ibx-lri-34fe5y4-1', + [ + ['content', [15], false], + ['location', [12], false], + ['location_path', ['2'], false], + ], + ['c-15', 'l-12', 'lp-2'], + [ + ['location_remote_id', [], true], + ], + ['ibx-lri'], + $location, + ], + [ + 'loadByRemoteId', + ['34fe5y4', ['eng-GB', 'arg-ES']], + 'ibx-lri-34fe5y4-arg-ES|eng-GB|1', + [ + ['content', [15], false], + ['location', [12], false], + ['location_path', ['2'], false], + ], + ['c-15', 'l-12', 'lp-2'], + [ + ['location_remote_id', [], true], + ], + ['ibx-lri'], + $location, + ], + ]; + } +} + +class_alias(LocationHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\LocationHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/NotificationHandlerTest.php b/tests/lib/Persistence/Cache/NotificationHandlerTest.php similarity index 92% rename from eZ/Publish/Core/Persistence/Cache/Tests/NotificationHandlerTest.php rename to tests/lib/Persistence/Cache/NotificationHandlerTest.php index 050dc0166c..60e514d8bc 100644 --- a/eZ/Publish/Core/Persistence/Cache/Tests/NotificationHandlerTest.php +++ b/tests/lib/Persistence/Cache/NotificationHandlerTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache\Tests; +namespace Ibexa\Tests\Core\Persistence\Cache; -use eZ\Publish\API\Repository\Values\Notification\Notification; -use eZ\Publish\SPI\Persistence\Notification\CreateStruct; -use eZ\Publish\SPI\Persistence\Notification\Handler as SPINotificationHandler; -use eZ\Publish\SPI\Persistence\Notification\Notification as SPINotification; -use eZ\Publish\SPI\Persistence\Notification\UpdateStruct; +use Ibexa\Contracts\Core\Persistence\Notification\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Notification\Handler as SPINotificationHandler; +use Ibexa\Contracts\Core\Persistence\Notification\Notification as SPINotification; +use Ibexa\Contracts\Core\Persistence\Notification\UpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Notification\Notification; /** * Test case for Persistence\Cache\NotificationHandler. @@ -207,3 +207,5 @@ public function providerForCachedLoadMethodsMiss(): array ]; } } + +class_alias(NotificationHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\NotificationHandlerTest'); diff --git a/tests/lib/Persistence/Cache/ObjectStateHandlerTest.php b/tests/lib/Persistence/Cache/ObjectStateHandlerTest.php new file mode 100644 index 0000000000..dbbf5034b5 --- /dev/null +++ b/tests/lib/Persistence/Cache/ObjectStateHandlerTest.php @@ -0,0 +1,216 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence\Content\ObjectState as SPIObjectState; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group as SPIObjectStateGroup; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\Handler as SPIObjectStateHandler; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct as SPIInputStruct; + +/** + * Test case for Persistence\Cache\ObjectStateHandler. + */ +class ObjectStateHandlerTest extends AbstractCacheHandlerTest +{ + public function getHandlerMethodName(): string + { + return 'objectStateHandler'; + } + + public function getHandlerClassName(): string + { + return SPIObjectStateHandler::class; + } + + public function providerForUnCachedMethods(): array + { + // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue + return [ + ['createGroup', [new SPIInputStruct()], null, [['state_group_all', [], true]], null, 'ibx-sga'], + ['updateGroup', [5, new SPIInputStruct()], [['state_group', [5], false]], null, ['sg-5']], + ['deleteGroup', [5], [['state_group', [5], false]], null, ['sg-5']], + ['create', [5, new SPIInputStruct()], null, [['state_list_by_group', [5], true]], [], 'ibx-slbg-5'], + ['update', [7, new SPIInputStruct()], [['state', [7], false]], null, ['s-7']], + ['setPriority', [7, 99], [['state', [7], false]], null, ['s-7']], + ['delete', [7], [['state', [7], false]], null, ['s-7']], + ['setContentState', [4, 5, 7], null, [['state_by_group_on_content', [5, 4], true]], [], 'ibx-sbg-5-oc-4'], + ]; + } + + public function providerForCachedLoadMethodsHit(): array + { + $group = new SPIObjectStateGroup(['id' => 5]); + $state = new SPIObjectState(['id' => 7]); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + ['loadGroup', [5], 'ibx-sg-5', null, null, [['state_group', [], true]], ['ibx-sg'], $group], + [ + 'loadGroupByIdentifier', + ['lock'], + 'ibx-sg-lock-bi', + null, + null, + [ + ['state_group', [], true], + ['by_identifier_suffix', [], false], + ], + ['ibx-sg', '-bi'], + $group, + ], + ['loadAllGroups', [], 'ibx-sga', null, null, [['state_group_all', [], true]], ['ibx-sga'], [$group]], + ['loadObjectStates', [5], 'ibx-slbg-5', null, null, [['state_list_by_group', [], true]], ['ibx-slbg'], [$state]], + ['load', [7], 'ibx-s-7', null, null, [['state', [], true]], ['ibx-s'], $state], + [ + 'loadByIdentifier', + ['lock', 5], + 'ibx-si-lock-bg-5', + null, + null, + [ + ['state_identifier', [], true], + ['by_group', [5], false], + ], + ['ibx-si', 'bg-5'], + $state, + ], + [ + 'getContentState', + [4, 5], + 'ibx-sbg-5-oc-4', + null, + null, + [ + ['state_by_group', [], true], + ['on_content', [4], false], + ], + ['ibx-sbg', 'oc-4'], + $state, + ], + ]; + } + + public function providerForCachedLoadMethodsMiss(): array + { + $group = new SPIObjectStateGroup(['id' => 5]); + $state = new SPIObjectState([ + 'id' => 7, + 'groupId' => 5, + ]); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + [ + 'loadGroup', + [5], + 'ibx-sg-5', + [ + ['state_group', [5], false], + ], + ['sg-5'], + [ + ['state_group', [], true], + ], + ['ibx-sg'], + $group, + ], + [ + 'loadGroupByIdentifier', + ['lock'], + 'ibx-sg-lock-bi', + [ + ['state_group', [5], false], + ], + ['sg-5'], + [ + ['state_group', [], true], + ['by_identifier_suffix', [], false], + ], + ['ibx-sg', '-bi'], + $group, + ], + [ + 'loadAllGroups', + [], + 'ibx-sga', + [ + ['state_group', [5], false], + ], + ['sg-5'], + [ + ['state_group_all', [], true], + ], + ['ibx-sga'], + [$group], + ], + [ + 'loadObjectStates', + [5], + 'ibx-slbg-5', + [ + ['state_group', [5], false], + ['state', [7], false], + ], + ['sg-5', 's-7'], + [ + ['state_list_by_group', [], true], + ], + ['ibx-slbg'], + [$state], + ], + [ + 'load', + [7], + 'ibx-s-7', + [ + ['state', [7], false], + ['state_group', [5], false], + ], + ['s-7', 'sg-5'], + [ + ['state', [], true], + ], + ['ibx-s'], + $state, + ], + [ + 'loadByIdentifier', + ['lock', 5], + 'ibx-si-lock-bg-5', + [ + ['state', [7], false], + ['state_group', [5], false], + ], + ['s-7', 'sg-5'], + [ + ['state_identifier', [], true], + ['by_group', [5], false], + ], + ['ibx-si', 'bg-5'], + $state, + ], + [ + 'getContentState', + [4, 5], + 'ibx-sbg-5-oc-4', + [ + ['state', [7], false], + ['content', [4], false], + ], + ['s-7', 'c-4'], + [ + ['state_by_group', [], true], + ['on_content', [4], false], + ], + ['ibx-sbg', 'oc-4'], + $state, + ], + ]; + } +} + +class_alias(ObjectStateHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\ObjectStateHandlerTest'); diff --git a/tests/lib/Persistence/Cache/PersistenceHandlerTest.php b/tests/lib/Persistence/Cache/PersistenceHandlerTest.php new file mode 100644 index 0000000000..8b379dd5bd --- /dev/null +++ b/tests/lib/Persistence/Cache/PersistenceHandlerTest.php @@ -0,0 +1,114 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence as SPIPersistence; +use Ibexa\Core\Persistence\Cache; + +/** + * Test case for Persistence\Cache\Handler. + * + * @covers \Ibexa\Core\Persistence\Cache\Handler + */ +class PersistenceHandlerTest extends AbstractBaseHandlerTest +{ + public function testHandler() + { + $this->assertInstanceOf(SPIPersistence\Handler::class, $this->persistenceCacheHandler); + $this->assertInstanceOf(Cache\Handler::class, $this->persistenceCacheHandler); + } + + public function testContentHandler() + { + $this->loggerMock->expects($this->never())->method($this->anything()); + $handler = $this->persistenceCacheHandler->contentHandler(); + $this->assertInstanceOf(SPIPersistence\Content\Handler::class, $handler); + $this->assertInstanceOf(Cache\ContentHandler::class, $handler); + } + + public function testLanguageHandler() + { + $this->loggerMock->expects($this->never())->method($this->anything()); + $handler = $this->persistenceCacheHandler->contentLanguageHandler(); + $this->assertInstanceOf(SPIPersistence\Content\Language\Handler::class, $handler); + $this->assertInstanceOf(Cache\ContentLanguageHandler::class, $handler); + } + + public function testContentTypeHandler() + { + $this->loggerMock->expects($this->never())->method($this->anything()); + $handler = $this->persistenceCacheHandler->contentTypeHandler(); + $this->assertInstanceOf(SPIPersistence\Content\Type\Handler::class, $handler); + $this->assertInstanceOf(Cache\ContentTypeHandler::class, $handler); + } + + public function testContentLocationHandler() + { + $this->loggerMock->expects($this->never())->method($this->anything()); + $handler = $this->persistenceCacheHandler->locationHandler(); + $this->assertInstanceOf(SPIPersistence\Content\Location\Handler::class, $handler); + $this->assertInstanceOf(Cache\LocationHandler::class, $handler); + } + + public function testTrashHandler() + { + $this->loggerMock->expects($this->never())->method($this->anything()); + $handler = $this->persistenceCacheHandler->trashHandler(); + $this->assertInstanceOf(SPIPersistence\Content\Location\Trash\Handler::class, $handler); + $this->assertInstanceOf(Cache\TrashHandler::class, $handler); + } + + public function testObjectStateHandler() + { + $this->loggerMock->expects($this->never())->method($this->anything()); + $handler = $this->persistenceCacheHandler->objectStateHandler(); + $this->assertInstanceOf(SPIPersistence\Content\ObjectState\Handler::class, $handler); + $this->assertInstanceOf(Cache\ObjectStateHandler::class, $handler); + } + + public function testSectionHandler() + { + $this->loggerMock->expects($this->never())->method($this->anything()); + $handler = $this->persistenceCacheHandler->sectionHandler(); + $this->assertInstanceOf(SPIPersistence\Content\Section\Handler::class, $handler); + $this->assertInstanceOf(Cache\SectionHandler::class, $handler); + } + + public function testUserHandler() + { + $this->loggerMock->expects($this->never())->method($this->anything()); + $handler = $this->persistenceCacheHandler->userHandler(); + $this->assertInstanceOf(SPIPersistence\User\Handler::class, $handler); + $this->assertInstanceOf(Cache\UserHandler::class, $handler); + } + + public function testUrlAliasHandler() + { + $this->loggerMock->expects($this->never())->method($this->anything()); + $handler = $this->persistenceCacheHandler->urlAliasHandler(); + $this->assertInstanceOf(SPIPersistence\Content\UrlAlias\Handler::class, $handler); + $this->assertInstanceOf(Cache\UrlAliasHandler::class, $handler); + } + + public function testUrlWildcardHandler() + { + $this->loggerMock->expects($this->never())->method($this->anything()); + $handler = $this->persistenceCacheHandler->urlWildcardHandler(); + $this->assertInstanceOf(SPIPersistence\Content\UrlWildcard\Handler::class, $handler); + $this->assertInstanceOf(Cache\UrlWildcardHandler::class, $handler); + } + + public function testTransactionHandler() + { + $this->loggerMock->expects($this->never())->method($this->anything()); + $handler = $this->persistenceCacheHandler->transactionHandler(); + $this->assertInstanceOf(SPIPersistence\TransactionHandler::class, $handler); + $this->assertInstanceOf(Cache\TransactionHandler::class, $handler); + } +} + +class_alias(PersistenceHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\PersistenceHandlerTest'); diff --git a/tests/lib/Persistence/Cache/PersistenceLoggerTest.php b/tests/lib/Persistence/Cache/PersistenceLoggerTest.php new file mode 100644 index 0000000000..23eda2909d --- /dev/null +++ b/tests/lib/Persistence/Cache/PersistenceLoggerTest.php @@ -0,0 +1,100 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Core\Persistence\Cache\PersistenceLogger; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Cache\PersistenceLogger::getName + */ +class PersistenceLoggerTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Cache\PersistenceLogger */ + protected $logger; + + /** + * Setup the HandlerTest. + */ + protected function setUp(): void + { + parent::setUp(); + $this->logger = new PersistenceLogger(); + } + + /** + * Tear down test (properties). + */ + protected function tearDown(): void + { + unset($this->logger); + parent::tearDown(); + } + + public function testGetName() + { + $this->assertEquals(PersistenceLogger::NAME, $this->logger->getName()); + } + + public function testGetCount() + { + $this->assertEquals(0, $this->logger->getCount()); + } + + public function testGetCalls() + { + $this->assertEquals([], $this->logger->getCalls()); + } + + public function testLogCall() + { + $this->assertNull($this->logger->logCall(__METHOD__)); + $this->logger->logCall(__METHOD__); + $this->logger->logCall(__METHOD__); + $this->logger->logCall(__METHOD__, [33]); + + return $this->logger; + } + + /** + * @depends testLogCall + * + * @param \Ibexa\Core\Persistence\Cache\PersistenceLogger $logger + */ + public function testGetCountValues($logger) + { + $this->assertEquals(4, $logger->getCount()); + + return $logger; + } + + /** + * @depends testGetCountValues + * + * @param \Ibexa\Core\Persistence\Cache\PersistenceLogger $logger + */ + public function testGetCallValues($logger) + { + $calls = $logger->getCalls(); + // As we don't care about the hash index we get the array values instead + $calls = array_values($calls); + + $method = __CLASS__ . '::testLogCall'; + + $this->assertEquals($method, $calls[0]['method']); + $this->assertEquals([], $calls[0]['arguments']); + $this->assertCount(1, $calls[0]['traces']); + $this->assertEquals(['uncached' => 3, 'miss' => 0, 'hit' => 0, 'memory' => 0], $calls[0]['stats']); + + $this->assertEquals($method, $calls[1]['method']); + $this->assertEquals([33], $calls[1]['arguments']); + $this->assertCount(1, $calls[1]['traces']); + $this->assertEquals(['uncached' => 1, 'miss' => 0, 'hit' => 0, 'memory' => 0], $calls[1]['stats']); + } +} + +class_alias(PersistenceLoggerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\PersistenceLoggerTest'); diff --git a/tests/lib/Persistence/Cache/SectionHandlerTest.php b/tests/lib/Persistence/Cache/SectionHandlerTest.php new file mode 100644 index 0000000000..012af0783e --- /dev/null +++ b/tests/lib/Persistence/Cache/SectionHandlerTest.php @@ -0,0 +1,91 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence\Content\Section as SPISection; +use Ibexa\Contracts\Core\Persistence\Content\Section\Handler as SPISectionHandler; + +/** + * Test case for Persistence\Cache\SectionHandler. + */ +class SectionHandlerTest extends AbstractCacheHandlerTest +{ + public function getHandlerMethodName(): string + { + return 'sectionHandler'; + } + + public function getHandlerClassName(): string + { + return SPISectionHandler::class; + } + + public function providerForUnCachedMethods(): array + { + // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue + return [ + ['create', ['Standard', 'standard']], + ['update', [5, 'Standard', 'standard'], [['section', [5], false]], null, ['se-5']], + ['loadAll', []], + ['delete', [5], [['section', [5], false]], null, ['se-5']], + ['assign', [5, 42], [['content', [42], false]], null, ['c-42']], + ['assignmentsCount', [5]], + ['policiesCount', [5]], + ['countRoleAssignmentsUsingSection', [5]], + ]; + } + + public function providerForCachedLoadMethodsHit(): array + { + $object = new SPISection(['id' => 5]); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + ['load', [5], 'ibx-se-5', null, null, [['section', [5], true]], ['ibx-se-5'], $object], + ['loadByIdentifier', ['standard'], 'ibx-se-standard-bi', null, null, [['section_with_by_id', ['standard'], true]], ['ibx-se-standard-bi'], $object], + ]; + } + + public function providerForCachedLoadMethodsMiss(): array + { + $object = new SPISection(['id' => 5]); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + [ + 'load', + [5], + 'ibx-se-5', + [ + ['section', [5], false], + ], + ['se-5'], + [ + ['section', [5], true], + ], + ['ibx-se-5'], + $object, + ], + [ + 'loadByIdentifier', + ['standard'], + 'ibx-se-standard-bi', + [ + ['section', [5], false], + ], + ['se-5'], + [ + ['section_with_by_id', ['standard'], true], + ], + ['ibx-se-standard-bi'], + $object, + ], + ]; + } +} + +class_alias(SectionHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\SectionHandlerTest'); diff --git a/tests/lib/Persistence/Cache/SettingHandlerTest.php b/tests/lib/Persistence/Cache/SettingHandlerTest.php new file mode 100644 index 0000000000..96b05914b2 --- /dev/null +++ b/tests/lib/Persistence/Cache/SettingHandlerTest.php @@ -0,0 +1,101 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence\Setting\Handler as SettingHandler; +use Ibexa\Contracts\Core\Persistence\Setting\Setting; + +/** + * Test case for Persistence\Cache\SettingHandler. + */ +class SettingHandlerTest extends AbstractCacheHandlerTest +{ + public function getHandlerMethodName(): string + { + return 'settingHandler'; + } + + public function getHandlerClassName(): string + { + return SettingHandler::class; + } + + public function providerForUnCachedMethods(): array + { + // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue + return [ + [ + 'create', + ['group_a1', 'identifier_b2', 'value_c3'], + null, + null, + null, + null, + new Setting(), + ], + [ + 'update', + ['group_a1', 'identifier_b2', 'update_value_c3'], + [['setting', ['group_a1', 'identifier_b2'], true]], + null, + ['ibx-set-group_a1-identifier_b2'], + null, + new Setting(), + ], + [ + 'delete', + ['group_a1', 'identifier_b2'], + [['setting', ['group_a1', 'identifier_b2'], true]], + null, + ['ibx-set-group_a1-identifier_b2'], + ], + ]; + } + + public function providerForCachedLoadMethodsHit(): array + { + $object = new Setting(['group' => 'group_a1', 'identifier' => 'identifier_b2']); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + [ + 'load', + ['group_a1', 'identifier_b2'], + 'ibx-set-group_a1-identifier_b2', + [['setting', ['group_a1', 'identifier_b2'], true]], + ['ibx-set-group_a1-identifier_b2'], + null, + null, + $object, + ], + ]; + } + + public function providerForCachedLoadMethodsMiss(): array + { + $object = new Setting(['group' => 'group_a1', 'identifier' => 'identifier_b2']); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + [ + 'load', + ['group_a1', 'identifier_b2'], + 'ibx-set-group_a1-identifier_b2', + [ + ['setting', ['group_a1', 'identifier_b2'], true], + ['setting', ['group_a1', 'identifier_b2'], true], + ], + ['ibx-set-group_a1-identifier_b2', 'ibx-set-group_a1-identifier_b2'], + null, + null, + $object, + ], + ]; + } +} + +class_alias(SettingHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\SettingHandlerTest'); diff --git a/tests/lib/Persistence/Cache/TransactionHandlerTest.php b/tests/lib/Persistence/Cache/TransactionHandlerTest.php new file mode 100644 index 0000000000..65eb580861 --- /dev/null +++ b/tests/lib/Persistence/Cache/TransactionHandlerTest.php @@ -0,0 +1,126 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence\TransactionHandler; + +/** + * @covers \Ibexa\Core\Persistence\Cache\TransactionHandler + */ +class TransactionHandlerTest extends AbstractCacheHandlerTest +{ + public function getHandlerMethodName(): string + { + return 'transactionHandler'; + } + + public function getHandlerClassName(): string + { + return TransactionHandler::class; + } + + public function providerForUnCachedMethods(): array + { + // string $method, array $arguments, array $arguments, array? $cacheTagGeneratingArguments, array? $cacheKeyGeneratingArguments, array? $tags, string? $key + return [ + ['beginTransaction', []], + ['commit', []], + ]; + } + + public function providerForCachedLoadMethodsHit(): array + { + // string $method, array $arguments, array? $cacheIdentifierGeneratorArguments, array? $cacheIdentifierGeneratorResults, string $key, mixed? $data + return [ + ]; + } + + public function providerForCachedLoadMethodsMiss(): array + { + // string $method, array $arguments, array? $cacheIdentifierGeneratorArguments, array? $cacheIdentifierGeneratorResults, string $key, mixed? $data + return [ + ]; + } + + public function testRollback() + { + $this->loggerMock + ->expects($this->once()) + ->method('logCall'); + + $this->cacheMock + ->expects($this->never()) + ->method('clear'); + + $this->cacheMock + ->expects($this->once()) + ->method('rollbackTransaction'); + + $innerHandlerMock = $this->createMock(TransactionHandler::class); + $this->persistenceHandlerMock + ->expects($this->once()) + ->method('transactionHandler') + ->willReturn($innerHandlerMock); + + $innerHandlerMock + ->expects($this->once()) + ->method('rollback'); + + $handler = $this->persistenceCacheHandler->transactionHandler(); + $handler->rollback(); + } + + public function testCommitStopsCacheTransaction() + { + $this->loggerMock + ->expects($this->once()) + ->method('logCall'); + + $this->cacheMock + ->expects($this->once()) + ->method('commitTransaction'); + + $innerHandlerMock = $this->createMock(TransactionHandler::class); + $this->persistenceHandlerMock + ->expects($this->once()) + ->method('transactionHandler') + ->willReturn($innerHandlerMock); + + $innerHandlerMock + ->expects($this->once()) + ->method('commit'); + + $handler = $this->persistenceCacheHandler->transactionHandler(); + $handler->commit(); + } + + public function testBeginTransactionStartsCacheTransaction() + { + $this->loggerMock + ->expects($this->once()) + ->method('logCall'); + + $this->cacheMock + ->expects($this->once()) + ->method('beginTransaction'); + + $innerHandlerMock = $this->createMock(TransactionHandler::class); + $this->persistenceHandlerMock + ->expects($this->once()) + ->method('transactionHandler') + ->willReturn($innerHandlerMock); + + $innerHandlerMock + ->expects($this->once()) + ->method('beginTransaction'); + + $handler = $this->persistenceCacheHandler->transactionHandler(); + $handler->beginTransaction(); + } +} + +class_alias(TransactionHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\TransactionHandlerTest'); diff --git a/tests/lib/Persistence/Cache/TrashHandlerTest.php b/tests/lib/Persistence/Cache/TrashHandlerTest.php new file mode 100644 index 0000000000..dcfc8712bd --- /dev/null +++ b/tests/lib/Persistence/Cache/TrashHandlerTest.php @@ -0,0 +1,310 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\Location\Trash\Handler as TrashHandler; +use Ibexa\Contracts\Core\Persistence\Content\Location\Trashed; +use Ibexa\Contracts\Core\Persistence\Content\Relation; +use Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResult; +use Ibexa\Core\Persistence\Cache\ContentHandler; +use Ibexa\Core\Persistence\Cache\LocationHandler; + +/** + * Test case for Persistence\Cache\SectionHandler. + */ +class TrashHandlerTest extends AbstractCacheHandlerTest +{ + public function getHandlerMethodName(): string + { + return 'trashHandler'; + } + + public function getHandlerClassName(): string + { + return TrashHandler::class; + } + + public function providerForUnCachedMethods(): array + { + // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue + return [ + ['loadTrashItem', [6]], + ]; + } + + public function providerForCachedLoadMethodsHit(): array + { + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + ]; + } + + public function providerForCachedLoadMethodsMiss(): array + { + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + ]; + } + + public function testRecover() + { + $originalLocationId = 6; + $targetLocationId = 2; + $contentId = 42; + + $tags = [ + 'c-' . $contentId, + 'lp-' . $originalLocationId, + ]; + + $handlerMethodName = $this->getHandlerMethodName(); + + $this->loggerMock->expects($this->once())->method('logCall'); + + $innerHandler = $this->createMock($this->getHandlerClassName()); + $contentHandlerMock = $this->createMock(ContentHandler::class); + $locationHandlerMock = $this->createMock(LocationHandler::class); + + $locationHandlerMock + ->method('load') + ->willReturn(new Location(['id' => $originalLocationId, 'contentId' => $contentId])); + + $this->persistenceHandlerMock + ->method('contentHandler') + ->willReturn($contentHandlerMock); + + $this->persistenceHandlerMock + ->method('locationHandler') + ->willReturn($locationHandlerMock); + + $this->persistenceHandlerMock + ->expects($this->once()) + ->method($handlerMethodName) + ->willReturn($innerHandler); + + $innerHandler + ->expects($this->once()) + ->method('recover') + ->with($originalLocationId, $targetLocationId) + ->willReturn(null); + + $this->cacheIdentifierGeneratorMock + ->expects($this->exactly(2)) + ->method('generateTag') + ->withConsecutive( + ['content', [$contentId], false], + ['location_path', [$originalLocationId], false] + ) + ->willReturnOnConsecutiveCalls( + 'c-' . $contentId, + 'lp-' . $originalLocationId + ); + + $this->cacheMock + ->expects($this->once()) + ->method('invalidateTags') + ->with($tags); + + $handler = $this->persistenceCacheHandler->$handlerMethodName(); + $handler->recover($originalLocationId, $targetLocationId); + } + + public function testTrashSubtree() + { + $locationId = 6; + $contentId = 42; + + $tags = [ + 'c-' . $contentId, + 'lp-' . $locationId, + ]; + + $handlerMethodName = $this->getHandlerMethodName(); + + $this->loggerMock->expects($this->once())->method('logCall'); + + $innerHandler = $this->createMock($this->getHandlerClassName()); + $contentHandlerMock = $this->createMock(ContentHandler::class); + $locationHandlerMock = $this->createMock(LocationHandler::class); + + $locationHandlerMock + ->method('load') + ->willReturn(new Location(['id' => $locationId, 'contentId' => $contentId])); + + $this->persistenceHandlerMock + ->method('contentHandler') + ->willReturn($contentHandlerMock); + + $this->persistenceHandlerMock + ->method('locationHandler') + ->willReturn($locationHandlerMock); + + $this->persistenceHandlerMock + ->expects($this->once()) + ->method($handlerMethodName) + ->willReturn($innerHandler); + + $innerHandler + ->expects($this->once()) + ->method('trashSubtree') + ->with($locationId) + ->willReturn(null); + + $this->cacheIdentifierGeneratorMock + ->expects($this->exactly(2)) + ->method('generateTag') + ->withConsecutive( + ['content', [$contentId], false], + ['location_path', [$locationId], false] + ) + ->willReturnOnConsecutiveCalls(...$tags); + + $this->cacheMock + ->expects($this->once()) + ->method('invalidateTags') + ->with($tags); + + $handler = $this->persistenceCacheHandler->$handlerMethodName(); + $handler->trashSubtree($locationId); + } + + public function testDeleteTrashItem() + { + $trashedId = 6; + $contentId = 42; + $relationSourceContentId = 44; + + $handlerMethodName = $this->getHandlerMethodName(); + + $innerHandler = $this->createMock($this->getHandlerClassName()); + + $trashed = new Trashed(['id' => $trashedId, 'contentId' => $contentId]); + $innerHandler + ->expects($this->once()) + ->method('deleteTrashItem') + ->with($trashedId) + ->willReturn(new TrashItemDeleteResult(['trashItemId' => $trashedId, 'contentId' => $contentId])); + + $innerHandler + ->expects($this->once()) + ->method('loadTrashItem') + ->with($trashedId) + ->willReturn($trashed); + + $this->persistenceHandlerMock + ->method($handlerMethodName) + ->willReturn($innerHandler); + + $contentHandlerMock = $this->createMock(ContentHandler::class); + + $contentHandlerMock + ->expects($this->once()) + ->method('loadReverseRelations') + ->with($contentId) + ->willReturn([new Relation(['sourceContentId' => $relationSourceContentId])]); + + $this->persistenceHandlerMock + ->method('contentHandler') + ->willReturn($contentHandlerMock); + + $tags = [ + 'c-' . $contentId, + 'lp-' . $trashedId, + 'c-' . $relationSourceContentId, + ]; + + $this->cacheIdentifierGeneratorMock + ->expects($this->exactly(3)) + ->method('generateTag') + ->withConsecutive( + ['content', [$relationSourceContentId], false], + ['content', [$contentId], false], + ['location_path', [$trashedId], false] + ) + ->willReturnOnConsecutiveCalls( + 'c-' . $relationSourceContentId, + 'c-' . $contentId, + 'lp-' . $trashedId + ); + + $this->cacheMock + ->expects($this->once()) + ->method('invalidateTags') + ->with($tags); + + /** @var \Ibexa\Contracts\Core\Persistence\Content\Location\Trash\Handler $handler */ + $handler = $this->persistenceCacheHandler->$handlerMethodName(); + $handler->deleteTrashItem($trashedId); + } + + public function testEmptyTrash() + { + $trashedId = 6; + $contentId = 42; + $relationSourceContentId = 44; + + $handlerMethodName = $this->getHandlerMethodName(); + + $innerHandler = $this->createMock($this->getHandlerClassName()); + + $innerHandler + ->expects($this->exactly(2)) + ->method('findTrashItems') + ->willReturn(new Location\Trash\TrashResult([ + 'items' => [new Trashed(['id' => $trashedId, 'contentId' => $contentId])], + // trigger the bulk loading several times to have some minimal coverage on the loop exit logic + 'totalCount' => 101, + ])); + + $this->persistenceHandlerMock + ->method($handlerMethodName) + ->willReturn($innerHandler); + + $contentHandlerMock = $this->createMock(ContentHandler::class); + + $contentHandlerMock + ->expects($this->exactly(2)) + ->method('loadReverseRelations') + ->with($contentId) + ->willReturn([new Relation(['sourceContentId' => $relationSourceContentId])]); + + $this->persistenceHandlerMock + ->method('contentHandler') + ->willReturn($contentHandlerMock); + + $cacheIdentifierGeneratorArguments = [ + ['content', [$relationSourceContentId], false], + ['content', [$contentId], false], + ['location_path', [$trashedId], false], + ]; + + $tags = [ + 'c-' . $relationSourceContentId, + 'c-' . $contentId, + 'lp-' . $trashedId, + ]; + + //one set of arguments and tags for each relation + $this->cacheIdentifierGeneratorMock + ->expects($this->exactly(6)) + ->method('generateTag') + ->withConsecutive(...array_merge($cacheIdentifierGeneratorArguments, $cacheIdentifierGeneratorArguments)) + ->willReturnOnConsecutiveCalls(...array_merge($tags, $tags)); + + $this->cacheMock + ->expects($this->once()) + ->method('invalidateTags') + ->with($tags); + + /** @var \Ibexa\Contracts\Core\Persistence\Content\Location\Trash\Handler $handler */ + $handler = $this->persistenceCacheHandler->$handlerMethodName(); + $handler->emptyTrash(); + } +} + +class_alias(TrashHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\TrashHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/URLHandlerTest.php b/tests/lib/Persistence/Cache/URLHandlerTest.php similarity index 91% rename from eZ/Publish/Core/Persistence/Cache/Tests/URLHandlerTest.php rename to tests/lib/Persistence/Cache/URLHandlerTest.php index 51bfc2ae7e..34cee6878d 100644 --- a/eZ/Publish/Core/Persistence/Cache/Tests/URLHandlerTest.php +++ b/tests/lib/Persistence/Cache/URLHandlerTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Cache\Tests; +namespace Ibexa\Tests\Core\Persistence\Cache; -use eZ\Publish\API\Repository\Values\URL\URLQuery; -use eZ\Publish\SPI\Persistence\URL\Handler as SpiURLHandler; -use eZ\Publish\SPI\Persistence\URL\URL; -use eZ\Publish\SPI\Persistence\URL\URLUpdateStruct; +use Ibexa\Contracts\Core\Persistence\URL\Handler as SpiURLHandler; +use Ibexa\Contracts\Core\Persistence\URL\URL; +use Ibexa\Contracts\Core\Persistence\URL\URLUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\URL\URLQuery; class URLHandlerTest extends AbstractCacheHandlerTest { @@ -70,7 +70,7 @@ public function testUpdateUrlWhenAddressIsUpdated(): void { $urlId = 1; $updateStruct = new URLUpdateStruct(); - $updateStruct->url = 'http://ez.no'; + $updateStruct->url = 'http://ibexa.co'; $this->loggerMock->expects($this->once())->method('logCall'); @@ -154,3 +154,5 @@ public function testUpdateUrlStatusIsUpdated() $handler->updateUrl($urlId, $updateStruct); } } + +class_alias(URLHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\URLHandlerTest'); diff --git a/tests/lib/Persistence/Cache/UrlAliasHandlerTest.php b/tests/lib/Persistence/Cache/UrlAliasHandlerTest.php new file mode 100644 index 0000000000..f6af94e240 --- /dev/null +++ b/tests/lib/Persistence/Cache/UrlAliasHandlerTest.php @@ -0,0 +1,224 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence\Content\UrlAlias; +use Ibexa\Contracts\Core\Persistence\Content\UrlAlias\Handler as SPIUrlAliasHandler; + +/** + * Test case for Persistence\Cache\UrlAliasHandler. + */ +class UrlAliasHandlerTest extends AbstractInMemoryCacheHandlerTest +{ + public function getHandlerMethodName(): string + { + return 'urlAliasHandler'; + } + + public function getHandlerClassName(): string + { + return SPIUrlAliasHandler::class; + } + + public function providerForUnCachedMethods(): array + { + // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue + return [ + [ + 'publishUrlAliasForLocation', + [44, 2, 'name', 'eng-GB', true, false], + [ + ['url_alias_location', [44], false], + ['url_alias_location_path', [44], false], + ['url_alias', ['44-abc'], false], + ['url_alias_not_found', [], false], + ], + null, + ['urlal-44', 'urlalp-44', 'urla-44-abc', 'urlanf'], + null, + '44-abc', + ], + [ + 'createCustomUrlAlias', + [44, '1/2/44', true, null, false], + [ + ['url_alias_location', [44], false], + ['url_alias_location_path', [44], false], + ['url_alias_not_found', [], false], + ['url_alias', [5], false], + ], + null, + ['urlal-44', 'urlalp-44', 'urlanf', 'urla-5'], + null, + new UrlAlias(['id' => 5]), + ], + ['createGlobalUrlAlias', ['something', '1/2/44', true, null, false], [['url_alias_not_found', [], false]], null, ['urlanf']], + ['createGlobalUrlAlias', ['something', '1/2/44', true, 'eng-GB', false], [['url_alias_not_found', [], false]], null, ['urlanf']], + ['listGlobalURLAliases', ['eng-GB', 10, 50]], + [ + 'removeURLAliases', + [[new UrlAlias(['id' => 5, 'type' => UrlAlias::LOCATION, 'isCustom' => true, 'destination' => 21])]], + [ + ['url_alias', [5], false], + ['url_alias_location', [21], false], + ['url_alias_location_path', [21], false], + ['url_alias_custom', [21], false], + ], + null, + ['urla-5', 'urlal-21', 'urlalp-21', 'urlac-21'], + ], + [ + 'locationMoved', + [21, 45, 12], + [ + ['url_alias_location', [21], false], + ['url_alias_location_path', [21], false], + ], + null, + ['urlal-21', 'urlalp-21'], + ], + [ + 'locationCopied', + [21, 33, 12], + [ + ['url_alias_location', [21], false], + ['url_alias_location', [33], false], + ], + null, + ['urlal-21', 'urlal-33'], + ], + [ + 'locationDeleted', + [21], + [ + ['url_alias_location', [21], false], + ['url_alias_location_path', [21], false], + ], + null, + ['urlal-21', 'urlalp-21'], + null, + [], + ], + [ + 'locationSwapped', + [21, 2, 33, 45], + [ + ['url_alias_location', [21], false], + ['url_alias_location_path', [21], false], + ['url_alias_location', [33], false], + ['url_alias_location_path', [33], false], + ], + null, + ['urlal-21', 'urlalp-21', 'urlal-33', 'urlalp-33'], + ], + [ + 'translationRemoved', + [[21, 33], 'eng-GB'], + [ + ['url_alias_location', [21], false], + ['url_alias_location_path', [21], false], + ['url_alias_location', [33], false], + ['url_alias_location_path', [33], false], + ], + null, + ['urlal-21', 'urlalp-21', 'urlal-33', 'urlalp-33'], + ], + [ + 'archiveUrlAliasesForDeletedTranslations', + [21, 33, ['eng-GB']], + [ + ['url_alias_location', [21], false], + ['url_alias_location_path', [21], false], + ], + null, + ['urlal-21', 'urlalp-21'], + ], + ]; + } + + public function providerForCachedLoadMethodsHit(): array + { + $object = new UrlAlias(['id' => 5]); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + ['listURLAliasesForLocation', [5], 'ibx-urlall-5', null, null, [['url_alias_location_list', [5], true]], ['ibx-urlall-5'], [$object]], + ['listURLAliasesForLocation', [5, true], 'ibx-urlall-5-c', null, null, [['url_alias_location_list_custom', [5], true]], ['ibx-urlall-5-c'], [$object]], + ['lookup', ['/Home'], 'ibx-urlau-_SHome', null, null, [['url_alias_url', ['_SHome'], true]], ['ibx-urlau-_SHome'], $object], + ['loadUrlAlias', [5], 'ibx-urla-5', null, null, [['url_alias', [5], true]], ['ibx-urla-5'], $object], + ]; + } + + public function providerForCachedLoadMethodsMiss(): array + { + $object = new UrlAlias(['id' => 5]); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + [ + 'listURLAliasesForLocation', + [5], + 'ibx-urlall-5', + [ + ['url_alias_location', [5], false], + ['url_alias', [5], false], + ], + ['urlal-5', 'urla-5'], + [ + ['url_alias_location_list', [5], true], + ], + ['ibx-urlall-5'], + [$object], + ], + [ + 'listURLAliasesForLocation', + [5, true], + 'ibx-urlall-5-c', + [ + ['url_alias_location', [5], false], + ['url_alias', [5], false], + ], + ['urlal-5', 'urla-5'], + [ + ['url_alias_location_list_custom', [5], true], + ], + ['ibx-urlall-5-c'], + [$object], + ], + [ + 'lookup', + ['/Home'], + 'ibx-urlau-_SHome', + [ + ['url_alias', [5], false], + ], + ['urla-5'], + [ + ['url_alias_url', ['_SHome'], true], + ], + ['ibx-urlau-_SHome'], + $object, + ], + [ + 'loadUrlAlias', + [5], + 'ibx-urla-5', + [ + ['url_alias', [5], false], + ], + ['urla-5'], + [ + ['url_alias', [5], true], + ], + ['ibx-urla-5'], + $object, + ], + ]; + } +} + +class_alias(UrlAliasHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\UrlAliasHandlerTest'); diff --git a/tests/lib/Persistence/Cache/UrlWildcardHandlerTest.php b/tests/lib/Persistence/Cache/UrlWildcardHandlerTest.php new file mode 100644 index 0000000000..c62be9b139 --- /dev/null +++ b/tests/lib/Persistence/Cache/UrlWildcardHandlerTest.php @@ -0,0 +1,86 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence\Content\UrlWildcard; +use Ibexa\Contracts\Core\Persistence\Content\UrlWildcard\Handler as SpiUrlWildcardHandler; + +class UrlWildcardHandlerTest extends AbstractCacheHandlerTest +{ + public function getHandlerMethodName(): string + { + return 'urlWildcardHandler'; + } + + public function getHandlerClassName(): string + { + return SpiUrlWildcardHandler::class; + } + + public function providerForUnCachedMethods(): array + { + $wildcard = new UrlWildcard(['id' => 1]); + + // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, ?mixed $returnValue + return [ + ['create', ['/home/about', '/web3/some/page/link', true], [['url_wildcard_not_found', [], false]], null, ['urlwnf'], null, $wildcard], + ['remove', [1], [['url_wildcard', [1], false]], null, ['urlw-1']], + ['loadAll', [], null, null, null, null, [$wildcard]], + ['exactSourceUrlExists', ['/home/about'], null, null, null, null, true], + ]; + } + + public function providerForCachedLoadMethodsHit(): array + { + $wildcard = new UrlWildcard(['id' => 1]); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + ['load', [1], 'ibx-urlw-1', null, null, [['url_wildcard', [1], true]], ['ibx-urlw-1'], $wildcard], + ['translate', ['/home/about'], 'ibx-urlws-_Shome_Sabout', null, null, [['url_wildcard_source', ['_Shome_Sabout'], true]], ['ibx-urlws-_Shome_Sabout'], $wildcard], + ]; + } + + public function providerForCachedLoadMethodsMiss(): array + { + $wildcard = new UrlWildcard(['id' => 1]); + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + [ + 'load', + [1], + 'ibx-urlw-1', + [ + ['url_wildcard', [1], false], + ], + ['urlw-1'], + [ + ['url_wildcard', [1], true], + ], + ['ibx-urlw-1'], + $wildcard, + ], + [ + 'translate', + ['/home/about'], + 'ibx-urlws-_Shome_Sabout', + [ + ['url_wildcard', [1], false], + ], + ['urlw-1'], + [ + ['url_wildcard_source', ['_Shome_Sabout'], true], + ], + ['ibx-urlws-_Shome_Sabout'], + $wildcard, + ], + ]; + } +} + +class_alias(UrlWildcardHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\UrlWildcardHandlerTest'); diff --git a/tests/lib/Persistence/Cache/UserHandlerTest.php b/tests/lib/Persistence/Cache/UserHandlerTest.php new file mode 100644 index 0000000000..108f91494c --- /dev/null +++ b/tests/lib/Persistence/Cache/UserHandlerTest.php @@ -0,0 +1,640 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Cache; + +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\User; +use Ibexa\Contracts\Core\Persistence\User\Handler as SPIUserHandler; +use Ibexa\Contracts\Core\Persistence\User\Policy; +use Ibexa\Contracts\Core\Persistence\User\Role; +use Ibexa\Contracts\Core\Persistence\User\RoleAssignment; +use Ibexa\Contracts\Core\Persistence\User\RoleCreateStruct; +use Ibexa\Contracts\Core\Persistence\User\RoleUpdateStruct; +use Ibexa\Core\Persistence\Cache\UserHandler; +use Ibexa\Core\Persistence\Legacy\Content\Location\Handler as SPILocationHandler; + +/** + * Test case for Persistence\Cache\UserHandler. + */ +class UserHandlerTest extends AbstractInMemoryCacheHandlerTest +{ + public function getHandlerMethodName(): string + { + return 'userHandler'; + } + + public function getHandlerClassName(): string + { + return SPIUserHandler::class; + } + + public function providerForUnCachedMethods(): array + { + $user = new User(['id' => 14, 'login' => 'otto', 'email' => 'otto@ibexa.co']); + $policy = new Policy(['id' => 13, 'roleId' => 9]); + $userToken = new User\UserTokenUpdateStruct(['userId' => 14, 'hashKey' => '4irj8t43r']); + $escapedLogin = str_replace('@', '_A', $user->login); + $escapedEmail = str_replace('@', '_A', $user->email); + + // string $method, array $arguments, array? $tagGeneratingArguments, array? $keyGeneratingArguments, array? $tags, array? $key, returned $returnValue, bool $callInnerHandler + return [ + [ + 'create', + [$user], + [ + ['content', [14], false], + ], + [ + ['user', [14], true], + ['user_with_by_login_suffix', [$escapedLogin], true], + ['user_with_by_email_suffix', [$escapedEmail], true], + ['users_with_by_email_suffix', [$escapedEmail], true], + ], + ['c-14'], + [ + 'ibx-u-14', + 'ibx-u-' . $escapedLogin . '-bl', + 'ibx-u-' . $escapedEmail . '-be', + 'ibx-us-' . $escapedEmail . '-be', + ], + $user, + false, + ], + [ + 'update', + [$user], + [ + ['content', [14], false], + ['user', [14], false], + ], + [ + ['user_with_by_email_suffix', [$escapedEmail], true], + ['users_with_by_email_suffix', [$escapedEmail], true], + ], + ['c-14', 'u-14'], + [ + 'ibx-u-' . $escapedEmail . '-be', + 'ibx-us-' . $escapedEmail . '-be', + ], + $user, + false, + ], + [ + 'updateUserToken', + [$userToken], + [ + ['user_with_account_key_suffix', [14], false], + ], + [ + ['user_with_by_account_key_suffix', ['4irj8t43r'], true], + ], + ['u-14-ak'], + ['ibx-u-4irj8t43r-bak'], + ], + ['expireUserToken', ['4irj8t43r'], null, [['user_with_by_account_key_suffix', ['4irj8t43r'], true]], null, ['ibx-u-4irj8t43r-bak']], + [ + 'delete', + [14], + [ + ['content', [14], false], + ['user', [14], false], + ], + null, + ['c-14', 'u-14'], + null, + null, + false, + ], + ['countRoleAssignments', [9], null, [], null, [], 1], + ['createRole', [new RoleCreateStruct()]], + ['createRoleDraft', [new RoleCreateStruct()]], + ['loadRole', [9, 1]], + ['loadRoleByIdentifier', ['member', 1]], + ['loadRoleDraftByRoleId', [9]], + ['loadRoles', []], + ['updateRole', [new RoleUpdateStruct(['id' => 9])], [['role', [9], false]], null, ['r-9']], + [ + 'deleteRole', + [9], + [ + ['role', [9], false], + ['role_assignment_role_list', [9], false], + ], + null, + ['r-9', 'rarl-9'], + ], + ['deleteRole', [9, 1]], + ['addPolicyByRoleDraft', [9, $policy]], + ['addPolicy', [9, $policy], [['role', [9], false]], null, ['r-9']], + [ + 'updatePolicy', + [$policy], + [ + ['policy', [13], false], + ['role', [9], false], + ], + null, + ['p-13', 'r-9'], + ], + [ + 'deletePolicy', + [13, 9], + [ + ['policy', [13], false], + ['role', [9], false], + ], + null, + ['p-13', 'r-9'], + ], + ['loadPoliciesByUserId', [14]], + [ + 'unassignRole', + [14, 9], + [ + ['role_assignment_group_list', [14], false], + ['role_assignment_role_list', [9], false], + ], + null, + ['ragl-14', 'rarl-9'], + ], + ]; + } + + public function providerForCachedLoadMethodsHit(): array + { + $user = new User(['id' => 14]); + $role = new Role(['id' => 9]); + $roleAssignment = new RoleAssignment(['id' => 11, 'roleId' => 9, 'contentId' => 14]); + $calls = [['locationHandler', Location\Handler::class, 'loadLocationsByContent', [new Location(['pathString' => '/1/2/43/'])]]]; + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + ['load', [14], 'ibx-u-14', null, null, [['user', [], true]], ['ibx-u'], $user], + [ + 'loadByLogin', + ['admin'], + 'ibx-u-admin-bl', + null, + null, + [ + ['user', [], true], + ['by_login_suffix', [], false], + ], + ['ibx-u', 'bl'], + $user, + ], + [ + 'loadByEmail', + ['admin@link.invalid'], + 'ibx-u-admin_Alink.invalid-be', + null, + null, + [ + ['user', [], true], + ['by_email_suffix', [], false], + ], + ['ibx-u', 'be'], + $user, + ], + [ + 'loadUserByToken', + ['hash'], + 'ibx-u-hash-bak', + null, + null, + [ + ['user', [], true], + ['by_account_key_suffix', [], false], + ], + ['ibx-u', '-bak'], + $user, + ], + ['loadRole', [9], 'ibx-r-9', null, null, [['role', [], true]], ['ibx-r'], $role], + [ + 'loadRoleByIdentifier', + ['member'], + 'ibx-r-member-bi', + null, + null, + [ + ['role', [], true], + ['by_identifier_suffix', [], false], + ], + ['ibx-r', '-bi'], + $role, + ], + ['loadRoleAssignment', [11], 'ibx-ra-11', null, null, [['role_assignment', [], true]], ['ibx-ra'], $roleAssignment], + ['loadRoleAssignmentsByRoleId', [$role->id], 'ibx-ra-9-bro', null, null, [['role_assignment_with_by_role_suffix', [9], true]], ['ibx-ra-9-bro'], [$roleAssignment]], + [ + 'loadRoleAssignmentsByRoleIdWithOffsetAndLimit', + [9, 0, 10], + 'ibx-ra-9-bro-0-10', + null, + null, + [['role_assignment_with_by_role_offset_limit_suffix', [9, 0, 10], true]], + ['ibx-ra-9-bro-0-10'], + [$roleAssignment], + ], + [ + 'loadRoleAssignmentsByGroupId', + [14], + 'ibx-ra-14-bg', + null, + null, + [ + ['role_assignment_with_by_group_suffix', [14], true], + ], + ['ibx-ra-14-bg'], + [$roleAssignment], + false, + $calls, + ], + [ + 'loadRoleAssignmentsByGroupId', + [14, true], + 'ibx-ra-14-bgi', + null, + null, + [['role_assignment_with_by_group_inherited_suffix', [14], true]], + ['ibx-ra-14-bgi'], + [$roleAssignment], + false, + $calls, + ], + ]; + } + + public function providerForCachedLoadMethodsMiss(): array + { + $user = new User(['id' => 14]); + $role = new Role(['id' => 9]); + $roleAssignment = new RoleAssignment(['id' => 11, 'roleId' => 9, 'contentId' => 14]); + $calls = [['locationHandler', Location\Handler::class, 'loadLocationsByContent', [new Location(['pathString' => '/1/2/43/'])]]]; + + // string $method, array $arguments, string $key, array? $tagGeneratingArguments, array? $tagGeneratingResults, array? $keyGeneratingArguments, array? $keyGeneratingResults, mixed? $data, bool $multi + return [ + [ + 'load', + [14], + 'ibx-u-14', + [ + ['content', [14], false], + ['user', [14], false], + ], + ['c-14', 'u-14'], + [ + ['user', [], true], + ], + ['ibx-u'], + $user, + ], + [ + 'loadByLogin', + ['admin'], + 'ibx-u-admin-bl', + [ + ['content', [14], false], + ['user', [14], false], + ], + ['c-14', 'u-14'], + [ + ['user', [], true], + ['by_login_suffix', [], false], + ], + ['ibx-u', 'bl'], + $user, + ], + [ + 'loadByEmail', + ['admin@link.invalid'], + 'ibx-u-admin_Alink.invalid-be', + [ + ['content', [14], false], + ['user', [14], false], + ], + ['c-14', 'u-14'], + [ + ['user', [], true], + ['by_email_suffix', [], false], + ], + ['ibx-u', 'be'], + $user, + ], + [ + 'loadUserByToken', + ['hash'], + 'ibx-u-hash-bak', + [ + ['content', [14], false], + ['user', [14], false], + ['user_with_account_key_suffix', [14], false], + ], + ['c-14', 'u-14', 'u-14-bak'], + [ + ['user', [], true], + ['by_account_key_suffix', [], false], + ], + ['ibx-u', '-bak'], + $user, + ], + [ + 'loadRole', + [9], + 'ibx-r-9', + [ + ['role', [9], false], + ], + ['r-9'], + [ + ['role', [], true], + ], + ['ibx-r'], + $role, + ], + [ + 'loadRoleByIdentifier', + ['member'], + 'ibx-r-member-bi', + [ + ['role', [9], false], + ], + ['r-9'], + [ + ['role', [], true], + ['by_identifier_suffix', [], false], + ], + ['ibx-r', '-bi'], + $role, + ], + [ + 'loadRoleAssignment', + [11], + 'ibx-ra-11', + [ + ['role_assignment', [11], false], + ['role_assignment_group_list', [14], false], + ['role_assignment_role_list', [9], false], + ], + ['ra-11', 'ragl-14', 'rarl-9'], + [ + ['role_assignment', [], true], + ], + ['ibx-ra'], + $roleAssignment, + ], + [ + 'loadRoleAssignmentsByRoleId', + [9], + 'ibx-ra-9-bro', + [ + ['role_assignment_role_list', [9], false], + ['role', [9], false], + ['role_assignment', [11], false], + ['role_assignment_group_list', [14], false], + ['role_assignment_role_list', [9], false], + ], + ['rarl-9', 'r-9', 'ra-11', 'ragl-14', 'rarl-9'], + [ + ['role_assignment_with_by_role_suffix', [9], true], + ], + ['ibx-ra-9-bro'], + [$roleAssignment], + ], + [ + 'loadRoleAssignmentsByRoleIdWithOffsetAndLimit', + [9, 0, 10], + 'ibx-ra-9-bro-0-10', + [ + ['role_assignment_role_list', [9], false], + ['role', [9], false], + ['role_assignment', [11], false], + ['role_assignment_group_list', [14], false], + ['role_assignment_role_list', [9], false], + ], + ['rarl-9', 'r-9', 'ra-11', 'ragl-14', 'rarl-9'], + [ + ['role_assignment_with_by_role_offset_limit_suffix', [9, 0, 10], true], + ], + ['ibx-ra-9-bro-0-10'], + [$roleAssignment], + ], + [ + 'loadRoleAssignmentsByGroupId', + [14], + 'ibx-ra-14-bg', + [ + ['role_assignment_group_list', [14], false], + ['location_path', ['2'], false], + ['location_path', ['43'], false], + ['role_assignment', [11], false], + ['role_assignment_group_list', [14], false], + ['role_assignment_role_list', [9], false], + ], + ['ragl-14', 'lp-2', 'lp-43', 'ra-11', 'ragl-14', 'rarl-9'], + [ + ['role_assignment_with_by_group_suffix', [14], true], + ], + ['ibx-ra-14-bg'], + [$roleAssignment], + false, + $calls, + ], + [ + 'loadRoleAssignmentsByGroupId', + [14, true], + 'ibx-ra-14-bgi', + [ + ['role_assignment_group_list', [14], false], + ['location_path', ['2'], false], + ['location_path', ['43'], false], + ['role_assignment', [11], false], + ['role_assignment_group_list', [14], false], + ['role_assignment_role_list', [9], false], + ], + ['ragl-14', 'lp-2', 'lp-43', 'ra-11', 'ragl-14', 'rarl-9'], + [ + ['role_assignment_with_by_group_inherited_suffix', [14], true], + ], + ['ibx-ra-14-bgi'], + [$roleAssignment], + false, + $calls, + ], + ]; + } + + public function testPublishRoleDraftFromExistingRole() + { + $this->loggerMock->expects($this->once())->method('logCall'); + $innerHandlerMock = $this->createMock(SPIUserHandler::class); + + $this->persistenceHandlerMock + ->expects($this->once()) + ->method('userHandler') + ->willReturn($innerHandlerMock); + + $roleDraftId = 33; + $originalRoleId = 30; + + $innerHandlerMock + ->expects($this->once()) + ->method('loadRole') + ->with($roleDraftId, Role::STATUS_DRAFT) + ->willReturn(new Role(['originalId' => $originalRoleId])); + + $innerHandlerMock + ->expects($this->once()) + ->method('publishRoleDraft') + ->with($roleDraftId); + + $roleTag = 'r-' . $originalRoleId; + + $this->cacheIdentifierGeneratorMock + ->expects($this->once()) + ->method('generateTag') + ->with('role', [$originalRoleId], false) + ->willReturn($roleTag); + + $this->cacheMock + ->expects($this->once()) + ->method('invalidateTags') + ->with([$roleTag]); + + $this->cacheMock + ->expects($this->never()) + ->method('deleteItem'); + + $handler = $this->persistenceCacheHandler->userHandler(); + $handler->publishRoleDraft($roleDraftId); + } + + public function testPublishNewRoleDraft() + { + $this->loggerMock->expects($this->once())->method('logCall'); + $innerHandlerMock = $this->createMock(SPIUserHandler::class); + $this->persistenceHandlerMock + ->expects($this->once()) + ->method('userHandler') + ->willReturn($innerHandlerMock); + $roleDraftId = 33; + $innerHandlerMock + ->expects($this->at(0)) + ->method('loadRole') + ->with($roleDraftId, Role::STATUS_DRAFT) + ->willReturn(new Role(['originalId' => -1])); + $innerHandlerMock + ->expects($this->at(1)) + ->method('publishRoleDraft') + ->with($roleDraftId); + $this->cacheMock + ->expects($this->never()) + ->method($this->anything()); + $handler = $this->persistenceCacheHandler->userHandler(); + $handler->publishRoleDraft($roleDraftId); + } + + public function testAssignRole() + { + $innerUserHandlerMock = $this->createMock(SPIUserHandler::class); + $innerLocationHandlerMock = $this->createMock(SPILocationHandler::class); + + $contentId = 14; + $roleId = 9; + + $this->loggerMock->expects($this->once())->method('logCall'); + + $this->persistenceHandlerMock + ->expects($this->once()) + ->method('userHandler') + ->willReturn($innerUserHandlerMock); + + $innerUserHandlerMock + ->expects($this->once()) + ->method('assignRole') + ->with($contentId, $roleId) + ->willReturn(null); + + $this->persistenceHandlerMock + ->expects($this->once()) + ->method('locationHandler') + ->willReturn($innerLocationHandlerMock); + + $innerLocationHandlerMock + ->expects($this->once()) + ->method('loadLocationsByContent') + ->with($contentId) + ->willReturn([new Location(['id' => '43'])]); + + $tags = ['ragl-14', 'rarl-9', 'lp-43']; + + $this->cacheIdentifierGeneratorMock + ->expects($this->exactly(3)) + ->method('generateTag') + ->withConsecutive( + ['role_assignment_group_list', [14], false], + ['role_assignment_role_list', [9], false], + ['location_path', [43], false] + ) + ->willReturnOnConsecutiveCalls(...$tags); + + $this->cacheMock + ->expects($this->once()) + ->method('invalidateTags') + ->with($tags); + + $this->cacheMock + ->expects($this->never()) + ->method('deleteItem'); + + $handler = $this->persistenceCacheHandler->userHandler(); + $handler->assignRole($contentId, $roleId); + } + + public function testRemoveRoleAssignment(): void + { + $handler = $this->persistenceCacheHandler->userHandler(); + $methodName = 'removeRoleAssignment'; + + $innerHandler = $this->createMock(SPIUserHandler::class); + $this->persistenceHandlerMock->method('userHandler')->willReturn($innerHandler); + $roleAssignmentId = 1; + $contentId = 2; + $roleId = 3; + $innerHandler + ->method('loadRoleAssignment') + ->willReturn( + new RoleAssignment(['id' => $roleAssignmentId, 'contentId' => $contentId, 'roleId' => $roleId]) + ); + + $this->loggerMock->method('logCall')->with( + UserHandler::class . "::$methodName", + [ + 'assignment' => $roleAssignmentId, + 'contentId' => $contentId, + 'roleId' => $roleId, + ] + ); + $innerHandler->method($methodName)->with($roleAssignmentId); + + $tags = [ + "ra-$roleAssignmentId", + "ragl-$contentId", + "rarl-$roleId", + ]; + $this->cacheIdentifierGeneratorMock + ->expects(self::exactly(count($tags))) + ->method('generateTag') + ->withConsecutive(['role_assignment'], ['role_assignment_group_list'], ['role_assignment_role_list']) + ->willReturnOnConsecutiveCalls(...$tags); + + $this->cacheMock->method('invalidateTags')->with($tags); + + $handler->removeRoleAssignment($roleAssignmentId); + } +} + +class_alias(UserHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\UserHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Cache/Tests/UserPreferenceHandlerTest.php b/tests/lib/Persistence/Cache/UserPreferenceHandlerTest.php similarity index 89% rename from eZ/Publish/Core/Persistence/Cache/Tests/UserPreferenceHandlerTest.php rename to tests/lib/Persistence/Cache/UserPreferenceHandlerTest.php index 67963ef3f8..853e29d2e2 100644 --- a/eZ/Publish/Core/Persistence/Cache/Tests/UserPreferenceHandlerTest.php +++ b/tests/lib/Persistence/Cache/UserPreferenceHandlerTest.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Cache\Tests; +namespace Ibexa\Tests\Core\Persistence\Cache; -use eZ\Publish\SPI\Persistence\UserPreference\Handler as SPIUserPreferenceHandler; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreference as SPIUserPreference; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreferenceSetStruct; +use Ibexa\Contracts\Core\Persistence\UserPreference\Handler as SPIUserPreferenceHandler; +use Ibexa\Contracts\Core\Persistence\UserPreference\UserPreference as SPIUserPreference; +use Ibexa\Contracts\Core\Persistence\UserPreference\UserPreferenceSetStruct; /** * Test case for Persistence\Cache\UserPreferenceHandler. @@ -128,3 +128,5 @@ public function providerForCachedLoadMethodsMiss(): array ]; } } + +class_alias(UserPreferenceHandlerTest::class, 'eZ\Publish\Core\Persistence\Cache\Tests\UserPreferenceHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Tests/DatabaseConnectionFactory.php b/tests/lib/Persistence/DatabaseConnectionFactory.php similarity index 90% rename from eZ/Publish/Core/Persistence/Tests/DatabaseConnectionFactory.php rename to tests/lib/Persistence/DatabaseConnectionFactory.php index 261df49ace..dfedf5b930 100644 --- a/eZ/Publish/Core/Persistence/Tests/DatabaseConnectionFactory.php +++ b/tests/lib/Persistence/DatabaseConnectionFactory.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Tests; +namespace Ibexa\Tests\Core\Persistence; use Doctrine\Common\EventManager; use Doctrine\DBAL\Connection; @@ -37,7 +37,7 @@ class DatabaseConnectionFactory private static $connectionPool; /** - * @param \EzSystems\DoctrineSchema\Database\DbPlatform\DbPlatform[] $databasePlatforms + * @param \Ibexa\DoctrineSchema\Database\DbPlatform\DbPlatformInterface[] $databasePlatforms * @param \Doctrine\Common\EventManager $eventManager */ public function __construct(iterable $databasePlatforms, EventManager $eventManager) @@ -85,3 +85,5 @@ public function createConnection(string $databaseURL): Connection return self::$connectionPool[$databaseURL]; } } + +class_alias(DatabaseConnectionFactory::class, 'eZ\Publish\Core\Persistence\Tests\DatabaseConnectionFactory'); diff --git a/tests/lib/Persistence/FieldTypeRegistryTest.php b/tests/lib/Persistence/FieldTypeRegistryTest.php new file mode 100644 index 0000000000..9b9b616444 --- /dev/null +++ b/tests/lib/Persistence/FieldTypeRegistryTest.php @@ -0,0 +1,96 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence; + +use Ibexa\Contracts\Core\FieldType\FieldType as SPIFieldType; +use Ibexa\Contracts\Core\Persistence\FieldType as SPIPersistenceFieldType; +use Ibexa\Core\Base\Exceptions\NotFound\FieldTypeNotFoundException; +use Ibexa\Core\Persistence\FieldTypeRegistry; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\FieldTypeRegistry + */ +class FieldTypeRegistryTest extends TestCase +{ + private const FIELD_TYPE_IDENTIFIER = 'some-type'; + + public function testConstructor(): void + { + $fieldType = $this->getFieldTypeMock(); + $registry = new FieldTypeRegistry([self::FIELD_TYPE_IDENTIFIER => $fieldType]); + + $this->assertInstanceOf( + SPIPersistenceFieldType::class, + $registry->getFieldType(self::FIELD_TYPE_IDENTIFIER) + ); + } + + public function testGetFieldTypeInstance() + { + $instance = $this->getFieldTypeMock(); + $registry = new FieldTypeRegistry([self::FIELD_TYPE_IDENTIFIER => $instance]); + + $result = $registry->getFieldType(self::FIELD_TYPE_IDENTIFIER); + + $this->assertInstanceOf(SPIPersistenceFieldType::class, $result); + } + + /** + * @since 5.3.2 + */ + public function testGetNotFound() + { + $this->expectException(FieldTypeNotFoundException::class); + + $registry = new FieldTypeRegistry([]); + $registry->getFieldType('not-found'); + } + + /** + * BC with 5.0-5.3.2. + */ + public function testGetNotFoundBCException() + { + $this->expectException(\RuntimeException::class); + + $registry = new FieldTypeRegistry([]); + $registry->getFieldType('not-found'); + } + + public function testGetNotInstance() + { + $this->expectException(\TypeError::class); + + $registry = new FieldTypeRegistry([self::FIELD_TYPE_IDENTIFIER => new \DateTime()]); + $registry->getFieldType(self::FIELD_TYPE_IDENTIFIER); + } + + public function testRegister() + { + $fieldType = $this->getFieldTypeMock(); + $registry = new FieldTypeRegistry([]); + $registry->register(self::FIELD_TYPE_IDENTIFIER, $fieldType); + + $this->assertInstanceOf( + SPIPersistenceFieldType::class, + $registry->getFieldType(self::FIELD_TYPE_IDENTIFIER) + ); + } + + /** + * Returns a mock for persistence field type. + * + * @return \Ibexa\Contracts\Core\Persistence\FieldType + */ + protected function getFieldTypeMock() + { + return $this->createMock(SPIFieldType::class); + } +} + +class_alias(FieldTypeRegistryTest::class, 'eZ\Publish\Core\Persistence\Tests\FieldTypeRegistryTest'); diff --git a/tests/lib/Persistence/FieldValue/Converter/ImageConverterTest.php b/tests/lib/Persistence/FieldValue/Converter/ImageConverterTest.php new file mode 100644 index 0000000000..e31971fb53 --- /dev/null +++ b/tests/lib/Persistence/FieldValue/Converter/ImageConverterTest.php @@ -0,0 +1,219 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\UrlRedecoratorInterface; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use PHPUnit\Framework\TestCase; + +final class ImageConverterTest extends TestCase +{ + private const MIME_TYPES = [ + 'image/png', + 'image/jpeg', + ]; + + private const MIME_TYPES_STORAGE_VALUE = '["image\/png","image\/jpeg"]'; + + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageConverter */ + private $imageConverter; + + /** @var \Ibexa\Core\IO\UrlRedecoratorInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $urlRedecorator; + + /** @var \Ibexa\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $ioService; + + protected function setUp(): void + { + $this->ioService = $this->createMock(IOServiceInterface::class); + $this->urlRedecorator = $this->createMock(UrlRedecoratorInterface::class); + + $this->imageConverter = new ImageConverter( + $this->ioService, + $this->urlRedecorator + ); + } + + /** + * @dataProvider dataProviderForTestToStorageFieldDefinition + */ + public function testToStorageFieldDefinition( + FieldDefinition $fieldDefinition, + StorageFieldDefinition $expectedStorageDef + ): void { + $storageFieldDefinition = new StorageFieldDefinition(); + + $this->imageConverter->toStorageFieldDefinition($fieldDefinition, $storageFieldDefinition); + + self::assertEquals( + $expectedStorageDef, + $storageFieldDefinition + ); + } + + public function dataProviderForTestToStorageFieldDefinition(): iterable + { + yield 'No validators' => [ + new FieldDefinition([ + 'fieldTypeConstraints' => new FieldTypeConstraints([ + 'validators' => [], + ]), + ]), + new StorageFieldDefinition([ + 'dataFloat1' => 0.0, + 'dataInt2' => 0, + 'dataText1' => 'MB', + 'dataText5' => '[]', + ]), + ]; + + yield 'FileSizeValidator' => [ + new FieldDefinition([ + 'fieldTypeConstraints' => new FieldTypeConstraints([ + 'validators' => [ + 'FileSizeValidator' => [ + 'maxFileSize' => 1.0, + ], + ], + ]), + ]), + new StorageFieldDefinition([ + 'dataFloat1' => 1.0, + 'dataInt2' => 0, + 'dataText1' => 'MB', + 'dataText5' => '[]', + ]), + ]; + + yield 'AlternativeTextValidator - required' => [ + new FieldDefinition([ + 'fieldTypeConstraints' => new FieldTypeConstraints([ + 'validators' => [ + 'AlternativeTextValidator' => [ + 'required' => true, + ], + ], + ]), + ]), + new StorageFieldDefinition([ + 'dataFloat1' => 0.0, + 'dataInt2' => 1, + 'dataText1' => 'MB', + 'dataText5' => '[]', + ]), + ]; + + yield 'AlternativeTextValidator - not required' => [ + new FieldDefinition([ + 'fieldTypeConstraints' => new FieldTypeConstraints([ + 'validators' => [ + 'AlternativeTextValidator' => [ + 'required' => false, + ], + ], + ]), + ]), + new StorageFieldDefinition([ + 'dataFloat1' => 0.0, + 'dataInt2' => 0, + 'dataText1' => 'MB', + 'dataText5' => '[]', + ]), + ]; + + yield 'mimeTypes' => [ + new FieldDefinition([ + 'fieldTypeConstraints' => new FieldTypeConstraints([ + 'fieldSettings' => [ + 'mimeTypes' => self::MIME_TYPES, + ], + ]), + ]), + new StorageFieldDefinition([ + 'dataFloat1' => 0.0, + 'dataInt2' => 0, + 'dataText1' => 'MB', + 'dataText5' => self::MIME_TYPES_STORAGE_VALUE, + ]), + ]; + } + + /** + * @dataProvider dataProviderForTestToFieldDefinition + */ + public function testToFieldDefinition( + StorageFieldDefinition $storageDef, + FieldDefinition $expectedFieldDefinition + ): void { + $fieldDefinition = new FieldDefinition(); + + $this->imageConverter->toFieldDefinition($storageDef, $fieldDefinition); + + self::assertEquals( + $expectedFieldDefinition, + $fieldDefinition + ); + } + + public function dataProviderForTestToFieldDefinition(): iterable + { + yield [ + new StorageFieldDefinition([ + 'dataFloat1' => 0.0, + 'dataInt2' => 0, + 'dataText5' => [], + ]), + new FieldDefinition([ + 'fieldTypeConstraints' => new FieldTypeConstraints([ + 'validators' => [ + 'FileSizeValidator' => [ + 'maxFileSize' => null, + ], + 'AlternativeTextValidator' => [ + 'required' => false, + ], + ], + 'fieldSettings' => [ + 'mimeTypes' => [], + ], + ]), + ]), + ]; + + yield [ + new StorageFieldDefinition([ + 'dataFloat1' => 1.0, + 'dataInt2' => 1, + 'dataText5' => self::MIME_TYPES_STORAGE_VALUE, + ]), + new FieldDefinition([ + 'fieldTypeConstraints' => new FieldTypeConstraints([ + 'validators' => [ + 'FileSizeValidator' => [ + 'maxFileSize' => 1.0, + ], + 'AlternativeTextValidator' => [ + 'required' => true, + ], + ], + 'fieldSettings' => [ + 'mimeTypes' => self::MIME_TYPES, + ], + ]), + ]), + ]; + } +} + +class_alias(ImageConverterTest::class, 'eZ\Publish\Core\Persistence\Tests\FieldValue\Converter\ImageConverterTest'); diff --git a/tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..f73e1f96cc --- /dev/null +++ b/tests/lib/Persistence/Legacy/Bookmark/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,161 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Bookmark\Gateway; + +use Ibexa\Contracts\Core\Persistence\Bookmark\Bookmark; +use Ibexa\Core\Persistence\Legacy\Bookmark\Gateway; +use Ibexa\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; +use PDO; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Bookmark\Gateway\DoctrineDatabase::insertBookmark + */ +class DoctrineDatabaseTest extends TestCase +{ + public const EXISTING_BOOKMARK_ID = 1; + public const EXISTING_BOOKMARK_DATA = [ + 'id' => 1, + 'name' => 'Lorem ipsum dolor', + 'node_id' => 5, + 'user_id' => 14, + ]; + + protected function setUp(): void + { + parent::setUp(); + + $this->insertDatabaseFixture(__DIR__ . '/../_fixtures/bookmarks.php'); + } + + public function testInsertBookmark() + { + $id = $this->getGateway()->insertBookmark(new Bookmark([ + 'userId' => 14, + 'locationId' => 54, + 'name' => 'Lorem ipsum dolor...', + ])); + + $data = $this->loadBookmark($id); + + $this->assertEquals([ + 'id' => $id, + 'name' => 'Lorem ipsum dolor...', + 'node_id' => '54', + 'user_id' => '14', + ], $data); + } + + public function testDeleteBookmark() + { + $this->getGateway()->deleteBookmark(self::EXISTING_BOOKMARK_ID); + + $this->assertEmpty($this->loadBookmark(self::EXISTING_BOOKMARK_ID)); + } + + public function testLoadBookmarkDataById() + { + $this->assertEquals( + [self::EXISTING_BOOKMARK_DATA], + $this->getGateway()->loadBookmarkDataById(self::EXISTING_BOOKMARK_ID) + ); + } + + public function testLoadBookmarkDataByUserIdAndLocationId() + { + $data = $this->getGateway()->loadBookmarkDataByUserIdAndLocationId( + (int) self::EXISTING_BOOKMARK_DATA['user_id'], + [(int) self::EXISTING_BOOKMARK_DATA['node_id']] + ); + + $this->assertEquals([self::EXISTING_BOOKMARK_DATA], $data); + } + + /** + * @dataProvider dataProviderForLoadUserBookmarks + */ + public function testLoadUserBookmarks(int $userId, int $offset, int $limit, array $expected) + { + $this->assertEquals($expected, $this->getGateway()->loadUserBookmarks($userId, $offset, $limit)); + } + + /** + * @dataProvider dataProviderForLoadUserBookmarks + */ + public function testCountUserBookmarks(int $userId, int $offset, int $limit, array $expected) + { + $this->assertEquals(count($expected), $this->getGateway()->countUserBookmarks($userId)); + } + + public function dataProviderForLoadUserBookmarks(): array + { + $fixtures = (require __DIR__ . '/../_fixtures/bookmarks.php')[DoctrineDatabase::TABLE_BOOKMARKS]; + + $expectedRows = static function ($userId) use ($fixtures) { + $rows = array_filter($fixtures, static function (array $row) use ($userId) { + return $row['user_id'] == $userId; + }); + + usort($rows, static function ($a, $b) { + return $b['id'] <=> $a['id']; + }); + + return $rows; + }; + + $userId = self::EXISTING_BOOKMARK_DATA['user_id']; + + return [ + [ + $userId, 0, 10, $expectedRows($userId), + ], + ]; + } + + public function testLocationSwapped() + { + $bookmark1Id = 3; + $bookmark2Id = 4; + + $bookmark1BeforeSwap = $this->loadBookmark($bookmark1Id); + $bookmark2BeforeSwap = $this->loadBookmark($bookmark2Id); + + $this->getGateway()->locationSwapped( + (int) $bookmark1BeforeSwap['node_id'], + (int) $bookmark2BeforeSwap['node_id'] + ); + + $bookmark1AfterSwap = $this->loadBookmark($bookmark1Id); + $bookmark2AfterSwap = $this->loadBookmark($bookmark2Id); + + $this->assertEquals($bookmark1BeforeSwap['node_id'], $bookmark2AfterSwap['node_id']); + $this->assertEquals($bookmark2BeforeSwap['node_id'], $bookmark1AfterSwap['node_id']); + } + + /** + * Return a ready to test DoctrineStorage gateway. + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function getGateway(): Gateway + { + return new DoctrineDatabase($this->getDatabaseConnection()); + } + + private function loadBookmark(int $id): array + { + $data = $this->connection + ->executeQuery('SELECT * FROM ezcontentbrowsebookmark WHERE id = :id', ['id' => $id]) + ->fetch(PDO::FETCH_ASSOC); + + return is_array($data) ? $data : []; + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Bookmark\Gateway\DoctrineDatabaseTest'); diff --git a/tests/lib/Persistence/Legacy/Bookmark/HandlerTest.php b/tests/lib/Persistence/Legacy/Bookmark/HandlerTest.php new file mode 100644 index 0000000000..65f96a6203 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Bookmark/HandlerTest.php @@ -0,0 +1,198 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Bookmark; + +use Ibexa\Contracts\Core\Persistence\Bookmark\Bookmark; +use Ibexa\Contracts\Core\Persistence\Bookmark\CreateStruct; +use Ibexa\Core\Persistence\Legacy\Bookmark\Gateway; +use Ibexa\Core\Persistence\Legacy\Bookmark\Handler; +use Ibexa\Core\Persistence\Legacy\Bookmark\Mapper; +use PHPUnit\Framework\TestCase; + +class HandlerTest extends TestCase +{ + public const BOOKMARK_ID = 7; + + /** @var \Ibexa\Core\Persistence\Legacy\Bookmark\Gateway|\PHPUnit\Framework\MockObject\MockObject */ + private $gateway; + + /** @var \Ibexa\Core\Persistence\Legacy\Bookmark\Mapper|\PHPUnit\Framework\MockObject\MockObject */ + private $mapper; + + /** @var \Ibexa\Core\Persistence\Legacy\Bookmark\Handler */ + private $handler; + + protected function setUp(): void + { + $this->gateway = $this->createMock(Gateway::class); + $this->mapper = $this->createMock(Mapper::class); + $this->handler = new Handler($this->gateway, $this->mapper); + } + + public function testCreate() + { + $createStruct = new CreateStruct([ + 'name' => 'Contact', + 'locationId' => 54, + 'userId' => 87, + ]); + + $bookmark = new Bookmark([ + 'name' => 'Contact', + 'locationId' => 54, + 'userId' => 87, + ]); + + $this->mapper + ->expects($this->once()) + ->method('createBookmarkFromCreateStruct') + ->with($createStruct) + ->willReturn($bookmark); + + $this->gateway + ->expects($this->once()) + ->method('insertBookmark') + ->with($bookmark) + ->willReturn(self::BOOKMARK_ID); + + $this->handler->create($createStruct); + + $this->assertEquals($bookmark->id, self::BOOKMARK_ID); + } + + public function testDelete() + { + $this->gateway + ->expects($this->once()) + ->method('deleteBookmark') + ->with(self::BOOKMARK_ID); + + $this->handler->delete(self::BOOKMARK_ID); + } + + public function testLoadByUserIdAndLocationIdExistingBookmark() + { + $userId = 87; + $locationId = 54; + + $rows = [ + [ + 'name' => 'Contact', + 'node_id' => $locationId, + 'user_id' => $userId, + ], + ]; + + $object = new Bookmark([ + 'name' => 'Contact', + 'locationId' => $locationId, + 'userId' => $userId, + ]); + + $this->gateway + ->expects($this->once()) + ->method('loadBookmarkDataByUserIdAndLocationId') + ->with($userId, [$locationId]) + ->willReturn($rows); + + $this->mapper + ->expects($this->once()) + ->method('extractBookmarksFromRows') + ->with($rows) + ->willReturn([$object]); + + $this->assertEquals([$locationId => $object], $this->handler->loadByUserIdAndLocationId($userId, [$locationId])); + } + + public function testLoadByUserIdAndLocationIdNonExistingBookmark() + { + $userId = 87; + $locationId = 54; + + $this->gateway + ->expects($this->once()) + ->method('loadBookmarkDataByUserIdAndLocationId') + ->with($userId, [$locationId]) + ->willReturn([]); + + $this->mapper + ->expects($this->once()) + ->method('extractBookmarksFromRows') + ->with([]) + ->willReturn([]); + + $this->assertEmpty($this->handler->loadByUserIdAndLocationId($userId, [$locationId])); + } + + public function testLoadUserBookmarks() + { + $userId = 87; + $offset = 50; + $limit = 25; + + $rows = [ + [ + 'id' => '12', + 'name' => 'Home', + 'node_id' => '2', + 'user_id' => $userId, + ], + [ + 'id' => '75', + 'name' => 'Contact', + 'node_id' => '54', + 'user_id' => $userId, + ], + ]; + + $objects = [ + new Bookmark([ + 'id' => 12, + 'name' => 'Home', + 'locationId' => 2, + 'userId' => 78, + ]), + new Bookmark([ + 'id' => 75, + 'name' => 'Contact', + 'locationId' => 54, + 'userId' => 87, + ]), + ]; + + $this->gateway + ->expects($this->once()) + ->method('loadUserBookmarks') + ->with($userId, $offset, $limit) + ->willReturn($rows); + + $this->mapper + ->expects($this->once()) + ->method('extractBookmarksFromRows') + ->with($rows) + ->willReturn($objects); + + $this->assertEquals($objects, $this->handler->loadUserBookmarks($userId, $offset, $limit)); + } + + public function testLocationSwapped() + { + $location1Id = 1; + $location2Id = 2; + + $this->gateway + ->expects($this->once()) + ->method('locationSwapped') + ->with($location1Id, $location2Id); + + $this->handler->locationSwapped($location1Id, $location2Id); + } +} + +class_alias(HandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Bookmark\HandlerTest'); diff --git a/tests/lib/Persistence/Legacy/Bookmark/MapperTest.php b/tests/lib/Persistence/Legacy/Bookmark/MapperTest.php new file mode 100644 index 0000000000..5806326a42 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Bookmark/MapperTest.php @@ -0,0 +1,80 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Bookmark; + +use Ibexa\Contracts\Core\Persistence\Bookmark\Bookmark; +use Ibexa\Contracts\Core\Persistence\Bookmark\CreateStruct; +use Ibexa\Core\Persistence\Legacy\Bookmark\Mapper; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Bookmark\Mapper + */ +class MapperTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Bookmark\Mapper */ + private $mapper; + + protected function setUp(): void + { + $this->mapper = new Mapper(); + } + + public function testCreateBookmarkFromCreateStruct() + { + $createStruct = new CreateStruct([ + 'name' => 'Contact', + 'locationId' => 54, + 'userId' => 87, + ]); + + $this->assertEquals(new Bookmark([ + 'name' => 'Contact', + 'locationId' => 54, + 'userId' => 87, + ]), $this->mapper->createBookmarkFromCreateStruct($createStruct)); + } + + public function testExtractBookmarksFromRows() + { + $rows = [ + [ + 'id' => '12', + 'name' => 'Home', + 'node_id' => '2', + 'user_id' => '78', + ], + [ + 'id' => '75', + 'name' => 'Contact', + 'node_id' => '54', + 'user_id' => '87', + ], + ]; + + $objects = [ + new Bookmark([ + 'id' => 12, + 'name' => 'Home', + 'locationId' => 2, + 'userId' => 78, + ]), + new Bookmark([ + 'id' => 75, + 'name' => 'Contact', + 'locationId' => 54, + 'userId' => 87, + ]), + ]; + + $this->assertEquals($objects, $this->mapper->extractBookmarksFromRows($rows)); + } +} + +class_alias(MapperTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Bookmark\MapperTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Bookmark/_fixtures/bookmarks.php b/tests/lib/Persistence/Legacy/Bookmark/_fixtures/bookmarks.php similarity index 100% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Bookmark/_fixtures/bookmarks.php rename to tests/lib/Persistence/Legacy/Bookmark/_fixtures/bookmarks.php diff --git a/tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php b/tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php new file mode 100644 index 0000000000..93cf5abb2d --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/ContentHandlerTest.php @@ -0,0 +1,1707 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content; + +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Location\CreateStruct as LocationCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\MetadataUpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Relation; +use Ibexa\Contracts\Core\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Type; +use Ibexa\Contracts\Core\Persistence\Content\UpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation as RelationValue; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Persistence\Legacy\Content\FieldHandler; +use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; +use Ibexa\Core\Persistence\Legacy\Content\Handler; +use Ibexa\Core\Persistence\Legacy\Content\Language\Handler as LanguageHandler; +use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; +use Ibexa\Core\Persistence\Legacy\Content\Mapper; +use Ibexa\Core\Persistence\Legacy\Content\TreeHandler; +use Ibexa\Core\Persistence\Legacy\Content\Type\Gateway as ContentTypeGateway; +use Ibexa\Core\Persistence\Legacy\Content\Type\Handler as ContentTypeHandler; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway as UrlAliasGateway; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; +use ReflectionException; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Handler + */ +class ContentHandlerTest extends TestCase +{ + private const RELATION_ID = 1; + + /** + * Content handler to test. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Handler + */ + protected $contentHandler; + + /** + * Gateway mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Gateway + */ + protected $gatewayMock; + + /** + * Location gateway mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Location\Gateway + */ + protected $locationGatewayMock; + + /** + * Type gateway mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Type\Gateway + */ + protected $typeGatewayMock; + + /** + * Mapper mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Mapper + */ + protected $mapperMock; + + /** + * Field handler mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\FieldHandler + */ + protected $fieldHandlerMock; + + /** + * Location handler mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\TreeHandler + */ + protected $treeHandlerMock; + + /** + * Slug converter mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter + */ + protected $slugConverterMock; + + /** + * Location handler mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway + */ + protected $urlAliasGatewayMock; + + /** + * ContentType handler mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Type\Handler + */ + protected $contentTypeHandlerMock; + + /** + * @var \PHPUnit\Framework\MockObject\MockObject&\eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler + */ + private $languageHandlerMock; + + /** + * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Handler::create + * + * @todo Current method way to complex to test, refactor! + */ + public function testCreate() + { + $handler = $this->getContentHandler(); + + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + $fieldHandlerMock = $this->getFieldHandlerMock(); + $locationMock = $this->getLocationGatewayMock(); + $contentTypeHandlerMock = $this->getContentTypeHandlerMock(); + $contentTypeMock = $this->createMock(Type::class); + $createStruct = $this->getCreateStructFixture(); + + $contentTypeHandlerMock->expects($this->once()) + ->method('load') + ->with($createStruct->typeId) + ->will($this->returnValue($contentTypeMock)); + + $mapperMock->expects($this->once()) + ->method('createVersionInfoFromCreateStruct') + ->with( + $this->isInstanceOf( + CreateStruct::class + ) + )->will( + $this->returnValue( + new VersionInfo( + [ + 'names' => [], + 'contentInfo' => new ContentInfo(), + ] + ) + ) + ); + + $gatewayMock->expects($this->once()) + ->method('insertContentObject') + ->with( + $this->isInstanceOf(CreateStruct::class) + )->will($this->returnValue(23)); + + $gatewayMock->expects($this->once()) + ->method('insertVersion') + ->with( + $this->isInstanceOf(VersionInfo::class), + $this->isType('array') + )->will($this->returnValue(1)); + + $fieldHandlerMock->expects($this->once()) + ->method('createNewFields') + ->with( + $this->isInstanceOf(Content::class), + $this->isInstanceOf(Type::class) + ); + + $locationMock->expects($this->once()) + ->method('createNodeAssignment') + ->with( + $this->isInstanceOf( + LocationCreateStruct::class + ), + $this->equalTo(42), + $this->equalTo(3) // Location\Gateway::NODE_ASSIGNMENT_OP_CODE_CREATE + ); + + $res = $handler->create($createStruct); + + // @todo Make subsequent tests + + $this->assertInstanceOf( + Content::class, + $res, + 'Content not created' + ); + $this->assertEquals( + 23, + $res->versionInfo->contentInfo->id, + 'Content ID not set correctly' + ); + $this->assertInstanceOf( + VersionInfo::class, + $res->versionInfo, + 'Version infos not created' + ); + $this->assertEquals( + 1, + $res->versionInfo->id, + 'Version ID not set correctly' + ); + $this->assertCount( + 2, + $res->fields, + 'Fields not set correctly in version' + ); + } + + public function testPublishFirstVersion() + { + $handler = $this->getPartlyMockedHandler(['loadVersionInfo']); + + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + $locationMock = $this->getLocationGatewayMock(); + $fieldHandlerMock = $this->getFieldHandlerMock(); + $metadataUpdateStruct = new MetadataUpdateStruct(); + + $handler->expects($this->at(0)) + ->method('loadVersionInfo') + ->with(23, 1) + ->will( + $this->returnValue( + new VersionInfo([ + 'contentInfo' => new ContentInfo([ + 'currentVersionNo' => 1, + 'mainLanguageCode' => 'eng-GB', + ]), + 'names' => [ + 'eng-GB' => '', + ], + ]) + ) + ); + + $contentRows = [['ezcontentobject_version_version' => 1]]; + + $gatewayMock->expects($this->once()) + ->method('load') + ->with( + $this->equalTo(23), + $this->equalTo(1), + $this->equalTo(null) + )->willReturn($contentRows); + + $gatewayMock->expects($this->once()) + ->method('loadVersionedNameData') + ->with( + $this->equalTo([['id' => 23, 'version' => 1]]) + )->will( + $this->returnValue([22]) + ); + + $mapperMock->expects($this->once()) + ->method('extractContentFromRows') + ->with($this->equalTo($contentRows), $this->equalTo([22])) + ->will($this->returnValue([$this->getContentFixtureForDraft()])); + + $fieldHandlerMock->expects($this->once()) + ->method('loadExternalFieldData') + ->with($this->isInstanceOf(Content::class)); + + $gatewayMock + ->expects($this->once()) + ->method('updateContent') + ->with(23, $metadataUpdateStruct); + + $locationMock + ->expects($this->once()) + ->method('createLocationsFromNodeAssignments') + ->with(23, 1); + + $locationMock + ->expects($this->once()) + ->method('updateLocationsContentVersionNo') + ->with(23, 1); + + $gatewayMock + ->expects($this->once()) + ->method('setPublishedStatus') + ->with(23, 1); + + $handler->publish(23, 1, $metadataUpdateStruct); + } + + public function testPublish() + { + $handler = $this->getPartlyMockedHandler(['loadVersionInfo', 'setStatus']); + + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + $locationMock = $this->getLocationGatewayMock(); + $fieldHandlerMock = $this->getFieldHandlerMock(); + $metadataUpdateStruct = new MetadataUpdateStruct(); + + $handler->expects($this->at(0)) + ->method('loadVersionInfo') + ->with(23, 2) + ->will( + $this->returnValue( + new VersionInfo([ + 'contentInfo' => new ContentInfo([ + 'currentVersionNo' => 1, + 'mainLanguageCode' => 'eng-GB', + ]), + 'names' => [ + 'eng-GB' => '', + ], + ]) + ) + ); + + $handler + ->expects($this->at(1)) + ->method('setStatus') + ->with(23, VersionInfo::STATUS_ARCHIVED, 1); + + $contentRows = [['ezcontentobject_version_version' => 2]]; + + $gatewayMock->expects($this->once()) + ->method('load') + ->with( + $this->equalTo(23), + $this->equalTo(2), + $this->equalTo(null) + ) + ->willReturn($contentRows); + + $gatewayMock->expects($this->once()) + ->method('loadVersionedNameData') + ->with( + $this->equalTo([['id' => 23, 'version' => 2]]) + )->will( + $this->returnValue([22]) + ); + + $mapperMock->expects($this->once()) + ->method('extractContentFromRows') + ->with($this->equalTo($contentRows), $this->equalTo([22])) + ->will($this->returnValue([$this->getContentFixtureForDraft()])); + + $fieldHandlerMock->expects($this->once()) + ->method('loadExternalFieldData') + ->with($this->isInstanceOf(Content::class)); + + $gatewayMock + ->expects($this->once()) + ->method('updateContent') + ->with(23, $metadataUpdateStruct, $this->isInstanceOf(VersionInfo::class)); + + $locationMock + ->expects($this->once()) + ->method('createLocationsFromNodeAssignments') + ->with(23, 2); + + $locationMock + ->expects($this->once()) + ->method('updateLocationsContentVersionNo') + ->with(23, 2); + + $gatewayMock + ->expects($this->once()) + ->method('setPublishedStatus') + ->with(23, 2); + + $handler->publish(23, 2, $metadataUpdateStruct); + } + + public function testCreateDraftFromVersion() + { + $handler = $this->getPartlyMockedHandler(['load']); + + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + $fieldHandlerMock = $this->getFieldHandlerMock(); + $languageHandlerMock = $this->getLanguageHandlerMock(); + $contentTypeHandlerMock = $this->getContentTypeHandlerMock(); + + $handler->expects($this->once()) + ->method('load') + ->with(23, 2) + ->will($this->returnValue($this->getContentFixtureForDraft())); + + $mapperMock->expects($this->once()) + ->method('createVersionInfoForContent') + ->with( + $this->isInstanceOf(Content::class), + $this->equalTo(3), + $this->equalTo(14) + )->will( + $this->returnValue( + new VersionInfo( + [ + 'names' => [], + 'versionNo' => 3, + 'contentInfo' => new ContentInfo(), + ] + ) + ) + ); + + $languageHandlerMock->method('loadByLanguageCode') + ->willReturn(new Content\Language()); + + $contentTypeHandlerMock->method('load') + ->willReturn(new Type()); + + $gatewayMock->expects($this->once()) + ->method('insertVersion') + ->with( + $this->isInstanceOf(VersionInfo::class), + $this->getContentFixtureForDraft()->fields + )->will($this->returnValue(42)); + + $gatewayMock->expects($this->once()) + ->method('getLastVersionNumber') + ->with($this->equalTo(23)) + ->will($this->returnValue(2)); + + $fieldHandlerMock->expects($this->once()) + ->method('createExistingFieldsInNewVersion') + ->with($this->isInstanceOf(Content::class)); + + $relationData = [ + [ + 'ezcontentobject_link_contentclassattribute_id' => 0, + 'ezcontentobject_link_to_contentobject_id' => 42, + 'ezcontentobject_link_relation_type' => 1, + ], + ]; + + $gatewayMock->expects($this->once()) + ->method('loadRelations') + ->with( + $this->equalTo(23), + $this->equalTo(2) + ) + ->will($this->returnValue($relationData)); + + $relationStruct = new RelationCreateStruct( + [ + 'sourceContentId' => 23, + 'sourceContentVersionNo' => 3, + 'sourceFieldDefinitionId' => 0, + 'destinationContentId' => 42, + 'type' => 1, + ] + ); + + $gatewayMock->expects($this->once()) + ->method('insertRelation') + ->with($this->equalTo($relationStruct)); + + $result = $handler->createDraftFromVersion(23, 2, 14); + + $this->assertInstanceOf( + Content::class, + $result + ); + $this->assertEquals( + 42, + $result->versionInfo->id + ); + } + + public function testLoad() + { + $handler = $this->getContentHandler(); + + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + $fieldHandlerMock = $this->getFieldHandlerMock(); + + $contentRows = [['ezcontentobject_version_version' => 2]]; + + $gatewayMock->expects($this->once()) + ->method('load') + ->with( + $this->equalTo(23), + $this->equalTo(2), + $this->equalTo(['eng-GB']) + )->will( + $this->returnValue($contentRows) + ); + + $gatewayMock->expects($this->once()) + ->method('loadVersionedNameData') + ->with( + $this->equalTo([['id' => 23, 'version' => 2]]) + )->will( + $this->returnValue([22]) + ); + + $mapperMock->expects($this->once()) + ->method('extractContentFromRows') + ->with($this->equalTo($contentRows), $this->equalTo([22])) + ->will($this->returnValue([$this->getContentFixtureForDraft()])); + + $fieldHandlerMock->expects($this->once()) + ->method('loadExternalFieldData') + ->with($this->isInstanceOf(Content::class)); + + $result = $handler->load(23, 2, ['eng-GB']); + + $this->assertEquals( + $result, + $this->getContentFixtureForDraft() + ); + } + + public function testLoadContentList() + { + $handler = $this->getContentHandler(); + + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + $fieldHandlerMock = $this->getFieldHandlerMock(); + $contentRows = [ + ['ezcontentobject_id' => 2, 'ezcontentobject_version_version' => 2], + ['ezcontentobject_id' => 3, 'ezcontentobject_version_version' => 1], + ]; + $gatewayMock->expects($this->once()) + ->method('loadContentList') + ->with([2, 3], ['eng-GB', 'eng-US']) + ->willReturn($contentRows); + + $nameDataRows = [ + ['ezcontentobject_name_contentobject_id' => 2, 'ezcontentobject_name_content_version' => 2], + ['ezcontentobject_name_contentobject_id' => 3, 'ezcontentobject_name_content_version' => 1], + ]; + + $gatewayMock->expects($this->once()) + ->method('loadVersionedNameData') + ->with($this->equalTo([['id' => 2, 'version' => 2], ['id' => 3, 'version' => 1]])) + ->willReturn($nameDataRows); + + $expected = [ + 2 => $this->getContentFixtureForDraft(2, 2), + 3 => $this->getContentFixtureForDraft(3, 1), + ]; + $mapperMock->expects($this->at(0)) + ->method('extractContentFromRows') + ->with($this->equalTo([$contentRows[0]]), $this->equalTo([$nameDataRows[0]])) + ->willReturn([$expected[2]]); + + $mapperMock->expects($this->at(1)) + ->method('extractContentFromRows') + ->with($this->equalTo([$contentRows[1]]), $this->equalTo([$nameDataRows[1]])) + ->willReturn([$expected[3]]); + + $fieldHandlerMock->expects($this->exactly(2)) + ->method('loadExternalFieldData') + ->with($this->isInstanceOf(Content::class)); + + $result = $handler->loadContentList([2, 3], ['eng-GB', 'eng-US']); + + $this->assertEquals( + $expected, + $result + ); + } + + public function testLoadContentInfoByRemoteId() + { + $contentInfoData = [new ContentInfo()]; + $this->getGatewayMock()->expects($this->once()) + ->method('loadContentInfoByRemoteId') + ->with( + $this->equalTo('15b256dbea2ae72418ff5facc999e8f9') + )->will( + $this->returnValue([42]) + ); + + $this->getMapperMock()->expects($this->once()) + ->method('extractContentInfoFromRow') + ->with($this->equalTo([42])) + ->will($this->returnValue($contentInfoData)); + + $this->assertSame( + $contentInfoData, + $this->getContentHandler()->loadContentInfoByRemoteId('15b256dbea2ae72418ff5facc999e8f9') + ); + } + + public function testLoadErrorNotFound() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getContentHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('load') + ->will( + $this->returnValue([]) + ); + + $result = $handler->load(23, 2, ['eng-GB']); + } + + /** + * Returns a Content for {@link testCreateDraftFromVersion()}. + * + * @param int $id Optional id + * @param int $versionNo Optional version number + * + * @return \Ibexa\Contracts\Core\Persistence\Content + */ + protected function getContentFixtureForDraft(int $id = 23, int $versionNo = 2) + { + $content = new Content(); + $content->versionInfo = new VersionInfo(); + $content->versionInfo->versionNo = $versionNo; + + $content->versionInfo->contentInfo = new ContentInfo(['id' => $id]); + + $field = new Field(); + $field->versionNo = $versionNo; + + $content->fields = [$field]; + + return $content; + } + + public function testUpdateContent() + { + $handler = $this->getPartlyMockedHandler(['load', 'loadContentInfo']); + + $gatewayMock = $this->getGatewayMock(); + $fieldHandlerMock = $this->getFieldHandlerMock(); + $contentTypeHandlerMock = $this->getContentTypeHandlerMock(); + $contentTypeMock = $this->createMock(Type::class); + $contentStub = new Content( + [ + 'versionInfo' => new VersionInfo( + [ + 'contentInfo' => new ContentInfo( + [ + 'contentTypeId' => 4242, + ] + ), + ] + ), + ] + ); + + $contentTypeHandlerMock->expects($this->once()) + ->method('load') + ->with($contentStub->versionInfo->contentInfo->contentTypeId) + ->will($this->returnValue($contentTypeMock)); + + $gatewayMock->expects($this->once()) + ->method('updateContent') + ->with(14, $this->isInstanceOf(MetadataUpdateStruct::class)); + $gatewayMock->expects($this->once()) + ->method('updateVersion') + ->with(14, 4, $this->isInstanceOf(UpdateStruct::class)); + + $fieldHandlerMock->expects($this->once()) + ->method('updateFields') + ->with( + $this->isInstanceOf(Content::class), + $this->isInstanceOf(UpdateStruct::class), + $this->isInstanceOf(Type::class) + ); + + $handler->expects($this->at(0)) + ->method('load') + ->with(14, 4) + ->will($this->returnValue($contentStub)); + + $handler->expects($this->at(1)) + ->method('load') + ->with(14, 4); + + $handler->expects($this->at(2)) + ->method('loadContentInfo') + ->with(14); + + $resultContent = $handler->updateContent( + 14, // ContentId + 4, // VersionNo + new UpdateStruct( + [ + 'creatorId' => 14, + 'modificationDate' => time(), + 'initialLanguageId' => 2, + 'fields' => [ + new Field( + [ + 'id' => 23, + 'fieldDefinitionId' => 42, + 'type' => 'some-type', + 'value' => new FieldValue(), + ] + ), + new Field( + [ + 'id' => 23, + 'fieldDefinitionId' => 43, + 'type' => 'some-type', + 'value' => new FieldValue(), + ] + ), + ], + ] + ) + ); + + $resultContentInfo = $handler->updateMetadata( + 14, // ContentId + new MetadataUpdateStruct( + [ + 'ownerId' => 14, + 'name' => 'Some name', + 'modificationDate' => time(), + 'alwaysAvailable' => true, + ] + ) + ); + } + + public function testUpdateMetadata() + { + $handler = $this->getPartlyMockedHandler(['load', 'loadContentInfo']); + + $gatewayMock = $this->getGatewayMock(); + $fieldHandlerMock = $this->getFieldHandlerMock(); + $updateStruct = new MetadataUpdateStruct( + [ + 'ownerId' => 14, + 'name' => 'Some name', + 'modificationDate' => time(), + 'alwaysAvailable' => true, + ] + ); + + $gatewayMock->expects($this->once()) + ->method('updateContent') + ->with(14, $updateStruct); + + $handler->expects($this->once()) + ->method('loadContentInfo') + ->with(14) + ->will( + $this->returnValue( + $this->createMock(ContentInfo::class) + ) + ); + + $resultContentInfo = $handler->updateMetadata( + 14, // ContentId + $updateStruct + ); + self::assertInstanceOf(ContentInfo::class, $resultContentInfo); + } + + public function testUpdateMetadataUpdatesPathIdentificationString() + { + $handler = $this->getPartlyMockedHandler(['load', 'loadContentInfo']); + $locationGatewayMock = $this->getLocationGatewayMock(); + $slugConverterMock = $this->getSlugConverterMock(); + $urlAliasGatewayMock = $this->getUrlAliasGatewayMock(); + $gatewayMock = $this->getGatewayMock(); + $updateStruct = new MetadataUpdateStruct(['mainLanguageId' => 2]); + + $gatewayMock->expects($this->once()) + ->method('updateContent') + ->with(14, $updateStruct); + + $locationGatewayMock->expects($this->once()) + ->method('loadLocationDataByContent') + ->with(14) + ->will( + $this->returnValue( + [ + [ + 'node_id' => 100, + 'parent_node_id' => 200, + ], + ] + ) + ); + + $urlAliasGatewayMock->expects($this->once()) + ->method('loadLocationEntries') + ->with(100, false, 2) + ->will( + $this->returnValue( + [ + [ + 'text' => 'slug', + ], + ] + ) + ); + + $slugConverterMock->expects($this->once()) + ->method('convert') + ->with('slug', 'node_100', 'urlalias_compat') + ->will($this->returnValue('transformed_slug')); + + $locationGatewayMock->expects($this->once()) + ->method('updatePathIdentificationString') + ->with(100, 200, 'transformed_slug'); + + $handler->expects($this->once()) + ->method('loadContentInfo') + ->with(14) + ->will( + $this->returnValue( + $this->createMock(ContentInfo::class) + ) + ); + + $handler->updateMetadata( + 14, // ContentId + $updateStruct + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testLoadRelation(): void + { + $handler = $this->getContentHandler(); + + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + $relationFixture = $this->getRelationFixture(); + + $gatewayMock + ->expects(self::once()) + ->method('loadRelation') + ->with(self::RELATION_ID) + ->willReturn([self::RELATION_ID]); + + $mapperMock + ->expects(self::once()) + ->method('extractRelationFromRow') + ->with([self::RELATION_ID]) + ->willReturn($relationFixture); + + $result = $handler->loadRelation(self::RELATION_ID); + + $this->assertEquals( + $result, + $relationFixture + ); + } + + public function testLoadRelations() + { + $handler = $this->getContentHandler(); + + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + + $gatewayMock->expects($this->once()) + ->method('loadRelations') + ->with( + $this->equalTo(23), + $this->equalTo(null), + $this->equalTo(null) + )->will( + $this->returnValue([42]) + ); + + $mapperMock->expects($this->once()) + ->method('extractRelationsFromRows') + ->with($this->equalTo([42])) + ->will($this->returnValue($this->getRelationFixture())); + + $result = $handler->loadRelations(23); + + $this->assertEquals( + $result, + $this->getRelationFixture() + ); + } + + public function testLoadReverseRelations() + { + $handler = $this->getContentHandler(); + + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + + $gatewayMock->expects($this->once()) + ->method('loadReverseRelations') + ->with( + $this->equalTo(23), + $this->equalTo(null) + )->will( + $this->returnValue([42]) + ); + + $mapperMock->expects($this->once()) + ->method('extractRelationsFromRows') + ->with($this->equalTo([42])) + ->will($this->returnValue($this->getRelationFixture())); + + $result = $handler->loadReverseRelations(23); + + $this->assertEquals( + $result, + $this->getRelationFixture() + ); + } + + public function testAddRelation() + { + // expected relation object after creation + $expectedRelationObject = new Relation(); + $expectedRelationObject->id = 42; // mocked value, not a real one + $expectedRelationObject->sourceContentId = 23; + $expectedRelationObject->sourceContentVersionNo = 1; + $expectedRelationObject->destinationContentId = 66; + $expectedRelationObject->type = RelationValue::COMMON; + + // relation create struct + $relationCreateStruct = new Relation\CreateStruct(); + $relationCreateStruct->destinationContentId = 66; + $relationCreateStruct->sourceContentId = 23; + $relationCreateStruct->sourceContentVersionNo = 1; + $relationCreateStruct->type = RelationValue::COMMON; + + $handler = $this->getContentHandler(); + + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + + $mapperMock->expects($this->once()) + ->method('createRelationFromCreateStruct') + // @todo Connected with the todo above + ->with($this->equalTo($relationCreateStruct)) + ->will($this->returnValue($expectedRelationObject)); + + $gatewayMock->expects($this->once()) + ->method('insertRelation') + ->with($this->equalTo($relationCreateStruct)) + ->will( + // @todo Should this return a row as if it was selected from the database, the id... ? Check with other, similar create methods + $this->returnValue(42) + ); + + $result = $handler->addRelation($relationCreateStruct); + + $this->assertEquals( + $result, + $expectedRelationObject + ); + } + + public function testRemoveRelation() + { + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('deleteRelation') + ->with($this->equalTo(1, RelationValue::COMMON)); + + $this->getContentHandler()->removeRelation(1, RelationValue::COMMON); + } + + protected function getRelationFixture() + { + $relation = new Relation(); + $relation->id = self::RELATION_ID; + $relation->sourceContentId = 23; + $relation->sourceContentVersionNo = 1; + $relation->destinationContentId = 69; + + return $relation; + } + + /** + * Returns a CreateStruct fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\CreateStruct + */ + public function getCreateStructFixture() + { + $struct = new CreateStruct(); + + $struct->typeId = 4242; + + $firstField = new Field(); + $firstField->type = 'some-type'; + $firstField->value = new FieldValue(); + + $secondField = clone $firstField; + + $struct->fields = [ + $firstField, $secondField, + ]; + + $struct->locations = [ + new LocationCreateStruct( + ['parentId' => 42] + ), + ]; + + $struct->name = [ + 'eng-GB' => 'This is a test name', + ]; + + return $struct; + } + + public function testLoadDraftsForUser() + { + $handler = $this->getContentHandler(); + $rows = [['ezcontentobject_version_contentobject_id' => 42, 'ezcontentobject_version_version' => 2]]; + + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + + $gatewayMock->expects($this->once()) + ->method('listVersionsForUser') + ->with($this->equalTo(23)) + ->will($this->returnValue($rows)); + + $gatewayMock->expects($this->once()) + ->method('loadVersionedNameData') + ->with($this->equalTo([['id' => 42, 'version' => 2]])) + ->will($this->returnValue([])); + + $mapperMock->expects($this->once()) + ->method('extractVersionInfoListFromRows') + ->with($this->equalTo($rows), $this->equalTo([])) + ->will($this->returnValue([new VersionInfo()])); + + $res = $handler->loadDraftsForUser(23); + + $this->assertEquals( + [new VersionInfo()], + $res + ); + } + + public function testListVersions() + { + $handler = $this->getContentHandler(); + + $treeHandlerMock = $this->getTreeHandlerMock(); + + $treeHandlerMock + ->expects($this->once()) + ->method('listVersions') + ->with(23) + ->will($this->returnValue([new VersionInfo()])); + + $versions = $handler->listVersions(23); + + $this->assertEquals( + [new VersionInfo()], + $versions + ); + } + + public function testRemoveRawContent() + { + $handler = $this->getContentHandler(); + $treeHandlerMock = $this->getTreeHandlerMock(); + + $treeHandlerMock + ->expects($this->once()) + ->method('removeRawContent') + ->with(23); + + $handler->removeRawContent(23); + } + + /** + * Test for the deleteContent() method. + */ + public function testDeleteContentWithLocations() + { + $handlerMock = $this->getPartlyMockedHandler(['getAllLocationIds']); + $gatewayMock = $this->getGatewayMock(); + $treeHandlerMock = $this->getTreeHandlerMock(); + + $gatewayMock->expects($this->once()) + ->method('getAllLocationIds') + ->with($this->equalTo(23)) + ->will($this->returnValue([42, 24])); + $treeHandlerMock->expects($this->exactly(2)) + ->method('removeSubtree') + ->with( + $this->logicalOr( + $this->equalTo(42), + $this->equalTo(24) + ) + ); + + $handlerMock->deleteContent(23); + } + + /** + * Test for the deleteContent() method. + */ + public function testDeleteContentWithoutLocations() + { + $handlerMock = $this->getPartlyMockedHandler(['removeRawContent']); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('getAllLocationIds') + ->with($this->equalTo(23)) + ->will($this->returnValue([])); + $handlerMock->expects($this->once()) + ->method('removeRawContent') + ->with($this->equalTo(23)); + + $handlerMock->deleteContent(23); + } + + public function testDeleteVersion() + { + $handler = $this->getContentHandler(); + + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + $locationHandlerMock = $this->getLocationGatewayMock(); + $fieldHandlerMock = $this->getFieldHandlerMock(); + + $rows = [['ezcontentobject_version_version' => 2]]; + + // Load VersionInfo to delete fields + $gatewayMock->expects($this->once()) + ->method('loadVersionInfo') + ->with($this->equalTo(225), $this->equalTo(2)) + ->willReturn($rows); + + $gatewayMock->expects($this->once()) + ->method('loadVersionedNameData') + ->with($this->equalTo([['id' => 225, 'version' => 2]])) + ->will($this->returnValue([22])); + + $mapperMock->expects($this->once()) + ->method('extractVersionInfoListFromRows') + ->with($this->equalTo($rows), $this->equalTo([22])) + ->will($this->returnValue([new VersionInfo()])); + + $locationHandlerMock->expects($this->once()) + ->method('deleteNodeAssignment') + ->with( + $this->equalTo(225), + $this->equalTo(2) + ); + + $fieldHandlerMock->expects($this->once()) + ->method('deleteFields') + ->with( + $this->equalTo(225), + $this->isInstanceOf(VersionInfo::class) + ); + $gatewayMock->expects($this->once()) + ->method('deleteRelations') + ->with( + $this->equalTo(225), + $this->equalTo(2) + ); + $gatewayMock->expects($this->once()) + ->method('deleteVersions') + ->with( + $this->equalTo(225), + $this->equalTo(2) + ); + $gatewayMock->expects($this->once()) + ->method('deleteNames') + ->with( + $this->equalTo(225), + $this->equalTo(2) + ); + + $handler->deleteVersion(225, 2); + } + + public function testCopySingleVersion() + { + $handler = $this->getPartlyMockedHandler(['load', 'internalCreate']); + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + + $handler->expects( + $this->once() + )->method( + 'load' + )->with( + $this->equalTo(23), + $this->equalTo(32) + )->will( + $this->returnValue(new Content()) + ); + + $mapperMock->expects( + $this->once() + )->method( + 'createCreateStructFromContent' + )->with( + $this->isInstanceOf(Content::class) + )->will( + $this->returnValue(new CreateStruct()) + ); + + $handler->expects( + $this->once() + )->method( + 'internalCreate' + )->with( + $this->isInstanceOf(CreateStruct::class), + $this->equalTo(32) + )->will( + $this->returnValue( + new Content( + [ + 'versionInfo' => new VersionInfo(['contentInfo' => new ContentInfo(['id' => 24])]), + ] + ) + ) + ); + + $gatewayMock->expects($this->once()) + ->method('copyRelations') + ->with( + $this->equalTo(23), + $this->equalTo(24), + $this->equalTo(32) + ) + ->will($this->returnValue(null)); + + $result = $handler->copy(23, 32); + + $this->assertInstanceOf( + Content::class, + $result + ); + } + + public function testCopyAllVersions() + { + $handler = $this->getPartlyMockedHandler( + [ + 'loadContentInfo', + 'load', + 'internalCreate', + 'listVersions', + ] + ); + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + $fieldHandlerMock = $this->getFieldHandlerMock(); + $contentTypeHandlerMock = $this->getContentTypeHandlerMock(); + $contentTypeMock = $this->createMock(Type::class); + $time = time(); + $createStructStub = new CreateStruct( + [ + 'modified' => $time, + 'typeId' => 4242, + ] + ); + + $contentTypeHandlerMock->expects($this->once()) + ->method('load') + ->with($createStructStub->typeId) + ->will($this->returnValue($contentTypeMock)); + + $handler->expects($this->once()) + ->method('loadContentInfo') + ->with($this->equalTo(23)) + ->will($this->returnValue(new ContentInfo(['currentVersionNo' => 2]))); + + $handler->expects($this->at(1)) + ->method('load') + ->with($this->equalTo(23), $this->equalTo(2)) + ->will($this->returnValue(new Content())); + + $mapperMock->expects($this->once()) + ->method('createCreateStructFromContent') + ->with($this->isInstanceOf(Content::class)) + ->will( + $this->returnValue($createStructStub) + ); + + $handler->expects($this->once()) + ->method('internalCreate') + ->with( + $this->isInstanceOf(CreateStruct::class), + $this->equalTo(2) + )->will( + $this->returnValue( + new Content( + [ + 'versionInfo' => new VersionInfo( + [ + 'contentInfo' => new ContentInfo(['id' => 24]), + ] + ), + ] + ) + ) + ); + + $handler->expects($this->once()) + ->method('listVersions') + ->with($this->equalTo(23)) + ->will( + $this->returnValue( + [ + new VersionInfo(['versionNo' => 1]), + new VersionInfo(['versionNo' => 2]), + ] + ) + ); + + $versionInfo = new VersionInfo( + [ + 'names' => ['eng-US' => 'Test'], + 'contentInfo' => new ContentInfo( + [ + 'id' => 24, + 'alwaysAvailable' => true, + ] + ), + ] + ); + $handler->expects($this->at(4)) + ->method('load') + ->with($this->equalTo(23), $this->equalTo(1)) + ->will( + $this->returnValue( + new Content( + [ + 'versionInfo' => $versionInfo, + 'fields' => [], + ] + ) + ) + ); + + $versionInfo->creationDate = $time; + $versionInfo->modificationDate = $time; + $gatewayMock->expects($this->once()) + ->method('insertVersion') + ->with( + $this->equalTo($versionInfo), + $this->isType('array') + )->will($this->returnValue(42)); + + $versionInfo = clone $versionInfo; + $versionInfo->id = 42; + $fieldHandlerMock->expects($this->once()) + ->method('createNewFields') + ->with( + $this->equalTo( + new Content( + [ + 'versionInfo' => $versionInfo, + 'fields' => [], + ] + ) + ), + $this->isInstanceOf(Type::class) + ); + + $gatewayMock->expects($this->once()) + ->method('setName') + ->with( + $this->equalTo(24), + $this->equalTo(1), + $this->equalTo('Test'), + $this->equalTo('eng-US') + ); + + $gatewayMock->expects($this->once()) + ->method('copyRelations') + ->with( + $this->equalTo(23), + $this->equalTo(24), + $this->equalTo(null) + ) + ->will($this->returnValue(null)); + + $result = $handler->copy(23); + + $this->assertInstanceOf( + Content::class, + $result + ); + } + + public function testCopyThrowsNotFoundExceptionContentNotFound() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getContentHandler(); + + $treeHandlerMock = $this->getTreeHandlerMock(); + $treeHandlerMock + ->expects($this->once()) + ->method('loadContentInfo') + ->with($this->equalTo(23)) + ->will( + $this->throwException(new NotFoundException('ContentInfo', 23)) + ); + + $handler->copy(23); + } + + public function testCopyThrowsNotFoundExceptionVersionNotFound() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getContentHandler(); + + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('load') + ->with($this->equalTo(23, 32)) + ->will($this->returnValue([])); + + $result = $handler->copy(23, 32); + } + + public function testSetStatus() + { + $handler = $this->getContentHandler(); + + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('setStatus') + ->with(23, 5, 2) + ->will($this->returnValue(true)); + + $this->assertTrue( + $handler->setStatus(23, 2, 5) + ); + } + + /** + * @covers \Ibexa\Contracts\Core\Persistence\Legacy\Content\Handler::loadVersionInfoList + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testLoadVersionInfoList(): void + { + $handler = $this->getContentHandler(); + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + + $contentIds = [2, 3]; + $versionInfo1 = new VersionInfo([ + 'contentInfo' => new ContentInfo(['id' => 2]), + ]); + $versionInfo2 = new VersionInfo([ + 'contentInfo' => new ContentInfo(['id' => 3]), + ]); + + $versionRows = [ + ['ezcontentobject_id' => 2, 'ezcontentobject_version_version' => 2], + ['ezcontentobject_id' => 3, 'ezcontentobject_version_version' => 1], + ]; + + $gatewayMock->expects(self::once()) + ->method('loadVersionInfoList') + ->with($contentIds) + ->willReturn($versionRows); + + $nameDataRows = [ + ['ezcontentobject_name_contentobject_id' => 2, 'ezcontentobject_name_content_version' => 2], + ['ezcontentobject_name_contentobject_id' => 3, 'ezcontentobject_name_content_version' => 1], + ]; + + $gatewayMock->expects(self::once()) + ->method('loadVersionedNameData') + ->with([['id' => 2, 'version' => 2], ['id' => 3, 'version' => 1]]) + ->willReturn($nameDataRows); + + $mapperMock->expects(self::once()) + ->method('extractVersionInfoListFromRows') + ->with($versionRows) + ->willReturn([ + $versionInfo1, + $versionInfo2, + ]); + + $expected = [ + 2 => $versionInfo1, + 3 => $versionInfo2, + ]; + + $result = $handler->loadVersionInfoList($contentIds); + + self::assertEquals($expected, $result); + } + + /** + * Returns the handler to test. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Handler + */ + protected function getContentHandler() + { + if (!isset($this->contentHandler)) { + $this->contentHandler = new Handler( + $this->getGatewayMock(), + $this->getLocationGatewayMock(), + $this->getMapperMock(), + $this->getFieldHandlerMock(), + $this->getSlugConverterMock(), + $this->getUrlAliasGatewayMock(), + $this->getContentTypeHandlerMock(), + $this->getTreeHandlerMock(), + $this->getLanguageHandlerMock(), + ); + } + + return $this->contentHandler; + } + + /** + * Returns the handler to test with $methods mocked. + * + * @param string[] $methods + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Handler + */ + protected function getPartlyMockedHandler(array $methods) + { + return $this->getMockBuilder(Handler::class) + ->setMethods($methods) + ->setConstructorArgs( + [ + $this->getGatewayMock(), + $this->getLocationGatewayMock(), + $this->getMapperMock(), + $this->getFieldHandlerMock(), + $this->getSlugConverterMock(), + $this->getUrlAliasGatewayMock(), + $this->getContentTypeHandlerMock(), + $this->getTreeHandlerMock(), + $this->getLanguageHandlerMock(), + ] + ) + ->getMock(); + } + + /** + * Returns a TreeHandler mock. + * + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\TreeHandler + */ + protected function getTreeHandlerMock() + { + if (!isset($this->treeHandlerMock)) { + $this->treeHandlerMock = $this->createMock(TreeHandler::class); + } + + return $this->treeHandlerMock; + } + + /** + * Returns a ContentTypeHandler mock. + * + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Type\Handler + */ + protected function getContentTypeHandlerMock() + { + if (!isset($this->contentTypeHandlerMock)) { + $this->contentTypeHandlerMock = $this->createMock(ContentTypeHandler::class); + } + + return $this->contentTypeHandlerMock; + } + + /** + * @return \PHPUnit\Framework\MockObject\MockObject&\eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler + */ + protected function getLanguageHandlerMock(): LanguageHandler + { + if (!isset($this->languageHandlerMock)) { + $this->languageHandlerMock = $this->createMock(LanguageHandler::class); + } + + return $this->languageHandlerMock; + } + + /** + * Returns a FieldHandler mock. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\FieldHandler + */ + protected function getFieldHandlerMock() + { + if (!isset($this->fieldHandlerMock)) { + $this->fieldHandlerMock = $this->createMock(FieldHandler::class); + } + + return $this->fieldHandlerMock; + } + + /** + * Returns a Mapper mock. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Mapper + */ + protected function getMapperMock() + { + if (!isset($this->mapperMock)) { + $this->mapperMock = $this->createMock(Mapper::class); + } + + return $this->mapperMock; + } + + /** + * Returns a Location Gateway mock. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Location\Gateway + */ + protected function getLocationGatewayMock() + { + if (!isset($this->locationGatewayMock)) { + $this->locationGatewayMock = $this->createMock(LocationGateway::class); + } + + return $this->locationGatewayMock; + } + + /** + * Returns a content type gateway mock. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\Gateway + */ + protected function getTypeGatewayMock() + { + if (!isset($this->typeGatewayMock)) { + $this->typeGatewayMock = $this->createMock(ContentTypeGateway::class); + } + + return $this->typeGatewayMock; + } + + /** + * Returns a mock object for the Content Gateway. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Gateway|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getGatewayMock() + { + if (!isset($this->gatewayMock)) { + try { + $this->gatewayMock = $this->getMockForAbstractClass(ContentGateway::class); + } catch (ReflectionException $e) { + self::fail($e); + } + } + + return $this->gatewayMock; + } + + /** + * Returns a mock object for the UrlAlias Handler. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter + */ + protected function getSlugConverterMock() + { + if (!isset($this->slugConverterMock)) { + $this->slugConverterMock = $this->createMock(SlugConverter::class); + } + + return $this->slugConverterMock; + } + + /** + * Returns a mock object for the UrlAlias Gateway. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway + */ + protected function getUrlAliasGatewayMock() + { + if (!isset($this->urlAliasGatewayMock)) { + $this->urlAliasGatewayMock = $this->getMockForAbstractClass(UrlAliasGateway::class); + } + + return $this->urlAliasGatewayMock; + } +} + +class_alias(ContentHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\ContentHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldHandlerTest.php b/tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php similarity index 90% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldHandlerTest.php rename to tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php index 93b010529c..a938b8b850 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldHandlerTest.php +++ b/tests/lib/Persistence/Legacy/Content/FieldHandlerTest.php @@ -4,61 +4,61 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content; - -use eZ\Publish\Core\Persistence\FieldTypeRegistry; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\UpdateStruct; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; -use eZ\Publish\SPI\Persistence\FieldType as SPIFieldType; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content; + +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\UpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Contracts\Core\Persistence\FieldType as SPIFieldType; +use Ibexa\Core\Persistence\FieldTypeRegistry; +use Ibexa\Core\Persistence\Legacy\Content\FieldHandler; +use Ibexa\Core\Persistence\Legacy\Content\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Mapper; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use Ibexa\Core\Persistence\Legacy\Content\StorageHandler; /** - * Test case for Content Handler. + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldHandler */ class FieldHandlerTest extends LanguageAwareTestCase { /** * Gateway mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway + * @var \Ibexa\Core\Persistence\Legacy\Content\Gateway */ protected $contentGatewayMock; /** * Mapper mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Mapper + * @var \Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected $mapperMock; /** * Storage handler mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler + * @var \Ibexa\Core\Persistence\Legacy\Content\StorageHandler */ protected $storageHandlerMock; /** * Field type registry mock. * - * @var \eZ\Publish\Core\Persistence\FieldTypeRegistry + * @var \Ibexa\Core\Persistence\FieldTypeRegistry */ protected $fieldTypeRegistryMock; /** * Field type mock. * - * @var \eZ\Publish\SPI\FieldType\FieldType + * @var \Ibexa\Contracts\Core\FieldType\FieldType */ protected $fieldTypeMock; @@ -124,9 +124,6 @@ protected function assertCreateNewFields($storageHandlerUpdatesFields = false) )->will($this->returnValue($storageHandlerUpdatesFields)); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::createNewFields - */ public function testCreateNewFields() { $fieldHandler = $this->getFieldHandler(); @@ -145,9 +142,6 @@ public function testCreateNewFields() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::createNewFields - */ public function testCreateNewFieldsUpdatingStorageHandler() { $fieldHandler = $this->getFieldHandler(); @@ -217,9 +211,6 @@ protected function assertCreateNewFieldsForMainLanguage($storageHandlerUpdatesFi } } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::createNewFields - */ public function testCreateNewFieldsForMainLanguage() { $fieldHandler = $this->getFieldHandler(); @@ -238,9 +229,6 @@ public function testCreateNewFieldsForMainLanguage() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::createNewFields - */ public function testCreateNewFieldsForMainLanguageUpdatingStorageHandler() { $fieldHandler = $this->getFieldHandler(); @@ -309,9 +297,6 @@ protected function assertCreateExistingFieldsInNewVersion($storageHandlerUpdates } } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::createExistingFieldsInNewVersion - */ public function testCreateExistingFieldsInNewVersion() { $fieldHandler = $this->getFieldHandler(); @@ -327,9 +312,6 @@ public function testCreateExistingFieldsInNewVersion() $fieldHandler->createExistingFieldsInNewVersion($this->getContentFixture()); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::createExistingFieldsInNewVersion - */ public function testCreateExistingFieldsInNewVersionUpdatingStorageHandler() { $fieldHandler = $this->getFieldHandler(); @@ -353,9 +335,6 @@ public function testCreateExistingFieldsInNewVersionUpdatingStorageHandler() $fieldHandler->createExistingFieldsInNewVersion($this->getContentFixture()); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::loadExternalFieldData - */ public function testLoadExternalFieldData() { $fieldHandler = $this->getFieldHandler(); @@ -432,9 +411,6 @@ public function assertUpdateFieldsWithNewLanguage($storageHandlerUpdatesFields = )->will($this->returnValue($storageHandlerUpdatesFields)); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::updateFields - */ public function testUpdateFieldsWithNewLanguage() { $mapperMock = $this->getMapperMock(); @@ -467,9 +443,6 @@ public function testUpdateFieldsWithNewLanguage() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::updateFields - */ public function testUpdateFieldsWithNewLanguageUpdatingStorageHandler() { $fieldHandler = $this->getFieldHandler(); @@ -563,9 +536,6 @@ public function assertUpdateFieldsExistingLanguages($storageHandlerUpdatesFields } } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::updateFields - */ public function testUpdateFieldsExistingLanguages() { $fieldHandler = $this->getFieldHandler(); @@ -593,9 +563,6 @@ public function testUpdateFieldsExistingLanguages() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::updateFields - */ public function testUpdateFieldsExistingLanguagesUpdatingStorageHandler() { $fieldHandler = $this->getFieldHandler(); @@ -675,9 +642,6 @@ public function assertUpdateFieldsForInitialLanguage($storageHandlerUpdatesField } } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::updateFields - */ public function testUpdateFieldsForInitialLanguage() { $fieldHandler = $this->getFieldHandler(); @@ -700,9 +664,6 @@ public function testUpdateFieldsForInitialLanguage() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::updateFields - */ public function testUpdateFieldsForInitialLanguageUpdatingStorageHandler() { $fieldHandler = $this->getFieldHandler(); @@ -733,9 +694,6 @@ public function testUpdateFieldsForInitialLanguageUpdatingStorageHandler() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler::deleteFields - */ public function testDeleteFields() { $fieldHandler = $this->getFieldHandler(); @@ -770,7 +728,7 @@ public function testDeleteFields() /** * Returns a Content fixture. * - * @return \eZ\Publish\SPI\Persistence\Content + * @return \Ibexa\Contracts\Core\Persistence\Content */ protected function getContentPartialFieldsFixture() { @@ -808,7 +766,7 @@ protected function getContentPartialFieldsFixture() /** * Returns a Content fixture. * - * @return \eZ\Publish\SPI\Persistence\Content + * @return \Ibexa\Contracts\Core\Persistence\Content */ protected function getContentNoFieldsFixture() { @@ -828,7 +786,7 @@ protected function getContentNoFieldsFixture() /** * Returns a Content fixture. * - * @return \eZ\Publish\SPI\Persistence\Content + * @return \Ibexa\Contracts\Core\Persistence\Content */ protected function getContentSingleLanguageFixture() { @@ -860,7 +818,7 @@ protected function getContentSingleLanguageFixture() /** * Returns a Content fixture. * - * @return \eZ\Publish\SPI\Persistence\Content + * @return \Ibexa\Contracts\Core\Persistence\Content */ protected function getContentFixture() { @@ -905,7 +863,7 @@ protected function getContentFixture() /** * Returns a ContentType fixture. * - * @return \eZ\Publish\SPI\Persistence\Content\Type + * @return \Ibexa\Contracts\Core\Persistence\Content\Type */ protected function getContentTypeFixture() { @@ -943,7 +901,7 @@ protected function getContentTypeFixture() /** * Returns an UpdateStruct fixture. * - * @return \eZ\Publish\SPI\Persistence\Content\UpdateStruct + * @return \Ibexa\Contracts\Core\Persistence\Content\UpdateStruct */ protected function getUpdateStructFixture() { @@ -968,7 +926,7 @@ protected function getUpdateStructFixture() /** * Returns a FieldHandler to test. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler + * @return \Ibexa\Core\Persistence\Legacy\Content\FieldHandler */ protected function getFieldHandler() { @@ -986,7 +944,7 @@ protected function getFieldHandler() /** * Returns a StorageHandler mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Persistence\Legacy\Content\StorageHandler|\PHPUnit\Framework\MockObject\MockObject */ protected function getStorageHandlerMock() { @@ -1000,7 +958,7 @@ protected function getStorageHandlerMock() /** * Returns a Mapper mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Mapper|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Persistence\Legacy\Content\Mapper|\PHPUnit\Framework\MockObject\MockObject */ protected function getMapperMock() { @@ -1014,7 +972,7 @@ protected function getMapperMock() /** * Returns a mock object for the Content Gateway. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Gateway|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Persistence\Legacy\Content\Gateway|\PHPUnit\Framework\MockObject\MockObject */ protected function getContentGatewayMock() { @@ -1026,7 +984,7 @@ protected function getContentGatewayMock() } /** - * @return \eZ\Publish\Core\Persistence\FieldTypeRegistry|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Persistence\FieldTypeRegistry|\PHPUnit\Framework\MockObject\MockObject */ protected function getFieldTypeRegistryMock() { @@ -1048,7 +1006,7 @@ protected function getFieldTypeRegistryMock() } /** - * @return \eZ\Publish\SPI\Persistence\FieldType|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\Persistence\FieldType|\PHPUnit\Framework\MockObject\MockObject */ protected function getFieldTypeMock() { @@ -1059,3 +1017,5 @@ protected function getFieldTypeMock() return $this->fieldTypeMock; } } + +class_alias(FieldHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldHandlerTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorTest.php new file mode 100644 index 0000000000..96e18e03fe --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/AuthorTest.php @@ -0,0 +1,164 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use DOMDocument; +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as SPIFieldDefinition; +use Ibexa\Core\FieldType\Author\Type as AuthorType; +use Ibexa\Core\FieldType\FieldSettings; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter + * + * @group fieldType + * @group ezauthor + */ +class AuthorTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\AuthorConverter */ + protected $converter; + + /** @var \Ibexa\Core\FieldType\Author\Author[] */ + private $authors; + + protected function setUp(): void + { + parent::setUp(); + $this->converter = new AuthorConverter(); + $this->authors = [ + ['id' => 21, 'name' => 'Boba Fett', 'email' => 'boba.fett@bountyhunters.com'], + ['id' => 42, 'name' => 'Darth Vader', 'email' => 'darth.vader@evilempire.biz'], + ['id' => 63, 'name' => 'Luke Skywalker', 'email' => 'luke@imtheone.net'], + ]; + } + + protected function tearDown(): void + { + unset($this->authors); + parent::tearDown(); + } + + public function testToStorageValue() + { + $value = new FieldValue(); + $value->data = $this->authors; + $storageFieldValue = new StorageFieldValue(); + + $this->converter->toStorageValue($value, $storageFieldValue); + $doc = new DOMDocument('1.0', 'utf-8'); + self::assertTrue($doc->loadXML($storageFieldValue->dataText)); + + $authorsXml = $doc->getElementsByTagName('author'); + self::assertSame(count($this->authors), $authorsXml->length); + + // Loop against XML nodes and compare them to the real Author objects. + // Then remove Author from $this->authors + // This way, we can check if all authors have been converted in XML + foreach ($authorsXml as $authorXml) { + foreach ($this->authors as $i => $author) { + if ($authorXml->getAttribute('id') == $author['id']) { + self::assertSame($author['name'], $authorXml->getAttribute('name')); + self::assertSame($author['email'], $authorXml->getAttribute('email')); + unset($this->authors[$i]); + break; + } + } + } + + self::assertEmpty($this->authors, 'All authors have not been converted as expected'); + } + + public function testToFieldValue() + { + $storageFieldValue = new StorageFieldValue(); + $storageFieldValue->dataText = <<<EOT +<?xml version="1.0" encoding="utf-8"?> +<ezauthor> + <authors> + <author id="1" name="Boba Fett" email="boba.fett@bountyhunters.com"/> + <author id="2" name="Darth Vader" email="darth.vader@evilempire.biz"/> + <author id="3" name="Luke Skywalker" email="luke@imtheone.net"/> + </authors> +</ezauthor> +EOT; + $doc = new DOMDocument('1.0', 'utf-8'); + self::assertTrue($doc->loadXML($storageFieldValue->dataText)); + $authorsXml = $doc->getElementsByTagName('author'); + $fieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $fieldValue); + self::assertIsArray($fieldValue->data); + + $authorsXml = $doc->getElementsByTagName('author'); + self::assertSame($authorsXml->length, count($fieldValue->data)); + + $aAuthors = $fieldValue->data; + foreach ($fieldValue->data as $i => $author) { + foreach ($authorsXml as $authorXml) { + if ($authorXml->getAttribute('id') == $author['id']) { + self::assertSame($authorXml->getAttribute('name'), $author['name']); + self::assertSame($authorXml->getAttribute('email'), $author['email']); + unset($aAuthors[$i]); + break; + } + } + } + self::assertEmpty($aAuthors, 'All authors have not been converted as expected from storage'); + } + + public function testToStorageFieldDefinitionDefaultCurrentUser() + { + $storageFieldDef = new StorageFieldDefinition(); + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'defaultAuthor' => AuthorType::DEFAULT_CURRENT_USER, + ] + ); + $fieldDef = new SPIFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + self::assertSame( + AuthorType::DEFAULT_CURRENT_USER, + $storageFieldDef->dataInt1 + ); + } + + public function testToStorageFieldDefinitionDefaultEmpty() + { + $storageFieldDef = new StorageFieldDefinition(); + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'defaultAuthor' => AuthorType::DEFAULT_VALUE_EMPTY, + ] + ); + $fieldDef = new SPIFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + self::assertSame( + AuthorType::DEFAULT_VALUE_EMPTY, + $storageFieldDef->dataInt1 + ); + } +} + +class_alias(AuthorTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\AuthorTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxTest.php new file mode 100644 index 0000000000..5d0dc8e2a6 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CheckboxTest.php @@ -0,0 +1,107 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter + */ +class CheckboxTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\CheckboxConverter */ + protected $converter; + + protected function setUp(): void + { + parent::setUp(); + $this->converter = new CheckboxConverter(); + } + + /** + * @group fieldType + * @group ezboolean + */ + public function testToStorageValue() + { + $value = new FieldValue(); + $value->data = true; + $value->sortKey = 1; + $storageFieldValue = new StorageFieldValue(); + + $this->converter->toStorageValue($value, $storageFieldValue); + self::assertSame((int)$value->data, $storageFieldValue->dataInt); + self::assertSame($value->sortKey, $storageFieldValue->sortKeyInt); + self::assertSame('', $storageFieldValue->sortKeyString); + } + + /** + * @group fieldType + * @group ezboolean + */ + public function testToFieldValue() + { + $storageFieldValue = new StorageFieldValue(); + $storageFieldValue->dataInt = 1; + $storageFieldValue->sortKeyInt = 1; + $storageFieldValue->sortKeyString = ''; + $fieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $fieldValue); + self::assertSame((bool)$storageFieldValue->dataInt, $fieldValue->data); + self::assertSame($storageFieldValue->sortKeyInt, $fieldValue->sortKey); + } + + /** + * @group fieldType + * @group ezboolean + */ + public function testToStorageFieldDefinition() + { + $defaultBool = false; + $storageFieldDef = new StorageFieldDefinition(); + $defaultValue = new FieldValue(); + $defaultValue->data = $defaultBool; + $fieldDef = new PersistenceFieldDefinition( + [ + 'defaultValue' => $defaultValue, + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + self::assertSame( + (int)$fieldDef->defaultValue->data, + $storageFieldDef->dataInt3 + ); + } + + /** + * @group fieldType + * @group ezboolean + */ + public function testToFieldDefinition() + { + $defaultBool = true; + $fieldDef = new PersistenceFieldDefinition(); + $storageDef = new StorageFieldDefinition( + [ + 'dataInt3' => 1, + ] + ); + + $this->converter->toFieldDefinition($storageDef, $fieldDef); + self::assertSame($defaultBool, $fieldDef->defaultValue->data); + self::assertNull($fieldDef->fieldTypeConstraints->fieldSettings); + } +} + +class_alias(CheckboxTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\CheckboxTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php new file mode 100644 index 0000000000..e65b6300be --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/CountryTest.php @@ -0,0 +1,201 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\FieldType\FieldSettings; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter + */ +class CountryTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\CountryConverter */ + protected $converter; + + protected function setUp(): void + { + parent::setUp(); + $this->converter = new CountryConverter(); + } + + public function providerForTestToStorageValue() + { + return [ + [['BE', 'FR'], 'belgium,france', 'BE,FR', 'belgium,france'], + [null, '', '', ''], + ]; + } + + /** + * @group fieldType + * @group country + * @dataProvider providerForTestToStorageValue + */ + public function testToStorageValue($data, $sortKey, $dataText, $sortKeyString) + { + $value = new FieldValue(); + $value->data = $data; + $value->sortKey = $sortKey; + $storageFieldValue = new StorageFieldValue(); + + $this->converter->toStorageValue($value, $storageFieldValue); + self::assertSame($dataText, $storageFieldValue->dataText); + self::assertSame($sortKeyString, $storageFieldValue->sortKeyString); + } + + public function providerForTestToFieldValue() + { + return [ + ['BE,FR', 'belgium,france', ['BE', 'FR']], + ['', '', null], + ]; + } + + /** + * @group fieldType + * @group country + * @dataProvider providerForTestToFieldValue + */ + public function testToFieldValue($dataText, $sortKeyString, $data) + { + $storageFieldValue = new StorageFieldValue(); + $storageFieldValue->dataText = $dataText; + $storageFieldValue->sortKeyString = $sortKeyString; + $fieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $fieldValue); + self::assertSame($data, $fieldValue->data); + } + + /** + * @group fieldType + * @group country + */ + public function testToStorageFieldDefinitionMultiple() + { + $defaultValue = new FieldValue(); + $defaultValue->data = ['BE', 'FR']; + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'isMultiple' => true, + ] + ); + + $storageFieldDef = new StorageFieldDefinition(); + $this->converter->toStorageFieldDefinition( + new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + 'defaultValue' => $defaultValue, + ] + ), + $storageFieldDef + ); + self::assertSame( + 1, + $storageFieldDef->dataInt1 + ); + self::assertSame( + 'BE,FR', + $storageFieldDef->dataText5 + ); + } + + /** + * @group fieldType + * @group country + */ + public function testToStorageFieldDefinitionSingle() + { + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'isMultiple' => false, + ] + ); + + $storageFieldDef = new StorageFieldDefinition(); + $this->converter->toStorageFieldDefinition( + new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + ] + ), + $storageFieldDef + ); + self::assertSame( + 0, + $storageFieldDef->dataInt1 + ); + self::assertEmpty( + $storageFieldDef->dataText5 + ); + } + + /** + * @group fieldType + * @group country + */ + public function testToFieldDefinitionMultiple() + { + $fieldDef = new PersistenceFieldDefinition(); + + $this->converter->toFieldDefinition( + new StorageFieldDefinition( + [ + 'dataInt1' => 1, + 'dataText5' => 'BE,FR', + ] + ), + $fieldDef + ); + self::assertInstanceOf(FieldSettings::class, $fieldDef->fieldTypeConstraints->fieldSettings); + self::assertTrue( + $fieldDef->fieldTypeConstraints->fieldSettings['isMultiple'] + ); + self::assertEquals( + ['BE', 'FR'], + $fieldDef->defaultValue->data + ); + } + + /** + * @group fieldType + * @group country + */ + public function testToFieldDefinitionSingle() + { + $fieldDef = new PersistenceFieldDefinition(); + + $this->converter->toFieldDefinition( + new StorageFieldDefinition( + [ + 'dataInt1' => 0, + 'dataText5' => '', + ] + ), + $fieldDef + ); + self::assertInstanceOf(FieldSettings::class, $fieldDef->fieldTypeConstraints->fieldSettings); + self::assertFalse( + $fieldDef->fieldTypeConstraints->fieldSettings['isMultiple'] + ); + self::assertNull( + $fieldDef->defaultValue->data + ); + } +} + +class_alias(CountryTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\CountryTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php new file mode 100644 index 0000000000..0ab500db69 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateAndTimeTest.php @@ -0,0 +1,385 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use DateInterval; +use DateTime; +use DOMDocument; +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\FieldType\DateAndTime\Type as DateAndTimeType; +use Ibexa\Core\FieldType\FieldSettings; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use PHPUnit\Framework\TestCase; +use ReflectionObject; +use SimpleXMLElement; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter + */ +class DateAndTimeTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter */ + protected $converter; + + /** @var \DateTime */ + protected $date; + + protected function setUp(): void + { + parent::setUp(); + $this->converter = new DateAndTimeConverter(); + $this->date = new DateTime('@1048633200'); + } + + /** + * @group fieldType + * @group dateTime + */ + public function testToStorageValue() + { + $value = new FieldValue(); + $value->data = [ + 'timestamp' => $this->date->getTimestamp(), + 'rfc850' => $this->date->format(\DateTime::RFC850), + ]; + $value->sortKey = $this->date->getTimestamp(); + $storageFieldValue = new StorageFieldValue(); + + $this->converter->toStorageValue($value, $storageFieldValue); + self::assertSame($value->data['timestamp'], $storageFieldValue->dataInt); + self::assertSame($value->sortKey, $storageFieldValue->sortKeyInt); + self::assertSame('', $storageFieldValue->sortKeyString); + } + + /** + * @group fieldType + * @group dateTime + */ + public function testToFieldValue() + { + $storageFieldValue = new StorageFieldValue(); + $storageFieldValue->dataInt = $this->date->getTimestamp(); + $storageFieldValue->sortKeyString = ''; + $storageFieldValue->sortKeyInt = $this->date->getTimestamp(); + $fieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $fieldValue); + self::assertSame( + [ + 'rfc850' => null, + 'timestamp' => 1048633200, + ], + $fieldValue->data + ); + self::assertSame($storageFieldValue->dataInt, $fieldValue->data['timestamp']); + self::assertSame($storageFieldValue->sortKeyInt, $fieldValue->sortKey); + } + + /** + * @group fieldType + * @group dateTime + */ + public function testToStorageFieldDefinitionWithAdjustment() + { + $storageFieldDef = new StorageFieldDefinition(); + $dateInterval = DateInterval::createFromDateString('+10 years, -1 month, +3 days, -13 hours'); + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'useSeconds' => true, + 'defaultType' => DateAndTimeType::DEFAULT_CURRENT_DATE_ADJUSTED, + 'dateInterval' => $dateInterval, + ] + ); + $fieldDef = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + self::assertSame( + DateAndTimeType::DEFAULT_CURRENT_DATE_ADJUSTED, + $storageFieldDef->dataInt1 + ); + self::assertSame( + 1, + $storageFieldDef->dataInt2 + ); + + $xml = new SimpleXMLElement($storageFieldDef->dataText5); + foreach ($this->getXMLToDateIntervalMap() as $xmlNode => $property) { + self::assertSame( + $dateInterval->format("%$property"), + (string)$xml->{$xmlNode}['value'] + ); + } + } + + /** + * @group fieldType + * @group dateTime + */ + public function testToStorageFieldDefinitionNoDefault() + { + $storageFieldDef = new StorageFieldDefinition(); + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'useSeconds' => true, + 'defaultType' => DateAndTimeType::DEFAULT_EMPTY, + 'dateInterval' => null, + ] + ); + $fieldDef = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + self::assertSame( + DateAndTimeType::DEFAULT_EMPTY, + $storageFieldDef->dataInt1 + ); + self::assertSame( + 1, + $storageFieldDef->dataInt2 + ); + self::assertNull($storageFieldDef->dataText5); + } + + /** + * @group fieldType + * @group dateTime + */ + public function testToStorageFieldDefinitionCurrentDate() + { + $storageFieldDef = new StorageFieldDefinition(); + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'useSeconds' => true, + 'defaultType' => DateAndTimeType::DEFAULT_CURRENT_DATE, + 'dateInterval' => null, + ] + ); + $fieldDef = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + self::assertSame( + DateAndTimeType::DEFAULT_CURRENT_DATE, + $storageFieldDef->dataInt1 + ); + self::assertSame( + 1, + $storageFieldDef->dataInt2 + ); + self::assertNull($storageFieldDef->dataText5); + } + + /** + * Returns map from internal XML nodes to DateInterval properties for date adjustment. + * + * @return array Key is the XML node name, value is the DateInterval property + */ + private function getXMLToDateIntervalMap() + { + return [ + 'year' => 'y', + 'month' => 'm', + 'day' => 'd', + 'hour' => 'h', + 'minute' => 'i', + 'second' => 's', + ]; + } + + /** + * @group fieldType + * @group dateTime + */ + public function testToFieldDefinitionNoDefault() + { + $fieldDef = new PersistenceFieldDefinition(); + $storageDef = new StorageFieldDefinition( + [ + 'dataInt1' => DateAndTimeType::DEFAULT_EMPTY, + 'dataInt2' => 1, + ] + ); + + $this->converter->toFieldDefinition($storageDef, $fieldDef); + self::assertNull($fieldDef->defaultValue->data); + } + + /** + * @group fieldType + * @group dateTime + */ + public function testToFieldDefinitionCurrentDate() + { + $time = time(); + $fieldDef = new PersistenceFieldDefinition(); + $storageDef = new StorageFieldDefinition( + [ + 'dataInt1' => DateAndTimeType::DEFAULT_CURRENT_DATE, + 'dataInt2' => 1, + ] + ); + + $this->converter->toFieldDefinition($storageDef, $fieldDef); + sleep(1); + $dateTimeFromString = new DateTime($fieldDef->defaultValue->data['timestring']); + + self::assertIsArray($fieldDef->defaultValue->data); + self::assertCount(3, $fieldDef->defaultValue->data); + self::assertNull($fieldDef->defaultValue->data['rfc850']); + self::assertGreaterThanOrEqual($time, $fieldDef->defaultValue->data['timestamp']); + self::assertEqualsWithDelta($time + 1, $dateTimeFromString->getTimestamp(), 1, 'Time does not match within 1s delta'); + } + + /** + * @group fieldType + * @group dateTime + */ + public function testToFieldDefinitionWithAdjustmentAndSeconds() + { + $fieldDef = new PersistenceFieldDefinition(); + $dateInterval = DateInterval::createFromDateString('2 years, 1 month, -4 days, 2 hours, 0 minute, 34 seconds'); + $date = new DateTime(); + $date->add($dateInterval); + $timestamp = $date->getTimestamp(); + + $storageDef = new StorageFieldDefinition( + [ + 'dataInt1' => DateAndTimeType::DEFAULT_CURRENT_DATE_ADJUSTED, + 'dataInt2' => 1, + 'dataText5' => $this->getXMLStringFromDateInterval($dateInterval), + ] + ); + + $this->converter->toFieldDefinition($storageDef, $fieldDef); + $dateTimeFromString = new DateTime($fieldDef->defaultValue->data['timestring']); + + self::assertIsArray($fieldDef->defaultValue->data); + self::assertCount(3, $fieldDef->defaultValue->data); + self::assertNull($fieldDef->defaultValue->data['rfc850']); + self::assertGreaterThanOrEqual($timestamp, $fieldDef->defaultValue->data['timestamp']); + self::assertGreaterThanOrEqual($timestamp, $dateTimeFromString->getTimestamp()); + // Giving a margin of 1 second for test execution + self::assertLessThanOrEqual($timestamp + 1, $fieldDef->defaultValue->data['timestamp']); + self::assertLessThanOrEqual($timestamp + 1, $dateTimeFromString->getTimestamp()); + } + + /** + * @group fieldType + * @group dateTime + */ + public function testToFieldDefinitionWithAdjustmentNoSeconds() + { + $fieldDef = new PersistenceFieldDefinition(); + $seconds = 34; + $dateInterval = DateInterval::createFromDateString("2 years, 1 month, -4 days, 2 hours, 0 minute, $seconds seconds"); + $date = new DateTime(); + $date->add($dateInterval); + // Removing $seconds as they're not supposed to be taken into account + $timestamp = $date->getTimestamp() - $seconds; + + $storageDef = new StorageFieldDefinition( + [ + 'dataInt1' => DateAndTimeType::DEFAULT_CURRENT_DATE_ADJUSTED, + 'dataInt2' => 0, + 'dataText5' => $this->getXMLStringFromDateInterval($dateInterval), + ] + ); + + $this->converter->toFieldDefinition($storageDef, $fieldDef); + $dateTimeFromString = new DateTime($fieldDef->defaultValue->data['timestring']); + + self::assertIsArray($fieldDef->defaultValue->data); + self::assertCount(3, $fieldDef->defaultValue->data); + self::assertNull($fieldDef->defaultValue->data['rfc850']); + self::assertGreaterThanOrEqual($timestamp, $fieldDef->defaultValue->data['timestamp']); + self::assertGreaterThanOrEqual($timestamp, $dateTimeFromString->getTimestamp()); + // Giving a margin of 1 second for test execution + self::assertLessThanOrEqual($timestamp + 1, $fieldDef->defaultValue->data['timestamp']); + self::assertLessThanOrEqual($timestamp + 1, $dateTimeFromString->getTimestamp()); + } + + /** + * Generates XML string from $dateInterval. + * + * @param \DateInterval $dateInterval + * + * @return string + */ + private function getXMLStringFromDateInterval(DateInterval $dateInterval) + { + $xmlString = <<<EOT +<?xml version="1.0" encoding="utf-8"?> +<adjustment> + <year value="$dateInterval->y"/> + <month value="$dateInterval->m"/> + <day value="$dateInterval->d"/> + <hour value="$dateInterval->h"/> + <minute value="$dateInterval->i"/> + <second value="$dateInterval->s"/> +</adjustment> +EOT; + + return $xmlString; + } + + /** + * @group fieldType + * @group dateTime + */ + public function testGetDateIntervalFromXML() + { + $dateIntervalReference = DateInterval::createFromDateString('2 years, 1 month, -4 days, 2 hours, 0 minute, 34 seconds'); + + $refConverter = new ReflectionObject($this->converter); + $refMethod = $refConverter->getMethod('getDateIntervalFromXML'); + $refMethod->setAccessible(true); + $generatedDateInterval = $refMethod->invoke( + $this->converter, + $this->getXMLStringFromDateInterval($dateIntervalReference) + ); + self::assertEquals($dateIntervalReference, $generatedDateInterval); + } + + /** + * @group fieldType + * @group dateTime + */ + public function testGenerateDateIntervalXML() + { + $dateIntervalReference = DateInterval::createFromDateString('2 years, 1 month, -4 days, 2 hours, 0 minute, 34 seconds'); + $dom = new DOMDocument(); + $dom->preserveWhiteSpace = false; + $dom->loadXML($this->getXMLStringFromDateInterval($dateIntervalReference)); + + $refConverter = new ReflectionObject($this->converter); + $refMethod = $refConverter->getMethod('generateDateIntervalXML'); + $refMethod->setAccessible(true); + self::assertEquals( + $dom->saveXML(), + $refMethod->invoke($this->converter, $dateIntervalReference) + ); + } +} + +class_alias(DateAndTimeTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\DateAndTimeTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateTest.php new file mode 100644 index 0000000000..f2e32c8cfa --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/DateTest.php @@ -0,0 +1,153 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use DateTime; +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\FieldType\Date\Type as DateType; +use Ibexa\Core\FieldType\FieldSettings; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter + * + * @group fieldType + * @group date + */ +class DateTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\DateConverter */ + protected $converter; + + /** @var \DateTime */ + protected $date; + + protected function setUp(): void + { + parent::setUp(); + $this->converter = new DateConverter(); + $this->date = new DateTime('@1362614400'); + } + + public function testToStorageValue() + { + $value = new FieldValue(); + $value->data = [ + 'timestamp' => $this->date->getTimestamp(), + 'rfc850' => $this->date->format(\DateTime::RFC850), + ]; + $value->sortKey = $this->date->getTimestamp(); + $storageFieldValue = new StorageFieldValue(); + + $this->converter->toStorageValue($value, $storageFieldValue); + self::assertSame($value->data['timestamp'], $storageFieldValue->dataInt); + self::assertSame($value->sortKey, $storageFieldValue->sortKeyInt); + self::assertSame('', $storageFieldValue->sortKeyString); + } + + public function testToFieldValue() + { + $storageFieldValue = new StorageFieldValue(); + $storageFieldValue->dataInt = $this->date->getTimestamp(); + $storageFieldValue->sortKeyString = ''; + $storageFieldValue->sortKeyInt = $this->date->getTimestamp(); + $fieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $fieldValue); + self::assertSame( + [ + 'timestamp' => $this->date->getTimestamp(), + 'rfc850' => null, + ], + $fieldValue->data + ); + self::assertSame($storageFieldValue->dataInt, $fieldValue->data['timestamp']); + self::assertSame($storageFieldValue->sortKeyInt, $fieldValue->sortKey); + } + + public function testToStorageFieldDefinitionDefaultEmpty() + { + $storageFieldDef = new StorageFieldDefinition(); + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'defaultType' => DateType::DEFAULT_EMPTY, + ] + ); + $fieldDef = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + self::assertSame( + DateType::DEFAULT_EMPTY, + $storageFieldDef->dataInt1 + ); + } + + public function testToStorageFieldDefinitionDefaultCurrentDate() + { + $storageFieldDef = new StorageFieldDefinition(); + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'defaultType' => DateType::DEFAULT_CURRENT_DATE, + ] + ); + $fieldDef = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + self::assertSame( + DateType::DEFAULT_CURRENT_DATE, + $storageFieldDef->dataInt1 + ); + } + + public function testToFieldDefinitionDefaultEmpty() + { + $fieldDef = new PersistenceFieldDefinition(); + $storageDef = new StorageFieldDefinition( + [ + 'dataInt1' => DateType::DEFAULT_EMPTY, + ] + ); + + $this->converter->toFieldDefinition($storageDef, $fieldDef); + self::assertNull($fieldDef->defaultValue->data); + } + + public function testToFieldDefinitionDefaultCurrentDate() + { + $timestamp = time(); + $fieldDef = new PersistenceFieldDefinition(); + $storageDef = new StorageFieldDefinition( + [ + 'dataInt1' => DateType::DEFAULT_CURRENT_DATE, + ] + ); + + $this->converter->toFieldDefinition($storageDef, $fieldDef); + self::assertIsArray($fieldDef->defaultValue->data); + self::assertCount(3, $fieldDef->defaultValue->data); + self::assertNull($fieldDef->defaultValue->data['rfc850']); + self::assertSame($timestamp, $fieldDef->defaultValue->data['timestamp']); + self::assertSame('now', $fieldDef->defaultValue->data['timestring']); + } +} + +class_alias(DateTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\DateTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNTest.php new file mode 100644 index 0000000000..f0a03deeef --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/ISBNTest.php @@ -0,0 +1,54 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\ISBNConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\ISBNConverter + */ +class ISBNTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\ISBNConverter */ + protected $converter; + + protected function setUp(): void + { + $this->converter = new ISBNConverter(); + } + + /** + * @dataProvider providerForTestToFieldDefinition + */ + public function testToFieldDefinition($dataInt, $excpectedIsbn13Value) + { + $fieldDef = new PersistenceFieldDefinition(); + $storageDefinition = new StorageFieldDefinition([ + 'dataInt1' => $dataInt, + ]); + + $this->converter->toFieldDefinition($storageDefinition, $fieldDef); + + /** @var \Ibexa\Core\FieldType\FieldSettings $fieldSettings */ + $fieldSettings = $fieldDef->fieldTypeConstraints->fieldSettings; + self::assertSame($excpectedIsbn13Value, $fieldSettings['isISBN13']); + } + + public function providerForTestToFieldDefinition() + { + return [ + [1, true], + [0, false], + [null, false], + ]; + } +} + +class_alias(ISBNTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\ISBNTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordTest.php new file mode 100644 index 0000000000..fcdad63838 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/KeywordTest.php @@ -0,0 +1,82 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter + */ +class KeywordTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\KeywordConverter */ + protected $converter; + + protected function setUp(): void + { + parent::setUp(); + $this->converter = new KeywordConverter(); + } + + /** + * @group fieldType + * @group keyword + */ + public function testToStorageValue() + { + $value = new FieldValue(); + $value->data = ['key1', 'key2']; + $value->sortKey = false; + $storageFieldValue = new StorageFieldValue(); + + $this->converter->toStorageValue($value, $storageFieldValue); + $this->assertNull($storageFieldValue->dataText); + $this->assertNull($storageFieldValue->dataInt); + $this->assertNull($storageFieldValue->dataFloat); + $this->assertEquals(0, $storageFieldValue->sortKeyInt); + $this->assertEquals('', $storageFieldValue->sortKeyString); + } + + /** + * @group fieldType + * @group keyword + */ + public function testToFieldValue() + { + $storageFieldValue = new StorageFieldValue(); + $fieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $fieldValue); + $this->assertSame([], $fieldValue->data); + $this->assertEquals('', $fieldValue->sortKey); + } + + /** + * @group fieldType + * @group keyword + */ + public function testToStorageFieldDefinition() + { + $this->converter->toStorageFieldDefinition(new PersistenceFieldDefinition(), new StorageFieldDefinition()); + } + + /** + * @group fieldType + * @group keyword + */ + public function testToFieldDefinition() + { + $this->converter->toFieldDefinition(new StorageFieldDefinition(), new PersistenceFieldDefinition()); + } +} + +class_alias(KeywordTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\KeywordTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/MediaTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/MediaTest.php new file mode 100644 index 0000000000..296e05bef1 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/MediaTest.php @@ -0,0 +1,96 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\FieldType\FieldSettings; +use Ibexa\Core\FieldType\Media\Type as MediaType; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\MediaConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\MediaConverter + */ +class MediaTest extends TestCase +{ + protected $converter; + + protected function setUp(): void + { + $this->converter = new MediaConverter(); + } + + /** + * @group fieldType + * @group ezmedia + */ + public function testToStorageFieldDefinition() + { + $storageFieldDef = new StorageFieldDefinition(); + + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->validators = [ + // Setting max file size to 1MB (1.048.576 bytes) + 'FileSizeValidator' => ['maxFileSize' => 1048576], + ]; + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'mediaType' => MediaType::TYPE_HTML5_VIDEO, + ] + ); + + $fieldDef = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + 'defaultValue' => null, + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + + self::assertSame( + $fieldDef->fieldTypeConstraints->validators['FileSizeValidator'], + ['maxFileSize' => $storageFieldDef->dataInt1] + ); + self::assertSame( + $fieldDef->fieldTypeConstraints->fieldSettings['mediaType'], + $storageFieldDef->dataText1 + ); + } + + /** + * @group fieldType + * @group ezmedia + */ + public function testToFieldDefinition() + { + $fieldDef = new PersistenceFieldDefinition(); + $storageDef = new StorageFieldDefinition( + [ + 'dataInt1' => 1048576, + 'dataText1' => MediaType::TYPE_HTML5_VIDEO, + ] + ); + + $this->converter->toFieldDefinition($storageDef, $fieldDef); + self::assertSame( + [ + 'FileSizeValidator' => ['maxFileSize' => $storageDef->dataInt1], + ], + $fieldDef->fieldTypeConstraints->validators + ); + self::assertInstanceOf(FieldSettings::class, $fieldDef->fieldTypeConstraints->fieldSettings); + self::assertSame( + ['mediaType' => MediaType::TYPE_HTML5_VIDEO], + $fieldDef->fieldTypeConstraints->fieldSettings->getArrayCopy() + ); + } +} + +class_alias(MediaTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\MediaTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListTest.php new file mode 100644 index 0000000000..7cb5158b5c --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationListTest.php @@ -0,0 +1,295 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\FieldType\RelationList\Type; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationListConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationListConverter + */ +class RelationListTest extends TestCase +{ + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationListConverter */ + protected $converter; + + protected function setUp(): void + { + parent::setUp(); + $this->converter = $this + ->getMockBuilder(RelationListConverter::class) + ->disableOriginalConstructor() + ->setMethods(['getRelationXmlHashFromDB']) + ->getMock(); + } + + /** + * @group fieldType + * @group relationlist + */ + public function testToStorageValue() + { + $destinationContentIds = [3, 2, 1]; + $fieldValue = new FieldValue(); + $fieldValue->sortKey = false; + $fieldValue->data = ['destinationContentIds' => $destinationContentIds]; + + $expectedStorageFieldValue = new StorageFieldValue(); + $expectedStorageFieldValue->dataText = <<<EOT +<?xml version="1.0" encoding="utf-8"?> +<related-objects><relation-list><relation-item priority="1" contentobject-id="3" contentobject-version="33" node-id="35" parent-node-id="36" contentclass-id="34" contentclass-identifier="37" contentobject-remote-id="32"/><relation-item priority="2" contentobject-id="2" contentobject-version="23" node-id="25" parent-node-id="26" contentclass-id="24" contentclass-identifier="27" contentobject-remote-id="22"/><relation-item priority="3" contentobject-id="1" contentobject-version="13" node-id="15" parent-node-id="16" contentclass-id="14" contentclass-identifier="17" contentobject-remote-id="12"/></relation-list></related-objects> + +EOT; + + $actualStorageFieldValue = new StorageFieldValue(); + + $this->converter + ->expects($this->once()) + ->method('getRelationXmlHashFromDB') + ->with($destinationContentIds) + ->will( + $this->returnValue( + [ + '1' => [ + [ + 'ezcontentobject_remote_id' => '12', + 'ezcontentobject_current_version' => '13', + 'ezcontentobject_contentclass_id' => '14', + 'ezcontentobject_tree_node_id' => '15', + 'ezcontentobject_tree_parent_node_id' => '16', + 'ezcontentclass_identifier' => '17', + ], + ], + '3' => [ + [ + 'ezcontentobject_remote_id' => '32', + 'ezcontentobject_current_version' => '33', + 'ezcontentobject_contentclass_id' => '34', + 'ezcontentobject_tree_node_id' => '35', + 'ezcontentobject_tree_parent_node_id' => '36', + 'ezcontentclass_identifier' => '37', + ], + ], + '2' => [ + [ + 'ezcontentobject_remote_id' => '22', + 'ezcontentobject_current_version' => '23', + 'ezcontentobject_contentclass_id' => '24', + 'ezcontentobject_tree_node_id' => '25', + 'ezcontentobject_tree_parent_node_id' => '26', + 'ezcontentclass_identifier' => '27', + ], + ], + ] + ) + ); + + $this->converter->toStorageValue($fieldValue, $actualStorageFieldValue); + + $this->assertEquals( + $expectedStorageFieldValue, + $actualStorageFieldValue + ); + } + + /** + * @group fieldType + * @group relationlist + */ + public function testToStorageValueEmpty() + { + $destinationContentIds = []; + $fieldValue = new FieldValue(); + $fieldValue->sortKey = false; + $fieldValue->data = ['destinationContentIds' => $destinationContentIds]; + + $expectedStorageFieldValue = new StorageFieldValue(); + $expectedStorageFieldValue->dataText = <<<EOT +<?xml version="1.0" encoding="utf-8"?> +<related-objects><relation-list/></related-objects> + +EOT; + + $actualStorageFieldValue = new StorageFieldValue(); + + $this->converter + ->expects($this->once()) + ->method('getRelationXmlHashFromDB') + ->with($destinationContentIds) + ->will($this->returnValue([])); + + $this->converter->toStorageValue($fieldValue, $actualStorageFieldValue); + + $this->assertEquals( + $expectedStorageFieldValue, + $actualStorageFieldValue + ); + } + + /** + * @group fieldType + * @group relationlist + */ + public function testToFieldValue() + { + $storageFieldValue = new StorageFieldValue(); + $storageFieldValue->sortKeyString = ''; + $storageFieldValue->dataText = <<<EOT +<?xml version="1.0" encoding="utf-8"?> +<related-objects><relation-list><relation-item priority="2" contentobject-id="2" contentobject-version="23" node-id="25" parent-node-id="26" contentclass-id="24" contentclass-identifier="27" contentobject-remote-id="22"/><relation-item priority="3" contentobject-id="1" contentobject-version="13" node-id="15" parent-node-id="16" contentclass-id="14" contentclass-identifier="17" contentobject-remote-id="12"/><relation-item priority="1" contentobject-id="3" contentobject-version="33" node-id="35" parent-node-id="36" contentclass-id="34" contentclass-identifier="37" contentobject-remote-id="32"/></relation-list></related-objects> + +EOT; + + $expectedFieldValue = new FieldValue(); + $expectedFieldValue->data = ['destinationContentIds' => [3, 2, 1]]; + + $actualFieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $actualFieldValue); + + $this->assertEquals( + $expectedFieldValue, + $actualFieldValue + ); + } + + /** + * @group fieldType + * @group relationlist + */ + public function testToFieldValueEmpty() + { + $storageFieldValue = new StorageFieldValue(); + $storageFieldValue->sortKeyString = ''; + $storageFieldValue->dataText = <<<EOT +<?xml version="1.0" encoding="utf-8"?> +<related-objects><relation-list/></related-objects> + +EOT; + + $expectedFieldValue = new FieldValue(); + $expectedFieldValue->data = ['destinationContentIds' => []]; + + $actualFieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $actualFieldValue); + + $this->assertEquals( + $expectedFieldValue, + $actualFieldValue + ); + } + + /** + * @group fieldType + * @group relationlist + */ + public function testToStorageFieldDefinition() + { + $fieldDefinition = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => new FieldTypeConstraints( + [ + 'fieldSettings' => [ + 'selectionMethod' => Type::SELECTION_BROWSE, + 'selectionDefaultLocation' => 12345, + 'selectionContentTypes' => ['article', 'blog_post'], + 'rootDefaultLocation' => true, + ], + 'validators' => [ + 'RelationListValueValidator' => [ + 'selectionLimit' => 5, + ], + ], + ] + ), + ] + ); + + $expectedStorageFieldDefinition = new StorageFieldDefinition(); + $expectedStorageFieldDefinition->dataText5 = <<<EOT +<?xml version="1.0" encoding="utf-8"?> +<related-objects><constraints><allowed-class contentclass-identifier="article"/><allowed-class contentclass-identifier="blog_post"/></constraints><type value="2"/><object_class value=""/><selection_type value="0"/><contentobject-placement node-id="12345"/><root_default_location value="1"/><selection_limit value="5"/></related-objects> + +EOT; + + $actualStorageFieldDefinition = new StorageFieldDefinition(); + + $this->converter->toStorageFieldDefinition($fieldDefinition, $actualStorageFieldDefinition); + + $this->assertEquals( + $expectedStorageFieldDefinition, + $actualStorageFieldDefinition + ); + } + + /** + * @group fieldType + * @group relationlist + */ + public function testToFieldDefinitionMultiple() + { + $storageFieldDefinition = new StorageFieldDefinition(); + $storageFieldDefinition->dataText5 = <<<EOT +<?xml version="1.0" encoding="utf-8"?> +<related-objects> + <constraints> + <allowed-class contentclass-identifier="forum"/> + <allowed-class contentclass-identifier="folder"/> + </constraints><type value="2"/> + <object_class value=""/> + <selection_type value="1"/> + <selection_limit value="1"/> + <root_default_location value="1"/> + <contentobject-placement node-id="54321"/> +</related-objects> + +EOT; + + $expectedFieldDefinition = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => new FieldTypeConstraints( + [ + 'fieldSettings' => [ + 'selectionMethod' => Type::SELECTION_DROPDOWN, + 'selectionDefaultLocation' => 54321, + 'selectionContentTypes' => ['forum', 'folder'], + 'rootDefaultLocation' => true, + ], + 'validators' => [ + 'RelationListValueValidator' => [ + 'selectionLimit' => 1, + ], + ], + ] + ), + 'defaultValue' => new FieldValue( + [ + 'data' => ['destinationContentIds' => []], + ] + ), + ] + ); + + $actualFieldDefinition = new PersistenceFieldDefinition(); + + $this->converter->toFieldDefinition($storageFieldDefinition, $actualFieldDefinition); + + $this->assertEquals( + $expectedFieldDefinition, + $actualFieldDefinition + ); + } +} + +class_alias(RelationListTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\RelationListTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationTest.php new file mode 100644 index 0000000000..b360c5f9f4 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/RelationTest.php @@ -0,0 +1,72 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\FieldType\RelationList\Type; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationConverter + */ +class RelationTest extends TestCase +{ + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\RelationConverter */ + protected $converter; + + protected function setUp(): void + { + parent::setUp(); + $this->converter = new RelationConverter(); + } + + /** + * @group fieldType + * @group relationlist + */ + public function testToStorageFieldDefinition() + { + $fieldDefinition = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => new FieldTypeConstraints( + [ + 'fieldSettings' => [ + 'selectionMethod' => Type::SELECTION_BROWSE, + 'selectionRoot' => 12345, + 'selectionContentTypes' => ['article', 'blog_post'], + 'rootDefaultLocation' => true, + ], + ] + ), + ] + ); + + $expectedStorageFieldDefinition = new StorageFieldDefinition(); + $expectedStorageFieldDefinition->dataText5 = <<<EOT +<?xml version="1.0" encoding="utf-8"?> +<related-objects><constraints><allowed-class contentclass-identifier="article"/><allowed-class contentclass-identifier="blog_post"/></constraints><selection_type value="0"/><root_default_location value="1"/><contentobject-placement node-id="12345"/></related-objects> + +EOT; + // For BC these are still set + $expectedStorageFieldDefinition->dataInt1 = 0; + $expectedStorageFieldDefinition->dataInt2 = 12345; + + $actualStorageFieldDefinition = new StorageFieldDefinition(); + + $this->converter->toStorageFieldDefinition($fieldDefinition, $actualStorageFieldDefinition); + + $this->assertEquals( + $expectedStorageFieldDefinition, + $actualStorageFieldDefinition + ); + } +} + +class_alias(RelationTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\RelationTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionTest.php new file mode 100644 index 0000000000..24aab067b1 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SelectionTest.php @@ -0,0 +1,330 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\FieldType\FieldSettings; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter + */ +class SelectionTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\SelectionConverter */ + protected $converter; + + protected function setUp(): void + { + parent::setUp(); + + $this->converter = new SelectionConverter(); + } + + /** + * @group fieldType + * @group selection + */ + public function testToStorageValue() + { + $fieldValue = new FieldValue(); + $fieldValue->data = [1, 3]; + $fieldValue->sortKey = '1-3'; + + $expectedStorageFieldValue = new StorageFieldValue(); + $expectedStorageFieldValue->dataText = '1-3'; + $expectedStorageFieldValue->sortKeyString = '1-3'; + + $actualStorageFieldValue = new StorageFieldValue(); + + $this->converter->toStorageValue($fieldValue, $actualStorageFieldValue); + + $this->assertEquals( + $expectedStorageFieldValue, + $actualStorageFieldValue + ); + } + + /** + * @group fieldType + * @group selection + */ + public function testToStorageValueEmpty() + { + $fieldValue = new FieldValue(); + $fieldValue->data = []; + $fieldValue->sortKey = ''; + + $expectedStorageFieldValue = new StorageFieldValue(); + $expectedStorageFieldValue->dataText = ''; + $expectedStorageFieldValue->sortKeyString = ''; + + $actualStorageFieldValue = new StorageFieldValue(); + + $this->converter->toStorageValue($fieldValue, $actualStorageFieldValue); + + $this->assertEquals( + $expectedStorageFieldValue, + $actualStorageFieldValue + ); + } + + /** + * @group fieldType + * @group selection + */ + public function testToFieldValue() + { + $storageFieldValue = new StorageFieldValue(); + $storageFieldValue->dataText = '1-3'; + $storageFieldValue->sortKeyString = '1-3'; + + $expectedFieldValue = new FieldValue(); + $expectedFieldValue->data = [1, 3]; + $expectedFieldValue->sortKey = '1-3'; + + $actualFieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $actualFieldValue); + + $this->assertEquals( + $expectedFieldValue, + $actualFieldValue + ); + } + + /** + * @group fieldType + * @group selection + */ + public function testToFieldValueEmpty() + { + $storageFieldValue = new StorageFieldValue(); + $storageFieldValue->dataText = ''; + $storageFieldValue->sortKeyString = ''; + + $expectedFieldValue = new FieldValue(); + $expectedFieldValue->data = []; + $expectedFieldValue->sortKey = ''; + + $actualFieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $actualFieldValue); + + $this->assertEquals( + $expectedFieldValue, + $actualFieldValue + ); + } + + /** + * @group fieldType + * @group selection + */ + public function testToStorageFieldDefinitionMultiple() + { + $fieldDefinition = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => new FieldTypeConstraints( + [ + 'fieldSettings' => new FieldSettings( + [ + 'isMultiple' => true, + 'options' => [ + 0 => 'First', + 1 => 'Second', + 2 => 'Third', + ], + ] + ), + ] + ), + ] + ); + + $expectedStorageFieldDefinition = new StorageFieldDefinition(); + $expectedStorageFieldDefinition->dataInt1 = 1; + $expectedStorageFieldDefinition->dataText5 = <<<EOT +<?xml version="1.0" encoding="utf-8"?> +<ezselection><options><option id="0" name="First"/><option id="1" name="Second"/><option id="2" name="Third"/></options></ezselection> + +EOT; + + $actualStorageFieldDefinition = new StorageFieldDefinition(); + + $this->converter->toStorageFieldDefinition($fieldDefinition, $actualStorageFieldDefinition); + + $this->assertEquals($expectedStorageFieldDefinition, $actualStorageFieldDefinition); + } + + /** + * @group fieldType + * @group selection + */ + public function testToStorageFieldDefinitionSingle() + { + $fieldDefinition = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => new FieldTypeConstraints( + [ + 'fieldSettings' => new FieldSettings( + [ + 'isMultiple' => false, + 'options' => [ + 0 => 'First', + ], + ] + ), + ] + ), + ] + ); + + $expectedStorageFieldDefinition = new StorageFieldDefinition(); + $expectedStorageFieldDefinition->dataInt1 = 0; + $expectedStorageFieldDefinition->dataText5 = <<<EOT +<?xml version="1.0" encoding="utf-8"?> +<ezselection><options><option id="0" name="First"/></options></ezselection> + +EOT; + + $actualStorageFieldDefinition = new StorageFieldDefinition(); + + $this->converter->toStorageFieldDefinition($fieldDefinition, $actualStorageFieldDefinition); + + $this->assertEquals($expectedStorageFieldDefinition, $actualStorageFieldDefinition); + } + + /** + * @group fieldType + * @group selection + */ + public function testToFieldDefinitionMultiple() + { + $storageFieldDefinition = new StorageFieldDefinition(); + $storageFieldDefinition->dataInt1 = 1; + $storageFieldDefinition->dataText5 = <<<EOT +<?xml version="1.0" encoding="utf-8"?> +<ezselection> + <options> + <option id="0" name="First"/> + <option id="1" name="Second"/> + <option id="2" name="Third"/> + </options> +</ezselection> +EOT; + + $expectedFieldDefinition = new PersistenceFieldDefinition( + [ + 'name' => [ + 'eng-GB' => 'test name', + ], + 'mainLanguageCode' => 'eng-GB', + 'fieldTypeConstraints' => new FieldTypeConstraints( + [ + 'fieldSettings' => new FieldSettings( + [ + 'isMultiple' => true, + 'options' => [ + 0 => 'First', + 1 => 'Second', + 2 => 'Third', + ], + 'multilingualOptions' => [ + 'eng-GB' => [ + 0 => 'First', + 1 => 'Second', + 2 => 'Third', + ], + ], + ] + ), + ] + ), + 'defaultValue' => new FieldValue( + [ + 'data' => [], + 'sortKey' => '', + ] + ), + ] + ); + + $actualFieldDefinition = new PersistenceFieldDefinition( + [ + 'name' => [ + 'eng-GB' => 'test name', + ], + 'mainLanguageCode' => 'eng-GB', + ] + ); + + $this->converter->toFieldDefinition($storageFieldDefinition, $actualFieldDefinition); + + $this->assertEquals($expectedFieldDefinition, $actualFieldDefinition); + } + + /** + * @group fieldType + * @group selection + */ + public function testToFieldDefinitionSingleEmpty() + { + $storageFieldDefinition = new StorageFieldDefinition(); + $storageFieldDefinition->dataInt1 = 0; + $storageFieldDefinition->dataText5 = <<<EOT +<?xml version="1.0" encoding="utf-8"?> +<ezselection> + <options> + </options> +</ezselection> +EOT; + + $expectedFieldDefinition = new PersistenceFieldDefinition( + [ + 'name' => [ + 'eng-GB' => 'test name', + ], + 'mainLanguageCode' => 'eng-GB', + 'fieldTypeConstraints' => new FieldTypeConstraints( + [ + 'fieldSettings' => new FieldSettings( + [ + 'isMultiple' => false, + 'options' => [], + 'multilingualOptions' => [ + 'eng-GB' => [], + ], + ] + ), + ] + ), + 'defaultValue' => new FieldValue(['data' => []]), + ] + ); + + $actualFieldDefinition = new PersistenceFieldDefinition( + [ + 'name' => [ + 'eng-GB' => 'test name', + ], + 'mainLanguageCode' => 'eng-GB', + ] + ); + + $this->converter->toFieldDefinition($storageFieldDefinition, $actualFieldDefinition); + + $this->assertEquals($expectedFieldDefinition, $actualFieldDefinition); + } +} + +class_alias(SelectionTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\SelectionTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/SerializableConverterTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverterTest.php similarity index 82% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/SerializableConverterTest.php rename to tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverterTest.php index 5a972e81a6..14afbf217d 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/FieldValue/Converter/SerializableConverterTest.php +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/SerializableConverterTest.php @@ -6,20 +6,20 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter; - -use eZ\Publish\Core\FieldType\FieldSettings; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SerializableConverter; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldDefinition; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\SPI\FieldType\ValueSerializerInterface; -use eZ\Publish\SPI\Persistence\Content\FieldTypeConstraints; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use Ibexa\Contracts\Core\FieldType\ValueSerializerInterface; +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Core\FieldType\FieldSettings; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\SerializableConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; use PHPUnit\Framework\TestCase; /** - * Test case for Generic converter in Legacy storage. + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\SerializableConverter */ class SerializableConverterTest extends TestCase { @@ -30,10 +30,10 @@ class SerializableConverterTest extends TestCase private const EXAMPLE_JSON = '{"foo":"foo","bar":"bar"}'; - /** @var \eZ\Publish\SPI\FieldType\ValueSerializerInterface|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\FieldType\ValueSerializerInterface|\PHPUnit\Framework\MockObject\MockObject */ private $serializer; - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter\SerializableConverter */ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\SerializableConverter */ private $converter; protected function setUp(): void @@ -180,3 +180,5 @@ public function testEmptyToFieldDefinition(): void $this->assertNull($fieldDefinition->fieldTypeConstraints->fieldSettings); } } + +class_alias(SerializableConverterTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\SerializableConverterTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockTest.php new file mode 100644 index 0000000000..ef8bc2010d --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextBlockTest.php @@ -0,0 +1,128 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\FieldType\FieldSettings; +use Ibexa\Core\FieldType\TextBlock\Value as TextBlockValue; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter + */ +class TextBlockTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TextBlockConverter */ + protected $converter; + + protected $longText; + + protected function setUp(): void + { + parent::setUp(); + $this->converter = new TextBlockConverter(); + $this->longText = <<<EOT +Now that we know who you are, I know who I am. +I'm not a mistake! It all makes sense! In a comic, you know how you can tell who the arch-villain's going to be? +He's the exact opposite of the hero. And most times they're friends, like you and me! I should've known way back when... +You know why, David? Because of the kids. + +They called me Mr Glass. +EOT; + } + + /** + * @group fieldType + * @group textBlock + */ + public function testToStorageValue() + { + $value = new FieldValue(); + $value->data = $this->longText; + $value->sortKey = 'Now that we know who you are'; + $storageFieldValue = new StorageFieldValue(); + + $this->converter->toStorageValue($value, $storageFieldValue); + self::assertSame($value->data, $storageFieldValue->dataText); + self::assertSame($value->sortKey, $storageFieldValue->sortKeyString); + self::assertSame(0, $storageFieldValue->sortKeyInt); + } + + /** + * @group fieldType + * @group textBlock + */ + public function testToFieldValue() + { + $storageFieldValue = new StorageFieldValue(); + $storageFieldValue->dataText = $this->longText; + $storageFieldValue->sortKeyString = 'Now that we know who you are'; + $fieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $fieldValue); + self::assertSame($storageFieldValue->dataText, $fieldValue->data); + self::assertSame($storageFieldValue->sortKeyString, $fieldValue->sortKey); + } + + /** + * @group fieldType + * @group textBlock + */ + public function testToStorageFieldDefinition() + { + $storageFieldDef = new StorageFieldDefinition(); + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'textRows' => 15, + ] + ); + $fieldDef = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + 'defaultValue' => new TextBlockValue(), + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + self::assertSame( + 15, + $storageFieldDef->dataInt1 + ); + } + + /** + * @group fieldType + * @group textBlock + */ + public function testToFieldDefinition() + { + $fieldDef = new PersistenceFieldDefinition(); + $storageDef = new StorageFieldDefinition( + [ + 'dataInt1' => 20, + ] + ); + + $this->converter->toFieldDefinition($storageDef, $fieldDef); + + self::assertSame('', $fieldDef->defaultValue->sortKey); + self::assertNull($fieldDef->fieldTypeConstraints->validators); + self::assertInstanceOf(FieldSettings::class, $fieldDef->fieldTypeConstraints->fieldSettings); + self::assertSame( + ['textRows' => 20], + $fieldDef->fieldTypeConstraints->fieldSettings->getArrayCopy() + ); + } +} + +class_alias(TextBlockTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\TextBlockTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineTest.php new file mode 100644 index 0000000000..48595753c7 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TextLineTest.php @@ -0,0 +1,159 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\FieldType\FieldSettings; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter + */ +class TextLineTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TextLineConverter */ + protected $converter; + + protected function setUp(): void + { + parent::setUp(); + $this->converter = new TextLineConverter(); + } + + /** + * @group fieldType + * @group textLine + */ + public function testToStorageValue() + { + $value = new FieldValue(); + $value->data = "He's holding a thermal detonator!"; + $value->sortKey = "He's holding"; + $storageFieldValue = new StorageFieldValue(); + + $this->converter->toStorageValue($value, $storageFieldValue); + self::assertSame($value->data, $storageFieldValue->dataText); + self::assertSame($value->sortKey, $storageFieldValue->sortKeyString); + self::assertSame(0, $storageFieldValue->sortKeyInt); + } + + /** + * @group fieldType + * @group textLine + */ + public function testToFieldValue() + { + $storageFieldValue = new StorageFieldValue(); + $storageFieldValue->dataText = 'When 900 years old, you reach... Look as good, you will not.'; + $storageFieldValue->sortKeyString = 'When 900 years old, you reach...'; + $storageFieldValue->sortKeyInt = 0; + $fieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $fieldValue); + self::assertSame($storageFieldValue->dataText, $fieldValue->data); + self::assertSame($storageFieldValue->sortKeyString, $fieldValue->sortKey); + } + + /** + * @group fieldType + * @group textLine + */ + public function testToStorageFieldDefinitionWithValidator() + { + $defaultText = 'This is a default text'; + $storageFieldDef = new StorageFieldDefinition(); + $defaultValue = new FieldValue(); + $defaultValue->data = $defaultText; + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->validators = [ + TextLineConverter::STRING_LENGTH_VALIDATOR_IDENTIFIER => ['maxStringLength' => 100], + ]; + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'defaultText' => $defaultText, + ] + ); + $fieldDef = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + 'defaultValue' => $defaultValue, + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + self::assertSame( + $fieldDef->fieldTypeConstraints->validators[TextLineConverter::STRING_LENGTH_VALIDATOR_IDENTIFIER], + ['maxStringLength' => $storageFieldDef->dataInt1] + ); + self::assertSame( + $fieldDef->fieldTypeConstraints->fieldSettings['defaultText'], + $storageFieldDef->dataText1 + ); + } + + /** + * @group fieldType + * @group textLine + */ + public function testToStorageFieldDefinitionNoValidator() + { + $defaultText = 'This is a default text'; + $storageFieldDef = new StorageFieldDefinition(); + $defaultValue = new FieldValue(); + $defaultValue->data = $defaultText; + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldDef = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + 'defaultValue' => $defaultValue, + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + self::assertSame( + 0, + $storageFieldDef->dataInt1 + ); + self::assertSame( + $fieldDef->defaultValue->data, + $storageFieldDef->dataText1 + ); + } + + /** + * @group fieldType + * @group textLine + */ + public function testToFieldDefinition() + { + $defaultText = 'This is a default value'; + $fieldDef = new PersistenceFieldDefinition(); + $storageDef = new StorageFieldDefinition( + [ + 'dataInt1' => 100, + 'dataText1' => $defaultText, + ] + ); + + $this->converter->toFieldDefinition($storageDef, $fieldDef); + self::assertSame( + [ + TextLineConverter::STRING_LENGTH_VALIDATOR_IDENTIFIER => ['maxStringLength' => 100], + ], + $fieldDef->fieldTypeConstraints->validators + ); + self::assertSame($defaultText, $fieldDef->defaultValue->data); + self::assertSame($defaultText, $fieldDef->defaultValue->sortKey); + } +} + +class_alias(TextLineTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\TextLineTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeTest.php new file mode 100644 index 0000000000..530362c1e0 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/TimeTest.php @@ -0,0 +1,159 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use DateTime; +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\FieldType\FieldSettings; +use Ibexa\Core\FieldType\Time\Type as TimeType; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\DateAndTimeConverter + * + * @group fieldType + * @group time + */ +class TimeTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\TimeConverter */ + protected $converter; + + /** @var int */ + protected $time; + + protected function setUp(): void + { + parent::setUp(); + $this->converter = new TimeConverter(); + $this->time = 3661; + } + + public function testToStorageValue() + { + $value = new FieldValue(); + $value->data = $this->time; + $value->sortKey = $this->time; + $storageFieldValue = new StorageFieldValue(); + + $this->converter->toStorageValue($value, $storageFieldValue); + self::assertSame($value->data, $storageFieldValue->dataInt); + self::assertSame($value->sortKey, $storageFieldValue->sortKeyInt); + self::assertSame('', $storageFieldValue->sortKeyString); + } + + public function testToFieldValue() + { + $storageFieldValue = new StorageFieldValue(); + $storageFieldValue->dataInt = $this->time; + $storageFieldValue->sortKeyString = ''; + $storageFieldValue->sortKeyInt = $this->time; + $fieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $fieldValue); + self::assertSame($this->time, $fieldValue->data); + self::assertSame($storageFieldValue->dataInt, $fieldValue->data); + self::assertSame($storageFieldValue->sortKeyInt, $fieldValue->sortKey); + } + + public function testToStorageFieldDefinitionDefaultEmpty() + { + $storageFieldDef = new StorageFieldDefinition(); + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'useSeconds' => true, + 'defaultType' => TimeType::DEFAULT_EMPTY, + ] + ); + $fieldDef = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + self::assertSame(TimeType::DEFAULT_EMPTY, $storageFieldDef->dataInt1); + self::assertSame(1, $storageFieldDef->dataInt2); + } + + public function testToStorageFieldDefinitionDefaultCurrentTime() + { + $storageFieldDef = new StorageFieldDefinition(); + $fieldTypeConstraints = new FieldTypeConstraints(); + $fieldTypeConstraints->fieldSettings = new FieldSettings( + [ + 'useSeconds' => false, + 'defaultType' => TimeType::DEFAULT_CURRENT_TIME, + ] + ); + $fieldDef = new PersistenceFieldDefinition( + [ + 'fieldTypeConstraints' => $fieldTypeConstraints, + ] + ); + + $this->converter->toStorageFieldDefinition($fieldDef, $storageFieldDef); + self::assertSame(TimeType::DEFAULT_CURRENT_TIME, $storageFieldDef->dataInt1); + self::assertSame(0, $storageFieldDef->dataInt2); + } + + public function testToFieldDefinitionDefaultEmpty() + { + $fieldDef = new PersistenceFieldDefinition(); + $storageDef = new StorageFieldDefinition( + [ + 'dataInt2' => 1, + 'dataInt1' => TimeType::DEFAULT_EMPTY, + ] + ); + + $this->converter->toFieldDefinition($storageDef, $fieldDef); + self::assertNull($fieldDef->defaultValue->data); + self::assertEquals( + new FieldSettings( + [ + 'useSeconds' => true, + 'defaultType' => TimeType::DEFAULT_EMPTY, + ] + ), + $fieldDef->fieldTypeConstraints->fieldSettings + ); + } + + public function testToFieldDefinitionDefaultCurrentTime() + { + $fieldDef = new PersistenceFieldDefinition(); + $storageDef = new StorageFieldDefinition( + [ + 'dataInt2' => 0, + 'dataInt1' => TimeType::DEFAULT_CURRENT_TIME, + ] + ); + + $dateTime = new DateTime(); + $dateTime->setTime(0, 0, 0); + $this->converter->toFieldDefinition($storageDef, $fieldDef); + self::assertSame(time() - $dateTime->getTimestamp(), $fieldDef->defaultValue->data); + self::assertEquals( + new FieldSettings( + [ + 'useSeconds' => false, + 'defaultType' => TimeType::DEFAULT_CURRENT_TIME, + ] + ), + $fieldDef->fieldTypeConstraints->fieldSettings + ); + } +} + +class_alias(TimeTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\TimeTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlTest.php b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlTest.php new file mode 100644 index 0000000000..67c2ee65c4 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValue/Converter/UrlTest.php @@ -0,0 +1,88 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition as PersistenceFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter + */ +class UrlTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\UrlConverter */ + protected $converter; + + protected function setUp(): void + { + parent::setUp(); + $this->converter = new UrlConverter(); + } + + /** + * @group fieldType + * @group url + */ + public function testToStorageValue() + { + $value = new FieldValue(); + $text = 'Ibexa'; + $value->data = ['text' => $text]; + $value->externalData = 'http://ibexa.co/'; + $value->sortKey = false; + $storageFieldValue = new StorageFieldValue(); + + $this->converter->toStorageValue($value, $storageFieldValue); + self::assertSame($text, $storageFieldValue->dataText); + } + + /** + * @group fieldType + * @group url + */ + public function testToFieldValue() + { + $text = "A link's text"; + $urlId = 842; + $storageFieldValue = new StorageFieldValue(); + $storageFieldValue->dataText = $text; + $storageFieldValue->dataInt = $urlId; + $storageFieldValue->sortKeyString = false; + $storageFieldValue->sortKeyInt = false; + $fieldValue = new FieldValue(); + + $this->converter->toFieldValue($storageFieldValue, $fieldValue); + self::assertIsArray($fieldValue->data); + self::assertFalse($fieldValue->sortKey); + self::assertSame($text, $fieldValue->data['text']); + self::assertEquals($urlId, $fieldValue->data['urlId']); + } + + /** + * @group fieldType + * @group url + */ + public function testToStorageFieldDefinition() + { + $this->converter->toStorageFieldDefinition(new PersistenceFieldDefinition(), new StorageFieldDefinition()); + } + + /** + * @group fieldType + * @group url + */ + public function testToFieldDefinition() + { + $this->converter->toFieldDefinition(new StorageFieldDefinition(), new PersistenceFieldDefinition()); + } +} + +class_alias(UrlTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValue\Converter\UrlTest'); diff --git a/tests/lib/Persistence/Legacy/Content/FieldValueConverterRegistryTest.php b/tests/lib/Persistence/Legacy/Content/FieldValueConverterRegistryTest.php new file mode 100644 index 0000000000..08ccabe4a8 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/FieldValueConverterRegistryTest.php @@ -0,0 +1,59 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content; + +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry as Registry; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry + */ +class FieldValueConverterRegistryTest extends TestCase +{ + private const TYPE_NAME = 'some-type'; + + public function testRegister() + { + $converter = $this->getFieldValueConverterMock(); + $registry = new Registry([self::TYPE_NAME => $converter]); + + $this->assertSame($converter, $registry->getConverter(self::TYPE_NAME)); + } + + public function testGetStorage() + { + $converter = $this->getFieldValueConverterMock(); + $registry = new Registry([self::TYPE_NAME => $converter]); + + $res = $registry->getConverter(self::TYPE_NAME); + + $this->assertSame( + $converter, + $res + ); + } + + public function testGetNotFound() + { + $this->expectException(Converter\Exception\NotFound::class); + + $registry = new Registry([]); + + $registry->getConverter('not-found'); + } + + /** + * @return \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter + */ + protected function getFieldValueConverterMock() + { + return $this->createMock(Converter::class); + } +} + +class_alias(FieldValueConverterRegistryTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\FieldValueConverterRegistryTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..8d8a004560 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,1994 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Gateway; + +use Doctrine\DBAL\ParameterType; +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\MetadataUpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\UpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation as RelationValue; +use Ibexa\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use Ibexa\Tests\Core\Persistence\Legacy\Content\LanguageAwareTestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase::insertContentObject + */ +class DoctrineDatabaseTest extends LanguageAwareTestCase +{ + /** + * Database gateway to test. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase + */ + protected $databaseGateway; + + /** + * @todo Fix not available fields + */ + public function testInsertContentObject() + { + $struct = $this->getCreateStructFixture(); + + $gateway = $this->getDatabaseGateway(); + $gateway->insertContentObject($struct); + + $this->assertQueryResult( + [ + [ + 'name' => 'Content name', + 'contentclass_id' => '23', + 'section_id' => '42', + 'owner_id' => '13', + 'current_version' => '1', + 'initial_language_id' => '2', + 'remote_id' => 'some_remote_id', + 'language_mask' => '3', + 'modified' => '0', + 'published' => '0', + 'status' => ContentInfo::STATUS_DRAFT, + ], + ], + $this->getDatabaseConnection() + ->createQueryBuilder() + ->select( + [ + 'name', + 'contentclass_id', + 'section_id', + 'owner_id', + 'current_version', + 'initial_language_id', + 'remote_id', + 'language_mask', + 'modified', + 'published', + 'status', + ] + )->from('ezcontentobject') + ); + } + + /** + * Returns a Content fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\CreateStruct + */ + protected function getCreateStructFixture() + { + $struct = new CreateStruct(); + + $struct->typeId = 23; + $struct->sectionId = 42; + $struct->ownerId = 13; + $struct->initialLanguageId = 2; + $struct->remoteId = 'some_remote_id'; + $struct->alwaysAvailable = true; + $struct->modified = 456; + $struct->name = [ + 'eng-US' => 'Content name', + ]; + $struct->fields = [ + new Field(['languageCode' => 'eng-US']), + ]; + $struct->locations = []; + + return $struct; + } + + /** + * Returns a Content fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content + */ + protected function getContentFixture() + { + $content = new Content(); + + $content->versionInfo = new VersionInfo(); + $content->versionInfo->names = [ + 'eng-US' => 'Content name', + ]; + $content->versionInfo->status = VersionInfo::STATUS_PENDING; + + $content->versionInfo->contentInfo = new ContentInfo(); + $content->versionInfo->contentInfo->contentTypeId = 23; + $content->versionInfo->contentInfo->sectionId = 42; + $content->versionInfo->contentInfo->ownerId = 13; + $content->versionInfo->contentInfo->currentVersionNo = 2; + $content->versionInfo->contentInfo->mainLanguageCode = 'eng-US'; + $content->versionInfo->contentInfo->remoteId = 'some_remote_id'; + $content->versionInfo->contentInfo->alwaysAvailable = true; + $content->versionInfo->contentInfo->publicationDate = 123; + $content->versionInfo->contentInfo->modificationDate = 456; + $content->versionInfo->contentInfo->isPublished = false; + $content->versionInfo->contentInfo->name = 'Content name'; + + return $content; + } + + /** + * Returns a Version fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\VersionInfo + */ + protected function getVersionFixture() + { + $version = new VersionInfo(); + + $version->id = null; + $version->versionNo = 1; + $version->creatorId = 13; + $version->status = 0; + $version->creationDate = 1312278322; + $version->modificationDate = 1312278323; + $version->initialLanguageCode = self::ENG_GB; + $version->contentInfo = new ContentInfo( + [ + 'id' => 2342, + 'alwaysAvailable' => true, + ] + ); + + return $version; + } + + public function testInsertVersion() + { + $version = $this->getVersionFixture(); + + $gateway = $this->getDatabaseGateway(); + $gateway->insertVersion($version, []); + + $this->assertQueryResult( + [ + [ + 'contentobject_id' => '2342', + 'created' => '1312278322', + 'creator_id' => '13', + 'modified' => '1312278323', + 'status' => '0', + 'workflow_event_pos' => '0', + 'version' => '1', + 'language_mask' => '5', + 'initial_language_id' => '4', + // Not needed, according to field mapping document + // 'user_id', + ], + ], + $this->getDatabaseConnection() + ->createQueryBuilder() + ->select( + [ + 'contentobject_id', + 'created', + 'creator_id', + 'modified', + 'status', + 'workflow_event_pos', + 'version', + 'language_mask', + 'initial_language_id', + ] + )->from('ezcontentobject_version') + ); + } + + public function testSetStatus() + { + $gateway = $this->getDatabaseGateway(); + + // insert content + $struct = $this->getCreateStructFixture(); + $contentId = $gateway->insertContentObject($struct); + + // insert version + $version = $this->getVersionFixture(); + $version->contentInfo->id = $contentId; + $gateway->insertVersion($version, []); + + $this->assertTrue( + $gateway->setStatus($version->contentInfo->id, $version->versionNo, VersionInfo::STATUS_PENDING) + ); + + $this->assertQueryResult( + [[VersionInfo::STATUS_PENDING]], + $this->getDatabaseConnection() + ->createQueryBuilder() + ->select('status') + ->from('ezcontentobject_version') + ); + + // check that content status has not been set to published + $this->assertQueryResult( + [[VersionInfo::STATUS_DRAFT]], + $this->getDatabaseConnection() + ->createQueryBuilder() + ->select('status') + ->from('ezcontentobject') + ); + } + + public function testSetStatusPublished() + { + $gateway = $this->getDatabaseGateway(); + + // insert content + $struct = $this->getCreateStructFixture(); + $contentId = $gateway->insertContentObject($struct); + + // insert version + $version = $this->getVersionFixture(); + $version->contentInfo->id = $contentId; + $gateway->insertVersion($version, []); + + $this->assertTrue( + $gateway->setStatus($version->contentInfo->id, $version->versionNo, VersionInfo::STATUS_PUBLISHED) + ); + + $this->assertQueryResult( + [[VersionInfo::STATUS_PUBLISHED]], + $this->getDatabaseConnection() + ->createQueryBuilder() + ->select('status') + ->from('ezcontentobject_version') + ); + + // check that content status has been set to published + $this->assertQueryResult( + [[ContentInfo::STATUS_PUBLISHED]], + $this->getDatabaseConnection() + ->createQueryBuilder() + ->select('status') + ->from('ezcontentobject') + ); + } + + public function testSetStatusUnknownVersion() + { + $gateway = $this->getDatabaseGateway(); + + $this->assertFalse( + $gateway->setStatus(23, 42, 2) + ); + } + + public function testUpdateContent() + { + $gateway = $this->getDatabaseGateway(); + + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $metadataStruct = $this->getMetadataUpdateStructFixture(); + + $gateway->updateContent(10, $metadataStruct); + + $this->assertQueryResult( + [ + [ + 'initial_language_id' => '3', + 'modified' => '234567', + 'owner_id' => '42', + 'published' => '123456', + 'remote_id' => 'ghjk1234567890ghjk1234567890', + 'name' => 'Thoth', + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select( + 'initial_language_id', + 'modified', + 'owner_id', + 'published', + 'remote_id', + 'name' + )->from('ezcontentobject') + ->where('id = 10') + ); + } + + /** + * Returns an UpdateStruct fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\UpdateStruct + */ + protected function getUpdateStructFixture() + { + $struct = new UpdateStruct(); + $struct->creatorId = 23; + $struct->fields = []; + $struct->modificationDate = 234567; + $struct->initialLanguageId = 2; + + return $struct; + } + + /** + * Returns a MetadataUpdateStruct fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\MetadataUpdateStruct + */ + protected function getMetadataUpdateStructFixture() + { + $struct = new MetadataUpdateStruct(); + $struct->ownerId = 42; + $struct->publicationDate = 123456; + $struct->mainLanguageId = 3; + $struct->modificationDate = 234567; + $struct->remoteId = 'ghjk1234567890ghjk1234567890'; + $struct->name = 'Thoth'; + + return $struct; + } + + public function testUpdateVersion() + { + $gateway = $this->getDatabaseGateway(); + + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway->updateVersion(10, 2, $this->getUpdateStructFixture()); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $expr = $query->expr(); + $this->assertQueryResult( + [ + [ + 'creator_id' => '23', + 'initial_language_id' => '2', + 'modified' => '234567', + ], + ], + $query + ->select( + [ + 'creator_id', + 'initial_language_id', + 'modified', + ] + )->from('ezcontentobject_version') + ->where( + $expr->andX( + $expr->eq('contentobject_id', 10), + $expr->eq('version', 2) + ) + ) + ); + } + + public function testInsertNewField() + { + $content = $this->getContentFixture(); + $content->versionInfo->contentInfo->id = 2342; + + $field = $this->getFieldFixture(); + $value = $this->getStorageValueFixture(); + + $gateway = $this->getDatabaseGateway(); + $gateway->insertNewField($content, $field, $value); + + $this->assertQueryResult( + [ + [ + 'contentclassattribute_id' => '231', + 'contentobject_id' => '2342', + 'data_float' => '24.42', + 'data_int' => '42', + 'data_text' => 'Test text', + 'data_type_string' => 'ezstring', + 'language_code' => self::ENG_GB, + 'language_id' => '4', + 'sort_key_int' => '23', + 'sort_key_string' => 'Test', + 'version' => '1', + ], + ], + $this->getDatabaseConnection() + ->createQueryBuilder() + ->select( + [ + 'contentclassattribute_id', + 'contentobject_id', + 'data_float', + 'data_int', + 'data_text', + 'data_type_string', + 'language_code', + 'language_id', + 'sort_key_int', + 'sort_key_string', + 'version', + ] + )->from('ezcontentobject_attribute') + ); + } + + public function testInsertNewAlwaysAvailableField() + { + $content = $this->getContentFixture(); + $content->versionInfo->contentInfo->id = 2342; + // Set main language to the one used in the field fixture + $content->versionInfo->contentInfo->mainLanguageCode = self::ENG_GB; + + $field = $this->getFieldFixture(); + $value = $this->getStorageValueFixture(); + + $gateway = $this->getDatabaseGateway(); + $gateway->insertNewField($content, $field, $value); + + $this->assertQueryResult( + [ + [ + 'contentclassattribute_id' => '231', + 'contentobject_id' => '2342', + 'data_float' => '24.42', + 'data_int' => '42', + 'data_text' => 'Test text', + 'data_type_string' => 'ezstring', + 'language_code' => self::ENG_GB, + 'language_id' => '5', + 'sort_key_int' => '23', + 'sort_key_string' => 'Test', + 'version' => '1', + ], + ], + $this->getDatabaseConnection() + ->createQueryBuilder() + ->select( + [ + 'contentclassattribute_id', + 'contentobject_id', + 'data_float', + 'data_int', + 'data_text', + 'data_type_string', + 'language_code', + 'language_id', + 'sort_key_int', + 'sort_key_string', + 'version', + ] + )->from('ezcontentobject_attribute') + ); + } + + public function testUpdateField() + { + $content = $this->getContentFixture(); + $content->versionInfo->contentInfo->id = 2342; + + $field = $this->getFieldFixture(); + $value = $this->getStorageValueFixture(); + + $gateway = $this->getDatabaseGateway(); + $field->id = $gateway->insertNewField($content, $field, $value); + + $newValue = new StorageFieldValue( + [ + 'dataFloat' => 124.42, + 'dataInt' => 142, + 'dataText' => 'New text', + 'sortKeyInt' => 123, + 'sortKeyString' => 'new_text', + ] + ); + + $gateway->updateField($field, $newValue); + + $this->assertQueryResult( + [ + [ + 'data_float' => '124.42', + 'data_int' => '142', + 'data_text' => 'New text', + 'sort_key_int' => '123', + 'sort_key_string' => 'new_text', + ], + ], + $this->getDatabaseConnection() + ->createQueryBuilder() + ->select( + [ + 'data_float', + 'data_int', + 'data_text', + 'sort_key_int', + 'sort_key_string', + ] + )->from('ezcontentobject_attribute') + ); + } + + public function testUpdateNonTranslatableField() + { + $content = $this->getContentFixture(); + $content->versionInfo->contentInfo->id = 2342; + + $fieldGb = $this->getFieldFixture(); + $fieldUs = $this->getOtherLanguageFieldFixture(); + $value = $this->getStorageValueFixture(); + + $gateway = $this->getDatabaseGateway(); + $fieldGb->id = $gateway->insertNewField($content, $fieldGb, $value); + $fieldUs->id = $gateway->insertNewField($content, $fieldUs, $value); + + $updateStruct = new Content\UpdateStruct(); + + $newValue = new StorageFieldValue( + [ + 'dataFloat' => 124.42, + 'dataInt' => 142, + 'dataText' => 'New text', + 'sortKeyInt' => 123, + 'sortKeyString' => 'new_text', + ] + ); + + $gateway->updateNonTranslatableField($fieldGb, $newValue, $content->versionInfo->contentInfo->id); + + $this->assertQueryResult( + [ + // Both fields updated + [ + 'data_float' => '124.42', + 'data_int' => '142', + 'data_text' => 'New text', + 'sort_key_int' => '123', + 'sort_key_string' => 'new_text', + ], + [ + 'data_float' => '124.42', + 'data_int' => '142', + 'data_text' => 'New text', + 'sort_key_int' => '123', + 'sort_key_string' => 'new_text', + ], + ], + $this->getDatabaseConnection() + ->createQueryBuilder() + ->select( + [ + 'data_float', + 'data_int', + 'data_text', + 'sort_key_int', + 'sort_key_string', + ] + )->from('ezcontentobject_attribute') + ); + } + + public function testListVersions(): void + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + $res = $gateway->listVersions(226); + + $this->assertCount( + 2, + $res + ); + + foreach ($res as $row) { + $this->assertCount( + 23, + $row + ); + } + + $this->assertEquals( + 675, + $res[0]['ezcontentobject_version_id'] + ); + $this->assertEquals( + 676, + $res[1]['ezcontentobject_version_id'] + ); + } + + public function testListVersionNumbers() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + $res = $gateway->listVersionNumbers(226); + + $this->assertEquals([1, 2], $res); + } + + public function testListVersionsForUser() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + $res = $gateway->listVersionsForUser(14); + + $this->assertCount( + 2, + $res + ); + + foreach ($res as $row) { + $this->assertCount( + 23, + $row + ); + } + + $this->assertEquals( + 677, + $res[0]['ezcontentobject_version_id'] + ); + $this->assertEquals( + 0, + $res[0]['ezcontentobject_version_status'] + ); + $this->assertEquals( + 678, + $res[1]['ezcontentobject_version_id'] + ); + $this->assertEquals( + 0, + $res[1]['ezcontentobject_version_status'] + ); + } + + public function testLoadWithAllTranslations() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + $res = $gateway->load(226, 2); + + $this->assertValuesInRows( + 'ezcontentobject_attribute_language_code', + ['eng-US', self::ENG_GB], + $res + ); + + $this->assertValuesInRows( + 'ezcontentobject_attribute_language_id', + ['2', '4'], + $res + ); + } + + public function testCreateFixtureForMapperExtractContentFromRowsMultipleVersions() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + + $resFirst = $gateway->load(11, 1); + $resSecond = $gateway->load(11, 2); + + $res = array_merge($resFirst, $resSecond); + + $orig = include __DIR__ . '/../_fixtures/extract_content_from_rows_multiple_versions.php'; + + /*$this->storeFixture( + __DIR__ . '/../_fixtures/extract_content_from_rows_multiple_versions.php', + $res + );*/ + + $this->assertEquals($orig, $res, 'Fixtures differ between what was previously stored(expected) and what it now generates(actual), this hints either some mistake in impl or that the fixture (../_fixtures/extract_content_from_rows_multiple_versions.php) and tests needs to be adapted.'); + } + + public function testCreateFixtureForMapperExtractContentFromRows() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + + $res = array_merge($gateway->load(226, 2)); + + $orig = include __DIR__ . '/../_fixtures/extract_content_from_rows.php'; + + /*$this->storeFixture( + __DIR__ . '/../_fixtures/extract_content_from_rows.php', + $res + );*/ + + $this->assertEquals($orig, $res, 'Fixtures differ between what was previously stored(expected) and what it now generates(actual), this hints either some mistake in impl or that the fixture (../_fixtures/extract_content_from_rows.php) and tests needs to be adapted.'); + } + + public function testLoadWithSingleTranslation() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + $res = $gateway->load(226, 2, [self::ENG_GB]); + + $this->assertValuesInRows( + 'ezcontentobject_attribute_language_code', + [self::ENG_GB], + $res + ); + $this->assertValuesInRows( + 'ezcontentobject_attribute_language_id', + ['4'], + $res + ); + $this->assertCount( + 1, + $res + ); + } + + public function testLoadNonExistentTranslation() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + $res = $gateway->load(226, 2, ['de-DE']); + + $this->assertCount( + 0, + $res + ); + } + + /** + * Asserts that $columnKey in $actualRows exactly contains $expectedValues. + * + * @param string $columnKey + * @param string[] $expectedValues + * @param string[][] $actualRows + */ + protected function assertValuesInRows($columnKey, array $expectedValues, array $actualRows) + { + $expectedValues = array_fill_keys( + array_values($expectedValues), + true + ); + + $containedValues = []; + + foreach ($actualRows as $row) { + if (isset($row[$columnKey])) { + $containedValues[$row[$columnKey]] = true; + } + } + + $this->assertEquals( + $expectedValues, + $containedValues + ); + } + + public function testGetAllLocationIds() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + + $this->assertEquals( + [228], + $gateway->getAllLocationIds(226) + ); + } + + public function testGetFieldIdsByType() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + + $this->assertEquals( + [ + 'ezstring' => [841], + 'ezimage' => [843], + 'ezkeyword' => [844], + ], + $gateway->getFieldIdsByType(149) + ); + } + + public function testGetFieldIdsByTypeWithSecondArgument() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + + $this->assertEquals( + [ + 'ezstring' => [4001, 4002], + ], + $gateway->getFieldIdsByType(225, 2) + ); + } + + public function testDeleteRelationsTo() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $beforeCount = [ + 'all' => $this->countContentRelations(), + 'from' => $this->countContentRelations(149), + 'to' => $this->countContentRelations(null, 149), + ]; + + $gateway = $this->getDatabaseGateway(); + $gateway->deleteRelations(149); + + $this->assertEquals( + // yes, relates to itself! + [ + 'all' => $beforeCount['all'] - 2, + 'from' => $beforeCount['from'] - 1, + 'to' => $beforeCount['to'] - 2, + ], + [ + 'all' => $this->countContentRelations(), + 'from' => $this->countContentRelations(149), + 'to' => $this->countContentRelations(null, 149), + ] + ); + } + + public function testDeleteRelationsFrom() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $beforeCount = [ + 'all' => $this->countContentRelations(), + 'from' => $this->countContentRelations(75), + 'to' => $this->countContentRelations(null, 75), + ]; + + $gateway = $this->getDatabaseGateway(); + $gateway->deleteRelations(75); + + $this->assertEquals( + [ + 'all' => $beforeCount['all'] - 6, + 'from' => $beforeCount['from'] - 6, + 'to' => $beforeCount['to'], + ], + [ + 'all' => $this->countContentRelations(), + 'from' => $this->countContentRelations(75), + 'to' => $this->countContentRelations(null, 75), + ] + ); + } + + public function testDeleteRelationsWithSecondArgument() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $beforeCount = [ + 'all' => $this->countContentRelations(), + 'from' => $this->countContentRelations(225), + 'to' => $this->countContentRelations(null, 225), + ]; + + $gateway = $this->getDatabaseGateway(); + $gateway->deleteRelations(225, 2); + + $this->assertEquals( + [ + 'all' => $beforeCount['all'] - 1, + 'from' => $beforeCount['from'] - 1, + 'to' => $beforeCount['to'], + ], + [ + 'all' => $this->countContentRelations(), + 'from' => $this->countContentRelations(225), + 'to' => $this->countContentRelations(null, 225), + ] + ); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function testDeleteField(): void + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $beforeCount = $this->countContentFields(); + + $gateway = $this->getDatabaseGateway(); + $gateway->deleteField(22); + + $this->assertEquals( + $beforeCount - 2, + $this->countContentFields() + ); + + $this->assertQueryResult( + [], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('*') + ->from('ezcontentobject_attribute') + ->where('id=22') + ); + } + + public function testDeleteFields() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $beforeCount = [ + 'all' => $this->countContentFields(), + 'this' => $this->countContentFields(4), + ]; + + $gateway = $this->getDatabaseGateway(); + $gateway->deleteFields(4); + + $this->assertEquals( + [ + 'all' => $beforeCount['all'] - 2, + 'this' => 0, + ], + [ + 'all' => $this->countContentFields(), + 'this' => $this->countContentFields(4), + ] + ); + } + + public function testDeleteFieldsWithSecondArgument() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $beforeCount = [ + 'all' => $this->countContentFields(), + 'this' => $this->countContentFields(225), + ]; + + $gateway = $this->getDatabaseGateway(); + $gateway->deleteFields(225, 2); + + $this->assertEquals( + [ + 'all' => $beforeCount['all'] - 2, + 'this' => $beforeCount['this'] - 2, + ], + [ + 'all' => $this->countContentFields(), + 'this' => $this->countContentFields(225), + ] + ); + } + + public function testDeleteVersions() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $beforeCount = [ + 'all' => $this->countContentVersions(), + 'this' => $this->countContentVersions(14), + ]; + + $gateway = $this->getDatabaseGateway(); + $gateway->deleteVersions(14); + + $this->assertEquals( + [ + 'all' => $beforeCount['all'] - 2, + 'this' => 0, + ], + [ + 'all' => $this->countContentVersions(), + 'this' => $this->countContentVersions(14), + ] + ); + } + + public function testDeleteVersionsWithSecondArgument() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $beforeCount = [ + 'all' => $this->countContentVersions(), + 'this' => $this->countContentVersions(225), + ]; + + $gateway = $this->getDatabaseGateway(); + $gateway->deleteVersions(225, 2); + + $this->assertEquals( + [ + 'all' => $beforeCount['all'] - 1, + 'this' => $beforeCount['this'] - 1, + ], + [ + 'all' => $this->countContentVersions(), + 'this' => $this->countContentVersions(225), + ] + ); + } + + /** + * @throws \Exception + */ + public function testSetName() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + + $gateway->setName(14, 2, 'Hello world!', self::ENG_GB); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [[self::ENG_GB, 2, 14, 4, 'Hello world!', self::ENG_GB]], + $query + ->select( + [ + 'content_translation', + 'content_version', + 'contentobject_id', + 'language_id', + 'name', + 'real_translation', + ] + ) + ->from('ezcontentobject_name') + ->where('contentobject_id = :content_id') + ->andWhere('content_version = :version_no') + ->andWhere('content_translation = :language_code') + ->setParameter('content_id', 14, ParameterType::INTEGER) + ->setParameter('version_no', 2, ParameterType::INTEGER) + ->setParameter('language_code', self::ENG_GB, ParameterType::STRING) + ); + } + + public function testDeleteNames() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $beforeCount = [ + 'all' => $this->countContentNames(), + 'this' => $this->countContentNames(14), + ]; + + $gateway = $this->getDatabaseGateway(); + $gateway->deleteNames(14); + + $this->assertEquals( + [ + 'all' => $beforeCount['all'] - 2, + 'this' => 0, + ], + [ + 'all' => $this->countContentNames(), + 'this' => $this->countContentNames(14), + ] + ); + } + + public function testDeleteNamesWithSecondArgument() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $beforeCount = [ + 'all' => $this->countContentNames(), + 'this' => $this->countContentNames(225), + ]; + + $gateway = $this->getDatabaseGateway(); + $gateway->deleteNames(225, 2); + + $this->assertEquals( + [ + 'all' => $beforeCount['all'] - 1, + 'this' => $beforeCount['this'] - 1, + ], + [ + 'all' => $this->countContentNames(), + 'this' => $this->countContentNames(225), + ] + ); + } + + public function testDeleteContent() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $beforeCount = $this->countContent(); + + $gateway = $this->getDatabaseGateway(); + $gateway->deleteContent(14); + + $this->assertEquals( + [ + 'all' => $beforeCount - 1, + 'this' => 0, + ], + [ + 'all' => $this->countContent(), + 'this' => $this->countContent(14), + ] + ); + } + + public function testLoadRelationThrowNotFoundExceptionWhenRelationNotFound(): void + { + $this->expectException(NotFoundException::class); + $this->expectExceptionMessage('Could not find \'Relation\' with identifier \'3\''); + + $this->insertRelationFixture(); + $gateway = $this->getDatabaseGateway(); + $gateway->loadRelation(3); + } + + public function testLoadRelationById(): void + { + $relationId = 2; + + $this->insertRelationFixture(); + + $gateway = $this->getDatabaseGateway(); + + $relation = $gateway->loadRelation($relationId); + + self::assertEquals( + $relationId, + $relation['ezcontentobject_link_id'] + ); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function testLoadRelations(): void + { + $this->insertRelationFixture(); + + $gateway = $this->getDatabaseGateway(); + + $relations = $gateway->loadRelations(57); + + $this->assertCount(3, $relations); + + $this->assertValuesInRows( + 'ezcontentobject_link_to_contentobject_id', + [58, 59, 60], + $relations + ); + + $this->assertValuesInRows( + 'ezcontentobject_link_from_contentobject_id', + [57], + $relations + ); + $this->assertValuesInRows( + 'ezcontentobject_link_from_contentobject_version', + [2], + $relations + ); + } + + public function testLoadRelationsByType() + { + $this->insertRelationFixture(); + + $gateway = $this->getDatabaseGateway(); + + $relations = $gateway->loadRelations(57, null, RelationValue::COMMON); + + $this->assertCount(1, $relations, 'Expecting one relation to be loaded'); + + $this->assertValuesInRows( + 'ezcontentobject_link_relation_type', + [RelationValue::COMMON], + $relations + ); + + $this->assertValuesInRows( + 'ezcontentobject_link_to_contentobject_id', + [58], + $relations + ); + } + + public function testLoadRelationsByVersion() + { + $this->insertRelationFixture(); + + $gateway = $this->getDatabaseGateway(); + + $relations = $gateway->loadRelations(57, 1); + + $this->assertCount(1, $relations, 'Expecting one relation to be loaded'); + + $this->assertValuesInRows( + 'ezcontentobject_link_to_contentobject_id', + [58], + $relations + ); + } + + public function testLoadRelationsNoResult() + { + $this->insertRelationFixture(); + + $gateway = $this->getDatabaseGateway(); + + $relations = $gateway->loadRelations(57, 1, RelationValue::EMBED); + + $this->assertCount(0, $relations, 'Expecting no relation to be loaded'); + } + + public function testLoadReverseRelations() + { + $this->insertRelationFixture(); + + $gateway = $this->getDatabaseGateway(); + + $relations = $gateway->loadReverseRelations(58); + + self::assertCount(2, $relations); + + $this->assertValuesInRows( + 'ezcontentobject_link_from_contentobject_id', + [57, 61], + $relations + ); + } + + public function testLoadReverseRelationsWithType() + { + $this->insertRelationFixture(); + + $gateway = $this->getDatabaseGateway(); + + $relations = $gateway->loadReverseRelations(58, RelationValue::COMMON); + + self::assertCount(1, $relations); + + $this->assertValuesInRows( + 'ezcontentobject_link_from_contentobject_id', + [57], + $relations + ); + + $this->assertValuesInRows( + 'ezcontentobject_link_relation_type', + [RelationValue::COMMON], + $relations + ); + } + + /** + * Inserts the relation database fixture from relation_data.php. + */ + protected function insertRelationFixture() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/relations_data.php' + ); + } + + public function testGetLastVersionNumber() + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + + $this->assertEquals( + 1, + $gateway->getLastVersionNumber(4) + ); + } + + public function testInsertRelation() + { + $struct = $this->getRelationCreateStructFixture(); + $gateway = $this->getDatabaseGateway(); + $gateway->insertRelation($struct); + + $this->assertQueryResult( + [ + [ + 'id' => 1, + 'from_contentobject_id' => $struct->sourceContentId, + 'from_contentobject_version' => $struct->sourceContentVersionNo, + 'contentclassattribute_id' => $struct->sourceFieldDefinitionId, + 'to_contentobject_id' => $struct->destinationContentId, + 'relation_type' => $struct->type, + ], + ], + $this->getDatabaseConnection() + ->createQueryBuilder() + ->select( + [ + 'id', + 'from_contentobject_id', + 'from_contentobject_version', + 'contentclassattribute_id', + 'to_contentobject_id', + 'relation_type', + ] + )->from('ezcontentobject_link') + ->where('id = 1') + ); + } + + public function testDeleteRelation() + { + $this->insertRelationFixture(); + + self::assertEquals(4, $this->countContentRelations(57)); + + $gateway = $this->getDatabaseGateway(); + $gateway->deleteRelation(2, RelationValue::COMMON); + + self::assertEquals(3, $this->countContentRelations(57)); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function testDeleteRelationWithCompositeBitmask(): void + { + $this->insertRelationFixture(); + + $gateway = $this->getDatabaseGateway(); + $gateway->deleteRelation(11, RelationValue::COMMON); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [['relation_type' => RelationValue::LINK]], + $query + ->select(['relation_type']) + ->from('ezcontentobject_link') + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter(11, ParameterType::INTEGER) + ) + ) + ); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function testUpdateAlwaysAvailableFlagRemove(): void + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + $gateway->updateAlwaysAvailableFlag(103, false); + + $connection = $this->getDatabaseConnection(); + $query = $connection->createQueryBuilder(); + $this->assertQueryResult( + [['id' => 2]], + $query + ->select(['language_mask']) + ->from('ezcontentobject') + ->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter(103, ParameterType::INTEGER) + ) + ) + ); + + $query = $connection->createQueryBuilder(); + $this->assertQueryResult( + [['language_id' => 2]], + $query + ->select( + ['language_id'] + )->from( + 'ezcontentobject_name' + )->where( + $query->expr()->andX( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter(103, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'content_version', + $query->createPositionalParameter(1, ParameterType::INTEGER) + ) + ) + ) + ); + + $query = $connection->createQueryBuilder(); + $this->assertQueryResult( + [ + ['language_id' => 2], + ], + $query + ->select('DISTINCT language_id') + ->from('ezcontentobject_attribute') + ->where( + $query->expr()->andX( + $query->expr()->eq('contentobject_id', 103), + $query->expr()->eq('version', 1) + ) + ) + ); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function testUpdateAlwaysAvailableFlagAdd(): void + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + $contentId = 102; + $gateway->updateAlwaysAvailableFlag($contentId, true); + + $connection = $this->getDatabaseConnection(); + $expectedLanguageId = 3; + $this->assertQueryResult( + [['id' => $expectedLanguageId]], + $connection->createQueryBuilder() + ->select(['language_mask']) + ->from('ezcontentobject') + ->where('id = 102') + ); + + $versionNo = 1; + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + ['language_id' => $expectedLanguageId], + ], + $query + ->select('language_id') + ->from('ezcontentobject_name') + ->where( + $query->expr()->andX( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'content_version', + $query->createPositionalParameter($versionNo, ParameterType::INTEGER) + ) + ) + ) + ); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + ['language_id' => $expectedLanguageId], + ], + $query + ->select('DISTINCT language_id') + ->from('ezcontentobject_attribute') + ->where( + $query->expr()->andX( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'version', + $query->createPositionalParameter($versionNo, ParameterType::INTEGER) + ) + ) + ) + ); + } + + /** + * @throws \Doctrine\DBAL\DBALException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testUpdateContentAddAlwaysAvailableFlagMultilingual(): void + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects_multilingual.php' + ); + + $gateway = $this->getDatabaseGateway(); + $contentMetadataUpdateStruct = new MetadataUpdateStruct( + [ + 'mainLanguageId' => 4, + 'alwaysAvailable' => true, + ] + ); + $gateway->updateContent(4, $contentMetadataUpdateStruct); + + $this->assertQueryResult( + [['id' => 7]], + $this->getDatabaseConnection()->createQueryBuilder()->select( + ['language_mask'] + )->from( + 'ezcontentobject' + )->where( + 'id = 4' + ) + ); + + $this->assertContentVersionAttributesLanguages( + 4, + 2, + [ + ['id' => '7', 'language_id' => 2], + ['id' => '8', 'language_id' => 5], + ] + ); + + $this->assertContentVersionAttributesLanguages( + 4, + 1, + [ + ['id' => '7', 'language_id' => 2], + ['id' => '8', 'language_id' => 5], + ] + ); + } + + /** + * @throws \Doctrine\DBAL\DBALException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testUpdateContentRemoveAlwaysAvailableFlagMultilingual(): void + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects_multilingual.php' + ); + + $gateway = $this->getDatabaseGateway(); + $contentMetadataUpdateStruct = new MetadataUpdateStruct( + [ + 'mainLanguageId' => 4, + 'alwaysAvailable' => false, + ] + ); + $gateway->updateContent(4, $contentMetadataUpdateStruct); + + $this->assertQueryResult( + [['id' => 6]], + $this->getDatabaseConnection()->createQueryBuilder()->select( + ['language_mask'] + )->from( + 'ezcontentobject' + )->where( + 'id = 4' + ) + ); + + $this->assertContentVersionAttributesLanguages( + 4, + 2, + [ + ['id' => '7', 'language_id' => 2], + ['id' => '8', 'language_id' => 4], + ] + ); + + $this->assertContentVersionAttributesLanguages( + 4, + 1, + [ + ['id' => '7', 'language_id' => 2], + ['id' => '8', 'language_id' => 5], + ] + ); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function testLoadVersionInfo(): void + { + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + + $resFirst = $gateway->loadVersionInfo(11, 1); + $resSecond = $gateway->loadVersionInfo(11, 2); + + $res = array_merge($resFirst, $resSecond); + + $orig = include __DIR__ . '/../_fixtures/extract_version_info_from_rows_multiple_versions.php'; + + $this->assertEquals($orig, $res, 'Fixtures differ between what was previously stored(expected) and what it now generates(actual), this hints either some mistake in impl or that the fixture (../_fixtures/extract_content_from_rows_multiple_versions.php) and tests needs to be adapted.'); + } + + /** + * Counts the number of relations in the database. + * + * @param int $fromId + * @param int $toId + * + * @return int + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function countContentRelations(?int $fromId = null, ?int $toId = null): int + { + $connection = $this->getDatabaseConnection(); + $dbPlatform = $connection->getDatabasePlatform(); + $query = $connection->createQueryBuilder(); + $query + ->select($dbPlatform->getCountExpression('id')) + ->from('ezcontentobject_link'); + + if ($fromId !== null) { + $query->where( + $query->expr()->eq( + 'from_contentobject_id', + $query->createPositionalParameter($fromId) + ) + ); + } + if ($toId !== null) { + $query->andWhere( + $query->expr()->eq( + 'to_contentobject_id', + $query->createPositionalParameter($toId) + ) + ); + } + + $statement = $query->execute(); + + return (int)$statement->fetchColumn(); + } + + /** + * Counts the number of fields. + * + * @param int $contentId + * + * @return int + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function countContentFields(?int $contentId = null): int + { + $connection = $this->getDatabaseConnection(); + $dbPlatform = $connection->getDatabasePlatform(); + + $query = $connection->createQueryBuilder(); + $query + ->select($dbPlatform->getCountExpression('id')) + ->from('ezcontentobject_attribute'); + + if ($contentId !== null) { + $query->where( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ); + } + + $statement = $query->execute(); + + return (int)$statement->fetchColumn(); + } + + /** + * Counts the number of versions. + * + * @param int $contentId + * + * @return int + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function countContentVersions(?int $contentId = null): int + { + $connection = $this->getDatabaseConnection(); + $dbPlatform = $connection->getDatabasePlatform(); + + $query = $connection->createQueryBuilder(); + $query + ->select($dbPlatform->getCountExpression('id')) + ->from('ezcontentobject_version'); + + if ($contentId !== null) { + $query->where( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ); + } + + $statement = $query->execute(); + + return (int)$statement->fetchColumn(); + } + + /** + * Counts the number of content names. + * + * @param int $contentId + * + * @return int + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function countContentNames(?int $contentId = null): int + { + $connection = $this->getDatabaseConnection(); + $dbPlatform = $connection->getDatabasePlatform(); + + $query = $connection->createQueryBuilder(); + $query + ->select($dbPlatform->getCountExpression('contentobject_id')) + ->from('ezcontentobject_name'); + + if ($contentId !== null) { + $query->where( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ); + } + + $statement = $query->execute(); + + return (int)$statement->fetchColumn(); + } + + /** + * Counts the number of content objects. + * + * @param int|null $contentId + * + * @return int + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function countContent(?int $contentId = null): int + { + $connection = $this->getDatabaseConnection(); + $dbPlatform = $connection->getDatabasePlatform(); + $query = $connection->createQueryBuilder(); + $query + ->select($dbPlatform->getCountExpression('id')) + ->from('ezcontentobject'); + + if ($contentId !== null) { + $query->where( + $query->expr()->eq( + 'id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ); + } + + $statement = $query->execute(); + + return (int)$statement->fetchColumn(); + } + + /** + * Stores $fixture in $file to be required as a fixture. + * + * @param string $file + * @param mixed $fixture + */ + protected function storeFixture($file, $fixture) + { + file_put_contents( + $file, + "<?php\n\nreturn " . str_replace(" \n", "\n", var_export($fixture, true)) . ";\n" + ); + } + + /** + * Returns a Field fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Field + */ + protected function getFieldFixture() + { + $field = new Field(); + + $field->fieldDefinitionId = 231; + $field->type = 'ezstring'; + $field->languageCode = self::ENG_GB; + $field->versionNo = 1; + + return $field; + } + + /** + * Returns a Field fixture in a different language. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Field + */ + protected function getOtherLanguageFieldFixture() + { + $field = $this->getFieldFixture(); + $field->languageCode = 'eng-US'; + + return $field; + } + + /** + * Returns a StorageFieldValue fixture. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue + */ + protected function getStorageValueFixture() + { + $value = new StorageFieldValue(); + + $value->dataFloat = 24.42; + $value->dataInt = 42; + $value->dataText = 'Test text'; + $value->sortKeyInt = 23; + $value->sortKeyString = 'Test'; + + return $value; + } + + /** + * Returns a ready to test DoctrineDatabase gateway. + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function getDatabaseGateway(): DoctrineDatabase + { + if (!isset($this->databaseGateway)) { + $connection = $this->getDatabaseConnection(); + $this->databaseGateway = new DoctrineDatabase( + $connection, + $this->getSharedGateway(), + new DoctrineDatabase\QueryBuilder($connection), + $this->getLanguageHandler(), + $this->getLanguageMaskGenerator() + ); + } + + return $this->databaseGateway; + } + + /** + * DoctrineDatabaseTest::getRelationCreateStructFixture(). + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Relation\CreateStruct + */ + protected function getRelationCreateStructFixture() + { + $struct = new RelationCreateStruct(); + + $struct->destinationContentId = 1; + $struct->sourceContentId = 1; + $struct->sourceContentVersionNo = 1; + $struct->sourceFieldDefinitionId = 0; + $struct->type = RelationValue::COMMON; + + return $struct; + } + + /** + * @param int $contentId + * @param int $versionNo + * @param array $expectation + * + * @throws \Doctrine\DBAL\DBALException + */ + private function assertContentVersionAttributesLanguages( + int $contentId, + int $versionNo, + array $expectation + ): void { + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + $expectation, + $query + ->select('DISTINCT id, language_id') + ->from('ezcontentobject_attribute') + ->where( + $query->expr()->andX( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'version', + $query->createPositionalParameter($versionNo, ParameterType::INTEGER) + ) + ) + ) + ->orderBy('id') + ); + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Gateway\DoctrineDatabaseTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Gateway/RandomSortClauseHandlerFactoryTest.php b/tests/lib/Persistence/Legacy/Content/Gateway/RandomSortClauseHandlerFactoryTest.php new file mode 100644 index 0000000000..627062083b --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Gateway/RandomSortClauseHandlerFactoryTest.php @@ -0,0 +1,102 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Gateway; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Platforms\AbstractPlatform; +use Ibexa\Core\Base\Exceptions\InvalidArgumentException; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\Factory\RandomSortClauseHandlerFactory; +use PHPUnit\Framework\TestCase; + +class RandomSortClauseHandlerFactoryTest extends TestCase +{ + /** + * @dataProvider getGateways + * + * @param \Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom[] $gateways + * + * @throws \Doctrine\DBAL\DBALException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + */ + public function testGetGateway(array $gateways) + { + $platform = $this->createMock(AbstractPlatform::class); + + $platform + ->method('getName') + ->willReturn('testStorage'); + + $connection = $this->createMock(Connection::class); + + $connection + ->method('getDatabasePlatform') + ->willReturn($platform); + + $handlerFactory = new RandomSortClauseHandlerFactory($connection, $gateways); + $this->assertEquals( + 'testStorage', + $handlerFactory->getGateway()->getDriverName() + ); + } + + /** + * @dataProvider getGateways + * + * @param \Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler\AbstractRandom[] $gateways + * + * @throws \Doctrine\DBAL\DBALException + * @throws \Ibexa\Core\Base\Exceptions\InvalidArgumentException + */ + public function testGetGatewayNotImplemented(array $gateways) + { + $platform = $this->createMock(AbstractPlatform::class); + + $platform + ->method('getName') + ->willReturn('notImplemented'); + + $connection = $this->createMock(Connection::class); + + $connection + ->method('getDatabasePlatform') + ->willReturn($platform); + + $handlerFactory = new RandomSortClauseHandlerFactory($connection, $gateways); + + $this->expectException(InvalidArgumentException::class); + $handlerFactory->getGateway(); + } + + public function getGateways(): array + { + $goodGateway = $this + ->createMock(AbstractRandom::class); + $goodGateway + ->method('getDriverName') + ->willReturn('testStorage'); + + $badGateway = $this + ->createMock(AbstractRandom::class); + $badGateway + ->method('getDriverName') + ->willReturn('otherStorage'); + + return [ + [ + [ + $goodGateway, + $badGateway, + ], + ], + ]; + } +} + +class_alias(RandomSortClauseHandlerFactoryTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Gateway\RandomSortClauseHandlerFactoryTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/CachingLanguageHandlerTest.php b/tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php similarity index 81% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/CachingLanguageHandlerTest.php rename to tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php index 11d293cf89..6bae3ef379 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/CachingLanguageHandlerTest.php +++ b/tests/lib/Persistence/Legacy/Content/Language/CachingLanguageHandlerTest.php @@ -4,50 +4,47 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Language; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException as APINotFoundException; -use eZ\Publish\Core\Base\Exceptions\NotFoundException; -use eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Content\Language; -use eZ\Publish\SPI\Persistence\Content\Language\CreateStruct as SPILanguageCreateStruct; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as SPILanguageHandler; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Language; + +use Ibexa\Contracts\Core\Persistence\Content\Language; +use Ibexa\Contracts\Core\Persistence\Content\Language\CreateStruct as SPILanguageCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as SPILanguageHandler; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as APINotFoundException; +use Ibexa\Core\Base\Exceptions\NotFoundException; use Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface; +use Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache; +use Ibexa\Core\Persistence\Legacy\Content\Language\CachingHandler; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; /** - * Test case for caching Language Handler. + * @covers \Ibexa\Core\Persistence\Legacy\Content\Language\CachingHandler */ class CachingLanguageHandlerTest extends TestCase { /** * Language handler. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler + * @var \Ibexa\Core\Persistence\Legacy\Content\Language\Handler */ protected $languageHandler; /** * Inner language handler mock. * - * @var \eZ\Publish\SPI\Persistence\Content\Language\Handler + * @var \Ibexa\Contracts\Core\Persistence\Content\Language\Handler */ protected $innerHandlerMock; /** * Language cache mock. * - * @var \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache + * @var \Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache */ protected $languageCacheMock; /** @var \Ibexa\Core\Persistence\Cache\Identifier\CacheIdentifierGeneratorInterface */ protected $cacheIdentifierGeneratorMock; - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler::create - */ public function testCreate() { $handler = $this->getLanguageHandler(); @@ -81,7 +78,7 @@ public function testCreate() /** * Returns a Language CreateStruct. * - * @return \eZ\Publish\SPI\Persistence\Content\Language\CreateStruct + * @return \Ibexa\Contracts\Core\Persistence\Content\Language\CreateStruct */ protected function getCreateStructFixture() { @@ -91,7 +88,7 @@ protected function getCreateStructFixture() /** * Returns a Language. * - * @return \eZ\Publish\SPI\Persistence\Content\Language + * @return \Ibexa\Contracts\Core\Persistence\Content\Language */ protected function getLanguageFixture() { @@ -102,9 +99,6 @@ protected function getLanguageFixture() return $language; } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler::update - */ public function testUpdate() { $handler = $this->getLanguageHandler(); @@ -124,9 +118,6 @@ public function testUpdate() $handler->update($languageFixture); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler::load - */ public function testLoad() { $handler = $this->getLanguageHandler(); @@ -151,9 +142,6 @@ public function testLoad() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler::load - */ public function testLoadFailure() { $handler = $this->getLanguageHandler(); @@ -184,9 +172,6 @@ public function testLoadFailure() $handler->load(2); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler::loadByLanguageCode - */ public function testLoadByLanguageCode() { $handler = $this->getLanguageHandler(); @@ -211,9 +196,6 @@ public function testLoadByLanguageCode() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler::loadByLanguageCode - */ public function testLoadByLanguageCodeFailure() { $handler = $this->getLanguageHandler(); @@ -244,9 +226,6 @@ public function testLoadByLanguageCodeFailure() $handler->loadByLanguageCode('eng-US'); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler::loadAll - */ public function testLoadAll() { $handler = $this->getLanguageHandler(); @@ -268,9 +247,6 @@ public function testLoadAll() $this->assertIsArray($result); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler::delete - */ public function testDelete() { $handler = $this->getLanguageHandler(); @@ -303,7 +279,7 @@ public function testDelete() /** * Returns the language handler to test. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingHandler + * @return \Ibexa\Core\Persistence\Legacy\Content\Language\CachingHandler */ protected function getLanguageHandler() { @@ -321,7 +297,7 @@ protected function getLanguageHandler() /** * Returns a mock for the inner language handler. * - * @return \eZ\Publish\SPI\Persistence\Content\Language\Handler|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\Persistence\Content\Language\Handler|\PHPUnit\Framework\MockObject\MockObject */ protected function getInnerLanguageHandlerMock() { @@ -335,7 +311,7 @@ protected function getInnerLanguageHandlerMock() /** * Returns a mock for the in-memory cache. * - * @return \eZ\Publish\Core\Persistence\Cache\InMemory\InMemoryCache|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Persistence\Cache\InMemory\InMemoryCache|\PHPUnit\Framework\MockObject\MockObject */ protected function getLanguageCacheMock() { @@ -361,7 +337,7 @@ protected function getCacheIdentifierGeneratorMock() /** * Returns an array with 2 languages. * - * @return \eZ\Publish\SPI\Persistence\Content\Language[] + * @return \Ibexa\Contracts\Core\Persistence\Content\Language[] */ protected function getLanguagesFixture() { @@ -380,3 +356,5 @@ protected function getLanguagesFixture() return [$langUs, $langGb]; } } + +class_alias(CachingLanguageHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Language\CachingLanguageHandlerTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..37f9fdfa68 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Language/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,191 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Language\Gateway; + +use Ibexa\Contracts\Core\Persistence\Content\Language; +use Ibexa\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase + */ +class DoctrineDatabaseTest extends TestCase +{ + /** + * Database gateway to test. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase + */ + protected $databaseGateway; + + /** + * Inserts DB fixture. + */ + protected function setUp(): void + { + parent::setUp(); + + $this->insertDatabaseFixture( + __DIR__ . '/../../_fixtures/languages.php' + ); + } + + public function testInsertLanguage() + { + $gateway = $this->getDatabaseGateway(); + + $gateway->insertLanguage($this->getLanguageFixture()); + + $this->assertQueryResult( + [ + [ + 'id' => '8', + 'locale' => 'de-DE', + 'name' => 'Deutsch (Deutschland)', + 'disabled' => '0', + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('id', 'locale', 'name', 'disabled') + ->from('ezcontent_language') + ->where('id=8') + ); + } + + /** + * Returns a Language fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Language + */ + protected function getLanguageFixture() + { + $language = new Language(); + + $language->languageCode = 'de-DE'; + $language->name = 'Deutsch (Deutschland)'; + $language->isEnabled = true; + + return $language; + } + + public function testUpdateLanguage() + { + $gateway = $this->getDatabaseGateway(); + + $language = $this->getLanguageFixture(); + $language->id = 2; + + $gateway->updateLanguage($language); + + $this->assertQueryResult( + [ + [ + 'id' => '2', + 'locale' => 'de-DE', + 'name' => 'Deutsch (Deutschland)', + 'disabled' => '0', + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('id', 'locale', 'name', 'disabled') + ->from('ezcontent_language') + ->where('id=2') + ); + } + + public function testLoadLanguageListData() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->loadLanguageListData([2]); + + $this->assertEquals( + [ + [ + 'id' => '2', + 'locale' => 'eng-US', + 'name' => 'English (American)', + 'disabled' => '0', + ], + ], + $result + ); + } + + public function testLoadAllLanguagesData() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->loadAllLanguagesData(); + + $this->assertEquals( + [ + [ + 'id' => '2', + 'locale' => 'eng-US', + 'name' => 'English (American)', + 'disabled' => '0', + ], + [ + 'id' => '4', + 'locale' => 'eng-GB', + 'name' => 'English (United Kingdom)', + 'disabled' => '0', + ], + ], + $result + ); + } + + public function testDeleteLanguage() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->deleteLanguage(2); + + $this->assertQueryResult( + [ + [ + 'count' => '1', + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('COUNT( * ) AS count') + ->from('ezcontent_language') + ); + + $this->assertQueryResult( + [ + [ + 'count' => '0', + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('COUNT( * ) AS count') + ->from('ezcontent_language') + ->where('id=2') + ); + } + + /** + * Return a ready to test DoctrineDatabase gateway. + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function getDatabaseGateway(): DoctrineDatabase + { + if (!isset($this->databaseGateway)) { + $this->databaseGateway = new DoctrineDatabase( + $this->getDatabaseConnection() + ); + } + + return $this->databaseGateway; + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Language\Gateway\DoctrineDatabaseTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/LanguageHandlerTest.php b/tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php similarity index 76% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/LanguageHandlerTest.php rename to tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php index cb9f5665ae..764f54b114 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/LanguageHandlerTest.php +++ b/tests/lib/Persistence/Legacy/Content/Language/LanguageHandlerTest.php @@ -4,44 +4,42 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Language; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Language; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway as LanguageGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\Mapper as LanguageMapper; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Content\Language; -use eZ\Publish\SPI\Persistence\Content\Language\CreateStruct as SPILanguageCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Language; +use Ibexa\Contracts\Core\Persistence\Content\Language\CreateStruct as SPILanguageCreateStruct; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Core\Persistence\Legacy\Content\Language\Gateway as LanguageGateway; +use Ibexa\Core\Persistence\Legacy\Content\Language\Handler; +use Ibexa\Core\Persistence\Legacy\Content\Language\Mapper as LanguageMapper; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; /** - * Test case for Language Handler. + * @covers \Ibexa\Core\Persistence\Legacy\Content\Language\Handler */ class LanguageHandlerTest extends TestCase { /** * Language handler. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler + * @var \Ibexa\Core\Persistence\Legacy\Content\Language\Handler */ protected $languageHandler; /** * Language gateway mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway + * @var \Ibexa\Core\Persistence\Legacy\Content\Language\Gateway */ protected $gatewayMock; /** * Language mapper mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Mapper + * @var \Ibexa\Core\Persistence\Legacy\Content\Language\Mapper */ protected $mapperMock; - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler::create - */ public function testCreate() { $handler = $this->getLanguageHandler(); @@ -81,16 +79,13 @@ public function testCreate() /** * Returns a Language CreateStruct. * - * @return \eZ\Publish\SPI\Persistence\Content\Language\CreateStruct + * @return \Ibexa\Contracts\Core\Persistence\Content\Language\CreateStruct */ protected function getCreateStructFixture() { return new Language\CreateStruct(); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler::update - */ public function testUpdate() { $handler = $this->getLanguageHandler(); @@ -106,16 +101,13 @@ public function testUpdate() /** * Returns a Language. * - * @return \eZ\Publish\SPI\Persistence\Content\Language + * @return \Ibexa\Contracts\Core\Persistence\Content\Language */ protected function getLanguageFixture() { return new Language(); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler::load - */ public function testLoad() { $handler = $this->getLanguageHandler(); @@ -140,12 +132,9 @@ public function testLoad() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler::load - */ public function testLoadFailure() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $handler = $this->getLanguageHandler(); $mapperMock = $this->getMapperMock(); @@ -165,9 +154,6 @@ public function testLoadFailure() $result = $handler->load(2); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler::loadByLanguageCode - */ public function testLoadByLanguageCode() { $handler = $this->getLanguageHandler(); @@ -192,12 +178,9 @@ public function testLoadByLanguageCode() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler::loadByLanguageCode - */ public function testLoadByLanguageCodeFailure() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $handler = $this->getLanguageHandler(); $mapperMock = $this->getMapperMock(); @@ -217,9 +200,6 @@ public function testLoadByLanguageCodeFailure() $result = $handler->loadByLanguageCode('eng-US'); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler::loadAll - */ public function testLoadAll() { $handler = $this->getLanguageHandler(); @@ -242,9 +222,6 @@ public function testLoadAll() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler::delete - */ public function testDeleteSuccess() { $handler = $this->getLanguageHandler(); @@ -261,9 +238,6 @@ public function testDeleteSuccess() $result = $handler->delete(2); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler::delete - */ public function testDeleteFail() { $this->expectException(\LogicException::class); @@ -284,7 +258,7 @@ public function testDeleteFail() /** * Returns the language handler to test. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler + * @return \Ibexa\Core\Persistence\Legacy\Content\Language\Handler */ protected function getLanguageHandler() { @@ -301,7 +275,7 @@ protected function getLanguageHandler() /** * Returns a language mapper mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Language\Mapper + * @return \Ibexa\Core\Persistence\Legacy\Content\Language\Mapper */ protected function getMapperMock() { @@ -315,7 +289,7 @@ protected function getMapperMock() /** * Returns a mock for the language gateway. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Language\Gateway + * @return \Ibexa\Core\Persistence\Legacy\Content\Language\Gateway */ protected function getGatewayMock() { @@ -326,3 +300,5 @@ protected function getGatewayMock() return $this->gatewayMock; } } + +class_alias(LanguageHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Language\LanguageHandlerTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Language/MapperTest.php b/tests/lib/Persistence/Legacy/Content/Language/MapperTest.php new file mode 100644 index 0000000000..f9724dd435 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Language/MapperTest.php @@ -0,0 +1,116 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Language; + +use Ibexa\Contracts\Core\Persistence\Content\Language; +use Ibexa\Contracts\Core\Persistence\Content\Language\CreateStruct; +use Ibexa\Core\Persistence\Legacy\Content\Language\Mapper; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Language\Mapper + */ +class MapperTest extends TestCase +{ + public function testCreateLanguageFromCreateStruct() + { + $mapper = new Mapper(); + + $createStruct = $this->getCreateStructFixture(); + + $result = $mapper->createLanguageFromCreateStruct($createStruct); + + $this->assertStructsEqual( + $this->getLanguageFixture(), + $result, + ['languageCode', 'name', 'isEnabled'] + ); + } + + public function testExtractLanguagesFromRows() + { + $mapper = new Mapper(); + + $rows = $this->getRowsFixture(); + + $result = $mapper->extractLanguagesFromRows($rows); + + $this->assertEquals( + $this->getExtractReference(), + $result + ); + } + + /** + * Returns a result rows fixture. + * + * @return string[][] + */ + protected function getRowsFixture() + { + return [ + ['disabled' => '0', 'id' => '2', 'locale' => 'eng-US', 'name' => 'English (American)'], + ['disabled' => '0', 'id' => '4', 'locale' => 'eng-GB', 'name' => 'English (United Kingdom)'], + ]; + } + + /** + * Returns reference for the extraction from rows. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Language[] + */ + protected function getExtractReference() + { + $langUs = new Language(); + $langUs->id = 2; + $langUs->languageCode = 'eng-US'; + $langUs->name = 'English (American)'; + $langUs->isEnabled = true; + + $langGb = new Language(); + $langGb->id = 4; + $langGb->languageCode = 'eng-GB'; + $langGb->name = 'English (United Kingdom)'; + $langGb->isEnabled = true; + + return ['eng-US' => $langUs, 'eng-GB' => $langGb]; + } + + /** + * Returns a Language CreateStruct fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Language\CreateStruct + */ + protected function getCreateStructFixture() + { + $struct = new CreateStruct(); + + $struct->languageCode = 'de-DE'; + $struct->name = 'Deutsch (Deutschland)'; + $struct->isEnabled = true; + + return $struct; + } + + /** + * Returns a Language fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Language + */ + protected function getLanguageFixture() + { + $struct = new Language(); + + $struct->languageCode = 'de-DE'; + $struct->name = 'Deutsch (Deutschland)'; + $struct->isEnabled = true; + + return $struct; + } +} + +class_alias(MapperTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Language\MapperTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/MaskGeneratorTest.php b/tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php similarity index 84% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/MaskGeneratorTest.php rename to tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php index d94acd56cf..5f94935f24 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Language/MaskGeneratorTest.php +++ b/tests/lib/Persistence/Legacy/Content/Language/MaskGeneratorTest.php @@ -4,23 +4,19 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Language; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Language; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator; -use eZ\Publish\Core\Persistence\Legacy\Tests\Content\LanguageAwareTestCase; -use eZ\Publish\SPI\Persistence\Content\Language; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; +use Ibexa\Contracts\Core\Persistence\Content\Language; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler; +use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator; +use Ibexa\Tests\Core\Persistence\Legacy\Content\LanguageAwareTestCase; -/** - * Test case for Language MaskGenerator. - */ class MaskGeneratorTest extends LanguageAwareTestCase { /** * @param array $languages * @param int $expectedMask * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator::generateLanguageMask * @dataProvider getLanguageMaskData */ public function testGenerateLanguageMask(array $languages, $expectedMask) @@ -37,7 +33,7 @@ public function testGenerateLanguageMask(array $languages, $expectedMask) * @param array $languages * @param int $expectedMask * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator::generateLanguageMaskFromLanguageCodes + * * @dataProvider getLanguageMaskData */ public function testGenerateLanguageMaskFromLanguagesCodes(array $languages, $expectedMask) @@ -93,7 +89,6 @@ public static function getLanguageMaskData() * @param bool $alwaysAvailable * @param int $expectedIndicator * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator::generateLanguageIndicator * @dataProvider getLanguageIndicatorData */ public function testGenerateLanguageIndicator( @@ -130,9 +125,6 @@ public static function getLanguageIndicatorData() ]; } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator::isLanguageAlwaysAvailable - */ public function testIsLanguageAlwaysAvailable() { $generator = $this->getMaskGenerator(); @@ -148,9 +140,6 @@ public function testIsLanguageAlwaysAvailable() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator::isLanguageAlwaysAvailable - */ public function testIsLanguageAlwaysAvailableOtherLanguage() { $generator = $this->getMaskGenerator(); @@ -166,9 +155,6 @@ public function testIsLanguageAlwaysAvailableOtherLanguage() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator::isLanguageAlwaysAvailable - */ public function testIsLanguageAlwaysAvailableNoDefault() { $generator = $this->getMaskGenerator(); @@ -184,10 +170,9 @@ public function testIsLanguageAlwaysAvailableNoDefault() } /** - * @param int $languageMask + * @param int $langMask * @param bool $expectedResult * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator::isAlwaysAvailable * @dataProvider isAlwaysAvailableProvider */ public function testIsAlwaysAvailable($langMask, $expectedResult) @@ -213,7 +198,6 @@ public function isAlwaysAvailableProvider() } /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator::removeAlwaysAvailableFlag * @dataProvider removeAlwaysAvailableFlagProvider */ public function testRemoveAlwaysAvailableFlag($langMask, $expectedResult) @@ -241,7 +225,6 @@ public function removeAlwaysAvailableFlagProvider() * @param int $langMask * @param array $expectedResult * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator::extractLanguageIdsFromMask * @dataProvider languageIdsFromMaskProvider */ public function testExtractLanguageIdsFromMask($langMask, array $expectedResult) @@ -276,7 +259,7 @@ public function languageIdsFromMaskProvider() /** * Returns the mask generator to test. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator + * @return \Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator */ protected function getMaskGenerator() { @@ -286,7 +269,7 @@ protected function getMaskGenerator() /** * Returns a language handler mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler + * @return \Ibexa\Core\Persistence\Legacy\Content\Language\Handler */ protected function getLanguageHandler() { @@ -332,3 +315,5 @@ static function ($languageCodes) { return $this->languageHandler; } } + +class_alias(MaskGeneratorTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Language\MaskGeneratorTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/LanguageAwareTestCase.php b/tests/lib/Persistence/Legacy/Content/LanguageAwareTestCase.php similarity index 77% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/LanguageAwareTestCase.php rename to tests/lib/Persistence/Legacy/Content/LanguageAwareTestCase.php index ddb9b1c61e..02d95b6425 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/LanguageAwareTestCase.php +++ b/tests/lib/Persistence/Legacy/Content/LanguageAwareTestCase.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content; -use eZ\Publish\Core\Persistence; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\Core\Search\Common\FieldNameGenerator; -use eZ\Publish\Core\Search\Common\FieldRegistry; -use eZ\Publish\Core\Search\Legacy\Content\Mapper\FullTextMapper; +use Ibexa\Core\Persistence; +use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; +use Ibexa\Core\Search\Common\FieldNameGenerator; +use Ibexa\Core\Search\Common\FieldRegistry; +use Ibexa\Core\Search\Legacy\Content\Mapper\FullTextMapper; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; /** * Test case for Language aware classes. @@ -23,21 +23,21 @@ abstract class LanguageAwareTestCase extends TestCase /** * Language handler. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\CachingLanguageHandler + * @var \Ibexa\Contracts\Core\Persistence\Content\Language\Handler */ protected $languageHandler; /** * Language mask generator. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator + * @var \Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator */ protected $languageMaskGenerator; /** * Returns a language handler mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler + * @return \Ibexa\Contracts\Core\Persistence\Content\Language\Handler */ protected function getLanguageHandler() { @@ -51,7 +51,7 @@ protected function getLanguageHandler() /** * Returns a language mask generator. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator + * @return \Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator */ protected function getLanguageMaskGenerator() { @@ -80,11 +80,11 @@ protected function getDefinitionBasedTransformationProcessor() ); } - /** @var \eZ\Publish\Core\Search\Common\FieldNameGenerator|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\Search\Common\FieldNameGenerator|\PHPUnit\Framework\MockObject\MockObject */ protected $fieldNameGeneratorMock; /** - * @return \eZ\Publish\Core\Search\Common\FieldNameGenerator|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Search\Common\FieldNameGenerator|\PHPUnit\Framework\MockObject\MockObject */ protected function getFieldNameGeneratorMock() { @@ -96,9 +96,9 @@ protected function getFieldNameGeneratorMock() } /** - * @param \eZ\Publish\Core\Persistence\Legacy\Content\Type\Handler $contentTypeHandler + * @param \Ibexa\Core\Persistence\Legacy\Content\Type\Handler $contentTypeHandler * - * @return \eZ\Publish\Core\Search\Legacy\Content\Mapper\FullTextMapper + * @return \Ibexa\Core\Search\Legacy\Content\Mapper\FullTextMapper */ protected function getFullTextMapper(Persistence\Legacy\Content\Type\Handler $contentTypeHandler) { @@ -153,3 +153,5 @@ protected function getFullTextSearchConfiguration() ]; } } + +class_alias(LanguageAwareTestCase::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\LanguageAwareTestCase'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/LanguageHandlerMock.php b/tests/lib/Persistence/Legacy/Content/LanguageHandlerMock.php similarity index 75% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/LanguageHandlerMock.php rename to tests/lib/Persistence/Legacy/Content/LanguageHandlerMock.php index bfe38358ea..5b37fbec5e 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/LanguageHandlerMock.php +++ b/tests/lib/Persistence/Legacy/Content/LanguageHandlerMock.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content; -use eZ\Publish\SPI\Persistence\Content\Language; -use eZ\Publish\SPI\Persistence\Content\Language\CreateStruct; -use eZ\Publish\SPI\Persistence\Content\Language\Handler as LanguageHandler; +use Ibexa\Contracts\Core\Persistence\Content\Language; +use Ibexa\Contracts\Core\Persistence\Content\Language\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as LanguageHandler; /** * Simple mock for a Language\Handler. @@ -45,9 +45,9 @@ public function __construct() /** * Create a new language. * - * @param \eZ\Publish\SPI\Persistence\Content\Language\CreateStruct $struct + * @param \Ibexa\Contracts\Core\Persistence\Content\Language\CreateStruct $struct * - * @return \eZ\Publish\SPI\Persistence\Content\Language + * @return \Ibexa\Contracts\Core\Persistence\Content\Language */ public function create(CreateStruct $struct) { @@ -57,7 +57,7 @@ public function create(CreateStruct $struct) /** * Update language. * - * @param \eZ\Publish\SPI\Persistence\Content\Language $struct + * @param \Ibexa\Contracts\Core\Persistence\Content\Language $struct */ public function update(Language $struct) { @@ -69,9 +69,9 @@ public function update(Language $struct) * * @param mixed $id * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language could not be found by $id + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If language could not be found by $id * - * @return \eZ\Publish\SPI\Persistence\Content\Language + * @return \Ibexa\Contracts\Core\Persistence\Content\Language */ public function load($id) { @@ -88,9 +88,9 @@ public function load($id) * * @param string $languageCode * - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException If language could not be found by $languageCode + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException If language could not be found by $languageCode * - * @return \eZ\Publish\SPI\Persistence\Content\Language + * @return \Ibexa\Contracts\Core\Persistence\Content\Language */ public function loadByLanguageCode($languageCode) { @@ -107,7 +107,7 @@ public function loadByLanguageCode($languageCode) * * Return list of languages where key of hash is language code. * - * @return \eZ\Publish\SPI\Persistence\Content\Language[] + * @return \Ibexa\Contracts\Core\Persistence\Content\Language[] */ public function loadAll() { @@ -156,3 +156,5 @@ public function loadListByLanguageCodes(array $languageCodes): iterable return $languages; } } + +class_alias(LanguageHandlerMock::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\LanguageHandlerMock'); diff --git a/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..789530d83d --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,1483 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Location\Gateway; + +use Doctrine\DBAL\FetchMode; +use Doctrine\DBAL\ParameterType; +use Doctrine\DBAL\Query\QueryBuilder; +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\Location\CreateStruct; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase; +use Ibexa\Core\Search\Legacy\Content; +use Ibexa\Tests\Core\Persistence\Legacy\Content\LanguageAwareTestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase + */ +class DoctrineDatabaseTest extends LanguageAwareTestCase +{ + protected function getLocationGateway() + { + return new DoctrineDatabase( + $this->getDatabaseConnection(), + $this->getLanguageMaskGenerator(), + $this->getTrashCriteriaConverterDependency(), + $this->getTrashSortClauseConverterDependency() + ); + } + + private static function getLoadLocationValues(): array + { + return [ + 'node_id' => 77, + 'priority' => 0, + 'is_hidden' => 0, + 'is_invisible' => 0, + 'remote_id' => 'dbc2f3c8716c12f32c379dbf0b1cb133', + 'contentobject_id' => 75, + 'parent_node_id' => 2, + 'path_identification_string' => 'solutions', + 'path_string' => '/1/2/77/', + 'modified_subnode' => 1311065017, + 'main_node_id' => 77, + 'depth' => 2, + 'sort_field' => 2, + 'sort_order' => 1, + ]; + } + + private function assertLoadLocationProperties(array $locationData): void + { + foreach (self::getLoadLocationValues() as $field => $expectedValue) { + self::assertEquals( + $expectedValue, + $locationData[$field], + "Value in property $field not as expected." + ); + } + } + + public function testLoadLocationByRemoteId() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $data = $gateway->getBasicNodeDataByRemoteId('dbc2f3c8716c12f32c379dbf0b1cb133'); + + self::assertLoadLocationProperties($data); + } + + public function testLoadLocation() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $data = $gateway->getBasicNodeData(77); + + self::assertLoadLocationProperties($data); + } + + public function testLoadLocationList() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $locationsData = $gateway->getNodeDataList([77]); + + self::assertCount(1, $locationsData); + + $locationRow = reset($locationsData); + + self::assertLoadLocationProperties($locationRow); + } + + public function testLoadInvalidLocation() + { + $this->expectException(NotFoundException::class); + + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->getBasicNodeData(1337); + } + + public function testLoadLocationDataByContent() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + + $gateway = $this->getLocationGateway(); + + $locationsData = $gateway->loadLocationDataByContent(75); + + self::assertCount(1, $locationsData); + + $locationRow = reset($locationsData); + + self::assertLoadLocationProperties($locationRow); + } + + public function testLoadParentLocationDataForDraftContentAll() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + + $gateway = $this->getLocationGateway(); + + $locationsData = $gateway->loadParentLocationsDataForDraftContent(226); + + $this->assertCount(1, $locationsData); + + $locationRow = reset($locationsData); + + self::assertLoadLocationProperties($locationRow); + } + + public function testLoadLocationDataByContentLimitSubtree() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + + $gateway = $this->getLocationGateway(); + + $locationsData = $gateway->loadLocationDataByContent(75, 3); + + $this->assertCount(0, $locationsData); + } + + public function testMoveSubtreePathUpdate() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->moveSubtreeNodes( + [ + 'path_string' => '/1/2/69/', + 'contentobject_id' => 67, + 'path_identification_string' => 'products', + 'is_hidden' => 0, + 'is_invisible' => 0, + ], + [ + 'path_string' => '/1/2/77/', + 'path_identification_string' => 'solutions', + 'is_hidden' => 0, + 'is_invisible' => 0, + ] + ); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + [65, '/1/2/', '', 1, 1, 0, 0], + [67, '/1/2/77/69/', 'solutions/products', 77, 3, 0, 0], + [69, '/1/2/77/69/70/71/', 'solutions/products/software/os_type_i', 70, 5, 0, 0], + [73, '/1/2/77/69/72/75/', 'solutions/products/boxes/cd_dvd_box_iii', 72, 5, 0, 0], + [75, '/1/2/77/', 'solutions', 2, 2, 0, 0], + ], + $query + ->select( + 'contentobject_id', + 'path_string', + 'path_identification_string', + 'parent_node_id', + 'depth', + 'is_hidden', + 'is_invisible' + ) + ->from('ezcontentobject_tree') + ->where($query->expr()->in('node_id', [69, 71, 75, 77, 2])) + ->orderBy('contentobject_id') + ); + } + + public function testMoveHiddenDestinationUpdate() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->hideSubtree('/1/2/77/'); + $gateway->moveSubtreeNodes( + [ + 'path_string' => '/1/2/69/', + 'contentobject_id' => 67, + 'path_identification_string' => 'products', + 'is_hidden' => 0, + 'is_invisible' => 0, + ], + [ + 'path_string' => '/1/2/77/', + 'path_identification_string' => 'solutions', + 'is_hidden' => 1, + 'is_invisible' => 1, + ] + ); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + [65, '/1/2/', '', 1, 1, 0, 0], + [67, '/1/2/77/69/', 'solutions/products', 77, 3, 0, 1], + [69, '/1/2/77/69/70/71/', 'solutions/products/software/os_type_i', 70, 5, 0, 1], + [73, '/1/2/77/69/72/75/', 'solutions/products/boxes/cd_dvd_box_iii', 72, 5, 0, 1], + [75, '/1/2/77/', 'solutions', 2, 2, 1, 1], + ], + $query + ->select( + 'contentobject_id', + 'path_string', + 'path_identification_string', + 'parent_node_id', + 'depth', + 'is_hidden', + 'is_invisible' + ) + ->from('ezcontentobject_tree') + ->where($query->expr()->in('node_id', [69, 71, 75, 77, 2])) + ->orderBy('contentobject_id') + ); + } + + public function testMoveHiddenSourceUpdate() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->hideSubtree('/1/2/69/'); + $gateway->moveSubtreeNodes( + [ + 'path_string' => '/1/2/69/', + 'contentobject_id' => 67, + 'path_identification_string' => 'products', + 'is_hidden' => 1, + 'is_invisible' => 1, + ], + [ + 'path_string' => '/1/2/77/', + 'path_identification_string' => 'solutions', + 'is_hidden' => 0, + 'is_invisible' => 0, + ] + ); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + [65, '/1/2/', '', 1, 1, 0, 0], + [67, '/1/2/77/69/', 'solutions/products', 77, 3, 1, 1], + [69, '/1/2/77/69/70/71/', 'solutions/products/software/os_type_i', 70, 5, 0, 1], + [73, '/1/2/77/69/72/75/', 'solutions/products/boxes/cd_dvd_box_iii', 72, 5, 0, 1], + [75, '/1/2/77/', 'solutions', 2, 2, 0, 0], + ], + $query + ->select( + 'contentobject_id', + 'path_string', + 'path_identification_string', + 'parent_node_id', + 'depth', + 'is_hidden', + 'is_invisible' + ) + ->from('ezcontentobject_tree') + ->where($query->expr()->in('node_id', [69, 71, 75, 77, 2])) + ->orderBy('contentobject_id') + ); + } + + public function testMoveHiddenSourceChildUpdate() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->hideSubtree('/1/2/69/70/'); + + $gateway->moveSubtreeNodes( + [ + 'path_string' => '/1/2/69/', + 'contentobject_id' => 67, + 'path_identification_string' => 'products', + 'is_hidden' => 0, + 'is_invisible' => 0, + ], + [ + 'path_string' => '/1/2/77/', + 'path_identification_string' => 'solutions', + 'is_hidden' => 0, + 'is_invisible' => 0, + ] + ); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + [65, '/1/2/', '', 1, 1, 0, 0], + [67, '/1/2/77/69/', 'solutions/products', 77, 3, 0, 0], + [68, '/1/2/77/69/70/', 'solutions/products/software', 69, 4, 1, 1], + [69, '/1/2/77/69/70/71/', 'solutions/products/software/os_type_i', 70, 5, 0, 1], + [73, '/1/2/77/69/72/75/', 'solutions/products/boxes/cd_dvd_box_iii', 72, 5, 0, 0], + [75, '/1/2/77/', 'solutions', 2, 2, 0, 0], + ], + $query + ->select( + 'contentobject_id', + 'path_string', + 'path_identification_string', + 'parent_node_id', + 'depth', + 'is_hidden', + 'is_invisible' + ) + ->from('ezcontentobject_tree') + ->where($query->expr()->in('node_id', [69, 70, 71, 75, 77, 2])) + ->orderBy('contentobject_id') + ); + } + + /** + * @throws \Exception + */ + public function testMoveSubtreeAssignmentUpdate() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->updateNodeAssignment(67, 2, 77, 5); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + [67, 1, 0, 53, 1, 5, 77, '9cec85d730eec7578190ee95ce5a36f5', 0, 2, 1, 0, 0], + ], + $query + ->select( + [ + 'contentobject_id', + 'contentobject_version', + 'from_node_id', + 'id', + 'is_main', + 'op_code', + 'parent_node', + 'parent_remote_id', + 'remote_id', + 'sort_field', + 'sort_order', + 'priority', + 'is_hidden', + ] + ) + ->from('eznode_assignment') + ->where($query->expr()->eq('contentobject_id', 67)) + ); + } + + public function testUpdateSubtreeModificationTime() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $time = time(); + $gateway->updateSubtreeModificationTime('/1/2/69/'); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + ['/1/'], + ['/1/2/'], + ['/1/2/69/'], + ], + $query + ->select('path_string') + ->from('ezcontentobject_tree') + ->where($query->expr()->gte('modified_subnode', $time)) + ->orderBy('path_string') + ); + } + + public function testHideUpdateHidden() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->hideSubtree('/1/2/69/'); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + [1, 0, 0], + [2, 0, 0], + [69, 1, 1], + [75, 0, 1], + ], + $query + ->select('node_id', 'is_hidden', 'is_invisible') + ->from('ezcontentobject_tree') + ->where($query->expr()->in('node_id', [1, 2, 69, 75])) + ->orderBy('node_id') + ); + } + + /** + * @depends testHideUpdateHidden + */ + public function testHideUnhideUpdateHidden() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->hideSubtree('/1/2/69/'); + $gateway->unhideSubtree('/1/2/69/'); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + [1, 0, 0], + [2, 0, 0], + [69, 0, 0], + [75, 0, 0], + ], + $query + ->select('node_id', 'is_hidden', 'is_invisible') + ->from('ezcontentobject_tree') + ->where($query->expr()->in('node_id', [1, 2, 69, 75])) + ->orderBy('node_id') + ); + } + + /** + * @depends testHideUpdateHidden + */ + public function testHideUnhideParentTree() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->hideSubtree('/1/2/69/'); + $gateway->hideSubtree('/1/2/69/70/'); + $gateway->unhideSubtree('/1/2/69/'); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + [1, 0, 0], + [2, 0, 0], + [69, 0, 0], + [70, 1, 1], + [71, 0, 1], + [75, 0, 0], + ], + $query + ->select('node_id', 'is_hidden', 'is_invisible') + ->from('ezcontentobject_tree') + ->where($query->expr()->in('node_id', [1, 2, 69, 70, 71, 75])) + ->orderBy('node_id') + ); + } + + /** + * @depends testHideUpdateHidden + */ + public function testHideUnhidePartialSubtree() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->hideSubtree('/1/2/69/'); + $gateway->hideSubtree('/1/2/69/70/'); + $gateway->unhideSubtree('/1/2/69/70/'); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + [1, 0, 0], + [2, 0, 0], + [69, 1, 1], + [70, 0, 1], + [71, 0, 1], + [75, 0, 1], + ], + $query + ->select('node_id', 'is_hidden', 'is_invisible') + ->from('ezcontentobject_tree') + ->where($query->expr()->in('node_id', [1, 2, 69, 70, 71, 75])) + ->orderBy('node_id') + ); + } + + public function testSwapLocations() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->swap(70, 78); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + [70, 76], + [78, 68], + ], + $query + ->select('node_id', 'contentobject_id') + ->from('ezcontentobject_tree') + ->where($query->expr()->in('node_id', [70, 78])) + ->orderBy('node_id') + ); + } + + public function testCreateLocation() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->create( + new CreateStruct( + [ + 'contentId' => 68, + 'remoteId' => 'some_id', + ] + ), + [ + 'node_id' => '77', + 'depth' => '2', + 'path_string' => '/1/2/77/', + ] + ); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + [70, '/1/2/69/70/'], + [77, '/1/2/77/'], + [228, '/1/2/77/228/'], + ], + $query + ->select('node_id', 'path_string') + ->from('ezcontentobject_tree') + ->where($query->expr()->in('contentobject_id', [68, 75])) + ->orderBy('node_id') + ); + } + + /** + * @depends testCreateLocation + */ + public function testGetMainNodeId() + { + $gateway = $this->getLocationGateway(); + + $parentLocationData = [ + 'node_id' => '77', + 'depth' => '2', + 'path_string' => '/1/2/77/', + ]; + + // main location + $mainLocation = $gateway->create( + new CreateStruct( + [ + 'contentId' => 68, + 'contentVersion' => 1, + 'remoteId' => 'some_id', + 'mainLocationId' => true, + ] + ), + $parentLocationData + ); + + // secondary location + $gateway->create( + new CreateStruct( + [ + 'contentId' => 68, + 'contentVersion' => 1, + 'remoteId' => 'some_id', + 'mainLocationId' => $mainLocation->id, + ] + ), + $parentLocationData + ); + + $gatewayReflection = new \ReflectionObject($gateway); + $methodReflection = $gatewayReflection->getMethod('getMainNodeId'); + $methodReflection->setAccessible(true); + self::assertEquals($mainLocation->id, $res = $methodReflection->invoke($gateway, 68)); + } + + public static function getCreateLocationValues() + { + return [ + ['contentobject_id', 68], + ['contentobject_is_published', 1], + ['contentobject_version', 1], + ['depth', 3], + ['is_hidden', 0], + ['is_invisible', 0], + ['main_node_id', 42], + ['parent_node_id', 77], + ['path_identification_string', ''], + ['priority', 1], + ['remote_id', 'some_id'], + ['sort_field', 1], + ['sort_order', 1], + ]; + } + + /** + * @depends testCreateLocation + * @dataProvider getCreateLocationValues + */ + public function testCreateLocationValues($field, $value) + { + if ($value === null) { + $this->markTestIncomplete('Proper value setting yet unknown.'); + } + + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->create( + new CreateStruct( + [ + 'contentId' => 68, + 'contentVersion' => 1, + 'mainLocationId' => 42, + 'priority' => 1, + 'remoteId' => 'some_id', + 'sortField' => 1, + 'sortOrder' => 1, + ] + ), + [ + 'node_id' => '77', + 'depth' => '2', + 'path_string' => '/1/2/77/', + ] + ); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [[$value]], + $query + ->select($field) + ->from('ezcontentobject_tree') + ->where($query->expr()->eq('node_id', 228)) + ); + } + + public static function getCreateLocationReturnValues() + { + return [ + ['id', 228], + ['priority', 1], + ['hidden', false], + ['invisible', false], + ['remoteId', 'some_id'], + ['contentId', '68'], + ['parentId', '77'], + ['pathIdentificationString', ''], + ['pathString', '/1/2/77/228/'], + ['depth', 3], + ['sortField', 1], + ['sortOrder', 1], + ]; + } + + /** + * @depends testCreateLocation + * @dataProvider getCreateLocationReturnValues + */ + public function testCreateLocationReturnValues($field, $value) + { + if ($value === null) { + $this->markTestIncomplete('Proper value setting yet unknown.'); + } + + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $location = $gateway->create( + new CreateStruct( + [ + 'contentId' => 68, + 'contentVersion' => 1, + 'mainLocationId' => true, + 'priority' => 1, + 'remoteId' => 'some_id', + 'sortField' => 1, + 'sortOrder' => 1, + ] + ), + [ + 'node_id' => '77', + 'depth' => '2', + 'path_string' => '/1/2/77/', + ] + ); + + $this->assertTrue($location instanceof Location); + $this->assertEquals($value, $location->$field); + } + + public static function getUpdateLocationData() + { + return [ + ['priority', 23], + ['remote_id', 'someNewHash'], + ['sort_field', 4], + ['sort_order', 4], + ]; + } + + /** + * @dataProvider getUpdateLocationData + */ + public function testUpdateLocation($field, $value) + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->update( + new Location\UpdateStruct( + [ + 'priority' => 23, + 'remoteId' => 'someNewHash', + 'sortField' => 4, + 'sortOrder' => 4, + ] + ), + 70 + ); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [[$value]], + $query + ->select($field) + ->from('ezcontentobject_tree') + ->where($query->expr()->in('node_id', [70])) + ); + } + + public static function getNodeAssignmentValues() + { + return [ + ['contentobject_version', [1]], + ['from_node_id', [0]], + ['id', [215]], + ['is_main', [0]], + ['op_code', [3]], + ['parent_node', [77]], + ['parent_remote_id', ['some_id']], + ['remote_id', ['0']], + ['sort_field', [2]], + ['sort_order', [0]], + ['is_main', [0]], + ['priority', [1]], + ['is_hidden', [1]], + ]; + } + + private function buildGenericNodeSelectContentWithParentQuery( + int $contentId, + int $parentLocationId, + string $nodeTable, + string $parentNodeIdColumnName, + array $fields + ): QueryBuilder { + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select($fields) + ->from($nodeTable) + ->where( + $expr->eq( + 'contentobject_id', + $query->createPositionalParameter($contentId, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + $parentNodeIdColumnName, + $query->createPositionalParameter($parentLocationId, ParameterType::INTEGER) + ) + ); + + return $query; + } + + private function buildNodeAssignmentSelectContentWithParentQuery( + int $contentId, + int $parentLocationId, + array $fields + ): QueryBuilder { + return $this->buildGenericNodeSelectContentWithParentQuery( + $contentId, + $parentLocationId, + 'eznode_assignment', + 'parent_node', + $fields + ); + } + + private function buildContentTreeSelectContentWithParentQuery( + int $contentId, + int $parentLocationId, + array $fields + ): QueryBuilder { + return $this->buildGenericNodeSelectContentWithParentQuery( + $contentId, + $parentLocationId, + Gateway::CONTENT_TREE_TABLE, + 'parent_node_id', + $fields + ); + } + + /** + * @depends testCreateLocation + * @dataProvider getNodeAssignmentValues + * + * @param string $field + * @param array $expectedResult + */ + public function testCreateLocationNodeAssignmentCreation(string $field, array $expectedResult) + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->createNodeAssignment( + new CreateStruct( + [ + 'contentId' => 68, + 'contentVersion' => 1, + 'mainLocationId' => 1, + 'priority' => 1, + 'remoteId' => 'some_id', + 'sortField' => 2, + 'sortOrder' => 0, + 'hidden' => 1, + ] + ), + 77, + DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE + ); + + $this->assertQueryResult( + [$expectedResult], + $this->buildNodeAssignmentSelectContentWithParentQuery(68, 77, [$field]) + ); + } + + /** + * @depends testCreateLocation + */ + public function testCreateLocationNodeAssignmentCreationMainLocation() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + $gateway->createNodeAssignment( + new CreateStruct( + [ + 'contentId' => 68, + 'contentVersion' => 1, + 'mainLocationId' => true, + 'priority' => 1, + 'remoteId' => 'some_id', + 'sortField' => 1, + 'sortOrder' => 1, + ] + ), + '77', + DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE + ); + + $this->assertQueryResult( + [[1]], + $this->buildNodeAssignmentSelectContentWithParentQuery(68, 77, ['is_main']) + ); + } + + public function testUpdateLocationsContentVersionNo() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + + $gateway->create( + new CreateStruct( + [ + 'contentId' => 4096, + 'remoteId' => 'some_id', + 'contentVersion' => 1, + ] + ), + [ + 'node_id' => '77', + 'depth' => '2', + 'path_string' => '/1/2/77/', + ] + ); + + $gateway->updateLocationsContentVersionNo(4096, 2); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [ + [2], + ], + $query->select( + 'contentobject_version' + )->from( + 'ezcontentobject_tree' + )->where( + $query->expr()->eq( + 'contentobject_id', + 4096 + ) + ) + ); + } + + public function testDeleteNodeAssignment() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + + $gateway->deleteNodeAssignment(11); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [[0]], + $query + ->select('count(*)') + ->from('eznode_assignment') + ->where( + $query->expr()->eq('contentobject_id', 11) + ) + ); + } + + public function testDeleteNodeAssignmentWithSecondArgument() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + $gateway = $this->getLocationGateway(); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $query + ->select('count(*)') + ->from('eznode_assignment') + ->where( + $query->expr()->eq('contentobject_id', 11) + ); + $statement = $query->execute(); + $nodeAssignmentsCount = (int)$statement->fetchColumn(); + + $gateway->deleteNodeAssignment(11, 1); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [[$nodeAssignmentsCount - 1]], + $query + ->select('count(*)') + ->from('eznode_assignment') + ->where( + $query->expr()->eq('contentobject_id', 11) + ) + ); + } + + public static function getConvertNodeAssignmentsLocationValues() + { + return [ + ['contentobject_id', '68'], + ['contentobject_is_published', '1'], + ['contentobject_version', '1'], + ['depth', '3'], + ['is_hidden', '1'], + ['is_invisible', '1'], + ['main_node_id', '70'], + ['modified_subnode', time()], + ['node_id', '228'], + ['parent_node_id', '77'], + ['path_identification_string', null], + ['path_string', '/1/2/77/228/'], + ['priority', '101'], + ['remote_id', 'some_id'], + ['sort_field', '1'], + ['sort_order', '1'], + ]; + } + + /** + * @depends testCreateLocationNodeAssignmentCreation + * @dataProvider getConvertNodeAssignmentsLocationValues + */ + public function testConvertNodeAssignments($field, $value) + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + + $gateway = $this->getLocationGateway(); + $gateway->createNodeAssignment( + new CreateStruct( + [ + 'contentId' => 68, + 'contentVersion' => 1, + 'mainLocationId' => false, + 'priority' => 101, + 'remoteId' => 'some_id', + 'sortField' => 1, + 'sortOrder' => 1, + 'hidden' => true, + // Note: not stored in node assignment, will be calculated from parent + // visibility upon Location creation from node assignment + 'invisible' => false, + ] + ), + '77', + DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE + ); + + $gateway->createLocationsFromNodeAssignments(68, 1); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $expr = $query->expr(); + $query + ->select($field) + ->from(Gateway::CONTENT_TREE_TABLE) + ->where( + $expr->eq( + 'contentobject_id', + $query->createPositionalParameter(68, ParameterType::INTEGER) + ) + ) + ->andWhere( + $expr->eq( + 'parent_node_id', + $query->createPositionalParameter(77, ParameterType::INTEGER) + ) + ); + + if ($field === 'modified_subnode') { + $statement = $query->execute(); + $result = $statement->fetch(FetchMode::ASSOCIATIVE); + $this->assertGreaterThanOrEqual($value, $result); + } else { + $this->assertQueryResult( + [[$value]], + $query + ); + } + } + + /** + * @depends testCreateLocationNodeAssignmentCreation + */ + public function testConvertNodeAssignmentsMainLocation() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + + $gateway = $this->getLocationGateway(); + $gateway->createNodeAssignment( + new CreateStruct( + [ + 'contentId' => 68, + 'contentVersion' => 1, + 'mainLocationId' => true, + 'priority' => 101, + 'remoteId' => 'some_id', + 'sortField' => 1, + 'sortOrder' => 1, + 'hidden' => true, + // Note: not stored in node assignment, will be calculated from parent + // visibility upon Location creation from node assignment + 'invisible' => false, + ] + ), + 77, + DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE + ); + + $gateway->createLocationsFromNodeAssignments(68, 1); + + $this->assertQueryResult( + [[228]], + $this->buildContentTreeSelectContentWithParentQuery(68, 77, ['main_node_id']) + ); + } + + /** + * @depends testCreateLocationNodeAssignmentCreation + */ + public function testConvertNodeAssignmentsParentHidden() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + + $gateway = $this->getLocationGateway(); + $gateway->createNodeAssignment( + new CreateStruct( + [ + 'contentId' => 68, + 'contentVersion' => 1, + 'mainLocationId' => true, + 'priority' => 101, + 'remoteId' => 'some_id', + 'sortField' => 1, + 'sortOrder' => 1, + 'hidden' => false, + // Note: not stored in node assignment, will be calculated from parent + // visibility upon Location creation from node assignment + 'invisible' => false, + ] + ), + 224, + DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE + ); + + $gateway->createLocationsFromNodeAssignments(68, 1); + + $this->assertQueryResult( + [[0, 1]], + $this->buildContentTreeSelectContentWithParentQuery( + 68, + 224, + ['is_hidden, is_invisible'] + ) + ); + } + + /** + * @depends testCreateLocationNodeAssignmentCreation + */ + public function testConvertNodeAssignmentsParentInvisible() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + + $gateway = $this->getLocationGateway(); + $gateway->createNodeAssignment( + new CreateStruct( + [ + 'contentId' => 68, + 'contentVersion' => 1, + 'mainLocationId' => true, + 'priority' => 101, + 'remoteId' => 'some_id', + 'sortField' => 1, + 'sortOrder' => 1, + 'hidden' => false, + // Note: not stored in node assignment, will be calculated from parent + // visibility upon Location creation from node assignment + 'invisible' => false, + ] + ), + 225, + DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE + ); + + $gateway->createLocationsFromNodeAssignments(68, 1); + + $this->assertQueryResult( + [[0, 1]], + $this->buildContentTreeSelectContentWithParentQuery( + 68, + 225, + ['is_hidden, is_invisible'] + ) + ); + } + + /** + * @depends testCreateLocationNodeAssignmentCreation + */ + public function testConvertNodeAssignmentsUpdateAssignment() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + + $gateway = $this->getLocationGateway(); + $gateway->createNodeAssignment( + new CreateStruct( + [ + 'contentId' => 68, + 'contentVersion' => 1, + 'mainLocationId' => 1, + 'priority' => 1, + 'remoteId' => 'some_id', + 'sortField' => 1, + 'sortOrder' => 1, + ] + ), + '77', + DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE + ); + + $gateway->createLocationsFromNodeAssignments(68, 1); + + $this->assertQueryResult( + [[DoctrineDatabase::NODE_ASSIGNMENT_OP_CODE_CREATE_NOP]], + $this->buildNodeAssignmentSelectContentWithParentQuery(68, 77, ['op_code']) + ); + } + + /** + * Test for the setSectionForSubtree() method. + */ + public function testSetSectionForSubtree() + { + $this->insertDatabaseFixture(__DIR__ . '/../../_fixtures/contentobjects.php'); + $gateway = $this->getLocationGateway(); + $gateway->setSectionForSubtree('/1/2/69/70/', 23); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [[68], [69]], + $query + ->select('id') + ->from('ezcontentobject') + ->where($query->expr()->eq('section_id', 23)) + ); + } + + /** + * Test for the changeMainLocation() method. + * + * + * + * @throws \Doctrine\DBAL\DBALException + */ + public function testChangeMainLocation() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + // Create additional location and assignment for test purpose + $connection = $this->getDatabaseConnection(); + $query = $connection->createQueryBuilder(); + $query + ->insert('ezcontentobject_tree') + ->values( + [ + 'contentobject_id' => $query->createPositionalParameter( + 10, + ParameterType::INTEGER + ), + 'contentobject_version' => $query->createPositionalParameter( + 2, + ParameterType::INTEGER + ), + 'main_node_id' => $query->createPositionalParameter(15, ParameterType::INTEGER), + 'node_id' => $query->createPositionalParameter(228, ParameterType::INTEGER), + 'parent_node_id' => $query->createPositionalParameter( + 227, + ParameterType::INTEGER + ), + 'path_string' => $query->createPositionalParameter( + '/1/5/13/228/', + ParameterType::STRING + ), + 'remote_id' => $query->createPositionalParameter( + 'asdfg123437', + ParameterType::STRING + ), + ] + ); + $query->execute(); + + $query = $connection->createQueryBuilder(); + $query + ->insert('eznode_assignment') + ->values( + [ + 'contentobject_id' => $query->createPositionalParameter( + 10, + ParameterType::INTEGER + ), + 'contentobject_version' => $query->createPositionalParameter( + 2, + ParameterType::INTEGER + ), + 'id' => $query->createPositionalParameter(0, ParameterType::INTEGER), + 'is_main' => $query->createPositionalParameter(0, ParameterType::INTEGER), + 'parent_node' => $query->createPositionalParameter(227, ParameterType::INTEGER), + 'parent_remote_id' => $query->createPositionalParameter( + '5238a276bf8231fbcf8a986cdc82a6a5', + ParameterType::STRING + ), + ] + ); + $query->execute(); + + $gateway = $this->getLocationGateway(); + + $gateway->changeMainLocation( + 10, // content id + 228, // new main location id + 2, // content version number + 227 // new main location parent id + ); + + $query = $connection->createQueryBuilder(); + $this->assertQueryResult( + [[228], [228]], + $query + ->select('main_node_id') + ->from('ezcontentobject_tree') + ->where( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter(10, ParameterType::INTEGER) + ) + ) + ); + + $query = $connection->createQueryBuilder(); + $this->assertQueryResult( + [[1]], + $query + ->select('is_main') + ->from('eznode_assignment') + ->where( + $query->expr()->andX( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter(10, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'contentobject_version', + $query->createPositionalParameter(2, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'parent_node', + $query->createPositionalParameter(227, ParameterType::INTEGER) + ) + ) + ) + ); + + $query = $connection->createQueryBuilder(); + $this->assertQueryResult( + [[0]], + $query + ->select('is_main') + ->from('eznode_assignment') + ->where( + $query->expr()->andX( + $query->expr()->eq( + 'contentobject_id', + $query->createPositionalParameter(10, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'contentobject_version', + $query->createPositionalParameter(2, ParameterType::INTEGER) + ), + $query->expr()->eq( + 'parent_node', + $query->createPositionalParameter(44, ParameterType::INTEGER) + ) + ) + ) + ); + } + + /** + * Test for the getChildren() method. + */ + public function testGetChildren() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + + $gateway = $this->getLocationGateway(); + $childrenRows = $gateway->getChildren(213); + + $this->assertCount(2, $childrenRows); + $this->assertCount(16, $childrenRows[0]); + $this->assertEquals(214, $childrenRows[0]['node_id']); + $this->assertCount(16, $childrenRows[1]); + $this->assertEquals(215, $childrenRows[1]['node_id']); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function testGetFallbackMainNodeData(): void + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + // Create additional location for test purpose + $connection = $this->getDatabaseConnection(); + $query = $connection->createQueryBuilder(); + $query + ->insert('ezcontentobject_tree') + ->values( + [ + 'contentobject_id' => $query->createPositionalParameter( + 12, + ParameterType::INTEGER + ), + 'contentobject_version' => $query->createPositionalParameter( + 1, + ParameterType::INTEGER + ), + 'main_node_id' => $query->createPositionalParameter(13, ParameterType::INTEGER), + 'node_id' => $query->createPositionalParameter(228, ParameterType::INTEGER), + 'parent_node_id' => $query->createPositionalParameter( + 227, + ParameterType::INTEGER + ), + 'path_string' => $query->createPositionalParameter( + '/1/5/13/228/', + ParameterType::STRING + ), + 'remote_id' => $query->createPositionalParameter( + 'asdfg123437', + ParameterType::STRING + ), + ] + ); + $query->execute(); + + $gateway = $this->getLocationGateway(); + $data = $gateway->getFallbackMainNodeData(12, 13); + + $this->assertEquals(228, $data['node_id']); + $this->assertEquals(1, $data['contentobject_version']); + $this->assertEquals(227, $data['parent_node_id']); + } + + /** + * Test for the removeLocation() method. + */ + public function testRemoveLocation() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + + $gateway = $this->getLocationGateway(); + $gateway->removeLocation(13); + + try { + $gateway->getBasicNodeData(13); + $this->fail('Location was not deleted!'); + } catch (NotFoundException $e) { + // Do nothing + } + } + + public function providerForTestUpdatePathIdentificationString() + { + return [ + [77, 2, 'new_solutions', 'new_solutions'], + [75, 69, 'stylesheets', 'products/stylesheets'], + ]; + } + + /** + * Test for the updatePathIdentificationString() method. + * + * + * @dataProvider providerForTestUpdatePathIdentificationString + */ + public function testUpdatePathIdentificationString( + $locationId, + $parentLocationId, + $text, + $expected + ) { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); + + $gateway = $this->getLocationGateway(); + $gateway->updatePathIdentificationString($locationId, $parentLocationId, $text); + + $query = $this->getDatabaseConnection()->createQueryBuilder(); + $this->assertQueryResult( + [[$expected]], + $query->select( + 'path_identification_string' + )->from( + 'ezcontentobject_tree' + )->where( + $query->expr()->eq('node_id', $locationId) + ) + ); + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Location\Gateway\DoctrineDatabaseTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/Gateway/DoctrineDatabaseTrashTest.php b/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php similarity index 81% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/Gateway/DoctrineDatabaseTrashTest.php rename to tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php index 589f6f6ecc..322dfacd79 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Location/Gateway/DoctrineDatabaseTrashTest.php +++ b/tests/lib/Persistence/Legacy/Content/Location/Gateway/DoctrineDatabaseTrashTest.php @@ -4,16 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Location\Gateway; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Location\Gateway; use Doctrine\DBAL\ParameterType; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase; -use eZ\Publish\Core\Persistence\Legacy\Tests\Content\LanguageAwareTestCase; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase; +use Ibexa\Tests\Core\Persistence\Legacy\Content\LanguageAwareTestCase; /** - * Test case for eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase. + * @covers \Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase */ class DoctrineDatabaseTrashTest extends LanguageAwareTestCase { @@ -28,8 +29,6 @@ protected function getLocationGateway() } /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::trashLocation - * * @todo test updated content status */ public function testTrashLocation() @@ -53,9 +52,6 @@ public function testTrashLocation() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::trashLocation - */ public function testTrashLocationUpdateTrashTable() { $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); @@ -94,7 +90,6 @@ public static function getUntrashedLocationValues() } /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::untrashLocation * @dataProvider getUntrashedLocationValues */ public function testUntrashLocationDefault($property, $value) @@ -115,9 +110,6 @@ public function testUntrashLocationDefault($property, $value) ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::untrashLocation - */ public function testUntrashLocationNewParent() { $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); @@ -136,12 +128,9 @@ public function testUntrashLocationNewParent() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::untrashLocation - */ public function testUntrashInvalidLocation() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); $handler = $this->getLocationGateway(); @@ -149,12 +138,9 @@ public function testUntrashInvalidLocation() $handler->untrashLocation(23); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::untrashLocation - */ public function testUntrashLocationInvalidParent() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); $handler = $this->getLocationGateway(); @@ -163,12 +149,9 @@ public function testUntrashLocationInvalidParent() $handler->untrashLocation(71, 1337); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::untrashLocation - */ public function testUntrashLocationInvalidOldParent() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); $handler = $this->getLocationGateway(); @@ -200,7 +183,6 @@ public static function getLoadTrashValues() } /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::loadTrashByLocation * @dataProvider getLoadTrashValues */ public function testLoadTrashByLocationId($field, $value) @@ -218,9 +200,6 @@ public function testLoadTrashByLocationId($field, $value) ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::countTrashed - */ public function testCountTrashed() { $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); @@ -239,9 +218,6 @@ public function testCountTrashed() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::listTrashed - */ public function testListEmptyTrash() { $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); @@ -266,9 +242,6 @@ protected function trashSubtree() $handler->trashLocation(76); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::listTrashed - */ public function testListFullTrash() { $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); @@ -281,9 +254,6 @@ public function testListFullTrash() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::listTrashed - */ public function testListTrashLimited() { $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); @@ -318,7 +288,6 @@ public static function getTrashValues() } /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::listTrashed * @dataProvider getTrashValues */ public function testListTrashItem($key, $value) @@ -331,9 +300,6 @@ public function testListTrashItem($key, $value) $this->assertEquals($value, $trashList[0][$key]); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::listTrashed - */ public function testListTrashSortedPathStringDesc() { $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); @@ -366,9 +332,6 @@ static function ($trashItem) { ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::listTrashed - */ public function testListTrashSortedDepth() { $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); @@ -402,9 +365,6 @@ static function ($trashItem) { ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::cleanupTrash - */ public function testCleanupTrash() { $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); @@ -421,9 +381,6 @@ public function testCleanupTrash() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::removeElementFromTrash - */ public function testRemoveElementFromTrash() { $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); @@ -441,9 +398,6 @@ public function testRemoveElementFromTrash() ); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase::countLocationsByContentId - */ public function testCountLocationsByContentId() { $this->insertDatabaseFixture(__DIR__ . '/_fixtures/full_example_tree.php'); @@ -484,3 +438,5 @@ public function testCountLocationsByContentId() self::assertSame(2, $handler->countLocationsByContentId(67)); } } + +class_alias(DoctrineDatabaseTrashTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Location\Gateway\DoctrineDatabaseTrashTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Location/Gateway/_fixtures/full_example_tree.php b/tests/lib/Persistence/Legacy/Content/Location/Gateway/_fixtures/full_example_tree.php new file mode 100644 index 0000000000..0405bbcbe3 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Location/Gateway/_fixtures/full_example_tree.php @@ -0,0 +1,396 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezcontentobject_tree' => [ + ['contentobject_id' => 0, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 0, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 1, 'modified_subnode' => 1311065058, 'node_id' => 1, 'parent_node_id' => 1, 'path_identification_string' => '', 'path_string' => '/1/', 'priority' => 0, 'remote_id' => '629709ba256fe317c3ddcee35453a96a', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 65, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 2, 'modified_subnode' => 1311065058, 'node_id' => 2, 'parent_node_id' => 1, 'path_identification_string' => '', 'path_string' => '/1/2/', 'priority' => 0, 'remote_id' => 'f3e90596361e31d496d4026eb624c983', 'sort_field' => 8, 'sort_order' => 1], + ['contentobject_id' => 4, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 5, 'modified_subnode' => 1311065058, 'node_id' => 5, 'parent_node_id' => 1, 'path_identification_string' => 'users', 'path_string' => '/1/5/', 'priority' => 0, 'remote_id' => '3f6d92f8044aed134f32153517850f5a', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 11, 'contentobject_is_published' => 1, 'contentobject_version' => 2, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 12, 'modified_subnode' => 1311065058, 'node_id' => 12, 'parent_node_id' => 5, 'path_identification_string' => 'users/members', 'path_string' => '/1/5/12/', 'priority' => 0, 'remote_id' => '602dcf84765e56b7f999eaafd3821dd3', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 12, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 13, 'modified_subnode' => 1311065057, 'node_id' => 13, 'parent_node_id' => 5, 'path_identification_string' => 'users/administrator_users', 'path_string' => '/1/5/13/', 'priority' => 0, 'remote_id' => '769380b7aa94541679167eab817ca893', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 13, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 14, 'modified_subnode' => 1081860719, 'node_id' => 14, 'parent_node_id' => 5, 'path_identification_string' => 'users/editors', 'path_string' => '/1/5/14/', 'priority' => 0, 'remote_id' => 'f7dda2854fc68f7c8455d9cb14bd04a9', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 14, 'contentobject_is_published' => 1, 'contentobject_version' => 4, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 15, 'modified_subnode' => 1311065057, 'node_id' => 15, 'parent_node_id' => 13, 'path_identification_string' => 'users/administrator_users/administrator_user', 'path_string' => '/1/5/13/15/', 'priority' => 0, 'remote_id' => 'e5161a99f733200b9ed4e80f9c16187b', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 41, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 43, 'modified_subnode' => 1311065012, 'node_id' => 43, 'parent_node_id' => 1, 'path_identification_string' => 'media', 'path_string' => '/1/43/', 'priority' => 0, 'remote_id' => '75c715a51699d2d309a924eca6a95145', 'sort_field' => 9, 'sort_order' => 1], + ['contentobject_id' => 42, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 44, 'modified_subnode' => 1081860719, 'node_id' => 44, 'parent_node_id' => 5, 'path_identification_string' => 'users/anonymous_users', 'path_string' => '/1/5/44/', 'priority' => 0, 'remote_id' => '4fdf0072da953bb276c0c7e0141c5c9b', 'sort_field' => 9, 'sort_order' => 1], + ['contentobject_id' => 10, 'contentobject_is_published' => 1, 'contentobject_version' => 2, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 45, 'modified_subnode' => 1081860719, 'node_id' => 45, 'parent_node_id' => 44, 'path_identification_string' => 'users/anonymous_users/anonymous_user', 'path_string' => '/1/5/44/45/', 'priority' => 0, 'remote_id' => '2cf8343bee7b482bab82b269d8fecd76', 'sort_field' => 9, 'sort_order' => 1], + ['contentobject_id' => 45, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 48, 'modified_subnode' => 1184592117, 'node_id' => 48, 'parent_node_id' => 1, 'path_identification_string' => 'setup2', 'path_string' => '/1/48/', 'priority' => 0, 'remote_id' => '182ce1b5af0c09fa378557c462ba2617', 'sort_field' => 9, 'sort_order' => 1], + ['contentobject_id' => 49, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 51, 'modified_subnode' => 1311065012, 'node_id' => 51, 'parent_node_id' => 43, 'path_identification_string' => 'media/images', 'path_string' => '/1/43/51/', 'priority' => 0, 'remote_id' => '1b26c0454b09bb49dfb1b9190ffd67cb', 'sort_field' => 9, 'sort_order' => 1], + ['contentobject_id' => 50, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 52, 'modified_subnode' => 1081860720, 'node_id' => 52, 'parent_node_id' => 43, 'path_identification_string' => 'media/files', 'path_string' => '/1/43/52/', 'priority' => 0, 'remote_id' => '0b113a208f7890f9ad3c24444ff5988c', 'sort_field' => 9, 'sort_order' => 1], + ['contentobject_id' => 51, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 53, 'modified_subnode' => 1081860720, 'node_id' => 53, 'parent_node_id' => 43, 'path_identification_string' => 'media/multimedia', 'path_string' => '/1/43/53/', 'priority' => 0, 'remote_id' => '4f18b82c75f10aad476cae5adf98c11f', 'sort_field' => 9, 'sort_order' => 1], + ['contentobject_id' => 52, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 54, 'modified_subnode' => 1184592117, 'node_id' => 54, 'parent_node_id' => 48, 'path_identification_string' => 'setup2/common_ini_settings', 'path_string' => '/1/48/54/', 'priority' => 0, 'remote_id' => 'fa9f3cff9cf90ecfae335718dcbddfe2', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 54, 'contentobject_is_published' => 1, 'contentobject_version' => 2, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 56, 'modified_subnode' => 1311065058, 'node_id' => 56, 'parent_node_id' => 58, 'path_identification_string' => 'design/plain_site', 'path_string' => '/1/58/56/', 'priority' => 0, 'remote_id' => '772da20ecf88b3035d73cbdfcea0f119', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 56, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 58, 'modified_subnode' => 1311065058, 'node_id' => 58, 'parent_node_id' => 1, 'path_identification_string' => 'design', 'path_string' => '/1/58/', 'priority' => 0, 'remote_id' => '79f2d67372ab56f59b5d65bb9e0ca3b9', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 57, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 59, 'modified_subnode' => 1311065011, 'node_id' => 59, 'parent_node_id' => 43, 'path_identification_string' => 'media/banners', 'path_string' => '/1/43/59/', 'priority' => 0, 'remote_id' => '437ef9d0a9b7ae326ec83fa3bb73956d', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 58, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 60, 'modified_subnode' => 1311065010, 'node_id' => 60, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/woman_communities', 'path_string' => '/1/43/59/60/', 'priority' => 0, 'remote_id' => 'eaa7f2f48c3f35801961abad12151db4', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 59, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 61, 'modified_subnode' => 1311065010, 'node_id' => 61, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/woman_frontpage', 'path_string' => '/1/43/59/61/', 'priority' => 0, 'remote_id' => 'b8c85fd926d61dab6e68fa1865cee987', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 60, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 62, 'modified_subnode' => 1311065011, 'node_id' => 62, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/woman_products', 'path_string' => '/1/43/59/62/', 'priority' => 0, 'remote_id' => 'c65aba2485585bdd09dfb66afccf645e', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 61, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 63, 'modified_subnode' => 1311065011, 'node_id' => 63, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/woman_partners', 'path_string' => '/1/43/59/63/', 'priority' => 0, 'remote_id' => '64bb803471e53898aa38a7c29e482370', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 62, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 64, 'modified_subnode' => 1311065011, 'node_id' => 64, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/pencils_support', 'path_string' => '/1/43/59/64/', 'priority' => 0, 'remote_id' => '95e29503817570c6458fa0f37d227306', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 63, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 65, 'modified_subnode' => 1311065011, 'node_id' => 65, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/woman_training', 'path_string' => '/1/43/59/65/', 'priority' => 0, 'remote_id' => 'aa4a1afd9c02d00f2f31186e8a271332', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 64, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 66, 'modified_subnode' => 1311065011, 'node_id' => 66, 'parent_node_id' => 59, 'path_identification_string' => 'media/banners/woman_solutions', 'path_string' => '/1/43/59/66/', 'priority' => 0, 'remote_id' => '93d5115082a23b266613868051b8d803', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 66, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 68, 'modified_subnode' => 1311065012, 'node_id' => 68, 'parent_node_id' => 51, 'path_identification_string' => 'media/images/rest_api', 'path_string' => '/1/43/51/68/', 'priority' => 0, 'remote_id' => '31fd28362c18a36cb56223f3609d5d90', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 67, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 69, 'modified_subnode' => 1311065014, 'node_id' => 69, 'parent_node_id' => 2, 'path_identification_string' => 'products', 'path_string' => '/1/2/69/', 'priority' => 0, 'remote_id' => '9cec85d730eec7578190ee95ce5a36f5', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 68, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 70, 'modified_subnode' => 1311065013, 'node_id' => 70, 'parent_node_id' => 69, 'path_identification_string' => 'products/software', 'path_string' => '/1/2/69/70/', 'priority' => 0, 'remote_id' => 'b0b85c15125ca1732e5e528de2717599', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 69, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 71, 'modified_subnode' => 1311065013, 'node_id' => 71, 'parent_node_id' => 70, 'path_identification_string' => 'products/software/os_type_i', 'path_string' => '/1/2/69/70/71/', 'priority' => 0, 'remote_id' => '087adb763245e0cdcac593fb4a5996cf', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 70, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 72, 'modified_subnode' => 1311065014, 'node_id' => 72, 'parent_node_id' => 69, 'path_identification_string' => 'products/boxes', 'path_string' => '/1/2/69/72/', 'priority' => 0, 'remote_id' => 'e607aab6e924091909f3def02415bc53', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 71, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 73, 'modified_subnode' => 1311065013, 'node_id' => 73, 'parent_node_id' => 72, 'path_identification_string' => 'products/boxes/cd_dvd_box_i', 'path_string' => '/1/2/69/72/73/', 'priority' => 0, 'remote_id' => '054d9f10c6fa97689c0fc3b2ac412ebd', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 72, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 74, 'modified_subnode' => 1311065014, 'node_id' => 74, 'parent_node_id' => 72, 'path_identification_string' => 'products/boxes/cd_dvd_box_ii', 'path_string' => '/1/2/69/72/74/', 'priority' => 0, 'remote_id' => '9801bda46e5f8b9d692e1120d50fc7b3', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 73, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 75, 'modified_subnode' => 1311065014, 'node_id' => 75, 'parent_node_id' => 72, 'path_identification_string' => 'products/boxes/cd_dvd_box_iii', 'path_string' => '/1/2/69/72/75/', 'priority' => 0, 'remote_id' => '005067a5eee6505aa0f601cca30681d0', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 74, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 76, 'modified_subnode' => 1311065014, 'node_id' => 76, 'parent_node_id' => 69, 'path_identification_string' => 'products/products_sheets', 'path_string' => '/1/2/69/76/', 'priority' => 0, 'remote_id' => '18f14551cc555c094b15a732ccd27fb2', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 75, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 77, 'modified_subnode' => 1311065017, 'node_id' => 77, 'parent_node_id' => 2, 'path_identification_string' => 'solutions', 'path_string' => '/1/2/77/', 'priority' => 0, 'remote_id' => 'dbc2f3c8716c12f32c379dbf0b1cb133', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 76, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 78, 'modified_subnode' => 1311065016, 'node_id' => 78, 'parent_node_id' => 77, 'path_identification_string' => 'solutions/web_publishing', 'path_string' => '/1/2/77/78/', 'priority' => 0, 'remote_id' => 'bc766fe955437def220a3fa2966a34ee', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 77, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 79, 'modified_subnode' => 1311065015, 'node_id' => 79, 'parent_node_id' => 78, 'path_identification_string' => 'solutions/web_publishing/fusce_sagittis_sagittis', 'path_string' => '/1/2/77/78/79/', 'priority' => 0, 'remote_id' => 'f0c2216ecb29600cd8ae93951a0c8f3a', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 78, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 80, 'modified_subnode' => 1311065015, 'node_id' => 80, 'parent_node_id' => 78, 'path_identification_string' => 'solutions/web_publishing/etiam_posuere_sodales_arcu', 'path_string' => '/1/2/77/78/80/', 'priority' => 0, 'remote_id' => 'eaf16bddfd36206dad265aadfbc98f17', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 79, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 81, 'modified_subnode' => 1311065016, 'node_id' => 81, 'parent_node_id' => 78, 'path_identification_string' => 'solutions/web_publishing/in_hac_habitasse_platea', 'path_string' => '/1/2/77/78/81/', 'priority' => 0, 'remote_id' => 'd2a11e56093b77eb7a347229361c3377', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 80, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 82, 'modified_subnode' => 1311065017, 'node_id' => 82, 'parent_node_id' => 77, 'path_identification_string' => 'solutions/content_management', 'path_string' => '/1/2/77/82/', 'priority' => 0, 'remote_id' => '17d65b568e3500cf1f8b42bc5de2d12b', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 81, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 83, 'modified_subnode' => 1311065016, 'node_id' => 83, 'parent_node_id' => 82, 'path_identification_string' => 'solutions/content_management/fusce_sagittis_sagittis_urna', 'path_string' => '/1/2/77/82/83/', 'priority' => 0, 'remote_id' => 'ecc4f0e94b05bf10f4f783d660ff0ad0', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 82, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 84, 'modified_subnode' => 1311065016, 'node_id' => 84, 'parent_node_id' => 82, 'path_identification_string' => 'solutions/content_management/class_aptent_taciti_sociosqu', 'path_string' => '/1/2/77/82/84/', 'priority' => 0, 'remote_id' => '74280af2cba9002ea9660749225562b6', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 83, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 85, 'modified_subnode' => 1311065017, 'node_id' => 85, 'parent_node_id' => 82, 'path_identification_string' => 'solutions/content_management/aenean_malesuada_ligula', 'path_string' => '/1/2/77/82/85/', 'priority' => 0, 'remote_id' => '4e526426523e47aeacf353541284cbf8', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 84, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 86, 'modified_subnode' => 1311065019, 'node_id' => 86, 'parent_node_id' => 2, 'path_identification_string' => 'training', 'path_string' => '/1/2/86/', 'priority' => 0, 'remote_id' => '95f3c4261719ea27ab67f980fbee0694', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 85, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 87, 'modified_subnode' => 1311065017, 'node_id' => 87, 'parent_node_id' => 86, 'path_identification_string' => 'training/certification', 'path_string' => '/1/2/86/87/', 'priority' => 0, 'remote_id' => '4a1391d3563d056c9d9ea2653093ae3e', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 86, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 88, 'modified_subnode' => 1311065018, 'node_id' => 88, 'parent_node_id' => 86, 'path_identification_string' => 'training/professional_workshops', 'path_string' => '/1/2/86/88/', 'priority' => 0, 'remote_id' => '8889727909b5f34b6aa23f7eee32606b', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 87, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 89, 'modified_subnode' => 1311065017, 'node_id' => 89, 'parent_node_id' => 88, 'path_identification_string' => 'training/professional_workshops/etiam_sodales_mauris', 'path_string' => '/1/2/86/88/89/', 'priority' => 0, 'remote_id' => '1721dbee55639fe280f6a54195f9577c', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 88, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 90, 'modified_subnode' => 1311065018, 'node_id' => 90, 'parent_node_id' => 88, 'path_identification_string' => 'training/professional_workshops/class_aptent_taciti', 'path_string' => '/1/2/86/88/90/', 'priority' => 0, 'remote_id' => '7fabb9ee5bcb6630a3947d1c5585d995', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 89, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 91, 'modified_subnode' => 1311065018, 'node_id' => 91, 'parent_node_id' => 88, 'path_identification_string' => 'training/professional_workshops/duis_auctor_vehicula_erat', 'path_string' => '/1/2/86/88/91/', 'priority' => 0, 'remote_id' => 'a95ae3e4a64f08eb2002b31680fe8989', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 90, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 92, 'modified_subnode' => 1311065018, 'node_id' => 92, 'parent_node_id' => 86, 'path_identification_string' => 'training/events_and_seminars', 'path_string' => '/1/2/86/92/', 'priority' => 0, 'remote_id' => '2acc8bb8f7eda4de2cf74f6dc277661f', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 91, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 93, 'modified_subnode' => 1311065019, 'node_id' => 93, 'parent_node_id' => 86, 'path_identification_string' => 'training/self_paced_courses', 'path_string' => '/1/2/86/93/', 'priority' => 0, 'remote_id' => '6d8cc9831b86d79b8c184507f8e0cbb6', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 92, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 94, 'modified_subnode' => 1311065019, 'node_id' => 94, 'parent_node_id' => 86, 'path_identification_string' => 'training/instructor_led_courses', 'path_string' => '/1/2/86/94/', 'priority' => 0, 'remote_id' => '30f12ecd29608eceb20bb903ddb780c7', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 93, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 95, 'modified_subnode' => 1311065019, 'node_id' => 95, 'parent_node_id' => 86, 'path_identification_string' => 'training/additional_learning_resources', 'path_string' => '/1/2/86/95/', 'priority' => 0, 'remote_id' => '341e63bdce0f1601519d1b8e82e62766', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 94, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 96, 'modified_subnode' => 1311065021, 'node_id' => 96, 'parent_node_id' => 2, 'path_identification_string' => 'support', 'path_string' => '/1/2/96/', 'priority' => 0, 'remote_id' => '0d55a3f510cc7cd9a7b8eb838f50ff5c', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 95, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 97, 'modified_subnode' => 1311065020, 'node_id' => 97, 'parent_node_id' => 96, 'path_identification_string' => 'support/knowledgebase', 'path_string' => '/1/2/96/97/', 'priority' => 0, 'remote_id' => '89ce8eed8fa06c4105fd612aa83d87d6', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 96, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 98, 'modified_subnode' => 1311065020, 'node_id' => 98, 'parent_node_id' => 97, 'path_identification_string' => 'support/knowledgebase/sed_suscipit', 'path_string' => '/1/2/96/97/98/', 'priority' => 0, 'remote_id' => '78f3b9a9268c7b0206bf1c4d39211495', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 97, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 99, 'modified_subnode' => 1311065020, 'node_id' => 99, 'parent_node_id' => 98, 'path_identification_string' => 'support/knowledgebase/sed_suscipit/ut_interdum', 'path_string' => '/1/2/96/97/98/99/', 'priority' => 0, 'remote_id' => 'a1f9b32547e58064e645388512c16a39', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 98, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 100, 'modified_subnode' => 1311065020, 'node_id' => 100, 'parent_node_id' => 96, 'path_identification_string' => 'support/documentation', 'path_string' => '/1/2/96/100/', 'priority' => 0, 'remote_id' => '0f9c7380f8af1f29f1017e412bdd4016', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 99, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 101, 'modified_subnode' => 1311065020, 'node_id' => 101, 'parent_node_id' => 100, 'path_identification_string' => 'support/documentation/mauris_pretium', 'path_string' => '/1/2/96/100/101/', 'priority' => 0, 'remote_id' => '036394ec8b160f0782bec2dda452d798', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 100, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 102, 'modified_subnode' => 1311065020, 'node_id' => 102, 'parent_node_id' => 101, 'path_identification_string' => 'support/documentation/mauris_pretium/aliquam_posuere', 'path_string' => '/1/2/96/100/101/102/', 'priority' => 0, 'remote_id' => '78da6a4fae1c2eaabe1dbe7af818d970', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 101, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 103, 'modified_subnode' => 1311065021, 'node_id' => 103, 'parent_node_id' => 96, 'path_identification_string' => 'support/books', 'path_string' => '/1/2/96/103/', 'priority' => 0, 'remote_id' => '06cdeb27be466ea8330de5df16144263', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 102, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 104, 'modified_subnode' => 1311065021, 'node_id' => 104, 'parent_node_id' => 103, 'path_identification_string' => 'support/books/aliquam_pulvinar_suscipit_tellus', 'path_string' => '/1/2/96/103/104/', 'priority' => 0, 'remote_id' => 'ab30399896b8e54442c3a619ba7eeecb', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 103, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 105, 'modified_subnode' => 1311065021, 'node_id' => 105, 'parent_node_id' => 96, 'path_identification_string' => 'support/customer_service', 'path_string' => '/1/2/96/105/', 'priority' => 0, 'remote_id' => 'ca2ae9d0f0322798f632e896325f22c3', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 104, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 106, 'modified_subnode' => 1311065021, 'node_id' => 106, 'parent_node_id' => 96, 'path_identification_string' => 'support/support_programs', 'path_string' => '/1/2/96/106/', 'priority' => 0, 'remote_id' => '7ba1d48c0151bae62366095ef6f64c28', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 105, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 107, 'modified_subnode' => 1311065034, 'node_id' => 107, 'parent_node_id' => 2, 'path_identification_string' => 'getting_started', 'path_string' => '/1/2/107/', 'priority' => 0, 'remote_id' => 'e81507d3446726ebd7361352fef5fad3', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 106, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 108, 'modified_subnode' => 1311065022, 'node_id' => 108, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/tutorials_for', 'path_string' => '/1/2/107/108/', 'priority' => 0, 'remote_id' => '51278360f39d5b8ce1d9249953f4de98', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 107, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 109, 'modified_subnode' => 1311065023, 'node_id' => 109, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/get_involved', 'path_string' => '/1/2/107/109/', 'priority' => 0, 'remote_id' => '33151e24acea9c837d2b9fc52e03b1de', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 108, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 110, 'modified_subnode' => 1311065024, 'node_id' => 110, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/how_to_use_ibexa', 'path_string' => '/1/2/107/110/', 'priority' => 0, 'remote_id' => '6b3bcab0f149c5acc2e3728df7c66b73', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 109, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 111, 'modified_subnode' => 1311065023, 'node_id' => 111, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics1', 'path_string' => '/1/2/107/110/111/', 'priority' => 0, 'remote_id' => '28f9dfe5c0680955eec7a2dec4ebc642', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 110, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 112, 'modified_subnode' => 1311065023, 'node_id' => 112, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics12', 'path_string' => '/1/2/107/110/112/', 'priority' => 0, 'remote_id' => '191faba79dc108a19893944befa50d94', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 111, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 113, 'modified_subnode' => 1311065024, 'node_id' => 113, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics13', 'path_string' => '/1/2/107/110/113/', 'priority' => 0, 'remote_id' => '8679b5fa3ad0ec216293419ab9834e44', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 112, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 114, 'modified_subnode' => 1311065024, 'node_id' => 114, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics14', 'path_string' => '/1/2/107/110/114/', 'priority' => 0, 'remote_id' => '7f32cdefd0cf55b966a44aa52181e30d', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 113, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 115, 'modified_subnode' => 1311065024, 'node_id' => 115, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics15', 'path_string' => '/1/2/107/110/115/', 'priority' => 0, 'remote_id' => '29db3c1fcdb497e5cb2c74eb85c0906a', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 114, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 116, 'modified_subnode' => 1311065024, 'node_id' => 116, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics16', 'path_string' => '/1/2/107/110/116/', 'priority' => 0, 'remote_id' => '96e30009f712a0315217fed93eba6a18', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 115, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 117, 'modified_subnode' => 1311065024, 'node_id' => 117, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics17', 'path_string' => '/1/2/107/110/117/', 'priority' => 0, 'remote_id' => '3a6b87470a25b19a6ad15caa5e24e719', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 116, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 118, 'modified_subnode' => 1311065024, 'node_id' => 118, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics18', 'path_string' => '/1/2/107/110/118/', 'priority' => 0, 'remote_id' => 'd0bc77a21920b63543d7b0accab81b24', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 117, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 119, 'modified_subnode' => 1311065024, 'node_id' => 119, 'parent_node_id' => 110, 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics19', 'path_string' => '/1/2/107/110/119/', 'priority' => 0, 'remote_id' => 'c75b4b4b870e0e3611e19e1323103282', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 118, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 120, 'modified_subnode' => 1311065027, 'node_id' => 120, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/how_to_manage_ibexa', 'path_string' => '/1/2/107/120/', 'priority' => 0, 'remote_id' => '262d8c936d3757ff81e7bb49392b703f', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 119, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 121, 'modified_subnode' => 1311065025, 'node_id' => 121, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics1', 'path_string' => '/1/2/107/120/121/', 'priority' => 0, 'remote_id' => '220d4e10bf4619525c3165823079482c', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 120, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 122, 'modified_subnode' => 1311065025, 'node_id' => 122, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics12', 'path_string' => '/1/2/107/120/122/', 'priority' => 0, 'remote_id' => 'f13a938ce2c79a9d438548299220d6dd', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 121, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 123, 'modified_subnode' => 1311065025, 'node_id' => 123, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics13', 'path_string' => '/1/2/107/120/123/', 'priority' => 0, 'remote_id' => '04f5e08293954b1851a4dd1cbd976cff', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 122, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 124, 'modified_subnode' => 1311065026, 'node_id' => 124, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics14', 'path_string' => '/1/2/107/120/124/', 'priority' => 0, 'remote_id' => 'dc80ae3d9de55855a16218b032062c62', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 123, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 125, 'modified_subnode' => 1311065026, 'node_id' => 125, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics15', 'path_string' => '/1/2/107/120/125/', 'priority' => 0, 'remote_id' => '4cb6d4015dd80474074043ab28f96e36', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 124, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 126, 'modified_subnode' => 1311065026, 'node_id' => 126, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics16', 'path_string' => '/1/2/107/120/126/', 'priority' => 0, 'remote_id' => '74ce7d6cbae81d90592b4a8a242a284a', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 125, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 127, 'modified_subnode' => 1311065027, 'node_id' => 127, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics17', 'path_string' => '/1/2/107/120/127/', 'priority' => 0, 'remote_id' => '6f76d2f7e5812acf4f7c5c70258f4f7e', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 126, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 128, 'modified_subnode' => 1311065027, 'node_id' => 128, 'parent_node_id' => 120, 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics18', 'path_string' => '/1/2/107/120/128/', 'priority' => 0, 'remote_id' => '5ba07103968a6bb3f05c275c212440f6', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 127, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 129, 'modified_subnode' => 1311065028, 'node_id' => 129, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/how_to_develop_with_ibexa', 'path_string' => '/1/2/107/129/', 'priority' => 0, 'remote_id' => '3579b6c5cd166d5137eada55274892d3', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 128, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 130, 'modified_subnode' => 1311065028, 'node_id' => 130, 'parent_node_id' => 129, 'path_identification_string' => 'getting_started/how_to_develop_with_ibexa/graphics1', 'path_string' => '/1/2/107/129/130/', 'priority' => 0, 'remote_id' => 'e38a43ce35832e5af2df4f1e9272b926', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 129, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 131, 'modified_subnode' => 1311065028, 'node_id' => 131, 'parent_node_id' => 129, 'path_identification_string' => 'getting_started/how_to_develop_with_ibexa/graphics12', 'path_string' => '/1/2/107/129/131/', 'priority' => 0, 'remote_id' => '1ba7889f63e32acf70f66349ce1a8953', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 130, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 132, 'modified_subnode' => 1311065028, 'node_id' => 132, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/adding_siteaccesses_in_ibexa', 'path_string' => '/1/2/107/132/', 'priority' => 0, 'remote_id' => '19ae9fcb7334a0e407b2781920247122', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 131, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 133, 'modified_subnode' => 1311065034, 'node_id' => 133, 'parent_node_id' => 107, 'path_identification_string' => 'getting_started/new_features', 'path_string' => '/1/2/107/133/', 'priority' => 0, 'remote_id' => '423b6d20325263abaa2012536cc736a4', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 132, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 134, 'modified_subnode' => 1311065030, 'node_id' => 134, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/improved_user_registration_workflow', 'path_string' => '/1/2/107/133/134/', 'priority' => 0, 'remote_id' => '07f0de334c83d631a59defe61f1a7cbf', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 133, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 135, 'modified_subnode' => 1311065029, 'node_id' => 135, 'parent_node_id' => 134, 'path_identification_string' => 'getting_started/new_features/improved_user_registration_workflow/graphics1', 'path_string' => '/1/2/107/133/134/135/', 'priority' => 0, 'remote_id' => 'a7ca1a8878210a9978df0b00e9184b20', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 134, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 136, 'modified_subnode' => 1311065029, 'node_id' => 136, 'parent_node_id' => 134, 'path_identification_string' => 'getting_started/new_features/improved_user_registration_workflow/graphics12', 'path_string' => '/1/2/107/133/134/136/', 'priority' => 0, 'remote_id' => '12c86be2151c426b990d47efd023cc40', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 135, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 137, 'modified_subnode' => 1311065030, 'node_id' => 137, 'parent_node_id' => 134, 'path_identification_string' => 'getting_started/new_features/improved_user_registration_workflow/graphics13', 'path_string' => '/1/2/107/133/134/137/', 'priority' => 0, 'remote_id' => 'ff4473534c7cef8212a11feab57a5f0e', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 136, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 138, 'modified_subnode' => 1311065030, 'node_id' => 138, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/improved_block_editing', 'path_string' => '/1/2/107/133/138/', 'priority' => 0, 'remote_id' => 'e0a1b1b2a3d248bf8d43a83581c22ae8', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 137, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 139, 'modified_subnode' => 1311065030, 'node_id' => 139, 'parent_node_id' => 138, 'path_identification_string' => 'getting_started/new_features/improved_block_editing/graphics1', 'path_string' => '/1/2/107/133/138/139/', 'priority' => 0, 'remote_id' => '8e6f0ba5195410b03a84ef3fdcc3ff0b', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 138, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 140, 'modified_subnode' => 1311065030, 'node_id' => 140, 'parent_node_id' => 138, 'path_identification_string' => 'getting_started/new_features/improved_block_editing/graphics12', 'path_string' => '/1/2/107/133/138/140/', 'priority' => 0, 'remote_id' => 'bc416b97e1592c7a396f6f5f85e131ac', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 139, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 141, 'modified_subnode' => 1311065032, 'node_id' => 141, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/improved_front_end_editing', 'path_string' => '/1/2/107/133/141/', 'priority' => 0, 'remote_id' => 'facfd2587054ea863cbe53768da04a47', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 140, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 142, 'modified_subnode' => 1311065031, 'node_id' => 142, 'parent_node_id' => 141, 'path_identification_string' => 'getting_started/new_features/improved_front_end_editing/graphics1', 'path_string' => '/1/2/107/133/141/142/', 'priority' => 0, 'remote_id' => 'd159e8d4d590e481b06ecb441c0f14dc', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 141, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 143, 'modified_subnode' => 1311065031, 'node_id' => 143, 'parent_node_id' => 141, 'path_identification_string' => 'getting_started/new_features/improved_front_end_editing/graphics12', 'path_string' => '/1/2/107/133/141/143/', 'priority' => 0, 'remote_id' => '7457db5919dd349bd878b60592fc009b', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 142, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 144, 'modified_subnode' => 1311065032, 'node_id' => 144, 'parent_node_id' => 141, 'path_identification_string' => 'getting_started/new_features/improved_front_end_editing/graphics13', 'path_string' => '/1/2/107/133/141/144/', 'priority' => 0, 'remote_id' => 'ebe1e9d1242a3aeedfda25da7cc25564', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 143, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 145, 'modified_subnode' => 1311065032, 'node_id' => 145, 'parent_node_id' => 141, 'path_identification_string' => 'getting_started/new_features/improved_front_end_editing/graphics14', 'path_string' => '/1/2/107/133/141/145/', 'priority' => 0, 'remote_id' => 'b3acf4c0f2df8977f1c6932725339a1d', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 144, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 146, 'modified_subnode' => 1311065032, 'node_id' => 146, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/fastcgi', 'path_string' => '/1/2/107/133/146/', 'priority' => 0, 'remote_id' => 'a324a48fea49eb3e6826b3295dc5f413', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 145, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 147, 'modified_subnode' => 1311065033, 'node_id' => 147, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/support_for_red_hat_enterprise', 'path_string' => '/1/2/107/133/147/', 'priority' => 0, 'remote_id' => '8c0133434d7bea077a614e1a85fca5b7', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 146, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 148, 'modified_subnode' => 1311065033, 'node_id' => 148, 'parent_node_id' => 147, 'path_identification_string' => 'getting_started/new_features/support_for_red_hat_enterprise/graphics1', 'path_string' => '/1/2/107/133/147/148/', 'priority' => 0, 'remote_id' => '46653e0e5fa60ab86788cad8084a759d', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 147, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 149, 'modified_subnode' => 1311065033, 'node_id' => 149, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/canonical_links', 'path_string' => '/1/2/107/133/149/', 'priority' => 0, 'remote_id' => '9b17e8d0b2cee501b75f7b481155e64f', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 148, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 150, 'modified_subnode' => 1311065034, 'node_id' => 150, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/asynchronous_publishing', 'path_string' => '/1/2/107/133/150/', 'priority' => 0, 'remote_id' => '3983d3aa7604f45a8ecbb953a98d646f', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 149, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 151, 'modified_subnode' => 1311065034, 'node_id' => 151, 'parent_node_id' => 150, 'path_identification_string' => 'getting_started/new_features/asynchronous_publishing/graphics1', 'path_string' => '/1/2/107/133/150/151/', 'priority' => 0, 'remote_id' => '92756622f207c0b1902ca156947a77de', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 150, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 152, 'modified_subnode' => 1311065034, 'node_id' => 152, 'parent_node_id' => 133, 'path_identification_string' => 'getting_started/new_features/rest_api_interface', 'path_string' => '/1/2/107/133/152/', 'priority' => 0, 'remote_id' => '60adbb1e37a6524246d3af372366754f', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 151, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 153, 'modified_subnode' => 1311065035, 'node_id' => 153, 'parent_node_id' => 2, 'path_identification_string' => 'ibexa_enterprise', 'path_string' => '/1/2/153/', 'priority' => 0, 'remote_id' => 'f4bf2e6c1cf75e15b15f0123a82778a1', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 152, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 154, 'modified_subnode' => 1311065035, 'node_id' => 154, 'parent_node_id' => 153, 'path_identification_string' => 'ibexa_enterprise/graphics1', 'path_string' => '/1/2/153/154/', 'priority' => 0, 'remote_id' => '1b95356b68bd59ef32b0fd7bb2c29130', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 153, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 155, 'modified_subnode' => 1311065035, 'node_id' => 155, 'parent_node_id' => 153, 'path_identification_string' => 'ibexa_enterprise/graphics12', 'path_string' => '/1/2/153/155/', 'priority' => 0, 'remote_id' => '94b7059eb5e0fe667f7c63eadd53a62b', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 154, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 156, 'modified_subnode' => 1311065039, 'node_id' => 156, 'parent_node_id' => 2, 'path_identification_string' => 'partners', 'path_string' => '/1/2/156/', 'priority' => 3, 'remote_id' => '0c523dfd6b1eccbfb0d1bfe632ee7411', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 155, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 157, 'modified_subnode' => 1311065036, 'node_id' => 157, 'parent_node_id' => 156, 'path_identification_string' => 'partners/partner_news', 'path_string' => '/1/2/156/157/', 'priority' => 0, 'remote_id' => 'f5db8acf1150afc15eb514d380e3855d', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 156, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 158, 'modified_subnode' => 1311065036, 'node_id' => 158, 'parent_node_id' => 157, 'path_identification_string' => 'partners/partner_news/pellentesque_quam_mauris', 'path_string' => '/1/2/156/157/158/', 'priority' => 0, 'remote_id' => '6366a99ac1cec2133e87fdd805d108c8', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 157, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 159, 'modified_subnode' => 1311065036, 'node_id' => 159, 'parent_node_id' => 157, 'path_identification_string' => 'partners/partner_news/penatibus_et_magnis_dis', 'path_string' => '/1/2/156/157/159/', 'priority' => 0, 'remote_id' => '7ee406cb6e5f0b2a29bc9f97f5a5b9db', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 158, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 160, 'modified_subnode' => 1311065038, 'node_id' => 160, 'parent_node_id' => 156, 'path_identification_string' => 'partners/news', 'path_string' => '/1/2/156/160/', 'priority' => 0, 'remote_id' => '007109e7f353c86dfd48d844cb18ce73', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 159, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 161, 'modified_subnode' => 1311065037, 'node_id' => 161, 'parent_node_id' => 160, 'path_identification_string' => 'partners/news/curabitur_hendrerit_dignissim', 'path_string' => '/1/2/156/160/161/', 'priority' => 0, 'remote_id' => '63769c39380a45a731aa44068c0f4e33', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 160, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 162, 'modified_subnode' => 1311065037, 'node_id' => 162, 'parent_node_id' => 160, 'path_identification_string' => 'partners/news/proin_condimentum_risus', 'path_string' => '/1/2/156/160/162/', 'priority' => 0, 'remote_id' => 'f761918d15e356e50ce94294cad79765', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 161, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 163, 'modified_subnode' => 1311065038, 'node_id' => 163, 'parent_node_id' => 160, 'path_identification_string' => 'partners/news/morbi_tristique_senectus', 'path_string' => '/1/2/156/160/163/', 'priority' => 0, 'remote_id' => '68187b44ef9051a4db10bd9bb7f228a4', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 162, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 164, 'modified_subnode' => 1311065038, 'node_id' => 164, 'parent_node_id' => 156, 'path_identification_string' => 'partners/partner_products', 'path_string' => '/1/2/156/164/', 'priority' => 0, 'remote_id' => '472e9bc321148baa71d2070063f24bb9', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 163, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 165, 'modified_subnode' => 1311065039, 'node_id' => 165, 'parent_node_id' => 156, 'path_identification_string' => 'partners/partners', 'path_string' => '/1/2/156/165/', 'priority' => 0, 'remote_id' => 'b1a68df10cdf379cf78999c8012db679', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 164, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 166, 'modified_subnode' => 1311065039, 'node_id' => 166, 'parent_node_id' => 165, 'path_identification_string' => 'partners/partners/neque_orci_malesuada_felis', 'path_string' => '/1/2/156/165/166/', 'priority' => 0, 'remote_id' => 'e9ee6724fffb012f918300de66af27e1', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 165, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 167, 'modified_subnode' => 1311065044, 'node_id' => 167, 'parent_node_id' => 2, 'path_identification_string' => 'community', 'path_string' => '/1/2/167/', 'priority' => 4, 'remote_id' => 'c4604fb2e100a6681a4f53fbe6e5eeae', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 166, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 168, 'modified_subnode' => 1311065040, 'node_id' => 168, 'parent_node_id' => 167, 'path_identification_string' => 'community/forum', 'path_string' => '/1/2/167/168/', 'priority' => 0, 'remote_id' => '5e6762a083ab4c0357bad163902ba8f7', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 167, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 169, 'modified_subnode' => 1311065039, 'node_id' => 169, 'parent_node_id' => 168, 'path_identification_string' => 'community/forum/nulla_vitae_tellus_sit_amet', 'path_string' => '/1/2/167/168/169/', 'priority' => 0, 'remote_id' => '2549eee03064d55ae7fc84fe41ae1ef6', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 168, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 170, 'modified_subnode' => 1311065040, 'node_id' => 170, 'parent_node_id' => 168, 'path_identification_string' => 'community/forum/ut_mollis_sodales_nibh', 'path_string' => '/1/2/167/168/170/', 'priority' => 0, 'remote_id' => '7bfca4bdde619ac9104e235629e2dbc7', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 169, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 171, 'modified_subnode' => 1311065040, 'node_id' => 171, 'parent_node_id' => 168, 'path_identification_string' => 'community/forum/nam_risus_leo', 'path_string' => '/1/2/167/168/171/', 'priority' => 0, 'remote_id' => '81fd930afe94962395074b762592a356', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 170, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 172, 'modified_subnode' => 1311065041, 'node_id' => 172, 'parent_node_id' => 167, 'path_identification_string' => 'community/wiki', 'path_string' => '/1/2/167/172/', 'priority' => 0, 'remote_id' => '8b570ac03318b7e29eb5fd1bae15efde', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 171, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 173, 'modified_subnode' => 1311065041, 'node_id' => 173, 'parent_node_id' => 172, 'path_identification_string' => 'community/wiki/sed_suscipit', 'path_string' => '/1/2/167/172/173/', 'priority' => 0, 'remote_id' => '4329dcc6a8d441aa7253660512dfd5b6', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 172, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 174, 'modified_subnode' => 1311065041, 'node_id' => 174, 'parent_node_id' => 173, 'path_identification_string' => 'community/wiki/sed_suscipit/ut_interdum', 'path_string' => '/1/2/167/172/173/174/', 'priority' => 0, 'remote_id' => '6cf7b9370a0d9800ed49ba3be13b6be5', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 173, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 175, 'modified_subnode' => 1311065041, 'node_id' => 175, 'parent_node_id' => 173, 'path_identification_string' => 'community/wiki/sed_suscipit/fusce_pulvinar', 'path_string' => '/1/2/167/172/173/175/', 'priority' => 0, 'remote_id' => 'f71191fcce35c83530cddde467a908b2', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 174, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 176, 'modified_subnode' => 1311065041, 'node_id' => 176, 'parent_node_id' => 172, 'path_identification_string' => 'community/wiki/duis_id_tortor', 'path_string' => '/1/2/167/172/176/', 'priority' => 0, 'remote_id' => '2d3cf4fe8d439e16e0ed75ef947b5e3d', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 175, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 177, 'modified_subnode' => 1311065041, 'node_id' => 177, 'parent_node_id' => 176, 'path_identification_string' => 'community/wiki/duis_id_tortor/curabitur_lacinia', 'path_string' => '/1/2/167/172/176/177/', 'priority' => 0, 'remote_id' => '49c9ad954328ab5f5a76d580873961a4', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 176, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 178, 'modified_subnode' => 1311065044, 'node_id' => 178, 'parent_node_id' => 167, 'path_identification_string' => 'community/pictures', 'path_string' => '/1/2/167/178/', 'priority' => 0, 'remote_id' => 'ae32173cedf3a5ec441e3afcadc4abcb', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 177, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 179, 'modified_subnode' => 1311065042, 'node_id' => 179, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/ivo_on_ibexa_tags', 'path_string' => '/1/2/167/178/179/', 'priority' => 0, 'remote_id' => 'c6c32c884db955ea09338c90eee84746', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 178, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 180, 'modified_subnode' => 1311065042, 'node_id' => 180, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/are_you_ready_for_the_community_day', 'path_string' => '/1/2/167/178/180/', 'priority' => 0, 'remote_id' => 'db191e87b9b6ac22a18742cd32d2b3d4', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 179, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 181, 'modified_subnode' => 1311065042, 'node_id' => 181, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/let_s_work_together', 'path_string' => '/1/2/167/178/181/', 'priority' => 0, 'remote_id' => '02a6d1506bf437eda1317d0c36a8545b', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 180, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 182, 'modified_subnode' => 1311065043, 'node_id' => 182, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/the_mediterranean_seen_from_nice', 'path_string' => '/1/2/167/178/182/', 'priority' => 0, 'remote_id' => '40d743025e4ec4d813bd864dad08e0cc', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 181, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 183, 'modified_subnode' => 1311065043, 'node_id' => 183, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/hotel_lobby_at_isola2000', 'path_string' => '/1/2/167/178/183/', 'priority' => 0, 'remote_id' => 'eb2a8ef7d4b1fb8ffb68f0764b615de1', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 182, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 184, 'modified_subnode' => 1311065043, 'node_id' => 184, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/alexander_on_the_past_gaby_on_today_and_tomorrow', 'path_string' => '/1/2/167/178/184/', 'priority' => 0, 'remote_id' => '9e45f3ee0f15995b670fb3a117b6ee49', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 183, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 185, 'modified_subnode' => 1311065043, 'node_id' => 185, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/isola2000_in_white_beautiful_skiing', 'path_string' => '/1/2/167/178/185/', 'priority' => 0, 'remote_id' => 'f368c4ae469c596344e15fca8c7370c2', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 184, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 186, 'modified_subnode' => 1311065044, 'node_id' => 186, 'parent_node_id' => 178, 'path_identification_string' => 'community/pictures/ceo_future_pitch_adaptable_open_reliable', 'path_string' => '/1/2/167/178/186/', 'priority' => 0, 'remote_id' => '7e153b75dd0ee0476ac7463d4a3daaef', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 185, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 187, 'modified_subnode' => 1311065044, 'node_id' => 187, 'parent_node_id' => 167, 'path_identification_string' => 'community/blog', 'path_string' => '/1/2/167/187/', 'priority' => 0, 'remote_id' => '09fb4147e4b124b9770a527bdd66799c', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 186, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 188, 'modified_subnode' => 1311065044, 'node_id' => 188, 'parent_node_id' => 187, 'path_identification_string' => 'community/blog/visit_paris', 'path_string' => '/1/2/167/187/188/', 'priority' => 0, 'remote_id' => '8f73d6d4ffca33c619d2f2645340ccc0', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 187, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 189, 'modified_subnode' => 1311065044, 'node_id' => 189, 'parent_node_id' => 187, 'path_identification_string' => 'community/blog/paris_in_france', 'path_string' => '/1/2/167/187/189/', 'priority' => 0, 'remote_id' => '5cdc175d45aeb3fa054a95f40df56e79', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 188, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 190, 'modified_subnode' => 1311065055, 'node_id' => 190, 'parent_node_id' => 2, 'path_identification_string' => 'company', 'path_string' => '/1/2/190/', 'priority' => 6, 'remote_id' => 'e060ff40aa17eb21dc1e4595c9a5eb9a', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 189, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 191, 'modified_subnode' => 1311065045, 'node_id' => 191, 'parent_node_id' => 190, 'path_identification_string' => 'company/about_company', 'path_string' => '/1/2/190/191/', 'priority' => 0, 'remote_id' => '7bf290af79b3b51e6ccc1f95f321618c', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 190, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 192, 'modified_subnode' => 1311065053, 'node_id' => 192, 'parent_node_id' => 190, 'path_identification_string' => 'company/new_features', 'path_string' => '/1/2/190/192/', 'priority' => 0, 'remote_id' => '9f804eee139dcee343dbe4175d14654c', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 191, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 193, 'modified_subnode' => 1311065046, 'node_id' => 193, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/separate_content_design_in_ibexa', 'path_string' => '/1/2/190/192/193/', 'priority' => 0, 'remote_id' => '10d57795c986ca61689e6c2c11382eb3', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 192, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 194, 'modified_subnode' => 1311065046, 'node_id' => 194, 'parent_node_id' => 193, 'path_identification_string' => 'company/new_features/separate_content_design_in_ibexa/separation_of_content_and_design', 'path_string' => '/1/2/190/192/193/194/', 'priority' => 0, 'remote_id' => '87417129b080d24d335eb2653dd1a40c', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 193, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 195, 'modified_subnode' => 1311065048, 'node_id' => 195, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/how_to_use_ibexa', 'path_string' => '/1/2/190/192/195/', 'priority' => 0, 'remote_id' => 'fd56d839df6e2b52055d4a62f4df7c3e', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 194, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 196, 'modified_subnode' => 1311065047, 'node_id' => 196, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ibexa/openoffice_import_large', 'path_string' => '/1/2/190/192/195/196/', 'priority' => 0, 'remote_id' => 'ab2f2093f16c56ac2e61f40f25d2dbe0', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 195, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 197, 'modified_subnode' => 1311065047, 'node_id' => 197, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ibexa/translation_large', 'path_string' => '/1/2/190/192/195/197/', 'priority' => 0, 'remote_id' => '185a1737e9012e7c3025df876bbb4f9b', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 196, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 198, 'modified_subnode' => 1311065047, 'node_id' => 198, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ibexa/multiupload_large', 'path_string' => '/1/2/190/192/195/198/', 'priority' => 0, 'remote_id' => 'efdca2f6ddd2c8befc283ae3465b9acd', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 197, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 199, 'modified_subnode' => 1311065047, 'node_id' => 199, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ibexa/gallery_large', 'path_string' => '/1/2/190/192/195/199/', 'priority' => 0, 'remote_id' => 'f81b0e4c31ea13d94b9838e9e7e7ad7e', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 198, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 200, 'modified_subnode' => 1311065048, 'node_id' => 200, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ibexa/search_result_large', 'path_string' => '/1/2/190/192/195/200/', 'priority' => 0, 'remote_id' => '666efc49c267cddf55ca707408f4ac42', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 199, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 201, 'modified_subnode' => 1311065048, 'node_id' => 201, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ibexa/ez_tool_bar_large', 'path_string' => '/1/2/190/192/195/201/', 'priority' => 0, 'remote_id' => 'afb51a5f898c549c9e1f35afc75d66f3', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 200, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 202, 'modified_subnode' => 1311065048, 'node_id' => 202, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ibexa/quicktime_large', 'path_string' => '/1/2/190/192/195/202/', 'priority' => 0, 'remote_id' => '94b71866ede0d2776c140e85a823d31d', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 201, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 203, 'modified_subnode' => 1311065048, 'node_id' => 203, 'parent_node_id' => 195, 'path_identification_string' => 'company/new_features/how_to_use_ibexa/web_2_0_large', 'path_string' => '/1/2/190/192/195/203/', 'priority' => 0, 'remote_id' => 'e74475a240fb7061b40c75f5e6fcd52c', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 202, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 204, 'modified_subnode' => 1311065051, 'node_id' => 204, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/how_to_manage_ibexa', 'path_string' => '/1/2/190/192/204/', 'priority' => 0, 'remote_id' => '42a3b07037390457ad0f2c65494bfd97', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 203, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 205, 'modified_subnode' => 1311065049, 'node_id' => 205, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/usermanagement_large', 'path_string' => '/1/2/190/192/204/205/', 'priority' => 0, 'remote_id' => '6da1a82ba3d962d2ad953fff8ee26362', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 204, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 206, 'modified_subnode' => 1311065049, 'node_id' => 206, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/workflow_large', 'path_string' => '/1/2/190/192/204/206/', 'priority' => 0, 'remote_id' => 'b284abd88bdcb1887a86e40304ffc66f', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 205, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 207, 'modified_subnode' => 1311065050, 'node_id' => 207, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/versioning_large', 'path_string' => '/1/2/190/192/204/207/', 'priority' => 0, 'remote_id' => 'e3400bb44aae5fcb416156e7450f3f32', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 206, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 208, 'modified_subnode' => 1311065050, 'node_id' => 208, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/datatypes_attributes_content_class_and_objects_large', 'path_string' => '/1/2/190/192/204/208/', 'priority' => 0, 'remote_id' => '2b2b27bbaaec688aaec1a5d39b4b13ae', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 207, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 209, 'modified_subnode' => 1311065050, 'node_id' => 209, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/multiple_location_large', 'path_string' => '/1/2/190/192/204/209/', 'priority' => 0, 'remote_id' => '37e5feed6574d1d8da4b4531569a27a2', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 208, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 210, 'modified_subnode' => 1311065050, 'node_id' => 210, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/multisite_deployment_large', 'path_string' => '/1/2/190/192/204/210/', 'priority' => 0, 'remote_id' => '5625695a66034f53b9b6e14029e06e80', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 209, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 211, 'modified_subnode' => 1311065051, 'node_id' => 211, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/seo_large', 'path_string' => '/1/2/190/192/204/211/', 'priority' => 0, 'remote_id' => '824aba4122ee51d9638847e55642feec', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 210, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 212, 'modified_subnode' => 1311065051, 'node_id' => 212, 'parent_node_id' => 204, 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/admin_site_large', 'path_string' => '/1/2/190/192/204/212/', 'priority' => 0, 'remote_id' => '73e95051b800ce051c3851c60807787b', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 211, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 213, 'modified_subnode' => 1311065052, 'node_id' => 213, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/how_to_develop_with_ibexa', 'path_string' => '/1/2/190/192/213/', 'priority' => 0, 'remote_id' => '3f921d3c490a6c17870b1f01fcfbceed', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 212, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 214, 'modified_subnode' => 1311065052, 'node_id' => 214, 'parent_node_id' => 213, 'path_identification_string' => 'company/new_features/how_to_develop_with_ibexa/datatypes_attributes_content_class_and_objects_large', 'path_string' => '/1/2/190/192/213/214/', 'priority' => 0, 'remote_id' => '97f7b391f1522bf404ff672ac338b91c', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 213, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 5, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 215, 'modified_subnode' => 1311065052, 'node_id' => 215, 'parent_node_id' => 213, 'path_identification_string' => 'company/new_features/how_to_develop_with_ibexa/site_style_large', 'path_string' => '/1/2/190/192/213/215/', 'priority' => 0, 'remote_id' => 'c5549bf68962a5384bd2225931cba302', 'sort_field' => 2, 'sort_order' => 0], + ['contentobject_id' => 214, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 216, 'modified_subnode' => 1311065052, 'node_id' => 216, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/rest_api_interface', 'path_string' => '/1/2/190/192/216/', 'priority' => 0, 'remote_id' => '54adfd9f7c80638f5560eb3f0618c57b', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 215, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 217, 'modified_subnode' => 1311065052, 'node_id' => 217, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/asynchronous_publishing', 'path_string' => '/1/2/190/192/217/', 'priority' => 0, 'remote_id' => 'a7dbafbf963821b71e5522c21e09d9dd', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 216, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 218, 'modified_subnode' => 1311065053, 'node_id' => 218, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/canonical_links', 'path_string' => '/1/2/190/192/218/', 'priority' => 0, 'remote_id' => '61e3b15f020590df0759b779e5f1fb9d', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 217, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 219, 'modified_subnode' => 1311065053, 'node_id' => 219, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/support_for_red_hat_enterprise', 'path_string' => '/1/2/190/192/219/', 'priority' => 0, 'remote_id' => '42560fa30a55ac5ddf1f76a52d44a613', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 218, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 220, 'modified_subnode' => 1311065053, 'node_id' => 220, 'parent_node_id' => 192, 'path_identification_string' => 'company/new_features/fastcgi', 'path_string' => '/1/2/190/192/220/', 'priority' => 0, 'remote_id' => '416c28c9ba34a9114381a6ec3ae197e0', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 219, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 221, 'modified_subnode' => 1311065053, 'node_id' => 221, 'parent_node_id' => 190, 'path_identification_string' => 'company/events', 'path_string' => '/1/2/190/221/', 'priority' => 0, 'remote_id' => 'ae6afe955af19209125961b6c0d9a840', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 220, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 222, 'modified_subnode' => 1311065054, 'node_id' => 222, 'parent_node_id' => 190, 'path_identification_string' => 'company/career', 'path_string' => '/1/2/190/222/', 'priority' => 0, 'remote_id' => '28b7fd81eaf6b9702c9f8ff8268daac9', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 221, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 223, 'modified_subnode' => 1311065054, 'node_id' => 223, 'parent_node_id' => 190, 'path_identification_string' => 'company/investors_relation', 'path_string' => '/1/2/190/223/', 'priority' => 0, 'remote_id' => '627c1c5cdf124dc2f92489e686cd04bb', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 222, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 1, 'is_invisible' => 1, 'main_node_id' => 224, 'modified_subnode' => 1311065055, 'node_id' => 224, 'parent_node_id' => 190, 'path_identification_string' => 'company/contact', 'path_string' => '/1/2/190/224/', 'priority' => 0, 'remote_id' => 'daa3004ede25b0130ca5bef83c1922c3', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 223, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 4, 'is_hidden' => 0, 'is_invisible' => 1, 'main_node_id' => 225, 'modified_subnode' => 1311065055, 'node_id' => 225, 'parent_node_id' => 224, 'path_identification_string' => 'company/contact/morbi_neque', 'path_string' => '/1/2/190/224/225/', 'priority' => 0, 'remote_id' => '9d2bdcb8b35464ee03651c191ac4d1ff', 'sort_field' => 2, 'sort_order' => 1], + ['contentobject_id' => 224, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 3, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 226, 'modified_subnode' => 1311065055, 'node_id' => 226, 'parent_node_id' => 190, 'path_identification_string' => 'company/company_banner', 'path_string' => '/1/2/190/226/', 'priority' => 0, 'remote_id' => '9ae96e2ecc3a5a79964405273cc8542a', 'sort_field' => 1, 'sort_order' => 1], + ['contentobject_id' => 225, 'contentobject_is_published' => 1, 'contentobject_version' => 1, 'depth' => 2, 'is_hidden' => 0, 'is_invisible' => 0, 'main_node_id' => 227, 'modified_subnode' => 1311065058, 'node_id' => 227, 'parent_node_id' => 5, 'path_identification_string' => 'users/partners', 'path_string' => '/1/5/227/', 'priority' => 0, 'remote_id' => '5238a276bf8231fbcf8a986cdc82a6a5', 'sort_field' => 1, 'sort_order' => 1], + ], + 'eznode_assignment' => [ + ['contentobject_id' => 8, 'contentobject_version' => 2, 'from_node_id' => 0, 'id' => 4, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 42, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 5, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 9, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 10, 'contentobject_version' => 2, 'from_node_id' => -1, 'id' => 6, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 44, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 9, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 4, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 7, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 1, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 12, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 8, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 13, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 9, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 41, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 11, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 1, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 11, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 12, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 45, 'contentobject_version' => 1, 'from_node_id' => -1, 'id' => 16, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 1, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 9, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 49, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 27, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 43, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 9, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 50, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 28, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 43, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 9, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 51, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 29, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 43, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 9, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 52, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 30, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 48, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 56, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 34, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 1, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 14, 'contentobject_version' => 3, 'from_node_id' => -1, 'id' => 38, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 13, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 54, 'contentobject_version' => 2, 'from_node_id' => -1, 'id' => 39, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 58, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 57, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 43, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 43, 'parent_remote_id' => '437ef9d0a9b7ae326ec83fa3bb73956d', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 58, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 44, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => 'eaa7f2f48c3f35801961abad12151db4', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 59, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 45, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => 'b8c85fd926d61dab6e68fa1865cee987', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 60, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 46, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => 'c65aba2485585bdd09dfb66afccf645e', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 61, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 47, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => '64bb803471e53898aa38a7c29e482370', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 62, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 48, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => '95e29503817570c6458fa0f37d227306', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 63, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 49, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => 'aa4a1afd9c02d00f2f31186e8a271332', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 64, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 50, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 59, 'parent_remote_id' => '93d5115082a23b266613868051b8d803', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 65, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 51, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => '1a8744c6d6be8675f7b27fa8b7b8437d', 'remote_id' => 0, 'sort_field' => 8, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 66, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 52, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 51, 'parent_remote_id' => '31fd28362c18a36cb56223f3609d5d90', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 67, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 53, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => '9cec85d730eec7578190ee95ce5a36f5', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 68, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 54, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 69, 'parent_remote_id' => 'b0b85c15125ca1732e5e528de2717599', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 69, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 55, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 70, 'parent_remote_id' => '087adb763245e0cdcac593fb4a5996cf', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 70, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 56, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 69, 'parent_remote_id' => 'e607aab6e924091909f3def02415bc53', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 71, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 57, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 72, 'parent_remote_id' => '054d9f10c6fa97689c0fc3b2ac412ebd', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 72, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 58, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 72, 'parent_remote_id' => '9801bda46e5f8b9d692e1120d50fc7b3', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 73, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 59, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 72, 'parent_remote_id' => '005067a5eee6505aa0f601cca30681d0', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 74, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 60, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 69, 'parent_remote_id' => '18f14551cc555c094b15a732ccd27fb2', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 75, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 61, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => 'dbc2f3c8716c12f32c379dbf0b1cb133', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 76, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 62, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 77, 'parent_remote_id' => 'bc766fe955437def220a3fa2966a34ee', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 77, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 63, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 78, 'parent_remote_id' => 'f0c2216ecb29600cd8ae93951a0c8f3a', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 78, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 64, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 78, 'parent_remote_id' => 'eaf16bddfd36206dad265aadfbc98f17', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 79, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 65, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 78, 'parent_remote_id' => 'd2a11e56093b77eb7a347229361c3377', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 80, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 66, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 77, 'parent_remote_id' => '17d65b568e3500cf1f8b42bc5de2d12b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 81, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 67, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 82, 'parent_remote_id' => 'ecc4f0e94b05bf10f4f783d660ff0ad0', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 82, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 68, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 82, 'parent_remote_id' => '74280af2cba9002ea9660749225562b6', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 83, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 69, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 82, 'parent_remote_id' => '4e526426523e47aeacf353541284cbf8', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 84, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 70, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => '95f3c4261719ea27ab67f980fbee0694', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 85, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 71, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 86, 'parent_remote_id' => '4a1391d3563d056c9d9ea2653093ae3e', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 86, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 72, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 86, 'parent_remote_id' => '8889727909b5f34b6aa23f7eee32606b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 87, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 73, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 88, 'parent_remote_id' => '1721dbee55639fe280f6a54195f9577c', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 88, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 74, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 88, 'parent_remote_id' => '7fabb9ee5bcb6630a3947d1c5585d995', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 89, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 75, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 88, 'parent_remote_id' => 'a95ae3e4a64f08eb2002b31680fe8989', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 90, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 76, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 86, 'parent_remote_id' => '2acc8bb8f7eda4de2cf74f6dc277661f', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 91, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 77, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 86, 'parent_remote_id' => '6d8cc9831b86d79b8c184507f8e0cbb6', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 92, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 78, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 86, 'parent_remote_id' => '30f12ecd29608eceb20bb903ddb780c7', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 93, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 79, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 86, 'parent_remote_id' => '341e63bdce0f1601519d1b8e82e62766', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 94, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 80, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => '0d55a3f510cc7cd9a7b8eb838f50ff5c', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 95, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 81, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 96, 'parent_remote_id' => '89ce8eed8fa06c4105fd612aa83d87d6', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 96, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 82, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 97, 'parent_remote_id' => '78f3b9a9268c7b0206bf1c4d39211495', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 97, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 83, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 98, 'parent_remote_id' => 'a1f9b32547e58064e645388512c16a39', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 98, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 84, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 96, 'parent_remote_id' => '0f9c7380f8af1f29f1017e412bdd4016', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 99, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 85, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 100, 'parent_remote_id' => '036394ec8b160f0782bec2dda452d798', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 100, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 86, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 101, 'parent_remote_id' => '78da6a4fae1c2eaabe1dbe7af818d970', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 101, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 87, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 96, 'parent_remote_id' => '06cdeb27be466ea8330de5df16144263', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 102, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 88, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 103, 'parent_remote_id' => 'ab30399896b8e54442c3a619ba7eeecb', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 103, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 89, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 96, 'parent_remote_id' => 'ca2ae9d0f0322798f632e896325f22c3', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 104, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 90, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 96, 'parent_remote_id' => '7ba1d48c0151bae62366095ef6f64c28', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 105, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 91, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => 'e81507d3446726ebd7361352fef5fad3', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 106, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 92, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '51278360f39d5b8ce1d9249953f4de98', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 107, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 93, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '33151e24acea9c837d2b9fc52e03b1de', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 108, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 94, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '6b3bcab0f149c5acc2e3728df7c66b73', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 109, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 95, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '28f9dfe5c0680955eec7a2dec4ebc642', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 110, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 96, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '191faba79dc108a19893944befa50d94', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 111, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 97, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '8679b5fa3ad0ec216293419ab9834e44', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 112, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 98, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '7f32cdefd0cf55b966a44aa52181e30d', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 113, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 99, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '29db3c1fcdb497e5cb2c74eb85c0906a', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 114, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 100, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '96e30009f712a0315217fed93eba6a18', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 115, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 101, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => '3a6b87470a25b19a6ad15caa5e24e719', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 116, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 102, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => 'd0bc77a21920b63543d7b0accab81b24', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 117, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 103, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 110, 'parent_remote_id' => 'c75b4b4b870e0e3611e19e1323103282', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 118, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 104, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '262d8c936d3757ff81e7bb49392b703f', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 119, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 105, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => '220d4e10bf4619525c3165823079482c', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 120, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 106, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => 'f13a938ce2c79a9d438548299220d6dd', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 121, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 107, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => '04f5e08293954b1851a4dd1cbd976cff', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 122, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 108, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => 'dc80ae3d9de55855a16218b032062c62', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 123, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 109, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => '4cb6d4015dd80474074043ab28f96e36', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 124, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 110, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => '74ce7d6cbae81d90592b4a8a242a284a', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 125, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 111, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => '6f76d2f7e5812acf4f7c5c70258f4f7e', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 126, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 112, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 120, 'parent_remote_id' => '5ba07103968a6bb3f05c275c212440f6', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 127, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 113, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '3579b6c5cd166d5137eada55274892d3', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 128, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 114, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 129, 'parent_remote_id' => 'e38a43ce35832e5af2df4f1e9272b926', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 129, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 115, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 129, 'parent_remote_id' => '1ba7889f63e32acf70f66349ce1a8953', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 130, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 116, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '19ae9fcb7334a0e407b2781920247122', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 131, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 117, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 107, 'parent_remote_id' => '423b6d20325263abaa2012536cc736a4', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 132, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 118, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => '07f0de334c83d631a59defe61f1a7cbf', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 133, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 119, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 134, 'parent_remote_id' => 'a7ca1a8878210a9978df0b00e9184b20', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 134, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 120, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 134, 'parent_remote_id' => '12c86be2151c426b990d47efd023cc40', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 135, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 121, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 134, 'parent_remote_id' => 'ff4473534c7cef8212a11feab57a5f0e', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 136, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 122, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => 'e0a1b1b2a3d248bf8d43a83581c22ae8', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 137, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 123, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 138, 'parent_remote_id' => '8e6f0ba5195410b03a84ef3fdcc3ff0b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 138, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 124, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 138, 'parent_remote_id' => 'bc416b97e1592c7a396f6f5f85e131ac', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 139, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 125, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => 'facfd2587054ea863cbe53768da04a47', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 140, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 126, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 141, 'parent_remote_id' => 'd159e8d4d590e481b06ecb441c0f14dc', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 141, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 127, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 141, 'parent_remote_id' => '7457db5919dd349bd878b60592fc009b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 142, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 128, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 141, 'parent_remote_id' => 'ebe1e9d1242a3aeedfda25da7cc25564', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 143, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 129, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 141, 'parent_remote_id' => 'b3acf4c0f2df8977f1c6932725339a1d', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 144, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 130, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => 'a324a48fea49eb3e6826b3295dc5f413', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 145, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 131, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => '8c0133434d7bea077a614e1a85fca5b7', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 146, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 132, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 147, 'parent_remote_id' => '46653e0e5fa60ab86788cad8084a759d', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 147, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 133, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => '9b17e8d0b2cee501b75f7b481155e64f', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 148, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 134, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => '3983d3aa7604f45a8ecbb953a98d646f', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 149, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 135, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 150, 'parent_remote_id' => '92756622f207c0b1902ca156947a77de', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 150, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 136, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 133, 'parent_remote_id' => '60adbb1e37a6524246d3af372366754f', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 151, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 137, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => 'f4bf2e6c1cf75e15b15f0123a82778a1', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 152, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 138, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 153, 'parent_remote_id' => '1b95356b68bd59ef32b0fd7bb2c29130', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 153, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 139, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 153, 'parent_remote_id' => '94b7059eb5e0fe667f7c63eadd53a62b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 154, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 140, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => '0c523dfd6b1eccbfb0d1bfe632ee7411', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 3, 'is_hidden' => 0], + ['contentobject_id' => 155, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 141, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 156, 'parent_remote_id' => 'f5db8acf1150afc15eb514d380e3855d', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 156, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 142, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 157, 'parent_remote_id' => '6366a99ac1cec2133e87fdd805d108c8', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 157, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 143, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 157, 'parent_remote_id' => '7ee406cb6e5f0b2a29bc9f97f5a5b9db', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 158, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 144, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 156, 'parent_remote_id' => '007109e7f353c86dfd48d844cb18ce73', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 159, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 145, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 160, 'parent_remote_id' => '63769c39380a45a731aa44068c0f4e33', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 160, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 146, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 160, 'parent_remote_id' => 'f761918d15e356e50ce94294cad79765', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 161, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 147, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 160, 'parent_remote_id' => '68187b44ef9051a4db10bd9bb7f228a4', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 162, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 148, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 156, 'parent_remote_id' => '472e9bc321148baa71d2070063f24bb9', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 163, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 149, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 156, 'parent_remote_id' => 'b1a68df10cdf379cf78999c8012db679', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 164, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 150, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 165, 'parent_remote_id' => 'e9ee6724fffb012f918300de66af27e1', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 165, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 151, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => 'c4604fb2e100a6681a4f53fbe6e5eeae', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 4, 'is_hidden' => 0], + ['contentobject_id' => 166, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 152, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 167, 'parent_remote_id' => '5e6762a083ab4c0357bad163902ba8f7', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 167, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 153, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 168, 'parent_remote_id' => '2549eee03064d55ae7fc84fe41ae1ef6', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 168, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 154, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 168, 'parent_remote_id' => '7bfca4bdde619ac9104e235629e2dbc7', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 169, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 155, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 168, 'parent_remote_id' => '81fd930afe94962395074b762592a356', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 170, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 156, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 167, 'parent_remote_id' => '8b570ac03318b7e29eb5fd1bae15efde', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 171, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 157, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 172, 'parent_remote_id' => '4329dcc6a8d441aa7253660512dfd5b6', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 172, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 158, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 173, 'parent_remote_id' => '6cf7b9370a0d9800ed49ba3be13b6be5', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 173, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 159, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 173, 'parent_remote_id' => 'f71191fcce35c83530cddde467a908b2', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 174, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 160, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 172, 'parent_remote_id' => '2d3cf4fe8d439e16e0ed75ef947b5e3d', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 175, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 161, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 176, 'parent_remote_id' => '49c9ad954328ab5f5a76d580873961a4', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 176, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 162, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 167, 'parent_remote_id' => 'ae32173cedf3a5ec441e3afcadc4abcb', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 177, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 163, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => 'c6c32c884db955ea09338c90eee84746', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 178, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 164, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => 'db191e87b9b6ac22a18742cd32d2b3d4', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 179, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 165, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => '02a6d1506bf437eda1317d0c36a8545b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 180, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 166, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => '40d743025e4ec4d813bd864dad08e0cc', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 181, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 167, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => 'eb2a8ef7d4b1fb8ffb68f0764b615de1', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 182, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 168, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => '9e45f3ee0f15995b670fb3a117b6ee49', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 183, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 169, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => 'f368c4ae469c596344e15fca8c7370c2', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 184, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 170, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 178, 'parent_remote_id' => '7e153b75dd0ee0476ac7463d4a3daaef', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 185, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 171, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 167, 'parent_remote_id' => '09fb4147e4b124b9770a527bdd66799c', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 186, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 172, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 187, 'parent_remote_id' => '8f73d6d4ffca33c619d2f2645340ccc0', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 187, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 173, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 187, 'parent_remote_id' => '5cdc175d45aeb3fa054a95f40df56e79', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 188, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 174, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 2, 'parent_remote_id' => 'e060ff40aa17eb21dc1e4595c9a5eb9a', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 6, 'is_hidden' => 0], + ['contentobject_id' => 189, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 175, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => '7bf290af79b3b51e6ccc1f95f321618c', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 190, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 176, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => '9f804eee139dcee343dbe4175d14654c', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 191, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 177, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '10d57795c986ca61689e6c2c11382eb3', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 192, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 178, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 193, 'parent_remote_id' => '87417129b080d24d335eb2653dd1a40c', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 193, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 179, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => 'fd56d839df6e2b52055d4a62f4df7c3e', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 194, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 180, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => 'ab2f2093f16c56ac2e61f40f25d2dbe0', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 195, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 181, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => '185a1737e9012e7c3025df876bbb4f9b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 196, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 182, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => 'efdca2f6ddd2c8befc283ae3465b9acd', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 197, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 183, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => 'f81b0e4c31ea13d94b9838e9e7e7ad7e', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 198, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 184, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => '666efc49c267cddf55ca707408f4ac42', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 199, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 185, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => 'afb51a5f898c549c9e1f35afc75d66f3', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 200, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 186, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => '94b71866ede0d2776c140e85a823d31d', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 201, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 187, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 195, 'parent_remote_id' => 'e74475a240fb7061b40c75f5e6fcd52c', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 202, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 188, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '42a3b07037390457ad0f2c65494bfd97', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 203, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 189, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => '6da1a82ba3d962d2ad953fff8ee26362', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 204, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 190, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => 'b284abd88bdcb1887a86e40304ffc66f', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 205, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 191, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => 'e3400bb44aae5fcb416156e7450f3f32', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 206, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 192, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => '2b2b27bbaaec688aaec1a5d39b4b13ae', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 207, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 193, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => '37e5feed6574d1d8da4b4531569a27a2', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 208, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 194, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => '5625695a66034f53b9b6e14029e06e80', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 209, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 195, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => '824aba4122ee51d9638847e55642feec', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 210, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 196, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 204, 'parent_remote_id' => '73e95051b800ce051c3851c60807787b', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 211, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 197, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '3f921d3c490a6c17870b1f01fcfbceed', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 212, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 198, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 213, 'parent_remote_id' => '97f7b391f1522bf404ff672ac338b91c', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 213, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 199, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 213, 'parent_remote_id' => 'c5549bf68962a5384bd2225931cba302', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 0, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 214, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 200, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '54adfd9f7c80638f5560eb3f0618c57b', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 215, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 201, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => 'a7dbafbf963821b71e5522c21e09d9dd', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 216, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 202, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '61e3b15f020590df0759b779e5f1fb9d', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 217, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 203, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '42560fa30a55ac5ddf1f76a52d44a613', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 218, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 204, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 192, 'parent_remote_id' => '416c28c9ba34a9114381a6ec3ae197e0', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 219, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 205, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => 'ae6afe955af19209125961b6c0d9a840', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 220, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 206, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => '28b7fd81eaf6b9702c9f8ff8268daac9', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 221, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 207, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => '627c1c5cdf124dc2f92489e686cd04bb', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 222, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 208, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => 'daa3004ede25b0130ca5bef83c1922c3', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 223, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 209, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 224, 'parent_remote_id' => '9d2bdcb8b35464ee03651c191ac4d1ff', 'remote_id' => 0, 'sort_field' => 2, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 224, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 210, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 190, 'parent_remote_id' => '9ae96e2ecc3a5a79964405273cc8542a', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 14, 'contentobject_version' => 4, 'from_node_id' => -1, 'id' => 211, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 13, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 225, 'contentobject_version' => 1, 'from_node_id' => 0, 'id' => 212, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 11, 'contentobject_version' => 2, 'from_node_id' => -1, 'id' => 213, 'is_main' => 1, 'op_code' => 2, 'parent_node' => 5, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ['contentobject_id' => 226, 'contentobject_version' => 1, 'from_node_id' => -1, 'id' => 214, 'is_main' => 1, 'op_code' => 3, 'parent_node' => 77, 'parent_remote_id' => '', 'remote_id' => 0, 'sort_field' => 1, 'sort_order' => 1, 'priority' => 0, 'is_hidden' => 0], + ], + 'ezcontentobject' => [ + ['id' => 226, 'status' => 0], + ['id' => 67, 'status' => 0], + ['id' => 68, 'status' => 0], + ['id' => 69, 'status' => 0], + ['id' => 70, 'status' => 0], + ['id' => 71, 'status' => 0], + ['id' => 72, 'status' => 0], + ['id' => 73, 'status' => 0], + ['id' => 74, 'status' => 0], + ], +]; diff --git a/tests/lib/Persistence/Legacy/Content/Location/MapperTest.php b/tests/lib/Persistence/Legacy/Content/Location/MapperTest.php new file mode 100644 index 0000000000..e242ed5619 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Location/MapperTest.php @@ -0,0 +1,167 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Location; + +use Ibexa\Contracts\Core\Persistence\Content\Location as SPILocation; +use Ibexa\Contracts\Core\Persistence\Content\Location\Trashed; +use Ibexa\Core\Persistence\Legacy\Content\Location\Mapper; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Location\Mapper + */ +class MapperTest extends TestCase +{ + /** + * Location data from the database. + * + * @var array + */ + protected $locationRow = [ + 'node_id' => 77, + 'priority' => 0, + 'is_hidden' => 0, + 'is_invisible' => 0, + 'remote_id' => 'dbc2f3c8716c12f32c379dbf0b1cb133', + 'contentobject_id' => 75, + 'contentobject_version' => 1, + 'parent_node_id' => 2, + 'path_identification_string' => 'solutions', + 'path_string' => '/1/2/77/', + 'modified_subnode' => 1311065017, + 'main_node_id' => 77, + 'depth' => 2, + 'sort_field' => 2, + 'sort_order' => 1, + ]; + + /** + * Expected Location object properties values. + * + * @var array + */ + protected $locationValues = [ + 'id' => 77, + 'priority' => 0, + 'hidden' => false, + 'invisible' => false, + 'remoteId' => 'dbc2f3c8716c12f32c379dbf0b1cb133', + 'contentId' => 75, + 'parentId' => 2, + 'pathIdentificationString' => 'solutions', + 'pathString' => '/1/2/77/', + 'depth' => 2, + 'sortField' => 2, + 'sortOrder' => 1, + ]; + + /** + * Expected Location CreateStruct object properties values. + * + * @var array + */ + protected $locationCreateStructValues = [ + 'contentId' => 75, + 'contentVersion' => 1, + 'hidden' => 0, + 'invisible' => 0, + 'parentId' => 2, + 'pathIdentificationString' => 'solutions', + 'priority' => 0, + 'sortField' => 2, + 'sortOrder' => 1, + ]; + + public function testCreateLocationFromRow() + { + $mapper = new Mapper(); + + $location = $mapper->createLocationFromRow( + $this->locationRow + ); + + $this->assertPropertiesCorrect( + $this->locationValues, + $location + ); + } + + public function testCreateLocationsFromRows() + { + $inputRows = []; + for ($i = 0; $i < 3; ++$i) { + $row = $this->locationRow; + $row['node_id'] += $i; + $inputRows[] = $row; + } + + $mapper = new Mapper(); + + $locations = $mapper->createLocationsFromRows($inputRows); + + $this->assertCount(3, $locations); + foreach ($locations as $location) { + $this->assertInstanceOf( + SPILocation::class, + $location + ); + } + } + + public function testCreateTrashedFromRow() + { + $mapper = new Mapper(); + + $location = $mapper->createLocationFromRow( + $this->locationRow, + null, + new Trashed() + ); + + $this->assertTrue($location instanceof Trashed); + $this->assertPropertiesCorrect( + $this->locationValues, + $location + ); + } + + public function testCreateLocationFromRowWithPrefix() + { + $prefix = 'some_prefix_'; + + $data = []; + foreach ($this->locationRow as $key => $val) { + $data[$prefix . $key] = $val; + } + + $mapper = new Mapper(); + + $location = $mapper->createLocationFromRow($data, $prefix); + + $this->assertPropertiesCorrect( + $this->locationValues, + $location + ); + } + + public function testGetLocationCreateStruct() + { + $mapper = new Mapper(); + + $createStruct = $mapper->getLocationCreateStruct( + $this->locationRow + ); + + $this->assertNotEquals($this->locationRow['remote_id'], $createStruct->remoteId); + $this->assertPropertiesCorrect( + $this->locationCreateStructValues, + $createStruct + ); + } +} + +class_alias(MapperTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Location\MapperTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php b/tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php new file mode 100644 index 0000000000..1dd78d7b72 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Location/TrashHandlerTest.php @@ -0,0 +1,580 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Location; + +use Ibexa\Contracts\Core\Persistence\Content\Location\Trash\TrashResult; +use Ibexa\Contracts\Core\Persistence\Content\Location\Trashed; +use Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResultList; +use Ibexa\Core\Persistence\Legacy\Content as CoreContent; +use Ibexa\Core\Persistence\Legacy\Content\Location\Trash\Handler; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Location\Trash\Handler + */ +class TrashHandlerTest extends TestCase +{ + /** + * Mocked location handler instance. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Location\Handler + */ + protected $locationHandler; + + /** + * Mocked location gateway instance. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Location\Gateway + */ + protected $locationGateway; + + /** + * Mocked location mapper instance. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Location\Mapper + */ + protected $locationMapper; + + /** + * Mocked content handler instance. + * + * @var \PHPUnit\Framework\MockObject\MockObject + */ + protected $contentHandler; + + protected function getTrashHandler() + { + return new Handler( + $this->locationHandler = $this->createMock(CoreContent\Location\Handler::class), + $this->locationGateway = $this->createMock(CoreContent\Location\Gateway::class), + $this->locationMapper = $this->createMock(CoreContent\Location\Mapper::class), + $this->contentHandler = $this->createMock(CoreContent\Handler::class) + ); + } + + public function testTrashSubtree() + { + $handler = $this->getTrashHandler(); + + $this->locationGateway + ->expects($this->at(0)) + ->method('getSubtreeContent') + ->with(20) + ->will( + $this->returnValue( + [ + [ + 'contentobject_id' => 10, + 'node_id' => 20, + 'main_node_id' => 30, + 'parent_node_id' => 40, + ], + [ + 'contentobject_id' => 11, + 'node_id' => 21, + 'main_node_id' => 31, + 'parent_node_id' => 41, + ], + ] + ) + ); + + $this->locationGateway + ->expects($this->at(1)) + ->method('countLocationsByContentId') + ->with(10) + ->will($this->returnValue(1)); + + $this->locationGateway + ->expects($this->at(2)) + ->method('trashLocation') + ->with(20); + + $this->locationGateway + ->expects($this->at(3)) + ->method('countLocationsByContentId') + ->with(11) + ->will($this->returnValue(2)); + + $this->locationGateway + ->expects($this->at(4)) + ->method('removeLocation') + ->with(21); + + $this->locationHandler + ->expects($this->once()) + ->method('markSubtreeModified') + ->with(40); + + $this->locationGateway + ->expects($this->at(5)) + ->method('loadTrashByLocation') + ->with(20) + ->will($this->returnValue($array = ['data…'])); + + $this->locationMapper + ->expects($this->once()) + ->method('createLocationFromRow') + ->with($array, null, new Trashed()) + ->will($this->returnValue(new Trashed(['id' => 20]))); + + $trashedObject = $handler->trashSubtree(20); + self::assertInstanceOf(Trashed::class, $trashedObject); + self::assertSame(20, $trashedObject->id); + } + + public function testTrashSubtreeReturnsNull() + { + $handler = $this->getTrashHandler(); + + $this->locationGateway + ->expects($this->at(0)) + ->method('getSubtreeContent') + ->with(20) + ->will( + $this->returnValue( + [ + [ + 'contentobject_id' => 10, + 'node_id' => 20, + 'main_node_id' => 30, + 'parent_node_id' => 40, + ], + [ + 'contentobject_id' => 11, + 'node_id' => 21, + 'main_node_id' => 31, + 'parent_node_id' => 41, + ], + ] + ) + ); + + $this->locationGateway + ->expects($this->at(1)) + ->method('countLocationsByContentId') + ->with(10) + ->will($this->returnValue(2)); + + $this->locationGateway + ->expects($this->at(2)) + ->method('removeLocation') + ->with(20); + + $this->locationGateway + ->expects($this->at(3)) + ->method('countLocationsByContentId') + ->with(11) + ->will($this->returnValue(1)); + + $this->locationGateway + ->expects($this->at(4)) + ->method('trashLocation') + ->with(21); + + $this->locationHandler + ->expects($this->once()) + ->method('markSubtreeModified') + ->with(40); + + $returnValue = $handler->trashSubtree(20); + self::assertNull($returnValue); + } + + public function testTrashSubtreeUpdatesMainLocation() + { + $handler = $this->getTrashHandler(); + + $this->locationGateway + ->expects($this->at(0)) + ->method('getSubtreeContent') + ->with(20) + ->will( + $this->returnValue( + [ + [ + 'contentobject_id' => 10, + 'node_id' => 20, + 'main_node_id' => 30, + 'parent_node_id' => 40, + ], + [ + 'contentobject_id' => 11, + 'node_id' => 21, + 'main_node_id' => 21, + 'parent_node_id' => 41, + ], + ] + ) + ); + + $this->locationGateway + ->expects($this->at(1)) + ->method('countLocationsByContentId') + ->with(10) + ->will($this->returnValue(1)); + + $this->locationGateway + ->expects($this->at(2)) + ->method('trashLocation') + ->with(20); + + $this->locationGateway + ->expects($this->at(3)) + ->method('countLocationsByContentId') + ->with(11) + ->will($this->returnValue(2)); + + $this->locationGateway + ->expects($this->at(4)) + ->method('removeLocation') + ->with(21); + + $this->locationGateway + ->expects($this->at(5)) + ->method('getFallbackMainNodeData') + ->with(11, 21) + ->will( + $this->returnValue( + [ + 'node_id' => 100, + 'contentobject_version' => 101, + 'parent_node_id' => 102, + ] + ) + ); + + $this->locationHandler + ->expects($this->once()) + ->method('changeMainLocation') + ->with(11, 100, 101, 102); + + $this->locationHandler + ->expects($this->once()) + ->method('markSubtreeModified') + ->with(40); + + $this->locationGateway + ->expects($this->at(6)) + ->method('loadTrashByLocation') + ->with(20) + ->will($this->returnValue($array = ['data…'])); + + $this->locationMapper + ->expects($this->once()) + ->method('createLocationFromRow') + ->with($array, null, new Trashed()) + ->will($this->returnValue(new Trashed(['id' => 20]))); + + $trashedObject = $handler->trashSubtree(20); + self::assertInstanceOf(Trashed::class, $trashedObject); + self::assertSame(20, $trashedObject->id); + } + + public function testRecover() + { + $handler = $this->getTrashHandler(); + + $this->locationGateway + ->expects($this->at(0)) + ->method('untrashLocation') + ->with(69, 23) + ->will( + $this->returnValue( + new Trashed(['id' => 70]) + ) + ); + + self::assertSame(70, $handler->recover(69, 23)); + } + + public function testLoadTrashItem() + { + $handler = $this->getTrashHandler(); + + $this->locationGateway + ->expects($this->at(0)) + ->method('loadTrashByLocation') + ->with(69) + ->will($this->returnValue($array = ['data…'])); + + $this->locationMapper + ->expects($this->at(0)) + ->method('createLocationFromRow') + ->with($array, null, new Trashed()); + + $handler->loadTrashItem(69); + } + + public function testEmptyTrash(): void + { + $handler = $this->getTrashHandler(); + + $expectedTrashed = [ + [ + 'node_id' => 69, + 'path_string' => '/1/2/69/', + 'contentobject_id' => 67, + ], + [ + 'node_id' => 70, + 'path_string' => '/1/2/70/', + 'contentobject_id' => 68, + ], + ]; + + // Index for locationGateway calls + $i = 0; + // Index for contentHandler calls + $iContent = 0; + // Index for locationMapper calls + $iLocation = 0; + + $this->locationGateway + ->expects(self::at($i++)) + ->method('countTrashed') + ->willReturn(2); + + $this->locationGateway + ->expects(self::at($i++)) + ->method('listTrashed') + ->willReturn($expectedTrashed); + + $trashedItemIds = []; + $trashedContentIds = []; + + foreach ($expectedTrashed as $trashedElement) { + $this->locationMapper + ->expects(self::at($iLocation++)) + ->method('createLocationFromRow') + ->willReturn( + new Trashed( + [ + 'id' => $trashedElement['node_id'], + 'contentId' => $trashedElement['contentobject_id'], + 'pathString' => $trashedElement['path_string'], + ] + ) + ); + + $this->contentHandler + ->expects(self::at($iContent++)) + ->method('loadReverseRelations') + ->with($trashedElement['contentobject_id']) + ->willReturn([]); + + $this->locationGateway + ->expects(self::at($i++)) + ->method('removeElementFromTrash') + ->with($trashedElement['node_id']); + + $this->locationGateway + ->expects(self::at($i++)) + ->method('countLocationsByContentId') + ->with($trashedElement['contentobject_id']) + ->willReturn(0); + + $this->contentHandler + ->expects(self::at($iContent++)) + ->method('deleteContent') + ->with($trashedElement['contentobject_id']); + + $trashedItemIds[] = $trashedElement['node_id']; + $trashedContentIds[] = $trashedElement['contentobject_id']; + } + + $returnValue = $handler->emptyTrash(); + + self::assertInstanceOf(TrashItemDeleteResultList::class, $returnValue); + + foreach ($returnValue->items as $key => $trashItemDeleteResult) { + self::assertEquals($trashItemDeleteResult->trashItemId, $trashedItemIds[$key]); + self::assertEquals($trashItemDeleteResult->contentId, $trashedContentIds[$key]); + self::assertTrue($trashItemDeleteResult->contentRemoved); + } + } + + public function testDeleteTrashItemNoMoreLocations(): void + { + $handler = $this->getTrashHandler(); + + $trashItemId = 69; + $contentId = 67; + $this->locationGateway + ->expects(self::once()) + ->method('loadTrashByLocation') + ->with($trashItemId) + ->willReturn( + [ + 'node_id' => $trashItemId, + 'contentobject_id' => $contentId, + 'path_string' => '/1/2/69', + ] + ); + + $this->locationMapper + ->expects(self::once()) + ->method('createLocationFromRow') + ->willReturn( + new Trashed( + [ + 'id' => $trashItemId, + 'contentId' => $contentId, + 'pathString' => '/1/2/69', + ] + ) + ); + + $this->contentHandler + ->expects(self::once()) + ->method('loadReverseRelations') + ->with($contentId) + ->willReturn([]); + + $this->locationGateway + ->expects(self::once()) + ->method('removeElementFromTrash') + ->with($trashItemId); + + $this->locationGateway + ->expects(self::once()) + ->method('countLocationsByContentId') + ->with($contentId) + ->willReturn(0); + + $this->contentHandler + ->expects(self::once()) + ->method('deleteContent') + ->with($contentId); + + $trashItemDeleteResult = $handler->deleteTrashItem($trashItemId); + + self::assertInstanceOf(TrashItemDeleteResult::class, $trashItemDeleteResult); + self::assertEquals($trashItemId, $trashItemDeleteResult->trashItemId); + self::assertEquals($contentId, $trashItemDeleteResult->contentId); + self::assertTrue($trashItemDeleteResult->contentRemoved); + } + + public function testDeleteTrashItemStillHaveLocations() + { + $handler = $this->getTrashHandler(); + + $trashItemId = 69; + $contentId = 67; + $this->locationGateway + ->expects($this->once()) + ->method('loadTrashByLocation') + ->with($trashItemId) + ->will( + $this->returnValue( + [ + 'node_id' => $trashItemId, + 'contentobject_id' => $contentId, + 'path_string' => '/1/2/69', + ] + ) + ); + + $this->locationMapper + ->expects($this->once()) + ->method('createLocationFromRow') + ->will( + $this->returnValue( + new Trashed( + [ + 'id' => $trashItemId, + 'contentId' => $contentId, + 'pathString' => '/1/2/69', + ] + ) + ) + ); + + $this->locationGateway + ->expects($this->once()) + ->method('removeElementFromTrash') + ->with($trashItemId); + + $this->locationGateway + ->expects($this->once()) + ->method('countLocationsByContentId') + ->with($contentId) + ->will($this->returnValue(1)); + + $this->contentHandler + ->expects($this->never()) + ->method('deleteContent'); + + $trashItemDeleteResult = $handler->deleteTrashItem($trashItemId); + + $this->assertInstanceOf(TrashItemDeleteResult::class, $trashItemDeleteResult); + $this->assertEquals($trashItemId, $trashItemDeleteResult->trashItemId); + $this->assertEquals($contentId, $trashItemDeleteResult->contentId); + $this->assertFalse($trashItemDeleteResult->contentRemoved); + } + + public function testFindTrashItemsWhenEmpty() + { + $handler = $this->getTrashHandler(); + + $this->locationGateway + ->expects($this->once()) + ->method('countTrashed') + ->willReturn(0); + + $this->locationGateway + ->expects($this->never()) + ->method('listTrashed'); + + $this->locationMapper + ->expects($this->never()) + ->method($this->anything()); + + $trashResult = $handler->findTrashItems(); + + $this->assertInstanceOf(TrashResult::class, $trashResult); + $this->assertEquals(0, $trashResult->totalCount); + $this->assertIsArray($trashResult->items); + $this->assertEmpty($trashResult->items); + $this->assertIsIterable($trashResult); + $this->assertCount(0, $trashResult);// Can't assert as empty, however we can count it. + } + + public function testFindTrashItemsWithLimits() + { + $handler = $this->getTrashHandler(); + + $this->locationGateway + ->expects($this->once()) + ->method('countTrashed') + ->willReturn(2); + + $this->locationGateway + ->expects($this->once()) + ->method('listTrashed') + ->with(1, 1, null) + ->willReturn([['fake data']]); + + $this->locationMapper + ->expects($this->once()) + ->method('createLocationFromRow') + ->with(['fake data']) + ->willReturn(new \stdClass()); + + $trashResult = $handler->findTrashItems(null, 1, 1); + + $this->assertInstanceOf(TrashResult::class, $trashResult); + $this->assertEquals(2, $trashResult->totalCount); + $this->assertIsArray($trashResult->items); + $this->assertCount(1, $trashResult->items); + $this->assertIsIterable($trashResult); + $this->assertCount(1, $trashResult); + } +} + +class_alias(TrashHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Location\TrashHandlerTest'); diff --git a/tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php b/tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php new file mode 100644 index 0000000000..6b971543cc --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/LocationHandlerTest.php @@ -0,0 +1,712 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content; + +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\Location\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Location\UpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group as ObjectStateGroup; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Core\Persistence\Legacy\Content\Handler as ContentHandler; +use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Location\Handler; +use Ibexa\Core\Persistence\Legacy\Content\Location\Handler as LocationHandler; +use Ibexa\Core\Persistence\Legacy\Content\Location\Mapper; +use Ibexa\Core\Persistence\Legacy\Content\ObjectState\Handler as ObjectStateHandler; +use Ibexa\Core\Persistence\Legacy\Content\TreeHandler; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Location\Handler + */ +class LocationHandlerTest extends TestCase +{ + /** + * Mocked location gateway instance. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Location\Gateway + */ + protected $locationGateway; + + /** + * Mocked location mapper instance. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Location\Mapper + */ + protected $locationMapper; + + /** + * Mocked content handler instance. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Handler + */ + protected $contentHandler; + + /** + * Mocked object state handler instance. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\ObjectState\Handler|\PHPUnit\Framework\MockObject\MockObject + */ + protected $objectStateHandler; + + /** + * Mocked Tree handler instance. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\TreeHandler|\PHPUnit\Framework\MockObject\MockObject + */ + protected $treeHandler; + + protected function setUp(): void + { + parent::setUp(); + + $this->locationGateway = $this->createMock(Gateway::class); + $this->locationMapper = $this->createMock(Mapper::class); + $this->treeHandler = $this->createMock(TreeHandler::class); + $this->contentHandler = $this->createMock(ContentHandler::class); + } + + protected function getLocationHandler() + { + return new Handler( + $this->locationGateway, + $this->locationMapper, + $this->contentHandler, + $this->createMock(ObjectStateHandler::class), + $this->treeHandler + ); + } + + public function testLoadLocation() + { + $handler = $this->getLocationHandler(); + + $this->treeHandler + ->expects($this->once()) + ->method('loadLocation') + ->with(77) + ->willReturn(new Location()); + + $location = $handler->load(77); + + self::assertInstanceOf(Location::class, $location); + } + + public function testLoadLocationSubtree() + { + $this->locationGateway + ->expects($this->once()) + ->method('getSubtreeContent') + ->with(77, true) + ->will( + $this->returnValue( + [ + [77 => 100], + [78 => 101], + ] + ) + ); + + $this->assertCount(2, $this->getLocationHandler()->loadSubtreeIds(77)); + } + + public function testLoadLocationByRemoteId() + { + $handler = $this->getLocationHandler(); + + $this->locationGateway + ->expects($this->once()) + ->method('getBasicNodeDataByRemoteId') + ->with('abc123') + ->willReturn( + [ + 'node_id' => 77, + ] + ); + + $this->locationMapper + ->expects($this->once()) + ->method('createLocationFromRow') + ->with(['node_id' => 77]) + ->willReturn(new Location()); + + $location = $handler->loadByRemoteId('abc123'); + + self::assertInstanceOf(Location::class, $location); + } + + public function testLoadLocationsByContent() + { + $handler = $this->getLocationHandler(); + + $this->locationGateway + ->expects($this->once()) + ->method('loadLocationDataByContent') + ->with(23, 42) + ->will( + $this->returnValue( + [] + ) + ); + + $this->locationMapper + ->expects($this->once()) + ->method('createLocationsFromRows') + ->with([]) + ->will($this->returnValue(['a', 'b'])); + + $locations = $handler->loadLocationsByContent(23, 42); + + $this->assertIsArray($locations); + } + + public function loadParentLocationsForDraftContent() + { + $handler = $this->getLocationHandler(); + + $this->locationGateway + ->expects($this->once()) + ->method('loadParentLocationsDataForDraftContent') + ->with(23) + ->will( + $this->returnValue( + [] + ) + ); + + $this->locationMapper + ->expects($this->once()) + ->method('createLocationsFromRows') + ->with([]) + ->will($this->returnValue(['a', 'b'])); + + $locations = $handler->loadParentLocationsForDraftContent(23); + + $this->assertIsArray($locations); + } + + public function testMoveSubtree() + { + $handler = $this->getLocationHandler(); + + $sourceData = [ + 'node_id' => 69, + 'path_string' => '/1/2/69/', + 'parent_node_id' => 2, + 'contentobject_id' => 67, + ]; + $this->locationGateway + ->expects($this->at(0)) + ->method('getBasicNodeData') + ->with(69) + ->will($this->returnValue($sourceData)); + + $destinationData = [ + 'node_id' => 77, + 'path_string' => '/1/2/77/', + 'contentobject_id' => 68, + ]; + $this->locationGateway + ->expects($this->at(1)) + ->method('getBasicNodeData') + ->with(77) + ->will($this->returnValue($destinationData)); + + $this->locationGateway + ->expects($this->once()) + ->method('moveSubtreeNodes') + ->with($sourceData, $destinationData); + + $this->locationGateway + ->expects($this->once()) + ->method('updateNodeAssignment') + ->with(67, 2, 77, 5); + + $this->treeHandler + ->expects($this->at(0)) + ->method('loadLocation') + ->with($sourceData['node_id']) + ->will($this->returnValue( + new Location( + [ + 'id' => $sourceData['node_id'], + 'contentId' => $sourceData['contentobject_id'], + ] + ) + )); + + $this->treeHandler + ->expects($this->at(1)) + ->method('loadLocation') + ->with($destinationData['node_id']) + ->will($this->returnValue(new Location(['contentId' => $destinationData['contentobject_id']]))); + + $this->contentHandler + ->expects($this->at(0)) + ->method('loadContentInfo') + ->with($destinationData['contentobject_id']) + ->will($this->returnValue(new ContentInfo(['sectionId' => 12345]))); + + $this->contentHandler + ->expects($this->at(1)) + ->method('loadContentInfo') + ->with($sourceData['contentobject_id']) + ->will($this->returnValue(new ContentInfo(['mainLocationId' => 69]))); + + $this->treeHandler + ->expects($this->once()) + ->method('setSectionForSubtree') + ->with(69, 12345); + + $handler->move(69, 77); + } + + public function testHideUpdateHidden() + { + $handler = $this->getLocationHandler(); + + $this->locationGateway + ->expects($this->at(0)) + ->method('getBasicNodeData') + ->with(69) + ->will( + $this->returnValue( + [ + 'node_id' => 69, + 'path_string' => '/1/2/69/', + 'contentobject_id' => 67, + ] + ) + ); + + $this->locationGateway + ->expects($this->once()) + ->method('hideSubtree') + ->with('/1/2/69/'); + + $handler->hide(69); + } + + /** + * @depends testHideUpdateHidden + */ + public function testHideUnhideUpdateHidden() + { + $handler = $this->getLocationHandler(); + + $this->locationGateway + ->expects($this->at(0)) + ->method('getBasicNodeData') + ->with(69) + ->will( + $this->returnValue( + [ + 'node_id' => 69, + 'path_string' => '/1/2/69/', + 'contentobject_id' => 67, + ] + ) + ); + + $this->locationGateway + ->expects($this->once()) + ->method('unhideSubtree') + ->with('/1/2/69/'); + + $handler->unhide(69); + } + + public function testSwapLocations() + { + $handler = $this->getLocationHandler(); + + $this->locationGateway + ->expects($this->once()) + ->method('swap') + ->with(70, 78); + + $handler->swap(70, 78); + } + + public function testCreateLocation() + { + $handler = $this->getLocationHandler(); + + $createStruct = new CreateStruct(); + $createStruct->parentId = 77; + $spiLocation = new Location(); + $spiLocation->id = 78; + $spiLocation->parentId = 77; + $spiLocation->pathString = '/1/2/77/78/'; + + $this->locationGateway + ->expects($this->once()) + ->method('getBasicNodeData') + ->with(77) + ->will( + $this->returnValue( + $parentInfo = [ + 'node_id' => 77, + 'path_string' => '/1/2/77/', + ] + ) + ); + + $this->locationGateway + ->expects($this->once()) + ->method('create') + ->with($createStruct, $parentInfo) + ->will($this->returnValue($spiLocation)); + + $this->locationGateway + ->expects($this->once()) + ->method('createNodeAssignment') + ->with($createStruct, 77, 2); + + $handler->create($createStruct); + } + + public function testUpdateLocation() + { + $handler = $this->getLocationHandler(); + + $updateStruct = new UpdateStruct(); + $updateStruct->priority = 77; + + $this->locationGateway + ->expects($this->once()) + ->method('update') + ->with($updateStruct, 23); + + $handler->update($updateStruct, 23); + } + + public function testSetSectionForSubtree() + { + $handler = $this->getLocationHandler(); + + $this->treeHandler + ->expects($this->once()) + ->method('setSectionForSubtree') + ->with(69, 3); + + $handler->setSectionForSubtree(69, 3); + } + + public function testMarkSubtreeModified() + { + $handler = $this->getLocationHandler(); + + $this->locationGateway + ->expects($this->at(0)) + ->method('getBasicNodeData') + ->with(69) + ->will( + $this->returnValue( + [ + 'node_id' => 69, + 'path_string' => '/1/2/69/', + 'contentobject_id' => 67, + ] + ) + ); + + $this->locationGateway + ->expects($this->at(1)) + ->method('updateSubtreeModificationTime') + ->with('/1/2/69/'); + + $handler->markSubtreeModified(69); + } + + public function testChangeMainLocation() + { + $handler = $this->getLocationHandler(); + + $this->treeHandler + ->expects($this->once()) + ->method('changeMainLocation') + ->with(12, 34); + + $handler->changeMainLocation(12, 34); + } + + /** + * Test for the removeSubtree() method. + */ + public function testRemoveSubtree() + { + $handler = $this->getLocationHandler(); + + $this->treeHandler + ->expects($this->once()) + ->method('removeSubtree') + ->with(42); + + $handler->removeSubtree(42); + } + + /** + * Test for the copySubtree() method. + */ + public function testCopySubtree() + { + $handler = $this->getPartlyMockedHandler( + [ + 'load', + 'changeMainLocation', + 'setSectionForSubtree', + 'create', + ] + ); + $subtreeContentRows = [ + ['node_id' => 10, 'main_node_id' => 1, 'parent_node_id' => 3, 'contentobject_id' => 21, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_10', 'sort_field' => 2, 'sort_order' => 1], + ['node_id' => 11, 'main_node_id' => 11, 'parent_node_id' => 10, 'contentobject_id' => 211, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_11', 'sort_field' => 2, 'sort_order' => 1], + ['node_id' => 12, 'main_node_id' => 15, 'parent_node_id' => 10, 'contentobject_id' => 215, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_12', 'sort_field' => 2, 'sort_order' => 1], + ['node_id' => 13, 'main_node_id' => 2, 'parent_node_id' => 10, 'contentobject_id' => 22, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_13', 'sort_field' => 2, 'sort_order' => 1], + ['node_id' => 14, 'main_node_id' => 11, 'parent_node_id' => 13, 'contentobject_id' => 211, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_14', 'sort_field' => 2, 'sort_order' => 1], + ['node_id' => 15, 'main_node_id' => 15, 'parent_node_id' => 13, 'contentobject_id' => 215, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_15', 'sort_field' => 2, 'sort_order' => 1], + ['node_id' => 16, 'main_node_id' => 16, 'parent_node_id' => 15, 'contentobject_id' => 216, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 0, 'priority' => 0, 'path_identification_string' => 'test_16', 'sort_field' => 2, 'sort_order' => 1], + ]; + $destinationData = ['node_id' => 5, 'main_node_id' => 5, 'parent_node_id' => 4, 'contentobject_id' => 200, 'contentobject_version' => 1, 'is_hidden' => 0, 'is_invisible' => 1, 'path_identification_string' => 'test_destination']; + $mainLocationsMap = [true, true, true, true, 1011, 1012, true]; + $updateMainLocationsMap = [1215 => 1015]; + $offset = 1000; + + $this->locationGateway + ->expects($this->once()) + ->method('getSubtreeContent') + ->with($subtreeContentRows[0]['node_id']) + ->will($this->returnValue($subtreeContentRows)); + $this->locationGateway + ->expects($this->once()) + ->method('getBasicNodeData') + ->with($destinationData['node_id']) + ->will($this->returnValue($destinationData)); + + $objectStateHandlerCall = 0; + $this->objectStateHandler->expects($this->at($objectStateHandlerCall++)) + ->method('loadAllGroups') + ->will( + $this->returnValue( + [ + new ObjectStateGroup(['id' => 10]), + new ObjectStateGroup(['id' => 20]), + ] + ) + ); + $this->objectStateHandler->expects($this->at($objectStateHandlerCall++)) + ->method('loadObjectStates') + ->with($this->equalTo(10)) + ->will( + $this->returnValue( + [ + new ObjectState(['id' => 11, 'groupId' => 10]), + new ObjectState(['id' => 12, 'groupId' => 10]), + ] + ) + ); + $this->objectStateHandler->expects($this->at($objectStateHandlerCall++)) + ->method('loadObjectStates') + ->with($this->equalTo(20)) + ->will( + $this->returnValue( + [ + new ObjectState(['id' => 21, 'groupId' => 20]), + new ObjectState(['id' => 22, 'groupId' => 20]), + ] + ) + ); + $defaultObjectStates = [ + new ObjectState(['id' => 11, 'groupId' => 10]), + new ObjectState(['id' => 21, 'groupId' => 20]), + ]; + + $contentIds = array_values( + array_unique( + array_column($subtreeContentRows, 'contentobject_id') + ) + ); + foreach ($contentIds as $index => $contentId) { + $this->contentHandler + ->expects($this->at($index * 2)) + ->method('copy') + ->with($contentId, 1) + ->will( + $this->returnValue( + new Content( + [ + 'versionInfo' => new VersionInfo( + [ + 'contentInfo' => new ContentInfo( + [ + 'id' => $contentId + $offset, + 'currentVersionNo' => 1, + ] + ), + ] + ), + ] + ) + ) + ); + + foreach ($defaultObjectStates as $objectState) { + $this->objectStateHandler->expects($this->at($objectStateHandlerCall++)) + ->method('setContentState') + ->with( + $contentId + $offset, + $objectState->groupId, + $objectState->id + ); + } + + $this->contentHandler + ->expects($this->at($index * 2 + 1)) + ->method('publish') + ->with( + $contentId + $offset, + 1, + $this->isInstanceOf(Content\MetadataUpdateStruct::class) + ) + ->will( + $this->returnValue( + new Content( + [ + 'versionInfo' => new VersionInfo( + [ + 'contentInfo' => new ContentInfo( + [ + 'id' => ($contentId + $offset), + ] + ), + ] + ), + ] + ) + ) + ); + } + $lastContentHandlerIndex = $index * 2 + 1; + + $pathStrings = [$destinationData['node_id'] => $destinationData['path_identification_string']]; + foreach ($subtreeContentRows as $index => $row) { + $mapper = new Mapper(); + $createStruct = $mapper->getLocationCreateStruct($row); + $this->locationMapper + ->expects($this->at($index)) + ->method('getLocationCreateStruct') + ->with($row) + ->will($this->returnValue($createStruct)); + + $createStruct = clone $createStruct; + $createStruct->contentId = $createStruct->contentId + $offset; + $createStruct->parentId = $index === 0 ? $destinationData['node_id'] : $createStruct->parentId + $offset; + $createStruct->invisible = true; + $createStruct->mainLocationId = $mainLocationsMap[$index]; + $createStruct->pathIdentificationString = $pathStrings[$createStruct->parentId] . '/' . $row['path_identification_string']; + $pathStrings[$row['node_id'] + $offset] = $createStruct->pathIdentificationString; + $handler + ->expects($this->at($index)) + ->method('create') + ->with($createStruct) + ->will( + $this->returnValue( + new Location( + [ + 'id' => $row['node_id'] + $offset, + 'contentId' => $row['contentobject_id'], + 'hidden' => false, + 'invisible' => true, + 'pathIdentificationString' => $createStruct->pathIdentificationString, + ] + ) + ) + ); + } + + foreach ($updateMainLocationsMap as $contentId => $locationId) { + $handler + ->expects($this->any()) + ->method('changeMainLocation') + ->with($contentId, $locationId); + } + + $handler + ->expects($this->once()) + ->method('load') + ->with($destinationData['node_id']) + ->will($this->returnValue(new Location(['contentId' => $destinationData['contentobject_id']]))); + + $this->contentHandler + ->expects($this->at($lastContentHandlerIndex + 1)) + ->method('loadContentInfo') + ->with($destinationData['contentobject_id']) + ->will($this->returnValue(new ContentInfo(['sectionId' => 12345]))); + + $this->contentHandler + ->expects($this->at($lastContentHandlerIndex + 2)) + ->method('loadContentInfo') + ->with(21) + ->will($this->returnValue(new ContentInfo(['mainLocationId' => 1010]))); + + $handler + ->expects($this->once()) + ->method('setSectionForSubtree') + ->with($subtreeContentRows[0]['node_id'] + $offset, 12345); + + $handler->copySubtree( + $subtreeContentRows[0]['node_id'], + $destinationData['node_id'] + ); + } + + /** + * @covers \Ibexa\Contracts\Core\Persistence\Content\Location\Handler::countLocationsByContent + */ + public function testCountLocationsByContent(): void + { + $handler = $this->getLocationHandler(); + + $contentId = 41; + + $this->locationGateway + ->expects(self::once()) + ->method('countLocationsByContentId') + ->with($contentId); + + $handler->countLocationsByContent($contentId); + } + + /** + * Returns the handler to test with $methods mocked. + * + * @param string[] $methods + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Location\Handler + */ + protected function getPartlyMockedHandler(array $methods) + { + return $this->getMockBuilder(LocationHandler::class) + ->setMethods($methods) + ->setConstructorArgs( + [ + $this->locationGateway = $this->createMock(Gateway::class), + $this->locationMapper = $this->createMock(Mapper::class), + $this->contentHandler = $this->createMock(ContentHandler::class), + $this->objectStateHandler = $this->createMock(ObjectStateHandler::class), + $this->treeHandler = $this->createMock(TreeHandler::class), + ] + ) + ->getMock(); + } +} + +class_alias(LocationHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\LocationHandlerTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Mapper/ResolveVirtualFieldSubscriberTest.php b/tests/lib/Persistence/Legacy/Content/Mapper/ResolveVirtualFieldSubscriberTest.php index 67e50f6496..38beb6aaa7 100644 --- a/tests/lib/Persistence/Legacy/Content/Mapper/ResolveVirtualFieldSubscriberTest.php +++ b/tests/lib/Persistence/Legacy/Content/Mapper/ResolveVirtualFieldSubscriberTest.php @@ -8,19 +8,19 @@ namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Mapper; -use eZ\Publish\Core\FieldType\NullStorage; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry; -use eZ\Publish\SPI\FieldType\FieldStorage; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; use Ibexa\Contracts\Core\Event\Mapper\ResolveMissingFieldEvent; use Ibexa\Contracts\Core\FieldType\DefaultDataFieldStorage; +use Ibexa\Contracts\Core\FieldType\FieldStorage; +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Core\FieldType\NullStorage; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; +use Ibexa\Core\Persistence\Legacy\Content\Gateway as ContentGateway; use Ibexa\Core\Persistence\Legacy\Content\Mapper\ResolveVirtualFieldSubscriber; +use Ibexa\Core\Persistence\Legacy\Content\StorageRegistry; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcher; diff --git a/tests/lib/Persistence/Legacy/Content/MapperTest.php b/tests/lib/Persistence/Legacy/Content/MapperTest.php new file mode 100644 index 0000000000..1caaac1715 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/MapperTest.php @@ -0,0 +1,792 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content; + +use function count; +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\Language; +use Ibexa\Contracts\Core\Persistence\Content\Location\CreateStruct as LocationCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Relation as SPIRelation; +use Ibexa\Contracts\Core\Persistence\Content\Relation\CreateStruct as RelationCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation as RelationValue; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry as Registry; +use Ibexa\Core\Persistence\Legacy\Content\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Mapper; +use Ibexa\Core\Persistence\Legacy\Content\Mapper\ResolveVirtualFieldSubscriber; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use Ibexa\Core\Persistence\Legacy\Content\StorageRegistry; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Mapper + */ +class MapperTest extends LanguageAwareTestCase +{ + /** + * Value converter registry mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry + */ + protected $valueConverterRegistryMock; + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\CreateStruct + */ + protected function getCreateStructFixture() + { + $struct = new CreateStruct(); + + $struct->name = 'Content name'; + $struct->typeId = 23; + $struct->sectionId = 42; + $struct->ownerId = 13; + $struct->initialLanguageId = 2; + $struct->locations = [ + new LocationCreateStruct( + ['parentId' => 2] + ), + new LocationCreateStruct( + ['parentId' => 3] + ), + new LocationCreateStruct( + ['parentId' => 4] + ), + ]; + $struct->fields = [new Field()]; + + return $struct; + } + + public function testCreateVersionInfoForContent() + { + $content = $this->getFullContentFixture(); + $time = time(); + + $mapper = $this->getMapper(); + + $versionInfo = $mapper->createVersionInfoForContent( + $content, + 1, + 14 + ); + + $this->assertPropertiesCorrect( + [ + 'id' => null, + 'versionNo' => 1, + 'creatorId' => 14, + 'status' => 0, + 'initialLanguageCode' => 'eng-GB', + 'languageCodes' => ['eng-GB'], + ], + $versionInfo + ); + self::assertGreaterThanOrEqual($time, $versionInfo->creationDate); + self::assertGreaterThanOrEqual($time, $versionInfo->modificationDate); + } + + /** + * Returns a Content fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content + */ + protected function getFullContentFixture() + { + $content = new Content(); + + $content->fields = [ + new Field(['languageCode' => 'eng-GB']), + ]; + $content->versionInfo = new VersionInfo( + [ + 'versionNo' => 1, + 'initialLanguageCode' => 'eng-GB', + 'languageCodes' => ['eng-GB'], + ] + ); + + $content->versionInfo->contentInfo = new ContentInfo(); + $content->versionInfo->contentInfo->id = 2342; + $content->versionInfo->contentInfo->contentTypeId = 23; + $content->versionInfo->contentInfo->sectionId = 42; + $content->versionInfo->contentInfo->ownerId = 13; + + return $content; + } + + public function testConvertToStorageValue() + { + $convMock = $this->createMock(Converter::class); + $convMock->expects($this->once()) + ->method('toStorageValue') + ->with( + $this->isInstanceOf( + FieldValue::class + ), + $this->isInstanceOf( + StorageFieldValue::class + ) + )->will($this->returnValue(new StorageFieldValue())); + + $reg = new Registry(['some-type' => $convMock]); + + $field = new Field(); + $field->type = 'some-type'; + $field->value = new FieldValue(); + + $mapper = new Mapper( + $reg, + $this->getLanguageHandler(), + $this->getContentTypeHandler(), + $this->getEventDispatcher(), + ); + $res = $mapper->convertToStorageValue($field); + + self::assertInstanceOf( + StorageFieldValue::class, + $res + ); + } + + public function testExtractContentFromRows() + { + $rowsFixture = $this->getContentExtractFixture(); + $nameRowsFixture = $this->getNamesExtractFixture(); + + $contentType = $this->getContentTypeFromRows($rowsFixture); + + $contentTypeHandlerMock = $this->getContentTypeHandler(); + $contentTypeHandlerMock->method('load')->willReturn($contentType); + + $reg = $this->getFieldRegistry([ + 'ezauthor', + 'ezstring', + 'ezboolean', + 'ezimage', + 'ezdatetime', + 'ezkeyword', + ], count($rowsFixture) - 1); + + $mapper = new Mapper( + $reg, + $this->getLanguageHandler(), + $contentTypeHandlerMock, + $this->getEventDispatcher() + ); + $result = $mapper->extractContentFromRows($rowsFixture, $nameRowsFixture); + + $expected = [$this->getContentExtractReference()]; + + self::assertEquals( + $expected, + $result + ); + } + + public function testExtractContentFromRowsWithNewFieldDefinitions(): void + { + $rowsFixture = $this->getContentExtractFixture(); + $nameRowsFixture = $this->getNamesExtractFixture(); + + $contentType = $this->getContentTypeFromRows($rowsFixture); + $contentType->fieldDefinitions[] = new Content\Type\FieldDefinition([ + 'fieldType' => 'eznumber', + ]); + + $contentTypeHandlerMock = $this->getContentTypeHandler(); + $contentTypeHandlerMock->method('load')->willReturn($contentType); + + $reg = $this->getFieldRegistry([ + 'ezauthor', + 'ezstring', + 'ezboolean', + 'ezimage', + 'ezdatetime', + 'ezkeyword', + 'eznumber', + ], count($rowsFixture) - 1); + + $mapper = new Mapper( + $reg, + $this->getLanguageHandler(), + $contentTypeHandlerMock, + $this->getEventDispatcher() + ); + $result = $mapper->extractContentFromRows($rowsFixture, $nameRowsFixture); + + $expectedContent = $this->getContentExtractReference(); + $expectedContent->fields[] = new Field([ + 'type' => 'eznumber', + 'languageCode' => 'eng-US', + 'value' => new FieldValue(), + 'versionNo' => 2, + ]); + + self::assertEquals( + [ + $expectedContent, + ], + $result + ); + } + + public function testExtractContentFromRowsWithRemovedFieldDefinitions(): void + { + $rowsFixture = $this->getContentExtractFixture(); + $nameRowsFixture = $this->getNamesExtractFixture(); + + $contentType = $this->getContentTypeFromRows($rowsFixture); + $contentType->fieldDefinitions = array_filter( + $contentType->fieldDefinitions, + static function (Content\Type\FieldDefinition $fieldDefinition): bool { + // ref. fixtures, ezauthor + return $fieldDefinition->id !== 185; + } + ); + + $contentTypeHandlerMock = $this->getContentTypeHandler(); + $contentTypeHandlerMock->method('load')->willReturn($contentType); + + $reg = $this->getFieldRegistry([ + 'ezstring', + 'ezboolean', + 'ezimage', + 'ezdatetime', + 'ezkeyword', + ], count($rowsFixture) - 2); + + $mapper = new Mapper( + $reg, + $this->getLanguageHandler(), + $contentTypeHandlerMock, + $this->getEventDispatcher() + ); + $result = $mapper->extractContentFromRows($rowsFixture, $nameRowsFixture); + + $expectedContent = $this->getContentExtractReference(); + $expectedContent->fields = array_values( + array_filter($expectedContent->fields, static function (Field $field): bool { + return $field->fieldDefinitionId !== 185; + }) + ); + + self::assertEquals( + [ + $expectedContent, + ], + $result + ); + } + + public function testExtractContentFromRowsMultipleVersions() + { + $reg = $this->getFieldRegistry([ + 'ezstring', + 'ezdatetime', + ]); + + $rowsFixture = $this->getMultipleVersionsExtractFixture(); + $nameRowsFixture = $this->getMultipleVersionsNamesExtractFixture(); + + $contentType = $this->getContentTypeFromRows($rowsFixture); + + $contentTypeHandlerMock = $this->getContentTypeHandler(); + $contentTypeHandlerMock->method('load')->willReturn($contentType); + + $mapper = new Mapper( + $reg, + $this->getLanguageHandler(), + $contentTypeHandlerMock, + $this->getEventDispatcher() + ); + $result = $mapper->extractContentFromRows($rowsFixture, $nameRowsFixture); + + self::assertCount( + 2, + $result + ); + + self::assertEquals( + 11, + $result[0]->versionInfo->contentInfo->id + ); + self::assertEquals( + 11, + $result[1]->versionInfo->contentInfo->id + ); + + self::assertEquals( + 1, + $result[0]->versionInfo->versionNo + ); + self::assertEquals( + 2, + $result[1]->versionInfo->versionNo + ); + } + + /** + * @param string[] $fieldTypeIdentifiers + */ + private function getFieldRegistry( + array $fieldTypeIdentifiers = [], + ?int $expectedConverterCalls = null + ): Registry { + $converterMock = $this->createMock(Converter::class); + $converterMock->expects( + $expectedConverterCalls === null + ? self::any() + : self::exactly($expectedConverterCalls) + ) + ->method('toFieldValue') + ->willReturn(new FieldValue()); + + $converters = []; + foreach ($fieldTypeIdentifiers as $fieldTypeIdentifier) { + $converters[$fieldTypeIdentifier] = $converterMock; + } + + return new Registry($converters); + } + + public function testCreateCreateStructFromContent() + { + $time = time(); + $mapper = $this->getMapper(); + + $content = $this->getContentExtractReference(); + + $struct = $mapper->createCreateStructFromContent($content); + + self::assertInstanceOf(CreateStruct::class, $struct); + + return [ + 'original' => $content, + 'result' => $struct, + 'time' => $time, + ]; + + // parentLocations + // fields + } + + /** + * @depends testCreateCreateStructFromContent + */ + public function testCreateCreateStructFromContentBasicProperties($data) + { + $content = $data['original']; + $struct = $data['result']; + $time = $data['time']; + $this->assertStructsEqual( + $content->versionInfo->contentInfo, + $struct, + ['sectionId', 'ownerId'] + ); + self::assertNotEquals($content->versionInfo->contentInfo->remoteId, $struct->remoteId); + self::assertSame($content->versionInfo->contentInfo->contentTypeId, $struct->typeId); + self::assertSame(2, $struct->initialLanguageId); + self::assertSame($content->versionInfo->contentInfo->alwaysAvailable, $struct->alwaysAvailable); + self::assertGreaterThanOrEqual($time, $struct->modified); + } + + /** + * @depends testCreateCreateStructFromContent + */ + public function testCreateCreateStructFromContentParentLocationsEmpty($data) + { + self::assertEquals( + [], + $data['result']->locations + ); + } + + /** + * @depends testCreateCreateStructFromContent + */ + public function testCreateCreateStructFromContentFieldCount($data) + { + self::assertEquals( + count($data['original']->fields), + count($data['result']->fields) + ); + } + + /** + * @depends testCreateCreateStructFromContent + */ + public function testCreateCreateStructFromContentFieldsNoId($data) + { + foreach ($data['result']->fields as $field) { + self::assertNull($field->id); + } + } + + public function testExtractRelationsFromRows() + { + $mapper = $this->getMapper(); + + $rows = $this->getRelationExtractFixture(); + + $res = $mapper->extractRelationsFromRows($rows); + + self::assertEquals( + $this->getRelationExtractReference(), + $res + ); + } + + public function testCreateCreateStructFromContentWithPreserveOriginalLanguage() + { + $time = time(); + $mapper = $this->getMapper(); + + $content = $this->getContentExtractReference(); + $content->versionInfo->contentInfo->mainLanguageCode = 'eng-GB'; + + $struct = $mapper->createCreateStructFromContent($content, true); + + self::assertInstanceOf(CreateStruct::class, $struct); + $this->assertStructsEqual($content->versionInfo->contentInfo, $struct, ['sectionId', 'ownerId']); + self::assertNotEquals($content->versionInfo->contentInfo->remoteId, $struct->remoteId); + self::assertSame($content->versionInfo->contentInfo->contentTypeId, $struct->typeId); + self::assertSame(2, $struct->initialLanguageId); + self::assertSame(4, $struct->mainLanguageId); + self::assertSame($content->versionInfo->contentInfo->alwaysAvailable, $struct->alwaysAvailable); + self::assertGreaterThanOrEqual($time, $struct->modified); + } + + /** + * @dataProvider extractContentInfoFromRowProvider + * + * @param array $fixtures + * @param string $prefix + */ + public function testExtractContentInfoFromRow(array $fixtures, $prefix) + { + $contentInfoReference = $this->getContentExtractReference()->versionInfo->contentInfo; + $mapper = new Mapper( + $this->getValueConverterRegistryMock(), + $this->getLanguageHandler(), + $this->getContentTypeHandler(), + $this->getEventDispatcher() + ); + self::assertEquals($contentInfoReference, $mapper->extractContentInfoFromRow($fixtures, $prefix)); + } + + /** + * Returns test data for {@link testExtractContentInfoFromRow()}. + * + * @return array + */ + public function extractContentInfoFromRowProvider() + { + $fixtures = $this->getContentExtractFixture(); + $fixturesNoPrefix = []; + foreach ($fixtures[0] as $key => $value) { + $keyNoPrefix = $key === 'ezcontentobject_tree_main_node_id' + ? $key + : str_replace('ezcontentobject_', '', $key); + $fixturesNoPrefix[$keyNoPrefix] = $value; + } + + return [ + [$fixtures[0], 'ezcontentobject_'], + [$fixturesNoPrefix, ''], + ]; + } + + public function testCreateRelationFromCreateStruct() + { + $struct = $this->getRelationCreateStructFixture(); + + $mapper = $this->getMapper(); + $relation = $mapper->createRelationFromCreateStruct($struct); + + self::assertInstanceOf(SPIRelation::class, $relation); + foreach ($struct as $property => $value) { + self::assertSame($value, $relation->$property); + } + } + + /** + * Returns test data for {@link testExtractVersionInfoFromRow()}. + * + * @return array + */ + public function extractVersionInfoFromRowProvider() + { + $fixturesAll = $this->getContentExtractFixture(); + $fixtures = $fixturesAll[0]; + $fixtures['ezcontentobject_version_names'] = [ + ['content_translation' => 'eng-US', 'name' => 'Something'], + ]; + $fixtures['ezcontentobject_version_languages'] = [2]; + $fixtures['ezcontentobject_version_initial_language_code'] = 'eng-US'; + $fixturesNoPrefix = []; + foreach ($fixtures as $key => $value) { + $keyNoPrefix = str_replace('ezcontentobject_version_', '', $key); + $fixturesNoPrefix[$keyNoPrefix] = $value; + } + + return [ + [$fixtures, 'ezcontentobject_version_'], + [$fixturesNoPrefix, ''], + ]; + } + + /** + * Returns a fixture of database rows for content extraction. + * + * Fixture is stored in _fixtures/extract_content_from_rows.php + * + * @return array + */ + protected function getContentExtractFixture() + { + return require __DIR__ . '/_fixtures/extract_content_from_rows.php'; + } + + /** + * Returns a fixture of database rows for content names extraction. + * + * Fixture is stored in _fixtures/extract_names_from_rows.php + * + * @return array + */ + protected function getNamesExtractFixture() + { + return require __DIR__ . '/_fixtures/extract_names_from_rows.php'; + } + + /** + * Returns a reference result for content extraction. + * + * Fixture is stored in _fixtures/extract_content_from_rows_result.php + * + * @return \Ibexa\Contracts\Core\Persistence\Content + */ + protected function getContentExtractReference() + { + return require __DIR__ . '/_fixtures/extract_content_from_rows_result.php'; + } + + /** + * Returns a fixture for mapping multiple versions of a content object. + * + * @return string[][] + */ + protected function getMultipleVersionsExtractFixture() + { + return require __DIR__ . '/_fixtures/extract_content_from_rows_multiple_versions.php'; + } + + /** + * Returns a fixture of database rows for content names extraction across multiple versions. + * + * Fixture is stored in _fixtures/extract_names_from_rows_multiple_versions.php + * + * @return array + */ + protected function getMultipleVersionsNamesExtractFixture() + { + return require __DIR__ . '/_fixtures/extract_names_from_rows_multiple_versions.php'; + } + + /** + * Returns a fixture of database rows for relations extraction. + * + * Fixture is stored in _fixtures/relations.php + * + * @return array + */ + protected function getRelationExtractFixture() + { + return require __DIR__ . '/_fixtures/relations_rows.php'; + } + + /** + * Returns a reference result for content extraction. + * + * Fixture is stored in _fixtures/relations_results.php + * + * @return \Ibexa\Contracts\Core\Persistence\Content + */ + protected function getRelationExtractReference() + { + return require __DIR__ . '/_fixtures/relations_results.php'; + } + + /** + * Returns a Mapper. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Mapper + */ + protected function getMapper($valueConverter = null) + { + return new Mapper( + $this->getValueConverterRegistryMock(), + $this->getLanguageHandler(), + $this->getContentTypeHandler(), + $this->getEventDispatcher() + ); + } + + /** + * Returns a FieldValue converter registry mock. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry + */ + protected function getValueConverterRegistryMock() + { + if (!isset($this->valueConverterRegistryMock)) { + $this->valueConverterRegistryMock = $this->getMockBuilder(Registry::class) + ->setMethods([]) + ->getMock(); + + $this->valueConverterRegistryMock + ->method('getConverter') + ->willReturn($this->createMock(Converter::class)); + } + + return $this->valueConverterRegistryMock; + } + + /** + * Returns a {@see \Ibexa\Contracts\Core\Persistence\Content\Relation\CreateStruct} fixture. + */ + protected function getRelationCreateStructFixture(): RelationCreateStruct + { + $struct = new RelationCreateStruct(); + + $struct->destinationContentId = 0; + $struct->sourceContentId = 0; + $struct->sourceContentVersionNo = 1; + $struct->sourceFieldDefinitionId = 1; + $struct->type = RelationValue::COMMON; + + return $struct; + } + + protected function getEventDispatcher(): EventDispatcherInterface + { + $eventDispatcher = new EventDispatcher(); + $eventDispatcher->addSubscriber( + new ResolveVirtualFieldSubscriber( + $this->getValueConverterRegistryMock(), + $this->createMock(StorageRegistry::class), + $this->createMock(Gateway::class) + ) + ); + + return $eventDispatcher; + } + + /** + * Returns a language handler mock. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Language\Handler + */ + protected function getLanguageHandler() + { + $languages = [ + 'eng-US' => new Language( + [ + 'id' => 2, + 'languageCode' => 'eng-US', + 'name' => 'US english', + ] + ), + 'eng-GB' => new Language( + [ + 'id' => 4, + 'languageCode' => 'eng-GB', + 'name' => 'British english', + ] + ), + ]; + + if (!isset($this->languageHandler)) { + $this->languageHandler = $this->createMock(Language\Handler::class); + $this->languageHandler->expects($this->any()) + ->method('load') + ->will( + $this->returnCallback( + static function ($id) use ($languages) { + foreach ($languages as $language) { + if ($language->id == $id) { + return $language; + } + } + } + ) + ); + $this->languageHandler->expects($this->any()) + ->method('loadByLanguageCode') + ->will( + $this->returnCallback( + static function ($languageCode) use ($languages) { + foreach ($languages as $language) { + if ($language->languageCode == $languageCode) { + return $language; + } + } + } + ) + ); + $this->languageHandler->expects($this->any()) + ->method('loadAll') + ->willReturn($languages); + } + + return $this->languageHandler; + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Handler|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getContentTypeHandler(): Content\Type\Handler + { + return $this->createMock(Content\Type\Handler::class); + } + + /** + * @param array<int, array<string, mixed>> $rows + */ + protected function getContentTypeFromRows(array $rows): Content\Type + { + $contentType = new Content\Type(); + $fieldDefinitions = []; + + foreach ($rows as $row) { + $fieldDefinitionId = $row['ezcontentobject_attribute_contentclassattribute_id']; + $fieldType = $row['ezcontentobject_attribute_data_type_string']; + + if (isset($fieldDefinitions[$fieldDefinitionId])) { + continue; + } + + $fieldDefinitions[$fieldDefinitionId] = new Content\Type\FieldDefinition([ + 'id' => $fieldDefinitionId, + 'fieldType' => $fieldType, + ]); + } + + $contentType->fieldDefinitions = array_values($fieldDefinitions); + + return $contentType; + } +} + +class_alias(MapperTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\MapperTest'); diff --git a/tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..1105684cad --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/ObjectState/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,522 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\ObjectState\Gateway; + +use Ibexa\Contracts\Core\Persistence\Content\ObjectState; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group; +use Ibexa\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase; +use Ibexa\Tests\Core\Persistence\Legacy\Content\LanguageAwareTestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase + */ +class DoctrineDatabaseTest extends LanguageAwareTestCase +{ + /** + * Database gateway to test. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\ObjectState\Gateway\DoctrineDatabase + */ + protected $databaseGateway; + + /** + * Language mask generator. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator + */ + protected $languageMaskGenerator; + + /** + * Inserts DB fixture. + */ + protected function setUp(): void + { + parent::setUp(); + + $this->insertDatabaseFixture( + __DIR__ . '/../../_fixtures/contentobjects.php' + ); + + $this->insertDatabaseFixture( + __DIR__ . '/../../_fixtures/objectstates.php' + ); + } + + public function testLoadObjectStateData() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->loadObjectStateData(1); + + $this->assertEquals( + [ + [ + 'ezcobj_state_default_language_id' => 2, + 'ezcobj_state_group_id' => 2, + 'ezcobj_state_id' => 1, + 'ezcobj_state_identifier' => 'not_locked', + 'ezcobj_state_language_mask' => 3, + 'ezcobj_state_priority' => 0, + 'ezcobj_state_language_description' => '', + 'ezcobj_state_language_language_id' => 3, + 'ezcobj_state_language_name' => 'Not locked', + ], + ], + $result + ); + } + + public function testLoadObjectStateDataByIdentifier() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->loadObjectStateDataByIdentifier('not_locked', 2); + + $this->assertEquals( + [ + [ + 'ezcobj_state_default_language_id' => 2, + 'ezcobj_state_group_id' => 2, + 'ezcobj_state_id' => 1, + 'ezcobj_state_identifier' => 'not_locked', + 'ezcobj_state_language_mask' => 3, + 'ezcobj_state_priority' => 0, + 'ezcobj_state_language_description' => '', + 'ezcobj_state_language_language_id' => 3, + 'ezcobj_state_language_name' => 'Not locked', + ], + ], + $result + ); + } + + public function testLoadObjectStateListData() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->loadObjectStateListData(2); + + $this->assertEquals( + [ + [ + [ + 'ezcobj_state_default_language_id' => 2, + 'ezcobj_state_group_id' => 2, + 'ezcobj_state_id' => 1, + 'ezcobj_state_identifier' => 'not_locked', + 'ezcobj_state_language_mask' => 3, + 'ezcobj_state_priority' => 0, + 'ezcobj_state_language_description' => '', + 'ezcobj_state_language_language_id' => 3, + 'ezcobj_state_language_name' => 'Not locked', + ], + ], + [ + [ + 'ezcobj_state_default_language_id' => 2, + 'ezcobj_state_group_id' => 2, + 'ezcobj_state_id' => 2, + 'ezcobj_state_identifier' => 'locked', + 'ezcobj_state_language_mask' => 3, + 'ezcobj_state_priority' => 1, + 'ezcobj_state_language_description' => '', + 'ezcobj_state_language_language_id' => 3, + 'ezcobj_state_language_name' => 'Locked', + ], + ], + ], + $result + ); + } + + public function testLoadObjectStateGroupData() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->loadObjectStateGroupData(2); + + $this->assertEquals( + [ + [ + 'ezcobj_state_group_default_language_id' => 2, + 'ezcobj_state_group_id' => 2, + 'ezcobj_state_group_identifier' => 'ez_lock', + 'ezcobj_state_group_language_mask' => 3, + 'ezcobj_state_group_language_description' => '', + 'ezcobj_state_group_language_language_id' => 3, + 'ezcobj_state_group_language_real_language_id' => 2, + 'ezcobj_state_group_language_name' => 'Lock', + ], + ], + $result + ); + } + + public function testLoadObjectStateGroupDataByIdentifier() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->loadObjectStateGroupDataByIdentifier('ez_lock'); + + $this->assertEquals( + [ + [ + 'ezcobj_state_group_default_language_id' => 2, + 'ezcobj_state_group_id' => 2, + 'ezcobj_state_group_identifier' => 'ez_lock', + 'ezcobj_state_group_language_mask' => 3, + 'ezcobj_state_group_language_description' => '', + 'ezcobj_state_group_language_language_id' => 3, + 'ezcobj_state_group_language_real_language_id' => 2, + 'ezcobj_state_group_language_name' => 'Lock', + ], + ], + $result + ); + } + + public function testLoadObjectStateGroupListData() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->loadObjectStateGroupListData(0, -1); + + $this->assertEquals( + [ + [ + [ + 'ezcobj_state_group_default_language_id' => 2, + 'ezcobj_state_group_id' => 2, + 'ezcobj_state_group_identifier' => 'ez_lock', + 'ezcobj_state_group_language_mask' => 3, + 'ezcobj_state_group_language_description' => '', + 'ezcobj_state_group_language_language_id' => 3, + 'ezcobj_state_group_language_real_language_id' => 2, + 'ezcobj_state_group_language_name' => 'Lock', + ], + ], + ], + $result + ); + } + + public function testInsertObjectState() + { + $gateway = $this->getDatabaseGateway(); + + $gateway->insertObjectState($this->getObjectStateFixture(), 2); + + $this->assertEquals( + [ + [ + 'ezcobj_state_default_language_id' => 4, + 'ezcobj_state_group_id' => 2, + // The new state should be added with state ID = 3 + 'ezcobj_state_id' => 3, + 'ezcobj_state_identifier' => 'test_state', + 'ezcobj_state_language_mask' => 5, + // The new state should have priority = 2 + 'ezcobj_state_priority' => 2, + 'ezcobj_state_language_description' => 'Test state description', + 'ezcobj_state_language_language_id' => 5, + 'ezcobj_state_language_name' => 'Test state', + ], + ], + // The new state should be added with state ID = 3 + $this->getDatabaseGateway()->loadObjectStateData(3) + ); + } + + public function testInsertObjectStateInEmptyGroup() + { + $gateway = $this->getDatabaseGateway(); + + $gateway->insertObjectStateGroup($this->getObjectStateGroupFixture()); + $gateway->insertObjectState($this->getObjectStateFixture(), 3); + + $this->assertEquals( + [ + [ + 'ezcobj_state_default_language_id' => 4, + // New group should be added with group ID = 3 + 'ezcobj_state_group_id' => 3, + // The new state should be added with state ID = 3 + 'ezcobj_state_id' => 3, + 'ezcobj_state_identifier' => 'test_state', + 'ezcobj_state_language_mask' => 5, + // The new state should have priority = 0 + 'ezcobj_state_priority' => 0, + 'ezcobj_state_language_description' => 'Test state description', + 'ezcobj_state_language_language_id' => 5, + 'ezcobj_state_language_name' => 'Test state', + ], + ], + // The new state should be added with state ID = 3 + $this->getDatabaseGateway()->loadObjectStateData(3) + ); + + $this->assertEquals( + // 185 is the number of objects in the fixture + 185, + $gateway->getContentCount(3) + ); + } + + public function testUpdateObjectState() + { + $gateway = $this->getDatabaseGateway(); + + $objectStateFixture = $this->getObjectStateFixture(); + $objectStateFixture->id = 1; + + $gateway->updateObjectState($objectStateFixture); + + $this->assertEquals( + [ + [ + 'ezcobj_state_default_language_id' => 4, + 'ezcobj_state_group_id' => 2, + 'ezcobj_state_id' => 1, + 'ezcobj_state_identifier' => 'test_state', + 'ezcobj_state_language_mask' => 5, + 'ezcobj_state_priority' => 0, + 'ezcobj_state_language_description' => 'Test state description', + 'ezcobj_state_language_language_id' => 5, + 'ezcobj_state_language_name' => 'Test state', + ], + ], + $this->getDatabaseGateway()->loadObjectStateData(1) + ); + } + + public function testDeleteObjectState() + { + $gateway = $this->getDatabaseGateway(); + + $gateway->deleteObjectState(1); + + $this->assertEquals( + [], + $this->getDatabaseGateway()->loadObjectStateData(1) + ); + } + + public function testUpdateObjectStateLinks() + { + $gateway = $this->getDatabaseGateway(); + + $gateway->updateObjectStateLinks(1, 2); + + self::assertSame(0, $gateway->getContentCount(1)); + self::assertSame(185, $gateway->getContentCount(2)); + } + + public function testDeleteObjectStateLinks() + { + $gateway = $this->getDatabaseGateway(); + + $gateway->deleteObjectStateLinks(1); + + self::assertSame(0, $gateway->getContentCount(1)); + } + + public function testInsertObjectStateGroup() + { + $gateway = $this->getDatabaseGateway(); + + $gateway->insertObjectStateGroup($this->getObjectStateGroupFixture()); + + $this->assertEquals( + [ + [ + 'ezcobj_state_group_default_language_id' => 4, + // The new state group should be added with state group ID = 3 + 'ezcobj_state_group_id' => 3, + 'ezcobj_state_group_identifier' => 'test_group', + 'ezcobj_state_group_language_mask' => 5, + 'ezcobj_state_group_language_description' => 'Test group description', + 'ezcobj_state_group_language_language_id' => 5, + 'ezcobj_state_group_language_real_language_id' => 4, + 'ezcobj_state_group_language_name' => 'Test group', + ], + ], + // The new state group should be added with state group ID = 3 + $this->getDatabaseGateway()->loadObjectStateGroupData(3) + ); + } + + public function testUpdateObjectStateGroup() + { + $gateway = $this->getDatabaseGateway(); + + $groupFixture = $this->getObjectStateGroupFixture(); + $groupFixture->id = 2; + + $gateway->updateObjectStateGroup($groupFixture); + + $this->assertEquals( + [ + [ + 'ezcobj_state_group_default_language_id' => 4, + 'ezcobj_state_group_id' => 2, + 'ezcobj_state_group_identifier' => 'test_group', + 'ezcobj_state_group_language_mask' => 5, + 'ezcobj_state_group_language_description' => 'Test group description', + 'ezcobj_state_group_language_language_id' => 5, + 'ezcobj_state_group_language_real_language_id' => 4, + 'ezcobj_state_group_language_name' => 'Test group', + ], + ], + $this->getDatabaseGateway()->loadObjectStateGroupData(2) + ); + } + + public function testDeleteObjectStateGroup() + { + $gateway = $this->getDatabaseGateway(); + + $gateway->deleteObjectStateGroup(2); + + $this->assertEquals( + [], + $this->getDatabaseGateway()->loadObjectStateGroupData(2) + ); + } + + public function testSetContentState() + { + $gateway = $this->getDatabaseGateway(); + + $gateway->setContentState(42, 2, 2); + + $this->assertQueryResult( + [ + [ + 'contentobject_id' => 42, + 'contentobject_state_id' => 2, + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('contentobject_id', 'contentobject_state_id') + ->from('ezcobj_state_link') + ->where('contentobject_id = 42') + ); + } + + public function testLoadObjectStateDataForContent() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->loadObjectStateDataForContent(42, 2); + + $this->assertEquals( + [ + [ + 'ezcobj_state_default_language_id' => 2, + 'ezcobj_state_group_id' => 2, + 'ezcobj_state_id' => 1, + 'ezcobj_state_identifier' => 'not_locked', + 'ezcobj_state_language_mask' => 3, + 'ezcobj_state_priority' => 0, + 'ezcobj_state_language_description' => '', + 'ezcobj_state_language_language_id' => 3, + 'ezcobj_state_language_name' => 'Not locked', + ], + ], + $result + ); + } + + public function testGetContentCount() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->getContentCount(1); + + // 185 is the number of objects in the fixture + $this->assertEquals(185, $result); + } + + public function testUpdateObjectStatePriority() + { + $gateway = $this->getDatabaseGateway(); + + $gateway->updateObjectStatePriority(1, 10); + + $objectStateData = $gateway->loadObjectStateData(1); + + $this->assertEquals( + [ + [ + 'ezcobj_state_default_language_id' => 2, + 'ezcobj_state_group_id' => 2, + 'ezcobj_state_id' => 1, + 'ezcobj_state_identifier' => 'not_locked', + 'ezcobj_state_language_mask' => 3, + 'ezcobj_state_priority' => 10, + 'ezcobj_state_language_description' => '', + 'ezcobj_state_language_language_id' => 3, + 'ezcobj_state_language_name' => 'Not locked', + ], + ], + $objectStateData + ); + } + + /** + * Returns an object state fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState + */ + protected function getObjectStateFixture() + { + $objectState = new ObjectState(); + $objectState->identifier = 'test_state'; + $objectState->defaultLanguage = 'eng-GB'; + $objectState->languageCodes = ['eng-GB']; + $objectState->name = ['eng-GB' => 'Test state']; + $objectState->description = ['eng-GB' => 'Test state description']; + + return $objectState; + } + + /** + * Returns an object state group fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group + */ + protected function getObjectStateGroupFixture() + { + $group = new Group(); + $group->identifier = 'test_group'; + $group->defaultLanguage = 'eng-GB'; + $group->languageCodes = ['eng-GB']; + $group->name = ['eng-GB' => 'Test group']; + $group->description = ['eng-GB' => 'Test group description']; + + return $group; + } + + /** + * Returns a ready to test DoctrineDatabase gateway. + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function getDatabaseGateway(): DoctrineDatabase + { + if (!isset($this->databaseGateway)) { + $this->databaseGateway = new DoctrineDatabase( + $this->getDatabaseConnection(), + $this->getLanguageMaskGenerator() + ); + } + + return $this->databaseGateway; + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\ObjectState\Gateway\DoctrineDatabaseTest'); diff --git a/tests/lib/Persistence/Legacy/Content/ObjectState/MapperTest.php b/tests/lib/Persistence/Legacy/Content/ObjectState/MapperTest.php new file mode 100644 index 0000000000..0ed86adc50 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/ObjectState/MapperTest.php @@ -0,0 +1,234 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\ObjectState; + +use Ibexa\Contracts\Core\Persistence\Content\ObjectState; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct; +use Ibexa\Core\Persistence\Legacy\Content\ObjectState\Mapper; +use Ibexa\Tests\Core\Persistence\Legacy\Content\LanguageAwareTestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\ObjectState\Mapper + */ +class MapperTest extends LanguageAwareTestCase +{ + public function testCreateObjectStateFromData() + { + $mapper = $this->getMapper(); + + $rows = $this->getObjectStateRowsFixture(); + + $result = $mapper->createObjectStateFromData($rows); + + $this->assertStructsEqual( + $this->getObjectStateFixture(), + $result, + ['identifier', 'defaultLanguage', 'languageCodes', 'name', 'description'] + ); + } + + public function testCreateObjectStateListFromData() + { + $mapper = $this->getMapper(); + + $rows = [$this->getObjectStateRowsFixture()]; + + $result = $mapper->createObjectStateListFromData($rows); + + $this->assertStructsEqual( + $this->getObjectStateFixture(), + $result[0], + ['identifier', 'defaultLanguage', 'languageCodes', 'name', 'description'] + ); + } + + public function testCreateObjectStateGroupFromData() + { + $mapper = $this->getMapper(); + + $rows = $this->getObjectStateGroupRowsFixture(); + + $result = $mapper->createObjectStateGroupFromData($rows); + + $this->assertStructsEqual( + $this->getObjectStateGroupFixture(), + $result, + ['identifier', 'defaultLanguage', 'languageCodes', 'name', 'description'] + ); + } + + public function testCreateObjectStateGroupListFromData() + { + $mapper = $this->getMapper(); + + $rows = [$this->getObjectStateGroupRowsFixture()]; + + $result = $mapper->createObjectStateGroupListFromData($rows); + + $this->assertStructsEqual( + $this->getObjectStateGroupFixture(), + $result[0], + ['identifier', 'defaultLanguage', 'languageCodes', 'name', 'description'] + ); + } + + public function testCreateObjectStateFromInputStruct() + { + $mapper = $this->getMapper(); + + $inputStruct = $this->getObjectStateInputStructFixture(); + + $result = $mapper->createObjectStateFromInputStruct($inputStruct); + + $this->assertStructsEqual( + $this->getObjectStateFixture(), + $result, + ['identifier', 'defaultLanguage', 'languageCodes', 'name', 'description'] + ); + } + + public function testCreateObjectStateGroupFromInputStruct() + { + $mapper = $this->getMapper(); + + $inputStruct = $this->getObjectStateGroupInputStructFixture(); + + $result = $mapper->createObjectStateGroupFromInputStruct($inputStruct); + + $this->assertStructsEqual( + $this->getObjectStateGroupFixture(), + $result, + ['identifier', 'defaultLanguage', 'languageCodes', 'name', 'description'] + ); + } + + /** + * Returns a Mapper. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\ObjectState\Mapper + */ + protected function getMapper() + { + return new Mapper( + $this->getLanguageHandler() + ); + } + + /** + * Returns an object state result rows fixture. + * + * @return array[][] + */ + protected function getObjectStateRowsFixture() + { + return [ + [ + 'ezcobj_state_default_language_id' => 2, + 'ezcobj_state_group_id' => 2, + 'ezcobj_state_id' => 1, + 'ezcobj_state_identifier' => 'not_locked', + 'ezcobj_state_language_mask' => 3, + 'ezcobj_state_priority' => 0, + 'ezcobj_state_language_description' => '', + 'ezcobj_state_language_language_id' => 3, + 'ezcobj_state_language_name' => 'Not locked', + ], + ]; + } + + /** + * Returns an object state group result rows fixture. + * + * @return array[][] + */ + protected function getObjectStateGroupRowsFixture() + { + return [ + [ + 'ezcobj_state_group_default_language_id' => 2, + 'ezcobj_state_group_id' => 1, + 'ezcobj_state_group_identifier' => 'ez_lock', + 'ezcobj_state_group_language_mask' => 3, + 'ezcobj_state_group_language_description' => '', + 'ezcobj_state_group_language_language_id' => 3, + 'ezcobj_state_group_language_real_language_id' => 2, + 'ezcobj_state_group_language_name' => 'Lock', + ], + ]; + } + + /** + * Returns an object state fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState + */ + protected function getObjectStateFixture() + { + $objectState = new ObjectState(); + $objectState->identifier = 'not_locked'; + $objectState->defaultLanguage = 'eng-US'; + $objectState->languageCodes = ['eng-US']; + $objectState->name = ['eng-US' => 'Not locked']; + $objectState->description = ['eng-US' => '']; + + return $objectState; + } + + /** + * Returns an object state group fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group + */ + protected function getObjectStateGroupFixture() + { + $group = new Group(); + $group->identifier = 'ez_lock'; + $group->defaultLanguage = 'eng-US'; + $group->languageCodes = ['eng-US']; + $group->name = ['eng-US' => 'Lock']; + $group->description = ['eng-US' => '']; + + return $group; + } + + /** + * Returns the InputStruct fixture for creating object states. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct + */ + protected function getObjectStateInputStructFixture() + { + $inputStruct = new InputStruct(); + + $inputStruct->defaultLanguage = 'eng-US'; + $inputStruct->identifier = 'not_locked'; + $inputStruct->name = ['eng-US' => 'Not locked']; + $inputStruct->description = ['eng-US' => '']; + + return $inputStruct; + } + + /** + * Returns the InputStruct fixture for creating object state groups. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct + */ + protected function getObjectStateGroupInputStructFixture() + { + $inputStruct = new InputStruct(); + + $inputStruct->defaultLanguage = 'eng-US'; + $inputStruct->identifier = 'ez_lock'; + $inputStruct->name = ['eng-US' => 'Lock']; + $inputStruct->description = ['eng-US' => '']; + + return $inputStruct; + } +} + +class_alias(MapperTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\ObjectState\MapperTest'); diff --git a/tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php b/tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php new file mode 100644 index 0000000000..075e4b50b6 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/ObjectState/ObjectStateHandlerTest.php @@ -0,0 +1,659 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\ObjectState; + +use Ibexa\Contracts\Core\Persistence\Content\ObjectState; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Core\Persistence\Legacy\Content\ObjectState\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\ObjectState\Handler; +use Ibexa\Core\Persistence\Legacy\Content\ObjectState\Mapper; +use Ibexa\Tests\Core\Persistence\Legacy\Content\LanguageAwareTestCase; +use Ibexa\Tests\Integration\Core\Repository\BaseTest as APIBaseTest; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\ObjectState\Handler + */ +class ObjectStateHandlerTest extends LanguageAwareTestCase +{ + /** + * Object state handler. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\ObjectState\Handler + */ + protected $objectStateHandler; + + /** + * Object state gateway mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\ObjectState\Gateway + */ + protected $gatewayMock; + + /** + * Object state mapper mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\ObjectState\Mapper + */ + protected $mapperMock; + + public function testCreateGroup() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $mapperMock->expects($this->once()) + ->method('createObjectStateGroupFromInputStruct') + ->with($this->equalTo($this->getInputStructFixture())) + ->will($this->returnValue($this->getObjectStateGroupFixture())); + + $gatewayMock->expects($this->once()) + ->method('insertObjectStateGroup') + ->with($this->equalTo($this->getObjectStateGroupFixture())) + ->will($this->returnValue($this->getObjectStateGroupFixture())); + + $result = $handler->createGroup($this->getInputStructFixture()); + + $this->assertInstanceOf( + Group::class, + $result + ); + } + + public function testLoadGroup() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateGroupData') + ->with($this->equalTo(2)) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->once()) + ->method('createObjectStateGroupFromData') + ->with($this->equalTo([[]])) + ->will($this->returnValue($this->getObjectStateGroupFixture())); + + $result = $handler->loadGroup(2); + + $this->assertInstanceOf( + Group::class, + $result + ); + } + + public function testLoadGroupThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getObjectStateHandler(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateGroupData') + ->with($this->equalTo(APIBaseTest::DB_INT_MAX)) + ->will($this->returnValue([])); + + $handler->loadGroup(APIBaseTest::DB_INT_MAX); + } + + public function testLoadGroupByIdentifier() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateGroupDataByIdentifier') + ->with($this->equalTo('ez_lock')) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->once()) + ->method('createObjectStateGroupFromData') + ->with($this->equalTo([[]])) + ->will($this->returnValue($this->getObjectStateGroupFixture())); + + $result = $handler->loadGroupByIdentifier('ez_lock'); + + $this->assertInstanceOf( + Group::class, + $result + ); + } + + public function testLoadGroupByIdentifierThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getObjectStateHandler(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateGroupDataByIdentifier') + ->with($this->equalTo('unknown')) + ->will($this->returnValue([])); + + $handler->loadGroupByIdentifier('unknown'); + } + + public function testLoadAllGroups() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateGroupListData') + ->with($this->equalTo(0), $this->equalTo(-1)) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->once()) + ->method('createObjectStateGroupListFromData') + ->with($this->equalTo([[]])) + ->will($this->returnValue([$this->getObjectStateGroupFixture()])); + + $result = $handler->loadAllGroups(); + + foreach ($result as $resultItem) { + $this->assertInstanceOf( + Group::class, + $resultItem + ); + } + } + + public function testLoadObjectStates() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateListData') + ->with($this->equalTo(2)) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->once()) + ->method('createObjectStateListFromData') + ->with($this->equalTo([[]])) + ->will($this->returnValue([$this->getObjectStateFixture(), $this->getObjectStateFixture()])); + + $result = $handler->loadObjectStates(2); + + foreach ($result as $resultItem) { + $this->assertInstanceOf( + ObjectState::class, + $resultItem + ); + } + } + + public function testUpdateGroup() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $mapperMock->expects($this->once()) + ->method('createObjectStateGroupFromInputStruct') + ->with($this->equalTo($this->getInputStructFixture())) + ->will($this->returnValue($this->getObjectStateGroupFixture())); + + $gatewayMock->expects($this->once()) + ->method('updateObjectStateGroup') + ->with($this->equalTo(new Group(['id' => 2]))); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateGroupData') + ->with($this->equalTo(2)) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->once()) + ->method('createObjectStateGroupFromData') + ->with($this->equalTo([[]])) + ->will($this->returnValue($this->getObjectStateGroupFixture())); + + $result = $handler->updateGroup(2, $this->getInputStructFixture()); + + $this->assertInstanceOf( + Group::class, + $result + ); + } + + public function testDeleteGroup() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateListData') + ->with($this->equalTo(2)) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->once()) + ->method('createObjectStateListFromData') + ->with($this->equalTo([[]])) + ->will( + $this->returnValue( + [ + new ObjectState(['id' => 1]), + new ObjectState(['id' => 2]), + ] + ) + ); + + $gatewayMock->expects($this->exactly(2)) + ->method('deleteObjectStateLinks'); + + $gatewayMock->expects($this->exactly(2)) + ->method('deleteObjectState'); + + $gatewayMock->expects($this->at(1)) + ->method('deleteObjectStateLinks') + ->with($this->equalTo(1)); + + $gatewayMock->expects($this->at(2)) + ->method('deleteObjectState') + ->with($this->equalTo(1)); + + $gatewayMock->expects($this->at(3)) + ->method('deleteObjectStateLinks') + ->with($this->equalTo(2)); + + $gatewayMock->expects($this->at(4)) + ->method('deleteObjectState') + ->with($this->equalTo(2)); + + $gatewayMock->expects($this->once()) + ->method('deleteObjectStateGroup') + ->with($this->equalTo(2)); + + $handler->deleteGroup(2); + } + + public function testCreate() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $mapperMock->expects($this->once()) + ->method('createObjectStateFromInputStruct') + ->with($this->equalTo($this->getInputStructFixture())) + ->will($this->returnValue($this->getObjectStateFixture())); + + $gatewayMock->expects($this->once()) + ->method('insertObjectState') + ->with($this->equalTo($this->getObjectStateFixture()), $this->equalTo(2)) + ->will($this->returnValue($this->getObjectStateFixture())); + + $result = $handler->create(2, $this->getInputStructFixture()); + + $this->assertInstanceOf( + ObjectState::class, + $result + ); + } + + public function testLoad() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateData') + ->with($this->equalTo(1)) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->once()) + ->method('createObjectStateFromData') + ->with($this->equalTo([[]])) + ->will($this->returnValue($this->getObjectStateFixture())); + + $result = $handler->load(1); + + $this->assertInstanceOf( + ObjectState::class, + $result + ); + } + + public function testLoadThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getObjectStateHandler(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateData') + ->with($this->equalTo(APIBaseTest::DB_INT_MAX)) + ->will($this->returnValue([])); + + $handler->load(APIBaseTest::DB_INT_MAX); + } + + public function testLoadByIdentifier() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateDataByIdentifier') + ->with($this->equalTo('not_locked'), $this->equalTo(2)) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->once()) + ->method('createObjectStateFromData') + ->with($this->equalTo([[]])) + ->will($this->returnValue($this->getObjectStateFixture())); + + $result = $handler->loadByIdentifier('not_locked', 2); + + $this->assertInstanceOf( + ObjectState::class, + $result + ); + } + + public function testLoadByIdentifierThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getObjectStateHandler(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateDataByIdentifier') + ->with($this->equalTo('unknown'), $this->equalTo(2)) + ->will($this->returnValue([])); + + $handler->loadByIdentifier('unknown', 2); + } + + public function testUpdate() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $mapperMock->expects($this->once()) + ->method('createObjectStateFromInputStruct') + ->with($this->equalTo($this->getInputStructFixture())) + ->will($this->returnValue($this->getObjectStateFixture())); + + $gatewayMock->expects($this->once()) + ->method('updateObjectState') + ->with($this->equalTo(new ObjectState(['id' => 1]))); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateData') + ->with($this->equalTo(1)) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->once()) + ->method('createObjectStateFromData') + ->with($this->equalTo([[]])) + ->will($this->returnValue($this->getObjectStateFixture())); + + $result = $handler->update(1, $this->getInputStructFixture()); + + $this->assertInstanceOf( + ObjectState::class, + $result + ); + } + + public function testSetPriority() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateData') + ->with($this->equalTo(2)) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->once()) + ->method('createObjectStateFromData') + ->with($this->equalTo([[]])) + ->will($this->returnValue(new ObjectState(['id' => 2, 'groupId' => 2]))); + + $gatewayMock->expects($this->any()) + ->method('loadObjectStateListData') + ->with($this->equalTo(2)) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->any()) + ->method('createObjectStateListFromData') + ->with($this->equalTo([[]])) + ->will( + $this->returnValue( + [ + new ObjectState(['id' => 1, 'groupId' => 2]), + new ObjectState(['id' => 2, 'groupId' => 2]), + new ObjectState(['id' => 3, 'groupId' => 2]), + ] + ) + ); + + $gatewayMock->expects($this->exactly(3)) + ->method('updateObjectStatePriority'); + + $gatewayMock->expects($this->at(2)) + ->method('updateObjectStatePriority') + ->with($this->equalTo(2), $this->equalTo(0)); + + $gatewayMock->expects($this->at(3)) + ->method('updateObjectStatePriority') + ->with($this->equalTo(1), $this->equalTo(1)); + + $gatewayMock->expects($this->at(4)) + ->method('updateObjectStatePriority') + ->with($this->equalTo(3), $this->equalTo(2)); + + $handler->setPriority(2, 0); + } + + public function testDelete() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateData') + ->with($this->equalTo(1)) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->once()) + ->method('createObjectStateFromData') + ->with($this->equalTo([[]])) + ->will($this->returnValue(new ObjectState(['id' => 1, 'groupId' => 2]))); + + $gatewayMock->expects($this->once()) + ->method('deleteObjectState') + ->with($this->equalTo(1)); + + $gatewayMock->expects($this->any()) + ->method('loadObjectStateListData') + ->with($this->equalTo(2)) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->any()) + ->method('createObjectStateListFromData') + ->with($this->equalTo([[]])) + ->will($this->returnValue([new ObjectState(['id' => 2, 'groupId' => 2])])); + + $gatewayMock->expects($this->once()) + ->method('updateObjectStatePriority') + ->with($this->equalTo(2), $this->equalTo(0)); + + $gatewayMock->expects($this->once()) + ->method('updateObjectStateLinks') + ->with($this->equalTo(1), $this->equalTo(2)); + + $handler->delete(1); + } + + public function testDeleteThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getObjectStateHandler(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateData') + ->with($this->equalTo(APIBaseTest::DB_INT_MAX)) + ->will($this->returnValue([])); + + $handler->delete(APIBaseTest::DB_INT_MAX); + } + + public function testSetContentState() + { + $handler = $this->getObjectStateHandler(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('setContentState') + ->with($this->equalTo(42), $this->equalTo(2), $this->equalTo(2)); + + $result = $handler->setContentState(42, 2, 2); + + $this->assertTrue($result); + } + + public function testGetContentState() + { + $handler = $this->getObjectStateHandler(); + $mapperMock = $this->getMapperMock(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadObjectStateDataForContent') + ->with($this->equalTo(42), $this->equalTo(2)) + ->will($this->returnValue([[]])); + + $mapperMock->expects($this->once()) + ->method('createObjectStateFromData') + ->with($this->equalTo([[]])) + ->will($this->returnValue($this->getObjectStateFixture())); + + $result = $handler->getContentState(42, 2); + + $this->assertInstanceOf( + ObjectState::class, + $result + ); + } + + public function testGetContentCount() + { + $handler = $this->getObjectStateHandler(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('getContentCount') + ->with($this->equalTo(1)) + ->will($this->returnValue(185)); + + $result = $handler->getContentCount(1); + + $this->assertEquals(185, $result); + } + + /** + * Returns an object state. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState + */ + protected function getObjectStateFixture() + { + return new ObjectState(); + } + + /** + * Returns an object state group. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group + */ + protected function getObjectStateGroupFixture() + { + return new Group(); + } + + /** + * Returns the InputStruct. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\ObjectState\InputStruct + */ + protected function getInputStructFixture() + { + return new InputStruct(); + } + + /** + * Returns the object state handler to test. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\ObjectState\Handler + */ + protected function getObjectStateHandler() + { + if (!isset($this->objectStateHandler)) { + $this->objectStateHandler = new Handler( + $this->getGatewayMock(), + $this->getMapperMock() + ); + } + + return $this->objectStateHandler; + } + + /** + * Returns an object state mapper mock. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\ObjectState\Mapper + */ + protected function getMapperMock() + { + if (!isset($this->mapperMock)) { + $this->mapperMock = $this->getMockBuilder(Mapper::class) + ->setConstructorArgs([$this->getLanguageHandler()]) + ->setMethods([]) + ->getMock(); + } + + return $this->mapperMock; + } + + /** + * Returns a mock for the object state gateway. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\ObjectState\Gateway + */ + protected function getGatewayMock() + { + if (!isset($this->gatewayMock)) { + $this->gatewayMock = $this->getMockForAbstractClass(Gateway::class); + } + + return $this->gatewayMock; + } +} + +class_alias(ObjectStateHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\ObjectState\ObjectStateHandlerTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..48f2a680b8 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Section/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,272 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Section\Gateway; + +use Ibexa\Core\Persistence\Legacy\Content\Section\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase::insertSection + */ +class DoctrineDatabaseTest extends TestCase +{ + /** + * Database gateway to test. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase + */ + protected $databaseGateway; + + /** + * Inserts DB fixture. + */ + protected function setUp(): void + { + parent::setUp(); + + $this->insertDatabaseFixture( + __DIR__ . '/../../_fixtures/sections.php' + ); + } + + public function testInsertSection() + { + $gateway = $this->getDatabaseGateway(); + + $gateway->insertSection('New Section', 'new_section'); + $query = $this->getDatabaseConnection()->createQueryBuilder(); + + $this->assertQueryResult( + [ + [ + 'id' => '7', + 'identifier' => 'new_section', + 'name' => 'New Section', + 'locale' => '', + ], + ], + $query + ->select('id', 'identifier', 'name', 'locale') + ->from('ezsection') + ->where( + $query->expr()->eq( + 'identifier', + $query->createPositionalParameter('new_section') + ) + ) + ); + } + + public function testUpdateSection() + { + $gateway = $this->getDatabaseGateway(); + + $gateway->updateSection(2, 'New Section', 'new_section'); + + $this->assertQueryResult( + [ + [ + 'id' => '2', + 'identifier' => 'new_section', + 'name' => 'New Section', + 'locale' => '', + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('id', 'identifier', 'name', 'locale') + ->from('ezsection') + ->where('id=2') + ); + } + + public function testLoadSectionData() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->loadSectionData(2); + + $this->assertEquals( + [ + [ + 'id' => '2', + 'identifier' => 'users', + 'name' => 'Users', + ], + ], + $result + ); + } + + public function testLoadAllSectionData() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->loadAllSectionData(); + + $expected = [ + [ + 'id' => '1', + 'identifier' => 'standard', + 'name' => 'Standard', + ], + + [ + 'id' => '2', + 'identifier' => 'users', + 'name' => 'Users', + ], + + [ + 'id' => '3', + 'identifier' => 'media', + 'name' => 'Media', + ], + + [ + 'id' => '4', + 'identifier' => 'setup', + 'name' => 'Setup', + ], + + [ + 'id' => '5', + 'identifier' => 'design', + 'name' => 'Design', + ], + + [ + 'id' => '6', + 'identifier' => '', + 'name' => 'Restricted', + ], + ]; + $this->assertEquals( + $expected, + $result + ); + } + + public function testLoadSectionDataByIdentifier() + { + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->loadSectionDataByIdentifier('users'); + + $this->assertEquals( + [ + [ + 'id' => '2', + 'identifier' => 'users', + 'name' => 'Users', + ], + ], + $result + ); + } + + public function testCountContentObjectsInSection() + { + $this->insertDatabaseFixture( + __DIR__ . '/../../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->countContentObjectsInSection(2); + + $this->assertSame( + 7, + $result + ); + } + + public function testCountRoleAssignmentsUsingSection() + { + $this->insertDatabaseFixture( + __DIR__ . '/../../../User/_fixtures/roles.php' + ); + + $gateway = $this->getDatabaseGateway(); + + $result = $gateway->countRoleAssignmentsUsingSection(2); + + $this->assertSame( + 1, + $result + ); + } + + public function testDeleteSection() + { + $gateway = $this->getDatabaseGateway(); + + $gateway->deleteSection(2); + + $this->assertQueryResult( + [ + [ + 'count' => '5', + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('COUNT( * ) AS count') + ->from('ezsection') + ); + + $this->assertQueryResult( + [ + [ + 'count' => '0', + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('COUNT( * ) AS count') + ->from('ezsection') + ->where('id=2') + ); + } + + /** + * @depends testCountContentObjectsInSection + */ + public function testAssignSectionToContent() + { + $this->insertDatabaseFixture( + __DIR__ . '/../../_fixtures/contentobjects.php' + ); + + $gateway = $this->getDatabaseGateway(); + + $beforeCount = $gateway->countContentObjectsInSection(4); + + $result = $gateway->assignSectionToContent(4, 10); + + $this->assertSame( + $beforeCount + 1, + $gateway->countContentObjectsInSection(4) + ); + } + + /** + * Returns a ready to test DoctrineDatabase gateway. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Section\Gateway\DoctrineDatabase + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function getDatabaseGateway(): Gateway + { + if (!isset($this->databaseGateway)) { + $this->databaseGateway = new DoctrineDatabase($this->getDatabaseConnection()); + } + + return $this->databaseGateway; + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Section\Gateway\DoctrineDatabaseTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php b/tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php new file mode 100644 index 0000000000..e9357cdeba --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Section/SectionHandlerTest.php @@ -0,0 +1,321 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Section; + +use Ibexa\Contracts\Core\Persistence\Content\Section; +use Ibexa\Core\Persistence\Legacy\Content\Section\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Section\Handler; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Section\Handler + */ +class SectionHandlerTest extends TestCase +{ + /** + * Section handler. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Section\Handler + */ + protected $sectionHandler; + + /** + * Section gateway mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Section\Gateway + */ + protected $gatewayMock; + + public function testCreate() + { + $handler = $this->getSectionHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('insertSection') + ->with( + $this->equalTo('New Section'), + $this->equalTo('new_section') + )->will($this->returnValue(23)); + + $sectionRef = new Section(); + $sectionRef->id = 23; + $sectionRef->name = 'New Section'; + $sectionRef->identifier = 'new_section'; + + $result = $handler->create('New Section', 'new_section'); + + $this->assertEquals( + $sectionRef, + $result + ); + } + + public function testUpdate() + { + $handler = $this->getSectionHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('updateSection') + ->with( + $this->equalTo(23), + $this->equalTo('New Section'), + $this->equalTo('new_section') + ); + + $sectionRef = new Section(); + $sectionRef->id = 23; + $sectionRef->name = 'New Section'; + $sectionRef->identifier = 'new_section'; + + $result = $handler->update(23, 'New Section', 'new_section'); + + $this->assertEquals( + $sectionRef, + $result + ); + } + + public function testLoad() + { + $handler = $this->getSectionHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadSectionData') + ->with( + $this->equalTo(23) + )->will( + $this->returnValue( + [ + [ + 'id' => '23', + 'identifier' => 'new_section', + 'name' => 'New Section', + ], + ] + ) + ); + + $sectionRef = new Section(); + $sectionRef->id = 23; + $sectionRef->name = 'New Section'; + $sectionRef->identifier = 'new_section'; + + $result = $handler->load(23); + + $this->assertEquals( + $sectionRef, + $result + ); + } + + public function testLoadAll() + { + $handler = $this->getSectionHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadAllSectionData') + ->will( + $this->returnValue( + [ + [ + 'id' => '23', + 'identifier' => 'new_section', + 'name' => 'New Section', + ], + [ + 'id' => '46', + 'identifier' => 'new_section2', + 'name' => 'New Section2', + ], + ] + ) + ); + + $sectionRef = new Section(); + $sectionRef->id = 23; + $sectionRef->name = 'New Section'; + $sectionRef->identifier = 'new_section'; + + $sectionRef2 = new Section(); + $sectionRef2->id = 46; + $sectionRef2->name = 'New Section2'; + $sectionRef2->identifier = 'new_section2'; + + $result = $handler->loadAll(); + + $this->assertEquals( + [$sectionRef, $sectionRef2], + $result + ); + } + + public function testLoadByIdentifier() + { + $handler = $this->getSectionHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('loadSectionDataByIdentifier') + ->with( + $this->equalTo('new_section') + )->will( + $this->returnValue( + [ + [ + 'id' => '23', + 'identifier' => 'new_section', + 'name' => 'New Section', + ], + ] + ) + ); + + $sectionRef = new Section(); + $sectionRef->id = 23; + $sectionRef->name = 'New Section'; + $sectionRef->identifier = 'new_section'; + + $result = $handler->loadByIdentifier('new_section'); + + $this->assertEquals( + $sectionRef, + $result + ); + } + + public function testDelete() + { + $handler = $this->getSectionHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('countContentObjectsInSection') + ->with($this->equalTo(23)) + ->will($this->returnValue(0)); + + $gatewayMock->expects($this->once()) + ->method('deleteSection') + ->with( + $this->equalTo(23) + ); + + $result = $handler->delete(23); + } + + public function testDeleteFailure() + { + $this->expectException(\RuntimeException::class); + + $handler = $this->getSectionHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('countContentObjectsInSection') + ->with($this->equalTo(23)) + ->will($this->returnValue(2)); + + $gatewayMock->expects($this->never()) + ->method('deleteSection'); + + $result = $handler->delete(23); + } + + public function testAssign() + { + $handler = $this->getSectionHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('assignSectionToContent') + ->with( + $this->equalTo(23), + $this->equalTo(42) + ); + + $result = $handler->assign(23, 42); + } + + public function testPoliciesCount() + { + $handler = $this->getSectionHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('countPoliciesUsingSection') + ->with( + $this->equalTo(1) + ) + ->will( + $this->returnValue(7) + ); + + $result = $handler->policiesCount(1); + } + + public function testCountRoleAssignmentsUsingSection() + { + $handler = $this->getSectionHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('countRoleAssignmentsUsingSection') + ->with( + $this->equalTo(1) + ) + ->will( + $this->returnValue(0) + ); + + $handler->countRoleAssignmentsUsingSection(1); + } + + /** + * Returns the section handler to test. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Section\Handler + */ + protected function getSectionHandler() + { + if (!isset($this->sectionHandler)) { + $this->sectionHandler = new Handler( + $this->getGatewayMock() + ); + } + + return $this->sectionHandler; + } + + /** + * Returns a mock for the section gateway. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Section\Gateway + */ + protected function getGatewayMock() + { + if (!isset($this->gatewayMock)) { + $this->gatewayMock = $this->getMockForAbstractClass(Gateway::class); + } + + return $this->gatewayMock; + } +} + +class_alias(SectionHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Section\SectionHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/StorageHandlerTest.php b/tests/lib/Persistence/Legacy/Content/StorageHandlerTest.php similarity index 81% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/StorageHandlerTest.php rename to tests/lib/Persistence/Legacy/Content/StorageHandlerTest.php index 72b6ea4598..2ef2185b43 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/StorageHandlerTest.php +++ b/tests/lib/Persistence/Legacy/Content/StorageHandlerTest.php @@ -4,50 +4,30 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\FieldType\FieldStorage; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; +use Ibexa\Contracts\Core\FieldType\FieldStorage; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Core\Persistence\Legacy\Content\StorageHandler; +use Ibexa\Core\Persistence\Legacy\Content\StorageRegistry; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; /** - * Test case for Content Handler. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler + * @covers \Ibexa\Core\Persistence\Legacy\Content\StorageHandler */ class StorageHandlerTest extends TestCase { - /** - * StorageRegistry mock. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry - */ - protected $storageRegistryMock; + /** @var \Ibexa\Core\Persistence\Legacy\Content\StorageRegistry&\PHPUnit\Framework\MockObject\MockObject */ + protected StorageRegistry $storageRegistryMock; - /** - * StorageHandler to test. - * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler - */ - protected $storageHandler; + protected StorageHandler $storageHandler; - /** - * Mock for external storage. - * - * @var \eZ\Publish\SPI\FieldType\FieldStorage - */ - protected $storageMock; + /** @var \Ibexa\Contracts\Core\FieldType\FieldStorage&\PHPUnit\Framework\MockObject\MockObject */ + protected FieldStorage $storageMock; - /** - * Mock for versionInfo. - * - * @var \eZ\Publish\Core\Repository\Values\Content\VersionInfo - */ - protected $versionInfoMock; + protected VersionInfo $versionInfoMock; public function testStoreFieldData(): void { @@ -179,7 +159,7 @@ public function testDeleteFieldData(): void /** * Returns the StorageHandler to test. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler + * @return \Ibexa\Core\Persistence\Legacy\Content\StorageHandler */ protected function getStorageHandler(): StorageHandler { @@ -206,7 +186,7 @@ protected function getContextMock(): array /** * Returns a StorageRegistry mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\StorageRegistry + * @return \Ibexa\Core\Persistence\Legacy\Content\StorageRegistry&\PHPUnit\Framework\MockObject\MockObject */ protected function getStorageRegistryMock(): StorageRegistry { @@ -223,7 +203,7 @@ protected function getStorageRegistryMock(): StorageRegistry /** * Returns a Storage mock. * - * @return \eZ\Publish\SPI\FieldType\FieldStorage + * @return \Ibexa\Contracts\Core\FieldType\FieldStorage&\PHPUnit\Framework\MockObject\MockObject */ protected function getStorageMock(): FieldStorage { @@ -243,3 +223,5 @@ protected function getVersionInfoMock(): VersionInfo return $this->versionInfoMock; } } + +class_alias(StorageHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\StorageHandlerTest'); diff --git a/tests/lib/Persistence/Legacy/Content/StorageRegistryTest.php b/tests/lib/Persistence/Legacy/Content/StorageRegistryTest.php new file mode 100644 index 0000000000..06e605b411 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/StorageRegistryTest.php @@ -0,0 +1,62 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content; + +use Ibexa\Contracts\Core\FieldType\FieldStorage; +use Ibexa\Core\FieldType\NullStorage; +use Ibexa\Core\Persistence\Legacy\Content\StorageRegistry; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\StorageRegistry + */ +class StorageRegistryTest extends TestCase +{ + private const TYPE_NAME = 'some-type'; + + public function testRegister(): void + { + $storage = $this->getStorageMock(); + $registry = new StorageRegistry([self::TYPE_NAME => $storage]); + + $this->assertSame($storage, $registry->getStorage(self::TYPE_NAME)); + } + + public function testGetStorage() + { + $storage = $this->getStorageMock(); + $registry = new StorageRegistry([self::TYPE_NAME => $storage]); + + $res = $registry->getStorage(self::TYPE_NAME); + + $this->assertSame( + $storage, + $res + ); + } + + public function testGetNotFound() + { + $registry = new StorageRegistry([]); + self::assertInstanceOf( + NullStorage::class, + $registry->getStorage('not-found') + ); + } + + /** + * Returns a mock for Storage. + * + * @return \Ibexa\Contracts\Core\FieldType\FieldStorage + */ + protected function getStorageMock() + { + return $this->createMock(FieldStorage::class); + } +} + +class_alias(StorageRegistryTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\StorageRegistryTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/TreeHandlerTest.php b/tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php similarity index 87% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/TreeHandlerTest.php rename to tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php index d5d4cc5be2..a3403d8fb8 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/TreeHandlerTest.php +++ b/tests/lib/Persistence/Legacy/Content/TreeHandlerTest.php @@ -4,18 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content; + +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; +use Ibexa\Core\Persistence\Legacy\Content\FieldHandler; +use Ibexa\Core\Persistence\Legacy\Content\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; +use Ibexa\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; +use Ibexa\Core\Persistence\Legacy\Content\Mapper; +use Ibexa\Core\Persistence\Legacy\Content\TreeHandler; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; /** * Test case for Tree Handler. @@ -96,7 +96,7 @@ public function testRemoveRawContent() ->method('deleteFields') ->with( $this->equalTo(23), - $this->isInstanceOf('eZ\\Publish\\SPI\\Persistence\\Content\\VersionInfo') + $this->isInstanceOf(VersionInfo::class) ); $this->getContentGatewayMock() @@ -400,13 +400,13 @@ public function testLoadLocation() $this->assertTrue($location instanceof Location); } - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway */ + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Location\Gateway */ protected $locationGatewayMock; /** * Returns Location Gateway mock. * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Location\Gateway + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Location\Gateway */ protected function getLocationGatewayMock() { @@ -417,13 +417,13 @@ protected function getLocationGatewayMock() return $this->locationGatewayMock; } - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper */ + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Location\Mapper */ protected $locationMapperMock; /** * Returns a Location Mapper mock. * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Location\Mapper */ protected function getLocationMapperMock() { @@ -434,13 +434,13 @@ protected function getLocationMapperMock() return $this->locationMapperMock; } - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Gateway */ + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Gateway */ protected $contentGatewayMock; /** * Returns Content Gateway mock. * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Gateway + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Gateway */ protected function getContentGatewayMock() { @@ -451,13 +451,13 @@ protected function getContentGatewayMock() return $this->contentGatewayMock; } - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Mapper */ + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected $contentMapper; /** * Returns a Content Mapper mock. * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Mapper + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected function getContentMapperMock() { @@ -468,13 +468,13 @@ protected function getContentMapperMock() return $this->contentMapper; } - /** @var \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler */ + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\FieldHandler */ protected $fieldHandlerMock; /** * Returns a FieldHandler mock. * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\FieldHandler */ protected function getFieldHandlerMock() { @@ -488,7 +488,7 @@ protected function getFieldHandlerMock() /** * @param array $methods * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\TreeHandler */ protected function getPartlyMockedTreeHandler(array $methods) { @@ -507,7 +507,7 @@ protected function getPartlyMockedTreeHandler(array $methods) } /** - * @return \eZ\Publish\Core\Persistence\Legacy\Content\TreeHandler + * @return \Ibexa\Core\Persistence\Legacy\Content\TreeHandler */ protected function getTreeHandler() { @@ -520,3 +520,5 @@ protected function getTreeHandler() ); } } + +class_alias(TreeHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\TreeHandlerTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php b/tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php new file mode 100644 index 0000000000..23bf963870 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Type/ContentTypeHandlerTest.php @@ -0,0 +1,1243 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Type; + +use Ibexa\Contracts\Core\Persistence\Content\Type; +use Ibexa\Contracts\Core\Persistence\Content\Type\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\Type\Group; +use Ibexa\Contracts\Core\Persistence\Content\Type\Group\CreateStruct as GroupCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Type\Group\UpdateStruct as GroupUpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Type\UpdateStruct; +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\Type\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Type\Handler; +use Ibexa\Core\Persistence\Legacy\Content\Type\Mapper; +use Ibexa\Core\Persistence\Legacy\Content\Type\StorageDispatcherInterface; +use Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler as UpdateHandler; +use Ibexa\Core\Persistence\Legacy\Exception; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Type\Handler + */ +class ContentTypeHandlerTest extends TestCase +{ + /** + * Gateway mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Type\Gateway + */ + protected $gatewayMock; + + /** + * Mapper mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Type\Mapper + */ + protected $mapperMock; + + /** + * Update\Handler mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler + */ + protected $updateHandlerMock; + + /** @var \Ibexa\Core\Persistence\Legacy\Content\Type\StorageDispatcherInterface&\PHPUnit\Framework\MockObject\MockObject */ + protected $storageDispatcherMock; + + public function testCreateGroup() + { + $createStruct = new GroupCreateStruct(); + + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once()) + ->method('createGroupFromCreateStruct') + ->with( + $this->isInstanceOf( + GroupCreateStruct::class + ) + ) + ->will( + $this->returnValue(new Group()) + ); + + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('insertGroup') + ->with( + $this->isInstanceOf( + Group::class + ) + ) + ->will($this->returnValue(23)); + + $handler = $this->getHandler(); + $group = $handler->createGroup( + new GroupCreateStruct() + ); + + $this->assertInstanceOf( + Group::class, + $group + ); + $this->assertEquals( + 23, + $group->id + ); + } + + public function testUpdateGroup() + { + $updateStruct = new GroupUpdateStruct(); + $updateStruct->id = 23; + + $mapperMock = $this->getMapperMock(); + + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('updateGroup') + ->with( + $this->isInstanceOf( + GroupUpdateStruct::class + ) + ); + + $handlerMock = $this->getMockBuilder(Handler::class) + ->setMethods(['loadGroup']) + ->setConstructorArgs([ + $gatewayMock, + $mapperMock, + $this->getUpdateHandlerMock(), + $this->getStorageDispatcherMock(), + ]) + ->getMock(); + + $handlerMock->expects($this->once()) + ->method('loadGroup') + ->with( + $this->equalTo(23) + )->will( + $this->returnValue(new Group()) + ); + + $res = $handlerMock->updateGroup( + $updateStruct + ); + + $this->assertInstanceOf( + Group::class, + $res + ); + } + + public function testDeleteGroupSuccess() + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('countTypesInGroup') + ->with($this->equalTo(23)) + ->will($this->returnValue(0)); + $gatewayMock->expects($this->once()) + ->method('deleteGroup') + ->with($this->equalTo(23)); + + $handler = $this->getHandler(); + $handler->deleteGroup(23); + } + + public function testDeleteGroupFailure() + { + $this->expectException(Exception\GroupNotEmpty::class); + $this->expectExceptionMessage('Group with ID "23" is not empty.'); + + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('countTypesInGroup') + ->with($this->equalTo(23)) + ->will($this->returnValue(42)); + $gatewayMock->expects($this->never()) + ->method('deleteGroup'); + + $handler = $this->getHandler(); + $handler->deleteGroup(23); + } + + public function testLoadGroup() + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('loadGroupData') + ->with($this->equalTo([23])) + ->will($this->returnValue([])); + + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once()) + ->method('extractGroupsFromRows') + ->with($this->equalTo([])) + ->will($this->returnValue([new Group()])); + + $handler = $this->getHandler(); + $res = $handler->loadGroup(23); + + $this->assertEquals( + new Group(), + $res + ); + } + + public function testLoadGroupByIdentifier() + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('loadGroupDataByIdentifier') + ->with($this->equalTo('content')) + ->will($this->returnValue([])); + + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once()) + ->method('extractGroupsFromRows') + ->with($this->equalTo([])) + ->will($this->returnValue([new Group()])); + + $handler = $this->getHandler(); + $res = $handler->loadGroupByIdentifier('content'); + + $this->assertEquals( + new Group(), + $res + ); + } + + public function testLoadAllGroups() + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('loadAllGroupsData') + ->will($this->returnValue([])); + + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once()) + ->method('extractGroupsFromRows') + ->with($this->equalTo([])) + ->will($this->returnValue([new Group()])); + + $handler = $this->getHandler(); + $res = $handler->loadAllGroups(); + + $this->assertEquals( + [new Group()], + $res + ); + } + + public function testLoadContentTypes() + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('loadTypesDataForGroup') + ->with($this->equalTo(23), $this->equalTo(0)) + ->will($this->returnValue([])); + + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once()) + ->method('extractTypesFromRows') + ->with($this->equalTo([])) + ->will($this->returnValue([new Type()])); + + $handler = $this->getHandler(); + $res = $handler->loadContentTypes(23, 0); + + $this->assertEquals( + [new Type()], + $res + ); + } + + public function testLoadContentTypeList(): void + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('loadTypesListData') + ->with($this->equalTo([23, 24])) + ->willReturn([]); + + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once()) + ->method('extractTypesFromRows') + ->with($this->equalTo([])) + ->willReturn([23 => new Type()]); + + $handler = $this->getHandler(); + $types = $handler->loadContentTypeList([23, 24]); + + $this->assertEquals( + [23 => new Type()], + $types, + 'Types not loaded correctly' + ); + } + + public function testLoadContentTypesByFieldDefinitionIdentifier(): void + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('loadTypesDataByFieldDefinitionIdentifier') + ->with('ezstring') + ->willReturn([]); + + $mapperMock = $this->getMapperMock(); + $mapperMock->expects(self::once()) + ->method('extractTypesFromRows') + ->with([]) + ->willReturn([23 => new Type()]); + + $handler = $this->getHandler(); + $types = $handler->loadContentTypesByFieldDefinitionIdentifier('ezstring'); + + self::assertEquals( + [23 => new Type()], + $types, + 'Types not loaded correctly' + ); + } + + public function testLoad() + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('loadTypeData') + ->with( + $this->equalTo(23), + $this->equalTo(1) + ) + ->will($this->returnValue([])); + + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once()) + ->method('extractTypesFromRows') + ->with($this->equalTo([])) + ->will( + $this->returnValue( + [new Type()] + ) + ); + + $handler = $this->getHandler(); + $type = $handler->load(23, 1); + + $this->assertEquals( + new Type(), + $type, + 'Type not loaded correctly' + ); + } + + public function testLoadNotFound() + { + $this->expectException(Exception\TypeNotFound::class); + + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('loadTypeData') + ->with( + $this->equalTo(23), + $this->equalTo(1) + ) + ->will($this->returnValue([])); + + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once()) + ->method('extractTypesFromRows') + ->with($this->equalTo([])) + ->will( + $this->returnValue( + [] + ) + ); + + $handler = $this->getHandler(); + $type = $handler->load(23, 1); + } + + public function testLoadDefaultVersion() + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('loadTypeData') + ->with( + $this->equalTo(23), + $this->equalTo(0) + ) + ->will($this->returnValue([])); + + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once()) + ->method('extractTypesFromRows') + ->will( + $this->returnValue( + [new Type()] + ) + ); + + $handler = $this->getHandler(); + $type = $handler->load(23); + + $this->assertEquals( + new Type(), + $type, + 'Type not loaded correctly' + ); + } + + public function testLoadByIdentifier() + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('loadTypeDataByIdentifier') + ->with( + $this->equalTo('blogentry'), + $this->equalTo(0) + ) + ->will($this->returnValue([])); + + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once()) + ->method('extractTypesFromRows') + ->will( + $this->returnValue( + [new Type()] + ) + ); + + $handler = $this->getHandler(); + $type = $handler->loadByIdentifier('blogentry'); + + $this->assertEquals( + new Type(), + $type, + 'Type not loaded correctly' + ); + } + + public function testLoadByRemoteId() + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('loadTypeDataByRemoteId') + ->with( + $this->equalTo('someLongHash'), + $this->equalTo(0) + ) + ->will($this->returnValue([])); + + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once()) + ->method('extractTypesFromRows') + ->will( + $this->returnValue( + [new Type()] + ) + ); + + $handler = $this->getHandler(); + $type = $handler->loadByRemoteId('someLongHash'); + + $this->assertEquals( + new Type(), + $type, + 'Type not loaded correctly' + ); + } + + public function testCreate() + { + $createStructFix = $this->getContentTypeCreateStructFixture(); + $createStructClone = clone $createStructFix; + + $mapperMock = $this->getMapperMock( + [ + 'toStorageFieldDefinition', + ] + ); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('insertType') + ->with( + $this->isInstanceOf( + Type::class + ) + ) + ->will($this->returnValue(23)); + $gatewayMock->expects($this->once()) + ->method('insertGroupAssignment') + ->with( + $this->equalTo(42), + $this->equalTo(23), + $this->equalTo(1) + ); + $gatewayMock->expects($this->exactly(2)) + ->method('insertFieldDefinition') + ->with( + $this->equalTo(23), + $this->equalTo(1), + $this->isInstanceOf(FieldDefinition::class), + $this->isInstanceOf(StorageFieldDefinition::class) + ) + ->will($this->returnValue(42)); + + $mapperMock->expects($this->exactly(2)) + ->method('toStorageFieldDefinition') + ->with( + $this->isInstanceOf(FieldDefinition::class), + $this->isInstanceOf(StorageFieldDefinition::class) + ); + + $handler = $this->getHandler(); + $type = $handler->create($createStructFix); + + $this->assertInstanceOf( + Type::class, + $type, + 'Incorrect type returned from create()' + ); + $this->assertEquals( + 23, + $type->id, + 'Incorrect ID for Type.' + ); + + $this->assertEquals( + 42, + $type->fieldDefinitions[0]->id, + 'Field definition ID not set correctly' + ); + $this->assertEquals( + 42, + $type->fieldDefinitions[1]->id, + 'Field definition ID not set correctly' + ); + + $this->assertEquals( + $createStructClone, + $createStructFix, + 'Create struct manipulated' + ); + } + + public function testUpdate() + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('updateType') + ->with( + $this->equalTo(23), + $this->equalTo(1), + $this->isInstanceOf( + Type::class + ) + ); + + $handlerMock = $this->getMockBuilder(Handler::class) + ->setMethods(['load']) + ->setConstructorArgs([ + $gatewayMock, + $this->getMapperMock(), + $this->getUpdateHandlerMock(), + $this->getStorageDispatcherMock(), + ]) + ->getMock(); + + $handlerMock->expects($this->once()) + ->method('load') + ->with( + $this->equalTo(23), + $this->equalTo(1) + ) + ->will($this->returnValue(new Type())); + + $res = $handlerMock->update(23, 1, new UpdateStruct()); + + $this->assertInstanceOf( + Type::class, + $res + ); + } + + public function testDeleteSuccess() + { + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects( + $this->once() + )->method( + 'countInstancesOfType' + )->with( + $this->equalTo(23) + )->will( + $this->returnValue(0) + ); + + $gatewayMock->expects($this->once())->method('loadTypeData')->with(23, 0)->willReturn([]); + + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once())->method('extractTypesFromRows')->with([])->willReturn([new Type()]); + + $gatewayMock->expects( + $this->once() + )->method( + 'delete' + )->with( + $this->equalTo(23), + $this->equalTo(0) + ); + + $handler = $this->getHandler(); + $res = $handler->delete(23, 0); + + $this->assertTrue($res); + } + + public function testDeleteThrowsBadStateException() + { + $this->expectException(BadStateException::class); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects( + $this->once() + )->method( + 'countInstancesOfType' + )->with( + $this->equalTo(23) + )->will( + $this->returnValue(1) + ); + + $gatewayMock->expects($this->never())->method('delete'); + + $handler = $this->getHandler(); + $res = $handler->delete(23, 0); + } + + public function testCreateVersion() + { + $userId = 42; + $contentTypeId = 23; + + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once()) + ->method('createCreateStructFromType') + ->with( + $this->isInstanceOf( + Type::class + ) + )->will( + $this->returnValue(new CreateStruct()) + ); + + $handlerMock = $this->getMockBuilder(Handler::class) + ->setMethods(['load', 'internalCreate']) + ->setConstructorArgs([ + $gatewayMock, + $mapperMock, + $this->getUpdateHandlerMock(), + $this->getStorageDispatcherMock(), + ]) + ->getMock(); + + $handlerMock->expects($this->once()) + ->method('load') + ->with( + $this->equalTo($contentTypeId, Type::STATUS_DEFINED) + )->will( + $this->returnValue( + new Type() + ) + ); + + $typeDraft = new Type(); + $handlerMock->expects($this->once()) + ->method('internalCreate') + ->with( + $this->isInstanceOf(CreateStruct::class), + $this->equalTo($contentTypeId) + )->will( + $this->returnValue($typeDraft) + ); + + $res = $handlerMock->createDraft($userId, $contentTypeId); + + $this->assertSame( + $typeDraft, + $res + ); + } + + public function testCopy() + { + $gatewayMock = $this->getGatewayMock(); + $mapperMock = $this->getMapperMock(['createCreateStructFromType']); + $mapperMock->expects($this->once()) + ->method('createCreateStructFromType') + ->with( + $this->isInstanceOf( + Type::class + ) + )->willReturn( + new CreateStruct(['identifier' => 'testCopy']) + ); + + $handlerMock = $this->getMockBuilder(Handler::class) + ->setMethods(['load', 'internalCreate', 'update']) + ->setConstructorArgs([ + $gatewayMock, + $mapperMock, + $this->getUpdateHandlerMock(), + $this->getStorageDispatcherMock(), + ]) + ->getMock(); + + $userId = 42; + $type = new Type([ + 'id' => 23, + 'identifier' => md5(uniqid(get_class($handlerMock), true)), + 'status' => Type::STATUS_DEFINED, + ]); + + $handlerMock->expects($this->once()) + ->method('load') + ->with( + $this->equalTo($type->id, Type::STATUS_DEFINED) + )->willReturn( + $type + ); + + $typeCopy = clone $type; + $typeCopy->id = 24; + $typeCopy->identifier = 'copy_of' . $type->identifier . '_' . $type->id; + + $handlerMock->expects($this->once()) + ->method('internalCreate') + ->with( + $this->isInstanceOf(CreateStruct::class), + )->willReturn( + $typeCopy + ); + + $handlerMock->expects($this->once()) + ->method('update') + ->with( + $this->equalTo($typeCopy->id), + $this->equalTo(Type::STATUS_DEFINED), + $this->isInstanceOf(UpdateStruct::class) + ) + ->will( + $this->returnValue($typeCopy) + ); + + $res = $handlerMock->copy($userId, $type->id, Type::STATUS_DEFINED); + + $this->assertEquals( + $typeCopy, + $res + ); + } + + public function testLink() + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('insertGroupAssignment') + ->with( + $this->equalTo(3), + $this->equalTo(23), + $this->equalTo(1) + ); + + $mapperMock = $this->getMapperMock(); + + $handler = $this->getHandler(); + $res = $handler->link(3, 23, 1); + + $this->assertTrue($res); + } + + public function testUnlinkSuccess() + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('countGroupsForType') + ->with( + $this->equalTo(23), + $this->equalTo(1) + )->will($this->returnValue(2)); + + $gatewayMock->expects($this->once()) + ->method('deleteGroupAssignment') + ->with( + $this->equalTo(3), + $this->equalTo(23), + $this->equalTo(1) + ); + + $mapperMock = $this->getMapperMock(); + + $handler = $this->getHandler(); + $res = $handler->unlink(3, 23, 1); + + $this->assertTrue($res); + } + + public function testUnlinkFailure() + { + $this->expectException(Exception\RemoveLastGroupFromType::class); + $this->expectExceptionMessage('Type with ID "23" in status "1" cannot be unlinked from its last group.'); + + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('countGroupsForType') + ->with( + $this->equalTo(23), + $this->equalTo(1) + ) + // Only 1 group assigned + ->will($this->returnValue(1)); + + $mapperMock = $this->getMapperMock(); + + $handler = $this->getHandler(); + $res = $handler->unlink(3, 23, 1); + } + + public function testGetFieldDefinition() + { + $mapperMock = $this->getMapperMock( + [ + 'extractFieldFromRow', + 'extractMultilingualData', + ] + ); + $mapperMock->expects($this->once()) + ->method('extractFieldFromRow') + ->with( + $this->equalTo([]) + )->will( + $this->returnValue(new FieldDefinition()) + ); + + $mapperMock->expects($this->once()) + ->method('extractMultilingualData') + ->with( + $this->equalTo([ + [], + ]) + )->will( + $this->returnValue([]) + ); + + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('loadFieldDefinition') + ->with( + $this->equalTo(42), + $this->equalTo(Type::STATUS_DEFINED) + )->will( + $this->returnValue([ + [], + ]) + ); + + $handler = $this->getHandler(); + $fieldDefinition = $handler->getFieldDefinition(42, Type::STATUS_DEFINED); + + $this->assertInstanceOf( + FieldDefinition::class, + $fieldDefinition + ); + } + + public function testAddFieldDefinition() + { + $mapperMock = $this->getMapperMock( + ['toStorageFieldDefinition'] + ); + $mapperMock->expects($this->once()) + ->method('toStorageFieldDefinition') + ->with( + $this->isInstanceOf( + FieldDefinition::class + ), + $this->isInstanceOf( + StorageFieldDefinition::class + ) + ); + + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('insertFieldDefinition') + ->with( + $this->equalTo(23), + $this->equalTo(1), + $this->isInstanceOf( + FieldDefinition::class + ), + $this->isInstanceOf( + StorageFieldDefinition::class + ) + )->will( + $this->returnValue(42) + ); + + $fieldDef = new FieldDefinition(); + + $storageDispatcherMock = $this->getStorageDispatcherMock(); + $storageDispatcherMock + ->expects($this->once()) + ->method('storeFieldConstraintsData') + ->with($fieldDef); + + $handler = $this->getHandler(); + $handler->addFieldDefinition(23, 1, $fieldDef); + + $this->assertEquals( + 42, + $fieldDef->id + ); + } + + public function testGetContentCount() + { + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('countInstancesOfType') + ->with( + $this->equalTo(23) + )->will( + $this->returnValue(42) + ); + + $handler = $this->getHandler(); + + $this->assertEquals( + 42, + $handler->getContentCount(23) + ); + } + + public function testRemoveFieldDefinition() + { + $storageDispatcherMock = $this->getStorageDispatcherMock(); + $storageDispatcherMock + ->expects($this->once()) + ->method('deleteFieldConstraintsData') + ->with('ezstring', 42); + + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('deleteFieldDefinition') + ->with( + $this->equalTo(23), + $this->equalTo(1), + $this->equalTo(42) + ); + + $handler = $this->getHandler(); + $handler->removeFieldDefinition(23, 1, new FieldDefinition(['id' => 42, 'fieldType' => 'ezstring'])); + } + + public function testUpdateFieldDefinition() + { + $fieldDef = new FieldDefinition(); + + $mapperMock = $this->getMapperMock( + ['toStorageFieldDefinition'] + ); + $mapperMock->expects($this->once()) + ->method('toStorageFieldDefinition') + ->with( + $this->identicalTo($fieldDef), + $this->isInstanceOf(StorageFieldDefinition::class) + ); + + $gatewayMock = $this->getGatewayMock(); + $gatewayMock->expects($this->once()) + ->method('updateFieldDefinition') + ->with( + $this->equalTo(23), + $this->equalTo(1), + $fieldDef + ); + + $storageDispatcherMock = $this->getStorageDispatcherMock(); + $storageDispatcherMock + ->expects($this->once()) + ->method('storeFieldConstraintsData') + ->with($fieldDef); + + $handler = $this->getHandler(); + $handler->updateFieldDefinition(23, 1, $fieldDef); + } + + public function testPublish() + { + $handler = $this->getPartlyMockedHandler(['load']); + $updateHandlerMock = $this->getUpdateHandlerMock(); + + $handler->expects($this->exactly(2)) + ->method('load') + ->with( + $this->equalTo(23), + $this->logicalOr( + $this->equalTo(0), + $this->equalTo(1) + ) + )->will( + $this->returnValue(new Type()) + ); + + $updateHandlerMock->expects($this->never()) + ->method('updateContentObjects'); + + $updateHandlerMock->expects($this->once()) + ->method('deleteOldType') + ->with( + $this->isInstanceOf(Type::class) + ); + $updateHandlerMock->expects($this->once()) + ->method('publishNewType') + ->with( + $this->isInstanceOf(Type::class), + $this->equalTo(0) + ); + + $handler->publish(23); + } + + public function testPublishNoOldType() + { + $handler = $this->getPartlyMockedHandler(['load']); + $updateHandlerMock = $this->getUpdateHandlerMock(); + + $handler->expects($this->at(0)) + ->method('load') + ->with( + $this->equalTo(23), + $this->equalTo(1) + )->will( + $this->returnValue(new Type()) + ); + + $handler->expects($this->at(1)) + ->method('load') + ->with( + $this->equalTo(23), + $this->equalTo(0) + )->will( + $this->throwException(new Exception\TypeNotFound(23, 0)) + ); + + $updateHandlerMock->expects($this->never()) + ->method('updateContentObjects'); + $updateHandlerMock->expects($this->never()) + ->method('deleteOldType'); + $updateHandlerMock->expects($this->once()) + ->method('publishNewType') + ->with( + $this->isInstanceOf(Type::class), + $this->equalTo(0) + ); + + $handler->publish(23); + } + + /** + * Returns a handler to test, based on mock objects. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\Handler + */ + protected function getHandler() + { + return new Handler( + $this->getGatewayMock(), + $this->getMapperMock(), + $this->getUpdateHandlerMock(), + $this->getStorageDispatcherMock() + ); + } + + /** + * Returns a handler to test with $methods mocked. + * + * @param array $methods + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\Handler + */ + protected function getPartlyMockedHandler(array $methods) + { + return $this->getMockBuilder(Handler::class) + ->setMethods($methods) + ->setConstructorArgs( + [ + $this->getGatewayMock(), + $this->getMapperMock(), + $this->getUpdateHandlerMock(), + $this->getStorageDispatcherMock(), + ] + ) + ->getMock(); + } + + /** + * Returns a gateway mock. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\Gateway + */ + protected function getGatewayMock() + { + if (!isset($this->gatewayMock)) { + $this->gatewayMock = $this->getMockForAbstractClass( + Gateway::class + ); + } + + return $this->gatewayMock; + } + + /** + * Returns a mapper mock. + * + * @param array $methods + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\Mapper + */ + protected function getMapperMock($methods = []) + { + if (!isset($this->mapperMock)) { + $this->mapperMock = $this->getMockBuilder(Mapper::class) + ->disableOriginalConstructor() + ->setMethods($methods) + ->getMock(); + } + + return $this->mapperMock; + } + + /** + * Returns a Update\Handler mock. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler + */ + public function getUpdateHandlerMock() + { + if (!isset($this->updateHandlerMock)) { + $this->updateHandlerMock = $this->getMockBuilder(UpdateHandler::class) + ->disableOriginalConstructor() + ->setMethods([]) + ->getMock(); + } + + return $this->updateHandlerMock; + } + + /** + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\StorageDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject + */ + public function getStorageDispatcherMock(): StorageDispatcherInterface + { + if (!isset($this->storageDispatcherMock)) { + $this->storageDispatcherMock = $this->createMock(StorageDispatcherInterface::class); + } + + return $this->storageDispatcherMock; + } + + /** + * Returns a CreateStruct fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\CreateStruct + */ + protected function getContentTypeCreateStructFixture() + { + $struct = new CreateStruct(); + $struct->status = 1; + $struct->groupIds = [ + 42, + ]; + $struct->name = [ + 'eng-GB' => 'test name', + ]; + + $fieldDefName = new FieldDefinition(['position' => 1]); + $fieldDefShortDescription = new FieldDefinition(['position' => 2]); + + $struct->fieldDefinitions = [ + $fieldDefName, + $fieldDefShortDescription, + ]; + + return $struct; + } + + public function testRemoveContentTypeTranslation() + { + $mapperMock = $this->getMapperMock(); + $mapperMock->expects($this->once()) + ->method('createUpdateStructFromType') + ->with( + $this->isInstanceOf( + Type::class + ) + ) + ->will( + $this->returnValue(new UpdateStruct()) + ); + + $handlerMock = $this->getMockBuilder(Handler::class) + ->setMethods(['load', 'update']) + ->setConstructorArgs([ + $this->getGatewayMock(), + $mapperMock, + $this->getUpdateHandlerMock(), + $this->getStorageDispatcherMock(), + ]) + ->getMock(); + + $handlerMock->expects($this->once()) + ->method('load') + ->with( + $this->equalTo(23), + $this->equalTo(1) + ) + ->will($this->returnValue(new Type(['id' => 23]))); + + $handlerMock->expects($this->once()) + ->method('update') + ->with( + $this->equalTo(23), + $this->equalTo(1), + $this->isInstanceOf( + UpdateStruct::class + ) + ) + ->will($this->returnValue(new Type())); + + $res = $handlerMock->removeContentTypeTranslation(23, 'eng-GB'); + + $this->assertInstanceOf( + Type::class, + $res + ); + } +} + +class_alias(ContentTypeHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type\ContentTypeHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/ContentUpdater/Action/AddFieldTest.php b/tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php similarity index 90% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/ContentUpdater/Action/AddFieldTest.php rename to tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php index e0052359cb..74bb0971ee 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/ContentUpdater/Action/AddFieldTest.php +++ b/tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/AddFieldTest.php @@ -4,57 +4,57 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type\ContentUpdater\Action; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper as ContentMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageFieldValue; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\AddField; -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\Field; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action; + +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Mapper as ContentMapper; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use Ibexa\Core\Persistence\Legacy\Content\StorageHandler; +use Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\AddField; use PHPUnit\Framework\TestCase; use ReflectionObject; /** - * Test case for Content Type Updater. + * Test case for content type Updater. */ class AddFieldTest extends TestCase { /** * Content gateway mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway + * @var \Ibexa\Core\Persistence\Legacy\Content\Gateway */ protected $contentGatewayMock; /** * Content gateway mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler + * @var \Ibexa\Core\Persistence\Legacy\Content\StorageHandler */ protected $contentStorageHandlerMock; /** * FieldValue converter mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter + * @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter */ protected $fieldValueConverterMock; - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Mapper */ + /** @var \Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected $contentMapperMock; /** * AddField action to test. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\AddField + * @var \Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\AddField */ protected $addFieldAction; /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater::__construct + * @covers \Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater::__construct */ public function testConstructor() { @@ -497,7 +497,7 @@ public function testInsertExistingFieldUpdating() * @param int $versionNo * @param array $languageCodes * - * @return \eZ\Publish\SPI\Persistence\Content + * @return \Ibexa\Contracts\Core\Persistence\Content */ protected function getContentFixture($versionNo, array $languageCodes) { @@ -523,7 +523,7 @@ protected function getContentFixture($versionNo, array $languageCodes) /** * Returns a Content Gateway mock. * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Gateway + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Gateway */ protected function getContentGatewayMock() { @@ -537,7 +537,7 @@ protected function getContentGatewayMock() /** * Returns a FieldValue converter mock. * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter */ protected function getFieldValueConverterMock() { @@ -551,7 +551,7 @@ protected function getFieldValueConverterMock() /** * Returns a Content StorageHandler mock. * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\StorageHandler */ protected function getContentStorageHandlerMock() { @@ -565,7 +565,7 @@ protected function getContentStorageHandlerMock() /** * Returns a Content mapper mock. * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Mapper + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected function getContentMapperMock() { @@ -579,7 +579,7 @@ protected function getContentMapperMock() /** * Returns a FieldDefinition fixture. * - * @return \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition */ protected function getFieldDefinitionFixture() { @@ -599,7 +599,7 @@ protected function getFieldDefinitionFixture() * @param int $versionNo * @param string $languageCode * - * @return \eZ\Publish\SPI\Persistence\Content\Field + * @return \Ibexa\Contracts\Core\Persistence\Content\Field */ public function getFieldReference($id, $versionNo, $languageCode) { @@ -618,7 +618,7 @@ public function getFieldReference($id, $versionNo, $languageCode) /** * @param $methods * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\AddField + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\AddField */ protected function getMockedAction($methods = []) { @@ -637,3 +637,5 @@ protected function getMockedAction($methods = []) ->getMock(); } } + +class_alias(AddFieldTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type\ContentUpdater\Action\AddFieldTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/ContentUpdater/Action/RemoveFieldTest.php b/tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php similarity index 86% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/ContentUpdater/Action/RemoveFieldTest.php rename to tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php index 6ec40a4f72..434f513d02 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/ContentUpdater/Action/RemoveFieldTest.php +++ b/tests/lib/Persistence/Legacy/Content/Type/ContentUpdater/Action/RemoveFieldTest.php @@ -4,47 +4,44 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type\ContentUpdater\Action; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper as ContentMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\RemoveField; -use eZ\Publish\SPI\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Core\Persistence\Legacy\Content\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Mapper as ContentMapper; +use Ibexa\Core\Persistence\Legacy\Content\StorageHandler; +use Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\RemoveField; use PHPUnit\Framework\TestCase; /** - * Test case for Content Type Updater. + * @covers \Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\RemoveField */ class RemoveFieldTest extends TestCase { /** * Content gateway mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway + * @var \Ibexa\Core\Persistence\Legacy\Content\Gateway */ protected $contentGatewayMock; /** * Content gateway mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler + * @var \Ibexa\Core\Persistence\Legacy\Content\StorageHandler */ protected $contentStorageHandlerMock; - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Mapper */ + /** @var \Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected $contentMapperMock; /** * RemoveField action to test. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\RemoveField + * @var \Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\RemoveField */ protected $removeFieldAction; - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\RemoveField::apply - */ public function testApplySingleVersionSingleTranslation() { $contentId = 42; @@ -93,9 +90,6 @@ public function testApplySingleVersionSingleTranslation() $action->apply($contentId); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\RemoveField::apply - */ public function testApplyMultipleVersionsSingleTranslation() { $contentId = 42; @@ -167,9 +161,6 @@ public function testApplyMultipleVersionsSingleTranslation() $action->apply($contentId); } - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\RemoveField::apply - */ public function testApplyMultipleVersionsMultipleTranslations() { $contentId = 42; @@ -287,7 +278,7 @@ protected function getContentFixture(int $versionNo, array $languageCodes): Cont /** * Returns a Content Gateway mock. * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Gateway + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Gateway */ protected function getContentGatewayMock() { @@ -301,7 +292,7 @@ protected function getContentGatewayMock() /** * Returns a Content StorageHandler mock. * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\StorageHandler */ protected function getContentStorageHandlerMock() { @@ -315,7 +306,7 @@ protected function getContentStorageHandlerMock() /** * Returns a Content mapper mock. * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Persistence\Legacy\Content\Mapper + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected function getContentMapperMock() { @@ -329,7 +320,7 @@ protected function getContentMapperMock() /** * Returns a FieldDefinition fixture. * - * @return \eZ\Publish\SPI\Persistence\Content\Type\FieldDefinition + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition */ protected function getFieldDefinitionFixture() { @@ -344,7 +335,7 @@ protected function getFieldDefinitionFixture() /** * Returns the RemoveField action to test. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\RemoveField + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action\RemoveField */ protected function getRemoveFieldAction() { @@ -360,3 +351,5 @@ protected function getRemoveFieldAction() return $this->removeFieldAction; } } + +class_alias(RemoveFieldTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type\ContentUpdater\Action\RemoveFieldTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/ContentUpdaterTest.php b/tests/lib/Persistence/Legacy/Content/Type/ContentUpdaterTest.php similarity index 77% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/ContentUpdaterTest.php rename to tests/lib/Persistence/Legacy/Content/Type/ContentUpdaterTest.php index cc60e44499..13eb38a168 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/ContentUpdaterTest.php +++ b/tests/lib/Persistence/Legacy/Content/Type/ContentUpdaterTest.php @@ -4,65 +4,61 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type; - -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\Converter; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; -use eZ\Publish\Core\Persistence\Legacy\Content\Gateway; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater; -use eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action; -use eZ\Publish\SPI\Persistence\Content\Type; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Type; + +use Ibexa\Contracts\Core\Persistence\Content\Type; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; +use Ibexa\Core\Persistence\Legacy\Content\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Mapper; +use Ibexa\Core\Persistence\Legacy\Content\StorageHandler; +use Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater; +use Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater\Action; use PHPUnit\Framework\TestCase; /** - * Test case for Content Type Updater. + * @covers \Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater */ class ContentUpdaterTest extends TestCase { /** * Content gateway mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Gateway + * @var \Ibexa\Core\Persistence\Legacy\Content\Gateway */ protected $contentGatewayMock; /** * FieldValue converter registry mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry + * @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry */ protected $converterRegistryMock; /** * Search handler mock. * - * @var \eZ\Publish\Core\Search\Legacy\Content\Handler + * @var \Ibexa\Core\Search\Legacy\Content\Handler */ protected $searchHandlerMock; /** * Content StorageHandler mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler + * @var \Ibexa\Core\Persistence\Legacy\Content\StorageHandler */ protected $contentStorageHandlerMock; /** * Content Updater to test. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater + * @var \Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater */ protected $contentUpdater; - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Mapper */ + /** @var \Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected $contentMapperMock; - /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater::determineActions - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater::hasFieldDefinition - */ public function testDetermineActions() { $fromType = $this->getFromTypeFixture(); @@ -147,7 +143,7 @@ public function testApplyUpdates() /** * Returns a fixture for the from Type. * - * @return \eZ\Publish\SPI\Persistence\Content\Type + * @return \Ibexa\Contracts\Core\Persistence\Content\Type */ protected function getFromTypeFixture() { @@ -171,7 +167,7 @@ protected function getFromTypeFixture() /** * Returns a fixture for the to Type. * - * @return \eZ\Publish\SPI\Persistence\Content\Type + * @return \Ibexa\Contracts\Core\Persistence\Content\Type */ protected function getToTypeFixture() { @@ -191,7 +187,7 @@ protected function getToTypeFixture() /** * Returns a Content Gateway mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Gateway + * @return \Ibexa\Core\Persistence\Legacy\Content\Gateway */ protected function getContentGatewayMock() { @@ -205,7 +201,7 @@ protected function getContentGatewayMock() /** * Returns a FieldValue Converter registry mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry + * @return \Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry */ protected function getConverterRegistryMock() { @@ -219,7 +215,7 @@ protected function getConverterRegistryMock() /** * Returns a Content StorageHandler mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\StorageHandler + * @return \Ibexa\Core\Persistence\Legacy\Content\StorageHandler */ protected function getContentStorageHandlerMock() { @@ -233,7 +229,7 @@ protected function getContentStorageHandlerMock() /** * Returns a Content mapper mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Mapper + * @return \Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected function getContentMapperMock() { @@ -247,7 +243,7 @@ protected function getContentMapperMock() /** * Returns the content updater to test. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Type\ContentUpdater + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater */ protected function getContentUpdater() { @@ -263,3 +259,5 @@ protected function getContentUpdater() return $this->contentUpdater; } } + +class_alias(ContentUpdaterTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type\ContentUpdaterTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..f06999a73e --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Type/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,1164 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Type\Gateway; + +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\Type; +// For SORT_ORDER_* constants +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\Type\Group; +use Ibexa\Contracts\Core\Persistence\Content\Type\Group\UpdateStruct as GroupUpdateStruct; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase; +use Ibexa\Tests\Core\Persistence\Legacy\Content\LanguageAwareTestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase + */ +class DoctrineDatabaseTest extends LanguageAwareTestCase +{ + /** + * The DoctrineDatabase gateway to test. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase + */ + protected $gateway; + + protected function setUp(): void + { + parent::setUp(); + + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/languages.php'); + } + + public function testInsertGroup() + { + $gateway = $this->getGateway(); + + $group = $this->getGroupFixture(); + + $id = $gateway->insertGroup($group); + + $this->assertQueryResult( + [ + [ + 'id' => '1', + 'created' => '1032009743', + 'creator_id' => '14', + 'modified' => '1033922120', + 'modifier_id' => '14', + 'name' => 'Media', + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select( + 'id', + 'created', + 'creator_id', + 'modified', + 'modifier_id', + 'name' + ) + ->from('ezcontentclassgroup') + ); + } + + /** + * Returns a Group fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Group + */ + protected function getGroupFixture() + { + $group = new Group(); + + $group->name = [ + 'always-available' => 'eng-GB', + 'eng-GB' => 'Media', + ]; + $group->description = [ + 'always-available' => 'eng-GB', + 'eng-GB' => '', + ]; + $group->identifier = 'Media'; + $group->created = 1032009743; + $group->modified = 1033922120; + $group->creatorId = 14; + $group->modifierId = 14; + + return $group; + } + + public function testUpdateGroup() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_groups.php' + ); + + $gateway = $this->getGateway(); + + $struct = $this->getGroupUpdateStructFixture(); + + $res = $gateway->updateGroup($struct); + + $this->assertQueryResult( + [ + ['4'], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('COUNT(*)') + ->from('ezcontentclassgroup') + ); + + $q = $this->getDatabaseConnection()->createQueryBuilder(); + $q + ->select( + 'id', + 'created', + 'creator_id', + 'modified', + 'modifier_id', + 'name' + ) + ->from('ezcontentclassgroup') + ->orderBy('id'); + $this->assertQueryResult( + [ + [ + 'id' => 1, + 'created' => 1031216928, + 'creator_id' => 14, + 'modified' => 1033922106, + 'modifier_id' => 14, + 'name' => 'Content', + ], + [ + 'id' => 2, + 'created' => 1031216941, + 'creator_id' => 14, + 'modified' => 1311454096, + 'modifier_id' => 23, + 'name' => 'UpdatedGroup', + ], + [ + 'id' => 3, + 'created' => 1032009743, + 'creator_id' => 14, + 'modified' => 1033922120, + 'modifier_id' => 14, + 'name' => 'Media', + ], + [ + 'id' => 4, + 'created' => 1634895910, + 'creator_id' => 14, + 'modified' => 1634895910, + 'modifier_id' => 14, + 'name' => 'System', + ], + ], + $q + ); + } + + /** + * Returns a Group update struct fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Group\UpdateStruct + */ + protected function getGroupUpdateStructFixture() + { + $struct = new GroupUpdateStruct(); + + $struct->id = 2; + $struct->name = [ + 'always-available' => 'eng-GB', + 'eng-GB' => 'UpdatedGroupName', + ]; + $struct->description = [ + 'always-available' => 'eng-GB', + 'eng-GB' => '', + ]; + $struct->identifier = 'UpdatedGroup'; + $struct->modified = 1311454096; + $struct->modifierId = 23; + + return $struct; + } + + public function testCountTypesInGroup() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + + $this->assertEquals( + 3, + $gateway->countTypesInGroup(1) + ); + $this->assertEquals( + 0, + $gateway->countTypesInGroup(23) + ); + } + + public function testCountGroupsForType() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + + $this->assertEquals( + 1, + $gateway->countGroupsForType(1, 1) + ); + $this->assertEquals( + 0, + $gateway->countGroupsForType(23, 0) + ); + } + + public function testDeleteGroup() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_groups.php' + ); + + $gateway = $this->getGateway(); + + $gateway->deleteGroup(2); + + $this->assertQueryResult( + [ + ['1'], + ['3'], + ['4'], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('id') + ->from('ezcontentclassgroup') + ); + } + + public function testLoadGroupData() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_groups.php' + ); + + $gateway = $this->getGateway(); + $data = $gateway->loadGroupData([2]); + + $this->assertEquals( + [ + [ + 'created' => '1031216941', + 'creator_id' => '14', + 'id' => '2', + 'modified' => '1033922113', + 'modifier_id' => '14', + 'name' => 'Users', + 'is_system' => '0', + ], + ], + $data + ); + } + + public function testLoadGroupDataByIdentifier() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_groups.php' + ); + + $gateway = $this->getGateway(); + $data = $gateway->loadGroupDataByIdentifier('Users'); + + $this->assertEquals( + [ + [ + 'created' => '1031216941', + 'creator_id' => '14', + 'id' => '2', + 'modified' => '1033922113', + 'modifier_id' => '14', + 'name' => 'Users', + 'is_system' => '0', + ], + ], + $data + ); + } + + public function testLoadAllGroupsData() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_groups.php' + ); + + $gateway = $this->getGateway(); + $data = $gateway->loadAllGroupsData(); + + $this->assertCount( + 3, + $data + ); + + $this->assertEquals( + [ + 'created' => '1031216941', + 'creator_id' => '14', + 'id' => '2', + 'modified' => '1033922113', + 'modifier_id' => '14', + 'name' => 'Users', + 'is_system' => '0', + ], + $data[1] + ); + } + + public function testLoadTypesDataForGroup() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + $rows = $gateway->loadTypesDataForGroup(1, 0); + + $this->assertCount( + 4, + $rows + ); + } + + public function testLoadTypeData() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + $rows = $gateway->loadTypeData(1, 0); + + $this->assertCount( + 3, + $rows + ); + $this->assertCount( + 50, + $rows[0] + ); + + /* + * Store mapper fixture + * + file_put_contents( + dirname( __DIR__ ) . '/_fixtures/map_load_type.php', + "<?php\n\nreturn " . var_export( $rows, true ) . ";\n" + ); + */ + } + + public function testLoadTypeDataByIdentifier() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + $rows = $gateway->loadTypeDataByIdentifier('folder', 0); + + $this->assertCount( + 3, + $rows + ); + $this->assertCount( + 50, + $rows[0] + ); + } + + public function testLoadTypeDataByRemoteId() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + $rows = $gateway->loadTypeDataByRemoteId('a3d405b81be900468eb153d774f4f0d2', 0); + + $this->assertCount( + 3, + $rows + ); + $this->assertCount( + 50, + $rows[0] + ); + } + + /** + * Returns the expected data from creating a type. + * + * @return string[][] + */ + public static function getTypeCreationExpectations() + { + return [ + ['always_available', 0], + ['contentobject_name', '<short_name|name>'], + ['created', '1024392098'], + ['creator_id', '14'], + ['identifier', 'folder'], + ['initial_language_id', '2'], + ['is_container', '1'], + ['language_mask', 7], + ['modified', '1082454875'], + ['modifier_id', '14'], + ['remote_id', 'a3d405b81be900468eb153d774f4f0d2'], + ['serialized_description_list', 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}'], + ['serialized_name_list', 'a:3:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:6:"Folder";s:6:"eng-GB";s:11:"Folder (GB)";}'], + ['sort_field', 7], + ['sort_order', 1], + ['url_alias_name', ''], + ['version', '0'], + ]; + } + + /** + * @dataProvider getTypeCreationExpectations + */ + public function testInsertType($column, $expectation) + { + $gateway = $this->getGateway(); + $type = $this->getTypeFixture(); + + $gateway->insertType($type); + + $this->assertQueryResult( + [[$expectation]], + $this->getDatabaseConnection()->createQueryBuilder() + ->select($column) + ->from('ezcontentclass'), + 'Inserted Type data incorrect in column ' . $column + ); + } + + /** + * Returns the data expected to be inserted in ezcontentclass_name. + * + * @return string[][] + */ + public static function getTypeCreationContentClassNameExpectations() + { + return [ + ['contentclass_version', [0, 0]], + ['language_id', [3, 4]], + ['language_locale', ['eng-US', 'eng-GB']], + ['name', ['Folder', 'Folder (GB)']], + ]; + } + + /** + * @dataProvider getTypeCreationContentClassNameExpectations + */ + public function testInsertTypeContentClassName($column, $expectation) + { + $gateway = $this->getGateway(); + $type = $this->getTypeFixture(); + + $gateway->insertType($type); + + $this->assertQueryResult( + array_map( + static function ($value) { + return [$value]; + }, + $expectation + ), + $this->getDatabaseConnection()->createQueryBuilder() + ->select($column) + ->from('ezcontentclass_name'), + 'Inserted Type data incorrect in column ' . $column + ); + } + + /** + * Returns a Type fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + protected function getTypeFixture() + { + $type = new Type(); + + $type->status = 0; + $type->name = [ + 'always-available' => 'eng-US', + 'eng-US' => 'Folder', + 'eng-GB' => 'Folder (GB)', + ]; + $type->description = [ + 0 => '', + 'always-available' => false, + ]; + $type->identifier = 'folder'; + $type->created = 1024392098; + $type->modified = 1082454875; + $type->creatorId = 14; + $type->modifierId = 14; + $type->remoteId = 'a3d405b81be900468eb153d774f4f0d2'; + $type->urlAliasSchema = ''; + $type->nameSchema = '<short_name|name>'; + $type->isContainer = true; + $type->initialLanguageId = 2; + $type->sortField = Location::SORT_FIELD_CLASS_NAME; + $type->sortOrder = Location::SORT_ORDER_ASC; + $type->languageCodes = [ + 'eng-US', + 'eng-GB', + ]; + + return $type; + } + + public function testInsertFieldDefinition() + { + $gateway = $this->getGateway(); + + $field = $this->getFieldDefinitionFixture(); + $storageField = $this->getStorageFieldDefinitionFixture(); + + $gateway->insertFieldDefinition(23, 1, $field, $storageField); + + $this->assertQueryResult( + [ + [ + 'contentclass_id' => '23', + 'serialized_name_list' => 'a:2:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:11:"Description";}', + 'serialized_description_list' => 'a:2:{s:16:"always-available";s:6:"eng-GB";s:6:"eng-GB";s:16:"Some description";}', + 'identifier' => 'description', + 'category' => 'meta', + 'placement' => '4', + 'data_type_string' => 'ezstring', + 'can_translate' => '1', + 'is_required' => '1', + 'is_information_collector' => '1', + 'version' => '1', + + 'data_float1' => '0.1', + 'data_float2' => '0.2', + 'data_float3' => '0.3', + 'data_float4' => '0.4', + 'data_int1' => '1', + 'data_int2' => '2', + 'data_int3' => '3', + 'data_int4' => '4', + 'data_text1' => 'a', + 'data_text2' => 'b', + 'data_text3' => 'c', + 'data_text4' => 'd', + 'data_text5' => 'e', + 'serialized_data_text' => 'a:2:{i:0;s:3:"foo";i:1;s:3:"bar";}', + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select( + 'contentclass_id', + 'serialized_name_list', + 'serialized_description_list', + 'identifier', + 'category', + 'placement', + 'data_type_string', + 'can_translate', + 'is_required', + 'is_information_collector', + 'version', + 'data_float1', + 'data_float2', + 'data_float3', + 'data_float4', + 'data_int1', + 'data_int2', + 'data_int3', + 'data_int4', + 'data_text1', + 'data_text2', + 'data_text3', + 'data_text4', + 'data_text5', + 'serialized_data_text' + ) + ->from('ezcontentclass_attribute'), + 'FieldDefinition not inserted correctly' + ); + } + + /** + * Returns a FieldDefinition fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition + */ + protected function getFieldDefinitionFixture() + { + $field = new FieldDefinition(); + + $field->name = [ + 'always-available' => 'eng-US', + 'eng-US' => 'Description', + ]; + $field->description = [ + 'always-available' => 'eng-GB', + 'eng-GB' => 'Some description', + ]; + $field->identifier = 'description'; + $field->fieldGroup = 'meta'; + $field->position = 4; + $field->fieldType = 'ezstring'; + $field->isTranslatable = true; + $field->isRequired = true; + $field->isInfoCollector = true; + // $field->fieldTypeConstraints ??? + $field->defaultValue = [ + 0 => '', + 'always-available' => false, + ]; + + return $field; + } + + /** + * Returns a StorageFieldDefinition fixture. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition + */ + protected function getStorageFieldDefinitionFixture() + { + $fieldDef = new StorageFieldDefinition(); + + $fieldDef->dataFloat1 = 0.1; + $fieldDef->dataFloat2 = 0.2; + $fieldDef->dataFloat3 = 0.3; + $fieldDef->dataFloat4 = 0.4; + + $fieldDef->dataInt1 = 1; + $fieldDef->dataInt2 = 2; + $fieldDef->dataInt3 = 3; + $fieldDef->dataInt4 = 4; + + $fieldDef->dataText1 = 'a'; + $fieldDef->dataText2 = 'b'; + $fieldDef->dataText3 = 'c'; + $fieldDef->dataText4 = 'd'; + $fieldDef->dataText5 = 'e'; + + $fieldDef->serializedDataText = [ + 'foo', 'bar', + ]; + + return $fieldDef; + } + + public function testDeleteFieldDefinition() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + + $gateway->deleteFieldDefinition(1, 0, 119); + + $this->assertQueryResult( + [[5]], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('COUNT(*)') + ->from('ezcontentclass_attribute') + ); + } + + public function testUpdateFieldDefinition() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + $fieldDefinitionFixture = $this->getFieldDefinitionFixture(); + $fieldDefinitionFixture->id = 160; + $storageFieldDefinitionFixture = $this->getStorageFieldDefinitionFixture(); + + $gateway = $this->getGateway(); + $gateway->updateFieldDefinition(2, 0, $fieldDefinitionFixture, $storageFieldDefinitionFixture); + + $this->assertQueryResult( + [ + // "random" sample + [ + 'category' => 'meta', + 'contentclass_id' => '2', + 'version' => '0', + 'data_type_string' => 'ezstring', + 'identifier' => 'description', + 'is_information_collector' => '1', + 'placement' => '4', + 'serialized_description_list' => 'a:2:{s:16:"always-available";s:6:"eng-GB";s:6:"eng-GB";s:16:"Some description";}', + + 'data_float1' => '0.1', + 'data_float2' => '0.2', + 'data_float3' => '0.3', + 'data_float4' => '0.4', + 'data_int1' => '1', + 'data_int2' => '2', + 'data_int3' => '3', + 'data_int4' => '4', + 'data_text1' => 'a', + 'data_text2' => 'b', + 'data_text3' => 'c', + 'data_text4' => 'd', + 'data_text5' => 'e', + 'serialized_data_text' => 'a:2:{i:0;s:3:"foo";i:1;s:3:"bar";}', + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select( + 'category', + 'contentclass_id', + 'version', + 'data_type_string', + 'identifier', + 'is_information_collector', + 'placement', + 'serialized_description_list', + 'data_float1', + 'data_float2', + 'data_float3', + 'data_float4', + 'data_int1', + 'data_int2', + 'data_int3', + 'data_int4', + 'data_text1', + 'data_text2', + 'data_text3', + 'data_text4', + 'data_text5', + 'serialized_data_text' + ) + ->from('ezcontentclass_attribute') + ->where('id = 160'), + 'FieldDefinition not updated correctly' + ); + } + + public function testInsertGroupAssignment() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_groups.php' + ); + + $gateway = $this->getGateway(); + + $gateway->insertGroupAssignment(3, 42, 1); + + $this->assertQueryResult( + [ + [ + 'contentclass_id' => '42', + 'contentclass_version' => '1', + 'group_id' => '3', + 'group_name' => 'Media', + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select( + 'contentclass_id', + 'contentclass_version', + 'group_id', + 'group_name' + )->from('ezcontentclass_classgroup') + ); + } + + public function testDeleteGroupAssignment() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + + $gateway->deleteGroupAssignment(1, 1, 0); + + $this->assertQueryResult( + [['1']], + $this->getDatabaseConnection()->createQueryBuilder() + ->select( + 'COUNT(*)' + )->from('ezcontentclass_classgroup') + ->where('contentclass_id = 1') + ); + } + + /** + * @dataProvider getTypeUpdateExpectations + */ + public function testUpdateType($fieldName, $expectedValue) + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + + $type = $this->getUpdateTypeFixture(); + + $gateway->updateType(1, 0, $type); + + $this->assertQueryResult( + [ + [ + $fieldName => $expectedValue, + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select( + $fieldName + )->from('ezcontentclass') + ->where('id = 1 AND version = 0'), + "Incorrect value stored for '{$fieldName}'." + ); + } + + public function testUpdateTypeName() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + + $type = $this->getUpdateTypeFixture(); + + $gateway->updateType(1, 0, $type); + + $this->assertQueryResult( + [ + [ + 'contentclass_id' => 1, + 'contentclass_version' => 0, + 'language_id' => 3, + 'language_locale' => 'eng-US', + 'name' => 'New Folder', + ], + [ + 'contentclass_id' => 1, + 'contentclass_version' => 0, + 'language_id' => 4, + 'language_locale' => 'eng-GB', + 'name' => 'New Folder for you', + ], + ], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('*') + ->from('ezcontentclass_name') + ->where('contentclass_id = 1 AND contentclass_version = 0') + ); + } + + /** + * Returns expected data after update. + * + * Data provider for {@link testUpdateType()}. + * + * @return string[][] + */ + public static function getTypeUpdateExpectations() + { + return [ + ['serialized_name_list', 'a:3:{s:16:"always-available";s:6:"eng-US";s:6:"eng-US";s:10:"New Folder";s:6:"eng-GB";s:18:"New Folder for you";}'], + ['serialized_description_list', 'a:2:{i:0;s:0:"";s:16:"always-available";b:0;}'], + ['identifier', 'new_folder'], + ['modified', '1311621548'], + ['modifier_id', '42'], + ['remote_id', 'foobar'], + ['url_alias_name', 'some scheke'], + ['contentobject_name', '<short_name>'], + ['is_container', '0'], + ['initial_language_id', '23'], + ['sort_field', '3'], + ['sort_order', '0'], + ['always_available', '1'], + ]; + } + + /** + * Returns a {@see \Ibexa\Contracts\Core\Persistence\Content\Type} fixture for update operation. + */ + protected function getUpdateTypeFixture(): Type + { + $type = new Type(); + + $type->name = [ + 'always-available' => 'eng-US', + 'eng-US' => 'New Folder', + 'eng-GB' => 'New Folder for you', + ]; + $type->description = [ + 0 => '', + 'always-available' => false, + ]; + $type->identifier = 'new_folder'; + $type->modified = 1311621548; + $type->modifierId = 42; + $type->remoteId = 'foobar'; + $type->urlAliasSchema = 'some scheke'; + $type->nameSchema = '<short_name>'; + $type->isContainer = false; + $type->initialLanguageId = 23; + $type->sortField = 3; + $type->sortOrder = Location::SORT_ORDER_DESC; + $type->defaultAlwaysAvailable = true; + + return $type; + } + + public function testCountInstancesOfTypeExist() + { + $this->insertDatabaseFixture( + // Fixture for content objects + __DIR__ . '/../../_fixtures/contentobjects.php' + ); + + $gateway = $this->getGateway(); + $res = $gateway->countInstancesOfType(3, 0); + + $this->assertEquals( + 6, + $res + ); + } + + public function testCountInstancesOfTypeNotExist() + { + $this->insertDatabaseFixture( + // Fixture for content objects + __DIR__ . '/../../_fixtures/contentobjects.php' + ); + + $gateway = $this->getGateway(); + $res = $gateway->countInstancesOfType(23422342, 1); + + $this->assertEquals( + 0, + $res + ); + } + + public function testDeleteFieldDefinitionsForTypeExisting() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + + $gateway->deleteFieldDefinitionsForType(1, 0); + + $countAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); + $countAffectedAttr + ->select('COUNT(*)') + ->from('ezcontentclass_attribute') + ->where( + $countAffectedAttr->expr()->eq( + 'contentclass_id', + 1 + ) + ); + // 1 left with version 1 + $this->assertQueryResult( + [[1]], + $countAffectedAttr + ); + + $countNotAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); + $countNotAffectedAttr->select('COUNT(*)') + ->from('ezcontentclass_attribute'); + + $this->assertQueryResult( + [[2]], + $countNotAffectedAttr + ); + } + + public function testDeleteFieldDefinitionsForTypeNotExisting() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + + $gateway->deleteFieldDefinitionsForType(23, 1); + + $countNotAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); + $countNotAffectedAttr->select('COUNT(*)') + ->from('ezcontentclass_attribute'); + + $this->assertQueryResult( + [[5]], + $countNotAffectedAttr + ); + } + + public function testDeleteGroupAssignmentsForTypeExisting() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + + $gateway->deleteGroupAssignmentsForType(1, 0); + + $countAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); + $countAffectedAttr->select('COUNT(*)') + ->from('ezcontentclass_classgroup'); + + $this->assertQueryResult( + [[2]], + $countAffectedAttr + ); + } + + public function testDeleteGroupAssignmentsForTypeNotExisting() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + + $gateway->deleteType(23, 1); + + $countAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); + $countAffectedAttr->select('COUNT(*)') + ->from('ezcontentclass_classgroup'); + + $this->assertQueryResult( + [[3]], + $countAffectedAttr + ); + } + + public function testDeleteTypeExisting() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + + $gateway->deleteType(1, 0); + + $countAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); + $countAffectedAttr->select('COUNT(*)') + ->from('ezcontentclass'); + + $this->assertQueryResult( + [[1]], + $countAffectedAttr + ); + } + + public function testDeleteTypeNotExisting() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/existing_types.php' + ); + + $gateway = $this->getGateway(); + + $gateway->deleteType(23, 1); + + $countAffectedAttr = $this->getDatabaseConnection()->createQueryBuilder(); + $countAffectedAttr->select('COUNT(*)') + ->from('ezcontentclass'); + + $this->assertQueryResult( + [[2]], + $countAffectedAttr + ); + } + + public function testPublishTypeAndFields() + { + $this->insertDatabaseFixture( + __DIR__ . '/_fixtures/type_to_publish.php' + ); + + $gateway = $this->getGateway(); + $gateway->publishTypeAndFields(1, 1, 0); + + $this->assertQueryResult( + [[1]], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('COUNT( * )') + ->from('ezcontentclass') + ->where('id = 1 AND version = 0') + ); + + $this->assertQueryResult( + [[2]], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('COUNT( * )') + ->from('ezcontentclass_classgroup') + ->where('contentclass_id = 1 AND contentclass_version = 0') + ); + + $this->assertQueryResult( + [[3]], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('COUNT( * )') + ->from('ezcontentclass_attribute') + ->where('contentclass_id = 1 AND version = 0') + ); + + $this->assertQueryResult( + [[1]], + $this->getDatabaseConnection()->createQueryBuilder() + ->select('COUNT( * )') + ->from('ezcontentclass_name') + ->where('contentclass_id = 1 AND contentclass_version = 0') + ); + } + + /** + * Return the DoctrineDatabase gateway to test. + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function getGateway(): DoctrineDatabase + { + if (!isset($this->gateway)) { + $this->gateway = new DoctrineDatabase( + $this->getDatabaseConnection(), + $this->getSharedGateway(), + $this->getLanguageMaskGenerator() + ); + } + + return $this->gateway; + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type\Gateway\DoctrineDatabaseTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Type/Gateway/_fixtures/existing_groups.php b/tests/lib/Persistence/Legacy/Content/Type/Gateway/_fixtures/existing_groups.php new file mode 100644 index 0000000000..71b98b5673 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Type/Gateway/_fixtures/existing_groups.php @@ -0,0 +1,46 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezcontentclassgroup' => [ + [ + 'created' => 1031216928, + 'creator_id' => 14, + 'id' => 1, + 'modified' => 1033922106, + 'modifier_id' => 14, + 'name' => 'Content', + 'is_system' => 0, + ], + [ + 'created' => 1031216941, + 'creator_id' => 14, + 'id' => 2, + 'modified' => 1033922113, + 'modifier_id' => 14, + 'name' => 'Users', + 'is_system' => 0, + ], + [ + 'created' => 1032009743, + 'creator_id' => 14, + 'id' => 3, + 'modified' => 1033922120, + 'modifier_id' => 14, + 'name' => 'Media', + 'is_system' => 0, + ], + [ + 'created' => 1634895910, + 'creator_id' => 14, + 'id' => 4, + 'modified' => 1634895910, + 'modifier_id' => 14, + 'name' => 'System', + 'is_system' => 1, + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/_fixtures/existing_types.php b/tests/lib/Persistence/Legacy/Content/Type/Gateway/_fixtures/existing_types.php similarity index 97% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/_fixtures/existing_types.php rename to tests/lib/Persistence/Legacy/Content/Type/Gateway/_fixtures/existing_types.php index acb30a5508..b31c5ccc0c 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/_fixtures/existing_types.php +++ b/tests/lib/Persistence/Legacy/Content/Type/Gateway/_fixtures/existing_types.php @@ -1,5 +1,9 @@ <?php +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 'ezcontentclass' => [ // loaded diff --git a/tests/lib/Persistence/Legacy/Content/Type/Gateway/_fixtures/languages.php b/tests/lib/Persistence/Legacy/Content/Type/Gateway/_fixtures/languages.php new file mode 100644 index 0000000000..4f65791842 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Type/Gateway/_fixtures/languages.php @@ -0,0 +1,22 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezcontent_language' => [ + 0 => [ + 'disabled' => '0', + 'id' => '2', + 'locale' => 'eng-US', + 'name' => 'English (American)', + ], + 1 => [ + 'disabled' => '0', + 'id' => '4', + 'locale' => 'eng-GB', + 'name' => 'English (United Kingdom)', + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/_fixtures/type_to_publish.php b/tests/lib/Persistence/Legacy/Content/Type/Gateway/_fixtures/type_to_publish.php similarity index 97% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/_fixtures/type_to_publish.php rename to tests/lib/Persistence/Legacy/Content/Type/Gateway/_fixtures/type_to_publish.php index 5b27e8cb19..1d98bfe967 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/Gateway/_fixtures/type_to_publish.php +++ b/tests/lib/Persistence/Legacy/Content/Type/Gateway/_fixtures/type_to_publish.php @@ -1,5 +1,9 @@ <?php +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 'ezcontentclass' => [ [ diff --git a/tests/lib/Persistence/Legacy/Content/Type/MapperTest.php b/tests/lib/Persistence/Legacy/Content/Type/MapperTest.php new file mode 100644 index 0000000000..ac0c855526 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Type/MapperTest.php @@ -0,0 +1,516 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Type; + +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\Type; +use Ibexa\Contracts\Core\Persistence\Content\Type\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Contracts\Core\Persistence\Content\Type\Group; +use Ibexa\Contracts\Core\Persistence\Content\Type\Group\CreateStruct as GroupCreateStruct; +// Needed for $sortOrder and $sortField properties +use Ibexa\Contracts\Core\Persistence\Content\Type\UpdateStruct; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; +use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\Type\Mapper; +use Ibexa\Core\Persistence\Legacy\Content\Type\StorageDispatcherInterface; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Type\Mapper + */ +class MapperTest extends TestCase +{ + public function testCreateGroupFromCreateStruct() + { + $createStruct = $this->getGroupCreateStructFixture(); + + $mapper = new Mapper( + $this->getConverterRegistryMock(), + $this->getMaskGeneratorMock(), + $this->getStorageDispatcherMock() + ); + + $group = $mapper->createGroupFromCreateStruct($createStruct); + + $this->assertInstanceOf( + Group::class, + $group + ); + $this->assertPropertiesCorrect( + [ + 'id' => null, + 'name' => [ + 'eng-GB' => 'Media', + ], + 'description' => [], + 'identifier' => 'Media', + 'created' => 1032009743, + 'modified' => 1033922120, + 'creatorId' => 14, + 'modifierId' => 14, + ], + $group + ); + } + + /** + * Returns a GroupCreateStruct fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Group\CreateStruct + */ + protected function getGroupCreateStructFixture() + { + $struct = new GroupCreateStruct(); + + $struct->name = [ + 'eng-GB' => 'Media', + ]; + $struct->description = []; + $struct->identifier = 'Media'; + $struct->created = 1032009743; + $struct->modified = 1033922120; + $struct->creatorId = 14; + $struct->modifierId = 14; + + return $struct; + } + + public function testTypeFromCreateStruct() + { + $struct = $this->getContentTypeCreateStructFixture(); + + $mapper = new Mapper( + $this->getConverterRegistryMock(), + $this->getMaskGeneratorMock(), + $this->getStorageDispatcherMock() + ); + $type = $mapper->createTypeFromCreateStruct($struct); + + foreach ($struct as $propName => $propVal) { + $this->assertEquals( + $struct->$propName, + $type->$propName, + "Property \${$propName} not equal" + ); + } + } + + public function testTypeFromUpdateStruct() + { + $struct = $this->getContentTypeUpdateStructFixture(); + + $mapper = new Mapper( + $this->getConverterRegistryMock(), + $this->getMaskGeneratorMock(), + $this->getStorageDispatcherMock() + ); + $type = $mapper->createTypeFromUpdateStruct($struct); + + $this->assertStructsEqual( + $struct, + $type + ); + } + + /** + * Returns a CreateStruct fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\CreateStruct + */ + protected function getContentTypeCreateStructFixture() + { + // Taken from example DB + $struct = new CreateStruct(); + $struct->name = [ + 'eng-US' => 'Folder', + ]; + $struct->status = 0; + $struct->description = []; + $struct->identifier = 'folder'; + $struct->created = 1024392098; + $struct->modified = 1082454875; + $struct->creatorId = 14; + $struct->modifierId = 14; + $struct->remoteId = 'a3d405b81be900468eb153d774f4f0d2'; + $struct->urlAliasSchema = ''; + $struct->nameSchema = '<short_name|name>'; + $struct->isContainer = true; + $struct->initialLanguageId = 2; + $struct->sortField = Location::SORT_FIELD_MODIFIED_SUBNODE; + $struct->sortOrder = Location::SORT_ORDER_ASC; + $struct->defaultAlwaysAvailable = true; + + $struct->groupIds = [ + 1, + ]; + + $fieldDefName = new FieldDefinition(); + + $fieldDefShortDescription = new FieldDefinition(); + + $struct->fieldDefinitions = [ + $fieldDefName, + $fieldDefShortDescription, + ]; + + return $struct; + } + + /** + * Returns a CreateStruct fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\UpdateStruct + */ + protected function getContentTypeUpdateStructFixture(): UpdateStruct + { + // Taken from example DB + $struct = new UpdateStruct(); + $struct->name = [ + 'eng-US' => 'Folder', + ]; + $struct->description = []; + $struct->identifier = 'folder'; + $struct->modified = 1082454875; + $struct->modifierId = 14; + $struct->remoteId = md5(microtime() . uniqid()); + $struct->urlAliasSchema = ''; + $struct->nameSchema = '<short_name|name>'; + $struct->isContainer = true; + $struct->initialLanguageId = 2; + $struct->sortField = Location::SORT_FIELD_MODIFIED_SUBNODE; + $struct->sortOrder = Location::SORT_ORDER_ASC; + $struct->defaultAlwaysAvailable = true; + + return $struct; + } + + public function testCreateStructFromType() + { + $type = $this->getContentTypeFixture(); + + $mapper = new Mapper( + $this->getConverterRegistryMock(), + $this->getMaskGeneratorMock(), + $this->getStorageDispatcherMock() + ); + $struct = $mapper->createCreateStructFromType($type); + + // Iterate through struct, since it has fewer props + foreach ($struct as $propName => $propVal) { + $this->assertEquals( + $struct->$propName, + $type->$propName, + "Property \${$propName} not equal" + ); + } + } + + /** + * Returns a Type fixture. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type + */ + protected function getContentTypeFixture() + { + // Taken from example DB + $type = new Type(); + $type->id = 23; + $type->name = [ + 'eng-US' => 'Folder', + ]; + $type->status = 0; + $type->description = []; + $type->identifier = 'folder'; + $type->created = 1024392098; + $type->modified = 1082454875; + $type->creatorId = 14; + $type->modifierId = 14; + $type->remoteId = 'a3d405b81be900468eb153d774f4f0d2'; + $type->urlAliasSchema = ''; + $type->nameSchema = '<short_name|name>'; + $type->isContainer = true; + $type->initialLanguageId = 2; + $type->sortField = Location::SORT_FIELD_MODIFIED_SUBNODE; + $type->sortOrder = Location::SORT_ORDER_ASC; + $type->defaultAlwaysAvailable = true; + $type->groupIds = [ + 1, + ]; + + $fieldDefName = new FieldDefinition(); + $fieldDefName->id = 42; + + $fieldDefShortDescription = new FieldDefinition(); + $fieldDefName->id = 128; + + $type->fieldDefinitions = [ + $fieldDefName, + $fieldDefShortDescription, + ]; + + return $type; + } + + public function testExtractGroupsFromRows() + { + $rows = $this->getLoadGroupFixture(); + + $mapper = new Mapper( + $this->getConverterRegistryMock(), + $this->getMaskGeneratorMock(), + $this->getStorageDispatcherMock() + ); + $groups = $mapper->extractGroupsFromRows($rows); + + $groupFixtureMedia = new Group(); + $groupFixtureMedia->created = 1032009743; + $groupFixtureMedia->creatorId = 14; + $groupFixtureMedia->id = 3; + $groupFixtureMedia->modified = 1033922120; + $groupFixtureMedia->modifierId = 14; + $groupFixtureMedia->identifier = 'Media'; + $groupFixtureMedia->isSystem = false; + + $groupFixtureSystem = new Group(); + $groupFixtureSystem->created = 1634895910; + $groupFixtureSystem->creatorId = 14; + $groupFixtureSystem->id = 4; + $groupFixtureSystem->modified = 1634895910; + $groupFixtureSystem->modifierId = 14; + $groupFixtureSystem->identifier = 'System'; + $groupFixtureSystem->isSystem = true; + + $this->assertEquals( + [$groupFixtureMedia, $groupFixtureSystem], + $groups + ); + } + + public function testExtractTypesFromRowsSingle() + { + $rows = $this->getLoadTypeFixture(); + + $mapper = $this->getNonConvertingMapper(); + $types = $mapper->extractTypesFromRows($rows); + + $this->assertCount( + 1, + $types, + 'Incorrect number of types extracted' + ); + + $this->assertPropertiesCorrect( + [ + 'id' => 1, + 'status' => 0, + 'name' => [ + 'eng-US' => 'Folder', + ], + 'description' => [], + 'created' => 1024392098, + 'creatorId' => 14, + 'modified' => 1082454875, + 'modifierId' => 14, + 'identifier' => 'folder', + 'remoteId' => 'a3d405b81be900468eb153d774f4f0d2', + 'urlAliasSchema' => '', + 'nameSchema' => '<short_name|name>', + 'isContainer' => true, + 'initialLanguageId' => 2, + 'groupIds' => [1], + 'sortField' => 1, + 'sortOrder' => 1, + 'defaultAlwaysAvailable' => true, + ], + $types[0] + ); + + $this->assertCount( + 4, + $types[0]->fieldDefinitions, + 'Incorrect number of field definitions' + ); + $this->assertPropertiesCorrect( + [ + 'id' => 155, + 'name' => [ + 'eng-US' => 'Short name', + ], + 'description' => [], + 'identifier' => 'short_name', + 'fieldGroup' => '', + 'fieldType' => 'ezstring', + 'isTranslatable' => true, + 'isRequired' => false, + 'isInfoCollector' => false, + 'isSearchable' => true, + 'position' => 2, + ], + $types[0]->fieldDefinitions[1] + ); + + $this->assertPropertiesCorrect( + [ + 'id' => 159, + 'name' => [], + 'description' => [], + 'identifier' => 'show_children', + 'fieldGroup' => '', + 'fieldType' => 'ezboolean', + 'isTranslatable' => false, + 'isRequired' => false, + 'isInfoCollector' => false, + 'isSearchable' => false, + 'position' => 6, + ], + $types[0]->fieldDefinitions[3] + ); + } + + public function testToStorageFieldDefinition() + { + $converterMock = $this->createMock(Converter::class); + $converterMock->expects($this->once()) + ->method('toStorageFieldDefinition') + ->with( + $this->isInstanceOf( + FieldDefinition::class + ), + $this->isInstanceOf( + StorageFieldDefinition::class + ) + ); + + $converterRegistry = new ConverterRegistry(['some_type' => $converterMock]); + + $mapper = new Mapper( + $converterRegistry, + $this->getMaskGeneratorMock(), + $this->getStorageDispatcherMock() + ); + + $fieldDef = new FieldDefinition(); + $fieldDef->fieldType = 'some_type'; + $fieldDef->name = [ + 'eng-GB' => 'some name', + ]; + + $storageFieldDef = new StorageFieldDefinition(); + + $mapper->toStorageFieldDefinition($fieldDef, $storageFieldDef); + } + + public function testToFieldDefinition() + { + $storageFieldDef = new StorageFieldDefinition(); + + $fieldDef = new FieldDefinition(); + $fieldDef->fieldType = 'some_type'; + + $converterMock = $this->createMock(Converter::class); + $converterMock->expects($this->once()) + ->method('toFieldDefinition') + ->with($storageFieldDef, $fieldDef); + + $converterRegistry = new ConverterRegistry(['some_type' => $converterMock]); + $storageDispatcher = $this->getStorageDispatcherMock(); + $storageDispatcher + ->expects($this->once()) + ->method('loadFieldConstraintsData') + ->with($fieldDef); + + $mapper = new Mapper( + $converterRegistry, + $this->getMaskGeneratorMock(), + $storageDispatcher + ); + + $mapper->toFieldDefinition($storageFieldDef, $fieldDef); + } + + /** + * Returns a Mapper with conversion methods mocked. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\Mapper + */ + protected function getNonConvertingMapper() + { + $mapper = $this->getMockBuilder(Mapper::class) + ->setMethods(['toFieldDefinition']) + ->setConstructorArgs([ + $this->getConverterRegistryMock(), + $this->getMaskGeneratorMock(), + $this->getStorageDispatcherMock(), + ]) + ->getMock(); + + // Dedicatedly tested test + $mapper->expects($this->atLeastOnce()) + ->method('toFieldDefinition') + ->with( + $this->isInstanceOf( + StorageFieldDefinition::class + ) + )->will( + $this->returnCallback( + static function () { + return new FieldDefinition(); + } + ) + ); + + return $mapper; + } + + /** + * Returns a converter registry mock. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry + */ + protected function getConverterRegistryMock() + { + return $this->createMock(ConverterRegistry::class); + } + + /** + * Returns fixture for {@link testExtractTypesFromRowsSingle()}. + * + * @return array + */ + protected function getLoadTypeFixture() + { + return require __DIR__ . '/_fixtures/map_load_type.php'; + } + + /** + * Returns fixture for {@link testExtractGroupsFromRows()}. + * + * @return array + */ + protected function getLoadGroupFixture() + { + return require __DIR__ . '/_fixtures/map_load_group.php'; + } + + protected function getMaskGeneratorMock() + { + return $this->createMock(MaskGenerator::class); + } + + /** + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\StorageDispatcherInterface|\PHPUnit\Framework\MockObject\MockObject + */ + private function getStorageDispatcherMock(): StorageDispatcherInterface + { + return $this->createMock(StorageDispatcherInterface::class); + } +} + +class_alias(MapperTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type\MapperTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Type/StorageDispatcherTest.php b/tests/lib/Persistence/Legacy/Content/Type/StorageDispatcherTest.php new file mode 100644 index 0000000000..50f4e3ab40 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Type/StorageDispatcherTest.php @@ -0,0 +1,175 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Type; + +use Ibexa\Contracts\Core\FieldType\FieldConstraintsStorage; +use Ibexa\Contracts\Core\Persistence\Content\FieldTypeConstraints; +use Ibexa\Contracts\Core\Persistence\Content\Type as ContentType; +use Ibexa\Contracts\Core\Persistence\Content\Type\FieldDefinition; +use Ibexa\Core\Persistence\Legacy\Content\Type\StorageDispatcher; +use Ibexa\Core\Persistence\Legacy\Content\Type\StorageRegistryInterface; +use PHPUnit\Framework\TestCase; + +final class StorageDispatcherTest extends TestCase +{ + private const EXAMPLE_FIELD_DEFINITION_ID = 1; + private const EXAMPLE_FIELD_TYPE_IDENTIFIER = 'example_ft'; + + public function testPublishFieldConstraintsData(): void + { + $storage = $this->createMock(FieldConstraintsStorage::class); + $storage + ->expects($this->once()) + ->method('publishFieldConstraintsData') + ->with(self::EXAMPLE_FIELD_DEFINITION_ID); + + $fieldDefinition = new FieldDefinition(); + $fieldDefinition->id = self::EXAMPLE_FIELD_DEFINITION_ID; + $fieldDefinition->fieldType = self::EXAMPLE_FIELD_TYPE_IDENTIFIER; + + $registry = $this->createStorageRegistryMockWithExternalStorage($storage); + + $dispatcher = new StorageDispatcher($registry); + $dispatcher->publishFieldConstraintsData($fieldDefinition); + } + + public function testStoreFieldConstraintsData(): void + { + $status = ContentType::STATUS_DEFINED; + $constraints = $this->createMock(FieldTypeConstraints::class); + + $storage = $this->createMock(FieldConstraintsStorage::class); + $storage + ->expects($this->once()) + ->method('storeFieldConstraintsData') + ->with(self::EXAMPLE_FIELD_DEFINITION_ID, $status, $constraints); + + $fieldDefinition = new FieldDefinition(); + $fieldDefinition->fieldTypeConstraints = $constraints; + $fieldDefinition->id = self::EXAMPLE_FIELD_DEFINITION_ID; + $fieldDefinition->fieldType = self::EXAMPLE_FIELD_TYPE_IDENTIFIER; + + $registry = $this->createStorageRegistryMockWithExternalStorage($storage); + + $dispatcher = new StorageDispatcher($registry); + $dispatcher->storeFieldConstraintsData($fieldDefinition, $status); + } + + public function testStoreFieldConstraintsDataForNonSupportedFieldType(): void + { + $status = ContentType::STATUS_DEFINED; + + $fieldDefinition = new FieldDefinition(); + $fieldDefinition->id = self::EXAMPLE_FIELD_DEFINITION_ID; + $fieldDefinition->fieldType = self::EXAMPLE_FIELD_TYPE_IDENTIFIER; + + $registry = $this->createStorageRegistryMockWithoutExternalStorage(); + + $dispatcher = new StorageDispatcher($registry); + $dispatcher->storeFieldConstraintsData($fieldDefinition, $status); + } + + public function testLoadFieldConstraintsData(): void + { + $constraints = $this->createMock(FieldTypeConstraints::class); + + $storage = $this->createMock(FieldConstraintsStorage::class); + $storage + ->expects($this->once()) + ->method('getFieldConstraintsData') + ->with(self::EXAMPLE_FIELD_DEFINITION_ID) + ->willReturn($constraints); + + $fieldDefinition = new FieldDefinition(); + $fieldDefinition->id = self::EXAMPLE_FIELD_DEFINITION_ID; + $fieldDefinition->fieldType = self::EXAMPLE_FIELD_TYPE_IDENTIFIER; + + $registry = $this->createStorageRegistryMockWithExternalStorage($storage); + + $dispatcher = new StorageDispatcher($registry); + $dispatcher->loadFieldConstraintsData($fieldDefinition, ContentType::STATUS_DEFINED); + + self::assertSame( + $constraints, + $fieldDefinition->fieldTypeConstraints + ); + } + + public function testLoadFieldConstraintsDataForNonSupportedFieldType(): void + { + $constraints = $this->createMock(FieldTypeConstraints::class); + + $fieldDefinition = new FieldDefinition(); + $fieldDefinition->id = self::EXAMPLE_FIELD_DEFINITION_ID; + $fieldDefinition->fieldType = self::EXAMPLE_FIELD_TYPE_IDENTIFIER; + $fieldDefinition->fieldTypeConstraints = $constraints; + + $registry = $this->createStorageRegistryMockWithoutExternalStorage(); + + $dispatcher = new StorageDispatcher($registry); + $dispatcher->loadFieldConstraintsData($fieldDefinition, ContentType::STATUS_DEFINED); + + self::assertSame( + $constraints, + $fieldDefinition->fieldTypeConstraints + ); + } + + public function testDeleteFieldConstraintsData(): void + { + $storage = $this->createMock(FieldConstraintsStorage::class); + $storage + ->expects($this->once()) + ->method('deleteFieldConstraintsData') + ->with(self::EXAMPLE_FIELD_DEFINITION_ID); + + $registry = $this->createStorageRegistryMockWithExternalStorage($storage); + + $dispatcher = new StorageDispatcher($registry); + $dispatcher->deleteFieldConstraintsData( + self::EXAMPLE_FIELD_TYPE_IDENTIFIER, + self::EXAMPLE_FIELD_DEFINITION_ID, + ContentType::STATUS_DEFINED, + ); + } + + public function testDeleteFieldConstraintsDataForNonSupportedFieldType(): void + { + $registry = $this->createStorageRegistryMockWithoutExternalStorage(); + + $dispatcher = new StorageDispatcher($registry); + $dispatcher->deleteFieldConstraintsData( + self::EXAMPLE_FIELD_TYPE_IDENTIFIER, + self::EXAMPLE_FIELD_DEFINITION_ID, + ContentType::STATUS_DEFINED, + ); + } + + private function createStorageRegistryMockWithoutExternalStorage(): StorageRegistryInterface + { + $registry = $this->createMock(StorageRegistryInterface::class); + $registry->method('hasStorage')->with(self::EXAMPLE_FIELD_TYPE_IDENTIFIER)->willReturn(false); + $registry + ->expects($this->never()) + ->method('getStorage') + ->with(self::EXAMPLE_FIELD_TYPE_IDENTIFIER); + + return $registry; + } + + private function createStorageRegistryMockWithExternalStorage( + FieldConstraintsStorage $storage + ): StorageRegistryInterface { + $registry = $this->createMock(StorageRegistryInterface::class); + $registry->method('hasStorage')->with(self::EXAMPLE_FIELD_TYPE_IDENTIFIER)->willReturn(true); + $registry->method('getStorage')->with(self::EXAMPLE_FIELD_TYPE_IDENTIFIER)->willReturn($storage); + + return $registry; + } +} diff --git a/tests/lib/Persistence/Legacy/Content/Type/StorageRegistryTest.php b/tests/lib/Persistence/Legacy/Content/Type/StorageRegistryTest.php new file mode 100644 index 0000000000..b887e38273 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Type/StorageRegistryTest.php @@ -0,0 +1,55 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Type; + +use Ibexa\Contracts\Core\FieldType\FieldConstraintsStorage; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Core\Persistence\Legacy\Content\Type\StorageRegistry; +use PHPUnit\Framework\TestCase; + +final class StorageRegistryTest extends TestCase +{ + public function testHasStorage(): void + { + $registry = new StorageRegistry([ + 'foo' => $this->createMock(FieldConstraintsStorage::class), + 'bar' => $this->createMock(FieldConstraintsStorage::class), + ]); + + self::assertTrue($registry->hasStorage('foo')); + self::assertTrue($registry->hasStorage('bar')); + // baz field type is not supported + self::assertFalse($registry->hasStorage('baz')); + } + + public function testGetStorage(): void + { + $storages = [ + 'foo' => $this->createMock(FieldConstraintsStorage::class), + 'bar' => $this->createMock(FieldConstraintsStorage::class), + ]; + + $registry = new StorageRegistry($storages); + + self::assertSame($storages['foo'], $registry->getStorage('foo')); + self::assertSame($storages['bar'], $registry->getStorage('bar')); + } + + public function testGetStorageForNonSupportedFieldType(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectErrorMessage('Argument \'$typeName\' is invalid: Undefined Ibexa\Contracts\Core\FieldType\FieldConstraintsStorage for "baz" field type'); + + $registry = new StorageRegistry([ + 'foo' => $this->createMock(FieldConstraintsStorage::class), + 'bar' => $this->createMock(FieldConstraintsStorage::class), + ]); + $registry->getStorage('baz'); + } +} diff --git a/tests/lib/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..dd2a5dd2f6 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Type/Update/Handler/DoctrineDatabaseTest.php @@ -0,0 +1,144 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\Type\Update\Handler; + +use Ibexa\Contracts\Core\Persistence\Content\Type; +use Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater; +use Ibexa\Core\Persistence\Legacy\Content\Type\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase + */ +class DoctrineDatabaseTest extends TestCase +{ + /** + * Gateway mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Type\Gateway + */ + protected $gatewayMock; + + /** + * Content Updater mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater + */ + protected $contentUpdaterMock; + + public function testUpdateContentObjects() + { + $handler = $this->getUpdateHandler(); + + $updaterMock = $this->getContentUpdaterMock(); + + $updaterMock->expects($this->never()) + ->method('determineActions'); + + $updaterMock->expects($this->never()) + ->method('applyUpdates'); + + $types = $this->getTypeFixtures(); + + $handler->updateContentObjects($types['from'], $types['to']); + } + + public function testDeleteOldType() + { + $handler = $this->getUpdateHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects($this->once()) + ->method('delete') + ->with( + $this->equalTo(23), + $this->equalTo(0) + ); + + $types = $this->getTypeFixtures(); + + $handler->deleteOldType($types['from'], $types['to']); + } + + public function testPublishNewType() + { + $handler = $this->getUpdateHandler(); + + $gatewayMock = $this->getGatewayMock(); + $updaterMock = $this->getContentUpdaterMock(); + + $gatewayMock->expects($this->once()) + ->method('publishTypeAndFields') + ->with($this->equalTo(23), $this->equalTo(1), $this->equalTo(0)); + + $types = $this->getTypeFixtures(); + + $handler->publishNewType($types['to'], 0); + } + + /** + * Returns an array with 'from' and 'to' types. + * + * @return \Ibexa\Contracts\Core\Persistence\Content\Type[] + */ + protected function getTypeFixtures() + { + $types = []; + + $types['from'] = new Type(); + $types['from']->id = 23; + $types['from']->status = Type::STATUS_DEFINED; + + $types['to'] = new Type(); + $types['to']->id = 23; + $types['to']->status = Type::STATUS_DRAFT; + + return $types; + } + + /** + * Returns the Update Handler to test. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler\DoctrineDatabase + */ + protected function getUpdateHandler() + { + return new DoctrineDatabase($this->getGatewayMock()); + } + + /** + * Returns a gateway mock. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\Gateway + */ + protected function getGatewayMock() + { + if (!isset($this->gatewayMock)) { + $this->gatewayMock = $this->getMockForAbstractClass(Gateway::class); + } + + return $this->gatewayMock; + } + + /** + * Returns a Content Updater mock. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\ContentUpdater + */ + protected function getContentUpdaterMock() + { + if (!isset($this->contentUpdaterMock)) { + $this->contentUpdaterMock = $this->createMock(ContentUpdater::class); + } + + return $this->contentUpdaterMock; + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\Type\Update\Handler\DoctrineDatabaseTest'); diff --git a/tests/lib/Persistence/Legacy/Content/Type/_fixtures/map_load_group.php b/tests/lib/Persistence/Legacy/Content/Type/_fixtures/map_load_group.php new file mode 100644 index 0000000000..6b2fa2e1dd --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/Type/_fixtures/map_load_group.php @@ -0,0 +1,26 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + [ + 'created' => 1032009743, + 'creator_id' => 14, + 'id' => 3, + 'modified' => 1033922120, + 'modifier_id' => 14, + 'name' => 'Media', + 'is_system' => 0, + ], + [ + 'created' => 1634895910, + 'creator_id' => 14, + 'id' => 4, + 'modified' => 1634895910, + 'modifier_id' => 14, + 'name' => 'System', + 'is_system' => 1, + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/_fixtures/map_load_type.php b/tests/lib/Persistence/Legacy/Content/Type/_fixtures/map_load_type.php similarity index 98% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/_fixtures/map_load_type.php rename to tests/lib/Persistence/Legacy/Content/Type/_fixtures/map_load_type.php index 9e2200e369..1e4ebcbd6d 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/Type/_fixtures/map_load_type.php +++ b/tests/lib/Persistence/Legacy/Content/Type/_fixtures/map_load_type.php @@ -1,5 +1,9 @@ <?php +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 0 => [ 'ezcontentclass_id' => '1', diff --git a/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..b5a4d87770 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,516 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\UrlAlias\Gateway; + +use Ibexa\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase as LanguageGateway; +use Ibexa\Core\Persistence\Legacy\Content\Language\Handler as LanguageHandler; +use Ibexa\Core\Persistence\Legacy\Content\Language\Mapper as LanguageMapper; +use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase + * + * @group urlalias-gateway + */ +class DoctrineDatabaseTest extends TestCase +{ + /** + * Database gateway to test. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway + */ + protected $gateway; + + /** + * Test for the loadUrlAliasData() method. + */ + public function testLoadUrlaliasDataNonExistent() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_simple.php'); + $gateway = $this->getGateway(); + + $rows = $gateway->loadUrlAliasData([md5('tri')]); + + self::assertEmpty($rows); + } + + /** + * Test for the loadUrlAliasData() method. + */ + public function testLoadUrlaliasData() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_simple.php'); + $gateway = $this->getGateway(); + + $row = $gateway->loadUrlAliasData([md5('jedan'), md5('dva')]); + + self::assertEquals( + [ + 'ezurlalias_ml0_id' => '2', + 'ezurlalias_ml0_link' => '2', + 'ezurlalias_ml0_is_alias' => '0', + 'ezurlalias_ml0_alias_redirects' => '1', + 'ezurlalias_ml0_is_original' => '1', + 'ezurlalias_ml0_action' => 'eznode:314', + 'ezurlalias_ml0_action_type' => 'eznode', + 'ezurlalias_ml0_lang_mask' => '2', + 'ezurlalias_ml0_text' => 'jedan', + 'ezurlalias_ml0_parent' => '0', + 'ezurlalias_ml0_text_md5' => '6896260129051a949051c3847c34466f', + 'id' => '3', + 'link' => '3', + 'is_alias' => '0', + 'alias_redirects' => '1', + 'is_original' => '1', + 'action' => 'eznode:315', + 'action_type' => 'eznode', + 'lang_mask' => '3', + 'text' => 'dva', + 'parent' => '2', + 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', + ], + $row + ); + } + + /** + * Test for the loadUrlAliasData() method. + * + * Test with fixture containing language mask with multiple languages. + */ + public function testLoadUrlaliasDataMultipleLanguages() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_multilang.php'); + $gateway = $this->getGateway(); + + $row = $gateway->loadUrlAliasData([md5('jedan'), md5('dva')]); + + self::assertEquals( + [ + 'ezurlalias_ml0_id' => '2', + 'ezurlalias_ml0_link' => '2', + 'ezurlalias_ml0_is_alias' => '0', + 'ezurlalias_ml0_alias_redirects' => '1', + 'ezurlalias_ml0_is_original' => '1', + 'ezurlalias_ml0_action' => 'eznode:314', + 'ezurlalias_ml0_action_type' => 'eznode', + 'ezurlalias_ml0_lang_mask' => '3', + 'ezurlalias_ml0_text' => 'jedan', + 'ezurlalias_ml0_parent' => '0', + 'ezurlalias_ml0_text_md5' => '6896260129051a949051c3847c34466f', + 'id' => '3', + 'link' => '3', + 'is_alias' => '0', + 'alias_redirects' => '1', + 'is_original' => '1', + 'action' => 'eznode:315', + 'action_type' => 'eznode', + 'lang_mask' => '6', + 'text' => 'dva', + 'parent' => '2', + 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', + ], + $row + ); + } + + /** + * @return array + */ + public function providerForTestLoadPathData() + { + return [ + [ + 2, + [ + [ + ['parent' => '0', 'lang_mask' => '3', 'text' => 'jedan'], + ], + ], + ], + [ + 3, + [ + [ + ['parent' => '0', 'lang_mask' => '3', 'text' => 'jedan'], + ], + [ + ['parent' => '2', 'lang_mask' => '5', 'text' => 'two'], + ['parent' => '2', 'lang_mask' => '3', 'text' => 'dva'], + ], + ], + ], + [ + 4, + [ + [ + ['parent' => '0', 'lang_mask' => '3', 'text' => 'jedan'], + ], + [ + ['parent' => '2', 'lang_mask' => '5', 'text' => 'two'], + ['parent' => '2', 'lang_mask' => '3', 'text' => 'dva'], + ], + [ + ['parent' => '3', 'lang_mask' => '9', 'text' => 'drei'], + ['parent' => '3', 'lang_mask' => '5', 'text' => 'three'], + ['parent' => '3', 'lang_mask' => '3', 'text' => 'tri'], + ], + ], + ], + ]; + } + + /** + * Test for the loadPathData() method. + * + * + * @dataProvider providerForTestLoadPathData + */ + public function testLoadPathData($id, $pathData) + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_fallback.php'); + $gateway = $this->getGateway(); + + $loadedPathData = $gateway->loadPathData($id); + + self::assertEquals( + $pathData, + $loadedPathData + ); + } + + /** + * @return array + */ + public function providerForTestLoadPathDataMultipleLanguages() + { + return [ + [ + 2, + [ + [ + ['parent' => '0', 'lang_mask' => '3', 'text' => 'jedan'], + ], + ], + ], + [ + 3, + [ + [ + ['parent' => '0', 'lang_mask' => '3', 'text' => 'jedan'], + ], + [ + ['parent' => '2', 'lang_mask' => '6', 'text' => 'dva'], + ], + ], + ], + [ + 4, + [ + [ + ['parent' => '0', 'lang_mask' => '3', 'text' => 'jedan'], + ], + [ + ['parent' => '2', 'lang_mask' => '6', 'text' => 'dva'], + ], + [ + ['parent' => '3', 'lang_mask' => '4', 'text' => 'three'], + ['parent' => '3', 'lang_mask' => '2', 'text' => 'tri'], + ], + ], + ], + ]; + } + + /** + * Test for the loadPathData() method. + * + * + * @dataProvider providerForTestLoadPathDataMultipleLanguages + */ + public function testLoadPathDataMultipleLanguages($id, $pathData) + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_multilang.php'); + $gateway = $this->getGateway(); + + $loadedPathData = $gateway->loadPathData($id); + + self::assertEquals( + $pathData, + $loadedPathData + ); + } + + /** + * @return array + */ + public function providerForTestCleanupAfterPublishHistorize() + { + return [ + [ + 'action' => 'eznode:314', + 'languageId' => 2, + 'parentId' => 0, + 'textMD5' => '6896260129051a949051c3847c34466f', + ], + [ + 'action' => 'eznode:315', + 'languageId' => 2, + 'parentId' => 0, + 'textMD5' => 'c67ed9a09ab136fae610b6a087d82e21', + ], + ]; + } + + /** + * Data provider for testArchiveUrlAliasesForDeletedTranslations. + * + * @see testArchiveUrlAliasesForDeletedTranslations + * + * @return array + */ + public function providerForTestArchiveUrlAliasesForDeletedTranslations() + { + return [ + [314, [2]], + [315, [4]], + [316, [4]], + [317, [2, 8]], + [318, [2, 8]], + ]; + } + + /** + * Test for the cleanupAfterPublish() method. + * + * + * + * @dataProvider providerForTestCleanupAfterPublishHistorize + */ + public function testCleanupAfterPublishHistorize($action, $languageId, $parentId, $textMD5) + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_downgrade.php'); + $gateway = $this->getGateway(); + + $loadedRow = $gateway->loadRow($parentId, $textMD5); + + $gateway->cleanupAfterPublish($action, $languageId, 42, $parentId, 'jabberwocky'); + + $reloadedRow = $gateway->loadRow($parentId, $textMD5); + $loadedRow['is_original'] = '0'; + $loadedRow['link'] = 42; + $loadedRow['id'] = 6; + + self::assertEquals($reloadedRow, $loadedRow); + } + + /** + * @return array + */ + public function providerForTestCleanupAfterPublishRemovesLanguage() + { + return [ + [ + 'action' => 'eznode:316', + 'languageId' => 2, + 'parentId' => 0, + 'textMD5' => 'd2cfe69af2d64330670e08efb2c86df7', + ], + [ + 'action' => 'eznode:317', + 'languageId' => 2, + 'parentId' => 0, + 'textMD5' => '538dca05643d220317ad233cd7be7a0a', + ], + ]; + } + + /** + * Test for the cleanupAfterPublish() method. + * + * + * + * @dataProvider providerForTestCleanupAfterPublishRemovesLanguage + */ + public function testCleanupAfterPublishRemovesLanguage($action, $languageId, $parentId, $textMD5) + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_downgrade.php'); + $gateway = $this->getGateway(); + + $loadedRow = $gateway->loadRow($parentId, $textMD5); + + $gateway->cleanupAfterPublish($action, $languageId, 42, $parentId, 'jabberwocky'); + + $reloadedRow = $gateway->loadRow($parentId, $textMD5); + $loadedRow['lang_mask'] = $loadedRow['lang_mask'] & ~$languageId; + + self::assertEquals($reloadedRow, $loadedRow); + } + + /** + * Test for the reparent() method. + * + * @todo document + */ + public function testReparent() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_simple.php'); + $gateway = $this->getGateway(); + + $gateway->reparent(2, 42); + + self::assertEquals( + [ + 'action' => 'eznode:315', + 'action_type' => 'eznode', + 'alias_redirects' => '1', + 'id' => '3', + 'is_alias' => '0', + 'is_original' => '1', + 'lang_mask' => '3', + 'link' => '3', + 'parent' => '42', + 'text' => 'dva', + 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', + ], + $gateway->loadRow(42, 'c67ed9a09ab136fae610b6a087d82e21') + ); + } + + /** + * Test for the remove() method. + */ + public function testRemove() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_remove.php'); + $gateway = $this->getGateway(); + + $gateway->remove('eznode:314'); + + self::assertEmpty($gateway->loadRow(0, 'd5189de027922f81005951e6efe0efd5')); + self::assertEmpty($gateway->loadRow(0, 'a59d9f07e3d5fcf77911155650956a73')); + self::assertEmpty($gateway->loadRow(0, '6449cba11bb134a57af94c8cb7f6c99c')); + self::assertNotEmpty($gateway->loadRow(0, '0a06c09b6dd9a4606b4eb6d60ab188f0')); + self::assertNotEmpty($gateway->loadRow(0, '82f2bce3283a0806a398fe78beda17d9')); + self::assertNotEmpty($gateway->loadRow(0, '863d659d9fec68e5ab117b5f585a4ee7')); + } + + /** + * Test for the remove() method. + */ + public function testRemoveWithId() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_remove.php'); + $gateway = $this->getGateway(); + + $gateway->remove('eznode:315', 6); + + self::assertEmpty($gateway->loadRow(0, '0a06c09b6dd9a4606b4eb6d60ab188f0')); + self::assertEmpty($gateway->loadRow(0, '82f2bce3283a0806a398fe78beda17d9')); + self::assertNotEmpty($gateway->loadRow(0, '863d659d9fec68e5ab117b5f585a4ee7')); + self::assertNotEmpty($gateway->loadRow(0, 'd5189de027922f81005951e6efe0efd5')); + self::assertNotEmpty($gateway->loadRow(0, 'a59d9f07e3d5fcf77911155650956a73')); + self::assertNotEmpty($gateway->loadRow(0, '6449cba11bb134a57af94c8cb7f6c99c')); + } + + /** + * Test for the removeCustomAlias() method. + */ + public function testRemoveCustomAlias() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_remove.php'); + $gateway = $this->getGateway(); + + $result = $gateway->removeCustomAlias(0, '6449cba11bb134a57af94c8cb7f6c99c'); + + self::assertTrue($result); + self::assertNotEmpty($gateway->loadRow(0, 'd5189de027922f81005951e6efe0efd5')); + self::assertNotEmpty($gateway->loadRow(0, 'a59d9f07e3d5fcf77911155650956a73')); + self::assertEmpty($gateway->loadRow(0, '6449cba11bb134a57af94c8cb7f6c99c')); + } + + /** + * Test for the removeByAction() method. + */ + public function testRemoveCustomAliasFails() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_remove.php'); + $gateway = $this->getGateway(); + + $result = $gateway->removeCustomAlias(0, 'd5189de027922f81005951e6efe0efd5'); + + self::assertFalse($result); + self::assertNotEmpty($gateway->loadRow(0, 'd5189de027922f81005951e6efe0efd5')); + } + + /** + * Test for the getNextId() method. + */ + public function testGetNextId() + { + $gateway = $this->getGateway(); + + self::assertEquals(1, $gateway->getNextId()); + self::assertEquals(2, $gateway->getNextId()); + } + + /** + * @dataProvider providerForTestArchiveUrlAliasesForDeletedTranslations + * + * @param int $locationId + * @param int[] $removedLanguageIds + */ + public function testArchiveUrlAliasesForDeletedTranslations($locationId, array $removedLanguageIds) + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_multilang.php'); + $gateway = $this->getGateway(); + + foreach ($gateway->loadLocationEntries($locationId) as $row) { + $gateway->archiveUrlAliasesForDeletedTranslations( + $locationId, + (int) $row['parent'], + $removedLanguageIds + ); + } + + // check results + $languageMask = 0; + foreach ($removedLanguageIds as $languageId) { + $languageMask |= $languageId; + } + foreach ($gateway->loadLocationEntries($locationId) as $row) { + self::assertNotEquals(0, (int) $row['lang_mask']); + self::assertNotEquals(1, (int) $row['lang_mask']); + self::assertEquals(0, (int) $row['lang_mask'] & $languageMask); + } + } + + /** + * Return the DoctrineDatabase gateway implementation to test. + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function getGateway(): DoctrineDatabase + { + if (!isset($this->gateway)) { + $languageHandler = new LanguageHandler( + new LanguageGateway($this->getDatabaseConnection()), + new LanguageMapper() + ); + $this->gateway = new DoctrineDatabase( + $this->getDatabaseConnection(), + new LanguageMaskGenerator($languageHandler) + ); + } + + return $this->gateway; + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlAlias\Gateway\DoctrineDatabaseTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_downgrade.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_downgrade.php similarity index 79% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_downgrade.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_downgrade.php index ff134f2df4..db246cd6a7 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_downgrade.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_downgrade.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '0', 'text' => 'tri', 'text_md5' => 'd2cfe69af2d64330670e08efb2c86df7', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,23 +70,23 @@ 'parent' => '0', 'text' => 'cetiri', 'text_md5' => '538dca05643d220317ad233cd7be7a0a', - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - ), -); + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_fallback.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_fallback.php similarity index 78% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_fallback.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_fallback.php index 0c4539d368..8697a4e9a6 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_fallback.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_fallback.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '2', 'text' => 'two', 'text_md5' => 'b8a9f715dbb64fd5c56e7783c6820a61', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '2', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '3', 'text' => 'drei', 'text_md5' => '1d8d2fd0a99802b89eb356a86e029d25', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -79,8 +83,8 @@ 'parent' => '3', 'text' => 'three', 'text_md5' => '35d6d33467aae9a2e3dccb4b6b027878', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -92,46 +96,46 @@ 'parent' => '3', 'text' => 'tri', 'text_md5' => 'd2cfe69af2d64330670e08efb2c86df7', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - 2 => array( + 'name' => 'English (United Kingdom)', + ], + 2 => [ 'disabled' => 0, 'id' => 8, 'locale' => 'ger-DE', - 'name' => 'German' - ), - 3 => array( + 'name' => 'German', + ], + 3 => [ 'disabled' => 0, 'id' => 16, 'locale' => 'kli-KR', - 'name' => 'Klingon (Kronos)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'Klingon (Kronos)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - ), -); + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_multilang.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_multilang.php similarity index 82% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_multilang.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_multilang.php index 0f09b0f7a8..b3beaf5a90 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_multilang.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_multilang.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '2', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '3', 'text' => 'three', 'text_md5' => '35d6d33467aae9a2e3dccb4b6b027878', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '3', 'text' => 'tri', 'text_md5' => 'd2cfe69af2d64330670e08efb2c86df7', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -79,8 +83,8 @@ 'parent' => '3', 'text' => 'multi-composite-always-available', 'text_md5' => '33e6762b930e3a428a9f0e94907a8eaa', - ), - 7 => array( + ], + 7 => [ 'action' => 'eznode:318', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -92,46 +96,46 @@ 'parent' => '3', 'text' => 'multi-composite', 'text_md5' => 'bc3644bfb4f44ebbd4dd2de0de2ce6c2', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', 'name' => 'Croatian (Hrvatski)', - ), - 1 => array( + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', 'name' => 'English (United Kingdom)', - ), - 2 => array( + ], + 2 => [ 'disabled' => 0, 'id' => 8, 'locale' => 'pol-PL', 'name' => 'Polish (polski)', - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - 5 => array( + ], + 5 => [ 'id' => '6', - ), - ), -); + ], + ], +]; diff --git a/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_relink.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_relink.php new file mode 100644 index 0000000000..cd3503d2fb --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_relink.php @@ -0,0 +1,63 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ + 'action' => 'eznode:2', + 'action_type' => 'eznode', + 'alias_redirects' => '1', + 'id' => '1', + 'is_alias' => '0', + 'is_original' => '1', + 'lang_mask' => '3', + 'link' => '1', + 'parent' => '0', + 'text' => '', + 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', + ], + 1 => [ + 'action' => 'eznode:314', + 'action_type' => 'eznode', + 'alias_redirects' => '1', + 'id' => '2', + 'is_alias' => '0', + 'is_original' => '0', + 'lang_mask' => '2', + 'link' => '2', + 'parent' => '0', + 'text' => 'history', + 'text_md5' => '3cd15f8f2940aff879df34df4e5c2cd1', + ], + 2 => [ + 'action' => 'eznode:315', + 'action_type' => 'eznode', + 'alias_redirects' => '1', + 'id' => '3', + 'is_alias' => '0', + 'is_original' => '0', + 'lang_mask' => '2', + 'link' => '3', + 'parent' => '0', + 'text' => 'reused-history', + 'text_md5' => '51e775a611265b7b0cde62a413c91cdc', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ + 'id' => '1', + ], + 1 => [ + 'id' => '2', + ], + 2 => [ + 'id' => '3', + ], + 3 => [ + 'id' => '4', + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_remove.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_remove.php similarity index 82% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_remove.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_remove.php index 14e96e6d86..771ad6f526 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/Gateway/_fixtures/urlaliases_remove.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_remove.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'location', 'text_md5' => 'd5189de027922f81005951e6efe0efd5', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'location-history', 'text_md5' => 'a59d9f07e3d5fcf77911155650956a73', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '0', 'text' => 'location-custom', 'text_md5' => '6449cba11bb134a57af94c8cb7f6c99c', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '0', 'text' => 'location2', 'text_md5' => '0a06c09b6dd9a4606b4eb6d60ab188f0', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -79,8 +83,8 @@ 'parent' => '0', 'text' => 'location2-translation', 'text_md5' => '82f2bce3283a0806a398fe78beda17d9', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -92,29 +96,29 @@ 'parent' => '0', 'text' => 'location2-custom', 'text_md5' => '863d659d9fec68e5ab117b5f585a4ee7', - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - 5 => array( + ], + 5 => [ 'id' => '6', - ), - 6 => array( + ], + 6 => [ 'id' => '7', - ), - ), -); + ], + ], +]; diff --git a/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_simple.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_simple.php new file mode 100644 index 0000000000..0b85b15a6d --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/Gateway/_fixtures/urlaliases_simple.php @@ -0,0 +1,68 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ + 'action' => 'eznode:2', + 'action_type' => 'eznode', + 'alias_redirects' => '1', + 'id' => '1', + 'is_alias' => '0', + 'is_original' => '1', + 'lang_mask' => '3', + 'link' => '1', + 'parent' => '0', + 'text' => '', + 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', + ], + 1 => [ + 'action' => 'eznode:314', + 'action_type' => 'eznode', + 'alias_redirects' => '1', + 'id' => '2', + 'is_alias' => '0', + 'is_original' => '1', + 'lang_mask' => '2', + 'link' => '2', + 'parent' => '0', + 'text' => 'jedan', + 'text_md5' => '6896260129051a949051c3847c34466f', + ], + 2 => [ + 'action' => 'eznode:315', + 'action_type' => 'eznode', + 'alias_redirects' => '1', + 'id' => '3', + 'is_alias' => '0', + 'is_original' => '1', + 'lang_mask' => '3', + 'link' => '3', + 'parent' => '2', + 'text' => 'dva', + 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', + ], + ], + 'ezcontent_language' => [ + 0 => [ + 'disabled' => 0, + 'id' => 2, + 'locale' => 'cro-HR', + 'name' => 'Croatian (Hrvatski)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ + 'id' => '1', + ], + 1 => [ + 'id' => '2', + ], + 2 => [ + 'id' => '3', + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/SlugConverterTest.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php similarity index 82% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/SlugConverterTest.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php index 371a01b1fa..ea188520d3 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/SlugConverterTest.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/SlugConverterTest.php @@ -4,25 +4,23 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlAlias; - -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Persistence\TransformationProcessor\PcreCompiler; -use eZ\Publish\Core\Persistence\TransformationProcessor\PreprocessedBased; -use eZ\Publish\Core\Persistence\Utf8Converter; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\UrlAlias; + +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; +use Ibexa\Core\Persistence\TransformationProcessor; +use Ibexa\Core\Persistence\TransformationProcessor\PcreCompiler; +use Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased; +use Ibexa\Core\Persistence\Utf8Converter; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; use PHPUnit\Framework\TestSuite; /** - * Test case for URL slug converter. + * @covers \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter */ class SlugConverterTest extends TestCase { /** * Test for the convert() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter::convert */ public function testConvert() { @@ -51,8 +49,6 @@ public function testConvert() /** * Test for the convert() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter::convert */ public function testConvertWithDefaultTextFallback() { @@ -81,8 +77,6 @@ public function testConvertWithDefaultTextFallback() /** * Test for the convert() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter::convert */ public function testConvertWithGivenTransformation() { @@ -123,7 +117,6 @@ public function providerForTestGetUniqueCounterValue() * Test for the getUniqueCounterValue() method. * * @dataProvider providerForTestGetUniqueCounterValue - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter::getUniqueCounterValue */ public function testGetUniqueCounterValue($text, $isRootLevel, $returnValue) { @@ -160,12 +153,11 @@ public function cleanupTextData() * Test for the cleanupText() method. * * @dataProvider cleanupTextData - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter::cleanupText */ public function testCleanupText($text, $method, $expected) { $testMethod = new \ReflectionMethod( - '\eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter', + SlugConverter::class, 'cleanupText' ); $testMethod->setAccessible(true); @@ -206,7 +198,7 @@ public function convertData() * Test for the convert() method. * * @dataProvider convertData - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter::convert + * * @depends testCleanupText */ public function testConvertNoMocking($text, $defaultText, $transformation, $expected) @@ -216,10 +208,10 @@ public function testConvertNoMocking($text, $defaultText, $transformation, $expe new Utf8Converter() ), [ - __DIR__ . '../../../../../Tests/TransformationProcessor/_fixtures/transformations/ascii.tr.result', - __DIR__ . '../../../../../Tests/TransformationProcessor/_fixtures/transformations/basic.tr.result', - __DIR__ . '../../../../../Tests/TransformationProcessor/_fixtures/transformations/latin.tr.result', - __DIR__ . '../../../../../Tests/TransformationProcessor/_fixtures/transformations/search.tr.result', + __DIR__ . '/../../../TransformationProcessor/_fixtures/transformations/ascii.tr.result', + __DIR__ . '/../../../TransformationProcessor/_fixtures/transformations/basic.tr.result', + __DIR__ . '/../../../TransformationProcessor/_fixtures/transformations/latin.tr.result', + __DIR__ . '/../../../TransformationProcessor/_fixtures/transformations/search.tr.result', ] ); $slugConverter = new SlugConverter($transformationProcessor); @@ -252,7 +244,7 @@ public function testConvertNoMocking($text, $defaultText, $transformation, $expe ], ]; - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter */ + /** @var \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter */ protected $slugConverter; /** @var \PHPUnit\Framework\MockObject\MockObject */ @@ -262,7 +254,7 @@ public function testConvertNoMocking($text, $defaultText, $transformation, $expe protected $transformationProcessorMock; /** - * @return \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter + * @return \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter */ protected function getMockedSlugConverter() { @@ -279,7 +271,7 @@ protected function getMockedSlugConverter() /** * @param array $methods * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter|\PHPUnit\Framework\MockObject\MockObject */ protected function getSlugConverterMock(array $methods = []) { @@ -328,3 +320,5 @@ public static function suite() return new TestSuite(__CLASS__); } } + +class_alias(SlugConverterTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlAlias\SlugConverterTest'); diff --git a/tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php new file mode 100644 index 0000000000..13569aefd1 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasHandlerTest.php @@ -0,0 +1,5536 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\UrlAlias; + +use Ibexa\Contracts\Core\Persistence\Content\UrlAlias; +use Ibexa\Contracts\Core\Persistence\TransactionHandler; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Persistence\Legacy\Content\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Gateway\DoctrineDatabase as ContentGateway; +use Ibexa\Core\Persistence\Legacy\Content\Language\Gateway\DoctrineDatabase as LanguageGateway; +use Ibexa\Core\Persistence\Legacy\Content\Language\Handler as LanguageHandler; +use Ibexa\Core\Persistence\Legacy\Content\Language\Mapper as LanguageMapper; +use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; +use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway as LocationGateway; +use Ibexa\Core\Persistence\Legacy\Content\Location\Gateway\DoctrineDatabase as DoctrineDatabaseLocation; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway as UrlAliasGateway; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Gateway\DoctrineDatabase; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Handler; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Mapper; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\SlugConverter; +use Ibexa\Core\Persistence\TransformationProcessor\DefinitionBased; +use Ibexa\Core\Persistence\TransformationProcessor\DefinitionBased\Parser; +use Ibexa\Core\Persistence\TransformationProcessor\PcreCompiler; +use Ibexa\Core\Persistence\Utf8Converter; +use Ibexa\Core\Search\Legacy\Content; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Handler + * + * @group urlalias-handler + */ +class UrlAliasHandlerTest extends TestCase +{ + /** + * Test for the lookup() method. + * + * Simple lookup case. + * + * @group location + * @group virtual + * @group resource + * @group case-correction + * @group multiple-languages + */ + public function testLookup() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location.php'); + + $urlAlias = $handler->lookup('jedan'); + self::assertInstanceOf(UrlAlias::class, $urlAlias); + } + + /** + * Test for the lookup() method. + * + * Trying to lookup non existent URL alias throws NotFoundException. + * + * + * @group location + * @group virtual + * @group resource + */ + public function testLookupThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getHandler(); + $handler->lookup('wooden/iron'); + } + + /** + * Test for the lookup() method. + * + * Trying to lookup URL alias with exceeded path segments limit + * + * @group location + * @group case-correction + */ + public function testLookupThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $handler = $this->getHandler(); + $handler->lookup(str_repeat('/1', 99)); + } + + public function providerForTestLookupLocationUrlAlias() + { + return [ + [ + 'jedan', + [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + ], + ['cro-HR'], + true, + 314, + '0-6896260129051a949051c3847c34466f', + ], + [ + 'jedan/dva', + [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ], + ], + ['cro-HR'], + false, + 315, + '2-c67ed9a09ab136fae610b6a087d82e21', + ], + [ + 'jedan/two', + [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ], + ], + ['eng-GB'], + false, + 315, + '2-b8a9f715dbb64fd5c56e7783c6820a61', + ], + [ + 'jedan/dva/tri', + [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + 'ger-DE' => 'drei', + ], + ], + ], + ['cro-HR'], + false, + 316, + '3-d2cfe69af2d64330670e08efb2c86df7', + ], + [ + 'jedan/two/three', + [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + 'ger-DE' => 'drei', + ], + ], + ], + ['eng-GB'], + false, + 316, + '3-35d6d33467aae9a2e3dccb4b6b027878', + ], + [ + 'jedan/dva/three', + [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + 'ger-DE' => 'drei', + ], + ], + ], + ['eng-GB'], + false, + 316, + '3-35d6d33467aae9a2e3dccb4b6b027878', + ], + [ + 'jedan/two/tri', + [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + 'ger-DE' => 'drei', + ], + ], + ], + ['cro-HR'], + false, + 316, + '3-d2cfe69af2d64330670e08efb2c86df7', + ], + [ + 'jedan/dva/drei', + [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + 'ger-DE' => 'drei', + ], + ], + ], + ['ger-DE'], + false, + 316, + '3-1d8d2fd0a99802b89eb356a86e029d25', + ], + [ + 'jedan/two/drei', + [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + 'ger-DE' => 'drei', + ], + ], + ], + ['ger-DE'], + false, + 316, + '3-1d8d2fd0a99802b89eb356a86e029d25', + ], + ]; + } + + /** + * Test for the lookup() method. + * + * Testing that UrlAlias is found and has expected state. + * + * @dataProvider providerForTestLookupLocationUrlAlias + * @depends testLookup + * @group location + */ + public function testLookupLocationUrlAlias( + $url, + array $pathData, + array $languageCodes, + $alwaysAvailable, + $locationId, + $id + ) { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location.php'); + + $urlAlias = $handler->lookup($url); + + self::assertInstanceOf(UrlAlias::class, $urlAlias); + self::assertEquals( + new UrlAlias( + [ + 'id' => $id, + 'type' => UrlAlias::LOCATION, + 'destination' => $locationId, + 'languageCodes' => $languageCodes, + 'pathData' => $pathData, + 'alwaysAvailable' => $alwaysAvailable, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Testing that looking up case incorrect URL results in redirection to case correct path. + * + * Note that case corrected path is not always equal to case corrected case incorrect path, eg. "JEDAN/TWO/THREE" + * will not always redirect to "jedan/two/three". + * In some cases, depending on list of prioritized languages and if Content available in the different language + * higher in the list of prioritized languages, path showing to that Content will be used. + * Example: "JEDAN/TWO/DREI" with "eng-GB" and "ger-DE" as prioritized languages will produce redirection + * to the "jedan/two/three", as "eng-GB" is the most prioritized language and Content that URL alias is pointing + * to is available in it. + * + * + * @dataProvider providerForTestLookupLocationUrlAlias + * @depends testLookup + * @group case-correction + * @group location + * + * @todo refactor, only forward pertinent + */ + public function testLookupLocationCaseCorrection( + $url, + array $pathData, + array $languageCodes, + $alwaysAvailable, + $locationId, + $id + ) { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location.php'); + + $urlAlias = $handler->lookup(strtoupper($url)); + + self::assertInstanceOf(UrlAlias::class, $urlAlias); + self::assertEquals( + new UrlAlias( + [ + 'id' => $id, + 'type' => UrlAlias::LOCATION, + 'destination' => $locationId, + 'languageCodes' => $languageCodes, + 'pathData' => $pathData, + 'alwaysAvailable' => $alwaysAvailable, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + public function providerForTestLookupLocationMultipleLanguages() + { + return [ + [ + 'jedan/dva', + [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'dva', + ], + ], + ], + ['cro-HR', 'eng-GB'], + false, + 315, + '2-c67ed9a09ab136fae610b6a087d82e21', + ], + [ + 'jedan/dva/tri', + [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + ], + ], + ], + ['cro-HR'], + false, + 316, + '3-d2cfe69af2d64330670e08efb2c86df7', + ], + [ + 'jedan/dva/three', + [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + ], + ], + ], + ['eng-GB'], + false, + 316, + '3-35d6d33467aae9a2e3dccb4b6b027878', + ], + ]; + } + + /** + * Test for the lookup() method. + * + * @dataProvider providerForTestLookupLocationMultipleLanguages + * @depends testLookup + * @group multiple-languages + * @group location + */ + public function testLookupLocationMultipleLanguages( + $url, + array $pathData, + array $languageCodes, + $alwaysAvailable, + $locationId, + $id + ) { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_multilang.php'); + + $urlAlias = $handler->lookup($url); + + self::assertInstanceOf(UrlAlias::class, $urlAlias); + self::assertEquals( + new UrlAlias( + [ + 'id' => $id, + 'type' => UrlAlias::LOCATION, + 'destination' => $locationId, + 'languageCodes' => $languageCodes, + 'pathData' => $pathData, + 'alwaysAvailable' => $alwaysAvailable, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the lookup() method. + * + * @todo document + * + * @depends testLookup + * @group history + * @group location + */ + public function testLookupLocationHistoryUrlAlias() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location.php'); + + $urlAlias = $handler->lookup('jedan/dva/tri-history'); + + self::assertEquals( + $this->getHistoryAlias(), + $urlAlias + ); + } + + public function providerForTestLookupCustomLocationUrlAlias() + { + return [ + [ + 'autogenerated-hello/everybody', + [ + [ + 'always-available' => true, + 'translations' => [ + 'eng-GB' => 'autogenerated-hello', + ], + ], + [ + 'always-available' => true, + 'translations' => [ + 'eng-GB' => 'everybody', + ], + ], + ], + ['eng-GB'], + false, + true, + 315, + '2-88150d7d17390010ba6222de68bfafb5', + ], + [ + 'hello', + [ + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'hello', + ], + ], + ], + ['eng-GB'], + true, + false, + 314, + '0-5d41402abc4b2a76b9719d911017c592', + ], + [ + 'hello/and/goodbye', + [ + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'hello', + ], + ], + [ + 'always-available' => true, + 'translations' => [ + 'always-available' => 'and', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'goodbye', + ], + ], + ], + ['eng-GB'], + true, + false, + 316, + '8-69faab6268350295550de7d587bc323d', + ], + [ + 'hello/everyone', + [ + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'hello', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'everyone', + ], + ], + ], + ['eng-GB'], + true, + false, + 315, + '6-ed881bac6397ede33c0a285c9f50bb83', + ], + [ + 'well/ha-ha-ha', + [ + [ + 'always-available' => true, + 'translations' => [ + 'always-available' => 'well', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'ha-ha-ha', + ], + ], + ], + ['eng-GB'], + false, + false, + 317, + '10-17a197f4bbe127c368b889a67effd1b3', + ], + ]; + } + + /** + * Test for the lookup() method. + * + * Testing that UrlAlias is found and has expected state. + * + * @dataProvider providerForTestLookupCustomLocationUrlAlias + * @depends testLookup + * @group location + * @group custom + */ + public function testLookupCustomLocationUrlAlias( + $url, + array $pathData, + array $languageCodes, + $forward, + $alwaysAvailable, + $destination, + $id + ) { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_custom.php'); + + $urlAlias = $handler->lookup($url); + self::assertInstanceOf(UrlAlias::class, $urlAlias); + self::assertEquals( + new UrlAlias( + [ + 'id' => $id, + 'type' => UrlAlias::LOCATION, + 'destination' => $destination, + 'languageCodes' => $languageCodes, + 'pathData' => $pathData, + 'alwaysAvailable' => $alwaysAvailable, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => $forward, + ] + ), + $urlAlias + ); + } + + /** + * Test for the lookup() method. + * + * Testing that UrlAlias is found and has expected state. + * + * @dataProvider providerForTestLookupCustomLocationUrlAlias + * @depends testLookup + * @group location + * @group custom + */ + public function testLookupCustomLocationUrlAliasCaseCorrection( + $url, + array $pathData, + array $languageCodes, + $forward, + $alwaysAvailable, + $destination, + $id + ) { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_custom.php'); + + $urlAlias = $handler->lookup(strtoupper($url)); + + self::assertInstanceOf(UrlAlias::class, $urlAlias); + self::assertEquals( + new UrlAlias( + [ + 'id' => $id, + 'type' => UrlAlias::LOCATION, + 'destination' => $destination, + 'languageCodes' => $languageCodes, + 'pathData' => $pathData, + 'alwaysAvailable' => $alwaysAvailable, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => $forward, + ] + ), + $urlAlias + ); + } + + public function providerForTestLookupVirtualUrlAlias() + { + return [ + [ + 'hello/and', + '6-be5d5d37542d75f93a87094459f76678', + ], + [ + 'HELLO/AND', + '6-be5d5d37542d75f93a87094459f76678', + ], + ]; + } + + /** + * Test for the lookup() method. + * + * Testing that NOP action redirects to site root. + * + * @dataProvider providerForTestLookupVirtualUrlAlias + * @depends testLookup + * @group virtual + */ + public function testLookupVirtualUrlAlias($url, $id) + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_custom.php'); + + $urlAlias = $handler->lookup($url); + + $this->assertVirtualUrlAliasValid($urlAlias, $id); + } + + public function providerForTestLookupResourceUrlAlias() + { + return [ + [ + 'is-alive', + [ + [ + 'always-available' => true, + 'translations' => [ + 'eng-GB' => 'is-alive', + ], + ], + ], + ['eng-GB'], + true, + true, + 'ezinfo/isalive', + '0-d003895fa282a14c8ec3eddf23ca4ca2', + ], + [ + 'is-alive/then/search', + [ + [ + 'always-available' => true, + 'translations' => [ + 'eng-GB' => 'is-alive', + ], + ], + [ + 'always-available' => true, + 'translations' => [ + 'always-available' => 'then', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'search', + ], + ], + ], + ['cro-HR'], + false, + false, + 'content/search', + '3-06a943c59f33a34bb5924aaf72cd2995', + ], + ]; + } + + /** + * Test for the lookup() method. + * + * Testing that UrlAlias is found and has expected state. + * + * @dataProvider providerForTestLookupResourceUrlAlias + * @depends testLookup + * @group resource + */ + public function testLookupResourceUrlAlias( + $url, + $pathData, + array $languageCodes, + $forward, + $alwaysAvailable, + $destination, + $id + ) { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); + + $urlAlias = $handler->lookup($url); + + self::assertInstanceOf(UrlAlias::class, $urlAlias); + self::assertEquals( + new UrlAlias( + [ + 'id' => $id, + 'type' => UrlAlias::RESOURCE, + 'destination' => $destination, + 'languageCodes' => $languageCodes, + 'pathData' => $pathData, + 'alwaysAvailable' => $alwaysAvailable, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => $forward, + ] + ), + $urlAlias + ); + } + + /** + * Test for the lookup() method. + * + * Testing that UrlAlias is found and has expected state. + * + * @dataProvider providerForTestLookupResourceUrlAlias + * @depends testLookup + * @group resource + */ + public function testLookupResourceUrlAliasCaseInsensitive( + $url, + $pathData, + array $languageCodes, + $forward, + $alwaysAvailable, + $destination, + $id + ) { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); + + $urlAlias = $handler->lookup(strtoupper($url)); + + self::assertInstanceOf(UrlAlias::class, $urlAlias); + self::assertEquals( + new UrlAlias( + [ + 'id' => $id, + 'type' => UrlAlias::RESOURCE, + 'destination' => $destination, + 'languageCodes' => $languageCodes, + 'pathData' => $pathData, + 'alwaysAvailable' => $alwaysAvailable, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => $forward, + ] + ), + $urlAlias + ); + } + + /** + * Test for the lookup() method with uppercase utf8 characters. + * + * + * @depends testLookup + */ + public function testLookupUppercaseIri() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_iri.php'); + + $urlAlias = $handler->lookup('ŒÄ'); + self::assertInstanceOf(UrlAlias::class, $urlAlias); + } + + protected function assertVirtualUrlAliasValid(UrlAlias $urlAlias, $id) + { + self::assertInstanceOf(UrlAlias::class, $urlAlias); + self::assertEquals($id, $urlAlias->id); + self::assertEquals(UrlAlias::VIRTUAL, $urlAlias->type); + } + + /** + * Test for the listURLAliasesForLocation() method. + */ + public function testListURLAliasesForLocation() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location.php'); + + $urlAliases = $handler->listURLAliasesForLocation(315); + + self::assertEquals( + [ + new UrlAlias( + [ + 'id' => '2-b8a9f715dbb64fd5c56e7783c6820a61', + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => true, + 'translations' => ['cro-HR' => 'jedan'], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + new UrlAlias( + [ + 'id' => '2-c67ed9a09ab136fae610b6a087d82e21', + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => ['cro-HR'], + 'pathData' => [ + [ + 'always-available' => true, + 'translations' => ['cro-HR' => 'jedan'], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + ], + $urlAliases + ); + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @todo document + * + * + * @depends testLookupLocationUrlAlias + * @group publish + */ + public function testPublishUrlAliasForLocation() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $handler->publishUrlAliasForLocation(314, 2, 'simple', 'eng-GB', true); + $publishedUrlAlias = $handler->lookup('simple'); + + self::assertEquals(4, $this->countRows()); + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('simple'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => true, + 'translations' => [ + 'eng-GB' => 'simple', + 'cro-HR' => 'path314', + ], + ], + ], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $publishedUrlAlias + ); + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @todo document + * + * + * @depends testPublishUrlAliasForLocation + * @group publish + */ + public function testPublishUrlAliasForLocationRepublish() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $handler->publishUrlAliasForLocation(314, 2, 'simple', 'eng-GB', true); + $publishedUrlAlias = $handler->lookup('simple'); + $handler->publishUrlAliasForLocation(314, 2, 'simple', 'eng-GB', true); + $republishedUrlAlias = $handler->lookup('simple'); + + self::assertEquals(4, $this->countRows()); + self::assertEquals( + $publishedUrlAlias, + $republishedUrlAlias + ); + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @todo document + * + * + * @depends testPublishUrlAliasForLocation + * @group publish + */ + public function testPublishUrlAliasCreatesUniqueAlias() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $handler->publishUrlAliasForLocation(314, 2, 'simple', 'eng-GB', true); + $handler->publishUrlAliasForLocation(315, 2, 'simple', 'eng-GB', true); + self::assertEquals(5, $this->countRows()); + + $urlAlias = $handler->lookup('simple2'); + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('simple2'), + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => true, + 'translations' => [ + 'eng-GB' => 'simple2', + ], + ], + ], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * @return array + */ + public function providerForTestPublishUrlAliasForLocationComplex() + { + return $this->providerForTestLookupLocationUrlAlias(); + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @todo document + * + * + * @dataProvider providerForTestPublishUrlAliasForLocationComplex + * @depends testPublishUrlAliasForLocation + * @group publish + */ + public function testPublishUrlAliasForLocationComplex( + $url, + $pathData, + array $languageCodes, + $alwaysAvailable, + $locationId, + $id + ) { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR', true); + $handler->publishUrlAliasForLocation(315, 314, 'dva', 'cro-HR', false); + $handler->publishUrlAliasForLocation(315, 314, 'two', 'eng-GB', false); + $handler->publishUrlAliasForLocation(316, 315, 'tri', 'cro-HR', false); + $handler->publishUrlAliasForLocation(316, 315, 'three', 'eng-GB', false); + $handler->publishUrlAliasForLocation(316, 315, 'drei', 'ger-DE', false); + + $urlAlias = $handler->lookup($url); + + self::assertInstanceOf(UrlAlias::class, $urlAlias); + self::assertEquals( + new UrlAlias( + [ + 'id' => $id, + 'type' => UrlAlias::LOCATION, + 'destination' => $locationId, + 'languageCodes' => $languageCodes, + 'pathData' => $pathData, + 'alwaysAvailable' => $alwaysAvailable, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @todo document + * + * + * @depends testPublishUrlAliasForLocation + * @group publish + */ + public function testPublishUrlAliasForLocationSameAliasForMultipleLanguages() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR', false); + $urlAlias1 = $handler->lookup('jedan'); + $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'eng-GB', false); + $urlAlias2 = $handler->lookup('jedan'); + + self::assertEquals(4, $this->countRows()); + + foreach ($urlAlias2 as $propertyName => $propertyValue) { + if ($propertyName === 'languageCodes') { + self::assertEquals( + ['cro-HR', 'eng-GB'], + $urlAlias2->languageCodes + ); + } elseif ($propertyName === 'pathData') { + self::assertEquals( + [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + 'eng-GB' => 'jedan', + ], + ], + ], + $urlAlias2->pathData + ); + } else { + self::assertEquals( + $urlAlias1->$propertyName, + $urlAlias2->$propertyName + ); + } + } + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @todo document + * + * + * @depends testPublishUrlAliasForLocation + * @group publish + */ + public function testPublishUrlAliasForLocationDowngradesOldEntryToHistory() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR', false); + $handler->publishUrlAliasForLocation(314, 2, 'dva', 'cro-HR', true); + + self::assertEquals(5, $this->countRows()); + + $newUrlAlias = $handler->lookup('dva'); + + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-c67ed9a09ab136fae610b6a087d82e21', + 'type' => 0, + 'destination' => 314, + 'languageCodes' => ['cro-HR'], + 'pathData' => [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + ], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $newUrlAlias + ); + + $historyUrlAlias = $handler->lookup('jedan'); + + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-6896260129051a949051c3847c34466f', + 'type' => 0, + 'destination' => 314, + 'languageCodes' => ['cro-HR'], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $historyUrlAlias + ); + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @todo document + * + * + * @depends testPublishUrlAliasForLocation + * @depends testPublishUrlAliasForLocationSameAliasForMultipleLanguages + * @group publish + * @group downgrade + */ + public function testPublishUrlAliasForLocationDowngradesOldEntryRemovesLanguage() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR'); + $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'eng-GB'); + $handler->publishUrlAliasForLocation(314, 2, 'dva', 'eng-GB'); + + self::assertEquals(5, $this->countRows()); + + $urlAlias = $handler->lookup('dva'); + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-c67ed9a09ab136fae610b6a087d82e21', + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + 'eng-GB' => 'dva', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $downgradedUrlAlias = $handler->lookup('jedan'); + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-6896260129051a949051c3847c34466f', + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => ['cro-HR'], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + 'eng-GB' => 'dva', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $downgradedUrlAlias + ); + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @todo document + * + * + * @depends testPublishUrlAliasForLocation + * @depends testPublishUrlAliasForLocationDowngradesOldEntryToHistory + * @group publish + */ + public function testPublishUrlAliasForLocationReusesHistory() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR'); + $urlAlias = $handler->lookup('jedan'); + $handler->publishUrlAliasForLocation(314, 2, 'dva', 'cro-HR'); + $countBeforeReusing = $this->countRows(); + $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR'); + $urlAliasReusesHistory = $handler->lookup('jedan'); + + self::assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + self::assertEquals( + $urlAlias, + $urlAliasReusesHistory + ); + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @todo document + * + * + * @depends testPublishUrlAliasForLocation + * @depends testPublishUrlAliasForLocationDowngradesOldEntryToHistory + * @group publish + */ + public function testPublishUrlAliasForLocationReusesHistoryOfDifferentLanguage() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $handler->publishUrlAliasForLocation(314, 2, 'jedan', 'cro-HR'); + $handler->publishUrlAliasForLocation(314, 2, 'one-history', 'eng-GB'); + $handler->publishUrlAliasForLocation(314, 2, 'one-new', 'eng-GB'); + $countBeforeReusing = $this->countRows(); + $handler->publishUrlAliasForLocation(314, 2, 'one-history', 'cro-HR'); + $urlAliasReusesHistory = $handler->lookup('one-history'); + + self::assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('one-history'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => ['cro-HR'], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'one-history', + 'eng-GB' => 'one-new', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAliasReusesHistory + ); + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @todo document + * + * + * @depends testPublishUrlAliasForLocation + * @group publish + */ + public function testPublishUrlAliasForLocationReusesCustomAlias() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); + + $countBeforeReusing = $this->countRows(); + $handler->publishUrlAliasForLocation(314, 2, 'custom-hello', 'eng-GB', false); + $urlAlias = $handler->lookup('custom-hello'); + + self::assertEquals( + $countBeforeReusing, + $this->countRows() + ); + self::assertFalse($urlAlias->isCustom); + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @todo document + * + * + * @depends testPublishUrlAliasForLocation + */ + public function testPublishUrlAliasForLocationReusingNopElement() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); + + $countBeforeReusing = $this->countRows(); + $virtualUrlAlias = $handler->lookup('nop-element/search'); + $handler->publishUrlAliasForLocation(315, 2, 'nop-element', 'eng-GB', false); + $publishedLocationUrlAlias = $handler->lookup('nop-element'); + + self::assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + self::assertInstanceOf(UrlAlias::class, $publishedLocationUrlAlias); + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-de55c2fff721217cc4cb67b58dc35f85', + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'nop-element'], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $publishedLocationUrlAlias + ); + + $virtualUrlAliasReloaded = $handler->lookup('nop-element/search'); + foreach ($virtualUrlAliasReloaded as $propertyName => $propertyValue) { + if ($propertyName === 'pathData') { + self::assertEquals( + [ + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'nop-element'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'search'], + ], + ], + $virtualUrlAliasReloaded->pathData + ); + } else { + self::assertEquals( + $virtualUrlAlias->$propertyName, + $virtualUrlAliasReloaded->$propertyName + ); + } + } + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @todo document + * + * + * @depends testPublishUrlAliasForLocation + * @depends testPublishUrlAliasForLocationReusingNopElement + */ + public function testPublishUrlAliasForLocationReusingNopElementChangesCustomPath() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); + + $countBeforeReusing = $this->countRows(); + $virtualUrlAlias = $handler->lookup('nop-element/search'); + $handler->publishUrlAliasForLocation(315, 2, 'nop-element', 'eng-GB', false); + $handler->publishUrlAliasForLocation(315, 2, 'nop-element-renamed', 'eng-GB', false); + $virtualUrlAliasChanged = $handler->lookup('nop-element-renamed/search'); + + self::assertEquals( + $countBeforeReusing + 1, + $this->countRows() + ); + + foreach ($virtualUrlAliasChanged as $propertyName => $propertyValue) { + if ($propertyName === 'pathData') { + self::assertEquals( + [ + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'nop-element-renamed'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'search'], + ], + ], + $virtualUrlAliasChanged->pathData + ); + } else { + self::assertEquals( + $virtualUrlAlias->$propertyName, + $virtualUrlAliasChanged->$propertyName + ); + } + } + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @todo document + * + * + * @depends testPublishUrlAliasForLocation + * @depends testPublishUrlAliasForLocationReusingNopElementChangesCustomPath + */ + public function testPublishUrlAliasForLocationReusingNopElementChangesCustomPathAndCreatesHistory() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); + + $handler->publishUrlAliasForLocation(315, 2, 'nop-element', 'eng-GB', false); + $handler->publishUrlAliasForLocation(315, 2, 'nop-element-renamed', 'eng-GB', false); + + $customUrlAliasChanged = $handler->lookup('nop-element-renamed/search'); + $customUrlAliasHistory = $handler->lookup('nop-element/search'); + + self::assertTrue($customUrlAliasHistory->isHistory); + $customUrlAliasHistory->isHistory = false; + self::assertEquals( + $customUrlAliasChanged, + $customUrlAliasHistory + ); + } + + /** + * Test for the publishUrlAliasForLocation() method. + */ + public function testPublishUrlAliasForLocationUpdatesLocationPathIdentificationString() + { + $handler = $this->getHandler(); + $locationGateway = $this->getLocationGateway(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + // Publishes the alias indicating that language is main, triggering updating of path_identification_string + $handler->publishUrlAliasForLocation(316, 315, 'TEST TEST TEST', 'eng-GB', false, true); + + $locationData = $locationGateway->getBasicNodeData(316); + + self::assertEquals('path314/path315/test_test_test', $locationData['path_identification_string']); + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @group cleanup + */ + public function testPublishUrlAliasReuseNopCleanupCustomAliasIsDestroyed() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_cleanup_nop.php'); + + $handler->lookup('nop-element/search'); + $handler->publishUrlAliasForLocation(314, 2, 'nop-element', 'cro-HR', false); + + $urlAlias = $handler->lookup('jedan'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('jedan'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => ['cro-HR' => 'jedan'], + ], + ], + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('nop-element'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('nop-element'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'nop-element', + 'eng-GB' => 'dva', + ], + ], + ], + 'languageCodes' => [ + 'cro-HR', + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + try { + $handler->lookup('nop-element/search'); + $this->fail('Custom alias is not destroyed'); + } catch (NotFoundException $e) { + // Custom alias is destroyed by reusing NOP entry with existing autogenerated alias + // on the same level (that means link and ID are updated to the existing alias ID, + // so custom alias children entries are no longer properly linked (parent-link)) + } + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @group cleanup + */ + public function testPublishUrlAliasReuseHistoryCleanup() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_cleanup_history.php'); + + $handler->publishUrlAliasForLocation(314, 2, 'tri', 'cro-HR', false); + + $urlAlias = $handler->lookup('jedan'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('jedan'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => ['cro-HR' => 'jedan'], + ], + ], + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('tri'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('tri'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'dva', + ], + ], + ], + 'languageCodes' => [ + 'cro-HR', + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @group cleanup + */ + public function testPublishUrlAliasReuseAutogeneratedCleanup() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_cleanup_reusing.php'); + + $handler->publishUrlAliasForLocation(314, 2, 'dva', 'cro-HR', false); + + $urlAlias = $handler->lookup('jedan'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('jedan'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => ['cro-HR' => 'jedan'], + ], + ], + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('dva'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'dva', + ], + ], + ], + 'languageCodes' => [ + 'cro-HR', + 'eng-GB', + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the createCustomUrlAlias() method. + * + * + * @group create + * @group custom + */ + public function testCreateCustomUrlAliasBehaviour() + { + $handlerMock = $this->getPartlyMockedHandler(['createUrlAlias']); + + $handlerMock->expects( + $this->once() + )->method( + 'createUrlAlias' + )->with( + $this->equalTo('eznode:1'), + $this->equalTo('path'), + $this->equalTo(false), + $this->equalTo(null), + $this->equalTo(false) + )->will( + $this->returnValue( + new UrlAlias() + ) + ); + + $this->assertInstanceOf( + UrlAlias::class, + $handlerMock->createCustomUrlAlias(1, 'path') + ); + } + + /** + * Test for the createGlobalUrlAlias() method. + * + * + * @group create + * @group global + */ + public function testCreateGlobalUrlAliasBehaviour() + { + $handlerMock = $this->getPartlyMockedHandler(['createUrlAlias']); + + $handlerMock->expects( + $this->once() + )->method( + 'createUrlAlias' + )->with( + $this->equalTo('module/module'), + $this->equalTo('path'), + $this->equalTo(false), + $this->equalTo(null), + $this->equalTo(false) + )->will( + $this->returnValue( + new UrlAlias() + ) + ); + + $this->assertInstanceOf( + UrlAlias::class, + $handlerMock->createGlobalUrlAlias('module/module', 'path') + ); + } + + /** + * Test for the createUrlAlias() method. + * + * + * @group create + * @group custom + */ + public function testCreateCustomUrlAlias() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $path = 'custom-location-alias'; + $customUrlAlias = $handler->createCustomUrlAlias( + 314, + $path, + false, + 'cro-HR', + false + ); + + self::assertEquals(4, $this->countRows()); + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5($path), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'custom-location-alias', + ], + ], + ], + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => false, + ] + ), + $customUrlAlias + ); + } + + /** + * Test for the createUrlAlias() method. + * + * + * @group create + * @group custom + */ + public function testCreateCustomUrlAliasWithNonameParts() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $path = 'there-is-a//custom-location-alias//here'; + $customUrlAlias = $handler->createCustomUrlAlias( + 314, + $path, + false, + 'cro-HR', + false + ); + + self::assertEquals(8, $this->countRows()); + + self::assertEquals( + new UrlAlias( + [ + 'id' => '7-' . md5('here'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'pathData' => [ + [ + 'always-available' => true, + 'translations' => [ + 'always-available' => 'there-is-a', + ], + ], + [ + 'always-available' => true, + 'translations' => [ + 'always-available' => 'noname2', + ], + ], + [ + 'always-available' => true, + 'translations' => [ + 'always-available' => 'custom-location-alias', + ], + ], + [ + 'always-available' => true, + 'translations' => [ + 'always-available' => 'noname4', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'here', + ], + ], + ], + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => false, + ] + ), + $customUrlAlias + ); + } + + /** + * Test for the createUrlAlias() method. + * + * + * @group create + * @group custom + * + * @todo pathData + */ + public function testCreatedCustomUrlAliasIsLoadable() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $path = 'custom-location-alias'; + $customUrlAlias = $handler->createCustomUrlAlias( + 314, + $path, + false, + 'cro-HR', + false + ); + $loadedCustomUrlAlias = $handler->lookup($path); + + self::assertEquals(4, $this->countRows()); + + foreach ($loadedCustomUrlAlias as $propertyName => $propertyValue) { + if ($propertyName === 'pathData') { + self::assertEquals( + [ + [ + 'always-available' => false, + 'translations' => ['cro-HR' => $path], + ], + ], + $loadedCustomUrlAlias->$propertyName + ); + } else { + self::assertEquals( + $customUrlAlias->$propertyName, + $loadedCustomUrlAlias->$propertyName + ); + } + } + } + + /** + * Test for the createUrlAlias() method. + * + * + * @group create + * @group custom + */ + public function testCreateCustomUrlAliasWithNopElement(): void + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $path = 'ribar/palunko'; + $customUrlAlias = $handler->createCustomUrlAlias( + 314, + $path, + false, + 'cro-HR', + true + ); + + self::assertEquals(5, $this->countRows()); + self::assertEquals( + new UrlAlias( + [ + 'id' => '4-' . md5('palunko'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'pathData' => [ + [ + 'always-available' => true, + 'translations' => [ + 'always-available' => 'ribar', + ], + ], + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'palunko', + ], + ], + ], + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => false, + ] + ), + $customUrlAlias + ); + + // test that valid NOP element has been created + $url = 'ribar'; + $urlAlias = $handler->lookup($url); + + $this->assertVirtualUrlAliasValid( + $urlAlias, + '0-' . md5($url) + ); + } + + /** + * Test for the createUrlAlias() method. + * + * + * @group create + * @group custom + */ + public function testCreateCustomUrlAliasReusesHistory() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); + + $countBeforeReusing = $this->countRows(); + $handler->createCustomUrlAlias( + 314, + 'history-hello', + true, + 'eng-GB', + true + ); + + self::assertEquals( + $countBeforeReusing, + $this->countRows() + ); + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-da94285592c46d4396d3ca6904a4aa8f', + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => true, + 'translations' => ['eng-GB' => 'history-hello'], + ], + ], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => true, + ] + ), + $handler->lookup('history-hello') + ); + } + + /** + * @group create + * @group custom + */ + public function testCreateCustomUrlAliasAddLanguage(): void + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + // create a new custom entry since the existing one is a system URL + $handler->createCustomUrlAlias(314, 'custom-path314', false, 'cro-HR', true); + + $countBeforeReusing = $this->countRows(); + $handler->createCustomUrlAlias( + 314, + 'custom-path314', + false, + 'eng-GB', + true + ); + + self::assertEquals( + $countBeforeReusing, + $this->countRows() + ); + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-e8797691eeba6b598a353e5c5af99438', + 'type' => UrlAlias::LOCATION, + 'destination' => '314', + 'languageCodes' => ['cro-HR', 'eng-GB'], + 'pathData' => [ + [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'custom-path314', + 'eng-GB' => 'custom-path314', + ], + ], + ], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => false, + ] + ), + $handler->lookup('custom-path314') + ); + } + + /** + * Test for the createUrlAlias() method. + * + * + * @group create + * @group custom + */ + public function testCreateCustomUrlAliasReusesHistoryOfDifferentLanguage() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); + + $countBeforeReusing = $this->countRows(); + $handler->createCustomUrlAlias( + 314, + 'history-hello', + true, + 'cro-HR', + true + ); + + self::assertEquals( + $countBeforeReusing, + $this->countRows() + ); + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-da94285592c46d4396d3ca6904a4aa8f', + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => ['cro-HR'], + 'pathData' => [ + [ + 'always-available' => true, + 'translations' => ['cro-HR' => 'history-hello'], + ], + ], + 'alwaysAvailable' => true, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => true, + ] + ), + $handler->lookup('history-hello') + ); + } + + /** + * Test for the createUrlAlias() method. + * + * + * @group create + * @group custom + */ + public function testCreateCustomUrlAliasReusesNopElement() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); + + $countBeforeReusing = $this->countRows(); + $handler->createCustomUrlAlias( + 314, + 'nop-element', + true, + 'cro-HR', + true + ); + + self::assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + // Check that custom alias whose nop element was reused still works as expected + self::assertEquals( + new UrlAlias( + [ + 'id' => '2-06a943c59f33a34bb5924aaf72cd2995', + 'type' => UrlAlias::RESOURCE, + 'destination' => 'content/search', + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => true, + 'translations' => ['cro-HR' => 'nop-element'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'search'], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => false, + ] + ), + $handler->lookup('nop-element/search') + ); + } + + /** + * Test for the createUrlAlias() method. + * + * + * @group create + * @group custom + */ + public function testCreateCustomUrlAliasReusesLocationElement() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_reusing.php'); + + $countBeforeReusing = $this->countRows(); + $locationUrlAlias = $handler->lookup('autogenerated-hello'); + $handler->createCustomUrlAlias( + 315, + 'autogenerated-hello/custom-location-alias-for-315', + true, + 'cro-HR', + true + ); + + self::assertEquals( + $countBeforeReusing + 1, + $this->countRows() + ); + + // Check that location alias still works as expected + self::assertEquals( + $locationUrlAlias, + $handler->lookup('autogenerated-hello') + ); + } + + /** + * Test for the listGlobalURLAliases() method. + * + * + * @depends testLookupResourceUrlAlias + */ + public function testListGlobalURLAliases() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); + + $globalAliasList = $handler->listGlobalURLAliases(); + + self::assertEquals( + [ + $handler->lookup('is-alive'), + $handler->lookup('is-alive/then/search'), + $handler->lookup('nop-element/search'), + ], + $globalAliasList + ); + } + + /** + * Test for the listGlobalURLAliases() method. + * + * + * @depends testLookupResourceUrlAlias + */ + public function testListGlobalURLAliasesWithLanguageCode() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); + + $globalAliasList = $handler->listGlobalURLAliases('eng-GB'); + + self::assertEquals( + [ + $handler->lookup('is-alive'), + $handler->lookup('nop-element/search'), + ], + $globalAliasList + ); + } + + /** + * Test for the listGlobalURLAliases() method. + * + * + * @depends testLookupResourceUrlAlias + */ + public function testListGlobalURLAliasesWithOffset() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); + + $globalAliasList = $handler->listGlobalURLAliases(null, 2); + + self::assertEquals( + [ + $handler->lookup('nop-element/search'), + ], + $globalAliasList + ); + } + + /** + * Test for the listGlobalURLAliases() method. + * + * + * @depends testLookupResourceUrlAlias + */ + public function testListGlobalURLAliasesWithOffsetAndLimit() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); + + $globalAliasList = $handler->listGlobalURLAliases(null, 1, 1); + + self::assertEquals( + [ + $handler->lookup('is-alive/then/search'), + ], + $globalAliasList + ); + } + + /** + * Test for the locationDeleted() method. + */ + public function testLocationDeleted() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_delete.php'); + + $countBeforeDeleting = $this->countRows(); + $countAfterFirstDeleting = $countBeforeDeleting - 5; + + $handler->locationDeleted(5); + + self::assertEquals( + $countAfterFirstDeleting, + $this->countRows() + ); + + self::assertEmpty( + $handler->listURLAliasesForLocation(5) + ); + + $removedAliases = [ + 'moved-original-parent/moved-history', + 'moved-original-parent/sub', + 'moved-original-parent', + 'moved-original-parent-history', + 'custom-below/moved-original-parent-custom', + ]; + foreach ($removedAliases as $path) { + try { + $handler->lookup($path); + $this->fail("Alias '$path' not removed!"); + } catch (NotFoundException $e) { + // Do nothing + } + } + + // Deleting location historically related with some relative alias + $handler->locationDeleted(9); + + self::assertEquals( + $countAfterFirstDeleting - 1, + $this->countRows() + ); + self::assertCount( + 1, + $handler->listURLAliasesForLocation(10) + ); + + $handler->lookup('to-delete-folder/article-to-move-relative-alias'); + } + + /** + * Test for the locationMoved() method. + */ + public function testLocationMovedHistorize() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); + + $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); + $handler->locationMoved(4, 2, 3); + + $urlAlias = $handler->lookup('move-this'); + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('move-this'), + 'type' => UrlAlias::LOCATION, + 'destination' => '4', + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'move-this'], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationMoved() method. + */ + public function testLocationMovedHistory() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); + + $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); + $handler->locationMoved(4, 2, 3); + + $urlAlias = $handler->lookup('move-this-history'); + self::assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('move-this-history'), + 'type' => UrlAlias::LOCATION, + 'destination' => '4', + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'move-this-history'], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationMoved() method. + */ + public function testLocationMovedHistorySubtree() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); + + $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); + $handler->locationMoved(4, 2, 3); + + $urlAlias = $handler->lookup('move-this/sub1/sub2'); + self::assertEquals( + new UrlAlias( + [ + 'id' => '5-' . md5('sub2'), + 'type' => UrlAlias::LOCATION, + 'destination' => '6', + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'move-this'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'sub1'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'sub2'], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationMoved() method. + */ + public function testLocationMovedReparent() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); + + $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); + $handler->locationMoved(4, 2, 3); + + $urlAlias = $handler->lookup('move-here/move-this/sub1'); + self::assertEquals( + new UrlAlias( + [ + 'id' => '9-' . md5('sub1'), + 'type' => UrlAlias::LOCATION, + 'destination' => '5', + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'move-here'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'move-this'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'sub1'], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationMoved() method. + */ + public function testLocationMovedReparentHistory() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); + + $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); + $handler->locationMoved(4, 2, 3); + + $handler->lookup('move-here/move-this-history'); + } + + /** + * Test for the locationMoved() method. + */ + public function testLocationMovedReparentSubtree() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); + + $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); + $handler->locationMoved(4, 2, 3); + + $urlAlias = $handler->lookup('move-here/move-this/sub1/sub2'); + self::assertEquals( + new UrlAlias( + [ + 'id' => '5-' . md5('sub2'), + 'type' => UrlAlias::LOCATION, + 'destination' => '6', + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'move-here'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'move-this'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'sub1'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'sub2'], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationMoved() method. + */ + public function testLocationMovedReparentSubtreeHistory() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); + + $handler->publishUrlAliasForLocation(4, 3, 'move-this', 'eng-GB', false); + $handler->locationMoved(4, 2, 3); + + $urlAlias = $handler->lookup('move-here/move-this/sub1/sub2-history'); + self::assertEquals( + new UrlAlias( + [ + 'id' => '5-' . md5('sub2-history'), + 'type' => UrlAlias::LOCATION, + 'destination' => '6', + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'move-here'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'move-this'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'sub1'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'sub2-history'], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * @throws \Doctrine\DBAL\DBALException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + public function testLocationMovedReparentWithCustomAlias(): void + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_move.php'); + + $handler->createCustomUrlAlias( + 6, // Location's id of 'sub2' alias + 'move-this/sub1/sub2-alias', + false, + 'eng-GB', + false + ); + + $handler->publishUrlAliasForLocation( + 5, // Location's id of 'sub1' alias that we are moving + 3, // Location's id of 'move-here' alias that would be a parent + 'sub1', + 'eng-GB', + false + ); + + $handler->locationMoved( + 5, + 4, + 3 + ); + + $customUrlAlias = $handler->lookup('move-here/sub1/sub2-alias'); + + self::assertEquals( + new UrlAlias( + [ + 'id' => '10-' . md5('sub2-alias'), + 'type' => UrlAlias::LOCATION, + 'destination' => '6', + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'move-here'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'sub1'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'sub2-alias'], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => false, + ] + ), + $customUrlAlias + ); + } + + /** + * Test for the locationCopied() method. + */ + public function testLocationCopiedCopiedLocationAliasIsValid() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_copy.php'); + + $urlAlias = $handler->lookup('move-this'); + + $handler->locationCopied(4, 400, 3); + + self::assertEquals( + $urlAlias, + $handler->lookup('move-this') + ); + } + + /** + * Test for the locationCopied() method. + */ + public function testLocationCopiedCopiedSubtreeIsValid() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_copy.php'); + + $urlAlias = $handler->lookup('move-this/sub1/sub2'); + + $handler->locationCopied(4, 400, 3); + + self::assertEquals( + $urlAlias, + $handler->lookup('move-this/sub1/sub2') + ); + } + + /** + * Test for the locationCopied() method. + */ + public function testLocationCopiedHistoryNotCopied() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_copy.php'); + + $handler->locationCopied(4, 400, 3); + + $handler->lookup('move-here/move-this-history'); + } + + /** + * Test for the locationCopied() method. + */ + public function testLocationCopiedSubtreeHistoryNotCopied() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_copy.php'); + + $handler->locationCopied(4, 400, 3); + + $handler->lookup('move-here/move-this/sub1/sub2-history'); + } + + /** + * Test for the locationCopied() method. + */ + public function testLocationCopiedSubtree() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_copy.php'); + + $countBeforeCopying = $this->countRows(); + + $handler->locationCopied(4, 400, 3); + + self::assertEquals( + $countBeforeCopying + 2, + $this->countRows() + ); + + $urlAlias = $handler->lookup('move-here/move-this/sub1/sub2'); + self::assertEquals( + new UrlAlias( + [ + 'id' => '10-' . md5('sub2'), + 'type' => UrlAlias::LOCATION, + 'destination' => 600, + 'languageCodes' => ['eng-GB'], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'move-here'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'move-this'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'sub1'], + ], + [ + 'always-available' => false, + 'translations' => ['eng-GB' => 'sub2'], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the loadUrlAlias() method. + * + * + * @dataProvider providerForTestLookupLocationMultipleLanguages + */ + public function testLoadAutogeneratedUrlAlias( + $url, + array $pathData, + array $languageCodes, + $alwaysAvailable, + $locationId, + $id + ) { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_multilang.php'); + + $urlAlias = $handler->loadUrlAlias($id); + + self::assertInstanceOf(UrlAlias::class, $urlAlias); + self::assertEquals( + new UrlAlias( + [ + 'id' => $id, + 'type' => UrlAlias::LOCATION, + 'destination' => $locationId, + 'languageCodes' => $languageCodes, + 'pathData' => $pathData, + 'alwaysAvailable' => $alwaysAvailable, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the loadUrlAlias() method. + * + * + * @dataProvider providerForTestLookupResourceUrlAlias + */ + public function testLoadResourceUrlAlias( + $url, + $pathData, + array $languageCodes, + $forward, + $alwaysAvailable, + $destination, + $id + ) { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_resource.php'); + + $urlAlias = $handler->loadUrlAlias($id); + + self::assertInstanceOf(UrlAlias::class, $urlAlias); + self::assertEquals( + new UrlAlias( + [ + 'id' => $id, + 'type' => UrlAlias::RESOURCE, + 'destination' => $destination, + 'languageCodes' => $languageCodes, + 'pathData' => $pathData, + 'alwaysAvailable' => $alwaysAvailable, + 'isHistory' => false, + 'isCustom' => true, + 'forward' => $forward, + ] + ), + $urlAlias + ); + } + + /** + * Test for the loadUrlAlias() method. + * + * + * @dataProvider providerForTestLookupVirtualUrlAlias + */ + public function testLoadVirtualUrlAlias($url, $id) + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location_custom.php'); + + $urlAlias = $handler->loadUrlAlias($id); + + $this->assertVirtualUrlAliasValid($urlAlias, $id); + } + + protected function getHistoryAlias() + { + return new UrlAlias( + [ + 'id' => '3-5f46413bb0ba5998caef84ab1ea590e1', + 'type' => UrlAlias::LOCATION, + 'destination' => '316', + 'pathData' => [ + [ + 'always-available' => true, + 'translations' => ['cro-HR' => 'jedan'], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri-history', + ], + ], + ], + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ); + } + + /** + * Test for the loadUrlAlias() method. + */ + public function testLoadHistoryUrlAlias() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_location.php'); + + $historyAlias = $this->getHistoryAlias(); + $urlAlias = $handler->loadUrlAlias($historyAlias->id); + + self::assertEquals( + $historyAlias, + $urlAlias + ); + } + + /** + * Test for the loadUrlAlias() method. + */ + public function testLoadUrlAliasThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getHandler(); + + $handler->loadUrlAlias('non-existent'); + } + + public function providerForTestPublishUrlAliasForLocationSkipsReservedWord() + { + return [ + [ + 'section', + 'section2', + ], + [ + 'claß', + 'class2', + ], + ]; + } + + /** + * Test for the publishUrlAliasForLocation() method. + * + * @dataProvider providerForTestPublishUrlAliasForLocationSkipsReservedWord + * @covers \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Handler::publishUrlAliasForLocation + * @group publish + */ + public function testPublishUrlAliasForLocationSkipsReservedWord($text, $alias) + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_base.php'); + + $handler->publishUrlAliasForLocation(314, 2, $text, 'kli-KR'); + + $urlAlias = $handler->lookup($alias); + + $this->assertEquals(314, $urlAlias->destination); + $this->assertEquals(['kli-KR'], $urlAlias->languageCodes); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedSimple() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_simple.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(316, 314, 317, 315); + + $this->assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + $urlAlias = $handler->lookup('jedan/swap'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedSimpleWithHistory() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_simple_history.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(316, 314, 317, 315); + + $this->assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + $urlAlias = $handler->lookup('jedan/swap'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('jedan/swap-new'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-new'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-new', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-new'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-new'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-new', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedSimpleWithConflict() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_simple_conflict.php'); + + $urlAlias1TakenExpected = $handler->lookup('jedan/swap-new-2'); + $urlAlias2TakenExpected = $handler->lookup('dva/swap-new-1'); + + $urlAlias1HistorizedExpected = $handler->lookup('jedan/swap-new-1'); + $urlAlias1HistorizedExpected->isHistory = true; + $urlAlias2HistorizedExpected = $handler->lookup('dva/swap-new-2'); + $urlAlias2HistorizedExpected->isHistory = true; + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(316, 314, 317, 315); + + $this->assertEquals( + $countBeforeReusing + 2, + $this->countRows() + ); + + $urlAlias1Taken = $handler->lookup('jedan/swap-new-2'); + $urlAlias2Taken = $handler->lookup('dva/swap-new-1'); + + $urlAlias1Historized = $handler->lookup('jedan/swap-new-1'); + $urlAlias2Historized = $handler->lookup('dva/swap-new-2'); + + $this->assertEquals($urlAlias1TakenExpected, $urlAlias1Taken); + $this->assertEquals($urlAlias2TakenExpected, $urlAlias2Taken); + + $this->assertEquals($urlAlias1HistorizedExpected, $urlAlias1Historized); + $this->assertEquals($urlAlias2HistorizedExpected, $urlAlias2Historized); + + $urlAlias1New = $handler->lookup('jedan/swap-new-22'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-new-22'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-new-22', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias1New + ); + + $urlAlias2New = $handler->lookup('dva/swap-new-12'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-new-12'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-new-12', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias2New + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedSiblingsSimple() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_simple.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(314, 2, 315, 2); + + $this->assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + $urlAlias = $handler->lookup('jedan'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('jedan'), + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('dva'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedSiblingsSimpleReverse() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_simple.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(315, 2, 314, 2); + + $this->assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + $urlAlias = $handler->lookup('jedan'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('jedan'), + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('dva'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedSiblingsSimpleWithHistory() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_simple_history.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(314, 2, 315, 2); + + $this->assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + $urlAlias = $handler->lookup('jedan'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('jedan'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('dva'), + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('jedan-new'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('jedan-new'), + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan-new', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva-new'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('dva-new'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva-new', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedSiblingsSimpleWithHistoryReverse() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_simple_history.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(315, 2, 314, 2); + + $this->assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + $urlAlias = $handler->lookup('jedan'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('jedan'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('dva'), + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('jedan-new'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('jedan-new'), + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan-new', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva-new'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('dva-new'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva-new', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedSiblingsSameName() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_same_name.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(314, 2, 315, 2); + + $this->assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + $urlAlias = $handler->lookup('swap'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('swap'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('swap2'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('swap2'), + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap2', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedSiblingsSameNameReverse() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_same_name.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(315, 2, 314, 2); + + $this->assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + $urlAlias = $handler->lookup('swap'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('swap'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('swap2'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('swap2'), + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap2', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedSiblingsSameNameMultipleLanguages() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_siblings_same_name_multilang.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(314, 2, 315, 2); + + $this->assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + $urlAlias = $handler->lookup('swap-hr'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('swap-hr'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-hr', + 'eng-GB' => 'swap-en2', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('swap-hr2'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('swap-hr2'), + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-hr2', + 'eng-GB' => 'swap-en', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('swap-en'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('swap-en'), + 'type' => UrlAlias::LOCATION, + 'destination' => 315, + 'languageCodes' => [ + 'eng-GB', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-hr2', + 'eng-GB' => 'swap-en', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('swap-en2'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '0-' . md5('swap-en2'), + 'type' => UrlAlias::LOCATION, + 'destination' => 314, + 'languageCodes' => [ + 'eng-GB', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-hr', + 'eng-GB' => 'swap-en2', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedMultipleLanguagesSimple() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_multilang_simple.php'); + + $urlAlias1HRExpected = $handler->lookup('jedan/swap-hr'); + $urlAlias1ENExpected = $handler->lookup('jedan/swap-en'); + $urlAlias2HRExpected = $handler->lookup('dva/swap-hr'); + $urlAlias2ENExpected = $handler->lookup('dva/swap-en'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(316, 314, 317, 315); + + $this->assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + $urlAlias1HR = $handler->lookup('jedan/swap-hr'); + $urlAlias1EN = $handler->lookup('jedan/swap-en'); + $urlAlias2HR = $handler->lookup('dva/swap-hr'); + $urlAlias2EN = $handler->lookup('dva/swap-en'); + + $this->assertEquals($urlAlias1HRExpected, $urlAlias1HR); + $this->assertEquals($urlAlias1ENExpected, $urlAlias1EN); + $this->assertEquals($urlAlias2HRExpected, $urlAlias2HR); + $this->assertEquals($urlAlias2ENExpected, $urlAlias2EN); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedMultipleLanguagesDifferentLanguagesSimple() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_multilang_diff_simple.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(316, 314, 317, 315); + + $this->assertEquals( + $countBeforeReusing + 2, + $this->countRows() + ); + + $urlAlias = $handler->lookup('jedan/swap-hr'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-hr'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-hr', + 'ger-DE' => 'swap-de', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('jedan/swap-de'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-de'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'ger-DE', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-hr', + 'ger-DE' => 'swap-de', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('jedan/swap-en'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-en'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'eng-GB', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'swap-en', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-hr'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-hr'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'swap-en', + 'cro-HR' => 'swap-hr', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-en'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-en'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'eng-GB', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'swap-en', + 'cro-HR' => 'swap-hr', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-de'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-de'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'ger-DE', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'ger-DE' => 'swap-de', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedMultipleLanguagesDifferentLanguages() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_multilang_diff.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(317, 315, 316, 314); + + $this->assertEquals( + $countBeforeReusing + 2, + $this->countRows() + ); + + $urlAlias = $handler->lookup('jedan/swap-this'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-this'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'ger-DE', + 'nor-NO', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-hr', + 'ger-DE' => 'swap-this', + 'nor-NO' => 'swap-this', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('jedan/swap-en'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-en'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'eng-GB', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'swap-en', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-hr'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-hr'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-hr', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-this'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-this'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'cro-HR', + 'ger-DE', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-this', + 'ger-DE' => 'swap-this', + 'eng-GB' => 'swap-en', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedMultipleLanguagesWithCompositeHistory() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_multilang_cleanup_composite.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(317, 315, 316, 314); + + $this->assertEquals( + $countBeforeReusing + 4, + $this->countRows() + ); + + $urlAlias = $handler->lookup('jedan/swap-this'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-this'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-this', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('jedan/swap-en'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-en'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'eng-GB', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'swap-en', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('jedan/swap-hr'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-hr'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-hr', + 'ger-DE' => 'swap-that', + 'nor-NO' => 'swap-that', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('jedan/swap-that'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-that'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'ger-DE', + 'nor-NO', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-hr', + 'ger-DE' => 'swap-that', + 'nor-NO' => 'swap-that', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-hr'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-hr'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-hr', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-that'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-that'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'ger-DE', + 'nor-NO', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'ger-DE' => 'swap-that', + 'nor-NO' => 'swap-that', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-this'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-this'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-this', + 'eng-GB' => 'swap-en', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-en'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-en'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'eng-GB', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-this', + 'eng-GB' => 'swap-en', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedWithReusingExternalHistory() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_reusing_external_history.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(318, 314, 319, 315); + + $this->assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + $urlAlias = $handler->lookup('jedan/swap-that'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-that'), + 'type' => UrlAlias::LOCATION, + 'destination' => 318, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-that', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-this'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-this'), + 'type' => UrlAlias::LOCATION, + 'destination' => 319, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-this', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-that'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-that'), + 'type' => UrlAlias::LOCATION, + 'destination' => 319, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-that', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('jedan/swap-this'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-this'), + 'type' => UrlAlias::LOCATION, + 'destination' => 318, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-this', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedWithReusingNopEntry() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_reusing_nop.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(316, 314, 317, 315); + + $this->assertEquals( + $countBeforeReusing + 1, + $this->countRows() + ); + + $urlAlias = $handler->lookup('jedan/swap-that'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-that'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-that', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-this'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-this'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-this', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => false, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('dva/swap-that'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '3-' . md5('swap-that'), + 'type' => UrlAlias::LOCATION, + 'destination' => 317, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-that', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + + $urlAlias = $handler->lookup('jedan/swap-this'); + $this->assertEquals( + new UrlAlias( + [ + 'id' => '2-' . md5('swap-this'), + 'type' => UrlAlias::LOCATION, + 'destination' => 316, + 'languageCodes' => [ + 'cro-HR', + ], + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'swap-this', + ], + ], + ], + 'alwaysAvailable' => false, + 'isHistory' => true, + 'isCustom' => false, + 'forward' => false, + ] + ), + $urlAlias + ); + } + + /** + * Test for the locationSwapped() method. + * + * @depends testLocationSwappedWithReusingNopEntry + * @group swap + */ + public function testLocationSwappedWithReusingNopEntryCustomAliasIsDestroyed() + { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_reusing_nop.php'); + + $handler->lookup('jedan/swap-that/search'); + $handler->locationSwapped(316, 314, 317, 315); + + try { + $handler->lookup('jedan/swap-that/search'); + $this->fail('Custom alias is not destroyed'); + } catch (NotFoundException $e) { + // Custom alias is destroyed by reusing NOP entry with existing autogenerated alias + // on the same level (that means link and ID are updated to the existing alias ID, + // so custom alias children entries are no longer properly linked (parent-link)) + } + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedUpdatesLocationPathIdentificationString() + { + $handler = $this->getHandler(); + $locationGateway = $this->getLocationGateway(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_path_identification_string.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(314, 2, 315, 2); + + $this->assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + $locationData = $locationGateway->getBasicNodeData(314); + self::assertEquals('dva', $locationData['path_identification_string']); + + $locationData = $locationGateway->getBasicNodeData(315); + self::assertEquals('jedan', $locationData['path_identification_string']); + } + + /** + * Test for the locationSwapped() method. + * + * @group swap + */ + public function testLocationSwappedMultipleLanguagesUpdatesLocationPathIdentificationString() + { + $handler = $this->getHandler(); + $locationGateway = $this->getLocationGateway(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlaliases_swap_multilang_path_identification_string.php'); + + $countBeforeReusing = $this->countRows(); + + $handler->locationSwapped(314, 2, 315, 2); + + $this->assertEquals( + $countBeforeReusing, + $this->countRows() + ); + + $locationData = $locationGateway->getBasicNodeData(314); + self::assertEquals('zwei', $locationData['path_identification_string']); + + $locationData = $locationGateway->getBasicNodeData(315); + self::assertEquals('jedan', $locationData['path_identification_string']); + } + + protected function countRows(): int + { + $connection = $this->getDatabaseConnection(); + $query = $connection->createQueryBuilder(); + $query + ->select($connection->getDatabasePlatform()->getCountExpression('*')) + ->from(UrlAliasGateway::TABLE); + + $statement = $query->execute(); + + return (int)$statement->fetchColumn(); + } + + /** @var \Ibexa\Core\Persistence\Legacy\Content\Location\Gateway */ + protected $locationGateway; + + /** @var \Ibexa\Core\Persistence\Legacy\Content\Language\Handler */ + protected $languageHandler; + + /** @var \Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator */ + protected $languageMaskGenerator; + + /** + * @param array $methods + * + * @return \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Handler|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getPartlyMockedHandler(array $methods) + { + return $this->getMockBuilder(Handler::class) + ->setConstructorArgs( + [ + $this->createMock(UrlAliasGateway::class), + $this->createMock(Mapper::class), + $this->createMock(LocationGateway::class), + $this->createMock(LanguageHandler::class), + $this->createMock(SlugConverter::class), + $this->createMock(Gateway::class), + $this->createMock(LanguageMaskGenerator::class), + $this->createMock(TransactionHandler::class), + ] + ) + ->setMethods($methods) + ->getMock(); + } + + /** + * @return \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Handler + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function getHandler(): Handler + { + $languageHandler = $this->getLanguageHandler(); + $languageMaskGenerator = $this->getLanguageMaskGenerator(); + $gateway = new DoctrineDatabase( + $this->getDatabaseConnection(), + $languageMaskGenerator + ); + $mapper = new Mapper($languageMaskGenerator); + $slugConverter = new SlugConverter($this->getProcessor()); + $connection = $this->getDatabaseConnection(); + $contentGateway = new ContentGateway( + $connection, + $this->getSharedGateway(), + new ContentGateway\QueryBuilder($connection), + $languageHandler, + $languageMaskGenerator + ); + + return new Handler( + $gateway, + $mapper, + $this->getLocationGateway(), + $languageHandler, + $slugConverter, + $contentGateway, + $languageMaskGenerator, + $this->createMock(TransactionHandler::class) + ); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + protected function getLanguageHandler(): LanguageHandler + { + if (!isset($this->languageHandler)) { + $this->languageHandler = new LanguageHandler( + new LanguageGateway( + $this->getDatabaseConnection() + ), + new LanguageMapper() + ); + } + + return $this->languageHandler; + } + + /** + * @return \Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator + */ + protected function getLanguageMaskGenerator() + { + if (!isset($this->languageMaskGenerator)) { + $this->languageMaskGenerator = new LanguageMaskGenerator( + $this->getLanguageHandler() + ); + } + + return $this->languageMaskGenerator; + } + + /** + * @return \Ibexa\Core\Persistence\Legacy\Content\Location\Gateway + */ + protected function getLocationGateway() + { + if (!isset($this->locationGateway)) { + $this->locationGateway = new DoctrineDatabaseLocation( + $this->getDatabaseConnection(), + $this->getLanguageMaskGenerator(), + $this->getTrashCriteriaConverterDependency(), + $this->getTrashSortClauseConverterDependency() + ); + } + + return $this->locationGateway; + } + + /** + * @return \Ibexa\Core\Persistence\TransformationProcessor + */ + public function getProcessor() + { + return new DefinitionBased( + new Parser(), + new PcreCompiler(new Utf8Converter()), + glob(__DIR__ . '/../../../TransformationProcessor/_fixtures/transformations/*.tr') + ); + } + + /** + * Data provider for tests of archiveUrlAliasesForDeletedTranslations. + * + * @see testArchiveUrlAliasesForDeletedTranslations for the description of parameters + * + * @return array + */ + public function providerForArchiveUrlAliasesForDeletedTranslations() + { + return [ + [2, ['eng-GB', 'pol-PL'], 'pol-PL'], + [3, ['eng-GB', 'pol-PL', 'nor-NO'], 'pol-PL'], + ]; + } + + /** + * @dataProvider providerForArchiveUrlAliasesForDeletedTranslations + * + * @param int $locationId + * @param string[] $expectedLanguages expected language codes before deleting + * @param string $removeLanguage language code to be deleted + */ + public function testArchiveUrlAliasesForDeletedTranslations( + $locationId, + array $expectedLanguages, + $removeLanguage + ) { + $handler = $this->getHandler(); + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/publish_multilingual.php'); + + // collect data persisted from fixtures + $urlAliases = $handler->listURLAliasesForLocation($locationId); + $collectedLanguages = []; + $collectedUrls = []; + foreach ($urlAliases as $urlAlias) { + // collect languages of all URL aliases + $collectedLanguages = array_merge($collectedLanguages, $urlAlias->languageCodes); + $isComposite = count($urlAlias->languageCodes) > 1; + foreach ($urlAlias->pathData as $pathData) { + // collect also actual unique URLs to be removed to check them after removal + if (!empty($pathData['translations'][$removeLanguage])) { + $url = $pathData['translations'][$removeLanguage]; + $collectedUrls[$url] = $isComposite; + } + } + } + // sanity check + self::assertEquals($expectedLanguages, $collectedLanguages); + + // remove language + $publishedLanguages = array_values(array_diff($collectedLanguages, [$removeLanguage])); + $handler->archiveUrlAliasesForDeletedTranslations($locationId, 1, $publishedLanguages); + + // check reloaded structures + $urlAliases = $handler->listURLAliasesForLocation($locationId); + foreach ($urlAliases as $urlAlias) { + self::assertNotContains($removeLanguage, $urlAlias->languageCodes); + foreach ($urlAlias->pathData as $pathData) { + self::assertNotContains($removeLanguage, $pathData['translations']); + foreach ($pathData['translations'] as $url) { + $lookupUrlAlias = $handler->lookup($url); + self::assertNotContains($removeLanguage, $lookupUrlAlias->languageCodes); + } + } + } + + // lookup removed URLs to check they're not found + foreach ($collectedUrls as $url => $isComposite) { + $urlAlias = $handler->lookup($url); + if ($isComposite) { + // check if alias no longer refers to removed Translation + self::assertNotContains($removeLanguage, $urlAlias->languageCodes); + foreach ($urlAlias->pathData as $pathData) { + self::assertNotContains($removeLanguage, $pathData['translations']); + } + } else { + // check if non composite alias for removed translation is historized + self::assertTrue($urlAlias->isHistory); + } + } + } +} + +class_alias(UrlAliasHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlAlias\UrlAliasHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/UrlAliasMapperTest.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasMapperTest.php similarity index 91% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/UrlAliasMapperTest.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasMapperTest.php index e7956d5421..2c76721889 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/UrlAliasMapperTest.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/UrlAliasMapperTest.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlAlias; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\UrlAlias; -use eZ\Publish\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Tests\Content\LanguageAwareTestCase; -use eZ\Publish\SPI\Persistence\Content\UrlAlias; +use Ibexa\Contracts\Core\Persistence\Content\UrlAlias; +use Ibexa\Core\Persistence\Legacy\Content\Language\MaskGenerator as LanguageMaskGenerator; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Mapper; +use Ibexa\Tests\Core\Persistence\Legacy\Content\LanguageAwareTestCase; /** - * Test case for UrlAliasMapper. + * @covers \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Mapper */ class UrlAliasMapperTest extends LanguageAwareTestCase { @@ -236,7 +236,6 @@ public function providerForTestExtractUrlAliasFromData() /** * Test for the extractUrlAliasFromData() method. * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Mapper::extractUrlAliasFromData * @dataProvider providerForTestExtractUrlAliasFromData */ public function testExtractUrlAliasFromData($index) @@ -255,7 +254,6 @@ public function testExtractUrlAliasFromData($index) /** * Test for the extractUrlAliasListFromData() method. * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Mapper::extractUrlAliasListFromData * @depends testExtractUrlAliasFromData */ public function testExtractUrlAliasListFromData() @@ -270,8 +268,6 @@ public function testExtractUrlAliasListFromData() /** * Test for the extractLanguageCodesFromData method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Mapper::extractLanguageCodesFromData */ public function testExtractLanguageCodesFromData() { @@ -284,7 +280,7 @@ public function testExtractLanguageCodesFromData() } /** - * @return \eZ\Publish\Core\Persistence\Legacy\Content\UrlAlias\Mapper + * @return \Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Mapper */ protected function getMapper() { @@ -294,3 +290,5 @@ protected function getMapper() return new Mapper($languageMaskGenerator); } } + +class_alias(UrlAliasMapperTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlAlias\UrlAliasMapperTest'); diff --git a/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/publish_base.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/publish_base.php new file mode 100644 index 0000000000..8326424445 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/publish_base.php @@ -0,0 +1,123 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ + 'action' => 'eznode:2', + 'action_type' => 'eznode', + 'alias_redirects' => '1', + 'id' => '1', + 'is_alias' => '0', + 'is_original' => '1', + 'lang_mask' => '3', + 'link' => '1', + 'parent' => '0', + 'text' => '', + 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', + ], + 1 => [ + 'action' => 'eznode:314', + 'action_type' => 'eznode', + 'alias_redirects' => '0', + 'id' => '2', + 'is_alias' => '0', + 'is_original' => '1', + 'lang_mask' => '3', + 'link' => '2', + 'parent' => '0', + 'text' => 'path314', + 'text_md5' => 'fdbbfa1e24e78ef56cb16ba4482c7771', + ], + 2 => [ + 'action' => 'eznode:315', + 'action_type' => 'eznode', + 'alias_redirects' => '0', + 'id' => '3', + 'is_alias' => '0', + 'is_original' => '1', + 'lang_mask' => '4', + 'link' => '3', + 'parent' => '2', + 'text' => 'path315', + 'text_md5' => 'afbe70de5f03a22e867723655a995279', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ + 'id' => '1', + ], + 1 => [ + 'id' => '2', + ], + 2 => [ + 'id' => '3', + ], + ], + 'ezcontent_language' => [ + 0 => [ + 'disabled' => 0, + 'id' => 2, + 'locale' => 'cro-HR', + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ + 'disabled' => 0, + 'id' => 4, + 'locale' => 'eng-GB', + 'name' => 'English (United Kingdom)', + ], + 2 => [ + 'disabled' => 0, + 'id' => 8, + 'locale' => 'ger-DE', + 'name' => 'German', + ], + 3 => [ + 'disabled' => 0, + 'id' => 16, + 'locale' => 'kli-KR', + 'name' => 'Klingon (Kronos)', + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ + 'node_id' => 1, + 'parent_node_id' => 1, + 'path_string' => '', + 'path_identification_string' => '', + 'remote_id' => '', + ], + 1 => [ + 'node_id' => 2, + 'parent_node_id' => 1, + 'path_string' => '', + 'path_identification_string' => '', + 'remote_id' => '', + ], + 2 => [ + 'node_id' => 314, + 'parent_node_id' => 2, + 'path_string' => '', + 'path_identification_string' => 'path314', + 'remote_id' => '', + ], + 3 => [ + 'node_id' => 315, + 'parent_node_id' => 314, + 'path_string' => '', + 'path_identification_string' => 'path314/path315', + 'remote_id' => '', + ], + 4 => [ + 'node_id' => 316, + 'parent_node_id' => 315, + 'path_string' => '', + 'path_identification_string' => 'path314/path315/path316', + 'remote_id' => '', + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/publish_multilingual.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/publish_multilingual.php similarity index 94% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/publish_multilingual.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/publish_multilingual.php index 370e03634b..c6e6db8583 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/publish_multilingual.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/publish_multilingual.php @@ -1,5 +1,9 @@ <?php +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 'ezurlalias_ml' => [ [ diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_cleanup_history.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_cleanup_history.php similarity index 79% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_cleanup_history.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_cleanup_history.php index e76d416f24..5d807b96c8 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_cleanup_history.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_cleanup_history.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '0', 'text' => 'tri', 'text_md5' => 'd2cfe69af2d64330670e08efb2c86df7', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,34 +70,34 @@ 'parent' => '0', 'text' => 'cetiri', 'text_md5' => '538dca05643d220317ad233cd7be7a0a', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', 'name' => 'Croatian (Hrvatski)', - ), - 1 => array( + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', 'name' => 'English (United Kingdom)', - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - ), -); + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_cleanup_nop.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_cleanup_nop.php similarity index 76% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_cleanup_nop.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_cleanup_nop.php index db9232fe95..b38efa5505 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_cleanup_nop.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_cleanup_nop.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 4 => array( + ], + 4 => [ 'action' => 'nop:', 'action_type' => 'nop', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '0', 'text' => 'nop-element', 'text_md5' => 'de55c2fff721217cc4cb67b58dc35f85', - ), - 5 => array( + ], + 5 => [ 'action' => 'module:content/search', 'action_type' => 'module', 'alias_redirects' => '0', @@ -66,34 +70,34 @@ 'parent' => '3', 'text' => 'search', 'text_md5' => '06a943c59f33a34bb5924aaf72cd2995', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'English (United Kingdom)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - ), -); + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_cleanup_reusing.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_cleanup_reusing.php similarity index 75% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_cleanup_reusing.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_cleanup_reusing.php index 1cb81f8bcb..bc26e6cd37 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_cleanup_reusing.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_cleanup_reusing.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,28 +44,28 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', 'name' => 'Croatian (Hrvatski)', - ), - 1 => array( + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', 'name' => 'English (United Kingdom)', - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - ), -); + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_copy.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_copy.php similarity index 80% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_copy.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_copy.php index 5946c7d592..5d8b649f30 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_copy.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_copy.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:3', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'move-here', 'text_md5' => '8c09d75fa9c06724b51b2f837107a5ca', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:4', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'move-this', 'text_md5' => '93dc83851ede7c440fe00c29e7487d1b', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:4', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -53,8 +57,8 @@ 'parent' => '0', 'text' => 'move-this-history', 'text_md5' => '869f933f715cc635b70923256fa04033', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:5', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -66,8 +70,8 @@ 'parent' => '3', 'text' => 'sub1', 'text_md5' => '1b52eb8ef2c1875cfdf3ffbe9e3c05da', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:6', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -79,8 +83,8 @@ 'parent' => '5', 'text' => 'sub2-history', 'text_md5' => 'be302a8ff37091d2b3bc31f2b8f95207', - ), - 7 => array( + ], + 7 => [ 'action' => 'eznode:6', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -92,8 +96,8 @@ 'parent' => '5', 'text' => 'sub2', 'text_md5' => '5fbef65269a99bddc2106251dd89b1dc', - ), - 8 => array( + ], + 8 => [ 'action' => 'eznode:400', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -105,105 +109,105 @@ 'parent' => '2', 'text' => 'move-this', 'text_md5' => '93dc83851ede7c440fe00c29e7487d1b', - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - 5 => array( + ], + 5 => [ 'id' => '6', - ), - 6 => array( + ], + 6 => [ 'id' => '7', - ), - 7 => array( + ], + 7 => [ 'id' => '8', - ), - 8 => array( + ], + 8 => [ 'id' => '9', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + 'name' => 'English (United Kingdom)', + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 1, 'parent_node_id' => 1, 'path_string' => '/1/', 'remote_id' => '', - ), - 1 => array( + ], + 1 => [ 'node_id' => 2, 'parent_node_id' => 1, 'path_string' => '/1/2/', 'remote_id' => '', - ), - 2 => array( + ], + 2 => [ 'node_id' => 3, 'parent_node_id' => 2, 'path_string' => '/1/2/3/', 'remote_id' => '', - ), - 3 => array( + ], + 3 => [ 'node_id' => 4, 'parent_node_id' => 2, 'path_string' => '/1/2/4/', 'remote_id' => '', - ), - 4 => array( + ], + 4 => [ 'node_id' => 5, 'parent_node_id' => 4, 'path_string' => '/1/2/4/5/', 'remote_id' => '', - ), - 5 => array( + ], + 5 => [ 'node_id' => 6, 'parent_node_id' => 5, 'path_string' => '/1/2/4/5/6/', 'remote_id' => '', - ), - 6 => array( + ], + 6 => [ 'node_id' => 400, 'parent_node_id' => 3, 'path_string' => '/1/2/3/400/', 'remote_id' => '', - ), - 7 => array( + ], + 7 => [ 'node_id' => 500, 'parent_node_id' => 400, 'path_string' => '/1/2/3/400/500/', 'remote_id' => '', - ), - 8 => array( + ], + 8 => [ 'node_id' => 600, 'parent_node_id' => 500, 'path_string' => '/1/2/3/400/500/600/', 'remote_id' => '', - ), - ), -); + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location.php similarity index 79% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location.php index e60acd70ad..a32d5028ac 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -40,8 +44,8 @@ 'parent' => '2', 'text' => 'two', 'text_md5' => 'b8a9f715dbb64fd5c56e7783c6820a61', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '2', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '3', 'text' => 'drei', 'text_md5' => '1d8d2fd0a99802b89eb356a86e029d25', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -79,8 +83,8 @@ 'parent' => '3', 'text' => 'three', 'text_md5' => '35d6d33467aae9a2e3dccb4b6b027878', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -92,8 +96,8 @@ 'parent' => '3', 'text' => 'tri', 'text_md5' => 'd2cfe69af2d64330670e08efb2c86df7', - ), - 7 => array( + ], + 7 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -105,49 +109,49 @@ 'parent' => '3', 'text' => 'tri-history', 'text_md5' => '5f46413bb0ba5998caef84ab1ea590e1', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - 2 => array( + 'name' => 'English (United Kingdom)', + ], + 2 => [ 'disabled' => 0, 'id' => 8, 'locale' => 'ger-DE', - 'name' => 'German' - ), - 3 => array( + 'name' => 'German', + ], + 3 => [ 'disabled' => 0, 'id' => 16, 'locale' => 'kli-KR', - 'name' => 'Klingon (Kronos)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'Klingon (Kronos)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - ), -); + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location_custom.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location_custom.php similarity index 82% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location_custom.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location_custom.php index 4b7eb508f9..a9485f4fbb 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location_custom.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location_custom.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'autogenerated-hello', 'text_md5' => '2eb35041e168cb62fe790b7555a0e90d', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'autogenerated-everyone', 'text_md5' => 'ab383b12f6b706215a252d06ada52b20', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '0', 'text' => 'autogenerated-goodbye', 'text_md5' => 'da37edc08656a56c3e65a0e3d1c07457', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '0', 'text' => 'autogenerated-ha-ha-ha', 'text_md5' => '830ee0c223eca7f011dfa2e25318bb21', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -79,8 +83,8 @@ 'parent' => '0', 'text' => 'hello', 'text_md5' => '5d41402abc4b2a76b9719d911017c592', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -92,8 +96,8 @@ 'parent' => '6', 'text' => 'everyone', 'text_md5' => 'ed881bac6397ede33c0a285c9f50bb83', - ), - 7 => array( + ], + 7 => [ 'action' => 'nop:', 'action_type' => 'nop', 'alias_redirects' => '1', @@ -105,8 +109,8 @@ 'parent' => '6', 'text' => 'and', 'text_md5' => 'be5d5d37542d75f93a87094459f76678', - ), - 8 => array( + ], + 8 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -118,8 +122,8 @@ 'parent' => '8', 'text' => 'goodbye', 'text_md5' => '69faab6268350295550de7d587bc323d', - ), - 9 => array( + ], + 9 => [ 'action' => 'nop:', 'action_type' => 'nop', 'alias_redirects' => '1', @@ -131,8 +135,8 @@ 'parent' => '0', 'text' => 'well', 'text_md5' => 'f9323f5b51fc23e30c10623bd38de6ff', - ), - 10 => array( + ], + 10 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -144,8 +148,8 @@ 'parent' => '10', 'text' => 'ha-ha-ha', 'text_md5' => '17a197f4bbe127c368b889a67effd1b3', - ), - 11 => array( + ], + 11 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -157,58 +161,58 @@ 'parent' => '2', 'text' => 'everybody', 'text_md5' => '88150d7d17390010ba6222de68bfafb5', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'English (United Kingdom)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - 5 => array( + ], + 5 => [ 'id' => '6', - ), - 6 => array( + ], + 6 => [ 'id' => '7', - ), - 7 => array( + ], + 7 => [ 'id' => '8', - ), - 8 => array( + ], + 8 => [ 'id' => '9', - ), - 9 => array( + ], + 9 => [ 'id' => '10', - ), - 10 => array( + ], + 10 => [ 'id' => '11', - ), - 11 => array( + ], + 11 => [ 'id' => '12', - ), - ), -); + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location_delete.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location_delete.php similarity index 85% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location_delete.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location_delete.php index 8aba3047a2..5cde90278c 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location_delete.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location_delete.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:3', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'moved-parent', 'text_md5' => 'b3c65c78b56bcec4b9884dd694088379', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:4', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -40,8 +44,8 @@ 'parent' => '2', 'text' => 'moved', 'text_md5' => '11dfd868d93bc2b0e4ce0bee5756f8b1', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:5', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -53,8 +57,8 @@ 'parent' => '0', 'text' => 'moved-original-parent', 'text_md5' => '6b01ae8cd40d07ea8be54bbddc9a42f8', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:4', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -66,8 +70,8 @@ 'parent' => '4', 'text' => 'moved-history', 'text_md5' => '7e6a74afb9efb5db7d18fdbd2edecaec', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:6', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -79,8 +83,8 @@ 'parent' => '3', 'text' => 'sub', 'text_md5' => '8a68dc3e925eacf92633be230722a140', - ), - 7 => array( + ], + 7 => [ 'action' => 'eznode:7', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -92,8 +96,8 @@ 'parent' => '3', 'text' => 'sub-history', 'text_md5' => 'e9f37abc40f517ebd795873f2683e690', - ), - 8 => array( + ], + 8 => [ 'action' => 'eznode:4', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -105,8 +109,8 @@ 'parent' => '0', 'text' => 'moved-previous-history', 'text_md5' => '520472b9a76d8980eefc68bf519aa446', - ), - 9 => array( + ], + 9 => [ 'action' => 'eznode:8', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -118,8 +122,8 @@ 'parent' => '4', 'text' => 'sub2', 'text_md5' => '5fbef65269a99bddc2106251dd89b1dc', - ), - 10 => array( + ], + 10 => [ 'action' => 'nop:', 'action_type' => 'nop', 'alias_redirects' => '1', @@ -131,8 +135,8 @@ 'parent' => '0', 'text' => 'custom-below', 'text_md5' => '4fedee449bc4573f786d5839bf39ef31', - ), - 11 => array( + ], + 11 => [ 'action' => 'eznode:5', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -144,8 +148,8 @@ 'parent' => '10', 'text' => 'moved-original-parent-custom', 'text_md5' => '1098a946541fe683a50fb569b13a2fd9', - ), - 12 => array( + ], + 12 => [ 'action' => 'eznode:5', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -157,8 +161,8 @@ 'parent' => '0', 'text' => 'moved-original-parent-history', 'text_md5' => '1098a946541fe683a50fb569b13a2fd9', - ), - 13 => array( + ], + 13 => [ 'action' => 'eznode:9', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -170,8 +174,8 @@ 'parent' => '0', 'text' => 'to-delete-folder', 'text_md5' => '0e6c631eff791a398821ce8ab54ccdc1', - ), - 14 => array( + ], + 14 => [ 'action' => 'eznode:10', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -183,8 +187,8 @@ 'parent' => '13', 'text' => 'article-to-move-relative-alias', 'text_md5' => 'cad0af09404cf6c7863415319ae7accb', - ), - 15 => array( + ], + 15 => [ 'action' => 'eznode:10', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -196,8 +200,8 @@ 'parent' => '0', 'text' => 'article-to-move', 'text_md5' => 'bad5f760788aeeb88006892230f26e64', - ), - 16 => array( + ], + 16 => [ 'action' => 'eznode:10', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -209,58 +213,58 @@ 'parent' => '13', 'text' => 'article-to-move', 'text_md5' => 'bad5f760788aeeb88006892230f26e64', - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - 5 => array( + ], + 5 => [ 'id' => '6', - ), - 6 => array( + ], + 6 => [ 'id' => '7', - ), - 7 => array( + ], + 7 => [ 'id' => '8', - ), - 8 => array( + ], + 8 => [ 'id' => '9', - ), - 9 => array( + ], + 9 => [ 'id' => '10', - ), - 10 => array( + ], + 10 => [ 'id' => '11', - ), - 11 => array( + ], + 11 => [ 'id' => '12', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - ), -); + 'name' => 'English (United Kingdom)', + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location_iri.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location_iri.php similarity index 76% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location_iri.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location_iri.php index df1f7f31ce..15eaf1f9d8 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location_iri.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location_iri.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'ŒÄ', 'text_md5' => 'c2bc273ec708a8e4ce1a5c2cab974a6d', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '2', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '3', 'text' => 'three', 'text_md5' => '35d6d33467aae9a2e3dccb4b6b027878', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,34 +70,34 @@ 'parent' => '3', 'text' => 'tri', 'text_md5' => 'd2cfe69af2d64330670e08efb2c86df7', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'English (United Kingdom)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - ), -); + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location_multilang.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location_multilang.php similarity index 76% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location_multilang.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location_multilang.php index 701283272d..b21f18dc04 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_location_multilang.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_location_multilang.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '2', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '3', 'text' => 'three', 'text_md5' => '35d6d33467aae9a2e3dccb4b6b027878', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,34 +70,34 @@ 'parent' => '3', 'text' => 'tri', 'text_md5' => 'd2cfe69af2d64330670e08efb2c86df7', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'English (United Kingdom)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - ), -); + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_move.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_move.php similarity index 78% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_move.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_move.php index 33ba95eb3c..caa6ed6fa0 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_move.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_move.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:3', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'move-here', 'text_md5' => '8c09d75fa9c06724b51b2f837107a5ca', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:4', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'move-this', 'text_md5' => '93dc83851ede7c440fe00c29e7487d1b', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:4', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -53,8 +57,8 @@ 'parent' => '0', 'text' => 'move-this-history', 'text_md5' => '869f933f715cc635b70923256fa04033', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:5', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -66,8 +70,8 @@ 'parent' => '3', 'text' => 'sub1', 'text_md5' => '1b52eb8ef2c1875cfdf3ffbe9e3c05da', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:6', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -79,8 +83,8 @@ 'parent' => '5', 'text' => 'sub2-history', 'text_md5' => 'be302a8ff37091d2b3bc31f2b8f95207', - ), - 7 => array( + ], + 7 => [ 'action' => 'eznode:6', 'action_type' => 'eznode', 'alias_redirects' => '0', @@ -92,46 +96,46 @@ 'parent' => '5', 'text' => 'sub2', 'text_md5' => '5fbef65269a99bddc2106251dd89b1dc', - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - 5 => array( + ], + 5 => [ 'id' => '6', - ), - 6 => array( + ], + 6 => [ 'id' => '7', - ), - 7 => array( + ], + 7 => [ 'id' => '8', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - ), -); + 'name' => 'English (United Kingdom)', + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_resource.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_resource.php similarity index 77% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_resource.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_resource.php index 962407fce6..5a8a666b93 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_resource.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_resource.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'module:ezinfo/isalive', 'action_type' => 'module', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'is-alive', 'text_md5' => 'd003895fa282a14c8ec3eddf23ca4ca2', - ), - 2 => array( + ], + 2 => [ 'action' => 'nop:', 'action_type' => 'nop', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '2', 'text' => 'then', 'text_md5' => '0e5243d9965540f62aac19a985f3f33e', - ), - 3 => array( + ], + 3 => [ 'action' => 'module:content/search', 'action_type' => 'module', 'alias_redirects' => '0', @@ -53,8 +57,8 @@ 'parent' => '3', 'text' => 'search', 'text_md5' => '06a943c59f33a34bb5924aaf72cd2995', - ), - 4 => array( + ], + 4 => [ 'action' => 'nop:', 'action_type' => 'nop', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '0', 'text' => 'nop-element', 'text_md5' => 'de55c2fff721217cc4cb67b58dc35f85', - ), - 5 => array( + ], + 5 => [ 'action' => 'module:content/search', 'action_type' => 'module', 'alias_redirects' => '0', @@ -79,60 +83,60 @@ 'parent' => '5', 'text' => 'search', 'text_md5' => '06a943c59f33a34bb5924aaf72cd2995', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 1, 'parent_node_id' => 1, 'path_string' => '', 'remote_id' => '', - ), - 1 => array( + ], + 1 => [ 'node_id' => 2, 'parent_node_id' => 1, 'path_string' => '', 'remote_id' => '', - ), - 2 => array( + ], + 2 => [ 'node_id' => 314, 'parent_node_id' => 2, 'path_string' => '', 'remote_id' => '', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'English (United Kingdom)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - 5 => array( + ], + 5 => [ 'id' => '6', - ), - ), -); + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_reusing.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_reusing.php similarity index 78% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_reusing.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_reusing.php index 9a8c9124d7..4e57d5de42 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_reusing.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_reusing.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'nop:', 'action_type' => 'nop', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'nop-element', 'text_md5' => 'de55c2fff721217cc4cb67b58dc35f85', - ), - 2 => array( + ], + 2 => [ 'action' => 'module:content/search', 'action_type' => 'module', 'alias_redirects' => '0', @@ -40,8 +44,8 @@ 'parent' => '2', 'text' => 'search', 'text_md5' => '06a943c59f33a34bb5924aaf72cd2995', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '0', 'text' => 'history-hello', 'text_md5' => 'da94285592c46d4396d3ca6904a4aa8f', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '0', 'text' => 'autogenerated-hello', 'text_md5' => '2eb35041e168cb62fe790b7555a0e90d', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -79,66 +83,66 @@ 'parent' => '0', 'text' => 'custom-hello', 'text_md5' => 'c0254f50ddbeea89a6523d79c330cd57', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 1, 'parent_node_id' => 1, 'path_string' => '', 'remote_id' => '', - ), - 1 => array( + ], + 1 => [ 'node_id' => 2, 'parent_node_id' => 1, 'path_string' => '', 'remote_id' => '', - ), - 2 => array( + ], + 2 => [ 'node_id' => 314, 'parent_node_id' => 2, 'path_string' => '', 'remote_id' => '', - ), - 3 => array( + ], + 3 => [ 'node_id' => 315, 'parent_node_id' => 2, 'path_string' => '', 'remote_id' => '', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'English (United Kingdom)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - 5 => array( + ], + 5 => [ 'id' => '6', - ), - ), -); + ], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_cleanup_composite.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_cleanup_composite.php similarity index 82% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_cleanup_composite.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_cleanup_composite.php index 5f6bc43a46..f163a68873 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_cleanup_composite.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_cleanup_composite.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '2', 'text' => 'swap-this', 'text_md5' => '21940df6bebbfc9501b3b512640dffe5', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '2', 'text' => 'swap-en', 'text_md5' => '5a1cafd1fc29c227c11c751d79b0c155', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -79,8 +83,8 @@ 'parent' => '3', 'text' => 'swap-hr', 'text_md5' => 'b0a33436ea51b6cc92f20b7d5be52cf6', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -92,53 +96,53 @@ 'parent' => '3', 'text' => 'swap-that', 'text_md5' => 'b8d555a5436774b6d3a035a4437ea37c', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - 2 => array( + 'name' => 'English (United Kingdom)', + ], + 2 => [ 'disabled' => 0, 'id' => 8, 'locale' => 'ger-DE', - 'name' => 'German' - ), - 3 => array( + 'name' => 'German', + ], + 3 => [ 'disabled' => 0, 'id' => 16, 'locale' => 'nor-NO', - 'name' => 'Norwegian (Bokmal)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'Norwegian (Bokmal)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 314, 'main_node_id' => 314, 'parent_node_id' => 2, @@ -146,8 +150,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 1, - ), - 1 => array( + ], + 1 => [ 'node_id' => 315, 'main_node_id' => 315, 'parent_node_id' => 2, @@ -155,8 +159,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 2, - ), - 2 => array( + ], + 2 => [ 'node_id' => 316, 'main_node_id' => 316, 'parent_node_id' => 314, @@ -164,8 +168,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 3, - ), - 3 => array( + ], + 3 => [ 'node_id' => 317, 'main_node_id' => 317, 'parent_node_id' => 315, @@ -173,20 +177,20 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 4, - ), - ), - 'ezcontentobject' => array( - 0 => array( + ], + ], + 'ezcontentobject' => [ + 0 => [ 'id' => 3, 'initial_language_id' => 2, 'current_version' => 1, - ), - 1 => array( + ], + 1 => [ 'id' => 4, 'initial_language_id' => 2, 'current_version' => 1, - ), - ), + ], + ], 'ezcontentobject_name' => [ 0 => [ 'contentobject_id' => 3, @@ -219,4 +223,4 @@ 'content_translation' => 'eng-GB', ], ], -); +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_diff.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_diff.php similarity index 83% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_diff.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_diff.php index e979937299..d465fe35e2 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_diff.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_diff.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '2', 'text' => 'swap-this', 'text_md5' => '21940df6bebbfc9501b3b512640dffe5', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '2', 'text' => 'swap-en', 'text_md5' => '5a1cafd1fc29c227c11c751d79b0c155', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -79,8 +83,8 @@ 'parent' => '3', 'text' => 'swap-hr', 'text_md5' => 'b0a33436ea51b6cc92f20b7d5be52cf6', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -92,53 +96,53 @@ 'parent' => '3', 'text' => 'swap-this', 'text_md5' => '21940df6bebbfc9501b3b512640dffe5', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - 2 => array( + 'name' => 'English (United Kingdom)', + ], + 2 => [ 'disabled' => 0, 'id' => 8, 'locale' => 'ger-DE', - 'name' => 'German' - ), - 3 => array( + 'name' => 'German', + ], + 3 => [ 'disabled' => 0, 'id' => 16, 'locale' => 'nor-NO', - 'name' => 'Norwegian (Bokmal)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'Norwegian (Bokmal)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 314, 'main_node_id' => 314, 'parent_node_id' => 2, @@ -146,8 +150,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 1, - ), - 1 => array( + ], + 1 => [ 'node_id' => 315, 'main_node_id' => 315, 'parent_node_id' => 2, @@ -155,8 +159,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 2, - ), - 2 => array( + ], + 2 => [ 'node_id' => 316, 'main_node_id' => 316, 'parent_node_id' => 314, @@ -164,8 +168,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 3, - ), - 3 => array( + ], + 3 => [ 'node_id' => 317, 'main_node_id' => 317, 'parent_node_id' => 315, @@ -173,20 +177,20 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 4, - ), - ), - 'ezcontentobject' => array( - 0 => array( + ], + ], + 'ezcontentobject' => [ + 0 => [ 'id' => 3, 'initial_language_id' => 2, 'current_version' => 1, - ), - 1 => array( + ], + 1 => [ 'id' => 4, 'initial_language_id' => 2, 'current_version' => 1, - ), - ), + ], + ], 'ezcontentobject_name' => [ 0 => [ 'contentobject_id' => 3, @@ -225,4 +229,4 @@ 'content_translation' => 'eng-GB', ], ], -); +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_diff_simple.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_diff_simple.php similarity index 82% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_diff_simple.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_diff_simple.php index 45a62b4086..fc98ba8f8b 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_diff_simple.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_diff_simple.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '2', 'text' => 'swap-hr', 'text_md5' => 'b0a33436ea51b6cc92f20b7d5be52cf6', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '2', 'text' => 'swap-en', 'text_md5' => '5a1cafd1fc29c227c11c751d79b0c155', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -79,8 +83,8 @@ 'parent' => '3', 'text' => 'swap-hr', 'text_md5' => 'b0a33436ea51b6cc92f20b7d5be52cf6', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -92,47 +96,47 @@ 'parent' => '3', 'text' => 'swap-de', 'text_md5' => '66f7d87bf0f1463e6c607388e65eedb9', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - 2 => array( + 'name' => 'English (United Kingdom)', + ], + 2 => [ 'disabled' => 0, 'id' => 8, 'locale' => 'ger-DE', - 'name' => 'German' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'German', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 314, 'main_node_id' => 314, 'parent_node_id' => 2, @@ -140,8 +144,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 1, - ), - 1 => array( + ], + 1 => [ 'node_id' => 315, 'main_node_id' => 315, 'parent_node_id' => 2, @@ -149,8 +153,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 2, - ), - 2 => array( + ], + 2 => [ 'node_id' => 316, 'main_node_id' => 316, 'parent_node_id' => 314, @@ -158,8 +162,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 3, - ), - 3 => array( + ], + 3 => [ 'node_id' => 317, 'main_node_id' => 317, 'parent_node_id' => 315, @@ -167,20 +171,20 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 4, - ), - ), - 'ezcontentobject' => array( - 0 => array( + ], + ], + 'ezcontentobject' => [ + 0 => [ 'id' => 3, 'initial_language_id' => 2, 'current_version' => 1, - ), - 1 => array( + ], + 1 => [ 'id' => 4, 'initial_language_id' => 2, 'current_version' => 1, - ), - ), + ], + ], 'ezcontentobject_name' => [ 0 => [ 'contentobject_id' => 3, @@ -207,4 +211,4 @@ 'content_translation' => 'eng-GB', ], ], -); +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_path_identification_string.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_path_identification_string.php similarity index 83% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_path_identification_string.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_path_identification_string.php index 866a9405a8..2034a1743c 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_path_identification_string.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_path_identification_string.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'one', 'text_md5' => 'f97c5d29941bfb1b2fdab0874906ab82', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,41 +70,41 @@ 'parent' => '0', 'text' => 'zwei', 'text_md5' => '15d55d085e6eda9586293acad7c0a4f4', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', 'name' => 'Croatian (Hrvatski)', - ), - 1 => array( + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', 'name' => 'English (United Kingdom)', - ), - 2 => array( + ], + 2 => [ 'disabled' => 0, 'id' => 8, 'locale' => 'ger-DE', 'name' => 'German', - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 2, 'main_node_id' => 2, 'parent_node_id' => 1, @@ -108,8 +112,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 1, - ), - 1 => array( + ], + 1 => [ 'node_id' => 314, 'main_node_id' => 314, 'parent_node_id' => 2, @@ -117,8 +121,8 @@ 'path_identification_string' => 'jedan', 'remote_id' => '', 'contentobject_id' => 2, - ), - 2 => array( + ], + 2 => [ 'node_id' => 315, 'main_node_id' => 315, 'parent_node_id' => 2, @@ -126,20 +130,20 @@ 'path_identification_string' => 'zwei', 'remote_id' => '', 'contentobject_id' => 3, - ), - ), - 'ezcontentobject' => array( - 0 => array( + ], + ], + 'ezcontentobject' => [ + 0 => [ 'id' => 2, 'initial_language_id' => 2, 'current_version' => 1, - ), - 1 => array( + ], + 1 => [ 'id' => 3, 'initial_language_id' => 8, 'current_version' => 1, - ), - ), + ], + ], 'ezcontentobject_name' => [ 0 => [ 'contentobject_id' => 2, @@ -166,4 +170,4 @@ 'content_translation' => 'eng-GB', ], ], -); +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_simple.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_simple.php similarity index 83% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_simple.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_simple.php index bb5891154d..6492f1e66c 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_simple.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_multilang_simple.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '2', 'text' => 'swap-hr', 'text_md5' => 'b0a33436ea51b6cc92f20b7d5be52cf6', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '2', 'text' => 'swap-en', 'text_md5' => '5a1cafd1fc29c227c11c751d79b0c155', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -79,8 +83,8 @@ 'parent' => '3', 'text' => 'swap-hr', 'text_md5' => 'b0a33436ea51b6cc92f20b7d5be52cf6', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -92,41 +96,41 @@ 'parent' => '3', 'text' => 'swap-en', 'text_md5' => '5a1cafd1fc29c227c11c751d79b0c155', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - 1 => array( + 'name' => 'Croatian (Hrvatski)', + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', - 'name' => 'English (United Kingdom)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'English (United Kingdom)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 314, 'main_node_id' => 314, 'parent_node_id' => 2, @@ -134,8 +138,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 1, - ), - 1 => array( + ], + 1 => [ 'node_id' => 315, 'main_node_id' => 315, 'parent_node_id' => 2, @@ -143,8 +147,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 2, - ), - 2 => array( + ], + 2 => [ 'node_id' => 316, 'main_node_id' => 316, 'parent_node_id' => 314, @@ -152,8 +156,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 3, - ), - 3 => array( + ], + 3 => [ 'node_id' => 317, 'main_node_id' => 317, 'parent_node_id' => 315, @@ -161,20 +165,20 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 4, - ), - ), - 'ezcontentobject' => array( - 0 => array( + ], + ], + 'ezcontentobject' => [ + 0 => [ 'id' => 3, 'initial_language_id' => 2, 'current_version' => 1, - ), - 1 => array( + ], + 1 => [ 'id' => 4, 'initial_language_id' => 2, 'current_version' => 1, - ), - ), + ], + ], 'ezcontentobject_name' => [ 0 => [ 'contentobject_id' => 3, @@ -201,4 +205,4 @@ 'content_translation' => 'eng-GB', ], ], -); +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_path_identification_string.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_path_identification_string.php similarity index 80% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_path_identification_string.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_path_identification_string.php index f3f86ea830..488d2907ae 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_path_identification_string.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_path_identification_string.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,41 +44,41 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', 'name' => 'Croatian (Hrvatski)', - ), - 1 => array( + ], + 1 => [ 'disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', 'name' => 'English (United Kingdom)', - ), - 2 => array( + ], + 2 => [ 'disabled' => 0, 'id' => 8, 'locale' => 'ger-DE', 'name' => 'German', - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 2, 'main_node_id' => 2, 'parent_node_id' => 1, @@ -82,8 +86,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 1, - ), - 1 => array( + ], + 1 => [ 'node_id' => 314, 'main_node_id' => 314, 'parent_node_id' => 2, @@ -91,8 +95,8 @@ 'path_identification_string' => 'jedan', 'remote_id' => '', 'contentobject_id' => 2, - ), - 2 => array( + ], + 2 => [ 'node_id' => 315, 'main_node_id' => 315, 'parent_node_id' => 2, @@ -100,20 +104,20 @@ 'path_identification_string' => 'dva', 'remote_id' => '', 'contentobject_id' => 3, - ), - ), - 'ezcontentobject' => array( - 0 => array( + ], + ], + 'ezcontentobject' => [ + 0 => [ 'id' => 2, 'initial_language_id' => 2, 'current_version' => 1, - ), - 1 => array( + ], + 1 => [ 'id' => 3, 'initial_language_id' => 2, 'current_version' => 1, - ), - ), + ], + ], 'ezcontentobject_name' => [ 0 => [ 'contentobject_id' => 2, @@ -128,4 +132,4 @@ 'content_translation' => 'cro-HR', ], ], -); +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_reusing_external_history.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_reusing_external_history.php similarity index 83% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_reusing_external_history.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_reusing_external_history.php index 319bc894d6..7b006ca6f9 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_reusing_external_history.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_reusing_external_history.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '2', 'text' => 'swap-that', 'text_md5' => 'b8d555a5436774b6d3a035a4437ea37c', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '3', 'text' => 'swap-this', 'text_md5' => '21940df6bebbfc9501b3b512640dffe5', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:318', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -79,8 +83,8 @@ 'parent' => '2', 'text' => 'swap-this', 'text_md5' => '21940df6bebbfc9501b3b512640dffe5', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:319', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -92,8 +96,8 @@ 'parent' => '3', 'text' => 'swap-that', 'text_md5' => 'b8d555a5436774b6d3a035a4437ea37c', - ), - 7 => array( + ], + 7 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -105,8 +109,8 @@ 'parent' => '2', 'text' => 'swap-not-that', 'text_md5' => 'd28a35f7595771f6a0be76f0b890715a', - ), - 8 => array( + ], + 8 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -118,47 +122,47 @@ 'parent' => '3', 'text' => 'swap-not-this', 'text_md5' => '2b78a26576e43f93e266cb502ff1dece', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'Croatian (Hrvatski)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - 5 => array( + ], + 5 => [ 'id' => '6', - ), - 6 => array( + ], + 6 => [ 'id' => '7', - ), - 7 => array( + ], + 7 => [ 'id' => '8', - ), - 8 => array( + ], + 8 => [ 'id' => '9', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 314, 'main_node_id' => 314, 'parent_node_id' => 2, @@ -166,8 +170,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 1, - ), - 1 => array( + ], + 1 => [ 'node_id' => 315, 'main_node_id' => 315, 'parent_node_id' => 314, @@ -175,8 +179,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 2, - ), - 2 => array( + ], + 2 => [ 'node_id' => 318, 'main_node_id' => 318, 'parent_node_id' => 2, @@ -184,8 +188,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 3, - ), - 3 => array( + ], + 3 => [ 'node_id' => 319, 'main_node_id' => 319, 'parent_node_id' => 315, @@ -193,20 +197,20 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 4, - ), - ), - 'ezcontentobject' => array( - 0 => array( + ], + ], + 'ezcontentobject' => [ + 0 => [ 'id' => 3, 'initial_language_id' => 2, 'current_version' => 1, - ), - 1 => array( + ], + 1 => [ 'id' => 4, 'initial_language_id' => 2, 'current_version' => 1, - ), - ), + ], + ], 'ezcontentobject_name' => [ 0 => [ 'contentobject_id' => 3, @@ -221,4 +225,4 @@ 'content_translation' => 'cro-HR', ], ], -); +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_reusing_nop.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_reusing_nop.php similarity index 82% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_reusing_nop.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_reusing_nop.php index 50f186e756..b73b2e0185 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_reusing_nop.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_reusing_nop.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '2', 'text' => 'swap-this', 'text_md5' => '21940df6bebbfc9501b3b512640dffe5', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '3', 'text' => 'swap-that', 'text_md5' => 'b8d555a5436774b6d3a035a4437ea37c', - ), - 5 => array( + ], + 5 => [ 'action' => 'nop:', 'action_type' => 'nop', 'alias_redirects' => '1', @@ -79,8 +83,8 @@ 'parent' => '2', 'text' => 'swap-that', 'text_md5' => 'b8d555a5436774b6d3a035a4437ea37c', - ), - 6 => array( + ], + 6 => [ 'action' => 'module:content/search', 'action_type' => 'module', 'alias_redirects' => '1', @@ -92,41 +96,41 @@ 'parent' => '6', 'text' => 'search', 'text_md5' => '06a943c59f33a34bb5924aaf72cd2995', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'Croatian (Hrvatski)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - 5 => array( + ], + 5 => [ 'id' => '6', - ), - 6 => array( + ], + 6 => [ 'id' => '7', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 314, 'main_node_id' => 314, 'parent_node_id' => 2, @@ -134,8 +138,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 1, - ), - 1 => array( + ], + 1 => [ 'node_id' => 315, 'main_node_id' => 315, 'parent_node_id' => 2, @@ -143,8 +147,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 2, - ), - 2 => array( + ], + 2 => [ 'node_id' => 316, 'main_node_id' => 316, 'parent_node_id' => 314, @@ -152,8 +156,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 3, - ), - 3 => array( + ], + 3 => [ 'node_id' => 317, 'main_node_id' => 317, 'parent_node_id' => 315, @@ -161,20 +165,20 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 4, - ), - ), - 'ezcontentobject' => array( - 0 => array( + ], + ], + 'ezcontentobject' => [ + 0 => [ 'id' => 3, 'initial_language_id' => 2, 'current_version' => 1, - ), - 1 => array( + ], + 1 => [ 'id' => 4, 'initial_language_id' => 2, 'current_version' => 1, - ), - ), + ], + ], 'ezcontentobject_name' => [ 0 => [ 'contentobject_id' => 3, @@ -189,4 +193,4 @@ 'content_translation' => 'cro-HR', ], ], -); +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_same_name.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_same_name.php similarity index 94% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_same_name.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_same_name.php index c8800b5773..3de9a6da9c 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_same_name.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_same_name.php @@ -1,5 +1,9 @@ <?php +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 'ezurlalias_ml' => [ 0 => [ diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_same_name_multilang.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_same_name_multilang.php similarity index 96% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_same_name_multilang.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_same_name_multilang.php index 3e7ee93403..5e6862c3d8 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_same_name_multilang.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_same_name_multilang.php @@ -1,5 +1,9 @@ <?php +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 'ezurlalias_ml' => [ 0 => [ diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_simple.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_simple.php similarity index 78% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_simple.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_simple.php index 946e80663b..a8b350cf20 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_simple.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_simple.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,29 +44,29 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'Croatian (Hrvatski)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 2, 'main_node_id' => 2, 'parent_node_id' => 1, @@ -70,8 +74,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 1, - ), - 1 => array( + ], + 1 => [ 'node_id' => 314, 'main_node_id' => 314, 'parent_node_id' => 2, @@ -79,8 +83,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 2, - ), - 2 => array( + ], + 2 => [ 'node_id' => 315, 'main_node_id' => 315, 'parent_node_id' => 2, @@ -88,20 +92,20 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 3, - ), - ), - 'ezcontentobject' => array( - 0 => array( + ], + ], + 'ezcontentobject' => [ + 0 => [ 'id' => 2, 'initial_language_id' => 2, 'current_version' => 1, - ), - 1 => array( + ], + 1 => [ 'id' => 3, 'initial_language_id' => 2, 'current_version' => 1, - ), - ), + ], + ], 'ezcontentobject_name' => [ 0 => [ 'contentobject_id' => 2, @@ -116,4 +120,4 @@ 'content_translation' => 'cro-HR', ], ], -); +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_simple_history.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_simple_history.php similarity index 80% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_simple_history.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_simple_history.php index 76a3c95e0e..49e74d0807 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_simple_history.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_siblings_simple_history.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '0', 'text' => 'jedan-new', 'text_md5' => '0d1c74fb1d039ba5026253965a293325', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,35 +70,35 @@ 'parent' => '0', 'text' => 'dva-new', 'text_md5' => '8b9bbf68d41279d4db7c0d6c4be1ad25', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'Croatian (Hrvatski)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 2, 'main_node_id' => 2, 'parent_node_id' => 1, @@ -102,8 +106,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 1, - ), - 1 => array( + ], + 1 => [ 'node_id' => 314, 'main_node_id' => 314, 'parent_node_id' => 2, @@ -111,8 +115,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 2, - ), - 2 => array( + ], + 2 => [ 'node_id' => 315, 'main_node_id' => 315, 'parent_node_id' => 2, @@ -120,20 +124,20 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 3, - ), - ), - 'ezcontentobject' => array( - 0 => array( + ], + ], + 'ezcontentobject' => [ + 0 => [ 'id' => 2, 'initial_language_id' => 2, 'current_version' => 1, - ), - 1 => array( + ], + 1 => [ 'id' => 3, 'initial_language_id' => 2, 'current_version' => 1, - ), - ), + ], + ], 'ezcontentobject_name' => [ 0 => [ 'contentobject_id' => 2, @@ -148,4 +152,4 @@ 'content_translation' => 'cro-HR', ], ], -); +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_simple.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_simple.php similarity index 81% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_simple.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_simple.php index 2672d81184..f1aaab3b69 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_simple.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_simple.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '2', 'text' => 'swap', 'text_md5' => 'f0a1dfdc675b0a14a64099f7ac1cee83', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,35 +70,35 @@ 'parent' => '3', 'text' => 'swap', 'text_md5' => 'f0a1dfdc675b0a14a64099f7ac1cee83', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'Croatian (Hrvatski)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 314, 'main_node_id' => 314, 'parent_node_id' => 2, @@ -102,8 +106,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 1, - ), - 1 => array( + ], + 1 => [ 'node_id' => 315, 'main_node_id' => 315, 'parent_node_id' => 2, @@ -111,8 +115,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 2, - ), - 2 => array( + ], + 2 => [ 'node_id' => 316, 'main_node_id' => 316, 'parent_node_id' => 314, @@ -120,8 +124,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 3, - ), - 3 => array( + ], + 3 => [ 'node_id' => 317, 'main_node_id' => 317, 'parent_node_id' => 315, @@ -129,20 +133,20 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 4, - ), - ), - 'ezcontentobject' => array( - 0 => array( + ], + ], + 'ezcontentobject' => [ + 0 => [ 'id' => 3, 'initial_language_id' => 2, 'current_version' => 1, - ), - 1 => array( + ], + 1 => [ 'id' => 4, 'initial_language_id' => 2, 'current_version' => 1, - ), - ), + ], + ], 'ezcontentobject_name' => [ 0 => [ 'contentobject_id' => 3, @@ -157,4 +161,4 @@ 'content_translation' => 'cro-HR', ], ], -); +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_simple_conflict.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_simple_conflict.php similarity index 84% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_simple_conflict.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_simple_conflict.php index 3b2c60d253..6079ac3ce0 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_simple_conflict.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_simple_conflict.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '2', 'text' => 'swap', 'text_md5' => 'f0a1dfdc675b0a14a64099f7ac1cee83', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '3', 'text' => 'swap', 'text_md5' => 'f0a1dfdc675b0a14a64099f7ac1cee83', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -79,8 +83,8 @@ 'parent' => '2', 'text' => 'swap-new-1', 'text_md5' => 'b665ad011db07a6886adfe13913a68b9', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -92,8 +96,8 @@ 'parent' => '3', 'text' => 'swap-new-2', 'text_md5' => '57ab1094dcc1d94fb1513000613175b4', - ), - 7 => array( + ], + 7 => [ 'action' => 'eznode:318', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -105,8 +109,8 @@ 'parent' => '2', 'text' => 'swap-new-2', 'text_md5' => '57ab1094dcc1d94fb1513000613175b4', - ), - 8 => array( + ], + 8 => [ 'action' => 'eznode:319', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -118,41 +122,41 @@ 'parent' => '3', 'text' => 'swap-new-1', 'text_md5' => 'b665ad011db07a6886adfe13913a68b9', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'Croatian (Hrvatski)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - 5 => array( + ], + 5 => [ 'id' => '6', - ), - 6 => array( + ], + 6 => [ 'id' => '7', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 314, 'main_node_id' => 314, 'parent_node_id' => 2, @@ -160,8 +164,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 1, - ), - 1 => array( + ], + 1 => [ 'node_id' => 315, 'main_node_id' => 315, 'parent_node_id' => 2, @@ -169,8 +173,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 2, - ), - 2 => array( + ], + 2 => [ 'node_id' => 316, 'main_node_id' => 316, 'parent_node_id' => 314, @@ -178,8 +182,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 3, - ), - 3 => array( + ], + 3 => [ 'node_id' => 317, 'main_node_id' => 317, 'parent_node_id' => 315, @@ -187,20 +191,20 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 4, - ), - ), - 'ezcontentobject' => array( - 0 => array( + ], + ], + 'ezcontentobject' => [ + 0 => [ 'id' => 3, 'initial_language_id' => 2, 'current_version' => 1, - ), - 1 => array( + ], + 1 => [ 'id' => 4, 'initial_language_id' => 2, 'current_version' => 1, - ), - ), + ], + ], 'ezcontentobject_name' => [ 0 => [ 'contentobject_id' => 3, @@ -215,4 +219,4 @@ 'content_translation' => 'cro-HR', ], ], -); +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_simple_history.php b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_simple_history.php similarity index 82% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_simple_history.php rename to tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_simple_history.php index 165b529c20..194ec2aaca 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlAlias/_fixtures/urlaliases_swap_simple_history.php +++ b/tests/lib/Persistence/Legacy/Content/UrlAlias/_fixtures/urlaliases_swap_simple_history.php @@ -1,8 +1,12 @@ <?php -return array( - 'ezurlalias_ml' => array( - 0 => array( +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlalias_ml' => [ + 0 => [ 'action' => 'eznode:2', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -14,8 +18,8 @@ 'parent' => '0', 'text' => '', 'text_md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ), - 1 => array( + ], + 1 => [ 'action' => 'eznode:314', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -27,8 +31,8 @@ 'parent' => '0', 'text' => 'jedan', 'text_md5' => '6896260129051a949051c3847c34466f', - ), - 2 => array( + ], + 2 => [ 'action' => 'eznode:315', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -40,8 +44,8 @@ 'parent' => '0', 'text' => 'dva', 'text_md5' => 'c67ed9a09ab136fae610b6a087d82e21', - ), - 3 => array( + ], + 3 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -53,8 +57,8 @@ 'parent' => '2', 'text' => 'swap', 'text_md5' => 'f0a1dfdc675b0a14a64099f7ac1cee83', - ), - 4 => array( + ], + 4 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -66,8 +70,8 @@ 'parent' => '3', 'text' => 'swap', 'text_md5' => 'f0a1dfdc675b0a14a64099f7ac1cee83', - ), - 5 => array( + ], + 5 => [ 'action' => 'eznode:316', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -79,8 +83,8 @@ 'parent' => '2', 'text' => 'swap-new', 'text_md5' => 'edaf4b1fb4bdaf25868db636ab1d4f9e', - ), - 6 => array( + ], + 6 => [ 'action' => 'eznode:317', 'action_type' => 'eznode', 'alias_redirects' => '1', @@ -92,41 +96,41 @@ 'parent' => '3', 'text' => 'swap-new', 'text_md5' => 'edaf4b1fb4bdaf25868db636ab1d4f9e', - ), - ), - 'ezcontent_language' => array( - 0 => array( + ], + ], + 'ezcontent_language' => [ + 0 => [ 'disabled' => 0, 'id' => 2, 'locale' => 'cro-HR', - 'name' => 'Croatian (Hrvatski)' - ), - ), - 'ezurlalias_ml_incr' => array( - 0 => array( + 'name' => 'Croatian (Hrvatski)', + ], + ], + 'ezurlalias_ml_incr' => [ + 0 => [ 'id' => '1', - ), - 1 => array( + ], + 1 => [ 'id' => '2', - ), - 2 => array( + ], + 2 => [ 'id' => '3', - ), - 3 => array( + ], + 3 => [ 'id' => '4', - ), - 4 => array( + ], + 4 => [ 'id' => '5', - ), - 5 => array( + ], + 5 => [ 'id' => '6', - ), - 6 => array( + ], + 6 => [ 'id' => '7', - ), - ), - 'ezcontentobject_tree' => array( - 0 => array( + ], + ], + 'ezcontentobject_tree' => [ + 0 => [ 'node_id' => 314, 'main_node_id' => 314, 'parent_node_id' => 2, @@ -134,8 +138,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 1, - ), - 1 => array( + ], + 1 => [ 'node_id' => 315, 'main_node_id' => 315, 'parent_node_id' => 2, @@ -143,8 +147,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 2, - ), - 2 => array( + ], + 2 => [ 'node_id' => 316, 'main_node_id' => 316, 'parent_node_id' => 314, @@ -152,8 +156,8 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 3, - ), - 3 => array( + ], + 3 => [ 'node_id' => 317, 'main_node_id' => 317, 'parent_node_id' => 315, @@ -161,20 +165,20 @@ 'path_identification_string' => '', 'remote_id' => '', 'contentobject_id' => 4, - ), - ), - 'ezcontentobject' => array( - 0 => array( + ], + ], + 'ezcontentobject' => [ + 0 => [ 'id' => 3, 'initial_language_id' => 2, 'current_version' => 1, - ), - 1 => array( + ], + 1 => [ 'id' => 4, 'initial_language_id' => 2, 'current_version' => 1, - ), - ), + ], + ], 'ezcontentobject_name' => [ 0 => [ 'contentobject_id' => 3, @@ -189,4 +193,4 @@ 'content_translation' => 'cro-HR', ], ], -); +]; diff --git a/tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..45a2bc20c4 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,181 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\UrlWildcard\Gateway; + +use Ibexa\Contracts\Core\Persistence\Content\UrlWildcard; +use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase; +use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriteriaConverter; +use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriterionHandler\MatchAll; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase + */ +class DoctrineDatabaseTest extends TestCase +{ + /** + * Database gateway to test. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase + */ + protected $gateway; + + protected $fixtureData = [ + 0 => [ + 'id' => '1', + 'source_url' => 'developer/*', + 'destination_url' => 'dev/{1}', + 'type' => '2', + ], + 1 => [ + 'id' => '2', + 'source_url' => 'repository/*', + 'destination_url' => 'repo/{1}', + 'type' => '2', + ], + 2 => [ + 'id' => '3', + 'source_url' => 'information/*', + 'destination_url' => 'info/{1}', + 'type' => '2', + ], + ]; + + /** + * Test for the loadUrlWildcardData() method. + */ + public function testLoadUrlWildcardData() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlwildcards.php'); + $gateway = $this->getGateway(); + + $row = $gateway->loadUrlWildcardData(1); + + self::assertEquals( + $this->fixtureData[0], + $row + ); + } + + /** + * Test for the loadUrlWildcardsData() method. + */ + public function testLoadUrlWildcardsData() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlwildcards.php'); + $gateway = $this->getGateway(); + + $rows = $gateway->loadUrlWildcardsData(); + + self::assertEquals( + $this->fixtureData, + $rows + ); + } + + /** + * Test for the loadUrlWildcardsData() method. + */ + public function testLoadUrlWildcardsDataWithOffset() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlwildcards.php'); + $gateway = $this->getGateway(); + + $row = $gateway->loadUrlWildcardsData(1); + + self::assertEquals( + [ + 0 => $this->fixtureData[1], + 1 => $this->fixtureData[2], + ], + $row + ); + } + + /** + * Test for the loadUrlWildcardsData() method. + */ + public function testLoadUrlWildcardsDataWithOffsetAndLimit() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlwildcards.php'); + $gateway = $this->getGateway(); + + $row = $gateway->loadUrlWildcardsData(1, 1); + + self::assertEquals( + [ + 0 => $this->fixtureData[1], + ], + $row + ); + } + + /** + * Test for the insertUrlWildcard() method. + * + * @depends testLoadUrlWildcardData + */ + public function testInsertUrlWildcard() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlwildcards.php'); + $gateway = $this->getGateway(); + + $id = $gateway->insertUrlWildcard( + new UrlWildcard( + [ + 'sourceUrl' => '/contact-information/*', + 'destinationUrl' => '/contact/{1}', + 'forward' => true, + ] + ) + ); + + self::assertEquals( + [ + 'id' => $id, + 'source_url' => 'contact-information/*', + 'destination_url' => 'contact/{1}', + 'type' => '1', + ], + $gateway->loadUrlWildcardData($id) + ); + } + + /** + * Test for the deleteUrlWildcard() method. + * + * @depends testLoadUrlWildcardData + */ + public function testDeleteUrlWildcard() + { + $this->insertDatabaseFixture(__DIR__ . '/_fixtures/urlwildcards.php'); + $gateway = $this->getGateway(); + + $gateway->deleteUrlWildcard(1); + + self::assertEmpty($gateway->loadUrlWildcardData(1)); + } + + /** + * Return the DoctrineDatabase gateway to test. + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function getGateway(): DoctrineDatabase + { + if (!isset($this->gateway)) { + $criteriaConverter = new CriteriaConverter([new MatchAll()]); + $this->gateway = new DoctrineDatabase($this->getDatabaseConnection(), $criteriaConverter); + } + + return $this->gateway; + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlWildcard\Gateway\DoctrineDatabaseTest'); diff --git a/tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/_fixtures/urlwildcards.php b/tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/_fixtures/urlwildcards.php new file mode 100644 index 0000000000..65f513ea82 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/UrlWildcard/Gateway/_fixtures/urlwildcards.php @@ -0,0 +1,28 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezurlwildcard' => [ + 0 => [ + 'id' => '1', + 'source_url' => 'developer/*', + 'destination_url' => 'dev/{1}', + 'type' => '2', + ], + 1 => [ + 'id' => '2', + 'source_url' => 'repository/*', + 'destination_url' => 'repo/{1}', + 'type' => '2', + ], + 2 => [ + 'id' => '3', + 'source_url' => 'information/*', + 'destination_url' => 'info/{1}', + 'type' => '2', + ], + ], +]; diff --git a/tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardHandlerTest.php b/tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardHandlerTest.php new file mode 100644 index 0000000000..d4fb4f436a --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardHandlerTest.php @@ -0,0 +1,250 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\UrlWildcard; + +use Ibexa\Contracts\Core\Persistence\Content\UrlWildcard; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase; +use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Handler; +use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Mapper; +use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriteriaConverter; +use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Query\CriterionHandler\MatchAll; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Handler + */ +class UrlWildcardHandlerTest extends TestCase +{ + public function testLoad() + { + $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); + $handler = $this->getHandler(); + + $urlWildcard = $handler->load(1); + + self::assertEquals( + new UrlWildcard( + [ + 'id' => 1, + 'sourceUrl' => '/developer/*', + 'destinationUrl' => '/dev/{1}', + 'forward' => false, + ] + ), + $urlWildcard + ); + } + + /** + * Test for the load() method. + */ + public function testLoadThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); + $handler = $this->getHandler(); + + $handler->load(100); + } + + /** + * Test for the create() method. + * + * + * @depends testLoad + */ + public function testCreate() + { + $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); + $handler = $this->getHandler(); + + $urlWildcard = $handler->create( + 'amber', + 'pattern', + true + ); + + self::assertEquals( + new UrlWildcard( + [ + 'id' => 4, + 'sourceUrl' => '/amber', + 'destinationUrl' => '/pattern', + 'forward' => true, + ] + ), + $urlWildcard + ); + + self::assertEquals( + $urlWildcard, + $handler->load(4) + ); + } + + /** + * @depends testLoad + */ + public function testUpdate(): void + { + $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); + $handler = $this->getHandler(); + + $urlWildcard = $handler->load(1); + + $urlWildcardUpdated = $handler->update( + $urlWildcard->id, + 'amber-updated', + 'pattern-updated', + true + ); + + self::assertEquals( + new UrlWildcard( + [ + 'id' => 1, + 'sourceUrl' => '/amber-updated', + 'destinationUrl' => '/pattern-updated', + 'forward' => true, + ] + ), + $urlWildcardUpdated + ); + + self::assertEquals( + $urlWildcardUpdated, + $handler->load(1) + ); + } + + /** + * Test for the remove() method. + * + * @depends testLoad + */ + public function testRemove() + { + $this->expectException(NotFoundException::class); + + $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); + $handler = $this->getHandler(); + + $handler->remove(1); + $handler->load(1); + } + + /** + * Test for the loadAll() method. + */ + public function testLoadAll() + { + $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); + $handler = $this->getHandler(); + + $urlWildcards = $handler->loadAll(); + + self::assertEquals( + [ + new UrlWildcard($this->fixtureData[0]), + new UrlWildcard($this->fixtureData[1]), + new UrlWildcard($this->fixtureData[2]), + ], + $urlWildcards + ); + } + + /** + * Test for the loadAll() method. + */ + public function testLoadAllWithOffset() + { + $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); + $handler = $this->getHandler(); + + $urlWildcards = $handler->loadAll(2); + + self::assertEquals( + [ + new UrlWildcard($this->fixtureData[2]), + ], + $urlWildcards + ); + } + + /** + * Test for the loadAll() method. + */ + public function testLoadAllWithOffsetAndLimit() + { + $this->insertDatabaseFixture(__DIR__ . '/Gateway/_fixtures/urlwildcards.php'); + $handler = $this->getHandler(); + + $urlWildcards = $handler->loadAll(1, 1); + + self::assertEquals( + [ + new UrlWildcard($this->fixtureData[1]), + ], + $urlWildcards + ); + } + + protected $fixtureData = [ + [ + 'id' => 1, + 'sourceUrl' => '/developer/*', + 'destinationUrl' => '/dev/{1}', + 'forward' => false, + ], + [ + 'id' => 2, + 'sourceUrl' => '/repository/*', + 'destinationUrl' => '/repo/{1}', + 'forward' => false, + ], + [ + 'id' => 3, + 'sourceUrl' => '/information/*', + 'destinationUrl' => '/info/{1}', + 'forward' => false, + ], + ]; + + /** @var \Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Gateway\DoctrineDatabase */ + protected $gateway; + + /** @var \Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Mapper */ + protected $mapper; + + /** @var \Ibexa\Contracts\Core\Persistence\Content\UrlWildcard\Handler */ + protected $urlWildcardHandler; + + /** + * @throws \Doctrine\DBAL\DBALException + */ + protected function getHandler(): UrlWildcard\Handler + { + if (!isset($this->urlWildcardHandler)) { + $criteriaConverter = new CriteriaConverter([new MatchAll()]); + $this->gateway = new DoctrineDatabase($this->getDatabaseConnection(), $criteriaConverter); + $this->mapper = new Mapper(); + + $this->urlWildcardHandler = new Handler( + $this->gateway, + $this->mapper + ); + } + + return $this->urlWildcardHandler; + } +} + +class_alias(UrlWildcardHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlWildcard\UrlWildcardHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlWildcard/UrlWildcardMapperTest.php b/tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardMapperTest.php similarity index 81% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlWildcard/UrlWildcardMapperTest.php rename to tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardMapperTest.php index 3754209188..8df9df5265 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/UrlWildcard/UrlWildcardMapperTest.php +++ b/tests/lib/Persistence/Legacy/Content/UrlWildcard/UrlWildcardMapperTest.php @@ -4,21 +4,19 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlWildcard; +namespace Ibexa\Tests\Core\Persistence\Legacy\Content\UrlWildcard; -use eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Mapper; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard; +use Ibexa\Contracts\Core\Persistence\Content\UrlWildcard; +use Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Mapper; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; /** - * Test case for UrlWildcard Mapper. + * @covers \Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Mapper */ class UrlWildcardMapperTest extends TestCase { /** * Test for the createUrlWildcard() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Mapper::createUrlWildcard */ public function testCreateUrlWildcard() { @@ -45,8 +43,6 @@ public function testCreateUrlWildcard() /** * Test for the extractUrlWildcardFromRow() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Mapper::extractUrlWildcardFromRow */ public function testExtractUrlWildcardFromRow() { @@ -75,8 +71,6 @@ public function testExtractUrlWildcardFromRow() /** * Test for the extractUrlWildcardFromRow() method. - * - * @covers \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Mapper::extractUrlWildcardFromRow */ public function testExtractUrlWildcardsFromRows() { @@ -122,10 +116,12 @@ public function testExtractUrlWildcardsFromRows() } /** - * @return \eZ\Publish\Core\Persistence\Legacy\Content\UrlWildcard\Mapper + * @return \Ibexa\Core\Persistence\Legacy\Content\UrlWildcard\Mapper */ protected function getMapper() { return new Mapper(); } } + +class_alias(UrlWildcardMapperTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Content\UrlWildcard\UrlWildcardMapperTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/contentobjects.php b/tests/lib/Persistence/Legacy/Content/_fixtures/contentobjects.php similarity index 95% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/contentobjects.php rename to tests/lib/Persistence/Legacy/Content/_fixtures/contentobjects.php index 46b565592b..2f8a7ec864 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/contentobjects.php +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/contentobjects.php @@ -1,5 +1,9 @@ <?php +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 'ezcontentobject_attribute' => [ 0 => [ @@ -308,7 +312,7 @@ 'contentobject_id' => '42', 'data_float' => '0', 'data_int' => '0', - 'data_text' => 'Anonymous Users', + 'data_text' => 'Anonymous users', 'data_type_string' => 'ezstring', 'id' => '100', 'language_code' => 'eng-US', @@ -758,8 +762,8 @@ 'contentobject_id' => '54', 'data_float' => '0', 'data_int' => '0', - 'data_text' => 'author=eZ Systems -copyright=eZ Systems + 'data_text' => 'author=Ibexa +copyright=Ibexa description=Content Management System keywords=cms, publish, e-commerce, content management, development framework', 'data_type_string' => 'ezinisetting', @@ -808,7 +812,7 @@ 'contentobject_id' => '54', 'data_float' => '0', 'data_int' => '0', - 'data_text' => 'kn@ez.no', + 'data_text' => 'kn@ibexa.co', 'data_type_string' => 'ezinisetting', 'id' => '175', 'language_code' => 'eng-US', @@ -5055,7 +5059,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '564', @@ -5179,7 +5183,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '576', @@ -5272,13 +5276,13 @@ 'contentobject_id' => '108', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'How to use eZ Publish', + 'data_text' => 'How to use Ibexa', 'data_type_string' => 'ezstring', 'id' => '586', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'how to use ez publish', + 'sort_key_string' => 'how to use ibexa', 'version' => '1', ], 358 => [ @@ -5303,7 +5307,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '588', @@ -5335,7 +5339,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="How-to-use-eZ-Publish.jpg" suffix="jpg" basename="How-to-use-eZ-Publish" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/592-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/592-1-eng-US/How-to-use-eZ-Publish.jpg" original_filename="affa2705.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154185"><original attribute_id="592" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="How-to-use-Ibexa.jpg" suffix="jpg" basename="How-to-use-Ibexa" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/592-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/592-1-eng-US/How-to-use-Ibexa.jpg" original_filename="affa2705.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154185"><original attribute_id="592" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '592', @@ -5412,7 +5416,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics1/600-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics1/600-1-eng-US/graphics1.jpg" original_filename="c6a49b95.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="600" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics1/600-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics1/600-1-eng-US/graphics1.jpg" original_filename="c6a49b95.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="600" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '600', @@ -5459,7 +5463,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics12/604-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics12/604-1-eng-US/graphics1.jpg" original_filename="afc64b77.jpg" mime_type="image/jpeg" width="540" height="357" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="604" attribute_version="1" attribute_language="eng-US"/><information Height="357" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics12/604-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics12/604-1-eng-US/graphics1.jpg" original_filename="afc64b77.jpg" mime_type="image/jpeg" width="540" height="357" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="604" attribute_version="1" attribute_language="eng-US"/><information Height="357" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '604', @@ -5506,7 +5510,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics13/608-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics13/608-1-eng-US/graphics1.jpg" original_filename="ccb0f9f7.jpg" mime_type="image/jpeg" width="540" height="419" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="608" attribute_version="1" attribute_language="eng-US"/><information Height="419" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics13/608-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics13/608-1-eng-US/graphics1.jpg" original_filename="ccb0f9f7.jpg" mime_type="image/jpeg" width="540" height="419" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="608" attribute_version="1" attribute_language="eng-US"/><information Height="419" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '608', @@ -5553,7 +5557,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics14/612-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics14/612-1-eng-US/graphics1.jpg" original_filename="be2fb4a2.jpg" mime_type="image/jpeg" width="540" height="404" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="612" attribute_version="1" attribute_language="eng-US"/><information Height="404" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics14/612-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics14/612-1-eng-US/graphics1.jpg" original_filename="be2fb4a2.jpg" mime_type="image/jpeg" width="540" height="404" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="612" attribute_version="1" attribute_language="eng-US"/><information Height="404" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '612', @@ -5600,7 +5604,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics15/616-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics15/616-1-eng-US/graphics1.jpg" original_filename="2db91653.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="616" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics15/616-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics15/616-1-eng-US/graphics1.jpg" original_filename="2db91653.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="616" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '616', @@ -5647,7 +5651,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics16/620-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics16/620-1-eng-US/graphics1.jpg" original_filename="b14081cb.jpg" mime_type="image/jpeg" width="540" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="620" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics16/620-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics16/620-1-eng-US/graphics1.jpg" original_filename="b14081cb.jpg" mime_type="image/jpeg" width="540" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="620" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '620', @@ -5694,7 +5698,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics17/624-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics17/624-1-eng-US/graphics1.jpg" original_filename="dd1815c4.jpg" mime_type="image/jpeg" width="540" height="425" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="624" attribute_version="1" attribute_language="eng-US"/><information Height="425" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics17/624-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics17/624-1-eng-US/graphics1.jpg" original_filename="dd1815c4.jpg" mime_type="image/jpeg" width="540" height="425" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="624" attribute_version="1" attribute_language="eng-US"/><information Height="425" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '624', @@ -5741,7 +5745,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics18/628-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics18/628-1-eng-US/graphics1.jpg" original_filename="485af257.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="628" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics18/628-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics18/628-1-eng-US/graphics1.jpg" original_filename="485af257.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="628" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '628', @@ -5788,7 +5792,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics19/632-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics19/632-1-eng-US/graphics1.jpg" original_filename="1a13bc86.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="632" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics19/632-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics19/632-1-eng-US/graphics1.jpg" original_filename="1a13bc86.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="632" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '632', @@ -5819,13 +5823,13 @@ 'contentobject_id' => '118', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'How to manage eZ Publish', + 'data_text' => 'How to manage Ibexa', 'data_type_string' => 'ezstring', 'id' => '634', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'how to manage ez publish', + 'sort_key_string' => 'how to manage ibexa', 'version' => '1', ], 394 => [ @@ -5850,7 +5854,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '636', @@ -5882,7 +5886,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="How-to-manage-eZ-Publish.jpg" suffix="jpg" basename="How-to-manage-eZ-Publish" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/640-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/640-1-eng-US/How-to-manage-eZ-Publish.jpg" original_filename="c6fabb5a.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="640" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="How-to-manage-Ibexa.jpg" suffix="jpg" basename="How-to-manage-Ibexa" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/640-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/640-1-eng-US/How-to-manage-Ibexa.jpg" original_filename="c6fabb5a.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="640" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '640', @@ -5959,7 +5963,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics1/648-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics1/648-1-eng-US/graphics1.jpg" original_filename="3268defa.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="648" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics1/648-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics1/648-1-eng-US/graphics1.jpg" original_filename="3268defa.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="648" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '648', @@ -6006,7 +6010,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics12/652-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics12/652-1-eng-US/graphics1.jpg" original_filename="06b28423.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="652" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics12/652-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics12/652-1-eng-US/graphics1.jpg" original_filename="06b28423.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="652" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '652', @@ -6053,7 +6057,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics13/656-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics13/656-1-eng-US/graphics1.jpg" original_filename="fb93c150.jpg" mime_type="image/jpeg" width="540" height="414" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="656" attribute_version="1" attribute_language="eng-US"/><information Height="414" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics13/656-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics13/656-1-eng-US/graphics1.jpg" original_filename="fb93c150.jpg" mime_type="image/jpeg" width="540" height="414" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="656" attribute_version="1" attribute_language="eng-US"/><information Height="414" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '656', @@ -6100,7 +6104,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics14/660-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics14/660-1-eng-US/graphics1.jpg" original_filename="e472d575.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="660" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics14/660-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics14/660-1-eng-US/graphics1.jpg" original_filename="e472d575.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="660" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '660', @@ -6147,7 +6151,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics15/664-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics15/664-1-eng-US/graphics1.png" original_filename="724f9667.png" mime_type="image/png" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="664" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics15/664-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics15/664-1-eng-US/graphics1.png" original_filename="724f9667.png" mime_type="image/png" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="664" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '664', @@ -6194,7 +6198,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics16/668-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics16/668-1-eng-US/graphics1.jpg" original_filename="13338db0.jpg" mime_type="image/jpeg" width="540" height="405" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="668" attribute_version="1" attribute_language="eng-US"/><information Height="405" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics16/668-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics16/668-1-eng-US/graphics1.jpg" original_filename="13338db0.jpg" mime_type="image/jpeg" width="540" height="405" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="668" attribute_version="1" attribute_language="eng-US"/><information Height="405" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '668', @@ -6241,7 +6245,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics17/672-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics17/672-1-eng-US/graphics1.jpg" original_filename="00bc8122.jpg" mime_type="image/jpeg" width="500" height="423" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="672" attribute_version="1" attribute_language="eng-US"/><information Height="423" Width="500" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics17/672-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics17/672-1-eng-US/graphics1.jpg" original_filename="00bc8122.jpg" mime_type="image/jpeg" width="500" height="423" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="672" attribute_version="1" attribute_language="eng-US"/><information Height="423" Width="500" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '672', @@ -6288,7 +6292,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics18/676-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics18/676-1-eng-US/graphics1.jpg" original_filename="d265bd27.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="676" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics18/676-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics18/676-1-eng-US/graphics1.jpg" original_filename="d265bd27.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="676" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '676', @@ -6319,13 +6323,13 @@ 'contentobject_id' => '127', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'How to develop with eZ Publish', + 'data_text' => 'How to develop with Ibexa', 'data_type_string' => 'ezstring', 'id' => '678', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'how to develop with ez publish', + 'sort_key_string' => 'how to develop with ibexa', 'version' => '1', ], 427 => [ @@ -6350,7 +6354,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '680', @@ -6382,7 +6386,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="How-to-develop-with-eZ-Publish.png" suffix="png" basename="How-to-develop-with-eZ-Publish" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ez-publish/684-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ez-publish/684-1-eng-US/How-to-develop-with-eZ-Publish.png" original_filename="db3eaa06.png" mime_type="image/png" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="684" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="How-to-develop-with-Ibexa.png" suffix="png" basename="How-to-develop-with-Ibexa" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ibexa-dxp/684-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ibexa-dxp/684-1-eng-US/How-to-develop-with-Ibexa.png" original_filename="db3eaa06.png" mime_type="image/png" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="684" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '684', @@ -6459,7 +6463,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ez-publish/graphics1/692-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ez-publish/graphics1/692-1-eng-US/graphics1.jpg" original_filename="c9dbc90f.jpg" mime_type="image/jpeg" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154190"><original attribute_id="692" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="500" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ibexa-dxp/graphics1/692-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ibexa-dxp/graphics1/692-1-eng-US/graphics1.jpg" original_filename="c9dbc90f.jpg" mime_type="image/jpeg" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154190"><original attribute_id="692" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="500" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '692', @@ -6506,7 +6510,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ez-publish/graphics12/696-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ez-publish/graphics12/696-1-eng-US/graphics1.png" original_filename="7e906b13.png" mime_type="image/png" width="540" height="411" alternative_text="" alias_key="1293033771" timestamp="1311154190"><original attribute_id="696" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ibexa-dxp/graphics12/696-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ibexa-dxp/graphics12/696-1-eng-US/graphics1.png" original_filename="7e906b13.png" mime_type="image/png" width="540" height="411" alternative_text="" alias_key="1293033771" timestamp="1311154190"><original attribute_id="696" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '696', @@ -6537,13 +6541,13 @@ 'contentobject_id' => '130', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'Lots of Websites, One eZ Publish Installation', + 'data_text' => 'Lots of Websites, One Ibexa Installation', 'data_type_string' => 'ezstring', 'id' => '698', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'lots of websites, one ez publish installation', + 'sort_key_string' => 'lots of websites, one ibexa installation', 'version' => '1', ], 442 => [ @@ -6552,13 +6556,13 @@ 'contentobject_id' => '130', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'Adding Siteaccesses in eZ Publish', + 'data_text' => 'Adding Siteaccesses in Ibexa', 'data_type_string' => 'ezstring', 'id' => '699', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'adding siteaccesses in ez publish', + 'sort_key_string' => 'adding siteaccesses in ibexa', 'version' => '1', ], 443 => [ @@ -6767,7 +6771,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '719', @@ -7032,7 +7036,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '743', @@ -7250,7 +7254,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '763', @@ -7562,7 +7566,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '791', @@ -7686,7 +7690,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '803', @@ -7857,7 +7861,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '819', @@ -7981,7 +7985,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '831', @@ -8152,7 +8156,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '847', @@ -8245,13 +8249,13 @@ 'contentobject_id' => '151', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'eZ Publish Enterprise', + 'data_text' => 'Ibexa Enterprise', 'data_type_string' => 'ezstring', 'id' => '857', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'ez publish enterprise', + 'sort_key_string' => 'ibexa dxp', 'version' => '1', ], 561 => [ @@ -8276,7 +8280,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '859', @@ -8385,7 +8389,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/ez-publish-enterprise/graphics1/871-1-eng-US" url="var/ezwebin_site/storage/images/ez-publish-enterprise/graphics1/871-1-eng-US/graphics1.png" original_filename="068699a4.png" mime_type="image/png" width="332" height="293" alternative_text="" alias_key="1293033771" timestamp="1311154195"><original attribute_id="871" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/ibexa-dxp-enterprise/graphics1/871-1-eng-US" url="var/ezwebin_site/storage/images/ibexa-dxp-enterprise/graphics1/871-1-eng-US/graphics1.png" original_filename="068699a4.png" mime_type="image/png" width="332" height="293" alternative_text="" alias_key="1293033771" timestamp="1311154195"><original attribute_id="871" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '871', @@ -8432,7 +8436,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/ez-publish-enterprise/graphics12/875-1-eng-US" url="var/ezwebin_site/storage/images/ez-publish-enterprise/graphics12/875-1-eng-US/graphics1.png" original_filename="d1120108.png" mime_type="image/png" width="448" height="151" alternative_text="" alias_key="1293033771" timestamp="1311154196"><original attribute_id="875" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/ibexa-dxp-enterprise/graphics12/875-1-eng-US" url="var/ezwebin_site/storage/images/ibexa-dxp-enterprise/graphics12/875-1-eng-US/graphics1.png" original_filename="d1120108.png" mime_type="image/png" width="448" height="151" alternative_text="" alias_key="1293033771" timestamp="1311154196"><original attribute_id="875" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '875', @@ -10092,13 +10096,13 @@ 'contentobject_id' => '177', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'Ivo on eZ Tags', + 'data_text' => 'Ivo on ibexa tags', 'data_type_string' => 'ezstring', 'id' => '1033', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'ivo on ez tags', + 'sort_key_string' => 'ivo on ibexa tags', 'version' => '1', ], 689 => [ @@ -10108,7 +10112,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="Ivo-on-eZ-Tags.jpg" suffix="jpg" basename="Ivo-on-eZ-Tags" dirpath="var/ezwebin_site/storage/images/community/pictures/ivo-on-ez-tags/1035-1-eng-US" url="var/ezwebin_site/storage/images/community/pictures/ivo-on-ez-tags/1035-1-eng-US/Ivo-on-eZ-Tags.jpg" original_filename="9c0a818b.jpg" mime_type="image/jpeg" width="640" height="480" alternative_text="" alias_key="1293033771" timestamp="1311154202"><original attribute_id="1035" attribute_version="1" attribute_language="eng-US"/><information Height="480" Width="640" IsColor="1" ByteOrderMotorola="0" ApertureFNumber="f/4.9" Thumbnail.FileType="2" Thumbnail.MimeType="image/jpeg"><array name="ifd0"><item key="Make" base64="1">UGFuYXNvbmlj</item><item key="Model" base64="1">RE1DLVRaNQ==</item><item key="XResolution" base64="1">MTgwLzE=</item><item key="YResolution" base64="1">MTgwLzE=</item><item key="ResolutionUnit" base64="1">Mg==</item><item key="Software" base64="1">VmVyLjEuMCAg</item><item key="DateTime" base64="1">MjAxMTowMToyNyAxMTozNjo1MQ==</item><item key="Artist" base64="1">UGljYXNh</item><item key="YCbCrPositioning" base64="1">Mg==</item><item key="Exif_IFD_Pointer" base64="1">NTAy</item><item key="UndefinedTag:0xC4A5" base64="1">UHJpbnRJTQAwMjUwAAAOAAEAFgAWAAIAAAAAAAMAZAAAAAcAAAAAAAgAAAAAAAkAAAAAAAoAAAAAAAsArAAAAAwAAAAAAA0AAAAAAA4AxAAAAAABBQAAAAEBAQAAABABgAAAAAkRAAAQJwAACw8AABAnAACXBQAAECcAALAIAAAQJwAAARwAABAnAABeAgAAECcAAIsAAAAQJwAAywMAABAnAADlGwAAECcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==</item><item key="UndefinedTag:0xC6D2" base64="1">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==</item></array><array name="exif"><item key="ExposureTime" base64="1">MTAvMzAw</item><item key="FNumber" base64="1">NDkvMTA=</item><item key="ExposureProgram" base64="1">Mg==</item><item key="ISOSpeedRatings" base64="1">ODAw</item><item key="ExifVersion" base64="1">MDIyMQ==</item><item key="DateTimeOriginal" base64="1">MjAxMTowMToyNyAxMTozNjo1MQ==</item><item key="DateTimeDigitized" base64="1">MjAxMTowMToyNyAxMTozNjo1MQ==</item><item key="ComponentsConfiguration" base64="1">AQIDAA==</item><item key="CompressedBitsPerPixel" base64="1">NC8x</item><item key="ExposureBiasValue" base64="1">MC8xMDA=</item><item key="MaxApertureValue" base64="1">MzQ0LzEwMA==</item><item key="MeteringMode" base64="1">NQ==</item><item key="LightSource" base64="1">MA==</item><item key="Flash" base64="1">MTY=</item><item key="FocalLength" base64="1">NDcwLzEw</item><item key="MakerNote" base64="1">UGFuYXNvbmljAAAANgABAAMAAQAAAAIAAAACAAcABAAAAAABAgADAAMAAQAAAAEAAAAHAAMAAQAAAAUAAAAPAAEAAgAAAAAQAAAaAAMAAQAAAAIAAAAcAAMAAQAAAAIAAAAfAAMAAQAAAAEAAAAgAAMAAQAAAAIAAAAhAAcACCAAALwGAAAiAAMAAQAAAAAAAAAjAAMAAQAAAAAAAAAkAAMAAQAAAAAAAAAlAAcAEAAAAMQmAAAmAAcABAAAADAyNzAnAAMAAQAAAAAAAAAoAAMAAQAAAAEAAAApAAQAAQAAANAlAAAqAAMAAQAAAAAAAAArAAQAAQAAAAAAAAAsAAMAAQAAAAAAAAAtAAMAAQAAAAAAAAAuAAMAAQAAAAEAAAAvAAMAAQAAAAEAAAAwAAMAAQAAAAEAAAAxAAMAAQAAAAIAAAAyAAMAAQAAAAAAAAAzAAIAFAAAANQmAAA0AAMAAQAAAAEAAAA1AAMAAQAAAAEAAAA2AAMAAQAAAP//AAA3AAMAAQAAAAEBAAA4AAMAAQAAAAEAAAA6AAMAAQAAAAIAAAA7AAMAAQAAAAEAAAA8AAMAAQAAAP//AAA9AAMAAQAAAAEAAAA+AAMAAQAAAAEAAAA/AAMAAQAAAAAAAABNAAUAAgAAAOgmAABOAAcAKgAAAPgmAABPAAMAAQAAAAAAAABeAAcABAAAAAAAAAAAgAcABAAAADAxMjEBgAMAAQAAAAAAAAACgAMAAQAAAAIAAAADgAMAAQAAAAEAAAAEgAMAAQAAAKQGAAAFgAMAAQAAAB4EAAAGgAMAAQAAAIYHAAAHgAMAAQAAAAEAAAAIgAMAAQAAAAEAAAAJgAMAAQAAAAEAAAAQgAIAFAAAACInAAAAAAAARFYBAkVQAADw/0RCCgbw/0FGtgCgrxCAoq8AAqSvADCmrwAAyq9EALKvAQC0rwAAuq8IALyv7/++rw0A+K8AAPqvAACor1QEqq9sD8ivTUDYr3wDtq8gAbivAADOr9MA0q8hANCvKACsr5UBsK9EAK6vRADUr2UA9K8oAPKvKADirwAA5K8CAOCvXw/mrwAA6K8BAOqvAADsrwAA9q9xNNqvAADWrwAE8K8kAAQGwxkKBmwPyAYBAZqvAACcryAAnq8AAPD/U1RyAKQGAACmBgAAqAYAAKoGAACsBgAA+gcAAK4GAACwBgAAtgYAALgGAAC6BgAA9AcAAPYHAACyBgAAtAYAALAEAACyBAAAYKkAAGKpAABkqQAAaKkAAGapAABqqQAAeKkAAHqpAAB8qQAAfqkAAPD/QUUmASAFXQIiBWIC7gddAiQFcgQQBWgD3gaeAyYFCwDABgAAKAU6ARgFWQI6BQEAxRkAAFAFVwJSBYACygaoAAAFAwA2BQAAQgUAAAgFAAACBQAACgUAAAQFAABABQAABAcAAAwHAAAGBwAACAcAAAoHAADyBgAA9gb///QGAAD4Bv//DgVnAyoFTgAsBQEDMgUBA/oGWQL8BqgAAAcAAjsFAQA+BQEAxhlXAsgZkAIwBQABHAXfAOgG+AoaBWoT7wYBAPAGAAD+BoAALgUAAAEFAABgBQAANAUAAA5noAI4BQAADgczABAHAAASByMAFAcBABYHXgAXBwAAGAcAAEgFAABKBQAARAUAAEYFAAAUBTEBFgUnADwFKgFYBRIDWgXUAPD/V0LqAAAEpAYCBIYHXAQeBAQE/QAGBA0BYASeAxoEGQBeBA4AXwQGABIE1wAUBPsAFgR1ARgERgHMBGcEzgTnCdAE7wbSBDkHCAQDAQoEKgG0BNIAtgQAAUAE9QAwBF4AOAR4ADIEgAA6BIQANATg/zwEBAA2BBAAPgQpAEwE/gBOBAsBwAT9AMIEDQHqBAAAgAUOAIIFfwCDBYUAjAUAAI4FhgCPBYwAUgTXAFQE+wBWBNcAWAT7ANQE1wDWBPsA2ATXANoE+wDwBCQH8gRRBvQEGAH2BDEB+AQhB/oERwb8BBcB/gQzAfD/WUPmAE6qFABQqhQAUqoUAFSqFABEqv//RqrMiEiqdwNKqgAATKoAADiqUAA6qlAAPKpQAD6qUAAuqoiIMKrMiDKqd1U0qiIRNqoAAIIEIACABAkAhAQAAISqcABgqikzYqopM2SqYHxmqmB8aKo6SWqqAQBsqgAAbqoAAIaqAACIqgAAiqoUAIyqFACOqgAAkKoAAJKqHwCUqh8AlqoAAJiqAACgqvAAoqoQAKSq6ACmqgAAqKoKAFiqAQBaqgAAXKoAAF6qIACaqgAAnKoAAMCq0ADCqtAAxKrQAMaq0ADIqggI8P9DTQ4A/AUAMASsAADw/0RTLgAArwABAq+nhQqv5wAEr4UBBq8QAQivCQAMr0gBDq8QARCvhQESr6sA8P9JU8oAiK4AALSuawC2rmIAuK4UC7quFAuArq8Dgq7+A4Su5wOGrucDAK64AwKusAMErqwDBq6mAwiupgMKrqIDDK6mAw6upAMQrqYDEq6oAxSurAMWrq4DGK62AxquugMcrsADHq7IAyCuGAQirg4EJK4QBCauEAQorhYEKq4UBCyuFAQurgwEMK4MBDKuCgQ0rgQENq72Aziu9AM6rvgDPK74Az6uAARArgYCQq4HAkSuAABGrgAAYK4RAmKuEAJkrgAAZq4AAPD/RkSmAGCsAABirAAAgKwAAIKsAACErAAAhqwAAIisAACKrAAAjKwAAI6sAACQrAAAkqwAAJSsAACWrAAAmKwAAJqsAACcrAAAnqwAAECsAABCrAAARKwAAEasAABIrAAASqwAAEysAABOrAAAUKwAAFKsAABUrAAAVqwAAFisAABarAAAXKwAAF6sAABmBQAAbgUAAHAFAAByBQAAbAUAAGQFAADw/0lBOgCgqf//oqn//6Sp//+mqf//qKn//6qp//+sqf//rqn//4CpAACCqQAAhKkAAIipAACGqTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFFQk0OB3QHnQeZB3YHNQd8AjQBogAdAEEAHwAfACAAIAAgAOcGagfWB6wHkgerB6ECUgGnAB8ARgAiACIAIwAjACMADQiaCIYISgizBwEIlgJZAaUAIQBJACMAIwAlACUAJACjCOcI5gjYCKUIYQh0Ak4BmAAXACMAOAA5ADcANQAzAHYI3gjXCMMImAhgCGMCTAHMAHEALACXAOUA5wDqAOwAvwjTCKwH2waXB10IPgJNAcgAcAB/AOUA8gD3APIA/wDnCAkHKQbIB1wIXAgPAjoBfgBYAKYAmgBSAI8ACgE8AecIVQfMBdMFGAdbCOUBFAEgABwALgAkAAwADgCfAEEB3AjKBnAFkgSHBl4FWQF4ABUAEgAdABUADAAMAFAAbQHGCF8HFwW5A4ABswBKAA4AFAAPACIAIgALABIAFQARAf0CWwKNAV4APABHADMAPAAaAA4AGwAaAAoAEwAPANEArAMGBO4ExAKrA1UFIgHzACkADAAdADIAEgAPABkAdwASCIAIbwg2CA4Inwc4AfsAJgAMAEMAUAAbABMALwDqACkHRQfwB6sH5gdsBzcBAQEgAAwAHwAeAA0AGgCiAHgB1AXQBWQGHgZIBtQFJgHyABEACQAWABcAEAAkAGwAlgHLAOIA/QAIAQgBDwERAeAADAAGAAgACgAHABAANwCiAVBSU1RXGBsDvgJAA7oDxwOWA/EDCgUdBSYD/gzFC+sLNw1IEJ0Z3wNhA9oDDAQaBBAEkgXJBx8H/wM3C4oINwjLDnoLDRyjBDoERgRWBI8ECQX/B5AKdwoQCX8DYwKOAbMFOwcpIDkHuAdSBx0HJwc5CB8N+A9uDoAI8gEXAboAAQIqBH4fhgUuBTQFNwVcBcYG0QyXEjURAgXYAe4ApQAZAgQEwB1VBnAFlBTxLRMsvCLjIaQzvjHyBbICCwNhAq4CAgTUGgAGJQUIFLYcawOoAqYCSQKKDLcLHgPmBmMLlwEbBBgYHAWlBHwPVBRGAggCkAIrAfkA5wAyAXYC3Ag5AmkDQRbBBCAETAbUC7ID6QM8BKQB8QClAvMBtgIGB/wBpAdiFSUE6QN6Dk8NGQIrArECpAGiAukB1wCiBCYQtwPwACIUUgbWBj8JDhKtA00JwQgSCmsRbAzCB8kGNgdfAskAkg3LA4EEywQsBQkEfwOSBc4GoAWzBH4EHQOmAcsA3gAFHbQfTyTEIRIi8BtVDW0eRyGKCBYEhgLNALsAiADJAH8iA2qxdRt5nX2ceMxJzYWnO9AH+APNAYsAaAB5AI4AoyoLyvTIgtBwxlO182UbyjFOWAdiAsQAoQAhAo8AgQCYLd3Npsy10hO1pJ6FrT/I81BTB90B/QEsAisDQAGTAPoWzALMAm0D0AO+A6MDMQSWBREFxQOODHgLqguXDrQOUxhKA4ID7AMcBCQEMgQNBmgIowe4BesJ9QYrB9ANjArLG88DUgRRBG0EiwRTBasIGAuhClUIfQLhASoBKwU9BsceqwaxB3YHHgc6B4sIxw0+EHgOaQfNAQUBrQBOAgsEER6qBDUFpgU3CGoNSRLyGLkdKhZaBOwB3ACqALoCFQSdHN4EYAUFGmAzVCzAH8IfzjPTMlIIMQPbA9oDVQJGBKEZmQQEBZkVrxn7A8oCpAJrAnYOVwuDA7UHmwqBAVEE6BYvBIwEnxHXDxMC1wGCAiYB7gDbAHYBHQKjCA4C5wMmFbYD8APyB0gNAAT7AxcEfwHoAHcCuAGYAsUGBAJ3Bw4UjwPNA9MO4QoUAh4CuAJ9AZsC4wHXAOgGTxEzA/MAixISBsIGmwdnDDgCNQdqBloGrAidCZ0IlAaXBJcB1gDTDcsHEAnJCPUImgVqBeIIUAvOBdwDtgP2AXoBoAD5AHQeJSKeJi8k2CRoG60QlCAFIqAGSwQEAqAAjgCCAM8AXCuViauTNZeQmoSED19Bo145zwVYA3MBcgBYAHAAhQCHN1nMq8np0DK7O6bzaVrJxkFzBSECsADtAMsCjQBzABQ8086EzZTSU65joCOtxsejRLkF2gEzAj0C9QQCAoMAAAAAAAAAgAHgDzwMLAoACAAOBAgIAMAPwA0AAAAAAAAmAAAAAAAAAAAAwB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAABwBGQ0NWBQABAGwPXw8AAAAAAAAAAAAAAAAAAAAAAAAAAFIlCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBgDcsBoQH6APgB+QDfACQBkwHpAKoDGAT5AkwBfASWATYB/QF7A9QAjAfyAAAAAACiADMBAAAAAIAO3gG6AQUBIgI3Ad8ARQGAAfkA5AM5BBgDagGRBBQCNgEjAo0D5gDNB/cAAAAAAKYARAEAAAAAoA/gAboBHwEkAjgB2gBbAZQBBAHdA1QEMgN5AZ8EHgIuAT8CtAPwAAgI/wAAAAAArQBWAQAAAADAEMsBwAEhAf4BNQHZAGUBhwEFAdEDQwQ1A4EBmAQQAiwBRgKWA/EAAwgAAQAAAAChAFcBAAAAAOARhQGkAQ0B3gENAdgAKQFDAfEAUgMLBAgDYAF5BP4BNQEBAsIC6QCIB9YAAAAAAIQA8AAAAAAAlTjTAOYG5QHGAcABbwLyAbUBAAAMBAwCvA2jA60BBgMfAxkDogEAAOMF6wAAAAAAHAAOAAAAAAA1NyQBRAEKASEBhAI9ATgBBAFZAQYDBwFwAvgAJwIKBscB/QFsAu8C0QW/AQAAAABsAvgCAAAAABU2RgGeATMBUgGWA1ABZAGBAR4C/gNIAfYCHgGFAisHDgI+AgoDlQP7BjUCAAAAAJ8C8wIAAAAA9TRhAbsBNwFnAQ4EVAFmAZoBLAJqBHMBVQM0AacCeAcgAkwCLgO3A3wHjwIAAAAA/QILAwAAAADVM50BOwJbAdgBwwShAbkBMALCAmsFngGdA10BvgLcB0QCZwJIA/4DEQiFAgAAAADjAv0CAAAAALUyaQG3AUoBgAGkA2MBcgFwAf0BSASUAVcDUgFkAkAHAwInAr0CjQNlB00CAAAAAK4CnQIAAAAAlTEuAUgBGAE3AR8CQQEoAeoAOgECA18B1AIiAQ8CCgbAAd8BUQLYAjIGRQIAAAAApQKYAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV0JDWg8PDw8PDw8MDA4KDg4ODg4PDw8PDw8PAwoOCg4ODg4ODw8PDw8PDwMKDg4ODg4ODg8PDw8PDw8DCgoOCgoKCgoPDw8PDw8PAwoKCgoKCgoKDw8PDw8PDwMKCgoKCgoKCg8PDw8PDw8DCg4KCQ4OCgoPDw8PDw8PDAoODg4ODgoKDw8PDwUPAQEODg4ODg4KCg8PDw8PAQ8ODg4ODg4OCQoPDw8BDw8PCQoODg4ODg4KDwIBDw8PDwEKDgoKDg4OCg8PDw8PDw8DCg4KCg4ODgoPDw8PDw8PAwoODg4ODgoKDw8PDw8PDwMKDg4ODg4KCgUFBQICAgEDCg4ODg4OCgpCTUhMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEY1NTA4MTAxNTAxMTkAMDA5OTk5Ojk5Ojk5IDAwOjAwOjAwAH8AAAAAAQAAfwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5OTk5Ojk5Ojk5IDAwOjAwOjAwAA==</item><item key="FlashPixVersion" base64="1">MDEwMA==</item><item key="ColorSpace" base64="1">MQ==</item><item key="ExifImageWidth" base64="1">NjQw</item><item key="ExifImageLength" base64="1">NDgw</item><item key="InteroperabilityOffset" base64="1">MTAwMzg=</item><item key="SensingMethod" base64="1">Mg==</item><item key="FileSource" base64="1">Aw==</item><item key="SceneType" base64="1">AQ==</item><item key="CustomRendered" base64="1">MA==</item><item key="ExposureMode" base64="1">MA==</item><item key="WhiteBalance" base64="1">MA==</item><item key="DigitalZoomRatio" base64="1">MC8xMA==</item><item key="FocalLengthIn35mmFilm" base64="1">Mjgw</item><item key="SceneCaptureType" base64="1">MA==</item><item key="GainControl" base64="1">Mg==</item><item key="Contrast" base64="1">MA==</item><item key="Saturation" base64="1">MA==</item><item key="Sharpness" base64="1">MA==</item><item key="ImageUniqueID" base64="1">ZjMzYWVmMjUxZTZhNWRhYjYwMDdhYzAyOGJjZDU3NjI=</item></array></information><alias name="listitem" filename="Ivo-on-eZ-Tags_listitem.jpg" suffix="jpg" dirpath="var/ezwebin_site/storage/images/community/pictures/ivo-on-ez-tags/1035-1-eng-US" url="var/ezwebin_site/storage/images/community/pictures/ivo-on-ez-tags/1035-1-eng-US/Ivo-on-eZ-Tags_listitem.jpg" mime_type="image/jpeg" width="130" height="98" alias_key="379714049" timestamp="1311154279" is_valid="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="Ivo-on-ibexa-tags.jpg" suffix="jpg" basename="Ivo-on-ibexa-tags" dirpath="var/ezwebin_site/storage/images/community/pictures/ivo-on-ibexa-tags/1035-1-eng-US" url="var/ezwebin_site/storage/images/community/pictures/ivo-on-ibexa-tags/1035-1-eng-US/Ivo-on-ibexa-tags.jpg" original_filename="9c0a818b.jpg" mime_type="image/jpeg" width="640" height="480" alternative_text="" alias_key="1293033771" timestamp="1311154202"><original attribute_id="1035" attribute_version="1" attribute_language="eng-US"/><information Height="480" Width="640" IsColor="1" ByteOrderMotorola="0" ApertureFNumber="f/4.9" Thumbnail.FileType="2" Thumbnail.MimeType="image/jpeg"><array name="ifd0"><item key="Make" base64="1">UGFuYXNvbmlj</item><item key="Model" base64="1">RE1DLVRaNQ==</item><item key="XResolution" base64="1">MTgwLzE=</item><item key="YResolution" base64="1">MTgwLzE=</item><item key="ResolutionUnit" base64="1">Mg==</item><item key="Software" base64="1">VmVyLjEuMCAg</item><item key="DateTime" base64="1">MjAxMTowMToyNyAxMTozNjo1MQ==</item><item key="Artist" base64="1">UGljYXNh</item><item key="YCbCrPositioning" base64="1">Mg==</item><item key="Exif_IFD_Pointer" base64="1">NTAy</item><item key="UndefinedTag:0xC4A5" base64="1">UHJpbnRJTQAwMjUwAAAOAAEAFgAWAAIAAAAAAAMAZAAAAAcAAAAAAAgAAAAAAAkAAAAAAAoAAAAAAAsArAAAAAwAAAAAAA0AAAAAAA4AxAAAAAABBQAAAAEBAQAAABABgAAAAAkRAAAQJwAACw8AABAnAACXBQAAECcAALAIAAAQJwAAARwAABAnAABeAgAAECcAAIsAAAAQJwAAywMAABAnAADlGwAAECcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==</item><item key="UndefinedTag:0xC6D2" base64="1">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==</item></array><array name="exif"><item key="ExposureTime" base64="1">MTAvMzAw</item><item key="FNumber" base64="1">NDkvMTA=</item><item key="ExposureProgram" base64="1">Mg==</item><item key="ISOSpeedRatings" base64="1">ODAw</item><item key="ExifVersion" base64="1">MDIyMQ==</item><item key="DateTimeOriginal" base64="1">MjAxMTowMToyNyAxMTozNjo1MQ==</item><item key="DateTimeDigitized" base64="1">MjAxMTowMToyNyAxMTozNjo1MQ==</item><item key="ComponentsConfiguration" base64="1">AQIDAA==</item><item key="CompressedBitsPerPixel" base64="1">NC8x</item><item key="ExposureBiasValue" base64="1">MC8xMDA=</item><item key="MaxApertureValue" base64="1">MzQ0LzEwMA==</item><item key="MeteringMode" base64="1">NQ==</item><item key="LightSource" base64="1">MA==</item><item key="Flash" base64="1">MTY=</item><item key="FocalLength" base64="1">NDcwLzEw</item><item key="MakerNote" base64="1">UGFuYXNvbmljAAAANgABAAMAAQAAAAIAAAACAAcABAAAAAABAgADAAMAAQAAAAEAAAAHAAMAAQAAAAUAAAAPAAEAAgAAAAAQAAAaAAMAAQAAAAIAAAAcAAMAAQAAAAIAAAAfAAMAAQAAAAEAAAAgAAMAAQAAAAIAAAAhAAcACCAAALwGAAAiAAMAAQAAAAAAAAAjAAMAAQAAAAAAAAAkAAMAAQAAAAAAAAAlAAcAEAAAAMQmAAAmAAcABAAAADAyNzAnAAMAAQAAAAAAAAAoAAMAAQAAAAEAAAApAAQAAQAAANAlAAAqAAMAAQAAAAAAAAArAAQAAQAAAAAAAAAsAAMAAQAAAAAAAAAtAAMAAQAAAAAAAAAuAAMAAQAAAAEAAAAvAAMAAQAAAAEAAAAwAAMAAQAAAAEAAAAxAAMAAQAAAAIAAAAyAAMAAQAAAAAAAAAzAAIAFAAAANQmAAA0AAMAAQAAAAEAAAA1AAMAAQAAAAEAAAA2AAMAAQAAAP//AAA3AAMAAQAAAAEBAAA4AAMAAQAAAAEAAAA6AAMAAQAAAAIAAAA7AAMAAQAAAAEAAAA8AAMAAQAAAP//AAA9AAMAAQAAAAEAAAA+AAMAAQAAAAEAAAA/AAMAAQAAAAAAAABNAAUAAgAAAOgmAABOAAcAKgAAAPgmAABPAAMAAQAAAAAAAABeAAcABAAAAAAAAAAAgAcABAAAADAxMjEBgAMAAQAAAAAAAAACgAMAAQAAAAIAAAADgAMAAQAAAAEAAAAEgAMAAQAAAKQGAAAFgAMAAQAAAB4EAAAGgAMAAQAAAIYHAAAHgAMAAQAAAAEAAAAIgAMAAQAAAAEAAAAJgAMAAQAAAAEAAAAQgAIAFAAAACInAAAAAAAARFYBAkVQAADw/0RCCgbw/0FGtgCgrxCAoq8AAqSvADCmrwAAyq9EALKvAQC0rwAAuq8IALyv7/++rw0A+K8AAPqvAACor1QEqq9sD8ivTUDYr3wDtq8gAbivAADOr9MA0q8hANCvKACsr5UBsK9EAK6vRADUr2UA9K8oAPKvKADirwAA5K8CAOCvXw/mrwAA6K8BAOqvAADsrwAA9q9xNNqvAADWrwAE8K8kAAQGwxkKBmwPyAYBAZqvAACcryAAnq8AAPD/U1RyAKQGAACmBgAAqAYAAKoGAACsBgAA+gcAAK4GAACwBgAAtgYAALgGAAC6BgAA9AcAAPYHAACyBgAAtAYAALAEAACyBAAAYKkAAGKpAABkqQAAaKkAAGapAABqqQAAeKkAAHqpAAB8qQAAfqkAAPD/QUUmASAFXQIiBWIC7gddAiQFcgQQBWgD3gaeAyYFCwDABgAAKAU6ARgFWQI6BQEAxRkAAFAFVwJSBYACygaoAAAFAwA2BQAAQgUAAAgFAAACBQAACgUAAAQFAABABQAABAcAAAwHAAAGBwAACAcAAAoHAADyBgAA9gb///QGAAD4Bv//DgVnAyoFTgAsBQEDMgUBA/oGWQL8BqgAAAcAAjsFAQA+BQEAxhlXAsgZkAIwBQABHAXfAOgG+AoaBWoT7wYBAPAGAAD+BoAALgUAAAEFAABgBQAANAUAAA5noAI4BQAADgczABAHAAASByMAFAcBABYHXgAXBwAAGAcAAEgFAABKBQAARAUAAEYFAAAUBTEBFgUnADwFKgFYBRIDWgXUAPD/V0LqAAAEpAYCBIYHXAQeBAQE/QAGBA0BYASeAxoEGQBeBA4AXwQGABIE1wAUBPsAFgR1ARgERgHMBGcEzgTnCdAE7wbSBDkHCAQDAQoEKgG0BNIAtgQAAUAE9QAwBF4AOAR4ADIEgAA6BIQANATg/zwEBAA2BBAAPgQpAEwE/gBOBAsBwAT9AMIEDQHqBAAAgAUOAIIFfwCDBYUAjAUAAI4FhgCPBYwAUgTXAFQE+wBWBNcAWAT7ANQE1wDWBPsA2ATXANoE+wDwBCQH8gRRBvQEGAH2BDEB+AQhB/oERwb8BBcB/gQzAfD/WUPmAE6qFABQqhQAUqoUAFSqFABEqv//RqrMiEiqdwNKqgAATKoAADiqUAA6qlAAPKpQAD6qUAAuqoiIMKrMiDKqd1U0qiIRNqoAAIIEIACABAkAhAQAAISqcABgqikzYqopM2SqYHxmqmB8aKo6SWqqAQBsqgAAbqoAAIaqAACIqgAAiqoUAIyqFACOqgAAkKoAAJKqHwCUqh8AlqoAAJiqAACgqvAAoqoQAKSq6ACmqgAAqKoKAFiqAQBaqgAAXKoAAF6qIACaqgAAnKoAAMCq0ADCqtAAxKrQAMaq0ADIqggI8P9DTQ4A/AUAMASsAADw/0RTLgAArwABAq+nhQqv5wAEr4UBBq8QAQivCQAMr0gBDq8QARCvhQESr6sA8P9JU8oAiK4AALSuawC2rmIAuK4UC7quFAuArq8Dgq7+A4Su5wOGrucDAK64AwKusAMErqwDBq6mAwiupgMKrqIDDK6mAw6upAMQrqYDEq6oAxSurAMWrq4DGK62AxquugMcrsADHq7IAyCuGAQirg4EJK4QBCauEAQorhYEKq4UBCyuFAQurgwEMK4MBDKuCgQ0rgQENq72Aziu9AM6rvgDPK74Az6uAARArgYCQq4HAkSuAABGrgAAYK4RAmKuEAJkrgAAZq4AAPD/RkSmAGCsAABirAAAgKwAAIKsAACErAAAhqwAAIisAACKrAAAjKwAAI6sAACQrAAAkqwAAJSsAACWrAAAmKwAAJqsAACcrAAAnqwAAECsAABCrAAARKwAAEasAABIrAAASqwAAEysAABOrAAAUKwAAFKsAABUrAAAVqwAAFisAABarAAAXKwAAF6sAABmBQAAbgUAAHAFAAByBQAAbAUAAGQFAADw/0lBOgCgqf//oqn//6Sp//+mqf//qKn//6qp//+sqf//rqn//4CpAACCqQAAhKkAAIipAACGqTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFFQk0OB3QHnQeZB3YHNQd8AjQBogAdAEEAHwAfACAAIAAgAOcGagfWB6wHkgerB6ECUgGnAB8ARgAiACIAIwAjACMADQiaCIYISgizBwEIlgJZAaUAIQBJACMAIwAlACUAJACjCOcI5gjYCKUIYQh0Ak4BmAAXACMAOAA5ADcANQAzAHYI3gjXCMMImAhgCGMCTAHMAHEALACXAOUA5wDqAOwAvwjTCKwH2waXB10IPgJNAcgAcAB/AOUA8gD3APIA/wDnCAkHKQbIB1wIXAgPAjoBfgBYAKYAmgBSAI8ACgE8AecIVQfMBdMFGAdbCOUBFAEgABwALgAkAAwADgCfAEEB3AjKBnAFkgSHBl4FWQF4ABUAEgAdABUADAAMAFAAbQHGCF8HFwW5A4ABswBKAA4AFAAPACIAIgALABIAFQARAf0CWwKNAV4APABHADMAPAAaAA4AGwAaAAoAEwAPANEArAMGBO4ExAKrA1UFIgHzACkADAAdADIAEgAPABkAdwASCIAIbwg2CA4Inwc4AfsAJgAMAEMAUAAbABMALwDqACkHRQfwB6sH5gdsBzcBAQEgAAwAHwAeAA0AGgCiAHgB1AXQBWQGHgZIBtQFJgHyABEACQAWABcAEAAkAGwAlgHLAOIA/QAIAQgBDwERAeAADAAGAAgACgAHABAANwCiAVBSU1RXGBsDvgJAA7oDxwOWA/EDCgUdBSYD/gzFC+sLNw1IEJ0Z3wNhA9oDDAQaBBAEkgXJBx8H/wM3C4oINwjLDnoLDRyjBDoERgRWBI8ECQX/B5AKdwoQCX8DYwKOAbMFOwcpIDkHuAdSBx0HJwc5CB8N+A9uDoAI8gEXAboAAQIqBH4fhgUuBTQFNwVcBcYG0QyXEjURAgXYAe4ApQAZAgQEwB1VBnAFlBTxLRMsvCLjIaQzvjHyBbICCwNhAq4CAgTUGgAGJQUIFLYcawOoAqYCSQKKDLcLHgPmBmMLlwEbBBgYHAWlBHwPVBRGAggCkAIrAfkA5wAyAXYC3Ag5AmkDQRbBBCAETAbUC7ID6QM8BKQB8QClAvMBtgIGB/wBpAdiFSUE6QN6Dk8NGQIrArECpAGiAukB1wCiBCYQtwPwACIUUgbWBj8JDhKtA00JwQgSCmsRbAzCB8kGNgdfAskAkg3LA4EEywQsBQkEfwOSBc4GoAWzBH4EHQOmAcsA3gAFHbQfTyTEIRIi8BtVDW0eRyGKCBYEhgLNALsAiADJAH8iA2qxdRt5nX2ceMxJzYWnO9AH+APNAYsAaAB5AI4AoyoLyvTIgtBwxlO182UbyjFOWAdiAsQAoQAhAo8AgQCYLd3Npsy10hO1pJ6FrT/I81BTB90B/QEsAisDQAGTAPoWzALMAm0D0AO+A6MDMQSWBREFxQOODHgLqguXDrQOUxhKA4ID7AMcBCQEMgQNBmgIowe4BesJ9QYrB9ANjArLG88DUgRRBG0EiwRTBasIGAuhClUIfQLhASoBKwU9BsceqwaxB3YHHgc6B4sIxw0+EHgOaQfNAQUBrQBOAgsEER6qBDUFpgU3CGoNSRLyGLkdKhZaBOwB3ACqALoCFQSdHN4EYAUFGmAzVCzAH8IfzjPTMlIIMQPbA9oDVQJGBKEZmQQEBZkVrxn7A8oCpAJrAnYOVwuDA7UHmwqBAVEE6BYvBIwEnxHXDxMC1wGCAiYB7gDbAHYBHQKjCA4C5wMmFbYD8APyB0gNAAT7AxcEfwHoAHcCuAGYAsUGBAJ3Bw4UjwPNA9MO4QoUAh4CuAJ9AZsC4wHXAOgGTxEzA/MAixISBsIGmwdnDDgCNQdqBloGrAidCZ0IlAaXBJcB1gDTDcsHEAnJCPUImgVqBeIIUAvOBdwDtgP2AXoBoAD5AHQeJSKeJi8k2CRoG60QlCAFIqAGSwQEAqAAjgCCAM8AXCuViauTNZeQmoSED19Bo145zwVYA3MBcgBYAHAAhQCHN1nMq8np0DK7O6bzaVrJxkFzBSECsADtAMsCjQBzABQ8086EzZTSU65joCOtxsejRLkF2gEzAj0C9QQCAoMAAAAAAAAAgAHgDzwMLAoACAAOBAgIAMAPwA0AAAAAAAAmAAAAAAAAAAAAwB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAABwBGQ0NWBQABAGwPXw8AAAAAAAAAAAAAAAAAAAAAAAAAAFIlCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBgDcsBoQH6APgB+QDfACQBkwHpAKoDGAT5AkwBfASWATYB/QF7A9QAjAfyAAAAAACiADMBAAAAAIAO3gG6AQUBIgI3Ad8ARQGAAfkA5AM5BBgDagGRBBQCNgEjAo0D5gDNB/cAAAAAAKYARAEAAAAAoA/gAboBHwEkAjgB2gBbAZQBBAHdA1QEMgN5AZ8EHgIuAT8CtAPwAAgI/wAAAAAArQBWAQAAAADAEMsBwAEhAf4BNQHZAGUBhwEFAdEDQwQ1A4EBmAQQAiwBRgKWA/EAAwgAAQAAAAChAFcBAAAAAOARhQGkAQ0B3gENAdgAKQFDAfEAUgMLBAgDYAF5BP4BNQEBAsIC6QCIB9YAAAAAAIQA8AAAAAAAlTjTAOYG5QHGAcABbwLyAbUBAAAMBAwCvA2jA60BBgMfAxkDogEAAOMF6wAAAAAAHAAOAAAAAAA1NyQBRAEKASEBhAI9ATgBBAFZAQYDBwFwAvgAJwIKBscB/QFsAu8C0QW/AQAAAABsAvgCAAAAABU2RgGeATMBUgGWA1ABZAGBAR4C/gNIAfYCHgGFAisHDgI+AgoDlQP7BjUCAAAAAJ8C8wIAAAAA9TRhAbsBNwFnAQ4EVAFmAZoBLAJqBHMBVQM0AacCeAcgAkwCLgO3A3wHjwIAAAAA/QILAwAAAADVM50BOwJbAdgBwwShAbkBMALCAmsFngGdA10BvgLcB0QCZwJIA/4DEQiFAgAAAADjAv0CAAAAALUyaQG3AUoBgAGkA2MBcgFwAf0BSASUAVcDUgFkAkAHAwInAr0CjQNlB00CAAAAAK4CnQIAAAAAlTEuAUgBGAE3AR8CQQEoAeoAOgECA18B1AIiAQ8CCgbAAd8BUQLYAjIGRQIAAAAApQKYAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV0JDWg8PDw8PDw8MDA4KDg4ODg4PDw8PDw8PAwoOCg4ODg4ODw8PDw8PDwMKDg4ODg4ODg8PDw8PDw8DCgoOCgoKCgoPDw8PDw8PAwoKCgoKCgoKDw8PDw8PDwMKCgoKCgoKCg8PDw8PDw8DCg4KCQ4OCgoPDw8PDw8PDAoODg4ODgoKDw8PDwUPAQEODg4ODg4KCg8PDw8PAQ8ODg4ODg4OCQoPDw8BDw8PCQoODg4ODg4KDwIBDw8PDwEKDgoKDg4OCg8PDw8PDw8DCg4KCg4ODgoPDw8PDw8PAwoODg4ODgoKDw8PDw8PDwMKDg4ODg4KCgUFBQICAgEDCg4ODg4OCgpCTUhMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEY1NTA4MTAxNTAxMTkAMDA5OTk5Ojk5Ojk5IDAwOjAwOjAwAH8AAAAAAQAAfwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5OTk5Ojk5Ojk5IDAwOjAwOjAwAA==</item><item key="FlashPixVersion" base64="1">MDEwMA==</item><item key="ColorSpace" base64="1">MQ==</item><item key="ExifImageWidth" base64="1">NjQw</item><item key="ExifImageLength" base64="1">NDgw</item><item key="InteroperabilityOffset" base64="1">MTAwMzg=</item><item key="SensingMethod" base64="1">Mg==</item><item key="FileSource" base64="1">Aw==</item><item key="SceneType" base64="1">AQ==</item><item key="CustomRendered" base64="1">MA==</item><item key="ExposureMode" base64="1">MA==</item><item key="WhiteBalance" base64="1">MA==</item><item key="DigitalZoomRatio" base64="1">MC8xMA==</item><item key="FocalLengthIn35mmFilm" base64="1">Mjgw</item><item key="SceneCaptureType" base64="1">MA==</item><item key="GainControl" base64="1">Mg==</item><item key="Contrast" base64="1">MA==</item><item key="Saturation" base64="1">MA==</item><item key="Sharpness" base64="1">MA==</item><item key="ImageUniqueID" base64="1">ZjMzYWVmMjUxZTZhNWRhYjYwMDdhYzAyOGJjZDU3NjI=</item></array></information><alias name="listitem" filename="Ivo-on-ibexa-tags_listitem.jpg" suffix="jpg" dirpath="var/ezwebin_site/storage/images/community/pictures/ivo-on-ibexa-tags/1035-1-eng-US" url="var/ezwebin_site/storage/images/community/pictures/ivo-on-ibexa-tags/1035-1-eng-US/Ivo-on-ibexa-tags_listitem.jpg" mime_type="image/jpeg" width="130" height="98" alias_key="379714049" timestamp="1311154279" is_valid="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1035', @@ -10873,13 +10877,13 @@ 'contentobject_id' => '191', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'Separate content & design in eZ Publish', + 'data_text' => 'Separate content & design in Ibexa', 'data_type_string' => 'ezstring', 'id' => '1101', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'separate content & design in ez publish', + 'sort_key_string' => 'separate content & design in ibexa', 'version' => '1', ], 740 => [ @@ -10936,7 +10940,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="Separate-content-design-in-eZ-Publish.jpg" suffix="jpg" basename="Separate-content-design-in-eZ-Publish" dirpath="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ez-publish/1107-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ez-publish/1107-1-eng-US/Separate-content-design-in-eZ-Publish.jpg" original_filename="844e123d.jpg" mime_type="image/jpeg" width="530" height="420" alternative_text="" alias_key="1293033771" timestamp="1311154205"><original attribute_id="1107" attribute_version="1" attribute_language="eng-US"/><information Height="420" Width="530" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="Separate-content-design-in-Ibexa.jpg" suffix="jpg" basename="Separate-content-design-in-Ibexa" dirpath="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ibexa-dxp/1107-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ibexa-dxp/1107-1-eng-US/Separate-content-design-in-Ibexa.jpg" original_filename="844e123d.jpg" mime_type="image/jpeg" width="530" height="420" alternative_text="" alias_key="1293033771" timestamp="1311154205"><original attribute_id="1107" attribute_version="1" attribute_language="eng-US"/><information Height="420" Width="530" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1107', @@ -11013,7 +11017,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="separation_of_content_and_design.jpg" suffix="jpg" basename="separation_of_content_and_design" dirpath="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ez-publish/separation_of_content_and_design/1115-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ez-publish/separation_of_content_and_design/1115-1-eng-US/separation_of_content_and_design.jpg" original_filename="1cb1af18.jpg" mime_type="image/jpeg" width="500" height="195" alternative_text="" alias_key="1293033771" timestamp="1311154205"><original attribute_id="1115" attribute_version="1" attribute_language="eng-US"/><information Height="195" Width="500" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="separation_of_content_and_design.jpg" suffix="jpg" basename="separation_of_content_and_design" dirpath="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ibexa-dxp/separation_of_content_and_design/1115-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ibexa-dxp/separation_of_content_and_design/1115-1-eng-US/separation_of_content_and_design.jpg" original_filename="1cb1af18.jpg" mime_type="image/jpeg" width="500" height="195" alternative_text="" alias_key="1293033771" timestamp="1311154205"><original attribute_id="1115" attribute_version="1" attribute_language="eng-US"/><information Height="195" Width="500" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1115', @@ -11044,13 +11048,13 @@ 'contentobject_id' => '193', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'How to use eZ Publish', + 'data_text' => 'How to use Ibexa', 'data_type_string' => 'ezstring', 'id' => '1117', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'how to use ez publish', + 'sort_key_string' => 'how to use ibexa', 'version' => '1', ], 752 => [ @@ -11107,7 +11111,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="How-to-use-eZ-Publish.jpg" suffix="jpg" basename="How-to-use-eZ-Publish" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/1123-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/1123-1-eng-US/How-to-use-eZ-Publish.jpg" original_filename="0aab879e.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154205"><original attribute_id="1123" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="How-to-use-Ibexa.jpg" suffix="jpg" basename="How-to-use-Ibexa" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/1123-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/1123-1-eng-US/How-to-use-Ibexa.jpg" original_filename="0aab879e.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154205"><original attribute_id="1123" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1123', @@ -11184,7 +11188,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="openoffice_import_large.jpg" suffix="jpg" basename="openoffice_import_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/openoffice_import_large/1131-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/openoffice_import_large/1131-1-eng-US/openoffice_import_large.jpg" original_filename="36b1357b.jpg" mime_type="image/jpeg" width="540" height="357" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1131" attribute_version="1" attribute_language="eng-US"/><information Height="357" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="openoffice_import_large.jpg" suffix="jpg" basename="openoffice_import_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/openoffice_import_large/1131-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/openoffice_import_large/1131-1-eng-US/openoffice_import_large.jpg" original_filename="36b1357b.jpg" mime_type="image/jpeg" width="540" height="357" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1131" attribute_version="1" attribute_language="eng-US"/><information Height="357" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1131', @@ -11231,7 +11235,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="translation_large.jpg" suffix="jpg" basename="translation_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/translation_large/1135-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/translation_large/1135-1-eng-US/translation_large.jpg" original_filename="503004df.jpg" mime_type="image/jpeg" width="540" height="419" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1135" attribute_version="1" attribute_language="eng-US"/><information Height="419" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="translation_large.jpg" suffix="jpg" basename="translation_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/translation_large/1135-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/translation_large/1135-1-eng-US/translation_large.jpg" original_filename="503004df.jpg" mime_type="image/jpeg" width="540" height="419" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1135" attribute_version="1" attribute_language="eng-US"/><information Height="419" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1135', @@ -11278,7 +11282,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="multiupload_large.jpg" suffix="jpg" basename="multiupload_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/multiupload_large/1139-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/multiupload_large/1139-1-eng-US/multiupload_large.jpg" original_filename="9652f5a1.jpg" mime_type="image/jpeg" width="540" height="404" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1139" attribute_version="1" attribute_language="eng-US"/><information Height="404" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="multiupload_large.jpg" suffix="jpg" basename="multiupload_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/multiupload_large/1139-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/multiupload_large/1139-1-eng-US/multiupload_large.jpg" original_filename="9652f5a1.jpg" mime_type="image/jpeg" width="540" height="404" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1139" attribute_version="1" attribute_language="eng-US"/><information Height="404" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1139', @@ -11325,7 +11329,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="gallery_large.jpg" suffix="jpg" basename="gallery_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/gallery_large/1143-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/gallery_large/1143-1-eng-US/gallery_large.jpg" original_filename="b1b5ed39.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1143" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="gallery_large.jpg" suffix="jpg" basename="gallery_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/gallery_large/1143-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/gallery_large/1143-1-eng-US/gallery_large.jpg" original_filename="b1b5ed39.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1143" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1143', @@ -11372,7 +11376,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="search_result_large.jpg" suffix="jpg" basename="search_result_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/search_result_large/1147-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/search_result_large/1147-1-eng-US/search_result_large.jpg" original_filename="fb6cf7d3.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1147" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="search_result_large.jpg" suffix="jpg" basename="search_result_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/search_result_large/1147-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/search_result_large/1147-1-eng-US/search_result_large.jpg" original_filename="fb6cf7d3.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1147" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1147', @@ -11419,7 +11423,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="ez_tool_bar_large.jpg" suffix="jpg" basename="ez_tool_bar_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/ez_tool_bar_large/1151-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/ez_tool_bar_large/1151-1-eng-US/ez_tool_bar_large.jpg" original_filename="9c1fd673.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1151" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="ez_tool_bar_large.jpg" suffix="jpg" basename="ez_tool_bar_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/ez_tool_bar_large/1151-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/ez_tool_bar_large/1151-1-eng-US/ez_tool_bar_large.jpg" original_filename="9c1fd673.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1151" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1151', @@ -11466,7 +11470,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="quicktime_large.jpg" suffix="jpg" basename="quicktime_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/quicktime_large/1155-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/quicktime_large/1155-1-eng-US/quicktime_large.jpg" original_filename="af4e1807.jpg" mime_type="image/jpeg" width="540" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1155" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="quicktime_large.jpg" suffix="jpg" basename="quicktime_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/quicktime_large/1155-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/quicktime_large/1155-1-eng-US/quicktime_large.jpg" original_filename="af4e1807.jpg" mime_type="image/jpeg" width="540" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1155" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1155', @@ -11513,7 +11517,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="web_2_0_large.jpg" suffix="jpg" basename="web_2_0_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/web_2_0_large/1159-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/web_2_0_large/1159-1-eng-US/web_2_0_large.jpg" original_filename="cd03d9fc.jpg" mime_type="image/jpeg" width="540" height="425" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1159" attribute_version="1" attribute_language="eng-US"/><information Height="425" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="web_2_0_large.jpg" suffix="jpg" basename="web_2_0_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/web_2_0_large/1159-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/web_2_0_large/1159-1-eng-US/web_2_0_large.jpg" original_filename="cd03d9fc.jpg" mime_type="image/jpeg" width="540" height="425" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1159" attribute_version="1" attribute_language="eng-US"/><information Height="425" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1159', @@ -11544,13 +11548,13 @@ 'contentobject_id' => '202', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'How to manage eZ Publish', + 'data_text' => 'How to manage Ibexa', 'data_type_string' => 'ezstring', 'id' => '1161', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'how to manage ez publish', + 'sort_key_string' => 'how to manage ibexa', 'version' => '1', ], 785 => [ @@ -11607,7 +11611,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="How-to-manage-eZ-Publish.jpg" suffix="jpg" basename="How-to-manage-eZ-Publish" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/1167-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/1167-1-eng-US/How-to-manage-eZ-Publish.jpg" original_filename="4da87751.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1167" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="How-to-manage-Ibexa.jpg" suffix="jpg" basename="How-to-manage-Ibexa" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/1167-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/1167-1-eng-US/How-to-manage-Ibexa.jpg" original_filename="4da87751.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1167" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1167', @@ -11684,7 +11688,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="usermanagement_large.jpg" suffix="jpg" basename="usermanagement_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/usermanagement_large/1175-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/usermanagement_large/1175-1-eng-US/usermanagement_large.jpg" original_filename="c7fdf3e4.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154208"><original attribute_id="1175" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="usermanagement_large.jpg" suffix="jpg" basename="usermanagement_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/usermanagement_large/1175-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/usermanagement_large/1175-1-eng-US/usermanagement_large.jpg" original_filename="c7fdf3e4.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154208"><original attribute_id="1175" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1175', @@ -11731,7 +11735,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="workflow_large.jpg" suffix="jpg" basename="workflow_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/workflow_large/1179-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/workflow_large/1179-1-eng-US/workflow_large.jpg" original_filename="d4a5a15d.jpg" mime_type="image/jpeg" width="540" height="414" alternative_text="" alias_key="1293033771" timestamp="1311154208"><original attribute_id="1179" attribute_version="1" attribute_language="eng-US"/><information Height="414" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="workflow_large.jpg" suffix="jpg" basename="workflow_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/workflow_large/1179-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/workflow_large/1179-1-eng-US/workflow_large.jpg" original_filename="d4a5a15d.jpg" mime_type="image/jpeg" width="540" height="414" alternative_text="" alias_key="1293033771" timestamp="1311154208"><original attribute_id="1179" attribute_version="1" attribute_language="eng-US"/><information Height="414" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1179', @@ -11778,7 +11782,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="versioning_large.jpg" suffix="jpg" basename="versioning_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/versioning_large/1183-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/versioning_large/1183-1-eng-US/versioning_large.jpg" original_filename="5041e7aa.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154208"><original attribute_id="1183" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="versioning_large.jpg" suffix="jpg" basename="versioning_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/versioning_large/1183-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/versioning_large/1183-1-eng-US/versioning_large.jpg" original_filename="5041e7aa.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154208"><original attribute_id="1183" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1183', @@ -11825,7 +11829,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="datatypes_attributes_content_class_and_objects_large.png" suffix="png" basename="datatypes_attributes_content_class_and_objects_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/datatypes_attributes_content_class_and_objects_large/1187-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/datatypes_attributes_content_class_and_objects_large/1187-1-eng-US/datatypes_attributes_content_class_and_objects_large.png" original_filename="3ac3d302.png" mime_type="image/png" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1187" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="datatypes_attributes_content_class_and_objects_large.png" suffix="png" basename="datatypes_attributes_content_class_and_objects_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/datatypes_attributes_content_class_and_objects_large/1187-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/datatypes_attributes_content_class_and_objects_large/1187-1-eng-US/datatypes_attributes_content_class_and_objects_large.png" original_filename="3ac3d302.png" mime_type="image/png" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1187" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1187', @@ -11872,7 +11876,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="multiple_location_large.jpg" suffix="jpg" basename="multiple_location_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/multiple_location_large/1191-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/multiple_location_large/1191-1-eng-US/multiple_location_large.jpg" original_filename="d2bd9a18.jpg" mime_type="image/jpeg" width="540" height="405" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1191" attribute_version="1" attribute_language="eng-US"/><information Height="405" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="multiple_location_large.jpg" suffix="jpg" basename="multiple_location_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/multiple_location_large/1191-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/multiple_location_large/1191-1-eng-US/multiple_location_large.jpg" original_filename="d2bd9a18.jpg" mime_type="image/jpeg" width="540" height="405" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1191" attribute_version="1" attribute_language="eng-US"/><information Height="405" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1191', @@ -11919,7 +11923,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="multisite_deployment_large.jpg" suffix="jpg" basename="multisite_deployment_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/multisite_deployment_large/1195-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/multisite_deployment_large/1195-1-eng-US/multisite_deployment_large.jpg" original_filename="fa5f0924.jpg" mime_type="image/jpeg" width="500" height="423" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1195" attribute_version="1" attribute_language="eng-US"/><information Height="423" Width="500" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="multisite_deployment_large.jpg" suffix="jpg" basename="multisite_deployment_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/multisite_deployment_large/1195-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/multisite_deployment_large/1195-1-eng-US/multisite_deployment_large.jpg" original_filename="fa5f0924.jpg" mime_type="image/jpeg" width="500" height="423" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1195" attribute_version="1" attribute_language="eng-US"/><information Height="423" Width="500" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1195', @@ -11966,7 +11970,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="seo_large.jpg" suffix="jpg" basename="seo_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/seo_large/1199-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/seo_large/1199-1-eng-US/seo_large.jpg" original_filename="25149bcc.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1199" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="seo_large.jpg" suffix="jpg" basename="seo_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/seo_large/1199-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/seo_large/1199-1-eng-US/seo_large.jpg" original_filename="25149bcc.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1199" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1199', @@ -12013,7 +12017,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="admin_site_large.jpg" suffix="jpg" basename="admin_site_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/admin_site_large/1203-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/admin_site_large/1203-1-eng-US/admin_site_large.jpg" original_filename="57b1a75c.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154210"><original attribute_id="1203" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="admin_site_large.jpg" suffix="jpg" basename="admin_site_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/admin_site_large/1203-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/admin_site_large/1203-1-eng-US/admin_site_large.jpg" original_filename="57b1a75c.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154210"><original attribute_id="1203" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1203', @@ -12044,13 +12048,13 @@ 'contentobject_id' => '211', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'How to develop with eZ Publish', + 'data_text' => 'How to develop with Ibexa', 'data_type_string' => 'ezstring', 'id' => '1205', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'how to develop with ez publish', + 'sort_key_string' => 'how to develop with ibexa', 'version' => '1', ], 818 => [ @@ -12107,7 +12111,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="How-to-develop-with-eZ-Publish.png" suffix="png" basename="How-to-develop-with-eZ-Publish" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ez-publish/1211-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ez-publish/1211-1-eng-US/How-to-develop-with-eZ-Publish.png" original_filename="33e36be8.png" mime_type="image/png" width="500" height="409" alternative_text="Content class" alias_key="1293033771" timestamp="1311154210"><original attribute_id="1211" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="How-to-develop-with-Ibexa.png" suffix="png" basename="How-to-develop-with-Ibexa" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ibexa-dxp/1211-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ibexa-dxp/1211-1-eng-US/How-to-develop-with-Ibexa.png" original_filename="33e36be8.png" mime_type="image/png" width="500" height="409" alternative_text="Content class" alias_key="1293033771" timestamp="1311154210"><original attribute_id="1211" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1211', @@ -12184,7 +12188,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="datatypes_attributes_content_class_and_objects_large.jpg" suffix="jpg" basename="datatypes_attributes_content_class_and_objects_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ez-publish/datatypes_attributes_content_class_and_objects_large/1219-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ez-publish/datatypes_attributes_content_class_and_objects_large/1219-1-eng-US/datatypes_attributes_content_class_and_objects_large.jpg" original_filename="e252a562.jpg" mime_type="image/jpeg" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154210"><original attribute_id="1219" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="500" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="datatypes_attributes_content_class_and_objects_large.jpg" suffix="jpg" basename="datatypes_attributes_content_class_and_objects_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ibexa-dxp/datatypes_attributes_content_class_and_objects_large/1219-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ibexa-dxp/datatypes_attributes_content_class_and_objects_large/1219-1-eng-US/datatypes_attributes_content_class_and_objects_large.jpg" original_filename="e252a562.jpg" mime_type="image/jpeg" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154210"><original attribute_id="1219" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="500" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1219', @@ -12231,7 +12235,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="site_style_large.png" suffix="png" basename="site_style_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ez-publish/site_style_large/1223-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ez-publish/site_style_large/1223-1-eng-US/site_style_large.png" original_filename="b3b89834.png" mime_type="image/png" width="540" height="411" alternative_text="" alias_key="1293033771" timestamp="1311154211"><original attribute_id="1223" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="site_style_large.png" suffix="png" basename="site_style_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ibexa-dxp/site_style_large/1223-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ibexa-dxp/site_style_large/1223-1-eng-US/site_style_large.png" original_filename="b3b89834.png" mime_type="image/png" width="540" height="411" alternative_text="" alias_key="1293033771" timestamp="1311154211"><original attribute_id="1223" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1223', @@ -12293,7 +12297,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '1227', @@ -12417,7 +12421,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '1239', @@ -12541,7 +12545,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '1251', @@ -12665,7 +12669,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '1263', @@ -12789,7 +12793,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '1275', @@ -13398,7 +13402,7 @@ 'contentobject_id' => '54', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'Copyright © 2011 <a href="http://ez.no" title="eZ Systems">eZ Systems AS</a> (except where otherwise noted). All rights reserved.', + 'data_text' => 'Copyright © 2011 <a href="http://ibexa.co" title="Ibexa">Ibexa AS</a> (except where otherwise noted). All rights reserved.', 'data_type_string' => 'eztext', 'id' => '1327', 'language_code' => 'eng-US', @@ -13534,7 +13538,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="kn@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="kn@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '1334', @@ -13551,7 +13555,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="kn@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="kn@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '1334', @@ -16341,7 +16345,7 @@ 'initial_language_id' => '2', 'language_mask' => '3', 'modified' => '1072180330', - 'name' => 'Anonymous Users', + 'name' => 'Anonymous users', 'owner_id' => '14', 'published' => '1072180330', 'remote_id' => '15b256dbea2ae72418ff5facc999e8f9', @@ -17167,7 +17171,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1299782140', - 'name' => 'How to use eZ Publish', + 'name' => 'How to use Ibexa', 'owner_id' => '14', 'published' => '1299772464', 'remote_id' => '2ded978c269bba85013b86c2964009be', @@ -17307,7 +17311,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1299772545', - 'name' => 'How to manage eZ Publish', + 'name' => 'How to manage Ibexa', 'owner_id' => '14', 'published' => '1299772545', 'remote_id' => '90ee91c224c69f28aa775cf9edc34ef3', @@ -17433,7 +17437,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1299782177', - 'name' => 'How to develop with eZ Publish', + 'name' => 'How to develop with Ibexa', 'owner_id' => '14', 'published' => '1299772637', 'remote_id' => '564f94afa87f70227199e951450b19a4', @@ -17475,7 +17479,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1284398357', - 'name' => 'Adding Siteaccesses in eZ Publish', + 'name' => 'Adding Siteaccesses in Ibexa', 'owner_id' => '14', 'published' => '1284398357', 'remote_id' => 'e02b80d7cd9059cf469aafbe7d2d1969', @@ -17769,7 +17773,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1299780653', - 'name' => 'eZ Publish Enterprise', + 'name' => 'Ibexa Enterprise', 'owner_id' => '14', 'published' => '1299780355', 'remote_id' => 'bea2aa5ed17c617531344b6c71aff596', @@ -18133,7 +18137,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1299773210', - 'name' => 'Ivo on eZ Tags', + 'name' => 'Ivo on ibexa tags', 'owner_id' => '14', 'published' => '1299772902', 'remote_id' => '32709d6df489b5debc03caddec908363', @@ -18329,7 +18333,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1193935923', - 'name' => 'Separate content & design in eZ Publish', + 'name' => 'Separate content & design in Ibexa', 'owner_id' => '14', 'published' => '1193907125', 'remote_id' => '932072228b62943d3d3521e7712b54e9', @@ -18357,7 +18361,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1193914552', - 'name' => 'How to use eZ Publish', + 'name' => 'How to use Ibexa', 'owner_id' => '14', 'published' => '1193908304', 'remote_id' => 'afad8dfca54311e15bfd241ef09da280', @@ -18483,7 +18487,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1299782207', - 'name' => 'How to manage eZ Publish', + 'name' => 'How to manage Ibexa', 'owner_id' => '14', 'published' => '1193908398', 'remote_id' => '81d949fde405dbe3e5bc6d83452bd927', @@ -18609,7 +18613,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1193914399', - 'name' => 'How to develop with eZ Publish', + 'name' => 'How to develop with Ibexa', 'owner_id' => '14', 'published' => '1193908946', 'remote_id' => '96a6b67d897f8dda1053f2a56a734941', @@ -20045,7 +20049,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '110', 'parent_node_id' => '107', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish', + 'path_identification_string' => 'getting_started/how_to_use_ibexa', 'path_string' => '/1/2/107/110/', 'priority' => '0', 'remote_id' => '6b3bcab0f149c5acc2e3728df7c66b73', @@ -20063,7 +20067,7 @@ 'modified_subnode' => '1311154186', 'node_id' => '111', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics1', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics1', 'path_string' => '/1/2/107/110/111/', 'priority' => '0', 'remote_id' => '28f9dfe5c0680955eec7a2dec4ebc642', @@ -20081,7 +20085,7 @@ 'modified_subnode' => '1311154186', 'node_id' => '112', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics12', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics12', 'path_string' => '/1/2/107/110/112/', 'priority' => '0', 'remote_id' => '191faba79dc108a19893944befa50d94', @@ -20099,7 +20103,7 @@ 'modified_subnode' => '1311154186', 'node_id' => '113', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics13', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics13', 'path_string' => '/1/2/107/110/113/', 'priority' => '0', 'remote_id' => '8679b5fa3ad0ec216293419ab9834e44', @@ -20117,7 +20121,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '114', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics14', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics14', 'path_string' => '/1/2/107/110/114/', 'priority' => '0', 'remote_id' => '7f32cdefd0cf55b966a44aa52181e30d', @@ -20135,7 +20139,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '115', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics15', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics15', 'path_string' => '/1/2/107/110/115/', 'priority' => '0', 'remote_id' => '29db3c1fcdb497e5cb2c74eb85c0906a', @@ -20153,7 +20157,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '116', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics16', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics16', 'path_string' => '/1/2/107/110/116/', 'priority' => '0', 'remote_id' => '96e30009f712a0315217fed93eba6a18', @@ -20171,7 +20175,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '117', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics17', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics17', 'path_string' => '/1/2/107/110/117/', 'priority' => '0', 'remote_id' => '3a6b87470a25b19a6ad15caa5e24e719', @@ -20189,7 +20193,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '118', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics18', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics18', 'path_string' => '/1/2/107/110/118/', 'priority' => '0', 'remote_id' => 'd0bc77a21920b63543d7b0accab81b24', @@ -20207,7 +20211,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '119', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics19', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics19', 'path_string' => '/1/2/107/110/119/', 'priority' => '0', 'remote_id' => 'c75b4b4b870e0e3611e19e1323103282', @@ -20225,7 +20229,7 @@ 'modified_subnode' => '1311154189', 'node_id' => '120', 'parent_node_id' => '107', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa', 'path_string' => '/1/2/107/120/', 'priority' => '0', 'remote_id' => '262d8c936d3757ff81e7bb49392b703f', @@ -20243,7 +20247,7 @@ 'modified_subnode' => '1311154188', 'node_id' => '121', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics1', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics1', 'path_string' => '/1/2/107/120/121/', 'priority' => '0', 'remote_id' => '220d4e10bf4619525c3165823079482c', @@ -20261,7 +20265,7 @@ 'modified_subnode' => '1311154188', 'node_id' => '122', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics12', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics12', 'path_string' => '/1/2/107/120/122/', 'priority' => '0', 'remote_id' => 'f13a938ce2c79a9d438548299220d6dd', @@ -20279,7 +20283,7 @@ 'modified_subnode' => '1311154188', 'node_id' => '123', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics13', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics13', 'path_string' => '/1/2/107/120/123/', 'priority' => '0', 'remote_id' => '04f5e08293954b1851a4dd1cbd976cff', @@ -20297,7 +20301,7 @@ 'modified_subnode' => '1311154188', 'node_id' => '124', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics14', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics14', 'path_string' => '/1/2/107/120/124/', 'priority' => '0', 'remote_id' => 'dc80ae3d9de55855a16218b032062c62', @@ -20315,7 +20319,7 @@ 'modified_subnode' => '1311154189', 'node_id' => '125', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics15', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics15', 'path_string' => '/1/2/107/120/125/', 'priority' => '0', 'remote_id' => '4cb6d4015dd80474074043ab28f96e36', @@ -20333,7 +20337,7 @@ 'modified_subnode' => '1311154189', 'node_id' => '126', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics16', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics16', 'path_string' => '/1/2/107/120/126/', 'priority' => '0', 'remote_id' => '74ce7d6cbae81d90592b4a8a242a284a', @@ -20351,7 +20355,7 @@ 'modified_subnode' => '1311154189', 'node_id' => '127', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics17', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics17', 'path_string' => '/1/2/107/120/127/', 'priority' => '0', 'remote_id' => '6f76d2f7e5812acf4f7c5c70258f4f7e', @@ -20369,7 +20373,7 @@ 'modified_subnode' => '1311154189', 'node_id' => '128', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics18', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics18', 'path_string' => '/1/2/107/120/128/', 'priority' => '0', 'remote_id' => '5ba07103968a6bb3f05c275c212440f6', @@ -20387,7 +20391,7 @@ 'modified_subnode' => '1311154190', 'node_id' => '129', 'parent_node_id' => '107', - 'path_identification_string' => 'getting_started/how_to_develop_with_ez_publish', + 'path_identification_string' => 'getting_started/how_to_develop_with_ibexa', 'path_string' => '/1/2/107/129/', 'priority' => '0', 'remote_id' => '3579b6c5cd166d5137eada55274892d3', @@ -20405,7 +20409,7 @@ 'modified_subnode' => '1311154190', 'node_id' => '130', 'parent_node_id' => '129', - 'path_identification_string' => 'getting_started/how_to_develop_with_ez_publish/graphics1', + 'path_identification_string' => 'getting_started/how_to_develop_with_ibexa/graphics1', 'path_string' => '/1/2/107/129/130/', 'priority' => '0', 'remote_id' => 'e38a43ce35832e5af2df4f1e9272b926', @@ -20423,7 +20427,7 @@ 'modified_subnode' => '1311154190', 'node_id' => '131', 'parent_node_id' => '129', - 'path_identification_string' => 'getting_started/how_to_develop_with_ez_publish/graphics12', + 'path_identification_string' => 'getting_started/how_to_develop_with_ibexa/graphics12', 'path_string' => '/1/2/107/129/131/', 'priority' => '0', 'remote_id' => '1ba7889f63e32acf70f66349ce1a8953', @@ -20441,7 +20445,7 @@ 'modified_subnode' => '1311154190', 'node_id' => '132', 'parent_node_id' => '107', - 'path_identification_string' => 'getting_started/adding_siteaccesses_in_ez_publish', + 'path_identification_string' => 'getting_started/adding_siteaccesses_in_ibexa', 'path_string' => '/1/2/107/132/', 'priority' => '0', 'remote_id' => '19ae9fcb7334a0e407b2781920247122', @@ -20819,7 +20823,7 @@ 'modified_subnode' => '1311154196', 'node_id' => '153', 'parent_node_id' => '2', - 'path_identification_string' => 'ez_publish_enterprise', + 'path_identification_string' => 'ibexa_enterprise', 'path_string' => '/1/2/153/', 'priority' => '0', 'remote_id' => 'f4bf2e6c1cf75e15b15f0123a82778a1', @@ -20837,7 +20841,7 @@ 'modified_subnode' => '1311154196', 'node_id' => '154', 'parent_node_id' => '153', - 'path_identification_string' => 'ez_publish_enterprise/graphics1', + 'path_identification_string' => 'ibexa_enterprise/graphics1', 'path_string' => '/1/2/153/154/', 'priority' => '0', 'remote_id' => '1b95356b68bd59ef32b0fd7bb2c29130', @@ -20855,7 +20859,7 @@ 'modified_subnode' => '1311154196', 'node_id' => '155', 'parent_node_id' => '153', - 'path_identification_string' => 'ez_publish_enterprise/graphics12', + 'path_identification_string' => 'ibexa_enterprise/graphics12', 'path_string' => '/1/2/153/155/', 'priority' => '0', 'remote_id' => '94b7059eb5e0fe667f7c63eadd53a62b', @@ -21287,7 +21291,7 @@ 'modified_subnode' => '1311154202', 'node_id' => '179', 'parent_node_id' => '178', - 'path_identification_string' => 'community/pictures/ivo_on_ez_tags', + 'path_identification_string' => 'community/pictures/ivo_on_ibexa_tags', 'path_string' => '/1/2/167/178/179/', 'priority' => '0', 'remote_id' => 'c6c32c884db955ea09338c90eee84746', @@ -21539,7 +21543,7 @@ 'modified_subnode' => '1311154205', 'node_id' => '193', 'parent_node_id' => '192', - 'path_identification_string' => 'company/new_features/separate_content_design_in_ez_publish', + 'path_identification_string' => 'company/new_features/separate_content_design_in_ibexa', 'path_string' => '/1/2/190/192/193/', 'priority' => '0', 'remote_id' => '10d57795c986ca61689e6c2c11382eb3', @@ -21557,7 +21561,7 @@ 'modified_subnode' => '1311154205', 'node_id' => '194', 'parent_node_id' => '193', - 'path_identification_string' => 'company/new_features/separate_content_design_in_ez_publish/separation_of_content_and_design', + 'path_identification_string' => 'company/new_features/separate_content_design_in_ibexa/separation_of_content_and_design', 'path_string' => '/1/2/190/192/193/194/', 'priority' => '0', 'remote_id' => '87417129b080d24d335eb2653dd1a40c', @@ -21575,7 +21579,7 @@ 'modified_subnode' => '1311154207', 'node_id' => '195', 'parent_node_id' => '192', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa', 'path_string' => '/1/2/190/192/195/', 'priority' => '0', 'remote_id' => 'fd56d839df6e2b52055d4a62f4df7c3e', @@ -21593,7 +21597,7 @@ 'modified_subnode' => '1311154206', 'node_id' => '196', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/openoffice_import_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/openoffice_import_large', 'path_string' => '/1/2/190/192/195/196/', 'priority' => '0', 'remote_id' => 'ab2f2093f16c56ac2e61f40f25d2dbe0', @@ -21611,7 +21615,7 @@ 'modified_subnode' => '1311154206', 'node_id' => '197', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/translation_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/translation_large', 'path_string' => '/1/2/190/192/195/197/', 'priority' => '0', 'remote_id' => '185a1737e9012e7c3025df876bbb4f9b', @@ -21629,7 +21633,7 @@ 'modified_subnode' => '1311154206', 'node_id' => '198', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/multiupload_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/multiupload_large', 'path_string' => '/1/2/190/192/195/198/', 'priority' => '0', 'remote_id' => 'efdca2f6ddd2c8befc283ae3465b9acd', @@ -21647,7 +21651,7 @@ 'modified_subnode' => '1311154207', 'node_id' => '199', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/gallery_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/gallery_large', 'path_string' => '/1/2/190/192/195/199/', 'priority' => '0', 'remote_id' => 'f81b0e4c31ea13d94b9838e9e7e7ad7e', @@ -21665,7 +21669,7 @@ 'modified_subnode' => '1311154207', 'node_id' => '200', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/search_result_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/search_result_large', 'path_string' => '/1/2/190/192/195/200/', 'priority' => '0', 'remote_id' => '666efc49c267cddf55ca707408f4ac42', @@ -21683,7 +21687,7 @@ 'modified_subnode' => '1311154207', 'node_id' => '201', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/ez_tool_bar_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/ez_tool_bar_large', 'path_string' => '/1/2/190/192/195/201/', 'priority' => '0', 'remote_id' => 'afb51a5f898c549c9e1f35afc75d66f3', @@ -21701,7 +21705,7 @@ 'modified_subnode' => '1311154207', 'node_id' => '202', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/quicktime_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/quicktime_large', 'path_string' => '/1/2/190/192/195/202/', 'priority' => '0', 'remote_id' => '94b71866ede0d2776c140e85a823d31d', @@ -21719,7 +21723,7 @@ 'modified_subnode' => '1311154207', 'node_id' => '203', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/web_2_0_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/web_2_0_large', 'path_string' => '/1/2/190/192/195/203/', 'priority' => '0', 'remote_id' => 'e74475a240fb7061b40c75f5e6fcd52c', @@ -21737,7 +21741,7 @@ 'modified_subnode' => '1311154210', 'node_id' => '204', 'parent_node_id' => '192', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa', 'path_string' => '/1/2/190/192/204/', 'priority' => '0', 'remote_id' => '42a3b07037390457ad0f2c65494bfd97', @@ -21755,7 +21759,7 @@ 'modified_subnode' => '1311154208', 'node_id' => '205', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/usermanagement_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/usermanagement_large', 'path_string' => '/1/2/190/192/204/205/', 'priority' => '0', 'remote_id' => '6da1a82ba3d962d2ad953fff8ee26362', @@ -21773,7 +21777,7 @@ 'modified_subnode' => '1311154208', 'node_id' => '206', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/workflow_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/workflow_large', 'path_string' => '/1/2/190/192/204/206/', 'priority' => '0', 'remote_id' => 'b284abd88bdcb1887a86e40304ffc66f', @@ -21791,7 +21795,7 @@ 'modified_subnode' => '1311154209', 'node_id' => '207', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/versioning_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/versioning_large', 'path_string' => '/1/2/190/192/204/207/', 'priority' => '0', 'remote_id' => 'e3400bb44aae5fcb416156e7450f3f32', @@ -21809,7 +21813,7 @@ 'modified_subnode' => '1311154209', 'node_id' => '208', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/datatypes_attributes_content_class_and_objects_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/datatypes_attributes_content_class_and_objects_large', 'path_string' => '/1/2/190/192/204/208/', 'priority' => '0', 'remote_id' => '2b2b27bbaaec688aaec1a5d39b4b13ae', @@ -21827,7 +21831,7 @@ 'modified_subnode' => '1311154209', 'node_id' => '209', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/multiple_location_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/multiple_location_large', 'path_string' => '/1/2/190/192/204/209/', 'priority' => '0', 'remote_id' => '37e5feed6574d1d8da4b4531569a27a2', @@ -21845,7 +21849,7 @@ 'modified_subnode' => '1311154209', 'node_id' => '210', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/multisite_deployment_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/multisite_deployment_large', 'path_string' => '/1/2/190/192/204/210/', 'priority' => '0', 'remote_id' => '5625695a66034f53b9b6e14029e06e80', @@ -21863,7 +21867,7 @@ 'modified_subnode' => '1311154210', 'node_id' => '211', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/seo_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/seo_large', 'path_string' => '/1/2/190/192/204/211/', 'priority' => '0', 'remote_id' => '824aba4122ee51d9638847e55642feec', @@ -21881,7 +21885,7 @@ 'modified_subnode' => '1311154210', 'node_id' => '212', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/admin_site_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/admin_site_large', 'path_string' => '/1/2/190/192/204/212/', 'priority' => '0', 'remote_id' => '73e95051b800ce051c3851c60807787b', @@ -21899,7 +21903,7 @@ 'modified_subnode' => '1311154211', 'node_id' => '213', 'parent_node_id' => '192', - 'path_identification_string' => 'company/new_features/how_to_develop_with_ez_publish', + 'path_identification_string' => 'company/new_features/how_to_develop_with_ibexa', 'path_string' => '/1/2/190/192/213/', 'priority' => '0', 'remote_id' => '3f921d3c490a6c17870b1f01fcfbceed', @@ -21917,7 +21921,7 @@ 'modified_subnode' => '1311154211', 'node_id' => '214', 'parent_node_id' => '213', - 'path_identification_string' => 'company/new_features/how_to_develop_with_ez_publish/datatypes_attributes_content_class_and_objects_large', + 'path_identification_string' => 'company/new_features/how_to_develop_with_ibexa/datatypes_attributes_content_class_and_objects_large', 'path_string' => '/1/2/190/192/213/214/', 'priority' => '0', 'remote_id' => '97f7b391f1522bf404ff672ac338b91c', @@ -21935,7 +21939,7 @@ 'modified_subnode' => '1311154211', 'node_id' => '215', 'parent_node_id' => '213', - 'path_identification_string' => 'company/new_features/how_to_develop_with_ez_publish/site_style_large', + 'path_identification_string' => 'company/new_features/how_to_develop_with_ibexa/site_style_large', 'path_string' => '/1/2/190/192/213/215/', 'priority' => '0', 'remote_id' => 'c5549bf68962a5384bd2225931cba302', @@ -23305,7 +23309,7 @@ 'content_version' => 1, 'contentobject_id' => 42, 'language_id' => 3, - 'name' => 'Anonymous Users', + 'name' => 'Anonymous users', 'real_translation' => 'eng-US', ], 10 => [ @@ -23777,7 +23781,7 @@ 'content_version' => 1, 'contentobject_id' => 108, 'language_id' => 2, - 'name' => 'How to use eZ Publish', + 'name' => 'How to use Ibexa', 'real_translation' => 'eng-US', ], 69 => [ @@ -23857,7 +23861,7 @@ 'content_version' => 1, 'contentobject_id' => 118, 'language_id' => 2, - 'name' => 'How to manage eZ Publish', + 'name' => 'How to manage Ibexa', 'real_translation' => 'eng-US', ], 79 => [ @@ -23929,7 +23933,7 @@ 'content_version' => 1, 'contentobject_id' => 127, 'language_id' => 2, - 'name' => 'How to develop with eZ Publish', + 'name' => 'How to develop with Ibexa', 'real_translation' => 'eng-US', ], 88 => [ @@ -23953,7 +23957,7 @@ 'content_version' => 1, 'contentobject_id' => 130, 'language_id' => 2, - 'name' => 'Adding Siteaccesses in eZ Publish', + 'name' => 'Adding Siteaccesses in Ibexa', 'real_translation' => 'eng-US', ], 91 => [ @@ -24121,7 +24125,7 @@ 'content_version' => 1, 'contentobject_id' => 151, 'language_id' => 2, - 'name' => 'eZ Publish Enterprise', + 'name' => 'Ibexa Enterprise', 'real_translation' => 'eng-US', ], 112 => [ @@ -24329,7 +24333,7 @@ 'content_version' => 1, 'contentobject_id' => 177, 'language_id' => 2, - 'name' => 'Ivo on eZ Tags', + 'name' => 'Ivo on ibexa tags', 'real_translation' => 'eng-US', ], 138 => [ @@ -24444,7 +24448,7 @@ 'content_version' => 1, 'contentobject_id' => 191, 'language_id' => 2, - 'name' => 'Separate content & design in eZ Publish', + 'name' => 'Separate content & design in Ibexa', 'real_translation' => 'eng-US', ], 152 => [ @@ -24460,7 +24464,7 @@ 'content_version' => 1, 'contentobject_id' => 193, 'language_id' => 2, - 'name' => 'How to use eZ Publish', + 'name' => 'How to use Ibexa', 'real_translation' => 'eng-US', ], 154 => [ @@ -24532,7 +24536,7 @@ 'content_version' => 1, 'contentobject_id' => 202, 'language_id' => 2, - 'name' => 'How to manage eZ Publish', + 'name' => 'How to manage Ibexa', 'real_translation' => 'eng-US', ], 163 => [ @@ -24604,7 +24608,7 @@ 'content_version' => 1, 'contentobject_id' => 211, 'language_id' => 2, - 'name' => 'How to develop with eZ Publish', + 'name' => 'How to develop with Ibexa', 'real_translation' => 'eng-US', ], 172 => [ diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/contentobjects_multilingual.php b/tests/lib/Persistence/Legacy/Content/_fixtures/contentobjects_multilingual.php similarity index 93% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/contentobjects_multilingual.php rename to tests/lib/Persistence/Legacy/Content/_fixtures/contentobjects_multilingual.php index 20ecc100e5..e0c85442e7 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/contentobjects_multilingual.php +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/contentobjects_multilingual.php @@ -1,5 +1,9 @@ <?php +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 'ezcontentobject_attribute' => [ 0 => [ diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_content_from_rows.php b/tests/lib/Persistence/Legacy/Content/_fixtures/extract_content_from_rows.php similarity index 98% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_content_from_rows.php rename to tests/lib/Persistence/Legacy/Content/_fixtures/extract_content_from_rows.php index fefffdb5be..0e05b81b1d 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_content_from_rows.php +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/extract_content_from_rows.php @@ -1,5 +1,9 @@ <?php +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 0 => [ 'ezcontentobject_id' => 226, @@ -98,7 +102,7 @@ 'ezcontentobject_attribute_data_float' => 0.0, 'ezcontentobject_attribute_data_int' => null, 'ezcontentobject_attribute_data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="kn@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="kn@ibexa.co"/></authors></ezauthor> ', 'ezcontentobject_attribute_sort_key_int' => 0, 'ezcontentobject_attribute_sort_key_string' => '', @@ -311,4 +315,4 @@ 'ezcontentobject_tree_main_node_id' => 228, 'ezcontentobject_is_hidden' => 0, ], -]; \ No newline at end of file +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_content_from_rows_multiple_versions.php b/tests/lib/Persistence/Legacy/Content/_fixtures/extract_content_from_rows_multiple_versions.php similarity index 97% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_content_from_rows_multiple_versions.php rename to tests/lib/Persistence/Legacy/Content/_fixtures/extract_content_from_rows_multiple_versions.php index 1627832a0f..7acb59c8fc 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_content_from_rows_multiple_versions.php +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/extract_content_from_rows_multiple_versions.php @@ -1,5 +1,9 @@ <?php +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 0 => [ 'ezcontentobject_id' => '11', diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_content_from_rows_result.php b/tests/lib/Persistence/Legacy/Content/_fixtures/extract_content_from_rows_result.php similarity index 87% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_content_from_rows_result.php rename to tests/lib/Persistence/Legacy/Content/_fixtures/extract_content_from_rows_result.php index 9460576b1c..03f9783faa 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_content_from_rows_result.php +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/extract_content_from_rows_result.php @@ -1,10 +1,14 @@ <?php -use eZ\Publish\SPI\Persistence\Content; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\Field; -use eZ\Publish\SPI\Persistence\Content\FieldValue; -use eZ\Publish\SPI\Persistence\Content\VersionInfo; +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\Field; +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo; $content = new Content(); diff --git a/tests/lib/Persistence/Legacy/Content/_fixtures/extract_names_from_rows.php b/tests/lib/Persistence/Legacy/Content/_fixtures/extract_names_from_rows.php new file mode 100644 index 0000000000..5f2d8de5a2 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/extract_names_from_rows.php @@ -0,0 +1,20 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 0 => [ + 'ezcontentobject_name_contentobject_id' => '226', + 'ezcontentobject_name_content_version' => '2', + 'ezcontentobject_name_name' => 'Something', + 'ezcontentobject_name_content_translation' => 'eng-GB', + ], + 1 => [ + 'ezcontentobject_name_contentobject_id' => '226', + 'ezcontentobject_name_content_version' => '2', + 'ezcontentobject_name_name' => 'Something', + 'ezcontentobject_name_content_translation' => 'eng-US', + ], +]; diff --git a/tests/lib/Persistence/Legacy/Content/_fixtures/extract_names_from_rows_multiple_versions.php b/tests/lib/Persistence/Legacy/Content/_fixtures/extract_names_from_rows_multiple_versions.php new file mode 100644 index 0000000000..9e6d0f2e2d --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/extract_names_from_rows_multiple_versions.php @@ -0,0 +1,20 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 0 => [ + 'ezcontentobject_name_contentobject_id' => '11', + 'ezcontentobject_name_content_version' => '1', + 'ezcontentobject_name_name' => 'Guest accounts', + 'ezcontentobject_name_content_translation' => 'eng-US', + ], + 1 => [ + 'ezcontentobject_name_contentobject_id' => '11', + 'ezcontentobject_name_content_version' => '2', + 'ezcontentobject_name_name' => 'Members', + 'ezcontentobject_name_content_translation' => 'eng-US', + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_version_info_from_rows_multiple_versions.php b/tests/lib/Persistence/Legacy/Content/_fixtures/extract_version_info_from_rows_multiple_versions.php similarity index 92% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_version_info_from_rows_multiple_versions.php rename to tests/lib/Persistence/Legacy/Content/_fixtures/extract_version_info_from_rows_multiple_versions.php index 8df69b1c61..bfe811f9ae 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/extract_version_info_from_rows_multiple_versions.php +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/extract_version_info_from_rows_multiple_versions.php @@ -1,5 +1,9 @@ <?php +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 0 => [ 'ezcontentobject_version_id' => '439', diff --git a/tests/lib/Persistence/Legacy/Content/_fixtures/languages.php b/tests/lib/Persistence/Legacy/Content/_fixtures/languages.php new file mode 100644 index 0000000000..ff716567a8 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/languages.php @@ -0,0 +1,12 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + 'ezcontent_language' => [ + ['disabled' => 0, 'id' => 2, 'locale' => 'eng-US', 'name' => 'English (American)'], + ['disabled' => 0, 'id' => 4, 'locale' => 'eng-GB', 'name' => 'English (United Kingdom)'], + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/objectstates.php b/tests/lib/Persistence/Legacy/Content/_fixtures/objectstates.php similarity index 99% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/objectstates.php rename to tests/lib/Persistence/Legacy/Content/_fixtures/objectstates.php index 793cc7b378..0d807983bd 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/objectstates.php +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/objectstates.php @@ -1,5 +1,9 @@ <?php +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 'ezcobj_state_group' => [ [ diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/relations_data.php b/tests/lib/Persistence/Legacy/Content/_fixtures/relations_data.php similarity index 94% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/relations_data.php rename to tests/lib/Persistence/Legacy/Content/_fixtures/relations_data.php index 1e3df96b60..f7baa883ca 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/relations_data.php +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/relations_data.php @@ -1,4 +1,9 @@ <?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 'ezcontentobject_link' => [ ['contentclassattribute_id' => '0', 'from_contentobject_id' => '57', 'from_contentobject_version' => '1', 'id' => '2', 'relation_type' => '1', 'to_contentobject_id' => '58'], diff --git a/tests/lib/Persistence/Legacy/Content/_fixtures/relations_results.php b/tests/lib/Persistence/Legacy/Content/_fixtures/relations_results.php new file mode 100644 index 0000000000..6d0ba1b62e --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/relations_results.php @@ -0,0 +1,16 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +use Ibexa\Contracts\Core\Persistence\Content\Relation; + +$relation = new Relation(); +$relation->id = 1; +$relation->sourceContentId = 1; +$relation->sourceContentVersionNo = 1; +$relation->type = 1; +$relation->destinationContentId = 2; + +return [1 => $relation]; diff --git a/tests/lib/Persistence/Legacy/Content/_fixtures/relations_rows.php b/tests/lib/Persistence/Legacy/Content/_fixtures/relations_rows.php new file mode 100644 index 0000000000..c9cc0725a6 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/relations_rows.php @@ -0,0 +1,16 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +return [ + [ + 'ezcontentobject_link_id' => 1, + 'ezcontentobject_link_contentclassattribute_id' => 0, + 'ezcontentobject_link_from_contentobject_id' => 1, + 'ezcontentobject_link_from_contentobject_version' => 1, + 'ezcontentobject_link_relation_type' => 1, + 'ezcontentobject_link_to_contentobject_id' => 2, + ], +]; diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/sections.php b/tests/lib/Persistence/Legacy/Content/_fixtures/sections.php similarity index 88% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/sections.php rename to tests/lib/Persistence/Legacy/Content/_fixtures/sections.php index 17ebcb3c31..9c2ca7a91b 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/Content/_fixtures/sections.php +++ b/tests/lib/Persistence/Legacy/Content/_fixtures/sections.php @@ -1,5 +1,9 @@ <?php +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ return [ 'ezsection' => [ [ diff --git a/tests/lib/Persistence/Legacy/FieldValue/Converter/ImageConverterTest.php b/tests/lib/Persistence/Legacy/FieldValue/Converter/ImageConverterTest.php new file mode 100644 index 0000000000..804176ce0c --- /dev/null +++ b/tests/lib/Persistence/Legacy/FieldValue/Converter/ImageConverterTest.php @@ -0,0 +1,230 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\FieldValue\Converter; + +use Ibexa\Contracts\Core\Persistence\Content\FieldValue; +use Ibexa\Core\IO\IOServiceInterface; +use Ibexa\Core\IO\UrlRedecoratorInterface; +use Ibexa\Core\IO\Values\BinaryFile; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageConverter; +use Ibexa\Core\Persistence\Legacy\Content\StorageFieldValue; +use PHPUnit\Framework\TestCase; +use Symfony\Bridge\PhpUnit\ClockMock; + +final class ImageConverterTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter\ImageConverter */ + private $imageConverter; + + /** @var \Ibexa\Core\IO\UrlRedecoratorInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $urlRedecorator; + + /** @var \Ibexa\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $ioService; + + protected function setUp(): void + { + $this->ioService = $this->createMock(IOServiceInterface::class); + $this->urlRedecorator = $this->createMock(UrlRedecoratorInterface::class); + + $this->imageConverter = new ImageConverter( + $this->ioService, + $this->urlRedecorator + ); + } + + /** + * @dataProvider fieldValueToXmlProvider + */ + public function testToStorageValue(FieldValue $fieldValue, string $expectedXml): void + { + ClockMock::register(ImageConverter::class); + ClockMock::withClockMock(true); + + $time = ClockMock::time(); + $expectedXml = str_replace('{timestampToReplace}', (string)$time, $expectedXml); + + $storageValue = new StorageFieldValue(); + + $pathToImg = __DIR__ . '/../_fixtures/ibexa_fav.png'; + $this + ->urlRedecorator + ->method('redecorateFromSource') + ->willReturn($pathToImg); + + $this->imageConverter->toStorageValue($fieldValue, $storageValue); + + $this->assertEquals( + $expectedXml, + $storageValue->dataText + ); + + ClockMock::withClockMock(false); + } + + public function fieldValueToXmlProvider(): array + { + $pathToImg = __DIR__ . '/../_fixtures/ibexa_fav.png'; + $dir = __DIR__ . '/../_fixtures'; + + return [ + 'with_additional_data' => [ + new FieldValue([ + 'data' => [ + 'width' => 100, + 'height' => 200, + 'alternativeText' => 'test', + 'mime' => 'image/png', + 'fieldId' => 1, + 'uri' => $pathToImg, + 'versionNo' => 1, + 'languageCode' => 'eng-GB', + 'additionalData' => [ + 'focalPointX' => 50, + 'focalPointY' => 100, + 'author' => 'John Smith', + ], + ], + ]), + <<< XML +<?xml version="1.0" encoding="utf-8"?> +<ezimage serial_number="1" is_valid="1" filename="ibexa_fav.png" + suffix="png" basename="ibexa_fav" dirpath="{$dir}" url="{$pathToImg}" + original_filename="ibexa_fav.png" mime_type="image/png" width="100" + height="200" alternative_text="test" alias_key="1293033771" timestamp="{timestampToReplace}"> + <original attribute_id="1" attribute_version="1" attribute_language="eng-GB"/> + <information Height="200" Width="100" IsColor="1"/> + <additional_data><attribute key="focalPointX">50</attribute><attribute key="focalPointY">100</attribute><attribute key="author">John Smith</attribute></additional_data> +</ezimage> +XML, + ], + 'without_additional_data_stored' => [ + new FieldValue([ + 'data' => [ + 'width' => 100, + 'height' => 200, + 'alternativeText' => 'test', + 'mime' => 'image/png', + 'fieldId' => 1, + 'uri' => $pathToImg, + 'versionNo' => 1, + 'languageCode' => 'eng-GB', + ], + ]), + <<< XML +<?xml version="1.0" encoding="utf-8"?> +<ezimage serial_number="1" is_valid="1" filename="ibexa_fav.png" + suffix="png" basename="ibexa_fav" dirpath="{$dir}" url="{$pathToImg}" + original_filename="ibexa_fav.png" mime_type="image/png" width="100" + height="200" alternative_text="test" alias_key="1293033771" timestamp="{timestampToReplace}"> + <original attribute_id="1" attribute_version="1" attribute_language="eng-GB"/> + <information Height="200" Width="100" IsColor="1"/> + <additional_data/> +</ezimage> +XML, + ], + ]; + } + + /** + * @dataProvider xmlToFieldValueProvider + */ + public function testToFieldValue(string $xml, FieldValue $expectedFieldValue): void + { + ClockMock::register(ImageConverter::class); + ClockMock::withClockMock(true); + + $time = ClockMock::time(); + $xml = str_replace('{timestampToReplace}', (string) $time, $xml); + $storageValue = new StorageFieldValue([ + 'dataText' => $xml, + ]); + + $this + ->ioService + ->method('loadBinaryFileByUri') + ->willReturn(new BinaryFile(['id' => 1])); + + $fieldValue = new FieldValue(); + $this->imageConverter->toFieldValue($storageValue, $fieldValue); + + $this->assertEquals( + $expectedFieldValue->data, + $fieldValue->data + ); + + ClockMock::withClockMock(false); + } + + public function xmlToFieldValueProvider(): array + { + $pathToImg = __DIR__ . '/../_fixtures/ibexa_fav.png'; + $dir = __DIR__ . '/../_fixtures'; + + return [ + 'with_additional_data' => [ +<<< XML +<?xml version="1.0" encoding="utf-8"?> +<ezimage serial_number="1" is_valid="1" filename="ibexa_fav.png" + suffix="png" basename="ibexa_fav" dirpath="{$dir}" url="{$pathToImg}" + original_filename="ibexa_fav.png" mime_type="image/png" width="100" + height="200" alternative_text="test" alias_key="1293033771" timestamp="{timestampToReplace}"> + <original attribute_id="1" attribute_version="1" attribute_language="eng-GB"/> + <information Height="200" Width="100" IsColor="1"/> + <additional_data> + <attribute key="focalPointX">50</attribute> + <attribute key="focalPointY">100</attribute> + <attribute key="author">John Smith</attribute> + </additional_data> +</ezimage> +XML, + new FieldValue([ + 'data' => [ + 'width' => '100', + 'height' => '200', + 'alternativeText' => 'test', + 'mime' => 'image/png', + 'id' => 1, + 'fileName' => 'ibexa_fav.png', + 'additionalData' => [ + 'focalPointX' => 50, + 'focalPointY' => 100, + 'author' => 'John Smith', + ], + ], + ]), + ], + 'without_additional_data_stored' => [ +<<< XML +<?xml version="1.0" encoding="utf-8"?> +<ezimage serial_number="1" is_valid="1" filename="ibexa_fav.png" + suffix="png" basename="ibexa_fav" dirpath="{$dir}" url="{$pathToImg}" + original_filename="ibexa_fav.png" mime_type="image/png" width="100" + height="200" alternative_text="test" alias_key="1293033771" timestamp="{timestampToReplace}"> + <original attribute_id="1" attribute_version="1" attribute_language="eng-GB"/> + <information Height="200" Width="100" IsColor="1"/> +</ezimage> +XML, + new FieldValue([ + 'data' => [ + 'width' => '100', + 'height' => '200', + 'alternativeText' => 'test', + 'mime' => 'image/png', + 'id' => 1, + 'fileName' => 'ibexa_fav.png', + 'additionalData' => [], + ], + ]), + ], + ]; + } +} + +class_alias(ImageConverterTest::class, 'eZ\Publish\Core\Persistence\Tests\Legacy\FieldValue\Converter\ImageConverterTest'); diff --git a/eZ/Publish/Core/Persistence/Tests/Legacy/FieldValue/Converter/_fixtures/ibexa_fav.png b/tests/lib/Persistence/Legacy/FieldValue/_fixtures/ibexa_fav.png similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/Legacy/FieldValue/Converter/_fixtures/ibexa_fav.png rename to tests/lib/Persistence/Legacy/FieldValue/_fixtures/ibexa_fav.png diff --git a/tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php b/tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php new file mode 100644 index 0000000000..67b8fcbcd0 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Filter/BaseCriterionVisitorQueryBuilderTestCase.php @@ -0,0 +1,107 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Filter; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Query\Expression\ExpressionBuilder; +use Ibexa\Contracts\Core\Persistence\Filter\Doctrine\FilteringQueryBuilder; +use Ibexa\Contracts\Core\Repository\Values\Filter\FilteringCriterion; +use Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder; +use Ibexa\Core\Persistence\Legacy\Filter\CriterionVisitor; +use PHPUnit\Framework\TestCase; + +abstract class BaseCriterionVisitorQueryBuilderTestCase extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Filter\CriterionVisitor */ + private $criterionVisitor; + + /** + * @return \Ibexa\Contracts\Core\Repository\Values\Filter\CriterionQueryBuilder[] + */ + abstract protected function getCriterionQueryBuilders(): iterable; + + /** + * Data provider for {@see testVisitCriteriaProducesQuery}. + */ + abstract public function getFilteringCriteriaQueryData(): iterable; + + protected function setUp(): void + { + $this->criterionVisitor = new CriterionVisitor([]); + $this->criterionVisitor->setCriterionQueryBuilders( + array_merge( + $this->getBaseCriterionQueryBuilders($this->criterionVisitor), + $this->getCriterionQueryBuilders() + ) + ); + } + + /** + * @dataProvider getFilteringCriteriaQueryData + * + * @covers \Ibexa\Contracts\Core\Repository\Values\Filter\CriterionQueryBuilder::buildQueryConstraint + * @covers \Ibexa\Contracts\Core\Repository\Values\Filter\CriterionQueryBuilder::accepts + * @covers \Ibexa\Core\Persistence\Legacy\Filter\CriterionVisitor::visitCriteria + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function testVisitCriteriaProducesQuery( + FilteringCriterion $criterion, + string $expectedQuery, + array $expectedParameterValues + ): void { + $queryBuilder = $this->getQueryBuilder(); + $actualQuery = $this->criterionVisitor->visitCriteria($queryBuilder, $criterion); + $criterionFQCN = get_class($criterion); + self::assertSame( + $expectedQuery, + $actualQuery, + sprintf( + 'Query Builder for %s Criterion does not produce expected query', + $criterionFQCN + ) + ); + self::assertSame( + $expectedParameterValues, + $queryBuilder->getParameters(), + sprintf( + 'Query Builder for %s Criterion does not bind expected query parameter values', + $criterionFQCN + ) + ); + } + + private function getQueryBuilder(): FilteringQueryBuilder + { + $connectionMock = $this->createMock(Connection::class); + $connectionMock + ->method('getExpressionBuilder') + ->willReturn( + new ExpressionBuilder($connectionMock) + ); + + return new FilteringQueryBuilder($connectionMock); + } + + /** + * Create Query Builders needed for every test case. + * + * @see getCriterionQueryBuilders + */ + private function getBaseCriterionQueryBuilders(CriterionVisitor $criterionVisitor): iterable + { + return [ + new CriterionQueryBuilder\LogicalAndQueryBuilder($criterionVisitor), + new CriterionQueryBuilder\LogicalOrQueryBuilder($criterionVisitor), + new CriterionQueryBuilder\LogicalNotQueryBuilder($criterionVisitor), + ]; + } +} + +class_alias(BaseCriterionVisitorQueryBuilderTestCase::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Filter\BaseCriterionVisitorQueryBuilderTestCase'); diff --git a/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/LanguageCodeQueryBuilderQueryBuilderTest.php b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/LanguageCodeQueryBuilderQueryBuilderTest.php new file mode 100644 index 0000000000..6f32ce7d22 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/LanguageCodeQueryBuilderQueryBuilderTest.php @@ -0,0 +1,41 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\LanguageCodeQueryBuilder; +use Ibexa\Tests\Core\Persistence\Legacy\Filter\BaseCriterionVisitorQueryBuilderTestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\LanguageCodeQueryBuilder + */ +final class LanguageCodeQueryBuilderQueryBuilderTest extends BaseCriterionVisitorQueryBuilderTestCase +{ + public function getFilteringCriteriaQueryData(): iterable + { + yield 'Language Code IN (eng-GB, eng-US), match always available' => [ + new Criterion\LanguageCode(['eng-GB', 'eng-US']), + '(language.locale IN (:dcValue1)) OR (version.language_mask & 1 = 1)', + ['dcValue1' => ['eng-GB', 'eng-US']], + ]; + + yield 'Language Code=pol-PL, don\'t match always available' => [ + new Criterion\LanguageCode('pol-PL', false), + 'language.locale IN (:dcValue1)', + ['dcValue1' => ['pol-PL']], + ]; + } + + protected function getCriterionQueryBuilders(): iterable + { + return [new LanguageCodeQueryBuilder()]; + } +} + +class_alias(LanguageCodeQueryBuilderQueryBuilderTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Filter\CriterionQueryBuilder\Content\LanguageCodeQueryBuilderQueryBuilderTest'); diff --git a/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/ContentTypeGroupIdQueryBuilderTest.php b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/ContentTypeGroupIdQueryBuilderTest.php index f1bba73033..b4379d12ef 100644 --- a/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/ContentTypeGroupIdQueryBuilderTest.php +++ b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/ContentTypeGroupIdQueryBuilderTest.php @@ -8,25 +8,24 @@ namespace Ibexa\Tests\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeGroupId; -use eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\GroupIdQueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Tests\Filter\BaseCriterionVisitorQueryBuilderTestCase; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ContentTypeGroupId; +use Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\GroupIdQueryBuilder; +use Ibexa\Tests\Core\Persistence\Legacy\Filter\BaseCriterionVisitorQueryBuilderTestCase; /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\GroupIdQueryBuilder::buildQueryConstraint - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\GroupIdQueryBuilder::accepts + * @covers \Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\GroupIdQueryBuilder */ class ContentTypeGroupIdQueryBuilderTest extends BaseCriterionVisitorQueryBuilderTestCase { public function getFilteringCriteriaQueryData(): iterable { - yield 'Content Type Group ID=1' => [ + yield 'Content type group ID=1' => [ new ContentTypeGroupId(1), 'content_type_group.id IN (:dcValue1)', ['dcValue1' => [1]], ]; - yield 'Content Type Group ID IN (1, 2)' => [ + yield 'Content type group ID IN (1, 2)' => [ new ContentTypeGroupId([1, 2]), 'content_type_group.id IN (:dcValue1)', ['dcValue1' => [1, 2]], diff --git a/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/ContentTypeQueryBuildersTest.php b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/ContentTypeQueryBuildersTest.php index 2dce8cca6c..cf0b6d1fe3 100644 --- a/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/ContentTypeQueryBuildersTest.php +++ b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Content/Type/ContentTypeQueryBuildersTest.php @@ -8,34 +8,32 @@ namespace Ibexa\Tests\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\IdentifierQueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\IdQueryBuilder; -use eZ\Publish\Core\Persistence\Legacy\Tests\Filter\BaseCriterionVisitorQueryBuilderTestCase; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\IdentifierQueryBuilder; +use Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\IdQueryBuilder; +use Ibexa\Tests\Core\Persistence\Legacy\Filter\BaseCriterionVisitorQueryBuilderTestCase; /** - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\IdentifierQueryBuilder::buildQueryConstraint - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\IdentifierQueryBuilder::accepts - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\IdQueryBuilder::buildQueryConstraint - * @covers \eZ\Publish\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\IdQueryBuilder::accepts + * @covers \Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\IdentifierQueryBuilder + * @covers \Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\Type\IdQueryBuilder */ final class ContentTypeQueryBuildersTest extends BaseCriterionVisitorQueryBuilderTestCase { public function getFilteringCriteriaQueryData(): iterable { - yield 'Content Type Identifier=article' => [ + yield 'Content type identifier=article' => [ new Criterion\ContentTypeIdentifier('article'), 'content_type.identifier IN (:dcValue1)', ['dcValue1' => ['article']], ]; - yield 'Content Type ID=1' => [ + yield 'Content type ID=1' => [ new Criterion\ContentTypeId(3), 'content_type.id IN (:dcValue1)', ['dcValue1' => [3]], ]; - yield 'Content Type Identifier=folder OR Content Type ID IN (1, 2)' => [ + yield 'Content type identifier=folder OR content type ID IN (1, 2)' => [ new Criterion\LogicalOr( [ new Criterion\ContentTypeIdentifier('folder'), diff --git a/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilderTest.php b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilderTest.php new file mode 100644 index 0000000000..2b29f587fd --- /dev/null +++ b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/AncestorQueryBuilderTest.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Ancestor; +use Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\AncestorQueryBuilder; +use Ibexa\Tests\Core\Persistence\Legacy\Filter\BaseCriterionVisitorQueryBuilderTestCase; + +class AncestorQueryBuilderTest extends BaseCriterionVisitorQueryBuilderTestCase +{ + protected function getCriterionQueryBuilders(): iterable + { + return [new AncestorQueryBuilder()]; + } + + public function getFilteringCriteriaQueryData(): iterable + { + yield 'Ancestor=/1/2/' => [ + new Ancestor('/1/2/'), + 'location.node_id IN (:dcValue1)', + ['dcValue1' => [1, 2]], + ]; + + yield 'Ancestor IN (/1/2/, /1/4/10/' => [ + new Ancestor(['/1/2/', '/1/4/10/']), + 'location.node_id IN (:dcValue1)', + ['dcValue1' => [1, 2, 4, 10]], + ]; + } +} + +class_alias(AncestorQueryBuilderTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Filter\CriterionQueryBuilder\Location\AncestorQueryBuilderTest'); diff --git a/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/LocationIdQueryBuilderTest.php b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/LocationIdQueryBuilderTest.php new file mode 100644 index 0000000000..44e5b42a45 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/LocationIdQueryBuilderTest.php @@ -0,0 +1,46 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\IdQueryBuilder; +use Ibexa\Tests\Core\Persistence\Legacy\Filter\BaseCriterionVisitorQueryBuilderTestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\ParentLocationIdQueryBuilder + */ +final class LocationIdQueryBuilderTest extends BaseCriterionVisitorQueryBuilderTestCase +{ + public function getFilteringCriteriaQueryData(): iterable + { + yield 'Location ID=1' => [ + new Criterion\LocationId(1), + 'location.node_id IN (:dcValue1)', + ['dcValue1' => [1]], + ]; + + yield 'Location ID=1 OR Location ID=2' => [ + new Criterion\LogicalOr( + [ + new Criterion\LocationId(1), + new Criterion\LocationId(2), + ] + ), + '(location.node_id IN (:dcValue1)) OR (location.node_id IN (:dcValue2))', + ['dcValue1' => [1], 'dcValue2' => [2]], + ]; + } + + protected function getCriterionQueryBuilders(): iterable + { + return [new IdQueryBuilder()]; + } +} + +class_alias(LocationIdQueryBuilderTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Filter\CriterionQueryBuilder\Location\LocationIdQueryBuilderTest'); diff --git a/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/ParentLocationQueryBuilderTest.php b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/ParentLocationQueryBuilderTest.php new file mode 100644 index 0000000000..e5f615cb9e --- /dev/null +++ b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/Location/ParentLocationQueryBuilderTest.php @@ -0,0 +1,46 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\ParentLocationIdQueryBuilder; +use Ibexa\Tests\Core\Persistence\Legacy\Filter\BaseCriterionVisitorQueryBuilderTestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\ParentLocationIdQueryBuilder + */ +final class ParentLocationQueryBuilderTest extends BaseCriterionVisitorQueryBuilderTestCase +{ + public function getFilteringCriteriaQueryData(): iterable + { + yield 'Parent Location ID=1' => [ + new Criterion\ParentLocationId(1), + 'location.parent_node_id IN (:dcValue1)', + ['dcValue1' => [1]], + ]; + + yield 'Parent Location ID=1 OR Parent Location ID=2' => [ + new Criterion\LogicalOr( + [ + new Criterion\ParentLocationId(1), + new Criterion\ParentLocationId(2), + ] + ), + '(location.parent_node_id IN (:dcValue1)) OR (location.parent_node_id IN (:dcValue2))', + ['dcValue1' => [1], 'dcValue2' => [2]], + ]; + } + + protected function getCriterionQueryBuilders(): iterable + { + return [new ParentLocationIdQueryBuilder()]; + } +} + +class_alias(ParentLocationQueryBuilderTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Filter\CriterionQueryBuilder\Location\ParentLocationQueryBuilderTest'); diff --git a/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalOperatorQueryBuilderQueryBuilderTest.php b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalOperatorQueryBuilderQueryBuilderTest.php new file mode 100644 index 0000000000..f1de4942f4 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Filter/CriterionQueryBuilder/LogicalOperatorQueryBuilderQueryBuilderTest.php @@ -0,0 +1,77 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Filter\CriterionQueryBuilder; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\ContentIdQueryBuilder; +use Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Content\LanguageCodeQueryBuilder; +use Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\Location\ParentLocationIdQueryBuilder; +use Ibexa\Tests\Core\Persistence\Legacy\Filter\BaseCriterionVisitorQueryBuilderTestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalAndQueryBuilder + * @covers \Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalOrQueryBuilder + * @covers \Ibexa\Core\Persistence\Legacy\Filter\CriterionQueryBuilder\LogicalNotQueryBuilder + */ +final class LogicalOperatorQueryBuilderQueryBuilderTest extends BaseCriterionVisitorQueryBuilderTestCase +{ + public function getFilteringCriteriaQueryData(): iterable + { + yield 'Parent Location ID=1 AND Language Code=eng-GB' => [ + new Criterion\LogicalAnd( + [ + new Criterion\ParentLocationId(1), + new Criterion\LanguageCode('eng-GB'), + ] + ), + '(location.parent_node_id IN (:dcValue1)) AND ((language.locale IN (:dcValue2)) OR (version.language_mask & 1 = 1))', + ['dcValue1' => [1], 'dcValue2' => ['eng-GB']], + ]; + + yield 'Language Code=eng-US OR Parent Location ID=2' => [ + new Criterion\LogicalOr( + [ + new Criterion\LanguageCode('eng-GB'), + new Criterion\ParentLocationId(2), + ] + ), + '((language.locale IN (:dcValue1)) OR (version.language_mask & 1 = 1)) OR (location.parent_node_id IN (:dcValue2))', + ['dcValue1' => ['eng-GB'], 'dcValue2' => [2]], + ]; + + yield 'NOT(Content ID=1 OR (Parent Location ID=2 AND Content ID = 1)' => [ + new Criterion\LogicalNot( + new Criterion\LogicalOr( + [ + new Criterion\ContentId(1), + new Criterion\LogicalAnd( + [ + new Criterion\ParentLocationId(2), + new Criterion\ContentId(1), + ] + ), + ] + ) + ), + 'NOT ((content.id IN (:dcValue1)) OR ((location.parent_node_id IN (:dcValue2)) AND (content.id IN (:dcValue3))))', + ['dcValue1' => [1], 'dcValue2' => [2], 'dcValue3' => [1]], + ]; + } + + protected function getCriterionQueryBuilders(): iterable + { + return [ + new ParentLocationIdQueryBuilder(), + new LanguageCodeQueryBuilder(), + new ContentIdQueryBuilder(), + ]; + } +} + +class_alias(LogicalOperatorQueryBuilderQueryBuilderTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Filter\CriterionQueryBuilder\LogicalOperatorQueryBuilderQueryBuilderTest'); diff --git a/tests/lib/Persistence/Legacy/HandlerTest.php b/tests/lib/Persistence/Legacy/HandlerTest.php new file mode 100644 index 0000000000..3000d07044 --- /dev/null +++ b/tests/lib/Persistence/Legacy/HandlerTest.php @@ -0,0 +1,275 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy; + +use Ibexa\Contracts\Core\Container; +use Ibexa\Contracts\Core\Persistence\Content\Handler as SPIContentHandler; +use Ibexa\Contracts\Core\Persistence\Content\Language\Handler as SPILanguageHandler; +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as SPILocationHandler; +use Ibexa\Contracts\Core\Persistence\Content\Section\Handler as SPISectionHandler; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as SPIContentTypeHandler; +use Ibexa\Contracts\Core\Persistence\Content\UrlAlias\Handler as SPIUrlAliasHandler; +use Ibexa\Contracts\Core\Persistence\TransactionHandler as SPITransactionHandler; +use Ibexa\Contracts\Core\Persistence\User\Handler as SPIUserHandler; +use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; +use Ibexa\Core\Base\ServiceContainer; +use Ibexa\Core\Persistence\Legacy\Content\Handler as ContentHandler; +use Ibexa\Core\Persistence\Legacy\Content\Location\Handler as LocationHandler; +use Ibexa\Core\Persistence\Legacy\Content\Section\Handler as SectionHandler; +use Ibexa\Core\Persistence\Legacy\Content\UrlAlias\Handler as UrlAliasHandler; +use Ibexa\Core\Persistence\Legacy\Handler; +use Ibexa\Core\Persistence\Legacy\TransactionHandler; +use Ibexa\Core\Persistence\Legacy\User\Handler as UserHandler; +use Ibexa\Tests\Integration\Core\LegacyTestContainerBuilder; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Handler::contentHandler + */ +class HandlerTest extends TestCase +{ + public function testContentHandler(): void + { + $handler = $this->getHandlerFixture(); + $contentHandler = $handler->contentHandler(); + + $this->assertInstanceOf( + SPIContentHandler::class, + $contentHandler + ); + $this->assertInstanceOf( + ContentHandler::class, + $contentHandler + ); + } + + public function testContentHandlerTwice(): void + { + $handler = $this->getHandlerFixture(); + + $this->assertSame( + $handler->contentHandler(), + $handler->contentHandler() + ); + } + + public function testContentTypeHandler(): void + { + $handler = $this->getHandlerFixture(); + $contentTypeHandler = $handler->contentTypeHandler(); + + $this->assertInstanceOf( + SPIContentTypeHandler::class, + $contentTypeHandler + ); + } + + public function testContentLanguageHandler(): void + { + $handler = $this->getHandlerFixture(); + $contentLanguageHandler = $handler->contentLanguageHandler(); + + $this->assertInstanceOf( + SPILanguageHandler::class, + $contentLanguageHandler + ); + } + + public function testContentTypeHandlerTwice(): void + { + $handler = $this->getHandlerFixture(); + + $this->assertSame( + $handler->contentTypeHandler(), + $handler->contentTypeHandler() + ); + } + + public function testLocationHandler(): void + { + $handler = $this->getHandlerFixture(); + $locationHandler = $handler->locationHandler(); + + $this->assertInstanceOf( + SPILocationHandler::class, + $locationHandler + ); + $this->assertInstanceOf( + LocationHandler::class, + $locationHandler + ); + } + + public function testLocationHandlerTwice(): void + { + $handler = $this->getHandlerFixture(); + + $this->assertSame( + $handler->locationHandler(), + $handler->locationHandler() + ); + } + + public function testUserHandler(): void + { + $handler = $this->getHandlerFixture(); + $userHandler = $handler->userHandler(); + + $this->assertInstanceOf( + SPIUserHandler::class, + $userHandler + ); + $this->assertInstanceOf( + UserHandler::class, + $userHandler + ); + } + + public function testUserHandlerTwice(): void + { + $handler = $this->getHandlerFixture(); + + $this->assertSame( + $handler->userHandler(), + $handler->userHandler() + ); + } + + public function testSectionHandler(): void + { + $handler = $this->getHandlerFixture(); + $sectionHandler = $handler->sectionHandler(); + + $this->assertInstanceOf( + SPISectionHandler::class, + $sectionHandler + ); + $this->assertInstanceOf( + SectionHandler::class, + $sectionHandler + ); + } + + public function testSectionHandlerTwice(): void + { + $handler = $this->getHandlerFixture(); + + $this->assertSame( + $handler->sectionHandler(), + $handler->sectionHandler() + ); + } + + public function testUrlAliasHandler(): void + { + $handler = $this->getHandlerFixture(); + $urlAliasHandler = $handler->urlAliasHandler(); + + $this->assertInstanceOf( + SPIUrlAliasHandler::class, + $urlAliasHandler + ); + $this->assertInstanceOf( + UrlAliasHandler::class, + $urlAliasHandler + ); + } + + public function testUrlAliasHandlerTwice(): void + { + $handler = $this->getHandlerFixture(); + + $this->assertSame( + $handler->urlAliasHandler(), + $handler->urlAliasHandler() + ); + } + + public function testNotificationHandlerTwice(): void + { + $handler = $this->getHandlerFixture(); + + $this->assertSame( + $handler->notificationHandler(), + $handler->notificationHandler() + ); + } + + public function testTransactionHandler(): void + { + $handler = $this->getHandlerFixture(); + $transactionHandler = $handler->transactionHandler(); + + $this->assertInstanceOf( + SPITransactionHandler::class, + $transactionHandler + ); + $this->assertInstanceOf( + TransactionHandler::class, + $transactionHandler + ); + } + + public function testTransactionHandlerTwice(): void + { + $handler = $this->getHandlerFixture(); + + $this->assertSame( + $handler->transactionHandler(), + $handler->transactionHandler() + ); + } + + protected static Handler $legacyHandler; + + protected function getHandlerFixture(): Handler + { + if (!isset(self::$legacyHandler)) { + $container = $this->getContainer(); + + self::$legacyHandler = $container->get(Handler::class); + } + + return self::$legacyHandler; + } + + protected static Container $container; + + protected function getContainer(): Container + { + if (!isset(self::$container)) { + $installDir = self::getInstallationDir(); + + $containerBuilder = new LegacyTestContainerBuilder(); + + $loader = $containerBuilder->getCoreLoader(); + $loader->load('search_engines/legacy.yml'); + // tests/integration/Core/Resources/settings/integration_legacy.yml + $loader->load('integration_legacy.yml'); + + $containerBuilder->setParameter( + 'languages', + ['eng-US', 'eng-GB'] + ); + $containerBuilder->setParameter( + 'ibexa.persistence.legacy.dsn', + $this->getDsn() + ); + + self::$container = new ServiceContainer( + $containerBuilder, + $installDir, + Legacy::getCacheDir(), + true, + true + ); + } + + return self::$container; + } +} + +class_alias(HandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\HandlerTest'); diff --git a/tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..369459d921 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Notification/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,177 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Notification\Gateway; + +use Doctrine\DBAL\FetchMode; +use Ibexa\Contracts\Core\Persistence\Notification\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Notification\Notification; +use Ibexa\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase::insert + */ +class DoctrineDatabaseTest extends TestCase +{ + public const EXISTING_NOTIFICATION_ID = 1; + public const EXISTING_NOTIFICATION_DATA = [ + 'id' => 1, + 'owner_id' => 14, + 'is_pending' => 1, + 'type' => 'Workflow:Review', + 'created' => 1529995052, + 'data' => null, + ]; + + protected function setUp(): void + { + parent::setUp(); + + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/notifications.php' + ); + } + + public function testInsert() + { + $id = $this->getGateway()->insert(new CreateStruct([ + 'ownerId' => 14, + 'isPending' => true, + 'type' => 'Workflow:Review', + 'created' => 1529995052, + 'data' => null, + ])); + + $data = $this->loadNotification($id); + + $this->assertEquals([ + 'id' => $id, + 'owner_id' => '14', + 'is_pending' => 1, + 'type' => 'Workflow:Review', + 'created' => '1529995052', + 'data' => 'null', + ], $data); + } + + public function testGetNotificationById() + { + $data = $this->getGateway()->getNotificationById(self::EXISTING_NOTIFICATION_ID); + + $this->assertEquals([ + self::EXISTING_NOTIFICATION_DATA, + ], $data); + } + + public function testUpdateNotification() + { + $notification = new Notification([ + 'id' => self::EXISTING_NOTIFICATION_ID, + 'ownerId' => 14, + 'isPending' => false, + 'type' => 'Workflow:Review', + 'created' => 1529995052, + 'data' => null, + ]); + + $this->getGateway()->updateNotification($notification); + + $this->assertEquals([ + 'id' => (string) self::EXISTING_NOTIFICATION_ID, + 'owner_id' => '14', + 'is_pending' => '0', + 'type' => 'Workflow:Review', + 'created' => '1529995052', + 'data' => null, + ], $this->loadNotification(self::EXISTING_NOTIFICATION_ID)); + } + + public function testCountUserNotifications() + { + $this->assertEquals(5, $this->getGateway()->countUserNotifications( + self::EXISTING_NOTIFICATION_DATA['owner_id'] + )); + } + + public function testCountUserPendingNotifications() + { + $this->assertEquals( + 3, + $this->getGateway()->countUserPendingNotifications( + self::EXISTING_NOTIFICATION_DATA['owner_id'] + ) + ); + } + + public function testLoadUserNotifications() + { + $userId = 14; + $offset = 1; + $limit = 3; + + $results = $this->getGateway()->loadUserNotifications($userId, $offset, $limit); + + $this->assertEquals([ + [ + 'id' => '4', + 'owner_id' => '14', + 'is_pending' => 1, + 'type' => 'Workflow:Review', + 'created' => '1530005852', + 'data' => null, + ], + [ + 'id' => '3', + 'owner_id' => '14', + 'is_pending' => 0, + 'type' => 'Workflow:Reject', + 'created' => '1530002252', + 'data' => null, + ], + [ + 'id' => '2', + 'owner_id' => '14', + 'is_pending' => 0, + 'type' => 'Workflow:Approve', + 'created' => '1529998652', + 'data' => null, + ], + ], $results); + } + + public function testDelete() + { + $this->getGateway()->delete(self::EXISTING_NOTIFICATION_ID); + + $this->assertEmpty($this->loadNotification(self::EXISTING_NOTIFICATION_ID)); + } + + /** + * Return a ready to test DoctrineStorage gateway. + * + * @return \Ibexa\Core\Persistence\Legacy\Notification\Gateway\DoctrineDatabase + */ + protected function getGateway(): DoctrineDatabase + { + return new DoctrineDatabase( + $this->getDatabaseConnection() + ); + } + + private function loadNotification(int $id): array + { + $data = $this->connection + ->executeQuery('SELECT * FROM eznotification WHERE id = :id', ['id' => $id]) + ->fetch(FetchMode::ASSOCIATIVE); + + return is_array($data) ? $data : []; + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Notification\Gateway\DoctrineDatabaseTest'); diff --git a/tests/lib/Persistence/Legacy/Notification/HandlerTest.php b/tests/lib/Persistence/Legacy/Notification/HandlerTest.php new file mode 100644 index 0000000000..3dd8d06225 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Notification/HandlerTest.php @@ -0,0 +1,213 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Notification; + +use Ibexa\Contracts\Core\Persistence\Notification\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Notification\Notification; +use Ibexa\Contracts\Core\Persistence\Notification\UpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Notification\Notification as APINotification; +use Ibexa\Core\Persistence\Legacy\Notification\Gateway; +use Ibexa\Core\Persistence\Legacy\Notification\Handler; +use Ibexa\Core\Persistence\Legacy\Notification\Mapper; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Notification\Handler + */ +class HandlerTest extends TestCase +{ + public const NOTIFICATION_ID = 1; + + /** @var \Ibexa\Core\Persistence\Legacy\Notification\Gateway|\PHPUnit\Framework\MockObject\MockObject */ + private $gateway; + + /** @var \Ibexa\Core\Persistence\Legacy\Notification\Mapper|\PHPUnit\Framework\MockObject\MockObject */ + private $mapper; + + /** @var \Ibexa\Core\Persistence\Legacy\Notification\Handler */ + private $handler; + + protected function setUp(): void + { + $this->gateway = $this->createMock(Gateway::class); + $this->mapper = $this->createMock(Mapper::class); + $this->handler = new Handler($this->gateway, $this->mapper); + } + + public function testCreateNotification() + { + $createStruct = new CreateStruct([ + 'ownerId' => 5, + 'type' => 'TEST', + 'isPending' => true, + 'data' => [], + 'created' => 0, + ]); + + $this->gateway + ->expects($this->once()) + ->method('insert') + ->with($createStruct) + ->willReturn(self::NOTIFICATION_ID); + + $this->mapper + ->expects($this->once()) + ->method('extractNotificationsFromRows') + ->willReturn([new Notification([ + 'id' => self::NOTIFICATION_ID, + ])]); + + $notification = $this->handler->createNotification($createStruct); + + $this->assertEquals($notification->id, self::NOTIFICATION_ID); + } + + public function testCountPendingNotifications() + { + $ownerId = 10; + $expectedCount = 12; + + $this->gateway + ->expects($this->once()) + ->method('countUserPendingNotifications') + ->with($ownerId) + ->willReturn($expectedCount); + + $this->assertEquals($expectedCount, $this->handler->countPendingNotifications($ownerId)); + } + + public function testGetNotificationById() + { + $rows = [ + [ + 'id' => 1, /* ... */ + ], + ]; + + $object = new Notification([ + 'id' => 1, /* ... */ + ]); + + $this->gateway + ->expects($this->once()) + ->method('getNotificationById') + ->with(self::NOTIFICATION_ID) + ->willReturn($rows); + + $this->mapper + ->expects($this->once()) + ->method('extractNotificationsFromRows') + ->with($rows) + ->willReturn([$object]); + + $this->assertEquals($object, $this->handler->getNotificationById(self::NOTIFICATION_ID)); + } + + public function testUpdateNotification() + { + $updateStruct = new UpdateStruct([ + 'isPending' => false, + ]); + + $data = [ + 'id' => self::NOTIFICATION_ID, + 'ownerId' => null, + 'isPending' => true, + 'type' => null, + 'created' => null, + 'data' => [], + ]; + + $apiNotification = new APINotification($data); + $spiNotification = new Notification($data); + + $this->mapper + ->expects($this->once()) + ->method('createNotificationFromUpdateStruct') + ->with($updateStruct) + ->willReturn($spiNotification); + + $this->gateway + ->expects($this->once()) + ->method('updateNotification') + ->with($spiNotification); + + $this->mapper + ->expects($this->once()) + ->method('extractNotificationsFromRows') + ->willReturn([new Notification([ + 'id' => self::NOTIFICATION_ID, + ])]); + + $this->handler->updateNotification($apiNotification, $updateStruct); + } + + public function testCountNotifications() + { + $ownerId = 10; + $expectedCount = 12; + + $this->gateway + ->expects($this->once()) + ->method('countUserNotifications') + ->with($ownerId) + ->willReturn($expectedCount); + + $this->assertEquals($expectedCount, $this->handler->countNotifications($ownerId)); + } + + public function testLoadUserNotifications() + { + $ownerId = 9; + $limit = 5; + $offset = 0; + + $rows = [ + ['id' => 1/* ... */], + ['id' => 2/* ... */], + ['id' => 3/* ... */], + ]; + + $objects = [ + new Notification(['id' => 1/* ... */]), + new Notification(['id' => 2/* ... */]), + new Notification(['id' => 3/* ... */]), + ]; + + $this->gateway + ->expects($this->once()) + ->method('loadUserNotifications') + ->with($ownerId, $offset, $limit) + ->willReturn($rows); + + $this->mapper + ->expects($this->once()) + ->method('extractNotificationsFromRows') + ->with($rows) + ->willReturn($objects); + + $this->assertEquals($objects, $this->handler->loadUserNotifications($ownerId, $offset, $limit)); + } + + public function testDelete() + { + $notification = new APINotification([ + 'id' => self::NOTIFICATION_ID, /* ... */ + ]); + + $this->gateway + ->expects($this->once()) + ->method('delete') + ->with($notification->id); + + $this->handler->delete($notification); + } +} + +class_alias(HandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Notification\HandlerTest'); diff --git a/tests/lib/Persistence/Legacy/Notification/MapperTest.php b/tests/lib/Persistence/Legacy/Notification/MapperTest.php new file mode 100644 index 0000000000..73f7dfd3a4 --- /dev/null +++ b/tests/lib/Persistence/Legacy/Notification/MapperTest.php @@ -0,0 +1,110 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\Notification; + +use Ibexa\Contracts\Core\Persistence\Notification\Notification; +use Ibexa\Contracts\Core\Persistence\Notification\UpdateStruct; +use Ibexa\Core\Persistence\Legacy\Notification\Mapper; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Notification\Mapper + */ +class MapperTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Notification\Mapper */ + private $mapper; + + protected function setUp(): void + { + $this->mapper = new Mapper(); + } + + public function testExtractNotificationsFromRows() + { + $rows = [ + [ + 'id' => 1, + 'owner_id' => 5, + 'type' => 'FOO', + 'created' => 1529913161, + 'is_pending' => 0, + 'data' => null, + ], + [ + 'id' => 1, + 'owner_id' => 5, + 'type' => 'BAR', + 'created' => 1529910161, + 'is_pending' => 1, + 'data' => json_encode([ + 'foo' => 'Foo', + 'bar' => 'Bar', + 'baz' => ['B', 'A', 'Z'], + ]), + ], + ]; + + $objects = [ + new Notification([ + 'id' => 1, + 'ownerId' => 5, + 'type' => 'FOO', + 'created' => 1529913161, + 'isPending' => false, + 'data' => [], + ]), + new Notification([ + 'id' => 1, + 'ownerId' => 5, + 'type' => 'BAR', + 'created' => 1529910161, + 'isPending' => true, + 'data' => [ + 'foo' => 'Foo', + 'bar' => 'Bar', + 'baz' => ['B', 'A', 'Z'], + ], + ]), + ]; + + $this->assertEquals($objects, $this->mapper->extractNotificationsFromRows($rows)); + } + + public function testExtractNotificationsFromRowsThrowsRuntimeException() + { + $this->expectException(\RuntimeException::class); + + $rows = [ + [ + 'id' => 1, + 'owner_id' => 5, + 'type' => 'FOO', + 'created' => 1529913161, + 'is_pending' => false, + 'data' => '{ InvalidJSON }', + ], + ]; + + $this->mapper->extractNotificationsFromRows($rows); + } + + public function testCreateNotificationFromUpdateStruct() + { + $updateStruct = new UpdateStruct([ + 'isPending' => false, + ]); + + $this->assertEquals(new Notification([ + 'isPending' => false, + ]), $this->mapper->createNotificationFromUpdateStruct($updateStruct)); + } +} + +class_alias(MapperTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Notification\MapperTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/Notification/_fixtures/notifications.php b/tests/lib/Persistence/Legacy/Notification/_fixtures/notifications.php similarity index 100% rename from eZ/Publish/Core/Persistence/Legacy/Tests/Notification/_fixtures/notifications.php rename to tests/lib/Persistence/Legacy/Notification/_fixtures/notifications.php diff --git a/tests/lib/Persistence/Legacy/Setting/SettingHandlerTest.php b/tests/lib/Persistence/Legacy/Setting/SettingHandlerTest.php new file mode 100644 index 0000000000..20bb9113fb --- /dev/null +++ b/tests/lib/Persistence/Legacy/Setting/SettingHandlerTest.php @@ -0,0 +1,295 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\Setting; + +use Ibexa\Contracts\Core\Persistence\Setting\Setting; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Persistence\Legacy\Setting\Gateway; +use Ibexa\Core\Persistence\Legacy\Setting\Handler; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; +use PHPUnit\Framework\MockObject\MockObject; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\Setting\Handler::create + */ +final class SettingHandlerTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\Setting\Handler */ + private $settingHandler; + + /** @var \Ibexa\Core\Persistence\Legacy\Setting\Gateway */ + private $gatewayMock; + + public function testCreate(): void + { + $handler = $this->getSettingHandler(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects(self::once()) + ->method('insertSetting') + ->with( + self::identicalTo('group_a1'), + self::identicalTo('identifier_b2'), + self::identicalTo('value_c3') + )->willReturn(123); + + $gatewayMock->expects(self::once()) + ->method('loadSettingById') + ->with( + self::identicalTo(123) + ) + ->willReturn([ + 'group' => 'group_a1', + 'identifier' => 'identifier_b2', + 'value' => 'value_c3', + ]); + + $settingRef = new Setting([ + 'group' => 'group_a1', + 'identifier' => 'identifier_b2', + 'serializedValue' => 'value_c3', + ]); + + $result = $handler->create( + 'group_a1', + 'identifier_b2', + 'value_c3', + ); + + self::assertEquals( + $settingRef, + $result + ); + } + + public function testCreateFailsToLoad(): void + { + $handler = $this->getSettingHandler(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects(self::once()) + ->method('insertSetting') + ->with( + self::identicalTo('group_a1'), + self::identicalTo('identifier_b2'), + self::identicalTo('value_c3') + )->willReturn(123); + + $gatewayMock->expects(self::once()) + ->method('loadSettingById') + ->with( + self::identicalTo(123) + ) + ->willReturn(null); + + $this->expectException(NotFoundException::class); + $this->expectExceptionMessage(<<<ERROR + Could not find 'Setting' with identifier 'array ( + 'group' => 'group_a1', + 'identifier' => 'identifier_b2', + )' + ERROR); + + $handler->create( + 'group_a1', + 'identifier_b2', + 'value_c3', + ); + } + + /** + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + */ + public function testUpdate(): void + { + $handler = $this->getSettingHandler(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects(self::once()) + ->method('updateSetting') + ->with( + self::identicalTo('group_d1'), + self::identicalTo('identifier_e2'), + self::identicalTo('value_f3') + ); + + $gatewayMock->expects(self::once()) + ->method('loadSetting') + ->with( + self::identicalTo('group_d1'), + self::identicalTo('identifier_e2') + ) + ->will($this->returnValue([ + 'group' => 'group_d1', + 'identifier' => 'identifier_e2', + 'value' => 'value_f3', + ])); + + $settingRef = new Setting([ + 'group' => 'group_d1', + 'identifier' => 'identifier_e2', + 'serializedValue' => 'value_f3', + ]); + + $result = $handler->update( + 'group_d1', + 'identifier_e2', + 'value_f3' + ); + + self::assertEquals( + $settingRef, + $result + ); + } + + public function testUpdateFailsToLoad(): void + { + $handler = $this->getSettingHandler(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects(self::once()) + ->method('updateSetting') + ->with( + self::identicalTo('group_d1'), + self::identicalTo('identifier_e2'), + self::identicalTo('value_f3') + ); + + $gatewayMock->expects(self::once()) + ->method('loadSetting') + ->with( + self::identicalTo('group_d1'), + self::identicalTo('identifier_e2') + ) + ->will($this->returnValue(null)); + + $this->expectException(NotFoundException::class); + $this->expectExceptionMessage(<<<ERROR + Could not find 'Setting' with identifier 'array ( + 'group' => 'group_d1', + 'identifier' => 'identifier_e2', + )' + ERROR); + + $handler->update( + 'group_d1', + 'identifier_e2', + 'value_f3' + ); + } + + /** + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + */ + public function testLoad(): void + { + $handler = $this->getSettingHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects(self::once()) + ->method('loadSetting') + ->with( + self::identicalTo('group_a1'), + self::identicalTo('identifier_b2') + )->willReturn([ + 'group' => 'group_a1', + 'identifier' => 'identifier_b2', + 'value' => 'value_c3', + ]); + + $settingRef = new Setting([ + 'group' => 'group_a1', + 'identifier' => 'identifier_b2', + 'serializedValue' => 'value_c3', + ]); + + $result = $handler->load( + 'group_a1', + 'identifier_b2' + ); + + self::assertEquals( + $settingRef, + $result + ); + } + + /** + * @throws \Ibexa\Core\Base\Exceptions\NotFoundException + */ + public function testLoadFailsToLoad(): void + { + $handler = $this->getSettingHandler(); + + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects(self::once()) + ->method('loadSetting') + ->with( + self::identicalTo('group_a1'), + self::identicalTo('identifier_b2') + )->willReturn(null); + + $this->expectException(NotFoundException::class); + $this->expectExceptionMessage(<<<ERROR + Could not find 'Setting' with identifier 'array ( + 'group' => 'group_a1', + 'identifier' => 'identifier_b2', + )' + ERROR); + + $handler->load( + 'group_a1', + 'identifier_b2' + ); + } + + public function testDelete(): void + { + $handler = $this->getSettingHandler(); + $gatewayMock = $this->getGatewayMock(); + + $gatewayMock->expects(self::once()) + ->method('deleteSetting') + ->with( + self::identicalTo('group_a1'), + self::identicalTo('identifier_b2') + ); + + $handler->delete( + 'group_a1', + 'identifier_b2' + ); + } + + protected function getSettingHandler(): Handler + { + if (!isset($this->settingHandler)) { + $this->settingHandler = new Handler( + $this->getGatewayMock() + ); + } + + return $this->settingHandler; + } + + /** + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Persistence\Legacy\Setting\Gateway + */ + protected function getGatewayMock(): MockObject + { + if (!isset($this->gatewayMock)) { + $this->gatewayMock = $this->createMock(Gateway::class); + } + + return $this->gatewayMock; + } +} + +class_alias(SettingHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\Setting\SettingHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/SharedGateway/GatewayFactoryTest.php b/tests/lib/Persistence/Legacy/SharedGateway/GatewayFactoryTest.php similarity index 80% rename from eZ/Publish/Core/Persistence/Legacy/Tests/SharedGateway/GatewayFactoryTest.php rename to tests/lib/Persistence/Legacy/SharedGateway/GatewayFactoryTest.php index 649da896b8..090c366bf9 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/SharedGateway/GatewayFactoryTest.php +++ b/tests/lib/Persistence/Legacy/SharedGateway/GatewayFactoryTest.php @@ -6,22 +6,22 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Legacy\Tests\SharedGateway; +namespace Ibexa\Tests\Core\Persistence\Legacy\SharedGateway; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Platforms; -use eZ\Publish\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\FallbackGateway; -use eZ\Publish\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\SqliteGateway; -use eZ\Publish\Core\Persistence\Legacy\SharedGateway\GatewayFactory; +use Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\FallbackGateway; +use Ibexa\Core\Persistence\Legacy\SharedGateway\DatabasePlatform\SqliteGateway; +use Ibexa\Core\Persistence\Legacy\SharedGateway\GatewayFactory; use PHPUnit\Framework\TestCase; use Traversable; /** - * @covers \eZ\Publish\Core\Persistence\Legacy\SharedGateway\GatewayFactory + * @covers \Ibexa\Core\Persistence\Legacy\SharedGateway\GatewayFactory */ final class GatewayFactoryTest extends TestCase { - /** @var \eZ\Publish\Core\Persistence\Legacy\SharedGateway\GatewayFactory */ + /** @var \Ibexa\Core\Persistence\Legacy\SharedGateway\GatewayFactory */ private $factory; /** @@ -40,8 +40,6 @@ public function setUp(): void } /** - * @covers \eZ\Publish\Core\Persistence\Legacy\SharedGateway\GatewayFactory::buildSharedGateway - * * @dataProvider getTestBuildSharedGatewayData * * @param \Doctrine\DBAL\Connection $connectionMock @@ -88,3 +86,5 @@ public function getTestBuildSharedGatewayData(): Traversable } } } + +class_alias(GatewayFactoryTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\SharedGateway\GatewayFactoryTest'); diff --git a/tests/lib/Persistence/Legacy/TestCase.php b/tests/lib/Persistence/Legacy/TestCase.php new file mode 100644 index 0000000000..887861cfe7 --- /dev/null +++ b/tests/lib/Persistence/Legacy/TestCase.php @@ -0,0 +1,350 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy; + +use Doctrine\Common\EventManager as DoctrineEventManager; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\ConnectionException; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\FetchMode; +use Doctrine\DBAL\Query\QueryBuilder; +use Ibexa\Contracts\Core\Test\Persistence\Fixture\FileFixtureFactory; +use Ibexa\Contracts\Core\Test\Persistence\Fixture\FixtureImporter; +use Ibexa\Contracts\Core\Test\Persistence\Fixture\YamlFixture; +use Ibexa\Contracts\Core\Test\Repository\SetupFactory\Legacy; +use Ibexa\Core\Persistence\Legacy\SharedGateway; +use Ibexa\Core\Search\Legacy\Content; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter; +use Ibexa\DoctrineSchema\Database\DbPlatform\SqliteDbPlatform; +use Ibexa\Tests\Core\Persistence\DatabaseConnectionFactory; +use Ibexa\Tests\Core\Repository\LegacySchemaImporter; +use InvalidArgumentException; +use PDOException; +use PHPUnit\Framework\TestCase as BaseTestCase; +use ReflectionObject; +use ReflectionProperty; + +/** + * Base test case for database related tests. + */ +abstract class TestCase extends BaseTestCase +{ + /** + * DSN used for the DB backend. + * + * @var string + */ + protected $dsn; + + /** + * Name of the DB, extracted from DSN. + * + * @var string + */ + protected $db; + + /** + * Doctrine Database connection -- to not be constructed twice for one test. + * + * @internal + * + * @var \Doctrine\DBAL\Connection + */ + protected $connection; + + /** @var \Ibexa\Core\Persistence\Legacy\SharedGateway\Gateway */ + private $sharedGateway; + + /** + * Get data source name. + * + * The database connection string is read from an optional environment + * variable "DATABASE" and defaults to an in-memory SQLite database. + * + * @return string + */ + protected function getDsn() + { + if (!$this->dsn) { + $this->dsn = getenv('DATABASE'); + if (!$this->dsn) { + $this->dsn = 'sqlite://:memory:'; + } + $this->db = preg_replace('(^([a-z]+).*)', '\\1', $this->dsn); + } + + return $this->dsn; + } + + /** + * Get native Doctrine database connection. + */ + final public function getDatabaseConnection(): Connection + { + if (!$this->connection) { + $eventManager = new DoctrineEventManager(); + $connectionFactory = new DatabaseConnectionFactory( + [new SqliteDbPlatform()], + $eventManager + ); + + try { + $this->connection = $connectionFactory->createConnection($this->getDsn()); + } catch (DBALException $e) { + self::fail('Connection failed: ' . $e->getMessage()); + } + } + + return $this->connection; + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + final public function getSharedGateway(): SharedGateway\Gateway + { + if (!$this->sharedGateway) { + $connection = $this->getDatabaseConnection(); + $factory = new SharedGateway\GatewayFactory( + new SharedGateway\DatabasePlatform\FallbackGateway($connection), + [ + 'sqlite' => new SharedGateway\DatabasePlatform\SqliteGateway(), + ] + ); + + $this->sharedGateway = $factory->buildSharedGateway($connection); + } + + return $this->sharedGateway; + } + + /** + * Resets the database on test setup, so we always operate on a clean + * database. + */ + protected function setUp(): void + { + try { + $schemaImporter = new LegacySchemaImporter($this->getDatabaseConnection()); + $schemaImporter->importSchema( + dirname(__DIR__, 4) . + '/src/bundle/Core/Resources/config/storage/legacy/schema.yaml' + ); + } catch (PDOException | ConnectionException $e) { + self::fail( + sprintf( + 'PDO session could not be created: %s: %s', + get_class($e), + $e->getMessage() + ) + ); + } + } + + protected function tearDown(): void + { + unset($this->connection); + } + + /** + * Get a text representation of a result set. + * + * @param array $result + * + * @return string + */ + protected static function getResultTextRepresentation(array $result) + { + return implode( + "\n", + array_map( + static function ($row) { + return implode(', ', $row); + }, + $result + ) + ); + } + + /** + * Insert a database fixture from the given file. + */ + protected function insertDatabaseFixture(string $file): void + { + try { + $fixtureImporter = new FixtureImporter($this->getDatabaseConnection()); + $fixtureImporter->import((new FileFixtureFactory())->buildFixture($file)); + } catch (DBALException $e) { + self::fail('Database fixture import failed: ' . $e->getMessage()); + } + } + + /** + * Insert test_data.yaml fixture, common for many test cases. + * + * See: eZ/Publish/API/Repository/Tests/_fixtures/Legacy/data/test_data.yaml + */ + protected function insertSharedDatabaseFixture(): void + { + try { + $fixtureImporter = new FixtureImporter($this->getDatabaseConnection()); + $fixtureImporter->import( + new YamlFixture( + __DIR__ . '/../../../integration/Core/Repository/_fixtures/Legacy/data/test_data.yaml' + ) + ); + } catch (DBALException $e) { + self::fail('Database fixture import failed: ' . $e->getMessage()); + } + } + + /** + * Assert query result as correct. + * + * Builds text representations of the asserted and fetched query result, + * based on a QueryBuilder object. Compares them using classic diff for + * maximum readability of the differences between expectations and real + * results. + * + * The expectation MUST be passed as a two dimensional array containing + * rows of columns. + * + * @param array $expectation expected raw database rows + */ + public static function assertQueryResult( + array $expectation, + QueryBuilder $query, + string $message = '' + ): void { + $result = $query->execute()->fetchAll(FetchMode::ASSOCIATIVE); + + self::assertEquals( + self::getResultTextRepresentation($expectation), + self::getResultTextRepresentation($result), + $message + ); + } + + /** + * Asserts correct property values on $object. + * + * Asserts that for all keys in $properties a corresponding property + * exists in $object with the *same* value as in $properties. + * + * @param array $properties + * @param object $object + */ + protected function assertPropertiesCorrect(array $properties, $object) + { + if (!is_object($object)) { + throw new InvalidArgumentException( + 'Received ' . gettype($object) . ' instead of object as second parameter' + ); + } + foreach ($properties as $propName => $propVal) { + $this->assertSame( + $propVal, + $object->$propName, + "Incorrect value for \${$propName}" + ); + } + } + + /** + * Asserts $expStruct equals $actStruct in at least $propertyNames. + * + * Asserts that properties of $actStruct equal properties of $expStruct (not + * vice versa!). If $propertyNames is null, all properties are checked. + * Otherwise, $propertyNames provides a white list. + * + * @param object $expStruct + * @param object $actStruct + * @param array $propertyNames + */ + protected function assertStructsEqual( + $expStruct, + $actStruct, + array $propertyNames = null + ) { + if ($propertyNames === null) { + $propertyNames = $this->getPublicPropertyNames($expStruct); + } + foreach ($propertyNames as $propName) { + $this->assertEquals( + $expStruct->$propName, + $actStruct->$propName, + "Properties \${$propName} not same" + ); + } + } + + /** + * Returns public property names in $object. + * + * @param object $object + * + * @return array + */ + protected function getPublicPropertyNames($object) + { + $refl = new ReflectionObject($object); + + return array_map( + static function ($prop) { + return $prop->getName(); + }, + $refl->getProperties(ReflectionProperty::IS_PUBLIC) + ); + } + + /** + * @deprecated since Ibexa 4.0, rewrite test case to use {@see \Ibexa\Contracts\Core\Test\IbexaKernelTestCase} instead. + * + * @return string + */ + protected static function getInstallationDir(): string + { + return Legacy::getInstallationDir(); + } + + protected function getTrashCriteriaConverterDependency(): CriteriaConverter + { + $connection = $this->getDatabaseConnection(); + + return new CriteriaConverter( + [ + new CriterionHandler\LogicalAnd($connection), + new CriterionHandler\SectionId($connection), + new CriterionHandler\ContentTypeId($connection), + new CriterionHandler\DateMetadata($connection), + new CriterionHandler\UserMetadata($connection), + ] + ); + } + + protected function getTrashSortClauseConverterDependency(): SortClauseConverter + { + $connection = $this->getDatabaseConnection(); + + return new SortClauseConverter( + [ + new Content\Common\Gateway\SortClauseHandler\SectionName($connection), + new Content\Common\Gateway\SortClauseHandler\ContentName($connection), + new Content\Common\Gateway\SortClauseHandler\Trash\ContentTypeName($connection), + new Content\Common\Gateway\SortClauseHandler\Trash\UserLogin($connection), + new Content\Common\Gateway\SortClauseHandler\Trash\DateTrashed($connection), + new Content\Location\Gateway\SortClauseHandler\Location\Path($connection), + new Content\Location\Gateway\SortClauseHandler\Location\Depth($connection), + new Content\Location\Gateway\SortClauseHandler\Location\Priority($connection), + ] + ); + } +} + +class_alias(TestCase::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\TestCase'); diff --git a/tests/lib/Persistence/Legacy/TransactionHandlerTest.php b/tests/lib/Persistence/Legacy/TransactionHandlerTest.php new file mode 100644 index 0000000000..47f7e7beee --- /dev/null +++ b/tests/lib/Persistence/Legacy/TransactionHandlerTest.php @@ -0,0 +1,184 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy; + +use Doctrine\DBAL\Connection; +use Exception; +use Ibexa\Core\Persistence\Legacy\Content\Language\CachingHandler; +use Ibexa\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler; +use Ibexa\Core\Persistence\Legacy\TransactionHandler; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\TransactionHandler + */ +class TransactionHandlerTest extends TestCase +{ + /** + * Transaction handler to test. + * + * @var \Ibexa\Core\Persistence\Legacy\TransactionHandler + */ + protected $transactionHandler; + + /** @var \Doctrine\DBAL\Connection|\PHPUnit\Framework\MockObject\MockObject */ + protected $connectionMock; + + /** @var \Ibexa\Contracts\Core\Persistence\Content\Type\Handler|\PHPUnit\Framework\MockObject\MockObject */ + protected $contentTypeHandlerMock; + + /** @var \Ibexa\Contracts\Core\Persistence\Content\Language\Handler|\PHPUnit\Framework\MockObject\MockObject */ + protected $languageHandlerMock; + + public function testBeginTransaction() + { + $handler = $this->getTransactionHandler(); + $this->getConnectionMock() + ->expects($this->once()) + ->method('beginTransaction'); + $this->getContentTypeHandlerMock() + ->expects($this->never()) + ->method($this->anything()); + $this->getLanguageHandlerMock() + ->expects($this->never()) + ->method($this->anything()); + + $handler->beginTransaction(); + } + + public function testCommit() + { + $handler = $this->getTransactionHandler(); + $this->getConnectionMock() + ->expects($this->once()) + ->method('commit'); + $this->getContentTypeHandlerMock() + ->expects($this->never()) + ->method($this->anything()); + $this->getLanguageHandlerMock() + ->expects($this->never()) + ->method($this->anything()); + + $handler->commit(); + } + + public function testCommitException() + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('test'); + + $handler = $this->getTransactionHandler(); + $this->getConnectionMock() + ->expects($this->once()) + ->method('commit') + ->will($this->throwException(new Exception('test'))); + $this->getContentTypeHandlerMock() + ->expects($this->never()) + ->method($this->anything()); + $this->getLanguageHandlerMock() + ->expects($this->never()) + ->method($this->anything()); + + $handler->commit(); + } + + public function testRollback() + { + $handler = $this->getTransactionHandler(); + $this->getConnectionMock() + ->expects($this->once()) + ->method('rollback'); + $this->getContentTypeHandlerMock() + ->expects($this->once()) + ->method('clearCache'); + $this->getLanguageHandlerMock() + ->expects($this->once()) + ->method('clearCache'); + + $handler->rollback(); + } + + public function testRollbackException() + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('test'); + + $handler = $this->getTransactionHandler(); + $this->getConnectionMock() + ->expects($this->once()) + ->method('rollback') + ->will($this->throwException(new Exception('test'))); + $this->getContentTypeHandlerMock() + ->expects($this->never()) + ->method($this->anything()); + $this->getLanguageHandlerMock() + ->expects($this->never()) + ->method($this->anything()); + + $handler->rollback(); + } + + /** + * Returns a mock object for the Content Gateway. + * + * @return \Ibexa\Core\Persistence\Legacy\TransactionHandler + */ + protected function getTransactionHandler() + { + if (!isset($this->transactionHandler)) { + $this->transactionHandler = new TransactionHandler( + $this->getConnectionMock(), + $this->getContentTypeHandlerMock(), + $this->getLanguageHandlerMock() + ); + } + + return $this->transactionHandler; + } + + /** + * @return \Doctrine\DBAL\Connection|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getConnectionMock(): Connection + { + if (!isset($this->connectionMock)) { + $this->connectionMock = $this->createMock(Connection::class); + } + + return $this->connectionMock; + } + + /** + * Returns a mock object for the content type handler. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Type\MemoryCachingHandler|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getContentTypeHandlerMock() + { + if (!isset($this->contentTypeHandlerMock)) { + $this->contentTypeHandlerMock = $this->createMock(MemoryCachingHandler::class); + } + + return $this->contentTypeHandlerMock; + } + + /** + * Returns a mock object for the Content Language Gateway. + * + * @return \Ibexa\Core\Persistence\Legacy\Content\Language\CachingHandler|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getLanguageHandlerMock() + { + if (!isset($this->languageHandlerMock)) { + $this->languageHandlerMock = $this->createMock(CachingHandler::class); + } + + return $this->languageHandlerMock; + } +} + +class_alias(TransactionHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\TransactionHandlerTest'); diff --git a/tests/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..e1f7e46e91 --- /dev/null +++ b/tests/lib/Persistence/Legacy/URL/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,104 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\URL\Gateway; + +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion\MatchAll as MatchAllCriterion; +use Ibexa\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriteriaConverter; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\MatchAll; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase + */ +class DoctrineDatabaseTest extends TestCase +{ + /** + * Database gateway to test. + * + * @var \Ibexa\Core\Persistence\Legacy\URL\Gateway\DoctrineDatabase + */ + private $gateway; + + /** @var array[] */ + private $fixtureData; + + protected function setUp(): void + { + parent::setUp(); + + $fixtureLocation = __DIR__ . '/_fixtures/urls.php'; + $this->fixtureData = (require $fixtureLocation)['ezurl']; + $this->insertDatabaseFixture($fixtureLocation); + $this->initGateway(); + } + + public function testLoadUrlData(): void + { + $row = $this->gateway->loadUrlData(23); + + self::assertEquals( + $this->fixtureData[0], + $row[0] + ); + } + + public function testLoadUrlDataByUrl(): void + { + $rows = $this->gateway->loadUrlDataByUrl('https://doc.ibexa.co/display/USER/'); + + self::assertEquals( + $this->fixtureData[0], + $rows[0] + ); + } + + public function testFind(): void + { + $criterion = new MatchAllCriterion(); + $results = $this->gateway->find($criterion, 0, 10); + + self::assertEquals( + [ + 'count' => count($this->fixtureData), + 'rows' => $this->fixtureData, + ], + $results + ); + } + + public function testFindWithDisabledCounting(): void + { + $criterion = new MatchAllCriterion(); + $results = $this->gateway->find($criterion, 0, 10, [], false); + + self::assertEquals( + [ + 'count' => null, + 'rows' => $this->fixtureData, + ], + $results + ); + } + + /** + * Return the DoctrineDatabase gateway to test. + */ + protected function initGateway(): DoctrineDatabase + { + if (!isset($this->gateway)) { + $criteriaConverter = new CriteriaConverter([new MatchAll()]); + $this->gateway = new DoctrineDatabase($this->getDatabaseConnection(), $criteriaConverter); + } + + return $this->gateway; + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\URL\Gateway\DoctrineDatabaseTest'); diff --git a/tests/lib/Persistence/Legacy/URL/Gateway/_fixtures/urls.php b/tests/lib/Persistence/Legacy/URL/Gateway/_fixtures/urls.php new file mode 100644 index 0000000000..6c08c8c1ed --- /dev/null +++ b/tests/lib/Persistence/Legacy/URL/Gateway/_fixtures/urls.php @@ -0,0 +1,39 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +return [ + 'ezurl' => [ + [ + 'id' => 23, + 'created' => 1448832197, + 'is_valid' => 1, + 'last_checked' => 0, + 'modified' => 1448832197, + 'original_url_md5' => 'f76e41d421b2a72232264943026a6ee5', + 'url' => 'https://doc.ibexa.co/display/USER/', + ], + [ + 'id' => 24, + 'created' => 1448832277, + 'is_valid' => 1, + 'last_checked' => 0, + 'modified' => 1505717756, + 'original_url_md5' => 'a00ab36edb35bb641cc027eb27410934', + 'url' => 'https://doc.ezplatform.com/en/latest/', + ], + [ + 'id' => 25, + 'created' => 1448832412, + 'is_valid' => 1, + 'last_checked' => 0, + 'modified' => 1505717756, + 'original_url_md5' => '03c4188f5fdcb679192e25a7dad09c2d', + 'url' => 'https://doc.ezplatform.com/en/latest/tutorials/platform_beginner/building_a_bicycle_route_tracker_in_ez_platform/', + ], + ], +]; diff --git a/tests/lib/Persistence/Legacy/URL/HandlerTest.php b/tests/lib/Persistence/Legacy/URL/HandlerTest.php new file mode 100644 index 0000000000..1fbed75d42 --- /dev/null +++ b/tests/lib/Persistence/Legacy/URL/HandlerTest.php @@ -0,0 +1,204 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\URL; + +use Ibexa\Contracts\Core\Persistence\URL\URL; +use Ibexa\Contracts\Core\Persistence\URL\URLUpdateStruct; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\URL\URLQuery; +use Ibexa\Core\Persistence\Legacy\URL\Gateway; +use Ibexa\Core\Persistence\Legacy\URL\Handler; +use Ibexa\Core\Persistence\Legacy\URL\Mapper; +use PHPUnit\Framework\TestCase; + +class HandlerTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\URL\Gateway|\PHPUnit\Framework\MockObject\MockObject */ + private $gateway; + + /** @var \Ibexa\Core\Persistence\Legacy\URL\Mapper|\PHPUnit\Framework\MockObject\MockObject */ + private $mapper; + + /** @var \Ibexa\Core\Persistence\Legacy\URL\Handler */ + private $handler; + + protected function setUp(): void + { + parent::setUp(); + $this->gateway = $this->createMock(Gateway::class); + $this->mapper = $this->createMock(Mapper::class); + $this->handler = new Handler($this->gateway, $this->mapper); + } + + public function testUpdateUrl() + { + $urlUpdateStruct = new URLUpdateStruct(); + $url = $this->getUrl(1, 'http://ibexa.co'); + + $this->mapper + ->expects($this->once()) + ->method('createURLFromUpdateStruct') + ->with($urlUpdateStruct) + ->willReturn($url); + + $this->gateway + ->expects($this->once()) + ->method('updateUrl') + ->with($url); + + $this->assertEquals($url, $this->handler->updateUrl($url->id, $urlUpdateStruct)); + } + + public function testFind() + { + $query = new URLQuery(); + $query->filter = new Criterion\Validity(true); + $query->sortClauses = [ + new SortClause\Id(), + ]; + $query->offset = 2; + $query->limit = 10; + + $results = [ + 'count' => 1, + 'rows' => [ + [ + 'id' => 1, + 'url' => 'http://ibexa.co', + ], + ], + ]; + + $expected = [ + 'count' => 1, + 'items' => [ + $this->getUrl(1, 'http://ibexa.co'), + ], + ]; + + $this->gateway + ->expects($this->once()) + ->method('find') + ->with($query->filter, $query->offset, $query->limit, $query->sortClauses, $query->performCount) + ->willReturn($results); + + $this->mapper + ->expects($this->once()) + ->method('extractURLsFromRows') + ->with($results['rows']) + ->willReturn($expected['items']); + + $this->assertEquals($expected, $this->handler->find($query)); + } + + public function testLoadByIdWithoutUrlData() + { + $this->expectException(NotFoundException::class); + + $id = 1; + + $this->gateway + ->expects($this->once()) + ->method('loadUrlData') + ->with($id) + ->willReturn([]); + + $this->mapper + ->expects($this->once()) + ->method('extractURLsFromRows') + ->with([]) + ->willReturn([]); + + $this->handler->loadById($id); + } + + public function testLoadByIdWithUrlData() + { + $url = $this->getUrl(1, 'http://ibexa.co'); + + $this->gateway + ->expects($this->once()) + ->method('loadUrlData') + ->with($url->id) + ->willReturn([$url]); + + $this->mapper + ->expects($this->once()) + ->method('extractURLsFromRows') + ->with([$url]) + ->willReturn([$url]); + + $this->assertEquals($url, $this->handler->loadById($url->id)); + } + + public function testLoadByUrlWithoutUrlData() + { + $this->expectException(NotFoundException::class); + + $url = 'http://ibexa.co'; + + $this->gateway + ->expects($this->once()) + ->method('loadUrlDataByUrl') + ->with($url) + ->willReturn([]); + + $this->mapper + ->expects($this->once()) + ->method('extractURLsFromRows') + ->with([]) + ->willReturn([]); + + $this->handler->loadByUrl($url); + } + + public function testLoadByUrlWithUrlData() + { + $url = $this->getUrl(1, 'http://ibexa.co'); + + $this->gateway + ->expects($this->once()) + ->method('loadUrlDataByUrl') + ->with($url->url) + ->willReturn([$url]); + + $this->mapper + ->expects($this->once()) + ->method('extractURLsFromRows') + ->with([$url]) + ->willReturn([$url]); + + $this->assertEquals($url, $this->handler->loadByUrl($url->url)); + } + + public function testFindUsages() + { + $url = $this->getUrl(); + $ids = [1, 2, 3]; + + $this->gateway + ->expects($this->once()) + ->method('findUsages') + ->with($url->id) + ->will($this->returnValue($ids)); + + $this->assertEquals($ids, $this->handler->findUsages($url->id)); + } + + private function getUrl($id = 1, $urlAddr = 'http://ibexa.co') + { + $url = new URL(); + $url->id = $id; + $url->url = $url; + + return $url; + } +} + +class_alias(HandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\URL\HandlerTest'); diff --git a/tests/lib/Persistence/Legacy/URL/MapperTest.php b/tests/lib/Persistence/Legacy/URL/MapperTest.php new file mode 100644 index 0000000000..7603a5044a --- /dev/null +++ b/tests/lib/Persistence/Legacy/URL/MapperTest.php @@ -0,0 +1,89 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\URL; + +use Ibexa\Contracts\Core\Persistence\URL\URL; +use Ibexa\Contracts\Core\Persistence\URL\URLUpdateStruct; +use Ibexa\Core\Persistence\Legacy\URL\Mapper; +use PHPUnit\Framework\TestCase; + +class MapperTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\URL\Mapper */ + private $mapper; + + protected function setUp(): void + { + parent::setUp(); + $this->mapper = new Mapper(); + } + + public function testCreateURLFromUpdateStruct() + { + $urlUpdateStruct = new URLUpdateStruct(); + $urlUpdateStruct->url = 'https://ibexa.co'; + $urlUpdateStruct->isValid = true; + $urlUpdateStruct->lastChecked = 0; + $urlUpdateStruct->modified = time(); + + $expected = new URL(); + $expected->url = $urlUpdateStruct->url; + $expected->originalUrlMd5 = md5($urlUpdateStruct->url); + $expected->isValid = $urlUpdateStruct->isValid; + $expected->lastChecked = $urlUpdateStruct->lastChecked; + $expected->created = 0; + $expected->modified = $urlUpdateStruct->modified; + + $this->assertEquals($expected, $this->mapper->createURLFromUpdateStruct($urlUpdateStruct)); + } + + public function testExtractURLsFromRows() + { + $rows = [ + [ + 'id' => 12, + 'url' => 'https://ibexa.co', + 'original_url_md5' => 'd74110041197e107722d8821f5f4d89c', + 'is_valid' => 0, + 'last_checked' => 0, + 'created' => 1510770207, + 'modified' => 0, + ], + [ + 'id' => 52, + 'url' => 'https://ezplatform.com', + 'original_url_md5' => '59697373afe0a059dc424ea2fc6946d5', + 'is_valid' => 1, + 'last_checked' => 0, + 'created' => 1510770293, + 'modified' => 0, + ], + ]; + + $urlEzNo = new URL(); + $urlEzNo->id = (int)$rows[0]['id']; + $urlEzNo->url = $rows[0]['url']; + $urlEzNo->originalUrlMd5 = $rows[0]['original_url_md5']; + $urlEzNo->isValid = (bool)$rows[0]['is_valid']; + $urlEzNo->lastChecked = (int)$rows[0]['last_checked']; + $urlEzNo->created = (int)$rows[0]['created']; + $urlEzNo->modified = (int)$rows[0]['modified']; + + $urlEzplatformCom = new URL(); + $urlEzplatformCom->id = (int)$rows[1]['id']; + $urlEzplatformCom->url = $rows[1]['url']; + $urlEzplatformCom->originalUrlMd5 = $rows[1]['original_url_md5']; + $urlEzplatformCom->isValid = (bool)$rows[1]['is_valid']; + $urlEzplatformCom->lastChecked = (int)$rows[1]['last_checked']; + $urlEzplatformCom->created = (int)$rows[1]['created']; + $urlEzplatformCom->modified = (int)$rows[1]['modified']; + + $this->assertEquals([$urlEzNo, $urlEzplatformCom], $this->mapper->extractURLsFromRows($rows)); + } +} + +class_alias(MapperTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\URL\MapperTest'); diff --git a/tests/lib/Persistence/Legacy/URL/Query/CriteriaConverterTest.php b/tests/lib/Persistence/Legacy/URL/Query/CriteriaConverterTest.php new file mode 100644 index 0000000000..ccc199b14f --- /dev/null +++ b/tests/lib/Persistence/Legacy/URL/Query/CriteriaConverterTest.php @@ -0,0 +1,85 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\URL\Query; + +use Doctrine\DBAL\Query\QueryBuilder; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriteriaConverter; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\User\Gateway\DoctrineDatabase + */ +class CriteriaConverterTest extends TestCase +{ + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function testConvertCriteriaSuccess(): void + { + $fooCriterionHandler = $this->createMock(CriterionHandler::class); + $barCriterionHandler = $this->createMock(CriterionHandler::class); + + $criteriaConverter = new CriteriaConverter([ + $fooCriterionHandler, + $barCriterionHandler, + ]); + + $barCriterion = $this->createMock(Criterion::class); + + $selectQuery = $this->createMock(QueryBuilder::class); + + $fooCriterionHandler + ->expects($this->once()) + ->method('accept') + ->with($barCriterion) + ->willReturn(false); + + $fooCriterionHandler + ->expects($this->never()) + ->method('handle'); + + $barCriterionHandler + ->expects($this->once()) + ->method('accept') + ->with($barCriterion) + ->willReturn(true); + + $sqlExpression = 'SQL EXPRESSION'; + $barCriterionHandler + ->expects($this->once()) + ->method('handle') + ->with($criteriaConverter, $selectQuery, $barCriterion) + ->willReturn($sqlExpression); + + $this->assertEquals( + $sqlExpression, + $criteriaConverter->convertCriteria( + $selectQuery, + $barCriterion + ) + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function testConvertCriteriaFailure(): void + { + $this->expectException(NotImplementedException::class); + + $criteriaConverter = new CriteriaConverter(); + $criteriaConverter->convertCriteria( + $this->createMock(QueryBuilder::class), + $this->createMock(Criterion::class) + ); + } +} + +class_alias(CriteriaConverterTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriteriaConverterTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/CriterionHandlerTest.php b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTest.php similarity index 82% rename from eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/CriterionHandlerTest.php rename to tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTest.php index da19b778b9..8f7497f3e6 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/CriterionHandlerTest.php +++ b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/CriterionHandlerTest.php @@ -4,14 +4,14 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler; +namespace Ibexa\Tests\Core\Persistence\Legacy\URL\Query\CriterionHandler; use Doctrine\DBAL\Query\Expression\CompositeExpression; use Doctrine\DBAL\Query\Expression\ExpressionBuilder; use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriteriaConverter; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler; use PHPUnit\Framework\TestCase; abstract class CriterionHandlerTest extends TestCase @@ -23,7 +23,7 @@ abstract public function testHandle(); /** * Check if critetion handler accepts specyfied criterion class. * - * @param \eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler $handler + * @param \Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler $handler * @param string $criterionClass */ protected function assertHandlerAcceptsCriterion(CriterionHandler $handler, $criterionClass) @@ -34,7 +34,7 @@ protected function assertHandlerAcceptsCriterion(CriterionHandler $handler, $cri /** * Check if critetion handler rejects specyfied criterion class. * - * @param \eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler $handler + * @param \Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler $handler * @param string $criterionClass */ protected function assertHandlerRejectsCriterion(CriterionHandler $handler, $criterionClass) @@ -87,3 +87,5 @@ protected function mockConverterForLogicalOperator( return $converter; } } + +class_alias(CriterionHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler\CriterionHandlerTest'); diff --git a/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalAndTest.php b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalAndTest.php new file mode 100644 index 0000000000..c38530f72c --- /dev/null +++ b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalAndTest.php @@ -0,0 +1,65 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\URL\Query\CriterionHandler; + +use Doctrine\DBAL\Query\Expression\CompositeExpression; +use Doctrine\DBAL\Query\QueryBuilder; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion\LogicalAnd; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalAnd as LogicalAndHandler; + +class LogicalAndTest extends CriterionHandlerTest +{ + /** + * {@inheritdoc} + */ + public function testAccept() + { + $handler = new LogicalAndHandler(); + + $this->assertTrue($handler->accept($this->createMock(LogicalAnd::class))); + $this->assertFalse($handler->accept($this->createMock(Criterion::class))); + } + + /** + * {@inheritdoc} + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function testHandle(): void + { + $foo = $this->createMock(Criterion::class); + $bar = $this->createMock(Criterion::class); + + $fooExpr = 'FOO'; + $barExpr = 'BAR'; + + $expected = '(FOO) AND (BAR)'; + + $queryBuilder = $this->createMock(QueryBuilder::class); + $converter = $this->mockConverterForLogicalOperator( + CompositeExpression::TYPE_AND, + $queryBuilder, + 'andX', + $fooExpr, + $barExpr, + $foo, + $bar + ); + + $handler = new LogicalAndHandler(); + $actual = (string)$handler->handle( + $converter, + $queryBuilder, + new LogicalAnd([$foo, $bar]) + ); + + $this->assertEquals($expected, $actual); + } +} + +class_alias(LogicalAndTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler\LogicalAndTest'); diff --git a/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalNotTest.php b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalNotTest.php new file mode 100644 index 0000000000..23dd93d009 --- /dev/null +++ b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalNotTest.php @@ -0,0 +1,59 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\URL\Query\CriterionHandler; + +use Doctrine\DBAL\Query\QueryBuilder; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion\LogicalNot; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriteriaConverter; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalNot as LogicalNotHandler; + +class LogicalNotTest extends CriterionHandlerTest +{ + /** + * {@inheritdoc} + */ + public function testAccept() + { + $handler = new LogicalNotHandler(); + + $this->assertHandlerAcceptsCriterion($handler, LogicalNot::class); + $this->assertHandlerRejectsCriterion($handler, Criterion::class); + } + + /** + * {@inheritdoc} + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function testHandle(): void + { + $foo = $this->createMock(Criterion::class); + $fooExpr = 'FOO'; + $expected = 'NOT (FOO)'; + + $queryBuilder = $this->createMock(QueryBuilder::class); + + $converter = $this->createMock(CriteriaConverter::class); + $converter + ->expects($this->at(0)) + ->method('convertCriteria') + ->with($queryBuilder, $foo) + ->willReturn($fooExpr); + + $handler = new LogicalNotHandler(); + $actual = $handler->handle( + $converter, + $queryBuilder, + new LogicalNot($foo) + ); + + $this->assertEquals($expected, $actual); + } +} + +class_alias(LogicalNotTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler\LogicalNotTest'); diff --git a/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalOrTest.php b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalOrTest.php new file mode 100644 index 0000000000..f7e3c2e006 --- /dev/null +++ b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/LogicalOrTest.php @@ -0,0 +1,65 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\URL\Query\CriterionHandler; + +use Doctrine\DBAL\Query\Expression\CompositeExpression; +use Doctrine\DBAL\Query\QueryBuilder; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion\LogicalOr; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\LogicalOr as LogicalOrHandler; + +class LogicalOrTest extends CriterionHandlerTest +{ + /** + * {@inheritdoc} + */ + public function testAccept() + { + $handler = new LogicalOrHandler(); + + $this->assertHandlerAcceptsCriterion($handler, LogicalOr::class); + $this->assertHandlerRejectsCriterion($handler, Criterion::class); + } + + /** + * {@inheritdoc} + * + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException + */ + public function testHandle(): void + { + $foo = $this->createMock(Criterion::class); + $bar = $this->createMock(Criterion::class); + + $fooExpr = 'FOO'; + $barExpr = 'BAR'; + + $expected = '(FOO) OR (BAR)'; + + $queryBuilder = $this->createMock(QueryBuilder::class); + $converter = $this->mockConverterForLogicalOperator( + CompositeExpression::TYPE_OR, + $queryBuilder, + 'orX', + $fooExpr, + $barExpr, + $foo, + $bar + ); + + $handler = new LogicalOrHandler(); + $actual = (string)$handler->handle( + $converter, + $queryBuilder, + new LogicalOr([$foo, $bar]) + ); + + $this->assertEquals($expected, $actual); + } +} + +class_alias(LogicalOrTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler\LogicalOrTest'); diff --git a/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/MatchAllTest.php b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/MatchAllTest.php new file mode 100644 index 0000000000..fa13a51b62 --- /dev/null +++ b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/MatchAllTest.php @@ -0,0 +1,46 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\URL\Query\CriterionHandler; + +use Doctrine\DBAL\Query\QueryBuilder; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion\MatchAll; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriteriaConverter; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\MatchAll as MatchAllHandler; + +class MatchAllTest extends CriterionHandlerTest +{ + /** + * {@inheritdoc} + */ + public function testAccept() + { + $handler = new MatchAllHandler(); + + $this->assertHandlerAcceptsCriterion($handler, MatchAll::class); + $this->assertHandlerRejectsCriterion($handler, Criterion::class); + } + + /** + * {@inheritdoc} + */ + public function testHandle() + { + $criterion = new MatchAll(); + $expected = '1 = 1'; + + $queryBuilder = $this->createMock(QueryBuilder::class); + $converter = $this->createMock(CriteriaConverter::class); + + $handler = new MatchAllHandler(); + $actual = $handler->handle($converter, $queryBuilder, $criterion); + + $this->assertEquals($expected, $actual); + } +} + +class_alias(MatchAllTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler\MatchAllTest'); diff --git a/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/MatchNoneTest.php b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/MatchNoneTest.php new file mode 100644 index 0000000000..bb748fed78 --- /dev/null +++ b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/MatchNoneTest.php @@ -0,0 +1,46 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\URL\Query\CriterionHandler; + +use Doctrine\DBAL\Query\QueryBuilder; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion\MatchNone; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriteriaConverter; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\MatchNone as MatchNoneHandler; + +class MatchNoneTest extends CriterionHandlerTest +{ + /** + * {@inheritdoc} + */ + public function testAccept() + { + $handler = new MatchNoneHandler(); + + $this->assertHandlerAcceptsCriterion($handler, MatchNone::class); + $this->assertHandlerRejectsCriterion($handler, Criterion::class); + } + + /** + * {@inheritdoc} + */ + public function testHandle() + { + $criterion = new MatchNone(); + $expected = '1 = 0'; + + $query = $this->createMock(QueryBuilder::class); + $converter = $this->createMock(CriteriaConverter::class); + + $handler = new MatchNoneHandler(); + $actual = $handler->handle($converter, $query, $criterion); + + $this->assertEquals($expected, $actual); + } +} + +class_alias(MatchNoneTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler\MatchNoneTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/PatternTest.php b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/PatternTest.php similarity index 78% rename from eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/PatternTest.php rename to tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/PatternTest.php index 0aa5e64d7b..a9b3196ff4 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/PatternTest.php +++ b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/PatternTest.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler; +namespace Ibexa\Tests\Core\Persistence\Legacy\URL\Query\CriterionHandler; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Query\Expression\ExpressionBuilder; use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion\Pattern; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\Pattern as PatternHandler; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion\Pattern; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriteriaConverter; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\Pattern as PatternHandler; class PatternTest extends CriterionHandlerTest { @@ -59,3 +59,5 @@ public function testHandle() $this->assertEquals($expected, $actual); } } + +class_alias(PatternTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler\PatternTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/ValidityTest.php b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/ValidityTest.php similarity index 77% rename from eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/ValidityTest.php rename to tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/ValidityTest.php index c111f069c3..bf1bc8f684 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/ValidityTest.php +++ b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/ValidityTest.php @@ -4,15 +4,15 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler; +namespace Ibexa\Tests\Core\Persistence\Legacy\URL\Query\CriterionHandler; use Doctrine\DBAL\ParameterType; use Doctrine\DBAL\Query\Expression\ExpressionBuilder; use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion\Validity; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\Validity as ValidityHandler; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion\Validity; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriteriaConverter; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\Validity as ValidityHandler; class ValidityTest extends CriterionHandlerTest { @@ -61,3 +61,5 @@ public function testHandle() $this->assertEquals($expected, $actual); } } + +class_alias(ValidityTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler\ValidityTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/VisibleOnlyTest.php b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/VisibleOnlyTest.php similarity index 80% rename from eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/VisibleOnlyTest.php rename to tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/VisibleOnlyTest.php index 31a8511a8a..2287d6281e 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/URL/Query/CriterionHandler/VisibleOnlyTest.php +++ b/tests/lib/Persistence/Legacy/URL/Query/CriterionHandler/VisibleOnlyTest.php @@ -6,15 +6,18 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler; +namespace Ibexa\Tests\Core\Persistence\Legacy\URL\Query\CriterionHandler; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\Expression\ExpressionBuilder; use Doctrine\DBAL\Query\QueryBuilder; -use eZ\Publish\API\Repository\Values\URL\Query\Criterion; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriteriaConverter; -use eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\VisibleOnly as VisibleOnlyHandler; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\Criterion; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriteriaConverter; +use Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\VisibleOnly as VisibleOnlyHandler; +/** + * @covers \Ibexa\Core\Persistence\Legacy\URL\Query\CriterionHandler\VisibleOnly + */ class VisibleOnlyTest extends CriterionHandlerTest { /** @@ -29,8 +32,6 @@ public function testAccept(): void } /** - * @covers \eZ\Publish\Core\Persistence\Legacy\URL\Query\CriterionHandler\VisibleOnly::handle - * * Note: more complex case with multiple Criteria trying to join the same table multiple times * has been covered by integration tests. */ @@ -68,3 +69,5 @@ private function createDoctrineQueryBuilder(): QueryBuilder return new QueryBuilder($connection); } } + +class_alias(VisibleOnlyTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\URL\Query\CriterionHandler\VisibleOnlyTest'); diff --git a/tests/lib/Persistence/Legacy/User/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/User/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..7ea199de33 --- /dev/null +++ b/tests/lib/Persistence/Legacy/User/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,99 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\User\Gateway; + +use Doctrine\DBAL\ParameterType; +use Ibexa\Core\Persistence\Legacy\User\Gateway\DoctrineDatabase; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\User\Gateway\DoctrineDatabase + */ +class DoctrineDatabaseTest extends TestCase +{ + /** + * Database gateway to test. + * + * @var \Ibexa\Core\Persistence\Legacy\User\Gateway\DoctrineDatabase + */ + protected $databaseGateway; + + /** + * Inserts DB fixture. + */ + protected function setUp(): void + { + parent::setUp(); + + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/roles.php' + ); + } + + public function testRemoveRoleByAssignmentId(): void + { + $gateway = $this->getDatabaseGateway(); + + $gateway->removeRoleAssignmentById(38); + $query = $this->getDatabaseConnection()->createQueryBuilder(); + + $this->assertQueryResult( + [ + [ + 'contentobject_id' => '11', + 'id' => '34', + 'limit_identifier' => '', + 'limit_value' => '', + 'role_id' => '5', + ], + [ + 'contentobject_id' => '59', + 'id' => '36', + 'limit_identifier' => '', + 'limit_value' => '', + 'role_id' => '5', + ], + [ + 'contentobject_id' => '13', + 'id' => '39', + 'limit_identifier' => 'Section', + 'limit_value' => '2', + 'role_id' => '5', + ], + ], + $query + ->select('contentobject_id', 'id', 'limit_identifier', 'limit_value', 'role_id') + ->from('ezuser_role') + ->where( + $query->expr()->eq( + 'role_id', + $query->createPositionalParameter(5, ParameterType::INTEGER) + ) + ) + ); + } + + /** + * Returns a ready to test DoctrineDatabase gateway. + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function getDatabaseGateway(): DoctrineDatabase + { + if (!isset($this->databaseGateway)) { + $this->databaseGateway = new DoctrineDatabase( + $this->getDatabaseConnection() + ); + } + + return $this->databaseGateway; + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\User\Gateway\DoctrineDatabaseTest'); diff --git a/tests/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..e58c9f10fc --- /dev/null +++ b/tests/lib/Persistence/Legacy/User/Role/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,214 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\User\Role\Gateway; + +use Doctrine\DBAL\ParameterType; +use Ibexa\Contracts\Core\Persistence\User\Role; +use Ibexa\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase + */ +class DoctrineDatabaseTest extends TestCase +{ + /** + * Database gateway to test. + * + * @var \Ibexa\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase + */ + protected $databaseGateway; + + /** + * Inserts DB fixture. + * + * @throws \Exception + */ + protected function setUp(): void + { + parent::setUp(); + + $this->insertDatabaseFixture( + __DIR__ . '/../../_fixtures/roles.php' + ); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function testCreateRole(): void + { + $gateway = $this->getDatabaseGateway(); + + $spiRole = new Role([ + 'identifier' => 'new_role', + 'status' => Role::STATUS_DRAFT, + ]); + $gateway->createRole($spiRole); + $query = $this->getDatabaseConnection()->createQueryBuilder(); + + $this->assertQueryResult( + [ + [ + 'id' => '6', + 'name' => 'new_role', + 'version' => -1, + ], + ], + $query + ->select('id', 'name', 'version') + ->from('ezrole') + ->where( + $query->expr()->eq( + 'name', + $query->createPositionalParameter('new_role', ParameterType::STRING) + ) + ) + ); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function testLoadRoleAssignment(): void + { + $gateway = $this->getDatabaseGateway(); + + $this->assertEquals( + [ + [ + 'contentobject_id' => '12', + 'id' => '25', + 'limit_identifier' => '', + 'limit_value' => '', + 'role_id' => '2', + ], + ], + $gateway->loadRoleAssignment(25) + ); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function testLoadRoleAssignmentsByGroupId(): void + { + $gateway = $this->getDatabaseGateway(); + + $this->assertEquals( + [ + [ + 'contentobject_id' => '11', + 'id' => '28', + 'limit_identifier' => '', + 'limit_value' => '', + 'role_id' => '1', + ], + [ + 'contentobject_id' => '11', + 'id' => '34', + 'limit_identifier' => '', + 'limit_value' => '', + 'role_id' => '5', + ], + [ + 'contentobject_id' => '11', + 'id' => '40', + 'limit_identifier' => 'Section', + 'limit_value' => '3', + 'role_id' => '4', + ], + ], + $gateway->loadRoleAssignmentsByGroupId(11) + ); + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + public function testLoadRoleAssignmentsByRoleId(): void + { + $gateway = $this->getDatabaseGateway(); + + $this->assertEquals( + [ + [ + 'contentobject_id' => '11', + 'id' => '28', + 'limit_identifier' => '', + 'limit_value' => '', + 'role_id' => '1', + ], + [ + 'contentobject_id' => '42', + 'id' => '31', + 'limit_identifier' => '', + 'limit_value' => '', + 'role_id' => '1', + ], + [ + 'contentobject_id' => '59', + 'id' => '37', + 'limit_identifier' => '', + 'limit_value' => '', + 'role_id' => '1', + ], + ], + $gateway->loadRoleAssignmentsByRoleId(1) + ); + } + + /** + * @covers \Ibexa\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase::loadRoleAssignmentsByRoleIdWithOffsetAndLimit + */ + public function testLoadRoleAssignmentsByRoleIdWithOffsetAndLimit(): void + { + $gateway = $this->getDatabaseGateway(); + + self::assertEquals( + [ + [ + 'contentobject_id' => '11', + 'id' => '28', + 'limit_identifier' => '', + 'limit_value' => '', + 'role_id' => '1', + ], + [ + 'contentobject_id' => '42', + 'id' => '31', + 'limit_identifier' => '', + 'limit_value' => '', + 'role_id' => '1', + ], + ], + $gateway->loadRoleAssignmentsByRoleIdWithOffsetAndLimit(1, 0, 2) + ); + } + + /** + * Returns a ready to test DoctrineDatabase gateway. + * + * @return \Ibexa\Core\Persistence\Legacy\User\Role\Gateway\DoctrineDatabase + * + * @throws \Doctrine\DBAL\DBALException + */ + protected function getDatabaseGateway(): DoctrineDatabase + { + if (!isset($this->databaseGateway)) { + $this->databaseGateway = new DoctrineDatabase( + $this->getDatabaseConnection() + ); + } + + return $this->databaseGateway; + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\User\Role\Gateway\DoctrineDatabaseTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/User/Role/LimitationConverterTest.php b/tests/lib/Persistence/Legacy/User/Role/LimitationConverterTest.php similarity index 90% rename from eZ/Publish/Core/Persistence/Legacy/Tests/User/Role/LimitationConverterTest.php rename to tests/lib/Persistence/Legacy/User/Role/LimitationConverterTest.php index 52cd02b9d0..e08459fa0c 100644 --- a/eZ/Publish/Core/Persistence/Legacy/Tests/User/Role/LimitationConverterTest.php +++ b/tests/lib/Persistence/Legacy/User/Role/LimitationConverterTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Legacy\Tests\User\Role; +namespace Ibexa\Tests\Core\Persistence\Legacy\User\Role; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationConverter; -use eZ\Publish\Core\Persistence\Legacy\User\Role\LimitationHandler\ObjectStateHandler as ObjectStateLimitationHandler; -use eZ\Publish\SPI\Persistence\User\Policy; +use Ibexa\Contracts\Core\Persistence\User\Policy; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Core\Persistence\Legacy\User\Role\LimitationConverter; +use Ibexa\Core\Persistence\Legacy\User\Role\LimitationHandler\ObjectStateHandler as ObjectStateLimitationHandler; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; /** * Test case for LimitationConverter. @@ -148,3 +148,5 @@ public function testObjectStateToSPI() ); } } + +class_alias(LimitationConverterTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\User\Role\LimitationConverterTest'); diff --git a/tests/lib/Persistence/Legacy/User/UserHandlerTest.php b/tests/lib/Persistence/Legacy/User/UserHandlerTest.php new file mode 100644 index 0000000000..e5f1fa989c --- /dev/null +++ b/tests/lib/Persistence/Legacy/User/UserHandlerTest.php @@ -0,0 +1,1214 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\Legacy\User; + +use DateInterval; +use DateTime; +use Ibexa\Contracts\Core\Persistence; +use Ibexa\Contracts\Core\Persistence\User\Handler; +use Ibexa\Contracts\Core\Persistence\User\Role; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Repository\Values\User\Role as APIRole; +use Ibexa\Core\Persistence\Legacy\User; +use Ibexa\Core\Persistence\Legacy\User\Role\LimitationConverter; +use Ibexa\Core\Persistence\Legacy\User\Role\LimitationHandler\ObjectStateHandler as ObjectStateLimitationHandler; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; +use LogicException; + +/** + * Test case for UserHandlerTest. + */ +class UserHandlerTest extends TestCase +{ + private const TEST_USER_ID = 42; + + /** + * @throws \Doctrine\DBAL\DBALException + */ + protected function getUserHandler(User\Gateway $userGateway = null): Handler + { + $connection = $this->getDatabaseConnection(); + + return new User\Handler( + $userGateway ?? new User\Gateway\DoctrineDatabase($connection), + new User\Role\Gateway\DoctrineDatabase($connection), + new User\Mapper(), + new LimitationConverter([new ObjectStateLimitationHandler($connection)]) + ); + } + + protected function getValidUser() + { + $user = new Persistence\User(); + $user->id = self::TEST_USER_ID; + $user->login = 'kore'; + $user->email = 'kore@example.org'; + $user->passwordHash = '1234567890'; + $user->hashAlgorithm = 2; + $user->isEnabled = true; + $user->maxLogin = 23; + $user->passwordUpdatedAt = 1569229200; + + return $user; + } + + protected function getValidUserToken($time = null) + { + $userToken = new Persistence\User\UserTokenUpdateStruct(); + $userToken->userId = self::TEST_USER_ID; + $userToken->hashKey = md5('hash'); + $userToken->time = $time ?? (new DateTime())->add(new DateInterval('P1D'))->getTimestamp(); + + return $userToken; + } + + public function testCreateUser() + { + $handler = $this->getUserHandler(); + + $this->expectException(NotImplementedException::class); + $handler->create($this->getValidUser()); + } + + protected function getGatewayReturnValue(): array + { + return [ + $this->getDummyUser( + self::TEST_USER_ID, + 'kore', + 'kore@example.org' + ), + ]; + } + + protected function getDummyUser( + int $id, + string $login, + string $email + ): array { + return [ + 'contentobject_id' => $id, + 'login' => $login, + 'email' => $email, + 'password_hash' => '1234567890', + 'password_hash_type' => 2, + 'is_enabled' => true, + 'max_login' => 23, + 'password_updated_at' => 1569229200, + ]; + } + + public function testLoadUser() + { + $gatewayMock = $this + ->createMock(User\Gateway::class); + + $gatewayMock + ->method('load') + ->with(self::TEST_USER_ID) + ->willReturn($this->getGatewayReturnValue()); + + $handler = $this->getUserHandler($gatewayMock); + + $user = $this->getValidUser(); + + $this->assertEquals( + $user, + $handler->load($user->id) + ); + } + + public function testLoadUnknownUser() + { + $this->expectException(NotFoundException::class); + $gatewayMock = $this + ->createMock(User\Gateway::class); + + $gatewayMock + ->method('load') + ->with(1337) + ->willReturn([]); + + $handler = $this->getUserHandler($gatewayMock); + + $handler->load(1337); + } + + public function testLoadUserByLogin() + { + $gatewayMock = $this + ->createMock(User\Gateway::class); + + $gatewayMock + ->method('loadByLogin') + ->with('kore') + ->willReturn($this->getGatewayReturnValue()); + + $handler = $this->getUserHandler($gatewayMock); + $user = $this->getValidUser(); + + $loadedUser = $handler->loadByLogin($user->login); + $this->assertEquals( + $user, + $loadedUser + ); + } + + public function testLoadMultipleUsersByLogin() + { + $this->expectException(LogicException::class); + + $gatewayMock = $this + ->createMock(User\Gateway::class); + + $gatewayMock + ->method('loadByLogin') + ->with('kore') + ->willReturn([ + $this->getDummyUser(self::TEST_USER_ID, 'kore', 'kore@example.org'), + $this->getDummyUser(self::TEST_USER_ID + 1, 'kore', 'kore@example.org'), + ]); + + $handler = $this->getUserHandler($gatewayMock); + $user = $this->getValidUser(); + + $handler->loadByLogin($user->login); + } + + public function testLoadMultipleUsersByEmail() + { + $this->expectException(LogicException::class); + + $gatewayMock = $this + ->createMock(User\Gateway::class); + + $gatewayMock + ->method('loadByEmail') + ->with('kore@example.org') + ->willReturn([ + $this->getDummyUser(self::TEST_USER_ID, 'kore_a', 'kore@example.org'), + $this->getDummyUser(self::TEST_USER_ID + 1, 'kore_b', 'kore@example.org'), + ]); + + $handler = $this->getUserHandler($gatewayMock); + $user = $this->getValidUser(); + + $handler->loadByEmail($user->email); + } + + public function testLoadUserByEmailNotFound() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getUserHandler(); + $user = $this->getValidUser(); + + $handler->loadByLogin($user->email); + } + + public function testLoadUserByEmail() + { + $gatewayMock = $this + ->createMock(User\Gateway::class); + + $gatewayMock + ->method('loadByEmail') + ->with('kore@example.org') + ->willReturn($this->getGatewayReturnValue()); + + $handler = $this->getUserHandler($gatewayMock); + $validUser = $this->getValidUser(); + + $user = $handler->loadByEmail($validUser->email); + $this->assertEquals( + $validUser, + $user + ); + } + + public function testLoadUsersByEmail() + { + $gatewayMock = $this + ->createMock(User\Gateway::class); + + $gatewayMock + ->method('loadByEmail') + ->with('kore@example.org') + ->willReturn($this->getGatewayReturnValue()); + + $handler = $this->getUserHandler($gatewayMock); + $user = $this->getValidUser(); + + $users = $handler->loadUsersByEmail($user->email); + $this->assertEquals( + $user, + $users[0] + ); + } + + public function testLoadUserByTokenNotFound() + { + $this->expectException(NotFoundException::class); + + $handler = $this->getUserHandler(); + $handler->updateUserToken($this->getValidUserToken()); + + $handler->loadUserByToken('asd'); + } + + public function testLoadUserByToken() + { + $gatewayMock = $this + ->createMock(User\Gateway::class); + + $userToken = $this->getValidUserToken(); + $gatewayMock + ->method('loadUserByToken') + ->with($userToken->hashKey) + ->willReturn($this->getGatewayReturnValue()); + + $handler = $this->getUserHandler($gatewayMock); + $user = $this->getValidUser(); + $handler->updateUserToken($userToken); + + $loadedUser = $handler->loadUserByToken($userToken->hashKey); + $this->assertEquals( + $user, + $loadedUser + ); + } + + public function testUpdateUserToken() + { + $handler = $this->getUserHandler(); + + $handler->updateUserToken($this->getValidUserToken(1234567890)); + + $this->assertQueryResult( + [['0800fc577294c34e0b28ad2839435945', 1, 1234567890, self::TEST_USER_ID]], + $this->getDatabaseConnection()->createQueryBuilder()->select( + ['hash_key', 'id', 'time', 'user_id'] + )->from('ezuser_accountkey'), + 'Expected user data to be updated.' + ); + + $handler->updateUserToken($this->getValidUserToken(2234567890)); + + $this->assertQueryResult( + [['0800fc577294c34e0b28ad2839435945', 1, 2234567890, self::TEST_USER_ID]], + $this->getDatabaseConnection()->createQueryBuilder()->select( + ['hash_key', 'id', 'time', 'user_id'] + )->from('ezuser_accountkey'), + 'Expected user token data to be updated.' + ); + } + + public function testExpireUserToken() + { + $handler = $this->getUserHandler(); + + $handler->updateUserToken($userToken = $this->getValidUserToken(1234567890)); + + $this->assertQueryResult( + [['0800fc577294c34e0b28ad2839435945', 1, 1234567890, self::TEST_USER_ID]], + $this->getDatabaseConnection()->createQueryBuilder()->select( + ['hash_key', 'id', 'time', 'user_id'] + )->from('ezuser_accountkey'), + 'Expected user data to be updated.' + ); + + $handler->expireUserToken($userToken->hashKey); + + $this->assertQueryResult( + [['0800fc577294c34e0b28ad2839435945', 1, 0, self::TEST_USER_ID]], + $this->getDatabaseConnection()->createQueryBuilder()->select( + ['hash_key', 'id', 'time', 'user_id'] + )->from('ezuser_accountkey'), + 'Expected user token to be expired.' + ); + } + + public function testDeleteNonExistingUser() + { + $handler = $this->getUserHandler(); + + $this->expectException(NotImplementedException::class); + $handler->delete(1337); + } + + public function testUpdateUser() + { + $handler = $this->getUserHandler(); + $user = $this->getValidUser(); + + $user->login = 'New_lögin'; + $this->expectException(NotImplementedException::class); + $handler->update($user); + } + + public function testUpdateUserSettings() + { + $handler = $this->getUserHandler(); + $user = $this->getValidUser(); + + $user->maxLogin = 42; + $this->expectException(NotImplementedException::class); + $handler->update($user); + } + + public function testCreateNewRoleWithoutPolicies() + { + $handler = $this->getUserHandler(); + + $createStruct = new Persistence\User\RoleCreateStruct(); + $createStruct->identifier = 'Test'; + + $handler->createRole($createStruct); + + $this->assertQueryResult( + [[1, 'Test', -1]], + $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'name', 'version')->from('ezrole'), + 'Expected a new role draft.' + ); + } + + public function testCreateRoleDraftWithoutPolicies() + { + $handler = $this->getUserHandler(); + + $createStruct = new Persistence\User\RoleCreateStruct(); + $createStruct->identifier = 'Test'; + + $roleDraft = $handler->createRole($createStruct); + $handler->publishRoleDraft($roleDraft->id); + + $handler->createRoleDraft($roleDraft->id); + + $publishedRoleId = 1; + $this->assertQueryResult( + [ + [$publishedRoleId, 'Test', APIRole::STATUS_DEFINED], + [2, 'Test', $publishedRoleId], + ], + $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'name', 'version')->from('ezrole'), + 'Expected a role and a role draft.' + ); + } + + public function testCreateNewRoleRoleId() + { + $handler = $this->getUserHandler(); + + $createStruct = new Persistence\User\RoleCreateStruct(); + $createStruct->identifier = 'Test'; + + $roleDraft = $handler->createRole($createStruct); + + $this->assertSame(1, $roleDraft->id); + } + + public function testLoadRole() + { + $handler = $this->getUserHandler(); + + $createStruct = new Persistence\User\RoleCreateStruct(); + $createStruct->identifier = 'Test'; + + $roleDraft = $handler->createRole($createStruct); + $handler->publishRoleDraft($roleDraft->id); + $role = $handler->loadRole($roleDraft->id); + + $this->assertEquals( + $roleDraft->id, + $role->id + ); + } + + public function testLoadRoleWithPolicies() + { + $handler = $this->getUserHandler(); + + $createStruct = new Persistence\User\RoleCreateStruct(); + $createStruct->identifier = 'Test'; + + $roleDraft = $handler->createRole($createStruct); + + $policy = new Persistence\User\Policy(); + $policy->module = 'foo'; + $policy->function = 'bar'; + + $handler->addPolicyByRoleDraft($roleDraft->id, $policy); + $handler->publishRoleDraft($roleDraft->id); + + $loaded = $handler->loadRole($roleDraft->id); + $this->assertEquals( + [ + new Persistence\User\Policy( + [ + 'id' => 1, + 'roleId' => 1, + 'module' => 'foo', + 'function' => 'bar', + 'limitations' => '*', + 'originalId' => null, + ] + ), + ], + $loaded->policies + ); + } + + public function testLoadRoleWithPoliciesAndGroups() + { + $handler = $this->getUserHandler(); + + $createStruct = new Persistence\User\RoleCreateStruct(); + $createStruct->identifier = 'Test'; + + $roleDraft = $handler->createRole($createStruct); + + $policy = new Persistence\User\Policy(); + $policy->module = 'foo'; + $policy->function = 'bar'; + + $handler->addPolicyByRoleDraft($roleDraft->id, $policy); + + $handler->assignRole(23, $roleDraft->id); + $handler->assignRole(42, $roleDraft->id); + + $handler->publishRoleDraft($roleDraft->id); + + $loaded = $handler->loadRole($roleDraft->id); + $this->assertEquals( + [ + new Persistence\User\Policy( + [ + 'id' => 1, + 'roleId' => 1, + 'module' => 'foo', + 'function' => 'bar', + 'limitations' => '*', + 'originalId' => null, + ] + ), + ], + $loaded->policies + ); + } + + public function testLoadRoleWithPolicyLimitations() + { + $handler = $this->getUserHandler(); + + $createStruct = new Persistence\User\RoleCreateStruct(); + $createStruct->identifier = 'Test'; + + $roleDraft = $handler->createRole($createStruct); + + $policy = new Persistence\User\Policy(); + $policy->module = 'foo'; + $policy->function = 'bar'; + $policy->limitations = [ + 'Subtree' => ['/1', '/1/2'], + 'Foo' => ['Bar'], + ]; + + $handler->addPolicyByRoleDraft($roleDraft->id, $policy); + $handler->publishRoleDraft($roleDraft->id); + + $loaded = $handler->loadRole($roleDraft->id); + $this->assertEquals( + [ + new Persistence\User\Policy( + [ + 'id' => 1, + 'roleId' => 1, + 'module' => 'foo', + 'function' => 'bar', + 'limitations' => [ + 'Subtree' => ['/1', '/1/2'], + 'Foo' => ['Bar'], + ], + 'originalId' => null, + ] + ), + ], + $loaded->policies + ); + } + + public function testLoadRoles() + { + $handler = $this->getUserHandler(); + + $this->assertEquals( + [], + $handler->loadRoles() + ); + + $role = $this->createTestRole($handler); + + $this->assertEquals( + [$role], + $handler->loadRoles() + ); + } + + public function testUpdateRole() + { + $handler = $this->getUserHandler(); + + $role = $this->createTestRole($handler); + + $update = new Persistence\User\RoleUpdateStruct(); + $update->id = $role->id; + $update->identifier = 'Changed'; + + $handler->updateRole($update); + + $this->assertQueryResult( + [[1, 'Changed']], + $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'name')->from('ezrole'), + 'Expected a changed role.' + ); + } + + public function testDeleteRole() + { + $this->insertSharedDatabaseFixture(); + $handler = $this->getUserHandler(); + + // 3 is the ID of Editor role + $handler->deleteRole(3); + + $this->assertQueryResult( + [], + $this->getDatabaseConnection()->createQueryBuilder()->select('id')->from('ezrole')->where('id = 3'), + 'Expected an empty set.' + ); + + $this->assertQueryResult( + [], + $this->getDatabaseConnection()->createQueryBuilder()->select('role_id')->from('ezpolicy')->where('role_id = 3'), + 'Expected an empty set.' + ); + + $this->assertQueryResult( + [], + $this->getDatabaseConnection()->createQueryBuilder()->select('role_id')->from('ezuser_role')->where('role_id = 3'), + 'Expected an empty set.' + ); + } + + public function testDeleteRoleDraft() + { + $this->insertSharedDatabaseFixture(); + $handler = $this->getUserHandler(); + + // 3 is the ID of Editor role + $roleDraft = $handler->createRoleDraft(3); + $handler->deleteRole($roleDraft->id, APIRole::STATUS_DRAFT); + + $this->assertQueryResult( + [['3', APIRole::STATUS_DEFINED]], + $this->getDatabaseConnection()->createQueryBuilder()->select('id, version')->from('ezrole')->where('id = 3'), + 'Expected a published role.' + ); + + $this->assertQueryResult( + [[implode("\n", array_fill(0, 28, '3, ' . APIRole::STATUS_DEFINED))]], + $this->getDatabaseConnection()->createQueryBuilder()->select('role_id, original_id')->from('ezpolicy')->where('role_id = 3'), + 'Expected 28 policies for the published role.' + ); + + $this->assertQueryResult( + [[3], [3]], + $this->getDatabaseConnection()->createQueryBuilder()->select('role_id')->from('ezuser_role')->where('role_id = 3'), + 'Expected that role assignments still exist.' + ); + } + + public function testAddPolicyToRoleLimitations() + { + $handler = $this->getUserHandler(); + + $role = $this->createTestRole($handler); + + $policy = new Persistence\User\Policy(); + $policy->module = 'foo'; + $policy->function = 'bar'; + + $handler->addPolicy($role->id, $policy); + + $this->assertQueryResult( + [[1, 'foo', 'bar', 1]], + $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'module_name', 'function_name', 'role_id')->from('ezpolicy'), + 'Expected a new policy.' + ); + } + + public function testAddPolicyPolicyId() + { + $handler = $this->getUserHandler(); + + $role = $this->createTestRole($handler); + + $policy = new Persistence\User\Policy(); + $policy->module = 'foo'; + $policy->function = 'bar'; + + $policy = $handler->addPolicy($role->id, $policy); + + $this->assertEquals(1, $policy->id); + } + + public function testAddPolicyLimitations() + { + $this->createTestRoleWithTestPolicy(); + + $this->assertQueryResult( + [ + [1, 'Subtree', 1], + [2, 'Foo', 1], + ], + $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'identifier', 'policy_id')->from('ezpolicy_limitation'), + 'Expected a new policy.' + ); + } + + public function testAddPolicyLimitationValues() + { + $this->createTestRoleWithTestPolicy(); + + $this->assertQueryResult( + [ + [1, '/1', 1], + [2, '/1/2', 1], + [3, 'Bar', 2], + ], + $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'value', 'limitation_id')->from('ezpolicy_limitation_value'), + 'Expected a new policy.' + ); + } + + protected function createRole() + { + $handler = $this->getUserHandler(); + + $policy1 = new Persistence\User\Policy(); + $policy1->module = 'foo'; + $policy1->function = 'bar'; + $policy1->limitations = [ + 'Subtree' => ['/1', '/1/2'], + 'Foo' => ['Bar'], + ]; + + $policy2 = new Persistence\User\Policy(); + $policy2->module = 'foo'; + $policy2->function = 'blubb'; + $policy2->limitations = [ + 'Foo' => ['Blubb'], + ]; + + $createStruct = new Persistence\User\RoleCreateStruct(); + $createStruct->identifier = 'Test'; + $createStruct->policies = [$policy1, $policy2]; + + return $handler->createRole($createStruct); + } + + public function testImplicitlyCreatePolicies() + { + $this->createRole(); + + $this->assertQueryResult( + [ + [1, 'foo', 'bar', 1], + [2, 'foo', 'blubb', 1], + ], + $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'module_name', 'function_name', 'role_id')->from('ezpolicy'), + 'Expected a new policy.' + ); + } + + public function testDeletePolicy() + { + $handler = $this->getUserHandler(); + + $roleDraft = $this->createRole(); + $handler->publishRoleDraft($roleDraft->id); + $handler->deletePolicy($roleDraft->policies[0]->id, $roleDraft->policies[0]->roleId); + + $this->assertQueryResult( + [ + [2, 'foo', 'blubb', 1], + ], + $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'module_name', 'function_name', 'role_id')->from('ezpolicy')->where('original_id = 0'), + 'Expected a new policy.' + ); + } + + public function testDeletePolicyLimitations() + { + $handler = $this->getUserHandler(); + + $roleDraft = $this->createRole(); + $handler->deletePolicy($roleDraft->policies[0]->id, $roleDraft->policies[0]->roleId); + + $this->assertQueryResult( + [[3, 'Foo', 2]], + $this->getDatabaseConnection()->createQueryBuilder()->select('*')->from('ezpolicy_limitation') + ); + } + + public function testDeletePolicyLimitationValues() + { + $handler = $this->getUserHandler(); + + $roleDraft = $this->createRole(); + $handler->deletePolicy($roleDraft->policies[0]->id, $roleDraft->policies[0]->roleId); + + $this->assertQueryResult( + [[4, 3, 'Blubb']], + $this->getDatabaseConnection()->createQueryBuilder()->select('*')->from('ezpolicy_limitation_value') + ); + } + + public function testUpdatePolicies() + { + $handler = $this->getUserHandler(); + + $roleDraft = $this->createRole(); + + $policy = $roleDraft->policies[0]; + $policy->limitations = [ + 'new' => ['something'], + ]; + + $handler->updatePolicy($policy); + + $this->assertQueryResult( + [ + [3, 'Foo', 2], + [4, 'new', 1], + ], + $this->getDatabaseConnection()->createQueryBuilder()->select('*')->from('ezpolicy_limitation') + ); + + $this->assertQueryResult( + [ + [4, 3, 'Blubb'], + [5, 4, 'something'], + ], + $this->getDatabaseConnection()->createQueryBuilder()->select('*')->from('ezpolicy_limitation_value') + ); + } + + public function testAddRoleToUser() + { + $handler = $this->getUserHandler(); + + $roleDraft = $this->createRole(); + $handler->publishRoleDraft($roleDraft->id); + $role = $handler->loadRole($roleDraft->id); + $user = $this->getValidUser(); + + $handler->assignRole($user->id, $role->id, []); + + $this->assertQueryResult( + [ + [1, self::TEST_USER_ID, 1, null, null], + ], + $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'contentobject_id', 'role_id', 'limit_identifier', 'limit_value')->from('ezuser_role'), + 'Expected a new user policy association.' + ); + } + + public function testAddRoleToUserWithLimitation() + { + $handler = $this->getUserHandler(); + + $roleDraft = $this->createRole(); + $handler->publishRoleDraft($roleDraft->id); + $role = $handler->loadRole($roleDraft->id); + $user = $this->getValidUser(); + + $handler->assignRole( + $user->id, + $role->id, + [ + 'Subtree' => ['/1'], + ] + ); + + $this->assertQueryResult( + [ + [1, self::TEST_USER_ID, 1, 'Subtree', '/1'], + ], + $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'contentobject_id', 'role_id', 'limit_identifier', 'limit_value')->from('ezuser_role'), + 'Expected a new user policy association.' + ); + } + + public function testAddRoleToUserWithComplexLimitation() + { + $handler = $this->getUserHandler(); + + $roleDraft = $this->createRole(); + $handler->publishRoleDraft($roleDraft->id); + $role = $handler->loadRole($roleDraft->id); + $user = $this->getValidUser(); + + $handler->assignRole( + $user->id, + $role->id, + [ + 'Subtree' => ['/1', '/1/2'], + 'Foo' => ['Bar'], + ] + ); + + $this->assertQueryResult( + [ + [1, self::TEST_USER_ID, 1, 'Subtree', '/1'], + [2, self::TEST_USER_ID, 1, 'Subtree', '/1/2'], + [3, self::TEST_USER_ID, 1, 'Foo', 'Bar'], + ], + $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'contentobject_id', 'role_id', 'limit_identifier', 'limit_value')->from('ezuser_role'), + 'Expected a new user policy association.' + ); + } + + public function testRemoveUserRoleAssociation() + { + $handler = $this->getUserHandler(); + + $roleDraft = $this->createRole(); + $handler->publishRoleDraft($roleDraft->id); + $role = $handler->loadRole($roleDraft->id); + $user = $this->getValidUser(); + + $handler->assignRole( + $user->id, + $role->id, + [ + 'Subtree' => ['/1', '/1/2'], + 'Foo' => ['Bar'], + ] + ); + + $handler->unassignRole($user->id, $role->id); + + $this->assertQueryResult( + [], + $this->getDatabaseConnection()->createQueryBuilder()->select('id', 'contentobject_id', 'role_id', 'limit_identifier', 'limit_value')->from('ezuser_role'), + 'Expected no user policy associations.' + ); + } + + public function testLoadPoliciesForUser() + { + $this->insertSharedDatabaseFixture(); + $handler = $this->getUserHandler(); + + $policies = $handler->loadPoliciesByUserId(10); // Anonymous user + + // Verify, that we received an array of Policy objects + $this->assertTrue( + array_reduce( + array_map( + static function ($policy) { + return $policy instanceof Persistence\User\Policy; + }, + $policies + ), + static function ($a, $b) { + return $a && $b; + }, + true + ) + ); + $this->assertCount(8, $policies); + } + + public function testLoadRoleAssignmentsByGroupId() + { + $this->insertSharedDatabaseFixture(); + $handler = $this->getUserHandler(); + + $this->assertEquals( + [ + new Persistence\User\RoleAssignment( + [ + 'id' => 28, + 'roleId' => 1, + 'contentId' => 11, + ] + ), + new Persistence\User\RoleAssignment( + [ + 'id' => 34, + 'roleId' => 5, + 'contentId' => 11, + ] + ), + ], + $handler->loadRoleAssignmentsByGroupId(11)// 11: Members + ); + + $this->assertEquals( + [ + new Persistence\User\RoleAssignment( + [ + 'id' => 31, + 'roleId' => 1, + 'contentId' => 42, + ] + ), + ], + $handler->loadRoleAssignmentsByGroupId(42)// 42: Anonymous users + ); + + $this->assertEquals( + [], + $handler->loadRoleAssignmentsByGroupId(10)// 10: Anonymous User + ); + } + + public function testLoadRoleAssignmentsByGroupIdInherited() + { + $this->insertSharedDatabaseFixture(); + $handler = $this->getUserHandler(); + + $this->assertEquals( + [ + new Persistence\User\RoleAssignment( + [ + 'id' => 31, + 'roleId' => 1, + 'contentId' => 42, + ] + ), + ], + $handler->loadRoleAssignmentsByGroupId(10, true)// 10: Anonymous User + ); + } + + public function testLoadComplexRoleAssignments() + { + $this->insertSharedDatabaseFixture(); + $handler = $this->getUserHandler(); + + $this->assertEquals( + [ + new Persistence\User\RoleAssignment( + [ + 'id' => 32, + 'roleId' => 3, + 'contentId' => 13, + 'limitationIdentifier' => 'Subtree', + 'values' => ['/1/2/'], + ] + ), + new Persistence\User\RoleAssignment( + [ + 'id' => 33, + 'roleId' => 3, + 'contentId' => 13, + 'limitationIdentifier' => 'Subtree', + 'values' => ['/1/43/'], + ] + ), + new Persistence\User\RoleAssignment( + [ + 'id' => 38, + 'roleId' => 5, + 'contentId' => 13, + ] + ), + ], + $handler->loadRoleAssignmentsByGroupId(13) + ); + + $this->assertEquals( + [ + new Persistence\User\RoleAssignment( + [ + 'id' => 32, + 'roleId' => 3, + 'contentId' => 13, + 'limitationIdentifier' => 'Subtree', + 'values' => ['/1/2/'], + ] + ), + new Persistence\User\RoleAssignment( + [ + 'id' => 33, + 'roleId' => 3, + 'contentId' => 13, + 'limitationIdentifier' => 'Subtree', + 'values' => ['/1/43/'], + ] + ), + new Persistence\User\RoleAssignment( + [ + 'id' => 38, + 'roleId' => 5, + 'contentId' => 13, + ] + ), + ], + $handler->loadRoleAssignmentsByGroupId(13, true) + ); + } + + public function testLoadRoleAssignmentsByRoleId(): void + { + $this->insertSharedDatabaseFixture(); + $handler = $this->getUserHandler(); + + self::assertEquals( + [ + new Persistence\User\RoleAssignment( + [ + 'id' => 28, + 'roleId' => 1, + 'contentId' => 11, + ] + ), + new Persistence\User\RoleAssignment( + [ + 'id' => 31, + 'roleId' => 1, + 'contentId' => 42, + ] + ), + new Persistence\User\RoleAssignment( + [ + 'id' => 37, + 'roleId' => 1, + 'contentId' => 59, + ] + ), + ], + $handler->loadRoleAssignmentsByRoleId(1) + ); + } + + public function testLoadRoleAssignmentsByRoleIdWithOffsetAndLimit(): void + { + $this->insertSharedDatabaseFixture(); + $handler = $this->getUserHandler(); + + self::assertEquals( + [ + new Persistence\User\RoleAssignment( + [ + 'id' => 28, + 'roleId' => 1, + 'contentId' => 11, + ] + ), + new Persistence\User\RoleAssignment( + [ + 'id' => 31, + 'roleId' => 1, + 'contentId' => 42, + ] + ), + ], + $handler->loadRoleAssignmentsByRoleIdWithOffsetAndLimit(1, 0, 2) + ); + } + + public function testLoadRoleDraftByRoleId() + { + $this->insertSharedDatabaseFixture(); + $handler = $this->getUserHandler(); + + // 3 is the ID of Editor role + $originalRoleId = 3; + $draft = $handler->createRoleDraft($originalRoleId); + $loadedDraft = $handler->loadRoleDraftByRoleId($originalRoleId); + self::assertSame($loadedDraft->originalId, $originalRoleId); + self::assertEquals($draft, $loadedDraft); + } + + public function testRoleDraftOnlyHavePolicyDraft() + { + $this->insertSharedDatabaseFixture(); + $handler = $this->getUserHandler(); + $originalRoleId = 3; + $originalRole = $handler->loadRole($originalRoleId); + $originalPolicies = []; + foreach ($originalRole->policies as $policy) { + $originalPolicies[$policy->id] = $policy; + } + + $draft = $handler->createRoleDraft($originalRoleId); + $loadedDraft = $handler->loadRole($draft->id, Role::STATUS_DRAFT); + self::assertSame($loadedDraft->originalId, $originalRoleId); + self::assertEquals($draft, $loadedDraft); + foreach ($loadedDraft->policies as $policy) { + self::assertTrue(isset($originalPolicies[$policy->originalId])); + } + + // Now add a new policy. Original ID of the new one must be the same as its actual ID. + $newPolicyModule = 'foo'; + $newPolicyFunction = 'bar'; + $policy = new Persistence\User\Policy(['module' => $newPolicyModule, 'function' => $newPolicyFunction]); + $policyDraft = $handler->addPolicyByRoleDraft($loadedDraft->id, $policy); + + // Test again by reloading the draft. + $loadedDraft = $handler->loadRole($draft->id, Role::STATUS_DRAFT); + foreach ($loadedDraft->policies as $policy) { + if ($policy->id != $policyDraft->id) { + continue; + } + + self::assertNotNull($policy->originalId); + self::assertSame($policy->id, $policy->originalId); + } + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function createTestRole(User\Handler $handler): Role + { + $createStruct = new Persistence\User\RoleCreateStruct(); + $createStruct->identifier = 'Test'; + + $roleDraft = $handler->createRole($createStruct); + $handler->publishRoleDraft($roleDraft->id); + + return $handler->loadRole($roleDraft->id); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + */ + private function createTestRoleWithTestPolicy(): void + { + $handler = $this->getUserHandler(); + + $role = $this->createTestRole($handler); + + $policy = new Persistence\User\Policy(); + $policy->module = 'foo'; + $policy->function = 'bar'; + $policy->limitations = [ + 'Subtree' => ['/1', '/1/2'], + 'Foo' => ['Bar'], + ]; + + $handler->addPolicy($role->id, $policy); + } +} + +class_alias(UserHandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\User\UserHandlerTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/User/_fixtures/roles.php b/tests/lib/Persistence/Legacy/User/_fixtures/roles.php similarity index 100% rename from eZ/Publish/Core/Persistence/Legacy/Tests/User/_fixtures/roles.php rename to tests/lib/Persistence/Legacy/User/_fixtures/roles.php diff --git a/tests/lib/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabaseTest.php b/tests/lib/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabaseTest.php new file mode 100644 index 0000000000..444fd5be30 --- /dev/null +++ b/tests/lib/Persistence/Legacy/UserPreference/Gateway/DoctrineDatabaseTest.php @@ -0,0 +1,142 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\UserPreference\Gateway; + +use Doctrine\DBAL\FetchMode; +use Doctrine\DBAL\ParameterType; +use Ibexa\Contracts\Core\Persistence\UserPreference\UserPreferenceSetStruct; +use Ibexa\Core\Persistence\Legacy\UserPreference\Gateway; +use Ibexa\Core\Persistence\Legacy\UserPreference\Gateway\DoctrineDatabase; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\UserPreference\Gateway + */ +class DoctrineDatabaseTest extends TestCase +{ + public const EXISTING_USER_PREFERENCE_ID = 1; + public const EXISTING_USER_PREFERENCE_DATA = [ + 'id' => 1, + 'user_id' => 14, + 'name' => 'timezone', + 'value' => 'America/New_York', + ]; + + protected function setUp(): void + { + parent::setUp(); + + $this->insertDatabaseFixture( + __DIR__ . '/../_fixtures/user_preferences.php' + ); + } + + public function testInsert() + { + $id = $this->getGateway()->setUserPreference(new UserPreferenceSetStruct([ + 'userId' => 14, + 'name' => 'setting_3', + 'value' => 'value_3', + ])); + + $data = $this->loadUserPreference($id); + + $this->assertEquals([ + 'id' => $id, + 'user_id' => '14', + 'name' => 'setting_3', + 'value' => 'value_3', + ], $data); + } + + public function testUpdateUserPreference() + { + $userPreference = new UserPreferenceSetStruct([ + 'userId' => 14, + 'name' => 'timezone', + 'value' => 'Europe/Warsaw', + ]); + + $this->getGateway()->setUserPreference($userPreference); + + $this->assertEquals([ + 'id' => (string) self::EXISTING_USER_PREFERENCE_ID, + 'user_id' => '14', + 'name' => 'timezone', + 'value' => 'Europe/Warsaw', + ], $this->loadUserPreference(self::EXISTING_USER_PREFERENCE_ID)); + } + + public function testCountUserPreferences() + { + $this->assertEquals(3, $this->getGateway()->countUserPreferences( + self::EXISTING_USER_PREFERENCE_DATA['user_id'] + )); + } + + public function testLoadUserPreferences() + { + $userId = 14; + $offset = 1; + $limit = 2; + + $results = $this->getGateway()->loadUserPreferences($userId, $offset, $limit); + + $this->assertEquals([ + [ + 'id' => '2', + 'user_id' => '14', + 'name' => 'setting_1', + 'value' => 'value_1', + ], + [ + 'id' => '3', + 'user_id' => '14', + 'name' => 'setting_2', + 'value' => 'value_2', + ], + ], $results); + } + + /** + * Return a ready to test DoctrineStorage gateway. + * + * @return \Ibexa\Core\Persistence\Legacy\UserPreference\Gateway + */ + protected function getGateway(): Gateway + { + return new DoctrineDatabase( + $this->getDatabaseConnection() + ); + } + + /** + * @param int $id + * + * @return array + */ + private function loadUserPreference(int $id): array + { + $queryBuilder = $this->connection->createQueryBuilder(); + $queryBuilder + ->select('id', 'user_id', 'name', 'value') + ->from('ezpreferences', 'p') + ->where( + $queryBuilder->expr()->eq( + 'p.id', + $queryBuilder->createPositionalParameter($id, ParameterType::INTEGER) + ) + ); + $result = $queryBuilder->execute()->fetchAll(FetchMode::ASSOCIATIVE); + + return reset($result); + } +} + +class_alias(DoctrineDatabaseTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\UserPreference\Gateway\DoctrineDatabaseTest'); diff --git a/tests/lib/Persistence/Legacy/UserPreference/HandlerTest.php b/tests/lib/Persistence/Legacy/UserPreference/HandlerTest.php new file mode 100644 index 0000000000..18d4205c9c --- /dev/null +++ b/tests/lib/Persistence/Legacy/UserPreference/HandlerTest.php @@ -0,0 +1,115 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\UserPreference; + +use Ibexa\Contracts\Core\Persistence\UserPreference\UserPreference; +use Ibexa\Contracts\Core\Persistence\UserPreference\UserPreferenceSetStruct; +use Ibexa\Core\Persistence\Legacy\UserPreference\Gateway; +use Ibexa\Core\Persistence\Legacy\UserPreference\Handler; +use Ibexa\Core\Persistence\Legacy\UserPreference\Mapper; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\UserPreference\Handler + */ +class HandlerTest extends TestCase +{ + public const USER_PREFERENCE_ID = 1; + + /** @var \Ibexa\Core\Persistence\Legacy\UserPreference\Gateway|\PHPUnit\Framework\MockObject\MockObject */ + private $gateway; + + /** @var \Ibexa\Core\Persistence\Legacy\UserPreference\Mapper|\PHPUnit\Framework\MockObject\MockObject */ + private $mapper; + + /** @var \Ibexa\Core\Persistence\Legacy\UserPreference\Handler */ + private $handler; + + protected function setUp(): void + { + $this->gateway = $this->createMock(Gateway::class); + $this->mapper = $this->createMock(Mapper::class); + $this->handler = new Handler($this->gateway, $this->mapper); + } + + public function testSetUserPreference() + { + $setStruct = new UserPreferenceSetStruct([ + 'userId' => 5, + 'name' => 'setting', + 'value' => 'value', + ]); + + $this->gateway + ->expects($this->once()) + ->method('setUserPreference') + ->with($setStruct) + ->willReturn(self::USER_PREFERENCE_ID); + + $this->mapper + ->expects($this->once()) + ->method('extractUserPreferencesFromRows') + ->willReturn([new UserPreference([ + 'id' => self::USER_PREFERENCE_ID, + ])]); + + $userPreference = $this->handler->setUserPreference($setStruct); + + $this->assertEquals($userPreference->id, self::USER_PREFERENCE_ID); + } + + public function testCountUserPreferences() + { + $ownerId = 10; + $expectedCount = 12; + + $this->gateway + ->expects($this->once()) + ->method('countUserPreferences') + ->with($ownerId) + ->willReturn($expectedCount); + + $this->assertEquals($expectedCount, $this->handler->countUserPreferences($ownerId)); + } + + public function testLoadUserPreferences() + { + $ownerId = 9; + $limit = 5; + $offset = 0; + + $rows = [ + ['id' => 1/* ... */], + ['id' => 2/* ... */], + ['id' => 3/* ... */], + ]; + + $objects = [ + new UserPreference(['id' => 1/* ... */]), + new UserPreference(['id' => 2/* ... */]), + new UserPreference(['id' => 3/* ... */]), + ]; + + $this->gateway + ->expects($this->once()) + ->method('loadUserPreferences') + ->with($ownerId, $offset, $limit) + ->willReturn($rows); + + $this->mapper + ->expects($this->once()) + ->method('extractUserPreferencesFromRows') + ->with($rows) + ->willReturn($objects); + + $this->assertEquals($objects, $this->handler->loadUserPreferences($ownerId, $offset, $limit)); + } +} + +class_alias(HandlerTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\UserPreference\HandlerTest'); diff --git a/tests/lib/Persistence/Legacy/UserPreference/MapperTest.php b/tests/lib/Persistence/Legacy/UserPreference/MapperTest.php new file mode 100644 index 0000000000..98483bc8ee --- /dev/null +++ b/tests/lib/Persistence/Legacy/UserPreference/MapperTest.php @@ -0,0 +1,64 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Persistence\Legacy\UserPreference; + +use Ibexa\Contracts\Core\Persistence\UserPreference\UserPreference; +use Ibexa\Core\Persistence\Legacy\UserPreference\Mapper; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Persistence\Legacy\UserPreference\Mapper + */ +class MapperTest extends TestCase +{ + /** @var \Ibexa\Core\Persistence\Legacy\UserPreference\Mapper */ + private $mapper; + + protected function setUp(): void + { + $this->mapper = new Mapper(); + } + + public function testExtractUserPreferencesFromRows() + { + $rows = [ + [ + 'id' => 1, + 'user_id' => 5, + 'name' => 'setting_1', + 'value' => 'value_1', + ], + [ + 'id' => 1, + 'user_id' => 5, + 'name' => 'setting_2', + 'value' => 'value_2', + ], + ]; + + $objects = [ + new UserPreference([ + 'id' => 1, + 'userId' => 5, + 'name' => 'setting_1', + 'value' => 'value_1', + ]), + new UserPreference([ + 'id' => 1, + 'userId' => 5, + 'name' => 'setting_2', + 'value' => 'value_2', + ]), + ]; + + $this->assertEquals($objects, $this->mapper->extractUserPreferencesFromRows($rows)); + } +} + +class_alias(MapperTest::class, 'eZ\Publish\Core\Persistence\Legacy\Tests\UserPreference\MapperTest'); diff --git a/eZ/Publish/Core/Persistence/Legacy/Tests/UserPreference/_fixtures/user_preferences.php b/tests/lib/Persistence/Legacy/UserPreference/_fixtures/user_preferences.php similarity index 100% rename from eZ/Publish/Core/Persistence/Legacy/Tests/UserPreference/_fixtures/user_preferences.php rename to tests/lib/Persistence/Legacy/UserPreference/_fixtures/user_preferences.php diff --git a/eZ/Publish/SPI/Tests/Limitation/Target/Builder/VersionBuilderTest.php b/tests/lib/Persistence/Limitation/Target/Builder/VersionBuilderTest.php similarity index 84% rename from eZ/Publish/SPI/Tests/Limitation/Target/Builder/VersionBuilderTest.php rename to tests/lib/Persistence/Limitation/Target/Builder/VersionBuilderTest.php index c8302ed738..563bc20596 100644 --- a/eZ/Publish/SPI/Tests/Limitation/Target/Builder/VersionBuilderTest.php +++ b/tests/lib/Persistence/Limitation/Target/Builder/VersionBuilderTest.php @@ -6,16 +6,16 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Tests\Limitation\Target\Builder; +namespace Ibexa\Tests\Core\Persistence\Limitation\Target\Builder; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\SPI\Limitation\Target; -use eZ\Publish\SPI\Limitation\Target\Builder\VersionBuilder; +use Ibexa\Contracts\Core\Limitation\Target; +use Ibexa\Contracts\Core\Limitation\Target\Builder\VersionBuilder; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; use PHPUnit\Framework\TestCase; /** - * @covers \eZ\Publish\SPI\Limitation\Target\Builder\VersionBuilder + * @covers \Ibexa\Contracts\Core\Limitation\Target\Builder\VersionBuilder */ class VersionBuilderTest extends TestCase { @@ -100,19 +100,17 @@ public function providerForTestBuild(): array } /** - * @covers \eZ\Publish\SPI\Limitation\Target\Builder\VersionBuilder::build - * * @dataProvider providerForTestBuild * - * @param \eZ\Publish\SPI\Limitation\Target\Version $expectedTargetVersion + * @param \Ibexa\Contracts\Core\Limitation\Target\Version $expectedTargetVersion * @param int $newStatus * @param string $initialLanguageCode - * @param \eZ\Publish\API\Repository\Values\Content\Field[] $newFields + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $newFields * @param string[] $languagesList * @param int[] $contentTypeIdsList * @param string[] $publishLanguageCodes * - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function testBuild( Target\Version $expectedTargetVersion, @@ -136,3 +134,5 @@ public function testBuild( self::assertEquals($expectedTargetVersion, $versionBuilder->build()); } } + +class_alias(VersionBuilderTest::class, 'eZ\Publish\SPI\Tests\Limitation\Target\Builder\VersionBuilderTest'); diff --git a/tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedParserTest.php b/tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedParserTest.php new file mode 100644 index 0000000000..b97711af39 --- /dev/null +++ b/tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedParserTest.php @@ -0,0 +1,42 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Persistence\TransformationProcessor; + +use Ibexa\Core\Persistence; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; + +/** + * Test case for LocationHandlerTest. + */ +class TransformationProcessorDefinitionBasedParserTest extends TestCase +{ + public static function getTestFiles() + { + return array_map( + static function ($file) { + return [realpath($file)]; + }, + glob(__DIR__ . '/_fixtures/transformations/*.tr') + ); + } + + /** + * @dataProvider getTestFiles + */ + public function testParse($file) + { + $parser = new Persistence\TransformationProcessor\DefinitionBased\Parser(); + + $fixture = include $file . '.result'; + $this->assertEquals( + $fixture, + $parser->parse($file) + ); + } +} + +class_alias(TransformationProcessorDefinitionBasedParserTest::class, 'eZ\Publish\Core\Persistence\Tests\TransformationProcessor\TransformationProcessorDefinitionBasedParserTest'); diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/TransformationProcessorDefinitionBasedTest.php b/tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedTest.php similarity index 82% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/TransformationProcessorDefinitionBasedTest.php rename to tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedTest.php index c758ac26b7..29a5003b21 100644 --- a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/TransformationProcessorDefinitionBasedTest.php +++ b/tests/lib/Persistence/TransformationProcessor/TransformationProcessorDefinitionBasedTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Tests\TransformationProcessor; +namespace Ibexa\Tests\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Persistence; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\Core\Persistence\TransformationProcessor\DefinitionBased; +use Ibexa\Core\Persistence; +use Ibexa\Core\Persistence\TransformationProcessor\DefinitionBased; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; /** * Test case for LocationHandlerTest. @@ -69,3 +69,5 @@ public function testAllNormalizations() ); } } + +class_alias(TransformationProcessorDefinitionBasedTest::class, 'eZ\Publish\Core\Persistence\Tests\TransformationProcessor\TransformationProcessorDefinitionBasedTest'); diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/TransformationProcessorPcreCompilerTest.php b/tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php similarity index 95% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/TransformationProcessorPcreCompilerTest.php rename to tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php index f1d34e7d33..5781f975ff 100644 --- a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/TransformationProcessorPcreCompilerTest.php +++ b/tests/lib/Persistence/TransformationProcessor/TransformationProcessorPcreCompilerTest.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Tests\TransformationProcessor; +namespace Ibexa\Tests\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Persistence; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; +use Ibexa\Core\Persistence; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; /** * Test case for LocationHandlerTest. @@ -213,3 +213,5 @@ public function testCompileModuloTranspose() ); } } + +class_alias(TransformationProcessorPcreCompilerTest::class, 'eZ\Publish\Core\Persistence\Tests\TransformationProcessor\TransformationProcessorPcreCompilerTest'); diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/TransformationProcessorPreprocessedBasedTest.php b/tests/lib/Persistence/TransformationProcessor/TransformationProcessorPreprocessedBasedTest.php similarity index 79% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/TransformationProcessorPreprocessedBasedTest.php rename to tests/lib/Persistence/TransformationProcessor/TransformationProcessorPreprocessedBasedTest.php index 81e5bf9a36..eac4764acb 100644 --- a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/TransformationProcessorPreprocessedBasedTest.php +++ b/tests/lib/Persistence/TransformationProcessor/TransformationProcessorPreprocessedBasedTest.php @@ -4,11 +4,11 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Persistence\Tests\TransformationProcessor; +namespace Ibexa\Tests\Core\Persistence\TransformationProcessor; -use eZ\Publish\Core\Persistence; -use eZ\Publish\Core\Persistence\Legacy\Tests\TestCase; -use eZ\Publish\Core\Persistence\TransformationProcessor\PreprocessedBased; +use Ibexa\Core\Persistence; +use Ibexa\Core\Persistence\TransformationProcessor\PreprocessedBased; +use Ibexa\Tests\Core\Persistence\Legacy\TestCase; /** * Test case for LocationHandlerTest. @@ -58,3 +58,5 @@ public function testAllNormalizations() ); } } + +class_alias(TransformationProcessorPreprocessedBasedTest::class, 'eZ\Publish\Core\Persistence\Tests\TransformationProcessor\TransformationProcessorPreprocessedBasedTest'); diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/ascii.tr b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/ascii.tr similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/ascii.tr rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/ascii.tr diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/ascii.tr.result b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/ascii.tr.result similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/ascii.tr.result rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/ascii.tr.result diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/basic.tr b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/basic.tr similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/basic.tr rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/basic.tr diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/basic.tr.result b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/basic.tr.result similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/basic.tr.result rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/basic.tr.result diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/cyrillic.tr b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/cyrillic.tr similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/cyrillic.tr rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/cyrillic.tr diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/cyrillic.tr.result b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/cyrillic.tr.result similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/cyrillic.tr.result rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/cyrillic.tr.result diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/greek.tr b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/greek.tr similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/greek.tr rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/greek.tr diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/greek.tr.result b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/greek.tr.result similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/greek.tr.result rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/greek.tr.result diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/hebrew.tr b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/hebrew.tr similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/hebrew.tr rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/hebrew.tr diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/hebrew.tr.result b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/hebrew.tr.result similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/hebrew.tr.result rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/hebrew.tr.result diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/latin.tr b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/latin.tr similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/latin.tr rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/latin.tr diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/latin.tr.result b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/latin.tr.result similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/latin.tr.result rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/latin.tr.result diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/search.tr b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/search.tr similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/search.tr rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/search.tr diff --git a/eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/search.tr.result b/tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/search.tr.result similarity index 100% rename from eZ/Publish/Core/Persistence/Tests/TransformationProcessor/_fixtures/transformations/search.tr.result rename to tests/lib/Persistence/TransformationProcessor/_fixtures/transformations/search.tr.result diff --git a/tests/lib/Query/QueryFactoryTest.php b/tests/lib/Query/QueryFactoryTest.php new file mode 100644 index 0000000000..d15fc70316 --- /dev/null +++ b/tests/lib/Query/QueryFactoryTest.php @@ -0,0 +1,64 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Query; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Core\Query\QueryFactory; +use Ibexa\Core\QueryType\QueryType; +use Ibexa\Core\QueryType\QueryTypeRegistry; +use Ibexa\Tests\Core\Search\TestCase; + +final class QueryFactoryTest extends TestCase +{ + private const EXAMPLE_QUERY_TYPE = 'Example'; + private const EXAMPLE_QUERY_PARAMS = [ + 'foo' => 'foo', + 'bar' => 'bar', + 'baz' => 'baz', + ]; + + /** @var \Ibexa\Core\QueryType\QueryTypeRegistry|\PHPUnit\Framework\MockObject\MockObject */ + private $queryTypeRegistry; + + /** @var \Ibexa\Core\Query\QueryFactory */ + private $queryFactory; + + protected function setUp(): void + { + $this->queryTypeRegistry = $this->createMock(QueryTypeRegistry::class); + $this->queryFactory = new QueryFactory($this->queryTypeRegistry); + } + + public function testCreate(): void + { + $expectedQuery = new Query(); + + $queryType = $this->createMock(QueryType::class); + $queryType + ->expects($this->once()) + ->method('getQuery') + ->with(self::EXAMPLE_QUERY_PARAMS) + ->willReturn($expectedQuery); + + $this->queryTypeRegistry + ->expects($this->once()) + ->method('getQueryType') + ->with(self::EXAMPLE_QUERY_TYPE) + ->willReturn($queryType); + + $actualQuery = $this->queryFactory->create( + self::EXAMPLE_QUERY_TYPE, + self::EXAMPLE_QUERY_PARAMS + ); + + $this->assertEquals($expectedQuery, $actualQuery); + } +} + +class_alias(QueryFactoryTest::class, 'eZ\Publish\Core\Query\Tests\QueryFactoryTest'); diff --git a/tests/lib/QueryType/BuiltIn/AbstractQueryTypeTest.php b/tests/lib/QueryType/BuiltIn/AbstractQueryTypeTest.php new file mode 100644 index 0000000000..2a53114f68 --- /dev/null +++ b/tests/lib/QueryType/BuiltIn/AbstractQueryTypeTest.php @@ -0,0 +1,105 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\QueryType\BuiltIn; + +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\QueryType\BuiltIn\SortClausesFactoryInterface; +use Ibexa\Core\QueryType\QueryType; +use Ibexa\Core\Repository\Values\Content\Location; +use PHPUnit\Framework\TestCase; + +abstract class AbstractQueryTypeTest extends TestCase +{ + protected const ROOT_LOCATION_ID = 2; + protected const ROOT_LOCATION_PATH_STRING = '/1/2/'; + + /** @var \Ibexa\Contracts\Core\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject */ + private $repository; + + /** @var \Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $configResolver; + + /** @var \Ibexa\Core\QueryType\BuiltIn\SortClausesFactoryInterface|\PHPUnit\Framework\MockObject\MockObject */ + private $sortClausesFactory; + + /** @var \Ibexa\Core\QueryType\QueryType */ + private $queryType; + + protected function setUp(): void + { + $rootLocation = new Location([ + 'id' => self::ROOT_LOCATION_ID, + 'pathString' => self::ROOT_LOCATION_PATH_STRING, + ]); + + $locationService = $this->createMock(LocationService::class); + $locationService + ->method('loadLocation') + ->with(self::ROOT_LOCATION_ID) + ->willReturn($rootLocation); + + $this->repository = $this->createMock(Repository::class); + $this->repository->method('getLocationService')->willReturn($locationService); + + $this->configResolver = $this->createMock(ConfigResolverInterface::class); + $this->configResolver + ->method('getParameter') + ->with('content.tree_root.location_id') + ->willReturn(self::ROOT_LOCATION_ID); + + $this->sortClausesFactory = $this->createMock(SortClausesFactoryInterface::class); + + $this->queryType = $this->createQueryType( + $this->repository, + $this->configResolver, + $this->sortClausesFactory + ); + } + + /** + * @dataProvider dataProviderForGetQuery + */ + final public function testGetQuery(array $parameters, Query $expectedQuery): void + { + $this->assertEquals($expectedQuery, $this->queryType->getQuery($parameters)); + } + + final public function testGetName(): void + { + $this->assertEquals( + $this->getExpectedName(), + $this->queryType->getName() + ); + } + + final public function testGetSupportedParameters(): void + { + $this->assertEqualsCanonicalizing( + $this->getExpectedSupportedParameters(), + $this->queryType->getSupportedParameters() + ); + } + + abstract public function dataProviderForGetQuery(): iterable; + + abstract protected function createQueryType( + Repository $repository, + ConfigResolverInterface $configResolver, + SortClausesFactoryInterface $sortClausesFactory + ): QueryType; + + abstract protected function getExpectedName(): string; + + abstract protected function getExpectedSupportedParameters(): array; +} + +class_alias(AbstractQueryTypeTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\Tests\AbstractQueryTypeTest'); diff --git a/eZ/Publish/Core/QueryType/BuiltIn/Tests/AncestorsQueryTypeTest.php b/tests/lib/QueryType/BuiltIn/AncestorsQueryTypeTest.php similarity index 82% rename from eZ/Publish/Core/QueryType/BuiltIn/Tests/AncestorsQueryTypeTest.php rename to tests/lib/QueryType/BuiltIn/AncestorsQueryTypeTest.php index e4e945226b..f4df2fbae5 100644 --- a/eZ/Publish/Core/QueryType/BuiltIn/Tests/AncestorsQueryTypeTest.php +++ b/tests/lib/QueryType/BuiltIn/AncestorsQueryTypeTest.php @@ -6,24 +6,24 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\QueryType\BuiltIn\Tests; +namespace Ibexa\Tests\Core\QueryType\BuiltIn; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Ancestor; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LocationId; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalNot; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Visibility; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location\Priority; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\QueryType\BuiltIn\AncestorsQueryType; -use eZ\Publish\Core\QueryType\BuiltIn\SortClausesFactoryInterface; -use eZ\Publish\Core\QueryType\QueryType; -use eZ\Publish\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Ancestor; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LocationId; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalAnd; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalNot; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Subtree; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Visibility; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Location\Priority; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\QueryType\BuiltIn\AncestorsQueryType; +use Ibexa\Core\QueryType\BuiltIn\SortClausesFactoryInterface; +use Ibexa\Core\QueryType\QueryType; +use Ibexa\Core\Repository\Values\Content\Location; final class AncestorsQueryTypeTest extends AbstractQueryTypeTest { @@ -188,3 +188,5 @@ protected function getExpectedSupportedParameters(): array return ['filter', 'offset', 'limit', 'sort', 'location', 'content']; } } + +class_alias(AncestorsQueryTypeTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\Tests\AncestorsQueryTypeTest'); diff --git a/eZ/Publish/Core/QueryType/BuiltIn/Tests/ChildrenQueryTypeTest.php b/tests/lib/QueryType/BuiltIn/ChildrenQueryTypeTest.php similarity index 79% rename from eZ/Publish/Core/QueryType/BuiltIn/Tests/ChildrenQueryTypeTest.php rename to tests/lib/QueryType/BuiltIn/ChildrenQueryTypeTest.php index fa57b45b36..a4d84bd0af 100644 --- a/eZ/Publish/Core/QueryType/BuiltIn/Tests/ChildrenQueryTypeTest.php +++ b/tests/lib/QueryType/BuiltIn/ChildrenQueryTypeTest.php @@ -6,22 +6,22 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\QueryType\BuiltIn\Tests; +namespace Ibexa\Tests\Core\QueryType\BuiltIn; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ParentLocationId; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Visibility; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location\Priority; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\QueryType\BuiltIn\ChildrenQueryType; -use eZ\Publish\Core\QueryType\BuiltIn\SortClausesFactoryInterface; -use eZ\Publish\Core\QueryType\QueryType; -use eZ\Publish\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalAnd; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ParentLocationId; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Subtree; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Visibility; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Location\Priority; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\QueryType\BuiltIn\ChildrenQueryType; +use Ibexa\Core\QueryType\BuiltIn\SortClausesFactoryInterface; +use Ibexa\Core\QueryType\QueryType; +use Ibexa\Core\Repository\Values\Content\Location; final class ChildrenQueryTypeTest extends AbstractQueryTypeTest { @@ -154,3 +154,5 @@ protected function getExpectedSupportedParameters(): array return ['filter', 'offset', 'limit', 'sort', 'location', 'content']; } } + +class_alias(ChildrenQueryTypeTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\Tests\ChildrenQueryTypeTest'); diff --git a/eZ/Publish/Core/QueryType/BuiltIn/Tests/GeoLocationQueryTypeTest.php b/tests/lib/QueryType/BuiltIn/GeoLocationQueryTypeTest.php similarity index 80% rename from eZ/Publish/Core/QueryType/BuiltIn/Tests/GeoLocationQueryTypeTest.php rename to tests/lib/QueryType/BuiltIn/GeoLocationQueryTypeTest.php index 57ff21885f..0f9045c0f8 100644 --- a/eZ/Publish/Core/QueryType/BuiltIn/Tests/GeoLocationQueryTypeTest.php +++ b/tests/lib/QueryType/BuiltIn/GeoLocationQueryTypeTest.php @@ -6,21 +6,21 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\QueryType\BuiltIn\Tests; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MapLocationDistance; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Visibility; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\ContentName; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\QueryType\BuiltIn\GeoLocationQueryType; -use eZ\Publish\Core\QueryType\BuiltIn\SortClausesFactoryInterface; -use eZ\Publish\Core\QueryType\QueryType; +namespace Ibexa\Tests\Core\QueryType\BuiltIn; + +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalAnd; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\MapLocationDistance; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Subtree; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Visibility; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\ContentName; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\QueryType\BuiltIn\GeoLocationQueryType; +use Ibexa\Core\QueryType\BuiltIn\SortClausesFactoryInterface; +use Ibexa\Core\QueryType\QueryType; final class GeoLocationQueryTypeTest extends AbstractQueryTypeTest { @@ -160,3 +160,5 @@ protected function getExpectedSupportedParameters(): array return ['filter', 'offset', 'limit', 'sort', 'field', 'distance', 'latitude', 'longitude', 'operator']; } } + +class_alias(GeoLocationQueryTypeTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\Tests\GeoLocationQueryTypeTest'); diff --git a/eZ/Publish/Core/QueryType/BuiltIn/Tests/RelatedToContentQueryTypeTest.php b/tests/lib/QueryType/BuiltIn/RelatedToContentQueryTypeTest.php similarity index 82% rename from eZ/Publish/Core/QueryType/BuiltIn/Tests/RelatedToContentQueryTypeTest.php rename to tests/lib/QueryType/BuiltIn/RelatedToContentQueryTypeTest.php index 137e282af5..03dec16712 100644 --- a/eZ/Publish/Core/QueryType/BuiltIn/Tests/RelatedToContentQueryTypeTest.php +++ b/tests/lib/QueryType/BuiltIn/RelatedToContentQueryTypeTest.php @@ -6,21 +6,21 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\QueryType\BuiltIn\Tests; +namespace Ibexa\Tests\Core\QueryType\BuiltIn; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\FieldRelation; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Visibility; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\ContentName; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\QueryType\BuiltIn\RelatedToContentQueryType; -use eZ\Publish\Core\QueryType\BuiltIn\SortClausesFactoryInterface; -use eZ\Publish\Core\QueryType\QueryType; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\FieldRelation; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalAnd; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Subtree; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Visibility; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\ContentName; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\QueryType\BuiltIn\RelatedToContentQueryType; +use Ibexa\Core\QueryType\BuiltIn\SortClausesFactoryInterface; +use Ibexa\Core\QueryType\QueryType; final class RelatedToContentQueryTypeTest extends AbstractQueryTypeTest { @@ -156,3 +156,5 @@ protected function getExpectedSupportedParameters(): array return ['filter', 'offset', 'limit', 'sort', 'content', 'field']; } } + +class_alias(RelatedToContentQueryTypeTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\Tests\RelatedToContentQueryTypeTest'); diff --git a/eZ/Publish/Core/QueryType/BuiltIn/Tests/SiblingsQueryTypeTest.php b/tests/lib/QueryType/BuiltIn/SiblingsQueryTypeTest.php similarity index 79% rename from eZ/Publish/Core/QueryType/BuiltIn/Tests/SiblingsQueryTypeTest.php rename to tests/lib/QueryType/BuiltIn/SiblingsQueryTypeTest.php index f0b097e72b..117726f23f 100644 --- a/eZ/Publish/Core/QueryType/BuiltIn/Tests/SiblingsQueryTypeTest.php +++ b/tests/lib/QueryType/BuiltIn/SiblingsQueryTypeTest.php @@ -6,22 +6,22 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\QueryType\BuiltIn\Tests; +namespace Ibexa\Tests\Core\QueryType\BuiltIn; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Sibling; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Visibility; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location\Priority; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SiblingsQueryType; -use eZ\Publish\Core\QueryType\BuiltIn\SortClausesFactoryInterface; -use eZ\Publish\Core\QueryType\QueryType; -use eZ\Publish\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalAnd; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Sibling; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Subtree; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Visibility; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Location\Priority; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\QueryType\BuiltIn\SiblingsQueryType; +use Ibexa\Core\QueryType\BuiltIn\SortClausesFactoryInterface; +use Ibexa\Core\QueryType\QueryType; +use Ibexa\Core\Repository\Values\Content\Location; final class SiblingsQueryTypeTest extends AbstractQueryTypeTest { @@ -155,3 +155,5 @@ protected function getExpectedSupportedParameters(): array return ['filter', 'offset', 'limit', 'sort', 'location', 'content']; } } + +class_alias(SiblingsQueryTypeTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\Tests\SiblingsQueryTypeTest'); diff --git a/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/CustomFieldSortClauseParserTest.php b/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/CustomFieldSortClauseParserTest.php new file mode 100644 index 0000000000..2b6f314155 --- /dev/null +++ b/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/CustomFieldSortClauseParserTest.php @@ -0,0 +1,60 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\QueryType\BuiltIn\SortSpec\SortClauseParser; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\CustomField; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\CustomFieldSortClauseParser; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\Token; +use PHPUnit\Framework\TestCase; + +final class CustomFieldSortClauseParserTest extends TestCase +{ + private const EXAMPLE_SEARCH_INDEX_FIELD = 'custom_field_s'; + + /** @var \Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\CustomFieldSortClauseParser */ + private $parser; + + protected function setUp(): void + { + $this->parser = new CustomFieldSortClauseParser(); + } + + public function testParse(): void + { + $parser = $this->createMock(SortSpecParserInterface::class); + $parser + ->method('match') + ->with(Token::TYPE_ID) + ->willReturn( + new Token( + Token::TYPE_ID, + self::EXAMPLE_SEARCH_INDEX_FIELD + ), + ); + + $parser->method('parseSortDirection')->willReturn(Query::SORT_ASC); + + $this->assertEquals( + new CustomField( + self::EXAMPLE_SEARCH_INDEX_FIELD, + Query::SORT_ASC + ), + $this->parser->parse($parser, 'custom_field') + ); + } + + public function testSupports(): void + { + $this->assertTrue($this->parser->supports('custom_field')); + } +} + +class_alias(CustomFieldSortClauseParserTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortClauseParser\CustomFieldSortClauseParserTest'); diff --git a/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/DefaultSortClauseParserTest.php b/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/DefaultSortClauseParserTest.php new file mode 100644 index 0000000000..8ad71c8939 --- /dev/null +++ b/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/DefaultSortClauseParserTest.php @@ -0,0 +1,73 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\QueryType\BuiltIn\SortSpec\SortClauseParser; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Location; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\Exception\UnsupportedSortClauseException; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\DefaultSortClauseParser; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; +use PHPUnit\Framework\TestCase; + +final class DefaultSortClauseParserTest extends TestCase +{ + /** @var \Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\DefaultSortClauseParser */ + private $defaultSortClauseParser; + + protected function setUp(): void + { + $this->defaultSortClauseParser = new DefaultSortClauseParser([ + 'depth' => Location\Depth::class, + 'priority' => Location\Priority::class, + 'id' => Location\Id::class, + ]); + } + + public function testParse(): void + { + $parser = $this->createMock(SortSpecParserInterface::class); + $parser->method('parseSortDirection')->willReturn(Query::SORT_ASC); + + $this->assertEquals( + new Location\Depth(Query::SORT_ASC), + $this->defaultSortClauseParser->parse($parser, 'depth') + ); + + $this->assertEquals( + new Location\Priority(Query::SORT_ASC), + $this->defaultSortClauseParser->parse($parser, 'priority') + ); + } + + public function testParseThrowsUnsupportedSortClauseException(): void + { + $this->expectException(UnsupportedSortClauseException::class); + $this->expectExceptionMessage(sprintf( + 'Could not find %s for unsupported sort clause', + SortClauseParserInterface::class + )); + + $this->defaultSortClauseParser->parse( + $this->createMock(SortSpecParserInterface::class), + 'unsupported' + ); + } + + public function testSupports(): void + { + $this->assertTrue($this->defaultSortClauseParser->supports('depth')); + $this->assertTrue($this->defaultSortClauseParser->supports('priority')); + $this->assertTrue($this->defaultSortClauseParser->supports('id')); + + $this->assertFalse($this->defaultSortClauseParser->supports('unsupported')); + } +} + +class_alias(DefaultSortClauseParserTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortClauseParser\DefaultSortClauseParserTest'); diff --git a/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/FieldSortClauseParserTest.php b/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/FieldSortClauseParserTest.php new file mode 100644 index 0000000000..67f8f516fd --- /dev/null +++ b/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/FieldSortClauseParserTest.php @@ -0,0 +1,61 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\QueryType\BuiltIn\SortSpec\SortClauseParser; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Field; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\FieldSortClauseParser; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\Token; +use PHPUnit\Framework\TestCase; + +final class FieldSortClauseParserTest extends TestCase +{ + private const EXAMPLE_CONTENT_TYPE_ID = 'article'; + private const EXAMPLE_FIELD_ID = 'title'; + + /** @var \Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\FieldSortClauseParser */ + private $fieldSortClauseParser; + + protected function setUp(): void + { + $this->fieldSortClauseParser = new FieldSortClauseParser(); + } + + public function testParse(): void + { + $parser = $this->createMock(SortSpecParserInterface::class); + $parser + ->method('match') + ->withConsecutive( + [Token::TYPE_ID], + [Token::TYPE_DOT], + [Token::TYPE_ID] + ) + ->willReturnOnConsecutiveCalls( + new Token(Token::TYPE_ID, self::EXAMPLE_CONTENT_TYPE_ID), + new Token(Token::TYPE_DOT), + new Token(Token::TYPE_ID, self::EXAMPLE_FIELD_ID) + ); + + $parser->method('parseSortDirection')->willReturn(Query::SORT_ASC); + + $this->assertEquals( + new Field(self::EXAMPLE_CONTENT_TYPE_ID, self::EXAMPLE_FIELD_ID, Query::SORT_ASC), + $this->fieldSortClauseParser->parse($parser, 'field') + ); + } + + public function testSupports(): void + { + $this->assertTrue($this->fieldSortClauseParser->supports('field')); + } +} + +class_alias(FieldSortClauseParserTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortClauseParser\FieldSortClauseParserTest'); diff --git a/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/MapDistanceSortClauseParserTest.php b/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/MapDistanceSortClauseParserTest.php new file mode 100644 index 0000000000..53e79cc717 --- /dev/null +++ b/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/MapDistanceSortClauseParserTest.php @@ -0,0 +1,73 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\QueryType\BuiltIn\SortSpec\SortClauseParser; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\MapLocationDistance; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\MapDistanceSortClauseParser; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\Token; +use PHPUnit\Framework\TestCase; + +final class MapDistanceSortClauseParserTest extends TestCase +{ + private const EXAMPLE_CONTENT_TYPE_ID = 'place'; + private const EXAMPLE_FIELD_ID = 'location'; + private const EXAMPLE_LAT = 50.0647; + private const EXAMPLE_LON = 19.9450; + + /** @var \Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\MapDistanceSortClauseParser */ + private $mapDistanceSortClauseParser; + + protected function setUp(): void + { + $this->mapDistanceSortClauseParser = new MapDistanceSortClauseParser(); + } + + public function testParse(): void + { + $parser = $this->createMock(SortSpecParserInterface::class); + $parser + ->method('match') + ->withConsecutive( + [Token::TYPE_ID], + [Token::TYPE_DOT], + [Token::TYPE_ID], + [Token::TYPE_FLOAT], + [Token::TYPE_FLOAT] + ) + ->willReturnOnConsecutiveCalls( + new Token(Token::TYPE_ID, self::EXAMPLE_CONTENT_TYPE_ID), + new Token(Token::TYPE_DOT), + new Token(Token::TYPE_ID, self::EXAMPLE_FIELD_ID), + new Token(Token::TYPE_FLOAT, (string)self::EXAMPLE_LAT), + new Token(Token::TYPE_FLOAT, (string)self::EXAMPLE_LON) + ); + + $parser->method('parseSortDirection')->willReturn(Query::SORT_ASC); + + $this->assertEquals( + new MapLocationDistance( + self::EXAMPLE_CONTENT_TYPE_ID, + self::EXAMPLE_FIELD_ID, + self::EXAMPLE_LAT, + self::EXAMPLE_LON, + Query::SORT_ASC + ), + $this->mapDistanceSortClauseParser->parse($parser, 'map_distance') + ); + } + + public function testSupports(): void + { + $this->assertTrue($this->mapDistanceSortClauseParser->supports('map_distance')); + } +} + +class_alias(MapDistanceSortClauseParserTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortClauseParser\MapDistanceSortClauseParserTest'); diff --git a/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/RandomSortClauseParserTest.php b/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/RandomSortClauseParserTest.php new file mode 100644 index 0000000000..ef8ad4af4c --- /dev/null +++ b/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParser/RandomSortClauseParserTest.php @@ -0,0 +1,57 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\QueryType\BuiltIn\SortSpec\SortClauseParser; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Random; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\RandomSortClauseParser; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\Token; +use PHPUnit\Framework\TestCase; + +final class RandomSortClauseParserTest extends TestCase +{ + private const EXAMPLE_SEED = 1; + + /** @var \Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParser\RandomSortClauseParser */ + private $randomSortClauseParser; + + protected function setUp(): void + { + $this->randomSortClauseParser = new RandomSortClauseParser(); + } + + public function testParse(): void + { + $parser = $this->createMock(SortSpecParserInterface::class); + $parser + ->method('isNextToken') + ->with(Token::TYPE_INT) + ->willReturn(true); + + $parser + ->method('match') + ->with(Token::TYPE_INT) + ->willReturn(new Token(Token::TYPE_INT, (string)self::EXAMPLE_SEED)); + + $parser->method('parseSortDirection')->willReturn(Query::SORT_ASC); + + $this->assertEquals( + new Random(self::EXAMPLE_SEED, Query::SORT_ASC), + $this->randomSortClauseParser->parse($parser, 'random') + ); + } + + public function testSupports(): void + { + $this->assertTrue($this->randomSortClauseParser->supports('random')); + } +} + +class_alias(RandomSortClauseParserTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortClauseParser\RandomSortClauseParserTest'); diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParserDispatcherTest.php b/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParserDispatcherTest.php similarity index 78% rename from eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParserDispatcherTest.php rename to tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParserDispatcherTest.php index f377ba2582..a324383604 100644 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortClauseParserDispatcherTest.php +++ b/tests/lib/QueryType/BuiltIn/SortSpec/SortClauseParserDispatcherTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests; +namespace Ibexa\Tests\Core\QueryType\BuiltIn\SortSpec; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Exception\UnsupportedSortClauseException; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserDispatcher; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\Exception\UnsupportedSortClauseException; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParserDispatcher; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortSpecParserInterface; use PHPUnit\Framework\TestCase; final class SortClauseParserDispatcherTest extends TestCase @@ -62,3 +62,5 @@ public function testSupports(): void $this->assertTrue($dispatcher->supports(self::EXAMPLE_SORT_CLAUSE)); } } + +class_alias(SortClauseParserDispatcherTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortClauseParserDispatcherTest'); diff --git a/tests/lib/QueryType/BuiltIn/SortSpec/SortSpecLexerStub.php b/tests/lib/QueryType/BuiltIn/SortSpec/SortSpecLexerStub.php new file mode 100644 index 0000000000..7d40172e25 --- /dev/null +++ b/tests/lib/QueryType/BuiltIn/SortSpec/SortSpecLexerStub.php @@ -0,0 +1,62 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\QueryType\BuiltIn\SortSpec; + +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortSpecLexerInterface; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\Token; + +/** + * Dummy {@see \Ibexa\Core\QueryType\BuiltIn\SortSpec\SortSpecLexerInterface} implementation. + */ +final class SortSpecLexerStub implements SortSpecLexerInterface +{ + /** @var \Ibexa\Core\QueryType\BuiltIn\SortSpec\Token[] */ + private $tokens; + + /** @var string|null */ + private $input; + + /** @var int */ + private $position; + + public function __construct(array $tokens = []) + { + $this->tokens = $tokens; + $this->position = -1; + } + + public function consume(): Token + { + ++$this->position; + + return $this->tokens[$this->position]; + } + + public function isEOF(): bool + { + return $this->position + 1 >= count($this->tokens) - 1; + } + + public function tokenize(string $input): void + { + $this->input = $input; + } + + public function getInput(): string + { + return (string)$this->input; + } + + public function peek(): ?Token + { + return $this->tokens[$this->position + 1] ?? null; + } +} + +class_alias(SortSpecLexerStub::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortSpecLexerStub'); diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortSpecLexerTest.php b/tests/lib/QueryType/BuiltIn/SortSpec/SortSpecLexerTest.php similarity index 94% rename from eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortSpecLexerTest.php rename to tests/lib/QueryType/BuiltIn/SortSpec/SortSpecLexerTest.php index 10ef1e40de..5535ef5837 100644 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortSpecLexerTest.php +++ b/tests/lib/QueryType/BuiltIn/SortSpec/SortSpecLexerTest.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests; +namespace Ibexa\Tests\Core\QueryType\BuiltIn\SortSpec; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecLexer; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Token; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortSpecLexer; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\Token; use PHPUnit\Framework\TestCase; final class SortSpecLexerTest extends TestCase @@ -185,3 +185,5 @@ public function testConsume(): void ], $output); } } + +class_alias(SortSpecLexerTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortSpecLexerTest'); diff --git a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortSpecParserTest.php b/tests/lib/QueryType/BuiltIn/SortSpec/SortSpecParserTest.php similarity index 87% rename from eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortSpecParserTest.php rename to tests/lib/QueryType/BuiltIn/SortSpec/SortSpecParserTest.php index d8af2b633e..1f04b168f9 100644 --- a/eZ/Publish/Core/QueryType/BuiltIn/SortSpec/Tests/SortSpecParserTest.php +++ b/tests/lib/QueryType/BuiltIn/SortSpec/SortSpecParserTest.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests; +namespace Ibexa\Tests\Core\QueryType\BuiltIn\SortSpec; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecLexerInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\SortSpecParser; -use eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Token; -use eZ\Publish\Core\Search\Tests\TestCase; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortClauseParserInterface; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortSpecLexerInterface; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\SortSpecParser; +use Ibexa\Core\QueryType\BuiltIn\SortSpec\Token; +use Ibexa\Tests\Core\Search\TestCase; final class SortSpecParserTest extends TestCase { @@ -135,3 +135,5 @@ public function testMatchAny(): void $this->assertEquals($token, $parser->matchAnyOf(Token::TYPE_ASC, Token::TYPE_DESC)); } } + +class_alias(SortSpecParserTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\SortSpec\Tests\SortSpecParserTest'); diff --git a/eZ/Publish/Core/QueryType/BuiltIn/Tests/SubtreeQueryTest.php b/tests/lib/QueryType/BuiltIn/SubtreeQueryTest.php similarity index 81% rename from eZ/Publish/Core/QueryType/BuiltIn/Tests/SubtreeQueryTest.php rename to tests/lib/QueryType/BuiltIn/SubtreeQueryTest.php index 65f6fe398e..a63f05c5f7 100644 --- a/eZ/Publish/Core/QueryType/BuiltIn/Tests/SubtreeQueryTest.php +++ b/tests/lib/QueryType/BuiltIn/SubtreeQueryTest.php @@ -6,23 +6,23 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\QueryType\BuiltIn\Tests; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Location\Depth; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\LogicalAnd; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Operator; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Subtree; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\Visibility; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause\Location\Priority; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SortClausesFactoryInterface; -use eZ\Publish\Core\QueryType\BuiltIn\SubtreeQueryType; -use eZ\Publish\Core\QueryType\QueryType; -use eZ\Publish\Core\Repository\Values\Content\Location; +namespace Ibexa\Tests\Core\QueryType\BuiltIn; + +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ContentTypeIdentifier; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Location\Depth; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LogicalAnd; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Operator; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Subtree; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\Visibility; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause\Location\Priority; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\QueryType\BuiltIn\SortClausesFactoryInterface; +use Ibexa\Core\QueryType\BuiltIn\SubtreeQueryType; +use Ibexa\Core\QueryType\QueryType; +use Ibexa\Core\Repository\Values\Content\Location; final class SubtreeQueryTest extends AbstractQueryTypeTest { @@ -176,3 +176,5 @@ protected function getExpectedSupportedParameters(): array return ['filter', 'offset', 'limit', 'sort', 'location', 'content', 'depth']; } } + +class_alias(SubtreeQueryTest::class, 'eZ\Publish\Core\QueryType\BuiltIn\Tests\SubtreeQueryTest'); diff --git a/eZ/Publish/API/Repository/Tests/Common/FacetedSearchProvider.php b/tests/lib/Repository/Common/FacetedSearchProvider.php similarity index 94% rename from eZ/Publish/API/Repository/Tests/Common/FacetedSearchProvider.php rename to tests/lib/Repository/Common/FacetedSearchProvider.php index 509d2488b0..1c290d098e 100644 --- a/eZ/Publish/API/Repository/Tests/Common/FacetedSearchProvider.php +++ b/tests/lib/Repository/Common/FacetedSearchProvider.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\Common; +namespace Ibexa\Tests\Core\Repository\Common; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\FacetBuilder; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\FacetBuilder; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; /** * Provider for facet tests against SearchService. @@ -18,8 +18,8 @@ * - class const: QUERY_CLASS * - method: getFixtureDir * - * @see \eZ\Publish\API\Repository\Tests\SearchServiceTest - * @see \eZ\Publish\API\Repository\Tests\SearchServiceLocationTest + * @see \Ibexa\Tests\Integration\Core\Repository\SearchServiceTest + * @see \Ibexa\Tests\Integration\Core\Repository\SearchServiceLocationTest */ trait FacetedSearchProvider { @@ -265,3 +265,5 @@ public function getFacetedSearches() ]; } } + +class_alias(FacetedSearchProvider::class, 'eZ\Publish\API\Repository\Tests\Common\FacetedSearchProvider'); diff --git a/tests/lib/Repository/ContentServiceTest.php b/tests/lib/Repository/ContentServiceTest.php index 8b157812e5..ce2e14a2aa 100644 --- a/tests/lib/Repository/ContentServiceTest.php +++ b/tests/lib/Repository/ContentServiceTest.php @@ -8,18 +8,18 @@ namespace Ibexa\Tests\Core\Repository; -use eZ\Publish\API\Repository\PermissionService; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\Repository\ContentService; -use eZ\Publish\Core\Repository\Helper\NameSchemaService; -use eZ\Publish\Core\Repository\Helper\RelationProcessor; -use eZ\Publish\Core\Repository\Mapper\ContentDomainMapper; -use eZ\Publish\Core\Repository\Mapper\ContentMapper; -use eZ\Publish\SPI\Persistence\Filter\Content\Handler as ContentFilteringHandler; -use eZ\Publish\SPI\Persistence\Handler as PersistenceHandler; -use eZ\Publish\SPI\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Persistence\Filter\Content\Handler as ContentFilteringHandler; +use Ibexa\Contracts\Core\Persistence\Handler as PersistenceHandler; +use Ibexa\Contracts\Core\Repository\NameSchema\NameSchemaServiceInterface; +use Ibexa\Contracts\Core\Repository\PermissionService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use Ibexa\Core\FieldType\FieldTypeRegistry; +use Ibexa\Core\Repository\ContentService; +use Ibexa\Core\Repository\Helper\RelationProcessor; +use Ibexa\Core\Repository\Mapper\ContentDomainMapper; +use Ibexa\Core\Repository\Mapper\ContentMapper; use PHPUnit\Framework\TestCase; /** @@ -27,7 +27,7 @@ */ final class ContentServiceTest extends TestCase { - /** @var \eZ\Publish\API\Repository\ContentService */ + /** @var \Ibexa\Contracts\Core\Repository\ContentService */ private $contentService; protected function setUp(): void @@ -37,7 +37,7 @@ protected function setUp(): void $this->createMock(PersistenceHandler::class), $this->createMock(ContentDomainMapper::class), $this->createMock(RelationProcessor::class), - $this->createMock(NameSchemaService::class), + $this->createMock(NameSchemaServiceInterface::class), $this->createMock(FieldTypeRegistry::class), $this->createMock(PermissionService::class), $this->createMock(ContentMapper::class), diff --git a/eZ/Publish/Core/Repository/Tests/ContentThumbnail/ContentFieldStrategyTest.php b/tests/lib/Repository/ContentThumbnail/ContentFieldStrategyTest.php similarity index 85% rename from eZ/Publish/Core/Repository/Tests/ContentThumbnail/ContentFieldStrategyTest.php rename to tests/lib/Repository/ContentThumbnail/ContentFieldStrategyTest.php index a0dcf5ee2c..c9db0f8cf9 100644 --- a/eZ/Publish/Core/Repository/Tests/ContentThumbnail/ContentFieldStrategyTest.php +++ b/tests/lib/Repository/ContentThumbnail/ContentFieldStrategyTest.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\Tests\ContentThumbnail; +namespace Ibexa\Tests\Core\Repository\ContentThumbnail; use ArrayIterator; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Strategy\ContentThumbnail\Field\ContentFieldStrategy; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\Field\FieldTypeBasedThumbnailStrategy; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Strategy\ContentThumbnail\Field\FieldTypeBasedThumbnailStrategy; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\Thumbnail; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Strategy\ContentThumbnail\Field\ContentFieldStrategy; use PHPUnit\Framework\TestCase; class ContentFieldStrategyTest extends TestCase @@ -116,3 +116,5 @@ public function testGetThumbnailNotFound(): void $contentFieldStrategy->getThumbnail($field); } } + +class_alias(ContentFieldStrategyTest::class, 'eZ\Publish\Core\Repository\Tests\ContentThumbnail\ContentFieldStrategyTest'); diff --git a/tests/lib/Repository/ContentThumbnail/StaticStrategyTest.php b/tests/lib/Repository/ContentThumbnail/StaticStrategyTest.php new file mode 100644 index 0000000000..8780bcba70 --- /dev/null +++ b/tests/lib/Repository/ContentThumbnail/StaticStrategyTest.php @@ -0,0 +1,46 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\ContentThumbnail; + +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\Thumbnail; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Strategy\ContentThumbnail\StaticStrategy; +use PHPUnit\Framework\TestCase; + +class StaticStrategyTest extends TestCase +{ + public function testStaticStrategy() + { + $resource = 'static-test-resource'; + + $staticStrategy = new StaticStrategy($resource); + + $contentTypeMock = $this->createMock(ContentType::class); + $fieldMocks = [ + $this->createMock(Field::class), + $this->createMock(Field::class), + $this->createMock(Field::class), + ]; + + $result = $staticStrategy->getThumbnail( + $contentTypeMock, + $fieldMocks, + ); + + $this->assertEquals( + new Thumbnail([ + 'resource' => $resource, + ]), + $result + ); + } +} + +class_alias(StaticStrategyTest::class, 'eZ\Publish\Core\Repository\Tests\ContentThumbnail\StaticStrategyTest'); diff --git a/eZ/Publish/Core/Repository/Tests/ContentThumbnail/ThumbnailChainStrategyTest.php b/tests/lib/Repository/ContentThumbnail/ThumbnailChainStrategyTest.php similarity index 84% rename from eZ/Publish/Core/Repository/Tests/ContentThumbnail/ThumbnailChainStrategyTest.php rename to tests/lib/Repository/ContentThumbnail/ThumbnailChainStrategyTest.php index e76d39ebaa..59eef016e4 100644 --- a/eZ/Publish/Core/Repository/Tests/ContentThumbnail/ThumbnailChainStrategyTest.php +++ b/tests/lib/Repository/ContentThumbnail/ThumbnailChainStrategyTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\Tests\ContentThumbnail; +namespace Ibexa\Tests\Core\Repository\ContentThumbnail; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\Thumbnail; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy; -use eZ\Publish\SPI\Repository\Strategy\ContentThumbnail\ThumbnailStrategy; +use Ibexa\Contracts\Core\Repository\Strategy\ContentThumbnail\ThumbnailStrategy; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\Thumbnail; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Strategy\ContentThumbnail\ThumbnailChainStrategy; use PHPUnit\Framework\TestCase; class ThumbnailChainStrategyTest extends TestCase @@ -99,3 +99,5 @@ public function testThumbnailStrategyChainBreakOnThumbnailFound(): void $this->assertEquals(new Thumbnail(['resource' => 'second']), $result); } } + +class_alias(ThumbnailChainStrategyTest::class, 'eZ\Publish\Core\Repository\Tests\ContentThumbnail\ThumbnailChainStrategyTest'); diff --git a/eZ/Publish/Core/Repository/Tests/ContentValidator/ContentValidatorStrategyTest.php b/tests/lib/Repository/ContentValidator/ContentValidatorStrategyTest.php similarity index 85% rename from eZ/Publish/Core/Repository/Tests/ContentValidator/ContentValidatorStrategyTest.php rename to tests/lib/Repository/ContentValidator/ContentValidatorStrategyTest.php index 6226f2958e..b28b45d2cf 100644 --- a/eZ/Publish/Core/Repository/Tests/ContentValidator/ContentValidatorStrategyTest.php +++ b/tests/lib/Repository/ContentValidator/ContentValidatorStrategyTest.php @@ -4,13 +4,13 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Tests\ContentValidator; +namespace Ibexa\Tests\Core\Repository\ContentValidator; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Repository\Strategy\ContentValidator\ContentValidatorStrategy; -use eZ\Publish\Core\Repository\Values\ObjectState\ObjectState; -use eZ\Publish\SPI\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Repository\Strategy\ContentValidator\ContentValidatorStrategy; +use Ibexa\Core\Repository\Values\ObjectState\ObjectState; use PHPUnit\Framework\TestCase; class ContentValidatorStrategyTest extends TestCase @@ -18,7 +18,7 @@ class ContentValidatorStrategyTest extends TestCase public function testUnknownValidationObject(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Argument \'$object\' is invalid: Validator for eZ\Publish\Core\Repository\Values\ObjectState\ObjectState type not found.'); + $this->expectExceptionMessage('Argument \'$object\' is invalid: Validator for Ibexa\Core\Repository\Values\ObjectState\ObjectState type not found.'); $contentValidatorStrategy = new ContentValidatorStrategy([]); $contentValidatorStrategy->validate(new ObjectState()); @@ -114,3 +114,5 @@ public function validate( }; } } + +class_alias(ContentValidatorStrategyTest::class, 'eZ\Publish\Core\Repository\Tests\ContentValidator\ContentValidatorStrategyTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/BookmarkServiceDecoratorTest.php b/tests/lib/Repository/Decorator/BookmarkServiceDecoratorTest.php similarity index 86% rename from eZ/Publish/SPI/Repository/Tests/Decorator/BookmarkServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/BookmarkServiceDecoratorTest.php index 60c481c184..538aa839d8 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/BookmarkServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/BookmarkServiceDecoratorTest.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; +namespace Ibexa\Tests\Core\Repository\Decorator; -use eZ\Publish\API\Repository\BookmarkService; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\SPI\Repository\Decorator\BookmarkServiceDecorator; +use Ibexa\Contracts\Core\Repository\BookmarkService; +use Ibexa\Contracts\Core\Repository\Decorator\BookmarkServiceDecorator; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -78,3 +78,5 @@ public function testIsBookmarkedDecorator() $decoratedService->isBookmarked(...$parameters); } } + +class_alias(BookmarkServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\BookmarkServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/ContentServiceDecoratorTest.php b/tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php similarity index 93% rename from eZ/Publish/SPI/Repository/Tests/Decorator/ContentServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php index 203f30196f..71ec775c27 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/ContentServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/ContentServiceDecoratorTest.php @@ -6,20 +6,20 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; - -use eZ\Publish\API\Repository\ContentService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\ContentCreateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\ContentMetadataUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\ContentUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\SPI\Repository\Decorator\ContentServiceDecorator; +namespace Ibexa\Tests\Core\Repository\Decorator; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Decorator\ContentServiceDecorator; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentMetadataUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\User\User; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -475,9 +475,9 @@ public function testNewContentUpdateStructDecorator() } /** - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function testLoadVersionInfoListByContentInfoDecorator(): void { @@ -495,3 +495,5 @@ public function testLoadVersionInfoListByContentInfoDecorator(): void $decoratedService->loadVersionInfoListByContentInfo($argument); } } + +class_alias(ContentServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\ContentServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/ContentTypeServiceDecoratorTest.php b/tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php similarity index 92% rename from eZ/Publish/SPI/Repository/Tests/Decorator/ContentTypeServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php index ee3da1c559..4c33354bad 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/ContentTypeServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/ContentTypeServiceDecoratorTest.php @@ -6,21 +6,21 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; - -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeDraft; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroup; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\ContentTypeUpdateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionCreateStruct; -use eZ\Publish\API\Repository\Values\ContentType\FieldDefinitionUpdateStruct; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\SPI\Repository\Decorator\ContentTypeServiceDecorator; +namespace Ibexa\Tests\Core\Repository\Decorator; + +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\Decorator\ContentTypeServiceDecorator; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeDraft; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroup; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\User; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -464,3 +464,5 @@ public function testRemoveContentTypeTranslationDecorator() $decoratedService->removeContentTypeTranslation(...$parameters); } } + +class_alias(ContentTypeServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\ContentTypeServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/FieldTypeServiceDecoratorTest.php b/tests/lib/Repository/Decorator/FieldTypeServiceDecoratorTest.php similarity index 85% rename from eZ/Publish/SPI/Repository/Tests/Decorator/FieldTypeServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/FieldTypeServiceDecoratorTest.php index fb3e8b1316..5f64e4f594 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/FieldTypeServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/FieldTypeServiceDecoratorTest.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; +namespace Ibexa\Tests\Core\Repository\Decorator; -use eZ\Publish\API\Repository\FieldTypeService; -use eZ\Publish\SPI\Repository\Decorator\FieldTypeServiceDecorator; +use Ibexa\Contracts\Core\Repository\Decorator\FieldTypeServiceDecorator; +use Ibexa\Contracts\Core\Repository\FieldTypeService; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -62,3 +62,5 @@ public function testHasFieldTypeDecorator() $decoratedService->hasFieldType(...$parameters); } } + +class_alias(FieldTypeServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\FieldTypeServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/LanguageServiceDecoratorTest.php b/tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php similarity index 92% rename from eZ/Publish/SPI/Repository/Tests/Decorator/LanguageServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php index 4a02ba525c..b6023105d1 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/LanguageServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/LanguageServiceDecoratorTest.php @@ -6,12 +6,12 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; +namespace Ibexa\Tests\Core\Repository\Decorator; -use eZ\Publish\API\Repository\LanguageService; -use eZ\Publish\API\Repository\Values\Content\Language; -use eZ\Publish\API\Repository\Values\Content\LanguageCreateStruct; -use eZ\Publish\SPI\Repository\Decorator\LanguageServiceDecorator; +use Ibexa\Contracts\Core\Repository\Decorator\LanguageServiceDecorator; +use Ibexa\Contracts\Core\Repository\LanguageService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\LanguageCreateStruct; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -175,3 +175,5 @@ public function testNewLanguageCreateStructDecorator() $decoratedService->newLanguageCreateStruct(...$parameters); } } + +class_alias(LanguageServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\LanguageServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/LocationServiceDecoratorTest.php b/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php similarity index 93% rename from eZ/Publish/SPI/Repository/Tests/Decorator/LocationServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php index 128fc40758..2e579230e3 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/LocationServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/LocationServiceDecoratorTest.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; - -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationCreateStruct; -use eZ\Publish\API\Repository\Values\Content\LocationUpdateStruct; -use eZ\Publish\API\Repository\Values\Content\VersionInfo; -use eZ\Publish\SPI\Repository\Decorator\LocationServiceDecorator; +namespace Ibexa\Tests\Core\Repository\Decorator; + +use Ibexa\Contracts\Core\Repository\Decorator\LocationServiceDecorator; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -305,3 +305,5 @@ public function testLoadAllLocationsDecorator() $decoratedService->loadAllLocations(...$parameters); } } + +class_alias(LocationServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\LocationServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/NotificationServiceDecoratorTest.php b/tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php similarity index 88% rename from eZ/Publish/SPI/Repository/Tests/Decorator/NotificationServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php index 3b8b8b2a13..23271ed559 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/NotificationServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/NotificationServiceDecoratorTest.php @@ -6,12 +6,12 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; +namespace Ibexa\Tests\Core\Repository\Decorator; -use eZ\Publish\API\Repository\NotificationService; -use eZ\Publish\API\Repository\Values\Notification\CreateStruct; -use eZ\Publish\API\Repository\Values\Notification\Notification; -use eZ\Publish\SPI\Repository\Decorator\NotificationServiceDecorator; +use Ibexa\Contracts\Core\Repository\Decorator\NotificationServiceDecorator; +use Ibexa\Contracts\Core\Repository\NotificationService; +use Ibexa\Contracts\Core\Repository\Values\Notification\CreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Notification\Notification; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -115,3 +115,5 @@ public function testDeleteNotificationDecorator() $decoratedService->deleteNotification(...$parameters); } } + +class_alias(NotificationServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\NotificationServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/ObjectStateServiceDecoratorTest.php b/tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php similarity index 92% rename from eZ/Publish/SPI/Repository/Tests/Decorator/ObjectStateServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php index 4ad247d8da..89e633188d 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/ObjectStateServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/ObjectStateServiceDecoratorTest.php @@ -6,17 +6,17 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; - -use eZ\Publish\API\Repository\ObjectStateService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectState; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroup; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\ObjectState\ObjectStateUpdateStruct; -use eZ\Publish\SPI\Repository\Decorator\ObjectStateServiceDecorator; +namespace Ibexa\Tests\Core\Repository\Decorator; + +use Ibexa\Contracts\Core\Repository\Decorator\ObjectStateServiceDecorator; +use Ibexa\Contracts\Core\Repository\ObjectStateService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectState; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateUpdateStruct; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -332,3 +332,5 @@ public function testNewObjectStateUpdateStructDecorator() $decoratedService->newObjectStateUpdateStruct(...$parameters); } } + +class_alias(ObjectStateServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\ObjectStateServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/RoleServiceDecoratorTest.php b/tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php similarity index 92% rename from eZ/Publish/SPI/Repository/Tests/Decorator/RoleServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php index 597dd6c75d..16606a9bdc 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/RoleServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/RoleServiceDecoratorTest.php @@ -6,21 +6,21 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; - -use eZ\Publish\API\Repository\RoleService; -use eZ\Publish\API\Repository\Values\User\Limitation\RoleLimitation; -use eZ\Publish\API\Repository\Values\User\PolicyCreateStruct; -use eZ\Publish\API\Repository\Values\User\PolicyDraft; -use eZ\Publish\API\Repository\Values\User\PolicyUpdateStruct; -use eZ\Publish\API\Repository\Values\User\Role; -use eZ\Publish\API\Repository\Values\User\RoleAssignment; -use eZ\Publish\API\Repository\Values\User\RoleCreateStruct; -use eZ\Publish\API\Repository\Values\User\RoleDraft; -use eZ\Publish\API\Repository\Values\User\RoleUpdateStruct; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\SPI\Repository\Decorator\RoleServiceDecorator; +namespace Ibexa\Tests\Core\Repository\Decorator; + +use Ibexa\Contracts\Core\Repository\Decorator\RoleServiceDecorator; +use Ibexa\Contracts\Core\Repository\RoleService; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\Role; +use Ibexa\Contracts\Core\Repository\Values\User\RoleAssignment; +use Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\RoleDraft; +use Ibexa\Contracts\Core\Repository\Values\User\RoleUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroup; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -394,3 +394,5 @@ public function testGetLimitationTypesByModuleFunctionDecorator() $decoratedService->getLimitationTypesByModuleFunction(...$parameters); } } + +class_alias(RoleServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\RoleServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/SearchServiceDecoratorTest.php b/tests/lib/Repository/Decorator/SearchServiceDecoratorTest.php similarity index 89% rename from eZ/Publish/SPI/Repository/Tests/Decorator/SearchServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/SearchServiceDecoratorTest.php index dbe2116390..d87382a6bc 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/SearchServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/SearchServiceDecoratorTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; +namespace Ibexa\Tests\Core\Repository\Decorator; -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\SPI\Repository\Decorator\SearchServiceDecorator; +use Ibexa\Contracts\Core\Repository\Decorator\SearchServiceDecorator; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -145,3 +145,5 @@ public function getSearchEngineCapabilities(): array ]; } } + +class_alias(SearchServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\SearchServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/SectionServiceDecoratorTest.php b/tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php similarity index 90% rename from eZ/Publish/SPI/Repository/Tests/Decorator/SectionServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php index 539b5c273c..af6a0e9ce9 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/SectionServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/SectionServiceDecoratorTest.php @@ -6,15 +6,15 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; - -use eZ\Publish\API\Repository\SectionService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Section; -use eZ\Publish\API\Repository\Values\Content\SectionCreateStruct; -use eZ\Publish\API\Repository\Values\Content\SectionUpdateStruct; -use eZ\Publish\SPI\Repository\Decorator\SectionServiceDecorator; +namespace Ibexa\Tests\Core\Repository\Decorator; + +use Ibexa\Contracts\Core\Repository\Decorator\SectionServiceDecorator; +use Ibexa\Contracts\Core\Repository\SectionService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\Section; +use Ibexa\Contracts\Core\Repository\Values\Content\SectionCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\SectionUpdateStruct; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -186,3 +186,5 @@ public function testNewSectionUpdateStructDecorator() $decoratedService->newSectionUpdateStruct(...$parameters); } } + +class_alias(SectionServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\SectionServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/SettingServiceDecoratorTest.php b/tests/lib/Repository/Decorator/SettingServiceDecoratorTest.php similarity index 86% rename from eZ/Publish/SPI/Repository/Tests/Decorator/SettingServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/SettingServiceDecoratorTest.php index 6e0d6acb68..332d5c54f5 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/SettingServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/SettingServiceDecoratorTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; +namespace Ibexa\Tests\Core\Repository\Decorator; -use eZ\Publish\API\Repository\SettingService; -use eZ\Publish\API\Repository\Values\Setting\Setting; -use eZ\Publish\API\Repository\Values\Setting\SettingCreateStruct; -use eZ\Publish\API\Repository\Values\Setting\SettingUpdateStruct; -use eZ\Publish\SPI\Repository\Decorator\SettingServiceDecorator; +use Ibexa\Contracts\Core\Repository\Decorator\SettingServiceDecorator; +use Ibexa\Contracts\Core\Repository\SettingService; +use Ibexa\Contracts\Core\Repository\Values\Setting\Setting; +use Ibexa\Contracts\Core\Repository\Values\Setting\SettingCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Setting\SettingUpdateStruct; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -110,3 +110,5 @@ public function testNewSettingUpdateStructDecorator() $decoratedService->newSettingUpdateStruct(...$parameters); } } + +class_alias(SettingServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\SettingServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/TranslationServiceDecoratorTest.php b/tests/lib/Repository/Decorator/TranslationServiceDecoratorTest.php similarity index 81% rename from eZ/Publish/SPI/Repository/Tests/Decorator/TranslationServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/TranslationServiceDecoratorTest.php index fa63842552..95a3ad4b76 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/TranslationServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/TranslationServiceDecoratorTest.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; +namespace Ibexa\Tests\Core\Repository\Decorator; -use eZ\Publish\API\Repository\TranslationService; -use eZ\Publish\API\Repository\Values\Translation; -use eZ\Publish\SPI\Repository\Decorator\TranslationServiceDecorator; +use Ibexa\Contracts\Core\Repository\Decorator\TranslationServiceDecorator; +use Ibexa\Contracts\Core\Repository\TranslationService; +use Ibexa\Contracts\Core\Repository\Values\Translation; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -57,3 +57,5 @@ public function testTranslateStringDecorator() $decoratedService->translateString(...$parameters); } } + +class_alias(TranslationServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\TranslationServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/TrashServiceDecoratorTest.php b/tests/lib/Repository/Decorator/TrashServiceDecoratorTest.php similarity index 86% rename from eZ/Publish/SPI/Repository/Tests/Decorator/TrashServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/TrashServiceDecoratorTest.php index 8d722f4e66..be533efc27 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/TrashServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/TrashServiceDecoratorTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; +namespace Ibexa\Tests\Core\Repository\Decorator; -use eZ\Publish\API\Repository\TrashService; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\TrashItem; -use eZ\Publish\SPI\Repository\Decorator\TrashServiceDecorator; +use Ibexa\Contracts\Core\Repository\Decorator\TrashServiceDecorator; +use Ibexa\Contracts\Core\Repository\TrashService; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\TrashItem; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -104,3 +104,5 @@ public function testFindTrashItemsDecorator() $decoratedService->findTrashItems(...$parameters); } } + +class_alias(TrashServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\TrashServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/URLAliasServiceDecoratorTest.php b/tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php similarity index 93% rename from eZ/Publish/SPI/Repository/Tests/Decorator/URLAliasServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php index 62a8381230..c81a849d60 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/URLAliasServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/URLAliasServiceDecoratorTest.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; +namespace Ibexa\Tests\Core\Repository\Decorator; -use eZ\Publish\API\Repository\URLAliasService; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\SPI\Repository\Decorator\URLAliasServiceDecorator; +use Ibexa\Contracts\Core\Repository\Decorator\URLAliasServiceDecorator; +use Ibexa\Contracts\Core\Repository\URLAliasService; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -176,3 +176,5 @@ public function testDeleteCorruptedUrlAliasesDecorator() $decoratedService->deleteCorruptedUrlAliases(...$parameters); } } + +class_alias(URLAliasServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\URLAliasServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/URLServiceDecoratorTest.php b/tests/lib/Repository/Decorator/URLServiceDecoratorTest.php similarity index 86% rename from eZ/Publish/SPI/Repository/Tests/Decorator/URLServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/URLServiceDecoratorTest.php index cf0bc6d84d..2ddf07e7b0 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/URLServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/URLServiceDecoratorTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; +namespace Ibexa\Tests\Core\Repository\Decorator; -use eZ\Publish\API\Repository\URLService; -use eZ\Publish\API\Repository\Values\URL\URL; -use eZ\Publish\API\Repository\Values\URL\URLQuery; -use eZ\Publish\API\Repository\Values\URL\URLUpdateStruct; -use eZ\Publish\SPI\Repository\Decorator\URLServiceDecorator; +use Ibexa\Contracts\Core\Repository\Decorator\URLServiceDecorator; +use Ibexa\Contracts\Core\Repository\URLService; +use Ibexa\Contracts\Core\Repository\Values\URL\URL; +use Ibexa\Contracts\Core\Repository\Values\URL\URLQuery; +use Ibexa\Contracts\Core\Repository\Values\URL\URLUpdateStruct; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -108,3 +108,5 @@ public function testUpdateUrlDecorator() $decoratedService->updateUrl(...$parameters); } } + +class_alias(URLServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\URLServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/URLWildcardServiceDecoratorTest.php b/tests/lib/Repository/Decorator/URLWildcardServiceDecoratorTest.php similarity index 85% rename from eZ/Publish/SPI/Repository/Tests/Decorator/URLWildcardServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/URLWildcardServiceDecoratorTest.php index f9b4d15417..d6faa6f966 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/URLWildcardServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/URLWildcardServiceDecoratorTest.php @@ -6,11 +6,11 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; +namespace Ibexa\Tests\Core\Repository\Decorator; -use eZ\Publish\API\Repository\URLWildcardService; -use eZ\Publish\API\Repository\Values\Content\URLWildcard; -use eZ\Publish\SPI\Repository\Decorator\URLWildcardServiceDecorator; +use Ibexa\Contracts\Core\Repository\Decorator\URLWildcardServiceDecorator; +use Ibexa\Contracts\Core\Repository\URLWildcardService; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -87,10 +87,12 @@ public function testTranslateDecorator() $serviceMock = $this->createServiceMock(); $decoratedService = $this->createDecorator($serviceMock); - $parameters = ['ez.no']; + $parameters = ['ibexa.co']; $serviceMock->expects($this->once())->method('translate')->with(...$parameters); $decoratedService->translate(...$parameters); } } + +class_alias(URLWildcardServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\URLWildcardServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/UserPreferenceServiceDecoratorTest.php b/tests/lib/Repository/Decorator/UserPreferenceServiceDecoratorTest.php similarity index 87% rename from eZ/Publish/SPI/Repository/Tests/Decorator/UserPreferenceServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/UserPreferenceServiceDecoratorTest.php index 21d83df381..1fa544f83b 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/UserPreferenceServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/UserPreferenceServiceDecoratorTest.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; +namespace Ibexa\Tests\Core\Repository\Decorator; -use eZ\Publish\API\Repository\UserPreferenceService; -use eZ\Publish\SPI\Repository\Decorator\UserPreferenceServiceDecorator; +use Ibexa\Contracts\Core\Repository\Decorator\UserPreferenceServiceDecorator; +use Ibexa\Contracts\Core\Repository\UserPreferenceService; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -77,3 +77,5 @@ public function testGetUserPreferenceCountDecorator() $decoratedService->getUserPreferenceCount(...$parameters); } } + +class_alias(UserPreferenceServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\UserPreferenceServiceDecoratorTest'); diff --git a/eZ/Publish/SPI/Repository/Tests/Decorator/UserServiceDecoratorTest.php b/tests/lib/Repository/Decorator/UserServiceDecoratorTest.php similarity index 93% rename from eZ/Publish/SPI/Repository/Tests/Decorator/UserServiceDecoratorTest.php rename to tests/lib/Repository/Decorator/UserServiceDecoratorTest.php index ee8a74be03..23cb2ab1be 100644 --- a/eZ/Publish/SPI/Repository/Tests/Decorator/UserServiceDecoratorTest.php +++ b/tests/lib/Repository/Decorator/UserServiceDecoratorTest.php @@ -6,20 +6,20 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Repository\Tests\Decorator; - -use eZ\Publish\API\Repository\UserService; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\API\Repository\Values\User\PasswordValidationContext; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\API\Repository\Values\User\UserCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroup; -use eZ\Publish\API\Repository\Values\User\UserGroupCreateStruct; -use eZ\Publish\API\Repository\Values\User\UserGroupUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserTokenUpdateStruct; -use eZ\Publish\API\Repository\Values\User\UserUpdateStruct; -use eZ\Publish\SPI\Repository\Decorator\UserServiceDecorator; +namespace Ibexa\Tests\Core\Repository\Decorator; + +use Ibexa\Contracts\Core\Repository\Decorator\UserServiceDecorator; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Repository\Values\User\PasswordValidationContext; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroup; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserTokenUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserUpdateStruct; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -466,3 +466,5 @@ public function testValidatePasswordDecorator() $decoratedService->validatePassword(...$parameters); } } + +class_alias(UserServiceDecoratorTest::class, 'eZ\Publish\SPI\Repository\Tests\Decorator\UserServiceDecoratorTest'); diff --git a/eZ/Publish/API/Repository/Tests/Filtering/TestContentProvider.php b/tests/lib/Repository/Filtering/TestContentProvider.php similarity index 81% rename from eZ/Publish/API/Repository/Tests/Filtering/TestContentProvider.php rename to tests/lib/Repository/Filtering/TestContentProvider.php index dc609d08fa..cdafd33fdd 100644 --- a/eZ/Publish/API/Repository/Tests/Filtering/TestContentProvider.php +++ b/tests/lib/Repository/Filtering/TestContentProvider.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\Filtering; +namespace Ibexa\Tests\Core\Repository\Filtering; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Tests\BaseTest; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Section; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Section; +use Ibexa\Tests\Integration\Core\Repository\BaseTest; class TestContentProvider { @@ -28,10 +28,10 @@ class TestContentProvider 'article3' => 'remote-id-article-3', ]; - /** @var \eZ\Publish\API\Repository\Repository */ + /** @var \Ibexa\Contracts\Core\Repository\Repository */ private $repository; - /** @var \eZ\Publish\API\Repository\Tests\BaseTest */ + /** @var \Ibexa\Tests\Integration\Core\Repository\BaseTest */ private $testInstance; public function __construct(Repository $repository, BaseTest $testInstance) @@ -41,9 +41,9 @@ public function __construct(Repository $repository, BaseTest $testInstance) } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function createSharedContentStructure(): Content { @@ -120,9 +120,9 @@ public function createSharedContentStructure(): Content * ] * </code> * - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ public function createContentDraft( string $contentTypeIdentifier, @@ -153,9 +153,9 @@ public function createContentDraft( } /** - * @throws \eZ\Publish\API\Repository\Exceptions\ForbiddenException - * @throws \eZ\Publish\API\Repository\Exceptions\NotFoundException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ForbiddenException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ private function createArticle( string $title, @@ -183,8 +183,8 @@ private function createArticle( } /** - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException - * @throws \eZ\Publish\API\Repository\Exceptions\UnauthorizedException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException */ private function createSection(string $sectionIdentifier): Section { @@ -196,3 +196,5 @@ private function createSection(string $sectionIdentifier): Section return $sectionService->createSection($sectionCreate); } } + +class_alias(TestContentProvider::class, 'eZ\Publish\API\Repository\Tests\Filtering\TestContentProvider'); diff --git a/tests/lib/Repository/Helper/FieldTypeRegistryTest.php b/tests/lib/Repository/Helper/FieldTypeRegistryTest.php new file mode 100644 index 0000000000..8a503fd729 --- /dev/null +++ b/tests/lib/Repository/Helper/FieldTypeRegistryTest.php @@ -0,0 +1,99 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Helper; + +use Ibexa\Contracts\Core\FieldType\FieldType; +use Ibexa\Core\Base\Exceptions\NotFound\FieldTypeNotFoundException; +use Ibexa\Core\FieldType\FieldTypeRegistry; +use PHPUnit\Framework\TestCase; + +/** + * Unit test case for FieldTypeRegistry helper. + */ +class FieldTypeRegistryTest extends TestCase +{ + private const FIELD_TYPE_ID = 'one'; + + public function testConstructor() + { + $fieldType = $this->getFieldTypeMock(); + $fieldTypes = [self::FIELD_TYPE_ID => $fieldType]; + + $registry = new FieldTypeRegistry($fieldTypes); + $this->assertTrue($registry->hasFieldType(self::FIELD_TYPE_ID)); + } + + protected function getFieldTypeMock() + { + return $this->createMock(FieldType::class); + } + + public function testGetFieldType() + { + $fieldTypes = [ + self::FIELD_TYPE_ID => $this->getFieldTypeMock(), + ]; + + $registry = new FieldTypeRegistry($fieldTypes); + + $fieldType = $registry->getFieldType(self::FIELD_TYPE_ID); + + $this->assertInstanceOf( + FieldType::class, + $fieldType + ); + } + + public function testGetFieldTypeThrowsNotFoundException() + { + $this->expectException(FieldTypeNotFoundException::class); + + $registry = new FieldTypeRegistry([]); + + $registry->getFieldType('none'); + } + + public function testGetFieldTypeThrowsRuntimeExceptionIncorrectType() + { + $this->expectException(\TypeError::class); + + $registry = new FieldTypeRegistry( + [ + 'none' => "I'm not a field type", + ] + ); + + $registry->getFieldType('none'); + } + + public function testGetFieldTypes() + { + $fieldTypes = [ + self::FIELD_TYPE_ID => $this->getFieldTypeMock(), + 'two' => $this->getFieldTypeMock(), + ]; + + $registry = new FieldTypeRegistry($fieldTypes); + + $fieldTypes = $registry->getFieldTypes(); + + $this->assertIsArray($fieldTypes); + $this->assertCount(2, $fieldTypes); + $this->assertArrayHasKey(self::FIELD_TYPE_ID, $fieldTypes); + $this->assertInstanceOf( + FieldType::class, + $fieldTypes[self::FIELD_TYPE_ID] + ); + $this->assertArrayHasKey('two', $fieldTypes); + $this->assertInstanceOf( + FieldType::class, + $fieldTypes['two'] + ); + } +} + +class_alias(FieldTypeRegistryTest::class, 'eZ\Publish\Core\Repository\Tests\Helper\FieldTypeRegistryTest'); diff --git a/eZ/Publish/API/Repository/Tests/IdManager/Php.php b/tests/lib/Repository/IdManager/Php.php similarity index 84% rename from eZ/Publish/API/Repository/Tests/IdManager/Php.php rename to tests/lib/Repository/IdManager/Php.php index afead9b31d..d858bb7354 100644 --- a/eZ/Publish/API/Repository/Tests/IdManager/Php.php +++ b/tests/lib/Repository/IdManager/Php.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\API\Repository\Tests\IdManager; +namespace Ibexa\Tests\Core\Repository\IdManager; -use eZ\Publish\API\Repository\Tests\IdManager; +use Ibexa\Tests\Integration\Core\Repository\IdManager; /** * ID manager for the basic PHP usage of the Public API. @@ -45,3 +45,5 @@ public function parseId($type, $id) return $id; } } + +class_alias(Php::class, 'eZ\Publish\API\Repository\Tests\IdManager\Php'); diff --git a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php similarity index 79% rename from eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php rename to tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php index 4e86dd89eb..bac886e79f 100644 --- a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/AbstractSearchAdapterTest.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorAdapter; +namespace Ibexa\Tests\Core\Repository\Iterator\BatchIteratorAdapter; -use eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter; -use eZ\Publish\API\Repository\SearchService; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion\MatchAll; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; +use Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\MatchAll; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; use Iterator; use PHPUnit\Framework\TestCase; @@ -73,3 +73,5 @@ protected function newQuery(): Query return new Query(); } } + +class_alias(AbstractSearchAdapterTest::class, 'eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorAdapter\AbstractSearchAdapterTest'); diff --git a/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentFilteringAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentFilteringAdapterTest.php new file mode 100644 index 0000000000..a90d018ee0 --- /dev/null +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentFilteringAdapterTest.php @@ -0,0 +1,74 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Iterator\BatchIteratorAdapter; + +use Ibexa\Contracts\Core\Repository\ContentService; +use Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\ContentFilteringAdapter; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentList; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\MatchAll; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use PHPUnit\Framework\TestCase; + +final class ContentFilteringAdapterTest extends TestCase +{ + private const EXAMPLE_LANGUAGE_FILTER = ['eng-GB', 'pol-PL']; + private const EXAMPLE_OFFSET = 10; + private const EXAMPLE_LIMIT = 25; + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testFetch(): void + { + $content1 = $this->createMock(Content::class); + $content2 = $this->createMock(Content::class); + $content3 = $this->createMock(Content::class); + + $contentList = new ContentList(3, [ + $content1, + $content2, + $content3, + ]); + + $expectedResults = [ + $content1, + $content2, + $content3, + ]; + + $originalFilter = new Filter(); + $originalFilter->withCriterion(new MatchAll()); + + $expectedFilter = new Filter(); + $expectedFilter->withCriterion(new MatchAll()); + $expectedFilter->sliceBy(self::EXAMPLE_LIMIT, self::EXAMPLE_OFFSET); + + $contentService = $this->createMock(ContentService::class); + $contentService + ->expects($this->once()) + ->method('find') + ->with($expectedFilter, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($contentList); + + $adapter = new ContentFilteringAdapter($contentService, $originalFilter, self::EXAMPLE_LANGUAGE_FILTER); + + self::assertSame( + $expectedResults, + iterator_to_array($adapter->fetch(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT)) + ); + + // Input $filter remains untouched + self::assertSame(0, $originalFilter->getOffset()); + self::assertSame(0, $originalFilter->getLimit()); + } +} + +class_alias(ContentFilteringAdapterTest::class, 'eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorAdapter\ContentFilteringAdapterTest'); diff --git a/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php new file mode 100644 index 0000000000..9b6e1b0f6e --- /dev/null +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentInfoSearchAdapterTest.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Iterator\BatchIteratorAdapter; + +use Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter; +use Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\ContentInfoSearchAdapter; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; + +final class ContentInfoSearchAdapterTest extends AbstractSearchAdapterTest +{ + protected function createAdapterUnderTest( + SearchService $searchService, + Query $query, + array $languageFilter, + bool $filterOnPermissions + ): AbstractSearchAdapter { + return new ContentInfoSearchAdapter( + $searchService, + $query, + self::EXAMPLE_LANGUAGE_FILTER, + true + ); + } + + protected function getExpectedFindMethod(): string + { + return 'findContentInfo'; + } +} + +class_alias(ContentInfoSearchAdapterTest::class, 'eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorAdapter\ContentInfoSearchAdapterTest'); diff --git a/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php new file mode 100644 index 0000000000..b3a38aa430 --- /dev/null +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/ContentSearchAdapterTest.php @@ -0,0 +1,38 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Iterator\BatchIteratorAdapter; + +use Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter; +use Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\ContentSearchAdapter; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; + +final class ContentSearchAdapterTest extends AbstractSearchAdapterTest +{ + protected function createAdapterUnderTest( + SearchService $searchService, + Query $query, + array $languageFilter, + bool $filterOnPermissions + ): AbstractSearchAdapter { + return new ContentSearchAdapter( + $searchService, + $query, + self::EXAMPLE_LANGUAGE_FILTER, + true + ); + } + + protected function getExpectedFindMethod(): string + { + return 'findContent'; + } +} + +class_alias(ContentSearchAdapterTest::class, 'eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorAdapter\ContentSearchAdapterTest'); diff --git a/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationFilteringAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationFilteringAdapterTest.php new file mode 100644 index 0000000000..3a58cb9157 --- /dev/null +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationFilteringAdapterTest.php @@ -0,0 +1,77 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Iterator\BatchIteratorAdapter; + +use Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\LocationFilteringAdapter; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationList; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\MatchAll; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use PHPUnit\Framework\TestCase; + +final class LocationFilteringAdapterTest extends TestCase +{ + private const EXAMPLE_LANGUAGE_FILTER = ['eng-GB', 'pol-PL']; + private const EXAMPLE_OFFSET = 10; + private const EXAMPLE_LIMIT = 25; + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException + */ + public function testFetch(): void + { + $location1 = $this->createMock(Location::class); + $location2 = $this->createMock(Location::class); + $location3 = $this->createMock(Location::class); + + $locationList = new LocationList([ + 'locations' => [ + $location1, + $location2, + $location3, + ], + 'totalCount' => 3, + ]); + + $expectedResults = [ + $location1, + $location2, + $location3, + ]; + + $originalFilter = new Filter(); + $originalFilter->withCriterion(new MatchAll()); + + $expectedFilter = new Filter(); + $expectedFilter->withCriterion(new MatchAll()); + $expectedFilter->sliceBy(self::EXAMPLE_LIMIT, self::EXAMPLE_OFFSET); + + $locationService = $this->createMock(LocationService::class); + $locationService + ->expects($this->once()) + ->method('find') + ->with($expectedFilter, self::EXAMPLE_LANGUAGE_FILTER) + ->willReturn($locationList); + + $adapter = new LocationFilteringAdapter($locationService, $originalFilter, self::EXAMPLE_LANGUAGE_FILTER); + + self::assertSame( + $expectedResults, + iterator_to_array($adapter->fetch(self::EXAMPLE_OFFSET, self::EXAMPLE_LIMIT)) + ); + + // Input $filter remains untouched + self::assertSame(0, $originalFilter->getOffset()); + self::assertSame(0, $originalFilter->getLimit()); + } +} + +class_alias(LocationFilteringAdapterTest::class, 'eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorAdapter\LocationFilteringAdapterTest'); diff --git a/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php b/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php new file mode 100644 index 0000000000..94f63cae53 --- /dev/null +++ b/tests/lib/Repository/Iterator/BatchIteratorAdapter/LocationSearchAdapterTest.php @@ -0,0 +1,44 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Iterator\BatchIteratorAdapter; + +use Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\AbstractSearchAdapter; +use Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter\LocationSearchAdapter; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; + +final class LocationSearchAdapterTest extends AbstractSearchAdapterTest +{ + protected function createAdapterUnderTest( + SearchService $searchService, + Query $query, + array $languageFilter, + bool $filterOnPermissions + ): AbstractSearchAdapter { + return new LocationSearchAdapter( + $searchService, + $query, + self::EXAMPLE_LANGUAGE_FILTER, + true + ); + } + + protected function getExpectedFindMethod(): string + { + return 'findLocations'; + } + + protected function newQuery(): Query + { + return new LocationQuery(); + } +} + +class_alias(LocationSearchAdapterTest::class, 'eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorAdapter\LocationSearchAdapterTest'); diff --git a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorTest.php b/tests/lib/Repository/Iterator/BatchIteratorTest.php similarity index 87% rename from eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorTest.php rename to tests/lib/Repository/Iterator/BatchIteratorTest.php index 80c4d19261..2d493389f0 100644 --- a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorTest.php +++ b/tests/lib/Repository/Iterator/BatchIteratorTest.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\Iterator; +namespace Ibexa\Tests\Core\Repository\Iterator; -use eZ\Publish\API\Repository\Iterator\BatchIterator; +use Ibexa\Contracts\Core\Repository\Iterator\BatchIterator; use PHPUnit\Framework\TestCase; final class BatchIteratorTest extends TestCase @@ -48,3 +48,5 @@ public function testIterateOverEmptyResultSet(): void $this->assertEquals(1, $adapter->getFetchCounter()); } } + +class_alias(BatchIteratorTest::class, 'eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorTest'); diff --git a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorTestAdapter.php b/tests/lib/Repository/Iterator/BatchIteratorTestAdapter.php similarity index 77% rename from eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorTestAdapter.php rename to tests/lib/Repository/Iterator/BatchIteratorTestAdapter.php index e29d8023f6..f3adf24287 100644 --- a/eZ/Publish/API/Repository/Tests/Iterator/BatchIteratorTestAdapter.php +++ b/tests/lib/Repository/Iterator/BatchIteratorTestAdapter.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\Iterator; +namespace Ibexa\Tests\Core\Repository\Iterator; use ArrayIterator; -use eZ\Publish\API\Repository\Iterator\BatchIteratorAdapter; +use Ibexa\Contracts\Core\Repository\Iterator\BatchIteratorAdapter; use Iterator; final class BatchIteratorTestAdapter implements BatchIteratorAdapter @@ -38,3 +38,5 @@ public function getFetchCounter(): int return $this->fetchCounter; } } + +class_alias(BatchIteratorTestAdapter::class, 'eZ\Publish\API\Repository\Tests\Iterator\BatchIteratorTestAdapter'); diff --git a/eZ/Publish/API/Repository/Tests/LegacySchemaImporter.php b/tests/lib/Repository/LegacySchemaImporter.php similarity index 89% rename from eZ/Publish/API/Repository/Tests/LegacySchemaImporter.php rename to tests/lib/Repository/LegacySchemaImporter.php index 9ddcf8e5fd..2b9d52a8e3 100644 --- a/eZ/Publish/API/Repository/Tests/LegacySchemaImporter.php +++ b/tests/lib/Repository/LegacySchemaImporter.php @@ -6,19 +6,19 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests; +namespace Ibexa\Tests\Core\Repository; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Schema\Schema as DoctrineSchema; -use EzSystems\DoctrineSchema\API\Exception\InvalidConfigurationException; -use EzSystems\DoctrineSchema\Importer\SchemaImporter; +use Ibexa\Contracts\DoctrineSchema\Exception\InvalidConfigurationException; +use Ibexa\DoctrineSchema\Importer\SchemaImporter; use RuntimeException; /** * Legacy database Schema Importer for database integration tests. * - * @uses \EzSystems\DoctrineSchema\Importer\SchemaImporter + * @uses \Ibexa\DoctrineSchema\Importer\SchemaImporter * * @internal For internal use by the Repository test cases. */ @@ -87,3 +87,5 @@ private function getDropSqlStatementsForExistingSchema( return $statements; } } + +class_alias(LegacySchemaImporter::class, 'eZ\Publish\API\Repository\Tests\LegacySchemaImporter'); diff --git a/tests/lib/Repository/LocationResolver/PermissionAwareLocationResolverTest.php b/tests/lib/Repository/LocationResolver/PermissionAwareLocationResolverTest.php new file mode 100644 index 0000000000..b885bd978a --- /dev/null +++ b/tests/lib/Repository/LocationResolver/PermissionAwareLocationResolverTest.php @@ -0,0 +1,125 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\LocationResolver; + +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Core\Repository\LocationResolver\PermissionAwareLocationResolver; +use Ibexa\Core\Repository\Values\Content\Location; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Repository\LocationResolver\PermissionAwareLocationResolver + */ +final class PermissionAwareLocationResolverTest extends TestCase +{ + /** @var \Ibexa\Contracts\Core\Repository\LocationService */ + private $locationService; + + /** @var \Ibexa\Core\Repository\LocationResolver\LocationResolver */ + private $locationResolver; + + public function setUp(): void + { + $this->locationService = $this->createMock(LocationService::class); + + $this->locationResolver = new PermissionAwareLocationResolver($this->locationService); + } + + public function testResolveMainLocation(): void + { + $contentInfo = new ContentInfo(['mainLocationId' => 42]); + $location = new Location(['id' => 42]); + + // User has access to the main Location + $this->locationService + ->method('loadLocation') + ->willReturn($location); + + $this->assertSame($location, $this->locationResolver->resolveLocation($contentInfo)); + } + + /** + * Test for the resolveLocation() method. + */ + public function testResolveSecondaryLocation(): void + { + $contentInfo = new ContentInfo(['mainLocationId' => 42]); + $location1 = new Location(['id' => 43]); + $location2 = new Location(['id' => 44]); + + // User doesn't have access to main location but to the third Content's location + $this->locationService + ->method('loadLocation') + ->willThrowException($this->createMock(UnauthorizedException::class)); + + $this->locationService + ->method('loadLocations') + ->willReturn([$location1, $location2]); + + $this->assertSame($location1, $this->locationResolver->resolveLocation($contentInfo)); + } + + /** + * Test for the resolveLocation() method when Locations don't exist. + */ + public function testExpectNotFoundExceptionWhenLocationDoesNotExist(): void + { + $contentInfo = new ContentInfo(['mainLocationId' => 42]); + + $this->locationService + ->method('loadLocation') + ->willThrowException($this->createMock(NotFoundException::class)); + + $this->locationService + ->method('loadLocations') + ->willReturn([]); + + $this->expectException(NotFoundException::class); + + $this->locationResolver->resolveLocation($contentInfo); + } + + /** + * Test for the resolveLocation() method when ContentInfo's mainLocationId is null. + */ + public function testExpectNotFoundExceptionWhenMainLocationIdIsNull(): void + { + $contentInfo = new ContentInfo(['mainLocationId' => null]); + + $this->expectException(NotFoundException::class); + + $this->locationResolver->resolveLocation($contentInfo); + } + + /** + * Test for the resolveLocation() method when Location is not yet published. + */ + public function testExpectBadStateExceptionWhenContentNotYetPublished(): void + { + $contentInfo = new ContentInfo(['mainLocationId' => 42, 'status' => ContentInfo::STATUS_DRAFT]); + + $this->locationService + ->method('loadLocation') + ->willThrowException($this->createMock(NotFoundException::class)); + + $this->locationService + ->method('loadLocations') + ->willThrowException($this->createMock(BadStateException::class)); + + $this->expectException(BadStateException::class); + + $this->locationResolver->resolveLocation($contentInfo); + } +} + +class_alias(PermissionAwareLocationResolverTest::class, 'eZ\Publish\Core\Repository\Tests\LocationResolver\PermissionAwareLocationResolverTest'); diff --git a/tests/lib/Repository/LocationServiceTest.php b/tests/lib/Repository/LocationServiceTest.php index 1cd53cf855..2c3b583bf9 100644 --- a/tests/lib/Repository/LocationServiceTest.php +++ b/tests/lib/Repository/LocationServiceTest.php @@ -8,21 +8,21 @@ namespace Ibexa\Tests\Core\Repository; -use eZ\Publish\API\Repository\ContentTypeService; -use eZ\Publish\API\Repository\PermissionCriterionResolver; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\Core\Repository\Helper\NameSchemaService; -use eZ\Publish\Core\Repository\LocationService; -use eZ\Publish\Core\Repository\Mapper\ContentDomainMapper; -use eZ\Publish\SPI\Persistence\Filter\Location\Handler as LocationFilteringHandler; -use eZ\Publish\SPI\Persistence\Handler as PersistenceHandler; +use Ibexa\Contracts\Core\Persistence\Filter\Location\Handler as LocationFilteringHandler; +use Ibexa\Contracts\Core\Persistence\Handler as PersistenceHandler; +use Ibexa\Contracts\Core\Repository\ContentTypeService; +use Ibexa\Contracts\Core\Repository\PermissionCriterionResolver; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use Ibexa\Core\Repository\Helper\NameSchemaService; +use Ibexa\Core\Repository\LocationService; +use Ibexa\Core\Repository\Mapper\ContentDomainMapper; use PHPUnit\Framework\TestCase; final class LocationServiceTest extends TestCase { - /** @var \eZ\Publish\API\Repository\LocationService */ + /** @var \Ibexa\Contracts\Core\Repository\LocationService */ private $locationService; protected function setUp(): void @@ -40,7 +40,7 @@ protected function setUp(): void } /** - * @throws \eZ\Publish\API\Repository\Exceptions\Exception + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception */ public function testFindDoesNotModifyFilter(): void { diff --git a/tests/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationServiceTest.php b/tests/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationServiceTest.php index 1a251a2b21..3cc1b5c43e 100644 --- a/tests/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationServiceTest.php +++ b/tests/lib/Repository/Mapper/ContentLocationMapper/DecoratedLocationServiceTest.php @@ -8,12 +8,12 @@ namespace Ibexa\Tests\Core\Repository\Mapper\ContentLocationMapper; -use eZ\Publish\API\Repository\LocationService as ApiLocationService; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\LocationList; -use eZ\Publish\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\LocationService as ApiLocationService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationList; use Ibexa\Core\Repository\Mapper\ContentLocationMapper\ContentLocationMapper; use Ibexa\Core\Repository\Mapper\ContentLocationMapper\DecoratedLocationService; +use Ibexa\Core\Repository\Values\Content\Location; use PHPUnit\Framework\TestCase; class DecoratedLocationServiceTest extends TestCase @@ -21,7 +21,7 @@ class DecoratedLocationServiceTest extends TestCase /** @var \Ibexa\Core\Repository\Mapper\ContentLocationMapper\DecoratedLocationService */ private $locationService; - /** @var \eZ\Publish\API\Repository\LocationService */ + /** @var \Ibexa\Contracts\Core\Repository\LocationService */ private $innerLocationService; /** @var \Ibexa\Core\Repository\Mapper\ContentLocationMapper\ContentLocationMapper */ diff --git a/tests/lib/Repository/Mapper/ContentMapperTest.php b/tests/lib/Repository/Mapper/ContentMapperTest.php index 0c6d7a3abd..5ef2e201ab 100644 --- a/tests/lib/Repository/Mapper/ContentMapperTest.php +++ b/tests/lib/Repository/Mapper/ContentMapperTest.php @@ -8,34 +8,33 @@ namespace Ibexa\Tests\Core\Repository\Mapper; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\API\Repository\Values\Content\VersionInfo as APIVersionInfo; -use eZ\Publish\Core\FieldType\FieldTypeRegistry; -use eZ\Publish\Core\FieldType\TextLine; -use eZ\Publish\Core\Persistence\Cache\ContentLanguageHandler; -use eZ\Publish\Core\Repository\Mapper\ContentMapper; -use eZ\Publish\Core\Repository\Values\Content\Content; -use eZ\Publish\Core\Repository\Values\Content\VersionInfo; -use eZ\Publish\Core\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinitionCollection; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Core\FieldType\FieldTypeRegistry; +use Ibexa\Core\FieldType\TextLine; +use Ibexa\Core\Persistence\Legacy\Content\Language\Handler; +use Ibexa\Core\Repository\Mapper\ContentMapper; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinitionCollection; use PHPUnit\Framework\TestCase; final class ContentMapperTest extends TestCase { - /** @var \eZ\Publish\Core\Persistence\Legacy\Content\Language\Handler|\PHPUnit\Framework\MockObject\MockObject */ - private $contentLanguageHandler; + /** @var \Ibexa\Core\Persistence\Legacy\Content\Language\Handler&\PHPUnit\Framework\MockObject\MockObject */ + private Handler $contentLanguageHandler; - /** @var \eZ\Publish\Core\FieldType\FieldTypeRegistry|\PHPUnit\Framework\MockObject\MockObject */ - private $fieldTypeRegistry; + /** @var \Ibexa\Core\FieldType\FieldTypeRegistry&\PHPUnit\Framework\MockObject\MockObject */ + private FieldTypeRegistry $fieldTypeRegistry; - /** @var \eZ\Publish\Core\Repository\Mapper\ContentMapper */ - private $contentMapper; + private ContentMapper $contentMapper; protected function setUp(): void { - $this->contentLanguageHandler = $this->createMock(ContentLanguageHandler::class); + $this->contentLanguageHandler = $this->createMock(Handler::class); $this->fieldTypeRegistry = $this->createMock(FieldTypeRegistry::class); $this->contentMapper = new ContentMapper( @@ -45,9 +44,9 @@ protected function setUp(): void } /** - * @covers \eZ\Publish\Core\Repository\ContentService::updateContent + * @covers \Ibexa\Core\Repository\ContentService::updateContent * - * @throws \eZ\Publish\Core\Base\Exceptions\ContentValidationException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException */ public function testUpdateContentGetsProperFieldsToUpdate(): void { diff --git a/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php new file mode 100644 index 0000000000..95c9d6a820 --- /dev/null +++ b/tests/lib/Repository/NameSchema/NameSchemaServiceTest.php @@ -0,0 +1,387 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\NameSchema; + +use Ibexa\Contracts\Core\Event\NameSchema\AbstractSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveContentNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveNameSchemaEvent; +use Ibexa\Contracts\Core\Event\NameSchema\ResolveUrlAliasSchemaEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCollection as APIFieldDefinitionCollection; +use Ibexa\Core\FieldType\TextLine\Type as TextLineFieldType; +use Ibexa\Core\FieldType\TextLine\Value as TextLineValue; +use Ibexa\Core\Repository\NameSchema\NameSchemaService; +use Ibexa\Core\Repository\NameSchema\SchemaIdentifierExtractor; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinitionCollection; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; +use Traversable; + +/** + * @covers \Ibexa\Core\Repository\NameSchema\NameSchemaService + */ +final class NameSchemaServiceTest extends BaseServiceMockTest +{ + private const NAME_SCHEMA = '<name_schema>'; + + public function testResolveUrlAliasSchema(): void + { + $content = $this->buildTestContentObject(); + $contentType = $this->buildTestContentTypeStub(); + + $event = new ResolveUrlAliasSchemaEvent(['field' => ['<url_alias_schema>']], $content); + $event->setTokenValues(['eng-GB' => ['url_alias_schema' => 'foo']]); + + $nameSchemaService = $this->buildNameSchemaService($event); + + $result = $nameSchemaService->resolveUrlAliasSchema($content, $contentType); + + self::assertEquals(['eng-GB' => 'foo'], $result); + } + + public function testResolveUrlAliasSchemaFallbackToNameSchema(): void + { + $content = $this->buildTestContentObject(); + $contentType = $this->buildTestContentTypeStub(self::NAME_SCHEMA, ''); + + $event = new ResolveUrlAliasSchemaEvent(['field' => [self::NAME_SCHEMA]], $content); + $event->setTokenValues(['eng-GB' => ['name_schema' => 'bar']]); + + $nameSchemaService = $this->buildNameSchemaService($event); + $result = $nameSchemaService->resolveUrlAliasSchema($content, $contentType); + + self::assertEquals(['eng-GB' => 'bar'], $result); + } + + /** + * @return iterable<string, array{ + * 0: array<int|string, array<string, \Ibexa\Contracts\Core\FieldType\Value>>, + * 1: array<string, array<string, string>>, + * 2: array<string>, + * 3: array<string, string> + * }> + */ + public static function getDataForTestResolveNameSchema(): iterable + { + yield 'Default: Field Map and Languages taken from Content Version' => [ + [], + [ + 'eng-GB' => ['text2' => 'two'], + 'cro-HR' => ['text2' => 'dva'], + ], + [], + [ + 'eng-GB' => 'two', + 'cro-HR' => 'dva', + ], + ]; + + yield 'Field Map and Languages for update' => [ + [ + 'text1' => ['cro-HR' => new TextLineValue('jedan'), 'eng-GB' => new TextLineValue('one')], + 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], + 'text3' => ['eng-GB' => new TextLineValue('three')], + ], + [ + 'eng-GB' => ['text2' => 'two', 'text3' => 'three'], + 'cro-HR' => ['text2' => 'Dva'], + ], + ['eng-GB', 'cro-HR'], + [ + 'eng-GB' => 'three', + 'cro-HR' => 'Dva', + ], + ]; + } + + /** + * @dataProvider getDataForTestResolveNameSchema + * + * @param array<int|string, array<string, \Ibexa\Contracts\Core\FieldType\Value>> $fieldMap + * @param array<string, array<string, string>> $tokenValues + * @param array<string> $languageCodes + * @param array<string, string> $expectedNames + */ + public function testResolveNameSchema( + array $fieldMap, + array $tokenValues, + array $languageCodes, + array $expectedNames + ): void { + $content = $this->buildTestContentObject(); + $nameSchema = '<text3|text2>'; + $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); + $event = new ResolveContentNameSchemaEvent( + $content, + ['field' => ['text3', 'text2']], + $contentType, + $fieldMap, + $languageCodes + ); + $event->setTokenValues($tokenValues); + + $nameSchemaService = $this->buildNameSchemaService( + $event + ); + + $result = $nameSchemaService->resolveContentNameSchema($content, $fieldMap, $languageCodes, $contentType); + + self::assertEquals( + $expectedNames, + $result + ); + } + + /** + * Data provider for the testResolve method. + * + * @return array<array{ + * 0: array<string, array<string>>, + * 1: string, + * 2: array<int|string, array<string, \Ibexa\Contracts\Core\FieldType\Value>>, + * 3: array<string, string>, + * 4: array<string, array<string, string>>, + * 5?: array{limit?: int, sequence?: string} + * }> + * + * @see testResolve + */ + public static function getDataForTestResolve(): array + { + return [ + [ + ['field' => ['text1']], + '<text1>', + [ + 'text1' => ['cro-HR' => new TextLineValue('jedan'), 'eng-GB' => new TextLineValue('one')], + 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], + 'text3' => ['eng-GB' => new TextLineValue('three')], +], + [ + 'eng-GB' => 'one', + 'cro-HR' => 'jedan', + ], + [ + 'eng-GB' => ['text1' => 'one'], + 'cro-HR' => ['text1' => 'jedan'], + ], + ], + [ + ['field' => ['text2']], + '<text2>', + [ + 'text1' => ['cro-HR' => new TextLineValue('jedan'), 'eng-GB' => new TextLineValue('one')], + 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], + 'text3' => ['eng-GB' => new TextLineValue('three')], + ], + [ + 'eng-GB' => 'two', + 'cro-HR' => 'dva', + ], + [ + 'eng-GB' => ['text2' => 'two'], + 'cro-HR' => ['text2' => 'dva'], + ], + ], + [ + ['field' => ['text2', 'text2']], + 'Hello, <text1> and <text2> and then goodbye and hello again', + [ + 'text1' => ['cro-HR' => new TextLineValue('jedan'), 'eng-GB' => new TextLineValue('one')], + 'text2' => ['cro-HR' => new TextLineValue('Dva'), 'eng-GB' => new TextLineValue('two')], + 'text3' => ['eng-GB' => new TextLineValue('three')], + ], + [ + 'eng-GB' => 'Hello, one and two and then goodbye...', + 'cro-HR' => 'Hello, jedan and dva and then goodb...', + ], + [ + 'eng-GB' => ['text1' => 'one', 'text2' => 'two'], + 'cro-HR' => ['text1' => 'jedan', 'text2' => 'dva'], + ], + [ + 'limit' => 38, + 'sequence' => '...', + ], + ], + ]; + } + + /** + * @dataProvider getDataForTestResolve + * + * @param array<string, array<string>> $schemaIdentifiers + * @param array<string> $languageFieldValues field value translations + * @param array<int|string, array<string, \Ibexa\Contracts\Core\FieldType\Value>> $fieldMap + * @param array<string, array<string, string>> $fieldTitles [language => [field_identifier => title]] + * @param array{limit?: int, sequence?: string} $settings NameSchemaService settings + */ + public function testResolve( + array $schemaIdentifiers, + string $nameSchema, + array $fieldMap, + array $languageFieldValues, + array $fieldTitles, + array $settings = [] + ): void { + $content = $this->buildTestContentObject(); + $contentType = $this->buildTestContentTypeStub($nameSchema, $nameSchema); + + $event = new ResolveNameSchemaEvent( + $schemaIdentifiers, + $contentType, + $fieldMap, + $content->versionInfo->languageCodes + ); + + $event->setTokenValues($fieldTitles); + + $nameSchemaService = $this->buildNameSchemaService( + $event, + $settings + ); + + $result = $nameSchemaService->resolveNameSchema( + $nameSchema, + $contentType, + $fieldMap, + $content->versionInfo->languageCodes + ); + + self::assertEquals($languageFieldValues, $result); + } + + /** + * @return \Traversable<\Ibexa\Contracts\Core\Repository\Values\Content\Field> + */ + protected function getFields(): Traversable + { + $translatedFieldValueMap = [ + 'eng-GB' => [ + 'text1' => 'one', + 'text2' => 'two', + 'text3' => '', + ], + 'cro-HR' => [ + 'text1' => 'jedan', + 'text2' => 'dva', + 'text3' => '', + ], + ]; + + foreach ($translatedFieldValueMap as $languageCode => $fieldValues) { + foreach ($fieldValues as $fieldDefinitionIdentifier => $textValue) { + yield new Field( + [ + 'languageCode' => $languageCode, + 'fieldDefIdentifier' => $fieldDefinitionIdentifier, + 'value' => new TextLineValue($textValue), + 'fieldTypeIdentifier' => 'ezstring', + ] + ); + } + } + } + + protected function getFieldDefinitions(): APIFieldDefinitionCollection + { + return new FieldDefinitionCollection( + [ + new FieldDefinition( + [ + 'id' => '1', + 'identifier' => 'text1', + 'fieldTypeIdentifier' => 'ezstring', + ] + ), + new FieldDefinition( + [ + 'id' => '2', + 'identifier' => 'text2', + 'fieldTypeIdentifier' => 'ezstring', + ] + ), + new FieldDefinition( + [ + 'id' => '3', + 'identifier' => 'text3', + 'fieldTypeIdentifier' => 'ezstring', + ] + ), + ] + ); + } + + /** + * Build Content Object stub for testing purpose. + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + protected function buildTestContentObject() + { + return new Content( + [ + 'internalFields' => iterator_to_array($this->getFields()), + 'versionInfo' => new VersionInfo( + [ + 'languageCodes' => ['eng-GB', 'cro-HR'], + ] + ), + ] + ); + } + + protected function buildTestContentTypeStub( + string $nameSchema = '<name_schema>', + string $urlAliasSchema = '<url_alias_schema>' + ): ContentType { + return new ContentType( + [ + 'nameSchema' => $nameSchema, + 'urlAliasSchema' => $urlAliasSchema, + 'fieldDefinitions' => $this->getFieldDefinitions(), + ] + ); + } + + protected function getEventDispatcherMock( + AbstractSchemaEvent $event + ): EventDispatcherInterface { + $eventDispatcherMock = $this->getEventDispatcher(); + $eventDispatcherMock->method('dispatch') + ->willReturn($event); + + return $eventDispatcherMock; + } + + /** + * @param array{limit?: integer, sequence?: string} $settings + */ + private function buildNameSchemaService( + AbstractSchemaEvent $event, + array $settings = [] + ): NameSchemaService { + $fieldTypeRegistryMock = $this->getFieldTypeRegistryMock(); + $fieldTypeRegistryMock + ->method('getFieldType') + ->with('ezstring') + ->willReturn(new TextLineFieldType()); + + return new NameSchemaService( + $fieldTypeRegistryMock, + new SchemaIdentifierExtractor(), + $this->getEventDispatcherMock($event), + $settings + ); + } +} diff --git a/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php b/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php new file mode 100644 index 0000000000..29517d557c --- /dev/null +++ b/tests/lib/Repository/NameSchema/SchemaIdentifierExtractorTest.php @@ -0,0 +1,123 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\NameSchema; + +use Ibexa\Contracts\Core\Repository\NameSchema\SchemaIdentifierExtractorInterface; +use Ibexa\Core\Repository\NameSchema\SchemaIdentifierExtractor; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Repository\NameSchema\SchemaIdentifierExtractor + */ +final class SchemaIdentifierExtractorTest extends TestCase +{ + private SchemaIdentifierExtractorInterface $extractor; + + /** + * @return iterable<string, array{string, array<string, array<string>>}> + */ + public function getDataForTestExtract(): iterable + { + $schemaString = '<short_name|name>'; + yield $schemaString => [ + $schemaString, + [ + 'field' => ['short_name', 'name'], + ], + ]; + + $schemaString = '<custom_strategy:foo|field:bar>'; + yield $schemaString => [ + $schemaString, + [ + 'custom_strategy' => ['foo'], + 'field' => ['bar'], + ], + ]; + + $schemaString = '<custom_strategy:bar|baz>'; + yield $schemaString => [ + $schemaString, + [ + 'custom_strategy' => ['bar'], + 'field' => ['baz'], + ], + ]; + + $schemaString = '<custom_strategy:foo>-<field:bar>'; + yield $schemaString => [ + $schemaString, + [ + 'custom_strategy' => ['foo'], + 'field' => ['bar'], + ], + ]; + + $schemaString = '<custom_strategy:foo|custom_strategy:bar>-<field:bar|baz>'; + yield $schemaString => [ + $schemaString, + [ + 'custom_strategy' => ['foo', 'bar'], + 'field' => ['bar', 'baz'], + ], + ]; + + $schemaString = '<specification|(<name> <image1>)-<custom:bar|baz>-<field:bar|baz>'; + yield $schemaString => [ + $schemaString, + [ + 'field' => ['specification', 'name', 'image1', 'baz', 'bar'], + 'custom' => ['bar'], + ], + ]; + + $schemaString = '<specification|(<name> <image1>)-(<custom:bar(|baz|bar)>)-<field:bar|baz>'; + yield $schemaString => [ + $schemaString, + [ + 'field' => ['specification', 'name', 'image1', 'baz', 'bar'], + 'custom' => ['bar'], + ], + ]; + + $schemaString = '<description|(<attribute:mouse_type> <attribute:mouse_weight>)>'; + yield $schemaString => [ + $schemaString, + [ + 'field' => ['description'], + 'attribute' => ['mouse_type', 'mouse_weight'], + ], + ]; + + $schemaString = '<abc|(<xyz> <name>)><abc|(<attribute:color> <attribute:color>)>'; + yield $schemaString => [ + $schemaString, + [ + 'field' => ['abc', 'xyz', 'name'], + 'attribute' => ['color'], + ], + ]; + } + + protected function setUp(): void + { + $this->extractor = new SchemaIdentifierExtractor(); + } + + /** + * @dataProvider getDataForTestExtract + * + * @param array<string, array<string>> $expectedStrategyIdentifierMap + */ + public function testExtract(string $schemaString, array $expectedStrategyIdentifierMap): void + { + $extracted = $this->extractor->extract($schemaString); + self::assertSame($expectedStrategyIdentifierMap, $extracted); + } +} diff --git a/eZ/Publish/API/Repository/Tests/PHPUnitConstraint/AllValidationErrorsOccur.php b/tests/lib/Repository/PHPUnitConstraint/AllValidationErrorsOccur.php similarity index 77% rename from eZ/Publish/API/Repository/Tests/PHPUnitConstraint/AllValidationErrorsOccur.php rename to tests/lib/Repository/PHPUnitConstraint/AllValidationErrorsOccur.php index 9306dd79a6..bfa9c8ab3c 100644 --- a/eZ/Publish/API/Repository/Tests/PHPUnitConstraint/AllValidationErrorsOccur.php +++ b/tests/lib/Repository/PHPUnitConstraint/AllValidationErrorsOccur.php @@ -6,9 +6,9 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\PHPUnitConstraint; +namespace Ibexa\Tests\Core\Repository\PHPUnitConstraint; -use eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException; use PHPUnit\Framework\Constraint\Constraint as AbstractPHPUnitConstraint; use RecursiveArrayIterator; use RecursiveIteratorIterator; @@ -18,8 +18,8 @@ * PHPUnit's constraint checking that all the given validation error messages occur in the asserted * ContentFieldValidationException. * - * @see \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException - * @see \eZ\Publish\SPI\FieldType\ValidationError + * @see \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @see \Ibexa\Contracts\Core\FieldType\ValidationError */ class AllValidationErrorsOccur extends AbstractPHPUnitConstraint { @@ -40,7 +40,7 @@ public function __construct(array $expectedValidationErrorMessages) } /** - * @param \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException $other + * @param \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException $other * * @return bool */ @@ -57,7 +57,7 @@ protected function matches($other): bool } /** - * @param \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException $exception + * @param \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException $exception * * @return string[] */ @@ -67,7 +67,7 @@ private function extractAllFieldErrorMessages(ContentFieldValidationException $e } /** - * @param array<int, <string, array<\eZ\Publish\SPI\FieldType\ValidationError>>> $fieldErrors + * @param array<int, <string, array<\Ibexa\Contracts\Core\FieldType\ValidationError>>> $fieldErrors * * @return \Traversable<string> translated message string */ @@ -85,7 +85,7 @@ private function extractTranslatable(array $fieldErrors): Traversable } /** - * @param \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException $other + * @param \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException $other * * @return string */ @@ -110,3 +110,5 @@ public function toString(): string return "contain the messages: '{$messages}'"; } } + +class_alias(AllValidationErrorsOccur::class, 'eZ\Publish\API\Repository\Tests\PHPUnitConstraint\AllValidationErrorsOccur'); diff --git a/eZ/Publish/API/Repository/Tests/PHPUnitConstraint/ContentItemEquals.php b/tests/lib/Repository/PHPUnitConstraint/ContentItemEquals.php similarity index 92% rename from eZ/Publish/API/Repository/Tests/PHPUnitConstraint/ContentItemEquals.php rename to tests/lib/Repository/PHPUnitConstraint/ContentItemEquals.php index e4981c81a3..557b99ac89 100644 --- a/eZ/Publish/API/Repository/Tests/PHPUnitConstraint/ContentItemEquals.php +++ b/tests/lib/Repository/PHPUnitConstraint/ContentItemEquals.php @@ -6,10 +6,10 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\PHPUnitConstraint; +namespace Ibexa\Tests\Core\Repository\PHPUnitConstraint; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; use PHPUnit\Framework\Constraint\Constraint as AbstractPHPUnitConstraint; use PHPUnit\Framework\ExpectationFailedException; use SebastianBergmann\Comparator\ComparisonFailure; @@ -19,7 +19,7 @@ class ContentItemEquals extends AbstractPHPUnitConstraint { - /** @var \eZ\Publish\API\Repository\Values\Content\Content */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Content */ private $expectedContent; public function __construct(Content $expectedContent) @@ -155,3 +155,5 @@ private function compareStrings( ); } } + +class_alias(ContentItemEquals::class, 'eZ\Publish\API\Repository\Tests\PHPUnitConstraint\ContentItemEquals'); diff --git a/eZ/Publish/API/Repository/Tests/PHPUnitConstraint/ValidationErrorOccurs.php b/tests/lib/Repository/PHPUnitConstraint/ValidationErrorOccurs.php similarity index 76% rename from eZ/Publish/API/Repository/Tests/PHPUnitConstraint/ValidationErrorOccurs.php rename to tests/lib/Repository/PHPUnitConstraint/ValidationErrorOccurs.php index d5ee8af772..fd8433cd28 100644 --- a/eZ/Publish/API/Repository/Tests/PHPUnitConstraint/ValidationErrorOccurs.php +++ b/tests/lib/Repository/PHPUnitConstraint/ValidationErrorOccurs.php @@ -6,13 +6,13 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\PHPUnitConstraint; +namespace Ibexa\Tests\Core\Repository\PHPUnitConstraint; /** * PHPUnit constraint checking that the given ValidationError message occurs in asserted ContentFieldValidationException. * - * @see \eZ\Publish\API\Repository\Exceptions\ContentFieldValidationException - * @see \eZ\Publish\SPI\FieldType\ValidationError + * @see \Ibexa\Contracts\Core\Repository\Exceptions\ContentFieldValidationException + * @see \Ibexa\Contracts\Core\FieldType\ValidationError */ class ValidationErrorOccurs extends AllValidationErrorsOccur { @@ -39,3 +39,5 @@ public function toString(): string return "contain the message '{$this->expectedValidationErrorMessage}'"; } } + +class_alias(ValidationErrorOccurs::class, 'eZ\Publish\API\Repository\Tests\PHPUnitConstraint\ValidationErrorOccurs'); diff --git a/eZ/Publish/API/Repository/Tests/Parallel/ParallelProcessList.php b/tests/lib/Repository/Parallel/ParallelProcessList.php similarity index 79% rename from eZ/Publish/API/Repository/Tests/Parallel/ParallelProcessList.php rename to tests/lib/Repository/Parallel/ParallelProcessList.php index a98b8d726d..835e4dce2d 100644 --- a/eZ/Publish/API/Repository/Tests/Parallel/ParallelProcessList.php +++ b/tests/lib/Repository/Parallel/ParallelProcessList.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\Parallel; +namespace Ibexa\Tests\Core\Repository\Parallel; use Jenner\SimpleFork\Process; @@ -25,3 +25,5 @@ public function getIterator(): \Iterator return new \ArrayIterator($this->pool); } } + +class_alias(ParallelProcessList::class, 'eZ\Publish\API\Repository\Tests\Parallel\ParallelProcessList'); diff --git a/eZ/Publish/Core/Repository/Tests/Permission/CachedPermissionServiceTest.php b/tests/lib/Repository/Permission/CachedPermissionServiceTest.php similarity index 89% rename from eZ/Publish/Core/Repository/Tests/Permission/CachedPermissionServiceTest.php rename to tests/lib/Repository/Permission/CachedPermissionServiceTest.php index 146cead965..f37668b42f 100644 --- a/eZ/Publish/Core/Repository/Tests/Permission/CachedPermissionServiceTest.php +++ b/tests/lib/Repository/Permission/CachedPermissionServiceTest.php @@ -6,7 +6,7 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\Permission; +namespace Ibexa\Core\Repository\Permission; /** * Avoid test failure caused by time passing between generating expected & actual object. @@ -20,15 +20,15 @@ function time() return ++$time; } -namespace eZ\Publish\Core\Repository\Tests\Permission; +namespace Ibexa\Tests\Core\Repository\Permission; -use eZ\Publish\API\Repository\PermissionCriterionResolver; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\User\UserReference; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Repository\Permission\CachedPermissionService; +use Ibexa\Contracts\Core\Repository\PermissionCriterionResolver; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\User\UserReference; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Core\Repository\Permission\CachedPermissionService; use PHPUnit\Framework\TestCase; /** @@ -69,7 +69,7 @@ public function providerForTestPermissionResolverPassTrough() * * @param $method * @param array $arguments - * @param $return + * @param $expectedReturn */ public function testPermissionResolverPassTrough($method, array $arguments, $expectedReturn) { @@ -167,7 +167,7 @@ public function testSetCurrentUserReferenceCacheClear(): void * * @param int $ttl * - * @return \eZ\Publish\Core\Repository\Permission\CachedPermissionService + * @return \Ibexa\Core\Repository\Permission\CachedPermissionService */ protected function getCachedPermissionService($ttl = 5) { @@ -210,3 +210,5 @@ protected function getPermissionCriterionResolverMock($methods = []) ->getMockForAbstractClass(); } } + +class_alias(CachedPermissionServiceTest::class, 'eZ\Publish\Core\Repository\Tests\Permission\CachedPermissionServiceTest'); diff --git a/eZ/Publish/Core/Repository/Tests/Permission/PermissionCriterionResolverTest.php b/tests/lib/Repository/Permission/PermissionCriterionResolverTest.php similarity index 93% rename from eZ/Publish/Core/Repository/Tests/Permission/PermissionCriterionResolverTest.php rename to tests/lib/Repository/Permission/PermissionCriterionResolverTest.php index d4aa131c40..b9c041e7bd 100644 --- a/eZ/Publish/Core/Repository/Tests/Permission/PermissionCriterionResolverTest.php +++ b/tests/lib/Repository/Permission/PermissionCriterionResolverTest.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Tests\Permission; +namespace Ibexa\Tests\Core\Repository\Permission; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\User\User; -use eZ\Publish\Core\Limitation\TargetOnlyLimitationType; -use eZ\Publish\Core\Repository\Permission\LimitationService; -use eZ\Publish\Core\Repository\Permission\PermissionCriterionResolver; -use eZ\Publish\Core\Repository\Values\User\Policy; -use eZ\Publish\SPI\Limitation\Type; +use Ibexa\Contracts\Core\Limitation\Type; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Core\Limitation\TargetOnlyLimitationType; +use Ibexa\Core\Repository\Permission\LimitationService; +use Ibexa\Core\Repository\Permission\PermissionCriterionResolver; +use Ibexa\Core\Repository\Values\User\Policy; use PHPUnit\Framework\TestCase; /** @@ -337,7 +337,7 @@ public function testGetPermissionsCriterionBooleanPermissionSets($permissionSets * * @param string[]|null $methods * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\Permission\PermissionCriterionResolver + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Repository\Permission\PermissionCriterionResolver */ protected function getPermissionCriterionResolverMock($methods = []) { @@ -385,3 +385,5 @@ protected function getLimitationServiceMock($methods = []) ->getMock(); } } + +class_alias(PermissionCriterionResolverTest::class, 'eZ\Publish\Core\Repository\Tests\Permission\PermissionCriterionResolverTest'); diff --git a/tests/lib/Repository/Service/Mock/Base.php b/tests/lib/Repository/Service/Mock/Base.php new file mode 100644 index 0000000000..dfda628e4f --- /dev/null +++ b/tests/lib/Repository/Service/Mock/Base.php @@ -0,0 +1,463 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Service\Mock; + +use Ibexa\Contracts\Core\Persistence\Filter\Content\Handler as ContentFilteringHandler; +use Ibexa\Contracts\Core\Persistence\Filter\Location\Handler as LocationFilteringHandler; +use Ibexa\Contracts\Core\Persistence\Handler; +use Ibexa\Contracts\Core\Repository\LanguageResolver; +use Ibexa\Contracts\Core\Repository\NameSchema\NameSchemaServiceInterface; +use Ibexa\Contracts\Core\Repository\PasswordHashService; +use Ibexa\Contracts\Core\Repository\PermissionService; +use Ibexa\Contracts\Core\Repository\Repository as APIRepository; +use Ibexa\Contracts\Core\Repository\Strategy\ContentThumbnail\ThumbnailStrategy; +use Ibexa\Contracts\Core\Repository\Validator\ContentValidator; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\FieldType\FieldTypeRegistry; +use Ibexa\Core\Repository\FieldTypeService; +use Ibexa\Core\Repository\Helper\RelationProcessor; +use Ibexa\Core\Repository\Mapper\ContentDomainMapper; +use Ibexa\Core\Repository\Mapper\ContentMapper; +use Ibexa\Core\Repository\Mapper\ContentTypeDomainMapper; +use Ibexa\Core\Repository\Mapper\RoleDomainMapper; +use Ibexa\Core\Repository\Permission\LimitationService; +use Ibexa\Core\Repository\ProxyFactory\ProxyDomainMapperFactoryInterface; +use Ibexa\Core\Repository\Repository; +use Ibexa\Core\Repository\Strategy\ContentValidator\ContentValidatorStrategy; +use Ibexa\Core\Repository\User\PasswordValidatorInterface; +use Ibexa\Core\Repository\Validator\ContentCreateStructValidator; +use Ibexa\Core\Repository\Validator\ContentUpdateStructValidator; +use Ibexa\Core\Repository\Validator\VersionValidator; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\User\User; +use Ibexa\Core\Search\Common\BackgroundIndexer\NullIndexer; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +/** + * Base test case for tests on services using Mock testing. + */ +abstract class Base extends TestCase +{ + /** @var \Ibexa\Contracts\Core\Repository\Repository */ + private $repository; + + /** @var \Ibexa\Contracts\Core\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject */ + private $repositoryMock; + + /** @var \Ibexa\Contracts\Core\Repository\PermissionService|\PHPUnit\Framework\MockObject\MockObject */ + private $permissionServiceMock; + + /** @var \Ibexa\Contracts\Core\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject */ + private $persistenceMock; + + /** @var \Ibexa\Contracts\Core\Repository\Strategy\ContentThumbnail\ThumbnailStrategy|\PHPUnit\Framework\MockObject\MockObject */ + private $thumbnailStrategyMock; + + /** + * The Content / Location / Search ... handlers for the persistence / Search / .. handler mocks. + * + * @var \PHPUnit\Framework\MockObject\MockObject[] Key is relative to "Ibexa\Contracts\Core\" + * + * @see getPersistenceMockHandler() + */ + private $spiMockHandlers = []; + + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Repository\Mapper\ContentTypeDomainMapper */ + private $contentTypeDomainMapperMock; + + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Repository\Mapper\ContentDomainMapper */ + private $contentDomainMapperMock; + + /** @var \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Repository\Permission\LimitationService */ + private $limitationServiceMock; + + /** @var \Ibexa\Contracts\Core\Repository\LanguageResolver|\PHPUnit\Framework\MockObject\MockObject */ + private $languageResolverMock; + + /** @var \Ibexa\Core\Repository\Mapper\RoleDomainMapper|\PHPUnit\Framework\MockObject\MockObject */ + protected $roleDomainMapperMock; + + /** @var \Ibexa\Core\Repository\Mapper\ContentMapper|\PHPUnit\Framework\MockObject\MockObject */ + protected $contentMapperMock; + + /** @var \Ibexa\Contracts\Core\Repository\Validator\ContentValidator|\PHPUnit\Framework\MockObject\MockObject */ + protected $contentValidatorStrategyMock; + + /** @var \Ibexa\Contracts\Core\Persistence\Filter\Content\Handler|\PHPUnit\Framework\MockObject\MockObject */ + private $contentFilteringHandlerMock; + + /** @var \Ibexa\Contracts\Core\Persistence\Filter\Location\Handler|\PHPUnit\Framework\MockObject\MockObject */ + private $locationFilteringHandlerMock; + + /** + * Get Real repository with mocked dependencies. + * + * @param array $serviceSettings If set then non shared instance of Repository is returned + * + * @return \Ibexa\Contracts\Core\Repository\Repository + */ + protected function getRepository(array $serviceSettings = []) + { + if ($this->repository === null || !empty($serviceSettings)) { + $repository = new Repository( + $this->getPersistenceMock(), + $this->getSPIMockHandler('Search\\Handler'), + new NullIndexer(), + $this->getRelationProcessorMock(), + $this->getFieldTypeRegistryMock(), + $this->createMock(PasswordHashService::class), + $this->getThumbnailStrategy(), + $this->createMock(ProxyDomainMapperFactoryInterface::class), + $this->getContentDomainMapperMock(), + $this->getContentTypeDomainMapperMock(), + $this->getRoleDomainMapperMock(), + $this->getContentMapper(), + $this->getContentValidatorStrategy(), + $this->getLimitationServiceMock(), + $this->getLanguageResolverMock(), + $this->getPermissionServiceMock(), + $this->getContentFilteringHandlerMock(), + $this->getLocationFilteringHandlerMock(), + $this->createMock(PasswordValidatorInterface::class), + $this->createMock(ConfigResolverInterface::class), + $this->createMock(NameSchemaServiceInterface::class), + $serviceSettings, + ); + + if (!empty($serviceSettings)) { + return $repository; + } + + $this->repository = $repository; + } + + return $this->repository; + } + + protected $fieldTypeServiceMock; + + /** + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Core\Repository\FieldTypeService + */ + protected function getFieldTypeServiceMock() + { + if (!isset($this->fieldTypeServiceMock)) { + $this->fieldTypeServiceMock = $this->createMock(FieldTypeService::class); + } + + return $this->fieldTypeServiceMock; + } + + protected $fieldTypeRegistryMock; + + /** + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\FieldType\FieldTypeRegistry + */ + protected function getFieldTypeRegistryMock() + { + if (!isset($this->fieldTypeRegistryMock)) { + $this->fieldTypeRegistryMock = $this->createMock(FieldTypeRegistry::class); + } + + return $this->fieldTypeRegistryMock; + } + + protected EventDispatcherInterface $eventDispatcher; + + /** + * @return \Symfony\Contracts\EventDispatcher\EventDispatcherInterface&\PHPUnit\Framework\MockObject\MockObject + */ + protected function getEventDispatcher(): EventDispatcherInterface + { + if (!isset($this->eventDispatcher)) { + $this->eventDispatcher = $this->createMock(EventDispatcherInterface::class); + } + + return $this->eventDispatcher; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Strategy\ContentThumbnail\ThumbnailStrategy|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getThumbnailStrategy() + { + if (!isset($this->thumbnailStrategyMock)) { + $this->thumbnailStrategyMock = $this->createMock(ThumbnailStrategy::class); + } + + return $this->thumbnailStrategyMock; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getRepositoryMock() + { + if (!isset($this->repositoryMock)) { + $this->repositoryMock = $this->createMock(APIRepository::class); + } + + return $this->repositoryMock; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getPermissionResolverMock() + { + return $this->getPermissionServiceMock(); + } + + /** + * @return \Ibexa\Contracts\Core\Repository\PermissionService|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getPermissionServiceMock(): PermissionService + { + if (!isset($this->permissionServiceMock)) { + $this->permissionServiceMock = $this->createMock(PermissionService::class); + } + + return $this->permissionServiceMock; + } + + /** + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Repository\Mapper\ContentDomainMapper + */ + protected function getContentDomainMapperMock(): MockObject + { + if (!isset($this->contentDomainMapperMock)) { + $this->contentDomainMapperMock = $this->createMock(ContentDomainMapper::class); + } + + return $this->contentDomainMapperMock; + } + + /** + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Repository\Mapper\ContentTypeDomainMapper + */ + protected function getContentTypeDomainMapperMock() + { + if (!isset($this->contentTypeDomainMapperMock)) { + $this->contentTypeDomainMapperMock = $this->createMock(ContentTypeDomainMapper::class); + } + + return $this->contentTypeDomainMapperMock; + } + + /** + * Returns a persistence Handler mock. + * + * @return \Ibexa\Contracts\Core\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getPersistenceMock() + { + if (!isset($this->persistenceMock)) { + $this->persistenceMock = $this->createMock(Handler::class); + + $this->persistenceMock->expects($this->any()) + ->method('contentHandler') + ->will($this->returnValue($this->getPersistenceMockHandler('Content\\Handler'))); + + $this->persistenceMock->expects($this->any()) + ->method('contentTypeHandler') + ->will($this->returnValue($this->getPersistenceMockHandler('Content\\Type\\Handler'))); + + $this->persistenceMock->expects($this->any()) + ->method('contentLanguageHandler') + ->will($this->returnValue($this->getPersistenceMockHandler('Content\\Language\\Handler'))); + + $this->persistenceMock->expects($this->any()) + ->method('locationHandler') + ->will($this->returnValue($this->getPersistenceMockHandler('Content\\Location\\Handler'))); + + $this->persistenceMock->expects($this->any()) + ->method('objectStateHandler') + ->will($this->returnValue($this->getPersistenceMockHandler('Content\\ObjectState\\Handler'))); + + $this->persistenceMock->expects($this->any()) + ->method('trashHandler') + ->will($this->returnValue($this->getPersistenceMockHandler('Content\\Location\\Trash\\Handler'))); + + $this->persistenceMock->expects($this->any()) + ->method('userHandler') + ->will($this->returnValue($this->getPersistenceMockHandler('User\\Handler'))); + + $this->persistenceMock->expects($this->any()) + ->method('sectionHandler') + ->will($this->returnValue($this->getPersistenceMockHandler('Content\\Section\\Handler'))); + + $this->persistenceMock->expects($this->any()) + ->method('urlAliasHandler') + ->will($this->returnValue($this->getPersistenceMockHandler('Content\\UrlAlias\\Handler'))); + + $this->persistenceMock->expects($this->any()) + ->method('urlWildcardHandler') + ->will($this->returnValue($this->getPersistenceMockHandler('Content\\UrlWildcard\\Handler'))); + + $this->persistenceMock->expects($this->any()) + ->method('urlWildcardHandler') + ->will($this->returnValue($this->getPersistenceMockHandler('URL\\Handler'))); + } + + return $this->persistenceMock; + } + + protected function getRelationProcessorMock() + { + return $this->createMock(RelationProcessor::class); + } + + /** + * Returns a SPI Handler mock. + * + * @param string $handler For instance "Content\Type\Handler" or "Search\Handler", must be relative to "Ibexa\Contracts\Core" + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + protected function getSPIMockHandler($handler) + { + if (!isset($this->spiMockHandlers[$handler])) { + $this->spiMockHandlers[$handler] = $this->getMockBuilder("Ibexa\\Contracts\\Core\\{$handler}") + ->setMethods([]) + ->disableOriginalConstructor() + ->setConstructorArgs([]) + ->getMock(); + } + + return $this->spiMockHandlers[$handler]; + } + + /** + * Returns a persistence Handler mock. + * + * @param string $handler For instance "Content\Type\Handler", must be relative to "Ibexa\Contracts\Core\Persistence" + * + * @return \PHPUnit\Framework\MockObject\MockObject + */ + protected function getPersistenceMockHandler($handler) + { + return $this->getSPIMockHandler("Persistence\\{$handler}"); + } + + /** + * Returns User stub with $id as User/Content id. + * + * @param int $id + * + * @return \Ibexa\Contracts\Core\Repository\Values\User\User + */ + protected function getStubbedUser($id) + { + return new User( + [ + 'content' => new Content( + [ + 'versionInfo' => new VersionInfo( + [ + 'contentInfo' => new ContentInfo(['id' => $id]), + ] + ), + 'internalFields' => [], + ] + ), + ] + ); + } + + /** + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Repository\Permission\LimitationService + */ + protected function getLimitationServiceMock(): MockObject + { + if ($this->limitationServiceMock === null) { + $this->limitationServiceMock = $this->createMock(LimitationService::class); + } + + return $this->limitationServiceMock; + } + + protected function getLanguageResolverMock(): LanguageResolver + { + if ($this->languageResolverMock === null) { + $this->languageResolverMock = $this->createMock(LanguageResolver::class); + } + + return $this->languageResolverMock; + } + + /** + * @param string[] $methods + * + * @return \Ibexa\Core\Repository\Mapper\RoleDomainMapper|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getRoleDomainMapperMock(array $methods = []): RoleDomainMapper + { + if ($this->roleDomainMapperMock === null) { + $mockBuilder = $this->getMockBuilder(RoleDomainMapper::class); + if (!empty($methods)) { + $mockBuilder->onlyMethods($methods); + } + $this->roleDomainMapperMock = $mockBuilder + ->disableOriginalConstructor() + ->getMock(); + } + + return $this->roleDomainMapperMock; + } + + protected function getContentMapper(): ContentMapper + { + return new ContentMapper( + $this->getPersistenceMock()->contentLanguageHandler(), + $this->getFieldTypeRegistryMock() + ); + } + + protected function getContentValidatorStrategy(): ContentValidator + { + $validators = [ + new ContentCreateStructValidator( + $this->getContentMapper(), + $this->getFieldTypeRegistryMock() + ), + new ContentUpdateStructValidator( + $this->getContentMapper(), + $this->getFieldTypeRegistryMock(), + $this->getPersistenceMock()->contentLanguageHandler() + ), + new VersionValidator( + $this->getFieldTypeRegistryMock(), + ), + ]; + + return new ContentValidatorStrategy($validators); + } + + protected function getContentFilteringHandlerMock(): ContentFilteringHandler + { + if (null === $this->contentFilteringHandlerMock) { + $this->contentFilteringHandlerMock = $this->createMock(ContentFilteringHandler::class); + } + + return $this->contentFilteringHandlerMock; + } + + private function getLocationFilteringHandlerMock(): LocationFilteringHandler + { + if (null === $this->locationFilteringHandlerMock) { + $this->locationFilteringHandlerMock = $this->createMock(LocationFilteringHandler::class); + } + + return $this->locationFilteringHandlerMock; + } +} + +class_alias(Base::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\Base'); diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/BookmarkTest.php b/tests/lib/Repository/Service/Mock/BookmarkTest.php similarity index 85% rename from eZ/Publish/Core/Repository/Tests/Service/Mock/BookmarkTest.php rename to tests/lib/Repository/Service/Mock/BookmarkTest.php index 5c23d98b3e..234032eb1b 100644 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/BookmarkTest.php +++ b/tests/lib/Repository/Service/Mock/BookmarkTest.php @@ -6,18 +6,19 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; +namespace Ibexa\Tests\Core\Repository\Service\Mock; use Exception; -use eZ\Publish\API\Repository\LocationService; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\Core\Repository\BookmarkService; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\Core\Repository\Values\Content\Location; -use eZ\Publish\Core\Repository\Values\User\UserReference; -use eZ\Publish\SPI\Persistence\Bookmark\Bookmark; -use eZ\Publish\SPI\Persistence\Bookmark\CreateStruct; +use Ibexa\Contracts\Core\Persistence\Bookmark\Bookmark; +use Ibexa\Contracts\Core\Persistence\Bookmark\CreateStruct; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\LocationService; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Core\Repository\BookmarkService; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Repository\Values\User\UserReference; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; use PHPUnit\Framework\MockObject\MockObject; class BookmarkTest extends BaseServiceMockTest @@ -26,7 +27,7 @@ class BookmarkTest extends BaseServiceMockTest public const CURRENT_USER_ID = 7; public const LOCATION_ID = 1; - /** @var \eZ\Publish\SPI\Persistence\Bookmark\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Bookmark\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $bookmarkHandler; protected function setUp(): void @@ -49,7 +50,7 @@ protected function setUp(): void } /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::createBookmark + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::createBookmark */ public function testCreateBookmark() { @@ -79,11 +80,11 @@ public function testCreateBookmark() } /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::createBookmark + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::createBookmark */ public function testCreateBookmarkThrowsInvalidArgumentException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $location = $this->createLocation(self::LOCATION_ID); @@ -103,7 +104,7 @@ public function testCreateBookmarkThrowsInvalidArgumentException() } /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::createBookmark + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::createBookmark */ public function testCreateBookmarkWithRollback() { @@ -130,7 +131,7 @@ public function testCreateBookmarkWithRollback() } /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::deleteBookmark + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::deleteBookmark */ public function testDeleteBookmarkExisting() { @@ -157,7 +158,7 @@ public function testDeleteBookmarkExisting() } /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::deleteBookmark + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::deleteBookmark */ public function testDeleteBookmarkWithRollback() { @@ -184,11 +185,11 @@ public function testDeleteBookmarkWithRollback() } /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::deleteBookmark + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::deleteBookmark */ public function testDeleteBookmarkNonExisting() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $location = $this->createLocation(self::LOCATION_ID); @@ -208,7 +209,7 @@ public function testDeleteBookmarkNonExisting() } /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::loadBookmarks + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::loadBookmarks */ public function testLoadBookmarks() { @@ -255,7 +256,7 @@ public function testLoadBookmarks() } /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::loadBookmarks + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::loadBookmarks */ public function testLoadBookmarksEmptyList() { @@ -276,7 +277,7 @@ public function testLoadBookmarksEmptyList() } /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::isBookmarked + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::isBookmarked */ public function testLocationShouldNotBeBookmarked() { @@ -290,7 +291,7 @@ public function testLocationShouldNotBeBookmarked() } /** - * @covers \eZ\Publish\Core\Repository\BookmarkService::isBookmarked + * @covers \Ibexa\Contracts\Core\Repository\BookmarkService::isBookmarked */ public function testLocationShouldBeBookmarked() { @@ -358,7 +359,7 @@ private function createLocation(int $id = self::CURRENT_USER_ID, string $name = } /** - * @return \eZ\Publish\API\Repository\BookmarkService|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\Repository\BookmarkService|\PHPUnit\Framework\MockObject\MockObject */ private function createBookmarkService(array $methods = null) { @@ -369,3 +370,5 @@ private function createBookmarkService(array $methods = null) ->getMock(); } } + +class_alias(BookmarkTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\BookmarkTest'); diff --git a/tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php b/tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php new file mode 100644 index 0000000000..525f740ea3 --- /dev/null +++ b/tests/lib/Repository/Service/Mock/ContentDomainMapperTest.php @@ -0,0 +1,350 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Service\Mock; + +use DateTime; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo as SPIContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\Field as PersistenceContentField; +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\Type as PersistenceContentType; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo as SPIVersionInfo; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Location as APILocation; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Mapper\ContentDomainMapper; +use Ibexa\Core\Repository\ProxyFactory\ProxyDomainMapperInterface; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinitionCollection; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; +use Psr\Log\LoggerInterface; +use Symfony\Bridge\PhpUnit\ExpectDeprecationTrait; + +/** + * @covers \Ibexa\Core\Repository\Mapper\ContentDomainMapper + */ +final class ContentDomainMapperTest extends BaseServiceMockTest +{ + use ExpectDeprecationTrait; + + private const EXAMPLE_CONTENT_TYPE_ID = 1; + private const EXAMPLE_SECTION_ID = 1; + private const EXAMPLE_MAIN_LOCATION_ID = 1; + private const EXAMPLE_MAIN_LANGUAGE_CODE = 'ger-DE'; + private const EXAMPLE_OWNER_ID = 1; + private const EXAMPLE_INITIAL_LANGUAGE_CODE = 'eng-GB'; + private const EXAMPLE_CREATOR_ID = 23; + + /** + * @dataProvider providerForBuildVersionInfo + */ + public function testBuildVersionInfo(SPIVersionInfo $spiVersionInfo) + { + $languageHandlerMock = $this->getLanguageHandlerMock(); + $languageHandlerMock->expects($this->never())->method('load'); + + $versionInfo = $this->getContentDomainMapper()->buildVersionInfoDomainObject($spiVersionInfo); + + $this->assertInstanceOf(APIVersionInfo::class, $versionInfo); + } + + public function testBuildLocationWithContentForRootLocation() + { + $spiRootLocation = new Location(['id' => 1, 'parentId' => 1]); + $apiRootLocation = $this->getContentDomainMapper()->buildLocationWithContent($spiRootLocation, null); + + $legacyDateTime = new DateTime(); + $legacyDateTime->setTimestamp(1030968000); + + $expectedContentInfo = new ContentInfo([ + 'id' => 0, + 'name' => 'Top Level Nodes', + 'sectionId' => 1, + 'mainLocationId' => 1, + 'contentTypeId' => 1, + 'currentVersionNo' => 1, + 'published' => 1, + 'ownerId' => 14, + 'modificationDate' => $legacyDateTime, + 'publishedDate' => $legacyDateTime, + 'alwaysAvailable' => 1, + 'remoteId' => null, + 'mainLanguageCode' => 'eng-GB', + ]); + + $expectedContent = new Content([ + 'versionInfo' => new VersionInfo([ + 'names' => [ + $expectedContentInfo->mainLanguageCode => $expectedContentInfo->name, + ], + 'contentInfo' => $expectedContentInfo, + 'versionNo' => $expectedContentInfo->currentVersionNo, + 'modificationDate' => $expectedContentInfo->modificationDate, + 'creationDate' => $expectedContentInfo->modificationDate, + 'creatorId' => $expectedContentInfo->ownerId, + ]), + ]); + + $this->assertInstanceOf(APILocation::class, $apiRootLocation); + $this->assertEquals($spiRootLocation->id, $apiRootLocation->id); + $this->assertEquals($expectedContentInfo->id, $apiRootLocation->getContentInfo()->id); + $this->assertEquals($expectedContent, $apiRootLocation->getContent()); + } + + public function testBuildLocationWithContentThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$content\' is invalid: Location 2 has missing Content'); + + $nonRootLocation = new Location(['id' => 2, 'parentId' => 1]); + + $this->getContentDomainMapper()->buildLocationWithContent($nonRootLocation, null); + } + + public function testBuildLocationWithContentIsAlignedWithBuildLocation() + { + $spiRootLocation = new Location(['id' => 1, 'parentId' => 1]); + + $this->assertEquals( + $this->getContentDomainMapper()->buildLocationWithContent($spiRootLocation, null), + $this->getContentDomainMapper()->buildLocation($spiRootLocation) + ); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\Exception + * + * @group legacy + */ + public function testBuildDomainFieldsDeprecatedBehavior(): void + { + $persistenceFields = [new PersistenceContentField()]; + $persistenceContentType = $this->createMock(PersistenceContentType::class); + $apiContentTypeMock = $this->createMock(ContentType::class); + $apiContentTypeMock->method('getFieldDefinitions')->willReturn(new FieldDefinitionCollection()); + $this + ->getContentTypeDomainMapperMock() + ->method('buildContentTypeDomainObject') + ->with($persistenceContentType, [])->willReturn($apiContentTypeMock) + ; + + $this->expectDeprecation( + 'Since ibexa/core 4.6: Passing Ibexa\Contracts\Core\Persistence\Content\Type instead of ' . + 'Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType as 2nd argument of ' . + 'Ibexa\Core\Repository\Mapper\ContentDomainMapper::buildDomainFields() method is deprecated and will cause ' . + 'a fatal error in 5.0. Build Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType using ' . + 'Ibexa\Core\Repository\Mapper\ContentTypeDomainMapper::buildContentTypeDomainObject prior passing it to the method' + ); + + $this->getContentDomainMapper()->buildDomainFields($persistenceFields, $persistenceContentType); + } + + public function providerForBuildVersionInfo() + { + $properties = [ + 'contentInfo' => new SPIContentInfo([ + 'contentTypeId' => self::EXAMPLE_CONTENT_TYPE_ID, + 'sectionId' => self::EXAMPLE_SECTION_ID, + 'mainLocationId' => self::EXAMPLE_MAIN_LOCATION_ID, + 'mainLanguageCode' => self::EXAMPLE_MAIN_LANGUAGE_CODE, + 'ownerId' => self::EXAMPLE_OWNER_ID, + ]), + 'creatorId' => self::EXAMPLE_CREATOR_ID, + 'initialLanguageCode' => self::EXAMPLE_INITIAL_LANGUAGE_CODE, + ]; + + return [ + [ + new SPIVersionInfo( + $properties + [ + 'status' => 44, + ] + ), + ], + [ + new SPIVersionInfo( + $properties + [ + 'status' => SPIVersionInfo::STATUS_DRAFT, + ] + ), + ], + [ + new SPIVersionInfo( + $properties + [ + 'status' => SPIVersionInfo::STATUS_PENDING, + ] + ), + ], + [ + new SPIVersionInfo( + $properties + [ + 'status' => SPIVersionInfo::STATUS_ARCHIVED, + 'languageCodes' => ['eng-GB', 'nor-NB', 'fre-FR'], + ] + ), + ], + [ + new SPIVersionInfo( + $properties + [ + 'status' => SPIVersionInfo::STATUS_PUBLISHED, + ] + ), + ], + ]; + } + + public function providerForBuildLocationDomainObjectsOnSearchResult() + { + $properties = [ + 'contentTypeId' => self::EXAMPLE_CONTENT_TYPE_ID, + 'sectionId' => self::EXAMPLE_SECTION_ID, + 'mainLocationId' => self::EXAMPLE_MAIN_LOCATION_ID, + 'mainLanguageCode' => self::EXAMPLE_MAIN_LANGUAGE_CODE, + 'ownerId' => self::EXAMPLE_OWNER_ID, + ]; + + $locationHits = [ + new Location(['id' => 21, 'contentId' => 32, 'parentId' => 1]), + new Location(['id' => 22, 'contentId' => 33, 'parentId' => 1]), + ]; + + return [ + [ + $locationHits, + [32, 33], + [], + [ + 32 => new SPIContentInfo($properties + ['id' => 32]), + 33 => new SPIContentInfo($properties + ['id' => 33]), + ], + 0, + ], + [ + $locationHits, + [32, 33], + ['languages' => ['eng-GB']], + [ + 32 => new SPIContentInfo($properties + ['id' => 32]), + ], + 1, + ], + [ + $locationHits, + [32, 33], + ['languages' => ['eng-GB']], + [], + 2, + ], + ]; + } + + /** + * @dataProvider providerForBuildLocationDomainObjectsOnSearchResult + * + * @param array $locationHits + * @param array $contentIds + * @param array $languageFilter + * @param array $contentInfoList + * @param int $missing + */ + public function testBuildLocationDomainObjectsOnSearchResult( + array $locationHits, + array $contentIds, + array $languageFilter, + array $contentInfoList, + int $missing + ) { + $contentHandlerMock = $this->getContentHandlerMock(); + $contentHandlerMock + ->expects($this->once()) + ->method('loadContentInfoList') + ->with($contentIds) + ->willReturn($contentInfoList); + + $result = new SearchResult(['totalCount' => 10]); + foreach ($locationHits as $locationHit) { + $result->searchHits[] = new SearchHit(['valueObject' => $locationHit]); + } + + $spiResult = clone $result; + $missingLocations = $this->getContentDomainMapper()->buildLocationDomainObjectsOnSearchResult( + $result, + $languageFilter + ); + $this->assertIsArray($missingLocations); + + if (!$missing) { + $this->assertEmpty($missingLocations); + } else { + $this->assertNotEmpty($missingLocations); + } + + $this->assertCount($missing, $missingLocations); + $this->assertEquals($spiResult->totalCount - $missing, $result->totalCount); + $this->assertCount(count($spiResult->searchHits) - $missing, $result->searchHits); + } + + /** + * Returns ContentDomainMapper. + * + * @return \Ibexa\Core\Repository\Mapper\ContentDomainMapper + */ + protected function getContentDomainMapper(): ContentDomainMapper + { + return new ContentDomainMapper( + $this->getContentHandlerMock(), + $this->getPersistenceMockHandler('Content\\Location\\Handler'), + $this->getTypeHandlerMock(), + $this->getContentTypeDomainMapperMock(), + $this->getLanguageHandlerMock(), + $this->getFieldTypeRegistryMock(), + $this->getThumbnailStrategy(), + $this->getLoggerMock(), + $this->getProxyFactoryMock() + ); + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Handler|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getContentHandlerMock() + { + return $this->getPersistenceMockHandler('Content\\Handler'); + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Language\Handler|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getLanguageHandlerMock() + { + return $this->getPersistenceMockHandler('Content\\Language\\Handler'); + } + + /** + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Handler|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getTypeHandlerMock() + { + return $this->getPersistenceMockHandler('Content\\Type\\Handler'); + } + + protected function getProxyFactoryMock(): ProxyDomainMapperInterface + { + return $this->createMock(ProxyDomainMapperInterface::class); + } + + protected function getLoggerMock(): LoggerInterface + { + return $this->createMock(LoggerInterface::class); + } +} + +class_alias(ContentDomainMapperTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\DomainMapperTest'); diff --git a/tests/lib/Repository/Service/Mock/ContentTest.php b/tests/lib/Repository/Service/Mock/ContentTest.php new file mode 100644 index 0000000000..b6d49d0d9e --- /dev/null +++ b/tests/lib/Repository/Service/Mock/ContentTest.php @@ -0,0 +1,6315 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Service\Mock; + +use Exception; +use Ibexa\Contracts\Core\FieldType\FieldType; +use Ibexa\Contracts\Core\FieldType\FieldType as SPIFieldType; +use Ibexa\Contracts\Core\FieldType\Value as SPIValue; +use Ibexa\Contracts\Core\Limitation\Target\DestinationLocation; +use Ibexa\Contracts\Core\Persistence\Content as SPIContent; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo as SPIContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\CreateStruct as SPIContentCreateStruct; +use Ibexa\Contracts\Core\Persistence\Content\Field as SPIField; +use Ibexa\Contracts\Core\Persistence\Content\Location as SPILocation; +use Ibexa\Contracts\Core\Persistence\Content\MetadataUpdateStruct as SPIMetadataUpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState as SPIObjectState; +use Ibexa\Contracts\Core\Persistence\Content\ObjectState\Group as SPIObjectStateGroup; +use Ibexa\Contracts\Core\Persistence\Content\UpdateStruct as SPIContentUpdateStruct; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo as SPIVersionInfo; +use Ibexa\Contracts\Core\Repository\ContentTypeService as APIContentTypeService; +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as APINotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\LocationService as APILocationService; +use Ibexa\Contracts\Core\Repository\NameSchema\NameSchemaServiceInterface; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\Content\Content as APIContent; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentCreateStruct as APIContentCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo as APIContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\Location as APILocation; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType as APIContentType; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition; +use Ibexa\Core\Base\Exceptions\ContentFieldValidationException; +use Ibexa\Core\Base\Exceptions\ContentValidationException; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\FieldType\Value; +use Ibexa\Core\Repository\ContentService; +use Ibexa\Core\Repository\Helper\RelationProcessor; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Core\Repository\Values\Content\ContentUpdateStruct; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinitionCollection; +use Ibexa\Core\Repository\Values\User\UserReference; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; + +/** + * Mock test case for Content service. + */ +class ContentTest extends BaseServiceMockTest +{ + private const EMPTY_FIELD_VALUE = 'empty'; + + /** + * Test for the __construct() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::__construct + */ + public function testConstructor(): void + { + $repositoryMock = $this->getRepositoryMock(); + /** @var \Ibexa\Contracts\Core\Persistence\Handler $persistenceHandlerMock */ + $persistenceHandlerMock = $this->getPersistenceMockHandler('Handler'); + $contentDomainMapperMock = $this->getContentDomainMapperMock(); + $relationProcessorMock = $this->getRelationProcessorMock(); + $nameSchemaServiceMock = $this->getNameSchemaServiceMock(); + $fieldTypeRegistryMock = $this->getFieldTypeRegistryMock(); + $permissionServiceMock = $this->getPermissionServiceMock(); + $contentMapper = $this->getContentMapper(); + $contentValidatorStrategy = $this->getContentValidatorStrategy(); + $contentFilteringHandlerMock = $this->getContentFilteringHandlerMock(); + $settings = [ + 'default_version_archive_limit' => 10, + 'remove_archived_versions_on_publish' => true, + ]; + + new ContentService( + $repositoryMock, + $persistenceHandlerMock, + $contentDomainMapperMock, + $relationProcessorMock, + $nameSchemaServiceMock, + $fieldTypeRegistryMock, + $permissionServiceMock, + $contentMapper, + $contentValidatorStrategy, + $contentFilteringHandlerMock, + $settings + ); + } + + /** + * Test for the loadVersionInfo() method, of published version. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById + */ + public function testLoadVersionInfoById() + { + $contentServiceMock = $this->getPartlyMockedContentService(['loadContentInfo']); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + $domainMapperMock = $this->getContentDomainMapperMock(); + $versionInfoMock = $this->createMock(APIVersionInfo::class); + $permissionResolver = $this->getPermissionResolverMock(); + + $versionInfoMock->expects($this->once()) + ->method('isPublished') + ->willReturn(true); + + $contentServiceMock->expects($this->never()) + ->method('loadContentInfo'); + + $contentHandler->expects($this->once()) + ->method('loadVersionInfo') + ->with( + $this->equalTo(42), + $this->equalTo(null) + )->will( + $this->returnValue(new SPIVersionInfo()) + ); + + $domainMapperMock->expects($this->once()) + ->method('buildVersionInfoDomainObject') + ->with(new SPIVersionInfo()) + ->will($this->returnValue($versionInfoMock)); + + $permissionResolver->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('read'), + $this->equalTo($versionInfoMock) + )->will($this->returnValue(true)); + + $result = $contentServiceMock->loadVersionInfoById(42); + + $this->assertEquals($versionInfoMock, $result); + } + + /** + * Test for the loadVersionInfo() method, of a draft. + * + * @depends testLoadVersionInfoById + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById + */ + public function testLoadVersionInfoByIdAndVersionNumber() + { + $permissionResolver = $this->getPermissionResolverMock(); + $contentServiceMock = $this->getPartlyMockedContentService(['loadContentInfo']); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + $domainMapperMock = $this->getContentDomainMapperMock(); + $versionInfoMock = $this->createMock(APIVersionInfo::class); + + $versionInfoMock->expects($this->any()) + ->method('__get') + ->with('status') + ->willReturn(APIVersionInfo::STATUS_DRAFT); + + $contentServiceMock->expects($this->never()) + ->method('loadContentInfo'); + + $contentHandler->expects($this->once()) + ->method('loadVersionInfo') + ->with( + $this->equalTo(42), + $this->equalTo(2) + )->willReturn(new SPIVersionInfo()); + + $domainMapperMock->expects($this->once()) + ->method('buildVersionInfoDomainObject') + ->with(new SPIVersionInfo()) + ->willReturn($versionInfoMock); + + $permissionResolver->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('versionread'), + $this->equalTo($versionInfoMock) + )->willReturn(true); + + $result = $contentServiceMock->loadVersionInfoById(42, 2); + + $this->assertEquals($versionInfoMock, $result); + } + + /** + * Test for the loadVersionInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById + */ + public function testLoadVersionInfoByIdThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $contentServiceMock = $this->getPartlyMockedContentService(['loadContentInfo']); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + + $contentHandler->expects($this->once()) + ->method('loadVersionInfo') + ->with( + $this->equalTo(42), + $this->equalTo(24) + )->will( + $this->throwException( + new NotFoundException( + 'Content', + [ + 'contentId' => 42, + 'versionNo' => 24, + ] + ) + ) + ); + + $contentServiceMock->loadVersionInfoById(42, 24); + } + + /** + * Test for the loadVersionInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById + */ + public function testLoadVersionInfoByIdThrowsUnauthorizedExceptionNonPublishedVersion() + { + $this->expectException(UnauthorizedException::class); + + $contentServiceMock = $this->getPartlyMockedContentService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + $domainMapperMock = $this->getContentDomainMapperMock(); + $versionInfoMock = $this->createMock(APIVersionInfo::class); + $permissionResolver = $this->getPermissionResolverMock(); + + $versionInfoMock->expects($this->any()) + ->method('isPublished') + ->willReturn(false); + + $contentHandler->expects($this->once()) + ->method('loadVersionInfo') + ->with( + $this->equalTo(42), + $this->equalTo(24) + )->will( + $this->returnValue(new SPIVersionInfo()) + ); + + $domainMapperMock->expects($this->once()) + ->method('buildVersionInfoDomainObject') + ->with(new SPIVersionInfo()) + ->will($this->returnValue($versionInfoMock)); + + $permissionResolver->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('versionread'), + $this->equalTo($versionInfoMock) + )->will($this->returnValue(false)); + + $contentServiceMock->loadVersionInfoById(42, 24); + } + + /** + * Test for the loadVersionInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById + */ + public function testLoadVersionInfoByIdPublishedVersion() + { + $contentServiceMock = $this->getPartlyMockedContentService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + $domainMapperMock = $this->getContentDomainMapperMock(); + $versionInfoMock = $this->createMock(APIVersionInfo::class); + $permissionResolver = $this->getPermissionResolverMock(); + + $versionInfoMock->expects($this->once()) + ->method('isPublished') + ->willReturn(true); + + $contentHandler->expects($this->once()) + ->method('loadVersionInfo') + ->with( + $this->equalTo(42), + $this->equalTo(24) + )->will( + $this->returnValue(new SPIVersionInfo()) + ); + + $domainMapperMock->expects($this->once()) + ->method('buildVersionInfoDomainObject') + ->with(new SPIVersionInfo()) + ->will($this->returnValue($versionInfoMock)); + + $permissionResolver->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('read'), + $this->equalTo($versionInfoMock) + )->will($this->returnValue(true)); + + $result = $contentServiceMock->loadVersionInfoById(42, 24); + + $this->assertEquals($versionInfoMock, $result); + } + + /** + * Test for the loadVersionInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfoById + */ + public function testLoadVersionInfoByIdNonPublishedVersion() + { + $contentServiceMock = $this->getPartlyMockedContentService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + $domainMapperMock = $this->getContentDomainMapperMock(); + $versionInfoMock = $this->createMock(APIVersionInfo::class); + $permissionResolver = $this->getPermissionResolverMock(); + + $versionInfoMock->expects($this->once()) + ->method('isPublished') + ->willReturn(false); + + $contentHandler->expects($this->once()) + ->method('loadVersionInfo') + ->with( + $this->equalTo(42), + $this->equalTo(24) + )->will( + $this->returnValue(new SPIVersionInfo()) + ); + + $domainMapperMock->expects($this->once()) + ->method('buildVersionInfoDomainObject') + ->with(new SPIVersionInfo()) + ->will($this->returnValue($versionInfoMock)); + + $permissionResolver->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('versionread'), + $this->equalTo($versionInfoMock) + )->will($this->returnValue(true)); + + $result = $contentServiceMock->loadVersionInfoById(42, 24); + + $this->assertEquals($versionInfoMock, $result); + } + + /** + * Test for the loadVersionInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadVersionInfo + * @depends Ibexa\Tests\Core\Repository\Service\Mock\ContentTest::testLoadVersionInfoById + * @depends Ibexa\Tests\Core\Repository\Service\Mock\ContentTest::testLoadVersionInfoByIdThrowsNotFoundException + * @depends Ibexa\Tests\Core\Repository\Service\Mock\ContentTest::testLoadVersionInfoByIdThrowsUnauthorizedExceptionNonPublishedVersion + * @depends Ibexa\Tests\Core\Repository\Service\Mock\ContentTest::testLoadVersionInfoByIdPublishedVersion + * @depends Ibexa\Tests\Core\Repository\Service\Mock\ContentTest::testLoadVersionInfoByIdNonPublishedVersion + */ + public function testLoadVersionInfo() + { + $expectedResult = $this->createMock(VersionInfo::class); + + $contentServiceMock = $this->getPartlyMockedContentService( + ['loadVersionInfoById'] + ); + $contentServiceMock->expects( + $this->once() + )->method( + 'loadVersionInfoById' + )->with( + $this->equalTo(42), + $this->equalTo(7) + )->will( + $this->returnValue($expectedResult) + ); + + $result = $contentServiceMock->loadVersionInfo( + new ContentInfo(['id' => 42]), + 7 + ); + + $this->assertEquals($expectedResult, $result); + } + + public function testLoadContent() + { + $contentService = $this->getPartlyMockedContentService(['internalLoadContentById']); + $content = $this->createMock(APIContent::class); + $versionInfo = $this->createMock(APIVersionInfo::class); + $permissionResolver = $this->getPermissionResolverMock(); + + $content + ->expects($this->once()) + ->method('getVersionInfo') + ->will($this->returnValue($versionInfo)); + $versionInfo + ->expects($this->once()) + ->method('isPublished') + ->willReturn(true); + $contentId = 123; + $contentService + ->expects($this->once()) + ->method('internalLoadContentById') + ->with($contentId) + ->will($this->returnValue($content)); + + $permissionResolver + ->expects($this->once()) + ->method('canUser') + ->with('content', 'read', $content) + ->will($this->returnValue(true)); + + $this->assertSame($content, $contentService->loadContent($contentId)); + } + + public function testLoadContentNonPublished() + { + $contentService = $this->getPartlyMockedContentService(['internalLoadContentById']); + $content = $this->createMock(APIContent::class); + $versionInfo = $this->createMock(APIVersionInfo::class); + $permissionResolver = $this->getPermissionResolverMock(); + + $content + ->expects($this->once()) + ->method('getVersionInfo') + ->will($this->returnValue($versionInfo)); + $contentId = 123; + $contentService + ->expects($this->once()) + ->method('internalLoadContentById') + ->with($contentId) + ->will($this->returnValue($content)); + + $permissionResolver + ->expects($this->exactly(2)) + ->method('canUser') + ->will( + $this->returnValueMap( + [ + ['content', 'read', $content, [], true], + ['content', 'versionread', $content, [], true], + ] + ) + ); + + $this->assertSame($content, $contentService->loadContent($contentId)); + } + + public function testLoadContentUnauthorized() + { + $this->expectException(UnauthorizedException::class); + + $permissionResolver = $this->getPermissionResolverMock(); + + $contentService = $this->getPartlyMockedContentService(['internalLoadContentById']); + $content = $this->createMock(APIContent::class); + $contentId = 123; + $contentService + ->expects($this->once()) + ->method('internalLoadContentById') + ->with($contentId) + ->will($this->returnValue($content)); + + $permissionResolver + ->expects($this->once()) + ->method('canUser') + ->with('content', 'read', $content) + ->will($this->returnValue(false)); + + $contentService->loadContent($contentId); + } + + public function testLoadContentNotPublishedStatusUnauthorized() + { + $this->expectException(UnauthorizedException::class); + + $permissionResolver = $this->getPermissionResolverMock(); + $contentService = $this->getPartlyMockedContentService(['internalLoadContentById']); + $content = $this->createMock(APIContent::class); + $versionInfo = $this + ->getMockBuilder(APIVersionInfo::class) + ->getMockForAbstractClass(); + $content + ->expects($this->once()) + ->method('getVersionInfo') + ->will($this->returnValue($versionInfo)); + $contentId = 123; + $contentService + ->expects($this->once()) + ->method('internalLoadContentById') + ->with($contentId) + ->will($this->returnValue($content)); + + $permissionResolver + ->expects($this->exactly(2)) + ->method('canUser') + ->will( + $this->returnValueMap( + [ + ['content', 'read', $content, [], true], + ['content', 'versionread', $content, [], false], + ] + ) + ); + + $contentService->loadContent($contentId); + } + + /** + * @dataProvider internalLoadContentProviderById + */ + public function testInternalLoadContentById(int $id, ?array $languages, ?int $versionNo, bool $useAlwaysAvailable): void + { + if (!empty($languages) && $useAlwaysAvailable) { + $spiContentInfo = new SPIContentInfo(['id' => $id, 'alwaysAvailable' => false]); + } else { + $spiContentInfo = new SPIContentInfo(['id' => $id]); + } + + $spiContent = new SPIContent([ + 'versionInfo' => new VersionInfo([ + 'contentInfo' => new ContentInfo([ + 'id' => 42, + 'contentTypeId' => 123, + ]), + ]), + ]); + + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + $contentHandler + ->expects($this->once()) + ->method('loadContentInfo') + ->with($id) + ->will($this->returnValue($spiContentInfo)); + + $contentHandler + ->expects($this->once()) + ->method('load') + ->with($id, $versionNo, $languages) + ->willReturn($spiContent); + + $contentService = $this->getPartlyMockedContentService(); + + $expectedContent = $this->mockBuildContentDomainObject($spiContent, $languages); + $actualContent = $contentService->internalLoadContentById($id, $languages, $versionNo, $useAlwaysAvailable); + + $this->assertSame($expectedContent, $actualContent); + } + + /** + * @dataProvider internalLoadContentProviderByRemoteId + */ + public function testInternalLoadContentByRemoteId(string $remoteId, ?array $languages, ?int $versionNo, bool $useAlwaysAvailable) + { + $realId = 123; + + $spiContentInfo = new SPIContentInfo([ + 'currentVersionNo' => $versionNo ?: 7, + 'id' => $realId, + ]); + + $spiContent = new SPIContent([ + 'versionInfo' => new VersionInfo([ + 'contentInfo' => new ContentInfo(['id' => 42, 'contentTypeId' => 123]), + ]), + ]); + + $contentService = $this->getPartlyMockedContentService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + $contentHandler + ->expects($this->once()) + ->method('loadContentInfoByRemoteId') + ->with($remoteId) + ->will($this->returnValue($spiContentInfo)); + + $contentHandler + ->expects($this->once()) + ->method('load') + ->with($realId, $versionNo, $languages) + ->willReturn($spiContent); + + $expectedContent = $this->mockBuildContentDomainObject($spiContent, $languages); + + $actualContent = $contentService->internalLoadContentByRemoteId( + $remoteId, + $languages, + $versionNo, + $useAlwaysAvailable + ); + + $this->assertSame($expectedContent, $actualContent); + } + + public function internalLoadContentProviderById(): array + { + return [ + [123, null, null, false], + [123, null, 456, false], + [456, null, 123, true], + [456, null, 2, false], + [456, ['eng-GB'], 2, true], + [456, ['eng-GB', 'fre-FR'], null, false], + [456, ['eng-GB', 'fre-FR', 'nor-NO'], 2, false], + ]; + } + + public function internalLoadContentProviderByRemoteId(): array + { + return [ + ['123', null, null, false], + ['someRemoteId', null, 456, false], + ['456', null, 123, false], + ['someRemoteId', null, 2, false], + ['someRemoteId', ['eng-GB'], 2, false], + ['456', ['eng-GB', 'fre-FR'], null, false], + ['someRemoteId', ['eng-GB', 'fre-FR', 'nor-NO'], 2, false], + ]; + } + + public function testInternalLoadContentByIdNotFound(): void + { + $this->expectException(NotFoundException::class); + + $id = 123; + $versionNo = 7; + $languages = null; + + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + $contentHandler + ->expects($this->once()) + ->method('loadContentInfo') + ->with($id) + ->willReturn(new SPIContent\ContentInfo(['id' => $id])); + + $contentHandler + ->expects($this->once()) + ->method('load') + ->with($id, $versionNo, $languages) + ->will( + $this->throwException( + $this->createMock(APINotFoundException::class) + ) + ); + + $contentService = $this->getPartlyMockedContentService(); + $contentService->internalLoadContentById($id, $languages, $versionNo); + } + + public function testInternalLoadContentByRemoteIdNotFound(): void + { + $this->expectException(NotFoundException::class); + + $remoteId = 'dca290623518d393126d3408b45af6ee'; + $id = 123; + $versionNo = 7; + $languages = null; + + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + $contentHandler + ->expects($this->once()) + ->method('loadContentInfoByRemoteId') + ->with($remoteId) + ->willReturn(new SPIContent\ContentInfo(['id' => $id])); + + $contentHandler + ->expects($this->once()) + ->method('load') + ->with($id, $versionNo, $languages) + ->willThrowException( + $this->createMock(APINotFoundException::class) + ); + + $contentService = $this->getPartlyMockedContentService(); + $contentService->internalLoadContentByRemoteId($remoteId, $languages, $versionNo); + } + + /** + * Test for the loadContentByContentInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByContentInfo + */ + public function testLoadContentByContentInfo() + { + $versionInfo = $this->createMock(APIVersionInfo::class); + $content = $this->createMock(APIContent::class); + $content->method('getVersionInfo') + ->will($this->returnValue($versionInfo)); + + $permissionResolver = $this->getPermissionResolverMock(); + $permissionResolver->expects($this->any()) + ->method('canUser') + ->will($this->returnValue(true)); + + $contentServiceMock = $this->getPartlyMockedContentService( + ['internalLoadContentById'] + ); + + $contentServiceMock + ->method( + 'internalLoadContentById' + )->with( + $this->equalTo(42), + $this->equalTo(['cro-HR']), + $this->equalTo(7), + $this->equalTo(false) + )->will( + $this->returnValue($content) + ); + + $result = $contentServiceMock->loadContentByContentInfo( + new ContentInfo(['id' => 42]), + ['cro-HR'], + 7 + ); + + $this->assertEquals($content, $result); + } + + /** + * Test for the loadContentByVersionInfo() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::loadContentByVersionInfo + */ + public function testLoadContentByVersionInfo() + { + $expectedResult = $this->createMock(Content::class); + + $contentServiceMock = $this->getPartlyMockedContentService( + ['loadContent'] + ); + $contentServiceMock->expects( + $this->once() + )->method( + 'loadContent' + )->with( + $this->equalTo(42), + $this->equalTo(['cro-HR']), + $this->equalTo(7), + $this->equalTo(false) + )->will( + $this->returnValue($expectedResult) + ); + + $result = $contentServiceMock->loadContentByVersionInfo( + new VersionInfo( + [ + 'contentInfo' => new ContentInfo(['id' => 42]), + 'versionNo' => 7, + ] + ), + ['cro-HR'] + ); + + $this->assertEquals($expectedResult, $result); + } + + /** + * Test for the deleteContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteContent + */ + public function testDeleteContentThrowsUnauthorizedException() + { + $this->expectException(UnauthorizedException::class); + + $permissionResolver = $this->getPermissionResolverMock(); + $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfoById']); + $contentInfo = $this->createMock(APIContentInfo::class); + + $contentInfo->expects($this->any()) + ->method('__get') + ->willReturnMap( + [ + ['id', 42], + ['currentVersionNo', 7], + ] + ); + + $persistenceHandlerMock = $this->getPersistenceMockHandler('Handler'); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + + $contentHandler + ->expects($this->once()) + ->method('loadVersionInfo') + ->with( + $this->equalTo(42), + $this->equalTo(7) + )->will( + $this->returnValue(new SPIVersionInfo()) + ); + + $contentService->expects($this->once()) + ->method('internalLoadContentInfoById') + ->with(42) + ->will($this->returnValue($contentInfo)); + + $permissionResolver->expects($this->once()) + ->method('canUser') + ->with('content', 'remove') + ->will($this->returnValue(false)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo */ + $contentService->deleteContent($contentInfo); + } + + /** + * Test for the deleteContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteContent + */ + public function testDeleteContent() + { + $repository = $this->getRepositoryMock(); + $permissionResolver = $this->getPermissionResolverMock(); + + $permissionResolver->expects($this->once()) + ->method('canUser') + ->with('content', 'remove') + ->will($this->returnValue(true)); + + $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfoById']); + /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandler */ + $urlAliasHandler = $this->getPersistenceMock()->urlAliasHandler(); + /** @var \PHPUnit\Framework\MockObject\MockObject $locationHandler */ + $locationHandler = $this->getPersistenceMock()->locationHandler(); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + + $contentInfo = $this->createMock(APIContentInfo::class); + + $contentService->expects($this->once()) + ->method('internalLoadContentInfoById') + ->with(42) + ->will($this->returnValue($contentInfo)); + + $contentInfo->expects($this->any()) + ->method('__get') + ->willReturnMap( + [ + ['id', 42], + ['currentVersionNo', 7], + ] + ); + + $persistenceHandlerMock = $this->getPersistenceMockHandler('Handler'); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + + $contentHandler + ->expects($this->once()) + ->method('loadVersionInfo') + ->with( + $this->equalTo(42), + $this->equalTo(7) + )->will( + $this->returnValue(new SPIVersionInfo()) + ); + + $repository->expects($this->once())->method('beginTransaction'); + + $spiLocations = [ + new SPILocation(['id' => 1]), + new SPILocation(['id' => 2]), + ]; + $locationHandler->expects($this->once()) + ->method('loadLocationsByContent') + ->with(42) + ->will($this->returnValue($spiLocations)); + + $contentHandler->expects($this->once()) + ->method('deleteContent') + ->with(42); + + foreach ($spiLocations as $index => $spiLocation) { + $urlAliasHandler->expects($this->at($index)) + ->method('locationDeleted') + ->with($spiLocation->id); + } + + $repository->expects($this->once())->method('commit'); + + /* @var \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo */ + $contentService->deleteContent($contentInfo); + } + + /** + * Test for the deleteContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteContent + */ + public function testDeleteContentWithRollback() + { + $this->expectException(\Exception::class); + + $repository = $this->getRepositoryMock(); + $permissionResolver = $this->getPermissionResolverMock(); + + $permissionResolver->expects($this->once()) + ->method('canUser') + ->with('content', 'remove') + ->will($this->returnValue(true)); + + $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfoById']); + /** @var \PHPUnit\Framework\MockObject\MockObject $locationHandler */ + $locationHandler = $this->getPersistenceMock()->locationHandler(); + + $contentInfo = $this->createMock(APIContentInfo::class); + + $contentService->expects($this->once()) + ->method('internalLoadContentInfoById') + ->with(42) + ->will($this->returnValue($contentInfo)); + + $contentInfo->expects($this->any()) + ->method('__get') + ->willReturnMap( + [ + ['id', 42], + ['currentVersionNo', 7], + ] + ); + + $persistenceHandlerMock = $this->getPersistenceMockHandler('Handler'); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + + $contentHandler + ->expects($this->once()) + ->method('loadVersionInfo') + ->with( + $this->equalTo(42), + $this->equalTo(7) + )->will( + $this->returnValue(new SPIVersionInfo()) + ); + + $repository->expects($this->once())->method('beginTransaction'); + + $locationHandler->expects($this->once()) + ->method('loadLocationsByContent') + ->with(42) + ->will($this->throwException(new \Exception())); + + $repository->expects($this->once())->method('rollback'); + + /* @var \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo */ + $contentService->deleteContent($contentInfo); + } + + /** + * Test for the deleteVersion() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::deleteVersion + */ + public function testDeleteVersionThrowsBadStateExceptionLastVersion() + { + $this->expectException(BadStateException::class); + + $repository = $this->getRepositoryMock(); + $permissionResolver = $this->getPermissionResolverMock(); + + $permissionResolver + ->expects($this->once()) + ->method('canUser') + ->with('content', 'versionremove') + ->will($this->returnValue(true)); + $repository + ->expects($this->never()) + ->method('beginTransaction'); + + $contentService = $this->getPartlyMockedContentService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandler */ + $contentHandler = $this->getPersistenceMock()->contentHandler(); + $contentInfo = $this->createMock(APIContentInfo::class); + $versionInfo = $this->createMock(APIVersionInfo::class); + + $contentInfo + ->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(42)); + + $versionInfo + ->expects($this->any()) + ->method('__get') + ->will( + $this->returnValueMap( + [ + ['versionNo', 123], + ['contentInfo', $contentInfo], + ] + ) + ); + $versionInfo + ->expects($this->once()) + ->method('isPublished') + ->willReturn(false); + + $contentHandler + ->expects($this->once()) + ->method('listVersions') + ->with(42) + ->will($this->returnValue(['version'])); + + /* @var \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfo */ + $contentService->deleteVersion($versionInfo); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + */ + public function testCreateContentThrowsInvalidArgumentExceptionMainLanguageCodeNotSet() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$contentCreateStruct\' is invalid: the \'mainLanguageCode\' property must be set'); + + $mockedService = $this->getPartlyMockedContentService(); + $mockedService->createContent(new ContentCreateStruct(), []); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + */ + public function testCreateContentThrowsInvalidArgumentExceptionContentTypeNotSet() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Argument \'$contentCreateStruct\' is invalid: the \'contentType\' property must be set'); + + $mockedService = $this->getPartlyMockedContentService(); + $mockedService->createContent( + new ContentCreateStruct(['mainLanguageCode' => 'eng-US']), + [] + ); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + */ + public function testCreateContentThrowsUnauthorizedException() + { + $this->expectException(UnauthorizedException::class); + + $repositoryMock = $this->getRepositoryMock(); + + $permissionResolver = $this->getPermissionResolverMock(); + $permissionResolver->expects($this->once()) + ->method('getCurrentUserReference') + ->will($this->returnValue(new UserReference(169))); + + $mockedService = $this->getPartlyMockedContentService(); + $contentTypeServiceMock = $this->getContentTypeServiceMock(); + $contentType = new ContentType( + [ + 'id' => 123, + 'fieldDefinitions' => [], + ] + ); + $contentCreateStruct = new ContentCreateStruct( + [ + 'ownerId' => 169, + 'alwaysAvailable' => false, + 'mainLanguageCode' => 'eng-US', + 'contentType' => $contentType, + ] + ); + + $contentTypeServiceMock->expects($this->once()) + ->method('loadContentType') + ->with($this->equalTo(123)) + ->will($this->returnValue($contentType)); + + $repositoryMock->expects($this->once()) + ->method('getContentTypeService') + ->will($this->returnValue($contentTypeServiceMock)); + + $permissionResolver->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('create'), + $this->isInstanceOf(get_class($contentCreateStruct)), + $this->equalTo([]) + )->will($this->returnValue(false)); + + $mockedService->createContent( + new ContentCreateStruct( + [ + 'mainLanguageCode' => 'eng-US', + 'contentType' => $contentType, + ] + ), + [] + ); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + * @exceptionMessage Argument '$contentCreateStruct' is invalid: Another content with remoteId 'faraday' exists + */ + public function testCreateContentThrowsInvalidArgumentExceptionDuplicateRemoteId() + { + $this->expectException(InvalidArgumentException::class); + + $repositoryMock = $this->getRepositoryMock(); + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock + ->expects($this->once()) + ->method('getCurrentUserReference') + ->willReturn($this->createMock(UserReference::class)); + + $mockedService = $this->getPartlyMockedContentService(['loadContentByRemoteId']); + $contentTypeServiceMock = $this->getContentTypeServiceMock(); + $contentType = new ContentType( + [ + 'id' => 123, + 'fieldDefinitions' => [], + ] + ); + $contentCreateStruct = new ContentCreateStruct( + [ + 'ownerId' => 169, + 'alwaysAvailable' => false, + 'remoteId' => 'faraday', + 'mainLanguageCode' => 'eng-US', + 'contentType' => $contentType, + ] + ); + + $contentTypeServiceMock->expects($this->once()) + ->method('loadContentType') + ->with($this->equalTo(123)) + ->will($this->returnValue($contentType)); + + $repositoryMock->expects($this->once()) + ->method('getContentTypeService') + ->will($this->returnValue($contentTypeServiceMock)); + + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('create'), + $this->isInstanceOf(get_class($contentCreateStruct)), + $this->equalTo([]) + )->will($this->returnValue(true)); + + $mockedService->expects($this->once()) + ->method('loadContentByRemoteId') + ->with($contentCreateStruct->remoteId) + ->will($this->returnValue($this->createMock(Content::class))); + + $mockedService->createContent( + new ContentCreateStruct( + [ + 'remoteId' => 'faraday', + 'mainLanguageCode' => 'eng-US', + 'contentType' => $contentType, + ] + ), + [] + ); + } + + /** + * @param string $mainLanguageCode + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $structFields + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions + * + * @return array + */ + protected function mapStructFieldsForCreate($mainLanguageCode, $structFields, $fieldDefinitions) + { + $mappedFieldDefinitions = []; + foreach ($fieldDefinitions as $fieldDefinition) { + $mappedFieldDefinitions[$fieldDefinition->identifier] = $fieldDefinition; + } + + $mappedStructFields = []; + foreach ($structFields as $structField) { + if ($structField->languageCode === null) { + $languageCode = $mainLanguageCode; + } else { + $languageCode = $structField->languageCode; + } + + $mappedStructFields[$structField->fieldDefIdentifier][$languageCode] = (string)$structField->value; + } + + return $mappedStructFields; + } + + /** + * Returns full, possibly redundant array of field values, indexed by field definition + * identifier and language code. + * + * @param string $mainLanguageCode + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $structFields + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions + * @param array $languageCodes + * + * @return array + * + * @throws \RuntimeException Method is intended to be used only with consistent fixtures + */ + protected function determineValuesForCreate( + $mainLanguageCode, + array $structFields, + array $fieldDefinitions, + array $languageCodes + ) { + $mappedStructFields = $this->mapStructFieldsForCreate( + $mainLanguageCode, + $structFields, + $fieldDefinitions + ); + + $values = []; + + foreach ($fieldDefinitions as $fieldDefinition) { + $identifier = $fieldDefinition->identifier; + foreach ($languageCodes as $languageCode) { + if (!$fieldDefinition->isTranslatable) { + if (isset($mappedStructFields[$identifier][$mainLanguageCode])) { + $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$mainLanguageCode]; + } else { + $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue; + } + continue; + } + + if (isset($mappedStructFields[$identifier][$languageCode])) { + $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$languageCode]; + continue; + } + + $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue; + } + } + + return $this->stubValues($values); + } + + /** + * @param string $mainLanguageCode + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $structFields + * + * @return string[] + */ + protected function determineLanguageCodesForCreate($mainLanguageCode, array $structFields) + { + $languageCodes = []; + + foreach ($structFields as $field) { + if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) { + continue; + } + + $languageCodes[$field->languageCode] = true; + } + + $languageCodes[$mainLanguageCode] = true; + + return array_keys($languageCodes); + } + + /** + * Asserts that calling createContent() with given API field set causes calling + * Handler::createContent() with given SPI field set. + * + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $structFields + * @param \Ibexa\Contracts\Core\Persistence\Content\Field[] $spiFields + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions + * @param \Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct[] $locationCreateStructs + */ + protected function assertForTestCreateContentNonRedundantFieldSet( + string $mainLanguageCode, + array $structFields, + array $spiFields, + array $fieldDefinitions, + array $locationCreateStructs = [], + bool $withObjectStates = false, + bool $execute = true + ): ContentCreateStruct { + $repositoryMock = $this->getRepositoryMock(); + $mockedService = $this->getPartlyMockedContentService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ + $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); + /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ + $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); + /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */ + $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler(); + $contentTypeServiceMock = $this->getContentTypeServiceMock(); + $domainMapperMock = $this->getContentDomainMapperMock(); + $relationProcessorMock = $this->getRelationProcessorMock(); + $nameSchemaServiceMock = $this->getNameSchemaServiceMock(); + $permissionResolverMock = $this->getPermissionResolverMock(); + $fieldTypeMock = $this->createMock(SPIFieldType::class); + $languageCodes = $this->determineLanguageCodesForCreate($mainLanguageCode, $structFields); + + list($contentType, $contentCreateStruct) = $this->provideCommonCreateContentObjects( + $fieldDefinitions, + $structFields, + $mainLanguageCode + ); + + $this->commonContentCreateMocks( + $languageHandlerMock, + $contentTypeServiceMock, + $repositoryMock, + $contentType + ); + + $repositoryMock->expects(self::once())->method('beginTransaction'); + + $that = $this; + $permissionResolverMock->expects(self::once()) + ->method('canUser') + ->with( + self::equalTo('content'), + self::equalTo('create'), + self::isInstanceOf(APIContentCreateStruct::class), + self::equalTo($locationCreateStructs) + )->will( + self::returnCallback( + static function () use ($that, $contentCreateStruct) { + $that->assertEquals($contentCreateStruct, func_get_arg(2)); + + return true; + } + ) + ); + + $this->getUniqueHashDomainMapperMock($domainMapperMock, $that, $contentCreateStruct); + $this->acceptFieldTypeValueMock($fieldTypeMock); + $this->toHashFieldTypeMock($fieldTypeMock); + + $fieldTypeMock->expects(self::any()) + ->method('toPersistenceValue') + ->will( + self::returnCallback( + static function (ValueStub $value) { + return (string)$value; + } + ) + ); + + $this->isEmptyValueFieldTypeMock($fieldTypeMock); + + $fieldTypeMock->expects(self::any()) + ->method('validate') + ->will(self::returnValue([])); + + $this->getFieldTypeRegistryMock()->expects(self::any()) + ->method('getFieldType') + ->will(self::returnValue($fieldTypeMock)); + + $relationProcessorMock + ->expects(self::exactly(count($fieldDefinitions) * count($languageCodes))) + ->method('appendFieldRelations') + ->with( + self::isType('array'), + self::isType('array'), + self::isInstanceOf(SPIFieldType::class), + self::isInstanceOf(Value::class), + self::anything() + ); + + $values = $this->determineValuesForCreate( + $mainLanguageCode, + $structFields, + $fieldDefinitions, + $languageCodes + ); + $nameSchemaServiceMock->expects(self::once()) + ->method('resolveNameSchema') + ->with( + self::equalTo($contentType->nameSchema), + self::equalTo($contentType), + self::equalTo($values), + self::equalTo($languageCodes) + )->will(self::returnValue([])); + + $relationProcessorMock->expects(self::any()) + ->method('processFieldRelations') + ->with( + self::isType('array'), + self::equalTo(42), + self::isType('int'), + self::equalTo($contentType), + self::equalTo([]) + ); + + if (!$withObjectStates) { + $objectStateHandlerMock->expects(self::once()) + ->method('loadAllGroups') + ->will(self::returnValue([])); + } + + if ($execute) { + $spiContentCreateStruct = new SPIContentCreateStruct( + [ + 'name' => [], + 'typeId' => 123, + 'sectionId' => 1, + 'ownerId' => 169, + 'remoteId' => 'hash', + 'fields' => $spiFields, + 'modified' => time(), + 'initialLanguageId' => 4242, + ] + ); + $spiContentCreateStruct2 = clone $spiContentCreateStruct; + ++$spiContentCreateStruct2->modified; + + $spiContent = new SPIContent( + [ + 'versionInfo' => new SPIContent\VersionInfo( + [ + 'contentInfo' => new SPIContent\ContentInfo(['id' => 42]), + 'versionNo' => 7, + ] + ), + ] + ); + + $contentHandlerMock->expects(self::once()) + ->method('create') + ->with(self::logicalOr($spiContentCreateStruct, $spiContentCreateStruct2)) + ->will(self::returnValue($spiContent)); + + $repositoryMock->expects(self::once())->method('commit'); + $domainMapperMock->expects(self::once()) + ->method('buildContentDomainObject') + ->with( + self::isInstanceOf(SPIContent::class), + self::equalTo($contentType) + ) + ->willReturn(self::createMock(APIContent::class)); + + $mockedService->createContent($contentCreateStruct, []); + } + + return $contentCreateStruct; + } + + public function providerForTestCreateContentNonRedundantFieldSet1() + { + $spiFields = [ + new SPIField( + [ + 'fieldDefinitionId' => 'fieldDefinitionId', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'eng-US', + ] + ), + ]; + + return [ + // 0. Without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'eng-US', + ] + ), + ], + $spiFields, + ], + // 1. Without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => null, + ] + ), + ], + $spiFields, + ], + ]; + } + + /** + * Test for the createContent() method. + * + * Testing the simplest use case. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::cloneField + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getDefaultObjectStates + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + * @dataProvider providerForTestCreateContentNonRedundantFieldSet1 + */ + public function testCreateContentNonRedundantFieldSet1($mainLanguageCode, $structFields, $spiFields) + { + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier', + 'isRequired' => false, + 'defaultValue' => new ValueStub('someValue'), + ] + ), + ]; + + $this->assertForTestCreateContentNonRedundantFieldSet( + $mainLanguageCode, + $structFields, + $spiFields, + $fieldDefinitions + ); + } + + public function providerForTestCreateContentNonRedundantFieldSet2() + { + $spiFields = [ + new SPIField( + [ + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + ] + ), + new SPIField( + [ + 'fieldDefinitionId' => 'fieldDefinitionId2', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'ger-DE', + ] + ), + ]; + + return [ + // 0. With language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'ger-DE', + ] + ), + ], + $spiFields, + ], + // 1. Without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => null, + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'ger-DE', + ] + ), + ], + $spiFields, + ], + ]; + } + + /** + * Test for the createContent() method. + * + * Testing multiple languages with multiple translatable fields with empty default value. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::cloneField + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getDefaultObjectStates + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + * @dataProvider providerForTestCreateContentNonRedundantFieldSet2 + */ + public function testCreateContentNonRedundantFieldSet2($mainLanguageCode, $structFields, $spiFields) + { + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId1', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => true, + 'identifier' => 'identifier1', + 'isRequired' => false, + 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), + ] + ), + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId2', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => true, + 'identifier' => 'identifier2', + 'isRequired' => false, + 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), + ] + ), + ]; + + $this->assertForTestCreateContentNonRedundantFieldSet( + $mainLanguageCode, + $structFields, + $spiFields, + $fieldDefinitions + ); + } + + public function providerForTestCreateContentNonRedundantFieldSetComplex() + { + $spiFields0 = [ + new SPIField( + [ + 'fieldDefinitionId' => 'fieldDefinitionId2', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('defaultValue2'), + 'languageCode' => 'eng-US', + ] + ), + new SPIField( + [ + 'fieldDefinitionId' => 'fieldDefinitionId4', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('defaultValue4'), + 'languageCode' => 'eng-US', + ] + ), + ]; + $spiFields1 = [ + new SPIField( + [ + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'ger-DE', + ] + ), + new SPIField( + [ + 'fieldDefinitionId' => 'fieldDefinitionId2', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('defaultValue2'), + 'languageCode' => 'ger-DE', + ] + ), + new SPIField( + [ + 'fieldDefinitionId' => 'fieldDefinitionId2', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-US', + ] + ), + new SPIField( + [ + 'fieldDefinitionId' => 'fieldDefinitionId4', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue4'), + 'languageCode' => 'eng-US', + ] + ), + ]; + + return [ + // 0. Creating by default values only + [ + 'eng-US', + [], + $spiFields0, + ], + // 1. Multiple languages with language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'ger-DE', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-US', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier4', + 'value' => new ValueStub('newValue4'), + 'languageCode' => 'eng-US', + ] + ), + ], + $spiFields1, + ], + // 2. Multiple languages without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'ger-DE', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub('newValue2'), + 'languageCode' => null, + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier4', + 'value' => new ValueStub('newValue4'), + 'languageCode' => null, + ] + ), + ], + $spiFields1, + ], + ]; + } + + protected function fixturesForTestCreateContentNonRedundantFieldSetComplex() + { + return [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId1', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => true, + 'identifier' => 'identifier1', + 'isRequired' => false, + 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), + ] + ), + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId2', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => true, + 'identifier' => 'identifier2', + 'isRequired' => false, + 'defaultValue' => new ValueStub('defaultValue2'), + ] + ), + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId3', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier3', + 'isRequired' => false, + 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), + ] + ), + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId4', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier4', + 'isRequired' => false, + 'defaultValue' => new ValueStub('defaultValue4'), + ] + ), + ]; + } + + /** + * Test for the createContent() method. + * + * Testing multiple languages with multiple translatable fields with empty default value. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::cloneField + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getDefaultObjectStates + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + * @dataProvider providerForTestCreateContentNonRedundantFieldSetComplex + */ + public function testCreateContentNonRedundantFieldSetComplex($mainLanguageCode, $structFields, $spiFields) + { + $fieldDefinitions = $this->fixturesForTestCreateContentNonRedundantFieldSetComplex(); + + $this->assertForTestCreateContentNonRedundantFieldSet( + $mainLanguageCode, + $structFields, + $spiFields, + $fieldDefinitions + ); + } + + public function providerForTestCreateContentWithInvalidLanguage() + { + return [ + [ + 'eng-GB', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'Klingon', + ] + ), + ], + ], + [ + 'Klingon', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'eng-GB', + ] + ), + ], + ], + ]; + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + * @dataProvider providerForTestCreateContentWithInvalidLanguage + */ + public function testCreateContentWithInvalidLanguage($mainLanguageCode, $structFields) + { + $this->expectException(APINotFoundException::class); + $this->expectExceptionMessage('Could not find \'Language\' with identifier \'Klingon\''); + + $repositoryMock = $this->getRepositoryMock(); + $mockedService = $this->getPartlyMockedContentService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ + $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); + $contentTypeServiceMock = $this->getContentTypeServiceMock(); + $domainMapperMock = $this->getContentDomainMapperMock(); + $permissionResolver = $this->getPermissionResolverMock(); + + $fieldTypeMock = $this->createMock(FieldType::class); + $this->acceptFieldTypeValueMock($fieldTypeMock); + $this->toHashFieldTypeMock($fieldTypeMock); + $this->getFieldTypeFieldTypeRegistryMock($fieldTypeMock); + + $contentType = new ContentType( + [ + 'id' => 123, + 'fieldDefinitions' => new FieldDefinitionCollection([ + new FieldDefinition([ + 'id' => 'fieldDefinitionId', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier', + 'isRequired' => false, + 'defaultValue' => new ValueStub('someValue'), + ]), + ]), + ] + ); + $contentCreateStruct = new ContentCreateStruct( + [ + 'fields' => $structFields, + 'mainLanguageCode' => $mainLanguageCode, + 'contentType' => $contentType, + 'alwaysAvailable' => false, + 'ownerId' => 169, + 'sectionId' => 1, + ] + ); + + $languageHandlerMock->expects($this->any()) + ->method('loadByLanguageCode') + ->with($this->isType('string')) + ->will( + $this->returnCallback( + static function ($languageCode) { + if ($languageCode === 'Klingon') { + throw new NotFoundException('Language', 'Klingon'); + } + + return new Language(['id' => 4242]); + } + ) + ); + + $contentTypeServiceMock->expects($this->once()) + ->method('loadContentType') + ->with($this->equalTo($contentType->id)) + ->will($this->returnValue($contentType)); + + $repositoryMock->expects($this->once()) + ->method('getContentTypeService') + ->will($this->returnValue($contentTypeServiceMock)); + + $that = $this; + $permissionResolver->expects($this->any()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('create'), + $this->isInstanceOf(APIContentCreateStruct::class), + $this->equalTo([]) + )->will( + $this->returnCallback( + static function () use ($that, $contentCreateStruct) { + $that->assertEquals($contentCreateStruct, func_get_arg(2)); + + return true; + } + ) + ); + + $domainMapperMock->expects($this->once()) + ->method('getUniqueHash') + ->with($this->isInstanceOf(APIContentCreateStruct::class)) + ->will( + $this->returnCallback( + static function ($object) use ($that, $contentCreateStruct) { + $that->assertEquals($contentCreateStruct, $object); + + return 'hash'; + } + ) + ); + + $mockedService->createContent($contentCreateStruct, []); + } + + protected function assertForCreateContentContentValidationException( + $mainLanguageCode, + $structFields, + $fieldDefinitions = [] + ) { + $repositoryMock = $this->getRepositoryMock(); + $mockedService = $this->getPartlyMockedContentService(['loadContentByRemoteId']); + $contentTypeServiceMock = $this->getContentTypeServiceMock(); + $permissionResolver = $this->getPermissionResolverMock(); + + $fieldTypeMock = $this->createMock(FieldType::class); + $fieldTypeMock->expects($this->any()) + ->method('acceptValue') + ->will( + $this->returnCallback( + static function ($valueString) { + return new ValueStub($valueString); + } + ) + ); + + $this->toHashFieldTypeMock($fieldTypeMock); + $this->getFieldTypeFieldTypeRegistryMock($fieldTypeMock); + + $contentType = new ContentType( + [ + 'id' => 123, + 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), + ] + ); + $contentCreateStruct = new ContentCreateStruct( + [ + 'ownerId' => 169, + 'alwaysAvailable' => false, + 'remoteId' => 'faraday', + 'mainLanguageCode' => $mainLanguageCode, + 'fields' => $structFields, + 'contentType' => $contentType, + ] + ); + + $contentTypeServiceMock->expects($this->once()) + ->method('loadContentType') + ->with($this->equalTo(123)) + ->will($this->returnValue($contentType)); + + $repositoryMock->expects($this->once()) + ->method('getContentTypeService') + ->will($this->returnValue($contentTypeServiceMock)); + + $permissionResolver->expects($this->any()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('create'), + $this->isInstanceOf(get_class($contentCreateStruct)), + $this->equalTo([]) + )->will($this->returnValue(true)); + + $mockedService->expects($this->any()) + ->method('loadContentByRemoteId') + ->with($contentCreateStruct->remoteId) + ->will( + $this->throwException(new NotFoundException('Content', 'faraday')) + ); + + $mockedService->createContent($contentCreateStruct, []); + } + + public function providerForTestCreateContentThrowsContentValidationExceptionFieldDefinition() + { + return [ + [ + 'eng-GB', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'eng-GB', + ] + ), + ], + ], + ]; + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionFieldDefinition + */ + public function testCreateContentThrowsContentValidationExceptionFieldDefinition($mainLanguageCode, $structFields) + { + $this->expectException(ContentValidationException::class); + $this->expectExceptionMessage('Field definition \'identifier\' does not exist in the given content type'); + + $this->assertForCreateContentContentValidationException( + $mainLanguageCode, + $structFields, + [] + ); + } + + public function providerForTestCreateContentThrowsContentValidationExceptionTranslation() + { + return [ + [ + 'eng-GB', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'eng-US', + ] + ), + ], + ], + ]; + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionTranslation + */ + public function testCreateContentThrowsContentValidationExceptionTranslation($mainLanguageCode, $structFields) + { + $this->expectException(ContentValidationException::class); + $this->expectExceptionMessage('You cannot set a value for the non-translatable Field definition \'identifier\' in language \'eng-US\''); + + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId1', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier', + 'isRequired' => false, + 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), + ] + ), + ]; + + $this->assertForCreateContentContentValidationException( + $mainLanguageCode, + $structFields, + $fieldDefinitions + ); + } + + private function provideCommonCreateContentObjects(array $fieldDefinitions, array $structFields, $mainLanguageCode): array + { + $contentType = new ContentType( + [ + 'id' => 123, + 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), + 'nameSchema' => '<nameSchema>', + ] + ); + $contentCreateStruct = new ContentCreateStruct( + [ + 'fields' => $structFields, + 'mainLanguageCode' => $mainLanguageCode, + 'contentType' => $contentType, + 'alwaysAvailable' => false, + 'ownerId' => 169, + 'sectionId' => 1, + ] + ); + + return [$contentType, $contentCreateStruct]; + } + + private function commonContentCreateMocks( + \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock, + \PHPUnit\Framework\MockObject\MockObject $contentTypeServiceMock, + \PHPUnit\Framework\MockObject\MockObject $repositoryMock, + ContentType $contentType + ): void { + $this->loadByLanguageCodeMock($languageHandlerMock); + + $contentTypeServiceMock->expects(self::once()) + ->method('loadContentType') + ->with(self::equalTo($contentType->id)) + ->will(self::returnValue($contentType)); + + $repositoryMock->expects(self::once()) + ->method('getContentTypeService') + ->will(self::returnValue($contentTypeServiceMock)); + } + + private function loadByLanguageCodeMock(\PHPUnit\Framework\MockObject\MockObject $languageHandlerMock): void + { + $languageHandlerMock->expects(self::any()) + ->method('loadByLanguageCode') + ->with(self::isType('string')) + ->will( + self::returnCallback( + static function () { + return new Language(['id' => 4242]); + } + ) + ); + } + + /** + * Asserts behaviour necessary for testing ContentFieldValidationException because of required + * field being empty. + * + * @param string $mainLanguageCode + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $structFields + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions + * + * @return mixed + */ + protected function assertForTestCreateContentRequiredField( + $mainLanguageCode, + array $structFields, + array $fieldDefinitions + ) { + $repositoryMock = $this->getRepositoryMock(); + /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ + $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); + $contentTypeServiceMock = $this->getContentTypeServiceMock(); + $domainMapperMock = $this->getContentDomainMapperMock(); + $fieldTypeMock = $this->createMock(SPIFieldType::class); + $permissionResolver = $this->getPermissionResolverMock(); + + list($contentType, $contentCreateStruct) = $this->provideCommonCreateContentObjects( + $fieldDefinitions, + $structFields, + $mainLanguageCode + ); + + $this->commonContentCreateMocks( + $languageHandlerMock, + $contentTypeServiceMock, + $repositoryMock, + $contentType + ); + + $that = $this; + $permissionResolver->expects(self::once()) + ->method('canUser') + ->with( + self::equalTo('content'), + self::equalTo('create'), + self::isInstanceOf(APIContentCreateStruct::class), + self::equalTo([]) + )->will( + self::returnCallback( + static function () use ($that, $contentCreateStruct) { + $that->assertEquals($contentCreateStruct, func_get_arg(2)); + + return true; + } + ) + ); + + $this->getUniqueHashDomainMapperMock($domainMapperMock, $that, $contentCreateStruct); + + $this->acceptFieldTypeValueMock($fieldTypeMock); + $this->toHashFieldTypeMock($fieldTypeMock); + + $this->isEmptyValueFieldTypeMock($fieldTypeMock); + + $fieldTypeMock->expects(self::any()) + ->method('validate') + ->will(self::returnValue([])); + + $this->getFieldTypeRegistryMock()->expects(self::any()) + ->method('getFieldType') + ->will(self::returnValue($fieldTypeMock)); + + return $contentCreateStruct; + } + + public function providerForTestCreateContentThrowsContentValidationExceptionRequiredField() + { + return [ + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => null, + ] + ), + ], + 'identifier', + 'eng-US', + ], + ]; + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionRequiredField + */ + public function testCreateContentRequiredField( + $mainLanguageCode, + $structFields, + $identifier, + $languageCode + ) { + $this->expectException(ContentFieldValidationException::class); + + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => true, + 'identifier' => 'identifier', + 'isRequired' => true, + 'defaultValue' => new ValueStub('defaultValue'), + ] + ), + ]; + $contentCreateStruct = $this->assertForTestCreateContentRequiredField( + $mainLanguageCode, + $structFields, + $fieldDefinitions + ); + + $mockedService = $this->getPartlyMockedContentService(); + + try { + $mockedService->createContent($contentCreateStruct, []); + } catch (ContentValidationException $e) { + $this->assertEquals( + "Value for required field definition '{$identifier}' with language '{$languageCode}' is empty", + $e->getMessage() + ); + + throw $e; + } + } + + /** + * Asserts behaviour necessary for testing ContentFieldValidationException because of + * field not being valid. + * + * @param string $mainLanguageCode + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $structFields + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions + */ + protected function assertForTestCreateContentThrowsContentFieldValidationException( + $mainLanguageCode, + array $structFields, + array $fieldDefinitions + ): array { + $repositoryMock = $this->getRepositoryMock(); + /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ + $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); + $contentTypeServiceMock = $this->getContentTypeServiceMock(); + $domainMapperMock = $this->getContentDomainMapperMock(); + $relationProcessorMock = $this->getRelationProcessorMock(); + $fieldTypeMock = $this->createMock(SPIFieldType::class); + $languageCodes = $this->determineLanguageCodesForCreate($mainLanguageCode, $structFields); + $permissionResolver = $this->getPermissionResolverMock(); + + $contentType = new ContentType( + [ + 'id' => 123, + 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), + 'nameSchema' => '<nameSchema>', + ] + ); + $contentCreateStruct = new ContentCreateStruct( + [ + 'fields' => $structFields, + 'mainLanguageCode' => $mainLanguageCode, + 'contentType' => $contentType, + 'alwaysAvailable' => false, + 'ownerId' => 169, + 'sectionId' => 1, + ] + ); + + $this->commonContentCreateMocks( + $languageHandlerMock, + $contentTypeServiceMock, + $repositoryMock, + $contentType + ); + + $that = $this; + $permissionResolver->expects(self::once()) + ->method('canUser') + ->with( + self::equalTo('content'), + self::equalTo('create'), + self::isInstanceOf(APIContentCreateStruct::class), + self::equalTo([]) + )->will( + self::returnCallback( + static function () use ($that, $contentCreateStruct) { + $that->assertEquals($contentCreateStruct, func_get_arg(2)); + + return true; + } + ) + ); + + $this->getUniqueHashDomainMapperMock($domainMapperMock, $that, $contentCreateStruct); + + $this->getFieldTypeRegistryMock()->expects(self::any()) + ->method('getFieldType') + ->will(self::returnValue($fieldTypeMock)); + + $relationProcessorMock + ->expects(self::any()) + ->method('appendFieldRelations') + ->with( + self::isType('array'), + self::isType('array'), + self::isInstanceOf(SPIFieldType::class), + self::isInstanceOf(Value::class), + self::anything() + ); + + $fieldValues = $this->determineValuesForCreate( + $mainLanguageCode, + $structFields, + $fieldDefinitions, + $languageCodes + ); + $allFieldErrors = []; + $emptyValue = new ValueStub(self::EMPTY_FIELD_VALUE); + + $fieldTypeMock + ->method('acceptValue') + ->will( + $this->returnCallback( + static function ($value) { + return $value instanceof SPIValue + ? $value + : new ValueStub($value); + } + ) + ); + + $fieldTypeMock + ->method('isEmptyValue') + ->will( + $this->returnCallback( + static function (ValueStub $value) use ($emptyValue) { + return (string)$emptyValue === (string)$value; + } + ) + ); + + $this->toHashFieldTypeMock($fieldTypeMock); + + $emptyValue = new ValueStub(self::EMPTY_FIELD_VALUE); + foreach ($contentType->getFieldDefinitions() as $fieldDefinition) { + foreach ($fieldValues[$fieldDefinition->identifier] as $languageCode => $value) { + if ((string)$emptyValue === (string)$value) { + continue; + } + + $fieldTypeMock + ->method('validate') + ->willReturn(new ValidationError(1)); + + $allFieldErrors[$fieldDefinition->id][$languageCode] = new ValidationError(1); + } + } + + return [$contentCreateStruct, $allFieldErrors]; + } + + public function providerForTestCreateContentThrowsContentFieldValidationException() + { + return $this->providerForTestCreateContentNonRedundantFieldSetComplex(); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + * @dataProvider providerForTestCreateContentThrowsContentFieldValidationException + */ + public function testCreateContentThrowsContentFieldValidationException($mainLanguageCode, $structFields): void + { + $this->expectException(ContentFieldValidationException::class); + $this->expectExceptionMessage('Content fields did not validate'); + + $fieldDefinitions = $this->fixturesForTestCreateContentNonRedundantFieldSetComplex(); + list($contentCreateStruct, $allFieldErrors) = + $this->assertForTestCreateContentThrowsContentFieldValidationException( + $mainLanguageCode, + $structFields, + $fieldDefinitions + ); + + $mockedService = $this->getPartlyMockedContentService(); + + try { + $mockedService->createContent($contentCreateStruct); + } catch (ContentFieldValidationException $e) { + $this->assertEquals($allFieldErrors, $e->getFieldErrors()); + throw $e; + } + } + + private function acceptFieldTypeValueMock(\PHPUnit\Framework\MockObject\MockObject $fieldTypeMock): void + { + $fieldTypeMock->expects(self::any()) + ->method('acceptValue') + ->will( + self::returnCallback( + static function ($valueString) { + return new ValueStub($valueString); + } + ) + ); + } + + private function toHashFieldTypeMock(\PHPUnit\Framework\MockObject\MockObject $fieldTypeMock): void + { + $fieldTypeMock + ->method('toHash') + ->willReturnCallback(static function (SPIValue $value) { + return ['value' => $value->value]; + }); + } + + private function getFieldTypeFieldTypeRegistryMock(\PHPUnit\Framework\MockObject\MockObject $fieldTypeMock): void + { + $this->getFieldTypeRegistryMock()->expects(self::any()) + ->method('getFieldType') + ->will(self::returnValue($fieldTypeMock)); + } + + private function isEmptyValueFieldTypeMock(\PHPUnit\Framework\MockObject\MockObject $fieldTypeMock): void + { + $emptyValue = new ValueStub(self::EMPTY_FIELD_VALUE); + $fieldTypeMock->expects(self::any()) + ->method('isEmptyValue') + ->will( + self::returnCallback( + static function (ValueStub $value) use ($emptyValue) { + return (string)$emptyValue === (string)$value; + } + ) + ); + } + + private function getUniqueHashDomainMapperMock( + \PHPUnit\Framework\MockObject\MockObject $domainMapperMock, + self $that, + ContentCreateStruct $contentCreateStruct + ): void { + $domainMapperMock->expects(self::once()) + ->method('getUniqueHash') + ->with(self::isInstanceOf(APIContentCreateStruct::class)) + ->will( + self::returnCallback( + static function ($object) use ($that, $contentCreateStruct) { + $that->assertEquals($contentCreateStruct, $object); + + return 'hash'; + } + ) + ); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::buildSPILocationCreateStructs + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + */ + public function testCreateContentWithLocations() + { + $spiFields = [ + new SPIField( + [ + 'fieldDefinitionId' => 'fieldDefinitionId', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('defaultValue'), + 'languageCode' => 'eng-US', + ] + ), + ]; + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier', + 'isRequired' => false, + 'defaultValue' => new ValueStub('defaultValue'), + ] + ), + ]; + + // Set up a simple case that will pass + $locationCreateStruct1 = new LocationCreateStruct(['parentLocationId' => 321]); + $locationCreateStruct2 = new LocationCreateStruct(['parentLocationId' => 654]); + $locationCreateStructs = [$locationCreateStruct1, $locationCreateStruct2]; + $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet( + 'eng-US', + [], + $spiFields, + $fieldDefinitions, + $locationCreateStructs, + false, + // Do not execute + false + ); + + $repositoryMock = $this->getRepositoryMock(); + $mockedService = $this->getPartlyMockedContentService(); + $locationServiceMock = $this->getLocationServiceMock(); + /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */ + $handlerMock = $this->getPersistenceMock()->contentHandler(); + $domainMapperMock = $this->getContentDomainMapperMock(); + $spiLocationCreateStruct = new SPILocation\CreateStruct(); + $parentLocation = new Location(['contentInfo' => new ContentInfo(['sectionId' => 1])]); + + $locationServiceMock->expects($this->at(0)) + ->method('loadLocation') + ->with($this->equalTo(321)) + ->will($this->returnValue($parentLocation)); + + $locationServiceMock->expects($this->at(1)) + ->method('loadLocation') + ->with($this->equalTo(654)) + ->will($this->returnValue($parentLocation)); + + $repositoryMock->expects($this->atLeastOnce()) + ->method('getLocationService') + ->will($this->returnValue($locationServiceMock)); + + $domainMapperMock->expects($this->at(1)) + ->method('buildSPILocationCreateStruct') + ->with( + $this->equalTo($locationCreateStruct1), + $this->equalTo($parentLocation), + $this->equalTo(true), + $this->equalTo(null), + $this->equalTo(null), + $this->equalTo(false) + )->will($this->returnValue($spiLocationCreateStruct)); + + $domainMapperMock->expects($this->at(2)) + ->method('buildSPILocationCreateStruct') + ->with( + $this->equalTo($locationCreateStruct2), + $this->equalTo($parentLocation), + $this->equalTo(false), + $this->equalTo(null), + $this->equalTo(null), + $this->equalTo(false) + )->will($this->returnValue($spiLocationCreateStruct)); + + $spiContentCreateStruct = new SPIContentCreateStruct( + [ + 'name' => [], + 'typeId' => 123, + 'sectionId' => 1, + 'ownerId' => 169, + 'remoteId' => 'hash', + 'fields' => $spiFields, + 'modified' => time(), + 'initialLanguageId' => 4242, + 'locations' => [$spiLocationCreateStruct, $spiLocationCreateStruct], + ] + ); + $spiContentCreateStruct2 = clone $spiContentCreateStruct; + ++$spiContentCreateStruct2->modified; + + $spiContent = new SPIContent( + [ + 'versionInfo' => new SPIContent\VersionInfo( + [ + 'contentInfo' => new SPIContent\ContentInfo(['id' => 42]), + 'versionNo' => 7, + ] + ), + ] + ); + + $handlerMock->expects($this->once()) + ->method('create') + ->with($this->logicalOr($spiContentCreateStruct, $spiContentCreateStruct2)) + ->will($this->returnValue($spiContent)); + + $domainMapperMock->expects($this->once()) + ->method('buildContentDomainObject') + ->with( + $this->isInstanceOf(SPIContent::class), + $this->isInstanceOf(APIContentType::class) + ) + ->willReturn($this->createMock(APIContent::class)); + + $repositoryMock->expects($this->once())->method('commit'); + + // Execute + $mockedService->createContent($contentCreateStruct, $locationCreateStructs); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::buildSPILocationCreateStructs + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + */ + public function testCreateContentWithLocationsDuplicateUnderParent() + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('You provided multiple LocationCreateStructs with the same parent Location \'321\''); + + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier', + 'isRequired' => false, + 'defaultValue' => new ValueStub('defaultValue'), + ] + ), + ]; + + $repositoryMock = $this->getRepositoryMock(); + $mockedService = $this->getPartlyMockedContentService(); + $locationServiceMock = $this->getLocationServiceMock(); + $contentTypeServiceMock = $this->getContentTypeServiceMock(); + $domainMapperMock = $this->getContentDomainMapperMock(); + $permissionResolver = $this->getPermissionResolverMock(); + /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ + $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); + $spiLocationCreateStruct = new SPILocation\CreateStruct(); + $parentLocation = new Location(['id' => 321]); + $locationCreateStruct = new LocationCreateStruct(['parentLocationId' => 321]); + $locationCreateStructs = [$locationCreateStruct, clone $locationCreateStruct]; + $contentType = new ContentType( + [ + 'id' => 123, + 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), + 'nameSchema' => '<nameSchema>', + ] + ); + $contentCreateStruct = new ContentCreateStruct( + [ + 'fields' => [ + new Field([ + 'fieldDefIdentifier' => 'identifier', + 'value' => 123, + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'languageCode' => 'eng-US', + ]), + ], + 'mainLanguageCode' => 'eng-US', + 'contentType' => $contentType, + 'alwaysAvailable' => false, + 'ownerId' => 169, + 'sectionId' => 1, + ] + ); + + $languageHandlerMock->expects($this->any()) + ->method('loadByLanguageCode') + ->with($this->isType('string')) + ->will( + $this->returnCallback( + static function () { + return new Language(['id' => 4242]); + } + ) + ); + + $fieldTypeMock = $this->createMock(FieldType::class); + $fieldTypeMock->expects($this->any()) + ->method('acceptValue') + ->will( + $this->returnCallback( + static function ($valueString) { + return new ValueStub($valueString); + } + ) + ); + + $this->toHashFieldTypeMock($fieldTypeMock); + $this->getFieldTypeFieldTypeRegistryMock($fieldTypeMock); + + $contentTypeServiceMock->expects($this->once()) + ->method('loadContentType') + ->with($this->equalTo($contentType->id)) + ->will($this->returnValue($contentType)); + + $repositoryMock->expects($this->once()) + ->method('getContentTypeService') + ->will($this->returnValue($contentTypeServiceMock)); + + $that = $this; + $permissionResolver->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('create'), + $this->isInstanceOf(APIContentCreateStruct::class), + $this->equalTo($locationCreateStructs) + )->will( + $this->returnCallback( + static function () use ($that, $contentCreateStruct) { + $that->assertEquals($contentCreateStruct, func_get_arg(2)); + + return true; + } + ) + ); + + $domainMapperMock->expects($this->once()) + ->method('getUniqueHash') + ->with($this->isInstanceOf(APIContentCreateStruct::class)) + ->will( + $this->returnCallback( + static function ($object) use ($that, $contentCreateStruct) { + $that->assertEquals($contentCreateStruct, $object); + + return 'hash'; + } + ) + ); + + $locationServiceMock->expects($this->once()) + ->method('loadLocation') + ->with($this->equalTo(321)) + ->will($this->returnValue($parentLocation)); + + $repositoryMock->expects($this->any()) + ->method('getLocationService') + ->will($this->returnValue($locationServiceMock)); + + $domainMapperMock->expects($this->any()) + ->method('buildSPILocationCreateStruct') + ->with( + $this->equalTo($locationCreateStruct), + $this->equalTo($parentLocation), + $this->equalTo(true), + $this->equalTo(null), + $this->equalTo(null), + $this->equalTo(false) + )->will($this->returnValue($spiLocationCreateStruct)); + + $fieldTypeMock = $this->createMock(SPIFieldType::class); + $fieldTypeMock + ->method('acceptValue') + ->will( + $this->returnCallback( + static function ($valueString) { + return new ValueStub($valueString); + } + ) + ); + + $this->getFieldTypeRegistryMock() + ->method('getFieldType') + ->will($this->returnValue($fieldTypeMock)); + + $mockedService->createContent( + $contentCreateStruct, + $locationCreateStructs + ); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getDefaultObjectStates + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + */ + public function testCreateContentObjectStates() + { + $spiFields = [ + new SPIField( + [ + 'fieldDefinitionId' => 'fieldDefinitionId', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('defaultValue'), + 'languageCode' => 'eng-US', + ] + ), + ]; + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier', + 'isRequired' => false, + 'defaultValue' => new ValueStub('defaultValue'), + ] + ), + ]; + $objectStateGroups = [ + new SPIObjectStateGroup(['id' => 10]), + new SPIObjectStateGroup(['id' => 20]), + ]; + + // Set up a simple case that will pass + $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet( + 'eng-US', + [], + $spiFields, + $fieldDefinitions, + [], + true, + // Do not execute + false + ); + $timestamp = time(); + $contentCreateStruct->modificationDate = new \DateTime("@{$timestamp}"); + + $repositoryMock = $this->getRepositoryMock(); + $mockedService = $this->getPartlyMockedContentService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */ + $handlerMock = $this->getPersistenceMock()->contentHandler(); + $domainMapperMock = $this->getContentDomainMapperMock(); + + $this->mockGetDefaultObjectStates(); + $this->mockSetDefaultObjectStates(); + + $spiContentCreateStruct = new SPIContentCreateStruct( + [ + 'name' => [], + 'typeId' => 123, + 'sectionId' => 1, + 'ownerId' => 169, + 'remoteId' => 'hash', + 'fields' => $spiFields, + 'modified' => $timestamp, + 'initialLanguageId' => 4242, + 'locations' => [], + ] + ); + $spiContentCreateStruct2 = clone $spiContentCreateStruct; + ++$spiContentCreateStruct2->modified; + + $spiContent = new SPIContent( + [ + 'versionInfo' => new SPIContent\VersionInfo( + [ + 'contentInfo' => new SPIContent\ContentInfo(['id' => 42]), + 'versionNo' => 7, + ] + ), + ] + ); + + $handlerMock->expects($this->once()) + ->method('create') + ->with($this->equalTo($spiContentCreateStruct)) + ->will($this->returnValue($spiContent)); + + $domainMapperMock->expects($this->once()) + ->method('buildContentDomainObject') + ->with( + $this->isInstanceOf(SPIContent::class), + $this->isInstanceOf(APIContentType::class) + ) + ->willReturn($this->createMock(APIContent::class)); + + $repositoryMock->expects($this->once())->method('commit'); + + // Execute + $mockedService->createContent($contentCreateStruct, []); + } + + /** + * Test for the createContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForCreate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getDefaultObjectStates + * @covers \Ibexa\Contracts\Core\Repository\ContentService::createContent + * @dataProvider providerForTestCreateContentThrowsContentValidationExceptionTranslation + */ + public function testCreateContentWithRollback() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Store failed'); + + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier', + 'isRequired' => false, + 'defaultValue' => new ValueStub('defaultValue'), + ] + ), + ]; + + // Setup a simple case that will pass + $contentCreateStruct = $this->assertForTestCreateContentNonRedundantFieldSet( + 'eng-US', + [], + [], + $fieldDefinitions, + [], + false, + // Do not execute test + false + ); + + $repositoryMock = $this->getRepositoryMock(); + $repositoryMock->expects($this->never())->method('commit'); + $repositoryMock->expects($this->once())->method('rollback'); + + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ + $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); + $contentHandlerMock->expects($this->once()) + ->method('create') + ->with($this->anything()) + ->will($this->throwException(new \Exception('Store failed'))); + + // Execute + $this->partlyMockedContentService->createContent($contentCreateStruct, []); + } + + public function providerForTestUpdateContentThrowsBadStateException() + { + return [ + [VersionInfo::STATUS_PUBLISHED], + [VersionInfo::STATUS_ARCHIVED], + ]; + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + * @dataProvider providerForTestUpdateContentThrowsBadStateException + */ + public function testUpdateContentThrowsBadStateException($status) + { + $this->expectException(BadStateException::class); + + $versionInfo = new VersionInfo( + [ + 'contentInfo' => new ContentInfo(['id' => 42]), + 'versionNo' => 7, + 'status' => $status, + ] + ); + $content = new Content( + [ + 'versionInfo' => $versionInfo, + 'internalFields' => [], + 'contentType' => new ContentType([]), + ] + ); + + $mockedService = $this->getPartlyMockedContentService(['loadContent', 'internalLoadContentById']); + $mockedService + ->method('loadContent') + ->with( + $this->equalTo(42), + $this->equalTo(null), + $this->equalTo(7) + )->will( + $this->returnValue($content) + ); + $mockedService + ->method('internalLoadContentById') + ->will( + $this->returnValue($content) + ); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock + ->method('canUser') + ->will($this->returnValue(true)); + + $contentUpdateStruct = new ContentUpdateStruct(); + + $mockedService->updateContent($versionInfo, $contentUpdateStruct); + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + */ + public function testUpdateContentThrowsUnauthorizedException() + { + $this->expectException(UnauthorizedException::class); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $mockedService = $this->getPartlyMockedContentService(['loadContent']); + $contentUpdateStruct = new ContentUpdateStruct(); + $versionInfo = new VersionInfo( + [ + 'contentInfo' => new ContentInfo(['id' => 42]), + 'versionNo' => 7, + 'status' => VersionInfo::STATUS_DRAFT, + ] + ); + $content = new Content( + [ + 'versionInfo' => $versionInfo, + 'internalFields' => [], + 'contentType' => new ContentType([]), + ] + ); + + $mockedService->expects($this->once()) + ->method('loadContent') + ->with( + $this->equalTo(42), + $this->equalTo(null), + $this->equalTo(7) + )->will( + $this->returnValue($content) + ); + + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('edit'), + $this->equalTo($content), + $this->isType('array') + )->will($this->returnValue(false)); + + $mockedService->updateContent($versionInfo, $contentUpdateStruct); + } + + /** + * @param string $initialLanguageCode + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $structFields + * @param string[] $existingLanguages + * + * @return string[] + */ + protected function determineLanguageCodesForUpdate($initialLanguageCode, array $structFields, $existingLanguages) + { + $languageCodes = array_fill_keys($existingLanguages, true); + if ($initialLanguageCode !== null) { + $languageCodes[$initialLanguageCode] = true; + } + + foreach ($structFields as $field) { + if ($field->languageCode === null || isset($languageCodes[$field->languageCode])) { + continue; + } + + $languageCodes[$field->languageCode] = true; + } + + return array_keys($languageCodes); + } + + /** + * @param string $initialLanguageCode + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $structFields + * @param string $mainLanguageCode + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions + * + * @return array + */ + protected function mapStructFieldsForUpdate($initialLanguageCode, $structFields, $mainLanguageCode, $fieldDefinitions) + { + $initialLanguageCode = $initialLanguageCode ?: $mainLanguageCode; + + $mappedFieldDefinitions = []; + foreach ($fieldDefinitions as $fieldDefinition) { + $mappedFieldDefinitions[$fieldDefinition->identifier] = $fieldDefinition; + } + + $mappedStructFields = []; + foreach ($structFields as $structField) { + $identifier = $structField->fieldDefIdentifier; + + if ($structField->languageCode !== null) { + $languageCode = $structField->languageCode; + } elseif ($mappedFieldDefinitions[$identifier]->isTranslatable) { + $languageCode = $initialLanguageCode; + } else { + $languageCode = $mainLanguageCode; + } + + $mappedStructFields[$identifier][$languageCode] = (string)$structField->value; + } + + return $mappedStructFields; + } + + /** + * Returns full, possibly redundant array of field values, indexed by field definition + * identifier and language code. + * + * @param string $initialLanguageCode + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $structFields + * @param \Ibexa\Core\Repository\Values\Content\Content $content + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions + * @param array $languageCodes + * + * @return array + */ + protected function determineValuesForUpdate( + $initialLanguageCode, + array $structFields, + Content $content, + array $fieldDefinitions, + array $languageCodes + ) { + $mainLanguageCode = $content->versionInfo->contentInfo->mainLanguageCode; + + $mappedStructFields = $this->mapStructFieldsForUpdate( + $initialLanguageCode, + $structFields, + $mainLanguageCode, + $fieldDefinitions + ); + + $values = []; + + foreach ($fieldDefinitions as $fieldDefinition) { + $identifier = $fieldDefinition->identifier; + foreach ($languageCodes as $languageCode) { + if (!$fieldDefinition->isTranslatable) { + if (isset($mappedStructFields[$identifier][$mainLanguageCode])) { + $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$mainLanguageCode]; + } else { + $values[$identifier][$languageCode] = (string)$content->fields[$identifier][$mainLanguageCode]; + } + continue; + } + + if (isset($mappedStructFields[$identifier][$languageCode])) { + $values[$identifier][$languageCode] = $mappedStructFields[$identifier][$languageCode]; + continue; + } + + if (isset($content->fields[$identifier][$languageCode])) { + $values[$identifier][$languageCode] = (string)$content->fields[$identifier][$languageCode]; + continue; + } + + $values[$identifier][$languageCode] = (string)$fieldDefinition->defaultValue; + } + } + + return $this->stubValues($values); + } + + protected function stubValues(array $fieldValues) + { + foreach ($fieldValues as &$languageValues) { + foreach ($languageValues as &$value) { + $value = new ValueStub($value); + } + } + + return $fieldValues; + } + + /** + * Asserts that calling updateContent() with given API field set causes calling + * Handler::updateContent() with given SPI field set. + * + * @param string $initialLanguageCode + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $structFields + * @param \Ibexa\Contracts\Core\Persistence\Content\Field[] $spiFields + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Field[] $existingFields + * @param \Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition[] $fieldDefinitions + * @param bool $execute + * + * @return mixed + */ + protected function assertForTestUpdateContentNonRedundantFieldSet( + $initialLanguageCode, + array $structFields, + array $spiFields, + array $existingFields, + array $fieldDefinitions, + $execute = true + ) { + $repositoryMock = $this->getRepositoryMock(); + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock + ->expects($this->once()) + ->method('getCurrentUserReference') + ->willReturn(new UserReference(169)); + $mockedService = $this->getPartlyMockedContentService(['internalLoadContentById'], $permissionResolverMock); + $permissionResolverMock = $this->getPermissionResolverMock(); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ + $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); + /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ + $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); + $contentTypeServiceMock = $this->getContentTypeServiceMock(); + $domainMapperMock = $this->getContentDomainMapperMock(); + $relationProcessorMock = $this->getRelationProcessorMock(); + $nameSchemaServiceMock = $this->getNameSchemaServiceMock(); + $fieldTypeMock = $this->createMock(SPIFieldType::class); + $existingLanguageCodes = array_map( + static function (Field $field) { + return $field->languageCode; + }, + $existingFields + ); + $languageCodes = $this->determineLanguageCodesForUpdate( + $initialLanguageCode, + $structFields, + $existingLanguageCodes + ); + $versionInfo = new VersionInfo( + [ + 'contentInfo' => new ContentInfo( + [ + 'id' => 42, + 'contentTypeId' => 24, + 'mainLanguageCode' => 'eng-GB', + ] + ), + 'versionNo' => 7, + 'languageCodes' => $existingLanguageCodes, + 'status' => VersionInfo::STATUS_DRAFT, + ] + ); + + $contentType = new ContentType([ + 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), + ]); + + $content = new Content( + [ + 'versionInfo' => $versionInfo, + 'internalFields' => $existingFields, + 'contentType' => $contentType, + ] + ); + + $languageHandlerMock->expects($this->any()) + ->method('loadByLanguageCode') + ->with($this->isType('string')) + ->will( + $this->returnCallback( + static function () { + return new Language(['id' => 4242]); + } + ) + ); + + $mockedService + ->method('internalLoadContentById') + ->with( + $this->equalTo(42), + $this->equalTo(null), + $this->equalTo(7) + )->will( + $this->returnValue($content) + ); + + $repositoryMock->expects($this->once())->method('beginTransaction'); + + $permissionResolverMock->expects($this->any()) + ->method('canUser') + ->will($this->returnValue(true)); + + $contentTypeServiceMock->expects($this->once()) + ->method('loadContentType') + ->with($this->equalTo(24)) + ->will($this->returnValue($contentType)); + + $repositoryMock->expects($this->once()) + ->method('getContentTypeService') + ->will($this->returnValue($contentTypeServiceMock)); + + $fieldTypeMock->expects($this->any()) + ->method('acceptValue') + ->will( + $this->returnCallback( + static function ($value) { + return $value instanceof SPIValue + ? $value + : new ValueStub($value); + } + ) + ); + + $this->toHashFieldTypeMock($fieldTypeMock); + + $emptyValue = new ValueStub(self::EMPTY_FIELD_VALUE); + $fieldTypeMock->expects($this->any()) + ->method('toPersistenceValue') + ->will( + $this->returnCallback( + static function (ValueStub $value) { + return (string)$value; + } + ) + ); + + $fieldTypeMock->expects($this->any()) + ->method('isEmptyValue') + ->will( + $this->returnCallback( + static function (SPIValue $value) use ($emptyValue) { + return (string)$emptyValue === (string)$value; + } + ) + ); + + $fieldTypeMock->expects($this->any()) + ->method('validate') + ->will($this->returnValue([])); + + $this->getFieldTypeFieldTypeRegistryMock($fieldTypeMock); + + $relationProcessorMock + ->expects($this->exactly(count($fieldDefinitions) * count($languageCodes))) + ->method('appendFieldRelations') + ->with( + $this->isType('array'), + $this->isType('array'), + $this->isInstanceOf(SPIFieldType::class), + $this->isInstanceOf(Value::class), + $this->anything() + ); + + $values = $this->determineValuesForUpdate( + $initialLanguageCode, + $structFields, + $content, + $fieldDefinitions, + $languageCodes + ); + $nameSchemaServiceMock->expects($this->once()) + ->method('resolveContentNameSchema') + ->with( + $this->equalTo($content), + $this->equalTo($values), + $this->equalTo($languageCodes) + )->will($this->returnValue([])); + + $existingRelations = ['RELATIONS!!!']; + $repositoryMock->method('sudo')->willReturn($existingRelations); + $relationProcessorMock->expects($this->any()) + ->method('processFieldRelations') + ->with( + $this->isType('array'), + $this->equalTo(42), + $this->isType('int'), + $this->equalTo($contentType), + $this->equalTo($existingRelations) + ); + + $contentUpdateStruct = new ContentUpdateStruct( + [ + 'fields' => $structFields, + 'initialLanguageCode' => $initialLanguageCode, + ] + ); + + if ($execute) { + $spiContentUpdateStruct = new SPIContentUpdateStruct( + [ + 'creatorId' => 169, + 'fields' => $spiFields, + 'modificationDate' => time(), + 'initialLanguageId' => 4242, + ] + ); + + // During code coverage runs, timestamp might differ 1-3 seconds + $spiContentUpdateStructTs1 = clone $spiContentUpdateStruct; + ++$spiContentUpdateStructTs1->modificationDate; + + $spiContentUpdateStructTs2 = clone $spiContentUpdateStructTs1; + ++$spiContentUpdateStructTs2->modificationDate; + + $spiContentUpdateStructTs3 = clone $spiContentUpdateStructTs2; + ++$spiContentUpdateStructTs3->modificationDate; + + $spiContent = new SPIContent( + [ + 'versionInfo' => new SPIContent\VersionInfo( + [ + 'contentInfo' => new SPIContent\ContentInfo(['id' => 42]), + 'versionNo' => 7, + ] + ), + ] + ); + + $contentHandlerMock->expects($this->once()) + ->method('updateContent') + ->with( + 42, + 7, + $this->logicalOr($spiContentUpdateStruct, $spiContentUpdateStructTs1, $spiContentUpdateStructTs2, $spiContentUpdateStructTs3) + ) + ->will($this->returnValue($spiContent)); + + $repositoryMock->expects($this->once())->method('commit'); + $domainMapperMock + ->method('buildContentDomainObject') + ->with( + $this->isInstanceOf(SPIContent::class), + $this->isInstanceOf(APIContentType::class) + ) + ->will($this->returnValue($content)); + + $mockedService->updateContent($content->versionInfo, $contentUpdateStruct); + } + + return [$content->versionInfo, $contentUpdateStruct]; + } + + public function providerForTestUpdateContentNonRedundantFieldSet1() + { + $spiFields = [ + new SPIField( + [ + 'id' => '100', + 'fieldDefinitionId' => 'fieldDefinitionId', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'eng-GB', + 'versionNo' => 7, + ] + ), + ]; + + return [ + // With languages set + [ + 'eng-GB', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields, + ], + // Without languages set + [ + null, + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => null, + ] + ), + ], + $spiFields, + ], + // Adding new language without fields + [ + 'eng-US', + [], + [], + ], + ]; + } + + /** + * Test for the updateContent() method. + * + * Testing the simplest use case. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + * @dataProvider providerForTestUpdateContentNonRedundantFieldSet1 + */ + public function testUpdateContentNonRedundantFieldSet1($initialLanguageCode, $structFields, $spiFields) + { + $existingFields = [ + new Field( + [ + 'id' => '100', + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('id100'), + 'languageCode' => 'eng-GB', + ] + ), + ]; + + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier', + 'isRequired' => false, + 'defaultValue' => new ValueStub('defaultValue'), + ] + ), + ]; + + $this->assertForTestUpdateContentNonRedundantFieldSet( + $initialLanguageCode, + $structFields, + $spiFields, + $existingFields, + $fieldDefinitions + ); + } + + public function providerForTestUpdateContentNonRedundantFieldSet2() + { + $spiFields0 = [ + new SPIField( + [ + 'id' => '100', + 'fieldDefinitionId' => 'fieldDefinitionId', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'eng-GB', + 'versionNo' => 7, + ] + ), + ]; + $spiFields1 = [ + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + ]; + $spiFields2 = [ + new SPIField( + [ + 'id' => 100, + 'fieldDefinitionId' => 'fieldDefinitionId', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-GB', + 'versionNo' => 7, + ] + ), + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + ]; + + return [ + // 0. With languages set + [ + 'eng-GB', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields0, + ], + // 1. Without languages set + [ + null, + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => null, + ] + ), + ], + $spiFields0, + ], + // 2. New language with language set + [ + 'eng-GB', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'eng-US', + ] + ), + ], + $spiFields1, + ], + // 3. New language without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => null, + ] + ), + ], + $spiFields1, + ], + // 4. New language and existing language with language set + [ + 'eng-GB', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields2, + ], + // 5. New language and existing language without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue1'), + 'languageCode' => null, + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields2, + ], + // 6. Adding new language without fields + [ + 'eng-US', + [], + [ + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('defaultValue'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + ], + ], + ]; + } + + /** + * Test for the updateContent() method. + * + * Testing with translatable field. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + * @dataProvider providerForTestUpdateContentNonRedundantFieldSet2 + */ + public function testUpdateContentNonRedundantFieldSet2($initialLanguageCode, $structFields, $spiFields) + { + $existingFields = [ + new Field( + [ + 'id' => '100', + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('id100'), + 'languageCode' => 'eng-GB', + ] + ), + ]; + + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => true, + 'identifier' => 'identifier', + 'isRequired' => false, + 'defaultValue' => new ValueStub('defaultValue'), + ] + ), + ]; + + $this->assertForTestUpdateContentNonRedundantFieldSet( + $initialLanguageCode, + $structFields, + $spiFields, + $existingFields, + $fieldDefinitions + ); + } + + public function providerForTestUpdateContentNonRedundantFieldSet3() + { + $spiFields0 = [ + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + ]; + $spiFields1 = [ + new SPIField( + [ + 'id' => 100, + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-GB', + 'versionNo' => 7, + ] + ), + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + ]; + $spiFields2 = [ + new SPIField( + [ + 'id' => 100, + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-GB', + 'versionNo' => 7, + ] + ), + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + new SPIField( + [ + 'id' => 101, + 'fieldDefinitionId' => 'fieldDefinitionId2', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue3'), + 'languageCode' => 'eng-GB', + 'versionNo' => 7, + ] + ), + ]; + $spiFields3 = [ + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('defaultValue1'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + ]; + + return [ + // 0. ew language with language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + ] + ), + ], + $spiFields0, + ], + // 1. New language without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => null, + ] + ), + ], + $spiFields0, + ], + // 2. New language and existing language with language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields1, + ], + // 3. New language and existing language without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => null, + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields1, + ], + // 4. New language and existing language with untranslatable field, with language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-GB', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub('newValue3'), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields2, + ], + // 5. New language and existing language with untranslatable field, without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => null, + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-GB', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub('newValue3'), + 'languageCode' => null, + ] + ), + ], + $spiFields2, + ], + // 6. Adding new language without fields + [ + 'eng-US', + [], + $spiFields3, + ], + ]; + } + + /** + * Test for the updateContent() method. + * + * Testing with new language and untranslatable field. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + * @dataProvider providerForTestUpdateContentNonRedundantFieldSet3 + */ + public function testUpdateContentNonRedundantFieldSet3($initialLanguageCode, $structFields, $spiFields) + { + $existingFields = [ + new Field( + [ + 'id' => '100', + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('id100'), + 'languageCode' => 'eng-GB', + ] + ), + new Field( + [ + 'id' => '101', + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub('id101'), + 'languageCode' => 'eng-GB', + ] + ), + ]; + + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId1', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => true, + 'identifier' => 'identifier1', + 'isRequired' => false, + 'defaultValue' => new ValueStub('defaultValue1'), + ] + ), + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId2', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier2', + 'isRequired' => false, + 'defaultValue' => new ValueStub('defaultValue2'), + ] + ), + ]; + + $this->assertForTestUpdateContentNonRedundantFieldSet( + $initialLanguageCode, + $structFields, + $spiFields, + $existingFields, + $fieldDefinitions + ); + } + + public function providerForTestUpdateContentNonRedundantFieldSet4() + { + $spiFields0 = [ + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + ]; + $spiFields1 = [ + new SPIField( + [ + 'id' => 100, + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => 'eng-GB', + 'versionNo' => 7, + ] + ), + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + ]; + $spiFields2 = [ + new SPIField( + [ + 'id' => 100, + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => 'eng-GB', + 'versionNo' => 7, + ] + ), + ]; + + return [ + // 0. New translation with empty field by default + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + ] + ), + ], + $spiFields0, + ], + // 1. New translation with empty field by default, without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => null, + ] + ), + ], + $spiFields0, + ], + // 2. New translation with empty field given + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => 'eng-US', + ] + ), + ], + $spiFields0, + ], + // 3. New translation with empty field given, without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => null, + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => null, + ] + ), + ], + $spiFields0, + ], + // 4. Updating existing language with empty value + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => 'eng-US', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields1, + ], + // 5. Updating existing language with empty value, without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1'), + 'languageCode' => null, + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields1, + ], + // 6. Updating existing language with empty value and adding new language with empty value + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => 'eng-US', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields2, + ], + // 7. Updating existing language with empty value and adding new language with empty value, + // without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => null, + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields2, + ], + // 8. Adding new language with no fields given + [ + 'eng-US', + [], + [], + ], + // 9. Adding new language with fields + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => 'eng-US', + ] + ), + ], + [], + ], + // 10. Adding new language with fields, without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => null, + ] + ), + ], + [], + ], + ]; + } + + /** + * Test for the updateContent() method. + * + * Testing with empty values. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + * @dataProvider providerForTestUpdateContentNonRedundantFieldSet4 + */ + public function testUpdateContentNonRedundantFieldSet4($initialLanguageCode, $structFields, $spiFields) + { + $existingFields = [ + new Field( + [ + 'id' => '100', + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('id100'), + 'languageCode' => 'eng-GB', + ] + ), + new Field( + [ + 'id' => '101', + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub('id101'), + 'languageCode' => 'eng-GB', + ] + ), + ]; + + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId1', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => true, + 'identifier' => 'identifier1', + 'isRequired' => false, + 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), + ] + ), + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId2', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => true, + 'identifier' => 'identifier2', + 'isRequired' => false, + 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), + ] + ), + ]; + + $this->assertForTestUpdateContentNonRedundantFieldSet( + $initialLanguageCode, + $structFields, + $spiFields, + $existingFields, + $fieldDefinitions + ); + } + + /** + * @return array + * + * @todo add first field empty + */ + public function providerForTestUpdateContentNonRedundantFieldSetComplex() + { + $spiFields0 = [ + new SPIField( + [ + 'id' => 100, + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue1-eng-GB'), + 'languageCode' => 'eng-GB', + 'versionNo' => 7, + ] + ), + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId4', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue4'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + ]; + $spiFields1 = [ + new SPIField( + [ + 'id' => 100, + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue1-eng-GB'), + 'languageCode' => 'eng-GB', + 'versionNo' => 7, + ] + ), + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId2', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId4', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('defaultValue4'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + ]; + $spiFields2 = [ + new SPIField( + [ + 'id' => 100, + 'fieldDefinitionId' => 'fieldDefinitionId1', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue1-eng-GB'), + 'languageCode' => 'eng-GB', + 'versionNo' => 7, + ] + ), + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId2', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId4', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('defaultValue4'), + 'languageCode' => 'ger-DE', + 'versionNo' => 7, + ] + ), + new SPIField( + [ + 'id' => null, + 'fieldDefinitionId' => 'fieldDefinitionId4', + 'type' => 'fieldTypeIdentifier', + 'value' => new ValueStub('defaultValue4'), + 'languageCode' => 'eng-US', + 'versionNo' => 7, + ] + ), + ]; + + return [ + // 0. Add new language and update existing + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier4', + 'value' => new ValueStub('newValue4'), + 'languageCode' => 'eng-US', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1-eng-GB'), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields0, + ], + // 1. Add new language and update existing, without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier4', + 'value' => new ValueStub('newValue4'), + 'languageCode' => null, + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1-eng-GB'), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields0, + ], + // 2. Add new language and update existing variant + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-US', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1-eng-GB'), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields1, + ], + // 3. Add new language and update existing variant, without language set + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub('newValue2'), + 'languageCode' => null, + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1-eng-GB'), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields1, + ], + // 4. Update with multiple languages + [ + 'ger-DE', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-US', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1-eng-GB'), + 'languageCode' => 'eng-GB', + ] + ), + ], + $spiFields2, + ], + // 5. Update with multiple languages without language set + [ + 'ger-DE', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub('newValue2'), + 'languageCode' => 'eng-US', + ] + ), + new Field( + [ + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('newValue1-eng-GB'), + 'languageCode' => null, + ] + ), + ], + $spiFields2, + ], + ]; + } + + protected function fixturesForTestUpdateContentNonRedundantFieldSetComplex() + { + $existingFields = [ + new Field( + [ + 'id' => '100', + 'fieldDefIdentifier' => 'identifier1', + 'value' => new ValueStub('initialValue1'), + 'languageCode' => 'eng-GB', + ] + ), + new Field( + [ + 'id' => '101', + 'fieldDefIdentifier' => 'identifier2', + 'value' => new ValueStub('initialValue2'), + 'languageCode' => 'eng-GB', + ] + ), + new Field( + [ + 'id' => '102', + 'fieldDefIdentifier' => 'identifier3', + 'value' => new ValueStub('initialValue3'), + 'languageCode' => 'eng-GB', + ] + ), + new Field( + [ + 'id' => '103', + 'fieldDefIdentifier' => 'identifier4', + 'value' => new ValueStub('initialValue4'), + 'languageCode' => 'eng-GB', + ] + ), + ]; + + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId1', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier1', + 'isRequired' => false, + 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), + ] + ), + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId2', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => true, + 'identifier' => 'identifier2', + 'isRequired' => false, + 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), + ] + ), + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId3', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier3', + 'isRequired' => false, + 'defaultValue' => new ValueStub('defaultValue3'), + ] + ), + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId4', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => true, + 'identifier' => 'identifier4', + 'isRequired' => false, + 'defaultValue' => new ValueStub('defaultValue4'), + ] + ), + ]; + + return [$existingFields, $fieldDefinitions]; + } + + /** + * Test for the updateContent() method. + * + * Testing more complex cases. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + * @dataProvider providerForTestUpdateContentNonRedundantFieldSetComplex + */ + public function testUpdateContentNonRedundantFieldSetComplex($initialLanguageCode, $structFields, $spiFields) + { + list($existingFields, $fieldDefinitions) = $this->fixturesForTestUpdateContentNonRedundantFieldSetComplex(); + + $this->assertForTestUpdateContentNonRedundantFieldSet( + $initialLanguageCode, + $structFields, + $spiFields, + $existingFields, + $fieldDefinitions + ); + } + + public function providerForTestUpdateContentWithInvalidLanguage() + { + return [ + [ + 'eng-GB', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => $this->createMock(Value::class), + 'languageCode' => 'Klingon', + ] + ), + ], + ], + [ + 'Klingon', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => $this->createMock(Value::class), + 'languageCode' => 'eng-GB', + ] + ), + ], + ], + ]; + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + * @dataProvider providerForTestUpdateContentWithInvalidLanguage + */ + public function testUpdateContentWithInvalidLanguage($initialLanguageCode, $structFields) + { + $this->expectException(APINotFoundException::class); + $this->expectExceptionMessage('Could not find \'Language\' with identifier \'Klingon\''); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $mockedService = $this->getPartlyMockedContentService(['loadContent', 'internalLoadContentById']); + $fieldTypeMock = $this->createMock(SPIFieldType::class); + /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ + $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); + $versionInfo = new VersionInfo( + [ + 'contentInfo' => new ContentInfo( + [ + 'id' => 42, + 'contentTypeId' => 24, + 'mainLanguageCode' => 'eng-GB', + ] + ), + 'versionNo' => 7, + 'languageCodes' => ['eng-GB'], + 'status' => VersionInfo::STATUS_DRAFT, + ] + ); + + $fieldValueMock = $this->createMock(Value::class); + + $content = new Content( + [ + 'versionInfo' => $versionInfo, + 'internalFields' => [ + new Field([ + 'fieldDefIdentifier' => 'identifier', + 'value' => $fieldValueMock, + 'languageCode' => 'eng-GB', + ]), + ], + 'contentType' => new ContentType([ + 'fieldDefinitions' => new FieldDefinitionCollection([ + new FieldDefinition([ + 'identifier' => 'identifier', + 'defaultValue' => $fieldValueMock, + ]), + ]), + ]), + ] + ); + + $fieldTypeMock->expects($this->any()) + ->method('acceptValue')->will($this->returnValue($fieldValueMock)); + + $this->getFieldTypeRegistryMock()->expects($this->any()) + ->method('getFieldType')->will($this->returnValue($fieldTypeMock)); + + $languageHandlerMock->expects($this->any()) + ->method('loadByLanguageCode') + ->with($this->isType('string')) + ->will( + $this->returnCallback( + static function ($languageCode) { + if ($languageCode === 'Klingon') { + throw new NotFoundException('Language', 'Klingon'); + } + + return new Language(['id' => 4242]); + } + ) + ); + + $mockedService + ->method('loadContent') + ->with( + $this->equalTo(42), + $this->equalTo(null), + $this->equalTo(7) + )->will( + $this->returnValue($content) + ); + + $mockedService + ->method('internalLoadContentById') + ->will( + $this->returnValue($content) + ); + + $permissionResolverMock + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('edit'), + $this->equalTo($content), + $this->isType('array') + )->will($this->returnValue(true)); + + $contentUpdateStruct = new ContentUpdateStruct( + [ + 'fields' => $structFields, + 'initialLanguageCode' => $initialLanguageCode, + ] + ); + + $mockedService->updateContent($content->versionInfo, $contentUpdateStruct); + } + + protected function assertForUpdateContentContentValidationException( + $initialLanguageCode, + $structFields, + $fieldDefinitions = [] + ) { + $permissionResolverMock = $this->getPermissionResolverMock(); + $mockedService = $this->getPartlyMockedContentService(['internalLoadContentById', 'loadContent']); + /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ + $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); + $versionInfo = new VersionInfo( + [ + 'contentInfo' => new ContentInfo( + [ + 'id' => 42, + 'contentTypeId' => 24, + 'mainLanguageCode' => 'eng-GB', + ] + ), + 'versionNo' => 7, + 'languageCodes' => ['eng-GB'], + 'status' => VersionInfo::STATUS_DRAFT, + ] + ); + $contentType = new ContentType([ + 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), + ]); + $content = new Content( + [ + 'versionInfo' => $versionInfo, + 'internalFields' => [], + 'contentType' => $contentType, + ] + ); + + $fieldTypeMock = $this->createMock(FieldType::class); + + $fieldTypeMock + ->method('acceptValue') + ->will( + $this->returnCallback( + static function ($value) { + return $value instanceof SPIValue + ? $value + : new ValueStub($value); + } + ) + ); + + $this->toHashFieldTypeMock($fieldTypeMock); + + $this->getFieldTypeRegistryMock()->expects($this->any()) + ->method('getFieldType')->will($this->returnValue($fieldTypeMock)); + + $languageHandlerMock->expects($this->any()) + ->method('loadByLanguageCode') + ->with($this->isType('string')) + ->will( + $this->returnCallback( + static function ($languageCode) { + if ($languageCode === 'Klingon') { + throw new NotFoundException('Language', 'Klingon'); + } + + return new Language(['id' => 4242]); + } + ) + ); + + $mockedService + ->method('loadContent') + ->with( + $this->equalTo(42), + $this->equalTo(null), + $this->equalTo(7) + )->will( + $this->returnValue($content) + ); + + $mockedService + ->method('internalLoadContentById') + ->will( + $this->returnValue($content) + ); + + $permissionResolverMock->expects($this->any()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('edit'), + $this->equalTo($content), + $this->isType('array') + )->will($this->returnValue(true)); + + /* + $contentTypeServiceMock->expects($this->once()) + ->method('loadContentType') + ->with($this->equalTo($contentType->id)) + ->will($this->returnValue($contentType)); + */ + + $contentUpdateStruct = new ContentUpdateStruct( + [ + 'fields' => $structFields, + 'initialLanguageCode' => $initialLanguageCode, + ] + ); + + $mockedService->updateContent($content->versionInfo, $contentUpdateStruct); + } + + private function prepareContentForTestCreateAndUpdateContent( + array $existingLanguageCodes, + array $fieldDefinitions, + array $existingFields + ): Content { + $versionInfo = new VersionInfo( + [ + 'contentInfo' => new ContentInfo( + [ + 'id' => 42, + 'contentTypeId' => 24, + 'mainLanguageCode' => 'eng-GB', + ] + ), + 'versionNo' => 7, + 'languageCodes' => $existingLanguageCodes, + 'status' => VersionInfo::STATUS_DRAFT, + 'names' => [ + 'eng-GB' => 'Test', + ], + 'initialLanguageCode' => 'eng-GB', + ] + ); + $contentType = new ContentType([ + 'fieldDefinitions' => new FieldDefinitionCollection($fieldDefinitions), + ]); + + return new Content( + [ + 'versionInfo' => $versionInfo, + 'internalFields' => $existingFields, + 'contentType' => $contentType, + ] + ); + } + + public function providerForTestUpdateContentThrowsContentValidationExceptionFieldDefinition() + { + return [ + [ + 'eng-GB', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'eng-GB', + ] + ), + ], + ], + ]; + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + * @dataProvider providerForTestUpdateContentThrowsContentValidationExceptionFieldDefinition + */ + public function testUpdateContentThrowsContentValidationExceptionFieldDefinition($initialLanguageCode, $structFields) + { + $this->expectException(ContentValidationException::class); + $this->expectExceptionMessage('Field definition \'identifier\' does not exist in given content type'); + + $this->assertForUpdateContentContentValidationException( + $initialLanguageCode, + $structFields, + [] + ); + } + + public function providerForTestUpdateContentThrowsContentValidationExceptionTranslation() + { + return [ + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('newValue'), + 'languageCode' => 'eng-US', + ] + ), + ], + ], + ]; + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + * @dataProvider providerForTestUpdateContentThrowsContentValidationExceptionTranslation + */ + public function testUpdateContentThrowsContentValidationExceptionTranslation($initialLanguageCode, $structFields) + { + $this->expectException(ContentValidationException::class); + $this->expectExceptionMessage('You cannot set a value for the non-translatable Field definition \'identifier\' in language \'eng-US\''); + + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId1', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier', + 'isRequired' => false, + 'defaultValue' => new ValueStub(self::EMPTY_FIELD_VALUE), + ] + ), + ]; + + $this->assertForUpdateContentContentValidationException( + $initialLanguageCode, + $structFields, + $fieldDefinitions + ); + } + + public function assertForTestUpdateContentRequiredField( + $initialLanguageCode, + $structFields, + $existingFields, + $fieldDefinitions + ) { + $permissionResolver = $this->getPermissionResolverMock(); + $mockedService = $this->getPartlyMockedContentService(['internalLoadContentById', 'loadContent']); + /** @var \PHPUnit\Framework\MockObject\MockObject $languageHandlerMock */ + $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); + $fieldTypeMock = $this->createMock(SPIFieldType::class); + $existingLanguageCodes = array_map( + static function (Field $field) { + return $field->languageCode; + }, + $existingFields + ); + + $content = $this->prepareContentForTestCreateAndUpdateContent( + $existingLanguageCodes, + $fieldDefinitions, + $existingFields + ); + + $this->loadByLanguageCodeMock($languageHandlerMock); + + $mockedService + ->method('loadContent') + ->with( + self::equalTo(42), + self::equalTo(null), + self::equalTo(7) + )->will( + self::returnValue($content) + ); + + $mockedService + ->method('internalLoadContentById') + ->will( + self::returnValue($content) + ); + + $permissionResolver->expects(self::any()) + ->method('canUser') + ->with( + self::equalTo('content'), + self::equalTo('edit'), + self::equalTo($content), + self::isType('array') + )->will(self::returnValue(true)); + + $this->acceptFieldTypeValueMock($fieldTypeMock); + + $this->isEmptyValueFieldTypeMock($fieldTypeMock); + + $fieldTypeMock->expects(self::any()) + ->method('validate') + ->with( + self::isInstanceOf(APIFieldDefinition::class), + self::isInstanceOf(Value::class) + ); + + $this->getFieldTypeRegistryMock()->expects(self::any()) + ->method('getFieldType') + ->will(self::returnValue($fieldTypeMock)); + + $contentUpdateStruct = new ContentUpdateStruct( + [ + 'fields' => $structFields, + 'initialLanguageCode' => $initialLanguageCode, + ] + ); + + return [$content->versionInfo, $contentUpdateStruct]; + } + + public function providerForTestUpdateContentRequiredField() + { + return [ + [ + 'eng-US', + [ + new Field( + [ + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub(self::EMPTY_FIELD_VALUE), + 'languageCode' => null, + ] + ), + ], + 'identifier', + 'eng-US', + ], + ]; + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + * @dataProvider providerForTestUpdateContentRequiredField + */ + public function testUpdateContentRequiredField( + $initialLanguageCode, + $structFields, + $identifier, + $languageCode + ) { + $this->expectException(ContentFieldValidationException::class); + + $existingFields = [ + new Field( + [ + 'id' => '100', + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('initialValue'), + 'languageCode' => 'eng-GB', + ] + ), + ]; + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => true, + 'identifier' => 'identifier', + 'isRequired' => true, + 'defaultValue' => new ValueStub('defaultValue'), + ] + ), + ]; + list($versionInfo, $contentUpdateStruct) = + $this->assertForTestUpdateContentRequiredField( + $initialLanguageCode, + $structFields, + $existingFields, + $fieldDefinitions + ); + + try { + $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct); + } catch (ContentValidationException $e) { + $this->assertEquals( + "Value for required field definition '{$identifier}' with language '{$languageCode}' is empty", + $e->getMessage() + ); + + throw $e; + } + } + + public function assertForTestUpdateContentThrowsContentFieldValidationException( + $initialLanguageCode, + $structFields, + $existingFields, + $fieldDefinitions, + array $allFieldErrors + ): array { + $permissionResolverMock = $this->getPermissionResolverMock(); + $mockedService = $this->getPartlyMockedContentService(['internalLoadContentById', 'loadContent']); + $languageHandlerMock = $this->getPersistenceMock()->contentLanguageHandler(); + $fieldTypeMock = $this->createMock(SPIFieldType::class); + $existingLanguageCodes = array_map( + static function (Field $field) { + return $field->languageCode; + }, + $existingFields + ); + + $content = $this->prepareContentForTestCreateAndUpdateContent($existingLanguageCodes, $fieldDefinitions, $existingFields); + + $this->loadByLanguageCodeMock($languageHandlerMock); + + $mockedService + ->method('internalLoadContentById') + ->will( + self::returnValue($content) + ); + + $mockedService + ->method('loadContent') + ->with( + self::equalTo(42), + self::equalTo(null), + self::equalTo(7) + ) + ->will( + self::returnValue($content) + ); + + $permissionResolverMock + ->method('canUser') + ->will(self::returnValue(true)); + + $emptyValue = new ValueStub(self::EMPTY_FIELD_VALUE); + + $fieldTypeMock + ->method('acceptValue') + ->will( + self::returnCallback( + static function ($value) { + return $value instanceof SPIValue + ? $value + : new ValueStub($value); + } + ) + ); + + $fieldTypeMock + ->method('isEmptyValue') + ->will( + self::returnCallback( + static function (ValueStub $value) use ($emptyValue) { + return (string)$emptyValue === (string)$value; + } + ) + ); + + $fieldTypeMock + ->expects(self::any()) + ->method('validate') + ->will( + self::returnCallback( + static function (FieldDefinition $fieldDefinition) use ($allFieldErrors, $structFields) { + foreach ($structFields as $structField) { + if ($structField->fieldDefIdentifier !== $fieldDefinition->identifier) { + continue; + } + + return $allFieldErrors[$fieldDefinition->id][$structField->languageCode] ?? null; + } + + return null; + } + ) + ); + + $this->getFieldTypeRegistryMock()->expects(self::any()) + ->method('getFieldType') + ->will(self::returnValue($fieldTypeMock)); + + $contentUpdateStruct = new ContentUpdateStruct( + [ + 'fields' => $structFields, + 'initialLanguageCode' => $initialLanguageCode, + ] + ); + + return [$content->versionInfo, $contentUpdateStruct, $allFieldErrors]; + } + + public function providerForTestUpdateContentThrowsContentFieldValidationException(): array + { + $newValue1engGBValidationError = new ValidationError('newValue1-eng-GB'); + $newValue2ValidationError = new ValidationError('newValue2'); + $newValue4ValidationError = new ValidationError('newValue4'); + + $allFieldErrors = [ + [ + 'fieldDefinitionId1' => [ + 'eng-GB' => $newValue1engGBValidationError, + 'eng-US' => $newValue1engGBValidationError, + ], + 'fieldDefinitionId4' => [ + 'eng-GB' => $newValue4ValidationError, + 'eng-US' => $newValue4ValidationError, + ], + ], + [ + 'fieldDefinitionId1' => [ + 'eng-GB' => $newValue1engGBValidationError, + 'eng-US' => $newValue1engGBValidationError, + ], + ], + [ + 'fieldDefinitionId1' => [ + 'eng-GB' => $newValue1engGBValidationError, + 'eng-US' => $newValue1engGBValidationError, + ], + 'fieldDefinitionId2' => [ + 'eng-GB' => $newValue2ValidationError, + 'eng-US' => $newValue2ValidationError, + ], + ], + [ + 'fieldDefinitionId1' => [ + 'eng-GB' => $newValue1engGBValidationError, + 'eng-US' => $newValue1engGBValidationError, + ], + ], + [ + 'fieldDefinitionId1' => [ + 'eng-GB' => $newValue1engGBValidationError, + 'ger-DE' => $newValue1engGBValidationError, + 'eng-US' => $newValue1engGBValidationError, + ], + 'fieldDefinitionId2' => [ + 'eng-GB' => $newValue2ValidationError, + 'eng-US' => $newValue2ValidationError, + ], + ], + [ + 'fieldDefinitionId2' => [ + 'eng-US' => $newValue2ValidationError, + ], + ], + ]; + + $data = $this->providerForTestUpdateContentNonRedundantFieldSetComplex(); + $count = count($data); + for ($i = 0; $i < $count; ++$i) { + $data[$i][] = $allFieldErrors[$i]; + } + + return $data; + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + * @dataProvider providerForTestUpdateContentThrowsContentFieldValidationException + */ + public function testUpdateContentThrowsContentFieldValidationException( + $initialLanguageCode, + $structFields, + $spiField, + $allFieldErrors + ): void { + $this->expectException(ContentFieldValidationException::class); + $this->expectExceptionMessage('Content "Test" fields did not validate'); + + list($existingFields, $fieldDefinitions) = $this->fixturesForTestUpdateContentNonRedundantFieldSetComplex(); + list($versionInfo, $contentUpdateStruct) = + $this->assertForTestUpdateContentThrowsContentFieldValidationException( + $initialLanguageCode, + $structFields, + $existingFields, + $fieldDefinitions, + $allFieldErrors + ); + + try { + $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct); + } catch (ContentFieldValidationException $e) { + $this->assertEquals($allFieldErrors, $e->getFieldErrors()); + throw $e; + } + } + + /** + * Test for the updateContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getLanguageCodesForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::mapFieldsForUpdate + * @covers \Ibexa\Contracts\Core\Repository\ContentService::updateContent + */ + public function testUpdateContentTransactionRollback() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Store failed'); + + $existingFields = [ + new Field( + [ + 'id' => '100', + 'fieldDefIdentifier' => 'identifier', + 'value' => new ValueStub('initialValue'), + 'languageCode' => 'eng-GB', + ] + ), + ]; + + $fieldDefinitions = [ + new FieldDefinition( + [ + 'id' => 'fieldDefinitionId', + 'fieldTypeIdentifier' => 'fieldTypeIdentifier', + 'isTranslatable' => false, + 'identifier' => 'identifier', + 'isRequired' => false, + 'defaultValue' => new ValueStub('defaultValue'), + ] + ), + ]; + + // Setup a simple case that will pass + list($versionInfo, $contentUpdateStruct) = $this->assertForTestUpdateContentNonRedundantFieldSet( + 'eng-US', + [], + [], + $existingFields, + $fieldDefinitions, + // Do not execute test + false + ); + + $repositoryMock = $this->getRepositoryMock(); + $repositoryMock->expects($this->never())->method('commit'); + $repositoryMock->expects($this->once())->method('rollback'); + + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ + $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); + $contentHandlerMock->expects($this->once()) + ->method('updateContent') + ->with( + $this->anything(), + $this->anything(), + $this->anything() + )->will($this->throwException(new \Exception('Store failed'))); + + // Execute + $this->partlyMockedContentService->updateContent($versionInfo, $contentUpdateStruct); + } + + /** + * Test for the copyContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::copyContent + */ + public function testCopyContentThrowsUnauthorizedException() + { + $this->expectException(UnauthorizedException::class); + + $repository = $this->getRepositoryMock(); + $contentService = $this->getPartlyMockedContentService(['internalLoadContentInfo']); + $contentInfo = $this->createMock(APIContentInfo::class); + $locationCreateStruct = new LocationCreateStruct(); + $locationCreateStruct->parentLocationId = 1; + $location = new Location(['id' => $locationCreateStruct->parentLocationId]); + $locationServiceMock = $this->getLocationServiceMock(); + $permissionResolver = $this->getPermissionResolverMock(); + + $repository->expects($this->once()) + ->method('getLocationService') + ->will(self::returnValue($locationServiceMock)); + + $locationServiceMock->expects(self::once()) + ->method('loadLocation') + ->with( + $locationCreateStruct->parentLocationId + ) + ->will(self::returnValue($location)); + + $contentInfo->expects(self::any()) + ->method('__get') + ->with('sectionId') + ->will(self::returnValue(42)); + + $destinationLocationTarget = (new DestinationLocation($locationCreateStruct->parentLocationId, $contentInfo)); + $permissionResolver + ->method('canUser') + ->with( + 'content', + 'create', + $contentInfo, + [$location, $destinationLocationTarget] + ) + ->will(self::returnValue(false)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfo */ + $contentService->copyContent($contentInfo, $locationCreateStruct); + } + + /** + * Test for the copyContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::copyContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getDefaultObjectStates + * @covers \Ibexa\Contracts\Core\Repository\ContentService::internalPublishVersion + */ + public function testCopyContent() + { + $repositoryMock = $this->getRepositoryMock(); + $contentService = $this->getPartlyMockedContentService([ + 'internalLoadContentInfo', + 'internalLoadContentById', + 'loadContentByVersionInfo', + 'getUnixTimestamp', + ]); + $locationServiceMock = $this->getLocationServiceMock(); + $contentInfoMock = $this->createMock(APIContentInfo::class); + $locationCreateStruct = new LocationCreateStruct(); + $locationCreateStruct->parentLocationId = 2; + $location = new Location(['id' => $locationCreateStruct->parentLocationId]); + $user = $this->getStubbedUser(14); + + $permissionResolverMock = $this->getPermissionResolverMock(); + + $permissionResolverMock + ->method('getCurrentUserReference') + ->willReturn($user); + + $repositoryMock + ->method('getPermissionResolver') + ->willReturn($permissionResolverMock); + + $repositoryMock->expects(self::exactly(3)) + ->method('getLocationService') + ->will(self::returnValue($locationServiceMock)); + + $locationServiceMock->expects(self::once()) + ->method('loadLocation') + ->with($locationCreateStruct->parentLocationId) + ->will(self::returnValue($location)); + + $contentInfoMock->expects(self::any()) + ->method('__get') + ->will( + self::returnValueMap( + [ + ['isHidden', true], + ['id', 42], + ] + ) + ); + $versionInfoMock = $this->createMock(APIVersionInfo::class); + + $versionInfoMock->expects(self::any()) + ->method('__get') + ->will( + self::returnValueMap( + [ + ['versionNo', 123], + ] + ) + ); + + $versionInfoMock->expects(self::once()) + ->method('isDraft') + ->willReturn(true); + + $versionInfoMock + ->method('getContentInfo') + ->will(self::returnValue($contentInfoMock)); + + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ + $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); + $domainMapperMock = $this->getContentDomainMapperMock(); + + $repositoryMock->expects(self::once())->method('beginTransaction'); + $repositoryMock->expects(self::once())->method('commit'); + + $destinationLocationTarget = (new DestinationLocation($locationCreateStruct->parentLocationId, $contentInfoMock)); + $permissionResolverMock + ->method('canUser') + ->withConsecutive( + ['content', 'create', $contentInfoMock, [$location, $destinationLocationTarget]], + ['content', 'manage_locations', $contentInfoMock, [$location]], + ) + ->willReturnOnConsecutiveCalls(true, true); + + $spiContentInfo = new SPIContentInfo(['id' => 42]); + $spiVersionInfo = new SPIVersionInfo( + [ + 'contentInfo' => $spiContentInfo, + 'creationDate' => 123456, + ] + ); + $spiContent = new SPIContent(['versionInfo' => $spiVersionInfo]); + $contentHandlerMock->expects($this->once()) + ->method('copy') + ->with(42, null) + ->will(self::returnValue($spiContent)); + + $this->mockGetDefaultObjectStates(); + $this->mockSetDefaultObjectStates(); + + $domainMapperMock->expects(self::once()) + ->method('buildVersionInfoDomainObject') + ->with($spiVersionInfo) + ->will(self::returnValue($versionInfoMock)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfoMock */ + $content = $this->mockPublishVersion(123456, 126666, true); + $locationServiceMock->expects(self::once()) + ->method('createLocation') + ->with( + $content->getVersionInfo()->getContentInfo(), + $locationCreateStruct + ); + + $contentService + ->method('internalLoadContentById') + ->with( + $content->id + ) + ->will(self::returnValue($content)); + + $contentService->expects(self::once()) + ->method('getUnixTimestamp') + ->will(self::returnValue(126666)); + + $contentService + ->method('loadContentByVersionInfo') + ->will(self::returnValue($content)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfoMock */ + $contentService->copyContent($contentInfoMock, $locationCreateStruct, null); + } + + /** + * Test for the copyContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::copyContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getDefaultObjectStates + * @covers \Ibexa\Contracts\Core\Repository\ContentService::internalPublishVersion + */ + public function testCopyContentWithVersionInfo() + { + $repositoryMock = $this->getRepositoryMock(); + $contentService = $this->getPartlyMockedContentService([ + 'internalLoadContentById', + 'getUnixTimestamp', + ]); + $locationServiceMock = $this->getLocationServiceMock(); + $contentInfoMock = $this->createMock(APIContentInfo::class); + $locationCreateStruct = new LocationCreateStruct(); + $locationCreateStruct->parentLocationId = 2; + $location = new Location(['id' => $locationCreateStruct->parentLocationId]); + $user = $this->getStubbedUser(14); + + $permissionResolverMock = $this->getPermissionResolverMock(); + + $permissionResolverMock + ->method('getCurrentUserReference') + ->willReturn($user); + + $repositoryMock + ->method('getPermissionResolver') + ->willReturn($permissionResolverMock); + + $repositoryMock->expects($this->exactly(3)) + ->method('getLocationService') + ->will($this->returnValue($locationServiceMock)); + + $locationServiceMock->expects($this->once()) + ->method('loadLocation') + ->with($locationCreateStruct->parentLocationId) + ->will($this->returnValue($location)); + + $contentInfoMock->expects($this->any()) + ->method('__get') + ->will( + $this->returnValueMap([ + ['isHidden', true], + ['id', 42], + ]) + ); + + $versionInfoMock = $this->createMock(APIVersionInfo::class); + + $versionInfoMock->expects($this->any()) + ->method('__get') + ->will( + $this->returnValueMap( + [ + ['versionNo', 123], + ] + ) + ); + $versionInfoMock->expects($this->once()) + ->method('isDraft') + ->willReturn(true); + $versionInfoMock + ->method('getContentInfo') + ->will($this->returnValue($contentInfoMock)); + + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ + $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); + $domainMapperMock = $this->getContentDomainMapperMock(); + + $repositoryMock->expects($this->once())->method('beginTransaction'); + $repositoryMock->expects($this->once())->method('commit'); + + $destinationLocationTarget = (new DestinationLocation($locationCreateStruct->parentLocationId, $contentInfoMock)); + $permissionResolverMock + ->method('canUser') + ->withConsecutive( + ['content', 'create', $contentInfoMock, [$location, $destinationLocationTarget]], + ['content', 'manage_locations', $contentInfoMock, [$location]], + ) + ->willReturnOnConsecutiveCalls(true, true); + + $spiContentInfo = new SPIContentInfo(['id' => 42]); + $spiVersionInfo = new SPIVersionInfo( + [ + 'contentInfo' => $spiContentInfo, + 'creationDate' => 123456, + ] + ); + $spiContent = new SPIContent(['versionInfo' => $spiVersionInfo]); + $contentHandlerMock->expects($this->once()) + ->method('copy') + ->with(42, 123) + ->will($this->returnValue($spiContent)); + + $this->mockGetDefaultObjectStates(); + $this->mockSetDefaultObjectStates(); + + $domainMapperMock->expects($this->once()) + ->method('buildVersionInfoDomainObject') + ->with($spiVersionInfo) + ->will($this->returnValue($versionInfoMock)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo $versionInfoMock */ + $content = $this->mockPublishVersion(123456, 126666, true); + $locationServiceMock->expects($this->once()) + ->method('createLocation') + ->with( + $content->getVersionInfo()->getContentInfo(), + $locationCreateStruct + ); + + $contentService + ->method('internalLoadContentById') + ->with( + $content->id + ) + ->will($this->returnValue($content)); + + $contentService->expects($this->once()) + ->method('getUnixTimestamp') + ->will($this->returnValue(126666)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfoMock */ + $contentService->copyContent($contentInfoMock, $locationCreateStruct, $versionInfoMock); + } + + /** + * Test for the copyContent() method. + * + * @covers \Ibexa\Contracts\Core\Repository\ContentService::copyContent + * @covers \Ibexa\Contracts\Core\Repository\ContentService::getDefaultObjectStates + * @covers \Ibexa\Contracts\Core\Repository\ContentService::internalPublishVersion + */ + public function testCopyContentWithRollback() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Handler threw an exception'); + + $repositoryMock = $this->getRepositoryMock(); + $contentService = $this->getPartlyMockedContentService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ + $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); + $locationCreateStruct = new LocationCreateStruct(); + $locationCreateStruct->parentLocationId = 2; + $location = new Location(['id' => $locationCreateStruct->parentLocationId]); + $locationServiceMock = $this->getLocationServiceMock(); + $user = $this->getStubbedUser(14); + + $permissionResolverMock = $this->getPermissionResolverMock(); + + $permissionResolverMock + ->method('getCurrentUserReference') + ->willReturn($user); + + $repositoryMock + ->method('getPermissionResolver') + ->willReturn($permissionResolverMock); + + $repositoryMock->expects(self::once()) + ->method('getLocationService') + ->will(self::returnValue($locationServiceMock)); + + $locationServiceMock->expects(self::once()) + ->method('loadLocation') + ->with($locationCreateStruct->parentLocationId) + ->will(self::returnValue($location)); + + $contentInfoMock = $this->createMock(APIContentInfo::class); + $contentInfoMock->expects(self::any()) + ->method('__get') + ->with('id') + ->will(self::returnValue(42)); + + $this->mockGetDefaultObjectStates(); + + $repositoryMock->expects(self::once())->method('beginTransaction'); + $repositoryMock->expects(self::once())->method('rollback'); + + $destinationLocationTarget = (new DestinationLocation($locationCreateStruct->parentLocationId, $contentInfoMock)); + $permissionResolverMock + ->method('canUser') + ->withConsecutive( + ['content', 'create', $contentInfoMock, [$location, $destinationLocationTarget]], + ['content', 'manage_locations', $contentInfoMock, [$location]], + ) + ->willReturnOnConsecutiveCalls(true, true); + + $contentHandlerMock->expects(self::once()) + ->method('copy') + ->with(42, null) + ->will($this->throwException(new Exception('Handler threw an exception'))); + + /* @var \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo $contentInfoMock */ + $contentService->copyContent($contentInfoMock, $locationCreateStruct, null); + } + + /** + * Reusable method for setting exceptions on buildContentDomainObject usage. + * + * Plain usage as in when content type is loaded directly. + * + * @param \Ibexa\Contracts\Core\Persistence\Content $spiContent + * @param array $translations + * @param bool $useAlwaysAvailable + * + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + private function mockBuildContentDomainObject(SPIContent $spiContent, array $translations = null, bool $useAlwaysAvailable = null) + { + $contentTypeId = $spiContent->versionInfo->contentInfo->contentTypeId; + $contentTypeServiceMock = $this->getContentTypeServiceMock(); + $repositoryMock = $this->getRepositoryMock(); + + $contentType = new ContentType([ + 'id' => $contentTypeId, + 'fieldDefinitions' => new FieldDefinitionCollection([]), + ]); + + $repositoryMock->expects($this->once()) + ->method('getContentTypeService') + ->willReturn($contentTypeServiceMock); + + $contentTypeServiceMock + ->method('loadContentType') + ->with($this->equalTo($contentTypeId)) + ->willReturn($contentType); + + $content = $this->createMock(APIContent::class); + $content->method('getContentType') + ->willReturn($contentType); + + $this->getContentDomainMapperMock() + ->expects($this->once()) + ->method('buildContentDomainObject') + ->with($spiContent, $contentType, $translations ?? [], $useAlwaysAvailable) + ->willReturn($content); + + return $content; + } + + protected function mockGetDefaultObjectStates() + { + /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */ + $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler(); + + $objectStateGroups = [ + new SPIObjectStateGroup(['id' => 10]), + new SPIObjectStateGroup(['id' => 20]), + ]; + + /* @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */ + $objectStateHandlerMock->expects($this->once()) + ->method('loadAllGroups') + ->will($this->returnValue($objectStateGroups)); + + $objectStateHandlerMock->expects($this->at(1)) + ->method('loadObjectStates') + ->with($this->equalTo(10)) + ->will( + $this->returnValue( + [ + new SPIObjectState(['id' => 11, 'groupId' => 10]), + new SPIObjectState(['id' => 12, 'groupId' => 10]), + ] + ) + ); + + $objectStateHandlerMock->expects($this->at(2)) + ->method('loadObjectStates') + ->with($this->equalTo(20)) + ->will( + $this->returnValue( + [ + new SPIObjectState(['id' => 21, 'groupId' => 20]), + new SPIObjectState(['id' => 22, 'groupId' => 20]), + ] + ) + ); + } + + protected function mockSetDefaultObjectStates() + { + /** @var \PHPUnit\Framework\MockObject\MockObject $objectStateHandlerMock */ + $objectStateHandlerMock = $this->getPersistenceMock()->objectStateHandler(); + + $defaultObjectStates = [ + new SPIObjectState(['id' => 11, 'groupId' => 10]), + new SPIObjectState(['id' => 21, 'groupId' => 20]), + ]; + foreach ($defaultObjectStates as $index => $objectState) { + $objectStateHandlerMock->expects($this->at($index + 3)) + ->method('setContentState') + ->with( + 42, + $objectState->groupId, + $objectState->id + ); + } + } + + /** + * @param int|null $publicationDate + * @param int|null $modificationDate + * @param bool $isHidden + * + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Content + */ + protected function mockPublishVersion($publicationDate = null, $modificationDate = null, $isHidden = false) + { + $versionInfoMock = $this->createMock(APIVersionInfo::class); + $contentInfoMock = $this->createMock(APIContentInfo::class); + /* @var \PHPUnit\Framework\MockObject\MockObject $contentHandlerMock */ + $contentHandlerMock = $this->getPersistenceMock()->contentHandler(); + $metadataUpdateStruct = new SPIMetadataUpdateStruct(); + + $spiContent = new SPIContent([ + 'versionInfo' => new VersionInfo([ + 'contentInfo' => new ContentInfo(['id' => 42, 'contentTypeId' => 123]), + ]), + 'fields' => new FieldDefinitionCollection([]), + ]); + + $contentMock = $this->mockBuildContentDomainObject($spiContent); + $contentMock->expects($this->any()) + ->method('__get') + ->will( + $this->returnValueMap( + [ + ['id', 42], + ['contentInfo', $contentInfoMock], + ['versionInfo', $versionInfoMock], + ] + ) + ); + $contentMock->expects($this->any()) + ->method('getVersionInfo') + ->will($this->returnValue($versionInfoMock)); + $versionInfoMock->expects($this->any()) + ->method('getContentInfo') + ->will($this->returnValue($contentInfoMock)); + $versionInfoMock->expects($this->any()) + ->method('__get') + ->will( + $this->returnValueMap( + [ + ['languageCodes', ['eng-GB']], + ] + ) + ); + $contentInfoMock->expects($this->any()) + ->method('__get') + ->will( + $this->returnValueMap( + [ + ['alwaysAvailable', true], + ['mainLanguageCode', 'eng-GB'], + ] + ) + ); + + $currentTime = time(); + if ($publicationDate === null && $versionInfoMock->versionNo === 1) { + $publicationDate = $currentTime; + } + + // Account for 1 second of test execution time + $metadataUpdateStruct->publicationDate = $publicationDate; + $metadataUpdateStruct->modificationDate = $modificationDate ?? $currentTime; + $metadataUpdateStruct->isHidden = $isHidden; + + $contentHandlerMock->expects($this->once()) + ->method('publish') + ->with( + 42, + 123, + $metadataUpdateStruct + ) + ->will($this->returnValue($spiContent)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\Content\Content $contentMock */ + $this->mockPublishUrlAliasesForContent($contentMock); + + return $contentMock; + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Content $content + */ + protected function mockPublishUrlAliasesForContent(APIContent $content) + { + $nameSchemaServiceMock = $this->getNameSchemaServiceMock(); + /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ + $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); + $locationServiceMock = $this->getLocationServiceMock(); + $location = $this->createMock(APILocation::class); + + $location->expects($this->at(0)) + ->method('__get') + ->with('id') + ->will($this->returnValue(123)); + $location->expects($this->at(1)) + ->method('__get') + ->with('parentLocationId') + ->will($this->returnValue(456)); + + $urlAliasNames = ['eng-GB' => 'hello']; + $nameSchemaServiceMock->expects($this->once()) + ->method('resolveUrlAliasSchema') + ->with($content) + ->will($this->returnValue($urlAliasNames)); + + $locationServiceMock->expects($this->once()) + ->method('loadLocations') + ->with($content->getVersionInfo()->getContentInfo()) + ->will($this->returnValue([$location])); + + $urlAliasHandlerMock->expects($this->once()) + ->method('publishUrlAliasForLocation') + ->with(123, 456, 'hello', 'eng-GB', true, true); + + $location->expects($this->at(2)) + ->method('__get') + ->with('id') + ->will($this->returnValue(123)); + + $location->expects($this->at(3)) + ->method('__get') + ->with('parentLocationId') + ->will($this->returnValue(456)); + + $urlAliasHandlerMock->expects($this->once()) + ->method('archiveUrlAliasesForDeletedTranslations') + ->with(123, 456, ['eng-GB']); + } + + protected $relationProcessorMock; + + /** + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Repository\Helper\RelationProcessor + */ + protected function getRelationProcessorMock() + { + if (!isset($this->relationProcessorMock)) { + $this->relationProcessorMock = $this->createMock(RelationProcessor::class); + } + + return $this->relationProcessorMock; + } + + /** + * @var \PHPUnit\Framework\MockObject\MockObject + * &\Ibexa\Contracts\Core\Repository\NameSchema\NameSchemaServiceInterface + */ + protected NameSchemaServiceInterface $nameSchemaServiceMock; + + /** + * @return \PHPUnit\Framework\MockObject\MockObject + * &\Ibexa\Contracts\Core\Repository\NameSchema\NameSchemaServiceInterface + */ + protected function getNameSchemaServiceMock(): NameSchemaServiceInterface + { + if (!isset($this->nameSchemaServiceMock)) { + $this->nameSchemaServiceMock = $this->createMock(NameSchemaServiceInterface::class); + } + + return $this->nameSchemaServiceMock; + } + + protected $contentTypeServiceMock; + + /** + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Core\Repository\ContentTypeService + */ + protected function getContentTypeServiceMock() + { + if (!isset($this->contentTypeServiceMock)) { + $this->contentTypeServiceMock = $this->createMock(APIContentTypeService::class); + } + + return $this->contentTypeServiceMock; + } + + protected $locationServiceMock; + + /** + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Core\Repository\LocationService + */ + protected function getLocationServiceMock() + { + if (!isset($this->locationServiceMock)) { + $this->locationServiceMock = $this->createMock(APILocationService::class); + } + + return $this->locationServiceMock; + } + + /** @var \Ibexa\Core\Repository\ContentService */ + protected $partlyMockedContentService; + + /** + * Returns the content service to test with $methods mocked. + * + * Injected Repository comes from {@see getRepositoryMock()} and persistence handler from {@see getPersistenceMock()} + * + * @param string[] $methods + * + * @return \Ibexa\Core\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getPartlyMockedContentService(array $methods = null) + { + if (!isset($this->partlyMockedContentService)) { + $this->partlyMockedContentService = $this->getMockBuilder(ContentService::class) + ->setMethods($methods) + ->setConstructorArgs( + [ + $this->getRepositoryMock(), + $this->getPersistenceMock(), + $this->getContentDomainMapperMock(), + $this->getRelationProcessorMock(), + $this->getNameSchemaServiceMock(), + $this->getFieldTypeRegistryMock(), + $this->getPermissionServiceMock(), + $this->getContentMapper(), + $this->getContentValidatorStrategy(), + $this->getContentFilteringHandlerMock(), + [], + ] + ) + ->getMock(); + } + + return $this->partlyMockedContentService; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getRepositoryMock(): Repository + { + $repositoryMock = parent::getRepositoryMock(); + $repositoryMock + ->expects($this->any()) + ->method('getPermissionResolver') + ->willReturn($this->getPermissionResolverMock()); + + return $repositoryMock; + } +} + +class_alias(ContentTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\ContentTest'); diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/PermissionTest.php b/tests/lib/Repository/Service/Mock/PermissionTest.php similarity index 93% rename from eZ/Publish/Core/Repository/Tests/Service/Mock/PermissionTest.php rename to tests/lib/Repository/Service/Mock/PermissionTest.php index cd8c9e198b..96b464c382 100644 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/PermissionTest.php +++ b/tests/lib/Repository/Service/Mock/PermissionTest.php @@ -4,21 +4,22 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; - -use eZ\Publish\API\Repository\Repository; -use eZ\Publish\API\Repository\Values\User\Limitation; -use eZ\Publish\API\Repository\Values\ValueObject; -use eZ\Publish\Core\Base\Exceptions\NotFound\LimitationNotFoundException; -use eZ\Publish\Core\MVC\ConfigResolverInterface; -use eZ\Publish\Core\Repository\Permission\PermissionResolver; -use eZ\Publish\Core\Repository\Repository as CoreRepository; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\Core\Repository\Values\User\UserReference; -use eZ\Publish\SPI\Limitation\Type; -use eZ\Publish\SPI\Persistence\User\Policy; -use eZ\Publish\SPI\Persistence\User\Role; -use eZ\Publish\SPI\Persistence\User\RoleAssignment; +namespace Ibexa\Tests\Core\Repository\Service\Mock; + +use Ibexa\Contracts\Core\Limitation\Type; +use Ibexa\Contracts\Core\Persistence\User\Policy; +use Ibexa\Contracts\Core\Persistence\User\Role; +use Ibexa\Contracts\Core\Persistence\User\RoleAssignment; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation; +use Ibexa\Contracts\Core\Repository\Values\ValueObject; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\Base\Exceptions\InvalidArgumentValue; +use Ibexa\Core\Base\Exceptions\NotFound\LimitationNotFoundException; +use Ibexa\Core\Repository\Permission\PermissionResolver; +use Ibexa\Core\Repository\Repository as CoreRepository; +use Ibexa\Core\Repository\Values\User\UserReference; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; /** * Mock test case for PermissionResolver. @@ -312,7 +313,7 @@ public function testHasAccessReturnsPermissionSets(array $roles, array $roleAssi $permissionSets = []; $count = 0; - /* @var $roleAssignments \eZ\Publish\SPI\Persistence\User\RoleAssignment[] */ + /* @var $roleAssignments \Ibexa\Contracts\Core\Persistence\User\RoleAssignment[] */ foreach ($roleAssignments as $i => $roleAssignment) { $permissionSet = ['limitation' => null]; foreach ($roles[$roleAssignment->roleId]->policies as $k => $policy) { @@ -332,7 +333,7 @@ public function testHasAccessReturnsPermissionSets(array $roles, array $roleAssi } } - /* @var $repositoryMock \eZ\Publish\Core\Repository\Repository */ + /* @var $repositoryMock \Ibexa\Core\Repository\Repository */ self::assertEquals( $permissionSets, $permissionResolverMock->hasAccess('dummy-module', 'dummy-function') @@ -400,7 +401,7 @@ public function providerForTestHasAccessReturnsLimitationNotFoundException() */ public function testHasAccessReturnsLimitationNotFoundException(array $roles, array $roleAssignments) { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\NotFound\LimitationNotFoundException::class); + $this->expectException(LimitationNotFoundException::class); /** @var $userHandlerMock \PHPUnit\Framework\MockObject\MockObject */ $userHandlerMock = $this->getPersistenceMock()->userHandler(); @@ -427,7 +428,7 @@ public function testHasAccessReturnsLimitationNotFoundException(array $roles, ar } $count = 0; - /* @var $roleAssignments \eZ\Publish\SPI\Persistence\User\RoleAssignment[] */ + /* @var $roleAssignments \Ibexa\Contracts\Core\Persistence\User\RoleAssignment[] */ foreach ($roleAssignments as $i => $roleAssignment) { $permissionSet = ['limitation' => null]; foreach ($roles[$roleAssignment->roleId]->policies as $k => $policy) { @@ -515,13 +516,13 @@ public function providerForTestHasAccessReturnsInvalidArgumentValueException() */ public function testHasAccessReturnsInvalidArgumentValueException(array $roles, array $roleAssignments) { - $this->expectException(\eZ\Publish\Core\Base\Exceptions\InvalidArgumentValue::class); + $this->expectException(InvalidArgumentValue::class); $permissionResolverMock = $this->getPermissionResolverMock(['getCurrentUserReference']); - /** @var $role \eZ\Publish\SPI\Persistence\User\Role */ + /** @var $role \Ibexa\Contracts\Core\Persistence\User\Role */ foreach ($roles as $role) { - /** @var $policy \eZ\Publish\SPI\Persistence\User\Policy */ + /** @var $policy \Ibexa\Contracts\Core\Persistence\User\Policy */ foreach ($role->policies as $policy) { $permissionResolverMock->hasAccess($policy->module, $policy->function); } @@ -607,7 +608,7 @@ public function testHasAccessReturnsPermissionSetsWithRoleLimitation(array $role } $permissionSets = []; - /** @var $roleAssignments \eZ\Publish\SPI\Persistence\User\RoleAssignment[] */ + /** @var $roleAssignments \Ibexa\Contracts\Core\Persistence\User\RoleAssignment[] */ foreach ($roleAssignments as $i => $roleAssignment) { $permissionSet = []; foreach ($roles[$roleAssignment->roleId]->policies as $k => $policy) { @@ -647,7 +648,7 @@ public function testHasAccessReturnsPermissionSetsWithRoleLimitation(array $role * @param array $policiesData * @param mixed $roleId * - * @return \eZ\Publish\SPI\Persistence\User\Role + * @return \Ibexa\Contracts\Core\Persistence\User\Role */ private function createRole(array $policiesData, $roleId = null) { @@ -696,7 +697,7 @@ public function testCanUserSimple($permissionSets, $result) ->with($this->equalTo('test-module'), $this->equalTo('test-function')) ->will($this->returnValue($permissionSets)); - /** @var $valueObject \eZ\Publish\API\Repository\Values\ValueObject */ + /** @var $valueObject \Ibexa\Contracts\Core\Repository\Values\ValueObject */ $valueObject = $this->getMockForAbstractClass(ValueObject::class); self::assertEquals( @@ -746,7 +747,7 @@ public function testCanUserWithoutLimitations() ->method('getCurrentUserReference') ->will($this->returnValue(new UserReference(14))); - /** @var $valueObject \eZ\Publish\API\Repository\Values\ValueObject */ + /** @var $valueObject \Ibexa\Contracts\Core\Repository\Values\ValueObject */ $valueObject = $this->getMockForAbstractClass(ValueObject::class); self::assertTrue( @@ -886,7 +887,7 @@ public function providerForTestCanUserComplex() */ public function testCanUserComplex(array $roleLimitationEvaluations, array $policyLimitationEvaluations, $userCan) { - /** @var $valueObject \eZ\Publish\API\Repository\Values\ValueObject */ + /** @var $valueObject \Ibexa\Contracts\Core\Repository\Values\ValueObject */ $valueObject = $this->createMock(ValueObject::class); $limitationServiceMock = $this->getLimitationServiceMock(); $permissionResolverMock = $this->getPermissionResolverMock( @@ -928,7 +929,7 @@ public function testCanUserComplex(array $roleLimitationEvaluations, array $poli } for ($j = 0; $j < count($permissionSets[$i]['policies']); ++$j) { - /** @var $policy \eZ\Publish\API\Repository\Values\User\Policy */ + /** @var $policy \Ibexa\Contracts\Core\Repository\Values\User\Policy */ $policy = $permissionSets[$i]['policies'][$j]; $limitations = $policy->getLimitations(); for ($k = 0; $k < count($limitations); ++$k) { @@ -1003,7 +1004,7 @@ public function testGetCurrentUserReferenceReturnsAnonymousUser() protected $permissionResolverMock; /** - * @return \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ protected function getPermissionResolverMock($methods = []) { @@ -1063,7 +1064,7 @@ protected function getUserReferenceMock() protected $repositoryMock; /** - * @return \eZ\Publish\API\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject */ protected function getRepositoryMock(): Repository { @@ -1078,3 +1079,5 @@ protected function getRepositoryMock(): Repository return $this->repositoryMock; } } + +class_alias(PermissionTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\PermissionTest'); diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/PermissionsCriterionHandlerTest.php b/tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php similarity index 91% rename from eZ/Publish/Core/Repository/Tests/Service/Mock/PermissionsCriterionHandlerTest.php rename to tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php index 7aec22c50a..5b28ecd82f 100644 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/PermissionsCriterionHandlerTest.php +++ b/tests/lib/Repository/Service/Mock/PermissionsCriterionHandlerTest.php @@ -4,14 +4,16 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; +namespace Ibexa\Tests\Core\Repository\Service\Mock; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\User\Limitation as APILimitation; -use eZ\Publish\Core\Repository\PermissionsCriterionHandler; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\Core\Repository\Values\User\Policy; +use Ibexa\Contracts\Core\Limitation\Type; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation as APILimitation; +use Ibexa\Core\Repository\PermissionsCriterionHandler; +use Ibexa\Core\Repository\Values\User\Policy; +use Ibexa\Core\Repository\Values\User\User; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; /** * Mock test case for PermissionCriterionHandler. @@ -43,7 +45,7 @@ public function testAddPermissionsCriterionWithBooleanPermission($permissionsCri ->method('getPermissionsCriterion') ->will($this->returnValue($permissionsCriterion)); - /* @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterionMock */ + /* @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterionMock */ $result = $handler->addPermissionsCriterion($criterionMock); $this->assertSame($permissionsCriterion, $result); @@ -80,7 +82,7 @@ public function testAddPermissionsCriterion($permissionsCriterionMock, $givenCri ->method('getPermissionsCriterion') ->will($this->returnValue($permissionsCriterionMock)); - /* @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterionMock */ + /* @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterionMock */ $result = $handler->addPermissionsCriterion($givenCriterion); $this->assertTrue($result); @@ -274,8 +276,8 @@ public function providerForTestGetPermissionsCriterion() protected function mockServices($criterionMock, $limitationCount, $permissionSets) { - $userMock = $this->createMock('eZ\\Publish\\API\\Repository\\Values\\User\\User'); - $limitationTypeMock = $this->createMock('eZ\\Publish\\SPI\\Limitation\\Type'); + $userMock = $this->createMock(User::class); + $limitationTypeMock = $this->createMock(Type::class); $limitationServiceMock = $this->getLimitationServiceMock(); $permissionResolverMock = $this->getPermissionResolverMock( [ @@ -363,7 +365,7 @@ public function testGetPermissionsCriterionBooleanPermissionSets($permissionSets * * @param string[]|null $methods * - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\Core\Repository\PermissionsCriterionHandler + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Repository\PermissionsCriterionHandler */ protected function getPermissionsCriterionHandlerMock($methods = []) { @@ -394,3 +396,5 @@ protected function getPermissionResolverMock($methods = []) return $this->permissionResolverMock; } } + +class_alias(PermissionsCriterionHandlerTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\PermissionsCriterionHandlerTest'); diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/RelationProcessorTest.php b/tests/lib/Repository/Service/Mock/RelationProcessorTest.php similarity index 90% rename from eZ/Publish/Core/Repository/Tests/Service/Mock/RelationProcessorTest.php rename to tests/lib/Repository/Service/Mock/RelationProcessorTest.php index 569e713f6c..f9b98ac687 100644 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/RelationProcessorTest.php +++ b/tests/lib/Repository/Service/Mock/RelationProcessorTest.php @@ -4,24 +4,25 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; - -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\ContentInfo; -use eZ\Publish\API\Repository\Values\Content\Relation; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\FieldType\Value; -use eZ\Publish\Core\Repository\FieldTypeService; -use eZ\Publish\Core\Repository\Helper\RelationProcessor; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\Core\Repository\Values\ContentType\FieldDefinition; -use eZ\Publish\SPI\FieldType\FieldType; -use eZ\Publish\SPI\Persistence\Content\Location; -use eZ\Publish\SPI\Persistence\Content\Relation\CreateStruct; +namespace Ibexa\Tests\Core\Repository\Service\Mock; + +use Ibexa\Contracts\Core\FieldType\FieldType; +use Ibexa\Contracts\Core\Persistence\Content\Location; +use Ibexa\Contracts\Core\Persistence\Content\Relation\CreateStruct; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\FieldType\Value; +use Ibexa\Core\Repository\FieldTypeService; +use Ibexa\Core\Repository\Helper\RelationProcessor; +use Ibexa\Core\Repository\Values\Content\Relation as RelationValue; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; use Psr\Log\LoggerInterface; /** - * Mock Test case for RelationProcessor service. + * @covers \Ibexa\Core\Repository\Helper\RelationProcessor */ class RelationProcessorTest extends BaseServiceMockTest { @@ -161,7 +162,6 @@ public function providerForTestAppendRelations() * Test for the appendFieldRelations() method. * * @dataProvider providerForTestAppendRelations - * @covers \eZ\Publish\Core\Repository\Helper\RelationProcessor::appendFieldRelations */ public function testAppendFieldRelations(array $fieldRelations, array $expected) { @@ -228,8 +228,6 @@ protected function assertLocationHandlerExpectation($locationHandlerMock, $field /** * Test for the appendFieldRelations() method. - * - * @covers \eZ\Publish\Core\Repository\Helper\RelationProcessor::appendFieldRelations */ public function testAppendFieldRelationsLocationMappingWorks() { @@ -345,8 +343,6 @@ public function testAppendFieldRelationsLogsMissingLocations() /** * Test for the processFieldRelations() method. - * - * @covers \eZ\Publish\Core\Repository\Helper\RelationProcessor::processFieldRelations */ public function testProcessFieldRelationsNoChanges() { @@ -417,8 +413,6 @@ public function testProcessFieldRelationsNoChanges() /** * Test for the processFieldRelations() method. - * - * @covers \eZ\Publish\Core\Repository\Helper\RelationProcessor::processFieldRelations */ public function testProcessFieldRelationsAddsRelations() { @@ -527,8 +521,6 @@ public function testProcessFieldRelationsAddsRelations() /** * Test for the processFieldRelations() method. - * - * @covers \eZ\Publish\Core\Repository\Helper\RelationProcessor::processFieldRelations */ public function testProcessFieldRelationsRemovesRelations() { @@ -575,42 +567,45 @@ public function testProcessFieldRelationsRemovesRelations() $contentHandlerMock->expects($this->never())->method('addRelation'); - $contentTypeMock->expects($this->at(0)) + $contentTypeMock->expects(self::at(0)) ->method('getFieldDefinition') ->with($this->equalTo('identifier42')) ->will($this->returnValue(new FieldDefinition(['id' => 42]))); - $contentTypeMock->expects($this->at(1)) + $contentTypeMock->expects(self::at(1)) ->method('getFieldDefinition') ->with($this->equalTo('identifier44')) ->will($this->returnValue(new FieldDefinition(['id' => 44]))); - $contentHandlerMock->expects($this->at(0)) + $contentHandlerMock->expects(self::at(0)) ->method('removeRelation') ->with( - $this->equalTo(7), - $this->equalTo(Relation::EMBED) + self::equalTo(7), + self::equalTo(Relation::EMBED), + self::equalTo(16) ); - $contentHandlerMock->expects($this->at(1)) + $contentHandlerMock->expects(self::at(1)) ->method('removeRelation') ->with( - $this->equalTo(7), - $this->equalTo(Relation::LINK) + self::equalTo(7), + self::equalTo(Relation::LINK), + self::equalTo(16) ); - $contentHandlerMock->expects($this->at(2)) + $contentHandlerMock->expects(self::at(2)) ->method('removeRelation') ->with( - $this->equalTo(4), - $this->equalTo(Relation::FIELD) + self::equalTo(4), + self::equalTo(Relation::FIELD), + self::equalTo(13) ); - $contentHandlerMock->expects($this->at(3)) + $contentHandlerMock->expects(self::at(3)) ->method('removeRelation') ->with( - $this->equalTo(9), - $this->equalTo(Relation::FIELD) + self::equalTo(9), + self::equalTo(Relation::FIELD) ); $relationProcessor->processFieldRelations( @@ -624,8 +619,6 @@ public function testProcessFieldRelationsRemovesRelations() /** * Test for the processFieldRelations() method. - * - * @covers \eZ\Publish\Core\Repository\Helper\RelationProcessor::processFieldRelations */ public function testProcessFieldRelationsWhenRelationFieldNoLongerExists() { @@ -653,7 +646,7 @@ public function testProcessFieldRelationsWhenRelationFieldNoLongerExists() protected function getStubbedRelation($id, $type, $fieldDefinitionId, $contentId) { - return new \eZ\Publish\Core\Repository\Values\Content\Relation( + return new RelationValue( [ 'id' => $id, 'type' => $type, @@ -672,7 +665,7 @@ protected function getStubbedRelation($id, $type, $fieldDefinitionId, $contentId * * @param string[] $methods * - * @return \eZ\Publish\Core\Repository\Helper\RelationProcessor|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Repository\Helper\RelationProcessor|\PHPUnit\Framework\MockObject\MockObject */ protected function getPartlyMockedRelationProcessor(array $methods = null) { @@ -694,3 +687,5 @@ protected function getFieldTypeServiceMock() return $this->createMock(FieldTypeService::class); } } + +class_alias(RelationProcessorTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\RelationProcessorTest'); diff --git a/tests/lib/Repository/Service/Mock/RepositoryTest.php b/tests/lib/Repository/Service/Mock/RepositoryTest.php new file mode 100644 index 0000000000..c3d9497015 --- /dev/null +++ b/tests/lib/Repository/Service/Mock/RepositoryTest.php @@ -0,0 +1,120 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Service\Mock; + +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; + +/** + * Mock test case for Repository. + */ +class RepositoryTest extends BaseServiceMockTest +{ + /** + * Test for the beginTransaction() method. + * + * @covers \Ibexa\Contracts\Core\Repository\Repository::beginTransaction + */ + public function testBeginTransaction() + { + $mockedRepository = $this->getRepository(); + $persistenceHandlerMock = $this->getPersistenceMock(); + + $persistenceHandlerMock->expects( + $this->once() + )->method( + 'beginTransaction' + ); + + $mockedRepository->beginTransaction(); + } + + /** + * Test for the commit() method. + * + * @covers \Ibexa\Contracts\Core\Repository\Repository::commit + */ + public function testCommit() + { + $mockedRepository = $this->getRepository(); + $persistenceHandlerMock = $this->getPersistenceMock(); + + $persistenceHandlerMock->expects( + $this->once() + )->method( + 'commit' + ); + + $mockedRepository->commit(); + } + + /** + * Test for the commit() method. + * + * @covers \Ibexa\Contracts\Core\Repository\Repository::commit + */ + public function testCommitThrowsRuntimeException() + { + $this->expectException(\RuntimeException::class); + + $mockedRepository = $this->getRepository(); + $persistenceHandlerMock = $this->getPersistenceMock(); + + $persistenceHandlerMock->expects( + $this->once() + )->method( + 'commit' + )->will( + $this->throwException(new \Exception()) + ); + + $mockedRepository->commit(); + } + + /** + * Test for the rollback() method. + * + * @covers \Ibexa\Contracts\Core\Repository\Repository::rollback + */ + public function testRollback() + { + $mockedRepository = $this->getRepository(); + $persistenceHandlerMock = $this->getPersistenceMock(); + + $persistenceHandlerMock->expects( + $this->once() + )->method( + 'rollback' + ); + + $mockedRepository->rollback(); + } + + /** + * Test for the rollback() method. + * + * @covers \Ibexa\Contracts\Core\Repository\Repository::rollback + */ + public function testRollbackThrowsRuntimeException() + { + $this->expectException(\RuntimeException::class); + + $mockedRepository = $this->getRepository(); + $persistenceHandlerMock = $this->getPersistenceMock(); + + $persistenceHandlerMock->expects( + $this->once() + )->method( + 'rollback' + )->will( + $this->throwException(new \Exception()) + ); + + $mockedRepository->rollback(); + } +} + +class_alias(RepositoryTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\RepositoryTest'); diff --git a/tests/lib/Repository/Service/Mock/RoleTest.php b/tests/lib/Repository/Service/Mock/RoleTest.php new file mode 100644 index 0000000000..0fe6400443 --- /dev/null +++ b/tests/lib/Repository/Service/Mock/RoleTest.php @@ -0,0 +1,1100 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Service\Mock; + +use ArrayIterator; +use Ibexa\Contracts\Core\Limitation\Type as SPIType; +use Ibexa\Contracts\Core\Persistence\User as SPIUser; +use Ibexa\Contracts\Core\Persistence\User\Role as SPIRole; +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Contracts\Core\Repository\Exceptions\LimitationValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\UserService; +use Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft; +use Ibexa\Contracts\Core\Repository\Values\User\PolicyUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\Role; +use Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\RoleDraft; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroup; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Repository\Mapper\RoleDomainMapper; +use Ibexa\Core\Repository\Permission\LimitationService; +use Ibexa\Core\Repository\RoleService; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; + +/** + * @covers \Ibexa\Contracts\Core\Repository\RoleService + * @covers \Ibexa\Core\Repository\Permission\LimitationService::validateLimitations + * @covers \Ibexa\Core\Repository\Permission\LimitationService::validateLimitation + */ +class RoleTest extends BaseServiceMockTest +{ + /** + * Test for the createRole() method. + */ + public function testCreateRoleThrowsLimitationValidationException() + { + $this->expectException(LimitationValidationException::class); + + $limitationMock = $this->createMock(RoleLimitation::class); + $limitationTypeMock = $this->createMock(SPIType::class); + + $limitationMock->expects($this->any()) + ->method('getIdentifier') + ->will($this->returnValue('mockIdentifier')); + + $limitationTypeMock->expects($this->once()) + ->method('acceptValue') + ->with($this->equalTo($limitationMock)); + $limitationTypeMock->expects($this->once()) + ->method('validate') + ->with($this->equalTo($limitationMock)) + ->will($this->returnValue([42])); + + $settings = [ + 'policyMap' => ['mockModule' => ['mockFunction' => ['mockIdentifier' => true]]], + 'limitationTypes' => ['mockIdentifier' => $limitationTypeMock], + ]; + + $roleServiceMock = $this->getPartlyMockedRoleService(['loadRoleByIdentifier'], $settings); + + /** @var \Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct $roleCreateStructMock */ + $roleCreateStructMock = $this->createMock(RoleCreateStruct::class); + $policyCreateStructMock = $this->createMock(PolicyCreateStruct::class); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct $policyCreateStructMock */ + $policyCreateStructMock->module = 'mockModule'; + $policyCreateStructMock->function = 'mockFunction'; + $roleCreateStructMock->identifier = 'mockIdentifier'; + $roleServiceMock->expects($this->once()) + ->method('loadRoleByIdentifier') + ->with($this->equalTo('mockIdentifier')) + ->will($this->throwException(new NotFoundException('Role', 'mockIdentifier'))); + + /* @var \PHPUnit\Framework\MockObject\MockObject $roleCreateStructMock */ + $roleCreateStructMock->expects($this->once()) + ->method('getPolicies') + ->will($this->returnValue([$policyCreateStructMock])); + + /* @var \PHPUnit\Framework\MockObject\MockObject $policyCreateStructMock */ + $policyCreateStructMock->expects($this->once()) + ->method('getLimitations') + ->will($this->returnValue([$limitationMock])); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('create'), + $this->equalTo($roleCreateStructMock) + )->will($this->returnValue(true)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\RoleCreateStruct $roleCreateStructMock */ + $roleServiceMock->createRole($roleCreateStructMock); + } + + /** + * Test for the addPolicy() method. + */ + public function testAddPolicyThrowsLimitationValidationException() + { + $this->expectException(LimitationValidationException::class); + + $limitationMock = $this->createMock(RoleLimitation::class); + $limitationTypeMock = $this->createMock(SPIType::class); + + $limitationTypeMock->expects($this->once()) + ->method('acceptValue') + ->with($this->equalTo($limitationMock)); + $limitationTypeMock->expects($this->once()) + ->method('validate') + ->with($this->equalTo($limitationMock)) + ->will($this->returnValue([42])); + + $limitationMock->expects($this->any()) + ->method('getIdentifier') + ->will($this->returnValue('mockIdentifier')); + + $settings = [ + 'policyMap' => ['mockModule' => ['mockFunction' => ['mockIdentifier' => true]]], + 'limitationTypes' => ['mockIdentifier' => $limitationTypeMock], + ]; + + $roleServiceMock = $this->getPartlyMockedRoleService(['loadRoleDraft'], $settings); + + $roleDraftMock = $this->createMock(RoleDraft::class); + $policyCreateStructMock = $this->createMock(PolicyCreateStruct::class); + + $roleDraftMock->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(42)); + /* @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct $policyCreateStructMock */ + $policyCreateStructMock->module = 'mockModule'; + $policyCreateStructMock->function = 'mockFunction'; + + $roleServiceMock->expects($this->once()) + ->method('loadRoleDraft') + ->with($this->equalTo(42)) + ->will($this->returnValue($roleDraftMock)); + + /* @var \PHPUnit\Framework\MockObject\MockObject $policyCreateStructMock */ + $policyCreateStructMock->expects($this->once()) + ->method('getLimitations') + ->will($this->returnValue([$limitationMock])); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('update'), + $this->equalTo($roleDraftMock) + )->will($this->returnValue(true)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Role $roleDraftMock */ + /* @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyCreateStruct $policyCreateStructMock */ + $roleServiceMock->addPolicyByRoleDraft($roleDraftMock, $policyCreateStructMock); + } + + /** + * Test for the updatePolicyByRoleDraft() method. + */ + public function testUpdatePolicyThrowsLimitationValidationException() + { + $this->expectException(LimitationValidationException::class); + + $limitationMock = $this->createMock(RoleLimitation::class); + $limitationTypeMock = $this->createMock(SPIType::class); + + $limitationTypeMock->expects($this->once()) + ->method('acceptValue') + ->with($this->equalTo($limitationMock)); + $limitationTypeMock->expects($this->once()) + ->method('validate') + ->with($this->equalTo($limitationMock)) + ->will($this->returnValue([42])); + + $limitationMock->expects($this->any()) + ->method('getIdentifier') + ->will($this->returnValue('mockIdentifier')); + + $settings = [ + 'policyMap' => ['mockModule' => ['mockFunction' => ['mockIdentifier' => true]]], + 'limitationTypes' => ['mockIdentifier' => $limitationTypeMock], + ]; + + $roleServiceMock = $this->getPartlyMockedRoleService(['loadRole'], $settings); + + $roleDraftMock = $this->createMock(RoleDraft::class); + $policyDraftMock = $this->createMock(PolicyDraft::class); + $policyUpdateStructMock = $this->createMock(PolicyUpdateStruct::class); + + $policyDraftMock->expects($this->any()) + ->method('__get') + ->will( + $this->returnCallback( + static function ($propertyName) { + switch ($propertyName) { + case 'module': + return 'mockModule'; + case 'function': + return 'mockFunction'; + } + + return null; + } + ) + ); + + /* @var \PHPUnit\Framework\MockObject\MockObject $policyCreateStructMock */ + $policyUpdateStructMock->expects($this->once()) + ->method('getLimitations') + ->will($this->returnValue([$limitationMock])); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('update'), + $this->equalTo($roleDraftMock) + )->will($this->returnValue(true)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Policy $policyDraftMock */ + /* @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyUpdateStruct $policyUpdateStructMock */ + $roleServiceMock->updatePolicyByRoleDraft( + $roleDraftMock, + $policyDraftMock, + $policyUpdateStructMock + ); + } + + /** + * Test for the assignRoleToUser() method. + */ + public function testAssignRoleToUserThrowsUnauthorizedException() + { + $this->expectException(UnauthorizedException::class); + + $roleServiceMock = $this->getPartlyMockedRoleService(); + /** @var \Ibexa\Contracts\Core\Repository\Values\User\Role $roleMock */ + $roleMock = $this->createMock(Role::class); + /** @var \Ibexa\Contracts\Core\Repository\Values\User\User $userMock */ + $userMock = $this->createMock(User::class); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('assign'), + $this->equalTo($userMock), + $this->equalTo([$roleMock]) + )->will($this->returnValue(false)); + + $roleServiceMock->assignRoleToUser($roleMock, $userMock, null); + } + + /** + * Test for the assignRoleToUser() method. + */ + public function testAssignRoleToUserThrowsLimitationValidationException() + { + $this->expectException(LimitationValidationException::class); + + $limitationMock = $this->createMock(RoleLimitation::class); + $limitationTypeMock = $this->createMock(SPIType::class); + + $limitationTypeMock->expects($this->once()) + ->method('acceptValue') + ->with($this->equalTo($limitationMock)); + $limitationTypeMock->expects($this->once()) + ->method('validate') + ->with($this->equalTo($limitationMock)) + ->will($this->returnValue([42])); + + $limitationMock->expects($this->once()) + ->method('getIdentifier') + ->will($this->returnValue('testIdentifier')); + + $settings = [ + 'limitationTypes' => ['testIdentifier' => $limitationTypeMock], + ]; + + $roleServiceMock = $this->getPartlyMockedRoleService(null, $settings); + + /** @var \Ibexa\Contracts\Core\Repository\Values\User\Role $roleMock */ + $roleMock = $this->createMock(Role::class); + /** @var \Ibexa\Contracts\Core\Repository\Values\User\User $userMock */ + $userMock = $this->createMock(User::class); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('assign'), + $this->equalTo($userMock), + $this->equalTo([$roleMock]) + )->will($this->returnValue(true)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation $limitationMock */ + $roleServiceMock->assignRoleToUser($roleMock, $userMock, $limitationMock); + } + + /** + * Test for the assignRoleToUser() method. + */ + public function testAssignRoleToUserThrowsBadStateException() + { + $this->expectException(BadStateException::class); + + $roleServiceMock = $this->getPartlyMockedRoleService(); + /** @var \Ibexa\Contracts\Core\Repository\Values\User\Role $roleMock */ + $roleMock = $this->createMock(Role::class); + /** @var \Ibexa\Contracts\Core\Repository\Values\User\User $userMock */ + $userMock = $this->createMock(User::class); + $limitationMock = $this->createMock(RoleLimitation::class); + + $limitationMock->expects($this->once()) + ->method('getIdentifier') + ->will($this->returnValue('testIdentifier')); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('assign'), + $this->equalTo($userMock), + $this->equalTo([$roleMock]) + )->will($this->returnValue(true)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation $limitationMock */ + $roleServiceMock->assignRoleToUser($roleMock, $userMock, $limitationMock); + } + + /** + * Test for the assignRoleToUser() method. + */ + public function testAssignRoleToUser() + { + $limitationMock = $this->createMock(RoleLimitation::class); + $limitationTypeMock = $this->createMock(SPIType::class); + + $limitationTypeMock->expects($this->once()) + ->method('acceptValue') + ->with($this->equalTo($limitationMock)); + $limitationTypeMock->expects($this->once()) + ->method('validate') + ->with($this->equalTo($limitationMock)) + ->will($this->returnValue([])); + + $limitationMock->expects($this->exactly(2)) + ->method('getIdentifier') + ->will($this->returnValue('testIdentifier')); + + $settings = [ + 'limitationTypes' => ['testIdentifier' => $limitationTypeMock], + ]; + + $roleServiceMock = $this->getPartlyMockedRoleService(['checkAssignmentAndFilterLimitationValues'], $settings); + + $repository = $this->getRepositoryMock(); + $roleMock = $this->createMock(Role::class); + $userMock = $this->createMock(User::class); + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + + $userMock->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(24)); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('assign'), + $this->equalTo($userMock), + $this->equalTo([$roleMock]) + )->will($this->returnValue(true)); + + $roleMock->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(42)); + + $userHandlerMock->expects($this->once()) + ->method('loadRole') + ->with($this->equalTo(42)) + ->will($this->returnValue(new SPIRole(['id' => 42]))); + + $userHandlerMock->expects($this->once()) + ->method('load') + ->with($this->equalTo(24)) + ->will($this->returnValue(new SPIUser(['id' => 24]))); + + $roleServiceMock->expects($this->once()) + ->method('checkAssignmentAndFilterLimitationValues') + ->with(24, $this->isInstanceOf(SPIRole::class), ['testIdentifier' => []]) + ->will($this->returnValue(['testIdentifier' => []])); + + $repository->expects($this->once())->method('beginTransaction'); + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + $userHandlerMock->expects($this->once()) + ->method('assignRole') + ->with( + $this->equalTo(24), + $this->equalTo(42), + $this->equalTo(['testIdentifier' => []]) + ); + $repository->expects($this->once())->method('commit'); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Role $roleMock */ + /* @var \Ibexa\Contracts\Core\Repository\Values\User\User $userMock */ + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation $limitationMock */ + $roleServiceMock->assignRoleToUser($roleMock, $userMock, $limitationMock); + } + + /** + * Test for the assignRoleToUser() method. + */ + public function testAssignRoleToUserWithNullLimitation() + { + $repository = $this->getRepositoryMock(); + $roleServiceMock = $this->getPartlyMockedRoleService(['checkAssignmentAndFilterLimitationValues']); + $roleMock = $this->createMock(Role::class); + $userMock = $this->createMock(User::class); + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + + $userMock->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(24)); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('assign'), + $this->equalTo($userMock), + $this->equalTo([$roleMock]) + )->will($this->returnValue(true)); + + $roleMock->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(42)); + + $userHandlerMock->expects($this->once()) + ->method('loadRole') + ->with($this->equalTo(42)) + ->will($this->returnValue(new SPIRole(['id' => 42]))); + + $userHandlerMock->expects($this->once()) + ->method('load') + ->with($this->equalTo(24)) + ->will($this->returnValue(new SPIUser(['id' => 24]))); + + $roleServiceMock->expects($this->once()) + ->method('checkAssignmentAndFilterLimitationValues') + ->with(24, $this->isInstanceOf(SPIRole::class), null) + ->will($this->returnValue(null)); + + $repository->expects($this->once())->method('beginTransaction'); + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + $userHandlerMock->expects($this->once()) + ->method('assignRole') + ->with( + $this->equalTo(24), + $this->equalTo(42), + $this->equalTo(null) + ); + $repository->expects($this->once())->method('commit'); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Role $roleMock */ + /* @var \Ibexa\Contracts\Core\Repository\Values\User\User $userMock */ + $roleServiceMock->assignRoleToUser($roleMock, $userMock, null); + } + + /** + * Test for the assignRoleToUser() method. + */ + public function testAssignRoleToUserWithRollback() + { + $this->expectException(\Exception::class); + + $repository = $this->getRepositoryMock(); + $roleServiceMock = $this->getPartlyMockedRoleService(['checkAssignmentAndFilterLimitationValues']); + $roleMock = $this->createMock(Role::class); + $userMock = $this->createMock(User::class); + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + + $userMock->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(24)); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('assign'), + $this->equalTo($userMock), + $this->equalTo([$roleMock]) + )->will($this->returnValue(true)); + + $roleMock->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(42)); + + $userHandlerMock->expects($this->once()) + ->method('loadRole') + ->with($this->equalTo(42)) + ->will($this->returnValue(new SPIRole(['id' => 42]))); + + $userHandlerMock->expects($this->once()) + ->method('load') + ->with($this->equalTo(24)) + ->will($this->returnValue(new SPIUser(['id' => 24]))); + + $roleServiceMock->expects($this->once()) + ->method('checkAssignmentAndFilterLimitationValues') + ->with(24, $this->isInstanceOf(SPIRole::class), null) + ->will($this->returnValue(null)); + + $repository->expects($this->once())->method('beginTransaction'); + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + $userHandlerMock->expects($this->once()) + ->method('assignRole') + ->with( + $this->equalTo(24), + $this->equalTo(42), + $this->equalTo(null) + )->will($this->throwException(new \Exception())); + $repository->expects($this->once())->method('rollback'); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Role $roleMock */ + /* @var \Ibexa\Contracts\Core\Repository\Values\User\User $userMock */ + $roleServiceMock->assignRoleToUser($roleMock, $userMock, null); + } + + /** + * Test for the assignRoleToUserGroup() method. + */ + public function testAssignRoleToUserGroupThrowsUnauthorizedException() + { + $this->expectException(UnauthorizedException::class); + + $repository = $this->getRepositoryMock(); + $roleServiceMock = $this->getPartlyMockedRoleService(); + /** @var \Ibexa\Contracts\Core\Repository\Values\User\Role $roleMock */ + $roleMock = $this->createMock(Role::class); + /** @var \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroupMock */ + $userGroupMock = $this->createMock(UserGroup::class); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('assign'), + $this->equalTo($userGroupMock), + $this->equalTo([$roleMock]) + )->will($this->returnValue(false)); + + $roleServiceMock->assignRoleToUserGroup($roleMock, $userGroupMock, null); + } + + /** + * Test for the assignRoleToUserGroup() method. + */ + public function testAssignRoleToUserGroupThrowsLimitationValidationException() + { + $this->expectException(LimitationValidationException::class); + + $limitationMock = $this->createMock(RoleLimitation::class); + $limitationTypeMock = $this->createMock(SPIType::class); + + $limitationTypeMock->expects($this->once()) + ->method('acceptValue') + ->with($this->equalTo($limitationMock)); + $limitationTypeMock->expects($this->once()) + ->method('validate') + ->with($this->equalTo($limitationMock)) + ->will($this->returnValue([42])); + + $limitationMock->expects($this->once()) + ->method('getIdentifier') + ->will($this->returnValue('testIdentifier')); + + $settings = [ + 'limitationTypes' => ['testIdentifier' => $limitationTypeMock], + ]; + + $roleServiceMock = $this->getPartlyMockedRoleService(null, $settings); + + $repository = $this->getRepositoryMock(); + /** @var \Ibexa\Contracts\Core\Repository\Values\User\Role $roleMock */ + $roleMock = $this->createMock(Role::class); + /** @var \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroupMock */ + $userGroupMock = $this->createMock(UserGroup::class); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('assign'), + $this->equalTo($userGroupMock), + $this->equalTo([$roleMock]) + )->will($this->returnValue(true)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation $limitationMock */ + $roleServiceMock->assignRoleToUserGroup($roleMock, $userGroupMock, $limitationMock); + } + + /** + * Test for the assignRoleToUserGroup() method. + */ + public function testAssignRoleGroupToUserThrowsBadStateException() + { + $this->expectException(BadStateException::class); + + $repository = $this->getRepositoryMock(); + $roleServiceMock = $this->getPartlyMockedRoleService(); + /** @var \Ibexa\Contracts\Core\Repository\Values\User\Role $roleMock */ + $roleMock = $this->createMock(Role::class); + /** @var \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroupMock */ + $userGroupMock = $this->createMock(UserGroup::class); + $limitationMock = $this->createMock(RoleLimitation::class); + + $limitationMock->expects($this->once()) + ->method('getIdentifier') + ->will($this->returnValue('testIdentifier')); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('assign'), + $this->equalTo($userGroupMock), + $this->equalTo([$roleMock]) + )->will($this->returnValue(true)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation $limitationMock */ + $roleServiceMock->assignRoleToUserGroup($roleMock, $userGroupMock, $limitationMock); + } + + /** + * Test for the assignRoleToUserGroup() method. + */ + public function testAssignRoleToUserGroup() + { + $limitationMock = $this->createMock(RoleLimitation::class); + $limitationTypeMock = $this->createMock(SPIType::class); + + $limitationTypeMock->expects($this->once()) + ->method('acceptValue') + ->with($this->equalTo($limitationMock)); + $limitationTypeMock->expects($this->once()) + ->method('validate') + ->with($this->equalTo($limitationMock)) + ->will($this->returnValue([])); + + $limitationMock->expects($this->exactly(2)) + ->method('getIdentifier') + ->will($this->returnValue('testIdentifier')); + + $settings = [ + 'limitationTypes' => ['testIdentifier' => $limitationTypeMock], + ]; + + $roleServiceMock = $this->getPartlyMockedRoleService(['checkAssignmentAndFilterLimitationValues'], $settings); + + $repository = $this->getRepositoryMock(); + $roleMock = $this->createMock(Role::class); + $userGroupMock = $this->createMock(UserGroup::class); + $userServiceMock = $this->createMock(UserService::class); + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + + $repository->expects($this->once()) + ->method('getUserService') + ->will($this->returnValue($userServiceMock)); + $userGroupMock->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(24)); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('assign'), + $this->equalTo($userGroupMock), + $this->equalTo([$roleMock]) + )->will($this->returnValue(true)); + + $roleMock->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(42)); + + $userHandlerMock->expects($this->once()) + ->method('loadRole') + ->with($this->equalTo(42)) + ->will($this->returnValue(new SPIRole(['id' => 42]))); + + $userServiceMock->expects($this->once()) + ->method('loadUserGroup') + ->with($this->equalTo(24)) + ->will($this->returnValue($userGroupMock)); + + $roleServiceMock->expects($this->once()) + ->method('checkAssignmentAndFilterLimitationValues') + ->with(24, $this->isInstanceOf(SPIRole::class), ['testIdentifier' => []]) + ->will($this->returnValue(['testIdentifier' => []])); + + $repository->expects($this->once())->method('beginTransaction'); + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + $userHandlerMock->expects($this->once()) + ->method('assignRole') + ->with( + $this->equalTo(24), + $this->equalTo(42), + $this->equalTo(['testIdentifier' => []]) + ); + $repository->expects($this->once())->method('commit'); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Role $roleMock */ + /* @var \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroupMock */ + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Limitation\RoleLimitation $limitationMock */ + $roleServiceMock->assignRoleToUserGroup($roleMock, $userGroupMock, $limitationMock); + } + + /** + * Test for the assignRoleToUserGroup() method. + */ + public function testAssignRoleToUserGroupWithNullLimitation() + { + $repository = $this->getRepositoryMock(); + $roleServiceMock = $this->getPartlyMockedRoleService(['checkAssignmentAndFilterLimitationValues']); + $roleMock = $this->createMock(Role::class); + $userGroupMock = $this->createMock(UserGroup::class); + $userServiceMock = $this->createMock(UserService::class); + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + + $repository->expects($this->once()) + ->method('getUserService') + ->will($this->returnValue($userServiceMock)); + $userGroupMock->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(24)); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('assign'), + $this->equalTo($userGroupMock), + $this->equalTo([$roleMock]) + )->will($this->returnValue(true)); + + $roleMock->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(42)); + + $userHandlerMock->expects($this->once()) + ->method('loadRole') + ->with($this->equalTo(42)) + ->will($this->returnValue(new SPIRole(['id' => 42]))); + + $userServiceMock->expects($this->once()) + ->method('loadUserGroup') + ->with($this->equalTo(24)) + ->will($this->returnValue($userGroupMock)); + + $roleServiceMock->expects($this->once()) + ->method('checkAssignmentAndFilterLimitationValues') + ->with(24, $this->isInstanceOf(SPIRole::class), null) + ->will($this->returnValue(null)); + + $repository->expects($this->once())->method('beginTransaction'); + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + $userHandlerMock->expects($this->once()) + ->method('assignRole') + ->with( + $this->equalTo(24), + $this->equalTo(42), + $this->equalTo(null) + ); + $repository->expects($this->once())->method('commit'); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Role $roleMock */ + /* @var \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroupMock */ + $roleServiceMock->assignRoleToUserGroup($roleMock, $userGroupMock, null); + } + + /** + * Test for the assignRoleToUserGroup() method. + */ + public function testAssignRoleToUserGroupWithRollback() + { + $this->expectException(\Exception::class); + + $repository = $this->getRepositoryMock(); + $roleServiceMock = $this->getPartlyMockedRoleService(['checkAssignmentAndFilterLimitationValues']); + $roleMock = $this->createMock(Role::class); + $userGroupMock = $this->createMock(UserGroup::class); + $userServiceMock = $this->createMock(UserService::class); + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + + $repository->expects($this->once()) + ->method('getUserService') + ->will($this->returnValue($userServiceMock)); + $userGroupMock->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(24)); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('assign'), + $this->equalTo($userGroupMock), + $this->equalTo([$roleMock]) + )->will($this->returnValue(true)); + + $roleMock->expects($this->any()) + ->method('__get') + ->with('id') + ->will($this->returnValue(42)); + + $userHandlerMock->expects($this->once()) + ->method('loadRole') + ->with($this->equalTo(42)) + ->will($this->returnValue(new SPIRole(['id' => 42]))); + + $userServiceMock->expects($this->once()) + ->method('loadUserGroup') + ->with($this->equalTo(24)) + ->will($this->returnValue($userGroupMock)); + + $roleServiceMock->expects($this->once()) + ->method('checkAssignmentAndFilterLimitationValues') + ->with(24, $this->isInstanceOf(SPIRole::class), null) + ->will($this->returnValue(null)); + + $repository->expects($this->once())->method('beginTransaction'); + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + $userHandlerMock->expects($this->once()) + ->method('assignRole') + ->with( + $this->equalTo(24), + $this->equalTo(42), + $this->equalTo(null) + )->will($this->throwException(new \Exception())); + $repository->expects($this->once())->method('rollback'); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Role $roleMock */ + /* @var \Ibexa\Contracts\Core\Repository\Values\User\UserGroup $userGroupMock */ + $roleServiceMock->assignRoleToUserGroup($roleMock, $userGroupMock, null); + } + + public function testRemovePolicyByRoleDraftThrowsUnauthorizedException() + { + $this->expectException(UnauthorizedException::class); + + $roleDraftMock = $this->createMock(RoleDraft::class); + $roleDomainMapper = $this->createMock(RoleDomainMapper::class); + $roleDomainMapper + ->method('buildDomainRoleObject') + ->willReturn($roleDraftMock); + + $roleServiceMock = $this->getPartlyMockedRoleService(null, [], $roleDomainMapper); + $policyDraftMock = $this->createMock(PolicyDraft::class); + + $policyDraftMock->expects($this->any()) + ->method('__get') + ->will( + $this->returnValueMap( + [ + ['roleId', 17], + ] + ) + ); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('update'), + $this->equalTo($roleDraftMock) + )->will($this->returnValue(false)); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Policy $policyMock */ + $roleServiceMock->removePolicyByRoleDraft($roleDraftMock, $policyDraftMock); + } + + /** + * Test for the removePolicyByRoleDraft() method. + */ + public function testRemovePolicyByRoleDraftWithRollback() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Handler threw an exception'); + + $repository = $this->getRepositoryMock(); + $roleDraftMock = $this->createMock(RoleDraft::class); + $roleDraftMock->expects($this->any()) + ->method('__get') + ->with('id') + ->willReturn(17); + + $roleDomainMapper = $this->createMock(RoleDomainMapper::class); + $roleDomainMapper + ->method('buildDomainRoleObject') + ->willReturn($roleDraftMock); + $roleServiceMock = $this->getPartlyMockedRoleService(null, [], $roleDomainMapper); + + $policyDraftMock = $this->createMock(PolicyDraft::class); + $policyDraftMock->expects($this->any()) + ->method('__get') + ->will( + $this->returnValueMap( + [ + ['id', 42], + ['roleId', 17], + ] + ) + ); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('update'), + $this->equalTo($roleDraftMock) + )->will($this->returnValue(true)); + + $repository->expects($this->once())->method('beginTransaction'); + + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + + $userHandlerMock->expects($this->once()) + ->method('deletePolicy') + ->with( + $this->equalTo(42) + )->will($this->throwException(new \Exception('Handler threw an exception'))); + + $repository->expects($this->once())->method('rollback'); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\Policy $policyDraftMock */ + $roleServiceMock->removePolicyByRoleDraft($roleDraftMock, $policyDraftMock); + } + + public function testRemovePolicyByRoleDraft() + { + $repository = $this->getRepositoryMock(); + $roleDraftMock = $this->createMock(RoleDraft::class); + $roleDraftMock + ->expects($this->any()) + ->method('__get') + ->with('id') + ->willReturn(17); + + $roleDomainMapper = $this->createMock(RoleDomainMapper::class); + $roleDomainMapper + ->method('buildDomainRoleObject') + ->willReturn($roleDraftMock); + + $roleServiceMock = $this->getPartlyMockedRoleService(['loadRoleDraft'], [], $roleDomainMapper); + + $policyDraftMock = $this->createMock(PolicyDraft::class); + $policyDraftMock->expects($this->any()) + ->method('__get') + ->will( + $this->returnValueMap( + [ + ['id', 42], + ['roleId', 17], + ] + ) + ); + + $permissionResolverMock = $this->getPermissionResolverMock(); + $permissionResolverMock->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('role'), + $this->equalTo('update'), + $this->equalTo($roleDraftMock) + )->will($this->returnValue(true)); + + $repository->expects($this->once())->method('beginTransaction'); + + $userHandlerMock = $this->getPersistenceMockHandler('User\\Handler'); + + $userHandlerMock->expects($this->once()) + ->method('deletePolicy') + ->with( + $this->equalTo(42) + ); + + $roleServiceMock->expects($this->once()) + ->method('loadRoleDraft') + ->with($this->equalTo(17)) + ->will($this->returnValue($roleDraftMock)); + + $repository->expects($this->once())->method('commit'); + + /* @var \Ibexa\Contracts\Core\Repository\Values\User\PolicyDraft $policyDraftMock */ + $roleServiceMock->removePolicyByRoleDraft($roleDraftMock, $policyDraftMock); + } + + /** @var \Ibexa\Core\Repository\RoleService */ + protected $partlyMockedRoleService; + + /** + * Returns the role service to test with $methods mocked. + * + * Injected Repository comes from {@see getRepositoryMock()} and persistence handler from {@see getPersistenceMock()} + * + * @param string[] $methods + * @param array $settings + * @param \Ibexa\Core\Repository\Mapper\RoleDomainMapper|null $roleDomainMapper + * + * @return \Ibexa\Core\Repository\RoleService|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getPartlyMockedRoleService( + array $methods = null, + array $settings = [], + ?RoleDomainMapper $roleDomainMapper = null + ) { + if (!isset($this->partlyMockedRoleService) || !empty($settings) || $roleDomainMapper) { + $limitationService = new LimitationService( + new ArrayIterator($settings['limitationTypes'] ?? []) + ); + if ($roleDomainMapper === null) { + $roleDomainMapper = $this->getMockBuilder(RoleDomainMapper::class) + ->setMethods([]) + ->setConstructorArgs([$limitationService]) + ->getMock(); + } + + $this->partlyMockedRoleService = $this->getMockBuilder(RoleService::class) + ->setMethods($methods) + ->setConstructorArgs( + [ + $this->getRepositoryMock(), + $this->getPersistenceMockHandler('User\\Handler'), + $limitationService, + $roleDomainMapper, + $settings, + ] + ) + ->getMock(); + } + + return $this->partlyMockedRoleService; + } + + /** + * @return \Ibexa\Contracts\Core\Repository\Repository|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getRepositoryMock(): Repository + { + $repositoryMock = parent::getRepositoryMock(); + $repositoryMock + ->expects($this->any()) + ->method('getPermissionResolver') + ->willReturn($this->getPermissionResolverMock()); + + return $repositoryMock; + } +} + +class_alias(RoleTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\RoleTest'); diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/SearchTest.php b/tests/lib/Repository/Service/Mock/SearchTest.php similarity index 87% rename from eZ/Publish/Core/Repository/Tests/Service/Mock/SearchTest.php rename to tests/lib/Repository/Service/Mock/SearchTest.php index 606da1ecaf..848c431a57 100644 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/SearchTest.php +++ b/tests/lib/Repository/Service/Mock/SearchTest.php @@ -4,26 +4,27 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; +namespace Ibexa\Tests\Core\Repository\Service\Mock; use Exception; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\Content\Location; -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Content\Search\SearchHit; -use eZ\Publish\API\Repository\Values\Content\Search\SearchResult; -use eZ\Publish\Core\Repository\ContentService; -use eZ\Publish\Core\Repository\Permission\PermissionCriterionResolver; -use eZ\Publish\Core\Repository\SearchService; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\Core\Search\Common\BackgroundIndexer; -use eZ\Publish\Core\Search\Common\BackgroundIndexer\NullIndexer; -use eZ\Publish\SPI\Persistence\Content\ContentInfo as SPIContentInfo; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo as SPIContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\Location as SPILocation; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Core\Repository\ContentService; +use Ibexa\Core\Repository\Permission\PermissionCriterionResolver; +use Ibexa\Core\Repository\SearchService; +use Ibexa\Core\Search\Common\BackgroundIndexer; +use Ibexa\Core\Search\Common\BackgroundIndexer\NullIndexer; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; /** * Mock test case for Search service. @@ -39,12 +40,12 @@ class SearchTest extends BaseServiceMockTest /** * Test for the __construct() method. * - * @covers \eZ\Publish\Core\Repository\SearchService::__construct + * @covers \Ibexa\Contracts\Core\Repository\SearchService::__construct */ public function testConstructor() { $repositoryMock = $this->getRepositoryMock(); - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $contentDomainMapperMock = $this->getContentDomainMapperMock(); $permissionsCriterionResolverMock = $this->getPermissionCriterionResolverMock(); @@ -95,10 +96,10 @@ public function providerForFindContentValidatesLocationCriteriaAndSortClauses() */ public function testFindContentValidatesLocationCriteriaAndSortClauses($query, $exceptionMessage) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $repositoryMock = $this->getRepositoryMock(); - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $permissionsCriterionResolverMock = $this->getPermissionCriterionResolverMock(); @@ -144,10 +145,10 @@ public function providerForFindSingleValidatesLocationCriteria() */ public function testFindSingleValidatesLocationCriteria($criterion, $exceptionMessage) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $repositoryMock = $this->getRepositoryMock(); - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $permissionsCriterionResolverMock = $this->getPermissionCriterionResolverMock(); $service = new SearchService( @@ -172,8 +173,8 @@ public function testFindSingleValidatesLocationCriteria($criterion, $exceptionMe /** * Test for the findContent() method. * - * @covers \eZ\Publish\Core\Repository\SearchService::addPermissionsCriterion - * @covers \eZ\Publish\Core\Repository\SearchService::findContent + * @covers \Ibexa\Contracts\Core\Repository\SearchService::addPermissionsCriterion + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent */ public function testFindContentThrowsHandlerException() { @@ -181,7 +182,7 @@ public function testFindContentThrowsHandlerException() $this->expectExceptionMessage('Handler threw an exception'); $repositoryMock = $this->getRepositoryMock(); - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $permissionsCriterionResolverMock = $this->getPermissionCriterionResolverMock(); @@ -194,7 +195,7 @@ public function testFindContentThrowsHandlerException() [] ); - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterionMock */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterionMock */ $criterionMock = $this ->getMockBuilder(Criterion::class) ->disableOriginalConstructor() @@ -212,7 +213,7 @@ public function testFindContentThrowsHandlerException() /** * Test for the findContent() method when search is out of sync with persistence. * - * @covers \eZ\Publish\Core\Repository\SearchService::findContent + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent */ public function testFindContentWhenDomainMapperThrowsException() { @@ -257,12 +258,12 @@ public function testFindContentWhenDomainMapperThrowsException() /** * Test for the findContent() method. * - * @covers \eZ\Publish\Core\Repository\SearchService::addPermissionsCriterion - * @covers \eZ\Publish\Core\Repository\SearchService::findContent + * @covers \Ibexa\Contracts\Core\Repository\SearchService::addPermissionsCriterion + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent */ public function testFindContentNoPermissionsFilter() { - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $repositoryMock = $this->getRepositoryMock(); $service = new SearchService( @@ -322,12 +323,12 @@ public function testFindContentNoPermissionsFilter() /** * Test for the findContent() method. * - * @covers \eZ\Publish\Core\Repository\SearchService::addPermissionsCriterion - * @covers \eZ\Publish\Core\Repository\SearchService::findContent + * @covers \Ibexa\Contracts\Core\Repository\SearchService::addPermissionsCriterion + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent */ public function testFindContentWithPermission() { - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $domainMapperMock = $this->getContentDomainMapperMock(); $permissionsCriterionResolverMock = $this->getPermissionCriterionResolverMock(); @@ -395,12 +396,12 @@ public function testFindContentWithPermission() /** * Test for the findContent() method. * - * @covers \eZ\Publish\Core\Repository\SearchService::addPermissionsCriterion - * @covers \eZ\Publish\Core\Repository\SearchService::findContent + * @covers \Ibexa\Contracts\Core\Repository\SearchService::addPermissionsCriterion + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findContent */ public function testFindContentWithNoPermission() { - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $permissionsCriterionResolverMock = $this->getPermissionCriterionResolverMock(); $service = new SearchService( @@ -444,7 +445,7 @@ public function testFindContentWithNoPermission() */ public function testFindContentWithDefaultQueryValues() { - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $domainMapperMock = $this->getContentDomainMapperMock(); $service = new SearchService( @@ -510,15 +511,15 @@ public function testFindContentWithDefaultQueryValues() /** * Test for the findSingle() method. * - * @covers \eZ\Publish\Core\Repository\SearchService::addPermissionsCriterion - * @covers \eZ\Publish\Core\Repository\SearchService::findSingle + * @covers \Ibexa\Contracts\Core\Repository\SearchService::addPermissionsCriterion + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findSingle */ public function testFindSingleThrowsNotFoundException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $repositoryMock = $this->getRepositoryMock(); - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $service = new SearchService( $repositoryMock, @@ -529,7 +530,7 @@ public function testFindSingleThrowsNotFoundException() [] ); - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterionMock */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterionMock */ $criterionMock = $this ->getMockBuilder(Criterion::class) ->disableOriginalConstructor() @@ -546,8 +547,8 @@ public function testFindSingleThrowsNotFoundException() /** * Test for the findSingle() method. * - * @covers \eZ\Publish\Core\Repository\SearchService::addPermissionsCriterion - * @covers \eZ\Publish\Core\Repository\SearchService::findSingle + * @covers \Ibexa\Contracts\Core\Repository\SearchService::addPermissionsCriterion + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findSingle */ public function testFindSingleThrowsHandlerException() { @@ -555,7 +556,7 @@ public function testFindSingleThrowsHandlerException() $this->expectExceptionMessage('Handler threw an exception'); $repositoryMock = $this->getRepositoryMock(); - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $permissionsCriterionResolverMock = $this->getPermissionCriterionResolverMock(); $service = new SearchService( @@ -567,7 +568,7 @@ public function testFindSingleThrowsHandlerException() [] ); - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterionMock */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterionMock */ $criterionMock = $this ->getMockBuilder(Criterion::class) ->disableOriginalConstructor() @@ -584,13 +585,13 @@ public function testFindSingleThrowsHandlerException() /** * Test for the findSingle() method. * - * @covers \eZ\Publish\Core\Repository\SearchService::addPermissionsCriterion - * @covers \eZ\Publish\Core\Repository\SearchService::findSingle + * @covers \Ibexa\Contracts\Core\Repository\SearchService::addPermissionsCriterion + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findSingle */ public function testFindSingle() { $repositoryMock = $this->getRepositoryMock(); - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $domainMapperMock = $this->getContentDomainMapperMock(); $permissionsCriterionResolverMock = $this->getPermissionCriterionResolverMock(); @@ -615,7 +616,7 @@ public function testFindSingle() ) ); - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterionMock */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterionMock */ $criterionMock = $this ->getMockBuilder(Criterion::class) ->disableOriginalConstructor() @@ -656,7 +657,7 @@ public function testFindSingle() public function testFindLocationsWithPermission() { $repositoryMock = $this->getRepositoryMock(); - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $domainMapperMock = $this->getContentDomainMapperMock(); $permissionsCriterionResolverMock = $this->getPermissionCriterionResolverMock(); @@ -727,7 +728,7 @@ public function testFindLocationsWithPermission() public function testFindLocationsWithNoPermissionsFilter() { $repositoryMock = $this->getRepositoryMock(); - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $domainMapperMock = $this->getContentDomainMapperMock(); $permissionsCriterionResolverMock = $this->getPermissionCriterionResolverMock(); @@ -791,7 +792,7 @@ public function testFindLocationsWithNoPermissionsFilter() /** * Test for the findLocations() method when search is out of sync with persistence. * - * @covers \eZ\Publish\Core\Repository\SearchService::findLocations + * @covers \Ibexa\Contracts\Core\Repository\SearchService::findLocations */ public function testFindLocationsBackgroundIndexerWhenDomainMapperThrowsException() { @@ -847,7 +848,7 @@ public function testFindLocationsThrowsHandlerException() $this->expectExceptionMessage('Handler threw an exception'); $repositoryMock = $this->getRepositoryMock(); - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $permissionsCriterionResolverMock = $this->getPermissionCriterionResolverMock(); @@ -860,7 +861,7 @@ public function testFindLocationsThrowsHandlerException() [] ); - /** @var \eZ\Publish\API\Repository\Values\Content\Query\Criterion $criterionMock */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion $criterionMock */ $criterionMock = $this ->getMockBuilder(Criterion::class) ->disableOriginalConstructor() @@ -881,7 +882,7 @@ public function testFindLocationsThrowsHandlerException() public function testFindLocationsWithDefaultQueryValues() { $repositoryMock = $this->getRepositoryMock(); - /** @var \eZ\Publish\SPI\Search\Handler $searchHandlerMock */ + /** @var \Ibexa\Contracts\Core\Search\Handler $searchHandlerMock */ $searchHandlerMock = $this->getSPIMockHandler('Search\\Handler'); $domainMapperMock = $this->getContentDomainMapperMock(); $service = new SearchService( @@ -944,7 +945,7 @@ public function testFindLocationsWithDefaultQueryValues() } /** - * @return \PHPUnit\Framework\MockObject\MockObject|\eZ\Publish\API\Repository\PermissionCriterionResolver + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Contracts\Core\Repository\PermissionCriterionResolver */ protected function getPermissionCriterionResolverMock() { @@ -958,3 +959,5 @@ protected function getPermissionCriterionResolverMock() return $this->permissionsCriterionResolverMock; } } + +class_alias(SearchTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\SearchTest'); diff --git a/tests/lib/Repository/Service/Mock/UrlAliasTest.php b/tests/lib/Repository/Service/Mock/UrlAliasTest.php new file mode 100644 index 0000000000..c077982d38 --- /dev/null +++ b/tests/lib/Repository/Service/Mock/UrlAliasTest.php @@ -0,0 +1,3416 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Service\Mock; + +use Exception; +use Ibexa\Contracts\Core\Persistence\Content\UrlAlias as SPIUrlAlias; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException as ApiNotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\LanguageResolver; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; +use Ibexa\Core\Base\Exceptions\ForbiddenException; +use Ibexa\Core\Base\Exceptions\NotFoundException; +use Ibexa\Core\Repository\Helper\NameSchemaService; +use Ibexa\Core\Repository\LocationService; +use Ibexa\Core\Repository\URLAliasService; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; + +/** + * Mock test case for UrlAlias Service. + */ +class UrlAliasTest extends BaseServiceMockTest +{ + private const EXAMPLE_ID = 'eznode:42'; + private const EXAMPLE_LANGUAGE_CODE = 'pol-PL'; + private const EXAMPLE_PATH = 'folder/article'; + private const EXAMPLE_OFFSET = 10; + private const EXAMPLE_LIMIT = 100; + + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ + private $permissionResolver; + + /** @var \Ibexa\Contracts\Core\Persistence\Content\UrlAlias\Handler|\PHPUnit\Framework\MockObject\MockObject */ + private $urlAliasHandler; + + protected function setUp(): void + { + parent::setUp(); + $this->urlAliasHandler = $this->getPersistenceMockHandler('Content\\UrlAlias\\Handler'); + $this->permissionResolver = $this->getPermissionResolverMock(); + } + + /** + * Test for the __construct() method. + */ + public function testConstructor() + { + $repositoryMock = $this->getRepositoryMock(); + + new UrlALiasService( + $repositoryMock, + $this->urlAliasHandler, + $this->getNameSchemaServiceMock(), + $this->permissionResolver, + $this->getLanguageResolverMock() + ); + } + + /** + * Test for the load() method. + */ + public function testLoad() + { + $mockedService = $this->getPartlyMockedURLAliasServiceService(['extractPath']); + /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ + $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); + + $urlAliasHandlerMock + ->expects($this->once()) + ->method('loadUrlAlias') + ->with(self::EXAMPLE_ID) + ->willReturn(new SPIUrlAlias()); + + $mockedService + ->expects($this->once()) + ->method('extractPath') + ->with($this->isInstanceOf(SPIUrlAlias::class), null) + ->willReturn('path'); + + $urlAlias = $mockedService->load(self::EXAMPLE_ID); + + self::assertInstanceOf(URLAlias::class, $urlAlias); + } + + /** + * Test for the load() method. + */ + public function testLoadThrowsNotFoundException() + { + $mockedService = $this->getPartlyMockedURLAliasServiceService(['extractPath']); + /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ + $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); + + $urlAliasHandlerMock + ->expects($this->once()) + ->method('loadUrlAlias') + ->with(self::EXAMPLE_ID) + ->will($this->throwException(new NotFoundException('UrlAlias', self::EXAMPLE_ID))); + + $this->expectException(ApiNotFoundException::class); + $mockedService->load(self::EXAMPLE_ID); + } + + protected function getSpiUrlAlias() + { + $pathElement1 = [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ]; + $pathElement2 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ]; + $pathElement3 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + 'ger-DE' => 'drei', + ], + ]; + + return new SPIUrlAlias( + [ + 'id' => '3', + 'pathData' => [$pathElement1, $pathElement2, $pathElement3], + 'languageCodes' => ['ger-DE'], + 'alwaysAvailable' => false, + ] + ); + } + + /** + * Test for the load() method. + */ + public function testLoadThrowsNotFoundExceptionPath() + { + $spiUrlAlias = $this->getSpiUrlAlias(); + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + ['fre-FR'] + ); + + $this->urlAliasHandler + ->expects($this->once()) + ->method('loadUrlAlias') + ->with(self::EXAMPLE_ID) + ->willReturn($spiUrlAlias); + + $this->expectException(ApiNotFoundException::class); + + $urlAliasService->load(self::EXAMPLE_ID); + } + + /** + * Test for the removeAliases() method. + */ + public function testRemoveAliasesThrowsInvalidArgumentException() + { + $aliasList = [new URLAlias(['isCustom' => false])]; + $mockedService = $this->getPartlyMockedURLAliasServiceService(); + $this->permissionResolver + ->expects($this->once()) + ->method('hasAccess')->with( + $this->equalTo('content'), + $this->equalTo('urltranslator') + ) + ->willReturn(true); + + $this->expectException(InvalidArgumentException::class); + + $mockedService->removeAliases($aliasList); + } + + /** + * Test for the removeAliases() method. + */ + public function testRemoveAliases() + { + $aliasList = [new URLAlias(['isCustom' => true])]; + $spiAliasList = [new SPIUrlAlias(['isCustom' => true])]; + $this->permissionResolver + ->expects($this->once()) + ->method('hasAccess')->with( + $this->equalTo('content'), + $this->equalTo('urltranslator') + )->willReturn(true); + + $repositoryMock = $this->getRepositoryMock(); + + $mockedService = $this->getPartlyMockedURLAliasServiceService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ + $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); + + $repositoryMock + ->expects($this->once()) + ->method('beginTransaction'); + $repositoryMock + ->expects($this->once()) + ->method('commit'); + + $urlAliasHandlerMock + ->expects($this->once()) + ->method('removeURLAliases') + ->with($spiAliasList); + + $mockedService->removeAliases($aliasList); + } + + /** + * Test for the removeAliases() method. + */ + public function testRemoveAliasesWithRollback() + { + $aliasList = [new URLAlias(['isCustom' => true])]; + $spiAliasList = [new SPIUrlAlias(['isCustom' => true])]; + $this->permissionResolver + ->expects($this->once()) + ->method('hasAccess')->with( + $this->equalTo('content'), + $this->equalTo('urltranslator') + )->willReturn(true); + + $repositoryMock = $this->getRepositoryMock(); + + $mockedService = $this->getPartlyMockedURLAliasServiceService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ + $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); + + $repositoryMock + ->expects($this->once()) + ->method('beginTransaction'); + $repositoryMock + ->expects($this->once()) + ->method('rollback'); + + $urlAliasHandlerMock + ->expects($this->once()) + ->method('removeURLAliases') + ->with($spiAliasList) + ->will($this->throwException(new Exception('Handler threw an exception'))); + + $this->expectException(Exception::class); + $this->expectExceptionMessage('Handler threw an exception'); + + $mockedService->removeAliases($aliasList); + } + + public function providerForTestListAutogeneratedLocationAliasesPath() + { + $pathElement1 = [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ]; + $pathElement2 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ]; + $pathElement3 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + 'ger-DE' => 'drei', + ], + ]; + $pathData1 = [$pathElement1]; + $pathData2 = [$pathElement1, $pathElement2]; + $pathData3 = [$pathElement1, $pathElement2, $pathElement3]; + $spiUrlAliases1 = [ + new SPIUrlAlias( + [ + 'id' => '1', + 'pathData' => $pathData1, + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => true, + ] + ), + ]; + $spiUrlAliases2 = [ + new SPIUrlAlias( + [ + 'id' => '1', + 'pathData' => $pathData2, + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + ] + ), + new SPIUrlAlias( + [ + 'id' => '2', + 'pathData' => $pathData2, + 'languageCodes' => ['eng-GB'], + 'alwaysAvailable' => false, + ] + ), + ]; + $spiUrlAliases3 = [ + new SPIUrlAlias( + [ + 'id' => '1', + 'pathData' => $pathData3, + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + ] + ), + new SPIUrlAlias( + [ + 'id' => '2', + 'pathData' => $pathData3, + 'languageCodes' => ['eng-GB'], + 'alwaysAvailable' => false, + ] + ), + new SPIUrlAlias( + [ + 'id' => '3', + 'pathData' => $pathData3, + 'languageCodes' => ['ger-DE'], + 'alwaysAvailable' => false, + ] + ), + ]; + + return [ + [ + $spiUrlAliases1, + ['cro-HR'], + [ + 'cro-HR' => '/jedan', + ], + 'cro-HR', + ], + [ + $spiUrlAliases1, + ['eng-GB'], + [ + 'cro-HR' => '/jedan', + ], + 'cro-HR', + ], + [ + $spiUrlAliases1, + ['ger-DE'], + [ + 'cro-HR' => '/jedan', + ], + 'cro-HR', + ], + [ + $spiUrlAliases1, + ['cro-HR', 'eng-GB', 'ger-DE'], + [ + 'cro-HR' => '/jedan', + ], + 'cro-HR', + ], + [ + $spiUrlAliases2, + ['cro-HR'], + [ + 'cro-HR' => '/jedan/dva', + ], + 'cro-HR', + ], + [ + $spiUrlAliases2, + ['eng-GB'], + [ + 'eng-GB' => '/jedan/two', + ], + 'eng-GB', + ], + [ + $spiUrlAliases2, + ['cro-HR', 'eng-GB'], + [ + 'cro-HR' => '/jedan/dva', + 'eng-GB' => '/jedan/two', + ], + 'cro-HR', + ], + [ + $spiUrlAliases2, + ['cro-HR', 'ger-DE'], + [ + 'cro-HR' => '/jedan/dva', + ], + 'cro-HR', + ], + [ + $spiUrlAliases2, + ['eng-GB', 'cro-HR'], + [ + 'eng-GB' => '/jedan/two', + 'cro-HR' => '/jedan/dva', + ], + 'eng-GB', + ], + [ + $spiUrlAliases2, + ['eng-GB', 'ger-DE'], + [ + 'eng-GB' => '/jedan/two', + ], + 'eng-GB', + ], + [ + $spiUrlAliases2, + ['ger-DE', 'cro-HR'], + [ + 'cro-HR' => '/jedan/dva', + ], + 'cro-HR', + ], + [ + $spiUrlAliases2, + ['ger-DE', 'eng-GB'], + [ + 'eng-GB' => '/jedan/two', + ], + 'eng-GB', + ], + [ + $spiUrlAliases2, + ['cro-HR', 'eng-GB', 'ger-DE'], + [ + 'cro-HR' => '/jedan/dva', + 'eng-GB' => '/jedan/two', + ], + 'cro-HR', + ], + [ + $spiUrlAliases2, + ['cro-HR', 'ger-DE', 'eng-GB'], + [ + 'cro-HR' => '/jedan/dva', + 'eng-GB' => '/jedan/two', + ], + 'cro-HR', + ], + [ + $spiUrlAliases2, + ['eng-GB', 'cro-HR', 'ger-DE'], + [ + 'eng-GB' => '/jedan/two', + 'cro-HR' => '/jedan/dva', + ], + 'eng-GB', + ], + [ + $spiUrlAliases2, + ['eng-GB', 'ger-DE', 'cro-HR'], + [ + 'eng-GB' => '/jedan/two', + 'cro-HR' => '/jedan/dva', + ], + 'eng-GB', + ], + [ + $spiUrlAliases2, + ['ger-DE', 'cro-HR', 'eng-GB'], + [ + 'cro-HR' => '/jedan/dva', + 'eng-GB' => '/jedan/two', + ], + 'cro-HR', + ], + [ + $spiUrlAliases2, + ['ger-DE', 'eng-GB', 'cro-HR'], + [ + 'eng-GB' => '/jedan/two', + 'cro-HR' => '/jedan/dva', + ], + 'eng-GB', + ], + [ + $spiUrlAliases3, + ['cro-HR'], + [ + 'cro-HR' => '/jedan/dva/tri', + ], + 'cro-HR', + ], + [ + $spiUrlAliases3, + ['eng-GB'], + [ + 'eng-GB' => '/jedan/two/three', + ], + 'eng-GB', + ], + [ + $spiUrlAliases3, + ['cro-HR', 'eng-GB'], + [ + 'cro-HR' => '/jedan/dva/tri', + 'eng-GB' => '/jedan/dva/three', + ], + 'cro-HR', + ], + [ + $spiUrlAliases3, + ['cro-HR', 'ger-DE'], + [ + 'cro-HR' => '/jedan/dva/tri', + 'ger-DE' => '/jedan/dva/drei', + ], + 'cro-HR', + ], + [ + $spiUrlAliases3, + ['eng-GB', 'cro-HR'], + [ + 'eng-GB' => '/jedan/two/three', + 'cro-HR' => '/jedan/two/tri', + ], + 'eng-GB', + ], + [ + $spiUrlAliases3, + ['eng-GB', 'ger-DE'], + [ + 'eng-GB' => '/jedan/two/three', + 'ger-DE' => '/jedan/two/drei', + ], + 'eng-GB', + ], + [ + $spiUrlAliases3, + ['ger-DE', 'eng-GB'], + [ + 'ger-DE' => '/jedan/two/drei', + 'eng-GB' => '/jedan/two/three', + ], + 'ger-DE', + ], + [ + $spiUrlAliases3, + ['ger-DE', 'cro-HR'], + [ + 'ger-DE' => '/jedan/dva/drei', + 'cro-HR' => '/jedan/dva/tri', + ], + 'ger-DE', + ], + [ + $spiUrlAliases3, + ['cro-HR', 'eng-GB', 'ger-DE'], + [ + 'cro-HR' => '/jedan/dva/tri', + 'eng-GB' => '/jedan/dva/three', + 'ger-DE' => '/jedan/dva/drei', + ], + 'cro-HR', + ], + [ + $spiUrlAliases3, + ['cro-HR', 'ger-DE', 'eng-GB'], + [ + 'cro-HR' => '/jedan/dva/tri', + 'ger-DE' => '/jedan/dva/drei', + 'eng-GB' => '/jedan/dva/three', + ], + 'cro-HR', + ], + [ + $spiUrlAliases3, + ['eng-GB', 'cro-HR', 'ger-DE'], + [ + 'eng-GB' => '/jedan/two/three', + 'cro-HR' => '/jedan/two/tri', + 'ger-DE' => '/jedan/two/drei', + ], + 'eng-GB', + ], + [ + $spiUrlAliases3, + ['eng-GB', 'ger-DE', 'cro-HR'], + [ + 'eng-GB' => '/jedan/two/three', + 'ger-DE' => '/jedan/two/drei', + 'cro-HR' => '/jedan/two/tri', + ], + 'eng-GB', + ], + [ + $spiUrlAliases3, + ['ger-DE', 'cro-HR', 'eng-GB'], + [ + 'ger-DE' => '/jedan/dva/drei', + 'cro-HR' => '/jedan/dva/tri', + 'eng-GB' => '/jedan/dva/three', + ], + 'ger-DE', + ], + [ + $spiUrlAliases3, + ['ger-DE', 'eng-GB', 'cro-HR'], + [ + 'ger-DE' => '/jedan/two/drei', + 'eng-GB' => '/jedan/two/three', + 'cro-HR' => '/jedan/two/tri', + ], + 'ger-DE', + ], + ]; + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesPath + */ + public function testListAutogeneratedLocationAliasesPath($spiUrlAliases, $prioritizedLanguageCodes, $paths) + { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageCodes, + ); + + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases($location, false, null); + + self::assertEquals( + \count($paths), + \count($urlAliases) + ); + + foreach ($urlAliases as $index => $urlAlias) { + $pathKeys = array_keys($paths); + self::assertEquals( + $paths[$pathKeys[$index]], + $urlAlias->path + ); + self::assertEquals( + [$pathKeys[$index]], + $urlAlias->languageCodes + ); + } + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesPath + */ + public function testListAutogeneratedLocationAliasesPathCustomConfiguration( + $spiUrlAliases, + $prioritizedLanguageCodes, + $paths + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + [] + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases( + $location, + false, + null, + false, + $prioritizedLanguageCodes + ); + + self::assertEquals( + \count($paths), + \count($urlAliases) + ); + + foreach ($urlAliases as $index => $urlAlias) { + $pathKeys = array_keys($paths); + self::assertEquals( + $paths[$pathKeys[$index]], + $urlAlias->path + ); + self::assertEquals( + [$pathKeys[$index]], + $urlAlias->languageCodes + ); + } + } + + /** + * Test for the load() method. + */ + public function testListLocationAliasesWithShowAllTranslations() + { + $pathElement1 = [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ]; + $pathElement2 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ]; + $pathElement3 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + 'ger-DE' => 'drei', + ], + ]; + $spiUrlAlias = new SPIUrlAlias( + [ + 'id' => '3', + 'pathData' => [$pathElement1, $pathElement2, $pathElement3], + 'languageCodes' => ['ger-DE'], + 'alwaysAvailable' => false, + ] + ); + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + ['fre-FR'], + true + ); + + $this->urlAliasHandler + ->expects($this->once()) + ->method('listURLAliasesForLocation') + ->with( + $this->equalTo(42), + $this->equalTo(false) + ) + ->willReturn([$spiUrlAlias]); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases($location, false, null); + + self::assertCount(1, $urlAliases); + self::assertInstanceOf(URLAlias::class, $urlAliases[0]); + self::assertEquals('/jedan/dva/tri', $urlAliases[0]->path); + } + + /** + * Test for the load() method. + */ + public function testListLocationAliasesWithShowAllTranslationsCustomConfiguration() + { + $pathElement1 = [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ]; + $pathElement2 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ]; + $pathElement3 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + 'ger-DE' => 'drei', + ], + ]; + $spiUrlAlias = new SPIUrlAlias( + [ + 'id' => '3', + 'pathData' => [$pathElement1, $pathElement2, $pathElement3], + 'languageCodes' => ['ger-DE'], + 'alwaysAvailable' => false, + ] + ); + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + [] + ); + + $this->urlAliasHandler + ->expects($this->once()) + ->method('listURLAliasesForLocation') + ->with( + $this->equalTo(42), + $this->equalTo(false) + ) + ->willReturn([$spiUrlAlias]); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases( + $location, + false, + null, + true, + ['fre-FR'] + ); + + self::assertCount(1, $urlAliases); + self::assertInstanceOf(URLAlias::class, $urlAliases[0]); + self::assertEquals('/jedan/dva/tri', $urlAliases[0]->path); + } + + public function providerForTestListAutogeneratedLocationAliasesEmpty() + { + $pathElement1 = [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => '/jedan', + ], + ]; + $pathElement2 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ]; + $pathElement3 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + 'ger-DE' => 'drei', + ], + ]; + $pathData2 = [$pathElement1, $pathElement2]; + $pathData3 = [$pathElement1, $pathElement2, $pathElement3]; + $spiUrlAliases2 = [ + new SPIUrlAlias( + [ + 'pathData' => $pathData2, + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + ] + ), + new SPIUrlAlias( + [ + 'pathData' => $pathData2, + 'languageCodes' => ['eng-GB'], + 'alwaysAvailable' => false, + ] + ), + ]; + $spiUrlAliases3 = [ + new SPIUrlAlias( + [ + 'pathData' => $pathData3, + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + ] + ), + new SPIUrlAlias( + [ + 'pathData' => $pathData3, + 'languageCodes' => ['eng-GB'], + 'alwaysAvailable' => false, + ] + ), + new SPIUrlAlias( + [ + 'pathData' => $pathData3, + 'languageCodes' => ['ger-DE'], + 'alwaysAvailable' => false, + ] + ), + ]; + + return [ + [ + $spiUrlAliases2, + ['ger-DE'], + ], + [ + $spiUrlAliases3, + ['ger-DE'], + ], + ]; + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesEmpty + */ + public function testListAutogeneratedLocationAliasesEmpty($spiUrlAliases, $prioritizedLanguageCodes) + { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageCodes + ); + + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases($location, false, null); + + self::assertEmpty($urlAliases); + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesEmpty + */ + public function testListAutogeneratedLocationAliasesEmptyCustomConfiguration( + $spiUrlAliases, + $prioritizedLanguageCodes + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + [] + ); + + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases( + $location, + false, + null, + false, + $prioritizedLanguageCodes + ); + + self::assertEmpty($urlAliases); + } + + public function providerForTestListAutogeneratedLocationAliasesWithLanguageCodePath() + { + $pathElement1 = [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => 'jedan', + ], + ]; + $pathElement2 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ]; + $pathElement3 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + 'ger-DE' => 'drei', + ], + ]; + $pathData1 = [$pathElement1]; + $pathData2 = [$pathElement1, $pathElement2]; + $pathData3 = [$pathElement1, $pathElement2, $pathElement3]; + $spiUrlAliases1 = [ + new SPIUrlAlias( + [ + 'pathData' => $pathData1, + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => true, + ] + ), + ]; + $spiUrlAliases2 = [ + new SPIUrlAlias( + [ + 'pathData' => $pathData2, + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + ] + ), + new SPIUrlAlias( + [ + 'pathData' => $pathData2, + 'languageCodes' => ['eng-GB'], + 'alwaysAvailable' => false, + ] + ), + ]; + $spiUrlAliases3 = [ + new SPIUrlAlias( + [ + 'pathData' => $pathData3, + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + ] + ), + new SPIUrlAlias( + [ + 'pathData' => $pathData3, + 'languageCodes' => ['eng-GB'], + 'alwaysAvailable' => false, + ] + ), + new SPIUrlAlias( + [ + 'pathData' => $pathData3, + 'languageCodes' => ['ger-DE'], + 'alwaysAvailable' => false, + ] + ), + ]; + + return [ + [ + $spiUrlAliases1, + 'cro-HR', + ['cro-HR'], + [ + '/jedan', + ], + ], + [ + $spiUrlAliases1, + 'cro-HR', + ['eng-GB'], + [ + '/jedan', + ], + ], + [ + $spiUrlAliases2, + 'cro-HR', + ['cro-HR'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases2, + 'eng-GB', + ['eng-GB'], + [ + '/jedan/two', + ], + ], + [ + $spiUrlAliases2, + 'eng-GB', + ['cro-HR', 'eng-GB'], + [ + '/jedan/two', + ], + ], + [ + $spiUrlAliases2, + 'cro-HR', + ['cro-HR', 'ger-DE'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases2, + 'cro-HR', + ['eng-GB', 'cro-HR'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases2, + 'eng-GB', + ['eng-GB', 'ger-DE'], + [ + '/jedan/two', + ], + ], + [ + $spiUrlAliases2, + 'cro-HR', + ['ger-DE', 'cro-HR'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases2, + 'eng-GB', + ['ger-DE', 'eng-GB'], + [ + '/jedan/two', + ], + ], + [ + $spiUrlAliases2, + 'cro-HR', + ['cro-HR', 'eng-GB', 'ger-DE'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases2, + 'eng-GB', + ['cro-HR', 'ger-DE', 'eng-GB'], + [ + '/jedan/two', + ], + ], + [ + $spiUrlAliases2, + 'cro-HR', + ['eng-GB', 'cro-HR', 'ger-DE'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases2, + 'cro-HR', + ['eng-GB', 'ger-DE', 'cro-HR'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases2, + 'cro-HR', + ['ger-DE', 'cro-HR', 'eng-GB'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases2, + 'cro-HR', + ['ger-DE', 'eng-GB', 'cro-HR'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases3, + 'cro-HR', + ['cro-HR'], + [ + '/jedan/dva/tri', + ], + ], + [ + $spiUrlAliases3, + 'eng-GB', + ['eng-GB'], + [ + '/jedan/two/three', + ], + ], + [ + $spiUrlAliases3, + 'eng-GB', + ['cro-HR', 'eng-GB'], + [ + '/jedan/dva/three', + ], + ], + [ + $spiUrlAliases3, + 'ger-DE', + ['cro-HR', 'ger-DE'], + [ + '/jedan/dva/drei', + ], + ], + [ + $spiUrlAliases3, + 'cro-HR', + ['eng-GB', 'cro-HR'], + [ + '/jedan/two/tri', + ], + ], + [ + $spiUrlAliases3, + 'ger-DE', + ['eng-GB', 'ger-DE'], + [ + '/jedan/two/drei', + ], + ], + [ + $spiUrlAliases3, + 'eng-GB', + ['ger-DE', 'eng-GB'], + [ + '/jedan/two/three', + ], + ], + [ + $spiUrlAliases3, + 'ger-DE', + ['ger-DE', 'cro-HR'], + [ + '/jedan/dva/drei', + ], + ], + [ + $spiUrlAliases3, + 'ger-DE', + ['cro-HR', 'eng-GB', 'ger-DE'], + [ + '/jedan/dva/drei', + ], + ], + [ + $spiUrlAliases3, + 'ger-DE', + ['cro-HR', 'ger-DE', 'eng-GB'], + [ + '/jedan/dva/drei', + ], + ], + [ + $spiUrlAliases3, + 'ger-DE', + ['eng-GB', 'cro-HR', 'ger-DE'], + [ + '/jedan/two/drei', + ], + ], + [ + $spiUrlAliases3, + 'ger-DE', + ['eng-GB', 'ger-DE', 'cro-HR'], + [ + '/jedan/two/drei', + ], + ], + [ + $spiUrlAliases3, + 'eng-GB', + ['ger-DE', 'cro-HR', 'eng-GB'], + [ + '/jedan/dva/three', + ], + ], + [ + $spiUrlAliases3, + 'cro-HR', + ['ger-DE', 'eng-GB', 'cro-HR'], + [ + '/jedan/two/tri', + ], + ], + ]; + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodePath + */ + public function testListAutogeneratedLocationAliasesWithLanguageCodePath( + $spiUrlAliases, + $languageCode, + $prioritizedLanguageCodes, + $paths + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageCodes + ); + + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases($location, false, $languageCode); + + self::assertEquals( + \count($paths), + \count($urlAliases) + ); + + foreach ($urlAliases as $index => $urlAlias) { + self::assertEquals( + $paths[$index], + $urlAlias->path + ); + } + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodePath + */ + public function testListAutogeneratedLocationAliasesWithLanguageCodePathCustomConfiguration( + $spiUrlAliases, + $languageCode, + $prioritizedLanguageCodes, + $paths + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + [] + ); + + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases( + $location, + false, + $languageCode, + false, + $prioritizedLanguageCodes + ); + + self::assertEquals( + \count($paths), + \count($urlAliases) + ); + + foreach ($urlAliases as $index => $urlAlias) { + self::assertEquals( + $paths[$index], + $urlAlias->path + ); + } + } + + public function providerForTestListAutogeneratedLocationAliasesWithLanguageCodeEmpty() + { + $pathElement1 = [ + 'always-available' => true, + 'translations' => [ + 'cro-HR' => '/jedan', + ], + ]; + $pathElement2 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ]; + $pathElement3 = [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'tri', + 'eng-GB' => 'three', + 'ger-DE' => 'drei', + ], + ]; + $pathData1 = [$pathElement1]; + $pathData2 = [$pathElement1, $pathElement2]; + $pathData3 = [$pathElement1, $pathElement2, $pathElement3]; + $spiUrlAliases1 = [ + new SPIUrlAlias( + [ + 'pathData' => $pathData1, + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => true, + ] + ), + ]; + $spiUrlAliases2 = [ + new SPIUrlAlias( + [ + 'pathData' => $pathData2, + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + ] + ), + new SPIUrlAlias( + [ + 'pathData' => $pathData2, + 'languageCodes' => ['eng-GB'], + 'alwaysAvailable' => false, + ] + ), + ]; + $spiUrlAliases3 = [ + new SPIUrlAlias( + [ + 'pathData' => $pathData3, + 'languageCodes' => ['cro-HR'], + 'alwaysAvailable' => false, + ] + ), + new SPIUrlAlias( + [ + 'pathData' => $pathData3, + 'languageCodes' => ['eng-GB'], + 'alwaysAvailable' => false, + ] + ), + new SPIUrlAlias( + [ + 'pathData' => $pathData3, + 'languageCodes' => ['ger-DE'], + 'alwaysAvailable' => false, + ] + ), + ]; + + return [ + [ + $spiUrlAliases1, + 'eng-GB', + ['ger-DE'], + ], + [ + $spiUrlAliases1, + 'ger-DE', + ['cro-HR', 'eng-GB', 'ger-DE'], + ], + [ + $spiUrlAliases2, + 'eng-GB', + ['cro-HR'], + ], + [ + $spiUrlAliases2, + 'ger-DE', + ['cro-HR', 'eng-GB'], + ], + [ + $spiUrlAliases2, + 'ger-DE', + ['cro-HR', 'ger-DE'], + ], + [ + $spiUrlAliases2, + 'ger-DE', + ['eng-GB', 'ger-DE'], + ], + [ + $spiUrlAliases2, + 'ger-DE', + ['ger-DE', 'cro-HR'], + ], + [ + $spiUrlAliases2, + 'ger-DE', + ['ger-DE', 'eng-GB'], + ], + [ + $spiUrlAliases2, + 'ger-DE', + ['cro-HR', 'eng-GB', 'ger-DE'], + ], + [ + $spiUrlAliases2, + 'ger-DE', + ['cro-HR', 'ger-DE', 'eng-GB'], + ], + [ + $spiUrlAliases2, + 'ger-DE', + ['eng-GB', 'cro-HR', 'ger-DE'], + ], + [ + $spiUrlAliases2, + 'ger-DE', + ['eng-GB', 'ger-DE', 'cro-HR'], + ], + [ + $spiUrlAliases2, + 'ger-DE', + ['ger-DE', 'cro-HR', 'eng-GB'], + ], + [ + $spiUrlAliases2, + 'ger-DE', + ['ger-DE', 'eng-GB', 'cro-HR'], + ], + [ + $spiUrlAliases3, + 'ger-DE', + ['cro-HR'], + ], + [ + $spiUrlAliases3, + 'cro-HR', + ['eng-GB'], + ], + [ + $spiUrlAliases3, + 'ger-DE', + ['cro-HR', 'eng-GB'], + ], + [ + $spiUrlAliases3, + 'eng-GB', + ['cro-HR', 'ger-DE'], + ], + [ + $spiUrlAliases3, + 'ger-DE', + ['eng-GB', 'cro-HR'], + ], + [ + $spiUrlAliases3, + 'cro-HR', + ['eng-GB', 'ger-DE'], + ], + [ + $spiUrlAliases3, + 'cro-HR', + ['ger-DE', 'eng-GB'], + ], + [ + $spiUrlAliases3, + 'eng-GB', + ['ger-DE', 'cro-HR'], + ], + ]; + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeEmpty + */ + public function testListAutogeneratedLocationAliasesWithLanguageCodeEmpty( + $spiUrlAliases, + $languageCode, + $prioritizedLanguageCodes + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageCodes + ); + + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases($location, false, $languageCode); + + self::assertEmpty($urlAliases); + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeEmpty + */ + public function testListAutogeneratedLocationAliasesWithLanguageCodeEmptyCustomConfiguration( + $spiUrlAliases, + $languageCode, + $prioritizedLanguageCodes + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + [] + ); + + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases( + $location, + false, + $languageCode, + false, + $prioritizedLanguageCodes + ); + + self::assertEmpty($urlAliases); + } + + public function providerForTestListAutogeneratedLocationAliasesMultipleLanguagesPath() + { + $spiUrlAliases = [ + new SPIUrlAlias( + [ + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + 'eng-GB' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'dva', + 'ger-DE' => 'dva', + ], + ], + ], + 'languageCodes' => ['eng-GB', 'ger-DE'], + 'alwaysAvailable' => false, + ] + ), + ]; + + return [ + [ + $spiUrlAliases, + ['cro-HR', 'ger-DE'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases, + ['ger-DE', 'cro-HR'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases, + ['eng-GB'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases, + ['eng-GB', 'ger-DE', 'cro-HR'], + [ + '/jedan/dva', + ], + ], + ]; + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesMultipleLanguagesPath + */ + public function testListAutogeneratedLocationAliasesMultipleLanguagesPath($spiUrlAliases, $prioritizedLanguageCodes, $paths) + { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageCodes + ); + + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases($location, false, null); + + self::assertEquals( + \count($paths), + \count($urlAliases) + ); + + foreach ($urlAliases as $index => $urlAlias) { + self::assertEquals( + $paths[$index], + $urlAlias->path + ); + } + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesMultipleLanguagesPath + */ + public function testListAutogeneratedLocationAliasesMultipleLanguagesPathCustomConfiguration( + $spiUrlAliases, + $prioritizedLanguageCodes, + $paths + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + [] + ); + + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases( + $location, + false, + null, + false, + $prioritizedLanguageCodes + ); + + self::assertEquals( + \count($paths), + \count($urlAliases) + ); + + foreach ($urlAliases as $index => $urlAlias) { + self::assertEquals( + $paths[$index], + $urlAlias->path + ); + } + } + + public function providerForTestListAutogeneratedLocationAliasesMultipleLanguagesEmpty() + { + $spiUrlAliases = [ + new SPIUrlAlias( + [ + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => '/jedan', + 'eng-GB' => '/jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'dva', + 'ger-DE' => 'dva', + ], + ], + ], + 'languageCodes' => ['eng-GB', 'ger-DE'], + 'alwaysAvailable' => false, + ] + ), + ]; + + return [ + [ + $spiUrlAliases, + ['cro-HR'], + ], + [ + $spiUrlAliases, + ['ger-DE'], + ], + ]; + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesMultipleLanguagesEmpty + */ + public function testListAutogeneratedLocationAliasesMultipleLanguagesEmpty($spiUrlAliases, $prioritizedLanguageCodes) + { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageCodes + ); + + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases($location, false, null); + + self::assertEmpty($urlAliases); + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesMultipleLanguagesEmpty + */ + public function testListAutogeneratedLocationAliasesMultipleLanguagesEmptyCustomConfiguration( + $spiUrlAliases, + $prioritizedLanguageCodes + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + [] + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases( + $location, + false, + null, + false, + $prioritizedLanguageCodes + ); + + self::assertEmpty($urlAliases); + } + + public function providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath() + { + $spiUrlAliases = [ + new SPIUrlAlias( + [ + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + 'eng-GB' => 'jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'dva', + 'ger-DE' => 'dva', + ], + ], + ], + 'languageCodes' => ['eng-GB', 'ger-DE'], + 'alwaysAvailable' => false, + ] + ), + ]; + + return [ + [ + $spiUrlAliases, + 'ger-DE', + ['cro-HR', 'ger-DE'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases, + 'ger-DE', + ['ger-DE', 'cro-HR'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases, + 'eng-GB', + ['eng-GB'], + [ + '/jedan/dva', + ], + ], + [ + $spiUrlAliases, + 'eng-GB', + ['eng-GB', 'ger-DE', 'cro-HR'], + [ + '/jedan/dva', + ], + ], + ]; + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath + */ + public function testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath( + $spiUrlAliases, + $languageCode, + $prioritizedLanguageCodes, + $paths + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageCodes + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases($location, false, $languageCode); + + self::assertEquals( + \count($paths), + \count($urlAliases) + ); + + foreach ($urlAliases as $index => $urlAlias) { + self::assertEquals( + $paths[$index], + $urlAlias->path + ); + } + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPath + */ + public function testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesPathCustomConfiguration( + $spiUrlAliases, + $languageCode, + $prioritizedLanguageCodes, + $paths + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + [] + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases( + $location, + false, + $languageCode, + false, + $prioritizedLanguageCodes + ); + + self::assertEquals( + \count($paths), + \count($urlAliases) + ); + + foreach ($urlAliases as $index => $urlAlias) { + self::assertEquals( + $paths[$index], + $urlAlias->path + ); + } + } + + public function providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmpty() + { + $spiUrlAliases = [ + new SPIUrlAlias( + [ + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => '/jedan', + 'eng-GB' => '/jedan', + ], + ], + [ + 'always-available' => false, + 'translations' => [ + 'eng-GB' => 'dva', + 'ger-DE' => 'dva', + ], + ], + ], + 'languageCodes' => ['eng-GB', 'ger-DE'], + 'alwaysAvailable' => false, + ] + ), + ]; + + return [ + [ + $spiUrlAliases, + 'cro-HR', + ['cro-HR'], + ], + [ + $spiUrlAliases, + 'cro-HR', + ['cro-HR', 'eng-GB'], + ], + [ + $spiUrlAliases, + 'cro-HR', + ['ger-DE'], + ], + [ + $spiUrlAliases, + 'cro-HR', + ['cro-HR', 'eng-GB', 'ger-DE'], + ], + ]; + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmpty + */ + public function testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmpty( + $spiUrlAliases, + $languageCode, + $prioritizedLanguageCodes + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageCodes + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases($location, false, $languageCode); + + self::assertEmpty($urlAliases); + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmpty + */ + public function testListAutogeneratedLocationAliasesWithLanguageCodeMultipleLanguagesEmptyCustomConfiguration( + $spiUrlAliases, + $languageCode, + $prioritizedLanguageCodes + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + [] + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases( + $location, + false, + $languageCode, + false, + $prioritizedLanguageCodes + ); + + self::assertEmpty($urlAliases); + } + + public function providerForTestListAutogeneratedLocationAliasesAlwaysAvailablePath() + { + $spiUrlAliases = [ + new SPIUrlAlias( + [ + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + 'eng-GB' => 'one', + ], + ], + [ + 'always-available' => true, + 'translations' => [ + 'ger-DE' => 'zwei', + ], + ], + ], + 'languageCodes' => ['ger-DE'], + 'alwaysAvailable' => true, + ] + ), + ]; + + return [ + [ + $spiUrlAliases, + ['cro-HR', 'ger-DE'], + [ + '/jedan/zwei', + ], + ], + [ + $spiUrlAliases, + ['ger-DE', 'cro-HR'], + [ + '/jedan/zwei', + ], + ], + [ + $spiUrlAliases, + ['eng-GB'], + [ + '/one/zwei', + ], + ], + [ + $spiUrlAliases, + ['cro-HR', 'eng-GB', 'ger-DE'], + [ + '/jedan/zwei', + ], + ], + [ + $spiUrlAliases, + ['eng-GB', 'ger-DE', 'cro-HR'], + [ + '/one/zwei', + ], + ], + ]; + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesAlwaysAvailablePath + */ + public function testListAutogeneratedLocationAliasesAlwaysAvailablePath( + $spiUrlAliases, + $prioritizedLanguageCodes, + $paths + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageCodes + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases($location, false, null); + + self::assertEquals( + \count($paths), + \count($urlAliases) + ); + + foreach ($urlAliases as $index => $urlAlias) { + self::assertEquals( + $paths[$index], + $urlAlias->path + ); + } + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesAlwaysAvailablePath + */ + public function testListAutogeneratedLocationAliasesAlwaysAvailablePathCustomConfiguration( + $spiUrlAliases, + $prioritizedLanguageCodes, + $paths + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + [] + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases( + $location, + false, + null, + false, + $prioritizedLanguageCodes + ); + + self::assertEquals( + \count($paths), + \count($urlAliases) + ); + + foreach ($urlAliases as $index => $urlAlias) { + self::assertEquals( + $paths[$index], + $urlAlias->path + ); + } + } + + public function providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath() + { + $spiUrlAliases = [ + new SPIUrlAlias( + [ + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + 'eng-GB' => 'one', + ], + ], + [ + 'always-available' => true, + 'translations' => [ + 'ger-DE' => 'zwei', + ], + ], + ], + 'languageCodes' => ['ger-DE'], + 'alwaysAvailable' => true, + ] + ), + ]; + + return [ + [ + $spiUrlAliases, + 'ger-DE', + ['cro-HR', 'ger-DE'], + [ + '/jedan/zwei', + ], + ], + [ + $spiUrlAliases, + 'ger-DE', + ['ger-DE', 'cro-HR'], + [ + '/jedan/zwei', + ], + ], + ]; + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath + */ + public function testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath( + $spiUrlAliases, + $languageCode, + $prioritizedLanguageCodes, + $paths + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageCodes + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases($location, false, $languageCode); + + self::assertEquals( + \count($paths), + \count($urlAliases) + ); + + foreach ($urlAliases as $index => $urlAlias) { + self::assertEquals( + $paths[$index], + $urlAlias->path + ); + } + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePath + */ + public function testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailablePathCustomConfiguration( + $spiUrlAliases, + $languageCode, + $prioritizedLanguageCodes, + $paths + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + [] + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases( + $location, + false, + $languageCode, + false, + $prioritizedLanguageCodes + ); + + self::assertEquals( + \count($paths), + \count($urlAliases) + ); + + foreach ($urlAliases as $index => $urlAlias) { + self::assertEquals( + $paths[$index], + $urlAlias->path + ); + } + } + + public function providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmpty() + { + $spiUrlAliases = [ + new SPIUrlAlias( + [ + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'jedan', + 'eng-GB' => 'one', + ], + ], + [ + 'always-available' => true, + 'translations' => [ + 'ger-DE' => 'zwei', + ], + ], + ], + 'languageCodes' => ['ger-DE'], + 'alwaysAvailable' => true, + ] + ), + ]; + + return [ + [ + $spiUrlAliases, + 'eng-GB', + ['eng-GB'], + ], + [ + $spiUrlAliases, + 'eng-GB', + ['cro-HR', 'eng-GB', 'ger-DE'], + ], + [ + $spiUrlAliases, + 'eng-GB', + ['eng-GB', 'ger-DE', 'cro-HR'], + ], + ]; + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmpty + */ + public function testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmpty( + $spiUrlAliases, + $languageCode, + $prioritizedLanguageCodes + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageCodes + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases($location, false, $languageCode); + + self::assertEmpty($urlAliases); + } + + /** + * Test for the listLocationAliases() method. + * + * @dataProvider providerForTestListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmpty + */ + public function testListAutogeneratedLocationAliasesWithLanguageCodeAlwaysAvailableEmptyCustomConfiguration( + $spiUrlAliases, + $languageCode, + $prioritizedLanguageCodes + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + [] + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAliases = $urlAliasService->listLocationAliases( + $location, + false, + $languageCode, + false, + $prioritizedLanguageCodes + ); + + self::assertEmpty($urlAliases); + } + + /** + * Test for the listGlobalAliases() method. + */ + public function testListGlobalAliases() + { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + ['ger-DE'], + true + ); + + $this->urlAliasHandler->expects( + $this->once() + )->method( + 'listGlobalURLAliases' + )->with( + $this->equalTo(null), + $this->equalTo(0), + $this->equalTo(-1) + )->willReturn( + [ + new SPIUrlAlias( + [ + 'pathData' => [ + [ + 'always-available' => true, + 'translations' => [ + 'ger-DE' => 'squirrel', + ], + ], + ], + 'languageCodes' => ['ger-DE'], + 'alwaysAvailable' => true, + ] + ), + ] + ); + + $urlAliases = $urlAliasService->listGlobalAliases(); + + self::assertCount(1, $urlAliases); + self::assertInstanceOf(URLAlias::class, $urlAliases[0]); + } + + /** + * Test for the listGlobalAliases() method. + */ + public function testListGlobalAliasesEmpty() + { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService(); + + $this->urlAliasHandler->expects( + $this->once() + )->method( + 'listGlobalURLAliases' + )->with( + $this->equalTo(null), + $this->equalTo(0), + $this->equalTo(-1) + )->willReturn( + [ + new SPIUrlAlias( + [ + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => [ + 'ger-DE' => 'squirrel', + ], + ], + ], + 'languageCodes' => ['ger-DE'], + 'alwaysAvailable' => false, + ] + ), + ] + ); + + $urlAliases = $urlAliasService->listGlobalAliases(); + + self::assertCount(0, $urlAliases); + } + + /** + * Test for the listGlobalAliases() method. + */ + public function testListGlobalAliasesWithParameters() + { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService(); + + $this->urlAliasHandler->expects( + $this->once() + )->method( + 'listGlobalURLAliases' + )->with( + $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), + $this->equalTo(self::EXAMPLE_OFFSET), + $this->equalTo(self::EXAMPLE_LIMIT) + )->willReturn( + [] + ); + + $urlAliases = $urlAliasService->listGlobalAliases( + self::EXAMPLE_LANGUAGE_CODE, + self::EXAMPLE_OFFSET, + self::EXAMPLE_LIMIT + ); + + self::assertEmpty($urlAliases); + } + + /** + * Test for the lookup() method. + */ + public function testLookupThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $urlAliasService = $this->getPartlyMockedURLAliasServiceService(); + + $this->urlAliasHandler->expects( + $this->once() + )->method( + 'lookup' + )->with( + $this->equalTo('url') + )->will( + $this->throwException(new NotFoundException('UrlAlias', 'url')) + ); + + $urlAliasService->lookup('url'); + } + + public function providerForTestLookupThrowsNotFoundExceptionPath() + { + return [ + // alias does not exist in requested language + ['ein/dva', ['cro-HR', 'ger-DE'], 'ger-DE'], + // alias exists in requested language but the language is not in prioritized languages list + ['ein/dva', ['ger-DE'], 'eng-GB'], + // alias path is not matched + ['jedan/dva', ['cro-HR', 'ger-DE'], 'cro-HR'], + // path is not loadable for prioritized languages list + ['ein/dva', ['cro-HR'], 'cro-HR'], + ]; + } + + /** + * Test for the lookup() method. + * + * @dataProvider providerForTestLookupThrowsNotFoundExceptionPath + */ + public function testLookupThrowsNotFoundExceptionPathNotMatchedOrNotLoadable($url, $prioritizedLanguageList, $languageCode) + { + $this->expectException(NotFoundException::class); + + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageList + ); + + $this->urlAliasHandler->expects( + $this->once() + )->method( + 'lookup' + )->with( + $this->equalTo($url) + )->willReturn( + new SPIUrlAlias( + [ + 'pathData' => [ + [ + 'always-available' => false, + 'translations' => ['ger-DE' => 'ein'], + ], + [ + 'always-available' => false, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ], + ], + 'languageCodes' => ['eng-GB', 'cro-HR'], + 'alwaysAvailable' => false, + ] + ) + ); + + $urlAliasService->lookup($url, $languageCode); + } + + public function providerForTestLookup() + { + return [ + // showAllTranslations setting is true + [['ger-DE'], true, false, null], + // alias is always available + [['ger-DE'], false, true, null], + // works with available language code + [['cro-HR'], false, false, 'eng-GB'], + ]; + } + + /** + * Test for the lookup() method. + * + * @dataProvider providerForTestLookup + */ + public function testLookup($prioritizedLanguageList, $showAllTranslations, $alwaysAvailable, $languageCode) + { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageList, + $showAllTranslations + ); + + $this->urlAliasHandler->expects( + $this->once() + )->method( + 'lookup' + )->with( + $this->equalTo('jedan/dva') + )->willReturn( + new SPIUrlAlias( + [ + 'pathData' => [ + [ + 'always-available' => $alwaysAvailable, + 'translations' => ['cro-HR' => 'jedan'], + ], + [ + 'always-available' => $alwaysAvailable, + 'translations' => [ + 'cro-HR' => 'dva', + 'eng-GB' => 'two', + ], + ], + ], + 'languageCodes' => ['eng-GB', 'cro-HR'], + 'alwaysAvailable' => $alwaysAvailable, + ] + ) + ); + + $urlAlias = $urlAliasService->lookup('jedan/dva', $languageCode); + + self::assertInstanceOf( + URLAlias::class, + $urlAlias + ); + } + + public function providerForTestLookupWithSharedTranslation() + { + return [ + // showAllTranslations setting is true + [['ger-DE'], true, false, null], + // alias is always available + [['ger-DE'], false, true, null], + // works with available language codes + [['cro-HR'], false, false, 'eng-GB'], + [['eng-GB'], false, false, 'cro-HR'], + // works with cro-HR only + [['cro-HR'], false, false, null], + // works with eng-GB only + [['eng-GB'], false, false, null], + // works with cro-HR first + [['cro-HR', 'eng-GB'], false, false, null], + // works with eng-GB first + [['eng-GB', 'cro-HR'], false, false, null], + ]; + } + + /** + * Test for the lookup() method. + * + * @dataProvider providerForTestLookupWithSharedTranslation + */ + public function testLookupWithSharedTranslation( + $prioritizedLanguageList, + $showAllTranslations, + $alwaysAvailable, + $languageCode + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageList, + $showAllTranslations + ); + + $this->urlAliasHandler->expects( + $this->once() + )->method( + 'lookup' + )->with( + $this->equalTo('jedan/two') + )->willReturn( + new SPIUrlAlias( + [ + 'pathData' => [ + [ + 'always-available' => $alwaysAvailable, + 'translations' => [ + 'cro-HR' => 'jedan', + 'eng-GB' => 'jedan', + ], + ], + [ + 'always-available' => $alwaysAvailable, + 'translations' => [ + 'cro-HR' => 'two', + 'eng-GB' => 'two', + ], + ], + ], + 'languageCodes' => ['eng-GB', 'cro-HR'], + 'alwaysAvailable' => $alwaysAvailable, + ] + ) + ); + + $urlAlias = $urlAliasService->lookup('jedan/two', $languageCode); + + self::assertInstanceOf(URLAlias::class, $urlAlias); + } + + /** + * Test for the reverseLookup() method. + */ + public function testReverseLookupCustomConfiguration() + { + $this->expectException(NotFoundException::class); + + $mockedService = $this->getPartlyMockedURLAliasServiceService(['listLocationAliases']); + $location = $this->getLocationStub(); + $mockedService->expects( + $this->once() + )->method( + 'listLocationAliases' + )->with( + $this->equalTo($location), + $this->equalTo(false), + $this->equalTo(null), + $this->equalTo($showAllTranslations = true), + $this->equalTo($prioritizedLanguageList = ['LANGUAGES!']) + )->willReturn( + [] + ); + + $mockedService->reverseLookup($location, null, $showAllTranslations, $prioritizedLanguageList); + } + + /** + * Test for the reverseLookup() method. + */ + public function testReverseLookupThrowsNotFoundException() + { + $this->expectException(NotFoundException::class); + + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + ['listLocationAliases'], + ['ger-DE'] + ); + + $languageCode = 'eng-GB'; + $location = $this->getLocationStub(); + + $urlAliasService->expects( + $this->once() + )->method( + 'listLocationAliases' + )->with( + $this->equalTo($location), + $this->equalTo(false), + $this->equalTo($languageCode) + )->willReturn( + [ + new UrlAlias( + [ + 'languageCodes' => ['eng-GB'], + 'alwaysAvailable' => false, + ] + ), + ] + ); + + $urlAliasService->reverseLookup($location, $languageCode); + } + + public function providerForTestReverseLookup() + { + return $this->providerForTestListAutogeneratedLocationAliasesPath(); + } + + /** + * Test for the reverseLookup() method. + * + * @dataProvider providerForTestReverseLookup + */ + public function testReverseLookupPath($spiUrlAliases, $prioritizedLanguageCodes, $paths, $reverseLookupLanguageCode) + { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageCodes + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAlias = $urlAliasService->reverseLookup($location); + + self::assertEquals( + [$reverseLookupLanguageCode], + $urlAlias->languageCodes + ); + self::assertEquals( + $paths[$reverseLookupLanguageCode], + $urlAlias->path + ); + } + + public function providerForTestReverseLookupAlwaysAvailablePath() + { + return $this->providerForTestListAutogeneratedLocationAliasesAlwaysAvailablePath(); + } + + /** + * Test for the reverseLookup() method. + * + * @dataProvider providerForTestReverseLookupAlwaysAvailablePath + */ + public function testReverseLookupAlwaysAvailablePath( + $spiUrlAliases, + $prioritizedLanguageCodes, + $paths + ) { + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + $prioritizedLanguageCodes + ); + $this->configureListURLAliasesForLocation($spiUrlAliases); + + $location = $this->getLocationStub(); + $urlAlias = $urlAliasService->reverseLookup($location); + + self::assertEquals( + reset($paths), + $urlAlias->path + ); + } + + /** + * Test for the reverseLookup() method. + */ + public function testReverseLookupWithShowAllTranslations() + { + $spiUrlAlias = $this->getSpiUrlAlias(); + $urlAliasService = $this->getPartlyMockedURLAliasServiceService( + null, + ['fre-FR'], + true + ); + $this->configureListURLAliasesForLocation([$spiUrlAlias]); + + $location = $this->getLocationStub(); + $urlAlias = $urlAliasService->reverseLookup($location); + + self::assertEquals('/jedan/dva/tri', $urlAlias->path); + } + + /** + * Test for the createUrlAlias() method. + */ + public function testCreateUrlAlias() + { + $location = $this->getLocationStub(); + $this->permissionResolver + ->expects($this->once()) + ->method('canUser')->with( + $this->equalTo('content'), + $this->equalTo('urltranslator'), + $this->equalTo($location) + ) + ->willReturn(true); + + $repositoryMock = $this->getRepositoryMock(); + + $mockedService = $this->getPartlyMockedURLAliasServiceService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ + $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); + + $repositoryMock + ->expects($this->once()) + ->method('beginTransaction'); + $repositoryMock + ->expects($this->once()) + ->method('commit'); + + $urlAliasHandlerMock->expects( + $this->once() + )->method( + 'createCustomUrlAlias' + )->with( + $this->equalTo($location->id), + $this->equalTo(self::EXAMPLE_PATH), + $this->equalTo(true), + $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), + $this->equalTo(true) + )->willReturn( + new SPIUrlAlias() + ); + + $urlAlias = $mockedService->createUrlAlias( + $location, + self::EXAMPLE_PATH, + self::EXAMPLE_LANGUAGE_CODE, + true, + true + ); + + self::assertInstanceOf(URLAlias::class, $urlAlias); + } + + /** + * Test for the createUrlAlias() method. + */ + public function testCreateUrlAliasWithRollback() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Handler threw an exception'); + + $location = $this->getLocationStub(); + + $this->permissionResolver + ->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('urltranslator'), + $this->equalTo($location) + ) + ->willReturn(true); + + $repositoryMock = $this->getRepositoryMock(); + + $mockedService = $this->getPartlyMockedURLAliasServiceService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ + $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); + + $repositoryMock + ->expects($this->once()) + ->method('beginTransaction'); + $repositoryMock + ->expects($this->once()) + ->method('rollback'); + + $urlAliasHandlerMock->expects( + $this->once() + )->method( + 'createCustomUrlAlias' + )->with( + $this->equalTo($location->id), + $this->equalTo(self::EXAMPLE_PATH), + $this->equalTo(true), + $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), + $this->equalTo(true) + )->will( + $this->throwException(new Exception('Handler threw an exception')) + ); + + $mockedService->createUrlAlias( + $location, + self::EXAMPLE_PATH, + self::EXAMPLE_LANGUAGE_CODE, + true, + true + ); + } + + /** + * Test for the createUrlAlias() method. + */ + public function testCreateUrlAliasThrowsInvalidArgumentException() + { + $this->expectException(InvalidArgumentException::class); + + $location = $this->getLocationStub(); + + $mockedService = $this->getPartlyMockedURLAliasServiceService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $handlerMock */ + $handlerMock = $this->getPersistenceMock()->urlAliasHandler(); + + $this->permissionResolver + ->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('content'), + $this->equalTo('urltranslator'), + $this->equalTo($location) + ) + ->willReturn(true); + + $handlerMock->expects( + $this->once() + )->method( + 'createCustomUrlAlias' + )->with( + $this->equalTo($location->id), + $this->equalTo(self::EXAMPLE_PATH), + $this->equalTo(true), + $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), + $this->equalTo(true) + )->will( + $this->throwException(new ForbiddenException('Forbidden!')) + ); + + $mockedService->createUrlAlias( + $location, + self::EXAMPLE_PATH, + self::EXAMPLE_LANGUAGE_CODE, + true, + true + ); + } + + /** + * Test for the createGlobalUrlAlias() method. + */ + public function testCreateGlobalUrlAlias() + { + $resource = 'module:content/search'; + + $this->permissionResolver + ->expects($this->once()) + ->method('hasAccess') + ->with( + $this->equalTo('content'), + $this->equalTo('urltranslator') + ) + ->willReturn(true); + + $repositoryMock = $this->getRepositoryMock(); + + $mockedService = $this->getPartlyMockedURLAliasServiceService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ + $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); + + $repositoryMock + ->expects($this->once()) + ->method('beginTransaction'); + $repositoryMock + ->expects($this->once()) + ->method('commit'); + + $urlAliasHandlerMock->expects( + $this->once() + )->method( + 'createGlobalUrlAlias' + )->with( + $this->equalTo($resource), + $this->equalTo(self::EXAMPLE_PATH), + $this->equalTo(true), + $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), + $this->equalTo(true) + )->willReturn( + new SPIUrlAlias() + ); + + $urlAlias = $mockedService->createGlobalUrlAlias( + $resource, + self::EXAMPLE_PATH, + self::EXAMPLE_LANGUAGE_CODE, + true, + true + ); + + self::assertInstanceOf(URLAlias::class, $urlAlias); + } + + /** + * Test for the createGlobalUrlAlias() method. + */ + public function testCreateGlobalUrlAliasWithRollback() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Handler threw an exception'); + + $resource = 'module:content/search'; + + $this->permissionResolver + ->expects($this->once()) + ->method('hasAccess') + ->with( + $this->equalTo('content'), + $this->equalTo('urltranslator') + ) + ->willReturn(true); + + $repositoryMock = $this->getRepositoryMock(); + + $mockedService = $this->getPartlyMockedURLAliasServiceService(); + /** @var \PHPUnit\Framework\MockObject\MockObject $urlAliasHandlerMock */ + $urlAliasHandlerMock = $this->getPersistenceMock()->urlAliasHandler(); + + $repositoryMock + ->expects($this->once()) + ->method('beginTransaction'); + $repositoryMock + ->expects($this->once()) + ->method('rollback'); + + $urlAliasHandlerMock->expects( + $this->once() + )->method( + 'createGlobalUrlAlias' + )->with( + $this->equalTo($resource), + $this->equalTo(self::EXAMPLE_PATH), + $this->equalTo(true), + $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), + $this->equalTo(true) + )->will( + $this->throwException(new Exception('Handler threw an exception')) + ); + + $mockedService->createGlobalUrlAlias( + $resource, + self::EXAMPLE_PATH, + self::EXAMPLE_LANGUAGE_CODE, + true, + true + ); + } + + /** + * Test for the createGlobalUrlAlias() method. + */ + public function testCreateGlobalUrlAliasThrowsInvalidArgumentExceptionResource() + { + $this->expectException(InvalidArgumentException::class); + + $mockedService = $this->getPartlyMockedURLAliasServiceService(); + $this->permissionResolver + ->expects($this->once()) + ->method('hasAccess')->with( + $this->equalTo('content'), + $this->equalTo('urltranslator') + ) + ->willReturn(true); + + $mockedService->createGlobalUrlAlias( + 'invalid/resource', + self::EXAMPLE_PATH, + self::EXAMPLE_LANGUAGE_CODE, + true, + true + ); + } + + /** + * Test for the createGlobalUrlAlias() method. + */ + public function testCreateGlobalUrlAliasThrowsInvalidArgumentExceptionPath() + { + $this->expectException(InvalidArgumentException::class); + + $resource = 'module:content/search'; + $mockedService = $this->getPartlyMockedURLAliasServiceService(); + + $this->permissionResolver + ->expects($this->once()) + ->method('hasAccess') + ->with( + $this->equalTo('content'), + $this->equalTo('urltranslator') + ) + ->willReturn(true); + + $this->urlAliasHandler->expects( + $this->once() + )->method( + 'createGlobalUrlAlias' + )->with( + $this->equalTo($resource), + $this->equalTo(self::EXAMPLE_PATH), + $this->equalTo(true), + $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), + $this->equalTo(true) + )->will( + $this->throwException(new ForbiddenException('Forbidden!')) + ); + + $mockedService->createGlobalUrlAlias( + $resource, + self::EXAMPLE_PATH, + self::EXAMPLE_LANGUAGE_CODE, + true, + true + ); + } + + /** + * Test for the createGlobalUrlAlias() method. + * + * @depends Ibexa\Tests\Core\Repository\Service\Mock\UrlAliasTest::testCreateUrlAlias + * @depends Ibexa\Tests\Core\Repository\Service\Mock\UrlAliasTest::testCreateUrlAliasWithRollback + * @depends Ibexa\Tests\Core\Repository\Service\Mock\UrlAliasTest::testCreateUrlAliasThrowsInvalidArgumentException + */ + public function testCreateGlobalUrlAliasForLocation() + { + $repositoryMock = $this->getRepositoryMock(); + $mockedService = $this->getPartlyMockedURLAliasServiceService(['createUrlAlias']); + $location = $this->getLocationStub(); + $locationServiceMock = $this->createMock(LocationService::class); + + $locationServiceMock->expects( + $this->exactly(2) + )->method( + 'loadLocation' + )->with( + $this->equalTo(42) + )->willReturn( + $location + ); + + $repositoryMock->expects( + $this->exactly(2) + )->method( + 'getLocationService' + )->willReturn( + $locationServiceMock + ); + + $this->permissionResolver + ->expects($this->exactly(2)) + ->method('canUser')->with( + $this->equalTo('content'), + $this->equalTo('urltranslator'), + $this->equalTo($location) + ) + ->willReturn(true); + + $mockedService->expects( + $this->exactly(2) + )->method( + 'createUrlAlias' + )->with( + $this->equalTo($location), + $this->equalTo(self::EXAMPLE_PATH), + $this->equalTo(self::EXAMPLE_LANGUAGE_CODE), + $this->equalTo(true), + $this->equalTo(true) + ); + + $mockedService->createGlobalUrlAlias( + 'eznode:42', + self::EXAMPLE_PATH, + self::EXAMPLE_LANGUAGE_CODE, + true, + true + ); + $mockedService->createGlobalUrlAlias( + 'module:content/view/full/42', + self::EXAMPLE_PATH, + self::EXAMPLE_LANGUAGE_CODE, + true, + true + ); + } + + /** + * @param int $id + * + * @return \Ibexa\Core\Repository\Values\Content\Location + */ + protected function getLocationStub($id = 42) + { + return new Location(['id' => $id]); + } + + /** + * @param object $urlAliasService + * @param array $configuration + */ + protected function setConfiguration($urlAliasService, array $configuration) + { + $refObject = new \ReflectionObject($urlAliasService); + $refProperty = $refObject->getProperty('settings'); + $refProperty->setAccessible(true); + $refProperty->setValue( + $urlAliasService, + $configuration + ); + } + + /** + * Returns the content service to test with $methods mocked. + * + * Injected Repository comes from {@see getRepositoryMock()} and persistence handler from {@see getPersistenceMock()} + * + * @param string[] $methods + * + * @return \Ibexa\Core\Repository\URLAliasService|\PHPUnit\Framework\MockObject\MockObject + */ + protected function getPartlyMockedURLAliasServiceService( + array $methods = null, + array $prioritizedLanguages = ['eng-GB'], + bool $showAllTranslations = false + ) { + $languageResolverMock = $this->createMock(LanguageResolver::class); + + $languageResolverMock + ->method('getPrioritizedLanguages') + ->willReturn($prioritizedLanguages); + + $languageResolverMock + ->method('getShowAllTranslations') + ->willReturn($showAllTranslations); + + return $this->getMockBuilder(URLAliasService::class) + ->setMethods($methods) + ->setConstructorArgs( + [ + $this->getRepositoryMock(), + $this->getPersistenceMock()->urlAliasHandler(), + $this->getNameSchemaServiceMock(), + $this->permissionResolver, + $languageResolverMock, + ] + ) + ->getMock(); + } + + /** + * Test for the createUrlAlias() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createUrlAlias + */ + public function testCreateUrlAliasThrowsUnauthorizedException() + { + $this->expectException(UnauthorizedException::class); + + $mockedService = $this->getPartlyMockedURLAliasServiceService(); + $location = $this->getLocationStub(); + $this->permissionResolver + ->expects($this->once()) + ->method('canUser')->with( + $this->equalTo('content'), + $this->equalTo('urltranslator'), + $this->equalTo($location) + ) + ->willReturn(false); + + $mockedService->createUrlAlias( + $location, + self::EXAMPLE_PATH, + self::EXAMPLE_LANGUAGE_CODE, + true + ); + } + + /** + * Test for the createGlobalUrlAlias() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::createGlobalUrlAlias + */ + public function testCreateGlobalUrlAliasThrowsUnauthorizedException() + { + $this->expectException(UnauthorizedException::class); + + $mockedService = $this->getPartlyMockedURLAliasServiceService(); + $this->permissionResolver + ->expects($this->once()) + ->method('hasAccess')->with( + $this->equalTo('content'), + $this->equalTo('urltranslator') + ) + ->willReturn(false); + + $mockedService->createGlobalUrlAlias( + 'eznode:42', + self::EXAMPLE_PATH, + self::EXAMPLE_LANGUAGE_CODE, + true, + true + ); + } + + /** + * Test for the removeAliases() method. + * + * @covers \Ibexa\Contracts\Core\Repository\URLAliasService::removeAliases + */ + public function testRemoveAliasesThrowsUnauthorizedException() + { + $this->expectException(UnauthorizedException::class); + + $aliasList = [new URLAlias(['isCustom' => true])]; + $mockedService = $this->getPartlyMockedURLAliasServiceService(); + $this->permissionResolver + ->expects($this->once()) + ->method('hasAccess')->with( + $this->equalTo('content'), + $this->equalTo('urltranslator') + ) + ->willReturn(false); + + $mockedService->removeAliases($aliasList); + } + + /** + * @return \PHPUnit\Framework\MockObject\MockObject|\Ibexa\Core\Repository\Helper\NameSchemaService + */ + protected function getNameSchemaServiceMock() + { + return $this->createMock(NameSchemaService::class); + } + + /** + * @param \Ibexa\Contracts\Core\Persistence\Content\UrlAlias[] $spiUrlAliases + */ + private function configureListURLAliasesForLocation(array $spiUrlAliases): void + { + $this->urlAliasHandler + ->expects($this->once()) + ->method('listURLAliasesForLocation') + ->with( + $this->equalTo(42), + $this->equalTo(false) + ) + ->willReturn($spiUrlAliases); + } +} + +class_alias(UrlAliasTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\UrlAliasTest'); diff --git a/tests/lib/Repository/Service/Mock/UrlTest.php b/tests/lib/Repository/Service/Mock/UrlTest.php new file mode 100644 index 0000000000..d43089ef67 --- /dev/null +++ b/tests/lib/Repository/Service/Mock/UrlTest.php @@ -0,0 +1,488 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Service\Mock; + +use DateTime; +use Ibexa\Contracts\Core\Persistence\URL\URL as SpiUrl; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\SearchService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Query as ContentQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion as ContentCriterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchHit; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult as ContentSearchResults; +use Ibexa\Contracts\Core\Repository\Values\URL\SearchResult; +use Ibexa\Contracts\Core\Repository\Values\URL\URL; +use Ibexa\Contracts\Core\Repository\Values\URL\URLQuery; +use Ibexa\Contracts\Core\Repository\Values\URL\URLUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\URL\UsageSearchResult; +use Ibexa\Core\Base\Exceptions\InvalidArgumentValue; +use Ibexa\Core\Base\Exceptions\UnauthorizedException; +use Ibexa\Core\Repository\URLService; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; + +class UrlTest extends BaseServiceMockTest +{ + private const URL_ID = 12; + private const URL_IBEXA_CO = 'http://ibexa.co'; + private const URL_IBEXA_COM = 'http://ibexa.com'; + + /** @var \Ibexa\Contracts\Core\Repository\URLService|\PHPUnit\Framework\MockObject\MockObject */ + private $urlHandler; + + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ + private $permissionResolver; + + protected function setUp(): void + { + parent::setUp(); + $this->urlHandler = $this->getPersistenceMockHandler('URL\\Handler'); + $this->permissionResolver = $this->getPermissionResolverMock(); + } + + public function testFindUrlsUnauthorized() + { + $this->configureUrlViewPermissionForHasAccess(false); + + $this->expectException(UnauthorizedException::class); + $this->createUrlService()->findUrls(new URLQuery()); + } + + public function testFindUrlsNonNumericOffset() + { + $this->expectException(InvalidArgumentValue::class); + + $query = new URLQuery(); + $query->offset = 'foo'; + + $this->createUrlService()->findUrls($query); + } + + public function testFindUrlsNonNumericLimit() + { + $this->expectException(InvalidArgumentValue::class); + + $query = new URLQuery(); + $query->limit = 'foo'; + + $this->createUrlService()->findUrls($query); + } + + public function testFindUrls() + { + $url = $this->getApiUrl(); + + $this->configureUrlViewPermissionForHasAccess(true); + + $query = new URLQuery(); + + $results = [ + 'count' => 1, + 'items' => [ + new SpiUrl(), + ], + ]; + + $expected = new SearchResult([ + 'totalCount' => 1, + 'items' => [$url], + ]); + + $this->urlHandler + ->expects($this->once()) + ->method('find') + ->with($query) + ->willReturn($results); + + $this->assertEquals($expected, $this->createUrlService()->findUrls($query)); + } + + public function testUpdateUrlUnauthorized() + { + $this->expectException(UnauthorizedException::class); + + $url = $this->getApiUrl(); + + $this->configureUrlUpdatePermission($url, false); + + $this->createUrlService()->updateUrl($url, new URLUpdateStruct()); + } + + public function testUpdateUrlNonUnique() + { + $this->expectException(InvalidArgumentException::class); + + $url = $this->getApiUrl(self::URL_ID, self::URL_IBEXA_CO); + + $this->configureUrlUpdatePermission($url, true); + + $struct = new URLUpdateStruct([ + 'url' => self::URL_IBEXA_COM, + ]); + + $urlService = $this->createUrlService(['isUnique']); + $urlService + ->expects($this->once()) + ->method('isUnique') + ->with($url->id, $struct->url) + ->willReturn(false); + + $urlService->updateUrl($url, $struct); + } + + public function testUpdateUrl() + { + $apiUrl = $this->getApiUrl(self::URL_ID, self::URL_IBEXA_CO); + $apiStruct = new URLUpdateStruct([ + 'url' => self::URL_IBEXA_COM, + 'isValid' => false, + 'lastChecked' => new DateTime(), + ]); + + $this->configurePermissions([ + ['url', 'update', $apiUrl, []], + ['url', 'view', $apiUrl, []], + ['url', 'view', new URL(['id' => self::URL_ID, 'url' => self::URL_IBEXA_COM, 'isValid' => true]), []], + ]); + + $urlService = $this->createUrlService(['isUnique']); + $urlService + ->expects($this->once()) + ->method('isUnique') + ->with($apiUrl->id, $apiStruct->url) + ->willReturn(true); + + $this->urlHandler + ->expects($this->once()) + ->method('updateUrl') + ->willReturnCallback(function ($id, $struct) use ($apiUrl, $apiStruct) { + $this->assertEquals($apiUrl->id, $id); + + $this->assertEquals($apiStruct->url, $struct->url); + $this->assertEquals(0, $struct->lastChecked); + $this->assertTrue($struct->isValid); + }); + + $this->urlHandler + ->method('loadById') + ->with($apiUrl->id) + ->willReturnOnConsecutiveCalls( + new SpiUrl([ + 'id' => $apiUrl->id, + 'url' => $apiUrl->url, + 'isValid' => $apiUrl->isValid, + 'lastChecked' => $apiUrl->lastChecked, + ]), + new SpiUrl([ + 'id' => $apiUrl->id, + 'url' => $apiStruct->url, + 'isValid' => true, + 'lastChecked' => 0, + ]) + ); + + $this->assertEquals(new URL([ + 'id' => $apiUrl->id, + 'url' => $apiStruct->url, + 'isValid' => true, + 'lastChecked' => null, + ]), $urlService->updateUrl($apiUrl, $apiStruct)); + } + + public function testUpdateUrlStatus() + { + $apiUrl = $this->getApiUrl(self::URL_ID, self::URL_IBEXA_CO); + $apiStruct = new URLUpdateStruct([ + 'isValid' => true, + 'lastChecked' => new DateTime('@' . time()), + ]); + + $urlAfterUpdate = new URL([ + 'id' => self::URL_ID, + 'url' => self::URL_IBEXA_CO, + 'isValid' => true, + 'lastChecked' => new DateTime('@' . time()), + ]); + + $this->configurePermissions([ + ['url', 'update', $apiUrl, []], + ['url', 'view', $apiUrl, []], + ['url', 'view', $urlAfterUpdate, []], + ]); + + $urlService = $this->createUrlService(['isUnique']); + + $this->urlHandler + ->expects($this->once()) + ->method('updateUrl') + ->willReturnCallback(function ($id, $struct) use ($apiUrl, $apiStruct) { + $this->assertEquals($apiUrl->id, $id); + + $this->assertEquals($apiUrl->url, $struct->url); + $this->assertEquals($apiStruct->lastChecked->getTimestamp(), $struct->lastChecked); + $this->assertTrue($apiStruct->isValid, $struct->isValid); + }); + + $this->urlHandler + ->method('loadById') + ->with($apiUrl->id) + ->willReturnOnConsecutiveCalls( + new SpiUrl([ + 'id' => $apiUrl->id, + 'url' => $apiUrl->url, + 'isValid' => $apiUrl->isValid, + 'lastChecked' => $apiUrl->lastChecked, + ]), + new SpiUrl([ + 'id' => $apiUrl->id, + 'url' => $apiUrl->url, + 'isValid' => $apiStruct->isValid, + 'lastChecked' => $apiStruct->lastChecked->getTimestamp(), + ]) + ); + + $this->assertEquals(new URL([ + 'id' => $apiUrl->id, + 'url' => $apiUrl->url, + 'isValid' => $apiStruct->isValid, + 'lastChecked' => $apiStruct->lastChecked, + ]), $urlService->updateUrl($apiUrl, $apiStruct)); + } + + public function testLoadByIdUnauthorized() + { + $this->expectException(UnauthorizedException::class); + + $this->configureUrlViewPermission( + new URL([ + 'id' => self::URL_ID, + ]), + false + ); + + $this->urlHandler + ->expects($this->once()) + ->method('loadById') + ->with(self::URL_ID) + ->willReturn(new SpiUrl([ + 'id' => self::URL_ID, + ])); + + $this->createUrlService()->loadById(self::URL_ID); + } + + public function testLoadById() + { + $url = new URL([ + 'id' => self::URL_ID, + ]); + + $this->configureUrlViewPermission($url, true); + + $this->urlHandler + ->expects($this->once()) + ->method('loadById') + ->with(self::URL_ID) + ->willReturn(new SpiUrl([ + 'id' => self::URL_ID, + ])); + + $this->assertEquals($url, $this->createUrlService()->loadById(self::URL_ID)); + } + + public function testLoadByUrlUnauthorized() + { + $this->expectException(UnauthorizedException::class); + + $url = self::URL_IBEXA_CO; + + $this->configureUrlViewPermission( + new URL([ + 'id' => self::URL_ID, + ]), + false + ); + + $this->urlHandler + ->expects($this->once()) + ->method('loadByUrl') + ->with($url) + ->willReturn(new SpiUrl([ + 'id' => self::URL_ID, + ])); + + $this->createUrlService()->loadByUrl(self::URL_IBEXA_CO); + } + + public function testLoadByUrl() + { + $url = self::URL_IBEXA_CO; + + $apiUrl = new URL([ + 'url' => $url, + ]); + + $this->configureUrlViewPermission($apiUrl, true); + + $this->urlHandler + ->expects($this->once()) + ->method('loadByUrl') + ->with($url) + ->willReturn(new SpiUrl([ + 'url' => $url, + ])); + + $this->assertEquals($apiUrl, $this->createUrlService()->loadByUrl($url)); + } + + /** + * @dataProvider dateProviderForFindUsages + */ + public function testFindUsages($offset, $limit, ContentQuery $expectedQuery, array $usages) + { + $url = $this->getApiUrl(self::URL_ID, self::URL_IBEXA_CO); + + if (!empty($usages)) { + $searchService = $this->createMock(SearchService::class); + $searchService + ->expects($this->once()) + ->method('findContentInfo') + ->willReturnCallback(function ($query) use ($expectedQuery, $usages) { + $this->assertEquals($expectedQuery, $query); + + return new ContentSearchResults([ + 'searchHits' => array_map(static function ($id) { + return new SearchHit([ + 'valueObject' => new ContentInfo([ + 'id' => $id, + ]), + ]); + }, $usages), + 'totalCount' => count($usages), + ]); + }); + + $this->getRepositoryMock() + ->expects($this->once()) + ->method('getSearchService') + ->willReturn($searchService); + } + + $this->urlHandler + ->expects($this->once()) + ->method('findUsages') + ->with($url->id) + ->willReturn($usages); + + $usageSearchResult = $this->createUrlService()->findUsages($url, $offset, $limit); + + $this->assertInstanceOf(UsageSearchResult::class, $usageSearchResult); + $this->assertEquals(count($usages), $usageSearchResult->totalCount); + foreach ($usageSearchResult as $contentInfo) { + $this->assertContains($contentInfo->id, $usages); + } + } + + public function dateProviderForFindUsages() + { + return [ + [ + 10, -1, new ContentQuery([ + 'filter' => new ContentCriterion\MatchNone(), + 'offset' => 10, + ]), [], + ], + [ + 10, -1, new ContentQuery([ + 'filter' => new ContentCriterion\LogicalAnd([ + new ContentCriterion\ContentId([1, 2, 3]), + new ContentCriterion\Visibility(ContentCriterion\Visibility::VISIBLE), + ]), + 'offset' => 10, + ]), [1, 2, 3], + ], + [ + 10, 10, new ContentQuery([ + 'filter' => new ContentCriterion\LogicalAnd([ + new ContentCriterion\ContentId([1, 2, 3]), + new ContentCriterion\Visibility(ContentCriterion\Visibility::VISIBLE), + ]), + 'offset' => 10, + 'limit' => 10, + ]), [1, 2, 3], + ], + ]; + } + + public function testCreateUpdateStruct() + { + $this->assertEquals(new URLUpdateStruct(), $this->createUrlService()->createUpdateStruct()); + } + + protected function configureUrlViewPermissionForHasAccess($hasAccess = false) + { + $this->permissionResolver + ->expects($this->once()) + ->method('hasAccess') + ->with('url', 'view') + ->willReturn($hasAccess); + } + + protected function configureUrlViewPermission($object, $hasAccess = false) + { + $this->permissionResolver + ->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('url'), + $this->equalTo('view'), + $this->equalTo($object) + ) + ->will($this->returnValue($hasAccess)); + } + + protected function configureUrlUpdatePermission($object, $hasAccess = false) + { + $this->permissionResolver + ->expects($this->once()) + ->method('canUser') + ->with( + $this->equalTo('url'), + $this->equalTo('update'), + $this->equalTo($object) + ) + ->will($this->returnValue($hasAccess)); + } + + protected function configurePermissions(array $permissions) + { + $this->permissionResolver + ->expects($this->exactly(count($permissions))) + ->method('canUser') + ->withConsecutive(...$permissions) + ->willReturn(true); + } + + /** + * @return \Ibexa\Contracts\Core\Repository\URLService|\PHPUnit\Framework\MockObject\MockObject + */ + private function createUrlService(array $methods = null) + { + return $this + ->getMockBuilder(URLService::class) + ->setConstructorArgs([$this->getRepositoryMock(), $this->urlHandler, $this->permissionResolver]) + ->setMethods($methods) + ->getMock(); + } + + private function getApiUrl($id = null, $url = null) + { + return new URL(['id' => $id, 'url' => $url]); + } +} + +class_alias(UrlTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\UrlTest'); diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/UrlWildcardTest.php b/tests/lib/Repository/Service/Mock/UrlWildcardTest.php similarity index 88% rename from eZ/Publish/Core/Repository/Tests/Service/Mock/UrlWildcardTest.php rename to tests/lib/Repository/Service/Mock/UrlWildcardTest.php index 2579dc0ae6..48f75f9f0b 100644 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/UrlWildcardTest.php +++ b/tests/lib/Repository/Service/Mock/UrlWildcardTest.php @@ -6,18 +6,19 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; +namespace Ibexa\Tests\Core\Repository\Service\Mock; use Exception; -use eZ\Publish\API\Repository\Exceptions\ContentValidationException; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Exceptions\UnauthorizedException; -use eZ\Publish\API\Repository\Values\Content\URLWildcard; -use eZ\Publish\API\Repository\Values\Content\URLWildcardTranslationResult; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\Core\Repository\URLWildcardService; -use eZ\Publish\SPI\Persistence\Content\UrlWildcard as SPIURLWildcard; +use Ibexa\Contracts\Core\Persistence\Content\UrlWildcard as SPIURLWildcard; +use Ibexa\Contracts\Core\Repository\Exceptions\ContentValidationException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcard; +use Ibexa\Contracts\Core\Repository\Values\Content\URLWildcardTranslationResult; +use Ibexa\Core\Base\Exceptions\NotFoundException as APINotFoundException; +use Ibexa\Core\Repository\URLWildcardService; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; /** * Mock Test case for UrlWildcard Service. @@ -26,10 +27,10 @@ class UrlWildcardTest extends BaseServiceMockTest { private const EXAMPLE_URL_WILDCARD_ID = 1; - /** @var \eZ\Publish\API\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Repository\PermissionResolver|\PHPUnit\Framework\MockObject\MockObject */ private $permissionResolver; - /** @var \eZ\Publish\SPI\Persistence\Content\UrlWildcard\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\UrlWildcard\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $urlWildcardHandler; protected function setUp(): void @@ -42,7 +43,7 @@ protected function setUp(): void /** * Test for the create() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::create + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::create */ public function testCreateThrowsUnauthorizedException() { @@ -69,11 +70,11 @@ public function testCreateThrowsUnauthorizedException() /** * Test for the create() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::create + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::create */ public function testCreateThrowsInvalidArgumentException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $mockedService = $this->getPartlyMockedURLWildcardService(); @@ -111,12 +112,12 @@ public function providerForTestCreateThrowsContentValidationException() /** * Test for the create() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::create + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::create * @dataProvider providerForTestCreateThrowsContentValidationException */ public function testCreateThrowsContentValidationException($sourceUrl, $destinationUrl, $forward) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\ContentValidationException::class); + $this->expectException(ContentValidationException::class); $mockedService = $this->getPartlyMockedURLWildcardService(); @@ -158,7 +159,7 @@ public function providerForTestCreate() /** * Test for the create() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::create + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::create * @dataProvider providerForTestCreate */ public function testCreate($sourceUrl, $destinationUrl, $forward) @@ -228,7 +229,7 @@ public function testCreate($sourceUrl, $destinationUrl, $forward) /** * Test for the create() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::create + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::create */ public function testCreateWithRollback() { @@ -281,7 +282,7 @@ public function testCreateWithRollback() /** * Test for the remove() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::remove + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::remove */ public function testRemoveThrowsUnauthorizedException() { @@ -314,7 +315,7 @@ public function testRemoveThrowsUnauthorizedException() /** * Test for the remove() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::remove + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::remove */ public function testRemove() { @@ -352,7 +353,7 @@ public function testRemove() /** * Test for the remove() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::remove + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::remove */ public function testRemoveWithRollback() { @@ -396,7 +397,7 @@ public function testRemoveWithRollback() /** * Test for the load() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::remove + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::remove */ public function testLoadThrowsException() { @@ -422,7 +423,7 @@ public function testLoadThrowsException() /** * Test for the load() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::remove + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::remove */ public function testLoad() { @@ -465,7 +466,7 @@ public function testLoad() /** * Test for the loadAll() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::loadAll + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::loadAll */ public function testLoadAll() { @@ -488,7 +489,7 @@ public function testLoadAll() /** * Test for the loadAll() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::loadAll + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::loadAll */ public function testLoadAllWithLimitAndOffset() { @@ -577,12 +578,12 @@ public function providerForTestTranslateThrowsNotFoundException() /** * Test for the translate() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::translate + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::translate * @dataProvider providerForTestTranslateThrowsNotFoundException */ public function testTranslateThrowsNotFoundException($createArray, $url) { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\NotFoundException::class); + $this->expectException(NotFoundException::class); $mockedService = $this->getPartlyMockedURLWildcardService(); @@ -592,7 +593,7 @@ public function testTranslateThrowsNotFoundException($createArray, $url) ->expects($this->once()) ->method('translate') ->with($trimmedUrl) - ->willThrowException(new \eZ\Publish\Core\Base\Exceptions\NotFoundException('UrlWildcard', $trimmedUrl)); + ->willThrowException(new APINotFoundException('UrlWildcard', $trimmedUrl)); $this->expectException(NotFoundException::class); @@ -683,7 +684,7 @@ public function providerForTestTranslate() /** * Test for the translate() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::translate + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::translate * @dataProvider providerForTestTranslate */ public function testTranslate($createArray, $url, $uri) @@ -719,7 +720,7 @@ public function testTranslate($createArray, $url, $uri) /** * Test for the translate() method. * - * @covers \eZ\Publish\Core\Repository\URLWildcardService::translate + * @covers \Ibexa\Contracts\Core\Repository\URLWildcardService::translate */ public function testTranslateUsesLongestMatchingWildcard() { @@ -757,7 +758,7 @@ public function testTranslateUsesLongestMatchingWildcard() * * @param string[] $methods * - * @return \eZ\Publish\Core\Repository\URLWildcardService|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Repository\URLWildcardService|\PHPUnit\Framework\MockObject\MockObject */ protected function getPartlyMockedURLWildcardService(array $methods = null) { @@ -773,3 +774,5 @@ protected function getPartlyMockedURLWildcardService(array $methods = null) ->getMock(); } } + +class_alias(UrlWildcardTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\UrlWildcardTest'); diff --git a/tests/lib/Repository/Service/Mock/UserPasswordValidatorTest.php b/tests/lib/Repository/Service/Mock/UserPasswordValidatorTest.php new file mode 100644 index 0000000000..486e22529e --- /dev/null +++ b/tests/lib/Repository/Service/Mock/UserPasswordValidatorTest.php @@ -0,0 +1,245 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Service\Mock; + +use Ibexa\Core\FieldType\ValidationError; +use Ibexa\Core\Repository\Validator\UserPasswordValidator; +use Ibexa\Tests\Core\Search\TestCase; + +/** + * @covers \Ibexa\Core\Repository\Validator\UserPasswordValidator + */ +class UserPasswordValidatorTest extends TestCase +{ + /** + * @dataProvider dateProviderForValidate + */ + public function testValidate(array $constraints, string $password, array $expectedErrors) + { + $validator = new UserPasswordValidator($constraints); + + $this->assertEqualsCanonicalizing($expectedErrors, $validator->validate($password), ''); + } + + public function dateProviderForValidate(): array + { + return [ + [ + [ + 'minLength' => -1, + 'requireAtLeastOneLowerCaseCharacter' => false, + 'requireAtLeastOneUpperCaseCharacter' => false, + 'requireAtLeastOneNumericCharacter' => false, + 'requireAtLeastOneNonAlphanumericCharacter' => false, + 'requireNotCompromisedPassword' => false, + ], + 'pass', + [/* No errors */], + ], + [ + [ + 'minLength' => 6, + 'requireAtLeastOneLowerCaseCharacter' => false, + 'requireAtLeastOneUpperCaseCharacter' => false, + 'requireAtLeastOneNumericCharacter' => false, + 'requireAtLeastOneNonAlphanumericCharacter' => false, + 'requireNotCompromisedPassword' => false, + ], + '123', + [ + new ValidationError('User password must be at least %length% characters long', null, [ + '%length%' => 6, + ], 'password'), + ], + ], + [ + [ + 'minLength' => 6, + 'requireAtLeastOneLowerCaseCharacter' => false, + 'requireAtLeastOneUpperCaseCharacter' => false, + 'requireAtLeastOneNumericCharacter' => false, + 'requireAtLeastOneNonAlphanumericCharacter' => false, + 'requireNotCompromisedPassword' => false, + ], + '123456!', + [/* No errors */], + ], + [ + [ + 'minLength' => -1, + 'requireAtLeastOneLowerCaseCharacter' => true, + 'requireAtLeastOneUpperCaseCharacter' => false, + 'requireAtLeastOneNumericCharacter' => false, + 'requireAtLeastOneNonAlphanumericCharacter' => false, + 'requireNotCompromisedPassword' => false, + ], + 'PASS', + [ + new ValidationError('User password must include at least one lower case letter', null, [], 'password'), + ], + ], + [ + [ + 'minLength' => -1, + 'requireAtLeastOneLowerCaseCharacter' => true, + 'requireAtLeastOneUpperCaseCharacter' => false, + 'requireAtLeastOneNumericCharacter' => false, + 'requireAtLeastOneNonAlphanumericCharacter' => false, + 'requireNotCompromisedPassword' => false, + ], + 'PaSS', + [/* No errors */], + ], + [ + [ + 'minLength' => -1, + 'requireAtLeastOneLowerCaseCharacter' => false, + 'requireAtLeastOneUpperCaseCharacter' => true, + 'requireAtLeastOneNumericCharacter' => false, + 'requireAtLeastOneNonAlphanumericCharacter' => false, + 'requireNotCompromisedPassword' => false, + ], + 'pass', + [ + new ValidationError('User password must include at least one upper case letter', null, [], 'password'), + ], + ], + [ + [ + 'minLength' => -1, + 'requireAtLeastOneLowerCaseCharacter' => false, + 'requireAtLeastOneUpperCaseCharacter' => true, + 'requireAtLeastOneNumericCharacter' => false, + 'requireAtLeastOneNonAlphanumericCharacter' => false, + 'requireNotCompromisedPassword' => false, + ], + 'pAss', + [/* No errors */], + ], + [ + [ + 'minLength' => -1, + 'requireAtLeastOneLowerCaseCharacter' => false, + 'requireAtLeastOneUpperCaseCharacter' => false, + 'requireAtLeastOneNumericCharacter' => true, + 'requireAtLeastOneNonAlphanumericCharacter' => false, + 'requireNotCompromisedPassword' => false, + ], + 'pass', + [ + new ValidationError('User password must include at least one number', null, [], 'password'), + ], + ], + [ + [ + 'minLength' => -1, + 'requireAtLeastOneLowerCaseCharacter' => false, + 'requireAtLeastOneUpperCaseCharacter' => false, + 'requireAtLeastOneNumericCharacter' => true, + 'requireAtLeastOneNonAlphanumericCharacter' => false, + 'requireNotCompromisedPassword' => false, + ], + 'pass1', + [/* No errors */], + ], + [ + [ + 'minLength' => -1, + 'requireAtLeastOneLowerCaseCharacter' => false, + 'requireAtLeastOneUpperCaseCharacter' => false, + 'requireAtLeastOneNumericCharacter' => false, + 'requireAtLeastOneNonAlphanumericCharacter' => true, + 'requireNotCompromisedPassword' => false, + ], + 'pass', + [ + new ValidationError('User password must include at least one special character', null, [], 'password'), + ], + ], + [ + [ + 'minLength' => -1, + 'requireAtLeastOneLowerCaseCharacter' => false, + 'requireAtLeastOneUpperCaseCharacter' => false, + 'requireAtLeastOneNumericCharacter' => false, + 'requireAtLeastOneNonAlphanumericCharacter' => true, + 'requireNotCompromisedPassword' => false, + ], + 'pass!', + [/* No errors */], + ], + [ + [ + 'minLength' => 6, + 'requireAtLeastOneLowerCaseCharacter' => true, + 'requireAtLeastOneUpperCaseCharacter' => true, + 'requireAtLeastOneNumericCharacter' => true, + 'requireAtLeastOneNonAlphanumericCharacter' => true, + 'requireNotCompromisedPassword' => false, + ], + 'asdf', + [ + new ValidationError('User password must be at least %length% characters long', null, [ + '%length%' => 6, + ], 'password'), + new ValidationError('User password must include at least one upper case letter', null, [], 'password'), + new ValidationError('User password must include at least one number', null, [], 'password'), + new ValidationError('User password must include at least one special character', null, [], 'password'), + ], + ], + [ + [ + 'minLength' => 6, + 'requireAtLeastOneLowerCaseCharacter' => true, + 'requireAtLeastOneUpperCaseCharacter' => true, + 'requireAtLeastOneNumericCharacter' => true, + 'requireAtLeastOneNonAlphanumericCharacter' => true, + 'requireNotCompromisedPassword' => false, + ], + 'H@xxi0r!', + [/* No errors */], + ], + [ + [ + 'minLength' => -1, + 'requireAtLeastOneLowerCaseCharacter' => false, + 'requireAtLeastOneUpperCaseCharacter' => false, + 'requireAtLeastOneNumericCharacter' => false, + 'requireAtLeastOneNonAlphanumericCharacter' => false, + 'requireNotCompromisedPassword' => true, + ], + // 64 chars, very unlikely to ever be in a breach + bin2hex(random_bytes(32)), + [/* No errors */], + ], + [ + [ + 'minLength' => -1, + 'requireAtLeastOneLowerCaseCharacter' => false, + 'requireAtLeastOneUpperCaseCharacter' => false, + 'requireAtLeastOneNumericCharacter' => false, + 'requireAtLeastOneNonAlphanumericCharacter' => false, + 'requireNotCompromisedPassword' => true, + ], + 'secret', + [ + new ValidationError( + 'This password has been leaked in a data breach, it must not be used. ' + . 'Please use another password.', + null, + [], + 'password' + ), + ], + ], + ]; + } +} + +class_alias(UserPasswordValidatorTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\UserPasswordValidatorTest'); diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/UserPreferenceTest.php b/tests/lib/Repository/Service/Mock/UserPreferenceTest.php similarity index 81% rename from eZ/Publish/Core/Repository/Tests/Service/Mock/UserPreferenceTest.php rename to tests/lib/Repository/Service/Mock/UserPreferenceTest.php index 21613bff38..56048e5476 100644 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/UserPreferenceTest.php +++ b/tests/lib/Repository/Service/Mock/UserPreferenceTest.php @@ -6,17 +6,18 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; +namespace Ibexa\Tests\Core\Repository\Service\Mock; use Exception; -use eZ\Publish\API\Repository\PermissionResolver; -use eZ\Publish\API\Repository\Values\UserPreference\UserPreference as APIUserPreference; -use eZ\Publish\API\Repository\Values\UserPreference\UserPreferenceSetStruct as APIUserPreferenceSetStruct; -use eZ\Publish\Core\Repository\Tests\Service\Mock\Base as BaseServiceMockTest; -use eZ\Publish\Core\Repository\UserPreferenceService; -use eZ\Publish\Core\Repository\Values\User\UserReference; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreference; -use eZ\Publish\SPI\Persistence\UserPreference\UserPreferenceSetStruct; +use Ibexa\Contracts\Core\Persistence\UserPreference\UserPreference; +use Ibexa\Contracts\Core\Persistence\UserPreference\UserPreferenceSetStruct; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\PermissionResolver; +use Ibexa\Contracts\Core\Repository\Values\UserPreference\UserPreference as APIUserPreference; +use Ibexa\Contracts\Core\Repository\Values\UserPreference\UserPreferenceSetStruct as APIUserPreferenceSetStruct; +use Ibexa\Core\Repository\UserPreferenceService; +use Ibexa\Core\Repository\Values\User\UserReference; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; class UserPreferenceTest extends BaseServiceMockTest { @@ -24,7 +25,7 @@ class UserPreferenceTest extends BaseServiceMockTest public const USER_PREFERENCE_NAME = 'setting'; public const USER_PREFERENCE_VALUE = 'value'; - /** @var \eZ\Publish\SPI\Persistence\UserPreference\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\UserPreference\Handler|\PHPUnit\Framework\MockObject\MockObject */ private $userSPIPreferenceHandler; protected function setUp(): void @@ -42,7 +43,7 @@ protected function setUp(): void } /** - * @covers \eZ\Publish\Core\Repository\UserPreferenceService::setUserPreference() + * @covers \Ibexa\Contracts\Core\Repository\UserPreferenceService::setUserPreference() */ public function testSetUserPreference() { @@ -68,11 +69,11 @@ public function testSetUserPreference() } /** - * @covers \eZ\Publish\Core\Repository\UserPreferenceService::setUserPreference + * @covers \Ibexa\Contracts\Core\Repository\UserPreferenceService::setUserPreference */ public function testSetUserPreferenceThrowsInvalidArgumentException() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $apiUserPreferenceSetStruct = new APIUserPreferenceSetStruct([ 'value' => 'value', @@ -86,7 +87,7 @@ public function testSetUserPreferenceThrowsInvalidArgumentException() } /** - * @covers \eZ\Publish\Core\Repository\UserPreferenceService::setUserPreference + * @covers \Ibexa\Contracts\Core\Repository\UserPreferenceService::setUserPreference */ public function testSetUserPreferenceWithRollback() { @@ -108,7 +109,7 @@ public function testSetUserPreferenceWithRollback() } /** - * @covers \eZ\Publish\Core\Repository\UserPreferenceService::getUserPreference() + * @covers \Ibexa\Contracts\Core\Repository\UserPreferenceService::getUserPreference() */ public function testGetUserPreference() { @@ -134,7 +135,7 @@ public function testGetUserPreference() } /** - * @covers \eZ\Publish\Core\Repository\UserPreferenceService::loadUserPreferences + * @covers \Ibexa\Contracts\Core\Repository\UserPreferenceService::loadUserPreferences */ public function testLoadUserPreferences() { @@ -170,7 +171,7 @@ public function testLoadUserPreferences() } /** - * @covers \eZ\Publish\Core\Repository\UserPreferenceService::getUserPreferenceCount() + * @covers \Ibexa\Contracts\Core\Repository\UserPreferenceService::getUserPreferenceCount() */ public function testGetUserPreferenceCount() { @@ -188,7 +189,7 @@ public function testGetUserPreferenceCount() } /** - * @return \eZ\Publish\API\Repository\UserPreferenceService|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\Repository\UserPreferenceService|\PHPUnit\Framework\MockObject\MockObject */ private function createAPIUserPreferenceService(array $methods = null) { @@ -234,3 +235,5 @@ private function createAPIUserPreference(): APIUserPreference ]); } } + +class_alias(UserPreferenceTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\UserPreferenceTest'); diff --git a/tests/lib/Repository/Service/Mock/UserTest.php b/tests/lib/Repository/Service/Mock/UserTest.php new file mode 100644 index 0000000000..fc38331218 --- /dev/null +++ b/tests/lib/Repository/Service/Mock/UserTest.php @@ -0,0 +1,147 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Service\Mock; + +use Exception; +use Ibexa\Contracts\Core\Persistence\User\Handler as PersistenceUserHandler; +use Ibexa\Contracts\Core\Persistence\User\RoleAssignment; +use Ibexa\Contracts\Core\Repository\ContentService as APIContentService; +use Ibexa\Contracts\Core\Repository\PasswordHashService; +use Ibexa\Contracts\Core\Repository\Repository; +use Ibexa\Contracts\Core\Repository\UserService as APIUserService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo as APIContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo as APIVersionInfo; +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Contracts\Core\Repository\Values\User\User as APIUser; +use Ibexa\Contracts\Core\SiteAccess\ConfigResolverInterface; +use Ibexa\Core\Repository\User\PasswordValidatorInterface; +use Ibexa\Core\Repository\UserService; +use Ibexa\Tests\Core\Repository\Service\Mock\Base as BaseServiceMockTest; + +/** + * @covers \Ibexa\Core\Repository\UserService + */ +class UserTest extends BaseServiceMockTest +{ + private const MOCKED_USER_ID = 42; + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testDeleteUser(): void + { + $repository = $this->getRepositoryMock(); + $userService = $this->getPartlyMockedUserService(['loadUser']); + $contentService = $this->createMock(APIContentService::class); + /* @var \PHPUnit\Framework\MockObject\MockObject $userHandler */ + $userHandler = $this->getPersistenceMock()->userHandler(); + + $user = $this->createMock(APIUser::class); + $contentInfo = $this->createMock(APIContentInfo::class); + $this->mockDeleteUserFlow($repository, $userService, $contentService, $user, $contentInfo, $userHandler); + + $contentService->expects(self::once())->method('deleteContent')->with($contentInfo); + $userHandler->expects(self::once())->method('delete')->with(self::MOCKED_USER_ID); + $repository->expects(self::once())->method('commit'); + + $userService->deleteUser($user); + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\UnauthorizedException + */ + public function testDeleteUserWithRollback(): void + { + $repository = $this->getRepositoryMock(); + $userService = $this->getPartlyMockedUserService(['loadUser']); + $contentService = $this->createMock(APIContentService::class); + /* @var \Ibexa\Contracts\Core\Persistence\User\Handler&\PHPUnit\Framework\MockObject\MockObject $userHandler */ + $userHandler = $this->getPersistenceMock()->userHandler(); + + $user = $this->createMock(APIUser::class); + $contentInfo = $this->createMock(APIContentInfo::class); + $this->mockDeleteUserFlow($repository, $userService, $contentService, $user, $contentInfo, $userHandler); + + $exception = new Exception(); + $contentService->expects(self::once()) + ->method('deleteContent') + ->with($contentInfo) + ->willThrowException($exception); + + $repository->expects(self::once())->method('rollback'); + + $this->expectExceptionObject($exception); + $userService->deleteUser($user); + } + + /** + * Returns the User service to test with $methods mocked. + * + * Injected Repository comes from {@see getRepositoryMock()} and persistence handler from {@see getPersistenceMock()} + * + * @param string[] $methods + * + * @return \Ibexa\Contracts\Core\Repository\UserService&\PHPUnit\Framework\MockObject\MockObject + */ + protected function getPartlyMockedUserService(array $methods = null): APIUserService + { + return $this->getMockBuilder(UserService::class) + ->onlyMethods($methods) + ->setConstructorArgs( + [ + $this->getRepositoryMock(), + $this->getPermissionResolverMock(), + $this->getPersistenceMock()->userHandler(), + $this->getPersistenceMock()->locationHandler(), + $this->createMock(PasswordHashService::class), + $this->createMock(PasswordValidatorInterface::class), + $this->createMock(ConfigResolverInterface::class), + ] + ) + ->getMock(); + } + + /** + * @param \Ibexa\Contracts\Core\Repository\Repository&\PHPUnit\Framework\MockObject\MockObject $repository + * @param \Ibexa\Contracts\Core\Repository\UserService&\PHPUnit\Framework\MockObject\MockObject $userService + * @param \Ibexa\Contracts\Core\Repository\ContentService&\PHPUnit\Framework\MockObject\MockObject $contentService + * @param \Ibexa\Contracts\Core\Repository\Values\User\User&\PHPUnit\Framework\MockObject\MockObject $user + * @param \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo&\PHPUnit\Framework\MockObject\MockObject $contentInfo + * @param \Ibexa\Contracts\Core\Persistence\User\Handler&\PHPUnit\Framework\MockObject\MockObject $userHandler + */ + private function mockDeleteUserFlow( + Repository $repository, + APIUserService $userService, + APIContentService $contentService, + User $user, + APIContentInfo $contentInfo, + PersistenceUserHandler $userHandler + ): void { + $loadedUser = $this->createMock(APIUser::class); + $versionInfo = $this->createMock(APIVersionInfo::class); + + $user->method('__get')->with('id')->willReturn(self::MOCKED_USER_ID); + $versionInfo->method('getContentInfo')->willReturn($contentInfo); + $loadedUser->method('getVersionInfo')->willReturn($versionInfo); + $loadedUser->method('__get')->with('id')->willReturn(self::MOCKED_USER_ID); + + $userService->method('loadUser')->with(self::MOCKED_USER_ID)->willReturn($loadedUser); + + $userHandler + ->expects(self::once()) + ->method('loadRoleAssignmentsByGroupId') + ->with(self::MOCKED_USER_ID) + ->willReturn([new RoleAssignment(['id' => 1])]); + + $userHandler->method('removeRoleAssignment')->with(1); + + $repository->expects(self::once())->method('beginTransaction'); + $repository->expects(self::once())->method('getContentService')->willReturn($contentService); + } +} + +class_alias(UserTest::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\UserTest'); diff --git a/eZ/Publish/Core/Repository/Tests/Service/Mock/ValueStub.php b/tests/lib/Repository/Service/Mock/ValueStub.php similarity index 75% rename from eZ/Publish/Core/Repository/Tests/Service/Mock/ValueStub.php rename to tests/lib/Repository/Service/Mock/ValueStub.php index 581dc5a1ca..0a3393e434 100644 --- a/eZ/Publish/Core/Repository/Tests/Service/Mock/ValueStub.php +++ b/tests/lib/Repository/Service/Mock/ValueStub.php @@ -4,9 +4,9 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Tests\Service\Mock; +namespace Ibexa\Tests\Core\Repository\Service\Mock; -use eZ\Publish\Core\FieldType\Value as BaseValue; +use Ibexa\Core\FieldType\Value as BaseValue; /** * Value for TextLine field type. @@ -26,11 +26,10 @@ public function __construct($value) $this->value = $value; } - /** - * @see \eZ\Publish\Core\FieldType\Value - */ public function __toString() { return (string)$this->value; } } + +class_alias(ValueStub::class, 'eZ\Publish\Core\Repository\Tests\Service\Mock\ValueStub'); diff --git a/tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php b/tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php new file mode 100644 index 0000000000..0635a5106c --- /dev/null +++ b/tests/lib/Repository/SiteAccessAware/AbstractServiceTest.php @@ -0,0 +1,282 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\SiteAccessAware; + +use Closure; +use Ibexa\Contracts\Core\Repository\LanguageResolver; +use PHPUnit\Framework\TestCase; +use ReflectionClass; +use ReflectionMethod; + +/** + * Abstract tests for SiteAccessAware Services. + * + * Implies convention for methods on these services to either: + * - Do nothing, pass-through call and optionally (default:true) return value + * - lookup languages [IF not defined by callee] on one of the arguments given and pass it to next one. + */ +abstract class AbstractServiceTest extends TestCase +{ + /** + * Purely to attempt to make tests easier to read. + * + * As language parameter is ignored from providers and replced with values in tests, this is used to mark value of + * language argument instead of either askingproviders to use 0, or a valid language array which would then not be + * used. + */ + public const LANG_ARG = 0; + + /** @var \object|\PHPUnit\Framework\MockObject\MockObject */ + protected $innerApiServiceMock; + + /** @var object */ + protected $service; + + /** @var \Ibexa\Contracts\Core\Repository\LanguageResolver|\PHPUnit\Framework\MockObject\MockObject */ + protected $languageResolverMock; + + abstract public function getAPIServiceClassName(); + + abstract public function getSiteAccessAwareServiceClassName(); + + protected function setUp(): void + { + parent::setUp(); + $this->innerApiServiceMock = $this->getMockBuilder($this->getAPIServiceClassName())->getMock(); + $this->languageResolverMock = $this->getMockBuilder(LanguageResolver::class) + ->disableOriginalConstructor() + ->getMock(); + $serviceClassName = $this->getSiteAccessAwareServiceClassName(); + + $this->service = new $serviceClassName( + $this->innerApiServiceMock, + $this->languageResolverMock + ); + } + + protected function tearDown(): void + { + unset($this->service); + unset($this->languageResolverMock); + unset($this->innerApiServiceMock); + parent::tearDown(); + } + + /** + * @return array See signature on {@link testForPassTrough} for arguments and their type. + */ + abstract public function providerForPassTroughMethods(); + + /** + * Make sure these methods does nothing more then passing the arguments to inner service. + * + * Methods tested here are basically those without as languages argument. + * + * @dataProvider providerForPassTroughMethods + * + * @param string $method + * @param array $arguments + * @param mixed $return + */ + final public function testForPassTrough($method, array $arguments, $return = true) + { + if ($return) { + $this->innerApiServiceMock + ->expects($this->once()) + ->method($method) + ->with(...$arguments) + ->willReturn($return); + } else { + $this->innerApiServiceMock + ->expects($this->once()) + ->method($method) + ->with(...$arguments); + } + + $actualReturn = $this->service->$method(...$arguments); + + if ($return) { + $this->assertEquals($return, $actualReturn); + } + } + + /** + * @return array See signature on {@link testForLanguagesLookup} for arguments and their type. + * NOTE: languages / prioritizedLanguage, can be set to 0, it will be replaced by tests methods. + */ + abstract public function providerForLanguagesLookupMethods(); + + /** + * Method to be able to customize the logic for setting expected language argument during {@see testForLanguagesLookup()}. + * + * @param array $arguments + * @param int $languageArgumentIndex + * @param array $languages + * + * @return array + */ + protected function setLanguagesLookupExpectedArguments(array $arguments, $languageArgumentIndex, array $languages) + { + $arguments[$languageArgumentIndex] = $languages; + + return $arguments; + } + + /** + * Method to be able to customize the logic for setting expected language argument during {@see testForLanguagesLookup()}. + * + * @param array $arguments + * @param int $languageArgumentIndex + * + * @return array + */ + protected function setLanguagesLookupArguments(array $arguments, $languageArgumentIndex) + { + $arguments[$languageArgumentIndex] = []; + + return $arguments; + } + + /** + * Test that language aware methods does a language lookup when language is not set. + * + * @dataProvider providerForLanguagesLookupMethods + * + * @param string $method + * @param array $arguments + * @param mixed|null $return + * @param int $languageArgumentIndex From 0 and up, so the array index on $arguments. + */ + final public function testForLanguagesLookup($method, array $arguments, $return, $languageArgumentIndex, callable $callback = null, int $alwaysAvailableArgumentIndex = null) + { + $languages = ['eng-GB', 'eng-US']; + + $arguments = $this->setLanguagesLookupArguments($arguments, $languageArgumentIndex); + + $expectedArguments = $this->setLanguagesLookupExpectedArguments($arguments, $languageArgumentIndex, $languages); + + $this->languageResolverMock + ->expects($this->once()) + ->method('getPrioritizedLanguages') + ->with([]) + ->willReturn($languages); + + if ($alwaysAvailableArgumentIndex) { + $arguments[$alwaysAvailableArgumentIndex] = null; + $expectedArguments[$alwaysAvailableArgumentIndex] = true; + $this->languageResolverMock + ->expects($this->once()) + ->method('getUseAlwaysAvailable') + ->with(null) + ->willReturn(true); + } + + $this->innerApiServiceMock + ->expects($this->once()) + ->method($method) + ->with(...$expectedArguments) + ->willReturn($return); + + if ($callback instanceof Closure) { + $callback->bindTo($this, static::class)(true); + } + + $actualReturn = $this->service->$method(...$arguments); + + if ($return) { + $this->assertEquals($return, $actualReturn); + } + } + + /** + * Method to be able to customize the logic for setting expected language argument during {@see testForLanguagesPassTrough()}. + * + * @param array $arguments + * @param int $languageArgumentIndex + * @param array $languages + * + * @return array + */ + protected function setLanguagesPassTroughArguments(array $arguments, $languageArgumentIndex, array $languages) + { + $arguments[$languageArgumentIndex] = $languages; + + return $arguments; + } + + /** + * Make sure these methods does nothing more then passing the arguments to inner service. + * + * @dataProvider providerForLanguagesLookupMethods + * + * @param string $method + * @param array $arguments + * @param mixed|null $return + * @param int $languageArgumentIndex From 0 and up, so the array index on $arguments. + */ + final public function testForLanguagesPassTrough($method, array $arguments, $return, $languageArgumentIndex, callable $callback = null, int $alwaysAvailableArgumentIndex = null) + { + $languages = ['eng-GB', 'eng-US']; + $arguments = $this->setLanguagesPassTroughArguments($arguments, $languageArgumentIndex, $languages); + + $this->languageResolverMock + ->expects($this->once()) + ->method('getPrioritizedLanguages') + ->with($languages) + ->willReturn($languages); + + if ($alwaysAvailableArgumentIndex) { + $this->languageResolverMock + ->expects($this->once()) + ->method('getUseAlwaysAvailable') + ->with($arguments[$alwaysAvailableArgumentIndex]) + ->willReturn($arguments[$alwaysAvailableArgumentIndex]); + } + + $this->innerApiServiceMock + ->expects($this->once()) + ->method($method) + ->with(...$arguments) + ->willReturn($return); + + if ($callback instanceof Closure) { + $callback->bindTo($this, static::class)(false); + } + + $actualReturn = $this->service->$method(...$arguments); + + if ($return) { + $this->assertEquals($return, $actualReturn); + } + } + + /** + * @todo replace with coverage testing (see EZP-31035) + */ + final public function testIfThereIsMissingTest(): void + { + $tested = array_merge( + array_column($this->providerForLanguagesLookupMethods(), 0), + array_column($this->providerForPassTroughMethods(), 0) + ); + + $class = new ReflectionClass($this->getSiteAccessAwareServiceClassName()); + foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { + if (!$method->isConstructor() && !in_array($method->getShortName(), $tested)) { + $this->addWarning( + sprintf( + 'Test for the %s::%s method is missing', + $this->getSiteAccessAwareServiceClassName(), + $method->getName() + ) + ); + } + } + } +} + +class_alias(AbstractServiceTest::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Tests\AbstractServiceTest'); diff --git a/tests/lib/Repository/SiteAccessAware/ContentServiceTest.php b/tests/lib/Repository/SiteAccessAware/ContentServiceTest.php new file mode 100644 index 0000000000..ee0f691163 --- /dev/null +++ b/tests/lib/Repository/SiteAccessAware/ContentServiceTest.php @@ -0,0 +1,171 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\SiteAccessAware; + +use Ibexa\Contracts\Core\Repository\ContentService as APIService; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentDraftList; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentList; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentMetadataUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\ContentId; +use Ibexa\Contracts\Core\Repository\Values\Content\Relation; +use Ibexa\Contracts\Core\Repository\Values\Content\RelationList; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use Ibexa\Core\Repository\SiteAccessAware\ContentService; +use Ibexa\Core\Repository\Values\Content\ContentCreateStruct; +use Ibexa\Core\Repository\Values\Content\ContentUpdateStruct; +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Values\User\User; + +/** + * @property \Ibexa\Contracts\Core\Repository\ContentService $service + */ +class ContentServiceTest extends AbstractServiceTest +{ + public function getAPIServiceClassName() + { + return APIService::class; + } + + public function getSiteAccessAwareServiceClassName() + { + return ContentService::class; + } + + public function providerForPassTroughMethods(): array + { + $contentInfo = new ContentInfo(); + $versionInfo = new VersionInfo(); + $content = $this->createMock(Content::class); + $relation = $this->createMock(Relation::class); + $relationList = new RelationList(); + $contentCreateStruct = new ContentCreateStruct(); + $contentUpdateStruct = new ContentUpdateStruct(); + $contentMetaStruct = new ContentMetadataUpdateStruct(); + $locationCreateStruct = new LocationCreateStruct(); + $user = new User(); + $contentType = new ContentType(); + $language = new Language(); + + // string $method, array $arguments, bool $return = true + return [ + ['loadContentInfo', [42], $contentInfo], + ['loadContentInfoList', [[42]], [$contentInfo]], + + ['loadContentInfoByRemoteId', ['f348tj4gorgji4'], $contentInfo], + + ['loadVersionInfo', [$contentInfo], $versionInfo], + ['loadVersionInfo', [$contentInfo, 3], $versionInfo], + + ['loadVersionInfoById', [42], $versionInfo], + ['loadVersionInfoById', [42, 3], $versionInfo], + + ['createContent', [$contentCreateStruct], $content], + ['createContent', [$contentCreateStruct, [44]], $content], + + ['updateContentMetadata', [$contentInfo, $contentMetaStruct], $content], + + ['deleteContent', [$contentInfo], null], + + ['createContentDraft', [$contentInfo], $content], + ['createContentDraft', [$contentInfo, $versionInfo], $content], + ['createContentDraft', [$contentInfo, $versionInfo, $user], $content], + ['createContentDraft', [$contentInfo, $versionInfo, $user, $language], $content], + + ['countContentDrafts', [], 0], + ['countContentDrafts', [$user], 0], + + ['loadContentDrafts', [], [$content]], + ['loadContentDrafts', [$user], [$content]], + + ['loadContentDraftList', [], new ContentDraftList()], + ['loadContentDraftList', [$user], new ContentDraftList()], + ['loadContentDraftList', [$user, 1], new ContentDraftList()], + ['loadContentDraftList', [$user, 1, 25], new ContentDraftList()], + + ['updateContent', [$versionInfo, $contentUpdateStruct], $content], + + ['publishVersion', [$versionInfo], $content], + + ['deleteVersion', [$versionInfo], null], + + ['loadVersions', [$contentInfo], [$versionInfo]], + + ['copyContent', [$contentInfo, $locationCreateStruct], $content], + ['copyContent', [$contentInfo, $locationCreateStruct, $versionInfo], $content], + + ['loadRelations', [$versionInfo], [$relation]], + ['loadRelationList', [$versionInfo], $relationList], + ['countRelations', [$versionInfo], 0], + + ['countReverseRelations', [$contentInfo], 0], + + ['loadReverseRelations', [$contentInfo], $relationList], + ['loadReverseRelationList', [$contentInfo], $relationList], + + ['addRelation', [$versionInfo, $contentInfo], null], + + ['deleteRelation', [$versionInfo, $contentInfo], null], + + ['deleteTranslation', [$contentInfo, 'eng-GB'], null], + + ['deleteTranslationFromDraft', [$versionInfo, 'eng-GB'], $content], + + ['hideContent', [$contentInfo], null], + ['revealContent', [$contentInfo], null], + + ['newContentCreateStruct', [$contentType, 'eng-GB'], $contentCreateStruct], + ['newContentMetadataUpdateStruct', [], $contentMetaStruct], + ['newContentUpdateStruct', [], $contentUpdateStruct], + ['validate', [$contentUpdateStruct, []], []], + + ['loadVersionInfoListByContentInfo', [[$contentInfo]], [$versionInfo]], + ]; + } + + /** + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + */ + public function providerForLanguagesLookupMethods(): array + { + $content = $this->createMock(Content::class); + $contentInfo = new ContentInfo(); + $versionInfo = new VersionInfo(); + + $filter = new Filter(new ContentId(1)); + + // string $method, array $arguments, bool $return, int $languageArgumentIndex + return [ + ['loadContentByContentInfo', [$contentInfo], $content, 1], + ['loadContentByContentInfo', [$contentInfo, self::LANG_ARG, 4, false], $content, 1], + + ['loadContentByVersionInfo', [$versionInfo], $content, 1], + ['loadContentByVersionInfo', [$versionInfo, self::LANG_ARG, false], $content, 1], + + ['loadContent', [42], $content, 1], + ['loadContent', [42, self::LANG_ARG, 4, false], $content, 1], + + ['loadContentByRemoteId', ['f348tj4gorgji4'], $content, 1], + ['loadContentByRemoteId', ['f348tj4gorgji4', self::LANG_ARG, 4, false], $content, 1], + + ['loadContentListByContentInfo', [[$contentInfo]], [], 1], + ['loadContentListByContentInfo', [[$contentInfo], self::LANG_ARG, false], [], 1], + + ['find', [$filter], new ContentList(1, [$content]), 1], + ['find', [$filter, self::LANG_ARG], new ContentList(1, [$content]), 1], + + ['count', [$filter], 0, 1], + ['count', [$filter, self::LANG_ARG], 0, 1], + ]; + } +} + +class_alias(ContentServiceTest::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Tests\ContentServiceTest'); diff --git a/tests/lib/Repository/SiteAccessAware/ContentTypeServiceTest.php b/tests/lib/Repository/SiteAccessAware/ContentTypeServiceTest.php new file mode 100644 index 0000000000..5701ffb0db --- /dev/null +++ b/tests/lib/Repository/SiteAccessAware/ContentTypeServiceTest.php @@ -0,0 +1,131 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\SiteAccessAware; + +use Ibexa\Contracts\Core\Repository\ContentTypeService as APIService; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentTypeUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionUpdateStruct; +use Ibexa\Core\Repository\SiteAccessAware\ContentTypeService; +use Ibexa\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Values\ContentType\ContentTypeCreateStruct; +use Ibexa\Core\Repository\Values\ContentType\ContentTypeDraft; +use Ibexa\Core\Repository\Values\ContentType\ContentTypeGroup; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Repository\Values\User\User; + +class ContentTypeServiceTest extends AbstractServiceTest +{ + public function getAPIServiceClassName() + { + return APIService::class; + } + + public function getSiteAccessAwareServiceClassName() + { + return ContentTypeService::class; + } + + public function providerForPassTroughMethods() + { + $contentTypeGroupCreateStruct = new ContentTypeGroupCreateStruct(); + $contentTypeGroupUpdateStruct = new ContentTypeGroupUpdateStruct(); + $contentTypeGroup = new ContentTypeGroup(); + + $contentTypeCreateStruct = new ContentTypeCreateStruct(); + $contentTypeUpdateStruct = new ContentTypeUpdateStruct(); + $contentType = new ContentType(); + $contentTypeDraft = new ContentTypeDraft(); + + $fieldDefinition = new FieldDefinition(); + $fieldDefinitionCreateStruct = new FieldDefinitionCreateStruct(); + $fieldDefinitionUpdateStruct = new FieldDefinitionUpdateStruct(); + + $user = new User(); + + // string $method, array $arguments, bool $return = true + return [ + ['createContentTypeGroup', [$contentTypeGroupCreateStruct], $contentTypeGroup], + + ['updateContentTypeGroup', [$contentTypeGroup, $contentTypeGroupUpdateStruct], null], + + ['deleteContentTypeGroup', [$contentTypeGroup], null], + + ['createContentType', [$contentTypeCreateStruct, [$contentTypeGroup]], $contentTypeDraft], + + ['loadContentTypeDraft', [22], $contentTypeDraft], + + ['createContentTypeDraft', [$contentType], $contentTypeDraft], + + ['updateContentTypeDraft', [$contentTypeDraft, $contentTypeUpdateStruct], null], + + ['deleteContentType', [$contentType], null], + + ['copyContentType', [$contentType], $contentType], + ['copyContentType', [$contentType, $user], $contentType], + + ['assignContentTypeGroup', [$contentType, $contentTypeGroup], null], + + ['unassignContentTypeGroup', [$contentType, $contentTypeGroup], null], + + ['addFieldDefinition', [$contentTypeDraft, $fieldDefinitionCreateStruct], null], + + ['removeFieldDefinition', [$contentTypeDraft, $fieldDefinition], null], + + ['updateFieldDefinition', [$contentTypeDraft, $fieldDefinition, $fieldDefinitionUpdateStruct], null], + + ['publishContentTypeDraft', [$contentTypeDraft], null], + + ['newContentTypeGroupCreateStruct', ['media'], $contentTypeGroupCreateStruct], + + ['newContentTypeCreateStruct', ['blog'], $contentTypeCreateStruct], + + ['newContentTypeUpdateStruct', [], $contentTypeUpdateStruct], + + ['newContentTypeGroupUpdateStruct', [], $contentTypeGroupUpdateStruct], + + ['newFieldDefinitionCreateStruct', ['body', 'ezstring'], $fieldDefinitionCreateStruct], + + ['newFieldDefinitionUpdateStruct', [], $fieldDefinitionUpdateStruct], + + ['isContentTypeUsed', [$contentType], true], + + ['removeContentTypeTranslation', [$contentTypeDraft, 'ger-DE'], $contentTypeDraft], + + ['deleteUserDrafts', [14], null], + ]; + } + + public function providerForLanguagesLookupMethods() + { + $contentType = new ContentType(); + $contentTypeGroup = new ContentTypeGroup(); + + // string $method, array $arguments, bool $return, int $languageArgumentIndex + return [ + ['loadContentTypeGroup', [33, self::LANG_ARG], $contentTypeGroup, 1], + + ['loadContentTypeGroupByIdentifier', ['content', self::LANG_ARG], $contentTypeGroup, 1], + + ['loadContentTypeGroups', [self::LANG_ARG], [$contentTypeGroup], 0], + + ['loadContentType', [22, self::LANG_ARG], $contentType, 1], + + ['loadContentTypeList', [[22, self::LANG_ARG]], [$contentType], 1], + + ['loadContentTypeByIdentifier', ['article', self::LANG_ARG], $contentType, 1], + + ['loadContentTypeByRemoteId', ['w4ini3tn4f', self::LANG_ARG], $contentType, 1], + + ['loadContentTypes', [$contentTypeGroup, self::LANG_ARG], [$contentType], 1], + ]; + } +} + +class_alias(ContentTypeServiceTest::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Tests\ContentTypeServiceTest'); diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/Language/LanguageResolverTest.php b/tests/lib/Repository/SiteAccessAware/Language/LanguageResolverTest.php similarity index 83% rename from eZ/Publish/Core/Repository/SiteAccessAware/Tests/Language/LanguageResolverTest.php rename to tests/lib/Repository/SiteAccessAware/Language/LanguageResolverTest.php index 6737f612b4..7d4b47ad1b 100644 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/Language/LanguageResolverTest.php +++ b/tests/lib/Repository/SiteAccessAware/Language/LanguageResolverTest.php @@ -6,16 +6,18 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Repository\SiteAccessAware\Tests\Language; +namespace Ibexa\Tests\Core\Repository\SiteAccessAware\Language; -use eZ\Publish\Core\Repository\SiteAccessAware\Language\LanguageResolver; +use Ibexa\Core\Repository\SiteAccessAware\Language\LanguageResolver; use PHPUnit\Framework\TestCase; +/** + * @covers \Ibexa\Core\Repository\SiteAccessAware\Language\AbstractLanguageResolver + * @covers \Ibexa\Core\Repository\SiteAccessAware\Language\LanguageResolver + */ class LanguageResolverTest extends TestCase { /** - * @covers \eZ\Publish\Core\Repository\SiteAccessAware\Language\AbstractLanguageResolver::getPrioritizedLanguages - * * @dataProvider getDataForTestGetPrioritizedLanguages * * @param array $expectedPrioritizedLanguagesList @@ -75,3 +77,5 @@ public function getDataForTestGetPrioritizedLanguages(): array ]; } } + +class_alias(LanguageResolverTest::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Tests\Language\LanguageResolverTest'); diff --git a/tests/lib/Repository/SiteAccessAware/LanguageServiceTest.php b/tests/lib/Repository/SiteAccessAware/LanguageServiceTest.php new file mode 100644 index 0000000000..defce7d2f1 --- /dev/null +++ b/tests/lib/Repository/SiteAccessAware/LanguageServiceTest.php @@ -0,0 +1,64 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\SiteAccessAware; + +use Ibexa\Contracts\Core\Repository\LanguageService as APIService; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Contracts\Core\Repository\Values\Content\LanguageCreateStruct; +use Ibexa\Core\Repository\SiteAccessAware\LanguageService; + +class LanguageServiceTest extends AbstractServiceTest +{ + public function getAPIServiceClassName() + { + return APIService::class; + } + + public function getSiteAccessAwareServiceClassName() + { + return LanguageService::class; + } + + public function providerForPassTroughMethods() + { + $languageCreateStruct = new LanguageCreateStruct(); + $language = new Language(); + + // string $method, array $arguments, bool $return = true + return [ + ['createLanguage', [$languageCreateStruct], $language], + + ['updateLanguageName', [$language, 'Afrikaans'], $language], + + ['enableLanguage', [$language], $language], + + ['disableLanguage', [$language], $language], + + ['loadLanguage', ['eng-GB'], $language], + ['loadLanguageListByCode', [['eng-GB']], []], + + ['loadLanguages', [], []], + + ['loadLanguageById', [4], $language], + ['loadLanguageListById', [[4]], []], + + ['deleteLanguage', [$language], null], + + ['getDefaultLanguageCode', [], ''], + + ['newLanguageCreateStruct', [], $languageCreateStruct], + ]; + } + + public function providerForLanguagesLookupMethods() + { + // string $method, array $arguments, bool $return, int $languageArgumentIndex + return []; + } +} + +class_alias(LanguageServiceTest::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Tests\LanguageServiceTest'); diff --git a/tests/lib/Repository/SiteAccessAware/LocationServiceTest.php b/tests/lib/Repository/SiteAccessAware/LocationServiceTest.php new file mode 100644 index 0000000000..532cd536f8 --- /dev/null +++ b/tests/lib/Repository/SiteAccessAware/LocationServiceTest.php @@ -0,0 +1,111 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\SiteAccessAware; + +use Ibexa\Contracts\Core\Repository\LocationService as APIService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationList; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\LocationId; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use Ibexa\Core\Repository\SiteAccessAware\LocationService; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Repository\Values\Content\VersionInfo; + +class LocationServiceTest extends AbstractServiceTest +{ + public function getAPIServiceClassName() + { + return APIService::class; + } + + public function getSiteAccessAwareServiceClassName() + { + return LocationService::class; + } + + public function providerForPassTroughMethods(): array + { + $location = new Location(); + $contentInfo = new ContentInfo(); + $locationCreateStruct = new LocationCreateStruct(); + $locationUpdateStruct = new LocationUpdateStruct(); + + // string $method, array $arguments, bool $return = true + return [ + ['copySubtree', [$location, $location], $location], + + ['getLocationChildCount', [$location], 100], + + ['getSubtreeSize', [$location], 100], + + ['createLocation', [$contentInfo, $locationCreateStruct], $location], + + ['updateLocation', [$location, $locationUpdateStruct], $location], + + ['swapLocation', [$location, $location], null], + + ['hideLocation', [$location], $location], + + ['unhideLocation', [$location], $location], + + ['moveSubtree', [$location, $location], null], + + ['deleteLocation', [$location], null], + + ['newLocationCreateStruct', [55], new LocationCreateStruct()], + + ['newLocationUpdateStruct', [], new LocationUpdateStruct()], + + ['getAllLocationsCount', [], 100], + ['loadAllLocations', [10, 100], [$location]], + ]; + } + + public function providerForLanguagesLookupMethods(): array + { + $location = new Location(); + $locationList = new LocationList(); + $contentInfo = new ContentInfo(); + $versionInfo = new VersionInfo(); + + $filter = new Filter(new LocationId(1)); + + // string $method, array $arguments, mixed|null $return, int $languageArgumentIndex, ?callable $callback, ?int $alwaysAvailableArgumentIndex + return [ + ['loadLocation', [55], $location, 1], + ['loadLocation', [55, self::LANG_ARG], $location, 1], + ['loadLocation', [55, self::LANG_ARG, true], $location, 1, null, 2], + + ['loadLocationList', [[55]], [55 => $location], 1], + ['loadLocationList', [[55], self::LANG_ARG], [55 => $location], 1], + ['loadLocationList', [[55], self::LANG_ARG, true], [55 => $location], 1, null, 2], + + ['loadLocationByRemoteId', ['ergemiotregf'], $location, 1], + ['loadLocationByRemoteId', ['ergemiotregf', self::LANG_ARG], $location, 1], + ['loadLocationByRemoteId', ['ergemiotregf', self::LANG_ARG, true], $location, 1, null, 2], + + ['loadLocations', [$contentInfo, null], [$location], 2], + ['loadLocations', [$contentInfo, $location, self::LANG_ARG], [$location], 2], + + ['loadLocationChildren', [$location, 0, 15], $locationList, 3], + ['loadLocationChildren', [$location, 50, 50, self::LANG_ARG], $locationList, 3], + + ['loadParentLocationsForDraftContent', [$versionInfo], [$location], 1], + ['loadParentLocationsForDraftContent', [$versionInfo, self::LANG_ARG], [$location], 1], + + ['find', [$filter], $locationList, 1], + ['find', [$filter, self::LANG_ARG], $locationList, 1], + + ['count', [$filter], 0, 1], + ['count', [$filter, self::LANG_ARG], 0, 1], + ]; + } +} + +class_alias(LocationServiceTest::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Tests\LocationServiceTest'); diff --git a/tests/lib/Repository/SiteAccessAware/ObjectStateServiceTest.php b/tests/lib/Repository/SiteAccessAware/ObjectStateServiceTest.php new file mode 100644 index 0000000000..8720c0b73f --- /dev/null +++ b/tests/lib/Repository/SiteAccessAware/ObjectStateServiceTest.php @@ -0,0 +1,82 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\SiteAccessAware; + +use Ibexa\Contracts\Core\Repository\ObjectStateService as APIService; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupCreateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateUpdateStruct; +use Ibexa\Core\Repository\SiteAccessAware\ObjectStateService; +use Ibexa\Core\Repository\Values\ObjectState\ObjectState; +use Ibexa\Core\Repository\Values\ObjectState\ObjectStateGroup; + +class ObjectStateServiceTest extends AbstractServiceTest +{ + public function getAPIServiceClassName() + { + return APIService::class; + } + + public function getSiteAccessAwareServiceClassName() + { + return ObjectStateService::class; + } + + public function providerForPassTroughMethods() + { + $objectStateGroupCreateStruct = new ObjectStateGroupCreateStruct(); + $objectStateGroupUpdateStruct = new ObjectStateGroupUpdateStruct(); + $objectStateGroup = new ObjectStateGroup(); + + $objectStateCreateStruct = new ObjectStateCreateStruct(); + $objectStateUpdateStruct = new ObjectStateUpdateStruct(); + $objectState = new ObjectState(); + + $contentInfo = new ContentInfo(); + + // string $method, array $arguments, mixed $return = true + return [ + ['createObjectStateGroup', [$objectStateGroupCreateStruct], $objectStateGroup], + ['updateObjectStateGroup', [$objectStateGroup, $objectStateGroupUpdateStruct], $objectStateGroup], + ['deleteObjectStateGroup', [$objectStateGroup], null], + + ['createObjectState', [$objectStateGroup, $objectStateCreateStruct], $objectState], + ['updateObjectState', [$objectState, $objectStateUpdateStruct], $objectState], + ['setPriorityOfObjectState', [$objectState, 4], null], + ['deleteObjectState', [$objectState], null], + + ['setContentState', [$contentInfo, $objectStateGroup, $objectState], null], + ['getContentState', [$contentInfo, $objectStateGroup], $objectState], + ['getContentCount', [$objectState], 100], + + ['newObjectStateGroupCreateStruct', ['locker'], $objectStateGroupCreateStruct], + ['newObjectStateGroupUpdateStruct', [], $objectStateGroupUpdateStruct], + ['newObjectStateCreateStruct', ['locked'], $objectStateCreateStruct], + ['newObjectStateUpdateStruct', [], $objectStateUpdateStruct], + ]; + } + + public function providerForLanguagesLookupMethods() + { + $objectStateGroup = new ObjectStateGroup(); + $objectState = new ObjectState(); + + // string $method, array $arguments, mixed $return, int $languageArgumentIndex + return [ + ['loadObjectStateGroup', [11, self::LANG_ARG], $objectStateGroup, 1], + ['loadObjectStateGroupByIdentifier', ['ez_lock', self::LANG_ARG], $objectStateGroup, 1], + ['loadObjectStateGroups', [50, 50, self::LANG_ARG], [$objectStateGroup], 2], + ['loadObjectStates', [$objectStateGroup, self::LANG_ARG], [$objectState], 1], + ['loadObjectState', [3, self::LANG_ARG], $objectState, 1], + ['loadObjectStateByIdentifier', [$objectStateGroup, 'locked', self::LANG_ARG], $objectState, 2], + ]; + } +} + +class_alias(ObjectStateServiceTest::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Tests\ObjectStateServiceTest'); diff --git a/tests/lib/Repository/SiteAccessAware/SearchServiceTest.php b/tests/lib/Repository/SiteAccessAware/SearchServiceTest.php new file mode 100644 index 0000000000..a5342a4f37 --- /dev/null +++ b/tests/lib/Repository/SiteAccessAware/SearchServiceTest.php @@ -0,0 +1,93 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\SiteAccessAware; + +use Ibexa\Contracts\Core\Repository\SearchService as APIService; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Search\SearchResult; +use Ibexa\Core\Repository\SiteAccessAware\SearchService; +use Ibexa\Core\Repository\Values\Content\Content; + +class SearchServiceTest extends AbstractServiceTest +{ + public function getAPIServiceClassName() + { + return APIService::class; + } + + public function getSiteAccessAwareServiceClassName() + { + return SearchService::class; + } + + public function providerForPassTroughMethods() + { + // string $method, array $arguments, bool $return = true + return [ + ['suggest', ['prefix', [], 11]], + ['supports', [SearchService::CAPABILITY_ADVANCED_FULLTEXT]], + ]; + } + + public function providerForLanguagesLookupMethods() + { + $query = new Query(); + $locationQuery = new LocationQuery(); + $criterion = new Query\Criterion\ContentId(44); + $content = new Content(); + $searchResults = new SearchResult(); + + $callback = function ($languageLookup) { + $this->languageResolverMock + ->expects($this->once()) + ->method('getUseAlwaysAvailable') + ->with($languageLookup ? null : true) + ->willReturn(true); + }; + + // string $method, array $arguments, bool $return, int $languageArgumentIndex, callable $callback + return [ + ['findContent', [$query, self::LANG_ARG, false], $searchResults, 1, $callback], + ['findContentInfo', [$query, self::LANG_ARG, false], $searchResults, 1, $callback], + ['findSingle', [$criterion, self::LANG_ARG, false], $content, 1, $callback], + ['findLocations', [$locationQuery, self::LANG_ARG, false], $searchResults, 1, $callback], + ]; + } + + protected function setLanguagesLookupArguments(array $arguments, $languageArgumentIndex) + { + $arguments[$languageArgumentIndex] = [ + 'languages' => [], + 'useAlwaysAvailable' => null, + ]; + + return $arguments; + } + + protected function setLanguagesLookupExpectedArguments(array $arguments, $languageArgumentIndex, array $languages) + { + $arguments[$languageArgumentIndex] = [ + 'languages' => $languages, + 'useAlwaysAvailable' => true, + ]; + + return $arguments; + } + + protected function setLanguagesPassTroughArguments(array $arguments, $languageArgumentIndex, array $languages) + { + $arguments[$languageArgumentIndex] = [ + 'languages' => $languages, + 'useAlwaysAvailable' => true, + ]; + + return $arguments; + } +} + +class_alias(SearchServiceTest::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Tests\SearchServiceTest'); diff --git a/tests/lib/Repository/SiteAccessAware/TrashServiceTest.php b/tests/lib/Repository/SiteAccessAware/TrashServiceTest.php new file mode 100644 index 0000000000..2e6c172821 --- /dev/null +++ b/tests/lib/Repository/SiteAccessAware/TrashServiceTest.php @@ -0,0 +1,58 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\SiteAccessAware; + +use Ibexa\Contracts\Core\Repository\TrashService as APIService; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Trash\SearchResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResult; +use Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResultList; +use Ibexa\Core\Repository\SiteAccessAware\TrashService; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Repository\Values\Content\TrashItem; + +class TrashServiceTest extends AbstractServiceTest +{ + public function getAPIServiceClassName() + { + return APIService::class; + } + + public function getSiteAccessAwareServiceClassName() + { + return TrashService::class; + } + + public function providerForPassTroughMethods() + { + $location = new Location(); + $newLocation = new Location(); + $trashItem = new TrashItem(); + $query = new Query(); + $searchResult = new SearchResult(); + $trashItemDeleteResult = new TrashItemDeleteResult(); + $trashItemDeleteResultList = new TrashItemDeleteResultList(); + + // string $method, array $arguments, bool $return = true + return [ + ['loadTrashItem', [22], $trashItem], + ['trash', [$location], $trashItem], + ['recover', [$trashItem, $location], $newLocation], + ['emptyTrash', [], $trashItemDeleteResultList], + ['deleteTrashItem', [$trashItem], $trashItemDeleteResult], + ['findTrashItems', [$query], $searchResult], + ]; + } + + public function providerForLanguagesLookupMethods() + { + // string $method, array $arguments, bool $return, int $languageArgumentIndex + return []; + } +} + +class_alias(TrashServiceTest::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Tests\TrashServiceTest'); diff --git a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/UrlAliasServiceTest.php b/tests/lib/Repository/SiteAccessAware/UrlAliasServiceTest.php similarity index 85% rename from eZ/Publish/Core/Repository/SiteAccessAware/Tests/UrlAliasServiceTest.php rename to tests/lib/Repository/SiteAccessAware/UrlAliasServiceTest.php index 5c5ae40940..0714f10703 100644 --- a/eZ/Publish/Core/Repository/SiteAccessAware/Tests/UrlAliasServiceTest.php +++ b/tests/lib/Repository/SiteAccessAware/UrlAliasServiceTest.php @@ -4,12 +4,12 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\SiteAccessAware\Tests; +namespace Ibexa\Tests\Core\Repository\SiteAccessAware; -use eZ\Publish\API\Repository\URLAliasService as APIService; -use eZ\Publish\API\Repository\Values\Content\URLAlias; -use eZ\Publish\Core\Repository\SiteAccessAware\URLAliasService; -use eZ\Publish\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\URLAliasService as APIService; +use Ibexa\Contracts\Core\Repository\Values\Content\URLAlias; +use Ibexa\Core\Repository\SiteAccessAware\URLAliasService; +use Ibexa\Core\Repository\Values\Content\Location; class UrlAliasServiceTest extends AbstractServiceTest { @@ -74,3 +74,5 @@ protected function setLanguagesPassTroughArguments(array $arguments, $languageAr return $this->setLanguagesLookupExpectedArguments($arguments, $languageArgumentIndex, $languages); } } + +class_alias(UrlAliasServiceTest::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Tests\UrlAliasServiceTest'); diff --git a/tests/lib/Repository/SiteAccessAware/UserServiceTest.php b/tests/lib/Repository/SiteAccessAware/UserServiceTest.php new file mode 100644 index 0000000000..fc1c3260ed --- /dev/null +++ b/tests/lib/Repository/SiteAccessAware/UserServiceTest.php @@ -0,0 +1,107 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\SiteAccessAware; + +use DateInterval; +use DateTime; +use Ibexa\Contracts\Core\Repository\UserService as APIService; +use Ibexa\Contracts\Core\Repository\Values\User\PasswordInfo; +use Ibexa\Contracts\Core\Repository\Values\User\PasswordValidationContext; +use Ibexa\Contracts\Core\Repository\Values\User\UserGroupUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserTokenUpdateStruct; +use Ibexa\Contracts\Core\Repository\Values\User\UserUpdateStruct; +use Ibexa\Core\Repository\SiteAccessAware\UserService; +use Ibexa\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Values\User\User; +use Ibexa\Core\Repository\Values\User\UserCreateStruct; +use Ibexa\Core\Repository\Values\User\UserGroup; +use Ibexa\Core\Repository\Values\User\UserGroupCreateStruct; + +class UserServiceTest extends AbstractServiceTest +{ + public function getAPIServiceClassName() + { + return APIService::class; + } + + public function getSiteAccessAwareServiceClassName() + { + return UserService::class; + } + + public function providerForPassTroughMethods() + { + $userGroupCreateStruct = new UserGroupCreateStruct(); + $userGroupUpdateStruct = new UserGroupUpdateStruct(); + $userGroup = new UserGroup(); + $userGroupId = 1; + + $userCreateStruct = new UserCreateStruct(); + $userUpdateStruct = new UserUpdateStruct(); + $userTokenUpdateStruct = new UserTokenUpdateStruct(); + $user = new User(); + $userId = 14; + $contentType = $this->createMock(ContentType::class); + + $passwordValidationContext = new PasswordValidationContext(); + $passwordExpirationDate = (new DateTime())->add(new DateInterval('P30D')); + $passwordExpirationWarningDate = (new DateTime())->add(new DateInterval('P16D')); + + // string $method, array $arguments, bool $return = true + return [ + ['createUserGroup', [$userGroupCreateStruct, $userGroup], $userGroup], + ['deleteUserGroup', [$userGroup], [$userGroupId]], + ['moveUserGroup', [$userGroup, $userGroup], null], + ['updateUserGroup', [$userGroup, $userGroupUpdateStruct], $userGroup], + + ['createUser', [$userCreateStruct, [$userGroup]], $user], + ['deleteUser', [$user], [$userId]], + ['updateUser', [$user, $userUpdateStruct], $user], + ['updateUserPassword', [$user, 'H@xi0r!'], $user], + + ['assignUserToUserGroup', [$user, $userGroup], null], + ['unAssignUserFromUserGroup', [$user, $userGroup], null], + + ['updateUserToken', [$user, $userTokenUpdateStruct], $user], + ['expireUserToken', ['43ir43jrt43'], null], + + ['newUserCreateStruct', ['adam', 'adam@gmail.com', 'Eve', 'eng-AU', $contentType], $userCreateStruct], + ['newUserGroupCreateStruct', ['eng-AU', $contentType], $userGroupCreateStruct], + ['newUserUpdateStruct', [], $userUpdateStruct], + ['newUserGroupUpdateStruct', [], $userGroupUpdateStruct], + + ['isUser', [$userGroup]], + ['isUserGroup', [$userGroup]], + + ['checkUserCredentials', [$user, 'H@xi0r!']], + ['validatePassword', ['H@xi0r!', $passwordValidationContext], []], + ['getPasswordInfo', [$user], new PasswordInfo()], + ]; + } + + public function providerForLanguagesLookupMethods() + { + $userGroup = new UserGroup(); + $user = new User(); + + // string $method, array $arguments, bool $return, int $languageArgumentIndex + return [ + ['loadUserGroup', [4, self::LANG_ARG], $userGroup, 1], + ['loadUserGroupByRemoteId', ['5f7f0bdb3381d6a461d8c29ff53d908f', self::LANG_ARG], $userGroup, 1], + ['loadSubUserGroups', [$userGroup, 50, 50, self::LANG_ARG], [$userGroup], 3], + ['loadUser', [14, self::LANG_ARG], $user, 1], + ['loadUserByLogin', ['admin', self::LANG_ARG], $user, 1], + ['loadUserByEmail', ['admin@link.invalid', self::LANG_ARG], $user, 1], + ['loadUsersByEmail', ['admin@link.invalid', self::LANG_ARG], [$user], 1], + ['loadUserGroupsOfUser', [$user, 50, 50, self::LANG_ARG], [$userGroup], 3], + ['loadUsersOfUserGroup', [$userGroup, 50, 50, self::LANG_ARG], [$user], 3], + ['loadUserByToken', ['43ir43jrt43', self::LANG_ARG], $user, 1], + ]; + } +} + +class_alias(UserServiceTest::class, 'eZ\Publish\Core\Repository\SiteAccessAware\Tests\UserServiceTest'); diff --git a/tests/lib/Repository/User/PasswordHashServiceTest.php b/tests/lib/Repository/User/PasswordHashServiceTest.php new file mode 100644 index 0000000000..99f9a243ee --- /dev/null +++ b/tests/lib/Repository/User/PasswordHashServiceTest.php @@ -0,0 +1,82 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\User; + +use Ibexa\Contracts\Core\Repository\Values\User\User; +use Ibexa\Core\Repository\User\Exception\UnsupportedPasswordHashType; +use Ibexa\Core\Repository\User\PasswordHashService; +use PHPUnit\Framework\TestCase; + +final class PasswordHashServiceTest extends TestCase +{ + private const NON_EXISTING_PASSWORD_HASH = PHP_INT_MAX; + + /** @var \Ibexa\Core\Repository\User\PasswordHashService */ + private $passwordHashService; + + protected function setUp(): void + { + $this->passwordHashService = new PasswordHashService(); + } + + public function testGetSupportedHashTypes(): void + { + $this->assertEquals( + [ + User::PASSWORD_HASH_BCRYPT, + User::PASSWORD_HASH_PHP_DEFAULT, + ], + $this->passwordHashService->getSupportedHashTypes() + ); + } + + public function testIsHashTypeSupported(): void + { + $this->assertTrue($this->passwordHashService->isHashTypeSupported(User::DEFAULT_PASSWORD_HASH)); + $this->assertFalse($this->passwordHashService->isHashTypeSupported(self::NON_EXISTING_PASSWORD_HASH)); + } + + public function testCreatePasswordHashExceptionHidesSensitiveParameter(): void + { + $ignoreArgs = ini_get('zend.exception_ignore_args'); + $paramMax = ini_get('zend.exception_string_param_max_len'); + + ini_set('zend.exception_ignore_args', '0'); + ini_set('zend.exception_string_param_max_len', '10'); + + $password = 'secret'; + + try { + $this->passwordHashService->createPasswordHash($password, self::NON_EXISTING_PASSWORD_HASH); + self::fail(sprintf( + 'Expected exception %s to be thrown.', + UnsupportedPasswordHashType::class, + )); + } catch (UnsupportedPasswordHashType $e) { + $stackTrace = $e->getTrace(); + self::assertIsArray($stackTrace); + self::assertGreaterThan(1, count($stackTrace)); + self::assertArrayHasKey('function', $stackTrace[0]); + self::assertEquals('createPasswordHash', $stackTrace[0]['function']); + self::assertArrayHasKey('args', $stackTrace[0]); + + // SensitiveParameter was introduced in PHP 8.2, in older versions it is ignored + if (\PHP_VERSION_ID < 80200) { + self::assertEquals($password, $stackTrace[0]['args'][0]); + } else { + self::assertInstanceOf(\SensitiveParameterValue::class, $stackTrace[0]['args'][0]); + } + } + + ini_set('zend.exception_ignore_args', (string)$ignoreArgs); + ini_set('zend.exception_string_param_max_len', (string)$paramMax); + } +} + +class_alias(PasswordHashServiceTest::class, 'eZ\Publish\Core\Repository\Tests\User\PasswordHashServiceTest'); diff --git a/tests/lib/Repository/Validator/TargetContentValidatorTest.php b/tests/lib/Repository/Validator/TargetContentValidatorTest.php index d9bd171a93..b7c0f07e1a 100644 --- a/tests/lib/Repository/Validator/TargetContentValidatorTest.php +++ b/tests/lib/Repository/Validator/TargetContentValidatorTest.php @@ -8,18 +8,18 @@ namespace Ibexa\Tests\Core\Repository\Validator; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\Core\FieldType\ValidationError; -use eZ\Publish\SPI\Persistence\Content; +use Ibexa\Contracts\Core\FieldType\ValidationError; +use Ibexa\Contracts\Core\Persistence\Content; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; use Ibexa\Core\Repository\Validator\TargetContentValidator; use PHPUnit\Framework\TestCase; final class TargetContentValidatorTest extends TestCase { - /** @var \eZ\Publish\SPI\Persistence\Content\Handler|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Handler|\PHPUnit_Framework_MockObject_MockObject */ private $contentHandler; - /** @var \eZ\Publish\SPI\Persistence\Content\Type\Handler|\PHPUnit_Framework_MockObject_MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Type\Handler|\PHPUnit_Framework_MockObject_MockObject */ private $contentTypeHandler; /** @var \Ibexa\Core\Repository\Validator\TargetContentValidator */ diff --git a/tests/lib/Repository/Values/Content/ContentInfoTest.php b/tests/lib/Repository/Values/Content/ContentInfoTest.php new file mode 100644 index 0000000000..9f02ad8c10 --- /dev/null +++ b/tests/lib/Repository/Values/Content/ContentInfoTest.php @@ -0,0 +1,60 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Values\Content; + +use DateTimeImmutable; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo + */ +final class ContentInfoTest extends TestCase +{ + public function testCreateObject(): void + { + $dateTime = new DateTimeImmutable(); + $contentInfo = new ContentInfo( + [ + 'id' => 1, + 'contentTypeId' => 2, + 'name' => 'foo', + 'sectionId' => 1, + 'currentVersionNo' => 1, + 'status' => 1, + 'ownerId' => 10, + 'modificationDate' => $dateTime, + 'publishedDate' => $dateTime, + 'alwaysAvailable' => false, + 'remoteId' => '1qaz2wsx', + 'mainLanguageCode' => 'eng-GB', + 'mainLocationId' => 2, + 'isHidden' => true, + ] + ); + + $dateFormatted = $dateTime->format('c'); + self::assertSame(1, $contentInfo->getId()); + self::assertSame(2, $contentInfo->contentTypeId); + self::assertSame('foo', $contentInfo->getName()); + self::assertSame(1, $contentInfo->getSectionId()); + self::assertSame(1, $contentInfo->currentVersionNo); + self::assertTrue($contentInfo->isPublished()); + self::assertSame(10, $contentInfo->ownerId); + self::assertSame($dateFormatted, $contentInfo->modificationDate->format('c')); + self::assertSame($dateFormatted, $contentInfo->publishedDate->format('c')); + self::assertFalse($contentInfo->alwaysAvailable); + self::assertSame('1qaz2wsx', $contentInfo->remoteId); + self::assertSame('eng-GB', $contentInfo->getMainLanguageCode()); + self::assertSame(2, $contentInfo->getMainLocationId()); + self::assertTrue($contentInfo->isHidden()); + } +} + +class_alias(ContentInfoTest::class, 'eZ\Publish\Core\Repository\Tests\Values\Content\ContentInfoTest'); diff --git a/tests/lib/Repository/Values/Content/ContentTest.php b/tests/lib/Repository/Values/Content/ContentTest.php index 247ff7e0ae..fc0f284fbb 100644 --- a/tests/lib/Repository/Values/Content/ContentTest.php +++ b/tests/lib/Repository/Values/Content/ContentTest.php @@ -8,22 +8,22 @@ namespace Ibexa\Tests\Core\Repository\Values\Content; -use eZ\Publish\API\Repository\Values\Content\Field; -use eZ\Publish\Core\FieldType\TextLine\Value as TextLineValue; -use eZ\Publish\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Field; +use Ibexa\Core\FieldType\TextLine\Value as TextLineValue; +use Ibexa\Core\Repository\Values\Content\Content; +use Ibexa\Core\Repository\Values\Content\VersionInfo; use PHPUnit\Framework\TestCase; /** - * @see \eZ\Publish\Core\Repository\Tests\Values\Content\ContentTest for Legacy set of unit tests. - * - * @covers \eZ\Publish\Core\Repository\Values\Content\Content + * @covers \Ibexa\Core\Repository\Values\Content\Content */ final class ContentTest extends TestCase { - /** @var \eZ\Publish\API\Repository\Values\Content\Field[] */ + /** @var \Ibexa\Contracts\Core\Repository\Values\Content\Field[] */ private $internalFields; - /** @var \eZ\Publish\Core\Repository\Values\Content\Content */ + /** @var \Ibexa\Core\Repository\Values\Content\Content */ private $content; protected function setUp(): void @@ -90,4 +90,57 @@ public function testGetFieldsByLanguage(): void $this->content->getFieldsByLanguage('pol-PL') ); } + + public function testObjectProperties(): void + { + $object = new Content(['internalFields' => []]); + $properties = $object->attributes(); + self::assertNotContains('internalFields', $properties, 'Internal property found '); + self::assertContains('id', $properties, 'Property not found '); + self::assertContains('fields', $properties, 'Property not found '); + self::assertContains('versionInfo', $properties, 'Property not found '); + self::assertContains('contentInfo', $properties, 'Property not found '); + + // check for duplicates and double check existence of property + $propertiesHash = []; + foreach ($properties as $property) { + if (isset($propertiesHash[$property])) { + self::fail("Property '{$property}' exists several times in properties list"); + } elseif (!isset($object->$property)) { + self::fail("Property '{$property}' does not exist on object, even though it was hinted to be there"); + } + $propertiesHash[$property] = 1; + } + } + + public function testStrictGetters(): void + { + $contentInfo = new ContentInfo(['id' => 123]); + $content = new Content( + [ + 'versionInfo' => new VersionInfo( + [ + 'contentInfo' => $contentInfo, + ] + ), + ] + ); + self::assertEquals(123, $content->getId()); + self::assertEquals($contentInfo, $content->getContentInfo()); + } + + public function testGetName(): void + { + $name = 'Translated name'; + $versionInfoMock = $this->createMock(VersionInfo::class); + $versionInfoMock->expects($this->once()) + ->method('getName') + ->willReturn($name); + + $object = new Content(['versionInfo' => $versionInfoMock]); + + $this->assertEquals($name, $object->getName()); + } } + +class_alias(ContentTest::class, 'eZ\Publish\Core\Repository\Tests\Values\Content\ContentTest'); diff --git a/tests/lib/Repository/Values/Content/LanguageTest.php b/tests/lib/Repository/Values/Content/LanguageTest.php new file mode 100644 index 0000000000..cc023cc16e --- /dev/null +++ b/tests/lib/Repository/Values/Content/LanguageTest.php @@ -0,0 +1,93 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Values\Content; + +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyReadOnlyException; +use Ibexa\Contracts\Core\Repository\Values\Content\Language; +use Ibexa\Tests\Core\Repository\Values\ValueObjectTestTrait; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Contracts\Core\Repository\Values\Content\Language + */ +class LanguageTest extends TestCase +{ + use ValueObjectTestTrait; + + /** + * Test default properties of just created class. + */ + public function testNewClass() + { + $language = new Language(); + + $this->assertPropertiesCorrect( + [ + 'id' => null, + 'languageCode' => null, + 'name' => null, + 'enabled' => null, + ], + $language + ); + } + + /** + * Test retrieving missing property. + */ + public function testMissingProperty() + { + $this->expectException(PropertyNotFoundException::class); + $this->expectExceptionMessage('Property \'notDefined\' not found on class'); + + $language = new Language(); + $value = $language->notDefined; + self::fail('Succeeded getting non existing property'); + } + + /** + * Test setting read only property. + */ + public function testReadOnlyProperty() + { + $this->expectException(PropertyReadOnlyException::class); + $this->expectExceptionMessage('Property \'id\' is readonly on class'); + + $language = new Language(); + $language->id = 42; + self::fail('Succeeded setting read only property'); + } + + /** + * Test if property exists. + */ + public function testIsPropertySet() + { + $language = new Language(); + $value = isset($language->notDefined); + self::assertFalse($value); + + $value = isset($language->id); + self::assertTrue($value); + } + + /** + * Test unsetting a property. + */ + public function testUnsetProperty() + { + $this->expectException(PropertyReadOnlyException::class); + $this->expectExceptionMessage('Property \'id\' is readonly on class'); + + $language = new Language(['id' => 2]); + unset($language->id); + self::fail('Unsetting read-only property succeeded'); + } +} + +class_alias(LanguageTest::class, 'eZ\Publish\API\Repository\Tests\Values\Content\LanguageTest'); diff --git a/tests/lib/Repository/Values/Content/LocationTest.php b/tests/lib/Repository/Values/Content/LocationTest.php new file mode 100644 index 0000000000..082d8a796b --- /dev/null +++ b/tests/lib/Repository/Values/Content/LocationTest.php @@ -0,0 +1,170 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Values\Content; + +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyReadOnlyException; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Tests\Core\Repository\Values\ValueObjectTestTrait; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Repository\Values\Content\Location + */ +final class LocationTest extends TestCase +{ + use ValueObjectTestTrait; + + /** + * Test a new class and default values on properties. + */ + public function testNewClass(): void + { + $location = new Location(); + + $this->assertPropertiesCorrect( + [ + 'id' => null, + 'contentInfo' => null, + 'priority' => null, + 'hidden' => null, + 'invisible' => null, + 'remoteId' => null, + 'parentLocationId' => null, + 'pathString' => null, + 'path' => [], + 'depth' => null, + 'sortField' => null, + 'sortOrder' => null, + ], + $location + ); + } + + public function testStrictGetters(): void + { + $location = new Location( + [ + 'id' => 123, + 'contentInfo' => new ContentInfo(['id' => 456]), + 'hidden' => true, + 'depth' => 3, + 'pathString' => '/1/2/123/', + ] + ); + + self::assertSame(123, $location->getId()); + self::assertSame(456, $location->getContentId()); + self::assertTrue($location->isHidden()); + self::assertSame(3, $location->getDepth()); + self::assertSame('/1/2/123/', $location->getPathString()); + } + + /** + * @return iterable<string, array{\Ibexa\Core\Repository\Values\Content\Location, string[]}> + */ + public static function getDataForTestPathComputedPropertyGetter(): iterable + { + yield 'nested path' => [ + new Location(['id' => 3, 'pathString' => '/1/2/3/']), + ['1', '2', '3'], + ]; + + yield 'nested path no trailing slash' => [ + new Location(['id' => 4, 'pathString' => '/1/2/4']), + ['1', '2', '4'], + ]; + + yield 'root element' => [ + new Location(['id' => 1, 'pathString' => '/1/']), + ['1'], + ]; + + yield 'malformed path' => [ + new Location(['id' => 1, 'pathString' => '/']), + [], + ]; + + yield 'empty path' => [ + new Location(['id' => 1, 'pathString' => '']), + [], + ]; + + yield 'null path' => [ + new Location(['id' => 1, 'pathString' => null]), + [], + ]; + } + + /** + * @dataProvider getDataForTestPathComputedPropertyGetter + * + * @param string[] $expectedPathValue + */ + public function testPathComputedPropertyGetter(Location $location, array $expectedPathValue): void + { + self::assertSame($expectedPathValue, $location->getPath()); + } + + /** + * Test retrieving missing property. + */ + public function testMissingProperty(): void + { + $this->expectException(PropertyNotFoundException::class); + + $location = new Location(); + $value = $location->notDefined; + self::fail('Succeeded getting non existing property'); + } + + /** + * Test setting read only property. + * + * @covers \Ibexa\Core\Repository\Values\Content\Location::__set + */ + public function testReadOnlyProperty(): void + { + $this->expectException(PropertyReadOnlyException::class); + + $location = new Location(); + $location->id = 42; + self::fail('Succeeded setting read only property'); + } + + /** + * Test if property exists. + */ + public function testIsPropertySet(): void + { + $location = new Location(); + $value = isset($location->notDefined); + self::assertFalse($value); + + $value = isset($location->id); + self::assertTrue($value); + } + + /** + * Test unsetting a property. + * + * @covers \Ibexa\Core\Repository\Values\Content\Location::__unset + */ + public function testUnsetProperty(): void + { + $this->expectException(PropertyReadOnlyException::class); + + $location = new Location(['id' => 2]); + unset($location->id); + self::fail('Unsetting read-only property succeeded'); + } +} + +class_alias(LocationTest::class, 'eZ\Publish\Core\Repository\Tests\Values\Content\LocationTest'); diff --git a/tests/lib/Repository/Values/Content/Query/Aggregation/Location/SubtreeTermAggregationTest.php b/tests/lib/Repository/Values/Content/Query/Aggregation/Location/SubtreeTermAggregationTest.php new file mode 100644 index 0000000000..35e39e6c48 --- /dev/null +++ b/tests/lib/Repository/Values/Content/Query/Aggregation/Location/SubtreeTermAggregationTest.php @@ -0,0 +1,52 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Values\Content\Query\Aggregation\Location; + +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Location; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Location\SubtreeTermAggregation; +use PHPUnit\Framework\TestCase; + +final class SubtreeTermAggregationTest extends TestCase +{ + private const EXAMPLE_PATH_STRING = '/1/2/'; + private const EXAMPLE_AGGREGATION_NAME = 'foo'; + + public function testConstruct(): void + { + $aggregation = new SubtreeTermAggregation( + self::EXAMPLE_AGGREGATION_NAME, + self::EXAMPLE_PATH_STRING + ); + + $this->assertEquals(self::EXAMPLE_AGGREGATION_NAME, $aggregation->getName()); + $this->assertEquals(self::EXAMPLE_PATH_STRING, $aggregation->getPathString()); + } + + public function testConstructThrowsInvalidArgumentExceptionOnInvalidPathString(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectErrorMessage("'/INVALID/PATH' value must follow the path string format, e.g. /1/2/"); + + $aggregation = new SubtreeTermAggregation('foo', '/INVALID/PATH'); + } + + public function testFromLocation(): void + { + $location = $this->createMock(Location::class); + $location->method('__get')->with('pathString')->willReturn(self::EXAMPLE_PATH_STRING); + + $aggregation = SubtreeTermAggregation::fromLocation(self::EXAMPLE_AGGREGATION_NAME, $location); + + $this->assertEquals(self::EXAMPLE_AGGREGATION_NAME, $aggregation->getName()); + $this->assertEquals(self::EXAMPLE_PATH_STRING, $aggregation->getPathString()); + } +} + +class_alias(SubtreeTermAggregationTest::class, 'eZ\Publish\API\Repository\Tests\Values\Content\Query\Aggregation\Location\SubtreeTermAggregationTest'); diff --git a/tests/lib/Repository/Values/Content/Query/Aggregation/RangeTest.php b/tests/lib/Repository/Values/Content/Query/Aggregation/RangeTest.php new file mode 100644 index 0000000000..7cffc94f41 --- /dev/null +++ b/tests/lib/Repository/Values/Content/Query/Aggregation/RangeTest.php @@ -0,0 +1,139 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Values\Content\Query\Aggregation; + +use DateTimeImmutable; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Aggregation\Range; +use PHPUnit\Framework\TestCase; + +final class RangeTest extends TestCase +{ + /** + * @dataProvider dataProviderForTestToString + */ + public function testToString(Range $range, string $expected): void + { + $this->assertEquals($expected, (string)$range); + } + + public function dataProviderForTestToString(): iterable + { + yield 'empty' => [ + new Range(null, null), + '[*;*)', + ]; + + yield 'int' => [ + new Range(1, 10), + '[1;10)', + ]; + + yield 'float' => [ + new Range(0.25, 3.25), + '[0.25;3.25)', + ]; + + yield 'datetime' => [ + new Range( + new DateTimeImmutable('2020-01-01T00:00:00+0000'), + new DateTimeImmutable('2020-12-31T23:59:59+0000'), + ), + '[2020-01-01T00:00:00+0000;2020-12-31T23:59:59+0000)', + ]; + } + + public function testOfInt(): void + { + $this->assertEquals(new Range(null, 10), Range::ofInt(null, 10)); + $this->assertEquals(new Range(1, 10), Range::ofInt(1, 10)); + $this->assertEquals(new Range(1, null), Range::ofInt(1, null)); + } + + public function testOfFloat(): void + { + $this->assertEquals(new Range(null, 10.0), Range::ofFloat(null, 10.0)); + $this->assertEquals(new Range(1.0, 10.0), Range::ofFloat(1.0, 10.0)); + $this->assertEquals(new Range(1.0, null), Range::ofFloat(1.0, null)); + } + + public function testOfDateTime(): void + { + $a = new DateTimeImmutable('2020-01-01T00:00:00+0000'); + $b = new DateTimeImmutable('2020-12-31T23:59:59+0000'); + + $this->assertEquals(new Range(null, $b), Range::ofDateTime(null, $b)); + $this->assertEquals(new Range($a, $b), Range::ofDateTime($a, $b)); + $this->assertEquals(new Range($a, null), Range::ofDateTime($a, null)); + } + + /** + * @dataProvider dataProviderForEqualsTo + */ + public function testEqualsTo(Range $rangeA, Range $rangeB, bool $expectedResult): void + { + self::assertEquals($expectedResult, $rangeA->equalsTo($rangeB)); + self::assertEquals($expectedResult, $rangeB->equalsTo($rangeA)); + } + + /** + * @return iterable<string, array{Range, Range, bool}> + */ + public function dataProviderForEqualsTo(): iterable + { + yield 'int (true)' => [ + Range::ofInt(1, 10), + Range::ofInt(1, 10), + true, + ]; + + yield 'int (false)' => [ + Range::ofInt(1, 10), + Range::ofInt(1, 100), + false, + ]; + + yield 'float (true)' => [ + Range::ofFloat(1.0, 10.0), + Range::ofFloat(1.0, 10.0), + true, + ]; + + yield 'float (false)' => [ + Range::ofFloat(1.0, 10.0), + Range::ofFloat(1.0, 100.0), + false, + ]; + + yield 'data & time (true)' => [ + Range::ofDateTime( + new DateTimeImmutable('2023-01-01 00:00:00'), + new DateTimeImmutable('2023-12-01 00:00:00') + ), + Range::ofDateTime( + new DateTimeImmutable('2023-01-01 00:00:00'), + new DateTimeImmutable('2023-12-01 00:00:00') + ), + true, + ]; + + yield 'data & time (false)' => [ + Range::ofDateTime( + new DateTimeImmutable('2023-01-01 00:00:00'), + new DateTimeImmutable('2023-12-01 00:00:00') + ), + Range::ofDateTime( + new DateTimeImmutable('2024-01-01 00:00:00'), + new DateTimeImmutable('2024-12-01 00:00:00') + ), + false, + ]; + } +} + +class_alias(RangeTest::class, 'eZ\Publish\API\Repository\Tests\Values\Content\Query\Aggregation\RangeTest'); diff --git a/tests/lib/Repository/Values/Content/Query/Criterion/DateMetadataTest.php b/tests/lib/Repository/Values/Content/Query/Criterion/DateMetadataTest.php new file mode 100644 index 0000000000..9279b95cd6 --- /dev/null +++ b/tests/lib/Repository/Values/Content/Query/Criterion/DateMetadataTest.php @@ -0,0 +1,43 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Values\Content\Query\Criterion; + +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion\DateMetadata; +use PHPUnit\Framework\TestCase; + +final class DateMetadataTest extends TestCase +{ + /** + * @dataProvider provideValidConstructorArguments + */ + public function testConstruction(string $target, string $operator, $value): void + { + $criterion = new DateMetadata($target, $operator, $value); + self::assertSame($target, $criterion->target); + } + + /** + * @return iterable<array{non-empty-string, string, integer}> + */ + public static function provideValidConstructorArguments(): iterable + { + $date = 0; + $operator = '='; + + yield ['modified', $operator, $date]; + yield ['created', $operator, $date]; + yield ['published', $operator, $date]; + yield ['trashed', $operator, $date]; + } + + public function testExceptionOnInvalidTarget(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Unknown DateMetadata target "foo". Expected one of: "modified", "created", "published", "trashed"'); + new DateMetadata('foo', '=', 0); + } +} diff --git a/tests/lib/Repository/Values/Content/SectionTest.php b/tests/lib/Repository/Values/Content/SectionTest.php new file mode 100644 index 0000000000..f4cd626170 --- /dev/null +++ b/tests/lib/Repository/Values/Content/SectionTest.php @@ -0,0 +1,96 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Values\Content; + +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyReadOnlyException; +use Ibexa\Contracts\Core\Repository\Values\Content\Section; +use Ibexa\Tests\Core\Repository\Values\ValueObjectTestTrait; +use PHPUnit\Framework\TestCase; + +class SectionTest extends TestCase +{ + use ValueObjectTestTrait; + + /** + * Test a new class and default values on properties. + * + * @covers \Ibexa\Contracts\Core\Repository\Values\Content\Section::__construct + */ + public function testNewClass() + { + $section = new Section(); + + $this->assertPropertiesCorrect( + [ + 'id' => null, + 'identifier' => null, + 'name' => null, + ], + $section + ); + } + + /** + * Test retrieving missing property. + * + * @covers \Ibexa\Contracts\Core\Repository\Values\Content\Section::__get + */ + public function testMissingProperty() + { + $this->expectException(PropertyNotFoundException::class); + + $section = new Section(); + $value = $section->notDefined; + self::fail('Succeeded getting non existing property'); + } + + /** + * Test setting read only property. + * + * @covers \Ibexa\Contracts\Core\Repository\Values\Content\Section::__set + */ + public function testReadOnlyProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $section = new Section(); + $section->id = 22; + self::fail('Succeeded setting read only property'); + } + + /** + * Test if property exists. + * + * @covers \Ibexa\Contracts\Core\Repository\Values\Content\Section::__isset + */ + public function testIsPropertySet() + { + $section = new Section(); + $value = isset($section->notDefined); + self::assertFalse($value); + + $value = isset($section->id); + self::assertTrue($value); + } + + /** + * Test unsetting a property. + * + * @covers \Ibexa\Contracts\Core\Repository\Values\Content\Section::__unset + */ + public function testUnsetProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $section = new Section(['id' => 1]); + unset($section->id); + self::fail('Unsetting read-only property succeeded'); + } +} + +class_alias(SectionTest::class, 'eZ\Publish\API\Repository\Tests\Values\Content\SectionTest'); diff --git a/tests/lib/Repository/Values/Content/TrashItemTest.php b/tests/lib/Repository/Values/Content/TrashItemTest.php new file mode 100644 index 0000000000..d9b6013d7d --- /dev/null +++ b/tests/lib/Repository/Values/Content/TrashItemTest.php @@ -0,0 +1,101 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Values\Content; + +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyReadOnlyException; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Core\Repository\Values\Content\TrashItem; +use Ibexa\Tests\Core\Repository\Values\ValueObjectTestTrait; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Repository\Values\Content\TrashItem + */ +class TrashItemTest extends TestCase +{ + use ValueObjectTestTrait; + + public function testNewClass() + { + // create ContentInfo to be able to retrieve the contentId property via magic method + $contentInfo = new ContentInfo(); + $trashItem = new TrashItem(['contentInfo' => $contentInfo]); + + $this->assertPropertiesCorrect( + [ + 'contentInfo' => $contentInfo, + 'contentId' => null, + 'id' => null, + 'priority' => null, + 'hidden' => null, + 'invisible' => null, + 'remoteId' => null, + 'parentLocationId' => null, + 'pathString' => null, + 'path' => [], + 'depth' => null, + 'sortField' => null, + 'sortOrder' => null, + ], + $trashItem + ); + } + + /** + * Test retrieving missing property. + */ + public function testMissingProperty() + { + $this->expectException(PropertyNotFoundException::class); + + $trashItem = new TrashItem(); + $value = $trashItem->notDefined; + self::fail('Succeeded getting non existing property'); + } + + /** + * Test setting read only property. + */ + public function testReadOnlyProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $trashItem = new TrashItem(); + $trashItem->id = 42; + self::fail('Succeeded setting read only property'); + } + + /** + * Test if property exists. + */ + public function testIsPropertySet() + { + $trashItem = new TrashItem(); + $value = isset($trashItem->notDefined); + self::assertFalse($value); + + $value = isset($trashItem->id); + self::assertTrue($value); + } + + /** + * Test unsetting a property. + * + * @covers \Ibexa\Core\Repository\Values\Content\TrashItem::__unset + */ + public function testUnsetProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $trashItem = new TrashItem(['id' => 2]); + unset($trashItem->id); + self::fail('Unsetting read-only property succeeded'); + } +} + +class_alias(TrashItemTest::class, 'eZ\Publish\Core\Repository\Tests\Values\Content\TrashItemTest'); diff --git a/tests/lib/Repository/Values/Content/VersionInfoTest.php b/tests/lib/Repository/Values/Content/VersionInfoTest.php new file mode 100644 index 0000000000..e190798d15 --- /dev/null +++ b/tests/lib/Repository/Values/Content/VersionInfoTest.php @@ -0,0 +1,67 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Values\Content; + +use Ibexa\Core\Repository\Values\Content\VersionInfo; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Repository\Values\Content\VersionInfo + */ +final class VersionInfoTest extends TestCase +{ + public function testIsDraft(): void + { + $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_DRAFT); + self::assertTrue($versionInfo->isDraft()); + + $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_ARCHIVED); + self::assertFalse($versionInfo->isDraft()); + $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_PUBLISHED); + self::assertFalse($versionInfo->isDraft()); + } + + public function testIsPublished(): void + { + $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_PUBLISHED); + self::assertTrue($versionInfo->isPublished()); + + $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_DRAFT); + self::assertFalse($versionInfo->isPublished()); + $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_ARCHIVED); + self::assertFalse($versionInfo->isPublished()); + } + + public function testIsArchived(): void + { + $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_ARCHIVED); + self::assertTrue($versionInfo->isArchived()); + + $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_DRAFT); + self::assertFalse($versionInfo->isArchived()); + $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_PUBLISHED); + self::assertFalse($versionInfo->isArchived()); + } + + public function testStrictGetters(): void + { + $versionInfo = $this->createVersionInfoWithStatus(VersionInfo::STATUS_PUBLISHED); + self::assertSame(123, $versionInfo->getVersionNo()); + } + + private function createVersionInfoWithStatus(int $status): VersionInfo + { + return new VersionInfo([ + 'versionNo' => 123, + 'status' => $status, + ]); + } +} + +class_alias(VersionInfoTest::class, 'eZ\Publish\Core\Repository\Tests\Values\Content\VersionInfoTest'); diff --git a/eZ/Publish/Core/Repository/Tests/Values/ContentType/ContentTypeDraftTest.php b/tests/lib/Repository/Values/ContentType/ContentTypeDraftTest.php similarity index 87% rename from eZ/Publish/Core/Repository/Tests/Values/ContentType/ContentTypeDraftTest.php rename to tests/lib/Repository/Values/ContentType/ContentTypeDraftTest.php index 4da624315d..3e0cbe4f95 100644 --- a/eZ/Publish/Core/Repository/Tests/Values/ContentType/ContentTypeDraftTest.php +++ b/tests/lib/Repository/Values/ContentType/ContentTypeDraftTest.php @@ -4,17 +4,17 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Tests\Values\ContentType; +namespace Ibexa\Tests\Core\Repository\Values\ContentType; -use eZ\Publish\Core\Repository\Values\ContentType\ContentType; -use eZ\Publish\Core\Repository\Values\ContentType\ContentTypeDraft; +use Ibexa\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Core\Repository\Values\ContentType\ContentTypeDraft; use PHPUnit\Framework\TestCase; +/** + * @covers \Ibexa\Core\Repository\Values\ContentType\ContentTypeDraft + */ class ContentTypeDraftTest extends TestCase { - /** - * @covers \eZ\Publish\Core\Repository\Values\ContentType\ContentTypeDraft::getProperties - */ public function testObjectProperties() { $object = new ContentTypeDraft( @@ -54,3 +54,5 @@ public function testObjectProperties() } } } + +class_alias(ContentTypeDraftTest::class, 'eZ\Publish\Core\Repository\Tests\Values\ContentType\ContentTypeDraftTest'); diff --git a/tests/lib/Repository/Values/ContentType/ContentTypeTest.php b/tests/lib/Repository/Values/ContentType/ContentTypeTest.php new file mode 100644 index 0000000000..9de1080898 --- /dev/null +++ b/tests/lib/Repository/Values/ContentType/ContentTypeTest.php @@ -0,0 +1,184 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Values\ContentType; + +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinitionCollection as APIFieldDefinitionCollection; +use Ibexa\Core\Repository\Values\ContentType\ContentType; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Repository\Values\ContentType\ContentType + */ +final class ContentTypeTest extends TestCase +{ + private const EXAMPLE_FIELD_DEFINITION_IDENTIFIER = 'example'; + private const EXAMPLE_FIELD_TYPE_IDENTIFIER = 'ezcustom'; + + public function testObjectProperties(): void + { + $object = new ContentType([ + 'fieldDefinitions' => $this->createMock(APIFieldDefinitionCollection::class), + ]); + + $properties = $object->attributes(); + + self::assertNotContains('internalFields', $properties, 'Internal property found '); + self::assertContains('contentTypeGroups', $properties, 'Property not found'); + self::assertContains('fieldDefinitions', $properties, 'Property not found'); + self::assertContains('id', $properties, 'Property not found'); + self::assertContains('status', $properties, 'Property not found'); + self::assertContains('identifier', $properties, 'Property not found'); + self::assertContains('creationDate', $properties, 'Property not found'); + self::assertContains('modificationDate', $properties, 'Property not found'); + self::assertContains('creatorId', $properties, 'Property not found'); + self::assertContains('modifierId', $properties, 'Property not found'); + self::assertContains('remoteId', $properties, 'Property not found'); + self::assertContains('urlAliasSchema', $properties, 'Property not found'); + self::assertContains('nameSchema', $properties, 'Property not found'); + self::assertContains('isContainer', $properties, 'Property not found'); + self::assertContains('mainLanguageCode', $properties, 'Property not found'); + self::assertContains('defaultAlwaysAvailable', $properties, 'Property not found'); + self::assertContains('defaultSortField', $properties, 'Property not found'); + self::assertContains('defaultSortOrder', $properties, 'Property not found'); + + // check for duplicates and double check existence of property + $propertiesHash = []; + foreach ($properties as $property) { + if (isset($propertiesHash[$property])) { + self::fail("Property '{$property}' exists several times in properties list"); + } elseif (!isset($object->$property)) { + self::fail("Property '{$property}' does not exist on object, even though it was hinted to be there"); + } + + $propertiesHash[$property] = 1; + } + } + + public function testStrictGetters(): void + { + $identifier = 'foo_content_type'; + $contentType = new ContentType(['identifier' => $identifier]); + + self::assertSame($identifier, $contentType->getIdentifier()); + } + + public function testGetFieldDefinition(): void + { + $fieldDefinition = $this->createMock(FieldDefinition::class); + + $fieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); + + $fieldDefinitionCollection + ->expects($this->once()) + ->method('has') + ->with(self::EXAMPLE_FIELD_DEFINITION_IDENTIFIER) + ->willReturn(true); + + $fieldDefinitionCollection + ->expects($this->once()) + ->method('get') + ->with(self::EXAMPLE_FIELD_DEFINITION_IDENTIFIER) + ->willReturn($fieldDefinition); + + $contentType = new ContentType([ + 'fieldDefinitions' => $fieldDefinitionCollection, + ]); + + $this->assertEquals( + $fieldDefinition, + $contentType->getFieldDefinition(self::EXAMPLE_FIELD_DEFINITION_IDENTIFIER) + ); + } + + public function testHasFieldDefinition(): void + { + $fieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); + $fieldDefinitionCollection + ->expects($this->once()) + ->method('has') + ->with(self::EXAMPLE_FIELD_DEFINITION_IDENTIFIER) + ->willReturn(true); + + $contentType = new ContentType([ + 'fieldDefinitions' => $fieldDefinitionCollection, + ]); + + $this->assertTrue( + $contentType->hasFieldDefinition(self::EXAMPLE_FIELD_DEFINITION_IDENTIFIER) + ); + } + + public function testHasFieldDefinitionOfType(): void + { + $fieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); + $fieldDefinitionCollection + ->expects($this->once()) + ->method('anyOfType') + ->with(self::EXAMPLE_FIELD_TYPE_IDENTIFIER) + ->willReturn(true); + + $contentType = new ContentType([ + 'fieldDefinitions' => $fieldDefinitionCollection, + ]); + + $this->assertTrue( + $contentType->hasFieldDefinitionOfType(self::EXAMPLE_FIELD_TYPE_IDENTIFIER) + ); + } + + public function testGetFieldDefinitionsOfType(): void + { + $expectedFieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); + + $fieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); + $fieldDefinitionCollection + ->expects($this->once()) + ->method('filterByType') + ->with(self::EXAMPLE_FIELD_TYPE_IDENTIFIER) + ->willReturn($expectedFieldDefinitionCollection); + + $contentType = new ContentType([ + 'fieldDefinitions' => $fieldDefinitionCollection, + ]); + + $this->assertEquals( + $expectedFieldDefinitionCollection, + $contentType->getFieldDefinitionsOfType(self::EXAMPLE_FIELD_TYPE_IDENTIFIER) + ); + } + + public function testGetFirstFieldDefinitionOfType(): void + { + $expectedFieldDefinition = $this->createMock(FieldDefinition::class); + + $filteredFieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); + $filteredFieldDefinitionCollection + ->method('first') + ->willReturn($expectedFieldDefinition); + + $fieldDefinitionCollection = $this->createMock(APIFieldDefinitionCollection::class); + $fieldDefinitionCollection + ->expects($this->once()) + ->method('filterByType') + ->with(self::EXAMPLE_FIELD_TYPE_IDENTIFIER) + ->willReturn($filteredFieldDefinitionCollection); + + $contentType = new ContentType([ + 'fieldDefinitions' => $fieldDefinitionCollection, + ]); + + $this->assertEquals( + $expectedFieldDefinition, + $contentType->getFirstFieldDefinitionOfType(self::EXAMPLE_FIELD_TYPE_IDENTIFIER) + ); + } +} + +class_alias(ContentTypeTest::class, 'eZ\Publish\Core\Repository\Tests\Values\ContentType\ContentTypeTest'); diff --git a/tests/lib/Repository/Values/ContentType/FieldDefinitionCollectionTest.php b/tests/lib/Repository/Values/ContentType/FieldDefinitionCollectionTest.php new file mode 100644 index 0000000000..d1e847f100 --- /dev/null +++ b/tests/lib/Repository/Values/ContentType/FieldDefinitionCollectionTest.php @@ -0,0 +1,347 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Values\ContentType; + +use Closure; +use Ibexa\Contracts\Core\Repository\Exceptions\OutOfBoundsException; +use Ibexa\Contracts\Core\Repository\Values\ContentType\FieldDefinition as APIFieldDefinition; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinition; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinitionCollection; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Repository\Values\ContentType\FieldDefinitionCollection + */ +final class FieldDefinitionCollectionTest extends TestCase +{ + public function testGet(): void + { + list($a, $b, $c) = $this->createFieldDefinitions('A', 'B', 'C'); + + $collection = new FieldDefinitionCollection([$a, $b, $c]); + + $this->assertEquals($a, $collection->get('A')); + $this->assertEquals($b, $collection->get('B')); + $this->assertEquals($c, $collection->get('C')); + } + + public function testGetThrowsOutOfBoundsExceptionForNonExistingFieldDefinition(): void + { + $this->expectException(OutOfBoundsException::class); + $this->expectExceptionMessage("Field Definition Collection does not contain element with identifier 'Z'"); + + $collection = new FieldDefinitionCollection( + $this->createFieldDefinitions('A', 'B', 'C') + ); + + $collection->get('Z'); + } + + public function testHasReturnTrueForExistingFieldDefinition(): void + { + $collection = new FieldDefinitionCollection( + $this->createFieldDefinitions('A', 'B', 'C') + ); + + $this->assertTrue($collection->has('A')); + $this->assertTrue($collection->has('B')); + $this->assertTrue($collection->has('C')); + } + + public function testHasReturnFalseForNonExistingFieldDefinition(): void + { + $collection = new FieldDefinitionCollection( + $this->createFieldDefinitions('A', 'B', 'C') + ); + + $this->assertFalse($collection->has('Z')); + } + + public function testIsEmptyReturnsTrueForEmptyCollection(): void + { + $collection = new FieldDefinitionCollection(); + + $this->assertTrue($collection->isEmpty()); + } + + public function testIsEmptyReturnsFalseForNonEmptyCollection(): void + { + $collection = new FieldDefinitionCollection([ + $this->createFieldDefinition('Example'), + ]); + + $this->assertFalse($collection->isEmpty()); + } + + public function testFirstThrowsOutOfBoundsExceptionForEmptyCollection(): void + { + $this->expectException(OutOfBoundsException::class); + $this->expectExceptionMessage('Field Definition Collection is empty'); + + $collection = new FieldDefinitionCollection(); + $collection->first(); + } + + public function testFirstReturnsFieldDefinitionForNonEmptyCollection(): void + { + list($a, $b, $c) = $this->createFieldDefinitions('A', 'B', 'C'); + + $collection = new FieldDefinitionCollection([$a, $b, $c]); + + $this->assertEquals($a, $collection->first()); + } + + public function testLastReturnsFieldDefinitionForNonEmptyCollection(): void + { + list($a, $b, $c) = $this->createFieldDefinitions('A', 'B', 'C'); + + $collection = new FieldDefinitionCollection([$a, $b, $c]); + + $this->assertEquals($c, $collection->last()); + } + + public function testLastThrowsOutOfBoundsExceptionForEmptyCollection(): void + { + $this->expectException(OutOfBoundsException::class); + $this->expectExceptionMessage('Field Definition Collection is empty'); + + $collection = new FieldDefinitionCollection(); + $collection->last(); + } + + public function testFirstAndLastAreEqualForCollectionWithOneElement(): void + { + $fieldDefinition = $this->createFieldDefinition('Example'); + + $collection = new FieldDefinitionCollection([$fieldDefinition]); + + $this->assertEquals($fieldDefinition, $collection->first()); + $this->assertEquals($fieldDefinition, $collection->last()); + } + + public function testCountForNonEmptyCollection(): void + { + list($a, $b, $c) = $this->createFieldDefinitions('A', 'B', 'C'); + + $collection = new FieldDefinitionCollection([$a, $b, $c]); + + $this->assertEquals(3, $collection->count()); + } + + public function testCountReturnsZeroForEmptyCollection(): void + { + $collection = new FieldDefinitionCollection(); + + $this->assertEquals(0, $collection->count()); + } + + public function testMap(): void + { + $collection = new FieldDefinitionCollection($this->createFieldDefinitions('A', 'B', 'C')); + + $closure = static function (FieldDefinition $fieldDefinition): string { + return strtolower($fieldDefinition->identifier); + }; + + $this->assertEquals(['a', 'b', 'c'], $collection->map($closure)); + } + + public function testFilter(): void + { + list($a, $b, $c) = $this->createFieldDefinitions('A', 'B', 'C'); + + $collection = new FieldDefinitionCollection([$a, $b, $c]); + + $this->assertEquals( + new FieldDefinitionCollection([$a, $c]), + $collection->filter($this->getIdentifierIsEqualPredicate('A', 'C')) + ); + + $this->assertEquals( + new FieldDefinitionCollection(), + $collection->filter($this->getContraction()) + ); + + $this->assertEquals( + new FieldDefinitionCollection([$a, $b, $c]), + $collection->filter($this->getTautology()) + ); + } + + public function testFilterByType(): void + { + list($a, $b, $c) = $this->createFieldDefinitionsWith('fieldTypeIdentifier', ['ezstring', 'ezstring', 'ezimage']); + + $collection = new FieldDefinitionCollection([$a, $b, $c]); + + $this->assertEquals( + new FieldDefinitionCollection([$a, $b]), + $collection->filterByType('ezstring') + ); + } + + public function filterByGroup(): void + { + list($a, $b, $c) = $this->createFieldDefinitionsWith('fieldGroup', ['default', 'default', 'seo']); + + $collection = new FieldDefinitionCollection([$a, $b, $c]); + + $this->assertEquals( + new FieldDefinitionCollection([$c]), + $collection->filterByType('seo') + ); + } + + public function testAll(): void + { + $collection = new FieldDefinitionCollection($this->createFieldDefinitions('A', 'B', 'C')); + + $this->assertTrue($collection->all($this->getIdentifierIsEqualPredicate('A', 'B', 'C'))); + $this->assertFalse($collection->all($this->getIdentifierIsEqualPredicate('A'))); + + $this->assertTrue($collection->all($this->getTautology())); + $this->assertFalse($collection->all($this->getContraction())); + } + + public function testAny(): void + { + $collection = new FieldDefinitionCollection($this->createFieldDefinitions('A', 'B', 'C')); + + $this->assertTrue($collection->any($this->getIdentifierIsEqualPredicate('A'))); + $this->assertFalse($collection->any($this->getIdentifierIsEqualPredicate('Z'))); + + $this->assertTrue($collection->any($this->getTautology())); + $this->assertFalse($collection->any($this->getContraction())); + } + + public function testAnyOfType(): void + { + $collection = new FieldDefinitionCollection( + $this->createFieldDefinitionsWith('fieldTypeIdentifier', ['ezstring', 'ezstring', 'ezimage']) + ); + + $this->assertTrue($collection->anyOfType('ezstring')); + $this->assertFalse($collection->anyOfType('ezrichtext')); + } + + public function testAnyInGroup(): void + { + $collection = new FieldDefinitionCollection( + $this->createFieldDefinitionsWith('fieldGroup', ['default', 'default', 'seo']) + ); + + $this->assertTrue($collection->anyInGroup('default')); + $this->assertFalse($collection->anyInGroup('comments')); + } + + public function testPartition(): void + { + list($a, $b, $c) = $this->createFieldDefinitions('A', 'B', 'C'); + + $collection = new FieldDefinitionCollection([$a, $b, $c]); + + $this->assertEquals( + [ + new FieldDefinitionCollection([$a, $c]), + new FieldDefinitionCollection([$b]), + ], + $collection->partition($this->getIdentifierIsEqualPredicate('A', 'C')) + ); + + $this->assertEquals( + [ + new FieldDefinitionCollection([$a, $b, $c]), + new FieldDefinitionCollection(), + ], + $collection->partition($this->getTautology()) + ); + + $this->assertEquals( + [ + new FieldDefinitionCollection(), + new FieldDefinitionCollection([$a, $b, $c]), + ], + $collection->partition($this->getContraction()) + ); + } + + public function testToArray(): void + { + $fieldDefinitions = $this->createFieldDefinitions('A', 'B', 'C'); + + $collection = new FieldDefinitionCollection($fieldDefinitions); + + $this->assertEquals($fieldDefinitions, $collection->toArray()); + } + + private function createFieldDefinitions(string ...$identifiers): array + { + return array_map( + fn (string $identifier): APIFieldDefinition => $this->createFieldDefinition($identifier), + $identifiers + ); + } + + private function createFieldDefinitionsWith(string $property, array $values): array + { + return array_map( + fn (string $value): APIFieldDefinition => $this->createFieldDefinition( + uniqid('field_def_identifier', true), + $property, + $value + ), + $values + ); + } + + private function createFieldDefinition( + string $identifier, + ?string $property = null, + ?string $value = null + ): APIFieldDefinition { + $properties = ['identifier' => $identifier]; + if (null !== $property) { + $properties[$property] = $value; + } + + return new FieldDefinition($properties); + } + + /** + * Returns predicate which test if field definition identifier belongs to given set. + */ + private function getIdentifierIsEqualPredicate(string ...$identifiers): Closure + { + return static function (APIFieldDefinition $fieldDefinition) use ($identifiers): bool { + return in_array($fieldDefinition->identifier, $identifiers); + }; + } + + /** + * Returns a predicate which is always true. + */ + private function getTautology(): Closure + { + return static function (APIFieldDefinition $fieldDefinition): bool { + return true; + }; + } + + /** + * Returns a predicate which is always false. + */ + private function getContraction(): Closure + { + return static function (APIFieldDefinition $fieldDefinition): bool { + return false; + }; + } +} + +class_alias(FieldDefinitionCollectionTest::class, 'eZ\Publish\Core\Repository\Tests\Values\ContentType\FieldDefinitionCollectionTest'); diff --git a/tests/lib/Repository/Values/ContentType/FieldDefinitionTest.php b/tests/lib/Repository/Values/ContentType/FieldDefinitionTest.php new file mode 100644 index 0000000000..6634f3bdfa --- /dev/null +++ b/tests/lib/Repository/Values/ContentType/FieldDefinitionTest.php @@ -0,0 +1,53 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Values\ContentType; + +use Ibexa\Core\FieldType\Value as BaseFieldValue; +use Ibexa\Core\Repository\Values\ContentType\FieldDefinition; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Repository\Values\ContentType\FieldDefinition + */ +final class FieldDefinitionTest extends TestCase +{ + public function testStrictGetters(): void + { + $defaultValueMock = $this->createMock(BaseFieldValue::class); + $fieldDefinition = new FieldDefinition( + [ + 'id' => 123, + 'identifier' => 'my_field_definition', + 'fieldTypeIdentifier' => 'ibexa_field_type', + 'fieldGroup' => 'content', + 'position' => 1, + 'isTranslatable' => true, + 'isRequired' => true, + 'isInfoCollector' => false, + 'defaultValue' => $defaultValueMock, + 'isSearchable' => true, + 'mainLanguageCode' => 'eng-GB', + 'isThumbnail' => true, + ] + ); + + self::assertSame(123, $fieldDefinition->getId()); + self::assertSame('my_field_definition', $fieldDefinition->getIdentifier()); + self::assertSame('ibexa_field_type', $fieldDefinition->getFieldTypeIdentifier()); + self::assertSame('content', $fieldDefinition->getFieldGroup()); + self::assertSame(1, $fieldDefinition->getPosition()); + self::assertTrue($fieldDefinition->isTranslatable()); + self::assertTrue($fieldDefinition->isRequired()); + self::assertFalse($fieldDefinition->isInfoCollector()); + self::assertSame($defaultValueMock, $fieldDefinition->getDefaultValue()); + self::assertTrue($fieldDefinition->isSearchable()); + self::assertSame('eng-GB', $fieldDefinition->getMainLanguageCode()); + self::assertTrue($fieldDefinition->isThumbnail()); + } +} diff --git a/eZ/Publish/API/Repository/Tests/Values/Filter/FilterTest.php b/tests/lib/Repository/Values/Filter/FilterTest.php similarity index 77% rename from eZ/Publish/API/Repository/Tests/Values/Filter/FilterTest.php rename to tests/lib/Repository/Values/Filter/FilterTest.php index 6e602f3ab5..292fe399a4 100644 --- a/eZ/Publish/API/Repository/Tests/Values/Filter/FilterTest.php +++ b/tests/lib/Repository/Values/Filter/FilterTest.php @@ -6,25 +6,26 @@ */ declare(strict_types=1); -namespace eZ\Publish\API\Repository\Tests\Values\Filter; - -use eZ\Publish\API\Repository\Exceptions\BadStateException; -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\API\Repository\Values\Filter\Filter; -use eZ\Publish\API\Repository\Values\URL\Query\SortClause as URLQuerySortClause; +namespace Ibexa\Tests\Core\Repository\Values\Filter; + +use Ibexa\Contracts\Core\Repository\Exceptions\BadStateException; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Contracts\Core\Repository\Values\Filter\Filter; +use Ibexa\Contracts\Core\Repository\Values\URL\Query\SortClause as URLQuerySortClause; use function md5; use PHPUnit\Framework\TestCase; use function sprintf; +/** + * @covers \Ibexa\Contracts\Core\Repository\Values\Filter\Filter + */ final class FilterTest extends TestCase { /** - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::__construct - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException */ public function testConstructor(): void { @@ -43,9 +44,7 @@ public function testConstructor(): void /** * @dataProvider getInvalidSortClausesData * - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::__construct - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException */ public function testConstructorThrowsBadStateException( array $sortClauses, @@ -64,7 +63,7 @@ public function getInvalidSortClausesData(): iterable new SortClause\Location\Priority(), 1, ], - 'Expected an instance of "eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause", ' . + 'Expected an instance of "Ibexa\Contracts\Core\Repository\Values\Filter\FilteringSortClause", ' . 'got "integer" at position 1', ]; @@ -74,8 +73,8 @@ public function getInvalidSortClausesData(): iterable new URLQuerySortClause\URL(Query::SORT_DESC), Query::SORT_ASC, ], - 'Expected an instance of "eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause", ' . - 'got "eZ\Publish\API\Repository\Values\URL\Query\SortClause\URL" at position 1', + 'Expected an instance of "Ibexa\Contracts\Core\Repository\Values\Filter\FilteringSortClause", ' . + 'got "Ibexa\Contracts\Core\Repository\Values\URL\Query\SortClause\URL" at position 1', ]; yield [ @@ -86,16 +85,13 @@ public function getInvalidSortClausesData(): iterable new class('', Query::SORT_DESC) extends URLQuerySortClause { }, ], - 'Expected an instance of "eZ\Publish\SPI\Repository\Values\Filter\FilteringSortClause", ' . + 'Expected an instance of "Ibexa\Contracts\Core\Repository\Values\Filter\FilteringSortClause", ' . 'got "string" at position 2', ]; } /** - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::withCriterion - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::getCriterion - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException */ public function testWithCriterion(): Filter { @@ -109,7 +105,7 @@ public function testWithCriterion(): Filter } /** - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException */ public function testWithCriterionThrowsBadStateException(): void { @@ -121,10 +117,7 @@ public function testWithCriterionThrowsBadStateException(): void } /** - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::andWithCriterion - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::getCriterion - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException */ public function testAndWithCriterion(): Filter { @@ -147,10 +140,7 @@ public function testAndWithCriterion(): Filter } /** - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::orWithCriterion - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::getCriterion - * - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException */ public function testOrWithCriterion(): Filter { @@ -173,10 +163,6 @@ public function testOrWithCriterion(): Filter return $filter; } - /** - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::withSortClause - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::getSortClauses - */ public function testWithSortClause(): Filter { $filter = new Filter(); @@ -196,14 +182,9 @@ public function testWithSortClause(): Filter } /** - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::getCriterion - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::getSortClauses - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::getOffset - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::getLimit - * * @dataProvider getComplexFilterTestData * - * @param \eZ\Publish\API\Repository\Values\Content\Query\SortClause[] $expectedSortClauses + * @param \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause[] $expectedSortClauses */ public function testBuildingComplexFilter( Filter $filter, @@ -219,8 +200,8 @@ public function testBuildingComplexFilter( } /** - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException - * @throws \eZ\Publish\API\Repository\Exceptions\InvalidArgumentException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException */ public function getComplexFilterTestData(): iterable { @@ -350,8 +331,6 @@ public function getComplexFilterTestData(): iterable } /** - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::sliceBy - * * @dataProvider getFiltersWithInvalidSliceData */ public function testSliceByThrowsInvalidArgumentException( @@ -382,12 +361,6 @@ public function getFiltersWithInvalidSliceData(): iterable } /** - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::reset - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::getCriterion - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::getSortClauses - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::getOffset - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::getLimit - * * @dataProvider getFilters */ public function testReset(Filter $filter): void @@ -400,8 +373,6 @@ public function testReset(Filter $filter): void } /** - * @covers \eZ\Publish\API\Repository\Values\Filter\Filter::__clone - * * @dataProvider getFilters */ public function testClone(Filter $filter): void @@ -420,7 +391,7 @@ public function testClone(Filter $filter): void } /** - * @throws \eZ\Publish\API\Repository\Exceptions\BadStateException + * @throws \Ibexa\Contracts\Core\Repository\Exceptions\BadStateException */ public function getFilters(): iterable { @@ -447,3 +418,5 @@ public function getFilters(): iterable yield 'Empty Filter' => [new Filter()]; } } + +class_alias(FilterTest::class, 'eZ\Publish\API\Repository\Tests\Values\Filter\FilterTest'); diff --git a/eZ/Publish/Core/Repository/Tests/Values/MultiLanguageTestTrait.php b/tests/lib/Repository/Values/MultiLanguageTestTrait.php similarity index 84% rename from eZ/Publish/Core/Repository/Tests/Values/MultiLanguageTestTrait.php rename to tests/lib/Repository/Values/MultiLanguageTestTrait.php index 450a75fb0a..3b6a11e629 100644 --- a/eZ/Publish/Core/Repository/Tests/Values/MultiLanguageTestTrait.php +++ b/tests/lib/Repository/Values/MultiLanguageTestTrait.php @@ -4,10 +4,10 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Repository\Tests\Values; +namespace Ibexa\Tests\Core\Repository\Values; -use eZ\Publish\SPI\Repository\Values\MultiLanguageDescription; -use eZ\Publish\SPI\Repository\Values\MultiLanguageName; +use Ibexa\Contracts\Core\Repository\Values\MultiLanguageDescription; +use Ibexa\Contracts\Core\Repository\Values\MultiLanguageName; use ReflectionClass; /** @@ -20,7 +20,7 @@ trait MultiLanguageTestTrait /** * @depends testNewClassWithMultiLanguageProperties * - * @param \eZ\Publish\SPI\Repository\Values\MultiLanguageName $object tested ValueObject + * @param \Ibexa\Contracts\Core\Repository\Values\MultiLanguageName $object tested ValueObject */ public function testGetMultiLanguagePrioritizedName($object) { @@ -39,7 +39,7 @@ public function testGetMultiLanguagePrioritizedName($object) /** * @depends testNewClassWithMultiLanguageProperties * - * @param \eZ\Publish\SPI\Repository\Values\MultiLanguageName $object tested ValueObject + * @param \Ibexa\Contracts\Core\Repository\Values\MultiLanguageName $object tested ValueObject */ public function testGetMultiLanguageDefaultName($object) { @@ -69,7 +69,7 @@ public function testGetMultiLanguageDefaultName($object) /** * @depends testNewClassWithMultiLanguageProperties * - * @param \eZ\Publish\SPI\Repository\Values\MultiLanguageDescription $object tested ValueObject + * @param \Ibexa\Contracts\Core\Repository\Values\MultiLanguageDescription $object tested ValueObject */ public function testGetMultiLanguagePrioritizedDescription($object) { @@ -88,7 +88,7 @@ public function testGetMultiLanguagePrioritizedDescription($object) /** * @depends testNewClassWithMultiLanguageProperties * - * @param \eZ\Publish\SPI\Repository\Values\MultiLanguageDescription $object tested ValueObject + * @param \Ibexa\Contracts\Core\Repository\Values\MultiLanguageDescription $object tested ValueObject */ public function testGetMultiLanguageDefaultDescription($object) { @@ -116,3 +116,5 @@ public function testGetMultiLanguageDefaultDescription($object) } } } + +class_alias(MultiLanguageTestTrait::class, 'eZ\Publish\Core\Repository\Tests\Values\MultiLanguageTestTrait'); diff --git a/tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php b/tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php new file mode 100644 index 0000000000..dbbc8f2952 --- /dev/null +++ b/tests/lib/Repository/Values/ObjectState/ObjectStateGroupTest.php @@ -0,0 +1,130 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Values\ObjectState; + +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyReadOnlyException; +use Ibexa\Core\Repository\Values\ObjectState\ObjectStateGroup; +use Ibexa\Tests\Core\Repository\Values\MultiLanguageTestTrait; +use Ibexa\Tests\Core\Repository\Values\ValueObjectTestTrait; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Repository\Values\ObjectState\ObjectStateGroup + */ +class ObjectStateGroupTest extends TestCase +{ + use ValueObjectTestTrait; + use MultiLanguageTestTrait; + + /** + * Test a new class and default values on properties. + */ + public function testNewClass() + { + $objectStateGroup = new ObjectStateGroup(); + + $this->assertPropertiesCorrect( + [ + 'id' => null, + 'identifier' => null, + 'mainLanguageCode' => null, + 'languageCodes' => null, + 'names' => [], + 'descriptions' => [], + ], + $objectStateGroup + ); + } + + /** + * Test a new class with unified multi language logic properties. + * + * @return \Ibexa\Core\Repository\Values\ObjectState\ObjectStateGroup + */ + public function testNewClassWithMultiLanguageProperties() + { + $properties = [ + 'names' => [ + 'eng-US' => 'Name', + 'pol-PL' => 'Nazwa', + ], + 'descriptions' => [ + 'eng-US' => 'Description', + 'pol-PL' => 'Opis', + ], + 'mainLanguageCode' => 'eng-US', + 'prioritizedLanguages' => ['pol-PL', 'eng-US'], + ]; + + $objectStateGroup = new ObjectStateGroup($properties); + $this->assertPropertiesCorrect($properties, $objectStateGroup); + + // BC test: + self::assertTrue(isset($objectStateGroup->defaultLanguageCode)); + self::assertSame('eng-US', $objectStateGroup->defaultLanguageCode); + + return $objectStateGroup; + } + + /** + * Test retrieving missing property. + * + * @covers \Ibexa\Core\Repository\Values\ObjectState\ObjectStateGroup::__get + */ + public function testMissingProperty() + { + $this->expectException(PropertyNotFoundException::class); + + $objectStateGroup = new ObjectStateGroup(); + $value = $objectStateGroup->notDefined; + $this->fail('Succeeded getting non existing property'); + } + + /** + * Test setting read only property. + * + * @covers \Ibexa\Core\Repository\Values\ObjectState\ObjectStateGroup::__set + */ + public function testReadOnlyProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $objectStateGroup = new ObjectStateGroup(); + $objectStateGroup->id = 42; + $this->fail('Succeeded setting read only property'); + } + + /** + * Test if property exists. + */ + public function testIsPropertySet() + { + $objectStateGroup = new ObjectStateGroup(); + $value = isset($objectStateGroup->notDefined); + $this->assertFalse($value); + + $value = isset($objectStateGroup->id); + $this->assertTrue($value); + } + + /** + * Test unsetting a property. + * + * @covers \Ibexa\Core\Repository\Values\ObjectState\ObjectStateGroup::__unset + */ + public function testUnsetProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $objectStateGroup = new ObjectStateGroup(['id' => 2]); + unset($objectStateGroup->id); + $this->fail('Unsetting read-only property succeeded'); + } +} + +class_alias(ObjectStateGroupTest::class, 'eZ\Publish\Core\Repository\Tests\Values\ObjectState\ObjectStateGroupTest'); diff --git a/tests/lib/Repository/Values/ObjectState/ObjectStateTest.php b/tests/lib/Repository/Values/ObjectState/ObjectStateTest.php new file mode 100644 index 0000000000..41255bfb60 --- /dev/null +++ b/tests/lib/Repository/Values/ObjectState/ObjectStateTest.php @@ -0,0 +1,134 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Values\ObjectState; + +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyReadOnlyException; +use Ibexa\Core\Repository\Values\ObjectState\ObjectState; +use Ibexa\Tests\Core\Repository\Values\MultiLanguageTestTrait; +use Ibexa\Tests\Core\Repository\Values\ValueObjectTestTrait; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Repository\Values\ObjectState\ObjectState + */ +class ObjectStateTest extends TestCase +{ + use ValueObjectTestTrait; + use MultiLanguageTestTrait; + + /** + * Test a new class and default values on properties. + */ + public function testNewClass() + { + $objectState = new ObjectState(); + + $this->assertPropertiesCorrect( + [ + 'id' => null, + 'identifier' => null, + 'priority' => null, + 'mainLanguageCode' => null, + 'languageCodes' => null, + 'names' => [], + 'descriptions' => [], + ], + $objectState + ); + } + + /** + * Test a new class with unified multi language logic properties. + * + * @return \Ibexa\Core\Repository\Values\ObjectState\ObjectState + */ + public function testNewClassWithMultiLanguageProperties() + { + $properties = [ + 'names' => [ + 'eng-US' => 'Name', + 'pol-PL' => 'Nazwa', + ], + 'descriptions' => [ + 'eng-US' => 'Description', + 'pol-PL' => 'Opis', + ], + 'mainLanguageCode' => 'eng-US', + 'prioritizedLanguages' => ['pol-PL', 'eng-US'], + ]; + + $objectState = new ObjectState($properties); + $this->assertPropertiesCorrect($properties, $objectState); + + // BC test: + self::assertTrue(isset($objectState->defaultLanguageCode)); + self::assertSame('eng-US', $objectState->defaultLanguageCode); + + return $objectState; + } + + /** + * Test retrieving missing property. + * + * @covers \Ibexa\Core\Repository\Values\ObjectState\ObjectState::__get + * @covers \Ibexa\Core\Repository\Values\ObjectState\ObjectStateGroup::__get + */ + public function testMissingProperty() + { + $this->expectException(PropertyNotFoundException::class); + + $objectState = new ObjectState(); + $value = $objectState->notDefined; + $this->fail('Succeeded getting non existing property'); + } + + /** + * Test setting read only property. + * + * @covers \Ibexa\Core\Repository\Values\ObjectState\ObjectState::__set + * @covers \Ibexa\Core\Repository\Values\ObjectState\ObjectStateGroup::__set + */ + public function testReadOnlyProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $objectState = new ObjectState(); + $objectState->id = 42; + $this->fail('Succeeded setting read only property'); + } + + /** + * Test if property exists. + */ + public function testIsPropertySet() + { + $objectState = new ObjectState(); + $value = isset($objectState->notDefined); + $this->assertFalse($value); + + $value = isset($objectState->id); + $this->assertTrue($value); + } + + /** + * Test unsetting a property. + * + * @covers \Ibexa\Core\Repository\Values\ObjectState\ObjectState::__unset + * @covers \Ibexa\Contracts\Core\Repository\Values\ObjectState\ObjectStateGroup::__unset + */ + public function testUnsetProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $objectState = new ObjectState(['id' => 2]); + unset($objectState->id); + $this->fail('Unsetting read-only property succeeded'); + } +} + +class_alias(ObjectStateTest::class, 'eZ\Publish\Core\Repository\Tests\Values\ObjectState\ObjectStateTest'); diff --git a/tests/lib/Repository/Values/Translation/MessageTest.php b/tests/lib/Repository/Values/Translation/MessageTest.php new file mode 100644 index 0000000000..278847cb4b --- /dev/null +++ b/tests/lib/Repository/Values/Translation/MessageTest.php @@ -0,0 +1,61 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Values\Translation; + +use Ibexa\Contracts\Core\Repository\Values\Translation\Message; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Contracts\Core\Repository\Values\Translation\Message + */ +final class MessageTest extends TestCase +{ + /** + * @dataProvider getDataForTestStringable + */ + public function testStringable(Message $message, string $expectedString): void + { + self::assertSame($expectedString, (string)$message); + } + + /** + * @return array<string, array{\Ibexa\Contracts\Core\Repository\Values\Translation\Message, string}> + */ + public static function getDataForTestStringable(): iterable + { + yield 'message with substitution values' => [ + new Message( + 'Anna has some oranges in %object%', + [ + '%object%' => 'a basket', + ] + ), + 'Anna has some oranges in a basket', + ]; + + yield 'message with multiple substitution values' => [ + new Message( + '%first_name% has some data in %storage_type%', + [ + '%first_name%' => 'Anna', + '%storage_type%' => 'her database', + ] + ), + 'Anna has some data in her database', + ]; + + yield 'message with no substitution values' => [ + new Message( + 'This value is not correct', + [] + ), + 'This value is not correct', + ]; + } +} diff --git a/tests/lib/Repository/Values/Translation/PluralTest.php b/tests/lib/Repository/Values/Translation/PluralTest.php new file mode 100644 index 0000000000..d0d6e57f96 --- /dev/null +++ b/tests/lib/Repository/Values/Translation/PluralTest.php @@ -0,0 +1,63 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Repository\Values\Translation; + +use Ibexa\Contracts\Core\Repository\Values\Translation\Plural; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Contracts\Core\Repository\Values\Translation\Plural + */ +final class PluralTest extends TestCase +{ + /** + * @dataProvider getDataForTestStringable + */ + public function testStringable(Plural $message, string $expectedString): void + { + self::assertSame($expectedString, (string)$message); + } + + /** + * @return array<string, array{\Ibexa\Contracts\Core\Repository\Values\Translation\Plural, string}> + */ + public static function getDataForTestStringable(): iterable + { + yield 'singular form' => [ + new Plural( + 'John has %apple_count% apple', + 'John has %apple_count% apples', + [ + '%apple_count%' => 1, + ] + ), + 'John has 1 apple', + ]; + + yield 'plural form' => [ + new Plural( + 'John has %apple_count% apple', + 'John has %apple_count% apples', + [ + '%apple_count%' => 2, + ] + ), + 'John has 2 apples', + ]; + + yield 'no substitution values' => [ + new Plural( + 'John has some apples', + 'John has a lot of apples', + [] + ), + 'John has a lot of apples', + ]; + } +} diff --git a/tests/lib/Repository/Values/User/PolicyTest.php b/tests/lib/Repository/Values/User/PolicyTest.php new file mode 100644 index 0000000000..4b82889e59 --- /dev/null +++ b/tests/lib/Repository/Values/User/PolicyTest.php @@ -0,0 +1,96 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Values\User; + +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyReadOnlyException; +use Ibexa\Core\Repository\Values\User\Policy; +use Ibexa\Tests\Core\Repository\Values\ValueObjectTestTrait; +use PHPUnit\Framework\TestCase; + +class PolicyTest extends TestCase +{ + use ValueObjectTestTrait; + + /** + * Test a new class and default values on properties. + * + * @covers \Ibexa\Core\Repository\Values\User\Policy::__construct + */ + public function testNewClass() + { + $this->assertPropertiesCorrect( + [ + 'id' => null, + 'roleId' => null, + 'module' => null, + 'function' => null, + 'limitations' => [], + ], + new Policy() + ); + } + + /** + * Test retrieving missing property. + * + * @covers \Ibexa\Core\Repository\Values\User\Policy::__get + */ + public function testMissingProperty() + { + $this->expectException(PropertyNotFoundException::class); + + $policy = new Policy(); + $value = $policy->notDefined; + self::fail('Succeeded getting non existing property'); + } + + /** + * Test setting read only property. + * + * @covers \Ibexa\Core\Repository\Values\User\Policy::__set + */ + public function testReadOnlyProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $policy = new Policy(); + $policy->id = 42; + self::fail('Succeeded setting read only property'); + } + + /** + * Test if property exists. + * + * @covers \Ibexa\Core\Repository\Values\User\Policy::__isset + */ + public function testIsPropertySet() + { + $policy = new Policy(); + $value = isset($policy->notDefined); + self::assertFalse($value); + + $value = isset($policy->id); + self::assertTrue($value); + } + + /** + * Test unsetting a property. + * + * @covers \Ibexa\Core\Repository\Values\User\Policy::__unset + */ + public function testUnsetProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $policy = new Policy(['id' => 1]); + unset($policy->id); + self::fail('Unsetting read-only property succeeded'); + } +} + +class_alias(PolicyTest::class, 'eZ\Publish\Core\Repository\Tests\Values\User\PolicyTest'); diff --git a/tests/lib/Repository/Values/User/RoleTest.php b/tests/lib/Repository/Values/User/RoleTest.php new file mode 100644 index 0000000000..e506323d13 --- /dev/null +++ b/tests/lib/Repository/Values/User/RoleTest.php @@ -0,0 +1,93 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Values\User; + +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyReadOnlyException; +use Ibexa\Core\Repository\Values\User\Role; +use Ibexa\Tests\Core\Repository\Values\ValueObjectTestTrait; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Repository\Values\User\Role + */ +class RoleTest extends TestCase +{ + use ValueObjectTestTrait; + + /** + * Test a new class and default values on properties. + */ + public function testNewClass() + { + $this->assertPropertiesCorrect( + [ + 'id' => null, + 'identifier' => null, + 'policies' => [], + ], + new Role() + ); + } + + /** + * Test retrieving missing property. + * + * @covers \Ibexa\Core\Repository\Values\User\Role::__get + */ + public function testMissingProperty() + { + $this->expectException(PropertyNotFoundException::class); + + $role = new Role(); + $value = $role->notDefined; + self::fail('Succeeded getting non existing property'); + } + + /** + * Test setting read only property. + * + * @covers \Ibexa\Core\Repository\Values\User\Role::__set + */ + public function testReadOnlyProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $role = new Role(); + $role->id = 42; + self::fail('Succeeded setting read only property'); + } + + /** + * Test if property exists. + */ + public function testIsPropertySet() + { + $role = new Role(); + $value = isset($role->notDefined); + self::assertFalse($value); + + $value = isset($role->id); + self::assertTrue($value); + } + + /** + * Test unsetting a property. + * + * @covers \Ibexa\Core\Repository\Values\User\Role::__unset + */ + public function testUnsetProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $role = new Role(['id' => 1]); + unset($role->id); + self::fail('Unsetting read-only property succeeded'); + } +} + +class_alias(RoleTest::class, 'eZ\Publish\Core\Repository\Tests\Values\User\RoleTest'); diff --git a/tests/lib/Repository/Values/User/UserGroupTest.php b/tests/lib/Repository/Values/User/UserGroupTest.php new file mode 100644 index 0000000000..755dac342f --- /dev/null +++ b/tests/lib/Repository/Values/User/UserGroupTest.php @@ -0,0 +1,131 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Values\User; + +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyReadOnlyException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\User\UserGroup; +use Ibexa\Tests\Core\Repository\Values\ValueObjectTestTrait; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Repository\Values\User\UserGroup + */ +class UserGroupTest extends TestCase +{ + use ValueObjectTestTrait; + + public function testNewClass() + { + $group = new UserGroup(); + self::assertNull($group->parentId); + + $this->assertPropertiesCorrect( + [ + 'parentId' => null, + ], + $group + ); + } + + /** + * Test getName method. + */ + public function testGetName() + { + $name = 'Translated name'; + $contentMock = $this->createMock(Content::class); + $versionInfoMock = $this->createMock(VersionInfo::class); + + $contentMock->expects($this->once()) + ->method('getVersionInfo') + ->willReturn($versionInfoMock); + + $versionInfoMock->expects($this->once()) + ->method('getName') + ->willReturn($name); + + $object = new UserGroup(['content' => $contentMock]); + + $this->assertEquals($name, $object->getName()); + } + + /** + * Test retrieving missing property. + */ + public function testMissingProperty() + { + $this->expectException(PropertyNotFoundException::class); + + $userGroup = new UserGroup(); + $value = $userGroup->notDefined; + self::fail('Succeeded getting non existing property'); + } + + public function testObjectProperties() + { + $object = new UserGroup(); + $properties = $object->attributes(); + self::assertNotContains('internalFields', $properties, 'Internal property found '); + self::assertContains('id', $properties, 'Property not found '); + self::assertContains('fields', $properties, 'Property not found '); + self::assertContains('versionInfo', $properties, 'Property not found '); + self::assertContains('contentInfo', $properties, 'Property not found '); + + // check for duplicates and double check existence of property + $propertiesHash = []; + foreach ($properties as $property) { + if (isset($propertiesHash[$property])) { + self::fail("Property '{$property}' exists several times in properties list"); + } elseif (!isset($object->$property)) { + self::fail("Property '{$property}' does not exist on object, even though it was hinted to be there"); + } + $propertiesHash[$property] = 1; + } + } + + /** + * Test setting read only property. + */ + public function testReadOnlyProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $userGroup = new UserGroup(); + $userGroup->parentId = 42; + self::fail('Succeeded setting read only property'); + } + + /** + * Test if property exists. + */ + public function testIsPropertySet() + { + $userGroup = new UserGroup(); + $value = isset($userGroup->notDefined); + self::assertFalse($value); + + $value = isset($userGroup->parentId); + self::assertTrue($value); + } + + /** + * Test unsetting a property. + */ + public function testUnsetProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $userGroup = new UserGroup(['parentId' => 1]); + unset($userGroup->parentId); + self::fail('Unsetting read-only property succeeded'); + } +} + +class_alias(UserGroupTest::class, 'eZ\Publish\Core\Repository\Tests\Values\User\UserGroupTest'); diff --git a/tests/lib/Repository/Values/User/UserTest.php b/tests/lib/Repository/Values/User/UserTest.php new file mode 100644 index 0000000000..24b2cf13f3 --- /dev/null +++ b/tests/lib/Repository/Values/User/UserTest.php @@ -0,0 +1,138 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Values\User; + +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyNotFoundException; +use Ibexa\Contracts\Core\Repository\Exceptions\PropertyReadOnlyException; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\Content\VersionInfo; +use Ibexa\Core\Repository\Values\User\User; +use Ibexa\Tests\Core\Repository\Values\ValueObjectTestTrait; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Repository\Values\User\User + */ +class UserTest extends TestCase +{ + use ValueObjectTestTrait; + + /** + * Test a new class and default values on properties. + */ + public function testNewClass() + { + $user = new User(); + + $this->assertPropertiesCorrect( + [ + 'login' => null, + 'email' => null, + 'passwordHash' => null, + 'hashAlgorithm' => null, + 'maxLogin' => null, + 'enabled' => false, + ], + $user + ); + } + + /** + * Test getName method. + */ + public function testGetName() + { + $name = 'Translated name'; + $contentMock = $this->createMock(Content::class); + $versionInfoMock = $this->createMock(VersionInfo::class); + + $contentMock->expects($this->once()) + ->method('getVersionInfo') + ->willReturn($versionInfoMock); + + $versionInfoMock->expects($this->once()) + ->method('getName') + ->willReturn($name); + + $object = new User(['content' => $contentMock]); + + $this->assertEquals($name, $object->getName()); + } + + /** + * Test retrieving missing property. + */ + public function testMissingProperty() + { + $this->expectException(PropertyNotFoundException::class); + + $user = new User(); + $value = $user->notDefined; + self::fail('Succeeded getting non existing property'); + } + + public function testObjectProperties() + { + $object = new User(); + $properties = $object->attributes(); + self::assertNotContains('internalFields', $properties, 'Internal property found '); + self::assertContains('id', $properties, 'Property not found '); + self::assertContains('fields', $properties, 'Property not found '); + self::assertContains('versionInfo', $properties, 'Property not found '); + self::assertContains('contentInfo', $properties, 'Property not found '); + + // check for duplicates and double check existence of property + $propertiesHash = []; + foreach ($properties as $property) { + if (isset($propertiesHash[$property])) { + self::fail("Property '{$property}' exists several times in properties list"); + } elseif (!isset($object->$property)) { + self::fail("Property '{$property}' does not exist on object, even though it was hinted to be there"); + } + $propertiesHash[$property] = 1; + } + } + + /** + * Test setting read only property. + */ + public function testReadOnlyProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $user = new User(); + $user->login = 'user'; + self::fail('Succeeded setting read only property'); + } + + /** + * Test if property exists. + */ + public function testIsPropertySet() + { + $user = new User(); + $value = isset($user->notDefined); + self::assertFalse($value); + + $value = isset($user->login); + self::assertTrue($value); + } + + /** + * Test unsetting a property. + */ + public function testUnsetProperty() + { + $this->expectException(PropertyReadOnlyException::class); + + $user = new User(['login' => 'admin']); + unset($user->login); + self::fail('Unsetting read-only property succeeded'); + } +} + +class_alias(UserTest::class, 'eZ\Publish\Core\Repository\Tests\Values\User\UserTest'); diff --git a/tests/lib/Repository/Values/ValueObjectTestTrait.php b/tests/lib/Repository/Values/ValueObjectTestTrait.php new file mode 100644 index 0000000000..d92dd19d37 --- /dev/null +++ b/tests/lib/Repository/Values/ValueObjectTestTrait.php @@ -0,0 +1,32 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Repository\Values; + +use Ibexa\Contracts\Core\Repository\Values\ValueObject; + +trait ValueObjectTestTrait +{ + /** + * Asserts that properties given in $expectedValues are correctly set in + * $mockedValueObject. + * + * @param mixed[] $expectedValues + * @param \Ibexa\Contracts\Core\Repository\Values\ValueObject $actualValueObject + */ + public function assertPropertiesCorrect(array $expectedValues, ValueObject $actualValueObject) + { + foreach ($expectedValues as $propertyName => $propertyValue) { + self::assertSame( + $propertyValue, + $actualValueObject->$propertyName, + sprintf('Property %s value is incorrect', $propertyName) + ); + } + } +} + +class_alias(ValueObjectTestTrait::class, 'eZ\Publish\API\Repository\Tests\Values\ValueObjectTestTrait'); diff --git a/tests/lib/Search/Common/EventSubscriber/TrashEventSubscriberTest.php b/tests/lib/Search/Common/EventSubscriber/TrashEventSubscriberTest.php index 1dbb291d71..4fb72112c8 100644 --- a/tests/lib/Search/Common/EventSubscriber/TrashEventSubscriberTest.php +++ b/tests/lib/Search/Common/EventSubscriber/TrashEventSubscriberTest.php @@ -8,25 +8,25 @@ namespace Ibexa\Tests\Core\Search\Common\EventSubscriber; -use eZ\Publish\API\Repository\Events\Trash\DeleteTrashItemEvent; -use eZ\Publish\API\Repository\Values\Content\Trash\TrashItemDeleteResult; -use eZ\Publish\Core\Repository\Values\Content\TrashItem; -use eZ\Publish\Core\Search\Common\EventSubscriber\TrashEventSubscriber; -use eZ\Publish\SPI\Persistence\Content as PersistenceContent; -use eZ\Publish\SPI\Persistence\Content\Handler; -use eZ\Publish\SPI\Persistence\Handler as PersistenceHandler; -use eZ\Publish\SPI\Search\Handler as SearchHandler; +use Ibexa\Contracts\Core\Persistence\Content as PersistenceContent; +use Ibexa\Contracts\Core\Persistence\Content\Handler; +use Ibexa\Contracts\Core\Persistence\Handler as PersistenceHandler; +use Ibexa\Contracts\Core\Repository\Events\Trash\DeleteTrashItemEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\Trash\TrashItemDeleteResult; +use Ibexa\Contracts\Core\Search\Handler as SearchHandler; +use Ibexa\Core\Repository\Values\Content\TrashItem; +use Ibexa\Core\Search\Common\EventSubscriber\TrashEventSubscriber; use PHPUnit\Framework\TestCase; final class TrashEventSubscriberTest extends TestCase { - /** @var \eZ\Publish\SPI\Search\Handler&\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Search\Handler&\PHPUnit\Framework\MockObject\MockObject */ private $searchHandler; - /** @var \eZ\Publish\SPI\Persistence\Handler&\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Handler&\PHPUnit\Framework\MockObject\MockObject */ private $persistenceHandler; - /** @var \eZ\Publish\Core\Search\Common\EventSubscriber\TrashEventSubscriber */ + /** @var \Ibexa\Core\Search\Common\EventSubscriber\TrashEventSubscriber */ private $subscriber; protected function setUp(): void diff --git a/tests/lib/Search/Common/FieldValueMapper/AggregateTest.php b/tests/lib/Search/Common/FieldValueMapper/AggregateTest.php index df66bae410..ee34111799 100644 --- a/tests/lib/Search/Common/FieldValueMapper/AggregateTest.php +++ b/tests/lib/Search/Common/FieldValueMapper/AggregateTest.php @@ -8,22 +8,22 @@ namespace Ibexa\Tests\Core\Search\Common\FieldValueMapper; -use eZ\Publish\API\Repository\Exceptions\NotImplementedException; -use eZ\Publish\Core\Search\Common\FieldValueMapper\Aggregate; -use eZ\Publish\Core\Search\Common\FieldValueMapper\BooleanMapper; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\BooleanField; -use eZ\Publish\SPI\Search\FieldType\FloatField; +use Ibexa\Contracts\Core\Repository\Exceptions\NotImplementedException; +use Ibexa\Contracts\Core\Search\Field; +use Ibexa\Contracts\Core\Search\FieldType\BooleanField; +use Ibexa\Contracts\Core\Search\FieldType\FloatField; +use Ibexa\Core\Search\Common\FieldValueMapper\Aggregate; +use Ibexa\Core\Search\Common\FieldValueMapper\BooleanMapper; use PHPUnit\Framework\TestCase; /** - * @covers \eZ\Publish\Core\Search\Common\FieldValueMapper\Aggregate + * @covers \Ibexa\Core\Search\Common\FieldValueMapper\Aggregate */ final class AggregateTest extends TestCase { private const MAPPED_VALUE = true; - /** @var \eZ\Publish\Core\Search\Common\FieldValueMapper\Aggregate */ + /** @var \Ibexa\Core\Search\Common\FieldValueMapper\Aggregate */ private $aggregateMapper; public function setUp(): void diff --git a/eZ/Publish/Core/Search/Tests/Common/FieldValueMapper/RemoteIdentifierMapperTest.php b/tests/lib/Search/Common/FieldValueMapper/RemoteIdentifierMapperTest.php similarity index 81% rename from eZ/Publish/Core/Search/Tests/Common/FieldValueMapper/RemoteIdentifierMapperTest.php rename to tests/lib/Search/Common/FieldValueMapper/RemoteIdentifierMapperTest.php index a32318c599..2d09349b83 100644 --- a/eZ/Publish/Core/Search/Tests/Common/FieldValueMapper/RemoteIdentifierMapperTest.php +++ b/tests/lib/Search/Common/FieldValueMapper/RemoteIdentifierMapperTest.php @@ -6,19 +6,22 @@ */ declare(strict_types=1); -namespace eZ\Publish\Core\Search\Tests\Common\FieldValueMapper; +namespace Ibexa\Tests\Core\Search\Common\FieldValueMapper; -use eZ\Publish\Core\Search\Common\FieldValueMapper\RemoteIdentifierMapper; -use eZ\Publish\Core\Search\Tests\TestCase; -use eZ\Publish\SPI\Search\Field; -use eZ\Publish\SPI\Search\FieldType\IdentifierField; -use eZ\Publish\SPI\Search\FieldType\IntegerField; -use eZ\Publish\SPI\Search\FieldType\RemoteIdentifierField; -use eZ\Publish\SPI\Search\FieldType\StringField; +use Ibexa\Contracts\Core\Search\Field; +use Ibexa\Contracts\Core\Search\FieldType\IdentifierField; +use Ibexa\Contracts\Core\Search\FieldType\IntegerField; +use Ibexa\Contracts\Core\Search\FieldType\RemoteIdentifierField; +use Ibexa\Contracts\Core\Search\FieldType\StringField; +use Ibexa\Core\Search\Common\FieldValueMapper\RemoteIdentifierMapper; +use Ibexa\Tests\Core\Search\TestCase; +/** + * @covers \Ibexa\Core\Search\Common\FieldValueMapper\RemoteIdentifierMapper + */ final class RemoteIdentifierMapperTest extends TestCase { - /** @var \eZ\Publish\Core\Search\Common\FieldValueMapper\RemoteIdentifierMapper */ + /** @var \Ibexa\Core\Search\Common\FieldValueMapper\RemoteIdentifierMapper */ private $mapper; protected function setUp(): void @@ -27,8 +30,6 @@ protected function setUp(): void } /** - * @covers \eZ\Publish\Core\Search\Common\FieldValueMapper\RemoteIdentifierMapper::canMap - * * @dataProvider getDataForTestCanMap */ public function testCanMap(Field $field, bool $canMap): void @@ -60,8 +61,6 @@ public function getDataForTestCanMap(): iterable } /** - * @covers \eZ\Publish\Core\Search\Common\FieldValueMapper\IdentifierMapper::map - * * @dataProvider getDataForTestMap */ public function testMap(Field $field, string $expectedMappedValue): void @@ -141,3 +140,5 @@ public function getDataForTestMap(): iterable ]; } } + +class_alias(RemoteIdentifierMapperTest::class, 'eZ\Publish\Core\Search\Tests\Common\FieldValueMapper\RemoteIdentifierMapperTest'); diff --git a/tests/lib/Search/Common/LocationEventSubscriber/LocationEventSubscriberTest.php b/tests/lib/Search/Common/LocationEventSubscriber/LocationEventSubscriberTest.php new file mode 100644 index 0000000000..c7fbb1a9a7 --- /dev/null +++ b/tests/lib/Search/Common/LocationEventSubscriber/LocationEventSubscriberTest.php @@ -0,0 +1,130 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Search\Common\LocationEventSubscriber; + +use Ibexa\Contracts\Core\Persistence\Content as SPIContent; +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo as SPIContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\Handler as ContentHandler; +use Ibexa\Contracts\Core\Persistence\Content\Location as SPILocation; +use Ibexa\Contracts\Core\Persistence\Content\Location\Handler as LocationHandler; +use Ibexa\Contracts\Core\Persistence\Content\VersionInfo as SPIVersionInfo; +use Ibexa\Contracts\Core\Persistence\Handler as PersistenceHandler; +use Ibexa\Contracts\Core\Repository\Events\Location\CreateLocationEvent; +use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationCreateStruct; +use Ibexa\Core\Repository\Values\Content\Location; +use Ibexa\Core\Search\Common\EventSubscriber\LocationEventSubscriber; +use Ibexa\Core\Search\Legacy\Content\Handler as SearchHandler; +use PHPUnit\Framework\TestCase; + +final class LocationEventSubscriberTest extends TestCase +{ + private const EXAMPLE_LOCATION_ID = 54; + private const EXAMPLE_CONTENT_ID = 56; + private const EXAMPLE_VERSION_NO = 3; + + /** @var \Ibexa\Core\Search\Legacy\Content\Handler|\PHPUnit\Framework\MockObject\MockObject */ + private $searchHandler; + + /** @var \Ibexa\Contracts\Core\Persistence\Handler|\PHPUnit\Framework\MockObject\MockObject */ + private $persistenceHandler; + + /** @var \Ibexa\Core\Search\Common\EventSubscriber\LocationEventSubscriber */ + private $subscriber; + + protected function setUp(): void + { + $this->searchHandler = $this->createMock(SearchHandler::class); + $this->persistenceHandler = $this->createMock(PersistenceHandler::class); + + $this->subscriber = new LocationEventSubscriber( + $this->searchHandler, + $this->persistenceHandler + ); + } + + public function testOnCreateLocation(): void + { + $spiLocation = $this->getExampleSPILocation(); + $spiContent = $this->getExampleSPIContent(); + + $this->configurePersistenceHandler($spiContent, $spiLocation); + + $this->searchHandler->expects($this->atLeastOnce())->method('indexContent')->with($spiContent); + $this->searchHandler->expects($this->atLeastOnce())->method('indexLocation')->with($spiLocation); + + $this->subscriber->onCreateLocation( + new CreateLocationEvent( + $this->getExampleAPILocation(), + $this->getExampleAPIContentInfo(), + new LocationCreateStruct() + ) + ); + } + + private function configurePersistenceHandler(SPIContent $spiContent, SPILocation $spiLocation): void + { + $contentHandler = $this->createMock(ContentHandler::class); + $contentHandler + ->method('loadContentInfo') + ->with(self::EXAMPLE_CONTENT_ID) + ->willReturn($this->getExampleSPIContentInfo()); + + $contentHandler + ->method('load') + ->with(self::EXAMPLE_CONTENT_ID, self::EXAMPLE_VERSION_NO) + ->willReturn($spiContent); + + $locationHandler = $this->createMock(LocationHandler::class); + $locationHandler->method('load')->with(self::EXAMPLE_LOCATION_ID)->willReturn($spiLocation); + + $this->persistenceHandler->method('locationHandler')->willReturn($locationHandler); + $this->persistenceHandler->method('contentHandler')->willReturn($contentHandler); + } + + private function getExampleAPIContentInfo(): ContentInfo + { + return new ContentInfo([ + 'id' => self::EXAMPLE_CONTENT_ID, + 'currentVersionNo' => self::EXAMPLE_VERSION_NO, + ]); + } + + private function getExampleAPILocation(): Location + { + return new Location(['id' => self::EXAMPLE_LOCATION_ID]); + } + + private function getExampleSPILocation(): SPILocation + { + return new SPILocation([ + 'id' => self::EXAMPLE_LOCATION_ID, + ]); + } + + private function getExampleSPIContent(): SPIContent + { + return new SPIContent([ + 'versionInfo' => new SPIVersionInfo([ + 'id' => self::EXAMPLE_CONTENT_ID, + 'versionNo' => self::EXAMPLE_VERSION_NO, + ]), + ]); + } + + private function getExampleSPIContentInfo(): SPIContentInfo + { + return new SPIContentInfo([ + 'id' => self::EXAMPLE_CONTENT_ID, + 'currentVersionNo' => self::EXAMPLE_VERSION_NO, + ]); + } +} + +class_alias(LocationEventSubscriberTest::class, 'eZ\Publish\Core\Search\Tests\Common\LocationEventSubscriber\LocationEventSubscriberTest'); diff --git a/eZ/Publish/Core/Search/Tests/FieldNameResolverTest.php b/tests/lib/Search/FieldNameResolverTest.php similarity index 93% rename from eZ/Publish/Core/Search/Tests/FieldNameResolverTest.php rename to tests/lib/Search/FieldNameResolverTest.php index a4b17f737c..03b898e244 100644 --- a/eZ/Publish/Core/Search/Tests/FieldNameResolverTest.php +++ b/tests/lib/Search/FieldNameResolverTest.php @@ -4,23 +4,21 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Tests; +namespace Ibexa\Tests\Core\Search; use ArrayObject; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion as APICriterion; -use eZ\Publish\API\Repository\Values\Content\Query\CustomFieldInterface; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause as APISortClause; -use eZ\Publish\Core\Search\Common\FieldNameGenerator; -use eZ\Publish\Core\Search\Common\FieldNameResolver; -use eZ\Publish\Core\Search\Common\FieldRegistry; -use eZ\Publish\SPI\FieldType\Indexable; -use eZ\Publish\SPI\Persistence\Content\Type\Handler as SPIContentTypeHandler; -use eZ\Publish\SPI\Search\FieldType as SPIFieldType; +use Ibexa\Contracts\Core\FieldType\Indexable; +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as SPIContentTypeHandler; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion as APICriterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\CustomFieldInterface; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause as APISortClause; +use Ibexa\Contracts\Core\Search\FieldType as SPIFieldType; +use Ibexa\Core\Search\Common\FieldNameGenerator; +use Ibexa\Core\Search\Common\FieldNameResolver; +use Ibexa\Core\Search\Common\FieldRegistry; /** - * Test case for FieldNameResolver. - * - * @covers \eZ\Publish\Core\Search\Common\FieldNameResolver + * @covers \Ibexa\Core\Search\Common\FieldNameResolver */ class FieldNameResolverTest extends TestCase { @@ -766,7 +764,7 @@ public function testGetIndexFieldNameNamedFieldThrowsRuntimeException() /** * @param array $methods * - * @return \eZ\Publish\Core\Search\Common\FieldNameResolver|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Search\Common\FieldNameResolver|\PHPUnit\Framework\MockObject\MockObject */ protected function getMockedFieldNameResolver(array $methods = []) { @@ -785,11 +783,11 @@ protected function getMockedFieldNameResolver(array $methods = []) return $fieldNameResolver; } - /** @var \eZ\Publish\Core\Search\Common\FieldRegistry|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\Search\Common\FieldRegistry|\PHPUnit\Framework\MockObject\MockObject */ protected $fieldRegistryMock; /** - * @return \eZ\Publish\Core\Search\Common\FieldRegistry|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Search\Common\FieldRegistry|\PHPUnit\Framework\MockObject\MockObject */ protected function getFieldRegistryMock() { @@ -801,7 +799,7 @@ protected function getFieldRegistryMock() } /** - * @return \eZ\Publish\SPI\FieldType\Indexable|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\FieldType\Indexable|\PHPUnit\Framework\MockObject\MockObject */ protected function getIndexFieldTypeMock() { @@ -809,18 +807,18 @@ protected function getIndexFieldTypeMock() } /** - * @return \eZ\Publish\SPI\Search\FieldType|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\Search\FieldType|\PHPUnit\Framework\MockObject\MockObject */ protected function getSearchFieldTypeMock() { return $this->createMock(SPIFieldType::class); } - /** @var \eZ\Publish\SPI\Persistence\Content\Type\Handler|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Contracts\Core\Persistence\Content\Type\Handler|\PHPUnit\Framework\MockObject\MockObject */ protected $contentTypeHandlerMock; /** - * @return \eZ\Publish\SPI\Persistence\Content\Type\Handler|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\Persistence\Content\Type\Handler|\PHPUnit\Framework\MockObject\MockObject */ protected function getContentTypeHandlerMock() { @@ -831,11 +829,11 @@ protected function getContentTypeHandlerMock() return $this->contentTypeHandlerMock; } - /** @var \eZ\Publish\Core\Search\Common\FieldNameGenerator|\PHPUnit\Framework\MockObject\MockObject */ + /** @var \Ibexa\Core\Search\Common\FieldNameGenerator|\PHPUnit\Framework\MockObject\MockObject */ protected $fieldNameGeneratorMock; /** - * @return \eZ\Publish\Core\Search\Common\FieldNameGenerator|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Core\Search\Common\FieldNameGenerator|\PHPUnit\Framework\MockObject\MockObject */ protected function getFieldNameGeneratorMock() { @@ -847,7 +845,7 @@ protected function getFieldNameGeneratorMock() } /** - * @return \eZ\Publish\API\Repository\Values\Content\Query\Criterion|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion|\PHPUnit\Framework\MockObject\MockObject */ protected function getCriterionMock() { @@ -855,10 +853,12 @@ protected function getCriterionMock() } /** - * @return \eZ\Publish\API\Repository\Values\Content\Query\SortClause|\PHPUnit\Framework\MockObject\MockObject + * @return \Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause|\PHPUnit\Framework\MockObject\MockObject */ protected function getSortClauseMock() { return $this->createMock(APISortClause::class); } } + +class_alias(FieldNameResolverTest::class, 'eZ\Publish\Core\Search\Tests\FieldNameResolverTest'); diff --git a/tests/lib/Search/Indexer/ContentIdBatchListTest.php b/tests/lib/Search/Indexer/ContentIdBatchListTest.php new file mode 100644 index 0000000000..1a4a2d6887 --- /dev/null +++ b/tests/lib/Search/Indexer/ContentIdBatchListTest.php @@ -0,0 +1,120 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Search\Indexer; + +use ArrayIterator; +use Generator; +use Ibexa\Core\Search\Indexer\ContentIdBatchList; +use IteratorAggregate; +use PHPUnit\Framework\TestCase; +use Traversable; + +/** + * @covers \Ibexa\Core\Search\Indexer\ContentIdBatchList + */ +final class ContentIdBatchListTest extends TestCase +{ + /** + * @return iterable<string, array{iterable<int, array<int>>, int, array<int, array<int>>}> + */ + public function getDataForTestGetIterator(): iterable + { + yield 'generator' => [ + $this->buildGenerator(), + 5, + [ + [1, 2, 3], + [4, 5], + ], + ]; + + yield 'array' => [ + [ + [1, 2], + [3, 4], + [5], + ], + 5, + [ + [1, 2], + [3, 4], + [5], + ], + ]; + + yield 'Traversable object' => [ + $this->buildTraversableObject(), + 5, + [ + [1, 2, 3, 4], + [5], + ], + ]; + + yield 'empty generator' => [ + $this->buildEmptyGenerator(), + 0, + [], + ]; + } + + /** + * @dataProvider getDataForTestGetIterator + * + * @param iterable<int, array<int>> $list + * @param array<int, array<int>> $expectedBatches + */ + public function testGetIterator(iterable $list, int $totalCount, array $expectedBatches): void + { + $contentIdBatchList = new ContentIdBatchList($list, $totalCount); + $unpackedActualBatches = []; + foreach ($contentIdBatchList as $index => $items) { + $unpackedActualBatches[$index] = $items; + } + self::assertSame($expectedBatches, $unpackedActualBatches); + } + + public function testGetCount(): void + { + $contentIdBatchList = new ContentIdBatchList([[1, 2, 3]], 3); + self::assertSame(3, $contentIdBatchList->getCount()); + } + + private function buildGenerator(): Generator + { + yield [1, 2, 3]; + yield [4, 5]; + } + + private function buildEmptyGenerator(): \Generator + { + yield from []; + } + + /** + * @return \Traversable<int, array<int>> + */ + private function buildTraversableObject(): Traversable + { + return new class() implements IteratorAggregate { + /** + * @return \ArrayIterator<int, array{int, int, int, int}|array{int}> + */ + public function getIterator(): ArrayIterator + { + return new ArrayIterator( + [ + [1, 2, 3, 4], + [5], + ] + ); + } + }; + } +} diff --git a/tests/lib/Search/Legacy/Content/AbstractTestCase.php b/tests/lib/Search/Legacy/Content/AbstractTestCase.php new file mode 100644 index 0000000000..0ae8a3bf37 --- /dev/null +++ b/tests/lib/Search/Legacy/Content/AbstractTestCase.php @@ -0,0 +1,143 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Search\Legacy\Content; + +use Ibexa\Contracts\Core\Persistence\Content\Type\Handler as SPIContentTypeHandler; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\Converter; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; +use Ibexa\Core\Persistence\Legacy\Content\Gateway; +use Ibexa\Core\Persistence\Legacy\Content\Mapper\ResolveVirtualFieldSubscriber; +use Ibexa\Core\Persistence\Legacy\Content\StorageRegistry; +use Ibexa\Core\Persistence\Legacy\Content\Type\Gateway\DoctrineDatabase as ContentTypeGateway; +use Ibexa\Core\Persistence\Legacy\Content\Type\Handler as ContentTypeHandler; +use Ibexa\Core\Persistence\Legacy\Content\Type\Mapper as ContentTypeMapper; +use Ibexa\Core\Persistence\Legacy\Content\Type\StorageDispatcherInterface; +use Ibexa\Core\Persistence\Legacy\Content\Type\Update\Handler as ContentTypeUpdateHandler; +use Ibexa\Tests\Core\Persistence\Legacy\Content\LanguageAwareTestCase; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; + +/** + * Abstract test suite for legacy search. + */ +class AbstractTestCase extends LanguageAwareTestCase +{ + /** @var bool */ + private static $databaseInitialized = false; + + /** + * Field registry mock. + * + * @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry + */ + private $converterRegistry; + + /** @var \Ibexa\Contracts\Core\Persistence\Content\Type\Handler */ + private $contentTypeHandler; + + /** + * Only set up once for these read only tests on a large fixture. + * + * Skipping the reset-up, since setting up for these tests takes quite some + * time, which is not required to spent, since we are only reading from the + * database anyways. + */ + protected function setUp(): void + { + if (!self::$databaseInitialized) { + parent::setUp(); + $this->insertDatabaseFixture(__DIR__ . '/../_fixtures/full_dump.php'); + self::$databaseInitialized = true; + } + } + + /** + * Assert that the elements are. + */ + protected function assertSearchResults($expectedIds, $searchResult) + { + $ids = $this->getIds($searchResult); + $this->assertEquals($expectedIds, $ids); + } + + protected function getIds($searchResult) + { + $ids = array_map( + static function ($hit) { + return $hit->valueObject->id; + }, + $searchResult->searchHits + ); + + sort($ids); + + return $ids; + } + + /** + * @throws \Doctrine\DBAL\DBALException + */ + protected function getContentTypeHandler(): SPIContentTypeHandler + { + if (!isset($this->contentTypeHandler)) { + $this->contentTypeHandler = new ContentTypeHandler( + new ContentTypeGateway( + $this->getDatabaseConnection(), + $this->getSharedGateway(), + $this->getLanguageMaskGenerator(), + ), + new ContentTypeMapper( + $this->getConverterRegistry(), + $this->getLanguageMaskGenerator(), + $this->createMock(StorageDispatcherInterface::class) + ), + $this->createMock(ContentTypeUpdateHandler::class), + $this->createMock(StorageDispatcherInterface::class) + ); + } + + return $this->contentTypeHandler; + } + + protected function getConverterRegistry() + { + if (!isset($this->converterRegistry)) { + $this->converterRegistry = new ConverterRegistry( + [ + 'ezdatetime' => new Converter\DateAndTimeConverter(), + 'ezinteger' => new Converter\IntegerConverter(), + 'ezstring' => new Converter\TextLineConverter(), + 'ezfloat' => new Converter\FloatConverter(), + 'ezurl' => new Converter\UrlConverter(), + 'ezboolean' => new Converter\CheckboxConverter(), + 'ezkeyword' => new Converter\KeywordConverter(), + 'ezauthor' => new Converter\AuthorConverter(), + 'ezimage' => new Converter\NullConverter(), + 'ezmultioption' => new Converter\NullConverter(), + ] + ); + } + + return $this->converterRegistry; + } + + protected function getEventDispatcher(): EventDispatcherInterface + { + $eventDispatcher = new EventDispatcher(); + $eventDispatcher->addSubscriber( + new ResolveVirtualFieldSubscriber( + $this->getConverterRegistry(), + $this->createMock(StorageRegistry::class), + $this->createMock(Gateway::class) + ) + ); + + return $eventDispatcher; + } +} + +class_alias(AbstractTestCase::class, 'eZ\Publish\Core\Search\Legacy\Tests\Content\AbstractTestCase'); diff --git a/eZ/Publish/Core/Search/Legacy/Tests/Content/HandlerContentSortTest.php b/tests/lib/Search/Legacy/Content/HandlerContentSortTest.php similarity index 90% rename from eZ/Publish/Core/Search/Legacy/Tests/Content/HandlerContentSortTest.php rename to tests/lib/Search/Legacy/Content/HandlerContentSortTest.php index 9d057cbe4b..ec24e4efcb 100644 --- a/eZ/Publish/Core/Search/Legacy/Tests/Content/HandlerContentSortTest.php +++ b/tests/lib/Search/Legacy/Content/HandlerContentSortTest.php @@ -4,18 +4,18 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Legacy\Tests\Content; - -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper as ContentMapper; -use eZ\Publish\Core\Search\Legacy\Content; -use eZ\Publish\Core\Search\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; +namespace Ibexa\Tests\Core\Search\Legacy\Content; + +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Core\Persistence\Legacy\Content\FieldHandler; +use Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry; +use Ibexa\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; +use Ibexa\Core\Persistence\Legacy\Content\Mapper as ContentMapper; +use Ibexa\Core\Search\Legacy\Content; +use Ibexa\Core\Search\Legacy\Content\Location\Gateway as LocationGateway; /** * Content Search test case for ContentSearchHandler. @@ -25,7 +25,7 @@ class HandlerContentSortTest extends AbstractTestCase /** * Field registry mock. * - * @var \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry + * @var \Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry */ protected $fieldRegistry; @@ -37,7 +37,7 @@ class HandlerContentSortTest extends AbstractTestCase * * @param array $fullTextSearchConfiguration * - * @return \eZ\Publish\Core\Search\Legacy\Content\Handler + * @return \Ibexa\Core\Search\Legacy\Content\Handler */ protected function getContentSearchHandler(array $fullTextSearchConfiguration = []) { @@ -92,7 +92,7 @@ protected function getContentSearchHandler(array $fullTextSearchConfiguration = /** * Returns a content mapper mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Mapper + * @return \Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected function getContentMapperMock() { @@ -133,7 +133,7 @@ static function ($rows) { /** * Returns a field registry mock object. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry + * @return \Ibexa\Core\Persistence\Legacy\Content\FieldValue\ConverterRegistry */ protected function getFieldRegistry() { @@ -150,7 +150,7 @@ protected function getFieldRegistry() /** * Returns a content field handler mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler + * @return \Ibexa\Core\Persistence\Legacy\Content\FieldHandler */ protected function getContentFieldHandlerMock() { @@ -402,21 +402,21 @@ public function testSortFieldText() 'duis auctor vehicula erat' => [89], 'etiam posuere sodales arcu' => [78], 'etiam sodales mauris' => [87], - 'ez publish enterprise' => [151], - 'fastcgi' => [144, 218], - 'fusce sagittis sagittis' => [77], - 'fusce sagittis sagittis urna' => [81], - 'get involved' => [107], - 'how to develop with ez publish' => [127, 211], - 'how to manage ez publish' => [118, 202], - 'how to use ez publish' => [108, 193], + 'ibexa dxp' => [144], + 'fastcgi' => [77, 218], + 'fusce sagittis sagittis' => [81], + 'fusce sagittis sagittis urna' => [107], + 'get involved' => [127], + 'how to develop with ibexa' => [118, 211], + 'how to manage ibexa' => [108, 202], + 'how to use ibexa' => [151, 193], 'improved block editing' => [136], 'improved front-end editing' => [139], 'improved user registration workflow' => [132], 'in hac habitasse platea' => [79], - 'lots of websites, one ez publish installation' => [130], + 'lots of websites, one ibexa installation' => [130], 'rest api interface' => [150, 214], - 'separate content & design in ez publish' => [191], + 'separate content & design in ibexa' => [191], 'support for red hat enterprise' => [145, 217], 'tutorials for' => [106], ]; @@ -471,3 +471,5 @@ static function ($hit) { ); } } + +class_alias(HandlerContentSortTest::class, 'eZ\Publish\Core\Search\Legacy\Tests\Content\HandlerContentSortTest'); diff --git a/eZ/Publish/Core/Search/Legacy/Tests/Content/HandlerContentTest.php b/tests/lib/Search/Legacy/Content/HandlerContentTest.php similarity index 97% rename from eZ/Publish/Core/Search/Legacy/Tests/Content/HandlerContentTest.php rename to tests/lib/Search/Legacy/Content/HandlerContentTest.php index ef77af8f2f..3f37fe0a27 100644 --- a/eZ/Publish/Core/Search/Legacy/Tests/Content/HandlerContentTest.php +++ b/tests/lib/Search/Legacy/Content/HandlerContentTest.php @@ -4,21 +4,21 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Legacy\Tests\Content; - -use eZ\Publish\API\Repository\Exceptions\InvalidArgumentException; -use eZ\Publish\API\Repository\Exceptions\NotFoundException; -use eZ\Publish\API\Repository\Values\Content\Query; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Persistence; -use eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper as ContentMapper; -use eZ\Publish\Core\Search\Legacy\Content; -use eZ\Publish\Core\Search\Legacy\Content\Location\Gateway as LocationGateway; -use eZ\Publish\SPI\Persistence\Content\ContentInfo; -use eZ\Publish\SPI\Persistence\Content\Type; +namespace Ibexa\Tests\Core\Search\Legacy\Content; + +use Ibexa\Contracts\Core\Persistence\Content\ContentInfo; +use Ibexa\Contracts\Core\Persistence\Content\Type; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Exceptions\NotFoundException; +use Ibexa\Contracts\Core\Repository\Values\Content\Query; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Core\Persistence; +use Ibexa\Core\Persistence\Legacy\Content\FieldHandler; +use Ibexa\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; +use Ibexa\Core\Persistence\Legacy\Content\Mapper as ContentMapper; +use Ibexa\Core\Search\Legacy\Content; +use Ibexa\Core\Search\Legacy\Content\Location\Gateway as LocationGateway; /** * Content Search test case for ContentSearchHandler. @@ -33,7 +33,7 @@ class HandlerContentTest extends AbstractTestCase * * @param array $fullTextSearchConfiguration * - * @return \eZ\Publish\Core\Search\Legacy\Content\Handler + * @return \Ibexa\Core\Search\Legacy\Content\Handler */ protected function getContentSearchHandler(array $fullTextSearchConfiguration = []) { @@ -191,7 +191,7 @@ protected function getContentSearchHandler(array $fullTextSearchConfiguration = /** * Returns a content mapper mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Mapper + * @return \Ibexa\Core\Persistence\Legacy\Content\Mapper */ protected function getContentMapperMock() { @@ -232,7 +232,7 @@ static function ($rows) { /** * Returns a content field handler mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\FieldHandler + * @return \Ibexa\Core\Persistence\Legacy\Content\FieldHandler */ protected function getContentFieldHandlerMock() { @@ -1447,3 +1447,5 @@ public function testGetNonExistingFieldDefinition(): void $this->getContentTypeHandler()->getFieldDefinition(0, Type::STATUS_DEFINED); } } + +class_alias(HandlerContentTest::class, 'eZ\Publish\Core\Search\Legacy\Tests\Content\HandlerContentTest'); diff --git a/eZ/Publish/Core/Search/Legacy/Tests/Content/HandlerLocationSortTest.php b/tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php similarity index 91% rename from eZ/Publish/Core/Search/Legacy/Tests/Content/HandlerLocationSortTest.php rename to tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php index 7ed0c42083..29299760a2 100644 --- a/eZ/Publish/Core/Search/Legacy/Tests/Content/HandlerLocationSortTest.php +++ b/tests/lib/Search/Legacy/Content/HandlerLocationSortTest.php @@ -4,22 +4,22 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Legacy\Tests\Content; - -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper as ContentMapper; -use eZ\Publish\Core\Search\Legacy\Content; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler as CommonCriterionHandler; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler as CommonSortClauseHandler; -use eZ\Publish\Core\Search\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler as LocationCriterionHandler; -use eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler as LocationSortClauseHandler; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; +namespace Ibexa\Tests\Core\Search\Legacy\Content; + +use Ibexa\Contracts\Core\Persistence\Content\Location as SPILocation; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; +use Ibexa\Core\Persistence\Legacy\Content\Mapper as ContentMapper; +use Ibexa\Core\Search\Legacy\Content; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler as CommonCriterionHandler; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler as CommonSortClauseHandler; +use Ibexa\Core\Search\Legacy\Content\Gateway as ContentGateway; +use Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler as LocationCriterionHandler; +use Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler as LocationSortClauseHandler; /** * Location Search test case for ContentSearchHandler. @@ -43,7 +43,7 @@ static function ($hit) { * * This method returns a fully functional search handler to perform tests on. * - * @return \eZ\Publish\Core\Search\Legacy\Content\Handler + * @return \Ibexa\Core\Search\Legacy\Content\Handler */ protected function getContentSearchHandler() { @@ -107,7 +107,7 @@ protected function getContentSearchHandler() /** * Returns a location mapper mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper + * @return \Ibexa\Core\Persistence\Legacy\Content\Location\Mapper */ protected function getLocationMapperMock() { @@ -538,21 +538,21 @@ public function testSortFieldText() 'duis auctor vehicula erat' => [91], 'etiam posuere sodales arcu' => [80], 'etiam sodales mauris' => [89], - 'ez publish enterprise' => [153], - 'fastcgi' => [146, 220], - 'fusce sagittis sagittis' => [79], - 'fusce sagittis sagittis urna' => [83], - 'get involved' => [109], - 'how to develop with ez publish' => [129, 213], - 'how to manage ez publish' => [120, 204], - 'how to use ez publish' => [110, 195], + 'ibexa dxp' => [146], + 'fastcgi' => [79, 220], + 'fusce sagittis sagittis' => [83], + 'fusce sagittis sagittis urna' => [109], + 'get involved' => [129], + 'how to develop with ibexa' => [120, 213], + 'how to manage ibexa' => [110, 204], + 'how to use ibexa' => [153, 195], 'improved block editing' => [138], 'improved front-end editing' => [141], 'improved user registration workflow' => [134], 'in hac habitasse platea' => [81], - 'lots of websites, one ez publish installation' => [132], + 'lots of websites, one ibexa installation' => [132], 'rest api interface' => [152, 216], - 'separate content & design in ez publish' => [193], + 'separate content & design in ibexa' => [193], 'support for red hat enterprise' => [147, 219], 'tutorials for' => [108], ]; @@ -653,3 +653,5 @@ public function testSortIsMainLocationDescending() ); } } + +class_alias(HandlerLocationSortTest::class, 'eZ\Publish\Core\Search\Legacy\Tests\Content\HandlerLocationSortTest'); diff --git a/eZ/Publish/Core/Search/Legacy/Tests/Content/HandlerLocationTest.php b/tests/lib/Search/Legacy/Content/HandlerLocationTest.php similarity index 96% rename from eZ/Publish/Core/Search/Legacy/Tests/Content/HandlerLocationTest.php rename to tests/lib/Search/Legacy/Content/HandlerLocationTest.php index 9b843c5b97..ce1010cfd2 100644 --- a/eZ/Publish/Core/Search/Legacy/Tests/Content/HandlerLocationTest.php +++ b/tests/lib/Search/Legacy/Content/HandlerLocationTest.php @@ -4,23 +4,24 @@ * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ -namespace eZ\Publish\Core\Search\Legacy\Tests\Content; - -use eZ\Publish\API\Repository\Values\Content\LocationQuery; -use eZ\Publish\API\Repository\Values\Content\Query\Criterion; -use eZ\Publish\API\Repository\Values\Content\Query\SortClause; -use eZ\Publish\Core\Persistence; -use eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; -use eZ\Publish\Core\Persistence\Legacy\Content\Mapper as ContentMapper; -use eZ\Publish\Core\Search\Legacy\Content; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler as CommonCriterionHandler; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter; -use eZ\Publish\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler as CommonSortClauseHandler; -use eZ\Publish\Core\Search\Legacy\Content\Gateway as ContentGateway; -use eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler as LocationCriterionHandler; -use eZ\Publish\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler as LocationSortClauseHandler; -use eZ\Publish\SPI\Persistence\Content\Location as SPILocation; +namespace Ibexa\Tests\Core\Search\Legacy\Content; + +use Ibexa\Contracts\Core\Persistence\Content\Location as SPILocation; +use Ibexa\Contracts\Core\Repository\Exceptions\InvalidArgumentException; +use Ibexa\Contracts\Core\Repository\Values\Content\LocationQuery; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\Criterion; +use Ibexa\Contracts\Core\Repository\Values\Content\Query\SortClause; +use Ibexa\Core\Persistence; +use Ibexa\Core\Persistence\Legacy\Content\Location\Mapper as LocationMapper; +use Ibexa\Core\Persistence\Legacy\Content\Mapper as ContentMapper; +use Ibexa\Core\Search\Legacy\Content; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriteriaConverter; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\CriterionHandler as CommonCriterionHandler; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseConverter; +use Ibexa\Core\Search\Legacy\Content\Common\Gateway\SortClauseHandler as CommonSortClauseHandler; +use Ibexa\Core\Search\Legacy\Content\Gateway as ContentGateway; +use Ibexa\Core\Search\Legacy\Content\Location\Gateway\CriterionHandler as LocationCriterionHandler; +use Ibexa\Core\Search\Legacy\Content\Location\Gateway\SortClauseHandler as LocationSortClauseHandler; /** * Location Search test case for ContentSearchHandler. @@ -34,7 +35,7 @@ class HandlerLocationTest extends AbstractTestCase * * @param array $fullTextSearchConfiguration * - * @return \eZ\Publish\Core\Search\Legacy\Content\Handler + * @return \Ibexa\Core\Search\Legacy\Content\Handler */ protected function getContentSearchHandler(array $fullTextSearchConfiguration = []) { @@ -167,7 +168,7 @@ protected function getContentSearchHandler(array $fullTextSearchConfiguration = /** * Returns a location mapper mock. * - * @return \eZ\Publish\Core\Persistence\Legacy\Content\Location\Mapper + * @return \Ibexa\Core\Persistence\Legacy\Content\Location\Mapper */ protected function getLocationMapperMock() { @@ -1161,7 +1162,7 @@ static function ($hit) { public function testFullTextFilterInvalidStopwordThreshold() { - $this->expectException(\eZ\Publish\API\Repository\Exceptions\InvalidArgumentException::class); + $this->expectException(InvalidArgumentException::class); $this->getContentSearchHandler( [ @@ -1464,3 +1465,5 @@ public function testIsNotMainLocationFilter() ); } } + +class_alias(HandlerLocationTest::class, 'eZ\Publish\Core\Search\Legacy\Tests\Content\HandlerLocationTest'); diff --git a/eZ/Publish/Core/Search/Legacy/Tests/_fixtures/full_dump.php b/tests/lib/Search/Legacy/_fixtures/full_dump.php similarity index 99% rename from eZ/Publish/Core/Search/Legacy/Tests/_fixtures/full_dump.php rename to tests/lib/Search/Legacy/_fixtures/full_dump.php index 0899aaa795..f510afad48 100644 --- a/eZ/Publish/Core/Search/Legacy/Tests/_fixtures/full_dump.php +++ b/tests/lib/Search/Legacy/_fixtures/full_dump.php @@ -1,7 +1,7 @@ <?php /** - * @copyright Copyright (C) eZ Systems AS. All rights reserved. + * @copyright Copyright (C) Ibexa AS. All rights reserved. * @license For full copyright and license information view LICENSE file distributed with this source code. */ return [ @@ -5792,7 +5792,7 @@ 'initial_language_id' => '2', 'language_mask' => '3', 'modified' => '1072180330', - 'name' => 'Anonymous Users', + 'name' => 'Anonymous users', 'owner_id' => '14', 'published' => '1072180330', 'remote_id' => '15b256dbea2ae72418ff5facc999e8f9', @@ -6618,7 +6618,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1299782140', - 'name' => 'How to use eZ Publish', + 'name' => 'How to use Ibexa', 'owner_id' => '14', 'published' => '1299772464', 'remote_id' => '2ded978c269bba85013b86c2964009be', @@ -6758,7 +6758,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1299772545', - 'name' => 'How to manage eZ Publish', + 'name' => 'How to manage Ibexa', 'owner_id' => '14', 'published' => '1299772545', 'remote_id' => '90ee91c224c69f28aa775cf9edc34ef3', @@ -6884,7 +6884,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1299782177', - 'name' => 'How to develop with eZ Publish', + 'name' => 'How to develop with Ibexa', 'owner_id' => '14', 'published' => '1299772637', 'remote_id' => '564f94afa87f70227199e951450b19a4', @@ -6926,7 +6926,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1284398357', - 'name' => 'Adding Siteaccesses in eZ Publish', + 'name' => 'Adding Siteaccesses in Ibexa', 'owner_id' => '14', 'published' => '1284398357', 'remote_id' => 'e02b80d7cd9059cf469aafbe7d2d1969', @@ -7220,7 +7220,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1299780653', - 'name' => 'eZ Publish Enterprise', + 'name' => 'Ibexa Enterprise', 'owner_id' => '14', 'published' => '1299780355', 'remote_id' => 'bea2aa5ed17c617531344b6c71aff596', @@ -7584,7 +7584,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1299773210', - 'name' => 'Ivo on eZ Tags', + 'name' => 'Ivo on ibexa tags', 'owner_id' => '14', 'published' => '1299772902', 'remote_id' => '32709d6df489b5debc03caddec908363', @@ -7780,7 +7780,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1193935923', - 'name' => 'Separate content & design in eZ Publish', + 'name' => 'Separate content & design in Ibexa', 'owner_id' => '14', 'published' => '1193907125', 'remote_id' => '932072228b62943d3d3521e7712b54e9', @@ -7808,7 +7808,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1193914552', - 'name' => 'How to use eZ Publish', + 'name' => 'How to use Ibexa', 'owner_id' => '14', 'published' => '1193908304', 'remote_id' => 'afad8dfca54311e15bfd241ef09da280', @@ -7934,7 +7934,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1299782207', - 'name' => 'How to manage eZ Publish', + 'name' => 'How to manage Ibexa', 'owner_id' => '14', 'published' => '1193908398', 'remote_id' => '81d949fde405dbe3e5bc6d83452bd927', @@ -8060,7 +8060,7 @@ 'initial_language_id' => '2', 'language_mask' => '2', 'modified' => '1193914399', - 'name' => 'How to develop with eZ Publish', + 'name' => 'How to develop with Ibexa', 'owner_id' => '14', 'published' => '1193908946', 'remote_id' => '96a6b67d897f8dda1053f2a56a734941', @@ -8585,7 +8585,7 @@ 'contentobject_id' => '42', 'data_float' => '0', 'data_int' => '0', - 'data_text' => 'Anonymous Users', + 'data_text' => 'Anonymous users', 'data_type_string' => 'ezstring', 'id' => '100', 'language_code' => 'eng-US', @@ -9035,8 +9035,8 @@ 'contentobject_id' => '54', 'data_float' => '0', 'data_int' => '0', - 'data_text' => 'author=eZ Systems -copyright=eZ Systems + 'data_text' => 'author=Ibexa +copyright=Ibexa description=Content Management System keywords=cms, publish, e-commerce, content management, development framework', 'data_type_string' => 'ezinisetting', @@ -9085,7 +9085,7 @@ 'contentobject_id' => '54', 'data_float' => '0', 'data_int' => '0', - 'data_text' => 'kn@ez.no', + 'data_text' => 'kn@ibexa.co', 'data_type_string' => 'ezinisetting', 'id' => '175', 'language_code' => 'eng-US', @@ -13332,7 +13332,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '564', @@ -13456,7 +13456,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '576', @@ -13549,13 +13549,13 @@ 'contentobject_id' => '108', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'How to use eZ Publish', + 'data_text' => 'How to use Ibexa', 'data_type_string' => 'ezstring', 'id' => '586', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'how to use ez publish', + 'sort_key_string' => 'how to use ibexa', 'version' => '1', ], 475 => [ @@ -13580,7 +13580,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '588', @@ -13612,7 +13612,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="How-to-use-eZ-Publish.jpg" suffix="jpg" basename="How-to-use-eZ-Publish" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/592-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/592-1-eng-US/How-to-use-eZ-Publish.jpg" original_filename="affa2705.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154185"><original attribute_id="592" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="How-to-use-Ibexa.jpg" suffix="jpg" basename="How-to-use-Ibexa" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/592-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/592-1-eng-US/How-to-use-Ibexa.jpg" original_filename="affa2705.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154185"><original attribute_id="592" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '592', @@ -13689,7 +13689,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics1/600-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics1/600-1-eng-US/graphics1.jpg" original_filename="c6a49b95.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="600" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics1/600-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics1/600-1-eng-US/graphics1.jpg" original_filename="c6a49b95.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="600" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '600', @@ -13736,7 +13736,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics12/604-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics12/604-1-eng-US/graphics1.jpg" original_filename="afc64b77.jpg" mime_type="image/jpeg" width="540" height="357" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="604" attribute_version="1" attribute_language="eng-US"/><information Height="357" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics12/604-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics12/604-1-eng-US/graphics1.jpg" original_filename="afc64b77.jpg" mime_type="image/jpeg" width="540" height="357" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="604" attribute_version="1" attribute_language="eng-US"/><information Height="357" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '604', @@ -13783,7 +13783,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics13/608-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics13/608-1-eng-US/graphics1.jpg" original_filename="ccb0f9f7.jpg" mime_type="image/jpeg" width="540" height="419" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="608" attribute_version="1" attribute_language="eng-US"/><information Height="419" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics13/608-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics13/608-1-eng-US/graphics1.jpg" original_filename="ccb0f9f7.jpg" mime_type="image/jpeg" width="540" height="419" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="608" attribute_version="1" attribute_language="eng-US"/><information Height="419" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '608', @@ -13830,7 +13830,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics14/612-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics14/612-1-eng-US/graphics1.jpg" original_filename="be2fb4a2.jpg" mime_type="image/jpeg" width="540" height="404" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="612" attribute_version="1" attribute_language="eng-US"/><information Height="404" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics14/612-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics14/612-1-eng-US/graphics1.jpg" original_filename="be2fb4a2.jpg" mime_type="image/jpeg" width="540" height="404" alternative_text="" alias_key="1293033771" timestamp="1311154186"><original attribute_id="612" attribute_version="1" attribute_language="eng-US"/><information Height="404" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '612', @@ -13877,7 +13877,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics15/616-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics15/616-1-eng-US/graphics1.jpg" original_filename="2db91653.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="616" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics15/616-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics15/616-1-eng-US/graphics1.jpg" original_filename="2db91653.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="616" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '616', @@ -13924,7 +13924,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics16/620-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics16/620-1-eng-US/graphics1.jpg" original_filename="b14081cb.jpg" mime_type="image/jpeg" width="540" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="620" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics16/620-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics16/620-1-eng-US/graphics1.jpg" original_filename="b14081cb.jpg" mime_type="image/jpeg" width="540" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="620" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '620', @@ -13971,7 +13971,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics17/624-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics17/624-1-eng-US/graphics1.jpg" original_filename="dd1815c4.jpg" mime_type="image/jpeg" width="540" height="425" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="624" attribute_version="1" attribute_language="eng-US"/><information Height="425" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics17/624-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics17/624-1-eng-US/graphics1.jpg" original_filename="dd1815c4.jpg" mime_type="image/jpeg" width="540" height="425" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="624" attribute_version="1" attribute_language="eng-US"/><information Height="425" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '624', @@ -14018,7 +14018,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics18/628-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics18/628-1-eng-US/graphics1.jpg" original_filename="485af257.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="628" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics18/628-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics18/628-1-eng-US/graphics1.jpg" original_filename="485af257.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="628" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '628', @@ -14065,7 +14065,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics19/632-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ez-publish/graphics19/632-1-eng-US/graphics1.jpg" original_filename="1a13bc86.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="632" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics19/632-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-use-ibexa-dxp/graphics19/632-1-eng-US/graphics1.jpg" original_filename="1a13bc86.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="632" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '632', @@ -14096,13 +14096,13 @@ 'contentobject_id' => '118', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'How to manage eZ Publish', + 'data_text' => 'How to manage Ibexa', 'data_type_string' => 'ezstring', 'id' => '634', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'how to manage ez publish', + 'sort_key_string' => 'how to manage ibexa', 'version' => '1', ], 523 => [ @@ -14127,7 +14127,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '636', @@ -14159,7 +14159,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="How-to-manage-eZ-Publish.jpg" suffix="jpg" basename="How-to-manage-eZ-Publish" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/640-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/640-1-eng-US/How-to-manage-eZ-Publish.jpg" original_filename="c6fabb5a.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="640" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="How-to-manage-Ibexa.jpg" suffix="jpg" basename="How-to-manage-Ibexa" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/640-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/640-1-eng-US/How-to-manage-Ibexa.jpg" original_filename="c6fabb5a.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154187"><original attribute_id="640" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '640', @@ -14236,7 +14236,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics1/648-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics1/648-1-eng-US/graphics1.jpg" original_filename="3268defa.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="648" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics1/648-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics1/648-1-eng-US/graphics1.jpg" original_filename="3268defa.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="648" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '648', @@ -14283,7 +14283,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics12/652-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics12/652-1-eng-US/graphics1.jpg" original_filename="06b28423.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="652" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics12/652-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics12/652-1-eng-US/graphics1.jpg" original_filename="06b28423.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="652" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '652', @@ -14330,7 +14330,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics13/656-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics13/656-1-eng-US/graphics1.jpg" original_filename="fb93c150.jpg" mime_type="image/jpeg" width="540" height="414" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="656" attribute_version="1" attribute_language="eng-US"/><information Height="414" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics13/656-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics13/656-1-eng-US/graphics1.jpg" original_filename="fb93c150.jpg" mime_type="image/jpeg" width="540" height="414" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="656" attribute_version="1" attribute_language="eng-US"/><information Height="414" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '656', @@ -14377,7 +14377,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics14/660-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics14/660-1-eng-US/graphics1.jpg" original_filename="e472d575.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="660" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics14/660-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics14/660-1-eng-US/graphics1.jpg" original_filename="e472d575.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="660" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '660', @@ -14424,7 +14424,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics15/664-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics15/664-1-eng-US/graphics1.png" original_filename="724f9667.png" mime_type="image/png" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="664" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics15/664-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics15/664-1-eng-US/graphics1.png" original_filename="724f9667.png" mime_type="image/png" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154188"><original attribute_id="664" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '664', @@ -14471,7 +14471,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics16/668-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics16/668-1-eng-US/graphics1.jpg" original_filename="13338db0.jpg" mime_type="image/jpeg" width="540" height="405" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="668" attribute_version="1" attribute_language="eng-US"/><information Height="405" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics16/668-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics16/668-1-eng-US/graphics1.jpg" original_filename="13338db0.jpg" mime_type="image/jpeg" width="540" height="405" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="668" attribute_version="1" attribute_language="eng-US"/><information Height="405" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '668', @@ -14518,7 +14518,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics17/672-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics17/672-1-eng-US/graphics1.jpg" original_filename="00bc8122.jpg" mime_type="image/jpeg" width="500" height="423" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="672" attribute_version="1" attribute_language="eng-US"/><information Height="423" Width="500" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics17/672-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics17/672-1-eng-US/graphics1.jpg" original_filename="00bc8122.jpg" mime_type="image/jpeg" width="500" height="423" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="672" attribute_version="1" attribute_language="eng-US"/><information Height="423" Width="500" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '672', @@ -14565,7 +14565,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics18/676-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ez-publish/graphics18/676-1-eng-US/graphics1.jpg" original_filename="d265bd27.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="676" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics18/676-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-manage-ibexa-dxp/graphics18/676-1-eng-US/graphics1.jpg" original_filename="d265bd27.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="676" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '676', @@ -14596,13 +14596,13 @@ 'contentobject_id' => '127', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'How to develop with eZ Publish', + 'data_text' => 'How to develop with Ibexa', 'data_type_string' => 'ezstring', 'id' => '678', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'how to develop with ez publish', + 'sort_key_string' => 'how to develop with ibexa', 'version' => '1', ], 567 => [ @@ -14627,7 +14627,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '680', @@ -14659,7 +14659,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="How-to-develop-with-eZ-Publish.png" suffix="png" basename="How-to-develop-with-eZ-Publish" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ez-publish/684-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ez-publish/684-1-eng-US/How-to-develop-with-eZ-Publish.png" original_filename="db3eaa06.png" mime_type="image/png" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="684" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="How-to-develop-with-Ibexa.png" suffix="png" basename="How-to-develop-with-Ibexa" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ibexa-dxp/684-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ibexa-dxp/684-1-eng-US/How-to-develop-with-Ibexa.png" original_filename="db3eaa06.png" mime_type="image/png" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154189"><original attribute_id="684" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '684', @@ -14736,7 +14736,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ez-publish/graphics1/692-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ez-publish/graphics1/692-1-eng-US/graphics1.jpg" original_filename="c9dbc90f.jpg" mime_type="image/jpeg" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154190"><original attribute_id="692" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="500" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.jpg" suffix="jpg" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ibexa-dxp/graphics1/692-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ibexa-dxp/graphics1/692-1-eng-US/graphics1.jpg" original_filename="c9dbc90f.jpg" mime_type="image/jpeg" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154190"><original attribute_id="692" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="500" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '692', @@ -14783,7 +14783,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ez-publish/graphics12/696-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ez-publish/graphics12/696-1-eng-US/graphics1.png" original_filename="7e906b13.png" mime_type="image/png" width="540" height="411" alternative_text="" alias_key="1293033771" timestamp="1311154190"><original attribute_id="696" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ibexa-dxp/graphics12/696-1-eng-US" url="var/ezwebin_site/storage/images/getting-started/how-to-develop-with-ibexa-dxp/graphics12/696-1-eng-US/graphics1.png" original_filename="7e906b13.png" mime_type="image/png" width="540" height="411" alternative_text="" alias_key="1293033771" timestamp="1311154190"><original attribute_id="696" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '696', @@ -14814,13 +14814,13 @@ 'contentobject_id' => '130', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'Lots of Websites, One eZ Publish Installation', + 'data_text' => 'Lots of Websites, One Ibexa Installation', 'data_type_string' => 'ezstring', 'id' => '698', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'lots of websites, one ez publish installation', + 'sort_key_string' => 'lots of websites, one ibexa installation', 'version' => '1', ], 587 => [ @@ -14829,13 +14829,13 @@ 'contentobject_id' => '130', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'Adding Siteaccesses in eZ Publish', + 'data_text' => 'Adding Siteaccesses in Ibexa', 'data_type_string' => 'ezstring', 'id' => '699', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'adding siteaccesses in ez publish', + 'sort_key_string' => 'adding siteaccesses in ibexa', 'version' => '1', ], 588 => [ @@ -15044,7 +15044,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '719', @@ -15309,7 +15309,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '743', @@ -15527,7 +15527,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '763', @@ -15839,7 +15839,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '791', @@ -15963,7 +15963,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '803', @@ -16134,7 +16134,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '819', @@ -16258,7 +16258,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '831', @@ -16429,7 +16429,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '847', @@ -16522,13 +16522,13 @@ 'contentobject_id' => '151', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'eZ Publish Enterprise', + 'data_text' => 'Ibexa Enterprise', 'data_type_string' => 'ezstring', 'id' => '857', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'ez publish enterprise', + 'sort_key_string' => 'ibexa dxp', 'version' => '1', ], 746 => [ @@ -16553,7 +16553,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '859', @@ -16662,7 +16662,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/ez-publish-enterprise/graphics1/871-1-eng-US" url="var/ezwebin_site/storage/images/ez-publish-enterprise/graphics1/871-1-eng-US/graphics1.png" original_filename="068699a4.png" mime_type="image/png" width="332" height="293" alternative_text="" alias_key="1293033771" timestamp="1311154195"><original attribute_id="871" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/ibexa-dxp-enterprise/graphics1/871-1-eng-US" url="var/ezwebin_site/storage/images/ibexa-dxp-enterprise/graphics1/871-1-eng-US/graphics1.png" original_filename="068699a4.png" mime_type="image/png" width="332" height="293" alternative_text="" alias_key="1293033771" timestamp="1311154195"><original attribute_id="871" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '871', @@ -16709,7 +16709,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/ez-publish-enterprise/graphics12/875-1-eng-US" url="var/ezwebin_site/storage/images/ez-publish-enterprise/graphics12/875-1-eng-US/graphics1.png" original_filename="d1120108.png" mime_type="image/png" width="448" height="151" alternative_text="" alias_key="1293033771" timestamp="1311154196"><original attribute_id="875" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="graphics1.png" suffix="png" basename="graphics1" dirpath="var/ezwebin_site/storage/images/ibexa-dxp-enterprise/graphics12/875-1-eng-US" url="var/ezwebin_site/storage/images/ibexa-dxp-enterprise/graphics12/875-1-eng-US/graphics1.png" original_filename="d1120108.png" mime_type="image/png" width="448" height="151" alternative_text="" alias_key="1293033771" timestamp="1311154196"><original attribute_id="875" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '875', @@ -18369,13 +18369,13 @@ 'contentobject_id' => '177', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'Ivo on eZ Tags', + 'data_text' => 'Ivo on ibexa tags', 'data_type_string' => 'ezstring', 'id' => '1033', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'ivo on ez tags', + 'sort_key_string' => 'ivo on ibexa tags', 'version' => '1', ], 923 => [ @@ -18385,7 +18385,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="Ivo-on-eZ-Tags.jpg" suffix="jpg" basename="Ivo-on-eZ-Tags" dirpath="var/ezwebin_site/storage/images/community/pictures/ivo-on-ez-tags/1035-1-eng-US" url="var/ezwebin_site/storage/images/community/pictures/ivo-on-ez-tags/1035-1-eng-US/Ivo-on-eZ-Tags.jpg" original_filename="9c0a818b.jpg" mime_type="image/jpeg" width="640" height="480" alternative_text="" alias_key="1293033771" timestamp="1311154202"><original attribute_id="1035" attribute_version="1" attribute_language="eng-US"/><information Height="480" Width="640" IsColor="1" ByteOrderMotorola="0" ApertureFNumber="f/4.9" Thumbnail.FileType="2" Thumbnail.MimeType="image/jpeg"><array name="ifd0"><item key="Make" base64="1">UGFuYXNvbmlj</item><item key="Model" base64="1">RE1DLVRaNQ==</item><item key="XResolution" base64="1">MTgwLzE=</item><item key="YResolution" base64="1">MTgwLzE=</item><item key="ResolutionUnit" base64="1">Mg==</item><item key="Software" base64="1">VmVyLjEuMCAg</item><item key="DateTime" base64="1">MjAxMTowMToyNyAxMTozNjo1MQ==</item><item key="Artist" base64="1">UGljYXNh</item><item key="YCbCrPositioning" base64="1">Mg==</item><item key="Exif_IFD_Pointer" base64="1">NTAy</item><item key="UndefinedTag:0xC4A5" base64="1">UHJpbnRJTQAwMjUwAAAOAAEAFgAWAAIAAAAAAAMAZAAAAAcAAAAAAAgAAAAAAAkAAAAAAAoAAAAAAAsArAAAAAwAAAAAAA0AAAAAAA4AxAAAAAABBQAAAAEBAQAAABABgAAAAAkRAAAQJwAACw8AABAnAACXBQAAECcAALAIAAAQJwAAARwAABAnAABeAgAAECcAAIsAAAAQJwAAywMAABAnAADlGwAAECcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==</item><item key="UndefinedTag:0xC6D2" base64="1">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==</item></array><array name="exif"><item key="ExposureTime" base64="1">MTAvMzAw</item><item key="FNumber" base64="1">NDkvMTA=</item><item key="ExposureProgram" base64="1">Mg==</item><item key="ISOSpeedRatings" base64="1">ODAw</item><item key="ExifVersion" base64="1">MDIyMQ==</item><item key="DateTimeOriginal" base64="1">MjAxMTowMToyNyAxMTozNjo1MQ==</item><item key="DateTimeDigitized" base64="1">MjAxMTowMToyNyAxMTozNjo1MQ==</item><item key="ComponentsConfiguration" base64="1">AQIDAA==</item><item key="CompressedBitsPerPixel" base64="1">NC8x</item><item key="ExposureBiasValue" base64="1">MC8xMDA=</item><item key="MaxApertureValue" base64="1">MzQ0LzEwMA==</item><item key="MeteringMode" base64="1">NQ==</item><item key="LightSource" base64="1">MA==</item><item key="Flash" base64="1">MTY=</item><item key="FocalLength" base64="1">NDcwLzEw</item><item key="MakerNote" base64="1">UGFuYXNvbmljAAAANgABAAMAAQAAAAIAAAACAAcABAAAAAABAgADAAMAAQAAAAEAAAAHAAMAAQAAAAUAAAAPAAEAAgAAAAAQAAAaAAMAAQAAAAIAAAAcAAMAAQAAAAIAAAAfAAMAAQAAAAEAAAAgAAMAAQAAAAIAAAAhAAcACCAAALwGAAAiAAMAAQAAAAAAAAAjAAMAAQAAAAAAAAAkAAMAAQAAAAAAAAAlAAcAEAAAAMQmAAAmAAcABAAAADAyNzAnAAMAAQAAAAAAAAAoAAMAAQAAAAEAAAApAAQAAQAAANAlAAAqAAMAAQAAAAAAAAArAAQAAQAAAAAAAAAsAAMAAQAAAAAAAAAtAAMAAQAAAAAAAAAuAAMAAQAAAAEAAAAvAAMAAQAAAAEAAAAwAAMAAQAAAAEAAAAxAAMAAQAAAAIAAAAyAAMAAQAAAAAAAAAzAAIAFAAAANQmAAA0AAMAAQAAAAEAAAA1AAMAAQAAAAEAAAA2AAMAAQAAAP//AAA3AAMAAQAAAAEBAAA4AAMAAQAAAAEAAAA6AAMAAQAAAAIAAAA7AAMAAQAAAAEAAAA8AAMAAQAAAP//AAA9AAMAAQAAAAEAAAA+AAMAAQAAAAEAAAA/AAMAAQAAAAAAAABNAAUAAgAAAOgmAABOAAcAKgAAAPgmAABPAAMAAQAAAAAAAABeAAcABAAAAAAAAAAAgAcABAAAADAxMjEBgAMAAQAAAAAAAAACgAMAAQAAAAIAAAADgAMAAQAAAAEAAAAEgAMAAQAAAKQGAAAFgAMAAQAAAB4EAAAGgAMAAQAAAIYHAAAHgAMAAQAAAAEAAAAIgAMAAQAAAAEAAAAJgAMAAQAAAAEAAAAQgAIAFAAAACInAAAAAAAARFYBAkVQAADw/0RCCgbw/0FGtgCgrxCAoq8AAqSvADCmrwAAyq9EALKvAQC0rwAAuq8IALyv7/++rw0A+K8AAPqvAACor1QEqq9sD8ivTUDYr3wDtq8gAbivAADOr9MA0q8hANCvKACsr5UBsK9EAK6vRADUr2UA9K8oAPKvKADirwAA5K8CAOCvXw/mrwAA6K8BAOqvAADsrwAA9q9xNNqvAADWrwAE8K8kAAQGwxkKBmwPyAYBAZqvAACcryAAnq8AAPD/U1RyAKQGAACmBgAAqAYAAKoGAACsBgAA+gcAAK4GAACwBgAAtgYAALgGAAC6BgAA9AcAAPYHAACyBgAAtAYAALAEAACyBAAAYKkAAGKpAABkqQAAaKkAAGapAABqqQAAeKkAAHqpAAB8qQAAfqkAAPD/QUUmASAFXQIiBWIC7gddAiQFcgQQBWgD3gaeAyYFCwDABgAAKAU6ARgFWQI6BQEAxRkAAFAFVwJSBYACygaoAAAFAwA2BQAAQgUAAAgFAAACBQAACgUAAAQFAABABQAABAcAAAwHAAAGBwAACAcAAAoHAADyBgAA9gb///QGAAD4Bv//DgVnAyoFTgAsBQEDMgUBA/oGWQL8BqgAAAcAAjsFAQA+BQEAxhlXAsgZkAIwBQABHAXfAOgG+AoaBWoT7wYBAPAGAAD+BoAALgUAAAEFAABgBQAANAUAAA5noAI4BQAADgczABAHAAASByMAFAcBABYHXgAXBwAAGAcAAEgFAABKBQAARAUAAEYFAAAUBTEBFgUnADwFKgFYBRIDWgXUAPD/V0LqAAAEpAYCBIYHXAQeBAQE/QAGBA0BYASeAxoEGQBeBA4AXwQGABIE1wAUBPsAFgR1ARgERgHMBGcEzgTnCdAE7wbSBDkHCAQDAQoEKgG0BNIAtgQAAUAE9QAwBF4AOAR4ADIEgAA6BIQANATg/zwEBAA2BBAAPgQpAEwE/gBOBAsBwAT9AMIEDQHqBAAAgAUOAIIFfwCDBYUAjAUAAI4FhgCPBYwAUgTXAFQE+wBWBNcAWAT7ANQE1wDWBPsA2ATXANoE+wDwBCQH8gRRBvQEGAH2BDEB+AQhB/oERwb8BBcB/gQzAfD/WUPmAE6qFABQqhQAUqoUAFSqFABEqv//RqrMiEiqdwNKqgAATKoAADiqUAA6qlAAPKpQAD6qUAAuqoiIMKrMiDKqd1U0qiIRNqoAAIIEIACABAkAhAQAAISqcABgqikzYqopM2SqYHxmqmB8aKo6SWqqAQBsqgAAbqoAAIaqAACIqgAAiqoUAIyqFACOqgAAkKoAAJKqHwCUqh8AlqoAAJiqAACgqvAAoqoQAKSq6ACmqgAAqKoKAFiqAQBaqgAAXKoAAF6qIACaqgAAnKoAAMCq0ADCqtAAxKrQAMaq0ADIqggI8P9DTQ4A/AUAMASsAADw/0RTLgAArwABAq+nhQqv5wAEr4UBBq8QAQivCQAMr0gBDq8QARCvhQESr6sA8P9JU8oAiK4AALSuawC2rmIAuK4UC7quFAuArq8Dgq7+A4Su5wOGrucDAK64AwKusAMErqwDBq6mAwiupgMKrqIDDK6mAw6upAMQrqYDEq6oAxSurAMWrq4DGK62AxquugMcrsADHq7IAyCuGAQirg4EJK4QBCauEAQorhYEKq4UBCyuFAQurgwEMK4MBDKuCgQ0rgQENq72Aziu9AM6rvgDPK74Az6uAARArgYCQq4HAkSuAABGrgAAYK4RAmKuEAJkrgAAZq4AAPD/RkSmAGCsAABirAAAgKwAAIKsAACErAAAhqwAAIisAACKrAAAjKwAAI6sAACQrAAAkqwAAJSsAACWrAAAmKwAAJqsAACcrAAAnqwAAECsAABCrAAARKwAAEasAABIrAAASqwAAEysAABOrAAAUKwAAFKsAABUrAAAVqwAAFisAABarAAAXKwAAF6sAABmBQAAbgUAAHAFAAByBQAAbAUAAGQFAADw/0lBOgCgqf//oqn//6Sp//+mqf//qKn//6qp//+sqf//rqn//4CpAACCqQAAhKkAAIipAACGqTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFFQk0OB3QHnQeZB3YHNQd8AjQBogAdAEEAHwAfACAAIAAgAOcGagfWB6wHkgerB6ECUgGnAB8ARgAiACIAIwAjACMADQiaCIYISgizBwEIlgJZAaUAIQBJACMAIwAlACUAJACjCOcI5gjYCKUIYQh0Ak4BmAAXACMAOAA5ADcANQAzAHYI3gjXCMMImAhgCGMCTAHMAHEALACXAOUA5wDqAOwAvwjTCKwH2waXB10IPgJNAcgAcAB/AOUA8gD3APIA/wDnCAkHKQbIB1wIXAgPAjoBfgBYAKYAmgBSAI8ACgE8AecIVQfMBdMFGAdbCOUBFAEgABwALgAkAAwADgCfAEEB3AjKBnAFkgSHBl4FWQF4ABUAEgAdABUADAAMAFAAbQHGCF8HFwW5A4ABswBKAA4AFAAPACIAIgALABIAFQARAf0CWwKNAV4APABHADMAPAAaAA4AGwAaAAoAEwAPANEArAMGBO4ExAKrA1UFIgHzACkADAAdADIAEgAPABkAdwASCIAIbwg2CA4Inwc4AfsAJgAMAEMAUAAbABMALwDqACkHRQfwB6sH5gdsBzcBAQEgAAwAHwAeAA0AGgCiAHgB1AXQBWQGHgZIBtQFJgHyABEACQAWABcAEAAkAGwAlgHLAOIA/QAIAQgBDwERAeAADAAGAAgACgAHABAANwCiAVBSU1RXGBsDvgJAA7oDxwOWA/EDCgUdBSYD/gzFC+sLNw1IEJ0Z3wNhA9oDDAQaBBAEkgXJBx8H/wM3C4oINwjLDnoLDRyjBDoERgRWBI8ECQX/B5AKdwoQCX8DYwKOAbMFOwcpIDkHuAdSBx0HJwc5CB8N+A9uDoAI8gEXAboAAQIqBH4fhgUuBTQFNwVcBcYG0QyXEjURAgXYAe4ApQAZAgQEwB1VBnAFlBTxLRMsvCLjIaQzvjHyBbICCwNhAq4CAgTUGgAGJQUIFLYcawOoAqYCSQKKDLcLHgPmBmMLlwEbBBgYHAWlBHwPVBRGAggCkAIrAfkA5wAyAXYC3Ag5AmkDQRbBBCAETAbUC7ID6QM8BKQB8QClAvMBtgIGB/wBpAdiFSUE6QN6Dk8NGQIrArECpAGiAukB1wCiBCYQtwPwACIUUgbWBj8JDhKtA00JwQgSCmsRbAzCB8kGNgdfAskAkg3LA4EEywQsBQkEfwOSBc4GoAWzBH4EHQOmAcsA3gAFHbQfTyTEIRIi8BtVDW0eRyGKCBYEhgLNALsAiADJAH8iA2qxdRt5nX2ceMxJzYWnO9AH+APNAYsAaAB5AI4AoyoLyvTIgtBwxlO182UbyjFOWAdiAsQAoQAhAo8AgQCYLd3Npsy10hO1pJ6FrT/I81BTB90B/QEsAisDQAGTAPoWzALMAm0D0AO+A6MDMQSWBREFxQOODHgLqguXDrQOUxhKA4ID7AMcBCQEMgQNBmgIowe4BesJ9QYrB9ANjArLG88DUgRRBG0EiwRTBasIGAuhClUIfQLhASoBKwU9BsceqwaxB3YHHgc6B4sIxw0+EHgOaQfNAQUBrQBOAgsEER6qBDUFpgU3CGoNSRLyGLkdKhZaBOwB3ACqALoCFQSdHN4EYAUFGmAzVCzAH8IfzjPTMlIIMQPbA9oDVQJGBKEZmQQEBZkVrxn7A8oCpAJrAnYOVwuDA7UHmwqBAVEE6BYvBIwEnxHXDxMC1wGCAiYB7gDbAHYBHQKjCA4C5wMmFbYD8APyB0gNAAT7AxcEfwHoAHcCuAGYAsUGBAJ3Bw4UjwPNA9MO4QoUAh4CuAJ9AZsC4wHXAOgGTxEzA/MAixISBsIGmwdnDDgCNQdqBloGrAidCZ0IlAaXBJcB1gDTDcsHEAnJCPUImgVqBeIIUAvOBdwDtgP2AXoBoAD5AHQeJSKeJi8k2CRoG60QlCAFIqAGSwQEAqAAjgCCAM8AXCuViauTNZeQmoSED19Bo145zwVYA3MBcgBYAHAAhQCHN1nMq8np0DK7O6bzaVrJxkFzBSECsADtAMsCjQBzABQ8086EzZTSU65joCOtxsejRLkF2gEzAj0C9QQCAoMAAAAAAAAAgAHgDzwMLAoACAAOBAgIAMAPwA0AAAAAAAAmAAAAAAAAAAAAwB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAABwBGQ0NWBQABAGwPXw8AAAAAAAAAAAAAAAAAAAAAAAAAAFIlCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBgDcsBoQH6APgB+QDfACQBkwHpAKoDGAT5AkwBfASWATYB/QF7A9QAjAfyAAAAAACiADMBAAAAAIAO3gG6AQUBIgI3Ad8ARQGAAfkA5AM5BBgDagGRBBQCNgEjAo0D5gDNB/cAAAAAAKYARAEAAAAAoA/gAboBHwEkAjgB2gBbAZQBBAHdA1QEMgN5AZ8EHgIuAT8CtAPwAAgI/wAAAAAArQBWAQAAAADAEMsBwAEhAf4BNQHZAGUBhwEFAdEDQwQ1A4EBmAQQAiwBRgKWA/EAAwgAAQAAAAChAFcBAAAAAOARhQGkAQ0B3gENAdgAKQFDAfEAUgMLBAgDYAF5BP4BNQEBAsIC6QCIB9YAAAAAAIQA8AAAAAAAlTjTAOYG5QHGAcABbwLyAbUBAAAMBAwCvA2jA60BBgMfAxkDogEAAOMF6wAAAAAAHAAOAAAAAAA1NyQBRAEKASEBhAI9ATgBBAFZAQYDBwFwAvgAJwIKBscB/QFsAu8C0QW/AQAAAABsAvgCAAAAABU2RgGeATMBUgGWA1ABZAGBAR4C/gNIAfYCHgGFAisHDgI+AgoDlQP7BjUCAAAAAJ8C8wIAAAAA9TRhAbsBNwFnAQ4EVAFmAZoBLAJqBHMBVQM0AacCeAcgAkwCLgO3A3wHjwIAAAAA/QILAwAAAADVM50BOwJbAdgBwwShAbkBMALCAmsFngGdA10BvgLcB0QCZwJIA/4DEQiFAgAAAADjAv0CAAAAALUyaQG3AUoBgAGkA2MBcgFwAf0BSASUAVcDUgFkAkAHAwInAr0CjQNlB00CAAAAAK4CnQIAAAAAlTEuAUgBGAE3AR8CQQEoAeoAOgECA18B1AIiAQ8CCgbAAd8BUQLYAjIGRQIAAAAApQKYAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV0JDWg8PDw8PDw8MDA4KDg4ODg4PDw8PDw8PAwoOCg4ODg4ODw8PDw8PDwMKDg4ODg4ODg8PDw8PDw8DCgoOCgoKCgoPDw8PDw8PAwoKCgoKCgoKDw8PDw8PDwMKCgoKCgoKCg8PDw8PDw8DCg4KCQ4OCgoPDw8PDw8PDAoODg4ODgoKDw8PDwUPAQEODg4ODg4KCg8PDw8PAQ8ODg4ODg4OCQoPDw8BDw8PCQoODg4ODg4KDwIBDw8PDwEKDgoKDg4OCg8PDw8PDw8DCg4KCg4ODgoPDw8PDw8PAwoODg4ODgoKDw8PDw8PDwMKDg4ODg4KCgUFBQICAgEDCg4ODg4OCgpCTUhMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEY1NTA4MTAxNTAxMTkAMDA5OTk5Ojk5Ojk5IDAwOjAwOjAwAH8AAAAAAQAAfwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5OTk5Ojk5Ojk5IDAwOjAwOjAwAA==</item><item key="FlashPixVersion" base64="1">MDEwMA==</item><item key="ColorSpace" base64="1">MQ==</item><item key="ExifImageWidth" base64="1">NjQw</item><item key="ExifImageLength" base64="1">NDgw</item><item key="InteroperabilityOffset" base64="1">MTAwMzg=</item><item key="SensingMethod" base64="1">Mg==</item><item key="FileSource" base64="1">Aw==</item><item key="SceneType" base64="1">AQ==</item><item key="CustomRendered" base64="1">MA==</item><item key="ExposureMode" base64="1">MA==</item><item key="WhiteBalance" base64="1">MA==</item><item key="DigitalZoomRatio" base64="1">MC8xMA==</item><item key="FocalLengthIn35mmFilm" base64="1">Mjgw</item><item key="SceneCaptureType" base64="1">MA==</item><item key="GainControl" base64="1">Mg==</item><item key="Contrast" base64="1">MA==</item><item key="Saturation" base64="1">MA==</item><item key="Sharpness" base64="1">MA==</item><item key="ImageUniqueID" base64="1">ZjMzYWVmMjUxZTZhNWRhYjYwMDdhYzAyOGJjZDU3NjI=</item></array></information><alias name="listitem" filename="Ivo-on-eZ-Tags_listitem.jpg" suffix="jpg" dirpath="var/ezwebin_site/storage/images/community/pictures/ivo-on-ez-tags/1035-1-eng-US" url="var/ezwebin_site/storage/images/community/pictures/ivo-on-ez-tags/1035-1-eng-US/Ivo-on-eZ-Tags_listitem.jpg" mime_type="image/jpeg" width="130" height="98" alias_key="379714049" timestamp="1311154279" is_valid="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="Ivo-on-ibexa-tags.jpg" suffix="jpg" basename="Ivo-on-ibexa-tags" dirpath="var/ezwebin_site/storage/images/community/pictures/ivo-on-ibexa-tags/1035-1-eng-US" url="var/ezwebin_site/storage/images/community/pictures/ivo-on-ibexa-tags/1035-1-eng-US/Ivo-on-ibexa-tags.jpg" original_filename="9c0a818b.jpg" mime_type="image/jpeg" width="640" height="480" alternative_text="" alias_key="1293033771" timestamp="1311154202"><original attribute_id="1035" attribute_version="1" attribute_language="eng-US"/><information Height="480" Width="640" IsColor="1" ByteOrderMotorola="0" ApertureFNumber="f/4.9" Thumbnail.FileType="2" Thumbnail.MimeType="image/jpeg"><array name="ifd0"><item key="Make" base64="1">UGFuYXNvbmlj</item><item key="Model" base64="1">RE1DLVRaNQ==</item><item key="XResolution" base64="1">MTgwLzE=</item><item key="YResolution" base64="1">MTgwLzE=</item><item key="ResolutionUnit" base64="1">Mg==</item><item key="Software" base64="1">VmVyLjEuMCAg</item><item key="DateTime" base64="1">MjAxMTowMToyNyAxMTozNjo1MQ==</item><item key="Artist" base64="1">UGljYXNh</item><item key="YCbCrPositioning" base64="1">Mg==</item><item key="Exif_IFD_Pointer" base64="1">NTAy</item><item key="UndefinedTag:0xC4A5" base64="1">UHJpbnRJTQAwMjUwAAAOAAEAFgAWAAIAAAAAAAMAZAAAAAcAAAAAAAgAAAAAAAkAAAAAAAoAAAAAAAsArAAAAAwAAAAAAA0AAAAAAA4AxAAAAAABBQAAAAEBAQAAABABgAAAAAkRAAAQJwAACw8AABAnAACXBQAAECcAALAIAAAQJwAAARwAABAnAABeAgAAECcAAIsAAAAQJwAAywMAABAnAADlGwAAECcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==</item><item key="UndefinedTag:0xC6D2" base64="1">AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==</item></array><array name="exif"><item key="ExposureTime" base64="1">MTAvMzAw</item><item key="FNumber" base64="1">NDkvMTA=</item><item key="ExposureProgram" base64="1">Mg==</item><item key="ISOSpeedRatings" base64="1">ODAw</item><item key="ExifVersion" base64="1">MDIyMQ==</item><item key="DateTimeOriginal" base64="1">MjAxMTowMToyNyAxMTozNjo1MQ==</item><item key="DateTimeDigitized" base64="1">MjAxMTowMToyNyAxMTozNjo1MQ==</item><item key="ComponentsConfiguration" base64="1">AQIDAA==</item><item key="CompressedBitsPerPixel" base64="1">NC8x</item><item key="ExposureBiasValue" base64="1">MC8xMDA=</item><item key="MaxApertureValue" base64="1">MzQ0LzEwMA==</item><item key="MeteringMode" base64="1">NQ==</item><item key="LightSource" base64="1">MA==</item><item key="Flash" base64="1">MTY=</item><item key="FocalLength" base64="1">NDcwLzEw</item><item key="MakerNote" base64="1">UGFuYXNvbmljAAAANgABAAMAAQAAAAIAAAACAAcABAAAAAABAgADAAMAAQAAAAEAAAAHAAMAAQAAAAUAAAAPAAEAAgAAAAAQAAAaAAMAAQAAAAIAAAAcAAMAAQAAAAIAAAAfAAMAAQAAAAEAAAAgAAMAAQAAAAIAAAAhAAcACCAAALwGAAAiAAMAAQAAAAAAAAAjAAMAAQAAAAAAAAAkAAMAAQAAAAAAAAAlAAcAEAAAAMQmAAAmAAcABAAAADAyNzAnAAMAAQAAAAAAAAAoAAMAAQAAAAEAAAApAAQAAQAAANAlAAAqAAMAAQAAAAAAAAArAAQAAQAAAAAAAAAsAAMAAQAAAAAAAAAtAAMAAQAAAAAAAAAuAAMAAQAAAAEAAAAvAAMAAQAAAAEAAAAwAAMAAQAAAAEAAAAxAAMAAQAAAAIAAAAyAAMAAQAAAAAAAAAzAAIAFAAAANQmAAA0AAMAAQAAAAEAAAA1AAMAAQAAAAEAAAA2AAMAAQAAAP//AAA3AAMAAQAAAAEBAAA4AAMAAQAAAAEAAAA6AAMAAQAAAAIAAAA7AAMAAQAAAAEAAAA8AAMAAQAAAP//AAA9AAMAAQAAAAEAAAA+AAMAAQAAAAEAAAA/AAMAAQAAAAAAAABNAAUAAgAAAOgmAABOAAcAKgAAAPgmAABPAAMAAQAAAAAAAABeAAcABAAAAAAAAAAAgAcABAAAADAxMjEBgAMAAQAAAAAAAAACgAMAAQAAAAIAAAADgAMAAQAAAAEAAAAEgAMAAQAAAKQGAAAFgAMAAQAAAB4EAAAGgAMAAQAAAIYHAAAHgAMAAQAAAAEAAAAIgAMAAQAAAAEAAAAJgAMAAQAAAAEAAAAQgAIAFAAAACInAAAAAAAARFYBAkVQAADw/0RCCgbw/0FGtgCgrxCAoq8AAqSvADCmrwAAyq9EALKvAQC0rwAAuq8IALyv7/++rw0A+K8AAPqvAACor1QEqq9sD8ivTUDYr3wDtq8gAbivAADOr9MA0q8hANCvKACsr5UBsK9EAK6vRADUr2UA9K8oAPKvKADirwAA5K8CAOCvXw/mrwAA6K8BAOqvAADsrwAA9q9xNNqvAADWrwAE8K8kAAQGwxkKBmwPyAYBAZqvAACcryAAnq8AAPD/U1RyAKQGAACmBgAAqAYAAKoGAACsBgAA+gcAAK4GAACwBgAAtgYAALgGAAC6BgAA9AcAAPYHAACyBgAAtAYAALAEAACyBAAAYKkAAGKpAABkqQAAaKkAAGapAABqqQAAeKkAAHqpAAB8qQAAfqkAAPD/QUUmASAFXQIiBWIC7gddAiQFcgQQBWgD3gaeAyYFCwDABgAAKAU6ARgFWQI6BQEAxRkAAFAFVwJSBYACygaoAAAFAwA2BQAAQgUAAAgFAAACBQAACgUAAAQFAABABQAABAcAAAwHAAAGBwAACAcAAAoHAADyBgAA9gb///QGAAD4Bv//DgVnAyoFTgAsBQEDMgUBA/oGWQL8BqgAAAcAAjsFAQA+BQEAxhlXAsgZkAIwBQABHAXfAOgG+AoaBWoT7wYBAPAGAAD+BoAALgUAAAEFAABgBQAANAUAAA5noAI4BQAADgczABAHAAASByMAFAcBABYHXgAXBwAAGAcAAEgFAABKBQAARAUAAEYFAAAUBTEBFgUnADwFKgFYBRIDWgXUAPD/V0LqAAAEpAYCBIYHXAQeBAQE/QAGBA0BYASeAxoEGQBeBA4AXwQGABIE1wAUBPsAFgR1ARgERgHMBGcEzgTnCdAE7wbSBDkHCAQDAQoEKgG0BNIAtgQAAUAE9QAwBF4AOAR4ADIEgAA6BIQANATg/zwEBAA2BBAAPgQpAEwE/gBOBAsBwAT9AMIEDQHqBAAAgAUOAIIFfwCDBYUAjAUAAI4FhgCPBYwAUgTXAFQE+wBWBNcAWAT7ANQE1wDWBPsA2ATXANoE+wDwBCQH8gRRBvQEGAH2BDEB+AQhB/oERwb8BBcB/gQzAfD/WUPmAE6qFABQqhQAUqoUAFSqFABEqv//RqrMiEiqdwNKqgAATKoAADiqUAA6qlAAPKpQAD6qUAAuqoiIMKrMiDKqd1U0qiIRNqoAAIIEIACABAkAhAQAAISqcABgqikzYqopM2SqYHxmqmB8aKo6SWqqAQBsqgAAbqoAAIaqAACIqgAAiqoUAIyqFACOqgAAkKoAAJKqHwCUqh8AlqoAAJiqAACgqvAAoqoQAKSq6ACmqgAAqKoKAFiqAQBaqgAAXKoAAF6qIACaqgAAnKoAAMCq0ADCqtAAxKrQAMaq0ADIqggI8P9DTQ4A/AUAMASsAADw/0RTLgAArwABAq+nhQqv5wAEr4UBBq8QAQivCQAMr0gBDq8QARCvhQESr6sA8P9JU8oAiK4AALSuawC2rmIAuK4UC7quFAuArq8Dgq7+A4Su5wOGrucDAK64AwKusAMErqwDBq6mAwiupgMKrqIDDK6mAw6upAMQrqYDEq6oAxSurAMWrq4DGK62AxquugMcrsADHq7IAyCuGAQirg4EJK4QBCauEAQorhYEKq4UBCyuFAQurgwEMK4MBDKuCgQ0rgQENq72Aziu9AM6rvgDPK74Az6uAARArgYCQq4HAkSuAABGrgAAYK4RAmKuEAJkrgAAZq4AAPD/RkSmAGCsAABirAAAgKwAAIKsAACErAAAhqwAAIisAACKrAAAjKwAAI6sAACQrAAAkqwAAJSsAACWrAAAmKwAAJqsAACcrAAAnqwAAECsAABCrAAARKwAAEasAABIrAAASqwAAEysAABOrAAAUKwAAFKsAABUrAAAVqwAAFisAABarAAAXKwAAF6sAABmBQAAbgUAAHAFAAByBQAAbAUAAGQFAADw/0lBOgCgqf//oqn//6Sp//+mqf//qKn//6qp//+sqf//rqn//4CpAACCqQAAhKkAAIipAACGqTgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEFFQk0OB3QHnQeZB3YHNQd8AjQBogAdAEEAHwAfACAAIAAgAOcGagfWB6wHkgerB6ECUgGnAB8ARgAiACIAIwAjACMADQiaCIYISgizBwEIlgJZAaUAIQBJACMAIwAlACUAJACjCOcI5gjYCKUIYQh0Ak4BmAAXACMAOAA5ADcANQAzAHYI3gjXCMMImAhgCGMCTAHMAHEALACXAOUA5wDqAOwAvwjTCKwH2waXB10IPgJNAcgAcAB/AOUA8gD3APIA/wDnCAkHKQbIB1wIXAgPAjoBfgBYAKYAmgBSAI8ACgE8AecIVQfMBdMFGAdbCOUBFAEgABwALgAkAAwADgCfAEEB3AjKBnAFkgSHBl4FWQF4ABUAEgAdABUADAAMAFAAbQHGCF8HFwW5A4ABswBKAA4AFAAPACIAIgALABIAFQARAf0CWwKNAV4APABHADMAPAAaAA4AGwAaAAoAEwAPANEArAMGBO4ExAKrA1UFIgHzACkADAAdADIAEgAPABkAdwASCIAIbwg2CA4Inwc4AfsAJgAMAEMAUAAbABMALwDqACkHRQfwB6sH5gdsBzcBAQEgAAwAHwAeAA0AGgCiAHgB1AXQBWQGHgZIBtQFJgHyABEACQAWABcAEAAkAGwAlgHLAOIA/QAIAQgBDwERAeAADAAGAAgACgAHABAANwCiAVBSU1RXGBsDvgJAA7oDxwOWA/EDCgUdBSYD/gzFC+sLNw1IEJ0Z3wNhA9oDDAQaBBAEkgXJBx8H/wM3C4oINwjLDnoLDRyjBDoERgRWBI8ECQX/B5AKdwoQCX8DYwKOAbMFOwcpIDkHuAdSBx0HJwc5CB8N+A9uDoAI8gEXAboAAQIqBH4fhgUuBTQFNwVcBcYG0QyXEjURAgXYAe4ApQAZAgQEwB1VBnAFlBTxLRMsvCLjIaQzvjHyBbICCwNhAq4CAgTUGgAGJQUIFLYcawOoAqYCSQKKDLcLHgPmBmMLlwEbBBgYHAWlBHwPVBRGAggCkAIrAfkA5wAyAXYC3Ag5AmkDQRbBBCAETAbUC7ID6QM8BKQB8QClAvMBtgIGB/wBpAdiFSUE6QN6Dk8NGQIrArECpAGiAukB1wCiBCYQtwPwACIUUgbWBj8JDhKtA00JwQgSCmsRbAzCB8kGNgdfAskAkg3LA4EEywQsBQkEfwOSBc4GoAWzBH4EHQOmAcsA3gAFHbQfTyTEIRIi8BtVDW0eRyGKCBYEhgLNALsAiADJAH8iA2qxdRt5nX2ceMxJzYWnO9AH+APNAYsAaAB5AI4AoyoLyvTIgtBwxlO182UbyjFOWAdiAsQAoQAhAo8AgQCYLd3Npsy10hO1pJ6FrT/I81BTB90B/QEsAisDQAGTAPoWzALMAm0D0AO+A6MDMQSWBREFxQOODHgLqguXDrQOUxhKA4ID7AMcBCQEMgQNBmgIowe4BesJ9QYrB9ANjArLG88DUgRRBG0EiwRTBasIGAuhClUIfQLhASoBKwU9BsceqwaxB3YHHgc6B4sIxw0+EHgOaQfNAQUBrQBOAgsEER6qBDUFpgU3CGoNSRLyGLkdKhZaBOwB3ACqALoCFQSdHN4EYAUFGmAzVCzAH8IfzjPTMlIIMQPbA9oDVQJGBKEZmQQEBZkVrxn7A8oCpAJrAnYOVwuDA7UHmwqBAVEE6BYvBIwEnxHXDxMC1wGCAiYB7gDbAHYBHQKjCA4C5wMmFbYD8APyB0gNAAT7AxcEfwHoAHcCuAGYAsUGBAJ3Bw4UjwPNA9MO4QoUAh4CuAJ9AZsC4wHXAOgGTxEzA/MAixISBsIGmwdnDDgCNQdqBloGrAidCZ0IlAaXBJcB1gDTDcsHEAnJCPUImgVqBeIIUAvOBdwDtgP2AXoBoAD5AHQeJSKeJi8k2CRoG60QlCAFIqAGSwQEAqAAjgCCAM8AXCuViauTNZeQmoSED19Bo145zwVYA3MBcgBYAHAAhQCHN1nMq8np0DK7O6bzaVrJxkFzBSECsADtAMsCjQBzABQ8086EzZTSU65joCOtxsejRLkF2gEzAj0C9QQCAoMAAAAAAAAAgAHgDzwMLAoACAAOBAgIAMAPwA0AAAAAAAAmAAAAAAAAAAAAwB8AAAAAAAAAAAAAAAAAAAAAAAAAAAAABwBGQ0NWBQABAGwPXw8AAAAAAAAAAAAAAAAAAAAAAAAAAFIlCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQBgDcsBoQH6APgB+QDfACQBkwHpAKoDGAT5AkwBfASWATYB/QF7A9QAjAfyAAAAAACiADMBAAAAAIAO3gG6AQUBIgI3Ad8ARQGAAfkA5AM5BBgDagGRBBQCNgEjAo0D5gDNB/cAAAAAAKYARAEAAAAAoA/gAboBHwEkAjgB2gBbAZQBBAHdA1QEMgN5AZ8EHgIuAT8CtAPwAAgI/wAAAAAArQBWAQAAAADAEMsBwAEhAf4BNQHZAGUBhwEFAdEDQwQ1A4EBmAQQAiwBRgKWA/EAAwgAAQAAAAChAFcBAAAAAOARhQGkAQ0B3gENAdgAKQFDAfEAUgMLBAgDYAF5BP4BNQEBAsIC6QCIB9YAAAAAAIQA8AAAAAAAlTjTAOYG5QHGAcABbwLyAbUBAAAMBAwCvA2jA60BBgMfAxkDogEAAOMF6wAAAAAAHAAOAAAAAAA1NyQBRAEKASEBhAI9ATgBBAFZAQYDBwFwAvgAJwIKBscB/QFsAu8C0QW/AQAAAABsAvgCAAAAABU2RgGeATMBUgGWA1ABZAGBAR4C/gNIAfYCHgGFAisHDgI+AgoDlQP7BjUCAAAAAJ8C8wIAAAAA9TRhAbsBNwFnAQ4EVAFmAZoBLAJqBHMBVQM0AacCeAcgAkwCLgO3A3wHjwIAAAAA/QILAwAAAADVM50BOwJbAdgBwwShAbkBMALCAmsFngGdA10BvgLcB0QCZwJIA/4DEQiFAgAAAADjAv0CAAAAALUyaQG3AUoBgAGkA2MBcgFwAf0BSASUAVcDUgFkAkAHAwInAr0CjQNlB00CAAAAAK4CnQIAAAAAlTEuAUgBGAE3AR8CQQEoAeoAOgECA18B1AIiAQ8CCgbAAd8BUQLYAjIGRQIAAAAApQKYAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAV0JDWg8PDw8PDw8MDA4KDg4ODg4PDw8PDw8PAwoOCg4ODg4ODw8PDw8PDwMKDg4ODg4ODg8PDw8PDw8DCgoOCgoKCgoPDw8PDw8PAwoKCgoKCgoKDw8PDw8PDwMKCgoKCgoKCg8PDw8PDw8DCg4KCQ4OCgoPDw8PDw8PDAoODg4ODgoKDw8PDwUPAQEODg4ODg4KCg8PDw8PAQ8ODg4ODg4OCQoPDw8BDw8PCQoODg4ODg4KDwIBDw8PDwEKDgoKDg4OCg8PDw8PDw8DCg4KCg4ODgoPDw8PDw8PAwoODg4ODgoKDw8PDw8PDwMKDg4ODg4KCgUFBQICAgEDCg4ODg4OCgpCTUhMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEY1NTA4MTAxNTAxMTkAMDA5OTk5Ojk5Ojk5IDAwOjAwOjAwAH8AAAAAAQAAfwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA5OTk5Ojk5Ojk5IDAwOjAwOjAwAA==</item><item key="FlashPixVersion" base64="1">MDEwMA==</item><item key="ColorSpace" base64="1">MQ==</item><item key="ExifImageWidth" base64="1">NjQw</item><item key="ExifImageLength" base64="1">NDgw</item><item key="InteroperabilityOffset" base64="1">MTAwMzg=</item><item key="SensingMethod" base64="1">Mg==</item><item key="FileSource" base64="1">Aw==</item><item key="SceneType" base64="1">AQ==</item><item key="CustomRendered" base64="1">MA==</item><item key="ExposureMode" base64="1">MA==</item><item key="WhiteBalance" base64="1">MA==</item><item key="DigitalZoomRatio" base64="1">MC8xMA==</item><item key="FocalLengthIn35mmFilm" base64="1">Mjgw</item><item key="SceneCaptureType" base64="1">MA==</item><item key="GainControl" base64="1">Mg==</item><item key="Contrast" base64="1">MA==</item><item key="Saturation" base64="1">MA==</item><item key="Sharpness" base64="1">MA==</item><item key="ImageUniqueID" base64="1">ZjMzYWVmMjUxZTZhNWRhYjYwMDdhYzAyOGJjZDU3NjI=</item></array></information><alias name="listitem" filename="Ivo-on-ibexa-tags_listitem.jpg" suffix="jpg" dirpath="var/ezwebin_site/storage/images/community/pictures/ivo-on-ibexa-tags/1035-1-eng-US" url="var/ezwebin_site/storage/images/community/pictures/ivo-on-ibexa-tags/1035-1-eng-US/Ivo-on-ibexa-tags_listitem.jpg" mime_type="image/jpeg" width="130" height="98" alias_key="379714049" timestamp="1311154279" is_valid="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1035', @@ -19150,13 +19150,13 @@ 'contentobject_id' => '191', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'Separate content & design in eZ Publish', + 'data_text' => 'Separate content & design in Ibexa', 'data_type_string' => 'ezstring', 'id' => '1101', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'separate content & design in ez publish', + 'sort_key_string' => 'separate content & design in ibexa', 'version' => '1', ], 990 => [ @@ -19213,7 +19213,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="Separate-content-design-in-eZ-Publish.jpg" suffix="jpg" basename="Separate-content-design-in-eZ-Publish" dirpath="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ez-publish/1107-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ez-publish/1107-1-eng-US/Separate-content-design-in-eZ-Publish.jpg" original_filename="844e123d.jpg" mime_type="image/jpeg" width="530" height="420" alternative_text="" alias_key="1293033771" timestamp="1311154205"><original attribute_id="1107" attribute_version="1" attribute_language="eng-US"/><information Height="420" Width="530" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="Separate-content-design-in-Ibexa.jpg" suffix="jpg" basename="Separate-content-design-in-Ibexa" dirpath="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ibexa-dxp/1107-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ibexa-dxp/1107-1-eng-US/Separate-content-design-in-Ibexa.jpg" original_filename="844e123d.jpg" mime_type="image/jpeg" width="530" height="420" alternative_text="" alias_key="1293033771" timestamp="1311154205"><original attribute_id="1107" attribute_version="1" attribute_language="eng-US"/><information Height="420" Width="530" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1107', @@ -19290,7 +19290,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="separation_of_content_and_design.jpg" suffix="jpg" basename="separation_of_content_and_design" dirpath="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ez-publish/separation_of_content_and_design/1115-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ez-publish/separation_of_content_and_design/1115-1-eng-US/separation_of_content_and_design.jpg" original_filename="1cb1af18.jpg" mime_type="image/jpeg" width="500" height="195" alternative_text="" alias_key="1293033771" timestamp="1311154205"><original attribute_id="1115" attribute_version="1" attribute_language="eng-US"/><information Height="195" Width="500" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="separation_of_content_and_design.jpg" suffix="jpg" basename="separation_of_content_and_design" dirpath="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ibexa-dxp/separation_of_content_and_design/1115-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/separate-content-design-in-ibexa-dxp/separation_of_content_and_design/1115-1-eng-US/separation_of_content_and_design.jpg" original_filename="1cb1af18.jpg" mime_type="image/jpeg" width="500" height="195" alternative_text="" alias_key="1293033771" timestamp="1311154205"><original attribute_id="1115" attribute_version="1" attribute_language="eng-US"/><information Height="195" Width="500" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1115', @@ -19321,13 +19321,13 @@ 'contentobject_id' => '193', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'How to use eZ Publish', + 'data_text' => 'How to use Ibexa', 'data_type_string' => 'ezstring', 'id' => '1117', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'how to use ez publish', + 'sort_key_string' => 'how to use ibexa', 'version' => '1', ], 1006 => [ @@ -19384,7 +19384,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="How-to-use-eZ-Publish.jpg" suffix="jpg" basename="How-to-use-eZ-Publish" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/1123-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/1123-1-eng-US/How-to-use-eZ-Publish.jpg" original_filename="0aab879e.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154205"><original attribute_id="1123" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="How-to-use-Ibexa.jpg" suffix="jpg" basename="How-to-use-Ibexa" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/1123-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/1123-1-eng-US/How-to-use-Ibexa.jpg" original_filename="0aab879e.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154205"><original attribute_id="1123" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1123', @@ -19461,7 +19461,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="openoffice_import_large.jpg" suffix="jpg" basename="openoffice_import_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/openoffice_import_large/1131-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/openoffice_import_large/1131-1-eng-US/openoffice_import_large.jpg" original_filename="36b1357b.jpg" mime_type="image/jpeg" width="540" height="357" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1131" attribute_version="1" attribute_language="eng-US"/><information Height="357" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="openoffice_import_large.jpg" suffix="jpg" basename="openoffice_import_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/openoffice_import_large/1131-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/openoffice_import_large/1131-1-eng-US/openoffice_import_large.jpg" original_filename="36b1357b.jpg" mime_type="image/jpeg" width="540" height="357" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1131" attribute_version="1" attribute_language="eng-US"/><information Height="357" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1131', @@ -19508,7 +19508,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="translation_large.jpg" suffix="jpg" basename="translation_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/translation_large/1135-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/translation_large/1135-1-eng-US/translation_large.jpg" original_filename="503004df.jpg" mime_type="image/jpeg" width="540" height="419" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1135" attribute_version="1" attribute_language="eng-US"/><information Height="419" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="translation_large.jpg" suffix="jpg" basename="translation_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/translation_large/1135-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/translation_large/1135-1-eng-US/translation_large.jpg" original_filename="503004df.jpg" mime_type="image/jpeg" width="540" height="419" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1135" attribute_version="1" attribute_language="eng-US"/><information Height="419" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1135', @@ -19555,7 +19555,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="multiupload_large.jpg" suffix="jpg" basename="multiupload_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/multiupload_large/1139-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/multiupload_large/1139-1-eng-US/multiupload_large.jpg" original_filename="9652f5a1.jpg" mime_type="image/jpeg" width="540" height="404" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1139" attribute_version="1" attribute_language="eng-US"/><information Height="404" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="multiupload_large.jpg" suffix="jpg" basename="multiupload_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/multiupload_large/1139-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/multiupload_large/1139-1-eng-US/multiupload_large.jpg" original_filename="9652f5a1.jpg" mime_type="image/jpeg" width="540" height="404" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1139" attribute_version="1" attribute_language="eng-US"/><information Height="404" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1139', @@ -19602,7 +19602,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="gallery_large.jpg" suffix="jpg" basename="gallery_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/gallery_large/1143-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/gallery_large/1143-1-eng-US/gallery_large.jpg" original_filename="b1b5ed39.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1143" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="gallery_large.jpg" suffix="jpg" basename="gallery_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/gallery_large/1143-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/gallery_large/1143-1-eng-US/gallery_large.jpg" original_filename="b1b5ed39.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154206"><original attribute_id="1143" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1143', @@ -19649,7 +19649,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="search_result_large.jpg" suffix="jpg" basename="search_result_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/search_result_large/1147-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/search_result_large/1147-1-eng-US/search_result_large.jpg" original_filename="fb6cf7d3.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1147" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="search_result_large.jpg" suffix="jpg" basename="search_result_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/search_result_large/1147-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/search_result_large/1147-1-eng-US/search_result_large.jpg" original_filename="fb6cf7d3.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1147" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1147', @@ -19696,7 +19696,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="ez_tool_bar_large.jpg" suffix="jpg" basename="ez_tool_bar_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/ez_tool_bar_large/1151-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/ez_tool_bar_large/1151-1-eng-US/ez_tool_bar_large.jpg" original_filename="9c1fd673.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1151" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="ez_tool_bar_large.jpg" suffix="jpg" basename="ez_tool_bar_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/ez_tool_bar_large/1151-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/ez_tool_bar_large/1151-1-eng-US/ez_tool_bar_large.jpg" original_filename="9c1fd673.jpg" mime_type="image/jpeg" width="540" height="351" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1151" attribute_version="1" attribute_language="eng-US"/><information Height="351" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1151', @@ -19743,7 +19743,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="quicktime_large.jpg" suffix="jpg" basename="quicktime_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/quicktime_large/1155-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/quicktime_large/1155-1-eng-US/quicktime_large.jpg" original_filename="af4e1807.jpg" mime_type="image/jpeg" width="540" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1155" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="quicktime_large.jpg" suffix="jpg" basename="quicktime_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/quicktime_large/1155-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/quicktime_large/1155-1-eng-US/quicktime_large.jpg" original_filename="af4e1807.jpg" mime_type="image/jpeg" width="540" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1155" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1155', @@ -19790,7 +19790,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="web_2_0_large.jpg" suffix="jpg" basename="web_2_0_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/web_2_0_large/1159-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ez-publish/web_2_0_large/1159-1-eng-US/web_2_0_large.jpg" original_filename="cd03d9fc.jpg" mime_type="image/jpeg" width="540" height="425" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1159" attribute_version="1" attribute_language="eng-US"/><information Height="425" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="web_2_0_large.jpg" suffix="jpg" basename="web_2_0_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/web_2_0_large/1159-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-use-ibexa-dxp/web_2_0_large/1159-1-eng-US/web_2_0_large.jpg" original_filename="cd03d9fc.jpg" mime_type="image/jpeg" width="540" height="425" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1159" attribute_version="1" attribute_language="eng-US"/><information Height="425" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1159', @@ -19821,13 +19821,13 @@ 'contentobject_id' => '202', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'How to manage eZ Publish', + 'data_text' => 'How to manage Ibexa', 'data_type_string' => 'ezstring', 'id' => '1161', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'how to manage ez publish', + 'sort_key_string' => 'how to manage ibexa', 'version' => '1', ], 1050 => [ @@ -19884,7 +19884,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="How-to-manage-eZ-Publish.jpg" suffix="jpg" basename="How-to-manage-eZ-Publish" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/1167-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/1167-1-eng-US/How-to-manage-eZ-Publish.jpg" original_filename="4da87751.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1167" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="How-to-manage-Ibexa.jpg" suffix="jpg" basename="How-to-manage-Ibexa" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/1167-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/1167-1-eng-US/How-to-manage-Ibexa.jpg" original_filename="4da87751.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154207"><original attribute_id="1167" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1167', @@ -19961,7 +19961,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="usermanagement_large.jpg" suffix="jpg" basename="usermanagement_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/usermanagement_large/1175-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/usermanagement_large/1175-1-eng-US/usermanagement_large.jpg" original_filename="c7fdf3e4.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154208"><original attribute_id="1175" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="usermanagement_large.jpg" suffix="jpg" basename="usermanagement_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/usermanagement_large/1175-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/usermanagement_large/1175-1-eng-US/usermanagement_large.jpg" original_filename="c7fdf3e4.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154208"><original attribute_id="1175" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1175', @@ -20008,7 +20008,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="workflow_large.jpg" suffix="jpg" basename="workflow_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/workflow_large/1179-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/workflow_large/1179-1-eng-US/workflow_large.jpg" original_filename="d4a5a15d.jpg" mime_type="image/jpeg" width="540" height="414" alternative_text="" alias_key="1293033771" timestamp="1311154208"><original attribute_id="1179" attribute_version="1" attribute_language="eng-US"/><information Height="414" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="workflow_large.jpg" suffix="jpg" basename="workflow_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/workflow_large/1179-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/workflow_large/1179-1-eng-US/workflow_large.jpg" original_filename="d4a5a15d.jpg" mime_type="image/jpeg" width="540" height="414" alternative_text="" alias_key="1293033771" timestamp="1311154208"><original attribute_id="1179" attribute_version="1" attribute_language="eng-US"/><information Height="414" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1179', @@ -20055,7 +20055,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="versioning_large.jpg" suffix="jpg" basename="versioning_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/versioning_large/1183-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/versioning_large/1183-1-eng-US/versioning_large.jpg" original_filename="5041e7aa.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154208"><original attribute_id="1183" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="versioning_large.jpg" suffix="jpg" basename="versioning_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/versioning_large/1183-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/versioning_large/1183-1-eng-US/versioning_large.jpg" original_filename="5041e7aa.jpg" mime_type="image/jpeg" width="540" height="346" alternative_text="" alias_key="1293033771" timestamp="1311154208"><original attribute_id="1183" attribute_version="1" attribute_language="eng-US"/><information Height="346" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1183', @@ -20102,7 +20102,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="datatypes_attributes_content_class_and_objects_large.png" suffix="png" basename="datatypes_attributes_content_class_and_objects_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/datatypes_attributes_content_class_and_objects_large/1187-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/datatypes_attributes_content_class_and_objects_large/1187-1-eng-US/datatypes_attributes_content_class_and_objects_large.png" original_filename="3ac3d302.png" mime_type="image/png" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1187" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="datatypes_attributes_content_class_and_objects_large.png" suffix="png" basename="datatypes_attributes_content_class_and_objects_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/datatypes_attributes_content_class_and_objects_large/1187-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/datatypes_attributes_content_class_and_objects_large/1187-1-eng-US/datatypes_attributes_content_class_and_objects_large.png" original_filename="3ac3d302.png" mime_type="image/png" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1187" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1187', @@ -20149,7 +20149,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="multiple_location_large.jpg" suffix="jpg" basename="multiple_location_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/multiple_location_large/1191-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/multiple_location_large/1191-1-eng-US/multiple_location_large.jpg" original_filename="d2bd9a18.jpg" mime_type="image/jpeg" width="540" height="405" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1191" attribute_version="1" attribute_language="eng-US"/><information Height="405" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="multiple_location_large.jpg" suffix="jpg" basename="multiple_location_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/multiple_location_large/1191-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/multiple_location_large/1191-1-eng-US/multiple_location_large.jpg" original_filename="d2bd9a18.jpg" mime_type="image/jpeg" width="540" height="405" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1191" attribute_version="1" attribute_language="eng-US"/><information Height="405" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1191', @@ -20196,7 +20196,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="multisite_deployment_large.jpg" suffix="jpg" basename="multisite_deployment_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/multisite_deployment_large/1195-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/multisite_deployment_large/1195-1-eng-US/multisite_deployment_large.jpg" original_filename="fa5f0924.jpg" mime_type="image/jpeg" width="500" height="423" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1195" attribute_version="1" attribute_language="eng-US"/><information Height="423" Width="500" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="multisite_deployment_large.jpg" suffix="jpg" basename="multisite_deployment_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/multisite_deployment_large/1195-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/multisite_deployment_large/1195-1-eng-US/multisite_deployment_large.jpg" original_filename="fa5f0924.jpg" mime_type="image/jpeg" width="500" height="423" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1195" attribute_version="1" attribute_language="eng-US"/><information Height="423" Width="500" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1195', @@ -20243,7 +20243,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="seo_large.jpg" suffix="jpg" basename="seo_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/seo_large/1199-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/seo_large/1199-1-eng-US/seo_large.jpg" original_filename="25149bcc.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1199" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="seo_large.jpg" suffix="jpg" basename="seo_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/seo_large/1199-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/seo_large/1199-1-eng-US/seo_large.jpg" original_filename="25149bcc.jpg" mime_type="image/jpeg" width="540" height="347" alternative_text="" alias_key="1293033771" timestamp="1311154209"><original attribute_id="1199" attribute_version="1" attribute_language="eng-US"/><information Height="347" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1199', @@ -20290,7 +20290,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="admin_site_large.jpg" suffix="jpg" basename="admin_site_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/admin_site_large/1203-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ez-publish/admin_site_large/1203-1-eng-US/admin_site_large.jpg" original_filename="57b1a75c.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154210"><original attribute_id="1203" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="admin_site_large.jpg" suffix="jpg" basename="admin_site_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/admin_site_large/1203-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-manage-ibexa-dxp/admin_site_large/1203-1-eng-US/admin_site_large.jpg" original_filename="57b1a75c.jpg" mime_type="image/jpeg" width="540" height="332" alternative_text="" alias_key="1293033771" timestamp="1311154210"><original attribute_id="1203" attribute_version="1" attribute_language="eng-US"/><information Height="332" Width="540" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1203', @@ -20321,13 +20321,13 @@ 'contentobject_id' => '211', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'How to develop with eZ Publish', + 'data_text' => 'How to develop with Ibexa', 'data_type_string' => 'ezstring', 'id' => '1205', 'language_code' => 'eng-US', 'language_id' => '2', 'sort_key_int' => '0', - 'sort_key_string' => 'how to develop with ez publish', + 'sort_key_string' => 'how to develop with ibexa', 'version' => '1', ], 1094 => [ @@ -20384,7 +20384,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="How-to-develop-with-eZ-Publish.png" suffix="png" basename="How-to-develop-with-eZ-Publish" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ez-publish/1211-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ez-publish/1211-1-eng-US/How-to-develop-with-eZ-Publish.png" original_filename="33e36be8.png" mime_type="image/png" width="500" height="409" alternative_text="Content class" alias_key="1293033771" timestamp="1311154210"><original attribute_id="1211" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="How-to-develop-with-Ibexa.png" suffix="png" basename="How-to-develop-with-Ibexa" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ibexa-dxp/1211-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ibexa-dxp/1211-1-eng-US/How-to-develop-with-Ibexa.png" original_filename="33e36be8.png" mime_type="image/png" width="500" height="409" alternative_text="Content class" alias_key="1293033771" timestamp="1311154210"><original attribute_id="1211" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1211', @@ -20461,7 +20461,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="datatypes_attributes_content_class_and_objects_large.jpg" suffix="jpg" basename="datatypes_attributes_content_class_and_objects_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ez-publish/datatypes_attributes_content_class_and_objects_large/1219-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ez-publish/datatypes_attributes_content_class_and_objects_large/1219-1-eng-US/datatypes_attributes_content_class_and_objects_large.jpg" original_filename="e252a562.jpg" mime_type="image/jpeg" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154210"><original attribute_id="1219" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="500" IsColor="1"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="datatypes_attributes_content_class_and_objects_large.jpg" suffix="jpg" basename="datatypes_attributes_content_class_and_objects_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ibexa-dxp/datatypes_attributes_content_class_and_objects_large/1219-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ibexa-dxp/datatypes_attributes_content_class_and_objects_large/1219-1-eng-US/datatypes_attributes_content_class_and_objects_large.jpg" original_filename="e252a562.jpg" mime_type="image/jpeg" width="500" height="409" alternative_text="" alias_key="1293033771" timestamp="1311154210"><original attribute_id="1219" attribute_version="1" attribute_language="eng-US"/><information Height="409" Width="500" IsColor="1"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1219', @@ -20508,7 +20508,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezimage serial_number="1" is_valid="1" filename="site_style_large.png" suffix="png" basename="site_style_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ez-publish/site_style_large/1223-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ez-publish/site_style_large/1223-1-eng-US/site_style_large.png" original_filename="b3b89834.png" mime_type="image/png" width="540" height="411" alternative_text="" alias_key="1293033771" timestamp="1311154211"><original attribute_id="1223" attribute_version="1" attribute_language="eng-US"/></ezimage> +<ezimage serial_number="1" is_valid="1" filename="site_style_large.png" suffix="png" basename="site_style_large" dirpath="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ibexa-dxp/site_style_large/1223-1-eng-US" url="var/ezwebin_site/storage/images/company/new-features/how-to-develop-with-ibexa-dxp/site_style_large/1223-1-eng-US/site_style_large.png" original_filename="b3b89834.png" mime_type="image/png" width="540" height="411" alternative_text="" alias_key="1293033771" timestamp="1311154211"><original attribute_id="1223" attribute_version="1" attribute_language="eng-US"/></ezimage> ', 'data_type_string' => 'ezimage', 'id' => '1223', @@ -20570,7 +20570,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '1227', @@ -20694,7 +20694,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '1239', @@ -20818,7 +20818,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '1251', @@ -20942,7 +20942,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '1263', @@ -21066,7 +21066,7 @@ 'data_float' => '0', 'data_int' => null, 'data_text' => '<?xml version="1.0" encoding="utf-8"?> -<ezauthor><authors><author id="0" name="Administrator User" email="bk@ez.no"/></authors></ezauthor> +<ezauthor><authors><author id="0" name="Administrator User" email="bk@ibexa.co"/></authors></ezauthor> ', 'data_type_string' => 'ezauthor', 'id' => '1275', @@ -21675,7 +21675,7 @@ 'contentobject_id' => '54', 'data_float' => '0', 'data_int' => null, - 'data_text' => 'Copyright © 2011 <a href="http://ez.no" title="eZ Systems">eZ Systems AS</a> (except where otherwise noted). All rights reserved.', + 'data_text' => 'Copyright © 2011 <a href="http://ibexa.co" title="Ibexa">Ibexa AS</a> (except where otherwise noted). All rights reserved.', 'data_type_string' => 'eztext', 'id' => '1327', 'language_code' => 'eng-US', @@ -22902,7 +22902,7 @@ 'content_version' => '1', 'contentobject_id' => '42', 'language_id' => '3', - 'name' => 'Anonymous Users', + 'name' => 'Anonymous users', 'real_translation' => 'eng-US', ], 10 => [ @@ -23374,7 +23374,7 @@ 'content_version' => '1', 'contentobject_id' => '108', 'language_id' => '2', - 'name' => 'How to use eZ Publish', + 'name' => 'How to use Ibexa', 'real_translation' => 'eng-US', ], 69 => [ @@ -23454,7 +23454,7 @@ 'content_version' => '1', 'contentobject_id' => '118', 'language_id' => '2', - 'name' => 'How to manage eZ Publish', + 'name' => 'How to manage Ibexa', 'real_translation' => 'eng-US', ], 79 => [ @@ -23526,7 +23526,7 @@ 'content_version' => '1', 'contentobject_id' => '127', 'language_id' => '2', - 'name' => 'How to develop with eZ Publish', + 'name' => 'How to develop with Ibexa', 'real_translation' => 'eng-US', ], 88 => [ @@ -23550,7 +23550,7 @@ 'content_version' => '1', 'contentobject_id' => '130', 'language_id' => '2', - 'name' => 'Adding Siteaccesses in eZ Publish', + 'name' => 'Adding Siteaccesses in Ibexa', 'real_translation' => 'eng-US', ], 91 => [ @@ -23718,7 +23718,7 @@ 'content_version' => '1', 'contentobject_id' => '151', 'language_id' => '2', - 'name' => 'eZ Publish Enterprise', + 'name' => 'Ibexa Enterprise', 'real_translation' => 'eng-US', ], 112 => [ @@ -23926,7 +23926,7 @@ 'content_version' => '1', 'contentobject_id' => '177', 'language_id' => '2', - 'name' => 'Ivo on eZ Tags', + 'name' => 'Ivo on ibexa tags', 'real_translation' => 'eng-US', ], 138 => [ @@ -24038,7 +24038,7 @@ 'content_version' => '1', 'contentobject_id' => '191', 'language_id' => '2', - 'name' => 'Separate content & design in eZ Publish', + 'name' => 'Separate content & design in Ibexa', 'real_translation' => 'eng-US', ], 152 => [ @@ -24054,7 +24054,7 @@ 'content_version' => '1', 'contentobject_id' => '193', 'language_id' => '2', - 'name' => 'How to use eZ Publish', + 'name' => 'How to use Ibexa', 'real_translation' => 'eng-US', ], 154 => [ @@ -24126,7 +24126,7 @@ 'content_version' => '1', 'contentobject_id' => '202', 'language_id' => '2', - 'name' => 'How to manage eZ Publish', + 'name' => 'How to manage Ibexa', 'real_translation' => 'eng-US', ], 163 => [ @@ -24198,7 +24198,7 @@ 'content_version' => '1', 'contentobject_id' => '211', 'language_id' => '2', - 'name' => 'How to develop with eZ Publish', + 'name' => 'How to develop with Ibexa', 'real_translation' => 'eng-US', ], 172 => [ @@ -25532,7 +25532,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '110', 'parent_node_id' => '107', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish', + 'path_identification_string' => 'getting_started/how_to_use_ibexa', 'path_string' => '/1/2/107/110/', 'priority' => '0', 'remote_id' => '6b3bcab0f149c5acc2e3728df7c66b73', @@ -25550,7 +25550,7 @@ 'modified_subnode' => '1311154186', 'node_id' => '111', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics1', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics1', 'path_string' => '/1/2/107/110/111/', 'priority' => '0', 'remote_id' => '28f9dfe5c0680955eec7a2dec4ebc642', @@ -25568,7 +25568,7 @@ 'modified_subnode' => '1311154186', 'node_id' => '112', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics12', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics12', 'path_string' => '/1/2/107/110/112/', 'priority' => '0', 'remote_id' => '191faba79dc108a19893944befa50d94', @@ -25586,7 +25586,7 @@ 'modified_subnode' => '1311154186', 'node_id' => '113', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics13', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics13', 'path_string' => '/1/2/107/110/113/', 'priority' => '0', 'remote_id' => '8679b5fa3ad0ec216293419ab9834e44', @@ -25604,7 +25604,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '114', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics14', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics14', 'path_string' => '/1/2/107/110/114/', 'priority' => '0', 'remote_id' => '7f32cdefd0cf55b966a44aa52181e30d', @@ -25622,7 +25622,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '115', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics15', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics15', 'path_string' => '/1/2/107/110/115/', 'priority' => '0', 'remote_id' => '29db3c1fcdb497e5cb2c74eb85c0906a', @@ -25640,7 +25640,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '116', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics16', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics16', 'path_string' => '/1/2/107/110/116/', 'priority' => '0', 'remote_id' => '96e30009f712a0315217fed93eba6a18', @@ -25658,7 +25658,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '117', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics17', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics17', 'path_string' => '/1/2/107/110/117/', 'priority' => '0', 'remote_id' => '3a6b87470a25b19a6ad15caa5e24e719', @@ -25676,7 +25676,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '118', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics18', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics18', 'path_string' => '/1/2/107/110/118/', 'priority' => '0', 'remote_id' => 'd0bc77a21920b63543d7b0accab81b24', @@ -25694,7 +25694,7 @@ 'modified_subnode' => '1311154187', 'node_id' => '119', 'parent_node_id' => '110', - 'path_identification_string' => 'getting_started/how_to_use_ez_publish/graphics19', + 'path_identification_string' => 'getting_started/how_to_use_ibexa/graphics19', 'path_string' => '/1/2/107/110/119/', 'priority' => '0', 'remote_id' => 'c75b4b4b870e0e3611e19e1323103282', @@ -25712,7 +25712,7 @@ 'modified_subnode' => '1311154189', 'node_id' => '120', 'parent_node_id' => '107', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa', 'path_string' => '/1/2/107/120/', 'priority' => '0', 'remote_id' => '262d8c936d3757ff81e7bb49392b703f', @@ -25730,7 +25730,7 @@ 'modified_subnode' => '1311154188', 'node_id' => '121', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics1', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics1', 'path_string' => '/1/2/107/120/121/', 'priority' => '0', 'remote_id' => '220d4e10bf4619525c3165823079482c', @@ -25748,7 +25748,7 @@ 'modified_subnode' => '1311154188', 'node_id' => '122', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics12', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics12', 'path_string' => '/1/2/107/120/122/', 'priority' => '0', 'remote_id' => 'f13a938ce2c79a9d438548299220d6dd', @@ -25766,7 +25766,7 @@ 'modified_subnode' => '1311154188', 'node_id' => '123', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics13', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics13', 'path_string' => '/1/2/107/120/123/', 'priority' => '0', 'remote_id' => '04f5e08293954b1851a4dd1cbd976cff', @@ -25784,7 +25784,7 @@ 'modified_subnode' => '1311154188', 'node_id' => '124', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics14', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics14', 'path_string' => '/1/2/107/120/124/', 'priority' => '0', 'remote_id' => 'dc80ae3d9de55855a16218b032062c62', @@ -25802,7 +25802,7 @@ 'modified_subnode' => '1311154189', 'node_id' => '125', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics15', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics15', 'path_string' => '/1/2/107/120/125/', 'priority' => '0', 'remote_id' => '4cb6d4015dd80474074043ab28f96e36', @@ -25820,7 +25820,7 @@ 'modified_subnode' => '1311154189', 'node_id' => '126', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics16', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics16', 'path_string' => '/1/2/107/120/126/', 'priority' => '0', 'remote_id' => '74ce7d6cbae81d90592b4a8a242a284a', @@ -25838,7 +25838,7 @@ 'modified_subnode' => '1311154189', 'node_id' => '127', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics17', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics17', 'path_string' => '/1/2/107/120/127/', 'priority' => '0', 'remote_id' => '6f76d2f7e5812acf4f7c5c70258f4f7e', @@ -25856,7 +25856,7 @@ 'modified_subnode' => '1311154189', 'node_id' => '128', 'parent_node_id' => '120', - 'path_identification_string' => 'getting_started/how_to_manage_ez_publish/graphics18', + 'path_identification_string' => 'getting_started/how_to_manage_ibexa/graphics18', 'path_string' => '/1/2/107/120/128/', 'priority' => '0', 'remote_id' => '5ba07103968a6bb3f05c275c212440f6', @@ -25874,7 +25874,7 @@ 'modified_subnode' => '1311154190', 'node_id' => '129', 'parent_node_id' => '107', - 'path_identification_string' => 'getting_started/how_to_develop_with_ez_publish', + 'path_identification_string' => 'getting_started/how_to_develop_with_ibexa', 'path_string' => '/1/2/107/129/', 'priority' => '0', 'remote_id' => '3579b6c5cd166d5137eada55274892d3', @@ -25892,7 +25892,7 @@ 'modified_subnode' => '1311154190', 'node_id' => '130', 'parent_node_id' => '129', - 'path_identification_string' => 'getting_started/how_to_develop_with_ez_publish/graphics1', + 'path_identification_string' => 'getting_started/how_to_develop_with_ibexa/graphics1', 'path_string' => '/1/2/107/129/130/', 'priority' => '0', 'remote_id' => 'e38a43ce35832e5af2df4f1e9272b926', @@ -25910,7 +25910,7 @@ 'modified_subnode' => '1311154190', 'node_id' => '131', 'parent_node_id' => '129', - 'path_identification_string' => 'getting_started/how_to_develop_with_ez_publish/graphics12', + 'path_identification_string' => 'getting_started/how_to_develop_with_ibexa/graphics12', 'path_string' => '/1/2/107/129/131/', 'priority' => '0', 'remote_id' => '1ba7889f63e32acf70f66349ce1a8953', @@ -25928,7 +25928,7 @@ 'modified_subnode' => '1311154190', 'node_id' => '132', 'parent_node_id' => '107', - 'path_identification_string' => 'getting_started/adding_siteaccesses_in_ez_publish', + 'path_identification_string' => 'getting_started/adding_siteaccesses_in_ibexa', 'path_string' => '/1/2/107/132/', 'priority' => '0', 'remote_id' => '19ae9fcb7334a0e407b2781920247122', @@ -26306,7 +26306,7 @@ 'modified_subnode' => '1311154196', 'node_id' => '153', 'parent_node_id' => '2', - 'path_identification_string' => 'ez_publish_enterprise', + 'path_identification_string' => 'ibexa_enterprise', 'path_string' => '/1/2/153/', 'priority' => '0', 'remote_id' => 'f4bf2e6c1cf75e15b15f0123a82778a1', @@ -26324,7 +26324,7 @@ 'modified_subnode' => '1311154196', 'node_id' => '154', 'parent_node_id' => '153', - 'path_identification_string' => 'ez_publish_enterprise/graphics1', + 'path_identification_string' => 'ibexa_enterprise/graphics1', 'path_string' => '/1/2/153/154/', 'priority' => '0', 'remote_id' => '1b95356b68bd59ef32b0fd7bb2c29130', @@ -26342,7 +26342,7 @@ 'modified_subnode' => '1311154196', 'node_id' => '155', 'parent_node_id' => '153', - 'path_identification_string' => 'ez_publish_enterprise/graphics12', + 'path_identification_string' => 'ibexa_enterprise/graphics12', 'path_string' => '/1/2/153/155/', 'priority' => '0', 'remote_id' => '94b7059eb5e0fe667f7c63eadd53a62b', @@ -26774,7 +26774,7 @@ 'modified_subnode' => '1311154202', 'node_id' => '179', 'parent_node_id' => '178', - 'path_identification_string' => 'community/pictures/ivo_on_ez_tags', + 'path_identification_string' => 'community/pictures/ivo_on_ibexa_tags', 'path_string' => '/1/2/167/178/179/', 'priority' => '0', 'remote_id' => 'c6c32c884db955ea09338c90eee84746', @@ -27026,7 +27026,7 @@ 'modified_subnode' => '1311154205', 'node_id' => '193', 'parent_node_id' => '192', - 'path_identification_string' => 'company/new_features/separate_content_design_in_ez_publish', + 'path_identification_string' => 'company/new_features/separate_content_design_in_ibexa', 'path_string' => '/1/2/190/192/193/', 'priority' => '0', 'remote_id' => '10d57795c986ca61689e6c2c11382eb3', @@ -27044,7 +27044,7 @@ 'modified_subnode' => '1311154205', 'node_id' => '194', 'parent_node_id' => '193', - 'path_identification_string' => 'company/new_features/separate_content_design_in_ez_publish/separation_of_content_and_design', + 'path_identification_string' => 'company/new_features/separate_content_design_in_ibexa/separation_of_content_and_design', 'path_string' => '/1/2/190/192/193/194/', 'priority' => '0', 'remote_id' => '87417129b080d24d335eb2653dd1a40c', @@ -27062,7 +27062,7 @@ 'modified_subnode' => '1311154207', 'node_id' => '195', 'parent_node_id' => '192', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa', 'path_string' => '/1/2/190/192/195/', 'priority' => '0', 'remote_id' => 'fd56d839df6e2b52055d4a62f4df7c3e', @@ -27080,7 +27080,7 @@ 'modified_subnode' => '1311154206', 'node_id' => '196', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/openoffice_import_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/openoffice_import_large', 'path_string' => '/1/2/190/192/195/196/', 'priority' => '0', 'remote_id' => 'ab2f2093f16c56ac2e61f40f25d2dbe0', @@ -27098,7 +27098,7 @@ 'modified_subnode' => '1311154206', 'node_id' => '197', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/translation_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/translation_large', 'path_string' => '/1/2/190/192/195/197/', 'priority' => '0', 'remote_id' => '185a1737e9012e7c3025df876bbb4f9b', @@ -27116,7 +27116,7 @@ 'modified_subnode' => '1311154206', 'node_id' => '198', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/multiupload_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/multiupload_large', 'path_string' => '/1/2/190/192/195/198/', 'priority' => '0', 'remote_id' => 'efdca2f6ddd2c8befc283ae3465b9acd', @@ -27134,7 +27134,7 @@ 'modified_subnode' => '1311154207', 'node_id' => '199', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/gallery_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/gallery_large', 'path_string' => '/1/2/190/192/195/199/', 'priority' => '0', 'remote_id' => 'f81b0e4c31ea13d94b9838e9e7e7ad7e', @@ -27152,7 +27152,7 @@ 'modified_subnode' => '1311154207', 'node_id' => '200', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/search_result_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/search_result_large', 'path_string' => '/1/2/190/192/195/200/', 'priority' => '0', 'remote_id' => '666efc49c267cddf55ca707408f4ac42', @@ -27170,7 +27170,7 @@ 'modified_subnode' => '1311154207', 'node_id' => '201', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/ez_tool_bar_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/ez_tool_bar_large', 'path_string' => '/1/2/190/192/195/201/', 'priority' => '0', 'remote_id' => 'afb51a5f898c549c9e1f35afc75d66f3', @@ -27188,7 +27188,7 @@ 'modified_subnode' => '1311154207', 'node_id' => '202', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/quicktime_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/quicktime_large', 'path_string' => '/1/2/190/192/195/202/', 'priority' => '0', 'remote_id' => '94b71866ede0d2776c140e85a823d31d', @@ -27206,7 +27206,7 @@ 'modified_subnode' => '1311154207', 'node_id' => '203', 'parent_node_id' => '195', - 'path_identification_string' => 'company/new_features/how_to_use_ez_publish/web_2_0_large', + 'path_identification_string' => 'company/new_features/how_to_use_ibexa/web_2_0_large', 'path_string' => '/1/2/190/192/195/203/', 'priority' => '0', 'remote_id' => 'e74475a240fb7061b40c75f5e6fcd52c', @@ -27224,7 +27224,7 @@ 'modified_subnode' => '1311154210', 'node_id' => '204', 'parent_node_id' => '192', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa', 'path_string' => '/1/2/190/192/204/', 'priority' => '0', 'remote_id' => '42a3b07037390457ad0f2c65494bfd97', @@ -27242,7 +27242,7 @@ 'modified_subnode' => '1311154208', 'node_id' => '205', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/usermanagement_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/usermanagement_large', 'path_string' => '/1/2/190/192/204/205/', 'priority' => '0', 'remote_id' => '6da1a82ba3d962d2ad953fff8ee26362', @@ -27260,7 +27260,7 @@ 'modified_subnode' => '1311154208', 'node_id' => '206', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/workflow_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/workflow_large', 'path_string' => '/1/2/190/192/204/206/', 'priority' => '0', 'remote_id' => 'b284abd88bdcb1887a86e40304ffc66f', @@ -27278,7 +27278,7 @@ 'modified_subnode' => '1311154209', 'node_id' => '207', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/versioning_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/versioning_large', 'path_string' => '/1/2/190/192/204/207/', 'priority' => '0', 'remote_id' => 'e3400bb44aae5fcb416156e7450f3f32', @@ -27296,7 +27296,7 @@ 'modified_subnode' => '1311154209', 'node_id' => '208', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/datatypes_attributes_content_class_and_objects_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/datatypes_attributes_content_class_and_objects_large', 'path_string' => '/1/2/190/192/204/208/', 'priority' => '0', 'remote_id' => '2b2b27bbaaec688aaec1a5d39b4b13ae', @@ -27314,7 +27314,7 @@ 'modified_subnode' => '1311154209', 'node_id' => '209', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/multiple_location_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/multiple_location_large', 'path_string' => '/1/2/190/192/204/209/', 'priority' => '0', 'remote_id' => '37e5feed6574d1d8da4b4531569a27a2', @@ -27332,7 +27332,7 @@ 'modified_subnode' => '1311154209', 'node_id' => '210', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/multisite_deployment_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/multisite_deployment_large', 'path_string' => '/1/2/190/192/204/210/', 'priority' => '0', 'remote_id' => '5625695a66034f53b9b6e14029e06e80', @@ -27350,7 +27350,7 @@ 'modified_subnode' => '1311154210', 'node_id' => '211', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/seo_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/seo_large', 'path_string' => '/1/2/190/192/204/211/', 'priority' => '0', 'remote_id' => '824aba4122ee51d9638847e55642feec', @@ -27368,7 +27368,7 @@ 'modified_subnode' => '1311154210', 'node_id' => '212', 'parent_node_id' => '204', - 'path_identification_string' => 'company/new_features/how_to_manage_ez_publish/admin_site_large', + 'path_identification_string' => 'company/new_features/how_to_manage_ibexa/admin_site_large', 'path_string' => '/1/2/190/192/204/212/', 'priority' => '0', 'remote_id' => '73e95051b800ce051c3851c60807787b', @@ -27386,7 +27386,7 @@ 'modified_subnode' => '1311154211', 'node_id' => '213', 'parent_node_id' => '192', - 'path_identification_string' => 'company/new_features/how_to_develop_with_ez_publish', + 'path_identification_string' => 'company/new_features/how_to_develop_with_ibexa', 'path_string' => '/1/2/190/192/213/', 'priority' => '0', 'remote_id' => '3f921d3c490a6c17870b1f01fcfbceed', @@ -27404,7 +27404,7 @@ 'modified_subnode' => '1311154211', 'node_id' => '214', 'parent_node_id' => '213', - 'path_identification_string' => 'company/new_features/how_to_develop_with_ez_publish/datatypes_attributes_content_class_and_objects_large', + 'path_identification_string' => 'company/new_features/how_to_develop_with_ibexa/datatypes_attributes_content_class_and_objects_large', 'path_string' => '/1/2/190/192/213/214/', 'priority' => '0', 'remote_id' => '97f7b391f1522bf404ff672ac338b91c', @@ -27422,7 +27422,7 @@ 'modified_subnode' => '1311154211', 'node_id' => '215', 'parent_node_id' => '213', - 'path_identification_string' => 'company/new_features/how_to_develop_with_ez_publish/site_style_large', + 'path_identification_string' => 'company/new_features/how_to_develop_with_ibexa/site_style_large', 'path_string' => '/1/2/190/192/213/215/', 'priority' => '0', 'remote_id' => 'c5549bf68962a5384bd2225931cba302', @@ -32569,7 +32569,7 @@ 'last_checked' => '0', 'modified' => '1301057834', 'original_url_md5' => 'bb9c47d334fd775f1c54c45d460e6b2a', - 'url' => 'http://doc.ez.no/', + 'url' => 'http://doc.ibexa.co/', ], 1 => [ 'created' => '1301057836', @@ -32578,7 +32578,7 @@ 'last_checked' => '0', 'modified' => '1301057836', 'original_url_md5' => 'b13f5ff5cdcad2a4efb461e4edf6f718', - 'url' => 'http://ez.no/Demos-videos/eZ-Publish-Administration-Interface-Video-Tutorial', + 'url' => 'http://ibexa.co/Demos-videos/Ibexa-Administration-Interface-Video-Tutorial', ], 2 => [ 'created' => '1301057836', @@ -32587,7 +32587,7 @@ 'last_checked' => '0', 'modified' => '1301057836', 'original_url_md5' => '7b133bbdf1d039979a973e5a697e3743', - 'url' => 'http://ez.no/Demos-videos/eZ-Publish-Online-Editor-Video-Tutorial', + 'url' => 'http://ibexa.co/Demos-videos/Ibexa-Online-Editor-Video-Tutorial', ], 3 => [ 'created' => '1301057836', @@ -32596,7 +32596,7 @@ 'last_checked' => '0', 'modified' => '1301057836', 'original_url_md5' => '4e75c83ab35d461f109ec959aa1c5e1d', - 'url' => 'http://ez.no/Demos-videos/eZ-Flow-Video-Tutorial', + 'url' => 'http://ibexa.co/Demos-videos/eZ-Flow-Video-Tutorial', ], 4 => [ 'created' => '1301057836', @@ -32605,7 +32605,7 @@ 'last_checked' => '0', 'modified' => '1301057836', 'original_url_md5' => '215310c57a3d54ef1356c20855510357', - 'url' => 'http://share.ez.no/learn/ez-publish/an-introduction-to-developing-ez-publish-extensions', + 'url' => 'http://ibexa.co/learn/ibexa-dxp/an-introduction-to-developing-ibexa-dxp-extensions', ], 5 => [ 'created' => '1301057836', @@ -32614,7 +32614,7 @@ 'last_checked' => '0', 'modified' => '1301057836', 'original_url_md5' => '9ba078c54f33985da5bd1348a8f39741', - 'url' => 'http://share.ez.no/learn/ez-publish/creating-a-simple-custom-workflow-event', + 'url' => 'http://share.ibexa.co/learn/ibexa-dxp/creating-a-simple-custom-workflow-event', ], 6 => [ 'created' => '1301057836', @@ -32623,7 +32623,7 @@ 'last_checked' => '0', 'modified' => '1301057836', 'original_url_md5' => 'eb3d19c36acbd41176094024d8fccfd5', - 'url' => 'http://www.slideshare.net/ezcommunity/ole-marius-smestad-rest-api-how-to-turn-ez-publish-into-a-multichannel-machine', + 'url' => 'http://www.slideshare.net/ezcommunity/ole-marius-smestad-rest-api-how-to-turn-ibexa-dxp-into-a-multichannel-machine', ], 7 => [ 'created' => '1301057836', @@ -32641,7 +32641,7 @@ 'last_checked' => '0', 'modified' => '1301057836', 'original_url_md5' => 'af8f8bdc5fac2f3ada6ad337adab04cb', - 'url' => 'http://doc.ez.no/eZ-Publish/Upgrading/Upgrading-to-4.5', + 'url' => 'http://doc.ibexa.co/Ibexa/Upgrading/Upgrading-to-4.5', ], 9 => [ 'created' => '1301057836', @@ -32650,7 +32650,7 @@ 'last_checked' => '0', 'modified' => '1301057836', 'original_url_md5' => '3c6d6cfc2642951e9a946b697f84a306', - 'url' => 'http://share.ez.no/learn', + 'url' => 'http://share.ibexa.co/learn', ], 10 => [ 'created' => '1301057836', @@ -32659,7 +32659,7 @@ 'last_checked' => '0', 'modified' => '1301057836', 'original_url_md5' => 'ac3ba54b44950b2d77fa42cc57dab914', - 'url' => 'http://ez.no/Demos-videos', + 'url' => 'http://ibexa.co/Demos-videos', ], 11 => [ 'created' => '1311154174', @@ -32668,7 +32668,7 @@ 'last_checked' => '0', 'modified' => '1311154174', 'original_url_md5' => '590afa1809904842796205d4d571c976', - 'url' => 'http://doc.ez.no/eZ-Publish/Technical-manual/4.5', + 'url' => 'http://doc.ibexa.co/Ibexa/Technical-manual/4.5', ], 12 => [ 'created' => '1311154174', @@ -32677,7 +32677,7 @@ 'last_checked' => '0', 'modified' => '1311154174', 'original_url_md5' => '45cd300bca7761588e0ace23640eff3f', - 'url' => 'http://doc.ez.no/eZ-Publish/User-manual/4.5', + 'url' => 'http://doc.ibexa.co/Ibexa/User-manual/4.5', ], 13 => [ 'created' => '1311154185', @@ -32686,7 +32686,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '72707907a848a14f33f1352ae0308e5b', - 'url' => 'http://share.ez.no/forums', + 'url' => 'http://share.ibexa.co/forums', ], 14 => [ 'created' => '1311154185', @@ -32695,7 +32695,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'da16d9c486d95e264169293a39b67ad6', - 'url' => 'http://share.ez.no/blogs', + 'url' => 'http://share.ibexa.co/blogs', ], 15 => [ 'created' => '1311154185', @@ -32722,7 +32722,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'fd86f64652e4408d72ac42e78de462f5', - 'url' => 'http://share.ez.no/about/get-involved', + 'url' => 'http://share.ibexa.co/about/get-involved', ], 18 => [ 'created' => '1311154185', @@ -32731,7 +32731,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'abcf623ca9bfdedaae617b280473bda7', - 'url' => 'http://share.ez.no/about/etiquette-blogging-and-usage-guidelines', + 'url' => 'http://share.ibexa.co/about/etiquette-blogging-and-usage-guidelines', ], 19 => [ 'created' => '1311154185', @@ -32740,7 +32740,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '25f38d8b1ca7e64c6a82d7e126f80022', - 'url' => 'http://share.ez.no/forums/install-configuration', + 'url' => 'http://share.ibexa.co/forums/install-configuration', ], 20 => [ 'created' => '1311154185', @@ -32749,7 +32749,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '6a643a86a729aded29612aac280063b2', - 'url' => 'http://share.ez.no/forums/setup-design', + 'url' => 'http://share.ibexa.co/forums/setup-design', ], 21 => [ 'created' => '1311154185', @@ -32758,7 +32758,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '8736641c6fc3520c5f3adbb9c5f6de74', - 'url' => 'http://share.ez.no/forums/general', + 'url' => 'http://share.ibexa.co/forums/general', ], 22 => [ 'created' => '1311154185', @@ -32767,7 +32767,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '81ea35ab0b578ddbecf391f7c7a509ac', - 'url' => 'http://share.ez.no/forums/developer', + 'url' => 'http://share.ibexa.co/forums/developer', ], 23 => [ 'created' => '1311154185', @@ -32776,7 +32776,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '76de2bf179fe746173daf48499a81f23', - 'url' => 'http://share.ez.no/forums/suggestions', + 'url' => 'http://share.ibexa.co/forums/suggestions', ], 24 => [ 'created' => '1311154185', @@ -32785,7 +32785,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'a29b9ce655377694e606ee70a6636361', - 'url' => 'http://share.ez.no/forums/extensions', + 'url' => 'http://share.ibexa.co/forums/extensions', ], 25 => [ 'created' => '1311154185', @@ -32794,7 +32794,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '7b907a0c58bcc0665b3638e97c2597b6', - 'url' => 'http://share.ez.no/forums/extensions/ez-newsletter', + 'url' => 'http://share.ibexa.co/forums/extensions/ez-newsletter', ], 26 => [ 'created' => '1311154185', @@ -32803,7 +32803,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '12021495bfa320016e304ef52f72ad2c', - 'url' => 'http://share.ez.no/forums/extensions/website-interface', + 'url' => 'http://share.ibexa.co/forums/extensions/website-interface', ], 27 => [ 'created' => '1311154185', @@ -32812,7 +32812,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '958912ed4a05e34e44ff28acb5a7df3b', - 'url' => 'http://share.ez.no/forums/extensions/ez-flow', + 'url' => 'http://share.ibexa.co/forums/extensions/ez-flow', ], 28 => [ 'created' => '1311154185', @@ -32821,7 +32821,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'f36ed50995d7898feb21fea71a2e93c0', - 'url' => 'http://share.ez.no/forums/extensions/ez-find', + 'url' => 'http://share.ibexa.co/forums/extensions/ez-find', ], 29 => [ 'created' => '1311154185', @@ -32830,7 +32830,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '76538a2a362d14e50e52f2204c5f7fc0', - 'url' => 'http://share.ez.no/forums/translation', + 'url' => 'http://share.ibexa.co/forums/translation', ], 30 => [ 'created' => '1311154185', @@ -32839,7 +32839,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '10c21ad9cb1aaea53ae13922b8462078', - 'url' => 'http://share.ez.no/forums/localized-forums', + 'url' => 'http://share.ibexa.co/forums/localized-forums', ], 31 => [ 'created' => '1311154185', @@ -32848,7 +32848,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'f5b54a2571a6a92a1b714662461df808', - 'url' => 'http://share.ez.no/forums/feedback-and-ideas-for-this-portal', + 'url' => 'http://share.ibexa.co/forums/feedback-and-ideas-for-this-portal', ], 32 => [ 'created' => '1311154185', @@ -32857,7 +32857,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '53137591ef856777bddbd7843ed55945', - 'url' => 'http://issues.ez.no/ProjectSelect.php?Id=6', + 'url' => 'http://issues.ibexa.co/ProjectSelect.php?Id=6', ], 33 => [ 'created' => '1311154185', @@ -32866,7 +32866,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '9fed8384518daf741b21073051b2bc82', - 'url' => 'http://issues.ez.no/ProjectSelect.php?Id=3', + 'url' => 'http://issues.ibexa.co/ProjectSelect.php?Id=3', ], 34 => [ 'created' => '1311154185', @@ -32875,7 +32875,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '009202f0b8516c86a1ae2e713b236a0d', - 'url' => 'http://share.ez.no/articles', + 'url' => 'http://share.ibexa.co/articles', ], 35 => [ 'created' => '1311154185', @@ -32884,7 +32884,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'af97ad168859364ba6be544a10227dce', - 'url' => 'http://share.ez.no/tutorials', + 'url' => 'http://share.ibexa.co/tutorials', ], 36 => [ 'created' => '1311154185', @@ -32893,7 +32893,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '42e449f547a866fc91cd9cc0276bb856', - 'url' => 'http://share.ez.no/blogs/share.ez.no-team/share-your-experience-write-a-tutorial-for-the-community', + 'url' => 'http://share.ibexa.co/blogs/share.ibexa.co-team/share-your-experience-write-a-tutorial-for-the-community', ], 37 => [ 'created' => '1311154185', @@ -32983,7 +32983,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '48e508939abb04100c31feb3db7bca99', - 'url' => 'http://lists.ez.no/mailman/listinfo/sdk-public', + 'url' => 'http://lists.ibexa.co/mailman/listinfo/sdk-public', ], 47 => [ 'created' => '1311154185', @@ -32992,7 +32992,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'eb2c7ae92b6523fce7f380712dcdc3ee', - 'url' => 'http://lists.ez.no/mailman/listinfo/sdk-svn', + 'url' => 'http://lists.ibexa.co/mailman/listinfo/sdk-svn', ], 48 => [ 'created' => '1311154185', @@ -33001,7 +33001,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '2d823657cbdd8d41e63875e97497fa4a', - 'url' => 'http://share.ez.no/rss/feed/all_forums', + 'url' => 'http://share.ibexa.co/rss/feed/all_forums', ], 49 => [ 'created' => '1311154185', @@ -33010,7 +33010,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'a1c839f19e544f45f1b24e4081a859b8', - 'url' => 'http://share.ez.no/rss/feed/forum_topics_only', + 'url' => 'http://share.ibexa.co/rss/feed/forum_topics_only', ], 50 => [ 'created' => '1311154185', @@ -33019,7 +33019,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '3f2fff22712f47e136a46a9e7190418a', - 'url' => 'http://share.ez.no/rss/feed/articles', + 'url' => 'http://share.ibexa.co/rss/feed/articles', ], 51 => [ 'created' => '1311154185', @@ -33028,7 +33028,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'a3589cf032c79d5afbb5e0231f9bde63', - 'url' => 'http://share.ez.no/rss/feed/blogs', + 'url' => 'http://share.ibexa.co/rss/feed/blogs', ], 52 => [ 'created' => '1311154185', @@ -33037,7 +33037,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '7c1f530d6ad202571f22cc8c606bb83c', - 'url' => 'http://share.ez.no/rss/feed/forums_developer', + 'url' => 'http://share.ibexa.co/rss/feed/forums_developer', ], 53 => [ 'created' => '1311154185', @@ -33046,7 +33046,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'aedc6546cbc245956ca72a0f210bd685', - 'url' => 'http://share.ez.no/rss/feed/forums_discussions', + 'url' => 'http://share.ibexa.co/rss/feed/forums_discussions', ], 54 => [ 'created' => '1311154185', @@ -33055,7 +33055,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '06b3b46ac8297015c7cc1b4059ffd296', - 'url' => 'http://share.ez.no/rss/feed/forums_extensions', + 'url' => 'http://share.ibexa.co/rss/feed/forums_extensions', ], 55 => [ 'created' => '1311154185', @@ -33064,7 +33064,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'bdbaed15beb6ef757854ee3cd8fef0c2', - 'url' => 'http://share.ez.no/rss/feed/forums_feedback', + 'url' => 'http://share.ibexa.co/rss/feed/forums_feedback', ], 56 => [ 'created' => '1311154185', @@ -33073,7 +33073,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '41c2346efe6f237fc71a71a5c5be7157', - 'url' => 'http://share.ez.no/rss/feed/forums_general', + 'url' => 'http://share.ibexa.co/rss/feed/forums_general', ], 57 => [ 'created' => '1311154185', @@ -33082,7 +33082,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '075ff7cbbd7d64d25ba70ba8dbcc2247', - 'url' => 'http://share.ez.no/rss/feed/forums_install_and_configuration', + 'url' => 'http://share.ibexa.co/rss/feed/forums_install_and_configuration', ], 58 => [ 'created' => '1311154185', @@ -33091,7 +33091,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '60eb4cded3c2b0f8034179ea8cc78433', - 'url' => 'http://share.ez.no/rss/feed/forums_setup_and_design', + 'url' => 'http://share.ibexa.co/rss/feed/forums_setup_and_design', ], 59 => [ 'created' => '1311154185', @@ -33100,7 +33100,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '3ac4e53a5c5c4c40cdc7730f004fd1fc', - 'url' => 'http://share.ez.no/rss/feed/forums_suggestions', + 'url' => 'http://share.ibexa.co/rss/feed/forums_suggestions', ], 60 => [ 'created' => '1311154185', @@ -33109,7 +33109,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'f36ea06615ae2bda0c494c730a17d4c5', - 'url' => 'http://share.ez.no/rss/feed/forums_translation', + 'url' => 'http://share.ibexa.co/rss/feed/forums_translation', ], 61 => [ 'created' => '1311154185', @@ -33118,7 +33118,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'b8d97968cb3fea3a251ff6643d78b8c9', - 'url' => 'http://share.ez.no/rss/feed/projects', + 'url' => 'http://share.ibexa.co/rss/feed/projects', ], 62 => [ 'created' => '1311154185', @@ -33145,7 +33145,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'ec662565f30cac29d1c5e8b92fcf36db', - 'url' => 'http://share.ez.no/forums/general/ez-publish-on-google-wave', + 'url' => 'http://share.ibexa.co/forums/general/ibexa-dxp-on-google-wave', ], 65 => [ 'created' => '1311154185', @@ -33154,7 +33154,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '9fd5c2ca7e61e4e632fc5087ab478c48', - 'url' => 'http://projects.ez.no/', + 'url' => 'http://projects.ibexa.co/', ], 66 => [ 'created' => '1311154185', @@ -33172,7 +33172,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '887dd7ce7eca08e453cd6e1809e2050c', - 'url' => 'http://pubsvn.ez.no/doxygen/trunk/html/index.html', + 'url' => 'http://pubsvn.ibexa.co/doxygen/trunk/html/index.html', ], 68 => [ 'created' => '1311154185', @@ -33190,7 +33190,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '52b043bcd64a2d3e5ceca70ac3f94834', - 'url' => 'http://issues.ez.no/ezpublish', + 'url' => 'http://issues.ibexa.co/ezpublish', ], 70 => [ 'created' => '1311154185', @@ -33199,7 +33199,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '88c4142410b68a1216890e1e9f375521', - 'url' => 'http://share.ez.no/github.com/ezsystems/ezpublish', + 'url' => 'http://share.ibexa.co/github.com/ezsystems/ezpublish', ], 71 => [ 'created' => '1311154185', @@ -33208,7 +33208,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => '34822fe97d42d592f378247b5058e48d', - 'url' => 'http://projects.ez.no/ezpublish_translation', + 'url' => 'http://projects.ibexa.co/ezpublish_translation', ], 72 => [ 'created' => '1311154185', @@ -33217,7 +33217,7 @@ 'last_checked' => '0', 'modified' => '1311154185', 'original_url_md5' => 'd2a07a3cf059b5ebe90629b817558a2c', - 'url' => 'http://ez.no/ezfind', + 'url' => 'http://ibexa.co/ezfind', ], 73 => [ 'created' => '1311154187', @@ -33226,7 +33226,7 @@ 'last_checked' => '0', 'modified' => '1311154187', 'original_url_md5' => '73bbbe9943f1196c0282d5ed8467e003', - 'url' => 'http://ez.no/support_and_services/ez_publish_premium', + 'url' => 'http://ibexa.co/support_and_services/ibexa_premium', ], 74 => [ 'created' => '1311154189', @@ -33235,7 +33235,7 @@ 'last_checked' => '0', 'modified' => '1311154189', 'original_url_md5' => '443aa8489b0f6bedb1240fa4011694c9', - 'url' => 'http://ez.no/doc/ez_publish/technical_manual/4_0/templates/the_template_override_system', + 'url' => 'http://ibexa.co/doc/ibexa/technical_manual/4_0/templates/the_template_override_system', ], 75 => [ 'created' => '1311154189', @@ -33244,7 +33244,7 @@ 'last_checked' => '0', 'modified' => '1311154189', 'original_url_md5' => '903fb32b3f6da9ad8248504573e3720c', - 'url' => 'http://ez.no/developer/contribs/site_styles', + 'url' => 'http://ibexa.co/developer/contribs/site_styles', ], 76 => [ 'created' => '1311154189', @@ -33253,7 +33253,7 @@ 'last_checked' => '0', 'modified' => '1311154189', 'original_url_md5' => '7df50136b3e6e49601ec8f532a88f5fe', - 'url' => 'http://ez.no/ezcomponents', + 'url' => 'http://ibexa.co/ezcomponents', ], 77 => [ 'created' => '1311154189', @@ -33262,7 +33262,7 @@ 'last_checked' => '0', 'modified' => '1311154189', 'original_url_md5' => 'f7f365668e85a3db20b7e06c08274b2a', - 'url' => 'http://ez.no/developer/contribs', + 'url' => 'http://ibexa.co/developer/contribs', ], 78 => [ 'created' => '1311154189', @@ -33298,7 +33298,7 @@ 'last_checked' => '0', 'modified' => '1311154190', 'original_url_md5' => '712f33e020d970504ca0d2920d073f02', - 'url' => 'http://share.ez.no/tutorials/ez-publish/lots-of-websites-one-ez-publish-installation-adding-siteaccesses-in-ez-publish', + 'url' => 'http://share.ibexa.co/tutorials/ibexa-dxp/lots-of-websites-one-ibexa-dxp-installation-adding-siteaccesses-in-ibexa-dxp', ], 82 => [ 'created' => '1311154190', @@ -33334,7 +33334,7 @@ 'last_checked' => '0', 'modified' => '1311154205', 'original_url_md5' => 'd2be3bb903b0b7de8303dfea1cada332', - 'url' => 'http://ez.no/ezpublish', + 'url' => 'http://ibexa.co/ezpublish', ], 86 => [ 'created' => '1311154205', @@ -33343,7 +33343,7 @@ 'last_checked' => '0', 'modified' => '1311154205', 'original_url_md5' => '81a3c8f41320d5b9bf4a8a006551918f', - 'url' => 'http://ez.no/ezpublish/use_ez_publish', + 'url' => 'http://ibexa.co/ezpublish/use_ibexa', ], 87 => [ 'created' => '1311154205', @@ -33352,7 +33352,7 @@ 'last_checked' => '0', 'modified' => '1311154205', 'original_url_md5' => '00de4d3b098fa1640787b233185d1129', - 'url' => 'http://ez.no/solutions/digital_media/online_magazines', + 'url' => 'http://ibexa.co/solutions/digital_media/online_magazines', ], 88 => [ 'created' => '1311154205', @@ -33361,7 +33361,7 @@ 'last_checked' => '0', 'modified' => '1311154205', 'original_url_md5' => '0e4becdbf18714aaa1d7c63ccf2118ac', - 'url' => 'http://ez.no/solutions/digital_media/online_newspapers', + 'url' => 'http://ibexa.co/solutions/digital_media/online_newspapers', ], 89 => [ 'created' => '1311154205', @@ -33370,7 +33370,7 @@ 'last_checked' => '0', 'modified' => '1311154205', 'original_url_md5' => 'c6d6e36c5db2942e6a6cca1bfcfc17c8', - 'url' => 'http://ez.no/solutions/community_portal', + 'url' => 'http://ibexa.co/solutions/community_portal', ], 90 => [ 'created' => '1311154205', @@ -33379,7 +33379,7 @@ 'last_checked' => '0', 'modified' => '1311154205', 'original_url_md5' => 'fc218c4dd363b2845f79379fecb92f08', - 'url' => 'http://ez.no/developer', + 'url' => 'http://ibexa.co/developer', ], 91 => [ 'created' => '1311154210', @@ -33388,7 +33388,7 @@ 'last_checked' => '0', 'modified' => '1311154210', 'original_url_md5' => 'b906332d411e5325f9b7fd14c3845f54', - 'url' => 'http://ez.no/ezpublish/develop_with_ez_publish', + 'url' => 'http://ibexa.co/ezpublish/develop_with_ibexa', ], 92 => [ 'created' => '1311154211', @@ -34129,7 +34129,7 @@ 'is_internal' => '1', 'is_wildcard' => '0', 'source_md5' => '2dd3db5dc7122ea5f3ee539bb18fe97d', - 'source_url' => 'design/ez_publish', + 'source_url' => 'design/ibexa', ], 16 => [ 'destination_url' => 'content/view/full/58', @@ -34179,7 +34179,7 @@ 'lang_mask' => '3', 'link' => '46', 'parent' => '0', - 'text' => 'eZ-Publish', + 'text' => 'Ibexa', 'text_md5' => '10e4c3cb527fb9963258469986c16240', ], 3 => [ @@ -34283,7 +34283,7 @@ 'lang_mask' => '2', 'link' => '132', 'parent' => '0', - 'text' => 'eZ-Publish-Enterprise', + 'text' => 'Ibexa-Enterprise', 'text_md5' => '78e494fbf5158218fd054fcd2cf9d849', ], 11 => [ @@ -34777,7 +34777,7 @@ 'lang_mask' => '2', 'link' => '24', 'parent' => '25', - 'text' => 'eZ-publish', + 'text' => 'Ibexa', 'text_md5' => '10e4c3cb527fb9963258469986c16240', ], 49 => [ @@ -35336,7 +35336,7 @@ 'lang_mask' => '2', 'link' => '111', 'parent' => '86', - 'text' => 'Adding-Siteaccesses-in-eZ-Publish', + 'text' => 'Adding-Siteaccesses-in-Ibexa', 'text_md5' => '0ca9baa4f34c27e2b5bb8a6e01bc61f4', ], 92 => [ @@ -35362,7 +35362,7 @@ 'lang_mask' => '2', 'link' => '99', 'parent' => '86', - 'text' => 'How-to-manage-eZ-Publish', + 'text' => 'How-to-manage-Ibexa', 'text_md5' => '38f227f027d9b66fa32a1b7fd09102b0', ], 94 => [ @@ -35388,7 +35388,7 @@ 'lang_mask' => '2', 'link' => '89', 'parent' => '86', - 'text' => 'How-to-use-eZ-Publish', + 'text' => 'How-to-use-Ibexa', 'text_md5' => '7bf161f3ad9b79be40262c807b42c901', ], 96 => [ @@ -35401,7 +35401,7 @@ 'lang_mask' => '2', 'link' => '108', 'parent' => '86', - 'text' => 'How-to-develop-with-eZ-Publish', + 'text' => 'How-to-develop-with-Ibexa', 'text_md5' => 'bca34b10b4bf19a49be18802a0cb873a', ], 97 => [ @@ -36259,7 +36259,7 @@ 'lang_mask' => '2', 'link' => '158', 'parent' => '157', - 'text' => 'Ivo-on-eZ-Tags', + 'text' => 'Ivo-on-ibexa-tags', 'text_md5' => '95906379fb2066053d63420a897a1892', ], 163 => [ @@ -36467,7 +36467,7 @@ 'lang_mask' => '2', 'link' => '183', 'parent' => '171', - 'text' => 'How-to-manage-eZ-Publish', + 'text' => 'How-to-manage-Ibexa', 'text_md5' => '38f227f027d9b66fa32a1b7fd09102b0', ], 179 => [ @@ -36506,7 +36506,7 @@ 'lang_mask' => '2', 'link' => '174', 'parent' => '171', - 'text' => 'How-to-use-eZ-Publish', + 'text' => 'How-to-use-Ibexa', 'text_md5' => '7bf161f3ad9b79be40262c807b42c901', ], 182 => [ @@ -36519,7 +36519,7 @@ 'lang_mask' => '2', 'link' => '172', 'parent' => '171', - 'text' => 'Separate-content-design-in-eZ-Publish', + 'text' => 'Separate-content-design-in-Ibexa', 'text_md5' => '8c4722f2f329953dd24c29b2263feb48', ], 183 => [ @@ -36545,7 +36545,7 @@ 'lang_mask' => '2', 'link' => '192', 'parent' => '171', - 'text' => 'How-to-develop-with-eZ-Publish', + 'text' => 'How-to-develop-with-Ibexa', 'text_md5' => 'bca34b10b4bf19a49be18802a0cb873a', ], 185 => [ @@ -37462,7 +37462,7 @@ ], 2 => [ 'contentobject_id' => '226', - 'email' => 'pa@ez.no', + 'email' => 'pa@ibexa.co', 'login' => 'a_member', 'password_hash' => '$2y$10$FDn9NPwzhq85cLLxfD5Wu.L3SL3Z/LNCvhkltJUV0wcJj7ciJg2oy', 'password_hash_type' => '7', @@ -38762,7 +38762,7 @@ 1 => [ 'id' => '802', 'object_count' => '21', - 'word' => 'ez', + 'word' => 'ibexa', ], 2 => [ 'id' => '803', @@ -39267,7 +39267,7 @@ 102 => [ 'id' => '927', 'object_count' => '3', - 'word' => 'ez.no', + 'word' => 'ibexa.co', ], 103 => [ 'id' => '928', @@ -41057,7 +41057,7 @@ 460 => [ 'id' => '1288', 'object_count' => '2', - 'word' => 'share.ez.no', + 'word' => 'share.ibexa.co', ], 461 => [ 'id' => '1289', @@ -41427,7 +41427,7 @@ 534 => [ 'id' => '1362', 'object_count' => '2', - 'word' => 'ezpublish', + 'word' => 'ibexa', ], 535 => [ 'id' => '1363', @@ -41642,7 +41642,7 @@ 577 => [ 'id' => '1405', 'object_count' => '1', - 'word' => 'lists.ez.no', + 'word' => 'lists.ibexa.co', ], 578 => [ 'id' => '1406', @@ -42107,7 +42107,7 @@ 670 => [ 'id' => '1498', 'object_count' => '1', - 'word' => 'projects.ez.no', + 'word' => 'projects.ibexa.co', ], 671 => [ 'id' => '1499', @@ -42252,7 +42252,7 @@ 699 => [ 'id' => '1527', 'object_count' => '1', - 'word' => 'doc.ez.no', + 'word' => 'doc.ibexa.co', ], 700 => [ 'id' => '1528', @@ -42482,7 +42482,7 @@ 745 => [ 'id' => '1573', 'object_count' => '1', - 'word' => 'issues.ez.no', + 'word' => 'issues.ibexa.co', ], 746 => [ 'id' => '1574', diff --git a/tests/lib/Search/TestCase.php b/tests/lib/Search/TestCase.php new file mode 100644 index 0000000000..7539c6e5c8 --- /dev/null +++ b/tests/lib/Search/TestCase.php @@ -0,0 +1,18 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Search; + +use PHPUnit\Framework\TestCase as BaseTestCase; + +/** + * Base test case for Search Engine related tests. + */ +abstract class TestCase extends BaseTestCase +{ +} + +class_alias(TestCase::class, 'eZ\Publish\Core\Search\Tests\TestCase'); diff --git a/tests/lib/Specification/AbstractSpecificationTest.php b/tests/lib/Specification/AbstractSpecificationTest.php new file mode 100644 index 0000000000..bb5848fafc --- /dev/null +++ b/tests/lib/Specification/AbstractSpecificationTest.php @@ -0,0 +1,49 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Specification; + +class AbstractSpecificationTest extends BaseSpecificationTest +{ + public function testSpecification(): void + { + $isStringSpecification = $this->getIsStringSpecification(); + $isTestStringSpecification = $this->getIsTestStringSpecification(); + + self::assertTrue($isStringSpecification->isSatisfiedBy('test_string')); + self::assertFalse($isStringSpecification->isSatisfiedBy(1234)); + } + + public function testSpecificationAnd(): void + { + $isStringSpecification = $this->getIsStringSpecification(); + $isTestStringSpecification = $this->getIsTestStringSpecification(); + + self::assertTrue($isStringSpecification->and($isTestStringSpecification)->isSatisfiedBy('test')); + self::assertFalse($isStringSpecification->and($isTestStringSpecification)->isSatisfiedBy('test_string')); + self::assertFalse($isStringSpecification->and($isTestStringSpecification)->isSatisfiedBy(1234)); + } + + public function testSpecificationOr(): void + { + $isStringSpecification = $this->getIsStringSpecification(); + $isTestStringSpecification = $this->getIsTestStringSpecification(); + + self::assertTrue($isStringSpecification->or($isTestStringSpecification)->isSatisfiedBy('test')); + self::assertTrue($isStringSpecification->or($isTestStringSpecification)->isSatisfiedBy('test_string')); + self::assertFalse($isStringSpecification->or($isTestStringSpecification)->isSatisfiedBy(1234)); + } + + public function testSpecificationNot(): void + { + $isStringSpecification = $this->getIsStringSpecification(); + + self::assertFalse($isStringSpecification->not()->isSatisfiedBy('test_string')); + self::assertTrue($isStringSpecification->not()->isSatisfiedBy(1234)); + } +} diff --git a/tests/lib/Specification/AndSpecificationTest.php b/tests/lib/Specification/AndSpecificationTest.php new file mode 100644 index 0000000000..417e30ede8 --- /dev/null +++ b/tests/lib/Specification/AndSpecificationTest.php @@ -0,0 +1,24 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Specification; + +use Ibexa\Contracts\Core\Specification\AndSpecification; + +class AndSpecificationTest extends BaseSpecificationTest +{ + public function testAndSpecification(): void + { + $andSpecification = new AndSpecification( + $this->getIsStringSpecification(), + $this->getIsTestStringSpecification() + ); + + self::assertTrue($andSpecification->isSatisfiedBy('test')); + self::assertFalse($andSpecification->isSatisfiedBy('test_string')); + self::assertFalse($andSpecification->isSatisfiedBy(1234)); + } +} diff --git a/tests/lib/Specification/BaseSpecificationTest.php b/tests/lib/Specification/BaseSpecificationTest.php new file mode 100644 index 0000000000..e67f1e99d9 --- /dev/null +++ b/tests/lib/Specification/BaseSpecificationTest.php @@ -0,0 +1,34 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Specification; + +use Ibexa\Contracts\Core\Specification\AbstractSpecification; +use Ibexa\Contracts\Core\Specification\SpecificationInterface; +use PHPUnit\Framework\TestCase; + +abstract class BaseSpecificationTest extends TestCase +{ + protected function getIsStringSpecification(): SpecificationInterface + { + return new class() extends AbstractSpecification { + public function isSatisfiedBy($item): bool + { + return is_string($item); + } + }; + } + + protected function getIsTestStringSpecification(): SpecificationInterface + { + return new class() extends AbstractSpecification { + public function isSatisfiedBy($item): bool + { + return $item === 'test'; + } + }; + } +} diff --git a/tests/lib/Specification/Content/ContentContainerSpecificationTest.php b/tests/lib/Specification/Content/ContentContainerSpecificationTest.php new file mode 100644 index 0000000000..686bb488ce --- /dev/null +++ b/tests/lib/Specification/Content/ContentContainerSpecificationTest.php @@ -0,0 +1,64 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Specification\Content; + +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Specification\Content\ContentContainerSpecification; +use Ibexa\Contracts\Core\Specification\Content\ContentSpecification; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Contracts\Core\Specification\Content\ContentContainerSpecification + */ +final class ContentContainerSpecificationTest extends TestCase +{ + public function testConstructor(): void + { + $contentTypeSpecification = new ContentContainerSpecification(); + + $this->assertInstanceOf(ContentSpecification::class, $contentTypeSpecification); + } + + /** + * @dataProvider providerForIsSatisfiedBy + */ + public function testIsSatisfiedBy( + bool $isContainer, + bool $shouldBeSatisfied + ): void { + $contentContainerSpecification = new ContentContainerSpecification(); + + $contentTypeMock = $this->getMockBuilder(ContentType::class) + ->setConstructorArgs( + [['isContainer' => $isContainer]] + ) + ->getMockForAbstractClass(); + + $contentMock = $this->createMock(Content::class); + $contentMock->expects($this->once()) + ->method('getContentType') + ->willReturn($contentTypeMock); + + $this->assertEquals( + $contentContainerSpecification->isSatisfiedBy($contentMock), + $shouldBeSatisfied + ); + } + + public function providerForIsSatisfiedBy(): array + { + return [ + [true, true], + [false, false], + ]; + } +} + +class_alias(ContentContainerSpecificationTest::class, 'eZ\Publish\SPI\Specification\Tests\Content\ContentContainerSpecificationTest'); diff --git a/eZ/Publish/SPI/Specification/Tests/Content/ContentTypeSpecificationTest.php b/tests/lib/Specification/Content/ContentTypeSpecificationTest.php similarity index 81% rename from eZ/Publish/SPI/Specification/Tests/Content/ContentTypeSpecificationTest.php rename to tests/lib/Specification/Content/ContentTypeSpecificationTest.php index 93ebbda15d..6f077be26c 100644 --- a/eZ/Publish/SPI/Specification/Tests/Content/ContentTypeSpecificationTest.php +++ b/tests/lib/Specification/Content/ContentTypeSpecificationTest.php @@ -6,14 +6,17 @@ */ declare(strict_types=1); -namespace eZ\Publish\SPI\Specification\Tests\Content; +namespace Ibexa\Tests\Core\Specification\Content; -use eZ\Publish\API\Repository\Values\Content\Content; -use eZ\Publish\API\Repository\Values\ContentType\ContentType; -use eZ\Publish\SPI\Specification\Content\ContentSpecification; -use eZ\Publish\SPI\Specification\Content\ContentTypeSpecification; +use Ibexa\Contracts\Core\Repository\Values\Content\Content; +use Ibexa\Contracts\Core\Repository\Values\ContentType\ContentType; +use Ibexa\Contracts\Core\Specification\Content\ContentSpecification; +use Ibexa\Contracts\Core\Specification\Content\ContentTypeSpecification; use PHPUnit\Framework\TestCase; +/** + * @covers \Ibexa\Contracts\Core\Specification\Content\ContentTypeSpecification + */ final class ContentTypeSpecificationTest extends TestCase { private const EXISTING_CONTENT_TYPE_IDENTIFIER = 'article'; @@ -38,7 +41,6 @@ public function testConstructorWithNotExistingContentTypeIdentifier(): void } /** - * @covers \eZ\Publish\SPI\Specification\Content\ContentTypeSpecification::isSatisfiedBy * @dataProvider providerForIsSatisfiedBy */ public function testIsSatisfiedBy( @@ -75,3 +77,5 @@ public function providerForIsSatisfiedBy(): array ]; } } + +class_alias(ContentTypeSpecificationTest::class, 'eZ\Publish\SPI\Specification\Tests\Content\ContentTypeSpecificationTest'); diff --git a/tests/lib/Specification/NotSpecificationTest.php b/tests/lib/Specification/NotSpecificationTest.php new file mode 100644 index 0000000000..43f395c665 --- /dev/null +++ b/tests/lib/Specification/NotSpecificationTest.php @@ -0,0 +1,22 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Specification; + +use Ibexa\Contracts\Core\Specification\NotSpecification; + +class NotSpecificationTest extends BaseSpecificationTest +{ + public function testNotSpecification(): void + { + $andSpecification = new NotSpecification( + $this->getIsStringSpecification() + ); + + self::assertFalse($andSpecification->isSatisfiedBy('test')); + self::assertTrue($andSpecification->isSatisfiedBy(1234)); + } +} diff --git a/tests/lib/Specification/OrSpecificationTest.php b/tests/lib/Specification/OrSpecificationTest.php new file mode 100644 index 0000000000..dd2615b532 --- /dev/null +++ b/tests/lib/Specification/OrSpecificationTest.php @@ -0,0 +1,24 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +namespace Ibexa\Tests\Core\Specification; + +use Ibexa\Contracts\Core\Specification\OrSpecification; + +class OrSpecificationTest extends BaseSpecificationTest +{ + public function testOrSpecification(): void + { + $andSpecification = new OrSpecification( + $this->getIsStringSpecification(), + $this->getIsTestStringSpecification() + ); + + self::assertTrue($andSpecification->isSatisfiedBy('test')); + self::assertTrue($andSpecification->isSatisfiedBy('test_string')); + self::assertFalse($andSpecification->isSatisfiedBy(1234)); + } +} diff --git a/tests/lib/Token/RandomBytesGeneratorTest.php b/tests/lib/Token/RandomBytesGeneratorTest.php new file mode 100644 index 0000000000..138485510e --- /dev/null +++ b/tests/lib/Token/RandomBytesGeneratorTest.php @@ -0,0 +1,71 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Token; + +use Ibexa\Contracts\Core\Token\TokenGeneratorInterface; +use Ibexa\Core\Token\RandomBytesGenerator; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Token\RandomBytesGenerator + */ +final class RandomBytesGeneratorTest extends TestCase +{ + private TokenGeneratorInterface $tokenGenerator; + + protected function setUp(): void + { + $this->tokenGenerator = new RandomBytesGenerator(); + } + + /** + * @dataProvider provideDataForTestGenerateToken + * + * @throws \Exception + */ + public function testGenerateToken(int $expectedTokenLength): void + { + $generatedToken = $this->tokenGenerator->generateToken($expectedTokenLength); + + self::assertNotSame( + $generatedToken, + $this->tokenGenerator->generateToken($expectedTokenLength), + 'Token generator should return different values on subsequent calls', + ); + + self::assertSame( + $expectedTokenLength, + strlen($generatedToken) + ); + } + + /** + * @return iterable<array{ + * int + * }> + */ + public function provideDataForTestGenerateToken(): iterable + { + yield [ + 20, + ]; + + yield [ + 64, + ]; + + yield [ + 100, + ]; + + yield [ + 256, + ]; + } +} diff --git a/tests/lib/Token/WebSafeGeneratorTest.php b/tests/lib/Token/WebSafeGeneratorTest.php new file mode 100644 index 0000000000..e3ed9daafb --- /dev/null +++ b/tests/lib/Token/WebSafeGeneratorTest.php @@ -0,0 +1,103 @@ +<?php + +/** + * @copyright Copyright (C) Ibexa AS. All rights reserved. + * @license For full copyright and license information view LICENSE file distributed with this source code. + */ +declare(strict_types=1); + +namespace Ibexa\Tests\Core\Token; + +use Ibexa\Contracts\Core\Token\TokenGeneratorInterface; +use Ibexa\Core\Token\WebSafeGenerator; +use PHPUnit\Framework\TestCase; + +/** + * @covers \Ibexa\Core\Token\WebSafeGenerator + */ +final class WebSafeGeneratorTest extends TestCase +{ + private TokenGeneratorInterface $tokenGenerator; + + /** @var \Ibexa\Contracts\Core\Token\TokenGeneratorInterface&\PHPUnit\Framework\MockObject\MockObject */ + private TokenGeneratorInterface $randomBytesTokenGenerator; + + protected function setUp(): void + { + $this->randomBytesTokenGenerator = $this->createMock(TokenGeneratorInterface::class); + $this->tokenGenerator = new WebSafeGenerator($this->randomBytesTokenGenerator); + } + + /** + * @dataProvider provideDataForTestGenerateToken + * + * @throws \Exception + */ + public function testGenerateToken( + int $expectedTokenLength, + string $mockGeneratorOutputToken, + string $expectedToken + ): void { + $this->mockTokenGeneratorGenerateToken( + $expectedTokenLength, + $mockGeneratorOutputToken + ); + + $generatedToken = $this->tokenGenerator->generateToken($expectedTokenLength); + + self::assertSame( + $expectedTokenLength, + strlen($generatedToken) + ); + + self::assertSame( + $expectedToken, + $generatedToken + ); + } + + /** + * @return iterable<array{ + * int, + * string, + * string + * }> + */ + public function provideDataForTestGenerateToken(): iterable + { + yield [ + 20, + '123456+-1az2w3edc4==', + 'MTIzNDU2Ky0xYXoydzNl', + ]; + + yield [ + 64, + '123/561qaz2wsx3edc4rfv1234561qaz2wsx+-dc=3edc4rv1234561qarfv145=', + 'MTIzLzU2MXFhejJ3c3gzZWRjNHJmdjEyMzQ1NjFxYXoyd3N4Ky1kYz0zZWRjNHJ2', + ]; + + yield [ + 100, + '+-34561qaz2wsx3ec4rfv1234561qax3edc4rfv5tgbz2wsxaz2wsxdc4rfv123ec4rfv1234561qaz2wsx3edc4rfv457yhnzz=', + 'Ky0zNDU2MXFhejJ3c3gzZWM0cmZ2MTIzNDU2MXFheDNlZGM0cmZ2NXRnYnoyd3N4YXoyd3N4ZGM0cmZ2MTIzZWM0cmZ2MTIzNDU2', + ]; + + yield [ + 256, + '1234561qaz2wsx3ed+-rfv1234561qa561qaz2wsx3edc4rfv1==234561qaz2wsxz2wsx3ec4rfv1234561qaz2wsx3edc4rfv145=7yhnzz1234561qaz2wsx3edc4rfv1234561qaz2wsx3ec4rfv1234561qaz2wsx3edc4rfv1fv1234561qaz2wsx3ec4rf==234564567yhnz3ec4rfv1234561qaz2wsx3edc4rfv14567yhnzz12345', + 'MTIzNDU2MXFhejJ3c3gzZWQrLXJmdjEyMzQ1NjFxYTU2MXFhejJ3c3gzZWRjNHJmdjE9PTIzNDU2MXFhejJ3c3h6MndzeDNlYzRyZnYxMjM0NTYxcWF6MndzeDNlZGM0cmZ2MTQ1PTd5aG56ejEyMzQ1NjFxYXoyd3N4M2VkYzRyZnYxMjM0NTYxcWF6MndzeDNlYzRyZnYxMjM0NTYxcWF6MndzeDNlZGM0cmZ2MWZ2MTIzNDU2MXFhejJ3c3gz', + ]; + } + + private function mockTokenGeneratorGenerateToken( + int $length, + string $token + ): void { + $this->randomBytesTokenGenerator + ->expects(self::once()) + ->method('generateToken') + ->with($length) + ->willReturn($token); + } +} diff --git a/eZ/Publish/Core/FieldType/Tests/phppng2.PHP b/tests/lib/_fixtures/phppng.php similarity index 100% rename from eZ/Publish/Core/FieldType/Tests/phppng2.PHP rename to tests/lib/_fixtures/phppng.php diff --git a/tests/lib/_fixtures/phppng2.PHP b/tests/lib/_fixtures/phppng2.PHP new file mode 100644 index 0000000000..e69de29bb2 diff --git a/eZ/Publish/Core/FieldType/Tests/squirrel-developers.jpg b/tests/lib/_fixtures/squirrel-developers.jpg similarity index 100% rename from eZ/Publish/Core/FieldType/Tests/squirrel-developers.jpg rename to tests/lib/_fixtures/squirrel-developers.jpg